summaryrefslogtreecommitdiffstats
path: root/usr.bin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/Makefile58
-rw-r--r--usr.bin/Makefile.inc3
-rw-r--r--usr.bin/apply/Makefile5
-rw-r--r--usr.bin/apply/apply.1129
-rw-r--r--usr.bin/apply/apply.c234
-rw-r--r--usr.bin/apropos/Makefile7
-rw-r--r--usr.bin/apropos/apropos.1120
-rw-r--r--usr.bin/apropos/apropos.c223
-rw-r--r--usr.bin/ar/Makefile10
-rw-r--r--usr.bin/ar/append.c89
-rw-r--r--usr.bin/ar/ar.1257
-rw-r--r--usr.bin/ar/ar.1aout257
-rw-r--r--usr.bin/ar/ar.5145
-rw-r--r--usr.bin/ar/ar.5.5145
-rw-r--r--usr.bin/ar/ar.c237
-rw-r--r--usr.bin/ar/archive.c326
-rw-r--r--usr.bin/ar/archive.h105
-rw-r--r--usr.bin/ar/contents.c97
-rw-r--r--usr.bin/ar/delete.c96
-rw-r--r--usr.bin/ar/extern.h54
-rw-r--r--usr.bin/ar/extract.c128
-rw-r--r--usr.bin/ar/misc.c146
-rw-r--r--usr.bin/ar/move.c140
-rw-r--r--usr.bin/ar/pathnames.h40
-rw-r--r--usr.bin/ar/print.c90
-rw-r--r--usr.bin/ar/replace.c178
-rw-r--r--usr.bin/at/LEGAL29
-rw-r--r--usr.bin/at/Makefile32
-rw-r--r--usr.bin/at/Makefile.inc19
-rw-r--r--usr.bin/at/at.1216
-rw-r--r--usr.bin/at/at.c759
-rw-r--r--usr.bin/at/at.h31
-rw-r--r--usr.bin/at/at.man268
-rw-r--r--usr.bin/at/panic.c81
-rw-r--r--usr.bin/at/panic.h44
-rw-r--r--usr.bin/at/parsetime.c615
-rw-r--r--usr.bin/at/parsetime.h26
-rw-r--r--usr.bin/at/pathnames.h42
-rw-r--r--usr.bin/at/perm.c123
-rw-r--r--usr.bin/at/perm.h26
-rw-r--r--usr.bin/at/privs.h111
-rw-r--r--usr.bin/banner/Makefile6
-rw-r--r--usr.bin/banner/banner.672
-rw-r--r--usr.bin/banner/banner.c1160
-rw-r--r--usr.bin/basename/Makefile6
-rw-r--r--usr.bin/basename/basename.197
-rw-r--r--usr.bin/basename/basename.c138
-rw-r--r--usr.bin/bdes/Makefile5
-rw-r--r--usr.bin/bdes/bdes.1304
-rw-r--r--usr.bin/bdes/bdes.c1046
-rw-r--r--usr.bin/bdes/bdes.ps2945
-rw-r--r--usr.bin/biff/Makefile5
-rw-r--r--usr.bin/biff/biff.186
-rw-r--r--usr.bin/biff/biff.c114
-rw-r--r--usr.bin/cal/Makefile5
-rw-r--r--usr.bin/cal/README42
-rw-r--r--usr.bin/cal/cal.181
-rw-r--r--usr.bin/cal/cal.c427
-rw-r--r--usr.bin/calendar/Makefile9
-rw-r--r--usr.bin/calendar/calendar.1145
-rw-r--r--usr.bin/calendar/calendar.c411
-rw-r--r--usr.bin/calendar/calendars/calendar.birthday257
-rw-r--r--usr.bin/calendar/calendars/calendar.christian16
-rw-r--r--usr.bin/calendar/calendars/calendar.computer62
-rw-r--r--usr.bin/calendar/calendars/calendar.history486
-rw-r--r--usr.bin/calendar/calendars/calendar.holiday568
-rw-r--r--usr.bin/calendar/calendars/calendar.judaic30
-rw-r--r--usr.bin/calendar/calendars/calendar.music178
-rw-r--r--usr.bin/calendar/calendars/calendar.usholiday31
-rw-r--r--usr.bin/calendar/pathnames.h40
-rw-r--r--usr.bin/cap_mkdb/Makefile5
-rw-r--r--usr.bin/cap_mkdb/cap_mkdb.1101
-rw-r--r--usr.bin/cap_mkdb/cap_mkdb.c250
-rw-r--r--usr.bin/chat/Example5
-rw-r--r--usr.bin/chat/Makefile8
-rw-r--r--usr.bin/chat/README169
-rw-r--r--usr.bin/chat/chat.8251
-rw-r--r--usr.bin/chat/chat.c1166
-rwxr-xr-xusr.bin/chat/connect-ppp129
-rw-r--r--usr.bin/chat/fix-cua16
-rwxr-xr-xusr.bin/chat/ppp-off5
-rwxr-xr-xusr.bin/chat/ppp-on35
-rwxr-xr-xusr.bin/chat/unlock23
-rw-r--r--usr.bin/checknr/Makefile5
-rw-r--r--usr.bin/checknr/checknr.1159
-rw-r--r--usr.bin/checknr/checknr.c587
-rw-r--r--usr.bin/chflags/Makefile9
-rw-r--r--usr.bin/chflags/chflags.1125
-rw-r--r--usr.bin/chflags/chflags.c180
-rw-r--r--usr.bin/chpass/Makefile27
-rw-r--r--usr.bin/chpass/chpass.1336
-rw-r--r--usr.bin/chpass/chpass.c265
-rw-r--r--usr.bin/chpass/chpass.h70
-rw-r--r--usr.bin/chpass/edit.c237
-rw-r--r--usr.bin/chpass/field.c268
-rw-r--r--usr.bin/chpass/pathnames.h39
-rw-r--r--usr.bin/chpass/pw_copy.c115
-rw-r--r--usr.bin/chpass/pw_copy.h36
-rw-r--r--usr.bin/chpass/pw_yp.c356
-rw-r--r--usr.bin/chpass/pw_yp.h52
-rw-r--r--usr.bin/chpass/table.c60
-rw-r--r--usr.bin/chpass/util.c142
-rw-r--r--usr.bin/cksum/Makefile6
-rw-r--r--usr.bin/cksum/cksum.1164
-rw-r--r--usr.bin/cksum/cksum.c124
-rw-r--r--usr.bin/cksum/crc.c140
-rw-r--r--usr.bin/cksum/extern.h45
-rw-r--r--usr.bin/cksum/print.c73
-rw-r--r--usr.bin/cksum/sum1.c69
-rw-r--r--usr.bin/cksum/sum2.c71
-rw-r--r--usr.bin/cmp/Makefile6
-rw-r--r--usr.bin/cmp/cmp.1107
-rw-r--r--usr.bin/cmp/cmp.c162
-rw-r--r--usr.bin/cmp/extern.h45
-rw-r--r--usr.bin/cmp/misc.c64
-rw-r--r--usr.bin/cmp/regular.c98
-rw-r--r--usr.bin/cmp/special.c99
-rw-r--r--usr.bin/col/Makefile5
-rw-r--r--usr.bin/col/README48
-rw-r--r--usr.bin/col/col.1126
-rw-r--r--usr.bin/col/col.c544
-rw-r--r--usr.bin/colcrt/Makefile5
-rw-r--r--usr.bin/colcrt/colcrt.1108
-rw-r--r--usr.bin/colcrt/colcrt.c251
-rw-r--r--usr.bin/colldef/Makefile31
-rw-r--r--usr.bin/colldef/colldef.1194
-rw-r--r--usr.bin/colldef/data/lt_LN.ISO8859-119
-rw-r--r--usr.bin/colldef/data/lt_LN.ISO_8859-119
-rw-r--r--usr.bin/colldef/data/ru_SU.CP86620
-rw-r--r--usr.bin/colldef/data/ru_SU.KOI8-R20
-rw-r--r--usr.bin/colldef/parse.y248
-rw-r--r--usr.bin/colldef/scan.l212
-rw-r--r--usr.bin/colrm/Makefile5
-rw-r--r--usr.bin/colrm/colrm.178
-rw-r--r--usr.bin/colrm/colrm.c167
-rw-r--r--usr.bin/column/Makefile5
-rw-r--r--usr.bin/column/column.199
-rw-r--r--usr.bin/column/column.c304
-rw-r--r--usr.bin/comm/Makefile5
-rw-r--r--usr.bin/comm/comm.194
-rw-r--r--usr.bin/comm/comm.c186
-rw-r--r--usr.bin/compile_et/Makefile17
-rw-r--r--usr.bin/compile_et/compile_et.179
-rw-r--r--usr.bin/compile_et/compile_et.c290
-rw-r--r--usr.bin/compile_et/compiler.h20
-rw-r--r--usr.bin/compile_et/error_message.c77
-rw-r--r--usr.bin/compile_et/error_table.h17
-rw-r--r--usr.bin/compile_et/error_table.y233
-rw-r--r--usr.bin/compile_et/et_lex.lex.l26
-rw-r--r--usr.bin/compile_et/et_name.c44
-rw-r--r--usr.bin/compile_et/init_et.c67
-rw-r--r--usr.bin/compile_et/mit-sipb-copyright.h19
-rw-r--r--usr.bin/compile_et/perror.c76
-rw-r--r--usr.bin/compile_et/test/test.c43
-rw-r--r--usr.bin/compile_et/test/test1.et69
-rw-r--r--usr.bin/compile_et/test/test2.et9
-rw-r--r--usr.bin/compress/Makefile8
-rw-r--r--usr.bin/compress/compress.1172
-rw-r--r--usr.bin/compress/compress.c444
-rw-r--r--usr.bin/compress/doc/NOTES139
-rw-r--r--usr.bin/compress/doc/README283
-rw-r--r--usr.bin/compress/doc/revision.log116
-rw-r--r--usr.bin/compress/zcat.sh37
-rw-r--r--usr.bin/compress/zopen.3139
-rw-r--r--usr.bin/compress/zopen.c740
-rw-r--r--usr.bin/cpp/Makefile17
-rw-r--r--usr.bin/cpp/cpp.notraditional.sh91
-rw-r--r--usr.bin/cpp/cpp.sh92
-rw-r--r--usr.bin/ctags/C.c501
-rw-r--r--usr.bin/ctags/Makefile7
-rw-r--r--usr.bin/ctags/ctags.1214
-rw-r--r--usr.bin/ctags/ctags.c272
-rw-r--r--usr.bin/ctags/ctags.h90
-rw-r--r--usr.bin/ctags/fortran.c168
-rw-r--r--usr.bin/ctags/lisp.c105
-rw-r--r--usr.bin/ctags/print.c115
-rw-r--r--usr.bin/ctags/test/ctags.test67
-rw-r--r--usr.bin/ctags/tree.c135
-rw-r--r--usr.bin/ctags/yacc.c151
-rw-r--r--usr.bin/cut/Makefile5
-rw-r--r--usr.bin/cut/cut.1110
-rw-r--r--usr.bin/cut/cut.c297
-rw-r--r--usr.bin/devmenu/Makefile10
-rw-r--r--usr.bin/devmenu/devfilter.c198
-rw-r--r--usr.bin/devmenu/devmenu.1152
-rw-r--r--usr.bin/devmenu/devmenu.c253
-rw-r--r--usr.bin/devmenu/devmenu.h52
-rw-r--r--usr.bin/devmenu/ifmenu.c52
-rw-r--r--usr.bin/diff/diff/diff.1387
-rw-r--r--usr.bin/diff/diff3/diff3.1172
-rw-r--r--usr.bin/dig/Makefile11
-rw-r--r--usr.bin/dig/dig.16
-rw-r--r--usr.bin/dig/dig.c64
-rw-r--r--usr.bin/dirname/Makefile6
-rw-r--r--usr.bin/dirname/dirname.c144
-rw-r--r--usr.bin/du/Makefile5
-rw-r--r--usr.bin/du/du.1122
-rw-r--r--usr.bin/du/du.c232
-rw-r--r--usr.bin/ee/Makefile18
-rw-r--r--usr.bin/ee/README116
-rw-r--r--usr.bin/ee/doc/Artistic117
-rw-r--r--usr.bin/ee/doc/ee.i18n.guide141
-rw-r--r--usr.bin/ee/doc/new_curse.c3574
-rw-r--r--usr.bin/ee/doc/new_curse.h255
-rw-r--r--usr.bin/ee/ee.1508
-rw-r--r--usr.bin/ee/ee.c4797
-rw-r--r--usr.bin/ee/ee.msg170
-rw-r--r--usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg170
-rw-r--r--usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg170
-rw-r--r--usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg170
-rw-r--r--usr.bin/ee/nls/en_US.US-ASCII/ee.msg170
-rw-r--r--usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg170
-rw-r--r--usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg170
-rw-r--r--usr.bin/env/Makefile6
-rw-r--r--usr.bin/env/env.c82
-rw-r--r--usr.bin/error/Makefile6
-rw-r--r--usr.bin/error/error.1304
-rw-r--r--usr.bin/error/error.h224
-rw-r--r--usr.bin/error/filter.c194
-rw-r--r--usr.bin/error/input.c548
-rw-r--r--usr.bin/error/main.c287
-rw-r--r--usr.bin/error/pathnames.h43
-rw-r--r--usr.bin/error/pi.c404
-rw-r--r--usr.bin/error/subr.c423
-rw-r--r--usr.bin/error/touch.c768
-rw-r--r--usr.bin/expand/Makefile6
-rw-r--r--usr.bin/expand/expand.187
-rw-r--r--usr.bin/expand/expand.c152
-rw-r--r--usr.bin/f2c/Makefile31
-rw-r--r--usr.bin/f2c/Notice23
-rw-r--r--usr.bin/f2c/README145
-rw-r--r--usr.bin/f2c/cds.c195
-rw-r--r--usr.bin/f2c/data.c491
-rw-r--r--usr.bin/f2c/defines.h290
-rw-r--r--usr.bin/f2c/defs.h1053
-rw-r--r--usr.bin/f2c/dependencies60
-rw-r--r--usr.bin/f2c/disclaimer15
-rw-r--r--usr.bin/f2c/equiv.c398
-rw-r--r--usr.bin/f2c/error.c347
-rw-r--r--usr.bin/f2c/exec.c927
-rw-r--r--usr.bin/f2c/expr.c3366
-rw-r--r--usr.bin/f2c/f2c.1356
-rw-r--r--usr.bin/f2c/f2c.1t336
-rw-r--r--usr.bin/f2c/f2c.h214
-rw-r--r--usr.bin/f2c/f77.script114
-rw-r--r--usr.bin/f2c/format.c2517
-rw-r--r--usr.bin/f2c/format.h12
-rw-r--r--usr.bin/f2c/formatdata.c1166
-rw-r--r--usr.bin/f2c/ftypes.h51
-rw-r--r--usr.bin/f2c/gram.c1829
-rw-r--r--usr.bin/f2c/gram.dcl404
-rw-r--r--usr.bin/f2c/gram.exec143
-rw-r--r--usr.bin/f2c/gram.expr142
-rw-r--r--usr.bin/f2c/gram.head290
-rw-r--r--usr.bin/f2c/gram.io173
-rw-r--r--usr.bin/f2c/index127
-rw-r--r--usr.bin/f2c/index.html138
-rw-r--r--usr.bin/f2c/init.c516
-rw-r--r--usr.bin/f2c/intr.c879
-rw-r--r--usr.bin/f2c/io.c1505
-rw-r--r--usr.bin/f2c/iob.h26
-rw-r--r--usr.bin/f2c/lex.c1676
-rw-r--r--usr.bin/f2c/machdefs.h31
-rw-r--r--usr.bin/f2c/main.c683
-rw-r--r--usr.bin/f2c/makefile90
-rw-r--r--usr.bin/f2c/malloc.c166
-rw-r--r--usr.bin/f2c/mem.c268
-rw-r--r--usr.bin/f2c/memset.c66
-rw-r--r--usr.bin/f2c/misc.c1330
-rw-r--r--usr.bin/f2c/names.c822
-rw-r--r--usr.bin/f2c/names.h19
-rw-r--r--usr.bin/f2c/niceprintf.c441
-rw-r--r--usr.bin/f2c/niceprintf.h16
-rw-r--r--usr.bin/f2c/notice23
-rw-r--r--usr.bin/f2c/output.c1670
-rw-r--r--usr.bin/f2c/output.h64
-rw-r--r--usr.bin/f2c/p1defs.h158
-rw-r--r--usr.bin/f2c/p1output.c723
-rw-r--r--usr.bin/f2c/parse.h47
-rw-r--r--usr.bin/f2c/parse_args.c542
-rw-r--r--usr.bin/f2c/pccdefs.h64
-rw-r--r--usr.bin/f2c/permission23
-rw-r--r--usr.bin/f2c/pread.c990
-rw-r--r--usr.bin/f2c/proc.c1805
-rw-r--r--usr.bin/f2c/put.c440
-rw-r--r--usr.bin/f2c/putpcc.c2018
-rw-r--r--usr.bin/f2c/readme145
-rw-r--r--usr.bin/f2c/sysdep.c519
-rw-r--r--usr.bin/f2c/sysdep.h98
-rw-r--r--usr.bin/f2c/tokens100
-rw-r--r--usr.bin/f2c/usignal.h7
-rw-r--r--usr.bin/f2c/vax.c566
-rw-r--r--usr.bin/f2c/version.c2
-rw-r--r--usr.bin/f2c/xsum.c237
-rw-r--r--usr.bin/f2c/xsum0.out56
-rw-r--r--usr.bin/false/Makefile5
-rw-r--r--usr.bin/false/false.163
-rw-r--r--usr.bin/false/false.c47
-rw-r--r--usr.bin/fib/Makefile11
-rw-r--r--usr.bin/fib/fib.c58
-rw-r--r--usr.bin/fib/lex.l93
-rw-r--r--usr.bin/fib/parser.y853
-rw-r--r--usr.bin/file/LEGAL.NOTICE37
-rw-r--r--usr.bin/file/MAINT33
-rw-r--r--usr.bin/file/Magdir/Header5
-rw-r--r--usr.bin/file/Magdir/Localstuff3
-rw-r--r--usr.bin/file/Magdir/alliant15
-rw-r--r--usr.bin/file/Magdir/apl4
-rw-r--r--usr.bin/file/Magdir/apple10
-rw-r--r--usr.bin/file/Magdir/ar104
-rw-r--r--usr.bin/file/Magdir/arc3
-rw-r--r--usr.bin/file/Magdir/archive15
-rw-r--r--usr.bin/file/Magdir/att3b38
-rw-r--r--usr.bin/file/Magdir/audio43
-rw-r--r--usr.bin/file/Magdir/blit16
-rw-r--r--usr.bin/file/Magdir/bsdi2
-rw-r--r--usr.bin/file/Magdir/c-lang3
-rw-r--r--usr.bin/file/Magdir/chi4
-rw-r--r--usr.bin/file/Magdir/clipper63
-rw-r--r--usr.bin/file/Magdir/commands40
-rw-r--r--usr.bin/file/Magdir/compress40
-rw-r--r--usr.bin/file/Magdir/convex4
-rw-r--r--usr.bin/file/Magdir/cpio16
-rw-r--r--usr.bin/file/Magdir/diamond8
-rw-r--r--usr.bin/file/Magdir/diff6
-rw-r--r--usr.bin/file/Magdir/ditroff4
-rw-r--r--usr.bin/file/Magdir/dump80
-rw-r--r--usr.bin/file/Magdir/elf44
-rw-r--r--usr.bin/file/Magdir/encore20
-rw-r--r--usr.bin/file/Magdir/floppy.raw1
-rw-r--r--usr.bin/file/Magdir/fonts3
-rw-r--r--usr.bin/file/Magdir/frame28
-rw-r--r--usr.bin/file/Magdir/freebsd26
-rw-r--r--usr.bin/file/Magdir/gzip21
-rw-r--r--usr.bin/file/Magdir/hp191
-rw-r--r--usr.bin/file/Magdir/ibm37019
-rw-r--r--usr.bin/file/Magdir/ibm600017
-rw-r--r--usr.bin/file/Magdir/iff5
-rw-r--r--usr.bin/file/Magdir/imagen14
-rw-r--r--usr.bin/file/Magdir/images69
-rw-r--r--usr.bin/file/Magdir/intel31
-rw-r--r--usr.bin/file/Magdir/interleaf7
-rw-r--r--usr.bin/file/Magdir/iris57
-rw-r--r--usr.bin/file/Magdir/ispell23
-rw-r--r--usr.bin/file/Magdir/lex3
-rw-r--r--usr.bin/file/Magdir/lif6
-rw-r--r--usr.bin/file/Magdir/linux11
-rw-r--r--usr.bin/file/Magdir/magic1
-rw-r--r--usr.bin/file/Magdir/mail.news13
-rw-r--r--usr.bin/file/Magdir/microsoft68
-rw-r--r--usr.bin/file/Magdir/mips8
-rw-r--r--usr.bin/file/Magdir/mirage4
-rw-r--r--usr.bin/file/Magdir/mkid7
-rw-r--r--usr.bin/file/Magdir/mmdf1
-rw-r--r--usr.bin/file/Magdir/motorola28
-rw-r--r--usr.bin/file/Magdir/ms-dos8
-rw-r--r--usr.bin/file/Magdir/ncr47
-rw-r--r--usr.bin/file/Magdir/netbsd117
-rw-r--r--usr.bin/file/Magdir/news5
-rw-r--r--usr.bin/file/Magdir/pbm4
-rw-r--r--usr.bin/file/Magdir/pdp22
-rw-r--r--usr.bin/file/Magdir/pgp11
-rw-r--r--usr.bin/file/Magdir/pjl5
-rw-r--r--usr.bin/file/Magdir/pkgadd4
-rw-r--r--usr.bin/file/Magdir/plus516
-rw-r--r--usr.bin/file/Magdir/postscript20
-rw-r--r--usr.bin/file/Magdir/psdbms6
-rw-r--r--usr.bin/file/Magdir/pyramid10
-rw-r--r--usr.bin/file/Magdir/rle19
-rw-r--r--usr.bin/file/Magdir/sc2
-rw-r--r--usr.bin/file/Magdir/sccs17
-rw-r--r--usr.bin/file/Magdir/sendmail9
-rw-r--r--usr.bin/file/Magdir/sequent30
-rw-r--r--usr.bin/file/Magdir/sgml6
-rw-r--r--usr.bin/file/Magdir/softquad27
-rw-r--r--usr.bin/file/Magdir/sun84
-rw-r--r--usr.bin/file/Magdir/sunraster12
-rw-r--r--usr.bin/file/Magdir/terminfo8
-rw-r--r--usr.bin/file/Magdir/tex25
-rw-r--r--usr.bin/file/Magdir/troff6
-rw-r--r--usr.bin/file/Magdir/typeset5
-rw-r--r--usr.bin/file/Magdir/unknown35
-rw-r--r--usr.bin/file/Magdir/uuencode3
-rw-r--r--usr.bin/file/Magdir/varied.out5
-rw-r--r--usr.bin/file/Magdir/vax33
-rw-r--r--usr.bin/file/Magdir/visx30
-rw-r--r--usr.bin/file/Magdir/x1111
-rw-r--r--usr.bin/file/Magdir/zilog11
-rw-r--r--usr.bin/file/Magdir/zyxel11
-rw-r--r--usr.bin/file/Makefile58
-rw-r--r--usr.bin/file/PORTING76
-rw-r--r--usr.bin/file/README79
-rw-r--r--usr.bin/file/apprentice.c551
-rw-r--r--usr.bin/file/ascmagic.c120
-rw-r--r--usr.bin/file/compress.c125
-rw-r--r--usr.bin/file/cvsimport.sh17
-rw-r--r--usr.bin/file/file.1351
-rw-r--r--usr.bin/file/file.c276
-rw-r--r--usr.bin/file/file.h128
-rw-r--r--usr.bin/file/fsmagic.c174
-rw-r--r--usr.bin/file/is_tar.c100
-rw-r--r--usr.bin/file/magic.5194
-rw-r--r--usr.bin/file/names.h90
-rw-r--r--usr.bin/file/patchlevel.h63
-rw-r--r--usr.bin/file/print.c204
-rw-r--r--usr.bin/file/softmagic.c463
-rw-r--r--usr.bin/file/tar.h179
-rw-r--r--usr.bin/file2c/Makefile6
-rw-r--r--usr.bin/file2c/file2c.148
-rw-r--r--usr.bin/file2c/file2c.c46
-rw-r--r--usr.bin/find/Makefile6
-rw-r--r--usr.bin/find/extern.h79
-rw-r--r--usr.bin/find/find.1462
-rw-r--r--usr.bin/find/find.c199
-rw-r--r--usr.bin/find/find.h108
-rw-r--r--usr.bin/find/function.c1095
-rw-r--r--usr.bin/find/ls.c117
-rw-r--r--usr.bin/find/main.c153
-rw-r--r--usr.bin/find/misc.c127
-rw-r--r--usr.bin/find/operator.c270
-rw-r--r--usr.bin/find/option.c151
-rw-r--r--usr.bin/finger/Makefile6
-rw-r--r--usr.bin/finger/extern.h50
-rw-r--r--usr.bin/finger/finger.1198
-rw-r--r--usr.bin/finger/finger.c308
-rw-r--r--usr.bin/finger/finger.h65
-rw-r--r--usr.bin/finger/lprint.c352
-rw-r--r--usr.bin/finger/net.c147
-rw-r--r--usr.bin/finger/sprint.c166
-rw-r--r--usr.bin/finger/util.c426
-rw-r--r--usr.bin/fmt/Makefile7
-rw-r--r--usr.bin/fmt/fmt.189
-rw-r--r--usr.bin/fmt/fmt.c467
-rw-r--r--usr.bin/fold/Makefile5
-rw-r--r--usr.bin/fold/fold.164
-rw-r--r--usr.bin/fold/fold.c152
-rw-r--r--usr.bin/fpr/Makefile5
-rw-r--r--usr.bin/fpr/fpr.183
-rw-r--r--usr.bin/fpr/fpr.c410
-rw-r--r--usr.bin/from/Makefile5
-rw-r--r--usr.bin/from/from.183
-rw-r--r--usr.bin/from/from.c136
-rw-r--r--usr.bin/fsplit/Makefile5
-rw-r--r--usr.bin/fsplit/fsplit.1103
-rw-r--r--usr.bin/fsplit/fsplit.c408
-rw-r--r--usr.bin/fstat/Makefile10
-rw-r--r--usr.bin/fstat/fstat.1219
-rw-r--r--usr.bin/fstat/fstat.c747
-rw-r--r--usr.bin/ftp/Makefile9
-rw-r--r--usr.bin/ftp/cmds.c2230
-rw-r--r--usr.bin/ftp/cmdtab.c190
-rw-r--r--usr.bin/ftp/domacro.c148
-rw-r--r--usr.bin/ftp/extern.h154
-rw-r--r--usr.bin/ftp/ftp.11154
-rw-r--r--usr.bin/ftp/ftp.c1559
-rw-r--r--usr.bin/ftp/ftp_var.h127
-rw-r--r--usr.bin/ftp/main.c536
-rw-r--r--usr.bin/ftp/pathnames.h39
-rw-r--r--usr.bin/ftp/ruserpass.c281
-rw-r--r--usr.bin/gcore/Makefile14
-rw-r--r--usr.bin/gcore/aoutcore.c313
-rw-r--r--usr.bin/gcore/extern.h37
-rw-r--r--usr.bin/gcore/gcore.190
-rw-r--r--usr.bin/gcore/gcore.c313
-rw-r--r--usr.bin/gcore/md-nop.c53
-rw-r--r--usr.bin/gcore/md-sparc.c186
-rw-r--r--usr.bin/gencat/Makefile9
-rw-r--r--usr.bin/gencat/gencat.c255
-rw-r--r--usr.bin/gencat/gencat.h107
-rw-r--r--usr.bin/gencat/genlib.c892
-rw-r--r--usr.bin/getopt/Makefile7
-rw-r--r--usr.bin/getopt/README57
-rw-r--r--usr.bin/getopt/getopt.1103
-rw-r--r--usr.bin/getopt/getopt.c30
-rw-r--r--usr.bin/gprof/Makefile12
-rw-r--r--usr.bin/gprof/PSD.doc/Makefile12
-rw-r--r--usr.bin/gprof/PSD.doc/abstract.me66
-rw-r--r--usr.bin/gprof/PSD.doc/gathering.me231
-rw-r--r--usr.bin/gprof/PSD.doc/header.me38
-rw-r--r--usr.bin/gprof/PSD.doc/intro.me81
-rw-r--r--usr.bin/gprof/PSD.doc/postp.me190
-rw-r--r--usr.bin/gprof/PSD.doc/postp1.pic54
-rw-r--r--usr.bin/gprof/PSD.doc/postp2.pic56
-rw-r--r--usr.bin/gprof/PSD.doc/postp3.pic51
-rw-r--r--usr.bin/gprof/PSD.doc/pres1.pic56
-rw-r--r--usr.bin/gprof/PSD.doc/pres2.pic52
-rw-r--r--usr.bin/gprof/PSD.doc/present.me306
-rw-r--r--usr.bin/gprof/PSD.doc/profiling.me115
-rw-r--r--usr.bin/gprof/PSD.doc/refs.me63
-rw-r--r--usr.bin/gprof/amd64.c11
-rw-r--r--usr.bin/gprof/amd64.h44
-rw-r--r--usr.bin/gprof/arcs.c950
-rw-r--r--usr.bin/gprof/dfn.c325
-rw-r--r--usr.bin/gprof/gprof.1292
-rw-r--r--usr.bin/gprof/gprof.c768
-rw-r--r--usr.bin/gprof/gprof.callg108
-rw-r--r--usr.bin/gprof/gprof.flat32
-rw-r--r--usr.bin/gprof/gprof.h347
-rw-r--r--usr.bin/gprof/hertz.c59
-rw-r--r--usr.bin/gprof/hp300.c11
-rw-r--r--usr.bin/gprof/hp300.h44
-rw-r--r--usr.bin/gprof/i386.c11
-rw-r--r--usr.bin/gprof/i386.h44
-rw-r--r--usr.bin/gprof/lookup.c115
-rw-r--r--usr.bin/gprof/mips.c112
-rw-r--r--usr.bin/gprof/mips.h50
-rw-r--r--usr.bin/gprof/pathnames.h38
-rw-r--r--usr.bin/gprof/printgprof.c728
-rw-r--r--usr.bin/gprof/printlist.c91
-rw-r--r--usr.bin/gprof/sparc.c110
-rw-r--r--usr.bin/gprof/sparc.h48
-rw-r--r--usr.bin/gprof/tahoe.c349
-rw-r--r--usr.bin/gprof/tahoe.h59
-rw-r--r--usr.bin/gprof/vax.c347
-rw-r--r--usr.bin/gprof/vax.h65
-rw-r--r--usr.bin/grep/egrep/Makefile18
-rw-r--r--usr.bin/grep/egrep/egrep.c924
-rw-r--r--usr.bin/grep/egrep/grep.1250
-rw-r--r--usr.bin/grep/egrep/pathnames.h40
-rw-r--r--usr.bin/head/Makefile5
-rw-r--r--usr.bin/head/head.169
-rw-r--r--usr.bin/head/head.c179
-rw-r--r--usr.bin/hexdump/Makefile8
-rw-r--r--usr.bin/hexdump/conv.c128
-rw-r--r--usr.bin/hexdump/display.c379
-rw-r--r--usr.bin/hexdump/hexdump.1324
-rw-r--r--usr.bin/hexdump/hexdump.c112
-rw-r--r--usr.bin/hexdump/hexdump.h98
-rw-r--r--usr.bin/hexdump/hexsyntax.c127
-rw-r--r--usr.bin/hexdump/od.176
-rw-r--r--usr.bin/hexdump/odsyntax.c263
-rw-r--r--usr.bin/hexdump/parse.c507
-rw-r--r--usr.bin/host/Makefile6
-rw-r--r--usr.bin/host/host.12
-rw-r--r--usr.bin/host/host.c61
-rw-r--r--usr.bin/id/Makefile13
-rw-r--r--usr.bin/id/groups.163
-rw-r--r--usr.bin/id/groups.sh37
-rw-r--r--usr.bin/id/id.1139
-rw-r--r--usr.bin/id/id.c351
-rw-r--r--usr.bin/id/whoami.161
-rw-r--r--usr.bin/id/whoami.sh37
-rw-r--r--usr.bin/indent/Makefile6
-rw-r--r--usr.bin/indent/README97
-rw-r--r--usr.bin/indent/args.c300
-rw-r--r--usr.bin/indent/indent.1452
-rw-r--r--usr.bin/indent/indent.c1181
-rw-r--r--usr.bin/indent/indent_codes.h69
-rw-r--r--usr.bin/indent/indent_globs.h310
-rw-r--r--usr.bin/indent/io.c625
-rw-r--r--usr.bin/indent/lexi.c559
-rw-r--r--usr.bin/indent/parse.c324
-rw-r--r--usr.bin/indent/pr_comment.c418
-rw-r--r--usr.bin/ipcrm/Makefile5
-rw-r--r--usr.bin/ipcrm/ipcrm.183
-rw-r--r--usr.bin/ipcrm/ipcrm.c172
-rw-r--r--usr.bin/ipcs/Makefile9
-rw-r--r--usr.bin/ipcs/ipcs.1151
-rw-r--r--usr.bin/ipcs/ipcs.c485
-rw-r--r--usr.bin/join/Makefile5
-rw-r--r--usr.bin/join/join.1206
-rw-r--r--usr.bin/join/join.c582
-rw-r--r--usr.bin/jot/Makefile5
-rw-r--r--usr.bin/jot/jot.1195
-rw-r--r--usr.bin/jot/jot.c393
-rw-r--r--usr.bin/kdump/Makefile12
-rw-r--r--usr.bin/kdump/kdump.1100
-rw-r--r--usr.bin/kdump/kdump.c438
-rw-r--r--usr.bin/kdump/mkioctls33
-rw-r--r--usr.bin/key/Makefile11
-rw-r--r--usr.bin/key/README.WZV100
-rw-r--r--usr.bin/key/key.149
-rw-r--r--usr.bin/key/skey.159
-rw-r--r--usr.bin/key/skey.c129
-rw-r--r--usr.bin/keyinfo/Makefile9
-rw-r--r--usr.bin/keyinfo/keyinfo.140
-rw-r--r--usr.bin/keyinfo/keyinfo.sh10
-rw-r--r--usr.bin/keyinit/Makefile13
-rw-r--r--usr.bin/keyinit/keyinit.164
-rw-r--r--usr.bin/keyinit/skeyinit.c191
-rw-r--r--usr.bin/killall/Makefile7
-rw-r--r--usr.bin/killall/killall.1130
-rwxr-xr-xusr.bin/killall/killall.pl103
-rw-r--r--usr.bin/ktrace/Makefile6
-rw-r--r--usr.bin/ktrace/ktrace.1163
-rw-r--r--usr.bin/ktrace/ktrace.c186
-rw-r--r--usr.bin/ktrace/ktrace.h41
-rw-r--r--usr.bin/ktrace/subr.c107
-rw-r--r--usr.bin/kzip/Makefile6
-rw-r--r--usr.bin/kzip/kzip.c348
-rw-r--r--usr.bin/lam/Makefile5
-rw-r--r--usr.bin/lam/lam.1127
-rw-r--r--usr.bin/lam/lam.c233
-rw-r--r--usr.bin/last/Makefile5
-rw-r--r--usr.bin/last/last.1128
-rw-r--r--usr.bin/last/last.c414
-rw-r--r--usr.bin/lastcomm/Makefile5
-rw-r--r--usr.bin/lastcomm/lastcomm.1124
-rw-r--r--usr.bin/lastcomm/lastcomm.c223
-rw-r--r--usr.bin/lastcomm/pathnames.h38
-rw-r--r--usr.bin/ld/Makefile7
-rw-r--r--usr.bin/ld/cplus-dem.c970
-rw-r--r--usr.bin/ld/ld.c4718
-rw-r--r--usr.bin/ld/symseg.h358
-rw-r--r--usr.bin/ldd/Makefile7
-rw-r--r--usr.bin/ldd/ldd.125
-rw-r--r--usr.bin/ldd/ldd.c139
-rw-r--r--usr.bin/leave/Makefile5
-rw-r--r--usr.bin/leave/leave.1102
-rw-r--r--usr.bin/leave/leave.c163
-rw-r--r--usr.bin/lex/COPYING38
-rw-r--r--usr.bin/lex/FlexLexer.h175
-rw-r--r--usr.bin/lex/Makefile55
-rw-r--r--usr.bin/lex/NEWS703
-rw-r--r--usr.bin/lex/README67
-rw-r--r--usr.bin/lex/ccl.c149
-rw-r--r--usr.bin/lex/dfa.c1085
-rw-r--r--usr.bin/lex/ecs.c225
-rw-r--r--usr.bin/lex/flex.11001
-rw-r--r--usr.bin/lex/flex.skl1225
-rw-r--r--usr.bin/lex/flexdef.h902
-rw-r--r--usr.bin/lex/flexdoc.13045
-rw-r--r--usr.bin/lex/gen.c1461
-rw-r--r--usr.bin/lex/initscan.c2723
-rw-r--r--usr.bin/lex/lex.11001
-rw-r--r--usr.bin/lex/lexdoc.13045
-rw-r--r--usr.bin/lex/lib/Makefile14
-rw-r--r--usr.bin/lex/lib/libmain.c12
-rw-r--r--usr.bin/lex/lib/libyywrap.c8
-rw-r--r--usr.bin/lex/main.c989
-rw-r--r--usr.bin/lex/misc.c773
-rwxr-xr-xusr.bin/lex/mkskel.sh16
-rw-r--r--usr.bin/lex/nfa.c709
-rw-r--r--usr.bin/lex/parse.y817
-rw-r--r--usr.bin/lex/scan.l572
-rw-r--r--usr.bin/lex/skel.c1232
-rw-r--r--usr.bin/lex/sym.c262
-rw-r--r--usr.bin/lex/tblcmp.c888
-rw-r--r--usr.bin/lex/version.h1
-rw-r--r--usr.bin/lex/yylex.c199
-rw-r--r--usr.bin/locate/Makefile5
-rw-r--r--usr.bin/locate/bigram/Makefile7
-rw-r--r--usr.bin/locate/bigram/locate.bigram.c84
-rw-r--r--usr.bin/locate/code/Makefile8
-rw-r--r--usr.bin/locate/code/locate.code.c212
-rw-r--r--usr.bin/locate/locate/Makefile10
-rw-r--r--usr.bin/locate/locate/locate.181
-rw-r--r--usr.bin/locate/locate/locate.c196
-rw-r--r--usr.bin/locate/locate/locate.h41
-rw-r--r--usr.bin/locate/locate/pathnames.h36
-rw-r--r--usr.bin/locate/locate/updatedb.csh77
-rw-r--r--usr.bin/lock/Makefile9
-rw-r--r--usr.bin/lock/lock.168
-rw-r--r--usr.bin/lock/lock.c223
-rw-r--r--usr.bin/logger/Makefile5
-rw-r--r--usr.bin/logger/logger.1100
-rw-r--r--usr.bin/logger/logger.c192
-rw-r--r--usr.bin/login/Makefile30
-rw-r--r--usr.bin/login/README20
-rw-r--r--usr.bin/login/klogin.c202
-rw-r--r--usr.bin/login/login.1156
-rw-r--r--usr.bin/login/login.access.550
-rw-r--r--usr.bin/login/login.c678
-rw-r--r--usr.bin/login/login_access.c236
-rw-r--r--usr.bin/login/login_fbtab.c153
-rw-r--r--usr.bin/login/login_skey.c105
-rw-r--r--usr.bin/login/pathnames.h43
-rw-r--r--usr.bin/logname/Makefile5
-rw-r--r--usr.bin/logname/logname.176
-rw-r--r--usr.bin/logname/logname.c81
-rw-r--r--usr.bin/look/Makefile5
-rw-r--r--usr.bin/look/look.1104
-rw-r--r--usr.bin/look/look.c357
-rw-r--r--usr.bin/look/pathnames.h36
-rw-r--r--usr.bin/lorder/Makefile9
-rw-r--r--usr.bin/lorder/lorder.173
-rw-r--r--usr.bin/lorder/lorder.sh89
-rw-r--r--usr.bin/lsvfs/Makefile4
-rw-r--r--usr.bin/lsvfs/lsvfs.155
-rw-r--r--usr.bin/lsvfs/lsvfs.c93
-rw-r--r--usr.bin/m4/Makefile10
-rw-r--r--usr.bin/m4/NOTES64
-rw-r--r--usr.bin/m4/PSD.doc/Makefile10
-rw-r--r--usr.bin/m4/TEST/ack.m440
-rw-r--r--usr.bin/m4/TEST/hanoi.m445
-rw-r--r--usr.bin/m4/TEST/hash.m455
-rw-r--r--usr.bin/m4/TEST/sqroot.m445
-rw-r--r--usr.bin/m4/TEST/string.m445
-rw-r--r--usr.bin/m4/TEST/test.m4243
-rw-r--r--usr.bin/m4/eval.c793
-rw-r--r--usr.bin/m4/expr.c574
-rw-r--r--usr.bin/m4/extern.h96
-rw-r--r--usr.bin/m4/look.c145
-rw-r--r--usr.bin/m4/m4.1181
-rw-r--r--usr.bin/m4/main.c425
-rw-r--r--usr.bin/m4/mdef.h176
-rw-r--r--usr.bin/m4/misc.c270
-rw-r--r--usr.bin/m4/pathnames.h57
-rw-r--r--usr.bin/m4/serv.c475
-rw-r--r--usr.bin/m4/stdd.h56
-rw-r--r--usr.bin/mail/Makefile19
-rw-r--r--usr.bin/mail/USD.doc/Makefile11
-rw-r--r--usr.bin/mail/USD.doc/mail0.nr71
-rw-r--r--usr.bin/mail/USD.doc/mail1.nr92
-rw-r--r--usr.bin/mail/USD.doc/mail2.nr617
-rw-r--r--usr.bin/mail/USD.doc/mail3.nr133
-rw-r--r--usr.bin/mail/USD.doc/mail4.nr437
-rw-r--r--usr.bin/mail/USD.doc/mail5.nr1041
-rw-r--r--usr.bin/mail/USD.doc/mail6.nr125
-rw-r--r--usr.bin/mail/USD.doc/mail7.nr107
-rw-r--r--usr.bin/mail/USD.doc/mail8.nr75
-rw-r--r--usr.bin/mail/USD.doc/mail9.nr203
-rw-r--r--usr.bin/mail/USD.doc/maila.nr33
-rw-r--r--usr.bin/mail/aux.c705
-rw-r--r--usr.bin/mail/cmd1.c451
-rw-r--r--usr.bin/mail/cmd2.c530
-rw-r--r--usr.bin/mail/cmd3.c730
-rw-r--r--usr.bin/mail/cmdtab.c117
-rw-r--r--usr.bin/mail/collect.c635
-rw-r--r--usr.bin/mail/def.h276
-rw-r--r--usr.bin/mail/edit.c220
-rw-r--r--usr.bin/mail/extern.h253
-rw-r--r--usr.bin/mail/fio.c431
-rw-r--r--usr.bin/mail/getname.c72
-rw-r--r--usr.bin/mail/glob.h100
-rw-r--r--usr.bin/mail/head.c254
-rw-r--r--usr.bin/mail/lex.c676
-rw-r--r--usr.bin/mail/list.c801
-rw-r--r--usr.bin/mail/mail.11032
-rw-r--r--usr.bin/mail/main.c296
-rw-r--r--usr.bin/mail/misc/mail.help23
-rw-r--r--usr.bin/mail/misc/mail.rc2
-rw-r--r--usr.bin/mail/misc/mail.tildehelp22
-rw-r--r--usr.bin/mail/names.c694
-rw-r--r--usr.bin/mail/pathnames.h42
-rw-r--r--usr.bin/mail/popen.c373
-rw-r--r--usr.bin/mail/quit.c491
-rw-r--r--usr.bin/mail/rcv.h44
-rw-r--r--usr.bin/mail/send.c556
-rw-r--r--usr.bin/mail/strings.c129
-rw-r--r--usr.bin/mail/temp.c111
-rw-r--r--usr.bin/mail/tty.c273
-rw-r--r--usr.bin/mail/v7.local.c83
-rw-r--r--usr.bin/mail/vars.c190
-rw-r--r--usr.bin/mail/version.c42
-rw-r--r--usr.bin/make/Makefile15
-rw-r--r--usr.bin/make/Makefile.dist7
-rw-r--r--usr.bin/make/PSD.doc/Makefile7
-rw-r--r--usr.bin/make/PSD.doc/tutorial.ms3732
-rw-r--r--usr.bin/make/arch.c1091
-rw-r--r--usr.bin/make/bit.h100
-rw-r--r--usr.bin/make/buf.c436
-rw-r--r--usr.bin/make/buf.h80
-rw-r--r--usr.bin/make/compat.c650
-rw-r--r--usr.bin/make/cond.c1252
-rw-r--r--usr.bin/make/config.h92
-rw-r--r--usr.bin/make/dir.c1274
-rw-r--r--usr.bin/make/dir.h70
-rw-r--r--usr.bin/make/for.c299
-rw-r--r--usr.bin/make/hash.c418
-rw-r--r--usr.bin/make/hash.h116
-rw-r--r--usr.bin/make/job.c2668
-rw-r--r--usr.bin/make/job.h233
-rw-r--r--usr.bin/make/list.h298
-rw-r--r--usr.bin/make/lst.h164
-rw-r--r--usr.bin/make/lst.lib/lstAppend.c113
-rw-r--r--usr.bin/make/lst.lib/lstAtEnd.c70
-rw-r--r--usr.bin/make/lst.lib/lstAtFront.c71
-rw-r--r--usr.bin/make/lst.lib/lstClose.c77
-rw-r--r--usr.bin/make/lst.lib/lstConcat.c176
-rw-r--r--usr.bin/make/lst.lib/lstDatum.c71
-rw-r--r--usr.bin/make/lst.lib/lstDeQueue.c81
-rw-r--r--usr.bin/make/lst.lib/lstDestroy.c102
-rw-r--r--usr.bin/make/lst.lib/lstDupl.c99
-rw-r--r--usr.bin/make/lst.lib/lstEnQueue.c73
-rw-r--r--usr.bin/make/lst.lib/lstFind.c70
-rw-r--r--usr.bin/make/lst.lib/lstFindFrom.c94
-rw-r--r--usr.bin/make/lst.lib/lstFirst.c71
-rw-r--r--usr.bin/make/lst.lib/lstForEach.c72
-rw-r--r--usr.bin/make/lst.lib/lstForEachFrom.c111
-rw-r--r--usr.bin/make/lst.lib/lstInit.c76
-rw-r--r--usr.bin/make/lst.lib/lstInsert.c113
-rw-r--r--usr.bin/make/lst.lib/lstInt.h110
-rw-r--r--usr.bin/make/lst.lib/lstIsAtEnd.c81
-rw-r--r--usr.bin/make/lst.lib/lstIsEmpty.c69
-rw-r--r--usr.bin/make/lst.lib/lstLast.c71
-rw-r--r--usr.bin/make/lst.lib/lstMember.c69
-rw-r--r--usr.bin/make/lst.lib/lstNext.c114
-rw-r--r--usr.bin/make/lst.lib/lstOpen.c81
-rw-r--r--usr.bin/make/lst.lib/lstRemove.c131
-rw-r--r--usr.bin/make/lst.lib/lstReplace.c73
-rw-r--r--usr.bin/make/lst.lib/lstSucc.c73
-rw-r--r--usr.bin/make/main.c954
-rw-r--r--usr.bin/make/make.1883
-rw-r--r--usr.bin/make/make.c901
-rw-r--r--usr.bin/make/make.h365
-rw-r--r--usr.bin/make/nonints.h140
-rw-r--r--usr.bin/make/parse.c2618
-rw-r--r--usr.bin/make/pathnames.h40
-rw-r--r--usr.bin/make/sprite.h111
-rw-r--r--usr.bin/make/str.c492
-rw-r--r--usr.bin/make/suff.c2435
-rw-r--r--usr.bin/make/targ.c657
-rw-r--r--usr.bin/make/var.c2033
-rw-r--r--usr.bin/man/Makefile8
-rw-r--r--usr.bin/man/config.c174
-rw-r--r--usr.bin/man/config.h57
-rw-r--r--usr.bin/man/man.1188
-rw-r--r--usr.bin/man/man.c712
-rw-r--r--usr.bin/man/man.conf46
-rw-r--r--usr.bin/man/man.conf.5195
-rw-r--r--usr.bin/man/pathnames.h39
-rw-r--r--usr.bin/mesg/Makefile5
-rw-r--r--usr.bin/mesg/mesg.191
-rw-r--r--usr.bin/mesg/mesg.c104
-rw-r--r--usr.bin/mk_cmds/Makefile16
-rw-r--r--usr.bin/mk_cmds/cmd_tbl.l79
-rw-r--r--usr.bin/mk_cmds/copyright.h19
-rw-r--r--usr.bin/mk_cmds/ct.y77
-rw-r--r--usr.bin/mk_cmds/mk_cmds.c94
-rw-r--r--usr.bin/mk_cmds/options.c32
-rw-r--r--usr.bin/mk_cmds/utils.c136
-rw-r--r--usr.bin/mkdep/Makefile16
-rw-r--r--usr.bin/mkdep/mkdep.1103
-rw-r--r--usr.bin/mkdep/mkdep.append123
-rw-r--r--usr.bin/mkdep/mkdep.gcc.sh98
-rw-r--r--usr.bin/mkdep/mkdep.old.compiler143
-rw-r--r--usr.bin/mkdep/mkdep.sh111
-rw-r--r--usr.bin/mkdep/mkdep.ultrix124
-rw-r--r--usr.bin/mkfifo/Makefile5
-rw-r--r--usr.bin/mkfifo/mkfifo.172
-rw-r--r--usr.bin/mkfifo/mkfifo.c82
-rw-r--r--usr.bin/mklocale/Japanese158
-rw-r--r--usr.bin/mklocale/Makefile27
-rw-r--r--usr.bin/mklocale/POSIX33
-rw-r--r--usr.bin/mklocale/README.locale_name7
-rw-r--r--usr.bin/mklocale/data/ja_JP.EUC158
-rw-r--r--usr.bin/mklocale/data/lt_LN.ISO8859-135
-rw-r--r--usr.bin/mklocale/data/lt_LN.ISO_8859-139
-rw-r--r--usr.bin/mklocale/data/ru_SU.CP86642
-rw-r--r--usr.bin/mklocale/data/ru_SU.KOI8-R39
-rw-r--r--usr.bin/mklocale/iso_8859_135
-rw-r--r--usr.bin/mklocale/ja_JP.EUC158
-rw-r--r--usr.bin/mklocale/ldef.h53
-rw-r--r--usr.bin/mklocale/lex.l152
-rw-r--r--usr.bin/mklocale/mklocale.1257
-rw-r--r--usr.bin/mklocale/ru_SU.KOI8-R39
-rw-r--r--usr.bin/mklocale/yacc.y823
-rw-r--r--usr.bin/mkstr/Makefile5
-rw-r--r--usr.bin/mkstr/mkstr.1137
-rw-r--r--usr.bin/mkstr/mkstr.c310
-rw-r--r--usr.bin/modstat/Makefile44
-rw-r--r--usr.bin/modstat/modstat.869
-rw-r--r--usr.bin/modstat/modstat.c178
-rw-r--r--usr.bin/modstat/pathnames.h3
-rw-r--r--usr.bin/more/Makefile16
-rw-r--r--usr.bin/more/ch.c454
-rw-r--r--usr.bin/more/command.c671
-rw-r--r--usr.bin/more/decode.c201
-rw-r--r--usr.bin/more/help.c49
-rw-r--r--usr.bin/more/input.c241
-rw-r--r--usr.bin/more/less.h87
-rw-r--r--usr.bin/more/line.c508
-rw-r--r--usr.bin/more/linenum.c383
-rw-r--r--usr.bin/more/main.c367
-rw-r--r--usr.bin/more/more.1300
-rw-r--r--usr.bin/more/more.help39
-rw-r--r--usr.bin/more/option.c128
-rw-r--r--usr.bin/more/os.c283
-rw-r--r--usr.bin/more/output.c257
-rw-r--r--usr.bin/more/pathnames.h38
-rw-r--r--usr.bin/more/position.c163
-rw-r--r--usr.bin/more/prim.c749
-rw-r--r--usr.bin/more/screen.c610
-rw-r--r--usr.bin/more/signal.c220
-rw-r--r--usr.bin/more/tags.c205
-rw-r--r--usr.bin/more/ttyin.c79
-rw-r--r--usr.bin/msgs/Makefile8
-rw-r--r--usr.bin/msgs/msgs.1214
-rw-r--r--usr.bin/msgs/msgs.c862
-rw-r--r--usr.bin/msgs/pathnames.h40
-rw-r--r--usr.bin/mt/Makefile6
-rw-r--r--usr.bin/mt/mt.1222
-rw-r--r--usr.bin/mt/mt.c460
-rw-r--r--usr.bin/ncftp/.cvsimport.sh5
-rw-r--r--usr.bin/ncftp/Blurb66
-rw-r--r--usr.bin/ncftp/Makefile17
-rw-r--r--usr.bin/ncftp/Makefile.ORIG287
-rw-r--r--usr.bin/ncftp/README296
-rw-r--r--usr.bin/ncftp/cmds.c2223
-rw-r--r--usr.bin/ncftp/cmds.h132
-rw-r--r--usr.bin/ncftp/cmdtab.c277
-rw-r--r--usr.bin/ncftp/copyright.h25
-rw-r--r--usr.bin/ncftp/defaults.h138
-rw-r--r--usr.bin/ncftp/ftp.c1918
-rw-r--r--usr.bin/ncftp/ftp.h67
-rw-r--r--usr.bin/ncftp/ftprc.c615
-rw-r--r--usr.bin/ncftp/ftprc.h39
-rw-r--r--usr.bin/ncftp/getpass.c160
-rw-r--r--usr.bin/ncftp/getpass.h23
-rw-r--r--usr.bin/ncftp/glob.c655
-rw-r--r--usr.bin/ncftp/glob.h22
-rw-r--r--usr.bin/ncftp/main.c1160
-rw-r--r--usr.bin/ncftp/main.h45
-rw-r--r--usr.bin/ncftp/ncftp.11399
-rw-r--r--usr.bin/ncftp/open.c644
-rw-r--r--usr.bin/ncftp/open.h50
-rw-r--r--usr.bin/ncftp/patchlevel.h156
-rw-r--r--usr.bin/ncftp/set.c368
-rw-r--r--usr.bin/ncftp/set.h46
-rw-r--r--usr.bin/ncftp/sys.h636
-rw-r--r--usr.bin/ncftp/tips.c147
-rw-r--r--usr.bin/ncftp/util.c922
-rw-r--r--usr.bin/ncftp/util.h104
-rw-r--r--usr.bin/ncftp/v2_Note35
-rw-r--r--usr.bin/netstat/Makefile13
-rw-r--r--usr.bin/netstat/if.c415
-rw-r--r--usr.bin/netstat/inet.c510
-rw-r--r--usr.bin/netstat/iso.c843
-rw-r--r--usr.bin/netstat/main.c512
-rw-r--r--usr.bin/netstat/mbuf.c121
-rw-r--r--usr.bin/netstat/mroute.c235
-rw-r--r--usr.bin/netstat/netstat.1297
-rw-r--r--usr.bin/netstat/netstat.h110
-rw-r--r--usr.bin/netstat/ns.c351
-rw-r--r--usr.bin/netstat/route.c700
-rw-r--r--usr.bin/netstat/unix.c134
-rw-r--r--usr.bin/nfsstat/Makefile10
-rw-r--r--usr.bin/nfsstat/nfsstat.190
-rw-r--r--usr.bin/nfsstat/nfsstat.c404
-rw-r--r--usr.bin/nice/Makefile5
-rw-r--r--usr.bin/nice/nice.197
-rw-r--r--usr.bin/nice/nice.c93
-rw-r--r--usr.bin/nm/Makefile5
-rw-r--r--usr.bin/nm/nm.1117
-rw-r--r--usr.bin/nm/nm.1aout117
-rw-r--r--usr.bin/nm/nm.c630
-rw-r--r--usr.bin/nohup/Makefile5
-rw-r--r--usr.bin/nohup/nohup.190
-rw-r--r--usr.bin/nohup/nohup.c117
-rw-r--r--usr.bin/pagesize/Makefile9
-rw-r--r--usr.bin/pagesize/pagesize.156
-rw-r--r--usr.bin/pagesize/pagesize.sh40
-rw-r--r--usr.bin/passwd/Makefile42
-rw-r--r--usr.bin/passwd/extern.h38
-rw-r--r--usr.bin/passwd/kpasswd_proto.h54
-rw-r--r--usr.bin/passwd/krb_passwd.c319
-rw-r--r--usr.bin/passwd/local_passwd.c169
-rw-r--r--usr.bin/passwd/passwd.1160
-rw-r--r--usr.bin/passwd/passwd.c209
-rw-r--r--usr.bin/passwd/yp_passwd.c181
-rw-r--r--usr.bin/passwd/yppasswd.1115
-rw-r--r--usr.bin/paste/Makefile5
-rw-r--r--usr.bin/paste/paste.1119
-rw-r--r--usr.bin/paste/paste.c251
-rw-r--r--usr.bin/patch/EXTERN.h15
-rw-r--r--usr.bin/patch/INTERN.h15
-rw-r--r--usr.bin/patch/Makefile6
-rw-r--r--usr.bin/patch/README79
-rw-r--r--usr.bin/patch/common.h138
-rw-r--r--usr.bin/patch/config.h16
-rw-r--r--usr.bin/patch/inp.c313
-rw-r--r--usr.bin/patch/inp.h18
-rw-r--r--usr.bin/patch/patch.1446
-rw-r--r--usr.bin/patch/patch.c800
-rw-r--r--usr.bin/patch/patchlevel.h1
-rw-r--r--usr.bin/patch/pch.c1108
-rw-r--r--usr.bin/patch/pch.h36
-rw-r--r--usr.bin/patch/util.c339
-rw-r--r--usr.bin/patch/util.h74
-rw-r--r--usr.bin/patch/version.c28
-rw-r--r--usr.bin/patch/version.h9
-rw-r--r--usr.bin/pr/Makefile6
-rw-r--r--usr.bin/pr/egetopt.c215
-rw-r--r--usr.bin/pr/extern.h60
-rw-r--r--usr.bin/pr/pr.1347
-rw-r--r--usr.bin/pr/pr.c1804
-rw-r--r--usr.bin/pr/pr.h72
-rw-r--r--usr.bin/printenv/Makefile6
-rw-r--r--usr.bin/printenv/printenv.198
-rw-r--r--usr.bin/printenv/printenv.c99
-rw-r--r--usr.bin/printf/Makefile5
-rw-r--r--usr.bin/printf/printf.1272
-rw-r--r--usr.bin/printf/printf.c417
-rw-r--r--usr.bin/quota/Makefile7
-rw-r--r--usr.bin/quota/quota.1131
-rw-r--r--usr.bin/quota/quota.c513
-rw-r--r--usr.bin/ranlib/Makefile10
-rw-r--r--usr.bin/ranlib/build.c288
-rw-r--r--usr.bin/ranlib/misc.c104
-rw-r--r--usr.bin/ranlib/pathnames.h36
-rw-r--r--usr.bin/ranlib/ranlib.188
-rw-r--r--usr.bin/ranlib/ranlib.1aout88
-rw-r--r--usr.bin/ranlib/ranlib.570
-rw-r--r--usr.bin/ranlib/ranlib.5.570
-rw-r--r--usr.bin/ranlib/ranlib.c89
-rw-r--r--usr.bin/ranlib/touch.c85
-rw-r--r--usr.bin/rdist/Makefile12
-rw-r--r--usr.bin/rdist/cron.entry1
-rw-r--r--usr.bin/rdist/defs.h180
-rw-r--r--usr.bin/rdist/docmd.c638
-rw-r--r--usr.bin/rdist/expand.c666
-rw-r--r--usr.bin/rdist/gram.y519
-rw-r--r--usr.bin/rdist/lookup.c166
-rw-r--r--usr.bin/rdist/main.c327
-rw-r--r--usr.bin/rdist/pathnames.h38
-rw-r--r--usr.bin/rdist/rdist.1413
-rw-r--r--usr.bin/rdist/server.c1584
-rw-r--r--usr.bin/renice/Makefile6
-rw-r--r--usr.bin/renice/renice.8131
-rw-r--r--usr.bin/renice/renice.c128
-rw-r--r--usr.bin/rev/Makefile5
-rw-r--r--usr.bin/rev/rev.148
-rw-r--r--usr.bin/rev/rev.c109
-rw-r--r--usr.bin/rlogin/Makefile19
-rw-r--r--usr.bin/rlogin/des_rw.c203
-rw-r--r--usr.bin/rlogin/kcmd.c310
-rw-r--r--usr.bin/rlogin/krb.h38
-rw-r--r--usr.bin/rlogin/krcmd.c158
-rw-r--r--usr.bin/rlogin/rlogin.1188
-rw-r--r--usr.bin/rlogin/rlogin.c949
-rw-r--r--usr.bin/rpcgen/Makefile7
-rw-r--r--usr.bin/rpcgen/rpc_clntout.c130
-rw-r--r--usr.bin/rpcgen/rpc_cout.c355
-rw-r--r--usr.bin/rpcgen/rpc_hout.c374
-rw-r--r--usr.bin/rpcgen/rpc_main.c444
-rw-r--r--usr.bin/rpcgen/rpc_parse.c424
-rw-r--r--usr.bin/rpcgen/rpc_parse.h159
-rw-r--r--usr.bin/rpcgen/rpc_scan.c476
-rw-r--r--usr.bin/rpcgen/rpc_scan.h103
-rw-r--r--usr.bin/rpcgen/rpc_svcout.c276
-rw-r--r--usr.bin/rpcgen/rpc_util.c439
-rw-r--r--usr.bin/rpcgen/rpc_util.h113
-rw-r--r--usr.bin/rpcgen/rpcgen.1156
-rw-r--r--usr.bin/rpcinfo/Makefile8
-rw-r--r--usr.bin/rpcinfo/rpcinfo.8166
-rw-r--r--usr.bin/rpcinfo/rpcinfo.c666
-rw-r--r--usr.bin/rs/Makefile5
-rw-r--r--usr.bin/rs/rs.1197
-rw-r--r--usr.bin/rs/rs.c546
-rw-r--r--usr.bin/rsh/Makefile20
-rw-r--r--usr.bin/rsh/pathnames.h36
-rw-r--r--usr.bin/rsh/rsh.1181
-rw-r--r--usr.bin/rsh/rsh.c478
-rw-r--r--usr.bin/rup/Makefile9
-rw-r--r--usr.bin/rup/rup.194
-rw-r--r--usr.bin/rup/rup.c227
-rw-r--r--usr.bin/ruptime/Makefile5
-rw-r--r--usr.bin/ruptime/ruptime.181
-rw-r--r--usr.bin/ruptime/ruptime.c282
-rw-r--r--usr.bin/rusers/Makefile9
-rw-r--r--usr.bin/rusers/rusers.193
-rw-r--r--usr.bin/rusers/rusers.c249
-rw-r--r--usr.bin/rwall/Makefile9
-rw-r--r--usr.bin/rwall/rwall.179
-rw-r--r--usr.bin/rwall/rwall.c174
-rw-r--r--usr.bin/rwho/Makefile5
-rw-r--r--usr.bin/rwho/rwho.180
-rw-r--r--usr.bin/rwho/rwho.c187
-rw-r--r--usr.bin/sasc/INSTALL86
-rw-r--r--usr.bin/sasc/Makefile5
-rw-r--r--usr.bin/sasc/README9
-rw-r--r--usr.bin/sasc/sasc.194
-rw-r--r--usr.bin/sasc/sasc.c196
-rw-r--r--usr.bin/sccs/Makefile5
-rw-r--r--usr.bin/sccs/PSD.doc/Makefile10
-rw-r--r--usr.bin/sccs/PSD.doc/sccs.me1608
-rw-r--r--usr.bin/sccs/PSD.doc/spell.ok77
-rw-r--r--usr.bin/sccs/pathnames.h51
-rw-r--r--usr.bin/sccs/sccs.1398
-rw-r--r--usr.bin/sccs/sccs.c1621
-rw-r--r--usr.bin/script/Makefile7
-rw-r--r--usr.bin/script/script.1123
-rw-r--r--usr.bin/script/script.c267
-rw-r--r--usr.bin/sed/Makefile6
-rw-r--r--usr.bin/sed/POSIX198
-rw-r--r--usr.bin/sed/TEST/hanoi.sed102
-rw-r--r--usr.bin/sed/TEST/math.sed163
-rw-r--r--usr.bin/sed/TEST/sed.test552
-rw-r--r--usr.bin/sed/compile.c810
-rw-r--r--usr.bin/sed/defs.h145
-rw-r--r--usr.bin/sed/extern.h59
-rw-r--r--usr.bin/sed/main.c352
-rw-r--r--usr.bin/sed/misc.c141
-rw-r--r--usr.bin/sed/process.c631
-rw-r--r--usr.bin/sed/sed.1514
-rw-r--r--usr.bin/sgmlfmt/Makefile9
-rw-r--r--usr.bin/sgmlfmt/sgmlfmt.1185
-rwxr-xr-xusr.bin/sgmlfmt/sgmlfmt.pl722
-rw-r--r--usr.bin/sgmls/LICENSE43
-rw-r--r--usr.bin/sgmls/Makefile9
-rw-r--r--usr.bin/sgmls/Makefile.inc13
-rw-r--r--usr.bin/sgmls/README138
-rwxr-xr-xusr.bin/sgmls/configure617
-rw-r--r--usr.bin/sgmls/libsgmls/Makefile17
-rw-r--r--usr.bin/sgmls/libsgmls/sgmls.c1036
-rw-r--r--usr.bin/sgmls/libsgmls/sgmls.h127
-rw-r--r--usr.bin/sgmls/rast/Makefile18
-rw-r--r--usr.bin/sgmls/rast/rast.175
-rw-r--r--usr.bin/sgmls/rast/rast.c534
-rwxr-xr-xusr.bin/sgmls/sgmls.pl247
-rw-r--r--usr.bin/sgmls/sgmls/Makefile18
-rw-r--r--usr.bin/sgmls/sgmls/action.h179
-rw-r--r--usr.bin/sgmls/sgmls/adl.h118
-rw-r--r--usr.bin/sgmls/sgmls/ambig.c438
-rw-r--r--usr.bin/sgmls/sgmls/appl.h33
-rw-r--r--usr.bin/sgmls/sgmls/config.h147
-rw-r--r--usr.bin/sgmls/sgmls/context.c444
-rw-r--r--usr.bin/sgmls/sgmls/context.h17
-rw-r--r--usr.bin/sgmls/sgmls/dosproc.c40
-rw-r--r--usr.bin/sgmls/sgmls/ebcdic.c42
-rw-r--r--usr.bin/sgmls/sgmls/ebcdic.h40
-rw-r--r--usr.bin/sgmls/sgmls/entgen.c405
-rw-r--r--usr.bin/sgmls/sgmls/entity.h189
-rw-r--r--usr.bin/sgmls/sgmls/error.h61
-rw-r--r--usr.bin/sgmls/sgmls/etype.h91
-rw-r--r--usr.bin/sgmls/sgmls/exclude.c121
-rw-r--r--usr.bin/sgmls/sgmls/genlex.c114
-rw-r--r--usr.bin/sgmls/sgmls/getopt.c166
-rw-r--r--usr.bin/sgmls/sgmls/getopt.h11
-rw-r--r--usr.bin/sgmls/sgmls/keyword.h22
-rw-r--r--usr.bin/sgmls/sgmls/latin1.h51
-rw-r--r--usr.bin/sgmls/sgmls/lexcode.h11
-rw-r--r--usr.bin/sgmls/sgmls/lexrf.c124
-rw-r--r--usr.bin/sgmls/sgmls/lextaba.c559
-rw-r--r--usr.bin/sgmls/sgmls/lextabe.c184
-rw-r--r--usr.bin/sgmls/sgmls/lextoke.h10
-rw-r--r--usr.bin/sgmls/sgmls/lineout.c653
-rw-r--r--usr.bin/sgmls/sgmls/lineout.h23
-rw-r--r--usr.bin/sgmls/sgmls/main.c602
-rw-r--r--usr.bin/sgmls/sgmls/md1.c862
-rw-r--r--usr.bin/sgmls/sgmls/md2.c801
-rw-r--r--usr.bin/sgmls/sgmls/msg.h252
-rw-r--r--usr.bin/sgmls/sgmls/msgcat.c833
-rw-r--r--usr.bin/sgmls/sgmls/msgcat.h13
-rw-r--r--usr.bin/sgmls/sgmls/pars1.c958
-rw-r--r--usr.bin/sgmls/sgmls/pars2.c1308
-rw-r--r--usr.bin/sgmls/sgmls/pcbrf.c1344
-rw-r--r--usr.bin/sgmls/sgmls/portproc.c104
-rw-r--r--usr.bin/sgmls/sgmls/serv.c299
-rw-r--r--usr.bin/sgmls/sgmls/sgml1.c477
-rw-r--r--usr.bin/sgmls/sgmls/sgml2.c499
-rw-r--r--usr.bin/sgmls/sgmls/sgmlaux.h70
-rw-r--r--usr.bin/sgmls/sgmls/sgmldecl.c1741
-rw-r--r--usr.bin/sgmls/sgmls/sgmldecl.h84
-rw-r--r--usr.bin/sgmls/sgmls/sgmlfnsm.h129
-rw-r--r--usr.bin/sgmls/sgmls/sgmlincl.h20
-rw-r--r--usr.bin/sgmls/sgmls/sgmlio.c384
-rw-r--r--usr.bin/sgmls/sgmls/sgmlmain.h101
-rw-r--r--usr.bin/sgmls/sgmls/sgmlmsg.c514
-rw-r--r--usr.bin/sgmls/sgmls/sgmls.1871
-rw-r--r--usr.bin/sgmls/sgmls/sgmlxtrn.c223
-rw-r--r--usr.bin/sgmls/sgmls/sgmlxtrn.h121
-rw-r--r--usr.bin/sgmls/sgmls/source.h114
-rw-r--r--usr.bin/sgmls/sgmls/std.h116
-rw-r--r--usr.bin/sgmls/sgmls/stklen.c2
-rw-r--r--usr.bin/sgmls/sgmls/strerror.c36
-rw-r--r--usr.bin/sgmls/sgmls/synrf.c72
-rw-r--r--usr.bin/sgmls/sgmls/synxtrn.h152
-rw-r--r--usr.bin/sgmls/sgmls/tools.h76
-rw-r--r--usr.bin/sgmls/sgmls/trace.h113
-rw-r--r--usr.bin/sgmls/sgmls/traceset.c465
-rw-r--r--usr.bin/sgmls/sgmls/unix.cfg147
-rw-r--r--usr.bin/sgmls/sgmls/unixproc.c98
-rw-r--r--usr.bin/sgmls/sgmls/version.c1
-rw-r--r--usr.bin/sgmls/sgmls/xfprintf.c568
-rw-r--r--usr.bin/sgmls/sgmlsasp/Makefile18
-rw-r--r--usr.bin/sgmls/sgmlsasp/replace.c468
-rw-r--r--usr.bin/sgmls/sgmlsasp/replace.h35
-rw-r--r--usr.bin/sgmls/sgmlsasp/sgmlsasp.130
-rw-r--r--usr.bin/sgmls/sgmlsasp/sgmlsasp.c278
-rw-r--r--usr.bin/sgmls/sgmlsasp/sgmlsasp.h26
-rw-r--r--usr.bin/sgmls/unix.cfg147
-rw-r--r--usr.bin/shar/Makefile9
-rw-r--r--usr.bin/shar/shar.1102
-rw-r--r--usr.bin/shar/shar.sh74
-rw-r--r--usr.bin/showmount/Makefile6
-rw-r--r--usr.bin/showmount/showmount.893
-rw-r--r--usr.bin/showmount/showmount.c355
-rw-r--r--usr.bin/size/Makefile5
-rw-r--r--usr.bin/size/size.160
-rw-r--r--usr.bin/size/size.1aout60
-rw-r--r--usr.bin/size/size.c149
-rw-r--r--usr.bin/soelim/Makefile5
-rw-r--r--usr.bin/soelim/soelim.188
-rw-r--r--usr.bin/soelim/soelim.c160
-rw-r--r--usr.bin/sort/sort.1310
-rw-r--r--usr.bin/split/Makefile5
-rw-r--r--usr.bin/split/split.199
-rw-r--r--usr.bin/split/split.c288
-rw-r--r--usr.bin/strings/Makefile5
-rw-r--r--usr.bin/strings/strings.196
-rw-r--r--usr.bin/strings/strings.1aout96
-rw-r--r--usr.bin/strings/strings.c216
-rw-r--r--usr.bin/strip/Makefile19
-rw-r--r--usr.bin/strip/strip.172
-rw-r--r--usr.bin/strip/strip.1aout72
-rw-r--r--usr.bin/strip/strip.c278
-rw-r--r--usr.bin/su/Makefile26
-rw-r--r--usr.bin/su/su.1199
-rw-r--r--usr.bin/su/su.c462
-rw-r--r--usr.bin/symorder/Makefile5
-rw-r--r--usr.bin/symorder/symorder.196
-rw-r--r--usr.bin/symorder/symorder.c349
-rw-r--r--usr.bin/systat/Makefile12
-rw-r--r--usr.bin/systat/cmds.c192
-rw-r--r--usr.bin/systat/cmdtab.c62
-rw-r--r--usr.bin/systat/disks.c205
-rw-r--r--usr.bin/systat/extern.h119
-rw-r--r--usr.bin/systat/fetch.c54
-rw-r--r--usr.bin/systat/iostat.c386
-rw-r--r--usr.bin/systat/keyboard.c119
-rw-r--r--usr.bin/systat/main.c293
-rw-r--r--usr.bin/systat/mbufs.c163
-rw-r--r--usr.bin/systat/netcmds.c309
-rw-r--r--usr.bin/systat/netstat.c463
-rw-r--r--usr.bin/systat/pigs.c245
-rw-r--r--usr.bin/systat/swap.c263
-rw-r--r--usr.bin/systat/systat.1424
-rw-r--r--usr.bin/systat/systat.h60
-rw-r--r--usr.bin/systat/vmstat.c675
-rw-r--r--usr.bin/tail/Makefile6
-rw-r--r--usr.bin/tail/extern.h53
-rw-r--r--usr.bin/tail/forward.c234
-rw-r--r--usr.bin/tail/misc.c91
-rw-r--r--usr.bin/tail/read.c198
-rw-r--r--usr.bin/tail/reverse.c259
-rw-r--r--usr.bin/tail/tail.1165
-rw-r--r--usr.bin/tail/tail.c302
-rw-r--r--usr.bin/talk/Makefile10
-rw-r--r--usr.bin/talk/ctl.c113
-rw-r--r--usr.bin/talk/ctl_transact.c113
-rw-r--r--usr.bin/talk/display.c174
-rw-r--r--usr.bin/talk/get_addrs.c83
-rw-r--r--usr.bin/talk/get_names.c118
-rw-r--r--usr.bin/talk/init_disp.c168
-rw-r--r--usr.bin/talk/invite.c190
-rw-r--r--usr.bin/talk/io.c141
-rw-r--r--usr.bin/talk/look_up.c115
-rw-r--r--usr.bin/talk/msgs.c78
-rw-r--r--usr.bin/talk/talk.1133
-rw-r--r--usr.bin/talk/talk.c76
-rw-r--r--usr.bin/talk/talk.h58
-rw-r--r--usr.bin/talk/talk_ctl.h43
-rw-r--r--usr.bin/tconv/Makefile12
-rw-r--r--usr.bin/tconv/quit.c72
-rw-r--r--usr.bin/tconv/tconv.1180
-rw-r--r--usr.bin/tconv/tconv.c1384
-rw-r--r--usr.bin/tcopy/Makefile5
-rw-r--r--usr.bin/tcopy/pathnames.h36
-rw-r--r--usr.bin/tcopy/tcopy.189
-rw-r--r--usr.bin/tcopy/tcopy.c332
-rw-r--r--usr.bin/tee/Makefile5
-rw-r--r--usr.bin/tee/tee.188
-rw-r--r--usr.bin/tee/tee.c168
-rw-r--r--usr.bin/telnet/Makefile59
-rw-r--r--usr.bin/telnet/README566
-rw-r--r--usr.bin/telnet/authenc.c105
-rw-r--r--usr.bin/telnet/commands.c2779
-rw-r--r--usr.bin/telnet/defines.h61
-rw-r--r--usr.bin/telnet/externs.h477
-rw-r--r--usr.bin/telnet/fdset.h49
-rw-r--r--usr.bin/telnet/general.h45
-rw-r--r--usr.bin/telnet/krb4-proto.h207
-rw-r--r--usr.bin/telnet/main.c313
-rw-r--r--usr.bin/telnet/network.c173
-rw-r--r--usr.bin/telnet/ring.c315
-rw-r--r--usr.bin/telnet/ring.h96
-rw-r--r--usr.bin/telnet/sys_bsd.c1183
-rw-r--r--usr.bin/telnet/telnet.11360
-rw-r--r--usr.bin/telnet/telnet.c2559
-rw-r--r--usr.bin/telnet/terminal.c222
-rw-r--r--usr.bin/telnet/tn3270.c411
-rw-r--r--usr.bin/telnet/types.h52
-rw-r--r--usr.bin/telnet/utilities.c869
-rw-r--r--usr.bin/tftp/Makefile6
-rw-r--r--usr.bin/tftp/extern.h37
-rw-r--r--usr.bin/tftp/main.c733
-rw-r--r--usr.bin/tftp/tftp.1173
-rw-r--r--usr.bin/tftp/tftp.c453
-rw-r--r--usr.bin/tftp/tftpsubs.c273
-rw-r--r--usr.bin/tftp/tftpsubs.h48
-rw-r--r--usr.bin/time/Makefile5
-rw-r--r--usr.bin/time/time.199
-rw-r--r--usr.bin/time/time.c140
-rw-r--r--usr.bin/timedef/Makefile13
-rw-r--r--usr.bin/timedef/data/ru_SU.KOI8-R76
-rw-r--r--usr.bin/tip/Makefile3
-rw-r--r--usr.bin/tip/Makefile.inc5
-rw-r--r--usr.bin/tip/NEWS65
-rw-r--r--usr.bin/tip/README63
-rw-r--r--usr.bin/tip/TODO38
-rw-r--r--usr.bin/tip/acu.c196
-rw-r--r--usr.bin/tip/aculib/biz22.c187
-rw-r--r--usr.bin/tip/aculib/biz31.c248
-rw-r--r--usr.bin/tip/aculib/courier.c380
-rw-r--r--usr.bin/tip/aculib/df.c132
-rw-r--r--usr.bin/tip/aculib/dn11.c142
-rw-r--r--usr.bin/tip/aculib/hayes.c305
-rw-r--r--usr.bin/tip/aculib/t3000.c408
-rw-r--r--usr.bin/tip/aculib/v3451.c214
-rw-r--r--usr.bin/tip/aculib/v831.c259
-rw-r--r--usr.bin/tip/aculib/ventel.c251
-rw-r--r--usr.bin/tip/acutab.c97
-rw-r--r--usr.bin/tip/cmds.c888
-rw-r--r--usr.bin/tip/cmdtab.c64
-rw-r--r--usr.bin/tip/cu.c132
-rw-r--r--usr.bin/tip/hunt.c93
-rw-r--r--usr.bin/tip/libacu/Makefile12
-rw-r--r--usr.bin/tip/libacu/acucommon.c190
-rw-r--r--usr.bin/tip/libacu/acucommon.h6
-rw-r--r--usr.bin/tip/libacu/biz22.c188
-rw-r--r--usr.bin/tip/libacu/biz31.c249
-rw-r--r--usr.bin/tip/libacu/courier.c334
-rw-r--r--usr.bin/tip/libacu/df.c129
-rw-r--r--usr.bin/tip/libacu/dn11.c154
-rw-r--r--usr.bin/tip/libacu/hayes.c306
-rw-r--r--usr.bin/tip/libacu/multitech.c402
-rw-r--r--usr.bin/tip/libacu/t3000.c350
-rw-r--r--usr.bin/tip/libacu/tod.c107
-rw-r--r--usr.bin/tip/libacu/tod.h9
-rw-r--r--usr.bin/tip/libacu/unidialer.c800
-rw-r--r--usr.bin/tip/libacu/v3451.c215
-rw-r--r--usr.bin/tip/libacu/v831.c272
-rw-r--r--usr.bin/tip/libacu/ventel.c252
-rw-r--r--usr.bin/tip/log.c86
-rw-r--r--usr.bin/tip/partab.c58
-rw-r--r--usr.bin/tip/pathnames.h44
-rw-r--r--usr.bin/tip/remcap.c426
-rw-r--r--usr.bin/tip/remote.c226
-rw-r--r--usr.bin/tip/tip.1451
-rw-r--r--usr.bin/tip/tip.c608
-rw-r--r--usr.bin/tip/tip.h280
-rw-r--r--usr.bin/tip/tip/Makefile24
-rw-r--r--usr.bin/tip/tip/acu.c206
-rw-r--r--usr.bin/tip/tip/acutab.c114
-rw-r--r--usr.bin/tip/tip/cmds.c1042
-rw-r--r--usr.bin/tip/tip/cmdtab.c65
-rw-r--r--usr.bin/tip/tip/cu.c136
-rwxr-xr-xusr.bin/tip/tip/dial.sh21
-rw-r--r--usr.bin/tip/tip/hunt.c106
-rw-r--r--usr.bin/tip/tip/log.c87
-rw-r--r--usr.bin/tip/tip/modems.5140
-rw-r--r--usr.bin/tip/tip/partab.c58
-rw-r--r--usr.bin/tip/tip/pathnames.h58
-rw-r--r--usr.bin/tip/tip/remcap.c426
-rw-r--r--usr.bin/tip/tip/remote.c286
-rw-r--r--usr.bin/tip/tip/tip.1461
-rw-r--r--usr.bin/tip/tip/tip.c677
-rw-r--r--usr.bin/tip/tip/tip.h308
-rw-r--r--usr.bin/tip/tip/tipconf.h124
-rw-r--r--usr.bin/tip/tip/tipout.c170
-rw-r--r--usr.bin/tip/tip/uucplock.c146
-rw-r--r--usr.bin/tip/tip/value.c353
-rw-r--r--usr.bin/tip/tip/vars.c117
-rw-r--r--usr.bin/tip/tipout.c166
-rw-r--r--usr.bin/tip/uucplock.c102
-rw-r--r--usr.bin/tip/value.c353
-rw-r--r--usr.bin/tip/vars.c112
-rw-r--r--usr.bin/tn3270/Makefile17
-rw-r--r--usr.bin/tn3270/Makefile.inc4
-rw-r--r--usr.bin/tn3270/api/api_bsd.c281
-rw-r--r--usr.bin/tn3270/api/api_exch.c429
-rw-r--r--usr.bin/tn3270/api/api_exch.h161
-rw-r--r--usr.bin/tn3270/api/apilib.c411
-rw-r--r--usr.bin/tn3270/api/apilib.h44
-rw-r--r--usr.bin/tn3270/api/asc_ebc.c110
-rw-r--r--usr.bin/tn3270/api/asc_ebc.h51
-rw-r--r--usr.bin/tn3270/api/astosc.c98
-rw-r--r--usr.bin/tn3270/api/astosc.h58
-rw-r--r--usr.bin/tn3270/api/dctype.c245
-rw-r--r--usr.bin/tn3270/api/dctype.h54
-rw-r--r--usr.bin/tn3270/api/disp_asc.c45
-rw-r--r--usr.bin/tn3270/api/disp_asc.h43
-rw-r--r--usr.bin/tn3270/api/ebc_disp.c106
-rw-r--r--usr.bin/tn3270/api/ebc_disp.h38
-rw-r--r--usr.bin/tn3270/ascii/default.map79
-rw-r--r--usr.bin/tn3270/ascii/map3270.c934
-rw-r--r--usr.bin/tn3270/ascii/map3270.h41
-rw-r--r--usr.bin/tn3270/ascii/mset.c410
-rw-r--r--usr.bin/tn3270/ascii/state.h50
-rw-r--r--usr.bin/tn3270/ascii/termin.c281
-rw-r--r--usr.bin/tn3270/ctlr/3180.kbd182
-rw-r--r--usr.bin/tn3270/ctlr/3270pc.kbd182
-rw-r--r--usr.bin/tn3270/ctlr/api.c760
-rw-r--r--usr.bin/tn3270/ctlr/api.h403
-rw-r--r--usr.bin/tn3270/ctlr/declare.h53
-rw-r--r--usr.bin/tn3270/ctlr/externs.h66
-rw-r--r--usr.bin/tn3270/ctlr/function.c47
-rw-r--r--usr.bin/tn3270/ctlr/function.h166
-rw-r--r--usr.bin/tn3270/ctlr/hostctlr.h222
-rw-r--r--usr.bin/tn3270/ctlr/inbound.c1194
-rw-r--r--usr.bin/tn3270/ctlr/oia.c51
-rw-r--r--usr.bin/tn3270/ctlr/oia.h190
-rw-r--r--usr.bin/tn3270/ctlr/options.c181
-rw-r--r--usr.bin/tn3270/ctlr/options.h41
-rw-r--r--usr.bin/tn3270/ctlr/outbound.c605
-rw-r--r--usr.bin/tn3270/ctlr/screen.h145
-rw-r--r--usr.bin/tn3270/ctlr/scrnctlr.h48
-rw-r--r--usr.bin/tn3270/ctlr/unix.kbd184
-rw-r--r--usr.bin/tn3270/distribution/README99
-rw-r--r--usr.bin/tn3270/distribution/arpa/makefile67
-rw-r--r--usr.bin/tn3270/distribution/arpa/telnet.h191
-rw-r--r--usr.bin/tn3270/distribution/makefile_4.2268
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/makefile127
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/spint.h49
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/spintasm.asm252
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/spintc.c186
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/support.asm60
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/system.c141
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/termout.c514
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/termout.ext47
-rw-r--r--usr.bin/tn3270/distribution/sys_dos/video.h75
-rw-r--r--usr.bin/tn3270/distribution/telnet/Makefile_ultrix179
-rw-r--r--usr.bin/tn3270/distribution/ultrix.curses46
-rw-r--r--usr.bin/tn3270/distribution/utilities/adm3a.keys78
-rw-r--r--usr.bin/tn3270/distribution/utilities/cross.c55
-rw-r--r--usr.bin/tn3270/distribution/utilities/makefile121
-rw-r--r--usr.bin/tn3270/distribution/utilities/srccmd/tar/makefile8
-rw-r--r--usr.bin/tn3270/distribution/utilities/srccmd/tar/tar.h19
-rw-r--r--usr.bin/tn3270/distribution/utilities/srccmd/tar/tarread.c208
-rw-r--r--usr.bin/tn3270/distribution/utilities/tncomp.h51
-rw-r--r--usr.bin/tn3270/distribution/utilities/tnrecv.c674
-rw-r--r--usr.bin/tn3270/general/genbsubs.c125
-rw-r--r--usr.bin/tn3270/general/general.h65
-rw-r--r--usr.bin/tn3270/general/globals.c74
-rw-r--r--usr.bin/tn3270/general/globals.h130
-rw-r--r--usr.bin/tn3270/general/vaxbsubs.s101
-rw-r--r--usr.bin/tn3270/mset/Makefile34
-rw-r--r--usr.bin/tn3270/mset/map3270.5341
-rw-r--r--usr.bin/tn3270/mset/mset.1188
-rw-r--r--usr.bin/tn3270/sys_curses/system.c754
-rw-r--r--usr.bin/tn3270/sys_curses/telextrn.h71
-rw-r--r--usr.bin/tn3270/sys_curses/terminal.h81
-rw-r--r--usr.bin/tn3270/sys_curses/termout.c957
-rw-r--r--usr.bin/tn3270/telnet/Makefile87
-rw-r--r--usr.bin/tn3270/tn3270/Makefile104
-rw-r--r--usr.bin/tn3270/tn3270/tn3270.1339
-rw-r--r--usr.bin/tn3270/tools/Makefile5
-rw-r--r--usr.bin/tn3270/tools/mkastods/Makefile9
-rw-r--r--usr.bin/tn3270/tools/mkastods/mkastods.c77
-rw-r--r--usr.bin/tn3270/tools/mkastosc/Makefile9
-rw-r--r--usr.bin/tn3270/tools/mkastosc/mkastosc.c166
-rw-r--r--usr.bin/tn3270/tools/mkdctype/Makefile9
-rw-r--r--usr.bin/tn3270/tools/mkdctype/ectype.c313
-rw-r--r--usr.bin/tn3270/tools/mkdctype/ectype.h52
-rw-r--r--usr.bin/tn3270/tools/mkdctype/mkdctype.c99
-rw-r--r--usr.bin/tn3270/tools/mkdstoas/Makefile9
-rw-r--r--usr.bin/tn3270/tools/mkdstoas/mkdstoas.c78
-rw-r--r--usr.bin/tn3270/tools/mkhits/Makefile9
-rw-r--r--usr.bin/tn3270/tools/mkhits/dohits.c296
-rw-r--r--usr.bin/tn3270/tools/mkhits/dohits.h56
-rw-r--r--usr.bin/tn3270/tools/mkhits/mkhits.c147
-rw-r--r--usr.bin/tn3270/tools/mkmake.y1097
-rw-r--r--usr.bin/tn3270/tools/prt3270.c620
-rw-r--r--usr.bin/touch/Makefile5
-rw-r--r--usr.bin/touch/touch.1167
-rw-r--r--usr.bin/touch/touch.c342
-rw-r--r--usr.bin/tput/Makefile12
-rw-r--r--usr.bin/tput/clear.sh37
-rw-r--r--usr.bin/tput/tput.1117
-rw-r--r--usr.bin/tput/tput.c208
-rw-r--r--usr.bin/tr/Makefile6
-rw-r--r--usr.bin/tr/extern.h51
-rw-r--r--usr.bin/tr/str.c330
-rw-r--r--usr.bin/tr/tr.1292
-rw-r--r--usr.bin/tr/tr.c287
-rw-r--r--usr.bin/true/Makefile5
-rw-r--r--usr.bin/true/true.162
-rw-r--r--usr.bin/true/true.c47
-rw-r--r--usr.bin/tset/Makefile11
-rw-r--r--usr.bin/tset/extern.h52
-rw-r--r--usr.bin/tset/map.c252
-rw-r--r--usr.bin/tset/misc.c98
-rw-r--r--usr.bin/tset/set.c320
-rw-r--r--usr.bin/tset/term.c155
-rw-r--r--usr.bin/tset/tset.1405
-rw-r--r--usr.bin/tset/tset.c289
-rw-r--r--usr.bin/tset/wrterm.c112
-rw-r--r--usr.bin/tsort/Makefile5
-rw-r--r--usr.bin/tsort/tsort.183
-rw-r--r--usr.bin/tsort/tsort.c429
-rw-r--r--usr.bin/tty/Makefile5
-rw-r--r--usr.bin/tty/tty.178
-rw-r--r--usr.bin/tty/tty.c69
-rw-r--r--usr.bin/ul/Makefile7
-rw-r--r--usr.bin/ul/ul.1107
-rw-r--r--usr.bin/ul/ul.c508
-rw-r--r--usr.bin/uname/Makefile5
-rw-r--r--usr.bin/uname/uname.197
-rw-r--r--usr.bin/uname/uname.c162
-rw-r--r--usr.bin/unexpand/Makefile6
-rw-r--r--usr.bin/unexpand/unexpand.c131
-rw-r--r--usr.bin/unifdef/Makefile5
-rw-r--r--usr.bin/unifdef/unifdef.1165
-rw-r--r--usr.bin/unifdef/unifdef.c638
-rw-r--r--usr.bin/uniq/Makefile5
-rw-r--r--usr.bin/uniq/uniq.1130
-rw-r--r--usr.bin/uniq/uniq.c274
-rw-r--r--usr.bin/unvis/Makefile5
-rw-r--r--usr.bin/unvis/unvis.157
-rw-r--r--usr.bin/unvis/unvis.c147
-rw-r--r--usr.bin/users/Makefile5
-rw-r--r--usr.bin/users/users.159
-rw-r--r--usr.bin/users/users.c101
-rw-r--r--usr.bin/uucp/acucntrl/Makefile10
-rw-r--r--usr.bin/uucp/acucntrl/acucntrl.8164
-rw-r--r--usr.bin/uucp/acucntrl/acucntrl.c814
-rw-r--r--usr.bin/uucp/uupoll/Makefile10
-rw-r--r--usr.bin/uucp/uupoll/uupoll.8111
-rw-r--r--usr.bin/uucp/uupoll/uupoll.c129
-rw-r--r--usr.bin/uucp/uuq/Makefile9
-rw-r--r--usr.bin/uucp/uuq/uuq.1126
-rw-r--r--usr.bin/uucp/uuq/uuq.c435
-rw-r--r--usr.bin/uucp/uusend/Makefile6
-rw-r--r--usr.bin/uucp/uusend/uusend.196
-rw-r--r--usr.bin/uucp/uusend/uusend.c403
-rw-r--r--usr.bin/uucp/uusnap/Makefile8
-rw-r--r--usr.bin/uucp/uusnap/uusnap.880
-rw-r--r--usr.bin/uucp/uusnap/uusnap.c348
-rw-r--r--usr.bin/uudecode/Makefile6
-rw-r--r--usr.bin/uudecode/uudecode.c187
-rw-r--r--usr.bin/uuencode/Makefile9
-rw-r--r--usr.bin/uuencode/uuencode.1105
-rw-r--r--usr.bin/uuencode/uuencode.c150
-rw-r--r--usr.bin/uuencode/uuencode.format.5102
-rw-r--r--usr.bin/vacation/Makefile6
-rw-r--r--usr.bin/vacation/vacation.1171
-rw-r--r--usr.bin/vacation/vacation.c419
-rw-r--r--usr.bin/vgrind/Makefile26
-rw-r--r--usr.bin/vgrind/RETEST/Makefile10
-rw-r--r--usr.bin/vgrind/RETEST/retest.c105
-rw-r--r--usr.bin/vgrind/extern.h65
-rw-r--r--usr.bin/vgrind/pathnames.h36
-rw-r--r--usr.bin/vgrind/regexp.c593
-rw-r--r--usr.bin/vgrind/tmac.vgrind68
-rw-r--r--usr.bin/vgrind/vfontedpr.c705
-rw-r--r--usr.bin/vgrind/vgrind.1224
-rw-r--r--usr.bin/vgrind/vgrind.sh143
-rw-r--r--usr.bin/vgrind/vgrindefs.5158
-rw-r--r--usr.bin/vgrind/vgrindefs.c326
-rw-r--r--usr.bin/vgrind/vgrindefs.src146
-rw-r--r--usr.bin/vi/Makefile3
-rw-r--r--usr.bin/vi/README200
-rw-r--r--usr.bin/vi/USD.doc/edit/Makefile18
-rw-r--r--usr.bin/vi/USD.doc/edit/edit.vindex115
-rw-r--r--usr.bin/vi/USD.doc/edit/edittut.ms2322
-rw-r--r--usr.bin/vi/USD.doc/exref/Makefile14
-rw-r--r--usr.bin/vi/USD.doc/exref/ex.rm2230
-rw-r--r--usr.bin/vi/USD.doc/exref/ex.summary734
-rw-r--r--usr.bin/vi/USD.doc/vi.man/Makefile14
-rw-r--r--usr.bin/vi/USD.doc/vi.man/vi.0798
-rw-r--r--usr.bin/vi/USD.doc/vi.man/vi.0.ps1063
-rw-r--r--usr.bin/vi/USD.doc/vi.man/vi.11294
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/Makefile25
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff1776
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/merge.awk16
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/paper.ps30924
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/ref.so127
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/set.opt.roff949
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/spell.ok270
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff2984
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/vi.ref1270
-rw-r--r--usr.bin/vi/USD.doc/vi.ref/vi.ref.txt5544
-rw-r--r--usr.bin/vi/USD.doc/vitut/Makefile17
-rw-r--r--usr.bin/vi/USD.doc/vitut/vi.apwh.ms1079
-rw-r--r--usr.bin/vi/USD.doc/vitut/vi.chars644
-rw-r--r--usr.bin/vi/USD.doc/vitut/vi.in2064
-rw-r--r--usr.bin/vi/USD.doc/vitut/vi.summary468
-rw-r--r--usr.bin/vi/common/Makefile105
-rw-r--r--usr.bin/vi/common/args.h53
-rw-r--r--usr.bin/vi/common/cut.c366
-rw-r--r--usr.bin/vi/common/cut.h96
-rw-r--r--usr.bin/vi/common/delete.c195
-rw-r--r--usr.bin/vi/common/exf.c837
-rw-r--r--usr.bin/vi/common/exf.h128
-rw-r--r--usr.bin/vi/common/gs.h107
-rw-r--r--usr.bin/vi/common/line.c492
-rw-r--r--usr.bin/vi/common/log.c698
-rw-r--r--usr.bin/vi/common/log.h53
-rw-r--r--usr.bin/vi/common/main.c720
-rw-r--r--usr.bin/vi/common/mark.c272
-rw-r--r--usr.bin/vi/common/mark.h73
-rw-r--r--usr.bin/vi/common/mem.h194
-rw-r--r--usr.bin/vi/common/msg.c428
-rw-r--r--usr.bin/vi/common/msg.h82
-rw-r--r--usr.bin/vi/common/options.awk9
-rw-r--r--usr.bin/vi/common/options.c890
-rw-r--r--usr.bin/vi/common/options.h.stub108
-rw-r--r--usr.bin/vi/common/options_f.c518
-rw-r--r--usr.bin/vi/common/pathnames.h45
-rw-r--r--usr.bin/vi/common/put.c254
-rw-r--r--usr.bin/vi/common/recover.c869
-rw-r--r--usr.bin/vi/common/screen.c311
-rw-r--r--usr.bin/vi/common/screen.h342
-rw-r--r--usr.bin/vi/common/search.c833
-rw-r--r--usr.bin/vi/common/search.h54
-rw-r--r--usr.bin/vi/common/seq.c351
-rw-r--r--usr.bin/vi/common/seq.h79
-rw-r--r--usr.bin/vi/common/signal.c569
-rw-r--r--usr.bin/vi/common/term.c732
-rw-r--r--usr.bin/vi/common/term.h205
-rw-r--r--usr.bin/vi/common/trace.c84
-rw-r--r--usr.bin/vi/common/util.c213
-rw-r--r--usr.bin/vi/common/vi.h124
-rw-r--r--usr.bin/vi/docs/README200
-rw-r--r--usr.bin/vi/docs/bugs.current44
-rw-r--r--usr.bin/vi/docs/changelog528
-rw-r--r--usr.bin/vi/docs/ev55
-rw-r--r--usr.bin/vi/docs/features87
-rw-r--r--usr.bin/vi/docs/internals/autowrite88
-rw-r--r--usr.bin/vi/docs/internals/context32
-rw-r--r--usr.bin/vi/docs/internals/gdb.script77
-rw-r--r--usr.bin/vi/docs/internals/input350
-rw-r--r--usr.bin/vi/docs/internals/quoting219
-rw-r--r--usr.bin/vi/docs/internals/structures61
-rw-r--r--usr.bin/vi/docs/tutorial/vi.advanced1458
-rw-r--r--usr.bin/vi/docs/tutorial/vi.beginner741
-rwxr-xr-xusr.bin/vi/docs/tutorial/vi.tut.csh24
-rw-r--r--usr.bin/vi/ex/ex.c1866
-rw-r--r--usr.bin/vi/ex/ex_abbrev.c129
-rw-r--r--usr.bin/vi/ex/ex_append.c220
-rw-r--r--usr.bin/vi/ex/ex_args.c263
-rw-r--r--usr.bin/vi/ex/ex_argv.c609
-rw-r--r--usr.bin/vi/ex/ex_at.c118
-rw-r--r--usr.bin/vi/ex/ex_bang.c242
-rw-r--r--usr.bin/vi/ex/ex_cd.c223
-rw-r--r--usr.bin/vi/ex/ex_delete.c92
-rw-r--r--usr.bin/vi/ex/ex_digraph.c324
-rw-r--r--usr.bin/vi/ex/ex_display.c169
-rw-r--r--usr.bin/vi/ex/ex_edit.c122
-rw-r--r--usr.bin/vi/ex/ex_equal.c86
-rw-r--r--usr.bin/vi/ex/ex_exit.c79
-rw-r--r--usr.bin/vi/ex/ex_file.c103
-rw-r--r--usr.bin/vi/ex/ex_global.c400
-rw-r--r--usr.bin/vi/ex/ex_init.c202
-rw-r--r--usr.bin/vi/ex/ex_join.c200
-rw-r--r--usr.bin/vi/ex/ex_map.c160
-rw-r--r--usr.bin/vi/ex/ex_mark.c66
-rw-r--r--usr.bin/vi/ex/ex_mkexrc.c130
-rw-r--r--usr.bin/vi/ex/ex_move.c222
-rw-r--r--usr.bin/vi/ex/ex_open.c75
-rw-r--r--usr.bin/vi/ex/ex_preserve.c128
-rw-r--r--usr.bin/vi/ex/ex_print.c212
-rw-r--r--usr.bin/vi/ex/ex_put.c78
-rw-r--r--usr.bin/vi/ex/ex_read.c300
-rw-r--r--usr.bin/vi/ex/ex_screen.c155
-rw-r--r--usr.bin/vi/ex/ex_script.c582
-rw-r--r--usr.bin/vi/ex/ex_set.c70
-rw-r--r--usr.bin/vi/ex/ex_shell.c150
-rw-r--r--usr.bin/vi/ex/ex_shift.c204
-rw-r--r--usr.bin/vi/ex/ex_source.c66
-rw-r--r--usr.bin/vi/ex/ex_stop.c76
-rw-r--r--usr.bin/vi/ex/ex_subst.c1001
-rw-r--r--usr.bin/vi/ex/ex_tag.c905
-rw-r--r--usr.bin/vi/ex/ex_undo.c103
-rw-r--r--usr.bin/vi/ex/ex_usage.c197
-rw-r--r--usr.bin/vi/ex/ex_util.c189
-rw-r--r--usr.bin/vi/ex/ex_version.c71
-rw-r--r--usr.bin/vi/ex/ex_visual.c137
-rw-r--r--usr.bin/vi/ex/ex_write.c327
-rw-r--r--usr.bin/vi/ex/ex_yank.c69
-rw-r--r--usr.bin/vi/ex/ex_z.c180
-rw-r--r--usr.bin/vi/ex/excmd.awk6
-rw-r--r--usr.bin/vi/ex/excmd.c458
-rw-r--r--usr.bin/vi/ex/excmd.h.stub285
-rw-r--r--usr.bin/vi/ex/filter.c414
-rw-r--r--usr.bin/vi/ex/script.h45
-rw-r--r--usr.bin/vi/ex/tag.h58
-rw-r--r--usr.bin/vi/install/recover.script46
-rw-r--r--usr.bin/vi/sex/sex_confirm.c86
-rw-r--r--usr.bin/vi/sex/sex_get.c514
-rw-r--r--usr.bin/vi/sex/sex_refresh.c140
-rw-r--r--usr.bin/vi/sex/sex_screen.c340
-rw-r--r--usr.bin/vi/sex/sex_screen.h79
-rw-r--r--usr.bin/vi/sex/sex_term.c217
-rw-r--r--usr.bin/vi/sex/sex_util.c148
-rw-r--r--usr.bin/vi/sex/sex_window.c196
-rw-r--r--usr.bin/vi/svi/svi_confirm.c95
-rw-r--r--usr.bin/vi/svi/svi_curses.c252
-rw-r--r--usr.bin/vi/svi/svi_ex.c650
-rw-r--r--usr.bin/vi/svi/svi_get.c161
-rw-r--r--usr.bin/vi/svi/svi_line.c441
-rw-r--r--usr.bin/vi/svi/svi_refresh.c818
-rw-r--r--usr.bin/vi/svi/svi_relative.c334
-rw-r--r--usr.bin/vi/svi/svi_screen.c336
-rw-r--r--usr.bin/vi/svi/svi_screen.h262
-rw-r--r--usr.bin/vi/svi/svi_smap.c1216
-rw-r--r--usr.bin/vi/svi/svi_split.c635
-rw-r--r--usr.bin/vi/svi/svi_term.c310
-rw-r--r--usr.bin/vi/svi/svi_util.c347
-rw-r--r--usr.bin/vi/vi/getc.c268
-rw-r--r--usr.bin/vi/vi/v_ch.c340
-rw-r--r--usr.bin/vi/vi/v_delete.c160
-rw-r--r--usr.bin/vi/vi/v_ex.c352
-rw-r--r--usr.bin/vi/vi/v_increment.c163
-rw-r--r--usr.bin/vi/vi/v_init.c256
-rw-r--r--usr.bin/vi/vi/v_left.c287
-rw-r--r--usr.bin/vi/vi/v_mark.c210
-rw-r--r--usr.bin/vi/vi/v_match.c198
-rw-r--r--usr.bin/vi/vi/v_ntext.c1899
-rw-r--r--usr.bin/vi/vi/v_paragraph.c370
-rw-r--r--usr.bin/vi/vi/v_put.c168
-rw-r--r--usr.bin/vi/vi/v_redraw.c67
-rw-r--r--usr.bin/vi/vi/v_replace.c194
-rw-r--r--usr.bin/vi/vi/v_right.c162
-rw-r--r--usr.bin/vi/vi/v_screen.c90
-rw-r--r--usr.bin/vi/vi/v_scroll.c486
-rw-r--r--usr.bin/vi/vi/v_search.c414
-rw-r--r--usr.bin/vi/vi/v_section.c280
-rw-r--r--usr.bin/vi/vi/v_sentence.c386
-rw-r--r--usr.bin/vi/vi/v_status.c73
-rw-r--r--usr.bin/vi/vi/v_stop.c75
-rw-r--r--usr.bin/vi/vi/v_text.c886
-rw-r--r--usr.bin/vi/vi/v_ulcase.c208
-rw-r--r--usr.bin/vi/vi/v_undo.c162
-rw-r--r--usr.bin/vi/vi/v_util.c159
-rw-r--r--usr.bin/vi/vi/v_word.c570
-rw-r--r--usr.bin/vi/vi/v_xchar.c136
-rw-r--r--usr.bin/vi/vi/v_yank.c94
-rw-r--r--usr.bin/vi/vi/v_z.c159
-rw-r--r--usr.bin/vi/vi/v_zexit.c82
-rw-r--r--usr.bin/vi/vi/vcmd.c533
-rw-r--r--usr.bin/vi/vi/vcmd.h346
-rw-r--r--usr.bin/vi/vi/vi.c937
-rw-r--r--usr.bin/vi/xaw/xaw_screen.c98
-rw-r--r--usr.bin/vis/Makefile6
-rw-r--r--usr.bin/vis/foldit.c72
-rw-r--r--usr.bin/vis/vis.1124
-rw-r--r--usr.bin/vis/vis.c173
-rw-r--r--usr.bin/vmstat/Makefile11
-rw-r--r--usr.bin/vmstat/names.c271
-rw-r--r--usr.bin/vmstat/vmstat.8206
-rw-r--r--usr.bin/vmstat/vmstat.c834
-rw-r--r--usr.bin/w/Makefile14
-rw-r--r--usr.bin/w/extern.h39
-rw-r--r--usr.bin/w/pr_time.c108
-rw-r--r--usr.bin/w/proc_compare.c120
-rw-r--r--usr.bin/w/uptime.160
-rw-r--r--usr.bin/w/w.1141
-rw-r--r--usr.bin/w/w.c435
-rw-r--r--usr.bin/wall/Makefile8
-rw-r--r--usr.bin/wall/ttymsg.c163
-rw-r--r--usr.bin/wall/wall.163
-rw-r--r--usr.bin/wall/wall.c206
-rw-r--r--usr.bin/watch/Makefile6
-rw-r--r--usr.bin/watch/snp.449
-rw-r--r--usr.bin/watch/watch.877
-rw-r--r--usr.bin/watch/watch.c404
-rw-r--r--usr.bin/wc/Makefile5
-rw-r--r--usr.bin/wc/wc.1109
-rw-r--r--usr.bin/wc/wc.c243
-rw-r--r--usr.bin/what/Makefile5
-rw-r--r--usr.bin/what/what.168
-rw-r--r--usr.bin/what/what.c86
-rw-r--r--usr.bin/whatis/Makefile7
-rw-r--r--usr.bin/whatis/whatis.1105
-rw-r--r--usr.bin/whatis/whatis.c218
-rw-r--r--usr.bin/whereis/Makefile5
-rw-r--r--usr.bin/whereis/whereis.163
-rw-r--r--usr.bin/whereis/whereis.c115
-rw-r--r--usr.bin/which/Makefile9
-rw-r--r--usr.bin/which/which.164
-rwxr-xr-xusr.bin/which/which.pl64
-rwxr-xr-xusr.bin/which/which.sh62
-rw-r--r--usr.bin/who/Makefile5
-rw-r--r--usr.bin/who/who.1105
-rw-r--r--usr.bin/who/who.c135
-rw-r--r--usr.bin/whois/Makefile5
-rw-r--r--usr.bin/whois/whois.180
-rw-r--r--usr.bin/whois/whois.c131
-rw-r--r--usr.bin/window/:tt11
-rw-r--r--usr.bin/window/:tty6
-rw-r--r--usr.bin/window/:var2
-rw-r--r--usr.bin/window/:ww19
-rw-r--r--usr.bin/window/Makefile21
-rw-r--r--usr.bin/window/README199
-rw-r--r--usr.bin/window/alias.h52
-rw-r--r--usr.bin/window/char.c76
-rw-r--r--usr.bin/window/char.h61
-rw-r--r--usr.bin/window/cmd.c300
-rw-r--r--usr.bin/window/cmd1.c172
-rw-r--r--usr.bin/window/cmd2.c154
-rw-r--r--usr.bin/window/cmd3.c65
-rw-r--r--usr.bin/window/cmd4.c56
-rw-r--r--usr.bin/window/cmd5.c129
-rw-r--r--usr.bin/window/cmd6.c110
-rw-r--r--usr.bin/window/cmd7.c271
-rw-r--r--usr.bin/window/compress.c900
-rw-r--r--usr.bin/window/context.c131
-rw-r--r--usr.bin/window/context.h83
-rw-r--r--usr.bin/window/defs.h71
-rw-r--r--usr.bin/window/error.c95
-rw-r--r--usr.bin/window/lcmd.c154
-rw-r--r--usr.bin/window/lcmd.h61
-rw-r--r--usr.bin/window/lcmd1.c429
-rw-r--r--usr.bin/window/lcmd2.c397
-rw-r--r--usr.bin/window/local.h51
-rw-r--r--usr.bin/window/main.c202
-rw-r--r--usr.bin/window/mloop.c84
-rw-r--r--usr.bin/window/mystring.h65
-rw-r--r--usr.bin/window/parser.h47
-rw-r--r--usr.bin/window/parser1.c222
-rw-r--r--usr.bin/window/parser2.c231
-rw-r--r--usr.bin/window/parser3.c191
-rw-r--r--usr.bin/window/parser4.c296
-rw-r--r--usr.bin/window/parser5.c201
-rw-r--r--usr.bin/window/scanner.c549
-rw-r--r--usr.bin/window/startup.c92
-rw-r--r--usr.bin/window/string.c153
-rw-r--r--usr.bin/window/string.h65
-rw-r--r--usr.bin/window/token.h83
-rw-r--r--usr.bin/window/tt.h153
-rw-r--r--usr.bin/window/ttf100.c69
-rw-r--r--usr.bin/window/ttgeneric.c547
-rw-r--r--usr.bin/window/tth19.c278
-rw-r--r--usr.bin/window/tth29.c94
-rw-r--r--usr.bin/window/ttinit.c120
-rw-r--r--usr.bin/window/ttoutput.c146
-rw-r--r--usr.bin/window/tttermcap.c117
-rw-r--r--usr.bin/window/tttvi925.c64
-rw-r--r--usr.bin/window/ttwyse60.c76
-rw-r--r--usr.bin/window/ttwyse75.c76
-rw-r--r--usr.bin/window/ttzapple.c483
-rw-r--r--usr.bin/window/ttzentec.c66
-rw-r--r--usr.bin/window/value.h53
-rw-r--r--usr.bin/window/var.c157
-rw-r--r--usr.bin/window/var.h58
-rw-r--r--usr.bin/window/win.c370
-rw-r--r--usr.bin/window/window.1947
-rw-r--r--usr.bin/window/windowrc85
-rw-r--r--usr.bin/window/ww.h319
-rw-r--r--usr.bin/window/wwadd.c88
-rw-r--r--usr.bin/window/wwalloc.c71
-rw-r--r--usr.bin/window/wwbox.c66
-rw-r--r--usr.bin/window/wwchild.c71
-rw-r--r--usr.bin/window/wwclose.c59
-rw-r--r--usr.bin/window/wwclreol.c95
-rw-r--r--usr.bin/window/wwclreos.c55
-rw-r--r--usr.bin/window/wwcursor.c92
-rw-r--r--usr.bin/window/wwdata.c39
-rw-r--r--usr.bin/window/wwdelchar.c123
-rw-r--r--usr.bin/window/wwdelete.c139
-rw-r--r--usr.bin/window/wwdelline.c88
-rw-r--r--usr.bin/window/wwdump.c114
-rw-r--r--usr.bin/window/wwend.c66
-rw-r--r--usr.bin/window/wwenviron.c102
-rw-r--r--usr.bin/window/wwerror.c69
-rw-r--r--usr.bin/window/wwflush.c108
-rw-r--r--usr.bin/window/wwframe.c249
-rw-r--r--usr.bin/window/wwgets.c112
-rw-r--r--usr.bin/window/wwinit.c401
-rw-r--r--usr.bin/window/wwinschar.c130
-rw-r--r--usr.bin/window/wwinsline.c88
-rw-r--r--usr.bin/window/wwiomux.c203
-rw-r--r--usr.bin/window/wwlabel.c104
-rw-r--r--usr.bin/window/wwmisc.c67
-rw-r--r--usr.bin/window/wwmove.c98
-rw-r--r--usr.bin/window/wwopen.c189
-rw-r--r--usr.bin/window/wwprintf.c57
-rw-r--r--usr.bin/window/wwpty.c87
-rw-r--r--usr.bin/window/wwputc.c48
-rw-r--r--usr.bin/window/wwputs.c52
-rw-r--r--usr.bin/window/wwredraw.c56
-rw-r--r--usr.bin/window/wwredrawwin.c73
-rw-r--r--usr.bin/window/wwrint.c91
-rw-r--r--usr.bin/window/wwscroll.c234
-rw-r--r--usr.bin/window/wwsize.c191
-rw-r--r--usr.bin/window/wwspawn.c85
-rw-r--r--usr.bin/window/wwsuspend.c56
-rw-r--r--usr.bin/window/wwterminfo.c107
-rw-r--r--usr.bin/window/wwtty.c180
-rw-r--r--usr.bin/window/wwunframe.c76
-rw-r--r--usr.bin/window/wwupdate.c271
-rw-r--r--usr.bin/window/wwwrite.c298
-rw-r--r--usr.bin/window/xx.c253
-rw-r--r--usr.bin/window/xx.h58
-rw-r--r--usr.bin/window/xxflush.c196
-rw-r--r--usr.bin/write/Makefile7
-rw-r--r--usr.bin/write/write.1108
-rw-r--r--usr.bin/write/write.c326
-rw-r--r--usr.bin/xargs/Makefile5
-rw-r--r--usr.bin/xargs/pathnames.h36
-rw-r--r--usr.bin/xargs/xargs.1161
-rw-r--r--usr.bin/xargs/xargs.c325
-rw-r--r--usr.bin/xinstall/Makefile12
-rw-r--r--usr.bin/xinstall/install.1165
-rw-r--r--usr.bin/xinstall/pathnames.h36
-rw-r--r--usr.bin/xinstall/xinstall.c504
-rw-r--r--usr.bin/xstr/Makefile5
-rw-r--r--usr.bin/xstr/pathnames.h36
-rw-r--r--usr.bin/xstr/xstr.1159
-rw-r--r--usr.bin/xstr/xstr.c471
-rw-r--r--usr.bin/yacc/ACKNOWLEDGEMENTS25
-rw-r--r--usr.bin/yacc/Makefile12
-rw-r--r--usr.bin/yacc/NEW_FEATURES46
-rw-r--r--usr.bin/yacc/NOTES9
-rw-r--r--usr.bin/yacc/README23
-rw-r--r--usr.bin/yacc/closure.c295
-rw-r--r--usr.bin/yacc/defs.h327
-rw-r--r--usr.bin/yacc/error.c357
-rw-r--r--usr.bin/yacc/lalr.c678
-rw-r--r--usr.bin/yacc/lr0.c637
-rw-r--r--usr.bin/yacc/main.c423
-rw-r--r--usr.bin/yacc/mkpar.c395
-rw-r--r--usr.bin/yacc/output.c1250
-rw-r--r--usr.bin/yacc/reader.c1810
-rw-r--r--usr.bin/yacc/skeleton.c346
-rw-r--r--usr.bin/yacc/symtab.c158
-rw-r--r--usr.bin/yacc/test/error.output27
-rw-r--r--usr.bin/yacc/test/error.tab.c275
-rw-r--r--usr.bin/yacc/test/error.tab.h0
-rw-r--r--usr.bin/yacc/test/error.y6
-rw-r--r--usr.bin/yacc/test/ftp.output1625
-rw-r--r--usr.bin/yacc/test/ftp.tab.c1743
-rw-r--r--usr.bin/yacc/test/ftp.tab.h63
-rw-r--r--usr.bin/yacc/test/ftp.y1180
-rw-r--r--usr.bin/yacc/verbose.c366
-rw-r--r--usr.bin/yacc/warshall.c122
-rw-r--r--usr.bin/yacc/yacc.1145
-rw-r--r--usr.bin/yacc/yyfix.1112
-rw-r--r--usr.bin/yacc/yyfix.sh71
-rw-r--r--usr.bin/yes/Makefile5
-rw-r--r--usr.bin/yes/yes.154
-rw-r--r--usr.bin/yes/yes.c53
-rw-r--r--usr.bin/ypcat/Makefile6
-rw-r--r--usr.bin/ypcat/ypcat.170
-rw-r--r--usr.bin/ypcat/ypcat.c143
-rw-r--r--usr.bin/ypmatch/Makefile6
-rw-r--r--usr.bin/ypmatch/ypmatch.171
-rw-r--r--usr.bin/ypmatch/ypmatch.c134
-rw-r--r--usr.bin/ypwhich/Makefile7
-rw-r--r--usr.bin/ypwhich/ypwhich.c255
1933 files changed, 507275 insertions, 68 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile
new file mode 100644
index 0000000..ad73ddb
--- /dev/null
+++ b/usr.bin/Makefile
@@ -0,0 +1,58 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/7/94
+# $Id: Makefile,v 1.61 1995/09/29 19:50:40 ache Exp $
+
+# XXX MISSING: deroff diction graph learn plot
+# spell spline struct units xsend
+# XXX Use GNU versions: apropos bc dc diff grep ld man patch ptx uucp whatis
+# Moved to secure: bdes
+#
+SUBDIR= apply ar at banner basename biff cal calendar \
+ cap_mkdb chat checknr chflags chpass cksum col colcrt colldef colrm \
+ column comm compile_et compress cpp ctags cut devmenu \
+ dig dirname du ee env error expand f2c false file file2c \
+ find finger fmt fold fpr from fsplit fstat ftp gcore gencat getopt \
+ gprof head hexdump host id indent ipcrm ipcs \
+ join jot kdump ktrace key keyinfo keyinit killall lam last \
+ lastcomm leave lex locate lock logger login logname lorder \
+ lsvfs m4 mail make mesg mkdep mkfifo mklocale mkstr mk_cmds \
+ modstat more msgs mt ncftp netstat nfsstat nice \
+ nm nohup pagesize passwd paste pr printenv \
+ printf quota ranlib rdist renice rev rlogin rpcgen \
+ rpcinfo rs rsh rup ruptime rusers rwall \
+ rwho script sed sgmlfmt sgmls shar showmount size soelim split \
+ strings strip su symorder talk tconv tcopy tee tftp time \
+ tip tn3270 touch tput tr true tset tsort tty ul uname \
+ unexpand unifdef uniq unvis users uudecode uuencode vacation \
+ vgrind vi vis w wall wc what whereis which who whois window \
+ write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich
+
+.if !exists(../secure) || defined(NOSECURE)
+SUBDIR+=telnet
+.else
+SUBDIR+= ../secure/usr.bin/telnet
+.endif
+
+# Cmp, look and tail all use mmap, so new-VM only.
+# F77 and pascal are VAX/Tahoe only.
+.if make(clean) || make(cleandir)
+# XXX Should have `f77', `pascal' & `vmstat.sparc' judging by the
+# machine dependant lines, but we don't have them
+SUBDIR+=cmp kzip look systat tail vmstat
+.elif ${MACHINE} == "hp300"
+SUBDIR+=cmp ld look systat tail vmstat
+.elif ${MACHINE} == "i386"
+SUBDIR+=cmp kzip look sasc systat tail vmstat
+# XXX Use gnu/usr.bin/ld for now
+.elif ${MACHINE} == "luna68k"
+SUBDIR+=cmp ld look systat tail vmstat
+.elif ${MACHINE} == "mips"
+SUBDIR+=cmp look systat tail vmstat
+.elif ${MACHINE} == "sparc"
+SUBDIR+=cmp ld look tail vmstat.sparc
+.elif ${MACHINE} == "tahoe"
+SUBDIR+=f77 pascal systat vmstat
+.elif ${MACHINE} == "vax"
+SUBDIR+=f77 pascal systat vmstat
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/Makefile.inc b/usr.bin/Makefile.inc
new file mode 100644
index 0000000..84bfd7f
--- /dev/null
+++ b/usr.bin/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+
+BINDIR?= /usr/bin
diff --git a/usr.bin/apply/Makefile b/usr.bin/apply/Makefile
new file mode 100644
index 0000000..59f358e
--- /dev/null
+++ b/usr.bin/apply/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= apply
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/apply/apply.1 b/usr.bin/apply/apply.1
new file mode 100644
index 0000000..dd7a9ee
--- /dev/null
+++ b/usr.bin/apply/apply.1
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)apply.1 8.2 (Berkeley) 4/4/94
+.\"
+.Dd April 4, 1994
+.Dt APPLY 1
+.Os BSD 4.2
+.Sh NAME
+.Nm apply
+.Nd apply a command to a set of arguments
+.Sh SYNOPSIS
+.Nm apply
+.Op Fl a Ns Ar c
+.Op Fl Ns Ar #
+.Ar command argument ...
+.Sh DESCRIPTION
+.Nm Apply
+runs the named
+.Ar command
+on each
+argument
+.Ar argument
+in turn.
+.Pp
+Character sequences of the form
+.Dq Li \&%d
+in
+.Ar command ,
+where
+.Dq Li d
+is a digit from 1 to 9, are replaced by the
+.Li d Ns \'th
+following unused
+.Ar argument .
+In this case, the largest digit number of arguments are discarded for
+each execution of
+.Ar command .
+.Pp
+The options are as follows:
+.Bl -tag -width "-ac"
+.It Fl Ns Ar #
+Normally arguments are taken singly; the optional number
+.Fl #
+specifies the number of arguments to be passed to
+.Ar command .
+If the number is zero,
+.Ar command
+is run, without arguments, once for each
+.Ar argument .
+.Pp
+If any sequences of
+.Dq Li \&%d
+occur in command, the
+.Fl n
+option is ignored.
+.It Fl a Ns Ar c
+The use of the character
+.Dq Li %
+as a magic character may be changed with the
+.Fl a
+option.
+.El
+.Sh ENVIRONMENT VARIABLES
+The following environment variable affects the execution of
+.Nm apply :
+.Bl -tag -width SHELL
+.It Ev SHELL
+Pathname of shell to use.
+If this variable is not defined, the Bourne shell is used.
+.El
+.Sh EXAMPLES
+.Bl -tag -width apply -compact
+.It Li "apply echo a*"
+is similar to ls(1);
+.It Li "apply \-2 cmp a1 b1 a2 b2 a3 b3"
+compares the `a' files to the `b' files;
+.It Li "apply \-0 who 1 2 3 4 5"
+runs who(1) 5 times; and
+.It Li "apply \'ln %1 /usr/joe\'" *
+links all files in the current directory to the directory
+.Pa /usr/joe .
+.El
+.Sh Files
+.Bl -tag -width /bin/sh -compact
+.It Pa /bin/sh
+Default shell
+.El
+.Sh AUTHOR
+Rob Pike
+.Sh BUGS
+Shell metacharacters in
+.Ar command
+may have bizarre effects; it is best to enclose complicated
+commands in single quotes
+.Pq Sq .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.bin/apply/apply.c b/usr.bin/apply/apply.c
new file mode 100644
index 0000000..843488f
--- /dev/null
+++ b/usr.bin/apply/apply.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)apply.c 8.4 (Berkeley) 4/4/94";
+#endif /* not lint */
+
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void usage __P((void));
+int system __P((const char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, clen, debug, i, l, magic, n, nargs, rval;
+ char *c, *cmd, *p, *q;
+
+ debug = 0;
+ magic = '%'; /* Default magic char is `%'. */
+ nargs = -1;
+ while ((ch = getopt(argc, argv, "a:d0123456789")) != EOF)
+ switch (ch) {
+ case 'a':
+ if (optarg[1] != '\0')
+ errx(1,
+ "illegal magic character specification.");
+ magic = optarg[0];
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (nargs != -1)
+ errx(1,
+ "only one -# argument may be specified.");
+ nargs = optopt - '0';
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ usage();
+
+ /*
+ * The command to run is argv[0], and the args are argv[1..].
+ * Look for %digit references in the command, remembering the
+ * largest one.
+ */
+ for (n = 0, p = argv[0]; *p != '\0'; ++p)
+ if (p[0] == magic && isdigit(p[1]) && p[1] != '0') {
+ ++p;
+ if (p[0] - '0' > n)
+ n = p[0] - '0';
+ }
+
+ /*
+ * If there were any %digit references, then use those, otherwise
+ * build a new command string with sufficient %digit references at
+ * the end to consume (nargs) arguments each time round the loop.
+ * Allocate enough space to hold the maximum command.
+ */
+ if ((cmd = malloc(sizeof("exec ") - 1 +
+ strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1)) == NULL)
+ err(1, NULL);
+
+ if (n == 0) {
+ /* If nargs not set, default to a single argument. */
+ if (nargs == -1)
+ nargs = 1;
+
+ p = cmd;
+ p += sprintf(cmd, "exec %s", argv[0]);
+ for (i = 1; i <= nargs; i++)
+ p += sprintf(p, " %c%d", magic, i);
+
+ /*
+ * If nargs set to the special value 0, eat a single
+ * argument for each command execution.
+ */
+ if (nargs == 0)
+ nargs = 1;
+ } else {
+ (void)sprintf(cmd, "exec %s", argv[0]);
+ nargs = n;
+ }
+
+ /*
+ * Grab some space in which to build the command. Allocate
+ * as necessary later, but no reason to build it up slowly
+ * for the normal case.
+ */
+ if ((c = malloc(clen = 1024)) == NULL)
+ err(1, NULL);
+
+ /*
+ * (argc) and (argv) are still offset by one to make it simpler to
+ * expand %digit references. At the end of the loop check for (argc)
+ * equals 1 means that all the (argv) has been consumed.
+ */
+ for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) {
+ /*
+ * Find a max value for the command length, and ensure
+ * there's enough space to build it.
+ */
+ for (l = strlen(cmd), i = 0; i < nargs; i++)
+ l += strlen(argv[i]);
+ if (l > clen && (c = realloc(c, clen = l)) == NULL)
+ err(1, NULL);
+
+ /* Expand command argv references. */
+ for (p = cmd, q = c; *p != '\0'; ++p)
+ if (p[0] == magic && isdigit(p[1]) && p[1] != '0')
+ q += sprintf(q, "%s", argv[(++p)[0] - '0']);
+ else
+ *q++ = *p;
+
+ /* Terminate the command string. */
+ *q = '\0';
+
+ /* Run the command. */
+ if (debug)
+ (void)printf("%s\n", c);
+ else
+ if (system(c))
+ rval = 1;
+ }
+
+ if (argc != 1)
+ errx(1, "expecting additional argument%s after \"%s\"",
+ (nargs - argc) ? "s" : "", argv[argc - 1]);
+ exit(rval);
+}
+
+/*
+ * system --
+ * Private version of system(3). Use the user's SHELL environment
+ * variable as the shell to execute.
+ */
+int
+system(command)
+ const char *command;
+{
+ static char *name, *shell;
+ union wait pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+
+ if (shell == NULL) {
+ if ((shell = getenv("SHELL")) == NULL)
+ shell = _PATH_BSHELL;
+ if ((name = strrchr(shell, '/')) == NULL)
+ name = shell;
+ else
+ ++name;
+ }
+ if (!command) /* just checking... */
+ return(1);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ switch(pid = vfork()) {
+ case -1: /* error */
+ err(1, "fork");
+ case 0: /* child */
+ (void)sigsetmask(omask);
+ execl(shell, name, "-c", command, NULL);
+ err(1, "%s", shell);
+ }
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, (int *)&pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ return(pid == -1 ? -1 : pstat.w_status);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: apply [-a magic] [-0123456789] command arguments ...\n");
+ exit(1);
+}
diff --git a/usr.bin/apropos/Makefile b/usr.bin/apropos/Makefile
new file mode 100644
index 0000000..028a42c
--- /dev/null
+++ b/usr.bin/apropos/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= apropos
+SRCS= apropos.c config.c
+.PATH: ${.CURDIR}/../man
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/apropos/apropos.1 b/usr.bin/apropos/apropos.1
new file mode 100644
index 0000000..eb68f37
--- /dev/null
+++ b/usr.bin/apropos/apropos.1
@@ -0,0 +1,120 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)apropos.1 8.1 (Berkeley) 6/29/93
+.\"
+.Dd June 29, 1993
+.Dt APROPOS 1
+.Os
+.Sh NAME
+.Nm apropos
+.Nd locate commands by keyword lookup
+.Sh SYNOPSIS
+.Nm apropos
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Ar keyword ...
+.Sh DESCRIPTION
+.Nm Apropos
+shows which manual pages contain instances of any of the given
+.Ar keyword(s)
+in their title line.
+Each word is considered separately and case of letters is ignored.
+Words which are part of other words are considered; when looking for
+.Dq compile ,
+.Nm apropos
+will also list all instances of
+.Dq compiler .
+.Pp
+If the line output by
+.Nm apropos
+starts
+.Dq Li name(section) ...
+you can enter
+.Dq Li man section name
+to get
+its documentation.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl M
+Override the list of standard directories
+.Nm apropos
+searches for a database named
+.Pa whatis.db .
+The supplied
+.Ar path
+must be a colon
+.Dq \&:
+separated list of directories.
+This search path may also be set using the environment variable
+.Ev MANPATH .
+.It Fl m
+Augment the list of standard directories
+.Nm apropos
+searches for its database.
+The supplied
+.Ar path
+must be a colon
+.Dq \&:
+separated list of directories.
+These directories will be searched before the standard directories,
+or the directories supplied with the
+.Fl M
+option or the
+.Ev MANPATH
+environment variable.
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATH
+.It Ev MANPATH
+The standard search path used by
+.Xr man 1
+may be overridden by specifying a path in the
+.Ev MANPATH
+environment variable.
+The format of the path is a colon
+.Dq \&:
+separated list of directories.
+.El
+.Sh FILES
+.Bl -tag -width whatis.db -compact
+.It Pa whatis.db
+name of the apropos database
+.El
+.Sh SEE ALSO
+.Xr man 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1
+.Sh HISTORY
+The
+.Nm apropos
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/apropos/apropos.c b/usr.bin/apropos/apropos.c
new file mode 100644
index 0000000..a0e0525
--- /dev/null
+++ b/usr.bin/apropos/apropos.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)apropos.c 8.7 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../man/config.h"
+#include "../man/pathnames.h"
+
+static int *found, foundman;
+
+void apropos __P((char **, char *, int));
+void lowstr __P((char *, char *));
+int match __P((char *, char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ENTRY *ep;
+ TAG *tp;
+ int ch, rv;
+ char *conffile, **p, *p_augment, *p_path;
+
+ conffile = NULL;
+ p_augment = p_path = NULL;
+ while ((ch = getopt(argc, argv, "C:M:m:P:")) != EOF)
+ switch (ch) {
+ case 'C':
+ conffile = optarg;
+ break;
+ case 'M':
+ case 'P': /* backward compatible */
+ p_path = optarg;
+ break;
+ case 'm':
+ p_augment = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1)
+ usage();
+
+ if ((found = malloc((u_int)argc * sizeof(int))) == NULL)
+ err(1, NULL);
+ memset(found, 0, argc * sizeof(int));
+
+ for (p = argv; *p; ++p) /* convert to lower-case */
+ lowstr(*p, *p);
+
+ if (p_augment)
+ apropos(argv, p_augment, 1);
+ if (p_path || (p_path = getenv("MANPATH")))
+ apropos(argv, p_path, 1);
+ else {
+ config(conffile);
+ ep = (tp = getlist("_whatdb")) == NULL ?
+ NULL : tp->list.tqh_first;
+ for (; ep != NULL; ep = ep->q.tqe_next)
+ apropos(argv, ep->s, 0);
+ }
+
+ if (!foundman)
+ errx(1, "no %s file found", _PATH_WHATIS);
+
+ rv = 1;
+ for (p = argv; *p; ++p)
+ if (found[p - argv])
+ rv = 0;
+ else
+ (void)printf("%s: nothing appropriate\n", *p);
+ exit(rv);
+}
+
+void
+apropos(argv, path, buildpath)
+ char **argv, *path;
+ int buildpath;
+{
+ char *end, *name, **p;
+ char buf[LINE_MAX + 1], wbuf[LINE_MAX + 1];
+
+ for (name = path; name; name = end) { /* through name list */
+ if (end = strchr(name, ':'))
+ *end++ = '\0';
+
+ if (buildpath) {
+ char hold[MAXPATHLEN + 1];
+
+ (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS);
+ name = hold;
+ }
+
+ if (!freopen(name, "r", stdin))
+ continue;
+
+ foundman = 1;
+
+ /* for each file found */
+ while (fgets(buf, sizeof(buf), stdin)) {
+ if (!strchr(buf, '\n')) {
+ warnx("%s: line too long", name);
+ continue;
+ }
+ lowstr(buf, wbuf);
+ for (p = argv; *p; ++p)
+ if (match(wbuf, *p)) {
+ (void)printf("%s", buf);
+ found[p - argv] = 1;
+
+ /* only print line once */
+ while (*++p)
+ if (match(wbuf, *p))
+ found[p - argv] = 1;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * match --
+ * match anywhere the string appears
+ */
+int
+match(bp, str)
+ char *bp, *str;
+{
+ int len;
+ char test;
+
+ if (!*bp)
+ return (0);
+ /* backward compatible: everything matches empty string */
+ if (!*str)
+ return (1);
+ for (test = *str++, len = strlen(str); *bp;)
+ if (test == *bp++ && !strncmp(bp, str, len))
+ return (1);
+ return (0);
+}
+
+/*
+ * lowstr --
+ * convert a string to lower case
+ */
+void
+lowstr(from, to)
+ char *from, *to;
+{
+ char ch;
+
+ while ((ch = *from++) && ch != '\n')
+ *to++ = isupper(ch) ? tolower(ch) : ch;
+ *to = '\0';
+}
+
+/*
+ * usage --
+ * print usage message and die
+ */
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: apropos [-C file] [-M path] [-m path] keyword ...\n");
+ exit(1);
+}
diff --git a/usr.bin/ar/Makefile b/usr.bin/ar/Makefile
new file mode 100644
index 0000000..94bb84f
--- /dev/null
+++ b/usr.bin/ar/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ar
+CFLAGS+=-I${.CURDIR}
+SRCS= append.c ar.c archive.c contents.c delete.c extract.c misc.c \
+ move.c print.c replace.c
+MAN1= ar.1
+MAN5= ar.5
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ar/append.c b/usr.bin/ar/append.c
new file mode 100644
index 0000000..9bbe473
--- /dev/null
+++ b/usr.bin/ar/append.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)append.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "archive.h"
+#include "extern.h"
+
+/*
+ * append --
+ * Append files to the archive - modifies original archive or creates
+ * a new archive if named archive does not exist.
+ */
+int
+append(argv)
+ char **argv;
+{
+ int afd, fd, eval;
+ char *file;
+ CF cf;
+ struct stat sb;
+
+ afd = open_archive(O_CREAT|O_RDWR);
+ if (lseek(afd, (off_t)0, SEEK_END) == (off_t)-1)
+ error(archive);
+
+ /* Read from disk, write to an archive; pad on write. */
+ SETCF(0, 0, afd, archive, WPAD);
+ for (eval = 0; file = *argv++;) {
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ warn("%s", file);
+ eval = 1;
+ continue;
+ }
+ if (options & AR_V)
+ (void)printf("q - %s\n", file);
+ cf.rfd = fd;
+ cf.rname = file;
+ put_arobj(&cf, &sb);
+ (void)close(fd);
+ }
+ close_archive(afd);
+ return (eval);
+}
diff --git a/usr.bin/ar/ar.1 b/usr.bin/ar/ar.1
new file mode 100644
index 0000000..026aed6
--- /dev/null
+++ b/usr.bin/ar/ar.1
@@ -0,0 +1,257 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Hugh Smith at The University of Guelph.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ar.1 8.1 (Berkeley) 6/29/93
+.\"
+.TH AR 1 "June 29, 1993"
+.AT 3
+.SH NAME
+ar \- create and maintain library archives
+.SH SYNOPSIS
+.nf
+.ft B
+ar -d [-Tv] archive file ...
+ar -m [-Tv] archive file ...
+ar -m [-abiTv] position archive file ...
+ar -p [-Tv] archive [file ...]
+ar -q [-cTv] archive file ...
+ar -r [-cuTv] archive file ...
+ar -r [-abciuTv] position archive file ...
+ar -t [-Tv] archive [file ...]
+ar -x [-ouTv] archive [file ...]
+.fi
+.ft R
+.SH DESCRIPTION
+The
+.I ar
+utility creates and maintains groups of files combined into an archive.
+Once an archive has been created, new files can be added and existing
+files can be extracted, deleted, or replaced.
+.PP
+Files are named in the archive by a single component, i.e., if a file
+referenced by a path containing a slash (``/'') is archived it will be
+named by the last component of that path.
+When matching paths listed on the command line against file names stored
+in the archive, only the last component of the path will be compared.
+.PP
+All informational and error messages use the path listed on the command
+line, if any was specified; otherwise the name in the archive is used.
+If multiple files in the archive have the same name, and paths are listed
+on the command line to ``select'' archive files for an operation, only the
+.B first
+file with a matching name will be selected.
+.PP
+The normal use of
+.I ar
+is for the creation and maintenance of libraries suitable for use with
+the loader (see
+.IR ld (1)),
+although it is not restricted to this purpose.
+The options are as follows:
+.TP
+\-a
+A positioning modifier used with the options \-r and \-m.
+The files are entered or moved
+.B after
+the archive member
+.IR position ,
+which must be specified.
+.TP
+\-b
+A positioning modifier used with the options \-r and \-m.
+The files are entered or moved
+.B before
+the archive member
+.IR position ,
+which must be specified.
+.TP
+\-c
+Whenever an archive is created, an informational message to that effect
+is written to standard error.
+If the \-c option is specified,
+.I ar
+creates the archive silently.
+.TP
+\-d
+Delete the specified archive files.
+.TP
+\-i
+Identical to the \-b option.
+.TP
+\-m
+Move the specified archive files within the archive.
+If one of the options \-a, \-b or \-i is specified, the files are moved
+before or after the
+.I position
+file in the archive.
+If none of those options are specified, the files are moved
+to the end of the archive.
+.TP
+\-o
+Set the access and modification times of extracted files to the
+modification time of the file when it was entered into the archive.
+This will fail if the user is not the owner of the extracted file
+or the super-user.
+.TP
+\-p
+Write the contents of the specified archive files to the standard output.
+If no files are specified, the contents of all the files in the archive
+are written in the order they appear in the archive.
+.TP
+\-q
+(Quickly) append the specified files to the archive.
+If the archive does not exist a new archive file is created.
+Much faster than the \-r option, when creating a large archive
+piece-by-piece, as no checking is done to see if the files already
+exist in the archive.
+.TP
+\-r
+Replace or add the specified files to the archive.
+If the archive does not exist a new archive file is created.
+Files that replace existing files do not change the order of the files
+within the archive.
+New files are appended to the archive unless one of the options \-a, \-b
+or \-i is specified.
+.TP
+\-T
+Select and/or name archive members using only the first fifteen characters
+of the archive member or command line file name.
+The historic archive format had sixteen bytes for the name, but some
+historic archiver and loader implementations were unable to handle names
+that used the entire space.
+This means that file names that are not unique in their first fifteen
+characters can subsequently be confused.
+A warning message is printed to the standard error output if any file
+names are truncated.
+(See
+.IR ar (5)
+for more information.)
+.TP
+\-t
+List the specified files in the order in which they appear in the archive,
+each on a separate line.
+If no files are specified, all files in the archive are listed.
+.TP
+\-u
+Update files.
+When used with the \-r option, files in the archive will be replaced
+only if the disk file has a newer modification time than the file in
+the archive.
+When used with the \-x option, files in the archive will be extracted
+only if the archive file has a newer modification time than the file
+on disk.
+.TP
+\-v
+Provide verbose output.
+When used with the \-d, \-m, \-q or \-x options,
+.I ar
+gives a file-by-file description of the archive modification.
+This description consists of three, white-space separated fields: the
+option letter, a dash (``-'') and the file name.
+When used with the \-r option,
+.I ar
+displays the description as above, but the initial letter is an ``a'' if
+the file is added to the archive and an ``r'' if the file replaces a file
+already in the archive.
+.IP
+When used with the \-p option,
+the name of each printed file,
+enclosed in less-than (``<'') and greater-than (``>'') characters,
+is written to the standard output before
+the contents of the file;
+it is preceded by a single newline character, and
+followed by two newline characters.
+.IP
+When used with the \-t option,
+.I ar
+displays an ``ls -l'' style listing of information about the members of
+the archive.
+This listing consists of eight, white-space separated fields:
+the file permissions (see
+.IR strmode (3)),
+the decimal user and group ID's separated by a single slash (``/''),
+the file size (in bytes), the file modification time (in the
+.IR date (1)
+format ``%b %e %H:%M %Y''), and the name of the file.
+.TP
+\-x
+Extract the specified archive members into the files named by the command
+line arguments.
+If no members are specified, all the members of the archive are extracted into
+the current directory.
+.IP
+If the file does not exist, it is created; if it does exist, the owner
+and group will be unchanged.
+The file access and modification times are the time of the extraction
+(but see the \-o option).
+The file permissions will be set to those of the file when it was entered
+into the archive; this will fail if the user is not the owner of the
+extracted file or the super-user.
+.PP
+The
+.I ar
+utility exits 0 on success, and >0 if an error occurs.
+.SH ENVIRONMENT
+.TP
+TMPDIR
+The pathname of the directory to use when creating temporary files.
+.SH FILES
+.TP 14
+/tmp
+default temporary file directory
+.TP 14
+ar.XXXXXX
+temporary file names
+.SH COMPATIBILITY
+By default,
+.I ar
+writes archives that may be incompatible with historic archives, as
+the format used for storing archive members with names longer than
+fifteen characters has changed.
+This implementation of
+.I ar
+is backward compatible with previous versions of
+.I ar
+in that it can read and write (using the \-T option) historic archives.
+The \-T option is provided for compatibility only, and will be deleted
+in a future release.
+See
+.IR ar (5)
+for more information.
+.SH STANDARDS
+The
+.I ar
+utility is expected to offer a superset of the POSIX 1003.2 functionality.
+.SH "SEE ALSO"
+ld(1), ranlib(1), strmode(3), ar(5)
diff --git a/usr.bin/ar/ar.1aout b/usr.bin/ar/ar.1aout
new file mode 100644
index 0000000..026aed6
--- /dev/null
+++ b/usr.bin/ar/ar.1aout
@@ -0,0 +1,257 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Hugh Smith at The University of Guelph.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ar.1 8.1 (Berkeley) 6/29/93
+.\"
+.TH AR 1 "June 29, 1993"
+.AT 3
+.SH NAME
+ar \- create and maintain library archives
+.SH SYNOPSIS
+.nf
+.ft B
+ar -d [-Tv] archive file ...
+ar -m [-Tv] archive file ...
+ar -m [-abiTv] position archive file ...
+ar -p [-Tv] archive [file ...]
+ar -q [-cTv] archive file ...
+ar -r [-cuTv] archive file ...
+ar -r [-abciuTv] position archive file ...
+ar -t [-Tv] archive [file ...]
+ar -x [-ouTv] archive [file ...]
+.fi
+.ft R
+.SH DESCRIPTION
+The
+.I ar
+utility creates and maintains groups of files combined into an archive.
+Once an archive has been created, new files can be added and existing
+files can be extracted, deleted, or replaced.
+.PP
+Files are named in the archive by a single component, i.e., if a file
+referenced by a path containing a slash (``/'') is archived it will be
+named by the last component of that path.
+When matching paths listed on the command line against file names stored
+in the archive, only the last component of the path will be compared.
+.PP
+All informational and error messages use the path listed on the command
+line, if any was specified; otherwise the name in the archive is used.
+If multiple files in the archive have the same name, and paths are listed
+on the command line to ``select'' archive files for an operation, only the
+.B first
+file with a matching name will be selected.
+.PP
+The normal use of
+.I ar
+is for the creation and maintenance of libraries suitable for use with
+the loader (see
+.IR ld (1)),
+although it is not restricted to this purpose.
+The options are as follows:
+.TP
+\-a
+A positioning modifier used with the options \-r and \-m.
+The files are entered or moved
+.B after
+the archive member
+.IR position ,
+which must be specified.
+.TP
+\-b
+A positioning modifier used with the options \-r and \-m.
+The files are entered or moved
+.B before
+the archive member
+.IR position ,
+which must be specified.
+.TP
+\-c
+Whenever an archive is created, an informational message to that effect
+is written to standard error.
+If the \-c option is specified,
+.I ar
+creates the archive silently.
+.TP
+\-d
+Delete the specified archive files.
+.TP
+\-i
+Identical to the \-b option.
+.TP
+\-m
+Move the specified archive files within the archive.
+If one of the options \-a, \-b or \-i is specified, the files are moved
+before or after the
+.I position
+file in the archive.
+If none of those options are specified, the files are moved
+to the end of the archive.
+.TP
+\-o
+Set the access and modification times of extracted files to the
+modification time of the file when it was entered into the archive.
+This will fail if the user is not the owner of the extracted file
+or the super-user.
+.TP
+\-p
+Write the contents of the specified archive files to the standard output.
+If no files are specified, the contents of all the files in the archive
+are written in the order they appear in the archive.
+.TP
+\-q
+(Quickly) append the specified files to the archive.
+If the archive does not exist a new archive file is created.
+Much faster than the \-r option, when creating a large archive
+piece-by-piece, as no checking is done to see if the files already
+exist in the archive.
+.TP
+\-r
+Replace or add the specified files to the archive.
+If the archive does not exist a new archive file is created.
+Files that replace existing files do not change the order of the files
+within the archive.
+New files are appended to the archive unless one of the options \-a, \-b
+or \-i is specified.
+.TP
+\-T
+Select and/or name archive members using only the first fifteen characters
+of the archive member or command line file name.
+The historic archive format had sixteen bytes for the name, but some
+historic archiver and loader implementations were unable to handle names
+that used the entire space.
+This means that file names that are not unique in their first fifteen
+characters can subsequently be confused.
+A warning message is printed to the standard error output if any file
+names are truncated.
+(See
+.IR ar (5)
+for more information.)
+.TP
+\-t
+List the specified files in the order in which they appear in the archive,
+each on a separate line.
+If no files are specified, all files in the archive are listed.
+.TP
+\-u
+Update files.
+When used with the \-r option, files in the archive will be replaced
+only if the disk file has a newer modification time than the file in
+the archive.
+When used with the \-x option, files in the archive will be extracted
+only if the archive file has a newer modification time than the file
+on disk.
+.TP
+\-v
+Provide verbose output.
+When used with the \-d, \-m, \-q or \-x options,
+.I ar
+gives a file-by-file description of the archive modification.
+This description consists of three, white-space separated fields: the
+option letter, a dash (``-'') and the file name.
+When used with the \-r option,
+.I ar
+displays the description as above, but the initial letter is an ``a'' if
+the file is added to the archive and an ``r'' if the file replaces a file
+already in the archive.
+.IP
+When used with the \-p option,
+the name of each printed file,
+enclosed in less-than (``<'') and greater-than (``>'') characters,
+is written to the standard output before
+the contents of the file;
+it is preceded by a single newline character, and
+followed by two newline characters.
+.IP
+When used with the \-t option,
+.I ar
+displays an ``ls -l'' style listing of information about the members of
+the archive.
+This listing consists of eight, white-space separated fields:
+the file permissions (see
+.IR strmode (3)),
+the decimal user and group ID's separated by a single slash (``/''),
+the file size (in bytes), the file modification time (in the
+.IR date (1)
+format ``%b %e %H:%M %Y''), and the name of the file.
+.TP
+\-x
+Extract the specified archive members into the files named by the command
+line arguments.
+If no members are specified, all the members of the archive are extracted into
+the current directory.
+.IP
+If the file does not exist, it is created; if it does exist, the owner
+and group will be unchanged.
+The file access and modification times are the time of the extraction
+(but see the \-o option).
+The file permissions will be set to those of the file when it was entered
+into the archive; this will fail if the user is not the owner of the
+extracted file or the super-user.
+.PP
+The
+.I ar
+utility exits 0 on success, and >0 if an error occurs.
+.SH ENVIRONMENT
+.TP
+TMPDIR
+The pathname of the directory to use when creating temporary files.
+.SH FILES
+.TP 14
+/tmp
+default temporary file directory
+.TP 14
+ar.XXXXXX
+temporary file names
+.SH COMPATIBILITY
+By default,
+.I ar
+writes archives that may be incompatible with historic archives, as
+the format used for storing archive members with names longer than
+fifteen characters has changed.
+This implementation of
+.I ar
+is backward compatible with previous versions of
+.I ar
+in that it can read and write (using the \-T option) historic archives.
+The \-T option is provided for compatibility only, and will be deleted
+in a future release.
+See
+.IR ar (5)
+for more information.
+.SH STANDARDS
+The
+.I ar
+utility is expected to offer a superset of the POSIX 1003.2 functionality.
+.SH "SEE ALSO"
+ld(1), ranlib(1), strmode(3), ar(5)
diff --git a/usr.bin/ar/ar.5 b/usr.bin/ar/ar.5
new file mode 100644
index 0000000..29c8f8c
--- /dev/null
+++ b/usr.bin/ar/ar.5
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ar.5.5 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt AR 5
+.Os
+.Sh NAME
+.Nm ar
+.Nd archive (library) file format
+.Sh SYNOPSIS
+.Fd #include <ar.h>
+.Sh DESCRIPTION
+The archive command
+.Nm ar
+combines several files into one.
+Archives are mainly used as libraries of object files intended to be
+loaded using the link-editor
+.Xr ld 1 .
+.Pp
+A file created with
+.Nm ar
+begins with the ``magic'' string "!<arch>\en".
+The rest of the archive is made up of objects, each of which is composed
+of a header for a file, a possible file name, and the file contents.
+The header is portable between machine architectures, and, if the file
+contents are printable, the archive is itself printable.
+.Pp
+The header is made up of six variable length
+.Tn ASCII
+fields, followed by a
+two character trailer.
+The fields are the object name (16 characters), the file last modification
+time (12 characters), the user and group id's (each 6 characters), the file
+mode (8 characters) and the file size (10 characters).
+All numeric fields are in decimal, except for the file mode which is in
+octal.
+.Pp
+The modification time is the file
+.Fa st_mtime
+field, i.e.,
+.Dv CUT
+seconds since
+the epoch.
+The user and group id's are the file
+.Fa st_uid
+and
+.Fa st_gid
+fields.
+The file mode is the file
+.Fa st_mode
+field.
+The file size is the file
+.Fa st_size
+field.
+The two-byte trailer is the string "\`\en".
+.Pp
+Only the name field has any provision for overflow.
+If any file name is more than 16 characters in length or contains an
+embedded space, the string "#1/" followed by the
+.Tn ASCII
+length of the
+name is written in the name field.
+The file size (stored in the archive header) is incremented by the length
+of the name.
+The name is then written immediately following the archive header.
+.Pp
+Any unused characters in any of these fields are written as space
+characters.
+If any fields are their particular maximum number of characters in
+length, there will be no separation between the fields.
+.Pp
+Objects in the archive are always an even number of bytes long; files
+which are an odd number of bytes long are padded with a newline (``\en'')
+character, although the size in the header does not reflect this.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr stat 2
+.Sh HISTORY
+There have been at least four
+.Nm ar
+formats.
+The first was denoted by the leading ``magic'' number 0177555 (stored as
+type int).
+These archives were almost certainly created on a 16-bit machine, and
+contain headers made up of five fields.
+The fields are the object name (8 characters), the file last modification
+time (type long), the user id (type char), the file mode (type char) and
+the file size (type unsigned int).
+Files were padded to an even number of bytes.
+.Pp
+The second was denoted by the leading ``magic'' number 0177545 (stored as
+type int).
+These archives may have been created on either 16 or 32-bit machines, and
+contain headers made up of six fields.
+The fields are the object name (14 characters), the file last modification
+time (type long), the user and group id's (each type char), the file mode
+(type int) and the file size (type long).
+Files were padded to an even number of bytes.
+For more information on converting from this format see
+.Xr arcv 8 .
+.Pp
+The current archive format (without support for long character names and
+names with embedded spaces) was introduced in
+.Bx 4.0 .
+The headers were the same as the current format, with the exception that
+names longer than 16 characters were truncated, and names with embedded
+spaces (and often trailing spaces) were not supported.
+It has been extended for these reasons,
+as described above.
+This format first appeared in 4.4BSD.
+.Sh COMPATIBILITY
+No archive format is currently specified by any standard.
+.At V
+has historically distributed archives in a different format from
+all of the above.
diff --git a/usr.bin/ar/ar.5.5 b/usr.bin/ar/ar.5.5
new file mode 100644
index 0000000..29c8f8c
--- /dev/null
+++ b/usr.bin/ar/ar.5.5
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ar.5.5 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt AR 5
+.Os
+.Sh NAME
+.Nm ar
+.Nd archive (library) file format
+.Sh SYNOPSIS
+.Fd #include <ar.h>
+.Sh DESCRIPTION
+The archive command
+.Nm ar
+combines several files into one.
+Archives are mainly used as libraries of object files intended to be
+loaded using the link-editor
+.Xr ld 1 .
+.Pp
+A file created with
+.Nm ar
+begins with the ``magic'' string "!<arch>\en".
+The rest of the archive is made up of objects, each of which is composed
+of a header for a file, a possible file name, and the file contents.
+The header is portable between machine architectures, and, if the file
+contents are printable, the archive is itself printable.
+.Pp
+The header is made up of six variable length
+.Tn ASCII
+fields, followed by a
+two character trailer.
+The fields are the object name (16 characters), the file last modification
+time (12 characters), the user and group id's (each 6 characters), the file
+mode (8 characters) and the file size (10 characters).
+All numeric fields are in decimal, except for the file mode which is in
+octal.
+.Pp
+The modification time is the file
+.Fa st_mtime
+field, i.e.,
+.Dv CUT
+seconds since
+the epoch.
+The user and group id's are the file
+.Fa st_uid
+and
+.Fa st_gid
+fields.
+The file mode is the file
+.Fa st_mode
+field.
+The file size is the file
+.Fa st_size
+field.
+The two-byte trailer is the string "\`\en".
+.Pp
+Only the name field has any provision for overflow.
+If any file name is more than 16 characters in length or contains an
+embedded space, the string "#1/" followed by the
+.Tn ASCII
+length of the
+name is written in the name field.
+The file size (stored in the archive header) is incremented by the length
+of the name.
+The name is then written immediately following the archive header.
+.Pp
+Any unused characters in any of these fields are written as space
+characters.
+If any fields are their particular maximum number of characters in
+length, there will be no separation between the fields.
+.Pp
+Objects in the archive are always an even number of bytes long; files
+which are an odd number of bytes long are padded with a newline (``\en'')
+character, although the size in the header does not reflect this.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr stat 2
+.Sh HISTORY
+There have been at least four
+.Nm ar
+formats.
+The first was denoted by the leading ``magic'' number 0177555 (stored as
+type int).
+These archives were almost certainly created on a 16-bit machine, and
+contain headers made up of five fields.
+The fields are the object name (8 characters), the file last modification
+time (type long), the user id (type char), the file mode (type char) and
+the file size (type unsigned int).
+Files were padded to an even number of bytes.
+.Pp
+The second was denoted by the leading ``magic'' number 0177545 (stored as
+type int).
+These archives may have been created on either 16 or 32-bit machines, and
+contain headers made up of six fields.
+The fields are the object name (14 characters), the file last modification
+time (type long), the user and group id's (each type char), the file mode
+(type int) and the file size (type long).
+Files were padded to an even number of bytes.
+For more information on converting from this format see
+.Xr arcv 8 .
+.Pp
+The current archive format (without support for long character names and
+names with embedded spaces) was introduced in
+.Bx 4.0 .
+The headers were the same as the current format, with the exception that
+names longer than 16 characters were truncated, and names with embedded
+spaces (and often trailing spaces) were not supported.
+It has been extended for these reasons,
+as described above.
+This format first appeared in 4.4BSD.
+.Sh COMPATIBILITY
+No archive format is currently specified by any standard.
+.At V
+has historically distributed archives in a different format from
+all of the above.
diff --git a/usr.bin/ar/ar.c b/usr.bin/ar/ar.c
new file mode 100644
index 0000000..6dbef56
--- /dev/null
+++ b/usr.bin/ar/ar.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ar.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+CHDR chdr;
+u_int options;
+char *archive, *envtmp, *posarg, *posname;
+static void badoptions __P((char *));
+static void usage __P((void));
+
+/*
+ * main --
+ * main basically uses getopt to parse options and calls the appropriate
+ * functions. Some hacks that let us be backward compatible with 4.3 ar
+ * option parsing and sanity checking.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ char *p;
+ int (*fcall) __P((char **));
+
+ if (argc < 3)
+ usage();
+
+ /*
+ * Historic versions didn't require a '-' in front of the options.
+ * Fix it, if necessary.
+ */
+ if (*argv[1] != '-') {
+ if (!(p = malloc((u_int)(strlen(argv[1]) + 2))))
+ err(1, NULL);
+ *p = '-';
+ (void)strcpy(p + 1, argv[1]);
+ argv[1] = p;
+ }
+
+ while ((c = getopt(argc, argv, "abcdilmopqrTtuvx")) != EOF) {
+ switch(c) {
+ case 'a':
+ options |= AR_A;
+ break;
+ case 'b':
+ case 'i':
+ options |= AR_B;
+ break;
+ case 'c':
+ options |= AR_C;
+ break;
+ case 'd':
+ options |= AR_D;
+ fcall = delete;
+ break;
+ case 'l': /* not documented, compatibility only */
+ envtmp = ".";
+ break;
+ case 'm':
+ options |= AR_M;
+ fcall = move;
+ break;
+ case 'o':
+ options |= AR_O;
+ break;
+ case 'p':
+ options |= AR_P;
+ fcall = print;
+ break;
+ case 'q':
+ options |= AR_Q;
+ fcall = append;
+ break;
+ case 'r':
+ options |= AR_R;
+ fcall = replace;
+ break;
+ case 'T':
+ options |= AR_TR;
+ break;
+ case 't':
+ options |= AR_T;
+ fcall = contents;
+ break;
+ case 'u':
+ options |= AR_U;
+ break;
+ case 'v':
+ options |= AR_V;
+ break;
+ case 'x':
+ options |= AR_X;
+ fcall = extract;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ /* One of -dmpqrtx required. */
+ if (!(options & (AR_D|AR_M|AR_P|AR_Q|AR_R|AR_T|AR_X))) {
+ warnx("one of options -dmpqrtx is required");
+ usage();
+ }
+ /* Only one of -a and -bi allowed. */
+ if (options & AR_A && options & AR_B) {
+ warnx("only one of -a and -[bi] options allowed");
+ usage();
+ }
+ /* -ab require a position argument. */
+ if (options & (AR_A|AR_B)) {
+ if (!(posarg = *argv++)) {
+ warnx("no position operand specified");
+ usage();
+ }
+ posname = rname(posarg);
+ }
+ /* -d only valid with -Tv. */
+ if (options & AR_D && options & ~(AR_D|AR_TR|AR_V))
+ badoptions("-d");
+ /* -m only valid with -abiTv. */
+ if (options & AR_M && options & ~(AR_A|AR_B|AR_M|AR_TR|AR_V))
+ badoptions("-m");
+ /* -p only valid with -Tv. */
+ if (options & AR_P && options & ~(AR_P|AR_TR|AR_V))
+ badoptions("-p");
+ /* -q only valid with -cTv. */
+ if (options & AR_Q && options & ~(AR_C|AR_Q|AR_TR|AR_V))
+ badoptions("-q");
+ /* -r only valid with -abcuTv. */
+ if (options & AR_R && options & ~(AR_A|AR_B|AR_C|AR_R|AR_U|AR_TR|AR_V))
+ badoptions("-r");
+ /* -t only valid with -Tv. */
+ if (options & AR_T && options & ~(AR_T|AR_TR|AR_V))
+ badoptions("-t");
+ /* -x only valid with -ouTv. */
+ if (options & AR_X && options & ~(AR_O|AR_U|AR_TR|AR_V|AR_X))
+ badoptions("-x");
+
+ if (!(archive = *argv++)) {
+ warnx("no archive specified");
+ usage();
+ }
+
+ /* -dmqr require a list of archive elements. */
+ if (options & (AR_D|AR_M|AR_Q|AR_R) && !*argv) {
+ warnx("no archive members specified");
+ usage();
+ }
+
+ exit((*fcall)(argv));
+}
+
+static void
+badoptions(arg)
+ char *arg;
+{
+
+ warnx("illegal option combination for %s", arg);
+ usage();
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: ar -d [-Tv] archive file ...\n");
+ (void)fprintf(stderr, "\tar -m [-Tv] archive file ...\n");
+ (void)fprintf(stderr, "\tar -m [-abiTv] position archive file ...\n");
+ (void)fprintf(stderr, "\tar -p [-Tv] archive [file ...]\n");
+ (void)fprintf(stderr, "\tar -q [-cTv] archive file ...\n");
+ (void)fprintf(stderr, "\tar -r [-cuTv] archive file ...\n");
+ (void)fprintf(stderr, "\tar -r [-abciuTv] position archive file ...\n");
+ (void)fprintf(stderr, "\tar -t [-Tv] archive [file ...]\n");
+ (void)fprintf(stderr, "\tar -x [-ouTv] archive [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/ar/archive.c b/usr.bin/ar/archive.c
new file mode 100644
index 0000000..021e131
--- /dev/null
+++ b/usr.bin/ar/archive.c
@@ -0,0 +1,326 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)archive.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+typedef struct ar_hdr HDR;
+static char hb[sizeof(HDR) + 1]; /* real header */
+
+int
+open_archive(mode)
+ int mode;
+{
+ int created, fd, nr;
+ char buf[SARMAG];
+
+ created = 0;
+ if (mode & O_CREAT) {
+ mode |= O_EXCL;
+ if ((fd = open(archive, mode, DEFFILEMODE)) >= 0) {
+ /* POSIX.2 puts create message on stderr. */
+ if (!(options & AR_C))
+ warnx("creating archive %s", archive);
+ created = 1;
+ goto opened;
+ }
+ if (errno != EEXIST)
+ error(archive);
+ mode &= ~O_EXCL;
+ }
+ if ((fd = open(archive, mode, DEFFILEMODE)) < 0)
+ error(archive);
+
+ /*
+ * Attempt to place a lock on the opened file - if we get an
+ * error then someone is already working on this library (or
+ * it's going across NFS).
+ */
+opened: if (flock(fd, LOCK_EX|LOCK_NB) && errno != EOPNOTSUPP)
+ error(archive);
+
+ /*
+ * If not created, O_RDONLY|O_RDWR indicates that it has to be
+ * in archive format.
+ */
+ if (!created &&
+ ((mode & O_ACCMODE) == O_RDONLY || (mode & O_ACCMODE) == O_RDWR)) {
+ if ((nr = read(fd, buf, SARMAG) != SARMAG)) {
+ if (nr >= 0)
+ badfmt();
+ error(archive);
+ } else if (bcmp(buf, ARMAG, SARMAG))
+ badfmt();
+ } else if (write(fd, ARMAG, SARMAG) != SARMAG)
+ error(archive);
+ return (fd);
+}
+
+void
+close_archive(fd)
+ int fd;
+{
+
+ (void)close(fd); /* Implicit unlock. */
+}
+
+/* Convert ar header field to an integer. */
+#define AR_ATOI(from, to, len, base) { \
+ memmove(buf, from, len); \
+ buf[len] = '\0'; \
+ to = strtol(buf, (char **)NULL, base); \
+}
+
+/*
+ * get_arobj --
+ * read the archive header for this member
+ */
+int
+get_arobj(fd)
+ int fd;
+{
+ struct ar_hdr *hdr;
+ int len, nr;
+ char *p, buf[20];
+
+ nr = read(fd, hb, sizeof(HDR));
+ if (nr != sizeof(HDR)) {
+ if (!nr)
+ return (0);
+ if (nr < 0)
+ error(archive);
+ badfmt();
+ }
+
+ hdr = (struct ar_hdr *)hb;
+ if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1))
+ badfmt();
+
+ /* Convert the header into the internal format. */
+#define DECIMAL 10
+#define OCTAL 8
+
+ AR_ATOI(hdr->ar_date, chdr.date, sizeof(hdr->ar_date), DECIMAL);
+ AR_ATOI(hdr->ar_uid, chdr.uid, sizeof(hdr->ar_uid), DECIMAL);
+ AR_ATOI(hdr->ar_gid, chdr.gid, sizeof(hdr->ar_gid), DECIMAL);
+ AR_ATOI(hdr->ar_mode, chdr.mode, sizeof(hdr->ar_mode), OCTAL);
+ AR_ATOI(hdr->ar_size, chdr.size, sizeof(hdr->ar_size), DECIMAL);
+
+ /* Leading spaces should never happen. */
+ if (hdr->ar_name[0] == ' ')
+ badfmt();
+
+ /*
+ * Long name support. Set the "real" size of the file, and the
+ * long name flag/size.
+ */
+ if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) {
+ chdr.lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1);
+ if (len <= 0 || len > MAXNAMLEN)
+ badfmt();
+ nr = read(fd, chdr.name, len);
+ if (nr != len) {
+ if (nr < 0)
+ error(archive);
+ badfmt();
+ }
+ chdr.name[len] = 0;
+ chdr.size -= len;
+ } else {
+ chdr.lname = 0;
+ memmove(chdr.name, hdr->ar_name, sizeof(hdr->ar_name));
+
+ /* Strip trailing spaces, null terminate. */
+ for (p = chdr.name + sizeof(hdr->ar_name) - 1; *p == ' '; --p);
+ *++p = '\0';
+ }
+ return (1);
+}
+
+static int already_written;
+
+/*
+ * put_arobj --
+ * Write an archive member to a file.
+ */
+void
+put_arobj(cfp, sb)
+ CF *cfp;
+ struct stat *sb;
+{
+ int lname;
+ char *name;
+ struct ar_hdr *hdr;
+ off_t size;
+
+ /*
+ * If passed an sb structure, reading a file from disk. Get stat(2)
+ * information, build a name and construct a header. (Files are named
+ * by their last component in the archive.) If not, then just write
+ * the last header read.
+ */
+ if (sb) {
+ name = rname(cfp->rname);
+ (void)fstat(cfp->rfd, sb);
+
+ /*
+ * If not truncating names and the name is too long or contains
+ * a space, use extended format 1.
+ */
+ lname = strlen(name);
+ if (options & AR_TR) {
+ if (lname > OLDARMAXNAME) {
+ (void)fflush(stdout);
+ warnx("warning: %s truncated to %.*s",
+ name, OLDARMAXNAME, name);
+ (void)fflush(stderr);
+ }
+ (void)sprintf(hb, HDR3, name, sb->st_mtimespec.ts_sec,
+ sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size,
+ ARFMAG);
+ lname = 0;
+ } else if (lname > sizeof(hdr->ar_name) || strchr(name, ' '))
+ (void)sprintf(hb, HDR1, AR_EFMT1, lname,
+ sb->st_mtimespec.ts_sec, sb->st_uid, sb->st_gid,
+ sb->st_mode, sb->st_size + lname, ARFMAG);
+ else {
+ lname = 0;
+ (void)sprintf(hb, HDR2, name, sb->st_mtimespec.ts_sec,
+ sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size,
+ ARFMAG);
+ }
+ size = sb->st_size;
+ } else {
+ lname = chdr.lname;
+ name = chdr.name;
+ size = chdr.size;
+ }
+
+ if (write(cfp->wfd, hb, sizeof(HDR)) != sizeof(HDR))
+ error(cfp->wname);
+ if (lname) {
+ if (write(cfp->wfd, name, lname) != lname)
+ error(cfp->wname);
+ already_written = lname;
+ }
+ copy_ar(cfp, size);
+ already_written = 0;
+}
+
+/*
+ * copy_ar --
+ * Copy size bytes from one file to another - taking care to handle the
+ * extra byte (for odd size files) when reading archives and writing an
+ * extra byte if necessary when adding files to archive. The length of
+ * the object is the long name plus the object itself; the variable
+ * already_written gets set if a long name was written.
+ *
+ * The padding is really unnecessary, and is almost certainly a remnant
+ * of early archive formats where the header included binary data which
+ * a PDP-11 required to start on an even byte boundary. (Or, perhaps,
+ * because 16-bit word addressed copies were faster?) Anyhow, it should
+ * have been ripped out long ago.
+ */
+void
+copy_ar(cfp, size)
+ CF *cfp;
+ off_t size;
+{
+ static char pad = '\n';
+ off_t sz;
+ int from, nr, nw, off, to;
+ char buf[8*1024];
+
+ if (!(sz = size))
+ return;
+
+ from = cfp->rfd;
+ to = cfp->wfd;
+ sz = size;
+ while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) {
+ sz -= nr;
+ for (off = 0; off < nr; nr -= off, off += nw)
+ if ((nw = write(to, buf + off, nr)) < 0)
+ error(cfp->wname);
+ }
+ if (sz) {
+ if (nr == 0)
+ badfmt();
+ error(cfp->rname);
+ }
+
+ if (cfp->flags & RPAD && (size + chdr.lname) & 1 &&
+ (nr = read(from, buf, 1)) != 1) {
+ if (nr == 0)
+ badfmt();
+ error(cfp->rname);
+ }
+ if (cfp->flags & WPAD && (size + already_written) & 1 &&
+ write(to, &pad, 1) != 1)
+ error(cfp->wname);
+}
+
+/*
+ * skip_arobj -
+ * Skip over an object -- taking care to skip the pad bytes.
+ */
+void
+skip_arobj(fd)
+ int fd;
+{
+ off_t len;
+
+ len = chdr.size + (chdr.size + chdr.lname & 1);
+ if (lseek(fd, len, SEEK_CUR) == (off_t)-1)
+ error(archive);
+}
diff --git a/usr.bin/ar/archive.h b/usr.bin/ar/archive.h
new file mode 100644
index 0000000..23b391f
--- /dev/null
+++ b/usr.bin/ar/archive.h
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)archive.h 8.3 (Berkeley) 4/2/94
+ */
+
+/* Ar(1) options. */
+#define AR_A 0x0001
+#define AR_B 0x0002
+#define AR_C 0x0004
+#define AR_D 0x0008
+#define AR_M 0x0010
+#define AR_O 0x0020
+#define AR_P 0x0040
+#define AR_Q 0x0080
+#define AR_R 0x0100
+#define AR_T 0x0200
+#define AR_TR 0x0400
+#define AR_U 0x0800
+#define AR_V 0x1000
+#define AR_X 0x2000
+extern u_int options;
+
+/* Set up file copy. */
+#define SETCF(from, fromname, to, toname, pad) { \
+ cf.rfd = from; \
+ cf.rname = fromname; \
+ cf.wfd = to; \
+ cf.wname = toname; \
+ cf.flags = pad; \
+}
+
+/* File copy structure. */
+typedef struct {
+ int rfd; /* read file descriptor */
+ char *rname; /* read name */
+ int wfd; /* write file descriptor */
+ char *wname; /* write name */
+#define NOPAD 0x00 /* don't pad */
+#define RPAD 0x01 /* pad on reads */
+#define WPAD 0x02 /* pad on writes */
+ u_int flags; /* pad flags */
+} CF;
+
+/* Header structure internal format. */
+typedef struct {
+ off_t size; /* size of the object in bytes */
+ long date; /* date */
+ int lname; /* size of the long name in bytes */
+ int gid; /* group */
+ int uid; /* owner */
+ u_short mode; /* permissions */
+ char name[MAXNAMLEN + 1]; /* name */
+} CHDR;
+
+/* Header format strings. */
+#define HDR1 "%s%-13d%-12ld%-6u%-6u%-8o%-10qd%2s"
+#define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10qd%2s"
+
+#define OLDARMAXNAME 15
+#define HDR3 "%-16.15s%-12ld%-6u%-6u%-8o%-10qd%2s"
+
+
+#include <sys/cdefs.h>
+
+struct stat;
+
+void close_archive __P((int));
+void copy_ar __P((CF *, off_t));
+int get_arobj __P((int));
+int open_archive __P((int));
+void put_arobj __P((CF *, struct stat *));
+void skip_arobj __P((int));
diff --git a/usr.bin/ar/contents.c b/usr.bin/ar/contents.c
new file mode 100644
index 0000000..e000d535d
--- /dev/null
+++ b/usr.bin/ar/contents.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)contents.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+/*
+ * contents --
+ * Handles t[v] option - opens the archive and then reads headers,
+ * skipping member contents.
+ */
+int
+contents(argv)
+ char **argv;
+{
+ int afd, all;
+ struct tm *tp;
+ char *file, buf[25];
+
+ afd = open_archive(O_RDONLY);
+
+ for (all = !*argv; get_arobj(afd);) {
+ if (all)
+ file = chdr.name;
+ else if (!(file = files(argv)))
+ goto next;
+ if (options & AR_V) {
+ (void)strmode(chdr.mode, buf);
+ (void)printf("%s %6d/%-6d %8qd ",
+ buf + 1, chdr.uid, chdr.gid, chdr.size);
+ tp = localtime(&chdr.date);
+ (void)strftime(buf, sizeof(buf), "%c", tp);
+ buf[16] = '\0';
+ buf[24] = '\0';
+ (void)printf("%s %s %s\n", buf + 4, buf + 20, file);
+ } else
+ (void)printf("%s\n", file);
+ if (!all && !*argv)
+ break;
+next: skip_arobj(afd);
+ }
+ close_archive(afd);
+
+ if (*argv) {
+ orphans(argv);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/ar/delete.c b/usr.bin/ar/delete.c
new file mode 100644
index 0000000..5824b61
--- /dev/null
+++ b/usr.bin/ar/delete.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)delete.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*-
+ * delete --
+ * Deletes named members from the archive.
+ */
+int
+delete(argv)
+ char **argv;
+{
+ CF cf;
+ off_t size;
+ int afd, tfd;
+ char *file;
+
+ afd = open_archive(O_RDWR);
+ tfd = tmp();
+
+ /* Read and write to an archive; pad on both. */
+ SETCF(afd, archive, tfd, tname, RPAD|WPAD);
+ while (get_arobj(afd)) {
+ if (*argv && (file = files(argv))) {
+ if (options & AR_V)
+ (void)printf("d - %s\n", file);
+ skip_arobj(afd);
+ continue;
+ }
+ put_arobj(&cf, (struct stat *)NULL);
+ }
+
+ size = lseek(tfd, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd, (off_t)0, SEEK_SET);
+ (void)lseek(afd, (off_t)SARMAG, SEEK_SET);
+ SETCF(tfd, tname, afd, archive, NOPAD);
+ copy_ar(&cf, size);
+ (void)close(tfd);
+ (void)ftruncate(afd, size + SARMAG);
+ close_archive(afd);
+
+ if (*argv) {
+ orphans(argv);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/ar/extern.h b/usr.bin/ar/extern.h
new file mode 100644
index 0000000..a6d7d71
--- /dev/null
+++ b/usr.bin/ar/extern.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ */
+
+int append __P((char **));
+void badfmt __P((void));
+int compare __P((char *));
+int contents __P((char **));
+int delete __P((char **));
+void error __P((char *));
+int extract __P((char **));
+char *files __P((char **argv));
+int move __P((char **));
+void orphans __P((char **argv));
+int print __P((char **));
+int replace __P((char **));
+char *rname __P((char *));
+int tmp __P((void));
+
+extern char *archive;
+extern char *posarg, *posname; /* positioning file name */
+extern char *tname; /* temporary file "name" */
+extern CHDR chdr; /* converted header */
diff --git a/usr.bin/ar/extract.c b/usr.bin/ar/extract.c
new file mode 100644
index 0000000..c80e37c
--- /dev/null
+++ b/usr.bin/ar/extract.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)extract.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+/*
+ * extract --
+ * Extract files from the named archive - if member names given only
+ * extract those members otherwise extract all members. If 'o' option
+ * selected modify date of newly created file to be same as archive
+ * members date otherwise date is time of extraction. Does not modify
+ * archive.
+ */
+int
+extract(argv)
+ char **argv;
+{
+ char *file;
+ int afd, all, eval, tfd;
+ struct timeval tv[2];
+ struct stat sb;
+ CF cf;
+
+ eval = 0;
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+
+ afd = open_archive(O_RDONLY);
+
+ /* Read from an archive, write to disk; pad on read. */
+ SETCF(afd, archive, 0, 0, RPAD);
+ for (all = !*argv; get_arobj(afd);) {
+ if (all)
+ file = chdr.name;
+ else if (!(file = files(argv))) {
+ skip_arobj(afd);
+ continue;
+ }
+
+ if (options & AR_U && !stat(file, &sb) &&
+ sb.st_mtime > chdr.date)
+ continue;
+
+ if ((tfd = open(file, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR)) < 0) {
+ warn("%s", file);
+ skip_arobj(afd);
+ eval = 1;
+ continue;
+ }
+
+ if (options & AR_V)
+ (void)printf("x - %s\n", file);
+
+ cf.wfd = tfd;
+ cf.wname = file;
+ copy_ar(&cf, chdr.size);
+
+ if (fchmod(tfd, (short)chdr.mode)) {
+ warn("chmod: %s", file);
+ eval = 1;
+ }
+ if (options & AR_O) {
+ tv[0].tv_sec = tv[1].tv_sec = chdr.date;
+ if (utimes(file, tv)) {
+ warn("utimes: %s", file);
+ eval = 1;
+ }
+ }
+ (void)close(tfd);
+ if (!all && !*argv)
+ break;
+ }
+ close_archive(afd);
+
+ if (*argv) {
+ orphans(argv);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/ar/misc.c b/usr.bin/ar/misc.c
new file mode 100644
index 0000000..2a2ffb0
--- /dev/null
+++ b/usr.bin/ar/misc.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+#include "pathnames.h"
+
+char *tname = "temporary file"; /* temporary file "name" */
+
+int
+tmp()
+{
+ extern char *envtmp;
+ sigset_t set, oset;
+ static int first;
+ int fd;
+ char path[MAXPATHLEN];
+
+ if (!first && !envtmp) {
+ envtmp = getenv("TMPDIR");
+ first = 1;
+ }
+
+ if (envtmp)
+ (void)sprintf(path, "%s/%s", envtmp, _NAME_ARTMP);
+ else
+ strcpy(path, _PATH_ARTMP);
+
+ sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((fd = mkstemp(path)) == -1)
+ error(tname);
+ (void)unlink(path);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ return (fd);
+}
+
+/*
+ * files --
+ * See if the current file matches any file in the argument list; if it
+ * does, remove it from the argument list.
+ */
+char *
+files(argv)
+ char **argv;
+{
+ char **list, *p;
+
+ for (list = argv; *list; ++list)
+ if (compare(*list)) {
+ p = *list;
+ for (; list[0] = list[1]; ++list)
+ continue;
+ return (p);
+ }
+ return (NULL);
+}
+
+void
+orphans(argv)
+ char **argv;
+{
+
+ for (; *argv; ++argv)
+ warnx("%s: not found in archive", *argv);
+}
+
+char *
+rname(path)
+ char *path;
+{
+ char *ind;
+
+ return ((ind = strrchr(path, '/')) ? ind + 1 : path);
+}
+
+int
+compare(dest)
+ char *dest;
+{
+ int maxname = (options & AR_TR) ? OLDARMAXNAME : MAXNAMLEN;
+ return (!strncmp(chdr.name, rname(dest), maxname));
+}
+
+void
+badfmt()
+{
+
+ errx(1, "%s: %s", archive, strerror(EFTYPE));
+}
+
+void
+error(name)
+ char *name;
+{
+
+ errx(1, "%s", name);
+}
diff --git a/usr.bin/ar/move.c b/usr.bin/ar/move.c
new file mode 100644
index 0000000..c2dba36
--- /dev/null
+++ b/usr.bin/ar/move.c
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)move.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * move --
+ * Change location of named members in archive - if 'b' or 'i' option
+ * selected then named members are placed before 'posname'. If 'a'
+ * option selected members go after 'posname'. If no options, members
+ * are moved to end of archive.
+ */
+int
+move(argv)
+ char **argv;
+{
+ CF cf;
+ off_t size, tsize;
+ int afd, curfd, mods, tfd1, tfd2, tfd3;
+ char *file;
+
+ afd = open_archive(O_RDWR);
+ mods = options & (AR_A|AR_B);
+
+ tfd1 = tmp(); /* Files before key file. */
+ tfd2 = tmp(); /* Files selected by user. */
+ tfd3 = tmp(); /* Files after key file. */
+
+ /*
+ * Break archive into three parts -- selected entries and entries
+ * before and after the key entry. If positioning before the key,
+ * place the key at the beginning of the after key entries and if
+ * positioning after the key, place the key at the end of the before
+ * key entries. Put it all back together at the end.
+ */
+
+ /* Read and write to an archive; pad on both. */
+ SETCF(afd, archive, 0, tname, RPAD|WPAD);
+ for (curfd = tfd1; get_arobj(afd);) {
+ if (*argv && (file = files(argv))) {
+ if (options & AR_V)
+ (void)printf("m - %s\n", file);
+ cf.wfd = tfd2;
+ put_arobj(&cf, (struct stat *)NULL);
+ continue;
+ }
+ if (mods && compare(posname)) {
+ mods = 0;
+ if (options & AR_B)
+ curfd = tfd3;
+ cf.wfd = curfd;
+ put_arobj(&cf, (struct stat *)NULL);
+ if (options & AR_A)
+ curfd = tfd3;
+ } else {
+ cf.wfd = curfd;
+ put_arobj(&cf, (struct stat *)NULL);
+ }
+ }
+
+ if (mods) {
+ warnx("%s: archive member not found", posarg);
+ close_archive(afd);
+ return (1);
+ }
+ (void)lseek(afd, (off_t)SARMAG, SEEK_SET);
+
+ SETCF(tfd1, tname, afd, archive, NOPAD);
+ tsize = size = lseek(tfd1, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd1, (off_t)0, SEEK_SET);
+ copy_ar(&cf, size);
+
+ tsize += size = lseek(tfd2, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd2, (off_t)0, SEEK_SET);
+ cf.rfd = tfd2;
+ copy_ar(&cf, size);
+
+ tsize += size = lseek(tfd3, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd3, (off_t)0, SEEK_SET);
+ cf.rfd = tfd3;
+ copy_ar(&cf, size);
+
+ (void)ftruncate(afd, tsize + SARMAG);
+ close_archive(afd);
+
+ if (*argv) {
+ orphans(argv);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/ar/pathnames.h b/usr.bin/ar/pathnames.h
new file mode 100644
index 0000000..ed92db6
--- /dev/null
+++ b/usr.bin/ar/pathnames.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _NAME_ARTMP "ar.XXXXXX"
+#define _PATH_ARTMP "/tmp/ar.XXXXXX"
diff --git a/usr.bin/ar/print.c b/usr.bin/ar/print.c
new file mode 100644
index 0000000..4367628
--- /dev/null
+++ b/usr.bin/ar/print.c
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+/*
+ * print --
+ * Prints archive members on stdout - if member names given only
+ * print those members, otherwise print all members.
+ */
+int
+print(argv)
+ char **argv;
+{
+ CF cf;
+ int afd, all;
+ char *file;
+
+ afd = open_archive(O_RDONLY);
+
+ /* Read from an archive, write to stdout; pad on read. */
+ SETCF(afd, archive, STDOUT_FILENO, "stdout", RPAD);
+ for (all = !*argv; get_arobj(afd);) {
+ if (all)
+ file = chdr.name;
+ else if (!(file = files(argv))) {
+ skip_arobj(afd);
+ continue;
+ }
+ if (options & AR_V) {
+ (void)printf("\n<%s>\n\n", file);
+ (void)fflush(stdout);
+ }
+ copy_ar(&cf, chdr.size);
+ if (!all && !*argv)
+ break;
+ }
+ close_archive(afd);
+
+ if (*argv) {
+ orphans(argv);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/ar/replace.c b/usr.bin/ar/replace.c
new file mode 100644
index 0000000..a226235
--- /dev/null
+++ b/usr.bin/ar/replace.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)replace.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ar.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "archive.h"
+#include "extern.h"
+
+/*
+ * replace --
+ * Replace or add named members to archive. Entries already in the
+ * archive are swapped in place. Others are added before or after
+ * the key entry, based on the a, b and i options. If the u option
+ * is specified, modification dates select for replacement.
+ */
+int
+replace(argv)
+ char **argv;
+{
+ char *file;
+ int afd, curfd, errflg, exists, mods, sfd, tfd1, tfd2;
+ struct stat sb;
+ CF cf;
+ off_t size, tsize;
+
+ errflg = 0;
+ /*
+ * If doesn't exist, simply append to the archive. There's
+ * a race here, but it's pretty short, and not worth fixing.
+ */
+ exists = !stat(archive, &sb);
+ afd = open_archive(O_CREAT|O_RDWR);
+
+ if (!exists) {
+ tfd1 = -1;
+ tfd2 = tmp();
+ goto append;
+ }
+
+ tfd1 = tmp(); /* Files before key file. */
+ tfd2 = tmp(); /* Files after key file. */
+
+ /*
+ * Break archive into two parts -- entries before and after the key
+ * entry. If positioning before the key, place the key at the
+ * beginning of the after key entries and if positioning after the
+ * key, place the key at the end of the before key entries. Put it
+ * all back together at the end.
+ */
+ mods = (options & (AR_A|AR_B));
+ for (curfd = tfd1; get_arobj(afd);) {
+ if (*argv && (file = files(argv))) {
+ if ((sfd = open(file, O_RDONLY)) < 0) {
+ errflg = 1;
+ warn("%s", file);
+ goto useold;
+ }
+ (void)fstat(sfd, &sb);
+ if (options & AR_U && sb.st_mtime <= chdr.date) {
+ (void) close(sfd);
+ goto useold;
+ }
+
+ if (options & AR_V)
+ (void)printf("r - %s\n", file);
+
+ /* Read from disk, write to an archive; pad on write */
+ SETCF(sfd, file, curfd, tname, WPAD);
+ put_arobj(&cf, &sb);
+ (void)close(sfd);
+ skip_arobj(afd);
+ continue;
+ }
+
+ if (mods && compare(posname)) {
+ mods = 0;
+ if (options & AR_B)
+ curfd = tfd2;
+ /* Read and write to an archive; pad on both. */
+ SETCF(afd, archive, curfd, tname, RPAD|WPAD);
+ put_arobj(&cf, (struct stat *)NULL);
+ if (options & AR_A)
+ curfd = tfd2;
+ } else {
+ /* Read and write to an archive; pad on both. */
+useold: SETCF(afd, archive, curfd, tname, RPAD|WPAD);
+ put_arobj(&cf, (struct stat *)NULL);
+ }
+ }
+
+ if (mods) {
+ warnx("%s: archive member not found", posarg);
+ close_archive(afd);
+ return (1);
+ }
+
+ /* Append any left-over arguments to the end of the after file. */
+append: while (file = *argv++) {
+ if (options & AR_V)
+ (void)printf("a - %s\n", file);
+ if ((sfd = open(file, O_RDONLY)) < 0) {
+ errflg = 1;
+ warn("%s", file);
+ continue;
+ }
+ (void)fstat(sfd, &sb);
+ /* Read from disk, write to an archive; pad on write. */
+ SETCF(sfd, file,
+ options & (AR_A|AR_B) ? tfd1 : tfd2, tname, WPAD);
+ put_arobj(&cf, &sb);
+ (void)close(sfd);
+ }
+
+ (void)lseek(afd, (off_t)SARMAG, SEEK_SET);
+
+ SETCF(tfd1, tname, afd, archive, NOPAD);
+ if (tfd1 != -1) {
+ tsize = size = lseek(tfd1, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd1, (off_t)0, SEEK_SET);
+ copy_ar(&cf, size);
+ } else
+ tsize = 0;
+
+ tsize += size = lseek(tfd2, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd2, (off_t)0, SEEK_SET);
+ cf.rfd = tfd2;
+ copy_ar(&cf, size);
+
+ (void)ftruncate(afd, tsize + SARMAG);
+ close_archive(afd);
+ return (errflg);
+}
diff --git a/usr.bin/at/LEGAL b/usr.bin/at/LEGAL
new file mode 100644
index 0000000..92b1b49
--- /dev/null
+++ b/usr.bin/at/LEGAL
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+Sorry for the long wait, but there still were a few things to
+be ironed out in at, which I've finally done :-)
+
+The FreeBSD team does have my permission to use at, version 2.9,
+under the BSD license.
+
+You'll find it on sunsite.unc.edu's Incoming, hopefully; the
+md5 checksum is
+
+3ba2ca3c0e87e1a04feae2c6c1376b0d at-2.9.tgz
+
+Best regards
+ Thomas
+- --
+Thomas Koenig, Thomas.Koenig@ciw.uni-karlsruhe.de, ig25@dkauni2.bitnet.
+The joy of engineering is to find a straight line on a double
+logarithmic diagram.
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.2i
+
+iQCVAwUBMCjVrPBu+cbJcKCVAQFNiQP/dpWP57s/E8plVGUD3zfgOXDmKUvg8U7a
+VwRzJrIMuSgnSJs0wkpvcomc3NLicipfX7hhWLh/xatPM2YbF7O5HZoNdvWvexD2
+1Y67zJ+0HFb1mPnSBOrS5RFiQAe3KqmGec6E14Rih/qNoFQZBVRFXZ4xxuwP+0Rs
+e2U+TVTUz6A=
+=TvyW
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/at/Makefile b/usr.bin/at/Makefile
new file mode 100644
index 0000000..e1e3096
--- /dev/null
+++ b/usr.bin/at/Makefile
@@ -0,0 +1,32 @@
+# $Id: Makefile,v 1.1 1994/01/05 01:08:51 nate Exp $
+
+.include "${.CURDIR}/Makefile.inc"
+
+PROG= at
+SRCS= at.c panic.c parsetime.c perm.c
+LINKS= ${BINDIR}/at ${BINDIR}/atq \
+ ${BINDIR}/at ${BINDIR}/atrm \
+ ${BINDIR}/at ${BINDIR}/batch
+MLINKS= at.1 batch.1 \
+ at.1 atq.1 \
+ at.1 atrm.1
+
+BINOWN= root
+BINMODE= 4555
+MANSRC= .
+CLEANFILES += ${MAN1}
+MANDEPEND = ${MAN1}
+
+.include <bsd.prog.mk>
+
+${MAN1}: at.man
+ sed -e \
+ "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \
+ s@_ATJOB_DIR@$(ATJOB_DIR)@g; \
+ s@_DEFAULT_BATCH_QUEUE@$(DEFAULT_BATCH_QUEUE)@g; \
+ s@_DEFAULT_AT_QUEUE@$(DEFAULT_AT_QUEUE)@g; \
+ s@_LOADAVG_MX@$(LOADAVG_MX)@g; \
+ s@_PERM_PATH@$(PERM_PATH)@g; \
+ s@_LOCKFILE@$(LOCKFILE)@g" \
+ < $? > $@
+
diff --git a/usr.bin/at/Makefile.inc b/usr.bin/at/Makefile.inc
new file mode 100644
index 0000000..ec44e1f
--- /dev/null
+++ b/usr.bin/at/Makefile.inc
@@ -0,0 +1,19 @@
+VERSION= 2.9
+LOCKFILE = .lockfile
+ATSPOOL_DIR=/var/at/spool
+ATJOB_DIR=/var/at/jobs
+ATLIB_DIR=/usr/libexec
+LOADAVG_MX=1.5
+DAEMON_GID=1
+DAEMON_UID=1
+DEFAULT_BATCH_QUEUE=E
+DEFAULT_AT_QUEUE=c
+PERM_PATH=/var/at
+
+CFLAGS += -DATJOB_DIR=\"$(ATJOB_DIR)/\" \
+ -DLFILE=\"$(ATJOB_DIR)/$(LOCKFILE)\" \
+ -DLOADAVG_MX=$(LOADAVG_MX) -DATSPOOL_DIR=\"$(ATSPOOL_DIR)\" \
+ -DVERSION=\"$(VERSION)\" -DDAEMON_UID=$(DAEMON_UID) -DDAEMON_GID=$(DAEMON_GID) \
+ -DDEFAULT_BATCH_QUEUE=\'$(DEFAULT_BATCH_QUEUE)\' \
+ -DDEFAULT_AT_QUEUE=\'$(DEFAULT_AT_QUEUE)\' -DPERM_PATH=\"$(PERM_PATH)/\"
+
diff --git a/usr.bin/at/at.1 b/usr.bin/at/at.1
new file mode 100644
index 0000000..e33ba82
--- /dev/null
+++ b/usr.bin/at/at.1
@@ -0,0 +1,216 @@
+.\"
+.\" Copyright (c) 1993 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: at.1,v 1.1 1994/01/05 01:08:56 nate Exp $
+.\"
+.Dd December 5, 1993
+.Dt "AT" 1
+.Os FreeBSD 1.1
+.Sh NAME
+.Nm at, batch, atq, atrm
+.Nd queue, examine, or delete jobs for later execution
+.\"
+.Sh SYOPSIS
+.Nm at
+.Op Fl q Ar queue
+.Op Fl f Ar file
+.Op Fl m
+.Ar time
+.Pp
+.Nm atq
+.Op Fl q Ar queue
+.Op Fl v
+.Pp
+.Nm atrm
+.Ar job
+.Op Ar job ...
+.Pp
+.Nm batch
+.Op Fl f Ar file
+.Op Fl m
+.Ar time
+.Sh DESCRIPTION
+The
+.Nm at
+and
+.Nm batch
+utilities read commands from the standard input or a specified file
+which are to be executed at a later time, using
+.Xr sh 1 .
+.Pp
+The functions of the commands are as follows:
+.Bl -tag -width indent
+.It Nm at
+Executes commands at a specified time.
+.It Nm atq
+Lists the user's pending jobs, unless the user is
+the superuser. In that case, everybody's jobs are
+listed.
+.It Nm atrm
+Deletes jobs.
+.It Nm batch
+executes commands when system load levels permit.
+In other words, it executes the commands when the load
+average drops below a specified level.
+.El
+.Pp
+For both
+.Nm at
+and
+.Nm batch ,
+the working directory, environment (except for the variables
+.Nm TERM ,
+.Nm TERMCAP ,
+.Nm DISPLAY ,
+and
+.Nm _ )
+and the umask are retained from the time of invocation. The user
+will be mailed the standard output and standard error from
+his commands if any output is generated. If
+.Nm at
+is executed from a
+.Xr su 1
+shell, the owner of the login whell will receive the mail.
+.Sh OPTIONS
+.Bl -tag -width indent
+The available options are as follows:
+.It Fl q Ar queue
+Use the specified queue. A queue designation consists
+of a single letter; valid queue designation range from
+.Ar a
+to
+.Ar l .
+The
+.Ar a
+queue is the default, and
+.Ar b
+is the batch queue. Queues with higher letters run with
+increased niceness. If
+.Nm atq
+is given a specific queue, it will only show jobs pending
+in that queue.
+.It Fl m
+Send mail to the user when the job has completed, even if
+there was no output.
+.It Fl f Ar file
+Reads the job from
+.Ar file
+rather than the standard input.
+.It Fl v
+Shows completed but not yet deleted jobs in the queue.
+.Sh TIME SPECIFICATION
+.Nm At
+allows some moderately complex time specifications.
+It accepts times of the form
+.Ar HHMM
+or
+.Ar HH:MM
+to run a job at a specific time of day. If
+that time is already passed, the next day is assumed.
+You may also specify
+.Nm midnight ,
+.Nm noon ,
+or
+.Nm teatime
+(4PM) and you can give a time of day suffixed with
+.Nm AM
+or
+.Nm PM
+for running in the morning or the evening. You can
+also specify the date on which the job will be run
+by giving a date in the form
+.Ar month-name day
+with an optional
+.Ar year ,
+or giving a date of the form
+.Ar MMDDYY ,
+.Ar MM/DD/YY
+or
+.Ar DD.MM.YY .
+You can also give times like
+.Nm now +
+.Ar count time-units ,
+where the time units can be
+.Nm minutes, hours, days,
+or
+.Nm weeks
+You can suffix the time with
+.Nm today
+to run the job today, or
+.Nm tomorrow
+to run the job tomorrow.
+.Pp
+For example, to run a job at 4PM three days from now, you
+would specify a time of
+.Nm 4PM + 3 days .
+To run a job at 10:00AM on on July 31, you would specify
+a time of
+.Nm 10AM Jul 31 .
+Finally, to run a job at 1AM tomorrow, you would specify
+a time of
+.Nm 1AM tomorrow .
+.Sh FILES
+.Bl -tag -width /var/at/lockfile -compact
+.It Pa /var/at/jobs
+Directory containing job files
+.It Pa /var/at/spool
+Directory containing output spool files
+.It Pa /var/at/lockfile
+Job-creation lock file.
+.It Pa /var/run/utmp
+.El
+.Sh SEE ALSO
+.Xr crond 8 ,
+.Xr nice 1 ,
+.Xr sh 1 ,
+.Xr atrun 8
+.Sh AUTHOR
+.Bl -tag
+Thomas Koenig, ig25@rz.uni-karlsruhe.de
+.El
+.Sh BUGS
+Traditional access control to
+.Nm at
+and
+.Nm batch
+via the files
+.Pa /var/at/at.allow
+and
+.Pa /var/at/at.deny
+is not implemented.
+.Pp
+If the file
+.Pa /var/run/utmp
+is not available or corrupted, or if the user is not
+logged in at the time
+.Nm at
+is invoked, the mail is sent to the userid found in the
+environment variable
+.Nm LOGNAME .
+If that is undefined or empty, the current userid is assumed.
diff --git a/usr.bin/at/at.c b/usr.bin/at/at.c
new file mode 100644
index 0000000..6665590
--- /dev/null
+++ b/usr.bin/at/at.c
@@ -0,0 +1,759 @@
+/*
+ * at.c : Put file into atrun queue
+ * Copyright (C) 1993, 1994 Thomas Koenig
+ *
+ * Atrun & Atq modifications
+ * Copyright (C) 1993 David Parsons
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _USE_BSD 1
+
+/* System Headers */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifndef __FreeBSD__
+#include <getopt.h>
+#endif
+
+/* Local headers */
+
+#include "at.h"
+#include "panic.h"
+#include "parsetime.h"
+#include "perm.h"
+
+#define MAIN
+#include "privs.h"
+
+/* Macros */
+
+#ifndef ATJOB_DIR
+#define ATJOB_DIR "/usr/spool/atjobs/"
+#endif
+
+#ifndef LFILE
+#define LFILE ATJOB_DIR ".lockfile"
+#endif
+
+#ifndef ATJOB_MX
+#define ATJOB_MX 255
+#endif
+
+#define ALARMC 10 /* Number of seconds to wait for timeout */
+
+#define SIZE 255
+#define TIMESIZE 50
+
+enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */
+
+/* File scope variables */
+
+static char rcsid[] = "$Id: at.c,v 1.3 1995/08/08 15:24:51 ig25 Exp $";
+char *no_export[] =
+{
+ "TERM", "TERMCAP", "DISPLAY", "_"
+} ;
+static send_mail = 0;
+
+/* External variables */
+
+extern char **environ;
+int fcreated;
+char *namep;
+char atfile[] = ATJOB_DIR "12345678901234";
+
+char *atinput = (char*)0; /* where to get input from */
+char atqueue = 0; /* which queue to examine for jobs (atq) */
+char atverify = 0; /* verify time instead of queuing job */
+
+/* Function declarations */
+
+static void sigc(int signo);
+static void alarmc(int signo);
+static char *cwdname(void);
+static void writefile(time_t runtimer, char queue);
+static void list_jobs(void);
+
+/* Signal catching functions */
+
+static void sigc(int signo)
+{
+/* If the user presses ^C, remove the spool file and exit
+ */
+ if (fcreated)
+ {
+ PRIV_START
+ unlink(atfile);
+ PRIV_END
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+static void alarmc(int signo)
+{
+/* Time out after some seconds
+ */
+ panic("File locking timed out");
+}
+
+/* Local functions */
+
+static char *cwdname(void)
+{
+/* Read in the current directory; the name will be overwritten on
+ * subsequent calls.
+ */
+ static char *ptr = NULL;
+ static size_t size = SIZE;
+
+ if (ptr == NULL)
+ ptr = (char *) mymalloc(size);
+
+ while (1)
+ {
+ if (ptr == NULL)
+ panic("Out of memory");
+
+ if (getcwd(ptr, size-1) != NULL)
+ return ptr;
+
+ if (errno != ERANGE)
+ perr("Cannot get directory");
+
+ free (ptr);
+ size += SIZE;
+ ptr = (char *) mymalloc(size);
+ }
+}
+
+static long
+nextjob()
+{
+ long jobno;
+ FILE *fid;
+
+ if ((fid = fopen(ATJOB_DIR ".SEQ", "r+")) != (FILE*)0) {
+ if (fscanf(fid, "%5lx", &jobno) == 1) {
+ rewind(fid);
+ jobno = (1+jobno) % 0xfffff; /* 2^20 jobs enough? */
+ fprintf(fid, "%05lx\n", jobno);
+ }
+ else
+ jobno = EOF;
+ fclose(fid);
+ return jobno;
+ }
+ else if ((fid = fopen(ATJOB_DIR ".SEQ", "w")) != (FILE*)0) {
+ fprintf(fid, "%05lx\n", jobno = 1);
+ fclose(fid);
+ return 1;
+ }
+ return EOF;
+}
+
+static void
+writefile(time_t runtimer, char queue)
+{
+/* This does most of the work if at or batch are invoked for writing a job.
+ */
+ long jobno;
+ char *ap, *ppos, *mailname;
+ struct passwd *pass_entry;
+ struct stat statbuf;
+ int fdes, lockdes, fd2;
+ FILE *fp, *fpin;
+ struct sigaction act;
+ char **atenv;
+ int ch;
+ mode_t cmask;
+ struct flock lock;
+
+/* Install the signal handler for SIGINT; terminate after removing the
+ * spool file if necessary
+ */
+ act.sa_handler = sigc;
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ sigaction(SIGINT, &act, NULL);
+
+ ppos = atfile + strlen(ATJOB_DIR);
+
+ /* Loop over all possible file names for running something at this
+ * particular time, see if a file is there; the first empty slot at any
+ * particular time is used. Lock the file LFILE first to make sure
+ * we're alone when doing this.
+ */
+
+ PRIV_START
+
+ if ((lockdes = open(LFILE, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR)) < 0)
+ perr("Cannot open lockfile " LFILE);
+
+ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0;
+ lock.l_len = 0;
+
+ act.sa_handler = alarmc;
+ sigemptyset(&(act.sa_mask));
+ act.sa_flags = 0;
+
+ /* Set an alarm so a timeout occurs after ALARMC seconds, in case
+ * something is seriously broken.
+ */
+ sigaction(SIGALRM, &act, NULL);
+ alarm(ALARMC);
+ fcntl(lockdes, F_SETLKW, &lock);
+ alarm(0);
+
+ if ((jobno = nextjob()) == EOF)
+ perr("Cannot generate job number");
+
+ sprintf(ppos, "%c%5lx%8lx", queue,
+ jobno, (unsigned long) (runtimer/60));
+
+ for(ap=ppos; *ap != '\0'; ap ++)
+ if (*ap == ' ')
+ *ap = '0';
+
+ if (stat(atfile, &statbuf) != 0)
+ if (errno != ENOENT)
+ perr("Cannot access " ATJOB_DIR);
+
+ /* Create the file. The x bit is only going to be set after it has
+ * been completely written out, to make sure it is not executed in the
+ * meantime. To make sure they do not get deleted, turn off their r
+ * bit. Yes, this is a kluge.
+ */
+ cmask = umask(S_IRUSR | S_IWUSR | S_IXUSR);
+ if ((fdes = creat(atfile, O_WRONLY)) == -1)
+ perr("Cannot create atjob file");
+
+ if ((fd2 = dup(fdes)) <0)
+ perr("Error in dup() of job file");
+
+ if(fchown(fd2, real_uid, real_gid) != 0)
+ perr("Cannot give away file");
+
+ PRIV_END
+
+ /* We no longer need suid root; now we just need to be able to write
+ * to the directory, if necessary.
+ */
+
+ REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
+
+ /* We've successfully created the file; let's set the flag so it
+ * gets removed in case of an interrupt or error.
+ */
+ fcreated = 1;
+
+ /* Now we can release the lock, so other people can access it
+ */
+ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0;
+ lock.l_len = 0;
+ fcntl(lockdes, F_SETLKW, &lock);
+ close(lockdes);
+
+ if((fp = fdopen(fdes, "w")) == NULL)
+ panic("Cannot reopen atjob file");
+
+ /* Get the userid to mail to, first by trying getlogin(), which reads
+ * /etc/utmp, then from LOGNAME, finally from getpwuid().
+ */
+ mailname = getlogin();
+ if (mailname == NULL)
+ mailname = getenv("LOGNAME");
+
+ if ((mailname == NULL) || (mailname[0] == '\0')
+ || (strlen(mailname) > 8) || (getpwnam(mailname)==NULL))
+ {
+ pass_entry = getpwuid(real_uid);
+ if (pass_entry != NULL)
+ mailname = pass_entry->pw_name;
+ }
+
+ if (atinput != (char *) NULL)
+ {
+ fpin = freopen(atinput, "r", stdin);
+ if (fpin == NULL)
+ perr("Cannot open input file");
+ }
+ fprintf(fp, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %8s %d\n",
+ (long) real_uid, (long) real_gid, mailname, send_mail);
+
+ /* Write out the umask at the time of invocation
+ */
+ fprintf(fp, "umask %lo\n", (unsigned long) cmask);
+
+ /* Write out the environment. Anything that may look like a
+ * special character to the shell is quoted, except for \n, which is
+ * done with a pair of "'s. Dont't export the no_export list (such
+ * as TERM or DISPLAY) because we don't want these.
+ */
+ for (atenv= environ; *atenv != NULL; atenv++)
+ {
+ int export = 1;
+ char *eqp;
+
+ eqp = strchr(*atenv, '=');
+ if (ap == NULL)
+ eqp = *atenv;
+ else
+ {
+ int i;
+ for (i=0; i<sizeof(no_export)/sizeof(no_export[0]); i++)
+ {
+ export = export
+ && (strncmp(*atenv, no_export[i],
+ (size_t) (eqp-*atenv)) != 0);
+ }
+ eqp++;
+ }
+
+ if (export)
+ {
+ fwrite(*atenv, sizeof(char), eqp-*atenv, fp);
+ for(ap = eqp;*ap != '\0'; ap++)
+ {
+ if (*ap == '\n')
+ fprintf(fp, "\"\n\"");
+ else
+ {
+ if (!isalnum(*ap)) {
+ switch (*ap) {
+ case '%': case '/': case '{': case '[':
+ case ']': case '=': case '}': case '@':
+ case '+': case '#': case ',': case '.':
+ case ':': case '-': case '_':
+ break;
+ default:
+ fputc('\\', fp);
+ break;
+ }
+ }
+ fputc(*ap, fp);
+ }
+ }
+ fputs("; export ", fp);
+ fwrite(*atenv, sizeof(char), eqp-*atenv -1, fp);
+ fputc('\n', fp);
+
+ }
+ }
+ /* Cd to the directory at the time and write out all the
+ * commands the user supplies from stdin.
+ */
+ fprintf(fp, "cd ");
+ for (ap = cwdname(); *ap != '\0'; ap++)
+ {
+ if (*ap == '\n')
+ fprintf(fp, "\"\n\"");
+ else
+ {
+ if (*ap != '/' && !isalnum(*ap))
+ fputc('\\', fp);
+
+ fputc(*ap, fp);
+ }
+ }
+ /* Test cd's exit status: die if the original directory has been
+ * removed, become unreadable or whatever
+ */
+ fprintf(fp, " || {\n\t echo 'Execution directory "
+ "inaccessible' >&2\n\t exit 1\n}\n");
+
+ while((ch = getchar()) != EOF)
+ fputc(ch, fp);
+
+ fprintf(fp, "\n");
+ if (ferror(fp))
+ panic("Output error");
+
+ if (ferror(stdin))
+ panic("Input error");
+
+ fclose(fp);
+
+ /* Set the x bit so that we're ready to start executing
+ */
+
+ if (fchmod(fd2, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+ perr("Cannot give away file");
+
+ close(fd2);
+ fprintf(stderr, "Job %ld will be executed using /bin/sh\n", jobno);
+}
+
+static void
+list_jobs()
+{
+ /* List all a user's jobs in the queue, by looping through ATJOB_DIR,
+ * or everybody's if we are root
+ */
+ struct passwd *pw;
+ DIR *spool;
+ struct dirent *dirent;
+ struct stat buf;
+ struct tm runtime;
+ unsigned long ctm;
+ char queue;
+ long jobno;
+ time_t runtimer;
+ char timestr[TIMESIZE];
+ int first=1;
+
+ PRIV_START
+
+ if (chdir(ATJOB_DIR) != 0)
+ perr("Cannot change to " ATJOB_DIR);
+
+ if ((spool = opendir(".")) == NULL)
+ perr("Cannot open " ATJOB_DIR);
+
+ /* Loop over every file in the directory
+ */
+ while((dirent = readdir(spool)) != NULL) {
+ if (stat(dirent->d_name, &buf) != 0)
+ perr("Cannot stat in " ATJOB_DIR);
+
+ /* See it's a regular file and has its x bit turned on and
+ * is the user's
+ */
+ if (!S_ISREG(buf.st_mode)
+ || ((buf.st_uid != real_uid) && ! (real_uid == 0))
+ || !(S_IXUSR & buf.st_mode || atverify))
+ continue;
+
+ if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
+ continue;
+
+ if (atqueue && (queue != atqueue))
+ continue;
+
+ runtimer = 60*(time_t) ctm;
+ runtime = *localtime(&runtimer);
+ strftime(timestr, TIMESIZE, "%X %x", &runtime);
+ if (first) {
+ printf("Date\t\t\tOwner\tQueue\tJob#\n");
+ first=0;
+ }
+ pw = getpwuid(buf.st_uid);
+
+ printf("%s\t%s\t%c%s\t%ld\n",
+ timestr,
+ pw ? pw->pw_name : "???",
+ queue,
+ (S_IXUSR & buf.st_mode) ? "":"(done)",
+ jobno);
+ }
+ PRIV_END
+}
+
+static void
+process_jobs(int argc, char **argv, int what)
+{
+ /* Delete every argument (job - ID) given
+ */
+ int i;
+ struct stat buf;
+ DIR *spool;
+ struct dirent *dirent;
+ unsigned long ctm;
+ char queue;
+ long jobno;
+
+ PRIV_START
+
+ if (chdir(ATJOB_DIR) != 0)
+ perr("Cannot change to " ATJOB_DIR);
+
+ if ((spool = opendir(".")) == NULL)
+ perr("Cannot open " ATJOB_DIR);
+
+ PRIV_END
+
+ /* Loop over every file in the directory
+ */
+ while((dirent = readdir(spool)) != NULL) {
+
+ PRIV_START
+ if (stat(dirent->d_name, &buf) != 0)
+ perr("Cannot stat in " ATJOB_DIR);
+ PRIV_END
+
+ if(sscanf(dirent->d_name, "%c%5lx%8lx", &queue, &jobno, &ctm)!=3)
+ continue;
+
+ for (i=optind; i < argc; i++) {
+ if (atoi(argv[i]) == jobno) {
+ if ((buf.st_uid != real_uid) && !(real_uid == 0)) {
+ fprintf(stderr, "%s: Not owner\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ switch (what) {
+ case ATRM:
+
+ PRIV_START
+
+ if (unlink(dirent->d_name) != 0)
+ perr(dirent->d_name);
+
+ PRIV_END
+
+ break;
+
+ case CAT:
+ {
+ FILE *fp;
+ int ch;
+
+ PRIV_START
+
+ fp = fopen(dirent->d_name,"r");
+
+ PRIV_END
+
+ if (!fp) {
+ perr("Cannot open file");
+ }
+ while((ch = getc(fp)) != EOF) {
+ putchar(ch);
+ }
+ }
+ break;
+
+ default:
+ fprintf(stderr,
+ "Internal error, process_jobs = %d\n",what);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ }
+ }
+} /* delete_jobs */
+
+/* Global functions */
+
+void *
+mymalloc(size_t n)
+{
+ void *p;
+ if ((p=malloc(n))==(void *)0)
+ {
+ fprintf(stderr,"Virtual memory exhausted\n");
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char queue = DEFAULT_AT_QUEUE;
+ char queue_set = 0;
+ char *pgm;
+
+ enum { ATQ, ATRM, AT, BATCH, CAT }; /* what program we want to run */
+ int program = AT; /* our default program */
+ char *options = "q:f:mvldbVc"; /* default options for at */
+ int disp_version = 0;
+ time_t timer;
+
+ RELINQUISH_PRIVS
+
+ /* Eat any leading paths
+ */
+ if ((pgm = strrchr(argv[0], '/')) == NULL)
+ pgm = argv[0];
+ else
+ pgm++;
+
+ namep = pgm;
+
+ /* find out what this program is supposed to do
+ */
+ if (strcmp(pgm, "atq") == 0) {
+ program = ATQ;
+ options = "q:vV";
+ }
+ else if (strcmp(pgm, "atrm") == 0) {
+ program = ATRM;
+ options = "V";
+ }
+ else if (strcmp(pgm, "batch") == 0) {
+ program = BATCH;
+ options = "f:q:mvV";
+ }
+
+ /* process whatever options we can process
+ */
+ opterr=1;
+ while ((c=getopt(argc, argv, options)) != EOF)
+ switch (c) {
+ case 'v': /* verify time settings */
+ atverify = 1;
+ break;
+
+ case 'm': /* send mail when job is complete */
+ send_mail = 1;
+ break;
+
+ case 'f':
+ atinput = optarg;
+ break;
+
+ case 'q': /* specify queue */
+ if (strlen(optarg) > 1)
+ usage();
+
+ atqueue = queue = *optarg;
+ if (!(islower(queue)||isupper(queue)))
+ usage();
+
+ queue_set = 1;
+ break;
+
+ case 'd':
+ if (program != AT)
+ usage();
+
+ program = ATRM;
+ options = "V";
+ break;
+
+ case 'l':
+ if (program != AT)
+ usage();
+
+ program = ATQ;
+ options = "q:vV";
+ break;
+
+ case 'b':
+ if (program != AT)
+ usage();
+
+ program = BATCH;
+ options = "f:q:mvV";
+ break;
+
+ case 'V':
+ disp_version = 1;
+ break;
+
+ case 'c':
+ program = CAT;
+ options = "";
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ /* end of options eating
+ */
+
+ if (disp_version)
+ fprintf(stderr, "at version " VERSION "\n"
+ "Bug reports to: ig25@rz.uni-karlsruhe.de (Thomas Koenig)\n");
+
+ /* select our program
+ */
+ if(!check_permission())
+ {
+ fprintf(stderr, "You do not have permission to use %s.\n",namep);
+ exit(EXIT_FAILURE);
+ }
+ switch (program) {
+ case ATQ:
+
+ REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
+
+ list_jobs();
+ break;
+
+ case ATRM:
+
+ REDUCE_PRIV(DAEMON_UID, DAEMON_GID)
+
+ process_jobs(argc, argv, ATRM);
+ break;
+
+ case CAT:
+
+ process_jobs(argc, argv, CAT);
+ break;
+
+ case AT:
+ timer = parsetime(argc, argv);
+ if (atverify)
+ {
+ struct tm *tm = localtime(&timer);
+ fprintf(stderr, "%s\n", asctime(tm));
+ }
+ writefile(timer, queue);
+ break;
+
+ case BATCH:
+ if (queue_set)
+ queue = toupper(queue);
+ else
+ queue = DEFAULT_BATCH_QUEUE;
+
+ if (argc > optind)
+ timer = parsetime(argc, argv);
+ else
+ timer = time(NULL);
+
+ if (atverify)
+ {
+ struct tm *tm = localtime(&timer);
+ fprintf(stderr, "%s\n", asctime(tm));
+ }
+
+ writefile(timer, queue);
+ break;
+
+ default:
+ panic("Internal error");
+ break;
+ }
+ exit(EXIT_SUCCESS);
+}
diff --git a/usr.bin/at/at.h b/usr.bin/at/at.h
new file mode 100644
index 0000000..40a5a6f
--- /dev/null
+++ b/usr.bin/at/at.h
@@ -0,0 +1,31 @@
+/*
+ * at.h - header for at(1)
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+extern int fcreated;
+extern char *namep;
+extern char atfile[];
+extern char atverify;
+
+void *mymalloc(size_t n);
diff --git a/usr.bin/at/at.man b/usr.bin/at/at.man
new file mode 100644
index 0000000..8edd8c9
--- /dev/null
+++ b/usr.bin/at/at.man
@@ -0,0 +1,268 @@
+.\" $Id: at.man,v 1.3 1995/08/21 12:32:47 ache Exp $
+.Dd April 12, 1995
+.Dt "AT" 1
+.Os "FreeBSD 2.1"
+.Sh NAME
+.Nm at, batch, atq, atrm
+.Nd queue, examine or delete jobs for later execution
+.Sh SYNOPSIS
+.Nm at
+.Op Fl V
+.Op Fl q Ar queue
+.Op Fl f Ar file
+.Op Fl mldbv
+.Ar time
+.Pp
+.Nm at
+.Op Fl V
+.Fl c Ar job Op Ar job ...
+.Pp
+.Nm atq
+.Op Fl V
+.Op Fl q Ar queue
+.Op Fl v
+.Pp
+.Nm atrm
+.Op Fl V
+.Ar job
+.Op Ar job ...
+.Pp
+.Nm batch
+.Op Fl V
+.Op Fl q Ar queue
+.Op Fl f Ar file
+.Op Fl mv
+.Op Ar time
+.Sh DESCRIPTION
+.Nm At
+and
+.Nm batch
+read commands from standard input or a specified file which are to
+be executed at a later time, using
+.Xr sh 1 .
+.Bl -tag -width indent
+.It Nm at
+executes commands at a specified time;
+.It Nm atq
+lists the user's pending jobs, unless the user is the superuser; in that
+case, everybody's jobs are listed;
+.It Nm atrm
+deletes jobs;
+.It Nm batch
+executes commands when system load levels permit; in other words, when the load average
+drops below _LOADAVG_MX, or the value specified in the invocation of
+.Nm atrun .
+.El
+.Pp
+.Nm At
+allows some moderately complex
+.Ar time
+specifications. It accepts times of the form
+.Ar HHMM
+or
+.Ar HH:MM
+to run a job at a specific time of day.
+(If that time is already past, the next day is assumed.)
+You may also specify
+.Nm midnight ,
+.Nm noon ,
+or
+.Nm teatime
+(4pm)
+and you can have a time-of-day suffixed with
+.Nm AM
+or
+.Nm PM
+for running in the morning or the evening.
+You can also say what day the job will be run,
+by giving a date in the form
+.Ar \%month-name day
+with an optional
+.Ar year ,
+or giving a date of the form
+.Ar MMDDYY
+or
+.Ar MM/DD/YY
+or
+.Ar DD.MM.YY .
+The specification of a date must follow the specification of
+the time of day.
+You can also give times like
+.Op Nm now
+.Nm + Ar count \%time-units ,
+where the time-units can be
+.Nm minutes ,
+.Nm hours ,
+.Nm days ,
+or
+.Nm weeks
+and you can tell
+.Nm at
+to run the job today by suffixing the time with
+.Nm today
+and to run the job tomorrow by suffixing the time with
+.Nm tomorrow.
+.Pp
+For example, to run a job at 4pm three days from now, you would do
+.Nm at 4pm + 3 days ,
+to run a job at 10:00am on July 31, you would do
+.Nm at 10am Jul 31
+and to run a job at 1am tomorrow, you would do
+.Nm at 1am tomorrow.
+.Pp
+For both
+.Nm at
+and
+.Nm batch ,
+commands are read from standard input or the file specified
+with the
+.Fl f
+option and executed.
+The working directory, the environment (except for the variables
+.Nm TERM ,
+.Nm TERMCAP ,
+.Nm DISPLAY
+and
+.Nm _ )
+and the
+.Ar umask
+are retained from the time of invocation.
+An
+.Nm at
+or
+.Nm batch
+command invoked from a
+.Xr su 1
+shell will retain the current userid.
+The user will be mailed standard error and standard output from his
+commands, if any. Mail will be sent using the command
+.Xr sendmail 8 .
+If
+.Nm at
+is executed from a
+.Xr su 1
+shell, the owner of the login shell will receive the mail.
+.Pp
+The superuser may use these commands in any case.
+For other users, permission to use at is determined by the files
+.Pa _PERM_PATH/at.allow
+and
+.Pa _PERM_PATH/at.deny .
+.Pp
+If the file
+.Pa _PERM_PATH/at.allow
+exists, only usernames mentioned in it are allowed to use
+.Nm at .
+.Pp
+If
+.Pa _PERM_PATH/at.allow
+does not exist,
+.Pa _PERM_PATH/at.deny
+is checked, every username not mentioned in it is then allowed
+to use
+.Nm at .
+.Pp
+If neither exists, only the superuser is allowed use of
+.Nm at .
+This is the default configuration.
+.Pp
+An empty
+.Pa _PERM_PATH/at.deny
+means that every user is allowed use these commands.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl V
+prints the version number to standard error.
+.It Fl q Ar queue
+uses the specified queue.
+A queue designation consists of a single letter; valid queue designations
+range from
+.Nm a
+to
+.Nm z .
+and
+.Nm A
+to
+.Nm Z .
+The
+.Nm _DEFAULT_AT_QUEUE
+queue is the default for
+.Nm at
+and the
+.Nm _DEFAULT_BATCH_QUEUE
+queue for
+.Nm batch .
+Queues with higher letters run with increased niceness.
+If a job is submitted to a queue designated with an uppercase letter, it
+is treated as if it had been submitted to batch at that time.
+If
+.Nm atq
+is given a specific queue, it will only show jobs pending in that queue.
+.It Fl m
+Send mail to the user when the job has completed even if there was no
+output.
+.It Fl f Ar file
+Reads the job from
+.Ar file
+rather than standard input.
+.It Fl l
+Is an alias for
+.Nm atq.
+.It Fl d
+Is an alias for
+.Nm atrm.
+.It Fl b
+Is an alias for
+.Nm batch.
+.It Fl v
+For
+.Nm atq ,
+shows completed but not yet deleted jobs in the queue; otherwise
+shows the time the job will be executed.
+.It Fl c
+Cats the jobs listed on the command line to standart output.
+.Sh FILES
+.Bl -tag -width _ATJOB_DIR/_LOCKFILE -compact
+.It Pa _ATJOB_DIR
+Directory containing job files
+.It Pa _ATSPOOL_DIR
+Directory containing output spool files
+.It Pa /var/run/utmp
+Login records
+.It Pa _PERM_PATH/at.allow
+Allow permission control
+.It Pa _PERM_PATH/at.deny
+Deny permission control
+.It Pa _ATJOB_DIR/_LOCKFILE
+Job-creation lock file.
+.Sh SEE ALSO
+.Xr cron 8 ,
+.Xr nice 1 ,
+.Xr umask 2 ,
+.Xr sh 1 ,
+.Xr sendmail 8 ,
+.Xr atrun 8 .
+.El
+.Sh BUGS
+.Pp
+If the file
+.Pa /var/run/utmp
+is not available or corrupted, or if the user is not logged on at the
+time
+.Nm at
+is invoked, the mail is sent to the userid found
+in the environment variable
+.Nm LOGNAME .
+If that is undefined or empty, the current userid is assumed.
+.Pp
+.Nm At
+and
+.Nm batch
+as presently implemented are not suitable when users are competing for
+resources.
+If this is the case for your site, you might want to consider another
+batch system, such as
+.Nm nqs .
+.Sh AUTHOR
+At was mostly written by Thomas Koenig, ig25@rz.uni-karlsruhe.de.
+The time parsing routines are by David Parsons, orc@pell.chi.il.us.
diff --git a/usr.bin/at/panic.c b/usr.bin/at/panic.c
new file mode 100644
index 0000000..c171b5d
--- /dev/null
+++ b/usr.bin/at/panic.c
@@ -0,0 +1,81 @@
+/*
+ * panic.c - terminate fast in case of error
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* System Headers */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Local headers */
+
+#include "panic.h"
+#include "at.h"
+
+/* File scope variables */
+
+static char rcsid[] = "$Id: panic.c,v 1.1 1995/05/24 15:07:32 ig25 Exp $";
+
+/* External variables */
+
+/* Global functions */
+
+void
+panic(char *a)
+{
+/* Something fatal has happened, print error message and exit.
+ */
+ fprintf(stderr,"%s: %s\n",namep,a);
+ if (fcreated)
+ unlink(atfile);
+
+ exit (EXIT_FAILURE);
+}
+
+void
+perr(char *a)
+{
+/* Some operating system error; print error message and exit.
+ */
+ perror(a);
+ if (fcreated)
+ unlink(atfile);
+
+ exit(EXIT_FAILURE);
+}
+
+void
+usage(void)
+{
+/* Print usage and exit.
+*/
+ fprintf(stderr, "Usage: at [-V] [-q x] [-f file] [-m] time\n"
+ " at [-V] -c job [job ...]\n"
+ " atq [-V] [-q x] [-v]\n"
+ " atrm [-V] job [job ...]\n"
+ " batch [-V] [-f file] [-m]\n");
+ exit(EXIT_FAILURE);
+}
diff --git a/usr.bin/at/panic.h b/usr.bin/at/panic.h
new file mode 100644
index 0000000..a809b8a
--- /dev/null
+++ b/usr.bin/at/panic.h
@@ -0,0 +1,44 @@
+/*
+ * panic.h - header for at(1)
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef __FreeBSD__
+#define __NORETURN
+#endif
+
+void
+#ifdef __GNUC__
+__NORETURN
+#endif
+panic(char *a);
+void
+#ifdef __GNUC__
+__NORETURN
+#endif
+perr(char *a);
+void
+#ifdef __GNUC__
+__NORETURN
+#endif
+usage(void);
diff --git a/usr.bin/at/parsetime.c b/usr.bin/at/parsetime.c
new file mode 100644
index 0000000..a54e7f0
--- /dev/null
+++ b/usr.bin/at/parsetime.c
@@ -0,0 +1,615 @@
+/*
+ * parsetime.c - parse time for at(1)
+ * Copyright (C) 1993, 1994 Thomas Koenig
+ *
+ * modifications for english-language times
+ * Copyright (C) 1993 David Parsons
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * at [NOW] PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS
+ * /NUMBER [DOT NUMBER] [AM|PM]\ /[MONTH NUMBER [NUMBER]] \
+ * |NOON | |[TOMORROW] |
+ * |MIDNIGHT | |[DAY OF WEEK] |
+ * \TEATIME / |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
+ * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
+ */
+
+/* System Headers */
+
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <ctype.h>
+#ifndef __FreeBSD__
+#include <getopt.h>
+#endif
+
+/* Local headers */
+
+#include "at.h"
+#include "panic.h"
+
+
+/* Structures and unions */
+
+enum { /* symbols */
+ MIDNIGHT, NOON, TEATIME,
+ PM, AM, TOMORROW, TODAY, NOW,
+ MINUTES, HOURS, DAYS, WEEKS,
+ NUMBER, PLUS, DOT, SLASH, ID, JUNK,
+ JAN, FEB, MAR, APR, MAY, JUN,
+ JUL, AUG, SEP, OCT, NOV, DEC,
+ SUN, MON, TUE, WED, THU, FRI, SAT
+ };
+
+/* parse translation table - table driven parsers can be your FRIEND!
+ */
+struct {
+ char *name; /* token name */
+ int value; /* token id */
+ int plural; /* is this plural? */
+} Specials[] = {
+ { "midnight", MIDNIGHT,0 }, /* 00:00:00 of today or tomorrow */
+ { "noon", NOON,0 }, /* 12:00:00 of today or tomorrow */
+ { "teatime", TEATIME,0 }, /* 16:00:00 of today or tomorrow */
+ { "am", AM,0 }, /* morning times for 0-12 clock */
+ { "pm", PM,0 }, /* evening times for 0-12 clock */
+ { "tomorrow", TOMORROW,0 }, /* execute 24 hours from time */
+ { "today", TODAY, 0 }, /* execute today - don't advance time */
+ { "now", NOW,0 }, /* opt prefix for PLUS */
+
+ { "minute", MINUTES,0 }, /* minutes multiplier */
+ { "minutes", MINUTES,1 }, /* (pluralized) */
+ { "hour", HOURS,0 }, /* hours ... */
+ { "hours", HOURS,1 }, /* (pluralized) */
+ { "day", DAYS,0 }, /* days ... */
+ { "days", DAYS,1 }, /* (pluralized) */
+ { "week", WEEKS,0 }, /* week ... */
+ { "weeks", WEEKS,1 }, /* (pluralized) */
+ { "jan", JAN,0 },
+ { "feb", FEB,0 },
+ { "mar", MAR,0 },
+ { "apr", APR,0 },
+ { "may", MAY,0 },
+ { "jun", JUN,0 },
+ { "jul", JUL,0 },
+ { "aug", AUG,0 },
+ { "sep", SEP,0 },
+ { "oct", OCT,0 },
+ { "nov", NOV,0 },
+ { "dec", DEC,0 },
+ { "sunday", SUN, 0 },
+ { "sun", SUN, 0 },
+ { "monday", MON, 0 },
+ { "mon", MON, 0 },
+ { "tuesday", TUE, 0 },
+ { "tue", TUE, 0 },
+ { "wednesday", WED, 0 },
+ { "wed", WED, 0 },
+ { "thursday", THU, 0 },
+ { "thu", THU, 0 },
+ { "friday", FRI, 0 },
+ { "fri", FRI, 0 },
+ { "saturday", SAT, 0 },
+ { "sat", SAT, 0 },
+} ;
+
+/* File scope variables */
+
+static char **scp; /* scanner - pointer at arglist */
+static char scc; /* scanner - count of remaining arguments */
+static char *sct; /* scanner - next char pointer in current argument */
+static int need; /* scanner - need to advance to next argument */
+
+static char *sc_token; /* scanner - token buffer */
+static size_t sc_len; /* scanner - lenght of token buffer */
+static int sc_tokid; /* scanner - token id */
+static int sc_tokplur; /* scanner - is token plural? */
+
+static char rcsid[] = "$Id: parsetime.c,v 1.1 1995/05/24 15:07:32 ig25 Exp $";
+
+/* Local functions */
+
+/*
+ * parse a token, checking if it's something special to us
+ */
+static int
+parse_token(char *arg)
+{
+ int i;
+
+ for (i=0; i<(sizeof Specials/sizeof Specials[0]); i++)
+ if (strcasecmp(Specials[i].name, arg) == 0) {
+ sc_tokplur = Specials[i].plural;
+ return sc_tokid = Specials[i].value;
+ }
+
+ /* not special - must be some random id */
+ return ID;
+} /* parse_token */
+
+
+/*
+ * init_scanner() sets up the scanner to eat arguments
+ */
+static void
+init_scanner(int argc, char **argv)
+{
+ scp = argv;
+ scc = argc;
+ need = 1;
+ sc_len = 1;
+ while (argc-- > 0)
+ sc_len += strlen(*argv++);
+
+ sc_token = (char *) mymalloc(sc_len);
+} /* init_scanner */
+
+/*
+ * token() fetches a token from the input stream
+ */
+static int
+token()
+{
+ int idx;
+
+ while (1) {
+ memset(sc_token, 0, sc_len);
+ sc_tokid = EOF;
+ sc_tokplur = 0;
+ idx = 0;
+
+ /* if we need to read another argument, walk along the argument list;
+ * when we fall off the arglist, we'll just return EOF forever
+ */
+ if (need) {
+ if (scc < 1)
+ return sc_tokid;
+ sct = *scp;
+ scp++;
+ scc--;
+ need = 0;
+ }
+ /* eat whitespace now - if we walk off the end of the argument,
+ * we'll continue, which puts us up at the top of the while loop
+ * to fetch the next argument in
+ */
+ while (isspace(*sct))
+ ++sct;
+ if (!*sct) {
+ need = 1;
+ continue;
+ }
+
+ /* preserve the first character of the new token
+ */
+ sc_token[0] = *sct++;
+
+ /* then see what it is
+ */
+ if (isdigit(sc_token[0])) {
+ while (isdigit(*sct))
+ sc_token[++idx] = *sct++;
+ sc_token[++idx] = 0;
+ return sc_tokid = NUMBER;
+ }
+ else if (isalpha(sc_token[0])) {
+ while (isalpha(*sct))
+ sc_token[++idx] = *sct++;
+ sc_token[++idx] = 0;
+ return parse_token(sc_token);
+ }
+ else if (sc_token[0] == ':' || sc_token[0] == '.')
+ return sc_tokid = DOT;
+ else if (sc_token[0] == '+')
+ return sc_tokid = PLUS;
+ else if (sc_token[0] == '/')
+ return sc_tokid = SLASH;
+ else
+ return sc_tokid = JUNK;
+ } /* while (1) */
+} /* token */
+
+
+/*
+ * plonk() gives an appropriate error message if a token is incorrect
+ */
+static void
+plonk(int tok)
+{
+ panic((tok == EOF) ? "incomplete time"
+ : "garbled time");
+} /* plonk */
+
+
+/*
+ * expect() gets a token and dies most horribly if it's not the token we want
+ */
+static void
+expect(int desired)
+{
+ if (token() != desired)
+ plonk(sc_tokid); /* and we die here... */
+} /* expect */
+
+
+/*
+ * dateadd() adds a number of minutes to a date. It is extraordinarily
+ * stupid regarding day-of-month overflow, and will most likely not
+ * work properly
+ */
+static void
+dateadd(int minutes, struct tm *tm)
+{
+ /* increment days */
+
+ while (minutes > 24*60) {
+ minutes -= 24*60;
+ tm->tm_mday++;
+ }
+
+ /* increment hours */
+ while (minutes > 60) {
+ minutes -= 60;
+ tm->tm_hour++;
+ if (tm->tm_hour > 23) {
+ tm->tm_mday++;
+ tm->tm_hour = 0;
+ }
+ }
+
+ /* increment minutes */
+ tm->tm_min += minutes;
+
+ if (tm->tm_min > 59) {
+ tm->tm_hour++;
+ tm->tm_min -= 60;
+
+ if (tm->tm_hour > 23) {
+ tm->tm_mday++;
+ tm->tm_hour = 0;
+ }
+ }
+} /* dateadd */
+
+
+/*
+ * plus() parses a now + time
+ *
+ * at [NOW] PLUS NUMBER [MINUTES|HOURS|DAYS|WEEKS]
+ *
+ */
+static void
+plus(struct tm *tm)
+{
+ int delay;
+ int expectplur;
+
+ expect(NUMBER);
+
+ delay = atoi(sc_token);
+ expectplur = (delay != 1) ? 1 : 0;
+
+ switch (token()) {
+ case WEEKS:
+ delay *= 7;
+ case DAYS:
+ delay *= 24;
+ case HOURS:
+ delay *= 60;
+ case MINUTES:
+ if (expectplur != sc_tokplur)
+ fprintf(stderr, "at: pluralization is wrong\n");
+ dateadd(delay, tm);
+ return;
+ }
+ plonk(sc_tokid);
+} /* plus */
+
+
+/*
+ * tod() computes the time of day
+ * [NUMBER [DOT NUMBER] [AM|PM]]
+ */
+static void
+tod(struct tm *tm)
+{
+ int hour, minute = 0;
+ int tlen;
+
+ hour = atoi(sc_token);
+ tlen = strlen(sc_token);
+
+ /* first pick out the time of day - if it's 4 digits, we assume
+ * a HHMM time, otherwise it's HH DOT MM time
+ */
+ if (token() == DOT) {
+ expect(NUMBER);
+ minute = atoi(sc_token);
+ if (minute > 59)
+ panic("garbled time");
+ token();
+ }
+ else if (tlen == 4) {
+ minute = hour%100;
+ if (minute > 59)
+ panic("garbeld time");
+ hour = hour/100;
+ }
+
+ /* check if an AM or PM specifier was given
+ */
+ if (sc_tokid == AM || sc_tokid == PM) {
+ if (hour > 12)
+ panic("garbled time");
+
+ if (sc_tokid == PM)
+ hour += 12;
+ token();
+ }
+ else if (hour > 23)
+ panic("garbled time");
+
+ /* if we specify an absolute time, we don't want to bump the day even
+ * if we've gone past that time - but if we're specifying a time plus
+ * a relative offset, it's okay to bump things
+ */
+ if ((sc_tokid == EOF || sc_tokid == PLUS) && tm->tm_hour > hour) {
+ tm->tm_mday++;
+ tm->tm_wday++;
+ }
+
+ tm->tm_hour = hour;
+ tm->tm_min = minute;
+ if (tm->tm_hour == 24) {
+ tm->tm_hour = 0;
+ tm->tm_mday++;
+ }
+} /* tod */
+
+
+/*
+ * assign_date() assigns a date, wrapping to next year if needed
+ */
+static void
+assign_date(struct tm *tm, long mday, long mon, long year)
+{
+ if (year > 99) {
+ if (year > 1899)
+ year -= 1900;
+ else
+ panic("garbled time");
+ }
+
+ if (year < 0 &&
+ (tm->tm_mon > mon ||(tm->tm_mon == mon && tm->tm_mday > mday)))
+ year = tm->tm_year + 1;
+
+ tm->tm_mday = mday;
+ tm->tm_mon = mon;
+
+ if (year >= 0)
+ tm->tm_year = year;
+} /* assign_date */
+
+
+/*
+ * month() picks apart a month specification
+ *
+ * /[<month> NUMBER [NUMBER]] \
+ * |[TOMORROW] |
+ * |[DAY OF WEEK] |
+ * |NUMBER [SLASH NUMBER [SLASH NUMBER]]|
+ * \PLUS NUMBER MINUTES|HOURS|DAYS|WEEKS/
+ */
+static void
+month(struct tm *tm)
+{
+ long year= (-1);
+ long mday, wday, mon;
+ int tlen;
+
+ switch (sc_tokid) {
+ case PLUS:
+ plus(tm);
+ break;
+
+ case TOMORROW:
+ /* do something tomorrow */
+ tm->tm_mday ++;
+ tm->tm_wday ++;
+ case TODAY: /* force ourselves to stay in today - no further processing */
+ token();
+ break;
+
+ case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
+ case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
+ /* do month mday [year]
+ */
+ mon = (sc_tokid-JAN);
+ expect(NUMBER);
+ mday = atol(sc_token);
+ if (token() == NUMBER) {
+ year = atol(sc_token);
+ token();
+ }
+ assign_date(tm, mday, mon, year);
+ break;
+
+ case SUN: case MON: case TUE:
+ case WED: case THU: case FRI:
+ case SAT:
+ /* do a particular day of the week
+ */
+ wday = (sc_tokid-SUN);
+
+ mday = tm->tm_mday;
+
+ /* if this day is < today, then roll to next week
+ */
+ if (wday < tm->tm_wday)
+ mday += 7 - (tm->tm_wday - wday);
+ else
+ mday += (wday - tm->tm_wday);
+
+ tm->tm_wday = wday;
+
+ assign_date(tm, mday, tm->tm_mon, tm->tm_year);
+ break;
+
+ case NUMBER:
+ /* get numeric MMDDYY, mm/dd/yy, or dd.mm.yy
+ */
+ tlen = strlen(sc_token);
+ mon = atol(sc_token);
+ token();
+
+ if (sc_tokid == SLASH || sc_tokid == DOT) {
+ int sep;
+
+ sep = sc_tokid;
+ expect(NUMBER);
+ mday = atol(sc_token);
+ if (token() == sep) {
+ expect(NUMBER);
+ year = atol(sc_token);
+ token();
+ }
+
+ /* flip months and days for european timing
+ */
+ if (sep == DOT) {
+ int x = mday;
+ mday = mon;
+ mon = x;
+ }
+ }
+ else if (tlen == 6 || tlen == 8) {
+ if (tlen == 8) {
+ year = (mon % 10000) - 1900;
+ mon /= 10000;
+ }
+ else {
+ year = mon % 100;
+ mon /= 100;
+ }
+ mday = mon % 100;
+ mon /= 100;
+ }
+ else
+ panic("garbled time");
+
+ mon--;
+ if (mon < 0 || mon > 11 || mday < 1 || mday > 31)
+ panic("garbled time");
+
+ assign_date(tm, mday, mon, year);
+ break;
+ } /* case */
+} /* month */
+
+
+/* Global functions */
+
+time_t
+parsetime(int argc, char **argv)
+{
+/* Do the argument parsing, die if necessary, and return the time the job
+ * should be run.
+ */
+ time_t nowtimer, runtimer;
+ struct tm nowtime, runtime;
+ int hr = 0;
+ /* this MUST be initialized to zero for midnight/noon/teatime */
+
+ nowtimer = time(NULL);
+ nowtime = *localtime(&nowtimer);
+
+ runtime = nowtime;
+ runtime.tm_sec = 0;
+ runtime.tm_isdst = 0;
+
+ if (argc <= optind)
+ usage();
+
+ init_scanner(argc-optind, argv+optind);
+
+ switch (token()) {
+ case NOW: /* now is optional prefix for PLUS tree */
+ expect(PLUS);
+ case PLUS:
+ plus(&runtime);
+ break;
+
+ case NUMBER:
+ tod(&runtime);
+ month(&runtime);
+ break;
+
+ /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialised
+ * hr to zero up above, then fall into this case in such a
+ * way so we add +12 +4 hours to it for teatime, +12 hours
+ * to it for noon, and nothing at all for midnight, then
+ * set our runtime to that hour before leaping into the
+ * month scanner
+ */
+ case TEATIME:
+ hr += 4;
+ case NOON:
+ hr += 12;
+ case MIDNIGHT:
+ if (runtime.tm_hour >= hr) {
+ runtime.tm_mday++;
+ runtime.tm_wday++;
+ }
+ runtime.tm_hour = hr;
+ runtime.tm_min = 0;
+ token();
+ /* fall through to month setting */
+ default:
+ month(&runtime);
+ break;
+ } /* ugly case statement */
+ expect(EOF);
+
+ /* adjust for daylight savings time
+ */
+ runtime.tm_isdst = -1;
+ runtimer = mktime(&runtime);
+ if (runtime.tm_isdst > 0) {
+ runtimer -= 3600;
+ runtimer = mktime(&runtime);
+ }
+
+ if (runtimer < 0)
+ panic("garbled time");
+
+ if (nowtimer > runtimer)
+ panic("Trying to travel back in time");
+
+ return runtimer;
+} /* parsetime */
diff --git a/usr.bin/at/parsetime.h b/usr.bin/at/parsetime.h
new file mode 100644
index 0000000..30c3f20
--- /dev/null
+++ b/usr.bin/at/parsetime.h
@@ -0,0 +1,26 @@
+/*
+ * at.h - header for at(1)
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+time_t parsetime(int argc, char **argv);
diff --git a/usr.bin/at/pathnames.h b/usr.bin/at/pathnames.h
new file mode 100644
index 0000000..400ecfa
--- /dev/null
+++ b/usr.bin/at/pathnames.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: pathnames.h,v 1.1 1993/12/05 11:37:23 cgd Exp $
+ */
+
+#ifndef _PATHNAMES_H_
+#define _PATHNAMES_H_
+
+#include <paths.h>
+
+#define _PATH_ATJOBS "/var/at/jobs/"
+#define _PATH_ATSPOOL "/var/at/spool/"
+#define _PATH_LOCKFILE "/var/at/lockfile"
+
+#endif /* !_PATHNAMES_H_ */
diff --git a/usr.bin/at/perm.c b/usr.bin/at/perm.c
new file mode 100644
index 0000000..7577ac0
--- /dev/null
+++ b/usr.bin/at/perm.c
@@ -0,0 +1,123 @@
+/*
+ * perm.c - check user permission for at(1)
+ * Copyright (C) 1994 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* System Headers */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Local headers */
+
+#include "privs.h"
+#include "at.h"
+
+/* Macros */
+
+#define MAXUSERID 10
+
+/* Structures and unions */
+
+
+/* File scope variables */
+
+static char rcsid[] = "$Id: perm.c,v 1.1 1995/05/24 15:07:32 ig25 Exp $";
+
+/* Function declarations */
+
+static int check_for_user(FILE *fp,const char *name);
+
+/* Local functions */
+
+static int check_for_user(FILE *fp,const char *name)
+{
+ char *buffer;
+ size_t len;
+ int found = 0;
+
+ len = strlen(name);
+ buffer = mymalloc(len+2);
+
+ while(fgets(buffer, len+2, fp) != NULL)
+ {
+ if ((strncmp(name, buffer, len) == 0) &&
+ (buffer[len] == '\n'))
+ {
+ found = 1;
+ break;
+ }
+ }
+ fclose(fp);
+ free(buffer);
+ return found;
+}
+/* Global functions */
+int check_permission()
+{
+ FILE *fp;
+ uid_t uid = geteuid();
+ struct passwd *pentry;
+
+ if (uid==0)
+ return 1;
+
+ if ((pentry = getpwuid(uid)) == NULL)
+ {
+ perror("Cannot access user database");
+ exit(EXIT_FAILURE);
+ }
+
+ PRIV_START
+
+ fp=fopen(PERM_PATH "at.allow","r");
+
+ PRIV_END
+
+ if (fp != NULL)
+ {
+ return check_for_user(fp, pentry->pw_name);
+ }
+ else
+ {
+
+ PRIV_START
+
+ fp=fopen(PERM_PATH "at.deny", "r");
+
+ PRIV_END
+
+ if (fp != NULL)
+ {
+ return !check_for_user(fp, pentry->pw_name);
+ }
+ perror("at.deny");
+ }
+ return 0;
+}
diff --git a/usr.bin/at/perm.h b/usr.bin/at/perm.h
new file mode 100644
index 0000000..0a12a90
--- /dev/null
+++ b/usr.bin/at/perm.h
@@ -0,0 +1,26 @@
+/*
+ * perm.h - header for at(1)
+ * Copyright (C) 1994 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+int check_permission();
diff --git a/usr.bin/at/privs.h b/usr.bin/at/privs.h
new file mode 100644
index 0000000..2108efb
--- /dev/null
+++ b/usr.bin/at/privs.h
@@ -0,0 +1,111 @@
+/*
+ * privs.h - header for privileged operations
+ * Copyright (C) 1993 Thomas Koenig
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PRIVS_H
+#define _PRIVS_H
+
+#ifndef _USE_BSD
+#define _USE_BSD 1
+#include <unistd.h>
+#undef _USE_BSD
+#else
+#include <unistd.h>
+#endif
+
+/* Relinquish privileges temporarily for a setuid or setgid program
+ * with the option of getting them back later. This is done by swapping
+ * the real and effective userid BSD style. Call RELINQUISH_PRIVS once
+ * at the beginning of the main program. This will cause all operatons
+ * to be executed with the real userid. When you need the privileges
+ * of the setuid/setgid invocation, call PRIV_START; when you no longer
+ * need it, call PRIV_END. Note that it is an error to call PRIV_START
+ * and not PRIV_END within the same function.
+ *
+ * Use RELINQUISH_PRIVS_ROOT(a,b) if your program started out running
+ * as root, and you want to drop back the effective userid to a
+ * and the effective group id to b, with the option to get them back
+ * later.
+ *
+ * If you no longer need root privileges, but those of some other
+ * userid/groupid, you can call REDUCE_PRIV(a,b) when your effective
+ * is the user's.
+ *
+ * Problems: Do not use return between PRIV_START and PRIV_END; this
+ * will cause the program to continue running in an unprivileged
+ * state.
+ *
+ * It is NOT safe to call exec(), system() or popen() with a user-
+ * supplied program (i.e. without carefully checking PATH and any
+ * library load paths) with relinquished privileges; the called program
+ * can aquire them just as easily. Set both effective and real userid
+ * to the real userid before calling any of them.
+ */
+
+#ifndef MAIN
+extern
+#endif
+uid_t real_uid, effective_uid;
+
+#ifndef MAIN
+extern
+#endif
+gid_t real_gid, effective_gid;
+
+#define RELINQUISH_PRIVS { \
+ real_uid = getuid(); \
+ effective_uid = geteuid(); \
+ real_gid = getgid(); \
+ effective_gid = getegid(); \
+ setreuid(effective_uid, real_uid); \
+ setregid(effective_gid, real_gid); \
+ }
+
+#define RELINQUISH_PRIVS_ROOT(a,b) { \
+ real_uid = (a); \
+ effective_uid = geteuid(); \
+ real_gid = (b); \
+ effective_gid = getegid(); \
+ setregid(effective_gid, real_gid); \
+ setreuid(effective_uid, real_uid); \
+ }
+
+#define PRIV_START {\
+ setreuid(real_uid, effective_uid); \
+ setregid(real_gid, effective_gid);
+
+#define PRIV_END \
+ setregid(effective_gid, real_gid); \
+ setreuid(effective_uid, real_uid); \
+ }
+
+#define REDUCE_PRIV(a,b) {\
+ setreuid(real_uid, effective_uid); \
+ setregid(real_gid, effective_gid); \
+ effective_uid = (a); \
+ effective_gid = (b); \
+ setregid(effective_gid, real_gid); \
+ setreuid(effective_uid, real_uid); \
+ }
+#endif
diff --git a/usr.bin/banner/Makefile b/usr.bin/banner/Makefile
new file mode 100644
index 0000000..54a3ad5
--- /dev/null
+++ b/usr.bin/banner/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= banner
+MAN6= banner.6
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/banner/banner.6 b/usr.bin/banner/banner.6
new file mode 100644
index 0000000..3427a66
--- /dev/null
+++ b/usr.bin/banner/banner.6
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)banner.6 8.1 (Berkeley) 6/6/93
+.\"
+.TH BANNER 6 "June 6, 1993"
+.UC
+.SH NAME
+banner \- print large banner on printer
+.SH SYNOPSIS
+.B /usr/games/banner
+[
+.BI \-w n
+]
+message ...
+.SH DESCRIPTION
+.I Banner
+prints a large, high quality banner on the standard output.
+If the message is omitted, it prompts for and
+reads one line of its standard input. If
+.B \-w
+is given, the output is scrunched down from a width of 132 to
+.I n ,
+suitable for a narrow terminal. If
+.I n
+is omitted, it defaults to 80.
+.PP
+The output should be printed on a hard-copy device, up to 132 columns wide,
+with no breaks between the pages. The volume is great enough that you
+may want
+a printer or a fast hardcopy terminal, but if you are patient, a
+decwriter or other 300 baud terminal will do.
+.SH BUGS
+Several ASCII characters are not defined, notably <, >, [, ], \\,
+^, _, {, }, |, and ~. Also, the characters ", ', and & are funny
+looking (but in a useful way.)
+.PP
+The
+.B \-w
+option is implemented by skipping some rows and columns.
+The smaller it gets, the grainier the output.
+Sometimes it runs letters together.
+.SH AUTHOR
+Mark Horton
diff --git a/usr.bin/banner/banner.c b/usr.bin/banner/banner.c
new file mode 100644
index 0000000..d6d4392
--- /dev/null
+++ b/usr.bin/banner/banner.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 1980, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)banner.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * banner - prints large signs
+ * banner [-w#] [-d] [-t] message ...
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXMSG 1024
+#define DWIDTH 132
+#define NCHARS 128
+#define NBYTES 9271
+
+/* Pointers into data_table for each ASCII char */
+int asc_ptr[NCHARS] = {
+/* ^@ */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^H */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^P */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* ^X */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* */ 1, 3, 50, 81, 104, 281, 483, 590,
+/* ( */ 621, 685, 749, 851, 862, 893, 898, 921,
+/* 0 */1019, 1150, 1200, 1419, 1599, 1744, 1934, 2111,
+/* 8 */2235, 2445, 2622, 2659, 0, 2708, 0, 2715,
+/* @ */2857, 3072, 3273, 3403, 3560, 3662, 3730, 3785,
+/* H */3965, 4000, 4015, 4115, 4281, 4314, 4432, 4548,
+/* P */4709, 4790, 4999, 5188, 5397, 5448, 5576, 5710,
+/* X */5892, 6106, 6257, 0, 0, 0, 0, 0,
+/* ` */ 50, 6503, 6642, 6733, 6837, 6930, 7073, 7157,
+/* h */7380, 7452, 7499, 7584, 7689, 7702, 7797, 7869,
+/* p */7978, 8069, 8160, 8222, 8381, 8442, 8508, 8605,
+/* x */8732, 8888, 9016, 0, 0, 0, 0, 0
+};
+
+/*
+ * Table of stuff to print. Format:
+ * 128+n -> print current line n times.
+ * 64+n -> this is last byte of char.
+ * else, put m chars at position n (where m
+ * is the next elt in array) and goto second
+ * next element in array.
+ */
+char data_table[NBYTES] = {
+/* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 129, 227, 130, 34, 6, 90, 19, 129, 32, 10,
+/* 10 */ 74, 40, 129, 31, 12, 64, 53, 129, 30, 14,
+/* 20 */ 54, 65, 129, 30, 14, 53, 67, 129, 30, 14,
+/* 30 */ 54, 65, 129, 31, 12, 64, 53, 129, 32, 10,
+/* 40 */ 74, 40, 129, 34, 6, 90, 19, 129, 194, 130,
+/* 50 */ 99, 9, 129, 97, 14, 129, 96, 18, 129, 95,
+/* 60 */ 22, 129, 95, 16, 117, 2, 129, 95, 14, 129,
+/* 70 */ 96, 11, 129, 97, 9, 129, 99, 6, 129, 194,
+/* 80 */ 129, 87, 4, 101, 4, 131, 82, 28, 131, 87,
+/* 90 */ 4, 101, 4, 133, 82, 28, 131, 87, 4, 101,
+/* 100 */ 4, 131, 193, 129, 39, 1, 84, 27, 129, 38,
+/* 110 */ 3, 81, 32, 129, 37, 5, 79, 35, 129, 36,
+/* 120 */ 5, 77, 38, 129, 35, 5, 76, 40, 129, 34,
+/* 130 */ 5, 75, 21, 103, 14, 129, 33, 5, 74, 19,
+/* 140 */ 107, 11, 129, 32, 5, 73, 17, 110, 9, 129,
+/* 150 */ 32, 4, 73, 16, 112, 7, 129, 31, 4, 72,
+/* 160 */ 15, 114, 6, 129, 31, 4, 72, 14, 115, 5,
+/* 170 */ 129, 30, 4, 71, 15, 116, 5, 129, 27, 97,
+/* 180 */ 131, 30, 4, 69, 14, 117, 4, 129, 30, 4,
+/* 190 */ 68, 15, 117, 4, 132, 30, 4, 68, 14, 117,
+/* 200 */ 4, 129, 27, 97, 131, 30, 5, 65, 15, 116,
+/* 210 */ 5, 129, 31, 4, 65, 14, 116, 4, 129, 31,
+/* 220 */ 6, 64, 15, 116, 4, 129, 32, 7, 62, 16,
+/* 230 */ 115, 4, 129, 32, 9, 61, 17, 114, 5, 129,
+/* 240 */ 33, 11, 58, 19, 113, 5, 129, 34, 14, 55,
+/* 250 */ 21, 112, 5, 129, 35, 40, 111, 5, 129, 36,
+/* 260 */ 38, 110, 5, 129, 37, 35, 109, 5, 129, 38,
+/* 270 */ 32, 110, 3, 129, 40, 27, 111, 1, 129, 193,
+/* 280 */ 129, 30, 4, 103, 9, 129, 30, 7, 100, 15,
+/* 290 */ 129, 30, 10, 99, 17, 129, 33, 10, 97, 6,
+/* 300 */ 112, 6, 129, 36, 10, 96, 5, 114, 5, 129,
+/* 310 */ 39, 10, 96, 4, 115, 4, 129, 42, 10, 95,
+/* 320 */ 4, 116, 4, 129, 45, 10, 95, 3, 117, 3,
+/* 330 */ 129, 48, 10, 95, 3, 117, 3, 129, 51, 10,
+/* 340 */ 95, 4, 116, 4, 129, 54, 10, 96, 4, 115,
+/* 350 */ 4, 129, 57, 10, 96, 5, 114, 5, 129, 60,
+/* 360 */ 10, 97, 6, 112, 6, 129, 63, 10, 99, 17,
+/* 370 */ 129, 66, 10, 100, 15, 129, 69, 10, 103, 9,
+/* 380 */ 129, 39, 9, 72, 10, 129, 36, 15, 75, 10,
+/* 390 */ 129, 35, 17, 78, 10, 129, 33, 6, 48, 6,
+/* 400 */ 81, 10, 129, 32, 5, 50, 5, 84, 10, 129,
+/* 410 */ 32, 4, 51, 4, 87, 10, 129, 31, 4, 52,
+/* 420 */ 4, 90, 10, 129, 31, 3, 53, 3, 93, 10,
+/* 430 */ 129, 31, 3, 53, 3, 96, 10, 129, 31, 4,
+/* 440 */ 52, 4, 99, 10, 129, 32, 4, 51, 4, 102,
+/* 450 */ 10, 129, 32, 5, 50, 5, 105, 10, 129, 33,
+/* 460 */ 6, 48, 6, 108, 10, 129, 35, 17, 111, 10,
+/* 470 */ 129, 36, 15, 114, 7, 129, 40, 9, 118, 4,
+/* 480 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41,
+/* 490 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44,
+/* 500 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32,
+/* 510 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31,
+/* 520 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30,
+/* 530 */ 5, 79, 5, 129, 20, 74, 132, 30, 4, 80,
+/* 540 */ 4, 129, 31, 3, 79, 4, 129, 31, 4, 79,
+/* 550 */ 4, 129, 32, 3, 78, 4, 129, 32, 4, 76,
+/* 560 */ 6, 129, 33, 4, 74, 7, 129, 34, 4, 72,
+/* 570 */ 8, 129, 35, 5, 72, 7, 129, 37, 5, 73,
+/* 580 */ 4, 129, 39, 4, 74, 1, 129, 129, 193, 130,
+/* 590 */ 111, 6, 129, 109, 10, 129, 108, 12, 129, 107,
+/* 600 */ 14, 129, 97, 2, 105, 16, 129, 99, 22, 129,
+/* 610 */ 102, 18, 129, 105, 14, 129, 108, 9, 129, 194,
+/* 620 */ 130, 63, 25, 129, 57, 37, 129, 52, 47, 129,
+/* 630 */ 48, 55, 129, 44, 63, 129, 41, 69, 129, 38,
+/* 640 */ 75, 129, 36, 79, 129, 34, 83, 129, 33, 28,
+/* 650 */ 90, 28, 129, 32, 23, 96, 23, 129, 32, 17,
+/* 660 */ 102, 17, 129, 31, 13, 107, 13, 129, 30, 9,
+/* 670 */ 112, 9, 129, 30, 5, 116, 5, 129, 30, 1,
+/* 680 */ 120, 1, 129, 194, 130, 30, 1, 120, 1, 129,
+/* 690 */ 30, 5, 116, 5, 129, 30, 9, 112, 9, 129,
+/* 700 */ 31, 13, 107, 13, 129, 32, 17, 102, 17, 129,
+/* 710 */ 32, 23, 96, 23, 129, 33, 28, 90, 28, 129,
+/* 720 */ 34, 83, 129, 36, 79, 129, 38, 75, 129, 41,
+/* 730 */ 69, 129, 44, 63, 129, 48, 55, 129, 52, 47,
+/* 740 */ 129, 57, 37, 129, 63, 25, 129, 194, 129, 80,
+/* 750 */ 4, 130, 80, 4, 129, 68, 2, 80, 4, 94,
+/* 760 */ 2, 129, 66, 6, 80, 4, 92, 6, 129, 67,
+/* 770 */ 7, 80, 4, 90, 7, 129, 69, 7, 80, 4,
+/* 780 */ 88, 7, 129, 71, 6, 80, 4, 87, 6, 129,
+/* 790 */ 72, 20, 129, 74, 16, 129, 76, 12, 129, 62,
+/* 800 */ 40, 131, 76, 12, 129, 74, 16, 129, 72, 20,
+/* 810 */ 129, 71, 6, 80, 4, 87, 6, 129, 69, 7,
+/* 820 */ 80, 4, 88, 7, 129, 67, 7, 80, 4, 90,
+/* 830 */ 7, 129, 66, 6, 80, 4, 92, 6, 129, 68,
+/* 840 */ 2, 80, 4, 94, 2, 129, 80, 4, 130, 193,
+/* 850 */ 129, 60, 4, 139, 41, 42, 131, 60, 4, 139,
+/* 860 */ 193, 130, 34, 6, 129, 32, 10, 129, 31, 12,
+/* 870 */ 129, 30, 14, 129, 20, 2, 28, 16, 129, 22,
+/* 880 */ 22, 129, 24, 19, 129, 27, 15, 129, 31, 9,
+/* 890 */ 129, 194, 129, 60, 4, 152, 193, 130, 34, 6,
+/* 900 */ 129, 32, 10, 129, 31, 12, 129, 30, 14, 131,
+/* 910 */ 31, 12, 129, 32, 10, 129, 34, 6, 129, 194,
+/* 920 */ 129, 30, 4, 129, 30, 7, 129, 30, 10, 129,
+/* 930 */ 33, 10, 129, 36, 10, 129, 39, 10, 129, 42,
+/* 940 */ 10, 129, 45, 10, 129, 48, 10, 129, 51, 10,
+/* 950 */ 129, 54, 10, 129, 57, 10, 129, 60, 10, 129,
+/* 960 */ 63, 10, 129, 66, 10, 129, 69, 10, 129, 72,
+/* 970 */ 10, 129, 75, 10, 129, 78, 10, 129, 81, 10,
+/* 980 */ 129, 84, 10, 129, 87, 10, 129, 90, 10, 129,
+/* 990 */ 93, 10, 129, 96, 10, 129, 99, 10, 129, 102,
+/* 1000 */ 10, 129, 105, 10, 129, 108, 10, 129, 111, 10,
+/* 1010 */ 129, 114, 7, 129, 117, 4, 129, 193, 129, 60,
+/* 1020 */ 31, 129, 53, 45, 129, 49, 53, 129, 46, 59,
+/* 1030 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129,
+/* 1040 */ 37, 77, 129, 36, 79, 129, 35, 15, 101, 15,
+/* 1050 */ 129, 34, 11, 106, 11, 129, 33, 9, 109, 9,
+/* 1060 */ 129, 32, 7, 112, 7, 129, 31, 6, 114, 6,
+/* 1070 */ 129, 31, 5, 115, 5, 129, 30, 5, 116, 5,
+/* 1080 */ 129, 30, 4, 117, 4, 132, 30, 5, 116, 5,
+/* 1090 */ 129, 31, 5, 115, 5, 129, 31, 6, 114, 6,
+/* 1100 */ 129, 32, 7, 112, 7, 129, 33, 9, 109, 9,
+/* 1110 */ 129, 34, 11, 106, 11, 129, 35, 15, 101, 15,
+/* 1120 */ 129, 36, 79, 129, 37, 77, 129, 39, 73, 129,
+/* 1130 */ 41, 69, 129, 43, 65, 129, 46, 59, 129, 49,
+/* 1140 */ 53, 129, 53, 45, 129, 60, 31, 129, 193, 129,
+/* 1150 */ 30, 4, 129, 30, 4, 100, 1, 129, 30, 4,
+/* 1160 */ 100, 3, 129, 30, 4, 100, 5, 129, 30, 76,
+/* 1170 */ 129, 30, 78, 129, 30, 80, 129, 30, 82, 129,
+/* 1180 */ 30, 83, 129, 30, 85, 129, 30, 87, 129, 30,
+/* 1190 */ 89, 129, 30, 91, 129, 30, 4, 132, 193, 129,
+/* 1200 */ 30, 3, 129, 30, 7, 129, 30, 10, 112, 1,
+/* 1210 */ 129, 30, 13, 112, 2, 129, 30, 16, 112, 3,
+/* 1220 */ 129, 30, 18, 111, 5, 129, 30, 21, 111, 6,
+/* 1230 */ 129, 30, 23, 112, 6, 129, 30, 14, 47, 8,
+/* 1240 */ 113, 6, 129, 30, 14, 49, 8, 114, 5, 129,
+/* 1250 */ 30, 14, 51, 8, 115, 5, 129, 30, 14, 53,
+/* 1260 */ 8, 116, 4, 129, 30, 14, 55, 8, 116, 5,
+/* 1270 */ 129, 30, 14, 56, 9, 117, 4, 129, 30, 14,
+/* 1280 */ 57, 9, 117, 4, 129, 30, 14, 58, 10, 117,
+/* 1290 */ 4, 129, 30, 14, 59, 10, 117, 4, 129, 30,
+/* 1300 */ 14, 60, 11, 117, 4, 129, 30, 14, 61, 11,
+/* 1310 */ 116, 5, 129, 30, 14, 62, 11, 116, 5, 129,
+/* 1320 */ 30, 14, 63, 12, 115, 6, 129, 30, 14, 64,
+/* 1330 */ 13, 114, 7, 129, 30, 14, 65, 13, 113, 8,
+/* 1340 */ 129, 30, 14, 65, 15, 111, 9, 129, 30, 14,
+/* 1350 */ 66, 16, 109, 11, 129, 30, 14, 67, 17, 107,
+/* 1360 */ 12, 129, 30, 14, 68, 20, 103, 16, 129, 30,
+/* 1370 */ 14, 69, 49, 129, 30, 14, 70, 47, 129, 30,
+/* 1380 */ 14, 71, 45, 129, 30, 14, 73, 42, 129, 30,
+/* 1390 */ 15, 75, 38, 129, 33, 12, 77, 34, 129, 36,
+/* 1400 */ 10, 79, 30, 129, 40, 6, 82, 23, 129, 44,
+/* 1410 */ 3, 86, 15, 129, 47, 1, 129, 193, 129, 129,
+/* 1420 */ 38, 3, 129, 37, 5, 111, 1, 129, 36, 7,
+/* 1430 */ 111, 2, 129, 35, 9, 110, 5, 129, 34, 8,
+/* 1440 */ 110, 6, 129, 33, 7, 109, 8, 129, 32, 7,
+/* 1450 */ 110, 8, 129, 32, 6, 112, 7, 129, 31, 6,
+/* 1460 */ 113, 6, 129, 31, 5, 114, 6, 129, 30, 5,
+/* 1470 */ 115, 5, 129, 30, 5, 116, 4, 129, 30, 4,
+/* 1480 */ 117, 4, 131, 30, 4, 117, 4, 129, 30, 4,
+/* 1490 */ 79, 2, 117, 4, 129, 30, 5, 78, 4, 117,
+/* 1500 */ 4, 129, 30, 5, 77, 6, 116, 5, 129, 30,
+/* 1510 */ 6, 76, 8, 115, 6, 129, 30, 7, 75, 11,
+/* 1520 */ 114, 6, 129, 30, 8, 73, 15, 112, 8, 129,
+/* 1530 */ 31, 9, 71, 19, 110, 9, 129, 31, 11, 68,
+/* 1540 */ 26, 107, 12, 129, 32, 13, 65, 14, 82, 36,
+/* 1550 */ 129, 32, 16, 61, 17, 83, 34, 129, 33, 44,
+/* 1560 */ 84, 32, 129, 34, 42, 85, 30, 129, 35, 40,
+/* 1570 */ 87, 27, 129, 36, 38, 89, 23, 129, 38, 34,
+/* 1580 */ 92, 17, 129, 40, 30, 95, 11, 129, 42, 26,
+/* 1590 */ 129, 45, 20, 129, 49, 11, 129, 193, 129, 49,
+/* 1600 */ 1, 129, 49, 4, 129, 49, 6, 129, 49, 8,
+/* 1610 */ 129, 49, 10, 129, 49, 12, 129, 49, 14, 129,
+/* 1620 */ 49, 17, 129, 49, 19, 129, 49, 21, 129, 49,
+/* 1630 */ 23, 129, 49, 14, 65, 9, 129, 49, 14, 67,
+/* 1640 */ 9, 129, 49, 14, 69, 9, 129, 49, 14, 71,
+/* 1650 */ 10, 129, 49, 14, 74, 9, 129, 49, 14, 76,
+/* 1660 */ 9, 129, 49, 14, 78, 9, 129, 49, 14, 80,
+/* 1670 */ 9, 129, 49, 14, 82, 9, 129, 49, 14, 84,
+/* 1680 */ 9, 129, 30, 4, 49, 14, 86, 10, 129, 30,
+/* 1690 */ 4, 49, 14, 89, 9, 129, 30, 4, 49, 14,
+/* 1700 */ 91, 9, 129, 30, 4, 49, 14, 93, 9, 129,
+/* 1710 */ 30, 74, 129, 30, 76, 129, 30, 78, 129, 30,
+/* 1720 */ 81, 129, 30, 83, 129, 30, 85, 129, 30, 87,
+/* 1730 */ 129, 30, 89, 129, 30, 91, 129, 30, 4, 49,
+/* 1740 */ 14, 132, 193, 129, 37, 1, 129, 36, 3, 77,
+/* 1750 */ 3, 129, 35, 5, 78, 11, 129, 34, 7, 78,
+/* 1760 */ 21, 129, 33, 7, 79, 29, 129, 32, 7, 79,
+/* 1770 */ 38, 129, 32, 6, 80, 4, 92, 29, 129, 31,
+/* 1780 */ 6, 80, 5, 102, 19, 129, 31, 5, 80, 6,
+/* 1790 */ 107, 14, 129, 31, 4, 81, 5, 107, 14, 129,
+/* 1800 */ 30, 5, 81, 6, 107, 14, 129, 30, 4, 81,
+/* 1810 */ 6, 107, 14, 130, 30, 4, 81, 7, 107, 14,
+/* 1820 */ 129, 30, 4, 80, 8, 107, 14, 130, 30, 5,
+/* 1830 */ 80, 8, 107, 14, 129, 30, 5, 79, 9, 107,
+/* 1840 */ 14, 129, 31, 5, 79, 9, 107, 14, 129, 31,
+/* 1850 */ 6, 78, 10, 107, 14, 129, 32, 6, 76, 11,
+/* 1860 */ 107, 14, 129, 32, 8, 74, 13, 107, 14, 129,
+/* 1870 */ 33, 10, 71, 16, 107, 14, 129, 33, 15, 67,
+/* 1880 */ 19, 107, 14, 129, 34, 51, 107, 14, 129, 35,
+/* 1890 */ 49, 107, 14, 129, 36, 47, 107, 14, 129, 37,
+/* 1900 */ 45, 107, 14, 129, 39, 41, 107, 14, 129, 41,
+/* 1910 */ 37, 107, 14, 129, 44, 32, 107, 14, 129, 47,
+/* 1920 */ 25, 111, 10, 129, 51, 16, 115, 6, 129, 119,
+/* 1930 */ 2, 129, 193, 129, 56, 39, 129, 51, 49, 129,
+/* 1940 */ 47, 57, 129, 44, 63, 129, 42, 67, 129, 40,
+/* 1950 */ 71, 129, 38, 75, 129, 37, 77, 129, 35, 81,
+/* 1960 */ 129, 34, 16, 74, 5, 101, 16, 129, 33, 11,
+/* 1970 */ 76, 5, 107, 11, 129, 32, 9, 77, 5, 110,
+/* 1980 */ 9, 129, 32, 7, 79, 4, 112, 7, 129, 31,
+/* 1990 */ 6, 80, 4, 114, 6, 129, 31, 5, 81, 4,
+/* 2000 */ 115, 5, 129, 30, 5, 82, 4, 116, 5, 129,
+/* 2010 */ 30, 4, 82, 4, 116, 5, 129, 30, 4, 82,
+/* 2020 */ 5, 117, 4, 131, 30, 5, 82, 5, 117, 4,
+/* 2030 */ 129, 31, 5, 81, 6, 117, 4, 129, 31, 6,
+/* 2040 */ 80, 7, 117, 4, 129, 32, 7, 79, 8, 117,
+/* 2050 */ 4, 129, 32, 9, 77, 9, 116, 5, 129, 33,
+/* 2060 */ 11, 75, 11, 116, 4, 129, 34, 16, 69, 16,
+/* 2070 */ 115, 5, 129, 35, 49, 114, 5, 129, 37, 46,
+/* 2080 */ 113, 5, 129, 38, 44, 112, 6, 129, 40, 41,
+/* 2090 */ 112, 5, 129, 42, 37, 113, 3, 129, 44, 33,
+/* 2100 */ 114, 1, 129, 47, 27, 129, 51, 17, 129, 193,
+/* 2110 */ 129, 103, 2, 129, 103, 6, 129, 104, 9, 129,
+/* 2120 */ 105, 12, 129, 106, 15, 129, 107, 14, 135, 30,
+/* 2130 */ 10, 107, 14, 129, 30, 17, 107, 14, 129, 30,
+/* 2140 */ 25, 107, 14, 129, 30, 31, 107, 14, 129, 30,
+/* 2150 */ 37, 107, 14, 129, 30, 42, 107, 14, 129, 30,
+/* 2160 */ 46, 107, 14, 129, 30, 50, 107, 14, 129, 30,
+/* 2170 */ 54, 107, 14, 129, 30, 58, 107, 14, 129, 59,
+/* 2180 */ 32, 107, 14, 129, 64, 30, 107, 14, 129, 74,
+/* 2190 */ 23, 107, 14, 129, 81, 18, 107, 14, 129, 86,
+/* 2200 */ 16, 107, 14, 129, 91, 14, 107, 14, 129, 96,
+/* 2210 */ 25, 129, 100, 21, 129, 104, 17, 129, 107, 14,
+/* 2220 */ 129, 111, 10, 129, 114, 7, 129, 117, 4, 129,
+/* 2230 */ 120, 1, 129, 193, 129, 48, 13, 129, 44, 21,
+/* 2240 */ 129, 42, 26, 129, 40, 30, 92, 12, 129, 38,
+/* 2250 */ 34, 88, 20, 129, 36, 37, 86, 25, 129, 35,
+/* 2260 */ 39, 84, 29, 129, 34, 13, 63, 12, 82, 33,
+/* 2270 */ 129, 33, 11, 67, 9, 80, 36, 129, 32, 9,
+/* 2280 */ 70, 7, 79, 38, 129, 31, 8, 72, 46, 129,
+/* 2290 */ 30, 7, 74, 22, 108, 11, 129, 30, 6, 75,
+/* 2300 */ 19, 111, 9, 129, 30, 5, 75, 17, 113, 7,
+/* 2310 */ 129, 30, 5, 74, 16, 114, 6, 129, 30, 4,
+/* 2320 */ 73, 16, 115, 6, 129, 30, 4, 72, 16, 116,
+/* 2330 */ 5, 129, 30, 4, 72, 15, 117, 4, 129, 30,
+/* 2340 */ 4, 71, 16, 117, 4, 129, 30, 5, 70, 16,
+/* 2350 */ 117, 4, 129, 30, 5, 70, 15, 117, 4, 129,
+/* 2360 */ 30, 6, 69, 15, 116, 5, 129, 30, 7, 68,
+/* 2370 */ 17, 115, 5, 129, 30, 9, 67, 19, 114, 6,
+/* 2380 */ 129, 30, 10, 65, 22, 113, 6, 129, 31, 12,
+/* 2390 */ 63, 27, 110, 9, 129, 32, 14, 60, 21, 84,
+/* 2400 */ 9, 106, 12, 129, 33, 47, 85, 32, 129, 34,
+/* 2410 */ 45, 86, 30, 129, 35, 43, 88, 26, 129, 36,
+/* 2420 */ 40, 90, 22, 129, 38, 36, 93, 17, 129, 40,
+/* 2430 */ 32, 96, 10, 129, 42, 28, 129, 44, 23, 129,
+/* 2440 */ 48, 15, 129, 193, 129, 83, 17, 129, 77, 27,
+/* 2450 */ 129, 36, 1, 74, 33, 129, 35, 3, 72, 37,
+/* 2460 */ 129, 34, 5, 70, 41, 129, 33, 6, 69, 44,
+/* 2470 */ 129, 33, 5, 68, 46, 129, 32, 5, 67, 49,
+/* 2480 */ 129, 31, 5, 66, 17, 101, 16, 129, 31, 5,
+/* 2490 */ 66, 11, 108, 10, 129, 30, 4, 65, 9, 110,
+/* 2500 */ 9, 129, 30, 4, 64, 8, 112, 7, 129, 30,
+/* 2510 */ 4, 64, 7, 114, 6, 129, 30, 4, 64, 6,
+/* 2520 */ 115, 5, 129, 30, 4, 64, 5, 116, 5, 129,
+/* 2530 */ 30, 4, 64, 5, 117, 4, 131, 30, 4, 65,
+/* 2540 */ 4, 117, 4, 129, 30, 5, 65, 4, 116, 5,
+/* 2550 */ 129, 31, 5, 66, 4, 115, 5, 129, 31, 6,
+/* 2560 */ 67, 4, 114, 6, 129, 32, 7, 68, 4, 112,
+/* 2570 */ 7, 129, 32, 9, 69, 5, 110, 9, 129, 33,
+/* 2580 */ 11, 70, 5, 107, 11, 129, 34, 16, 72, 5,
+/* 2590 */ 101, 16, 129, 35, 81, 129, 37, 77, 129, 38,
+/* 2600 */ 75, 129, 40, 71, 129, 42, 67, 129, 44, 63,
+/* 2610 */ 129, 47, 57, 129, 51, 49, 129, 56, 39, 129,
+/* 2620 */ 193, 130, 34, 6, 74, 6, 129, 32, 10, 72,
+/* 2630 */ 10, 129, 31, 12, 71, 12, 129, 30, 14, 70,
+/* 2640 */ 14, 131, 31, 12, 71, 12, 129, 32, 10, 72,
+/* 2650 */ 10, 129, 34, 6, 74, 6, 129, 194, 130, 34,
+/* 2660 */ 6, 74, 6, 129, 32, 10, 72, 10, 129, 31,
+/* 2670 */ 12, 71, 12, 129, 30, 14, 70, 14, 129, 20,
+/* 2680 */ 2, 28, 16, 70, 14, 129, 22, 22, 70, 14,
+/* 2690 */ 129, 24, 19, 71, 12, 129, 27, 15, 72, 10,
+/* 2700 */ 129, 31, 9, 74, 6, 129, 194, 129, 53, 4,
+/* 2710 */ 63, 4, 152, 193, 130, 99, 7, 129, 97, 13,
+/* 2720 */ 129, 96, 16, 129, 96, 18, 129, 96, 19, 129,
+/* 2730 */ 97, 19, 129, 99, 6, 110, 7, 129, 112, 6,
+/* 2740 */ 129, 114, 5, 129, 34, 6, 57, 5, 115, 4,
+/* 2750 */ 129, 32, 10, 54, 12, 116, 4, 129, 31, 12,
+/* 2760 */ 53, 16, 117, 3, 129, 30, 14, 52, 20, 117,
+/* 2770 */ 4, 129, 30, 14, 52, 23, 117, 4, 129, 30,
+/* 2780 */ 14, 52, 25, 117, 4, 129, 31, 12, 52, 27,
+/* 2790 */ 117, 4, 129, 32, 10, 53, 10, 70, 11, 116,
+/* 2800 */ 5, 129, 34, 6, 55, 5, 73, 10, 115, 6,
+/* 2810 */ 129, 74, 11, 114, 7, 129, 75, 12, 112, 9,
+/* 2820 */ 129, 76, 13, 110, 10, 129, 77, 16, 106, 14,
+/* 2830 */ 129, 78, 41, 129, 80, 38, 129, 81, 36, 129,
+/* 2840 */ 82, 34, 129, 84, 30, 129, 86, 26, 129, 88,
+/* 2850 */ 22, 129, 92, 14, 129, 194, 129, 55, 15, 129,
+/* 2860 */ 50, 25, 129, 47, 32, 129, 45, 13, 70, 12,
+/* 2870 */ 129, 43, 9, 76, 10, 129, 42, 6, 79, 8,
+/* 2880 */ 129, 41, 5, 81, 7, 129, 40, 4, 84, 6,
+/* 2890 */ 129, 39, 4, 59, 12, 85, 6, 129, 38, 4,
+/* 2900 */ 55, 19, 87, 5, 129, 37, 4, 53, 23, 88,
+/* 2910 */ 4, 129, 36, 4, 51, 8, 71, 6, 89, 4,
+/* 2920 */ 129, 36, 4, 51, 6, 73, 4, 89, 4, 129,
+/* 2930 */ 36, 4, 50, 6, 74, 4, 90, 3, 129, 35,
+/* 2940 */ 4, 50, 5, 75, 3, 90, 4, 129, 35, 4,
+/* 2950 */ 50, 4, 75, 4, 90, 4, 131, 35, 4, 50,
+/* 2960 */ 5, 75, 4, 90, 4, 129, 36, 4, 51, 5,
+/* 2970 */ 75, 4, 90, 4, 129, 36, 4, 51, 6, 75,
+/* 2980 */ 4, 90, 4, 129, 36, 4, 53, 26, 90, 4,
+/* 2990 */ 129, 37, 4, 54, 25, 90, 4, 129, 37, 4,
+/* 3000 */ 52, 27, 90, 3, 129, 38, 4, 52, 4, 89,
+/* 3010 */ 4, 129, 39, 4, 51, 4, 88, 4, 129, 40,
+/* 3020 */ 4, 50, 4, 87, 5, 129, 41, 4, 50, 4,
+/* 3030 */ 86, 5, 129, 42, 4, 50, 4, 85, 5, 129,
+/* 3040 */ 43, 3, 50, 4, 83, 6, 129, 44, 2, 51,
+/* 3050 */ 5, 80, 7, 129, 46, 1, 52, 6, 76, 9,
+/* 3060 */ 129, 54, 28, 129, 56, 23, 129, 60, 16, 129,
+/* 3070 */ 193, 129, 30, 4, 132, 30, 5, 129, 30, 8,
+/* 3080 */ 129, 30, 12, 129, 30, 16, 129, 30, 4, 37,
+/* 3090 */ 12, 129, 30, 4, 41, 12, 129, 30, 4, 44,
+/* 3100 */ 13, 129, 30, 4, 48, 13, 129, 52, 13, 129,
+/* 3110 */ 56, 12, 129, 58, 14, 129, 58, 4, 64, 12,
+/* 3120 */ 129, 58, 4, 68, 12, 129, 58, 4, 72, 12,
+/* 3130 */ 129, 58, 4, 75, 13, 129, 58, 4, 79, 13,
+/* 3140 */ 129, 58, 4, 83, 13, 129, 58, 4, 87, 13,
+/* 3150 */ 129, 58, 4, 91, 12, 129, 58, 4, 95, 12,
+/* 3160 */ 129, 58, 4, 96, 15, 129, 58, 4, 93, 22,
+/* 3170 */ 129, 58, 4, 89, 30, 129, 58, 4, 85, 36,
+/* 3180 */ 129, 58, 4, 81, 38, 129, 58, 4, 77, 38,
+/* 3190 */ 129, 58, 4, 73, 38, 129, 58, 4, 70, 37,
+/* 3200 */ 129, 58, 4, 66, 37, 129, 58, 41, 129, 58,
+/* 3210 */ 37, 129, 54, 38, 129, 30, 4, 50, 38, 129,
+/* 3220 */ 30, 4, 46, 38, 129, 30, 4, 42, 38, 129,
+/* 3230 */ 30, 4, 38, 39, 129, 30, 43, 129, 30, 39,
+/* 3240 */ 129, 30, 35, 129, 30, 31, 129, 30, 27, 129,
+/* 3250 */ 30, 24, 129, 30, 20, 129, 30, 16, 129, 30,
+/* 3260 */ 12, 129, 30, 8, 129, 30, 5, 129, 30, 4,
+/* 3270 */ 132, 193, 129, 30, 4, 117, 4, 132, 30, 91,
+/* 3280 */ 137, 30, 4, 80, 4, 117, 4, 138, 30, 4,
+/* 3290 */ 80, 5, 116, 5, 129, 30, 5, 79, 6, 116,
+/* 3300 */ 5, 130, 30, 6, 78, 8, 115, 6, 129, 31,
+/* 3310 */ 6, 77, 9, 115, 6, 129, 31, 7, 76, 11,
+/* 3320 */ 114, 6, 129, 31, 8, 75, 14, 112, 8, 129,
+/* 3330 */ 32, 8, 74, 16, 111, 9, 129, 32, 9, 73,
+/* 3340 */ 19, 109, 10, 129, 33, 10, 71, 24, 106, 13,
+/* 3350 */ 129, 33, 13, 68, 12, 83, 35, 129, 34, 16,
+/* 3360 */ 64, 15, 84, 33, 129, 35, 43, 85, 31, 129,
+/* 3370 */ 36, 41, 86, 29, 129, 37, 39, 88, 25, 129,
+/* 3380 */ 38, 37, 90, 21, 129, 40, 33, 93, 15, 129,
+/* 3390 */ 42, 29, 96, 9, 129, 45, 24, 129, 49, 16,
+/* 3400 */ 129, 193, 129, 63, 25, 129, 57, 37, 129, 53,
+/* 3410 */ 45, 129, 50, 51, 129, 47, 57, 129, 45, 61,
+/* 3420 */ 129, 43, 65, 129, 41, 69, 129, 39, 73, 129,
+/* 3430 */ 38, 25, 92, 21, 129, 36, 21, 97, 18, 129,
+/* 3440 */ 35, 18, 102, 14, 129, 34, 16, 106, 11, 129,
+/* 3450 */ 33, 14, 108, 10, 129, 32, 12, 111, 8, 129,
+/* 3460 */ 32, 10, 113, 6, 129, 31, 10, 114, 6, 129,
+/* 3470 */ 31, 8, 115, 5, 129, 30, 8, 116, 5, 129,
+/* 3480 */ 30, 7, 116, 5, 129, 30, 6, 117, 4, 130,
+/* 3490 */ 30, 5, 117, 4, 131, 31, 4, 116, 5, 129,
+/* 3500 */ 32, 4, 116, 4, 129, 32, 5, 115, 5, 129,
+/* 3510 */ 33, 4, 114, 5, 129, 34, 4, 112, 6, 129,
+/* 3520 */ 35, 4, 110, 7, 129, 37, 4, 107, 9, 129,
+/* 3530 */ 39, 4, 103, 12, 129, 41, 4, 103, 18, 129,
+/* 3540 */ 43, 4, 103, 18, 129, 45, 5, 103, 18, 129,
+/* 3550 */ 48, 5, 103, 18, 129, 51, 1, 129, 193, 129,
+/* 3560 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 3570 */ 117, 4, 135, 30, 5, 116, 5, 130, 30, 6,
+/* 3580 */ 115, 6, 130, 31, 6, 114, 6, 129, 31, 7,
+/* 3590 */ 113, 7, 129, 32, 7, 112, 7, 129, 32, 8,
+/* 3600 */ 111, 8, 129, 33, 9, 109, 9, 129, 33, 12,
+/* 3610 */ 106, 12, 129, 34, 13, 104, 13, 129, 35, 15,
+/* 3620 */ 101, 15, 129, 36, 19, 96, 19, 129, 37, 24,
+/* 3630 */ 90, 24, 129, 39, 73, 129, 40, 71, 129, 42,
+/* 3640 */ 67, 129, 44, 63, 129, 46, 59, 129, 49, 53,
+/* 3650 */ 129, 52, 47, 129, 56, 39, 129, 61, 29, 129,
+/* 3660 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 137,
+/* 3670 */ 30, 4, 80, 4, 117, 4, 140, 30, 4, 79,
+/* 3680 */ 6, 117, 4, 129, 30, 4, 77, 10, 117, 4,
+/* 3690 */ 129, 30, 4, 73, 18, 117, 4, 132, 30, 4,
+/* 3700 */ 117, 4, 130, 30, 5, 116, 5, 130, 30, 7,
+/* 3710 */ 114, 7, 129, 30, 8, 113, 8, 129, 30, 11,
+/* 3720 */ 110, 11, 129, 30, 18, 103, 18, 132, 193, 129,
+/* 3730 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 3740 */ 80, 4, 117, 4, 132, 80, 4, 117, 4, 136,
+/* 3750 */ 79, 6, 117, 4, 129, 77, 10, 117, 4, 129,
+/* 3760 */ 73, 18, 117, 4, 132, 117, 4, 130, 116, 5,
+/* 3770 */ 130, 114, 7, 129, 113, 8, 129, 110, 11, 129,
+/* 3780 */ 103, 18, 132, 193, 129, 63, 25, 129, 57, 37,
+/* 3790 */ 129, 53, 45, 129, 50, 51, 129, 47, 57, 129,
+/* 3800 */ 45, 61, 129, 43, 65, 129, 41, 69, 129, 39,
+/* 3810 */ 73, 129, 38, 25, 92, 21, 129, 36, 21, 97,
+/* 3820 */ 18, 129, 35, 18, 102, 14, 129, 34, 16, 106,
+/* 3830 */ 11, 129, 33, 14, 108, 10, 129, 32, 12, 111,
+/* 3840 */ 8, 129, 32, 10, 113, 6, 129, 31, 10, 114,
+/* 3850 */ 6, 129, 31, 8, 115, 5, 129, 30, 8, 116,
+/* 3860 */ 5, 129, 30, 7, 116, 5, 129, 30, 6, 117,
+/* 3870 */ 4, 130, 30, 5, 117, 4, 131, 30, 5, 75,
+/* 3880 */ 4, 116, 5, 129, 31, 5, 75, 4, 116, 4,
+/* 3890 */ 129, 31, 6, 75, 4, 115, 5, 129, 32, 7,
+/* 3900 */ 75, 4, 114, 5, 129, 32, 9, 75, 4, 112,
+/* 3910 */ 6, 129, 33, 11, 75, 4, 110, 7, 129, 34,
+/* 3920 */ 15, 75, 4, 107, 9, 129, 35, 44, 103, 12,
+/* 3930 */ 129, 36, 43, 103, 18, 129, 38, 41, 103, 18,
+/* 3940 */ 129, 39, 40, 103, 18, 129, 41, 38, 103, 18,
+/* 3950 */ 129, 44, 35, 129, 48, 31, 129, 52, 27, 129,
+/* 3960 */ 61, 18, 129, 193, 129, 30, 4, 117, 4, 132,
+/* 3970 */ 30, 91, 137, 30, 4, 80, 4, 117, 4, 132,
+/* 3980 */ 80, 4, 140, 30, 4, 80, 4, 117, 4, 132,
+/* 3990 */ 30, 91, 137, 30, 4, 117, 4, 132, 193, 129,
+/* 4000 */ 30, 4, 117, 4, 132, 30, 91, 137, 30, 4,
+/* 4010 */ 117, 4, 132, 193, 129, 44, 7, 129, 40, 13,
+/* 4020 */ 129, 37, 17, 129, 35, 20, 129, 34, 22, 129,
+/* 4030 */ 33, 23, 129, 32, 24, 129, 32, 23, 129, 31,
+/* 4040 */ 6, 41, 13, 129, 31, 5, 42, 11, 129, 30,
+/* 4050 */ 5, 44, 7, 129, 30, 4, 132, 30, 5, 130,
+/* 4060 */ 31, 5, 129, 31, 6, 117, 4, 129, 31, 8,
+/* 4070 */ 117, 4, 129, 32, 9, 117, 4, 129, 33, 11,
+/* 4080 */ 117, 4, 129, 34, 87, 129, 35, 86, 129, 36,
+/* 4090 */ 85, 129, 37, 84, 129, 38, 83, 129, 40, 81,
+/* 4100 */ 129, 42, 79, 129, 45, 76, 129, 50, 71, 129,
+/* 4110 */ 117, 4, 132, 193, 129, 30, 4, 117, 4, 132,
+/* 4120 */ 30, 91, 137, 30, 4, 76, 8, 117, 4, 129,
+/* 4130 */ 30, 4, 73, 13, 117, 4, 129, 30, 4, 70,
+/* 4140 */ 18, 117, 4, 129, 30, 4, 67, 23, 117, 4,
+/* 4150 */ 129, 65, 26, 129, 62, 31, 129, 59, 35, 129,
+/* 4160 */ 56, 29, 89, 7, 129, 53, 29, 91, 7, 129,
+/* 4170 */ 50, 29, 93, 7, 129, 47, 29, 95, 6, 129,
+/* 4180 */ 30, 4, 45, 29, 96, 7, 129, 30, 4, 42,
+/* 4190 */ 29, 98, 7, 129, 30, 4, 39, 30, 100, 6,
+/* 4200 */ 129, 30, 4, 36, 30, 101, 7, 129, 30, 33,
+/* 4210 */ 103, 7, 117, 4, 129, 30, 30, 105, 6, 117,
+/* 4220 */ 4, 129, 30, 27, 106, 7, 117, 4, 129, 30,
+/* 4230 */ 25, 108, 7, 117, 4, 129, 30, 22, 110, 11,
+/* 4240 */ 129, 30, 19, 111, 10, 129, 30, 16, 113, 8,
+/* 4250 */ 129, 30, 13, 115, 6, 129, 30, 11, 116, 5,
+/* 4260 */ 129, 30, 8, 117, 4, 129, 30, 5, 117, 4,
+/* 4270 */ 129, 30, 4, 117, 4, 130, 30, 4, 130, 193,
+/* 4280 */ 129, 30, 4, 117, 4, 132, 30, 91, 137, 30,
+/* 4290 */ 4, 117, 4, 132, 30, 4, 144, 30, 5, 130,
+/* 4300 */ 30, 7, 129, 30, 8, 129, 30, 11, 129, 30,
+/* 4310 */ 18, 132, 193, 129, 30, 4, 117, 4, 132, 30,
+/* 4320 */ 91, 132, 30, 4, 103, 18, 129, 30, 4, 97,
+/* 4330 */ 24, 129, 30, 4, 92, 29, 129, 30, 4, 87,
+/* 4340 */ 34, 129, 81, 40, 129, 76, 45, 129, 70, 49,
+/* 4350 */ 129, 65, 49, 129, 60, 49, 129, 55, 49, 129,
+/* 4360 */ 50, 48, 129, 44, 49, 129, 39, 48, 129, 33,
+/* 4370 */ 49, 129, 30, 47, 129, 34, 37, 129, 40, 26,
+/* 4380 */ 129, 46, 19, 129, 52, 19, 129, 58, 19, 129,
+/* 4390 */ 64, 19, 129, 70, 19, 129, 76, 19, 129, 82,
+/* 4400 */ 19, 129, 30, 4, 88, 18, 129, 30, 4, 94,
+/* 4410 */ 18, 129, 30, 4, 100, 18, 129, 30, 4, 106,
+/* 4420 */ 15, 129, 30, 91, 137, 30, 4, 117, 4, 132,
+/* 4430 */ 193, 129, 30, 4, 117, 4, 132, 30, 91, 132,
+/* 4440 */ 30, 4, 107, 14, 129, 30, 4, 104, 17, 129,
+/* 4450 */ 30, 4, 101, 20, 129, 30, 4, 99, 22, 129,
+/* 4460 */ 96, 25, 129, 93, 28, 129, 91, 28, 129, 88,
+/* 4470 */ 29, 129, 85, 29, 129, 82, 29, 129, 79, 29,
+/* 4480 */ 129, 76, 29, 129, 74, 29, 129, 71, 29, 129,
+/* 4490 */ 68, 29, 129, 65, 29, 129, 62, 29, 129, 60,
+/* 4500 */ 29, 129, 57, 29, 129, 54, 29, 129, 51, 29,
+/* 4510 */ 129, 49, 28, 129, 46, 29, 129, 43, 29, 129,
+/* 4520 */ 40, 29, 117, 4, 129, 37, 29, 117, 4, 129,
+/* 4530 */ 35, 29, 117, 4, 129, 32, 29, 117, 4, 129,
+/* 4540 */ 30, 91, 132, 117, 4, 132, 193, 129, 63, 25,
+/* 4550 */ 129, 57, 37, 129, 53, 45, 129, 50, 51, 129,
+/* 4560 */ 47, 57, 129, 45, 61, 129, 43, 65, 129, 41,
+/* 4570 */ 69, 129, 39, 73, 129, 38, 21, 92, 21, 129,
+/* 4580 */ 36, 18, 97, 18, 129, 35, 14, 102, 14, 129,
+/* 4590 */ 34, 11, 106, 11, 129, 33, 10, 108, 10, 129,
+/* 4600 */ 32, 8, 111, 8, 129, 32, 6, 113, 6, 129,
+/* 4610 */ 31, 6, 114, 6, 129, 31, 5, 115, 5, 129,
+/* 4620 */ 30, 5, 116, 5, 130, 30, 4, 117, 4, 132,
+/* 4630 */ 30, 5, 116, 5, 130, 31, 5, 115, 5, 129,
+/* 4640 */ 31, 6, 114, 6, 129, 32, 6, 113, 6, 129,
+/* 4650 */ 32, 8, 111, 8, 129, 33, 10, 108, 10, 129,
+/* 4660 */ 34, 11, 106, 11, 129, 35, 14, 102, 14, 129,
+/* 4670 */ 36, 18, 97, 18, 129, 38, 21, 92, 21, 129,
+/* 4680 */ 39, 73, 129, 41, 69, 129, 43, 65, 129, 45,
+/* 4690 */ 61, 129, 47, 57, 129, 50, 51, 129, 53, 45,
+/* 4700 */ 129, 57, 37, 129, 63, 25, 129, 193, 129, 30,
+/* 4710 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 80,
+/* 4720 */ 4, 117, 4, 132, 80, 4, 117, 4, 134, 80,
+/* 4730 */ 5, 116, 5, 131, 80, 6, 115, 6, 130, 81,
+/* 4740 */ 6, 114, 6, 129, 81, 8, 112, 8, 129, 81,
+/* 4750 */ 9, 111, 9, 129, 82, 10, 109, 10, 129, 82,
+/* 4760 */ 13, 106, 13, 129, 83, 35, 129, 84, 33, 129,
+/* 4770 */ 85, 31, 129, 86, 29, 129, 88, 25, 129, 90,
+/* 4780 */ 21, 129, 93, 15, 129, 96, 9, 129, 193, 129,
+/* 4790 */ 63, 25, 129, 57, 37, 129, 53, 45, 129, 50,
+/* 4800 */ 51, 129, 47, 57, 129, 45, 61, 129, 43, 65,
+/* 4810 */ 129, 41, 69, 129, 39, 73, 129, 38, 21, 92,
+/* 4820 */ 21, 129, 36, 18, 97, 18, 129, 35, 14, 102,
+/* 4830 */ 14, 129, 34, 11, 106, 11, 129, 33, 10, 108,
+/* 4840 */ 10, 129, 32, 8, 111, 8, 129, 32, 6, 113,
+/* 4850 */ 6, 129, 31, 6, 114, 6, 129, 31, 5, 115,
+/* 4860 */ 5, 129, 30, 5, 116, 5, 130, 30, 4, 39,
+/* 4870 */ 2, 117, 4, 129, 30, 4, 40, 4, 117, 4,
+/* 4880 */ 129, 30, 4, 41, 5, 117, 4, 129, 30, 4,
+/* 4890 */ 41, 6, 117, 4, 129, 30, 5, 40, 8, 116,
+/* 4900 */ 5, 129, 30, 5, 39, 10, 116, 5, 129, 31,
+/* 4910 */ 5, 38, 11, 115, 5, 129, 31, 18, 114, 6,
+/* 4920 */ 129, 32, 17, 113, 6, 129, 32, 16, 111, 8,
+/* 4930 */ 129, 33, 15, 108, 10, 129, 33, 14, 106, 11,
+/* 4940 */ 129, 32, 17, 102, 14, 129, 31, 23, 97, 18,
+/* 4950 */ 129, 31, 28, 92, 21, 129, 30, 82, 129, 30,
+/* 4960 */ 80, 129, 30, 11, 43, 65, 129, 30, 10, 45,
+/* 4970 */ 61, 129, 31, 8, 47, 57, 129, 32, 6, 50,
+/* 4980 */ 51, 129, 33, 5, 53, 45, 129, 35, 4, 57,
+/* 4990 */ 37, 129, 38, 2, 63, 25, 129, 193, 129, 30,
+/* 5000 */ 4, 117, 4, 132, 30, 91, 137, 30, 4, 76,
+/* 5010 */ 8, 117, 4, 129, 30, 4, 73, 11, 117, 4,
+/* 5020 */ 129, 30, 4, 70, 14, 117, 4, 129, 30, 4,
+/* 5030 */ 67, 17, 117, 4, 129, 65, 19, 117, 4, 129,
+/* 5040 */ 62, 22, 117, 4, 129, 59, 25, 117, 4, 129,
+/* 5050 */ 56, 28, 117, 4, 129, 53, 31, 117, 4, 129,
+/* 5060 */ 50, 34, 117, 4, 129, 47, 29, 80, 5, 116,
+/* 5070 */ 5, 129, 30, 4, 45, 29, 80, 5, 116, 5,
+/* 5080 */ 129, 30, 4, 42, 29, 80, 5, 116, 5, 129,
+/* 5090 */ 30, 4, 39, 30, 80, 6, 115, 6, 129, 30,
+/* 5100 */ 4, 36, 30, 80, 6, 115, 6, 129, 30, 33,
+/* 5110 */ 81, 6, 114, 6, 129, 30, 30, 81, 8, 112,
+/* 5120 */ 8, 129, 30, 27, 81, 9, 111, 9, 129, 30,
+/* 5130 */ 25, 82, 10, 109, 10, 129, 30, 22, 82, 13,
+/* 5140 */ 106, 13, 129, 30, 19, 83, 35, 129, 30, 16,
+/* 5150 */ 84, 33, 129, 30, 13, 85, 31, 129, 30, 11,
+/* 5160 */ 86, 29, 129, 30, 8, 88, 25, 129, 30, 5,
+/* 5170 */ 90, 21, 129, 30, 4, 93, 15, 129, 30, 4,
+/* 5180 */ 96, 9, 129, 30, 4, 130, 193, 129, 30, 18,
+/* 5190 */ 130, 30, 18, 89, 15, 129, 30, 18, 85, 23,
+/* 5200 */ 129, 34, 11, 83, 27, 129, 34, 9, 81, 31,
+/* 5210 */ 129, 33, 8, 79, 35, 129, 33, 6, 78, 16,
+/* 5220 */ 106, 9, 129, 32, 6, 77, 15, 109, 7, 129,
+/* 5230 */ 32, 5, 76, 14, 111, 6, 129, 31, 5, 75,
+/* 5240 */ 14, 113, 5, 129, 31, 4, 74, 15, 114, 5,
+/* 5250 */ 129, 31, 4, 74, 14, 115, 4, 129, 30, 4,
+/* 5260 */ 73, 15, 116, 4, 129, 30, 4, 73, 14, 116,
+/* 5270 */ 4, 129, 30, 4, 73, 14, 117, 4, 129, 30,
+/* 5280 */ 4, 72, 15, 117, 4, 130, 30, 4, 71, 15,
+/* 5290 */ 117, 4, 130, 30, 4, 70, 15, 117, 4, 129,
+/* 5300 */ 30, 5, 70, 15, 117, 4, 129, 30, 5, 69,
+/* 5310 */ 15, 116, 5, 129, 30, 6, 68, 16, 115, 5,
+/* 5320 */ 129, 31, 6, 67, 16, 114, 6, 129, 31, 7,
+/* 5330 */ 66, 17, 113, 6, 129, 32, 7, 64, 18, 111,
+/* 5340 */ 8, 129, 32, 8, 62, 19, 109, 9, 129, 33,
+/* 5350 */ 9, 60, 20, 107, 10, 129, 34, 11, 57, 22,
+/* 5360 */ 103, 13, 129, 35, 43, 103, 18, 129, 36, 41,
+/* 5370 */ 103, 18, 129, 38, 38, 103, 18, 129, 39, 35,
+/* 5380 */ 103, 18, 129, 41, 31, 129, 43, 27, 129, 46,
+/* 5390 */ 22, 129, 49, 14, 129, 193, 129, 103, 18, 132,
+/* 5400 */ 110, 11, 129, 113, 8, 129, 114, 7, 129, 116,
+/* 5410 */ 5, 130, 117, 4, 132, 30, 4, 117, 4, 132,
+/* 5420 */ 30, 91, 137, 30, 4, 117, 4, 132, 117, 4,
+/* 5430 */ 132, 116, 5, 130, 114, 7, 129, 113, 8, 129,
+/* 5440 */ 110, 11, 129, 103, 18, 132, 193, 129, 117, 4,
+/* 5450 */ 132, 56, 65, 129, 50, 71, 129, 46, 75, 129,
+/* 5460 */ 44, 77, 129, 42, 79, 129, 40, 81, 129, 38,
+/* 5470 */ 83, 129, 36, 85, 129, 35, 86, 129, 34, 20,
+/* 5480 */ 117, 4, 129, 33, 17, 117, 4, 129, 32, 15,
+/* 5490 */ 117, 4, 129, 32, 13, 117, 4, 129, 31, 12,
+/* 5500 */ 129, 31, 10, 129, 31, 9, 129, 30, 9, 129,
+/* 5510 */ 30, 8, 130, 30, 7, 132, 31, 6, 130, 31,
+/* 5520 */ 7, 129, 32, 6, 129, 32, 7, 129, 33, 7,
+/* 5530 */ 129, 34, 7, 129, 35, 8, 129, 36, 9, 117,
+/* 5540 */ 4, 129, 38, 9, 117, 4, 129, 40, 10, 117,
+/* 5550 */ 4, 129, 42, 12, 117, 4, 129, 44, 77, 129,
+/* 5560 */ 46, 75, 129, 50, 71, 129, 56, 43, 100, 21,
+/* 5570 */ 129, 117, 4, 132, 193, 129, 117, 4, 132, 115,
+/* 5580 */ 6, 129, 110, 11, 129, 105, 16, 129, 101, 20,
+/* 5590 */ 129, 96, 25, 129, 92, 29, 129, 87, 34, 129,
+/* 5600 */ 83, 38, 129, 78, 43, 129, 74, 47, 129, 70,
+/* 5610 */ 42, 117, 4, 129, 65, 42, 117, 4, 129, 60,
+/* 5620 */ 43, 117, 4, 129, 56, 42, 129, 51, 42, 129,
+/* 5630 */ 46, 43, 129, 42, 43, 129, 37, 44, 129, 33,
+/* 5640 */ 43, 129, 30, 42, 129, 33, 34, 129, 38, 25,
+/* 5650 */ 129, 42, 16, 129, 47, 15, 129, 52, 15, 129,
+/* 5660 */ 57, 15, 129, 61, 16, 129, 66, 16, 129, 71,
+/* 5670 */ 16, 129, 76, 16, 129, 80, 16, 129, 85, 16,
+/* 5680 */ 117, 4, 129, 90, 16, 117, 4, 129, 95, 16,
+/* 5690 */ 117, 4, 129, 100, 21, 129, 105, 16, 129, 110,
+/* 5700 */ 11, 129, 114, 7, 129, 117, 4, 132, 193, 129,
+/* 5710 */ 117, 4, 132, 115, 6, 129, 110, 11, 129, 105,
+/* 5720 */ 16, 129, 101, 20, 129, 96, 25, 129, 92, 29,
+/* 5730 */ 129, 87, 34, 129, 83, 38, 129, 78, 43, 129,
+/* 5740 */ 74, 47, 129, 70, 42, 117, 4, 129, 65, 42,
+/* 5750 */ 117, 4, 129, 60, 43, 117, 4, 129, 56, 42,
+/* 5760 */ 129, 51, 42, 129, 46, 43, 129, 42, 43, 129,
+/* 5770 */ 37, 44, 129, 33, 43, 129, 30, 42, 129, 33,
+/* 5780 */ 34, 129, 38, 25, 129, 42, 16, 129, 47, 15,
+/* 5790 */ 129, 52, 15, 129, 57, 15, 129, 61, 16, 129,
+/* 5800 */ 65, 17, 129, 60, 27, 129, 56, 36, 129, 51,
+/* 5810 */ 42, 129, 46, 43, 129, 42, 43, 129, 37, 44,
+/* 5820 */ 129, 33, 43, 129, 30, 42, 129, 33, 34, 129,
+/* 5830 */ 38, 25, 129, 42, 16, 129, 47, 15, 129, 52,
+/* 5840 */ 15, 129, 57, 15, 129, 61, 16, 129, 66, 16,
+/* 5850 */ 129, 71, 16, 129, 76, 16, 129, 80, 16, 129,
+/* 5860 */ 85, 16, 117, 4, 129, 90, 16, 117, 4, 129,
+/* 5870 */ 95, 16, 117, 4, 129, 100, 21, 129, 105, 16,
+/* 5880 */ 129, 110, 11, 129, 114, 7, 129, 117, 4, 132,
+/* 5890 */ 193, 129, 30, 4, 117, 4, 132, 30, 4, 115,
+/* 5900 */ 6, 129, 30, 4, 112, 9, 129, 30, 6, 109,
+/* 5910 */ 12, 129, 30, 9, 106, 15, 129, 30, 11, 103,
+/* 5920 */ 18, 129, 30, 14, 100, 21, 129, 30, 4, 38,
+/* 5930 */ 9, 98, 23, 129, 30, 4, 40, 10, 95, 26,
+/* 5940 */ 129, 30, 4, 43, 9, 92, 29, 129, 46, 9,
+/* 5950 */ 89, 32, 129, 49, 8, 86, 28, 117, 4, 129,
+/* 5960 */ 51, 9, 83, 28, 117, 4, 129, 54, 9, 80,
+/* 5970 */ 28, 117, 4, 129, 57, 8, 77, 28, 117, 4,
+/* 5980 */ 129, 59, 9, 74, 28, 129, 62, 37, 129, 64,
+/* 5990 */ 33, 129, 66, 28, 129, 63, 28, 129, 60, 28,
+/* 6000 */ 129, 57, 28, 129, 54, 33, 129, 51, 39, 129,
+/* 6010 */ 48, 29, 83, 9, 129, 30, 4, 45, 29, 86,
+/* 6020 */ 9, 129, 30, 4, 42, 29, 89, 9, 129, 30,
+/* 6030 */ 4, 39, 29, 92, 8, 129, 30, 4, 36, 29,
+/* 6040 */ 94, 9, 129, 30, 32, 97, 9, 129, 30, 29,
+/* 6050 */ 100, 8, 117, 4, 129, 30, 26, 103, 8, 117,
+/* 6060 */ 4, 129, 30, 23, 105, 9, 117, 4, 129, 30,
+/* 6070 */ 20, 108, 13, 129, 30, 18, 111, 10, 129, 30,
+/* 6080 */ 15, 113, 8, 129, 30, 12, 116, 5, 129, 30,
+/* 6090 */ 9, 117, 4, 129, 30, 6, 117, 4, 129, 30,
+/* 6100 */ 4, 117, 4, 132, 193, 129, 117, 4, 132, 114,
+/* 6110 */ 7, 129, 111, 10, 129, 108, 13, 129, 105, 16,
+/* 6120 */ 129, 102, 19, 129, 100, 21, 129, 96, 25, 129,
+/* 6130 */ 93, 28, 129, 90, 31, 129, 87, 34, 129, 84,
+/* 6140 */ 30, 117, 4, 129, 30, 4, 81, 30, 117, 4,
+/* 6150 */ 129, 30, 4, 78, 30, 117, 4, 129, 30, 4,
+/* 6160 */ 75, 30, 117, 4, 129, 30, 4, 72, 30, 129,
+/* 6170 */ 30, 69, 129, 30, 66, 129, 30, 63, 129, 30,
+/* 6180 */ 60, 129, 30, 57, 129, 30, 54, 129, 30, 51,
+/* 6190 */ 129, 30, 48, 129, 30, 51, 129, 30, 4, 73,
+/* 6200 */ 12, 129, 30, 4, 76, 12, 129, 30, 4, 80,
+/* 6210 */ 12, 129, 30, 4, 83, 12, 129, 87, 12, 129,
+/* 6220 */ 90, 12, 117, 4, 129, 94, 11, 117, 4, 129,
+/* 6230 */ 97, 12, 117, 4, 129, 101, 12, 117, 4, 129,
+/* 6240 */ 104, 17, 129, 108, 13, 129, 111, 10, 129, 115,
+/* 6250 */ 6, 129, 117, 4, 134, 193, 129, 30, 1, 103,
+/* 6260 */ 18, 129, 30, 4, 103, 18, 129, 30, 7, 103,
+/* 6270 */ 18, 129, 30, 9, 103, 18, 129, 30, 12, 110,
+/* 6280 */ 11, 129, 30, 15, 113, 8, 129, 30, 18, 114,
+/* 6290 */ 7, 129, 30, 21, 116, 5, 129, 30, 24, 116,
+/* 6300 */ 5, 129, 30, 27, 117, 4, 129, 30, 30, 117,
+/* 6310 */ 4, 129, 30, 33, 117, 4, 129, 30, 4, 37,
+/* 6320 */ 28, 117, 4, 129, 30, 4, 40, 28, 117, 4,
+/* 6330 */ 129, 30, 4, 42, 29, 117, 4, 129, 30, 4,
+/* 6340 */ 45, 29, 117, 4, 129, 30, 4, 48, 29, 117,
+/* 6350 */ 4, 129, 30, 4, 51, 29, 117, 4, 129, 30,
+/* 6360 */ 4, 54, 29, 117, 4, 129, 30, 4, 57, 29,
+/* 6370 */ 117, 4, 129, 30, 4, 59, 30, 117, 4, 129,
+/* 6380 */ 30, 4, 62, 30, 117, 4, 129, 30, 4, 65,
+/* 6390 */ 30, 117, 4, 129, 30, 4, 68, 30, 117, 4,
+/* 6400 */ 129, 30, 4, 71, 30, 117, 4, 129, 30, 4,
+/* 6410 */ 74, 30, 117, 4, 129, 30, 4, 77, 30, 117,
+/* 6420 */ 4, 129, 30, 4, 80, 30, 117, 4, 129, 30,
+/* 6430 */ 4, 83, 30, 117, 4, 129, 30, 4, 86, 35,
+/* 6440 */ 129, 30, 4, 89, 32, 129, 30, 4, 91, 30,
+/* 6450 */ 129, 30, 4, 94, 27, 129, 30, 5, 97, 24,
+/* 6460 */ 129, 30, 5, 100, 21, 129, 30, 7, 103, 18,
+/* 6470 */ 129, 30, 8, 106, 15, 129, 30, 11, 109, 12,
+/* 6480 */ 129, 30, 18, 112, 9, 129, 30, 18, 115, 6,
+/* 6490 */ 129, 30, 18, 117, 4, 129, 30, 18, 120, 1,
+/* 6500 */ 129, 193, 129, 42, 8, 129, 38, 16, 129, 36,
+/* 6510 */ 20, 129, 34, 24, 71, 5, 129, 33, 26, 69,
+/* 6520 */ 10, 129, 32, 28, 68, 13, 129, 31, 30, 68,
+/* 6530 */ 14, 129, 31, 9, 52, 9, 68, 15, 129, 30,
+/* 6540 */ 8, 54, 8, 69, 14, 129, 30, 7, 55, 7,
+/* 6550 */ 71, 4, 78, 6, 129, 30, 6, 56, 6, 79,
+/* 6560 */ 5, 129, 30, 6, 56, 6, 80, 4, 130, 31,
+/* 6570 */ 5, 56, 5, 80, 4, 129, 31, 5, 56, 5,
+/* 6580 */ 79, 5, 129, 32, 5, 55, 5, 78, 6, 129,
+/* 6590 */ 33, 5, 54, 5, 77, 7, 129, 34, 6, 52,
+/* 6600 */ 6, 74, 9, 129, 35, 48, 129, 33, 49, 129,
+/* 6610 */ 32, 49, 129, 31, 49, 129, 30, 49, 129, 30,
+/* 6620 */ 47, 129, 30, 45, 129, 30, 41, 129, 30, 6,
+/* 6630 */ 129, 30, 4, 129, 30, 3, 129, 30, 2, 129,
+/* 6640 */ 193, 129, 30, 4, 117, 4, 130, 31, 90, 136,
+/* 6650 */ 37, 5, 72, 5, 129, 35, 5, 74, 5, 129,
+/* 6660 */ 33, 5, 76, 5, 129, 32, 5, 77, 5, 129,
+/* 6670 */ 31, 5, 78, 5, 129, 31, 4, 79, 4, 129,
+/* 6680 */ 30, 5, 79, 5, 131, 30, 6, 78, 6, 129,
+/* 6690 */ 30, 7, 77, 7, 129, 31, 8, 75, 8, 129,
+/* 6700 */ 31, 11, 72, 11, 129, 32, 15, 67, 15, 129,
+/* 6710 */ 33, 48, 129, 34, 46, 129, 35, 44, 129, 37,
+/* 6720 */ 40, 129, 39, 36, 129, 42, 30, 129, 46, 22,
+/* 6730 */ 129, 193, 129, 48, 18, 129, 43, 28, 129, 41,
+/* 6740 */ 32, 129, 39, 36, 129, 37, 40, 129, 35, 44,
+/* 6750 */ 129, 34, 46, 129, 33, 13, 68, 13, 129, 32,
+/* 6760 */ 9, 73, 9, 129, 32, 7, 75, 7, 129, 31,
+/* 6770 */ 6, 77, 6, 129, 31, 5, 78, 5, 129, 30,
+/* 6780 */ 5, 79, 5, 129, 30, 4, 80, 4, 133, 31,
+/* 6790 */ 3, 79, 4, 129, 31, 4, 79, 4, 129, 32,
+/* 6800 */ 3, 78, 4, 129, 32, 4, 76, 6, 129, 33,
+/* 6810 */ 4, 74, 7, 129, 34, 4, 72, 8, 129, 35,
+/* 6820 */ 5, 72, 7, 129, 37, 5, 73, 4, 129, 39,
+/* 6830 */ 4, 74, 1, 129, 129, 193, 129, 46, 22, 129,
+/* 6840 */ 42, 30, 129, 39, 36, 129, 37, 40, 129, 35,
+/* 6850 */ 44, 129, 34, 46, 129, 33, 48, 129, 32, 15,
+/* 6860 */ 67, 15, 129, 31, 11, 72, 11, 129, 31, 8,
+/* 6870 */ 75, 8, 129, 30, 7, 77, 7, 129, 30, 6,
+/* 6880 */ 78, 6, 129, 30, 5, 79, 5, 131, 31, 4,
+/* 6890 */ 79, 4, 129, 31, 5, 78, 5, 129, 32, 5,
+/* 6900 */ 77, 5, 129, 33, 5, 76, 5, 129, 35, 5,
+/* 6910 */ 74, 5, 117, 4, 129, 37, 5, 72, 5, 117,
+/* 6920 */ 4, 129, 30, 91, 136, 30, 4, 130, 193, 129,
+/* 6930 */ 48, 18, 129, 43, 28, 129, 41, 32, 129, 39,
+/* 6940 */ 36, 129, 37, 40, 129, 35, 44, 129, 34, 46,
+/* 6950 */ 129, 33, 13, 55, 4, 68, 13, 129, 32, 9,
+/* 6960 */ 55, 4, 73, 9, 129, 32, 7, 55, 4, 75,
+/* 6970 */ 7, 129, 31, 6, 55, 4, 77, 6, 129, 31,
+/* 6980 */ 5, 55, 4, 78, 5, 129, 30, 5, 55, 4,
+/* 6990 */ 79, 5, 129, 30, 4, 55, 4, 80, 4, 132,
+/* 7000 */ 30, 4, 55, 4, 79, 5, 129, 31, 3, 55,
+/* 7010 */ 4, 78, 5, 129, 31, 4, 55, 4, 77, 6,
+/* 7020 */ 129, 32, 3, 55, 4, 75, 7, 129, 32, 4,
+/* 7030 */ 55, 4, 73, 9, 129, 33, 4, 55, 4, 68,
+/* 7040 */ 13, 129, 34, 4, 55, 25, 129, 35, 5, 55,
+/* 7050 */ 24, 129, 37, 5, 55, 22, 129, 39, 4, 55,
+/* 7060 */ 20, 129, 55, 18, 129, 55, 16, 129, 55, 11,
+/* 7070 */ 129, 193, 129, 80, 4, 129, 30, 4, 80, 4,
+/* 7080 */ 130, 30, 78, 129, 30, 82, 129, 30, 85, 129,
+/* 7090 */ 30, 87, 129, 30, 88, 129, 30, 89, 129, 30,
+/* 7100 */ 90, 130, 30, 4, 80, 4, 115, 6, 129, 30,
+/* 7110 */ 4, 80, 4, 117, 4, 129, 80, 4, 105, 6,
+/* 7120 */ 117, 4, 129, 80, 4, 103, 10, 116, 5, 129,
+/* 7130 */ 80, 4, 102, 19, 129, 80, 4, 101, 19, 129,
+/* 7140 */ 101, 19, 129, 101, 18, 129, 102, 16, 129, 103,
+/* 7150 */ 12, 129, 105, 6, 129, 193, 129, 12, 10, 59,
+/* 7160 */ 11, 129, 9, 16, 55, 19, 129, 7, 20, 53,
+/* 7170 */ 23, 129, 6, 7, 23, 5, 32, 6, 51, 27,
+/* 7180 */ 129, 4, 7, 25, 16, 50, 29, 129, 3, 6,
+/* 7190 */ 27, 16, 49, 31, 129, 2, 6, 28, 16, 48,
+/* 7200 */ 33, 129, 1, 6, 27, 18, 47, 35, 129, 1,
+/* 7210 */ 6, 27, 31, 71, 12, 129, 1, 5, 26, 15,
+/* 7220 */ 44, 10, 75, 8, 129, 1, 5, 25, 14, 45,
+/* 7230 */ 7, 77, 7, 129, 1, 5, 25, 13, 45, 5,
+/* 7240 */ 79, 5, 129, 1, 5, 24, 14, 45, 4, 80,
+/* 7250 */ 4, 129, 1, 5, 24, 13, 45, 4, 80, 4,
+/* 7260 */ 129, 1, 5, 23, 14, 45, 4, 80, 4, 129,
+/* 7270 */ 1, 5, 23, 13, 45, 4, 80, 4, 129, 1,
+/* 7280 */ 6, 22, 13, 45, 5, 79, 5, 129, 1, 6,
+/* 7290 */ 21, 14, 45, 7, 77, 7, 129, 1, 7, 21,
+/* 7300 */ 13, 46, 8, 75, 8, 129, 1, 8, 20, 13,
+/* 7310 */ 46, 12, 71, 12, 129, 1, 10, 18, 15, 47,
+/* 7320 */ 35, 129, 2, 30, 48, 33, 129, 3, 29, 49,
+/* 7330 */ 32, 129, 4, 27, 50, 31, 129, 5, 25, 51,
+/* 7340 */ 27, 80, 2, 86, 4, 129, 7, 21, 53, 23,
+/* 7350 */ 80, 3, 85, 6, 129, 9, 17, 55, 19, 80,
+/* 7360 */ 12, 129, 12, 12, 59, 11, 81, 11, 129, 82,
+/* 7370 */ 10, 129, 84, 7, 129, 86, 4, 129, 193, 129,
+/* 7380 */ 30, 4, 117, 4, 130, 30, 91, 136, 30, 4,
+/* 7390 */ 72, 5, 129, 30, 4, 74, 5, 129, 75, 5,
+/* 7400 */ 129, 76, 5, 129, 76, 6, 129, 77, 6, 130,
+/* 7410 */ 77, 7, 130, 76, 8, 129, 30, 4, 75, 9,
+/* 7420 */ 129, 30, 4, 72, 12, 129, 30, 54, 129, 30,
+/* 7430 */ 53, 130, 30, 52, 129, 30, 51, 129, 30, 49,
+/* 7440 */ 129, 30, 46, 129, 30, 42, 129, 30, 4, 130,
+/* 7450 */ 193, 129, 30, 4, 80, 4, 129, 30, 4, 80,
+/* 7460 */ 4, 100, 6, 129, 30, 54, 98, 10, 129, 30,
+/* 7470 */ 54, 97, 12, 129, 30, 54, 96, 14, 131, 30,
+/* 7480 */ 54, 97, 12, 129, 30, 54, 98, 10, 129, 30,
+/* 7490 */ 54, 100, 6, 129, 30, 4, 130, 193, 129, 7,
+/* 7500 */ 6, 129, 4, 11, 129, 3, 13, 129, 2, 14,
+/* 7510 */ 129, 1, 15, 130, 1, 3, 6, 9, 129, 1,
+/* 7520 */ 3, 7, 6, 129, 1, 3, 130, 1, 4, 129,
+/* 7530 */ 1, 5, 80, 4, 129, 1, 7, 80, 4, 100,
+/* 7540 */ 6, 129, 2, 82, 98, 10, 129, 3, 81, 97,
+/* 7550 */ 12, 129, 4, 80, 96, 14, 129, 5, 79, 96,
+/* 7560 */ 14, 129, 7, 77, 96, 14, 129, 10, 74, 97,
+/* 7570 */ 12, 129, 14, 70, 98, 10, 129, 19, 65, 100,
+/* 7580 */ 6, 129, 193, 129, 30, 4, 117, 4, 130, 30,
+/* 7590 */ 91, 136, 30, 4, 57, 9, 129, 30, 4, 55,
+/* 7600 */ 12, 129, 52, 17, 129, 50, 20, 129, 48, 24,
+/* 7610 */ 129, 46, 27, 129, 44, 21, 69, 6, 129, 41,
+/* 7620 */ 22, 70, 6, 80, 4, 129, 30, 4, 39, 21,
+/* 7630 */ 72, 6, 80, 4, 129, 30, 4, 36, 22, 73,
+/* 7640 */ 11, 129, 30, 26, 75, 9, 129, 30, 23, 76,
+/* 7650 */ 8, 129, 30, 21, 78, 6, 129, 30, 19, 79,
+/* 7660 */ 5, 129, 30, 16, 80, 4, 129, 30, 14, 80,
+/* 7670 */ 4, 129, 30, 12, 129, 30, 10, 129, 30, 7,
+/* 7680 */ 129, 30, 5, 129, 30, 4, 130, 193, 129, 30,
+/* 7690 */ 4, 117, 4, 130, 30, 91, 136, 30, 4, 130,
+/* 7700 */ 193, 129, 30, 4, 80, 4, 130, 30, 54, 136,
+/* 7710 */ 30, 4, 72, 5, 129, 30, 4, 74, 5, 129,
+/* 7720 */ 75, 5, 129, 76, 5, 129, 30, 4, 75, 7,
+/* 7730 */ 129, 30, 4, 74, 9, 129, 30, 54, 132, 30,
+/* 7740 */ 53, 129, 30, 52, 129, 30, 51, 129, 30, 48,
+/* 7750 */ 129, 30, 4, 72, 5, 129, 30, 4, 74, 5,
+/* 7760 */ 129, 75, 5, 129, 76, 5, 129, 30, 4, 75,
+/* 7770 */ 7, 129, 30, 4, 74, 9, 129, 30, 54, 132,
+/* 7780 */ 30, 53, 129, 30, 52, 129, 30, 51, 129, 30,
+/* 7790 */ 48, 129, 30, 4, 130, 193, 129, 30, 4, 80,
+/* 7800 */ 4, 130, 30, 54, 136, 30, 4, 72, 5, 129,
+/* 7810 */ 30, 4, 74, 5, 129, 75, 5, 129, 76, 5,
+/* 7820 */ 129, 76, 6, 129, 77, 6, 130, 77, 7, 130,
+/* 7830 */ 76, 8, 129, 30, 4, 75, 9, 129, 30, 4,
+/* 7840 */ 72, 12, 129, 30, 54, 129, 30, 53, 130, 30,
+/* 7850 */ 52, 129, 30, 51, 129, 30, 49, 129, 30, 46,
+/* 7860 */ 129, 30, 42, 129, 30, 4, 130, 193, 129, 48,
+/* 7870 */ 18, 129, 43, 28, 129, 41, 32, 129, 39, 36,
+/* 7880 */ 129, 37, 40, 129, 35, 44, 129, 34, 46, 129,
+/* 7890 */ 33, 13, 68, 13, 129, 32, 9, 73, 9, 129,
+/* 7900 */ 32, 7, 75, 7, 129, 31, 6, 77, 6, 129,
+/* 7910 */ 31, 5, 78, 5, 129, 30, 5, 79, 5, 129,
+/* 7920 */ 30, 4, 80, 4, 132, 30, 5, 79, 5, 130,
+/* 7930 */ 31, 5, 78, 5, 129, 31, 6, 77, 6, 129,
+/* 7940 */ 32, 7, 75, 7, 129, 32, 9, 73, 9, 129,
+/* 7950 */ 33, 13, 68, 13, 129, 34, 46, 129, 35, 44,
+/* 7960 */ 129, 37, 40, 129, 39, 36, 129, 41, 32, 129,
+/* 7970 */ 43, 28, 129, 48, 18, 129, 193, 129, 1, 3,
+/* 7980 */ 80, 4, 130, 1, 83, 137, 37, 5, 72, 5,
+/* 7990 */ 129, 35, 5, 74, 5, 129, 33, 5, 76, 5,
+/* 8000 */ 129, 32, 5, 77, 5, 129, 31, 5, 78, 5,
+/* 8010 */ 129, 31, 4, 79, 4, 129, 30, 5, 79, 5,
+/* 8020 */ 131, 30, 6, 78, 6, 129, 30, 7, 77, 7,
+/* 8030 */ 129, 31, 8, 75, 8, 129, 31, 11, 72, 11,
+/* 8040 */ 129, 32, 15, 67, 15, 129, 33, 48, 129, 34,
+/* 8050 */ 46, 129, 35, 44, 129, 37, 40, 129, 39, 36,
+/* 8060 */ 129, 42, 30, 129, 46, 22, 129, 193, 129, 46,
+/* 8070 */ 22, 129, 42, 30, 129, 39, 36, 129, 37, 40,
+/* 8080 */ 129, 35, 44, 129, 34, 46, 129, 33, 48, 129,
+/* 8090 */ 32, 15, 67, 15, 129, 31, 11, 72, 11, 129,
+/* 8100 */ 31, 8, 75, 8, 129, 30, 7, 77, 7, 129,
+/* 8110 */ 30, 6, 78, 6, 129, 30, 5, 79, 5, 131,
+/* 8120 */ 31, 4, 79, 4, 129, 31, 5, 78, 5, 129,
+/* 8130 */ 32, 5, 77, 5, 129, 33, 5, 76, 5, 129,
+/* 8140 */ 35, 5, 74, 5, 129, 37, 5, 72, 5, 129,
+/* 8150 */ 1, 83, 136, 1, 3, 80, 4, 130, 193, 129,
+/* 8160 */ 30, 4, 80, 4, 130, 30, 54, 136, 30, 4,
+/* 8170 */ 68, 6, 129, 30, 4, 70, 6, 129, 71, 7,
+/* 8180 */ 129, 72, 7, 129, 73, 7, 129, 74, 7, 129,
+/* 8190 */ 74, 8, 129, 75, 8, 130, 69, 15, 129, 67,
+/* 8200 */ 17, 129, 66, 18, 129, 65, 19, 130, 65, 18,
+/* 8210 */ 130, 66, 16, 129, 67, 13, 129, 69, 8, 129,
+/* 8220 */ 193, 129, 30, 13, 64, 8, 129, 30, 13, 61,
+/* 8230 */ 14, 129, 30, 13, 59, 18, 129, 30, 13, 57,
+/* 8240 */ 22, 129, 33, 8, 56, 24, 129, 32, 7, 55,
+/* 8250 */ 26, 129, 32, 6, 54, 28, 129, 31, 6, 53,
+/* 8260 */ 16, 77, 6, 129, 31, 5, 53, 14, 79, 4,
+/* 8270 */ 129, 30, 5, 52, 14, 80, 4, 129, 30, 5,
+/* 8280 */ 52, 13, 80, 4, 129, 30, 4, 52, 13, 80,
+/* 8290 */ 4, 129, 30, 4, 52, 12, 80, 4, 129, 30,
+/* 8300 */ 4, 51, 13, 80, 4, 130, 30, 4, 50, 13,
+/* 8310 */ 79, 5, 129, 30, 4, 50, 13, 78, 5, 129,
+/* 8320 */ 30, 5, 49, 14, 77, 6, 129, 31, 4, 49,
+/* 8330 */ 13, 76, 6, 129, 31, 5, 48, 14, 75, 7,
+/* 8340 */ 129, 32, 5, 47, 14, 73, 8, 129, 32, 6,
+/* 8350 */ 45, 16, 71, 13, 129, 33, 27, 71, 13, 129,
+/* 8360 */ 34, 26, 71, 13, 129, 35, 24, 71, 13, 129,
+/* 8370 */ 37, 20, 129, 39, 16, 129, 43, 9, 129, 193,
+/* 8380 */ 129, 80, 4, 131, 41, 56, 129, 37, 60, 129,
+/* 8390 */ 35, 62, 129, 33, 64, 129, 32, 65, 129, 31,
+/* 8400 */ 66, 129, 30, 67, 130, 30, 11, 80, 4, 129,
+/* 8410 */ 30, 9, 80, 4, 129, 30, 8, 80, 4, 129,
+/* 8420 */ 31, 7, 80, 4, 129, 31, 6, 129, 32, 5,
+/* 8430 */ 129, 33, 5, 129, 35, 4, 129, 38, 3, 129,
+/* 8440 */ 193, 129, 80, 4, 130, 42, 42, 129, 38, 46,
+/* 8450 */ 129, 35, 49, 129, 33, 51, 129, 32, 52, 129,
+/* 8460 */ 31, 53, 130, 30, 54, 129, 30, 12, 129, 30,
+/* 8470 */ 9, 129, 30, 8, 129, 30, 7, 130, 31, 6,
+/* 8480 */ 130, 32, 6, 129, 33, 5, 129, 34, 5, 129,
+/* 8490 */ 35, 5, 80, 4, 129, 37, 5, 80, 4, 129,
+/* 8500 */ 30, 54, 136, 30, 4, 130, 193, 129, 80, 4,
+/* 8510 */ 130, 77, 7, 129, 74, 10, 129, 70, 14, 129,
+/* 8520 */ 66, 18, 129, 62, 22, 129, 59, 25, 129, 55,
+/* 8530 */ 29, 129, 51, 33, 129, 47, 37, 129, 44, 32,
+/* 8540 */ 80, 4, 129, 40, 32, 80, 4, 129, 36, 32,
+/* 8550 */ 129, 32, 33, 129, 30, 31, 129, 33, 24, 129,
+/* 8560 */ 36, 17, 129, 40, 12, 129, 44, 12, 129, 48,
+/* 8570 */ 12, 129, 51, 13, 129, 55, 13, 129, 59, 13,
+/* 8580 */ 80, 4, 129, 63, 13, 80, 4, 129, 67, 17,
+/* 8590 */ 129, 71, 13, 129, 74, 10, 129, 78, 6, 129,
+/* 8600 */ 80, 4, 131, 193, 129, 80, 4, 130, 77, 7,
+/* 8610 */ 129, 74, 10, 129, 70, 14, 129, 66, 18, 129,
+/* 8620 */ 62, 22, 129, 59, 25, 129, 55, 29, 129, 51,
+/* 8630 */ 33, 129, 47, 37, 129, 44, 32, 80, 4, 129,
+/* 8640 */ 40, 32, 80, 4, 129, 36, 32, 129, 32, 33,
+/* 8650 */ 129, 30, 31, 129, 33, 24, 129, 36, 17, 129,
+/* 8660 */ 40, 12, 129, 44, 12, 129, 47, 13, 129, 44,
+/* 8670 */ 20, 129, 40, 28, 129, 36, 31, 129, 32, 32,
+/* 8680 */ 129, 30, 30, 129, 33, 24, 129, 36, 17, 129,
+/* 8690 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51,
+/* 8700 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129,
+/* 8710 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13,
+/* 8720 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131,
+/* 8730 */ 193, 129, 30, 4, 80, 4, 130, 30, 4, 79,
+/* 8740 */ 5, 129, 30, 5, 77, 7, 129, 30, 6, 74,
+/* 8750 */ 10, 129, 30, 8, 72, 12, 129, 30, 11, 69,
+/* 8760 */ 15, 129, 30, 13, 67, 17, 129, 30, 4, 37,
+/* 8770 */ 8, 64, 20, 129, 30, 4, 39, 8, 62, 22,
+/* 8780 */ 129, 41, 8, 59, 25, 129, 43, 8, 57, 27,
+/* 8790 */ 129, 45, 8, 55, 22, 80, 4, 129, 47, 27,
+/* 8800 */ 80, 4, 129, 49, 23, 129, 47, 22, 129, 44,
+/* 8810 */ 23, 129, 42, 22, 129, 30, 4, 39, 27, 129,
+/* 8820 */ 30, 4, 37, 31, 129, 30, 27, 62, 8, 129,
+/* 8830 */ 30, 25, 64, 8, 129, 30, 22, 66, 8, 80,
+/* 8840 */ 4, 129, 30, 20, 68, 8, 80, 4, 129, 30,
+/* 8850 */ 17, 70, 8, 80, 4, 129, 30, 15, 73, 11,
+/* 8860 */ 129, 30, 12, 75, 9, 129, 30, 10, 77, 7,
+/* 8870 */ 129, 30, 7, 79, 5, 129, 30, 5, 80, 4,
+/* 8880 */ 129, 30, 4, 80, 4, 130, 193, 129, 4, 5,
+/* 8890 */ 80, 4, 129, 2, 9, 80, 4, 129, 1, 11,
+/* 8900 */ 77, 7, 129, 1, 12, 74, 10, 129, 1, 12,
+/* 8910 */ 70, 14, 129, 1, 12, 66, 18, 129, 1, 11,
+/* 8920 */ 62, 22, 129, 2, 9, 59, 25, 129, 4, 11,
+/* 8930 */ 55, 29, 129, 7, 12, 51, 33, 129, 10, 12,
+/* 8940 */ 47, 37, 129, 14, 12, 44, 32, 80, 4, 129,
+/* 8950 */ 17, 13, 40, 32, 80, 4, 129, 21, 13, 36,
+/* 8960 */ 32, 129, 25, 40, 129, 29, 32, 129, 33, 24,
+/* 8970 */ 129, 36, 17, 129, 40, 12, 129, 44, 12, 129,
+/* 8980 */ 48, 12, 129, 51, 13, 129, 55, 13, 129, 59,
+/* 8990 */ 13, 80, 4, 129, 63, 13, 80, 4, 129, 67,
+/* 9000 */ 17, 129, 71, 13, 129, 74, 10, 129, 78, 6,
+/* 9010 */ 129, 80, 4, 131, 193, 129, 30, 1, 71, 13,
+/* 9020 */ 129, 30, 3, 71, 13, 129, 30, 6, 71, 13,
+/* 9030 */ 129, 30, 9, 75, 9, 129, 30, 11, 77, 7,
+/* 9040 */ 129, 30, 14, 79, 5, 129, 30, 17, 79, 5,
+/* 9050 */ 129, 30, 19, 80, 4, 129, 30, 22, 80, 4,
+/* 9060 */ 129, 30, 25, 80, 4, 129, 30, 27, 80, 4,
+/* 9070 */ 129, 30, 4, 36, 24, 80, 4, 129, 30, 4,
+/* 9080 */ 38, 25, 80, 4, 129, 30, 4, 41, 24, 80,
+/* 9090 */ 4, 129, 30, 4, 44, 24, 80, 4, 129, 30,
+/* 9100 */ 4, 46, 25, 80, 4, 129, 30, 4, 49, 25,
+/* 9110 */ 80, 4, 129, 30, 4, 52, 24, 80, 4, 129,
+/* 9120 */ 30, 4, 54, 30, 129, 30, 4, 57, 27, 129,
+/* 9130 */ 30, 4, 59, 25, 129, 30, 4, 62, 22, 129,
+/* 9140 */ 30, 4, 65, 19, 129, 30, 5, 67, 17, 129,
+/* 9150 */ 30, 5, 70, 14, 129, 30, 7, 73, 11, 129,
+/* 9160 */ 30, 9, 76, 8, 129, 30, 13, 78, 6, 129,
+/* 9170 */ 30, 13, 81, 3, 129, 30, 13, 129, 193, 2,
+/* 9180 */ 9, 59, 25, 129, 4, 11, 55, 29, 129, 7,
+/* 9190 */ 12, 51, 33, 129, 10, 12, 47, 37, 129, 14,
+/* 9200 */ 12, 44, 32, 80, 4, 129, 17, 13, 40, 32,
+/* 9210 */ 80, 4, 129, 21, 13, 36, 32, 129, 25, 40,
+/* 9220 */ 129, 29, 32, 129, 33, 24, 129, 36, 17, 129,
+/* 9230 */ 40, 12, 129, 44, 12, 129, 48, 12, 129, 51,
+/* 9240 */ 13, 129, 55, 13, 129, 59, 13, 80, 4, 129,
+/* 9250 */ 63, 13, 80, 4, 129, 67, 17, 129, 71, 13,
+/* 9260 */ 129, 74, 10, 129, 78, 6, 129, 80, 4, 131,
+/* 9270 */ 193
+};
+
+char line[DWIDTH];
+char message[MAXMSG];
+char print[DWIDTH];
+int debug, i, j, linen, max, nchars, pc, term, trace, x, y;
+int width = DWIDTH; /* -w option: scrunch letters to 80 columns */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "w:td")) != EOF)
+ switch(ch) {
+ case 'w':
+ width = atoi(optarg);
+ if (width <= 0)
+ width = 80;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 't':
+ trace = 1;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "usage: banner [-w width]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ for (i = 0; i < width; i++) {
+ j = i * 132 / width;
+ print[j] = 1;
+ }
+
+ /* Have now read in the data. Next get the message to be printed. */
+ if (*argv) {
+ strcpy(message, *argv);
+ while (*++argv) {
+ strcat(message, " ");
+ strcat(message, *argv);
+ }
+ nchars = strlen(message);
+ } else {
+ fprintf(stderr,"Message: ");
+ (void)fgets(message, sizeof(message), stdin);
+ nchars = strlen(message);
+ message[nchars--] = '\0'; /* get rid of newline */
+ }
+
+ /* some debugging print statements */
+ if (debug) {
+ printf("int asc_ptr[128] = {\n");
+ for (i = 0; i < 128; i++) {
+ printf("%4d, ",asc_ptr[i]);
+ if ((i+1) % 8 == 0)
+ printf("\n");
+ }
+ printf("};\nchar data_table[NBYTES] = {\n");
+ printf(" /* ");
+ for (i = 0; i < 10; i++) printf(" %3d ",i);
+ printf(" */\n");
+ for (i = 0; i < NBYTES; i += 10) {
+ printf("/* %4d */ ",i);
+ for (j = i; j < i+10; j++) {
+ x = data_table[j] & 0377;
+ printf(" %3d, ",x);
+ }
+ putchar('\n');
+ }
+ printf("};\n");
+ }
+
+ /* check message to make sure it's legal */
+ j = 0;
+ for (i = 0; i < nchars; i++)
+ if ((u_char) message[i] >= NCHARS ||
+ asc_ptr[(u_char) message[i]] == 0) {
+ warnx("The character '%c' is not in my character set",
+ message[i]);
+ j++;
+ }
+ if (j)
+ exit(1);
+
+ if (trace)
+ printf("Message '%s' is OK\n",message);
+ /* Now have message. Print it one character at a time. */
+
+ for (i = 0; i < nchars; i++) {
+ if (trace)
+ printf("Char #%d: %c\n", i, message[i]);
+ for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+ pc = asc_ptr[(u_char) message[i]];
+ term = 0;
+ max = 0;
+ linen = 0;
+ while (!term) {
+ if (pc < 0 || pc > NBYTES) {
+ printf("bad pc: %d\n",pc);
+ exit(1);
+ }
+ x = data_table[pc] & 0377;
+ if (trace)
+ printf("pc=%d, term=%d, max=%d, linen=%d, x=%d\n",pc,term,max,linen,x);
+ if (x >= 128) {
+ if (x>192) term++;
+ x = x & 63;
+ while (x--) {
+ if (print[linen++]) {
+ for (j=0; j <= max; j++)
+ if (print[j])
+ putchar(line[j]);
+ putchar('\n');
+ }
+ }
+ for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+ pc++;
+ }
+ else {
+ y = data_table[pc+1];
+ /* compensate for narrow teminals */
+#ifdef notdef
+ x = (x*width + (DWIDTH/2)) / DWIDTH;
+ y = (y*width + (DWIDTH/2)) / DWIDTH;
+#endif
+ max = x+y;
+ while (x < max) line[x++] = '#';
+ pc += 2;
+ if (trace)
+ printf("x=%d, y=%d, max=%d\n",x,y,max);
+ }
+ }
+ }
+
+ exit(0);
+}
diff --git a/usr.bin/basename/Makefile b/usr.bin/basename/Makefile
new file mode 100644
index 0000000..21354bd
--- /dev/null
+++ b/usr.bin/basename/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= basename
+MLINKS= basename.1 dirname.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/basename/basename.1 b/usr.bin/basename/basename.1
new file mode 100644
index 0000000..be0963c
--- /dev/null
+++ b/usr.bin/basename/basename.1
@@ -0,0 +1,97 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)basename.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt BASENAME 1
+.Os
+.Sh NAME
+.Nm basename , dirname
+.Nd return filename or directory portion of pathname
+.Sh SYNOPSIS
+.Nm basename
+.Ar string
+.Op Ar suffix
+.Nm dirname
+.Ar string
+.Sh DESCRIPTION
+.Nm Basename
+deletes any prefix ending with the last slash
+.Ql \&/
+character present in
+.Ar string ,
+and a
+.Ar suffix ,
+if given.
+The resulting filename is written to the standard output.
+If
+.Ar string
+ends in the slash character,
+.Ql / ,
+or is the same as the
+.Ar suffix
+argument,
+a newline is output.
+A non-existent suffix is ignored.
+.Pp
+.Nm Dirname
+deletes the filename portion, beginning
+with the last slash
+.Ql \&/
+character to the end of
+.Ar string ,
+and writes the result to the standard output.
+.Sh EXAMPLES
+The following line sets the shell variable
+.Ev FOO
+to
+.Pa /usr/bin .
+.Pp
+.Dl FOO=`dirname /usr/bin/trail`
+.Pp
+Both the
+.Nm basename
+and
+.Nm dirname
+exit 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr csh 1
+.Xr sh 1
+.Sh STANDARDS
+The
+.Nm basename
+and
+.Nm dirname
+functions are expected to be POSIX 1003.2 compatible.
diff --git a/usr.bin/basename/basename.c b/usr.bin/basename/basename.c
new file mode 100644
index 0000000..b369190
--- /dev/null
+++ b/usr.bin/basename/basename.c
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)basename.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1 && argc != 2)
+ usage();
+
+ /*
+ * (1) If string is // it is implementation defined whether steps (2)
+ * through (5) are skipped or processed.
+ *
+ * (2) If string consists entirely of slash characters, string shall
+ * be set to a single slash character. In this case, skip steps
+ * (3) through (5).
+ */
+ for (p = *argv;; ++p) {
+ if (!*p) {
+ if (p > *argv)
+ (void)printf("/\n");
+ else
+ (void)printf("\n");
+ exit(0);
+ }
+ if (*p != '/')
+ break;
+ }
+
+ /*
+ * (3) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ for (; *p; ++p)
+ continue;
+ while (*--p == '/')
+ continue;
+ *++p = '\0';
+
+ /*
+ * (4) If there are any slash characters remaining in string, the
+ * prefix of string up to an including the last slash character
+ * in string shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p == '/')
+ break;
+ ++p;
+
+ /*
+ * (5) If the suffix operand is present, is not identical to the
+ * characters remaining in string, and is identical to a suffix
+ * of the characters remaining in string, the suffix suffix
+ * shall be removed from string.
+ */
+ if (*++argv) {
+ int suffixlen, stringlen, off;
+
+ suffixlen = strlen(*argv);
+ stringlen = strlen(p);
+
+ if (suffixlen < stringlen) {
+ off = stringlen - suffixlen;
+ if (!strcmp(p + off, *argv))
+ p[off] = '\0';
+ }
+ }
+ (void)printf("%s\n", p);
+ exit(0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: basename string [suffix]\n");
+ exit(1);
+}
diff --git a/usr.bin/bdes/Makefile b/usr.bin/bdes/Makefile
new file mode 100644
index 0000000..9166f72
--- /dev/null
+++ b/usr.bin/bdes/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= bdes
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/bdes/bdes.1 b/usr.bin/bdes/bdes.1
new file mode 100644
index 0000000..eecd81b
--- /dev/null
+++ b/usr.bin/bdes/bdes.1
@@ -0,0 +1,304 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Matt Bishop of Dartmouth College.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)bdes.1 8.1 (Berkeley) 6/29/93
+.\"
+.TH BDES 1 "June 29, 1993"
+.UC 6
+.SH NAME
+bdes \- encrypt/decrypt using the Data Encryption Standard
+.SH SYNOPSIS
+.nf
+.ft B
+bdes [ \-abdp ] [ \-F N ] [ \-f N ] [ \-k key ]
+.ti +5
+[ \-m N ] [ \-o N ] [ \-v vector ]
+.ft R
+.fi
+.SH DESCRIPTION
+.I Bdes
+implements all DES modes of operation described in FIPS PUB 81,
+including alternative cipher feedback mode and both authentication
+modes.
+.I Bdes
+reads from the standard input and writes to the standard output.
+By default, the input is encrypted using cipher block chaining mode.
+Using the same key for encryption and decryption preserves plain text.
+.PP
+All modes but the electronic code book mode require an initialization
+vector; if none is supplied, the zero vector is used.
+If no
+.I key
+is specified on the command line, the user is prompted for one (see
+.IR getpass (3)
+for more details).
+.PP
+The options are as follows:
+.TP
+\-a
+The key and initialization vector strings are to be taken as ASCII,
+suppressing the special interpretation given to leading ``0X'', ``0x'',
+``0B'', and ``0b'' characters.
+This flag applies to
+.I both
+the key and initialization vector.
+.TP
+\-b
+Use electronic code book mode.
+.TP
+\-d
+Decrypt the input.
+.TP
+\-F
+Use
+.IR N -bit
+alternative cipher feedback mode.
+Currently
+.I N
+must be a multiple of 7 between 7 and 56 inclusive (this does not conform
+to the alternative CFB mode specification).
+.TP
+\-f
+Use
+.IR N -bit
+cipher feedback mode.
+Currently
+.I N
+must be a multiple of 8 between 8 and 64 inclusive (this does not conform
+to the standard CFB mode specification).
+.TP
+\-k
+Use
+.I key
+as the cryptographic key.
+.TP
+\-m
+Compute a message authentication code (MAC) of
+.I N
+bits on the input.
+The value of
+.I N
+must be between 1 and 64 inclusive; if
+.I N
+is not a multiple of 8, enough 0 bits will be added to pad the MAC length
+to the nearest multiple of 8.
+Only the MAC is output.
+MACs are only available in cipher block chaining mode or in cipher feedback
+mode.
+.TP
+\-o
+Use
+.IR N -bit
+output feedback mode.
+Currently
+.I N
+must be a multiple of 8 between 8 and 64 inclusive (this does not conform
+to the OFB mode specification).
+.TP
+\-p
+Disable the resetting of the parity bit.
+This flag forces the parity bit of the key to be used as typed, rather than
+making each character be of odd parity.
+It is used only if the key is given in ASCII.
+.TP
+\-v
+Set the initialization vector to
+.IR vector ;
+the vector is interpreted in the same way as the key.
+The vector is ignored in electronic codebook mode.
+.PP
+The key and initialization vector are taken as sequences of ASCII
+characters which are then mapped into their bit representations.
+If either begins with ``0X'' or ``0x'',
+that one is taken as a sequence of hexadecimal digits indicating the
+bit pattern;
+if either begins with ``0B'' or ``0b'',
+that one is taken as a sequence of binary digits indicating the bit pattern.
+In either case,
+only the leading 64 bits of the key or initialization vector
+are used,
+and if fewer than 64 bits are provided, enough 0 bits are appended
+to pad the key to 64 bits.
+.PP
+According to the DES standard, the low-order bit of each character in the
+key string is deleted.
+Since most ASCII representations set the high-order bit to 0, simply
+deleting the low-order bit effectively reduces the size of the key space
+from 2\u\s-356\s0\d to 2\u\s-348\s0\d keys.
+To prevent this, the high-order bit must be a function depending in part
+upon the low-order bit; so, the high-order bit is set to whatever value
+gives odd parity.
+This preserves the key space size.
+Note this resetting of the parity bit is
+.I not
+done if the key is given in binary or hex, and can be disabled for ASCII
+keys as well.
+.PP
+The DES is considered a very strong cryptosystem, and other than table lookup
+attacks, key search attacks, and Hellman's time-memory tradeoff (all of which
+are very expensive and time-consuming), no cryptanalytic methods for breaking
+the DES are known in the open literature.
+No doubt the choice of keys and key security are the most vulnerable aspect
+of
+.IR bdes .
+.SH IMPLEMENTATION NOTES
+For implementors wishing to write software compatible with this program,
+the following notes are provided.
+This software is believed to be compatible with the implementation of the
+data encryption standard distributed by Sun Microsystems, Inc.
+.PP
+In the ECB and CBC modes, plaintext is encrypted in units of 64 bits (8 bytes,
+also called a block).
+To ensure that the plaintext file is encrypted correctly,
+.I bdes
+will (internally) append from 1 to 8 bytes, the last byte containing an
+integer stating how many bytes of that final block are from the plaintext
+file, and encrypt the resulting block.
+Hence, when decrypting, the last block may contain from 0 to 7 characters
+present in the plaintext file, and the last byte tells how many.
+Note that if during decryption the last byte of the file does not contain an
+integer between 0 and 7, either the file has been corrupted or an incorrect
+key has been given.
+A similar mechanism is used for the OFB and CFB modes, except that those
+simply require the length of the input to be a multiple of the mode size,
+and the final byte contains an integer between 0 and one less than the number
+of bytes being used as the mode.
+(This was another reason that the mode size must be a multiple of 8 for those
+modes.)
+.PP
+Unlike Sun's implementation, unused bytes of that last block are not filled
+with random data, but instead contain what was in those byte positions in
+the preceding block.
+This is quicker and more portable, and does not weaken the encryption
+significantly.
+.PP
+If the key is entered in ASCII, the parity bits of the key characters are set
+so that each key character is of odd parity.
+Unlike Sun's implementation, it is possible to enter binary or hexadecimal
+keys on the command line, and if this is done, the parity bits are
+.I not
+reset.
+This allows testing using arbitrary bit patterns as keys.
+.PP
+The Sun implementation always uses an initialization vector of 0
+(that is, all zeroes).
+By default,
+.I bdes
+does too, but this may be changed from the command line.
+.SH SEE ALSO
+crypt(1), crypt(3), getpass(3)
+.sp
+.IR "Data Encryption Standard" ,
+Federal Information Processing Standard #46,
+National Bureau of Standards,
+U.S. Department of Commerce,
+Washington DC
+(Jan. 1977)
+.sp
+.IR "DES Modes of Operation" ,
+Federal Information Processing Standard #81,
+National Bureau of Standards,
+U.S. Department of Commerce
+Washington DC
+(Dec. 1980)
+.sp
+Dorothy Denning,
+.IR "Cryptography and Data Security" ,
+Addison-Wesley Publishing Co.,
+Reading, MA
+\(co1982.
+.sp
+Matt Bishop,
+.IR "Implementation Notes on bdes(1)" ,
+Technical Report PCS-TR-91-158,
+Department of Mathematics and Computer Science,
+Dartmouth College,
+Hanover, NH 03755
+(Apr. 1991).
+.SH DISCLAIMER
+.nf
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+.fi
+.SH BUGS
+There is a controversy raging over whether the DES will still be secure
+in a few years.
+The advent of special-purpose hardware could reduce the cost of any of the
+methods of attack named above so that they are no longer computationally
+infeasible.
+.PP
+As the key or key schedule is stored in memory, the encryption can be
+compromised if memory is readable.
+Additionally, programs which display programs' arguments may compromise the
+key and initialization vector, if they are specified on the command line.
+To avoid this
+.I bdes
+overwrites its arguments, however, the obvious race cannot currently be
+avoided.
+.PP
+Certain specific keys should be avoided because they introduce potential
+weaknesses; these keys, called the
+.I weak
+and
+.I semiweak
+keys, are (in hex notation, where p is either 0 or 1, and P is either
+e or f):
+.sp
+.nf
+.in +10n
+.ta \w'0x0p0p0p0p0p0p0p0p\0\0\0'u+5n
+0x0p0p0p0p0p0p0p0p 0x0p1P0p1P0p0P0p0P
+0x0pep0pep0pfp0pfp 0x0pfP0pfP0pfP0pfP
+0x1P0p1P0p0P0p0P0p 0x1P1P1P1P0P0P0P0P
+0x1Pep1Pep0Pfp0Pfp 0x1PfP1PfP0PfP0PfP
+0xep0pep0pfp0pfp0p 0xep1Pep1pfp0Pfp0P
+0xepepepepepepepep 0xepfPepfPfpfPfpfP
+0xfP0pfP0pfP0pfP0p 0xfP1PfP1PfP0PfP0P
+0xfPepfPepfPepfPep 0xfPfPfPfPfPfPfPfP
+.fi
+.in -10n
+.sp
+This is inherent in the DES algorithm (see Moore and Simmons,
+\*(LqCycle structure of the DES with weak and semi-weak keys,\*(Rq
+.I "Advances in Cryptology \- Crypto '86 Proceedings" ,
+Springer-Verlag New York, \(co1987, pp. 9-32.)
diff --git a/usr.bin/bdes/bdes.c b/usr.bin/bdes/bdes.c
new file mode 100644
index 0000000..f702e01
--- /dev/null
+++ b/usr.bin/bdes/bdes.c
@@ -0,0 +1,1046 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Matt Bishop of Dartmouth College.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. NAG 2-680 between the National Aeronautics and
+ * Space Administration and Dartmouth College.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)bdes.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * BDES -- DES encryption package for Berkeley Software Distribution 4.4
+ * options:
+ * -a key is in ASCII
+ * -b use ECB (electronic code book) mode
+ * -d invert (decrypt) input
+ * -f b use b-bit CFB (cipher feedback) mode
+ * -F b use b-bit CFB (cipher feedback) alternative mode
+ * -k key use key as the cryptographic key
+ * -m b generate a MAC of length b
+ * -o b use b-bit OFB (output feedback) mode
+ * -p don't reset the parity bit
+ * -v v use v as the initialization vector (ignored for ECB)
+ * note: the last character of the last block is the integer indicating
+ * how many characters of that block are to be output
+ *
+ * Author: Matt Bishop
+ * Department of Mathematics and Computer Science
+ * Dartmouth College
+ * Hanover, NH 03755
+ * Email: Matt.Bishop@dartmouth.edu
+ * ...!decvax!dartvax!Matt.Bishop
+ *
+ * See Technical Report PCS-TR91-158, Department of Mathematics and Computer
+ * Science, Dartmouth College, for a detailed description of the implemen-
+ * tation and differences between it and Sun's. The DES is described in
+ * FIPS PUB 46, and the modes in FIPS PUB 81 (see either the manual page
+ * or the technical report for a complete reference).
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * BSD and System V systems offer special library calls that do
+ * block moves and fills, so if possible we take advantage of them
+ */
+#define MEMCPY(dest,src,len) bcopy((src),(dest),(len))
+#define MEMZERO(dest,len) bzero((dest),(len))
+
+/* Hide the calls to the primitive encryption routines. */
+#define FASTWAY
+#ifdef FASTWAY
+#define DES_KEY(buf) \
+ if (des_setkey(buf)) \
+ err("des_setkey", 0);
+#define DES_XFORM(buf) \
+ if (des_cipher(buf, buf, 0L, (inverse ? -1 : 1))) \
+ err("des_cipher", 0);
+#else
+#define DES_KEY(buf) { \
+ char bits1[64]; /* bits of key */ \
+ expand(buf, bits1); \
+ if (setkey(bits1)) \
+ err("setkey", 0); \
+ }
+#define DES_XFORM(buf) { \
+ char bits1[64]; /* bits of message */ \
+ expand(buf, bits1); \
+ if (encrypt(bits1, inverse)) \
+ err("encrypt", 0); \
+ compress(bits1, buf); \
+ }
+#endif
+
+/*
+ * this does an error-checking write
+ */
+#define READ(buf, n) fread(buf, sizeof(char), n, stdin)
+#define WRITE(buf,n) \
+ if (fwrite(buf, sizeof(char), n, stdout) != n) \
+ err(bn, NULL);
+
+/*
+ * some things to make references easier
+ */
+typedef char Desbuf[8];
+#define CHAR(x,i) (x[i])
+#define UCHAR(x,i) (x[i])
+#define BUFFER(x) (x)
+#define UBUFFER(x) (x)
+
+/*
+ * global variables and related macros
+ */
+#define KEY_DEFAULT 0 /* interpret radix of key from key */
+#define KEY_ASCII 1 /* key is in ASCII characters */
+int keybase = KEY_DEFAULT; /* how to interpret the key */
+
+enum { /* encrypt, decrypt, authenticate */
+ MODE_ENCRYPT, MODE_DECRYPT, MODE_AUTHENTICATE
+} mode = MODE_ENCRYPT;
+enum { /* ecb, cbc, cfb, cfba, ofb? */
+ ALG_ECB, ALG_CBC, ALG_CFB, ALG_OFB, ALG_CFBA
+} alg = ALG_CBC;
+
+Desbuf ivec; /* initialization vector */
+char bits[] = { /* used to extract bits from a char */
+ '\200', '\100', '\040', '\020', '\010', '\004', '\002', '\001'
+};
+int inverse; /* 0 to encrypt, 1 to decrypt */
+int macbits = -1; /* number of bits in authentication */
+int fbbits = -1; /* number of feedback bits */
+int pflag; /* 1 to preserve parity bits */
+
+main(ac, av)
+ int ac; /* arg count */
+ char **av; /* arg vector */
+{
+ extern int optind; /* option (argument) number */
+ extern char *optarg; /* argument to option if any */
+ register int i; /* counter in a for loop */
+ register char *p; /* used to obtain the key */
+ Desbuf msgbuf; /* I/O buffer */
+ int kflag; /* command-line encryptiooon key */
+ int argc; /* the real arg count */
+ char **argv; /* the real argument vector */
+
+ /*
+ * Hide the arguments from ps(1) by making private copies of them
+ * and clobbering the global (visible to ps(1)) ones.
+ */
+ argc = ac;
+ ac = 1;
+ argv = malloc((argc + 1) * sizeof(char *));
+ for (i = 0; i < argc; ++i) {
+ argv[i] = strdup(av[i]);
+ MEMZERO(av[i], strlen(av[i]));
+ }
+ argv[argc] = NULL;
+
+ /* initialize the initialization vctor */
+ MEMZERO(ivec, 8);
+
+ /* process the argument list */
+ kflag = 0;
+ while ((i = getopt(argc, argv, "abdF:f:k:m:o:pv:")) != EOF)
+ switch(i) {
+ case 'a': /* key is ASCII */
+ keybase = KEY_ASCII;
+ break;
+ case 'b': /* use ECB mode */
+ alg = ALG_ECB;
+ break;
+ case 'd': /* decrypt */
+ mode = MODE_DECRYPT;
+ break;
+ case 'F': /* use alternative CFB mode */
+ alg = ALG_CFBA;
+ if ((fbbits = setbits(optarg, 7)) > 56 || fbbits == 0)
+ err(-1, "-F: number must be 1-56 inclusive");
+ else if (fbbits == -1)
+ err(-1, "-F: number must be a multiple of 7");
+ break;
+ case 'f': /* use CFB mode */
+ alg = ALG_CFB;
+ if ((fbbits = setbits(optarg, 8)) > 64 || fbbits == 0)
+ err(-1, "-f: number must be 1-64 inclusive");
+ else if (fbbits == -1)
+ err(-1, "-f: number must be a multiple of 8");
+ break;
+ case 'k': /* encryption key */
+ kflag = 1;
+ cvtkey(BUFFER(msgbuf), optarg);
+ break;
+ case 'm': /* number of bits for MACing */
+ mode = MODE_AUTHENTICATE;
+ if ((macbits = setbits(optarg, 1)) > 64)
+ err(-1, "-m: number must be 0-64 inclusive");
+ break;
+ case 'o': /* use OFB mode */
+ alg = ALG_OFB;
+ if ((fbbits = setbits(optarg, 8)) > 64 || fbbits == 0)
+ err(-1, "-o: number must be 1-64 inclusive");
+ else if (fbbits == -1)
+ err(-1, "-o: number must be a multiple of 8");
+ break;
+ case 'p': /* preserve parity bits */
+ pflag = 1;
+ break;
+ case 'v': /* set initialization vector */
+ cvtkey(BUFFER(ivec), optarg);
+ break;
+ default: /* error */
+ usage();
+ }
+
+ if (!kflag) {
+ /*
+ * if the key's not ASCII, assume it is
+ */
+ keybase = KEY_ASCII;
+ /*
+ * get the key
+ */
+ p = getpass("Enter key: ");
+ /*
+ * copy it, nul-padded, into the key area
+ */
+ cvtkey(BUFFER(msgbuf), p);
+ }
+
+ makekey(msgbuf);
+ inverse = (alg == ALG_CBC || alg == ALG_ECB) && mode == MODE_DECRYPT;
+
+ switch(alg) {
+ case ALG_CBC:
+ switch(mode) {
+ case MODE_AUTHENTICATE: /* authenticate using CBC mode */
+ cbcauth();
+ break;
+ case MODE_DECRYPT: /* decrypt using CBC mode */
+ cbcdec();
+ break;
+ case MODE_ENCRYPT: /* encrypt using CBC mode */
+ cbcenc();
+ break;
+ }
+ break;
+ case ALG_CFB:
+ switch(mode) {
+ case MODE_AUTHENTICATE: /* authenticate using CFB mode */
+ cfbauth();
+ break;
+ case MODE_DECRYPT: /* decrypt using CFB mode */
+ cfbdec();
+ break;
+ case MODE_ENCRYPT: /* encrypt using CFB mode */
+ cfbenc();
+ break;
+ }
+ break;
+ case ALG_CFBA:
+ switch(mode) {
+ case MODE_AUTHENTICATE: /* authenticate using CFBA mode */
+ err(-1, "can't authenticate with CFBA mode");
+ break;
+ case MODE_DECRYPT: /* decrypt using CFBA mode */
+ cfbadec();
+ break;
+ case MODE_ENCRYPT: /* encrypt using CFBA mode */
+ cfbaenc();
+ break;
+ }
+ break;
+ case ALG_ECB:
+ switch(mode) {
+ case MODE_AUTHENTICATE: /* authenticate using ECB mode */
+ err(-1, "can't authenticate with ECB mode");
+ break;
+ case MODE_DECRYPT: /* decrypt using ECB mode */
+ ecbdec();
+ break;
+ case MODE_ENCRYPT: /* encrypt using ECB mode */
+ ecbenc();
+ break;
+ }
+ break;
+ case ALG_OFB:
+ switch(mode) {
+ case MODE_AUTHENTICATE: /* authenticate using OFB mode */
+ err(-1, "can't authenticate with OFB mode");
+ break;
+ case MODE_DECRYPT: /* decrypt using OFB mode */
+ ofbdec();
+ break;
+ case MODE_ENCRYPT: /* encrypt using OFB mode */
+ ofbenc();
+ break;
+ }
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * print a warning message and, possibly, terminate
+ */
+err(n, s)
+ int n; /* offending block number */
+ char *s; /* the message */
+{
+ if (n > 0)
+ (void)fprintf(stderr, "bdes (block %d): ", n);
+ else
+ (void)fprintf(stderr, "bdes: ");
+ (void)fprintf(stderr, "%s\n", s ? s : strerror(errno));
+ exit(1);
+}
+
+/*
+ * map a hex character to an integer
+ */
+tobinhex(c, radix)
+ char c; /* char to be converted */
+ int radix; /* base (2 to 16) */
+{
+ switch(c) {
+ case '0': return(0x0);
+ case '1': return(0x1);
+ case '2': return(radix > 2 ? 0x2 : -1);
+ case '3': return(radix > 3 ? 0x3 : -1);
+ case '4': return(radix > 4 ? 0x4 : -1);
+ case '5': return(radix > 5 ? 0x5 : -1);
+ case '6': return(radix > 6 ? 0x6 : -1);
+ case '7': return(radix > 7 ? 0x7 : -1);
+ case '8': return(radix > 8 ? 0x8 : -1);
+ case '9': return(radix > 9 ? 0x9 : -1);
+ case 'A': case 'a': return(radix > 10 ? 0xa : -1);
+ case 'B': case 'b': return(radix > 11 ? 0xb : -1);
+ case 'C': case 'c': return(radix > 12 ? 0xc : -1);
+ case 'D': case 'd': return(radix > 13 ? 0xd : -1);
+ case 'E': case 'e': return(radix > 14 ? 0xe : -1);
+ case 'F': case 'f': return(radix > 15 ? 0xf : -1);
+ }
+ /*
+ * invalid character
+ */
+ return(-1);
+}
+
+/*
+ * convert the key to a bit pattern
+ */
+cvtkey(obuf, ibuf)
+ char *obuf; /* bit pattern */
+ char *ibuf; /* the key itself */
+{
+ register int i, j; /* counter in a for loop */
+ int nbuf[64]; /* used for hex/key translation */
+
+ /*
+ * just switch on the key base
+ */
+ switch(keybase) {
+ case KEY_ASCII: /* ascii to integer */
+ (void)strncpy(obuf, ibuf, 8);
+ return;
+ case KEY_DEFAULT: /* tell from context */
+ /*
+ * leading '0x' or '0X' == hex key
+ */
+ if (ibuf[0] == '0' && (ibuf[1] == 'x' || ibuf[1] == 'X')) {
+ ibuf = &ibuf[2];
+ /*
+ * now translate it, bombing on any illegal hex digit
+ */
+ for (i = 0; ibuf[i] && i < 16; i++)
+ if ((nbuf[i] = tobinhex(ibuf[i], 16)) == -1)
+ err(-1, "bad hex digit in key");
+ while (i < 16)
+ nbuf[i++] = 0;
+ for (i = 0; i < 8; i++)
+ obuf[i] =
+ ((nbuf[2*i]&0xf)<<4) | (nbuf[2*i+1]&0xf);
+ /* preserve parity bits */
+ pflag = 1;
+ return;
+ }
+ /*
+ * leading '0b' or '0B' == binary key
+ */
+ if (ibuf[0] == '0' && (ibuf[1] == 'b' || ibuf[1] == 'B')) {
+ ibuf = &ibuf[2];
+ /*
+ * now translate it, bombing on any illegal binary digit
+ */
+ for (i = 0; ibuf[i] && i < 16; i++)
+ if ((nbuf[i] = tobinhex(ibuf[i], 2)) == -1)
+ err(-1, "bad binary digit in key");
+ while (i < 64)
+ nbuf[i++] = 0;
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ obuf[i] = (obuf[i]<<1)|nbuf[8*i+j];
+ /* preserve parity bits */
+ pflag = 1;
+ return;
+ }
+ /*
+ * no special leader -- ASCII
+ */
+ (void)strncpy(obuf, ibuf, 8);
+ }
+}
+
+/*
+ * convert an ASCII string into a decimal number:
+ * 1. must be between 0 and 64 inclusive
+ * 2. must be a valid decimal number
+ * 3. must be a multiple of mult
+ */
+setbits(s, mult)
+ char *s; /* the ASCII string */
+ int mult; /* what it must be a multiple of */
+{
+ register char *p; /* pointer in a for loop */
+ register int n = 0; /* the integer collected */
+
+ /*
+ * skip white space
+ */
+ while (isspace(*s))
+ s++;
+ /*
+ * get the integer
+ */
+ for (p = s; *p; p++) {
+ if (isdigit(*p))
+ n = n * 10 + *p - '0';
+ else {
+ err(-1, "bad decimal digit in MAC length");
+ }
+ }
+ /*
+ * be sure it's a multiple of mult
+ */
+ return((n % mult != 0) ? -1 : n);
+}
+
+/*****************
+ * DES FUNCTIONS *
+ *****************/
+/*
+ * This sets the DES key and (if you're using the deszip version)
+ * the direction of the transformation. This uses the Sun
+ * to map the 64-bit key onto the 56 bits that the key schedule
+ * generation routines use: the old way, which just uses the user-
+ * supplied 64 bits as is, and the new way, which resets the parity
+ * bit to be the same as the low-order bit in each character. The
+ * new way generates a greater variety of key schedules, since many
+ * systems set the parity (high) bit of each character to 0, and the
+ * DES ignores the low order bit of each character.
+ */
+makekey(buf)
+ Desbuf buf; /* key block */
+{
+ register int i, j; /* counter in a for loop */
+ register int par; /* parity counter */
+
+ /*
+ * if the parity is not preserved, flip it
+ */
+ if (!pflag) {
+ for (i = 0; i < 8; i++) {
+ par = 0;
+ for (j = 1; j < 8; j++)
+ if ((bits[j]&UCHAR(buf, i)) != 0)
+ par++;
+ if ((par&01) == 01)
+ UCHAR(buf, i) = UCHAR(buf, i)&0177;
+ else
+ UCHAR(buf, i) = (UCHAR(buf, i)&0177)|0200;
+ }
+ }
+
+ DES_KEY(UBUFFER(buf));
+}
+
+/*
+ * This encrypts using the Electronic Code Book mode of DES
+ */
+ecbenc()
+{
+ register int n; /* number of bytes actually read */
+ register int bn; /* block number */
+ Desbuf msgbuf; /* I/O buffer */
+
+ for (bn = 0; (n = READ(BUFFER(msgbuf), 8)) == 8; bn++) {
+ /*
+ * do the transformation
+ */
+ DES_XFORM(UBUFFER(msgbuf));
+ WRITE(BUFFER(msgbuf), 8);
+ }
+ /*
+ * at EOF or last block -- in either ase, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ bn++;
+ MEMZERO(&CHAR(msgbuf, n), 8 - n);
+ CHAR(msgbuf, 7) = n;
+ DES_XFORM(UBUFFER(msgbuf));
+ WRITE(BUFFER(msgbuf), 8);
+
+}
+
+/*
+ * This decrypts using the Electronic Code Book mode of DES
+ */
+ecbdec()
+{
+ register int n; /* number of bytes actually read */
+ register int c; /* used to test for EOF */
+ register int bn; /* block number */
+ Desbuf msgbuf; /* I/O buffer */
+
+ for (bn = 1; (n = READ(BUFFER(msgbuf), 8)) == 8; bn++) {
+ /*
+ * do the transformation
+ */
+ DES_XFORM(UBUFFER(msgbuf));
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = getchar()) == EOF) {
+ n = CHAR(msgbuf, 7);
+ if (n < 0 || n > 7)
+ err(bn, "decryption failed (block corrupted)");
+ }
+ else
+ (void)ungetc(c, stdin);
+ WRITE(BUFFER(msgbuf), n);
+ }
+ if (n > 0)
+ err(bn, "decryption failed (incomplete block)");
+}
+
+/*
+ * This encrypts using the Cipher Block Chaining mode of DES
+ */
+cbcenc()
+{
+ register int n; /* number of bytes actually read */
+ register int bn; /* block number */
+ Desbuf msgbuf; /* I/O buffer */
+
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(BUFFER(msgbuf), 8)) == 8; bn++) {
+ for (n = 0; n < 8; n++)
+ CHAR(msgbuf, n) ^= CHAR(ivec, n);
+ DES_XFORM(UBUFFER(msgbuf));
+ MEMCPY(BUFFER(ivec), BUFFER(msgbuf), 8);
+ WRITE(BUFFER(msgbuf), 8);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ bn++;
+ MEMZERO(&CHAR(msgbuf, n), 8 - n);
+ CHAR(msgbuf, 7) = n;
+ for (n = 0; n < 8; n++)
+ CHAR(msgbuf, n) ^= CHAR(ivec, n);
+ DES_XFORM(UBUFFER(msgbuf));
+ WRITE(BUFFER(msgbuf), 8);
+
+}
+
+/*
+ * This decrypts using the Cipher Block Chaining mode of DES
+ */
+cbcdec()
+{
+ register int n; /* number of bytes actually read */
+ Desbuf msgbuf; /* I/O buffer */
+ Desbuf ibuf; /* temp buffer for initialization vector */
+ register int c; /* used to test for EOF */
+ register int bn; /* block number */
+
+ for (bn = 0; (n = READ(BUFFER(msgbuf), 8)) == 8; bn++) {
+ /*
+ * do the transformation
+ */
+ MEMCPY(BUFFER(ibuf), BUFFER(msgbuf), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (c = 0; c < 8; c++)
+ UCHAR(msgbuf, c) ^= UCHAR(ivec, c);
+ MEMCPY(BUFFER(ivec), BUFFER(ibuf), 8);
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = getchar()) == EOF) {
+ n = CHAR(msgbuf, 7);
+ if (n < 0 || n > 7)
+ err(bn, "decryption failed (block corrupted)");
+ }
+ else
+ (void)ungetc(c, stdin);
+ WRITE(BUFFER(msgbuf), n);
+ }
+ if (n > 0)
+ err(bn, "decryption failed (incomplete block)");
+}
+
+/*
+ * This authenticates using the Cipher Block Chaining mode of DES
+ */
+cbcauth()
+{
+ register int n, j; /* number of bytes actually read */
+ Desbuf msgbuf; /* I/O buffer */
+ Desbuf encbuf; /* encryption buffer */
+
+ /*
+ * do the transformation
+ * note we DISCARD the encrypted block;
+ * we only care about the last one
+ */
+ while ((n = READ(BUFFER(msgbuf), 8)) == 8) {
+ for (n = 0; n < 8; n++)
+ CHAR(encbuf, n) = CHAR(msgbuf, n) ^ CHAR(ivec, n);
+ DES_XFORM(UBUFFER(encbuf));
+ MEMCPY(BUFFER(ivec), BUFFER(encbuf), 8);
+ }
+ /*
+ * now compute the last one, right padding with '\0' if need be
+ */
+ if (n > 0) {
+ MEMZERO(&CHAR(msgbuf, n), 8 - n);
+ for (n = 0; n < 8; n++)
+ CHAR(encbuf, n) = CHAR(msgbuf, n) ^ CHAR(ivec, n);
+ DES_XFORM(UBUFFER(encbuf));
+ }
+ /*
+ * drop the bits
+ * we write chars until fewer than 7 bits,
+ * and then pad the last one with 0 bits
+ */
+ for (n = 0; macbits > 7; n++, macbits -= 8)
+ (void)putchar(CHAR(encbuf, n));
+ if (macbits > 0) {
+ CHAR(msgbuf, 0) = 0x00;
+ for (j = 0; j < macbits; j++)
+ CHAR(msgbuf, 0) |= (CHAR(encbuf, n)&bits[j]);
+ (void)putchar(CHAR(msgbuf, 0));
+ }
+}
+
+/*
+ * This encrypts using the Cipher FeedBack mode of DES
+ */
+cfbenc()
+{
+ register int n; /* number of bytes actually read */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 8;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < 8 - nbytes; n++)
+ UCHAR(ivec, n) = UCHAR(ivec, n+nbytes);
+ for (n = 0; n < nbytes; n++)
+ UCHAR(ivec, 8-nbytes+n) = ibuf[n] ^ UCHAR(msgbuf, n);
+ WRITE(&CHAR(ivec, 8-nbytes), nbytes);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ bn++;
+ MEMZERO(&ibuf[n], nbytes - n);
+ ibuf[nbytes - 1] = n;
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < nbytes; n++)
+ ibuf[n] ^= UCHAR(msgbuf, n);
+ WRITE(ibuf, nbytes);
+}
+
+/*
+ * This decrypts using the Cipher Block Chaining mode of DES
+ */
+cfbdec()
+{
+ register int n; /* number of bytes actually read */
+ register int c; /* used to test for EOF */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ char obuf[8]; /* output buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 8;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (c = 0; c < 8 - nbytes; c++)
+ CHAR(ivec, c) = CHAR(ivec, c+nbytes);
+ for (c = 0; c < nbytes; c++) {
+ CHAR(ivec, 8-nbytes+c) = ibuf[c];
+ obuf[c] = ibuf[c] ^ UCHAR(msgbuf, c);
+ }
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = getchar()) == EOF) {
+ n = obuf[nbytes-1];
+ if (n < 0 || n > nbytes-1)
+ err(bn, "decryption failed (block corrupted)");
+ }
+ else
+ (void)ungetc(c, stdin);
+ WRITE(obuf, n);
+ }
+ if (n > 0)
+ err(bn, "decryption failed (incomplete block)");
+}
+
+/*
+ * This encrypts using the alternative Cipher FeedBack mode of DES
+ */
+cfbaenc()
+{
+ register int n; /* number of bytes actually read */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ char obuf[8]; /* output buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 7;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < 8 - nbytes; n++)
+ UCHAR(ivec, n) = UCHAR(ivec, n+nbytes);
+ for (n = 0; n < nbytes; n++)
+ UCHAR(ivec, 8-nbytes+n) = (ibuf[n] ^ UCHAR(msgbuf, n))
+ |0200;
+ for (n = 0; n < nbytes; n++)
+ obuf[n] = CHAR(ivec, 8-nbytes+n)&0177;
+ WRITE(obuf, nbytes);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ bn++;
+ MEMZERO(&ibuf[n], nbytes - n);
+ ibuf[nbytes - 1] = ('0' + n)|0200;
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < nbytes; n++)
+ ibuf[n] ^= UCHAR(msgbuf, n);
+ WRITE(ibuf, nbytes);
+}
+
+/*
+ * This decrypts using the alternative Cipher Block Chaining mode of DES
+ */
+cfbadec()
+{
+ register int n; /* number of bytes actually read */
+ register int c; /* used to test for EOF */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ char obuf[8]; /* output buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 7;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (c = 0; c < 8 - nbytes; c++)
+ CHAR(ivec, c) = CHAR(ivec, c+nbytes);
+ for (c = 0; c < nbytes; c++) {
+ CHAR(ivec, 8-nbytes+c) = ibuf[c]|0200;
+ obuf[c] = (ibuf[c] ^ UCHAR(msgbuf, c))&0177;
+ }
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = getchar()) == EOF) {
+ if ((n = (obuf[nbytes-1] - '0')) < 0
+ || n > nbytes-1)
+ err(bn, "decryption failed (block corrupted)");
+ }
+ else
+ (void)ungetc(c, stdin);
+ WRITE(obuf, n);
+ }
+ if (n > 0)
+ err(bn, "decryption failed (incomplete block)");
+}
+
+
+/*
+ * This encrypts using the Output FeedBack mode of DES
+ */
+ofbenc()
+{
+ register int n; /* number of bytes actually read */
+ register int c; /* used to test for EOF */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ char obuf[8]; /* output buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 8;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < 8 - nbytes; n++)
+ UCHAR(ivec, n) = UCHAR(ivec, n+nbytes);
+ for (n = 0; n < nbytes; n++) {
+ UCHAR(ivec, 8-nbytes+n) = UCHAR(msgbuf, n);
+ obuf[n] = ibuf[n] ^ UCHAR(msgbuf, n);
+ }
+ WRITE(obuf, nbytes);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ bn++;
+ MEMZERO(&ibuf[n], nbytes - n);
+ ibuf[nbytes - 1] = n;
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (c = 0; c < nbytes; c++)
+ ibuf[c] ^= UCHAR(msgbuf, c);
+ WRITE(ibuf, nbytes);
+}
+
+/*
+ * This decrypts using the Output Block Chaining mode of DES
+ */
+ofbdec()
+{
+ register int n; /* number of bytes actually read */
+ register int c; /* used to test for EOF */
+ register int nbytes; /* number of bytes to read */
+ register int bn; /* block number */
+ char ibuf[8]; /* input buffer */
+ char obuf[8]; /* output buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 8;
+ /*
+ * do the transformation
+ */
+ for (bn = 1; (n = READ(ibuf, nbytes)) == nbytes; bn++) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (c = 0; c < 8 - nbytes; c++)
+ CHAR(ivec, c) = CHAR(ivec, c+nbytes);
+ for (c = 0; c < nbytes; c++) {
+ CHAR(ivec, 8-nbytes+c) = UCHAR(msgbuf, c);
+ obuf[c] = ibuf[c] ^ UCHAR(msgbuf, c);
+ }
+ /*
+ * if the last one, handle it specially
+ */
+ if ((c = getchar()) == EOF) {
+ n = obuf[nbytes-1];
+ if (n < 0 || n > nbytes-1)
+ err(bn, "decryption failed (block corrupted)");
+ }
+ else
+ (void)ungetc(c, stdin);
+ /*
+ * dump it
+ */
+ WRITE(obuf, n);
+ }
+ if (n > 0)
+ err(bn, "decryption failed (incomplete block)");
+}
+
+/*
+ * This authenticates using the Cipher FeedBack mode of DES
+ */
+cfbauth()
+{
+ register int n, j; /* number of bytes actually read */
+ register int nbytes; /* number of bytes to read */
+ char ibuf[8]; /* input buffer */
+ Desbuf msgbuf; /* encryption buffer */
+
+ /*
+ * do things in bytes, not bits
+ */
+ nbytes = fbbits / 8;
+ /*
+ * do the transformation
+ */
+ while ((n = READ(ibuf, nbytes)) == nbytes) {
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < 8 - nbytes; n++)
+ UCHAR(ivec, n) = UCHAR(ivec, n+nbytes);
+ for (n = 0; n < nbytes; n++)
+ UCHAR(ivec, 8-nbytes+n) = ibuf[n] ^ UCHAR(msgbuf, n);
+ }
+ /*
+ * at EOF or last block -- in either case, the last byte contains
+ * the character representation of the number of bytes in it
+ */
+ MEMZERO(&ibuf[n], nbytes - n);
+ ibuf[nbytes - 1] = '0' + n;
+ MEMCPY(BUFFER(msgbuf), BUFFER(ivec), 8);
+ DES_XFORM(UBUFFER(msgbuf));
+ for (n = 0; n < nbytes; n++)
+ ibuf[n] ^= UCHAR(msgbuf, n);
+ /*
+ * drop the bits
+ * we write chars until fewer than 7 bits,
+ * and then pad the last one with 0 bits
+ */
+ for (n = 0; macbits > 7; n++, macbits -= 8)
+ (void)putchar(CHAR(msgbuf, n));
+ if (macbits > 0) {
+ CHAR(msgbuf, 0) = 0x00;
+ for (j = 0; j < macbits; j++)
+ CHAR(msgbuf, 0) |= (CHAR(msgbuf, n)&bits[j]);
+ (void)putchar(CHAR(msgbuf, 0));
+ }
+}
+
+#ifndef FASTWAY
+/*
+ * change from 8 bits/Uchar to 1 bit/Uchar
+ */
+expand(from, to)
+ Desbuf from; /* 8bit/unsigned char string */
+ char *to; /* 1bit/char string */
+{
+ register int i, j; /* counters in for loop */
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ *to++ = (CHAR(from, i)>>(7-j))&01;
+}
+
+/*
+ * change from 1 bit/char to 8 bits/Uchar
+ */
+compress(from, to)
+ char *from; /* 1bit/char string */
+ Desbuf to; /* 8bit/unsigned char string */
+{
+ register int i, j; /* counters in for loop */
+
+ for (i = 0; i < 8; i++) {
+ CHAR(to, i) = 0;
+ for (j = 0; j < 8; j++)
+ CHAR(to, i) = ((*from++)<<(7-j))|CHAR(to, i);
+ }
+}
+#endif
+
+/*
+ * message about usage
+ */
+usage()
+{
+ (void)fprintf(stderr, "%s\n",
+"usage: bdes [-abdp] [-F bit] [-f bit] [-k key] [-m bit] [-o bit] [-v vector]");
+ exit(1);
+}
diff --git a/usr.bin/bdes/bdes.ps b/usr.bin/bdes/bdes.ps
new file mode 100644
index 0000000..471c267
--- /dev/null
+++ b/usr.bin/bdes/bdes.ps
@@ -0,0 +1,2945 @@
+%!
+%%BoundingBox: (atend)
+%%Pages: (atend)
+%%DocumentFonts: (atend)
+%%EndComments
+%
+% FrameMaker PostScript Prolog 2.0, for use with FrameMaker 2.0
+% Copyright (c) 1986,87,89 by Frame Technology, Inc. All rights reserved.
+%
+% Known Problems:
+% Due to bugs in Transcript, the 'PS-Adobe-' is omitted from line 1
+/FMversion (2.0) def
+% Set up Color vs. Black-and-White
+ /FMPrintInColor systemdict /colorimage known def
+% Uncomment this line to force b&w on color printer
+% /FMPrintInColor false def
+/FrameDict 190 dict def
+systemdict /errordict known not {/errordict 10 dict def
+ errordict /rangecheck {stop} put} if
+% The readline in 23.0 doesn't recognize cr's as nl's on AppleTalk
+FrameDict /tmprangecheck errordict /rangecheck get put
+errordict /rangecheck {FrameDict /bug true put} put
+FrameDict /bug false put
+mark
+% Some PS machines read past the CR, so keep the following 3 lines together!
+currentfile 5 string readline
+00
+0000000000
+cleartomark
+errordict /rangecheck FrameDict /tmprangecheck get put
+FrameDict /bug get {
+ /readline {
+ /gstring exch def
+ /gfile exch def
+ /gindex 0 def
+ {
+ gfile read pop
+ dup 10 eq {exit} if
+ dup 13 eq {exit} if
+ gstring exch gindex exch put
+ /gindex gindex 1 add def
+ } loop
+ pop
+ gstring 0 gindex getinterval true
+ } def
+ } if
+/FMVERSION {
+ FMversion ne {
+ /Times-Roman findfont 18 scalefont setfont
+ 100 100 moveto
+ (FrameMaker version does not match postscript_prolog!)
+ dup =
+ show showpage
+ } if
+ } def
+/FMLOCAL {
+ FrameDict begin
+ 0 def
+ end
+ } def
+ /gstring FMLOCAL
+ /gfile FMLOCAL
+ /gindex FMLOCAL
+ /orgxfer FMLOCAL
+ /orgproc FMLOCAL
+ /organgle FMLOCAL
+ /orgfreq FMLOCAL
+ /yscale FMLOCAL
+ /xscale FMLOCAL
+ /manualfeed FMLOCAL
+ /paperheight FMLOCAL
+ /paperwidth FMLOCAL
+/FMDOCUMENT {
+ array /FMfonts exch def
+ /#copies exch def
+ FrameDict begin
+ 0 ne dup {setmanualfeed} if
+ /manualfeed exch def
+ /paperheight exch def
+ /paperwidth exch def
+ setpapername
+ manualfeed {true} {papersize} ifelse
+ {manualpapersize} {false} ifelse
+ {desperatepapersize} if
+ /yscale exch def
+ /xscale exch def
+ currenttransfer cvlit /orgxfer exch def
+ currentscreen cvlit /orgproc exch def
+ /organgle exch def /orgfreq exch def
+ end
+ } def
+ /pagesave FMLOCAL
+ /orgmatrix FMLOCAL
+ /landscape FMLOCAL
+/FMBEGINPAGE {
+ FrameDict begin
+ /pagesave save def
+ 3.86 setmiterlimit
+ /landscape exch 0 ne def
+ landscape {
+ 90 rotate 0 exch neg translate pop
+ }
+ {pop pop}
+ ifelse
+ xscale yscale scale
+ /orgmatrix matrix def
+ gsave
+ } def
+/FMENDPAGE {
+ grestore
+ pagesave restore
+ end
+ showpage
+ } def
+/FMDEFINEFONT {
+ FrameDict begin
+ findfont
+ ReEncode
+ 2 index exch
+ definefont exch
+ scalefont
+ FMfonts 3 1 roll
+ put
+ end
+ } bind def
+/FMNORMALIZEGRAPHICS {
+ newpath
+ 0.0 0.0 moveto
+ 1 setlinewidth
+ 0 setlinecap
+ 0 0 0 sethsbcolor
+ 0 setgray
+ } bind def
+ /fx FMLOCAL
+ /fy FMLOCAL
+ /fh FMLOCAL
+ /fw FMLOCAL
+ /llx FMLOCAL
+ /lly FMLOCAL
+ /urx FMLOCAL
+ /ury FMLOCAL
+/FMBEGINEPSF {
+ end
+ /FMEPSF save def
+ /showpage {} def
+ FMNORMALIZEGRAPHICS
+ [/fy /fx /fh /fw /ury /urx /lly /llx] {exch def} forall
+ fx fy translate
+ rotate
+ fw urx llx sub div fh ury lly sub div scale
+ llx neg lly neg translate
+ } bind def
+/FMENDEPSF {
+ FMEPSF restore
+ FrameDict begin
+ } bind def
+FrameDict begin
+/setmanualfeed {
+%%BeginFeature *ManualFeed True
+ statusdict /manualfeed true put
+%%EndFeature
+ } def
+/max {2 copy lt {exch} if pop} bind def
+/min {2 copy gt {exch} if pop} bind def
+/inch {72 mul} def
+/pagedimen {
+ paperheight sub abs 16 lt exch
+ paperwidth sub abs 16 lt and
+ {/papername exch def} {pop} ifelse
+ } def
+ /papersizedict FMLOCAL
+/setpapername {
+ /papersizedict 14 dict def
+ papersizedict begin
+ /papername /unknown def
+ /Letter 8.5 inch 11.0 inch pagedimen
+ /LetterSmall 7.68 inch 10.16 inch pagedimen
+ /Tabloid 11.0 inch 17.0 inch pagedimen
+ /Ledger 17.0 inch 11.0 inch pagedimen
+ /Legal 8.5 inch 14.0 inch pagedimen
+ /Statement 5.5 inch 8.5 inch pagedimen
+ /Executive 7.5 inch 10.0 inch pagedimen
+ /A3 11.69 inch 16.5 inch pagedimen
+ /A4 8.26 inch 11.69 inch pagedimen
+ /A4Small 7.47 inch 10.85 inch pagedimen
+ /B4 10.125 inch 14.33 inch pagedimen
+ /B5 7.16 inch 10.125 inch pagedimen
+ end
+ } def
+/papersize {
+ papersizedict begin
+ /Letter {lettertray} def
+ /LetterSmall {lettertray lettersmall} def
+ /Tabloid {11x17tray} def
+ /Ledger {ledgertray} def
+ /Legal {legaltray} def
+ /Statement {statementtray} def
+ /Executive {executivetray} def
+ /A3 {a3tray} def
+ /A4 {a4tray} def
+ /A4Small {a4tray a4small} def
+ /B4 {b4tray} def
+ /B5 {b5tray} def
+ /unknown {unknown} def
+ papersizedict dup papername known {papername} {/unknown} ifelse get
+ end
+ /FMdicttop countdictstack 1 add def
+ statusdict begin stopped end
+ countdictstack -1 FMdicttop {pop end} for
+ } def
+/manualpapersize {
+ papersizedict begin
+ /Letter {letter} def
+ /LetterSmall {lettersmall} def
+ /Tabloid {11x17} def
+ /Ledger {ledger} def
+ /Legal {legal} def
+ /Statement {statement} def
+ /Executive {executive} def
+ /A3 {a3} def
+ /A4 {a4} def
+ /A4Small {a4small} def
+ /B4 {b4} def
+ /B5 {b5} def
+ /unknown {unknown} def
+ papersizedict dup papername known {papername} {/unknown} ifelse get
+ end
+ stopped
+ } def
+/desperatepapersize {
+ statusdict /setpageparams known
+ {
+ paperwidth paperheight 0 1
+ statusdict begin
+ {setpageparams} stopped pop
+ end
+ } if
+ } def
+/savematrix {
+ orgmatrix currentmatrix pop
+ } bind def
+/restorematrix {
+ orgmatrix setmatrix
+ } bind def
+/dmatrix matrix def
+/dpi 72 0 dmatrix defaultmatrix dtransform
+ dup mul exch dup mul add sqrt def
+/freq dpi 18.75 div 8 div round dup 0 eq {pop 1} if 8 mul dpi exch div def
+/sangle 1 0 dmatrix defaultmatrix dtransform exch atan def
+/DiacriticEncoding [
+/.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 /quotesingle /parenleft
+/parenright /asterisk /plus /comma /hyphen /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 /grave /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 /Adieresis /Aring /Ccedilla /Eacute
+/Ntilde /Odieresis /Udieresis /aacute /agrave /acircumflex /adieresis
+/atilde /aring /ccedilla /eacute /egrave /ecircumflex /edieresis
+/iacute /igrave /icircumflex /idieresis /ntilde /oacute /ograve
+/ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex
+/udieresis /dagger /.notdef /cent /sterling /section /bullet
+/paragraph /germandbls /registered /copyright /trademark /acute
+/dieresis /.notdef /AE /Oslash /.notdef /.notdef /.notdef /.notdef
+/yen /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+/ordfeminine /ordmasculine /.notdef /ae /oslash /questiondown
+/exclamdown /logicalnot /.notdef /florin /.notdef /.notdef
+/guillemotleft /guillemotright /ellipsis /.notdef /Agrave /Atilde
+/Otilde /OE /oe /endash /emdash /quotedblleft /quotedblright
+/quoteleft /quoteright /.notdef /.notdef /ydieresis /Ydieresis
+/fraction /currency /guilsinglleft /guilsinglright /fi /fl /daggerdbl
+/periodcentered /quotesinglbase /quotedblbase /perthousand
+/Acircumflex /Ecircumflex /Aacute /Edieresis /Egrave /Iacute
+/Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex /.notdef /Ograve
+/Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde /macron
+/breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron
+] def
+/ReEncode {
+ dup
+ length
+ dict begin
+ {
+ 1 index /FID ne
+ {def}
+ {pop pop} ifelse
+ } forall
+ Encoding StandardEncoding eq
+ {
+ /Encoding DiacriticEncoding def
+ }if
+ currentdict
+ end
+ } bind def
+/graymode true def
+ /bwidth FMLOCAL
+ /bpside FMLOCAL
+ /bstring FMLOCAL
+ /onbits FMLOCAL
+ /offbits FMLOCAL
+ /xindex FMLOCAL
+ /yindex FMLOCAL
+ /x FMLOCAL
+ /y FMLOCAL
+/setpattern {
+ /bwidth exch def
+ /bpside exch def
+ /bstring exch def
+ /onbits 0 def /offbits 0 def
+ freq sangle landscape {90 add} if
+ {/y exch def
+ /x exch def
+ /xindex x 1 add 2 div bpside mul cvi def
+ /yindex y 1 add 2 div bpside mul cvi def
+ bstring yindex bwidth mul xindex 8 idiv add get
+ 1 7 xindex 8 mod sub bitshift and 0 ne
+ {/onbits onbits 1 add def 1}
+ {/offbits offbits 1 add def 0}
+ ifelse
+ }
+ setscreen
+ {} settransfer
+ offbits offbits onbits add div FMsetgray
+ /graymode false def
+ } bind def
+/grayness {
+ FMsetgray
+ graymode not {
+ /graymode true def
+ orgxfer cvx settransfer
+ orgfreq organgle orgproc cvx setscreen
+ } if
+ } bind def
+ /HUE FMLOCAL
+ /SAT FMLOCAL
+ /BRIGHT FMLOCAL
+ /Colors FMLOCAL
+FMPrintInColor
+
+ {
+ /HUE 0 def
+ /SAT 0 def
+ /BRIGHT 0 def
+ % array of arrays Hue and Sat values for the separations [HUE BRIGHT]
+ /Colors
+ [[0 0 ] % black
+ [0 0 ] % white
+ [0.00 1.0] % red
+ [0.37 1.0] % green
+ [0.60 1.0] % blue
+ [0.50 1.0] % cyan
+ [0.83 1.0] % magenta
+ [0.16 1.0] % comment / yellow
+ ] def
+
+ /BEGINBITMAPCOLOR {
+ BITMAPCOLOR} def
+ /BEGINBITMAPCOLORc {
+ BITMAPCOLORc} def
+ /K {
+ Colors exch get dup
+ 0 get /HUE exch store
+ 1 get /BRIGHT exch store
+ HUE 0 eq BRIGHT 0 eq and
+ {1.0 SAT sub setgray}
+ {HUE SAT BRIGHT sethsbcolor}
+ ifelse
+ } def
+ /FMsetgray {
+ /SAT exch 1.0 exch sub store
+ HUE 0 eq BRIGHT 0 eq and
+ {1.0 SAT sub setgray}
+ {HUE SAT BRIGHT sethsbcolor}
+ ifelse
+ } bind def
+ }
+
+ {
+ /BEGINBITMAPCOLOR {
+ BITMAPGRAY} def
+ /BEGINBITMAPCOLORc {
+ BITMAPGRAYc} def
+ /FMsetgray {setgray} bind def
+ /K {
+ pop
+ } def
+ }
+ifelse
+/normalize {
+ transform round exch round exch itransform
+ } bind def
+/dnormalize {
+ dtransform round exch round exch idtransform
+ } bind def
+/lnormalize {
+ 0 dtransform exch cvi 2 idiv 2 mul 1 add exch idtransform pop
+ } bind def
+/H {
+ lnormalize setlinewidth
+ } bind def
+/Z {
+ setlinecap
+ } bind def
+/X {
+ fillprocs exch get exec
+ } bind def
+/V {
+ gsave eofill grestore
+ } bind def
+/N {
+ stroke
+ } bind def
+/M {newpath moveto} bind def
+/E {lineto} bind def
+/D {curveto} bind def
+/O {closepath} bind def
+ /n FMLOCAL
+/L {
+ /n exch def
+ newpath
+ normalize
+ moveto
+ 2 1 n {pop normalize lineto} for
+ } bind def
+/Y {
+ L
+ closepath
+ } bind def
+ /x1 FMLOCAL
+ /x2 FMLOCAL
+ /y1 FMLOCAL
+ /y2 FMLOCAL
+ /rad FMLOCAL
+/R {
+ /y2 exch def
+ /x2 exch def
+ /y1 exch def
+ /x1 exch def
+ x1 y1
+ x2 y1
+ x2 y2
+ x1 y2
+ 4 Y
+ } bind def
+/RR {
+ /rad exch def
+ normalize
+ /y2 exch def
+ /x2 exch def
+ normalize
+ /y1 exch def
+ /x1 exch def
+ newpath
+ x1 y1 rad add moveto
+ x1 y2 x2 y2 rad arcto
+ x2 y2 x2 y1 rad arcto
+ x2 y1 x1 y1 rad arcto
+ x1 y1 x1 y2 rad arcto
+ closepath
+ 16 {pop} repeat
+ } bind def
+/C {
+ grestore
+ gsave
+ R
+ clip
+ } bind def
+/U {
+ grestore
+ gsave
+ } bind def
+/F {
+ FMfonts exch get
+ setfont
+ } bind def
+/T {
+ moveto show
+ } bind def
+/RF {
+ rotate
+ 0 ne {-1 1 scale} if
+ } bind def
+/TF {
+ gsave
+ moveto
+ RF
+ show
+ grestore
+ } bind def
+/P {
+ moveto
+ 0 32 3 2 roll widthshow
+ } bind def
+/PF {
+ gsave
+ moveto
+ RF
+ 0 32 3 2 roll widthshow
+ grestore
+ } bind def
+/S {
+ moveto
+ 0 exch ashow
+ } bind def
+/SF {
+ gsave
+ moveto
+ RF
+ 0 exch ashow
+ grestore
+ } bind def
+/B {
+ moveto
+ 0 32 4 2 roll 0 exch awidthshow
+ } bind def
+/BF {
+ gsave
+ moveto
+ RF
+ 0 32 4 2 roll 0 exch awidthshow
+ grestore
+ } bind def
+ /x FMLOCAL
+ /y FMLOCAL
+ /dx FMLOCAL
+ /dy FMLOCAL
+ /dl FMLOCAL
+ /t FMLOCAL
+ /t2 FMLOCAL
+ /Cos FMLOCAL
+ /Sin FMLOCAL
+ /r FMLOCAL
+/W {
+ dnormalize
+ /dy exch def
+ /dx exch def
+ normalize
+ /y exch def
+ /x exch def
+ /dl dx dx mul dy dy mul add sqrt def
+ dl 0.0 gt {
+ /t currentlinewidth def
+ savematrix
+ /Cos dx dl div def
+ /Sin dy dl div def
+ /r [Cos Sin Sin neg Cos 0.0 0.0] def
+ /t2 t 2.5 mul 3.5 max def
+ newpath
+ x y translate
+ r concat
+ 0.0 0.0 moveto
+ dl t 2.7 mul sub 0.0 rlineto
+ stroke
+ restorematrix
+ x dx add y dy add translate
+ r concat
+ t 0.67 mul setlinewidth
+ t 1.61 mul neg 0.0 translate
+ 0.0 0.0 moveto
+ t2 1.7 mul neg t2 2.0 div moveto
+ 0.0 0.0 lineto
+ t2 1.7 mul neg t2 2.0 div neg lineto
+ stroke
+ t setlinewidth
+ restorematrix
+ } if
+ } bind def
+/G {
+ gsave
+ newpath
+ normalize translate 0.0 0.0 moveto
+ dnormalize scale
+ 0.0 0.0 1.0 5 3 roll arc
+ closepath fill
+ grestore
+ } bind def
+/A {
+ gsave
+ savematrix
+ newpath
+ 2 index 2 div add exch 3 index 2 div sub exch
+ normalize 2 index 2 div sub exch 3 index 2 div add exch
+ translate
+ scale
+ 0.0 0.0 1.0 5 3 roll arc
+ restorematrix
+ stroke
+ grestore
+ } bind def
+ /x FMLOCAL
+ /y FMLOCAL
+ /w FMLOCAL
+ /h FMLOCAL
+ /xx FMLOCAL
+ /yy FMLOCAL
+ /ww FMLOCAL
+ /hh FMLOCAL
+ /FMsaveobject FMLOCAL
+ /FMoptop FMLOCAL
+ /FMdicttop FMLOCAL
+/BEGINPRINTCODE {
+ /FMdicttop countdictstack 1 add def
+ /FMoptop count 4 sub def
+ /FMsaveobject save def
+ userdict begin
+ /showpage {} def
+ FMNORMALIZEGRAPHICS
+ 3 index neg 3 index neg translate
+ } bind def
+/ENDPRINTCODE {
+ count -1 FMoptop {pop pop} for
+ countdictstack -1 FMdicttop {pop end} for
+ FMsaveobject restore
+ } bind def
+/gn {
+ 0
+ { 46 mul
+ cf read pop
+ 32 sub
+ dup 46 lt {exit} if
+ 46 sub add
+ } loop
+ add
+ } bind def
+ /str FMLOCAL
+/cfs {
+ /str sl string def
+ 0 1 sl 1 sub {str exch val put} for
+ str def
+ } bind def
+/ic [
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
+ 0
+ {0 hx} {1 hx} {2 hx} {3 hx} {4 hx} {5 hx} {6 hx} {7 hx} {8 hx} {9 hx}
+ {10 hx} {11 hx} {12 hx} {13 hx} {14 hx} {15 hx} {16 hx} {17 hx} {18 hx}
+ {19 hx} {gn hx} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12}
+ {13} {14} {15} {16} {17} {18} {19} {gn} {0 wh} {1 wh} {2 wh} {3 wh}
+ {4 wh} {5 wh} {6 wh} {7 wh} {8 wh} {9 wh} {10 wh} {11 wh} {12 wh}
+ {13 wh} {14 wh} {gn wh} {0 bl} {1 bl} {2 bl} {3 bl} {4 bl} {5 bl} {6 bl}
+ {7 bl} {8 bl} {9 bl} {10 bl} {11 bl} {12 bl} {13 bl} {14 bl} {gn bl}
+ {0 fl} {1 fl} {2 fl} {3 fl} {4 fl} {5 fl} {6 fl} {7 fl} {8 fl} {9 fl}
+ {10 fl} {11 fl} {12 fl} {13 fl} {14 fl} {gn fl}
+ ] def
+ /sl FMLOCAL
+ /val FMLOCAL
+ /ws FMLOCAL
+ /im FMLOCAL
+ /bs FMLOCAL
+ /cs FMLOCAL
+ /len FMLOCAL
+ /pos FMLOCAL
+/ms {
+ /sl exch def
+ /val 255 def
+ /ws cfs
+ /im cfs
+ /val 0 def
+ /bs cfs
+ /cs cfs
+ } bind def
+400 ms
+/ip {
+ is
+ 0
+ cf cs readline pop
+ { ic exch get exec
+ add
+ } forall
+ pop
+
+ } bind def
+/wh {
+ /len exch def
+ /pos exch def
+ ws 0 len getinterval im pos len getinterval copy pop
+ pos len
+ } bind def
+/bl {
+ /len exch def
+ /pos exch def
+ bs 0 len getinterval im pos len getinterval copy pop
+ pos len
+ } bind def
+/s1 1 string def
+/fl {
+ /len exch def
+ /pos exch def
+ /val cf s1 readhexstring pop 0 get def
+ pos 1 pos len add 1 sub {im exch val put} for
+ pos len
+ } bind def
+/hx {
+ 3 copy getinterval
+ cf exch readhexstring pop pop
+ } bind def
+ /h FMLOCAL
+ /w FMLOCAL
+ /d FMLOCAL
+ /lb FMLOCAL
+ /bitmapsave FMLOCAL
+ /is FMLOCAL
+ /cf FMLOCAL
+/wbytes {
+ dup
+ 8 eq {pop} {1 eq {7 add 8 idiv} {3 add 4 idiv} ifelse} ifelse
+ } bind def
+/BEGINBITMAPBWc {
+ 1 {} COMMONBITMAPc
+ } bind def
+/BEGINBITMAPGRAYc {
+ 8 {} COMMONBITMAPc
+ } bind def
+/BEGINBITMAP2BITc {
+ 2 {} COMMONBITMAPc
+ } bind def
+/COMMONBITMAPc {
+ /r exch def
+ /d exch def
+ gsave
+ translate rotate scale /h exch def /w exch def
+ /lb w d wbytes def
+ sl lb lt {lb ms} if
+ /bitmapsave save def
+ r
+ /is im 0 lb getinterval def
+ ws 0 lb getinterval is copy pop
+ /cf currentfile def
+ w h d [w 0 0 h neg 0 h]
+ {ip} image
+ bitmapsave restore
+ grestore
+ } bind def
+/BEGINBITMAPBW {
+ 1 {} COMMONBITMAP
+ } bind def
+/BEGINBITMAPGRAY {
+ 8 {} COMMONBITMAP
+ } bind def
+/BEGINBITMAP2BIT {
+ 2 {} COMMONBITMAP
+ } bind def
+/COMMONBITMAP {
+ /r exch def
+ /d exch def
+ gsave
+ translate rotate scale /h exch def /w exch def
+ /bitmapsave save def
+ r
+ /is w d wbytes string def
+ /cf currentfile def
+ w h d [w 0 0 h neg 0 h]
+ {cf is readhexstring pop} image
+ bitmapsave restore
+ grestore
+ } bind def
+ /proc1 FMLOCAL
+ /proc2 FMLOCAL
+ /newproc FMLOCAL
+/Fmcc {
+ /proc2 exch cvlit def
+ /proc1 exch cvlit def
+ /newproc proc1 length proc2 length add array def
+ newproc 0 proc1 putinterval
+ newproc proc1 length proc2 putinterval
+ newproc cvx
+} bind def
+/ngrayt 256 array def
+/nredt 256 array def
+/nbluet 256 array def
+/ngreent 256 array def
+ /gryt FMLOCAL
+ /blut FMLOCAL
+ /grnt FMLOCAL
+ /redt FMLOCAL
+ /indx FMLOCAL
+ /cynu FMLOCAL
+ /magu FMLOCAL
+ /yelu FMLOCAL
+ /k FMLOCAL
+ /u FMLOCAL
+/colorsetup {
+ currentcolortransfer
+ /gryt exch def
+ /blut exch def
+ /grnt exch def
+ /redt exch def
+ 0 1 255 {
+ /indx exch def
+ /cynu 1 red indx get 255 div sub def
+ /magu 1 green indx get 255 div sub def
+ /yelu 1 blue indx get 255 div sub def
+ /k cynu magu min yelu min def
+ /u k currentundercolorremoval exec def
+ nredt indx 1 0 cynu u sub max sub redt exec put
+ ngreent indx 1 0 magu u sub max sub grnt exec put
+ nbluet indx 1 0 yelu u sub max sub blut exec put
+ ngrayt indx 1 k currentblackgeneration exec sub gryt exec put
+ } for
+ {255 mul cvi nredt exch get}
+ {255 mul cvi ngreent exch get}
+ {255 mul cvi nbluet exch get}
+ {255 mul cvi ngrayt exch get}
+ setcolortransfer
+ {pop 0} setundercolorremoval
+ {} setblackgeneration
+ } bind def
+ /tran FMLOCAL
+/fakecolorsetup {
+ /tran 256 string def
+ 0 1 255 {/indx exch def
+ tran indx
+ red indx get 77 mul
+ green indx get 151 mul
+ blue indx get 28 mul
+ add add 256 idiv put} for
+ currenttransfer
+ {255 mul cvi tran exch get 255.0 div}
+ exch Fmcc settransfer
+} bind def
+/BITMAPCOLOR {
+ /d 8 def
+ gsave
+ translate rotate scale /h exch def /w exch def
+ /bitmapsave save def
+ colorsetup
+ /is w d wbytes string def
+ /cf currentfile def
+ w h d [w 0 0 h neg 0 h]
+ {cf is readhexstring pop} {is} {is} true 3 colorimage
+ bitmapsave restore
+ grestore
+ } bind def
+/BITMAPCOLORc {
+ /d 8 def
+ gsave
+ translate rotate scale /h exch def /w exch def
+ /lb w d wbytes def
+ sl lb lt {lb ms} if
+ /bitmapsave save def
+ colorsetup
+ /is im 0 lb getinterval def
+ ws 0 lb getinterval is copy pop
+ /cf currentfile def
+ w h d [w 0 0 h neg 0 h]
+ {ip} {is} {is} true 3 colorimage
+ bitmapsave restore
+ grestore
+ } bind def
+/BITMAPGRAY {
+ 8 {fakecolorsetup} COMMONBITMAP
+ } bind def
+/BITMAPGRAYc {
+ 8 {fakecolorsetup} COMMONBITMAPc
+ } bind def
+/ENDBITMAP {
+ } bind def
+end
+%%EndProlog
+%%BeginSetup
+(2.0) FMVERSION
+1 1 612 792 0 1 16 FMDOCUMENT
+/fillprocs 32 array def
+fillprocs 0 { 0.000000 grayness } put
+fillprocs 1 { 0.100000 grayness } put
+fillprocs 2 { 0.300000 grayness } put
+fillprocs 3 { 0.500000 grayness } put
+fillprocs 4 { 0.700000 grayness } put
+fillprocs 5 { 0.900000 grayness } put
+fillprocs 6 { 0.970000 grayness } put
+fillprocs 7 { 1.000000 grayness } put
+fillprocs 8 {<0f87c3e1f0783c1e> 8 1 setpattern } put
+fillprocs 9 {<0f1e3c78f0e1c387> 8 1 setpattern } put
+fillprocs 10 {<cccccccccccccccc> 8 1 setpattern } put
+fillprocs 11 {<ffff0000ffff0000> 8 1 setpattern } put
+fillprocs 12 {<8142241818244281> 8 1 setpattern } put
+fillprocs 13 {<8040201008040201> 8 1 setpattern } put
+fillprocs 14 {<03060c183060c081> 8 1 setpattern } put
+fillprocs 15 {} put
+fillprocs 16 { 1.000000 grayness } put
+fillprocs 17 { 0.900000 grayness } put
+fillprocs 18 { 0.700000 grayness } put
+fillprocs 19 { 0.500000 grayness } put
+fillprocs 20 { 0.300000 grayness } put
+fillprocs 21 { 0.100000 grayness } put
+fillprocs 22 { 0.030000 grayness } put
+fillprocs 23 { 0.000000 grayness } put
+fillprocs 24 {<f0783c1e0f87c3e1> 8 1 setpattern } put
+fillprocs 25 {<f0e1c3870f1e3c78> 8 1 setpattern } put
+fillprocs 26 {<3333333333333333> 8 1 setpattern } put
+fillprocs 27 {<0000ffff0000ffff> 8 1 setpattern } put
+fillprocs 28 {<7ebddbe7e7dbbd7e> 8 1 setpattern } put
+fillprocs 29 {<7fbfdfeff7fbfdfe> 8 1 setpattern } put
+fillprocs 30 {<fcf9f3e7cf9f3f7e> 8 1 setpattern } put
+fillprocs 31 {} put
+%%EndSetup
+0 12 /Helvetica-Bold FMDEFINEFONT
+1 12 /Helvetica-BoldOblique FMDEFINEFONT
+%%Page: "-1" 1
+%%BeginPaperSize: Letter
+%%EndPaperSize
+612 792 0 FMBEGINPAGE
+144 144 468 396 R
+7 X
+0 K
+V
+0 F
+0 X
+1.2 (IMPLEMENT) 178.34 388 S
+1.2 (A) 258.88 388 S
+1.2 (TION NOTES ON ) 267.85 388 S
+1 F
+1.2 (bdes) 382.61 388 S
+0 F
+1.2 (\0501\051) 415.4 388 S
+1.2 (Matt Bishop) 265.09 338 S
+1.2 (T) 197.74 288 S
+1.2 (echnical Report PCS-TR91-158) 205.38 288 S
+FMENDPAGE
+%%EndPage: "-1" 2
+%%Page: "0" 2
+612 792 0 FMBEGINPAGE
+72 72 540 720 R
+7 X
+0 K
+V
+FMENDPAGE
+%%EndPage: "0" 3
+0 12 /Times-Roman FMDEFINEFONT
+1 18 /Times-Bold FMDEFINEFONT
+2 18 /Times-BoldItalic FMDEFINEFONT
+3 12 /Times-Italic FMDEFINEFONT
+4 12 /Times-Bold FMDEFINEFONT
+5 10 /Times-Roman FMDEFINEFONT
+6 12 /Courier FMDEFINEFONT
+7 12 /Courier-Oblique FMDEFINEFONT
+8 12 /ZapfDingbats FMDEFINEFONT
+9 12 /Symbol FMDEFINEFONT
+10 12 /Courier-Bold FMDEFINEFONT
+%%Page: "1" 3
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 1 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+1 F
+0 X
+(Implementation Notes on ) 179.84 708 T
+2 F
+(bdes) 378.21 708 T
+1 F
+(\0501\051) 411.19 708 T
+0 F
+( ) 432.17 708 T
+3 F
+(Matt Bishop) 276.51 676 T
+0 F
+(Department of Mathematics and Computer Science) 182.92 656 T
+(Dartmouth College) 259.86 642 T
+(Hanover) 257.45 628 T
+(, NH 03755) 298.26 628 T
+3 F
+(ABSTRACT) 277.68 602 T
+0 F
+0.27 (This note describes the implementation of ) 108 582 P
+3 F
+0.27 (bdes) 314.13 582 P
+0 F
+0.27 (, the \336le encryption program being) 336.12 582 P
+0.36 (distributed in the 4.4 release of the Berkeley Software Distribution. It implements) 108 568 P
+(all modes of the Data Encryption Standard program.) 108 554 T
+4 F
+(1. Intr) 72 528 T
+(oduction) 104.43 528 T
+0 F
+-0.09 (The Data Encryption Standard is a standard endorsed by the federal government. It is con-) 108 504 P
+-0.56 (siderably stronger than the algorithm used by the ) 72 484 P
+5 F
+-0.47 (UNIX) 305.36 484 P
+0 F
+-0.56 (\252 ) 330.34 484 P
+3 F
+-0.56 (crypt) 344.53 484 P
+0 F
+-0.56 (\0501\051 program, and therefore is a more) 369.18 484 P
+0.11 (suitable candidate for protecting information, especially information contained in ) 72 464 P
+5 F
+0.09 (ASCII) 466.05 464 P
+0 F
+0.11 ( \336les. The) 492.14 464 P
+-0.65 (program ) 72 444 P
+3 F
+-0.65 (bdes) 114.99 444 P
+0 F
+-0.65 (\0501\051 implements the DES and all of its modes, including the two authentication modes.) 136.97 444 P
+-0.59 (Because others may wish to write software compatible with this program, this note presents) 108 420 P
+-0.04 (the layout of the encrypted \336les produced by ) 72 400 P
+3 F
+-0.04 (bdes) 288.86 400 P
+0 F
+-0.04 ( as well as internal details relevant to the imple-) 310.85 400 P
+-0.15 (mentation. Whereever possible and appropriate, the description of the ) 72 380 P
+3 F
+-0.15 (des) 408.04 380 P
+0 F
+-0.15 (\0501\051 program given in [4]) 424.03 380 P
+-0.2 (has been followed; thus, ) 72 360 P
+3 F
+-0.2 (bdes) 190.77 360 P
+0 F
+-0.2 ( is completely compatible with that program. However) 212.75 360 P
+-0.2 (, ) 473.33 360 P
+3 F
+-0.2 (bdes) 479.12 360 P
+0 F
+-0.2 ( also of-) 501.11 360 P
+(fers several extensions to ) 72 340 T
+3 F
+(des) 195.9 340 T
+0 F
+( that are not compatible, and these will be explicitly pointed out.) 211.89 340 T
+-0.14 (In this note, strings typed as shown will be in ) 108 316 P
+6 F
+-0.34 (Courier Roman font) 326.78 316 P
+0 F
+-0.14 (, and strings to be) 455.62 316 P
+-0.42 (chosen by the user will be in ) 72 296 P
+7 F
+-1 (Courier Oblique font) 209.32 296 P
+0 F
+-0.42 (. The space character \050) 351.24 296 P
+5 F
+-0.35 (ASCII) 457.79 296 P
+0 F
+-0.42 ( <) 483.88 296 P
+5 F
+-0.35 (SP) 493.23 296 P
+0 F
+-0.42 (>, octal) 504.34 296 P
+-0.43 (40, decimal 32, hex 20\051 will be represented as \322) 72 276 P
+8 F
+-0.47 (z) 296.98 276 P
+0 F
+-0.43 (\323 and the newline character \050) 301.96 276 P
+5 F
+-0.35 (ASCII) 438.03 276 P
+0 F
+-0.43 ( <) 464.13 276 P
+5 F
+-0.35 (NL) 473.46 276 P
+0 F
+-0.43 (>, octal 12,) 486.79 276 P
+-0.05 (decimal 10, hex a\051 as \322) 72 256 P
+9 F
+-0.05 (\277) 181.65 256 P
+0 F
+-0.05 (\323. Because it is often more convenient to represent arbitrary characters as) 189.54 256 P
+1.13 (a sequence of hexadecimal digits, that representation will often be used; these digits will be in) 72 236 P
+10 F
+(Courier Bold font) 72 216 T
+0 F
+( with spaces often inserted for readability) 194.33 216 T
+(.) 392.07 216 T
+4 F
+(2. Overview and Use) 72 184 T
+3 F
+-0.39 (Bdes) 108 160 P
+0 F
+-0.39 ( implements the Data Encryption Standard algorithm in software, and enables the user) 131.32 160 P
+-0.61 (to encrypt data using any of the four modes of operation of the DES \050Electronic Code Book, Cipher) 72 140 P
+72 72 540 720 C
+72 72 540 117 C
+72 72 549 108 R
+7 X
+0 K
+V
+5 F
+0 X
+(This work is based on work funded by grant NAG2-680 from the National
+Aeronautics and Space Administration to ) 72 101.33 T
+(Dartmouth College.) 72 89.33 T
+(UNIX is a Registered T) 72 77.33 T
+(rademark of A) 166.58 77.33 T
+(T&T Bell Laboratories.) 223.75 77.33 T
+72 72 540 720 C
+0 0 612 792 C
+72 126 225 126 2 L
+7 X
+0 K
+V
+0.5 H
+2 Z
+0 X
+N
+FMENDPAGE
+%%EndPage: "1" 4
+%%Page: "2" 4
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 2 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+0.31 (Block Chaining, ) 72 712 P
+3 F
+0.31 (k) 154.25 712 P
+0 F
+0.31 (-bit Cipher Feed Back, and ) 159.58 712 P
+3 F
+0.31 (k) 293.71 712 P
+0 F
+0.31 (-bit Output Feed Back\051 as well as the Alternate ) 299.04 712 P
+3 F
+0.31 (k) 530.68 712 P
+0 F
+0.31 (-) 536.01 712 P
+-0.04 (bit Cipher Feed Back mode. Further) 72 692 P
+-0.04 (, ) 244.52 692 P
+3 F
+-0.04 (bdes) 250.48 692 P
+0 F
+-0.04 ( supports message authentication code generation based) 272.46 692 P
+(on both the Cipher Block Chaining mode and the ) 72 672 T
+3 F
+(k) 310.86 672 T
+0 F
+(-bit Cipher Feed Back mode.) 316.19 672 T
+0.07 (By default, ) 108 648 P
+3 F
+0.07 (bdes) 164.43 648 P
+0 F
+0.07 ( encrypts an input \336le using Cipher Block Chaining mode, and is invoked) 186.41 648 P
+-0.4 (as a \336lter) 72 628 P
+-0.4 (. The key may be speci\336ed either on the command line or may be typed to the prompt. So,) 114.51 628 P
+(if the input \336le ) 72 608 T
+7 F
+(inputf) 145.96 608 T
+(ile) 189.14 608 T
+0 F
+( contains the message) 210.73 608 T
+6 F
+(a) 253.9 584 T
+8 F
+(z) 261.1 584 T
+6 F
+(test) 266.07 584 T
+8 F
+(z) 294.86 584 T
+6 F
+(message) 299.83 584 T
+9 F
+(\277) 350.21 584 T
+0 F
+(then the following command encrypts it using the key ) 72 560 T
+6 F
+(abcdefgh) 333.5 560 T
+0 F
+(:) 391.07 560 T
+6 F
+(bdes -k abcdefgh < ) 158.48 536 T
+7 F
+(inputf) 295.21 536 T
+(ile) 338.38 536 T
+6 F
+( > ) 359.97 536 T
+7 F
+(outputf) 381.56 536 T
+(ile) 431.93 536 T
+0 F
+(The option ) 72 512 T
+4 F
+(-k) 127.3 512 T
+0 F
+( indicates the next ar) 137.96 512 T
+(gument is the key) 237.01 512 T
+(. Now ) 321.17 512 T
+7 F
+(outputf) 353.48 512 T
+(ile) 403.86 512 T
+0 F
+( contains) 425.45 512 T
+10 F
+(16 0e eb af 68 a0 d0 19 f1 a2 9b 31 0d 8a 01 c3) 136.89 488 T
+0 F
+0.06 (Other modes are speci\336ed using command-line options, as is control of the way the key is) 108 464 P
+(interpreted. The next sections contain several examples, and the Appendix has the manual page.) 72 444 T
+4 F
+(3. Keys and Parity) 72 412 T
+0 F
+0.58 (The key consists of 64 bits, and may be presented in any of hex, binary) 108 388 P
+0.58 (, or as a string of) 456.48 388 P
+5 F
+0.12 (ASCII) 72 368 P
+0 F
+0.14 ( characters. If the key is given in hex or binary) 98.1 368 P
+0.14 (, it is used as is with no changes. However) 322.21 368 P
+0.14 (, if) 526.53 368 P
+-0.27 (the key is given in ) 72 348 P
+5 F
+-0.23 (ASCII) 161.59 348 P
+0 F
+-0.27 (, a delicate problem arises: by convention, the parity bit is usually set to 0.) 187.69 348 P
+-0.47 (This high-order bit is generally ignored by applications; but the DES
+does not do so. Instead, it dis-) 72 328 P
+-0.14 (cards the low-order bit, ef) 72 308 P
+-0.14 (fectively reducing the size of the space of possible keys from 2) 195.44 308 P
+5 F
+-0.12 (56) 495.97 312.8 P
+0 F
+-0.14 ( to 2) 505.97 308 P
+5 F
+-0.12 (48) 527.01 312.8 P
+0 F
+-0.14 (.) 537 308 P
+-0.46 ( T) 108 284 P
+-0.46 (o preserve the size of the key space, the value of the parity bit must be related to the value) 117.03 284 P
+-0.09 (in the low-order bit, so the program sets the high-order bit to make each character in the key be of) 72 264 P
+-0.7 (odd parity) 72 244 P
+-0.7 (. \050Note that the initial value of the parity bit is ) 119.49 244 P
+3 F
+-0.7 (not) 334.99 244 P
+0 F
+-0.7 ( used in this computation.\051 For example,) 350.31 244 P
+(if the key is ) 72 224 T
+6 F
+(abcdefgh) 131.29 224 T
+0 F
+(, the actual key bits used are determined as follows:) 188.86 224 T
+5 F
+(ASCII) 99 200 T
+0 F
+( key) 125.1 200 T
+6 F
+(a) 243 200 T
+(b) 279 200 T
+(c) 315 200 T
+(d) 351 200 T
+(e) 387 200 T
+(f) 423 200 T
+(g) 459 200 T
+(h) 495 200 T
+5 F
+(ASCII) 99 180 T
+0 F
+( key bits \050hex\051) 125.1 180 T
+10 F
+(61) 243 180 T
+(62) 279 180 T
+(63) 315 180 T
+(64) 351 180 T
+(65) 387 180 T
+(66) 423 180 T
+(67) 459 180 T
+(68) 495 180 T
+0 F
+(parity) 99 160 T
+(odd) 243 160 T
+(odd) 279 160 T
+(even) 315 160 T
+(odd) 351 160 T
+(even) 387 160 T
+(even) 423 160 T
+(odd) 459 160 T
+(odd) 495 160 T
+(key bits used \050hex\051) 99 140 T
+10 F
+(61) 243 140 T
+(62) 279 140 T
+(e3) 315 140 T
+(64) 351 140 T
+(e5) 387 140 T
+(e6) 423 140 T
+(67) 459 140 T
+(68) 495 140 T
+0 F
+0.18 (This convention \050as opposed to requiring even parity) 108 120 P
+0.18 (, or simply copying the low-order bit) 362 120 P
+-0.41 (to the high-order bit\051 was chosen to provide compatibility with the encryption program ) 72 100 P
+3 F
+-0.41 (des) 486.77 100 P
+0 F
+-0.41 ( distrib-) 502.76 100 P
+-0.52 (uted by Sun Microsystems, Inc. [4]. Whether the key is entered on the command line or on the key-) 72 80 P
+FMENDPAGE
+%%EndPage: "2" 5
+%%Page: "3" 5
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 3 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+1.89 (board, by default it is processed into the same key schedule generated by Sun\325) 72 712 P
+1.89 (s ) 471.02 712 P
+3 F
+1.89 (des) 480.58 712 P
+0 F
+1.89 (, so \336les) 496.56 712 P
+(encrypted on a Sun can be decrypted using ) 72 692 T
+3 F
+(bdes) 280.51 692 T
+0 F
+( \050and vice versa\051.) 302.49 692 T
+-0.3 (If the user does not wish to use the Sun convention, the option \320) 108 668 P
+4 F
+-0.3 (p) 411.9 668 P
+0 F
+-0.3 ( will disable the parity bit) 418.57 668 P
+-0.62 (changing; with it, the parity bit is that of the character typed. This
+is useful when the key is a known) 72 648 P
+5 F
+(ASCII) 72 628 T
+0 F
+( string and the \336le was encrypted on a system which does not alter parity bits.) 98.1 628 T
+-0.24 (A key may be represented as a bit vector) 108 604 P
+-0.24 (, rather than an ) 300.74 604 P
+5 F
+-0.2 (ASCII) 374.7 604 P
+0 F
+-0.24 ( string, in one of two ways. It) 400.8 604 P
+0.19 (may be represented as a string of up to 16 hexadecimal digits; if fewer than 16 are given, the key) 72 584 P
+0.16 (is right \336lled with 0 bits. Or) 72 564 P
+0.16 (, it may be represented as a string of up to 64 binary digits, and again) 206.11 564 P
+0.15 (if fewer than 64 are given, the key is right-\336lled with 0 bits. Bit
+vector keys must be given on the) 72 544 P
+0.51 (command line, and must begin with the characters ) 72 524 P
+6 F
+1.24 (0x) 320.28 524 P
+0 F
+0.51 ( or ) 334.67 524 P
+6 F
+1.24 (0X) 351.69 524 P
+0 F
+0.51 ( \050for hexadecimal\051 or ) 366.08 524 P
+6 F
+1.24 (0b) 472.71 524 P
+0 F
+0.51 ( or ) 487.1 524 P
+6 F
+1.24 (0B) 504.12 524 P
+0 F
+0.51 ( \050for) 518.51 524 P
+(binary\051. For example, all of the following strings generate the same key schedule:) 72 504 T
+5 F
+(ASCII) 72 480 T
+0 F
+( key) 98.1 480 T
+6 F
+(abcdefgh) 180 480 T
+0 F
+(hexadecimal key) 72 460 T
+6 F
+(0x6162e364e5e66768) 180 460 T
+0 F
+(binary key) 72 440 T
+6 F
+(0b0110000101100010111000110110100011100101111000-) 180 440 T
+(1100110011101101000) 180 420 T
+0 F
+-0.14 ( Note that giving the key on the command line as ) 108 396 P
+6 F
+-0.34 (0x6162636465666768) 345.27 396 P
+0 F
+-0.14 ( will ) 474.8 396 P
+3 F
+-0.14 (not) 499.17 396 P
+0 F
+-0.14 ( reset) 514.5 396 P
+0.25 (the parity bits, because it is interpreted as a sequence of hex digits, not ) 72 376 P
+5 F
+0.21 (ASCII) 416.58 376 P
+0 F
+0.25 ( characters. The dif-) 442.68 376 P
+0.69 (ference in interpretation is that here the user can specify all bits of the key exactly) 72 356 P
+0.69 (, whereas \050on) 474.34 356 P
+0.25 (most terminals\051 it is not possible to control how the parity bit of ) 72 336 P
+5 F
+0.21 (ASCII) 384.76 336 P
+0 F
+0.25 ( characters is set. On some) 410.85 336 P
+0.36 (systems, it is possible to use a \322Meta\323 key to set the parity bit for an ) 72 316 P
+5 F
+0.3 (ASCII) 407.23 316 P
+0 F
+0.36 ( character; should this) 433.33 316 P
+-0.3 (be the case and the user desire ) 72 296 P
+3 F
+-0.3 (bdes) 218.09 296 P
+0 F
+-0.3 ( not to reset the parity bit, the option ) 240.07 296 P
+4 F
+-0.3 (\320p) 415.25 296 P
+0 F
+-0.3 ( will force the parity bit) 427.92 296 P
+(to be used as typed.) 72 276 T
+4 F
+(4. Encryption Output Repr) 72 244 T
+(esentation) 211.05 244 T
+0 F
+0.01 (All modes of the DES output ciphertext in blocks; the size of the block is 64 bits \0508 bytes\051) 108 220 P
+-0.25 (for ECB and CBC modes, and ) 72 200 P
+3 F
+-0.25 (k) 218.74 200 P
+0 F
+-0.25 ( bits for the ) 224.07 200 P
+3 F
+-0.25 (k) 281.02 200 P
+0 F
+-0.25 (-bit CFB and OFB modes, and there are as many out-) 286.35 200 P
+-0.5 (put blocks as input blocks. However) 72 180 P
+-0.5 (, as the length of the input is usually not a multiple of the block) 243.55 180 P
+-0.35 (size, some padding is necessary; but as padding must be done by appending characters, these char-) 72 160 P
+0.29 (acters must be distinguished from the input characters somehow) 72 140 P
+0.29 (. The mechanism used is that the) 381.35 140 P
+0.31 (last character of the \050decrypted\051 last block is the
+\050integer\051 number of characters from the input in) 72 120 P
+(the last block.) 72 100 T
+FMENDPAGE
+%%EndPage: "3" 6
+%%Page: "4" 6
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 4 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+-0.59 (For example, suppose ) 108 712 P
+7 F
+-1.41 (inputf) 214.16 712 P
+-1.41 (ile) 257.34 712 P
+0 F
+-0.59 ( contains \322) 278.93 712 P
+6 F
+-1.41 (This) 329.04 712 P
+8 F
+-0.65 (z) 357.83 712 P
+6 F
+-1.41 (is) 362.8 712 P
+8 F
+-0.65 (z) 377.2 712 P
+6 F
+-1.41 (a) 382.17 712 P
+8 F
+-0.65 (z) 389.37 712 P
+6 F
+-1.41 (test) 394.35 712 P
+9 F
+-0.59 (\277) 423.13 712 P
+0 F
+-0.59 (\323, and it is encrypted in) 431.02 712 P
+(CBC mode using the key \322) 72 692 T
+6 F
+(abcdef#@) 200.93 692 T
+0 F
+(\323 and the initialization vector ) 258.5 692 T
+6 F
+(0x0) 401.4 692 T
+0 F
+(; the command is) 422.99 692 T
+6 F
+(bdes -k abcdef#@ < ) 158.48 668 T
+7 F
+(inputf) 295.21 668 T
+(ile) 338.38 668 T
+6 F
+( > ) 359.97 668 T
+7 F
+(outputf) 381.56 668 T
+(ile) 431.93 668 T
+0 F
+(as CBC is the default encryption mode and ) 72 644 T
+6 F
+(0x0) 281.2 644 T
+0 F
+( the default initialization vector:) 302.79 644 T
+(text) 72 620 T
+6 F
+(T) 117 620 T
+(h) 144 620 T
+(i) 171 620 T
+(s) 198 620 T
+8 F
+(z) 225 620 T
+6 F
+(i) 252 620 T
+(s) 279 620 T
+8 F
+(z) 306 620 T
+6 F
+(a) 333 620 T
+8 F
+(z) 360 620 T
+6 F
+(t) 387 620 T
+(e) 414 620 T
+(s) 441 620 T
+(t) 468 620 T
+9 F
+(\277) 495 620 T
+0 F
+(hex) 72 600 T
+10 F
+(54) 117 600 T
+(68) 144 600 T
+(69) 171 600 T
+(73) 198 600 T
+(20) 225 600 T
+(69) 252 600 T
+(73) 279 600 T
+(20) 306 600 T
+(61) 333 600 T
+(20) 360 600 T
+(74) 387 600 T
+(65) 414 600 T
+(73) 441 600 T
+(74) 468 600 T
+(0a) 495 600 T
+0 F
+(input) 72 580 T
+10 F
+(54) 117 580 T
+(68) 144 580 T
+(69) 171 580 T
+(73) 198 580 T
+(20) 225 580 T
+(69) 252 580 T
+(73) 279 580 T
+(20) 306 580 T
+(61) 333 580 T
+(20) 360 580 T
+(74) 387 580 T
+(65) 414 580 T
+(73) 441 580 T
+(74) 468 580 T
+(0a) 495 580 T
+(07) 522 580 T
+0 F
+(output) 72 560 T
+10 F
+(a5) 117 560 T
+(5f) 144 560 T
+(81) 171 560 T
+(53) 198 560 T
+(51) 225 560 T
+(98) 252 560 T
+(47) 279 560 T
+(02) 306 560 T
+(db) 333 560 T
+(5a) 360 560 T
+(c5) 387 560 T
+(fe) 414 560 T
+(50) 441 560 T
+(3d) 468 560 T
+(40) 495 560 T
+(ce) 522 560 T
+0 F
+0.04 (Notice that the text is 15 characters long, so there are 7 bytes following the last full block.) 108 540 P
+3 F
+0.22 (Bdes) 72 520 P
+0 F
+0.22 ( pads this to a full block by appending one byte containing the ) 95.32 520 P
+5 F
+0.19 (ASCII) 399.67 520 P
+0 F
+0.22 ( character with numeric) 425.77 520 P
+(value 7 \050the ) 72 500 T
+5 F
+(ASCII) 131.62 500 T
+0 F
+( character <) 157.71 500 T
+5 F
+(BEL) 214.42 500 T
+0 F
+(>\051. The result is then encrypted.) 233.3 500 T
+0.44 (As another example, suppose ) 108 476 P
+7 F
+1.07 (inputf) 253.34 476 P
+1.07 (ile) 296.52 476 P
+0 F
+0.44 ( contains \322) 318.11 476 P
+6 F
+1.07 (test) 370.29 476 P
+0 F
+0.44 (\323, and it is encrypted in ECB) 399.08 476 P
+(mode using the key \322) 72 456 T
+6 F
+(abcdef#@) 173.93 456 T
+0 F
+(\323; the command is) 231.5 456 T
+6 F
+(bdes -b \320k abcdef#@ < ) 147.69 432 T
+7 F
+(inputf) 306 432 T
+(ile) 349.18 432 T
+6 F
+( > ) 370.76 432 T
+7 F
+(outputf) 392.35 432 T
+(ile) 442.73 432 T
+0 F
+(because the option ) 72 408 T
+4 F
+(\320b) 164.26 408 T
+0 F
+( signi\336es ECB mode:) 176.93 408 T
+(text) 72 384 T
+6 F
+(t) 144 384 T
+(e) 171 384 T
+(s) 198 384 T
+(t) 225 384 T
+0 F
+(hex) 72 364 T
+10 F
+(74) 144 364 T
+(65) 171 364 T
+(73) 198 364 T
+(74) 225 364 T
+0 F
+(input) 72 344 T
+10 F
+(74) 144 344 T
+(65) 171 344 T
+(73) 198 344 T
+(74) 225 344 T
+(00) 252 344 T
+(00) 279 344 T
+(00) 306 344 T
+(04) 333 344 T
+0 F
+(output) 72 324 T
+10 F
+(0d) 144 324 T
+(8a) 171 324 T
+(6e) 198 324 T
+(57) 225 324 T
+(9c) 252 324 T
+(8f) 279 324 T
+(27) 306 324 T
+(5d) 333 324 T
+0 F
+-0.31 (Finally) 108 304 P
+-0.31 (, if the length of the message is indeed a multiple of the block size, an extra block of) 141.21 304 P
+0.83 (all 0 bits is added. Suppose ) 72 284 P
+7 F
+1.99 (inputf) 210.57 284 P
+1.99 (ile) 253.74 284 P
+0 F
+0.83 ( contains \322) 275.33 284 P
+6 F
+1.99 (test) 328.28 284 P
+9 F
+0.83 (\277) 357.07 284 P
+0 F
+0.83 (\323, and it is encrypted in 40-bit CFB) 364.96 284 P
+1.51 (mode using the key \322) 72 264 P
+6 F
+3.62 (abcdef#@) 179.96 264 P
+0 F
+1.51 (\323 and the initialization vector ) 237.53 264 P
+6 F
+3.62 (0x0123456789abcdef) 387.97 264 P
+0 F
+1.51 (; the) 517.5 264 P
+(command is) 72 244 T
+6 F
+-0.99 (bdes -f40 -v0x0123456789abcdef -kabcdef#@ < ) 72 220 P
+7 F
+-0.99 (inputf) 383.67 220 P
+-0.99 (ile) 426.85 220 P
+6 F
+-0.99 ( > ) 448.43 220 P
+7 F
+-0.99 (outputf) 468.04 220 P
+-0.99 (ile) 518.41 220 P
+0 F
+0.16 (because the option ) 72 196 P
+4 F
+0.16 (\320f40 ) 164.75 196 P
+0 F
+0.16 (signi\336es 40-bit CFB mode, and ) 189.89 196 P
+4 F
+0.16 (-v0x01234566789abcdef) 343.96 196 P
+0 F
+0.16 ( sets the initial-) 465.89 196 P
+(ization vector \050note that spaces between the option and its ar) 72 176 T
+(gument are optional\051:) 361.57 176 T
+(text) 72 152 T
+6 F
+(t) 144 152 T
+(e) 171 152 T
+(s) 198 152 T
+(t) 225 152 T
+9 F
+(\277) 252 152 T
+0 F
+(hex) 72 132 T
+10 F
+(74) 144 132 T
+(65) 171 132 T
+(73) 198 132 T
+(74) 225 132 T
+(0a) 252 132 T
+0 F
+(input) 72 112 T
+10 F
+(74) 144 112 T
+(65) 171 112 T
+(73) 198 112 T
+(74) 225 112 T
+(0a) 252 112 T
+(00) 279 112 T
+(00) 306 112 T
+(00) 333 112 T
+(00) 360 112 T
+(00) 387 112 T
+0 F
+(output) 72 92 T
+10 F
+(e2) 144 92 T
+(c2) 171 92 T
+(69) 198 92 T
+(a4) 225 92 T
+(5b) 252 92 T
+(3c) 279 92 T
+(3d) 306 92 T
+(b3) 333 92 T
+(f5) 360 92 T
+(3c) 387 92 T
+FMENDPAGE
+%%EndPage: "4" 7
+1 12 /Times-BoldItalic FMDEFINEFONT
+2 14 /Symbol FMDEFINEFONT
+%%Page: "5" 7
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 5 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+(Note here the block size is 40 bits \0505 bytes\051, not 64 bits \0508 bytes\051.) 108 712 T
+-0.4 (This technique allows complete compatibility with Sun\325) 108 688 P
+-0.4 (s ) 374.11 688 P
+3 F
+-0.4 (des) 381.37 688 P
+0 F
+-0.4 ( program. In Sun\325) 397.36 688 P
+-0.4 (s implemen-) 480.77 688 P
+0.02 (tation, padding is done with random bytes rather than bytes containing all zero bits. Cryptograph-) 72 668 P
+0.85 (ically) 72 648 P
+0.85 (, this makes no dif) 97.87 648 P
+0.85 (ference, as the DES is a suf) 189.32 648 P
+0.85 (\336ciently good random cipher to obscure the) 325.74 648 P
+(input \050see for example [2], Chapter 6\051, and known plaintext attacks are very dif) 72 628 T
+(\336cult [1].) 451.82 628 T
+4 F
+(5. Differ) 72 596 T
+(ences Between the Standard CFB and OFB Modes and ) 114.41 596 T
+1 F
+(bdes) 397.26 596 T
+0 F
+-0.11 (The UNIX operating system treats all \336les as streams of 8-bit bytes. In order to implement) 108 572 P
+-0.08 (the CFB and OFB modes properly) 72 552 P
+-0.08 (, it would be necessary to read ) 235.74 552 P
+3 F
+-0.08 (k) 383.74 552 P
+0 F
+-0.08 ( bits from the \336le, where ) 389.07 552 P
+3 F
+-0.08 (k) 509.51 552 P
+0 F
+-0.08 ( is an) 514.84 552 P
+0.98 (integer between 1 and 64 inclusive. However) 72 532 P
+0.98 (, this would require considerable buf) 294.22 532 P
+0.98 (fering and be) 474.77 532 P
+0.23 (quite inef) 72 512 P
+0.23 (\336cient and prohibitively slow) 117.65 512 P
+0.23 (. For these reasons, the current implementation of ) 258.48 512 P
+3 F
+0.23 (bdes) 501.48 512 P
+0 F
+0.23 ( re-) 523.46 512 P
+0.47 (quires that ) 72 492 P
+3 F
+0.47 (k) 126.23 492 P
+0 F
+0.47 ( be a multiple of 8, so that an integral number of bytes will always be read from the) 131.56 492 P
+(\336le. Other than this change, this mode is implemented as described in [3].) 72 472 T
+-0.58 (A similar observation holds for the alternate CFB mode described in [3]. Here, only the low) 108 448 P
+0.23 (7 bits of each byte are signi\336cant, and hence the parameter ) 72 428 P
+3 F
+0.23 (k) 358.95 428 P
+0 F
+0.23 ( is an integer from 1 to 56 inclusive;) 364.28 428 P
+(bdes requires k to be a multiple of 7. The high-order bit is retained for encryption and decryption,) 72 408 T
+(but output \050whether from encryption or decryption\051 always has the high-order bit set to zero.) 72 388 T
+4 F
+(6. Message Authentication Code Modes) 72 356 T
+0 F
+0.57 (The Data Encryption Standard provides two modes of authentication, each providing be-) 108 332 P
+1.27 (tween 1 and 64 bits of authentication data. In both cases an ) 72 312 P
+3 F
+1.27 (n) 373.32 312 P
+0 F
+1.27 (-bit message authentication code) 379.32 312 P
+0.62 (\050MAC\051 is generated, where 1) 72 292 P
+2 F
+0.73 ( ) 214.71 292 P
+9 F
+0.62 (\243) 218.94 292 P
+0 F
+0.62 ( ) 225.52 292 P
+3 F
+0.62 (n) 229.15 292 P
+0 F
+0.62 ( ) 235.14 292 P
+9 F
+0.62 (\243) 238.76 292 P
+0 F
+0.62 ( 64. The \336rst is based on the CBC encryption mode, and the) 245.35 292 P
+(second on CFB mode. Both work the same.) 72 272 T
+0.13 (First, the \336le is padded to a multiple of the block size by appending enough zero bits. It is) 108 248 P
+-0.16 (then encrypted using the standard CBC \050or CFB\051 algorithm, but
+all encrypted text is discarded ex-) 72 228 P
+-0.44 (cept for the last block. The ) 72 208 P
+3 F
+-0.44 (n) 200.9 208 P
+0 F
+-0.44 ( leading bits of the last block are used as the MAC. Note that the block) 206.9 208 P
+(size constrains the number of bits available as the MAC.) 72 188 T
+0.71 (The implementation allows the user to specify that the MAC is to be computed in either) 108 164 P
+-0.01 (CBC or CFB mode, and the user can specify any number of bits from 1 to 64 inclusive. However) 72 144 P
+-0.01 (,) 537 144 P
+-0.11 (because the UNIX operating system can only output bits in multiples of 8, if the number of bits of) 72 124 P
+-0.08 (MAC is not a multiple of 8, the MAC will be right-padded with the minimum number of zero bits) 72 104 P
+-0.31 (necessary to make the MAC length be a multiple of 8. However) 72 84 P
+-0.31 (, note that as the standard \050[3], Ap-) 374.6 84 P
+FMENDPAGE
+%%EndPage: "5" 8
+%%Page: "6" 8
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 6 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+-0.14 (pendix F\051 requires an incomplete \336nal block be right-padded with
+zeroes, the technique of forcing) 72 712 P
+(the last octet to contain the number of bytes in the message is ) 72 692 T
+3 F
+(not) 369.47 692 T
+0 F
+( used here.) 384.8 692 T
+-0.39 (For example, suppose ) 108 668 P
+7 F
+-0.94 (inputf) 214.76 668 P
+-0.94 (ile) 257.93 668 P
+0 F
+-0.39 ( contains \322) 279.52 668 P
+6 F
+-0.94 (This) 330.04 668 P
+8 F
+-0.43 (z) 358.82 668 P
+6 F
+-0.94 (is) 363.8 668 P
+8 F
+-0.43 (z) 378.19 668 P
+6 F
+-0.94 (a) 383.17 668 P
+8 F
+-0.43 (z) 390.36 668 P
+6 F
+-0.94 (test) 395.34 668 P
+9 F
+-0.39 (\277) 424.13 668 P
+0 F
+-0.39 (\323, and a 64-bit MAC is) 432.02 668 P
+-0.73 (to be generated using CBC mode, the key \322) 72 648 P
+6 F
+-1.74 (abcdef#@) 274.39 648 P
+0 F
+-0.73 (\323 and the initialization vector ) 331.96 648 P
+6 F
+-1.74 (0x0) 471.23 648 P
+0 F
+-0.73 (; the com-) 492.82 648 P
+(mand is) 72 628 T
+6 F
+(bdes -m 64 -k abcdef#@ < ) 136.89 604 T
+7 F
+(inputf) 316.79 604 T
+(ile) 359.97 604 T
+6 F
+( > ) 381.56 604 T
+7 F
+(outputf) 403.15 604 T
+(ile) 453.52 604 T
+0 F
+(as CBC is the default encryption mode and ) 72 580 T
+6 F
+(0x0) 281.2 580 T
+0 F
+( the default initialization vector:) 302.79 580 T
+(text) 72 556 T
+6 F
+(T) 117 556 T
+(h) 144 556 T
+(i) 171 556 T
+(s) 198 556 T
+8 F
+(z) 225 556 T
+6 F
+(i) 252 556 T
+(s) 279 556 T
+8 F
+(z) 306 556 T
+6 F
+(a) 333 556 T
+8 F
+(z) 360 556 T
+6 F
+(t) 387 556 T
+(e) 414 556 T
+(s) 441 556 T
+(t) 468 556 T
+9 F
+(\277) 495 556 T
+0 F
+(hex) 72 536 T
+10 F
+(54) 117 536 T
+(68) 144 536 T
+(69) 171 536 T
+(73) 198 536 T
+(20) 225 536 T
+(69) 252 536 T
+(73) 279 536 T
+(20) 306 536 T
+(61) 333 536 T
+(20) 360 536 T
+(74) 387 536 T
+(65) 414 536 T
+(73) 441 536 T
+(74) 468 536 T
+(0a) 495 536 T
+0 F
+(input) 72 516 T
+10 F
+(54) 117 516 T
+(68) 144 516 T
+(69) 171 516 T
+(73) 198 516 T
+(20) 225 516 T
+(69) 252 516 T
+(73) 279 516 T
+(20) 306 516 T
+(61) 333 516 T
+(20) 360 516 T
+(74) 387 516 T
+(65) 414 516 T
+(73) 441 516 T
+(74) 468 516 T
+(0a) 495 516 T
+(00) 522 516 T
+0 F
+(output) 72 496 T
+10 F
+(43) 117 496 T
+(18) 144 496 T
+(de) 171 496 T
+(74) 198 496 T
+(24) 225 496 T
+(a9) 252 496 T
+(65) 279 496 T
+(d1) 306 496 T
+0 F
+0.04 (Notice that the text is 15 characters long, so there are 7 bytes following the last full block.) 108 476 P
+3 F
+(Bdes) 72 456 T
+0 F
+( pads this to a full block by appending a zero-\336lled byte. The result is then encrypted and the) 95.32 456 T
+(last block of output is used as the MAC.) 72 436 T
+0.06 (As another example, suppose we used the same text, and wanted a 36-bit MAC to be gen-) 108 412 P
+6.91 (erated using 40-bit CFB mode, the key \322) 72 392 P
+6 F
+16.58 (abcdef#@) 314.9 392 P
+0 F
+6.91 (\323 and the initialization vector) 372.47 392 P
+6 F
+(0x0123456789abcdef) 72 372 T
+0 F
+(; the command is) 201.53 372 T
+6 F
+(bdes -m 36 -f 40 -v 0x0123456789abcdef < ) 79.32 348 T
+7 F
+(inputf) 374.36 348 T
+(ile) 417.54 348 T
+6 F
+( > ) 439.13 348 T
+7 F
+(outputf) 460.71 348 T
+(ile) 511.09 348 T
+0 F
+-0.19 (where ) 72 324 P
+4 F
+-0.19 (\320m 36) 104.11 324 P
+0 F
+-0.19 ( is the option to generate a 36-bit MAC, ) 134.91 324 P
+4 F
+-0.19 (\320f 40) 327.79 324 P
+0 F
+-0.19 ( indicates 40-bit CFB is to be used, and) 352.58 324 P
+4 F
+-0.31 (\320v 0x123456789abcdef) 72 304 P
+0 F
+-0.31 ( sets the initialization vector) 186.62 304 P
+-0.31 (. Note that, as the key is not given on the com-) 319.95 304 P
+(mand line, the user will be prompted for it. It gives:) 72 284 T
+(text) 72 260 T
+6 F
+(T) 117 260 T
+(h) 144 260 T
+(i) 171 260 T
+(s) 198 260 T
+8 F
+(z) 225 260 T
+6 F
+(i) 252 260 T
+(s) 279 260 T
+8 F
+(z) 306 260 T
+6 F
+(a) 333 260 T
+8 F
+(z) 360 260 T
+6 F
+(t) 387 260 T
+(e) 414 260 T
+(s) 441 260 T
+(t) 468 260 T
+9 F
+(\277) 495 260 T
+0 F
+(hex) 72 240 T
+10 F
+(54) 117 240 T
+(68) 144 240 T
+(69) 171 240 T
+(73) 198 240 T
+(20) 225 240 T
+(69) 252 240 T
+(73) 279 240 T
+(20) 306 240 T
+(61) 333 240 T
+(20) 360 240 T
+(74) 387 240 T
+(65) 414 240 T
+(73) 441 240 T
+(74) 468 240 T
+(0a) 495 240 T
+0 F
+(input) 72 220 T
+10 F
+(54) 117 220 T
+(68) 144 220 T
+(69) 171 220 T
+(73) 198 220 T
+(20) 225 220 T
+(69) 252 220 T
+(73) 279 220 T
+(20) 306 220 T
+(61) 333 220 T
+(20) 360 220 T
+(74) 387 220 T
+(65) 414 220 T
+(73) 441 220 T
+(74) 468 220 T
+(0a) 495 220 T
+0 F
+(output) 72 200 T
+10 F
+(2b) 117 200 T
+(18) 144 200 T
+(68) 171 200 T
+(2d) 198 200 T
+(60) 225 200 T
+0 F
+0.19 (Note that the MAC is padded on the right by four zero bits to produce \336ve characters that) 108 180 P
+(can be output.) 72 160 T
+4 F
+(7. Differ) 72 128 T
+(ences Between ) 114.41 128 T
+1 F
+(bdes) 191.01 128 T
+4 F
+( and Sun\325) 212.99 128 T
+(s DES Implementation) 261.88 128 T
+0 F
+0.02 (The program ) 108 104 P
+3 F
+0.02 (bdes) 173.33 104 P
+0 F
+0.02 ( is designed to be completely compatible with Sun Microsystems, Inc.\325) 195.31 104 P
+0.02 (s) 535.33 104 P
+0.57 (implementation of the Data Encryption Standard, called ) 72 84 P
+3 F
+0.57 (des) 347.14 84 P
+0 F
+0.57 ( and described in [4]. Thus, \336les en-) 363.13 84 P
+FMENDPAGE
+%%EndPage: "6" 9
+%%Page: "7" 9
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 7 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+0.44 (crypted using ) 72 712 P
+3 F
+0.44 (des) 140.84 712 P
+0 F
+0.44 ( can be decrypted using ) 156.83 712 P
+3 F
+0.44 (bdes) 275.29 712 P
+0 F
+0.44 (, and vice versa, provided modes common to both) 297.27 712 P
+-0.34 (are used. However) 72 692 P
+-0.34 (, ) 160.41 692 P
+3 F
+-0.34 (bdes) 166.06 692 P
+0 F
+-0.34 ( does not allow \336les to be named on the command line, nor does it support) 188.05 692 P
+-0.68 (hardware devices \050and so the ) 72 672 P
+4 F
+-0.68 (-s) 210.83 672 P
+0 F
+-0.68 ( and ) 219.49 672 P
+4 F
+-0.68 (-f) 241.45 672 P
+0 F
+-0.68 ( options of Sun\325) 249.44 672 P
+-0.68 (s ) 323.71 672 P
+3 F
+-0.68 (des) 330.7 672 P
+0 F
+-0.68 ( are not available\051. Further) 346.69 672 P
+-0.68 (, as encryption) 471.07 672 P
+-0.05 (is the default, the Sun ) 72 652 P
+3 F
+-0.05 (des) 179.01 652 P
+0 F
+-0.05 ( ) 195 652 P
+4 F
+-0.05 (-e) 197.95 652 P
+0 F
+-0.05 ( option is not recognized. As the manual page to ) 207.27 652 P
+3 F
+-0.05 (bdes) 441.6 652 P
+0 F
+-0.05 ( is in the appen-) 463.59 652 P
+(dix, these dif) 72 632 T
+(ferences will not be elaborated upon further) 134.08 632 T
+(.) 343.24 632 T
+0.44 (Sun\325) 108 608 P
+0.44 (s ) 130 608 P
+3 F
+0.44 (des) 138.1 608 P
+0 F
+0.44 ( supports the use of special-purpose hardware to encrypt and decrypt. Although) 154.09 608 P
+3 F
+1.33 (bdes) 72 588 P
+0 F
+1.33 ( does not directly support the use of such hardware, it uses the library routine ) 93.98 588 P
+3 F
+1.33 (encrypt) 487.05 588 P
+0 F
+1.33 (\0503\051,) 523.02 588 P
+-0.09 (which may) 72 568 P
+-0.09 (. Hardware support was not included directly to support as lar) 124.1 568 P
+-0.09 (ge a number of platforms) 419.11 568 P
+(as possible with installers needing to know as little about the hardware as possible.) 72 548 T
+-0.08 (Sun\325) 108 524 P
+-0.08 (s ) 130 524 P
+3 F
+-0.08 (des) 137.58 524 P
+0 F
+-0.08 ( supports only the CBC and ECB encryption modes; ) 153.57 524 P
+3 F
+-0.08 (bdes) 407.07 524 P
+0 F
+-0.08 ( supports all modes de-) 429.05 524 P
+0.26 (scribed in [3] \050although CFB and OFB are not completely supported\051 as well as both CBC-based) 72 504 P
+(and CFB-based MACs.) 72 484 T
+0.15 (Although input with length not a multiple of the block size is handled in the same way by) 108 460 P
+-0.47 (both ) 72 440 P
+3 F
+-0.47 (des) 95.85 440 P
+0 F
+-0.47 ( and ) 111.84 440 P
+3 F
+-0.47 (bdes) 134.21 440 P
+0 F
+-0.47 (, dif) 156.19 440 P
+-0.47 (ferent values of the padding bytes are used in all but the last byte of the input.) 174.82 440 P
+(Where ) 72 420 T
+3 F
+(bdes) 106.96 420 T
+0 F
+( puts zero bytes, ) 128.94 420 T
+3 F
+(des) 209.89 420 T
+0 F
+( puts bytes containing random values. The reason for Sun\325) 225.87 420 T
+(s doing) 505.02 420 T
+0.47 (so is to prevent a known plaintext attack on the \336le should an
+attacker determine that the input\325) 72 400 P
+0.47 (s) 535.33 400 P
+-0.29 (length were a multiple of the block size. W) 72 380 P
+-0.29 (ith ) 276.05 380 P
+3 F
+-0.29 (bdes) 291.43 380 P
+0 F
+-0.29 (, the plaintext contents of the last block of input) 313.41 380 P
+0.31 (for such a \336le is known \050a block with all bits zero\051. W) 72 360 P
+0.31 (ith ) 333.99 360 P
+3 F
+0.31 (des) 349.96 360 P
+0 F
+0.31 (, the plaintext contents of that block) 365.95 360 P
+0.73 (are not known. Cryptanalytically) 72 340 P
+0.73 (, given the information about the strength of the DES currently) 231.29 340 P
+0.2 (known, it is widely believed that known plaintext attacks are infeasible
+\050see for example [1]\051 and) 72 320 P
+1.86 (so initializing and invoking the pseudorandom number generator seems unnecessary) 72 300 P
+1.86 (. But this) 492.63 300 P
+(means that ciphertexts produced from a plaintext by ) 72 280 T
+3 F
+(bdes) 324.48 280 T
+0 F
+( and ) 346.47 280 T
+3 F
+(des) 369.78 280 T
+0 F
+( will dif) 385.77 280 T
+(fer in the last block.) 423.54 280 T
+4 F
+(Refer) 72 248 T
+(ences) 100.41 248 T
+0 F
+([1]) 72 224 T
+0.37 (D. Denning, \322The Data Encryption Standard: Fifteen Y) 108 224 P
+0.37 (ears of Public Scrutiny) 374.87 224 P
+0.37 (,\323 ) 484.8 224 P
+3 F
+0.37 (Pr) 496.49 224 P
+0.37 (oceed-) 508.04 224 P
+-0.47 (ings of the Sixth Annual Computer Security Applications Confer) 108 204 P
+-0.47 (ence) 411.65 204 P
+0 F
+-0.47 ( pp. x\320xv \050Dec. 1990\051.) 433.62 204 P
+([2]) 72 180 T
+(A. Konheim, ) 108 180 T
+3 F
+(Cryptography: A Primer) 173.29 180 T
+0 F
+(, John W) 291.4 180 T
+(iley and Sons, Inc., New Y) 333.9 180 T
+(ork, NY \0501981\051.) 461.94 180 T
+([3]) 72 156 T
+3 F
+0.63 (DES Modes of Operation) 108 156 P
+0 F
+0.63 (, Federal Information Processing Standards Publication 81, Na-) 231.47 156 P
+-0.07 (tional Bureau of Standards, U.S. Department of Commerce, W) 108 136 P
+-0.07 (ashington, DC \050Dec. 1980\051.) 407.62 136 P
+([4]) 72 112 T
+3 F
+(UNIX User) 108 112 T
+(\325) 162.74 112 T
+(s Manual) 165.18 112 T
+0 F
+(, Sun Microsystems Inc., Mountain V) 210.16 112 T
+(iew) 390 112 T
+(, CA \050Mar) 406.54 112 T
+(. 1988\051.) 455.51 112 T
+4 F
+(Appendix. The UNIX System Manual Page for ) 72 80 T
+1 F
+(bdes) 313.2 80 T
+FMENDPAGE
+%%EndPage: "7" 10
+1 11 /Times-Bold FMDEFINEFONT
+%%Page: "8" 10
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 8 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+1 F
+0 X
+(NAME) 72 712.67 T
+0 F
+(bdes - encrypt/decrypt using the Data Encryption Standard) 108 689 T
+1 F
+(SYNOPSIS) 72 663.67 T
+4 F
+(bdes) 108 640 T
+0 F
+( [) 131.33 640 T
+3 F
+( ) 138.32 640 T
+4 F
+(-abdp) 141.32 640 T
+0 F
+( ] [ ) 171.31 640 T
+4 F
+(-F) 188.3 640 T
+0 F
+( ) 199.62 640 T
+3 F
+(b) 202.62 640 T
+0 F
+( ] [ ) 208.61 640 T
+4 F
+(-f) 225.6 640 T
+0 F
+( ) 233.58 640 T
+3 F
+(b) 236.58 640 T
+0 F
+( ] [ ) 242.58 640 T
+4 F
+(-k) 259.56 640 T
+0 F
+( ) 270.22 640 T
+3 F
+(key) 273.22 640 T
+0 F
+( ] [ ) 289.2 640 T
+4 F
+(-m) 306.18 640 T
+0 F
+( ) 320.16 640 T
+3 F
+(b) 323.16 640 T
+0 F
+( ] [ ) 329.16 640 T
+4 F
+(-o) 346.14 640 T
+0 F
+( ) 356.13 640 T
+3 F
+(b) 359.13 640 T
+0 F
+( ] [ ) 365.13 640 T
+4 F
+(-v) 382.11 640 T
+0 F
+( ) 392.1 640 T
+3 F
+(vector) 395.1 640 T
+0 F
+( ]) 425.07 640 T
+1 F
+(DESCRIPTION) 72 614.67 T
+3 F
+-0.69 (Bdes) 108 591 P
+0 F
+-0.69 ( reads from the standard input and writes on the standard output. It implements all DES) 131.32 591 P
+-0.09 (modes of operation described in FIPS PUB 81 including alternative cipher feedback mode) 108 577 P
+0.74 (and both authentication modes. All modes but the electronic code book mode require an) 108 563 P
+-0.14 (initialization vector; if none is supplied, the zero vector is used. T) 108 549 P
+-0.14 (o protect the key and ini-) 420.44 549 P
+0.29 (tialization vector from being read by) 108 535 P
+3 F
+0.29 ( ps) 284.98 535 P
+0 F
+0.29 (\0501\051, ) 298.94 535 P
+3 F
+0.29 (bdes ) 319.21 535 P
+0 F
+0.29 (hides its ar) 344.48 535 P
+0.29 (guments on entry) 396.81 535 P
+0.29 (. If no ) 479.89 535 P
+3 F
+0.29 (key ) 512.74 535 P
+0 F
+0.29 (is) 532 535 P
+-0.61 (given, one is requested from the controlling terminal if that can be opened, or from the stan-) 108 521 P
+(dard input if not.) 108 507 T
+-0.17 (The key and initialization vector are taken as sequences of ) 108 489 P
+5 F
+-0.14 (ASCII) 389.38 489 P
+0 F
+-0.17 ( characters which are then) 415.48 489 P
+-0.35 (mapped into their bit representations. If either begins with
+\3240x\325 or \3240X\325, that one is taken as) 108 475 P
+1.02 (a sequence of hexadecimal digits indicating the bit pattern; if either begins with \3240b\325 or) 108 461 P
+-0.73 (\3240B\325, that one is taken as a sequence of binary digits
+indicating the bit pattern. In either case,) 108 447 P
+-0.37 (only the leading 64 bits of the key or initialization vector are used, and if fewer than 64 bits) 108 433 P
+0.35 (are provided, enough 0 bits are appended to pad the key to 64 bits. Note that if the key is) 108 419 P
+0.03 (not entered on the command line, it is interpreted in the same way) 108 405 P
+0.03 (, because with 4.4 BSD,) 424.31 405 P
+-0.36 (the password reading function ) 108 391 P
+3 F
+-0.36 (getpass) 254.45 391 P
+0 F
+-0.36 (\0503\051 allows enough characters for either hex or binary) 290.43 391 P
+(keys to be entered.) 108 377 T
+0.04 (According to the DES standard, the low-order bit of each character in the key string is de-) 108 359 P
+-0.18 (leted. Since most ) 108 345 P
+5 F
+-0.15 (ASCII) 192.75 345 P
+0 F
+-0.18 ( representations set the high-order bit to 0, simply deleting the low-) 218.84 345 P
+-0.29 (order bit ef) 108 331 P
+-0.29 (fectively reduces the size of the key space from 2) 160.49 331 P
+5 F
+-0.24 (56) 394.67 335.8 P
+0 F
+-0.29 ( to 2) 404.67 331 P
+5 F
+-0.24 (48) 425.41 335.8 P
+0 F
+-0.29 ( keys. T) 435.4 331 P
+-0.29 (o prevent this,) 472.29 331 P
+-0.46 (the high-order bit must be a function depending in part upon the low-order bit; so, the high-) 108 317 P
+0.11 (order bit is set to whatever value gives odd parity) 108 303 P
+0.11 (. This preserves the key space size. Note) 345.05 303 P
+(this resetting of the parity bit is ) 108 289 T
+3 F
+(not) 260.92 289 T
+0 F
+( done if the key is given in binary or hex.) 276.24 289 T
+-0.38 (By default, the standard input is encrypted using cipher block chaining mode and is written) 108 271 P
+0.18 (to the standard output. Using the same key for encryption and decryption preserves plain-) 108 257 P
+(text, so) 108 243 T
+( bdes ) 225.81 225 T
+3 F
+(key) 253.79 225 T
+0 F
+( < plaintext | bdes \320i ) 269.77 225 T
+3 F
+(key) 370.21 225 T
+0 F
+( ) 386.19 225 T
+(is a very expensive equivalent of ) 108 201 T
+3 F
+(cat) 268.54 201 T
+0 F
+(\0501\051.) 283.2 201 T
+(Options are:) 108 183 T
+( ) 108 165 T
+4 F
+(\320a) 111 165 T
+0 F
+-0.75 (The key and initialization vector strings are to be taken as ) 144 165 P
+5 F
+-0.62 (ASCII) 415.89 165 P
+0 F
+-0.75 ( suppressing the spe-) 441.98 165 P
+0.3 (cial interpretation given to leading \3240x\325, \3240X\325, \3240b\325,
+and \3240B\325 characters. Note this) 144 151 P
+(\337ag applies to ) 144 137 T
+3 F
+(both) 214.29 137 T
+0 F
+( the key and initialization vector) 235.62 137 T
+(.) 389.85 137 T
+4 F
+(\320b) 108 119 T
+0 F
+(Use electronic code book mode.) 144 119 T
+4 F
+(\320d) 108 101 T
+0 F
+(Decrypt the input.) 144 101 T
+FMENDPAGE
+%%EndPage: "8" 11
+%%Page: "9" 11
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 9 of 11) 479.71 34.7 T
+72 72 540 720 R
+7 X
+V
+4 F
+0 X
+(\320f) 108 712 T
+0 F
+( ) 117.99 712 T
+3 F
+(b) 120.99 712 T
+0 F
+-0.29 (Use ) 144 712 P
+3 F
+-0.29 (b) 165.36 712 P
+0 F
+-0.29 (-bit cipher feedback mode. Currently ) 171.35 712 P
+3 F
+-0.29 (b) 350.42 712 P
+0 F
+-0.29 ( must be a multiple of 8 between 8 and) 356.42 712 P
+(64 inclusive \050this does not conform to the standard CFB mode speci\336cation\051.) 144 698 T
+4 F
+(\320F) 108 680 T
+0 F
+( ) 121.32 680 T
+3 F
+(b) 124.32 680 T
+0 F
+-0.29 (Use ) 144 680 P
+3 F
+-0.29 (b) 165.36 680 P
+0 F
+-0.29 (-bit alternative cipher feedback mode. Currently ) 171.36 680 P
+3 F
+-0.29 (b) 403.77 680 P
+0 F
+-0.29 ( must be a multiple of 7 be-) 409.77 680 P
+-0.12 (tween 7 and 56 inclusive \050this does not conform to the alternative CFB mode spec-) 144 666 P
+(i\336cation\051.) 144 652 T
+4 F
+(\320k) 108 634 T
+0 F
+( ) 120.67 634 T
+3 F
+(key) 123.66 634 T
+0 F
+0.37 (Use the string ) 144 616 P
+3 F
+0.37 (key) 214.74 616 P
+0 F
+0.37 ( as the cryptographic key) 230.72 616 P
+0.37 (. If this ar) 352.01 616 P
+0.37 (gument is not given, the user) 399.54 616 P
+(will be prompted for the key) 144 602 T
+(.) 280.12 602 T
+4 F
+(\320m) 108 584 T
+0 F
+( ) 123.99 584 T
+3 F
+(b) 126.99 584 T
+0 F
+0.71 (Compute a message authentication code \050MAC\051 of ) 144 584 P
+3 F
+0.71 (b) 395.78 584 P
+0 F
+0.71 ( bits on the input. ) 401.77 584 P
+3 F
+0.71 (b) 491.94 584 P
+0 F
+0.71 ( must be) 497.94 584 P
+0.11 (between 1 and 64 inclusive; if ) 144 570 P
+3 F
+0.11 (b) 291.87 570 P
+0 F
+0.11 ( is not a multiple of 8, enough 0 bits will be added) 297.86 570 P
+-0.44 (to pad the MAC length to the nearest multiple of 8. Only the MAC is output. MACs) 144 556 P
+(are only available in cipher block chaining mode or in cipher feedback mode.) 144 542 T
+4 F
+(\320o) 108 524 T
+0 F
+( ) 119.99 524 T
+3 F
+(b) 122.99 524 T
+0 F
+-0.34 (Use ) 144 524 P
+3 F
+-0.34 (b) 165.31 524 P
+0 F
+-0.34 (-bit output feedback mode. Currently ) 171.31 524 P
+3 F
+-0.34 (b) 350.83 524 P
+0 F
+-0.34 ( must be a multiple of 8 between 8 and) 356.83 524 P
+(64 inclusive \050this does not conform to the OFB mode speci\336cation\051.) 144 510 T
+4 F
+(\320p) 108 492 T
+0 F
+-0.14 (Disable the resetting of the parity bit. This \337ag forces the parity bit of the key to be) 144 492 P
+0.03 (used as typed, rather than making each character be of odd parity) 144 478 P
+0.03 (. It is used only if) 455.91 478 P
+(the key is given in ) 144 464 T
+5 F
+(ASCII) 234.95 464 T
+0 F
+(.) 261.04 464 T
+4 F
+(\320v) 108 446 T
+0 F
+( ) 119.99 446 T
+3 F
+(vector) 122.99 446 T
+0 F
+-0.5 (Set the initialization vector to ) 144 428 P
+3 F
+-0.5 (v) 286.44 428 P
+0 F
+-0.5 (; the vector is interpreted in the same way as the key) 291.76 428 P
+-0.5 (.) 537 428 P
+(The vector is ignored in electronic codebook mode.) 144 414 T
+-0.55 (The DES is considered a very strong cryptosystem, and other than table lookup attacks, key) 108 396 P
+0.24 (search attacks, and Hellman\325) 108 382 P
+0.24 (s time-memory tradeof) 246.61 382 P
+0.24 (f \050all of which are very expensive and) 356.8 382 P
+0.66 (time-consuming\051, no cryptanalytic methods for breaking the DES are known in the open) 108 368 P
+0.33 (literature. No doubt the choice of keys and key security are the most vulnerable aspect of) 108 354 P
+3 F
+(bdes) 108 340 T
+0 F
+(.) 129.98 340 T
+4 F
+(IMPLEMENT) 72 314 T
+(A) 146.41 314 T
+(TION NOTES) 154.18 314 T
+0 F
+0.57 (For implementors wishing to write software compatible with this program, the following) 108 290 P
+-0.23 (notes are provided. This software is completely compatible with the implementation of the) 108 276 P
+(data encryption standard distributed by Sun Microsystems, Inc.) 108 262 T
+0.11 (In the ECB and CBC modes, plaintext is encrypted in units of 64 bits \0508 bytes, also called) 108 244 P
+0.52 (a block\051. T) 108 230 P
+0.52 (o ensure that the plaintext \336le is encrypted correctly) 160.49 230 P
+0.52 (, ) 413.01 230 P
+3 F
+0.52 (bdes ) 419.53 230 P
+0 F
+0.52 (will \050internally\051 ap-) 445.03 230 P
+0.29 (pend from 1 to 8 bytes, the last byte containing an integer stating how many bytes of that) 108 216 P
+-0.71 (\336nal block are from the plaintext \336le, and encrypt the resulting block. Hence, when decrypt-) 108 202 P
+0.27 (ing, the last block may contain from 0 to 7 characters present in the plaintext \336le, and the) 108 188 P
+-0.59 (last byte tells how many) 108 174 P
+-0.59 (. Note that if during decryption the last byte of the \336le does not con-) 221.46 174 P
+0.41 (tain an integer between 0 and 7, either the \336le has been corrupted or an incorrect key has) 108 160 P
+0.48 (been given. A similar mechanism is used for the OFB and CFB modes, except that those) 108 146 P
+0.26 (simply require the length of the input to be a multiple of the mode size, and the \336nal byte) 108 132 P
+-0.73 (contains an integer between 0 and one less than the number of bytes being used as the mode.) 108 118 P
+(\050This was another reason that the mode size must be a multiple of 8 for those modes.\051) 108 104 T
+FMENDPAGE
+%%EndPage: "9" 12
+%%Page: "10" 12
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 10 of 11) 473.71 34.7 T
+72 72 540 720 R
+7 X
+V
+0 X
+0.94 (Unlike Sun\325) 108 712 P
+0.94 (s implementation, unused bytes of that last block are not \336lled with random) 166.58 712 P
+0.57 (data, but instead contain what was in those byte positions in the preceding block. This is) 108 698 P
+(quicker and more portable, and does not weaken the encryption signi\336cantly) 108 684 T
+(.) 473.95 684 T
+0.36 (If the key is entered in ) 108 666 P
+5 F
+0.3 (ASCII) 220.76 666 P
+0 F
+0.36 (, the parity bits of the key characters are set so that each key) 246.85 666 P
+1.03 (character is of odd parity) 108 652 P
+1.03 (. Unlike Sun\325) 231.23 652 P
+1.03 (s implementation, it is possible to enter binary or) 296.92 652 P
+-0.57 (hexadecimal keys on the command line, and if this is done, the parity bits are ) 108 638 P
+3 F
+-0.57 (not ) 472.85 638 P
+0 F
+-0.57 (reset. This) 490.61 638 P
+(allows testing using arbitrary bit patterns as keys.) 108 624 T
+0.64 (The Sun implementation always uses an initialization vector of 0 \050that is, all zeroes\051. By) 108 606 P
+(default, ) 108 592 T
+3 F
+(bdes ) 147.3 592 T
+0 F
+(does too, but this may be changed from the command line.) 172.29 592 T
+4 F
+(FILES) 72 566 T
+0 F
+(/dev/tty) 108 542 T
+(controlling terminal for typed key) 180 542 T
+4 F
+(SEE ALSO) 72 516 T
+3 F
+(crypt) 108 492 T
+0 F
+(\0501\051, ) 132.65 492 T
+3 F
+(crypt) 152.63 492 T
+0 F
+(\0503\051) 177.27 492 T
+3 F
+-0.4 (Data Encryption Standar) 108 474 P
+-0.4 (d) 228.02 474 P
+0 F
+-0.4 (, Federal Information Processing Standard #46, National Bureau) 234.02 474 P
+(of Standards, U.S. Department of Commerce, W) 108 460 T
+(ashington DC \050Jan. 1977\051.) 340.2 460 T
+3 F
+0.16 (DES) 108 442 P
+0 F
+0.16 ( ) 129.98 442 P
+3 F
+0.16 (Modes of Operation, ) 133.15 442 P
+0 F
+0.16 (Federal Information Processing Standard #81, National Bureau) 236.24 442 P
+(of Standards, U.S. Department of Commerce, W) 108 428 T
+(ashington DC \050Dec. 1980\051.) 340.2 428 T
+2.75 (Dorothy Denning, ) 108 410 P
+3 F
+2.75 (Cryptography and Data Security) 203.77 410 P
+0 F
+2.75 (, Addison-W) 368.8 410 P
+2.75 (esley Publishing Co.,) 432.55 410 P
+(Reading, MA \2511982.) 108 396 T
+-0.19 ( Matt Bishop, \322Implementation Notes on ) 108 378 P
+3 F
+-0.19 (bdes) 305.76 378 P
+0 F
+-0.19 (\0501\051\323, T) 327.75 378 P
+-0.19 (echnical Report PCS-TR-91-158, De-) 359.35 378 P
+0.34 (partment of Mathematics and Computer Science, Dartmouth College, Hanover) 108 364 P
+0.34 (, NH \050Apr) 488.01 364 P
+0.34 (.) 537 364 P
+(1991\051.) 108 350 T
+4 F
+(CAUTION) 72 324 T
+0 F
+-0.55 (Certain speci\336c keys should be avoided because they introduce potential weaknesses; these) 108 300 P
+-0.44 (keys, called the ) 108 286 P
+3 F
+-0.44 (weak) 183.95 286 P
+0 F
+-0.44 ( and ) 208.6 286 P
+3 F
+-0.44 (semiweak) 231.03 286 P
+0 F
+-0.44 ( keys, are \050in hex notation, where ) 277.66 286 P
+6 F
+-1.06 (p) 437.45 286 P
+0 F
+-0.44 ( is either ) 444.64 286 P
+6 F
+-1.06 (0) 487.63 286 P
+0 F
+-0.44 ( or ) 494.82 286 P
+6 F
+-1.06 (1) 509.93 286 P
+0 F
+-0.44 (, and) 517.12 286 P
+6 F
+(P) 108 272 T
+0 F
+( is either ) 115.2 272 T
+6 F
+(e) 159.5 272 T
+0 F
+( or ) 166.7 272 T
+6 F
+(f) 182.68 272 T
+0 F
+(\051:) 189.88 272 T
+6 F
+(0x0p0p0p0p0p0p0p0p) 144 254 T
+(0x0p1P0p1P0p0P0p0P) 360 254 T
+(0x0pep0pep0pfp0pfp) 144 236 T
+(0x0pfP0pfP0pfP0pfP) 360 236 T
+(0x1P0p1P0p0P0p0P0p) 144 218 T
+(0x1P1P1P1P0P0P0P0P) 360 218 T
+(0x1Pep1Pep0Pfp0Pfp) 144 200 T
+(0x1PfP1PfP0PfP0PfP) 360 200 T
+(0xep0pep0pfp0pfp0p) 144 182 T
+(0xep1Pep1pfp0Pfp0P) 360 182 T
+(0xepepepepepepepep) 144 164 T
+(0xepfPepfPfpfPfpfP) 360 164 T
+(0xfP0pfP0pfP0pfP0p) 144 146 T
+(0xfP1PfP1PfP0PfP0P) 360 146 T
+(0xfPepfPepfPepfPep) 144 128 T
+(0xfPfPfPfPfPfPfPfP) 360 128 T
+0 F
+0.13 (The weakness of these keys is inherent in the DES algorithm \050see for example Moore and) 108 110 P
+-0.57 (Simmons, \322Cycle structure of the DES with weak and semi-weak keys,\323) 108 96 P
+3 F
+-0.57 ( Advances in Cryp-) 449.43 96 P
+(tology \320 Crypto \32486 Pr) 108 82 T
+(oceedings) 216.83 82 T
+0 F
+(, Springer) 264.79 82 T
+(-V) 311.85 82 T
+(erlag New Y) 323.17 82 T
+(ork, \2511987, pp. 9-32\051.) 383.25 82 T
+FMENDPAGE
+%%EndPage: "10" 13
+%%Page: "11" 13
+612 792 0 FMBEGINPAGE
+72 745.99 540 756 R
+7 X
+0 K
+V
+72 32.69 540 42.7 R
+V
+0 F
+0 X
+(Page 11 of 11) 473.71 34.7 T
+72 72 540 720 R
+7 X
+V
+4 F
+0 X
+(BUGS) 72 712 T
+0 F
+-0.18 (There is a controversy raging over whether the DES will still be secure in a few years. The) 108 688 P
+0.31 (advent of special-purpose hardware could reduce the cost of any of the methods of attack) 108 674 P
+(named above so that they are no longer computationally infeasible.) 108 660 T
+0.32 (Programs which display programs\325 ar) 108 642 P
+0.32 (guments may compromise the key and initialization) 289.59 642 P
+0.76 (vector if they are speci\336ed on the command line. T) 108 628 P
+0.76 (o avoid this ) 358.46 628 P
+3 F
+0.76 (bdes) 419.7 628 P
+0 F
+0.76 ( overwrites its ar) 441.68 628 P
+0.76 (gu-) 524.01 628 P
+(ments. However) 108 614 T
+(, the obvious race cannot currently be avoided.) 186.12 614 T
+0.25 (As the key or key schedule is kept in memory throughout the run of this program, the en-) 108 596 P
+(cryption can be compromised if memory is readable.) 108 582 T
+-0.4 (There is no warranty of merchantability nor any warranty of \336tness for a particular purpose) 108 564 P
+0.05 (nor any other warranty) 108 550 P
+0.05 (, either express or implied, as to the accuracy of the enclosed mate-) 216.95 550 P
+(rials or as to their suitability for any particular purpose.) 108 536 T
+-0.06 (Accordingly) 108 518 P
+-0.06 (, the user assumes full responsibility for their use. Further) 167.18 518 P
+-0.06 (, the author assumes) 442.93 518 P
+-0.25 (no obligation to furnish any assistance of any kind whatsoever) 108 504 P
+-0.25 (, or to furnish any additional) 404.69 504 P
+(information or documentation.) 108 490 T
+4 F
+(AUTHOR) 72 464 T
+0 F
+-0.54 (Matt Bishop, Department of Mathematics and Computer Science, Bradley Hall, Dartmouth) 108 440 P
+(College, Hanover) 108 426 T
+(, NH 03755) 192.12 426 T
+(Electronic mail addresses:) 108 408 T
+(Internet: Matt.Bishop@dartmouth.edu) 108 390 T
+(UUCP: decvax!dartvax!Matt.Bishop) 108 372 T
+FMENDPAGE
+%%EndPage: "11" 14
+%%Trailer
+%%BoundingBox: 0 0 612 792
+%%Pages: 13 1
+%%DocumentFonts: Helvetica-Bold
+%%+ Helvetica-BoldOblique
+%%+ Times-Roman
+%%+ Times-Bold
+%%+ Times-BoldItalic
+%%+ Times-Italic
+%%+ Courier
+%%+ Courier-Oblique
+%%+ ZapfDingbats
+%%+ Symbol
+%%+ Courier-Bold
diff --git a/usr.bin/biff/Makefile b/usr.bin/biff/Makefile
new file mode 100644
index 0000000..81cb86b
--- /dev/null
+++ b/usr.bin/biff/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= biff
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/biff/biff.1 b/usr.bin/biff/biff.1
new file mode 100644
index 0000000..89d560c
--- /dev/null
+++ b/usr.bin/biff/biff.1
@@ -0,0 +1,86 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)biff.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt BIFF 1
+.Os BSD 4
+.Sh NAME
+.Nm biff
+.Nd "be notified if mail arrives and who it is from"
+.Sh SYNOPSIS
+.Nm biff
+.Op Cm ny
+.Sh DESCRIPTION
+.Nm Biff
+informs the system whether you want to be notified when mail arrives
+during the current terminal session.
+.Pp
+Options supported by
+.Nm biff :
+.Bl -tag -width 4n
+.It Cm n
+Disables notification.
+.It Cm y
+Enables notification.
+.El
+.Pp
+When mail notification is enabled, the header and first few lines of
+the message will be printed on your screen whenever mail arrives.
+A
+.Dq Li biff y
+command is often included in the file
+.Pa \&.login
+or
+.Pa \&.profile
+to be executed at each login.
+.Pp
+.Nm Biff
+operates asynchronously.
+For synchronous notification use the
+.Ar MAIL
+variable of
+.Xr sh 1
+or the
+.Ar mail
+variable of
+.Xr csh 1 .
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr mail 1 ,
+.Xr sh 1 ,
+.Xr comsat 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/biff/biff.c b/usr.bin/biff/biff.c
new file mode 100644
index 0000000..8d816bf
--- /dev/null
+++ b/usr.bin/biff/biff.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)biff.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void usage __P((void));
+static void err __P((char *));
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ int ch;
+ char *name;
+
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((name = ttyname(STDERR_FILENO)) == NULL) {
+ (void)fprintf(stderr, "biff: unknown tty\n");
+ exit(2);
+ }
+
+ if (stat(name, &sb))
+ err(name);
+
+ if (*argv == NULL) {
+ (void)printf("is %s\n", sb.st_mode&0100 ? "y" : "n");
+ exit(sb.st_mode & 0100 ? 0 : 1);
+ }
+
+ switch(argv[0][0]) {
+ case 'n':
+ if (chmod(name, sb.st_mode & ~0100) < 0)
+ err(name);
+ break;
+ case 'y':
+ if (chmod(name, sb.st_mode | 0100) < 0)
+ err(name);
+ break;
+ default:
+ usage();
+ }
+ exit(sb.st_mode & 0100 ? 0 : 1);
+}
+
+static void
+err(name)
+ char *name;
+{
+ (void)fprintf(stderr, "biff: %s: %s\n", name, strerror(errno));
+ exit(2);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: biff [y | n]\n");
+ exit(2);
+}
diff --git a/usr.bin/cal/Makefile b/usr.bin/cal/Makefile
new file mode 100644
index 0000000..aed0afa
--- /dev/null
+++ b/usr.bin/cal/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= cal
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cal/README b/usr.bin/cal/README
new file mode 100644
index 0000000..638ac9d
--- /dev/null
+++ b/usr.bin/cal/README
@@ -0,0 +1,42 @@
+The cal(1) date routines were written from scratch, basically from first
+principles. The algorithm for calculating the day of week from any
+Gregorian date was "reverse engineered". This was necessary as most of
+the documented algorithms have to do with date calculations for other
+calendars (e.g. julian) and are only accurate when converted to gregorian
+within a narrow range of dates.
+
+1 Jan 1 is a Saturday because that's what cal says and I couldn't change
+that even if I was dumb enough to try. From this we can easily calculate
+the day of week for any date. The algorithm for a zero based day of week:
+
+ calculate the number of days in all prior years (year-1)*365
+ add the number of leap years (days?) since year 1
+ (not including this year as that is covered later)
+ add the day number within the year
+ this compensates for the non-inclusive leap year
+ calculation
+ if the day in question occurs before the gregorian reformation
+ (3 sep 1752 for our purposes), then simply return
+ (value so far - 1 + SATURDAY's value of 6) modulo 7.
+ if the day in question occurs during the reformation (3 sep 1752
+ to 13 sep 1752 inclusive) return THURSDAY. This is my
+ idea of what happened then. It does not matter much as
+ this program never tries to find day of week for any day
+ that is not the first of a month.
+ otherwise, after the reformation, use the same formula as the
+ days before with the additional step of subtracting the
+ number of days (11) that were adjusted out of the calendar
+ just before taking the modulo.
+
+It must be noted that the number of leap years calculation is sensitive
+to the date for which the leap year is being calculated. A year that occurs
+before the reformation is determined to be a leap year if its modulo of
+4 equals zero. But after the reformation, a year is only a leap year if
+its modulo of 4 equals zero and its modulo of 100 does not. Of course,
+there is an exception for these century years. If the modulo of 400 equals
+zero, then the year is a leap year anyway. This is, in fact, what the
+gregorian reformation was all about (a bit of error in the old algorithm
+that caused the calendar to be inaccurate.)
+
+Once we have the day in year for the first of the month in question, the
+rest is trivial.
diff --git a/usr.bin/cal/cal.1 b/usr.bin/cal/cal.1
new file mode 100644
index 0000000..80d95b2
--- /dev/null
+++ b/usr.bin/cal/cal.1
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Kim Letkeman.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cal.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CAL 1
+.Os
+.Sh NAME
+.Nm cal
+.Nd displays a calendar
+.Sh SYNOPSIS
+.Nm cal
+.Op Fl jy
+.Op Ar month Op Ar year
+.Sh DESCRIPTION
+.Nm Cal
+displays a simple calendar.
+If arguments are not specified,
+the current month is displayed.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl j
+Display julian dates (days one-based, numbered from January 1).
+.It Fl y
+Display a calendar for the current year.
+.El
+.Pp
+A single parameter specifies the year (1 - 9999) to be displayed;
+note the year must be fully specified:
+.Dq Li cal 89
+will
+.Em not
+display a calendar for 1989.
+Two parameters denote the month (1 - 12) and year.
+If no parameters are specified, the current month's calendar is
+displayed.
+.Pp
+A year starts on Jan 1.
+.Pp
+The Gregorian Reformation is assumed to have occurred in 1752 on the 3rd
+of September.
+By this time, most countries had recognized the reformation (although a
+few did not recognize it until the early 1900's.)
+Ten days following that date were eliminated by the reformation, so the
+calendar for that month is a bit unusual.
+.Sh HISTORY
+A
+.Nm
+command appeared in Version 6 AT&T UNIX.
diff --git a/usr.bin/cal/cal.c b/usr.bin/cal/cal.c
new file mode 100644
index 0000000..9bf218d
--- /dev/null
+++ b/usr.bin/cal/cal.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define THURSDAY 4 /* for reformation */
+#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
+
+#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
+#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
+
+#define MAXDAYS 42 /* max slots in a month array */
+#define SPACE -1 /* used in day array */
+
+static int days_in_month[2][13] = {
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+};
+
+int sep1752[MAXDAYS] = {
+ SPACE, SPACE, 1, 2, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+}, j_sep1752[MAXDAYS] = {
+ SPACE, SPACE, 245, 246, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+}, empty[MAXDAYS] = {
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+ SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
+};
+
+char *month_names[12] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December",
+};
+
+char *day_headings = "Su Mo Tu We Th Fr Sa";
+char *j_day_headings = " Su Mo Tu We Th Fr Sa";
+
+/* leap year -- account for gregorian reformation in 1752 */
+#define leap_year(yr) \
+ ((yr) <= 1752 ? !((yr) % 4) : \
+ !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
+
+/* number of centuries since 1700, not inclusive */
+#define centuries_since_1700(yr) \
+ ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define quad_centuries_since_1700(yr) \
+ ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define leap_years_since_year_1(yr) \
+ ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+int julian;
+
+void ascii_day __P((char *, int));
+void center __P((char *, int, int));
+void day_array __P((int, int, int *));
+int day_in_week __P((int, int, int));
+int day_in_year __P((int, int, int));
+void j_yearly __P((int));
+void monthly __P((int, int));
+void trim_trailing_spaces __P((char *));
+void usage __P((void));
+void yearly __P((int));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct tm *local_time;
+ time_t now;
+ int ch, month, year, yflag;
+
+ yflag = 0;
+ while ((ch = getopt(argc, argv, "jy")) != EOF)
+ switch(ch) {
+ case 'j':
+ julian = 1;
+ break;
+ case 'y':
+ yflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ month = 0;
+ switch(argc) {
+ case 2:
+ if ((month = atoi(*argv++)) < 1 || month > 12)
+ errx(1, "illegal month value: use 1-12");
+ /* FALLTHROUGH */
+ case 1:
+ if ((year = atoi(*argv)) < 1 || year > 9999)
+ errx(1, "illegal year value: use 1-9999");
+ break;
+ case 0:
+ (void)time(&now);
+ local_time = localtime(&now);
+ year = local_time->tm_year + 1900;
+ if (!yflag)
+ month = local_time->tm_mon + 1;
+ break;
+ default:
+ usage();
+ }
+
+ if (month)
+ monthly(month, year);
+ else if (julian)
+ j_yearly(year);
+ else
+ yearly(year);
+ exit(0);
+}
+
+#define DAY_LEN 3 /* 3 spaces per day */
+#define J_DAY_LEN 4 /* 4 spaces per day */
+#define WEEK_LEN 20 /* 7 * 3 - one space at the end */
+#define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
+#define HEAD_SEP 2 /* spaces between day headings */
+#define J_HEAD_SEP 2
+
+void
+monthly(month, year)
+ int month, year;
+{
+ int col, row, len, days[MAXDAYS];
+ char *p, lineout[30];
+
+ day_array(month, year, days);
+ len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+ (void)printf("%*s%s\n%s\n",
+ ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
+ lineout, julian ? j_day_headings : day_headings);
+ for (row = 0; row < 6; row++) {
+ for (col = 0, p = lineout; col < 7; col++,
+ p += julian ? J_DAY_LEN : DAY_LEN)
+ ascii_day(p, days[row * 7 + col]);
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ (void)printf("%s\n", lineout);
+ }
+}
+
+void
+j_yearly(year)
+ int year;
+{
+ int col, *dp, i, month, row, which_cal;
+ int days[12][MAXDAYS];
+ char *p, lineout[80];
+
+ (void)sprintf(lineout, "%d", year);
+ center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
+ (void)printf("\n\n");
+ for (i = 0; i < 12; i++)
+ day_array(i + 1, year, days[i]);
+ (void)memset(lineout, ' ', sizeof(lineout) - 1);
+ lineout[sizeof(lineout) - 1] = '\0';
+ for (month = 0; month < 12; month += 2) {
+ center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
+ center(month_names[month + 1], J_WEEK_LEN, 0);
+ (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
+ j_day_headings);
+ for (row = 0; row < 6; row++) {
+ for (which_cal = 0; which_cal < 2; which_cal++) {
+ p = lineout + which_cal * (J_WEEK_LEN + 2);
+ dp = &days[month + which_cal][row * 7];
+ for (col = 0; col < 7; col++, p += J_DAY_LEN)
+ ascii_day(p, *dp++);
+ }
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ (void)printf("%s\n", lineout);
+ }
+ }
+ (void)printf("\n");
+}
+
+void
+yearly(year)
+ int year;
+{
+ int col, *dp, i, month, row, which_cal;
+ int days[12][MAXDAYS];
+ char *p, lineout[80];
+
+ (void)sprintf(lineout, "%d", year);
+ center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
+ (void)printf("\n\n");
+ for (i = 0; i < 12; i++)
+ day_array(i + 1, year, days[i]);
+ (void)memset(lineout, ' ', sizeof(lineout) - 1);
+ lineout[sizeof(lineout) - 1] = '\0';
+ for (month = 0; month < 12; month += 3) {
+ center(month_names[month], WEEK_LEN, HEAD_SEP);
+ center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
+ center(month_names[month + 2], WEEK_LEN, 0);
+ (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
+ "", day_headings, HEAD_SEP, "", day_headings);
+ for (row = 0; row < 6; row++) {
+ for (which_cal = 0; which_cal < 3; which_cal++) {
+ p = lineout + which_cal * (WEEK_LEN + 2);
+ dp = &days[month + which_cal][row * 7];
+ for (col = 0; col < 7; col++, p += DAY_LEN)
+ ascii_day(p, *dp++);
+ }
+ *p = '\0';
+ trim_trailing_spaces(lineout);
+ (void)printf("%s\n", lineout);
+ }
+ }
+ (void)printf("\n");
+}
+
+/*
+ * day_array --
+ * Fill in an array of 42 integers with a calendar. Assume for a moment
+ * that you took the (maximum) 6 rows in a calendar and stretched them
+ * out end to end. You would have 42 numbers or spaces. This routine
+ * builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+void
+day_array(month, year, days)
+ int month, year;
+ int *days;
+{
+ int day, dw, dm;
+
+ if (month == 9 && year == 1752) {
+ memmove(days,
+ julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
+ return;
+ }
+ memmove(days, empty, MAXDAYS * sizeof(int));
+ dm = days_in_month[leap_year(year)][month];
+ dw = day_in_week(1, month, year);
+ day = julian ? day_in_year(1, month, year) : 1;
+ while (dm--)
+ days[dw++] = day++;
+}
+
+/*
+ * day_in_year --
+ * return the 1 based day number within the year
+ */
+int
+day_in_year(day, month, year)
+ int day, month, year;
+{
+ int i, leap;
+
+ leap = leap_year(year);
+ for (i = 1; i < month; i++)
+ day += days_in_month[leap][i];
+ return (day);
+}
+
+/*
+ * day_in_week
+ * return the 0 based day number for any date from 1 Jan. 1 to
+ * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
+ * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
+ * missing days.
+ */
+int
+day_in_week(day, month, year)
+ int day, month, year;
+{
+ long temp;
+
+ temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+ + day_in_year(day, month, year);
+ if (temp < FIRST_MISSING_DAY)
+ return ((temp - 1 + SATURDAY) % 7);
+ if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
+ return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+ return (THURSDAY);
+}
+
+void
+ascii_day(p, day)
+ char *p;
+ int day;
+{
+ int display, val;
+ static char *aday[] = {
+ "",
+ " 1", " 2", " 3", " 4", " 5", " 6", " 7",
+ " 8", " 9", "10", "11", "12", "13", "14",
+ "15", "16", "17", "18", "19", "20", "21",
+ "22", "23", "24", "25", "26", "27", "28",
+ "29", "30", "31",
+ };
+
+ if (day == SPACE) {
+ memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
+ return;
+ }
+ if (julian) {
+ if (val = day / 100) {
+ day %= 100;
+ *p++ = val + '0';
+ display = 1;
+ } else {
+ *p++ = ' ';
+ display = 0;
+ }
+ val = day / 10;
+ if (val || display)
+ *p++ = val + '0';
+ else
+ *p++ = ' ';
+ *p++ = day % 10 + '0';
+ } else {
+ *p++ = aday[day][0];
+ *p++ = aday[day][1];
+ }
+ *p = ' ';
+}
+
+void
+trim_trailing_spaces(s)
+ char *s;
+{
+ char *p;
+
+ for (p = s; *p; ++p)
+ continue;
+ while (p > s && isspace(*--p))
+ continue;
+ if (p > s)
+ ++p;
+ *p = '\0';
+}
+
+void
+center(str, len, separate)
+ char *str;
+ int len;
+ int separate;
+{
+
+ len -= strlen(str);
+ (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
+ if (separate)
+ (void)printf("%*s", separate, "");
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
+ exit(1);
+}
diff --git a/usr.bin/calendar/Makefile b/usr.bin/calendar/Makefile
new file mode 100644
index 0000000..7b6e434
--- /dev/null
+++ b/usr.bin/calendar/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= calendar
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${.CURDIR}/calendars/calendar.* ${DESTDIR}/usr/share/calendar
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/calendar/calendar.1 b/usr.bin/calendar/calendar.1
new file mode 100644
index 0000000..1ffa5e5
--- /dev/null
+++ b/usr.bin/calendar/calendar.1
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)calendar.1 8.1 (Berkeley) 6/29/93
+.\"
+.Dd June 29, 1993
+.Dt CALENDAR 1
+.Os
+.Sh NAME
+.Nm calendar
+.Nd reminder service
+.Sh SYNOPSIS
+.Nm calendar
+.Op Fl a
+.Sh DESCRIPTION
+.Nm Calendar
+checks the current directory for a file named
+.Pa calendar
+and displays lines that begin with either today's date
+or tomorrow's.
+On Fridays, events on Friday through Monday are displayed.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Process the ``calendar'' files of all users and mail the results
+to them.
+This requires super-user privileges.
+.El
+.Pp
+Lines should begin with a month and day.
+They may be entered in almost any format, either numeric or as character
+strings.
+A single asterisk (``*'') matches every month.
+A day without a month matches that day of every week.
+A month without a day matches the first of that month.
+Two numbers default to the month followed by the day.
+Lines with leading tabs default to the last entered date, allowing
+multiple line specifications for a single date.
+By convention, dates followed by an asterisk are not fixed, i.e., change
+from year to year.
+.Pp
+The ``calendar'' file is preprocessed by
+.Xr cpp 1 ,
+allowing the inclusion of shared files such as company holidays or
+meetings.
+If the shared file is not referenced by a full pathname,
+.Xr cpp 1
+searches in the current (or home) directory first, and then in the
+directory
+.Pa /usr/share/calendar .
+Empty lines and lines protected by the C commenting syntax
+.Pq Li /* ... */
+are ignored.
+.Pp
+Some possible calendar entries:
+.Bd -unfilled -offset indent
+#include <calendar.usholiday>
+#include <calendar.birthday>
+
+6/15 ... June 15 (if ambiguous, will default to month/day).
+Jun. 15 ... June 15.
+15 June ... June 15.
+Thursday ... Every Thursday.
+June ... Every June 1st.
+15 * ... 15th of every month.
+.Ed
+.Sh FILES
+The following default calendar files are provided:
+.Pp
+.Bl -tag -width calendar.christian -compact
+.It Pa calendar.birthday
+Births and deaths of famous (and not-so-famous) people.
+.It Pa calendar.christian
+Christian holidays.
+This calendar should be updated yearly by the local system administrator
+so that roving holidays are set correctly for the current year.
+.It Pa calendar.computer
+Days of special significance to computer people.
+.It Pa calendar.history
+Everything else, mostly U. S. historical events.
+.It Pa calendar.holiday
+Other holidays, including the not-well-known, obscure, and
+.Em really
+obscure.
+.It Pa calendar.judaic
+Jewish holidays.
+This calendar should be updated yearly by the local system administrator
+so that roving holidays are set correctly for the current year.
+.It Pa calendar.music
+Musical events, births, and deaths.
+Strongly oriented toward rock 'n' roll.
+.It Pa calendar.usholiday
+U.S. holidays.
+This calendar should be updated yearly by the local system administrator
+so that roving holidays are set correctly for the current year.
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr cpp 1 ,
+.Xr cron 8
+.Xr mail 1 ,
+.Sh COMPATIBILITY
+The
+.Nm calendar
+program previously selected lines which had the correct date anywhere
+in the line.
+This is no longer true, the date is only recognized when it occurs
+first on the line.
+.Sh HISTORY
+A
+.Nm
+command appeared in Version 7 AT&T UNIX.
+.Sh BUGS
+.Nm Calendar
+doesn't handle events that move around from year to year, i.e.,
+``the last Monday in April''.
diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c
new file mode 100644
index 0000000..7302dfb
--- /dev/null
+++ b/usr.bin/calendar/calendar.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+struct passwd *pw;
+int doall;
+
+void cal __P((void));
+void closecal __P((FILE *));
+int getday __P((char *));
+int getfield __P((char *, char **, int *));
+int getmonth __P((char *));
+int isnow __P((char *));
+FILE *opencal __P((void));
+void settime __P((void));
+void usage __P((void));
+#define isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "-a")) != EOF)
+ switch (ch) {
+ case '-': /* backward contemptible */
+ case 'a':
+ if (getuid()) {
+ errno = EPERM;
+ err(1, NULL);
+ }
+ doall = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ settime();
+ if (doall)
+ while ((pw = getpwent()) != NULL) {
+ (void)setegid(pw->pw_gid);
+ (void)seteuid(pw->pw_uid);
+ if (!chdir(pw->pw_dir))
+ cal();
+ (void)seteuid(0);
+ }
+ else
+ cal();
+ exit(0);
+}
+
+void
+cal()
+{
+ register int printing;
+ register char *p;
+ FILE *fp;
+ int ch;
+ char buf[2048 + 1];
+
+ if ((fp = opencal()) == NULL)
+ return;
+ for (printing = 0; fgets(buf, sizeof(buf), stdin) != NULL;) {
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+ else
+ while ((ch = getchar()) != '\n' && ch != EOF);
+ if (buf[0] == '\0')
+ continue;
+ if (buf[0] != '\t')
+ printing = isnow(buf) ? 1 : 0;
+ if (printing)
+ (void)fprintf(fp, "%s\n", buf);
+ }
+ closecal(fp);
+}
+
+struct iovec header[] = {
+ "From: ", 6,
+ NULL, 0,
+ " (Reminder Service)\nTo: ", 24,
+ NULL, 0,
+ "\nSubject: ", 10,
+ NULL, 0,
+ "'s Calendar\nPrecedence: bulk\n\n", 30,
+};
+
+/* 1-based month, 0-based days, cumulative */
+int daytab[][14] = {
+ 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364,
+ 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365,
+};
+struct tm *tp;
+int *cumdays, offset, yrdays;
+char dayname[10];
+
+void
+settime()
+{
+ time_t now;
+
+ (void)time(&now);
+ tp = localtime(&now);
+ if (isleap(tp->tm_year + 1900)) {
+ yrdays = 366;
+ cumdays = daytab[1];
+ } else {
+ yrdays = 365;
+ cumdays = daytab[0];
+ }
+ /* Friday displays Monday's events */
+ offset = tp->tm_wday == 5 ? 3 : 1;
+ header[5].iov_base = dayname;
+ header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp);
+}
+
+/*
+ * Possible date formats include any combination of:
+ * 3-charmonth (January, Jan, Jan)
+ * 3-charweekday (Friday, Monday, mon.)
+ * numeric month or day (1, 2, 04)
+ *
+ * Any character may separate them, or they may not be separated. Any line,
+ * following a line that is matched, that starts with "whitespace", is shown
+ * along with the matched line.
+ */
+int
+isnow(endp)
+ char *endp;
+{
+ int day, flags, month, v1, v2;
+
+#define F_ISMONTH 0x01
+#define F_ISDAY 0x02
+ flags = 0;
+ /* didn't recognize anything, skip it */
+ if (!(v1 = getfield(endp, &endp, &flags)))
+ return (0);
+ if (flags & F_ISDAY || v1 > 12) {
+ /* found a day */
+ day = v1;
+ /* if no recognizable month, assume just a day alone */
+ if (!(month = getfield(endp, &endp, &flags)))
+ month = tp->tm_mon + 1;
+ } else if (flags & F_ISMONTH) {
+ month = v1;
+ /* if no recognizable day, assume the first */
+ if (!(day = getfield(endp, &endp, &flags)))
+ day = 1;
+ } else {
+ v2 = getfield(endp, &endp, &flags);
+ if (flags & F_ISMONTH) {
+ day = v1;
+ month = v2;
+ } else {
+ /* F_ISDAY set, v2 > 12, or no way to tell */
+ month = v1;
+ /* if no recognizable day, assume the first */
+ day = v2 ? v2 : 1;
+ }
+ }
+ if (flags & F_ISDAY)
+ day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7);
+ day = cumdays[month] + day;
+
+ /* if today or today + offset days */
+ if (day >= tp->tm_yday && day <= tp->tm_yday + offset)
+ return (1);
+ /* if number of days left in this year + days to event in next year */
+ if (yrdays - tp->tm_yday + day <= offset)
+ return (1);
+ return (0);
+}
+
+int
+getfield(p, endp, flags)
+ char *p, **endp;
+ int *flags;
+{
+ int val;
+ char *start, savech;
+
+ for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
+ if (*p == '*') { /* `*' is current month */
+ *flags |= F_ISMONTH;
+ *endp = p+1;
+ return (tp->tm_mon + 1);
+ }
+ if (isdigit(*p)) {
+ val = strtol(p, &p, 10); /* if 0, it's failure */
+ for (; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
+ *endp = p;
+ return (val);
+ }
+ for (start = p; isalpha(*++p););
+ savech = *p;
+ *p = '\0';
+ if ((val = getmonth(start)) != 0)
+ *flags |= F_ISMONTH;
+ else if ((val = getday(start)) != 0)
+ *flags |= F_ISDAY;
+ else {
+ *p = savech;
+ return (0);
+ }
+ for (*p = savech; !isdigit(*p) && !isalpha(*p) && *p != '*'; ++p);
+ *endp = p;
+ return (val);
+}
+
+char path[MAXPATHLEN + 1];
+
+FILE *
+opencal()
+{
+ int fd, pdes[2];
+
+ /* open up calendar file as stdin */
+ if (!freopen("calendar", "r", stdin)) {
+ if (doall)
+ return (NULL);
+ errx(1, "no calendar file in current directory.");
+ }
+ if (pipe(pdes) < 0)
+ return (NULL);
+ switch (vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ return (NULL);
+ case 0:
+ /* child -- stdin already setup, set stdout to pipe input */
+ if (pdes[1] != STDOUT_FILENO) {
+ (void)dup2(pdes[1], STDOUT_FILENO);
+ (void)close(pdes[1]);
+ }
+ (void)close(pdes[0]);
+ execl(_PATH_CPP, "cpp", "-P", "-I.", _PATH_INCLUDE, NULL);
+ (void)fprintf(stderr,
+ "calendar: execl: %s: %s.\n", _PATH_CPP, strerror(errno));
+ _exit(1);
+ }
+ /* parent -- set stdin to pipe output */
+ (void)dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+
+ /* not reading all calendar files, just set output to stdout */
+ if (!doall)
+ return (stdout);
+
+ /* set output to a temporary file, so if no output don't send mail */
+ (void)snprintf(path, sizeof(path), "%s/_calXXXXXX", _PATH_TMP);
+ if ((fd = mkstemp(path)) < 0)
+ return (NULL);
+ return (fdopen(fd, "w+"));
+}
+
+void
+closecal(fp)
+ FILE *fp;
+{
+ struct stat sbuf;
+ int nread, pdes[2], status;
+ char buf[1024];
+
+ if (!doall)
+ return;
+
+ (void)rewind(fp);
+ if (fstat(fileno(fp), &sbuf) || !sbuf.st_size)
+ goto done;
+ if (pipe(pdes) < 0)
+ goto done;
+ switch (vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto done;
+ case 0:
+ /* child -- set stdin to pipe output */
+ if (pdes[0] != STDIN_FILENO) {
+ (void)dup2(pdes[0], STDIN_FILENO);
+ (void)close(pdes[0]);
+ }
+ (void)close(pdes[1]);
+ execl(_PATH_SENDMAIL, "sendmail", "-i", "-t", "-F",
+ "\"Reminder Service\"", "-f", "root", NULL);
+ (void)fprintf(stderr,
+ "calendar: %s: %s.\n", _PATH_SENDMAIL, strerror(errno));
+ _exit(1);
+ }
+ /* parent -- write to pipe input */
+ (void)close(pdes[0]);
+
+ header[1].iov_base = header[3].iov_base = pw->pw_name;
+ header[1].iov_len = header[3].iov_len = strlen(pw->pw_name);
+ writev(pdes[1], header, 7);
+ while ((nread = read(fileno(fp), buf, sizeof(buf))) > 0)
+ (void)write(pdes[1], buf, nread);
+ (void)close(pdes[1]);
+done: (void)fclose(fp);
+ (void)unlink(path);
+ while (wait(&status) >= 0);
+}
+
+static char *months[] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec", NULL,
+};
+
+int
+getmonth(s)
+ register char *s;
+{
+ register char **p;
+
+ for (p = months; *p; ++p)
+ if (!strncasecmp(s, *p, 3))
+ return ((p - months) + 1);
+ return (0);
+}
+
+static char *days[] = {
+ "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL,
+};
+
+int
+getday(s)
+ register char *s;
+{
+ register char **p;
+
+ for (p = days; *p; ++p)
+ if (!strncasecmp(s, *p, 3))
+ return ((p - days) + 1);
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: calendar [-a]\n");
+ exit(1);
+}
diff --git a/usr.bin/calendar/calendars/calendar.birthday b/usr.bin/calendar/calendars/calendar.birthday
new file mode 100644
index 0000000..0cb8cab
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.birthday
@@ -0,0 +1,257 @@
+01/01 J.D. Salinger born, 1919
+01/01 Paul Revere born in Boston, 1735
+01/02 Isaac Asimov born in Petrovichi, Russian SFSR (now part of USSR), 1920
+01/04 George Washington Carver born in Missouri, 1864
+01/04 Jakob Grimm born, 1785
+01/04 Wilhelm Beer born, 1797, first astronomer to map Mars
+01/05 DeWitt B. Brace born, 1859, inventor of spectrophotometer
+01/10 Ethan Allen born, 1738
+01/11 Alexander Hamilton born in Nevis, British West Indies, 1757?
+01/12 "Long" John Baldry is born in London, 1941
+01/13 Horatio Alger born, 1834
+01/13 Sophie Tucker born, 1884
+01/13 Wilhelm Wien born, 1864, Nobel prize for blackbody radiation laws
+01/14 Albert Schweitzer born, 1875
+01/15 Martin Luther King, Jr. born
+01/17 Benjamin Franklin born in Boston, 1706
+01/19 Edgar Allan Poe born in Boston, 1809
+01/19 Robert Edward Lee born in Stratford Estate, Virginia, 1807
+01/20 George Burns born, 1898
+01/21 Lenin died, 1924
+01/21 Thomas Jonathan "Stonewall" Jackson born in Clarksburg, VA, 1824
+01/22 Sir Francis Bacon born, 1561
+01/23 Ernst Abbe born, 1840, formulated diffraction theory
+01/23 Humphrey Bogart born in New York City, 1899
+01/23 John Hancock born, 1737
+01/23 Joseph Hewes born, 1730
+01/23 Samuel Barber died, 1981
+01/24 John Belushi is born in Chicago, 1949
+01/25 Robert Burns born, 1759
+01/25 Virginia Woolf born, 1882
+01/25 W. Somerset Maugham born, 1874
+01/27 Samuel Gompers born, 1850
+01/30 Franklin Delano Roosevelt born in Hyde Park, New York, 1882
+01/31 Jackie Robinson born, 1919
+02/03 Gertrude Stein born, 1874
+02/05 Alex Harvey (SAHB) is born in Glasgow, Scotland, 1935
+02/06 King George VI of UK dies; his daughter becomes Elizabeth II, 1952
+02/07 Sinclair Lewis born, 1885
+02/08 Friedleib F. Runge born, 1795, father of paper chromatography
+02/08 Jules Verne born in Nantes, France, 1828
+02/09 George Hartmann born, 1489, designed astrolabes, timepieces, etc.
+02/10 Charles Lamb born, 1775
+02/10 William Allen White born, 1868
+02/11 Thos. Edison born, 1847
+02/11 William Henry Fox Talbot born, 1489, photographic pioneer
+02/12 Abraham Lincoln born, 1809
+02/12 Charles Darwin born in Shrewsbury, England, 1809
+02/15 Galileo Galilei born in Pisa, Italy, 1564
+02/15 Susan B. Anthony born, 1820
+02/16 Pierre Bouguer born, 1698, founder of photometry
+02/17 Federick Eugene Ives born, 1856, pioneer of halftone
+02/17 Marion Anderson born, 1902
+02/17 T. J. Watson, Sr. born, 1874
+02/18 Ernst Mach born, 1838, philosopher & optics pioneer
+02/19 Nicolas Copernicus born in Thorn, Poland, 1473
+02/20 Ludwig Boltzmann born, 1838, atomic physics pioneer
+02/21 Alexis De Rochon born, 1838, developed the spyglass
+02/22 George Washington born, 1732
+02/22 Pierre Jules Cesar Janssen born, 1838, found hydrogen in the sun
+02/23 W.E.B. DuBois born, 1868
+02/24 Winslow Homer born, 1836
+02/25 George Harrison born in Liverpool, England, 1943
+02/25 Renoir born, 1841
+02/26 Dominique Francois Jean Arago born, 1786;
+ observed "Poisson's spot" cf June 21
+02/28 Michel de Mantaigne born, 1533
+02/29 Herman Hollerith born, 1860
+03/01 David Niven born, 1910
+03/02 Dr. Seuss born, 1904
+03/04 Casimir Pulaski born, 1747
+03/05 John Belushi dies in Los Angeles, 1982
+03/07 Aristotle died, 322BC
+03/07 Sir John Frederick William Herschel born, 1792, astronomer
+03/08 Alvan Clark born, 1804, astronomer & lens manufacturer
+03/08 Howard Aiken born, 1900
+03/11 Robert Treat Paine born, 1737
+03/11 Vannevar Bush born, 1890
+03/12 Gustav Robert Kirchhoff born, 1824, physicist
+03/14 Albert Einstein born, 1879
+03/14 Casey Jones born, 1864
+03/14 Giovanni Virginia Schiaparelli born, 1835, astronomer;
+ named Mars "canals"
+03/14 Jean Baptiste Joseph Fourier born, 1768, mathematician & physicist
+03/16 George Clymer born, 1739
+03/16 James Madison born, 1751
+03/24 Harry Houdini born, 1874
+03/26 Benjamin Thompson born, 1753, Count Rumford; physicist
+03/27 Wilhelm Conrad Roentgen born, 1845, discoverer of X-rays
+03/28 Pierre Simon de Laplace born, 1749, mathematician & astronomer
+03/30 Francisco Jose de Goya born, 1746
+03/30 Sean O'Casey born, 1880
+03/30 Vincent Van Gogh born, 1853
+03/31 Descartes born, 1596
+03/31 Rene Descartes born, 1596, mathematician & philosopher
+04/03 Washington Irving born, 1783
+04/05 Thomas Hobbes born, 1588, philosopher
+04/08 Buddha born, 563 BC
+04/08 David Rittenhouse born, 1732, astronomer & mathematician
+04/09 Edward Muybridge born, 1830, motion-picture pioneer
+04/09 J. Presper Eckert born, 1919
+04/10 Commodore Matthew Calbraith Perry born, 1854
+04/10 William Booth born, 1829, founder of the Salvation Army
+04/13 Thomas Jefferson born, 1743
+04/14 Christian Huygen born, 1629, physicist & astronomer;
+ discovered Saturn's rings
+04/15 Leonardo da Vinci born, 1452
+04/16 Charles (Charlie) Chaplin (Sir) born in London, 1889
+04/22 Kant born, 1724
+04/27 Louis Victor de Broglie born, 1774, physicist
+04/28 James Monroe born, 1758
+04/29 Jules Henri Poincare born, 1854, founder of topology
+04/29 William Randolph Hearst born in San Francisco, 1863
+04/30 Karl Friedrich Gauss born, 1777, mathematician & astronomer
+05/01 Little Walter (Marion Walter Jacobs) is born in Alexandria,
+ Louisiana, 1930
+05/02 Dr. Benjamin Spock born, 1903
+05/09 Pinza died, 1957
+05/10 Fred Astaire (Frederick Austerlitz) born in Omaha, Nebraska, 1899
+05/11 Johnny Appleseed born, 1768
+05/12 Florence Nightingale born in Florence, Italy, 1820
+05/13 Arthur S. Sullivan born, 1842
+05/15 Mike Oldfield is born in Essex, England, 1953
+05/19 Ho Chi Minh born, 1890
+05/21 Plato (Aristocles) born in Athens(?), 427BC
+05/27 Hubert H. Humphrey born, 1911
+05/28 Dionne quintuplets born, 1934
+05/29 Gilbert Keith Chesterton born, 1874
+05/29 John Fitzgerald Kennedy born, 1917
+05/29 Patrick Henry born, 1736
+05/30 Mel (Melvin Jerome) Blanc born in San Francisco, 1908
+06/01 Brigham Young born, 1801
+06/01 Marilyn Monroe born, 1928
+06/02 Edward Elgar (Sir) born in Worcester, England, 1857
+06/03 Henry James born, 1811
+06/07 (Eugene Henri) Paul Gaugin born, 1848
+06/07 George Bryan "Beau" Brummel born, 1778
+06/08 Frank Lloyd Wright born in Richland Center, Wisconsin, 1869
+06/13 Alexander the Great dies (323BC)
+06/15 Edward (Edvard Hagerup) Grieg born in Bergen, Norway, 1843
+06/16 Hammurabi the Great dies, Babylon, 1686 bc
+06/18 M.C. Escher born, 1898
+06/22 Carl Hubbell born, 1903
+06/22 Meryl Streep born in Summit, New Jersey, 1949
+06/25 Eric Arthur Blair (a.k.a. George Orwell) born, 1903
+06/27 Helen Keller born, 1880
+07/03 Franz Kafka born, 1883
+07/04 Nathaniel Hawthorne born in Salem, Massachusetts, 1804
+07/06 (Helen) Beatrix Potter born, 1866
+07/06 John Paul Jones born, 1747
+07/07 P.T. Barnum dies, 1891
+07/08 Count Ferdinand von Zeppelin born, 1838
+07/10 John Calvin born, 1509
+07/11 John Quincy Adams born, 1767
+07/12 Henry David Thoreau born, 1817
+07/15 Clement Clarke Moore born, 1779, author of "A Visit from
+ Saint Nicholas"
+07/18 Brian Auger is born in London, 1939
+07/25 Steve Goodman is born in Chicago, 1948
+07/29 Mussolini born, 1883
+07/30 Emily Bronte born, 1818
+07/30 Henry Ford born, 1863
+08/01 Herman Melville born, 1819
+08/03 Lenny Bruce dies of a morphine overdose, 1966
+08/08 Dustin Hoffman born in Los Angeles, 1937
+08/13 Annie Oakley born, 1860
+08/13 Fidel Castro born, 1927
+08/17 Mae West born, 1892
+08/18 Meriwether Lewis born, 1927
+08/23 Gene Kelly born, 1912
+08/27 Lyndon B. Johnson born, 1908
+08/29 Oliver Wendell Holmes born, 1809, physician & father of the jurist
+08/30 John W. Mauchly born, 1907
+09/05 King Louis XIV of France born, 1638
+09/05 Raquel Welch born, 1942
+09/06 Word is received that Perry has reached the North Pole and died, 1909
+09/07 James Fenimore Cooper born in Burlington, NJ, 1789
+09/07 Queen Elizabeth I of England born, 1533
+09/08 King Richard I of England born, 1157
+09/08 Peter Sellers born in Southsea, England, 1925
+09/09 Chinese Communist Party Chairman Mao Tse-Tung dies at age 82, 1976
+09/12 Jesse Owens born, 1913
+09/13 Walter Reed born, 1851
+09/15 Agatha Christie born in Torquay, England, 1890
+09/16 Allen Funt born in Brooklyn, NY, 1914
+09/18 Greta Garbo born, 1905
+09/18 Jimi Hendrix dies from an overdose, 1970
+09/20 Upton (Beall) Sinclair born, 1878
+09/21 H.G. (Herbert George) Wells born in Bromley, England, 1866
+09/21 Louis Joliet born, 1645
+09/22 President Garfield dies of wounds in Baltimore, 1881
+09/23 Augustus (Gaius Octavius) Caesar born in Rome, 63 BC
+09/23 Euripides born in Salamis, Greece, 480 BC
+09/24 F. Scott Fitzgerald born, 1896
+09/26 Johnny Appleseed born, 1774
+09/26 T.S. (Thomas Stearns) Eliot born in St. Louis, 1888
+09/27 Thomas Nast born, 1840
+09/28 Michelangelo Buanarroti born in Caprese, Italy, 1573
+09/28 Pompey (Gnaeus Pompeius Magnus) born in Rome, 106BC
+09/28 Seymour Cray born, 1925
+09/29 Gene Autry born, 1907
+10/01 Jimmy Carter born, 1924
+10/02 Aristotle dies of indigestion, 322 BC
+10/02 Mohandas K. Gandhi born at Porbandar, Kathiawad, India, 1869
+10/04 John V. Atanasoff born, 1903
+10/05 Pablo Picasso born in Malaga, Spain, 1881
+10/05 Ray Kroc (founder of McDonald's) born, 1902
+10/13 Lenny Bruce is born in New York City, 1925
+10/13 Virgil (Publius Vergilius Maro) born near Mantua, Italy, 70 BC
+10/14 Dwight David Eisenhower born, 1890
+10/14 William Penn born in London, 1644
+10/15 Pelham Grenville Wodehouse born, 1881
+10/16 Noah Webster born, 1758
+10/16 Oscar (Fingal O'Flahertie Wills) Wilde born in Dublin, 1854
+10/17 Richard Mentor Johnson born, 1780, 9th V.P. of U.S.
+10/21 Alfred Nobel born in Stockholm, 1833
+10/27 Gerald M. Weinberg born, 1933
+10/27 James Cook is born, 1466
+10/31 Chiang Kai-Shek born, 1887
+10/31 Dale Evans born, 1912
+11/02 Daniel Boone born near Reading, PA, 1734
+11/04 King William III of Orange born, 1650
+11/05 Roy Rogers born, 1912
+11/09 Carl Sagan born, 1934
+11/10 Martin Luther born in Eisleben, Germany, 1483
+11/10 Soviet President Leonid Brezhnev dies at age 75, 1982
+11/11 Kurt Vonnegut, Jr, born in Indianapolis, 1922
+11/13 Robert Louis Stevenson born, 1850
+11/13 St. Augustine of Hippo born in Numidia, Algeria, 354
+11/18 Imogene Coca born, 1908
+11/18 William S. Gilbert born, 1836
+11/20 RFK born, 1925
+11/26 Charles Schulz born in Minneapolis, 1922
+11/26 Norbert Weiner born, 1894
+11/29 John Mayall is born in Cheshire, England, 1933
+11/30 Cleopatra died, 30 BC
+11/30 Mark Twain (Samuel Clemmens) born in Florida, Missouri, 1835
+12/01 Woody Allen (Allen Stuart Konigsberg) born in Brooklyn, NY, 1935
+12/04 Tommy Bolin dies of a heroin overdose in Miami, 1976
+12/05 Walt (Walter Elias) Disney born in Chicago, 1901
+12/08 Horace (Quintus Horatius Flaccus) born in Venosa (Italy), 65BC
+12/08 James (Grover) Thurber born in Columbus, Ohio, 1894
+12/10 Emily Dickenson born, 1830
+12/12 E.G. Robinson born, 1893
+12/14 George Washington dies, 1799
+12/17 William Safire (Safir) born, 1929
+12/21 Benjamin Disraeli born, 1804
+12/22 Giacomo Puccini born, 1858
+12/23 Joseph Smith born, 1805
+12/25 Isaac Newton (Sir) born in Grantham, England, 1642
+12/26 Chas. Babbage born, 1791
+12/28 John von Neumann born, 1903
+03/15 J.J. Robert's Birthday in Liberia
+03/15 Julius Caesar assassinated by Brutus; Ides of March, 44BC
+07/04 John Adams and Thomas Jefferson die on same day, 1826
+07/12 Thoreau's Birthday, 1817
+08/12 Thomas Mann's Death, 1955
+08/20 Leon Trotsky assassinated, 1940
diff --git a/usr.bin/calendar/calendars/calendar.christian b/usr.bin/calendar/calendars/calendar.christian
new file mode 100644
index 0000000..7f565d6
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.christian
@@ -0,0 +1,16 @@
+01/06* Epiphany
+02/11* Shrove Tuesday / Mardi Gras (day before Ash Wednesday)
+02/08* Ash Wednesday (First day of Lent)
+03/19* Palm Sunday (7 days before Easter)
+03/23* Maundy Thursday (3 days before Easter)
+03/24* Good Friday (2 days before Easter)
+03/26* Easter Sunday
+05/04* Ascension Day (10 days before Pentecost)
+05/14* Pentecost (Whitsunday)
+05/15* Whitmonday
+05/21* Trinity Sunday (7 days after Pentecost)
+05/25* Corpus Christi (11 days after Pentecost)
+05/28* Rogation Sunday
+10/18 Feast Day of St. Luke
+12/03* First Sunday of Advent (4th Sunday before Christmas)
+12/06 St. Nicholas' Day
diff --git a/usr.bin/calendar/calendars/calendar.computer b/usr.bin/calendar/calendars/calendar.computer
new file mode 100644
index 0000000..cd0d85d
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.computer
@@ -0,0 +1,62 @@
+01/01 AT&T officially divests its local Bell companies, 1984
+01/01 The Epoch (Time 0 for UNIX systems, Midnight GMT, 1970)
+01/03 Apple Computer founded, 1977
+01/08 American Telephone and Telegraph loses antitrust case, 1982
+01/08 Herman Hollerith patents first data processing computer, 1889
+01/08 Justice Dept. drops IBM suit, 1982
+01/10 First CDC 1604 delivered to Navy, 1960
+01/16 Set uid bit patent issued, to Dennis Ritchie, 1979
+01/17 Justice Dept. begins IBM anti-trust suit, 1969 (drops it, 01/08/82)
+01/24 DG Nova introduced, 1969
+01/25 First U.S. meeting of ALGOL definition committee, 1958
+01/26 EDVAC demonstrated, 1952
+01/31 Hewlett-Packard founded, 1939
+02/11 Last day of JOSS service at RAND Corp., 1966
+02/14 First micro-on-a-chip patented (TI), 1978
+02/15 ENIAC demonstrated, 1946
+03/01 First NPL (later PL/I) report published, 1964
+03/04 First Cray-1 shipped to Los Alamos
+03/09 "GOTO considered harmful" (E.J. Dikstra) published in CACM, 1968
+03/14 LISP introduced, 1960
+03/28 DEC announces PDP-11, 1970
+03/31 Eckert-Mauchly Computer Corp. founded, Phila, 1946
+04/01 Yourdon, Inc. founded, 1974 (It figures.)
+04/03 IBM 701 introduced, 1953
+04/04 Tandy Corp. acquires Radio Shack, 1963 (9 stores)
+04/07 IBM announces System/360, 1964
+04/09 ENIAC Project begun, 1943
+04/28 Zilog Z-80 introduced
+05/06 EDSAC demonstrated, 1949
+05/01 First BASIC program run at Dartmouth, 1964
+05/16 First report on SNOBOL distributed (within BTL), 1963
+05/21 DEC announces PDP-8
+05/22 Ethernet first described, 1973
+05/27 First joint meeting of U.S. and European ALGOL definition cte., 1958
+05/28 First meeting of COBOL definition cte. (eventually CODASYL), 1959
+05/30 Colossus Mark II, 1944
+06/02 First issue of Computerworld, 1967
+06/10 First Apple II shipped, 1977
+06/15 UNIVAC I delivered to the Census Bureau, 1951
+06/16 First programming error at Census Bureau, 1951 (apocryphal)
+06/23 IBM unbundles software, 1969
+06/23 Turing's Birthday, 1912
+06/30 First advanced degree on computer related topic: to H. Karamanian,
+ Temple Univ., Phila, 1948, for symbolic diffentiation on the ENIAC
+07/08 Bell Telephone Co. formed (predecessor of AT&T), 1877
+07/08 CDC incorporated, 1957
+08/14 First Unix-based mallet created, 1954
+08/14 IBM PC announced, 1981
+08/22 CDC 6600 introduced, 1963
+08/23 DEC founded, 1957
+09/15 ACM founded, 1947
+09/20 Harlan Herrick runs first FORTRAN program, 1954
+10/02 First robotics-based CAM, 1939
+10/06 First GPSS manual published, 1961
+10/08 First VisiCalc prototype, 1978
+10/12 Univac gives contract for SIMULA compiler to Nygaard and Dahl, 1962
+10/14 British Computer Society founded, 1957
+10/15 First FORTRAN Programmer's Reference Manual published
+10/20 Zurich ALGOL report published, 1958
+10/25 DEC announces VAX-11/780
+11/04 UNIVAC I program predicts Eisenhower victory based on 7% of votes, 1952
+12/08 First Ph.D. awarded by Computer Science Dept, Univ. of Penna, 1965
diff --git a/usr.bin/calendar/calendars/calendar.history b/usr.bin/calendar/calendars/calendar.history
new file mode 100644
index 0000000..9fb2189
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.history
@@ -0,0 +1,486 @@
+01/01 Anniversary of the Triumph of the Revolution in Cuba
+01/01 Castro expells Cuban President Batista, 1959
+01/01 Churchill delivers his "Iron Curtain" speech, 1947
+01/01 First Rose Bowl; Michigan 49 - Stanford 0, 1902
+01/04 Quadrantid meteor shower (look north)
+01/05 -50 degrees F, Strawberry UT, 1913
+01/05 Fellowship enters Moria (LOTR)
+01/05 The FCC hears the first demonstration of FM radio, 1940
+01/05 Twelfth night
+01/06 Millard Fillmore's birthday (let's party!)
+01/08 Battle of New Orleans
+01/09 Fellowship reaches Lorien (LOTR)
+01/09 Plough Monday
+01/10 First meeting of United Nations General Assembly in London, 1946
+01/10 Thomas Paine's Common Sense published, 1776
+01/11 Anniversary of the Peoples Republic of Albania
+01/11 De Hostos' Birthday in Puerto Rico
+01/11 Milk delivered in bottles for first time, 1878
+01/11 Prithvi Jayanti in Nepal
+01/11 Surgeon General condemned cigarettes, 1964
+01/11 The Whiskey-A-Go-Go opens on Sunset Boulevard in Los Angeles, 1963
+01/14 The first "Be-In" is held in Golden Gate Park, 1967
+01/16 Prohibition begins, 1920
+01/17 Passing of Gandalf (LOTR)
+01/18 Grey whale migration, California
+01/20 St. Agnes Eve (Ah, bitter chill it was...)
+01/24 Eskimo Pie patented by Christian Nelson, 1922
+01/24 Gold discovered in California at Sutter's Mill, 1848
+01/26 Sydney Aust. settled, 1778
+01/27 Grissom, White and Chaffe burned to death in Apollo 1, 1967
+01/27 Vietnam War cease-fire signed, 1973
+01/28 First ski tow, Woodstock VT, 1914
+01/28 Space Shuttle Challenger (51-L) explodes 74 seconds after liftoff
+ killing Scobee, Smith, McNair, Resnick, Jarvis, Onizuka and McAuliffe,
+ 1986
+01/30 Mohandas Gandhi assassinated in New Delhi by Hindu fanatic, 1948
+01/30 Tet Offensive, 1968
+01/31 "Ham" the chimpanzee soars into space aboard Mercury-Redstone 2, 1961
+01/31 Explorer I launched, 1958. Van Allen Belt discovered
+01/31 Irving Langmuir, 1881, invented tungsten filament lamp
+02/01 First TV soap: Secret Storm, 1954
+02/01 Forces lead by Khomeini take over Iran, 1979
+02/04 Cybernet inaugurated, 1969
+02/04 Patricia Hearst kidnapped by Symbionese Liberation Army, 1974
+02/07 Fellowship leaves Lorien (LOTR)
+02/08 1963 Revolution Anniversary in Iraq
+02/09 -51 degrees F, Vanderbilt MI, 1934
+02/12 Lincoln's real birthday
+02/12 Santa Barbara oil leak, 1969
+02/14 Bombing of Dresden, 1945
+02/15 Chicago Seven convicted, 1970
+02/16 Nylon patented, 1937
+02/16 Stephen Decatur burns US frigate in Tripoli, 1804
+02/17 Death of Boromir (LOTR)
+02/18 Pluto discovered by Clyde Tombaugh, Lowell Observatory, AZ, 1930
+02/19 US Marines land on Iwo Jima, 1945
+02/20 John Glenn orbits the Earth 3 times, 1962
+02/20 Meriadoc & Pippin meet Treebeard (LOTR)
+02/21 Battle of Verdun begins, 1916 1M casualties
+02/21 First telephone directory, New Haven, Connecticut, 1878
+02/21 Malcom X shot to death in Harlem, 1965
+02/22 Passing of King Ellesar (LOTR)
+02/23 Lt. Calley confesses, implicates Cpt. Medina, 1971
+02/24 Ents destroy Isengard (LOTR)
+02/24 Impeachment proceedings against Andrew Johnson begin, 1868
+02/26 Aragorn takes the Paths of the Dead (LOTR)
+02/27 The Lionheart crowned, 1189
+02/28 The "French Connection" drug bust occurs in Marseilles, 1972
+02/29 French and Indian raid on Deerfield MA, 1704
+03/01 Sarah Goode, Sarah Osborne, and Tituba arrested for witchcraft
+ in Salem, Massachusetts, 1692
+03/02 Blackthorn winds (New England) (Does anyone know what this is?)
+03/04 First meeting of Congress, 1789, in N.Y.C.
+03/05 Frodo & Samwise encounter Shelob (LOTR)
+03/06 Hindenburg explodes and burns upon landing at Lakehurst, NJ, 1939
+03/08 Deaths of Denethor & Theoden (LOTR)
+03/13 "Striptease" introduced, Paris, 1894
+03/14 Teddy Roosevelt excludes Japanese laborers from continental US, 1907
+03/15 Buzzards return to Hinckley OH
+03/15 France assumes protectorate over Vietnam, 1874
+03/15 Watts, Los Angeles, riots kill two, injure 25, 1966
+03/16 First liquid-fuel-powered rocket flight, 1926
+03/16 MyLai Massacre; 300 non-combatant villagers killed by U.S. infantrymen
+03/16 Robert Goddard launches first liquid-fueled rocket, Auburn MA, 1926
+03/17 Vanguard I launched, 1958. Earth proved pear-shaped
+03/18 Aleksei Leonov performs first spacewalk, 1965
+03/18 Destruction of the Ring (LOTR)
+03/19 Swallows return to Capistrano
+03/20 Radio Caroline, the original British pirate radio station, sinks, 1980
+03/24 Construction of New York subway system begins, 1900
+03/25 Triangle Shirt Waist Fire, 1911
+03/26 Popeye statue unveiled, Crystal City TX Spinach Festival, 1937
+03/27 Khrushchev becomes Premier of Soviet Union, 1958
+03/28 Three Mile Island releases radioactive gas, 1979
+03/29 Flowering of the Mallorn (LOTR)
+03/29 Swedish settled Christiana (Wilmington) DE, 1638
+03/30 Alaska purchased from Russia for $7.2 million, 1867
+03/30 Five rings around Uranus discovered, 1977
+03/30 Pencil with eraser patented, 1858
+04/01 People of superb intelligence, savoir-faire, etc. born this day.
+04/04 Gandalf visits Bilbo (LOTR)
+04/04 Martin Luther King assassinated in Memphis, Tennessee, 1968
+04/04 NATO Established, 1949
+04/06 Joseph Smith founds Mormon Church, 1830
+04/07 Albert Hofmann synthesizes LSD in Switzerland, 1943
+04/07 Alewives run, Cape Cod
+04/09 Lee surrenders to Grant at Appomattox Courthouse, 1865
+04/12 Columbia launched, 1981
+04/12 Confederate troops fire first shots of Civil War at Ft Sumter, 1861
+04/12 Space Shuttle Columbia launched, 1981
+04/12 Yuri Gagarin becomes the first man in space, 1961
+04/13 Laotian New Year (3 days) in Laos
+04/14 Lincoln shot, 1865
+04/14 Titanic hits iceberg and sinks, 1912
+04/15 Ray Kroc opens first McDonalds in Des Plaines, IL, 1955
+04/16 Lincoln shot in Ford's Theatre by John Wilkes Booth, 1865
+04/17 An unexpected party (LOTR)
+04/17 Bay of Pigs invasion crushed by Castro forces, 1961
+04/18 Einstein's Death, 1955
+04/18 First Laundromat opens, Fort Worth Texas, 1934
+04/18 San Francisco earthquake, 1906
+04/19 Landing of the "33" in Uruguay
+04/19 Warsaw Ghetto uprising, 1943
+04/20 Supreme Court unanimously rules in favor of busing, 1971
+04/21 Lyrid meteor shower
+04/23 Crowning of King Ellesar (LOTR)
+04/23 Hank Aaron hits his first home run, 1954
+04/26 William Shakespeare baptised in Stratford-on-Avon, England, 1564,
+ birthdate unknown
+04/27 Magellan killed in Phillippines, 1521
+04/29 Zipper patented by Gideon Sindback, 1913
+05/01 Beltaine; Feast of the god Bel, sun god
+05/03 Anti-war protest disrupts business in Washington, 1971
+05/04 Four Kent State students are shot down by the National Guard, 1970
+05/05 John Scopes arrested for teaching evolution, Dayton, TN, 1925
+05/07 Germany surrenders after WWII, 1945
+05/08 Beginning of ostrich mating season
+05/08 US institutes mining of Haiphong Harbor, 1972
+05/09 94 degrees, New York, 1979
+05/10 Germany invades Low Countries, 1940
+05/10 Nazi bookburning, 1933
+05/14 Beginning of Lewis and Clark Expedition, 1804
+05/14 Nation of Israel proclaimed, 1948
+05/15 Asylum for Inebriates founded, Binghamton NY, 1854
+05/17 24" rain in 11 hours, Pearl River, S. China, 1982
+05/17 Six SLA members killed in televised gun fight, 1974
+05/18 Battle of Las Piedras in Uruguay
+05/18 Napoleon crowned Emperor, 1804
+05/19 Arwen leaves Lorian to wed King Ellesar (LOTR)
+05/21 Battle of Iquique in Chile
+05/21 US explodes first hydrogen bomb, 1956
+05/22 US Civil War ends, 1865
+05/23 Israeli raid into Argentina to capture Adolf Eichmann, 1960
+05/23 Two Yetis sighted, Mt. Everest, 1953
+05/24 Battle of Pinchincha in Ecuador
+05/25 Oral Roberts sees 900 foot tall Jesus Christ, Tulsa OK, 1980
+05/25 Successful test of the limelight in Purfleet, England, 1830
+05/26 Congress sets first immigration quotas, 1924
+05/27 Golden Gate Bridge opens, 1937
+05/29 Edmund Hillary and Tenzing Norkay climb Mt. Everest, 1953
+05/29 First food stamps issued, 1961
+05/30 US Marines sent to Nicaragua, 1912
+06/02 Native Americans "granted" citizenship, 1924
+06/04 Roquefort cheese developed, 1070
+06/05 Robert Kennedy assasinated, 1968
+06/05 US leaves the Gold Standard, 1933
+06/06 First drive-in movie, 1933
+06/06 Normandy landing, 1944
+06/10 Death of Alexander the Great, 323 B.C.
+06/10 Denver police tear gas Jethro Tull and 2000 fans at Red Rocks, 1971
+06/11 Greeks seize Troy, 1184BC
+06/11 Sauron attacks Osgilliath (LOTR)
+06/13 Bilbo returns to Bag End (LOTR)
+06/13 Pioneer flies past Neptune, and therefore out of the Solar System
+06/14 Sandpaper invented by I. Fischer, Jr., 1834
+06/15 Ben Franklin's kite experiment, 1752
+06/15 Magna Carta signed, 1215
+06/15 Series of photographs by Edward Muggeridge prove to Leland Stanford
+ that all the hooves of a horse are off the ground during the gallop,
+ 1878
+06/16 "The Blues Brothers" premieres in Chicago, 1980
+06/17 China explodes its first Hydrogen bomb, 1967
+06/17 Watergate Democratic National Committee break-in, 1972
+06/19 Julius and Ethel Rosenberg are executed in Sing-Sing prison, 1953
+06/19 Lizzie Bordon acquitted, 1893
+06/20 Victoria crowned, 1837
+06/21 Berlin airlift begins, 1948
+06/21 Sun rises over Heelstone at Stonehenge
+06/22 Civil rights workers disappear in Mississippi, 1964
+06/23 Slavery abolished in England, 1772
+06/23 Wedding of Ellesar & Arwen (LOTR)
+06/24 Senate repeals Gulf of Tonkin resolution, 1970
+06/25 Custer's Last Stand at Little Big Horn, 1876
+06/25 North Korea invades South Korea, 1950
+06/26 Battle of Gettysburg, 1863
+06/26 St. Lawrence Seaway dedicated by Eisenhower & Queen Elizabeth II, 1959
+06/26 Toothbrush invented, 1498
+06/27 100 degrees, Fort Yukon, 1915
+06/27 Bill Graham closes the Fillmore East, 1971
+06/28 Supreme Court decides in favor of Alan Bakke, 1978
+06/30 "That" explosion in Siberia, 1908
+06/30 China and Soviet Union announce split over ideology, 1960
+07/01 Battle of Gettysburg begins, 1863
+07/03 Dog days begin
+07/04 Battles of Vicksburg and Gettysburg won by Union forces, 1863
+07/04 Cloudy, 76 degrees, Philadelphia PA, 1776
+07/04 Gandalf imprisoned by Saruman (LOTR)
+07/04 New York abstains on Declaration of Independence vote, 1776
+07/04 Thoreau enters woods, 1845
+07/06 First `talkie' (talking motion picture) premiere in New York, 1928
+07/06 Lawrence of Arabia captures Aqaba, 1917
+07/07 First radio broadcast of "Dragnet", 1949
+07/08 First public reading of the Declaration of Independence, 1776
+07/08 Liberty Bell cracks while being rung at funeral of John Marshall, 1835
+07/09 10-hour working day set by law, NH, 1847
+07/10 134 degrees in Death Valley, 1913
+07/12 Minimum wages established: 40 cents/hour, 1933
+07/13 Women first compete in Olympic games, 1908
+07/16 Detonation of the first atomic bomb at Alamagordo, NM, 1945
+07/17 Disneyland opens, 1955
+07/18 Ty Cobb gets 4000th base hit, 1927
+07/19 Five Massachusetts women executed for witchcraft, 1692
+07/20 Armstrong and Aldrin land on moon, 1969
+07/21 First Train Robbery, Jesse James gets $3000 near Adair, Iowa, 1873
+07/21 Vietnam divided at 17th parallel, 1954
+07/23 Ice cream cone introduced, St. Louis MO, 1904
+07/24 Scopes Monkey Trial, 1925
+07/24 The ring comes to Bilbo (LOTR)
+07/26 Bilbo rescued from Wargs by Eagles (LOTR)
+07/30 "In God We Trust" made U.S. motto, 1956
+07/31 Harry S. Truman dedicates N.Y. Int'l Airport @ Idlewild Field, 1948,
+ later JFK
+08/01 Lughnasa; Feast of the god Lugh, a 30 day Celtic feast centers on
+ this day
+08/03 Columbus sets sail for Cathay, 1492
+08/03 Funeral of King Theoden (LOTR)
+08/03 USS Nautilus crosses under north polar ice cap, 1958
+08/04 Axe murder of Andrew and Abbey Borden, 1892
+08/04 Bombing of N. Vietnam begins, 1964
+08/04 Britain declares war on Germany starting World War I, 1914
+08/06 Atomic bomb dropped on Hiroshima, 1945
+08/06 Caricom in Barbados
+08/06 Cy Young pitches first game, 1890
+08/08 Atomic bomb dropped on Nagasaki, 1945
+08/08 Montenegro declares war on Germany, 1914
+08/08 Richard Nixon resigns the US presidency, 1974
+08/08 The Great Train Robbery -- $7,368,000, 1963
+08/09 Helter Skelter... the Charles Manson murders take place, 1969
+08/09 Persia defeats Spartan King Leonidas at Thermopylae, 480 BC
+08/09 US/Canada border defined in the Webster-Ashburton Treaty, 1842
+08/10 Chicago incorporated as a village of 300 people, 1833
+08/10 US and Panama agree to transfer the canal in the year 2000, 1977
+08/11 Dog days end
+08/11 France Ends War in Indochina, 1954
+08/11 Perseid meteor shower (look north; three days)
+08/12 First test flight of Space Shuttle "Enterprise" from 747, 1977
+08/12 Last U.S. ground troops out of Vietnam, 1972
+08/13 Berlin wall erected, 1961
+08/13 Li'l Abner debut, 1934
+08/14 Social Security begins in U.S., 1935
+08/15 Gandhi's movement obtains independence for Pakistan and India, 1947
+08/15 Hurricane hits Plimoth Plantation, 1635
+08/16 Roller Coaster patented, 1898
+08/17 First public bath opened in N.Y., 1891
+08/18 Anti-Cigarette League of America formed
+08/19 Air Force cargo plane snares payload from Discoverer 14 spy satellite,
+ marking start of practical military reconnaissance from space, 1960
+08/19 Gail Borden patents condensed milk, 1856
+08/22 Death of King Richard III, 1485, Last of the Plantagenets
+08/22 Joe Walker sets X-15 all time altitude mark (67 miles), 1963
+08/22 St. Columbia reports seeing monster in Loch Ness, 565
+08/23 Sacco and Vanzetti executed, 1927
+08/24 "Alice's Restaurant" premieres in New York and Los Angeles, 1969
+08/24 -126.9 F at Vostok, Antarctica, 1960
+08/24 British troops burn Washington, 1814
+08/25 Gen. DeGaulle leads French forces into Paris, 1944
+08/26 Women get the vote, 1920
+08/27 "Tarzan of the Apes" published, 1912
+08/27 Krakatoa, Java explodes with a force of 1,300 megatons, 1883
+08/28 King leads over 200K in civil rights rally in Washington, DC, 1963
+08/29 Star in Cygnus goes nova and becomes 4th brightest in sky, 1975;
+ Nova Cygni 1975.
+08/29 Saruman enters the Shire (LOTR)
+08/30 75 cents a pound tariff set on opium, 1842
+08/30 Japan Stationery Co. sells first felt-tipped pen, 1960
+08/30 St. Rose of Lima in Peru
+08/30 Washington-to-Moscow hot line connected, 1963
+08/31 269 people killed after Korean Airlines 747 shot down by USSR, 1983
+08/31 Mary Anne Nichols becomes Jack the Ripper's first victim, 1888
+08/31 Non-aggression pact signed by USSR and Afghanistan, 1926
+09/01 Bobby Fischer defeats Boris Spassky in World Chess Match, 1972
+09/01 Joshua A. Norton proclaims himself 'Emperor Norton I', 1859
+09/02 Great Britain adopts Gregorian Calendar, 1752
+09/02 Japan signs unconditional surrender on US battleship `Missouri', 1945
+09/03 Anniversary of the Founding of the Republic in San Marino
+09/05 Kennedy orders resumption of underground nuclear tests, 1961
+09/05 The first Continental Congress was convened in Philadelphia, 1774
+09/06 149 Pilgrims set forth from England aboard the Mayflower, 1620
+09/06 First Star Trek episode (The Man Trap) aired 1966
+09/06 Pres. McKinley shot, 1901
+09/06 Somhlolo in Swaziland
+09/08 "Star Trek" debuts on NBC (1966)
+09/08 Jack the Ripper kills again, Annie Chapman is second victim, 1888
+09/08 President Ford pardons Richard M. Nixon, 1974
+09/09 California becomes the 31st state, 1850
+09/09 United Colonies is renamed the United States, 1776
+09/10 Gandalf escapes from Orthanc (LOTR)
+09/10 Mountain Meadows Massacre. Mormons kill Gentile wagon train, 1857
+09/12 German paratroopers rescue Mussolini from captivity in Rome, 1943
+09/12 Germany annexes Sudentenland, 1938
+09/13 136.4 F at el Azizia, Libya, 1922
+09/13 British defeat the French at Abraham near Quebec City, 1788
+09/13 Building of Hadrian's Wall begun, 122
+09/13 Chiang Kai-Shek becomes president of China, 1943
+09/14 Benjamin Franklin is sent to France as an American minister, 1778
+09/14 Frodo & Bilbo's birthday (LOTR)
+09/14 Salem, Massachusetts, is founded, 1629
+09/14 The Selective Service Act establishes the first peacetime draft, 1940
+09/15 Black riders enter the Shire (LOTR)
+09/15 Soviet Premier Nikita Khrushchev begins his 13 day tour of the US, 1959
+09/15 The U.S. Foreign Affairs Dept. becomes the U.S. State Department, 1789
+09/16 The village of Shawmut, Massachusetts, becomes the city of Boston, 1630
+09/17 Battle of Antietam, 1862
+09/18 Frodo and company rescued by Bombadil (LOTR)
+09/18 Victory of Uprona in Burundi
+09/20 Equal Rights Party nominates Belva Lockwood for President, 1884
+09/20 First meeting of the American Association for the Advancement of
+ Science, 1848
+09/20 First meeting of the National Research Council, 1916
+09/20 Magellan leaves Spain on the first Round the World passage, 1519
+09/20 The Roxy Theater opens in Hollywood, 1973
+09/22 Allied forces form the independent nation West Germany, 1953
+09/22 President Lincoln issues the Emancipation Proclamation, 1862
+09/22 Special prosecutor Leon Jeworski subpoenaes President Nixon, 1974
+09/22 The first Soviet atomic bomb explodes, 1949
+09/23 Phillippine President Ferdinand Marcos declares martial law, 1972
+09/23 The New York Knickerbockers becomes the first U.S. Baseball club, 1845
+09/23 V.P. Nixon denies campaign fund fraud with his "Checkers" speech, 1952
+09/25 Sandra Day O'Connor becomes first woman on US Supreme Court, 1981
+09/27 The first passenger was hauled in a locomotive in England, 1825
+09/28 "Pilgrim's Progress" published, 1678
+09/28 A Greek soldier runs 26+ miles after the Persian defeat at Marathon,
+ 490BC
+09/28 Frodo wounded at Weathertop (LOTR)
+09/30 Red Jack kills 2, Elizabeth Stride (#3) and Catherine Eddowes (#4),
+ 1888
+09/30 The first tooth is extracted under anesthesia in Charleston, Mass, 1846
+09/30 The verdicts of the Nuremberg trials are announced, 1946
+10/01 NASA officially begins operations, 1958
+10/02 Thurgood Marshall sworn as the first black Supreme Court Justice, 1967
+10/04 Crimean war begins, 1853
+10/04 First space vehicle, Sputnik I, launched, 1957
+10/04 Sputnik 1, world's first orbiting satellite launched, 1957
+10/05 Frodo crosses bridge of Mitheithel
+10/06 Antioch College is the first public school to admit men and women, 1853
+10/06 Egyptian President Anwar Sadat is assassinated in Cairo, 1981
+10/06 Israel is attacked by the alliance of Egypt and Syria, 1973
+10/07 Foundation of the GDR in 1949 in German Democratic Republic
+10/07 Georgia Tech. beats Cumberland Univ. 222-0, 1916
+10/07 Maryland Governor Marvin Mandel sent to prison on fraud charges, 1977
+10/07 Mother Teresa of Calcutta awarded the Nobel Peace Prize, 1979
+10/07 Police stop Wilbur Mills car, Fanne Fox jumps into water, 1974
+10/08 Great Chicago Fire, 1871
+10/09 First two-way telephone conversation, 1876
+10/10 Beginning of the Wars for Independence in Cuba
+10/10 Foundation of the Workers Party in North Korea
+10/10 Mercury at Superior Conjunction with Sun. Moves into night sky. (1984)
+10/10 Spiro T. Agnew resigns as Vice-President due to income tax fraud, 1973
+10/11 "Saturday Night Live" premiers on NBC-TV, 1975
+10/11 The Gang of Four are arrested in Peking, 1976
+10/11 The first steam powered ferry ran between New York and Hoboken, 1811
+10/11 The second Vatican Ecumenical Council opens in Rome, 1962
+10/11 First broadcast of Saturday Night Live, 1975
+10/12 Bahama Natives discover Columbus of Europe lost on their shores, 1492
+10/12 Khrushchev pounds his desk with shoe during a speech to the UN, 1960
+10/12 Man O'War's last race, 1920
+10/12 Native Americans discover Columbus of Europe lost on their shores, 1492
+10/13 Italy declares war on Germany, 1943
+10/13 U.S. Navy born, 1775, authorized by the Second Continental Congress
+10/14 Battle of Hastings won by William the Conqueror and the Normans, 1066
+10/14 Chuck Yeager breaks sound barrier, 1947
+10/15 First draft card burned, 1965
+10/16 Boromir reaches Rivendell (LOTR)
+10/17 Council of Elrond (LOTR)
+10/18 Boston Shoemakers form first U.S. labor org., 1648
+10/18 Soviets anounce their probe took photos of the Moon's far side, 1959
+10/19 Mao Tse-tung establishes the People's Republic of China, 1949
+10/19 Napoleon's beaten army begins the long retreat from Moscow, 1812
+10/20 "Saturday Night Massacre", 1973
+10/20 OPEC embargo, 1973
+10/21 Edison makes the first practical incandescent lamp, 1879
+10/21 Guggenheim Museum opens, 1959
+10/23 Battle of Leyte Gulf begins, 1944
+10/23 Earth created at 6:30 AM, 4004BC.
+10/23 Swallows leave Capistrano
+10/25 End of War of the Ring (LOTR)
+10/25 The UN removes Taiwan and admits the People's Republic of China, 1971
+10/26 UN's World Health Organization declares smallpox eradicated, 1978
+10/27 New York's Boss Tweed is arrested on fraud charges, 1871
+10/27 The first New York Subway is opened, 1904
+10/28 Columbus discovers Cuba, 1492
+10/28 Constantine's army defeats forces of Maxentius at Mulvian Bridge, 312
+10/28 Harvard was founded in Massachusetts, 1636
+10/28 Statue of Liberty was dedicated on Bedloe's Island, 1886
+10/29 Stock Market Crash, 1929
+10/30 Orson Welles' "War of the Worlds" broadcast, 1938
+10/31 Luther nails 95 Theses to door of Castle Church, Wittenberg, 1517
+11/01 Austria-Hungary become two seperate nations, 1918
+11/01 Puerto Rican nationalists try to kill Truman at the Blair House, 1950
+11/02 Luftwaffe completes 57 consecutive nights of bombing of London, 1940
+11/02 Two Frenchmen make the first free hot air balloon flight, 1783
+11/03 Beef rises to 3 cents a pound, IL, 1837
+11/03 Linus Pauling wins Nobel Chemistry Prize, 1954
+11/03 Sputnik II launched, 1957, bearing spacedog Laika
+11/04 Iranian militants seize US embassy personnel in Teheran, 1979
+11/04 Soviet forces crush the anti-communist revolt in Hungary, 1956
+11/05 Guy Fawkes' Plot, 1605
+11/06 Anniversary of the October Socialist Revolution (2 days) in U.S.S.R.
+11/07 Abolitionist newspaperman Elijah P. Lovejoy murdered by mob, 1837
+11/07 Lewis and Clark Expedition in sight of the Pacific Ocean, 1805
+11/09 Blackout of New York, New England, and Eastern Canada, 1965
+11/09 Giant panda discovered (?!), China, 1927
+11/09 Jack the Ripper kills fifth and final victim, Jane Kelly, 1888
+11/09 Margaret Sanger forms American Birth Control League, 1921
+11/09 Roosevelt establishes the Civil Works Administration, 1933
+11/10 41 Women arrested in suffragette demonstartions near White House, 1917
+11/10 Cpt. Wirz, commandant of Andersonville Prison hanged, 1865
+11/10 Henry Stanley asks David Livingston, "Dr. Livingston, I presume?", 1871
+11/11 Washington becomes the 42nd state, 1889
+11/12 Dr. Sun Yat-sen's Birthday in Taiwan
+11/12 U.S. first exports oil to Europe, 1861
+11/14 Quarter Pounder price raised from $0.53 to $0.55 in violation of Nixon
+ price controls (but okayed by Price Commission after formal request
+ from McDonald's), 1971
+11/15 Niagara Falls power plant startup, 1896
+11/16 Bilbo reaches the Lonely Mountain (LOTR)
+11/16 Opening of the Suez Canal, 1869
+11/17 46,000 meteoroids fall over AZ in 20 minutes, 1966
+11/17 Richard Nixon says "I am not a crook.", 1973
+11/18 First hydrogen bomb blasts Enewetok, 1952
+11/18 Local standard time zones established for US, 1883
+11/19 Gettysburg Address delivered, 1863
+11/21 Announcement of 18 1/2 minute gap on Watergate tape, 1973
+11/22 Kennedy shot in Dallas, Texas by Lee Harvey Oswald, 1963
+11/23 First broadcast of Dr. Who (longest running TV series), 1963
+11/24 Lee Harvey Oswald killed by Jack Ruby, 1963
+11/25 Alfred Nobel invents dynamite, 1867
+11/27 Alfred Nobel establishes Nobel Prize, 1895
+11/27 Friction match invented, England, 1826
+11/27 Hoosac Railroad Tunnel completed, 1873, in NW Massachusetts
+11/29 King Tut's tomb opened, 1922
+12/01 First national corn-husking championship, Alleman IA, 1924
+12/01 Martin Luther King Jr., leads black boycott of Montgomery buses, 1955
+12/01 Rosa Parks refuses to move to back of the bus (Montgomery, AL), 1953
+12/03 First neon light display, Paris, 1910
+12/03 First successful human heart transplant lead by Dr. Barnard, 1967
+12/03 The Montreux Casino burns down during a Frank Zappa concert, 1971
+12/04 Washington takes leave of his officers at Fraunce's Tavern, NYC, 1783
+12/05 Death of Smaug (LOTR)
+12/05 End of Prohibition, 1933 (at least the alcohol part)
+12/05 Phi Beta Kappa founded, 1776
+12/05 The Eighteenth Amendment repealed, ending Prohibition, 1933
+12/07 Japan bombs Pearl Harbor, 1941
+12/09 Ball-bearing roller skates patented, 1884
+12/10 Metric system established in France, 1799
+12/10 Nobel Peace Prize awarded each year
+12/12 First wireless message sent across Atlantic by Marconi, 1901
+12/13 Apollo 17 leaves the moon, with "last" men to walk on moon aboard, 1972
+12/13 Dartmouth College chartered, 1769
+12/13 Geminid meteor shower (look south)
+12/15 Argo Merchant oil spill, 1976
+12/15 Bill of Rights adopted, 1791
+12/15 James Naismith invents basketball, Canada, 1891
+12/15 Sitting Bull shot in head while submitting to arrest, 1890
+12/16 Fellowship begins Quest (LOTR)
+12/20 U.S. buys ~1,000,000 sq. miles of Louisiana for ~$20/sq.mi.
+12/21 Phileas Fogg completes his trip around the world in less than 80 days
+12/24 KKK formed in Pulaski, Tenn, 1865
+12/26 DPMA founded, 1951
+12/27 APT report published, 1956
+12/27 Ether first used as anesthetic in childbirth, 1845
+12/28 Comet Kohoutek at perihelion, 1973
+12/29 Battle of Wounded knee, 1890
+12/30 First Los Angeles freeway dedicated, 1940
+12/31 St. Sylvester in Switzerland
+12/31 Winterland closes its doors, 1978
diff --git a/usr.bin/calendar/calendars/calendar.holiday b/usr.bin/calendar/calendars/calendar.holiday
new file mode 100644
index 0000000..c39ca41
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.holiday
@@ -0,0 +1,568 @@
+01/01 Independence Day in Haiti, Sudan
+01/01 Universal Fraternity Day in Mozambique
+01/02 Ancestry Day in Haiti
+01/02 St. Berchtold's Day in Switzerland
+01/03 New Year's Holiday in Scotland
+01/03 Revolution Day in Upper Volta
+01/04 Independence Day in Burma
+01/04 Martyrs Day in Zaire
+01/06 Children's Day in Uruguay
+01/06 Three Kings' Day in Puerto Rico
+01/07 Christmas in Ethiopia
+01/07 Pioneer's Day in Liberia
+01/09 Day of the Martyrs in Panama
+01/11 Armed Forces Day in Liberia
+01/12 Zanzibar Revolution Day in Tanzania
+01/13 National Liberation Day in Togo
+01/15 Adults Day in Japan
+01/15 Arbor Day in Jordan
+01/16 Martyrs Day in Benin
+01/18 Revolution Day in Tunisia
+01/19 Confederate Heroes Day in Texas
+01/19 Ethopian Epiphany in Ethiopia
+01/19 Nameday of Archbishop Makarios in Cyprus
+01/20 Army Day in Mali
+01/20 National Heroes Day in Guinea-Bissau
+01/20* Lee-Jackson Day in Virginia (3rd Monday)
+01/20* Martin Luther King Day in New York (3rd Sunday)
+01/20* Robert E. Lee's Birthday in Alabama & Mississippi (3rd Monday)
+01/21 Our Lady of Altagracia in Dominican Republic
+01/21* Lee-Jackson Day in Virginia (3rd Monday)
+01/23 Feast of St. Ildefonsus
+01/23 National Handwriting Day
+01/24 Economic Liberation Day in Togo
+01/26 Republic Day in India
+01/30 Australia Day in Australia
+02/01 Chinese New Year Holiday (3 days) in Taiwan
+02/02 Candlemas
+02/04 Independence Commemoration Day in Sri Lanka
+02/05 Constitution Day in Mexico
+02/06 New Zealand Day
+02/07 Independence Day in Grenada
+02/09 St. Maron's Day in Lebanon
+02/10 Feast of St. Paul's Shipwreck, AD 60
+02/11 National Foundation Day in Japan
+02/12 Pyidaungsa Day in Burma
+02/16 Makha Bucha Day in Thailand
+02/18 Democracy Day in Nepal
+02/18 Independence Day in The Gambia
+02/23 Republic Day in Guyana
+02/24 Gregorian Calendar Day
+02/25 National Day in Kuwait
+02/27 Independence Day in Dominican Republic
+03/01 Samil Independence Movement Day in South Korea
+03/01 St. David's Day, Cardiff
+03/02 Peasants Day in Burma
+03/02 Texas Independence day
+03/02 Victory of Adowa in Ethiopia
+03/03 Girl's Day in Japan
+03/03 Throne Day in Morocco
+03/04 Vermont Admission Day (admitted as 14th state in 1791)
+03/05 Independence Day in Equatorial Guinea
+03/06 Lantern Day, Bejing
+03/07* Purim - Feast of Lots
+03/08 First Annual International Women's Day, 1909
+03/08 International Women's Day in U.S.S.R.
+03/08 Syrian National Day in Libyan Arab Republic
+03/08 Women's Day in Guinea-Bissau, Taiwan, Yemen Democratic Republic
+03/08 Youth Day in Zambia
+03/09 Decoration Day in Liberia
+03/09 Falgun Purnima Day in Nepal
+03/10 Labor Day in South Korea
+03/11 Johnny Appleseed Day; anniversary of the death of John Chapman
+03/12 Commonwealth Day in Swaziland
+03/12 Independence Day in Mauritius
+03/12 Moshoeshoe's Birthday in Lesotho
+03/12 Renovation Day in Gabon
+03/13 National Day in Grenada
+03/16 Black Press Day; first Black newspaper founded in 1827
+03/17 Evacuation Day in Suffolk County, Massachusetts
+03/17 St. Patrick's Day
+03/19 St. Joseph's Day in Colombia, Costa Rica, Holy See, Liechtenstein,
+ San Marino, Spain, Venezuela
+03/19 Tree Planting Day in Lestho
+03/20 Independence Day in Tunsia
+03/20 Youth Day in Oklahoma
+03/21 Afghan New Year in Afghanistan
+03/21 Juarez' Birthday in Mexico
+03/22 Abolition Day in Puerto Rico
+03/23 Pakistan Day in Pakistan
+03/25 Greek Independence Day in Cyprus
+03/25 Lady Day (a.k.a. the Feast of the Annunciation)
+03/25 Maryland Day in Maryland
+03/25 National Holiday in Greece
+03/25* Seward's Day in Alaska (last Monday)
+03/26 Independence Day in Bangladesh
+03/26 Prince Jonah Kuhio Kalanianaole Day in Hawaii
+03/27 Armed Forces Day in Burma
+03/29 Death of President Barthelemy Boganda in Central African Republic
+03/29 Memorial Day in Madagascar
+03/31 National Day in Malta
+04/01 Youth Day in Benin
+04/02 Malvinas Day in Argentina
+04/02 Pascua Florida Day in Florida
+04/04 Ching Ming Festival in Hong Kong
+04/04 Liberation Day in Hungary
+04/04 National Day in Senegal
+04/05 Arbor Day in South Korea
+04/05 Tomb Sweeping Day in Taiwan
+04/06 Chakri Memorial Day in Thailand
+04/06 Victory Day in Ethiopia
+04/08 Fast and Prayer Day in Liberia
+04/09 Martyrs Day in Tunisia
+04/11 National Heroes Day in Costa Rica
+04/13 National Day in Chad
+04/13 Songkron Day in Thailand
+04/14 Day of the Americas in Honduras
+04/15 Bengali New Year in Bangladesh
+04/15* Patriot's Day in Maine & Massachusetts (3rd Monday)
+04/16 De Diego's Birthday (celebrated in Puerto Rico)
+04/16 Holy Week (5 days) in Venezuela
+04/16 Tourist Week (5 days) in Uruguay
+04/17 Burmese New Year in Burma
+04/18 Independence Day in Chile, Zimbabwe
+04/19 Declaration of Independence in Venezuela
+04/19 Republic Day in Sierra Leone
+04/21 San Jacinto Day in Texas
+04/22 Arbor Day in Nebraska & Delaware
+04/22 Oklahoma Day in Oklahoma
+04/24 Victory Day in Togo
+04/24* Pesach - First Day of Passover - Festival of Freedom
+04/25 Anzac Day in Australia, New Zealand, Tonga, Western Samoa
+04/25 Liberation Day in Italy
+04/25 National Flag Day in Swaziland
+04/26 Confederate Memorial Day in Florida & Georgia
+04/26 Union Day in Tanzania
+04/27 Independence Day in Togo
+04/28* Arbor Day in Wyoming (last Monday)
+04/28* Confederate Memorial Day in Alabama & Mississippi (last Monday)
+04/30 The Workers Day in Uruguay
+05/01 Labor Day in many places
+05/01 Law Day (decl. by Eisenhower)
+05/01 May Day in many places
+05/02 Constitution Day in Japan
+05/04 Rhode Island Independence Day
+05/05 Children's Day in Japan, South Korea
+05/05 Coronation Day in Thailand
+05/05 Liberation Day in Netherlands
+05/06 Bataan Day in Philippines
+05/06* Bank Holiday in UK
+05/07 May Day in United Kingdom
+05/08 Truman Day in Missouri
+05/09 Liberation Day in Czechoslovakia
+05/09 Victory Day in Poland, U.S.S.R.
+05/10 Confederate Memorial Day in South Carolina
+05/10 Mothers Day in Guatamala
+05/11 Minnesota Day in Minnesota
+05/14 Buddhist Holiday (Waisak 2528) in Indonesia
+05/14 Independence Day (2 days) in Paraguay
+05/14 Unification Day in Liberia
+05/15 Kamuzu Day in Malawi
+05/15 Vesak Day in Singapore, Malaysia
+05/15 Visakha Bucha Day in Thailand
+05/16 Discovery Day in Cayman Islands
+05/17 Constitution Day in Nauru, Norway
+05/18 Flag Day in Haiti
+05/18 Prayer Day in Denmark
+05/19 Youth and Sports Day in Turkey
+05/19* Memorial Day in Michigan (3rd Monday)
+05/20 Mecklenburg Independence Day in North Carolina
+05/20 National Day in Cameroon
+05/20 Victoria Day in Canada
+05/20* Memorial Day in Michigan (3rd Monday)
+05/22 National Heroes Day in Sri Lanka
+05/23 Commonwealth Day in Jamaica, Belize
+05/23 National Labor Day in Jamaica
+05/24 Bermuda Day in Bermuda
+05/24 Day of Slav Letters in Bulgaria
+05/25 African Freedom Day in Zimbabwe
+05/25 African Liberation Day in Chad, Mauritania, Zambia
+05/25 Independence Day in Jordan
+05/25 Memorial Day in New Mexico & Puerto Rico
+05/26* First Day of Shavuot
+05/27* Bank Holiday in UK
+05/28 Mothers Day in Central African Republic
+05/31 Pya Martyrs Day in Togo
+05/31 Republic Day in South Africa
+06/01 Independence Days (3 days) in Western Samoa
+06/01 Madaraka Day in Kenya
+06/01 Victory Day in Tunisia
+06/03 Confederate Memorial Day in Kentucky & Louisiana
+06/03 Labor Day in Bahamas
+06/03* Bank Holiday in Rep. of Ireland
+06/04 Emancipation Day in Tonga
+06/05 Constitution Day in Denmark
+06/05 Liberation Day in Seychelles
+06/06 Memorial Day in South Korea
+06/09 Senior Citizen's Day in Oklahoma
+06/10 Camoes Day in Portugal
+06/11 King Kamehameha I Day in Hawaii
+06/12 Independence Day in Philippines
+06/14 Flag Day
+06/17 Bunker Hill Day in Suffolk County, Massachusetts
+06/17 Independence Day in Iceland
+06/17 National Day in Federal Republic of Germany
+06/18 Evacuation Day in Egypt
+06/19 Emancipation Day in Texas
+06/19 Labor Day in Trinidad, Tobago
+06/19 Revolution Day in Algeria
+06/20 Flag Day in Argentina
+06/20 West Virginia Day in West Virginia
+06/22 National Sovereignty Day in Haiti
+06/23 National Holiday in Luxembourg
+06/24 Fisherman's Day in Madagascar, Mozambique, Somalia
+06/24 Kings Day in Spain
+06/24 Peasants Day in Peru
+06/24 St. Jean-Baptiste Day in Quebec
+06/28 Mothers Day in Central African Republic
+06/29 Independence Day in Seychelles
+06/29 Last Day of Ramadan* in Algeria, Oman
+06/30 Day of the Army in Guatemala
+07/01 Dominion Day in Canada
+07/01 Freedom Day in Suriname
+07/01 Independence Day in Burundi
+07/01 National Day in Rwamda
+07/01 Republic Day in Ghana
+07/02 National Day in Kiribati
+07/04 Caribbean Day in Guyana
+07/04 Constitution Day in Cayman Islands
+07/04 Family Day in Lesotho
+07/04 Heroes Day in Zambia
+07/04 Kadooment Day in Barbados
+07/04 Philippine-American Friendship Day in the Philippines
+07/04 Warriors Day (2 days) in Yugoslavia
+07/05 Day of Peace and Unity in Rwanda
+07/05 Independence Day in Algeria, Venezuela
+07/07 National Day in Malawi
+07/07 Saba Saba Day in Tanzania
+07/09 Independence Day in Argentina
+07/10 Independence Day in Bahamas
+07/11 National Holiday in the Mongolian People's Republic
+07/14 Bastille Day
+07/14 National Holiday in Monaco
+07/15 St. Swithin's Day
+07/16 Presidents Day in Botswanna
+07/17 Constitution Day in South Korea
+07/17 Public Holiday in Botswanna
+07/18 Constitution Day in Uruguay
+07/18 Liberation Day in Nicaragua
+07/19 Martyrs Day in Burma
+07/20 Independence Day in Colombia
+07/21 National Holiday in Belgium
+07/22 National Day in Poland
+07/23 Eqyptian National Day in Syrian Arab Republic
+07/23 Remembrance Day in Papua, New Guinea
+07/24 Pioneer Day in Utah
+07/24 Simon Bolivar's Day in Ecuador, Venezuela
+07/25 Constitution Day in Puerto Rico
+07/25 National Rebellion Day (3 days) in Cuba
+07/25 Republic Day in Tunisia
+07/26 Independence Day in Liberia
+07/26 National Day in Maldives
+07/28 Independence Days (2 days) in Peru
+07/29 Rain Day in Waynesburg, PA
+07/31 Revolution Day in Congo
+08/01 Discovery Day in Trinidad, Tobogo
+08/01 Emancipation Day in Granada
+08/01 Freedom Day in Guyana
+08/01 National Day in Switzerland
+08/01 National Holidays (5 days) in El Salvador
+08/01 Parent's Day in Zaire
+08/03 Independence Day in Jamaica, Niger
+08/03 Memorial Day of Archbishop Makarios in Cyprus
+08/04 Freedom Day in Guyana
+08/05* Bank Holiday in Scotland and Northern Ireland
+08/06 Bank Holiday in Australia, British Columbia, Fiji, Iceland, Ireland,
+ Ontario
+08/06 Emancipation Day in Bahamas
+08/06 Independence Day in Bolivia
+08/09 National Day in Singapore
+08/10 Independence Day in Ecuador
+08/11 Heroes Day (2 days) in Zimbabwe
+08/11 Independence Day in Chad
+08/13 Women's Day in Tunisia
+08/14 Independence Day in Pakistan
+08/14 VJ Day, 1945
+08/15 Independence Day in India
+08/15 Liberation Day in South Korea
+08/15 National Day in Congo
+08/15* Admission Day in Hawaii, 1984 (3rd Friday)
+08/16 Bennington Battle Day in Vermont
+08/16 Independence Days (3 days) in Gabon
+08/16 Restoration Day in Dominican Republic
+08/17 Independence Day in Indonesia
+08/19 Independence Day in Afghanistan
+08/20 Constitution Day in Hungary
+08/23 Liberation Days (2 days) in Romania
+08/24 National Flag Day in Liberia
+08/25 Constitution Day in Paragual
+08/25 Independence Day in Uruguay
+08/26 Susan B. Anthony Day in Massachussetts
+08/26* Bank Holiday in England and Wales
+08/27 Liberation Day in Hong Kong
+08/28 Heroes Day in Philippines
+08/30 Huey P. Long Day in Louisiana
+08/30 Victory Day in Turkey
+08/31 Independence Day in Trinidad, Tobago
+08/31 National Day in Malaysia
+08/31 Pashtoonian Day in Afghanistan
+09/01 Army Day in Chile
+09/03 Independence Day in Qatar
+09/03 Memorial Day in Tunisia
+09/06 Defense of Pakistan Day in Pakistan
+09/06 Settlers Day in South Africa
+09/07 Independence Day in Brazil
+09/09 Admission Day in California
+09/09 National Day in North Korea
+09/10 Korean Thanksgiving Day (Chusuk) in South Korea
+09/10 National Day in Belize
+09/11 National Holiday in Chile
+09/12 Defender's Day in Maryland
+09/12 Revolution Day in Ethiopia
+09/13 Barry Day commemorates the death of Commodore John Barry
+09/15 Respect for the Aged Day in Japan
+09/16 Cherokee Strip Day in Oklahoma
+09/16 Independence Day in Mexico, Papua, New Guinea
+09/17 National Heroes Day in Angola
+09/18 Independence Day in Chile, Zimbabwe
+09/19 Army Day in Chile
+09/21 Independence Day in Belize
+09/22 Independence Day in Mali
+09/22 National Sovereignty Day in Haiti
+09/24 Independence Day in Guinea-Bissau
+09/24 National Day in Saudi Arabia
+09/24 Republic Day in Trinidad, Tobago
+09/25 Army Day in Mozambique
+09/25 Referendum Day in Rwanda
+09/26 National Day in Maldives, Yemem Deomcratic Republic
+09/26 Revoluation Anniversary Day in Yemen Arab
+09/28 Confucious' Day in Taiwan
+09/30 Botswanna Day in Botswanna
+09/30 First Day of Sukkot
+10/01 Armed Forces Day in South Korea
+10/01 Independence Day in Nigeria
+10/01 Labor Day in Australia
+10/01 National Liberation Day (2 days) in China
+10/01 Public Holiday in Botswanna
+10/03 National Foundation Day in South Korea
+10/03 U.N. Day in Varbados
+10/04 Independence Day in Lesotho
+10/06 National Sports Day in Lesotho
+10/07 National Heroes Day in Jamaica
+10/08 Constitution Day in U.S.S.R
+10/08 Fiji Day in Fiji
+10/08 Thanksgiving Day in Canada
+10/09 Independence Day in Uganda
+10/09 Korean Alphabet Day in South Korea
+10/09 Leif Erikson Day commemorates the discovery of North America in AD 1000
+10/09 Republic Day in Khmer Republic
+10/10 Fiji Day in Fiji
+10/10 Health-Sports Day in Japan
+10/10 National Day in Taiwan
+10/10 Oklahoma Historical Day in Oklahoma
+10/11 Day of the Revolution in Panama
+10/11 Druger Day in South Africa
+10/12 Day of the Race in Argentina
+10/12 Discovery Day in Gahamas
+10/12 National Day in Equatorial Guinea, Spain
+10/12 Our Lady Aparecida Day in Brazil
+10/12 Pan American Day in Belize
+10/13 St. Edward's Day - Patron saint of England
+10/14 National Day in Yemen Arab Repyblic
+10/14 Young People's Day in Zaire
+10/14* Thanksgiving Day in Canada
+10/15 Evacuation Day in Tunisia
+10/16 National Boss Day
+10/17 Heroes Day in Jamaica
+10/17 Mother's Day in Malawi
+10/20 Kenyatta Day in Kenya
+10/21 Armed Forces Day in Honduras
+10/21 Revolution Days (2 days) in Somalia
+10/23 Chulalongkron's Day in Thsailand
+10/24 Independence Day in Zambia
+10/24 United Nations Day
+10/25 Labor Day in New Zealand
+10/25 Taiwan Restoration Day in Taiwan
+10/26 Agam Day in Nauru
+10/26 Armed Forces Day in Benin, Rwanda
+10/26 National Day in Austria
+10/28 National Holiday in Greece
+10/28 OHI Day in Cyprus
+10/28* Bank Holiday in Rep. of Ireland
+10/29 Republic Day in Turkey
+10/31 Nevada Day in Nevada
+11/01 All Saints Day
+11/02 All Souls Day in Bolivia, Brazil, El Salvador, Uruguay
+11/02 Memorial Day in Ecuador
+11/03 Culture Day in Japan
+11/03 Thanksgiving Day in Liberia
+11/04 Flag Day in Panama
+11/04 Will Rogers Day
+11/06 Green March Day in Morocco
+11/07 National Revolution Day
+11/07 October Revolution Day in Hungary
+11/11 Independence Day in Angola
+11/11 Rememberance Day in Canada
+11/11 Republic Day in Maldives
+11/15 Dynasty Day in Belgium
+11/17 Army Day in Zaire
+11/18 Independence Day in Morocco
+11/18 National Days (4 days) in Oman
+11/19 Discovery Day in Puerto Rico
+11/19 Feast Day of S.A.S. Prince Rainier in Monaco
+11/20 Revolution Day in Mexico
+11/21 Day of Prayer and Repentance in Federal Republic of Germany
+11/22 Independence Day in Lebanon
+11/23 Labor Thanksgiving Day in Japan
+11/25 Independence Day in Suriname
+11/28 Independence Day in Albania, Mauritania
+11/29 Day of the Republic (2 days) in Yugoslavia
+11/29 Goodwill Day in Liberia
+11/29 Liberation Day in Albania
+11/29 National Day in Burma
+11/30 Independence Day in Barbados, Yemen Deomcratic Republic
+11/30 National Day in Benin
+11/30 National Heroes Day in Philippines
+11/30 St. Andrew's Day
+12/01 Independence Day in Central African Republic
+12/02 National Holiday in United Arab Emirates
+12/03 National Holiday in Laos
+12/06 Independence Day in Finland
+12/07 Delaware Day in Delaware
+12/07 Independence Day in Ivory Coast, Panama
+12/08 Mother's Day in Panama
+12/09 Independence Day in Tanzania
+12/10 Human Rights Day
+12/10 Thai Constitution Day in Thailand
+12/10 Wyoming Day in Wyoming
+12/11 Independence Day in Upper Volta
+12/12 Independence Day in Kenya
+12/13 Republic Day in Malta
+12/15 Statue Day in Netherlands Antilles
+12/16 Constitution Day in Nepal
+12/16 Day of the Covenant in South Africa
+12/16 National Day in Bahrain
+12/16 Victry Day in Bangladesh
+12/17 National Day in Bhutan
+12/18 Republic Day in Niger
+12/23 Victory Day in Egypt
+12/25 Children's Day in Congo
+12/26 Bank Holiday in Canada, Rep. of Ireland, and UK
+12/26 Boxing Day
+12/26 Family Day in South Africa
+12/26 St. Stephen's Day
+12/26 Bank Holiday in Canada, Rep. of Ireland, and UK
+12/27 Bank Holiday in Cayman Islands
+12/27 Constitution Day in North Korea
+12/27 Public Holiday in Lesotho, Zimbabwe
+12/29 Civic Holidays (3 days) in Costa Rica
+12/31 Bank Holiday in El Salvador, Honduras, Pakistan
+12/31 Feed Yourself Day in Benin
+
+04/21 Tiradentes in Brazil
+04/25 Anniversary of the Revolution in Portugal
+04/29 Emperor's Birthday in Japan
+04/30 Queen's Birthday in Netherlands, Netherlands Antilles
+05/01 Boy's day in Japan
+05/02 King's Birthday in Lesotho
+05/05 Battle of Puebla in Mexico
+05/08 Buddha's Birthday in South Korea
+05/08 Elections for the National Assembly in Philippines
+05/14 Anniversary of the Founding of Guinean Deomcratic Party in Guinea
+
+05/25 Anniversary of the Revolution of 1810 in Argentina
+05/25 Revolution in the Sudan in Libyan Arab Republic
+05/27 Afghanistan attains sovereignty, 1921
+06/02 Corpus Christi in Paraguay
+06/03 Jefferson Davis's Birthday in Alabama & Mississippi (1st Monday)
+06/03 Jefferson Davis's Birthday in Florida, Georgia, & S. Carolina
+06/04 Queen's Birthday in New Zealand
+06/06 His Majesty, Yang Di-Pertuan Agong's Birthday in Malaysia
+06/11 Queen's Birthday
+06/12 Peace with Bolivia in Paraguay
+06/13 Corrective Movement in Yemen Arab Republic
+06/16 Bloomsday - Anniversary of Dublin events, 1904, in "Ulysses"
+06/18 Queen's Birthday in Fiji
+06/19 Artigas Birthday in Uruguay
+06/22 Corrective Movement in Yermen Democratic Republic
+06/22 Midsummer Eve in Finland, Sweden
+06/24 Battle of Carabobob in Venezuela
+07/01 Eid-Ul-Fitr* (2 days) in Pakistan
+07/01 Union of the Somalia Republic in Somalia
+07/07 Anniversary of the P.U.N. in Equatorial Guinea
+07/12 Battle of Boyne celebrated in Northern Ireland
+07/12 The Twelfth in Northern Ireland
+07/13 Buddhist Lent in Thailand
+07/14 Anniversary of the Revolution in Iraq
+07/17 July Revolution in Iraq
+07/17 Munoz Rivera's Birthday (celebrated in Puerto Rico)
+07/22 King's Birthday in Swaziland
+07/23 Anniversary of the Revolution in Egypt
+07/25 St. James, Patron Saint in Spain
+07/27 Barbosa's Birthday (celebrated in Puerto Rico)
+07/29 Olsok Eve in Norway to commemorate Norway's Viking King St. Olav
+08/01 Founding of Asuncion in Paraguay
+08/02 Our Lady of Los Angeles in Costa Rica
+08/03 Massacre du Pidjiguiti in Buinea-bissau
+08/07 Battle of Boyaca in Colombia
+08/11 King Hussein's Accession to the Throne in Jordan
+08/12 Queen's Birthday in Thailand
+08/13 Proclamation of Independence in Central African Republic
+08/14 Waddi Dhahab in Morocco
+08/15 Founding of Ascuncion in Paraguay
+08/15 Santa Maria in Malta
+08/17 Anniversary of the Death of General San Martin in Argentina
+09/09 Anniversary of the Socialist Revolution (2 days) in Bulgaria
+09/10 Moon Festival in Taiwan
+09/11 Aniversary of military coup in Chile
+09/11 Ethiopian New Year in Ethiopia
+09/12 Amilcar Cabral's Birthday in Guinea-Bissau
+09/14 Battle of San Jacinto in Nicaragua
+09/15 Foundation of Panama in Panama
+09/23 Grito de Lares in Puerto Rico
+09/24 Anniversary of the Third Republic in Ghana
+09/24 Our Lady of Mercedes in Dominican Republic
+09/27 Feast of Finding the True Cross in Ethiopia
+09/29 Battle of Boqueron in Paraquay
+10/02 Anniversary of Guinean Independence in Guinea
+10/03 Chung Yeung Festival in Hong Kong
+10/03 Francisco Morazan's Birthday in Honduras
+10/05 Anniversary of Proclamation of the Republic in Portugal
+10/08 Battle of Agamos in Peru
+10/09 Independence of Guayaquil in Ecuador
+10/17 Dessaline's Death Anniversary in Haiti
+10/20 Anniversary of the 1944 Revolution in Guatemala
+11/01 Feast of All Saints in Portugal
+11/01 Samhain; Beginning of the Celtic year and most important holiday.
+11/03 Independence from Columbia in Panama
+11/03 Independence of Cuenca in Ecuador
+11/06 Prophet Mohammed's Birthday in Malaysia
+11/07 Anniversary of Great October Revolution in Bulgaria
+11/08 Her Majesty, the Queen's Birthday in Nepal
+11/10 King's Birthday in Bhutan
+11/11 Angola gains independence from Portugal, 1975
+11/11 Independence of Cartagena in Colombia
+11/12 Prince Charles' Birthday in Fiji
+11/14 King Hussein's Birthday in Jordan
+11/15 Proclamation of the Republic in Brazil
+11/15 Thatlouang Festival in Laos
+11/16 Oklahoma Heritage Week in Oklahoma
+11/17 Corrective Movement in Syrian Arab Republic
+11/18 Battle of Viertieres in Haiti
+11/19 Anniversary of the 1968 Coup by the Army in Mali
+11/19 Garifuna Settlement in Belize
+11/19 Prince of Wales Birthday in Fiji
+11/22 Anniversary of Portuguese Aggression in Guinea
+11/24 Anniversary of the New Regime in Zaire
+11/28 Independence from Spain in Panama
+11/28 Proclamation of the Republic in Chad
+12/01 Anniversary of the Restoration of Independence in Portugal
+12/07 Prophet Mohammed's Birthday in Fiji
+12/08 Blessing of the Water in Uruguay
+12/08 Our Lady of the Cacupe in Paraguay
+12/10 Foundation of Worker's Party in Angola
+12/25 Birthday of Quaid-i-Azam in Pakistan
+12/26 Feast of Our Theotokos in Greece
+12/29 His Majesty, the King's Birthday in Nepal
+12/30 Anniversary of the Democratic Republic of Madagascar in Madagascar
+12/31 Proclamation of the Republic in Congo
diff --git a/usr.bin/calendar/calendars/calendar.judaic b/usr.bin/calendar/calendars/calendar.judaic
new file mode 100644
index 0000000..df39ad5
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.judaic
@@ -0,0 +1,30 @@
+03/08* Fast of Esther (Battle of Purim; 1 day before Purim; fast day)
+03/11* Purim (Feast of Lots; 30 days before Pesach)
+03/12* Purim (Feast of Lots)
+04/10* Pesach (First Day of Passover; sabbatical)
+04/11* Pesach (sabbatical)
+04/16* Pesach (sabbatical)
+04/17* Pesach (Last Day of Passover; 8th day of Pesach; sabbatical)
+04/30* Yom HaAtzmaut (Israel Independence Day)
+05/13* Lag Ba`omer (Commemoration of the Great Rebellion)
+05/22* Yom Yerushalayim (Reunification of Jerusalem)
+05/30* Shavuos (Festival of Weeks; 50 days after Pesach; sabbatical)
+05/31* Shavuos (Festival of Weeks; sabbatical)
+07/10* Fast of Shiv'a Asar B'Tammuz (Romans breach Wall of Jerusalem;
+ fast day)
+07/31* Fast of Tish'a B'Av (Babylon/Rome destroys Holy Temple; fast day)
+09/20* First Day of Rosh Hashanah (Jewish Lunar New Year; 5741 == 1980;
+ sabbatical)
+09/21* Rosh Hashanah (sabbatical)
+09/23* Fast of Gedalya (Murder of Gedalya and subsequent Exile; 1 day
+ after Rosh Hashanah; fast day)
+09/29* Yom Kippur (Day of Atonement; 9 days after Rosh Hashanah;
+ sabbatical, fast day)
+10/04* Succos (Festival of Tabernacles; 14 days after Rosh Hashanah;
+ sabbatical)
+10/05* Succos (sabbatical)
+10/10* Hoshanah Rabba (7th day of Succos)
+10/11* Shmini Atzeres (8th Day of Gathering; 1 day after Succos; sabbatical)
+10/12* Shmini Atzeres/Simchas Torah (Rejoicing of the Law; sabbatical)
+12/12* First Day of Chanukah
+12/27* Fast of Asara B'Tevet (Babylonians put siege on Jerusalem; fast day)
diff --git a/usr.bin/calendar/calendars/calendar.music b/usr.bin/calendar/calendars/calendar.music
new file mode 100644
index 0000000..90ddf38
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.music
@@ -0,0 +1,178 @@
+01/01 Country Joe McDonald is born in El Monte, California, 1942
+01/03 Steven Stills is born in Dallas, 1945
+01/04 Jazz great Charlie Mingus dies at 57 in Cuernavaca, Mexico, 1979
+01/08 David Bowie (then David Robert Jones) is born in London, 1947
+01/09 James Patrick Page (Led Zeppelin) is born in Middlesex, England, 1945
+01/10 Blues guitarist Howlin' Wolf dies in Chicago, 1976
+01/10 Jim Croce is born in Phildelphia, 1943
+01/10 Pat Benatar is born in Long Island, 1952
+01/10 Rod Stewart is born in Glasgow, Scotland, 1945
+01/13 Eric Clapton plays the "Rainbow Concert" in London, 1973
+01/17 Led Zeppelin's first album is released, 1969
+01/19 Janis Joplin is born in Port Arthur, Texas, 1943
+01/22 Sam Cooke is born in Chicago, 1935
+01/24 Warren Zevon is born, 1947
+01/25 Bob Dylan plays the second "Hurricane" benefit, in the Astrodome, 1978
+01/27 Bobby "Blue" Bland (Robert Calvin Bland) is born in Tennessee, 1930
+01/27 Wolfgang Amadeus Mozart born in Salzburg, 1756
+01/28 Jimi Hendrix headlines Madison Square Garden, 1970
+01/30 Lightnin' Hopkins, the most-recorded blues artist ever, dies, 1982
+01/31 The Grateful Dead are busted in New Orleans, 1970
+02/01 RCA Victor unveils the 45 rpm record playing system, 1949
+02/02 Graham Nash is born in Lancashire, England, 1942
+02/03 The Day The Music Died; Buddy Holly, Richie Valens, and the Big
+ Bopper are killed in a plane crash outside Mason City, Iowa, 1959
+02/07 Beatles land at JFK airport to begin first U.S. tour, 1964
+02/07 Steven Stills makes the first digitally recorded rock album, 1979
+02/09 Carole King (Carole Klein) is born in Brooklyn, 1941
+02/12 The Beatles play Carnegie Hall in New York City, 1964
+02/17 Jazz great Thelonius Monk dies in Englewood, New Jersey, 1982
+02/18 Yoko Ono Lennon is born in Tokyo, 1933
+02/19 Paul McCartney's "Give Ireland Back to the Irish" is banned in
+ Britain, 1972
+02/19 William "Smokey" Robinson is born in Detroit, 1940
+02/20 J. Geils (J. Geils Band) is born, 1946
+02/20 Yes sells out Madison Square Garden...without advertising, 1974
+02/23 Handel born, 1685
+02/23 Johnny Winter is born in Leland, Mississippi, 1944
+02/29 Jimmy Dorsey born, 1904
+03/01 Jim Morrison is busted for obscenity in Miami, 1969
+03/02 Blues guitarist Rory Gallagher is born in Ballyshannon, Ireland, 1949
+03/03 Buffalo Springfield is formed in Los Angeles, 1966
+03/04 Antonio Vivaldi born in Venice, Italy, 1678
+03/07 Last Gilbert & Sullivan opera produced, 1896
+03/08 Ron "Pigpen" McKernan (Grateful Dead) dies in California, 1973
+03/09 Robin Trower is born in London, 1945
+03/13 The Allman Brothers record their live album at the Fillmore East, 1971
+03/15 Sly Stone born, 1944
+03/17 Paul Kantner (Jefferson Airplane) is born in San Francisco, 1942
+03/21 J.S. Bach born, 1685
+03/22 Ten Years After plays their last concert, 1974
+03/25 Aretha Franklin is born in Detroit, 1943
+03/26 Emerson, Lake, and Palmer record "Pictures at an Exhibition" live, 1971
+03/29 Dr. Hook gets a group picture on the cover of "Rolling Stone", 1973
+03/30 Eric Clapton is born in Surrey, England, 1945
+04/02 Marvin Gaye is born in Washington, D.C., 1939
+04/04 Muddy Waters (McKinley Morganfield) is born in Rolling Fork,
+ Mississippi, 1915
+04/09 Paul Robeson born, 1898
+04/10 Paul McCartney announces that he's quitting the Beatles, 1970
+04/14 Ritchie Blackmore (Deep Purple, Rainbow) is born, 1945
+04/18 Yes breaks up after 13 years, 1981
+04/25 Blues guitarist Albert King is born, 1925
+04/25 Ella Fitzgerald born, 1918
+04/26 Carol Burnett born in San Antonio, Texas, 1933
+04/29 "Hair" premiers on Broadway, 1968
+05/01 Kate Smith born, 1909
+05/03 Bob Seger is born in Ann Arbor, Michigan, 1945
+05/07 Johannes Brahms born in Hamburg, 1833
+05/07 Tchaikowsky born, 1840
+05/10 Dave Mason is born in Worcester, England, 1945
+05/11 Bob Marley dies in his sleep in Miami, 1981
+05/12 Pink Floyd performs the first quadrophonic concert, 1977
+05/18 Rick Wakeman is born in West London, England, 1949
+05/19 Pete Townshend is born in London, 1945
+05/20 The Jimi Hendrix Experience is signed by Reprise Records, 1967
+05/22 Johann Sebastian Bach born in Eisenach, Germany, 1665
+05/23 Blues great Elmore James dies, 1963
+05/24 Bob Dylan (Robert Zimmerman) is born in Duluth, 1941
+05/26 Al Jolson born, 1886
+05/31 The Who perform the loudest concert ever -- 76,000 watts of PA, 1976
+06/01 The Beatles release "Sgt. Pepper", 1967
+06/06 "Rock Around The Clock" makes Billboard's #1 slot, 1955
+06/07 Blind Faith debuts in concert at London's Hyde Park, 1969
+06/09 Les Paul (Lester Polfus) is born in Waukesha, Wisconsin, 1923
+06/10 Howlin' Wolf (Chester Burnett) is born in West Point, Mississippi, 1910
+06/10 Judy Garland born, 1922
+06/15 Harry Nilsson is born in Brooklyn, 1941
+06/16 The Monterey Pop festival opens, 1967
+06/18 Paul McCartney born in Liverpool, England, 1942
+06/21 Columbia records announces the first mass production of LP's, 1948
+06/22 Todd Rundgren is born in Upper Darby, Pennsylvania, 1948
+06/24 Jeff Beck is born in Surrey, England, 1944
+07/02 Felix Pappalardi and Leslie West form Mountain, 1969
+07/03 Jim Morrison dies in Paris, 1971
+07/06 The Jefferson Airplane is formed in San Francisco, 1965
+07/07 Ringo Starr (Richard Starkey) born in Liverpool, England, 1940
+07/12 Chicago DJ Steve Dahl holds "Disco Demolition" at Kamisky Park, 1979
+07/14 Woodie Guthrie born, 1912
+07/16 Cream forms in the U.K., 1966
+07/16 Harry Chapin dies on Long Island Expressway, 1981
+07/17 "Yellow Submarine" premieres at the London Pavilon, 1968
+07/20 Carlos Santana is born in Autlan, Mexico, 1947
+07/25 Bob Dylan goes electric at the Newport Folk Festival, 1965
+07/25 Crosby, Stills, Nash & Young debut at the Fillmore East, 1969
+07/26 Mick Jagger is born in Kent, England, 1943
+07/28 Bach dies, 1750
+07/28 The Watkins Glen "Summer Jam" opens, 1973
+08/01 The Concert for Bangla Desh takes place at Madison Square Garden, 1971
+08/04 John Lennon points out that "the Beatles are more popular than Jesus",
+ 1966
+08/10 Ian Anderson (Jethro Tull) is born in Edinburgh, Scotland, 1947
+08/13 Dan Fogelberg is born in Peoria, Illinois, 1951
+08/15 Beatles replace drummer Pete Best with Richard Starkey
+08/15 The Beatles play Shea Stadium in New York, 1965
+08/15 Woodstock Festival, Max Yasgur's farm, 1969
+08/26 Jimi Hendrix gives his last performance at the Isle of Wight, 1970
+08/26 Jimi Hendrix's Electric Ladyland Studios opens in New York, 1970
+09/07 Keith Moon (The Who) dies in London of a drug overdose, 1978
+09/08 Anton Dvorak born in Nelahozeves, Czechoslovakia, 1841
+09/08 Ron "Pigpen" McKernan (Grateful Dead) is born in San Bruno,
+ California, 1945
+09/14 Francis Scott Key writes words to "Star Spangled Banner", 1814
+09/16 B.B. King is born in Itta Bena, Mississippi, 1925
+09/19 Simon & Garfunkel reunite to play New York's Central Park, 1981
+09/20 Jim Croce dies in a plane crash, 1973
+09/23 "Paul is dead" rumors sweep the country, 1969
+09/23 Bruce "The Boss" Springsteen is born in Freehold, New Jersey, 1949
+09/25 John Bonham (Led Zeppelin) dies of alcohol poisoning, 1980
+09/26 George Gershwin born in Brooklyn, NY
+10/04 Janis Joplin O.D.s, 1970
+10/04 Janis Joplin dies of a heroin overdose in Hollywood, 1970
+10/05 Steve Miller is born in Dallas, 1943
+10/07 First Bandstand (later, American Bandstand) broadcast, 1957
+10/09 John Lennon born in Liverpool, England, 1940
+10/10 John Prine is born in Maywood, Illinois, 1946
+10/12 The Jimi Hendrix Experience is formed in London, 1966
+10/16 Bob Weir (Grateful Dead) is born in San Francisco, 1947
+10/17 "Hair" opens at New York's Public Theater, 1967
+10/18 Chuck Berry is born in San Jose, California, 1926
+10/20 Three members of Lynyrd Skynyrd die in a plane crash, 1977
+10/22 Franz Liszt born, 1811
+10/25 Jon Anderson (Yes) is born in Lancashire, England, 1944
+10/25 The Rolling Stones appear on The Ed Sullivan Show, 1964
+10/29 Duane Allman dies in motorcycle crash near Macon, Georgia, 1971
+10/30 Grace Slick is born in Chicago, 1939
+11/02 Jimi Hendrix's "Electric Ladyland" enters US charts at #1, 1968
+11/02 Keith Emerson is born, 1944
+11/03 James Taylor and Carly Simon are married in Manhattan, 1972
+11/07 Joni Mitchell (Roberta Joan Anderson) is born in Alberta, Canada, 1943
+11/08 Patti Page born, 1927
+11/09 The first issue of "Rolling Stone" is published, 1967
+11/10 Greg Lake is born in Bournemouth, England, 1948
+11/12 Neil Young is born in Toronto, 1945
+11/13 Paul Simon born, 1942
+11/16 Bill Ham first demonstrates his psychedelic "Light Show", 1965
+11/20 Duane Allman is born in Nashville, Tennessee, 1946
+11/20 Joe Walsh is born in Cleveland, 1947
+11/24 Scott Joplin born, 1868
+11/25 "The Last Waltz" concert is played by The Band at Winterland, 1976
+11/25 Johann Strauss, Jr., writes `On the Beautiful Blue Danube', 1867
+11/26 Cream performs their farewell concert at Royal Albert Hall, 1968
+11/27 Jimi Hendrix (Johnny Allen Hendrix) is born in Seattle, 1942
+12/05 Mozart dies, 1791
+12/06 First sound recording made by Thomas Edison, 1877
+12/06 The Rolling Stones play Altamont Speedway near San Francisco, 1969
+12/07 Harry Chapin is born in New York City, 1942
+12/08 Jim Morrison is born in Melbourne, Florida, 1943
+12/08 John Lennon is shot and killed in New York City, 1980
+12/09 The Who's "Tommy" premieres in London, 1973
+12/13 Ted Nugent, the motor city madman, born in Detroit, 1949
+12/15 Thomas Edison receives patent on the phonograph, 1877
+12/16 Beethoven born, 1770
+12/16 Don McLean's "American Pie" is released, 1971
+12/16 Ludwig von Beethoven christened in Bonn, Germany, 1770
+12/21 Frank Zappa is born in Baltimore, 1940
+12/23 First G&S collaboration, Thespis, 1871
+12/28 Edgar Winter is born in Beaumont, Texas, 1946
+12/31 Jimi Hendrix introduces the Band of Gypsies at the Fillmore East, 1969
diff --git a/usr.bin/calendar/calendars/calendar.usholiday b/usr.bin/calendar/calendars/calendar.usholiday
new file mode 100644
index 0000000..f799d0b
--- /dev/null
+++ b/usr.bin/calendar/calendars/calendar.usholiday
@@ -0,0 +1,31 @@
+01/01 New Year's Day
+01/14 Julian Calendar New Year's Day
+02/02 Groundhog Day
+02/14 St. Valentine's Day
+02/20* President's Day (3rd Monday of February)
+03/05 Mother-in-Law Day
+03/17 St. Patrick's Day
+03/20* Vernal Equinox
+04/01 April Fool's Day
+04/15 Income Tax Day
+04/03* Daylight Savings Time begins; clocks move forward (1st Sunday of April)
+04/28* Arbor Day (varies from state to state)
+05/14* Mother's Day (2nd Sunday of May)
+05/20* Armed Forces Day (3rd Saturday of May)
+05/29* Memorial Day (Last Monday of May)
+06/18* Father's Day (3rd Sunday of June)
+06/21* Summer Solstice
+07/04 Independence Day
+09/04* Labor Day (1st Monday of September)
+09/09* Grandparent's Day (2nd Sunday of September; varies from state to state)
+09/22* Autumnal Equinox
+10/09* Columbus Day (2nd Monday of October)
+10/29* Daylight Savings Time ends; clocks move back (Last Sunday in October)
+10/31 All Hallows Eve (Halloween)
+11/06* Election Day (1st Tuesday after 1st Monday for even years)
+11/11 Veterans' Day
+11/29 Thanksgiving Day (Last Thursday in November)
+12/21* Winter Solstice
+12/24 Christmas Eve
+12/25 Christmas
+12/31 New Year's Eve
diff --git a/usr.bin/calendar/pathnames.h b/usr.bin/calendar/pathnames.h
new file mode 100644
index 0000000..8a0838c
--- /dev/null
+++ b/usr.bin/calendar/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_CPP "/usr/bin/cpp"
+ /* XXX -- fix when cpp parses arguments rationally */
+#define _PATH_INCLUDE "-I/usr/share/calendar"
diff --git a/usr.bin/cap_mkdb/Makefile b/usr.bin/cap_mkdb/Makefile
new file mode 100644
index 0000000..0e403af
--- /dev/null
+++ b/usr.bin/cap_mkdb/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= cap_mkdb
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cap_mkdb/cap_mkdb.1 b/usr.bin/cap_mkdb/cap_mkdb.1
new file mode 100644
index 0000000..b2a3900
--- /dev/null
+++ b/usr.bin/cap_mkdb/cap_mkdb.1
@@ -0,0 +1,101 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cap_mkdb.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd "June 6, 1993"
+.Dt CAP_MKDB 1
+.Os
+.Sh NAME
+.Nm cap_mkdb
+.Nd create capability database
+.Pp
+.Sh SYNOPSIS
+.Nm cap_mkdb
+.Op Fl v
+.Op Fl f Ar outfile
+.Ar file1
+.Op Ar file2 ...
+.Pp
+.Sh DESCRIPTION
+.Nm Cap_mkdb
+builds a hashed database out of the
+.Xr getcap 3
+logical database constructed by the concatenation of the specified
+files .
+.Pp
+The database is named by the basename of the first file argument and
+the string
+.Dq .db .
+The
+.Xr getcap 3
+routines can access the database in this form much more quickly
+than they can the original text file(s).
+.Pp
+The ``tc'' capabilities of the records are expanded before the
+record is stored into the database.
+.Pp
+The options as as follows:
+.Bl -tag -width XXXXXX -indent
+.It Fl f Ar outfile
+Specify a different database basename.
+.It Fl v
+Print out the number of capability records in the database.
+.El
+.Pp
+.Sh FORMAT
+Each record is stored in the database using two different types of keys.
+.Pp
+The first type is a key which consists of the first capability of
+the record (not including the trailing colon (``:'')) with a data
+field consisting of a special byte followed by the rest of the record.
+The special byte is either a 0 or 1, where a 0 means that the record
+is okay, and a 1 means that there was a ``tc'' capability in the record
+that couldn't be expanded.
+.Pp
+The second type is a key which consists of one of the names from the
+first capability of the record with a data field consisting a special
+byte followed by the the first capability of the record.
+The special byte is a 2.
+.Pp
+In normal operation names are looked up in the database, resulting
+in a key/data pair of the second type.
+The data field of this key/data pair is used to look up a key/data
+pair of the first type which has the real data associated with the
+name.
+.Sh RETURN VALUE
+The
+.Nm cap_mkdb
+utility exits 0 on success and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr dbopen 3 ,
+.Xr getcap 3 ,
+.Xr termcap 5
diff --git a/usr.bin/cap_mkdb/cap_mkdb.c b/usr.bin/cap_mkdb/cap_mkdb.c
new file mode 100644
index 0000000..f809c86
--- /dev/null
+++ b/usr.bin/cap_mkdb/cap_mkdb.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cap_mkdb.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void db_build __P((char **));
+void dounlink __P((void));
+void usage __P((void));
+
+DB *capdbp;
+int verbose;
+char *capdb, *capname, buf[8 * 1024];
+
+/*
+ * Mkcapdb creates a capability hash database for quick retrieval of capability
+ * records. The database contains 2 types of entries: records and references
+ * marked by the first byte in the data. A record entry contains the actual
+ * capability record whereas a reference contains the name (key) under which
+ * the correct record is stored.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+
+ capname = NULL;
+ while ((c = getopt(argc, argv, "f:v")) != EOF) {
+ switch(c) {
+ case 'f':
+ capname = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ /*
+ * The database file is the first argument if no name is specified.
+ * Make arrangements to unlink it if exit badly.
+ */
+ (void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv);
+ if ((capname = strdup(buf)) == NULL)
+ err(1, "");
+ if ((capdbp = dbopen(capname,
+ O_CREAT | O_TRUNC | O_RDWR, DEFFILEMODE, DB_HASH, NULL)) == NULL)
+ err(1, "%s", buf);
+
+ if (atexit(dounlink))
+ err(1, "atexit");
+
+ db_build(argv);
+
+ if (capdbp->close(capdbp) < 0)
+ err(1, "%s", capname);
+ capname = NULL;
+ exit(0);
+}
+
+void
+dounlink()
+{
+ if (capname != NULL)
+ (void)unlink(capname);
+}
+
+/*
+ * Any changes to these definitions should be made also in the getcap(3)
+ * library routines.
+ */
+#define RECOK (char)0
+#define TCERR (char)1
+#define SHADOW (char)2
+
+/*
+ * Db_build() builds the name and capabilty databases according to the
+ * details above.
+ */
+void
+db_build(ifiles)
+ char **ifiles;
+{
+ DBT key, data;
+ recno_t reccnt;
+ size_t len, bplen;
+ int st;
+ char *bp, *p, *t;
+
+ data.data = NULL;
+ key.data = NULL;
+ for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0;) {
+
+ /*
+ * Allocate enough memory to store record, terminating
+ * NULL and one extra byte.
+ */
+ len = strlen(bp);
+ if (bplen <= len + 2) {
+ bplen += MAX(256, len + 2);
+ if ((data.data = realloc(data.data, bplen)) == NULL)
+ err(1, "");
+ }
+
+ /* Find the end of the name field. */
+ if ((p = strchr(bp, ':')) == NULL) {
+ warnx("no name field: %.*s", MIN(len, 20), bp);
+ continue;
+ }
+
+ /* First byte of stored record indicates status. */
+ switch(st) {
+ case 1:
+ ((char *)(data.data))[0] = RECOK;
+ break;
+ case 2:
+ ((char *)(data.data))[0] = TCERR;
+ warnx("Record not tc expanded: %.*s", p - bp, bp);
+ break;
+ }
+
+ /* Create the stored record. */
+ memmove(&((u_char *)(data.data))[1], bp, len + 1);
+ data.size = len + 2;
+
+ /* Store the record under the name field. */
+ key.data = bp;
+ key.size = p - bp;
+
+ switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) {
+ case -1:
+ err(1, "put");
+ /* NOTREACHED */
+ case 1:
+ warnx("ignored duplicate: %.*s",
+ key.size, (char *)key.data);
+ continue;
+ }
+ ++reccnt;
+
+ /* If only one name, ignore the rest. */
+ if ((p = strchr(bp, '|')) == NULL)
+ continue;
+
+ /* The rest of the names reference the entire name. */
+ ((char *)(data.data))[0] = SHADOW;
+ memmove(&((u_char *)(data.data))[1], key.data, key.size);
+ data.size = key.size + 1;
+
+ /* Store references for other names. */
+ for (p = t = bp;; ++p) {
+ if (p > t && (*p == ':' || *p == '|')) {
+ key.size = p - t;
+ key.data = t;
+ switch(capdbp->put(capdbp,
+ &key, &data, R_NOOVERWRITE)) {
+ case -1:
+ err(1, "put");
+ /* NOTREACHED */
+ case 1:
+ warnx("ignored duplicate: %.*s",
+ key.size, (char *)key.data);
+ }
+ t = p + 1;
+ }
+ if (*p == ':')
+ break;
+ }
+ }
+
+ switch(st) {
+ case -1:
+ err(1, "file argument");
+ /* NOTREACHED */
+ case -2:
+ errx(1, "potential reference loop detected");
+ /* NOTREACHED */
+ }
+
+ if (verbose)
+ (void)printf("cap_mkdb: %d capability records\n", reccnt);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: cap_mkdb [-v] [-f outfile] file1 [file2 ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/chat/Example b/usr.bin/chat/Example
new file mode 100644
index 0000000..8b7d0d1
--- /dev/null
+++ b/usr.bin/chat/Example
@@ -0,0 +1,5 @@
+#
+
+../pppd/pppd -d connect 'chat "" ATDT5551212 CONNECT "" ogin: ppp' netmask 255.255.255.0 /dev/cuaa0 38400
+
+../pppd/pppd connect 'chat "" AATDT5551212 CONNECT "" ogin: ppp' netmask 255.255.255.0 /dev/cuaa0 38400
diff --git a/usr.bin/chat/Makefile b/usr.bin/chat/Makefile
new file mode 100644
index 0000000..be27271
--- /dev/null
+++ b/usr.bin/chat/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile.bsd,v 1.2 1994/05/26 06:45:03 paulus Exp $
+
+PROG= chat
+SRCS= chat.c
+MAN8= chat.8
+BINDIR= /usr/bin
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/chat/README b/usr.bin/chat/README
new file mode 100644
index 0000000..73d28af
--- /dev/null
+++ b/usr.bin/chat/README
@@ -0,0 +1,169 @@
+I run PPP between crappie.morningstar.com (137.175.6.3, my home
+machine) and remora.morningstar.com (137.175.2.7, my workstation at
+the office). This document describes how I use it. The installation
+of PPP itself is covered in the PPP distribution.
+
+I put a line like this in remora's /etc/passwd:
+
+ Pkarl:2y4613BDaQD3x:51:10:Karl's PPP login:/tmp:/usr/local/etc/pppstart
+
+I created a login shell script on remora called
+/usr/local/etc/pppstart:
+
+ #!/bin/sh
+ /usr/bin/mesg n
+ stty -tostop
+ exec /usr/local/etc/ppp 137.175.2.7:
+
+I use the ppp-on command to bring up a connection, and ppp-off to shut
+it down. These shell scripts, plus the unlock and fix-cua scripts and
+the source to the chat program are included. You will need to heavily
+modify these to suit your own situation, including Internet addresses,
+machine names, telephone numbers, modem dialing commands, baud rates,
+login names and passwords. Make the "ppp..." command in the ppp-on
+script look something like this:
+
+ ppp 137.175.6.3: /dev/cua &
+
+The "137.175.6.3:" is of the format "local-addr:remote-addr" with the
+remote address null (it will be negotiated by PPP). Look at the login
+shell script above; it can be common to all dial-in PPP users on your
+machine because it only specifies the address of the remora
+(receiving) end of the link.
+
+If you use the enclosed chat and unlock programs, be sure they are
+suid uucp, and fix-cua should be suid root. The ppp-on script should
+be chmod 700, owner yourself, to keep the password (semi-) secure.
+
+I use the following eeprom settings and /dev and /etc/ttytab entries
+in order to support dial-in and dial-out on a single phone line:
+
+ crappie 12% eeprom | grep ttya
+ ttya-mode=19200,8,1,n,h
+ ttya-rts-dtr-off=false
+ ttya-ignore-cd=false
+ crappie 13% ls -lg /dev/cua /dev/ttya
+ crw-rw-rw- 1 root staff 12, 128 Nov 20 09:14 /dev/cua
+ crw--w--w- 1 root wheel 12, 0 Nov 20 08:25 /dev/ttya
+ crappie 14% grep ttya /etc/ttytab
+ ttya "/usr/etc/getty std.19200" unknown on
+ crappie 15%
+
+On SunOS 4.1 and later, make sure that the /etc/ttytab line for ttya
+doesn't say "local":
+
+ ttya "/usr/etc/getty std.38400" unknown on
+
+Make sure your modem passes data transparently; watch out especially
+for ^S, ^Q, ^P (UUCP spoofing) and parity problems. I have a Telebit
+Trailblazer+ attached to /dev/ttya with the following register
+settings:
+
+ aaatz
+ OK
+ aat&n
+ E1 F1 M1 Q6 P V1 X0 Version BA4.00
+ S00=001 S01=000 S02=043 S03=013 S04=010 S05=008 S06=002 S07=060 S08=002 S09=006
+ S10=007 S11=070 S12=050
+ S45=000 S47=004 S48=000 S49=000
+ S50=000 S51=005 S52=002 S53=003 S54=001 S55=000 S56=017 S57=019 S58=002 S59=000
+ S60=000 S61=000 S62=003 S63=001 S64=000 S65=000 S66=001 S67=000 S68=255
+ S90=000 S91=000 S92=001 S95=000
+ S100=000 S101=000 S102=000 S104=000
+ S110=001 S111=030 S112=001
+ S121=000
+ N0:
+ N1:
+ N2:
+ N3:
+ N4:
+ N5:
+ N6:
+ N7:
+ N8:
+ N9:
+ OK
+
+And, the following entry is in /etc/gettytab:
+
+ #
+ # 19200/2400 dialin for Telebit Trailblazer+ modem
+ #
+ T|T19200:dial-19200:\
+ :nx=T2400:sp#19200:
+ T2400|dial-2400:\
+ :nx=T19200:sp#2400:
+
+My chat script dialing command looks like "ATs50=255s111=0DT4515678"
+instead of just "ATDT4515678" in order to force a PEP mode connection
+and to disable the UUCP spoofing (otherwise, the modem swallows or
+delays ^P characters).
+
+I run /usr/etc/in.routed on crappie (the calling end) and have this in
+my /etc/gateways file:
+
+ net 0.0.0.0 gateway remora metric 1 passive
+ host crappie gateway crappie metric 0 passive
+
+Routed is started in /etc/rc.local. This way, I don't have to
+manually add or delete routes when links come up. I ifconfig the ppp0
+interface on crappie at boot time like this (in /etc/rc.local with the
+other ifconfig's):
+
+ ifconfig ppp0 crappie remora netmask 0xffffff00 down
+
+I put "init ppp_attach" in my /sys/sun4c/conf/CRAPPIE file so that the
+above ifconfig down will work:
+
+ pseudo-device ppp1 init ppp_attach # Point-to-Point Protocol, 1 line
+
+Routed now keeps my routes sane at the crappie.MorningStar.Com end.
+
+My ethernet (le0) and PPP (ppp0) interfaces are configured with the
+same address and netmask. IP is smart enough to figure out (via the
+routes in /etc/gateways) that everything useful needs to go out ppp0.
+Also, the remora end of my PPP link is configured the same way -- the
+ppp0 interface there is configured with the same address and netmask
+as remora's le0 ethernet. This means that separate interface names
+like "remora-ppp" are not needed; point-to-point links (whether PPP,
+Xerox Synchronous Point-to-Point Protocol, SLIP, IGP or whatever) have
+(apparently) been used this (seemingly bizarre) way for some time.
+This works because when IP looks at a POINTOPOINT link it ignores the
+local address (unlike an ethernet interface) and only looks at the
+remote address.
+
+Here's what netstat shows for me:
+
+ crappie 109% netstat -r
+ Routing tables
+ Destination Gateway Flags Refcnt Use Interface
+ localhost localhost UH 0 0 lo0
+ crappie crappie UH 1 11339 le0
+ default remora UG 0 1266 ppp0
+ mstar-net-ppp-remora crappie U 0 0 le0
+ crappie 110% netstat -rn
+ Routing tables
+ Destination Gateway Flags Refcnt Use Interface
+ 127.0.0.1 127.0.0.1 UH 0 0 lo0
+ 137.175.6.3 137.175.6.3 UH 1 11339 le0
+ default 137.175.2.7 UG 0 1266 ppp0
+ 137.175.6.0 137.175.6.3 U 0 0 le0
+ crappie 111%
+
+The default route to remora is a result of the first line in the
+/etc/gateways file ("default" can't be used there; you have to say
+"0.0.0.0").
+
+On the network at work, I add a static route in our gateway machine's
+/etc/rc.local file:
+
+ /usr/etc/route add net 137.175.6 remora 1
+
+All the other machines in the office have default routes pointing at
+the gateway machine, and all PPP-connected external machines are on the
+137.175.6 subnet.
+
+Send me mail or post to the newsgroup comp.protocols.ppp if you have
+any questions.
+
+Karl Fox <karl@MorningStar.Com>
diff --git a/usr.bin/chat/chat.8 b/usr.bin/chat/chat.8
new file mode 100644
index 0000000..e54f6b4
--- /dev/null
+++ b/usr.bin/chat/chat.8
@@ -0,0 +1,251 @@
+.\" -*- nroff -*-
+.\" manual page [] for chat 1.8
+.\" $Id: chat.8,v 1.1.1.1 1994/11/12 05:25:32 lars Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH CHAT 8 "17 April 1994" "Chat Version 1.8"
+.SH NAME
+chat \- Automated conversational script with a modem
+.SH SYNOPSIS
+.B chat
+[
+.I options
+]
+.I script
+.SH DESCRIPTION
+.LP
+The \fIchat\fR program defines a conversational exchange between the
+computer and the modem. Its primary purpose is to establish the
+connection between the Point-to-Point Protocol Daemon (\fIpppd\fR) and
+the remote's \fIpppd\fR process.
+.SH OPTIONS
+.TP
+.B -f \fI<chat file>
+Read the chat script from the chat \fIfile\fR. The use of this option
+is mutually exclusive with the chat script parameters. The user must
+have read access to the file. Multiple lines are permitted in the
+file. Space or horizontal tab characters should be used to separate
+the strings.
+.TP
+.B -l \fI<lock file>
+Perform the UUCP style locking using the indicated lock file.
+.IP
+If the file could not be created then the \fIchat\fR program will
+fail. The lock file will be deleted only if the \fIchat\fR program
+fails to perform the script for any reason. If the script is
+successful the lock file will be left on the disk. It is expected that
+the lock file will be deleted when the \fIpppd\fR process no longer
+wishes to use the serial device.
+.IP
+The use of a lock file with
+.I chat
+and
+\fIpppd\fR\'s
+.I lock
+option should not be used at the same time. They are mutually
+exclusive options and will cause one or the other program to fail to
+achieve the required lock if you use both.
+.TP
+.B -t \fI<timeout>
+Set the timeout for the expected string to be received. If the string
+is not received within the time limit then the reply string is not
+sent. An alternate reply may be sent or the script will fail if there
+is no alternate reply string. A failed script will cause the
+\fIchat\fR program to terminate with a non-zero error code.
+.TP
+.B -v
+Request that the \fIchat\fR script be executed in a verbose mode. The
+\fIchat\fR program will then log all text received from the modem and
+the output strings which it sends to the SYSLOG.
+.TP
+.B script
+If the script is not specified in a file with the \fI-f\fR option then
+the script is included as parameters to the \fIchat\fR program.
+.SH CHAT SCRIPT
+.LP
+The \fIchat\fR script defines the communications.
+.LP
+A script consists of one or more "expect-send" pairs of strings,
+separated by spaces, with an optional "subexpect-subsend" string pair,
+separated by a dash as in the following example:
+.IP
+ogin:-BREAK-ogin: ppp ssword: hello2u2
+.LP
+This line indicates that the \fIchat\fR program should expect the string
+"ogin:". If it fails to receive a login prompt within the time interval
+allotted, it is to send a break sequence to the remote and then expect the
+string "ogin:". If the first "ogin:" is received then the break sequence is
+not generated.
+.LP
+Once it received the login prompt the \fIchat\fR program will send the string ppp
+and then expect the prompt "ssword:". When it receives the prompt for the
+password, it will send the password hello2u2.
+.LP
+A carriage return is normally sent following the reply string. It is not
+expected in the "expect" string unless it is specifically requested by using
+the \\r character sequence.
+.LP
+The expect sequence should contain only what is needed to identify the
+string. Since it is normally stored on a disk file, it should not contain
+variable information. It is generally not acceptable to look for time
+strings, network identification strings, or other variable pieces of data as
+an expect string.
+.LP
+To help correct for characters which may be corrupted during the initial
+sequence, look for the string "ogin:" rather than "login:". It is possible
+that the leading "l" character may be received in error and you may never
+find the string even though it was sent by the system. For this reason,
+scripts look for "ogin:" rather than "login:" and "ssword:" rather than
+"password:".
+.LP
+A very simple script might look like this:
+.IP
+ogin: ppp ssword: hello2u2
+.LP
+In other words, expect ....ogin:, send ppp, expect ...ssword:, send hello2u2.
+.LP
+In actual practice, simple scripts are rare. At the vary least, you
+should include sub-expect sequences should the original string not be
+received. For example, consider the following script:
+.IP
+ogin:--ogin: ppp ssowrd: hello2u2
+.LP
+This would be a better script than the simple one used earlier. This would look
+for the same login: prompt, however, if one was not received, a single
+return sequence is sent and then it will look for login: again. Should line
+noise obscure the first login prompt then sending the empty line will
+usually generate a login prompt again.
+.SH ABORT STRINGS
+Many modems will report the status of the call as a string. These
+strings may be \fBCONNECTED\fR or \fBNO CARRIER\fR or \fBBUSY\fR. It
+is often desirable to terminate the script should the modem fail to
+connect to the remote. The difficulty is that a script would not know
+exactly which modem string it may receive. On one attempt, it may
+receive \fBBUSY\fR while the next time it may receive \fBNO CARRIER\fR.
+.LP
+These "abort" strings may be specified in the script using the \fIABORT\fR
+sequence. It is written in the script as in the following example:
+.IP
+ABORT BUSY ABORT 'NO CARRIER' '' ATZ OK ATDT5551212 CONNECT
+.LP
+This sequence will expect nothing; and then send the string ATZ. The
+expected response to this is the string \fIOK\fR. When it receives \fIOK\fR,
+the string ATDT5551212 to dial the telephone. The expected string is
+\fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the
+script is executed. However, should the modem find a busy telephone, it will
+send the string \fIBUSY\fR. This will cause the string to match the abort
+character sequence. The script will then fail because it found a match to
+the abort string. If it received the string \fINO CARRIER\fR, it will abort
+for the same reason. Either string may be received. Either string will
+terminate the \fIchat\fR script.
+.SH TIMEOUT
+The initial timeout value is 45 seconds. This may be changed using the \fB-t\fR
+parameter.
+.LP
+To change the timeout value for the next expect string, the following
+example may be used:
+.IP
+ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:--ogin: TIMEOUT 5 assowrd: hello2u2
+.LP
+This will change the timeout to 10 seconds when it expects the login:
+prompt. The timeout is then changed to 5 seconds when it looks for the
+password prompt.
+.LP
+The timeout, once changed, remains in effect until it is changed again.
+.SH SENDING EOT
+The special reply string of \fIEOT\fR indicates that the chat program
+should send an EOT character to the remote. This is normally the
+End-of-file character sequence. A return character is not sent
+following the EOT.
+.PR
+The EOT sequence may be embedded into the send string using the
+sequence \fI^D\fR.
+.SH GENERATING BREAK
+The special reply string of \fIBREAK\fR will cause a break condition
+to be sent. The break is a special signal on the transmitter. The
+normal processing on the receiver is to change the transmission rate.
+It may be used to cycle through the available transmission rates on
+the remote until you are able to receive a valid login prompt.
+.PR
+The break sequence may be embedded into the send string using the
+\fI\\K\fR sequence.
+.SH ESCAPE SEQUENCES
+The expect and reply strings may contain escape sequences. All of the
+sequences are legal in the reply string. Many are legal in the expect.
+Those which are not valid in the expect sequence are so indicated.
+.TP
+.B ''
+Expects or sends a null string. If you send a null string then it will still
+send the return character. This sequence may either be a pair of apostrophe
+or quote characters.
+.TP
+.B \\\\b
+represents a backspace character.
+.TP
+.B \\\\c
+Suppresses the newline at the end of the reply string. This is the only
+method to send a string without a trailing return character. It must
+be at the end of the send string. For example,
+the sequence hello\\c will simply send the characters h, e, l, l, o.
+.I (not valid in expect.)
+.TP
+.B \\\\d
+Delay for one second. The program uses sleep(1) which will delay to a
+maximum of one second.
+.I (not valid in expect.)
+.TP
+.B \\\\K
+Insert a BREAK
+.I (not valid in expect.)
+.TP
+.B \\\\n
+Send a newline or linefeed character.
+.TP
+.B \\\\N
+Send a null character. The same sequence may be represented by \\0.
+.I (not valid in expect.)
+.TP
+.B \\\\p
+Pause for a fraction of a second. The delay is 1/10th of a second.
+.I (not valid in expect.)
+.TP
+.B \\\\q
+Suppress writing the string to the SYSLOG file. The string ?????? is
+written to the log in its place.
+.I (not valid in expect.)
+.TP
+.B \\\\r
+Send or expect a carriage return.
+.TP
+.B \\\\s
+Represents a space character in the string. This may be used when it
+is not desirable to quote the strings which contains spaces. The
+sequence 'HI TIM' and HI\\sTIM are the same.
+.TP
+.B \\\\t
+Send or expect a tab character.
+.TP
+.B \\\\\\\\
+Send or expect a backslash character.
+.TP
+.B \\\\ddd
+Collapse the octal digits (ddd) into a single ASCII character and send that
+character.
+.I (some characters are not valid in expect.)
+.TP
+.B \^^C
+Substitute the sequence with the control character represented by C.
+For example, the character DC1 (17) is shown as \^^Q.
+.I (some characters are not valid in expect.)
+.SH SEE ALSO
+Additional information about \fIchat\fR scripts may be found with UUCP
+documentation. The \fIchat\fR script was taken from the ideas proposed by the
+scripts used by the \fIuucico\fR program.
+.LP
+uucico(8), uucp(1)
+.SH COPYRIGHT
+The \fIchat\fR program is in public domain. This is not the GNU public
+license. If it breaks then you get to keep both pieces.
diff --git a/usr.bin/chat/chat.c b/usr.bin/chat/chat.c
new file mode 100644
index 0000000..66387d6
--- /dev/null
+++ b/usr.bin/chat/chat.c
@@ -0,0 +1,1166 @@
+/*
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * This software is in the public domain.
+ *
+ * Please send all bug reports, requests for information, etc. to:
+ *
+ * Al Longyear (longyear@netcom.com)
+ * (I was the last person to change this code.)
+ *
+ * The original author is:
+ *
+ * Karl Fox <karl@MorningStar.Com>
+ * Morning Star Technologies, Inc.
+ * 1760 Zollinger Road
+ * Columbus, OH 43221
+ * (614)451-1883
+ */
+
+static char rcsid[] = "$Id: chat.c,v 1.2 1994/12/19 01:02:11 ache Exp $";
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#ifndef TERMIO
+#undef TERMIOS
+#define TERMIOS
+#endif
+
+#ifdef sun
+# if defined(SUNOS) && SUNOS >= 41
+# ifndef HDB
+# define HDB
+# endif
+# endif
+#endif
+
+#ifdef TERMIO
+#include <termio.h>
+#endif
+#ifdef TERMIOS
+#include <termios.h>
+#endif
+
+#define STR_LEN 1024
+
+#ifndef SIGTYPE
+#define SIGTYPE void
+#endif
+
+#ifdef __STDC__
+#undef __P
+#define __P(x) x
+#else
+#define __P(x) ()
+#define const
+#endif
+
+/*************** Micro getopt() *********************************************/
+#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
+ (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
+ &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
+#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
+ (_O=4,(char*)0):(char*)0)
+#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
+#define ARG(c,v) (c?(--c,*v++):(char*)0)
+
+static int _O = 0; /* Internal state */
+/*************** Micro getopt() *********************************************/
+
+char *program_name;
+
+#ifndef LOCK_DIR
+# if defined(__NetBSD__) || defined(__FreeBSD__)
+# define PIDSTRING
+# define LOCK_DIR "/var/spool/lock"
+# else
+# ifdef HDB
+# define PIDSTRING
+# define LOCK_DIR "/usr/spool/locks"
+# else /* HDB */
+# define LOCK_DIR "/usr/spool/uucp"
+# endif /* HDB */
+# endif
+#endif /* LOCK_DIR */
+
+#define MAX_ABORTS 50
+#define DEFAULT_CHAT_TIMEOUT 45
+
+int verbose = 0;
+int quiet = 0;
+char *lock_file = (char *)0;
+char *chat_file = (char *)0;
+int timeout = DEFAULT_CHAT_TIMEOUT;
+
+int have_tty_parameters = 0;
+#ifdef TERMIO
+struct termio saved_tty_parameters;
+#endif
+#ifdef TERMIOS
+struct termios saved_tty_parameters;
+#endif
+
+char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
+ fail_buffer[50];
+int n_aborts = 0, abort_next = 0, timeout_next = 0;
+
+void *dup_mem __P((void *b, size_t c));
+void *copy_of __P((char *s));
+void usage __P((void));
+void logf __P((const char *str));
+void logflush __P((void));
+void fatal __P((const char *msg));
+void sysfatal __P((const char *msg));
+SIGTYPE sigalrm __P((int signo));
+SIGTYPE sigint __P((int signo));
+SIGTYPE sigterm __P((int signo));
+SIGTYPE sighup __P((int signo));
+void unalarm __P((void));
+void init __P((void));
+void set_tty_parameters __P((void));
+void break_sequence __P((void));
+void terminate __P((int status));
+void do_file __P((char *chat_file));
+void lock __P((void));
+void delay __P((void));
+int get_string __P((register char *string));
+int put_string __P((register char *s));
+int write_char __P((int c));
+int put_char __P((char c));
+int get_char __P((void));
+void chat_send __P((register char *s));
+char *character __P((char c));
+void chat_expect __P((register char *s));
+char *clean __P((register char *s, int sending));
+void unlock __P((void));
+void lock __P((void));
+void break_sequence __P((void));
+void terminate __P((int status));
+void die __P((void));
+
+void *dup_mem(b, c)
+void *b;
+size_t c;
+ {
+ void *ans = malloc (c);
+ if (!ans)
+ fatal ("memory error!\n");
+ memcpy (ans, b, c);
+ return ans;
+ }
+
+void *copy_of (s)
+char *s;
+ {
+ return dup_mem (s, strlen (s) + 1);
+ }
+
+/*
+ * chat [ -v ] [ -t timeout ] [ -l lock-file ] [ -f chat-file ] \
+ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
+ *
+ * Perform a UUCP-dialer-like chat script on stdin and stdout.
+ */
+int
+main(argc, argv)
+int argc;
+char **argv;
+ {
+ int option;
+ char *arg;
+
+ program_name = *argv;
+
+ while (option = OPTION(argc, argv))
+ switch (option)
+ {
+ case 'v':
+ ++verbose;
+ break;
+
+ case 'f':
+ if (arg = OPTARG(argc, argv))
+ chat_file = copy_of(arg);
+ else
+ usage();
+
+ break;
+
+ case 'l':
+ if (arg = OPTARG(argc, argv))
+ lock_file = copy_of(arg);
+ else
+ usage();
+
+ break;
+
+ case 't':
+ if (arg = OPTARG(argc, argv))
+ timeout = atoi(arg);
+ else
+ usage();
+
+ break;
+
+ default:
+ usage();
+ }
+
+#ifdef ultrix
+ openlog("chat", LOG_PID);
+#else
+ openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
+
+ if (verbose) {
+ setlogmask(LOG_UPTO(LOG_INFO));
+ } else {
+ setlogmask(LOG_UPTO(LOG_WARNING));
+ }
+#endif
+
+ init();
+
+ if (chat_file != NULL)
+ {
+ arg = ARG(argc, argv);
+ if (arg != NULL)
+ usage();
+ else
+ do_file (chat_file);
+ }
+ else
+ {
+ while (arg = ARG(argc, argv))
+ {
+ chat_expect(arg);
+
+ if (arg = ARG(argc, argv))
+ chat_send(arg);
+ }
+ }
+
+ terminate(0);
+ }
+
+/*
+ * Process a chat script when read from a file.
+ */
+
+void do_file (chat_file)
+char *chat_file;
+ {
+ int linect, len, sendflg;
+ char *sp, *arg, quote;
+ char buf [STR_LEN];
+ FILE *cfp;
+
+ if ((cfp = fopen (chat_file, "r")) == NULL)
+ {
+ syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
+ terminate (1);
+ }
+
+ linect = 0;
+ sendflg = 0;
+
+ while (fgets(buf, STR_LEN, cfp) != NULL)
+ {
+ sp = strchr (buf, '\n');
+ if (sp)
+ *sp = '\0';
+
+ linect++;
+ sp = buf;
+ while (*sp != '\0')
+ {
+ if (*sp == ' ' || *sp == '\t')
+ {
+ ++sp;
+ continue;
+ }
+
+ if (*sp == '"' || *sp == '\'')
+ {
+ quote = *sp++;
+ arg = sp;
+ while (*sp != quote)
+ {
+ if (*sp == '\0')
+ {
+ syslog (LOG_ERR, "unterminated quote (line %d)",
+ linect);
+ terminate (1);
+ }
+
+ if (*sp++ == '\\')
+ if (*sp != '\0')
+ ++sp;
+ }
+ }
+ else
+ {
+ arg = sp;
+ while (*sp != '\0' && *sp != ' ' && *sp != '\t')
+ ++sp;
+ }
+
+ if (*sp != '\0')
+ *sp++ = '\0';
+
+ if (sendflg)
+ {
+ chat_send (arg);
+ }
+ else
+ {
+ chat_expect (arg);
+ }
+ sendflg = !sendflg;
+ }
+ }
+ fclose (cfp);
+ }
+
+/*
+ * We got an error parsing the command line.
+ */
+void usage()
+ {
+ fprintf(stderr, "\
+Usage: %s [-v] [-l lock-file] [-t timeout] {-f chat-file || chat-script}\n",
+ program_name);
+ exit(1);
+ }
+
+char line[256];
+char *p;
+
+void logf (str)
+const char *str;
+ {
+ p = line + strlen(line);
+ strcat (p, str);
+
+ if (str[strlen(str)-1] == '\n')
+ {
+ syslog (LOG_INFO, "%s", line);
+ line[0] = 0;
+ }
+ }
+
+void logflush()
+ {
+ if (line[0] != 0)
+ {
+ syslog(LOG_INFO, "%s", line);
+ line[0] = 0;
+ }
+ }
+
+/*
+ * Unlock and terminate with an error.
+ */
+void die()
+ {
+ unlock();
+ terminate(1);
+ }
+
+/*
+ * Print an error message and terminate.
+ */
+
+void fatal (msg)
+const char *msg;
+ {
+ syslog(LOG_ERR, "%s", msg);
+ unlock();
+ terminate(1);
+ }
+
+/*
+ * Print an error message along with the system error message and
+ * terminate.
+ */
+
+void sysfatal (msg)
+const char *msg;
+ {
+ syslog(LOG_ERR, "%s: %m", msg);
+ unlock();
+ terminate(1);
+ }
+
+int alarmed = 0;
+
+SIGTYPE sigalrm(signo)
+int signo;
+ {
+ int flags;
+
+ alarm(1);
+ alarmed = 1; /* Reset alarm to avoid race window */
+ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */
+
+ logflush();
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ sysfatal("Can't get file mode flags on stdin");
+ else
+ if (fcntl(0, F_SETFL, flags | FNDELAY) == -1)
+ sysfatal("Can't set file mode flags on stdin");
+
+ if (verbose)
+ {
+ syslog(LOG_INFO, "alarm");
+ }
+ }
+
+void unalarm()
+ {
+ int flags;
+
+ if ((flags = fcntl(0, F_GETFL, 0)) == -1)
+ sysfatal("Can't get file mode flags on stdin");
+ else
+ if (fcntl(0, F_SETFL, flags & ~FNDELAY) == -1)
+ sysfatal("Can't set file mode flags on stdin");
+ }
+
+SIGTYPE sigint(signo)
+int signo;
+ {
+ fatal("SIGINT");
+ }
+
+SIGTYPE sigterm(signo)
+int signo;
+ {
+ fatal("SIGTERM");
+ }
+
+SIGTYPE sighup(signo)
+int signo;
+ {
+ fatal("SIGHUP");
+ }
+
+void init()
+ {
+ signal(SIGINT, sigint);
+ signal(SIGTERM, sigterm);
+ signal(SIGHUP, sighup);
+
+ if (lock_file)
+ lock();
+
+ set_tty_parameters();
+ signal(SIGALRM, sigalrm);
+ alarm(0);
+ alarmed = 0;
+ }
+
+void set_tty_parameters()
+ {
+#ifdef TERMIO
+ struct termio t;
+
+ if (ioctl(0, TCGETA, &t) < 0)
+ sysfatal("Can't get terminal parameters");
+#endif
+#ifdef TERMIOS
+ struct termios t;
+
+ if (tcgetattr(0, &t) < 0)
+ sysfatal("Can't get terminal parameters");
+#endif
+
+ saved_tty_parameters = t;
+ have_tty_parameters = 1;
+
+ t.c_iflag |= IGNBRK | ISTRIP | IGNPAR;
+ t.c_oflag = 0;
+ t.c_lflag = 0;
+ t.c_cc[VERASE] = t.c_cc[VKILL] = 0;
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+
+#ifdef TERMIO
+ if (ioctl(0, TCSETA, &t) < 0)
+ sysfatal("Can't set terminal parameters");
+#endif
+#ifdef TERMIOS
+ if (tcsetattr(0, TCSANOW, &t) < 0)
+ sysfatal("Can't set terminal parameters");
+#endif
+ }
+
+void break_sequence()
+ {
+#ifdef TERMIOS
+ tcsendbreak (0, 0);
+#endif
+ }
+
+void terminate(status)
+int status;
+ {
+ if (have_tty_parameters &&
+#ifdef TERMIO
+ ioctl(0, TCSETA, &saved_tty_parameters) < 0
+#endif
+#ifdef TERMIOS
+ tcsetattr(0, TCSANOW, &saved_tty_parameters) < 0
+#endif
+ ) {
+ syslog(LOG_ERR, "Can't restore terminal parameters: %m");
+ unlock();
+ exit(1);
+ }
+ exit(status);
+ }
+
+/*
+ * Create a lock file for the named lock device
+ */
+void lock()
+ {
+ int fd, pid;
+# ifdef PIDSTRING
+ char hdb_lock_buffer[12];
+# endif
+
+ lock_file = strcat(strcat(strcpy(malloc(strlen(LOCK_DIR)
+ + 1 + strlen(lock_file) + 1),
+ LOCK_DIR), "/"), lock_file);
+
+ if ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0)
+ {
+ char *s = lock_file;
+ lock_file = (char *)0; /* Don't remove someone else's lock file! */
+ syslog(LOG_ERR, "Can't get lock file '%s': %m", s);
+ die();
+ }
+
+# ifdef PIDSTRING
+ sprintf(hdb_lock_buffer, "%10d\n", getpid());
+ write(fd, hdb_lock_buffer, 11);
+# else
+ pid = getpid();
+ write(fd, &pid, sizeof pid);
+# endif
+
+ close(fd);
+ }
+
+/*
+ * Remove our lockfile
+ */
+void unlock()
+ {
+ if (lock_file)
+ {
+ unlink(lock_file);
+ lock_file = (char *)0;
+ }
+ }
+
+/*
+ * 'Clean up' this string.
+ */
+char *clean(s, sending)
+register char *s;
+int sending;
+ {
+ char temp[STR_LEN], cur_chr;
+ register char *s1;
+ int add_return = sending;
+#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
+
+ s1 = temp;
+ while (*s)
+ {
+ cur_chr = *s++;
+ if (cur_chr == '^')
+ {
+ cur_chr = *s++;
+ if (cur_chr == '\0')
+ {
+ *s1++ = '^';
+ break;
+ }
+ cur_chr &= 0x1F;
+ if (cur_chr != 0)
+ *s1++ = cur_chr;
+ continue;
+ }
+
+ if (cur_chr != '\\')
+ {
+ *s1++ = cur_chr;
+ continue;
+ }
+
+ cur_chr = *s++;
+ if (cur_chr == '\0')
+ {
+ if (sending)
+ {
+ *s1++ = '\\';
+ *s1++ = '\\';
+ }
+ break;
+ }
+
+ switch (cur_chr)
+ {
+ case 'b':
+ *s1++ = '\b';
+ break;
+
+ case 'c':
+ if (sending && *s == '\0')
+ add_return = 0;
+ else
+ *s1++ = cur_chr;
+ break;
+
+ case '\\':
+ case 'K':
+ case 'p':
+ case 'd':
+ if (sending)
+ *s1++ = '\\';
+
+ *s1++ = cur_chr;
+ break;
+
+ case 'q':
+ quiet = ! quiet;
+ break;
+
+ case 'r':
+ *s1++ = '\r';
+ break;
+
+ case 'n':
+ *s1++ = '\n';
+ break;
+
+ case 's':
+ *s1++ = ' ';
+ break;
+
+ case 't':
+ *s1++ = '\t';
+ break;
+
+ case 'N':
+ if (sending)
+ {
+ *s1++ = '\\';
+ *s1++ = '\0';
+ }
+ else
+ *s1++ = 'N';
+ break;
+
+ default:
+ if (isoctal (cur_chr))
+ {
+ cur_chr &= 0x07;
+ if (isoctal (*s))
+ {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ if (isoctal (*s))
+ {
+ cur_chr <<= 3;
+ cur_chr |= *s++ - '0';
+ }
+ }
+
+ if (cur_chr != 0 || sending)
+ {
+ if (sending && (cur_chr == '\\' || cur_chr == 0))
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ }
+ break;
+ }
+
+ if (sending)
+ *s1++ = '\\';
+ *s1++ = cur_chr;
+ break;
+ }
+ }
+
+ if (add_return)
+ *s1++ = '\r';
+
+ *s1++ = '\0'; /* guarantee closure */
+ *s1++ = '\0'; /* terminate the string */
+ return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
+ }
+
+/*
+ * Process the expect string
+ */
+void chat_expect(s)
+register char *s;
+ {
+ if (strcmp(s, "ABORT") == 0)
+ {
+ ++abort_next;
+ return;
+ }
+
+ if (strcmp(s, "TIMEOUT") == 0)
+ {
+ ++timeout_next;
+ return;
+ }
+
+ while (*s)
+ {
+ register char *hyphen;
+
+ for (hyphen = s; *hyphen; ++hyphen)
+ if (*hyphen == '-')
+ if (hyphen == s || hyphen[-1] != '\\')
+ break;
+
+ if (*hyphen == '-')
+ {
+ *hyphen = '\0';
+
+ if (get_string(s))
+ return;
+ else
+ {
+ s = hyphen + 1;
+
+ for (hyphen = s; *hyphen; ++hyphen)
+ if (*hyphen == '-')
+ if (hyphen == s || hyphen[-1] != '\\')
+ break;
+
+ if (*hyphen == '-')
+ {
+ *hyphen = '\0';
+
+ chat_send(s);
+ s = hyphen + 1;
+ }
+ else
+ {
+ chat_send(s);
+ return;
+ }
+ }
+ }
+ else
+ if (get_string(s))
+ return;
+ else
+ {
+ if (fail_reason)
+ syslog(LOG_INFO, "Failed (%s)", fail_reason);
+ else
+ syslog(LOG_INFO, "Failed");
+
+ unlock();
+ terminate(1);
+ }
+ }
+ }
+
+char *character(c)
+char c;
+ {
+ static char string[10];
+ char *meta;
+
+ meta = (c & 0x80) ? "M-" : "";
+ c &= 0x7F;
+
+ if (c < 32)
+ sprintf(string, "%s^%c", meta, (int)c + '@');
+ else
+ if (c == 127)
+ sprintf(string, "%s^?", meta);
+ else
+ sprintf(string, "%s%c", meta, c);
+
+ return (string);
+ }
+
+/*
+ * process the reply string
+ */
+void chat_send (s)
+register char *s;
+ {
+ if (abort_next)
+ {
+ char *s1;
+
+ abort_next = 0;
+
+ if (n_aborts >= MAX_ABORTS)
+ fatal("Too many ABORT strings");
+
+ s1 = clean(s, 0);
+
+ if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
+ {
+ syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
+ die();
+ }
+
+ abort_string[n_aborts++] = s1;
+
+ if (verbose)
+ {
+ logf("abort on (");
+
+ for (s1 = s; *s1; ++s1)
+ logf(character(*s1));
+
+ logf(")\n");
+ }
+ }
+ else
+ if (timeout_next)
+ {
+ timeout_next = 0;
+ timeout = atoi(s);
+
+ if (timeout <= 0)
+ timeout = DEFAULT_CHAT_TIMEOUT;
+
+ if (verbose)
+ {
+ syslog(LOG_INFO, "timeout set to %d seconds", timeout);
+ }
+ }
+ else
+ {
+ if (strcmp(s, "EOT") == 0)
+ s = "^D\\c";
+ else
+ if (strcmp(s, "BREAK") == 0)
+ s = "\\K\\c";
+ if ( ! put_string(s))
+ {
+ syslog(LOG_INFO, "Failed");
+ unlock();
+ terminate(1);
+ }
+ }
+ }
+
+int get_char()
+ {
+ int status;
+ char c;
+
+ status = read(0, &c, 1);
+
+ switch (status)
+ {
+ case 1:
+ return ((int)c & 0x7F);
+
+ default:
+ syslog(LOG_WARNING, "warning: read() on stdin returned %d",
+ status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ sysfatal("Can't get file mode flags on stdin");
+ else
+ if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
+ sysfatal("Can't set file mode flags on stdin");
+
+ return (-1);
+ }
+ }
+
+int put_char(c)
+char c;
+ {
+ int status;
+
+ delay();
+
+ status = write(1, &c, 1);
+
+ switch (status)
+ {
+ case 1:
+ return (0);
+
+ default:
+ syslog(LOG_WARNING, "warning: write() on stdout returned %d",
+ status);
+
+ case -1:
+ if ((status = fcntl(0, F_GETFL, 0)) == -1)
+ sysfatal("Can't get file mode flags on stdin");
+ else
+ if (fcntl(0, F_SETFL, status & ~FNDELAY) == -1)
+ sysfatal("Can't set file mode flags on stdin");
+
+ return (-1);
+ }
+ }
+
+int write_char (c)
+int c;
+ {
+ if (alarmed || put_char(c) < 0)
+ {
+ extern int errno;
+
+ alarm(0); alarmed = 0;
+
+ if (verbose)
+ {
+ if (errno == EINTR || errno == EWOULDBLOCK)
+ syslog(LOG_INFO, " -- write timed out");
+ else
+ syslog(LOG_INFO, " -- write failed: %m");
+ }
+ return (0);
+ }
+ return (1);
+ }
+
+int put_string (s)
+register char *s;
+ {
+ s = clean(s, 1);
+
+ if (verbose)
+ {
+ logf("send (");
+
+ if (quiet)
+ logf("??????");
+ else
+ {
+ register char *s1 = s;
+
+ for (s1 = s; *s1; ++s1)
+ logf(character(*s1));
+ }
+
+ logf(")\n");
+ }
+
+ alarm(timeout); alarmed = 0;
+
+ while (*s)
+ {
+ register char c = *s++;
+
+ if (c != '\\')
+ {
+ if (!write_char (c))
+ return 0;
+ continue;
+ }
+
+ c = *s++;
+ switch (c)
+ {
+ case 'd':
+ sleep(1);
+ break;
+
+ case 'K':
+ break_sequence();
+ break;
+
+ case 'p':
+ usleep(10000); /* 1/100th of a second. */
+ break;
+
+ default:
+ if (!write_char (c))
+ return 0;
+ break;
+ }
+ }
+
+ alarm(0);
+ alarmed = 0;
+ return (1);
+ }
+
+/*
+ * 'Wait for' this string to appear on this file descriptor.
+ */
+int get_string(string)
+register char *string;
+ {
+ char temp[STR_LEN];
+ int c, printed = 0, len, minlen;
+ register char *s = temp, *end = s + STR_LEN;
+
+ fail_reason = (char *)0;
+ string = clean(string, 0);
+ len = strlen(string);
+ minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
+
+ if (verbose)
+ {
+ register char *s1;
+
+ logf("expect (");
+
+ for (s1 = string; *s1; ++s1)
+ logf(character(*s1));
+
+ logf(")\n");
+ }
+
+ if (len > STR_LEN)
+ {
+ syslog(LOG_INFO, "expect string is too long");
+ return 0;
+ }
+
+ if (len == 0)
+ {
+ if (verbose)
+ {
+ syslog(LOG_INFO, "got it");
+ }
+
+ return (1);
+ }
+
+ alarm(timeout); alarmed = 0;
+
+ while ( ! alarmed && (c = get_char()) >= 0)
+ {
+ int n, abort_len;
+
+ if (verbose)
+ {
+ if (c == '\n')
+ logf("\n");
+ else
+ logf(character(c));
+ }
+
+ *s++ = c;
+
+ if (s - temp >= len &&
+ c == string[len - 1] &&
+ strncmp(s - len, string, len) == 0)
+ {
+ if (verbose)
+ {
+ logf(" -- got it\n");
+ }
+
+ alarm(0); alarmed = 0;
+ return (1);
+ }
+
+ for (n = 0; n < n_aborts; ++n)
+ if (s - temp >= (abort_len = strlen(abort_string[n])) &&
+ strncmp(s - abort_len, abort_string[n], abort_len) == 0)
+ {
+ if (verbose)
+ {
+ logf(" -- failed\n");
+ }
+
+ alarm(0); alarmed = 0;
+ strcpy(fail_reason = fail_buffer, abort_string[n]);
+ return (0);
+ }
+
+ if (s >= end)
+ {
+ strncpy(temp, s - minlen, minlen);
+ s = temp + minlen;
+ }
+
+ if (alarmed && verbose)
+ syslog(LOG_WARNING, "warning: alarm synchronization problem");
+ }
+
+ alarm(0);
+
+ if (verbose && printed)
+ {
+ if (alarmed)
+ logf(" -- read timed out\n");
+ else
+ {
+ logflush();
+ syslog(LOG_INFO, " -- read failed: %m");
+ }
+ }
+
+ alarmed = 0;
+ return (0);
+ }
+
+#ifdef ultrix
+#undef NO_USLEEP
+#include <sys/types.h>
+#include <sys/time.h>
+
+/*
+ usleep -- support routine for 4.2BSD system call emulations
+ last edit: 29-Oct-1984 D A Gwyn
+ */
+
+extern int select();
+
+int
+usleep( usec ) /* returns 0 if ok, else -1 */
+ long usec; /* delay in microseconds */
+{
+ static struct /* `timeval' */
+ {
+ long tv_sec; /* seconds */
+ long tv_usec; /* microsecs */
+ } delay; /* _select() timeout */
+
+ delay.tv_sec = usec / 1000000L;
+ delay.tv_usec = usec % 1000000L;
+
+ return select( 0, (long *)0, (long *)0, (long *)0, &delay );
+}
+#endif
+
+/*
+ * Delay an amount appropriate for between typed characters.
+ */
+void delay()
+ {
+# ifdef NO_USLEEP
+ register int i;
+
+ for (i = 0; i < 30000; ++i) /* ... did we just say appropriate? */
+ ;
+# else /* NO_USLEEP */
+ usleep(100);
+# endif /* NO_USLEEP */
+ }
diff --git a/usr.bin/chat/connect-ppp b/usr.bin/chat/connect-ppp
new file mode 100755
index 0000000..d37067d
--- /dev/null
+++ b/usr.bin/chat/connect-ppp
@@ -0,0 +1,129 @@
+#!/bin/sh
+#
+# USAGE: connect-ppp <host>
+#
+# Set up a PPP link to host.
+#
+# This script locks the tty so that faxd and uucp will not
+# interfere. If you are running with faxd as you "getty" then
+# faxd will remove the lock once it notices that pppd is gone.
+# This is the reason for pppd running in with the -detach flag,
+# and you probably would run this script in the background.
+#
+# I had to create the nodropdtr option to pppd in order to be
+# able to do what the script is doing here. Pathces has been
+# sent to the respective people, but I don't know if they like
+# them :-).
+#
+# Look for comments with <LOCAL> in the string. They identify
+# things that you want to set for your system
+
+#<LOCAL> define whatever your config file is.
+CON_DB=/etc/ppp-connections
+
+#<LOCAL> define whatever your device is.
+DEVICE=cuaa0
+
+#<LOCAL> define whatever your device speed is.
+DEVICESPEED=57600
+
+#<LOCAL> define whatever your lock directory is.
+LOCKDIR=/var/spool/lock
+LOCKFILE=$LOCKDIR/LCK..$DEVICE
+
+#<LOCAL> define whatever debug level you want.
+DEBUG="-d -d -d -d"
+
+# Check that we got a name to connect to. This need not be an actuall hostname
+# just the name you specified in the config file.
+if [ $# -ne 1 ] ; then
+ echo "Usage: $0 <host> &"
+ exit 1
+fi
+
+# Get the configuration that is in effect for <name>
+LINE=`grep "^$1" $CON_DB`
+if [ -z "$LINE" ] ; then
+ echo "Unknow host $1"
+ exit 1
+fi
+
+# parse the CON_DB. The format is:
+#
+# <hostname>:<phone number>:<user id>:<password>:<local ip address>:\
+# <remove_ip_address><netmask>:<pppd options>
+#
+# The last three are optional. But I would recomend specifying a netmask also
+# when you specify a ip address.
+
+IP_ADDR=""
+IFS=':'
+set $LINE
+IFS=' '
+HOST=$1
+PHONE=$2
+USER=$3
+PASSWORD=$4
+OUR_IP_ADDR=$5
+THEIR_IP_ADDR=$6
+NETMASK=$7
+shift 7
+OPTIONS=$*
+
+if [ -f $LOCKFILE ] ; then
+ echo "PPP device is locked"
+ exit 1
+else
+
+ # Lock the device
+ # faxd and UUCP wants 10 character lock id.
+ echo "$$" | awk '{printf("%10s",$0)}' > $LOCKFILE
+fi
+
+
+
+
+#Do we know our local ip address? If so pppd needs a : at the end of it.
+if [ ! -z "$OUR_IP_ADDR" ] ; then
+ IP_ADDR=${OUR_IP_ADDR}:${THEIR_IP_ADDR}
+fi
+
+#Did we specify a netmask? If so convert to pppd format.
+if [ ! -z "$NETMASK" ] ; then
+ NETMASK="netmask ${NETMASK}"
+fi
+
+# Do the actual work in a subshell so that we can turn off tostop and set
+# the tty speed before chat dials. The second reason for doing in like
+# is that if you aren't running BIDIR, and you are running faxd, clocal
+# doesn't get turned on from pppd so chat will never work if you exec
+# it from within pppd. I found that I needed to run uucp with the
+# HAVE_CLOCAL_BUG flag set to 1 in order to get it to work in conjunction
+# with faxd. Anyway, this setup seem to work.
+(
+
+ stty $DEVICESPEED -tostop hupcl 2> /dev/null
+
+ # <LOCAL> Modify the Modem initialization strings to be whatever works for you
+ if chat -v ABORT "NO CARRIER" ABORT BUSY "" ATZ0E1 OK ATS50=255DT$PHONE \
+ CONNECT "" ogin: $USER ssword: \\q$PASSWORD
+ then
+ # We got connected.
+ /usr/libexec/pppd $DEBUG $OPTIONS -detach modem defaultroute \
+ crtscts $NETMASK $DEVICE $DEVICESPEED $IP_ADDR
+
+ else
+ echo "PPP call failed" 1>&2
+ exit 1
+ fi
+) < /dev/$DEVICE > /dev/$DEVICE
+# Get the return code from the subshell.
+RC=$?
+
+# Clear the lock. Slight window here where someone could detect that
+# pppd is no longer running, remove its lock file and create its own.
+# How to fix??
+rm -f $LOCKFILE
+
+#Pass on the exit code.
+exit $RC
diff --git a/usr.bin/chat/fix-cua b/usr.bin/chat/fix-cua
new file mode 100644
index 0000000..74f000a
--- /dev/null
+++ b/usr.bin/chat/fix-cua
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+LOCKDIR=/var/spool/lock
+
+case "$1" in
+ "") echo "Usage: fix-cua device"; exit 1 ;;
+esac
+
+if [ -f $LOCKDIR/LCK..$1 ]
+then
+ echo "/dev/$1 is locked" 2>&1
+ exit 1
+fi
+
+chown root /dev/$1
+chmod 666 /dev/$1
diff --git a/usr.bin/chat/ppp-off b/usr.bin/chat/ppp-off
new file mode 100755
index 0000000..22b46f8
--- /dev/null
+++ b/usr.bin/chat/ppp-off
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+kill -INT `ps -ax | egrep " ppp " | egrep -v "egrep" | sed 's/^\([ 0-9]*\) .*/\1'/`
+
+exit 0
diff --git a/usr.bin/chat/ppp-on b/usr.bin/chat/ppp-on
new file mode 100755
index 0000000..d150cfb
--- /dev/null
+++ b/usr.bin/chat/ppp-on
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+#
+# ppp-on
+#
+# Set up a PPP link
+#
+
+LOCKDIR=/var/spool/lock
+DEVICE=cuaa0
+
+PHONE=4511234
+USER=Pkarl
+PASSWORD=password
+OUR_IP_ADDR=137.175.6.3
+
+if [ -f $LOCKDIR/LCK..$DEVICE ]
+then
+ echo "PPP device is locked"
+ exit 1
+fi
+
+(
+ stty 19200 -tostop
+
+ if chat -l LCK..$DEVICE ABORT "NO CARRIER" ABORT BUSY "" ATZ OK ATs50=255s111=0DT$PHONE CONNECT "" ogin: $USER ssword: \\q$PASSWORD
+ then
+ ppp mru 1500 $OUR_IP_ADDR: /dev/$DEVICE &
+ sleep 10
+ exit 0
+ else
+ echo "PPP call failed" 1>&2
+ exit 1
+ fi
+) < /dev/$DEVICE > /dev/$DEVICE
diff --git a/usr.bin/chat/unlock b/usr.bin/chat/unlock
new file mode 100755
index 0000000..978bc6e
--- /dev/null
+++ b/usr.bin/chat/unlock
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+LOCKDIR=/var/spool/lock
+
+case "$1" in
+ "") echo "Usage: unlock lockfile"; exit 1 ;;
+ .*) echo "Usage: unlock lockfile"; exit 1 ;;
+esac
+
+if [ -f $LOCKDIR/$1 ]
+then
+ if [ `wc -c < $LOCKDIR/$1` -eq 4 ]
+ then
+ rm -f $LOCKDIR/$1
+ exit 0
+ else
+ echo "Usage: unlock lockfile"
+ exit 1
+ fi
+else
+ echo "lockfile" $LOCKDIR/$1 "does not exist"
+ exit 1
+fi
diff --git a/usr.bin/checknr/Makefile b/usr.bin/checknr/Makefile
new file mode 100644
index 0000000..c265248
--- /dev/null
+++ b/usr.bin/checknr/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= checknr
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/checknr/checknr.1 b/usr.bin/checknr/checknr.1
new file mode 100644
index 0000000..1a49c5f
--- /dev/null
+++ b/usr.bin/checknr/checknr.1
@@ -0,0 +1,159 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)checknr.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CHECKNR 1
+.Os BSD 4
+.Sh NAME
+.Nm checknr
+.Nd check nroff/troff files
+.Sh SYNOPSIS
+.Nm checknr
+.Op Fl a Ns Ar \&.x1.y1.x2.y2. ... \&.xn.yn
+.Op Fl c Ns Ar \&.x1.x2.x3 ... \&.xn
+.Op Fl s
+.Op Fl f
+.Ar file
+.Sh DESCRIPTION
+.Nm Checknr
+checks a list of
+.Xr nroff 1
+or
+.Xr troff 1
+input files for certain kinds of errors
+involving mismatched opening and closing delimiters
+and unknown commands.
+If no files are specified,
+.Nm checknr
+checks the standard input.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl a
+Add additional pairs of macros to the list of known macros.
+This must be followed by groups of six characters, each group defining
+a pair of macros.
+The six characters are
+a period,
+the first macro name,
+another period,
+and the second macro name.
+For example, to define a pair .BS and .ES, use
+.Sq Li \-a.BS.ES
+.It Fl c
+Define commands which would otherwise be complained about
+as undefined.
+.It Fl f
+Request
+.Nm checknr
+to ignore
+.Ql \ef
+font changes.
+.It Fl s
+Ignore
+.Ql \es
+size changes.
+.El
+.Pp
+Delimiters checked are:
+.Bl -enum
+.It
+Font changes using \efx ... \efP.
+.It
+Size changes using \esx ... \es0.
+.It
+Macros that come in open ... close forms, for example,
+the .TS and .TE macros which must always come in pairs.
+.El
+.Pp
+.Nm Checknr
+is intended for use on documents that are prepared with
+.Nm checknr
+in mind, much the same as
+.Xr lint 1 .
+It expects a certain document writing style for
+.Ql \ef
+and
+.Ql \es
+commands,
+in that each
+.Ql \efx
+must be terminated with
+.Ql \efP
+and
+each
+.Ql \esx
+must be terminated with
+.Ql \es0 .
+While it will work to directly go into the next font or explicitly
+specify the original font or point size,
+and many existing documents actually do this,
+such a practice will produce complaints from
+.Nm checknr .
+Since it is probably better to use the
+.Ql \efP
+and
+.Ql \es0
+forms anyway,
+you should think of this as a contribution to your document
+preparation style.
+.Pp
+.Nm Checknr
+knows about the
+.Xr ms 7
+and
+.Xr me 7
+macro packages.
+.Sh SEE ALSO
+.Xr nroff 1 ,
+.Xr troff 1 ,
+.Xr checkeq 1 ,
+.Xr ms 7 ,
+.Xr me 7
+.Sh DIAGNOSTICS
+.Bd -ragged -compact
+Complaints about unmatched delimiters.
+Complaints about unrecognized commands.
+Various complaints about the syntax of commands.
+.Ed
+.Sh BUGS
+There is no way to define a 1 character macro name using
+.Fl a .
+.Pp
+Does not correctly recognize certain reasonable constructs,
+such as conditionals.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/checknr/checknr.c b/usr.bin/checknr/checknr.c
new file mode 100644
index 0000000..336eb67
--- /dev/null
+++ b/usr.bin/checknr/checknr.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)checknr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * checknr: check an nroff/troff input file for matching macro calls.
+ * we also attempt to match size and font changes, but only the embedded
+ * kind. These must end in \s0 and \fP resp. Maybe more sophistication
+ * later but for now think of these restrictions as contributions to
+ * structured typesetting.
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#define MAXSTK 100 /* Stack size */
+#define MAXBR 100 /* Max number of bracket pairs known */
+#define MAXCMDS 500 /* Max number of commands known */
+
+/*
+ * The stack on which we remember what we've seen so far.
+ */
+struct stkstr {
+ int opno; /* number of opening bracket */
+ int pl; /* '+', '-', ' ' for \s, 1 for \f, 0 for .ft */
+ int parm; /* parm to size, font, etc */
+ int lno; /* line number the thing came in in */
+} stk[MAXSTK];
+int stktop;
+
+/*
+ * The kinds of opening and closing brackets.
+ */
+struct brstr {
+ char *opbr;
+ char *clbr;
+} br[MAXBR] = {
+ /* A few bare bones troff commands */
+#define SZ 0
+ "sz", "sz", /* also \s */
+#define FT 1
+ "ft", "ft", /* also \f */
+ /* the -mm package */
+ "AL", "LE",
+ "AS", "AE",
+ "BL", "LE",
+ "BS", "BE",
+ "DF", "DE",
+ "DL", "LE",
+ "DS", "DE",
+ "FS", "FE",
+ "ML", "LE",
+ "NS", "NE",
+ "RL", "LE",
+ "VL", "LE",
+ /* the -ms package */
+ "AB", "AE",
+ "BD", "DE",
+ "CD", "DE",
+ "DS", "DE",
+ "FS", "FE",
+ "ID", "DE",
+ "KF", "KE",
+ "KS", "KE",
+ "LD", "DE",
+ "LG", "NL",
+ "QS", "QE",
+ "RS", "RE",
+ "SM", "NL",
+ "XA", "XE",
+ "XS", "XE",
+ /* The -me package */
+ "(b", ")b",
+ "(c", ")c",
+ "(d", ")d",
+ "(f", ")f",
+ "(l", ")l",
+ "(q", ")q",
+ "(x", ")x",
+ "(z", ")z",
+ /* Things needed by preprocessors */
+ "EQ", "EN",
+ "TS", "TE",
+ /* Refer */
+ "[", "]",
+ 0, 0
+};
+
+/*
+ * All commands known to nroff, plus macro packages.
+ * Used so we can complain about unrecognized commands.
+ */
+char *knowncmds[MAXCMDS] = {
+"$c", "$f", "$h", "$p", "$s", "(b", "(c", "(d", "(f", "(l", "(q", "(t",
+"(x", "(z", ")b", ")c", ")d", ")f", ")l", ")q", ")t", ")x", ")z", "++",
+"+c", "1C", "1c", "2C", "2c", "@(", "@)", "@C", "@D", "@F", "@I", "@M",
+"@c", "@e", "@f", "@h", "@m", "@n", "@o", "@p", "@r", "@t", "@z", "AB",
+"AE", "AF", "AI", "AL", "AM", "AS", "AT", "AU", "AX", "B", "B1", "B2",
+"BD", "BE", "BG", "BL", "BS", "BT", "BX", "C1", "C2", "CD", "CM", "CT",
+"D", "DA", "DE", "DF", "DL", "DS", "DT", "EC", "EF", "EG", "EH", "EM",
+"EN", "EQ", "EX", "FA", "FD", "FE", "FG", "FJ", "FK", "FL", "FN", "FO",
+"FQ", "FS", "FV", "FX", "H", "HC", "HD", "HM", "HO", "HU", "I", "ID",
+"IE", "IH", "IM", "IP", "IX", "IZ", "KD", "KE", "KF", "KQ", "KS", "LB",
+"LC", "LD", "LE", "LG", "LI", "LP", "MC", "ME", "MF", "MH", "ML", "MR",
+"MT", "ND", "NE", "NH", "NL", "NP", "NS", "OF", "OH", "OK", "OP", "P",
+"P1", "PF", "PH", "PP", "PT", "PX", "PY", "QE", "QP", "QS", "R", "RA",
+"RC", "RE", "RL", "RP", "RQ", "RS", "RT", "S", "S0", "S2", "S3", "SA",
+"SG", "SH", "SK", "SM", "SP", "SY", "T&", "TA", "TB", "TC", "TD", "TE",
+"TH", "TL", "TM", "TP", "TQ", "TR", "TS", "TX", "UL", "US", "UX", "VL",
+"WC", "WH", "XA", "XD", "XE", "XF", "XK", "XP", "XS", "[", "[-", "[0",
+"[1", "[2", "[3", "[4", "[5", "[<", "[>", "[]", "]", "]-", "]<", "]>",
+"][", "ab", "ac", "ad", "af", "am", "ar", "as", "b", "ba", "bc", "bd",
+"bi", "bl", "bp", "br", "bx", "c.", "c2", "cc", "ce", "cf", "ch", "cs",
+"ct", "cu", "da", "de", "di", "dl", "dn", "ds", "dt", "dw", "dy", "ec",
+"ef", "eh", "el", "em", "eo", "ep", "ev", "ex", "fc", "fi", "fl", "fo",
+"fp", "ft", "fz", "hc", "he", "hl", "hp", "ht", "hw", "hx", "hy", "i",
+"ie", "if", "ig", "in", "ip", "it", "ix", "lc", "lg", "li", "ll", "ln",
+"lo", "lp", "ls", "lt", "m1", "m2", "m3", "m4", "mc", "mk", "mo", "n1",
+"n2", "na", "ne", "nf", "nh", "nl", "nm", "nn", "np", "nr", "ns", "nx",
+"of", "oh", "os", "pa", "pc", "pi", "pl", "pm", "pn", "po", "pp", "ps",
+"q", "r", "rb", "rd", "re", "rm", "rn", "ro", "rr", "rs", "rt", "sb",
+"sc", "sh", "sk", "so", "sp", "ss", "st", "sv", "sz", "ta", "tc", "th",
+"ti", "tl", "tm", "tp", "tr", "u", "uf", "uh", "ul", "vs", "wh", "xp",
+"yr", 0
+};
+
+int lineno; /* current line number in input file */
+char line[256]; /* the current line */
+char *cfilename; /* name of current file */
+int nfiles; /* number of files to process */
+int fflag; /* -f: ignore \f */
+int sflag; /* -s: ignore \s */
+int ncmds; /* size of knowncmds */
+int slot; /* slot in knowncmds found by binsrch */
+
+char *malloc();
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ FILE *f;
+ int i;
+ char *cp;
+ char b1[4];
+
+ /* Figure out how many known commands there are */
+ while (knowncmds[ncmds])
+ ncmds++;
+ while (argc > 1 && argv[1][0] == '-') {
+ switch(argv[1][1]) {
+
+ /* -a: add pairs of macros */
+ case 'a':
+ i = strlen(argv[1]) - 2;
+ if (i % 6 != 0)
+ usage();
+ /* look for empty macro slots */
+ for (i=0; br[i].opbr; i++)
+ ;
+ for (cp=argv[1]+3; cp[-1]; cp += 6) {
+ br[i].opbr = malloc(3);
+ strncpy(br[i].opbr, cp, 2);
+ br[i].clbr = malloc(3);
+ strncpy(br[i].clbr, cp+3, 2);
+ addmac(br[i].opbr); /* knows pairs are also known cmds */
+ addmac(br[i].clbr);
+ i++;
+ }
+ break;
+
+ /* -c: add known commands */
+ case 'c':
+ i = strlen(argv[1]) - 2;
+ if (i % 3 != 0)
+ usage();
+ for (cp=argv[1]+3; cp[-1]; cp += 3) {
+ if (cp[2] && cp[2] != '.')
+ usage();
+ strncpy(b1, cp, 2);
+ b1[2] = '\0';
+ addmac(b1);
+ }
+ break;
+
+ /* -f: ignore font changes */
+ case 'f':
+ fflag = 1;
+ break;
+
+ /* -s: ignore size changes */
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc--; argv++;
+ }
+
+ nfiles = argc - 1;
+
+ if (nfiles > 0) {
+ for (i=1; i<argc; i++) {
+ cfilename = argv[i];
+ f = fopen(cfilename, "r");
+ if (f == NULL)
+ perror(cfilename);
+ else
+ process(f);
+ }
+ } else {
+ cfilename = "stdin";
+ process(stdin);
+ }
+ exit(0);
+}
+
+usage()
+{
+ printf("Usage: checknr -s -f -a.xx.yy.xx.yy... -c.xx.xx.xx...\n");
+ exit(1);
+}
+
+process(f)
+FILE *f;
+{
+ register int i, n;
+ char mac[5]; /* The current macro or nroff command */
+ int pl;
+
+ stktop = -1;
+ for (lineno = 1; fgets(line, sizeof line, f); lineno++) {
+ if (line[0] == '.') {
+ /*
+ * find and isolate the macro/command name.
+ */
+ strncpy(mac, line+1, 4);
+ if (isspace(mac[0])) {
+ pe(lineno);
+ printf("Empty command\n");
+ } else if (isspace(mac[1])) {
+ mac[1] = 0;
+ } else if (isspace(mac[2])) {
+ mac[2] = 0;
+ } else if (mac[0] != '\\' || mac[1] != '\"') {
+ pe(lineno);
+ printf("Command too long\n");
+ }
+
+ /*
+ * Is it a known command?
+ */
+ checkknown(mac);
+
+ /*
+ * Should we add it?
+ */
+ if (eq(mac, "de"))
+ addcmd(line);
+
+ chkcmd(line, mac);
+ }
+
+ /*
+ * At this point we process the line looking
+ * for \s and \f.
+ */
+ for (i=0; line[i]; i++)
+ if (line[i]=='\\' && (i==0 || line[i-1]!='\\')) {
+ if (!sflag && line[++i]=='s') {
+ pl = line[++i];
+ if (isdigit(pl)) {
+ n = pl - '0';
+ pl = ' ';
+ } else
+ n = 0;
+ while (isdigit(line[++i]))
+ n = 10 * n + line[i] - '0';
+ i--;
+ if (n == 0) {
+ if (stk[stktop].opno == SZ) {
+ stktop--;
+ } else {
+ pe(lineno);
+ printf("unmatched \\s0\n");
+ }
+ } else {
+ stk[++stktop].opno = SZ;
+ stk[stktop].pl = pl;
+ stk[stktop].parm = n;
+ stk[stktop].lno = lineno;
+ }
+ } else if (!fflag && line[i]=='f') {
+ n = line[++i];
+ if (n == 'P') {
+ if (stk[stktop].opno == FT) {
+ stktop--;
+ } else {
+ pe(lineno);
+ printf("unmatched \\fP\n");
+ }
+ } else {
+ stk[++stktop].opno = FT;
+ stk[stktop].pl = 1;
+ stk[stktop].parm = n;
+ stk[stktop].lno = lineno;
+ }
+ }
+ }
+ }
+ /*
+ * We've hit the end and look at all this stuff that hasn't been
+ * matched yet! Complain, complain.
+ */
+ for (i=stktop; i>=0; i--) {
+ complain(i);
+ }
+}
+
+complain(i)
+{
+ pe(stk[i].lno);
+ printf("Unmatched ");
+ prop(i);
+ printf("\n");
+}
+
+prop(i)
+{
+ if (stk[i].pl == 0)
+ printf(".%s", br[stk[i].opno].opbr);
+ else switch(stk[i].opno) {
+ case SZ:
+ printf("\\s%c%d", stk[i].pl, stk[i].parm);
+ break;
+ case FT:
+ printf("\\f%c", stk[i].parm);
+ break;
+ default:
+ printf("Bug: stk[%d].opno = %d = .%s, .%s",
+ i, stk[i].opno, br[stk[i].opno].opbr, br[stk[i].opno].clbr);
+ }
+}
+
+chkcmd(line, mac)
+char *line;
+char *mac;
+{
+ register int i, n;
+
+ /*
+ * Check to see if it matches top of stack.
+ */
+ if (stktop >= 0 && eq(mac, br[stk[stktop].opno].clbr))
+ stktop--; /* OK. Pop & forget */
+ else {
+ /* No. Maybe it's an opener */
+ for (i=0; br[i].opbr; i++) {
+ if (eq(mac, br[i].opbr)) {
+ /* Found. Push it. */
+ stktop++;
+ stk[stktop].opno = i;
+ stk[stktop].pl = 0;
+ stk[stktop].parm = 0;
+ stk[stktop].lno = lineno;
+ break;
+ }
+ /*
+ * Maybe it's an unmatched closer.
+ * NOTE: this depends on the fact
+ * that none of the closers can be
+ * openers too.
+ */
+ if (eq(mac, br[i].clbr)) {
+ nomatch(mac);
+ break;
+ }
+ }
+ }
+}
+
+nomatch(mac)
+char *mac;
+{
+ register int i, j;
+
+ /*
+ * Look for a match further down on stack
+ * If we find one, it suggests that the stuff in
+ * between is supposed to match itself.
+ */
+ for (j=stktop; j>=0; j--)
+ if (eq(mac,br[stk[j].opno].clbr)) {
+ /* Found. Make a good diagnostic. */
+ if (j == stktop-2) {
+ /*
+ * Check for special case \fx..\fR and don't
+ * complain.
+ */
+ if (stk[j+1].opno==FT && stk[j+1].parm!='R'
+ && stk[j+2].opno==FT && stk[j+2].parm=='R') {
+ stktop = j -1;
+ return;
+ }
+ /*
+ * We have two unmatched frobs. Chances are
+ * they were intended to match, so we mention
+ * them together.
+ */
+ pe(stk[j+1].lno);
+ prop(j+1);
+ printf(" does not match %d: ", stk[j+2].lno);
+ prop(j+2);
+ printf("\n");
+ } else for (i=j+1; i <= stktop; i++) {
+ complain(i);
+ }
+ stktop = j-1;
+ return;
+ }
+ /* Didn't find one. Throw this away. */
+ pe(lineno);
+ printf("Unmatched .%s\n", mac);
+}
+
+/* eq: are two strings equal? */
+eq(s1, s2)
+char *s1, *s2;
+{
+ return (strcmp(s1, s2) == 0);
+}
+
+/* print the first part of an error message, given the line number */
+pe(lineno)
+int lineno;
+{
+ if (nfiles > 1)
+ printf("%s: ", cfilename);
+ printf("%d: ", lineno);
+}
+
+checkknown(mac)
+char *mac;
+{
+
+ if (eq(mac, "."))
+ return;
+ if (binsrch(mac) >= 0)
+ return;
+ if (mac[0] == '\\' && mac[1] == '"') /* comments */
+ return;
+
+ pe(lineno);
+ printf("Unknown command: .%s\n", mac);
+}
+
+/*
+ * We have a .de xx line in "line". Add xx to the list of known commands.
+ */
+addcmd(line)
+char *line;
+{
+ char *mac;
+
+ /* grab the macro being defined */
+ mac = line+4;
+ while (isspace(*mac))
+ mac++;
+ if (*mac == 0) {
+ pe(lineno);
+ printf("illegal define: %s\n", line);
+ return;
+ }
+ mac[2] = 0;
+ if (isspace(mac[1]) || mac[1] == '\\')
+ mac[1] = 0;
+ if (ncmds >= MAXCMDS) {
+ printf("Only %d known commands allowed\n", MAXCMDS);
+ exit(1);
+ }
+ addmac(mac);
+}
+
+/*
+ * Add mac to the list. We should really have some kind of tree
+ * structure here but this is a quick-and-dirty job and I just don't
+ * have time to mess with it. (I wonder if this will come back to haunt
+ * me someday?) Anyway, I claim that .de is fairly rare in user
+ * nroff programs, and the register loop below is pretty fast.
+ */
+addmac(mac)
+char *mac;
+{
+ register char **src, **dest, **loc;
+
+ if (binsrch(mac) >= 0){ /* it's OK to redefine something */
+#ifdef DEBUG
+ printf("binsrch(%s) -> already in table\n", mac);
+#endif DEBUG
+ return;
+ }
+ /* binsrch sets slot as a side effect */
+#ifdef DEBUG
+printf("binsrch(%s) -> %d\n", mac, slot);
+#endif
+ loc = &knowncmds[slot];
+ src = &knowncmds[ncmds-1];
+ dest = src+1;
+ while (dest > loc)
+ *dest-- = *src--;
+ *loc = malloc(3);
+ strcpy(*loc, mac);
+ ncmds++;
+#ifdef DEBUG
+printf("after: %s %s %s %s %s, %d cmds\n", knowncmds[slot-2], knowncmds[slot-1], knowncmds[slot], knowncmds[slot+1], knowncmds[slot+2], ncmds);
+#endif
+}
+
+/*
+ * Do a binary search in knowncmds for mac.
+ * If found, return the index. If not, return -1.
+ */
+binsrch(mac)
+char *mac;
+{
+ register char *p; /* pointer to current cmd in list */
+ register int d; /* difference if any */
+ register int mid; /* mid point in binary search */
+ register int top, bot; /* boundaries of bin search, inclusive */
+
+ top = ncmds-1;
+ bot = 0;
+ while (top >= bot) {
+ mid = (top+bot)/2;
+ p = knowncmds[mid];
+ d = p[0] - mac[0];
+ if (d == 0)
+ d = p[1] - mac[1];
+ if (d == 0)
+ return mid;
+ if (d < 0)
+ bot = mid + 1;
+ else
+ top = mid - 1;
+ }
+ slot = bot; /* place it would have gone */
+ return -1;
+}
diff --git a/usr.bin/chflags/Makefile b/usr.bin/chflags/Makefile
new file mode 100644
index 0000000..875dc98
--- /dev/null
+++ b/usr.bin/chflags/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+NOSHARED= true
+
+PROG= chflags
+SRCS= chflags.c stat_flags.c
+.PATH: ${.CURDIR}/../../bin/ls
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/chflags/chflags.1 b/usr.bin/chflags/chflags.1
new file mode 100644
index 0000000..a240c4f
--- /dev/null
+++ b/usr.bin/chflags/chflags.1
@@ -0,0 +1,125 @@
+.\" Copyright (c) 1989, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chflags.1 8.2 (Berkeley) 3/31/94
+.\"
+.Dd March 31, 1994
+.Dt CHFLAGS 1
+.Os
+.Sh NAME
+.Nm chflags
+.Nd change file flags
+.Sh SYNOPSIS
+.Nm chflags
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar flags
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm chflags
+utility modifies the file flags of the listed files
+as specified by the
+.Ar flags
+operand.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+.It Fl R
+Change the file flags for the file hierarchies rooted
+in the files instead of just the files themselves.
+.El
+.Pp
+Flags are a comma separated list of keywords.
+The following keywords are currently defined:
+.Bd -literal -offset indent compact
+arch set the archived flag (super-user only)
+dump set the dump flag
+sappnd set the system append-only flag (super-user only)
+schg set the system immutable flag (super-user only)
+uappnd set the user append-only flag (owner or super-user only)
+uchg set the user immutable flag (owner or super-user only)
+archived, sappend, schange, simmutable, uappend, uchange, uimmutable
+ aliases for the above
+.Ed
+.Pp
+Putting the letters
+.Dq no
+before an option causes the flag to be turned off.
+For example:
+.Bd -literal -offset indent compact
+nodump the file should never be dumped
+.Ed
+.Pp
+Symbolic links do not have flags, so unless the
+.Fl H
+or
+.Fl L
+option is set,
+.Nm chflags
+on a symbolic link always succeeds and has no effect.
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Nm chflags
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr chflags 2 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
diff --git a/usr.bin/chflags/chflags.c b/usr.bin/chflags/chflags.c
new file mode 100644
index 0000000..7cca3eb
--- /dev/null
+++ b/usr.bin/chflags/chflags.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+u_long string_to_flags __P((char **, u_long *, u_long *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *ftsp;
+ FTSENT *p;
+ u_long clear, set;
+ long val;
+ int Hflag, Lflag, Pflag, Rflag, ch, fts_options, oct, rval;
+ char *flags, *ep;
+
+ Hflag = Lflag = Pflag = Rflag = 0;
+ while ((ch = getopt(argc, argv, "HLPR")) != EOF)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ fts_options = FTS_PHYSICAL;
+ if (Rflag) {
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ }
+
+ flags = *argv;
+ if (*flags >= '0' && *flags <= '7') {
+ errno = 0;
+ val = strtol(flags, &ep, 8);
+ if (val < 0)
+ errno = ERANGE;
+ if (errno)
+ err(1, "invalid flags: %s", flags);
+ if (*ep)
+ errx(1, "invalid flags: %s", flags);
+ set = val;
+ oct = 1;
+ } else {
+ if (string_to_flags(&flags, &set, &clear))
+ errx(1, "invalid flag: %s", flags);
+ clear = ~clear;
+ oct = 0;
+ }
+
+ if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (Rflag) /* Change it at FTS_DP. */
+ continue;
+ fts_set(ftsp, p, FTS_SKIP);
+ break;
+ case FTS_DNR: /* Warn, chflag, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL: /* Ignore. */
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ continue;
+ default:
+ break;
+ }
+ if (oct) {
+ if (!chflags(p->fts_accpath, set))
+ continue;
+ } else {
+ p->fts_statp->st_flags |= set;
+ p->fts_statp->st_flags &= clear;
+ if (!chflags(p->fts_accpath, p->fts_statp->st_flags))
+ continue;
+ }
+ warn("%s", p->fts_path);
+ rval = 1;
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: chflags [-R [-H | -L | -P]] flags file ...\n");
+ exit(1);
+}
diff --git a/usr.bin/chpass/Makefile b/usr.bin/chpass/Makefile
new file mode 100644
index 0000000..a1e2947
--- /dev/null
+++ b/usr.bin/chpass/Makefile
@@ -0,0 +1,27 @@
+# @(#)Makefile 8.2 (Berkeley) 4/2/94
+
+PROG= chpass
+SRCS= chpass.c edit.c field.c pw_copy.c pw_scan.c pw_util.c table.c util.c
+BINOWN= root
+BINMODE=4555
+.PATH: ${.CURDIR}/../../usr.sbin/pwd_mkdb ${.CURDIR}/../../usr.sbin/vipw
+CFLAGS+=-I${.CURDIR}/../../usr.sbin/pwd_mkdb -I${.CURDIR}/../../usr.sbin/vipw
+LINKS= ${BINDIR}/chpass ${BINDIR}/chfn
+LINKS+= ${BINDIR}/chpass ${BINDIR}/chsh
+LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchpass
+LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchfn
+LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchsh
+MLINKS= chpass.1 chfn.1 chpass.1 chsh.1
+COPTS+= -DYP
+SRCS+= pw_yp.c
+DPADD= ${LIBRPCSVC} ${LIBCRYPT}
+LDADD+= -lrpcsvc -lcrypt
+
+beforeinstall:
+ [ ! -e ${DESTDIR}${BINDIR}/chpass ] || \
+ chflags noschg ${DESTDIR}${BINDIR}/chpass
+
+afterinstall:
+ chflags schg ${DESTDIR}${BINDIR}/chpass
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1
new file mode 100644
index 0000000..9e47a4d
--- /dev/null
+++ b/usr.bin/chpass/chpass.1
@@ -0,0 +1,336 @@
+.\" Copyright (c) 1988, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chpass.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt CHPASS 1
+.Os
+.Sh NAME
+.Nm chpass, chfn, chsh, ypchpass, ypchfn, ypchsh
+.Nd add or change user database information
+.Sh SYNOPSIS
+chpass
+.Op Fl a Ar list
+.Op Fl p Ar encpass
+.Op Fl s Ar newshell
+.Op user
+.Sh DESCRIPTION
+The
+.Nm chpass
+program
+allows editing of the user database information associated
+with
+.Ar user
+or, by default, the current user.
+The information is formatted and supplied to an editor for changes.
+.Pp
+Only the information that the user is allowed to change is displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl a
+The super-user is allowed to directly supply a user database
+entry, in the format specified by
+.Xr passwd 5 ,
+as an argument.
+This argument must be a colon (``:'') separated list of all the
+user database fields, although they may be empty.
+.It Fl p
+The super-user is allowed to directly supply an encrypted password field,
+in the format used by
+.Xr crypt 3 ,
+as an argument.
+.It Fl s
+The
+.Fl s
+option attempts to change the user's shell to
+.Ar newshell .
+.El
+.Pp
+Possible display items are as follows:
+.Pp
+.Bl -tag -width "Home Directory:" -compact -offset indent
+.It Login:
+user's login name
+.It Password:
+user's encrypted password
+.It Uid:
+user's login
+.It Gid:
+user's login group
+.It Change:
+password change time
+.It Expire:
+account expiration time
+.It Class:
+user's general classification
+.It Home Directory:
+user's home directory
+.It Shell:
+user's login shell
+.It Full Name:
+user's real name
+.It Location:
+user's normal location
+.It Home Phone:
+user's home phone
+.It Office Phone:
+user's office phone
+.El
+.Pp
+The
+.Ar login
+field is the user name used to access the computer account.
+.Pp
+The
+.Ar password
+field contains the encrypted form of the user's password.
+.Pp
+The
+.Ar uid
+field is the number associated with the
+.Ar login
+field.
+Both of these fields should be unique across the system (and often
+across a group of systems) as they control file access.
+.Pp
+While it is possible to have multiple entries with identical login names
+and/or identical user id's, it is usually a mistake to do so. Routines
+that manipulate these files will often return only one of the multiple
+entries, and that one by random selection.
+.Pp
+The
+.Ar group
+field is the group that the user will be placed in at login.
+Since BSD supports multiple groups (see
+.Xr groups 1 )
+this field currently has little special meaning.
+This field may be filled in with either a number or a group name (see
+.Xr group 5 ) .
+.Pp
+The
+.Ar change
+field is the date by which the password must be changed.
+.Pp
+The
+.Ar expire
+field is the date on which the account expires.
+.Pp
+Both the
+.Ar change
+and
+.Ar expire
+fields should be entered in the form ``month day year'' where
+.Ar month
+is the month name (the first three characters are sufficient),
+.Ar day
+is the day of the month, and
+.Ar year
+is the year.
+.Pp
+The
+.Ar class
+field is currently unused. In the near future it will be a key to
+a
+.Xr termcap 5
+style database of user attributes.
+.Pp
+The user's
+.Ar home directory
+is the full UNIX path name where the user
+will be placed at login.
+.Pp
+The
+.Ar shell
+field is the command interpreter the user prefers.
+If the
+.Ar shell
+field is empty, the Bourne shell,
+.Pa /bin/sh ,
+is assumed.
+When altering a login shell, and not the super-user, the user
+may not change from a non-standard shell or to a non-standard
+shell.
+Non-standard is defined as a shell not found in
+.Pa /etc/shells .
+.Pp
+The last four fields are for storing the user's
+.Ar full name , office location ,
+and
+.Ar home
+and
+.Ar work telephone
+numbers.
+.Pp
+Once the information has been verified,
+.Nm chpass
+uses
+.Xr pwd_mkdb 8
+to update the user database.
+.Sh ENVIRONMENT
+The
+.Xr vi 1
+editor will be used unless the environment variable EDITOR is set to
+an alternate editor.
+When the editor terminates, the information is re-read and used to
+update the user database itself.
+Only the user, or the super-user, may edit the information associated
+with the user.
+.Sh NIS INTERACTION
+.Nm Chpass
+can also be used in conjunction with NIS, however some restrictions
+apply.
+Currently,
+.Nm chpass
+can only make changes to the NIS passwd maps through
+.Xr yppasswdd 8 ,
+which only permits changes to a user's password, shell and gecos
+fields. It can not be used to change other user information or to
+add new records to the NIS passwd maps. (Doing that would require
+something such as ypupdated, which is not yet supported.)
+Furthermore,
+.Xr yppasswdd 8
+requires password authentication before it will make any
+changes, even if it receives a request from the super-user.
+.Pp
+As a result, the following restrictions apply when
+.Nm chpass
+is used with NIS:
+.Bl -enum -offset indent
+.It
+.Pa Only the shell and gecos information may be changed.
+All other
+fields are restricted, even when
+.Nm chpass
+is invoked by the super-user, because the
+.Xr yppasswdd 8
+daemon has no support for updating them. While support for
+changing other fields could be added, this would lead to
+compatibility problems with other NIS-capable systems.
+Even though the super-user may supply data for other fields
+while editing an entry, the extra information (other than the
+password -- see below) will be silently discarded.
+.It
+.Pa Password authentication is required.
+.Nm Chpass
+will prompt for the user's NIS password before effecting
+any changes. If the password is invalid, all changes will be
+discarded.
+.It
+.Pa Adding new records to the local
+.Pa password database is discouraged.
+.Nm Chpass
+will allow the administrator to add new records to the
+local password database while NIS is enabled, but this can lead to
+some confusion since the new records are appended to the end of
+the master password file, usually after the special NIS '+' entries.
+The administrator should use
+.Xr vipw 8
+to modify the local password
+file when NIS is running.
+.It
+.Pa Password changes are not permitted.
+Users should use
+.Xr passwd 1
+or
+.Xr yppasswd 1
+to change their NIS passwords. The super-user is allowed to specify
+a new password (even though the ``Password:'' field does not show
+up in the editor template, the super-user may add it back by hand),
+but even the super-user must supply the user's original password
+otherwise
+.Xr yppasswdd 8
+will refuse to update the NIS maps.
+.El
+.Pp
+There are also two extra option flags that are available when
+.Nm chpass
+is compiled with NIS support:
+.Bl -tag -width flag
+.It Fl l
+The
+.Fl l
+flag forces
+.Nm chpass
+to modify the local copy of a user's password
+information in the even that a user exists in both
+the local and NIS databases.
+.It Fl y
+This flag has the opposite effect of
+.Fl l .
+This flag is largely redundant since
+.Nm chpass
+operates on NIS entries by default if NIS is enabled.
+.El
+.Pp
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/master.passwd
+The user database
+.It Pa /etc/passwd
+A Version 7 format password file
+.It Pa /etc/chpass.XXXXXX
+Temporary copy of the password file
+.It Pa /etc/shells
+The list of approved shells
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr getusershell 3 ,
+.Xr passwd 5 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+.Rs
+.%A Robert Morris
+and
+.%A Ken Thompson
+.%T "UNIX Password security"
+.Re
+.Sh NOTES
+The
+.Xr chfn 1 ,
+.Xr chsh 1 ,
+.Xr ypchpass 1 ,
+.Xr ypchfn 1
+and
+.Xr upchsh 1
+commands are really only links to
+.Nm chpass .
+.Sh BUGS
+User information should (and eventually will) be stored elsewhere.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 Reno .
diff --git a/usr.bin/chpass/chpass.c b/usr.bin/chpass/chpass.c
new file mode 100644
index 0000000..ea4a558
--- /dev/null
+++ b/usr.bin/chpass/chpass.c
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "From: @(#)chpass.c 8.4 (Berkeley) 4/2/94";
+static char rcsid[] =
+ "$Id: chpass.c,v 1.4 1995/08/13 16:12:24 wpaul Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pw_scan.h>
+#include <pw_util.h>
+#include "pw_copy.h"
+#ifdef YP
+#include "pw_yp.h"
+#endif
+
+#include "chpass.h"
+#include "pathnames.h"
+
+char *progname = "chpass";
+char *tempname;
+uid_t uid;
+
+void baduser __P((void));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW } op;
+ struct passwd *pw, lpw;
+ int ch, pfd, tfd;
+ char *arg;
+#ifdef YP
+ int force_local = 0;
+ int force_yp = 0;
+#endif
+
+ op = EDITENTRY;
+#ifdef YP
+ while ((ch = getopt(argc, argv, "a:p:s:ly")) != EOF)
+#else
+ while ((ch = getopt(argc, argv, "a:p:s:")) != EOF)
+#endif
+ switch(ch) {
+ case 'a':
+ op = LOADENTRY;
+ arg = optarg;
+ break;
+ case 's':
+ op = NEWSH;
+ arg = optarg;
+ break;
+ case 'p':
+ op = NEWPW;
+ arg = optarg;
+ break;
+#ifdef YP
+ case 'l':
+ force_local = 1;
+ break;
+ case 'y':
+ force_yp = 1;
+ break;
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ uid = getuid();
+
+ if (op == EDITENTRY || op == NEWSH || op == NEWPW)
+ switch(argc) {
+ case 0:
+ if (!(pw = getpwuid(uid)))
+ errx(1, "unknown user: uid %u", uid);
+ break;
+ case 1:
+ if (!(pw = getpwnam(*argv)))
+ errx(1, "unknown user: %s", *argv);
+ if (uid && uid != pw->pw_uid)
+ baduser();
+ break;
+ default:
+ usage();
+ }
+ if (op == NEWSH) {
+ /* protect p_shell -- it thinks NULL is /bin/sh */
+ if (!arg[0])
+ usage();
+ if (p_shell(arg, pw, (ENTRY *)NULL))
+ pw_error((char *)NULL, 0, 1);
+ }
+
+ if (op == LOADENTRY) {
+ if (uid)
+ baduser();
+ pw = &lpw;
+ if (!pw_scan(arg, pw))
+ exit(1);
+ }
+
+ if (op == NEWPW) {
+ if (uid)
+ baduser();
+
+ if(strchr(arg, ':')) {
+ errx(1, "invalid format for password");
+ }
+ pw->pw_passwd = arg;
+ }
+
+#ifdef YP
+ pw->pw_name = strdup(pw->pw_name);
+ _use_yp = use_yp(pw->pw_name);
+ if (_use_yp == USER_YP_ONLY) {
+ if (!force_local) {
+ _use_yp = 1;
+ pw = (struct passwd *)&yp_password;
+ } else
+ errx(1, "unknown local user: %s.", pw->pw_name);
+ } else if (_use_yp == USER_LOCAL_ONLY) {
+ if (!force_yp) {
+ _use_yp = 0;
+ pw = (struct passwd *)&local_password;
+ } else
+ errx(1, "unknown NIS user: %s.", pw->pw_name);
+ } else if (_use_yp == USER_YP_AND_LOCAL) {
+ if (!force_local) {
+ _use_yp = 1;
+ pw = (struct passwd *)&yp_password;
+ } else {
+ _use_yp = 0;
+ pw = (struct passwd *)&local_password;
+ }
+ }
+#endif /* YP */
+
+ /*
+ * The temporary file/file descriptor usage is a little tricky here.
+ * 1: We start off with two fd's, one for the master password
+ * file (used to lock everything), and one for a temporary file.
+ * 2: Display() gets an fp for the temporary file, and copies the
+ * user's information into it. It then gives the temporary file
+ * to the user and closes the fp, closing the underlying fd.
+ * 3: The user edits the temporary file some number of times.
+ * 4: Verify() gets an fp for the temporary file, and verifies the
+ * contents. It can't use an fp derived from the step #2 fd,
+ * because the user's editor may have created a new instance of
+ * the file. Once the file is verified, its contents are stored
+ * in a password structure. The verify routine closes the fp,
+ * closing the underlying fd.
+ * 5: Delete the temporary file.
+ * 6: Get a new temporary file/fd. Pw_copy() gets an fp for it
+ * file and copies the master password file into it, replacing
+ * the user record with a new one. We can't use the first
+ * temporary file for this because it was owned by the user.
+ * Pw_copy() closes its fp, flushing the data and closing the
+ * underlying file descriptor. We can't close the master
+ * password fp, or we'd lose the lock.
+ * 7: Call pw_mkdb() (which renames the temporary file) and exit.
+ * The exit closes the master passwd fp/fd.
+ */
+ pw_init();
+ pfd = pw_lock();
+ tfd = pw_tmp();
+
+ if (op == EDITENTRY) {
+ display(tfd, pw);
+ edit(pw);
+ (void)unlink(tempname);
+ tfd = pw_tmp();
+ }
+
+#ifdef YP
+ if (_use_yp) {
+ yp_submit(pw);
+ (void)unlink(tempname);
+ } else {
+#endif /* YP */
+ pw_copy(pfd, tfd, pw);
+
+ if (!pw_mkdb())
+ pw_error((char *)NULL, 0, 1);
+#ifdef YP
+ }
+#endif /* YP */
+ exit(0);
+}
+
+void
+baduser()
+{
+ errx(1, "%s", strerror(EACCES));
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+#ifdef YP
+ "usage: chpass [-l] [-y] [-a list] [-p encpass] [-s shell] [user]\n");
+#else
+ "usage: chpass [-a list] [-p encpass] [-s shell] [user]\n");
+#endif
+ exit(1);
+}
diff --git a/usr.bin/chpass/chpass.h b/usr.bin/chpass/chpass.h
new file mode 100644
index 0000000..e7891ce
--- /dev/null
+++ b/usr.bin/chpass/chpass.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)chpass.h 8.4 (Berkeley) 4/2/94
+ */
+
+struct passwd;
+
+typedef struct _entry {
+ char *prompt;
+ int (*func)(), restricted, len;
+ char *except, *save;
+} ENTRY;
+
+/* Field numbers. */
+#define E_BPHONE 8
+#define E_HPHONE 9
+#define E_LOCATE 10
+#define E_NAME 7
+#define E_SHELL 12
+
+extern ENTRY list[];
+extern uid_t uid;
+
+int atot __P((char *, time_t *));
+void display __P((int, struct passwd *));
+void edit __P((struct passwd *));
+char *ok_shell __P((char *));
+int p_change __P((char *, struct passwd *, ENTRY *));
+int p_class __P((char *, struct passwd *, ENTRY *));
+int p_expire __P((char *, struct passwd *, ENTRY *));
+int p_gecos __P((char *, struct passwd *, ENTRY *));
+int p_gid __P((char *, struct passwd *, ENTRY *));
+int p_hdir __P((char *, struct passwd *, ENTRY *));
+int p_login __P((char *, struct passwd *, ENTRY *));
+int p_login __P((char *, struct passwd *, ENTRY *));
+int p_passwd __P((char *, struct passwd *, ENTRY *));
+int p_shell __P((char *, struct passwd *, ENTRY *));
+int p_uid __P((char *, struct passwd *, ENTRY *));
+char *ttoa __P((time_t));
+int verify __P((struct passwd *));
diff --git a/usr.bin/chpass/edit.c b/usr.bin/chpass/edit.c
new file mode 100644
index 0000000..c0f61e0
--- /dev/null
+++ b/usr.bin/chpass/edit.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)edit.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pw_scan.h>
+#include <pw_util.h>
+
+#include "chpass.h"
+#ifdef YP
+#include "pw_yp.h"
+#endif /* YP */
+
+extern char *tempname;
+
+void
+edit(pw)
+ struct passwd *pw;
+{
+ struct stat begin, end;
+
+ for (;;) {
+ if (stat(tempname, &begin))
+ pw_error(tempname, 1, 1);
+ pw_edit(1);
+ if (stat(tempname, &end))
+ pw_error(tempname, 1, 1);
+ if (begin.st_mtime == end.st_mtime) {
+ warnx("no changes made");
+ pw_error(NULL, 0, 0);
+ }
+ if (verify(pw))
+ break;
+ pw_prompt();
+ }
+}
+
+/*
+ * display --
+ * print out the file for the user to edit; strange side-effect:
+ * set conditional flag if the user gets to edit the shell.
+ */
+void
+display(fd, pw)
+ int fd;
+ struct passwd *pw;
+{
+ FILE *fp;
+ char *bp, *p, *ttoa();
+
+ if (!(fp = fdopen(fd, "w")))
+ pw_error(tempname, 1, 1);
+
+ (void)fprintf(fp,
+#ifdef YP
+ "#Changing %s information for %s.\n", _use_yp ? "NIS" : "user database", pw->pw_name);
+ if (!uid && !_use_yp) {
+#else
+ "#Changing user database information for %s.\n", pw->pw_name)
+ if (!uid) {
+#endif /* YP */
+ (void)fprintf(fp, "Login: %s\n", pw->pw_name);
+ (void)fprintf(fp, "Password: %s\n", pw->pw_passwd);
+ (void)fprintf(fp, "Uid [#]: %d\n", pw->pw_uid);
+ (void)fprintf(fp, "Gid [# or name]: %d\n", pw->pw_gid);
+ (void)fprintf(fp, "Change [month day year]: %s\n",
+ ttoa(pw->pw_change));
+ (void)fprintf(fp, "Expire [month day year]: %s\n",
+ ttoa(pw->pw_expire));
+ (void)fprintf(fp, "Class: %s\n", pw->pw_class);
+ (void)fprintf(fp, "Home directory: %s\n", pw->pw_dir);
+ (void)fprintf(fp, "Shell: %s\n",
+ *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
+ }
+ /* Only admin can change "restricted" shells. */
+ else if (ok_shell(pw->pw_shell))
+ /*
+ * Make shell a restricted field. Ugly with a
+ * necklace, but there's not much else to do.
+ */
+ (void)fprintf(fp, "Shell: %s\n",
+ *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL);
+ else
+ list[E_SHELL].restricted = 1;
+ bp = pw->pw_gecos;
+
+ p = strsep(&bp, ",");
+ if (p)
+ list[E_NAME].save = strdup(p);
+ if (!list[E_NAME].restricted || !uid)
+ (void)fprintf(fp, "Full Name: %s\n", p ? p : "");
+
+ p = strsep(&bp, ",");
+ if (p)
+ list[E_LOCATE].save = strdup(p);
+ if (!list[E_LOCATE].restricted || !uid)
+ (void)fprintf(fp, "Location: %s\n", p ? p : "");
+
+ p = strsep(&bp, ",");
+ if (p)
+ list[E_BPHONE].save = strdup(p);
+ if (!list[E_BPHONE].restricted || !uid)
+ (void)fprintf(fp, "Office Phone: %s\n", p ? p : "");
+
+ p = strsep(&bp, ",");
+ if (p)
+ list[E_HPHONE].save = strdup(p);
+ if (!list[E_HPHONE].restricted || !uid)
+ (void)fprintf(fp, "Home Phone: %s\n", p ? p : "");
+
+ (void)fchown(fd, getuid(), getgid());
+ (void)fclose(fp);
+}
+
+int
+verify(pw)
+ struct passwd *pw;
+{
+ ENTRY *ep;
+ char *p;
+ struct stat sb;
+ FILE *fp;
+ int len;
+ char buf[LINE_MAX];
+
+ if (!(fp = fopen(tempname, "r")))
+ pw_error(tempname, 1, 1);
+ if (fstat(fileno(fp), &sb))
+ pw_error(tempname, 1, 1);
+ if (sb.st_size == 0) {
+ warnx("corrupted temporary file");
+ goto bad;
+ }
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (!buf[0] || buf[0] == '#')
+ continue;
+ if (!(p = strchr(buf, '\n'))) {
+ warnx("line too long");
+ goto bad;
+ }
+ *p = '\0';
+ for (ep = list;; ++ep) {
+ if (!ep->prompt) {
+ warnx("unrecognized field");
+ goto bad;
+ }
+ if (!strncasecmp(buf, ep->prompt, ep->len)) {
+ if (ep->restricted && uid) {
+ warnx(
+ "you may not change the %s field",
+ ep->prompt);
+ goto bad;
+ }
+ if (!(p = strchr(buf, ':'))) {
+ warnx("line corrupted");
+ goto bad;
+ }
+ while (isspace(*++p));
+ if (ep->except && strpbrk(p, ep->except)) {
+ warnx(
+ "illegal character in the \"%s\" field",
+ ep->prompt);
+ goto bad;
+ }
+ if ((ep->func)(p, pw, ep)) {
+bad: (void)fclose(fp);
+ return (0);
+ }
+ break;
+ }
+ }
+ }
+ (void)fclose(fp);
+
+ /* Build the gecos field. */
+ len = strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
+ strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save) + 4;
+ if (!(p = malloc(len)))
+ err(1, NULL);
+ (void)sprintf(pw->pw_gecos = p, "%s,%s,%s,%s", list[E_NAME].save,
+ list[E_LOCATE].save, list[E_BPHONE].save, list[E_HPHONE].save);
+
+ if (snprintf(buf, sizeof(buf),
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class,
+ pw->pw_change, pw->pw_expire, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell) >= sizeof(buf)) {
+ warnx("entries too long");
+ return (0);
+ }
+ return (pw_scan(buf, pw));
+}
diff --git a/usr.bin/chpass/field.c b/usr.bin/chpass/field.c
new file mode 100644
index 0000000..6229e42
--- /dev/null
+++ b/usr.bin/chpass/field.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)field.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "chpass.h"
+#include "pathnames.h"
+
+/* ARGSUSED */
+int
+p_login(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!*p) {
+ warnx("empty login field");
+ return (1);
+ }
+ if (*p == '-') {
+ warnx("login names may not begin with a hyphen");
+ return (1);
+ }
+ if (!(pw->pw_name = strdup(p))) {
+ warnx("can't save entry");
+ return (1);
+ }
+ if (strchr(p, '.'))
+ warnx("\'.\' is dangerous in a login name");
+ for (; *p; ++p)
+ if (isupper(*p)) {
+ warnx("upper-case letters are dangerous in a login name");
+ break;
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_passwd(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!*p)
+ pw->pw_passwd = ""; /* "NOLOGIN"; */
+ else if (!(pw->pw_passwd = strdup(p))) {
+ warnx("can't save password entry");
+ return (1);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_uid(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ uid_t id;
+ char *np;
+
+ if (!*p) {
+ warnx("empty uid field");
+ return (1);
+ }
+ if (!isdigit(*p)) {
+ warnx("illegal uid");
+ return (1);
+ }
+ errno = 0;
+ id = strtoul(p, &np, 10);
+ if (*np || (id == ULONG_MAX && errno == ERANGE)) {
+ warnx("illegal uid");
+ return (1);
+ }
+ pw->pw_uid = id;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_gid(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ struct group *gr;
+ gid_t id;
+ char *np;
+
+ if (!*p) {
+ warnx("empty gid field");
+ return (1);
+ }
+ if (!isdigit(*p)) {
+ if (!(gr = getgrnam(p))) {
+ warnx("unknown group %s", p);
+ return (1);
+ }
+ pw->pw_gid = gr->gr_gid;
+ return (0);
+ }
+ errno = 0;
+ id = strtoul(p, &np, 10);
+ if (*np || (id == ULONG_MAX && errno == ERANGE)) {
+ warnx("illegal gid");
+ return (1);
+ }
+ pw->pw_gid = id;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_class(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!*p)
+ pw->pw_class = "";
+ else if (!(pw->pw_class = strdup(p))) {
+ warnx("can't save entry");
+ return (1);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_change(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!atot(p, &pw->pw_change))
+ return (0);
+ warnx("illegal date for change field");
+ return (1);
+}
+
+/* ARGSUSED */
+int
+p_expire(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!atot(p, &pw->pw_expire))
+ return (0);
+ warnx("illegal date for expire field");
+ return (1);
+}
+
+/* ARGSUSED */
+int
+p_gecos(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!*p)
+ ep->save = "";
+ else if (!(ep->save = strdup(p))) {
+ warnx("can't save entry");
+ return (1);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_hdir(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ if (!*p) {
+ warnx("empty home directory field");
+ return (1);
+ }
+ if (!(pw->pw_dir = strdup(p))) {
+ warnx("can't save entry");
+ return (1);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+int
+p_shell(p, pw, ep)
+ char *p;
+ struct passwd *pw;
+ ENTRY *ep;
+{
+ char *t, *ok_shell();
+
+ if (!*p) {
+ pw->pw_shell = _PATH_BSHELL;
+ return (0);
+ }
+ /* only admin can change from or to "restricted" shells */
+ if (uid && pw->pw_shell && !ok_shell(pw->pw_shell)) {
+ warnx("%s: current shell non-standard", pw->pw_shell);
+ return (1);
+ }
+ if (!(t = ok_shell(p))) {
+ if (uid) {
+ warnx("%s: non-standard shell", p);
+ return (1);
+ }
+ }
+ else
+ p = t;
+ if (!(pw->pw_shell = strdup(p))) {
+ warnx("can't save entry");
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/chpass/pathnames.h b/usr.bin/chpass/pathnames.h
new file mode 100644
index 0000000..30f3c0d
--- /dev/null
+++ b/usr.bin/chpass/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/chpass.XXXXXX"
diff --git a/usr.bin/chpass/pw_copy.c b/usr.bin/chpass/pw_copy.c
new file mode 100644
index 0000000..4a1d9a5
--- /dev/null
+++ b/usr.bin/chpass/pw_copy.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pw_copy.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * This module is used to copy the master password file, replacing a single
+ * record, by chpass(1) and passwd(1).
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pw_util.h>
+#include "pw_copy.h"
+
+extern char *tempname;
+
+void
+pw_copy(ffd, tfd, pw)
+ int ffd, tfd;
+ struct passwd *pw;
+{
+ FILE *from, *to;
+ int done;
+ char *p, buf[8192];
+
+ if (!(from = fdopen(ffd, "r")))
+ pw_error(_PATH_MASTERPASSWD, 1, 1);
+ if (!(to = fdopen(tfd, "w")))
+ pw_error(tempname, 1, 1);
+
+ for (done = 0; fgets(buf, sizeof(buf), from);) {
+ if (!strchr(buf, '\n')) {
+ warnx("%s: line too long", _PATH_MASTERPASSWD);
+ pw_error(NULL, 0, 1);
+ }
+ if (done) {
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ if (!(p = strchr(buf, ':'))) {
+ warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
+ pw_error(NULL, 0, 1);
+ }
+ *p = '\0';
+ if (strcmp(buf, pw->pw_name)) {
+ *p = ':';
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
+ pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
+ pw->pw_dir, pw->pw_shell);
+ done = 1;
+ if (ferror(to))
+ goto err;
+ }
+ if (!done)
+#ifdef YP
+ /* Ultra paranoid: shouldn't happen. */
+ if (getuid()) {
+ warnx("%s: not found in %s -- permission denied",
+ pw->pw_name, _PATH_MASTERPASSWD);
+ pw_error(NULL, 0, 1);
+ } else
+#endif /* YP */
+ (void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
+ pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
+ pw->pw_dir, pw->pw_shell);
+
+ if (ferror(to))
+err: pw_error(NULL, 1, 1);
+ (void)fclose(to);
+}
diff --git a/usr.bin/chpass/pw_copy.h b/usr.bin/chpass/pw_copy.h
new file mode 100644
index 0000000..4ef68c5
--- /dev/null
+++ b/usr.bin/chpass/pw_copy.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pw_copy.h 8.1 (Berkeley) 4/2/94
+ */
+
+void pw_copy __P((int, int, struct passwd *));
diff --git a/usr.bin/chpass/pw_yp.c b/usr.bin/chpass/pw_yp.c
new file mode 100644
index 0000000..2c1c138
--- /dev/null
+++ b/usr.bin/chpass/pw_yp.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * NIS interface routines for chpass
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ *
+ * $Id: pw_yp.c,v 1.1 1995/08/13 16:12:27 wpaul Exp $
+ */
+
+#ifdef YP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <err.h>
+#include <unistd.h>
+#include <db.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <limits.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yppasswd.h>
+#include <pw_util.h>
+#include "pw_yp.h"
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 2048 * 1024, /* cachesize */
+ NULL, /* hash */
+ 0, /* lorder */
+};
+
+int _use_yp = 0;
+
+/* Save the local and NIS password information */
+struct passwd local_password;
+struct passwd yp_password;
+
+
+void copy_yp_pass(p, x, m)
+char *p;
+int x, m;
+{
+ register char *t, *s = p;
+
+ yp_password.pw_fields = 0;
+
+ t = (char *)malloc(m + 10);
+
+ /* Turn all colons into NULLs */
+ while (strchr(s, ':')) {
+ s = (strchr(s, ':') + 1);
+ *(s - 1)= '\0';
+ }
+
+#define EXPAND(e) e = t; while (*t++ = *p++);
+ EXPAND(yp_password.pw_name);
+ yp_password.pw_fields |= _PWF_NAME;
+ EXPAND(yp_password.pw_passwd);
+ yp_password.pw_fields |= _PWF_PASSWD;
+ yp_password.pw_uid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_UID;
+ yp_password.pw_gid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_GID;
+ if (x) {
+ EXPAND(yp_password.pw_class);
+ yp_password.pw_fields |= _PWF_CLASS;
+ yp_password.pw_change = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_CHANGE;
+ yp_password.pw_expire = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_expire |= _PWF_EXPIRE;
+ }
+ EXPAND(yp_password.pw_gecos);
+ yp_password.pw_fields |= _PWF_GECOS;
+ EXPAND(yp_password.pw_dir);
+ yp_password.pw_fields |= _PWF_DIR;
+ EXPAND(yp_password.pw_shell);
+ yp_password.pw_fields |= _PWF_SHELL;
+
+ return;
+}
+
+void copy_local_pass(p,m)
+char *p;
+int m;
+{
+ register char *t;
+
+ t = (char *)malloc(m + 10);
+
+ EXPAND(local_password.pw_name);
+ EXPAND(local_password.pw_passwd);
+ bcopy(p, (char *)&local_password.pw_uid, sizeof(int));
+ p += sizeof(int);
+ bcopy(p, (char *)&local_password.pw_gid, sizeof(int));
+ p += sizeof(int);
+ bcopy(p, (char *)&local_password.pw_change, sizeof(time_t));
+ p += sizeof(time_t);
+ EXPAND(local_password.pw_class);
+ EXPAND(local_password.pw_gecos);
+ EXPAND(local_password.pw_dir);
+ EXPAND(local_password.pw_shell);
+ bcopy(p, (char *)&local_password.pw_expire, sizeof(time_t));
+ p += sizeof(time_t);
+ bcopy(p, (char *)&local_password.pw_fields, sizeof local_password.pw_fields);
+ p += sizeof local_password.pw_fields;
+
+ return;
+}
+
+/*
+ * Check if the user we're working with is local or in NIS.
+ */
+int use_yp (user)
+char *user;
+{
+ int user_local = 0, user_yp = 0, user_exists = 0;
+ DB *dbp;
+ DBT key,data;
+ char bf[UT_NAMESIZE + 2];
+ char *domain;
+ char *result;
+ int resultlen, rval;
+
+
+ /* Is the user anywhere */
+ if (getpwnam(user) != NULL)
+ user_exists = 1;
+
+ if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
+ DB_HASH, &openinfo)) == NULL)
+ errx(1, "error opening database: %s.", _PATH_MP_DB);
+
+ /* Is NIS turned on */
+ bf[0] = _PW_KEYYPENABLED;
+ key.data = (u_char *)bf;
+ key.size = 1;
+ if (!(dbp->get)(dbp,&key,&data,0)) {
+ if ((rval = yp_get_default_domain(&domain))) {
+ warnx("can't get local NIS domain name: %s",yperr_string(rval));
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Is the user in the NIS passwd map */
+ if (!yp_match(domain, "passwd.byname", user, strlen(user),
+ &result, &resultlen)) {
+ user_yp = 1;
+ copy_yp_pass(result, 0, resultlen);
+ free(result);
+ }
+ /* Is the user in the NIS passwd map */
+ if (user_yp && !yp_match(domain, "master.passwd.byname",
+ user, strlen(user),
+ &result, &resultlen)) {
+ copy_yp_pass(result, 1, resultlen);
+ }
+ free(result);
+ }
+
+ /* Is the user in the local password database */
+
+ bf[0] = _PW_KEYBYNAME;
+ bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
+ key.data = (u_char *)bf;
+ key.size = strlen(user) + 1;
+ if (!(dbp->get)(dbp,&key,&data,0)) {
+ user_local = 1;
+ copy_local_pass(data.data, data.size);
+ }
+
+ (dbp->close)(dbp);
+
+ if (user_local && user_yp && user_exists)
+ return(USER_YP_AND_LOCAL);
+ else if (!user_local && user_yp && user_exists)
+ return(USER_YP_ONLY);
+ else if (user_local && !user_yp && user_exists)
+ return(USER_LOCAL_ONLY);
+ else if (!user_exists)
+ return(USER_UNKNOWN);
+ return(-1);
+}
+
+/*
+ * Find the name of the NIS master server for this domain
+ * and make sure it's running yppasswdd.
+ */
+static char *get_yp_master(void)
+{
+ char *domain, *mastername;
+ int rval;
+
+ /* Get default NIS domain. */
+
+ if ((rval = yp_get_default_domain(&domain))) {
+ warnx("can't get local NIS domain name: %s",yperr_string(rval));
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Get master server of passwd map. */
+
+ if ((rval = yp_master(domain, "passwd.byname", &mastername))) {
+ warnx("can't get master NIS server: %s", yperr_string(rval));
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Check if yppasswdd is out there. */
+
+ if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
+ IPPROTO_UDP)) == 0) {
+ warnx("yppasswdd not running on NIS master server");
+ pw_error(NULL, 0, 1);
+ }
+
+ /*
+ * Make sure it's on a reserved port.
+ * XXX Might break with yppasswdd servers running on Solaris 2.x.
+ */
+
+ if (rval >= IPPORT_RESERVED) {
+ warnx("yppasswdd server not running on reserved port");
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Everything checks out: return the name of the server. */
+
+ return (mastername);
+}
+/*
+ * Ask the user for his NIS password and submit the new information
+ * to yppasswdd. Note that yppasswdd requires password authentication
+ * and only allows changes to existing records rather than the addition
+ * of new records. (To do actual updates we would need something like
+ * secure RPC and ypupdated, which FreeBSD doesn't have yet.) This means
+ * that the superuser cannot use chpass(1) to add new users records to
+ * the NIS password database.
+ */
+void yp_submit(pw)
+struct passwd *pw;
+{
+ struct yppasswd yppasswd;
+ CLIENT *clnt;
+ char *master, *password, *encpass;
+ int rval, status = 0;
+ struct timeval tv;
+
+ /* Populate the yppasswd structure that gets handed to yppasswdd. */
+ /*
+ * XXX This is done first to work around what looks like a very
+ * strange memory corruption bug: the text fields pointed to
+ * by the members of the 'pw' structure appear to be clobbered
+ * after get_yp_master() returns (in particular, it happens
+ * during getrpcport()). I don't know exactly where the problem
+ * lies: I traced it all the way to gethostbyname(), then gave
+ * up.
+ */
+ yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+ yppasswd.newpw.pw_name = strdup(pw->pw_name);
+ yppasswd.newpw.pw_uid = pw->pw_uid;
+ yppasswd.newpw.pw_gid = pw->pw_gid;
+ yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+ yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+ yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+
+ /* Get NIS master server name */
+
+ master = get_yp_master();
+
+ /* Get the user's password for authentication purposes. */
+
+ printf ("Changing NIS information for %s on %s\n",
+ yppasswd.newpw.pw_name, master);
+ encpass = (getpwnam(yppasswd.newpw.pw_name))->pw_passwd;
+ password = getpass("Please enter password: ");
+ if (strncmp(crypt(password, encpass), encpass, strlen(encpass))) {
+ warnx("Password incorrect.");
+ pw_error(NULL, 0, 1);
+ }
+
+ yppasswd.oldpass = password; /* XXX */
+
+ /* Create a handle to yppasswdd. */
+
+ clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
+ clnt->cl_auth = authunix_create_default();
+
+ /* Set a timeout and make the RPC call. */
+
+ tv.tv_sec = 20;
+ tv.tv_usec = 0;
+ rval = clnt_call(clnt, YPPASSWDPROC_UPDATE, xdr_yppasswd,
+ (char *)&yppasswd, xdr_int, (char *)&status, &tv);
+
+ /* Call failed: signal the error. */
+
+ if (rval) {
+ warnx("NIS update failed: %s", clnt_sperrno(rval));
+ pw_error(NULL, 0, 1);
+ }
+
+ /* Success. */
+
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ warnx("NIS information changed on host %s", master);
+
+ return;
+}
+#endif /* YP */
diff --git a/usr.bin/chpass/pw_yp.h b/usr.bin/chpass/pw_yp.h
new file mode 100644
index 0000000..9d98a43
--- /dev/null
+++ b/usr.bin/chpass/pw_yp.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * NIS interface routines for chpass
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ *
+ * $Id: pw_yp.h,v 1.1 1995/08/13 16:12:28 wpaul Exp $
+ */
+
+#ifdef YP
+/* Four possible return codes from use_yp() */
+#define USER_UNKNOWN 0
+#define USER_YP_ONLY 1
+#define USER_LOCAL_ONLY 2
+#define USER_YP_AND_LOCAL 3
+extern int _use_yp;
+extern struct passwd local_password;
+extern struct passwd yp_password;
+void yp_submit __P(( struct passwd * ));
+int use_yp __P(( char * ));
+#endif /* YP */
diff --git a/usr.bin/chpass/table.c b/usr.bin/chpass/table.c
new file mode 100644
index 0000000..46a12d1
--- /dev/null
+++ b/usr.bin/chpass/table.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)table.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stddef.h>
+#include "chpass.h"
+
+char e1[] = ": ";
+char e2[] = ":,";
+
+ENTRY list[] = {
+ { "login", p_login, 1, 5, e1, },
+ { "password", p_passwd, 1, 8, e1, },
+ { "uid", p_uid, 1, 3, e1, },
+ { "gid", p_gid, 1, 3, e1, },
+ { "class", p_class, 1, 5, e1, },
+ { "change", p_change, 1, 6, NULL, },
+ { "expire", p_expire, 1, 6, NULL, },
+ { "full name", p_gecos, 0, 9, e2, },
+ { "office phone", p_gecos, 0, 12, e2, },
+ { "home phone", p_gecos, 0, 10, e2, },
+ { "location", p_gecos, 0, 8, e2, },
+ { "home directory", p_hdir, 1, 14, e1, },
+ { "shell", p_shell, 0, 5, e1, },
+ { NULL, 0, },
+};
diff --git a/usr.bin/chpass/util.c b/usr.bin/chpass/util.c
new file mode 100644
index 0000000..fe89d79
--- /dev/null
+++ b/usr.bin/chpass/util.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "chpass.h"
+#include "pathnames.h"
+
+static char *months[] =
+ { "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November",
+ "December", NULL };
+
+char *
+ttoa(tval)
+ time_t tval;
+{
+ struct tm *tp;
+ static char tbuf[50];
+
+ if (tval) {
+ tp = localtime(&tval);
+ (void)sprintf(tbuf, "%s %d, %d", months[tp->tm_mon],
+ tp->tm_mday, tp->tm_year + 1900);
+ }
+ else
+ *tbuf = '\0';
+ return (tbuf);
+}
+
+int
+atot(p, store)
+ char *p;
+ time_t *store;
+{
+ static struct tm *lt;
+ char *t, **mp;
+ time_t tval;
+ int day, month, year;
+
+ if (!*p) {
+ *store = 0;
+ return (0);
+ }
+ if (!lt) {
+ unsetenv("TZ");
+ (void)time(&tval);
+ lt = localtime(&tval);
+ }
+ if (!(t = strtok(p, " \t")))
+ goto bad;
+ for (mp = months;; ++mp) {
+ if (!*mp)
+ goto bad;
+ if (!strncasecmp(*mp, t, 3)) {
+ month = mp - months + 1;
+ break;
+ }
+ }
+ if (!(t = strtok((char *)NULL, " \t,")) || !isdigit(*t))
+ goto bad;
+ day = atoi(t);
+ if (!(t = strtok((char *)NULL, " \t,")) || !isdigit(*t))
+ goto bad;
+ year = atoi(t);
+ if (day < 1 || day > 31 || month < 1 || month > 12 || !year)
+ goto bad;
+ if (year < 100)
+ year += 1900;
+ if (year <= 1970)
+bad: return (1);
+ lt->tm_year = year - 1900;
+ lt->tm_mon = month - 1;
+ lt->tm_mday = day;
+ lt->tm_hour = 0;
+ lt->tm_min = 0;
+ lt->tm_sec = 0;
+ lt->tm_isdst = -1;
+ if ((tval = mktime(lt)) < 0)
+ return (1);
+ *store = tval;
+ return (0);
+}
+
+char *
+ok_shell(name)
+ char *name;
+{
+ char *p, *sh;
+
+ setusershell();
+ while (sh = getusershell()) {
+ if (!strcmp(name, sh))
+ return (name);
+ /* allow just shell name, but use "real" path */
+ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0)
+ return (sh);
+ }
+ return (NULL);
+}
diff --git a/usr.bin/cksum/Makefile b/usr.bin/cksum/Makefile
new file mode 100644
index 0000000..d2f09b7
--- /dev/null
+++ b/usr.bin/cksum/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= cksum
+SRCS= cksum.c crc.c print.c sum1.c sum2.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cksum/cksum.1 b/usr.bin/cksum/cksum.1
new file mode 100644
index 0000000..7c2cc24
--- /dev/null
+++ b/usr.bin/cksum/cksum.1
@@ -0,0 +1,164 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cksum.1 8.1 (Berkeley) 6/29/93
+.\"
+.Dd June 29, 1993
+.Dt CKSUM 1
+.Os BSD 4.4
+.Sh NAME
+.Nm cksum
+.Nd display file checksums and block counts
+.Sh SYNOPSIS
+.Nm cksum
+.Op Fl o Op \&1 \&| \&2
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm cksum
+utility writes to the standard output three whitespace separated
+fields for each input file.
+These fields are a checksum
+.Tn CRC ,
+the total number of octets in the file and the file name.
+If no file name is specified, the standard input is used and no file name
+is written.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Use historic algorithms instead of the (superior) default one.
+.Pp
+Algorithm 1 is the algorithm used by historic
+.Bx
+systems as the
+.Xr sum 1
+algorithm and by historic
+.At V
+systems as the
+.Xr sum
+algorithm when using the
+.Fl r
+option.
+This is a 16-bit checksum, with a right rotation before each addition;
+overflow is discarded.
+.Pp
+Algorithm 2 is the algorithm used by historic
+.At V
+systems as the
+default
+.Xr sum
+algorithm.
+This is a 32-bit checksum, and is defined as follows:
+.Bd -unfilled -offset indent
+s = sum of all bytes;
+r = s % 2^16 + (s % 2^32) / 2^16;
+cksum = (r % 2^16) + r / 2^16;
+.Ed
+.Pp
+Both algorithm 1 and 2 write to the standard output the same fields as
+the default algorithm except that the size of the file in bytes is
+replaced with the size of the file in blocks.
+For historic reasons, the block size is 1024 for algorithm 1 and 512
+for algorithm 2.
+Partial blocks are rounded up.
+.El
+.Pp
+The default
+.Tn CRC
+used is based on the polynomial used for
+.Tn CRC
+error checking
+in the networking standard
+.St -iso8802-3
+The
+.Tn CRC
+checksum encoding is defined by the generating polynomial:
+.Pp
+.Bd -unfilled -offset indent
+G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 +
+ x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
+.Ed
+.Pp
+Mathematically, the
+.Tn CRC
+value corresponding to a given file is defined by
+the following procedure:
+.Bd -filled -offset indent
+The
+.Ar n
+bits to be evaluated are considered to be the coefficients of a mod 2
+polynomial M(x) of degree
+.Ar n Ns \-1 .
+These
+.Ar n
+bits are the bits from the file, with the most significant bit being the most
+significant bit of the first octet of the file and the last bit being the least
+significant bit of the last octet, padded with zero bits (if necessary) to
+achieve an integral number of octets, followed by one or more octets
+representing the length of the file as a binary value, least significant octet
+first.
+The smallest number of octets capable of representing this integer are used.
+.Pp
+M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by
+G(x) using mod 2 division, producing a remainder R(x) of degree <= 31.
+.Pp
+The coefficients of R(x) are considered to be a 32-bit sequence.
+.Pp
+The bit sequence is complemented and the result is the CRC.
+.Ed
+.Pp
+The
+.Nm cksum
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+The default calculation is identical to that given in pseudo-code
+in the following
+.Tn ACM
+article.
+.Rs
+.%T "Computation of Cyclic Redundancy Checks Via Table Lookup"
+.%A Dilip V. Sarwate
+.%J "Communications of the \\*(tNACM\\*(sP"
+.%D "August 1988"
+.Re
+.Sh STANDARDS
+The
+.Nm cksum
+utility is expected to be POSIX 1003.2 compatible.
+.Sh HISTORY
+The
+.Nm cksum
+utility appears in
+.Bx 4.4 .
diff --git a/usr.bin/cksum/cksum.c b/usr.bin/cksum/cksum.c
new file mode 100644
index 0000000..3e66ca5
--- /dev/null
+++ b/usr.bin/cksum/cksum.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ u_long len, val;
+ register int ch, fd, rval;
+ char *fn;
+ int (*cfncn) __P((int, unsigned long *, unsigned long *));
+ void (*pfncn) __P((char *, unsigned long, unsigned long));
+
+ cfncn = crc;
+ pfncn = pcrc;
+ while ((ch = getopt(argc, argv, "o:")) != EOF)
+ switch(ch) {
+ case 'o':
+ if (*optarg == '1') {
+ cfncn = csum1;
+ pfncn = psum1;
+ } else if (*optarg == '2') {
+ cfncn = csum2;
+ pfncn = psum2;
+ } else {
+ (void)fprintf(stderr,
+ "cksum: illegal argument to -o option\n");
+ usage();
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ fd = STDIN_FILENO;
+ fn = NULL;
+ rval = 0;
+ do {
+ if (*argv) {
+ fn = *argv++;
+ if ((fd = open(fn, O_RDONLY, 0)) < 0) {
+ (void)fprintf(stderr, "cksum: %s: %s\n",
+ fn, strerror(errno));
+ rval = 1;
+ continue;
+ }
+ }
+ if (cfncn(fd, &val, &len)) {
+ (void)fprintf(stderr, "cksum: %s: %s\n",
+ fn ? fn : "stdin", strerror(errno));
+ rval = 1;
+ } else
+ pfncn(fn, val, len);
+ (void)close(fd);
+ } while (*argv);
+ exit(rval);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: cksum [-o 1 | 2] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/cksum/crc.c b/usr.bin/cksum/crc.c
new file mode 100644
index 0000000..2a5eb6d
--- /dev/null
+++ b/usr.bin/cksum/crc.c
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+static u_long crctab[] = {
+ 0x0,
+ 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+ 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+ 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+ 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+ 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+ 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+ 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+ 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+ 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+ 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+ 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+ 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+ 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+ 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+ 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+ 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+ 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+ 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+ 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+ 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+ 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+ 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+ 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+ 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+ 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+ 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+ 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+ 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+ 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+ 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+ 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+ 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+ 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+ 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/*
+ * Compute a POSIX 1003.2 checksum. This routine has been broken out so that
+ * other programs can use it. It takes a file descriptor to read from and
+ * locations to store the crc and the number of bytes read. It returns 0 on
+ * success and 1 on failure. Errno is set on failure.
+ */
+u_long crc_total = ~0; /* The crc over a number of files. */
+
+int
+crc(fd, cval, clen)
+ register int fd;
+ u_long *cval, *clen;
+{
+ register u_char *p;
+ register int nr;
+ register u_long crc, len;
+ u_char buf[16 * 1024];
+
+#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+
+ crc = len = 0;
+ crc_total = ~crc_total;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (len += nr, p = buf; nr--; ++p) {
+ COMPUTE(crc, *p);
+ COMPUTE(crc_total, *p);
+ }
+ if (nr < 0)
+ return (1);
+
+ *clen = len;
+
+ /* Include the length of the file. */
+ for (; len != 0; len >>= 8) {
+ COMPUTE(crc, len & 0xff);
+ COMPUTE(crc_total, len & 0xff);
+ }
+
+ *cval = ~crc;
+ crc_total = ~crc_total;
+ return (0);
+}
diff --git a/usr.bin/cksum/extern.h b/usr.bin/cksum/extern.h
new file mode 100644
index 0000000..ed61120
--- /dev/null
+++ b/usr.bin/cksum/extern.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int crc __P((int, unsigned long *, unsigned long *));
+void pcrc __P((char *, unsigned long, unsigned long));
+void psum1 __P((char *, unsigned long, unsigned long));
+void psum2 __P((char *, unsigned long, unsigned long));
+int csum1 __P((int, unsigned long *, unsigned long *));
+int csum2 __P((int, unsigned long *, unsigned long *));
+__END_DECLS
diff --git a/usr.bin/cksum/print.c b/usr.bin/cksum/print.c
new file mode 100644
index 0000000..d0ffeb3
--- /dev/null
+++ b/usr.bin/cksum/print.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "extern.h"
+
+void
+pcrc(fn, val, len)
+ char *fn;
+ u_long val, len;
+{
+ (void)printf("%lu %lu", val, len);
+ if (fn)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
+
+void
+psum1(fn, val, len)
+ char *fn;
+ u_long val, len;
+{
+ (void)printf("%lu %lu", val, (len + 1023) / 1024);
+ if (fn)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
+
+void
+psum2(fn, val, len)
+ char *fn;
+ u_long val, len;
+{
+ (void)printf("%lu %lu", val, (len + 511) / 512);
+ if (fn)
+ (void)printf(" %s", fn);
+ (void)printf("\n");
+}
diff --git a/usr.bin/cksum/sum1.c b/usr.bin/cksum/sum1.c
new file mode 100644
index 0000000..19eeebe
--- /dev/null
+++ b/usr.bin/cksum/sum1.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int
+csum1(fd, cval, clen)
+ register int fd;
+ u_long *cval, *clen;
+{
+ register u_long total;
+ register int nr;
+ register u_int crc;
+ register u_char *p;
+ u_char buf[8192];
+
+ /*
+ * 16-bit checksum, rotating right before each addition;
+ * overflow is discarded.
+ */
+ crc = total = 0;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (total += nr, p = buf; nr--; ++p) {
+ if (crc & 1)
+ crc |= 0x10000;
+ crc = ((crc >> 1) + *p) & 0xffff;
+ }
+ if (nr < 0)
+ return(1);
+
+ *cval = crc;
+ *clen = total;
+ return(0);
+}
diff --git a/usr.bin/cksum/sum2.c b/usr.bin/cksum/sum2.c
new file mode 100644
index 0000000..c54d88e
--- /dev/null
+++ b/usr.bin/cksum/sum2.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+int
+csum2(fd, cval, clen)
+ register int fd;
+ u_long *cval, *clen;
+{
+ register u_long crc, total;
+ register int nr;
+ register u_char *p;
+ u_char buf[8192];
+
+ /*
+ * Draft 8 POSIX 1003.2:
+ *
+ * s = sum of all bytes
+ * r = s % 2^16 + (s % 2^32) / 2^16
+ * crc = (r % 2^16) + r / 2^16
+ */
+ crc = total = 0;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (total += nr, p = buf; nr--; ++p)
+ crc += *p;
+ if (nr < 0)
+ return(1);
+
+ crc = (crc & 0xffff) + (crc >> 16);
+ crc = (crc & 0xffff) + (crc >> 16);
+
+ *cval = crc;
+ *clen = total;
+ return(0);
+}
diff --git a/usr.bin/cmp/Makefile b/usr.bin/cmp/Makefile
new file mode 100644
index 0000000..1b84ce3
--- /dev/null
+++ b/usr.bin/cmp/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= cmp
+SRCS= cmp.c misc.c regular.c special.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cmp/cmp.1 b/usr.bin/cmp/cmp.1
new file mode 100644
index 0000000..9db561a
--- /dev/null
+++ b/usr.bin/cmp/cmp.1
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cmp.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CMP 1
+.Os
+.Sh NAME
+.Nm cmp
+.Nd compare two files
+.Sh SYNOPSIS
+.Nm cmp
+.Op Fl l | Fl s
+.Ar file1 file2
+.Op Ar skip1 Op Ar skip2
+.Sh DESCRIPTION
+The cmp utility compares two files of any type and writes the results
+to the standard output.
+By default,
+.Nm
+is silent if the files are the same; if they differ, the byte
+and line number at which the first difference occurred is reported.
+.Pp
+Bytes and lines are numbered beginning with one.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.It Fl l
+Print the byte number (decimal) and the differing
+byte values (octal) for each difference.
+.It Fl s
+Print nothing for differing files; return exit
+status only.
+.El
+.Pp
+The optional arguments
+.Ar skip1
+and
+.Ar skip2
+are the byte offsets from the beginning of
+.Ar file1
+and
+.Ar file2 ,
+respectively, where the comparison will begin.
+The offset is decimal by default, but may be expressed as an hexadecimal
+or octal value by preceding it with a leading ``0x'' or ``0''.
+.Pp
+The
+.Nm cmp
+utility exits with one of the following values:
+.Bl -tag -width 4n
+.It 0
+The files are identical.
+.It 1
+The files are different; this includes the case
+where one file is identical to the first part of
+the other.
+In the latter case, if the
+.Fl s
+option has not been specified,
+.Nm cmp
+writes to standard output that EOF was reached in the shorter
+file (before any differences were found).
+.It >1
+An error occurred.
+.El
+.Sh SEE ALSO
+.Xr diff 1 ,
+.Xr diff3 1
+.Sh STANDARDS
+The
+.Nm cmp
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/cmp/cmp.c b/usr.bin/cmp/cmp.c
new file mode 100644
index 0000000..29fcb49
--- /dev/null
+++ b/usr.bin/cmp/cmp.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1987, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmp.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int lflag, sflag;
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb1, sb2;
+ off_t skip1, skip2;
+ int ch, fd1, fd2, special;
+ char *file1, *file2;
+
+ while ((ch = getopt(argc, argv, "-ls")) != EOF)
+ switch (ch) {
+ case 'l': /* print all differences */
+ lflag = 1;
+ break;
+ case 's': /* silent run */
+ sflag = 1;
+ break;
+ case '-': /* stdin (must be after options) */
+ --optind;
+ goto endargs;
+ case '?':
+ default:
+ usage();
+ }
+endargs:
+ argv += optind;
+ argc -= optind;
+
+ if (lflag && sflag)
+ errx(ERR_EXIT, "only one of -l and -s may be specified");
+
+ if (argc < 2 || argc > 4)
+ usage();
+
+ /* Backward compatibility -- handle "-" meaning stdin. */
+ special = 0;
+ if (strcmp(file1 = argv[0], "-") == 0) {
+ special = 1;
+ fd1 = 0;
+ file1 = "stdin";
+ }
+ else if ((fd1 = open(file1, O_RDONLY, 0)) < 0) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file1);
+ else
+ exit(1);
+ }
+ if (strcmp(file2 = argv[1], "-") == 0) {
+ if (special)
+ errx(ERR_EXIT,
+ "standard input may only be specified once");
+ special = 1;
+ fd2 = 0;
+ file2 = "stdin";
+ }
+ else if ((fd2 = open(file2, O_RDONLY, 0)) < 0) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file2);
+ else
+ exit(1);
+ }
+
+ skip1 = argc > 2 ? strtol(argv[2], NULL, 10) : 0;
+ skip2 = argc == 4 ? strtol(argv[3], NULL, 10) : 0;
+
+ if (!special) {
+ if (fstat(fd1, &sb1)) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file1);
+ else
+ exit(1);
+ }
+ if (!S_ISREG(sb1.st_mode))
+ special = 1;
+ else {
+ if (fstat(fd2, &sb2)) {
+ if (!sflag)
+ err(ERR_EXIT, "%s", file2);
+ else
+ exit(1);
+ }
+ if (!S_ISREG(sb2.st_mode))
+ special = 1;
+ }
+ }
+
+ if (special)
+ c_special(fd1, file1, skip1, fd2, file2, skip2);
+ else
+ c_regular(fd1, file1, skip1, sb1.st_size,
+ fd2, file2, skip2, sb2.st_size);
+ exit(0);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: cmp [-l | s] file1 file2 [skip1 [skip2]]\n");
+ exit(ERR_EXIT);
+}
diff --git a/usr.bin/cmp/extern.h b/usr.bin/cmp/extern.h
new file mode 100644
index 0000000..b01e2de
--- /dev/null
+++ b/usr.bin/cmp/extern.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/2/94
+ */
+
+#define OK_EXIT 0
+#define DIFF_EXIT 1
+#define ERR_EXIT 2 /* error exit code */
+
+void c_regular __P((int, char *, off_t, off_t, int, char *, off_t, off_t));
+void c_special __P((int, char *, off_t, int, char *, off_t));
+void diffmsg __P((char *, char *, off_t, off_t));
+void eofmsg __P((char *));
+
+extern int lflag, sflag;
diff --git a/usr.bin/cmp/misc.c b/usr.bin/cmp/misc.c
new file mode 100644
index 0000000..d5a601d
--- /dev/null
+++ b/usr.bin/cmp/misc.c
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "extern.h"
+
+void
+eofmsg(file)
+ char *file;
+{
+ if (!sflag)
+ warnx("EOF on %s", file);
+ exit(DIFF_EXIT);
+}
+
+void
+diffmsg(file1, file2, byte, line)
+ char *file1, *file2;
+ off_t byte, line;
+{
+ if (!sflag)
+ (void)printf("%s %s differ: char %qd, line %qd\n",
+ file1, file2, byte, line);
+ exit(DIFF_EXIT);
+}
diff --git a/usr.bin/cmp/regular.c b/usr.bin/cmp/regular.c
new file mode 100644
index 0000000..35f62d1
--- /dev/null
+++ b/usr.bin/cmp/regular.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)regular.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "extern.h"
+
+void
+c_regular(fd1, file1, skip1, len1, fd2, file2, skip2, len2)
+ int fd1, fd2;
+ char *file1, *file2;
+ off_t skip1, len1, skip2, len2;
+{
+ u_char ch, *p1, *p2;
+ off_t byte, length, line;
+ int dfound;
+
+ if (sflag && len1 != len2)
+ exit(1);
+
+ if (skip1 > len1)
+ eofmsg(file1);
+ len1 -= skip1;
+ if (skip2 > len2)
+ eofmsg(file2);
+ len2 -= skip2;
+
+ length = MIN(len1, len2);
+ if (length > SIZE_T_MAX)
+ return (c_special(fd1, file1, skip1, fd2, file2, skip2));
+
+ if ((p1 = (u_char *)mmap(NULL,
+ (size_t)length, PROT_READ, 0, fd1, skip1)) == (u_char *)-1)
+ err(ERR_EXIT, "%s", file1);
+ if ((p2 = (u_char *)mmap(NULL,
+ (size_t)length, PROT_READ, 0, fd2, skip2)) == (u_char *)-1)
+ err(ERR_EXIT, "%s", file2);
+
+ dfound = 0;
+ for (byte = line = 1; length--; ++p1, ++p2, ++byte) {
+ if ((ch = *p1) != *p2)
+ if (lflag) {
+ dfound = 1;
+ (void)printf("%6qd %3o %3o\n", byte, ch, *p2);
+ } else
+ diffmsg(file1, file2, byte, line);
+ /* NOTREACHED */
+ if (ch == '\n')
+ ++line;
+ }
+
+ if (len1 != len2)
+ eofmsg (len1 > len2 ? file2 : file1);
+ if (dfound)
+ exit(DIFF_EXIT);
+}
diff --git a/usr.bin/cmp/special.c b/usr.bin/cmp/special.c
new file mode 100644
index 0000000..0dcd0c5
--- /dev/null
+++ b/usr.bin/cmp/special.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)special.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "extern.h"
+
+void
+c_special(fd1, file1, skip1, fd2, file2, skip2)
+ int fd1, fd2;
+ char *file1, *file2;
+ off_t skip1, skip2;
+{
+ int ch1, ch2;
+ off_t byte, line;
+ FILE *fp1, *fp2;
+ int dfound;
+
+ if ((fp1 = fdopen(fd1, "r")) == NULL)
+ err(ERR_EXIT, "%s", file1);
+ if ((fp2 = fdopen(fd2, "r")) == NULL)
+ err(ERR_EXIT, "%s", file2);
+
+ while (skip1--)
+ if (getc(fp1) == EOF)
+ goto eof;
+ while (skip2--)
+ if (getc(fp2) == EOF)
+ goto eof;
+
+ dfound = 0;
+ for (byte = line = 1;; ++byte) {
+ ch1 = getc(fp1);
+ ch2 = getc(fp2);
+ if (ch1 == EOF || ch2 == EOF)
+ break;
+ if (ch1 != ch2)
+ if (lflag) {
+ dfound = 1;
+ (void)printf("%6qd %3o %3o\n", byte, ch1, ch2);
+ } else
+ diffmsg(file1, file2, byte, line);
+ /* NOTREACHED */
+ if (ch1 == '\n')
+ ++line;
+ }
+
+eof: if (ferror(fp1))
+ err(ERR_EXIT, "%s", file1);
+ if (ferror(fp2))
+ err(ERR_EXIT, "%s", file2);
+ if (feof(fp1)) {
+ if (!feof(fp2))
+ eofmsg(file1);
+ } else
+ if (feof(fp2))
+ eofmsg(file2);
+ if (dfound)
+ exit(DIFF_EXIT);
+}
diff --git a/usr.bin/col/Makefile b/usr.bin/col/Makefile
new file mode 100644
index 0000000..efa0933
--- /dev/null
+++ b/usr.bin/col/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= col
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/col/README b/usr.bin/col/README
new file mode 100644
index 0000000..f673f3a
--- /dev/null
+++ b/usr.bin/col/README
@@ -0,0 +1,48 @@
+# @(#)README 8.1 (Berkeley) 6/6/93
+
+col - filter out reverse line feeds.
+
+Options are:
+ -b do not print any backspaces (last character written is printed)
+ -f allow half line feeds in output, by default characters between
+ lines are pushed to the line below
+ -x do not compress spaces into tabs.
+ -l num keep (at least) num lines in memory, 128 are kept by default
+
+In the 32V source code to col(1) the default behavior was to NOT compress
+spaces into tabs. There was a -h option which caused it to compress spaces
+into tabs. There was no -x flag.
+
+The 32V documentation, however, was consistent with the SVID (actually, V7
+at the time) and documented a -x flag (as defined above) while making no
+mention of a -h flag. Just before 4.3BSD went out, CSRG updated the manual
+page to reflect the way the code worked. Suspecting that this was probably
+the wrong way to go, this version adopts the SVID defaults, and no longer
+documents the -h option.
+
+The S5 -p flag is not supported because it isn't clear what it does (looks
+like a kludge introduced for a particular printer).
+
+Known differences between AT&T's col and this one (# is delimiter):
+ Input AT&T col this col
+ #\nabc\E7def\n# # def\nabc\r# # def\nabc\n#
+ #a# ## #a\n#
+ - last line always ends with at least one \n (or \E9)
+ #1234567 8\n# #1234567\t8\n# #1234567 8\n#
+ - single space not expanded to tab
+ -f #a\E8b\n# #ab\n# # b\E9\ra\n#
+ - can back up past first line (as far as you want) so you
+ *can* have a super script on the first line
+ #\E9_\ba\E8\nb\n# #\n_\bb\ba\n# #\n_\ba\bb\n#
+ - always print last character written to a position,
+ AT&T col claims to do this but doesn't.
+
+If a character is to be placed on a line that has been flushed, a warning
+is produced (the AT&T col is silent). The -l flag (not in AT&T col) can
+be used to increase the number of lines buffered to avoid the problem.
+
+General algorithm: a limited number of lines are buffered in a linked
+list. When a printable character is read, it is put in the buffer of
+the current line along with the column it's supposed to be in. When
+a line is flushed, the characters in the line are sorted according to
+column and then printed.
diff --git a/usr.bin/col/col.1 b/usr.bin/col/col.1
new file mode 100644
index 0000000..2a55ef2
--- /dev/null
+++ b/usr.bin/col/col.1
@@ -0,0 +1,126 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Michael Rendell.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)col.1 8.1 (Berkeley) 6/29/93
+.\"
+.Dd June 29, 1993
+.Dt COL 1
+.Os
+.Sh NAME
+.Nm col
+.Nd filter reverse line feeds from input
+.Sh SYNOPSIS
+.Nm col
+.Op Fl bfx
+.Op Fl l Ar num
+.Sh DESCRIPTION
+.Nm Col
+filters out reverse (and half reverse) line feeds so that the output is
+in the correct order with only forward and half forward line
+feeds, and replaces white-space characters with tabs where possible.
+This can be useful in processing the output of
+.Xr nroff 1
+and
+.Xr tbl 1 .
+.Pp
+.Nm Col
+reads from the standard input and writes to the standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width "-l num "
+.It Fl b
+Do not output any backspaces, printing only the last character
+written to each column position.
+.It Fl f
+Forward half line feeds are permitted (``fine'' mode).
+Normally characters printed on a half line boundary are printed
+on the following line.
+.It Fl x
+Output multiple spaces instead of tabs.
+.It Fl l Ar num
+Buffer at least
+.Ar num
+lines in memory.
+By default, 128 lines are buffered.
+.El
+.Pp
+The control sequences for carriage motion that
+.Nm col
+understands and their decimal values are listed in the following
+table:
+.Pp
+.Bl -tag -width "carriage return" -compact
+.It ESC\-7
+reverse line feed (escape then 7)
+.It ESC\-8
+half reverse line feed (escape then 8)
+.It ESC\-9
+half forward line feed (escape then 9)
+.It backspace
+moves back one column (8); ignored in the first column
+.It carriage return
+(13)
+.It newline
+forward line feed (10); also does carriage return
+.It shift in
+shift to normal character set (15)
+.It shift out
+shift to alternate character set (14)
+.It space
+moves forward one column (32)
+.It tab
+moves forward to next tab stop (9)
+.It vertical tab
+reverse line feed (11)
+.El
+.Pp
+All unrecognized control characters and escape sequences are
+discarded.
+.Pp
+.Nm Col
+keeps track of the character set as characters are read and makes
+sure the character set is correct when they are output.
+.Pp
+If the input attempts to back up to the last flushed line,
+.Nm col
+will display a warning message.
+.Sh SEE ALSO
+.Xr expand 1 ,
+.Xr nroff 1 ,
+.Xr tbl 1
+.Sh HISTORY
+A
+.Nm col
+command
+appeared in Version 6 AT&T UNIX.
diff --git a/usr.bin/col/col.c b/usr.bin/col/col.c
new file mode 100644
index 0000000..719fef7
--- /dev/null
+++ b/usr.bin/col/col.c
@@ -0,0 +1,544 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of the Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)col.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BS '\b' /* backspace */
+#define TAB '\t' /* tab */
+#define SPACE ' ' /* space */
+#define NL '\n' /* newline */
+#define CR '\r' /* carriage return */
+#define ESC '\033' /* escape */
+#define SI '\017' /* shift in to normal character set */
+#define SO '\016' /* shift out to alternate character set */
+#define VT '\013' /* vertical tab (aka reverse line feed) */
+#define RLF '\007' /* ESC-07 reverse line feed */
+#define RHLF '\010' /* ESC-010 reverse half-line feed */
+#define FHLF '\011' /* ESC-011 forward half-line feed */
+
+/* build up at least this many lines before flushing them out */
+#define BUFFER_MARGIN 32
+
+typedef char CSET;
+
+typedef struct char_str {
+#define CS_NORMAL 1
+#define CS_ALTERNATE 2
+ short c_column; /* column character is in */
+ CSET c_set; /* character set (currently only 2) */
+ char c_char; /* character in question */
+} CHAR;
+
+typedef struct line_str LINE;
+struct line_str {
+ CHAR *l_line; /* characters on the line */
+ LINE *l_prev; /* previous line */
+ LINE *l_next; /* next line */
+ int l_lsize; /* allocated sizeof l_line */
+ int l_line_len; /* strlen(l_line) */
+ int l_needs_sort; /* set if chars went in out of order */
+ int l_max_col; /* max column in the line */
+};
+
+LINE *alloc_line __P((void));
+void dowarn __P((int));
+void flush_line __P((LINE *));
+void flush_lines __P((int));
+void flush_blanks __P((void));
+void free_line __P((LINE *));
+void usage __P((void));
+void wrerr __P((void));
+void *xmalloc __P((void *, size_t));
+
+CSET last_set; /* char_set of last char printed */
+LINE *lines;
+int compress_spaces; /* if doing space -> tab conversion */
+int fine; /* if `fine' resolution (half lines) */
+int max_bufd_lines; /* max # lines to keep in memory */
+int nblank_lines; /* # blanks after last flushed line */
+int no_backspaces; /* if not to output any backspaces */
+
+#define PUTC(ch) \
+ do { \
+ if (putchar(ch) == EOF) \
+ wrerr(); \
+ } while (0)
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ CHAR *c;
+ CSET cur_set; /* current character set */
+ LINE *l; /* current line */
+ int extra_lines; /* # of lines above first line */
+ int cur_col; /* current column */
+ int cur_line; /* line number of current position */
+ int max_line; /* max value of cur_line */
+ int this_line; /* line l points to */
+ int nflushd_lines; /* number of lines that were flushed */
+ int adjust, opt, warned;
+
+ max_bufd_lines = 128;
+ compress_spaces = 1; /* compress spaces into tabs */
+ while ((opt = getopt(argc, argv, "bfhl:x")) != EOF)
+ switch (opt) {
+ case 'b': /* do not output backspaces */
+ no_backspaces = 1;
+ break;
+ case 'f': /* allow half forward line feeds */
+ fine = 1;
+ break;
+ case 'h': /* compress spaces into tabs */
+ compress_spaces = 1;
+ break;
+ case 'l': /* buffered line count */
+ if ((max_bufd_lines = atoi(optarg)) <= 0) {
+ (void)fprintf(stderr,
+ "col: bad -l argument %s.\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'x': /* do not compress spaces into tabs */
+ compress_spaces = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (optind != argc)
+ usage();
+
+ /* this value is in half lines */
+ max_bufd_lines *= 2;
+
+ adjust = cur_col = extra_lines = warned = 0;
+ cur_line = max_line = nflushd_lines = this_line = 0;
+ cur_set = last_set = CS_NORMAL;
+ lines = l = alloc_line();
+
+ while ((ch = getchar()) != EOF) {
+ if (!isgraph(ch)) {
+ switch (ch) {
+ case BS: /* can't go back further */
+ if (cur_col == 0)
+ continue;
+ --cur_col;
+ continue;
+ case CR:
+ cur_col = 0;
+ continue;
+ case ESC: /* just ignore EOF */
+ switch(getchar()) {
+ case RLF:
+ cur_line -= 2;
+ break;
+ case RHLF:
+ cur_line--;
+ break;
+ case FHLF:
+ cur_line++;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ }
+ continue;
+ case NL:
+ cur_line += 2;
+ if (cur_line > max_line)
+ max_line = cur_line;
+ cur_col = 0;
+ continue;
+ case SPACE:
+ ++cur_col;
+ continue;
+ case SI:
+ cur_set = CS_NORMAL;
+ continue;
+ case SO:
+ cur_set = CS_ALTERNATE;
+ continue;
+ case TAB: /* adjust column */
+ cur_col |= 7;
+ ++cur_col;
+ continue;
+ case VT:
+ cur_line -= 2;
+ continue;
+ }
+ continue;
+ }
+
+ /* Must stuff ch in a line - are we at the right one? */
+ if (cur_line != this_line - adjust) {
+ LINE *lnew;
+ int nmove;
+
+ adjust = 0;
+ nmove = cur_line - this_line;
+ if (!fine) {
+ /* round up to next line */
+ if (cur_line & 1) {
+ adjust = 1;
+ nmove++;
+ }
+ }
+ if (nmove < 0) {
+ for (; nmove < 0 && l->l_prev; nmove++)
+ l = l->l_prev;
+ if (nmove) {
+ if (nflushd_lines == 0) {
+ /*
+ * Allow backup past first
+ * line if nothing has been
+ * flushed yet.
+ */
+ for (; nmove < 0; nmove++) {
+ lnew = alloc_line();
+ l->l_prev = lnew;
+ lnew->l_next = l;
+ l = lines = lnew;
+ extra_lines++;
+ }
+ } else {
+ if (!warned++)
+ dowarn(cur_line);
+ cur_line -= nmove;
+ }
+ }
+ } else {
+ /* may need to allocate here */
+ for (; nmove > 0 && l->l_next; nmove--)
+ l = l->l_next;
+ for (; nmove > 0; nmove--) {
+ lnew = alloc_line();
+ lnew->l_prev = l;
+ l->l_next = lnew;
+ l = lnew;
+ }
+ }
+ this_line = cur_line + adjust;
+ nmove = this_line - nflushd_lines;
+ if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
+ nflushd_lines += nmove - max_bufd_lines;
+ flush_lines(nmove - max_bufd_lines);
+ }
+ }
+ /* grow line's buffer? */
+ if (l->l_line_len + 1 >= l->l_lsize) {
+ int need;
+
+ need = l->l_lsize ? l->l_lsize * 2 : 90;
+ l->l_line = (CHAR *)xmalloc((void *) l->l_line,
+ (unsigned) need * sizeof(CHAR));
+ l->l_lsize = need;
+ }
+ c = &l->l_line[l->l_line_len++];
+ c->c_char = ch;
+ c->c_set = cur_set;
+ c->c_column = cur_col;
+ /*
+ * If things are put in out of order, they will need sorting
+ * when it is flushed.
+ */
+ if (cur_col < l->l_max_col)
+ l->l_needs_sort = 1;
+ else
+ l->l_max_col = cur_col;
+ cur_col++;
+ }
+ /* goto the last line that had a character on it */
+ for (; l->l_next; l = l->l_next)
+ this_line++;
+ flush_lines(this_line - nflushd_lines + extra_lines + 1);
+
+ /* make sure we leave things in a sane state */
+ if (last_set != CS_NORMAL)
+ PUTC('\017');
+
+ /* flush out the last few blank lines */
+ nblank_lines = max_line - this_line;
+ if (max_line & 1)
+ nblank_lines++;
+ else if (!nblank_lines)
+ /* missing a \n on the last line? */
+ nblank_lines = 2;
+ flush_blanks();
+ exit(0);
+}
+
+void
+flush_lines(nflush)
+ int nflush;
+{
+ LINE *l;
+
+ while (--nflush >= 0) {
+ l = lines;
+ lines = l->l_next;
+ if (l->l_line) {
+ flush_blanks();
+ flush_line(l);
+ }
+ nblank_lines++;
+ if (l->l_line)
+ (void)free((void *)l->l_line);
+ free_line(l);
+ }
+ if (lines)
+ lines->l_prev = NULL;
+}
+
+/*
+ * Print a number of newline/half newlines. If fine flag is set, nblank_lines
+ * is the number of half line feeds, otherwise it is the number of whole line
+ * feeds.
+ */
+void
+flush_blanks()
+{
+ int half, i, nb;
+
+ half = 0;
+ nb = nblank_lines;
+ if (nb & 1) {
+ if (fine)
+ half = 1;
+ else
+ nb++;
+ }
+ nb /= 2;
+ for (i = nb; --i >= 0;)
+ PUTC('\n');
+ if (half) {
+ PUTC('\033');
+ PUTC('9');
+ if (!nb)
+ PUTC('\r');
+ }
+ nblank_lines = 0;
+}
+
+/*
+ * Write a line to stdout taking care of space to tab conversion (-h flag)
+ * and character set shifts.
+ */
+void
+flush_line(l)
+ LINE *l;
+{
+ CHAR *c, *endc;
+ int nchars, last_col, this_col;
+
+ last_col = 0;
+ nchars = l->l_line_len;
+
+ if (l->l_needs_sort) {
+ static CHAR *sorted;
+ static int count_size, *count, i, save, sorted_size, tot;
+
+ /*
+ * Do an O(n) sort on l->l_line by column being careful to
+ * preserve the order of characters in the same column.
+ */
+ if (l->l_lsize > sorted_size) {
+ sorted_size = l->l_lsize;
+ sorted = (CHAR *)xmalloc((void *)sorted,
+ (unsigned)sizeof(CHAR) * sorted_size);
+ }
+ if (l->l_max_col >= count_size) {
+ count_size = l->l_max_col + 1;
+ count = (int *)xmalloc((void *)count,
+ (unsigned)sizeof(int) * count_size);
+ }
+ memset((char *)count, 0, sizeof(int) * l->l_max_col + 1);
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ count[c->c_column]++;
+
+ /*
+ * calculate running total (shifted down by 1) to use as
+ * indices into new line.
+ */
+ for (tot = 0, i = 0; i <= l->l_max_col; i++) {
+ save = count[i];
+ count[i] = tot;
+ tot += save;
+ }
+
+ for (i = nchars, c = l->l_line; --i >= 0; c++)
+ sorted[count[c->c_column]++] = *c;
+ c = sorted;
+ } else
+ c = l->l_line;
+ while (nchars > 0) {
+ this_col = c->c_column;
+ endc = c;
+ do {
+ ++endc;
+ } while (--nchars > 0 && this_col == endc->c_column);
+
+ /* if -b only print last character */
+ if (no_backspaces)
+ c = endc - 1;
+
+ if (this_col > last_col) {
+ int nspace = this_col - last_col;
+
+ if (compress_spaces && nspace > 1) {
+ while (1) {
+ int tab_col, tab_size;;
+
+ tab_col = (last_col + 8) & ~7;
+ if (tab_col > this_col)
+ break;
+ tab_size = tab_col - last_col;
+ if (tab_size == 1)
+ PUTC(' ');
+ else
+ PUTC('\t');
+ nspace -= tab_size;
+ last_col = tab_col;
+ }
+ }
+ while (--nspace >= 0)
+ PUTC(' ');
+ last_col = this_col;
+ }
+ last_col++;
+
+ for (;;) {
+ if (c->c_set != last_set) {
+ switch (c->c_set) {
+ case CS_NORMAL:
+ PUTC('\017');
+ break;
+ case CS_ALTERNATE:
+ PUTC('\016');
+ }
+ last_set = c->c_set;
+ }
+ PUTC(c->c_char);
+ if (++c >= endc)
+ break;
+ PUTC('\b');
+ }
+ }
+}
+
+#define NALLOC 64
+
+static LINE *line_freelist;
+
+LINE *
+alloc_line()
+{
+ LINE *l;
+ int i;
+
+ if (!line_freelist) {
+ l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC);
+ line_freelist = l;
+ for (i = 1; i < NALLOC; i++, l++)
+ l->l_next = l + 1;
+ l->l_next = NULL;
+ }
+ l = line_freelist;
+ line_freelist = l->l_next;
+
+ memset(l, 0, sizeof(LINE));
+ return (l);
+}
+
+void
+free_line(l)
+ LINE *l;
+{
+
+ l->l_next = line_freelist;
+ line_freelist = l;
+}
+
+void *
+xmalloc(p, size)
+ void *p;
+ size_t size;
+{
+
+ if (!(p = (void *)realloc(p, size)))
+ err(1, NULL);
+ return (p);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: col [-bfx] [-l nline]\n");
+ exit(1);
+}
+
+void
+wrerr()
+{
+
+ (void)fprintf(stderr, "col: write error.\n");
+ exit(1);
+}
+
+void
+dowarn(line)
+ int line;
+{
+
+ warnx("warning: can't back up %s",
+ line < 0 ? "past first line" : "-- line already flushed");
+}
diff --git a/usr.bin/colcrt/Makefile b/usr.bin/colcrt/Makefile
new file mode 100644
index 0000000..e03ab0c
--- /dev/null
+++ b/usr.bin/colcrt/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= colcrt
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/colcrt/colcrt.1 b/usr.bin/colcrt/colcrt.1
new file mode 100644
index 0000000..a9447af
--- /dev/null
+++ b/usr.bin/colcrt/colcrt.1
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)colcrt.1 8.1 (Berkeley) 6/30/93
+.\"
+.Dd June 30, 1993
+.Dt COLCRT 1
+.Os BSD 3
+.Sh NAME
+.Nm colcrt
+.Nd filter nroff output for CRT previewing
+.Sh SYNOPSIS
+.Nm colcrt
+.Op Fl
+.Op Fl \&2
+.Op Ar
+.Sh DESCRIPTION
+.Nm Colcrt
+provides virtual half-line and reverse line feed sequences
+for terminals without such capability, and on which overstriking
+is destructive.
+Half-line characters and underlining (changed to dashing `\-')
+are placed on new lines in between the normal output lines.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl
+Suppress all underlining.
+This option is especially useful for previewing
+.Em allboxed
+tables from
+.Xr tbl 1 .
+.It Fl 2
+Causes all half-lines to be printed, effectively double spacing the output.
+Normally, a minimal space output format is used which will suppress empty
+lines.
+The program never suppresses two consecutive empty lines, however.
+The
+.Fl 2
+option is useful for sending output to the line printer when the output
+contains superscripts and subscripts which would otherwise be invisible.
+.El
+.Sh EXAMPLES
+A typical use of
+.Nm colcrt
+would be
+.Bd -literal
+tbl exum2.n \&| nroff \-ms \&| colcrt \- \&| more
+.Ed
+.Sh SEE ALSO
+.Xr nroff 1 ,
+.Xr troff 1 ,
+.Xr col 1 ,
+.Xr more 1 ,
+.Xr ul 1
+.Sh BUGS
+Should fold underlines onto blanks even with the
+.Ql Fl
+option so that
+a true underline character would show.
+.Pp
+Can't back up more than 102 lines.
+.Pp
+General overstriking is lost;
+as a special case
+.Ql \&|
+overstruck with
+.Ql \-
+or underline becomes
+.Ql \&+ .
+.Pp
+Lines are trimmed to 132 characters.
+.Pp
+Some provision should be made for processing superscripts and subscripts
+in documents which are already double-spaced.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/colcrt/colcrt.c b/usr.bin/colcrt/colcrt.c
new file mode 100644
index 0000000..9e6136b
--- /dev/null
+++ b/usr.bin/colcrt/colcrt.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)colcrt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+/*
+ * colcrt - replaces col for crts with new nroff esp. when using tbl.
+ * Bill Joy UCB July 14, 1977
+ *
+ * This filter uses a screen buffer, 267 half-lines by 132 columns.
+ * It interprets the up and down sequences generated by the new
+ * nroff when used with tbl and by \u \d and \r.
+ * General overstriking doesn't work correctly.
+ * Underlining is split onto multiple lines, etc.
+ *
+ * Option - suppresses all underlining.
+ * Option -2 forces printing of all half lines.
+ */
+
+char page[267][132];
+
+int outline = 1;
+int outcol;
+
+char suppresul;
+char printall;
+
+char *progname;
+FILE *f;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register c;
+ register char *cp, *dp;
+
+ argc--;
+ progname = *argv++;
+ while (argc > 0 && argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 0:
+ suppresul = 1;
+ break;
+ case '2':
+ printall = 1;
+ break;
+ default:
+ printf("usage: %s [ - ] [ -2 ] [ file ... ]\n", progname);
+ fflush(stdout);
+ exit(1);
+ }
+ argc--;
+ argv++;
+ }
+ do {
+ if (argc > 0) {
+ close(0);
+ if (!(f = fopen(argv[0], "r"))) {
+ fflush(stdout);
+ perror(argv[0]);
+ exit (1);
+ }
+ argc--;
+ argv++;
+ }
+ for (;;) {
+ c = getc(stdin);
+ if (c == -1) {
+ pflush(outline);
+ fflush(stdout);
+ break;
+ }
+ switch (c) {
+ case '\n':
+ if (outline >= 265)
+ pflush(62);
+ outline += 2;
+ outcol = 0;
+ continue;
+ case '\016':
+ case '\017':
+ continue;
+ case 033:
+ c = getc(stdin);
+ switch (c) {
+ case '9':
+ if (outline >= 266)
+ pflush(62);
+ outline++;
+ continue;
+ case '8':
+ if (outline >= 1)
+ outline--;
+ continue;
+ case '7':
+ outline -= 2;
+ if (outline < 0)
+ outline = 0;
+ continue;
+ default:
+ continue;
+ }
+ case '\b':
+ if (outcol)
+ outcol--;
+ continue;
+ case '\t':
+ outcol += 8;
+ outcol &= ~7;
+ outcol--;
+ c = ' ';
+ default:
+ if (outcol >= 132) {
+ outcol++;
+ continue;
+ }
+ cp = &page[outline][outcol];
+ outcol++;
+ if (c == '_') {
+ if (suppresul)
+ continue;
+ cp += 132;
+ c = '-';
+ }
+ if (*cp == 0) {
+ *cp = c;
+ dp = cp - outcol;
+ for (cp--; cp >= dp && *cp == 0; cp--)
+ *cp = ' ';
+ } else
+ if (plus(c, *cp) || plus(*cp, c))
+ *cp = '+';
+ else if (*cp == ' ' || *cp == 0)
+ *cp = c;
+ continue;
+ }
+ }
+ } while (argc > 0);
+ fflush(stdout);
+ exit(0);
+}
+
+plus(c, d)
+ char c, d;
+{
+
+ return (c == '|' && d == '-' || d == '_');
+}
+
+int first;
+
+pflush(ol)
+ int ol;
+{
+ register int i, j;
+ register char *cp;
+ char lastomit;
+ int l;
+
+ l = ol;
+ lastomit = 0;
+ if (l > 266)
+ l = 266;
+ else
+ l |= 1;
+ for (i = first | 1; i < l; i++) {
+ move(i, i - 1);
+ move(i, i + 1);
+ }
+ for (i = first; i < l; i++) {
+ cp = page[i];
+ if (printall == 0 && lastomit == 0 && *cp == 0) {
+ lastomit = 1;
+ continue;
+ }
+ lastomit = 0;
+ printf("%s\n", cp);
+ }
+ bcopy(page[ol], page, (267 - ol) * 132);
+ bzero(page[267- ol], ol * 132);
+ outline -= ol;
+ outcol = 0;
+ first = 1;
+}
+
+move(l, m)
+ int l, m;
+{
+ register char *cp, *dp;
+
+ for (cp = page[l], dp = page[m]; *cp; cp++, dp++) {
+ switch (*cp) {
+ case '|':
+ if (*dp != ' ' && *dp != '|' && *dp != 0)
+ return;
+ break;
+ case ' ':
+ break;
+ default:
+ return;
+ }
+ }
+ if (*cp == 0) {
+ for (cp = page[l], dp = page[m]; *cp; cp++, dp++)
+ if (*cp == '|')
+ *dp = '|';
+ else if (*dp == 0)
+ *dp = ' ';
+ page[l][0] = 0;
+ }
+}
diff --git a/usr.bin/colldef/Makefile b/usr.bin/colldef/Makefile
new file mode 100644
index 0000000..9edfdb9
--- /dev/null
+++ b/usr.bin/colldef/Makefile
@@ -0,0 +1,31 @@
+# $Id: Makefile,v 1.4 1995/09/28 18:23:34 bde Exp $
+
+PROG = colldef
+LFLAGS = -8 -i
+YFLAGS = -d
+CFLAGS += -I. -I${.CURDIR}/../../lib/libc/locale
+SRCS = parse.c scan.c
+LDADD = -ll
+DPADD = ${LIBL}
+CLEANFILES += lex.yy.c parse.c scan.c y.tab.[ch]
+
+LOCALES= ru_SU.KOI8-R ru_SU.CP866 lt_LN.ISO_8859-1
+LOCALEDIR= ${DESTDIR}/usr/share/locale
+
+LATIN1LINKS = \
+ da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES fi_FI \
+ fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT nl_BE nl_NL no_NO \
+ pt_PT sv_SE
+
+afterinstall:
+ for l in ${LOCALES}; do \
+ colldef -o ${LOCALEDIR}/$$l/LC_COLLATE ${.CURDIR}/data/$$l; \
+ chown ${BINOWN}.${BINGRP} ${LOCALEDIR}/$$l/LC_COLLATE; \
+ chmod 644 ${LOCALEDIR}/$$l/LC_COLLATE; \
+ done
+ for l in ${LATIN1LINKS}; do \
+ ln -fs ../lt_LN.ISO_8859-1/LC_COLLATE \
+ ${LOCALEDIR}/$$l.ISO_8859-1/LC_COLLATE; \
+ done
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/colldef/colldef.1 b/usr.bin/colldef/colldef.1
new file mode 100644
index 0000000..5cb3b3a
--- /dev/null
+++ b/usr.bin/colldef/colldef.1
@@ -0,0 +1,194 @@
+.\" Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
+.\" at Electronni Visti IA, Kiev, Ukraine.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January, 27 1995
+.Dt COLLDEF 1
+.Os
+.Sh NAME
+.Nm colldef
+.Nd convert collation sequence source definition
+.Sh SYNOPSIS
+.Nm colldef
+.Ar [-o out_file] [filename]
+.Sh DESCRIPTION
+.Ar colldef
+converts a collation sequence source definition
+into a format usable by the
+.Fn strxfrm
+and
+.Fn strcoll
+functions. It is used to define the many ways in which
+strings can be ordered and collated.
+.Fn strxfrm
+transforms
+its first argument and places the result in its second
+argument. The transformed string is such that it can be
+correctly ordered with other transformed strings by using
+.Fn strcmp ,
+.Fn strncmp ,
+or
+.Fn memcmp .
+.Fn strcoll
+transforms its arguments and does a
+comparison.
+.Pp
+.Ar colldef
+reads the collation sequence source definition
+from the standard input and stores the converted definition in filename.
+The output file produced contains the
+database with collating sequence information in a form
+usable by system commands and routines.
+.Pp
+The collation sequence definition specifies a set of collating elements and
+the rules defining how strings containing these should be ordered.
+This is most useful for different language definitions.
+.Pp
+The specification file can consist of three statements:
+.Ar charmap
+,
+.Ar substitute
+, and
+.Ar order
+. Of these, only the order
+statement is required. When charmap or substitute is
+supplied, these statements must be ordered as above. Any
+statements after the order statement are ignored.
+.Pp
+Lines in the specification file beginning with a # are
+treated as comments and are ignored. Blank lines are also
+ignored.
+.Pp
+.Ar charmap charmapfile
+
+.Ar charmap
+defines where a mapping of the character
+and collating element symbols to the actual
+character encoding can be found.
+.Pp
+The format of
+.Ar charmapfile
+is shown below. Symbol
+names are separated from their values by TAB or
+SPACE characters. symbol-value can be specified in
+a hexadecimal (\ex??) or octal (\e???)
+representation, and can be only one character in length.
+.Pp
+.Ar symbol-name1 symbol-value1
+
+.Ar symbol-name2 symbol-value2
+
+.Ar ...
+
+.Pp
+Symbol names cannot be specified in substitute
+fields. Symbol names also cannot be combined with
+any other representation, such as, <c>h, c<h>,
+<c>\ex68, or <c><h>. Symbol names can be used with
+primary and secondary ordering as in the following
+example.
+.Pp
+The charmap statement is optional.
+.Pp
+.Ar substitute char with repl
+The
+.Ar substitute
+statement substitutes the character
+
+.Ar char
+with the string
+.Ar repl
+.
+.Pp
+The substitute statement is optional.
+.Pp
+.Ar order order_list
+
+.Ar order_list
+is a list of symbols, separated by semi colons, that defines the collating sequence. The
+special symbol,
+.Ar ...
+, specifies, in a short-hand
+form, symbols that are sequential in machine code
+order.
+.Pp
+A symbol can be up to two characters in length and
+can be represented in any one of the following
+ways:
+.Bl -tag -width XX
+.It o The symbol itself (for example,
+.Ar a
+for the lower-case letter
+.Ar a
+).
+.It o The symbol chain (for example,
+.Ar abc
+)
+.It o In octal representation (for example,
+.Ar \e141
+for the letter
+.Ar a
+).
+.It o In hexadecimal representation (for example,
+.Ar \ex61
+for the letter
+.Ar a
+).
+.It o The symbol name as defined in the charmap file (for example,
+.Ar <abc>
+for
+.Ar \e023 abc
+record in
+.Ar charmapfile
+).
+.It o Symbols
+.Ar \ea, \eb, \ef, \en, \er, \ev
+are permitted in its usual C-language meaning.
+.El
+.Pp
+The backslash character,
+.Ar \e
+, is used for continuation. In this case, no characters are permitted
+after the backslash character. And as a quotation mark.
+.Pp
+Symbols enclosed in parentheses are assigned the
+same primary ordering but different secondary
+ordering. Symbols enclosed in curly brackets are
+assigned only the same primary ordering
+and different secondary ordering.
+.Sh EXIT STATUS
+.Ar colldef
+exits with the following values:
+.Ar 0
+No errors were found and the output was successfully created.
+.Ar !=0
+Errors were found.
+.Sh FILES
+.Ar /usr/share/locale/<language>/LC_COLLATE
+standard shared location for collation orders
+under the locale locale
+.Sh SEE ALSO
+.Xr mklocale 1 ,
+.Xr strcoll 3 ,
+.Xr strxfrm 3
diff --git a/usr.bin/colldef/data/lt_LN.ISO8859-1 b/usr.bin/colldef/data/lt_LN.ISO8859-1
new file mode 100644
index 0000000..f63cfed
--- /dev/null
+++ b/usr.bin/colldef/data/lt_LN.ISO8859-1
@@ -0,0 +1,19 @@
+order \
+# controls
+ \x01;...;\x1f;\x7f;...;\x9f;\
+# spaces
+ \ ;\xa0;\
+# puncts
+ .;:;\;;\,;!;?;\xa1;\xbf;\
+# digits
+ 0;...;9;\
+# alphas
+ a;\xe0;...;\xe5;A;\xc0;...;\xc5;b;B;c;\xe7;C;\xc7;d;D;e;\xe8;...;\xeb;\
+ E;\xc8;...;\xcb;f;F;g;G;h;H;i;\xec;...;\xef;I;\xcc;...;\xcf;j;J;k;K;\
+ l;L;m;M;n;\xf1;N;\xd1;o;\xf2;...;\xf6;\xf8;O;\xd2;...;\xd6;\xd8;\
+ p;P;q;Q;r;R;s;S;t;T;u;\xf9;...;\xfc;U;\xd9;...;\xdc;v;V;w;W;x;X;y;\
+ \xfd;Y;\xdd;z;Z;\xf0;\xd0;\xfe;\xde;\xe6;\xc6;\xdf;\xff;\
+# remains
+ \";...;&;\
+ \(;...;+;/;\<;...;\>;@;[;...;`;\{;...;~;\xa2;...;\xbe;\
+ \xd7;\xf7
diff --git a/usr.bin/colldef/data/lt_LN.ISO_8859-1 b/usr.bin/colldef/data/lt_LN.ISO_8859-1
new file mode 100644
index 0000000..f63cfed
--- /dev/null
+++ b/usr.bin/colldef/data/lt_LN.ISO_8859-1
@@ -0,0 +1,19 @@
+order \
+# controls
+ \x01;...;\x1f;\x7f;...;\x9f;\
+# spaces
+ \ ;\xa0;\
+# puncts
+ .;:;\;;\,;!;?;\xa1;\xbf;\
+# digits
+ 0;...;9;\
+# alphas
+ a;\xe0;...;\xe5;A;\xc0;...;\xc5;b;B;c;\xe7;C;\xc7;d;D;e;\xe8;...;\xeb;\
+ E;\xc8;...;\xcb;f;F;g;G;h;H;i;\xec;...;\xef;I;\xcc;...;\xcf;j;J;k;K;\
+ l;L;m;M;n;\xf1;N;\xd1;o;\xf2;...;\xf6;\xf8;O;\xd2;...;\xd6;\xd8;\
+ p;P;q;Q;r;R;s;S;t;T;u;\xf9;...;\xfc;U;\xd9;...;\xdc;v;V;w;W;x;X;y;\
+ \xfd;Y;\xdd;z;Z;\xf0;\xd0;\xfe;\xde;\xe6;\xc6;\xdf;\xff;\
+# remains
+ \";...;&;\
+ \(;...;+;/;\<;...;\>;@;[;...;`;\{;...;~;\xa2;...;\xbe;\
+ \xd7;\xf7
diff --git a/usr.bin/colldef/data/ru_SU.CP866 b/usr.bin/colldef/data/ru_SU.CP866
new file mode 100644
index 0000000..4a6d583
--- /dev/null
+++ b/usr.bin/colldef/data/ru_SU.CP866
@@ -0,0 +1,20 @@
+# cp866
+order \
+# controls
+ \x01;...;\x1f;\x7f;\
+# spaces
+ \ ;\xff;\
+# puncts
+ .;:;\;;\,;!;?;\
+# digits
+ 0;...;9;\
+# alphas
+ A;a;B;b;C;c;D;d;E;e;F;f;G;g;H;h;I;i;J;j;K;k;L;l;\
+ M;m;N;n;O;o;P;p;Q;q;R;r;S;s;T;t;U;u;V;v;W;w;X;x;\
+ Y;y;Z;z;\
+ €; ;;¡;‚;¢;ƒ;£;„;¤;…;¥;ð;ñ;†;¦;‡;§;ˆ;¨;‰;©;Š;ª;\
+ ‹;«;Œ;¬;;­;Ž;®;;¯;;à;‘;á;’;â;“;ã;”;ä;•;å;–;æ;\
+ —;ç;˜;è;™;é;š;ê;›;ë;œ;ì;;í;ž;î;Ÿ;ï;\
+# remains
+ \";...;&;\(;...;+;/;\<;...;\>;@;[;...;`;\{;...;~;\
+ \xb0;...;\xdf;\xf2;...;\xfe
diff --git a/usr.bin/colldef/data/ru_SU.KOI8-R b/usr.bin/colldef/data/ru_SU.KOI8-R
new file mode 100644
index 0000000..c1f1544
--- /dev/null
+++ b/usr.bin/colldef/data/ru_SU.KOI8-R
@@ -0,0 +1,20 @@
+# koi8-r
+order \
+# controls
+ \x01;...;\x1f;\x7f;\
+# spaces
+ \ ;\x9a;\
+# puncts
+ .;:;\;;\,;!;?;\
+# digits
+ 0;...;9;\
+# alphas
+ A;a;B;b;C;c;D;d;E;e;F;f;G;g;H;h;I;i;J;j;K;k;L;l;\
+ M;m;N;n;O;o;P;p;Q;q;R;r;S;s;T;t;U;u;V;v;W;w;X;x;\
+ Y;y;Z;z;\
+ á;Á;â;Â;÷;×;ç;Ç;ä;Ä;å;Å;³;£;ö;Ö;ú;Ú;é;É;ê;Ê;ë;Ë;\
+ ì;Ì;í;Í;î;Î;ï;Ï;ð;Ð;ò;Ò;ó;Ó;ô;Ô;õ;Õ;æ;Æ;è;È;ã;Ã;\
+ þ;Þ;û;Û;ý;Ý;ÿ;ß;ù;Ù;ø;Ø;ü;Ü;à;À;ñ;Ñ;\
+# remains
+ \";...;&;\(;...;+;/;\<;...;\>;@;[;...;`;\{;...;~;\
+ \x80;...;\x99;\x9b;...;\xa2;\xa4;...;\xb2;\xb4;...;\xbf
diff --git a/usr.bin/colldef/parse.y b/usr.bin/colldef/parse.y
new file mode 100644
index 0000000..47f3cd5
--- /dev/null
+++ b/usr.bin/colldef/parse.y
@@ -0,0 +1,248 @@
+%{
+/*-
+ * Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
+ * at Electronni Visti IA, Kiev, Ukraine.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: parse.y,v 1.2 1995/01/24 11:15:47 alex Exp alex $
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include "collate.h"
+
+extern int line_no;
+extern FILE *yyin;
+
+u_char __collate_charmap_table[UCHAR_MAX + 1][STR_LEN];
+u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
+struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
+struct __collate_st_name_pri __collate_name_pri_table[TABLE_SIZE];
+struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
+int name_index, chain_index;
+int prim_pri = 1, sec_pri = 1;
+#ifdef COLLATE_DEBUG
+int debug;
+#endif
+
+char *out_file = "LC_COLLATE";
+%}
+%union {
+ u_char ch;
+ u_char str[STR_LEN];
+}
+%token SUBSTITUTE WITH ORDER RANGE
+%token <str> STRING
+%token <str> NAME
+%token <str> CHAIN
+%token <ch> CHAR
+%%
+collate : statment_list
+;
+statment_list : statment
+ | statment_list '\n' statment
+;
+statment :
+ | charmap
+ | substitute
+ | order
+;
+charmap : CHAIN CHAR {
+ strcpy(__collate_charmap_table[$2], $1);
+}
+ | CHAR CHAR {
+ __collate_charmap_table[$2][0] = $1;
+ __collate_charmap_table[$2][1] = '\0';
+}
+;
+substitute : SUBSTITUTE STRING WITH STRING {
+ strcpy(__collate_substitute_table[$2[0]], $4);
+}
+;
+order : ORDER order_list {
+ FILE *fp = fopen(out_file, "w");
+
+ if(!fp)
+ err(EX_UNAVAILABLE, "con't open destination file %s",
+ out_file);
+
+ fwrite(__collate_charmap_table, sizeof(__collate_charmap_table), 1, fp);
+ fwrite(__collate_substitute_table, sizeof(__collate_substitute_table), 1, fp);
+ fwrite(__collate_char_pri_table, sizeof(__collate_char_pri_table), 1, fp);
+ fwrite(__collate_chain_pri_table, sizeof(__collate_chain_pri_table), 1, fp);
+ fwrite(__collate_name_pri_table, sizeof(__collate_name_pri_table), 1, fp);
+#ifdef COLLATE_DEBUG
+ if (debug)
+ __collate_print_tables();
+#endif
+ exit(EX_OK);
+}
+;
+order_list : item
+ | order_list ';' item
+;
+item : CHAR { __collate_char_pri_table[$1].prim = prim_pri++; }
+ | CHAIN {
+ if (chain_index >= TABLE_SIZE - 1)
+ yyerror("__collate_chain_pri_table overflow");
+ strcpy(__collate_chain_pri_table[chain_index].str, $1);
+ __collate_chain_pri_table[chain_index++].prim = prim_pri++;
+}
+ | NAME {
+ if (name_index >= TABLE_SIZE - 1)
+ yyerror("__collate_name_pri_table overflow");
+ strcpy(__collate_name_pri_table[name_index].str, $1);
+ __collate_name_pri_table[name_index++].prim = prim_pri++;
+}
+ | CHAR RANGE CHAR {
+ u_int i;
+
+ if ($3 <= $1)
+ yyerror("Illegal range %c -- %c near line %d\n",
+ $1, $3, line_no);
+
+ for (i = $1; i <= $3; i++) {
+ __collate_char_pri_table[(u_char)i].prim = prim_pri++;
+ }
+}
+ | '{' prim_order_list '}' {
+ prim_pri++;
+}
+ | '(' sec_order_list ')' {
+ prim_pri++;
+ sec_pri = 1;
+}
+;
+prim_order_list : prim_sub_item
+ | prim_order_list ',' prim_sub_item
+;
+sec_order_list : sec_sub_item
+ | sec_order_list ',' sec_sub_item
+;
+prim_sub_item : CHAR {
+ __collate_char_pri_table[$1].prim = prim_pri;
+}
+ | CHAR RANGE CHAR {
+ u_int i;
+
+ if ($3 <= $1)
+ yyerror("Illegal range %c -- %c near line %d\n",
+ $1, $3, line_no);
+
+ for (i = $1; i <= $3; i++) {
+ __collate_char_pri_table[(u_char)i].prim = prim_pri;
+ }
+}
+ | NAME {
+ if (name_index >= TABLE_SIZE - 1)
+ yyerror("__collate_name_pri_table overflow");
+ strcpy(__collate_name_pri_table[name_index].str, $1);
+ __collate_name_pri_table[name_index++].prim = prim_pri;
+}
+ | CHAIN {
+ if (chain_index >= TABLE_SIZE - 1)
+ yyerror("__collate_chain_pri_table overflow");
+ strcpy(__collate_chain_pri_table[chain_index].str, $1);
+ __collate_chain_pri_table[chain_index++].prim = prim_pri;
+}
+;
+sec_sub_item : CHAR {
+ __collate_char_pri_table[$1].prim = prim_pri;
+ __collate_char_pri_table[$1].sec = sec_pri++;
+}
+ | CHAR RANGE CHAR {
+ u_int i;
+
+ if ($3 <= $1)
+ yyerror("Illegal range %c -- %c near line %d\n",
+ $1, $3, line_no);
+
+ for (i = $1; i <= $3; i++) {
+ __collate_char_pri_table[(u_char)i].prim = prim_pri;
+ __collate_char_pri_table[(u_char)i].sec = sec_pri++;
+ }
+}
+ | NAME {
+ if (name_index >= TABLE_SIZE - 1)
+ yyerror("__collate_name_pri_table overflow");
+ strcpy(__collate_name_pri_table[name_index].str, $1);
+ __collate_name_pri_table[name_index].prim = prim_pri;
+ __collate_name_pri_table[name_index++].sec = sec_pri++;
+}
+ | CHAIN {
+ if (chain_index >= TABLE_SIZE - 1)
+ yyerror("__collate_chain_pri_table overflow");
+ strcpy(__collate_chain_pri_table[chain_index].str, $1);
+ __collate_chain_pri_table[chain_index].prim = prim_pri;
+ __collate_chain_pri_table[chain_index++].sec = sec_pri++;
+}
+;
+%%
+main(ac, av)
+ char **av;
+{
+ int ch;
+
+#ifdef COLLATE_DEBUG
+ while((ch = getopt(ac, av, ":do:")) != EOF) {
+#else
+ while((ch = getopt(ac, av, ":o:")) != EOF) {
+#endif
+ switch (ch)
+ {
+#ifdef COLLATE_DEBUG
+ case 'd':
+ debug++;
+ break;
+#endif
+ case 'o':
+ out_file = optarg;
+ break;
+
+ default:
+ fprintf(stderr, "Usage: %s [-o out_file] [in_file]\n",
+ av[0]);
+ exit(EX_OK);
+ }
+ }
+ ac -= optind;
+ av += optind;
+ if(ac > 0) {
+ if((yyin = fopen(*av, "r")) == 0)
+ err(EX_UNAVAILABLE, "can't open source file %s", *av);
+ }
+ for(ch = 0; ch <= UCHAR_MAX; ch++)
+ __collate_substitute_table[ch][0] = ch;
+ yyparse();
+ return 0;
+}
+
+yyerror(msg)
+ char *msg;
+{
+ errx(EX_UNAVAILABLE, "%s near line %d", msg, line_no);
+}
diff --git a/usr.bin/colldef/scan.l b/usr.bin/colldef/scan.l
new file mode 100644
index 0000000..3be0463
--- /dev/null
+++ b/usr.bin/colldef/scan.l
@@ -0,0 +1,212 @@
+%x string name charmap
+%{
+/*-
+ * Copyright (c) 1995 Alex Tatmanjants <alex@elvisti.kiev.ua>
+ * at Electronni Visti IA, Kiev, Ukraine.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: scan.l,v 1.1 1995/01/22 20:37:31 alex Exp alex $
+ */
+
+#include <err.h>
+#include <unistd.h>
+#include <string.h>
+#include <sysexits.h>
+#include "collate.h"
+#include "y.tab.h"
+
+int line_no = 1;
+u_char buf[STR_LEN], *ptr;
+FILE *map_fp;
+YY_BUFFER_STATE main_buf, map_buf;
+#ifdef FLEX_DEBUG
+YYSTYPE yylval;
+#endif /* FLEX_DEBUG */
+%}
+%%
+<INITIAL,charmap>[ \t] ;
+\" { ptr = buf; BEGIN(string); }
+\< { ptr = buf; BEGIN(name); }
+^#.*\n line_no++;
+^\n line_no++;
+\\\n line_no++;
+\\t { yylval.ch = '\t'; return CHAR; }
+\\n { yylval.ch = '\n'; return CHAR; }
+\\b { yylval.ch = '\b'; return CHAR; }
+\\f { yylval.ch = '\f'; return CHAR; }
+\\v { yylval.ch = '\v'; return CHAR; }
+\\r { yylval.ch = '\r'; return CHAR; }
+\\a { yylval.ch = '\a'; return CHAR; }
+\\. { yylval.ch = yytext[1]; return CHAR; }
+<INITIAL,charmap>\n { line_no++; return '\n'; }
+[;,{}()] return *yytext;
+substitute return SUBSTITUTE;
+with return WITH;
+order return ORDER;
+charmap BEGIN(charmap);
+;[ \t]*\.\.\.[ \t]*; return RANGE;
+\\[0-7]{3} {
+ u_int v;
+
+ sscanf(&yytext[1], "%o", &v);
+ yylval.ch = (u_char)v;
+ return CHAR;
+}
+\\x[0-9a-z]{2} {
+ u_int v;
+
+ sscanf(&yytext[2], "%x", &v);
+ yylval.ch = (u_char)v;
+ return CHAR;
+}
+[^;,{}() \t\n"<]+ {
+ if(yyleng == 1) {
+ yylval.ch = *yytext;
+ return CHAR;
+ }
+ if(yyleng > STR_LEN - 1)
+ errx(EX_UNAVAILABLE, "chain buffer overflaw near line %u",
+ line_no);
+ strcpy(yylval.str, yytext);
+ return CHAIN;
+}
+<name>\\\> {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '>';
+}
+<string>\\\" {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '"';
+}
+<name>\> {
+ *ptr = '\0';
+ strcpy(yylval.str, buf);
+ BEGIN(INITIAL);
+ return NAME;
+}
+<string>\" {
+ *ptr = '\0';
+ strcpy(yylval.str, buf);
+ BEGIN(INITIAL);
+ return STRING;
+}
+<name,string>. {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = *yytext;
+}
+<name,string>\\t {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\t';
+}
+<name,string>\\b {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\b';
+}
+<name,string>\\f {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\f';
+}
+<name,string>\\v {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\v';
+}
+<name,string>\\n {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\n';
+}
+<name,string>\\r {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\r';
+}
+<name,string>\\a {
+ if(ptr >= buf + sizeof(buf) - 1)
+ errx(EX_UNAVAILABLE, "name/string buffer overflaw near line %u",
+ line_no);
+ *ptr++ = '\a';
+}
+<name,string><<EOF>> {
+ errx(EX_UNAVAILABLE, "unterminated name/string near line %u", line_no);
+}
+<name,string>\\x[0-9a-f]{2} {
+ u_int v;
+
+ sscanf(&yytext[2], "%x", &v);
+ *ptr++ = (u_char)v;
+}
+<name,string>\\[0-7]{3} {
+ u_int v;
+
+ sscanf(&yytext[1], "%o", &v);
+ *ptr++ = (u_char)v;
+}
+<charmap>[^ \t\n]+ {
+ if((map_fp = fopen(yytext, "r")) == 0)
+ err(EX_UNAVAILABLE, "can't open charmap file %s near line %u",
+ yytext, line_no);
+ map_buf = yy_new_buffer(map_fp, YY_BUF_SIZE);
+ main_buf = YY_CURRENT_BUFFER;
+ yy_switch_to_buffer(map_buf);
+ BEGIN(INITIAL);
+}
+<charmap><<EOF>> {
+ errx(EX_UNAVAILABLE, "charmap file name expected near line %u",
+ line_no);
+}
+<<EOF>> {
+ if(map_fp) {
+ yy_switch_to_buffer(main_buf);
+ yy_delete_buffer(map_buf);
+ fclose(map_fp);
+ map_fp = 0;
+ }
+ else
+ yyterminate();
+}
+%%
+#ifdef FLEX_DEBUG
+main()
+{
+ while(yylex())
+ ;
+ return 0;
+}
+#endif /* FLEX_DEBUG */
diff --git a/usr.bin/colrm/Makefile b/usr.bin/colrm/Makefile
new file mode 100644
index 0000000..b9c8341
--- /dev/null
+++ b/usr.bin/colrm/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= colrm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/colrm/colrm.1 b/usr.bin/colrm/colrm.1
new file mode 100644
index 0000000..5700b54
--- /dev/null
+++ b/usr.bin/colrm/colrm.1
@@ -0,0 +1,78 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)colrm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt COLRM 1
+.Os BSD 3
+.Sh NAME
+.Nm colrm
+.Nd remove columns from a file
+.Sh SYNOPSIS
+.Nm colrm
+.Op Ar start Op Ar stop
+.Sh DESCRIPTION
+.Nm Colrm
+removes selected columns from the lines of a file.
+A column is defined as a single character in a line.
+Input is read from the standard input.
+Output is written to the standard output.
+.Pp
+If only the
+.Ar start
+column is specified, columns numbered less than the
+.Ar start
+column will be written.
+If both
+.Ar start
+and
+.Ar stop
+columns are specified, columns numbered less than the
+.Ar start
+column
+or greater than the
+.Ar stop
+column will be written.
+Column numbering starts with one, not zero.
+.Pp
+Tab characters increment the column count to the next multiple of eight.
+Backspace characters decrement the column count by one.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr column 1 ,
+.Xr cut 1 ,
+.Xr paste 1
+.Sh HISTORY
+The
+.Nm colrm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/colrm/colrm.c b/usr.bin/colrm/colrm.c
new file mode 100644
index 0000000..a7c7a65
--- /dev/null
+++ b/usr.bin/colrm/colrm.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)colrm.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TAB 8
+
+void err __P((const char *, ...));
+void check __P((FILE *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register u_long column, start, stop;
+ register int ch;
+ char *p;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ start = stop = 0;
+ switch(argc) {
+ case 2:
+ stop = strtol(argv[1], &p, 10);
+ if (stop <= 0 || *p)
+ err("illegal column -- %s", argv[1]);
+ /* FALLTHROUGH */
+ case 1:
+ start = strtol(argv[0], &p, 10);
+ if (start <= 0 || *p)
+ err("illegal column -- %s", argv[0]);
+ break;
+ case 0:
+ break;
+ default:
+ usage();
+ }
+
+ if (stop && start > stop)
+ err("illegal start and stop columns");
+
+ for (column = 0;;) {
+ switch (ch = getchar()) {
+ case EOF:
+ check(stdin);
+ break;
+ case '\b':
+ if (column)
+ --column;
+ break;
+ case '\n':
+ column = 0;
+ break;
+ case '\t':
+ column = (column + TAB) & ~(TAB - 1);
+ break;
+ default:
+ ++column;
+ break;
+ }
+
+ if ((!start || column < start || stop && column > stop) &&
+ putchar(ch) == EOF)
+ check(stdout);
+ }
+}
+
+void
+check(stream)
+ FILE *stream;
+{
+ if (feof(stream))
+ exit(0);
+ if (ferror(stream))
+ err("%s: %s",
+ stream == stdin ? "stdin" : "stdout", strerror(errno));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: colrm [start [stop]]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "colrm: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/column/Makefile b/usr.bin/column/Makefile
new file mode 100644
index 0000000..1c304e2
--- /dev/null
+++ b/usr.bin/column/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= column
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/column/column.1 b/usr.bin/column/column.1
new file mode 100644
index 0000000..05d6a0f
--- /dev/null
+++ b/usr.bin/column/column.1
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)column.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Os
+.Dt COLUMN 1
+.Sh NAME
+.Nm column
+.Nd columnate lists
+.Sh SYNOPSIS
+.Nm column
+.Op Fl tx
+.Op Fl c Ar columns
+.Op Fl s Ar sep
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm column
+utility formats its input into multiple columns.
+Rows are filled before columns.
+Input is taken from
+.Ar file
+operands, or, by default, from the standard input.
+Empty lines are ignored.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Output is formatted for a display
+.Ar columns
+wide.
+.It Fl s
+Specify a set of characters to be used to delimit columns for the
+.Fl t
+option.
+.It Fl t
+Determine the number of columns the input contains and create a table.
+Columns are delimited with whitespace, by default, or with the characters
+supplied using the
+.Fl s
+option.
+Useful for pretty-printing displays.
+.It Fl x
+Fill columns before filling rows.
+.El
+.Pp
+.Nm Column
+exits 0 on success, >0 if an error occurred.
+.Sh ENVIRONMENT
+.Bl -tag -width COLUMNS
+.It Ev COLUMNS
+The environment variable
+.Ev COLUMNS
+is used to determine the size of
+the screen if no other information is available.
+.El
+.Sh EXAMPLES
+.Dl (printf \&"PERM LINKS OWNER SIZE MONTH DAY HH:MM/YEAR NAME\en\&"\ \&;\ \&\e
+.Dl ls -l \&| sed 1d) \&| column -t
+.Sh SEE ALSO
+.Xr colrm 1 ,
+.Xr ls 1 ,
+.Xr paste 1 ,
+.Xr sort 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 Reno .
diff --git a/usr.bin/column/column.c b/usr.bin/column/column.c
new file mode 100644
index 0000000..d3f71c5
--- /dev/null
+++ b/usr.bin/column/column.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)column.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void c_columnate __P((void));
+void *emalloc __P((int));
+void input __P((FILE *));
+void maketbl __P((void));
+void print __P((void));
+void r_columnate __P((void));
+void usage __P((void));
+
+int termwidth = 80; /* default terminal width */
+
+int entries; /* number of records */
+int eval; /* exit value */
+int maxlength; /* longest record */
+char **list; /* array of pointers to records */
+char *separator = "\t "; /* field separator for table option */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct winsize win;
+ FILE *fp;
+ int ch, tflag, xflag;
+ char *p;
+
+ if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
+ if (p = getenv("COLUMNS"))
+ termwidth = atoi(p);
+ } else
+ termwidth = win.ws_col;
+
+ tflag = xflag = 0;
+ while ((ch = getopt(argc, argv, "c:s:tx")) != EOF)
+ switch(ch) {
+ case 'c':
+ termwidth = atoi(optarg);
+ break;
+ case 's':
+ separator = optarg;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ input(stdin);
+ else for (; *argv; ++argv)
+ if (fp = fopen(*argv, "r")) {
+ input(fp);
+ (void)fclose(fp);
+ } else {
+ warn("%s", *argv);
+ eval = 1;
+ }
+
+ if (!entries)
+ exit(eval);
+
+ if (tflag)
+ maketbl();
+ else if (maxlength >= termwidth)
+ print();
+ else if (xflag)
+ c_columnate();
+ else
+ r_columnate();
+ exit(eval);
+}
+
+#define TAB 8
+void
+c_columnate()
+{
+ int chcnt, col, cnt, endcol, numcols;
+ char **lp;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ endcol = maxlength;
+ for (chcnt = col = 0, lp = list;; ++lp) {
+ chcnt += printf("%s", *lp);
+ if (!--entries)
+ break;
+ if (++col == numcols) {
+ chcnt = col = 0;
+ endcol = maxlength;
+ putchar('\n');
+ } else {
+ while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+ (void)putchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ }
+ if (chcnt)
+ putchar('\n');
+}
+
+void
+r_columnate()
+{
+ int base, chcnt, cnt, col, endcol, numcols, numrows, row;
+
+ maxlength = (maxlength + TAB) & ~(TAB - 1);
+ numcols = termwidth / maxlength;
+ numrows = entries / numcols;
+ if (entries % numcols)
+ ++numrows;
+
+ for (row = 0; row < numrows; ++row) {
+ endcol = maxlength;
+ for (base = row, chcnt = col = 0; col < numcols; ++col) {
+ chcnt += printf("%s", list[base]);
+ if ((base += numrows) >= entries)
+ break;
+ while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+ (void)putchar('\t');
+ chcnt = cnt;
+ }
+ endcol += maxlength;
+ }
+ putchar('\n');
+ }
+}
+
+void
+print()
+{
+ int cnt;
+ char **lp;
+
+ for (cnt = entries, lp = list; cnt--; ++lp)
+ (void)printf("%s\n", *lp);
+}
+
+typedef struct _tbl {
+ char **list;
+ int cols, *len;
+} TBL;
+#define DEFCOLS 25
+
+void
+maketbl()
+{
+ TBL *t;
+ int coloff, cnt;
+ char *p, **lp;
+ int *lens, maxcols;
+ TBL *tbl;
+ char **cols;
+
+ t = tbl = emalloc(entries * sizeof(TBL));
+ cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
+ lens = emalloc(maxcols * sizeof(int));
+ for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
+ for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
+ p = NULL)
+ if (++coloff == maxcols) {
+ if (!(cols = realloc(cols, (u_int)maxcols +
+ DEFCOLS * sizeof(char *))) ||
+ !(lens = realloc(lens,
+ (u_int)maxcols + DEFCOLS * sizeof(int))))
+ err(1, NULL);
+ memset((char *)lens + maxcols * sizeof(int),
+ 0, DEFCOLS * sizeof(int));
+ maxcols += DEFCOLS;
+ }
+ t->list = emalloc(coloff * sizeof(char *));
+ t->len = emalloc(coloff * sizeof(int));
+ for (t->cols = coloff; --coloff >= 0;) {
+ t->list[coloff] = cols[coloff];
+ t->len[coloff] = strlen(cols[coloff]);
+ if (t->len[coloff] > lens[coloff])
+ lens[coloff] = t->len[coloff];
+ }
+ }
+ for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
+ for (coloff = 0; coloff < t->cols - 1; ++coloff)
+ (void)printf("%s%*s", t->list[coloff],
+ lens[coloff] - t->len[coloff] + 2, " ");
+ (void)printf("%s\n", t->list[coloff]);
+ }
+}
+
+#define DEFNUM 1000
+#define MAXLINELEN (LINE_MAX + 1)
+
+void
+input(fp)
+ FILE *fp;
+{
+ static int maxentry;
+ int len;
+ char *p, buf[MAXLINELEN];
+
+ if (!list)
+ list = emalloc((maxentry = DEFNUM) * sizeof(char *));
+ while (fgets(buf, MAXLINELEN, fp)) {
+ for (p = buf; *p && isspace(*p); ++p);
+ if (!*p)
+ continue;
+ if (!(p = strchr(p, '\n'))) {
+ warnx("line too long");
+ eval = 1;
+ continue;
+ }
+ *p = '\0';
+ len = p - buf;
+ if (maxlength < len)
+ maxlength = len;
+ if (entries == maxentry) {
+ maxentry += DEFNUM;
+ if (!(list = realloc(list,
+ (u_int)maxentry * sizeof(char *))))
+ err(1, NULL);
+ }
+ list[entries++] = strdup(buf);
+ }
+}
+
+void *
+emalloc(size)
+ int size;
+{
+ char *p;
+
+ if (!(p = malloc(size)))
+ err(1, NULL);
+ memset(p, 0, size);
+ return (p);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: column [-tx] [-c columns] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/comm/Makefile b/usr.bin/comm/Makefile
new file mode 100644
index 0000000..6942023
--- /dev/null
+++ b/usr.bin/comm/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= comm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/comm/comm.1 b/usr.bin/comm/comm.1
new file mode 100644
index 0000000..0897430
--- /dev/null
+++ b/usr.bin/comm/comm.1
@@ -0,0 +1,94 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)comm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Os
+.Dt COMM 1
+.Sh NAME
+.Nm comm
+.Nd select or reject lines common to two files
+.Sh SYNOPSIS
+.Nm comm
+.Op Fl 123
+.Ar file1 file2
+.Sh DESCRIPTION
+The
+.Nm comm
+utility reads
+.Ar file1
+and
+.Ar file2 ,
+which should be
+sorted lexically, and produces three text
+columns as output: lines only in
+.Ar file1 ;
+lines only in
+.Ar file2 ;
+and lines in both files.
+.Pp
+The filename ``-'' means the standard input.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl 1
+Suppress printing of column 1.
+.It Fl 2
+Suppress printing of column 2.
+.It Fl 3
+Suppress printing of column 3.
+.El
+.Pp
+Each column will have a number of tab characters prepended to it
+equal to the number of lower numbered columns that are being printed.
+For example, if column number two is being suppressed, lines printed
+in column number one will not have any tabs preceding them, and lines
+printed in column number three will have one.
+.Pp
+.Nm Comm
+assumes that the files are lexically sorted; all characters
+participate in line comparisons.
+.Pp
+.Nm Comm
+exits 0 on success, >0 if an error occurred.
+.Sh SEE ALSO
+.Xr cmp 1 ,
+.Xr diff 1 ,
+.Xr sort 1 ,
+.Xr uniq 1
+.Sh STANDARDS
+The
+.Nm comm
+command is expected to be POSIX 1003.2 compatible.
diff --git a/usr.bin/comm/comm.c b/usr.bin/comm/comm.c
new file mode 100644
index 0000000..8ffbbb5
--- /dev/null
+++ b/usr.bin/comm/comm.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)comm.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXLINELEN (LINE_MAX + 1)
+
+char *tabs[] = { "", "\t", "\t\t" };
+
+FILE *file __P((char *));
+void show __P((FILE *, char *, char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int comp, file1done, file2done, read1, read2;
+ int ch, flag1, flag2, flag3;
+ FILE *fp1, *fp2;
+ char *col1, *col2, *col3;
+ char **p, line1[MAXLINELEN], line2[MAXLINELEN];
+
+ flag1 = flag2 = flag3 = 1;
+ while ((ch = getopt(argc, argv, "-123")) != EOF)
+ switch(ch) {
+ case '-':
+ --optind;
+ goto done;
+ case '1':
+ flag1 = 0;
+ break;
+ case '2':
+ flag2 = 0;
+ break;
+ case '3':
+ flag3 = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+done: argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ fp1 = file(argv[0]);
+ fp2 = file(argv[1]);
+
+ /* for each column printed, add another tab offset */
+ p = tabs;
+ col1 = col2 = col3 = NULL;
+ if (flag1)
+ col1 = *p++;
+ if (flag2)
+ col2 = *p++;
+ if (flag3)
+ col3 = *p;
+
+ for (read1 = read2 = 1;;) {
+ /* read next line, check for EOF */
+ if (read1)
+ file1done = !fgets(line1, MAXLINELEN, fp1);
+ if (read2)
+ file2done = !fgets(line2, MAXLINELEN, fp2);
+
+ /* if one file done, display the rest of the other file */
+ if (file1done) {
+ if (!file2done && col2)
+ show(fp2, col2, line2);
+ break;
+ }
+ if (file2done) {
+ if (!file1done && col1)
+ show(fp1, col1, line1);
+ break;
+ }
+
+ /* lines are the same */
+ if (!(comp = strcmp(line1, line2))) {
+ read1 = read2 = 1;
+ if (col3)
+ (void)printf("%s%s", col3, line1);
+ continue;
+ }
+
+ /* lines are different */
+ if (comp < 0) {
+ read1 = 1;
+ read2 = 0;
+ if (col1)
+ (void)printf("%s%s", col1, line1);
+ } else {
+ read1 = 0;
+ read2 = 1;
+ if (col2)
+ (void)printf("%s%s", col2, line2);
+ }
+ }
+ exit(0);
+}
+
+void
+show(fp, offset, buf)
+ FILE *fp;
+ char *offset, *buf;
+{
+
+ do {
+ (void)printf("%s%s", offset, buf);
+ } while (fgets(buf, MAXLINELEN, fp));
+}
+
+FILE *
+file(name)
+ char *name;
+{
+ FILE *fp;
+
+ if (!strcmp(name, "-"))
+ return (stdin);
+ if ((fp = fopen(name, "r")) == NULL) {
+ (void)fprintf(stderr, "comm: %s: %s\n", name, strerror(errno));
+ exit(1);
+ }
+ return (fp);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: comm [-123] file1 file2\n");
+ exit(1);
+}
diff --git a/usr.bin/compile_et/Makefile b/usr.bin/compile_et/Makefile
new file mode 100644
index 0000000..b03ff78
--- /dev/null
+++ b/usr.bin/compile_et/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.4 1995/01/14 22:30:32 wollman Exp $
+
+PROG= compile_et
+SRCS= compile_et.c error_table.y
+CFLAGS+= -I. -I${.CURDIR}/../../lib/libcom_err
+CLEANFILES+= et_lex.lex.c y.tab.h
+DPADD+= ${LIBL}
+LDADD+= -ll
+
+beforedepend: et_lex.lex.c
+error_table.o: et_lex.lex.c
+
+.l.c:
+ ${LEX} -l -t ${.IMPSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/compile_et/compile_et.1 b/usr.bin/compile_et/compile_et.1
new file mode 100644
index 0000000..f17a278
--- /dev/null
+++ b/usr.bin/compile_et/compile_et.1
@@ -0,0 +1,79 @@
+.\" Copyright (c) 1988 Massachusetts Institute of Technology,
+.\" Student Information Processing Board. All rights reserved.
+.\"
+.\" $Header$
+.\"
+.TH COMPILE_ET 1 "22 Nov 1988" SIPB
+.SH NAME
+compile_et \- error table compiler
+.SH SYNOPSIS
+.B compile_et
+file
+.SH DESCRIPTION
+.B Compile_et
+converts a table listing error-code names and associated messages into
+a C source file suitable for use with the
+.IR com_err (3)
+library.
+
+The source file name must end with a suffix of ``.et''; the file
+consists of a declaration supplying the name (up to four characters
+long) of the error-code table:
+
+.B error_table
+.I name
+
+followed by up to 256 entries of the form:
+
+.B error_code
+.I name,
+"
+.I string
+"
+
+and a final
+
+.B end
+
+to indicate the end of the table.
+
+The name of the table is used to construct the name of a subroutine
+.I initialize_XXXX_error_table
+which must be called in order for the
+.I com_err
+library to recognize the error table.
+
+The various error codes defined are assigned sequentially increasing
+numbers (starting with a large number computed as a hash function of
+the name of the table); thus for compatibility it is suggested that
+new codes be added only to the end of an existing table, and that no
+codes be removed from tables.
+
+The names defined in the table are placed into a C header file with
+preprocessor directives defining them as integer constants of up to
+32 bits in magnitude.
+
+A C source file is also generated which should be compiled and linked
+with the object files which reference these error codes; it contains
+the text of the messages and the initialization subroutine. Both C
+files have names derived from that of the original source file, with
+the ``.et'' suffix replaced by ``.c'' and ``.h''.
+
+A ``#'' in the source file is treated as a comment character, and all
+remaining text to the end of the source line will be ignored.
+
+.SH BUGS
+
+Since
+.B compile_et
+uses a very simple parser based on
+.IR yacc (1),
+its error recovery leaves much to be desired.
+
+.\" .IR for manual entries
+.\" .PP for paragraph breaks
+
+.SH "SEE ALSO"
+com_err (3).
+
+Ken Raeburn, "A Common Error Description Library for UNIX".
diff --git a/usr.bin/compile_et/compile_et.c b/usr.bin/compile_et/compile_et.c
new file mode 100644
index 0000000..8e3f492
--- /dev/null
+++ b/usr.bin/compile_et/compile_et.c
@@ -0,0 +1,290 @@
+/*
+ *
+ * Copyright 1986, 1987, 1988
+ * by MIT Student Information Processing Board.
+ *
+ * For copyright info, see "mit-sipb-copyright.h".
+ *
+ */
+
+#include <stdio.h>
+#include <sys/file.h>
+#include <string.h>
+#include <sys/param.h>
+#include "mit-sipb-copyright.h"
+#include "compiler.h"
+
+#ifndef __STDC__
+#define const
+#endif
+
+#ifndef lint
+static const char copyright[] =
+ "Copyright 1987,1988 by MIT Student Information Processing Board";
+
+static const char rcsid_compile_et_c[] =
+ "$Header: /afs/rel-eng.athena.mit.edu/project/release/current/source/athena/athena.lib/et/RCS/compile_et.c,v 1.3 91/02/28 15:15:23 epeisach Exp $";
+#endif
+
+extern char *gensym();
+extern char *current_token;
+extern int table_number, current;
+char buffer[BUFSIZ];
+char *table_name = (char *)NULL;
+FILE *hfile, *cfile;
+
+/* C library */
+extern char *malloc();
+extern int errno;
+
+/* lex stuff */
+extern FILE *yyin;
+extern int yylineno;
+
+char * xmalloc (size) unsigned int size; {
+ char * p = malloc (size);
+ if (!p) {
+ perror (whoami);
+ exit (1);
+ }
+ return p;
+}
+
+static int check_arg (str_list, arg) char const *const *str_list, *arg; {
+ while (*str_list)
+ if (!strcmp(arg, *str_list++))
+ return 1;
+ return 0;
+}
+
+static const char *const debug_args[] = {
+ "d",
+ "debug",
+ 0,
+};
+
+static const char *const lang_args[] = {
+ "lang",
+ "language",
+ 0,
+};
+
+static const char *const language_names[] = {
+ "C",
+ "K&R C",
+ "C++",
+ 0,
+};
+
+static const char * const c_src_prolog[] = {
+ "static const char * const text[] = {\n",
+ 0,
+};
+
+static const char * const krc_src_prolog[] = {
+ "#ifdef __STDC__\n",
+ "#define NOARGS void\n",
+ "#else\n",
+ "#define NOARGS\n",
+ "#define const\n",
+ "#endif\n\n",
+ "static const char * const text[] = {\n",
+ 0,
+};
+
+static const char *const struct_def[] = {
+ "struct error_table {\n",
+ " char const * const * msgs;\n",
+ " long base;\n",
+ " int n_msgs;\n",
+ "};\n",
+ "struct et_list {\n",
+ " struct et_list *next;\n",
+ " const struct error_table * table;\n",
+ "};\n",
+ "extern struct et_list *_et_list;\n",
+ "\n", 0,
+};
+
+static const char warning[] =
+ "/*\n * %s:\n * This file is automatically generated; please do not edit it.\n */\n";
+
+/* pathnames */
+char c_file[MAXPATHLEN]; /* output file */
+char h_file[MAXPATHLEN]; /* output */
+
+static void usage () {
+ fprintf (stderr, "%s: usage: %s ERROR_TABLE\n",
+ whoami, whoami);
+ exit (1);
+}
+
+static void dup_err (type, one, two) char const *type, *one, *two; {
+ fprintf (stderr, "%s: multiple %s specified: `%s' and `%s'\n",
+ whoami, type, one, two);
+ usage ();
+}
+
+int main (argc, argv) int argc; char **argv; {
+ char *p, *ename;
+ int len;
+ char const * const *cpp;
+ int got_language = 0;
+
+ /* argument parsing */
+ debug = 0;
+ filename = 0;
+ whoami = argv[0];
+ p = strrchr (whoami, '/');
+ if (p)
+ whoami = p+1;
+ while (argv++, --argc) {
+ char *arg = *argv;
+ if (arg[0] != '-') {
+ if (filename)
+ dup_err ("filenames", filename, arg);
+ filename = arg;
+ }
+ else {
+ arg++;
+ if (check_arg (debug_args, arg))
+ debug++;
+ else if (check_arg (lang_args, arg)) {
+ got_language++;
+ arg = *++argv, argc--;
+ if (!arg)
+ usage ();
+ if (language)
+ dup_err ("languanges", language_names[(int)language], arg);
+#define check_lang(x,v) else if (!strcasecmp(arg,x)) language = v
+ check_lang ("c", lang_C);
+ check_lang ("ansi_c", lang_C);
+ check_lang ("ansi-c", lang_C);
+ check_lang ("krc", lang_KRC);
+ check_lang ("kr_c", lang_KRC);
+ check_lang ("kr-c", lang_KRC);
+ check_lang ("k&r-c", lang_KRC);
+ check_lang ("k&r_c", lang_KRC);
+ check_lang ("c++", lang_CPP);
+ check_lang ("cplusplus", lang_CPP);
+ check_lang ("c-plus-plus", lang_CPP);
+#undef check_lang
+ else {
+ fprintf (stderr, "%s: unknown language name `%s'\n",
+ whoami, arg);
+ fprintf (stderr, "\tpick one of: C K&R-C\n");
+ exit (1);
+ }
+ }
+ else {
+ fprintf (stderr, "%s: unknown control argument -`%s'\n",
+ whoami, arg);
+ usage ();
+ }
+ }
+ }
+ if (!filename)
+ usage ();
+ if (!got_language)
+ language = lang_KRC;
+ else if (language == lang_CPP) {
+ fprintf (stderr, "%s: Sorry, C++ support is not yet finished.\n",
+ whoami);
+ exit (1);
+ }
+
+ p = xmalloc (strlen (filename) + 5);
+ strcpy (p, filename);
+ filename = p;
+ p = strrchr(filename, '/');
+ if (p == (char *)NULL)
+ p = filename;
+ else
+ p++;
+ ename = p;
+ len = strlen (ename);
+ p += len - 3;
+ if (strcmp (p, ".et"))
+ p += 3;
+ *p++ = '.';
+ /* now p points to where "et" suffix should start */
+ /* generate new filenames */
+ strcpy (p, "c");
+ strcpy (c_file, ename);
+ *p = 'h';
+ strcpy (h_file, ename);
+ strcpy (p, "et");
+
+ yyin = fopen(filename, "r");
+ if (!yyin) {
+ perror(filename);
+ exit(1);
+ }
+
+ hfile = fopen(h_file, "w");
+ if (hfile == (FILE *)NULL) {
+ perror(h_file);
+ exit(1);
+ }
+ fprintf (hfile, warning, h_file);
+
+ cfile = fopen(c_file, "w");
+ if (cfile == (FILE *)NULL) {
+ perror(c_file);
+ exit(1);
+ }
+ fprintf (cfile, warning, c_file);
+
+ /* prologue */
+ if (language == lang_C)
+ cpp = c_src_prolog;
+ else if (language == lang_KRC)
+ cpp = krc_src_prolog;
+ else
+ abort ();
+ while (*cpp)
+ fputs (*cpp++, cfile);
+
+ /* parse it */
+ yyparse();
+ fclose(yyin); /* bye bye input file */
+
+ fputs (" 0\n};\n\n", cfile);
+ for (cpp = struct_def; *cpp; cpp++)
+ fputs (*cpp, cfile);
+ fprintf(cfile,
+ "static const struct error_table et = { text, %ldL, %d };\n\n",
+ table_number, current);
+ fputs("static struct et_list link = { 0, 0 };\n\n",
+ cfile);
+ fprintf(cfile, "void initialize_%s_error_table (%s) {\n",
+ table_name, (language == lang_C) ? "void" : "NOARGS");
+ fputs(" if (!link.table) {\n", cfile);
+ fputs(" link.next = _et_list;\n", cfile);
+ fputs(" link.table = &et;\n", cfile);
+ fputs(" _et_list = &link;\n", cfile);
+ fputs(" }\n", cfile);
+ fputs("}\n", cfile);
+ fclose(cfile);
+
+ fprintf (hfile, "extern void initialize_%s_error_table ();\n",
+ table_name);
+ fprintf (hfile, "#define ERROR_TABLE_BASE_%s (%ldL)\n",
+ table_name, table_number);
+ /* compatibility... */
+ fprintf (hfile, "\n/* for compatibility with older versions... */\n");
+ fprintf (hfile, "#define init_%s_err_tbl initialize_%s_error_table\n",
+ table_name, table_name);
+ fprintf (hfile, "#define %s_err_base ERROR_TABLE_BASE_%s\n", table_name,
+ table_name);
+ fclose(hfile); /* bye bye include file */
+
+ return 0;
+}
+
+int yyerror(s) char *s; {
+ fputs(s, stderr);
+ fprintf(stderr, "\nLine number %d; last token was '%s'\n",
+ yylineno, current_token);
+}
+
diff --git a/usr.bin/compile_et/compiler.h b/usr.bin/compile_et/compiler.h
new file mode 100644
index 0000000..43752e2
--- /dev/null
+++ b/usr.bin/compile_et/compiler.h
@@ -0,0 +1,20 @@
+/*
+ * definitions common to the source files of the error table compiler
+ */
+
+#ifndef __STDC__
+/* loser */
+#undef const
+#define const
+#endif
+
+enum lang {
+ lang_C, /* ANSI C (default) */
+ lang_KRC, /* C: ANSI + K&R */
+ lang_CPP /* C++ */
+};
+
+int debug; /* dump debugging info? */
+char *filename; /* error table source */
+enum lang language;
+const char *whoami;
diff --git a/usr.bin/compile_et/error_message.c b/usr.bin/compile_et/error_message.c
new file mode 100644
index 0000000..92cec57
--- /dev/null
+++ b/usr.bin/compile_et/error_message.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1987 by the Student Information Processing Board
+ * of the Massachusetts Institute of Technology
+ * For copyright info, see "Copyright.SIPB".
+ *
+ * from: error_message.c,v 1.1 86/11/10 21:34:34 spook Exp $
+ * $Id: error_message.c,v 1.3 1994/09/09 21:43:22 g89r4222 Exp $
+ */
+
+#include <stdio.h>
+#include "error_table.h"
+extern int sys_nerr;
+
+static char buffer[25];
+
+char *
+error_message(code)
+ int code;
+{
+ register int offset;
+ register error_table **et;
+ register int table_num;
+ register int div;
+ register char *cp;
+
+ offset = code & ((1<<ERRCODE_RANGE)-1);
+ table_num = code - offset;
+ if ((_et_list == (error_table **)NULL) && table_num)
+ goto oops;
+ if (!table_num) {
+ if (offset < sys_nerr)
+ return(sys_errlist[offset]);
+ else
+ goto oops;
+ }
+ for (et = _et_list; *et != (error_table *)NULL; et++) {
+ if ((*et)->base == table_num) {
+ /* This is the right table */
+ if ((*et)->n_msgs <= offset)
+ goto oops;
+ return((*et)->msgs[offset]);
+ }
+ }
+ oops:
+ cp = buffer;
+ {
+ register char *cp1;
+ for (cp1 = "Unknown code "; *cp1; cp1++, cp++)
+ *cp = *cp1;
+ if (table_num) {
+ for (cp1 = error_table_name(table_num); *cp1; cp1++, cp++)
+ *cp = *cp1;
+ *cp++ = ' ';
+ *cp = '\0';
+ }
+ }
+ div = 1000000000;
+ if (offset == 0) {
+ *cp++ = '0';
+ *cp = '\0';
+ return(buffer);
+ }
+ while (div > offset)
+ div /= 10;
+ do {
+ register int n = offset / div;
+ *cp++ = '0' + n;
+ offset -= n * div;
+ div /= 10;
+ } while (offset && div);
+ while (div) {
+ *cp++ = '0';
+ div /= 10;
+ }
+ *cp = '\0';
+ return(buffer);
+}
diff --git a/usr.bin/compile_et/error_table.h b/usr.bin/compile_et/error_table.h
new file mode 100644
index 0000000..e32ec30
--- /dev/null
+++ b/usr.bin/compile_et/error_table.h
@@ -0,0 +1,17 @@
+#ifndef _ET
+extern int errno;
+typedef struct {
+ char **msgs;
+ int base;
+ int n_msgs;
+} error_table;
+extern error_table **_et_list;
+
+#define ERROR_CODE "int" /* type used for error codes */
+
+#define ERRCODE_RANGE 8 /* # of bits to shift table number */
+#define BITS_PER_CHAR 6 /* # bits to shift per character in name */
+
+extern char *error_table_name();
+#define _ET
+#endif
diff --git a/usr.bin/compile_et/error_table.y b/usr.bin/compile_et/error_table.y
new file mode 100644
index 0000000..cb3c0da
--- /dev/null
+++ b/usr.bin/compile_et/error_table.y
@@ -0,0 +1,233 @@
+%{
+#include <stdio.h>
+char *str_concat(), *ds(), *quote();
+void *malloc(), *realloc();
+char *current_token = (char *)NULL;
+extern char *table_name;
+%}
+%union {
+ char *dynstr;
+}
+
+%token ERROR_TABLE ERROR_CODE_ENTRY END
+%token <dynstr> STRING QUOTED_STRING
+%type <dynstr> ec_name description table_id
+%{
+%}
+%start error_table
+%%
+
+error_table : ERROR_TABLE table_id error_codes END
+ { table_name = ds($2);
+ current_token = table_name;
+ put_ecs(); }
+ ;
+
+table_id : STRING
+ { current_token = $1;
+ set_table_num($1);
+ $$ = $1; }
+ ;
+
+error_codes : error_codes ec_entry
+ | ec_entry
+ ;
+
+ec_entry : ERROR_CODE_ENTRY ec_name ',' description
+ { add_ec($2, $4);
+ free($2);
+ free($4); }
+ | ERROR_CODE_ENTRY ec_name '=' STRING ',' description
+ { add_ec_val($2, $4, $6);
+ free($2);
+ free($4);
+ free($6);
+ }
+ ;
+
+ec_name : STRING
+ { $$ = ds($1);
+ current_token = $$; }
+ ;
+
+description : QUOTED_STRING
+ { $$ = ds($1);
+ current_token = $$; }
+ ;
+
+%%
+/*
+ *
+ * Copyright 1986, 1987 by the MIT Student Information Processing Board
+ *
+ * For copyright info, see mit-sipb-copyright.h.
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "internal.h"
+#include "error_table.h"
+#include "mit-sipb-copyright.h"
+
+#ifndef lint
+static char const rcsid_error_table_y[] =
+ "$Header: /home/ncvs/src/usr.bin/compile_et/error_table.y,v 1.2 1995/01/14 22:29:33 wollman Exp $";
+#endif
+
+void *malloc(), *realloc();
+extern FILE *hfile, *cfile;
+
+static long gensym_n = 0;
+char *
+gensym(x)
+ char const *x;
+{
+ char *symbol;
+ if (!gensym_n) {
+ struct timeval tv;
+ struct timezone tzp;
+ gettimeofday(&tv, &tzp);
+ gensym_n = (tv.tv_sec%10000)*100 + tv.tv_usec/10000;
+ }
+ symbol = malloc(32 * sizeof(char));
+ gensym_n++;
+ sprintf(symbol, "et%ld", gensym_n);
+ return(symbol);
+}
+
+char *
+ds(string)
+ char const *string;
+{
+ char *rv;
+ rv = malloc(strlen(string)+1);
+ strcpy(rv, string);
+ return(rv);
+}
+
+char *
+quote(string)
+ char const *string;
+{
+ char *rv;
+ rv = malloc(strlen(string)+3);
+ strcpy(rv, "\"");
+ strcat(rv, string);
+ strcat(rv, "\"");
+ return(rv);
+}
+
+long table_number;
+int current = 0;
+char **error_codes = (char **)NULL;
+
+add_ec(name, description)
+ char const *name, *description;
+{
+ fprintf(cfile, "\t\"%s\",\n", description);
+ if (error_codes == (char **)NULL) {
+ error_codes = (char **)malloc(sizeof(char *));
+ *error_codes = (char *)NULL;
+ }
+ error_codes = (char **)realloc((char *)error_codes,
+ (current + 2)*sizeof(char *));
+ error_codes[current++] = ds(name);
+ error_codes[current] = (char *)NULL;
+}
+
+add_ec_val(name, val, description)
+ char const *name, *val, *description;
+{
+ const int ncurrent = atoi(val);
+ if (ncurrent < current) {
+ printf("Error code %s (%d) out of order", name,
+ current);
+ return;
+ }
+
+ while (ncurrent > current)
+ fputs("\t(char *)NULL,\n", cfile), current++;
+
+ fprintf(cfile, "\t\"%s\",\n", description);
+ if (error_codes == (char **)NULL) {
+ error_codes = (char **)malloc(sizeof(char *));
+ *error_codes = (char *)NULL;
+ }
+ error_codes = (char **)realloc((char *)error_codes,
+ (current + 2)*sizeof(char *));
+ error_codes[current++] = ds(name);
+ error_codes[current] = (char *)NULL;
+}
+
+put_ecs()
+{
+ int i;
+ for (i = 0; i < current; i++) {
+ if (error_codes[i] != (char *)NULL)
+ fprintf(hfile, "#define %-40s (%ldL)\n",
+ error_codes[i], table_number + i);
+ }
+}
+
+/*
+ * char_to_num -- maps letters and numbers into a small numbering space
+ * uppercase -> 1-26
+ * lowercase -> 27-52
+ * digits -> 53-62
+ * underscore-> 63
+ */
+
+static const char char_set[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
+int char_to_num(c)
+ char c;
+{
+ const char *where;
+ int diff;
+
+ where = strchr (char_set, c);
+ if (where) {
+ diff = where - char_set + 1;
+ assert (diff < (1 << ERRCODE_RANGE));
+ return diff;
+ }
+ else if (isprint (c))
+ fprintf (stderr,
+ "Illegal character `%c' in error table name\n",
+ c);
+ else
+ fprintf (stderr,
+ "Illegal character %03o in error table name\n",
+ c);
+ exit (1);
+}
+
+set_table_num(string)
+ char *string;
+{
+ if (char_to_num (string[0]) > char_to_num ('z')) {
+ fprintf (stderr, "%s%s%s%s",
+ "First character of error table name must be ",
+ "a letter; name ``",
+ string, "'' rejected\n");
+ exit (1);
+ }
+ if (strlen(string) > 4) {
+ fprintf(stderr, "Table name %s too long, truncated ",
+ string);
+ string[4] = '\0';
+ fprintf(stderr, "to %s\n", string);
+ }
+ while (*string != '\0') {
+ table_number = (table_number << BITS_PER_CHAR)
+ + char_to_num(*string);
+ string++;
+ }
+ table_number = table_number << ERRCODE_RANGE;
+}
+
+#include "et_lex.lex.c"
diff --git a/usr.bin/compile_et/et_lex.lex.l b/usr.bin/compile_et/et_lex.lex.l
new file mode 100644
index 0000000..0c848de
--- /dev/null
+++ b/usr.bin/compile_et/et_lex.lex.l
@@ -0,0 +1,26 @@
+PC [^\"]
+AN [A-Z_a-z0-9]
+%%
+
+error_table return ERROR_TABLE;
+et return ERROR_TABLE;
+error_code return ERROR_CODE_ENTRY;
+ec return ERROR_CODE_ENTRY;
+end return END;
+
+[\t\n ] ;
+
+\"{PC}*\" { register char *p; yylval.dynstr = ds(yytext+1);
+ if (p=rindex(yylval.dynstr, '"')) *p='\0';
+ return QUOTED_STRING;
+ }
+
+{AN}* { yylval.dynstr = ds(yytext); return STRING; }
+
+#.*\n ;
+
+. { return (*yytext); }
+%%
+#ifndef lint
+static char rcsid_et_lex_lex_l[] = "$Header: et_lex.lex.l,v 1.3 87/10/31 06:28:05 raeburn Exp $";
+#endif
diff --git a/usr.bin/compile_et/et_name.c b/usr.bin/compile_et/et_name.c
new file mode 100644
index 0000000..98ccb08
--- /dev/null
+++ b/usr.bin/compile_et/et_name.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 1987 by MIT Student Information Processing Board
+ * For copyright info, see Copyright.SIPB.
+ *
+ * $Id: et_name.c,v 1.2 1994/07/19 19:21:27 g89r4222 Exp $
+ */
+
+#include "error_table.h"
+
+static char copyright[] = "Copyright 1987 by MIT Student Information Processing Board";
+
+char *malloc();
+
+char *
+error_table_name(num)
+ int num;
+{
+ register int ch;
+ register int i;
+ register char *buf, *p;
+
+ /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */
+ buf = malloc(5);
+ p = buf;
+ num >>= ERRCODE_RANGE;
+ /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */
+ num &= 077777777;
+ /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */
+ for (i = 0; i < 5; i++) {
+ ch = (num >> 24-6*i) & 077;
+ if (ch == 0)
+ continue;
+ else if (ch < 27)
+ *p++ = ch - 1 + 'A';
+ else if (ch < 53)
+ *p++ = ch - 27 + 'a';
+ else if (ch < 63)
+ *p++ = ch - 53 + '0';
+ else /* ch == 63 */
+ *p++ = '_';
+ }
+ return(buf);
+}
+
diff --git a/usr.bin/compile_et/init_et.c b/usr.bin/compile_et/init_et.c
new file mode 100644
index 0000000..c23facb
--- /dev/null
+++ b/usr.bin/compile_et/init_et.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 1986 by MIT Information Systems and
+ * MIT Student Information Processing Board
+ * For copyright info, see Copyright.SIPB.
+ *
+ * form: init_et.c,v 1.1 86/11/10 21:42:26 spook Exp $
+ * $Id: init_et.c,v 1.2 1994/07/19 19:21:28 g89r4222 Exp $
+ */
+
+#include <stdio.h>
+#include "error_table.h"
+
+static char copyright[] = "Copyright 1987 by MIT Student Information Processing Board";
+
+extern char *malloc(), *realloc();
+
+/* useful */
+typedef error_table *etp;
+typedef etp *etpp;
+
+etpp _et_list = (etpp)NULL;
+static int n_allocated = 0, n_used = 0;
+
+int
+init_error_table(msgs, base, count)
+ char **msgs;
+ register int base;
+ int count;
+{
+ register int i;
+ register etp new_et;
+ register etpp list;
+
+ if (!base || !count || !msgs)
+ return;
+
+ new_et = (etp)malloc(sizeof(error_table));
+ new_et->msgs = msgs;
+ new_et->base = base;
+ new_et->n_msgs= count;
+
+ list = _et_list;
+ if (list == (etpp)NULL) {
+ _et_list = (etpp) malloc(10*sizeof(etp));
+ list = _et_list;
+ if (list == (etpp)NULL)
+ return; /* oops */
+ list[0] = new_et;
+ list[1] = (etp)NULL;
+ n_allocated = 10;
+ n_used = 1;
+ return;
+ }
+ for (i = 0; i < n_used; i++)
+ if (list[i]->base == base)
+ return; /* avoid duplicates */
+ if (n_used+2 > n_allocated) {
+ n_allocated += 10; /* don't re-allocate too often */
+ list = (etpp) realloc((char *)list,
+ (unsigned)n_allocated * sizeof(etp));
+ _et_list = list;
+ if (list == (etpp)NULL)
+ return; /* oops */
+ }
+ list[n_used++] = new_et;
+ list[n_used] = (etp)NULL;
+}
diff --git a/usr.bin/compile_et/mit-sipb-copyright.h b/usr.bin/compile_et/mit-sipb-copyright.h
new file mode 100644
index 0000000..2f7eb29
--- /dev/null
+++ b/usr.bin/compile_et/mit-sipb-copyright.h
@@ -0,0 +1,19 @@
+/*
+
+Copyright 1987, 1988 by the Student Information Processing Board
+ of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+*/
+
diff --git a/usr.bin/compile_et/perror.c b/usr.bin/compile_et/perror.c
new file mode 100644
index 0000000..ef50e07
--- /dev/null
+++ b/usr.bin/compile_et/perror.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1987 by MIT Student Information Processing Board
+ * For copyright info, see Copyright.SIPB
+ *
+ * $Id: perror.c,v 1.2 1994/07/19 19:21:30 g89r4222 Exp $
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include "error_table.h"
+
+typedef int (*int_func)();
+
+#if defined(mips) && defined(ultrix)
+int errno; /* this is needed to keep the loader from complaining */
+#endif
+
+int_func com_err_hook = (int_func) NULL;
+char *error_message();
+
+void
+com_err(whoami, code, message)
+ char *whoami;
+ int code;
+ char *message;
+{
+ struct iovec strings[6];
+
+ if (com_err_hook) {
+ (*com_err_hook)(whoami, code, message);
+ return;
+ }
+
+ strings[0].iov_base = whoami;
+ strings[0].iov_len = strlen(whoami);
+ if (whoami) {
+ strings[1].iov_base = ": ";
+ strings[1].iov_len = 2;
+ } else
+ strings[1].iov_len = 0;
+ if (code) {
+ register char *errmsg = error_message(code);
+ strings[2].iov_base = errmsg;
+ strings[2].iov_len = strlen(errmsg);
+ } else
+ strings[2].iov_len = 0;
+ strings[3].iov_base = " ";
+ strings[3].iov_len = 1;
+ strings[4].iov_base = message;
+ strings[4].iov_len = strlen(message);
+ strings[5].iov_base = "\n";
+ strings[5].iov_len = 1;
+ (void) writev(2, strings, 6);
+}
+
+int_func
+set_com_err_hook(new_proc)
+ int_func new_proc;
+{
+ register int_func x = com_err_hook;
+ com_err_hook = new_proc;
+ return (x);
+}
+
+reset_com_err_hook()
+{
+ com_err_hook = (int_func) NULL;
+}
+
+void
+perror(msg)
+ register const char *msg;
+{
+ com_err(msg, errno, (char *)NULL);
+}
diff --git a/usr.bin/compile_et/test/test.c b/usr.bin/compile_et/test/test.c
new file mode 100644
index 0000000..df430da
--- /dev/null
+++ b/usr.bin/compile_et/test/test.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <errno.h>
+#include "test1.h"
+#include "test2.h"
+char *error_message();
+extern int sys_nerr, errno;
+
+main()
+{
+ printf("\nBefore initiating error table:\n\n");
+ printf("Table name '%s'\n", error_table_name(KRB_MK_AP_TGTEXP));
+ printf("UNIX name '%s'\n", error_table_name(EPERM));
+ printf("Msg TGT-expired is '%s'\n", error_message(KRB_MK_AP_TGTEXP));
+ printf("Msg EPERM is '%s'\n", error_message(EPERM));
+ printf("Msg FOO_ERR is '%s'\n", error_message(FOO_ERR));
+ printf("Msg {sys_nerr-1} is '%s'\n", error_message(sys_nerr-1));
+ printf("Msg {sys_nerr} is '%s'\n", error_message(sys_nerr));
+
+ init_error_table(0, 0, 0);
+ printf("With 0: tgt-expired -> %s\n", error_message(KRB_MK_AP_TGTEXP));
+
+ init_krb_err_tbl();
+ printf("KRB error table initialized: base %d (%s), name %s\n",
+ krb_err_base, error_message(krb_err_base),
+ error_table_name(krb_err_base));
+ printf("With krb: tgt-expired -> %s\n",
+ error_message(KRB_MK_AP_TGTEXP));
+
+ init_quux_err_tbl();
+ printf("QUUX error table initialized: base %d (%s), name %s\n",
+ quux_err_base, error_message(quux_err_base),
+ error_table_name(quux_err_base));
+
+ printf("Msg for TGT-expired is '%s'\n",
+ error_message(KRB_MK_AP_TGTEXP));
+ printf("Msg {sys_nerr-1} is '%s'\n", error_message(sys_nerr-1));
+ printf("Msg FOO_ERR is '%s'\n", error_message(FOO_ERR));
+ printf("Msg KRB_SKDC_CANT is '%s'\n",
+ error_message(KRB_SKDC_CANT));
+ printf("Msg 1e6 is '%s'\n", error_message(1000000));
+ errno = FOO_ERR;
+ perror("FOO_ERR");
+}
diff --git a/usr.bin/compile_et/test/test1.et b/usr.bin/compile_et/test/test1.et
new file mode 100644
index 0000000..4c7b77f
--- /dev/null
+++ b/usr.bin/compile_et/test/test1.et
@@ -0,0 +1,69 @@
+ error_table krb
+
+ error_code KRB_MK_AP_TKFIL,
+ "Can't read ticket file"
+
+ ec KRB_MK_AP_NOTKT,
+ "Can't find ticket or TGT"
+
+ ec KRB_MK_AP_TGTEXP,
+ "TGT expired"
+
+ ec KRB_RD_AP_UNDEC,
+ "Can't decode authenticator"
+
+ ec KRB_RD_AP_EXP,
+ "Ticket expired"
+
+ ec KRB_RD_AP_REPEAT,
+ "Repeated request"
+
+ ec KRB_RD_AP_NOT_US,
+ "The ticket isn't for us"
+
+ ec KRB_RD_AP_INCON,
+ "Request is inconsistent"
+
+ ec KRB_RD_AP_TIME,
+ "Delta-T too big"
+
+ ec KRB_RD_AP_BADD,
+ "Incorrect net address"
+
+ ec KRB_RD_AP_VERSION,
+ "Protocol version mismatch"
+
+ ec KRB_RD_AP_MSG_TYPE,
+ "Invalid message type"
+
+ ec KRB_RD_AP_MODIFIED,
+ "Message stream modified"
+
+ ec KRB_RD_AP_ORDER,
+ "Message out of order"
+
+ ec KRB_RD_AP_UNAUTHOR,
+ "Unauthorized request"
+
+ ec KRB_GT_PW_NULL,
+ "Current password is null"
+
+ ec KRB_GT_PW_BADPW,
+ "Incorrect current password"
+
+ ec KRB_GT_PW_PROT,
+ "Protocol error"
+
+ ec KRB_GT_PW_KDCERR,
+ "Error returned by KDC"
+
+ ec KRB_GT_PW_NULLTKT,
+ "Null ticket returned by KDC"
+
+ ec KRB_SKDC_RETRY,
+ "Retry count exceeded"
+
+ ec KRB_SKDC_CANT,
+ "Can't send request"
+
+ end
diff --git a/usr.bin/compile_et/test/test2.et b/usr.bin/compile_et/test/test2.et
new file mode 100644
index 0000000..55ad74e
--- /dev/null
+++ b/usr.bin/compile_et/test/test2.et
@@ -0,0 +1,9 @@
+ error_table quux
+
+ ec FOO_ERR, "foo"
+
+ ec BAR_ERR, "bar"
+
+ ec BAZ_ERR, "meow"
+
+ end
diff --git a/usr.bin/compress/Makefile b/usr.bin/compress/Makefile
new file mode 100644
index 0000000..01c59bb
--- /dev/null
+++ b/usr.bin/compress/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 4/17/94
+
+PROG= compress
+SRCS= compress.c zopen.c
+LINKS= ${BINDIR}/compress ${BINDIR}/uncompress
+MLINKS= compress.1 uncompress.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/compress/compress.1 b/usr.bin/compress/compress.1
new file mode 100644
index 0000000..74b7348
--- /dev/null
+++ b/usr.bin/compress/compress.1
@@ -0,0 +1,172 @@
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" James A. Woods, derived from original work by Spencer Thomas
+.\" and Joseph Orost.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)compress.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt COMPRESS 1
+.Os BSD 4.3
+.Sh NAME
+.Nm compress ,
+.Nm uncompress ,
+.Nm zcat
+.Nd compress and expand data
+.Sh SYNOPSIS
+.Nm compress
+.Op Fl cfv
+.Op Fl b Ar bits
+.Op Ar
+.Nm uncompress
+.Op Fl cfv
+.Op Ar
+.Nm zcat
+.Op Ar
+.Sh DESCRIPTION
+.Nm Compress
+reduces the size of the named files using adaptive Lempel-Ziv coding.
+Each
+.Ar file
+is renamed to the same name plus the extension
+.Dq .Z .
+As many of the modification time, access time, file flags, file mode,
+user ID, and group ID as allowed by permissions are retained in the
+new file.
+If compression would not reduce the size of a
+.Ar file ,
+the file is ignored.
+.Pp
+.Nm Uncompress
+restores the compressed files to their original form, renaming the
+files by deleting the
+.Dq .Z
+extension.
+.Pp
+.Nm Zcat
+is an alias for
+.Dq "uncompress -c" .
+.Pp
+If renaming the files would cause files to be overwritten and the standard
+input device is a terminal, the user is prompted (on the standard error
+output) for confirmation.
+If prompting is not possible or confirmation is not received, the files
+are not overwritten.
+.Pp
+If no files are specified, the standard input is compressed or uncompressed
+to the standard output.
+If either the input and output files are not regular files, the checks for
+reduction in size and file overwriting are not performed, the input file is
+not removed, and the attributes of the input file are not retained.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Specify the
+.Ar bits
+code limit (see below).
+.It Fl c
+Compressed or uncompressed output is written to the standard output.
+No files are modified.
+.It Fl f
+Force compression of
+.Ar file ,
+even if it is not actually reduced in size.
+Additionally, files are overwritten without prompting for confirmation.
+.It Fl v
+Print the percentage reduction of each file.
+.El
+.Pp
+.Nm Compress
+uses a modified Lempel-Ziv algorithm.
+Common substrings in the file are first replaced by 9-bit codes 257 and up.
+When code 512 is reached, the algorithm switches to 10-bit codes and
+continues to use more bits until the
+limit specified by the
+.Fl b
+flag is reached (the default is 16).
+.Ar Bits
+must be between 9 and 16.
+.Pp
+After the
+.Ar bits
+limit is reached,
+.Nm compress
+periodically checks the compression ratio.
+If it is increasing,
+.Nm compress
+continues to use the existing code dictionary.
+However, if the compression ratio decreases,
+.Nm compress
+discards the table of substrings and rebuilds it from scratch. This allows
+the algorithm to adapt to the next "block" of the file.
+.Pp
+The
+.Fl b
+flag is omitted for
+.Ar uncompress
+since the
+.Ar bits
+parameter specified during compression
+is encoded within the output, along with
+a magic number to ensure that neither decompression of random data nor
+recompression of compressed data is attempted.
+.Pp
+.ne 8
+The amount of compression obtained depends on the size of the
+input, the number of
+.Ar bits
+per code, and the distribution of common substrings.
+Typically, text such as source code or English is reduced by 50\-60%.
+Compression is generally much better than that achieved by Huffman
+coding (as used in the historical command pack), or adaptive Huffman
+coding (as used in the historical command compact), and takes less
+time to compute.
+.Pp
+The
+.Nm compress
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Rs
+.%A Welch, Terry A.
+.%D June, 1984
+.%T "A Technique for High Performance Data Compression"
+.%J "IEEE Computer"
+.%V 17:6
+.%P pp. 8-19
+.Re
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/compress/compress.c b/usr.bin/compress/compress.c
new file mode 100644
index 0000000..dd74e0f
--- /dev/null
+++ b/usr.bin/compress/compress.c
@@ -0,0 +1,444 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void compress __P((char *, char *, int));
+void cwarn __P((const char *, ...));
+void cwarnx __P((const char *, ...));
+void decompress __P((char *, char *, int));
+int permission __P((char *));
+void setfile __P((char *, struct stat *));
+void usage __P((int));
+
+int eval, force, verbose;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ enum {COMPRESS, DECOMPRESS} style;
+ size_t len;
+ int bits, cat, ch;
+ char *p, newname[MAXPATHLEN];
+
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+ if (!strcmp(p, "uncompress"))
+ style = DECOMPRESS;
+ else if (!strcmp(p, "compress"))
+ style = COMPRESS;
+ else
+ errx(1, "unknown program name");
+
+ bits = cat = 0;
+ while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
+ switch(ch) {
+ case 'b':
+ bits = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "illegal bit count -- %s", optarg);
+ break;
+ case 'c':
+ cat = 1;
+ break;
+ case 'd': /* Backward compatible. */
+ style = DECOMPRESS;
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage(style == COMPRESS);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ switch(style) {
+ case COMPRESS:
+ (void)compress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ case DECOMPRESS:
+ (void)decompress("/dev/stdin", "/dev/stdout", bits);
+ break;
+ }
+ exit (eval);
+ }
+
+ if (cat == 1 && argc > 1)
+ errx(1, "the -c option permits only a single file argument");
+
+ for (; *argv; ++argv)
+ switch(style) {
+ case COMPRESS:
+ if (cat) {
+ compress(*argv, "/dev/stdout", bits);
+ break;
+ }
+ if ((p = rindex(*argv, '.')) != NULL &&
+ !strcmp(p, ".Z")) {
+ cwarnx("%s: name already has trailing .Z",
+ *argv);
+ break;
+ }
+ len = strlen(*argv);
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ compress(*argv, newname, bits);
+ break;
+ case DECOMPRESS:
+ len = strlen(*argv);
+ if ((p = rindex(*argv, '.')) == NULL ||
+ strcmp(p, ".Z")) {
+ if (len > sizeof(newname) - 3) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len);
+ newname[len] = '.';
+ newname[len + 1] = 'Z';
+ newname[len + 2] = '\0';
+ decompress(newname,
+ cat ? "/dev/stdout" : *argv, bits);
+ } else {
+ if (len - 2 > sizeof(newname) - 1) {
+ cwarnx("%s: name too long", *argv);
+ break;
+ }
+ memmove(newname, *argv, len - 2);
+ newname[len - 2] = '\0';
+ decompress(*argv,
+ cat ? "/dev/stdout" : newname, bits);
+ }
+ break;
+ }
+ exit (eval);
+}
+
+void
+compress(in, out, bits)
+ char *in, *out;
+ int bits;
+{
+ register int nr;
+ struct stat isb, sb;
+ FILE *ifp, *ofp;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ exists = !stat(out, &sb);
+ if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
+ return;
+ isreg = oreg = !exists || S_ISREG(sb.st_mode);
+
+ ifp = ofp = NULL;
+ if ((ifp = fopen(in, "r")) == NULL) {
+ cwarn("%s", in);
+ return;
+ }
+ if (stat(in, &isb)) { /* DON'T FSTAT! */
+ cwarn("%s", in);
+ goto err;
+ }
+ if (!S_ISREG(isb.st_mode))
+ isreg = 0;
+
+ if ((ofp = zopen(out, "w", bits)) == NULL) {
+ cwarn("%s", out);
+ goto err;
+ }
+ while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
+ if (fwrite(buf, 1, nr, ofp) != nr) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+ ofp = NULL;
+
+ if (isreg) {
+ if (stat(out, &sb)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (!force && sb.st_size >= isb.st_size) {
+ if (verbose)
+ (void)printf("%s: file would grow; left unmodified\n", in);
+ if (unlink(out))
+ cwarn("%s", out);
+ goto err;
+ }
+
+ setfile(out, &isb);
+
+ if (unlink(in))
+ cwarn("%s", in);
+
+ if (verbose) {
+ (void)printf("%s: ", out);
+ if (isb.st_size > sb.st_size)
+ (void)printf("%.0f%% compression\n",
+ ((float)sb.st_size / isb.st_size) * 100.0);
+ else
+ (void)printf("%.0f%% expansion\n",
+ ((float)isb.st_size / sb.st_size) * 100.0);
+ }
+ }
+ return;
+
+err: if (ofp) {
+ if (oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+decompress(in, out, bits)
+ char *in, *out;
+ int bits;
+{
+ register int nr;
+ struct stat sb;
+ FILE *ifp, *ofp;
+ int exists, isreg, oreg;
+ u_char buf[1024];
+
+ exists = !stat(out, &sb);
+ if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
+ return;
+ isreg = oreg = !exists || S_ISREG(sb.st_mode);
+
+ ifp = ofp = NULL;
+ if ((ofp = fopen(out, "w")) == NULL) {
+ cwarn("%s", out);
+ return;
+ }
+
+ if ((ifp = zopen(in, "r", bits)) == NULL) {
+ cwarn("%s", in);
+ goto err;
+ }
+ if (stat(in, &sb)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ if (!S_ISREG(sb.st_mode))
+ isreg = 0;
+
+ while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
+ if (fwrite(buf, 1, nr, ofp) != nr) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (ferror(ifp) || fclose(ifp)) {
+ cwarn("%s", in);
+ goto err;
+ }
+ ifp = NULL;
+
+ if (fclose(ofp)) {
+ cwarn("%s", out);
+ goto err;
+ }
+
+ if (isreg) {
+ setfile(out, &sb);
+
+ if (unlink(in))
+ cwarn("%s", in);
+ }
+ return;
+
+err: if (ofp) {
+ if (oreg)
+ (void)unlink(out);
+ (void)fclose(ofp);
+ }
+ if (ifp)
+ (void)fclose(ifp);
+}
+
+void
+setfile(name, fs)
+ char *name;
+ register struct stat *fs;
+{
+ static struct timeval tv[2];
+
+ fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
+ if (utimes(name, tv))
+ cwarn("utimes: %s", name);
+
+ /*
+ * Changing the ownership probably won't succeed, unless we're root
+ * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
+ * the mode; current BSD behavior is to remove all setuid bits on
+ * chown. If chown fails, lose setuid/setgid bits.
+ */
+ if (chown(name, fs->st_uid, fs->st_gid)) {
+ if (errno != EPERM)
+ cwarn("chown: %s", name);
+ fs->st_mode &= ~(S_ISUID|S_ISGID);
+ }
+ if (chmod(name, fs->st_mode))
+ cwarn("chown: %s", name);
+
+ if (chflags(name, fs->st_flags))
+ cwarn("chflags: %s", name);
+}
+
+int
+permission(fname)
+ char *fname;
+{
+ int ch, first;
+
+ if (!isatty(fileno(stderr)))
+ return (0);
+ (void)fprintf(stderr, "overwrite %s? ", fname);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ return (first == 'y');
+}
+
+void
+usage(iscompress)
+ int iscompress;
+{
+ if (iscompress)
+ (void)fprintf(stderr,
+ "usage: compress [-cfv] [-b bits] [file ...]\n");
+ else
+ (void)fprintf(stderr,
+ "usage: uncompress [-c] [-b bits] [file ...]\n");
+ exit(1);
+}
+
+void
+#if __STDC__
+cwarnx(const char *fmt, ...)
+#else
+cwarnx(fmt, va_alist)
+ int eval;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarnx(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}
+
+void
+#if __STDC__
+cwarn(const char *fmt, ...)
+#else
+cwarn(fmt, va_alist)
+ int eval;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+ eval = 1;
+}
diff --git a/usr.bin/compress/doc/NOTES b/usr.bin/compress/doc/NOTES
new file mode 100644
index 0000000..7c28c9c
--- /dev/null
+++ b/usr.bin/compress/doc/NOTES
@@ -0,0 +1,139 @@
+From: James A. Woods <jaw@eos.arc.nasa.gov>
+
+>From vn Fri Dec 2 18:05:27 1988
+Subject: Re: Looking for C source for RSA
+Newsgroups: sci.crypt
+
+# Illegitimi noncarborundum
+
+Patents are a tar pit.
+
+A good case can be made that most are just a license to sue, and nothing
+is illegal until a patent is upheld in court.
+
+For example, if you receive netnews by means other than 'nntp',
+these very words are being modulated by 'compress',
+a variation on the patented Lempel-Ziv-Welch algorithm.
+
+Original Ziv-Lempel is patent number 4,464,650, and the more powerful
+LZW method is #4,558,302. Yet despite any similarities between 'compress'
+and LZW (the public-domain 'compress' code was designed and given to the
+world before the ink on the Welch patent was dry), no attorneys from Sperry
+(the assignee) have asked you to unplug your Usenet connection.
+
+Why? I can't speak for them, but it is possible the claims are too broad,
+or, just as bad, not broad enough. ('compress' does things not mentioned
+in the Welch patent.) Maybe they realize that they can commercialize
+LZW better by selling hardware implementations rather than by licensing
+software. Again, the LZW software delineated in the patent is *not*
+the same as that of 'compress'.
+
+At any rate, court-tested software patents are a different animal;
+corporate patents in a portfolio are usually traded like baseball cards
+to shut out small fry rather than actually be defended before
+non-technical juries. Perhaps RSA will undergo this test successfully,
+although the grant to "exclude others from making, using, or selling"
+the invention would then only apply to the U.S. (witness the
+Genentech patent of the TPA molecule in the U.S. but struck down
+in Great Britain as too broad.)
+
+The concept is still exotic for those who learned in school the rule of thumb
+that one may patent "apparatus" but not an "idea".
+Apparently this all changed in Diamond v. Diehr (1981) when the U. S. Supreme
+Court reversed itself.
+
+Scholars should consult the excellent article in the Washington and Lee
+Law Review (fall 1984, vol. 41, no. 4) by Anthony and Colwell for a
+comprehensive survey of an area which will remain murky for some time.
+
+Until the dust clears, how you approach ideas which are patented depends
+on how paranoid you are of a legal onslaught. Arbitrary? Yes. But
+the patent bar the the CCPA (Court of Customs and Patent Appeals)
+thanks you for any uncertainty as they, at least, stand to gain
+from any trouble.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+From: James A. Woods <jaw@eos.arc.nasa.gov>
+Subject: Re: Looking for C source for RSA (actually 'compress' patents)
+
+ In article <2042@eos.UUCP> you write:
+ >The concept is still exotic for those who learned in school the rule of thumb
+ >that one may patent "apparatus" but not an "idea".
+
+A rule of thumb that has never been completely valid, as any chemical
+engineer can tell you. (Chemical processes were among the earliest patents,
+as I recall.)
+
+ ah yes -- i date myself when relaying out-of-date advice from elderly
+ attorneys who don't even specialize in patents. one other interesting
+ class of patents include the output of optical lens design programs,
+ which yield formulae which can then fairly directly can be molded
+ into glass. although there are restrictions on patenting equations,
+ the "embedded systems" seem to fly past the legal gauntlets.
+
+ anyway, i'm still learning about intellectual property law after
+ several conversations from a unisys (nee sperry) lawyer re 'compress'.
+
+ it's more complicated than this, but they're letting (oral
+ communication only) software versions of 'compress' slide
+ as far as licensing fees go. this includes 'arc', 'stuffit',
+ and other commercial wrappers for 'compress'. yet they are
+ signing up licensees for hardware chips. hewlett-packard
+ supposedly has an active vlsi project, and unisys has
+ board-level lzw-based tape controllers. (to build lzw into
+ a disk controller would be strange, as you'd have to build
+ in a filesystem too!)
+
+ it's byzantine
+ that unisys is in a tiff with hp regarding the patents,
+ after discovering some sort of "compress" button on some
+ hp terminal product. why? well, professor abraham lempel jumped
+ from being department chairman of computer science at technion in
+ israel to sperry (where he got the first patent), but then to work
+ at hewlett-packard on sabbatical. the second welch patent
+ is only weakly derivative of the first, so they want chip
+ licenses and hp relented. however, everyone agrees something
+ like the current unix implementation is the way to go with
+ software, so hp (and ucb) long ago asked spencer thomas and i to sign
+ off on copyright permission (although they didn't need to, it being pd).
+ lempel, hp, and unisys grumbles they can't make money off the
+ software since a good free implementation (not the best --
+ i have more ideas!) escaped via usenet. (lempel's own pascal
+ code was apparently horribly slow.)
+ i don't follow the ibm 'arc' legal bickering; my impression
+ is that the pc folks are making money off the archiver/wrapper
+ look/feel of the thing [if ms-dos can be said to have a look and feel].
+
+ now where is telebit with the compress firmware? in a limbo
+ netherworld, probably, with sperry still welcoming outfits
+ to sign patent licenses, a common tactic to bring other small fry
+ into the fold. the guy who crammed 12-bit compess into the modem
+ there left. also what is transpiring with 'compress' and sys 5 rel 4?
+ beats me, but if sperry got a hold of them on these issues,
+ at&t would likely re-implement another algorithm if they
+ thought 'compress' infringes. needful to say, i don't think
+ it does after the abovementioned legal conversation.
+ my own beliefs on whether algorithms should be patentable at all
+ change with the weather. if the courts finally nail down
+ patent protection for algorithms, academic publication in
+ textbooks will be somewhat at odds with the engineering world,
+ where the textbook codes will simply be a big tease to get
+ money into the patent holder coffers...
+
+ oh, if you implement lzw from the patent, you won't get
+ good rates because it doesn't mention adaptive table reset,
+ lack thereof being *the* serious deficiency of thomas' first version.
+
+ now i know that patent law generally protects against independent
+ re-invention (like the 'xor' hash function pleasantly mentioned
+ in the patent [but not the paper]).
+ but the upshot is that if anyone ever wanted to sue us,
+ we're partially covered with
+ independently-developed twists, plus the fact that some of us work
+ in a bureacratic morass (as contractor to a public agency in my case).
+
+ quite a mess, huh? i've wanted to tell someone this stuff
+ for a long time, for posterity if nothing else.
+
+james
+
diff --git a/usr.bin/compress/doc/README b/usr.bin/compress/doc/README
new file mode 100644
index 0000000..6803287
--- /dev/null
+++ b/usr.bin/compress/doc/README
@@ -0,0 +1,283 @@
+
+ @(#)README 8.1 (Berkeley) 6/9/93
+
+Compress version 4.0 improvements over 3.0:
+ o compress() speedup (10-50%) by changing division hash to xor
+ o decompress() speedup (5-10%)
+ o Memory requirements reduced (3-30%)
+ o Stack requirements reduced to less than 4kb
+ o Removed 'Big+Fast' compress code (FBITS) because of compress speedup
+ o Portability mods for Z8000 and PC/XT (but not zeus 3.2)
+ o Default to 'quiet' mode
+ o Unification of 'force' flags
+ o Manual page overhaul
+ o Portability enhancement for M_XENIX
+ o Removed text on #else and #endif
+ o Added "-V" switch to print version and options
+ o Added #defines for SIGNED_COMPARE_SLOW
+ o Added Makefile and "usermem" program
+ o Removed all floating point computations
+ o New programs: [deleted]
+
+The "usermem" script attempts to determine the maximum process size. Some
+editing of the script may be necessary (see the comments). [It should work
+fine on 4.3 bsd.] If you can't get it to work at all, just create file
+"USERMEM" containing the maximum process size in decimal.
+
+The following preprocessor symbols control the compilation of "compress.c":
+
+ o USERMEM Maximum process memory on the system
+ o SACREDMEM Amount to reserve for other proceses
+ o SIGNED_COMPARE_SLOW Unsigned compare instructions are faster
+ o NO_UCHAR Don't use "unsigned char" types
+ o BITS Overrules default set by USERMEM-SACREDMEM
+ o vax Generate inline assembler
+ o interdata Defines SIGNED_COMPARE_SLOW
+ o M_XENIX Makes arrays < 65536 bytes each
+ o pdp11 BITS=12, NO_UCHAR
+ o z8000 BITS=12
+ o pcxt BITS=12
+ o BSD4_2 Allow long filenames ( > 14 characters) &
+ Call setlinebuf(stderr)
+
+The difference "usermem-sacredmem" determines the maximum BITS that can be
+specified with the "-b" flag.
+
+memory: at least BITS
+------ -- ----- ----
+ 433,484 16
+ 229,600 15
+ 127,536 14
+ 73,464 13
+ 0 12
+
+The default is BITS=16.
+
+The maximum bits can be overrulled by specifying "-DBITS=bits" at
+compilation time.
+
+WARNING: files compressed on a large machine with more bits than allowed by
+a version of compress on a smaller machine cannot be decompressed! Use the
+"-b12" flag to generate a file on a large machine that can be uncompressed
+on a 16-bit machine.
+
+The output of compress 4.0 is fully compatible with that of compress 3.0.
+In other words, the output of compress 4.0 may be fed into uncompress 3.0 or
+the output of compress 3.0 may be fed into uncompress 4.0.
+
+The output of compress 4.0 not compatible with that of
+compress 2.0. However, compress 4.0 still accepts the output of
+compress 2.0. To generate output that is compatible with compress
+2.0, use the undocumented "-C" flag.
+
+ -from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85
+--------------------------------
+
+Enclosed is compress version 3.0 with the following changes:
+
+1. "Block" compression is performed. After the BITS run out, the
+ compression ratio is checked every so often. If it is decreasing,
+ the table is cleared and a new set of substrings are generated.
+
+ This makes the output of compress 3.0 not compatible with that of
+ compress 2.0. However, compress 3.0 still accepts the output of
+ compress 2.0. To generate output that is compatible with compress
+ 2.0, use the undocumented "-C" flag.
+
+2. A quiet "-q" flag has been added for use by the news system.
+
+3. The character chaining has been deleted and the program now uses
+ hashing. This improves the speed of the program, especially
+ during decompression. Other speed improvements have been made,
+ such as using putc() instead of fwrite().
+
+4. A large table is used on large machines when a relatively small
+ number of bits is specified. This saves much time when compressing
+ for a 16-bit machine on a 32-bit virtual machine. Note that the
+ speed improvement only occurs when the input file is > 30000
+ characters, and the -b BITS is less than or equal to the cutoff
+ described below.
+
+Most of these changes were made by James A. Woods (ames!jaw). Thank you
+James!
+
+To compile compress:
+
+ cc -O -DUSERMEM=usermem -o compress compress.c
+
+Where "usermem" is the amount of physical user memory available (in bytes).
+If any physical memory is to be reserved for other processes, put in
+"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved.
+
+The difference "usermem-sacredmem" determines the maximum BITS that can be
+specified, and the cutoff bits where the large+fast table is used.
+
+memory: at least BITS cutoff
+------ -- ----- ---- ------
+ 4,718,592 16 13
+ 2,621,440 16 12
+ 1,572,864 16 11
+ 1,048,576 16 10
+ 631,808 16 --
+ 329,728 15 --
+ 178,176 14 --
+ 99,328 13 --
+ 0 12 --
+
+The default memory size is 750,000 which gives a maximum BITS=16 and no
+large+fast table.
+
+The maximum bits can be overruled by specifying "-DBITS=bits" at
+compilation time.
+
+If your machine doesn't support unsigned characters, define "NO_UCHAR"
+when compiling.
+
+If your machine has "int" as 16-bits, define "SHORT_INT" when compiling.
+
+After compilation, move "compress" to a standard executable location, such
+as /usr/local. Then:
+ cd /usr/local
+ ln compress uncompress
+ ln compress zcat
+
+On machines that have a fixed stack size (such as Perkin-Elmer), set the
+stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer).
+
+Next, install the manual (compress.l).
+ cp compress.l /usr/man/manl
+ cd /usr/man/manl
+ ln compress.l uncompress.l
+ ln compress.l zcat.l
+
+ - or -
+
+ cp compress.l /usr/man/man1/compress.1
+ cd /usr/man/man1
+ ln compress.1 uncompress.1
+ ln compress.1 zcat.1
+
+ regards,
+ petsd!joe
+
+Here is a note from the net:
+
+>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985
+Path: ames!hplabs!pesnta!amd!turtlevax!ken
+From: ken@turtlevax.UUCP (Ken Turkowski)
+Newsgroups: net.sources
+Subject: Re: Compress release 3.0 : sample Makefile
+Organization: CADLINC, Inc. @ Menlo Park, CA
+
+In the compress 3.0 source recently posted to mod.sources, there is a
+#define variable which can be set for optimum performance on a machine
+with a large amount of memory. A program (usermem) to calculate the
+useable amount of physical user memory is enclosed, as well as a sample
+4.2bsd Vax Makefile for compress.
+
+Here is the README file from the previous version of compress (2.0):
+
+>Enclosed is compress.c version 2.0 with the following bugs fixed:
+>
+>1. The packed files produced by compress are different on different
+> machines and dependent on the vax sysgen option.
+> The bug was in the different byte/bit ordering on the
+> various machines. This has been fixed.
+>
+> This version is NOT compatible with the original vax posting
+> unless the '-DCOMPATIBLE' option is specified to the C
+> compiler. The original posting has a bug which I fixed,
+> causing incompatible files. I recommend you NOT to use this
+> option unless you already have a lot of packed files from
+> the original posting by thomas.
+>2. The exit status is not well defined (on some machines) causing the
+> scripts to fail.
+> The exit status is now 0,1 or 2 and is documented in
+> compress.l.
+>3. The function getopt() is not available in all C libraries.
+> The function getopt() is no longer referenced by the
+> program.
+>4. Error status is not being checked on the fwrite() and fflush() calls.
+> Fixed.
+>
+>The following enhancements have been made:
+>
+>1. Added facilities of "compact" into the compress program. "Pack",
+> "Unpack", and "Pcat" are no longer required (no longer supplied).
+>2. Installed work around for C compiler bug with "-O".
+>3. Added a magic number header (\037\235). Put the bits specified
+> in the file.
+>4. Added "-f" flag to force overwrite of output file.
+>5. Added "-c" flag and "zcat" program. 'ln compress zcat' after you
+> compile.
+>6. The 'uncompress' script has been deleted; simply
+> 'ln compress uncompress' after you compile and it will work.
+>7. Removed extra bit masking for machines that support unsigned
+> characters. If your machine doesn't support unsigned characters,
+> define "NO_UCHAR" when compiling.
+>
+>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a
+>standard executable location, such as /usr/local. Then:
+> cd /usr/local
+> ln compress uncompress
+> ln compress zcat
+>
+>On machines that have a fixed stack size (such as Perkin-Elmer), set the
+>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer).
+>
+>Next, install the manual (compress.l).
+> cp compress.l /usr/man/manl - or -
+> cp compress.l /usr/man/man1/compress.1
+>
+>Here is the README that I sent with my first posting:
+>
+>>Enclosed is a modified version of compress.c, along with scripts to make it
+>>run identically to pack(1), unpack(1), an pcat(1). Here is what I
+>>(petsd!joe) and a colleague (petsd!peora!srd) did:
+>>
+>>1. Removed VAX dependencies.
+>>2. Changed the struct to separate arrays; saves mucho memory.
+>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.)
+>>4. Sorted the character next chain and changed the search to stop
+>>prematurely. This saves a lot on the execution time when compressing.
+>>
+>>This version is totally compatible with the original version. Even though
+>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit
+>>machine, due to the size of the arrays.
+>>
+>>Here is the README file from the original author:
+>>
+>>>Well, with all this discussion about file compression (for news batching
+>>>in particular) going around, I decided to implement the text compression
+>>>algorithm described in the June Computer magazine. The author claimed
+>>>blinding speed and good compression ratios. It's certainly faster than
+>>>compact (but, then, what wouldn't be), but it's also the same speed as
+>>>pack, and gets better compression than both of them. On 350K bytes of
+>>>unix-wizards, compact took about 8 minutes of CPU, pack took about 80
+>>>seconds, and compress (herein) also took 80 seconds. But, compact and
+>>>pack got about 30% compression, whereas compress got over 50%. So, I
+>>>decided I had something, and that others might be interested, too.
+>>>
+>>>As is probably true of compact and pack (although I haven't checked),
+>>>the byte order within a word is probably relevant here, but as long as
+>>>you stay on a single machine type, you should be ok. (Can anybody
+>>>elucidate on this?) There are a couple of asm's in the code (extv and
+>>>insv instructions), so anyone porting it to another machine will have to
+>>>deal with this anyway (and could probably make it compatible with Vax
+>>>byte order at the same time). Anyway, I've linted the code (both with
+>>>and without -p), so it should run elsewhere. Note the longs in the
+>>>code, you can take these out if you reduce BITS to <= 15.
+>>>
+>>>Have fun, and as always, if you make good enhancements, or bug fixes,
+>>>I'd like to see them.
+>>>
+>>>=Spencer (thomas@utah-20, {harpo,hplabs,arizona}!utah-cs!thomas)
+>>
+>> regards,
+>> joe
+>>
+>>--
+>>Full-Name: Joseph M. Orost
+>>UUCP: ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe
+>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724
+>>Phone: (201) 870-5844
diff --git a/usr.bin/compress/doc/revision.log b/usr.bin/compress/doc/revision.log
new file mode 100644
index 0000000..b1d8b24
--- /dev/null
+++ b/usr.bin/compress/doc/revision.log
@@ -0,0 +1,116 @@
+/*
+ * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $
+ * $Log: compress.c,v $
+ * Revision 4.0 85/07/30 12:50:00 joe
+ * Removed ferror() calls in output routine on every output except first.
+ * Prepared for release to the world.
+ *
+ * Revision 3.6 85/07/04 01:22:21 joe
+ * Remove much wasted storage by overlaying hash table with the tables
+ * used by decompress: tab_suffix[1<<BITS], stack[8000]. Updated USERMEM
+ * computations. Fixed dump_tab() DEBUG routine.
+ *
+ * Revision 3.5 85/06/30 20:47:21 jaw
+ * Change hash function to use exclusive-or. Rip out hash cache. These
+ * speedups render the megamemory version defunct, for now. Make decoder
+ * stack global. Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply.
+ *
+ * Revision 3.4 85/06/27 12:00:00 ken
+ * Get rid of all floating-point calculations by doing all compression ratio
+ * calculations in fixed point.
+ *
+ * Revision 3.3 85/06/24 21:53:24 joe
+ * Incorporate portability suggestion for M_XENIX. Got rid of text on #else
+ * and #endif lines. Cleaned up #ifdefs for vax and interdata.
+ *
+ * Revision 3.2 85/06/06 21:53:24 jaw
+ * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list.
+ * Default to "quiet" output (no compression statistics).
+ *
+ * Revision 3.1 85/05/12 18:56:13 jaw
+ * Integrate decompress() stack speedups (from early pointer mods by McKie).
+ * Repair multi-file USERMEM gaffe. Unify 'force' flags to mimic semantics
+ * of SVR2 'pack'. Streamline block-compress table clear logic. Increase
+ * output byte count by magic number size.
+ *
+ * Revision 3.0 84/11/27 11:50:00 petsd!joe
+ * Set HSIZE depending on BITS. Set BITS depending on USERMEM. Unrolled
+ * loops in clear routines. Added "-C" flag for 2.0 compatibility. Used
+ * unsigned compares on Perkin-Elmer. Fixed foreground check.
+ *
+ * Revision 2.7 84/11/16 19:35:39 ames!jaw
+ * Cache common hash codes based on input statistics; this improves
+ * performance for low-density raster images. Pass on #ifdef bundle
+ * from Turkowski.
+ *
+ * Revision 2.6 84/11/05 19:18:21 ames!jaw
+ * Vary size of hash tables to reduce time for small files.
+ * Tune PDP-11 hash function.
+ *
+ * Revision 2.5 84/10/30 20:15:14 ames!jaw
+ * Junk chaining; replace with the simpler (and, on the VAX, faster)
+ * double hashing, discussed within. Make block compression standard.
+ *
+ * Revision 2.4 84/10/16 11:11:11 ames!jaw
+ * Introduce adaptive reset for block compression, to boost the rate
+ * another several percent. (See mailing list notes.)
+ *
+ * Revision 2.3 84/09/22 22:00:00 petsd!joe
+ * Implemented "-B" block compress. Implemented REVERSE sorting of tab_next.
+ * Bug fix for last bits. Changed fwrite to putchar loop everywhere.
+ *
+ * Revision 2.2 84/09/18 14:12:21 ames!jaw
+ * Fold in news changes, small machine typedef from thomas,
+ * #ifdef interdata from joe.
+ *
+ * Revision 2.1 84/09/10 12:34:56 ames!jaw
+ * Configured fast table lookup for 32-bit machines.
+ * This cuts user time in half for b <= FBITS, and is useful for news batching
+ * from VAX to PDP sites. Also sped up decompress() [fwrite->putc] and
+ * added signal catcher [plus beef in writeerr()] to delete effluvia.
+ *
+ * Revision 2.0 84/08/28 22:00:00 petsd!joe
+ * Add check for foreground before prompting user. Insert maxbits into
+ * compressed file. Force file being uncompressed to end with ".Z".
+ * Added "-c" flag and "zcat". Prepared for release.
+ *
+ * Revision 1.10 84/08/24 18:28:00 turtlevax!ken
+ * Will only compress regular files (no directories), added a magic number
+ * header (plus an undocumented -n flag to handle old files without headers),
+ * added -f flag to force overwriting of possibly existing destination file,
+ * otherwise the user is prompted for a response. Will tack on a .Z to a
+ * filename if it doesn't have one when decompressing. Will only replace
+ * file if it was compressed.
+ *
+ * Revision 1.9 84/08/16 17:28:00 turtlevax!ken
+ * Removed scanargs(), getopt(), added .Z extension and unlimited number of
+ * filenames to compress. Flags may be clustered (-Ddvb12) or separated
+ * (-D -d -v -b 12), or combination thereof. Modes and other status is
+ * copied with copystat(). -O bug for 4.2 seems to have disappeared with
+ * 1.8.
+ *
+ * Revision 1.8 84/08/09 23:15:00 joe
+ * Made it compatible with vax version, installed jim's fixes/enhancements
+ *
+ * Revision 1.6 84/08/01 22:08:00 joe
+ * Sped up algorithm significantly by sorting the compress chain.
+ *
+ * Revision 1.5 84/07/13 13:11:00 srd
+ * Added C version of vax asm routines. Changed structure to arrays to
+ * save much memory. Do unsigned compares where possible (faster on
+ * Perkin-Elmer)
+ *
+ * Revision 1.4 84/07/05 03:11:11 thomas
+ * Clean up the code a little and lint it. (Lint complains about all
+ * the regs used in the asm, but I'm not going to "fix" this.)
+ *
+ * Revision 1.3 84/07/05 02:06:54 thomas
+ * Minor fixes.
+ *
+ * Revision 1.2 84/07/05 00:27:27 thomas
+ * Add variable bit length output.
+ *
+ */
+
+static char rcs_ident[] =
+ "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
diff --git a/usr.bin/compress/zcat.sh b/usr.bin/compress/zcat.sh
new file mode 100644
index 0000000..c4931e4
--- /dev/null
+++ b/usr.bin/compress/zcat.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)zcat.sh 8.1 (Berkeley) 6/6/93
+#
+
+uncompress -c $*
diff --git a/usr.bin/compress/zopen.3 b/usr.bin/compress/zopen.3
new file mode 100644
index 0000000..853462f
--- /dev/null
+++ b/usr.bin/compress/zopen.3
@@ -0,0 +1,139 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)zopen.3 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt ZOPEN 3
+.Os
+.Sh NAME
+.Nm zopen
+.Nd compressed stream open function
+.Sh SYNOPSIS
+.Fd #include <stdio.h>
+.Ft FILE *
+.Fn zopen "const char *path" "const char *mode" "int bits"
+.Sh DESCRIPTION
+The
+.Fn zopen
+function
+opens the compressed file whose name is the string pointed to by
+.Fa path
+and associates a stream with it.
+.Pp
+The argument
+.Fa mode
+points to one of the following one-character strings:
+.Bl -tag -width indent
+.It Dq Li r
+Open compressed file for reading.
+The stream is positioned at the beginning of the file.
+.It Dq Li w
+Truncate file to zero length or create compressed file for writing.
+The stream is positioned at the beginning of the file.
+.El
+.Pp
+Any created files will have mode
+.Pf \\*q Dv S_IRUSR
+\&|
+.Dv S_IWUSR
+\&|
+.Dv S_IRGRP
+\&|
+.Dv S_IWGRP
+\&|
+.Dv S_IROTH
+\&|
+.Dv S_IWOTH Ns \\*q
+.Pq Li 0666 ,
+as modified by the process'
+umask value (see
+.Xr umask 2 ) .
+.Pp
+Files may only be read or written.
+Seek operations are not allowed.
+.Pp
+The
+.Fa bits
+argument, if non-zero, is set to the bits code limit.
+If zero, the default is 16.
+See
+.Fn compress 1
+for more information.
+.Sh RETURN VALUES
+Upon successful completion
+.Fn zopen
+returns a
+.Tn FILE
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width [EINVAL]
+.It Bq Er EINVAL
+The
+.Fa mode
+or
+.Fa bits
+arguments specified to
+.Fn zopen
+were invalid.
+.It Bq Er EFTYPE
+The compressed file starts with an invalid header, or the compressed
+file is compressed with more bits than can be handled.
+.El
+.Pp
+The
+.Fn zopen
+function may also fail and set
+.Va errno
+for any of the errors specified for the routines
+.Xr fopen 3
+or
+.Xr funopen 3 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr fopen 3 ,
+.Xr funopen 3
+.Sh HISTORY
+The
+.Nm zopen
+function
+first appeared in 4.4BSD.
+.Sh BUGS
+The
+.Fn zopen
+function
+may not be portable to systems other than
+.Bx .
diff --git a/usr.bin/compress/zopen.c b/usr.bin/compress/zopen.c
new file mode 100644
index 0000000..510b895
--- /dev/null
+++ b/usr.bin/compress/zopen.c
@@ -0,0 +1,740 @@
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*-
+ * fcompress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * Compress authors:
+ * Spencer W. Thomas (decvax!utah-cs!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ *
+ * Cleaned up and converted to library returning I/O streams by
+ * Diomidis Spinellis <dds@doc.ic.ac.uk>.
+ *
+ * zopen(filename, mode, bits)
+ * Returns a FILE * that can be used for read or write. The modes
+ * supported are only "r" and "w". Seeking is not allowed. On
+ * reading the file is decompressed, on writing it is compressed.
+ * The output is compatible with compress(1) with 16 bit tables.
+ * Any file produced by compress(1) can be read.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BITS 16 /* Default bits. */
+#define HSIZE 69001 /* 95% occupancy */
+
+/* A code_int must be able to hold 2**BITS values of type int, and also -1. */
+typedef long code_int;
+typedef long count_int;
+
+typedef u_char char_type;
+static char_type magic_header[] =
+ {'\037', '\235'}; /* 1F 9D */
+
+#define BIT_MASK 0x1f /* Defines for third byte of header. */
+#define BLOCK_MASK 0x80
+
+/*
+ * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
+ * a fourth header byte (for expansion).
+ */
+#define INIT_BITS 9 /* Initial number of bits/code. */
+
+#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
+
+struct s_zstate {
+ FILE *zs_fp; /* File stream for I/O */
+ char zs_mode; /* r or w */
+ enum {
+ S_START, S_MIDDLE, S_EOF
+ } zs_state; /* State of computation */
+ int zs_n_bits; /* Number of bits/code. */
+ int zs_maxbits; /* User settable max # bits/code. */
+ code_int zs_maxcode; /* Maximum code, given n_bits. */
+ code_int zs_maxmaxcode; /* Should NEVER generate this code. */
+ count_int zs_htab [HSIZE];
+ u_short zs_codetab [HSIZE];
+ code_int zs_hsize; /* For dynamic table sizing. */
+ code_int zs_free_ent; /* First unused entry. */
+ /*
+ * Block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+ int zs_block_compress;
+ int zs_clear_flg;
+ long zs_ratio;
+ count_int zs_checkpoint;
+ int zs_offset;
+ long zs_in_count; /* Length of input. */
+ long zs_bytes_out; /* Length of compressed output. */
+ long zs_out_count; /* # of codes output (for debugging). */
+ char_type zs_buf[BITS];
+ union {
+ struct {
+ long zs_fcode;
+ code_int zs_ent;
+ code_int zs_hsize_reg;
+ int zs_hshift;
+ } w; /* Write paramenters */
+ struct {
+ char_type *zs_stackp;
+ int zs_finchar;
+ code_int zs_code, zs_oldcode, zs_incode;
+ int zs_roffset, zs_size;
+ char_type zs_gbuf[BITS];
+ } r; /* Read parameters */
+ } u;
+};
+
+/* Definitions to retain old variable names */
+#define fp zs->zs_fp
+#define zmode zs->zs_mode
+#define state zs->zs_state
+#define n_bits zs->zs_n_bits
+#define maxbits zs->zs_maxbits
+#define maxcode zs->zs_maxcode
+#define maxmaxcode zs->zs_maxmaxcode
+#define htab zs->zs_htab
+#define codetab zs->zs_codetab
+#define hsize zs->zs_hsize
+#define free_ent zs->zs_free_ent
+#define block_compress zs->zs_block_compress
+#define clear_flg zs->zs_clear_flg
+#define ratio zs->zs_ratio
+#define checkpoint zs->zs_checkpoint
+#define offset zs->zs_offset
+#define in_count zs->zs_in_count
+#define bytes_out zs->zs_bytes_out
+#define out_count zs->zs_out_count
+#define buf zs->zs_buf
+#define fcode zs->u.w.zs_fcode
+#define hsize_reg zs->u.w.zs_hsize_reg
+#define ent zs->u.w.zs_ent
+#define hshift zs->u.w.zs_hshift
+#define stackp zs->u.r.zs_stackp
+#define finchar zs->u.r.zs_finchar
+#define code zs->u.r.zs_code
+#define oldcode zs->u.r.zs_oldcode
+#define incode zs->u.r.zs_incode
+#define roffset zs->u.r.zs_roffset
+#define size zs->u.r.zs_size
+#define gbuf zs->u.r.zs_gbuf
+
+/*
+ * To save much memory, we overlay the table used by compress() with those
+ * used by decompress(). The tab_prefix table is the same size and type as
+ * the codetab. The tab_suffix table needs 2**BITS characters. We get this
+ * from the beginning of htab. The output stack uses the rest of htab, and
+ * contains characters. There is plenty of room for any possible stack
+ * (stack used to be 8000 characters).
+ */
+
+#define htabof(i) htab[i]
+#define codetabof(i) codetab[i]
+
+#define tab_prefixof(i) codetabof(i)
+#define tab_suffixof(i) ((char_type *)(htab))[i]
+#define de_stack ((char_type *)&tab_suffixof(1 << BITS))
+
+#define CHECK_GAP 10000 /* Ratio check interval. */
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+static int cl_block __P((struct s_zstate *));
+static void cl_hash __P((struct s_zstate *, count_int));
+static code_int getcode __P((struct s_zstate *));
+static int output __P((struct s_zstate *, code_int));
+static int zclose __P((void *));
+static int zread __P((void *, char *, int));
+static int zwrite __P((void *, const char *, int));
+
+/*-
+ * Algorithm from "A Technique for High Performance Data Compression",
+ * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
+ *
+ * Algorithm:
+ * Modified Lempel-Ziv method (LZW). Basically finds common
+ * substrings and replaces them with a variable size code. This is
+ * deterministic, and can be done on the fly. Thus, the decompression
+ * procedure needs no input table, but tracks the way the table was built.
+ */
+
+/*-
+ * compress write
+ *
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation. Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please direct
+ * questions about this implementation to ames!jaw.
+ */
+static int
+zwrite(cookie, wbp, num)
+ void *cookie;
+ const char *wbp;
+ int num;
+{
+ register code_int i;
+ register int c, disp;
+ struct s_zstate *zs;
+ const u_char *bp;
+ u_char tmp;
+ int count;
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)wbp;
+ if (state == S_MIDDLE)
+ goto middle;
+ state = S_MIDDLE;
+
+ maxmaxcode = 1L << maxbits;
+ if (fwrite(magic_header,
+ sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header))
+ return (-1);
+ tmp = (u_char)((maxbits) | block_compress);
+ if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp))
+ return (-1);
+
+ offset = 0;
+ bytes_out = 3; /* Includes 3-byte header mojo. */
+ out_count = 0;
+ clear_flg = 0;
+ ratio = 0;
+ in_count = 1;
+ checkpoint = CHECK_GAP;
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ free_ent = ((block_compress) ? FIRST : 256);
+
+ ent = *bp++;
+ --count;
+
+ hshift = 0;
+ for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L)
+ hshift++;
+ hshift = 8 - hshift; /* Set hash code range bound. */
+
+ hsize_reg = hsize;
+ cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */
+
+middle: for (i = 0; count--;) {
+ c = *bp++;
+ in_count++;
+ fcode = (long)(((long)c << maxbits) + ent);
+ i = ((c << hshift) ^ ent); /* Xor hashing. */
+
+ if (htabof(i) == fcode) {
+ ent = codetabof(i);
+ continue;
+ } else if ((long)htabof(i) < 0) /* Empty slot. */
+ goto nomatch;
+ disp = hsize_reg - i; /* Secondary hash (after G. Knott). */
+ if (i == 0)
+ disp = 1;
+probe: if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (htabof(i) == fcode) {
+ ent = codetabof(i);
+ continue;
+ }
+ if ((long)htabof(i) >= 0)
+ goto probe;
+nomatch: if (output(zs, (code_int) ent) == -1)
+ return (-1);
+ out_count++;
+ ent = c;
+ if (free_ent < maxmaxcode) {
+ codetabof(i) = free_ent++; /* code -> hashtable */
+ htabof(i) = fcode;
+ } else if ((count_int)in_count >=
+ checkpoint && block_compress) {
+ if (cl_block(zs) == -1)
+ return (-1);
+ }
+ }
+ return (num);
+}
+
+static int
+zclose(cookie)
+ void *cookie;
+{
+ struct s_zstate *zs;
+ int rval;
+
+ zs = cookie;
+ if (zmode == 'w') { /* Put out the final code. */
+ if (output(zs, (code_int) ent) == -1) {
+ (void)fclose(fp);
+ free(zs);
+ return (-1);
+ }
+ out_count++;
+ if (output(zs, (code_int) - 1) == -1) {
+ (void)fclose(fp);
+ free(zs);
+ return (-1);
+ }
+ }
+ rval = fclose(fp) == EOF ? -1 : 0;
+ free(zs);
+ return (rval);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static char_type lmask[9] =
+ {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
+static char_type rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output(zs, ocode)
+ struct s_zstate *zs;
+ code_int ocode;
+{
+ register int bits, r_off;
+ register char_type *bp;
+
+ r_off = offset;
+ bits = n_bits;
+ bp = buf;
+ if (ocode >= 0) {
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+ /*
+ * Since ocode is always >= 8 bits, only need to mask the first
+ * hunk on the left.
+ */
+ *bp = (*bp & rmask[r_off]) | (ocode << r_off) & lmask[r_off];
+ bp++;
+ bits -= (8 - r_off);
+ ocode >>= 8 - r_off;
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ *bp++ = ocode;
+ ocode >>= 8;
+ bits -= 8;
+ }
+ /* Last bits. */
+ if (bits)
+ *bp = ocode;
+ offset += n_bits;
+ if (offset == (n_bits << 3)) {
+ bp = buf;
+ bits = n_bits;
+ bytes_out += bits;
+ if (fwrite(bp, sizeof(char), bits, fp) != bits)
+ return (-1);
+ bp += bits;
+ bits = 0;
+ offset = 0;
+ }
+ /*
+ * If the next entry is going to be too big for the ocode size,
+ * then increase it, if possible.
+ */
+ if (free_ent > maxcode || (clear_flg > 0)) {
+ /*
+ * Write the whole buffer, because the input side won't
+ * discover the size increase until after it has read it.
+ */
+ if (offset > 0) {
+ if (fwrite(buf, 1, n_bits, fp) != n_bits)
+ return (-1);
+ bytes_out += n_bits;
+ }
+ offset = 0;
+
+ if (clear_flg) {
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ clear_flg = 0;
+ } else {
+ n_bits++;
+ if (n_bits == maxbits)
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ }
+ } else {
+ /* At EOF, write the rest of the buffer. */
+ if (offset > 0) {
+ offset = (offset + 7) / 8;
+ if (fwrite(buf, 1, offset, fp) != offset)
+ return (-1);
+ bytes_out += offset;
+ }
+ offset = 0;
+ }
+ return (0);
+}
+
+/*
+ * Decompress read. This routine adapts to the codes in the file building
+ * the "string" table on-the-fly; requiring no table to be stored in the
+ * compressed file. The tables used herein are shared with those of the
+ * compress() routine. See the definitions above.
+ */
+static int
+zread(cookie, rbp, num)
+ void *cookie;
+ char *rbp;
+ int num;
+{
+ register u_int count;
+ struct s_zstate *zs;
+ u_char *bp, header[3];
+
+ if (num == 0)
+ return (0);
+
+ zs = cookie;
+ count = num;
+ bp = (u_char *)rbp;
+ switch (state) {
+ case S_START:
+ state = S_MIDDLE;
+ break;
+ case S_MIDDLE:
+ goto middle;
+ case S_EOF:
+ goto eof;
+ }
+
+ /* Check the magic number */
+ if (fread(header,
+ sizeof(char), sizeof(header), fp) != sizeof(header) ||
+ memcmp(header, magic_header, sizeof(magic_header)) != 0) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ maxbits = header[2]; /* Set -b from file. */
+ block_compress = maxbits & BLOCK_MASK;
+ maxbits &= BIT_MASK;
+ maxmaxcode = 1L << maxbits;
+ if (maxbits > BITS) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ /* As above, initialize the first 256 entries in the table. */
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ for (code = 255; code >= 0; code--) {
+ tab_prefixof(code) = 0;
+ tab_suffixof(code) = (char_type) code;
+ }
+ free_ent = block_compress ? FIRST : 256;
+
+ finchar = oldcode = getcode(zs);
+ if (oldcode == -1) /* EOF already? */
+ return (0); /* Get out of here */
+
+ /* First code must be 8 bits = char. */
+ *bp++ = (u_char)finchar;
+ count--;
+ stackp = de_stack;
+
+ while ((code = getcode(zs)) > -1) {
+
+ if ((code == CLEAR) && block_compress) {
+ for (code = 255; code >= 0; code--)
+ tab_prefixof(code) = 0;
+ clear_flg = 1;
+ free_ent = FIRST - 1;
+ if ((code = getcode(zs)) == -1) /* O, untimely death! */
+ break;
+ }
+ incode = code;
+
+ /* Special case for KwKwK string. */
+ if (code >= free_ent) {
+ *stackp++ = finchar;
+ code = oldcode;
+ }
+
+ /* Generate output characters in reverse order. */
+ while (code >= 256) {
+ *stackp++ = tab_suffixof(code);
+ code = tab_prefixof(code);
+ }
+ *stackp++ = finchar = tab_suffixof(code);
+
+ /* And put them out in forward order. */
+middle: do {
+ if (count-- == 0)
+ return (num);
+ *bp++ = *--stackp;
+ } while (stackp > de_stack);
+
+ /* Generate the new entry. */
+ if ((code = free_ent) < maxmaxcode) {
+ tab_prefixof(code) = (u_short) oldcode;
+ tab_suffixof(code) = finchar;
+ free_ent = code + 1;
+ }
+
+ /* Remember previous code. */
+ oldcode = incode;
+ }
+ state = S_EOF;
+eof: return (num - count);
+}
+
+/*-
+ * Read one code from the standard input. If EOF, return -1.
+ * Inputs:
+ * stdin
+ * Outputs:
+ * code or -1 is returned.
+ */
+static code_int
+getcode(zs)
+ struct s_zstate *zs;
+{
+ register code_int gcode;
+ register int r_off, bits;
+ register char_type *bp;
+
+ bp = gbuf;
+ if (clear_flg > 0 || roffset >= size || free_ent > maxcode) {
+ /*
+ * If the next entry will be too big for the current gcode
+ * size, then we must increase the size. This implies reading
+ * a new buffer full, too.
+ */
+ if (free_ent > maxcode) {
+ n_bits++;
+ if (n_bits == maxbits) /* Won't get any bigger now. */
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ if (clear_flg > 0) {
+ maxcode = MAXCODE(n_bits = INIT_BITS);
+ clear_flg = 0;
+ }
+ size = fread(gbuf, 1, n_bits, fp);
+ if (size <= 0) /* End of file. */
+ return (-1);
+ roffset = 0;
+ /* Round size down to integral number of codes. */
+ size = (size << 3) - (n_bits - 1);
+ }
+ r_off = roffset;
+ bits = n_bits;
+
+ /* Get to the first byte. */
+ bp += (r_off >> 3);
+ r_off &= 7;
+
+ /* Get first part (low order bits). */
+ gcode = (*bp++ >> r_off);
+ bits -= (8 - r_off);
+ r_off = 8 - r_off; /* Now, roffset into gcode word. */
+
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ gcode |= *bp++ << r_off;
+ r_off += 8;
+ bits -= 8;
+ }
+
+ /* High order bits. */
+ gcode |= (*bp & rmask[bits]) << r_off;
+ roffset += n_bits;
+
+ return (gcode);
+}
+
+static int
+cl_block(zs) /* Table clear for block compress. */
+ struct s_zstate *zs;
+{
+ register long rat;
+
+ checkpoint = in_count + CHECK_GAP;
+
+ if (in_count > 0x007fffff) { /* Shift will overflow. */
+ rat = bytes_out >> 8;
+ if (rat == 0) /* Don't divide by zero. */
+ rat = 0x7fffffff;
+ else
+ rat = in_count / rat;
+ } else
+ rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */
+ if (rat > ratio)
+ ratio = rat;
+ else {
+ ratio = 0;
+ cl_hash(zs, (count_int) hsize);
+ free_ent = FIRST;
+ clear_flg = 1;
+ if (output(zs, (code_int) CLEAR) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+cl_hash(zs, cl_hsize) /* Reset code table. */
+ struct s_zstate *zs;
+ register count_int cl_hsize;
+{
+ register count_int *htab_p;
+ register long i, m1;
+
+ m1 = -1;
+ htab_p = htab + cl_hsize;
+ i = cl_hsize - 16;
+ do { /* Might use Sys V memset(3) here. */
+ *(htab_p - 16) = m1;
+ *(htab_p - 15) = m1;
+ *(htab_p - 14) = m1;
+ *(htab_p - 13) = m1;
+ *(htab_p - 12) = m1;
+ *(htab_p - 11) = m1;
+ *(htab_p - 10) = m1;
+ *(htab_p - 9) = m1;
+ *(htab_p - 8) = m1;
+ *(htab_p - 7) = m1;
+ *(htab_p - 6) = m1;
+ *(htab_p - 5) = m1;
+ *(htab_p - 4) = m1;
+ *(htab_p - 3) = m1;
+ *(htab_p - 2) = m1;
+ *(htab_p - 1) = m1;
+ htab_p -= 16;
+ } while ((i -= 16) >= 0);
+ for (i += 16; i > 0; i--)
+ *--htab_p = m1;
+}
+
+FILE *
+zopen(fname, mode, bits)
+ const char *fname, *mode;
+ int bits;
+{
+ struct s_zstate *zs;
+
+ if (mode[0] != 'r' && mode[0] != 'w' || mode[1] != '\0' ||
+ bits < 0 || bits > BITS) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL)
+ return (NULL);
+
+ maxbits = bits ? bits : BITS; /* User settable max # bits/code. */
+ maxmaxcode = 1L << maxbits; /* Should NEVER generate this code. */
+ hsize = HSIZE; /* For dynamic table sizing. */
+ free_ent = 0; /* First unused entry. */
+ block_compress = BLOCK_MASK;
+ clear_flg = 0;
+ ratio = 0;
+ checkpoint = CHECK_GAP;
+ in_count = 1; /* Length of input. */
+ out_count = 0; /* # of codes output (for debugging). */
+ state = S_START;
+ roffset = 0;
+ size = 0;
+
+ /*
+ * Layering compress on top of stdio in order to provide buffering,
+ * and ensure that reads and write work with the data specified.
+ */
+ if ((fp = fopen(fname, mode)) == NULL) {
+ free(zs);
+ return (NULL);
+ }
+ switch (*mode) {
+ case 'r':
+ zmode = 'r';
+ return (funopen(zs, zread, NULL, NULL, zclose));
+ case 'w':
+ zmode = 'w';
+ return (funopen(zs, NULL, zwrite, NULL, zclose));
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/cpp/Makefile b/usr.bin/cpp/Makefile
new file mode 100644
index 0000000..eda7f17
--- /dev/null
+++ b/usr.bin/cpp/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 7/9/93
+
+NOMAN=noman
+NOOBJ=noobj
+
+all nologin clean cleandir depend lint tags:
+
+beforeinstall:
+.if ${MACHINE} == "sparc"
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/cpp.notraditional.sh ${DESTDIR}/usr/bin/cpp
+.else
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/cpp.sh ${DESTDIR}/usr/bin/cpp
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cpp/cpp.notraditional.sh b/usr.bin/cpp/cpp.notraditional.sh
new file mode 100644
index 0000000..7ed43f0
--- /dev/null
+++ b/usr.bin/cpp/cpp.notraditional.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Copyright (c) 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# the Systems Programming Group of the University of Utah Computer
+# Science Department.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)cpp.sh 8.1 (Berkeley) 6/6/93
+#
+# Transitional front end to CCCP to make it behave like (Reiser) CCP:
+# specifies -traditional
+# doesn't search gcc-include
+#
+PATH=/usr/bin:/bin
+CPP=/usr/libexec/gcc2/cpp
+ALST="-D__GNUC__=2 -$ "
+NSI=no
+OPTS=""
+INCS="-nostdinc"
+FOUNDFILES=no
+
+for A
+do
+ case $A in
+ -nostdinc)
+ NSI=yes
+ ;;
+ -traditional)
+ ;;
+ -I*)
+ INCS="$INCS $A"
+ ;;
+ -U__GNUC__)
+ ALST=`echo $ALST | sed -e 's/-D__GNUC__=2//'`
+ ;;
+ -*)
+ OPTS="$OPTS '$A'"
+ ;;
+ *)
+ FOUNDFILES=yes
+ if [ $NSI = "no" ]
+ then
+ INCS="$INCS -I/usr/include"
+ NSI=skip
+ fi
+ eval $CPP $ALST $INCS $LIBS $CSU $OPTS $A || exit $?
+ ;;
+ esac
+done
+
+if [ $FOUNDFILES = "no" ]
+then
+ # read standard input
+ if [ $NSI = "no" ]
+ then
+ INCS="$INCS -I/usr/include"
+ fi
+ eval exec $CPP $ALST $INCS $LIBS $CSU $OPTS
+fi
+
+exit 0
diff --git a/usr.bin/cpp/cpp.sh b/usr.bin/cpp/cpp.sh
new file mode 100644
index 0000000..34aa29e
--- /dev/null
+++ b/usr.bin/cpp/cpp.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# the Systems Programming Group of the University of Utah Computer
+# Science Department.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# From: @(#)cpp.sh 8.1 (Berkeley) 6/6/93
+# $Id: cpp.sh,v 1.3 1994/08/23 03:52:40 jkh Exp $
+#
+# Transitional front end to CCCP to make it behave like (Reiser) CCP:
+# specifies -traditional
+# doesn't search gcc-include
+#
+PATH=/usr/bin:/bin
+CPP=/usr/libexec/cpp
+ALST="-traditional -D__GNUC__=2 -$ "
+NSI=no
+OPTS=""
+INCS="-nostdinc"
+FOUNDFILES=no
+
+for A
+do
+ case $A in
+ -nostdinc)
+ NSI=yes
+ ;;
+ -traditional)
+ ;;
+ -I*)
+ INCS="$INCS $A"
+ ;;
+ -U__GNUC__)
+ ALST=`echo $ALST | sed -e 's/-D__GNUC__=2//'`
+ ;;
+ -*)
+ OPTS="$OPTS '$A'"
+ ;;
+ *)
+ FOUNDFILES=yes
+ if [ $NSI = "no" ]
+ then
+ INCS="$INCS -I/usr/include"
+ NSI=skip
+ fi
+ eval $CPP $ALST $INCS $LIBS $CSU $OPTS $A || exit $?
+ ;;
+ esac
+done
+
+if [ $FOUNDFILES = "no" ]
+then
+ # read standard input
+ if [ $NSI = "no" ]
+ then
+ INCS="$INCS -I/usr/include"
+ fi
+ eval exec $CPP $ALST $INCS $LIBS $CSU $OPTS
+fi
+
+exit 0
diff --git a/usr.bin/ctags/C.c b/usr.bin/ctags/C.c
new file mode 100644
index 0000000..de83f78
--- /dev/null
+++ b/usr.bin/ctags/C.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)C.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ctags.h"
+
+static int func_entry __P((void));
+static void hash_entry __P((void));
+static void skip_string __P((int));
+static int str_entry __P((int));
+
+/*
+ * c_entries --
+ * read .c and .h files and call appropriate routines
+ */
+void
+c_entries()
+{
+ int c; /* current character */
+ int level; /* brace level */
+ int token; /* if reading a token */
+ int t_def; /* if reading a typedef */
+ int t_level; /* typedef's brace level */
+ char *sp; /* buffer pointer */
+ char tok[MAXTOKEN]; /* token buffer */
+
+ lineftell = ftell(inf);
+ sp = tok; token = t_def = NO; t_level = -1; level = 0; lineno = 1;
+ while (GETC(!=, EOF)) {
+ switch (c) {
+ /*
+ * Here's where it DOESN'T handle: {
+ * foo(a)
+ * {
+ * #ifdef notdef
+ * }
+ * #endif
+ * if (a)
+ * puts("hello, world");
+ * }
+ */
+ case '{':
+ ++level;
+ goto endtok;
+ case '}':
+ /*
+ * if level goes below zero, try and fix
+ * it, even though we've already messed up
+ */
+ if (--level < 0)
+ level = 0;
+ goto endtok;
+
+ case '\n':
+ SETLINE;
+ /*
+ * the above 3 cases are similar in that they
+ * are special characters that also end tokens.
+ */
+ endtok: if (sp > tok) {
+ *sp = EOS;
+ token = YES;
+ sp = tok;
+ }
+ else
+ token = NO;
+ continue;
+
+ /*
+ * We ignore quoted strings and character constants
+ * completely.
+ */
+ case '"':
+ case '\'':
+ (void)skip_string(c);
+ break;
+
+ /*
+ * comments can be fun; note the state is unchanged after
+ * return, in case we found:
+ * "foo() XX comment XX { int bar; }"
+ */
+ case '/':
+ if (GETC(==, '*')) {
+ skip_comment();
+ continue;
+ }
+ (void)ungetc(c, inf);
+ c = '/';
+ goto storec;
+
+ /* hash marks flag #define's. */
+ case '#':
+ if (sp == tok) {
+ hash_entry();
+ break;
+ }
+ goto storec;
+
+ /*
+ * if we have a current token, parenthesis on
+ * level zero indicates a function.
+ */
+ case '(':
+ if (!level && token) {
+ int curline;
+
+ if (sp != tok)
+ *sp = EOS;
+ /*
+ * grab the line immediately, we may
+ * already be wrong, for example,
+ * foo\n
+ * (arg1,
+ */
+ getline();
+ curline = lineno;
+ if (func_entry()) {
+ ++level;
+ pfnote(tok, curline);
+ }
+ break;
+ }
+ goto storec;
+
+ /*
+ * semi-colons indicate the end of a typedef; if we find a
+ * typedef we search for the next semi-colon of the same
+ * level as the typedef. Ignoring "structs", they are
+ * tricky, since you can find:
+ *
+ * "typedef long time_t;"
+ * "typedef unsigned int u_int;"
+ * "typedef unsigned int u_int [10];"
+ *
+ * If looking at a typedef, we save a copy of the last token
+ * found. Then, when we find the ';' we take the current
+ * token if it starts with a valid token name, else we take
+ * the one we saved. There's probably some reasonable
+ * alternative to this...
+ */
+ case ';':
+ if (t_def && level == t_level) {
+ t_def = NO;
+ getline();
+ if (sp != tok)
+ *sp = EOS;
+ pfnote(tok, lineno);
+ break;
+ }
+ goto storec;
+
+ /*
+ * store characters until one that can't be part of a token
+ * comes along; check the current token against certain
+ * reserved words.
+ */
+ default:
+ storec: if (!intoken(c)) {
+ if (sp == tok)
+ break;
+ *sp = EOS;
+ if (tflag) {
+ /* no typedefs inside typedefs */
+ if (!t_def &&
+ !memcmp(tok, "typedef",8)) {
+ t_def = YES;
+ t_level = level;
+ break;
+ }
+ /* catch "typedef struct" */
+ if ((!t_def || t_level < level)
+ && (!memcmp(tok, "struct", 7)
+ || !memcmp(tok, "union", 6)
+ || !memcmp(tok, "enum", 5))) {
+ /*
+ * get line immediately;
+ * may change before '{'
+ */
+ getline();
+ if (str_entry(c))
+ ++level;
+ break;
+ /* } */
+ }
+ }
+ sp = tok;
+ }
+ else if (sp != tok || begtoken(c)) {
+ *sp++ = c;
+ token = YES;
+ }
+ continue;
+ }
+
+ sp = tok;
+ token = NO;
+ }
+}
+
+/*
+ * func_entry --
+ * handle a function reference
+ */
+static int
+func_entry()
+{
+ int c; /* current character */
+ int level = 0; /* for matching '()' */
+
+ /*
+ * Find the end of the assumed function declaration.
+ * Note that ANSI C functions can have type definitions so keep
+ * track of the parentheses nesting level.
+ */
+ while (GETC(!=, EOF)) {
+ switch (c) {
+ case '\'':
+ case '"':
+ /* skip strings and character constants */
+ skip_string(c);
+ break;
+ case '/':
+ /* skip comments */
+ if (GETC(==, '*'))
+ skip_comment();
+ break;
+ case '(':
+ level++;
+ break;
+ case ')':
+ if (level == 0)
+ goto fnd;
+ level--;
+ break;
+ case '\n':
+ SETLINE;
+ }
+ }
+ return (NO);
+fnd:
+ /*
+ * we assume that the character after a function's right paren
+ * is a token character if it's a function and a non-token
+ * character if it's a declaration. Comments don't count...
+ */
+ for (;;) {
+ while (GETC(!=, EOF) && iswhite(c))
+ if (c == '\n')
+ SETLINE;
+ if (intoken(c) || c == '{')
+ break;
+ if (c == '/' && GETC(==, '*'))
+ skip_comment();
+ else { /* don't ever "read" '/' */
+ (void)ungetc(c, inf);
+ return (NO);
+ }
+ }
+ if (c != '{')
+ (void)skip_key('{');
+ return (YES);
+}
+
+/*
+ * hash_entry --
+ * handle a line starting with a '#'
+ */
+static void
+hash_entry()
+{
+ int c; /* character read */
+ int curline; /* line started on */
+ char *sp; /* buffer pointer */
+ char tok[MAXTOKEN]; /* storage buffer */
+
+ curline = lineno;
+ for (sp = tok;;) { /* get next token */
+ if (GETC(==, EOF))
+ return;
+ if (iswhite(c))
+ break;
+ *sp++ = c;
+ }
+ *sp = EOS;
+ if (memcmp(tok, "define", 6)) /* only interested in #define's */
+ goto skip;
+ for (;;) { /* this doesn't handle "#define \n" */
+ if (GETC(==, EOF))
+ return;
+ if (!iswhite(c))
+ break;
+ }
+ for (sp = tok;;) { /* get next token */
+ *sp++ = c;
+ if (GETC(==, EOF))
+ return;
+ /*
+ * this is where it DOESN'T handle
+ * "#define \n"
+ */
+ if (!intoken(c))
+ break;
+ }
+ *sp = EOS;
+ if (dflag || c == '(') { /* only want macros */
+ getline();
+ pfnote(tok, curline);
+ }
+skip: if (c == '\n') { /* get rid of rest of define */
+ SETLINE
+ if (*(sp - 1) != '\\')
+ return;
+ }
+ (void)skip_key('\n');
+}
+
+/*
+ * str_entry --
+ * handle a struct, union or enum entry
+ */
+static int
+str_entry(c)
+ int c; /* current character */
+{
+ int curline; /* line started on */
+ char *sp; /* buffer pointer */
+ char tok[LINE_MAX]; /* storage buffer */
+
+ curline = lineno;
+ while (iswhite(c))
+ if (GETC(==, EOF))
+ return (NO);
+ if (c == '{') /* it was "struct {" */
+ return (YES);
+ for (sp = tok;;) { /* get next token */
+ *sp++ = c;
+ if (GETC(==, EOF))
+ return (NO);
+ if (!intoken(c))
+ break;
+ }
+ switch (c) {
+ case '{': /* it was "struct foo{" */
+ --sp;
+ break;
+ case '\n': /* it was "struct foo\n" */
+ SETLINE;
+ /*FALLTHROUGH*/
+ default: /* probably "struct foo " */
+ while (GETC(!=, EOF))
+ if (!iswhite(c))
+ break;
+ if (c != '{') {
+ (void)ungetc(c, inf);
+ return (NO);
+ }
+ }
+ *sp = EOS;
+ pfnote(tok, curline);
+ return (YES);
+}
+
+/*
+ * skip_comment --
+ * skip over comment
+ */
+void
+skip_comment()
+{
+ int c; /* character read */
+ int star; /* '*' flag */
+
+ for (star = 0; GETC(!=, EOF);)
+ switch(c) {
+ /* comments don't nest, nor can they be escaped. */
+ case '*':
+ star = YES;
+ break;
+ case '/':
+ if (star)
+ return;
+ break;
+ case '\n':
+ SETLINE;
+ /*FALLTHROUGH*/
+ default:
+ star = NO;
+ break;
+ }
+}
+
+/*
+ * skip_string --
+ * skip to the end of a string or character constant.
+ */
+void
+skip_string(key)
+ int key;
+{
+ int c,
+ skip;
+
+ for (skip = NO; GETC(!=, EOF); )
+ switch (c) {
+ case '\\': /* a backslash escapes anything */
+ skip = !skip; /* we toggle in case it's "\\" */
+ break;
+ case '\n':
+ SETLINE;
+ /*FALLTHROUGH*/
+ default:
+ if (c == key && !skip)
+ return;
+ skip = NO;
+ }
+}
+
+/*
+ * skip_key --
+ * skip to next char "key"
+ */
+int
+skip_key(key)
+ int key;
+{
+ int c,
+ skip,
+ retval;
+
+ for (skip = retval = NO; GETC(!=, EOF);)
+ switch(c) {
+ case '\\': /* a backslash escapes anything */
+ skip = !skip; /* we toggle in case it's "\\" */
+ break;
+ case ';': /* special case for yacc; if one */
+ case '|': /* of these chars occurs, we may */
+ retval = YES; /* have moved out of the rule */
+ break; /* not used by C */
+ case '\'':
+ case '"':
+ /* skip strings and character constants */
+ skip_string(c);
+ break;
+ case '/':
+ /* skip comments */
+ if (GETC(==, '*')) {
+ skip_comment();
+ break;
+ }
+ (void)ungetc(c, inf);
+ c = '/';
+ goto norm;
+ case '\n':
+ SETLINE;
+ /*FALLTHROUGH*/
+ default:
+ norm:
+ if (c == key && !skip)
+ return (retval);
+ skip = NO;
+ }
+ return (retval);
+}
diff --git a/usr.bin/ctags/Makefile b/usr.bin/ctags/Makefile
new file mode 100644
index 0000000..7a8ebce
--- /dev/null
+++ b/usr.bin/ctags/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ctags
+CFLAGS+=-I${.CURDIR}
+SRCS= C.c ctags.c fortran.c lisp.c print.c tree.c yacc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ctags/ctags.1 b/usr.bin/ctags/ctags.1
new file mode 100644
index 0000000..d11dbbd
--- /dev/null
+++ b/usr.bin/ctags/ctags.1
@@ -0,0 +1,214 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ctags.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CTAGS 1
+.Os BSD 4
+.Sh NAME
+.Nm ctags
+.Nd create a tags file
+.Sh SYNOPSIS
+.Nm ctags
+.Op Fl BFadtuwvx
+.Op Fl f Ar tagsfile
+.Ar name ...
+.Sh DESCRIPTION
+.Nm Ctags
+makes a tags file for
+.Xr ex 1
+from the specified C,
+Pascal, Fortran,
+.Tn YACC ,
+lex, and lisp sources.
+A tags file gives the locations of specified objects in a group of files.
+Each line of the tags file contains the object name, the file in which it
+is defined, and a search pattern for the object definition, separated by
+white-space.
+Using the
+.Ar tags
+file,
+.Xr ex 1
+can quickly locate these object definitions.
+Depending upon the options provided to
+.Nm ctags ,
+objects will consist of subroutines, typedefs, defines, structs,
+enums and unions.
+.Bl -tag -width Ds
+.It Fl B
+use backward searching patterns
+.Pq Li ?...? .
+.It Fl F
+use forward searching patterns
+.Pq Li /.../
+(the default).
+.It Fl a
+append to
+.Ar tags
+file.
+.It Fl d
+create tags for
+.Li #defines
+that don't take arguments;
+.Li #defines
+that take arguments are tagged automatically.
+.It Fl f
+Places the tag descriptions in a file called
+.Ar tagsfile .
+The default behaviour is to place them in a file called
+.Ar tags .
+.It Fl t
+create tags for typedefs, structs, unions, and enums.
+.It Fl u
+update the specified files in the
+.Ar tags
+file, that is, all
+references to them are deleted, and the new values are appended to the
+file. (Beware: this option is implemented in a way which is rather
+slow; it is usually faster to simply rebuild the
+.Ar tags
+file.)
+.It Fl v
+An index of the form expected by
+.Xr vgrind 1
+is produced on the standard output. This listing
+contains the object name, file name, and page number (assuming 64
+line pages). Since the output will be sorted into lexicographic order,
+it may be desired to run the output through
+.Xr sort 1 .
+Sample use:
+.Bd -literal -offset indent
+ctags \-v files \&| sort \-f > index
+vgrind \-x index
+.Ed
+.It Fl w
+suppress warning diagnostics.
+.It Fl x
+.Nm ctags
+produces a list of object
+names, the line number and file name on which each is defined, as well
+as the text of that line and prints this on the standard output. This
+is a simple index which can be printed out as an off-line readable
+function index.
+.El
+.Pp
+Files whose names end in
+.Nm \&.c
+or
+.Nm \&.h
+are assumed to be C
+source files and are searched for C style routine and macro definitions.
+Files whose names end in
+.Nm \&.y
+are assumed to be
+.Tn YACC
+source files.
+Files whose names end in
+.Nm \&.l
+are assumed to be lisp files if their
+first non-blank character is `;', `(', or `[',
+otherwise, they are
+treated as lex files. Other files are first examined to see if they
+contain any Pascal or Fortran routine definitions, and, if not, are
+searched for C style definitions.
+.Pp
+The tag
+.Li main
+is treated specially in C programs. The tag formed
+is created by prepending
+.Ar M
+to the name of the file, with the
+trailing
+.Nm \&.c
+and any leading pathname components removed. This
+makes use of
+.Nm ctags
+practical in directories with more than one
+program.
+.Pp
+Yacc and lex files each have a special tag.
+.Ar Yyparse
+is the start
+of the second section of the yacc file, and
+.Ar yylex
+is the start of
+the second section of the lex file.
+.Sh FILES
+.Bl -tag -width tags -compact
+.It Pa tags
+default output tags file
+.El
+.Sh DIAGNOSTICS
+.Nm Ctags
+exits with a value of 1 if an error occurred, 0 otherwise.
+Duplicate objects are not considered errors.
+.Sh SEE ALSO
+.Xr ex 1 ,
+.Xr vi 1
+.Sh BUGS
+Recognition of
+.Nm functions ,
+.Nm subroutines
+and
+.Nm procedures
+for
+.Tn FORTRAN
+and Pascal is done is a very simpleminded way. No attempt
+is made to deal with block structure; if you have two Pascal procedures
+in different blocks with the same name you lose.
+.Nm Ctags
+doesn't
+understand about Pascal types.
+.Pp
+The method of deciding whether to look for C, Pascal or
+.Tn FORTRAN
+functions is a hack.
+.Pp
+.Nm Ctags
+relies on the input being well formed, and any syntactical
+errors will completely confuse it. It also finds some legal syntax
+confusing; for example, since it doesn't understand
+.Li #ifdef Ns 's
+(incidentally, that's a feature, not a bug), any code with unbalanced
+braces inside
+.Li #ifdef Ns 's
+will cause it to become somewhat disoriented.
+In a similar fashion, multiple line changes within a definition will
+cause it to enter the last line of the object, rather than the first, as
+the searching pattern. The last line of multiple line
+.Li typedef Ns 's
+will similarly be noted.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/ctags/ctags.c b/usr.bin/ctags/ctags.c
new file mode 100644
index 0000000..b2e2330
--- /dev/null
+++ b/usr.bin/ctags/ctags.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ctags.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ctags.h"
+
+/*
+ * ctags: create a tags file
+ */
+
+NODE *head; /* head of the sorted binary tree */
+
+ /* boolean "func" (see init()) */
+bool _wht[256], _etk[256], _itk[256], _btk[256], _gd[256];
+
+FILE *inf; /* ioptr for current input file */
+FILE *outf; /* ioptr for tags file */
+
+long lineftell; /* ftell after getc( inf ) == '\n' */
+
+int lineno; /* line number of current line */
+int dflag; /* -d: non-macro defines */
+int tflag; /* -t: create tags for typedefs */
+int vflag; /* -v: vgrind style index output */
+int wflag; /* -w: suppress warnings */
+int xflag; /* -x: cxref style output */
+
+char *curfile; /* current input file name */
+char searchar = '/'; /* use /.../ searches by default */
+char lbuf[LINE_MAX];
+
+void init __P((void));
+void find_entries __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ static char *outfile = "tags"; /* output file */
+ int aflag; /* -a: append to tags */
+ int uflag; /* -u: update tags */
+ int exit_val; /* exit value */
+ int step; /* step through args */
+ int ch; /* getopts char */
+ char cmd[100]; /* too ugly to explain */
+
+ aflag = uflag = NO;
+ while ((ch = getopt(argc, argv, "BFadf:tuwvx")) != EOF)
+ switch(ch) {
+ case 'B':
+ searchar = '?';
+ break;
+ case 'F':
+ searchar = '/';
+ break;
+ case 'a':
+ aflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'f':
+ outfile = optarg;
+ break;
+ case 't':
+ tflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'w':
+ wflag++;
+ break;
+ case 'v':
+ vflag++;
+ case 'x':
+ xflag++;
+ break;
+ case '?':
+ default:
+ goto usage;
+ }
+ argv += optind;
+ argc -= optind;
+ if (!argc) {
+usage: (void)fprintf(stderr,
+ "usage: ctags [-BFadtuwvx] [-f tagsfile] file ...");
+ exit(1);
+ }
+
+ init();
+
+ for (exit_val = step = 0; step < argc; ++step)
+ if (!(inf = fopen(argv[step], "r"))) {
+ warn("%s", argv[step]);
+ exit_val = 1;
+ }
+ else {
+ curfile = argv[step];
+ find_entries(argv[step]);
+ (void)fclose(inf);
+ }
+
+ if (head)
+ if (xflag)
+ put_entries(head);
+ else {
+ if (uflag) {
+ for (step = 0; step < argc; step++) {
+ (void)sprintf(cmd,
+ "mv %s OTAGS; fgrep -v '\t%s\t' OTAGS >%s; rm OTAGS",
+ outfile, argv[step],
+ outfile);
+ system(cmd);
+ }
+ ++aflag;
+ }
+ if (!(outf = fopen(outfile, aflag ? "a" : "w")))
+ err(exit_val, "%s", outfile);
+ put_entries(head);
+ (void)fclose(outf);
+ if (uflag) {
+ (void)sprintf(cmd, "sort -o %s %s",
+ outfile, outfile);
+ system(cmd);
+ }
+ }
+ exit(exit_val);
+}
+
+/*
+ * init --
+ * this routine sets up the boolean psuedo-functions which work by
+ * setting boolean flags dependent upon the corresponding character.
+ * Every char which is NOT in that string is false with respect to
+ * the pseudo-function. Therefore, all of the array "_wht" is NO
+ * by default and then the elements subscripted by the chars in
+ * CWHITE are set to YES. Thus, "_wht" of a char is YES if it is in
+ * the string CWHITE, else NO.
+ */
+void
+init()
+{
+ int i;
+ unsigned char *sp;
+
+ for (i = 0; i < 256; i++) {
+ _wht[i] = _etk[i] = _itk[i] = _btk[i] = NO;
+ _gd[i] = YES;
+ }
+#define CWHITE " \f\t\n"
+ for (sp = CWHITE; *sp; sp++) /* white space chars */
+ _wht[*sp] = YES;
+#define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
+ for (sp = CTOKEN; *sp; sp++) /* token ending chars */
+ _etk[*sp] = YES;
+#define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
+ for (sp = CINTOK; *sp; sp++) /* valid in-token chars */
+ _itk[*sp] = YES;
+#define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
+ for (sp = CBEGIN; *sp; sp++) /* token starting chars */
+ _btk[*sp] = YES;
+#define CNOTGD ",;"
+ for (sp = CNOTGD; *sp; sp++) /* invalid after-function chars */
+ _gd[*sp] = NO;
+}
+
+/*
+ * find_entries --
+ * this routine opens the specified file and calls the function
+ * which searches the file.
+ */
+void
+find_entries(file)
+ char *file;
+{
+ char *cp;
+
+ lineno = 0; /* should be 1 ?? KB */
+ if (cp = strrchr(file, '.')) {
+ if (cp[1] == 'l' && !cp[2]) {
+ int c;
+
+ for (;;) {
+ if (GETC(==, EOF))
+ return;
+ if (!iswhite(c)) {
+ rewind(inf);
+ break;
+ }
+ }
+#define LISPCHR ";(["
+/* lisp */ if (strchr(LISPCHR, c)) {
+ l_entries();
+ return;
+ }
+/* lex */ else {
+ /*
+ * we search all 3 parts of a lex file
+ * for C references. This may be wrong.
+ */
+ toss_yysec();
+ (void)strcpy(lbuf, "%%$");
+ pfnote("yylex", lineno);
+ rewind(inf);
+ }
+ }
+/* yacc */ else if (cp[1] == 'y' && !cp[2]) {
+ /*
+ * we search only the 3rd part of a yacc file
+ * for C references. This may be wrong.
+ */
+ toss_yysec();
+ (void)strcpy(lbuf, "%%$");
+ pfnote("yyparse", lineno);
+ y_entries();
+ }
+/* fortran */ else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) {
+ if (PF_funcs())
+ return;
+ rewind(inf);
+ }
+ }
+/* C */ c_entries();
+}
diff --git a/usr.bin/ctags/ctags.h b/usr.bin/ctags/ctags.h
new file mode 100644
index 0000000..a42c68a
--- /dev/null
+++ b/usr.bin/ctags/ctags.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ctags.h 8.3 (Berkeley) 4/2/94
+ */
+
+#define bool char
+
+#define YES 1
+#define NO 0
+#define EOS '\0'
+
+#define ENDLINE 50 /* max length of pattern */
+#define MAXTOKEN 250 /* max size of single token */
+
+#define SETLINE {++lineno;lineftell = ftell(inf);}
+#define GETC(op,exp) ((c = getc(inf)) op (int)exp)
+
+#define iswhite(arg) (_wht[(unsigned)arg]) /* T if char is white */
+#define begtoken(arg) (_btk[(unsigned)arg]) /* T if char can start token */
+#define intoken(arg) (_itk[(unsigned)arg]) /* T if char can be in token */
+#define endtoken(arg) (_etk[(unsigned)arg]) /* T if char ends tokens */
+#define isgood(arg) (_gd[(unsigned)arg]) /* T if char can be after ')' */
+
+typedef struct nd_st { /* sorting structure */
+ struct nd_st *left,
+ *right; /* left and right sons */
+ char *entry, /* function or type name */
+ *file, /* file name */
+ *pat; /* search pattern */
+ int lno; /* for -x option */
+ bool been_warned; /* set if noticed dup */
+} NODE;
+
+extern char *curfile; /* current input file name */
+extern NODE *head; /* head of the sorted binary tree */
+extern FILE *inf; /* ioptr for current input file */
+extern FILE *outf; /* ioptr for current output file */
+extern long lineftell; /* ftell after getc( inf ) == '\n' */
+extern int lineno; /* line number of current line */
+extern int dflag; /* -d: non-macro defines */
+extern int tflag; /* -t: create tags for typedefs */
+extern int vflag; /* -v: vgrind style index output */
+extern int wflag; /* -w: suppress warnings */
+extern int xflag; /* -x: cxref style output */
+extern bool _wht[], _etk[], _itk[], _btk[], _gd[];
+extern char lbuf[LINE_MAX];
+extern char *lbp;
+extern char searchar; /* ex search character */
+
+extern int cicmp __P((char *));
+extern void getline __P((void));
+extern void pfnote __P((char *, int));
+extern int skip_key __P((int));
+extern void put_entries __P((NODE *));
+extern void toss_yysec __P((void));
+extern void l_entries __P((void));
+extern void y_entries __P((void));
+extern int PF_funcs __P((void));
+extern void c_entries __P((void));
+extern void skip_comment __P((void));
diff --git a/usr.bin/ctags/fortran.c b/usr.bin/ctags/fortran.c
new file mode 100644
index 0000000..d0db9ed
--- /dev/null
+++ b/usr.bin/ctags/fortran.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)fortran.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ctags.h"
+
+static void takeprec __P((void));
+
+char *lbp; /* line buffer pointer */
+
+int
+PF_funcs()
+{
+ bool pfcnt; /* pascal/fortran functions found */
+ char *cp;
+ char tok[MAXTOKEN];
+
+ for (pfcnt = NO;;) {
+ lineftell = ftell(inf);
+ if (!fgets(lbuf, sizeof(lbuf), inf))
+ return (pfcnt);
+ ++lineno;
+ lbp = lbuf;
+ if (*lbp == '%') /* Ratfor escape to fortran */
+ ++lbp;
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ if (!*lbp)
+ continue;
+ switch (*lbp | ' ') { /* convert to lower-case */
+ case 'c':
+ if (cicmp("complex") || cicmp("character"))
+ takeprec();
+ break;
+ case 'd':
+ if (cicmp("double")) {
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ if (!*lbp)
+ continue;
+ if (cicmp("precision"))
+ break;
+ continue;
+ }
+ break;
+ case 'i':
+ if (cicmp("integer"))
+ takeprec();
+ break;
+ case 'l':
+ if (cicmp("logical"))
+ takeprec();
+ break;
+ case 'r':
+ if (cicmp("real"))
+ takeprec();
+ break;
+ }
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ if (!*lbp)
+ continue;
+ switch (*lbp | ' ') {
+ case 'f':
+ if (cicmp("function"))
+ break;
+ continue;
+ case 'p':
+ if (cicmp("program") || cicmp("procedure"))
+ break;
+ continue;
+ case 's':
+ if (cicmp("subroutine"))
+ break;
+ default:
+ continue;
+ }
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ if (!*lbp)
+ continue;
+ for (cp = lbp + 1; *cp && intoken(*cp); ++cp)
+ continue;
+ if (cp = lbp + 1)
+ continue;
+ *cp = EOS;
+ (void)strcpy(tok, lbp);
+ getline(); /* process line for ex(1) */
+ pfnote(tok, lineno);
+ pfcnt = YES;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * cicmp --
+ * do case-independent strcmp
+ */
+int
+cicmp(cp)
+ char *cp;
+{
+ int len;
+ char *bp;
+
+ for (len = 0, bp = lbp; *cp && (*cp &~ ' ') == (*bp++ &~ ' ');
+ ++cp, ++len)
+ continue;
+ if (!*cp) {
+ lbp += len;
+ return (YES);
+ }
+ return (NO);
+}
+
+static void
+takeprec()
+{
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ if (*lbp == '*') {
+ for (++lbp; isspace(*lbp); ++lbp)
+ continue;
+ if (!isdigit(*lbp))
+ --lbp; /* force failure */
+ else
+ while (isdigit(*++lbp))
+ continue;
+ }
+}
diff --git a/usr.bin/ctags/lisp.c b/usr.bin/ctags/lisp.c
new file mode 100644
index 0000000..5f99984
--- /dev/null
+++ b/usr.bin/ctags/lisp.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lisp.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ctags.h"
+
+/*
+ * lisp tag functions
+ * just look for (def or (DEF
+ */
+void
+l_entries()
+{
+ int special;
+ char *cp;
+ char savedc;
+ char tok[MAXTOKEN];
+
+ for (;;) {
+ lineftell = ftell(inf);
+ if (!fgets(lbuf, sizeof(lbuf), inf))
+ return;
+ ++lineno;
+ lbp = lbuf;
+ if (!cicmp("(def"))
+ continue;
+ special = NO;
+ switch(*lbp | ' ') {
+ case 'm':
+ if (cicmp("method"))
+ special = YES;
+ break;
+ case 'w':
+ if (cicmp("wrapper") || cicmp("whopper"))
+ special = YES;
+ }
+ for (; !isspace(*lbp); ++lbp)
+ continue;
+ for (; isspace(*lbp); ++lbp)
+ continue;
+ for (cp = lbp; *cp && *cp != '\n'; ++cp)
+ continue;
+ *cp = EOS;
+ if (special) {
+ if (!(cp = strchr(lbp, ')')))
+ continue;
+ for (; cp >= lbp && *cp != ':'; --cp)
+ continue;
+ if (cp < lbp)
+ continue;
+ lbp = cp;
+ for (; *cp && *cp != ')' && *cp != ' '; ++cp)
+ continue;
+ }
+ else
+ for (cp = lbp + 1;
+ *cp && *cp != '(' && *cp != ' '; ++cp)
+ continue;
+ savedc = *cp;
+ *cp = EOS;
+ (void)strcpy(tok, lbp);
+ *cp = savedc;
+ getline();
+ pfnote(tok, lineno);
+ }
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/ctags/print.c b/usr.bin/ctags/print.c
new file mode 100644
index 0000000..b2c313a
--- /dev/null
+++ b/usr.bin/ctags/print.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)print.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ctags.h"
+
+/*
+ * getline --
+ * get the line the token of interest occurred on,
+ * prepare it for printing.
+ */
+void
+getline()
+{
+ long saveftell;
+ int c;
+ int cnt;
+ char *cp;
+
+ saveftell = ftell(inf);
+ (void)fseek(inf, lineftell, L_SET);
+ if (xflag)
+ for (cp = lbuf; GETC(!=, '\n'); *cp++ = c)
+ continue;
+ /*
+ * do all processing here, so we don't step through the
+ * line more than once; means you don't call this routine
+ * unless you're sure you've got a keeper.
+ */
+ else for (cnt = 0, cp = lbuf; GETC(!=, EOF) && cnt < ENDLINE; ++cnt) {
+ if (c == '\\') { /* backslashes */
+ if (cnt > ENDLINE - 2)
+ break;
+ *cp++ = '\\'; *cp++ = '\\';
+ ++cnt;
+ }
+ else if (c == (int)searchar) { /* search character */
+ if (cnt > ENDLINE - 2)
+ break;
+ *cp++ = '\\'; *cp++ = c;
+ ++cnt;
+ }
+ else if (c == '\n') { /* end of keep */
+ *cp++ = '$'; /* can find whole line */
+ break;
+ }
+ else
+ *cp++ = c;
+ }
+ *cp = EOS;
+ (void)fseek(inf, saveftell, L_SET);
+}
+
+/*
+ * put_entries --
+ * write out the tags
+ */
+void
+put_entries(node)
+ NODE *node;
+{
+
+ if (node->left)
+ put_entries(node->left);
+ if (vflag)
+ printf("%s %s %d\n",
+ node->entry, node->file, (node->lno + 63) / 64);
+ else if (xflag)
+ printf("%-16s%4d %-16s %s\n",
+ node->entry, node->lno, node->file, node->pat);
+ else
+ fprintf(outf, "%s\t%s\t%c^%s%c\n",
+ node->entry, node->file, searchar, node->pat, searchar);
+ if (node->right)
+ put_entries(node->right);
+}
diff --git a/usr.bin/ctags/test/ctags.test b/usr.bin/ctags/test/ctags.test
new file mode 100644
index 0000000..1f334ac
--- /dev/null
+++ b/usr.bin/ctags/test/ctags.test
@@ -0,0 +1,67 @@
+int bar = (1 + 5);
+
+FOO("here is a #define test: ) {");
+char sysent[20];
+int nsysent = sizeof (sysent) / sizeof (sysent[0]);
+/*
+ * now is the time for a comment.
+ * four lines in length...
+ */struct struct_xtra{int list;};r4(x,y){};typedef struct{int bar;}struct_xxe;
+#define FOO BAR
+struct struct_three {
+ int list;
+};
+#define SINGLE
+int BAD();
+enum color {red, green, gold, brown};
+char qq[] = " quote(one,two) {int bar;} ";
+typedef struct {
+ int bar;
+ struct struct_two {
+ int foo;
+ union union_3 {
+ struct struct_three entry;
+ char size[25];
+ };
+ struct last {
+ struct struct_three xentry;
+ char list[34];
+ };
+ };
+} struct_one;
+#define TWOLINE ((MAXLIST + FUTURE + 15) \
+ / (time_to_live ? 3 : 4))
+#if (defined(BAR))
+int bar;
+#endif
+#define MULTIPLE {\
+ multiple(one,two); \
+ lineno++; \
+ callroute(one,two); \
+}
+#if defined(BAR)
+int bar;
+#endif
+union union_one {
+ struct struct_three s3;
+ char foo[25];
+};
+#define XYZ(A,B) (A + B / 2) * (3 - 26 + l_lineno)
+routine1(one,two) /* comments here are fun... */
+ struct {
+ int entry;
+ char bar[34];
+ } *one;
+ char two[10];
+{
+typedef unsigned char u_char;
+ register struct buf *bp;
+ five(one,two);
+}
+ routine2 (one,two) { puts("hello\n"); }
+ routine3
+(one,
+two) { puts("world\n"); }
+routine4(int one, char (*two)(void)) /* test ANSI arguments */
+{
+}
diff --git a/usr.bin/ctags/tree.c b/usr.bin/ctags/tree.c
new file mode 100644
index 0000000..61decd2
--- /dev/null
+++ b/usr.bin/ctags/tree.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tree.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ctags.h"
+
+static void add_node __P((NODE *, NODE *));
+static void free_tree __P((NODE *));
+
+/*
+ * pfnote --
+ * enter a new node in the tree
+ */
+void
+pfnote(name, ln)
+ char *name;
+ int ln;
+{
+ NODE *np;
+ char *fp;
+ char nbuf[MAXTOKEN];
+
+ /*NOSTRICT*/
+ if (!(np = (NODE *)malloc(sizeof(NODE)))) {
+ warnx("too many entries to sort");
+ put_entries(head);
+ free_tree(head);
+ /*NOSTRICT*/
+ if (!(head = np = (NODE *)malloc(sizeof(NODE))))
+ err(1, "out of space");
+ }
+ if (!xflag && !strcmp(name, "main")) {
+ if (!(fp = strrchr(curfile, '/')))
+ fp = curfile;
+ else
+ ++fp;
+ (void)sprintf(nbuf, "M%s", fp);
+ fp = strrchr(nbuf, '.');
+ if (fp && !fp[2])
+ *fp = EOS;
+ name = nbuf;
+ }
+ if (!(np->entry = strdup(name)))
+ err(1, NULL);
+ np->file = curfile;
+ np->lno = ln;
+ np->left = np->right = 0;
+ if (!(np->pat = strdup(lbuf)))
+ err(1, NULL);
+ if (!head)
+ head = np;
+ else
+ add_node(np, head);
+}
+
+static void
+add_node(node, cur_node)
+ NODE *node,
+ *cur_node;
+{
+ int dif;
+
+ dif = strcmp(node->entry, cur_node->entry);
+ if (!dif) {
+ if (node->file == cur_node->file) {
+ if (!wflag)
+ fprintf(stderr, "Duplicate entry in file %s, line %d: %s\nSecond entry ignored\n", node->file, lineno, node->entry);
+ return;
+ }
+ if (!cur_node->been_warned)
+ if (!wflag)
+ fprintf(stderr, "Duplicate entry in files %s and %s: %s (Warning only)\n", node->file, cur_node->file, node->entry);
+ cur_node->been_warned = YES;
+ }
+ else if (dif < 0)
+ if (cur_node->left)
+ add_node(node, cur_node->left);
+ else
+ cur_node->left = node;
+ else if (cur_node->right)
+ add_node(node, cur_node->right);
+ else
+ cur_node->right = node;
+}
+
+static void
+free_tree(node)
+ NODE *node;
+{
+ while (node) {
+ if (node->right)
+ free_tree(node->right);
+ free(node);
+ node = node->left;
+ }
+}
diff --git a/usr.bin/ctags/yacc.c b/usr.bin/ctags/yacc.c
new file mode 100644
index 0000000..c013283
--- /dev/null
+++ b/usr.bin/ctags/yacc.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)yacc.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ctags.h"
+
+/*
+ * y_entries:
+ * find the yacc tags and put them in.
+ */
+void
+y_entries()
+{
+ int c;
+ char *sp;
+ bool in_rule;
+ char tok[MAXTOKEN];
+
+ in_rule = NO;
+
+ while (GETC(!=, EOF))
+ switch (c) {
+ case '\n':
+ SETLINE;
+ /* FALLTHROUGH */
+ case ' ':
+ case '\f':
+ case '\r':
+ case '\t':
+ break;
+ case '{':
+ if (skip_key('}'))
+ in_rule = NO;
+ break;
+ case '\'':
+ case '"':
+ if (skip_key(c))
+ in_rule = NO;
+ break;
+ case '%':
+ if (GETC(==, '%'))
+ return;
+ (void)ungetc(c, inf);
+ break;
+ case '/':
+ if (GETC(==, '*'))
+ skip_comment();
+ else
+ (void)ungetc(c, inf);
+ break;
+ case '|':
+ case ';':
+ in_rule = NO;
+ break;
+ default:
+ if (in_rule || !isalpha(c) && c != '.' && c != '_')
+ break;
+ sp = tok;
+ *sp++ = c;
+ while (GETC(!=, EOF) && (intoken(c) || c == '.'))
+ *sp++ = c;
+ *sp = EOS;
+ getline(); /* may change before ':' */
+ while (iswhite(c)) {
+ if (c == '\n')
+ SETLINE;
+ if (GETC(==, EOF))
+ return;
+ }
+ if (c == ':') {
+ pfnote(tok, lineno);
+ in_rule = YES;
+ }
+ else
+ (void)ungetc(c, inf);
+ }
+}
+
+/*
+ * toss_yysec --
+ * throw away lines up to the next "\n%%\n"
+ */
+void
+toss_yysec()
+{
+ int c; /* read character */
+ int state;
+
+ /*
+ * state == 0 : waiting
+ * state == 1 : received a newline
+ * state == 2 : received first %
+ * state == 3 : recieved second %
+ */
+ lineftell = ftell(inf);
+ for (state = 0; GETC(!=, EOF);)
+ switch (c) {
+ case '\n':
+ ++lineno;
+ lineftell = ftell(inf);
+ if (state == 3) /* done! */
+ return;
+ state = 1; /* start over */
+ break;
+ case '%':
+ if (state) /* if 1 or 2 */
+ ++state; /* goto 3 */
+ break;
+ default:
+ state = 0; /* reset */
+ break;
+ }
+}
diff --git a/usr.bin/cut/Makefile b/usr.bin/cut/Makefile
new file mode 100644
index 0000000..19b0100
--- /dev/null
+++ b/usr.bin/cut/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= cut
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/cut/cut.1 b/usr.bin/cut/cut.1
new file mode 100644
index 0000000..97c5645
--- /dev/null
+++ b/usr.bin/cut/cut.1
@@ -0,0 +1,110 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)cut.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CUT 1
+.Os
+.Sh NAME
+.Nm cut
+.Nd select portions of each line of a file
+.Sh SYNOPSIS
+.Nm cut
+.Fl c Ar list
+.Ar
+.Nm cut
+.Fl f Ar list
+.Op Fl d Ar string
+.Op Fl s
+.Ar
+.Sh DESCRIPTION
+The
+.Nm cut
+utility selects portions of each line (as specified by
+.Ar list )
+from each
+.Ar file
+(or the standard input by default), and writes them to the
+standard output.
+The items specified by
+.Ar list
+can be in terms of column position or in terms of fields delimited
+by a special character. Column numbering starts from 1.
+.Pp
+.Ar List
+is a comma or whitespace separated set of increasing numbers and/or
+number ranges.
+Number ranges consist of a number, a dash
+.Pq Li \- ,
+and a second number
+and select the fields or columns from the first number to the second,
+inclusive.
+Numbers or number ranges may be preceded by a dash, which selects all
+fields or columns from 1 to the first number.
+Numbers or number ranges may be followed by a dash, which selects all
+fields or columns from the last number to the end of the line.
+Numbers and number ranges may be repeated, overlapping, and in any order.
+It is not an error to select fields or columns not present in the
+input line.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl c Ar list
+The
+.Ar list
+specifies character positions.
+.It Fl d Ar string
+Use the first character of
+.Ar string
+as the field delimiter character instead of the tab character.
+.It Fl f Ar list
+The
+.Ar list
+specifies fields, delimited in the input by a single tab character.
+Output fields are separated by a single tab character.
+.It Fl s
+Suppresses lines with no field delimiter characters.
+Unless specified, lines with no delimiters are passed through unmodified.
+.El
+.Pp
+.Nm Cut
+exits 0 on success, 1 if an error occurred.
+.Sh SEE ALSO
+.Xr paste 1
+.Sh STANDARDS
+The
+.Nm cut
+utility is expected to conform to
+.St -p1003.2 .
diff --git a/usr.bin/cut/cut.c b/usr.bin/cut/cut.c
new file mode 100644
index 0000000..2c887ff
--- /dev/null
+++ b/usr.bin/cut/cut.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cut.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int cflag;
+char dchar;
+int dflag;
+int fflag;
+int sflag;
+
+void c_cut __P((FILE *, char *));
+void err __P((const char *, ...));
+void f_cut __P((FILE *, char *));
+void get_list __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ void (*fcn) __P((FILE *, char *));
+ int ch;
+
+ dchar = '\t'; /* default delimiter is \t */
+
+ while ((ch = getopt(argc, argv, "c:d:f:s")) != EOF)
+ switch(ch) {
+ case 'c':
+ fcn = c_cut;
+ get_list(optarg);
+ cflag = 1;
+ break;
+ case 'd':
+ dchar = *optarg;
+ dflag = 1;
+ break;
+ case 'f':
+ get_list(optarg);
+ fcn = f_cut;
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fflag) {
+ if (cflag)
+ usage();
+ } else if (!cflag || dflag || sflag)
+ usage();
+
+ if (*argv)
+ for (; *argv; ++argv) {
+ if (!(fp = fopen(*argv, "r")))
+ err("%s: %s\n", *argv, strerror(errno));
+ fcn(fp, *argv);
+ (void)fclose(fp);
+ }
+ else
+ fcn(stdin, "stdin");
+ exit(0);
+}
+
+int autostart, autostop, maxval;
+
+char positions[_POSIX2_LINE_MAX + 1];
+
+void
+get_list(list)
+ char *list;
+{
+ register int setautostart, start, stop;
+ register char *pos;
+ char *p;
+
+ /*
+ * set a byte in the positions array to indicate if a field or
+ * column is to be selected; use +1, it's 1-based, not 0-based.
+ * This parser is less restrictive than the Draft 9 POSIX spec.
+ * POSIX doesn't allow lists that aren't in increasing order or
+ * overlapping lists. We also handle "-3-5" although there's no
+ * real reason too.
+ */
+ for (; p = strtok(list, ", \t"); list = NULL) {
+ setautostart = start = stop = 0;
+ if (*p == '-') {
+ ++p;
+ setautostart = 1;
+ }
+ if (isdigit(*p)) {
+ start = stop = strtol(p, &p, 10);
+ if (setautostart && start > autostart)
+ autostart = start;
+ }
+ if (*p == '-') {
+ if (isdigit(p[1]))
+ stop = strtol(p + 1, &p, 10);
+ if (*p == '-') {
+ ++p;
+ if (!autostop || autostop > stop)
+ autostop = stop;
+ }
+ }
+ if (*p)
+ err("[-cf] list: illegal list value\n");
+ if (!stop || !start)
+ err("[-cf] list: values may not include zero\n");
+ if (stop > _POSIX2_LINE_MAX)
+ err("[-cf] list: %d too large (max %d)\n",
+ stop, _POSIX2_LINE_MAX);
+ if (maxval < stop)
+ maxval = stop;
+ for (pos = positions + start; start++ <= stop; *pos++ = 1);
+ }
+
+ /* overlapping ranges */
+ if (autostop && maxval > autostop)
+ maxval = autostop;
+
+ /* set autostart */
+ if (autostart)
+ memset(positions + 1, '1', autostart);
+}
+
+/* ARGSUSED */
+void
+c_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ register int ch, col;
+ register char *pos;
+
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getc(fp)) == EOF)
+ return;
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putchar(ch);
+ }
+ if (ch != '\n')
+ if (autostop)
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ (void)putchar(ch);
+ else
+ while ((ch = getc(fp)) != EOF && ch != '\n');
+ (void)putchar('\n');
+ }
+}
+
+void
+f_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ register int ch, field, isdelim;
+ register char *pos, *p, sep;
+ int output;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
+ output = 0;
+ for (isdelim = 0, p = lbuf;; ++p) {
+ if (!(ch = *p))
+ err("%s: line too long.\n", fname);
+ /* this should work if newline is delimiter */
+ if (ch == sep)
+ isdelim = 1;
+ if (ch == '\n') {
+ if (!isdelim && !sflag)
+ (void)printf("%s", lbuf);
+ break;
+ }
+ }
+ if (!isdelim)
+ continue;
+
+ pos = positions + 1;
+ for (field = maxval, p = lbuf; field; --field, ++pos) {
+ if (*pos) {
+ if (output++)
+ (void)putchar(sep);
+ while ((ch = *p++) != '\n' && ch != sep)
+ (void)putchar(ch);
+ } else
+ while ((ch = *p++) != '\n' && ch != sep);
+ if (ch == '\n')
+ break;
+ }
+ if (ch != '\n')
+ if (autostop) {
+ if (output)
+ (void)putchar(sep);
+ for (; (ch = *p) != '\n'; ++p)
+ (void)putchar(ch);
+ } else
+ for (; (ch = *p) != '\n'; ++p);
+ (void)putchar('\n');
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage:\tcut -c list [file1 ...]\n\tcut -f list [-s] [-d delim] [file ...]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "cut: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/devmenu/Makefile b/usr.bin/devmenu/Makefile
new file mode 100644
index 0000000..225756a
--- /dev/null
+++ b/usr.bin/devmenu/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.1 1995/04/13 21:10:56 wollman Exp $
+
+PROG= devmenu
+SRCS= devmenu.c devfilter.c ifmenu.c
+MAN1= devmenu.1
+LDADD+= -ldialog -lncurses -lmytinfo
+DPADD+= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO}
+CFLAGS+= -I/sys
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/devmenu/devfilter.c b/usr.bin/devmenu/devfilter.c
new file mode 100644
index 0000000..1493471
--- /dev/null
+++ b/usr.bin/devmenu/devfilter.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/devconf.h>
+#include <sys/proc.h>
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include "devmenu.h"
+
+char *
+devmenu_toname(const struct devconf *dev)
+{
+ static char buf[2*MAXDEVNAME];
+ snprintf(buf, sizeof buf, "%s%d", dev->dc_name, dev->dc_unit);
+ return buf;
+}
+
+int
+devmenu_filter(const struct devconf *dev, char **userlist)
+{
+ int i;
+ char *name;
+
+ if (!userlist)
+ return 1;
+
+ name = devmenu_toname(dev);
+
+ for (i = 0; userlist[i]; i++) {
+ if (strcmp(name, userlist[i]) == 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+struct devconf **
+devmenu_alldevs(void)
+{
+ int mib[3];
+ size_t size;
+ int ndevs, i, ndx;
+ struct devconf **rv;
+
+ size = sizeof ndevs;
+ mib[0] = CTL_HW;
+ mib[1] = HW_DEVCONF;
+ mib[2] = DEVCONF_NUMBER;
+
+ if (sysctl(mib, 3, &ndevs, &size, 0, 0) < 0) {
+ err(EX_OSERR, "sysctl failed reading hw.devconf.number");
+ }
+
+ rv = malloc((ndevs + 1) * sizeof *rv);
+ if (!rv) {
+ err(EX_UNAVAILABLE, "malloc(%lu)",
+ (unsigned long)(ndevs * sizeof *rv));
+ }
+
+ for (ndx = 0, i = 1; i <= ndevs; i++) {
+ mib[2] = i;
+ if (sysctl(mib, 3, 0, &size, 0, 0) < 0) {
+ continue;
+ }
+
+ rv[ndx] = malloc(size);
+ if (!rv[ndx]) {
+ err(EX_UNAVAILABLE, "malloc(%lu)",
+ (unsigned long)size);
+ }
+
+ if (sysctl(mib, 3, rv[ndx], &size, 0, 0) < 0) {
+ err(EX_OSERR, "sysctl reading hw.devconf.%d", i);
+ }
+
+ ndx++;
+ }
+
+ rv[ndx] = 0;
+
+ return rv;
+}
+
+void
+devmenu_freedevs(struct devconf ***devpp)
+{
+ struct devconf **devp = *devpp;
+ int i;
+
+ for (i = 0; devp[i]; i++) {
+ free(devp[i]);
+ }
+
+ free(devp);
+ *devpp = 0;
+}
+
+const char *
+devmenu_common(const char *title, const char *hfile, char **devnames,
+ const char *prompt, const char *none, enum dc_class class,
+ int states)
+{
+ struct devconf **devs;
+ int s;
+ unsigned char **items = 0;
+ int nitems = 0;
+ int itemindex = 0;
+ char *name;
+ int i;
+ static char resbuf[2*MAXDEVNAME];
+
+ if (hfile) {
+ use_helpfile((char *)hfile);
+ }
+ devs = devmenu_alldevs();
+
+ for (i = 0; devs[i]; i++) {
+ if (states && !((1 << devs[i]->dc_state) & states))
+ continue;
+ if (class && !(devs[i]->dc_class & class))
+ continue;
+ if (devmenu_filter(devs[i], devnames)) {
+ ++nitems;
+ items = realloc(items, 2 * nitems * sizeof *items);
+ if (!items) {
+ err(EX_UNAVAILABLE, "malloc(%lu)",
+ (unsigned long)(2 * nitems * sizeof *items));
+ }
+
+ name = devmenu_toname(devs[i]);
+
+ items[itemindex] = strdup(name);
+ if (!items[itemindex]) {
+ err(EX_UNAVAILABLE, "strdup-malloc(%lu)",
+ (unsigned long)(strlen(name) + 1));
+ }
+
+ items[itemindex + 1] = devs[i]->dc_descr;
+ itemindex += 2;
+ }
+ }
+
+ if (!nitems) {
+ dialog_msgbox((char *)title, none, -1, -1, 1);
+ return "none";
+ }
+
+ name = resbuf;
+
+ if(dialog_menu((char *)title, prompt, 24, 78, 18, nitems, items,
+ resbuf, 0, 0) != 0) {
+ name = "none";
+ }
+
+ for (i = 0; i < 2 * nitems; i += 2) {
+ free(items[i]);
+ }
+
+ free(items);
+
+ devmenu_freedevs(&devs);
+ return name;
+}
diff --git a/usr.bin/devmenu/devmenu.1 b/usr.bin/devmenu/devmenu.1
new file mode 100644
index 0000000..f277baa
--- /dev/null
+++ b/usr.bin/devmenu/devmenu.1
@@ -0,0 +1,152 @@
+.\"
+.\" Copyright 1995 Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software and
+.\" its documentation for any purpose and without fee is hereby
+.\" granted, provided that both the above copyright notice and this
+.\" permission notice appear in all copies, that both the above
+.\" copyright notice and this permission notice appear in all
+.\" supporting documentation, and that the name of M.I.T. not be used
+.\" in advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission. M.I.T. makes
+.\" no representations about the suitability of this software for any
+.\" purpose. It is provided "as is" without express or implied
+.\" warranty.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+.\" ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+.\" SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: devmenu.1,v 1.1 1995/04/13 21:10:58 wollman Exp $
+.Dd April 14, 1995
+.Dt DEVMENU 1
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm devmenu
+.Nd present a menu of available devices
+.Sh SYNOPSIS
+.Nm devmenu
+.Op Fl c Ar class
+.Op Fl f Ar outfile
+.Op Fl h Ar helpfile
+.Op Fl s Ar state
+.Op Fl t Ar title
+.Op Fl nid
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+program uses the
+.Xr dialog 3
+library to present a menu of available devices meeting some set of
+criteria. The list of devices is obtained using the
+.Xr sysctl 3
+device configuration database
+.Pq Dq devconf ,
+and intersected with the list of
+.No Ar name Ns s
+if one was specified on the command line. The following command-line
+flags are supported:
+.Bl -tag -width mhmhelpfile
+.It Fl c Ar class
+specifies a class of devices to further restrict the available
+choices. Multiple
+.Fl c
+options can be specified, in which case the union of the specified
+classes is used. The following classes are currently supported:
+.Pp
+.Bl -tag -compact -width parallel
+.It Li cpu
+a processor
+.It Li bus
+a system bus, or a peripheral bus
+.It Li disk
+a read-write disk drive
+.It Li rodisk
+a read-only disk drive (e.g., CD-ROM)
+.It Li display
+a display device (usually there is only one)
+.It Li serial
+a serial port or multiport controller
+.It Li parallel
+a parallel printer port
+.It Li netif
+a network interface
+.It Li misc
+everything else
+.El
+.Pp
+.It Fl s Ar state
+specifies a required state or set of states to further restrict the
+list of devices. States are specified using the following letters:
+.Pp
+.Bl -tag -compact -width "! or ~"
+.It Li \&! or Li \&~
+at the beginning of a string requests the complement of the specified
+states.
+.It Li \&?
+requests devices with state
+.Dv DC_UNKNOWN .
+.It Li \&u
+requests devices with state
+.Dv DC_UNCONFIGURED .
+.It Li \&i
+requests devices with state
+.Dv DC_IDLE .
+.It Li \&b
+requests devices with state
+.Dv DC_BUSY .
+.El
+.Pp
+Multiple
+.Fl s
+options can be specified, in which case the union of all the requested
+states is taken.
+.It Fl f Ar outfile
+specifies the name a file where the name of the selected device is
+written. If no file is specified, the name is written on the standard
+output.
+.It Fl h Ar helpfile
+can be used to specify the name of a file to be displayed when the
+user presses the `?' key.
+.It Fl t Ar title
+specifies the title of the window passed to the
+.Xr dialog 3
+library when creating the menu.
+.El
+.Pp
+In addition, there are three flags to request special menus with
+unique prompt text and possibly pseudo-devices (but this is not yet
+implemented). These are:
+.Bl -tag -width xxx
+.It Fl n
+requests a menu of network interfaces
+.It Fl i
+requests a menu of possible installation media (i.e., disks, CD-ROMs,
+and tapes)
+.It Fl d
+requests a menu of disks
+.El
+.Sh SEE ALSO
+.Xr sysctl 3 ,
+.Xr lsdev 8
+.Sh AUTHOR
+The
+.Nm
+program was written by Garrett A. Wollman at the MIT Laboratory for
+Computer Science.
+.Sh HISTORY
+A
+.Nm
+command first appeared in
+.Tn FreeBSD
+2.1.
diff --git a/usr.bin/devmenu/devmenu.c b/usr.bin/devmenu/devmenu.c
new file mode 100644
index 0000000..2e5c69b
--- /dev/null
+++ b/usr.bin/devmenu/devmenu.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * devmenu - present a menu of installed devices of a specified
+ * class or classes
+ *
+ * Garrett A. Wollman, April 1995
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: devmenu.c,v 1.2 1995/04/14 18:33:43 wollman Exp $";
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sys/devconf.h>
+#include <sysexits.h>
+#include <err.h>
+#include <dialog.h>
+
+#include "devmenu.h"
+
+static enum dc_class interpret_class(char *str);
+static int interpret_state(char *str);
+static void dm_err_exit(int);
+static int err_writefn(void *cookie, const char *val, int nchars);
+static void usage(const char *);
+
+static FILE *errfp;
+
+int
+main(int argc, char **argv)
+{
+ enum dc_class class = 0;
+ enum mode { GENERIC, NETIF, INSTALL, DISK } mode = GENERIC;
+ const char *title = 0, *hfile = 0;
+ char **devnames = 0;
+ int c;
+ int states = 0;
+ const char *fn = 0;
+ const char *sel = "none";
+
+ while ((c = getopt(argc, argv, "c:s:t:h:nidf:")) != EOF) {
+ switch(c) {
+ case 'c':
+ class |= interpret_class(optarg);
+ break;
+ case 's':
+ states |= interpret_state(optarg);
+ break;
+ case 't':
+ title = optarg;
+ break;
+ case 'h':
+ hfile = optarg;
+ break;
+ case 'n':
+ mode = NETIF;
+ break;
+ case 'i':
+ mode = INSTALL;
+ break;
+ case 'd':
+ mode = DISK;
+ break;
+ case 'f':
+ fn = optarg;
+ break;
+ case '?':
+ default:
+ usage(argv[0]);
+ return EX_USAGE;
+ }
+ }
+
+ if (optind < argc) {
+ devnames = &argv[optind];
+ }
+
+ errfp = fwopen(0, err_writefn);
+ if (!errfp) {
+ err(EX_UNAVAILABLE, "fwopen");
+ }
+
+ setvbuf(errfp, (char *)0, _IOLBF, (size_t)0);
+
+ init_dialog();
+ err_set_exit(dm_err_exit);
+ err_set_file(errfp);
+
+ switch(mode) {
+ case NETIF:
+ sel = devmenu_netif(title, hfile, devnames, states);
+ break;
+ case INSTALL:
+ sel = devmenu_common(title, hfile, devnames,
+ "Select an installation device",
+ "No installation devices found",
+ DC_CLS_DISK | DC_CLS_RDISK | DC_CLS_TAPE,
+ states);
+ break;
+ case DISK:
+ sel = devmenu_common(title, hfile, devnames,
+ "Select a disk",
+ "No disks found",
+ DC_CLS_DISK,
+ states);
+ break;
+ case GENERIC:
+ sel = devmenu_common(title, hfile, devnames,
+ "Select a device",
+ "No devices found",
+ class,
+ states);
+ break;
+ }
+ err_set_file(0);
+ fclose(errfp);
+ end_dialog();
+ err_set_exit(0);
+ if (fn) {
+ errfp = fopen(fn, "w");
+ if (!errfp) {
+ err(EX_OSERR, "fopen(%s)", fn);
+ }
+ fprintf(errfp, "%s\n", sel);
+ fclose(errfp);
+ } else {
+ printf("%s\n", sel);
+ }
+ return 0;
+}
+
+static const char *classes[] = DC_CLASSNAMES;
+#define NCLASSES ((sizeof classes)/(sizeof classes[0]))
+
+static enum dc_class
+interpret_class(char *str)
+{
+ int i;
+ enum dc_class rv;
+
+ for(i = 1; i < NCLASSES; i++) {
+ if(! strcmp(classes[i], str)) {
+ rv = (1 << (i - 1));
+ break;
+ }
+ }
+ if (i == NCLASSES) {
+ err(EX_USAGE, "unknown class `%s'", str);
+ }
+
+ return rv;
+}
+
+static int
+interpret_state(char *str)
+{
+ int rv = 0;
+ int invert = 0;
+
+ if (*str == '!' || *str == '~') {
+ invert = 1;
+ str++;
+ }
+
+ for (; *str; str++) {
+ switch(*str) {
+ case 'u':
+ case 'U':
+ rv |= 1 << DC_UNCONFIGURED;
+ break;
+ case '?':
+ rv |= 1 << DC_UNKNOWN;
+ break;
+ case 'i':
+ case 'I':
+ rv |= 1 << DC_IDLE;
+ break;
+ case 'b':
+ case 'B':
+ rv |= 1 << DC_BUSY;
+ break;
+ default:
+ err(EX_USAGE, "unknown state `%c'", *str);
+ }
+ }
+
+ return (invert ? ~rv : rv);
+}
+
+static void
+dm_err_exit(int rval)
+{
+ fflush(errfp);
+ fclose(errfp);
+ end_dialog();
+ exit(rval);
+}
+
+static int
+err_writefn(void *cookie, const char *val, int nchars)
+{
+ char buf[nchars + 1];
+
+ strncpy(buf, val, nchars);
+ buf[nchars] = '\0';
+
+ dialog_msgbox("Error", buf, -1, -1, 1);
+ return nchars;
+}
+
+static void
+usage(const char *argv0)
+{
+ fprintf(stderr, "%s: usage:\n"
+ "%s [-c class] [-s state] [-t title] [-h hfile] [-f outfile]"
+ "[-n] [-i] [-d]\n", argv0, argv0);
+
+}
diff --git a/usr.bin/devmenu/devmenu.h b/usr.bin/devmenu/devmenu.h
new file mode 100644
index 0000000..e3cf1a8
--- /dev/null
+++ b/usr.bin/devmenu/devmenu.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: devmenu.h,v 1.2 1995/04/14 18:33:44 wollman Exp $
+ */
+
+/* NB: must include sys/devconf.h before this file */
+
+/* Args are: title, help file, device name list, states */
+extern const char *devmenu_netif(const char *, const char *, char **, int);
+
+extern char *devmenu_toname(const struct devconf *);
+
+/* Args are: device, user's list of devices */
+extern int devmenu_filter(const struct devconf *, char **);
+
+/* Returns an array of pointers to all the devconf devices in the system */
+extern struct devconf **devmenu_alldevs(void);
+
+/* Frees the array allocated above */
+extern void devmenu_freedevs(struct devconf ***);
+
+/* Args are: title, help file, name list, prompt, no-devices, class, states */
+extern const char *devmenu_common(const char *, const char *, char **,
+ const char *, const char *, enum dc_class,
+ int);
+
diff --git a/usr.bin/devmenu/ifmenu.c b/usr.bin/devmenu/ifmenu.c
new file mode 100644
index 0000000..74b0e36
--- /dev/null
+++ b/usr.bin/devmenu/ifmenu.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * ifmenu - present a menu of network interfaces
+ *
+ * Garrett A. Wollman, April 1995
+ */
+
+#include <sys/types.h>
+#include <sys/devconf.h>
+#include "devmenu.h"
+
+/*
+ * This is provided in a separate file so that, in the future, non-physical
+ * network interfaces might be added as well.
+ */
+const char *
+devmenu_netif(const char *title, const char *hfile, char **devnames,
+ int states)
+{
+ return devmenu_common(title, hfile, devnames,
+ "Select a network interface",
+ "No network interfaces available",
+ DC_CLS_NETIF, states);
+}
diff --git a/usr.bin/diff/diff/diff.1 b/usr.bin/diff/diff/diff.1
new file mode 100644
index 0000000..1b5c078
--- /dev/null
+++ b/usr.bin/diff/diff/diff.1
@@ -0,0 +1,387 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)diff.1 8.1 (Berkeley) 6/30/93
+.\"
+.Dd June 30, 1993
+.Dt DIFF 1
+.Os BSD 4
+.Sh NAME
+.Nm diff
+.Nd differential file and directory comparator
+.Sh SYNOPSIS
+.Nm diff
+.Op Fl cefhn
+.Op Fl biwt
+.Ar file1 file2
+.Nm diff
+.Op Fl D Ns Ar string
+.Op Fl biw
+.Ar file1 file2
+.Nm diff
+.Op Fl l
+.Op Fl r
+.Op Fl s
+.Op Fl cefhn
+.Op Fl biwt
+.Op Fl S Ns Ar name
+.Ar dir1 dir2
+.Sh DESCRIPTION
+The
+.Nm diff
+utility compares the contents of
+.Ar file1
+and
+.Ar file2
+and writes to the standard output the list of changes necessary to
+convert one file into the other.
+No output is produced if the files are identical.
+.Pp
+Output options (mutually exclusive):
+.Bl -tag -width Ds
+.It Fl c
+produces a diff with lines of context.
+The default is to present 3 lines of context and may be changed, e.g., to 10, by
+.Fl c10 .
+With
+.Fl c
+the output format is modified slightly:
+the output beginning with identification of the files involved and
+their creation dates and then each change is separated
+by a line with a dozen *'s.
+The lines removed from
+.Ar file1
+are marked with `\(mi '; those added to
+.Ar file2
+are marked `+ '. Lines which are changed from one
+file to the other are marked in both files with `! '.
+Changes which lie within <context> lines of each other are grouped
+together on output. (This is a change from the previous ``diff -c''
+but the resulting output is usually much easier to interpret.)
+.It Fl e
+produces output in a form suitable as input for the editor utility,
+.Xr ed 1 ,
+which can then be used to convert file1 into file2.
+.Pp
+Extra commands are added to the output when comparing directories with
+.Fl e ,
+so that the result is a
+.Xr sh 1
+script for converting text files which are common to the two directories
+from their state in
+.Ar dir1
+to their state in
+.Ar dir2 .
+.It Fl f
+identical output to that of the
+.Fl e
+flag, but in reverse order. It cannot
+be digested by
+.Xr ed 1 .
+.It Fl h
+Invokes an alternate algorithm which can handle files of very long lengths.
+There is a trade off. The algorithm can only deal with changes which are
+clearly delimited and brief. Long sections of changes and overlaps will
+confuse it.
+.It Fl n
+produces a script similar to that of
+.Fl e ,
+but in the opposite order and with a count of changed lines on each
+insert or delete command. This is the form used by
+.Xr rcsdiff 1 .
+.It Fl D Ns Ar string
+creates a merged version of
+.Ar file1
+and
+.Ar file2
+on the standard output, with C preprocessor controls included so that
+a compilation of the result without defining
+.Ar string
+is equivalent
+to compiling
+.Ar file1 ,
+while defining
+.Ar string
+will yield
+.Ar file2 .
+.El
+.Pp
+Comparison options:
+.Bl -tag -width Ds
+.It Fl b
+causes trailing blanks (spaces and tabs) to be ignored, and other
+strings of blanks to compare equal.
+.It Fl i
+ignores the case of letters. E.g., ``A'' will compare equal to ``a''.
+.It Fl t
+will expand tabs in output lines. Normal or
+.Fl c
+output adds character(s) to the front of each line which may screw up
+the indentation of the original source lines and make the output listing
+difficult to interpret. This option will preserve the original source's
+indentation.
+.It Fl w
+is similar to
+.Fl b
+but causes whitespace (blanks and tabs) to be totally ignored. E.g.,
+``if\ (\ a\ ==\ b\ )'' will compare equal to ``if(a==b)''.
+.El
+.Pp
+Directory comparison options:
+.Bl -tag -width Ds
+.It Fl l
+long output format; each text file
+.Nm diff Ns \'d
+is piped through
+.Xr pr 1
+to paginate it,
+other differences are remembered and summarized
+after all text file differences are reported.
+.It Fl r
+causes application of
+.Nm diff
+recursively to common subdirectories encountered.
+.It Fl s
+causes
+.Nm diff
+to report files which are the same, which are otherwise not mentioned.
+.It Fl S Ns Ar name
+re-starts a directory
+.Nm diff
+in the middle beginning with file
+.Ar name .
+.El
+.Pp
+If both arguments are directories,
+.Nm diff
+sorts the contents of the directories by name, and then runs the
+regular file
+.Nm diff
+algorithm, producing a change list,
+on text files which are different.
+Binary files which differ,
+common subdirectories, and files which appear in only one directory
+are described as such.
+.Pp
+If only one of
+.Ar file1
+and
+.Ar file2
+is a directory,
+.Nm diff
+is applied to the non-directory file and the file contained in
+the directory file with a filename that is the same as the
+last component of the non-directory file.
+.Pp
+If either
+.Ar file1
+or
+.Ar file2
+is
+.Sq Fl ,
+the standard input is
+used in its place.
+.Ss Output Style
+The default (without
+.Fl e ,
+.Fl c ,
+or
+.Fl n
+.\" -C
+options)
+output contains lines of these forms, where
+.Va XX , YY , ZZ , QQ
+are line numbers respective of file order.
+.Pp
+.Bl -tag -width "XX,YYcZZ,QQ" -compact
+.It Li XX Ns Ic a Ns Li YY
+At (the end of) line
+.Va XX
+of
+.Ar file1 ,
+append the contents
+of line
+.Va YY
+of
+.Ar file2
+to make them equal.
+.It Li XX Ns Ic a Ns Li YY,ZZ
+Same as above, but append the range of lines,
+.Va YY
+through
+.Va ZZ
+of
+.Ar file2
+to line
+.Va XX
+of file1.
+.It Li XX Ns Ic d Ns Li YY
+At line
+.Va XX
+delete
+the line. The value
+.Va YY
+tells to which line the change
+would bring
+.Ar file1
+in line with
+.Ar file1 .
+.It Li XX,YY Ns Ic d Ns Li ZZ
+Delete the range of lines
+.Va XX
+through
+.Va YY
+in
+.Ar file1 .
+.It Li XX Ns Ic c Ns Li YY
+Change the line
+.Va XX
+in
+.Ar file1
+to the line
+.Va YY
+in
+.Ar file2.
+.It Li XX,YY Ns Ic c Ns Li ZZ
+Replace the range of specified lines with the line
+.Va ZZ .
+.It Li XX,YY Ns Ic c Ns Li ZZ,QQ
+Replace the range
+.Va XX , Ns YY
+from
+.Ar file1
+with the range
+.Va ZZ , Ns QQ
+from
+.Ar file2 .
+.El
+.Pp
+These lines resemble
+.Xr ed 1
+subcommands to convert
+.Ar file1
+into
+.Ar file2 .
+The line numbers before the action letters pertain to
+.Ar file1 ;
+those after pertain to
+.Ar file2 .
+Thus, by exchanging
+.Ic a
+for
+.Ic d
+and reading the line in reverse order, one can also
+determine how to convert
+.Ar file2
+into
+.Ar file1 .
+As in
+.Xr ed 1 ,
+identical
+pairs (where num1 = num2) are abbreviated as a single
+number.
+.Sh ENVIRONMENT
+.Bl -tag -width TMPDIR
+.It Ev TMPDIR
+If the environment variable
+.Ev TMPDIR
+exists,
+.Nm diff
+will use the directory specified by
+.Ev TMPDIR
+as the temporary directory.
+.El
+.Sh FILES
+.Bl -tag -width /usr/bin/diffh -compact
+.It Pa /tmp/d?????
+.It Pa /usr/bin/diffh
+Alternate algorithm version (used by option
+.Fl h ) .
+.It Pa /usr/bin/diff
+for directory diffs
+.It Pa /usr/bin/pr
+used by the
+.Fl l
+option.
+.El
+.Sh SEE ALSO
+.Xr cmp 1 ,
+.Xr cc 1 ,
+.Xr comm 1 ,
+.Xr ed 1 ,
+.Xr diff3 1
+.br
+.ne 1i
+.Sh DIAGNOSTICS
+The
+.Nm diff
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It \&0
+No differences were found.
+.It \&1
+Differences were found.
+.It "\&>\&1"
+An error occurred.
+.El
+.Sh BUGS
+The
+.Fl f
+and
+.Fl e
+options
+do not provide special handling for lines on which the
+first and only character is
+.Dq Li \&. .
+This can cause problems for
+.Xr ed 1 .
+.Pp
+When comparing directories with the
+.Fl b ,
+.Fl w
+or
+.Fl i
+options specified,
+.Nm diff
+first compares the files ala
+.Ar cmp ,
+and then decides to run the
+.Nm diff
+algorithm if they are not equal.
+This may cause a small amount of spurious output if the files
+then turn out to be identical because the only differences are
+insignificant white space or case differences.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/diff/diff3/diff3.1 b/usr.bin/diff/diff3/diff3.1
new file mode 100644
index 0000000..09b1d72
--- /dev/null
+++ b/usr.bin/diff/diff3/diff3.1
@@ -0,0 +1,172 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)diff3.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt DIFF3 1
+.Os BSD 4.3R
+.Sh NAME
+.Nm diff3
+.Nd 3-way differential file comparison
+.Sh SYNOPSIS
+.Nm diff3
+.Op Fl exEX3
+.Ar file1 file2 file3
+.Sh DESCRIPTION
+The
+.Nm diff3
+utility compares the contents of three different versions of a file,
+.Ar file1 ,
+.Ar file2
+and
+.Ar file3 ,
+writing the result to the standard output.
+The options describe different methods of merging and
+purging
+the separate versions into a new file.
+.Nm Diff3
+is used by
+.Xr RCS 1
+to merge specific versions or create
+new versions.
+.Pp
+Options are:
+.Bl -tag -width "--E, --X"
+.It Fl e
+Produces output in a form suitable as an input script for the
+.Xr ed 1
+utility. The script may then be used to merge differences common
+between all three files and differences specific to file1 and file3.
+In other words, the
+.Fl e
+option ignores differences specific to file1 and file2, and those
+specific to file2 and file3. It is useful for backing out changes
+specific to file2 only.
+.It Fl x
+Produces an output script suitable for
+.Xr ed 1
+with changes
+specific only to all three versions.
+.It Fl 3
+Produces an output script suitable for
+.Xr ed 1
+with changes
+specific only to file3.
+.It Fl E , X
+Similar to
+.Fl e
+and
+.Fl x ,
+respectively, but treat overlapping changes (i.e., changes that would
+be noted with ==== in the normal listing) differently. The overlapping
+lines from both files will be inserted by the edit script, bracketed
+by "<<<<<<" and ">>>>>>" lines.
+.El
+.Pp
+The
+.Fl E
+option is used by
+.Tn RCS
+.Xr merge 1
+to insure that overlapping changes in the merged files are preserved
+and brought to someone's attention.
+.Pp
+For example, suppose lines 7-8 are changed in both file1 and file2.
+Applying the edit script generated by the command
+.Pp
+.Dl diff3 -E file1 file2 file3
+.Pp
+to file1 results in the file:
+.Pp
+.Bd -literal -offset indent -compact
+lines 1-6
+of file1
+<<<<<<< file1
+lines 7-8
+of file1
+=======
+lines 7-8
+of file3
+>>>>>>> file3
+rest of file1
+.Ed
+.Pp
+The default output of
+.Nm diff3
+makes notation of the differences between all files, and those differences
+specific to each pair of files. The
+changes are described by
+the commands necessary for
+.Xr ed 1
+to create the desired target from the different versions.
+See
+.Xr diff 1
+for a description of the commands.
+.Bl -tag -width "====="
+.It Li \&====
+The lines beneath this notation are ranges of lines which are different
+between all files.
+.It \&==== Ns Va n
+The lines beneath this notation are ranges of lines which are exclusively
+different in file
+.Va n .
+.El
+.Sh FILES
+.Bl -tag -width /usr/bin/diff3 -compact
+.It Pa /tmp/d3?????
+temporary files.
+.It Pa /usr/bin/diff3
+the executable.
+.El
+.Sh SEE ALSO
+.Xr diff 1
+.Xr ed 1
+.Xr rcs 1
+.Sh BUGS
+The
+.Fl e
+option
+cannot catch and change
+lines which have
+.Ql \&.
+as the first and only character on the line.
+The resulting script will fail on that line
+as
+.Ql \&.
+is an
+.Xr ed 1
+editing command.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
diff --git a/usr.bin/dig/Makefile b/usr.bin/dig/Makefile
new file mode 100644
index 0000000..dd4044d
--- /dev/null
+++ b/usr.bin/dig/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= dig
+MAN1= dig.1
+SRCS= dig.c debug.c list.c send.c subr.c
+
+NSLOOKUP=${.CURDIR}/../../usr.sbin/nslookup
+CFLAGS+=-I${NSLOOKUP}
+.PATH: ${NSLOOKUP}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/dig/dig.1 b/usr.bin/dig/dig.1
index b94957a..71e4374 100644
--- a/usr.bin/dig/dig.1
+++ b/usr.bin/dig/dig.1
@@ -1,4 +1,4 @@
-.\" $Id: dig.1,v 8.1 1994/12/15 06:24:10 vixie Exp $
+.\" $Id: dig.1,v 1.1.1.1 1994/09/22 21:33:31 pst Exp $
.\"
.\" ++Copyright++ 1993
.\" -
@@ -188,7 +188,7 @@ Port number. Query a name server listening to a
non-standard port number. Default is 53.
.IP "\-P[\fIping-string\fP]"
After query returns, execute a
-.IR ping (8)
+.IR ping (@SYS_OPS_EXT@)
command
for response time comparison. This rather
unelegantly makes a call to the shell. The last
@@ -258,7 +258,7 @@ subsequent lines (i.e. they are not restored to the
"+" is used to specify an option to be changed in the
query packet or to change \fIdig\fP output specifics. Many
of these are the same parameters accepted by
-.IR nslookup (8).
+.IR nslookup (@SYS_OPS_EXT@).
If an option requires a parameter, the form is as
follows:
.sp 1
diff --git a/usr.bin/dig/dig.c b/usr.bin/dig/dig.c
index e6978a5..0557412 100644
--- a/usr.bin/dig/dig.c
+++ b/usr.bin/dig/dig.c
@@ -1,5 +1,5 @@
#ifndef lint
-static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
+static char rcsid[] = "$Id: dig.c,v 1.4 1995/05/30 06:29:46 rgrimes Exp $";
#endif
/*
@@ -7,7 +7,7 @@ static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
* -
* Copyright (c) 1989
* The Regents of the University of California. All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -23,7 +23,7 @@ static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -37,14 +37,14 @@ static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
+ *
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
@@ -89,7 +89,7 @@ static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
* in order to disclaim liability and warranty).
*
* Paul Vixie, Palo Alto, CA, April 1993
- ****************************************************************************
+ ****************************************************************************/
/*******************************************************************
** DiG -- Domain Information Groper **
@@ -157,14 +157,14 @@ static char rcsid[] = "$Id: dig.c,v 8.4 1995/06/19 08:35:06 vixie Exp $";
#include <netdb.h>
#include <stdio.h>
#include <resolv.h>
-#include <ctype.h>
+#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <setjmp.h>
#include <fcntl.h>
+#include <stdlib.h>
-#include "nslookup/res.h"
-#include "../conf/portability.h"
+#include "res.h"
#define PRF_DEF 0x2ff9
#define PRF_MIN 0xA930
@@ -345,8 +345,8 @@ main(argc, argv)
** while !EOF if batch mode
*/
*fileq = '\0';
- while ((dofile && (fgets(fileq,100,qfp) != NULL)) ||
- ((!dofile) && (once--)))
+ while ((dofile && (fgets(fileq,100,qfp) != NULL)) ||
+ ((!dofile) && (once--)))
{
if ((*fileq=='\n') || (*fileq=='#') || (*fileq==';')) {
continue; /* ignore blank lines & comments */
@@ -380,7 +380,7 @@ main(argc, argv)
* More cmd-line options than anyone should ever have to
* deal with ....
*/
- while (*(++argv) != NULL && **argv != '\0') {
+ while (*(++argv) != NULL && **argv != '\0') {
strcat(cmd,*argv); strcat(cmd," ");
if (**argv == '@') {
srv = (*argv+1);
@@ -392,7 +392,7 @@ main(argc, argv)
SetOption(*argv+1);
continue;
}
-
+
if (strncmp(*argv,"-nost",5) == 0) {
sticky = 0;
continue;
@@ -408,10 +408,10 @@ main(argc, argv)
}
if (**argv == '-') {
- switch (argv[0][1]) {
+ switch (argv[0][1]) {
case 'T': wait = atoi(*++argv);
break;
- case 'c':
+ case 'c':
if ((tmp = atoi(*++argv))
|| *argv[0]=='0') {
queryClass = tmp;
@@ -425,7 +425,7 @@ main(argc, argv)
);
}
break;
- case 't':
+ case 't':
if ((tmp = atoi(*++argv))
|| *argv[0]=='0') {
queryType = tmp;
@@ -504,7 +504,7 @@ main(argc, argv)
if (_res.pfcode & 0x80000)
printf("; pfcode: %08x, options: %08x\n",
_res.pfcode, _res.options);
-
+
/*
* Current env. (after this parse) is to become the
* new "working environmnet. Used in conj. with sticky.
@@ -542,7 +542,7 @@ main(argc, argv)
/*
* Find address of server to query. If not dot-notation, then
- * try to resolve domain-name (if so, save and turn off print
+ * try to resolve domain-name (if so, save and turn off print
* options, this domain-query is not the one we want. Restore
* user options when done.
* Things get a bit wierd since we need to use resolver to be
@@ -682,7 +682,7 @@ main(argc, argv)
printf(";; MSG SIZE sent: %d rcvd: %d\n",
bytes_out, bytes_in);
}
-
+
fflush(stdout);
/*
* Argh ... not particularly elegant. Should put in *real* ping code.
@@ -758,8 +758,8 @@ SetOption(string)
if (i != 1) {
fprintf(stderr, ";*** Invalid option: %s\n", option);
return(ERROR);
- }
-
+ }
+
if (strncmp(option, "aa", 2) == 0) { /* aaonly */
_res.options |= RES_AAONLY;
} else if (strncmp(option, "noaa", 4) == 0) {
@@ -842,47 +842,47 @@ SetOption(string)
_res.pfcode &= ~RES_PRF_ANS;
} else if (strncmp(option, "qu", 2) == 0) { /* question section */
_res.pfcode |= RES_PRF_QUES;
- } else if (strncmp(option, "noqu", 4) == 0) {
+ } else if (strncmp(option, "noqu", 4) == 0) {
_res.pfcode &= ~RES_PRF_QUES;
} else if (strncmp(option, "au", 2) == 0) { /* authority section */
_res.pfcode |= RES_PRF_AUTH;
- } else if (strncmp(option, "noau", 4) == 0) {
+ } else if (strncmp(option, "noau", 4) == 0) {
_res.pfcode &= ~RES_PRF_AUTH;
} else if (strncmp(option, "ad", 2) == 0) { /* addition section */
_res.pfcode |= RES_PRF_ADD;
- } else if (strncmp(option, "noad", 4) == 0) {
+ } else if (strncmp(option, "noad", 4) == 0) {
_res.pfcode &= ~RES_PRF_ADD;
} else if (strncmp(option, "tt", 2) == 0) { /* TTL & ID */
_res.pfcode |= RES_PRF_TTLID;
- } else if (strncmp(option, "nott", 4) == 0) {
+ } else if (strncmp(option, "nott", 4) == 0) {
_res.pfcode &= ~RES_PRF_TTLID;
} else if (strncmp(option, "he", 2) == 0) { /* head flags stats */
_res.pfcode |= RES_PRF_HEAD2;
- } else if (strncmp(option, "nohe", 4) == 0) {
+ } else if (strncmp(option, "nohe", 4) == 0) {
_res.pfcode &= ~RES_PRF_HEAD2;
} else if (strncmp(option, "H", 1) == 0) { /* header all */
_res.pfcode |= RES_PRF_HEADX;
- } else if (strncmp(option, "noH", 3) == 0) {
+ } else if (strncmp(option, "noH", 3) == 0) {
_res.pfcode &= ~(RES_PRF_HEADX);
} else if (strncmp(option, "qr", 2) == 0) { /* query */
_res.pfcode |= RES_PRF_QUERY;
- } else if (strncmp(option, "noqr", 4) == 0) {
+ } else if (strncmp(option, "noqr", 4) == 0) {
_res.pfcode &= ~RES_PRF_QUERY;
} else if (strncmp(option, "rep", 3) == 0) { /* reply */
_res.pfcode |= RES_PRF_REPLY;
- } else if (strncmp(option, "norep", 5) == 0) {
+ } else if (strncmp(option, "norep", 5) == 0) {
_res.pfcode &= ~RES_PRF_REPLY;
} else if (strncmp(option, "cm", 2) == 0) { /* command line */
_res.pfcode |= RES_PRF_CMD;
- } else if (strncmp(option, "nocm", 4) == 0) {
+ } else if (strncmp(option, "nocm", 4) == 0) {
_res.pfcode &= ~RES_PRF_CMD;
} else if (strncmp(option, "cl", 2) == 0) { /* class mnemonic */
_res.pfcode |= RES_PRF_CLASS;
- } else if (strncmp(option, "nocl", 4) == 0) {
+ } else if (strncmp(option, "nocl", 4) == 0) {
_res.pfcode &= ~RES_PRF_CLASS;
} else if (strncmp(option, "st", 2) == 0) { /* stats*/
_res.pfcode |= RES_PRF_STATS;
- } else if (strncmp(option, "nost", 4) == 0) {
+ } else if (strncmp(option, "nost", 4) == 0) {
_res.pfcode &= ~RES_PRF_STATS;
} else {
fprintf(stderr, "; *** Invalid option: %s\n", option);
diff --git a/usr.bin/dirname/Makefile b/usr.bin/dirname/Makefile
new file mode 100644
index 0000000..76b4089
--- /dev/null
+++ b/usr.bin/dirname/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= dirname
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/dirname/dirname.c b/usr.bin/dirname/dirname.c
new file mode 100644
index 0000000..0c27e07
--- /dev/null
+++ b/usr.bin/dirname/dirname.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)dirname.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ /*
+ * (1) If string is //, skip steps (2) through (5).
+ * (2) If string consists entirely of slash characters, string
+ * shall be set to a single slash character. In this case,
+ * skip steps (3) through (8).
+ */
+ for (p = *argv;; ++p) {
+ if (!*p) {
+ if (p > *argv)
+ (void)printf("/\n");
+ else
+ (void)printf(".\n");
+ exit(0);
+ }
+ if (*p != '/')
+ break;
+ }
+
+ /*
+ * (3) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ for (; *p; ++p);
+ while (*--p == '/')
+ continue;
+ *++p = '\0';
+
+ /*
+ * (4) If there are no slash characters remaining in string,
+ * string shall be set to a single period character. In this
+ * case skip steps (5) through (8).
+ *
+ * (5) If there are any trailing nonslash characters in string,
+ * they shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p == '/')
+ break;
+ ++p;
+ if (p == *argv) {
+ (void)printf(".\n");
+ exit(0);
+ }
+
+ /*
+ * (6) If the remaining string is //, it is implementation defined
+ * whether steps (7) and (8) are skipped or processed.
+ *
+ * This case has already been handled, as part of steps (1) and (2).
+ */
+
+ /*
+ * (7) If there are any trailing slash characters in string, they
+ * shall be removed.
+ */
+ while (--p >= *argv)
+ if (*p != '/')
+ break;
+ ++p;
+
+ /*
+ * (8) If the remaining string is empty, string shall be set to
+ * a single slash character.
+ */
+ *p = '\0';
+ (void)printf("%s\n", p == *argv ? "/" : *argv);
+ exit(0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: dirname path\n");
+ exit(1);
+}
diff --git a/usr.bin/du/Makefile b/usr.bin/du/Makefile
new file mode 100644
index 0000000..3b5f135
--- /dev/null
+++ b/usr.bin/du/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= du
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/du/du.1 b/usr.bin/du/du.1
new file mode 100644
index 0000000..97c366a
--- /dev/null
+++ b/usr.bin/du/du.1
@@ -0,0 +1,122 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)du.1 8.2 (Berkeley) 4/1/94
+.\"
+.Dd April 1, 1994
+.Dt DU 1
+.Os
+.Sh NAME
+.Nm du
+.Nd display disk usage statistics
+.Sh SYNOPSIS
+.Nm du
+.Op Fl H | Fl L | Fl P
+.Op Fl a | Fl s
+.Op Fl x
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm du
+utility displays the file system block usage for each file argument
+and for each directory in the file hierarchy rooted in each directory
+argument.
+If no file is specified, the block usage of the hierarchy rooted in
+the current directory is displayed.
+The number of blocks are in the same units as that returned by the
+.Xr stat 2
+system call, i.e. 512-byte blocks.
+Partial numbers of blocks are rounded up.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl H
+Symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl L
+All symbolic links are followed.
+.It Fl P
+No symbolic links are followed.
+.It Fl a
+Display an entry for each file in the file hierarchy.
+.It Fl k
+Report in 1024-byte (1-Kbyte) blocks rather than the default. Note that
+this overrides the
+.Ev BLOCKSIZE
+setting from the environment.
+.It Fl s
+Display only the grand total for the specified files.
+.It Fl x
+Filesystem mount points are not traversed.
+.El
+.Pp
+.Nm Du
+counts the storage used by symbolic links and not the files they
+reference unless the
+.Fl H
+or
+.Fl L
+option is specified.
+If either the
+.Fl H
+or
+.Fl L
+options are specified, storage used by any symbolic links which are
+followed is not counted or displayed.
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options override each other and the command's actions are determined
+by the last one specified.
+.Pp
+Files having multiple hard links are counted (and displayed) a single
+time per
+.Nm du
+execution.
+.Sh ENVIRONMENTAL VARIABLES
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environmental variable
+.Ev BLOCKSIZE
+is set, the block counts will be displayed in units of that size block.
+.El
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr fts 3 ,
+.Xr symlink 7 ,
+.Xr quot 8
+.Sh HISTORY
+A
+.Nm du
+command appeared in
+.At v6 .
diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c
new file mode 100644
index 0000000..26747c8
--- /dev/null
+++ b/usr.bin/du/du.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)du.c 8.4 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int linkchk __P((FTSENT *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *fts;
+ FTSENT *p;
+ long blocksize;
+ int ftsoptions, listdirs, listfiles;
+ int Hflag, Lflag, Pflag, aflag, ch, notused, rval, sflag;
+ char **save;
+
+ save = argv;
+ Hflag = Lflag = Pflag = aflag = sflag = 0;
+ ftsoptions = FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPaksx")) != EOF)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'k':
+ putenv("BLOCKSIZE=1024");
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * XXX
+ * Because of the way that fts(3) works, logical walks will not count
+ * the blocks actually used by symbolic links. We rationalize this by
+ * noting that users computing logical sizes are likely to do logical
+ * copies, so not counting the links is correct. The real reason is
+ * that we'd have to re-implement the kernel's symbolic link traversing
+ * algorithm to get this right. If, for example, you have relative
+ * symbolic links referencing other relative symbolic links, it gets
+ * very nasty, very fast. The bottom line is that it's documented in
+ * the man page, so it's a feature.
+ */
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ if (aflag) {
+ if (sflag)
+ usage();
+ listdirs = listfiles = 1;
+ } else if (sflag)
+ listdirs = listfiles = 0;
+ else {
+ listfiles = 0;
+ listdirs = 1;
+ }
+
+ if (!*argv) {
+ argv = save;
+ argv[0] = ".";
+ argv[1] = NULL;
+ }
+
+ (void)getbsize(&notused, &blocksize);
+ blocksize /= 512;
+
+ if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(fts)) != NULL;)
+ switch (p->fts_info) {
+ case FTS_D: /* Ignore. */
+ break;
+ case FTS_DP:
+ p->fts_parent->fts_number +=
+ p->fts_number += p->fts_statp->st_blocks;
+ /*
+ * If listing each directory, or not listing files
+ * or directories and this is post-order of the
+ * root of a traversal, display the total.
+ */
+ if (listdirs || !listfiles && !p->fts_level)
+ (void)printf("%ld\t%s\n",
+ howmany(p->fts_number, blocksize),
+ p->fts_path);
+ break;
+ case FTS_DC: /* Ignore. */
+ break;
+ case FTS_DNR: /* Warn, continue. */
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ default:
+ if (p->fts_statp->st_nlink > 1 && linkchk(p))
+ break;
+ /*
+ * If listing each file, or a non-directory file was
+ * the root of a traversal, display the total.
+ */
+ if (listfiles || !p->fts_level)
+ (void)printf("%qd\t%s\n",
+ howmany(p->fts_statp->st_blocks, blocksize),
+ p->fts_path);
+ p->fts_parent->fts_number += p->fts_statp->st_blocks;
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(0);
+}
+
+typedef struct _ID {
+ dev_t dev;
+ ino_t inode;
+} ID;
+
+int
+linkchk(p)
+ FTSENT *p;
+{
+ static ID *files;
+ static int maxfiles, nfiles;
+ ID *fp, *start;
+ ino_t ino;
+ dev_t dev;
+
+ ino = p->fts_statp->st_ino;
+ dev = p->fts_statp->st_dev;
+ if ((start = files) != NULL)
+ for (fp = start + nfiles - 1; fp >= start; --fp)
+ if (ino == fp->inode && dev == fp->dev)
+ return (1);
+
+ if (nfiles == maxfiles && (files = realloc((char *)files,
+ (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
+ err(1, "");
+ files[nfiles].inode = ino;
+ files[nfiles].dev = dev;
+ ++nfiles;
+ return (0);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: du [-H | -L | -P] [-a | -s] [-k] [-x] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/ee/Makefile b/usr.bin/ee/Makefile
new file mode 100644
index 0000000..5c6c850
--- /dev/null
+++ b/usr.bin/ee/Makefile
@@ -0,0 +1,18 @@
+CFLAGS+= -DCAP -DHAS_NCURSES -DHAS_UNISTD -DHAS_STDARG -DHAS_STDLIB \
+ -DHAS_CTYPE -DHAS_SYS_IOCTL -DHAS_SYS_WAIT -DSLCT_HDR
+
+PROG= ee
+SRCS= ee.c
+LINKS= ${BINDIR}/ee ${BINDIR}/ree
+DPADD= ${LIBNCURSES} ${LIBMYTINFO}
+LDADD= -lncurses -lmytinfo
+
+LANGS= en_US.ISO_8859-1 fr_FR.ISO_8859-1 de_DE.ISO_8859-1
+
+afterinstall:
+ @for i in ${LANGS}; do \
+ gencat -new ${DESTDIR}/usr/share/nls/$$i/ee.cat ${.CURDIR}/nls/$$i/ee.msg; \
+ chmod 444 ${.CURDIR}/nls/$$i/ee.msg; \
+ done
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ee/README b/usr.bin/ee/README
new file mode 100644
index 0000000..e150700
--- /dev/null
+++ b/usr.bin/ee/README
@@ -0,0 +1,116 @@
+ THIS MATERIAL IS PROVIDED "AS IS". THERE ARE NO WARRANTIES OF
+ ANY KIND WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE. Neither Hewlett-Packard nor
+ Hugh Mahon shall be liable for errors contained herein, nor for
+ incidental or consequential damages in connection with the
+ furnishing, performance or use of this material. Neither
+ Hewlett-Packard nor Hugh Mahon assumes any responsibility for
+ the use or reliability of this software or documentation. This
+ software and documentation is totally UNSUPPORTED. There is no
+ support contract available. Hewlett-Packard has done NO
+ Quality Assurance on ANY of the program or documentation. You
+ may find the quality of the materials inferior to supported
+ materials.
+
+ This software may be distributed under the terms of Larry Wall's
+ Artistic license, a copy of which is included in this distribution.
+ (see doc/Artistic).
+
+ This notice must be included with this software and any
+ derivatives.
+
+ Any modifications to this software by anyone but the original author
+ must be so noted.
+
+
+The editor 'ee' (easy editor) is intended to be a simple, easy to use
+terminal-based screen oriented editor that requires no instruction to
+use. Its primary use would be for people who are new to computers, or who
+use computers only for things like e-mail.
+
+ee's simplified interface is highlighted by the use of pop-up menus which
+make it possible for users to carry out tasks without the need to
+remember commands. An information window at the top of the screen shows
+the user the operations available with control-keys.
+
+ee allows users to use full eight-bit characters. If the host system has
+the capabilities, ee can use message catalogs, which would allow users to
+translate the message catalog into other languages which use eight-bit
+characters. See the file ee.i18n.guide for more details.
+
+ee relies on the virtual memory abilities of the platform it is running on
+and does not have its own memory management capabilities.
+
+I am releasing ee because I hate to see new users and non-computer types
+get frustrated by vi, and would like to see more intuitive interfaces for
+basic tools (both character-based and graphical) become more pervasive.
+Terminal capabilities and communication speeds have evolved considerably
+since the time in which vi's interface was created, allowing much more
+intuitive interfaces to be used. Since character-based I/O won't be
+completely replaced by graphical user interfaces for at least a few more
+years, I'd like to do what I can to make using computers with less
+glamorous interfaces as easy to use as possible. If terminal interfaces
+are still used in ten years, I hope neophytes won't still be stuck with
+only vi.
+
+For a text editor to be easy to use requires a certain set of abilities. In
+order for ee to work, a terminal must have the ability to position the cursor
+on the screen, and should have arrow keys that send unique sequences
+(multiple characters, the first character is an "escape", octal code
+'\033'). All of this information needs to be in a database called "terminfo"
+(System V implementations) or "termcap" (usually used for BSD systems). In
+case the arrow keys do not transmit unique sequences, motion operations are
+mapped to control keys as well, but this at least partially defeats the
+purpose. The curses package is used to handle the I/O which deals with the
+terminal's capabilities.
+
+While ee is based on curses, I have included here the source code to
+new_curse, a subset of curses developed for use with ee. 'curses' often
+will have a defect that reduces the usefulness of the editor relying upon
+it. This is unused by the FreeBSD version of ee (the existing ncurses
+library works just fine) but is included in the doc subdirectory for
+reference purposes should anyone wish to port ee to a platform for
+which the existing curses libraries are insufficient.
+
+The files doc/new_curse.[ch] contain a subset of the 'curses' library
+used by applications to handle screen output. Unfortunately, curses
+varies from system to system, so I developed new_curse to provide
+consistent behavior across systems. It works on both SystemV and BSD
+systems, and while it can sometimes be slower than other curses packages,
+it will get the information on the screen painted correctly more often
+than vendor supplied curses. Again, FreeBSD does not have this problem
+but you may find it useful on other platforms.
+
+If you experience problems with data being displayed improperly, check
+your terminal configuration, especially if you're using a terminal
+emulator, and make sure that you are using the right terminfo entry
+before rummaging through code. Terminfo entries often contain
+inaccuracies, or incomplete information, or may not totally match the
+terminal or emulator the terminal information is being used with.
+Complaints that ee isn't working quite right often end up being something
+else (like the terminal emulator being used).
+
+Both ee and new_curse were developed using K&R C (also known as "classic
+C"), but it can also be compiled with ANSI C. You should be able to
+build ee by simply typing "make".
+
+ee is the result of several conflicting design goals. While I know that it
+solves the problems of some users, I also have no doubt that some will decry
+its lack of more features. I will settle for knowing that ee does fulfill
+the needs of a minority (but still large number) of users. The goals of ee
+are:
+
+ 1. To be so easy to use as to require no instruction.
+ 2. To be easy to compile and, if necessary, port to new platforms
+ by people with relatively little knowledge of C and UNIX.
+ 3. To have a minimum number of files to be dealt with, for compile
+ and installation.
+ 4. To have enough functionality to be useful to a large number of
+ people.
+
+Hugh Mahon |___|
+h_mahon@fc.hp.com | |
+ |\ /|
+ | \/ |
+
diff --git a/usr.bin/ee/doc/Artistic b/usr.bin/ee/doc/Artistic
new file mode 100644
index 0000000..fbf7989
--- /dev/null
+++ b/usr.bin/ee/doc/Artistic
@@ -0,0 +1,117 @@
+
+
+
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) accompany any non-standard executables with their corresponding
+ Standard Version executables, giving the non-standard executables
+ non-standard names, and clearly documenting the differences in manual
+ pages (or equivalent), together with instructions on where to get
+ the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this Package.
+You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.
+
+7. C subroutines supplied by you and linked into this Package in order
+to emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/usr.bin/ee/doc/ee.i18n.guide b/usr.bin/ee/doc/ee.i18n.guide
new file mode 100644
index 0000000..0850c2e
--- /dev/null
+++ b/usr.bin/ee/doc/ee.i18n.guide
@@ -0,0 +1,141 @@
+Easy Editor ("ee") provides the ability to translate the messages displayed to
+the user and the commands entered. This is done via message catalogs,
+following X/Open standards. ee only supports eight bit characters.
+
+(The name ee.i18n.guide is for "ee internationalization guide". The i18n
+abbreviation is used because there are 18 characters between the first
+letter ("i") and last ("n") of "internationalization".)
+
+All of the messages, warnings, information, and commands, are contained in the
+message catalog. Each numbered entry represents an individual string used by
+ee. Some strings contain formatting information for formatted print
+statements, which are of the form "%s", or "%d", these must be preserved in
+the translation, or the correct information will not be displayed. For those
+strings containing multiple formatting codes, the order of each item must be
+preserved as well.
+
+Message content
+1 title for modes, or settings menu
+2 - 8 entries for modes menu, each line should be the same length
+ (padded with spaces)
+9 - 34 other menu titles and entries
+35 - 56 help screen
+57 - 61 actions assigned to control keys
+62 - 66 commands information
+67 message displayed when info window turned off
+68 indication that no file name was entered when invoking ee
+69 prompt for decimal value of character to be entered
+70 message displaying the print command being invoked
+71 prompt for command
+72 prompt for name of file to be written
+73 prompt for name of file to be read
+74 string used to display the decimal value of the character
+ the cursor is on
+75 string displaying an unrecognized command
+76 string indicating that the command entered is not a unique
+ substring of a valid command
+77 string indicating the current line number
+78 string for displaying the length of the line
+79 string for displaying the name of the file
+80 - 83 strings showing how to invoke ee, and its options
+84 message indicating that the file entered is a directory, not a
+ text file
+85 message informing that the entered file does not yet exist
+86 message informing that the file can't be opened (because of
+ permission problems)
+87 message after file has been read with the file name and number
+ of lines read
+88 message indicating that the file has been read
+89 message indicating that the file is being read
+90 message indicating that permissions only allow the file to be
+ read, not written
+91 message after file has been read with the file name and number
+ of lines read
+92 prompt for name of file to be saved (used when no name was
+ entered for a file to edit)
+93 message indicating that the file was not written, since no
+ name was entered at the prompt
+94 prompt asking user if changes should not be saved ("yes_char"
+ will be expected for affirmative response)
+95 "yes" character, single character expected to confirm action
+ (can be upper or lower case, will be converted to upper-case
+ during test)
+96 prompt
+97 error message
+98 message indicating that the named file is being written
+99 message indicating the name of the file written, the number of
+ lines, and the number of characters (order of items must be
+ maintained)
+100 search in progress message
+101 message that the string was not found
+102 prompt for search
+103 message that string could not be executed
+104 self-explanatory
+105 message for menus, indicating that the Escape character will
+ allow the user to exit the menu
+106 error message indicating the menu won't fit on the screen
+107 self-explanatory
+108 prompt for shell command
+109 message displayed while formatting a paragraph
+110 string which places message for spell checking at top of
+ buffer (the portions 'list of unrecognized words' and
+ '-=-=-=-=-=-' may be replaced, but the rest must remain the
+ same)
+111 message informing that spell checking is in progress
+112 prompt for right margin
+113 error informing user that operation is not permitted in ree
+114 string indicating mode is turned 'on' in modes menu
+115 string indicating mode is turned 'off' in modes menu
+116 - 131 strings used for commands (some also used for initialization)
+132 - 144 strings used for initialization
+145 entry for settings menu for emacs key bindings settings
+146 - 153 help screen entries for emacs key bindings info
+154 - 158 info window entries for emacs key bindings info
+159 string for turning on emacs key bindings in the init file
+160 string for turning off emacs key bindings in the init file
+
+Care should be taken when translating commands and initialization keywords
+because the algorithm used for detecting uniqueness of entered commands
+will not be able to distinguish words that are not unique before the end
+of the shorter word, for example, it would not be able to distinguish the
+command 'abcd' from 'abcde'.
+
+After translating the messages, use the 'gencat' command to create the compiled
+catalog used when running the software. The standard syntax would be:
+
+ gencat ee.cat ee.msg
+
+Where ee.msg is the file containing the translations, and ee.cat is the
+compiled catalog. If the file ee.cat does not exist, it will be created.
+Check the documentation for your system for proper syntax.
+
+Message catalog placement varies from system to system. A common location
+for message catalogs is in /usr/lib/nls. In this directory are
+directories with the names of other languages. The default language is
+'C'. There is also an environment variable, named NLSPATH used to
+determine where message catalogs can be found. This variable is similar
+to the PATH variable used for commands, but with some differences. The
+NLSPATH variable must have the ability to handle different names for
+languages and the catalog files, so it has field descriptors for these. A
+typical setting for NLSPATH could be:
+
+ NLSPATH=/usr/lib/nls/%L/%N.cat:/usr/local/lib/nls/%L/%N.cat
+
+Where "%L" is the field descriptor for the language (obtained from the
+LANG environment variable) and "%N" is the name of the file (with the
+".cat" appended by the path variable, it is not passed from the requesting
+program). The colon (:) is used to separate paths, so in the above
+example there are two paths possible for message catalogs. You may wish
+to maintain catalogs for applications that are not supported by your
+system vendor in a location unique for you, and this is facilitated by the
+NLSPATH variable. Remember to set and export both the LANG and NLSPATH
+variables for each user that expects to use localization either in a
+system-wide profile or in each user's profile. See your system
+documentation for more information.
+
+The message catalog supplied with ee also uses the '$quote' directive to
+specify a quote around strings to ensure proper padding. This directive
+may not be supported on all systems, and lead to quotes being included in
+the string used in ee, which will cause incorrect behavior. If the
+'$quote' directive is not supported by your system's gencat command, edit
+the msg file to remove the leading and trailing quotation marks.
diff --git a/usr.bin/ee/doc/new_curse.c b/usr.bin/ee/doc/new_curse.c
new file mode 100644
index 0000000..0e6cd54
--- /dev/null
+++ b/usr.bin/ee/doc/new_curse.c
@@ -0,0 +1,3574 @@
+/*
+ | new_curse.c
+ |
+ | A subset of curses developed for use with ae.
+ |
+ | written by Hugh Mahon
+ |
+ | THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+ | NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+ | MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+ | IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ | FITNESS FOR A PARTICULAR PURPOSE. Neither
+ | Hewlett-Packard nor Hugh Mahon shall be liable
+ | for errors contained herein, nor for
+ | incidental or consequential damages in
+ | connection with the furnishing, performance or
+ | use of this material. Neither Hewlett-Packard
+ | nor Hugh Mahon assumes any responsibility for
+ | the use or reliability of this software or
+ | documentation. This software and
+ | documentation is totally UNSUPPORTED. There
+ | is no support contract available. Hewlett-
+ | Packard has done NO Quality Assurance on ANY
+ | of the program or documentation. You may find
+ | the quality of the materials inferior to
+ | supported materials.
+ |
+ | This software is not a product of Hewlett-Packard, Co., or any
+ | other company. No support is implied or offered with this software.
+ | You've got the source, and you're on your own.
+ |
+ | This software may be distributed under the terms of Larry Wall's
+ | Artistic license, a copy of which is included in this distribution.
+ |
+ | This notice must be included with this software and any derivatives.
+ |
+ | Copyright (c) 1986, 1987, 1988, 1991, 1992, 1993, 1994, 1995 Hugh Mahon
+ | All are rights reserved.
+ |
+ | $Header: /home/hugh/sources/old_ae/RCS/new_curse.c,v 1.37 1995/08/28 23:49:26 hugh Exp $
+ |
+ */
+
+char *copyright_message[] = { "Copyright (c) 1986, 1987, 1988, 1991, 1992, 1993, 1994, 1995 Hugh Mahon",
+ "All rights are reserved."};
+
+char * new_curse_name= "@(#) new_curse.c $Revision: 1.37 $";
+
+#include "new_curse.h"
+#include <signal.h>
+#include <fcntl.h>
+
+#ifdef SYS5
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#ifdef BSD_SELECT
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef SLCT_HDR
+#include <sys/select.h> /* on AIX */
+#endif /* SLCT_HDR */
+
+#endif /* BSD_SELECT */
+
+#ifdef HAS_STDLIB
+#include <stdlib.h>
+#endif
+
+#if defined(__STDC__)
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAS_UNISTD
+#include <unistd.h>
+#endif
+
+#ifdef HAS_SYS_IOCTL
+#include <sys/ioctl.h>
+#endif
+
+
+WINDOW *curscr;
+static WINDOW *virtual_scr;
+WINDOW *stdscr;
+WINDOW *last_window_refreshed;
+
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+#endif
+
+#define min(a, b) (a < b ? a : b)
+
+#ifndef CAP
+#define String_Out(table, stack, place) Info_Out(table, stack, place)
+#else
+#define String_Out(table, stack, place) Cap_Out(table, stack, place)
+#endif
+
+#define bw__ 0 /* booleans */
+#define am__ 1
+#define xb__ 2
+#define xs__ 3 /* hp glitch (standout not erased by overwrite) */
+#define xn__ 4
+#define eo__ 5
+#define gn__ 6 /* generic type terminal */
+#define hc__ 7 /* hardcopy terminal */
+#define km__ 8
+#define hs__ 9
+#define in__ 10
+#define da__ 11
+#define db__ 12
+#define mi__ 13 /* safe to move during insert mode */
+#define ms__ 14 /* safe to move during standout mode */
+#define os__ 15
+#define es__ 16
+#define xt__ 17
+#define hz__ 18 /* hazeltine glitch */
+#define ul__ 19
+#define xo__ 20
+#define chts__ 21
+#define nxon__ 22
+#define nrrmc__ 23
+#define npc__ 24
+#define mc5i__ 25
+
+#define co__ 0 /* number of columns */ /* numbers */
+#define it__ 1 /* spaces per tab */
+#define li__ 2 /* number of lines */
+#define lm__ 3
+#define sg__ 4 /* magic cookie glitch */
+#define pb__ 5
+#define vt__ 6
+#define ws__ 7
+
+#define cols__ 0
+#define lines__ 2
+#define xmc__ 4
+#define vt__ 6
+#define wsl__ 7
+#define nlab__ 8
+#define lh__ 9
+#define lw__ 10
+
+#define bt__ 0 /* back tab */ /* strings */
+#define bl__ 1 /* bell */
+#define cr__ 2 /* carriage return */
+#define cs__ 3 /* change scroll region */
+#define ct__ 4 /* clear all tab stops */
+#define cl__ 5 /* clear screen and home cursor */
+#define ce__ 6 /* clear to end of line */
+#define cd__ 7 /* clear to end of display */
+#define ch__ 8 /* set cursor column */
+#define CC__ 9 /* term, settable cmd char in */
+#define cm__ 10 /* screen rel cursor motion, row, column */
+#define do__ 11 /* down one line */
+#define ho__ 12 /* home cursor */
+#define vi__ 13 /* make cursor invisible */
+#define le__ 14 /* move cursor left one space */
+#define CM__ 15 /* memory rel cursor addressing */
+#define ve__ 16 /* make cursor appear normal */
+#define nd__ 17 /* non-destructive space (cursor right) */
+#define ll__ 18 /* last line, first col */
+#define up__ 19 /* cursor up */
+#define vs__ 20
+#define dc__ 21 /* delete character */
+#define dl__ 22 /* delete line */
+#define ds__ 23
+#define hd__ 24
+#define as__ 25
+#define mb__ 26
+#define md__ 27 /* turn on bold */
+#define ti__ 28
+#define dm__ 29 /* turn on delete mode */
+#define mh__ 30 /* half bright mode */
+#define im__ 31 /* insert mode */
+#define mk__ 32
+#define mp__ 33
+#define mr__ 34
+#define so__ 35 /* enter standout mode */
+#define us__ 36
+#define ec__ 37
+#define ae__ 38
+#define me__ 39
+#define te__ 40
+#define ed__ 41
+#define ei__ 42 /* exit insert mode */
+#define se__ 43 /* exit standout mode */
+#define ue__ 44
+#define vb__ 45
+#define ff__ 46
+#define fs__ 47
+#define i1__ 48
+#define i2__ 49
+#define i3__ 50
+#define if__ 51
+#define ic__ 52
+#define al__ 53
+#define ip__ 54
+#define kb__ 55 /* backspace key */
+#define ka__ 56
+#define kC__ 57
+#define kt__ 58
+#define kD__ 59
+#define kL__ 60
+#define kd__ 61
+#define kM__ 62
+#define kE__ 63
+#define kS__ 64
+#define k0__ 65
+#define k1__ 66
+#define kf10__ 67
+#define k2__ 68
+#define k3__ 69
+#define k4__ 70
+#define k5__ 71
+#define k6__ 72
+#define k7__ 73
+#define k8__ 74
+#define k9__ 75
+#define kh__ 76
+#define kI__ 77
+#define kA__ 78
+#define kl__ 79
+#define kH__ 80
+#define kN__ 81
+#define kP__ 82
+#define kr__ 83
+#define kF__ 84
+#define kR__ 85
+#define kT__ 86
+#define ku__ 87 /* key up */
+#define ke__ 88
+#define ks__ 89
+#define l0__ 90
+#define l1__ 91
+#define la__ 92
+#define l2__ 93
+#define l3__ 94
+#define l4__ 95
+#define l5__ 96
+#define l6__ 97
+#define l7__ 98
+#define l8__ 99
+#define l9__ 100
+#define mo__ 101
+#define mm__ 102
+#define nw__ 103
+#define pc__ 104
+#define DC__ 105
+#define DL__ 106
+#define DO__ 107
+#define IC__ 118
+#define SF__ 109
+#define AL__ 110
+#define LE__ 111
+#define RI__ 112
+#define SR__ 113
+#define UP__ 114
+#define pk__ 115
+#define pl__ 116
+#define px__ 117
+#define ps__ 118
+#define pf__ 119
+#define po__ 120
+#define rp__ 121
+#define r1__ 122
+#define r2__ 123
+#define r3__ 124
+#define rf__ 125
+#define rc__ 126
+#define cv__ 127
+#define sc__ 128
+#define sf__ 129
+#define sr__ 130
+#define sa__ 131 /* sgr */
+#define st__ 132
+#define wi__ 133
+#define ta__ 134
+#define ts__ 135
+#define uc__ 136
+#define hu__ 137
+#define iP__ 138
+#define K1__ 139
+#define K2__ 140
+#define K3__ 141
+#define K4__ 142
+#define K5__ 143
+#define pO__ 144
+#define ml__ 145
+#define mu__ 146
+#define rmp__ 145
+#define acsc__ 146
+#define pln__ 147
+#define kcbt__ 148
+#define smxon__ 149
+#define rmxon__ 150
+#define smam__ 151
+#define rmam__ 152
+#define xonc__ 153
+#define xoffc__ 154
+#define enacs__ 155
+#define smln__ 156
+#define rmln__ 157
+#define kbeg__ 158
+#define kcan__ 159
+#define kclo__ 160
+#define kcmd__ 161
+#define kcpy__ 162
+#define kcrt__ 163
+#define kend__ 164
+#define kent__ 165
+#define kext__ 166
+#define kfnd__ 167
+#define khlp__ 168
+#define kmrk__ 169
+#define kmsg__ 170
+#define kmov__ 171
+#define knxt__ 172
+#define kopn__ 173
+#define kopt__ 174
+#define kprv__ 175
+#define kprt__ 176
+#define krdo__ 177
+#define kref__ 178
+#define krfr__ 179
+#define krpl__ 180
+#define krst__ 181
+#define kres__ 182
+#define ksav__ 183
+#define kspd__ 184
+#define kund__ 185
+#define kBEG__ 186
+#define kCAN__ 187
+#define kCMD__ 188
+#define kCPY__ 189
+#define kCRT__ 190
+#define kDC__ 191
+#define kDL__ 192
+#define kslt__ 193
+#define kEND__ 194
+#define kEOL__ 195
+#define kEXT__ 196
+#define kFND__ 197
+#define kHLP__ 198
+#define kHOM__ 199
+#define kIC__ 200
+#define kLFT__ 201
+#define kMSG__ 202
+#define kMOV__ 203
+#define kNXT__ 204
+#define kOPT__ 205
+#define kPRV__ 206
+#define kPRT__ 207
+#define kRDO__ 208
+#define kRPL__ 209
+#define kRIT__ 210
+#define kRES__ 211
+#define kSAV__ 212
+#define kSPD__ 213
+#define kUND__ 214
+#define rfi__ 215
+#define kf11__ 216
+#define kf12__ 217
+#define kf13__ 218
+#define kf14__ 219
+#define kf15__ 220
+#define kf16__ 221
+#define kf17__ 222
+#define kf18__ 223
+#define kf19__ 224
+#define kf20__ 225
+#define kf21__ 226
+#define kf22__ 227
+#define kf23__ 228
+#define kf24__ 229
+#define kf25__ 230
+#define kf26__ 231
+#define kf27__ 232
+#define kf28__ 233
+#define kf29__ 234
+#define kf30__ 235
+#define kf31__ 236
+#define kf32__ 237
+#define kf33__ 238
+#define kf34__ 239
+#define kf35__ 240
+#define kf36__ 241
+#define kf37__ 242
+#define kf38__ 243
+#define kf39__ 244
+#define kf40__ 245
+#define kf41__ 246
+#define kf42__ 247
+#define kf43__ 248
+#define kf44__ 249
+#define kf45__ 250
+#define kf46__ 251
+#define kf47__ 252
+#define kf48__ 253
+#define kf49__ 254
+#define kf50__ 255
+#define kf51__ 256
+#define kf52__ 257
+#define kf53__ 258
+#define kf54__ 259
+#define kf55__ 260
+#define kf56__ 261
+#define kf57__ 262
+#define kf58__ 263
+#define kf59__ 264
+#define kf60__ 265
+#define kf61__ 266
+#define kf62__ 267
+#define kf63__ 268
+#define el1__ 269
+#define mgc__ 270
+#define smgl__ 271
+#define smgr__ 272
+
+#ifdef CAP
+char *Boolean_names[] = {
+"bw", "am", "xb", "xs", "xn", "eo", "gn", "hc", "km", "hs", "in", "da", "db",
+"mi", "ms", "os", "es", "xt", "hz", "ul", "xo", "HC", "nx", "NR", "NP", "5i"
+};
+
+char *Number_names[] = {
+"co#", "it#", "li#", "lm#", "sg#", "pb#", "vt#", "ws#", "Nl#", "lh#", "lw#"
+};
+
+char *String_names[] = {
+"bt=", "bl=", "cr=", "cs=", "ct=", "cl=", "ce=", "cd=", "ch=", "CC=", "cm=",
+"do=", "ho=", "vi=", "le=", "CM=", "ve=", "nd=", "ll=", "up=", "vs=", "dc=",
+"dl=", "ds=", "hd=", "as=", "mb=", "md=", "ti=", "dm=", "mh=", "im=", "mk=",
+"mp=", "mr=", "so=", "us=", "ec=", "ae=", "me=", "te=", "ed=", "ei=", "se=",
+"ue=", "vb=", "ff=", "fs=", "i1=", "i2=", "i3=", "if=", "ic=", "al=", "ip=",
+"kb=", "ka=", "kC=", "kt=", "kD=", "kL=", "kd=", "kM=", "kE=", "kS=", "k0=",
+"k1=", "k;=", "k2=", "k3=", "k4=", "k5=", "k6=", "k7=", "k8=", "k9=", "kh=",
+"kI=", "kA=", "kl=", "kH=", "kN=", "kP=", "kr=", "kF=", "kR=", "kT=", "ku=",
+"ke=", "ks=", "l0=", "l1=", "la=", "l2=", "l3=", "l4=", "l5=", "l6=", "l7=",
+"l8=", "l9=", "mo=", "mm=", "nw=", "pc=", "DC=", "DL=", "DO=", "IC=", "SF=",
+"AL=", "LE=", "RI=", "SR=", "UP=", "pk=", "pl=", "px=", "ps=", "pf=", "po=",
+"rp=", "r1=", "r2=", "r3=", "rf=", "rc=", "cv=", "sc=", "sf=", "sr=", "sa=",
+"st=", "wi=", "ta=", "ts=", "uc=", "hu=", "iP=", "K1=", "K3=", "K2=", "K4=",
+"K5=", "pO=", "rP=", "ac=", "pn=", "kB=", "SX=", "RX=", "SA=", "RA=", "XN=",
+"XF=", "eA=", "LO=", "LF=", "@1=", "@2=", "@3=", "@4=", "@5=", "@6=", "@7=",
+"@8=", "@9=", "@0=", "%1=", "%2=", "%3=", "%4=", "%5=", "%6=", "%7=", "%8=",
+"%9=", "%0=", "&1=", "&2=", "&3=", "&4=", "&5=", "&6=", "&7=", "&8=", "&9=",
+"&0=", "*1=", "*2=", "*3=", "*4=", "*5=", "*6=", "*7=", "*8=", "*9=", "*0=",
+"#1=", "#2=", "#3=", "#4=", "%a=", "%b=", "%c=", "%d=", "%e=", "%f=", "%g=",
+"%h=", "%i=", "%j=", "!1=", "!2=", "!3=", "RF=", "F1=", "F2=", "F3=", "F4=",
+"F5=", "F6=", "F7=", "F8=", "F9=", "FA=", "FB=", "FC=", "FD=", "FE=", "FF=",
+"FG=", "FH=", "FI=", "FJ=", "FK=", "FL=", "FM=", "FN=", "FO=", "FP=", "FQ=",
+"FR=", "FS=", "FT=", "FU=", "FV=", "FW=", "FX=", "FY=", "FZ=", "Fa=", "Fb=",
+"Fc=", "Fd=", "Fe=", "Ff=", "Fg=", "Fh=", "Fi=", "Fj=", "Fk=", "Fl=", "Fm=",
+"Fn=", "Fo=", "Fp=", "Fq=", "Fr=", "cb=", "MC=", "ML=", "MR="
+};
+#endif
+
+char *new_curse = "October 1987";
+
+char in_buff[100]; /* buffer for ungetch */
+int bufp; /* next free position in in_buff */
+
+char *TERMINAL_TYPE = NULL; /* terminal type to be gotten from environment */
+int CFOUND = FALSE;
+int Data_Line_len = 0;
+int Max_Key_len; /* max length of a sequence sent by a key */
+char *Data_Line = NULL;
+char *TERM_PATH = NULL;
+char *TERM_data_ptr = NULL;
+char *Term_File_name = NULL; /* name of file containing terminal description */
+FILE *TFP; /* file pointer to file with terminal des. */
+int Fildes; /* file descriptor for terminfo file */
+int STAND = FALSE; /* is standout mode activated? */
+int TERM_INFO = FALSE; /* is terminfo being used (TRUE), or termcap (FALSE) */
+int Time_Out; /* set when time elapsed while trying to read function key */
+int Curr_x; /* current x position on screen */
+int Curr_y; /* current y position on the screen */
+int LINES;
+int COLS;
+int Move_It; /* flag to move cursor if magic cookie glitch */
+int initialized = FALSE; /* tells whether new_curse is initialized */
+float speed;
+float chars_per_millisecond;
+int Repaint_screen; /* if an operation to change screen impossible, repaint screen */
+int Intr; /* storeage for interrupt character */
+int Parity; /* 0 = no parity, 1 = odd parity, 2 = even parity */
+int Noblock; /* for BSD systems */
+int Num_bits; /* number of bits per character */
+int Flip_Bytes; /* some systems have byte order reversed */
+int interrupt_flag = FALSE; /* set true if SIGWINCH received */
+
+#ifndef CAP
+char *Strings;
+#endif
+
+struct KEYS {
+ int length; /* length of string sent by key */
+ char *string; /* string sent by key */
+ int value; /* CURSES value of key (9-bit) */
+ };
+
+struct KEY_STACK {
+ struct KEYS *element;
+ struct KEY_STACK *next;
+ };
+
+struct KEY_STACK *KEY_TOS = NULL;
+struct KEY_STACK *KEY_POINT;
+
+struct Parameters {
+ int value;
+ struct Parameters *next;
+ };
+
+int Key_vals[] = {
+ 0407, 0526, 0515, 0525, 0512, 0510, 0402, 0514, 0517, 0516, 0410, 0411,
+ 0422, 0412, 0413, 0414, 0415, 0416, 0417, 0420, 0421, 0406, 0513, 0511,
+ 0404, 0533, 0522, 0523, 0405, 0520, 0521, 0524, 0403,
+ 0534, 0535, 0536, 0537, 0540, 0541, 0542, 0543, 0544, 0545, 0546, 0547,
+ 0550, 0527, 0551, 0552, 0553, 0554, 0555, 0556, 0557, 0560, 0561, 0562,
+ 0532, 0563, 0564, 0565, 0566, 0567, 0570, 0571, 0627, 0630, 0572, 0573,
+ 0574, 0575, 0576, 0577, 0600, 0601, 0602, 0603, 0604, 0605, 0606, 0607,
+ 0610, 0611, 0612, 0613, 0614, 0615, 0616, 0617, 0620, 0621, 0622, 0623,
+ 0624, 0625, 0626, 0423, 0424, 0425, 0426, 0427, 0430, 0431,
+ 0432, 0433, 0434, 0435, 0436, 0437, 0440, 0441, 0442, 0443, 0444, 0445,
+ 0446, 0447, 0450, 0451, 0452, 0453, 0454, 0455, 0456, 0457, 0460, 0461,
+ 0462, 0463, 0464, 0465, 0466, 0467, 0470, 0471, 0472, 0473, 0474, 0475,
+ 0476, 0477, 0500, 0501, 0502, 0503, 0504, 0505, 0506, 0507
+};
+
+int attributes_set[9];
+
+#ifdef SYS5
+struct termio Terminal;
+struct termio Saved_tty;
+#else
+struct sgttyb Terminal;
+struct sgttyb Saved_tty;
+#endif
+
+char *tc_;
+
+int Booleans[128];
+int Numbers[128];
+char *String_table[1024];
+
+int *virtual_lines;
+
+static char nc_scrolling_ability = FALSE;
+
+#ifdef CAP
+
+#if __STDC__ || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif /* __STDC__ */
+
+int tc_Get_int P_((int));
+void CAP_PARSE P_((void));
+void Find_term P_((void));
+
+#undef P_
+
+#endif /* CAP */
+
+
+#ifndef __STDC__
+#ifndef HAS_STDLIB
+extern char *fgets();
+extern char *malloc();
+extern char *getenv();
+FILE *fopen(); /* declaration for open function */
+#endif /* HAS_STDLIB */
+#endif /* __STDC__ */
+
+#ifdef SIGWINCH
+
+/*
+ | Copy the contents of one window to another.
+ */
+
+void
+copy_window(origin, destination)
+WINDOW *origin, *destination;
+{
+ int row, column;
+ struct _line *orig, *dest;
+
+ orig = origin->first_line;
+ dest = destination->first_line;
+
+ for (row = 0;
+ row < (min(origin->Num_lines, destination->Num_lines));
+ row++)
+ {
+ for (column = 0;
+ column < (min(origin->Num_cols, destination->Num_cols));
+ column++)
+ {
+ dest->row[column] = orig->row[column];
+ dest->attributes[column] = orig->attributes[column];
+ }
+ dest->changed = orig->changed;
+ dest->scroll = orig->scroll;
+ dest->last_char = min(orig->last_char, destination->Num_cols);
+ orig = orig->next_screen;
+ dest = dest->next_screen;
+ }
+ destination->LX = min((destination->Num_cols - 1), origin->LX);
+ destination->LY = min((destination->Num_lines - 1), origin->LY);
+ destination->Attrib = origin->Attrib;
+ destination->scroll_up = origin->scroll_up;
+ destination->scroll_down = origin->scroll_down;
+ destination->SCROLL_CLEAR = origin->SCROLL_CLEAR;
+}
+
+void
+reinitscr(foo)
+int foo;
+{
+ WINDOW *local_virt;
+ WINDOW *local_std;
+ WINDOW *local_cur;
+
+ signal(SIGWINCH, reinitscr);
+#ifdef TIOCGWINSZ
+ if (ioctl(0, TIOCGWINSZ, &ws) >= 0)
+ {
+ if (ws.ws_row == LINES && ws.ws_col == COLS)
+ return;
+ if (ws.ws_row > 0)
+ LINES = ws.ws_row;
+ if (ws.ws_col > 0)
+ COLS = ws.ws_col;
+ }
+#endif /* TIOCGWINSZ */
+ local_virt = newwin(LINES, COLS, 0, 0);
+ local_std = newwin(LINES, COLS, 0, 0);
+ local_cur = newwin(LINES, COLS, 0, 0);
+ copy_window(virtual_scr, local_virt);
+ copy_window(stdscr, local_std);
+ copy_window(curscr, local_cur);
+ delwin(virtual_scr);
+ delwin(stdscr);
+ delwin(curscr);
+ virtual_scr = local_virt;
+ stdscr = local_std;
+ curscr = local_cur;
+ free(virtual_lines);
+ virtual_lines = (int *) malloc(LINES * (sizeof(int)));
+ interrupt_flag = TRUE;
+}
+#endif /* SIGWINCH */
+
+void
+initscr() /* initialize terminal for operations */
+{
+ int value;
+ char *lines_string;
+ char *columns_string;
+#ifdef CAP
+ char *pointer;
+#endif /* CAP */
+
+#ifdef DIAG
+printf("starting initscr \n");fflush(stdout);
+#endif
+ if (initialized)
+ return;
+#ifdef BSD_SELECT
+ setbuf(stdin, NULL);
+#endif /* BSD_SELECT */
+ Flip_Bytes = FALSE;
+ Parity = 0;
+ Time_Out = FALSE;
+ bufp = 0;
+ Move_It = FALSE;
+ Noblock = FALSE;
+#ifdef SYS5
+ value = ioctl(0, TCGETA, &Terminal);
+ if (Terminal.c_cflag & PARENB)
+ {
+ if (Terminal.c_cflag & PARENB)
+ Parity = 1;
+ else
+ Parity = 2;
+ }
+ if ((Terminal.c_cflag & CS8) == CS8)
+ {
+ Num_bits = 8;
+ }
+ else if ((Terminal.c_cflag & CS7) == CS7)
+ Num_bits = 7;
+ else if ((Terminal.c_cflag & CS6) == CS6)
+ Num_bits = 6;
+ else
+ Num_bits = 5;
+ value = Terminal.c_cflag & 037;
+ switch (value) {
+ case 01: speed = 50.0;
+ break;
+ case 02: speed = 75.0;
+ break;
+ case 03: speed = 110.0;
+ break;
+ case 04: speed = 134.5;
+ break;
+ case 05: speed = 150.0;
+ break;
+ case 06: speed = 200.0;
+ break;
+ case 07: speed = 300.0;
+ break;
+ case 010: speed = 600.0;
+ break;
+ case 011: speed = 900.0;
+ break;
+ case 012: speed = 1200.0;
+ break;
+ case 013: speed = 1800.0;
+ break;
+ case 014: speed = 2400.0;
+ break;
+ case 015: speed = 3600.0;
+ break;
+ case 016: speed = 4800.0;
+ break;
+ case 017: speed = 7200.0;
+ break;
+ case 020: speed = 9600.0;
+ break;
+ case 021: speed = 19200.0;
+ break;
+ case 022: speed = 38400.0;
+ break;
+ default: speed = 0.0;
+ }
+#else
+ value = ioctl(0, TIOCGETP, &Terminal);
+ if (Terminal.sg_flags & EVENP)
+ Parity = 2;
+ else if (Terminal.sg_flags & ODDP)
+ Parity = 1;
+ value = Terminal.sg_ospeed;
+ switch (value) {
+ case 01: speed = 50.0;
+ break;
+ case 02: speed = 75.0;
+ break;
+ case 03: speed = 110.0;
+ break;
+ case 04: speed = 134.5;
+ break;
+ case 05: speed = 150.0;
+ break;
+ case 06: speed = 200.0;
+ break;
+ case 07: speed = 300.0;
+ break;
+ case 010: speed = 600.0;
+ break;
+ case 011: speed = 1200.0;
+ break;
+ case 012: speed = 1800.0;
+ break;
+ case 013: speed = 2400.0;
+ break;
+ case 014: speed = 4800.0;
+ break;
+ case 015: speed = 9600.0;
+ break;
+ default: speed = 0.0;
+ }
+#endif
+ chars_per_millisecond = (0.001 * speed) / 8.0;
+ TERMINAL_TYPE = getenv("TERM");
+ if (TERMINAL_TYPE == NULL)
+ {
+ printf("unknown terminal type\n");
+ exit(0);
+ }
+#ifndef CAP
+ Fildes = -1;
+ TERM_PATH = getenv("TERMINFO");
+ if (TERM_PATH != NULL)
+ {
+ Data_Line_len = 23 + strlen(TERM_PATH) + strlen(TERMINAL_TYPE);
+ Term_File_name = malloc(Data_Line_len);
+ sprintf(Term_File_name, "%s/%c/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ }
+ if (Fildes == -1)
+ {
+ TERM_PATH = "/usr/lib/terminfo";
+ Data_Line_len = 23 + strlen(TERM_PATH) + strlen(TERMINAL_TYPE);
+ Term_File_name = malloc(Data_Line_len);
+ sprintf(Term_File_name, "%s/%c/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ }
+ if (Fildes == -1)
+ {
+ TERM_PATH = "/usr/share/lib/terminfo";
+ Data_Line_len = 23 + strlen(TERM_PATH) + strlen(TERMINAL_TYPE);
+ Term_File_name = malloc(Data_Line_len);
+ sprintf(Term_File_name, "%s/%c/%s", TERM_PATH, *TERMINAL_TYPE, TERMINAL_TYPE);
+ Fildes = open(Term_File_name, O_RDONLY);
+ }
+ if (Fildes == -1)
+ {
+ free(Term_File_name);
+ Term_File_name = NULL;
+ }
+ else
+ TERM_INFO = INFO_PARSE();
+#else
+ /*
+ | termcap information can be in the TERMCAP env variable, if so
+ | use that, otherwise check the /etc/termcap file
+ */
+ if ((pointer = Term_File_name = getenv("TERMCAP")) != NULL)
+ {
+ if (*Term_File_name != '/')
+ Term_File_name = "/etc/termcap";
+ }
+ else
+ {
+ Term_File_name = "/etc/termcap";
+ }
+ if ((TFP = fopen(Term_File_name, "r")) == NULL)
+ {
+ printf("unable to open /etc/termcap file \n");
+ exit(0);
+ }
+ for (value = 0; value < 1024; value++)
+ String_table[value] = NULL;
+ for (value = 0; value < 128; value++)
+ Booleans[value] = 0;
+ for (value = 0; value < 128; value++)
+ Numbers[value] = 0;
+ Data_Line = malloc(512);
+ if (pointer && *pointer != '/')
+ {
+ TERM_data_ptr = pointer;
+ CAP_PARSE();
+ }
+ else
+ {
+ Find_term();
+ CAP_PARSE();
+ }
+#endif
+ if (String_table[pc__] == NULL)
+ String_table[pc__] = "\0";
+ if ((String_table[cm__] == NULL) || (Booleans[hc__]))
+ {
+ fprintf(stderr, "sorry, unable to use this terminal type for screen editing\n");
+ exit(0);
+ }
+ Key_Get();
+ LINES = Numbers[li__];
+ COLS = Numbers[co__];
+ if ((lines_string = getenv("LINES")) != NULL)
+ {
+ value = atoi(lines_string);
+ if (value > 0)
+ LINES = value;
+ }
+ if ((columns_string = getenv("COLUMNS")) != NULL)
+ {
+ value = atoi(columns_string);
+ if (value > 0)
+ COLS = value;
+ }
+#ifdef TIOCGWINSZ
+ /*
+ | get the window size
+ */
+ if (ioctl(0, TIOCGWINSZ, &ws) >= 0)
+ {
+ if (ws.ws_row > 0)
+ LINES = ws.ws_row;
+ if (ws.ws_col > 0)
+ COLS = ws.ws_col;
+ }
+#endif
+ virtual_scr = newwin(LINES, COLS, 0, 0);
+ stdscr = newwin(LINES, COLS, 0, 0);
+ curscr = newwin(LINES, COLS, 0, 0);
+ wmove(stdscr, 0, 0);
+ werase(stdscr);
+ Repaint_screen = TRUE;
+ initialized = TRUE;
+ virtual_lines = (int *) malloc(LINES * (sizeof(int)));
+
+#ifdef SIGWINCH
+ /*
+ | reset size of windows and LINES and COLS if term window
+ | changes size
+ */
+ signal(SIGWINCH, reinitscr);
+#endif /* SIGWINCH */
+
+ /*
+ | check if scrolling is available
+ */
+
+ nc_scrolling_ability = ((String_table[al__] != NULL) &&
+ (String_table[dl__])) || ((String_table[cs__])
+ && (String_table[sr__]));
+
+}
+
+#ifndef CAP
+int
+Get_int() /* get a two-byte integer from the terminfo file */
+{
+ int High_byte;
+ int Low_byte;
+ int temp;
+
+ Low_byte = *((unsigned char *) TERM_data_ptr++);
+ High_byte = *((unsigned char *) TERM_data_ptr++);
+ if (Flip_Bytes)
+ {
+ temp = Low_byte;
+ Low_byte = High_byte;
+ High_byte = temp;
+ }
+ if ((High_byte == 255) && (Low_byte == 255))
+ return (-1);
+ else
+ return(Low_byte + (High_byte * 256));
+}
+
+int
+INFO_PARSE() /* parse off the data in the terminfo data file */
+{
+ int offset;
+ int magic_number = 0;
+ int counter = 0;
+ int Num_names = 0;
+ int Num_bools = 0;
+ int Num_ints = 0;
+ int Num_strings = 0;
+ int string_table_len = 0;
+ char *temp_ptr;
+
+ TERM_data_ptr = Data_Line = malloc((10240 * (sizeof(char))));
+ Data_Line_len = read(Fildes, Data_Line, 10240);
+ if ((Data_Line_len >= 10240) || (Data_Line_len < 0))
+ return(0);
+ /*
+ | get magic number
+ */
+ magic_number = Get_int();
+ /*
+ | if magic number not right, reverse byte order and check again
+ */
+ if (magic_number != 282)
+ {
+ Flip_Bytes = TRUE;
+ TERM_data_ptr--;
+ TERM_data_ptr--;
+ magic_number = Get_int();
+ if (magic_number != 282)
+ return(0);
+ }
+ /*
+ | get the number of each type in the terminfo data file
+ */
+ Num_names = Get_int();
+ Num_bools = Get_int();
+ Num_ints = Get_int();
+ Num_strings = Get_int();
+ string_table_len = Get_int();
+ Strings = malloc(string_table_len);
+ while (Num_names > 0)
+ {
+ TERM_data_ptr++;
+ Num_names--;
+ }
+ counter = 0;
+ while (Num_bools)
+ {
+ Num_bools--;
+ Booleans[counter++] = *TERM_data_ptr++;
+ }
+ if (((unsigned int) TERM_data_ptr) & 1) /* force alignment */
+ TERM_data_ptr++;
+ counter = 0;
+ while (Num_ints)
+ {
+ Num_ints--;
+ Numbers[counter] = Get_int();
+ counter++;
+ }
+ temp_ptr = TERM_data_ptr + Num_strings + Num_strings;
+ memcpy(Strings, temp_ptr, string_table_len);
+ counter = bt__;
+ while (Num_strings)
+ {
+ Num_strings--;
+ if ((offset=Get_int()) != -1)
+ {
+ if (String_table[counter] == NULL)
+ String_table[counter] = Strings + offset;
+ }
+ else
+ String_table[counter] = NULL;
+ counter++;
+ }
+ close(Fildes);
+ free(Data_Line);
+ return(TRUE);
+}
+#endif /* ifndef CAP */
+
+int
+AtoI() /* convert ascii text to integers */
+{
+ int Temp;
+
+ Temp = 0;
+ while ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Temp = (Temp * 10) + (*TERM_data_ptr - '0');
+ TERM_data_ptr++;
+ }
+ return(Temp);
+}
+
+void
+Key_Get() /* create linked list with all key sequences obtained from terminal database */
+{
+ int Counter;
+ int Klen;
+ int key_def;
+ struct KEY_STACK *Spoint;
+
+ Max_Key_len = 0;
+ Counter = 0;
+ key_def = kb__;
+ while (key_def <= kf63__)
+ {
+ if (key_def == ke__)
+ key_def = K1__;
+ else if (key_def == (K5__ + 1))
+ key_def = kcbt__;
+ else if (key_def == (kcbt__ + 1))
+ key_def = kbeg__;
+ else if (key_def == (kUND__ + 1))
+ key_def = kf11__;
+ if (String_table[key_def] != NULL)
+ {
+ if (KEY_TOS == NULL)
+ Spoint = KEY_TOS = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ else
+ {
+ Spoint = KEY_TOS;
+ while (Spoint->next != NULL)
+ Spoint = Spoint->next;
+ Spoint->next = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ Spoint = Spoint->next;
+ }
+ Spoint->next = NULL;
+ Spoint->element = (struct KEYS *) malloc(sizeof(struct KEYS));
+ Spoint->element->string = String_table[key_def];
+ Spoint->element->length = strlen(String_table[key_def]);
+ Spoint->element->value = Key_vals[Counter];
+ Klen = strlen(Spoint->element->string);
+ if (Klen > Max_Key_len)
+ Max_Key_len = Klen;
+ /*
+ | Some terminal types accept keystrokes of the form
+ | \E[A and \EOA, substituting '[' for 'O'. Make a
+ | duplicate of such key strings (since the
+ | database will only have one version) so new_curse
+ | can understand both.
+ */
+ if ((Spoint->element->length > 1) &&
+ ((String_table[key_def][1] == '[') ||
+ (String_table[key_def][1] == 'O')))
+ {
+ Spoint->next = (struct KEY_STACK *) malloc(sizeof(struct KEY_STACK));
+ Spoint = Spoint->next;
+ Spoint->next = NULL;
+ Spoint->element = (struct KEYS *) malloc(sizeof(struct KEYS));
+ Spoint->element->length = strlen(String_table[key_def]);
+ Spoint->element->string = malloc(Spoint->element->length + 1);
+ strcpy(Spoint->element->string, String_table[key_def]);
+ Spoint->element->value = Key_vals[Counter];
+ Klen = strlen(Spoint->element->string);
+ if (Klen > Max_Key_len)
+ Max_Key_len = Klen;
+
+ if (String_table[key_def][1] == '[')
+ Spoint->element->string[1] = 'O';
+ else
+ Spoint->element->string[1] = '[';
+ }
+ }
+ key_def++;
+ Counter++;
+ }
+}
+
+#ifdef CAP
+char *
+String_Get(param) /* read the string */
+char *param;
+{
+ char *String;
+ char *Temp;
+ int Counter;
+
+ if (param == NULL)
+ {
+ while (*TERM_data_ptr != '=')
+ TERM_data_ptr++;
+ Temp = ++TERM_data_ptr;
+ Counter = 1;
+ while ((*Temp != ':') && (*Temp != (char)NULL))
+ {
+ Counter++;
+ Temp++;
+ }
+ if (Counter == 1) /* no data */
+ return(NULL);
+ String = Temp = malloc(Counter);
+ while ((*TERM_data_ptr != ':') && (*TERM_data_ptr != (char)NULL))
+ {
+ if (*TERM_data_ptr == '\\')
+ {
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == 'n')
+ *Temp = '\n';
+ else if (*TERM_data_ptr == 't')
+ *Temp = '\t';
+ else if (*TERM_data_ptr == 'b')
+ *Temp = '\b';
+ else if (*TERM_data_ptr == 'r')
+ *Temp = '\r';
+ else if (*TERM_data_ptr == 'f')
+ *Temp = '\f';
+ else if ((*TERM_data_ptr == 'e') || (*TERM_data_ptr == 'E'))
+ *Temp = '\033'; /* escape */
+ else if (*TERM_data_ptr == '\\')
+ *Temp = '\\';
+ else if (*TERM_data_ptr == '\'')
+ *Temp = '\'';
+ else if ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Counter = 0;
+ while ((*TERM_data_ptr >= '0') && (*TERM_data_ptr <= '9'))
+ {
+ Counter = (8 * Counter) + (*TERM_data_ptr - '0');
+ TERM_data_ptr++; /* ? */
+ }
+ *Temp = Counter;
+ TERM_data_ptr--;
+ }
+ TERM_data_ptr++;
+ Temp++;
+ }
+ else if (*TERM_data_ptr == '^')
+ {
+ TERM_data_ptr++;
+ if ((*TERM_data_ptr >= '@') && (*TERM_data_ptr <= '_'))
+ *Temp = *TERM_data_ptr - '@';
+ else if (*TERM_data_ptr == '?')
+ *Temp = 127;
+ TERM_data_ptr++;
+ Temp++;
+ }
+ else
+ *Temp++ = *TERM_data_ptr++;
+ }
+ *Temp = (char)NULL;
+ param = String;
+ }
+ else
+ {
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != ':'))
+ TERM_data_ptr++;
+ }
+ return(param);
+}
+
+int
+tc_Get_int(param) /* read the integer */
+int param;
+{
+ int Itemp;
+
+ if (param == 0)
+ {
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != '#'))
+ TERM_data_ptr++;
+ TERM_data_ptr++;
+ Itemp = AtoI();
+ param = Itemp;
+ }
+ else
+ {
+ while (*TERM_data_ptr != ':')
+ TERM_data_ptr++;
+ }
+ return(param);
+}
+
+void
+Find_term() /* find terminal description in termcap file */
+{
+ char *Name;
+ char *Ftemp;
+
+ Ftemp = Name = malloc(strlen(TERMINAL_TYPE + 1) + 1);
+ strcpy(Name, TERMINAL_TYPE);
+ while (*Ftemp != (char)NULL)
+ Ftemp++;
+ *Ftemp++ = '|';
+ *Ftemp = (char)NULL;
+ CFOUND = FALSE;
+ Data_Line_len = strlen(TERMINAL_TYPE) + 1;
+ while ((!CFOUND) && ((TERM_data_ptr=fgets(Data_Line, 512, TFP)) != NULL))
+ {
+ if ((*TERM_data_ptr != ' ') && (*TERM_data_ptr != '\t') && (*TERM_data_ptr != '#'))
+ {
+ while ((!CFOUND) && (*TERM_data_ptr != (char)NULL))
+ {
+ CFOUND = !strncmp(TERM_data_ptr, Name, Data_Line_len);
+ while ((*TERM_data_ptr != (char)NULL) && (*TERM_data_ptr != '|') && (*TERM_data_ptr != '#') && (*TERM_data_ptr != ':'))
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == '|')
+ TERM_data_ptr++;
+ else if (!CFOUND)
+ *TERM_data_ptr = (char)NULL;
+ }
+ }
+ }
+ if (!CFOUND)
+ {
+ printf("terminal type %s not found\n", TERMINAL_TYPE);
+ exit(0);
+ }
+}
+
+void
+CAP_PARSE() /* parse off the data in the termcap data file */
+{
+ int offset;
+ int found;
+
+ do
+ {
+ while (*TERM_data_ptr != (char)NULL)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < 26); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, Boolean_names[offset], 2))
+ {
+ found = TRUE;
+ Booleans[offset] = TRUE;
+ }
+ }
+ if (!found)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < lw__); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, Number_names[offset], 3))
+ {
+ found = TRUE;
+ Numbers[offset] = tc_Get_int(Numbers[offset]);
+ }
+ }
+ }
+ if (!found)
+ {
+ for (found = FALSE, offset = 0; (!found) && (offset < smgr__); offset++)
+ {
+ if (!strncmp(TERM_data_ptr, String_names[offset], 3))
+ {
+ found = TRUE;
+ String_table[offset] = String_Get(String_table[offset]);
+ }
+ }
+ }
+
+ if (!strncmp(TERM_data_ptr, "tc=", 3))
+ tc_ = String_Get(NULL);
+ while ((*TERM_data_ptr != ':') && (*TERM_data_ptr != (char)NULL))
+ TERM_data_ptr++;
+ if (*TERM_data_ptr == ':')
+ TERM_data_ptr++;
+ }
+ } while (((TERM_data_ptr = fgets(Data_Line, 512, TFP)) != NULL) && ((*TERM_data_ptr == ' ') || (*TERM_data_ptr == '\t')));
+ if (tc_ != NULL)
+ {
+ TERMINAL_TYPE = tc_;
+ rewind(TFP);
+ Find_term();
+ free(tc_);
+ tc_ = NULL;
+ CAP_PARSE();
+ }
+ else
+ fclose(TFP);
+}
+#endif /* ifdef CAP */
+
+struct _line *
+Screenalloc(columns)
+int columns;
+{
+ int i;
+ struct _line *tmp;
+
+ tmp = (struct _line *) malloc(sizeof (struct _line));
+ tmp->row = malloc(columns + 1);
+ tmp->attributes = malloc(columns + 1);
+ tmp->prev_screen = NULL;
+ tmp->next_screen = NULL;
+ for (i = 0; i < columns; i++)
+ {
+ tmp->row[i] = ' ';
+ tmp->attributes[i] = (char) NULL;
+ }
+ tmp->scroll = tmp->changed = FALSE;
+ tmp->row[0] = (char) NULL;
+ tmp->attributes[0] = (char) NULL;
+ tmp->row[columns] = (char) NULL;
+ tmp->attributes[columns] = (char) NULL;
+ tmp->last_char = 0;
+ return(tmp);
+}
+
+WINDOW *newwin(lines, cols, start_l, start_c)
+int lines, cols; /* number of lines and columns to be in window */
+int start_l, start_c; /* starting line and column to be inwindow */
+{
+ WINDOW *Ntemp;
+ struct _line *temp_screen;
+ int i;
+
+ Ntemp = (WINDOW *) malloc(sizeof(WINDOW));
+ Ntemp->SR = start_l;
+ Ntemp->SC = start_c;
+ Ntemp->Num_lines = lines;
+ Ntemp->Num_cols = cols;
+ Ntemp->LX = 0;
+ Ntemp->LY = 0;
+ Ntemp->scroll_down = Ntemp->scroll_up = 0;
+ Ntemp->SCROLL_CLEAR = FALSE;
+ Ntemp->Attrib = FALSE;
+ Ntemp->first_line = temp_screen = Screenalloc(cols);
+ Ntemp->first_line->number = 0;
+ for (i = 1; i < lines; i++)
+ {
+ temp_screen->next_screen = Screenalloc(cols);
+ temp_screen->next_screen->number = i;
+ temp_screen->next_screen->prev_screen = temp_screen;
+ temp_screen = temp_screen->next_screen;
+ }
+ Ntemp->first_line->prev_screen = NULL;
+ temp_screen->next_screen = NULL;
+ return(Ntemp);
+}
+
+#ifdef CAP
+void
+Cap_Out(string, p_list, place) /* interpret the output string if necessary */
+char *string;
+int p_list[]; /* stack of values */
+int place; /* place keeper of top of stack */
+{
+ char *Otemp; /* temporary string pointer to parse output */
+ int delay;
+ int p1, p2, temp;
+ float chars;
+
+ if (string == NULL)
+ return;
+
+ if (p_list != NULL)
+ {
+ p1 = p_list[--place];
+ p2 = p_list[--place];
+ }
+ delay = 0;
+ Otemp = string;
+ if ((*Otemp >= '0') && (*Otemp <= '9'))
+ {
+ delay = atoi(Otemp);
+ while ((*Otemp >= '0') && (*Otemp <= '9'))
+ Otemp++;
+ if (*Otemp == '*')
+ Otemp++;
+ }
+ while (*Otemp != (char)NULL)
+ {
+ if (*Otemp == '%')
+ {
+ Otemp++;
+ if ((*Otemp == 'd') || (*Otemp == '2') || (*Otemp == '3') || (*Otemp == '.') || (*Otemp == '+'))
+ {
+ if (*Otemp == 'd')
+ printf("%d", p1);
+ else if (*Otemp == '2')
+ printf("%02d", p1);
+ else if (*Otemp == '3')
+ printf("%03d", p1);
+ else if (*Otemp == '+')
+ {
+ Otemp++;
+ p1 += *Otemp;
+ putchar(p1);
+ }
+ else if (*Otemp == '.')
+ putchar(p1);
+ p1 = p2;
+ p2 = 0;
+ }
+ else if (*Otemp == '>')
+ {
+ Otemp++;
+ if (p1 > *Otemp)
+ {
+ Otemp++;
+ p1 += *Otemp;
+ }
+ else
+ Otemp++;
+ }
+ else if (*Otemp == 'r')
+ {
+ temp = p1;
+ p1 = p2;
+ p2 = temp;
+ }
+ else if (*Otemp == 'i')
+ {
+ p1++;
+ p2++;
+ }
+ else if (*Otemp == '%')
+ putchar(*Otemp);
+ else if (*Otemp == 'n')
+ {
+ p1 ^= 0140;
+ p2 ^= 0140;
+ }
+ else if (*Otemp == 'B')
+ {
+ p1 = (16 * (p1/10)) + (p1 % 10);
+ p2 = (16 * (p2/10)) + (p2 % 10);
+ }
+ else if (*Otemp == 'D')
+ {
+ p1 = (p1 - 2 * (p1 % 16));
+ p2 = (p2 - 2 * (p2 % 16));
+ }
+ }
+ else
+ putchar (*Otemp);
+ Otemp++;
+ }
+ if (delay != 0)
+ {
+ chars = delay * chars_per_millisecond;
+ delay = chars;
+ if ((chars - delay) > 0.0)
+ delay++;
+ for (; delay > 0; delay--)
+ putchar(*String_table[pc__]);
+ }
+ fflush(stdout);
+}
+
+#else
+
+ char *Otemp; /* temporary string pointer to parse output */
+ float chars;
+ int p[10];
+ int variable[27];
+
+int
+Operation(Temp_Stack, place) /* handle conditional operations */
+int Temp_Stack[];
+int place;
+{
+ int temp;
+
+ if (*Otemp == 'd')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ printf("%d", temp);
+ }
+ else if (!strncmp(Otemp, "2d", 2))
+ {
+ temp = Temp_Stack[--place];
+ printf("%2d", temp);
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "3d", 2))
+ {
+ temp = Temp_Stack[--place];
+ printf("%0d", temp);
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "02d", 3))
+ {
+ temp = Temp_Stack[--place];
+ printf("%02d", temp);
+ Otemp++;
+ Otemp++;
+ Otemp++;
+ }
+ else if (!strncmp(Otemp, "03d", 3))
+ {
+ temp = Temp_Stack[--place];
+ printf("%03d", temp);
+ Otemp++;
+ Otemp++;
+ Otemp++;
+ }
+ else if (*Otemp == '+')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp += Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '-')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp -= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '*')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp *= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '/')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp /= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == 'm')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp %= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '&')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp &= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '|')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp |= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '^')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp ^= Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '=')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = (temp == Temp_Stack[--place]);
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '>')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = temp > Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == '<')
+ {
+ Otemp++;
+ temp = Temp_Stack[--place];
+ temp = temp < Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ }
+ else if (*Otemp == 'c')
+ {
+ Otemp++;
+ putchar(Temp_Stack[--place]);
+ }
+ else if (*Otemp == 'i')
+ {
+ Otemp++;
+ p[1]++;
+ p[2]++;
+ }
+ else if (*Otemp == '%')
+ {
+ putchar(*Otemp);
+ Otemp++;
+ }
+ else if (*Otemp == '!')
+ {
+ temp = ! Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ Otemp++;
+ }
+ else if (*Otemp == '~')
+ {
+ temp = ~Temp_Stack[--place];
+ Temp_Stack[place++] = temp;
+ Otemp++;
+ }
+ else if (*Otemp == 'p')
+ {
+ Otemp++;
+ Temp_Stack[place++] = p[*Otemp - '0'];
+ Otemp++;
+ }
+ else if (*Otemp == 'P')
+ {
+ Otemp++;
+ Temp_Stack[place++] = variable[*Otemp - 'a'];
+ Otemp++;
+ }
+ else if (*Otemp == 'g')
+ {
+ Otemp++;
+ variable[*Otemp - 'a'] = Temp_Stack[--place];
+ Otemp++;
+ }
+ else if (*Otemp == '\'')
+ {
+ Otemp++;
+ Temp_Stack[place++] = *Otemp;
+ Otemp++;
+ Otemp++;
+ }
+ else if (*Otemp == '{')
+ {
+ Otemp++;
+ temp = atoi(Otemp);
+ Temp_Stack[place++] = temp;
+ while (*Otemp != '}')
+ Otemp++;
+ Otemp++;
+ }
+ return(place);
+}
+
+void
+Info_Out(string, p_list, place) /* interpret the output string if necessary */
+char *string;
+int p_list[];
+int place;
+{
+ char *tchar;
+ int delay;
+ int temp;
+ int Cond_FLAG;
+ int EVAL;
+ int Cond_Stack[128];
+ int Cond_place;
+ int Stack[128];
+ int Top_of_stack;
+
+ if (string == NULL)
+ return;
+
+ Cond_FLAG = FALSE;
+ Cond_place = 0;
+ Top_of_stack = 0;
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0;
+ p[4] = 0;
+ p[5] = 0;
+ p[6] = 0;
+ p[7] = 0;
+ p[8] = 0;
+ p[9] = 0;
+ if (p_list != NULL)
+ {
+ for (temp = 1; (place != 0); temp++)
+ {
+ p[temp] = p_list[--place];
+ }
+ }
+ delay = 0;
+ Otemp = string;
+ while (*Otemp != (char) NULL)
+ {
+ if (*Otemp == '%')
+ {
+ Otemp++;
+ if ((*Otemp == '?') || (*Otemp == 't') || (*Otemp == 'e') || (*Otemp == ';'))
+ {
+ if (*Otemp == '?')
+ {
+ Otemp++;
+ Cond_FLAG = TRUE;
+ EVAL = TRUE;
+ while (EVAL)
+ {
+ /*
+ | find the end of the
+ | conditional statement
+ */
+ while ((strncmp(Otemp, "%t", 2)) && (*Otemp != (char) NULL))
+ {
+ /*
+ | move past '%'
+ */
+ Otemp++;
+ Cond_place = Operation(Cond_Stack, Cond_place);
+ }
+
+ /*
+ | if condition is true
+ */
+ if ((Cond_place > 0) && (Cond_Stack[Cond_place-1]))
+ {
+ /*
+ | end conditional
+ | parsing
+ */
+ EVAL = FALSE;
+ Otemp++;
+ Otemp++;
+ }
+ else /* condition is false */
+ {
+ /*
+ | find 'else' or end
+ | of if statement
+ */
+ while ((strncmp(Otemp, "%e", 2)) && (strncmp(Otemp, "%;", 2)) && (*Otemp != (char) NULL))
+ Otemp++;
+ /*
+ | if an 'else' found
+ */
+ if ((*Otemp != (char) NULL) && (!strncmp(Otemp, "%e", 2)))
+ {
+ Otemp++;
+ Otemp++;
+ tchar = Otemp;
+ /*
+ | check for 'then' part
+ */
+ while ((*tchar != (char) NULL) && (strncmp(tchar, "%t", 2)) && (strncmp(tchar, "%;", 2)))
+ tchar++;
+ /*
+ | if end of string
+ */
+ if (*tchar == (char) NULL)
+ {
+ EVAL = FALSE;
+ Cond_FLAG = FALSE;
+ Otemp = tchar;
+ }
+ /*
+ | if end of if found,
+ | set up to parse
+ | info
+ */
+ else if (!strncmp(tchar, "%;", 2))
+ EVAL = FALSE;
+ /*
+ | otherwise, check
+ | conditional in
+ | 'else'
+ */
+ }
+ /*
+ | if end of if found,
+ | get out of if
+ | statement
+ */
+ else if ((*Otemp != (char) NULL) && (!strncmp(Otemp, "%;", 2)))
+ {
+ EVAL = FALSE;
+ Otemp++;
+ Otemp++;
+ }
+ else /* Otemp == NULL */
+ {
+ EVAL = FALSE;
+ Cond_FLAG = FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ Otemp++;
+ Cond_FLAG = FALSE;
+ if (*Otemp != ';')
+ {
+ while ((*Otemp != (char) NULL) && (strncmp(Otemp, "%;", 2)))
+ Otemp++;
+ if (*Otemp != (char) NULL)
+ {
+ Otemp++;
+ Otemp++;
+ }
+ }
+ else
+ Otemp++;
+ }
+ }
+ else
+ {
+ Top_of_stack = Operation(Stack, Top_of_stack);
+ }
+ }
+ else if (!strncmp(Otemp, "$<", 2))
+ {
+ Otemp++;
+ Otemp++;
+ delay = atoi(Otemp);
+ while (*Otemp != '>')
+ Otemp++;
+ Otemp++;
+ chars = delay * chars_per_millisecond;
+ delay = chars;
+ if ((chars - delay) > 0.0)
+ delay++;
+ if (String_table[pc__] == NULL)
+ temp = 0;
+ else
+ temp = *String_table[pc__];
+ for (; delay > 0; delay--)
+ putc(temp, stdout);
+ }
+ else
+ {
+ putchar(*Otemp);
+ Otemp++;
+ }
+ }
+ fflush(stdout);
+}
+#endif
+
+void
+wmove(window, row, column) /* move cursor to indicated position in window */
+WINDOW *window;
+int row, column;
+{
+ if ((row < window->Num_lines) && (column < window->Num_cols))
+ {
+ window->LX = column;
+ window->LY = row;
+ }
+}
+
+void
+clear_line(line, column, cols)
+struct _line *line;
+int column;
+int cols;
+{
+ int j;
+
+ if (column > line->last_char)
+ line->row[line->last_char] = ' ';
+ line->last_char = column;
+ line->row[column] = (char) NULL;
+ line->attributes[column] = (char) NULL;
+ line->changed = TRUE;
+ for (j = column + 1; j < cols; j++)
+ {
+ line->row[j] = ' ';
+ line->attributes[j] = (char) NULL;
+ }
+}
+
+void
+werase(window) /* clear the specified window */
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR = CLEAR;
+ window->scroll_up = window->scroll_down = 0;
+ for (i = 0, tmp = window->first_line; i < window->Num_lines; i++, tmp = tmp->next_screen)
+ clear_line(tmp, 0, window->Num_cols);
+}
+
+void
+wclrtoeol(window) /* erase from current cursor position to end of line */
+WINDOW *window;
+{
+ int column, row;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR = CHANGE;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ clear_line(tmp, column, window->Num_cols);
+}
+
+void
+wrefresh(window) /* flush all previous output */
+WINDOW *window;
+{
+ wnoutrefresh(window);
+#ifdef DIAG
+{
+ struct _line *temp;
+ int value;
+ fprintf(stderr, "columns=%d, lines=%d, SC=%d, SR=%d\n",window->Num_cols, window->Num_lines, window->SC, window->SR);
+ for (value = 0, temp = window->first_line; value < window->Num_lines; value++, temp = temp->next_screen)
+ {
+ if (temp->number == -1)
+ fprintf(stderr, "line moved ");
+ if (temp->scroll)
+ fprintf(stderr, "scroll_x is set: ");
+ fprintf(stderr, "lc%d=%s|\n", temp->last_char, temp->row);
+ }
+ fprintf(stderr, "+-------------------- virtual screen ----------------------------------------+\n");
+ fprintf(stderr, "columns=%d, lines=%d \n",virtual_scr->Num_cols, virtual_scr->Num_lines);
+ for (value = 0, temp = virtual_scr->first_line; value < virtual_scr->Num_lines; value++, temp = temp->next_screen)
+ {
+ if (temp->number == -1)
+ fprintf(stderr, "line moved ");
+ if (temp->scroll)
+ fprintf(stderr, "scroll_x is set: ");
+ fprintf(stderr, "lc%d=%s|\n", temp->last_char, temp->row);
+ }
+ fprintf(stderr, "columns=%d, lines=%d \n",curscr->Num_cols, curscr->Num_lines);
+ for (value = 0, temp = curscr->first_line; value < curscr->Num_lines; value++, temp = temp->next_screen)
+ fprintf(stderr, "line=%s|\n", temp->row);
+}
+#endif
+ doupdate();
+ virtual_scr->SCROLL_CLEAR = FALSE;
+ virtual_scr->scroll_down = virtual_scr->scroll_up = 0;
+ fflush(stdout);
+}
+
+void
+touchwin(window)
+WINDOW *window;
+{
+ struct _line *user_line;
+ int line_counter = 0;
+
+ for (line_counter = 0, user_line = window->first_line;
+ line_counter < window->Num_lines; line_counter++)
+ {
+ user_line->changed = TRUE;
+ }
+ window->SCROLL_CLEAR = TRUE;
+}
+
+void
+wnoutrefresh(window)
+WINDOW *window;
+{
+ struct _line *user_line;
+ struct _line *virtual_line;
+ int line_counter = 0;
+ int user_col = 0;
+ int virt_col = 0;
+
+ if (window->SR >= virtual_scr->Num_lines)
+ return;
+ user_line = window->first_line;
+ virtual_line = virtual_scr->first_line;
+ virtual_scr->SCROLL_CLEAR = window->SCROLL_CLEAR;
+ virtual_scr->LX = window->LX + window->SC;
+ virtual_scr->LY = window->LY + window->SR;
+ virtual_scr->scroll_up = window->scroll_up;
+ virtual_scr->scroll_down = window->scroll_down;
+ if ((last_window_refreshed == window) && (!window->SCROLL_CLEAR))
+ return;
+ for (line_counter = 0; line_counter < window->SR; line_counter++)
+ {
+ virtual_line = virtual_line->next_screen;
+ }
+ for (line_counter = 0; (line_counter < window->Num_lines)
+ && ((line_counter + window->SR) < virtual_scr->Num_lines);
+ line_counter++)
+ {
+ if ((last_window_refreshed != window) || (user_line->changed) || ((SCROLL | CLEAR) & window->SCROLL_CLEAR))
+ {
+ for (user_col = 0, virt_col = window->SC;
+ (virt_col < virtual_scr->Num_cols)
+ && (user_col < window->Num_cols);
+ virt_col++, user_col++)
+ {
+ virtual_line->row[virt_col] = user_line->row[user_col];
+ virtual_line->attributes[virt_col] = user_line->attributes[user_col];
+ }
+ }
+ if (virtual_scr->Num_cols != window->Num_cols)
+ {
+ if (virtual_line->last_char < (user_line->last_char + window->SC))
+ {
+ if (virtual_line->row[virtual_line->last_char] == (char) NULL)
+ virtual_line->row[virtual_line->last_char] = ' ';
+ virtual_line->last_char =
+ min(virtual_scr->Num_cols,
+ (user_line->last_char + window->SC));
+ }
+ else if (virtual_line->last_char > (user_line->last_char + window->SC))
+ {
+ virtual_line->row[min(virtual_scr->Num_cols,
+ (user_line->last_char + window->SC))] = ' ';
+ }
+ }
+ else
+ virtual_line->last_char = user_line->last_char;
+ virtual_line->row[virtual_line->last_char] = (char) NULL;
+ virtual_line->changed = user_line->changed;
+ virtual_line = virtual_line->next_screen;
+ user_line = user_line->next_screen;
+ }
+ window->SCROLL_CLEAR = FALSE;
+ window->scroll_up = window->scroll_down = 0;
+ last_window_refreshed = window;
+}
+
+void
+flushinp() /* flush input */
+{
+}
+
+void
+ungetch(c) /* push a character back on input */
+int c;
+{
+ if (bufp < 100)
+ in_buff[bufp++] = c;
+}
+
+#ifdef BSD_SELECT
+int
+timed_getchar()
+{
+ struct timeval tv;
+ fd_set fds;
+ int ret_val;
+ int nfds = 1;
+ char temp;
+
+ FD_ZERO(&fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000; /* half a second */
+ FD_SET(0, &fds);
+ Time_Out = FALSE; /* just in case */
+
+ ret_val = select(nfds, &fds, 0, 0, &tv);
+
+ /*
+ | if ret_val is less than zero, there was no input
+ | otherwise, get a character and return it
+ */
+
+ if (ret_val <= 0)
+ {
+ Time_Out = TRUE;
+ return(-1);
+ }
+
+ return(read(0, &temp, 1)? temp : -1);
+}
+#endif
+
+int
+wgetch(window) /* get character from specified window */
+WINDOW *window;
+{
+ int in_value;
+ char temp;
+#ifndef SYS5
+ int old_arg;
+#endif /* SYS5 */
+
+#ifdef BSD_SELECT
+ if (Noblock)
+ in_value = ((bufp > 0) ? in_buff[--bufp] : timed_getchar());
+ else
+ in_value = ((bufp > 0) ? in_buff[--bufp] : read(0, &temp, 1)? temp : -1);
+#else /* BSD_SELECT */
+#ifdef SYS5
+ in_value = ((bufp > 0) ? in_buff[--bufp] :
+ (read(0, &temp, 1)> 0) ? temp : -1);
+#else /* SYS5 */
+ if (Noblock)
+ {
+ Time_Out = FALSE;
+ old_arg = fcntl(0, F_GETFL, 0);
+ in_value = fcntl(0, F_SETFL, old_arg | FNDELAY);
+ }
+ in_value = ((bufp > 0) ? in_buff[--bufp] : read(0, &temp, 1)? temp : -1);
+ if (Noblock)
+ {
+ fcntl(0, F_SETFL, old_arg);
+ if (Time_Out)
+ in_value = -1;
+ }
+#endif /* SYS5 */
+#endif /* BSD_SELECT */
+
+ if (in_value != -1)
+ {
+ in_value &= 0xff;
+ if ((Parity) && (Num_bits < 8))
+ /* strip eighth bit if parity in use */
+ in_value &= 0177;
+ }
+ else if (interrupt_flag)
+ {
+ interrupt_flag = FALSE;
+ in_value = wgetch(window);
+ }
+
+ if ((in_value == '\033') || (in_value == '\037'))/* escape character */
+ in_value = Get_key(in_value);
+ return(in_value);
+}
+
+#ifndef BSD_SELECT
+void
+Clear(arg) /* notify that time out has occurred */
+int arg;
+{
+ Time_Out = TRUE;
+#ifdef DEBUG
+fprintf(stderr, "inside Clear()\n");
+fflush(stderr);
+#endif /* DEBUG */
+}
+#endif /* BSD_SELECT */
+
+int
+Get_key(first_char) /* try to decode key sequence */
+int first_char; /* first character of sequence */
+{
+ int in_char;
+ int Count;
+ char string[128];
+ char *Gtemp;
+ int Found;
+#ifdef SYS5
+ struct termio Gterminal;
+#else
+ struct sgttyb Gterminal;
+#endif
+ struct KEY_STACK *St_point;
+#if (!defined( BSD_SELECT)) || (!defined(SYS5))
+ int value;
+#endif /* BSD_SELECT */
+
+ Count = 0;
+ Gtemp = string;
+ string[Count++] = first_char;
+ string[Count] = (char) NULL;
+ Time_Out = FALSE;
+#ifndef BSD_SELECT
+ signal(SIGALRM, Clear);
+ value = alarm(1);
+#endif /* BSD_SELECT */
+ Noblock = TRUE;
+#ifdef SYS5
+ Gterminal.c_cc[VTIME] = 0; /* timeout value */
+ Gterminal.c_lflag &= ~ICANON; /* disable canonical operation */
+ Gterminal.c_lflag &= ~ECHO; /* disable echo */
+#endif
+ Count = 1;
+ Found = FALSE;
+ while ((Count < Max_Key_len) && (!Time_Out) && (!Found))
+ {
+ in_char = wgetch(stdscr);
+#ifdef DEBUG
+fprintf(stderr, "back in GetKey()\n");
+fflush(stderr);
+#endif /* DEBUG */
+ if (in_char != -1)
+ {
+ string[Count++] = in_char;
+ string[Count] = (char) NULL;
+ St_point = KEY_TOS;
+ while ((St_point != NULL) && (!Found))
+ {
+ if (!strcmp(string, St_point->element->string))
+ Found = TRUE;
+ else
+ St_point = St_point->next;
+ }
+ }
+ }
+#ifndef BSD_SELECT
+ if (!Time_Out)
+ value = alarm(0);
+#endif /* BSD_SELECT */
+#ifdef SYS5
+/* value = ioctl(0, TCSETA, &Terminal);*/
+#else
+ value = ioctl(0, TIOCSETP, &Terminal);
+/* value = fcntl(0, F_SETFL, old_arg);*/
+#endif
+ Noblock = FALSE;
+ if (Found)
+ {
+ return(St_point->element->value);
+ }
+ else
+ {
+ while (Count > 1)
+ {
+ if ((string[--Count] != -1) &&
+ ((unsigned char) (string[Count]) != 255))
+ {
+#ifdef DIAG
+fprintf(stderr, "ungetting character %d\n", string[Count]);fflush(stdout);
+#endif
+ ungetch(string[Count]);
+ }
+ }
+ return(first_char);
+ }
+}
+
+void
+waddch(window, c) /* output the character in the specified window */
+WINDOW *window;
+int c;
+{
+ int row, column;
+ int shift; /* number of spaces to shift if a tab */
+ struct _line *tmpline;
+
+#ifdef DIAG
+/*printf("starting waddch \n");fflush(stdout);*/
+#endif
+ row = window->LY;
+ column = window->LX;
+ if (c == '\t')
+ {
+ shift = (column + 1) % 8;
+ if (shift == 0)
+ shift++;
+ else
+ shift = 9 - shift;
+ while (shift > 0)
+ {
+ shift--;
+ waddch(window, ' ');
+ }
+ }
+ else if ((column < window->Num_cols) && (row < window->Num_lines))
+ {
+ if ((c == '~') && (Booleans[hz__]))
+ c = '@';
+
+ if (( c != '\b') && (c != '\n') && (c != '\r'))
+ {
+ row = 0;
+ tmpline = window->first_line;
+ while (row < window->LY)
+ {
+ row++;
+ tmpline = tmpline->next_screen;
+ }
+ tmpline->row[column] = c;
+ tmpline->attributes[column] = window->Attrib;
+ tmpline->changed = TRUE;
+ if (column >= tmpline->last_char)
+ {
+ if (column > tmpline->last_char)
+ tmpline->row[tmpline->last_char] = ' ';
+ tmpline->row[column + 1] = (char) NULL;
+ tmpline->attributes[column + 1] = (char) NULL;
+ tmpline->last_char = column + 1;
+ }
+ }
+ if (c == '\n')
+ {
+ wclrtoeol(window);
+ window->LX = window->Num_cols;
+ }
+ else if (c == '\r')
+ window->LX = 0;
+ else if (c == '\b')
+ window->LX--;
+ else
+ window->LX++;
+ }
+ if (window->LX >= window->Num_cols)
+ {
+ window->LX = 0;
+ window->LY++;
+ if (window->LY >= window->Num_lines)
+ {
+ window->LY = window->Num_lines - 1;
+/* window->LY = row;
+ wmove(window, 0, 0);
+ wdeleteln(window);
+ wmove(window, row, 0);*/
+ }
+ }
+ window->SCROLL_CLEAR = CHANGE;
+}
+
+void
+winsertln(window) /* insert a blank line into the specified window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+ struct _line *tmp1;
+
+ window->scroll_down += 1;
+ window->SCROLL_CLEAR = SCROLL;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; (row < window->Num_lines) && (tmp->next_screen != NULL); row++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = NULL;
+ tmp1 = tmp;
+ clear_line(tmp1, 0, window->Num_cols);
+ tmp1->number = -1;
+ for (row = 0, tmp = window->first_line; (row < window->LY) && (tmp->next_screen != NULL); row++)
+ tmp = tmp->next_screen;
+ if ((window->LY == (window->Num_lines - 1)) && (window->Num_lines > 1))
+ {
+ tmp1->next_screen = tmp->next_screen;
+ tmp->next_screen = tmp1;
+ tmp->changed = TRUE;
+ tmp->next_screen->prev_screen = tmp;
+ }
+ else if (window->Num_lines > 1)
+ {
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp1;
+ tmp1->prev_screen = tmp->prev_screen;
+ tmp->prev_screen = tmp1;
+ tmp1->next_screen = tmp;
+ tmp->changed = TRUE;
+ tmp->scroll = DOWN;
+ }
+ if (window->LY == 0)
+ window->first_line = tmp1;
+}
+
+void
+wdeleteln(window) /* delete a line in the specified window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+ struct _line *tmpline;
+
+ if (window->Num_lines > 1)
+ {
+ window->scroll_up += 1;
+ window->SCROLL_CLEAR = SCROLL;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ if (window->LY == 0)
+ window->first_line = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ {
+ tmp->next_screen->changed = TRUE;
+ tmp->next_screen->scroll = UP;
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ }
+ tmpline = tmp;
+ clear_line(tmpline, 0, window->Num_cols);
+ tmpline->number = -1;
+ for (row = 0, tmp = window->first_line; tmp->next_screen != NULL; row++)
+ tmp = tmp->next_screen;
+ if (tmp != NULL)
+ {
+ tmp->next_screen = tmpline;
+ tmp->next_screen->prev_screen = tmp;
+ tmp->changed = TRUE;
+ tmp = tmp->next_screen;
+ }
+ else
+ tmp = tmpline;
+ tmp->next_screen = NULL;
+ }
+ else
+ {
+ clear_line(window->first_line, 0, window->Num_cols);
+ }
+}
+
+void
+wclrtobot(window) /* delete from current position to end of the window */
+WINDOW *window;
+{
+ int row, column;
+ struct _line *tmp;
+
+ window->SCROLL_CLEAR |= CLEAR;
+ column = window->LX;
+ row = window->LY;
+ for (row = 0, tmp = window->first_line; row < window->LY; row++)
+ tmp = tmp->next_screen;
+ clear_line(tmp, column, window->Num_cols);
+ for (row = (window->LY + 1); row < window->Num_lines; row++)
+ {
+ tmp = tmp->next_screen;
+ clear_line(tmp, 0, window->Num_cols);
+ }
+ wmove(window, row, column);
+}
+
+void
+wstandout(window) /* begin standout mode in window */
+WINDOW *window;
+{
+ if (Numbers[sg__] < 1) /* if not magic cookie glitch */
+ window->Attrib |= A_STANDOUT;
+}
+
+void
+wstandend(window) /* end standout mode in window */
+WINDOW *window;
+{
+ window->Attrib &= ~A_STANDOUT;
+}
+
+void
+waddstr(window, string) /* write 'string' in window */
+WINDOW *window;
+char *string;
+{
+ char *wstring;
+
+ for (wstring = string; *wstring != (char) NULL; wstring++)
+ waddch(window, *wstring);
+}
+
+void
+clearok(window, flag) /* erase screen and redraw at next refresh */
+WINDOW *window;
+int flag;
+{
+ Repaint_screen = TRUE;
+}
+
+void
+echo() /* turn on echoing */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag |= ECHO; /* enable echo */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags |= ECHO; /* enable echo */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+noecho() /* turn off echoing */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag &= ~ECHO; /* disable echo */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags &= ~ECHO; /* disable echo */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+raw() /* set to read characters immediately */
+{
+ int value;
+
+#ifdef SYS5
+ Intr = Terminal.c_cc[VINTR]; /* get the interrupt character */
+ Terminal.c_lflag &= ~ICANON; /* disable canonical operation */
+ Terminal.c_lflag &= ~ISIG; /* disable signal checking */
+#ifdef FLUSHO
+ Terminal.c_lflag &= ~FLUSHO;
+#endif
+#ifdef PENDIN
+ Terminal.c_lflag &= ~PENDIN;
+#endif
+#ifdef IEXTEN
+ Terminal.c_lflag &= ~IEXTEN;
+#endif
+ Terminal.c_cc[VMIN] = 1; /* minimum of one character */
+ Terminal.c_cc[VTIME] = 255; /* timeout value */
+ Terminal.c_cc[VINTR] = 0; /* eliminate interrupt */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags |= RAW; /* enable raw mode */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+noraw() /* set to normal character read mode */
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_lflag |= ICANON; /* enable canonical operation */
+ Terminal.c_lflag |= ISIG; /* enable signal checking */
+ Terminal.c_cc[VEOF] = 4; /* EOF character = 4 */
+ Terminal.c_cc[VEOL] = (char) NULL; /* EOL = 0 */
+ Terminal.c_cc[VINTR] = Intr; /* reset interrupt char */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#else
+ Terminal.sg_flags &= ~RAW; /* disable raw mode */
+ value = ioctl(0, TIOCSETP, &Terminal); /* set characteristics */
+/* old_arg = fcntl(0, F_GETFL, 0);
+ value = fcntl(0, F_SETFL, old_arg & ~FNDELAY);*/
+#endif
+}
+
+void
+nl()
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_iflag |= ICRNL; /* enable carriage-return to line-feed mapping */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+nonl()
+{
+ int value;
+
+#ifdef SYS5
+ Terminal.c_iflag &= ~ICRNL; /* disable carriage-return to line-feed mapping */
+ Terminal.c_iflag &= ~IGNCR; /* do not ignore carriage-return */
+ value = ioctl(0, TCSETA, &Terminal); /* set characteristics */
+#endif
+}
+
+void
+saveterm()
+{
+}
+
+void
+fixterm()
+{
+}
+
+void
+resetterm()
+{
+}
+
+void
+nodelay(window, flag)
+WINDOW *window;
+int flag;
+{
+}
+
+void
+idlok(window, flag)
+WINDOW *window;
+int flag;
+{
+}
+
+void
+keypad(window, flag)
+WINDOW *window;
+int flag;
+{
+ if (flag)
+ String_Out(String_table[ks__], NULL, 0);
+ else
+ String_Out(String_table[ke__], NULL, 0);
+}
+
+void
+savetty() /* save current tty stats */
+{
+ int value;
+
+#ifdef SYS5
+ value = ioctl(0, TCGETA, &Saved_tty); /* set characteristics */
+#else
+ value = ioctl(0, TIOCGETP, &Saved_tty); /* set characteristics */
+#endif
+}
+
+void
+resetty() /* restore previous tty stats */
+{
+ int value;
+
+#ifdef SYS5
+ value = ioctl(0, TCSETA, &Saved_tty); /* set characteristics */
+#else
+ value = ioctl(0, TIOCSETP, &Saved_tty); /* set characteristics */
+#endif
+}
+
+void
+endwin() /* end windows */
+{
+ keypad(stdscr, FALSE);
+ free(stdscr);
+ initialized = FALSE;
+ delwin(curscr);
+ delwin(virtual_scr);
+ delwin(stdscr);
+#ifndef SYS5
+{
+ int old_arg, value;
+/* old_arg = fcntl(0, F_GETFL, 0);
+ value = fcntl(0, F_SETFL, old_arg & ~FNDELAY);*/
+}
+#endif
+}
+
+void
+delwin(window) /* delete the window structure */
+WINDOW *window;
+{
+ int i;
+
+ for (i = 1; (i < window->Num_lines) && (window->first_line->next_screen != NULL); i++)
+ {
+ window->first_line = window->first_line->next_screen;
+ free(window->first_line->prev_screen->row);
+ free(window->first_line->prev_screen->attributes);
+ free(window->first_line->prev_screen);
+ }
+ if (window == last_window_refreshed)
+ last_window_refreshed = 0;
+ if (window->first_line != NULL)
+ {
+ free(window->first_line->row);
+ free(window->first_line->attributes);
+ free(window->first_line);
+ free(window);
+ }
+}
+
+#ifndef __STDC__
+void
+wprintw(va_alist)
+va_dcl
+#else /* __STDC__ */
+void
+wprintw(WINDOW *window, const char *format, ...)
+#endif /* __STDC__ */
+{
+#ifndef __STDC__
+ WINDOW *window;
+ char *format;
+ va_list ap;
+#else
+ va_list ap;
+#endif
+ int value;
+ char *fpoint;
+ char *wtemp;
+
+#ifndef __STDC__
+ va_start(ap);
+ window = va_arg(ap, WINDOW *);
+ format = va_arg(ap, char *);
+#else /* __STDC__ */
+ va_start(ap, format);
+#endif /* __STDC__ */
+
+ fpoint = (char *) format;
+ while (*fpoint != (char) NULL)
+ {
+ if (*fpoint == '%')
+ {
+ fpoint++;
+ if (*fpoint == 'd')
+ {
+ value = va_arg(ap, int);
+ iout(window, value);
+ }
+ else if (*fpoint == 'c')
+ {
+ value = va_arg(ap, int);
+ waddch(window, value);
+ }
+ else if (*fpoint == 's')
+ {
+ wtemp = va_arg(ap, char *);
+ waddstr(window, wtemp);
+ }
+ fpoint++;
+ }
+ else if (*fpoint == '\\')
+ {
+ fpoint++;
+ if (*fpoint == 'n')
+ waddch(window, '\n');
+ else if ((*fpoint >= '0') && (*fpoint <= '9'))
+ {
+ value = 0;
+ while ((*fpoint >= '0') && (*fpoint <= '9'))
+ {
+ value = (value * 8) + (*fpoint - '0');
+ fpoint++;
+ }
+ waddch(window, value);
+ }
+ fpoint++;
+ }
+ else
+ waddch(window, *fpoint++);
+ }
+#ifdef __STDC__
+ va_end(ap);
+#endif /* __STDC__ */
+}
+
+void
+iout(window, value) /* output characters */
+WINDOW *window;
+int value;
+{
+ int i;
+
+ if ((i = value / 10) != 0)
+ iout(window, i);
+ waddch(window, ((value % 10) + '0'));
+}
+
+int
+Comp_line(line1, line2) /* compare lines */
+struct _line *line1;
+struct _line *line2;
+{
+ int count1, count2;
+ int i;
+ char *att1, *att2;
+ char *c1, *c2;
+
+ c1 = line1->row;
+ c2 = line2->row;
+ att1 = line1->attributes;
+ att2 = line2->attributes;
+ count2 = strlen(c1) + 1;
+ count1 = strlen(c2) + 1;
+ if (count1 > count2)
+ {
+ i = count2;
+ count2 = count1;
+ count1 = i;
+ }
+ if (count2 > (count1 + count1))
+ return(2);
+ i = 0;
+ while ((c1[i] != (char) NULL) && (c2[i] != (char) NULL) && (c1[i] == c2[i]) && (att1[i] == att2[i]))
+ i++;
+ count1 = i + 1;
+ if ((count1 == 1) && (count2 == 1))
+ count1 = 0; /* both lines blank */
+ else if (count2 == count1)
+ count1 = -1; /* equal */
+ else
+ count1 = count2 / count1; /* lines unequal */
+ return(count1);
+}
+
+struct _line *
+Insert_line(row, end_row, window) /* insert line into screen */
+int row;
+int end_row;
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+ struct _line *tmp1;
+
+ for (i = 0, tmp = curscr->first_line; i < window->SR; i++)
+ tmp = tmp->next_screen;
+ if ((end_row + window->SR) == 0)
+ curscr->first_line = curscr->first_line->next_screen;
+ top_of_win = tmp;
+ /*
+ | find bottom line to delete
+ */
+ for (i = 0, tmp = top_of_win; (tmp->next_screen != NULL) && (i < end_row); i++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ tmp1 = tmp;
+ /*
+ | clear deleted line
+ */
+ clear_line(tmp, 0, window->Num_cols);
+ tmp1->number = -1;
+ for (i = 0, tmp = curscr->first_line; (tmp->next_screen != NULL) && (i < window->SR); i++)
+ tmp = tmp->next_screen;
+ top_of_win = tmp;
+ for (i = 0, tmp = top_of_win; i < row; i++)
+ tmp = tmp->next_screen;
+ if ((tmp->prev_screen != NULL) && (window->Num_lines > 0))
+ tmp->prev_screen->next_screen = tmp1;
+ tmp1->prev_screen = tmp->prev_screen;
+ tmp->prev_screen = tmp1;
+ tmp1->next_screen = tmp;
+ if ((row + window->SR) == 0)
+ curscr->first_line = tmp1;
+ if (tmp1->next_screen != NULL)
+ tmp1 = tmp1->next_screen;
+
+ if ((!String_table[cs__]) && (end_row < window->Num_lines))
+ {
+ Position(window, (window->SR + end_row), 0);
+ String_Out(String_table[dl__], NULL, 0);
+ }
+ Position(window, (window->SR + row), 0);
+ if (String_table[al__] != NULL)
+ String_Out(String_table[al__], NULL, 0);
+ else
+ String_Out(String_table[sr__], NULL, 0);
+
+ for (i = 0, top_of_win = curscr->first_line; (top_of_win->next_screen != NULL) && (i < window->SR); i++)
+ top_of_win = top_of_win->next_screen;
+ return(tmp1);
+}
+
+
+struct _line *
+Delete_line(row, end_row, window) /* delete a line on screen */
+int row;
+int end_row;
+WINDOW *window;
+{
+ int i;
+ struct _line *tmp;
+ struct _line *tmp1;
+ struct _line *tmp2;
+
+ i = 0;
+ tmp = curscr->first_line;
+ while (i < window->SR)
+ {
+ i++;
+ tmp = tmp->next_screen;
+ }
+ /*
+ | find line to delete
+ */
+ top_of_win = tmp;
+ if ((row + window->SR) == 0)
+ curscr->first_line = top_of_win->next_screen;
+ for (i = 0, tmp = top_of_win; i < row; i++)
+ tmp = tmp->next_screen;
+ if (tmp->prev_screen != NULL)
+ tmp->prev_screen->next_screen = tmp->next_screen;
+ if (tmp->next_screen != NULL)
+ tmp->next_screen->prev_screen = tmp->prev_screen;
+ tmp2 = tmp->next_screen;
+ tmp1 = tmp;
+ /*
+ | clear deleted line
+ */
+ clear_line(tmp1, 0, window->Num_cols);
+ tmp1->number = -1;
+ /*
+ | find location to insert deleted line
+ */
+ for (i = 0, tmp = curscr->first_line; (tmp->next_screen != NULL) && (i < window->SR); i++)
+ tmp = tmp->next_screen;
+ top_of_win = tmp;
+ for (i = 0, tmp = top_of_win; (i < end_row) && (tmp->next_screen != NULL); i++)
+ tmp = tmp->next_screen;
+ tmp1->next_screen = tmp;
+ tmp1->prev_screen = tmp->prev_screen;
+ if (tmp1->prev_screen != NULL)
+ tmp1->prev_screen->next_screen = tmp1;
+ tmp->prev_screen = tmp1;
+
+ Position(window, (window->SR + row), 0);
+ String_Out(String_table[dl__], NULL, 0);
+ if ((!String_table[cs__]) && (end_row < window->Num_lines))
+ {
+ Position(window, (window->SR + end_row), 0);
+ String_Out(String_table[al__], NULL, 0);
+ }
+ else if ((String_table[cs__] != NULL) && (String_table[dl__] == NULL))
+ {
+ Position(window, (window->SR + end_row), 0);
+ putchar('\n');
+ }
+
+ if (row == (window->Num_lines-1))
+ tmp2 = tmp1;
+ if ((row + window->SR) == 0)
+ curscr->first_line = top_of_win = tmp2;
+ return(tmp2);
+}
+
+void
+CLEAR_TO_EOL(window, row, column)
+WINDOW *window;
+int row, column;
+{
+ int x, y;
+ struct _line *tmp1;
+
+ for (y = 0, tmp1 = curscr->first_line; (y < (window->SR+row)) && (tmp1->next_screen != NULL); y++)
+ tmp1 = tmp1->next_screen;
+ for (x = column; x<window->Num_cols; x++)
+ {
+ tmp1->row[x] = ' ';
+ tmp1->attributes[x] = (char) NULL;
+ }
+ tmp1->row[column] = (char) NULL;
+ tmp1->last_char = column;
+ if (column < COLS)
+ {
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(window, row, column);
+ attribute_off();
+ }
+ if (String_table[ce__] != NULL)
+ String_Out(String_table[ce__], NULL, 0);
+ else
+ {
+ for (x = column; x < window->Num_cols; x++)
+ putchar(' ');
+ Curr_x = x;
+ }
+ }
+}
+
+int
+check_delete(window, line, offset, pointer_new, pointer_old)
+WINDOW *window;
+int line, offset;
+struct _line *pointer_new, *pointer_old;
+{
+ int end_old;
+ int end_new;
+ int k;
+ int changed;
+ char *old_lin;
+ char *new_lin;
+ char *old_att;
+ char *new_att;
+
+ changed = FALSE;
+ new_lin = pointer_new->row;
+ new_att = pointer_new->attributes;
+ old_lin = pointer_old->row;
+ old_att = pointer_old->attributes;
+ end_old = end_new = offset;
+ while (((new_lin[end_new] != old_lin[end_old]) || (new_att[end_new] != old_att[end_old])) && (old_lin[end_old] != (char) NULL) && (new_lin[end_old] != (char) NULL))
+ end_old++;
+ if (old_lin[end_old] != (char) NULL)
+ {
+ k = 0;
+ while ((old_lin[end_old+k] == new_lin[end_new+k]) && (new_att[end_new+k] == old_att[end_old+k]) && (new_lin[end_new+k] != (char) NULL) && (old_lin[end_old+k] != (char) NULL) && (k < 10))
+ k++;
+ if ((k > 8) || ((new_lin[end_new+k] == (char) NULL) && (k != 0)))
+ {
+ if (new_lin[end_new+k] == (char) NULL)
+ {
+ Position(window, line, (end_new+k));
+ CLEAR_TO_EOL(window, line, (end_new+k));
+ }
+ Position(window, line, offset);
+ for (k = offset; k < end_old; k++)
+ Char_del(old_lin, old_att, offset, window->Num_cols);
+ while ((old_lin[offset] != (char) NULL) && (offset < COLS))
+ offset++;
+ pointer_old->last_char = offset;
+ changed = TRUE;
+ }
+ }
+ return(changed);
+}
+
+int
+check_insert(window, line, offset, pointer_new, pointer_old)
+WINDOW *window;
+int line, offset;
+struct _line *pointer_new, *pointer_old;
+{
+ int changed;
+ int end_old, end_new;
+ int k;
+ int same = FALSE;
+ int old_off;
+ int insert;
+ char *old_lin;
+ char *new_lin;
+ char *old_att;
+ char *new_att;
+
+ changed = FALSE;
+ new_lin = pointer_new->row;
+ new_att = pointer_new->attributes;
+ old_lin = pointer_old->row;
+ old_att = pointer_old->attributes;
+ end_old = end_new = offset;
+ while (((new_lin[end_new] != old_lin[end_old]) || (new_att[end_new] != old_att[end_old])) && (new_lin[end_new] != (char) NULL) && (old_lin[end_new] != (char) NULL))
+ end_new++;
+ if (new_lin[end_new] != (char) NULL)
+ {
+ k = 0;
+ while ((old_lin[end_old+k] == new_lin[end_new+k]) && (old_att[end_old+k] == new_att[end_new+k]) && (new_lin[end_new+k] != (char) NULL) && (old_lin[end_old+k] != (char) NULL) && (k < 10))
+ k++;
+ /*
+ | check for commonality between rest of lines (are the old
+ | and new lines the same, except for a chunk in the middle?)
+ | if the rest of the lines are common, do not insert text
+ */
+ old_off = end_new;
+ while ((old_lin[old_off] != (char) NULL) && (new_lin[old_off] != (char) NULL) && (old_lin[old_off] == new_lin[old_off]) && (old_att[old_off] == new_att[old_off]))
+ old_off++;
+ if ((old_lin[old_off] == new_lin[old_off]) && (old_att[old_off] == new_att[old_off]))
+ same = TRUE;
+ if ((!same) && ((k > 8) || ((new_lin[end_new+k] == (char) NULL) && (k != 0))))
+ {
+ Position(window, line, offset);
+ insert = FALSE;
+ if (String_table[ic__] == NULL)
+ {
+ String_Out(String_table[im__], NULL, 0);
+ insert = TRUE;
+ }
+ for (k = offset; k < end_new; k++)
+ {
+ if (!insert)
+ String_Out(String_table[ic__], NULL, 0);
+ Char_ins(old_lin, old_att, new_lin[k], new_att[k], k, window->Num_cols);
+ }
+ if (insert)
+ String_Out(String_table[ei__], NULL, 0);
+ while ((old_lin[offset] != (char) NULL) && (offset < COLS))
+ offset++;
+ pointer_old->last_char = offset;
+ changed = TRUE;
+ }
+ }
+ return(changed);
+}
+
+void
+doupdate()
+{
+ WINDOW *window;
+ int similar;
+ int diff;
+ int begin_old, begin_new;
+ int end_old, end_new;
+ int count1, j;
+ int from_top, tmp_ft, offset;
+ int changed;
+ int first_time;
+ int first_same;
+ int last_same;
+ int list[10];
+
+ struct _line *curr;
+ struct _line *virt;
+ struct _line *old;
+
+ struct _line *new;
+
+ char *cur_lin;
+ char *vrt_lin;
+ char *cur_att;
+ char *vrt_att;
+ char *att1, *att2;
+ char *c1, *c2;
+
+ window = virtual_scr;
+
+ if (Repaint_screen)
+ {
+ if (String_table[cl__])
+ String_Out(String_table[cl__], NULL, 0);
+ else
+ {
+ from_top = 0;
+ while (from_top < LINES)
+ {
+ Position(curscr, from_top, 0);
+ if (String_table[ce__] != NULL)
+ String_Out(String_table[ce__], NULL, 0);
+ else
+ {
+ for (j = 0; j < window->Num_cols; j++)
+ putchar(' ');
+ }
+ from_top++;
+ }
+ }
+ for (from_top = 0, curr = curscr->first_line; from_top < curscr->Num_lines; from_top++, curr = curr->next_screen)
+ {
+ Position(curscr, from_top, 0);
+ for (j = 0; (curr->row[j] != (char) NULL) && (j < curscr->Num_cols); j++)
+ {
+ Char_out(curr->row[j], curr->attributes[j], curr->row, curr->attributes, j);
+ }
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(curscr, from_top, j);
+ attribute_off();
+ }
+ }
+ Repaint_screen = FALSE;
+ }
+
+ similar = 0;
+ diff = FALSE;
+ top_of_win = curscr->first_line;
+
+ for (from_top = 0, curr = top_of_win, virt = window->first_line;
+ from_top < window->Num_lines; from_top++)
+ {
+ virtual_lines[from_top] = TRUE;
+ if ((similar = Comp_line(curr, virt)) > 0)
+ {
+ virtual_lines[from_top] = FALSE;
+ diff = TRUE;
+ }
+ curr = curr->next_screen;
+ virt = virt->next_screen;
+ }
+
+ from_top = 0;
+ virt = window->first_line;
+ curr = top_of_win;
+ similar = 0;
+ /*
+ | if the window has lines that are different
+ */
+ if (diff)
+ {
+ last_same = -1;
+ changed = FALSE;
+ for (first_same = window->Num_lines;
+ (first_same > from_top) && (virtual_lines[first_same - 1]);
+ first_same--)
+ ;
+ count1 = first_same - 1;
+ for (last_same = 0;
+ (last_same < window->Num_lines) && (virtual_lines[last_same]== FALSE);
+ last_same++)
+ ;
+ while ((from_top < first_same) && nc_scrolling_ability)
+ /* check entire lines for diffs */
+ {
+
+
+ if (from_top >= last_same)
+ {
+ for (last_same = from_top;
+ (last_same < window->Num_lines) &&
+ (virtual_lines[last_same] == FALSE);
+ last_same++)
+ ;
+ }
+ if (!virtual_lines[from_top])
+ {
+ diff = TRUE;
+ /*
+ | check for lines deleted (scroll up)
+ */
+ for (tmp_ft = from_top+1, old = curr->next_screen;
+ ((window->scroll_up) && (diff) &&
+ (tmp_ft < last_same) &&
+ (!virtual_lines[tmp_ft]));
+ tmp_ft++)
+ {
+ if ((Comp_line(old, virt) == -1) && (!virtual_lines[from_top]))
+ {
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = from_top;
+ list[0] = min((last_same - 1), (window->Num_lines - 1));
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ for (offset = (tmp_ft - from_top); (offset > 0); offset--)
+ {
+ old = Delete_line(from_top, min((last_same - 1), (window->Num_lines - 1)), window);
+ diff = FALSE;
+ }
+
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = 0;
+ list[0] = LINES;
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ top_of_win = curscr->first_line;
+ curr = top_of_win;
+ for (offset = 0; offset < from_top; offset++)
+ curr = curr->next_screen;
+ for (offset = from_top, old=curr, new=virt;
+ offset < window->Num_lines;
+ old=old->next_screen, new=new->next_screen,
+ offset++)
+ {
+ similar = Comp_line(old, new);
+ virtual_lines[offset] = (similar > 0 ? FALSE : TRUE);
+ }
+ }
+ else
+ old = old->next_screen;
+ }
+ /*
+ | check for lines inserted (scroll down)
+ */
+ for (tmp_ft = from_top-1, old = curr->prev_screen;
+ ((window->scroll_down) && (tmp_ft >= 0) &&
+ (diff) &&
+ (!virtual_lines[tmp_ft]));
+ tmp_ft--)
+ {
+ if (Comp_line(old, virt) == -1)
+ {
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = tmp_ft;
+ list[0] = min((last_same - 1), (window->Num_lines - 1));
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ for (offset = (from_top - tmp_ft); (offset > 0); offset--)
+ {
+ old = Insert_line(tmp_ft, min((last_same - 1), (window->Num_lines -1)), window);
+ diff = FALSE;
+ }
+
+ if (String_table[cs__]) /* scrolling region */
+ {
+ list[1] = 0;
+ list[0] = LINES;
+ String_Out(String_table[cs__], list, 2);
+ Curr_y = Curr_x = -1;
+ }
+
+ top_of_win = curscr->first_line;
+ curr = top_of_win;
+ for (offset = 0; offset < from_top; offset++)
+ curr = curr->next_screen;
+ for (offset = from_top, old=curr, new=virt;
+ offset < window->Num_lines;
+ old=old->next_screen, new=new->next_screen,
+ offset++)
+ {
+ similar = Comp_line(old, new);
+ virtual_lines[offset] = (similar > 0 ? FALSE : TRUE);
+ }
+ }
+ else
+ old = old->prev_screen;
+ }
+ }
+ from_top++;
+ curr = curr->next_screen;
+ virt = virt->next_screen;
+ }
+ }
+
+ for (from_top = 0, curr = curscr->first_line; from_top < window->SR; from_top++)
+ curr = curr->next_screen;
+ top_of_win = curr;
+ for (from_top = 0, curr = top_of_win, virt = window->first_line; from_top < window->Num_lines; from_top++, curr = curr->next_screen, virt = virt->next_screen)
+ {
+ if (((String_table[ic__]) || (String_table[im__])) && (String_table[dc__]) && (curr->row[0] != (char) NULL))
+ {
+ j = 0;
+ first_time = TRUE;
+ vrt_lin = virt->row;
+ vrt_att = virt->attributes;
+ cur_lin = curr->row;
+ cur_att = curr->attributes;
+ while ((vrt_lin[j] != (char) NULL) && (j < window->Num_cols))
+ {
+ if ((STAND) && (Booleans[xs__]))
+ {
+ while ((vrt_lin[j] == cur_lin[j]) && (vrt_att[j] == cur_att[j]) && (vrt_lin[j] != (char) NULL) && (vrt_att[j]))
+ j++;
+ if ((STAND) && (!vrt_att[j]))
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ attribute_off();
+ }
+ }
+ else
+ {
+ while ((vrt_lin[j] == cur_lin[j]) && (vrt_att[j] == cur_att[j]) && (vrt_lin[j] != (char) NULL))
+ j++;
+ }
+ if ((vrt_att[j] != cur_att[j]) && (cur_att[j]) && (Booleans[xs__]))
+ {
+ Position(window, from_top, j);
+/* CLEAR_TO_EOL(window, from_top, j);*/
+ attribute_off();
+ attribute_off();
+ }
+ if (vrt_lin[j] != (char) NULL)
+ {
+ begin_new = j;
+ begin_old = j;
+ end_old = j;
+ end_new = j;
+ if ((first_time) && (virt->changed))
+ {
+ if (curr->last_char <= virt->last_char)
+ changed = check_insert(window, from_top, j, virt, curr);
+ }
+ changed = check_delete(window, from_top, j, virt, curr);
+ first_time = FALSE;
+ virt->changed = FALSE;
+ if (!changed)
+ changed = check_insert(window, from_top, j, virt, curr);
+ if (((!changed) || (cur_lin[j] != vrt_lin[j]) || (cur_att[j] != vrt_att[j])) && (j < window->Num_cols))
+ {
+ if ((vrt_lin[j] == ' ') && (cur_lin[j] == (char) NULL) && (vrt_att[j] == cur_att[j]))
+ cur_lin[j] = ' ';
+ else
+ {
+ Position(window, from_top, j);
+ Char_out(vrt_lin[j], vrt_att[j], cur_lin, cur_att, j);
+ }
+ }
+ if ((vrt_lin[j] != (char) NULL))
+ j++;
+ }
+ if ((STAND) && (!vrt_att[j]))
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ }
+ }
+ if ((vrt_lin[j] == (char) NULL) && (cur_lin[j] != (char) NULL))
+ {
+ Position(window, from_top, j);
+ CLEAR_TO_EOL(window, from_top, j);
+ }
+ }
+ else /*if ((similar != -1) && (similar != 0))*/
+ {
+ j = 0;
+ c1 = curr->row;
+ att1 = curr->attributes;
+ c2 = virt->row;
+ att2 = virt->attributes;
+ while ((j < window->Num_cols) && (c2[j] != (char) NULL))
+ {
+ while ((c1[j] == c2[j]) && (att1[j] == att2[j]) && (j < window->Num_cols) && (c2[j] != (char) NULL))
+ j++;
+ begin_old = j;
+ begin_new = j;
+ if ((j < window->Num_cols) && (c2[j] != (char) NULL))
+ {
+ Position(window, from_top, begin_old);
+ CLEAR_TO_EOL(window, from_top, j);
+ Position(window, from_top, begin_old);
+ for (j = begin_old; (c2[j] != (char) NULL) && (j < window->Num_cols); j++)
+ Char_out(c2[j], att2[j], c1, att1, j);
+ }
+ }
+ if ((c2[j] == (char) NULL) && (c1[j] != (char) NULL))
+ {
+ Position(window, from_top, j);
+ CLEAR_TO_EOL(window, from_top, j);
+ }
+ }
+ if (STAND)
+ {
+ STAND = FALSE;
+ Position(window, from_top, j);
+ attribute_off();
+ }
+ virt->number = from_top;
+ }
+ Position(window, window->LY, window->LX);
+}
+
+void
+Position(window, row, col) /* position the cursor for output on the screen */
+WINDOW *window;
+int row;
+int col;
+{
+ int list[10];
+ int place;
+
+ int pos_row;
+ int pos_column;
+
+ pos_row = row + window->SR;
+ pos_column = col + window->SC;
+ if ((pos_row != Curr_y) || (pos_column != Curr_x))
+ {
+ if (String_table[cm__] != NULL) /* && (row < window->Num_lines) && (column < window->Num_cols))*/
+ {
+ place = 0;
+ list[place++] = pos_column;
+ list[place++] = pos_row;
+ String_Out(String_table[cm__], list, place);
+ if ((STAND) && (!Booleans[ms__]))
+ attribute_on();
+ }
+ Curr_x = pos_column;
+ Curr_y = pos_row;
+ }
+}
+
+void
+Char_del(line, attrib, offset, maxlen) /* delete chars from line */
+char *line;
+char *attrib;
+int offset;
+int maxlen;
+{
+ int one, two;
+
+ for (one = offset, two = offset+1; (line[one] != (char) NULL) && (one < maxlen); one++, two++)
+ {
+ line[one] = line[two];
+ attrib[one] = attrib[two];
+ }
+ String_Out(String_table[dc__], NULL, 0);
+}
+
+void
+Char_ins(line, attrib, newc, newatt, offset, maxlen) /* insert chars in line */
+char *line;
+char *attrib;
+char newc;
+char newatt;
+int offset;
+int maxlen;
+{
+ int one, two;
+
+ one = 0;
+ while ((line[one] != (char) NULL) && (one < (maxlen - 2)))
+ one++;
+ for (two = one + 1; (two > offset); one--, two--)
+ {
+ line[two] = line[one];
+ attrib[two] = attrib[one];
+ }
+ line[offset] = newc;
+ attrib[offset] = newatt;
+ Char_out(newc, newatt, line, attrib, offset);
+}
+
+void
+attribute_on()
+{
+ if (String_table[sa__])
+ {
+ attributes_set[0] = 1;
+ String_Out(String_table[sa__], attributes_set, 1);
+ }
+ else if (String_table[so__])
+ String_Out(String_table[so__], NULL, 0);
+}
+
+void
+attribute_off()
+{
+ if (String_table[me__])
+ String_Out(String_table[me__], NULL, 0);
+ else if (String_table[sa__])
+ {
+ attributes_set[0] = 0;
+ String_Out(String_table[sa__], attributes_set, 1);
+ }
+ else if (String_table[se__])
+ String_Out(String_table[se__], NULL, 0);
+}
+
+void
+Char_out(newc, newatt, line, attrib, offset) /* output character with proper attribute */
+char newc;
+char newatt;
+char *line;
+char *attrib;
+int offset;
+{
+
+
+ if ((newatt) && (!STAND))
+ {
+ STAND = TRUE;
+ attribute_on();
+ }
+ else if ((STAND) && (!newatt))
+ {
+ STAND = FALSE;
+ attribute_off();
+ }
+
+ if ((newatt) && (STAND) && (Booleans[xs__]))
+ {
+ attribute_on();
+ }
+
+ if (!((Curr_y >= (LINES - 1)) && (Curr_x >= (COLS - 1))))
+ {
+ putchar(newc);
+ line[offset] = newc;
+ attrib[offset] = newatt;
+ }
+ Curr_x++;
+}
+
diff --git a/usr.bin/ee/doc/new_curse.h b/usr.bin/ee/doc/new_curse.h
new file mode 100644
index 0000000..f69ee59
--- /dev/null
+++ b/usr.bin/ee/doc/new_curse.h
@@ -0,0 +1,255 @@
+/*
+ | new_curse.h
+ |
+ | A subset of curses developed for use with ae.
+ |
+ | written by Hugh Mahon
+ |
+ | THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+ | NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+ | MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+ | IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ | FITNESS FOR A PARTICULAR PURPOSE. Neither
+ | Hewlett-Packard nor Hugh Mahon shall be liable
+ | for errors contained herein, nor for
+ | incidental or consequential damages in
+ | connection with the furnishing, performance or
+ | use of this material. Neither Hewlett-Packard
+ | nor Hugh Mahon assumes any responsibility for
+ | the use or reliability of this software or
+ | documentation. This software and
+ | documentation is totally UNSUPPORTED. There
+ | is no support contract available. Hewlett-
+ | Packard has done NO Quality Assurance on ANY
+ | of the program or documentation. You may find
+ | the quality of the materials inferior to
+ | supported materials.
+ |
+ | This software is not a product of Hewlett-Packard, Co., or any
+ | other company. No support is implied or offered with this software.
+ | You've got the source, and you're on your own.
+ |
+ | This software may be distributed under the terms of Larry Wall's
+ | Artistic license, a copy of which is included in this distribution.
+ |
+ | This notice must be included with this software and any derivatives.
+ |
+ | Copyright (c) 1986, 1987, 1988, 1991, 1995 Hugh Mahon
+ | All are rights reserved.
+ |
+ */
+
+#include <stdio.h>
+
+#ifdef SYS5
+#include <termio.h>
+#else
+#include <sgtty.h>
+#include <fcntl.h>
+#endif
+
+#define KEY_BREAK 0401
+#define KEY_DOWN 0402
+#define KEY_UP 0403
+#define KEY_LEFT 0404
+#define KEY_RIGHT 0405
+#define KEY_HOME 0406
+#define KEY_BACKSPACE 0407
+#define KEY_F0 0410
+#define KEY_F(n) (KEY_F0+(n))
+#define KEY_DL 0510
+#define KEY_IL 0511
+#define KEY_DC 0512
+#define KEY_IC 0513
+#define KEY_EIC 0514
+#define KEY_CLEAR 0515
+#define KEY_EOS 0516
+#define KEY_EOL 0517
+#define KEY_SF 0520
+#define KEY_SR 0521
+#define KEY_NPAGE 0522
+#define KEY_PPAGE 0523
+#define KEY_STAB 0524
+#define KEY_CTAB 0525
+#define KEY_CATAB 0526
+#define KEY_ENTER 0527
+#define KEY_SRESET 0530
+#define KEY_RESET 0531
+#define KEY_PRINT 0532
+#define KEY_LL 0533
+#define KEY_A1 0534
+#define KEY_A3 0535
+#define KEY_B2 0536
+#define KEY_C1 0537
+#define KEY_C3 0540
+#define KEY_BTAB 0541
+#define KEY_BEG 0542
+#define KEY_CANCEL 0543
+#define KEY_CLOSE 0544
+#define KEY_COMMAND 0545
+#define KEY_COPY 0546
+#define KEY_CREATE 0547
+#define KEY_END 0550
+#define KEY_EXIT 0551
+#define KEY_FIND 0552
+#define KEY_HELP 0553
+#define KEY_MARK 0554
+#define KEY_MESSAGE 0555
+#define KEY_MOVE 0556
+#define KEY_NEXT 0557
+#define KEY_OPEN 0560
+#define KEY_OPTIONS 0561
+#define KEY_PREVIOUS 0562
+#define KEY_REDO 0563
+#define KEY_REFERENCE 0564
+#define KEY_REFRESH 0565
+#define KEY_REPLACE 0566
+#define KEY_RESTART 0567
+#define KEY_RESUME 0570
+#define KEY_SAVE 0571
+#define KEY_SBEG 0572
+#define KEY_SCANCEL 0573
+#define KEY_SCOMMAND 0574
+#define KEY_SCOPY 0575
+#define KEY_SCREATE 0576
+#define KEY_SDC 0577
+#define KEY_SDL 0600
+#define KEY_SELECT 0601
+#define KEY_SEND 0602
+#define KEY_SEOL 0603
+#define KEY_SEXIT 0604
+#define KEY_SFIND 0605
+#define KEY_SHELP 0606
+#define KEY_SHOME 0607
+#define KEY_SIC 0610
+#define KEY_SLEFT 0611
+#define KEY_SMESSAGE 0612
+#define KEY_SMOVE 0613
+#define KEY_SNEXT 0614
+#define KEY_SOPTIONS 0615
+#define KEY_SPREVIOUS 0616
+#define KEY_SPRINT 0617
+#define KEY_SREDO 0620
+#define KEY_SREPLACE 0621
+#define KEY_SRIGHT 0622
+#define KEY_SRSUME 0623
+#define KEY_SSAVE 0624
+#define KEY_SSUSPEND 0625
+#define KEY_SUNDO 0626
+#define KEY_SUSPEND 0627
+#define KEY_UNDO 0630
+
+#define TRUE 1
+#define FALSE 0
+
+#define A_STANDOUT 0001 /* standout mode */
+#define SCROLL 1 /* text has been scrolled */
+#define CLEAR 2 /* window has been cleared */
+#define CHANGE 3 /* window has been changed */
+#define UP 1 /* direction of scroll */
+#define DOWN 2
+
+struct _line {
+ struct _line *next_screen;
+ struct _line *prev_screen;
+ char *row;
+ char *attributes;
+ int last_char;
+ int changed;
+ int scroll;
+ int number;
+ };
+
+struct _line *top_of_win;
+
+typedef struct WIND {
+ int SR; /* starting row */
+ int SC; /* starting column */
+ int LC; /* last column */
+ int LX; /* last cursor column position */
+ int LY; /* last cursor row position */
+ int Attrib; /* attributes active in window */
+ int Num_lines; /* number of lines */
+ int Num_cols; /* number of columns */
+ int scroll_up; /* number of lines moved */
+ int scroll_down;
+ int SCROLL_CLEAR; /* indicates that window has been scrolled or cleared */
+ struct _line *first_line;
+ } WINDOW;
+
+extern WINDOW *curscr;
+extern WINDOW *stdscr;
+
+extern int LINES, COLS;
+
+#if __STDC__ || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif
+
+extern void copy_window P_((WINDOW *origin, WINDOW *destination));
+extern void reinitscr P_((int));
+extern void initscr P_((void));
+extern int Get_int P_((void));
+extern int INFO_PARSE P_((void));
+extern int AtoI P_((void));
+extern void Key_Get P_((void));
+extern struct _line *Screenalloc P_((int columns));
+extern WINDOW *newwin P_((int lines, int cols, int start_l, int start_c));
+extern int Operation P_((int Temp_Stack[], int place));
+extern void Info_Out P_((char *string, int p_list[], int place));
+extern void wmove P_((WINDOW *window, int row, int column));
+extern void clear_line P_((struct _line *line, int column, int cols));
+extern void werase P_((WINDOW *window));
+extern void wclrtoeol P_((WINDOW *window));
+extern void wrefresh P_((WINDOW *window));
+extern void touchwin P_((WINDOW *window));
+extern void wnoutrefresh P_((WINDOW *window));
+extern void flushinp P_((void));
+extern void ungetch P_((int c));
+extern int wgetch P_((WINDOW *window));
+extern void Clear P_((int));
+extern int Get_key P_((int first_char));
+extern void waddch P_((WINDOW *window, int c));
+extern void winsertln P_((WINDOW *window));
+extern void wdeleteln P_((WINDOW *window));
+extern void wclrtobot P_((WINDOW *window));
+extern void wstandout P_((WINDOW *window));
+extern void wstandend P_((WINDOW *window));
+extern void waddstr P_((WINDOW *window, char *string));
+extern void clearok P_((WINDOW *window, int flag));
+extern void echo P_((void));
+extern void noecho P_((void));
+extern void raw P_((void));
+extern void noraw P_((void));
+extern void nl P_((void));
+extern void nonl P_((void));
+extern void saveterm P_((void));
+extern void fixterm P_((void));
+extern void resetterm P_((void));
+extern void nodelay P_((WINDOW *window, int flag));
+extern void idlok P_((WINDOW *window, int flag));
+extern void keypad P_((WINDOW *window, int flag));
+extern void savetty P_((void));
+extern void resetty P_((void));
+extern void endwin P_((void));
+extern void delwin P_((WINDOW *window));
+extern void wprintw P_((WINDOW *window, __const char* format, ...));
+extern void iout P_((WINDOW *window, int value));
+extern int Comp_line P_((struct _line *line1, struct _line *line2));
+extern struct _line *Insert_line P_((int row, int end_row, WINDOW *window));
+extern struct _line *Delete_line P_((int row, int end_row, WINDOW *window));
+extern void CLEAR_TO_EOL P_((WINDOW *window, int row, int column));
+extern int check_delete P_((WINDOW *window, int line, int offset, struct _line *pointer_new, struct _line *pointer_old));
+extern int check_insert P_((WINDOW *window, int line, int offset, struct _line *pointer_new, struct _line *pointer_old));
+extern void doupdate P_((void));
+extern void Position P_((WINDOW *window, int row, int col));
+extern void Char_del P_((char *line, char *attrib, int offset, int maxlen));
+extern void Char_ins P_((char *line, char *attrib, int newc, int newatt, int offset, int maxlen));
+extern void attribute_on P_((void));
+extern void attribute_off P_((void));
+extern void Char_out P_((int newc, int newatt, char *line, char *attrib, int offset));
+
+#undef P_
+
diff --git a/usr.bin/ee/ee.1 b/usr.bin/ee/ee.1
new file mode 100644
index 0000000..2014b46
--- /dev/null
+++ b/usr.bin/ee/ee.1
@@ -0,0 +1,508 @@
+.\"
+.\"
+.\" To format this reference page, use the command:
+.\"
+.\" nroff -man ee.1
+.\"
+.\" $Header: /users/hugh/tmp/old_ae/ee.1,v 1.16 1993/06/22 04:18:33 hugh Exp $
+.\"
+.\"
+.TH ee 1 "" "" "" ""
+.SH NAME
+ee \- easy editor
+.SH SYNOPSIS
+.nf
+ee [-e] [-i] [-h] [+#] [\fIfile\fR ...]
+ree [-e] [-i] [-h] [+#] [\fIfile\fR ...]
+.ta
+.fi
+.ad b
+.SH DESCRIPTION
+The command
+.I ee
+is a simple screen oriented text editor. It is always in text insertion
+mode unless there is a prompt at the bottom of the terminal, or a
+menu present (in a box in the middle of the terminal). The command
+.I ree
+is the same as
+.I ee,
+but restricted to editing the named
+file (no file operations, or shell escapes are allowed).
+.PP
+For
+.I ee
+to work properly, the environment variable
+.SM TERM
+must be set to indicate the type of terminal being used. For
+example, for an
+.SM HP 700/92
+terminal, the
+.SM TERM
+variable should be set to "70092". See your System Administrator if
+you need more information.
+.\"
+.\" options
+.\"
+.SS Options
+The following options are available from the command line:
+.PP
+.TP 4
+.B -e
+Turns off expansion of tab character to spaces.
+.TP
+.B -i
+Turns off display of information window at top of terminal.
+.TP
+.B -h
+Turns off highlighting of borders of windows and menus (improves
+performance on some terminals).
+.TP
+.B +#
+Moves the cursor to line '#' at startup.
+.br
+.\"
+.\" control keys
+.\"
+.SS "Control keys"
+To do anything other than insert text, the user must use the control
+keys (the
+.B Control
+key, represented by a "^", pressed in conjunction with an
+alphabetic key, e.g., ^a) and function keys available on the keyboard
+(such as
+.BR "Next Page" ", " "Prev Page" ,
+arrow keys, etc.).
+.PP
+Since not all terminals have function keys,
+.I ee
+has the basic cursor movement functions assigned to control keys as
+well as more intuitive keys on the keyboard when available. For
+instance, to move the cursor up, the user can use the up arrow key,
+or
+.BR ^u .
+.RS 4
+.nf
+.ta 1.4i
+.sp
+^a Prompt for the decimal value of a character to insert.
+^b Move to the bottom of the text.
+^c Get the prompt for a command.
+^d Move the cursor down.
+^e Prompt for the string to search for.
+^f Undelete the last deleted character.
+^g Move to the beginning of the line.
+^h Backspace.
+^i Tab.
+^j Insert a newline.
+^k Delete the character the cursor is sitting on.
+^l Move the cursor left.
+^m Insert a newline.
+^n Move to the next page.
+^o Move to the end of the line.
+^p Move to the previous page.
+^r Move the cursor to the right.
+^t Move to the top of the text.
+^u Move the cursor up.
+^v Undelete the last deleted word.
+^w Delete the word beginning at the cursor position.
+^x Search.
+^y Delete from the cursor position to the end of line.
+^z Undelete the last deleted line.
+^[ (ESC) Pop up menu.
+.ta
+.fi
+.RE
+.sp
+.SS "EMACS keys mode"
+.PP
+Since many shells provide an Emacs mode (for cursor movement and other editing
+operations), some bindings that may be more useful for people familiar with
+those bindings have been provided. These are accessible via the
+.B settings
+menu, or via the initialization file (see below). The mappings are as follows:
+.RS
+.nf
+.ta 1.4i
+^a Move to the beginning of the line.
+^b Back 1 character.
+^c Command prompt.
+^d Delete character the cursor is sitting on.
+^e End of line.
+^f Forward 1 character.
+^g Go back 1 page.
+^h Backspace.
+^i Tab.
+^j Undelete last deleted character.
+^k Delete line.
+^l Undelete last deleted line.
+^m Insert a newline.
+^n Move to the next line.
+^o Prompt for the decimal value of a character to insert.
+^p Previous line.
+^r Restore last deleted word.
+^t Move to the top of the text.
+^u Move to the bottom of the text.
+^v Move to the next page.
+^w Delete the word begining at the cursor position.
+^y Prompt for the string to search for.
+^z Next word.
+^[ (ESC) Pop up menu.
+.ta
+.fi
+.RE
+.sp
+.\"
+.\" function keys
+.\"
+.SS "Function Keys"
+.RS 4
+.IP "\fBNext Page\fR"
+Move to the next page.
+.IP "\fBPrev Page\fR"
+Move to the previous page.
+.IP "\fBDelete Char\fR"
+Delete the character the cursor is on.
+.IP "\fBDelete Line\fR"
+Delete from the cursor to the end of line.
+.IP "\fBInsert line\fR"
+Insert a newline at the cursor position.
+.IP "\fBArrow keys\fR"
+Move the cursor in the direction indicated.
+.RE
+.\"
+.\" commands
+.\"
+.SS Commands
+.PP
+Some operations require more information than a single keystroke can
+provide. For the most basic operations, there is a menu that can be
+obtained by pressing the
+.SM \fBESC\fR
+key. The same operations, and more can be performed by obtaining the
+command prompt (^c) and typing in one of the commands below.
+.RS 4
+.IP "!\fBcmd\fR"
+Execute \fBcmd\fR in a shell.
+.IP "\fB0-9\fR"
+Move to the line indicated.
+.IP "\fBcase\fR"
+Make searches case sensitive.
+.IP "\fBcharacter\fR"
+Display the ascii value of the character at the cursor.
+.IP "\fBexit\fR"
+Save the edited text, and leave the editor.
+.IP "\fBexpand\fR"
+Expand tabs to spaces.
+.IP "\fBfile\fR"
+Print the name of the file.
+.IP "\fBhelp\fR"
+Display help screen.
+.IP "\fBline\fR"
+Display the current line number.
+.IP "\fBnocase\fR
+Make searches insensitive to case (the default).
+.IP "\fBnoexpand\fR"
+Don't expand tab to spaces when the TAB key is pressed.
+.IP "\fBquit\fR"
+Leave the editor without saving changes.
+.IP "\fBread\fR \fIfile\fR"
+Read the named \fIfile\fR.
+.IP "\fBwrite\fR \fIfile\fR"
+Write the text to the named \fIfile\fR.
+.RE
+.\"
+.\" menu options
+.\"
+.SS "Menu Operations"
+.PP
+Pop-up menus can be obtained by pressing the
+.B escape
+key (or
+.B ^[
+if no
+.B escape
+key is present). When in the menu, the escape key can be
+used to leave the menu without performing any operations. Use the up and
+down arrow keys, or
+.B ^u
+for moving up and
+.B ^d
+for moving down to move to the desired items in the menu, then press
+.B return
+to perform the indicated task.
+.PP
+The main menu in \fIee\fR is as follows:
+.RS 4
+.IP "\fBleave editor\fR"
+If changes have been made, the user will get a menu prompting whether or
+not the changes should be saved.
+.IP "\fBhelp\fR"
+Displays a help screen, with all of the keyboard operations and commands.
+.IP "\fBfile operations\fR"
+Pops up a menu for selecting whether to read a file, write to a file, or
+save the current contents of the editor, as well as send the contents of
+the editor to a print command (see the section \fBInitializing ee from a
+file\fR).
+.IP "\fBredraw screen\fR"
+Provides a means to repaint the screen if the screen has been corrupted.
+.IP "\fBsettings\fR"
+Shows the current values of the operating modes, and right margin. By
+pressing return when the cursor is on a particular item, the value can be
+changed. To leave this menu, press the \fBescape\fR key. (See \fBModes\fR
+below.)
+.IP "\fBsearch\fR"
+.br
+Pops up a menu in which the user may choose to enter a string to search
+for, or search for a string already entered.
+.IP "\fBmiscellaneous\fR"
+Pops up a menu that allows the user to format the current paragraph,
+execute a shell command, or check the spelling of the text in the editor.
+.RE
+.\"
+.\" paragraph formatting
+.\"
+.SS "Paragraph Formatting"
+.PP
+Paragraphs are defined for \fIee\fR by a block of text bounded by:
+.sp
+.RS 8
+.IP \(bu
+Begin or end of file.
+.IP \(bu
+Line with no characters, or only spaces and/or tabs.
+.IP \(bu
+Line starting with a period ('.') or right angle bracket ('>').
+.RE
+.PP
+A paragraph may be formatted two ways: explicitly by choosing the
+\fBformat paragraph\fR menu item, or by setting \fIee\fR to automatically
+format paragraphs. The automatic mode may be set via a menu, or via the
+initialization file.
+.PP
+There are three states for text operation in \fIee\fR: free-form, margins,
+and automatic formatting.
+.PP
+"Free-form" is best used for things like programming. There are no
+restrictions on the length of lines, and no formatting takes place.
+.PP
+"Margins" allows the user to type in text without having to worry about going
+beyond the right margin (the right margin may be set in the \fBsettings\fR
+menu, the default is for the margin to be the right edge of the
+terminal). This is the mode that allows the \fBformat paragraph\fR menu
+item to work.
+.PP
+"Automatic formatting" provides word-processor-like behavior. The user
+may type in text, while \fIee\fR will make sure the entire paragraph fits
+within the width of the terminal every time the user inserts a space after
+typing or deleting text. Margin observation must also be enabled in order for
+automatic formatting to occur.
+.\"
+.\" modes
+.\"
+.SS Modes
+.PP
+Although ee is a 'modeless' editor (it is in text insertion mode all the
+time), there are modes in some of the things it does. These include:
+.RS 4
+.IP "\fBtab expansion\fR"
+Tabs may be inserted as a single tab character, or replaced with spaces.
+.IP "\fBcase sensitivity\fR"
+The search operation can be sensitive to whether characters are upper- or
+lower-case, or ignore case completely.
+.IP "\fBmargins observed\fR"
+Lines can either be truncated at the right margin, or extend on forever.
+.IP "\fBauto paragraph formatting\fR"
+While typing in text, the editor can try to keep it looking reasonably well
+within the width of the screen.
+.IP "\fBeightbit characters\fR"
+Toggles whether eight bit characters are displayed as their value in angle
+brackets (e.g. "<220>") or as a character.
+.IP "\fBinfo window\fR"
+A window showing the keyboard operations that can be performed can be
+displayed or not.
+.IP"\fBemacs keys\fR"
+Control keys may be given bindings similar to emacs, or not.
+.RE
+.PP
+You may set these modes via the initialization file (see below), or with a
+menu (see above).
+.\"
+.\" spell checking
+.\"
+.SS "Spell Checking"
+.PP
+There are two ways to have the spelling in the text checked from \fIee\fR.
+One is by the traditional \fIspell\fR(1) command, the other is with the
+optional \fIispell\fR(1) command.
+.PP
+Using \fIspell\fR, the words that are not recognized will be placed at the top
+of the file. For the \fIispell\fR option, the file is written to disk,
+then \fIispell\fR run on the file, and the file read back in once
+\fIispell\fR has completed making changes to the file.
+.\"
+.\" printing
+.\"
+.SS "Printing the contents of the editor"
+.PP
+The user may select a menu item which prints the contents of the editor.
+.I ee
+pipes the text in the editor to the command specified by the
+initialization command
+.B printcommand
+(see the section
+.B Initializing ee from a file
+below). The default is to send the contents to "lp".
+.PP
+Whatever the user assigns to
+.B printcommand
+must take input from
+standard input. See your system administrator for more details.
+.\"
+.\" shell operations
+.\"
+.SS "Shell operations"
+.PP
+Shell commands can be executed from within
+.I ee
+by selecting the
+.B shell command
+item in the
+.B miscellaneous
+menu, or by placing an exclamation mark ("!") before the command to
+execute at the
+.B command:
+prompt. Additionally, the user may direct the contents of the edit buffer
+out to a shell operation (via a pipe) by using the left angle bracket
+(">"), followed by a "!" and the shell command to execute. The output of
+a shell operation can also be directed into the edit buffer by using a
+right angle bracket ("<") before the exclamation mark. These can even be
+used together to send output to a shell operation and read back the
+results into the editor. So, if the editor contained a list of words
+to be sorted, they could be sorted by typing the following at the command
+prompt:
+.RS 4
+.sp
+><!sort
+.sp
+.RE
+This would send the contents of the editor to be piped into the
+.I sort
+utility and the result would be placed into the edit buffer at the current
+cursor location. The old information would have to be deleted by the user.
+.\"
+.\" initializing ee from a file
+.\"
+.SS "Initializing ee from a file"
+.PP
+Since different users have different preferences, \fIee\fR allows some
+slight configurability. There are three possible locations for an
+initialization file for ee: the file \fI/usr/share/misc/init.ee\fR, the
+file \fI.init.ee\fR in the user's home directory, or the file \fI.init.ee\fR
+in the current directory (if different from the home
+directory). This allows system administrators to set some preferences for
+the users on a system-wide basis (for example, the \fBprint\fR command),
+and the user to customize settings for particular directories (like one
+for correspondence, and a different directory for programming).
+.PP
+The file \fI\/usr/share/misc/init.ee\fR is read first, then
+\fI$HOME/.init.ee\fR, then \fI.init.ee\fR, with the settings specified by the
+most recent file read taking precedence.
+.PP
+The following items may be entered in the initialization file:
+.RS 4
+.IP \fBcase\fR
+Sets searches to be case sensitive.
+.IP \fBnocase\fR
+Sets searches to be insensitive to case (default).
+.IP \fBexpand\fR
+Causes \fIee\fR to expand tabs to spaces (default).
+.IP \fBnoexpand\fR
+Causes \fIee\fR to insert tabs as a single character.
+.IP \fBinfo\fR
+A small information window is displayed at the top of the terminal
+(default).
+.IP \fBnoinfo\fR
+Turns off the display of the information window.
+.IP \fBmargins\fR
+Causes \fIee\fR to truncate lines at the right margin when the
+cursor passes beyond the right margin as set by the user
+while text is being inserted
+(default).
+.IP \fBnomargins\fR
+Allows lines to extend beyond the right margin.
+.IP \fBautoformat\fR
+Causes \fIee\fR to automatically try to format the current paragraph while
+text insertion is occurring.
+.IP \fBnoautoformat\fR
+Turns off automatic paragraph formatting (default).
+.IP \fBprintcommand\fR
+Allows the setting of the print command (default: "lp").
+.IP \fBrightmargin\fR
+The user can select a value for the right margin (the first column on the
+screen is zero).
+.IP \fBhighlight\fR
+Turns on highlighting border of information window and menus (default).
+.IP \fBnohighlight\fR
+Turns off highlighting of border of information window and menus.
+.IP \fBeightbit\fR
+Turns on display of eight bit characters.
+.IP \fBnoeightbit\fR
+Turns off display of eight bit characters (they are displayed as their decimal
+value inside angle brackets, e.g., "<220>").
+.IP \fBemacs\fR
+Turns on emacs key bindings.
+.IP \fBnoemacs\fR
+Turns off emacs key bindings.
+.RE
+.SH CAVEATS
+.PP
+THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE. Neither
+Hewlett-Packard nor Hugh Mahon shall be liable
+for errors contained herein, nor for
+incidental or consequential damages in
+connection with the furnishing, performance or
+use of this material. Neither Hewlett-Packard
+nor Hugh Mahon assumes any responsibility for
+the use or reliability of this software or
+documentation. This software and
+documentation is totally UNSUPPORTED. There
+is no support contract available. Hewlett-Packard
+has done NO Quality Assurance on ANY
+of the program or documentation. You may find
+the quality of the materials inferior to
+supported materials.
+.PP
+Always make a copy of files that cannot be easily reproduced before
+editing. Save files early, and save often.
+.SS "International Code Set Support"
+.I ee
+supports single-byte character code sets (eight-bit clean).
+.SH WARNINGS
+The automatic paragraph formatting operation
+may be too slow for slower systems.
+.SH FILES
+.PP
+.I /usr/share/misc/init.ee
+.br
+.I $HOME/.init.ee
+.br
+.I .init.ee
+.SH AUTHOR
+.PP
+The software
+.I ee
+was developed by Hugh Mahon.
+.PP
+This software and documentation contains
+proprietary information which is protected by
+copyright. All rights are reserved.
+.PP
+Copyright (c) 1990, 1991, 1992, 1993 Hugh Mahon.
+.SH "SEE ALSO"
+.PP
+termcap(4), terminfo(4), environ(5), spell(1), ispell(1), lp(1)
+
diff --git a/usr.bin/ee/ee.c b/usr.bin/ee/ee.c
new file mode 100644
index 0000000..75c6994
--- /dev/null
+++ b/usr.bin/ee/ee.c
@@ -0,0 +1,4797 @@
+/*
+ | ee (easy editor)
+ |
+ | An easy to use, simple screen oriented editor.
+ |
+ | written by Hugh Mahon
+ |
+ | THIS MATERIAL IS PROVIDED "AS IS". THERE ARE
+ | NO WARRANTIES OF ANY KIND WITH REGARD TO THIS
+ | MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE
+ | IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ | FITNESS FOR A PARTICULAR PURPOSE. Neither
+ | Hewlett-Packard nor Hugh Mahon shall be liable
+ | for errors contained herein, nor for
+ | incidental or consequential damages in
+ | connection with the furnishing, performance or
+ | use of this material. Neither Hewlett-Packard
+ | nor Hugh Mahon assumes any responsibility for
+ | the use or reliability of this software or
+ | documentation. This software and
+ | documentation is totally UNSUPPORTED. There
+ | is no support contract available. Hewlett-
+ | Packard has done NO Quality Assurance on ANY
+ | of the program or documentation. You may find
+ | the quality of the materials inferior to
+ | supported materials.
+ |
+ | This software is not a product of Hewlett-Packard, Co., or any
+ | other company. No support is implied or offered with this software.
+ | You've got the source, and you're on your own.
+ |
+ | This software may be distributed under the terms of Larry Wall's
+ | Artistic license, a copy of which is included in this distribution.
+ |
+ | This notice must be included with this software and any derivatives.
+ |
+ | This editor was purposely developed to be simple, both in
+ | interface and implementation. This editor was developed to
+ | address a specific audience: the user who is new to computers
+ | (especially UNIX).
+ |
+ | ee is not aimed at technical users; for that reason more
+ | complex features were intentionally left out. In addition,
+ | ee is intended to be compiled by people with little computer
+ | experience, which means that it needs to be small, relatively
+ | simple in implementation, and portable.
+ |
+ | This software and documentation contains
+ | proprietary information which is protected by
+ | copyright. All rights are reserved.
+ |
+ | $Header: /home/ncvs/src/usr.bin/ee/ee.c,v 1.3 1995/08/30 17:42:28 ache Exp $
+ |
+ */
+
+char *ee_copyright_message =
+"Copyright (c) 1986, 1990, 1991, 1992, 1993, 1994, 1995 Hugh Mahon ";
+
+char *ee_long_notice[] = {
+ "This software and documentation contains",
+ "proprietary information which is protected by",
+ "copyright. All rights are reserved."
+ };
+
+char *version = "@(#) ee, version 1.2.4 $Revision: 1.3 $";
+
+#ifdef NCURSE
+#include "new_curse.h"
+#else
+#ifdef HAS_NCURSES
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+#endif
+
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+
+#ifdef HAS_SYS_WAIT
+#include <sys/wait.h>
+#endif
+
+#ifdef HAS_STDLIB
+#include <stdlib.h>
+#endif
+
+#ifdef HAS_STDARG
+#include <stdarg.h>
+#endif
+
+#ifdef HAS_UNISTD
+#include <unistd.h>
+#endif
+
+#ifdef HAS_CTYPE
+#include <ctype.h>
+#endif
+
+
+#ifndef NO_CATGETS
+#include <locale.h>
+#include <nl_types.h>
+
+nl_catd catalog;
+#else
+#define catgetlocal(a, b) (b)
+#endif /* NO_CATGETS */
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+#define TAB 9
+#define max(a, b) (a > b ? a : b)
+#define min(a, b) (a < b ? a : b)
+
+/*
+ | defines for type of data to show in info window
+ */
+
+#define CONTROL_KEYS 1
+#define COMMANDS 2
+
+struct text {
+ char *line; /* line of characters */
+ int line_number; /* line number */
+ int line_length; /* actual number of characters in the line */
+ int max_length; /* maximum number of characters the line handles */
+ struct text *next_line; /* next line of text */
+ struct text *prev_line; /* previous line of text */
+ };
+
+struct text *first_line; /* first line of current buffer */
+struct text *dlt_line; /* structure for info on deleted line */
+struct text *curr_line; /* current line cursor is on */
+struct text *tmp_line; /* temporary line pointer */
+struct text *srch_line; /* temporary pointer for search routine */
+
+struct files { /* structure to store names of files to be edited*/
+ char *name; /* name of file */
+ struct files *next_name;
+ };
+
+struct files *top_of_stack = NULL;
+
+int d_wrd_len; /* length of deleted word */
+int position; /* offset in bytes from begin of line */
+int scr_pos; /* horizontal position */
+int scr_vert; /* vertical position on screen */
+int scr_horz; /* horizontal position on screen */
+int tmp_vert, tmp_horz;
+int input_file; /* indicate to read input file */
+int recv_file; /* indicate reading a file */
+int edit; /* continue executing while true */
+int gold; /* 'gold' function key pressed */
+int fildes; /* file descriptor */
+int case_sen; /* case sensitive search flag */
+int last_line; /* last line for text display */
+int last_col; /* last column for text display */
+int horiz_offset = 0; /* offset from left edge of text */
+int clear_com_win; /* flag to indicate com_win needs clearing */
+int text_changes = FALSE; /* indicate changes have been made to text */
+int get_fd; /* file descriptor for reading a file */
+int info_window = TRUE; /* flag to indicate if help window visible */
+int info_type = CONTROL_KEYS; /* flag to indicate type of info to display */
+int expand_tabs = TRUE; /* flag for expanding tabs */
+int right_margin = 0; /* the right margin */
+int observ_margins = TRUE; /* flag for whether margins are observed */
+int shell_fork;
+int temp_stdin; /* temporary storage for stdin */
+int temp_stdout; /* temp storage for stdout descriptor */
+int temp_stderr; /* temp storage for stderr descriptor */
+int pipe_out[2]; /* pipe file desc for output */
+int pipe_in[2]; /* pipe file descriptors for input */
+int out_pipe; /* flag that info is piped out */
+int in_pipe; /* flag that info is piped in */
+int formatted = FALSE; /* flag indicating paragraph formatted */
+int auto_format = FALSE; /* flag for auto_format mode */
+int restricted = FALSE; /* flag to indicate restricted mode */
+int nohighlight = FALSE; /* turns off highlighting */
+int eightbit = TRUE; /* eight bit character flag */
+int local_LINES = 0; /* copy of LINES, to detect when win resizes */
+int local_COLS = 0; /* copy of COLS, to detect when win resizes */
+int curses_initialized = FALSE; /* flag indicating if curses has been started*/
+int emacs_keys_mode = FALSE; /* mode for if emacs key binings are used */
+
+char *point; /* points to current position in line */
+char *srch_str; /* pointer for search string */
+char *u_srch_str; /* pointer to non-case sensitive search */
+char *srch_1; /* pointer to start of suspect string */
+char *srch_2; /* pointer to next character of string */
+char *srch_3;
+char *in_file_name = NULL; /* name of input file */
+char *tmp_file; /* temporary file name */
+char d_char; /* deleted character */
+char *d_word; /* deleted word */
+char *d_line; /* deleted line */
+char in_string[513]; /* buffer for reading a file */
+char *print_command = "lp"; /* string to use for the print command */
+char *start_at_line = NULL; /* move to this line at start of session*/
+int in; /* input character */
+
+FILE *temp_fp; /* temporary file pointer */
+FILE *bit_bucket; /* file pointer to /dev/null */
+
+char *table[] = {
+ "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "\t", "^J",
+ "^K", "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U",
+ "^V", "^W", "^X", "^Y", "^Z", "^[", "^\\", "^]", "^^", "^_"
+ };
+
+WINDOW *com_win;
+WINDOW *text_win;
+WINDOW *help_win;
+WINDOW *info_win;
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define P_(s) s
+#else
+#define P_(s) ()
+#endif
+
+
+/*
+ | The following structure allows menu items to be flexibly declared.
+ | The first item is the string describing the selection, the second
+ | is the address of the procedure to call when the item is selected,
+ | and the third is the argument for the procedure.
+ |
+ | For those systems with i18n, the string should be accompanied by a
+ | catalog number. The 'int *' should be replaced with 'void *' on
+ | systems with that type.
+ |
+ | The first menu item will be the title of the menu, with NULL
+ | parameters for the procedure and argument, followed by the menu items.
+ |
+ | If the procedure value is NULL, the menu item is displayed, but no
+ | procedure is called when the item is selected. The number of the
+ | item will be returned. If the third (argument) parameter is -1, no
+ | argument is given to the procedure when it is called.
+ */
+
+struct menu_entries {
+ char *item_string;
+ int (*procedure)P_((struct menu_entries *));
+ struct menu_entries *ptr_argument;
+ int (*iprocedure)P_((int));
+ void (*nprocedure)P_((void));
+ unsigned int argument;
+ };
+
+int main P_((int argc, char *argv[]));
+char *resiz_line P_((int factor, struct text *rline, int rpos));
+void insert P_((int character));
+void delete P_((int disp));
+void scanline P_((char *pos));
+int tabshift P_((int temp_int));
+int out_char P_((WINDOW *window, int character, int column));
+int len_char P_((int character, int column));
+void draw_line P_((int vertical, int horiz, char *ptr, int t_pos, int length));
+void insert_line P_((int disp));
+struct text *txtalloc P_((void));
+struct files *name_alloc P_((void));
+char *next_word P_((char *string));
+void prev_word P_((void));
+void control P_((void));
+void emacs_control P_((void));
+void bottom P_((void));
+void top P_((void));
+void nextline P_((void));
+void prevline P_((void));
+void left P_((int disp));
+void right P_((int disp));
+void find_pos P_((void));
+void up P_((void));
+void down P_((void));
+void function_key P_((void));
+void print_buffer P_((void));
+void command_prompt P_((void));
+void command P_((char *cmd_str1));
+int scan P_((char *line, int offset, int column));
+char *get_string P_((char *prompt, int advance));
+int compare P_((char *string1, char *string2, int sensitive));
+void goto_line P_((char *cmd_str));
+void midscreen P_((int line, char *pnt));
+void get_options P_((int numargs, char *arguments[]));
+void check_fp P_((void));
+void get_file P_((char *file_name));
+void get_line P_((int length, char *in_string, int *append));
+void draw_screen P_((void));
+void finish P_((void));
+int quit P_((int noverify));
+void edit_abort P_((int arg));
+void delete_text P_((void));
+int write_file P_((char *file_name));
+int search P_((int display_message));
+void search_prompt P_((void));
+void del_char P_((void));
+void undel_char P_((void));
+void del_word P_((void));
+void undel_word P_((void));
+void del_line P_((void));
+void undel_line P_((void));
+void adv_word P_((void));
+void move_rel P_((char *direction, int lines));
+void eol P_((void));
+void bol P_((void));
+void adv_line P_((void));
+void sh_command P_((char *string));
+void set_up_term P_((void));
+void resize_check P_((void));
+int menu_op P_((struct menu_entries *));
+void help P_((void));
+void paint_info_win P_((void));
+void no_info_window P_((void));
+void create_info_window P_((void));
+int file_op P_((int arg));
+void shell_op P_((void));
+void leave_op P_((void));
+void redraw P_((void));
+int Blank_Line P_((struct text *test_line));
+void Format P_((void));
+void ee_init P_((void));
+void echo_string P_((char *string));
+void spell_op P_((void));
+void ispell_op P_((void));
+int first_word_len P_((struct text *test_line));
+void Auto_Format P_((void));
+void modes_op P_((void));
+char *is_in_string P_((char *string, char *substring));
+char *resolve_name P_((char *name));
+int restrict_mode P_((void));
+int unique_test P_((char *string, char *list[]));
+void strings_init P_((void));
+
+#undef P_
+/*
+ | allocate space here for the strings that will be in the menu
+ */
+
+struct menu_entries modes_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+char *mode_strings[9];
+
+#define NUM_MODES_ITEMS 9
+
+struct menu_entries leave_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, finish, -1},
+ {"", NULL, NULL, quit, NULL, TRUE},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+#define READ_FILE 1
+#define WRITE_FILE 2
+#define SAVE_FILE 3
+
+struct menu_entries file_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, file_op, NULL, READ_FILE},
+ {"", NULL, NULL, file_op, NULL, WRITE_FILE},
+ {"", NULL, NULL, file_op, NULL, SAVE_FILE},
+ {"", NULL, NULL, NULL, print_buffer, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries search_menu[] = {
+ {"", NULL, NULL, NULL, NULL, 0},
+ {"", NULL, NULL, NULL, search_prompt, -1},
+ {"", NULL, NULL, search, NULL, TRUE},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries spell_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, spell_op, -1},
+ {"", NULL, NULL, NULL, ispell_op, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries misc_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, Format, -1},
+ {"", NULL, NULL, NULL, shell_op, -1},
+ {"", menu_op, spell_menu, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+struct menu_entries main_menu[] = {
+ {"", NULL, NULL, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, leave_op, -1},
+ {"", NULL, NULL, NULL, help, -1},
+ {"", menu_op, file_menu, NULL, NULL, -1},
+ {"", NULL, NULL, NULL, redraw, -1},
+ {"", NULL, NULL, NULL, modes_op, -1},
+ {"", menu_op, search_menu, NULL, NULL, -1},
+ {"", menu_op, misc_menu, NULL, NULL, -1},
+ {NULL, NULL, NULL, NULL, NULL, -1}
+ };
+
+char *help_text[22];
+char *control_keys[5];
+
+char *emacs_help_text[22];
+char *emacs_control_keys[5];
+
+char *command_strings[5];
+char *commands[30];
+char *init_strings[20];
+
+/*
+ | Declarations for strings for localization
+ */
+
+char *com_win_message; /* to be shown in com_win if no info window */
+char *no_file_string;
+char *ascii_code_str;
+char *printer_msg_str;
+char *command_str;
+char *file_write_prompt_str;
+char *file_read_prompt_str;
+char *char_str;
+char *unkn_cmd_str;
+char *non_unique_cmd_msg;
+char *line_num_str;
+char *line_len_str;
+char *current_file_str;
+char *usage0;
+char *usage1;
+char *usage2;
+char *usage3;
+char *usage4;
+char *file_is_dir_msg;
+char *new_file_msg;
+char *cant_open_msg;
+char *open_file_msg;
+char *file_read_fin_msg;
+char *reading_file_msg;
+char *read_only_msg;
+char *file_read_lines_msg;
+char *save_file_name_prompt;
+char *file_not_saved_msg;
+char *changes_made_prompt;
+char *yes_char;
+char *file_exists_prompt;
+char *create_file_fail_msg;
+char *writing_file_msg;
+char *file_written_msg;
+char *searching_msg;
+char *str_not_found_msg;
+char *search_prompt_str;
+char *exec_err_msg;
+char *continue_msg;
+char *menu_cancel_msg;
+char *menu_size_err_msg;
+char *press_any_key_msg;
+char *shell_prompt;
+char *formatting_msg;
+char *shell_echo_msg;
+char *spell_in_prog_msg;
+char *margin_prompt;
+char *restricted_msg;
+char *ON;
+char *OFF;
+char *HELP;
+char *WRITE;
+char *READ;
+char *LINE;
+char *FILE_str;
+char *CHARACTER;
+char *REDRAW;
+char *RESEQUENCE;
+char *AUTHOR;
+char *VERSION;
+char *CASE;
+char *NOCASE;
+char *EXPAND;
+char *NOEXPAND;
+char *Exit_string;
+char *QUIT_string;
+char *INFO;
+char *NOINFO;
+char *MARGINS;
+char *NOMARGINS;
+char *AUTOFORMAT;
+char *NOAUTOFORMAT;
+char *Echo;
+char *PRINTCOMMAND;
+char *RIGHTMARGIN;
+char *HIGHLIGHT;
+char *NOHIGHLIGHT;
+char *EIGHTBIT;
+char *NOEIGHTBIT;
+char *EMACS_string;
+char *NOEMACS_string;
+
+#ifndef __STDC__
+#ifndef HAS_STDLIB
+extern char *malloc();
+extern char *realloc();
+extern char *getenv();
+FILE *fopen(); /* declaration for open function */
+#endif /* HAS_STDLIB */
+#endif /* __STDC__ */
+
+int
+main(argc, argv) /* beginning of main program */
+int argc;
+char *argv[];
+{
+ int counter;
+
+ for (counter = 1; counter < 24; counter++)
+ signal(counter, SIG_IGN);
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGINT, edit_abort);
+
+ d_char = 0;
+ d_word = malloc(150);
+ *d_word = (char) NULL;
+ d_line = NULL;
+ dlt_line = txtalloc();
+ dlt_line->line = d_line;
+ curr_line = first_line = txtalloc();
+ curr_line->line = point = malloc(10);
+ curr_line->line_length = 1;
+ curr_line->max_length = 10;
+ curr_line->prev_line = NULL;
+ curr_line->next_line = NULL;
+ curr_line->line_number = 1;
+ srch_str = NULL;
+ u_srch_str = NULL;
+ position = 1;
+ scr_pos =0;
+ scr_vert = 0;
+ scr_horz = 0;
+ bit_bucket = fopen("/dev/null", "w");
+ edit = TRUE;
+ gold = case_sen = FALSE;
+ shell_fork = TRUE;
+ strings_init();
+ ee_init();
+ if (argc > 0 )
+ get_options(argc, argv);
+ set_up_term();
+ if (right_margin == 0)
+ right_margin = COLS - 1;
+ if (top_of_stack == NULL)
+ {
+ wprintw(com_win, no_file_string);
+ wrefresh(com_win);
+ }
+ else
+ check_fp();
+
+ clear_com_win = TRUE;
+
+ while(edit)
+ {
+ wrefresh(text_win);
+ in = wgetch(text_win);
+ if (in == -1)
+ exit(0);
+
+ resize_check();
+
+ if (clear_com_win)
+ {
+ clear_com_win = FALSE;
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ if (!info_window)
+ {
+ wprintw(com_win, "%s", com_win_message);
+ }
+ wrefresh(com_win);
+ }
+
+ if (in > 255)
+ function_key();
+ else if ((in == '\10') || (in == 127))
+ delete(TRUE);
+ else if ((in > 31) || (in == 9))
+ insert(in);
+ else if ((in >= 0) && (in <= 31))
+ {
+ if (emacs_keys_mode)
+ emacs_control();
+ else
+ control();
+ }
+ }
+ return(0);
+}
+
+char *
+resiz_line(factor, rline, rpos) /* resize the line to length + factor*/
+int factor; /* resize factor */
+struct text *rline; /* position in line */
+int rpos;
+{
+ char *rpoint;
+ int resiz_var;
+
+ rline->max_length += factor;
+ rpoint = rline->line = realloc(rline->line, rline->max_length );
+ for (resiz_var = 1 ; (resiz_var < rpos) ; resiz_var++)
+ rpoint++;
+ return(rpoint);
+}
+
+void
+insert(character) /* insert character into line */
+int character; /* new character */
+{
+ int counter;
+ int value;
+ char *temp; /* temporary pointer */
+ char *temp2; /* temporary pointer */
+
+ if ((character == '\011') && (expand_tabs))
+ {
+ counter = len_char('\011', scr_horz);
+ for (; counter > 0; counter--)
+ insert(' ');
+ if (auto_format)
+ Auto_Format();
+ return;
+ }
+ text_changes = TRUE;
+ if ((curr_line->max_length - curr_line->line_length) < 5)
+ point = resiz_line(10, curr_line, position);
+ curr_line->line_length++;
+ temp = point;
+ counter = position;
+ while (counter < curr_line->line_length) /* find end of line */
+ {
+ counter++;
+ temp++;
+ }
+ temp++; /* increase length of line by one */
+ while (point < temp)
+ {
+ temp2=temp - 1;
+ *temp= *temp2; /* shift characters over by one */
+ temp--;
+ }
+ *point = character; /* insert new character */
+ wclrtoeol(text_win);
+ if (((character >= 0) && (character < ' ')) || (character >= 127)) /* check for TAB character*/
+ {
+ scr_pos = scr_horz += out_char(text_win, character, scr_horz);
+ point++;
+ position++;
+ }
+ else
+ {
+ waddch(text_win, character);
+ scr_pos = ++scr_horz;
+ point++;
+ position ++;
+ }
+
+ if ((observ_margins) && (right_margin < scr_pos))
+ {
+ counter = position;
+ while (scr_pos > right_margin)
+ prev_word();
+ if (scr_pos == 0)
+ {
+ while (position < counter)
+ right(TRUE);
+ }
+ else
+ {
+ counter -= position;
+ insert_line(TRUE);
+ for (value = 0; value < counter; value++)
+ right(TRUE);
+ }
+ }
+
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset += 8;
+ midscreen(scr_vert, point);
+ }
+
+ if ((auto_format) && (character == ' ') && (!formatted))
+ Auto_Format();
+ else if ((character != ' ') && (character != '\t'))
+ formatted = FALSE;
+
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+}
+
+void
+delete(disp) /* delete character */
+int disp;
+{
+ char *tp;
+ char *temp2;
+ struct text *temp_buff;
+ int temp_vert;
+ int temp_pos;
+
+ if (point != curr_line->line) /* if not at beginning of line */
+ {
+ text_changes = TRUE;
+ temp2 = tp = point;
+ tp--;
+ point--;
+ if ((*tp >= '\000') && (*tp < ' ')) /* check for TAB */
+ scanline(tp);
+ else
+ --scr_horz;
+ scr_pos = scr_horz;
+ if (in == 8)
+ d_char = *point; /* save deleted character */
+ temp_pos = --position;
+ curr_line->line_length--;
+ while (temp_pos <= curr_line->line_length)
+ {
+ temp_pos++;
+ *tp= *temp2;
+ tp++;
+ temp2++;
+ }
+ if (scr_horz < horiz_offset)
+ {
+ horiz_offset -= 8;
+ midscreen(scr_vert, point);
+ }
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ text_changes = TRUE;
+ left(disp); /* go to previous line */
+ temp_buff = curr_line->next_line;
+ point = resiz_line(temp_buff->line_length, curr_line, position);
+ if (temp_buff->next_line != NULL)
+ temp_buff->next_line->prev_line = curr_line;
+ curr_line->next_line = temp_buff->next_line;
+ temp2 = temp_buff->line;
+ if (in == 8)
+ d_char = '\n';
+ tp = point;
+ temp_pos = 1;
+ while (temp_pos < temp_buff->line_length)
+ {
+ curr_line->line_length++;
+ temp_pos++;
+ *tp = *temp2;
+ tp++;
+ temp2++;
+ }
+ *tp = (char) NULL;
+ free(temp_buff->line);
+ free(temp_buff);
+ temp_buff = curr_line;
+ temp_vert = scr_vert;
+ scr_pos = scr_horz;
+ if (scr_vert < last_line)
+ {
+ wmove(text_win, scr_vert + 1, 0);
+ wdeleteln(text_win);
+ }
+ while ((temp_buff != NULL) && (temp_vert < last_line))
+ {
+ temp_buff = temp_buff->next_line;
+ temp_vert++;
+ }
+ if ((temp_vert == last_line) && (temp_buff != NULL))
+ {
+ tp = temp_buff->line;
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ draw_line(last_line, 0, tp, 1, temp_buff->line_length);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ }
+ }
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+ formatted = FALSE;
+}
+
+void
+scanline(pos) /* find the proper horizontal position for the pointer */
+char *pos;
+{
+ int temp;
+ char *ptr;
+
+ ptr = curr_line->line;
+ temp = 0;
+ while (ptr < pos)
+ {
+ if ((*ptr >= 0) && (*ptr <= 8))
+ temp += 2;
+ else if (*ptr == 9)
+ temp += tabshift(temp);
+ else if ((*ptr >= 10) && (*ptr <= 31))
+ temp += 2;
+ else if ((*ptr >= 32) && (*ptr < 127))
+ temp++;
+ else if (*ptr == 127)
+ temp += 2;
+ else if (!eightbit)
+ temp += 5;
+ else
+ temp++;
+ ptr++;
+ }
+ scr_horz = temp;
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset = (scr_horz - (scr_horz % 8)) - (COLS - 8);
+ midscreen(scr_vert, point);
+ }
+ else if (scr_horz < horiz_offset)
+ {
+ horiz_offset = max(0, (scr_horz - (scr_horz % 8)));
+ midscreen(scr_vert, point);
+ }
+}
+
+int
+tabshift(temp_int) /* give the number of spaces to shift */
+int temp_int;
+{
+ int leftover;
+
+ leftover = ((temp_int + 1) % 8);
+ if (leftover == 0)
+ return (1);
+ else
+ return (9 - leftover);
+}
+
+int
+out_char(window, character, column) /* output non-printing character */
+WINDOW *window;
+char character;
+int column;
+{
+ int i1, i2;
+ char *string;
+ char string2[8];
+
+ if (character == TAB)
+ {
+ i1 = tabshift(column);
+ for (i2 = 0;
+ (i2 < i1) && (((column+i2+1)-horiz_offset) < last_col); i2++)
+ {
+ waddch(window, ' ');
+ }
+ return(i1);
+ }
+ else if ((character >= '\0') && (character < ' '))
+ {
+ string = table[(int) character];
+ }
+ else if ((character < 0) || (character >= 127))
+ {
+ if (character == 127)
+ string = "^?";
+ else if (!eightbit)
+ {
+ sprintf(string2, "<%d>", (character < 0) ? (character + 256) : character);
+ string = string2;
+ }
+ else
+ {
+ waddch(window, character & 0xFF);
+ return(1);
+ }
+ }
+ else
+ {
+ waddch(window, character);
+ return(1);
+ }
+ for (i2 = 0; (string[i2] != (char) NULL) && (((column+i2+1)-horiz_offset) < last_col); i2++)
+ waddch(window, string[i2]);
+ return(strlen(string));
+}
+
+int
+len_char(character, column) /* return the length of the character */
+char character;
+int column; /* the column must be known to provide spacing for tabs */
+{
+ int length;
+
+ if (character == '\t')
+ length = tabshift(column);
+ else if ((character >= 0) && (character < 32))
+ length = 2;
+ else if ((character >= 32) && (character <= 126))
+ length = 1;
+ else if (character == 127)
+ length = 2;
+ else if (((character > 126) || (character < 0)) && (!eightbit))
+ length = 5;
+ else
+ length = 1;
+
+ return(length);
+}
+
+void
+draw_line(vertical, horiz, ptr, t_pos, length) /* redraw line from current position */
+int vertical; /* current vertical position on screen */
+int horiz; /* current horizontal position on screen */
+char *ptr; /* pointer to line */
+int t_pos; /* current position (offset in bytes) from bol */
+int length; /* length (in bytes) of line */
+{
+ int d; /* partial length of special or tab char to display */
+ char *temp; /* temporary pointer to position in line */
+ int abs_column; /* offset in screen units from begin of line */
+ int column; /* horizontal position on screen */
+ int row; /* vertical position on screen */
+ int posit; /* temporary position indicator within line */
+
+ abs_column = horiz;
+ column = horiz - horiz_offset;
+ row = vertical;
+ temp = ptr;
+ d = 0;
+ posit = t_pos;
+ if (column < 0)
+ {
+ wmove(text_win, row, 0);
+ wclrtoeol(text_win);
+ }
+ while (column < 0)
+ {
+ d = len_char(*temp, abs_column);
+ abs_column += d;
+ column += d;
+ posit++;
+ temp++;
+ }
+ wmove(text_win, row, column);
+ wclrtoeol(text_win);
+ while ((posit < length) && (column <= last_col))
+ {
+ if ((*temp < 32) || (*temp == 127))
+ {
+ column += len_char(*temp, abs_column);
+ abs_column += out_char(text_win, *temp, abs_column);
+ }
+ else
+ {
+ abs_column++;
+ column++;
+ waddch(text_win, *temp);
+ }
+ posit++;
+ temp++;
+ }
+ if (column < last_col)
+ wclrtoeol(text_win);
+ wmove(text_win, vertical, (horiz - horiz_offset));
+}
+
+void
+insert_line(disp) /* insert new line */
+int disp;
+{
+ int temp_pos;
+ int temp_pos2;
+ char *temp;
+ char *extra;
+ struct text *temp_nod;
+
+ text_changes = TRUE;
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wclrtoeol(text_win);
+ temp_nod= txtalloc();
+ temp_nod->line = extra= malloc(10);
+ temp_nod->line_length = 1;
+ temp_nod->max_length = 10;
+ temp_nod->line_number = curr_line->line_number + 1;
+ temp_nod->next_line = curr_line->next_line;
+ if (temp_nod->next_line != NULL)
+ temp_nod->next_line->prev_line = temp_nod;
+ temp_nod->prev_line = curr_line;
+ curr_line->next_line = temp_nod;
+ temp_pos2 = position;
+ temp = point;
+ if (temp_pos2 < curr_line->line_length)
+ {
+ temp_pos = 1;
+ while (temp_pos2 < curr_line->line_length)
+ {
+ if ((temp_nod->max_length - temp_nod->line_length)< 5)
+ extra = resiz_line(10, temp_nod, temp_pos);
+ temp_nod->line_length++;
+ temp_pos++;
+ temp_pos2++;
+ *extra= *temp;
+ extra++;
+ temp++;
+ }
+ temp=point;
+ *temp = (char) NULL;
+ temp = resiz_line((1 - temp_nod->line_length), curr_line, position);
+ curr_line->line_length = 1 + temp - curr_line->line;
+ }
+ curr_line->line_length = position;
+ curr_line = temp_nod;
+ *extra = (char) NULL;
+ position = 1;
+ point= curr_line->line;
+ if (disp)
+ {
+ if (scr_vert < last_line)
+ {
+ scr_vert++;
+ wclrtoeol(text_win);
+ wmove(text_win, scr_vert, 0);
+ winsertln(text_win);
+ }
+ else
+ {
+ wmove(text_win, 0,0);
+ wdeleteln(text_win);
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ }
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ draw_line(scr_vert, scr_horz, point, position,
+ curr_line->line_length);
+ }
+}
+
+struct text *txtalloc() /* allocate space for line structure */
+{
+ return((struct text *) malloc(sizeof( struct text)));
+}
+
+struct files *name_alloc() /* allocate space for file name list node */
+{
+ return((struct files *) malloc(sizeof( struct files)));
+}
+
+char *next_word(string) /* move to next word in string */
+char *string;
+{
+ while ((*string != (char) NULL) && ((*string != 32) && (*string != 9)))
+ string++;
+ while ((*string != (char) NULL) && ((*string == 32) || (*string == 9)))
+ string++;
+ return(string);
+}
+
+void
+prev_word() /* move to start of previous word in text */
+{
+ if (position != 1)
+ {
+ if ((position != 1) && ((point[-1] == ' ') || (point[-1] == '\t')))
+ { /* if at the start of a word */
+ while ((position != 1) && ((*point != ' ') && (*point != '\t')))
+ left(TRUE);
+ }
+ while ((position != 1) && ((*point == ' ') || (*point == '\t')))
+ left(TRUE);
+ while ((position != 1) && ((*point != ' ') && (*point != '\t')))
+ left(TRUE);
+ if ((position != 1) && ((*point == ' ') || (*point == '\t')))
+ right(TRUE);
+ }
+ else
+ left(TRUE);
+}
+
+void
+control() /* use control for commands */
+{
+ char *string;
+
+ if (in == 1) /* control a */
+ {
+ string = get_string(ascii_code_str, TRUE);
+ if (*string != (char) NULL)
+ {
+ in = atoi(string);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ insert(in);
+ }
+ free(string);
+ }
+ else if (in == 2) /* control b */
+ bottom();
+ else if (in == 3) /* control c */
+ {
+ command_prompt();
+ }
+ else if (in == 4) /* control d */
+ down();
+ else if (in == 5) /* control e */
+ search_prompt();
+ else if (in == 6) /* control f */
+ undel_char();
+ else if (in == 7) /* control g */
+ bol();
+ else if (in == 8) /* control h */
+ delete(TRUE);
+ else if (in == 9) /* control i */
+ ;
+ else if (in == 10) /* control j */
+ insert_line(TRUE);
+ else if (in == 11) /* control k */
+ del_char();
+ else if (in == 12) /* control l */
+ left(TRUE);
+ else if (in == 13) /* control m */
+ insert_line(TRUE);
+ else if (in == 14) /* control n */
+ move_rel("d", max(5, (last_line - 5)));
+ else if (in == 15) /* control o */
+ eol();
+ else if (in == 16) /* control p */
+ move_rel("u", max(5, (last_line - 5)));
+ else if (in == 17) /* control q */
+ ;
+ else if (in == 18) /* control r */
+ right(TRUE);
+ else if (in == 19) /* control s */
+ ;
+ else if (in == 20) /* control t */
+ top();
+ else if (in == 21) /* control u */
+ up();
+ else if (in == 22) /* control v */
+ undel_word();
+ else if (in == 23) /* control w */
+ del_word();
+ else if (in == 24) /* control x */
+ search(TRUE);
+ else if (in == 25) /* control y */
+ del_line();
+ else if (in == 26) /* control z */
+ undel_line();
+ else if (in == 27) /* control [ (escape) */
+ {
+ menu_op(main_menu);
+ }
+}
+
+/*
+ | Emacs control-key bindings
+ */
+
+void
+emacs_control()
+{
+ char *string;
+
+ if (in == 1) /* control a */
+ bol();
+ else if (in == 2) /* control b */
+ left(TRUE);
+ else if (in == 3) /* control c */
+ {
+ command_prompt();
+ }
+ else if (in == 4) /* control d */
+ del_char();
+ else if (in == 5) /* control e */
+ eol();
+ else if (in == 6) /* control f */
+ right(TRUE);
+ else if (in == 7) /* control g */
+ move_rel("u", max(5, (last_line - 5)));
+ else if (in == 8) /* control h */
+ delete(TRUE);
+ else if (in == 9) /* control i */
+ ;
+ else if (in == 10) /* control j */
+ undel_char();
+ else if (in == 11) /* control k */
+ del_line();
+ else if (in == 12) /* control l */
+ undel_line();
+ else if (in == 13) /* control m */
+ insert_line(TRUE);
+ else if (in == 14) /* control n */
+ down();
+ else if (in == 15) /* control o */
+ {
+ string = get_string(ascii_code_str, TRUE);
+ if (*string != (char) NULL)
+ {
+ in = atoi(string);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ insert(in);
+ }
+ free(string);
+ }
+ else if (in == 16) /* control p */
+ up();
+ else if (in == 17) /* control q */
+ ;
+ else if (in == 18) /* control r */
+ undel_word();
+ else if (in == 19) /* control s */
+ ;
+ else if (in == 20) /* control t */
+ top();
+ else if (in == 21) /* control u */
+ bottom();
+ else if (in == 22) /* control v */
+ move_rel("d", max(5, (last_line - 5)));
+ else if (in == 23) /* control w */
+ del_word();
+ else if (in == 24) /* control x */
+ search(TRUE);
+ else if (in == 25) /* control y */
+ search_prompt();
+ else if (in == 26) /* control z */
+ adv_word();
+ else if (in == 27) /* control [ (escape) */
+ {
+ menu_op(main_menu);
+ }
+}
+
+void
+bottom() /* go to bottom of file */
+{
+ while (curr_line->next_line != NULL)
+ curr_line = curr_line->next_line;
+ point = curr_line->line;
+ if (horiz_offset)
+ horiz_offset = 0;
+ position = 1;
+ midscreen(last_line, point);
+ scr_pos = scr_horz;
+}
+
+void
+top() /* go to top of file */
+{
+ while (curr_line->prev_line != NULL)
+ curr_line = curr_line->prev_line;
+ point = curr_line->line;
+ if (horiz_offset)
+ horiz_offset = 0;
+ position = 1;
+ midscreen(0, point);
+ scr_pos = scr_horz;
+}
+
+void
+nextline() /* move pointers to start of next line */
+{
+ curr_line = curr_line->next_line;
+ point = curr_line->line;
+ position = 1;
+ if (scr_vert == last_line)
+ {
+ wmove(text_win, 0,0);
+ wdeleteln(text_win);
+ wmove(text_win, last_line,0);
+ wclrtobot(text_win);
+ draw_line(last_line,0,point,1,curr_line->line_length);
+ }
+ else
+ scr_vert++;
+}
+
+void
+prevline() /* move pointers to start of previous line*/
+{
+ curr_line = curr_line->prev_line;
+ point = curr_line->line;
+ position = 1;
+ if (scr_vert == 0)
+ {
+ winsertln(text_win);
+ draw_line(0,0,point,1,curr_line->line_length);
+ }
+ else
+ scr_vert--;
+ while (position < curr_line->line_length)
+ {
+ position++;
+ point++;
+ }
+}
+
+void
+left(disp) /* move left one character */
+int disp;
+{
+ if (point != curr_line->line) /* if not at begin of line */
+ {
+ point--;
+ position--;
+ scanline(point);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ scr_pos = scr_horz;
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ if (!disp)
+ {
+ curr_line = curr_line->prev_line;
+ point = curr_line->line + curr_line->line_length;
+ position = curr_line->line_length;
+ return;
+ }
+ position = 1;
+ prevline();
+ scanline(point);
+ scr_pos = scr_horz;
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ }
+}
+
+void
+right(disp) /* move right one character */
+int disp;
+{
+ if (position < curr_line->line_length)
+ {
+ point++;
+ position++;
+ scanline(point);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ scr_pos = scr_horz;
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ if (!disp)
+ {
+ curr_line = curr_line->next_line;
+ point = curr_line->line;
+ position = 1;
+ return;
+ }
+ nextline();
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ position = 1;
+ }
+}
+
+void
+find_pos() /* move to the same column as on other line */
+{
+ scr_horz = 0;
+ position = 1;
+ while ((scr_horz < scr_pos) && (position < curr_line->line_length))
+ {
+ if (*point == 9)
+ scr_horz += tabshift(scr_horz);
+ else if ((*point >= '\0') && (*point < ' '))
+ scr_horz += 2;
+ else
+ scr_horz++;
+ position++;
+ point++;
+ }
+ if ((scr_horz - horiz_offset) > last_col)
+ {
+ horiz_offset = (scr_horz - (scr_horz % 8)) - (COLS - 8);
+ midscreen(scr_vert, point);
+ }
+ else if (scr_horz < horiz_offset)
+ {
+ horiz_offset = max(0, (scr_horz - (scr_horz % 8)));
+ midscreen(scr_vert, point);
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+up() /* move up one line */
+{
+ if (curr_line->prev_line != NULL)
+ {
+ prevline();
+ point = curr_line->line;
+ find_pos();
+ }
+}
+
+void
+down() /* move down one line */
+{
+ if (curr_line->next_line != NULL)
+ {
+ nextline();
+ find_pos();
+ }
+}
+
+void
+function_key() /* process function key */
+{
+ if (in == KEY_LEFT)
+ left(TRUE);
+ else if (in == KEY_RIGHT)
+ right(TRUE);
+ else if ( in == KEY_HOME)
+ top();
+ else if ( in == KEY_UP)
+ up();
+ else if (in == KEY_DOWN)
+ down();
+ else if (in == KEY_NPAGE)
+ move_rel("d", max( 5, (last_line - 5)));
+ else if (in == KEY_PPAGE)
+ move_rel("u", max(5, (last_line - 5)));
+ else if (in == KEY_DL)
+ del_line();
+ else if (in == KEY_DC)
+ del_char();
+ else if (in == KEY_BACKSPACE)
+ delete(TRUE);
+ else if (in == KEY_IL)
+ { /* insert a line before current line */
+ insert_line(TRUE);
+ left(TRUE);
+ }
+ else if (in == KEY_F(1))
+ gold = !gold;
+ else if (in == KEY_F(2))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ undel_line();
+ }
+ else
+ undel_char();
+ }
+ else if (in == KEY_F(3))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ undel_word();
+ }
+ else
+ del_word();
+ }
+ else if (in == KEY_F(4))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ paint_info_win();
+ midscreen(scr_vert, point);
+ }
+ else
+ adv_word();
+ }
+ else if (in == KEY_F(5))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ search_prompt();
+ }
+ else
+ search(TRUE);
+ }
+ else if (in == KEY_F(6))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ bottom();
+ }
+ else
+ top();
+ }
+ else if (in == KEY_F(7))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ eol();
+ }
+ else
+ bol();
+ }
+ else if (in == KEY_F(8))
+ {
+ if (gold)
+ {
+ gold = FALSE;
+ command_prompt();
+ }
+ else
+ adv_line();
+ }
+}
+
+void
+print_buffer()
+{
+ char buffer[256];
+
+ sprintf(buffer, ">!%s", print_command);
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, printer_msg_str, print_command);
+ wrefresh(com_win);
+ command(buffer);
+}
+
+void
+command_prompt()
+{
+ char *cmd_str;
+ int result;
+
+ info_type = COMMANDS;
+ paint_info_win();
+ cmd_str = get_string(command_str, TRUE);
+ if ((result = unique_test(cmd_str, commands)) != 1)
+ {
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ if (result == 0)
+ wprintw(com_win, unkn_cmd_str, cmd_str);
+ else
+ wprintw(com_win, non_unique_cmd_msg);
+
+ wrefresh(com_win);
+
+ info_type = CONTROL_KEYS;
+ paint_info_win();
+
+ if (cmd_str != NULL)
+ free(cmd_str);
+ return;
+ }
+ command(cmd_str);
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ info_type = CONTROL_KEYS;
+ paint_info_win();
+ if (cmd_str != NULL)
+ free(cmd_str);
+}
+
+void
+command(cmd_str1) /* process commands from keyboard */
+char *cmd_str1;
+{
+ char *cmd_str2 = NULL;
+ char *cmd_str = cmd_str1;
+
+ clear_com_win = TRUE;
+ if (compare(cmd_str, HELP, FALSE))
+ help();
+ else if (compare(cmd_str, WRITE, FALSE))
+ {
+ if (restrict_mode())
+ {
+ return;
+ }
+ cmd_str = next_word(cmd_str);
+ if (*cmd_str == (char) NULL)
+ {
+ cmd_str = cmd_str2 = get_string(file_write_prompt_str, TRUE);
+ }
+ tmp_file = resolve_name(cmd_str);
+ write_file(tmp_file);
+ if (tmp_file != cmd_str)
+ free(tmp_file);
+ }
+ else if (compare(cmd_str, READ, FALSE))
+ {
+ if (restrict_mode())
+ {
+ return;
+ }
+ cmd_str = next_word(cmd_str);
+ if (*cmd_str == (char) NULL)
+ {
+ cmd_str = cmd_str2 = get_string(file_read_prompt_str, TRUE);
+ }
+ tmp_file = cmd_str;
+ recv_file = TRUE;
+ tmp_file = resolve_name(cmd_str);
+ check_fp();
+ if (tmp_file != cmd_str)
+ free(tmp_file);
+ }
+ else if (compare(cmd_str, LINE, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, line_num_str, curr_line->line_number);
+ wprintw(com_win, line_len_str, curr_line->line_length);
+ }
+ else if (compare(cmd_str, FILE_str, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (in_file_name == NULL)
+ wprintw(com_win, no_file_string);
+ else
+ wprintw(com_win, current_file_str, in_file_name);
+ }
+ else if ((*cmd_str >= '0') && (*cmd_str <= '9'))
+ goto_line(cmd_str);
+ else if (compare(cmd_str, CHARACTER, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (*point >= '\0')
+ wprintw(com_win, char_str, *point);
+ else
+ wprintw(com_win, char_str, (*point + 256));
+ }
+ else if (compare(cmd_str, REDRAW, FALSE))
+ redraw();
+ else if (compare(cmd_str, RESEQUENCE, FALSE))
+ {
+ tmp_line = first_line->next_line;
+ while (tmp_line != NULL)
+ {
+ tmp_line->line_number = tmp_line->prev_line->line_number + 1;
+ tmp_line = tmp_line->next_line;
+ }
+ }
+ else if (compare(cmd_str, AUTHOR, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, "written by Hugh Mahon");
+ }
+ else if (compare(cmd_str, VERSION, FALSE))
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, "%s", version);
+ }
+ else if (compare(cmd_str, CASE, FALSE))
+ case_sen = TRUE;
+ else if (compare(cmd_str, NOCASE, FALSE))
+ case_sen = FALSE;
+ else if (compare(cmd_str, EXPAND, FALSE))
+ expand_tabs = TRUE;
+ else if (compare(cmd_str, NOEXPAND, FALSE))
+ expand_tabs = FALSE;
+ else if (compare(cmd_str, Exit_string, FALSE))
+ finish();
+ else if (compare(cmd_str, QUIT_string, FALSE))
+ quit(0);
+ else if (*cmd_str == '!')
+ {
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == 9))
+ cmd_str = next_word(cmd_str);
+ sh_command(cmd_str);
+ }
+ else if ((*cmd_str == '<') && (!in_pipe))
+ {
+ in_pipe = TRUE;
+ shell_fork = FALSE;
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == '\t'))
+ cmd_str = next_word(cmd_str);
+ command(cmd_str);
+ in_pipe = FALSE;
+ shell_fork = TRUE;
+ }
+ else if ((*cmd_str == '>') && (!out_pipe))
+ {
+ out_pipe = TRUE;
+ cmd_str++;
+ if ((*cmd_str == ' ') || (*cmd_str == '\t'))
+ cmd_str = next_word(cmd_str);
+ command(cmd_str);
+ out_pipe = FALSE;
+ }
+ else
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, unkn_cmd_str, cmd_str);
+ }
+ if (cmd_str2 != NULL)
+ free(cmd_str2);
+}
+
+int
+scan(line, offset, column) /* determine horizontal position for get_string */
+char *line;
+int offset;
+int column;
+{
+ char *stemp;
+ int i;
+ int j;
+
+ stemp = line;
+ i = 0;
+ j = column;
+ while (i < offset)
+ {
+ i++;
+ j += len_char(*stemp, j);
+ stemp++;
+ }
+ return(j);
+}
+
+char *
+get_string(prompt, advance) /* read string from input on command line */
+char *prompt; /* string containing user prompt message */
+int advance; /* if true, skip leading spaces and tabs */
+{
+ char *string;
+ char *tmp_string;
+ char *nam_str;
+ char *g_point;
+ int tmp_int;
+ int g_horz, g_position, g_pos;
+ int esc_flag;
+
+ g_point = tmp_string = malloc(512);
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ waddstr(com_win, prompt);
+ wrefresh(com_win);
+ nam_str = tmp_string;
+ clear_com_win = TRUE;
+ g_horz = g_position = scan(prompt, strlen(prompt), 0);
+ g_pos = 0;
+ keypad(com_win, FALSE);
+ do
+ {
+ esc_flag = FALSE;
+ in = wgetch(com_win);
+ if (in == -1)
+ exit(0);
+ if (((in == 8) || (in == 127)) && (g_pos > 0))
+ {
+ tmp_int = g_horz;
+ g_pos--;
+ g_horz = scan(g_point, g_pos, g_position);
+ tmp_int = tmp_int - g_horz;
+ for (; 0 < tmp_int; tmp_int--)
+ {
+ if ((g_horz+tmp_int) < (last_col - 1))
+ {
+ waddch(com_win, '\010');
+ waddch(com_win, ' ');
+ waddch(com_win, '\010');
+ }
+ }
+ nam_str--;
+ }
+ else if ((in != 8) && (in != 127) && (in != '\n') && (in != '\r'))
+ {
+ if (in == '\026') /* control-v, accept next character verbatim */
+ { /* allows entry of ^m, ^j, and ^h */
+ esc_flag = TRUE;
+ in = wgetch(com_win);
+ if (in == -1)
+ exit(0);
+ }
+ *nam_str = in;
+ g_pos++;
+ if (((in < ' ') || (in > 126)) && (g_horz < (last_col - 1)))
+ g_horz += out_char(com_win, in, g_horz);
+ else
+ {
+ g_horz++;
+ if (g_horz < (last_col - 1))
+ waddch(com_win, in);
+ }
+ nam_str++;
+ }
+ wrefresh(com_win);
+ if (esc_flag)
+ in = (char) NULL;
+ } while ((in != '\n') && (in != '\r'));
+ keypad(com_win, TRUE);
+ *nam_str = (char) NULL;
+ nam_str = tmp_string;
+ if (((*nam_str == ' ') || (*nam_str == 9)) && (advance))
+ nam_str = next_word(nam_str);
+ string = malloc(strlen(nam_str) + 1);
+ strcpy(string, nam_str);
+ free(tmp_string);
+ wrefresh(com_win);
+ return(string);
+}
+
+int
+compare(string1, string2, sensitive) /* compare two strings */
+char *string1;
+char *string2;
+int sensitive;
+{
+ char *strng1;
+ char *strng2;
+ int tmp;
+ int equal;
+
+ strng1 = string1;
+ strng2 = string2;
+ tmp = 0;
+ if ((strng1 == NULL) || (strng2 == NULL) || (*strng1 == (char) NULL) || (*strng2 == (char) NULL))
+ return(FALSE);
+ equal = TRUE;
+ while (equal)
+ {
+ if (sensitive)
+ {
+ if (*strng1 != *strng2)
+ equal = FALSE;
+ }
+ else
+ {
+ if (toupper(*strng1) != toupper(*strng2))
+ equal = FALSE;
+ }
+ strng1++;
+ strng2++;
+ if ((*strng1 == (char) NULL) || (*strng2 == (char) NULL) || (*strng1 == ' ') || (*strng2 == ' '))
+ break;
+ tmp++;
+ }
+ return(equal);
+}
+
+void
+goto_line(cmd_str)
+char *cmd_str;
+{
+ int number;
+ int i;
+ char *ptr;
+ char *direction;
+ struct text *t_line;
+
+ ptr = cmd_str;
+ i= 0;
+ while ((*ptr >='0') && (*ptr <= '9'))
+ {
+ i= i * 10 + (*ptr - '0');
+ ptr++;
+ }
+ number = i;
+ i = 0;
+ t_line = curr_line;
+ while ((t_line->line_number > number) && (t_line->prev_line != NULL))
+ {
+ i++;
+ t_line = t_line->prev_line;
+ direction = "u";
+ }
+ while ((t_line->line_number < number) && (t_line->next_line != NULL))
+ {
+ i++;
+ direction = "d";
+ t_line = t_line->next_line;
+ }
+ if ((i < 30) && (i > 0))
+ {
+ move_rel(direction, i);
+ }
+ else
+ {
+ curr_line = t_line;
+ point = curr_line->line;
+ position = 1;
+ midscreen((last_line / 2), point);
+ scr_pos = scr_horz;
+ }
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, line_num_str, curr_line->line_number);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+midscreen(line, pnt) /* put current line in middle of screen */
+int line;
+char *pnt;
+{
+ struct text *mid_line;
+ int i;
+
+ line = min(line, last_line);
+ mid_line = curr_line;
+ for (i = 0; ((i < line) && (curr_line->prev_line != NULL)); i++)
+ curr_line = curr_line->prev_line;
+ scr_vert = scr_horz = 0;
+ wmove(text_win, 0, 0);
+ draw_screen();
+ scr_vert = i;
+ curr_line = mid_line;
+ scanline(pnt);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+get_options(numargs, arguments) /* get arguments from command line */
+int numargs;
+char *arguments[];
+{
+ char *buff;
+ int count;
+ struct files *temp_names;
+ char *name;
+ char *ptr;
+
+ /*
+ | see if editor was invoked as 'ree' (restricted mode)
+ */
+
+ if (!(name = strrchr(arguments[0], '/')))
+ name = arguments[0];
+ else
+ name++;
+ if (!strcmp(name, "ree"))
+ restricted = TRUE;
+
+ top_of_stack = NULL;
+ input_file = FALSE;
+ recv_file = FALSE;
+ count = 1;
+ while (count < numargs)
+ {
+ buff = arguments[count];
+ if (!strcmp("-i", buff))
+ {
+ info_window = FALSE;
+ }
+ else if (!strcmp("-e", buff))
+ {
+ expand_tabs = FALSE;
+ }
+ else if (!strcmp("-h", buff))
+ {
+ nohighlight = TRUE;
+ }
+ else if (!strcmp("-?", buff))
+ {
+ fprintf(stderr, usage0, arguments[0]);
+ fprintf(stderr, usage1);
+ fprintf(stderr, usage2);
+ fprintf(stderr, usage3);
+ fprintf(stderr, usage4);
+ exit(1);
+ }
+ else if (*buff == '+')
+ {
+ buff++;
+ start_at_line = buff;
+ }
+
+ else
+ {
+ if (top_of_stack == NULL)
+ {
+ temp_names = top_of_stack = name_alloc();
+ }
+ else
+ {
+ temp_names->next_name = name_alloc();
+ temp_names = temp_names->next_name;
+ }
+ ptr = temp_names->name = malloc(strlen(buff) + 1);
+ while (*buff != (char) NULL)
+ {
+ *ptr = *buff;
+ buff++;
+ ptr++;
+ }
+ *ptr = (char) NULL;
+ temp_names->next_name = NULL;
+ input_file = TRUE;
+ recv_file = TRUE;
+ }
+ count++;
+ }
+}
+
+void
+check_fp() /* open or close files according to flags */
+{
+ int line_num;
+ int temp;
+ struct stat buf;
+
+ clear_com_win = TRUE;
+ tmp_vert = scr_vert;
+ tmp_horz = scr_horz;
+ tmp_line = curr_line;
+ if (input_file)
+ {
+ in_file_name = tmp_file = top_of_stack->name;
+ top_of_stack = top_of_stack->next_name;
+ }
+ temp = stat(tmp_file, &buf);
+ buf.st_mode &= ~07777;
+ if ((temp != -1) && (buf.st_mode != 0100000) && (buf.st_mode != 0))
+ {
+ wprintw(com_win, file_is_dir_msg, tmp_file);
+ wrefresh(com_win);
+ if (input_file)
+ {
+ quit(0);
+ return;
+ }
+ else
+ return;
+ }
+ if ((get_fd = open(tmp_file, O_RDONLY)) == -1)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (input_file)
+ wprintw(com_win, new_file_msg, tmp_file);
+ else
+ wprintw(com_win, cant_open_msg, tmp_file);
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wrefresh(text_win);
+ recv_file = FALSE;
+ input_file = FALSE;
+ return;
+ }
+ else
+ get_file(tmp_file);
+
+ recv_file = FALSE;
+ line_num = curr_line->line_number;
+ scr_vert = tmp_vert;
+ scr_horz = tmp_horz;
+ if (input_file)
+ curr_line= first_line;
+ else
+ curr_line = tmp_line;
+ point = curr_line->line;
+ draw_screen();
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ if (input_file)
+ {
+ wprintw(com_win, open_file_msg, in_file_name, line_num);
+ input_file = FALSE;
+ if (start_at_line != NULL)
+ {
+ line_num = atoi(start_at_line) - 1;
+ move_rel("d", line_num);
+ line_num = 0;
+ start_at_line = NULL;
+ }
+ }
+ else
+ {
+ text_changes = TRUE;
+ if ((tmp_file != NULL) && (*tmp_file != (char) NULL))
+ wprintw(com_win, file_read_fin_msg, tmp_file);
+ }
+ wrefresh(com_win);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+ wrefresh(text_win);
+}
+
+void
+get_file(file_name) /* read specified file into current buffer */
+char *file_name;
+{
+ int can_read; /* file has at least one character */
+ int length; /* length of line read by read */
+ int append; /* should text be appended to current line */
+ struct text *temp_line;
+
+ if (recv_file) /* if reading a file */
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, reading_file_msg, file_name);
+ if (access(file_name, 2)) /* check permission to write */
+ {
+ if ((errno == ENOTDIR) || (errno == EACCES) || (errno == EROFS) || (errno == ETXTBSY) || (errno == EFAULT))
+ wprintw(com_win, read_only_msg);
+ }
+ wrefresh(com_win);
+ }
+ if (curr_line->line_length > 1) /* if current line is not blank */
+ {
+ insert_line(FALSE);
+ left(FALSE);
+ append = FALSE;
+ }
+ else
+ append = TRUE;
+ can_read = FALSE; /* test if file has any characters */
+ while (((length = read(get_fd, in_string, 512)) != 0) && (length != -1))
+ {
+ can_read = TRUE; /* if set file has at least 1 character */
+ get_line(length, in_string, &append);
+ }
+ if ((can_read) && (curr_line->line_length == 1))
+ {
+ temp_line = curr_line->prev_line;
+ temp_line->next_line = curr_line->next_line;
+ if (temp_line->next_line != NULL)
+ temp_line->next_line->prev_line = temp_line;
+ if (curr_line->line != NULL)
+ free(curr_line->line);
+ free(curr_line);
+ curr_line = temp_line;
+ }
+ if (input_file) /* if this is the file to be edited display number of lines */
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, file_read_lines_msg, in_file_name, curr_line->line_number);
+ wrefresh(com_win);
+ }
+ else if (can_read) /* not input_file and file is non-zero size */
+ text_changes = TRUE;
+
+ if (recv_file) /* if reading a file */
+ {
+ in = EOF;
+ }
+}
+
+void
+get_line(length, in_string, append) /* read string and split into lines */
+int length; /* length of string read by read */
+char *in_string; /* string read by read */
+int *append; /* TRUE if must append more text to end of current line */
+{
+ char *str1;
+ char *str2;
+ int num; /* offset from start of string */
+ int char_count; /* length of new line (or added portion */
+ int temp_counter; /* temporary counter value */
+ struct text *tline; /* temporary pointer to new line */
+ int first_time; /* if TRUE, the first time through the loop */
+
+ str2 = in_string;
+ num = 0;
+ first_time = TRUE;
+ while (num < length)
+ {
+ if (!first_time)
+ {
+ if (num < length)
+ {
+ str2++;
+ num++;
+ }
+ }
+ else
+ first_time = FALSE;
+ str1 = str2;
+ char_count = 1;
+ /* find end of line */
+ while ((*str2 != '\n') && (num < length))
+ {
+ str2++;
+ num++;
+ char_count++;
+ }
+ if (!(*append)) /* if not append to current line, insert new one */
+ {
+ tline = txtalloc(); /* allocate data structure for next line */
+ tline->line_number = curr_line->line_number + 1;
+ tline->next_line = curr_line->next_line;
+ tline->prev_line = curr_line;
+ curr_line->next_line = tline;
+ if (tline->next_line != NULL)
+ tline->next_line->prev_line = tline;
+ curr_line = tline;
+ curr_line->line = point = (char *) malloc(char_count);
+ curr_line->line_length = char_count;
+ curr_line->max_length = char_count;
+ }
+ else
+ {
+ point = resiz_line(char_count, curr_line, curr_line->line_length);
+ curr_line->line_length += (char_count - 1);
+ }
+ for (temp_counter = 1; temp_counter < char_count; temp_counter++)
+ {
+ *point = *str1;
+ point++;
+ str1++;
+ }
+ *point = (char) NULL;
+ *append = FALSE;
+ if ((num == length) && (*str2 != '\n'))
+ *append = TRUE;
+ }
+}
+
+void
+draw_screen() /* redraw the screen from current postion */
+{
+ struct text *temp_line;
+ char *line_out;
+ int temp_vert;
+
+ temp_line = curr_line;
+ temp_vert = scr_vert;
+ wclrtobot(text_win);
+ while ((temp_line != NULL) && (temp_vert <= last_line))
+ {
+ line_out = temp_line->line;
+ draw_line(temp_vert, 0, line_out, 1, temp_line->line_length);
+ temp_vert++;
+ temp_line = temp_line->next_line;
+ }
+ wmove(text_win, temp_vert, 0);
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+finish() /* prepare to exit edit session */
+{
+ char *file_name = in_file_name;
+
+ /*
+ | changes made here should be reflected in the 'save'
+ | portion of file_op()
+ */
+
+ if ((file_name == NULL) || (*file_name == (char) NULL))
+ file_name = get_string(save_file_name_prompt, TRUE);
+
+ if ((file_name == NULL) || (*file_name == (char) NULL))
+ {
+ wmove(com_win, 0, 0);
+ wprintw(com_win, file_not_saved_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return;
+ }
+
+ tmp_file = resolve_name(file_name);
+ if (tmp_file != file_name)
+ {
+ free(file_name);
+ file_name = tmp_file;
+ }
+
+ if (write_file(file_name))
+ {
+ text_changes = FALSE;
+ quit(0);
+ }
+}
+
+int
+quit(noverify) /* exit editor */
+int noverify;
+{
+ char *ans;
+
+ touchwin(text_win);
+ wrefresh(text_win);
+ if ((text_changes) && (!noverify))
+ {
+ ans = get_string(changes_made_prompt, TRUE);
+ if (toupper(*ans) == toupper(*yes_char))
+ text_changes = FALSE;
+ else
+ return(0);
+ free(ans);
+ }
+ if (top_of_stack == NULL)
+ {
+ if (info_window)
+ wrefresh(info_win);
+ wrefresh(com_win);
+ resetty();
+ endwin();
+ putchar('\n');
+ exit(0);
+ }
+ else
+ {
+ delete_text();
+ recv_file = TRUE;
+ input_file = TRUE;
+ check_fp();
+ }
+ return(0);
+}
+
+void
+edit_abort(arg)
+int arg;
+{
+ wrefresh(com_win);
+ resetty();
+ endwin();
+ putchar('\n');
+ exit(1);
+}
+
+void
+delete_text()
+{
+ while (curr_line->next_line != NULL)
+ curr_line = curr_line->next_line;
+ while (curr_line != first_line)
+ {
+ free(curr_line->line);
+ curr_line = curr_line->prev_line;
+ free(curr_line->next_line);
+ }
+ curr_line->next_line = NULL;
+ *curr_line->line = (char) NULL;
+ curr_line->line_length = 1;
+ curr_line->line_number = 1;
+ point = curr_line->line;
+ scr_pos = scr_vert = scr_horz = 0;
+ position = 1;
+}
+
+int
+write_file(file_name)
+char *file_name;
+{
+ char cr;
+ char *tmp_point;
+ struct text *out_line;
+ int lines, charac;
+ int temp_pos;
+ int write_flag = TRUE;
+
+ charac = lines = 0;
+ if ((in_file_name == NULL) || strcmp(in_file_name, file_name))
+ {
+ if ((temp_fp = fopen(file_name, "r")))
+ {
+ tmp_point = get_string(file_exists_prompt, TRUE);
+ if (toupper(*tmp_point) == toupper(*yes_char))
+ write_flag = TRUE;
+ else
+ write_flag = FALSE;
+ fclose(temp_fp);
+ free(tmp_point);
+ }
+ }
+
+ clear_com_win = TRUE;
+
+ if (write_flag)
+ {
+ if ((temp_fp = fopen(file_name, "w")) == NULL)
+ {
+ clear_com_win = TRUE;
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, create_file_fail_msg, file_name);
+ wrefresh(com_win);
+ return(FALSE);
+ }
+ else
+ {
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, writing_file_msg, file_name);
+ wrefresh(com_win);
+ cr = '\n';
+ out_line = first_line;
+ while (out_line != NULL)
+ {
+ temp_pos = 1;
+ tmp_point= out_line->line;
+ while (temp_pos < out_line->line_length)
+ {
+ putc(*tmp_point, temp_fp);
+ tmp_point++;
+ temp_pos++;
+ }
+ charac += out_line->line_length;
+ out_line = out_line->next_line;
+ putc(cr, temp_fp);
+ lines++;
+ }
+ fclose(temp_fp);
+ wmove(com_win,0,0);
+ wclrtoeol(com_win);
+ wprintw(com_win, file_written_msg, file_name, lines, charac);
+ wrefresh(com_win);
+ return(TRUE);
+ }
+ }
+ else
+ return(FALSE);
+}
+
+int
+search(display_message) /* search for string in srch_str */
+int display_message;
+{
+ int lines_moved;
+ int iter;
+ int found;
+
+ if ((srch_str == NULL) || (*srch_str == (char) NULL))
+ return(FALSE);
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, searching_msg);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ }
+ lines_moved = 0;
+ found = FALSE;
+ srch_line = curr_line;
+ srch_1 = point;
+ if (position < curr_line->line_length)
+ srch_1++;
+ iter = position + 1;
+ while ((!found) && (srch_line != NULL))
+ {
+ while ((iter < srch_line->line_length) && (!found))
+ {
+ srch_2 = srch_1;
+ if (case_sen) /* if case sensitive */
+ {
+ srch_3 = srch_str;
+ while ((*srch_2 == *srch_3) && (*srch_3 != (char) NULL))
+ {
+ found = TRUE;
+ srch_2++;
+ srch_3++;
+ } /* end while */
+ }
+ else /* if not case sensitive */
+ {
+ srch_3 = u_srch_str;
+ while ((toupper(*srch_2) == *srch_3) && (*srch_3 != (char) NULL))
+ {
+ found = TRUE;
+ srch_2++;
+ srch_3++;
+ }
+ } /* end else */
+ if (!((*srch_3 == (char) NULL) && (found)))
+ {
+ found = FALSE;
+ if (iter < srch_line->line_length)
+ srch_1++;
+ iter++;
+ }
+ }
+ if (!found)
+ {
+ srch_line = srch_line->next_line;
+ if (srch_line != NULL)
+ srch_1 = srch_line->line;
+ iter = 1;
+ lines_moved++;
+ }
+ }
+ if (found)
+ {
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ }
+ if (lines_moved == 0)
+ {
+ while (position < iter)
+ right(TRUE);
+ }
+ else
+ {
+ if (lines_moved < 30)
+ {
+ move_rel("d", lines_moved);
+ while (position < iter)
+ right(TRUE);
+ }
+ else
+ {
+ curr_line = srch_line;
+ point = srch_1;
+ position = iter;
+ scanline(point);
+ scr_pos = scr_horz;
+ midscreen((last_line / 2), point);
+ }
+ }
+ }
+ else
+ {
+ if (display_message)
+ {
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, str_not_found_msg, srch_str);
+ wrefresh(com_win);
+ }
+ wmove(text_win, scr_vert,(scr_horz - horiz_offset));
+ }
+ return(found);
+}
+
+void
+search_prompt() /* prompt and read search string (srch_str) */
+{
+ if (srch_str != NULL)
+ free(srch_str);
+ if ((u_srch_str != NULL) && (*u_srch_str != (char) NULL))
+ free(u_srch_str);
+ srch_str = get_string(search_prompt_str, FALSE);
+ gold = FALSE;
+ srch_3 = srch_str;
+ srch_1 = u_srch_str = malloc(strlen(srch_str) + 1);
+ while (*srch_3 != (char) NULL)
+ {
+ *srch_1 = toupper(*srch_3);
+ srch_1++;
+ srch_3++;
+ }
+ *srch_1 = (char) NULL;
+ search(TRUE);
+}
+
+void
+del_char() /* delete current character */
+{
+ in = 8; /* backspace */
+ if (position < curr_line->line_length) /* if not end of line */
+ {
+ position++;
+ point++;
+ scanline(point);
+ delete(TRUE);
+ }
+ else
+ {
+ right(FALSE);
+ delete(FALSE);
+ }
+}
+
+void
+undel_char() /* undelete last deleted character */
+{
+ if (d_char == '\n') /* insert line if last del_char deleted eol */
+ insert_line(TRUE);
+ else
+ {
+ in = d_char;
+ insert(in);
+ }
+}
+
+void
+del_word() /* delete word in front of cursor */
+{
+ int tposit;
+ int difference;
+ char *d_word2;
+ char *d_word3;
+ char tmp_char;
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = malloc(curr_line->line_length);
+ tmp_char = d_char;
+ d_word3 = point;
+ d_word2 = d_word;
+ tposit = position;
+ while ((tposit < curr_line->line_length) &&
+ ((*d_word3 != ' ') && (*d_word3 != '\t')))
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ while ((tposit < curr_line->line_length) &&
+ ((*d_word3 == ' ') || (*d_word3 == '\t')))
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ *d_word2 = (char) NULL;
+ d_wrd_len = difference = d_word2 - d_word;
+ d_word2 = point;
+ while (tposit < curr_line->line_length)
+ {
+ tposit++;
+ *d_word2 = *d_word3;
+ d_word2++;
+ d_word3++;
+ }
+ curr_line->line_length -= difference;
+ *d_word2 = (char) NULL;
+ draw_line(scr_vert, scr_horz,point,position,curr_line->line_length);
+ d_char = tmp_char;
+ text_changes = TRUE;
+ formatted = FALSE;
+}
+
+void
+undel_word() /* undelete last deleted word */
+{
+ int temp;
+ int tposit;
+ char *tmp_old_ptr;
+ char *tmp_space;
+ char *tmp_ptr;
+ char *d_word_ptr;
+
+ /*
+ | resize line to handle undeleted word
+ */
+ if ((curr_line->max_length - (curr_line->line_length + d_wrd_len)) < 5)
+ point = resiz_line(d_wrd_len, curr_line, position);
+ tmp_ptr = tmp_space = malloc(curr_line->line_length + d_wrd_len);
+ d_word_ptr = d_word;
+ temp = 1;
+ /*
+ | copy d_word contents into temp space
+ */
+ while (temp <= d_wrd_len)
+ {
+ temp++;
+ *tmp_ptr = *d_word_ptr;
+ tmp_ptr++;
+ d_word_ptr++;
+ }
+ tmp_old_ptr = point;
+ tposit = position;
+ /*
+ | copy contents of line from curent position to eol into
+ | temp space
+ */
+ while (tposit < curr_line->line_length)
+ {
+ temp++;
+ tposit++;
+ *tmp_ptr = *tmp_old_ptr;
+ tmp_ptr++;
+ tmp_old_ptr++;
+ }
+ curr_line->line_length += d_wrd_len;
+ tmp_old_ptr = point;
+ *tmp_ptr = (char) NULL;
+ tmp_ptr = tmp_space;
+ tposit = 1;
+ /*
+ | now copy contents from temp space back to original line
+ */
+ while (tposit < temp)
+ {
+ tposit++;
+ *tmp_old_ptr = *tmp_ptr;
+ tmp_ptr++;
+ tmp_old_ptr++;
+ }
+ *tmp_old_ptr = (char) NULL;
+ free(tmp_space);
+ draw_line(scr_vert, scr_horz, point, position, curr_line->line_length);
+}
+
+void
+del_line() /* delete from cursor to end of line */
+{
+ char *dl1;
+ char *dl2;
+ int tposit;
+
+ if (d_line != NULL)
+ free(d_line);
+ d_line = malloc(curr_line->line_length);
+ dl1 = d_line;
+ dl2 = point;
+ tposit = position;
+ while (tposit < curr_line->line_length)
+ {
+ *dl1 = *dl2;
+ dl1++;
+ dl2++;
+ tposit++;
+ }
+ dlt_line->line_length = 1 + tposit - position;
+ *dl1 = (char) NULL;
+ *point = (char) NULL;
+ curr_line->line_length = position;
+ wclrtoeol(text_win);
+ if (curr_line->next_line != NULL)
+ {
+ right(FALSE);
+ delete(FALSE);
+ }
+ text_changes = TRUE;
+}
+
+void
+undel_line() /* undelete last deleted line */
+{
+ char *ud1;
+ char *ud2;
+ int tposit;
+
+ insert_line(TRUE);
+ left(TRUE);
+ point = resiz_line(dlt_line->line_length, curr_line, position);
+ curr_line->line_length += dlt_line->line_length - 1;
+ ud1 = point;
+ ud2 = d_line;
+ tposit = 1;
+ while (tposit < dlt_line->line_length)
+ {
+ tposit++;
+ *ud1 = *ud2;
+ ud1++;
+ ud2++;
+ }
+ *ud1 = (char) NULL;
+ draw_line(scr_vert, scr_horz,point,position,curr_line->line_length);
+}
+
+void
+adv_word() /* advance to next word */
+{
+while ((position < curr_line->line_length) && ((*point != 32) && (*point != 9)))
+ right(TRUE);
+while ((position < curr_line->line_length) && ((*point == 32) || (*point == 9)))
+ right(TRUE);
+}
+
+void
+move_rel(direction, lines) /* move relative to current line */
+char *direction;
+int lines;
+{
+ int i;
+ char *tmp;
+
+ if (*direction == 'u')
+ {
+ scr_pos = 0;
+ while (position > 1)
+ left(TRUE);
+ for (i = 0; i < lines; i++)
+ {
+ up();
+ }
+ if ((last_line > 5) && ( scr_vert < 4))
+ {
+ tmp = point;
+ tmp_line = curr_line;
+ for (i= 0;(i<5)&&(curr_line->prev_line != NULL); i++)
+ {
+ up();
+ }
+ scr_vert = scr_vert + i;
+ curr_line = tmp_line;
+ point = tmp;
+ scanline(point);
+ }
+ }
+ else
+ {
+ if ((position != 1) && (curr_line->next_line != NULL))
+ {
+ nextline();
+ scr_pos = scr_horz = 0;
+ if (horiz_offset)
+ {
+ horiz_offset = 0;
+ midscreen(scr_vert, point);
+ }
+ }
+ else
+ adv_line();
+ for (i = 1; i < lines; i++)
+ {
+ down();
+ }
+ if ((last_line > 10) && (scr_vert > (last_line - 5)))
+ {
+ tmp = point;
+ tmp_line = curr_line;
+ for (i=0; (i<5) && (curr_line->next_line != NULL); i++)
+ {
+ down();
+ }
+ scr_vert = scr_vert - i;
+ curr_line = tmp_line;
+ point = tmp;
+ scanline(point);
+ }
+ }
+ wmove(text_win, scr_vert, (scr_horz - horiz_offset));
+}
+
+void
+eol() /* go to end of line */
+{
+ if (position < curr_line->line_length)
+ {
+ while (position < curr_line->line_length)
+ right(TRUE);
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ right(TRUE);
+ while (position < curr_line->line_length)
+ right(TRUE);
+ }
+}
+
+void
+bol() /* move to beginning of line */
+{
+ if (point != curr_line->line)
+ {
+ while (point != curr_line->line)
+ left(TRUE);
+ }
+ else if (curr_line->prev_line != NULL)
+ {
+ scr_pos = 0;
+ up();
+ }
+}
+
+void
+adv_line() /* advance to beginning of next line */
+{
+ if ((point != curr_line->line) || (scr_pos > 0))
+ {
+ while (position < curr_line->line_length)
+ right(TRUE);
+ right(TRUE);
+ }
+ else if (curr_line->next_line != NULL)
+ {
+ scr_pos = 0;
+ down();
+ }
+}
+
+void
+sh_command(string) /* execute shell command */
+char *string; /* string containing user command */
+{
+ char *temp_point;
+ char *last_slash;
+ char *path; /* directory path to executable */
+ int parent; /* zero if child, child's pid if parent */
+ int value;
+ int return_val;
+ struct text *line_holder;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+
+ if (!(path = getenv("SHELL")))
+ path = "/bin/sh";
+ last_slash = temp_point = path;
+ while (*temp_point != (char) NULL)
+ {
+ if (*temp_point == '/')
+ last_slash = ++temp_point;
+ else
+ temp_point++;
+ }
+
+ /*
+ | if in_pipe is true, then output of the shell operation will be
+ | read by the editor, and curses doesn't need to be turned off
+ */
+
+ if (!in_pipe)
+ {
+ keypad(com_win, FALSE);
+ keypad(text_win, FALSE);
+ echo();
+ nl();
+ noraw();
+ resetty();
+
+#ifndef NCURSE
+ endwin();
+#endif
+ }
+
+ if (in_pipe)
+ {
+ pipe(pipe_in); /* create a pipe */
+ parent = fork();
+ if (!parent) /* if the child */
+ {
+/*
+ | child process which will fork and exec shell command (if shell output is
+ | to be read by editor)
+ */
+ in_pipe = FALSE;
+/*
+ | redirect stdout to pipe
+ */
+ temp_stdout = dup(1);
+ close(1);
+ dup(pipe_in[1]);
+/*
+ | redirect stderr to pipe
+ */
+ temp_stderr = dup(2);
+ close(2);
+ dup(pipe_in[1]);
+ close(pipe_in[1]);
+ /*
+ | child will now continue down 'if (!in_pipe)'
+ | path below
+ */
+ }
+ else /* if the parent */
+ {
+/*
+ | prepare editor to read from the pipe
+ */
+ signal(SIGCHLD, SIG_IGN);
+ line_holder = curr_line;
+ tmp_vert = scr_vert;
+ close(pipe_in[1]);
+ get_fd = pipe_in[0];
+ get_file("");
+ close(pipe_in[0]);
+ scr_vert = tmp_vert;
+ scr_horz = scr_pos = 0;
+ position = 1;
+ curr_line = line_holder;
+ point = curr_line->line;
+ out_pipe = FALSE;
+ signal(SIGCHLD, SIG_DFL);
+/*
+ | since flag "in_pipe" is still TRUE, the path which waits for the child
+ | process to die will be avoided.
+ | (the pipe is closed, no more output can be expected)
+ */
+ }
+ }
+ if (!in_pipe)
+ {
+ signal(SIGINT, SIG_IGN);
+ if (out_pipe)
+ {
+ pipe(pipe_out);
+ }
+/*
+ | fork process which will exec command
+ */
+ parent = fork();
+ if (!parent) /* if the child */
+ {
+ if (shell_fork)
+ putchar('\n');
+ if (out_pipe)
+ {
+/*
+ | prepare the child process (soon to exec a shell command) to read from the
+ | pipe (which will be output from the editor's buffer)
+ */
+ close(0);
+ dup(pipe_out[0]);
+ close(pipe_out[0]);
+ close(pipe_out[1]);
+ }
+ for (value = 1; value < 24; value++)
+ signal(value, SIG_DFL);
+ execl(path, last_slash, "-c", string, NULL);
+ printf(exec_err_msg, path);
+ exit(-1);
+ }
+ else /* if the parent */
+ {
+ if (out_pipe)
+ {
+/*
+ | output the contents of the buffer to the pipe (to be read by the
+ | process forked and exec'd above as stdin)
+ */
+ close(pipe_out[0]);
+ line_holder = first_line;
+ while (line_holder != NULL)
+ {
+ write(pipe_out[1], line_holder->line, (line_holder->line_length-1));
+ write(pipe_out[1], "\n", 1);
+ line_holder = line_holder->next_line;
+ }
+ close(pipe_out[1]);
+ out_pipe = FALSE;
+ }
+ do
+ {
+ return_val = wait((int *) 0);
+ }
+ while ((return_val != parent) && (return_val != -1));
+/*
+ | if this process is actually the child of the editor, exit. Here's how it
+ | works:
+ | The editor forks a process. If output must be sent to the command to be
+ | exec'd another process is forked, and that process (the child's child)
+ | will exec the command. In this case, "shell_fork" will be FALSE. If no
+ | output is to be performed to the shell command, "shell_fork" will be TRUE.
+ | If this is the editor process, shell_fork will be true, otherwise this is
+ | the child of the edit process.
+ */
+ if (!shell_fork)
+ exit(0);
+ }
+ signal(SIGINT, edit_abort);
+ }
+ if (shell_fork)
+ {
+ printf(continue_msg);
+ fflush(stdout);
+ while ((in = getchar()) != '\n')
+ ;
+ }
+
+ if (!in_pipe)
+ {
+ fixterm();
+ noecho();
+ nonl();
+ raw();
+ keypad(text_win, TRUE);
+ keypad(com_win, TRUE);
+ if (info_window)
+ clearok(info_win, TRUE);
+ }
+
+ redraw();
+}
+
+void
+set_up_term() /* set up the terminal for operating with ae */
+{
+ if (!curses_initialized)
+ {
+ initscr();
+ savetty();
+ noecho();
+ raw();
+ nonl();
+ curses_initialized = TRUE;
+ }
+
+ if (((LINES > 15) && (COLS >= 80)) && info_window)
+ last_line = LINES - 8;
+ else
+ {
+ info_window = FALSE;
+ last_line = LINES - 2;
+ }
+
+ idlok(stdscr, TRUE);
+ com_win = newwin(1, COLS, (LINES - 1), 0);
+ keypad(com_win, TRUE);
+ idlok(com_win, TRUE);
+ wrefresh(com_win);
+ if (!info_window)
+ text_win = newwin((LINES - 1), COLS, 0, 0);
+ else
+ text_win = newwin((LINES - 7), COLS, 6, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ wrefresh(text_win);
+ help_win = newwin((LINES - 1), COLS, 0, 0);
+ keypad(help_win, TRUE);
+ idlok(help_win, TRUE);
+ if (info_window)
+ {
+ info_type = CONTROL_KEYS;
+ info_win = newwin(6, COLS, 0, 0);
+ werase(info_win);
+ paint_info_win();
+ }
+
+ last_col = COLS - 1;
+ local_LINES = LINES;
+ local_COLS = COLS;
+}
+
+void
+resize_check()
+{
+ if ((LINES == local_LINES) && (COLS == local_COLS))
+ return;
+
+ if (info_window)
+ delwin(info_win);
+ delwin(text_win);
+ delwin(com_win);
+ delwin(help_win);
+ set_up_term();
+ redraw();
+ wrefresh(text_win);
+}
+
+int
+menu_op(menu_list)
+struct menu_entries * menu_list;
+{
+ WINDOW *temp_win;
+ int max_width, max_height;
+ int x_off, y_off;
+ int counter;
+ int length;
+ int input;
+ int list_size;
+ int top_offset;
+ int temp_int;
+ char *cancel_string = menu_cancel_msg;
+
+ /*
+ | determine number and width of menu items
+ */
+
+ list_size = 1;
+ while (menu_list[list_size + 1].item_string != NULL)
+ list_size++;
+ max_width = strlen(cancel_string);
+ for (counter = 0; counter <= list_size; counter++)
+ {
+ if ((length = strlen(menu_list[counter].item_string)) > max_width)
+ max_width = length;
+ }
+ max_width += 6;
+
+ /*
+ | make sure that window is large enough to handle menu
+ | if not, print error message and return to calling function
+ */
+
+ if ((LINES < list_size) || (max_width > COLS))
+ {
+ wmove(com_win, 0, 0);
+ werase(com_win);
+ wprintw(com_win, menu_size_err_msg);
+ clear_com_win = TRUE;
+ return(0);
+ }
+
+ top_offset = 0;
+ max_height = list_size;
+
+ if (LINES >= (list_size + 8))
+ {
+ max_height = list_size + 8;
+ top_offset = 4;
+ }
+ x_off = (COLS - max_width) / 2;
+ y_off = (LINES - max_height - 1) / 2;
+ temp_win = newwin(max_height, max_width, y_off, x_off);
+ keypad(temp_win, TRUE);
+ werase(temp_win);
+
+ /*
+ | output top and bottom portions of menu box only if window
+ | large enough
+ */
+
+ if (max_height > list_size)
+ {
+ wmove(temp_win, 1, 1);
+ if (!nohighlight)
+ wstandout(temp_win);
+ waddch(temp_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(temp_win, '-');
+ waddch(temp_win, '+');
+
+ wmove(temp_win, (max_height - 2), 1);
+ waddch(temp_win, '+');
+ for (counter = 0; counter < (max_width - 4); counter++)
+ waddch(temp_win, '-');
+ waddch(temp_win, '+');
+ wstandend(temp_win);
+ wmove(temp_win, 2, 3);
+ waddstr(temp_win, menu_list[0].item_string);
+ wmove(temp_win, (max_height - 3), 3);
+ waddstr(temp_win, cancel_string);
+ }
+ if (!nohighlight)
+ wstandout(temp_win);
+ for (counter = 0; counter < (list_size + top_offset); counter++)
+ {
+ if (top_offset == 4)
+ {
+ temp_int = counter + 2;
+ }
+ else
+ temp_int = counter;
+
+ wmove(temp_win, temp_int, 1);
+ waddch(temp_win, '|');
+ wmove(temp_win, temp_int, (max_width - 2));
+ waddch(temp_win, '|');
+ }
+ wstandend(temp_win);
+ for (counter = 1; counter <= list_size; counter++)
+ {
+ wmove(temp_win, (top_offset + counter - 1), 3);
+ waddstr(temp_win, menu_list[counter].item_string);
+ }
+ counter = 1;
+ do
+ {
+ wmove(temp_win, (counter + top_offset - 1), 3);
+ wrefresh(temp_win);
+ input = wgetch(temp_win);
+ if (input == -1)
+ exit(0);
+ switch (input)
+ {
+ case ' ': /* space */
+ case '\022': /* ^r, right */
+ case '\004': /* ^d, down */
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ counter++;
+ if (counter > list_size)
+ counter = 1;
+ break;
+ case '\010': /* ^h, backspace*/
+ case '\014': /* ^l, left */
+ case '\025': /* ^u, up */
+ case 127: /* ^?, delete */
+ case KEY_LEFT:
+ case KEY_UP:
+ counter--;
+ if (counter == 0)
+ counter = list_size;
+ break;
+ case '\033': /* escape key */
+ counter = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ while ((input != '\r') && (input != '\n') && (input != '\033'));
+
+ werase(temp_win);
+ wrefresh(temp_win);
+ delwin(temp_win);
+
+ if ((menu_list[counter].procedure != NULL) ||
+ (menu_list[counter].iprocedure != NULL) ||
+ (menu_list[counter].nprocedure != NULL))
+ {
+ if (menu_list[counter].argument != -1)
+ (*menu_list[counter].iprocedure)(menu_list[counter].argument);
+ else if (menu_list[counter].ptr_argument != NULL)
+ (*menu_list[counter].procedure)(menu_list[counter].ptr_argument);
+ else
+ (*menu_list[counter].nprocedure)();
+ }
+
+ if (info_window)
+ paint_info_win();
+ midscreen(scr_vert, point);
+
+ return(counter);
+}
+
+void
+help()
+{
+ int counter;
+
+ werase(help_win);
+ clearok(help_win, TRUE);
+ for (counter = 0; counter < 22; counter++)
+ {
+ wmove(help_win, counter, 0);
+ waddstr(help_win, (emacs_keys_mode) ?
+ emacs_help_text[counter] : help_text[counter]);
+ }
+ wrefresh(help_win);
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ wprintw(com_win, press_any_key_msg);
+ wrefresh(com_win);
+ counter = wgetch(com_win);
+ if (counter == -1)
+ exit(0);
+ werase(com_win);
+ wmove(com_win, 0, 0);
+ werase(help_win);
+ wrefresh(help_win);
+ wrefresh(com_win);
+ redraw();
+}
+
+void
+paint_info_win()
+{
+ int counter;
+
+ if (!info_window)
+ return;
+
+ werase(info_win);
+ for (counter = 0; counter < 5; counter++)
+ {
+ wmove(info_win, counter, 0);
+ wclrtoeol(info_win);
+ if (info_type == CONTROL_KEYS)
+ waddstr(info_win, (emacs_keys_mode) ?
+ emacs_control_keys[counter] : control_keys[counter]);
+ else if (info_type == COMMANDS)
+ waddstr(info_win, command_strings[counter]);
+ }
+ wmove(info_win, 5, 0);
+ if (!nohighlight)
+ wstandout(info_win);
+ waddstr(info_win, "===============================================================================");
+ wstandend(info_win);
+ wrefresh(info_win);
+}
+
+void
+no_info_window()
+{
+ if (!info_window)
+ return;
+ delwin(info_win);
+ delwin(text_win);
+ info_window = FALSE;
+ last_line = LINES - 2;
+ text_win = newwin((LINES - 1), COLS, 0, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ clearok(text_win, TRUE);
+ midscreen(scr_vert, point);
+ wrefresh(text_win);
+ clear_com_win = TRUE;
+}
+
+void
+create_info_window()
+{
+ if (info_window)
+ return;
+ last_line = LINES - 8;
+ delwin(text_win);
+ text_win = newwin((LINES - 7), COLS, 6, 0);
+ keypad(text_win, TRUE);
+ idlok(text_win, TRUE);
+ werase(text_win);
+ info_window = TRUE;
+ info_win = newwin(6, COLS, 0, 0);
+ werase(info_win);
+ info_type = CONTROL_KEYS;
+ midscreen(min(scr_vert, last_line), point);
+ clearok(info_win, TRUE);
+ paint_info_win();
+ wrefresh(text_win);
+ clear_com_win = TRUE;
+}
+
+int
+file_op(arg)
+int arg;
+{
+ char *string;
+ int flag;
+
+ if (restrict_mode())
+ {
+ return(0);
+ }
+
+ if (arg == READ_FILE)
+ {
+ string = get_string(file_read_prompt_str, TRUE);
+ recv_file = TRUE;
+ tmp_file = resolve_name(string);
+ check_fp();
+ if (tmp_file != string)
+ free(tmp_file);
+ free(string);
+ }
+ else if (arg == WRITE_FILE)
+ {
+ string = get_string(file_write_prompt_str, TRUE);
+ tmp_file = resolve_name(string);
+ write_file(tmp_file);
+ if (tmp_file != string)
+ free(tmp_file);
+ free(string);
+ }
+ else if (arg == SAVE_FILE)
+ {
+ /*
+ | changes made here should be reflected in finish()
+ */
+
+ if (in_file_name)
+ flag = TRUE;
+ else
+ flag = FALSE;
+
+ string = in_file_name;
+ if ((string == NULL) || (*string == (char) NULL))
+ string = get_string(save_file_name_prompt, TRUE);
+ if ((string == NULL) || (*string == (char) NULL))
+ {
+ wmove(com_win, 0, 0);
+ wprintw(com_win, file_not_saved_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(0);
+ }
+ if (!flag)
+ {
+ tmp_file = resolve_name(string);
+ if (tmp_file != string)
+ {
+ free(string);
+ string = tmp_file;
+ }
+ }
+ if (write_file(string))
+ {
+ in_file_name = string;
+ text_changes = FALSE;
+ }
+ else if (!flag)
+ free(string);
+ }
+ return(0);
+}
+
+void
+shell_op()
+{
+ char *string;
+
+ if (((string = get_string(shell_prompt, TRUE)) != NULL) &&
+ (*string != (char) NULL))
+ {
+ sh_command(string);
+ free(string);
+ }
+}
+
+void
+leave_op()
+{
+ if (text_changes)
+ {
+ menu_op(leave_menu);
+ }
+ else
+ quit(TRUE);
+}
+
+void
+redraw()
+{
+ if (info_window)
+ {
+ clearok(info_win, TRUE);
+ paint_info_win();
+ }
+ else
+ clearok(text_win, TRUE);
+ midscreen(scr_vert, point);
+}
+
+/*
+ | The following routines will "format" a paragraph (as defined by a
+ | block of text with blank lines before and after the block).
+ */
+
+int
+Blank_Line(test_line) /* test if line has any non-space characters */
+struct text *test_line;
+{
+ char *line;
+ int length;
+
+ if (test_line == NULL)
+ return(TRUE);
+
+ length = 1;
+ line = test_line->line;
+
+ /*
+ | To handle troff/nroff documents, consider a line with a
+ | period ('.') in the first column to be blank. To handle mail
+ | messages with included text, consider a line with a '>' blank.
+ */
+
+ if ((*line == '.') || (*line == '>'))
+ return(TRUE);
+
+ while (((*line == ' ') || (*line == '\t')) && (length < test_line->line_length))
+ {
+ length++;
+ line++;
+ }
+ if (length != test_line->line_length)
+ return(FALSE);
+ else
+ return(TRUE);
+}
+
+void
+Format() /* format the paragraph according to set margins */
+{
+ int string_count;
+ int offset;
+ int temp_case;
+ int status;
+ int tmp_af;
+ int counter;
+ char *line;
+ char *tmp_srchstr;
+ char *temp1, *temp2;
+ char *temp_dword;
+ char temp_d_char = d_char;
+
+/*
+ | if observ_margins is not set, or the current line is blank,
+ | do not format the current paragraph
+ */
+
+ if ((!observ_margins) || (Blank_Line(curr_line)))
+ return;
+
+/*
+ | save the currently set flags, and clear them
+ */
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | get current position in paragraph, so after formatting, the cursor
+ | will be in the same relative position
+ */
+
+ tmp_af = auto_format;
+ auto_format = FALSE;
+ offset = position;
+ if (position != 1)
+ prev_word();
+ temp_dword = d_word;
+ d_word = NULL;
+ temp_case = case_sen;
+ case_sen = TRUE;
+ tmp_srchstr = srch_str;
+ temp2 = srch_str = (char *) malloc(1 + curr_line->line_length - position);
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ offset -= position;
+ counter = position;
+ line = temp1 = point;
+ while ((*temp1 != (char) NULL) && (*temp1 != ' ') && (*temp1 != '\t') && (counter < curr_line->line_length))
+ {
+ *temp2 = *temp1;
+ temp2++;
+ temp1++;
+ counter++;
+ }
+ *temp2 = (char) NULL;
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+ string_count = 0;
+ status = TRUE;
+ while ((line != point) && (status))
+ {
+ status = search(FALSE);
+ string_count++;
+ }
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | now get back to the start of the paragraph to start formatting
+ */
+
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+ observ_margins = FALSE;
+
+/*
+ | Start going through lines, putting spaces at end of lines if they do
+ | not already exist. Append lines together to get one long line, and
+ | eliminate spacing at begin of lines.
+ */
+
+ while (!Blank_Line(curr_line->next_line))
+ {
+ eol();
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+ del_char();
+ if ((*point == ' ') || (*point == '\t'))
+ del_word();
+ }
+
+/*
+ | Now there is one long line. Eliminate extra spaces within the line
+ | after the first word (so as not to blow away any indenting the user
+ | may have put in).
+ */
+
+ bol();
+ adv_word();
+ while (position < curr_line->line_length)
+ {
+ if ((*point == ' ') && (*(point + 1) == ' '))
+ del_char();
+ else
+ right(TRUE);
+ }
+
+/*
+ | Now make sure there are two spaces after a '.'.
+ */
+
+ bol();
+ while (position < curr_line->line_length)
+ {
+ if ((*point == '.') && (*(point + 1) == ' '))
+ {
+ right(TRUE);
+ insert(' ');
+ insert(' ');
+ while (*point == ' ')
+ del_char();
+ }
+ right(TRUE);
+ }
+
+ observ_margins = TRUE;
+ bol();
+
+ wmove(com_win, 0, 0);
+ wclrtoeol(com_win);
+ wprintw(com_win, formatting_msg);
+ wrefresh(com_win);
+
+/*
+ | create lines between margins
+ */
+
+ while (position < curr_line->line_length)
+ {
+ while ((scr_pos < right_margin) && (position < curr_line->line_length))
+ right(TRUE);
+ if (position < curr_line->line_length)
+ {
+ prev_word();
+ if (position == 1)
+ adv_word();
+ insert_line(TRUE);
+ }
+ }
+
+/*
+ | go back to begin of paragraph, put cursor back to original position
+ */
+
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+/*
+ | find word cursor was in
+ */
+
+ while ((status) && (string_count > 0))
+ {
+ search(FALSE);
+ string_count--;
+ }
+
+/*
+ | offset the cursor to where it was before from the start of the word
+ */
+
+ while (offset > 0)
+ {
+ offset--;
+ right(TRUE);
+ }
+
+/*
+ | reset flags and strings to what they were before formatting
+ */
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = temp_dword;
+ case_sen = temp_case;
+ free(srch_str);
+ srch_str = tmp_srchstr;
+ d_char = temp_d_char;
+ auto_format = tmp_af;
+
+ midscreen(scr_vert, point);
+ werase(com_win);
+ wrefresh(com_win);
+}
+
+char *init_name[3] = {
+ "/usr/share/misc/init.ee",
+ NULL,
+ ".init.ee"
+ };
+
+void
+ee_init() /* check for init file and read it if it exists */
+{
+ FILE *init_file;
+ char *string;
+ char *str1;
+ char *str2;
+ char *home;
+ int counter;
+ int temp_int;
+
+ string = getenv("HOME");
+ if (!string)
+ string = "/root"; /* Set to reasonable default so we don't crash */
+ str1 = home = malloc(strlen(string)+10);
+ strcpy(home, string);
+ strcat(home, "/.init.ee");
+ init_name[1] = home;
+ string = malloc(512);
+
+ for (counter = 0; counter < 3; counter++)
+ {
+ if (!(access(init_name[counter], 4)))
+ {
+ init_file = fopen(init_name[counter], "r");
+ while ((str2 = fgets(string, 512, init_file)) != NULL)
+ {
+ if (unique_test(string, init_strings) != 1)
+ continue;
+ str1 = str2 = string;
+ while (*str2 != '\n')
+ str2++;
+ *str2 = (char) NULL;
+ if (compare(str1, CASE, FALSE))
+ case_sen = TRUE;
+ else if (compare(str1, NOCASE, FALSE))
+ case_sen = FALSE;
+ else if (compare(str1, EXPAND, FALSE))
+ expand_tabs = TRUE;
+ else if (compare(str1, NOEXPAND, FALSE))
+ expand_tabs = FALSE;
+ else if (compare(str1, INFO, FALSE))
+ info_window = TRUE;
+ else if (compare(str1, NOINFO, FALSE))
+ info_window = FALSE;
+ else if (compare(str1, MARGINS, FALSE))
+ observ_margins = TRUE;
+ else if (compare(str1, NOMARGINS, FALSE))
+ observ_margins = FALSE;
+ else if (compare(str1, AUTOFORMAT, FALSE))
+ {
+ auto_format = TRUE;
+ observ_margins = TRUE;
+ }
+ else if (compare(str1, NOAUTOFORMAT, FALSE))
+ auto_format = FALSE;
+ else if (compare(str1, Echo, FALSE))
+ {
+ str1 = next_word(str1);
+ if (*str1 != (char) NULL)
+ echo_string(str1);
+ }
+ else if (compare(str1, PRINTCOMMAND, FALSE))
+ {
+ str1 = next_word(str1);
+ print_command = malloc(strlen(str1)+1);
+ strcpy(print_command, str1);
+ }
+ else if (compare(str1, RIGHTMARGIN, FALSE))
+ {
+ str1 = next_word(str1);
+ if ((*str1 >= '0') && (*str1 <= '9'))
+ {
+ temp_int = atoi(str1);
+ if (temp_int > 0)
+ right_margin = temp_int;
+ }
+ }
+ else if (compare(str1, HIGHLIGHT, FALSE))
+ nohighlight = FALSE;
+ else if (compare(str1, NOHIGHLIGHT, FALSE))
+ nohighlight = TRUE;
+ else if (compare(str1, EIGHTBIT, FALSE))
+ eightbit = TRUE;
+ else if (compare(str1, NOEIGHTBIT, FALSE))
+ eightbit = FALSE;
+ else if (compare(str1, EMACS_string, FALSE))
+ emacs_keys_mode = TRUE;
+ else if (compare(str1, NOEMACS_string, FALSE))
+ emacs_keys_mode = FALSE;
+ }
+ fclose(init_file);
+ }
+ }
+ free(string);
+ free(home);
+}
+
+void
+echo_string(string) /* echo the given string */
+char *string;
+{
+ char *temp;
+ int Counter;
+
+ temp = string;
+ while (*temp != (char) NULL)
+ {
+ if (*temp == '\\')
+ {
+ temp++;
+ if (*temp == 'n')
+ putchar('\n');
+ else if (*temp == 't')
+ putchar('\t');
+ else if (*temp == 'b')
+ putchar('\b');
+ else if (*temp == 'r')
+ putchar('\r');
+ else if (*temp == 'f')
+ putchar('\f');
+ else if ((*temp == 'e') || (*temp == 'E'))
+ putchar('\033'); /* escape */
+ else if (*temp == '\\')
+ putchar('\\');
+ else if (*temp == '\'')
+ putchar('\'');
+ else if ((*temp >= '0') && (*temp <= '9'))
+ {
+ Counter = 0;
+ while ((*temp >= '0') && (*temp <= '9'))
+ {
+ Counter = (8 * Counter) + (*temp - '0');
+ temp++;
+ }
+ putchar(Counter);
+ temp--;
+ }
+ temp++;
+ }
+ else
+ {
+ putchar(*temp);
+ temp++;
+ }
+ }
+
+ fflush(stdout);
+}
+
+void
+spell_op() /* check spelling of words in the editor */
+{
+ if (restrict_mode())
+ {
+ return;
+ }
+ top(); /* go to top of file */
+ insert_line(FALSE); /* create two blank lines */
+ insert_line(FALSE);
+ top();
+ command(shell_echo_msg);
+ adv_line();
+ wmove(com_win, 0, 0);
+ wprintw(com_win, spell_in_prog_msg);
+ wrefresh(com_win);
+ command("<>!spell"); /* send contents of buffer to command 'spell'
+ and read the results back into the editor */
+}
+
+void
+ispell_op()
+{
+ char name[128];
+ char string[256];
+ int pid;
+
+ if (restrict_mode())
+ {
+ return;
+ }
+ pid = getpid();
+ sprintf(name, "/tmp/ee.%d", pid);
+ if (write_file(name))
+ {
+ sprintf(string, "ispell %s", name);
+ sh_command(string);
+ delete_text();
+ tmp_file = name;
+ recv_file = TRUE;
+ check_fp();
+ unlink(name);
+ }
+}
+
+int
+first_word_len(test_line)
+struct text *test_line;
+{
+ int counter;
+ char *pnt;
+
+ pnt = test_line->line;
+ if ((test_line == NULL) || (pnt == NULL) || (*pnt == (char) NULL) ||
+ (*pnt == '.') || (*pnt == '>'))
+ return(0);
+
+ if ((*pnt == ' ') || (*pnt == '\t'))
+ {
+ pnt = next_word(pnt);
+ }
+
+ if (*pnt == (char) NULL)
+ return(0);
+
+ counter = 0;
+ while ((*pnt != (char) NULL) && ((*pnt != ' ') && (*pnt != '\t')))
+ {
+ pnt++;
+ counter++;
+ }
+ while ((*pnt != (char) NULL) && ((*pnt == ' ') || (*pnt == '\t')))
+ {
+ pnt++;
+ counter++;
+ }
+ return(counter);
+}
+
+void
+Auto_Format() /* format the paragraph according to set margins */
+{
+ int string_count;
+ int offset;
+ int temp_case;
+ int word_len;
+ int temp_dwl;
+ int tmp_d_line_length;
+ int leave_loop = FALSE;
+ int status;
+ int counter;
+ char *line;
+ char *tmp_srchstr;
+ char *temp1, *temp2;
+ char *temp_dword;
+ char temp_d_char = d_char;
+ char *tmp_d_line;
+
+/*
+ | if observ_margins is not set, or the current line is blank,
+ | do not format the current paragraph
+ */
+
+ if ((!observ_margins) || (Blank_Line(curr_line)))
+ return;
+
+/*
+ | get current position in paragraph, so after formatting, the cursor
+ | will be in the same relative position
+ */
+
+ tmp_d_line = d_line;
+ tmp_d_line_length = dlt_line->line_length;
+ d_line = NULL;
+ auto_format = FALSE;
+ offset = position;
+ if ((position != 1) && ((*point == ' ') || (*point == '\t') || (position == curr_line->line_length) || (*point == (char) NULL)))
+ prev_word();
+ temp_dword = d_word;
+ temp_dwl = d_wrd_len;
+ d_wrd_len = 0;
+ d_word = NULL;
+ temp_case = case_sen;
+ case_sen = TRUE;
+ tmp_srchstr = srch_str;
+ temp2 = srch_str = (char *) malloc(1 + curr_line->line_length - position);
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ offset -= position;
+ counter = position;
+ line = temp1 = point;
+ while ((*temp1 != (char) NULL) && (*temp1 != ' ') && (*temp1 != '\t') && (counter < curr_line->line_length))
+ {
+ *temp2 = *temp1;
+ temp2++;
+ temp1++;
+ counter++;
+ }
+ *temp2 = (char) NULL;
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+ string_count = 0;
+ status = TRUE;
+ while ((line != point) && (status))
+ {
+ status = search(FALSE);
+ string_count++;
+ }
+
+/*
+ | now get back to the start of the paragraph to start checking
+ */
+
+ if (position != 1)
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+/*
+ | Start going through lines, putting spaces at end of lines if they do
+ | not already exist. Check line length, and move words to the next line
+ | if they cross the margin. Then get words from the next line if they
+ | will fit in before the margin.
+ */
+
+ while (!leave_loop)
+ {
+ if (position != curr_line->line_length)
+ eol();
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+
+ /*
+ | fill line if first word on next line will fit
+ | in the line without crossing the margin
+ */
+
+ while ((curr_line->next_line != NULL) &&
+ ((word_len = first_word_len(curr_line->next_line)) > 0)
+ && ((scr_pos + word_len) < right_margin))
+ {
+ adv_line();
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ del_word();
+ if (position != 1)
+ bol();
+ if (Blank_Line(curr_line))
+ {
+ del_line();
+ }
+ /*
+ | go to end of previous line
+ */
+ left(TRUE);
+ undel_word();
+ eol();
+ /*
+ | make sure there's a space at the end of the line
+ */
+ left(TRUE);
+ if (*point != ' ')
+ {
+ right(TRUE);
+ insert(' ');
+ }
+ else
+ right(TRUE);
+ }
+
+ /*
+ | make sure line does not cross right margin
+ */
+
+ while (right_margin <= scr_pos)
+ {
+ prev_word();
+ if (position != 1)
+ {
+ del_word();
+ if (Blank_Line(curr_line->next_line))
+ insert_line(TRUE);
+ else
+ adv_line();
+ if ((*point == ' ') || (*point == '\t'))
+ adv_word();
+ undel_word();
+ if (position != 1)
+ bol();
+ left(TRUE);
+ }
+ }
+
+ if (!Blank_Line(curr_line->next_line))
+ adv_line();
+ else
+ leave_loop = TRUE;
+ }
+
+/*
+ | go back to begin of paragraph, put cursor back to original position
+ */
+
+ bol();
+ while (!Blank_Line(curr_line->prev_line))
+ bol();
+
+/*
+ | find word cursor was in
+ */
+
+ status = TRUE;
+ while ((status) && (string_count > 0))
+ {
+ status = search(FALSE);
+ string_count--;
+ }
+
+/*
+ | offset the cursor to where it was before from the start of the word
+ */
+
+ while (offset > 0)
+ {
+ offset--;
+ right(TRUE);
+ }
+
+ if ((string_count > 0) && (offset < 0))
+ {
+ while (offset < 0)
+ {
+ offset++;
+ left(TRUE);
+ }
+ }
+
+/*
+ | reset flags and strings to what they were before formatting
+ */
+
+ if (d_word != NULL)
+ free(d_word);
+ d_word = temp_dword;
+ d_wrd_len = temp_dwl;
+ case_sen = temp_case;
+ free(srch_str);
+ srch_str = tmp_srchstr;
+ d_char = temp_d_char;
+ auto_format = TRUE;
+ dlt_line->line_length = tmp_d_line_length;
+ d_line = tmp_d_line;
+
+ formatted = TRUE;
+ midscreen(scr_vert, point);
+}
+
+void
+modes_op()
+{
+ int ret_value;
+ int counter;
+ char *string;
+
+ do
+ {
+ sprintf(modes_menu[1].item_string, "%s %s", mode_strings[1],
+ (expand_tabs ? ON : OFF));
+ sprintf(modes_menu[2].item_string, "%s %s", mode_strings[2],
+ (case_sen ? ON : OFF));
+ sprintf(modes_menu[3].item_string, "%s %s", mode_strings[3],
+ (observ_margins ? ON : OFF));
+ sprintf(modes_menu[4].item_string, "%s %s", mode_strings[4],
+ (auto_format ? ON : OFF));
+ sprintf(modes_menu[5].item_string, "%s %s", mode_strings[5],
+ (eightbit ? ON : OFF));
+ sprintf(modes_menu[6].item_string, "%s %s", mode_strings[6],
+ (info_window ? ON : OFF));
+ sprintf(modes_menu[7].item_string, "%s %s", mode_strings[7],
+ (emacs_keys_mode ? ON : OFF));
+ sprintf(modes_menu[8].item_string, "%s %d", mode_strings[8],
+ right_margin);
+
+ ret_value = menu_op(modes_menu);
+
+ switch (ret_value)
+ {
+ case 1:
+ expand_tabs = !expand_tabs;
+ break;
+ case 2:
+ case_sen = !case_sen;
+ break;
+ case 3:
+ observ_margins = !observ_margins;
+ break;
+ case 4:
+ auto_format = !auto_format;
+ if (auto_format)
+ observ_margins = TRUE;
+ break;
+ case 5:
+ eightbit = !eightbit;
+ redraw();
+ wnoutrefresh(text_win);
+ break;
+ case 6:
+ if (info_window)
+ no_info_window();
+ else
+ create_info_window();
+ break;
+ case 7:
+ emacs_keys_mode = !emacs_keys_mode;
+ if (info_window)
+ paint_info_win();
+ break;
+ case 8:
+ string = get_string(margin_prompt, TRUE);
+ if (string != NULL)
+ {
+ counter = atoi(string);
+ if (counter > 0)
+ right_margin = counter;
+ free(string);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ while (ret_value != 0);
+}
+
+char *
+is_in_string(string, substring) /* a strstr() look-alike for systems without
+ strstr() */
+char * string, *substring;
+{
+ char *full, *sub;
+
+ for (sub = substring; (sub != NULL) && (*sub != (char)NULL); sub++)
+ {
+ for (full = string; (full != NULL) && (*full != (char)NULL);
+ full++)
+ {
+ if (*sub == *full)
+ return(full);
+ }
+ }
+ return(NULL);
+}
+
+/*
+ | handle names of the form "~/file", "~user/file",
+ | "$HOME/foo", "~/$FOO", etc.
+ */
+
+char *
+resolve_name(name)
+char *name;
+{
+ char long_buffer[1024];
+ char short_buffer[128];
+ char *buffer;
+ char *slash;
+ char *tmp;
+ char *start_of_var;
+ int offset;
+ int index;
+ int counter;
+ struct passwd *user;
+
+ if (name[0] == '~')
+ {
+ if (name[1] == '/')
+ {
+ index = getuid();
+ user = (struct passwd *) getpwuid(index);
+ slash = name + 1;
+ }
+ else
+ {
+ slash = strchr(name, '/');
+ if (slash == NULL)
+ return(name);
+ *slash = (char) NULL;
+ user = (struct passwd *) getpwnam((name + 1));
+ *slash = '/';
+ }
+ if (user == NULL)
+ {
+ return(name);
+ }
+ buffer = malloc(strlen(user->pw_dir) + strlen(slash) + 1);
+ strcpy(buffer, user->pw_dir);
+ strcat(buffer, slash);
+ }
+ else
+ buffer = name;
+
+ if (is_in_string(buffer, "$"))
+ {
+ tmp = buffer;
+ index = 0;
+
+ while ((*tmp != (char) NULL) && (index < 1024))
+ {
+
+ while ((*tmp != (char) NULL) && (*tmp != '$') &&
+ (index < 1024))
+ {
+ long_buffer[index] = *tmp;
+ tmp++;
+ index++;
+ }
+
+ if ((*tmp == '$') && (index < 1024))
+ {
+ counter = 0;
+ start_of_var = tmp;
+ tmp++;
+ if (*tmp == '{') /* } */ /* bracketed variable name */
+ {
+ tmp++; /* { */
+ while ((*tmp != (char) NULL) &&
+ (*tmp != '}') &&
+ (counter < 128))
+ {
+ short_buffer[counter] = *tmp;
+ counter++;
+ tmp++;
+ } /* { */
+ if (*tmp == '}')
+ tmp++;
+ }
+ else
+ {
+ while ((*tmp != (char) NULL) &&
+ (*tmp != '/') &&
+ (*tmp != '$') &&
+ (counter < 128))
+ {
+ short_buffer[counter] = *tmp;
+ counter++;
+ tmp++;
+ }
+ }
+ short_buffer[counter] = (char) NULL;
+ if ((slash = getenv(short_buffer)) != NULL)
+ {
+ offset = strlen(slash);
+ if ((offset + index) < 1024)
+ strcpy(&long_buffer[index], slash);
+ index += offset;
+ }
+ else
+ {
+ while ((start_of_var != tmp) && (index < 1024))
+ {
+ long_buffer[index] = *start_of_var;
+ start_of_var++;
+ index++;
+ }
+ }
+ }
+ }
+
+ if (index == 1024)
+ return(buffer);
+ else
+ long_buffer[index] = (char) NULL;
+
+ if (name != buffer)
+ free(buffer);
+ buffer = malloc(index + 1);
+ strcpy(buffer, long_buffer);
+ }
+
+ return(buffer);
+}
+
+int
+restrict_mode()
+{
+ if (!restricted)
+ return(FALSE);
+
+ wmove(com_win, 0, 0);
+ wprintw(com_win, restricted_msg);
+ wclrtoeol(com_win);
+ wrefresh(com_win);
+ clear_com_win = TRUE;
+ return(TRUE);
+}
+
+/*
+ | The following routine tests the input string against the list of
+ | strings, to determine if the string is a unique match with one of the
+ | valid values.
+ */
+
+int
+unique_test(string, list)
+char *string;
+char *list[];
+{
+ int counter;
+ int num_match;
+ int result;
+
+ num_match = 0;
+ counter = 0;
+ while (list[counter] != NULL)
+ {
+ result = compare(string, list[counter], FALSE);
+ if (result)
+ num_match++;
+ counter++;
+ }
+ return(num_match);
+}
+
+#ifndef NO_CATGETS
+/*
+ | Get the catalog entry, and if it got it from the catalog,
+ | make a copy, since the buffer will be overwritten by the
+ | next call to catgets().
+ */
+
+char *
+catgetlocal(number, string)
+int number;
+char *string;
+{
+ char *temp1;
+ char *temp2;
+
+ temp1 = catgets(catalog, 1, number, string);
+ if (temp1 != string)
+ {
+ temp2 = malloc(strlen(temp1) + 1);
+ strcpy(temp2, temp1);
+ temp1 = temp2;
+ }
+ return(temp1);
+}
+#endif /* NO_CATGETS */
+
+/*
+ | The following is to allow for using message catalogs which allow
+ | the software to be 'localized', that is, to use different languages
+ | all with the same binary. For more information, see your system
+ | documentation, or the X/Open Internationalization Guide.
+ */
+
+void
+strings_init()
+{
+ int counter;
+
+#ifndef NO_CATGETS
+ setlocale(LC_ALL, "");
+ catalog = catopen("ee", 0);
+#endif /* NO_CATGETS */
+
+ modes_menu[0].item_string = catgetlocal( 1, "modes menu");
+ mode_strings[1] = catgetlocal( 2, "tabs to spaces ");
+ mode_strings[2] = catgetlocal( 3, "case sensitive search");
+ mode_strings[3] = catgetlocal( 4, "margins observed ");
+ mode_strings[4] = catgetlocal( 5, "auto-paragraph format");
+ mode_strings[5] = catgetlocal( 6, "eightbit characters ");
+ mode_strings[6] = catgetlocal( 7, "info window ");
+ mode_strings[8] = catgetlocal( 8, "right margin ");
+ leave_menu[0].item_string = catgetlocal( 9, "leave menu");
+ leave_menu[1].item_string = catgetlocal( 10, "save changes");
+ leave_menu[2].item_string = catgetlocal( 11, "no save");
+ file_menu[0].item_string = catgetlocal( 12, "file menu");
+ file_menu[1].item_string = catgetlocal( 13, "read a file");
+ file_menu[2].item_string = catgetlocal( 14, "write a file");
+ file_menu[3].item_string = catgetlocal( 15, "save file");
+ file_menu[4].item_string = catgetlocal( 16, "print editor contents");
+ search_menu[0].item_string = catgetlocal( 17, "search menu");
+ search_menu[1].item_string = catgetlocal( 18, "search for ...");
+ search_menu[2].item_string = catgetlocal( 19, "search");
+ spell_menu[0].item_string = catgetlocal( 20, "spell menu");
+ spell_menu[1].item_string = catgetlocal( 21, "use 'spell'");
+ spell_menu[2].item_string = catgetlocal( 22, "use 'ispell'");
+ misc_menu[0].item_string = catgetlocal( 23, "miscellaneous menu");
+ misc_menu[1].item_string = catgetlocal( 24, "format paragraph");
+ misc_menu[2].item_string = catgetlocal( 25, "shell command");
+ misc_menu[3].item_string = catgetlocal( 26, "check spelling");
+ main_menu[0].item_string = catgetlocal( 27, "main menu");
+ main_menu[1].item_string = catgetlocal( 28, "leave editor");
+ main_menu[2].item_string = catgetlocal( 29, "help");
+ main_menu[3].item_string = catgetlocal( 30, "file operations");
+ main_menu[4].item_string = catgetlocal( 31, "redraw screen");
+ main_menu[5].item_string = catgetlocal( 32, "settings");
+ main_menu[6].item_string = catgetlocal( 33, "search");
+ main_menu[7].item_string = catgetlocal( 34, "miscellaneous");
+ help_text[0] = catgetlocal( 35, "Control keys: ");
+ help_text[1] = catgetlocal( 36, "^a ascii code ^i tab ^r right ");
+ help_text[2] = catgetlocal( 37, "^b bottom of text ^j newline ^t top of text ");
+ help_text[3] = catgetlocal( 38, "^c command ^k delete char ^u up ");
+ help_text[4] = catgetlocal( 39, "^d down ^l left ^v undelete word ");
+ help_text[5] = catgetlocal( 40, "^e search prompt ^m newline ^w delete word ");
+ help_text[6] = catgetlocal( 41, "^f undelete char ^n next page ^x search ");
+ help_text[7] = catgetlocal( 42, "^g begin of line ^o end of line ^y delete line ");
+ help_text[8] = catgetlocal( 43, "^h backspace ^p prev page ^z undelete line ");
+ help_text[9] = catgetlocal( 44, "^[ (escape) menu ");
+ help_text[10] = catgetlocal( 45, " ");
+ help_text[11] = catgetlocal( 46, "Commands: ");
+ help_text[12] = catgetlocal( 47, "help : get this info file : print file name ");
+ help_text[13] = catgetlocal( 48, "read : read a file char : ascii code of char ");
+ help_text[14] = catgetlocal( 49, "write : write a file case : case sensitive search ");
+ help_text[15] = catgetlocal( 50, "exit : leave and save nocase : case insensitive search ");
+ help_text[16] = catgetlocal( 51, "quit : leave, no save !cmd : execute \"cmd\" in shell ");
+ help_text[17] = catgetlocal( 52, "line : display line # 0-9 : go to line \"#\" ");
+ help_text[18] = catgetlocal( 53, "expand : expand tabs noexpand: do not expand tabs ");
+ help_text[19] = catgetlocal( 54, " ");
+ help_text[20] = catgetlocal( 55, " ee [-i] [-e] [-h] [file(s)] ");
+ help_text[21] = catgetlocal( 56, " -i : no information window -e : do not expand tabs -h : no highlight ");
+ control_keys[0] = catgetlocal( 57, "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page ");
+ control_keys[1] = catgetlocal( 58, "^a ascii code ^x search ^z undelete line ^d down ^n next page ");
+ control_keys[2] = catgetlocal( 59, "^b bottom of text ^g begin of line ^w delete word ^l left ");
+ control_keys[3] = catgetlocal( 60, "^t top of text ^o end of line ^v undelete word ^r right ");
+ control_keys[4] = catgetlocal( 61, "^c command ^k delete char ^f undelete char ");
+ command_strings[0] = catgetlocal( 62, "help : get help info |file : print file name |line : print line # ");
+ command_strings[1] = catgetlocal( 63, "read : read a file |char : ascii code of char |0-9 : go to line \"#\"");
+ command_strings[2] = catgetlocal( 64, "write: write a file |case : case sensitive search |exit : leave and save ");
+ command_strings[3] = catgetlocal( 65, "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save");
+ command_strings[4] = catgetlocal( 66, "expand: expand tabs |noexpand: do not expand tabs ");
+ com_win_message = catgetlocal( 67, " press Escape (^[) for menu");
+ no_file_string = catgetlocal( 68, "no file");
+ ascii_code_str = catgetlocal( 69, "ascii code: ");
+ printer_msg_str = catgetlocal( 70, "sending contents of buffer to \"%s\" ");
+ command_str = catgetlocal( 71, "command: ");
+ file_write_prompt_str = catgetlocal( 72, "name of file to write: ");
+ file_read_prompt_str = catgetlocal( 73, "name of file to read: ");
+ char_str = catgetlocal( 74, "character = %d");
+ unkn_cmd_str = catgetlocal( 75, "unknown command \"%s\"");
+ non_unique_cmd_msg = catgetlocal( 76, "entered command is not unique");
+ line_num_str = catgetlocal( 77, "line %d ");
+ line_len_str = catgetlocal( 78, "length = %d");
+ current_file_str = catgetlocal( 79, "current file is \"%s\" ");
+ usage0 = catgetlocal( 80, "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n");
+ usage1 = catgetlocal( 81, " -i turn off info window\n");
+ usage2 = catgetlocal( 82, " -e do not convert tabs to spaces\n");
+ usage3 = catgetlocal( 83, " -h do not use highlighting\n");
+ file_is_dir_msg = catgetlocal( 84, "file \"%s\" is a directory");
+ new_file_msg = catgetlocal( 85, "new file \"%s\"");
+ cant_open_msg = catgetlocal( 86, "can't open \"%s\"");
+ open_file_msg = catgetlocal( 87, "file \"%s\", %d lines");
+ file_read_fin_msg = catgetlocal( 88, "finished reading file \"%s\"");
+ reading_file_msg = catgetlocal( 89, "reading file \"%s\"");
+ read_only_msg = catgetlocal( 90, ", read only");
+ file_read_lines_msg = catgetlocal( 91, "file \"%s\", %d lines");
+ save_file_name_prompt = catgetlocal( 92, "enter name of file: ");
+ file_not_saved_msg = catgetlocal( 93, "no filename entered: file not saved");
+ changes_made_prompt = catgetlocal( 94, "changes have been made, are you sure? (y/n [n]) ");
+ yes_char = catgetlocal( 95, "y");
+ file_exists_prompt = catgetlocal( 96, "file already exists, overwrite? (y/n) [n] ");
+ create_file_fail_msg = catgetlocal( 97, "unable to create file \"%s\"");
+ writing_file_msg = catgetlocal( 98, "writing file \"%s\"");
+ file_written_msg = catgetlocal( 99, "\"%s\" %d lines, %d characters");
+ searching_msg = catgetlocal( 100, " ...searching");
+ str_not_found_msg = catgetlocal( 101, "string \"%s\" not found");
+ search_prompt_str = catgetlocal( 102, "search for: ");
+ exec_err_msg = catgetlocal( 103, "could not exec %s\n");
+ continue_msg = catgetlocal( 104, "press return to continue ");
+ menu_cancel_msg = catgetlocal( 105, "press Esc to cancel");
+ menu_size_err_msg = catgetlocal( 106, "menu too large for window");
+ press_any_key_msg = catgetlocal( 107, "press any key to continue ");
+ shell_prompt = catgetlocal( 108, "shell command: ");
+ formatting_msg = catgetlocal( 109, "...formatting paragraph...");
+ shell_echo_msg = catgetlocal( 110, "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-");
+ spell_in_prog_msg = catgetlocal( 111, "sending contents of edit buffer to 'spell'");
+ margin_prompt = catgetlocal( 112, "right margin is: ");
+ restricted_msg = catgetlocal( 113, "restricted mode: unable to perform requested operation");
+ ON = catgetlocal( 114, "ON");
+ OFF = catgetlocal( 115, "OFF");
+ HELP = catgetlocal( 116, "HELP");
+ WRITE = catgetlocal( 117, "WRITE");
+ READ = catgetlocal( 118, "READ");
+ LINE = catgetlocal( 119, "LINE");
+ FILE_str = catgetlocal( 120, "FILE");
+ CHARACTER = catgetlocal( 121, "CHARACTER");
+ REDRAW = catgetlocal( 122, "REDRAW");
+ RESEQUENCE = catgetlocal( 123, "RESEQUENCE");
+ AUTHOR = catgetlocal( 124, "AUTHOR");
+ VERSION = catgetlocal( 125, "VERSION");
+ CASE = catgetlocal( 126, "CASE");
+ NOCASE = catgetlocal( 127, "NOCASE");
+ EXPAND = catgetlocal( 128, "EXPAND");
+ NOEXPAND = catgetlocal( 129, "NOEXPAND");
+ Exit_string = catgetlocal( 130, "EXIT");
+ QUIT_string = catgetlocal( 131, "QUIT");
+ INFO = catgetlocal( 132, "INFO");
+ NOINFO = catgetlocal( 133, "NOINFO");
+ MARGINS = catgetlocal( 134, "MARGINS");
+ NOMARGINS = catgetlocal( 135, "NOMARGINS");
+ AUTOFORMAT = catgetlocal( 136, "AUTOFORMAT");
+ NOAUTOFORMAT = catgetlocal( 137, "NOAUTOFORMAT");
+ Echo = catgetlocal( 138, "ECHO");
+ PRINTCOMMAND = catgetlocal( 139, "PRINTCOMMAND");
+ RIGHTMARGIN = catgetlocal( 140, "RIGHTMARGIN");
+ HIGHLIGHT = catgetlocal( 141, "HIGHLIGHT");
+ NOHIGHLIGHT = catgetlocal( 142, "NOHIGHLIGHT");
+ EIGHTBIT = catgetlocal( 143, "EIGHTBIT");
+ NOEIGHTBIT = catgetlocal( 144, "NOEIGHTBIT");
+ /*
+ | additions
+ */
+ mode_strings[7] = catgetlocal( 145, "emacs key bindings ");
+ emacs_help_text[0] = help_text[0];
+ emacs_help_text[1] = catgetlocal( 146, "^a beginning of line ^i tab ^r restore word ");
+ emacs_help_text[2] = catgetlocal( 147, "^b back 1 char ^j undel char ^t top of text ");
+ emacs_help_text[3] = catgetlocal( 148, "^c command ^k delete line ^u bottom of text ");
+ emacs_help_text[4] = catgetlocal( 149, "^d delete char ^l undelete line ^v next page ");
+ emacs_help_text[5] = catgetlocal( 150, "^e end of line ^m newline ^w delete word ");
+ emacs_help_text[6] = catgetlocal( 151, "^f forward 1 char ^n next line ^x search ");
+ emacs_help_text[7] = catgetlocal( 152, "^g go back 1 page ^o ascii char insert ^y search prompt ");
+ emacs_help_text[8] = catgetlocal( 153, "^h backspace ^p prev line ^z next word ");
+ emacs_help_text[9] = help_text[9];
+ emacs_help_text[10] = help_text[10];
+ emacs_help_text[11] = help_text[11];
+ emacs_help_text[12] = help_text[12];
+ emacs_help_text[13] = help_text[13];
+ emacs_help_text[14] = help_text[14];
+ emacs_help_text[15] = help_text[15];
+ emacs_help_text[16] = help_text[16];
+ emacs_help_text[17] = help_text[17];
+ emacs_help_text[18] = help_text[18];
+ emacs_help_text[19] = help_text[19];
+ emacs_help_text[20] = help_text[20];
+ emacs_help_text[21] = help_text[21];
+ emacs_control_keys[0] = catgetlocal( 154, "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page");
+ emacs_control_keys[1] = catgetlocal( 155, "^o ascii code ^x search ^l undelete line ^n next li ^v next page");
+ emacs_control_keys[2] = catgetlocal( 156, "^u end of file ^a begin of line ^w delete word ^b back 1 char ");
+ emacs_control_keys[3] = catgetlocal( 157, "^t top of text ^e end of line ^r restore word ^f forward 1 char ");
+ emacs_control_keys[4] = catgetlocal( 158, "^c command ^d delete char ^j undelete char ^z next word ");
+ EMACS_string = catgetlocal( 159, "EMACS");
+ NOEMACS_string = catgetlocal( 160, "NOEMACS");
+ usage4 = catgetlocal( 161, " +# put cursor at line #\n");
+
+ commands[0] = HELP;
+ commands[1] = WRITE;
+ commands[2] = READ;
+ commands[3] = LINE;
+ commands[4] = FILE_str;
+ commands[5] = REDRAW;
+ commands[6] = RESEQUENCE;
+ commands[7] = AUTHOR;
+ commands[8] = VERSION;
+ commands[9] = CASE;
+ commands[10] = NOCASE;
+ commands[11] = EXPAND;
+ commands[12] = NOEXPAND;
+ commands[13] = Exit_string;
+ commands[14] = QUIT_string;
+ commands[15] = "<";
+ commands[16] = ">";
+ commands[17] = "!";
+ commands[18] = "0";
+ commands[19] = "1";
+ commands[20] = "2";
+ commands[21] = "3";
+ commands[22] = "4";
+ commands[23] = "5";
+ commands[24] = "6";
+ commands[25] = "7";
+ commands[26] = "8";
+ commands[27] = "9";
+ commands[28] = CHARACTER;
+ commands[29] = NULL;
+ init_strings[0] = CASE;
+ init_strings[1] = NOCASE;
+ init_strings[2] = EXPAND;
+ init_strings[3] = NOEXPAND;
+ init_strings[4] = INFO;
+ init_strings[5] = NOINFO;
+ init_strings[6] = MARGINS;
+ init_strings[7] = NOMARGINS;
+ init_strings[8] = AUTOFORMAT;
+ init_strings[9] = NOAUTOFORMAT;
+ init_strings[10] = Echo;
+ init_strings[11] = PRINTCOMMAND;
+ init_strings[12] = RIGHTMARGIN;
+ init_strings[13] = HIGHLIGHT;
+ init_strings[14] = NOHIGHLIGHT;
+ init_strings[15] = EIGHTBIT;
+ init_strings[16] = NOEIGHTBIT;
+ init_strings[17] = EMACS_string;
+ init_strings[18] = NOEMACS_string;
+ init_strings[19] = NULL;
+
+ /*
+ | allocate space for strings here for settings menu
+ */
+
+ for (counter = 1; counter < NUM_MODES_ITEMS; counter++)
+ {
+ modes_menu[counter].item_string = malloc(80);
+ }
+
+#ifndef NO_CATGETS
+ catclose(catalog);
+#endif /* NO_CATGETS */
+}
+
diff --git a/usr.bin/ee/ee.msg b/usr.bin/ee/ee.msg
new file mode 100644
index 0000000..2117d06
--- /dev/null
+++ b/usr.bin/ee/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header: /users/hugh/tmp/old_ae/ee.msg,v 1.3 1993/06/22 04:13:35 hugh Exp $
+$
+$set 1
+$quote "
+1 "modes menu"
+2 "tabs to spaces "
+3 "case sensitive search"
+4 "margins observed "
+5 "auto-paragraph format"
+6 "eightbit characters "
+7 "info window "
+8 "right margin "
+9 "leave menu"
+10 "save changes"
+11 "no save"
+12 "file menu"
+13 "read a file"
+14 "write a file"
+15 "save file"
+16 "print editor contents"
+17 "search menu"
+18 "search for ..."
+19 "search"
+20 "spell menu"
+21 "use 'spell'"
+22 "use 'ispell'"
+23 "miscellaneous menu"
+24 "format paragraph"
+25 "shell command"
+26 "check spelling"
+27 "main menu"
+28 "leave editor"
+29 "help"
+30 "file operations"
+31 "redraw screen"
+32 "settings"
+33 "search"
+34 "miscellaneous"
+35 "Control keys: "
+36 "^a ascii code ^i tab ^r right "
+37 "^b bottom of text ^j newline ^t top of text "
+38 "^c command ^k delete char ^u up "
+39 "^d down ^l left ^v undelete word "
+40 "^e search prompt ^m newline ^w delete word "
+41 "^f undelete char ^n next page ^x search "
+42 "^g begin of line ^o end of line ^y delete line "
+43 "^h backspace ^p prev page ^z undelete line "
+44 "^[ (escape) menu "
+45 " "
+46 "Commands: "
+47 "help : get this info file : print file name "
+48 "read : read a file char : ascii code of char "
+49 "write : write a file case : case sensitive search "
+50 "exit : leave and save nocase : case insensitive search "
+51 "quit : leave, no save !cmd : execute \"cmd\" in shell "
+52 "line : display line # 0-9 : go to line \"#\" "
+53 "expand : expand tabs noexpand: do not expand tabs "
+54 " "
+55 " ee [-i] [-e] [-h] [file(s)] "
+56 " -i : no information window -e : do not expand tabs -h : no highlight "
+57 "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page "
+58 "^a ascii code ^x search ^z undelete line ^d down ^n next page "
+59 "^b bottom of text ^g begin of line ^w delete word ^l left "
+60 "^t top of text ^o end of line ^v undelete word ^r right "
+61 "^c command ^k delete char ^f undelete char "
+62 "help : get help info |file : print file name |line : print line # "
+63 "read : read a file |char : ascii code of char |0-9 : go to line \"#\""
+64 "write: write a file |case : case sensitive search |exit : leave and save "
+65 "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save"
+66 "expand: expand tabs |noexpand: do not expand tabs "
+67 " press Escape (^[) for menu"
+68 "no file"
+69 "ascii code: "
+70 "sending contents of buffer to \"%s\" "
+71 "command: "
+72 "name of file to write: "
+73 "name of file to read: "
+74 "character = %d"
+75 "unknown command \"%s\""
+76 "entered command is not unique"
+77 "line %d "
+78 "length = %d"
+79 "current file is \"%s\" "
+80 "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n"
+81 " -i turn off info window\n"
+82 " -e do not convert tabs to spaces\n"
+83 " -h do not use highlighting\n"
+84 "file \"%s\" is a directory"
+85 "new file \"%s\""
+86 "can't open \"%s\""
+87 "file \"%s\", %d lines"
+88 "finished reading file \"%s\""
+89 "reading file \"%s\""
+90 ", read only"
+91 "file \"%s\", %d lines"
+92 "enter name of file: "
+93 "no filename entered: file not saved"
+94 "changes have been made, are you sure? (y/n [n]) "
+95 "y"
+96 "file already exists, overwrite? (y/n) [n] "
+97 "unable to create file \"%s\""
+98 "writing file \"%s\""
+99 "\"%s\" %d lines, %d characters"
+100 " ...searching"
+101 "string \"%s\" not found"
+102 "search for: "
+103 "could not exec %s\n"
+104 "press return to continue "
+105 "press Esc to cancel"
+106 "menu too large for window"
+107 "press any key to continue "
+108 "shell command: "
+109 "...formatting paragraph..."
+110 "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-"
+111 "sending contents of edit buffer to 'spell'"
+112 "right margin is: "
+113 "restricted mode: unable to perform requested operation"
+114 "ON"
+115 "OFF"
+116 "HELP"
+117 "WRITE"
+118 "READ"
+119 "LINE"
+120 "FILE"
+121 "CHARACTER"
+122 "REDRAW"
+123 "RESEQUENCE"
+124 "AUTHOR"
+125 "VERSION"
+126 "CASE"
+127 "NOCASE"
+128 "EXPAND"
+129 "NOEXPAND"
+130 "EXIT"
+131 "QUIT"
+132 "INFO"
+133 "NOINFO"
+134 "MARGINS"
+135 "NOMARGINS"
+136 "AUTOFORMAT"
+137 "NOAUTOFORMAT"
+138 "ECHO"
+139 "PRINTCOMMAND"
+140 "RIGHTMARGIN"
+141 "HIGHLIGHT"
+142 "NOHIGHLIGHT"
+143 "EIGHTBIT"
+144 "NOEIGHTBIT"
+145 "emacs key bindings "
+146 "^a beginning of line ^i tab ^r restore word "
+147 "^b back 1 char ^j undel char ^t top of text "
+148 "^c command ^k delete line ^u bottom of text "
+149 "^d delete char ^l undelete line ^v next page "
+150 "^e end of line ^m newline ^w delete word "
+151 "^f forward 1 char ^n next line ^x search "
+152 "^g go back 1 page ^o ascii char insert ^y search prompt "
+153 "^h backspace ^p prev line ^z next word "
+154 "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page"
+155 "^o ascii code ^x search ^l undelete line ^n next li ^v next page"
+156 "^u end of file ^a begin of line ^w delete word ^b back 1 char "
+157 "^t top of text ^e end of line ^r restore word ^f forward 1 char "
+158 "^c command ^d delete char ^j undelete char ^z next word "
+159 "EMACS"
+160 "NOEMACS"
+161 " +# put cursor at line #\n"
diff --git a/usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg b/usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg
new file mode 100644
index 0000000..f1aa101
--- /dev/null
+++ b/usr.bin/ee/nls/de_DE.ISO8859-1/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header$
+$
+$set 1
+$quote "
+1 "Modus-Menü"
+2 "Tab -> Leerzeichen "
+3 "Suche ohne Groß/Klein"
+4 "Ränder beachten "
+5 "Automatische Absätze "
+6 "8-Bit Zeichen (Uml.) "
+7 "Hilfefenster "
+8 "rechter Rand "
+9 "Ende-Menü"
+10 "Speichern"
+11 "Verwerfen"
+12 "Dateimenü"
+13 "Öffnen"
+14 "Schreiben in Datei"
+15 "Speichern"
+16 "Aktuellen Inhalt drucken"
+17 "Textsuche"
+18 "Suche nach ..."
+19 "Suchen"
+20 "Rechtschreibung"
+21 "'spell' benutzen"
+22 "'ispell' benutzen"
+23 "Verschiedenes"
+24 "Absatz formatieren"
+25 "Unix-Kommando"
+26 "Rechtschreibung prüfen"
+27 "Hauptmenü"
+28 "Editor beenden"
+29 "Hilfe"
+30 "Dateioperationen"
+31 "Bildschirm regenerieren"
+32 "Einstellungen"
+33 "Suche"
+34 "Verschiedenes"
+35 "Steuertasten: "
+36 "^a ASCII-Code direkt ^i Tabulator ^r nach rechts "
+37 "^b Ende des Textes ^j neue Zeile ^t Anfang des Textes "
+38 "^c Befehl ^k Zeichen löschen ^u hoch "
+39 "^d runter ^l nach links ^v Wort zurückholen "
+40 "^e Textsuche (Menü) ^m neue Zeile ^w Wort löschen "
+41 "^f Zeichen zurückholen ^n nächste Seite ^x Weitersuchen "
+42 "^g zum Zeilenanfang ^o zum Zeilenende ^y Zeile löschen "
+43 "^h Rückschritt ^p vorige Seite ^z Zeile zurückholen "
+44 "^[ (Escape) Menü "
+45 " "
+46 "Befehle: "
+47 "hilfe : diese Hilfe anzeigen datei : Dateinamen anzeigen "
+48 "lesen : Datei öffnen zeichen : ASCII-Code anzeigen "
+49 "schreiben:Datei schreiben grosskl : Suche mit Groß/Kleinschr."
+50 "ende : Sichern und Beenden klein : Suche ohne Groß/Klein. "
+51 "abbruch : Abbruch ohne Sichern !bef : Unix-Befehl \"bef\" ausf. "
+52 "zeile : Zeilennummer anzeigen 0-9 : Zur angegebenen Zeile "
+53 "leer : Tabulat. in Leerz. wandeln tabs : Tabulatoren belassen "
+54 " "
+55 " ee [-i] [-e] [-h] [datei(en)] "
+56 " -i : ohne Hilfefenster -e : Tabulatoren lassen -h : k. Hervorheb."
+57 "^[ (Escape) Menü ^e Textsuche ^y Zeile löschen ^u hoch ^p Seite zur. "
+58 "^a ASCII-Code ^x Weitersuchen ^z Zeile rückhl. ^d runter ^n Seite vor "
+59 "^b Textende ^g Zeilenanfang ^w Wort löschen ^l links "
+60 "^t Textanfang ^o Zeilenende ^v Wort rückhol. ^r rechts "
+61 "^c Befehl ^k Zeichen lösch. ^f Zeichen rückholen "
+62 "hilfe: Hilfe |datei : Dateiname anzeigen |zeile: Zeilennumer "
+63 "lesen: Datei lesen |zeichen:ASCII-Code des Zeichens |0-9 : zur Zeile "
+64 "schre: Datei schreib. |grosskl:Suche mit Groß/Klein |ende : Speichern,Ende "
+65 "!bef : Unix-\"bef\" |klein: Suche ohne Groß/Klein |abbr : Abbruch "
+66 "leer : Tab -> Leerz. |tabs : Tabulatoren belassen "
+67 " Escape (^[) drücken für Menü"
+68 "Keine Datei"
+69 "ASCII-Code: "
+70 "Pufferinhalt nach \"%s\" schreiben "
+71 "Befehl: "
+72 "Dateiname zum Schreiben: "
+73 "Dateiname zum Lesen: "
+74 "Zeichen = %d"
+75 "Unbekannter Befehl \"%s\""
+76 "Angegebener Befehl ist nicht eindeutig"
+77 "Zeile %d "
+78 "Länge = %d"
+79 "Aktuelle Datei ist \"%s\" "
+80 "Benutzung: %s [-i] [-e] [-h] [+zeilennummer] [dateien]\n"
+81 " -i Hilfefenster ausschalten\n"
+82 " -e Tabulatoren nicht in Leerzeichen wandeln\"
+83 " -h keine Hervorhebungen\n"
+84 "Datei \"%s\" ist ein Verzeichnis"
+85 "Neue Datei \"%s\""
+86 "Datei \"%s\" kann nicht geöffnet werden"
+87 "Datei \"%s\", %d Zeilen"
+88 "Lesen der Datei \"%s\" beendet"
+89 "Lese die Datei \"%s\""
+90 ", schreibgeschützt"
+91 "Datei \"%s\", %d Zeilen"
+92 "Dateinamen eingeben: "
+93 "Kein Name angegeben; Datei nicht gespeichert"
+94 "Pufferinhalt geändert, wirklich verlassen? (j/n [n]) "
+95 "j"
+96 "Datei existiert bereits, überschreiben? (j/n) [n] "
+97 "Datei \"%s\" kann nicht erzeugt werden"
+98 "Schreibe Datei \"%s\""
+99 "\"%s\" %d Zeilen, %d Zeichen"
+100 " ...Suche läuft"
+101 "Zeichenfolge \"%s\" nicht gefunden"
+102 "Suchen nach: "
+103 "Kann %s nicht ausführen\n"
+104 "Bitte die Eingabetaste drücken "
+105 "Escape zum Beenden"
+106 "Menü ist zu groß für das Fenster"
+107 "eine beliebige Taste drücken "
+108 "Unix-Befehl: "
+109 "...formatiere Absatz..."
+110 "<!echo 'Liste der nicht gefundenen Wörter'; echo -=-=-=-=-=-"
+111 "Sende den Pufferinhalt an 'spell'"
+112 "Rechter Rand: "
+113 "Eingeschränkter Modus: gewünschte Operation unzulässig"
+114 "EIN"
+115 "AUS"
+116 "HILFE"
+117 "SCHREIBEN"
+118 "LESEN"
+119 "ZEILE"
+120 "DATEI"
+121 "ZEICHEN"
+122 "REGENERIEREN"
+123 "UMNUMERIEREN"
+124 "AUTOR"
+125 "VERSION"
+126 "GROSSKL"
+127 "KLEIN"
+128 "LEER"
+129 "TABS"
+130 "ENDE"
+131 "ABBRUCH"
+132 "INFO"
+133 "[INFO]"
+134 "RAND"
+135 "[RAND]"
+136 "FORMAT."
+137 "[FORMAT.]"
+138 "ECHO"
+139 "DRUCKBEFEHL"
+140 "RECHTERRAND"
+141 "HERVORHEB."
+142 "[HERVORHEB.]"
+143 "8-BIT"
+144 "[8-BIT]"
+145 "Emacs-Tastenbelegung "
+146 "^a Zeilenanfang ^i Tabulator ^r Wort zurückholen "
+147 "^b ein Zeichen zurück ^j Zeichen zurückholen ^t Textanfang "
+148 "^c Befehl ^k Zeile löschen ^u Textende "
+149 "^d Zeichen löschen ^l Zeile zurückholen ^v nächste Seite "
+150 "^e Zeilenendee ^m neue Zeile ^w Wort löschen "
+151 "^f ein Zeichen vorwärts ^n neue Zeile ^x Weitersuchen "
+152 "^g vorige Seite ^o ASCII-Zeichen einfü. ^y Textsuche "
+153 "^h Rückschritt ^p vorige Zeile ^z nächstes Wort "
+154 "^[ (Escape) Menü ^y Suchtext eing. ^k Zeile löschen ^p vor.Zeile ^g vor.Seite"
+155 "^o ASCII-Zeichen ^x Weitersuchen ^l Zeile rückhol ^n nä. Zeile ^v nä. Seite"
+156 "^u Textende ^a Zeilenanfang ^w Wort löschen ^b ein Zeichen zurück "
+157 "^t Textanfang ^e Zeilenende ^r Wort rückhol. ^f ein Zeichen vor "
+158 "^c Befehl ^d Zeichen lösch. ^j Zeich. rückh. ^z nächstes Wort "
+159 "EMACS"
+160 "[EMACS]"
+161 " +<zahl> Zeiger auf Zeile <zahl> setzen"
diff --git a/usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg b/usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg
new file mode 100644
index 0000000..f1aa101
--- /dev/null
+++ b/usr.bin/ee/nls/de_DE.ISO_8859-1/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header$
+$
+$set 1
+$quote "
+1 "Modus-Menü"
+2 "Tab -> Leerzeichen "
+3 "Suche ohne Groß/Klein"
+4 "Ränder beachten "
+5 "Automatische Absätze "
+6 "8-Bit Zeichen (Uml.) "
+7 "Hilfefenster "
+8 "rechter Rand "
+9 "Ende-Menü"
+10 "Speichern"
+11 "Verwerfen"
+12 "Dateimenü"
+13 "Öffnen"
+14 "Schreiben in Datei"
+15 "Speichern"
+16 "Aktuellen Inhalt drucken"
+17 "Textsuche"
+18 "Suche nach ..."
+19 "Suchen"
+20 "Rechtschreibung"
+21 "'spell' benutzen"
+22 "'ispell' benutzen"
+23 "Verschiedenes"
+24 "Absatz formatieren"
+25 "Unix-Kommando"
+26 "Rechtschreibung prüfen"
+27 "Hauptmenü"
+28 "Editor beenden"
+29 "Hilfe"
+30 "Dateioperationen"
+31 "Bildschirm regenerieren"
+32 "Einstellungen"
+33 "Suche"
+34 "Verschiedenes"
+35 "Steuertasten: "
+36 "^a ASCII-Code direkt ^i Tabulator ^r nach rechts "
+37 "^b Ende des Textes ^j neue Zeile ^t Anfang des Textes "
+38 "^c Befehl ^k Zeichen löschen ^u hoch "
+39 "^d runter ^l nach links ^v Wort zurückholen "
+40 "^e Textsuche (Menü) ^m neue Zeile ^w Wort löschen "
+41 "^f Zeichen zurückholen ^n nächste Seite ^x Weitersuchen "
+42 "^g zum Zeilenanfang ^o zum Zeilenende ^y Zeile löschen "
+43 "^h Rückschritt ^p vorige Seite ^z Zeile zurückholen "
+44 "^[ (Escape) Menü "
+45 " "
+46 "Befehle: "
+47 "hilfe : diese Hilfe anzeigen datei : Dateinamen anzeigen "
+48 "lesen : Datei öffnen zeichen : ASCII-Code anzeigen "
+49 "schreiben:Datei schreiben grosskl : Suche mit Groß/Kleinschr."
+50 "ende : Sichern und Beenden klein : Suche ohne Groß/Klein. "
+51 "abbruch : Abbruch ohne Sichern !bef : Unix-Befehl \"bef\" ausf. "
+52 "zeile : Zeilennummer anzeigen 0-9 : Zur angegebenen Zeile "
+53 "leer : Tabulat. in Leerz. wandeln tabs : Tabulatoren belassen "
+54 " "
+55 " ee [-i] [-e] [-h] [datei(en)] "
+56 " -i : ohne Hilfefenster -e : Tabulatoren lassen -h : k. Hervorheb."
+57 "^[ (Escape) Menü ^e Textsuche ^y Zeile löschen ^u hoch ^p Seite zur. "
+58 "^a ASCII-Code ^x Weitersuchen ^z Zeile rückhl. ^d runter ^n Seite vor "
+59 "^b Textende ^g Zeilenanfang ^w Wort löschen ^l links "
+60 "^t Textanfang ^o Zeilenende ^v Wort rückhol. ^r rechts "
+61 "^c Befehl ^k Zeichen lösch. ^f Zeichen rückholen "
+62 "hilfe: Hilfe |datei : Dateiname anzeigen |zeile: Zeilennumer "
+63 "lesen: Datei lesen |zeichen:ASCII-Code des Zeichens |0-9 : zur Zeile "
+64 "schre: Datei schreib. |grosskl:Suche mit Groß/Klein |ende : Speichern,Ende "
+65 "!bef : Unix-\"bef\" |klein: Suche ohne Groß/Klein |abbr : Abbruch "
+66 "leer : Tab -> Leerz. |tabs : Tabulatoren belassen "
+67 " Escape (^[) drücken für Menü"
+68 "Keine Datei"
+69 "ASCII-Code: "
+70 "Pufferinhalt nach \"%s\" schreiben "
+71 "Befehl: "
+72 "Dateiname zum Schreiben: "
+73 "Dateiname zum Lesen: "
+74 "Zeichen = %d"
+75 "Unbekannter Befehl \"%s\""
+76 "Angegebener Befehl ist nicht eindeutig"
+77 "Zeile %d "
+78 "Länge = %d"
+79 "Aktuelle Datei ist \"%s\" "
+80 "Benutzung: %s [-i] [-e] [-h] [+zeilennummer] [dateien]\n"
+81 " -i Hilfefenster ausschalten\n"
+82 " -e Tabulatoren nicht in Leerzeichen wandeln\"
+83 " -h keine Hervorhebungen\n"
+84 "Datei \"%s\" ist ein Verzeichnis"
+85 "Neue Datei \"%s\""
+86 "Datei \"%s\" kann nicht geöffnet werden"
+87 "Datei \"%s\", %d Zeilen"
+88 "Lesen der Datei \"%s\" beendet"
+89 "Lese die Datei \"%s\""
+90 ", schreibgeschützt"
+91 "Datei \"%s\", %d Zeilen"
+92 "Dateinamen eingeben: "
+93 "Kein Name angegeben; Datei nicht gespeichert"
+94 "Pufferinhalt geändert, wirklich verlassen? (j/n [n]) "
+95 "j"
+96 "Datei existiert bereits, überschreiben? (j/n) [n] "
+97 "Datei \"%s\" kann nicht erzeugt werden"
+98 "Schreibe Datei \"%s\""
+99 "\"%s\" %d Zeilen, %d Zeichen"
+100 " ...Suche läuft"
+101 "Zeichenfolge \"%s\" nicht gefunden"
+102 "Suchen nach: "
+103 "Kann %s nicht ausführen\n"
+104 "Bitte die Eingabetaste drücken "
+105 "Escape zum Beenden"
+106 "Menü ist zu groß für das Fenster"
+107 "eine beliebige Taste drücken "
+108 "Unix-Befehl: "
+109 "...formatiere Absatz..."
+110 "<!echo 'Liste der nicht gefundenen Wörter'; echo -=-=-=-=-=-"
+111 "Sende den Pufferinhalt an 'spell'"
+112 "Rechter Rand: "
+113 "Eingeschränkter Modus: gewünschte Operation unzulässig"
+114 "EIN"
+115 "AUS"
+116 "HILFE"
+117 "SCHREIBEN"
+118 "LESEN"
+119 "ZEILE"
+120 "DATEI"
+121 "ZEICHEN"
+122 "REGENERIEREN"
+123 "UMNUMERIEREN"
+124 "AUTOR"
+125 "VERSION"
+126 "GROSSKL"
+127 "KLEIN"
+128 "LEER"
+129 "TABS"
+130 "ENDE"
+131 "ABBRUCH"
+132 "INFO"
+133 "[INFO]"
+134 "RAND"
+135 "[RAND]"
+136 "FORMAT."
+137 "[FORMAT.]"
+138 "ECHO"
+139 "DRUCKBEFEHL"
+140 "RECHTERRAND"
+141 "HERVORHEB."
+142 "[HERVORHEB.]"
+143 "8-BIT"
+144 "[8-BIT]"
+145 "Emacs-Tastenbelegung "
+146 "^a Zeilenanfang ^i Tabulator ^r Wort zurückholen "
+147 "^b ein Zeichen zurück ^j Zeichen zurückholen ^t Textanfang "
+148 "^c Befehl ^k Zeile löschen ^u Textende "
+149 "^d Zeichen löschen ^l Zeile zurückholen ^v nächste Seite "
+150 "^e Zeilenendee ^m neue Zeile ^w Wort löschen "
+151 "^f ein Zeichen vorwärts ^n neue Zeile ^x Weitersuchen "
+152 "^g vorige Seite ^o ASCII-Zeichen einfü. ^y Textsuche "
+153 "^h Rückschritt ^p vorige Zeile ^z nächstes Wort "
+154 "^[ (Escape) Menü ^y Suchtext eing. ^k Zeile löschen ^p vor.Zeile ^g vor.Seite"
+155 "^o ASCII-Zeichen ^x Weitersuchen ^l Zeile rückhol ^n nä. Zeile ^v nä. Seite"
+156 "^u Textende ^a Zeilenanfang ^w Wort löschen ^b ein Zeichen zurück "
+157 "^t Textanfang ^e Zeilenende ^r Wort rückhol. ^f ein Zeichen vor "
+158 "^c Befehl ^d Zeichen lösch. ^j Zeich. rückh. ^z nächstes Wort "
+159 "EMACS"
+160 "[EMACS]"
+161 " +<zahl> Zeiger auf Zeile <zahl> setzen"
diff --git a/usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg b/usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg
new file mode 100644
index 0000000..1ed22aa
--- /dev/null
+++ b/usr.bin/ee/nls/en_US.ISO_8859-1/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header: /home/ncvs/src/usr.bin/ee/ee.msg,v 1.1.1.1 1995/08/30 07:28:06 jkh Exp $
+$
+$set 1
+$quote "
+1 "modes menu"
+2 "tabs to spaces "
+3 "case sensitive search"
+4 "margins observed "
+5 "auto-paragraph format"
+6 "eightbit characters "
+7 "info window "
+8 "right margin "
+9 "leave menu"
+10 "save changes"
+11 "no save"
+12 "file menu"
+13 "read a file"
+14 "write a file"
+15 "save file"
+16 "print editor contents"
+17 "search menu"
+18 "search for ..."
+19 "search"
+20 "spell menu"
+21 "use 'spell'"
+22 "use 'ispell'"
+23 "miscellaneous menu"
+24 "format paragraph"
+25 "shell command"
+26 "check spelling"
+27 "main menu"
+28 "leave editor"
+29 "help"
+30 "file operations"
+31 "redraw screen"
+32 "settings"
+33 "search"
+34 "miscellaneous"
+35 "Control keys: "
+36 "^a ascii code ^i tab ^r right "
+37 "^b bottom of text ^j newline ^t top of text "
+38 "^c command ^k delete char ^u up "
+39 "^d down ^l left ^v undelete word "
+40 "^e search prompt ^m newline ^w delete word "
+41 "^f undelete char ^n next page ^x search "
+42 "^g begin of line ^o end of line ^y delete line "
+43 "^h backspace ^p prev page ^z undelete line "
+44 "^[ (escape) menu "
+45 " "
+46 "Commands: "
+47 "help : get this info file : print file name "
+48 "read : read a file char : ascii code of char "
+49 "write : write a file case : case sensitive search "
+50 "exit : leave and save nocase : case insensitive search "
+51 "quit : leave, no save !cmd : execute \"cmd\" in shell "
+52 "line : display line # 0-9 : go to line \"#\" "
+53 "expand : expand tabs noexpand: do not expand tabs "
+54 " "
+55 " ee [-i] [-e] [-h] [file(s)] "
+56 " -i : no information window -e : do not expand tabs -h : no highlight "
+57 "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page "
+58 "^a ascii code ^x search ^z undelete line ^d down ^n next page "
+59 "^b bottom of text ^g begin of line ^w delete word ^l left "
+60 "^t top of text ^o end of line ^v undelete word ^r right "
+61 "^c command ^k delete char ^f undelete char "
+62 "help : get help info |file : print file name |line : print line # "
+63 "read : read a file |char : ascii code of char |0-9 : go to line \"#\""
+64 "write: write a file |case : case sensitive search |exit : leave and save "
+65 "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save"
+66 "expand: expand tabs |noexpand: do not expand tabs "
+67 " press Escape (^[) for menu"
+68 "no file"
+69 "ascii code: "
+70 "sending contents of buffer to \"%s\" "
+71 "command: "
+72 "name of file to write: "
+73 "name of file to read: "
+74 "character = %d"
+75 "unknown command \"%s\""
+76 "entered command is not unique"
+77 "line %d "
+78 "length = %d"
+79 "current file is \"%s\" "
+80 "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n"
+81 " -i turn off info window\n"
+82 " -e do not convert tabs to spaces\n"
+83 " -h do not use highlighting\n"
+84 "file \"%s\" is a directory"
+85 "new file \"%s\""
+86 "can't open \"%s\""
+87 "file \"%s\", %d lines"
+88 "finished reading file \"%s\""
+89 "reading file \"%s\""
+90 ", read only"
+91 "file \"%s\", %d lines"
+92 "enter name of file: "
+93 "no filename entered: file not saved"
+94 "changes have been made, are you sure? (y/n [n]) "
+95 "y"
+96 "file already exists, overwrite? (y/n) [n] "
+97 "unable to create file \"%s\""
+98 "writing file \"%s\""
+99 "\"%s\" %d lines, %d characters"
+100 " ...searching"
+101 "string \"%s\" not found"
+102 "search for: "
+103 "could not exec %s\n"
+104 "press return to continue "
+105 "press Esc to cancel"
+106 "menu too large for window"
+107 "press any key to continue "
+108 "shell command: "
+109 "...formatting paragraph..."
+110 "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-"
+111 "sending contents of edit buffer to 'spell'"
+112 "right margin is: "
+113 "restricted mode: unable to perform requested operation"
+114 "ON"
+115 "OFF"
+116 "HELP"
+117 "WRITE"
+118 "READ"
+119 "LINE"
+120 "FILE"
+121 "CHARACTER"
+122 "REDRAW"
+123 "RESEQUENCE"
+124 "AUTHOR"
+125 "VERSION"
+126 "CASE"
+127 "NOCASE"
+128 "EXPAND"
+129 "NOEXPAND"
+130 "EXIT"
+131 "QUIT"
+132 "INFO"
+133 "NOINFO"
+134 "MARGINS"
+135 "NOMARGINS"
+136 "AUTOFORMAT"
+137 "NOAUTOFORMAT"
+138 "ECHO"
+139 "PRINTCOMMAND"
+140 "RIGHTMARGIN"
+141 "HIGHLIGHT"
+142 "NOHIGHLIGHT"
+143 "EIGHTBIT"
+144 "NOEIGHTBIT"
+145 "emacs key bindings "
+146 "^a beginning of line ^i tab ^r restore word "
+147 "^b back 1 char ^j undel char ^t top of text "
+148 "^c command ^k delete line ^u bottom of text "
+149 "^d delete char ^l undelete line ^v next page "
+150 "^e end of line ^m newline ^w delete word "
+151 "^f forward 1 char ^n next line ^x search "
+152 "^g go back 1 page ^o ascii char insert ^y search prompt "
+153 "^h backspace ^p prev line ^z next word "
+154 "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page"
+155 "^o ascii code ^x search ^l undelete line ^n next li ^v next page"
+156 "^u end of file ^a begin of line ^w delete word ^b back 1 char "
+157 "^t top of text ^e end of line ^r restore word ^f forward 1 char "
+158 "^c command ^d delete char ^j undelete char ^z next word "
+159 "EMACS"
+160 "NOEMACS"
+161 " +# put cursor at line #\n"
diff --git a/usr.bin/ee/nls/en_US.US-ASCII/ee.msg b/usr.bin/ee/nls/en_US.US-ASCII/ee.msg
new file mode 100644
index 0000000..1ed22aa
--- /dev/null
+++ b/usr.bin/ee/nls/en_US.US-ASCII/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Header: /home/ncvs/src/usr.bin/ee/ee.msg,v 1.1.1.1 1995/08/30 07:28:06 jkh Exp $
+$
+$set 1
+$quote "
+1 "modes menu"
+2 "tabs to spaces "
+3 "case sensitive search"
+4 "margins observed "
+5 "auto-paragraph format"
+6 "eightbit characters "
+7 "info window "
+8 "right margin "
+9 "leave menu"
+10 "save changes"
+11 "no save"
+12 "file menu"
+13 "read a file"
+14 "write a file"
+15 "save file"
+16 "print editor contents"
+17 "search menu"
+18 "search for ..."
+19 "search"
+20 "spell menu"
+21 "use 'spell'"
+22 "use 'ispell'"
+23 "miscellaneous menu"
+24 "format paragraph"
+25 "shell command"
+26 "check spelling"
+27 "main menu"
+28 "leave editor"
+29 "help"
+30 "file operations"
+31 "redraw screen"
+32 "settings"
+33 "search"
+34 "miscellaneous"
+35 "Control keys: "
+36 "^a ascii code ^i tab ^r right "
+37 "^b bottom of text ^j newline ^t top of text "
+38 "^c command ^k delete char ^u up "
+39 "^d down ^l left ^v undelete word "
+40 "^e search prompt ^m newline ^w delete word "
+41 "^f undelete char ^n next page ^x search "
+42 "^g begin of line ^o end of line ^y delete line "
+43 "^h backspace ^p prev page ^z undelete line "
+44 "^[ (escape) menu "
+45 " "
+46 "Commands: "
+47 "help : get this info file : print file name "
+48 "read : read a file char : ascii code of char "
+49 "write : write a file case : case sensitive search "
+50 "exit : leave and save nocase : case insensitive search "
+51 "quit : leave, no save !cmd : execute \"cmd\" in shell "
+52 "line : display line # 0-9 : go to line \"#\" "
+53 "expand : expand tabs noexpand: do not expand tabs "
+54 " "
+55 " ee [-i] [-e] [-h] [file(s)] "
+56 " -i : no information window -e : do not expand tabs -h : no highlight "
+57 "^[ (escape) menu ^e search prompt ^y delete line ^u up ^p prev page "
+58 "^a ascii code ^x search ^z undelete line ^d down ^n next page "
+59 "^b bottom of text ^g begin of line ^w delete word ^l left "
+60 "^t top of text ^o end of line ^v undelete word ^r right "
+61 "^c command ^k delete char ^f undelete char "
+62 "help : get help info |file : print file name |line : print line # "
+63 "read : read a file |char : ascii code of char |0-9 : go to line \"#\""
+64 "write: write a file |case : case sensitive search |exit : leave and save "
+65 "!cmd : shell \"cmd\" |nocase: ignore case in search |quit : leave, no save"
+66 "expand: expand tabs |noexpand: do not expand tabs "
+67 " press Escape (^[) for menu"
+68 "no file"
+69 "ascii code: "
+70 "sending contents of buffer to \"%s\" "
+71 "command: "
+72 "name of file to write: "
+73 "name of file to read: "
+74 "character = %d"
+75 "unknown command \"%s\""
+76 "entered command is not unique"
+77 "line %d "
+78 "length = %d"
+79 "current file is \"%s\" "
+80 "usage: %s [-i] [-e] [-h] [+line_number] [file(s)]\n"
+81 " -i turn off info window\n"
+82 " -e do not convert tabs to spaces\n"
+83 " -h do not use highlighting\n"
+84 "file \"%s\" is a directory"
+85 "new file \"%s\""
+86 "can't open \"%s\""
+87 "file \"%s\", %d lines"
+88 "finished reading file \"%s\""
+89 "reading file \"%s\""
+90 ", read only"
+91 "file \"%s\", %d lines"
+92 "enter name of file: "
+93 "no filename entered: file not saved"
+94 "changes have been made, are you sure? (y/n [n]) "
+95 "y"
+96 "file already exists, overwrite? (y/n) [n] "
+97 "unable to create file \"%s\""
+98 "writing file \"%s\""
+99 "\"%s\" %d lines, %d characters"
+100 " ...searching"
+101 "string \"%s\" not found"
+102 "search for: "
+103 "could not exec %s\n"
+104 "press return to continue "
+105 "press Esc to cancel"
+106 "menu too large for window"
+107 "press any key to continue "
+108 "shell command: "
+109 "...formatting paragraph..."
+110 "<!echo 'list of unrecognized words'; echo -=-=-=-=-=-"
+111 "sending contents of edit buffer to 'spell'"
+112 "right margin is: "
+113 "restricted mode: unable to perform requested operation"
+114 "ON"
+115 "OFF"
+116 "HELP"
+117 "WRITE"
+118 "READ"
+119 "LINE"
+120 "FILE"
+121 "CHARACTER"
+122 "REDRAW"
+123 "RESEQUENCE"
+124 "AUTHOR"
+125 "VERSION"
+126 "CASE"
+127 "NOCASE"
+128 "EXPAND"
+129 "NOEXPAND"
+130 "EXIT"
+131 "QUIT"
+132 "INFO"
+133 "NOINFO"
+134 "MARGINS"
+135 "NOMARGINS"
+136 "AUTOFORMAT"
+137 "NOAUTOFORMAT"
+138 "ECHO"
+139 "PRINTCOMMAND"
+140 "RIGHTMARGIN"
+141 "HIGHLIGHT"
+142 "NOHIGHLIGHT"
+143 "EIGHTBIT"
+144 "NOEIGHTBIT"
+145 "emacs key bindings "
+146 "^a beginning of line ^i tab ^r restore word "
+147 "^b back 1 char ^j undel char ^t top of text "
+148 "^c command ^k delete line ^u bottom of text "
+149 "^d delete char ^l undelete line ^v next page "
+150 "^e end of line ^m newline ^w delete word "
+151 "^f forward 1 char ^n next line ^x search "
+152 "^g go back 1 page ^o ascii char insert ^y search prompt "
+153 "^h backspace ^p prev line ^z next word "
+154 "^[ (escape) menu ^y search prompt ^k delete line ^p prev li ^g prev page"
+155 "^o ascii code ^x search ^l undelete line ^n next li ^v next page"
+156 "^u end of file ^a begin of line ^w delete word ^b back 1 char "
+157 "^t top of text ^e end of line ^r restore word ^f forward 1 char "
+158 "^c command ^d delete char ^j undelete char ^z next word "
+159 "EMACS"
+160 "NOEMACS"
+161 " +# put cursor at line #\n"
diff --git a/usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg b/usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg
new file mode 100644
index 0000000..1901d8e
--- /dev/null
+++ b/usr.bin/ee/nls/fr_FR.ISO8859-1/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Id$
+$
+$set 1
+$quote "
+1 "menu de configuration "
+2 "tabulation -> espaces "
+3 "recherche sensible aux maj/min "
+4 "respect des marges "
+5 "formattage automatique des paragraphes"
+6 "caractères 8 bits "
+7 "fenêtre d'informations "
+8 "marge de droite "
+9 "menu de sortie"
+10 "enregistrer les modifications"
+11 "ne pas enregistrer"
+12 "menu fichiers"
+13 "lire un fichier"
+14 "écrire un fichier"
+15 "enregistrer un fichier"
+16 "imprimer le contenu de l'éditeur"
+17 "menu recherche"
+18 "recherche de..."
+19 "rechercher"
+20 "menu correcteur orthographique"
+21 "utiliser 'spell'"
+22 "utiliser 'ispell'"
+23 "menu divers"
+24 "formatter le paragraphe"
+25 "commande du shell"
+26 "vérifier l'orthographe"
+27 "menu principal"
+28 "quitter l'éditeur"
+29 "aide"
+30 "opérations sur les fichiers"
+31 "rafraîchir l'écran"
+32 "configuration"
+33 "recherche"
+34 "divers"
+35 "Contrôle + touche: "
+36 "^a code ascii ^i tabulation ^r droite "
+37 "^b fin du texte ^j nouvelle ligne ^t début du texte "
+38 "^c commande ^k effacer caractère ^u haut "
+39 "^d bas ^l gauche ^v annuler effacement mot "
+40 "^e entrer recherche ^m nouvelle ligne ^w effacer un mot "
+41 "^f annuler eff. caract. ^n page suivante ^x recherche "
+42 "^g début de ligne ^o fin de ligne ^y effacer ligne "
+43 "^h arrière ^p page précédente ^z annuler effacement ligne"
+44 "^[ (échappement) menu "
+45 " "
+46 "Commandes: "
+47 "aide : pour cet écran d'info fichier: donne le nom du fichier "
+48 "lire : lire un fichier caract : code ascii d'un caractère"
+49 "ecrire : créer un fichier minmaj : recherche sensible aux maj/Min"
+50 "fin : quitter et enregistrer pasmin : recherche insensible aux maj/Min"
+51 "quitter: quitter, ne pas enregistrer !cmd : exécute \"cmd\" par le shell"
+52 "ligne : indique le nunéro de ligne 0-9 : aller à la ligne \"#\" "
+53 "tabs : étendre les tabulations pastabs: ne pas étendre les tabulations"
+54 " "
+55 " ee [-i] [-e] [-h] [fichier(s)] "
+56 " -i : pas de fenêtre d'info -e : ne pas étendre les tabs -h : pas de surbrillance"
+57 "^[ (echap.) menu ^e rechercher... ^y efface ligne ^u haut ^p page préc."
+58 "^a code ascii ^x rechercher ^z annul. eff. ligne ^d bas ^n page suiv."
+59 "^b fin du texte ^g début de ligne ^w efface mot ^l gauche "
+60 "^t début du texte ^o fin de ligne ^v annul. eff. mot ^r droite "
+61 "^c commande ^k efface caract. ^f annul. eff. caract. "
+62 "aide: fenêtre d'aide |fichier: nom du fichier |ligne: numéro de ligne"
+63 "lire: lecture fichier|caract : code ascii du car. |0-9: aller ligne \"#\""
+64 "ecrire: crée un fich. |minmaj: rech. sensible min/maj|fin: quitte et sauve"
+65 "!cmd: shell \"cmd\" |pasmin: rech. insens. min/maj |quitte: quitte sans sauver"
+66 "tabs: étend les tabs |pastabs: n'étend pas les tabulations"
+67 " presser sur Esc (^[) pour le menu"
+68 "pas de fichier"
+69 "code ascii: "
+70 "le contenu du buffer est imprimé sur \"%s\" "
+71 "commande: "
+72 "nom du fichier à créer: "
+73 "nom du fichier à lire: "
+74 "caractère = %d"
+75 "commande inconnue : \"%s\""
+76 "la commande tapée est ambiguë"
+77 "ligne %d "
+78 "longueur = %d"
+79 "le fichier courant est \"%s\" "
+80 "utilisation: %s [-i] [-e] [-h] [+numero_de_ligne] [fichier(s)]\n"
+81 " -i supprime la fenêtre d'informations\n"
+82 " -e ne convertit pas les tabs en espaces\n"
+83 " -h n'utilise pas de surbrillance\n"
+84 "le fichier \"%s\" est un répertoire"
+85 "nouveau fichier \"%s\""
+86 "impossible de d'ouvrir \"%s\""
+87 "fichier \"%s\", %d lignes"
+88 "le fichier \"%s\" a été lu"
+89 "lecture du fichier \"%s\""
+90 ", lecture seule"
+91 "fichier \"%s\", %d lignes"
+92 "entrer un nom de fichier : "
+93 "pas de nom de fichier donné : fichier non enregistré"
+94 "des changements ont été effectués, êtes vous sûr ? (o/n [n]) "
+95 "o"
+96 "le fichier existe déjà, réécrire ? (o/n) [n] "
+97 "impossible de créer le fichier \"%s\""
+98 "écriture du fichier \"%s\""
+99 "\"%s\" %d lignes, %d caractères"
+100 " ...recherche"
+101 "chaîne \"%s\" non trouvée"
+102 "rechercher: "
+103 "impossible d'exécuter %s\n"
+104 "taper return pour continuer "
+105 "presser sur echap pour annuler"
+106 "menu trop grand pour la fenêtre"
+107 "appuyer sur une touche pour continuer "
+108 "commande du shell: "
+109 "...formattage du paragraphe..."
+110 "<!echo 'liste des mots non reconnus'; echo -=-=-=-=-=-"
+111 "envoi du contenu du buffer à 'spell'"
+112 "colonne de la marge de droite : "
+113 "mode restreint: impossible d'effectuer l'opération demandée"
+114 "OUI"
+115 "NON"
+116 "AIDE"
+117 "ECRIRE"
+118 "LIRE"
+119 "LIGNE"
+120 "FICHIER"
+121 "CARACTERE"
+122 "RAFRAICHIR"
+23 "menu divers"
+124 "AUTEUR"
+125 "VERSION"
+126 "MINMAJ"
+127 "PASMINMAJ"
+128 "TABS"
+129 "PASTABS"
+130 "FIN"
+131 "QUITTE"
+132 "INFO"
+133 "PASINFO"
+134 "MARGES"
+135 "PASMARGES"
+136 "AUTOFORMAT"
+137 "PASAUTOFORMAT"
+138 "ECHO"
+139 "COMMANDEIMPRESSION"
+140 "MARGEDROITE"
+141 "SURBRILLANT"
+142 "PASSURBRILLANT"
+143 "8BIT"
+144 "PAS8BIT"
+145 "caractères de contrôle comme emacs "
+146 "^a début de ligne ^i tabulation ^r annule effacement mot "
+147 "^b arrière ^j annule eff. caract. ^t début du texte "
+148 "^c commande ^k efface ligne ^u fin du texte "
+149 "^d efface caractère ^l annule eff. ligne ^v page suivante "
+150 "^e fin de ligne ^m nouvelle ligne ^w effacer un mot "
+151 "^f caractère suivant ^n ligne suivante ^x recherche "
+152 "^g page précédente ^o insère caract. ascii ^y rechercher... "
+153 "^h efface en arrière ^p ligne précédente ^z mot suivant "
+154 "^[ (escape) menu ^y rechercher... ^k efface ligne ^p ligne prec ^g page prec"
+155 "^o code ascii ^x recherche ^l annul. eff.li ^n ligne suiv ^v page suiv"
+156 "^u fin du fichier ^a début de ligne ^w efface mot ^b arrière "
+157 "^t début du texte ^e fin de ligne ^r annul.eff.mot ^f avance 1 caractère "
+158 "^c commande ^d efface caract. ^j annul.eff.car ^z mot suivant "
+159 "EMACS"
+160 "PASEMACS"
+161 " +# positionne le curseur sur la ligne #\n"
diff --git a/usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg b/usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg
new file mode 100644
index 0000000..1901d8e
--- /dev/null
+++ b/usr.bin/ee/nls/fr_FR.ISO_8859-1/ee.msg
@@ -0,0 +1,170 @@
+$ This file contains the messages for ee ("easy editor"). See the file
+$ ee.i18n.guide for more information
+$
+$ For ee patchlevel 3
+$
+$ $Id$
+$
+$set 1
+$quote "
+1 "menu de configuration "
+2 "tabulation -> espaces "
+3 "recherche sensible aux maj/min "
+4 "respect des marges "
+5 "formattage automatique des paragraphes"
+6 "caractères 8 bits "
+7 "fenêtre d'informations "
+8 "marge de droite "
+9 "menu de sortie"
+10 "enregistrer les modifications"
+11 "ne pas enregistrer"
+12 "menu fichiers"
+13 "lire un fichier"
+14 "écrire un fichier"
+15 "enregistrer un fichier"
+16 "imprimer le contenu de l'éditeur"
+17 "menu recherche"
+18 "recherche de..."
+19 "rechercher"
+20 "menu correcteur orthographique"
+21 "utiliser 'spell'"
+22 "utiliser 'ispell'"
+23 "menu divers"
+24 "formatter le paragraphe"
+25 "commande du shell"
+26 "vérifier l'orthographe"
+27 "menu principal"
+28 "quitter l'éditeur"
+29 "aide"
+30 "opérations sur les fichiers"
+31 "rafraîchir l'écran"
+32 "configuration"
+33 "recherche"
+34 "divers"
+35 "Contrôle + touche: "
+36 "^a code ascii ^i tabulation ^r droite "
+37 "^b fin du texte ^j nouvelle ligne ^t début du texte "
+38 "^c commande ^k effacer caractère ^u haut "
+39 "^d bas ^l gauche ^v annuler effacement mot "
+40 "^e entrer recherche ^m nouvelle ligne ^w effacer un mot "
+41 "^f annuler eff. caract. ^n page suivante ^x recherche "
+42 "^g début de ligne ^o fin de ligne ^y effacer ligne "
+43 "^h arrière ^p page précédente ^z annuler effacement ligne"
+44 "^[ (échappement) menu "
+45 " "
+46 "Commandes: "
+47 "aide : pour cet écran d'info fichier: donne le nom du fichier "
+48 "lire : lire un fichier caract : code ascii d'un caractère"
+49 "ecrire : créer un fichier minmaj : recherche sensible aux maj/Min"
+50 "fin : quitter et enregistrer pasmin : recherche insensible aux maj/Min"
+51 "quitter: quitter, ne pas enregistrer !cmd : exécute \"cmd\" par le shell"
+52 "ligne : indique le nunéro de ligne 0-9 : aller à la ligne \"#\" "
+53 "tabs : étendre les tabulations pastabs: ne pas étendre les tabulations"
+54 " "
+55 " ee [-i] [-e] [-h] [fichier(s)] "
+56 " -i : pas de fenêtre d'info -e : ne pas étendre les tabs -h : pas de surbrillance"
+57 "^[ (echap.) menu ^e rechercher... ^y efface ligne ^u haut ^p page préc."
+58 "^a code ascii ^x rechercher ^z annul. eff. ligne ^d bas ^n page suiv."
+59 "^b fin du texte ^g début de ligne ^w efface mot ^l gauche "
+60 "^t début du texte ^o fin de ligne ^v annul. eff. mot ^r droite "
+61 "^c commande ^k efface caract. ^f annul. eff. caract. "
+62 "aide: fenêtre d'aide |fichier: nom du fichier |ligne: numéro de ligne"
+63 "lire: lecture fichier|caract : code ascii du car. |0-9: aller ligne \"#\""
+64 "ecrire: crée un fich. |minmaj: rech. sensible min/maj|fin: quitte et sauve"
+65 "!cmd: shell \"cmd\" |pasmin: rech. insens. min/maj |quitte: quitte sans sauver"
+66 "tabs: étend les tabs |pastabs: n'étend pas les tabulations"
+67 " presser sur Esc (^[) pour le menu"
+68 "pas de fichier"
+69 "code ascii: "
+70 "le contenu du buffer est imprimé sur \"%s\" "
+71 "commande: "
+72 "nom du fichier à créer: "
+73 "nom du fichier à lire: "
+74 "caractère = %d"
+75 "commande inconnue : \"%s\""
+76 "la commande tapée est ambiguë"
+77 "ligne %d "
+78 "longueur = %d"
+79 "le fichier courant est \"%s\" "
+80 "utilisation: %s [-i] [-e] [-h] [+numero_de_ligne] [fichier(s)]\n"
+81 " -i supprime la fenêtre d'informations\n"
+82 " -e ne convertit pas les tabs en espaces\n"
+83 " -h n'utilise pas de surbrillance\n"
+84 "le fichier \"%s\" est un répertoire"
+85 "nouveau fichier \"%s\""
+86 "impossible de d'ouvrir \"%s\""
+87 "fichier \"%s\", %d lignes"
+88 "le fichier \"%s\" a été lu"
+89 "lecture du fichier \"%s\""
+90 ", lecture seule"
+91 "fichier \"%s\", %d lignes"
+92 "entrer un nom de fichier : "
+93 "pas de nom de fichier donné : fichier non enregistré"
+94 "des changements ont été effectués, êtes vous sûr ? (o/n [n]) "
+95 "o"
+96 "le fichier existe déjà, réécrire ? (o/n) [n] "
+97 "impossible de créer le fichier \"%s\""
+98 "écriture du fichier \"%s\""
+99 "\"%s\" %d lignes, %d caractères"
+100 " ...recherche"
+101 "chaîne \"%s\" non trouvée"
+102 "rechercher: "
+103 "impossible d'exécuter %s\n"
+104 "taper return pour continuer "
+105 "presser sur echap pour annuler"
+106 "menu trop grand pour la fenêtre"
+107 "appuyer sur une touche pour continuer "
+108 "commande du shell: "
+109 "...formattage du paragraphe..."
+110 "<!echo 'liste des mots non reconnus'; echo -=-=-=-=-=-"
+111 "envoi du contenu du buffer à 'spell'"
+112 "colonne de la marge de droite : "
+113 "mode restreint: impossible d'effectuer l'opération demandée"
+114 "OUI"
+115 "NON"
+116 "AIDE"
+117 "ECRIRE"
+118 "LIRE"
+119 "LIGNE"
+120 "FICHIER"
+121 "CARACTERE"
+122 "RAFRAICHIR"
+23 "menu divers"
+124 "AUTEUR"
+125 "VERSION"
+126 "MINMAJ"
+127 "PASMINMAJ"
+128 "TABS"
+129 "PASTABS"
+130 "FIN"
+131 "QUITTE"
+132 "INFO"
+133 "PASINFO"
+134 "MARGES"
+135 "PASMARGES"
+136 "AUTOFORMAT"
+137 "PASAUTOFORMAT"
+138 "ECHO"
+139 "COMMANDEIMPRESSION"
+140 "MARGEDROITE"
+141 "SURBRILLANT"
+142 "PASSURBRILLANT"
+143 "8BIT"
+144 "PAS8BIT"
+145 "caractères de contrôle comme emacs "
+146 "^a début de ligne ^i tabulation ^r annule effacement mot "
+147 "^b arrière ^j annule eff. caract. ^t début du texte "
+148 "^c commande ^k efface ligne ^u fin du texte "
+149 "^d efface caractère ^l annule eff. ligne ^v page suivante "
+150 "^e fin de ligne ^m nouvelle ligne ^w effacer un mot "
+151 "^f caractère suivant ^n ligne suivante ^x recherche "
+152 "^g page précédente ^o insère caract. ascii ^y rechercher... "
+153 "^h efface en arrière ^p ligne précédente ^z mot suivant "
+154 "^[ (escape) menu ^y rechercher... ^k efface ligne ^p ligne prec ^g page prec"
+155 "^o code ascii ^x recherche ^l annul. eff.li ^n ligne suiv ^v page suiv"
+156 "^u fin du fichier ^a début de ligne ^w efface mot ^b arrière "
+157 "^t début du texte ^e fin de ligne ^r annul.eff.mot ^f avance 1 caractère "
+158 "^c commande ^d efface caract. ^j annul.eff.car ^z mot suivant "
+159 "EMACS"
+160 "PASEMACS"
+161 " +# positionne le curseur sur la ligne #\n"
diff --git a/usr.bin/env/Makefile b/usr.bin/env/Makefile
new file mode 100644
index 0000000..6b67b73
--- /dev/null
+++ b/usr.bin/env/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= env
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/env/env.c b/usr.bin/env/env.c
new file mode 100644
index 0000000..2c72ddb
--- /dev/null
+++ b/usr.bin/env/env.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)env.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+extern char **environ;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char **ep, *p;
+ char *cleanenv[1];
+ int ch;
+
+ while ((ch = getopt(argc, argv, "-")) != EOF)
+ switch(ch) {
+ case '-':
+ environ = cleanenv;
+ cleanenv[0] = NULL;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: env [-] [name=value ...] [command]\n");
+ exit(1);
+ }
+ for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv)
+ (void)setenv(*argv, ++p, 1);
+ if (*argv) {
+ execvp(*argv, argv);
+ err(1, "%s", *argv);
+ }
+ for (ep = environ; *ep; ep++)
+ (void)printf("%s\n", *ep);
+ exit(0);
+}
diff --git a/usr.bin/error/Makefile b/usr.bin/error/Makefile
new file mode 100644
index 0000000..4ec0ba0
--- /dev/null
+++ b/usr.bin/error/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= error
+SRCS= main.c input.c pi.c subr.c filter.c touch.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/error/error.1 b/usr.bin/error/error.1
new file mode 100644
index 0000000..ad7adf8
--- /dev/null
+++ b/usr.bin/error/error.1
@@ -0,0 +1,304 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)error.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt ERROR 1
+.Os BSD 4
+.Sh NAME
+.Nm error
+.Nd analyze and disperse compiler error messages
+.Sh SYNOPSIS
+.Nm error
+.Op Fl n
+.Op Fl s
+.Op Fl q
+.Op Fl v
+.Op Fl t Ar suffixlist
+.Op Fl I Ar ignorefile
+.Op name
+.Sh DESCRIPTION
+.Nm Error
+analyzes and optionally disperses the diagnostic error messages
+produced by a number of compilers and language processors to the source
+file and line where the errors occurred. It can replace the painful,
+traditional methods of scribbling abbreviations of errors on paper, and
+permits error messages and source code to be viewed simultaneously
+without machinations of multiple windows in a screen editor.
+.Pp
+Options are:
+.Bl -tag -width Ds
+.It Fl n
+Do
+.Em not
+touch any files; all error messages are sent to the
+standard output.
+.It Fl q
+The user is
+.Ar queried
+whether s/he wants to touch the file.
+A ``y'' or ``n'' to the question is necessary to continue.
+Absence of the
+.Fl q
+option implies that all referenced files
+(except those referring to discarded error messages)
+are to be touched.
+.It Fl v
+After all files have been touched,
+overlay the visual editor
+.Xr \&vi 1
+with it set up to edit all files touched,
+and positioned in the first touched file at the first error.
+If
+.Xr \&vi 1
+can't be found, try
+.Xr \&ex 1
+or
+.Xr \&ed 1
+from standard places.
+.It Fl t
+Take the following argument as a suffix list.
+Files whose suffixes do not appear in the suffix list are not touched.
+The suffix list is dot separated, and ``*'' wildcards work.
+Thus the suffix list:
+.Pp
+.Dl ".c.y.foo*.h"
+.Pp
+allows
+.Nm error
+to touch files ending with ``.c'', ``.y'', ``.foo*'' and ``.y''.
+.It Fl s
+Print out
+.Em statistics
+regarding the error categorization.
+Not too useful.
+.El
+.Pp
+.Nm Error
+looks at the error messages,
+either from the specified file
+.Ar name
+or from the standard input,
+and attempts to determine which
+language processor produced each error message,
+determines the source file and line number to which the error message refers,
+determines if the error message is to be ignored or not,
+and inserts the (possibly slightly modified) error message into
+the source file as a comment on the line preceding to which the
+line the error message refers.
+Error messages which can't be categorized by language processor
+or content are not inserted into any file,
+but are sent to the standard output.
+.Nm Error
+touches source files only after all input has been read.
+.Pp
+.Nm Error
+is intended to be run
+with its standard input
+connected via a pipe to the error message source.
+Some language processors put error messages on their standard error file;
+others put their messages on the standard output.
+Hence, both error sources should be piped together into
+.Nm error .
+For example, when using the
+.Xr csh 1
+syntax,
+.Pp
+.Dl make \-s lint \&| error \-q \-v
+.Pp
+will analyze all the error messages produced
+by whatever programs
+.Xr make 1
+runs when making lint.
+.Pp
+.Nm Error
+knows about the error messages produced by:
+.Xr make 1 ,
+.Xr \&cc 1 ,
+.Xr cpp 1 ,
+.Xr ccom 1 ,
+.Xr \&as 1 ,
+.Xr \&ld 1 ,
+.Xr lint 1 ,
+.Xr \&pi 1 ,
+.Xr \&pc 1 ,
+.Xr f77 1 ,
+and
+.Em DEC Western Research Modula\-2 .
+.Nm Error
+knows a standard format for error messages produced by
+the language processors,
+so is sensitive to changes in these formats.
+For all languages except
+.Em Pascal ,
+error messages are restricted to be on one line.
+Some error messages refer to more than one line in more than
+one files;
+.Nm error
+will duplicate the error message and insert it at
+all of the places referenced.
+.Pp
+.Nm Error
+will do one of six things with error messages.
+.Bl -tag -width Em synchronize
+.It Em synchronize
+Some language processors produce short errors describing
+which file it is processing.
+.Nm Error
+uses these to determine the file name for languages that
+don't include the file name in each error message.
+These synchronization messages are consumed entirely by
+.Nm error .
+.It Em discard
+Error messages from
+.Xr lint 1
+that refer to one of the two
+.Xr lint 1
+libraries,
+.Pa /usr/libdata/lint/llib-lc
+and
+.Pa /usr/libdata/lint/llib-port
+are discarded,
+to prevent accidently touching these libraries.
+Again, these error messages are consumed entirely by
+.Nm error .
+.It Em nullify
+Error messages from
+.Xr lint 1
+can be nullified if they refer to a specific function,
+which is known to generate diagnostics which are not interesting.
+Nullified error messages are not inserted into the source file,
+but are written to the standard output.
+The names of functions to ignore are taken from
+either the file named
+.Pa .errorrc
+in the users's home directory,
+or from the file named by the
+.Fl I
+option.
+If the file does not exist,
+no error messages are nullified.
+If the file does exist, there must be one function
+name per line.
+.It Em not file specific
+Error messages that can't be intuited are grouped together,
+and written to the standard output before any files are touched.
+They will not be inserted into any source file.
+.It Em file specific
+Error message that refer to a specific file,
+but to no specific line,
+are written to the standard output when
+that file is touched.
+.It Em true errors
+Error messages that can be intuited are candidates for
+insertion into the file to which they refer.
+.El
+.Pp
+Only true error messages are candidates for inserting into
+the file they refer to.
+Other error messages are consumed entirely by
+.Nm error
+or are written to the standard output.
+.Nm Error
+inserts the error messages into the source file on the line
+preceding the line the language processor found in error.
+Each error message is turned into a one line comment for the
+language,
+and is internally flagged
+with the string ``###'' at
+the beginning of the error,
+and ``%%%'' at the end of the error.
+This makes pattern searching for errors easier with an editor,
+and allows the messages to be easily removed.
+In addition, each error message contains the source line number
+for the line the message refers to.
+A reasonably formatted source program can be recompiled
+with the error messages still in it,
+without having the error messages themselves cause future errors.
+For poorly formatted source programs in free format languages,
+such as C or Pascal,
+it is possible to insert a comment into another comment,
+which can wreak havoc with a future compilation.
+To avoid this, programs with comments and source
+on the same line should be formatted
+so that language statements appear before comments.
+.Pp
+.Nm Error
+catches interrupt and terminate signals,
+and if in the insertion phase,
+will orderly terminate what it is doing.
+.Sh FILES
+.Bl -tag -width ~/.errorrc -compact
+.It Pa ~/.errorrc
+function names to ignore for
+.Xr lint 1
+error messages
+.It Pa /dev/tty
+user's teletype
+.El
+.Sh HISTORY
+The
+.Nm error
+command
+appeared in
+.Bx 4.0 .
+.Sh AUTHOR
+Robert Henry
+.Sh BUGS
+.Pp
+Opens the teletype directly to do user querying.
+.Pp
+Source files with links make a new copy of the file with
+only one link to it.
+.Pp
+Changing a language processor's format of error messages
+may cause
+.Nm error
+to not understand the error message.
+.Pp
+.Nm Error ,
+since it is purely mechanical,
+will not filter out subsequent errors caused by `floodgating'
+initiated by one syntactically trivial error.
+Humans are still much better at discarding these related errors.
+.Pp
+Pascal error messages belong after the lines affected
+(error puts them before). The alignment of the `\\' marking
+the point of error is also disturbed by
+.Nm error .
+.Pp
+.Nm Error
+was designed for work on
+.Tn CRT Ns 's
+at reasonably high speed.
+It is less pleasant on slow speed terminals, and has never been
+used on hardcopy terminals.
diff --git a/usr.bin/error/error.h b/usr.bin/error/error.h
new file mode 100644
index 0000000..d72e5bf
--- /dev/null
+++ b/usr.bin/error/error.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)error.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef int boolean;
+#define reg register
+
+#define TRUE 1
+#define FALSE 0
+
+#define true 1
+#define false 0
+/*
+ * Descriptors for the various languages we know about.
+ * If you touch these, also touch lang_table
+ */
+#define INUNKNOWN 0
+#define INCPP 1
+#define INCC 2
+#define INAS 3
+#define INLD 4
+#define INLINT 5
+#define INF77 6
+#define INPI 7
+#define INPC 8
+#define INFRANZ 9
+#define INLISP 10
+#define INVAXIMA 11
+#define INRATFOR 12
+#define INLEX 13
+#define INYACC 14
+#define INAPL 15
+#define INMAKE 16
+#define INRI 17
+#define INTROFF 18
+#define INMOD2 19
+
+extern int language;
+/*
+ * We analyze each line in the error message file, and
+ * attempt to categorize it by type, as well as language.
+ * Here are the type descriptors.
+ */
+typedef int Errorclass;
+
+#define C_FIRST 0 /* first error category */
+#define C_UNKNOWN 0 /* must be zero */
+#define C_IGNORE 1 /* ignore the message; used for pi */
+#define C_SYNC 2 /* synchronization errors */
+#define C_DISCARD 3 /* touches dangerous files, so discard */
+#define C_NONSPEC 4 /* not specific to any file */
+#define C_THISFILE 5 /* specific to this file, but at no line */
+#define C_NULLED 6 /* refers to special func; so null */
+#define C_TRUE 7 /* fits into true error format */
+#define C_DUPL 8 /* sub class only; duplicated error message */
+#define C_LAST 9 /* last error category */
+
+#define SORTABLE(x) (!(NOTSORTABLE(x)))
+#define NOTSORTABLE(x) (x <= C_NONSPEC)
+/*
+ * Resources to count and print out the error categories
+ */
+extern char *class_table[];
+extern int class_count[];
+
+#define nunknown class_count[C_UNKNOWN]
+#define nignore class_count[C_IGNORE]
+#define nsyncerrors class_count[C_SYNC]
+#define ndiscard class_count[C_DISCARD]
+#define nnonspec class_count[C_NONSPEC]
+#define nthisfile class_count[C_THISFILE]
+#define nnulled class_count[C_NULLED]
+#define ntrue class_count[C_TRUE]
+#define ndupl class_count[C_DUPL]
+
+/* places to put the error complaints */
+
+#define TOTHEFILE 1 /* touch the file */
+#define TOSTDOUT 2 /* just print them out (ho-hum) */
+
+FILE *errorfile; /* where error file comes from */
+FILE *queryfile; /* where the query responses from the user come from*/
+
+extern char *currentfilename;
+extern char *processname;
+extern char *scriptname;
+
+extern boolean query;
+extern boolean terse;
+int inquire(); /* inquire for yes/no */
+/*
+ * codes for inquire() to return
+ */
+#define Q_NO 1 /* 'N' */
+#define Q_no 2 /* 'n' */
+#define Q_YES 3 /* 'Y' */
+#define Q_yes 4 /* 'y' */
+
+int probethisfile();
+/*
+ * codes for probethisfile to return
+ */
+#define F_NOTEXIST 1
+#define F_NOTREAD 2
+#define F_NOTWRITE 3
+#define F_TOUCHIT 4
+
+/*
+ * Describes attributes about a language
+ */
+struct lang_desc{
+ char *lang_name;
+ char *lang_incomment; /* one of the following defines */
+ char *lang_outcomment; /* one of the following defines */
+};
+extern struct lang_desc lang_table[];
+
+#define CINCOMMENT "/*###"
+#define COUTCOMMENT "%%%*/\n"
+#define FINCOMMENT "C###"
+#define FOUTCOMMENT "%%%\n"
+#define NEWLINE "%%%\n"
+#define PIINCOMMENT "(*###"
+#define PIOUTCOMMENT "%%%*)\n"
+#define LISPINCOMMENT ";###"
+#define ASINCOMMENT "####"
+#define RIINCOMMENT CINCOMMENT
+#define RIOUTCOMMENT COUTCOMMENT
+#define TROFFINCOMMENT ".\\\"###"
+#define TROFFOUTCOMMENT NEWLINE
+#define MOD2INCOMMENT "(*###"
+#define MOD2OUTCOMMENT "%%%*)\n"
+/*
+ * Defines and resources for determing if a given line
+ * is to be discarded because it refers to a file not to
+ * be touched, or if the function reference is to a
+ * function the user doesn't want recorded.
+ */
+
+#define ERRORNAME "/.errorrc"
+int nignored;
+char **names_ignored;
+/*
+ * Structure definition for a full error
+ */
+typedef struct edesc Edesc;
+typedef Edesc *Eptr;
+
+struct edesc{
+ Eptr error_next; /*linked together*/
+ int error_lgtext; /* how many on the right hand side*/
+ char **error_text; /* the right hand side proper*/
+ Errorclass error_e_class; /* error category of this error*/
+ Errorclass error_s_class; /* sub descriptor of error_e_class*/
+ int error_language; /* the language for this error*/
+ int error_position; /* oridinal position */
+ int error_line; /* discovered line number*/
+ int error_no; /* sequence number on input */
+};
+/*
+ * Resources for the true errors
+ */
+extern int nerrors;
+extern Eptr er_head;
+extern Eptr *errors;
+/*
+ * Resources for each of the files mentioned
+ */
+extern int nfiles;
+extern Eptr **files; /* array of pointers into errors*/
+boolean *touchedfiles; /* which files we touched */
+/*
+ * The langauge the compilation is in, as intuited from
+ * the flavor of error messages analyzed.
+ */
+extern int langauge;
+extern char *currentfilename;
+/*
+ * Functional forwards
+ */
+char *Calloc();
+char *strsave();
+char *clobberfirst();
+char lastchar();
+char firstchar();
+char next_lastchar();
+char **wordvsplice();
+int wordvcmp();
+boolean persperdexplode();
+/*
+ * Printing hacks
+ */
+char *plural(), *verbform();
diff --git a/usr.bin/error/filter.c b/usr.bin/error/filter.c
new file mode 100644
index 0000000..40cf00d
--- /dev/null
+++ b/usr.bin/error/filter.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)filter.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "pathnames.h"
+
+char *lint_libs[] = {
+ IG_FILE1,
+ IG_FILE2,
+ IG_FILE3,
+ IG_FILE4,
+ 0
+};
+extern char* processname;
+int lexsort();
+/*
+ * Read the file ERRORNAME of the names of functions in lint
+ * to ignore complaints about.
+ */
+getignored(auxname)
+ char *auxname;
+{
+ reg int i;
+ FILE *fyle;
+ char inbuffer[256];
+ int uid;
+ char filename[128];
+ char *username;
+ struct passwd *passwdentry;
+
+ nignored = 0;
+ if (auxname == 0){ /* use the default */
+ if ( (username = (char *)getlogin()) == NULL){
+ username = "Unknown";
+ uid = getuid();
+ if ( (passwdentry = (struct passwd *)getpwuid(uid)) == NULL){
+ return;
+ }
+ } else {
+ if ( (passwdentry = (struct passwd *)getpwnam(username)) == NULL)
+ return;
+ }
+ strcpy(filename, passwdentry->pw_dir);
+ (void)strcat(filename, ERRORNAME);
+ } else
+ (void)strcpy(filename, auxname);
+#ifdef FULLDEBUG
+ printf("Opening file \"%s\" to read names to ignore.\n",
+ filename);
+#endif
+ if ( (fyle = fopen(filename, "r")) == NULL){
+#ifdef FULLDEBUG
+ fprintf(stderr, "%s: Can't open file \"%s\"\n",
+ processname, filename);
+#endif
+ return;
+ }
+ /*
+ * Make the first pass through the file, counting lines
+ */
+ for (nignored = 0; fgets(inbuffer, 255, fyle) != NULL; nignored++)
+ continue;
+ names_ignored = (char **)Calloc(nignored+1, sizeof (char *));
+ fclose(fyle);
+ if (freopen(filename, "r", fyle) == NULL){
+#ifdef FULLDEBUG
+ fprintf(stderr, "%s: Failure to open \"%s\" for second read.\n",
+ processname, filename);
+#endif
+ nignored = 0;
+ return;
+ }
+ for (i=0; i < nignored && (fgets (inbuffer, 255, fyle) != NULL); i++){
+ names_ignored[i] = strsave(inbuffer);
+ (void)substitute(names_ignored[i], '\n', '\0');
+ }
+ qsort(names_ignored, nignored, sizeof *names_ignored, lexsort);
+#ifdef FULLDEBUG
+ printf("Names to ignore follow.\n");
+ for (i=0; i < nignored; i++){
+ printf("\tIgnore: %s\n", names_ignored[i]);
+ }
+#endif
+}
+
+int lexsort(cpp1, cpp2)
+ char **cpp1, **cpp2;
+{
+ return(strcmp(*cpp1, *cpp2));
+}
+
+int search_ignore(key)
+ char *key;
+{
+ reg int ub, lb;
+ reg int halfway;
+ int order;
+
+ if (nignored == 0)
+ return(-1);
+ for(lb = 0, ub = nignored - 1; ub >= lb; ){
+ halfway = (ub + lb)/2;
+ if ( (order = strcmp(key, names_ignored[halfway])) == 0)
+ return(halfway);
+ if (order < 0) /*key is less than probe, throw away above*/
+ ub = halfway - 1;
+ else
+ lb = halfway + 1;
+ }
+ return(-1);
+}
+
+/*
+ * Tell if the error text is to be ignored.
+ * The error must have been canonicalized, with
+ * the file name the zeroth entry in the errorv,
+ * and the linenumber the second.
+ * Return the new categorization of the error class.
+ */
+Errorclass discardit(errorp)
+ reg Eptr errorp;
+{
+ int language;
+ reg int i;
+ Errorclass errorclass = errorp->error_e_class;
+
+ switch(errorclass){
+ case C_SYNC:
+ case C_NONSPEC:
+ case C_UNKNOWN: return(errorclass);
+ default: ;
+ }
+ if(errorp->error_lgtext < 2){
+ return(C_NONSPEC);
+ }
+ language = errorp->error_language;
+ if(language == INLINT){
+ if (errorclass != C_NONSPEC){ /* no file */
+ for(i=0; lint_libs[i] != 0; i++){
+ if (strcmp(errorp->error_text[0], lint_libs[i]) == 0){
+ return(C_DISCARD);
+ }
+ }
+ }
+ /* check if the argument to the error message is to be ignored*/
+ if (ispunct(lastchar(errorp->error_text[2])))
+ clob_last(errorp->error_text[2], '\0');
+ if (search_ignore(errorp->error_text[errorclass == C_NONSPEC ? 0 : 2]) >= 0){
+ return(C_NULLED);
+ }
+ }
+ return(errorclass);
+}
diff --git a/usr.bin/error/input.c b/usr.bin/error/input.c
new file mode 100644
index 0000000..6edd5f7
--- /dev/null
+++ b/usr.bin/error/input.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+
+int wordc; /* how long the current error message is */
+char **wordv; /* the actual error message */
+
+int nerrors;
+int language;
+
+Errorclass onelong();
+Errorclass cpp();
+Errorclass pccccom(); /* Portable C Compiler C Compiler */
+Errorclass richieccom(); /* Richie Compiler for 11 */
+Errorclass lint0();
+Errorclass lint1();
+Errorclass lint2();
+Errorclass lint3();
+Errorclass make();
+Errorclass f77();
+Errorclass pi();
+Errorclass ri();
+Errorclass troff();
+Errorclass mod2();
+/*
+ * Eat all of the lines in the input file, attempting to categorize
+ * them by their various flavors
+ */
+static char inbuffer[BUFSIZ];
+
+eaterrors(r_errorc, r_errorv)
+ int *r_errorc;
+ Eptr **r_errorv;
+{
+ extern boolean piflag;
+ Errorclass errorclass = C_SYNC;
+
+ for (;;){
+ if (fgets(inbuffer, BUFSIZ, errorfile) == NULL)
+ break;
+ wordvbuild(inbuffer, &wordc, &wordv);
+ /*
+ * for convience, convert wordv to be 1 based, instead
+ * of 0 based.
+ */
+ wordv -= 1;
+ if ( wordc > 0 &&
+ ((( errorclass = onelong() ) != C_UNKNOWN)
+ || (( errorclass = cpp() ) != C_UNKNOWN)
+ || (( errorclass = pccccom() ) != C_UNKNOWN)
+ || (( errorclass = richieccom() ) != C_UNKNOWN)
+ || (( errorclass = lint0() ) != C_UNKNOWN)
+ || (( errorclass = lint1() ) != C_UNKNOWN)
+ || (( errorclass = lint2() ) != C_UNKNOWN)
+ || (( errorclass = lint3() ) != C_UNKNOWN)
+ || (( errorclass = make() ) != C_UNKNOWN)
+ || (( errorclass = f77() ) != C_UNKNOWN)
+ || ((errorclass = pi() ) != C_UNKNOWN)
+ || (( errorclass = ri() )!= C_UNKNOWN)
+ || (( errorclass = mod2() )!= C_UNKNOWN)
+ || (( errorclass = troff() )!= C_UNKNOWN))
+ ) ;
+ else
+ errorclass = catchall();
+ if (wordc)
+ erroradd(wordc, wordv+1, errorclass, C_UNKNOWN);
+ }
+#ifdef FULLDEBUG
+ printf("%d errorentrys\n", nerrors);
+#endif
+ arrayify(r_errorc, r_errorv, er_head);
+}
+
+/*
+ * create a new error entry, given a zero based array and count
+ */
+erroradd(errorlength, errorv, errorclass, errorsubclass)
+ int errorlength;
+ char **errorv;
+ Errorclass errorclass;
+ Errorclass errorsubclass;
+{
+ reg Eptr newerror;
+ reg char *cp;
+
+ if (errorclass == C_TRUE){
+ /* check canonicalization of the second argument*/
+ for(cp = errorv[1]; *cp && isdigit(*cp); cp++)
+ continue;
+ errorclass = (*cp == '\0') ? C_TRUE : C_NONSPEC;
+#ifdef FULLDEBUG
+ if (errorclass != C_TRUE)
+ printf("The 2nd word, \"%s\" is not a number.\n",
+ errorv[1]);
+#endif
+ }
+ if (errorlength > 0){
+ newerror = (Eptr)Calloc(1, sizeof(Edesc));
+ newerror->error_language = language; /* language is global */
+ newerror->error_text = errorv;
+ newerror->error_lgtext = errorlength;
+ if (errorclass == C_TRUE)
+ newerror->error_line = atoi(errorv[1]);
+ newerror->error_e_class = errorclass;
+ newerror->error_s_class = errorsubclass;
+ switch(newerror->error_e_class = discardit(newerror)){
+ case C_SYNC: nsyncerrors++; break;
+ case C_DISCARD: ndiscard++; break;
+ case C_NULLED: nnulled++; break;
+ case C_NONSPEC: nnonspec++; break;
+ case C_THISFILE: nthisfile++; break;
+ case C_TRUE: ntrue++; break;
+ case C_UNKNOWN: nunknown++; break;
+ case C_IGNORE: nignore++; break;
+ }
+ newerror->error_next = er_head;
+ er_head = newerror;
+ newerror->error_no = nerrors++;
+ } /* length > 0 */
+}
+
+Errorclass onelong()
+{
+ char **nwordv;
+ if ( (wordc == 1) && (language != INLD) ){
+ /*
+ * We have either:
+ * a) file name from cc
+ * b) Assembler telling world that it is complaining
+ * c) Noise from make ("Stop.")
+ * c) Random noise
+ */
+ wordc = 0;
+ if (strcmp(wordv[1], "Stop.") == 0){
+ language = INMAKE; return(C_SYNC);
+ }
+ if (strcmp(wordv[1], "Assembler:") == 0){
+ /* assembler always alerts us to what happened*/
+ language = INAS; return(C_SYNC);
+ } else
+ if (strcmp(wordv[1], "Undefined:") == 0){
+ /* loader complains about unknown symbols*/
+ language = INLD; return(C_SYNC);
+ }
+ if (lastchar(wordv[1]) == ':'){
+ /* cc tells us what file we are in */
+ currentfilename = wordv[1];
+ (void)substitute(currentfilename, ':', '\0');
+ language = INCC; return(C_SYNC);
+ }
+ } else
+ if ( (wordc == 1) && (language == INLD) ){
+ nwordv = (char **)Calloc(4, sizeof(char *));
+ nwordv[0] = "ld:";
+ nwordv[1] = wordv[1];
+ nwordv[2] = "is";
+ nwordv[3] = "undefined.";
+ wordc = 4;
+ wordv = nwordv - 1;
+ return(C_NONSPEC);
+ } else
+ if (wordc == 1){
+ return(C_SYNC);
+ }
+ return(C_UNKNOWN);
+} /* end of one long */
+
+Errorclass cpp()
+{
+ /*
+ * Now attempt a cpp error message match
+ * Examples:
+ * ./morse.h: 23: undefined control
+ * morsesend.c: 229: MAGNIBBL: argument mismatch
+ * morsesend.c: 237: MAGNIBBL: argument mismatch
+ * test1.c: 6: undefined control
+ */
+ if ( (language != INLD) /* loader errors have almost same fmt*/
+ && (lastchar(wordv[1]) == ':')
+ && (isdigit(firstchar(wordv[2])))
+ && (lastchar(wordv[2]) == ':') ){
+ language = INCPP;
+ clob_last(wordv[1], '\0');
+ clob_last(wordv[2], '\0');
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+} /*end of cpp*/
+
+Errorclass pccccom()
+{
+ /*
+ * Now attempt a ccom error message match:
+ * Examples:
+ * "morsesend.c", line 237: operands of & have incompatible types
+ * "test.c", line 7: warning: old-fashioned initialization: use =
+ * "subdir.d/foo2.h", line 1: illegal initialization
+ */
+ if ( (firstchar(wordv[1]) == '"')
+ && (lastchar(wordv[1]) == ',')
+ && (next_lastchar(wordv[1]) == '"')
+ && (strcmp(wordv[2],"line") == 0)
+ && (isdigit(firstchar(wordv[3])))
+ && (lastchar(wordv[3]) == ':') ){
+ clob_last(wordv[1], '\0'); /* drop last , */
+ clob_last(wordv[1], '\0'); /* drop last " */
+ wordv[1]++; /* drop first " */
+ clob_last(wordv[3], '\0'); /* drop : on line number */
+ wordv[2] = wordv[1]; /* overwrite "line" */
+ wordv++; /*compensate*/
+ wordc--;
+ currentfilename = wordv[1];
+ language = INCC;
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+} /* end of ccom */
+/*
+ * Do the error message from the Richie C Compiler for the PDP11,
+ * which has this source:
+ *
+ * if (filename[0])
+ * fprintf(stderr, "%s:", filename);
+ * fprintf(stderr, "%d: ", line);
+ *
+ */
+Errorclass richieccom()
+{
+ reg char *cp;
+ reg char **nwordv;
+ char *file;
+
+ if (lastchar(wordv[1]) == ':'){
+ cp = wordv[1] + strlen(wordv[1]) - 1;
+ while (isdigit(*--cp))
+ continue;
+ if (*cp == ':'){
+ clob_last(wordv[1], '\0'); /* last : */
+ *cp = '\0'; /* first : */
+ file = wordv[1];
+ nwordv = wordvsplice(1, wordc, wordv+1);
+ nwordv[0] = file;
+ nwordv[1] = cp + 1;
+ wordc += 1;
+ wordv = nwordv - 1;
+ language = INCC;
+ currentfilename = wordv[1];
+ return(C_TRUE);
+ }
+ }
+ return(C_UNKNOWN);
+}
+
+Errorclass lint0()
+{
+ reg char **nwordv;
+ char *line, *file;
+ /*
+ * Attempt a match for the new lint style normal compiler
+ * error messages, of the form
+ *
+ * printf("%s(%d): %s\n", filename, linenumber, message);
+ */
+ if (wordc >= 2){
+ if ( (lastchar(wordv[1]) == ':')
+ && (next_lastchar(wordv[1]) == ')')
+ ) {
+ clob_last(wordv[1], '\0'); /* colon */
+ if (persperdexplode(wordv[1], &line, &file)){
+ nwordv = wordvsplice(1, wordc, wordv+1);
+ nwordv[0] = file; /* file name */
+ nwordv[1] = line; /* line number */
+ wordc += 1;
+ wordv = nwordv - 1;
+ language = INLINT;
+ return(C_TRUE);
+ }
+ wordv[1][strlen(wordv[1])] = ':';
+ }
+ }
+ return (C_UNKNOWN);
+}
+
+Errorclass lint1()
+{
+ char *line1, *line2;
+ char *file1, *file2;
+ char **nwordv1, **nwordv2;
+
+ /*
+ * Now, attempt a match for the various errors that lint
+ * can complain about.
+ *
+ * Look first for type 1 lint errors
+ */
+ if (wordc > 1 && strcmp(wordv[wordc-1], "::") == 0){
+ /*
+ * %.7s, arg. %d used inconsistently %s(%d) :: %s(%d)
+ * %.7s value used inconsistently %s(%d) :: %s(%d)
+ * %.7s multiply declared %s(%d) :: %s(%d)
+ * %.7s value declared inconsistently %s(%d) :: %s(%d)
+ * %.7s function value type must be declared before use %s(%d) :: %s(%d)
+ */
+ language = INLINT;
+ if (wordc > 2
+ && (persperdexplode(wordv[wordc], &line2, &file2))
+ && (persperdexplode(wordv[wordc-2], &line1, &file1)) ){
+ nwordv1 = wordvsplice(2, wordc, wordv+1);
+ nwordv2 = wordvsplice(2, wordc, wordv+1);
+ nwordv1[0] = file1; nwordv1[1] = line1;
+ erroradd(wordc+2, nwordv1, C_TRUE, C_DUPL); /* takes 0 based*/
+ nwordv2[0] = file2; nwordv2[1] = line2;
+ wordc = wordc + 2;
+ wordv = nwordv2 - 1; /* 1 based */
+ return(C_TRUE);
+ }
+ }
+ return(C_UNKNOWN);
+} /* end of lint 1*/
+
+Errorclass lint2()
+{
+ char *file;
+ char *line;
+ char **nwordv;
+ /*
+ * Look for type 2 lint errors
+ *
+ * %.7s used( %s(%d) ), but not defined
+ * %.7s defined( %s(%d) ), but never used
+ * %.7s declared( %s(%d) ), but never used or defined
+ *
+ * bufp defined( "./metric.h"(10) ), but never used
+ */
+ if ( (lastchar(wordv[2]) == '(' /* ')' */ )
+ && (strcmp(wordv[4], "),") == 0) ){
+ language = INLINT;
+ if (persperdexplode(wordv[3], &line, &file)){
+ nwordv = wordvsplice(2, wordc, wordv+1);
+ nwordv[0] = file; nwordv[1] = line;
+ wordc = wordc + 2;
+ wordv = nwordv - 1; /* 1 based */
+ return(C_TRUE);
+ }
+ }
+ return(C_UNKNOWN);
+} /* end of lint 2*/
+
+char *Lint31[4] = {"returns", "value", "which", "is"};
+char *Lint32[6] = {"value", "is", "used,", "but", "none", "returned"};
+Errorclass lint3()
+{
+ if ( (wordvcmp(wordv+2, 4, Lint31) == 0)
+ || (wordvcmp(wordv+2, 6, Lint32) == 0) ){
+ language = INLINT;
+ return(C_NONSPEC);
+ }
+ return(C_UNKNOWN);
+}
+
+/*
+ * Special word vectors for use by F77 recognition
+ */
+char *F77_fatal[3] = {"Compiler", "error", "line"};
+char *F77_error[3] = {"Error", "on", "line"};
+char *F77_warning[3] = {"Warning", "on", "line"};
+char *F77_no_ass[3] = {"Error.","No","assembly."};
+f77()
+{
+ char **nwordv;
+ /*
+ * look for f77 errors:
+ * Error messages from /usr/src/cmd/f77/error.c, with
+ * these printf formats:
+ *
+ * Compiler error line %d of %s: %s
+ * Error on line %d of %s: %s
+ * Warning on line %d of %s: %s
+ * Error. No assembly.
+ */
+ if (wordc == 3 && wordvcmp(wordv+1, 3, F77_no_ass) == 0) {
+ wordc = 0;
+ return(C_SYNC);
+ }
+ if (wordc < 6)
+ return(C_UNKNOWN);
+ if ( (lastchar(wordv[6]) == ':')
+ &&(
+ (wordvcmp(wordv+1, 3, F77_fatal) == 0)
+ || (wordvcmp(wordv+1, 3, F77_error) == 0)
+ || (wordvcmp(wordv+1, 3, F77_warning) == 0) )
+ ){
+ language = INF77;
+ nwordv = wordvsplice(2, wordc, wordv+1);
+ nwordv[0] = wordv[6];
+ clob_last(nwordv[0],'\0');
+ nwordv[1] = wordv[4];
+ wordc += 2;
+ wordv = nwordv - 1; /* 1 based */
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+} /* end of f77 */
+
+char *Make_Croak[3] = {"***", "Error", "code"};
+char *Make_NotRemade[5] = {"not", "remade", "because", "of", "errors"};
+Errorclass make()
+{
+ if (wordvcmp(wordv+1, 3, Make_Croak) == 0){
+ language = INMAKE;
+ return(C_SYNC);
+ }
+ if (wordvcmp(wordv+2, 5, Make_NotRemade) == 0){
+ language = INMAKE;
+ return(C_SYNC);
+ }
+ return(C_UNKNOWN);
+}
+Errorclass ri()
+{
+/*
+ * Match an error message produced by ri; here is the
+ * procedure yanked from the distributed version of ri
+ * April 24, 1980.
+ *
+ * serror(str, x1, x2, x3)
+ * char str[];
+ * char *x1, *x2, *x3;
+ * {
+ * extern int yylineno;
+ *
+ * putc('"', stdout);
+ * fputs(srcfile, stdout);
+ * putc('"', stdout);
+ * fprintf(stdout, " %d: ", yylineno);
+ * fprintf(stdout, str, x1, x2, x3);
+ * fprintf(stdout, "\n");
+ * synerrs++;
+ * }
+ */
+ if ( (firstchar(wordv[1]) == '"')
+ &&(lastchar(wordv[1]) == '"')
+ &&(lastchar(wordv[2]) == ':')
+ &&(isdigit(firstchar(wordv[2]))) ){
+ clob_last(wordv[1], '\0'); /* drop the last " */
+ wordv[1]++; /* skip over the first " */
+ clob_last(wordv[2], '\0');
+ language = INRI;
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+}
+
+Errorclass catchall()
+{
+ /*
+ * Catches random things.
+ */
+ language = INUNKNOWN;
+ return(C_NONSPEC);
+} /* end of catch all*/
+
+Errorclass troff()
+{
+ /*
+ * troff source error message, from eqn, bib, tbl...
+ * Just like pcc ccom, except uses `'
+ */
+ if ( (firstchar(wordv[1]) == '`')
+ && (lastchar(wordv[1]) == ',')
+ && (next_lastchar(wordv[1]) == '\'')
+ && (strcmp(wordv[2],"line") == 0)
+ && (isdigit(firstchar(wordv[3])))
+ && (lastchar(wordv[3]) == ':') ){
+ clob_last(wordv[1], '\0'); /* drop last , */
+ clob_last(wordv[1], '\0'); /* drop last " */
+ wordv[1]++; /* drop first " */
+ clob_last(wordv[3], '\0'); /* drop : on line number */
+ wordv[2] = wordv[1]; /* overwrite "line" */
+ wordv++; /*compensate*/
+ currentfilename = wordv[1];
+ language = INTROFF;
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+}
+Errorclass mod2()
+{
+ /*
+ * for decwrl modula2 compiler (powell)
+ */
+ if ( ( (strcmp(wordv[1], "!!!") == 0) /* early version */
+ ||(strcmp(wordv[1], "File") == 0)) /* later version */
+ && (lastchar(wordv[2]) == ',') /* file name */
+ && (strcmp(wordv[3], "line") == 0)
+ && (isdigit(firstchar(wordv[4]))) /* line number */
+ && (lastchar(wordv[4]) == ':') /* line number */
+ ){
+ clob_last(wordv[2], '\0'); /* drop last , on file name */
+ clob_last(wordv[4], '\0'); /* drop last : on line number */
+ wordv[3] = wordv[2]; /* file name on top of "line" */
+ wordv += 2;
+ wordc -= 2;
+ currentfilename = wordv[1];
+ language = INMOD2;
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+}
diff --git a/usr.bin/error/main.c b/usr.bin/error/main.c
new file mode 100644
index 0000000..0ba1be6
--- /dev/null
+++ b/usr.bin/error/main.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "pathnames.h"
+
+int nerrors = 0;
+Eptr er_head;
+Eptr *errors;
+
+int nfiles = 0;
+Eptr **files; /* array of pointers into errors*/
+int language = INCC;
+
+char *currentfilename = "????";
+char *processname;
+char im_on[] = _PATH_TTY; /* my tty name */
+
+boolean query = FALSE; /* query the operator if touch files */
+boolean notouch = FALSE; /* don't touch ANY files */
+boolean piflag = FALSE; /* this is not pi */
+boolean terse = FALSE; /* Terse output */
+
+char *suffixlist = ".*"; /* initially, can touch any file */
+
+int errorsort();
+void onintr();
+/*
+ * error [-I ignorename] [-n] [-q] [-t suffixlist] [-s] [-v] [infile]
+ *
+ * -T: terse output
+ *
+ * -I: the following name, `ignorename' contains a list of
+ * function names that are not to be treated as hard errors.
+ * Default: ~/.errorsrc
+ *
+ * -n: don't touch ANY files!
+ *
+ * -q: The user is to be queried before touching each
+ * file; if not specified, all files with hard, non
+ * ignorable errors are touched (assuming they can be).
+ *
+ * -t: touch only files ending with the list of suffices, each
+ * suffix preceded by a dot.
+ * eg, -t .c.y.l
+ * will touch only files ending with .c, .y or .l
+ *
+ * -s: print a summary of the error's categories.
+ *
+ * -v: after touching all files, overlay vi(1), ex(1) or ed(1)
+ * on top of error, entered in the first file with
+ * an error in it, with the appropriate editor
+ * set up to use the "next" command to get the other
+ * files containing errors.
+ *
+ * -p: (obsolete: for older versions of pi without bug
+ * fix regarding printing out the name of the main file
+ * with an error in it)
+ * Take the following argument and use it as the name of
+ * the pascal source file, suffix .p
+ *
+ * -E: show the errors in sorted order; intended for
+ * debugging.
+ *
+ * -S: show the errors in unsorted order
+ * (as they come from the error file)
+ *
+ * infile: The error messages come from this file.
+ * Default: stdin
+ */
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+ char *ignorename = 0;
+ int ed_argc;
+ char **ed_argv; /*return from touchfiles*/
+ boolean show_errors = FALSE;
+ boolean Show_Errors = FALSE;
+ boolean pr_summary = FALSE;
+ boolean edit_files = FALSE;
+
+ processname = argv[0];
+
+ errorfile = stdin;
+ if (argc > 1) for(; (argc > 1) && (argv[1][0] == '-'); argc--, argv++){
+ for (cp = argv[1] + 1; *cp; cp++) switch(*cp){
+ default:
+ fprintf(stderr, "%s: -%c: Unknown flag\n",
+ processname, *cp);
+ break;
+
+ case 'n': notouch = TRUE; break;
+ case 'q': query = TRUE; break;
+ case 'S': Show_Errors = TRUE; break;
+ case 's': pr_summary = TRUE; break;
+ case 'v': edit_files = TRUE; break;
+ case 'T': terse = TRUE; break;
+ case 't':
+ *cp-- = 0; argv++; argc--;
+ if (argc > 1){
+ suffixlist = argv[1];
+ }
+ break;
+ case 'I': /*ignore file name*/
+ *cp-- = 0; argv++; argc--;
+ if (argc > 1)
+ ignorename = argv[1];
+ break;
+ }
+ }
+ if (notouch)
+ suffixlist = 0;
+ if (argc > 1){
+ if (argc > 3){
+ fprintf(stderr, "%s: Only takes 0 or 1 arguments\n",
+ processname);
+ exit(3);
+ }
+ if ( (errorfile = fopen(argv[1], "r")) == NULL){
+ fprintf(stderr, "%s: %s: No such file or directory for reading errors.\n",
+ processname, argv[1]);
+ exit(4);
+ }
+ }
+ if ( (queryfile = fopen(im_on, "r")) == NULL){
+ if (query){
+ fprintf(stderr,
+ "%s: Can't open \"%s\" to query the user.\n",
+ processname, im_on);
+ exit(9);
+ }
+ }
+ if (signal(SIGINT, onintr) == SIG_IGN)
+ signal(SIGINT, SIG_IGN);
+ if (signal(SIGTERM, onintr) == SIG_IGN)
+ signal(SIGTERM, SIG_IGN);
+ getignored(ignorename);
+ eaterrors(&nerrors, &errors);
+ if (Show_Errors)
+ printerrors(TRUE, nerrors, errors);
+ qsort(errors, nerrors, sizeof(Eptr), errorsort);
+ if (show_errors)
+ printerrors(FALSE, nerrors, errors);
+ findfiles(nerrors, errors, &nfiles, &files);
+#define P(msg, arg) fprintf(stdout, msg, arg)
+ if (pr_summary){
+ if (nunknown)
+ P("%d Errors are unclassifiable.\n", nunknown);
+ if (nignore)
+ P("%d Errors are classifiable, but totally discarded.\n",nignore);
+ if (nsyncerrors)
+ P("%d Errors are synchronization errors.\n", nsyncerrors);
+ if (nignore)
+ P("%d Errors are discarded because they refer to sacrosinct files.\n", ndiscard);
+ if (nnulled)
+ P("%d Errors are nulled because they refer to specific functions.\n", nnulled);
+ if (nnonspec)
+ P("%d Errors are not specific to any file.\n", nnonspec);
+ if (nthisfile)
+ P("%d Errors are specific to a given file, but not to a line.\n", nthisfile);
+ if (ntrue)
+ P("%d Errors are true errors, and can be inserted into the files.\n", ntrue);
+ }
+ filenames(nfiles, files);
+ fflush(stdout);
+ if (touchfiles(nfiles, files, &ed_argc, &ed_argv) && edit_files)
+ forkvi(ed_argc, ed_argv);
+}
+
+forkvi(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (query){
+ switch(inquire(terse
+ ? "Edit? "
+ : "Do you still want to edit the files you touched? ")){
+ case Q_NO:
+ case Q_no:
+ return;
+ default:
+ break;
+ }
+ }
+ /*
+ * ed_agument's first argument is
+ * a vi/ex compatabile search argument
+ * to find the first occurance of ###
+ */
+ try("vi", argc, argv);
+ try("ex", argc, argv);
+ try("ed", argc-1, argv+1);
+ fprintf(stdout, "Can't find any editors.\n");
+}
+
+try(name, argc, argv)
+ char *name;
+ int argc;
+ char **argv;
+{
+ argv[0] = name;
+ wordvprint(stdout, argc, argv);
+ fprintf(stdout, "\n");
+ fflush(stderr);
+ fflush(stdout);
+ sleep(2);
+ if (freopen(im_on, "r", stdin) == NULL)
+ return;
+ if (freopen(im_on, "w", stdout) == NULL)
+ return;
+ execvp(name, argv);
+}
+
+int errorsort(epp1, epp2)
+ Eptr *epp1, *epp2;
+{
+ reg Eptr ep1, ep2;
+ int order;
+ /*
+ * Sort by:
+ * 1) synchronization, non specific, discarded errors first;
+ * 2) nulled and true errors last
+ * a) grouped by similar file names
+ * 1) grouped in ascending line number
+ */
+ ep1 = *epp1; ep2 = *epp2;
+ if (ep1 == 0 || ep2 == 0)
+ return(0);
+ if ( (NOTSORTABLE(ep1->error_e_class)) ^ (NOTSORTABLE(ep2->error_e_class))){
+ return(NOTSORTABLE(ep1->error_e_class) ? -1 : 1);
+ }
+ if (NOTSORTABLE(ep1->error_e_class)) /* then both are */
+ return(ep1->error_no - ep2->error_no);
+ order = strcmp(ep1->error_text[0], ep2->error_text[0]);
+ if (order == 0){
+ return(ep1->error_line - ep2->error_line);
+ }
+ return(order);
+}
diff --git a/usr.bin/error/pathnames.h b/usr.bin/error/pathnames.h
new file mode 100644
index 0000000..add09fb
--- /dev/null
+++ b/usr.bin/error/pathnames.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define IG_FILE1 "llib-lc"
+#define IG_FILE2 "llib-port"
+#define IG_FILE3 "/usr/lib/llib-lc"
+#define IG_FILE4 "/usr/lib/llib-port"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/ErrorXXXXXX"
diff --git a/usr.bin/error/pi.c b/usr.bin/error/pi.c
new file mode 100644
index 0000000..4fd70f9
--- /dev/null
+++ b/usr.bin/error/pi.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pi.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "error.h"
+
+extern char *currentfilename;
+static char *c_linenumber;
+static char *unk_hdr[] = {"In", "program", "???"};
+static char **c_header = &unk_hdr[0];
+
+/*
+ * Attempt to handle error messages produced by pi (and by pc)
+ *
+ * problem #1: There is no file name available when a file does not
+ * use a #include; this will have to be given to error
+ * in the command line.
+ * problem #2: pi doesn't always tell you what line number
+ * a error refers to; for example during the tree
+ * walk phase of code generation and error detection,
+ * an error can refer to "variable foo in procedure bletch"
+ * without giving a line number
+ * problem #3: line numbers, when available, are attached to
+ * the source line, along with the source line itself
+ * These line numbers must be extracted, and
+ * the source line thrown away.
+ * problem #4: Some error messages produce more than one line number
+ * on the same message.
+ * There are only two (I think):
+ * %s undefined on line%s
+ * %s improperly used on line%s
+ * here, the %s makes line plural or singular.
+ *
+ * Here are the error strings used in pi version 1.2 that can refer
+ * to a file name or line number:
+ *
+ * Multiply defined label in case, lines %d and %d
+ * Goto %s from line %d is into a structured statement
+ * End matched %s on line %d
+ * Inserted keyword end matching %s on line %d
+ *
+ * Here are the general pi patterns recognized:
+ * define piptr == -.*^-.*
+ * define msg = .*
+ * define digit = [0-9]
+ * definename = .*
+ * define date_format letter*3 letter*3 (digit | (digit digit))
+ * (digit | (digit digit)):digit*2 digit*4
+ *
+ * {e,E} (piptr) (msg) Encounter an error during textual scan
+ * E {digit}* - (msg) Have an error message that refers to a new line
+ * E - msg Have an error message that refers to current
+ * function, program or procedure
+ * (date_format) (name): When switch compilation files
+ * ... (msg) When refer to the previous line
+ * 'In' ('procedure'|'function'|'program') (name):
+ * pi is now complaining about 2nd pass errors.
+ *
+ * Here is the output from a compilation
+ *
+ *
+ * 2 var i:integer;
+ * e --------------^--- Inserted ';'
+ * E 2 - All variables must be declared in one var part
+ * E 5 - Include filename must end in .i
+ * Mon Apr 21 15:56 1980 test.h:
+ * 2 begin
+ * e ------^--- Inserted ';'
+ * Mon Apr 21 16:06 1980 test.p:
+ * E 2 - Function type must be specified
+ * 6 procedure foo(var x:real);
+ * e ------^--- Inserted ';'
+ * In function bletch:
+ * E - No assignment to the function variable
+ * w - variable x is never used
+ * E 6 - foo is already defined in this block
+ * In procedure foo:
+ * w - variable x is neither used nor set
+ * 9 z : = 23;
+ * E --------------^--- Undefined variable
+ * 10 y = [1];
+ * e ----------------^--- Inserted ':'
+ * 13 z := 345.;
+ * e -----------------------^--- Digits required after decimal point
+ * E 10 - Constant set involved in non set context
+ * E 11 - Type clash: real is incompatible with integer
+ * ... Type of expression clashed with type of variable in assignment
+ * E 12 - Parameter type not identical to type of var parameter x of foo
+ * In program mung:
+ * w - variable y is never used
+ * w - type foo is never used
+ * w - function bletch is never used
+ * E - z undefined on lines 9 13
+ */
+char *Months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct","Nov", "Dec",
+ 0
+};
+char *Days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0
+};
+char *Piroutines[] = {
+ "program", "function", "procedure", 0
+};
+
+
+static boolean structured, multiple;
+
+char *pi_Endmatched[] = {"End", "matched"};
+char *pi_Inserted[] = {"Inserted", "keyword", "end", "matching"};
+
+char *pi_multiple[] = {"Mutiply", "defined", "label", "in", "case,", "line"};
+char *pi_structured[] = {"is", "into", "a", "structured", "statement"};
+
+char *pi_und1[] = {"undefined", "on", "line"};
+char *pi_und2[] = {"undefined", "on", "lines"};
+char *pi_imp1[] = {"improperly", "used", "on", "line"};
+char *pi_imp2[] = {"improperly", "used", "on", "lines"};
+
+boolean alldigits(string)
+ reg char *string;
+{
+ for (; *string && isdigit(*string); string++)
+ continue;
+ return(*string == '\0');
+}
+boolean instringset(member, set)
+ char *member;
+ reg char **set;
+{
+ for(; *set; set++){
+ if (strcmp(*set, member) == 0)
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+boolean isdateformat(wordc, wordv)
+ int wordc;
+ char **wordv;
+{
+ return(
+ (wordc == 5)
+ && (instringset(wordv[0], Days))
+ && (instringset(wordv[1], Months))
+ && (alldigits(wordv[2]))
+ && (alldigits(wordv[4])) );
+}
+
+boolean piptr(string)
+ reg char *string;
+{
+ if (*string != '-')
+ return(FALSE);
+ while (*string && *string == '-')
+ string++;
+ if (*string != '^')
+ return(FALSE);
+ string++;
+ while (*string && *string == '-')
+ string++;
+ return(*string == '\0');
+}
+
+extern int wordc;
+extern char **wordv;
+
+Errorclass pi()
+{
+ char **nwordv;
+
+ if (wordc < 2)
+ return (C_UNKNOWN);
+ if ( ( strlen(wordv[1]) == 1)
+ && ( (wordv[1][0] == 'e') || (wordv[1][0] == 'E') )
+ && ( piptr(wordv[2]) )
+ ) {
+ boolean longpiptr = 0;
+ /*
+ * We have recognized a first pass error of the form:
+ * letter ------^---- message
+ *
+ * turn into an error message of the form:
+ *
+ * file line 'pascal errortype' letter \n |---- message
+ * or of the form:
+ * file line letter |---- message
+ * when there are strlen("(*[pi]") or more
+ * preceding '-' on the error pointer.
+ *
+ * Where the | is intended to be a down arrow, so that
+ * the pi error messages can be inserted above the
+ * line in error, instead of below. (All of the other
+ * langauges put thier messages before the source line,
+ * instead of after it as does pi.)
+ *
+ * where the pointer to the error has been truncated
+ * by 6 characters to account for the fact that
+ * the pointer points into a tab preceded input line.
+ */
+ language = INPI;
+ (void)substitute(wordv[2], '^', '|');
+ longpiptr = position(wordv[2],'|') > (6+8);
+ nwordv = wordvsplice(longpiptr ? 2 : 4, wordc, wordv+1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = strsave(c_linenumber);
+ if (!longpiptr){
+ nwordv[2] = "pascal errortype";
+ nwordv[3] = wordv[1];
+ nwordv[4] = strsave("%%%\n");
+ if (strlen(nwordv[5]) > (8-2)) /* this is the pointer */
+ nwordv[5] += (8-2); /* bump over 6 characters */
+ }
+ wordv = nwordv - 1; /* convert to 1 based */
+ wordc += longpiptr ? 2 : 4;
+ return(C_TRUE);
+ }
+ if ( (wordc >= 4)
+ && (strlen(wordv[1]) == 1)
+ && ( (*wordv[1] == 'E') || (*wordv[1] == 'w') || (*wordv[1] == 'e') )
+ && (alldigits(wordv[2]))
+ && (strlen(wordv[3]) == 1)
+ && (wordv[3][0] == '-')
+ ){
+ /*
+ * Message of the form: letter linenumber - message
+ * Turn into form: filename linenumber letter - message
+ */
+ language = INPI;
+ nwordv = wordvsplice(1, wordc, wordv + 1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = wordv[2];
+ nwordv[2] = wordv[1];
+ c_linenumber = wordv[2];
+ wordc += 1;
+ wordv = nwordv - 1;
+ return(C_TRUE);
+ }
+ if ( (wordc >= 3)
+ && (strlen(wordv[1]) == 1)
+ && ( (*(wordv[1]) == 'E') || (*(wordv[1]) == 'w') || (*(wordv[1]) == 'e') )
+ && (strlen(wordv[2]) == 1)
+ && (wordv[2][0] == '-')
+ ) {
+ /*
+ * Message of the form: letter - message
+ * This happens only when we are traversing the tree
+ * during the second pass of pi, and discover semantic
+ * errors.
+ *
+ * We have already (presumably) saved the header message
+ * and can now construct a nulled error message for the
+ * current file.
+ *
+ * Turns into a message of the form:
+ * filename (header) letter - message
+ *
+ * First, see if it is a message referring to more than
+ * one line number. Only of the form:
+ * %s undefined on line%s
+ * %s improperly used on line%s
+ */
+ boolean undefined = 0;
+ int wordindex;
+
+ language = INPI;
+ if ( (undefined = (wordvcmp(wordv+2, 3, pi_und1) == 0) )
+ || (undefined = (wordvcmp(wordv+2, 3, pi_und2) == 0) )
+ || (wordvcmp(wordv+2, 4, pi_imp1) == 0)
+ || (wordvcmp(wordv+2, 4, pi_imp2) == 0)
+ ){
+ for (wordindex = undefined ? 5 : 6; wordindex <= wordc;
+ wordindex++){
+ nwordv = wordvsplice(2, undefined ? 2 : 3, wordv+1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = wordv[wordindex];
+ if (wordindex != wordc)
+ erroradd(undefined ? 4 : 5, nwordv,
+ C_TRUE, C_UNKNOWN);
+ }
+ wordc = undefined ? 4 : 5;
+ wordv = nwordv - 1;
+ return(C_TRUE);
+ }
+
+ nwordv = wordvsplice(1+3, wordc, wordv+1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = strsave(c_header[0]);
+ nwordv[2] = strsave(c_header[1]);
+ nwordv[3] = strsave(c_header[2]);
+ wordv = nwordv - 1;
+ wordc += 1 + 3;
+ return(C_THISFILE);
+ }
+ if (strcmp(wordv[1], "...") == 0){
+ /*
+ * have a continuation error message
+ * of the form: ... message
+ * Turn into form : filename linenumber message
+ */
+ language = INPI;
+ nwordv = wordvsplice(1, wordc, wordv+1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = strsave(c_linenumber);
+ wordv = nwordv - 1;
+ wordc += 1;
+ return(C_TRUE);
+ }
+ if( (wordc == 6)
+ && (lastchar(wordv[6]) == ':')
+ && (isdateformat(5, wordv + 1))
+ ){
+ /*
+ * Have message that tells us we have changed files
+ */
+ language = INPI;
+ currentfilename = strsave(wordv[6]);
+ clob_last(currentfilename, '\0');
+ return(C_SYNC);
+ }
+ if( (wordc == 3)
+ && (strcmp(wordv[1], "In") == 0)
+ && (lastchar(wordv[3]) == ':')
+ && (instringset(wordv[2], Piroutines))
+ ) {
+ language = INPI;
+ c_header = wordvsplice(0, wordc, wordv+1);
+ return(C_SYNC);
+ }
+ /*
+ * now, check for just the line number followed by the text
+ */
+ if (alldigits(wordv[1])){
+ language = INPI;
+ c_linenumber = wordv[1];
+ return(C_IGNORE);
+ }
+ /*
+ * Attempt to match messages refering to a line number
+ *
+ * Multiply defined label in case, lines %d and %d
+ * Goto %s from line %d is into a structured statement
+ * End matched %s on line %d
+ * Inserted keyword end matching %s on line %d
+ */
+ multiple = structured = 0;
+ if (
+ ( (wordc == 6) && (wordvcmp(wordv+1, 2, pi_Endmatched) == 0))
+ || ( (wordc == 8) && (wordvcmp(wordv+1, 4, pi_Inserted) == 0))
+ || ( multiple = ((wordc == 9) && (wordvcmp(wordv+1,6, pi_multiple) == 0) ) )
+ || ( structured = ((wordc == 10) && (wordvcmp(wordv+6,5, pi_structured) == 0 ) ))
+ ){
+ language = INPI;
+ nwordv = wordvsplice(2, wordc, wordv+1);
+ nwordv[0] = strsave(currentfilename);
+ nwordv[1] = structured ? wordv [5] : wordv[wordc];
+ wordc += 2;
+ wordv = nwordv - 1;
+ if (!multiple)
+ return(C_TRUE);
+ erroradd(wordc, nwordv, C_TRUE, C_UNKNOWN);
+ nwordv = wordvsplice(0, wordc, nwordv);
+ nwordv[1] = wordv[wordc - 2];
+ return(C_TRUE);
+ }
+ return(C_UNKNOWN);
+}
diff --git a/usr.bin/error/subr.c b/usr.bin/error/subr.c
new file mode 100644
index 0000000..29b416a
--- /dev/null
+++ b/usr.bin/error/subr.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)subr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+/*
+ * Arrayify a list of rules
+ */
+arrayify(e_length, e_array, header)
+ int *e_length;
+ Eptr **e_array;
+ Eptr header;
+{
+ reg Eptr errorp;
+ reg Eptr *array;
+ reg int listlength;
+ reg int listindex;
+
+ for (errorp = header, listlength = 0;
+ errorp; errorp = errorp->error_next, listlength++)
+ continue;
+ array = (Eptr*)Calloc(listlength+1, sizeof (Eptr));
+ for(listindex = 0, errorp = header;
+ listindex < listlength;
+ listindex++, errorp = errorp->error_next){
+ array[listindex] = errorp;
+ errorp->error_position = listindex;
+ }
+ array[listindex] = (Eptr)0;
+ *e_length = listlength;
+ *e_array = array;
+}
+
+/*VARARGS1*/
+error(msg, a1, a2, a3)
+ char *msg;
+{
+ fprintf(stderr, "Error: ");
+ fprintf(stderr, msg, a1, a2, a3);
+ fprintf(stderr, "\n");
+ fflush(stdout);
+ fflush(stderr);
+ exit(6);
+}
+/*ARGSUSED*/
+char *Calloc(nelements, size)
+ int nelements;
+ int size;
+{
+ char *back;
+ if ( (back = (char *)calloc(nelements, size)) == (char *)NULL){
+ error("Ran out of memory.\n");
+ exit(1);
+ }
+ return(back);
+}
+
+char *strsave(instring)
+ char *instring;
+{
+ char *outstring;
+ (void)strcpy(outstring = (char *)Calloc(1, strlen(instring) + 1),
+ instring);
+ return(outstring);
+}
+/*
+ * find the position of a given character in a string
+ * (one based)
+ */
+int position(string, ch)
+ reg char *string;
+ reg char ch;
+{
+ reg int i;
+ if (string)
+ for (i=1; *string; string++, i++){
+ if (*string == ch)
+ return(i);
+ }
+ return(-1);
+}
+/*
+ * clobber the first occurance of ch in string by the new character
+ */
+char *substitute(string, chold, chnew)
+ char *string;
+ char chold, chnew;
+{
+ reg char *cp = string;
+
+ if (cp)
+ while (*cp){
+ if (*cp == chold){
+ *cp = chnew;
+ break;
+ }
+ cp++;
+ }
+ return(string);
+}
+
+char lastchar(string)
+ char *string;
+{
+ int length;
+ if (string == 0) return('\0');
+ length = strlen(string);
+ if (length >= 1)
+ return(string[length-1]);
+ else
+ return('\0');
+}
+
+char firstchar(string)
+ char *string;
+{
+ if (string)
+ return(string[0]);
+ else
+ return('\0');
+}
+
+char next_lastchar(string)
+ char *string;
+{
+ int length;
+ if (string == 0) return('\0');
+ length = strlen(string);
+ if (length >= 2)
+ return(string[length - 2]);
+ else
+ return('\0');
+}
+
+clob_last(string, newstuff)
+ char *string, newstuff;
+{
+ int length = 0;
+ if (string)
+ length = strlen(string);
+ if (length >= 1)
+ string[length - 1] = newstuff;
+}
+
+/*
+ * parse a string that is the result of a format %s(%d)
+ * return TRUE if this is of the proper format
+ */
+boolean persperdexplode(string, r_perd, r_pers)
+ char *string;
+ char **r_perd, **r_pers;
+{
+ reg char *cp;
+ int length = 0;
+
+ if (string)
+ length = strlen(string);
+ if ( (length >= 4)
+ && (string[length - 1] == ')' ) ){
+ for (cp = &string[length - 2];
+ (isdigit(*cp)) && (*cp != '(');
+ --cp)
+ continue;
+ if (*cp == '('){
+ string[length - 1] = '\0'; /* clobber the ) */
+ *r_perd = strsave(cp+1);
+ string[length - 1] = ')';
+ *cp = '\0'; /* clobber the ( */
+ *r_pers = strsave(string);
+ *cp = '(';
+ return(TRUE);
+ }
+ }
+ return(FALSE);
+}
+/*
+ * parse a quoted string that is the result of a format \"%s\"(%d)
+ * return TRUE if this is of the proper format
+ */
+boolean qpersperdexplode(string, r_perd, r_pers)
+ char *string;
+ char **r_perd, **r_pers;
+{
+ reg char *cp;
+ int length = 0;
+
+ if (string)
+ length = strlen(string);
+ if ( (length >= 4)
+ && (string[length - 1] == ')' ) ){
+ for (cp = &string[length - 2];
+ (isdigit(*cp)) && (*cp != '(');
+ --cp)
+ continue;
+ if (*cp == '(' && *(cp - 1) == '"'){
+ string[length - 1] = '\0';
+ *r_perd = strsave(cp+1);
+ string[length - 1] = ')';
+ *(cp - 1) = '\0'; /* clobber the " */
+ *r_pers = strsave(string + 1);
+ *(cp - 1) = '"';
+ return(TRUE);
+ }
+ }
+ return(FALSE);
+}
+
+static char cincomment[] = CINCOMMENT;
+static char coutcomment[] = COUTCOMMENT;
+static char fincomment[] = FINCOMMENT;
+static char foutcomment[] = FOUTCOMMENT;
+static char newline[] = NEWLINE;
+static char piincomment[] = PIINCOMMENT;
+static char pioutcomment[] = PIOUTCOMMENT;
+static char lispincomment[] = LISPINCOMMENT;
+static char riincomment[] = RIINCOMMENT;
+static char rioutcomment[] = RIOUTCOMMENT;
+static char troffincomment[] = TROFFINCOMMENT;
+static char troffoutcomment[] = TROFFOUTCOMMENT;
+static char mod2incomment[] = MOD2INCOMMENT;
+static char mod2outcomment[] = MOD2OUTCOMMENT;
+
+struct lang_desc lang_table[] = {
+ /*INUNKNOWN 0*/ "unknown", cincomment, coutcomment,
+ /*INCPP 1*/ "cpp", cincomment, coutcomment,
+ /*INCC 2*/ "cc", cincomment, coutcomment,
+ /*INAS 3*/ "as", ASINCOMMENT, newline,
+ /*INLD 4*/ "ld", cincomment, coutcomment,
+ /*INLINT 5*/ "lint", cincomment, coutcomment,
+ /*INF77 6*/ "f77", fincomment, foutcomment,
+ /*INPI 7*/ "pi", piincomment, pioutcomment,
+ /*INPC 8*/ "pc", piincomment, pioutcomment,
+ /*INFRANZ 9*/ "franz",lispincomment, newline,
+ /*INLISP 10*/ "lisp", lispincomment, newline,
+ /*INVAXIMA 11*/ "vaxima",lispincomment,newline,
+ /*INRATFOR 12*/ "ratfor",fincomment, foutcomment,
+ /*INLEX 13*/ "lex", cincomment, coutcomment,
+ /*INYACC 14*/ "yacc", cincomment, coutcomment,
+ /*INAPL 15*/ "apl", ".lm", newline,
+ /*INMAKE 16*/ "make", ASINCOMMENT, newline,
+ /*INRI 17*/ "ri", riincomment, rioutcomment,
+ /*INTROFF 18*/ "troff",troffincomment,troffoutcomment,
+ /*INMOD2 19*/ "mod2", mod2incomment, mod2outcomment,
+ 0, 0, 0
+};
+
+printerrors(look_at_subclass, errorc, errorv)
+ boolean look_at_subclass;
+ int errorc;
+ Eptr errorv[];
+{
+ reg int i;
+ reg Eptr errorp;
+
+ for (errorp = errorv[i = 0]; i < errorc; errorp = errorv[++i]){
+ if (errorp->error_e_class == C_IGNORE)
+ continue;
+ if (look_at_subclass && errorp->error_s_class == C_DUPL)
+ continue;
+ printf("Error %d, (%s error) [%s], text = \"",
+ i,
+ class_table[errorp->error_e_class],
+ lang_table[errorp->error_language].lang_name);
+ wordvprint(stdout,errorp->error_lgtext,errorp->error_text);
+ printf("\"\n");
+ }
+}
+
+wordvprint(fyle, wordc, wordv)
+ FILE *fyle;
+ int wordc;
+ char *wordv[];
+{
+ int i;
+ char *sep = "";
+
+ for(i = 0; i < wordc; i++)
+ if (wordv[i]) {
+ fprintf(fyle, "%s%s",sep,wordv[i]);
+ sep = " ";
+ }
+}
+
+/*
+ * Given a string, parse it into a number of words, and build
+ * a wordc wordv combination pointing into it.
+ */
+wordvbuild(string, r_wordc, r_wordv)
+ char *string;
+ int *r_wordc;
+ char ***r_wordv;
+{
+ reg char *cp;
+ char *saltedbuffer;
+ char **wordv;
+ int wordcount;
+ int wordindex;
+
+ saltedbuffer = strsave(string);
+ for (wordcount = 0, cp = saltedbuffer; *cp; wordcount++){
+ while (*cp && isspace(*cp))
+ cp++;
+ if (*cp == 0)
+ break;
+ while (!isspace(*cp))
+ cp++;
+ }
+ wordv = (char **)Calloc(wordcount + 1, sizeof (char *));
+ for (cp=saltedbuffer,wordindex=0; wordcount; wordindex++,--wordcount){
+ while (*cp && isspace(*cp))
+ cp++;
+ if (*cp == 0)
+ break;
+ wordv[wordindex] = cp;
+ while(!isspace(*cp))
+ cp++;
+ *cp++ = '\0';
+ }
+ if (wordcount != 0)
+ error("Initial miscount of the number of words in a line\n");
+ wordv[wordindex] = (char *)0;
+#ifdef FULLDEBUG
+ for (wordcount = 0; wordcount < wordindex; wordcount++)
+ printf("Word %d = \"%s\"\n", wordcount, wordv[wordcount]);
+ printf("\n");
+#endif
+ *r_wordc = wordindex;
+ *r_wordv = wordv;
+}
+/*
+ * Compare two 0 based wordvectors
+ */
+int wordvcmp(wordv1, wordc, wordv2)
+ char **wordv1;
+ int wordc;
+ char **wordv2;
+{
+ reg int i;
+ int back;
+ for (i = 0; i < wordc; i++){
+ if (wordv1[i] == 0 || wordv2[i] == 0)
+ return(-1);
+ if (back = strcmp(wordv1[i], wordv2[i])){
+ return(back);
+ }
+ }
+ return(0); /* they are equal */
+}
+
+/*
+ * splice a 0 basedword vector onto the tail of a
+ * new wordv, allowing the first emptyhead slots to be empty
+ */
+char **wordvsplice(emptyhead, wordc, wordv)
+ int emptyhead;
+ int wordc;
+ char **wordv;
+{
+ reg char **nwordv;
+ int nwordc = emptyhead + wordc;
+ reg int i;
+
+ nwordv = (char **)Calloc(nwordc, sizeof (char *));
+ for (i = 0; i < emptyhead; i++)
+ nwordv[i] = 0;
+ for(i = emptyhead; i < nwordc; i++){
+ nwordv[i] = wordv[i-emptyhead];
+ }
+ return(nwordv);
+}
+/*
+ * plural'ize and verb forms
+ */
+static char *S = "s";
+static char *N = "";
+char *plural(n)
+ int n;
+{
+ return( n > 1 ? S : N);
+}
+char *verbform(n)
+ int n;
+{
+ return( n > 1 ? N : S);
+}
+
diff --git a/usr.bin/error/touch.c b/usr.bin/error/touch.c
new file mode 100644
index 0000000..b4348a1
--- /dev/null
+++ b/usr.bin/error/touch.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "pathnames.h"
+
+/*
+ * Iterate through errors
+ */
+#define EITERATE(p, fv, i) for (p = fv[i]; p < fv[i+1]; p++)
+#define ECITERATE(ei, p, lb) for (ei = lb; p = errors[ei],ei < nerrors; ei++)
+
+#define FILEITERATE(fi, lb) for (fi = lb; fi <= nfiles; fi++)
+int touchstatus = Q_YES;
+
+findfiles(nerrors, errors, r_nfiles, r_files)
+ int nerrors;
+ Eptr *errors;
+ int *r_nfiles;
+ Eptr ***r_files;
+{
+ int nfiles;
+ Eptr **files;
+
+ char *name;
+ reg int ei;
+ int fi;
+ reg Eptr errorp;
+
+ nfiles = countfiles(errors);
+
+ files = (Eptr**)Calloc(nfiles + 3, sizeof (Eptr*));
+ touchedfiles = (boolean *)Calloc(nfiles+3, sizeof(boolean));
+ /*
+ * Now, partition off the error messages
+ * into those that are synchronization, discarded or
+ * not specific to any file, and those that were
+ * nulled or true errors.
+ */
+ files[0] = &errors[0];
+ ECITERATE(ei, errorp, 0){
+ if ( ! (NOTSORTABLE(errorp->error_e_class)))
+ break;
+ }
+ /*
+ * Now, and partition off all error messages
+ * for a given file.
+ */
+ files[1] = &errors[ei];
+ touchedfiles[0] = touchedfiles[1] = FALSE;
+ name = "\1";
+ fi = 1;
+ ECITERATE(ei, errorp, ei){
+ if ( (errorp->error_e_class == C_NULLED)
+ || (errorp->error_e_class == C_TRUE) ){
+ if (strcmp(errorp->error_text[0], name) != 0){
+ name = errorp->error_text[0];
+ touchedfiles[fi] = FALSE;
+ files[fi] = &errors[ei];
+ fi++;
+ }
+ }
+ }
+ files[fi] = &errors[nerrors];
+ *r_nfiles = nfiles;
+ *r_files = files;
+}
+
+int countfiles(errors)
+ Eptr *errors;
+{
+ char *name;
+ int ei;
+ reg Eptr errorp;
+
+ int nfiles;
+ nfiles = 0;
+ name = "\1";
+ ECITERATE(ei, errorp, 0){
+ if (SORTABLE(errorp->error_e_class)){
+ if (strcmp(errorp->error_text[0],name) != 0){
+ nfiles++;
+ name = errorp->error_text[0];
+ }
+ }
+ }
+ return(nfiles);
+}
+char *class_table[] = {
+ /*C_UNKNOWN 0 */ "Unknown",
+ /*C_IGNORE 1 */ "ignore",
+ /*C_SYNC 2 */ "synchronization",
+ /*C_DISCARD 3 */ "discarded",
+ /*C_NONSPEC 4 */ "non specific",
+ /*C_THISFILE 5 */ "specific to this file",
+ /*C_NULLED 6 */ "nulled",
+ /*C_TRUE 7 */ "true",
+ /*C_DUPL 8 */ "duplicated"
+};
+
+int class_count[C_LAST - C_FIRST] = {0};
+
+filenames(nfiles, files)
+ int nfiles;
+ Eptr **files;
+{
+ reg int fi;
+ char *sep = " ";
+ extern char *class_table[];
+ int someerrors;
+
+ /*
+ * first, simply dump out errors that
+ * don't pertain to any file
+ */
+ someerrors = nopertain(files);
+
+ if (nfiles){
+ someerrors++;
+ fprintf(stdout, terse
+ ? "%d file%s"
+ : "%d file%s contain%s errors",
+ nfiles, plural(nfiles), verbform(nfiles));
+ if (!terse){
+ FILEITERATE(fi, 1){
+ fprintf(stdout, "%s\"%s\" (%d)",
+ sep, (*files[fi])->error_text[0],
+ files[fi+1] - files[fi]);
+ sep = ", ";
+ }
+ }
+ fprintf(stdout, "\n");
+ }
+ if (!someerrors)
+ fprintf(stdout, "No errors.\n");
+}
+
+/*
+ * Dump out errors that don't pertain to any file
+ */
+int nopertain(files)
+ Eptr **files;
+{
+ int type;
+ int someerrors = 0;
+ reg Eptr *erpp;
+ reg Eptr errorp;
+
+ if (files[1] - files[0] <= 0)
+ return(0);
+ for(type = C_UNKNOWN; NOTSORTABLE(type); type++){
+ if (class_count[type] <= 0)
+ continue;
+ if (type > C_SYNC)
+ someerrors++;
+ if (terse){
+ fprintf(stdout, "\t%d %s errors NOT PRINTED\n",
+ class_count[type], class_table[type]);
+ } else {
+ fprintf(stdout, "\n\t%d %s errors follow\n",
+ class_count[type], class_table[type]);
+ EITERATE(erpp, files, 0){
+ errorp = *erpp;
+ if (errorp->error_e_class == type){
+ errorprint(stdout, errorp, TRUE);
+ }
+ }
+ }
+ }
+ return(someerrors);
+}
+
+extern boolean notouch;
+
+boolean touchfiles(nfiles, files, r_edargc, r_edargv)
+ int nfiles;
+ Eptr **files;
+ int *r_edargc;
+ char ***r_edargv;
+{
+ char *name;
+ reg Eptr errorp;
+ reg int fi;
+ reg Eptr *erpp;
+ int ntrueerrors;
+ boolean scribbled;
+ int n_pissed_on; /* # of file touched*/
+ int spread;
+
+ FILEITERATE(fi, 1){
+ name = (*files[fi])->error_text[0];
+ spread = files[fi+1] - files[fi];
+ fprintf(stdout, terse
+ ? "\"%s\" has %d error%s, "
+ : "\nFile \"%s\" has %d error%s.\n"
+ , name ,spread ,plural(spread));
+ /*
+ * First, iterate through all error messages in this file
+ * to see how many of the error messages really will
+ * get inserted into the file.
+ */
+ ntrueerrors = 0;
+ EITERATE(erpp, files, fi){
+ errorp = *erpp;
+ if (errorp->error_e_class == C_TRUE)
+ ntrueerrors++;
+ }
+ fprintf(stdout, terse
+ ? "insert %d\n"
+ : "\t%d of these errors can be inserted into the file.\n",
+ ntrueerrors);
+
+ hackfile(name, files, fi, ntrueerrors);
+ }
+ scribbled = FALSE;
+ n_pissed_on = 0;
+ FILEITERATE(fi, 1){
+ scribbled |= touchedfiles[fi];
+ n_pissed_on++;
+ }
+ if (scribbled){
+ /*
+ * Construct an execv argument
+ */
+ execvarg(n_pissed_on, r_edargc, r_edargv);
+ return(TRUE);
+ } else {
+ if (!terse)
+ fprintf(stdout, "You didn't touch any files.\n");
+ return(FALSE);
+ }
+}
+
+hackfile(name, files, ix, nerrors)
+ char *name;
+ Eptr **files;
+ int ix;
+{
+ boolean previewed;
+ int errordest; /* where errors go*/
+
+ if (!oktotouch(name)) {
+ previewed = FALSE;
+ errordest = TOSTDOUT;
+ } else {
+ previewed = preview(name, nerrors, files, ix);
+ errordest = settotouch(name);
+ }
+
+ if (errordest != TOSTDOUT)
+ touchedfiles[ix] = TRUE;
+
+ if (previewed && (errordest == TOSTDOUT))
+ return;
+
+ diverterrors(name, errordest, files, ix, previewed, nerrors);
+
+ if (errordest == TOTHEFILE){
+ /*
+ * overwrite the original file
+ */
+ writetouched(1);
+ }
+}
+
+boolean preview(name, nerrors, files, ix)
+ char *name;
+ int nerrors;
+ Eptr **files;
+ int ix;
+{
+ int back;
+ reg Eptr *erpp;
+
+ if (nerrors <= 0)
+ return(FALSE);
+ back = FALSE;
+ if(query){
+ switch(inquire(terse
+ ? "Preview? "
+ : "Do you want to preview the errors first? ")){
+ case Q_YES:
+ case Q_yes:
+ back = TRUE;
+ EITERATE(erpp, files, ix){
+ errorprint(stdout, *erpp, TRUE);
+ }
+ if (!terse)
+ fprintf(stdout, "\n");
+ default:
+ break;
+ }
+ }
+ return(back);
+}
+
+int settotouch(name)
+ char *name;
+{
+ int dest = TOSTDOUT;
+
+ if (query){
+ switch(touchstatus = inquire(terse
+ ? "Touch? "
+ : "Do you want to touch file \"%s\"? ",
+ name)){
+ case Q_NO:
+ case Q_no:
+ return(dest);
+ default:
+ break;
+ }
+ }
+
+ switch(probethisfile(name)){
+ case F_NOTREAD:
+ dest = TOSTDOUT;
+ fprintf(stdout, terse
+ ? "\"%s\" unreadable\n"
+ : "File \"%s\" is unreadable\n",
+ name);
+ break;
+ case F_NOTWRITE:
+ dest = TOSTDOUT;
+ fprintf(stdout, terse
+ ? "\"%s\" unwritable\n"
+ : "File \"%s\" is unwritable\n",
+ name);
+ break;
+ case F_NOTEXIST:
+ dest = TOSTDOUT;
+ fprintf(stdout, terse
+ ? "\"%s\" not found\n"
+ : "Can't find file \"%s\" to insert error messages into.\n",
+ name);
+ break;
+ default:
+ dest = edit(name) ? TOSTDOUT : TOTHEFILE;
+ break;
+ }
+ return(dest);
+}
+
+diverterrors(name, dest, files, ix, previewed, nterrors)
+ char *name;
+ int dest;
+ Eptr **files;
+ int ix;
+ boolean previewed;
+ int nterrors;
+{
+ int nerrors;
+ reg Eptr *erpp;
+ reg Eptr errorp;
+
+ nerrors = files[ix+1] - files[ix];
+
+ if ( (nerrors != nterrors)
+ && (!previewed) ){
+ fprintf(stdout, terse
+ ? "Uninserted errors\n"
+ : ">>Uninserted errors for file \"%s\" follow.\n",
+ name);
+ }
+
+ EITERATE(erpp, files, ix){
+ errorp = *erpp;
+ if (errorp->error_e_class != C_TRUE){
+ if (previewed || touchstatus == Q_NO)
+ continue;
+ errorprint(stdout, errorp, TRUE);
+ continue;
+ }
+ switch (dest){
+ case TOSTDOUT:
+ if (previewed || touchstatus == Q_NO)
+ continue;
+ errorprint(stdout,errorp, TRUE);
+ break;
+ case TOTHEFILE:
+ insert(errorp->error_line);
+ text(errorp, FALSE);
+ break;
+ }
+ }
+}
+
+int oktotouch(filename)
+ char *filename;
+{
+ extern char *suffixlist;
+ reg char *src;
+ reg char *pat;
+ char *osrc;
+
+ pat = suffixlist;
+ if (pat == 0)
+ return(0);
+ if (*pat == '*')
+ return(1);
+ while (*pat++ != '.')
+ continue;
+ --pat; /* point to the period */
+
+ for (src = &filename[strlen(filename)], --src;
+ (src > filename) && (*src != '.'); --src)
+ continue;
+ if (*src != '.')
+ return(0);
+
+ for (src++, pat++, osrc = src; *src && *pat; src = osrc, pat++){
+ for (; *src /* not at end of the source */
+ && *pat /* not off end of pattern */
+ && *pat != '.' /* not off end of sub pattern */
+ && *pat != '*' /* not wild card */
+ && *src == *pat; /* and equal... */
+ src++, pat++)
+ continue;
+ if (*src == 0 && (*pat == 0 || *pat == '.' || *pat == '*'))
+ return(1);
+ if (*src != 0 && *pat == '*')
+ return(1);
+ while (*pat && *pat != '.')
+ pat++;
+ if (! *pat)
+ return(0);
+ }
+ return(0);
+}
+/*
+ * Construct an execv argument
+ * We need 1 argument for the editor's name
+ * We need 1 argument for the initial search string
+ * We need n_pissed_on arguments for the file names
+ * We need 1 argument that is a null for execv.
+ * The caller fills in the editor's name.
+ * We fill in the initial search string.
+ * We fill in the arguments, and the null.
+ */
+execvarg(n_pissed_on, r_argc, r_argv)
+ int n_pissed_on;
+ int *r_argc;
+ char ***r_argv;
+{
+ Eptr p;
+ char *sep;
+ int fi;
+
+ (*r_argv) = (char **)Calloc(n_pissed_on + 3, sizeof(char *));
+ (*r_argc) = n_pissed_on + 2;
+ (*r_argv)[1] = "+1;/###/";
+ n_pissed_on = 2;
+ if (!terse){
+ fprintf(stdout, "You touched file(s):");
+ sep = " ";
+ }
+ FILEITERATE(fi, 1){
+ if (!touchedfiles[fi])
+ continue;
+ p = *(files[fi]);
+ if (!terse){
+ fprintf(stdout,"%s\"%s\"", sep, p->error_text[0]);
+ sep = ", ";
+ }
+ (*r_argv)[n_pissed_on++] = p->error_text[0];
+ }
+ if (!terse)
+ fprintf(stdout, "\n");
+ (*r_argv)[n_pissed_on] = 0;
+}
+
+FILE *o_touchedfile; /* the old file */
+FILE *n_touchedfile; /* the new file */
+char *o_name;
+char n_name[64];
+char *canon_name = _PATH_TMP;
+int o_lineno;
+int n_lineno;
+boolean tempfileopen = FALSE;
+/*
+ * open the file; guaranteed to be both readable and writable
+ * Well, if it isn't, then return TRUE if something failed
+ */
+boolean edit(name)
+ char *name;
+{
+ o_name = name;
+ if ( (o_touchedfile = fopen(name, "r")) == NULL){
+ fprintf(stderr, "%s: Can't open file \"%s\" to touch (read).\n",
+ processname, name);
+ return(TRUE);
+ }
+ (void)strcpy(n_name, canon_name);
+ (void)mktemp(n_name);
+ if ( (n_touchedfile = fopen(n_name, "w")) == NULL){
+ fprintf(stderr,"%s: Can't open file \"%s\" to touch (write).\n",
+ processname, name);
+ return(TRUE);
+ }
+ tempfileopen = TRUE;
+ n_lineno = 0;
+ o_lineno = 0;
+ return(FALSE);
+}
+/*
+ * Position to the line (before, after) the line given by place
+ */
+char edbuf[BUFSIZ];
+insert(place)
+ int place;
+{
+ --place; /* always insert messages before the offending line*/
+ for(; o_lineno < place; o_lineno++, n_lineno++){
+ if(fgets(edbuf, BUFSIZ, o_touchedfile) == NULL)
+ return;
+ fputs(edbuf, n_touchedfile);
+ }
+}
+
+text(p, use_all)
+ reg Eptr p;
+ boolean use_all;
+{
+ int offset = use_all ? 0 : 2;
+
+ fputs(lang_table[p->error_language].lang_incomment, n_touchedfile);
+ fprintf(n_touchedfile, "%d [%s] ",
+ p->error_line,
+ lang_table[p->error_language].lang_name);
+ wordvprint(n_touchedfile, p->error_lgtext-offset, p->error_text+offset);
+ fputs(lang_table[p->error_language].lang_outcomment,n_touchedfile);
+ n_lineno++;
+}
+
+/*
+ * write the touched file to its temporary copy,
+ * then bring the temporary in over the local file
+ */
+writetouched(overwrite)
+ int overwrite;
+{
+ reg int nread;
+ reg FILE *localfile;
+ reg FILE *tmpfile;
+ int botch;
+ int oktorm;
+
+ botch = 0;
+ oktorm = 1;
+ while((nread = fread(edbuf, 1, sizeof(edbuf), o_touchedfile)) != NULL){
+ if (nread != fwrite(edbuf, 1, nread, n_touchedfile)){
+ /*
+ * Catastrophe in temporary area: file system full?
+ */
+ botch = 1;
+ fprintf(stderr,
+ "%s: write failure: No errors inserted in \"%s\"\n",
+ processname, o_name);
+ }
+ }
+ fclose(n_touchedfile);
+ fclose(o_touchedfile);
+ /*
+ * Now, copy the temp file back over the original
+ * file, thus preserving links, etc
+ */
+ if (botch == 0 && overwrite){
+ botch = 0;
+ localfile = NULL;
+ tmpfile = NULL;
+ if ((localfile = fopen(o_name, "w")) == NULL){
+ fprintf(stderr,
+ "%s: Can't open file \"%s\" to overwrite.\n",
+ processname, o_name);
+ botch++;
+ }
+ if ((tmpfile = fopen(n_name, "r")) == NULL){
+ fprintf(stderr, "%s: Can't open file \"%s\" to read.\n",
+ processname, n_name);
+ botch++;
+ }
+ if (!botch)
+ oktorm = mustoverwrite(localfile, tmpfile);
+ if (localfile != NULL)
+ fclose(localfile);
+ if (tmpfile != NULL)
+ fclose(tmpfile);
+ }
+ if (oktorm == 0){
+ fprintf(stderr, "%s: Catastrophe: A copy of \"%s\": was saved in \"%s\"\n",
+ processname, o_name, n_name);
+ exit(1);
+ }
+ /*
+ * Kiss the temp file good bye
+ */
+ unlink(n_name);
+ tempfileopen = FALSE;
+ return(TRUE);
+}
+/*
+ * return 1 if the tmpfile can be removed after writing it out
+ */
+int mustoverwrite(preciousfile, tmpfile)
+ FILE *preciousfile;
+ FILE *tmpfile;
+{
+ int nread;
+
+ while((nread = fread(edbuf, 1, sizeof(edbuf), tmpfile)) != NULL){
+ if (mustwrite(edbuf, nread, preciousfile) == 0)
+ return(0);
+ }
+ return(1);
+}
+/*
+ * return 0 on catastrophe
+ */
+mustwrite(base, n, preciousfile)
+ char *base;
+ int n;
+ FILE *preciousfile;
+{
+ int nwrote;
+
+ if (n <= 0)
+ return(1);
+ nwrote = fwrite(base, 1, n, preciousfile);
+ if (nwrote == n)
+ return(1);
+ perror(processname);
+ switch(inquire(terse
+ ? "Botch overwriting: retry? "
+ : "Botch overwriting the source file: retry? ")){
+ case Q_YES:
+ case Q_yes:
+ mustwrite(base + nwrote, n - nwrote, preciousfile);
+ return(1);
+ case Q_NO:
+ case Q_no:
+ switch(inquire("Are you sure? ")){
+ case Q_YES:
+ case Q_yes:
+ return(0);
+ case Q_NO:
+ case Q_no:
+ mustwrite(base + nwrote, n - nwrote, preciousfile);
+ return(1);
+ }
+ default:
+ return(0);
+ }
+}
+
+void
+onintr()
+{
+ switch(inquire(terse
+ ? "\nContinue? "
+ : "\nInterrupt: Do you want to continue? ")){
+ case Q_YES:
+ case Q_yes:
+ signal(SIGINT, onintr);
+ return;
+ default:
+ if (tempfileopen){
+ /*
+ * Don't overwrite the original file!
+ */
+ writetouched(0);
+ }
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+errorprint(place, errorp, print_all)
+ FILE *place;
+ Eptr errorp;
+ boolean print_all;
+{
+ int offset = print_all ? 0 : 2;
+
+ if (errorp->error_e_class == C_IGNORE)
+ return;
+ fprintf(place, "[%s] ", lang_table[errorp->error_language].lang_name);
+ wordvprint(place,errorp->error_lgtext-offset,errorp->error_text+offset);
+ putc('\n', place);
+}
+
+int inquire(fmt, a1, a2)
+ char *fmt;
+ /*VARARGS1*/
+{
+ char buffer[128];
+
+ if (queryfile == NULL)
+ return(0);
+ for(;;){
+ do{
+ fflush(stdout);
+ fprintf(stderr, fmt, a1, a2);
+ fflush(stderr);
+ } while (fgets(buffer, 127, queryfile) == NULL);
+ switch(buffer[0]){
+ case 'Y': return(Q_YES);
+ case 'y': return(Q_yes);
+ case 'N': return(Q_NO);
+ case 'n': return(Q_no);
+ default: fprintf(stderr, "Yes or No only!\n");
+ }
+ }
+}
+
+int probethisfile(name)
+ char *name;
+{
+ struct stat statbuf;
+ if (stat(name, &statbuf) < 0)
+ return(F_NOTEXIST);
+ if((statbuf.st_mode & S_IREAD) == 0)
+ return(F_NOTREAD);
+ if((statbuf.st_mode & S_IWRITE) == 0)
+ return(F_NOTWRITE);
+ return(F_TOUCHIT);
+}
diff --git a/usr.bin/expand/Makefile b/usr.bin/expand/Makefile
new file mode 100644
index 0000000..751df25
--- /dev/null
+++ b/usr.bin/expand/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= expand
+MLINKS= expand.1 unexpand.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/expand/expand.1 b/usr.bin/expand/expand.1
new file mode 100644
index 0000000..e345065
--- /dev/null
+++ b/usr.bin/expand/expand.1
@@ -0,0 +1,87 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)expand.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt EXPAND 1
+.Os BSD 4
+.Sh NAME
+.Nm expand ,
+.Nm unexpand
+.Nd expand tabs to spaces, and vice versa
+.Sh SYNOPSIS
+.Nm expand
+.Op Fl Ns Ar tabstop
+.Op Fl Ns Ar tab1,tab2,...,tabn
+.Ar
+.Nm unexpand
+.Op Fl a
+.Ar
+.Sh DESCRIPTION
+.Nm Expand
+processes the named files or the standard input writing
+the standard output with tabs changed into blanks.
+Backspace characters are preserved into the output and decrement
+the column count for tab calculations.
+.Nm Expand
+is useful for pre-processing character files
+(before sorting, looking at specific columns, etc.) that
+contain tabs.
+.Pp
+If a single
+.Ar tabstop
+argument is given, then tabs are set
+.Ar tabstop
+spaces apart instead of the default 8.
+If multiple tabstops are given then the tabs are set at those
+specific columns.
+.Pp
+.Nm Unexpand
+puts tabs back into the data from the standard input or the named
+files and writes the result on the standard output.
+.Pp
+Option (with
+.Nm unexpand
+only):
+.Bl -tag -width flag
+.It Fl a
+By default, only leading blanks and tabs
+are reconverted to maximal strings of tabs. If the
+.Fl a
+option is given, then tabs are inserted whenever they would compress the
+resultant file by replacing two or more characters.
+.El
+.Sh HISTORY
+The
+.Nm expand
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/expand/expand.c b/usr.bin/expand/expand.c
new file mode 100644
index 0000000..c3a00e7
--- /dev/null
+++ b/usr.bin/expand/expand.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <stdio.h>
+/*
+ * expand - expand tabs to equivalent spaces
+ */
+int nstops;
+int tabstops[100];
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, column;
+ register int n;
+
+ argc--, argv++;
+ do {
+ while (argc > 0 && argv[0][0] == '-') {
+ getstops(argv[0]);
+ argc--, argv++;
+ }
+ if (argc > 0) {
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ perror(argv[0]);
+ exit(1);
+ }
+ argc--, argv++;
+ }
+ column = 0;
+ for (;;) {
+ c = getc(stdin);
+ if (c == -1)
+ break;
+ switch (c) {
+
+ case '\t':
+ if (nstops == 0) {
+ do {
+ putchar(' ');
+ column++;
+ } while (column & 07);
+ continue;
+ }
+ if (nstops == 1) {
+ do {
+ putchar(' ');
+ column++;
+ } while (((column - 1) % tabstops[0]) != (tabstops[0] - 1));
+ continue;
+ }
+ for (n = 0; n < nstops; n++)
+ if (tabstops[n] > column)
+ break;
+ if (n == nstops) {
+ putchar(' ');
+ column++;
+ continue;
+ }
+ while (column < tabstops[n]) {
+ putchar(' ');
+ column++;
+ }
+ continue;
+
+ case '\b':
+ if (column)
+ column--;
+ putchar('\b');
+ continue;
+
+ default:
+ putchar(c);
+ column++;
+ continue;
+
+ case '\n':
+ putchar(c);
+ column = 0;
+ continue;
+ }
+ }
+ } while (argc > 0);
+ exit(0);
+}
+
+getstops(cp)
+ register char *cp;
+{
+ register int i;
+
+ nstops = 0;
+ cp++;
+ for (;;) {
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + *cp++ - '0';
+ if (i <= 0 || i > 256) {
+bad:
+ fprintf(stderr, "Bad tab stop spec\n");
+ exit(1);
+ }
+ if (nstops > 0 && i <= tabstops[nstops-1])
+ goto bad;
+ tabstops[nstops++] = i;
+ if (*cp == 0)
+ break;
+ if (*cp++ != ',')
+ goto bad;
+ }
+}
diff --git a/usr.bin/f2c/Makefile b/usr.bin/f2c/Makefile
new file mode 100644
index 0000000..1a699d2
--- /dev/null
+++ b/usr.bin/f2c/Makefile
@@ -0,0 +1,31 @@
+# Makefile for f2c, a Fortran 77 to C converter
+
+PROG= f2c
+
+CFLAGS += -DANSI_Libraries -I${.CURDIR} -I.
+SHELL = /bin/sh
+
+SRCSd = main.c init.c gram.c lex.c proc.c equiv.c data.c format.c \
+ expr.c exec.c intr.c io.c misc.c error.c mem.c names.c \
+ output.c p1output.c pread.c put.c putpcc.c vax.c formatdata.c \
+ parse_args.c niceprintf.c cds.c sysdep.c version.c
+SRCS = $(SRCSd) # malloc.c
+
+GRAMFILES = ${.CURDIR}/gram.head ${.CURDIR}/gram.dcl ${.CURDIR}/gram.expr\
+ ${.CURDIR}/gram.exec ${.CURDIR}/gram.io
+
+gram.c: ${GRAMFILES} ${.CURDIR}/defs.h tokdefs.h
+ (sed < tokdefs.h "s/#define/%token/" ; \
+ cat ${GRAMFILES}) > gram.in
+ $(YACC) $(YFLAGS) gram.in
+ echo "(expect 4 shift/reduce)"
+ sed 's/^# line.*/\/* & *\//' y.tab.c >gram.c
+ rm -f gram.in y.tab.c
+
+tokdefs.h: ${.CURDIR}/tokens
+ grep -n . <${.CURDIR}/tokens | sed "s/\([^:]*\):\(.*\)/#define \2 \1/" >tokdefs.h
+
+CLEANFILES+=\
+ gram.c tokdefs.h y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/f2c/Notice b/usr.bin/f2c/Notice
new file mode 100644
index 0000000..9715a19
--- /dev/null
+++ b/usr.bin/f2c/Notice
@@ -0,0 +1,23 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
diff --git a/usr.bin/f2c/README b/usr.bin/f2c/README
new file mode 100644
index 0000000..b8e5a67
--- /dev/null
+++ b/usr.bin/f2c/README
@@ -0,0 +1,145 @@
+Type "make" to check the validity of the f2c source and compile f2c.
+
+On a PC, you may need to compile xsum.c with -DMSDOS (i.e., with
+MSDOS #defined).
+
+If your compiler does not understand ANSI/ISO C syntax (i.e., if
+you have a K&R C compiler), compile with -DKR_headers .
+
+On non-Unix systems where files have separate binary and text modes,
+you may need to "make xsumr.out" rather than "make xsum.out".
+
+If (in accordance with what follows) you need to modify the makefile
+or any of the source files, first issue a "make xsum.out" (or, if
+appropriate, "make xsumr.out") to check the validity of the f2c source,
+then make your changes, then type "make f2c".
+
+The file usignal.h is for the benefit of strictly ANSI include files
+on a UNIX system -- the ANSI signal.h does not define SIGHUP or SIGQUIT.
+You may need to modify usignal.h if you are not running f2c on a UNIX
+system.
+
+Should you get the message "xsum0.out xsum1.out differ", see what lines
+are different (`diff xsum0.out xsum1.out`) and ask netlib
+(e.g., netlib@research.att.com) to send you the files in question,
+plus the current xsum0.out (which may have changed) "from f2c/src".
+For example, if exec.c and expr.c have incorrect check sums, you would
+send netlib the message
+ send exec.c expr.c xsum0.out from f2c/src
+You can also ftp these files from netlib.att.com; for more details, ask
+netlib@research.att.com to "send readme from f2c".
+
+On some systems, the malloc and free in malloc.c let f2c run faster
+than do the standard malloc and free. Other systems may not tolerate
+redefinition of malloc and free (though changes of 8 Nov. 1994 may
+render this less of a problem than hitherto). If yours is such a
+system, you may either modify the makefile appropriately (remove
+"malloc.o" from the "OBJECTS =" assignment), or simply execute
+ cc -c -DCRAY malloc.c
+before typing "make". Still other systems have a -lmalloc that
+provides performance competitive with that from malloc.c; you may
+wish to compare the two on your system.
+
+On some BSD systems, you may need to create a file named "string.h"
+whose single line is
+#include <strings.h>
+you may need to add " -Dstrchr=index" to the "CFLAGS =" assignment
+in the makefile, and you may need to add " memset.o" to the "OBJECTS ="
+assignment in the makefile -- see the comments in memset.c .
+
+For non-UNIX systems, you may need to change some things in sysdep.c,
+such as the choice of intermediate file names.
+
+On some systems, you may need to modify parts of sysdep.h (which is
+included by defs.h). In particular, for Sun 4.1 systems and perhaps
+some others, you need to comment out the typedef of size_t. For some
+systems (e.g., IRIX 4.0.1 and AIX) it is better to add
+#define ANSI_Libraries
+to the beginning of sysdep.h (or to supply -DANSI_Libraries in the
+makefile).
+
+Alas, some systems #define __STDC__ but do not provide a true standard
+(ANSI or ISO) C environment, e.g. do not provide stdlib.h . If yours
+is such a system, then (a) you should complain loudly to your vendor
+about __STDC__ being erroneously defined, and (b) you should insert
+#undef __STDC__
+at the beginning of sysdep.h . You may need to make other adjustments.
+
+For some non-ANSI versions of stdio, you must change the values given
+to binread and binwrite in sysdep.c from "rb" and "wb" to "r" and "w".
+You may need to make this change if you run f2c and get an error
+message of the form
+ Compiler error ... cannot open intermediate file ...
+
+On many systems, it is best to combine libF77 and libI77 into a single
+library, say libf2c, as suggested in "readme from f2c". If you do not
+do this, then you should adjust the definition of link_msg in sysdep.c
+appropriately (e.g., replacing "-lf2c" by "-lF77 -lI77"). On Unix
+systems, the easiest way to create libf2c.a is to make libF77/libF77.a
+and libI77/libI77.a (after reading and heeding libF77/README and
+libI77/README), and then to say
+
+ cp libF77/libF77.a libf2c.a
+ ar ruv libf2c.a libI77/*.o
+ ranlib libf2c.a
+
+The last step, ranlib, may not be necessary on your system. On
+other systems, just compile all the .c files in libF77 and libI77,
+and put the resulting objects (except one or both of the Version
+objects) into a library, called perhaps f2c.lib .
+
+Some older C compilers object to
+ typedef void (*foo)();
+or to
+ typedef void zap;
+ zap (*foo)();
+If yours is such a compiler, change the definition of VOID in
+f2c.h from void to int.
+
+For convenience with systems that use control-Z to denote end-of-file,
+f2c treats control-Z characters (ASCII 26, '\x1a') that appear at the
+beginning of a line as an end-of-file indicator. You can disable this
+test by compiling lex.c with NO_EOF_CHAR_CHECK #defined, or can
+change control-Z to some other character by #defining EOF_CHAR to
+be the desired value.
+
+
+If your machine has IEEE, VAX, or IBM-mainframe arithmetic, but your
+printf is inaccurate (e.g., with Symantec C++ version 6.0,
+printf("%.17g",12.) prints 12.000000000000001), you can make f2c print
+correctly rounded numbers by compiling with -DUSE_DTOA and adding
+dtoa.o g_fmt.o to the makefile's OBJECTS = line, so it becomes
+
+ OBJECTS = $(OBJECTSd) malloc.o dtoa.o g_fmt.o
+
+Also add the rule
+
+ dtoa.o: dtoa.c
+ $(CC) -c $(CFLAGS) -DMALLOC=ckalloc -DIEEE... dtoa.c
+
+(without the initial tab) to the makefile, where IEEE... is one of
+IEEE_MC68k, IEEE_8087, VAX, or IBM, depending on your machine's
+arithmetic. See the comments near the start of dtoa.c.
+
+The relevant source files, dtoa.c and g_fmt.c, are available
+separately from netlib's fp directory. For example, you could
+send the E-mail message
+
+ send dtoa.c g_fmt.c from fp
+
+to netlib@research.att.com (or use anonymous ftp from netlib.att.com
+and look in directory /netlib/fp).
+
+The makefile has a rule for creating tokdefs.h. If you cannot use the
+makefile, an alternative is to extract tokdefs.h from the beginning of
+gram.c: it's the first 100 lines.
+
+
+Please send bug reports to dmg@research.att.com . The old index file
+(now called "readme" due to unfortunate changes in netlib conventions:
+"send readme from f2c") will report recent changes in the recent-change
+log at its end; all changes will be shown in the "changes" file
+("send changes from f2c"). To keep current source, you will need to
+request xsum0.out and version.c, in addition to the changed source
+files. Changes first appear on netlib@research.att.com, and in due
+time propagate to the other netlib sites that are kept current.
diff --git a/usr.bin/f2c/cds.c b/usr.bin/f2c/cds.c
new file mode 100644
index 0000000..80e91ae
--- /dev/null
+++ b/usr.bin/f2c/cds.c
@@ -0,0 +1,195 @@
+/****************************************************************
+Copyright 1990, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* Put strings representing decimal floating-point numbers
+ * into canonical form: always have a decimal point or
+ * exponent field; if using an exponent field, have the
+ * number before it start with a digit and decimal point
+ * (if the number has more than one digit); only have an
+ * exponent field if it saves space.
+ *
+ * Arrange that the return value, rv, satisfies rv[0] == '-' || rv[-1] == '-' .
+ */
+
+#include "defs.h"
+
+ char *
+#ifdef KR_headers
+cds(s, z0)
+ char *s;
+ char *z0;
+#else
+cds(char *s, char *z0)
+#endif
+{
+ int ea, esign, et, i, k, nd = 0, sign = 0, tz;
+ char c, *z;
+ char ebuf[24];
+ long ex = 0;
+ static char etype[Table_size], *db;
+ static int dblen = 64;
+
+ if (!db) {
+ etype['E'] = 1;
+ etype['e'] = 1;
+ etype['D'] = 1;
+ etype['d'] = 1;
+ etype['+'] = 2;
+ etype['-'] = 3;
+ db = Alloc(dblen);
+ }
+
+ while((c = *s++) == '0');
+ if (c == '-')
+ { sign = 1; c = *s++; }
+ else if (c == '+')
+ c = *s++;
+ k = strlen(s) + 2;
+ if (k >= dblen) {
+ do dblen <<= 1;
+ while(k >= dblen);
+ free(db);
+ db = Alloc(dblen);
+ }
+ if (etype[(unsigned char)c] >= 2)
+ while(c == '0') c = *s++;
+ tz = 0;
+ while(c >= '0' && c <= '9') {
+ if (c == '0')
+ tz++;
+ else {
+ if (nd)
+ for(; tz; --tz)
+ db[nd++] = '0';
+ else
+ tz = 0;
+ db[nd++] = c;
+ }
+ c = *s++;
+ }
+ ea = -tz;
+ if (c == '.') {
+ while((c = *s++) >= '0' && c <= '9') {
+ if (c == '0')
+ tz++;
+ else {
+ if (tz) {
+ ea += tz;
+ if (nd)
+ for(; tz; --tz)
+ db[nd++] = '0';
+ else
+ tz = 0;
+ }
+ db[nd++] = c;
+ ea++;
+ }
+ }
+ }
+ if (et = etype[(unsigned char)c]) {
+ esign = et == 3;
+ c = *s++;
+ if (et == 1) {
+ if(etype[(unsigned char)c] > 1) {
+ if (c == '-')
+ esign = 1;
+ c = *s++;
+ }
+ }
+ while(c >= '0' && c <= '9') {
+ ex = 10*ex + (c - '0');
+ c = *s++;
+ }
+ if (esign)
+ ex = -ex;
+ }
+ switch(c) {
+ case 0:
+ break;
+#ifndef VAX
+ case 'i':
+ case 'I':
+ Fatal("Overflow evaluating constant expression.");
+ case 'n':
+ case 'N':
+ Fatal("Constant expression yields NaN.");
+#endif
+ default:
+ Fatal("unexpected character in cds.");
+ }
+ ex -= ea;
+ if (!nd) {
+ if (!z0)
+ z0 = mem(4,0);
+ strcpy(z0, "-0.");
+ sign = 0;
+ }
+ else if (ex > 2 || ex + nd < -2) {
+ sprintf(ebuf, "%ld", ex + nd - 1);
+ k = strlen(ebuf) + nd + 3;
+ if (nd > 1)
+ k++;
+ if (!z0)
+ z0 = mem(k,0);
+ z = z0;
+ *z++ = '-';
+ *z++ = *db;
+ if (nd > 1) {
+ *z++ = '.';
+ for(k = 1; k < nd; k++)
+ *z++ = db[k];
+ }
+ *z++ = 'e';
+ strcpy(z, ebuf);
+ }
+ else {
+ k = (int)(ex + nd);
+ i = nd + 3;
+ if (k < 0)
+ i -= k;
+ else if (ex > 0)
+ i += ex;
+ if (!z0)
+ z0 = mem(i,0);
+ z = z0;
+ *z++ = '-';
+ if (ex >= 0) {
+ for(k = 0; k < nd; k++)
+ *z++ = db[k];
+ while(--ex >= 0)
+ *z++ = '0';
+ *z++ = '.';
+ }
+ else {
+ for(i = 0; i < k;)
+ *z++ = db[i++];
+ *z++ = '.';
+ while(++k <= 0)
+ *z++ = '0';
+ while(i < nd)
+ *z++ = db[i++];
+ }
+ *z = 0;
+ }
+ return sign ? z0 : z0+1;
+ }
diff --git a/usr.bin/f2c/data.c b/usr.bin/f2c/data.c
new file mode 100644
index 0000000..44b84ef
--- /dev/null
+++ b/usr.bin/f2c/data.c
@@ -0,0 +1,491 @@
+/****************************************************************
+Copyright 1990, 1993 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+
+/* ROUTINES CALLED DURING DATA AND PARAMETER STATEMENT PROCESSING */
+
+static char datafmt[] = "%s\t%09ld\t%d";
+static char *cur_varname;
+
+/* another initializer, called from parser */
+ void
+#ifdef KR_headers
+dataval(repp, valp)
+ register expptr repp;
+ register expptr valp;
+#else
+dataval(register expptr repp, register expptr valp)
+#endif
+{
+ int i, nrep;
+ ftnint elen;
+ register Addrp p;
+
+ if (parstate < INDATA) {
+ frexpr(repp);
+ goto ret;
+ }
+ if(repp == NULL)
+ nrep = 1;
+ else if (ISICON(repp) && repp->constblock.Const.ci >= 0)
+ nrep = repp->constblock.Const.ci;
+ else
+ {
+ err("invalid repetition count in DATA statement");
+ frexpr(repp);
+ goto ret;
+ }
+ frexpr(repp);
+
+ if( ! ISCONST(valp) ) {
+ if (valp->tag == TADDR
+ && valp->addrblock.uname_tag == UNAM_CONST) {
+ /* kludge */
+ frexpr(valp->addrblock.memoffset);
+ valp->tag = TCONST;
+ }
+ else {
+ err("non-constant initializer");
+ goto ret;
+ }
+ }
+
+ if(toomanyinit) goto ret;
+ for(i = 0 ; i < nrep ; ++i)
+ {
+ p = nextdata(&elen);
+ if(p == NULL)
+ {
+ err("too many initializers");
+ toomanyinit = YES;
+ goto ret;
+ }
+ setdata((Addrp)p, (Constp)valp, elen);
+ frexpr((expptr)p);
+ }
+
+ret:
+ frexpr(valp);
+}
+
+
+ Addrp
+#ifdef KR_headers
+nextdata(elenp)
+ ftnint *elenp;
+#else
+nextdata(ftnint *elenp)
+#endif
+{
+ register struct Impldoblock *ip;
+ struct Primblock *pp;
+ register Namep np;
+ register struct Rplblock *rp;
+ tagptr p;
+ expptr neltp;
+ register expptr q;
+ int skip;
+ ftnint off, vlen;
+
+ while(curdtp)
+ {
+ p = (tagptr)curdtp->datap;
+ if(p->tag == TIMPLDO)
+ {
+ ip = &(p->impldoblock);
+ if(ip->implb==NULL || ip->impub==NULL || ip->varnp==NULL)
+ fatali("bad impldoblock 0%o", (int) ip);
+ if(ip->isactive)
+ ip->varvp->Const.ci += ip->impdiff;
+ else
+ {
+ q = fixtype(cpexpr(ip->implb));
+ if( ! ISICON(q) )
+ goto doerr;
+ ip->varvp = (Constp) q;
+
+ if(ip->impstep)
+ {
+ q = fixtype(cpexpr(ip->impstep));
+ if( ! ISICON(q) )
+ goto doerr;
+ ip->impdiff = q->constblock.Const.ci;
+ frexpr(q);
+ }
+ else
+ ip->impdiff = 1;
+
+ q = fixtype(cpexpr(ip->impub));
+ if(! ISICON(q))
+ goto doerr;
+ ip->implim = q->constblock.Const.ci;
+ frexpr(q);
+
+ ip->isactive = YES;
+ rp = ALLOC(Rplblock);
+ rp->rplnextp = rpllist;
+ rpllist = rp;
+ rp->rplnp = ip->varnp;
+ rp->rplvp = (expptr) (ip->varvp);
+ rp->rpltag = TCONST;
+ }
+
+ if( (ip->impdiff>0 && (ip->varvp->Const.ci <= ip->implim))
+ || (ip->impdiff<0 && (ip->varvp->Const.ci >= ip->implim)) )
+ { /* start new loop */
+ curdtp = ip->datalist;
+ goto next;
+ }
+
+ /* clean up loop */
+
+ if(rpllist)
+ {
+ rp = rpllist;
+ rpllist = rpllist->rplnextp;
+ free( (charptr) rp);
+ }
+ else
+ Fatal("rpllist empty");
+
+ frexpr((expptr)ip->varvp);
+ ip->isactive = NO;
+ curdtp = curdtp->nextp;
+ goto next;
+ }
+
+ pp = (struct Primblock *) p;
+ np = pp->namep;
+ cur_varname = np->fvarname;
+ skip = YES;
+
+ if(p->primblock.argsp==NULL && np->vdim!=NULL)
+ { /* array initialization */
+ q = (expptr) mkaddr(np);
+ off = typesize[np->vtype] * curdtelt;
+ if(np->vtype == TYCHAR)
+ off *= np->vleng->constblock.Const.ci;
+ q->addrblock.memoffset =
+ mkexpr(OPPLUS, q->addrblock.memoffset, mkintcon(off) );
+ if( (neltp = np->vdim->nelt) && ISCONST(neltp))
+ {
+ if(++curdtelt < neltp->constblock.Const.ci)
+ skip = NO;
+ }
+ else
+ err("attempt to initialize adjustable array");
+ }
+ else
+ q = mklhs((struct Primblock *)cpexpr((expptr)pp), 0);
+ if(skip)
+ {
+ curdtp = curdtp->nextp;
+ curdtelt = 0;
+ }
+ if(q->headblock.vtype == TYCHAR)
+ if(ISICON(q->headblock.vleng))
+ *elenp = q->headblock.vleng->constblock.Const.ci;
+ else {
+ err("initialization of string of nonconstant length");
+ continue;
+ }
+ else *elenp = typesize[q->headblock.vtype];
+
+ if (np->vstg == STGBSS) {
+ vlen = np->vtype==TYCHAR
+ ? np->vleng->constblock.Const.ci
+ : typesize[np->vtype];
+ if(vlen > 0)
+ np->vstg = STGINIT;
+ }
+ return( (Addrp) q );
+
+doerr:
+ err("nonconstant implied DO parameter");
+ frexpr(q);
+ curdtp = curdtp->nextp;
+
+next:
+ curdtelt = 0;
+ }
+
+ return(NULL);
+}
+
+
+
+LOCAL FILEP dfile;
+
+ void
+#ifdef KR_headers
+setdata(varp, valp, elen)
+ register Addrp varp;
+ register Constp valp;
+ ftnint elen;
+#else
+setdata(register Addrp varp, register Constp valp, ftnint elen)
+#endif
+{
+ struct Constblock con;
+ register int type;
+ int i, k, valtype;
+ ftnint offset;
+ char *varname;
+ static Addrp badvar;
+ register unsigned char *s;
+ static int last_lineno;
+ static char *last_varname;
+
+ if (varp->vstg == STGCOMMON) {
+ if (!(dfile = blkdfile))
+ dfile = blkdfile = opf(blkdfname, textwrite);
+ }
+ else {
+ if (procclass == CLBLOCK) {
+ if (varp != badvar) {
+ badvar = varp;
+ warn1("%s is not in a COMMON block",
+ varp->uname_tag == UNAM_NAME
+ ? varp->user.name->fvarname
+ : "???");
+ }
+ return;
+ }
+ if (!(dfile = initfile))
+ dfile = initfile = opf(initfname, textwrite);
+ }
+ varname = dataname(varp->vstg, varp->memno);
+ offset = varp->memoffset->constblock.Const.ci;
+ type = varp->vtype;
+ valtype = valp->vtype;
+ if(type!=TYCHAR && valtype==TYCHAR)
+ {
+ if(! ftn66flag
+ && (last_varname != cur_varname || last_lineno != lineno)) {
+ /* prevent multiple warnings */
+ last_lineno = lineno;
+ warn1(
+ "non-character datum %.42s initialized with character string",
+ last_varname = cur_varname);
+ }
+ varp->vleng = ICON(typesize[type]);
+ varp->vtype = type = TYCHAR;
+ }
+ else if( (type==TYCHAR && valtype!=TYCHAR) ||
+ (cktype(OPASSIGN,type,valtype) == TYERROR) )
+ {
+ err("incompatible types in initialization");
+ return;
+ }
+ if(type == TYADDR)
+ con.Const.ci = valp->Const.ci;
+ else if(type != TYCHAR)
+ {
+ if(valtype == TYUNKNOWN)
+ con.Const.ci = valp->Const.ci;
+ else consconv(type, &con, valp);
+ }
+
+ k = 1;
+
+ switch(type)
+ {
+ case TYLOGICAL:
+ case TYINT1:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ dataline(varname, offset, type);
+ prconi(dfile, con.Const.ci);
+ break;
+
+ case TYADDR:
+ dataline(varname, offset, type);
+ prcona(dfile, con.Const.ci);
+ break;
+
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ k = 2;
+ case TYREAL:
+ case TYDREAL:
+ dataline(varname, offset, type);
+ prconr(dfile, &con, k);
+ break;
+
+ case TYCHAR:
+ k = valp -> vleng -> constblock.Const.ci;
+ if (elen < k)
+ k = elen;
+ s = (unsigned char *)valp->Const.ccp;
+ for(i = 0 ; i < k ; ++i) {
+ dataline(varname, offset++, TYCHAR);
+ fprintf(dfile, "\t%d\n", *s++);
+ }
+ k = elen - valp->vleng->constblock.Const.ci;
+ if(k > 0) {
+ dataline(varname, offset, TYBLANK);
+ fprintf(dfile, "\t%d\n", k);
+ }
+ break;
+
+ default:
+ badtype("setdata", type);
+ }
+
+}
+
+
+
+/*
+ output form of name is padded with blanks and preceded
+ with a storage class digit
+*/
+ char*
+#ifdef KR_headers
+dataname(stg, memno)
+ int stg;
+ long memno;
+#else
+dataname(int stg, long memno)
+#endif
+{
+ static char varname[64];
+ register char *s, *t;
+ char buf[16];
+
+ if (stg == STGCOMMON) {
+ varname[0] = '2';
+ sprintf(s = buf, "Q.%ld", memno);
+ }
+ else {
+ varname[0] = stg==STGEQUIV ? '1' : '0';
+ s = memname(stg, memno);
+ }
+ t = varname + 1;
+ while(*t++ = *s++);
+ *t = 0;
+ return(varname);
+}
+
+
+
+
+ void
+#ifdef KR_headers
+frdata(p0)
+ chainp p0;
+#else
+frdata(chainp p0)
+#endif
+{
+ register struct Chain *p;
+ register tagptr q;
+
+ for(p = p0 ; p ; p = p->nextp)
+ {
+ q = (tagptr)p->datap;
+ if(q->tag == TIMPLDO)
+ {
+ if(q->impldoblock.isbusy)
+ return; /* circular chain completed */
+ q->impldoblock.isbusy = YES;
+ frdata(q->impldoblock.datalist);
+ free( (charptr) q);
+ }
+ else
+ frexpr(q);
+ }
+
+ frchain( &p0);
+}
+
+
+ void
+#ifdef KR_headers
+dataline(varname, offset, type)
+ char *varname;
+ ftnint offset;
+ int type;
+#else
+dataline(char *varname, ftnint offset, int type)
+#endif
+{
+ fprintf(dfile, datafmt, varname, offset, type);
+}
+
+ void
+#ifdef KR_headers
+make_param(p, e)
+ register struct Paramblock *p;
+ expptr e;
+#else
+make_param(register struct Paramblock *p, expptr e)
+#endif
+{
+ register expptr q;
+ struct Constblock qc;
+
+ if (p->vstg == STGARG)
+ errstr("Dummy argument %.50s appears in a parameter statement.",
+ p->fvarname);
+ p->vclass = CLPARAM;
+ impldcl((Namep)p);
+ if (e->headblock.vtype != TYCHAR)
+ e = putx(fixtype(e));
+ p->paramval = q = mkconv(p->vtype, e);
+ if (p->vtype == TYCHAR) {
+ if (q->tag == TEXPR)
+ p->paramval = q = fixexpr((Exprp)q);
+ if (q->tag == TADDR && q->addrblock.uname_tag == UNAM_CONST) {
+ qc.Const = q->addrblock.user.Const;
+ qc.tag = TCONST;
+ qc.vtype = q->addrblock.vtype;
+ qc.vleng = q->addrblock.vleng;
+ q = (expptr)&qc;
+ }
+ if (!ISCONST(q) || q->constblock.vtype != TYCHAR) {
+ errstr("invalid value for character parameter %s",
+ p->fvarname);
+ return;
+ }
+ if (!(e = p->vleng))
+ p->vleng = ICON(q->constblock.vleng->constblock.Const.ci
+ + q->constblock.Const.ccp1.blanks);
+ else if (q->constblock.vleng->constblock.Const.ci
+ > e->constblock.Const.ci) {
+ q->constblock.vleng->constblock.Const.ci
+ = e->constblock.Const.ci;
+ q->constblock.Const.ccp1.blanks = 0;
+ }
+ else
+ q->constblock.Const.ccp1.blanks
+ = e->constblock.Const.ci
+ - q->constblock.vleng->constblock.Const.ci;
+ }
+ }
diff --git a/usr.bin/f2c/defines.h b/usr.bin/f2c/defines.h
new file mode 100644
index 0000000..db23ade
--- /dev/null
+++ b/usr.bin/f2c/defines.h
@@ -0,0 +1,290 @@
+#define PDP11 4
+
+#define BIGGEST_CHAR 0x7f /* Assumes 32-bit arithmetic */
+#define BIGGEST_SHORT 0x7fff /* Assumes 32-bit arithmetic */
+#define BIGGEST_LONG 0x7fffffff /* Assumes 32-bit arithmetic */
+
+#define M(x) (1<<x) /* Mask (x) returns 2^x */
+
+#define ALLOC(x) (struct x *) ckalloc((int)sizeof(struct x))
+#define ALLEXPR (expptr) ckalloc((int)sizeof(union Expression) )
+typedef int *ptr;
+typedef char *charptr;
+typedef FILE *FILEP;
+typedef int flag;
+typedef char field; /* actually need only 4 bits */
+typedef long int ftnint;
+#define LOCAL static
+
+#define NO 0
+#define YES 1
+
+#define CNULL (char *) 0 /* Character string null */
+#define PNULL (ptr) 0
+#define CHNULL (chainp) 0 /* Chain null */
+#define ENULL (expptr) 0
+
+
+/* BAD_MEMNO - used to distinguish between long string constants and other
+ constants in the table */
+
+#define BAD_MEMNO -32768
+
+
+/* block tag values -- syntactic stuff */
+
+#define TNAME 1
+#define TCONST 2
+#define TEXPR 3
+#define TADDR 4
+#define TPRIM 5 /* Primitive datum - should not appear in an
+ expptr variable, it should have already been
+ identified */
+#define TLIST 6
+#define TIMPLDO 7
+#define TERROR 8
+
+
+/* parser states - order is important, since there are several tests for
+ state < INDATA */
+
+#define OUTSIDE 0
+#define INSIDE 1
+#define INDCL 2
+#define INDATA 3
+#define INEXEC 4
+
+/* procedure classes */
+
+#define PROCMAIN 1
+#define PROCBLOCK 2
+#define PROCSUBR 3
+#define PROCFUNCT 4
+
+
+/* storage classes -- vstg values. BSS and INIT are used in the later
+ merge pass over identifiers; and they are entered differently into the
+ symbol table */
+
+#define STGUNKNOWN 0
+#define STGARG 1 /* adjustable dimensions */
+#define STGAUTO 2 /* for stack references */
+#define STGBSS 3 /* uninitialized storage (normal variables) */
+#define STGINIT 4 /* initialized storage */
+#define STGCONST 5
+#define STGEXT 6 /* external storage */
+#define STGINTR 7 /* intrinsic (late decision) reference. See
+ chapter 5 of the Fortran 77 standard */
+#define STGSTFUNCT 8
+#define STGCOMMON 9
+#define STGEQUIV 10
+#define STGREG 11 /* register - the outermost DO loop index will be
+ in a register (because the compiler is one
+ pass, it can't know where the innermost loop is
+ */
+#define STGLENG 12
+#define STGNULL 13
+#define STGMEMNO 14 /* interemediate-file pointer to constant table */
+
+/* name classes -- vclass values, also procclass values */
+
+#define CLUNKNOWN 0
+#define CLPARAM 1 /* Parameter - macro definition */
+#define CLVAR 2 /* variable */
+#define CLENTRY 3
+#define CLMAIN 4
+#define CLBLOCK 5
+#define CLPROC 6
+#define CLNAMELIST 7 /* in data with this tag, the vdcldone flag should
+ be ignored (according to vardcl()) */
+
+
+/* vprocclass values -- there is some overlap with the vclass values given
+ above */
+
+#define PUNKNOWN 0
+#define PEXTERNAL 1
+#define PINTRINSIC 2
+#define PSTFUNCT 3
+#define PTHISPROC 4 /* here to allow recursion - further distinction
+ is given in the CL tag (those just above).
+ This applies to the presence of the name of a
+ function used within itself. The function name
+ means either call the function again, or assign
+ some value to the storage allocated to the
+ function's return value. */
+
+/* control stack codes - these are part of a state machine which handles
+ the nesting of blocks (i.e. what to do about the ELSE statement) */
+
+#define CTLDO 1
+#define CTLIF 2
+#define CTLELSE 3
+#define CTLIFX 4
+
+
+/* operators for both Fortran input and C output. They are common because
+ so many are shared between the trees */
+
+#define OPPLUS 1
+#define OPMINUS 2
+#define OPSTAR 3
+#define OPSLASH 4
+#define OPPOWER 5
+#define OPNEG 6
+#define OPOR 7
+#define OPAND 8
+#define OPEQV 9
+#define OPNEQV 10
+#define OPNOT 11
+#define OPCONCAT 12
+#define OPLT 13
+#define OPEQ 14
+#define OPGT 15
+#define OPLE 16
+#define OPNE 17
+#define OPGE 18
+#define OPCALL 19
+#define OPCCALL 20
+#define OPASSIGN 21
+#define OPPLUSEQ 22
+#define OPSTAREQ 23
+#define OPCONV 24
+#define OPLSHIFT 25
+#define OPMOD 26
+#define OPCOMMA 27
+#define OPQUEST 28
+#define OPCOLON 29
+#define OPABS 30
+#define OPMIN 31
+#define OPMAX 32
+#define OPADDR 33
+#define OPCOMMA_ARG 34
+#define OPBITOR 35
+#define OPBITAND 36
+#define OPBITXOR 37
+#define OPBITNOT 38
+#define OPRSHIFT 39
+#define OPWHATSIN 40 /* dereferencing operator */
+#define OPMINUSEQ 41 /* assignment operators */
+#define OPSLASHEQ 42
+#define OPMODEQ 43
+#define OPLSHIFTEQ 44
+#define OPRSHIFTEQ 45
+#define OPBITANDEQ 46
+#define OPBITXOREQ 47
+#define OPBITOREQ 48
+#define OPPREINC 49 /* Preincrement (++x) operator */
+#define OPPREDEC 50 /* Predecrement (--x) operator */
+#define OPDOT 51 /* structure field reference */
+#define OPARROW 52 /* structure pointer field reference */
+#define OPNEG1 53 /* simple negation under forcedouble */
+#define OPDMIN 54 /* min(a,b) macro under forcedouble */
+#define OPDMAX 55 /* max(a,b) macro under forcedouble */
+#define OPASSIGNI 56 /* assignment for inquire stmt */
+#define OPIDENTITY 57 /* for turning TADDR into TEXPR */
+#define OPCHARCAST 58 /* for casting to char * (in I/O stmts) */
+#define OPDABS 59 /* abs macro under forcedouble */
+#define OPMIN2 60 /* min(a,b) macro */
+#define OPMAX2 61 /* max(a,b) macro */
+
+/* label type codes -- used with the ASSIGN statement */
+
+#define LABUNKNOWN 0
+#define LABEXEC 1
+#define LABFORMAT 2
+#define LABOTHER 3
+
+
+/* INTRINSIC function codes*/
+
+#define INTREND 0
+#define INTRCONV 1
+#define INTRMIN 2
+#define INTRMAX 3
+#define INTRGEN 4 /* General intrinsic, e.g. cos v. dcos, zcos, ccos */
+#define INTRSPEC 5
+#define INTRBOOL 6
+#define INTRCNST 7 /* constants, e.g. bigint(1.0) v. bigint (1d0) */
+
+
+/* I/O statement codes - these all form Integer Constants, and are always
+ reevaluated */
+
+#define IOSTDIN ICON(5)
+#define IOSTDOUT ICON(6)
+#define IOSTDERR ICON(0)
+
+#define IOSBAD (-1)
+#define IOSPOSITIONAL 0
+#define IOSUNIT 1
+#define IOSFMT 2
+
+#define IOINQUIRE 1
+#define IOOPEN 2
+#define IOCLOSE 3
+#define IOREWIND 4
+#define IOBACKSPACE 5
+#define IOENDFILE 6
+#define IOREAD 7
+#define IOWRITE 8
+
+
+/* User name tags -- these identify the form of the original identifier
+ stored in a struct Addrblock structure (in the user field). */
+
+#define UNAM_UNKNOWN 0 /* Not specified */
+#define UNAM_NAME 1 /* Local symbol, store in the hash table */
+#define UNAM_IDENT 2 /* Character string not stored elsewhere */
+#define UNAM_EXTERN 3 /* External reference; check symbol table
+ using memno as index */
+#define UNAM_CONST 4 /* Constant value */
+#define UNAM_CHARP 5 /* pointer to string */
+#define UNAM_REF 6 /* subscript reference with -s */
+
+
+#define IDENT_LEN 31 /* Maximum length user.ident */
+
+/* type masks - TYLOGICAL defined in ftypes */
+
+#define MSKLOGICAL M(TYLOGICAL)|M(TYLOGICAL1)|M(TYLOGICAL2)
+#define MSKADDR M(TYADDR)
+#define MSKCHAR M(TYCHAR)
+#ifdef TYQUAD
+#define MSKINT M(TYINT1)|M(TYSHORT)|M(TYLONG)|M(TYQUAD)
+#else
+#define MSKINT M(TYINT1)|M(TYSHORT)|M(TYLONG)
+#endif
+#define MSKREAL M(TYREAL)|M(TYDREAL) /* DREAL means Double Real */
+#define MSKCOMPLEX M(TYCOMPLEX)|M(TYDCOMPLEX)
+#define MSKSTATIC (M(STGINIT)|M(STGBSS)|M(STGCOMMON)|M(STGEQUIV)|M(STGCONST))
+
+/* miscellaneous macros */
+
+/* ONEOF (x, y) -- x is the number of one of the OR'ed masks in y (i.e., x is
+ the log of one of the OR'ed masks in y) */
+
+#define ONEOF(x,y) (M(x) & (y))
+#define ISCOMPLEX(z) ONEOF(z, MSKCOMPLEX)
+#define ISREAL(z) ONEOF(z, MSKREAL)
+#define ISNUMERIC(z) ONEOF(z, MSKINT|MSKREAL|MSKCOMPLEX)
+#define ISICON(z) (z->tag==TCONST && ISINT(z->constblock.vtype))
+#define ISLOGICAL(z) ONEOF(z, MSKLOGICAL)
+
+/* ISCHAR assumes that z has some kind of structure, i.e. is not null */
+
+#define ISCHAR(z) (z->headblock.vtype==TYCHAR)
+#define ISINT(z) ONEOF(z, MSKINT) /* z is a tag, i.e. a mask number */
+#define ISCONST(z) (z->tag==TCONST)
+#define ISERROR(z) (z->tag==TERROR)
+#define ISPLUSOP(z) (z->tag==TEXPR && z->exprblock.opcode==OPPLUS)
+#define ISSTAROP(z) (z->tag==TEXPR && z->exprblock.opcode==OPSTAR)
+#define ISONE(z) (ISICON(z) && z->constblock.Const.ci==1)
+#define INT(z) ONEOF(z, MSKINT|MSKCHAR) /* has INT storage in real life */
+#define ICON(z) mkintcon( (ftnint)(z) )
+
+/* NO66 -- F77 feature is being used
+ NOEXT -- F77 extension is being used */
+
+#define NO66(s) if(no66flag) err66(s)
+#define NOEXT(s) if(noextflag) errext(s)
diff --git a/usr.bin/f2c/defs.h b/usr.bin/f2c/defs.h
new file mode 100644
index 0000000..3404f14
--- /dev/null
+++ b/usr.bin/f2c/defs.h
@@ -0,0 +1,1053 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories, Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "sysdep.h"
+
+#include "ftypes.h"
+#include "defines.h"
+#include "machdefs.h"
+
+#define MAXDIM 20
+#define MAXINCLUDES 10
+#define MAXLITERALS 200 /* Max number of constants in the literal
+ pool */
+#define MAXCTL 20
+#define MAXHASH 401
+#define MAXSTNO 801
+#define MAXEXT 200
+#define MAXEQUIV 150
+#define MAXLABLIST 258 /* Max number of labels in an alternate
+ return CALL or computed GOTO */
+#define MAXCONTIN 99 /* Max continuation lines */
+
+/* These are the primary pointer types used in the compiler */
+
+typedef union Expression *expptr, *tagptr;
+typedef struct Chain *chainp;
+typedef struct Addrblock *Addrp;
+typedef struct Constblock *Constp;
+typedef struct Exprblock *Exprp;
+typedef struct Nameblock *Namep;
+
+extern FILEP infile;
+extern FILEP diagfile;
+extern FILEP textfile;
+extern FILEP asmfile;
+extern FILEP c_file; /* output file for all functions; extern
+ declarations will have to be prepended */
+extern FILEP pass1_file; /* Temp file to hold the function bodies
+ read on pass 1 */
+extern FILEP expr_file; /* Debugging file */
+extern FILEP initfile; /* Intermediate data file pointer */
+extern FILEP blkdfile; /* BLOCK DATA file */
+
+extern int current_ftn_file;
+extern int maxcontin;
+
+extern char *blkdfname, *initfname, *sortfname;
+extern long headoffset; /* Since the header block requires data we
+ don't know about until AFTER each
+ function has been processed, we keep a
+ pointer to the current (dummy) header
+ block (at the top of the assembly file)
+ here */
+
+extern char main_alias[]; /* name given to PROGRAM psuedo-op */
+extern char *token;
+extern int maxtoklen, toklen;
+extern long lineno;
+extern char *infname;
+extern int needkwd;
+extern struct Labelblock *thislabel;
+
+/* Used to allow runtime expansion of internal tables. In particular,
+ these values can exceed their associated constants */
+
+extern int maxctl;
+extern int maxequiv;
+extern int maxstno;
+extern int maxhash;
+extern int maxext;
+
+extern flag nowarnflag;
+extern flag ftn66flag; /* Generate warnings when weird f77
+ features are used (undeclared dummy
+ procedure, non-char initialized with
+ string, 1-dim subscript in EQUIV) */
+extern flag no66flag; /* Generate an error when a generic
+ function (f77 feature) is used */
+extern flag noextflag; /* Generate an error when an extension to
+ Fortran 77 is used (hex/oct/bin
+ constants, automatic, static, double
+ complex types) */
+extern flag zflag; /* enable double complex intrinsics */
+extern flag shiftcase;
+extern flag undeftype;
+extern flag shortsubs; /* Use short subscripts on arrays? */
+extern flag onetripflag; /* if true, always execute DO loop body */
+extern flag checksubs;
+extern flag debugflag;
+extern int nerr;
+extern int nwarn;
+
+extern int parstate;
+extern flag headerdone; /* True iff the current procedure's header
+ data has been written */
+extern int blklevel;
+extern flag saveall;
+extern flag substars; /* True iff some formal parameter is an
+ asterisk */
+extern int impltype[ ];
+extern ftnint implleng[ ];
+extern int implstg[ ];
+
+extern int tycomplex, tyint, tyioint, tyreal;
+extern int tylog, tylogical; /* TY____ of the implementation of logical.
+ This will be LONG unless '-2' is given
+ on the command line */
+extern int type_choice[];
+extern char *typename[];
+
+extern int typesize[]; /* size (in bytes) of an object of each
+ type. Indexed by TY___ macros */
+extern int typealign[];
+extern int proctype; /* Type of return value in this procedure */
+extern char * procname; /* External name of the procedure, or last ENTRY name */
+extern int rtvlabel[ ]; /* Return value labels, indexed by TY___ macros */
+extern Addrp retslot;
+extern Addrp xretslot[];
+extern int cxslot; /* Complex return argument slot (frame pointer offset)*/
+extern int chslot; /* Character return argument slot (fp offset) */
+extern int chlgslot; /* Argument slot for length of character buffer */
+extern int procclass; /* Class of the current procedure: either CLPROC,
+ CLMAIN, CLBLOCK or CLUNKNOWN */
+extern ftnint procleng; /* Length of function return value (e.g. char
+ string length). If this is -1, then the length is
+ not known at compile time */
+extern int nentry; /* Number of entry points (other than the original
+ function call) into this procedure */
+extern flag multitype; /* YES iff there is more than one return value
+ possible */
+extern int blklevel;
+extern long lastiolabno;
+extern int lastlabno;
+extern int lastvarno;
+extern int lastargslot; /* integer offset pointing to the next free
+ location for an argument to the current routine */
+extern int argloc;
+extern int autonum[]; /* for numbering
+ automatic variables, e.g. temporaries */
+extern int retlabel;
+extern int ret0label;
+extern int dorange; /* Number of the label which terminates
+ the innermost DO loop */
+extern int regnum[ ]; /* Numbers of DO indicies named in
+ regnamep (below) */
+extern Namep regnamep[ ]; /* List of DO indicies in registers */
+extern int maxregvar; /* number of elts in regnamep */
+extern int highregvar; /* keeps track of the highest register
+ number used by DO index allocator */
+extern int nregvar; /* count of DO indicies in registers */
+
+extern chainp templist[];
+extern int maxdim;
+extern chainp earlylabs;
+extern chainp holdtemps;
+extern struct Entrypoint *entries;
+extern struct Rplblock *rpllist;
+extern struct Chain *curdtp;
+extern ftnint curdtelt;
+extern chainp allargs; /* union of args in entries */
+extern int nallargs; /* total number of args */
+extern int nallchargs; /* total number of character args */
+extern flag toomanyinit; /* True iff too many initializers in a
+ DATA statement */
+
+extern flag inioctl;
+extern int iostmt;
+extern Addrp ioblkp;
+extern int nioctl;
+extern int nequiv;
+extern int eqvstart; /* offset to eqv number to guarantee uniqueness
+ and prevent <something> from going negative */
+extern int nintnames;
+
+/* Chain of tagged blocks */
+
+struct Chain
+ {
+ chainp nextp;
+ char * datap; /* Tagged block */
+ };
+
+extern chainp chains;
+
+/* Recall that field is intended to hold four-bit characters */
+
+/* This structure exists only to defeat the type checking */
+
+struct Headblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg;
+ expptr vleng; /* Expression for length of char string -
+ this may be a constant, or an argument
+ generated by mkarg() */
+ } ;
+
+/* Control construct info (for do loops, else, etc) */
+
+struct Ctlframe
+ {
+ unsigned ctltype:8;
+ unsigned dostepsign:8; /* 0 - variable, 1 - pos, 2 - neg */
+ unsigned dowhile:1;
+ int ctlabels[4]; /* Control labels, defined below */
+ int dolabel; /* label marking end of this DO loop */
+ Namep donamep; /* DO index variable */
+ expptr domax; /* constant or temp variable holding MAX
+ loop value; or expr of while(expr) */
+ expptr dostep; /* expression */
+ Namep loopname;
+ };
+#define endlabel ctlabels[0]
+#define elselabel ctlabels[1]
+#define dobodylabel ctlabels[1]
+#define doposlabel ctlabels[2]
+#define doneglabel ctlabels[3]
+extern struct Ctlframe *ctls; /* Keeps info on DO and BLOCK IF
+ structures - this is the stack
+ bottom */
+extern struct Ctlframe *ctlstack; /* Pointer to current nesting
+ level */
+extern struct Ctlframe *lastctl; /* Point to end of
+ dynamically-allocated array */
+
+typedef struct {
+ int type;
+ chainp cp;
+ } Atype;
+
+typedef struct {
+ int defined, dnargs, nargs, changes;
+ Atype atypes[1];
+ } Argtypes;
+
+/* External Symbols */
+
+struct Extsym
+ {
+ char *fextname; /* Fortran version of external name */
+ char *cextname; /* C version of external name */
+ field extstg; /* STG -- should be COMMON, UNKNOWN or EXT
+ */
+ unsigned extype:4; /* for transmitting type to output routines */
+ unsigned used_here:1; /* Boolean - true on the second pass
+ through a function if the block has
+ been referenced */
+ unsigned exused:1; /* Has been used (for help with error msgs
+ about externals typed differently in
+ different modules) */
+ unsigned exproto:1; /* type specified in a .P file */
+ unsigned extinit:1; /* Procedure has been defined,
+ or COMMON has DATA */
+ unsigned extseen:1; /* True if previously referenced */
+ chainp extp; /* List of identifiers in the common
+ block for this function, stored as
+ Namep (hash table pointers) */
+ chainp allextp; /* List of lists of identifiers; we keep one
+ list for each layout of this common block */
+ int curno; /* current number for this common block,
+ used for constructing appending _nnn
+ to the common block name */
+ int maxno; /* highest curno value for this common block */
+ ftnint extleng;
+ ftnint maxleng;
+ Argtypes *arginfo;
+ };
+typedef struct Extsym Extsym;
+
+extern Extsym *extsymtab; /* External symbol table */
+extern Extsym *nextext;
+extern Extsym *lastext;
+extern int complex_seen, dcomplex_seen;
+
+/* Statement labels */
+
+struct Labelblock
+ {
+ int labelno; /* Internal label */
+ unsigned blklevel:8; /* level of nesting, for branch-in-loop
+ checking */
+ unsigned labused:1;
+ unsigned fmtlabused:1;
+ unsigned labinacc:1; /* inaccessible? (i.e. has its scope
+ vanished) */
+ unsigned labdefined:1; /* YES or NO */
+ unsigned labtype:2; /* LAB{FORMAT,EXEC,etc} */
+ ftnint stateno; /* Original label */
+ char *fmtstring; /* format string */
+ };
+
+extern struct Labelblock *labeltab; /* Label table - keeps track of
+ all labels, including undefined */
+extern struct Labelblock *labtabend;
+extern struct Labelblock *highlabtab;
+
+/* Entry point list */
+
+struct Entrypoint
+ {
+ struct Entrypoint *entnextp;
+ Extsym *entryname; /* Name of this ENTRY */
+ chainp arglist;
+ int typelabel; /* Label for function exit; this
+ will return the proper type of
+ object */
+ Namep enamep; /* External name */
+ };
+
+/* Primitive block, or Primary block. This is a general template returned
+ by the parser, which will be interpreted in context. It is a template
+ for an identifier (variable name, function name), parenthesized
+ arguments (array subscripts, function parameters) and substring
+ specifications. */
+
+struct Primblock
+ {
+ field tag;
+ field vtype;
+ unsigned parenused:1; /* distinguish (a) from a */
+ Namep namep; /* Pointer to structure Nameblock */
+ struct Listblock *argsp;
+ expptr fcharp; /* first-char-index-pointer (in
+ substring) */
+ expptr lcharp; /* last-char-index-pointer (in
+ substring) */
+ };
+
+
+struct Hashentry
+ {
+ int hashval;
+ Namep varp;
+ };
+extern struct Hashentry *hashtab; /* Hash table */
+extern struct Hashentry *lasthash;
+
+struct Intrpacked /* bits for intrinsic function description */
+ {
+ unsigned f1:3;
+ unsigned f2:4;
+ unsigned f3:7;
+ unsigned f4:1;
+ };
+
+struct Nameblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg;
+ expptr vleng; /* length of character string, if applicable */
+ char *fvarname; /* name in the Fortran source */
+ char *cvarname; /* name in the resulting C */
+ chainp vlastdim; /* datap points to new_vars entry for the */
+ /* system variable, if any, storing the final */
+ /* dimension; we zero the datap if this */
+ /* variable is needed */
+ unsigned vprocclass:3; /* P____ macros - selects the varxptr
+ field below */
+ unsigned vdovar:1; /* "is it a DO variable?" for register
+ and multi-level loop checking */
+ unsigned vdcldone:1; /* "do I think I'm done?" - set when the
+ context is sufficient to determine its
+ status */
+ unsigned vadjdim:1; /* "adjustable dimension?" - needed for
+ information about copies */
+ unsigned vsave:1;
+ unsigned vimpldovar:1; /* used to prevent erroneous error messages
+ for variables used only in DATA stmt
+ implicit DOs */
+ unsigned vis_assigned:1;/* True if this variable has had some
+ label ASSIGNED to it; hence
+ varxptr.assigned_values is valid */
+ unsigned vimplstg:1; /* True if storage type is assigned implicitly;
+ this allows a COMMON variable to participate
+ in a DIMENSION before the COMMON declaration.
+ */
+ unsigned vcommequiv:1; /* True if EQUIVALENCEd onto STGCOMMON */
+ unsigned vfmt_asg:1; /* True if char *var_fmt needed */
+ unsigned vpassed:1; /* True if passed as a character-variable arg */
+ unsigned vknownarg:1; /* True if seen in a previous entry point */
+ unsigned visused:1; /* True if variable is referenced -- so we */
+ /* can omit variables that only appear in DATA */
+ unsigned vnamelist:1; /* Appears in a NAMELIST */
+ unsigned vimpltype:1; /* True if implicitly typed and not
+ invoked as a function or subroutine
+ (so we can consistently type procedures
+ declared external and passed as args
+ but never invoked).
+ */
+ unsigned vtypewarned:1; /* so we complain just once about
+ changed types of external procedures */
+ unsigned vinftype:1; /* so we can restore implicit type to a
+ procedure if it is invoked as a function
+ after being given a different type by -it */
+ unsigned vinfproc:1; /* True if -it infers this to be a procedure */
+ unsigned vcalled:1; /* has been invoked */
+ unsigned vdimfinish:1; /* need to invoke dim_finish() */
+ unsigned vrefused:1; /* Need to #define name_ref (for -s) */
+ unsigned vsubscrused:1; /* Need to #define name_subscr (for -2) */
+ unsigned veqvadjust:1; /* voffset has been adjusted for equivalence */
+
+/* The vardesc union below is used to store the number of an intrinsic
+ function (when vstg == STGINTR and vprocclass == PINTRINSIC), or to
+ store the index of this external symbol in extsymtab (when vstg ==
+ STGEXT and vprocclass == PEXTERNAL) */
+
+ union {
+ int varno; /* Return variable for a function.
+ This is used when a function is
+ assigned a return value. Also
+ used to point to the COMMON
+ block, when this is a field of
+ that block. Also points to
+ EQUIV block when STGEQUIV */
+ struct Intrpacked intrdesc; /* bits for intrinsic function*/
+ } vardesc;
+ struct Dimblock *vdim; /* points to the dimensions if they exist */
+ ftnint voffset; /* offset in a storage block (the variable
+ name will be "v.%d", voffset in a
+ common blck on the vax). Also holds
+ pointers for automatic variables. When
+ STGEQUIV, this is -(offset from array
+ base) */
+ union {
+ chainp namelist; /* points to names in the NAMELIST,
+ if this is a NAMELIST name */
+ chainp vstfdesc; /* points to (formals, expr) pair */
+ chainp assigned_values; /* list of integers, each being a
+ statement label assigned to
+ this variable in the current function */
+ } varxptr;
+ int argno; /* for multiple entries */
+ Argtypes *arginfo;
+ };
+
+
+/* PARAMETER statements */
+
+struct Paramblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg;
+ expptr vleng;
+ char *fvarname;
+ char *cvarname;
+ expptr paramval;
+ } ;
+
+
+/* Expression block */
+
+struct Exprblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg;
+ expptr vleng; /* in the case of a character expression, this
+ value is inherited from the children */
+ unsigned opcode;
+ expptr leftp;
+ expptr rightp;
+ int typefixed;
+ };
+
+
+union Constant
+ {
+ struct {
+ char *ccp0;
+ ftnint blanks;
+ } ccp1;
+ ftnint ci; /* Constant longeger */
+ double cd[2];
+ char *cds[2];
+ };
+#define ccp ccp1.ccp0
+
+struct Constblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg; /* vstg = 1 when using Const.cds */
+ expptr vleng;
+ union Constant Const;
+ };
+
+
+struct Listblock
+ {
+ field tag;
+ field vtype;
+ chainp listp;
+ };
+
+
+
+/* Address block - this is the FINAL form of identifiers before being
+ sent to pass 2. We'll want to add the original identifier here so that it can
+ be preserved in the translation.
+
+ An example identifier is q.7. The "q" refers to the storage class
+ (field vstg), the 7 to the variable number (int memno). */
+
+struct Addrblock
+ {
+ field tag;
+ field vtype;
+ field vclass;
+ field vstg;
+ expptr vleng;
+ /* put union...user here so the beginning of an Addrblock
+ * is the same as a Constblock.
+ */
+ union {
+ Namep name; /* contains a pointer into the hash table */
+ char ident[IDENT_LEN + 1]; /* C string form of identifier */
+ char *Charp;
+ union Constant Const; /* Constant value */
+ struct {
+ double dfill[2];
+ field vstg1;
+ } kludge; /* so we can distinguish string vs binary
+ * floating-point constants */
+ } user;
+ long memno; /* when vstg == STGCONST, this is the
+ numeric part of the assembler label
+ where the constant value is stored */
+ expptr memoffset; /* used in subscript computations, usually */
+ unsigned istemp:1; /* used in stack management of temporary
+ variables */
+ unsigned isarray:1; /* used to show that memoffset is
+ meaningful, even if zero */
+ unsigned ntempelt:10; /* for representing temporary arrays, as
+ in concatenation */
+ unsigned dbl_builtin:1; /* builtin to be declared double */
+ unsigned charleng:1; /* so saveargtypes can get i/o calls right */
+ unsigned cmplx_sub:1; /* used in complex arithmetic under -s */
+ unsigned skip_offset:1; /* used in complex arithmetic under -s */
+ unsigned parenused:1; /* distinguish (a) from a */
+ ftnint varleng; /* holds a copy of a constant length which
+ is stored in the vleng field (e.g.
+ a double is 8 bytes) */
+ int uname_tag; /* Tag describing which of the unions()
+ below to use */
+ char *Field; /* field name when dereferencing a struct */
+}; /* struct Addrblock */
+
+
+/* Errorbock - placeholder for errors, to allow the compilation to
+ continue */
+
+struct Errorblock
+ {
+ field tag;
+ field vtype;
+ };
+
+
+/* Implicit DO block, especially related to DATA statements. This block
+ keeps track of the compiler's location in the implicit DO while it's
+ running. In particular, the isactive and isbusy flags tell where
+ it is */
+
+struct Impldoblock
+ {
+ field tag;
+ unsigned isactive:1;
+ unsigned isbusy:1;
+ Namep varnp;
+ Constp varvp;
+ chainp impdospec;
+ expptr implb;
+ expptr impub;
+ expptr impstep;
+ ftnint impdiff;
+ ftnint implim;
+ struct Chain *datalist;
+ };
+
+
+/* Each of these components has a first field called tag. This union
+ exists just for allocation simplicity */
+
+union Expression
+ {
+ field tag;
+ struct Addrblock addrblock;
+ struct Constblock constblock;
+ struct Errorblock errorblock;
+ struct Exprblock exprblock;
+ struct Headblock headblock;
+ struct Impldoblock impldoblock;
+ struct Listblock listblock;
+ struct Nameblock nameblock;
+ struct Paramblock paramblock;
+ struct Primblock primblock;
+ } ;
+
+
+
+struct Dimblock
+ {
+ int ndim;
+ expptr nelt; /* This is NULL if the array is unbounded */
+ expptr baseoffset; /* a constant or local variable holding
+ the offset in this procedure */
+ expptr basexpr; /* expression for comuting the offset, if
+ it's not constant. If this is
+ non-null, the register named in
+ baseoffset will get initialized to this
+ value in the procedure's prolog */
+ struct
+ {
+ expptr dimsize; /* constant or register holding the size
+ of this dimension */
+ expptr dimexpr; /* as above in basexpr, this is an
+ expression for computing a variable
+ dimension */
+ } dims[1]; /* Dimblocks are allocated with enough
+ space for this to become dims[ndim] */
+ };
+
+
+/* Statement function identifier stack - this holds the name and value of
+ the parameters in a statement function invocation. For example,
+
+ f(x,y,z)=x+y+z
+ .
+ .
+ y = f(1,2,3)
+
+ generates a stack of depth 3, with <x 1>, <y 2>, <z 3> AT THE INVOCATION, NOT
+ at the definition */
+
+struct Rplblock /* name replacement block */
+ {
+ struct Rplblock *rplnextp;
+ Namep rplnp; /* Name of the formal parameter */
+ expptr rplvp; /* Value of the actual parameter */
+ expptr rplxp; /* Initialization of temporary variable,
+ if required; else null */
+ int rpltag; /* Tag on the value of the actual param */
+ };
+
+
+
+/* Equivalence block */
+
+struct Equivblock
+ {
+ struct Eqvchain *equivs; /* List (Eqvchain) of primblocks
+ holding variable identifiers */
+ flag eqvinit;
+ long eqvtop;
+ long eqvbottom;
+ int eqvtype;
+ } ;
+#define eqvleng eqvtop
+
+extern struct Equivblock *eqvclass;
+
+
+struct Eqvchain
+ {
+ struct Eqvchain *eqvnextp;
+ union
+ {
+ struct Primblock *eqvlhs;
+ Namep eqvname;
+ } eqvitem;
+ long eqvoffset;
+ } ;
+
+
+
+/* For allocation purposes only, and to keep lint quiet. In particular,
+ don't count on the tag being able to tell you which structure is used */
+
+
+/* There is a tradition in Fortran that the compiler not generate the same
+ bit pattern more than is necessary. This structure is used to do just
+ that; if two integer constants have the same bit pattern, just generate
+ it once. This could be expanded to optimize without regard to type, by
+ removing the type check in putconst() */
+
+struct Literal
+ {
+ short littype;
+ short litnum; /* numeric part of the assembler
+ label for this constant value */
+ int lituse; /* usage count */
+ union {
+ ftnint litival;
+ double litdval[2];
+ ftnint litival2[2]; /* length, nblanks for strings */
+ } litval;
+ char *cds[2];
+ };
+
+extern struct Literal *litpool;
+extern int maxliterals, nliterals;
+extern char Letters[];
+#define letter(x) Letters[x]
+
+struct Dims { expptr lb, ub; };
+
+extern int forcedouble; /* force real functions to double */
+extern int doin_setbound; /* special handling for array bounds */
+extern int Ansi;
+extern char hextoi_tab[];
+#define hextoi(x) hextoi_tab[(x) & 0xff]
+extern char *casttypes[], *ftn_types[], *protorettypes[], *usedcasts[];
+extern int Castargs, infertypes;
+extern FILE *protofile;
+extern char binread[], binwrite[], textread[], textwrite[];
+extern char *ei_first, *ei_last, *ei_next;
+extern char *wh_first, *wh_last, *wh_next;
+extern char *halign, *outbuf, *outbtail;
+extern flag keepsubs;
+#ifdef TYQUAD
+extern flag use_tyquad;
+#endif
+extern int n_keywords;
+extern char *c_keywords[];
+
+#ifdef KR_headers
+#define Argdcl(x) ()
+#define Void /* void */
+#else
+#define Argdcl(x) x
+#define Void void
+#endif
+
+char* Alloc Argdcl((int));
+char* Argtype Argdcl((int, char*));
+void Fatal Argdcl((char*));
+struct Impldoblock* mkiodo Argdcl((chainp, chainp));
+tagptr Inline Argdcl((int, int, chainp));
+struct Labelblock* execlab Argdcl((long));
+struct Labelblock* mklabel Argdcl((long));
+struct Listblock* mklist Argdcl((chainp));
+void Un_link_all Argdcl((int));
+void add_extern_to_list Argdcl((Addrp, chainp*));
+int addressable Argdcl((tagptr));
+tagptr addrof Argdcl((tagptr));
+char* addunder Argdcl((char*));
+Addrp autovar Argdcl((int, int, tagptr, char*));
+void backup Argdcl((char*, char*));
+void bad_atypes Argdcl((Argtypes*, char*, int, int, int, char*, char*));
+int badchleng Argdcl((tagptr));
+void badop Argdcl((char*, int));
+void badstg Argdcl((char*, int));
+void badtag Argdcl((char*, int));
+void badthing Argdcl((char*, char*, int));
+void badtype Argdcl((char*, int));
+Addrp builtin Argdcl((int, char*, int));
+char* c_name Argdcl((char*, int));
+tagptr call0 Argdcl((int, char*));
+tagptr call1 Argdcl((int, char*, tagptr));
+tagptr call2 Argdcl((int, char*, tagptr, tagptr));
+tagptr call3 Argdcl((int, char*, tagptr, tagptr, tagptr));
+tagptr call4 Argdcl((int, char*, tagptr, tagptr, tagptr, tagptr));
+tagptr callk Argdcl((int, char*, chainp));
+void cast_args Argdcl((int, chainp));
+char* cds Argdcl((char*, char*));
+void changedtype Argdcl((Namep));
+ptr ckalloc Argdcl((int));
+int cktype Argdcl((int, int, int));
+void clf Argdcl((FILEP*, char*, int));
+int cmpstr Argdcl((char*, char*, long, long));
+char* c_type_decl Argdcl((int, int));
+Extsym* comblock Argdcl((char*));
+char* comm_union_name Argdcl((int));
+void consconv Argdcl((int, Constp, Constp));
+void consnegop Argdcl((Constp));
+int conssgn Argdcl((tagptr));
+char* convic Argdcl((long));
+void copy_data Argdcl((chainp));
+char* copyn Argdcl((int, char*));
+char* copys Argdcl((char*));
+tagptr cpblock Argdcl((int, char*));
+tagptr cpexpr Argdcl((tagptr));
+void cpn Argdcl((int, char*, char*));
+char* cpstring Argdcl((char*));
+void dataline Argdcl((char*, long, int));
+char* dataname Argdcl((int, long));
+void dataval Argdcl((tagptr, tagptr));
+void dclerr Argdcl((char*, Namep));
+void def_commons Argdcl((FILEP));
+void def_start Argdcl((FILEP, char*, char*, char*));
+void deregister Argdcl((Namep));
+void do_uninit_equivs Argdcl((FILEP, ptr));
+void doequiv(Void);
+int dofork(Void);
+void doinclude Argdcl((char*));
+void doio Argdcl((chainp));
+void done Argdcl((int));
+void donmlist(Void);
+int dsort Argdcl((char*, char*));
+char* dtos Argdcl((double));
+void elif_out Argdcl((FILEP, tagptr));
+void end_else_out Argdcl((FILEP));
+void enddcl(Void);
+void enddo Argdcl((int));
+void endio(Void);
+void endioctl(Void);
+void endproc(Void);
+void entrypt Argdcl((int, int, long, Extsym*, chainp));
+int eqn Argdcl((int, char*, char*));
+char* equiv_name Argdcl((int, char*));
+void err Argdcl((char*));
+void err66 Argdcl((char*));
+void errext Argdcl((char*));
+void erri Argdcl((char*, int));
+void errl Argdcl((char*, long));
+tagptr errnode(Void);
+void errstr Argdcl((char*, char*));
+void exarif Argdcl((tagptr, struct Labelblock*, struct Labelblock*, struct Labelblock*));
+void exasgoto Argdcl((Namep));
+void exassign Argdcl((Namep, struct Labelblock*));
+void excall Argdcl((Namep, struct Listblock*, int, struct Labelblock**));
+void exdo Argdcl((int, Namep, chainp));
+void execerr Argdcl((char*, char*));
+void exelif Argdcl((tagptr));
+void exelse(Void);
+void exenddo Argdcl((Namep));
+void exendif(Void);
+void exequals Argdcl((struct Primblock*, tagptr));
+void exgoto Argdcl((struct Labelblock*));
+void exif Argdcl((tagptr));
+void exreturn Argdcl((tagptr));
+void exstop Argdcl((int, tagptr));
+void extern_out Argdcl((FILEP, Extsym*));
+void fatali Argdcl((char*, int));
+void fatalstr Argdcl((char*, char*));
+void ffilecopy Argdcl((FILEP, FILEP));
+void fileinit(Void);
+int fixargs Argdcl((int, struct Listblock*));
+tagptr fixexpr Argdcl((Exprp));
+tagptr fixtype Argdcl((tagptr));
+char* flconst Argdcl((char*, char*));
+void flline(Void);
+void fmt_init(Void);
+void fmtname Argdcl((Namep, Addrp));
+int fmtstmt Argdcl((struct Labelblock*));
+tagptr fold Argdcl((tagptr));
+void frchain Argdcl((chainp*));
+void frdata Argdcl((chainp));
+void freetemps(Void);
+void freqchain Argdcl((struct Equivblock*));
+void frexchain Argdcl((chainp*));
+void frexpr Argdcl((tagptr));
+void frrpl(Void);
+void frtemp Argdcl((Addrp));
+char* gmem Argdcl((int, int));
+void hashclear(Void);
+chainp hookup Argdcl((chainp, chainp));
+expptr imagpart Argdcl((Addrp));
+void impldcl Argdcl((Namep));
+int in_vector Argdcl((char*, char**, int));
+void incomm Argdcl((Extsym*, Namep));
+void inferdcl Argdcl((Namep, int));
+int inilex Argdcl((char*));
+void initkey(Void);
+int inregister Argdcl((Namep));
+long int commlen Argdcl((chainp));
+long int convci Argdcl((int, char*));
+long int iarrlen Argdcl((Namep));
+long int lencat Argdcl((expptr));
+long int lmax Argdcl((long, long));
+long int lmin Argdcl((long, long));
+long int wr_char_len Argdcl((FILEP, struct Dimblock*, int, int));
+Addrp intraddr Argdcl((Namep));
+tagptr intrcall Argdcl((Namep, struct Listblock*, int));
+int intrfunct Argdcl((char*));
+void ioclause Argdcl((int, expptr));
+int iocname(Void);
+int is_negatable Argdcl((Constp));
+int isaddr Argdcl((tagptr));
+int isnegative_const Argdcl((Constp));
+int isstatic Argdcl((tagptr));
+chainp length_comp Argdcl((struct Entrypoint*, int));
+int lengtype Argdcl((int, long));
+char* lexline Argdcl((ptr));
+void list_arg_types Argdcl((FILEP, struct Entrypoint*, chainp, int, char*));
+void list_decls Argdcl((FILEP));
+void list_init_data Argdcl((FILE **, char *, FILE *));
+void listargs Argdcl((FILEP, struct Entrypoint*, int, chainp));
+char* lit_name Argdcl((struct Literal*));
+int log_2 Argdcl((long));
+char* lower_string Argdcl((char*, char*));
+int main Argdcl((int, char**));
+expptr make_int_expr Argdcl((expptr));
+void make_param Argdcl((struct Paramblock*, tagptr));
+void many Argdcl((char*, char, int));
+void margin_printf Argdcl((FILEP, char*, ...));
+int maxtype Argdcl((int, int));
+char* mem Argdcl((int, int));
+void mem_init(Void);
+char* memname Argdcl((int, long));
+Addrp memversion Argdcl((Namep));
+tagptr mkaddcon Argdcl((long));
+Addrp mkaddr Argdcl((Namep));
+Addrp mkarg Argdcl((int, int));
+tagptr mkbitcon Argdcl((int, int, char*));
+chainp mkchain Argdcl((char*, chainp));
+Constp mkconst Argdcl((int));
+tagptr mkconv Argdcl((int, tagptr));
+tagptr mkcxcon Argdcl((tagptr, tagptr));
+tagptr mkexpr Argdcl((int, tagptr, tagptr));
+Extsym* mkext Argdcl((char*, char*));
+Extsym* mkext1 Argdcl((char*, char*));
+Addrp mkfield Argdcl((Addrp, char*, int));
+tagptr mkfunct Argdcl((tagptr));
+tagptr mkintcon Argdcl((long));
+tagptr mklhs Argdcl((struct Primblock*, int));
+tagptr mklogcon Argdcl((int));
+Namep mkname Argdcl((char*));
+Addrp mkplace Argdcl((Namep));
+tagptr mkprim Argdcl((Namep, struct Listblock*, chainp));
+tagptr mkrealcon Argdcl((int, char*));
+Addrp mkscalar Argdcl((Namep));
+void mkstfunct Argdcl((struct Primblock*, tagptr));
+tagptr mkstrcon Argdcl((int, char*));
+Addrp mktmp Argdcl((int, tagptr));
+Addrp mktmp0 Argdcl((int, tagptr));
+Addrp mktmpn Argdcl((int, int, tagptr));
+void namelist Argdcl((Namep));
+int ncat Argdcl((expptr));
+void negate_const Argdcl((Constp));
+void new_endif(Void);
+Extsym* newentry Argdcl((Namep, int));
+int newlabel(Void);
+void newproc(Void);
+Addrp nextdata Argdcl((long*));
+void nice_printf Argdcl((FILEP, char*, ...));
+void not_both Argdcl((char*));
+void np_init(Void);
+int oneof_stg Argdcl((Namep, int, int));
+int op_assign Argdcl((int));
+tagptr opconv Argdcl((tagptr, int));
+FILEP opf Argdcl((char*, char*));
+void out_addr Argdcl((FILEP, Addrp));
+void out_asgoto Argdcl((FILEP, tagptr));
+void out_call Argdcl((FILEP, int, int, tagptr, tagptr, tagptr));
+void out_const Argdcl((FILEP, Constp));
+void out_else Argdcl((FILEP));
+void out_for Argdcl((FILEP, tagptr, tagptr, tagptr));
+void out_init(Void);
+void outbuf_adjust(Void);
+void p1_label Argdcl((long));
+void prcona Argdcl((FILEP, long));
+void prconi Argdcl((FILEP, long));
+void prconr Argdcl((FILEP, Constp, int));
+void procinit(Void);
+void procode Argdcl((FILEP));
+void prolog Argdcl((FILEP, chainp));
+void protowrite Argdcl((FILEP, int, char*, struct Entrypoint*, chainp));
+expptr prune_left_conv Argdcl((expptr));
+int put_one_arg Argdcl((int, char*, char**, char*, char*));
+expptr putassign Argdcl((expptr, expptr));
+Addrp putchop Argdcl((tagptr));
+void putcmgo Argdcl((tagptr, int, struct Labelblock**));
+Addrp putconst Argdcl((Constp));
+tagptr putcxop Argdcl((tagptr));
+void puteq Argdcl((expptr, expptr));
+void putexpr Argdcl((expptr));
+void puthead Argdcl((char*, int));
+void putif Argdcl((tagptr, int));
+void putout Argdcl((tagptr));
+expptr putsteq Argdcl((Addrp, Addrp));
+void putwhile Argdcl((tagptr));
+tagptr putx Argdcl((tagptr));
+void r8fix(Void);
+int rdlong Argdcl((FILEP, long*));
+int rdname Argdcl((FILEP, ptr, char*));
+void read_Pfiles Argdcl((char**));
+Addrp realpart Argdcl((Addrp));
+chainp revchain Argdcl((chainp));
+int same_expr Argdcl((tagptr, tagptr));
+int same_ident Argdcl((tagptr, tagptr));
+void save_argtypes Argdcl((chainp, Argtypes**, Argtypes**, int, char*, int, int, int, int));
+void saveargtypes Argdcl((Exprp));
+void set_externs(Void);
+void set_tmp_names(Void);
+void setbound Argdcl((Namep, int, struct Dims*));
+void setdata Argdcl((Addrp, Constp, long));
+void setext Argdcl((Namep));
+void setfmt Argdcl((struct Labelblock*));
+void setimpl Argdcl((int, long, int, int));
+void setintr Argdcl((Namep));
+void settype Argdcl((Namep, int, long));
+void sigcatch Argdcl((int));
+void start_formatting(Void);
+void startioctl(Void);
+void startproc Argdcl((Extsym*, int));
+void startrw(Void);
+char* string_num Argdcl((char*, long));
+int struct_eq Argdcl((chainp, chainp));
+tagptr subcheck Argdcl((Namep, tagptr));
+tagptr suboffset Argdcl((struct Primblock*));
+int type_fixup Argdcl((Argtypes*, Atype*, int));
+void unamstring Argdcl((Addrp, char*));
+void unclassifiable(Void);
+void vardcl Argdcl((Namep));
+void warn Argdcl((char*));
+void warn1 Argdcl((char*, char*));
+void warni Argdcl((char*, int));
+void wr_abbrevs Argdcl((FILEP, int, chainp));
+char* wr_ardecls Argdcl((FILE*, struct Dimblock*, long));
+void wr_array_init Argdcl((FILEP, int, chainp));
+void wr_common_decls Argdcl((FILEP));
+void wr_equiv_init Argdcl((FILEP, int, chainp*, int));
+void wr_globals Argdcl((FILEP));
+void wr_nv_ident_help Argdcl((FILEP, Addrp));
+void wr_struct Argdcl((FILEP, chainp));
+void wronginf Argdcl((Namep));
+void yyerror Argdcl((char*));
+int yylex(Void);
+int yyparse(Void);
+
+#ifdef USE_DTOA
+#define atof(x) strtod(x,0)
+void g_fmt Argdcl((char*, double));
+#endif
diff --git a/usr.bin/f2c/dependencies b/usr.bin/f2c/dependencies
new file mode 100644
index 0000000..9937e0b
--- /dev/null
+++ b/usr.bin/f2c/dependencies
@@ -0,0 +1,60 @@
+f2c/src*
+Notice=
+notice
+README=
+readme
+cds.c=
+data.c=
+defines.h=
+defs.h=
+equiv.c=
+error.c=
+exec.c=
+expr.c=
+f2c.1=
+f2c.1t=
+f2c.h=
+format.c=
+format.h=
+formatdata.c=
+ftypes.h=
+gram.dcl=
+gram.exec=
+gram.expr=
+gram.head=
+gram.io=
+init.c=
+intr.c=
+io.c=
+iob.h=
+lex.c=
+machdefs.h=
+main.c=
+makefile=
+malloc.c=
+mem.c=
+memset.c=
+misc.c=
+names.c=
+names.h=
+niceprintf.c=
+niceprintf.h=
+output.c=
+output.h=
+p1defs.h=
+p1output.c=
+parse.h=
+parse_args.c=
+pccdefs.h=
+pread.c=
+proc.c=
+put.c=
+putpcc.c=
+sysdep.c=
+sysdep.h=
+tokens=
+usignal.h=
+vax.c=
+version.c=
+xsum.c=
+xsum0.out=
diff --git a/usr.bin/f2c/disclaimer b/usr.bin/f2c/disclaimer
new file mode 100644
index 0000000..59db1ec
--- /dev/null
+++ b/usr.bin/f2c/disclaimer
@@ -0,0 +1,15 @@
+f2c is a Fortran to C converter under development by
+ David Gay (AT&T Bell Labs)
+ Stu Feldman (Bellcore)
+ Mark Maimone (Carnegie-Mellon University)
+ Norm Schryer (AT&T Bell Labs)
+Please send bug reports to dmg@research.att.com or uunet!research!dmg.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
diff --git a/usr.bin/f2c/equiv.c b/usr.bin/f2c/equiv.c
new file mode 100644
index 0000000..645a77a
--- /dev/null
+++ b/usr.bin/f2c/equiv.c
@@ -0,0 +1,398 @@
+/****************************************************************
+Copyright 1990, 1993-5 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+
+static void eqvcommon Argdcl((struct Equivblock*, int, long int));
+static void eqveqv Argdcl((int, int, long int));
+static int nsubs Argdcl((struct Listblock*));
+
+/* ROUTINES RELATED TO EQUIVALENCE CLASS PROCESSING */
+
+/* called at end of declarations section to process chains
+ created by EQUIVALENCE statements
+ */
+ void
+doequiv(Void)
+{
+ register int i;
+ int inequiv; /* True if one namep occurs in
+ several EQUIV declarations */
+ int comno; /* Index into Extsym table of the last
+ COMMON block seen (implicitly assuming
+ that only one will be given) */
+ int ovarno;
+ ftnint comoffset; /* Index into the COMMON block */
+ ftnint offset; /* Offset from array base */
+ ftnint leng;
+ register struct Equivblock *equivdecl;
+ register struct Eqvchain *q;
+ struct Primblock *primp;
+ register Namep np;
+ int k, k1, ns, pref, t;
+ chainp cp;
+ extern int type_pref[];
+ char *s;
+
+ for(i = 0 ; i < nequiv ; ++i)
+ {
+
+/* Handle each equivalence declaration */
+
+ equivdecl = &eqvclass[i];
+ equivdecl->eqvbottom = equivdecl->eqvtop = 0;
+ comno = -1;
+
+
+
+ for(q = equivdecl->equivs ; q ; q = q->eqvnextp)
+ {
+ offset = 0;
+ primp = q->eqvitem.eqvlhs;
+ vardcl(np = primp->namep);
+ if(primp->argsp || primp->fcharp)
+ {
+ expptr offp;
+
+/* Pad ones onto the end of an array declaration when needed */
+
+ if(np->vdim!=NULL && np->vdim->ndim>1 &&
+ nsubs(primp->argsp)==1 )
+ {
+ if(! ftn66flag)
+ warni
+ ("1-dim subscript in EQUIVALENCE, %d-dim declared",
+ np -> vdim -> ndim);
+ cp = NULL;
+ ns = np->vdim->ndim;
+ while(--ns > 0)
+ cp = mkchain((char *)ICON(1), cp);
+ primp->argsp->listp->nextp = cp;
+ }
+
+ offp = suboffset(primp);
+ if(ISICON(offp))
+ offset = offp->constblock.Const.ci;
+ else {
+ dclerr
+ ("nonconstant subscript in equivalence ",
+ np);
+ np = NULL;
+ }
+ frexpr(offp);
+ }
+
+/* Free up the primblock, since we now have a hash table (Namep) entry */
+
+ frexpr((expptr)primp);
+
+ if(np && (leng = iarrlen(np))<0)
+ {
+ dclerr("adjustable in equivalence", np);
+ np = NULL;
+ }
+
+ if(np) switch(np->vstg)
+ {
+ case STGUNKNOWN:
+ case STGBSS:
+ case STGEQUIV:
+ break;
+
+ case STGCOMMON:
+
+/* The code assumes that all COMMON references in a given EQUIVALENCE will
+ be to the same COMMON block, and will all be consistent */
+
+ comno = np->vardesc.varno;
+ comoffset = np->voffset + offset;
+ break;
+
+ default:
+ dclerr("bad storage class in equivalence", np);
+ np = NULL;
+ break;
+ }
+
+ if(np)
+ {
+ q->eqvoffset = offset;
+
+/* eqvbottom gets the largest difference between the array base address
+ and the address specified in the EQUIV declaration */
+
+ equivdecl->eqvbottom =
+ lmin(equivdecl->eqvbottom, -offset);
+
+/* eqvtop gets the largest difference between the end of the array and
+ the address given in the EQUIVALENCE */
+
+ equivdecl->eqvtop =
+ lmax(equivdecl->eqvtop, leng-offset);
+ }
+ q->eqvitem.eqvname = np;
+ }
+
+/* Now all equivalenced variables are in the hash table with the proper
+ offset, and eqvtop and eqvbottom are set. */
+
+ if(comno >= 0)
+
+/* Get rid of all STGEQUIVS, they will be mapped onto STGCOMMON variables
+ */
+
+ eqvcommon(equivdecl, comno, comoffset);
+ else for(q = equivdecl->equivs ; q ; q = q->eqvnextp)
+ {
+ if(np = q->eqvitem.eqvname)
+ {
+ inequiv = NO;
+ if(np->vstg==STGEQUIV)
+ if( (ovarno = np->vardesc.varno) == i)
+ {
+
+/* Can't EQUIV different elements of the same array */
+
+ if(np->voffset + q->eqvoffset != 0)
+ dclerr
+ ("inconsistent equivalence", np);
+ }
+ else {
+ offset = np->voffset;
+ inequiv = YES;
+ }
+
+ np->vstg = STGEQUIV;
+ np->vardesc.varno = i;
+ np->voffset = - q->eqvoffset;
+
+ if(inequiv)
+
+/* Combine 2 equivalence declarations */
+
+ eqveqv(i, ovarno, q->eqvoffset + offset);
+ }
+ }
+ }
+
+/* Now each equivalence declaration is distinct (all connections have been
+ merged in eqveqv()), and some may be empty. */
+
+ for(i = 0 ; i < nequiv ; ++i)
+ {
+ equivdecl = & eqvclass[i];
+ if(equivdecl->eqvbottom!=0 || equivdecl->eqvtop!=0) {
+
+/* a live chain */
+
+ k = TYCHAR;
+ pref = 1;
+ for(q = equivdecl->equivs ; q; q = q->eqvnextp)
+ if ((np = q->eqvitem.eqvname)
+ && !np->veqvadjust) {
+ np->veqvadjust = 1;
+ np->voffset -= equivdecl->eqvbottom;
+ t = typealign[k1 = np->vtype];
+ if (pref < type_pref[k1]) {
+ k = k1;
+ pref = type_pref[k1];
+ }
+ if(np->voffset % t != 0) {
+ dclerr("bad alignment forced by equivalence", np);
+ --nerr; /* don't give bad return code for this */
+ }
+ }
+ equivdecl->eqvtype = k;
+ }
+ freqchain(equivdecl);
+ }
+}
+
+
+
+
+
+/* put equivalence chain p at common block comno + comoffset */
+
+ LOCAL void
+#ifdef KR_headers
+eqvcommon(p, comno, comoffset)
+ struct Equivblock *p;
+ int comno;
+ ftnint comoffset;
+#else
+eqvcommon(struct Equivblock *p, int comno, ftnint comoffset)
+#endif
+{
+ int ovarno;
+ ftnint k, offq;
+ register Namep np;
+ register struct Eqvchain *q;
+
+ if(comoffset + p->eqvbottom < 0)
+ {
+ errstr("attempt to extend common %s backward",
+ extsymtab[comno].fextname);
+ freqchain(p);
+ return;
+ }
+
+ if( (k = comoffset + p->eqvtop) > extsymtab[comno].extleng)
+ extsymtab[comno].extleng = k;
+
+
+ for(q = p->equivs ; q ; q = q->eqvnextp)
+ if(np = q->eqvitem.eqvname)
+ {
+ switch(np->vstg)
+ {
+ case STGUNKNOWN:
+ case STGBSS:
+ np->vstg = STGCOMMON;
+ np->vcommequiv = 1;
+ np->vardesc.varno = comno;
+
+/* np -> voffset will point to the base of the array */
+
+ np->voffset = comoffset - q->eqvoffset;
+ break;
+
+ case STGEQUIV:
+ ovarno = np->vardesc.varno;
+
+/* offq will point to the current element, even if it's in an array */
+
+ offq = comoffset - q->eqvoffset - np->voffset;
+ np->vstg = STGCOMMON;
+ np->vcommequiv = 1;
+ np->vardesc.varno = comno;
+
+/* np -> voffset will point to the base of the array */
+
+ np->voffset += offq;
+ if(ovarno != (p - eqvclass))
+ eqvcommon(&eqvclass[ovarno], comno, offq);
+ break;
+
+ case STGCOMMON:
+ if(comno != np->vardesc.varno ||
+ comoffset != np->voffset+q->eqvoffset)
+ dclerr("inconsistent common usage", np);
+ break;
+
+
+ default:
+ badstg("eqvcommon", np->vstg);
+ }
+ }
+
+ freqchain(p);
+ p->eqvbottom = p->eqvtop = 0;
+}
+
+
+/* Move all items on ovarno chain to the front of nvarno chain.
+ * adjust offsets of ovarno elements and top and bottom of nvarno chain
+ */
+
+ LOCAL void
+#ifdef KR_headers
+eqveqv(nvarno, ovarno, delta)
+ int nvarno;
+ int ovarno;
+ ftnint delta;
+#else
+eqveqv(int nvarno, int ovarno, ftnint delta)
+#endif
+{
+ register struct Equivblock *neweqv, *oldeqv;
+ register Namep np;
+ struct Eqvchain *q, *q1;
+
+ neweqv = eqvclass + nvarno;
+ oldeqv = eqvclass + ovarno;
+ neweqv->eqvbottom = lmin(neweqv->eqvbottom, oldeqv->eqvbottom - delta);
+ neweqv->eqvtop = lmax(neweqv->eqvtop, oldeqv->eqvtop - delta);
+ oldeqv->eqvbottom = oldeqv->eqvtop = 0;
+
+ for(q = oldeqv->equivs ; q ; q = q1)
+ {
+ q1 = q->eqvnextp;
+ if( (np = q->eqvitem.eqvname) && np->vardesc.varno==ovarno)
+ {
+ q->eqvnextp = neweqv->equivs;
+ neweqv->equivs = q;
+ q->eqvoffset += delta;
+ np->vardesc.varno = nvarno;
+ np->voffset -= delta;
+ }
+ else free( (charptr) q);
+ }
+ oldeqv->equivs = NULL;
+}
+
+
+
+ void
+#ifdef KR_headers
+freqchain(p)
+ register struct Equivblock *p;
+#else
+freqchain(register struct Equivblock *p)
+#endif
+{
+ register struct Eqvchain *q, *oq;
+
+ for(q = p->equivs ; q ; q = oq)
+ {
+ oq = q->eqvnextp;
+ free( (charptr) q);
+ }
+ p->equivs = NULL;
+}
+
+
+
+
+
+/* nsubs -- number of subscripts in this arglist (just the length of the
+ list) */
+
+ LOCAL int
+#ifdef KR_headers
+nsubs(p)
+ register struct Listblock *p;
+#else
+nsubs(register struct Listblock *p)
+#endif
+{
+ register int n;
+ register chainp q;
+
+ n = 0;
+ if(p)
+ for(q = p->listp ; q ; q = q->nextp)
+ ++n;
+
+ return(n);
+}
diff --git a/usr.bin/f2c/error.c b/usr.bin/f2c/error.c
new file mode 100644
index 0000000..049008e
--- /dev/null
+++ b/usr.bin/f2c/error.c
@@ -0,0 +1,347 @@
+/****************************************************************
+Copyright 1990, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+
+ void
+#ifdef KR_headers
+warni(s, t)
+ char *s;
+ int t;
+#else
+warni(char *s, int t)
+#endif
+{
+ char buf[100];
+ sprintf(buf,s,t);
+ warn(buf);
+ }
+
+ void
+#ifdef KR_headers
+warn1(s, t)
+ char *s;
+ char *t;
+#else
+warn1(char *s, char *t)
+#endif
+{
+ char buff[100];
+ sprintf(buff, s, t);
+ warn(buff);
+}
+
+ void
+#ifdef KR_headers
+warn(s)
+ char *s;
+#else
+warn(char *s)
+#endif
+{
+ if(nowarnflag)
+ return;
+ if (infname && *infname)
+ fprintf(diagfile, "Warning on line %ld of %s: %s\n",
+ lineno, infname, s);
+ else
+ fprintf(diagfile, "Warning on line %ld: %s\n", lineno, s);
+ fflush(diagfile);
+ ++nwarn;
+}
+
+ void
+#ifdef KR_headers
+errstr(s, t)
+ char *s;
+ char *t;
+#else
+errstr(char *s, char *t)
+#endif
+{
+ char buff[100];
+ sprintf(buff, s, t);
+ err(buff);
+}
+
+
+ void
+#ifdef KR_headers
+erri(s, t)
+ char *s;
+ int t;
+#else
+erri(char *s, int t)
+#endif
+{
+ char buff[100];
+ sprintf(buff, s, t);
+ err(buff);
+}
+
+ void
+#ifdef KR_headers
+errl(s, t)
+ char *s;
+ long t;
+#else
+errl(char *s, long t)
+#endif
+{
+ char buff[100];
+ sprintf(buff, s, t);
+ err(buff);
+}
+
+ char *err_proc = 0;
+
+ void
+#ifdef KR_headers
+err(s)
+ char *s;
+#else
+err(char *s)
+#endif
+{
+ if (err_proc)
+ fprintf(diagfile,
+ "Error processing %s before line %ld",
+ err_proc, lineno);
+ else
+ fprintf(diagfile, "Error on line %ld", lineno);
+ if (infname && *infname)
+ fprintf(diagfile, " of %s", infname);
+ fprintf(diagfile, ": %s\n", s);
+ fflush(diagfile);
+ ++nerr;
+}
+
+ void
+#ifdef KR_headers
+yyerror(s)
+ char *s;
+#else
+yyerror(char *s)
+#endif
+{
+ err(s);
+}
+
+
+ void
+#ifdef KR_headers
+dclerr(s, v)
+ char *s;
+ Namep v;
+#else
+dclerr(char *s, Namep v)
+#endif
+{
+ char buff[100];
+
+ if(v)
+ {
+ sprintf(buff, "Declaration error for %s: %s", v->fvarname, s);
+ err(buff);
+ }
+ else
+ errstr("Declaration error %s", s);
+}
+
+
+ void
+#ifdef KR_headers
+execerr(s, n)
+ char *s;
+ char *n;
+#else
+execerr(char *s, char *n)
+#endif
+{
+ char buf1[100], buf2[100];
+
+ sprintf(buf1, "Execution error %s", s);
+ sprintf(buf2, buf1, n);
+ err(buf2);
+}
+
+
+ void
+#ifdef KR_headers
+Fatal(t)
+ char *t;
+#else
+Fatal(char *t)
+#endif
+{
+ fprintf(diagfile, "Compiler error line %ld", lineno);
+ if (infname)
+ fprintf(diagfile, " of %s", infname);
+ fprintf(diagfile, ": %s\n", t);
+ done(3);
+}
+
+
+
+ void
+#ifdef KR_headers
+fatalstr(t, s)
+ char *t;
+ char *s;
+#else
+fatalstr(char *t, char *s)
+#endif
+{
+ char buff[100];
+ sprintf(buff, t, s);
+ Fatal(buff);
+}
+
+
+ void
+#ifdef KR_headers
+fatali(t, d)
+ char *t;
+ int d;
+#else
+fatali(char *t, int d)
+#endif
+{
+ char buff[100];
+ sprintf(buff, t, d);
+ Fatal(buff);
+}
+
+
+ void
+#ifdef KR_headers
+badthing(thing, r, t)
+ char *thing;
+ char *r;
+ int t;
+#else
+badthing(char *thing, char *r, int t)
+#endif
+{
+ char buff[50];
+ sprintf(buff, "Impossible %s %d in routine %s", thing, t, r);
+ Fatal(buff);
+}
+
+
+ void
+#ifdef KR_headers
+badop(r, t)
+ char *r;
+ int t;
+#else
+badop(char *r, int t)
+#endif
+{
+ badthing("opcode", r, t);
+}
+
+
+ void
+#ifdef KR_headers
+badtag(r, t)
+ char *r;
+ int t;
+#else
+badtag(char *r, int t)
+#endif
+{
+ badthing("tag", r, t);
+}
+
+
+
+
+ void
+#ifdef KR_headers
+badstg(r, t)
+ char *r;
+ int t;
+#else
+badstg(char *r, int t)
+#endif
+{
+ badthing("storage class", r, t);
+}
+
+
+
+ void
+#ifdef KR_headers
+badtype(r, t)
+ char *r;
+ int t;
+#else
+badtype(char *r, int t)
+#endif
+{
+ badthing("type", r, t);
+}
+
+ void
+#ifdef KR_headers
+many(s, c, n)
+ char *s;
+ char c;
+ int n;
+#else
+many(char *s, char c, int n)
+#endif
+{
+ char buff[250];
+
+ sprintf(buff,
+ "Too many %s.\nTable limit now %d.\nTry rerunning with the -N%c%d option.\n",
+ s, n, c, 2*n);
+ Fatal(buff);
+}
+
+ void
+#ifdef KR_headers
+err66(s)
+ char *s;
+#else
+err66(char *s)
+#endif
+{
+ errstr("Fortran 77 feature used: %s", s);
+ --nerr;
+}
+
+
+ void
+#ifdef KR_headers
+errext(s)
+ char *s;
+#else
+errext(char *s)
+#endif
+{
+ errstr("f2c extension used: %s", s);
+ --nerr;
+}
diff --git a/usr.bin/f2c/exec.c b/usr.bin/f2c/exec.c
new file mode 100644
index 0000000..bcd1e08
--- /dev/null
+++ b/usr.bin/f2c/exec.c
@@ -0,0 +1,927 @@
+/****************************************************************
+Copyright 1990, 1993 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "p1defs.h"
+#include "names.h"
+
+static void exar2 Argdcl((int, tagptr, struct Labelblock*, struct Labelblock*));
+static void popctl Argdcl((void));
+static void pushctl Argdcl((int));
+
+/* Logical IF codes
+*/
+
+ void
+#ifdef KR_headers
+exif(p)
+ expptr p;
+#else
+exif(expptr p)
+#endif
+{
+ pushctl(CTLIF);
+ putif(p, 0); /* 0 => if, not elseif */
+}
+
+
+ void
+#ifdef KR_headers
+exelif(p)
+ expptr p;
+#else
+exelif(expptr p)
+#endif
+{
+ if (ctlstack->ctltype == CTLIF || ctlstack->ctltype == CTLIFX)
+ putif(p, 1); /* 1 ==> elseif */
+ else
+ execerr("elseif out of place", CNULL);
+}
+
+
+
+
+ void
+exelse(Void)
+{
+ register struct Ctlframe *c;
+
+ for(c = ctlstack; c->ctltype == CTLIFX; --c);
+ if(c->ctltype == CTLIF) {
+ p1_else ();
+ c->ctltype = CTLELSE;
+ }
+ else
+ execerr("else out of place", CNULL);
+ }
+
+ void
+#ifdef KR_headers
+exendif()
+#else
+exendif()
+#endif
+{
+ while(ctlstack->ctltype == CTLIFX) {
+ popctl();
+ p1else_end();
+ }
+ if(ctlstack->ctltype == CTLIF) {
+ popctl();
+ p1_endif ();
+ }
+ else if(ctlstack->ctltype == CTLELSE) {
+ popctl();
+ p1else_end ();
+ }
+ else
+ execerr("endif out of place", CNULL);
+ }
+
+
+ void
+#ifdef KR_headers
+new_endif()
+#else
+new_endif()
+#endif
+{
+ if (ctlstack->ctltype == CTLIF || ctlstack->ctltype == CTLIFX)
+ pushctl(CTLIFX);
+ else
+ err("new_endif bug");
+ }
+
+/* pushctl -- Start a new control construct, initialize the labels (to
+ zero) */
+
+ LOCAL void
+#ifdef KR_headers
+pushctl(code)
+ int code;
+#else
+pushctl(int code)
+#endif
+{
+ register int i;
+
+ if(++ctlstack >= lastctl)
+ many("loops or if-then-elses", 'c', maxctl);
+ ctlstack->ctltype = code;
+ for(i = 0 ; i < 4 ; ++i)
+ ctlstack->ctlabels[i] = 0;
+ ctlstack->dowhile = 0;
+ ctlstack->domax = ctlstack->dostep = 0; /* in case of errors */
+ ++blklevel;
+}
+
+
+ LOCAL void
+popctl(Void)
+{
+ if( ctlstack-- < ctls )
+ Fatal("control stack empty");
+ --blklevel;
+}
+
+
+
+/* poplab -- update the flags in labeltab */
+
+ LOCAL void
+poplab(Void)
+{
+ register struct Labelblock *lp;
+
+ for(lp = labeltab ; lp < highlabtab ; ++lp)
+ if(lp->labdefined)
+ {
+ /* mark all labels in inner blocks unreachable */
+ if(lp->blklevel > blklevel)
+ lp->labinacc = YES;
+ }
+ else if(lp->blklevel > blklevel)
+ {
+ /* move all labels referred to in inner blocks out a level */
+ lp->blklevel = blklevel;
+ }
+}
+
+
+/* BRANCHING CODE
+*/
+ void
+#ifdef KR_headers
+exgoto(lab)
+ struct Labelblock *lab;
+#else
+exgoto(struct Labelblock *lab)
+#endif
+{
+ lab->labused = 1;
+ p1_goto (lab -> stateno);
+}
+
+
+
+
+
+
+ void
+#ifdef KR_headers
+exequals(lp, rp)
+ register struct Primblock *lp;
+ register expptr rp;
+#else
+exequals(register struct Primblock *lp, register expptr rp)
+#endif
+{
+ if(lp->tag != TPRIM)
+ {
+ err("assignment to a non-variable");
+ frexpr((expptr)lp);
+ frexpr(rp);
+ }
+ else if(lp->namep->vclass!=CLVAR && lp->argsp)
+ {
+ if(parstate >= INEXEC)
+ errstr("statement function %.62s amid executables.",
+ lp->namep->fvarname);
+ mkstfunct(lp, rp);
+ }
+ else if (lp->vtype == TYSUBR)
+ err("illegal use of subroutine name");
+ else
+ {
+ expptr new_lp, new_rp;
+
+ if(parstate < INDATA)
+ enddcl();
+ new_lp = mklhs (lp, keepsubs);
+ new_rp = fixtype (rp);
+ puteq(new_lp, new_rp);
+ }
+}
+
+
+
+/* Make Statement Function */
+
+long laststfcn = -1, thisstno;
+int doing_stmtfcn;
+
+ void
+#ifdef KR_headers
+mkstfunct(lp, rp)
+ struct Primblock *lp;
+ expptr rp;
+#else
+mkstfunct(struct Primblock *lp, expptr rp)
+#endif
+{
+ register struct Primblock *p;
+ register Namep np;
+ chainp args;
+
+ laststfcn = thisstno;
+ np = lp->namep;
+ if(np->vclass == CLUNKNOWN)
+ np->vclass = CLPROC;
+ else
+ {
+ dclerr("redeclaration of statement function", np);
+ return;
+ }
+ np->vprocclass = PSTFUNCT;
+ np->vstg = STGSTFUNCT;
+
+/* Set the type of the function */
+
+ impldcl(np);
+ if (np->vtype == TYCHAR && !np->vleng)
+ err("character statement function with length (*)");
+ args = (lp->argsp ? lp->argsp->listp : CHNULL);
+ np->varxptr.vstfdesc = mkchain((char *)args, (chainp)rp);
+
+ for(doing_stmtfcn = 1 ; args ; args = args->nextp)
+
+/* It is an error for the formal parameters to have arguments or
+ subscripts */
+
+ if( ((tagptr)(args->datap))->tag!=TPRIM ||
+ (p = (struct Primblock *)(args->datap) )->argsp ||
+ p->fcharp || p->lcharp ) {
+ err("non-variable argument in statement function definition");
+ args->datap = 0;
+ }
+ else
+ {
+
+/* Replace the name on the left-hand side */
+
+ args->datap = (char *)p->namep;
+ vardcl(p -> namep);
+ free((char *)p);
+ }
+ doing_stmtfcn = 0;
+}
+
+ static void
+#ifdef KR_headers
+mixed_type(np)
+ Namep np;
+#else
+mixed_type(Namep np)
+#endif
+{
+ char buf[128];
+ sprintf(buf, "%s function %.90s invoked as subroutine",
+ ftn_types[np->vtype], np->fvarname);
+ warn(buf);
+ }
+
+ void
+#ifdef KR_headers
+excall(name, args, nstars, labels)
+ Namep name;
+ struct Listblock *args;
+ int nstars;
+ struct Labelblock **labels;
+#else
+excall(Namep name, struct Listblock *args, int nstars, struct Labelblock **labels)
+#endif
+{
+ register expptr p;
+
+ if (name->vtype != TYSUBR) {
+ if (name->vinfproc && !name->vcalled) {
+ name->vtype = TYSUBR;
+ frexpr(name->vleng);
+ name->vleng = 0;
+ }
+ else if (!name->vimpltype && name->vtype != TYUNKNOWN)
+ mixed_type(name);
+ else
+ settype(name, TYSUBR, (ftnint)0);
+ }
+ p = mkfunct( mkprim(name, args, CHNULL) );
+ if (p->tag == TERROR)
+ return;
+
+/* Subroutines and their identifiers acquire the type INT */
+
+ p->exprblock.vtype = p->exprblock.leftp->headblock.vtype = TYINT;
+
+/* Handle the alternate return mechanism */
+
+ if(nstars > 0)
+ putcmgo(putx(fixtype(p)), nstars, labels);
+ else
+ putexpr(p);
+}
+
+
+ void
+#ifdef KR_headers
+exstop(stop, p)
+ int stop;
+ register expptr p;
+#else
+exstop(int stop, register expptr p)
+#endif
+{
+ char *str;
+ int n;
+
+ if(p)
+ {
+ if( ! ISCONST(p) )
+ {
+ execerr("pause/stop argument must be constant", CNULL);
+ frexpr(p);
+ p = mkstrcon(0, CNULL);
+ }
+ else if( ISINT(p->constblock.vtype) )
+ {
+ str = convic(p->constblock.Const.ci);
+ n = strlen(str);
+ if(n > 0)
+ {
+ p->constblock.Const.ccp = copyn(n, str);
+ p->constblock.Const.ccp1.blanks = 0;
+ p->constblock.vtype = TYCHAR;
+ p->constblock.vleng = (expptr) ICON(n);
+ }
+ else
+ p = (expptr) mkstrcon(0, CNULL);
+ }
+ else if(p->constblock.vtype != TYCHAR)
+ {
+ execerr("pause/stop argument must be integer or string", CNULL);
+ p = (expptr) mkstrcon(0, CNULL);
+ }
+ }
+ else p = (expptr) mkstrcon(0, CNULL);
+
+ {
+ expptr subr_call;
+
+ subr_call = call1(TYSUBR, (stop ? "s_stop" : "s_paus"), p);
+ putexpr( subr_call );
+ }
+}
+
+/* DO LOOP CODE */
+
+#define DOINIT par[0]
+#define DOLIMIT par[1]
+#define DOINCR par[2]
+
+
+/* Macros for ctlstack -> dostepsign */
+
+#define VARSTEP 0
+#define POSSTEP 1
+#define NEGSTEP 2
+
+
+/* exdo -- generate DO loop code. In the case of a variable increment,
+ positive increment tests are placed above the body, negative increment
+ tests are placed below (see enddo() ) */
+
+ void
+#ifdef KR_headers
+exdo(range, loopname, spec)
+ int range;
+ Namep loopname;
+ chainp spec;
+#else
+exdo(int range, Namep loopname, chainp spec)
+#endif
+ /* range = end label */
+ /* input spec must have at least 2 exprs */
+{
+ register expptr p;
+ register Namep np;
+ chainp cp; /* loops over the fields in spec */
+ register int i;
+ int dotype; /* type of the index variable */
+ int incsign; /* sign of the increment, if it's constant
+ */
+ Addrp dovarp; /* loop index variable */
+ expptr doinit; /* constant or register for init param */
+ expptr par[3]; /* local specification parameters */
+
+ expptr init, test, inc; /* Expressions in the resulting FOR loop */
+
+
+ test = ENULL;
+
+ pushctl(CTLDO);
+ dorange = ctlstack->dolabel = range;
+ ctlstack->loopname = loopname;
+
+/* Declare the loop index */
+
+ np = (Namep)spec->datap;
+ ctlstack->donamep = NULL;
+ if (!np) { /* do while */
+ ctlstack->dowhile = 1;
+#if 0
+ if (loopname) {
+ if (loopname->vtype == TYUNKNOWN) {
+ loopname->vdcldone = 1;
+ loopname->vclass = CLLABEL;
+ loopname->vprocclass = PLABEL;
+ loopname->vtype = TYLABEL;
+ }
+ if (loopname->vtype == TYLABEL)
+ if (loopname->vdovar)
+ dclerr("already in use as a loop name",
+ loopname);
+ else
+ loopname->vdovar = 1;
+ else
+ dclerr("already declared; cannot be a loop name",
+ loopname);
+ }
+#endif
+ putwhile((expptr)spec->nextp);
+ NOEXT("do while");
+ spec->nextp = 0;
+ frchain(&spec);
+ return;
+ }
+ if(np->vdovar)
+ {
+ errstr("nested loops with variable %s", np->fvarname);
+ ctlstack->donamep = NULL;
+ return;
+ }
+
+/* Create a memory-resident version of the index variable */
+
+ dovarp = mkplace(np);
+ if( ! ONEOF(dovarp->vtype, MSKINT|MSKREAL) )
+ {
+ err("bad type on do variable");
+ return;
+ }
+ ctlstack->donamep = np;
+
+ np->vdovar = YES;
+
+/* Now dovarp points to the index to be used within the loop, dostgp
+ points to the one which may need to be stored */
+
+ dotype = dovarp->vtype;
+
+/* Count the input specifications and type-check each one independently;
+ this just eliminates non-numeric values from the specification */
+
+ for(i=0 , cp = spec->nextp ; cp!=NULL && i<3 ; cp = cp->nextp)
+ {
+ p = par[i++] = fixtype((tagptr)cp->datap);
+ if( ! ONEOF(p->headblock.vtype, MSKINT|MSKREAL) )
+ {
+ err("bad type on DO parameter");
+ return;
+ }
+ }
+
+ frchain(&spec);
+ switch(i)
+ {
+ case 0:
+ case 1:
+ err("too few DO parameters");
+ return;
+
+ default:
+ err("too many DO parameters");
+ return;
+
+ case 2:
+ DOINCR = (expptr) ICON(1);
+
+ case 3:
+ break;
+ }
+
+
+/* Now all of the local specification fields are set, but their types are
+ not yet consistent */
+
+/* Declare the loop initialization value, casting it properly and declaring a
+ register if need be */
+
+ if (ISCONST (DOINIT) || !onetripflag)
+/* putx added 6-29-89 (mwm), not sure if fixtype is required, but I doubt it
+ since mkconv is called just before */
+ doinit = putx (mkconv (dotype, DOINIT));
+ else {
+ doinit = (expptr) mktmp(dotype, ENULL);
+ puteq (cpexpr (doinit), DOINIT);
+ } /* else */
+
+/* Declare the loop ending value, casting it to the type of the index
+ variable */
+
+ if( ISCONST(DOLIMIT) )
+ ctlstack->domax = mkconv(dotype, DOLIMIT);
+ else {
+ ctlstack->domax = (expptr) mktmp0(dotype, ENULL);
+ puteq (cpexpr (ctlstack -> domax), DOLIMIT);
+ } /* else */
+
+/* Declare the loop increment value, casting it to the type of the index
+ variable */
+
+ if( ISCONST(DOINCR) )
+ {
+ ctlstack->dostep = mkconv(dotype, DOINCR);
+ if( (incsign = conssgn(ctlstack->dostep)) == 0)
+ err("zero DO increment");
+ ctlstack->dostepsign = (incsign > 0 ? POSSTEP : NEGSTEP);
+ }
+ else
+ {
+ ctlstack->dostep = (expptr) mktmp0(dotype, ENULL);
+ ctlstack->dostepsign = VARSTEP;
+ puteq (cpexpr (ctlstack -> dostep), DOINCR);
+ }
+
+/* All data is now properly typed and in the ctlstack, except for the
+ initial value. Assignments of temps have been generated already */
+
+ switch (ctlstack -> dostepsign) {
+ case VARSTEP:
+ test = mkexpr (OPQUEST, mkexpr (OPLT,
+ cpexpr (ctlstack -> dostep), ICON(0)),
+ mkexpr (OPCOLON,
+ mkexpr (OPGE, cpexpr((expptr)dovarp),
+ cpexpr (ctlstack -> domax)),
+ mkexpr (OPLE, cpexpr((expptr)dovarp),
+ cpexpr (ctlstack -> domax))));
+ break;
+ case POSSTEP:
+ test = mkexpr (OPLE, cpexpr((expptr)dovarp),
+ cpexpr (ctlstack -> domax));
+ break;
+ case NEGSTEP:
+ test = mkexpr (OPGE, cpexpr((expptr)dovarp),
+ cpexpr (ctlstack -> domax));
+ break;
+ default:
+ erri ("exdo: bad dostepsign '%d'", ctlstack -> dostepsign);
+ break;
+ } /* switch (ctlstack -> dostepsign) */
+
+ if (onetripflag)
+ test = mkexpr (OPOR, test,
+ mkexpr (OPEQ, cpexpr((expptr)dovarp), cpexpr (doinit)));
+ init = mkexpr (OPASSIGN, cpexpr((expptr)dovarp), doinit);
+ inc = mkexpr (OPPLUSEQ, (expptr)dovarp, cpexpr (ctlstack -> dostep));
+
+ if (!onetripflag && ISCONST (ctlstack -> domax) && ISCONST (doinit)
+ && ctlstack -> dostepsign != VARSTEP) {
+ expptr tester;
+
+ tester = mkexpr (OPMINUS, cpexpr (doinit),
+ cpexpr (ctlstack -> domax));
+ if (incsign == conssgn (tester))
+ warn ("DO range never executed");
+ frexpr (tester);
+ } /* if !onetripflag && */
+
+ p1_for (init, test, inc);
+}
+
+ void
+#ifdef KR_headers
+exenddo(np)
+ Namep np;
+#else
+exenddo(Namep np)
+#endif
+{
+ Namep np1;
+ int here;
+ struct Ctlframe *cf;
+
+ if( ctlstack < ctls )
+ goto misplaced;
+ here = ctlstack->dolabel;
+ if (ctlstack->ctltype != CTLDO
+ || here >= 0 && (!thislabel || thislabel->labelno != here)) {
+ misplaced:
+ err("misplaced ENDDO");
+ return;
+ }
+ if (np != ctlstack->loopname) {
+ if (np1 = ctlstack->loopname)
+ errstr("expected \"enddo %s\"", np1->fvarname);
+ else
+ err("expected unnamed ENDDO");
+ for(cf = ctls; cf < ctlstack; cf++)
+ if (cf->ctltype == CTLDO && cf->loopname == np) {
+ here = cf->dolabel;
+ break;
+ }
+ }
+ enddo(here);
+ }
+
+ void
+#ifdef KR_headers
+enddo(here)
+ int here;
+#else
+enddo(int here)
+#endif
+{
+ register struct Ctlframe *q;
+ Namep np; /* name of the current DO index */
+ Addrp ap;
+ register int i;
+ register expptr e;
+
+/* Many DO's can end at the same statement, so keep looping over all
+ nested indicies */
+
+ while(here == dorange)
+ {
+ if(np = ctlstack->donamep)
+ {
+ p1for_end ();
+
+/* Now we're done with all of the tests, and the loop has terminated.
+ Store the index value back in long-term memory */
+
+ if(ap = memversion(np))
+ puteq((expptr)ap, (expptr)mkplace(np));
+ for(i = 0 ; i < 4 ; ++i)
+ ctlstack->ctlabels[i] = 0;
+ deregister(ctlstack->donamep);
+ ctlstack->donamep->vdovar = NO;
+ /* ctlstack->dostep and ctlstack->domax can be zero */
+ /* with sufficiently bizarre (erroneous) syntax */
+ if (e = ctlstack->dostep)
+ if (e->tag == TADDR && e->addrblock.istemp)
+ frtemp((Addrp)e);
+ else
+ frexpr(e);
+ if (e = ctlstack->domax)
+ if (e->tag == TADDR && e->addrblock.istemp)
+ frtemp((Addrp)e);
+ else
+ frexpr(e);
+ }
+ else if (ctlstack->dowhile)
+ p1for_end ();
+
+/* Set dorange to the closing label of the next most enclosing DO loop
+ */
+
+ popctl();
+ poplab();
+ dorange = 0;
+ for(q = ctlstack ; q>=ctls ; --q)
+ if(q->ctltype == CTLDO)
+ {
+ dorange = q->dolabel;
+ break;
+ }
+ }
+}
+
+ void
+#ifdef KR_headers
+exassign(vname, labelval)
+ register Namep vname;
+ struct Labelblock *labelval;
+#else
+exassign(register Namep vname, struct Labelblock *labelval)
+#endif
+{
+ Addrp p;
+ register Addrp q;
+ char *fs;
+ register chainp cp, cpprev;
+ register ftnint k, stno;
+
+ p = mkplace(vname);
+ if( ! ONEOF(p->vtype, MSKINT|MSKADDR) ) {
+ err("noninteger assign variable");
+ return;
+ }
+
+ /* If the label hasn't been defined, then we do things twice:
+ * once for an executable stmt label, once for a format
+ */
+
+ /* code for executable label... */
+
+/* Now store the assigned value in a list associated with this variable.
+ This will be used later to generate a switch() statement in the C output */
+
+ fs = labelval->fmtstring;
+ if (!labelval->labdefined || !fs) {
+
+ if (vname -> vis_assigned == 0) {
+ vname -> varxptr.assigned_values = CHNULL;
+ vname -> vis_assigned = 1;
+ }
+
+ /* don't duplicate labels... */
+
+ stno = labelval->stateno;
+ cpprev = 0;
+ for(k = 0, cp = vname->varxptr.assigned_values;
+ cp; cpprev = cp, cp = cp->nextp, k++)
+ if ((ftnint)cp->datap == stno)
+ break;
+ if (!cp) {
+ cp = mkchain((char *)stno, CHNULL);
+ if (cpprev)
+ cpprev->nextp = cp;
+ else
+ vname->varxptr.assigned_values = cp;
+ labelval->labused = 1;
+ }
+ putout(mkexpr(OPASSIGN, (expptr)p, mkintcon(k)));
+ }
+
+ /* Code for FORMAT label... */
+
+ if (!labelval->labdefined || fs) {
+
+ labelval->fmtlabused = 1;
+ p = ALLOC(Addrblock);
+ p->tag = TADDR;
+ p->vtype = TYCHAR;
+ p->vstg = STGAUTO;
+ p->memoffset = ICON(0);
+ fmtname(vname, p);
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ q->vtype = TYCHAR;
+ q->vstg = STGAUTO;
+ q->ntempelt = 1;
+ q->memoffset = ICON(0);
+ q->uname_tag = UNAM_IDENT;
+ sprintf(q->user.ident, "fmt_%ld", labelval->stateno);
+ putout(mkexpr(OPASSIGN, (expptr)p, (expptr)q));
+ }
+
+} /* exassign */
+
+
+ void
+#ifdef KR_headers
+exarif(expr, neglab, zerlab, poslab)
+ expptr expr;
+ struct Labelblock *neglab;
+ struct Labelblock *zerlab;
+ struct Labelblock *poslab;
+#else
+exarif(expptr expr, struct Labelblock *neglab, struct Labelblock *zerlab, struct Labelblock *poslab)
+#endif
+{
+ register int lm, lz, lp;
+
+ lm = neglab->stateno;
+ lz = zerlab->stateno;
+ lp = poslab->stateno;
+ expr = fixtype(expr);
+
+ if( ! ONEOF(expr->headblock.vtype, MSKINT|MSKREAL) )
+ {
+ err("invalid type of arithmetic if expression");
+ frexpr(expr);
+ }
+ else
+ {
+ if (lm == lz && lz == lp)
+ exgoto (neglab);
+ else if(lm == lz)
+ exar2(OPLE, expr, neglab, poslab);
+ else if(lm == lp)
+ exar2(OPNE, expr, neglab, zerlab);
+ else if(lz == lp)
+ exar2(OPGE, expr, zerlab, neglab);
+ else {
+ expptr t;
+
+ if (!addressable (expr)) {
+ t = (expptr) mktmp(expr -> headblock.vtype, ENULL);
+ expr = mkexpr (OPASSIGN, cpexpr (t), expr);
+ } else
+ t = (expptr) cpexpr (expr);
+
+ p1_if(putx(fixtype(mkexpr (OPLT, expr, ICON (0)))));
+ exgoto(neglab);
+ p1_elif (mkexpr (OPEQ, t, ICON (0)));
+ exgoto(zerlab);
+ p1_else ();
+ exgoto(poslab);
+ p1else_end ();
+ } /* else */
+ }
+}
+
+
+
+/* exar2 -- Do arithmetic IF for only 2 distinct labels; if !(e.op.0)
+ goto l2 else goto l1. If this seems backwards, that's because it is,
+ in order to make the 1 pass algorithm work. */
+
+ LOCAL void
+#ifdef KR_headers
+exar2(op, e, l1, l2)
+ int op;
+ expptr e;
+ struct Labelblock *l1;
+ struct Labelblock *l2;
+#else
+exar2(int op, expptr e, struct Labelblock *l1, struct Labelblock *l2)
+#endif
+{
+ expptr comp;
+
+ comp = mkexpr (op, e, ICON (0));
+ p1_if(putx(fixtype(comp)));
+ exgoto(l1);
+ p1_else ();
+ exgoto(l2);
+ p1else_end ();
+}
+
+
+/* exreturn -- return the value in p from a SUBROUTINE call -- used to
+ implement the alternate return mechanism */
+
+ void
+#ifdef KR_headers
+exreturn(p)
+ register expptr p;
+#else
+exreturn(register expptr p)
+#endif
+{
+ if(procclass != CLPROC)
+ warn("RETURN statement in main or block data");
+ if(p && (proctype!=TYSUBR || procclass!=CLPROC) )
+ {
+ err("alternate return in nonsubroutine");
+ p = 0;
+ }
+
+ if (p || proctype == TYSUBR) {
+ if (p == ENULL) p = ICON (0);
+ p = mkconv (TYLONG, fixtype (p));
+ p1_subr_ret (p);
+ } /* if p || proctype == TYSUBR */
+ else
+ p1_subr_ret((expptr)retslot);
+}
+
+
+ void
+#ifdef KR_headers
+exasgoto(labvar)
+ Namep labvar;
+#else
+exasgoto(Namep labvar)
+#endif
+{
+ register Addrp p;
+
+ p = mkplace(labvar);
+ if( ! ISINT(p->vtype) )
+ err("assigned goto variable must be integer");
+ else {
+ p1_asgoto (p);
+ } /* else */
+}
diff --git a/usr.bin/f2c/expr.c b/usr.bin/f2c/expr.c
new file mode 100644
index 0000000..258facc
--- /dev/null
+++ b/usr.bin/f2c/expr.c
@@ -0,0 +1,3366 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "output.h"
+#include "names.h"
+
+typedef struct { double dreal, dimag; } dcomplex;
+
+static void consbinop Argdcl((int, int, Constp, Constp, Constp));
+static void conspower Argdcl((Constp, Constp, long int));
+static void zdiv Argdcl((dcomplex*, dcomplex*, dcomplex*));
+static tagptr mkpower Argdcl((tagptr));
+static tagptr stfcall Argdcl((Namep, struct Listblock*));
+
+extern char dflttype[26];
+extern int htype;
+
+/* little routines to create constant blocks */
+
+ Constp
+#ifdef KR_headers
+mkconst(t)
+ register int t;
+#else
+mkconst(register int t)
+#endif
+{
+ register Constp p;
+
+ p = ALLOC(Constblock);
+ p->tag = TCONST;
+ p->vtype = t;
+ return(p);
+}
+
+
+/* mklogcon -- Make Logical Constant */
+
+ expptr
+#ifdef KR_headers
+mklogcon(l)
+ register int l;
+#else
+mklogcon(register int l)
+#endif
+{
+ register Constp p;
+
+ p = mkconst(tylog);
+ p->Const.ci = l;
+ return( (expptr) p );
+}
+
+
+
+/* mkintcon -- Make Integer Constant */
+
+ expptr
+#ifdef KR_headers
+mkintcon(l)
+ ftnint l;
+#else
+mkintcon(ftnint l)
+#endif
+{
+ register Constp p;
+
+ p = mkconst(tyint);
+ p->Const.ci = l;
+ return( (expptr) p );
+}
+
+
+
+
+/* mkaddcon -- Make Address Constant, given integer value */
+
+ expptr
+#ifdef KR_headers
+mkaddcon(l)
+ register long l;
+#else
+mkaddcon(register long l)
+#endif
+{
+ register Constp p;
+
+ p = mkconst(TYADDR);
+ p->Const.ci = l;
+ return( (expptr) p );
+}
+
+
+
+/* mkrealcon -- Make Real Constant. The type t is assumed
+ to be TYREAL or TYDREAL */
+
+ expptr
+#ifdef KR_headers
+mkrealcon(t, d)
+ register int t;
+ char *d;
+#else
+mkrealcon(register int t, char *d)
+#endif
+{
+ register Constp p;
+
+ p = mkconst(t);
+ p->Const.cds[0] = cds(d,CNULL);
+ p->vstg = 1;
+ return( (expptr) p );
+}
+
+
+/* mkbitcon -- Make bit constant. Reads the input string, which is
+ assumed to correctly specify a number in base 2^shift (where shift
+ is the input parameter). shift may not exceed 4, i.e. only binary,
+ quad, octal and hex bases may be input. Constants may not exceed 32
+ bits, or whatever the size of (struct Constblock).ci may be. */
+
+ expptr
+#ifdef KR_headers
+mkbitcon(shift, leng, s)
+ int shift;
+ int leng;
+ char *s;
+#else
+mkbitcon(int shift, int leng, char *s)
+#endif
+{
+ register Constp p;
+ register long x, y, z;
+ int len;
+ char buff[100], *fmt, *s0 = s;
+ static char *kind[3] = { "Binary", "Hex", "Octal" };
+
+ p = mkconst(TYLONG);
+ x = y = 0;
+ while(--leng >= 0)
+ if(*s != ' ') {
+ z = x;
+ x = (x << shift) | hextoi(*s++);
+ y |= (((unsigned long)x) >> shift) - z;
+ }
+ /* Don't change the type to short for short constants, as
+ * that is dangerous -- there is no syntax for long constants
+ * with small values.
+ */
+ p->Const.ci = x;
+ if (y) {
+ if (--shift == 3)
+ shift = 1;
+ if ((len = (int)leng) > 60)
+ sprintf(buff, "%s constant '%.60s' truncated.",
+ kind[shift], s0);
+ else
+ sprintf(buff, "%s constant '%.*s' truncated.",
+ kind[shift], len, s0);
+ err(buff);
+ }
+ return( (expptr) p );
+}
+
+
+
+
+
+/* mkstrcon -- Make string constant. Allocates storage and initializes
+ the memory for a copy of the input Fortran-string. */
+
+ expptr
+#ifdef KR_headers
+mkstrcon(l, v)
+ int l;
+ register char *v;
+#else
+mkstrcon(int l, register char *v)
+#endif
+{
+ register Constp p;
+ register char *s;
+
+ p = mkconst(TYCHAR);
+ p->vleng = ICON(l);
+ p->Const.ccp = s = (char *) ckalloc(l+1);
+ p->Const.ccp1.blanks = 0;
+ while(--l >= 0)
+ *s++ = *v++;
+ *s = '\0';
+ return( (expptr) p );
+}
+
+
+
+/* mkcxcon -- Make complex contsant. A complex number is a pair of
+ values, each of which may be integer, real or double. */
+
+ expptr
+#ifdef KR_headers
+mkcxcon(realp, imagp)
+ register expptr realp;
+ register expptr imagp;
+#else
+mkcxcon(register expptr realp, register expptr imagp)
+#endif
+{
+ int rtype, itype;
+ register Constp p;
+
+ rtype = realp->headblock.vtype;
+ itype = imagp->headblock.vtype;
+
+ if( ISCONST(realp) && ISNUMERIC(rtype) && ISCONST(imagp) && ISNUMERIC(itype) )
+ {
+ p = mkconst( (rtype==TYDREAL||itype==TYDREAL)
+ ? TYDCOMPLEX : tycomplex);
+ if (realp->constblock.vstg || imagp->constblock.vstg) {
+ p->vstg = 1;
+ p->Const.cds[0] = ISINT(rtype)
+ ? string_num("", realp->constblock.Const.ci)
+ : realp->constblock.vstg
+ ? realp->constblock.Const.cds[0]
+ : dtos(realp->constblock.Const.cd[0]);
+ p->Const.cds[1] = ISINT(itype)
+ ? string_num("", imagp->constblock.Const.ci)
+ : imagp->constblock.vstg
+ ? imagp->constblock.Const.cds[0]
+ : dtos(imagp->constblock.Const.cd[0]);
+ }
+ else {
+ p->Const.cd[0] = ISINT(rtype)
+ ? realp->constblock.Const.ci
+ : realp->constblock.Const.cd[0];
+ p->Const.cd[1] = ISINT(itype)
+ ? imagp->constblock.Const.ci
+ : imagp->constblock.Const.cd[0];
+ }
+ }
+ else
+ {
+ err("invalid complex constant");
+ p = (Constp)errnode();
+ }
+
+ frexpr(realp);
+ frexpr(imagp);
+ return( (expptr) p );
+}
+
+
+/* errnode -- Allocate a new error block */
+
+ expptr
+errnode(Void)
+{
+ struct Errorblock *p;
+ p = ALLOC(Errorblock);
+ p->tag = TERROR;
+ p->vtype = TYERROR;
+ return( (expptr) p );
+}
+
+
+
+
+
+/* mkconv -- Make type conversion. Cast expression p into type t.
+ Note that casting to a character copies only the first sizeof(char)
+ bytes. */
+
+ expptr
+#ifdef KR_headers
+mkconv(t, p)
+ register int t;
+ register expptr p;
+#else
+mkconv(register int t, register expptr p)
+#endif
+{
+ register expptr q;
+ register int pt, charwarn = 1;
+
+ if (t >= 100) {
+ t -= 100;
+ charwarn = 0;
+ }
+ if(t==TYUNKNOWN || t==TYERROR)
+ badtype("mkconv", t);
+ pt = p->headblock.vtype;
+
+/* Casting to the same type is a no-op */
+
+ if(t == pt)
+ return(p);
+
+/* If we're casting a constant which is not in the literal table ... */
+
+ else if( ISCONST(p) && pt!=TYADDR && pt != TYCHAR)
+ {
+ if (ISINT(t) && ISINT(pt) || ISREAL(t) && ISREAL(pt)) {
+ /* avoid trouble with -i2 */
+ p->headblock.vtype = t;
+ return p;
+ }
+ q = (expptr) mkconst(t);
+ consconv(t, &q->constblock, &p->constblock );
+ frexpr(p);
+ }
+ else {
+ if (pt == TYCHAR && t != TYADDR && charwarn
+ && (!halign || p->tag != TADDR
+ || p->addrblock.uname_tag != UNAM_CONST))
+ warn(
+ "ichar([first char. of] char. string) assumed for conversion to numeric");
+ q = opconv(p, t);
+ }
+
+ if(t == TYCHAR)
+ q->constblock.vleng = ICON(1);
+ return(q);
+}
+
+
+
+/* opconv -- Convert expression p to type t using the main
+ expression evaluator; returns an OPCONV expression, I think 14-jun-88 mwm */
+
+ expptr
+#ifdef KR_headers
+opconv(p, t)
+ expptr p;
+ int t;
+#else
+opconv(expptr p, int t)
+#endif
+{
+ register expptr q;
+
+ if (t == TYSUBR)
+ err("illegal use of subroutine name");
+ q = mkexpr(OPCONV, p, ENULL);
+ q->headblock.vtype = t;
+ return(q);
+}
+
+
+
+/* addrof -- Create an ADDR expression operation */
+
+ expptr
+#ifdef KR_headers
+addrof(p)
+ expptr p;
+#else
+addrof(expptr p)
+#endif
+{
+ return( mkexpr(OPADDR, p, ENULL) );
+}
+
+
+
+/* cpexpr - Returns a new copy of input expression p */
+
+ tagptr
+#ifdef KR_headers
+cpexpr(p)
+ register tagptr p;
+#else
+cpexpr(register tagptr p)
+#endif
+{
+ register tagptr e;
+ int tag;
+ register chainp ep, pp;
+
+/* This table depends on the ordering of the T macros, e.g. TNAME */
+
+ static int blksize[ ] =
+ {
+ 0,
+ sizeof(struct Nameblock),
+ sizeof(struct Constblock),
+ sizeof(struct Exprblock),
+ sizeof(struct Addrblock),
+ sizeof(struct Primblock),
+ sizeof(struct Listblock),
+ sizeof(struct Impldoblock),
+ sizeof(struct Errorblock)
+ };
+
+ if(p == NULL)
+ return(NULL);
+
+/* TNAMEs are special, and don't get copied. Each name in the current
+ symbol table has a unique TNAME structure. */
+
+ if( (tag = p->tag) == TNAME)
+ return(p);
+
+ e = cpblock(blksize[p->tag], (char *)p);
+
+ switch(tag)
+ {
+ case TCONST:
+ if(e->constblock.vtype == TYCHAR)
+ {
+ e->constblock.Const.ccp =
+ copyn((int)e->constblock.vleng->constblock.Const.ci+1,
+ e->constblock.Const.ccp);
+ e->constblock.vleng =
+ (expptr) cpexpr(e->constblock.vleng);
+ }
+ case TERROR:
+ break;
+
+ case TEXPR:
+ e->exprblock.leftp = (expptr) cpexpr(p->exprblock.leftp);
+ e->exprblock.rightp = (expptr) cpexpr(p->exprblock.rightp);
+ break;
+
+ case TLIST:
+ if(pp = p->listblock.listp)
+ {
+ ep = e->listblock.listp =
+ mkchain((char *)cpexpr((tagptr)pp->datap), CHNULL);
+ for(pp = pp->nextp ; pp ; pp = pp->nextp)
+ ep = ep->nextp =
+ mkchain((char *)cpexpr((tagptr)pp->datap),
+ CHNULL);
+ }
+ break;
+
+ case TADDR:
+ e->addrblock.vleng = (expptr) cpexpr(e->addrblock.vleng);
+ e->addrblock.memoffset = (expptr)cpexpr(e->addrblock.memoffset);
+ e->addrblock.istemp = NO;
+ break;
+
+ case TPRIM:
+ e->primblock.argsp = (struct Listblock *)
+ cpexpr((expptr)e->primblock.argsp);
+ e->primblock.fcharp = (expptr) cpexpr(e->primblock.fcharp);
+ e->primblock.lcharp = (expptr) cpexpr(e->primblock.lcharp);
+ break;
+
+ default:
+ badtag("cpexpr", tag);
+ }
+
+ return(e);
+}
+
+/* frexpr -- Free expression -- frees up memory used by expression p */
+
+ void
+#ifdef KR_headers
+frexpr(p)
+ register tagptr p;
+#else
+frexpr(register tagptr p)
+#endif
+{
+ register chainp q;
+
+ if(p == NULL)
+ return;
+
+ switch(p->tag)
+ {
+ case TCONST:
+ if( ISCHAR(p) )
+ {
+ free( (charptr) (p->constblock.Const.ccp) );
+ frexpr(p->constblock.vleng);
+ }
+ break;
+
+ case TADDR:
+ if (p->addrblock.vtype > TYERROR) /* i/o block */
+ break;
+ frexpr(p->addrblock.vleng);
+ frexpr(p->addrblock.memoffset);
+ break;
+
+ case TERROR:
+ break;
+
+/* TNAME blocks don't get free'd - probably because they're pointed to in
+ the hash table. 14-Jun-88 -- mwm */
+
+ case TNAME:
+ return;
+
+ case TPRIM:
+ frexpr((expptr)p->primblock.argsp);
+ frexpr(p->primblock.fcharp);
+ frexpr(p->primblock.lcharp);
+ break;
+
+ case TEXPR:
+ frexpr(p->exprblock.leftp);
+ if(p->exprblock.rightp)
+ frexpr(p->exprblock.rightp);
+ break;
+
+ case TLIST:
+ for(q = p->listblock.listp ; q ; q = q->nextp)
+ frexpr((tagptr)q->datap);
+ frchain( &(p->listblock.listp) );
+ break;
+
+ default:
+ badtag("frexpr", p->tag);
+ }
+
+ free( (charptr) p );
+}
+
+ void
+#ifdef KR_headers
+wronginf(np)
+ Namep np;
+#else
+wronginf(Namep np)
+#endif
+{
+ int c, k;
+ warn1("fixing wrong type inferred for %.65s", np->fvarname);
+ np->vinftype = 0;
+ c = letter(np->fvarname[0]);
+ if ((np->vtype = impltype[c]) == TYCHAR
+ && (k = implleng[c]))
+ np->vleng = ICON(k);
+ }
+
+/* fix up types in expression; replace subtrees and convert
+ names to address blocks */
+
+ expptr
+#ifdef KR_headers
+fixtype(p)
+ register tagptr p;
+#else
+fixtype(register tagptr p)
+#endif
+{
+
+ if(p == 0)
+ return(0);
+
+ switch(p->tag)
+ {
+ case TCONST:
+ if(ONEOF(p->constblock.vtype,MSKINT|MSKLOGICAL|MSKADDR|
+ MSKREAL) )
+ return( (expptr) p);
+
+ return( (expptr) putconst((Constp)p) );
+
+ case TADDR:
+ p->addrblock.memoffset = fixtype(p->addrblock.memoffset);
+ return( (expptr) p);
+
+ case TERROR:
+ return( (expptr) p);
+
+ default:
+ badtag("fixtype", p->tag);
+
+/* This case means that fixexpr can't call fixtype with any expr,
+ only a subexpr of its parameter. */
+
+ case TEXPR:
+ if (((Exprp)p)->typefixed)
+ return (expptr)p;
+ return( fixexpr((Exprp)p) );
+
+ case TLIST:
+ return( (expptr) p );
+
+ case TPRIM:
+ if(p->primblock.argsp && p->primblock.namep->vclass!=CLVAR)
+ {
+ if(p->primblock.namep->vtype == TYSUBR)
+ {
+ err("function invocation of subroutine");
+ return( errnode() );
+ }
+ else {
+ if (p->primblock.namep->vinftype)
+ wronginf(p->primblock.namep);
+ return( mkfunct(p) );
+ }
+ }
+
+/* The lack of args makes p a function name, substring reference
+ or variable name. */
+
+ else return mklhs((struct Primblock *) p, keepsubs);
+ }
+}
+
+
+ int
+#ifdef KR_headers
+badchleng(p)
+ register expptr p;
+#else
+badchleng(register expptr p)
+#endif
+{
+ if (!p->headblock.vleng) {
+ if (p->headblock.tag == TADDR
+ && p->addrblock.uname_tag == UNAM_NAME)
+ errstr("bad use of character*(*) variable %.60s",
+ p->addrblock.user.name->fvarname);
+ else
+ err("Bad use of character*(*)");
+ return 1;
+ }
+ return 0;
+ }
+
+
+ static expptr
+#ifdef KR_headers
+cplenexpr(p)
+ expptr p;
+#else
+cplenexpr(expptr p)
+#endif
+{
+ expptr rv;
+
+ if (badchleng(p))
+ return ICON(1);
+ rv = cpexpr(p->headblock.vleng);
+ if (ISCONST(p) && p->constblock.vtype == TYCHAR)
+ rv->constblock.Const.ci += p->constblock.Const.ccp1.blanks;
+ return rv;
+ }
+
+
+/* special case tree transformations and cleanups of expression trees.
+ Parameter p should have a TEXPR tag at its root, else an error is
+ returned */
+
+ expptr
+#ifdef KR_headers
+fixexpr(p)
+ register Exprp p;
+#else
+fixexpr(register Exprp p)
+#endif
+{
+ expptr lp;
+ register expptr rp;
+ register expptr q;
+ char *hsave;
+ int opcode, ltype, rtype, ptype, mtype;
+
+ if( ISERROR(p) || p->typefixed )
+ return( (expptr) p );
+ else if(p->tag != TEXPR)
+ badtag("fixexpr", p->tag);
+ opcode = p->opcode;
+
+/* First set the types of the left and right subexpressions */
+
+ lp = p->leftp;
+ if (!ISCONST(lp) || lp->constblock.vtype != TYCHAR)
+ lp = p->leftp = fixtype(lp);
+ ltype = lp->headblock.vtype;
+
+ if(opcode==OPASSIGN && lp->tag!=TADDR)
+ {
+ err("left side of assignment must be variable");
+ eret:
+ frexpr((expptr)p);
+ return( errnode() );
+ }
+
+ if(rp = p->rightp)
+ {
+ if (!ISCONST(rp) || rp->constblock.vtype != TYCHAR)
+ rp = p->rightp = fixtype(rp);
+ rtype = rp->headblock.vtype;
+ }
+ else
+ rtype = 0;
+
+ if(ltype==TYERROR || rtype==TYERROR)
+ goto eret;
+
+/* Now work on the whole expression */
+
+ /* force folding if possible */
+
+ if( ISCONST(lp) && (rp==NULL || ISCONST(rp)) )
+ {
+ q = opcode == OPCONV && lp->constblock.vtype == p->vtype
+ ? lp : mkexpr(opcode, lp, rp);
+
+/* mkexpr is expected to reduce constant expressions */
+
+ if( ISCONST(q) ) {
+ p->leftp = p->rightp = 0;
+ frexpr((expptr)p);
+ return(q);
+ }
+ free( (charptr) q ); /* constants did not fold */
+ }
+
+ if( (ptype = cktype(opcode, ltype, rtype)) == TYERROR)
+ goto eret;
+
+ if (ltype == TYCHAR && ISCONST(lp)) {
+ if (opcode == OPCONV) {
+ hsave = halign;
+ halign = 0;
+ lp = (expptr)putconst((Constp)lp);
+ halign = hsave;
+ }
+ else
+ lp = (expptr)putconst((Constp)lp);
+ p->leftp = lp;
+ }
+ if (rtype == TYCHAR && ISCONST(rp))
+ p->rightp = rp = (expptr)putconst((Constp)rp);
+
+ switch(opcode)
+ {
+ case OPCONCAT:
+ if(p->vleng == NULL)
+ p->vleng = mkexpr(OPPLUS, cplenexpr(lp),
+ cplenexpr(rp) );
+ break;
+
+ case OPASSIGN:
+ if (rtype == TYREAL || ISLOGICAL(ptype)
+ || rtype == TYDREAL && ltype == TYREAL && !ISCONST(rp))
+ break;
+ case OPPLUSEQ:
+ case OPSTAREQ:
+ if(ltype == rtype)
+ break;
+ if( ! ISCONST(rp) && ISREAL(ltype) && ISREAL(rtype) )
+ break;
+ if( ISCOMPLEX(ltype) || ISCOMPLEX(rtype) )
+ break;
+ if( ONEOF(ltype, MSKADDR|MSKINT) && ONEOF(rtype, MSKADDR|MSKINT)
+ && typesize[ltype]>=typesize[rtype] )
+ break;
+
+/* Cast the right hand side to match the type of the expression */
+
+ p->rightp = fixtype( mkconv(ptype, rp) );
+ break;
+
+ case OPSLASH:
+ if( ISCOMPLEX(rtype) )
+ {
+ p = (Exprp) call2(ptype,
+
+/* Handle double precision complex variables */
+
+ ptype == TYCOMPLEX ? "c_div" : "z_div",
+ mkconv(ptype, lp), mkconv(ptype, rp) );
+ break;
+ }
+ case OPPLUS:
+ case OPMINUS:
+ case OPSTAR:
+ case OPMOD:
+ if(ptype==TYDREAL && ( (ltype==TYREAL && ! ISCONST(lp) ) ||
+ (rtype==TYREAL && ! ISCONST(rp) ) ))
+ break;
+ if( ISCOMPLEX(ptype) )
+ break;
+
+/* Cast both sides of the expression to match the type of the whole
+ expression. */
+
+ if(ltype != ptype && (ltype < TYINT1 || ptype > TYDREAL))
+ p->leftp = fixtype(mkconv(ptype,lp));
+ if(rtype != ptype && (rtype < TYINT1 || ptype > TYDREAL))
+ p->rightp = fixtype(mkconv(ptype,rp));
+ break;
+
+ case OPPOWER:
+ rp = mkpower((expptr)p);
+ if (rp->tag == TEXPR)
+ rp->exprblock.typefixed = 1;
+ return rp;
+
+ case OPLT:
+ case OPLE:
+ case OPGT:
+ case OPGE:
+ case OPEQ:
+ case OPNE:
+ if(ltype == rtype)
+ break;
+ if (htype) {
+ if (ltype == TYCHAR) {
+ p->leftp = fixtype(mkconv(rtype,lp));
+ break;
+ }
+ if (rtype == TYCHAR) {
+ p->rightp = fixtype(mkconv(ltype,rp));
+ break;
+ }
+ }
+ mtype = cktype(OPMINUS, ltype, rtype);
+ if(mtype==TYDREAL && (ltype==TYREAL || rtype==TYREAL))
+ break;
+ if( ISCOMPLEX(mtype) )
+ break;
+ if(ltype != mtype)
+ p->leftp = fixtype(mkconv(mtype,lp));
+ if(rtype != mtype)
+ p->rightp = fixtype(mkconv(mtype,rp));
+ break;
+
+ case OPCONV:
+ ptype = cktype(OPCONV, p->vtype, ltype);
+ if(lp->tag==TEXPR && lp->exprblock.opcode==OPCOMMA
+ && !ISCOMPLEX(ptype))
+ {
+ lp->exprblock.rightp =
+ fixtype( mkconv(ptype, lp->exprblock.rightp) );
+ free( (charptr) p );
+ p = (Exprp) lp;
+ }
+ break;
+
+ case OPADDR:
+ if(lp->tag==TEXPR && lp->exprblock.opcode==OPADDR)
+ Fatal("addr of addr");
+ break;
+
+ case OPCOMMA:
+ case OPQUEST:
+ case OPCOLON:
+ break;
+
+ case OPMIN:
+ case OPMAX:
+ case OPMIN2:
+ case OPMAX2:
+ case OPDMIN:
+ case OPDMAX:
+ case OPABS:
+ case OPDABS:
+ ptype = p->vtype;
+ break;
+
+ default:
+ break;
+ }
+
+ p->vtype = ptype;
+ p->typefixed = 1;
+ return((expptr) p);
+}
+
+
+/* fix an argument list, taking due care for special first level cases */
+
+ int
+#ifdef KR_headers
+fixargs(doput, p0)
+ int doput;
+ struct Listblock *p0;
+#else
+fixargs(int doput, struct Listblock *p0)
+#endif
+ /* doput is true if constants need to be passed by reference */
+{
+ register chainp p;
+ register tagptr q, t;
+ register int qtag;
+ int nargs;
+
+ nargs = 0;
+ if(p0)
+ for(p = p0->listp ; p ; p = p->nextp)
+ {
+ ++nargs;
+ q = (tagptr)p->datap;
+ qtag = q->tag;
+ if(qtag == TCONST)
+ {
+
+/* Call putconst() to store values in a constant table. Since even
+ constants must be passed by reference, this can optimize on the storage
+ required */
+
+ p->datap = doput ? (char *)putconst((Constp)q)
+ : (char *)q;
+ }
+
+/* Take a function name and turn it into an Addr. This only happens when
+ nothing else has figured out the function beforehand */
+
+ else if(qtag==TPRIM && q->primblock.argsp==0 &&
+ q->primblock.namep->vclass==CLPROC &&
+ q->primblock.namep->vprocclass != PTHISPROC)
+ p->datap = (char *)mkaddr(q->primblock.namep);
+
+ else if(qtag==TPRIM && q->primblock.argsp==0 &&
+ q->primblock.namep->vdim!=NULL)
+ p->datap = (char *)mkscalar(q->primblock.namep);
+
+ else if(qtag==TPRIM && q->primblock.argsp==0 &&
+ q->primblock.namep->vdovar &&
+ (t = (tagptr) memversion(q->primblock.namep)) )
+ p->datap = (char *)fixtype(t);
+ else
+ p->datap = (char *)fixtype(q);
+ }
+ return(nargs);
+}
+
+
+
+/* mkscalar -- only called by fixargs above, and by some routines in
+ io.c */
+
+ Addrp
+#ifdef KR_headers
+mkscalar(np)
+ register Namep np;
+#else
+mkscalar(register Namep np)
+#endif
+{
+ register Addrp ap;
+
+ vardcl(np);
+ ap = mkaddr(np);
+
+ /* The prolog causes array arguments to point to the
+ * (0,...,0) element, unless subscript checking is on.
+ */
+ if( !checksubs && np->vstg==STGARG)
+ {
+ register struct Dimblock *dp;
+ dp = np->vdim;
+ frexpr(ap->memoffset);
+ ap->memoffset = mkexpr(OPSTAR,
+ (np->vtype==TYCHAR ?
+ cpexpr(np->vleng) :
+ (tagptr)ICON(typesize[np->vtype]) ),
+ cpexpr(dp->baseoffset) );
+ }
+ return(ap);
+}
+
+
+ static void
+#ifdef KR_headers
+adjust_arginfo(np)
+ register Namep np;
+#else
+adjust_arginfo(register Namep np)
+#endif
+ /* adjust arginfo to omit the length arg for the
+ arg that we now know to be a character-valued
+ function */
+{
+ struct Entrypoint *ep;
+ register chainp args;
+ Argtypes *at;
+
+ for(ep = entries; ep; ep = ep->entnextp)
+ for(args = ep->arglist; args; args = args->nextp)
+ if (np == (Namep)args->datap
+ && (at = ep->entryname->arginfo))
+ --at->nargs;
+ }
+
+
+
+ expptr
+#ifdef KR_headers
+mkfunct(p0)
+ expptr p0;
+#else
+mkfunct(expptr p0)
+#endif
+{
+ register struct Primblock *p = (struct Primblock *)p0;
+ struct Entrypoint *ep;
+ Addrp ap;
+ Extsym *extp;
+ register Namep np;
+ register expptr q;
+ extern chainp new_procs;
+ int k, nargs;
+ int class;
+
+ if(p->tag != TPRIM)
+ return( errnode() );
+
+ np = p->namep;
+ class = np->vclass;
+
+
+ if(class == CLUNKNOWN)
+ {
+ np->vclass = class = CLPROC;
+ if(np->vstg == STGUNKNOWN)
+ {
+ if(np->vtype!=TYSUBR && (k = intrfunct(np->fvarname))
+ && (zflag || !(*(struct Intrpacked *)&k).f4
+ || dcomplex_seen))
+ {
+ np->vstg = STGINTR;
+ np->vardesc.varno = k;
+ np->vprocclass = PINTRINSIC;
+ }
+ else
+ {
+ extp = mkext(np->fvarname,
+ addunder(np->cvarname));
+ extp->extstg = STGEXT;
+ np->vstg = STGEXT;
+ np->vardesc.varno = extp - extsymtab;
+ np->vprocclass = PEXTERNAL;
+ }
+ }
+ else if(np->vstg==STGARG)
+ {
+ if(np->vtype == TYCHAR) {
+ adjust_arginfo(np);
+ if (np->vpassed) {
+ char wbuf[160], *who;
+ who = np->fvarname;
+ sprintf(wbuf, "%s%s%s\n\t%s%s%s",
+ "Character-valued dummy procedure ",
+ who, " not declared EXTERNAL.",
+ "Code may be wrong for previous function calls having ",
+ who, " as a parameter.");
+ warn(wbuf);
+ }
+ }
+ np->vprocclass = PEXTERNAL;
+ }
+ }
+
+ if(class != CLPROC) {
+ if (np->vstg == STGCOMMON)
+ fatalstr(
+ "Cannot invoke common variable %.50s as a function.",
+ np->fvarname);
+ errstr("%.80s cannot be called.", np->fvarname);
+ goto error;
+ }
+
+/* F77 doesn't allow subscripting of function calls */
+
+ if(p->fcharp || p->lcharp)
+ {
+ err("no substring of function call");
+ goto error;
+ }
+ impldcl(np);
+ np->vimpltype = 0; /* invoking as function ==> inferred type */
+ np->vcalled = 1;
+ nargs = fixargs( np->vprocclass!=PINTRINSIC, p->argsp);
+
+ switch(np->vprocclass)
+ {
+ case PEXTERNAL:
+ if(np->vtype == TYUNKNOWN)
+ {
+ dclerr("attempt to use untyped function", np);
+ np->vtype = dflttype[letter(np->fvarname[0])];
+ }
+ ap = mkaddr(np);
+ if (!extsymtab[np->vardesc.varno].extseen) {
+ new_procs = mkchain((char *)np, new_procs);
+ extsymtab[np->vardesc.varno].extseen = 1;
+ }
+call:
+ q = mkexpr(OPCALL, (expptr)ap, (expptr)p->argsp);
+ q->exprblock.vtype = np->vtype;
+ if(np->vleng)
+ q->exprblock.vleng = (expptr) cpexpr(np->vleng);
+ break;
+
+ case PINTRINSIC:
+ q = intrcall(np, p->argsp, nargs);
+ break;
+
+ case PSTFUNCT:
+ q = stfcall(np, p->argsp);
+ break;
+
+ case PTHISPROC:
+ warn("recursive call");
+
+/* entries is the list of multiple entry points */
+
+ for(ep = entries ; ep ; ep = ep->entnextp)
+ if(ep->enamep == np)
+ break;
+ if(ep == NULL)
+ Fatal("mkfunct: impossible recursion");
+
+ ap = builtin(np->vtype, ep->entryname->cextname, -2);
+ /* the negative last arg prevents adding */
+ /* this name to the list of used builtins */
+ goto call;
+
+ default:
+ fatali("mkfunct: impossible vprocclass %d",
+ (int) (np->vprocclass) );
+ }
+ free( (charptr) p );
+ return(q);
+
+error:
+ frexpr((expptr)p);
+ return( errnode() );
+}
+
+
+
+ static expptr
+#ifdef KR_headers
+stfcall(np, actlist)
+ Namep np;
+ struct Listblock *actlist;
+#else
+stfcall(Namep np, struct Listblock *actlist)
+#endif
+{
+ register chainp actuals;
+ int nargs;
+ chainp oactp, formals;
+ int type;
+ expptr Ln, Lq, q, q1, rhs, ap;
+ Namep tnp;
+ register struct Rplblock *rp;
+ struct Rplblock *tlist;
+
+ if (np->arginfo) {
+ errstr("statement function %.66s calls itself.",
+ np->fvarname);
+ return ICON(0);
+ }
+ np->arginfo = (Argtypes *)np; /* arbitrary nonzero value */
+ if(actlist)
+ {
+ actuals = actlist->listp;
+ free( (charptr) actlist);
+ }
+ else
+ actuals = NULL;
+ oactp = actuals;
+
+ nargs = 0;
+ tlist = NULL;
+ if( (type = np->vtype) == TYUNKNOWN)
+ {
+ dclerr("attempt to use untyped statement function", np);
+ type = np->vtype = dflttype[letter(np->fvarname[0])];
+ }
+ formals = (chainp) np->varxptr.vstfdesc->datap;
+ rhs = (expptr) (np->varxptr.vstfdesc->nextp);
+
+ /* copy actual arguments into temporaries */
+ while(actuals!=NULL && formals!=NULL)
+ {
+ if (!(tnp = (Namep) formals->datap)) {
+ /* buggy statement function declaration */
+ q = ICON(1);
+ goto done;
+ }
+ rp = ALLOC(Rplblock);
+ rp->rplnp = tnp;
+ ap = fixtype((tagptr)actuals->datap);
+ if(tnp->vtype==ap->headblock.vtype && tnp->vtype!=TYCHAR
+ && (ap->tag==TCONST || ap->tag==TADDR) )
+ {
+
+/* If actuals are constants or variable names, no temporaries are required */
+ rp->rplvp = (expptr) ap;
+ rp->rplxp = NULL;
+ rp->rpltag = ap->tag;
+ }
+ else {
+ rp->rplvp = (expptr) mktmp(tnp->vtype, tnp->vleng);
+ rp -> rplxp = NULL;
+ putexpr ( mkexpr(OPASSIGN, cpexpr(rp->rplvp), ap));
+ if((rp->rpltag = rp->rplvp->tag) == TERROR)
+ err("disagreement of argument types in statement function call");
+ }
+ rp->rplnextp = tlist;
+ tlist = rp;
+ actuals = actuals->nextp;
+ formals = formals->nextp;
+ ++nargs;
+ }
+
+ if(actuals!=NULL || formals!=NULL)
+ err("statement function definition and argument list differ");
+
+ /*
+ now push down names involved in formal argument list, then
+ evaluate rhs of statement function definition in this environment
+*/
+
+ if(tlist) /* put tlist in front of the rpllist */
+ {
+ for(rp = tlist; rp->rplnextp; rp = rp->rplnextp)
+ ;
+ rp->rplnextp = rpllist;
+ rpllist = tlist;
+ }
+
+/* So when the expression finally gets evaled, that evaluator must read
+ from the globl rpllist 14-jun-88 mwm */
+
+ q = (expptr) mkconv(type, fixtype(cpexpr(rhs)) );
+
+ /* get length right of character-valued statement functions... */
+ if (type == TYCHAR
+ && (Ln = np->vleng)
+ && q->tag != TERROR
+ && (Lq = q->exprblock.vleng)
+ && (Lq->tag != TCONST
+ || Ln->constblock.Const.ci != Lq->constblock.Const.ci)) {
+ q1 = (expptr) mktmp(type, Ln);
+ putexpr ( mkexpr(OPASSIGN, cpexpr(q1), q));
+ q = q1;
+ }
+
+ /* now generate the tree ( t1=a1, (t2=a2,... , f))))) */
+ while(--nargs >= 0)
+ {
+ if(rpllist->rplxp)
+ q = mkexpr(OPCOMMA, rpllist->rplxp, q);
+ rp = rpllist->rplnextp;
+ frexpr(rpllist->rplvp);
+ free((char *)rpllist);
+ rpllist = rp;
+ }
+ done:
+ frchain( &oactp );
+ np->arginfo = 0;
+ return(q);
+}
+
+
+static int replaced;
+
+/* mkplace -- Figure out the proper storage class for the input name and
+ return an addrp with the appropriate stuff */
+
+ Addrp
+#ifdef KR_headers
+mkplace(np)
+ register Namep np;
+#else
+mkplace(register Namep np)
+#endif
+{
+ register Addrp s;
+ register struct Rplblock *rp;
+ int regn;
+
+ /* is name on the replace list? */
+
+ for(rp = rpllist ; rp ; rp = rp->rplnextp)
+ {
+ if(np == rp->rplnp)
+ {
+ replaced = 1;
+ if(rp->rpltag == TNAME)
+ {
+ np = (Namep) (rp->rplvp);
+ break;
+ }
+ else return( (Addrp) cpexpr(rp->rplvp) );
+ }
+ }
+
+ /* is variable a DO index in a register ? */
+
+ if(np->vdovar && ( (regn = inregister(np)) >= 0) )
+ if(np->vtype == TYERROR)
+ return((Addrp) errnode() );
+ else
+ {
+ s = ALLOC(Addrblock);
+ s->tag = TADDR;
+ s->vstg = STGREG;
+ s->vtype = TYIREG;
+ s->memno = regn;
+ s->memoffset = ICON(0);
+ s -> uname_tag = UNAM_NAME;
+ s -> user.name = np;
+ return(s);
+ }
+
+ if (np->vclass == CLPROC && np->vprocclass != PTHISPROC)
+ errstr("external %.60s used as a variable", np->fvarname);
+ vardcl(np);
+ return(mkaddr(np));
+}
+
+ static expptr
+#ifdef KR_headers
+subskept(p, a)
+ struct Primblock *p;
+ Addrp a;
+#else
+subskept(struct Primblock *p, Addrp a)
+#endif
+{
+ expptr ep;
+ struct Listblock *Lb;
+ chainp cp;
+
+ if (a->uname_tag != UNAM_NAME)
+ erri("subskept: uname_tag %d", a->uname_tag);
+ a->user.name->vrefused = 1;
+ a->user.name->visused = 1;
+ a->uname_tag = UNAM_REF;
+ Lb = (struct Listblock *)cpexpr((tagptr)p->argsp);
+ for(cp = Lb->listp; cp; cp = cp->nextp)
+ cp->datap = (char *)putx(fixtype((tagptr)cp->datap));
+ if (a->vtype == TYCHAR) {
+ ep = p->fcharp ? mkexpr(OPMINUS, cpexpr(p->fcharp), ICON(1))
+ : ICON(0);
+ Lb->listp = mkchain((char *)ep, Lb->listp);
+ }
+ return (expptr)Lb;
+ }
+
+ static int doing_vleng;
+
+/* mklhs -- Compute the actual address of the given expression; account
+ for array subscripts, stack offset, and substring offsets. The f -> C
+ translator will need this only to worry about the subscript stuff */
+
+ expptr
+#ifdef KR_headers
+mklhs(p, subkeep)
+ register struct Primblock *p;
+ int subkeep;
+#else
+mklhs(register struct Primblock *p, int subkeep)
+#endif
+{
+ register Addrp s;
+ Namep np;
+
+ if(p->tag != TPRIM)
+ return( (expptr) p );
+ np = p->namep;
+
+ replaced = 0;
+ s = mkplace(np);
+ if(s->tag!=TADDR || s->vstg==STGREG)
+ {
+ free( (charptr) p );
+ return( (expptr) s );
+ }
+ s->parenused = p->parenused;
+
+ /* compute the address modified by subscripts */
+
+ if (!replaced)
+ s->memoffset = (subkeep && np->vdim
+ && (np->vdim->ndim > 1 || np->vtype == TYCHAR
+ && (!ISCONST(np->vleng)
+ || np->vleng->constblock.Const.ci != 1)))
+ ? subskept(p,s)
+ : mkexpr(OPPLUS, s->memoffset, suboffset(p) );
+ frexpr((expptr)p->argsp);
+ p->argsp = NULL;
+
+ /* now do substring part */
+
+ if(p->fcharp || p->lcharp)
+ {
+ if(np->vtype != TYCHAR)
+ errstr("substring of noncharacter %s", np->fvarname);
+ else {
+ if(p->lcharp == NULL)
+ p->lcharp = (expptr) cpexpr(s->vleng);
+ if(p->fcharp) {
+ doing_vleng = 1;
+ s->vleng = fixtype(mkexpr(OPMINUS,
+ p->lcharp,
+ mkexpr(OPMINUS, p->fcharp, ICON(1) )));
+ doing_vleng = 0;
+ }
+ else {
+ frexpr(s->vleng);
+ s->vleng = p->lcharp;
+ }
+ }
+ }
+
+ s->vleng = fixtype( s->vleng );
+ s->memoffset = fixtype( s->memoffset );
+ free( (charptr) p );
+ return( (expptr) s );
+}
+
+
+
+
+
+/* deregister -- remove a register allocation from the list; assumes that
+ names are deregistered in stack order (LIFO order - Last In First Out) */
+
+ void
+#ifdef KR_headers
+deregister(np)
+ Namep np;
+#else
+deregister(Namep np)
+#endif
+{
+ if(nregvar>0 && regnamep[nregvar-1]==np)
+ {
+ --nregvar;
+ }
+}
+
+
+
+
+/* memversion -- moves a DO index REGISTER into a memory location; other
+ objects are passed through untouched */
+
+ Addrp
+#ifdef KR_headers
+memversion(np)
+ register Namep np;
+#else
+memversion(register Namep np)
+#endif
+{
+ register Addrp s;
+
+ if(np->vdovar==NO || (inregister(np)<0) )
+ return(NULL);
+ np->vdovar = NO;
+ s = mkplace(np);
+ np->vdovar = YES;
+ return(s);
+}
+
+
+
+/* inregister -- looks for the input name in the global list regnamep */
+
+ int
+#ifdef KR_headers
+inregister(np)
+ register Namep np;
+#else
+inregister(register Namep np)
+#endif
+{
+ register int i;
+
+ for(i = 0 ; i < nregvar ; ++i)
+ if(regnamep[i] == np)
+ return( regnum[i] );
+ return(-1);
+}
+
+
+
+/* suboffset -- Compute the offset from the start of the array, given the
+ subscripts as arguments */
+
+ expptr
+#ifdef KR_headers
+suboffset(p)
+ register struct Primblock *p;
+#else
+suboffset(register struct Primblock *p)
+#endif
+{
+ int n;
+ expptr si, size;
+ chainp cp;
+ expptr e, e1, offp, prod;
+ struct Dimblock *dimp;
+ expptr sub[MAXDIM+1];
+ register Namep np;
+
+ np = p->namep;
+ offp = ICON(0);
+ n = 0;
+ if(p->argsp)
+ for(cp = p->argsp->listp ; cp ; cp = cp->nextp)
+ {
+ si = fixtype(cpexpr((tagptr)cp->datap));
+ if (!ISINT(si->headblock.vtype)) {
+ NOEXT("non-integer subscript");
+ si = mkconv(TYLONG, si);
+ }
+ sub[n++] = si;
+ if(n > maxdim)
+ {
+ erri("more than %d subscripts", maxdim);
+ break;
+ }
+ }
+
+ dimp = np->vdim;
+ if(n>0 && dimp==NULL)
+ errstr("subscripts on scalar variable %.68s", np->fvarname);
+ else if(dimp && dimp->ndim!=n)
+ errstr("wrong number of subscripts on %.68s", np->fvarname);
+ else if(n > 0)
+ {
+ prod = sub[--n];
+ while( --n >= 0)
+ prod = mkexpr(OPPLUS, sub[n],
+ mkexpr(OPSTAR, prod, cpexpr(dimp->dims[n].dimsize)) );
+ if(checksubs || np->vstg!=STGARG)
+ prod = mkexpr(OPMINUS, prod, cpexpr(dimp->baseoffset));
+
+/* Add in the run-time bounds check */
+
+ if(checksubs)
+ prod = subcheck(np, prod);
+ size = np->vtype == TYCHAR ?
+ (expptr) cpexpr(np->vleng) : ICON(typesize[np->vtype]);
+ prod = mkexpr(OPSTAR, prod, size);
+ offp = mkexpr(OPPLUS, offp, prod);
+ }
+
+/* Check for substring indicator */
+
+ if(p->fcharp && np->vtype==TYCHAR) {
+ e = p->fcharp;
+ e1 = mkexpr(OPMINUS, cpexpr(e), ICON(1));
+ if (!ISCONST(e) && (e->tag != TPRIM || e->primblock.argsp)) {
+ e = (expptr)mktmp(TYLONG, ENULL);
+ putout(putassign(cpexpr(e), e1));
+ p->fcharp = mkexpr(OPPLUS, cpexpr(e), ICON(1));
+ e1 = e;
+ }
+ offp = mkexpr(OPPLUS, offp, e1);
+ }
+ return(offp);
+}
+
+
+
+
+ expptr
+#ifdef KR_headers
+subcheck(np, p)
+ Namep np;
+ register expptr p;
+#else
+subcheck(Namep np, register expptr p)
+#endif
+{
+ struct Dimblock *dimp;
+ expptr t, checkvar, checkcond, badcall;
+
+ dimp = np->vdim;
+ if(dimp->nelt == NULL)
+ return(p); /* don't check arrays with * bounds */
+ np->vlastdim = 0;
+ if( ISICON(p) )
+ {
+
+/* check for negative (constant) offset */
+
+ if(p->constblock.Const.ci < 0)
+ goto badsub;
+ if( ISICON(dimp->nelt) )
+
+/* see if constant offset exceeds the array declaration */
+
+ if(p->constblock.Const.ci < dimp->nelt->constblock.Const.ci)
+ return(p);
+ else
+ goto badsub;
+ }
+
+/* We know that the subscript offset p or dimp -> nelt is not a constant.
+ Now find a register to use for run-time bounds checking */
+
+ if(p->tag==TADDR && p->addrblock.vstg==STGREG)
+ {
+ checkvar = (expptr) cpexpr(p);
+ t = p;
+ }
+ else {
+ checkvar = (expptr) mktmp(p->headblock.vtype, ENULL);
+ t = mkexpr(OPASSIGN, cpexpr(checkvar), p);
+ }
+ checkcond = mkexpr(OPLT, t, cpexpr(dimp->nelt) );
+ if( ! ISICON(p) )
+ checkcond = mkexpr(OPAND, checkcond,
+ mkexpr(OPLE, ICON(0), cpexpr(checkvar)) );
+
+/* Construct the actual test */
+
+ badcall = call4(p->headblock.vtype, "s_rnge",
+ mkstrcon(strlen(np->fvarname), np->fvarname),
+ mkconv(TYLONG, cpexpr(checkvar)),
+ mkstrcon(strlen(procname), procname),
+ ICON(lineno) );
+ badcall->exprblock.opcode = OPCCALL;
+ p = mkexpr(OPQUEST, checkcond,
+ mkexpr(OPCOLON, checkvar, badcall));
+
+ return(p);
+
+badsub:
+ frexpr(p);
+ errstr("subscript on variable %s out of range", np->fvarname);
+ return ( ICON(0) );
+}
+
+
+
+
+ Addrp
+#ifdef KR_headers
+mkaddr(p)
+ register Namep p;
+#else
+mkaddr(register Namep p)
+#endif
+{
+ Extsym *extp;
+ register Addrp t;
+ int k;
+
+ switch( p->vstg)
+ {
+ case STGAUTO:
+ if(p->vclass == CLPROC && p->vprocclass == PTHISPROC)
+ return (Addrp) cpexpr((expptr)xretslot[p->vtype]);
+ goto other;
+
+ case STGUNKNOWN:
+ if(p->vclass != CLPROC)
+ break; /* Error */
+ extp = mkext(p->fvarname, addunder(p->cvarname));
+ extp->extstg = STGEXT;
+ p->vstg = STGEXT;
+ p->vardesc.varno = extp - extsymtab;
+ p->vprocclass = PEXTERNAL;
+ if ((extp->exproto || infertypes)
+ && (p->vtype == TYUNKNOWN || p->vimpltype)
+ && (k = extp->extype))
+ inferdcl(p, k);
+
+
+ case STGCOMMON:
+ case STGEXT:
+ case STGBSS:
+ case STGINIT:
+ case STGEQUIV:
+ case STGARG:
+ case STGLENG:
+ other:
+ t = ALLOC(Addrblock);
+ t->tag = TADDR;
+
+ t->vclass = p->vclass;
+ t->vtype = p->vtype;
+ t->vstg = p->vstg;
+ t->memno = p->vardesc.varno;
+ t->memoffset = ICON(p->voffset);
+ if (p->vdim)
+ t->isarray = 1;
+ if(p->vleng)
+ {
+ t->vleng = (expptr) cpexpr(p->vleng);
+ if( ISICON(t->vleng) )
+ t->varleng = t->vleng->constblock.Const.ci;
+ }
+
+/* Keep the original name around for the C code generation */
+
+ t -> uname_tag = UNAM_NAME;
+ t -> user.name = p;
+ return(t);
+
+ case STGINTR:
+
+ return ( intraddr (p));
+
+ case STGSTFUNCT:
+
+ errstr("invalid use of statement function %.64s.", p->fvarname);
+ return putconst((Constp)ICON(0));
+ }
+ badstg("mkaddr", p->vstg);
+ /* NOT REACHED */ return 0;
+}
+
+
+
+
+/* mkarg -- create storage for a new parameter. This is called when a
+ function returns a string (for the return value, which is the first
+ parameter), or when a variable-length string is passed to a function. */
+
+ Addrp
+#ifdef KR_headers
+mkarg(type, argno)
+ int type;
+ int argno;
+#else
+mkarg(int type, int argno)
+#endif
+{
+ register Addrp p;
+
+ p = ALLOC(Addrblock);
+ p->tag = TADDR;
+ p->vtype = type;
+ p->vclass = CLVAR;
+
+/* TYLENG is the type of the field holding the length of a character string */
+
+ p->vstg = (type==TYLENG ? STGLENG : STGARG);
+ p->memno = argno;
+ return(p);
+}
+
+
+
+
+/* mkprim -- Create a PRIM (primary/primitive) block consisting of a
+ Nameblock (or Paramblock), arguments (actual params or array
+ subscripts) and substring bounds. Requires that v have lots of
+ extra (uninitialized) storage, since it could be a paramblock or
+ nameblock */
+
+ expptr
+#ifdef KR_headers
+mkprim(v0, args, substr)
+ Namep v0;
+ struct Listblock *args;
+ chainp substr;
+#else
+mkprim(Namep v0, struct Listblock *args, chainp substr)
+#endif
+{
+ typedef union {
+ struct Paramblock paramblock;
+ struct Nameblock nameblock;
+ struct Headblock headblock;
+ } *Primu;
+ register Primu v = (Primu)v0;
+ register struct Primblock *p;
+
+ if(v->headblock.vclass == CLPARAM)
+ {
+
+/* v is to be a Paramblock */
+
+ if(args || substr)
+ {
+ errstr("no qualifiers on parameter name %s",
+ v->paramblock.fvarname);
+ frexpr((expptr)args);
+ if(substr)
+ {
+ frexpr((tagptr)substr->datap);
+ frexpr((tagptr)substr->nextp->datap);
+ frchain(&substr);
+ }
+ frexpr((expptr)v);
+ return( errnode() );
+ }
+ return( (expptr) cpexpr(v->paramblock.paramval) );
+ }
+
+ p = ALLOC(Primblock);
+ p->tag = TPRIM;
+ p->vtype = v->nameblock.vtype;
+
+/* v is to be a Nameblock */
+
+ p->namep = (Namep) v;
+ p->argsp = args;
+ if(substr)
+ {
+ p->fcharp = (expptr) substr->datap;
+ p->lcharp = (expptr) substr->nextp->datap;
+ frchain(&substr);
+ }
+ return( (expptr) p);
+}
+
+
+
+/* vardcl -- attempt to fill out the Name template for variable v.
+ This function is called on identifiers known to be variables or
+ recursive references to the same function */
+
+ void
+#ifdef KR_headers
+vardcl(v)
+ register Namep v;
+#else
+vardcl(register Namep v)
+#endif
+{
+ struct Dimblock *t;
+ expptr neltp;
+ extern int doing_stmtfcn;
+
+ if(v->vclass == CLUNKNOWN) {
+ v->vclass = CLVAR;
+ if (v->vinftype) {
+ v->vtype = TYUNKNOWN;
+ if (v->vdcldone) {
+ v->vdcldone = 0;
+ impldcl(v);
+ }
+ }
+ }
+ if(v->vdcldone)
+ return;
+ if(v->vclass == CLNAMELIST)
+ return;
+
+ if(v->vtype == TYUNKNOWN)
+ impldcl(v);
+ else if(v->vclass!=CLVAR && v->vprocclass!=PTHISPROC)
+ {
+ dclerr("used as variable", v);
+ return;
+ }
+ if(v->vstg==STGUNKNOWN) {
+ if (doing_stmtfcn) {
+ /* neither declare this variable if its only use */
+ /* is in defining a stmt function, nor complain */
+ /* that it is never used */
+ v->vimpldovar = 1;
+ return;
+ }
+ v->vstg = implstg[ letter(v->fvarname[0]) ];
+ v->vimplstg = 1;
+ }
+
+/* Compute the actual storage location, i.e. offsets from base addresses,
+ possibly the stack pointer */
+
+ switch(v->vstg)
+ {
+ case STGBSS:
+ v->vardesc.varno = ++lastvarno;
+ break;
+ case STGAUTO:
+ if(v->vclass==CLPROC && v->vprocclass==PTHISPROC)
+ break;
+ if(t = v->vdim)
+ if( (neltp = t->nelt) && ISCONST(neltp) ) ;
+ else
+ dclerr("adjustable automatic array", v);
+ break;
+
+ default:
+ break;
+ }
+ v->vdcldone = YES;
+}
+
+
+
+/* Set the implicit type declaration of parameter p based on its first
+ letter */
+
+ void
+#ifdef KR_headers
+impldcl(p)
+ register Namep p;
+#else
+impldcl(register Namep p)
+#endif
+{
+ register int k;
+ int type;
+ ftnint leng;
+
+ if(p->vdcldone || (p->vclass==CLPROC && p->vprocclass==PINTRINSIC) )
+ return;
+ if(p->vtype == TYUNKNOWN)
+ {
+ k = letter(p->fvarname[0]);
+ type = impltype[ k ];
+ leng = implleng[ k ];
+ if(type == TYUNKNOWN)
+ {
+ if(p->vclass == CLPROC)
+ return;
+ dclerr("attempt to use undefined variable", p);
+ type = dflttype[k];
+ leng = 0;
+ }
+ settype(p, type, leng);
+ p->vimpltype = 1;
+ }
+}
+
+ void
+#ifdef KR_headers
+inferdcl(np, type)
+ Namep np;
+ int type;
+#else
+inferdcl(Namep np, int type)
+#endif
+{
+ int k = impltype[letter(np->fvarname[0])];
+ if (k != type) {
+ np->vinftype = 1;
+ np->vtype = type;
+ frexpr(np->vleng);
+ np->vleng = 0;
+ }
+ np->vimpltype = 0;
+ np->vinfproc = 1;
+ }
+
+ LOCAL int
+#ifdef KR_headers
+zeroconst(e)
+ expptr e;
+#else
+zeroconst(expptr e)
+#endif
+{
+ register Constp c = (Constp) e;
+ if (c->tag == TCONST)
+ switch(c->vtype) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ return c->Const.ci == 0;
+
+ case TYREAL:
+ case TYDREAL:
+ if (c->vstg == 1)
+ return !strcmp(c->Const.cds[0],"0.");
+ return c->Const.cd[0] == 0.;
+
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if (c->vstg == 1)
+ return !strcmp(c->Const.cds[0],"0.")
+ && !strcmp(c->Const.cds[1],"0.");
+ return c->Const.cd[0] == 0. && c->Const.cd[1] == 0.;
+ }
+ return 0;
+ }
+
+
+#define ICONEQ(z, c) (ISICON(z) && z->constblock.Const.ci==c)
+#define COMMUTE { e = lp; lp = rp; rp = e; }
+
+/* mkexpr -- Make expression, and simplify constant subcomponents (tree
+ order is not preserved). Assumes that lp is nonempty, and uses
+ fold() to simplify adjacent constants */
+
+ expptr
+#ifdef KR_headers
+mkexpr(opcode, lp, rp)
+ int opcode;
+ register expptr lp;
+ register expptr rp;
+#else
+mkexpr(int opcode, register expptr lp, register expptr rp)
+#endif
+{
+ register expptr e, e1;
+ int etype;
+ int ltype, rtype;
+ int ltag, rtag;
+ long L;
+ static long divlineno;
+
+ ltype = lp->headblock.vtype;
+ ltag = lp->tag;
+ if(rp && opcode!=OPCALL && opcode!=OPCCALL)
+ {
+ rtype = rp->headblock.vtype;
+ rtag = rp->tag;
+ }
+ else rtype = 0;
+
+ etype = cktype(opcode, ltype, rtype);
+ if(etype == TYERROR)
+ goto error;
+
+ switch(opcode)
+ {
+ /* check for multiplication by 0 and 1 and addition to 0 */
+
+ case OPSTAR:
+ if( ISCONST(lp) )
+ COMMUTE
+
+ if( ISICON(rp) )
+ {
+ if(rp->constblock.Const.ci == 0)
+ goto retright;
+ goto mulop;
+ }
+ break;
+
+ case OPSLASH:
+ case OPMOD:
+ if( zeroconst(rp) && lineno != divlineno ) {
+ warn("attempted division by zero");
+ divlineno = lineno;
+ }
+ if(opcode == OPMOD)
+ break;
+
+/* Handle multiplying or dividing by 1, -1 */
+
+mulop:
+ if( ISICON(rp) )
+ {
+ if(rp->constblock.Const.ci == 1)
+ goto retleft;
+
+ if(rp->constblock.Const.ci == -1)
+ {
+ frexpr(rp);
+ return( mkexpr(OPNEG, lp, ENULL) );
+ }
+ }
+
+/* Group all constants together. In particular,
+
+ (x * CONST1) * CONST2 ==> x * (CONST1 * CONST2)
+ (x * CONST1) / CONST2 ==> x * (CONST1 / CONST2)
+*/
+
+ if (!ISINT(etype) || lp->tag != TEXPR || !lp->exprblock.rightp
+ || !ISICON(lp->exprblock.rightp))
+ break;
+
+ if (lp->exprblock.opcode == OPLSHIFT) {
+ L = 1 << lp->exprblock.rightp->constblock.Const.ci;
+ if (opcode == OPSTAR || ISICON(rp) &&
+ !(L % rp->constblock.Const.ci)) {
+ lp->exprblock.opcode = OPSTAR;
+ lp->exprblock.rightp->constblock.Const.ci = L;
+ }
+ }
+
+ if (lp->exprblock.opcode == OPSTAR) {
+ if(opcode == OPSTAR)
+ e = mkexpr(OPSTAR, lp->exprblock.rightp, rp);
+ else if(ISICON(rp) &&
+ (lp->exprblock.rightp->constblock.Const.ci %
+ rp->constblock.Const.ci) == 0)
+ e = mkexpr(OPSLASH, lp->exprblock.rightp, rp);
+ else break;
+
+ e1 = lp->exprblock.leftp;
+ free( (charptr) lp );
+ return( mkexpr(OPSTAR, e1, e) );
+ }
+ break;
+
+
+ case OPPLUS:
+ if( ISCONST(lp) )
+ COMMUTE
+ goto addop;
+
+ case OPMINUS:
+ if( ICONEQ(lp, 0) )
+ {
+ frexpr(lp);
+ return( mkexpr(OPNEG, rp, ENULL) );
+ }
+
+ if( ISCONST(rp) && is_negatable((Constp)rp))
+ {
+ opcode = OPPLUS;
+ consnegop((Constp)rp);
+ }
+
+/* Group constants in an addition expression (also subtraction, since the
+ subtracted value was negated above). In particular,
+
+ (x + CONST1) + CONST2 ==> x + (CONST1 + CONST2)
+*/
+
+addop:
+ if( ISICON(rp) )
+ {
+ if(rp->constblock.Const.ci == 0)
+ goto retleft;
+ if( ISPLUSOP(lp) && ISICON(lp->exprblock.rightp) )
+ {
+ e = mkexpr(OPPLUS, lp->exprblock.rightp, rp);
+ e1 = lp->exprblock.leftp;
+ free( (charptr) lp );
+ return( mkexpr(OPPLUS, e1, e) );
+ }
+ }
+ if (opcode == OPMINUS && (ISINT(etype) || doing_vleng)) {
+ /* check for (i [+const]) - (i [+const]) */
+ if (lp->tag == TPRIM)
+ e = lp;
+ else if (lp->tag == TEXPR && lp->exprblock.opcode == OPPLUS
+ && lp->exprblock.rightp->tag == TCONST) {
+ e = lp->exprblock.leftp;
+ if (e->tag != TPRIM)
+ break;
+ }
+ else
+ break;
+ if (e->primblock.argsp)
+ break;
+ if (rp->tag == TPRIM)
+ e1 = rp;
+ else if (rp->tag == TEXPR && rp->exprblock.opcode == OPPLUS
+ && rp->exprblock.rightp->tag == TCONST) {
+ e1 = rp->exprblock.leftp;
+ if (e1->tag != TPRIM)
+ break;
+ }
+ else
+ break;
+ if (e->primblock.namep != e1->primblock.namep
+ || e1->primblock.argsp)
+ break;
+ L = e == lp ? 0 : lp->exprblock.rightp->constblock.Const.ci;
+ if (e1 != rp)
+ L -= rp->exprblock.rightp->constblock.Const.ci;
+ frexpr(lp);
+ frexpr(rp);
+ return ICON(L);
+ }
+
+ break;
+
+
+ case OPPOWER:
+ break;
+
+/* Eliminate outermost double negations */
+
+ case OPNEG:
+ case OPNEG1:
+ if(ltag==TEXPR && lp->exprblock.opcode==OPNEG)
+ {
+ e = lp->exprblock.leftp;
+ free( (charptr) lp );
+ return(e);
+ }
+ break;
+
+/* Eliminate outermost double NOTs */
+
+ case OPNOT:
+ if(ltag==TEXPR && lp->exprblock.opcode==OPNOT)
+ {
+ e = lp->exprblock.leftp;
+ free( (charptr) lp );
+ return(e);
+ }
+ break;
+
+ case OPCALL:
+ case OPCCALL:
+ etype = ltype;
+ if(rp!=NULL && rp->listblock.listp==NULL)
+ {
+ free( (charptr) rp );
+ rp = NULL;
+ }
+ break;
+
+ case OPAND:
+ case OPOR:
+ if( ISCONST(lp) )
+ COMMUTE
+
+ if( ISCONST(rp) )
+ {
+ if(rp->constblock.Const.ci == 0)
+ if(opcode == OPOR)
+ goto retleft;
+ else
+ goto retright;
+ else if(opcode == OPOR)
+ goto retright;
+ else
+ goto retleft;
+ }
+ case OPEQV:
+ case OPNEQV:
+
+ case OPBITAND:
+ case OPBITOR:
+ case OPBITXOR:
+ case OPBITNOT:
+ case OPLSHIFT:
+ case OPRSHIFT:
+
+ case OPLT:
+ case OPGT:
+ case OPLE:
+ case OPGE:
+ case OPEQ:
+ case OPNE:
+
+ case OPCONCAT:
+ break;
+ case OPMIN:
+ case OPMAX:
+ case OPMIN2:
+ case OPMAX2:
+ case OPDMIN:
+ case OPDMAX:
+
+ case OPASSIGN:
+ case OPASSIGNI:
+ case OPPLUSEQ:
+ case OPSTAREQ:
+ case OPMINUSEQ:
+ case OPSLASHEQ:
+ case OPMODEQ:
+ case OPLSHIFTEQ:
+ case OPRSHIFTEQ:
+ case OPBITANDEQ:
+ case OPBITXOREQ:
+ case OPBITOREQ:
+
+ case OPCONV:
+ case OPADDR:
+ case OPWHATSIN:
+
+ case OPCOMMA:
+ case OPCOMMA_ARG:
+ case OPQUEST:
+ case OPCOLON:
+ case OPDOT:
+ case OPARROW:
+ case OPIDENTITY:
+ case OPCHARCAST:
+ case OPABS:
+ case OPDABS:
+ break;
+
+ default:
+ badop("mkexpr", opcode);
+ }
+
+ e = (expptr) ALLOC(Exprblock);
+ e->exprblock.tag = TEXPR;
+ e->exprblock.opcode = opcode;
+ e->exprblock.vtype = etype;
+ e->exprblock.leftp = lp;
+ e->exprblock.rightp = rp;
+ if(ltag==TCONST && (rp==0 || rtag==TCONST) )
+ e = fold(e);
+ return(e);
+
+retleft:
+ frexpr(rp);
+ if (lp->tag == TPRIM)
+ lp->primblock.parenused = 1;
+ return(lp);
+
+retright:
+ frexpr(lp);
+ if (rp->tag == TPRIM)
+ rp->primblock.parenused = 1;
+ return(rp);
+
+error:
+ frexpr(lp);
+ if(rp && opcode!=OPCALL && opcode!=OPCCALL)
+ frexpr(rp);
+ return( errnode() );
+}
+
+#define ERR(s) { errs = s; goto error; }
+
+/* cktype -- Check and return the type of the expression */
+
+#ifdef KR_headers
+cktype(op, lt, rt)
+ register int op;
+ register int lt;
+ register int rt;
+#else
+cktype(register int op, register int lt, register int rt)
+#endif
+{
+ char *errs;
+
+ if(lt==TYERROR || rt==TYERROR)
+ goto error1;
+
+ if(lt==TYUNKNOWN)
+ return(TYUNKNOWN);
+ if(rt==TYUNKNOWN)
+
+/* If not unary operation, return UNKNOWN */
+
+ if(!is_unary_op (op) && op != OPCALL && op != OPCCALL)
+ return(TYUNKNOWN);
+
+ switch(op)
+ {
+ case OPPLUS:
+ case OPMINUS:
+ case OPSTAR:
+ case OPSLASH:
+ case OPPOWER:
+ case OPMOD:
+ if( ISNUMERIC(lt) && ISNUMERIC(rt) )
+ return( maxtype(lt, rt) );
+ ERR("nonarithmetic operand of arithmetic operator")
+
+ case OPNEG:
+ case OPNEG1:
+ if( ISNUMERIC(lt) )
+ return(lt);
+ ERR("nonarithmetic operand of negation")
+
+ case OPNOT:
+ if(ISLOGICAL(lt))
+ return(lt);
+ ERR("NOT of nonlogical")
+
+ case OPAND:
+ case OPOR:
+ case OPEQV:
+ case OPNEQV:
+ if(ISLOGICAL(lt) && ISLOGICAL(rt))
+ return( maxtype(lt, rt) );
+ ERR("nonlogical operand of logical operator")
+
+ case OPLT:
+ case OPGT:
+ case OPLE:
+ case OPGE:
+ case OPEQ:
+ case OPNE:
+ if(lt==TYCHAR || rt==TYCHAR || ISLOGICAL(lt) || ISLOGICAL(rt))
+ {
+ if(lt != rt){
+ if (htype
+ && (lt == TYCHAR && ISNUMERIC(rt)
+ || rt == TYCHAR && ISNUMERIC(lt)))
+ return TYLOGICAL;
+ ERR("illegal comparison")
+ }
+ }
+
+ else if( ISCOMPLEX(lt) || ISCOMPLEX(rt) )
+ {
+ if(op!=OPEQ && op!=OPNE)
+ ERR("order comparison of complex data")
+ }
+
+ else if( ! ISNUMERIC(lt) || ! ISNUMERIC(rt) )
+ ERR("comparison of nonarithmetic data")
+ return(TYLOGICAL);
+
+ case OPCONCAT:
+ if(lt==TYCHAR && rt==TYCHAR)
+ return(TYCHAR);
+ ERR("concatenation of nonchar data")
+
+ case OPCALL:
+ case OPCCALL:
+ case OPIDENTITY:
+ return(lt);
+
+ case OPADDR:
+ case OPCHARCAST:
+ return(TYADDR);
+
+ case OPCONV:
+ if(rt == 0)
+ return(0);
+ if(lt==TYCHAR && ISINT(rt) )
+ return(TYCHAR);
+ if (ISLOGICAL(lt) && ISLOGICAL(rt))
+ return lt;
+ case OPASSIGN:
+ case OPASSIGNI:
+ case OPMINUSEQ:
+ case OPPLUSEQ:
+ case OPSTAREQ:
+ case OPSLASHEQ:
+ case OPMODEQ:
+ case OPLSHIFTEQ:
+ case OPRSHIFTEQ:
+ case OPBITANDEQ:
+ case OPBITXOREQ:
+ case OPBITOREQ:
+ if( ISINT(lt) && rt==TYCHAR)
+ return(lt);
+ if (ISLOGICAL(lt) && ISLOGICAL(rt) && op == OPASSIGN)
+ return lt;
+ if(lt==TYCHAR || rt==TYCHAR || ISLOGICAL(lt) || ISLOGICAL(rt))
+ if((op!=OPASSIGN && op != OPPLUSEQ && op != OPMINUSEQ)
+ || (lt!=rt))
+ {
+ ERR("impossible conversion")
+ }
+ return(lt);
+
+ case OPMIN:
+ case OPMAX:
+ case OPDMIN:
+ case OPDMAX:
+ case OPMIN2:
+ case OPMAX2:
+ case OPBITOR:
+ case OPBITAND:
+ case OPBITXOR:
+ case OPBITNOT:
+ case OPLSHIFT:
+ case OPRSHIFT:
+ case OPWHATSIN:
+ case OPABS:
+ case OPDABS:
+ return(lt);
+
+ case OPCOMMA:
+ case OPCOMMA_ARG:
+ case OPQUEST:
+ case OPCOLON: /* Only checks the rightmost type because
+ of C language definition (rightmost
+ comma-expr is the value of the expr) */
+ return(rt);
+
+ case OPDOT:
+ case OPARROW:
+ return (lt);
+ default:
+ badop("cktype", op);
+ }
+error:
+ err(errs);
+error1:
+ return(TYERROR);
+}
+
+ static void
+intovfl(Void)
+{ err("overflow simplifying integer constants."); }
+
+/* fold -- simplifies constant expressions; it assumes that e -> leftp and
+ e -> rightp are TCONST or NULL */
+
+ expptr
+#ifdef KR_headers
+fold(e)
+ register expptr e;
+#else
+fold(register expptr e)
+#endif
+{
+ Constp p;
+ register expptr lp, rp;
+ int etype, mtype, ltype, rtype, opcode;
+ int i, bl, ll, lr;
+ char *q, *s;
+ struct Constblock lcon, rcon;
+ ftnint L;
+ double d;
+
+ opcode = e->exprblock.opcode;
+ etype = e->exprblock.vtype;
+
+ lp = e->exprblock.leftp;
+ ltype = lp->headblock.vtype;
+ rp = e->exprblock.rightp;
+
+ if(rp == 0)
+ switch(opcode)
+ {
+ case OPNOT:
+ lp->constblock.Const.ci = ! lp->constblock.Const.ci;
+ retlp:
+ e->exprblock.leftp = 0;
+ frexpr(e);
+ return(lp);
+
+ case OPBITNOT:
+ lp->constblock.Const.ci = ~ lp->constblock.Const.ci;
+ goto retlp;
+
+ case OPNEG:
+ case OPNEG1:
+ consnegop((Constp)lp);
+ goto retlp;
+
+ case OPCONV:
+ case OPADDR:
+ return(e);
+
+ case OPABS:
+ case OPDABS:
+ switch(ltype) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ if ((L = lp->constblock.Const.ci) < 0) {
+ lp->constblock.Const.ci = -L;
+ if (L != -lp->constblock.Const.ci)
+ intovfl();
+ }
+ goto retlp;
+ case TYREAL:
+ case TYDREAL:
+ if (lp->constblock.vstg) {
+ s = lp->constblock.Const.cds[0];
+ if (*s == '-')
+ lp->constblock.Const.cds[0] = s + 1;
+ goto retlp;
+ }
+ if ((d = lp->constblock.Const.cd[0]) < 0.)
+ lp->constblock.Const.cd[0] = -d;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ return e; /* lazy way out */
+ }
+ default:
+ badop("fold", opcode);
+ }
+
+ rtype = rp->headblock.vtype;
+
+ p = ALLOC(Constblock);
+ p->tag = TCONST;
+ p->vtype = etype;
+ p->vleng = e->exprblock.vleng;
+
+ switch(opcode)
+ {
+ case OPCOMMA:
+ case OPCOMMA_ARG:
+ case OPQUEST:
+ case OPCOLON:
+ goto ereturn;
+
+ case OPAND:
+ p->Const.ci = lp->constblock.Const.ci &&
+ rp->constblock.Const.ci;
+ break;
+
+ case OPOR:
+ p->Const.ci = lp->constblock.Const.ci ||
+ rp->constblock.Const.ci;
+ break;
+
+ case OPEQV:
+ p->Const.ci = lp->constblock.Const.ci ==
+ rp->constblock.Const.ci;
+ break;
+
+ case OPNEQV:
+ p->Const.ci = lp->constblock.Const.ci !=
+ rp->constblock.Const.ci;
+ break;
+
+ case OPBITAND:
+ p->Const.ci = lp->constblock.Const.ci &
+ rp->constblock.Const.ci;
+ break;
+
+ case OPBITOR:
+ p->Const.ci = lp->constblock.Const.ci |
+ rp->constblock.Const.ci;
+ break;
+
+ case OPBITXOR:
+ p->Const.ci = lp->constblock.Const.ci ^
+ rp->constblock.Const.ci;
+ break;
+
+ case OPLSHIFT:
+ p->Const.ci = lp->constblock.Const.ci <<
+ rp->constblock.Const.ci;
+ if ((((unsigned long)p->Const.ci) >> rp->constblock.Const.ci)
+ != lp->constblock.Const.ci)
+ intovfl();
+ break;
+
+ case OPRSHIFT:
+ p->Const.ci = lp->constblock.Const.ci >>
+ rp->constblock.Const.ci;
+ break;
+
+ case OPCONCAT:
+ ll = lp->constblock.vleng->constblock.Const.ci;
+ lr = rp->constblock.vleng->constblock.Const.ci;
+ bl = lp->constblock.Const.ccp1.blanks;
+ p->Const.ccp = q = (char *) ckalloc(ll+lr+bl);
+ p->Const.ccp1.blanks = rp->constblock.Const.ccp1.blanks;
+ p->vleng = ICON(ll+lr+bl);
+ s = lp->constblock.Const.ccp;
+ for(i = 0 ; i < ll ; ++i)
+ *q++ = *s++;
+ for(i = 0 ; i < bl ; i++)
+ *q++ = ' ';
+ s = rp->constblock.Const.ccp;
+ for(i = 0; i < lr; ++i)
+ *q++ = *s++;
+ break;
+
+
+ case OPPOWER:
+ if( !ISINT(rtype)
+ || rp->constblock.Const.ci < 0 && zeroconst(lp))
+ goto ereturn;
+ conspower(p, (Constp)lp, rp->constblock.Const.ci);
+ break;
+
+ case OPSLASH:
+ if (zeroconst(rp))
+ goto ereturn;
+ /* no break */
+
+ default:
+ if(ltype == TYCHAR)
+ {
+ lcon.Const.ci = cmpstr(lp->constblock.Const.ccp,
+ rp->constblock.Const.ccp,
+ lp->constblock.vleng->constblock.Const.ci,
+ rp->constblock.vleng->constblock.Const.ci);
+ rcon.Const.ci = 0;
+ mtype = tyint;
+ }
+ else {
+ mtype = maxtype(ltype, rtype);
+ consconv(mtype, &lcon, &lp->constblock);
+ consconv(mtype, &rcon, &rp->constblock);
+ }
+ consbinop(opcode, mtype, p, &lcon, &rcon);
+ break;
+ }
+
+ frexpr(e);
+ return( (expptr) p );
+ ereturn:
+ free((char *)p);
+ return e;
+}
+
+
+
+/* assign constant l = r , doing coercion */
+
+ void
+#ifdef KR_headers
+consconv(lt, lc, rc)
+ int lt;
+ register Constp lc;
+ register Constp rc;
+#else
+consconv(int lt, register Constp lc, register Constp rc)
+#endif
+{
+ int rt = rc->vtype;
+ register union Constant *lv = &lc->Const, *rv = &rc->Const;
+
+ lc->vtype = lt;
+ if (ONEOF(lt, MSKREAL|MSKCOMPLEX) && ONEOF(rt, MSKREAL|MSKCOMPLEX)) {
+ memcpy((char *)lv, (char *)rv, sizeof(union Constant));
+ lc->vstg = rc->vstg;
+ if (ISCOMPLEX(lt) && ISREAL(rt)) {
+ if (rc->vstg)
+ lv->cds[1] = cds("0",CNULL);
+ else
+ lv->cd[1] = 0.;
+ }
+ return;
+ }
+ lc->vstg = 0;
+
+ switch(lt)
+ {
+
+/* Casting to character means just copying the first sizeof (character)
+ bytes into a new 1 character string. This is weird. */
+
+ case TYCHAR:
+ *(lv->ccp = (char *) ckalloc(1)) = rv->ci;
+ lv->ccp1.blanks = 0;
+ break;
+
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ if(rt == TYCHAR)
+ lv->ci = rv->ccp[0];
+ else if( ISINT(rt) )
+ lv->ci = rv->ci;
+ else lv->ci = rc->vstg ? atof(rv->cds[0]) : rv->cd[0];
+
+ break;
+
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ lv->cd[1] = 0.;
+ lv->cd[0] = rv->ci;
+ break;
+
+ case TYREAL:
+ case TYDREAL:
+ lv->cd[0] = rv->ci;
+ break;
+
+ case TYLOGICAL:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ lv->ci = rv->ci;
+ break;
+ }
+}
+
+
+
+/* Negate constant value -- changes the input node's value */
+
+ void
+#ifdef KR_headers
+consnegop(p)
+ register Constp p;
+#else
+consnegop(register Constp p)
+#endif
+{
+ register char *s;
+ ftnint L;
+
+ if (p->vstg) {
+ if (ISCOMPLEX(p->vtype)) {
+ s = p->Const.cds[1];
+ p->Const.cds[1] = *s == '-' ? s+1
+ : *s == '0' ? s : s-1;
+ }
+ s = p->Const.cds[0];
+ p->Const.cds[0] = *s == '-' ? s+1
+ : *s == '0' ? s : s-1;
+ return;
+ }
+ switch(p->vtype)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ p->Const.ci = -(L = p->Const.ci);
+ if (L != -p->Const.ci)
+ intovfl();
+ break;
+
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ p->Const.cd[1] = - p->Const.cd[1];
+ /* fall through and do the real parts */
+ case TYREAL:
+ case TYDREAL:
+ p->Const.cd[0] = - p->Const.cd[0];
+ break;
+ default:
+ badtype("consnegop", p->vtype);
+ }
+}
+
+
+
+/* conspower -- Expand out an exponentiation */
+
+ LOCAL void
+#ifdef KR_headers
+conspower(p, ap, n)
+ Constp p;
+ Constp ap;
+ ftnint n;
+#else
+conspower(Constp p, Constp ap, ftnint n)
+#endif
+{
+ register union Constant *powp = &p->Const;
+ register int type;
+ struct Constblock x, x0;
+
+ if (n == 1) {
+ memcpy((char *)powp, (char *)&ap->Const, sizeof(ap->Const));
+ return;
+ }
+
+ switch(type = ap->vtype) /* pow = 1 */
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ powp->ci = 1;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ powp->cd[1] = 0;
+ case TYREAL:
+ case TYDREAL:
+ powp->cd[0] = 1;
+ break;
+ default:
+ badtype("conspower", type);
+ }
+
+ if(n == 0)
+ return;
+ switch(type) /* x0 = ap */
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ x0.Const.ci = ap->Const.ci;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ x0.Const.cd[1] =
+ ap->vstg ? atof(ap->Const.cds[1]) : ap->Const.cd[1];
+ case TYREAL:
+ case TYDREAL:
+ x0.Const.cd[0] =
+ ap->vstg ? atof(ap->Const.cds[0]) : ap->Const.cd[0];
+ break;
+ }
+ x0.vtype = type;
+ x0.vstg = 0;
+ if(n < 0)
+ {
+ if( ISINT(type) )
+ {
+ err("integer ** negative number");
+ return;
+ }
+ else if (!x0.Const.cd[0]
+ && (!ISCOMPLEX(type) || !x0.Const.cd[1])) {
+ err("0.0 ** negative number");
+ return;
+ }
+ n = -n;
+ consbinop(OPSLASH, type, &x, p, &x0);
+ }
+ else
+ consbinop(OPSTAR, type, &x, p, &x0);
+
+ for( ; ; )
+ {
+ if(n & 01)
+ consbinop(OPSTAR, type, p, p, &x);
+ if(n >>= 1)
+ consbinop(OPSTAR, type, &x, &x, &x);
+ else
+ break;
+ }
+}
+
+
+
+/* do constant operation cp = a op b -- assumes that ap and bp have data
+ matching the input type */
+
+ LOCAL void
+#ifdef KR_headers
+consbinop(opcode, type, cpp, app, bpp)
+ int opcode;
+ int type;
+ Constp cpp;
+ Constp app;
+ Constp bpp;
+#else
+consbinop(int opcode, int type, Constp cpp, Constp app, Constp bpp)
+#endif
+{
+ register union Constant *ap = &app->Const,
+ *bp = &bpp->Const,
+ *cp = &cpp->Const;
+ int k;
+ double ad[2], bd[2], temp;
+ ftnint a, b;
+
+ cpp->vstg = 0;
+
+ if (ONEOF(type, MSKREAL|MSKCOMPLEX)) {
+ ad[0] = app->vstg ? atof(ap->cds[0]) : ap->cd[0];
+ bd[0] = bpp->vstg ? atof(bp->cds[0]) : bp->cd[0];
+ if (ISCOMPLEX(type)) {
+ ad[1] = app->vstg ? atof(ap->cds[1]) : ap->cd[1];
+ bd[1] = bpp->vstg ? atof(bp->cds[1]) : bp->cd[1];
+ }
+ }
+ switch(opcode)
+ {
+ case OPPLUS:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = ap->ci + bp->ci;
+ if (ap->ci != cp->ci - bp->ci)
+ intovfl();
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ cp->cd[1] = ad[1] + bd[1];
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] + bd[0];
+ break;
+ }
+ break;
+
+ case OPMINUS:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = ap->ci - bp->ci;
+ if (ap->ci != bp->ci + cp->ci)
+ intovfl();
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ cp->cd[1] = ad[1] - bd[1];
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] - bd[0];
+ break;
+ }
+ break;
+
+ case OPSTAR:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = (a = ap->ci) * (b = bp->ci);
+ if (a && cp->ci / a != b)
+ intovfl();
+ break;
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] * bd[0];
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ temp = ad[0] * bd[0] - ad[1] * bd[1] ;
+ cp->cd[1] = ad[0] * bd[1] + ad[1] * bd[0] ;
+ cp->cd[0] = temp;
+ break;
+ }
+ break;
+ case OPSLASH:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = ap->ci / bp->ci;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] / bd[0];
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ zdiv((dcomplex*)cp, (dcomplex*)ad, (dcomplex*)bd);
+ break;
+ }
+ break;
+
+ case OPMOD:
+ if( ISINT(type) )
+ {
+ cp->ci = ap->ci % bp->ci;
+ break;
+ }
+ else
+ Fatal("inline mod of noninteger");
+
+ case OPMIN2:
+ case OPDMIN:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = ap->ci <= bp->ci ? ap->ci : bp->ci;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] <= bd[0] ? ad[0] : bd[0];
+ break;
+ default:
+ Fatal("inline min of exected type");
+ }
+ break;
+
+ case OPMAX2:
+ case OPDMAX:
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp->ci = ap->ci >= bp->ci ? ap->ci : bp->ci;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ cp->cd[0] = ad[0] >= bd[0] ? ad[0] : bd[0];
+ break;
+ default:
+ Fatal("inline max of exected type");
+ }
+ break;
+
+ default: /* relational ops */
+ switch(type)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ if(ap->ci < bp->ci)
+ k = -1;
+ else if(ap->ci == bp->ci)
+ k = 0;
+ else k = 1;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ if(ad[0] < bd[0])
+ k = -1;
+ else if(ad[0] == bd[0])
+ k = 0;
+ else k = 1;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if(ad[0] == bd[0] &&
+ ad[1] == bd[1] )
+ k = 0;
+ else k = 1;
+ break;
+ }
+
+ switch(opcode)
+ {
+ case OPEQ:
+ cp->ci = (k == 0);
+ break;
+ case OPNE:
+ cp->ci = (k != 0);
+ break;
+ case OPGT:
+ cp->ci = (k == 1);
+ break;
+ case OPLT:
+ cp->ci = (k == -1);
+ break;
+ case OPGE:
+ cp->ci = (k >= 0);
+ break;
+ case OPLE:
+ cp->ci = (k <= 0);
+ break;
+ }
+ break;
+ }
+}
+
+
+
+/* conssgn - returns the sign of a Fortran constant */
+
+#ifdef KR_headers
+conssgn(p)
+ register expptr p;
+#else
+conssgn(register expptr p)
+#endif
+{
+ register char *s;
+
+ if( ! ISCONST(p) )
+ Fatal( "sgn(nonconstant)" );
+
+ switch(p->headblock.vtype)
+ {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ if(p->constblock.Const.ci > 0) return(1);
+ if(p->constblock.Const.ci < 0) return(-1);
+ return(0);
+
+ case TYREAL:
+ case TYDREAL:
+ if (p->constblock.vstg) {
+ s = p->constblock.Const.cds[0];
+ if (*s == '-')
+ return -1;
+ if (*s == '0')
+ return 0;
+ return 1;
+ }
+ if(p->constblock.Const.cd[0] > 0) return(1);
+ if(p->constblock.Const.cd[0] < 0) return(-1);
+ return(0);
+
+
+/* The sign of a complex number is 0 iff the number is 0 + 0i, else it's 1 */
+
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if (p->constblock.vstg)
+ return *p->constblock.Const.cds[0] != '0'
+ && *p->constblock.Const.cds[1] != '0';
+ return(p->constblock.Const.cd[0]!=0 || p->constblock.Const.cd[1]!=0);
+
+ default:
+ badtype( "conssgn", p->constblock.vtype);
+ }
+ /* NOT REACHED */ return 0;
+}
+
+char *powint[ ] = {
+ "pow_ii",
+#ifdef TYQUAD
+ "pow_qq",
+#endif
+ "pow_ri", "pow_di", "pow_ci", "pow_zi" };
+
+ LOCAL expptr
+#ifdef KR_headers
+mkpower(p)
+ register expptr p;
+#else
+mkpower(register expptr p)
+#endif
+{
+ register expptr q, lp, rp;
+ int ltype, rtype, mtype, tyi;
+
+ lp = p->exprblock.leftp;
+ rp = p->exprblock.rightp;
+ ltype = lp->headblock.vtype;
+ rtype = rp->headblock.vtype;
+
+ if (lp->tag == TADDR)
+ lp->addrblock.parenused = 0;
+
+ if (rp->tag == TADDR)
+ rp->addrblock.parenused = 0;
+
+ if(ISICON(rp))
+ {
+ if(rp->constblock.Const.ci == 0)
+ {
+ frexpr(p);
+ if( ISINT(ltype) )
+ return( ICON(1) );
+ else if (ISREAL (ltype))
+ return mkconv (ltype, ICON (1));
+ else
+ return( (expptr) putconst((Constp)
+ mkconv(ltype, ICON(1))) );
+ }
+ if(rp->constblock.Const.ci < 0)
+ {
+ if( ISINT(ltype) )
+ {
+ frexpr(p);
+ err("integer**negative");
+ return( errnode() );
+ }
+ rp->constblock.Const.ci = - rp->constblock.Const.ci;
+ p->exprblock.leftp = lp
+ = fixexpr((Exprp)mkexpr(OPSLASH, ICON(1), lp));
+ }
+ if(rp->constblock.Const.ci == 1)
+ {
+ frexpr(rp);
+ free( (charptr) p );
+ return(lp);
+ }
+
+ if( ONEOF(ltype, MSKINT|MSKREAL) ) {
+ p->exprblock.vtype = ltype;
+ return(p);
+ }
+ }
+ if( ISINT(rtype) )
+ {
+ if(ltype==TYSHORT && rtype==TYSHORT && (!ISCONST(lp) || tyint==TYSHORT) )
+ q = call2(TYSHORT, "pow_hh", lp, rp);
+ else {
+ if(ONEOF(ltype,M(TYINT1)|M(TYSHORT)))
+ {
+ ltype = TYLONG;
+ lp = mkconv(TYLONG,lp);
+ }
+#ifdef TYQUAD
+ if (ltype == TYQUAD)
+ rp = mkconv(TYQUAD,rp);
+ else
+#endif
+ rp = mkconv(TYLONG,rp);
+ if (ISCONST(rp)) {
+ tyi = tyint;
+ tyint = TYLONG;
+ rp = (expptr)putconst((Constp)rp);
+ tyint = tyi;
+ }
+ q = call2(ltype, powint[ltype-TYLONG], lp, rp);
+ }
+ }
+ else if( ISREAL( (mtype = maxtype(ltype,rtype)) )) {
+ extern int callk_kludge;
+ callk_kludge = TYDREAL;
+ q = call2(mtype, "pow_dd", mkconv(TYDREAL,lp), mkconv(TYDREAL,rp));
+ callk_kludge = 0;
+ }
+ else {
+ q = call2(TYDCOMPLEX, "pow_zz",
+ mkconv(TYDCOMPLEX,lp), mkconv(TYDCOMPLEX,rp));
+ if(mtype == TYCOMPLEX)
+ q = mkconv(TYCOMPLEX, q);
+ }
+ free( (charptr) p );
+ return(q);
+}
+
+
+/* Complex Division. Same code as in Runtime Library
+*/
+
+
+ LOCAL void
+#ifdef KR_headers
+zdiv(c, a, b)
+ register dcomplex *c;
+ register dcomplex *a;
+ register dcomplex *b;
+#else
+zdiv(register dcomplex *c, register dcomplex *a, register dcomplex *b)
+#endif
+{
+ double ratio, den;
+ double abr, abi;
+
+ if( (abr = b->dreal) < 0.)
+ abr = - abr;
+ if( (abi = b->dimag) < 0.)
+ abi = - abi;
+ if( abr <= abi )
+ {
+ if(abi == 0)
+ Fatal("complex division by zero");
+ ratio = b->dreal / b->dimag ;
+ den = b->dimag * (1 + ratio*ratio);
+ c->dreal = (a->dreal*ratio + a->dimag) / den;
+ c->dimag = (a->dimag*ratio - a->dreal) / den;
+ }
+
+ else
+ {
+ ratio = b->dimag / b->dreal ;
+ den = b->dreal * (1 + ratio*ratio);
+ c->dreal = (a->dreal + a->dimag*ratio) / den;
+ c->dimag = (a->dimag - a->dreal*ratio) / den;
+ }
+}
diff --git a/usr.bin/f2c/f2c.1 b/usr.bin/f2c/f2c.1
new file mode 100644
index 0000000..241fd98
--- /dev/null
+++ b/usr.bin/f2c/f2c.1
@@ -0,0 +1,356 @@
+. \" Definitions of F, L and LR for the benefit of systems
+. \" whose -man lacks them...
+.de F
+.nh
+.if n \%\&\\$1
+.if t \%\&\f(CW\\$1\fR
+.hy 14
+..
+.de L
+.nh
+.if n \%`\\$1'
+.if t \%\&\f(CW\\$1\fR
+.hy 14
+..
+.de LR
+.nh
+.if n \%`\\$1'\\$2
+.if t \%\&\f(CW\\$1\fR\\$2
+.hy 14
+..
+.TH F2C 1
+.CT 1 prog_other
+.SH NAME
+f2c \- Convert Fortran 77 to C or C++
+. \" f\^2c changed to f2c in the previous line for the benefit of
+. \" people on systems (e.g. Sun systems) whose makewhatis cannot
+. \" cope with troff formatting commands.
+.SH SYNOPSIS
+.B f\^2c
+[
+.I option ...
+]
+.I file ...
+.SH DESCRIPTION
+.I F2c
+converts Fortran 77 source code in
+.I files
+with names ending in
+.L .f
+or
+.L .F
+to C (or C++) source files in the
+current directory, with
+.L .c
+substituted
+for the final
+.L .f
+or
+.LR .F .
+If no Fortran files are named,
+.I f\^2c
+reads Fortran from standard input and
+writes C on standard output.
+.I File
+names that end with
+.L .p
+or
+.L .P
+are taken to be prototype
+files, as produced by option
+.LR -P ,
+and are read first.
+.PP
+The following options have the same meaning as in
+.IR f\^77 (1).
+.TP
+.B -C
+Compile code to check that subscripts are within declared array bounds.
+.TP
+.B -I2
+Render INTEGER and LOGICAL as short,
+INTEGER\(**4 as long int. Assume the default \fIlibF77\fR
+and \fIlibI77\fR: allow only INTEGER\(**4 (and no LOGICAL)
+variables in INQUIREs. Option
+.L -I4
+confirms the default rendering of INTEGER as long int.
+.TP
+.BI -I dir
+Look for a non-absolute include file first in the directory of the
+current input file, then in directories specified by \f(CW-I\fP
+options (one directory per option). Options
+\f(CW-I2\fP and \f(CW-I4\fP
+have precedence, so, e.g., a directory named \f(CW2\fP
+should be specified by \f(CW-I./2\fP .
+.TP
+.B -onetrip
+Compile DO loops that are performed at least once if reached.
+(Fortran 77 DO loops are not performed at all if the upper limit is smaller than the lower limit.)
+.TP
+.B -U
+Honor the case of variable and external names. Fortran keywords must be in
+.I
+lower
+case.
+.TP
+.B -u
+Make the default type of a variable `undefined' rather than using the default Fortran rules.
+.TP
+.B -w
+Suppress all warning messages, or, if the option is
+.LR -w66 ,
+just Fortran 66 compatibility warnings.
+.PP
+The following options are peculiar to
+.IR f\^2c .
+.TP
+.B -A
+Produce
+.SM ANSI
+C.
+Default is old-style C.
+.TP
+.B -a
+Make local variables automatic rather than static
+unless they appear in a
+.SM "DATA, EQUIVALENCE, NAMELIST,"
+or
+.SM SAVE
+statement.
+.TP
+.B -C++
+Output C++ code.
+.TP
+.B -c
+Include original Fortran source as comments.
+.TP
+.BI -d dir
+Write
+.L .c
+files in directory
+.I dir
+instead of the current directory.
+.TP
+.B -E
+Declare uninitialized
+.SM COMMON
+to be
+.B Extern
+(overridably defined in
+.F f2c.h
+as
+.B extern).
+.TP
+.B -ec
+Place uninitialized
+.SM COMMON
+blocks in separate files:
+.B COMMON /ABC/
+appears in file
+.BR abc_com.c .
+Option
+.LR -e1c
+bundles the separate files
+into the output file, with comments that give an unbundling
+.IR sed (1)
+script.
+.TP
+.B -ext
+Complain about
+.IR f\^77 (1)
+extensions.
+.TP
+.B -f
+Assume free-format input: accept text after column 72 and do not
+pad fixed-format lines shorter than 72 characters with blanks.
+.TP
+.B -72
+Treat text appearing after column 72 as an error.
+.TP
+.B -g
+Include original Fortran line numbers in \f(CW#line\fR lines.
+.TP
+.B -h
+Emulate Fortran 66's treatment of Hollerith: try to align character strings on
+word (or, if the option is
+.LR -hd ,
+on double-word) boundaries.
+.TP
+.B -i2
+Similar to
+.BR -I2 ,
+but assume a modified
+.I libF77
+and
+.I libI77
+(compiled with
+.BR -Df\^2c_i2 ),
+so
+.SM INTEGER
+and
+.SM LOGICAL
+variables may be assigned by
+.SM INQUIRE
+and array lengths are stored in short ints.
+.TP
+.B -kr
+Use temporary values to enforce Fortran expression evaluation
+where K&R (first edition) parenthesization rules allow rearrangement.
+If the option is
+.LR -krd ,
+use double precision temporaries even for single-precision operands.
+.TP
+.B -P
+Write a
+.IB file .P
+of ANSI (or C++) prototypes
+for definitions in each input
+.IB file .f
+or
+.IB file .F .
+When reading Fortran from standard input, write prototypes
+at the beginning of standard output. Option
+.B -Ps
+implies
+.B -P
+and gives exit status 4 if rerunning
+.I f\^2c
+may change prototypes or declarations.
+.TP
+.B -p
+Supply preprocessor definitions to make common-block members
+look like local variables.
+.TP
+.B -R
+Do not promote
+.SM REAL
+functions and operations to
+.SM DOUBLE PRECISION.
+Option
+.L -!R
+confirms the default, which imitates
+.IR f\^77 .
+.TP
+.B -r
+Cast values of REAL functions (including intrinsics) to REAL.
+.TP
+.B -r8
+Promote
+.SM REAL
+to
+.SM DOUBLE PRECISION, COMPLEX
+to
+.SM DOUBLE COMPLEX.
+.TP
+.B -s
+Preserve multidimensional subscripts. Suppressed by option
+.L -C
+\&.
+.TP
+.BI -T dir
+Put temporary files in directory
+.I dir.
+.TP
+.B -w8
+Suppress warnings when
+.SM COMMON
+or
+.SM EQUIVALENCE
+forces odd-word alignment of doubles.
+.TP
+.BI -W n
+Assume
+.I n
+characters/word (default 4)
+when initializing numeric variables with character data.
+.TP
+.B -z
+Do not implicitly recognize
+.SM DOUBLE COMPLEX.
+.TP
+.B -!bs
+Do not recognize \fIb\fRack\fIs\fRlash escapes
+(\e", \e', \e0, \e\e, \eb, \ef, \en, \er, \et, \ev) in character strings.
+.TP
+.B -!c
+Inhibit C output, but produce
+.B -P
+output.
+.TP
+.B -!I
+Reject
+.B include
+statements.
+.TP
+.B -!i8
+Disallow
+.SM INTEGER*8.
+.TP
+.B -!it
+Don't infer types of untyped
+.SM EXTERNAL
+procedures from use as parameters to previously defined or prototyped
+procedures.
+.TP
+.B -!P
+Do not attempt to infer
+.SM ANSI
+or C++
+prototypes from usage.
+.PP
+The resulting C invokes the support routines of
+.IR f\^77 ;
+object code should be loaded by
+.I f\^77
+or with
+.IR ld (1)
+or
+.IR cc (1)
+options
+.BR "-lF77 -lI77 -lm" .
+Calling conventions
+are those of
+.IR f\&77 :
+see the reference below.
+.br
+.SH FILES
+.TP
+.nr )I 1.75i
+.IB file .[fF]
+input file
+.TP
+.B *.c
+output file
+.TP
+.F /usr/include/f2c.h
+header file
+.TP
+.F /usr/lib/libF77.a
+intrinsic function library
+.TP
+.F /usr/lib/libI77.a
+Fortran I/O library
+.TP
+.F /lib/libc.a
+C library, see section 3
+.SH "SEE ALSO"
+S. I. Feldman and
+P. J. Weinberger,
+`A Portable Fortran 77 Compiler',
+\fIUNIX Time Sharing System Programmer's Manual\fR,
+Tenth Edition, Volume 2, AT&T Bell Laboratories, 1990.
+.SH DIAGNOSTICS
+The diagnostics produced by
+.I f\^2c
+are intended to be
+self-explanatory.
+.SH BUGS
+Floating-point constant expressions are simplified in
+the floating-point arithmetic of the machine running
+.IR f\^2c ,
+so they are typically accurate to at most 16 or 17 decimal places.
+.br
+Untypable
+.SM EXTERNAL
+functions are declared
+.BR int .
diff --git a/usr.bin/f2c/f2c.1t b/usr.bin/f2c/f2c.1t
new file mode 100644
index 0000000..2a59dff
--- /dev/null
+++ b/usr.bin/f2c/f2c.1t
@@ -0,0 +1,336 @@
+. \" Definitions of F, L and LR for the benefit of systems
+. \" whose -man lacks them...
+.de F
+.nh
+.if n \%\&\\$1
+.if t \%\&\f(CW\\$1\fR
+.hy 14
+..
+.de L
+.nh
+.if n \%`\\$1'
+.if t \%\&\f(CW\\$1\fR
+.hy 14
+..
+.de LR
+.nh
+.if n \%`\\$1'\\$2
+.if t \%\&\f(CW\\$1\fR\\$2
+.hy 14
+..
+.TH F2C 1
+.CT 1 prog_other
+.SH NAME
+f\^2c \(mi Convert Fortran 77 to C or C++
+.SH SYNOPSIS
+.B f\^2c
+[
+.I option ...
+]
+.I file ...
+.SH DESCRIPTION
+.I F2c
+converts Fortran 77 source code in
+.I files
+with names ending in
+.L .f
+or
+.L .F
+to C (or C++) source files in the
+current directory, with
+.L .c
+substituted
+for the final
+.L .f
+or
+.LR .F .
+If no Fortran files are named,
+.I f\^2c
+reads Fortran from standard input and
+writes C on standard output.
+.I File
+names that end with
+.L .p
+or
+.L .P
+are taken to be prototype
+files, as produced by option
+.LR -P ,
+and are read first.
+.PP
+The following options have the same meaning as in
+.IR f\^77 (1).
+.TP
+.B -C
+Compile code to check that subscripts are within declared array bounds.
+.TP
+.B -I2
+Render INTEGER and LOGICAL as short,
+INTEGER\(**4 as long int. Assume the default \fIlibF77\fR
+and \fIlibI77\fR: allow only INTEGER\(**4 (and no LOGICAL)
+variables in INQUIREs. Option
+.L -I4
+confirms the default rendering of INTEGER as long int.
+.TP
+.B -onetrip
+Compile DO loops that are performed at least once if reached.
+(Fortran 77 DO loops are not performed at all if the upper limit is smaller than the lower limit.)
+.TP
+.B -U
+Honor the case of variable and external names. Fortran keywords must be in
+.I
+lower
+case.
+.TP
+.B -u
+Make the default type of a variable `undefined' rather than using the default Fortran rules.
+.TP
+.B -w
+Suppress all warning messages.
+If the option is
+.LR -w66 ,
+only Fortran 66 compatibility warnings are suppressed.
+.PP
+The following options are peculiar to
+.IR f\^2c .
+.TP
+.B -A
+Produce
+.SM ANSI
+C.
+Default is old-style C.
+.TP
+.B -a
+Make local variables automatic rather than static
+unless they appear in a
+.SM "DATA, EQUIVALENCE, NAMELIST,"
+or
+.SM SAVE
+statement.
+.TP
+.B -C++
+Output C++ code.
+.TP
+.B -c
+Include original Fortran source as comments.
+.TP
+.B -E
+Declare uninitialized
+.SM COMMON
+to be
+.B Extern
+(overridably defined in
+.F f2c.h
+as
+.B extern).
+.TP
+.B -ec
+Place uninitialized
+.SM COMMON
+blocks in separate files:
+.B COMMON /ABC/
+appears in file
+.BR abc_com.c .
+Option
+.LR -e1c
+bundles the separate files
+into the output file, with comments that give an unbundling
+.IR sed (1)
+script.
+.TP
+.B -ext
+Complain about
+.IR f\^77 (1)
+extensions.
+.TP
+.B -f
+Assume free-format input: accept text after column 72 and do not
+pad fixed-format lines shorter than 72 characters with blanks.
+.TP
+.B -72
+Treat text appearing after column 72 as an error.
+.TP
+.B -g
+Include original Fortran line numbers in \f(CW#line\fR lines.
+.TP
+.B -h
+Emulate Fortran 66's treatment of Hollerith: try to align character strings on
+word (or, if the option is
+.LR -hd ,
+on double-word) boundaries.
+.TP
+.B -i2
+Similar to
+.BR -I2 ,
+but assume a modified
+.I libF77
+and
+.I libI77
+(compiled with
+.BR -Df\^2c_i2 ),
+so
+.SM INTEGER
+and
+.SM LOGICAL
+variables may be assigned by
+.SM INQUIRE
+and array lengths are stored in short ints.
+.TP
+.B -kr
+Use temporary values to enforce Fortran expression evaluation
+where K&R (first edition) parenthesization rules allow rearrangement.
+If the option is
+.LR -krd ,
+use double precision temporaries even for single-precision operands.
+.TP
+.B -P
+Write a
+.IB file .P
+of ANSI (or C++) prototypes
+for definitions in each input
+.IB file .f
+or
+.IB file .F .
+When reading Fortran from standard input, write prototypes
+at the beginning of standard output. Option
+.B -Ps
+implies
+.B -P
+and gives exit status 4 if rerunning
+.I f\^2c
+may change prototypes or declarations.
+.TP
+.B -p
+Supply preprocessor definitions to make common-block members
+look like local variables.
+.TP
+.B -R
+Do not promote
+.SM REAL
+functions and operations to
+.SM DOUBLE PRECISION.
+Option
+.L -!R
+confirms the default, which imitates
+.IR f\^77 .
+.TP
+.B -r
+Cast values of REAL functions (including intrinsics) to REAL.
+.TP
+.B -r8
+Promote
+.SM REAL
+to
+.SM DOUBLE PRECISION, COMPLEX
+to
+.SM DOUBLE COMPLEX.
+.TP
+.B -s
+Preserve multidimensional subscripts.
+.TP
+.BI -T dir
+Put temporary files in directory
+.I dir.
+.TP
+.B -w8
+Suppress warnings when
+.SM COMMON
+or
+.SM EQUIVALENCE
+forces odd-word alignment of doubles.
+.TP
+.BI -W n
+Assume
+.I n
+characters/word (default 4)
+when initializing numeric variables with character data.
+.TP
+.B -z
+Do not implicitly recognize
+.SM DOUBLE COMPLEX.
+.TP
+.B -!bs
+Do not recognize \fIb\fRack\fIs\fRlash escapes
+(\e", \e', \e0, \e\e, \eb, \ef, \en, \er, \et, \ev) in character strings.
+.TP
+.B -!c
+Inhibit C output, but produce
+.B -P
+output.
+.TP
+.B -!I
+Reject
+.B include
+statements.
+.TP
+.B -!i8
+Disallow
+.SM INTEGER*8.
+.TP
+.B -!it
+Don't infer types of untyped
+.SM EXTERNAL
+procedures from use as parameters to previously defined or prototyped
+procedures.
+.TP
+.B -!P
+Do not attempt to infer
+.SM ANSI
+or C++
+prototypes from usage.
+.PP
+The resulting C invokes the support routines of
+.IR f\^77 ;
+object code should be loaded by
+.I f\^77
+or with
+.IR ld (1)
+or
+.IR cc (1)
+options
+.BR "-lF77 -lI77 -lm" .
+Calling conventions
+are those of
+.IR f\&77 :
+see the reference below.
+.br
+.SH FILES
+.TP
+.IB file .[fF]
+input file
+.TP
+.B *.c
+output file
+.TP
+.F /usr/include/f2c.h
+header file
+.TP
+.F /usr/lib/libF77.a
+intrinsic function library
+.TP
+.F /usr/lib/libI77.a
+Fortran I/O library
+.TP
+.F /lib/libc.a
+C library, see section 3
+.SH "SEE ALSO"
+S. I. Feldman and
+P. J. Weinberger,
+`A Portable Fortran 77 Compiler',
+\fIUNIX Time Sharing System Programmer's Manual\fR,
+Tenth Edition, Volume 2, AT&T Bell Laboratories, 1990.
+.SH DIAGNOSTICS
+The diagnostics produced by
+.I f\^2c
+are intended to be
+self-explanatory.
+.SH BUGS
+Floating-point constant expressions are simplified in
+the floating-point arithmetic of the machine running
+.IR f\^2c ,
+so they are typically accurate to at most 16 or 17 decimal places.
+.br
+Untypable
+.SM EXTERNAL
+functions are declared
+.BR int .
diff --git a/usr.bin/f2c/f2c.h b/usr.bin/f2c/f2c.h
new file mode 100644
index 0000000..8f18f6c
--- /dev/null
+++ b/usr.bin/f2c/f2c.h
@@ -0,0 +1,214 @@
+/* f2c.h -- Standard Fortran to C header file */
+
+/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed."
+
+ - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */
+
+#ifndef F2C_INCLUDE
+#define F2C_INCLUDE
+
+typedef long int integer;
+typedef char *address;
+typedef short int shortint;
+typedef float real;
+typedef double doublereal;
+typedef struct { real r, i; } complex;
+typedef struct { doublereal r, i; } doublecomplex;
+typedef long int logical;
+typedef short int shortlogical;
+typedef char logical1;
+typedef char integer1;
+/* typedef long long longint; */ /* system-dependent */
+
+#define TRUE_ (1)
+#define FALSE_ (0)
+
+/* Extern is for use with -E */
+#ifndef Extern
+#define Extern extern
+#endif
+
+/* I/O stuff */
+
+#ifdef f2c_i2
+/* for -i2 */
+typedef short flag;
+typedef short ftnlen;
+typedef short ftnint;
+#else
+typedef long int flag;
+typedef long int ftnlen;
+typedef long int ftnint;
+#endif
+
+/*external read, write*/
+typedef struct
+{ flag cierr;
+ ftnint ciunit;
+ flag ciend;
+ char *cifmt;
+ ftnint cirec;
+} cilist;
+
+/*internal read, write*/
+typedef struct
+{ flag icierr;
+ char *iciunit;
+ flag iciend;
+ char *icifmt;
+ ftnint icirlen;
+ ftnint icirnum;
+} icilist;
+
+/*open*/
+typedef struct
+{ flag oerr;
+ ftnint ounit;
+ char *ofnm;
+ ftnlen ofnmlen;
+ char *osta;
+ char *oacc;
+ char *ofm;
+ ftnint orl;
+ char *oblnk;
+} olist;
+
+/*close*/
+typedef struct
+{ flag cerr;
+ ftnint cunit;
+ char *csta;
+} cllist;
+
+/*rewind, backspace, endfile*/
+typedef struct
+{ flag aerr;
+ ftnint aunit;
+} alist;
+
+/* inquire */
+typedef struct
+{ flag inerr;
+ ftnint inunit;
+ char *infile;
+ ftnlen infilen;
+ ftnint *inex; /*parameters in standard's order*/
+ ftnint *inopen;
+ ftnint *innum;
+ ftnint *innamed;
+ char *inname;
+ ftnlen innamlen;
+ char *inacc;
+ ftnlen inacclen;
+ char *inseq;
+ ftnlen inseqlen;
+ char *indir;
+ ftnlen indirlen;
+ char *infmt;
+ ftnlen infmtlen;
+ char *inform;
+ ftnint informlen;
+ char *inunf;
+ ftnlen inunflen;
+ ftnint *inrecl;
+ ftnint *innrec;
+ char *inblank;
+ ftnlen inblanklen;
+} inlist;
+
+#define VOID void
+
+union Multitype { /* for multiple entry points */
+ integer1 g;
+ shortint h;
+ integer i;
+ /* longint j; */
+ real r;
+ doublereal d;
+ complex c;
+ doublecomplex z;
+ };
+
+typedef union Multitype Multitype;
+
+/*typedef long int Long;*/ /* No longer used; formerly in Namelist */
+
+struct Vardesc { /* for Namelist */
+ char *name;
+ char *addr;
+ ftnlen *dims;
+ int type;
+ };
+typedef struct Vardesc Vardesc;
+
+struct Namelist {
+ char *name;
+ Vardesc **vars;
+ int nvars;
+ };
+typedef struct Namelist Namelist;
+
+#define abs(x) ((x) >= 0 ? (x) : -(x))
+#define dabs(x) (doublereal)abs(x)
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+#define dmin(a,b) (doublereal)min(a,b)
+#define dmax(a,b) (doublereal)max(a,b)
+
+/* procedure parameter types for -A and -C++ */
+
+#define F2C_proc_par_types 1
+#ifdef __cplusplus
+typedef int /* Unknown procedure type */ (*U_fp)(...);
+typedef shortint (*J_fp)(...);
+typedef integer (*I_fp)(...);
+typedef real (*R_fp)(...);
+typedef doublereal (*D_fp)(...), (*E_fp)(...);
+typedef /* Complex */ VOID (*C_fp)(...);
+typedef /* Double Complex */ VOID (*Z_fp)(...);
+typedef logical (*L_fp)(...);
+typedef shortlogical (*K_fp)(...);
+typedef /* Character */ VOID (*H_fp)(...);
+typedef /* Subroutine */ int (*S_fp)(...);
+#else
+typedef int /* Unknown procedure type */ (*U_fp)();
+typedef shortint (*J_fp)();
+typedef integer (*I_fp)();
+typedef real (*R_fp)();
+typedef doublereal (*D_fp)(), (*E_fp)();
+typedef /* Complex */ VOID (*C_fp)();
+typedef /* Double Complex */ VOID (*Z_fp)();
+typedef logical (*L_fp)();
+typedef shortlogical (*K_fp)();
+typedef /* Character */ VOID (*H_fp)();
+typedef /* Subroutine */ int (*S_fp)();
+#endif
+/* E_fp is for real functions when -R is not specified */
+typedef VOID C_f; /* complex function */
+typedef VOID H_f; /* character function */
+typedef VOID Z_f; /* double complex function */
+typedef doublereal E_f; /* real function with -R not specified */
+
+/* undef any lower-case symbols that your C compiler predefines, e.g.: */
+
+#ifndef Skip_f2c_Undefs
+#undef cray
+#undef gcos
+#undef mc68010
+#undef mc68020
+#undef mips
+#undef pdp11
+#undef sgi
+#undef sparc
+#undef sun
+#undef sun2
+#undef sun3
+#undef sun4
+#undef u370
+#undef u3b
+#undef u3b2
+#undef u3b5
+#undef unix
+#undef vax
+#endif
+#endif
diff --git a/usr.bin/f2c/f77.script b/usr.bin/f2c/f77.script
new file mode 100644
index 0000000..d98bd75
--- /dev/null
+++ b/usr.bin/f2c/f77.script
@@ -0,0 +1,114 @@
+#!/bin/sh
+# Compile Fortran code, adding -lf2c.
+# This is a slightly modified g++ script.
+
+: || exec /bin/sh -f $0 $argv:q
+
+# The compiler name might be different when doing cross-compilation
+# (this should be configured)
+gcc_name=cc
+speclang=-xnone
+
+# replace the command name by the name of the new command
+progname=`basename $0`
+case "$0" in
+ */*)
+ gcc=`echo $0 | sed -e "s;/[^/]*$;;"`/$gcc_name
+ ;;
+ *)
+ gcc=$gcc_name
+ ;;
+esac
+
+# $first is yes for first arg, no afterwards.
+first=yes
+# If next arg is the argument of an option, $quote is non-empty.
+# More precisely, it is the option that wants an argument.
+quote=
+# $library is made empty to disable use of libf2c.
+#library=-lF77 -lI77 -lm
+library="-lf2c -lm"
+numargs=$#
+
+# ash requires the newline before `do'.
+for arg
+do
+ if [ $first = yes ]
+ then
+ # Need some 1st arg to `set' which does not begin with `-'.
+ # We get rid of it after the loop ends.
+ set gcc
+ first=no
+ fi
+ # If you have to ask what this does, you should not edit this file. :-)
+ # The ``S'' at the start is so that echo -nostdinc does not eat the
+ # -nostdinc.
+ arg=`echo "S$arg" | sed "s/^S//; s/'/'\\\\\\\\''/g"`
+ if [ x$quote != x ]
+ then
+ quote=
+ else
+ quote=
+ case $arg in
+ -nostdlib)
+ # Inhibit linking with -lf2c.
+ library=
+ ;;
+ -lm | -lmath)
+ # Because libf2c uses things from the math library, make sure it
+ # always comes before the math library. We recognize both -lm
+ # and -lmath, since on some systems (e.g. m88k SVR3), it
+ # doesn't call it libm.a for some reason.
+ #set "$@" $library
+ #library=""
+ ;;
+ -[bBVDUoeTuIYmLiA] | -Tdata)
+ # these switches take following word as argument,
+ # so don't treat it as a file name.
+ quote=$arg
+ ;;
+ -[cSEM] | -MM)
+ # Don't specify libraries if we won't link,
+ # since that would cause a warning.
+ library=
+ ;;
+ -x*)
+ speclang=$arg
+ ;;
+ -v)
+ # catch `f77 -v'
+ if [ $numargs = 1 ] ; then library="" ; fi
+ ;;
+ -*)
+ # Pass other options through; they don't need -x and aren't inputs.
+ ;;
+ *)
+ # If file ends in .i, put options around it.
+ # But not if a specified -x option is currently active.
+ case "$speclang $arg" in -xnone\ *.i)
+ set "$@" -xf2c "'$arg'" -xnone
+ continue
+ esac
+ ;;
+ esac
+ fi
+ set "$@" "'$arg'"
+done
+
+# Get rid of that initial 1st arg
+if [ $first = no ]; then
+ shift
+else
+ echo "$0: No input files specified."
+ exit 1
+fi
+
+if [ x$quote != x ]
+then
+ echo "$0: argument to \`$quote' missing"
+ exit 1
+fi
+
+eval $gcc "$@" $library
+
+
diff --git a/usr.bin/f2c/format.c b/usr.bin/f2c/format.c
new file mode 100644
index 0000000..10aa39d
--- /dev/null
+++ b/usr.bin/f2c/format.c
@@ -0,0 +1,2517 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* Format.c -- this file takes an intermediate file (generated by pass 1
+ of the translator) and some state information about the contents of that
+ file, and generates C program text. */
+
+#include "defs.h"
+#include "p1defs.h"
+#include "format.h"
+#include "output.h"
+#include "names.h"
+#include "iob.h"
+
+int c_output_line_length = DEF_C_LINE_LENGTH;
+
+int last_was_label; /* Boolean used to generate semicolons
+ when a label terminates a block */
+static char this_proc_name[52]; /* Name of the current procedure. This is
+ probably too simplistic to handle
+ multiple entry points */
+
+static tagptr do_format Argdcl((FILEP, FILEP));
+static void do_p1_1while Argdcl((FILEP));
+static void do_p1_2while Argdcl((FILEP, FILEP));
+static tagptr do_p1_addr Argdcl((FILEP, FILEP));
+static void do_p1_asgoto Argdcl((FILEP, FILEP));
+static tagptr do_p1_charp Argdcl((FILEP));
+static void do_p1_comment Argdcl((FILEP, FILEP));
+static void do_p1_comp_goto Argdcl((FILEP, FILEP));
+static tagptr do_p1_const Argdcl((FILEP));
+static void do_p1_elif Argdcl((FILEP, FILEP));
+static void do_p1_else Argdcl((FILEP));
+static void do_p1_elseifstart Argdcl((FILEP));
+static void do_p1_end_for Argdcl((FILEP));
+static void do_p1_endelse Argdcl((FILEP));
+static void do_p1_endif Argdcl((FILEP));
+static tagptr do_p1_expr Argdcl((FILEP, FILEP));
+static tagptr do_p1_extern Argdcl((FILEP));
+static void do_p1_for Argdcl((FILEP, FILEP));
+static void do_p1_fortran Argdcl((FILEP, FILEP));
+static void do_p1_goto Argdcl((FILEP, FILEP));
+static tagptr do_p1_head Argdcl((FILEP, FILEP));
+static tagptr do_p1_ident Argdcl((FILEP));
+static void do_p1_if Argdcl((FILEP, FILEP));
+static void do_p1_label Argdcl((FILEP, FILEP));
+static tagptr do_p1_list Argdcl((FILEP, FILEP));
+static tagptr do_p1_literal Argdcl((FILEP));
+static tagptr do_p1_name_pointer Argdcl((FILEP));
+static void do_p1_set_line Argdcl((FILEP));
+static void do_p1_subr_ret Argdcl((FILEP, FILEP));
+static int get_p1_token Argdcl((FILEP));
+static int p1get_const Argdcl((FILEP, int, Constp*));
+static int p1getd Argdcl((FILEP, long int*));
+static int p1getf Argdcl((FILEP, char**));
+static int p1getn Argdcl((FILEP, int, char**));
+static int p1gets Argdcl((FILEP, char*, int));
+static void proto Argdcl((FILEP, Argtypes*, char*));
+
+extern chainp assigned_fmts;
+char filename[P1_FILENAME_MAX];
+extern int gflag, sharp_line;
+int gflag1;
+extern char *parens;
+
+ void
+start_formatting(Void)
+{
+ FILE *infile;
+ static int wrote_one = 0;
+ extern int usedefsforcommon;
+ extern char *p1_file, *p1_bakfile;
+
+ this_proc_name[0] = '\0';
+ last_was_label = 0;
+ ei_next = ei_first;
+ wh_next = wh_first;
+
+ (void) fclose (pass1_file);
+ if ((infile = fopen (p1_file, binread)) == NULL)
+ Fatal("start_formatting: couldn't open the intermediate file\n");
+
+ if (wrote_one)
+ nice_printf (c_file, "\n");
+
+ while (!feof (infile)) {
+ expptr this_expr;
+
+ this_expr = do_format (infile, c_file);
+ if (this_expr) {
+ out_and_free_statement (c_file, this_expr);
+ } /* if this_expr */
+ } /* while !feof infile */
+
+ (void) fclose (infile);
+
+ if (last_was_label)
+ nice_printf (c_file, ";\n");
+
+ prev_tab (c_file);
+ gflag1 = sharp_line = 0;
+ if (this_proc_name[0])
+ nice_printf (c_file, "} /* %s */\n", this_proc_name);
+
+
+/* Write the #undefs for common variable reference */
+
+ if (usedefsforcommon) {
+ Extsym *ext;
+ int did_one = 0;
+
+ for (ext = extsymtab; ext < nextext; ext++)
+ if (ext -> extstg == STGCOMMON && ext -> used_here) {
+ ext -> used_here = 0;
+ if (!did_one)
+ nice_printf (c_file, "\n");
+ wr_abbrevs(c_file, 0, ext->extp);
+ did_one = 1;
+ ext -> extp = CHNULL;
+ } /* if */
+
+ if (did_one)
+ nice_printf (c_file, "\n");
+ } /* if usedefsforcommon */
+
+ other_undefs(c_file);
+
+ wrote_one = 1;
+
+/* For debugging only */
+
+ if (debugflag && (pass1_file = fopen (p1_bakfile, binwrite)))
+ if (infile = fopen (p1_file, binread)) {
+ ffilecopy (infile, pass1_file);
+ fclose (infile);
+ fclose (pass1_file);
+ } /* if infile */
+
+/* End of "debugging only" */
+
+ scrub(p1_file); /* optionally unlink */
+
+ if ((pass1_file = fopen (p1_file, binwrite)) == NULL)
+ err ("start_formatting: couldn't reopen the pass1 file");
+
+} /* start_formatting */
+
+
+ static void
+#ifdef KR_headers
+put_semi(outfile)
+ FILE *outfile;
+#else
+put_semi(FILE *outfile)
+#endif
+{
+ nice_printf (outfile, ";\n");
+ last_was_label = 0;
+ }
+
+#define SEM_CHECK(x) if (last_was_label) put_semi(x)
+
+/* do_format -- takes an input stream (a file in pass1 format) and writes
+ the appropriate C code to outfile when possible. When reading an
+ expression, the expression tree is returned instead. */
+
+ static expptr
+#ifdef KR_headers
+do_format(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_format(FILE *infile, FILE *outfile)
+#endif
+{
+ int token_type, was_c_token;
+ expptr retval = ENULL;
+
+ token_type = get_p1_token (infile);
+ was_c_token = 1;
+ switch (token_type) {
+ case P1_COMMENT:
+ do_p1_comment (infile, outfile);
+ was_c_token = 0;
+ break;
+ case P1_SET_LINE:
+ do_p1_set_line (infile);
+ was_c_token = 0;
+ break;
+ case P1_FILENAME:
+ p1gets(infile, filename, P1_FILENAME_MAX);
+ was_c_token = 0;
+ break;
+ case P1_NAME_POINTER:
+ retval = do_p1_name_pointer (infile);
+ break;
+ case P1_CONST:
+ retval = do_p1_const (infile);
+ break;
+ case P1_EXPR:
+ retval = do_p1_expr (infile, outfile);
+ break;
+ case P1_IDENT:
+ retval = do_p1_ident(infile);
+ break;
+ case P1_CHARP:
+ retval = do_p1_charp(infile);
+ break;
+ case P1_EXTERN:
+ retval = do_p1_extern (infile);
+ break;
+ case P1_HEAD:
+ gflag1 = sharp_line = 0;
+ retval = do_p1_head (infile, outfile);
+ gflag1 = sharp_line = gflag;
+ break;
+ case P1_LIST:
+ retval = do_p1_list (infile, outfile);
+ break;
+ case P1_LITERAL:
+ retval = do_p1_literal (infile);
+ break;
+ case P1_LABEL:
+ do_p1_label (infile, outfile);
+ /* last_was_label = 1; -- now set in do_p1_label */
+ was_c_token = 0;
+ break;
+ case P1_ASGOTO:
+ do_p1_asgoto (infile, outfile);
+ break;
+ case P1_GOTO:
+ do_p1_goto (infile, outfile);
+ break;
+ case P1_IF:
+ do_p1_if (infile, outfile);
+ break;
+ case P1_ELSE:
+ SEM_CHECK(outfile);
+ do_p1_else (outfile);
+ break;
+ case P1_ELIF:
+ SEM_CHECK(outfile);
+ do_p1_elif (infile, outfile);
+ break;
+ case P1_ENDIF:
+ SEM_CHECK(outfile);
+ do_p1_endif (outfile);
+ break;
+ case P1_ENDELSE:
+ SEM_CHECK(outfile);
+ do_p1_endelse (outfile);
+ break;
+ case P1_ADDR:
+ retval = do_p1_addr (infile, outfile);
+ break;
+ case P1_SUBR_RET:
+ do_p1_subr_ret (infile, outfile);
+ break;
+ case P1_COMP_GOTO:
+ do_p1_comp_goto (infile, outfile);
+ break;
+ case P1_FOR:
+ do_p1_for (infile, outfile);
+ break;
+ case P1_ENDFOR:
+ SEM_CHECK(outfile);
+ do_p1_end_for (outfile);
+ break;
+ case P1_WHILE1START:
+ do_p1_1while(outfile);
+ break;
+ case P1_WHILE2START:
+ do_p1_2while(infile, outfile);
+ break;
+ case P1_PROCODE:
+ procode(outfile);
+ break;
+ case P1_ELSEIFSTART:
+ SEM_CHECK(outfile);
+ do_p1_elseifstart(outfile);
+ break;
+ case P1_FORTRAN:
+ do_p1_fortran(infile, outfile);
+ /* no break; */
+ case P1_EOF:
+ was_c_token = 0;
+ break;
+ case P1_UNKNOWN:
+ Fatal("do_format: Unknown token type in intermediate file");
+ break;
+ default:
+ Fatal("do_format: Bad token type in intermediate file");
+ break;
+ } /* switch */
+
+ if (was_c_token)
+ last_was_label = 0;
+ return retval;
+} /* do_format */
+
+
+ static void
+#ifdef KR_headers
+do_p1_comment(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_comment(FILE *infile, FILE *outfile)
+#endif
+{
+ extern int c_output_line_length, in_comment;
+
+ char storage[COMMENT_BUFFER_SIZE + 1];
+ int length;
+
+ if (!p1gets(infile, storage, COMMENT_BUFFER_SIZE + 1))
+ return;
+
+ length = strlen (storage);
+
+ gflag1 = sharp_line = 0;
+ in_comment = 1;
+ if (length > c_output_line_length - 6)
+ margin_printf(outfile, "/*%s*/\n", storage);
+ else
+ margin_printf(outfile, length ? "/* %s */\n" : "\n", storage);
+ in_comment = 0;
+ gflag1 = sharp_line = gflag;
+} /* do_p1_comment */
+
+ static void
+#ifdef KR_headers
+do_p1_set_line(infile)
+ FILE *infile;
+#else
+do_p1_set_line(FILE *infile)
+#endif
+{
+ int status;
+ long new_line_number = -1;
+
+ status = p1getd (infile, &new_line_number);
+
+ if (status == EOF)
+ err ("do_p1_set_line: Missing line number at end of file\n");
+ else if (status == 0 || new_line_number == -1)
+ errl("do_p1_set_line: Illegal line number in intermediate file: %ld\n",
+ new_line_number);
+ else {
+ lineno = new_line_number;
+ }
+} /* do_p1_set_line */
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_name_pointer(infile)
+ FILE *infile;
+#else
+do_p1_name_pointer(FILE *infile)
+#endif
+{
+ Namep namep = (Namep) NULL;
+ int status;
+
+ status = p1getd (infile, (long *) &namep);
+
+ if (status == EOF)
+ err ("do_p1_name_pointer: Missing pointer at end of file\n");
+ else if (status == 0 || namep == (Namep) NULL)
+ erri ("do_p1_name_pointer: Illegal name pointer in p1 file: '%x'\n",
+ (int) namep);
+
+ return (expptr) namep;
+} /* do_p1_name_pointer */
+
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_const(infile)
+ FILE *infile;
+#else
+do_p1_const(FILE *infile)
+#endif
+{
+ struct Constblock *c = (struct Constblock *) NULL;
+ long type = -1;
+ int status;
+
+ status = p1getd (infile, &type);
+
+ if (status == EOF)
+ err ("do_p1_const: Missing constant type at end of file\n");
+ else if (status == 0)
+ errl("do_p1_const: Illegal constant type in p1 file: %ld\n", type);
+ else {
+ status = p1get_const (infile, (int)type, &c);
+
+ if (status == EOF) {
+ err ("do_p1_const: Missing constant value at end of file\n");
+ c = (struct Constblock *) NULL;
+ } else if (status == 0) {
+ err ("do_p1_const: Illegal constant value in p1 file\n");
+ c = (struct Constblock *) NULL;
+ } /* else */
+ } /* else */
+ return (expptr) c;
+} /* do_p1_const */
+
+ void
+#ifdef KR_headers
+addrlit(addrp)
+ Addrp addrp;
+#else
+addrlit(Addrp addrp)
+#endif
+{
+ int memno = addrp->memno;
+ struct Literal *litp, *lastlit;
+
+ lastlit = litpool + nliterals;
+ for (litp = litpool; litp < lastlit; litp++)
+ if (litp->litnum == memno) {
+ addrp->vtype = litp->littype;
+ *((union Constant *) &(addrp->user)) =
+ *((union Constant *) &(litp->litval));
+ addrp->vstg = STGMEMNO;
+ return;
+ }
+ err("addrlit failure!");
+ }
+
+ static expptr
+#ifdef KR_headers
+do_p1_literal(infile)
+ FILE *infile;
+#else
+do_p1_literal(FILE *infile)
+#endif
+{
+ int status;
+ long memno;
+ Addrp addrp;
+
+ status = p1getd (infile, &memno);
+
+ if (status == EOF)
+ err ("do_p1_literal: Missing memno at end of file");
+ else if (status == 0)
+ err ("do_p1_literal: Missing memno in p1 file");
+ else {
+ addrp = ALLOC (Addrblock);
+ addrp -> tag = TADDR;
+ addrp -> vtype = TYUNKNOWN;
+ addrp -> Field = NULL;
+ addrp -> memno = memno;
+ addrlit(addrp);
+ addrp -> uname_tag = UNAM_CONST;
+ } /* else */
+
+ return (expptr) addrp;
+} /* do_p1_literal */
+
+
+ static void
+#ifdef KR_headers
+do_p1_label(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_label(FILE *infile, FILE *outfile)
+#endif
+{
+ int status;
+ ftnint stateno;
+ struct Labelblock *L;
+ char *fmt;
+
+ status = p1getd (infile, &stateno);
+
+ if (status == EOF)
+ err ("do_p1_label: Missing label at end of file");
+ else if (status == 0)
+ err ("do_p1_label: Missing label in p1 file ");
+ else if (stateno < 0) { /* entry */
+ margin_printf(outfile, "\n%s:\n", user_label(stateno));
+ last_was_label = 1;
+ }
+ else {
+ L = labeltab + stateno;
+ if (L->labused) {
+ fmt = "%s:\n";
+ last_was_label = 1;
+ }
+ else
+ fmt = "/* %s: */\n";
+ margin_printf(outfile, fmt, user_label(L->stateno));
+ } /* else */
+} /* do_p1_label */
+
+
+
+ static void
+#ifdef KR_headers
+do_p1_asgoto(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_asgoto(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr expr;
+
+ expr = do_format (infile, outfile);
+ out_asgoto (outfile, expr);
+
+} /* do_p1_asgoto */
+
+
+ static void
+#ifdef KR_headers
+do_p1_goto(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_goto(FILE *infile, FILE *outfile)
+#endif
+{
+ int status;
+ long stateno;
+
+ status = p1getd (infile, &stateno);
+
+ if (status == EOF)
+ err ("do_p1_goto: Missing goto label at end of file");
+ else if (status == 0)
+ err ("do_p1_goto: Missing goto label in p1 file");
+ else {
+ nice_printf (outfile, "goto %s;\n", user_label (stateno));
+ } /* else */
+} /* do_p1_goto */
+
+
+ static void
+#ifdef KR_headers
+do_p1_if(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_if(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr cond;
+
+ do {
+ cond = do_format (infile, outfile);
+ } while (cond == ENULL);
+
+ out_if (outfile, cond);
+} /* do_p1_if */
+
+
+ static void
+#ifdef KR_headers
+do_p1_else(outfile)
+ FILE *outfile;
+#else
+do_p1_else(FILE *outfile)
+#endif
+{
+ out_else (outfile);
+} /* do_p1_else */
+
+
+ static void
+#ifdef KR_headers
+do_p1_elif(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_elif(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr cond;
+
+ do {
+ cond = do_format (infile, outfile);
+ } while (cond == ENULL);
+
+ elif_out (outfile, cond);
+} /* do_p1_elif */
+
+ static void
+#ifdef KR_headers
+do_p1_endif(outfile)
+ FILE *outfile;
+#else
+do_p1_endif(FILE *outfile)
+#endif
+{
+ endif_out (outfile);
+} /* do_p1_endif */
+
+
+ static void
+#ifdef KR_headers
+do_p1_endelse(outfile)
+ FILE *outfile;
+#else
+do_p1_endelse(FILE *outfile)
+#endif
+{
+ end_else_out (outfile);
+} /* do_p1_endelse */
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_addr(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_addr(FILE *infile, FILE *outfile)
+#endif
+{
+ Addrp addrp = (Addrp) NULL;
+ int status;
+
+ status = p1getn (infile, (int)sizeof(struct Addrblock), (char **) &addrp);
+
+ if (status == EOF)
+ err ("do_p1_addr: Missing Addrp at end of file");
+ else if (status == 0)
+ err ("do_p1_addr: Missing Addrp in p1 file");
+ else if (addrp == (Addrp) NULL)
+ err ("do_p1_addr: Null addrp in p1 file");
+ else if (addrp -> tag != TADDR)
+ erri ("do_p1_addr: bad tag in p1 file '%d'", addrp -> tag);
+ else {
+ addrp -> vleng = do_format (infile, outfile);
+ addrp -> memoffset = do_format (infile, outfile);
+ }
+
+ return (expptr) addrp;
+} /* do_p1_addr */
+
+
+
+ static void
+#ifdef KR_headers
+do_p1_subr_ret(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_subr_ret(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr retval;
+
+ nice_printf (outfile, "return ");
+ retval = do_format (infile, outfile);
+ if (!multitype)
+ if (retval)
+ expr_out (outfile, retval);
+
+ nice_printf (outfile, ";\n");
+} /* do_p1_subr_ret */
+
+
+
+ static void
+#ifdef KR_headers
+do_p1_comp_goto(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_comp_goto(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr index;
+ expptr labels;
+
+ index = do_format (infile, outfile);
+
+ if (index == ENULL) {
+ err ("do_p1_comp_goto: no expression for computed goto");
+ return;
+ } /* if index == ENULL */
+
+ labels = do_format (infile, outfile);
+
+ if (labels && labels -> tag != TLIST)
+ erri ("do_p1_comp_goto: expected list, got tag '%d'", labels -> tag);
+ else
+ compgoto_out (outfile, index, labels);
+} /* do_p1_comp_goto */
+
+
+ static void
+#ifdef KR_headers
+do_p1_for(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_for(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr init, test, inc;
+
+ init = do_format (infile, outfile);
+ test = do_format (infile, outfile);
+ inc = do_format (infile, outfile);
+
+ out_for (outfile, init, test, inc);
+} /* do_p1_for */
+
+ static void
+#ifdef KR_headers
+do_p1_end_for(outfile)
+ FILE *outfile;
+#else
+do_p1_end_for(FILE *outfile)
+#endif
+{
+ out_end_for (outfile);
+} /* do_p1_end_for */
+
+
+ static void
+#ifdef KR_headers
+do_p1_fortran(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_fortran(FILE *infile, FILE *outfile)
+#endif
+{
+ char buf[P1_STMTBUFSIZE];
+ if (!p1gets(infile, buf, P1_STMTBUFSIZE))
+ return;
+ /* bypass nice_printf nonsense */
+ fprintf(outfile, "/*< %s >*/\n", buf+1); /* + 1 to skip by '$' */
+ }
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_expr(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_expr(FILE *infile, FILE *outfile)
+#endif
+{
+ int status;
+ long opcode, type;
+ struct Exprblock *result = (struct Exprblock *) NULL;
+
+ status = p1getd (infile, &opcode);
+
+ if (status == EOF)
+ err ("do_p1_expr: Missing expr opcode at end of file");
+ else if (status == 0)
+ err ("do_p1_expr: Missing expr opcode in p1 file");
+ else {
+
+ status = p1getd (infile, &type);
+
+ if (status == EOF)
+ err ("do_p1_expr: Missing expr type at end of file");
+ else if (status == 0)
+ err ("do_p1_expr: Missing expr type in p1 file");
+ else if (opcode == 0)
+ return ENULL;
+ else {
+ result = ALLOC (Exprblock);
+
+ result -> tag = TEXPR;
+ result -> vtype = type;
+ result -> opcode = opcode;
+ result -> vleng = do_format (infile, outfile);
+
+ if (is_unary_op (opcode))
+ result -> leftp = do_format (infile, outfile);
+ else if (is_binary_op (opcode)) {
+ result -> leftp = do_format (infile, outfile);
+ result -> rightp = do_format (infile, outfile);
+ } else
+ errl("do_p1_expr: Illegal opcode %ld", opcode);
+ } /* else */
+ } /* else */
+
+ return (expptr) result;
+} /* do_p1_expr */
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_ident(infile)
+ FILE *infile;
+#else
+do_p1_ident(FILE *infile)
+#endif
+{
+ Addrp addrp;
+ int status;
+ long vtype, vstg;
+
+ addrp = ALLOC (Addrblock);
+ addrp -> tag = TADDR;
+
+ status = p1getd (infile, &vtype);
+ if (status == EOF)
+ err ("do_p1_ident: Missing identifier type at end of file\n");
+ else if (status == 0 || vtype < 0 || vtype >= NTYPES)
+ errl("do_p1_ident: Bad type in intermediate file: %ld\n", vtype);
+ else
+ addrp -> vtype = vtype;
+
+ status = p1getd (infile, &vstg);
+ if (status == EOF)
+ err ("do_p1_ident: Missing identifier storage at end of file\n");
+ else if (status == 0 || vstg < 0 || vstg > STGNULL)
+ errl("do_p1_ident: Bad storage in intermediate file: %ld\n", vtype);
+ else
+ addrp -> vstg = vstg;
+
+ status = p1gets(infile, addrp->user.ident, IDENT_LEN);
+
+ if (status == EOF)
+ err ("do_p1_ident: Missing ident string at end of file");
+ else if (status == 0)
+ err ("do_p1_ident: Missing ident string in intermediate file");
+ addrp->uname_tag = UNAM_IDENT;
+ return (expptr) addrp;
+} /* do_p1_ident */
+
+ static expptr
+#ifdef KR_headers
+do_p1_charp(infile)
+ FILE *infile;
+#else
+do_p1_charp(FILE *infile)
+#endif
+{
+ Addrp addrp;
+ int status;
+ long vtype, vstg;
+ char buf[64];
+
+ addrp = ALLOC (Addrblock);
+ addrp -> tag = TADDR;
+
+ status = p1getd (infile, &vtype);
+ if (status == EOF)
+ err ("do_p1_ident: Missing identifier type at end of file\n");
+ else if (status == 0 || vtype < 0 || vtype >= NTYPES)
+ errl("do_p1_ident: Bad type in intermediate file: %ld\n", vtype);
+ else
+ addrp -> vtype = vtype;
+
+ status = p1getd (infile, &vstg);
+ if (status == EOF)
+ err ("do_p1_ident: Missing identifier storage at end of file\n");
+ else if (status == 0 || vstg < 0 || vstg > STGNULL)
+ errl("do_p1_ident: Bad storage in intermediate file: %ld\n", vtype);
+ else
+ addrp -> vstg = vstg;
+
+ status = p1gets(infile, buf, (int)sizeof(buf));
+
+ if (status == EOF)
+ err ("do_p1_ident: Missing charp ident string at end of file");
+ else if (status == 0)
+ err ("do_p1_ident: Missing charp ident string in intermediate file");
+ addrp->uname_tag = UNAM_CHARP;
+ addrp->user.Charp = strcpy(mem(strlen(buf)+1,0), buf);
+ return (expptr) addrp;
+}
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_extern(infile)
+ FILE *infile;
+#else
+do_p1_extern(FILE *infile)
+#endif
+{
+ Addrp addrp;
+
+ addrp = ALLOC (Addrblock);
+ if (addrp) {
+ int status;
+
+ addrp->tag = TADDR;
+ addrp->vstg = STGEXT;
+ addrp->uname_tag = UNAM_EXTERN;
+ status = p1getd (infile, &(addrp -> memno));
+ if (status == EOF)
+ err ("do_p1_extern: Missing memno at end of file");
+ else if (status == 0)
+ err ("do_p1_extern: Missing memno in intermediate file");
+ if (addrp->vtype = extsymtab[addrp->memno].extype)
+ addrp->vclass = CLPROC;
+ } /* if addrp */
+
+ return (expptr) addrp;
+} /* do_p1_extern */
+
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_head(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_head(FILE *infile, FILE *outfile)
+#endif
+{
+ int status;
+ int add_n_;
+ long class;
+ char storage[256];
+
+ status = p1getd (infile, &class);
+ if (status == EOF)
+ err ("do_p1_head: missing header class at end of file");
+ else if (status == 0)
+ err ("do_p1_head: missing header class in p1 file");
+ else {
+ status = p1gets (infile, storage, (int)sizeof(storage));
+ if (status == EOF || status == 0)
+ storage[0] = '\0';
+ } /* else */
+
+ if (class == CLPROC || class == CLMAIN) {
+ chainp lengths;
+
+ add_n_ = nentry > 1;
+ lengths = length_comp(entries, add_n_);
+
+ if (!add_n_ && protofile && class != CLMAIN)
+ protowrite(protofile, proctype, storage, entries, lengths);
+
+ if (class == CLMAIN)
+ nice_printf (outfile, "/* Main program */ ");
+ else
+ nice_printf(outfile, "%s ", multitype ? "VOID"
+ : c_type_decl(proctype, 1));
+
+ nice_printf(outfile, add_n_ ? "%s0_" : "%s", storage);
+ if (!Ansi) {
+ listargs(outfile, entries, add_n_, lengths);
+ nice_printf (outfile, "\n");
+ }
+ list_arg_types (outfile, entries, lengths, add_n_, "\n");
+ nice_printf (outfile, "{\n");
+ frchain(&lengths);
+ next_tab (outfile);
+ strcpy(this_proc_name, storage);
+ list_decls (outfile);
+
+ } else if (class == CLBLOCK)
+ next_tab (outfile);
+ else
+ errl("do_p1_head: got class %ld", class);
+
+ return NULL;
+} /* do_p1_head */
+
+
+ static expptr
+#ifdef KR_headers
+do_p1_list(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_list(FILE *infile, FILE *outfile)
+#endif
+{
+ long tag, type, count;
+ int status;
+ expptr result;
+
+ status = p1getd (infile, &tag);
+ if (status == EOF)
+ err ("do_p1_list: missing list tag at end of file");
+ else if (status == 0)
+ err ("do_p1_list: missing list tag in p1 file");
+ else {
+ status = p1getd (infile, &type);
+ if (status == EOF)
+ err ("do_p1_list: missing list type at end of file");
+ else if (status == 0)
+ err ("do_p1_list: missing list type in p1 file");
+ else {
+ status = p1getd (infile, &count);
+ if (status == EOF)
+ err ("do_p1_list: missing count at end of file");
+ else if (status == 0)
+ err ("do_p1_list: missing count in p1 file");
+ } /* else */
+ } /* else */
+
+ result = (expptr) ALLOC (Listblock);
+ if (result) {
+ chainp pointer;
+
+ result -> tag = tag;
+ result -> listblock.vtype = type;
+
+/* Assume there will be enough data */
+
+ if (count--) {
+ pointer = result->listblock.listp =
+ mkchain((char *)do_format(infile, outfile), CHNULL);
+ while (count--) {
+ pointer -> nextp =
+ mkchain((char *)do_format(infile, outfile), CHNULL);
+ pointer = pointer -> nextp;
+ } /* while (count--) */
+ } /* if (count) */
+ } /* if (result) */
+
+ return result;
+} /* do_p1_list */
+
+
+ chainp
+#ifdef KR_headers
+length_comp(e, add_n)
+ struct Entrypoint *e;
+ int add_n;
+#else
+length_comp(struct Entrypoint *e, int add_n)
+#endif
+ /* get lengths of characters args */
+{
+ chainp lengths;
+ chainp args, args1;
+ Namep arg, np;
+ int nchargs;
+ Argtypes *at;
+ Atype *a;
+ extern int init_ac[TYSUBR+1];
+
+ if (!e)
+ return 0; /* possible only with errors */
+ args = args1 = add_n ? allargs : e->arglist;
+ nchargs = 0;
+ for (lengths = NULL; args; args = args -> nextp)
+ if (arg = (Namep)args->datap) {
+ if (arg->vclass == CLUNKNOWN)
+ arg->vclass = CLVAR;
+ if (arg->vtype == TYCHAR && arg->vclass != CLPROC) {
+ lengths = mkchain((char *)arg, lengths);
+ nchargs++;
+ }
+ }
+ if (!add_n && (np = e->enamep)) {
+ /* one last check -- by now we know all we ever will
+ * about external args...
+ */
+ save_argtypes(e->arglist, &e->entryname->arginfo,
+ &np->arginfo, 0, np->fvarname, STGEXT, nchargs,
+ np->vtype, 1);
+ at = e->entryname->arginfo;
+ a = at->atypes + init_ac[np->vtype];
+ for(; args1; a++, args1 = args1->nextp) {
+ frchain(&a->cp);
+ if (arg = (Namep)args1->datap)
+ switch(arg->vclass) {
+ case CLPROC:
+ if (arg->vimpltype
+ && a->type >= 300)
+ a->type = TYUNKNOWN + 200;
+ break;
+ case CLUNKNOWN:
+ a->type %= 100;
+ }
+ }
+ }
+ return revchain(lengths);
+ }
+
+ void
+#ifdef KR_headers
+listargs(outfile, entryp, add_n_, lengths)
+ FILE *outfile;
+ struct Entrypoint *entryp;
+ int add_n_;
+ chainp lengths;
+#else
+listargs(FILE *outfile, struct Entrypoint *entryp, int add_n_, chainp lengths)
+#endif
+{
+ chainp args;
+ char *s;
+ Namep arg;
+ int did_one = 0;
+
+ nice_printf (outfile, "(");
+
+ if (add_n_) {
+ nice_printf(outfile, "n__");
+ did_one = 1;
+ args = allargs;
+ }
+ else {
+ if (!entryp)
+ return; /* possible only with errors */
+ args = entryp->arglist;
+ }
+
+ if (multitype)
+ {
+ nice_printf(outfile, ", ret_val");
+ did_one = 1;
+ args = allargs;
+ }
+ else if (ONEOF(proctype, MSKCOMPLEX|MSKCHAR))
+ {
+ s = xretslot[proctype]->user.ident;
+ nice_printf(outfile, did_one ? ", %s" : "%s",
+ *s == '(' /*)*/ ? "r_v" : s);
+ did_one = 1;
+ if (proctype == TYCHAR)
+ nice_printf (outfile, ", ret_val_len");
+ }
+ for (; args; args = args -> nextp)
+ if (arg = (Namep)args->datap) {
+ nice_printf (outfile, "%s", did_one ? ", " : "");
+ out_name (outfile, arg);
+ did_one = 1;
+ }
+
+ for (args = lengths; args; args = args -> nextp)
+ nice_printf(outfile, ", %s",
+ new_arg_length((Namep)args->datap));
+ nice_printf (outfile, ")");
+} /* listargs */
+
+
+ void
+#ifdef KR_headers
+list_arg_types(outfile, entryp, lengths, add_n_, finalnl)
+ FILE *outfile;
+ struct Entrypoint *entryp;
+ chainp lengths;
+ int add_n_;
+ char *finalnl;
+#else
+list_arg_types(FILE *outfile, struct Entrypoint *entryp, chainp lengths, int add_n_, char *finalnl)
+#endif
+{
+ chainp args;
+ int last_type = -1, last_class = -1;
+ int did_one = 0, done_one, is_ext;
+ char *s, *sep = "", *sep1;
+
+ if (outfile == (FILE *) NULL) {
+ err ("list_arg_types: null output file");
+ return;
+ } else if (entryp == (struct Entrypoint *) NULL) {
+ err ("list_arg_types: null procedure entry pointer");
+ return;
+ } /* else */
+
+ if (Ansi) {
+ done_one = 0;
+ sep1 = ", ";
+ nice_printf(outfile, "(" /*)*/);
+ }
+ else {
+ done_one = 1;
+ sep1 = ";\n";
+ }
+ args = entryp->arglist;
+ if (add_n_) {
+ nice_printf(outfile, "int n__");
+ did_one = done_one;
+ sep = sep1;
+ args = allargs;
+ }
+ if (multitype) {
+ nice_printf(outfile, "%sMultitype *ret_val", sep);
+ did_one = done_one;
+ sep = sep1;
+ }
+ else if (ONEOF (proctype, MSKCOMPLEX|MSKCHAR)) {
+ s = xretslot[proctype]->user.ident;
+ nice_printf(outfile, "%s%s *%s", sep, c_type_decl(proctype, 0),
+ *s == '(' /*)*/ ? "r_v" : s);
+ did_one = done_one;
+ sep = sep1;
+ if (proctype == TYCHAR)
+ nice_printf (outfile, "%sftnlen ret_val_len", sep);
+ } /* if ONEOF proctype */
+ for (; args; args = args -> nextp) {
+ Namep arg = (Namep) args->datap;
+
+/* Scalars are passed by reference, and arrays will have their lower bound
+ adjusted, so nearly everything is printed with a star in front. The
+ exception is character lengths, which are passed by value. */
+
+ if (arg) {
+ int type = arg -> vtype, class = arg -> vclass;
+
+ if (class == CLPROC)
+ if (arg->vimpltype)
+ type = Castargs ? TYUNKNOWN : TYSUBR;
+ else if (type == TYREAL && forcedouble && !Castargs)
+ type = TYDREAL;
+
+ if (type == last_type && class == last_class && did_one)
+ nice_printf (outfile, ", ");
+ else
+ if ((is_ext = class == CLPROC) && Castargs)
+ nice_printf(outfile, "%s%s ", sep,
+ usedcasts[type] = casttypes[type]);
+ else
+ nice_printf(outfile, "%s%s ", sep,
+ c_type_decl(type, is_ext));
+ if (class == CLPROC)
+ if (Castargs)
+ out_name(outfile, arg);
+ else {
+ nice_printf(outfile, "(*");
+ out_name(outfile, arg);
+ nice_printf(outfile, ") %s", parens);
+ }
+ else {
+ nice_printf (outfile, "*");
+ out_name (outfile, arg);
+ }
+
+ last_type = type;
+ last_class = class;
+ did_one = done_one;
+ sep = sep1;
+ } /* if (arg) */
+ } /* for args = entryp -> arglist */
+
+ for (args = lengths; args; args = args -> nextp)
+ nice_printf(outfile, "%sftnlen %s", sep,
+ new_arg_length((Namep)args->datap));
+ if (did_one)
+ nice_printf (outfile, ";\n");
+ else if (Ansi)
+ nice_printf(outfile,
+ /*((*/ sep != sep1 && Ansi == 1 ? "void)%s" : ")%s",
+ finalnl);
+} /* list_arg_types */
+
+ static void
+#ifdef KR_headers
+write_formats(outfile)
+ FILE *outfile;
+#else
+write_formats(FILE *outfile)
+#endif
+{
+ register struct Labelblock *lp;
+ int first = 1;
+ char *fs;
+
+ for(lp = labeltab ; lp < highlabtab ; ++lp)
+ if (lp->fmtlabused) {
+ if (first) {
+ first = 0;
+ nice_printf(outfile, "/* Format strings */\n");
+ }
+ nice_printf(outfile, "static char fmt_%ld[] = \"",
+ lp->stateno);
+ if (!(fs = lp->fmtstring))
+ fs = "";
+ nice_printf(outfile, "%s\";\n", fs);
+ }
+ if (!first)
+ nice_printf(outfile, "\n");
+ }
+
+ static void
+#ifdef KR_headers
+write_ioblocks(outfile)
+ FILE *outfile;
+#else
+write_ioblocks(FILE *outfile)
+#endif
+{
+ register iob_data *L;
+ register char *f, **s, *sep;
+
+ nice_printf(outfile, "/* Fortran I/O blocks */\n");
+ L = iob_list = (iob_data *)revchain((chainp)iob_list);
+ do {
+ nice_printf(outfile, "static %s %s = { ",
+ L->type, L->name);
+ sep = 0;
+ for(s = L->fields; f = *s; s++) {
+ if (sep)
+ nice_printf(outfile, sep);
+ sep = ", ";
+ if (*f == '"') { /* kludge */
+ nice_printf(outfile, "\"");
+ nice_printf(outfile, "%s\"", f+1);
+ }
+ else
+ nice_printf(outfile, "%s", f);
+ }
+ nice_printf(outfile, " };\n");
+ }
+ while(L = L->next);
+ nice_printf(outfile, "\n\n");
+ }
+
+ static void
+#ifdef KR_headers
+write_assigned_fmts(outfile)
+ FILE *outfile;
+#else
+write_assigned_fmts(FILE *outfile)
+#endif
+{
+ register chainp cp;
+ Namep np;
+ int did_one = 0;
+
+ cp = assigned_fmts = revchain(assigned_fmts);
+ nice_printf(outfile, "/* Assigned format variables */\nchar ");
+ do {
+ np = (Namep)cp->datap;
+ if (did_one)
+ nice_printf(outfile, ", ");
+ did_one = 1;
+ nice_printf(outfile, "*%s_fmt", np->fvarname);
+ }
+ while(cp = cp->nextp);
+ nice_printf(outfile, ";\n\n");
+ }
+
+ static char *
+#ifdef KR_headers
+to_upper(s)
+ register char *s;
+#else
+to_upper(register char *s)
+#endif
+{
+ static char buf[64];
+ register char *t = buf;
+ register int c;
+ while(*t++ = (c = *s++) >= 'a' && c <= 'z' ? c + 'A' - 'a' : c);
+ return buf;
+ }
+
+
+/* This routine creates static structures representing a namelist.
+ Declarations of the namelist and related structures are:
+
+ struct Vardesc {
+ char *name;
+ char *addr;
+ ftnlen *dims; /* laid out as struct dimensions below *//*
+ int type;
+ };
+ typedef struct Vardesc Vardesc;
+
+ struct Namelist {
+ char *name;
+ Vardesc **vars;
+ int nvars;
+ };
+
+ struct dimensions
+ {
+ ftnlen numberofdimensions;
+ ftnlen numberofelements
+ ftnlen baseoffset;
+ ftnlen span[numberofdimensions-1];
+ };
+
+ If dims is not null, then the corner element of the array is at
+ addr. However, the element with subscripts (i1,...,in) is at
+ addr + sizeoftype * (i1+span[0]*(i2+span[1]*...) - dimp->baseoffset)
+*/
+
+ static void
+#ifdef KR_headers
+write_namelists(nmch, outfile)
+ chainp nmch;
+ FILE *outfile;
+#else
+write_namelists(chainp nmch, FILE *outfile)
+#endif
+{
+ Namep var;
+ struct Hashentry *entry;
+ struct Dimblock *dimp;
+ int i, nd, type;
+ char *comma, *name;
+ register chainp q;
+ register Namep v;
+ extern int typeconv[];
+
+ nice_printf(outfile, "/* Namelist stuff */\n\n");
+ for (entry = hashtab; entry < lasthash; ++entry) {
+ if (!(v = entry->varp) || !v->vnamelist)
+ continue;
+ type = v->vtype;
+ name = v->cvarname;
+ if (dimp = v->vdim) {
+ nd = dimp->ndim;
+ nice_printf(outfile,
+ "static ftnlen %s_dims[] = { %d, %ld, %ld",
+ name, nd,
+ dimp->nelt->constblock.Const.ci,
+ dimp->baseoffset->constblock.Const.ci);
+ for(i = 0, --nd; i < nd; i++)
+ nice_printf(outfile, ", %ld",
+ dimp->dims[i].dimsize->constblock.Const.ci);
+ nice_printf(outfile, " };\n");
+ }
+ nice_printf(outfile, "static Vardesc %s_dv = { \"%s\", %s",
+ name, to_upper(v->fvarname),
+ type == TYCHAR ? ""
+ : (dimp || oneof_stg(v,v->vstg,
+ M(STGEQUIV)|M(STGCOMMON)))
+ ? "(char *)" : "(char *)&");
+ out_name(outfile, v);
+ nice_printf(outfile, dimp ? ", %s_dims" : ", (ftnlen *)0", name);
+ nice_printf(outfile, ", %ld };\n",
+ type != TYCHAR ? (long)typeconv[type]
+ : -v->vleng->constblock.Const.ci);
+ }
+
+ do {
+ var = (Namep)nmch->datap;
+ name = var->cvarname;
+ nice_printf(outfile, "\nstatic Vardesc *%s_vl[] = ", name);
+ comma = "{";
+ i = 0;
+ for(q = var->varxptr.namelist ; q ; q = q->nextp) {
+ v = (Namep)q->datap;
+ if (!v->vnamelist)
+ continue;
+ i++;
+ nice_printf(outfile, "%s &%s_dv", comma, v->cvarname);
+ comma = ",";
+ }
+ nice_printf(outfile, " };\n");
+ nice_printf(outfile,
+ "static Namelist %s = { \"%s\", %s_vl, %d };\n",
+ name, to_upper(var->fvarname), name, i);
+ }
+ while(nmch = nmch->nextp);
+ nice_printf(outfile, "\n");
+ }
+
+/* fixextype tries to infer from usage in previous procedures
+ the type of an external procedure declared
+ external and passed as an argument but never typed or invoked.
+ */
+
+ static int
+#ifdef KR_headers
+fixexttype(var)
+ Namep var;
+#else
+fixexttype(Namep var)
+#endif
+{
+ Extsym *e;
+ int type, type1;
+
+ type = var->vtype;
+ e = &extsymtab[var->vardesc.varno];
+ if ((type1 = e->extype) && type == TYUNKNOWN)
+ return var->vtype = type1;
+ if (var->visused) {
+ if (e->exused && type != type1)
+ changedtype(var);
+ e->exused = 1;
+ e->extype = type;
+ }
+ return type;
+ }
+
+ static void
+#ifdef KR_headers
+ref_defs(outfile, refdefs)
+ FILE *outfile;
+ chainp refdefs;
+#else
+ref_defs(FILE *outfile, chainp refdefs)
+#endif
+{
+ chainp cp;
+ int eb, i, j, n;
+ struct Dimblock *dimp;
+ expptr b, vl;
+ Namep var;
+ char *amp, *comma;
+
+ margin_printf(outfile, "\n");
+ for(cp = refdefs = revchain(refdefs); cp; cp = cp->nextp) {
+ var = (Namep)cp->datap;
+ cp->datap = 0;
+ amp = "_subscr";
+ if (!(eb = var->vsubscrused)) {
+ var->vrefused = 0;
+ if (!ISCOMPLEX(var->vtype))
+ amp = "_ref";
+ }
+ def_start(outfile, var->cvarname, amp, CNULL);
+ dimp = var->vdim;
+ vl = 0;
+ comma = "(";
+ amp = "";
+ if (var->vtype == TYCHAR) {
+ amp = "&";
+ vl = var->vleng;
+ if (ISCONST(vl) && vl->constblock.Const.ci == 1)
+ vl = 0;
+ nice_printf(outfile, "%sa_0", comma);
+ comma = ",";
+ }
+ n = dimp->ndim;
+ for(i = 1; i <= n; i++, comma = ",")
+ nice_printf(outfile, "%sa_%d", comma, i);
+ nice_printf(outfile, ") %s", amp);
+ if (var->vsubscrused)
+ var->vsubscrused = 0;
+ else if (!ISCOMPLEX(var->vtype)) {
+ out_name(outfile, var);
+ nice_printf(outfile, "[%s", vl ? "(" : "");
+ }
+ for(j = 2; j < n; j++)
+ nice_printf(outfile, "(");
+ while(--i > 1) {
+ nice_printf(outfile, "(a_%d)%s*", i, i == n ? "" : ")");
+ expr_out(outfile, cpexpr(dimp->dims[i-2].dimsize));
+ nice_printf(outfile, " + ");
+ }
+ nice_printf(outfile, "a_1");
+ if (var->vtype == TYCHAR) {
+ if (vl) {
+ nice_printf(outfile, ")*");
+ expr_out(outfile, cpexpr(vl));
+ }
+ nice_printf(outfile, " + a_0");
+ }
+ if ((var->vstg != STGARG /* || checksubs */ )
+ && (b = dimp->baseoffset)) {
+ b = cpexpr(b);
+ if (var->vtype == TYCHAR)
+ b = mkexpr(OPSTAR, cpexpr(var->vleng), b);
+ nice_printf(outfile, " - ");
+ expr_out(outfile, b);
+ }
+ if (ISCOMPLEX(var->vtype)) {
+ margin_printf(outfile, "\n");
+ def_start(outfile, var->cvarname, "_ref", CNULL);
+ comma = "(";
+ for(i = 1; i <= n; i++, comma = ",")
+ nice_printf(outfile, "%sa_%d", comma, i);
+ nice_printf(outfile, ") %s[%s_subscr",
+ var->cvarname, var->cvarname);
+ comma = "(";
+ for(i = 1; i <= n; i++, comma = ",")
+ nice_printf(outfile, "%sa_%d", comma, i);
+ nice_printf(outfile, ")");
+ }
+ margin_printf(outfile, "]\n" + eb);
+ }
+ nice_printf(outfile, "\n");
+ frchain(&refdefs);
+ }
+
+ void
+#ifdef KR_headers
+list_decls(outfile)
+ FILE *outfile;
+#else
+list_decls(FILE *outfile)
+#endif
+{
+ extern chainp used_builtins;
+ extern struct Hashentry *hashtab;
+ struct Hashentry *entry;
+ int write_header = 1;
+ int last_class = -1, last_stg = -1;
+ Namep var;
+ int Alias, Define, did_one, last_type, type;
+ extern int def_equivs, useauto;
+ extern chainp new_vars; /* Compiler-generated locals */
+ chainp namelists = 0, refdefs = 0;
+ char *ctype;
+ int useauto1 = useauto && !saveall;
+ long x;
+ extern int hsize;
+
+/* First write out the statically initialized data */
+
+ if (initfile)
+ list_init_data(&initfile, initfname, outfile);
+
+/* Next come formats */
+ write_formats(outfile);
+
+/* Now write out the system-generated identifiers */
+
+ if (new_vars || nequiv) {
+ chainp args, next_var, this_var;
+ chainp nv[TYVOID], nv1[TYVOID];
+ int i, j;
+ Addrp Var;
+ Namep arg;
+
+ /* zap unused dimension variables */
+
+ for(args = allargs; args; args = args->nextp) {
+ arg = (Namep)args->datap;
+ if (this_var = arg->vlastdim) {
+ frexpr((tagptr)this_var->datap);
+ this_var->datap = 0;
+ }
+ }
+
+ /* sort new_vars by type, skipping entries just zapped */
+
+ for(i = TYADDR; i < TYVOID; i++)
+ nv[i] = 0;
+ for(this_var = new_vars; this_var; this_var = next_var) {
+ next_var = this_var->nextp;
+ if (Var = (Addrp)this_var->datap) {
+ if (!(this_var->nextp = nv[j = Var->vtype]))
+ nv1[j] = this_var;
+ nv[j] = this_var;
+ }
+ else {
+ this_var->nextp = 0;
+ frchain(&this_var);
+ }
+ }
+ new_vars = 0;
+ for(i = TYVOID; --i >= TYADDR;)
+ if (this_var = nv[i]) {
+ nv1[i]->nextp = new_vars;
+ new_vars = this_var;
+ }
+
+ /* write the declarations */
+
+ did_one = 0;
+ last_type = -1;
+
+ for (this_var = new_vars; this_var; this_var = this_var -> nextp) {
+ Var = (Addrp) this_var->datap;
+
+ if (Var == (Addrp) NULL)
+ err ("list_decls: null variable");
+ else if (Var -> tag != TADDR)
+ erri ("list_decls: bad tag on new variable '%d'",
+ Var -> tag);
+
+ type = nv_type (Var);
+ if (Var->vstg == STGINIT
+ || Var->uname_tag == UNAM_IDENT
+ && *Var->user.ident == ' '
+ && multitype)
+ continue;
+ if (!did_one)
+ nice_printf (outfile, "/* System generated locals */\n");
+
+ if (last_type == type && did_one)
+ nice_printf (outfile, ", ");
+ else {
+ if (did_one)
+ nice_printf (outfile, ";\n");
+ nice_printf (outfile, "%s ",
+ c_type_decl (type, Var -> vclass == CLPROC));
+ } /* else */
+
+/* Character type is really a string type. Put out a '*' for parameters
+ with unknown length and functions returning character */
+
+ if (Var -> vtype == TYCHAR && (!ISICON ((Var -> vleng))
+ || Var -> vclass == CLPROC))
+ nice_printf (outfile, "*");
+
+ write_nv_ident(outfile, (Addrp)this_var->datap);
+ if (Var -> vtype == TYCHAR && Var->vclass != CLPROC &&
+ ISICON((Var -> vleng))
+ && (i = Var->vleng->constblock.Const.ci) > 0)
+ nice_printf (outfile, "[%d]", i);
+
+ did_one = 1;
+ last_type = nv_type (Var);
+ } /* for this_var */
+
+/* Handle the uninitialized equivalences */
+
+ do_uninit_equivs (outfile, &did_one);
+
+ if (did_one)
+ nice_printf (outfile, ";\n\n");
+ } /* if new_vars */
+
+/* Write out builtin declarations */
+
+ if (used_builtins) {
+ chainp cp;
+ Extsym *es;
+
+ last_type = -1;
+ did_one = 0;
+
+ nice_printf (outfile, "/* Builtin functions */");
+
+ for (cp = used_builtins; cp; cp = cp -> nextp) {
+ Addrp e = (Addrp)cp->datap;
+
+ switch(type = e->vtype) {
+ case TYDREAL:
+ case TYREAL:
+ /* if (forcedouble || e->dbl_builtin) */
+ /* libF77 currently assumes everything double */
+ type = TYDREAL;
+ ctype = "double";
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ type = TYVOID;
+ /* no break */
+ default:
+ ctype = c_type_decl(type, 0);
+ }
+
+ if (did_one && last_type == type)
+ nice_printf(outfile, ", ");
+ else
+ nice_printf(outfile, "%s\n%s ", did_one ? ";" : "", ctype);
+
+ extern_out(outfile, es = &extsymtab[e -> memno]);
+ proto(outfile, es->arginfo, es->fextname);
+ last_type = type;
+ did_one = 1;
+ } /* for cp = used_builtins */
+
+ nice_printf (outfile, ";\n\n");
+ } /* if used_builtins */
+
+ last_type = -1;
+ for (entry = hashtab; entry < lasthash; ++entry) {
+ var = entry -> varp;
+
+ if (var) {
+ int procclass = var -> vprocclass;
+ char *comment = NULL;
+ int stg = var -> vstg;
+ int class = var -> vclass;
+ type = var -> vtype;
+
+ if (var->vrefused)
+ refdefs = mkchain((char *)var, refdefs);
+ if (var->vsubscrused)
+ if (ISCOMPLEX(var->vtype))
+ var->vsubscrused = 0;
+ else
+ refdefs = mkchain((char *)var, refdefs);
+ if (ONEOF(stg, M(STGARG)|M(STGLENG)|M(STGINIT)))
+ continue;
+
+ if (useauto1 && stg == STGBSS && !var->vsave)
+ stg = STGAUTO;
+
+ switch (class) {
+ case CLVAR:
+ break;
+ case CLPROC:
+ switch(procclass) {
+ case PTHISPROC:
+ extsymtab[var->vardesc.varno].extype = type;
+ continue;
+ case PSTFUNCT:
+ case PINTRINSIC:
+ continue;
+ case PUNKNOWN:
+ err ("list_decls: unknown procedure class");
+ continue;
+ case PEXTERNAL:
+ if (stg == STGUNKNOWN) {
+ warn1(
+ "%.64s declared EXTERNAL but never used.",
+ var->fvarname);
+ /* to retain names declared EXTERNAL */
+ /* but not referenced, change
+ /* "continue" to "stg = STGEXT" */
+ continue;
+ }
+ else
+ type = fixexttype(var);
+ }
+ break;
+ case CLUNKNOWN:
+ /* declared but never used */
+ continue;
+ case CLPARAM:
+ continue;
+ case CLNAMELIST:
+ if (var->visused)
+ namelists = mkchain((char *)var, namelists);
+ continue;
+ default:
+ erri("list_decls: can't handle class '%d' yet",
+ class);
+ Fatal(var->fvarname);
+ continue;
+ } /* switch */
+
+ /* Might be equivalenced to a common. If not, don't process */
+ if (stg == STGCOMMON && !var->vcommequiv)
+ continue;
+
+/* Only write the header if system-generated locals, builtins, or
+ uninitialized equivs were already output */
+
+ if (write_header == 1 && (new_vars || nequiv || used_builtins)
+ && oneof_stg ( var, stg,
+ M(STGBSS)|M(STGEXT)|M(STGAUTO)|M(STGCOMMON)|M(STGEQUIV))) {
+ nice_printf (outfile, "/* Local variables */\n");
+ write_header = 2;
+ }
+
+
+ Alias = oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON));
+ if (Define = (Alias && def_equivs)) {
+ if (!write_header)
+ nice_printf(outfile, ";\n");
+ def_start(outfile, var->cvarname, CNULL, "(");
+ goto Alias1;
+ }
+ else if (type == last_type && class == last_class &&
+ stg == last_stg && !write_header)
+ nice_printf (outfile, ", ");
+ else {
+ if (!write_header && ONEOF(stg, M(STGBSS)|
+ M(STGEXT)|M(STGAUTO)|M(STGEQUIV)|M(STGCOMMON)))
+ nice_printf (outfile, ";\n");
+
+ switch (stg) {
+ case STGARG:
+ case STGLENG:
+ /* Part of the argument list, don't write them out
+ again */
+ continue; /* Go back to top of the loop */
+ case STGBSS:
+ case STGEQUIV:
+ case STGCOMMON:
+ nice_printf (outfile, "static ");
+ break;
+ case STGEXT:
+ nice_printf (outfile, "extern ");
+ break;
+ case STGAUTO:
+ break;
+ case STGINIT:
+ case STGUNKNOWN:
+ /* Don't want to touch the initialized data, that will
+ be handled elsewhere. Unknown data have
+ already been complained about, so skip them */
+ continue;
+ default:
+ erri("list_decls: can't handle storage class %d",
+ stg);
+ continue;
+ } /* switch */
+
+ if (type == TYCHAR && halign && class != CLPROC
+ && ISICON(var->vleng)) {
+ nice_printf(outfile, "struct { %s fill; char val",
+ halign);
+ x = wr_char_len(outfile, var->vdim,
+ var->vleng->constblock.Const.ci, 1);
+ if (x %= hsize)
+ nice_printf(outfile, "; char fill2[%ld]",
+ hsize - x);
+ nice_printf(outfile, "; } %s_st;\n", var->cvarname);
+ def_start(outfile, var->cvarname, CNULL, var->cvarname);
+ margin_printf(outfile, "_st.val\n");
+ last_type = -1;
+ write_header = 2;
+ continue;
+ }
+ nice_printf(outfile, "%s ",
+ c_type_decl(type, class == CLPROC));
+ } /* else */
+
+/* Character type is really a string type. Put out a '*' for variable
+ length strings, and also for equivalences */
+
+ if (type == TYCHAR && class != CLPROC
+ && (!var->vleng || !ISICON (var -> vleng))
+ || oneof_stg(var, stg, M(STGEQUIV)|M(STGCOMMON)))
+ nice_printf (outfile, "*%s", var->cvarname);
+ else {
+ nice_printf (outfile, "%s", var->cvarname);
+ if (class == CLPROC) {
+ Argtypes *at;
+ if (!(at = var->arginfo)
+ && var->vprocclass == PEXTERNAL)
+ at = extsymtab[var->vardesc.varno].arginfo;
+ proto(outfile, at, var->fvarname);
+ }
+ else if (type == TYCHAR && ISICON ((var -> vleng)))
+ wr_char_len(outfile, var->vdim,
+ (int)var->vleng->constblock.Const.ci, 0);
+ else if (var -> vdim &&
+ !oneof_stg (var, stg, M(STGEQUIV)|M(STGCOMMON)))
+ comment = wr_ardecls(outfile, var->vdim, 1L);
+ }
+
+ if (comment)
+ nice_printf (outfile, "%s", comment);
+ Alias1:
+ if (Alias) {
+ char *amp, *lp, *name, *rp;
+ ftnint voff = var -> voffset;
+ int et0, expr_type, k;
+ Extsym *E;
+ struct Equivblock *eb;
+ char buf[16];
+
+/* We DON'T want to use oneof_stg here, because we need to distinguish
+ between them */
+
+ if (stg == STGEQUIV) {
+ name = equiv_name(k = var->vardesc.varno, CNULL);
+ eb = eqvclass + k;
+ if (eb->eqvinit) {
+ amp = "&";
+ et0 = TYERROR;
+ }
+ else {
+ amp = "";
+ et0 = eb->eqvtype;
+ }
+ expr_type = et0;
+ }
+ else {
+ E = &extsymtab[var->vardesc.varno];
+ sprintf(name = buf, "%s%d", E->cextname, E->curno);
+ expr_type = type;
+ et0 = -1;
+ amp = "&";
+ } /* else */
+
+ if (!Define)
+ nice_printf (outfile, " = ");
+ if (voff) {
+ k = typesize[type];
+ switch((int)(voff % k)) {
+ case 0:
+ voff /= k;
+ expr_type = type;
+ break;
+ case SZSHORT:
+ case SZSHORT+SZLONG:
+ expr_type = TYSHORT;
+ voff /= SZSHORT;
+ break;
+ case SZLONG:
+ expr_type = TYLONG;
+ voff /= SZLONG;
+ break;
+ default:
+ expr_type = TYCHAR;
+ }
+ }
+
+ if (expr_type == type) {
+ lp = rp = "";
+ if (et0 == -1 && !voff)
+ goto cast;
+ }
+ else {
+ lp = "(";
+ rp = ")";
+ cast:
+ nice_printf(outfile, "(%s *)", c_type_decl(type, 0));
+ }
+
+/* Now worry about computing the offset */
+
+ if (voff) {
+ if (expr_type == et0)
+ nice_printf (outfile, "%s%s + %ld%s",
+ lp, name, voff, rp);
+ else
+ nice_printf(outfile, "%s(%s *)%s%s + %ld%s", lp,
+ c_type_decl (expr_type, 0), amp,
+ name, voff, rp);
+ } else
+ nice_printf(outfile, "%s%s", amp, name);
+/* Always put these at the end of the line */
+ last_type = last_class = last_stg = -1;
+ write_header = 0;
+ if (Define) {
+ margin_printf(outfile, ")\n");
+ write_header = 2;
+ }
+ continue;
+ }
+ write_header = 0;
+ last_type = type;
+ last_class = class;
+ last_stg = stg;
+ } /* if (var) */
+ } /* for (entry = hashtab */
+
+ if (!write_header)
+ nice_printf (outfile, ";\n\n");
+ else if (write_header == 2)
+ nice_printf(outfile, "\n");
+
+/* Next, namelists, which may reference equivs */
+
+ if (namelists) {
+ write_namelists(namelists = revchain(namelists), outfile);
+ frchain(&namelists);
+ }
+
+/* Finally, ioblocks (which may reference equivs and namelists) */
+ if (iob_list)
+ write_ioblocks(outfile);
+ if (assigned_fmts)
+ write_assigned_fmts(outfile);
+
+ if (refdefs)
+ ref_defs(outfile, refdefs);
+
+} /* list_decls */
+
+ void
+#ifdef KR_headers
+do_uninit_equivs(outfile, did_one)
+ FILE *outfile;
+ int *did_one;
+#else
+do_uninit_equivs(FILE *outfile, int *did_one)
+#endif
+{
+ extern int nequiv;
+ struct Equivblock *eqv, *lasteqv = eqvclass + nequiv;
+ int k, last_type = -1, t;
+
+ for (eqv = eqvclass; eqv < lasteqv; eqv++)
+ if (!eqv -> eqvinit && eqv -> eqvtop != eqv -> eqvbottom) {
+ if (!*did_one)
+ nice_printf (outfile, "/* System generated locals */\n");
+ t = eqv->eqvtype;
+ if (last_type == t)
+ nice_printf (outfile, ", ");
+ else {
+ if (*did_one)
+ nice_printf (outfile, ";\n");
+ nice_printf (outfile, "static %s ", c_type_decl(t, 0));
+ k = typesize[t];
+ } /* else */
+ nice_printf(outfile, "%s", equiv_name((int)(eqv - eqvclass), CNULL));
+ nice_printf(outfile, "[%ld]",
+ (eqv->eqvtop - eqv->eqvbottom + k - 1) / k);
+ last_type = t;
+ *did_one = 1;
+ } /* if !eqv -> eqvinit */
+} /* do_uninit_equivs */
+
+
+/* wr_ardecls -- Writes the brackets and size for an array
+ declaration. Because of the inner workings of the compiler,
+ multi-dimensional arrays get mapped directly into a one-dimensional
+ array, so we have to compute the size of the array here. When the
+ dimension is greater than 1, a string comment about the original size
+ is returned */
+
+ char *
+#ifdef KR_headers
+wr_ardecls(outfile, dimp, size)
+ FILE *outfile;
+ struct Dimblock *dimp;
+ long size;
+#else
+wr_ardecls(FILE *outfile, struct Dimblock *dimp, long size)
+#endif
+{
+ int i, k;
+ ftnint j;
+ static char buf[1000];
+
+ if (dimp == (struct Dimblock *) NULL)
+ return NULL;
+
+ sprintf(buf, "\t/* was "); /* would like to say k = sprintf(...), but */
+ k = strlen(buf); /* BSD doesn't return char transmitted count */
+
+ for (i = 0; i < dimp -> ndim; i++) {
+ expptr this_size = dimp -> dims[i].dimsize;
+
+ if (ISCONST(this_size)) {
+ if (ISINT(this_size->constblock.vtype))
+ j = this_size -> constblock.Const.ci;
+ else if (ISREAL(this_size->constblock.vtype))
+ j = (ftnint)this_size -> constblock.Const.cd[0];
+ else
+ goto non_const;
+ size *= j;
+ sprintf(buf+k, "[%ld]", j);
+ k += strlen(buf+k);
+ /* BSD prevents getting strlen from sprintf */
+ }
+ else {
+ non_const:
+ err ("wr_ardecls: nonconstant array size");
+ }
+ } /* for i = 0 */
+
+ nice_printf (outfile, "[%ld]", size);
+ strcat(buf+k, " */");
+
+ return (i > 1) ? buf : NULL;
+} /* wr_ardecls */
+
+
+
+/* ----------------------------------------------------------------------
+
+ The following routines read from the p1 intermediate file. If
+ that format changes, only these routines need be changed
+
+ ---------------------------------------------------------------------- */
+
+ static int
+#ifdef KR_headers
+get_p1_token(infile)
+ FILE *infile;
+#else
+get_p1_token(FILE *infile)
+#endif
+{
+ int token = P1_UNKNOWN;
+
+/* NOT PORTABLE!! */
+
+ if (fscanf (infile, "%d", &token) == EOF)
+ return P1_EOF;
+
+/* Skip over the ": " */
+
+ if (getc (infile) != '\n')
+ getc (infile);
+
+ return token;
+} /* get_p1_token */
+
+
+
+/* Returns a (null terminated) string from the input file */
+
+ static int
+#ifdef KR_headers
+p1gets(fp, str, size)
+ FILE *fp;
+ char *str;
+ int size;
+#else
+p1gets(FILE *fp, char *str, int size)
+#endif
+{
+ char c;
+
+ if (str == NULL)
+ return 0;
+
+ if ((c = getc (fp)) != ' ')
+ ungetc (c, fp);
+
+ if (fgets (str, size, fp)) {
+ int length;
+
+ str[size - 1] = '\0';
+ length = strlen (str);
+
+/* Get rid of the newline */
+
+ if (str[length - 1] == '\n')
+ str[length - 1] = '\0';
+ return 1;
+
+ } else if (feof (fp))
+ return EOF;
+ else
+ return 0;
+} /* p1gets */
+
+
+ static int
+#ifdef KR_headers
+p1get_const(infile, type, resultp)
+ FILE *infile;
+ int type;
+ struct Constblock **resultp;
+#else
+p1get_const(FILE *infile, int type, struct Constblock **resultp)
+#endif
+{
+ int status;
+ struct Constblock *result;
+
+ if (type != TYCHAR) {
+ *resultp = result = ALLOC(Constblock);
+ result -> tag = TCONST;
+ result -> vtype = type;
+ }
+
+ switch (type) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+ case TYLOGICAL:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ status = p1getd (infile, &(result -> Const.ci));
+ break;
+ case TYREAL:
+ case TYDREAL:
+ status = p1getf(infile, &result->Const.cds[0]);
+ result->vstg = 1;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ status = p1getf(infile, &result->Const.cds[0]);
+ if (status && status != EOF)
+ status = p1getf(infile, &result->Const.cds[1]);
+ result->vstg = 1;
+ break;
+ case TYCHAR:
+ status = fscanf(infile, "%lx", resultp);
+ break;
+ default:
+ erri ("p1get_const: bad constant type '%d'", type);
+ status = 0;
+ break;
+ } /* switch */
+
+ return status;
+} /* p1get_const */
+
+ static int
+#ifdef KR_headers
+p1getd(infile, result)
+ FILE *infile;
+ long *result;
+#else
+p1getd(FILE *infile, long *result)
+#endif
+{
+ return fscanf (infile, "%ld", result);
+} /* p1getd */
+
+ static int
+#ifdef KR_headers
+p1getf(infile, result)
+ FILE *infile;
+ char **result;
+#else
+p1getf(FILE *infile, char **result)
+#endif
+{
+
+ char buf[1324];
+ register int k;
+
+ k = fscanf (infile, "%s", buf);
+ if (k < 1)
+ k = EOF;
+ else
+ strcpy(*result = mem(strlen(buf)+1,0), buf);
+ return k;
+}
+
+ static int
+#ifdef KR_headers
+p1getn(infile, count, result)
+ FILE *infile;
+ int count;
+ char **result;
+#else
+p1getn(FILE *infile, int count, char **result)
+#endif
+{
+
+ char *bufptr;
+
+ bufptr = (char *) ckalloc (count);
+
+ if (result)
+ *result = bufptr;
+
+ for (; !feof (infile) && count > 0; count--)
+ *bufptr++ = getc (infile);
+
+ return feof (infile) ? EOF : 1;
+} /* p1getn */
+
+ static void
+#ifdef KR_headers
+proto(outfile, at, fname)
+ FILE *outfile;
+ Argtypes *at;
+ char *fname;
+#else
+proto(FILE *outfile, Argtypes *at, char *fname)
+#endif
+{
+ int i, j, k, n;
+ char *comma;
+ Atype *atypes;
+ Namep np;
+ chainp cp;
+
+ if (at) {
+ /* Correct types that we learn on the fly, e.g.
+ subroutine gotcha(foo)
+ external foo
+ call zap(...,foo,...)
+ call foo(...)
+ */
+ atypes = at->atypes;
+ n = at->defined ? at->dnargs : at->nargs;
+ for(i = 0; i++ < n; atypes++) {
+ if (!(cp = atypes->cp))
+ continue;
+ j = atypes->type;
+ do {
+ np = (Namep)cp->datap;
+ k = np->vtype;
+ if (np->vclass == CLPROC) {
+ if (!np->vimpltype && k)
+ k += 200;
+ else {
+ if (j >= 300)
+ j = TYUNKNOWN + 200;
+ continue;
+ }
+ }
+ if (j == k)
+ continue;
+ if (j >= 300
+ || j == 200 && k >= 200)
+ j = k;
+ else {
+ if (at->nargs >= 0)
+ bad_atypes(at,fname,i,j,k,""," and");
+ goto break2;
+ }
+ }
+ while(cp = cp->nextp);
+ atypes->type = j;
+ frchain(&atypes->cp);
+ }
+ }
+ break2:
+ if (parens) {
+ nice_printf(outfile, parens);
+ return;
+ }
+
+ if (!at || (n = at-> defined ? at->dnargs : at->nargs) < 0) {
+ nice_printf(outfile, Ansi == 1 ? "()" : "(...)");
+ return;
+ }
+
+ if (n == 0) {
+ nice_printf(outfile, Ansi == 1 ? "(void)" : "()");
+ return;
+ }
+
+ atypes = at->atypes;
+ nice_printf(outfile, "(");
+ comma = "";
+ for(; --n >= 0; atypes++) {
+ k = atypes->type;
+ if (k == TYADDR)
+ nice_printf(outfile, "%schar **", comma);
+ else if (k >= 200) {
+ k -= 200;
+ nice_printf(outfile, "%s%s", comma,
+ usedcasts[k] = casttypes[k]);
+ }
+ else if (k >= 100)
+ nice_printf(outfile,
+ k == TYCHAR + 100 ? "%s%s *" : "%s%s",
+ comma, c_type_decl(k-100, 0));
+ else
+ nice_printf(outfile, "%s%s *", comma,
+ c_type_decl(k, 0));
+ comma = ", ";
+ }
+ nice_printf(outfile, ")");
+ }
+
+ void
+#ifdef KR_headers
+protowrite(protofile, type, name, e, lengths)
+ FILE *protofile;
+ int type;
+ char *name;
+ struct Entrypoint *e;
+ chainp lengths;
+#else
+protowrite(FILE *protofile, int type, char *name, struct Entrypoint *e, chainp lengths)
+#endif
+{
+ extern char used_rets[];
+ int asave;
+
+ if (!(asave = Ansi))
+ Castargs = Ansi = 1;
+ nice_printf(protofile, "extern %s %s", protorettypes[type], name);
+ list_arg_types(protofile, e, lengths, 0, ";\n");
+ used_rets[type] = 1;
+ if (!(Ansi = asave))
+ Castargs = 0;
+ }
+
+ static void
+#ifdef KR_headers
+do_p1_1while(outfile)
+ FILE *outfile;
+#else
+do_p1_1while(FILE *outfile)
+#endif
+{
+ if (*wh_next) {
+ nice_printf(outfile,
+ "for(;;) { /* while(complicated condition) */\n" /*}*/ );
+ next_tab(outfile);
+ }
+ else
+ nice_printf(outfile, "while(" /*)*/ );
+ }
+
+ static void
+#ifdef KR_headers
+do_p1_2while(infile, outfile)
+ FILE *infile;
+ FILE *outfile;
+#else
+do_p1_2while(FILE *infile, FILE *outfile)
+#endif
+{
+ expptr test;
+
+ test = do_format(infile, outfile);
+ if (*wh_next)
+ nice_printf(outfile, "if (!(");
+ expr_out(outfile, test);
+ if (*wh_next++)
+ nice_printf(outfile, "))\n\tbreak;\n");
+ else {
+ nice_printf(outfile, /*(*/ ") {\n");
+ next_tab(outfile);
+ }
+ }
+
+ static void
+#ifdef KR_headers
+do_p1_elseifstart(outfile)
+ FILE *outfile;
+#else
+do_p1_elseifstart(FILE *outfile)
+#endif
+{
+ if (*ei_next++) {
+ prev_tab(outfile);
+ nice_printf(outfile, /*{*/
+ "} else /* if(complicated condition) */ {\n" /*}*/ );
+ next_tab(outfile);
+ }
+ }
diff --git a/usr.bin/f2c/format.h b/usr.bin/f2c/format.h
new file mode 100644
index 0000000..3de97f6
--- /dev/null
+++ b/usr.bin/f2c/format.h
@@ -0,0 +1,12 @@
+#define DEF_C_LINE_LENGTH 77
+/* actual max will be 79 */
+
+extern int c_output_line_length; /* max # chars per line in C source
+ code */
+
+chainp data_value Argdcl((FILEP, long int, int));
+int do_init_data Argdcl((FILEP, FILEP));
+void list_init_data Argdcl((FILEP*, char*, FILEP));
+char* wr_ardecls Argdcl((FILEP, struct Dimblock*, long int));
+void wr_one_init Argdcl((FILEP, char*, chainp*, int));
+void wr_output_values Argdcl((FILEP, Namep, chainp));
diff --git a/usr.bin/f2c/formatdata.c b/usr.bin/f2c/formatdata.c
new file mode 100644
index 0000000..690ee10
--- /dev/null
+++ b/usr.bin/f2c/formatdata.c
@@ -0,0 +1,1166 @@
+/****************************************************************
+Copyright 1990, 1991, 1993-5 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "output.h"
+#include "names.h"
+#include "format.h"
+
+#define MAX_INIT_LINE 100
+#define NAME_MAX 64
+
+static int memno2info Argdcl((int, Namep*));
+
+ extern char *initbname;
+
+ void
+#ifdef KR_headers
+list_init_data(Infile, Inname, outfile)
+ FILE **Infile;
+ char *Inname;
+ FILE *outfile;
+#else
+list_init_data(FILE **Infile, char *Inname, FILE *outfile)
+#endif
+{
+ FILE *sortfp;
+ int status;
+
+ fclose(*Infile);
+ *Infile = 0;
+
+ if (status = dsort(Inname, sortfname))
+ fatali ("sort failed, status %d", status);
+
+ scrub(Inname); /* optionally unlink Inname */
+
+ if ((sortfp = fopen(sortfname, textread)) == NULL)
+ Fatal("Couldn't open sorted initialization data");
+
+ do_init_data(outfile, sortfp);
+ fclose(sortfp);
+ scrub(sortfname);
+
+/* Insert a blank line after any initialized data */
+
+ nice_printf (outfile, "\n");
+
+ if (debugflag && infname)
+ /* don't back block data file up -- it won't be overwritten */
+ backup(initfname, initbname);
+} /* list_init_data */
+
+
+
+/* do_init_data -- returns YES when at least one declaration has been
+ written */
+
+ int
+#ifdef KR_headers
+do_init_data(outfile, infile)
+ FILE *outfile;
+ FILE *infile;
+#else
+do_init_data(FILE *outfile, FILE *infile)
+#endif
+{
+ char varname[NAME_MAX], ovarname[NAME_MAX];
+ ftnint offset;
+ ftnint type;
+ int vargroup; /* 0 --> init, 1 --> equiv, 2 --> common */
+ int did_one = 0; /* True when one has been output */
+ chainp values = CHNULL; /* Actual data values */
+ int keepit = 0;
+ Namep np;
+
+ ovarname[0] = '\0';
+
+ while (rdname (infile, &vargroup, varname) && rdlong (infile, &offset)
+ && rdlong (infile, &type)) {
+ if (strcmp (varname, ovarname)) {
+
+ /* If this is a new variable name, the old initialization has been
+ completed */
+
+ wr_one_init(outfile, ovarname, &values, keepit);
+
+ strcpy (ovarname, varname);
+ values = CHNULL;
+ if (vargroup == 0) {
+ if (memno2info(atoi(varname+2), &np)) {
+ if (((Addrp)np)->uname_tag != UNAM_NAME) {
+ err("do_init_data: expected NAME");
+ goto Keep;
+ }
+ np = ((Addrp)np)->user.name;
+ }
+ if (!(keepit = np->visused) && !np->vimpldovar)
+ warn1("local variable %s never used",
+ np->fvarname);
+ }
+ else {
+ Keep:
+ keepit = 1;
+ }
+ if (keepit && !did_one) {
+ nice_printf (outfile, "/* Initialized data */\n\n");
+ did_one = YES;
+ }
+ } /* if strcmp */
+
+ values = mkchain((char *)data_value(infile, offset, (int)type), values);
+ } /* while */
+
+/* Write out the last declaration */
+
+ wr_one_init (outfile, ovarname, &values, keepit);
+
+ return did_one;
+} /* do_init_data */
+
+
+ ftnint
+#ifdef KR_headers
+wr_char_len(outfile, dimp, n, extra1)
+ FILE *outfile;
+ struct Dimblock *dimp;
+ int n;
+ int extra1;
+#else
+wr_char_len(FILE *outfile, struct Dimblock *dimp, int n, int extra1)
+#endif
+{
+ int i, nd;
+ expptr e;
+ ftnint j, rv;
+
+ if (!dimp) {
+ nice_printf (outfile, extra1 ? "[%d+1]" : "[%d]", n);
+ return n + extra1;
+ }
+ nice_printf(outfile, "[%d", n);
+ nd = dimp->ndim;
+ rv = n;
+ for(i = 0; i < nd; i++) {
+ e = dimp->dims[i].dimsize;
+ if (ISCONST(e)) {
+ if (ISINT(e->constblock.vtype))
+ j = e->constblock.Const.ci;
+ else if (ISREAL(e->constblock.vtype))
+ j = (ftnint)e->constblock.Const.cd[0];
+ else
+ goto non_const;
+ nice_printf(outfile, "*%ld", j);
+ rv *= j;
+ }
+ else {
+ non_const:
+ err ("wr_char_len: nonconstant array size");
+ }
+ }
+ /* extra1 allows for stupid C compilers that complain about
+ * too many initializers in
+ * char x[2] = "ab";
+ */
+ nice_printf(outfile, extra1 ? "+1]" : "]");
+ return extra1 ? rv+1 : rv;
+ }
+
+ static int ch_ar_dim = -1; /* length of each element of char string array */
+ static int eqvmemno; /* kludge */
+
+ static void
+#ifdef KR_headers
+write_char_init(outfile, Values, namep)
+ FILE *outfile;
+ chainp *Values;
+ Namep namep;
+#else
+write_char_init(FILE *outfile, chainp *Values, Namep namep)
+#endif
+{
+ struct Equivblock *eqv;
+ long size;
+ struct Dimblock *dimp;
+ int i, nd, type;
+ ftnint j;
+ expptr ds;
+
+ if (!namep)
+ return;
+ if(nequiv >= maxequiv)
+ many("equivalences", 'q', maxequiv);
+ eqv = &eqvclass[nequiv];
+ eqv->eqvbottom = 0;
+ type = namep->vtype;
+ size = type == TYCHAR
+ ? namep->vleng->constblock.Const.ci
+ : typesize[type];
+ if (dimp = namep->vdim)
+ for(i = 0, nd = dimp->ndim; i < nd; i++) {
+ ds = dimp->dims[i].dimsize;
+ if (ISCONST(ds)) {
+ if (ISINT(ds->constblock.vtype))
+ j = ds->constblock.Const.ci;
+ else if (ISREAL(ds->constblock.vtype))
+ j = (ftnint)ds->constblock.Const.cd[0];
+ else
+ goto non_const;
+ size *= j;
+ }
+ else {
+ non_const:
+ err("write_char_values: nonconstant array size");
+ }
+ }
+ *Values = revchain(*Values);
+ eqv->eqvtop = size;
+ eqvmemno = ++lastvarno;
+ eqv->eqvtype = type;
+ wr_equiv_init(outfile, nequiv, Values, 0);
+ def_start(outfile, namep->cvarname, CNULL, "");
+ if (type == TYCHAR)
+ margin_printf(outfile, "((char *)&equiv_%d)\n\n", eqvmemno);
+ else
+ margin_printf(outfile, dimp
+ ? "((%s *)&equiv_%d)\n\n" : "(*(%s *)&equiv_%d)\n\n",
+ c_type_decl(type,0), eqvmemno);
+ }
+
+/* wr_one_init -- outputs the initialization of the variable pointed to
+ by info. When is_addr is true, info is an Addrp; otherwise,
+ treat it as a Namep */
+
+ void
+#ifdef KR_headers
+wr_one_init(outfile, varname, Values, keepit)
+ FILE *outfile;
+ char *varname;
+ chainp *Values;
+ int keepit;
+#else
+wr_one_init(FILE *outfile, char *varname, chainp *Values, int keepit)
+#endif
+{
+ static int memno;
+ static union {
+ Namep name;
+ Addrp addr;
+ } info;
+ Namep namep;
+ int is_addr, size, type;
+ ftnint last, loc;
+ int is_scalar = 0;
+ char *array_comment = NULL, *name;
+ chainp cp, values;
+ extern char datachar[];
+ static int e1[3] = {1, 0, 1};
+ ftnint x;
+ extern int hsize;
+
+ if (!keepit)
+ goto done;
+ if (varname == NULL || varname[1] != '.')
+ goto badvar;
+
+/* Get back to a meaningful representation; find the given memno in one
+ of the appropriate tables (user-generated variables in the hash table,
+ system-generated variables in a separate list */
+
+ memno = atoi(varname + 2);
+ switch(varname[0]) {
+ case 'q':
+ /* Must subtract eqvstart when the source file
+ * contains more than one procedure.
+ */
+ wr_equiv_init(outfile, eqvmemno = memno - eqvstart, Values, 0);
+ goto done;
+ case 'Q':
+ /* COMMON initialization (BLOCK DATA) */
+ wr_equiv_init(outfile, memno, Values, 1);
+ goto done;
+ case 'v':
+ break;
+ default:
+ badvar:
+ errstr("wr_one_init: unknown variable name '%s'", varname);
+ goto done;
+ }
+
+ is_addr = memno2info (memno, &info.name);
+ if (info.name == (Namep) NULL) {
+ err ("wr_one_init -- unknown variable");
+ return;
+ }
+ if (is_addr) {
+ if (info.addr -> uname_tag != UNAM_NAME) {
+ erri ("wr_one_init -- couldn't get name pointer; tag is %d",
+ info.addr -> uname_tag);
+ namep = (Namep) NULL;
+ nice_printf (outfile, " /* bad init data */");
+ } else
+ namep = info.addr -> user.name;
+ } else
+ namep = info.name;
+
+ /* check for character initialization */
+
+ *Values = values = revchain(*Values);
+ type = info.name->vtype;
+ if (type == TYCHAR) {
+ for(last = 0; values; values = values->nextp) {
+ cp = (chainp)values->datap;
+ loc = (ftnint)cp->datap;
+ if (loc > last) {
+ write_char_init(outfile, Values, namep);
+ goto done;
+ }
+ last = (int)cp->nextp->datap == TYBLANK
+ ? loc + (int)cp->nextp->nextp->datap
+ : loc + 1;
+ }
+ if (halign && info.name->tag == TNAME) {
+ nice_printf(outfile, "static struct { %s fill; char val",
+ halign);
+ x = wr_char_len(outfile, namep->vdim, ch_ar_dim =
+ info.name -> vleng -> constblock.Const.ci, 1);
+ if (x %= hsize)
+ nice_printf(outfile, "; char fill2[%ld]", hsize - x);
+ name = info.name->cvarname;
+ nice_printf(outfile, "; } %s_st = { 0,", name);
+ wr_output_values(outfile, namep, *Values);
+ nice_printf(outfile, " };\n");
+ ch_ar_dim = -1;
+ def_start(outfile, name, CNULL, name);
+ margin_printf(outfile, "_st.val\n");
+ goto done;
+ }
+ }
+ else {
+ size = typesize[type];
+ loc = 0;
+ for(; values; values = values->nextp) {
+ if ((int)((chainp)values->datap)->nextp->datap == TYCHAR) {
+ write_char_init(outfile, Values, namep);
+ goto done;
+ }
+ last = ((long) ((chainp) values->datap)->datap) / size;
+ if (last - loc > 4) {
+ write_char_init(outfile, Values, namep);
+ goto done;
+ }
+ loc = last;
+ }
+ }
+ values = *Values;
+
+ nice_printf (outfile, "static %s ", c_type_decl (type, 0));
+
+ if (is_addr)
+ write_nv_ident (outfile, info.addr);
+ else
+ out_name (outfile, info.name);
+
+ if (namep)
+ is_scalar = namep -> vdim == (struct Dimblock *) NULL;
+
+ if (namep && !is_scalar)
+ array_comment = type == TYCHAR
+ ? 0 : wr_ardecls(outfile, namep->vdim, 1L);
+
+ if (type == TYCHAR)
+ if (ISICON (info.name -> vleng))
+
+/* We'll make single strings one character longer, so that we can use the
+ standard C initialization. All this does is pad an extra zero onto the
+ end of the string */
+ wr_char_len(outfile, namep->vdim, ch_ar_dim =
+ info.name -> vleng -> constblock.Const.ci, e1[Ansi]);
+ else
+ err ("variable length character initialization");
+
+ if (array_comment)
+ nice_printf (outfile, "%s", array_comment);
+
+ nice_printf (outfile, " = ");
+ wr_output_values (outfile, namep, values);
+ ch_ar_dim = -1;
+ nice_printf (outfile, ";\n");
+ done:
+ frchain(Values);
+} /* wr_one_init */
+
+
+
+
+ chainp
+#ifdef KR_headers
+data_value(infile, offset, type)
+ FILE *infile;
+ ftnint offset;
+ int type;
+#else
+data_value(FILE *infile, ftnint offset, int type)
+#endif
+{
+ char line[MAX_INIT_LINE + 1], *pointer;
+ chainp vals, prev_val;
+ char *newval;
+
+ if (fgets (line, MAX_INIT_LINE, infile) == NULL) {
+ err ("data_value: error reading from intermediate file");
+ return CHNULL;
+ } /* if fgets */
+
+/* Get rid of the trailing newline */
+
+ if (line[0])
+ line[strlen (line) - 1] = '\0';
+
+#define iswhite(x) (isspace (x) || (x) == ',')
+
+ pointer = line;
+ prev_val = vals = CHNULL;
+
+ while (*pointer) {
+ register char *end_ptr, old_val;
+
+/* Move pointer to the start of the next word */
+
+ while (*pointer && iswhite (*pointer))
+ pointer++;
+ if (*pointer == '\0')
+ break;
+
+/* Move end_ptr to the end of the current word */
+
+ for (end_ptr = pointer + 1; *end_ptr && !iswhite (*end_ptr);
+ end_ptr++)
+ ;
+
+ old_val = *end_ptr;
+ *end_ptr = '\0';
+
+/* Add this value to the end of the list */
+
+ if (ONEOF(type, MSKREAL|MSKCOMPLEX))
+ newval = cpstring(pointer);
+ else
+ newval = (char *)atol(pointer);
+ if (vals) {
+ prev_val->nextp = mkchain(newval, CHNULL);
+ prev_val = prev_val -> nextp;
+ } else
+ prev_val = vals = mkchain(newval, CHNULL);
+ *end_ptr = old_val;
+ pointer = end_ptr;
+ } /* while *pointer */
+
+ return mkchain((char *)offset, mkchain((char *)LONG_CAST type, vals));
+} /* data_value */
+
+ static void
+overlapping(Void)
+{
+ extern char *filename0;
+ static int warned = 0;
+
+ if (warned)
+ return;
+ warned = 1;
+
+ fprintf(stderr, "Error");
+ if (filename0)
+ fprintf(stderr, " in file %s", filename0);
+ fprintf(stderr, ": overlapping initializations\n");
+ nerr++;
+ }
+
+ static void make_one_const Argdcl((int, union Constant*, chainp));
+ static long charlen;
+
+ void
+#ifdef KR_headers
+wr_output_values(outfile, namep, values)
+ FILE *outfile;
+ Namep namep;
+ chainp values;
+#else
+wr_output_values(FILE *outfile, Namep namep, chainp values)
+#endif
+{
+ int type = TYUNKNOWN;
+ struct Constblock Const;
+ static expptr Vlen;
+
+ if (namep)
+ type = namep -> vtype;
+
+/* Handle array initializations away from scalars */
+
+ if (namep && namep -> vdim)
+ wr_array_init (outfile, namep -> vtype, values);
+
+ else if (values->nextp && type != TYCHAR)
+ overlapping();
+
+ else {
+ make_one_const(type, &Const.Const, values);
+ Const.vtype = type;
+ Const.vstg = ONEOF(type, MSKREAL|MSKCOMPLEX) != 0;
+ if (type== TYCHAR) {
+ if (!Vlen)
+ Vlen = ICON(0);
+ Const.vleng = Vlen;
+ Vlen->constblock.Const.ci = charlen;
+ out_const (outfile, &Const);
+ free (Const.Const.ccp);
+ }
+ else
+ out_const (outfile, &Const);
+ }
+ }
+
+
+ void
+#ifdef KR_headers
+wr_array_init(outfile, type, values)
+ FILE *outfile;
+ int type;
+ chainp values;
+#else
+wr_array_init(FILE *outfile, int type, chainp values)
+#endif
+{
+ int size = typesize[type];
+ long index, main_index = 0;
+ int k;
+
+ if (type == TYCHAR) {
+ nice_printf(outfile, "\"");
+ k = 0;
+ if (Ansi != 1)
+ ch_ar_dim = -1;
+ }
+ else
+ nice_printf (outfile, "{ ");
+ while (values) {
+ struct Constblock Const;
+
+ index = ((long) ((chainp) values->datap)->datap) / size;
+ while (index > main_index) {
+
+/* Fill with zeros. The structure shorthand works because the compiler
+ will expand the "0" in braces to fill the size of the entire structure
+ */
+
+ switch (type) {
+ case TYREAL:
+ case TYDREAL:
+ nice_printf (outfile, "0.0,");
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ nice_printf (outfile, "{0},");
+ break;
+ case TYCHAR:
+ nice_printf(outfile, " ");
+ break;
+ default:
+ nice_printf (outfile, "0,");
+ break;
+ } /* switch */
+ main_index++;
+ } /* while index > main_index */
+
+ if (index < main_index)
+ overlapping();
+ else switch (type) {
+ case TYCHAR:
+ { int this_char;
+
+ if (k == ch_ar_dim) {
+ nice_printf(outfile, "\" \"");
+ k = 0;
+ }
+ this_char = (int) ((chainp) values->datap)->
+ nextp->nextp->datap;
+ if ((int)((chainp)values->datap)->nextp->datap == TYBLANK) {
+ main_index += this_char;
+ k += this_char;
+ while(--this_char >= 0)
+ nice_printf(outfile, " ");
+ values = values -> nextp;
+ continue;
+ }
+ nice_printf(outfile, str_fmt[this_char], this_char);
+ k++;
+ } /* case TYCHAR */
+ break;
+
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYREAL:
+ case TYDREAL:
+ case TYLOGICAL:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ make_one_const(type, &Const.Const, values);
+ Const.vtype = type;
+ Const.vstg = ONEOF(type, MSKREAL|MSKCOMPLEX) != 0;
+ out_const(outfile, &Const);
+ break;
+ default:
+ erri("wr_array_init: bad type '%d'", type);
+ break;
+ } /* switch */
+ values = values->nextp;
+
+ main_index++;
+ if (values && type != TYCHAR)
+ nice_printf (outfile, ",");
+ } /* while values */
+
+ if (type == TYCHAR) {
+ nice_printf(outfile, "\"");
+ }
+ else
+ nice_printf (outfile, " }");
+} /* wr_array_init */
+
+
+ static void
+#ifdef KR_headers
+make_one_const(type, storage, values)
+ int type;
+ union Constant *storage;
+ chainp values;
+#else
+make_one_const(int type, union Constant *storage, chainp values)
+#endif
+{
+ union Constant *Const;
+ register char **L;
+
+ if (type == TYCHAR) {
+ char *str, *str_ptr;
+ chainp v, prev;
+ int b = 0, k, main_index = 0;
+
+/* Find the max length of init string, by finding the highest offset
+ value stored in the list of initial values */
+
+ for(k = 1, prev = CHNULL, v = values; v; prev = v, v = v->nextp)
+ ;
+ if (prev != CHNULL)
+ k = ((int) (((chainp) prev->datap)->datap)) + 2;
+ /* + 2 above for null char at end */
+ str = Alloc (k);
+ for (str_ptr = str; values; str_ptr++) {
+ int index = (int) (((chainp) values->datap)->datap);
+
+ if (index < main_index)
+ overlapping();
+ while (index > main_index++)
+ *str_ptr++ = ' ';
+
+ k = (int) (((chainp) values->datap)->nextp->nextp->datap);
+ if ((int)((chainp)values->datap)->nextp->datap == TYBLANK) {
+ b = k;
+ break;
+ }
+ *str_ptr = k;
+ values = values -> nextp;
+ } /* for str_ptr */
+ *str_ptr = '\0';
+ Const = storage;
+ Const -> ccp = str;
+ Const -> ccp1.blanks = b;
+ charlen = str_ptr - str;
+ } else {
+ int i = 0;
+ chainp vals;
+
+ vals = ((chainp)values->datap)->nextp->nextp;
+ if (vals) {
+ L = (char **)storage;
+ do L[i++] = vals->datap;
+ while(vals = vals->nextp);
+ }
+
+ } /* else */
+
+} /* make_one_const */
+
+
+ int
+#ifdef KR_headers
+rdname(infile, vargroupp, name)
+ FILE *infile;
+ int *vargroupp;
+ char *name;
+#else
+rdname(FILE *infile, int *vargroupp, char *name)
+#endif
+{
+ register int i, c;
+
+ c = getc (infile);
+
+ if (feof (infile))
+ return NO;
+
+ *vargroupp = c - '0';
+ for (i = 1;; i++) {
+ if (i >= NAME_MAX)
+ Fatal("rdname: oversize name");
+ c = getc (infile);
+ if (feof (infile))
+ return NO;
+ if (c == '\t')
+ break;
+ *name++ = c;
+ }
+ *name = 0;
+ return YES;
+} /* rdname */
+
+ int
+#ifdef KR_headers
+rdlong(infile, n)
+ FILE *infile;
+ ftnint *n;
+#else
+rdlong(FILE *infile, ftnint *n)
+#endif
+{
+ register int c;
+
+ for (c = getc (infile); !feof (infile) && isspace (c); c = getc (infile))
+ ;
+
+ if (feof (infile))
+ return NO;
+
+ for (*n = 0; isdigit (c); c = getc (infile))
+ *n = 10 * (*n) + c - '0';
+ return YES;
+} /* rdlong */
+
+
+ static int
+#ifdef KR_headers
+memno2info(memno, info)
+ int memno;
+ Namep *info;
+#else
+memno2info(int memno, Namep *info)
+#endif
+{
+ chainp this_var;
+ extern chainp new_vars;
+ extern struct Hashentry *hashtab, *lasthash;
+ struct Hashentry *entry;
+
+ for (this_var = new_vars; this_var; this_var = this_var -> nextp) {
+ Addrp var = (Addrp) this_var->datap;
+
+ if (var == (Addrp) NULL)
+ Fatal("memno2info: null variable");
+ else if (var -> tag != TADDR)
+ Fatal("memno2info: bad tag");
+ if (memno == var -> memno) {
+ *info = (Namep) var;
+ return 1;
+ } /* if memno == var -> memno */
+ } /* for this_var = new_vars */
+
+ for (entry = hashtab; entry < lasthash; ++entry) {
+ Namep var = entry -> varp;
+
+ if (var && var -> vardesc.varno == memno && var -> vstg == STGINIT) {
+ *info = (Namep) var;
+ return 0;
+ } /* if entry -> vardesc.varno == memno */
+ } /* for entry = hashtab */
+
+ Fatal("memno2info: couldn't find memno");
+ return 0;
+} /* memno2info */
+
+ static chainp
+#ifdef KR_headers
+do_string(outfile, v, nloc)
+ FILE *outfile;
+ register chainp v;
+ ftnint *nloc;
+#else
+do_string(FILE *outfile, register chainp v, ftnint *nloc)
+#endif
+{
+ register chainp cp, v0;
+ ftnint dloc, k, loc;
+ unsigned long uk;
+ char buf[8], *comma;
+
+ nice_printf(outfile, "{");
+ cp = (chainp)v->datap;
+ loc = (ftnint)cp->datap;
+ comma = "";
+ for(v0 = v;;) {
+ switch((int)cp->nextp->datap) {
+ case TYBLANK:
+ k = (ftnint)cp->nextp->nextp->datap;
+ loc += k;
+ while(--k >= 0) {
+ nice_printf(outfile, "%s' '", comma);
+ comma = ", ";
+ }
+ break;
+ case TYCHAR:
+ uk = (ftnint)cp->nextp->nextp->datap;
+ sprintf(buf, chr_fmt[uk], uk);
+ nice_printf(outfile, "%s'%s'", comma, buf);
+ comma = ", ";
+ loc++;
+ break;
+ default:
+ goto done;
+ }
+ v0 = v;
+ if (!(v = v->nextp) || !(cp = (chainp)v->datap))
+ break;
+ dloc = (ftnint)cp->datap;
+ if (loc != dloc)
+ break;
+ }
+ done:
+ nice_printf(outfile, "}");
+ *nloc = loc;
+ return v0;
+ }
+
+ static chainp
+#ifdef KR_headers
+Ado_string(outfile, v, nloc)
+ FILE *outfile;
+ register chainp v;
+ ftnint *nloc;
+#else
+Ado_string(FILE *outfile, register chainp v, ftnint *nloc)
+#endif
+{
+ register chainp cp, v0;
+ ftnint dloc, k, loc;
+
+ nice_printf(outfile, "\"");
+ cp = (chainp)v->datap;
+ loc = (ftnint)cp->datap;
+ for(v0 = v;;) {
+ switch((int)cp->nextp->datap) {
+ case TYBLANK:
+ k = (ftnint)cp->nextp->nextp->datap;
+ loc += k;
+ while(--k >= 0)
+ nice_printf(outfile, " ");
+ break;
+ case TYCHAR:
+ k = (ftnint)cp->nextp->nextp->datap;
+ nice_printf(outfile, str_fmt[k], k);
+ loc++;
+ break;
+ default:
+ goto done;
+ }
+ v0 = v;
+ if (!(v = v->nextp) || !(cp = (chainp)v->datap))
+ break;
+ dloc = (ftnint)cp->datap;
+ if (loc != dloc)
+ break;
+ }
+ done:
+ nice_printf(outfile, "\"");
+ *nloc = loc;
+ return v0;
+ }
+
+ static char *
+#ifdef KR_headers
+Len(L, type)
+ long L;
+ int type;
+#else
+Len(long L, int type)
+#endif
+{
+ static char buf[24];
+ if (L == 1 && type != TYCHAR)
+ return "";
+ sprintf(buf, "[%ld]", L);
+ return buf;
+ }
+
+ void
+#ifdef KR_headers
+wr_equiv_init(outfile, memno, Values, iscomm)
+ FILE *outfile;
+ int memno;
+ chainp *Values;
+ int iscomm;
+#else
+wr_equiv_init(FILE *outfile, int memno, chainp *Values, int iscomm)
+#endif
+{
+ struct Equivblock *eqv;
+ int btype, curtype, dtype, filltype, filltype1, j, k, wasblank, xtype;
+ static char Blank[] = "";
+ register char *comma = Blank;
+ register chainp cp, v;
+ chainp sentinel, values, v1, vlast;
+ ftnint L, L1, dL, dloc, loc, loc0;
+ union Constant Const;
+ char imag_buf[50], real_buf[50];
+ int szshort = typesize[TYSHORT];
+ static char typepref[] = {0, 0, TYINT1, TYSHORT, TYLONG,
+#ifdef TYQUAD
+ TYQUAD,
+#endif
+ TYREAL, TYDREAL, TYREAL, TYDREAL,
+ TYLOGICAL1, TYLOGICAL2,
+ TYLOGICAL, TYCHAR};
+ static char basetype[] = {0, 0, TYCHAR, TYSHORT, TYLONG,
+#ifdef TYQUAD
+ TYDREAL,
+#endif
+ TYLONG, TYDREAL, TYLONG, TYDREAL,
+ TYCHAR, TYSHORT,
+ TYLONG, TYCHAR};
+ extern int htype;
+ char *z;
+
+ /* add sentinel */
+ if (iscomm) {
+ L = extsymtab[memno].maxleng;
+ xtype = extsymtab[memno].extype;
+ }
+ else {
+ eqv = &eqvclass[memno];
+ L = eqv->eqvtop - eqv->eqvbottom;
+ xtype = eqv->eqvtype;
+ }
+
+ if (halign && typealign[typepref[xtype]] < typealign[htype])
+ xtype = htype;
+ *Values = values = revchain(vlast = *Values);
+
+ if (xtype != TYCHAR) {
+
+ /* unless the data include a value of the appropriate
+ * type, we add an extra element in an attempt
+ * to force correct alignment */
+
+ btype = basetype[xtype];
+ loc = 0;
+ for(v = *Values;;v = v->nextp) {
+ if (!v) {
+ dtype = typepref[xtype];
+ z = ISREAL(dtype) ? cpstring("0.") : (char *)0;
+ k = typesize[dtype];
+ if (j = L % k)
+ L += k - j;
+ v = mkchain((char *)L,
+ mkchain((char *)LONG_CAST dtype,
+ mkchain(z, CHNULL)));
+ vlast = vlast->nextp =
+ mkchain((char *)v, CHNULL);
+ L += k;
+ break;
+ }
+ cp = (chainp)v->datap;
+ if (basetype[(int)cp->nextp->datap] == btype)
+ break;
+ dloc = (ftnint)cp->datap;
+ L1 = dloc - loc;
+ if (L1 > 0
+ && !(L1 % szshort)
+ && !(loc % szshort)
+ && btype <= type_choice[L1/szshort % 4]
+ && btype <= type_choice[loc/szshort % 4])
+ break;
+ dtype = (int)cp->nextp->datap;
+ loc = dloc + dtype == TYBLANK
+ ? (ftnint)cp->nextp->nextp->datap
+ : typesize[dtype];
+ }
+ }
+ sentinel = mkchain((char *)L, mkchain((char *)TYERROR,CHNULL));
+ vlast->nextp = mkchain((char *)sentinel, CHNULL);
+
+ /* use doublereal fillers only if there are doublereal values */
+
+ k = TYLONG;
+ for(v = values; v; v = v->nextp)
+ if (ONEOF((int)((chainp)v->datap)->nextp->datap,
+ M(TYDREAL)|M(TYDCOMPLEX))) {
+ k = TYDREAL;
+ break;
+ }
+ type_choice[0] = k;
+
+ nice_printf(outfile, "%sstruct {\n", iscomm ? "" : "static ");
+ next_tab(outfile);
+ loc = loc0 = k = 0;
+ curtype = -1;
+ for(v = values; v; v = v->nextp) {
+ cp = (chainp)v->datap;
+ dloc = (ftnint)cp->datap;
+ L = dloc - loc;
+ if (L < 0) {
+ overlapping();
+ if ((int)cp->nextp->datap != TYERROR) {
+ v1 = cp;
+ frchain(&v1);
+ v->datap = 0;
+ }
+ continue;
+ }
+ dtype = (int)cp->nextp->datap;
+ if (dtype == TYBLANK) {
+ dtype = TYCHAR;
+ wasblank = 1;
+ }
+ else
+ wasblank = 0;
+ if (curtype != dtype || L > 0) {
+ if (curtype != -1) {
+ L1 = (loc - loc0)/dL;
+ nice_printf(outfile, "%s e_%d%s;\n",
+ typename[curtype], ++k,
+ Len(L1,curtype));
+ }
+ curtype = dtype;
+ loc0 = dloc;
+ }
+ if (L > 0) {
+ if (xtype == TYCHAR)
+ filltype = TYCHAR;
+ else {
+ filltype = L % szshort ? TYCHAR
+ : type_choice[L/szshort % 4];
+ filltype1 = loc % szshort ? TYCHAR
+ : type_choice[loc/szshort % 4];
+ if (typesize[filltype] > typesize[filltype1])
+ filltype = filltype1;
+ }
+ L1 = L / typesize[filltype];
+ nice_printf(outfile, "%s fill_%d[%ld];\n",
+ typename[filltype], ++k, L1);
+ loc = dloc;
+ }
+ if (wasblank) {
+ loc += (ftnint)cp->nextp->nextp->datap;
+ dL = 1;
+ }
+ else {
+ dL = typesize[dtype];
+ loc += dL;
+ }
+ }
+ nice_printf(outfile, "} %s = { ", iscomm
+ ? extsymtab[memno].cextname
+ : equiv_name(eqvmemno, CNULL));
+ loc = 0;
+ for(v = values; ; v = v->nextp) {
+ cp = (chainp)v->datap;
+ if (!cp)
+ continue;
+ dtype = (int)cp->nextp->datap;
+ if (dtype == TYERROR)
+ break;
+ dloc = (ftnint)cp->datap;
+ if (dloc > loc) {
+ nice_printf(outfile, "%s{0}", comma);
+ comma = ", ";
+ loc = dloc;
+ }
+ if (comma != Blank)
+ nice_printf(outfile, ", ");
+ comma = ", ";
+ if (dtype == TYCHAR || dtype == TYBLANK) {
+ v = Ansi == 1 ? Ado_string(outfile, v, &loc)
+ : do_string(outfile, v, &loc);
+ continue;
+ }
+ make_one_const(dtype, &Const, v);
+ switch(dtype) {
+ case TYLOGICAL:
+ case TYLOGICAL2:
+ case TYLOGICAL1:
+ if (Const.ci < 0 || Const.ci > 1)
+ errl(
+ "wr_equiv_init: unexpected logical value %ld",
+ Const.ci);
+ nice_printf(outfile,
+ Const.ci ? "TRUE_" : "FALSE_");
+ break;
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ nice_printf(outfile, "%ld", Const.ci);
+ break;
+ case TYREAL:
+ nice_printf(outfile, "%s",
+ flconst(real_buf, Const.cds[0]));
+ break;
+ case TYDREAL:
+ nice_printf(outfile, "%s", Const.cds[0]);
+ break;
+ case TYCOMPLEX:
+ nice_printf(outfile, "%s, %s",
+ flconst(real_buf, Const.cds[0]),
+ flconst(imag_buf, Const.cds[1]));
+ break;
+ case TYDCOMPLEX:
+ nice_printf(outfile, "%s, %s",
+ Const.cds[0], Const.cds[1]);
+ break;
+ default:
+ erri("unexpected type %d in wr_equiv_init",
+ dtype);
+ }
+ loc += typesize[dtype];
+ }
+ nice_printf(outfile, " };\n\n");
+ prev_tab(outfile);
+ frchain(&sentinel);
+ }
diff --git a/usr.bin/f2c/ftypes.h b/usr.bin/f2c/ftypes.h
new file mode 100644
index 0000000..80d2deb
--- /dev/null
+++ b/usr.bin/f2c/ftypes.h
@@ -0,0 +1,51 @@
+
+/* variable types (stored in the vtype field of expptr)
+ * numeric assumptions:
+ * int < reals < complexes
+ * TYDREAL-TYREAL = TYDCOMPLEX-TYCOMPLEX
+ */
+
+#ifdef NO_TYQUAD
+#undef TYQUAD
+#define TYQUAD_inc 0
+#else
+#define TYQUAD 5
+#define TYQUAD_inc 1
+#endif
+
+#define TYUNKNOWN 0
+#define TYADDR 1
+#define TYINT1 2
+#define TYSHORT 3
+#define TYLONG 4
+/* #define TYQUAD 5 */
+#define TYREAL (5+TYQUAD_inc)
+#define TYDREAL (6+TYQUAD_inc)
+#define TYCOMPLEX (7+TYQUAD_inc)
+#define TYDCOMPLEX (8+TYQUAD_inc)
+#define TYLOGICAL1 (9+TYQUAD_inc)
+#define TYLOGICAL2 (10+TYQUAD_inc)
+#define TYLOGICAL (11+TYQUAD_inc)
+#define TYCHAR (12+TYQUAD_inc)
+#define TYSUBR (13+TYQUAD_inc)
+#define TYERROR (14+TYQUAD_inc)
+#define TYCILIST (15+TYQUAD_inc)
+#define TYICILIST (16+TYQUAD_inc)
+#define TYOLIST (17+TYQUAD_inc)
+#define TYCLLIST (18+TYQUAD_inc)
+#define TYALIST (19+TYQUAD_inc)
+#define TYINLIST (20+TYQUAD_inc)
+#define TYVOID (21+TYQUAD_inc)
+#define TYLABEL (22+TYQUAD_inc)
+#define TYFTNLEN (23+TYQUAD_inc)
+/* TYVOID is not in any tables. */
+
+/* NTYPES, NTYPES0 -- Total number of types, used to allocate tables indexed by
+ type. Such tables can include the size (in bytes) of objects of a given
+ type, or labels for returning objects of different types from procedures
+ (see array rtvlabels) */
+
+#define NTYPES TYVOID
+#define NTYPES0 TYCILIST
+#define TYBLANK TYSUBR /* Huh? */
+
diff --git a/usr.bin/f2c/gram.c b/usr.bin/f2c/gram.c
new file mode 100644
index 0000000..99ac190e
--- /dev/null
+++ b/usr.bin/f2c/gram.c
@@ -0,0 +1,1829 @@
+# define SEOS 1
+# define SCOMMENT 2
+# define SLABEL 3
+# define SUNKNOWN 4
+# define SHOLLERITH 5
+# define SICON 6
+# define SRCON 7
+# define SDCON 8
+# define SBITCON 9
+# define SOCTCON 10
+# define SHEXCON 11
+# define STRUE 12
+# define SFALSE 13
+# define SNAME 14
+# define SNAMEEQ 15
+# define SFIELD 16
+# define SSCALE 17
+# define SINCLUDE 18
+# define SLET 19
+# define SASSIGN 20
+# define SAUTOMATIC 21
+# define SBACKSPACE 22
+# define SBLOCK 23
+# define SCALL 24
+# define SCHARACTER 25
+# define SCLOSE 26
+# define SCOMMON 27
+# define SCOMPLEX 28
+# define SCONTINUE 29
+# define SDATA 30
+# define SDCOMPLEX 31
+# define SDIMENSION 32
+# define SDO 33
+# define SDOUBLE 34
+# define SELSE 35
+# define SELSEIF 36
+# define SEND 37
+# define SENDFILE 38
+# define SENDIF 39
+# define SENTRY 40
+# define SEQUIV 41
+# define SEXTERNAL 42
+# define SFORMAT 43
+# define SFUNCTION 44
+# define SGOTO 45
+# define SASGOTO 46
+# define SCOMPGOTO 47
+# define SARITHIF 48
+# define SLOGIF 49
+# define SIMPLICIT 50
+# define SINQUIRE 51
+# define SINTEGER 52
+# define SINTRINSIC 53
+# define SLOGICAL 54
+# define SNAMELIST 55
+# define SOPEN 56
+# define SPARAM 57
+# define SPAUSE 58
+# define SPRINT 59
+# define SPROGRAM 60
+# define SPUNCH 61
+# define SREAD 62
+# define SREAL 63
+# define SRETURN 64
+# define SREWIND 65
+# define SSAVE 66
+# define SSTATIC 67
+# define SSTOP 68
+# define SSUBROUTINE 69
+# define STHEN 70
+# define STO 71
+# define SUNDEFINED 72
+# define SWRITE 73
+# define SLPAR 74
+# define SRPAR 75
+# define SEQUALS 76
+# define SCOLON 77
+# define SCOMMA 78
+# define SCURRENCY 79
+# define SPLUS 80
+# define SMINUS 81
+# define SSTAR 82
+# define SSLASH 83
+# define SPOWER 84
+# define SCONCAT 85
+# define SAND 86
+# define SOR 87
+# define SNEQV 88
+# define SEQV 89
+# define SNOT 90
+# define SEQ 91
+# define SLT 92
+# define SGT 93
+# define SLE 94
+# define SGE 95
+# define SNE 96
+# define SENDDO 97
+# define SWHILE 98
+# define SSLASHD 99
+
+/* # line 124 "gram.in" */
+#include "defs.h"
+#include "p1defs.h"
+
+static int nstars; /* Number of labels in an
+ alternate return CALL */
+static int datagripe;
+static int ndim;
+static int vartype;
+int new_dcl;
+static ftnint varleng;
+static struct Dims dims[MAXDIM+1];
+extern struct Labelblock **labarray; /* Labels in an alternate
+ return CALL */
+extern int maxlablist;
+
+/* The next two variables are used to verify that each statement might be reached
+ during runtime. lastwasbranch is tested only in the defintion of the
+ stat: nonterminal. */
+
+int lastwasbranch = NO;
+static int thiswasbranch = NO;
+extern ftnint yystno;
+extern flag intonly;
+static chainp datastack;
+extern long laststfcn, thisstno;
+extern int can_include; /* for netlib */
+
+ftnint convci();
+Addrp nextdata();
+expptr mklogcon(), mkaddcon(), mkrealcon(), mkstrcon(), mkbitcon();
+expptr mkcxcon();
+struct Listblock *mklist();
+struct Listblock *mklist();
+struct Impldoblock *mkiodo();
+Extsym *comblock();
+#define ESNULL (Extsym *)0
+#define NPNULL (Namep)0
+#define LBNULL (struct Listblock *)0
+extern void freetemps(), make_param();
+
+ static void
+pop_datastack() {
+ chainp d0 = datastack;
+ if (d0->datap)
+ curdtp = (chainp)d0->datap;
+ datastack = d0->nextp;
+ d0->nextp = 0;
+ frchain(&d0);
+ }
+
+
+/* # line 178 "gram.in" */
+typedef union {
+ int ival;
+ ftnint lval;
+ char *charpval;
+ chainp chval;
+ tagptr tagval;
+ expptr expval;
+ struct Labelblock *labval;
+ struct Nameblock *namval;
+ struct Eqvchain *eqvval;
+ Extsym *extval;
+ } YYSTYPE;
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+extern int yychar;
+typedef int yytabelem;
+extern yytabelem yyerrflag;
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 150
+#endif
+YYSTYPE yylval, yyval;
+# define YYERRCODE 256
+yytabelem yyexca[] ={
+-1, 1,
+ 0, -1,
+ -2, 0,
+-1, 20,
+ 1, 38,
+ -2, 228,
+-1, 24,
+ 1, 42,
+ -2, 228,
+-1, 122,
+ 6, 240,
+ -2, 228,
+-1, 150,
+ 1, 244,
+ -2, 188,
+-1, 174,
+ 1, 265,
+ 78, 265,
+ -2, 188,
+-1, 223,
+ 77, 173,
+ -2, 139,
+-1, 245,
+ 74, 228,
+ -2, 225,
+-1, 271,
+ 1, 286,
+ -2, 143,
+-1, 275,
+ 1, 295,
+ 78, 295,
+ -2, 145,
+-1, 328,
+ 77, 174,
+ -2, 141,
+-1, 358,
+ 1, 267,
+ 14, 267,
+ 74, 267,
+ 78, 267,
+ -2, 189,
+-1, 436,
+ 91, 0,
+ 92, 0,
+ 93, 0,
+ 94, 0,
+ 95, 0,
+ 96, 0,
+ -2, 153,
+-1, 453,
+ 1, 289,
+ 78, 289,
+ -2, 143,
+-1, 455,
+ 1, 291,
+ 78, 291,
+ -2, 143,
+-1, 457,
+ 1, 293,
+ 78, 293,
+ -2, 143,
+-1, 459,
+ 1, 296,
+ 78, 296,
+ -2, 144,
+-1, 504,
+ 78, 289,
+ -2, 143,
+ };
+# define YYNPROD 301
+# define YYLAST 1346
+yytabelem yyact[]={
+
+ 237, 274, 471, 317, 316, 412, 420, 297, 470, 399,
+ 413, 397, 386, 357, 398, 266, 128, 356, 273, 252,
+ 292, 5, 116, 295, 326, 303, 222, 99, 184, 121,
+ 195, 229, 17, 203, 270, 304, 313, 199, 201, 118,
+ 94, 202, 396, 104, 210, 183, 236, 101, 106, 234,
+ 264, 103, 111, 336, 260, 95, 96, 97, 165, 166,
+ 334, 335, 336, 395, 105, 311, 309, 190, 130, 131,
+ 132, 133, 120, 135, 119, 114, 157, 129, 157, 475,
+ 103, 272, 334, 335, 336, 396, 521, 103, 278, 483,
+ 535, 165, 166, 334, 335, 336, 342, 341, 340, 339,
+ 338, 137, 343, 345, 344, 347, 346, 348, 450, 258,
+ 259, 260, 539, 165, 166, 258, 259, 260, 261, 525,
+ 102, 522, 155, 409, 155, 186, 187, 103, 408, 117,
+ 165, 166, 258, 259, 260, 318, 100, 527, 484, 188,
+ 446, 185, 480, 230, 240, 240, 194, 193, 290, 120,
+ 211, 119, 462, 481, 157, 294, 482, 257, 157, 243,
+ 468, 214, 463, 469, 461, 464, 460, 239, 241, 220,
+ 215, 218, 157, 219, 213, 165, 166, 334, 335, 336,
+ 342, 341, 340, 157, 371, 452, 343, 345, 344, 347,
+ 346, 348, 443, 428, 377, 294, 102, 102, 102, 102,
+ 155, 189, 447, 149, 155, 446, 192, 103, 98, 196,
+ 197, 198, 277, 376, 320, 321, 206, 288, 155, 289,
+ 300, 375, 299, 324, 315, 328, 275, 275, 330, 155,
+ 310, 333, 196, 216, 217, 350, 269, 207, 308, 352,
+ 353, 333, 100, 177, 354, 349, 323, 112, 245, 257,
+ 247, 110, 157, 417, 286, 287, 418, 362, 157, 157,
+ 157, 157, 157, 257, 257, 109, 108, 268, 279, 280,
+ 281, 265, 107, 355, 4, 333, 427, 465, 378, 370,
+ 170, 172, 176, 257, 165, 166, 258, 259, 260, 261,
+ 102, 406, 232, 293, 407, 381, 422, 390, 155, 400,
+ 391, 223, 419, 422, 155, 155, 155, 155, 155, 117,
+ 221, 314, 392, 319, 387, 359, 372, 196, 360, 373,
+ 374, 333, 333, 536, 350, 333, 275, 250, 424, 333,
+ 405, 333, 410, 532, 230, 432, 433, 434, 435, 436,
+ 437, 438, 439, 440, 441, 403, 331, 156, 401, 332,
+ 531, 333, 530, 333, 333, 333, 388, 526, 380, 529,
+ 524, 157, 257, 333, 431, 492, 257, 257, 257, 257,
+ 257, 382, 383, 235, 426, 384, 358, 494, 296, 333,
+ 448, 165, 166, 258, 259, 260, 261, 451, 165, 166,
+ 258, 259, 260, 261, 103, 445, 472, 400, 421, 191,
+ 402, 196, 103, 150, 307, 174, 285, 155, 474, 246,
+ 476, 416, 467, 466, 242, 226, 223, 200, 212, 136,
+ 209, 486, 171, 488, 490, 275, 275, 275, 141, 240,
+ 496, 429, 329, 333, 333, 333, 333, 333, 333, 333,
+ 333, 333, 333, 403, 497, 479, 401, 403, 487, 154,
+ 257, 154, 495, 493, 306, 485, 502, 454, 456, 458,
+ 500, 491, 268, 499, 505, 506, 507, 103, 451, 271,
+ 271, 472, 30, 333, 414, 501, 400, 508, 511, 509,
+ 387, 244, 208, 510, 516, 514, 515, 333, 517, 333,
+ 513, 333, 520, 293, 518, 225, 240, 333, 402, 523,
+ 92, 248, 402, 528, 6, 262, 123, 249, 81, 80,
+ 275, 275, 275, 79, 534, 533, 479, 78, 173, 263,
+ 314, 77, 403, 76, 537, 401, 351, 154, 75, 333,
+ 282, 154, 60, 49, 48, 333, 45, 33, 333, 538,
+ 113, 205, 454, 456, 458, 154, 267, 165, 166, 334,
+ 335, 336, 342, 540, 503, 411, 154, 204, 394, 393,
+ 298, 478, 503, 503, 503, 134, 389, 312, 115, 379,
+ 26, 25, 24, 23, 302, 22, 305, 402, 21, 385,
+ 284, 9, 503, 8, 7, 2, 519, 301, 20, 319,
+ 164, 51, 489, 291, 228, 327, 325, 415, 91, 361,
+ 255, 53, 337, 19, 55, 365, 366, 367, 368, 369,
+ 37, 224, 3, 1, 0, 351, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 154, 0, 0, 0, 0,
+ 0, 154, 154, 154, 154, 154, 0, 0, 0, 267,
+ 0, 512, 267, 267, 165, 166, 334, 335, 336, 342,
+ 341, 340, 339, 338, 0, 343, 345, 344, 347, 346,
+ 348, 165, 166, 334, 335, 336, 342, 341, 453, 455,
+ 457, 0, 343, 345, 344, 347, 346, 348, 0, 0,
+ 305, 0, 459, 0, 0, 0, 0, 165, 166, 334,
+ 335, 336, 342, 341, 340, 339, 338, 351, 343, 345,
+ 344, 347, 346, 348, 444, 0, 0, 0, 449, 165,
+ 166, 334, 335, 336, 342, 341, 340, 339, 338, 0,
+ 343, 345, 344, 347, 346, 348, 165, 166, 334, 335,
+ 336, 342, 0, 0, 154, 0, 498, 343, 345, 344,
+ 347, 346, 348, 0, 0, 267, 0, 0, 0, 0,
+ 0, 442, 0, 504, 455, 457, 165, 166, 334, 335,
+ 336, 342, 341, 340, 339, 338, 0, 343, 345, 344,
+ 347, 346, 348, 0, 0, 0, 0, 0, 0, 430,
+ 0, 477, 0, 305, 165, 166, 334, 335, 336, 342,
+ 341, 340, 339, 338, 0, 343, 345, 344, 347, 346,
+ 348, 423, 0, 0, 0, 0, 165, 166, 334, 335,
+ 336, 342, 341, 340, 339, 338, 0, 343, 345, 344,
+ 347, 346, 348, 0, 0, 0, 267, 0, 0, 0,
+ 0, 165, 166, 334, 335, 336, 342, 341, 340, 339,
+ 338, 12, 343, 345, 344, 347, 346, 348, 0, 0,
+ 0, 0, 0, 0, 305, 10, 56, 46, 73, 85,
+ 14, 61, 70, 90, 38, 66, 47, 42, 68, 72,
+ 31, 67, 35, 34, 11, 87, 36, 18, 41, 39,
+ 28, 16, 57, 58, 59, 50, 54, 43, 88, 64,
+ 40, 69, 44, 89, 29, 62, 84, 13, 0, 82,
+ 65, 52, 86, 27, 74, 63, 15, 0, 0, 71,
+ 83, 160, 161, 162, 163, 169, 168, 167, 158, 159,
+ 103, 0, 160, 161, 162, 163, 169, 168, 167, 158,
+ 159, 103, 0, 0, 32, 160, 161, 162, 163, 169,
+ 168, 167, 158, 159, 103, 0, 160, 161, 162, 163,
+ 169, 168, 167, 158, 159, 103, 0, 160, 161, 162,
+ 163, 169, 168, 167, 158, 159, 103, 0, 160, 161,
+ 162, 163, 169, 168, 167, 158, 159, 103, 0, 0,
+ 233, 0, 0, 0, 0, 0, 165, 166, 363, 0,
+ 364, 233, 227, 0, 0, 0, 238, 165, 166, 231,
+ 0, 0, 0, 0, 233, 0, 0, 238, 0, 0,
+ 165, 166, 473, 0, 0, 233, 0, 0, 0, 0,
+ 238, 165, 166, 231, 0, 0, 233, 0, 0, 0,
+ 0, 238, 165, 166, 425, 0, 0, 233, 0, 0,
+ 0, 0, 238, 165, 166, 0, 0, 0, 0, 0,
+ 0, 0, 0, 238, 160, 161, 162, 163, 169, 168,
+ 167, 158, 159, 103, 0, 160, 161, 162, 163, 169,
+ 168, 167, 158, 159, 103, 160, 161, 162, 163, 169,
+ 168, 167, 158, 159, 103, 0, 0, 0, 160, 161,
+ 162, 163, 169, 168, 167, 158, 159, 103, 256, 0,
+ 93, 160, 161, 162, 163, 169, 168, 167, 158, 159,
+ 103, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 276, 0, 0, 0, 0, 0, 165,
+ 166, 0, 122, 0, 322, 125, 126, 127, 0, 238,
+ 165, 166, 0, 0, 0, 0, 0, 138, 139, 0,
+ 238, 140, 0, 142, 143, 144, 0, 251, 145, 146,
+ 147, 0, 148, 165, 166, 253, 0, 254, 0, 0,
+ 153, 0, 0, 0, 0, 0, 165, 166, 151, 0,
+ 152, 178, 179, 180, 181, 182, 160, 161, 162, 163,
+ 169, 168, 167, 158, 159, 103, 160, 161, 162, 163,
+ 169, 168, 167, 158, 159, 103, 160, 161, 162, 163,
+ 169, 168, 167, 158, 159, 103, 160, 161, 162, 163,
+ 169, 168, 167, 158, 159, 103, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 251, 0, 0, 0, 0,
+ 0, 165, 166, 283, 0, 153, 0, 0, 0, 0,
+ 0, 165, 166, 175, 0, 404, 0, 0, 0, 0,
+ 0, 165, 166, 56, 46, 251, 85, 0, 61, 0,
+ 90, 165, 166, 47, 73, 0, 0, 0, 70, 0,
+ 0, 66, 87, 0, 68, 72, 0, 67, 0, 57,
+ 58, 59, 50, 0, 0, 88, 0, 0, 0, 0,
+ 89, 0, 62, 84, 0, 64, 82, 69, 52, 86,
+ 0, 0, 63, 0, 124, 0, 65, 83, 0, 0,
+ 74, 0, 0, 0, 0, 71 };
+yytabelem yypact[]={
+
+-1000, 18, 503, 837,-1000,-1000,-1000,-1000,-1000,-1000,
+ 495,-1000,-1000,-1000,-1000,-1000,-1000, 164, 453, -35,
+ 194, 188, 187, 173, 58, 169, -8, 66,-1000,-1000,
+-1000,-1000,-1000,1264,-1000,-1000,-1000, -5,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000, 453,-1000,-1000,-1000,-1000,
+-1000, 354,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,1096, 348,1191, 348, 165,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000, 453, 453, 453, 453,-1000, 453,
+-1000, 325,-1000,-1000, 453,-1000, -11, 453, 453, 453,
+ 343,-1000,-1000,-1000, 453, 159,-1000,-1000,-1000,-1000,
+ 468, 346, 58,-1000,-1000, 344,-1000,-1000,-1000,-1000,
+ 66, 453, 453, 343,-1000,-1000, 234, 342, 489,-1000,
+ 341, 917, 963, 963, 340, 475, 453, 335, 453,-1000,
+-1000,-1000,-1000,1083,-1000,-1000, 308,1211,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,1083, 193, 158,-1000,-1000,1049,1049,-1000,-1000,
+-1000,-1000,1181, 332,-1000,-1000, 325, 325, 453,-1000,
+-1000, 73, 304,-1000, 58,-1000, 304,-1000,-1000,-1000,
+ 453,-1000, 380,-1000, 330,1273, -17, 66, -18, 453,
+ 475, 37, 963,1060,-1000, 453,-1000,-1000,-1000,-1000,
+-1000, 963,-1000, 963, 361,-1000, 963,-1000, 271,-1000,
+ 751, 475,-1000, 963,-1000,-1000,-1000, 963, 963,-1000,
+ 751,-1000, 963,-1000,-1000, 58, 475,-1000, 301, 240,
+-1000,1211,-1000,-1000,-1000, 906,-1000,1211,1211,1211,
+1211,1211, -30, 204, 106, 388,-1000,-1000, 388, 388,
+-1000, 143, 135, 116, 751,-1000,1049,-1000,-1000,-1000,
+-1000,-1000, 308,-1000,-1000, 300,-1000,-1000, 325,-1000,
+-1000, 222,-1000,-1000,-1000, -5,-1000, -36,1201, 453,
+-1000, 216,-1000, 45,-1000,-1000, 380, 460,-1000, 453,
+-1000,-1000, 178,-1000, 226,-1000,-1000,-1000, 324, 220,
+ 726, 751, 952,-1000, 751, 299, 199, 115, 751, 453,
+ 704,-1000, 941, 963, 963, 963, 963, 963, 963, 963,
+ 963, 963, 963,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+ 676, 114, -31, 646, 629, 321, 127,-1000,-1000,-1000,
+1083, 33, 751,-1000,-1000, 27, -30, -30, -30, 50,
+-1000, 388, 106, 107, 106,1049,1049,1049, 607, 88,
+ 86, 74,-1000,-1000,-1000, 87,-1000, 201,-1000, 304,
+-1000, 113,-1000, 85, 930,-1000,1201,-1000,-1000, -3,
+1070,-1000,-1000,-1000, 963,-1000,-1000, 453,-1000, 380,
+ 64, 78,-1000, 8,-1000, 60,-1000,-1000, 453, 963,
+ 58, 963, 963, 391,-1000, 290, 303, 963, 963,-1000,
+ 475,-1000, 0, -31, -31, -31, 467, 95, 95, 581,
+ 646, -22,-1000, 963,-1000, 475, 475, 58,-1000, 308,
+-1000,-1000, 388,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+1049,1049,1049,-1000, 466, 465, -5,-1000,-1000, 930,
+-1000,-1000, 564,-1000,-1000,1201,-1000,-1000,-1000,-1000,
+ 380,-1000, 460, 460, 453,-1000, 751, 37, 11, 43,
+ 751,-1000,-1000,-1000, 963, 285, 751, 41, 282, 62,
+-1000, 963, 284, 227, 282, 277, 275, 258,-1000,-1000,
+-1000,-1000, 930,-1000,-1000, 7, 248,-1000,-1000,-1000,
+-1000,-1000, 963,-1000,-1000, 475,-1000,-1000, 751,-1000,
+-1000,-1000,-1000,-1000, 751,-1000,-1000, 751, 34, 475,
+-1000 };
+yytabelem yypgo[]={
+
+ 0, 613, 612, 13, 611, 81, 15, 32, 610, 604,
+ 603, 10, 0, 602, 601, 600, 16, 598, 35, 25,
+ 597, 596, 595, 3, 4, 594, 67, 593, 592, 50,
+ 34, 18, 26, 101, 20, 591, 30, 373, 1, 292,
+ 24, 347, 327, 2, 9, 14, 31, 49, 46, 590,
+ 588, 39, 28, 45, 587, 585, 584, 583, 581,1100,
+ 40, 580, 579, 12, 578, 575, 573, 572, 571, 570,
+ 568, 29, 567, 27, 566, 23, 41, 7, 44, 6,
+ 37, 565, 38, 561, 560, 11, 22, 36, 559, 558,
+ 8, 17, 33, 557, 555, 541, 5, 540, 472, 537,
+ 536, 534, 533, 532, 528, 203, 523, 521, 518, 517,
+ 513, 509, 88, 508, 507, 19 };
+yytabelem yyr1[]={
+
+ 0, 1, 1, 55, 55, 55, 55, 55, 55, 55,
+ 2, 56, 56, 56, 56, 56, 56, 56, 60, 52,
+ 33, 53, 53, 61, 61, 62, 62, 63, 63, 26,
+ 26, 26, 27, 27, 34, 34, 17, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 10,
+ 10, 10, 74, 7, 8, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 16, 16, 16, 50,
+ 50, 50, 50, 51, 51, 64, 64, 65, 65, 66,
+ 66, 80, 54, 54, 67, 67, 81, 82, 76, 83,
+ 84, 77, 77, 85, 85, 45, 45, 45, 70, 70,
+ 86, 86, 72, 72, 87, 36, 18, 18, 19, 19,
+ 75, 75, 89, 88, 88, 90, 90, 43, 43, 91,
+ 91, 3, 68, 68, 92, 92, 95, 93, 94, 94,
+ 96, 96, 11, 69, 69, 97, 20, 20, 71, 21,
+ 21, 22, 22, 38, 38, 38, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
+ 12, 12, 13, 13, 13, 13, 13, 13, 37, 37,
+ 37, 37, 32, 40, 40, 44, 44, 48, 48, 48,
+ 48, 48, 48, 48, 47, 49, 49, 49, 41, 41,
+ 42, 42, 42, 42, 42, 42, 42, 42, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 99, 23, 24,
+ 24, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 4, 100, 101, 101, 101, 101, 73, 73,
+ 35, 25, 25, 46, 46, 14, 14, 28, 28, 59,
+ 78, 79, 102, 103, 103, 103, 103, 103, 103, 103,
+ 103, 103, 103, 103, 103, 103, 103, 104, 111, 111,
+ 111, 106, 113, 113, 113, 108, 108, 105, 105, 114,
+ 114, 115, 115, 115, 115, 115, 115, 15, 107, 109,
+ 110, 110, 29, 29, 6, 6, 30, 30, 30, 31,
+ 31, 31, 31, 31, 31, 5, 5, 5, 5, 5,
+ 112 };
+yytabelem yyr2[]={
+
+ 0, 0, 3, 2, 2, 2, 3, 3, 2, 1,
+ 1, 3, 4, 3, 4, 4, 5, 3, 0, 1,
+ 1, 0, 1, 2, 3, 1, 3, 1, 3, 0,
+ 2, 3, 1, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 1, 5, 7,
+ 5, 5, 0, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 4, 6, 3,
+ 4, 5, 3, 1, 3, 3, 3, 3, 3, 3,
+ 3, 3, 1, 3, 3, 3, 0, 6, 0, 0,
+ 0, 2, 3, 1, 3, 1, 2, 1, 1, 3,
+ 1, 1, 1, 3, 3, 2, 1, 5, 1, 3,
+ 0, 3, 0, 2, 3, 1, 3, 1, 1, 1,
+ 3, 1, 3, 3, 4, 1, 0, 2, 1, 3,
+ 1, 3, 1, 1, 2, 4, 1, 3, 0, 0,
+ 1, 1, 3, 1, 3, 1, 1, 1, 3, 3,
+ 3, 3, 2, 3, 3, 3, 3, 3, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 4, 5, 5, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 5, 1, 1, 1, 1, 3,
+ 1, 1, 3, 3, 3, 3, 2, 3, 1, 7,
+ 4, 1, 2, 2, 6, 2, 2, 5, 3, 1,
+ 4, 4, 5, 2, 1, 1, 10, 1, 3, 4,
+ 3, 3, 1, 1, 3, 3, 7, 7, 0, 1,
+ 3, 1, 3, 1, 2, 1, 1, 1, 3, 0,
+ 0, 0, 1, 2, 2, 2, 2, 2, 2, 2,
+ 3, 4, 4, 2, 3, 1, 3, 3, 1, 1,
+ 1, 3, 1, 1, 1, 1, 1, 3, 3, 1,
+ 3, 1, 1, 1, 2, 2, 2, 1, 3, 3,
+ 4, 4, 1, 3, 1, 5, 1, 1, 1, 3,
+ 3, 3, 3, 3, 3, 1, 3, 5, 5, 5,
+ 0 };
+yytabelem yychk[]={
+
+-1000, -1, -55, -2, 256, 3, 1, -56, -57, -58,
+ 18, 37, 4, 60, 23, 69, 44, -7, 40, -10,
+ -50, -64, -65, -66, -67, -68, -69, 66, 43, 57,
+ -98, 33, 97, -99, 36, 35, 39, -8, 27, 42,
+ 53, 41, 30, 50, 55,-100, 20, 29,-101,-102,
+ 48, -35, 64, -14, 49, -9, 19, 45, 46, 47,
+-103, 24, 58, 68, 52, 63, 28, 34, 31, 54,
+ 25, 72, 32, 21, 67,-104,-106,-107,-109,-110,
+-111,-113, 62, 73, 59, 22, 65, 38, 51, 56,
+ 26, -17, 5, -59, -60, -60, -60, -60, 44, -73,
+ 78, -52, -33, 14, 78, 99, -73, 78, 78, 78,
+ 78, -73, 78, -97, 83, -70, -86, -33, -51, 85,
+ 83, -71, -59, -98, 70, -59, -59, -59, -16, 82,
+ -71, -71, -71, -71, -81, -71, -37, -33, -59, -59,
+ -59, 74, -59, -59, -59, -59, -59, -59, -59,-105,
+ -42, 82, 84, 74, -37, -48, -41, -12, 12, 13,
+ 5, 6, 7, 8, -49, 80, 81, 11, 10, 9,
+-105, 74,-105,-108, -42, 82,-105, 78, -59, -59,
+ -59, -59, -59, -53, -52, -53, -52, -52, -60, -33,
+ -26, 74, -33, -76, -51, -36, -33, -33, -33, -80,
+ 74, -82, -76, -92, -93, -95, -33, 78, 14, 74,
+ -78, -73, 74, -78, -36, -51, -33, -33, -80, -82,
+ -92, 76, -32, 74, -4, 6, 74, 75, -25, -46,
+ -38, 82, -39, 74, -47, -37, -48, -12, 90, -40,
+ -38, -40, 74, -3, 6, -33, 74, -33, -41,-114,
+ -42, 74,-115, 82, 84, -15, 15, -12, 82, 83,
+ 84, 85, -41, -41, -29, 78, -6, -37, 74, 78,
+ -30, -39, -5, -31, -38, -47, 74, -30,-112,-112,
+-112,-112, -41, 82, -61, 74, -26, -26, -52, -71,
+ 75, -27, -34, -33, 82, -75, 74, -77, -84, -73,
+ -75, -54, -37, -19, -18, -37, 74, 74, -7, 83,
+ -86, 83, -72, -87, -33, -3, -24, -23, 98, -33,
+ -38, -38, 74, -36, -38, -21, -40, -22, -38, 71,
+ -38, 75, 78, -12, 82, 83, 84, -13, 89, 88,
+ 87, 86, 85, 91, 93, 92, 95, 94, 96, -3,
+ -38, -39, -38, -38, -38, -73, -91, -3, 75, 75,
+ 78, -41, -38, 82, 84, -41, -41, -41, -41, -41,
+ 75, 78, -29, -29, -29, 78, 78, 78, -38, -39,
+ -5, -31,-112,-112, 75, -62, -63, 14, -26, -74,
+ 75, 78, -16, -88, -89, 99, 78, -85, -45, -44,
+ -12, -47, -33, -48, 74, -36, 75, 78, 83, 78,
+ -19, -94, -96, -11, 14, -20, -33, 75, 78, 76,
+ -79, 74, 76, 75, -79, 82, 75, 77, 78, -33,
+ 75, -46, -38, -38, -38, -38, -38, -38, -38, -38,
+ -38, -38, 75, 78, 75, 74, 78, 75,-115, -41,
+ 75, -6, 78, -39, -5, -39, -5, -39, -5, 75,
+ 78, 78, 78, 75, 78, 76, -75, -34, 75, 78,
+ -90, -43, -38, 82, -85, 82, -44, -37, -83, -18,
+ 78, 75, 78, 81, 78, -87, -38, -73, -38, -28,
+ -38, 70, 75, -32, 74, -40, -38, -3, -39, -91,
+ -3, -73, -23, -33, -39, -23, -23, -23, -63, 14,
+ -16, -90, 77, -45, -44, -77, -23, -96, -11, -33,
+ -24, 75, 78, -79, 75, 78, 75, 75, -38, 75,
+ 75, 75, 75, -43, -38, 83, 75, -38, -3, 78,
+ -3 };
+yytabelem yydef[]={
+
+ 1, -2, 0, 0, 9, 10, 2, 3, 4, 5,
+ 0, 239, 8, 18, 18, 18, 18, 228, 0, 37,
+ -2, 39, 40, 41, -2, 43, 44, 45, 47, 138,
+ 198, 239, 201, 0, 239, 239, 239, 66, 138, 138,
+ 138, 138, 86, 138, 133, 0, 239, 239, 214, 215,
+ 239, 217, 239, 239, 239, 54, 223, 239, 239, 239,
+ 242, 239, 235, 236, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 0, 0, 0, 0, 255,
+ 239, 239, 239, 239, 239, 258, 259, 260, 262, 263,
+ 264, 6, 36, 7, 21, 21, 0, 0, 18, 0,
+ 229, 29, 19, 20, 0, 88, 0, 229, 0, 0,
+ 0, 88, 126, 134, 0, 46, 98, 100, 101, 73,
+ 0, 0, -2, 202, 203, 0, 205, 206, 53, 240,
+ 0, 0, 0, 0, 88, 126, 0, 168, 0, 213,
+ 0, 0, 173, 173, 0, 0, 0, 0, 0, 243,
+ -2, 245, 246, 0, 190, 191, 0, 0, 177, 178,
+ 179, 180, 181, 182, 183, 160, 161, 185, 186, 187,
+ 247, 0, 248, 249, -2, 266, 253, 0, 300, 300,
+ 300, 300, 0, 11, 22, 13, 29, 29, 0, 138,
+ 17, 0, 110, 90, 228, 72, 110, 76, 78, 80,
+ 0, 85, 0, 123, 125, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 69, 0, 75, 77, 79, 84,
+ 122, 0, 169, -2, 0, 222, 0, 218, 0, 231,
+ 233, 0, 143, 0, 145, 146, 147, 0, 0, 220,
+ 174, 221, 0, 224, 121, -2, 0, 230, 271, 0,
+ 188, 0, 269, 272, 273, 0, 277, 0, 0, 0,
+ 0, 0, 196, 271, 250, 0, 282, 284, 0, 0,
+ 254, -2, 287, 288, 0, -2, 0, 256, 257, 261,
+ 278, 279, 300, 300, 12, 0, 14, 15, 29, 52,
+ 30, 0, 32, 34, 35, 66, 112, 0, 0, 0,
+ 105, 0, 82, 0, 108, 106, 0, 0, 127, 0,
+ 99, 74, 0, 102, 0, 241, 200, 209, 0, 0,
+ 0, 241, 0, 70, 211, 0, 0, 140, -2, 0,
+ 0, 219, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 162, 163, 164, 165, 166, 167, 234,
+ 0, 143, 152, 158, 0, 0, 0, 119, -2, 268,
+ 0, 0, 274, 275, 276, 192, 193, 194, 195, 197,
+ 267, 0, 252, 0, 251, 0, 0, 0, 0, 143,
+ 0, 0, 280, 281, 23, 0, 25, 27, 16, 110,
+ 31, 0, 50, 0, 0, 51, 0, 91, 93, 95,
+ 0, 97, 175, 176, 0, 71, 81, 0, 89, 0,
+ 0, 0, 128, 130, 132, 135, 136, 48, 0, 0,
+ 228, 0, 0, 0, 67, 0, 170, 173, 0, 212,
+ 0, 232, 148, 149, 150, 151, -2, 154, 155, 156,
+ 157, 159, 144, 0, 207, 0, 0, 228, 270, 271,
+ 189, 283, 0, -2, 290, -2, 292, -2, 294, -2,
+ 0, 0, 0, 24, 0, 0, 66, 33, 111, 0,
+ 113, 115, 118, 117, 92, 0, 96, 83, 90, 109,
+ 0, 124, 0, 0, 0, 103, 104, 0, 0, 208,
+ 237, 204, 241, 171, 173, 0, 142, 0, 143, 0,
+ 120, 0, 0, 168, -2, 0, 0, 0, 26, 28,
+ 49, 114, 0, 94, 95, 0, 0, 129, 131, 137,
+ 199, 210, 0, 68, 172, 0, 184, 226, 227, 285,
+ 297, 298, 299, 116, 118, 87, 107, 238, 0, 0,
+ 216 };
+# ifdef YYDEBUG
+# include "y.debug"
+# endif
+
+# define YYFLAG -1000
+# define YYERROR goto yyerrlab
+# define YYACCEPT return(0)
+# define YYABORT return(1)
+
+/* parser for yacc output */
+
+#ifdef YYDEBUG
+int yydebug = 0; /* 1 for debugging */
+#endif
+YYSTYPE yyv[YYMAXDEPTH]; /* where the values are stored */
+int yychar = -1; /* current input token number */
+int yynerrs = 0; /* number of errors */
+yytabelem yyerrflag = 0; /* error recovery flag */
+
+yyparse()
+{ yytabelem yys[YYMAXDEPTH];
+ int yyj, yym;
+ register YYSTYPE *yypvt;
+ register int yystate, yyn;
+ register yytabelem *yyps;
+ register YYSTYPE *yypv;
+ register yytabelem *yyxi;
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyps= &yys[-1];
+ yypv= &yyv[-1];
+
+yystack: /* put a state and value onto the stack */
+#ifdef YYDEBUG
+ if(yydebug >= 3)
+ if(yychar < 0 || yytoknames[yychar] == 0)
+ printf("char %d in %s", yychar, yystates[yystate]);
+ else
+ printf("%s in %s", yytoknames[yychar], yystates[yystate]);
+#endif
+ if( ++yyps >= &yys[YYMAXDEPTH] ) {
+ yyerror( "yacc stack overflow" );
+ return(1);
+ }
+ *yyps = yystate;
+ ++yypv;
+ *yypv = yyval;
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG) goto yydefault; /* simple state */
+ if(yychar<0) {
+ yychar = yylex();
+#ifdef YYDEBUG
+ if(yydebug >= 2) {
+ if(yychar <= 0)
+ printf("lex EOF\n");
+ else if(yytoknames[yychar])
+ printf("lex %s\n", yytoknames[yychar]);
+ else
+ printf("lex (%c)\n", yychar);
+ }
+#endif
+ if(yychar < 0)
+ yychar = 0;
+ }
+ if((yyn += yychar) < 0 || yyn >= YYLAST)
+ goto yydefault;
+ if( yychk[ yyn=yyact[ yyn ] ] == yychar ){ /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if( yyerrflag > 0 ) --yyerrflag;
+ goto yystack;
+ }
+yydefault:
+ /* default state action */
+ if( (yyn=yydef[yystate]) == -2 ) {
+ if(yychar < 0) {
+ yychar = yylex();
+#ifdef YYDEBUG
+ if(yydebug >= 2)
+ if(yychar < 0)
+ printf("lex EOF\n");
+ else
+ printf("lex %s\n", yytoknames[yychar]);
+#endif
+ if(yychar < 0)
+ yychar = 0;
+ }
+ /* look through exception table */
+ for(yyxi=yyexca; (*yyxi!= (-1)) || (yyxi[1]!=yystate);
+ yyxi += 2 ) ; /* VOID */
+ while( *(yyxi+=2) >= 0 ){
+ if( *yyxi == yychar ) break;
+ }
+ if( (yyn = yyxi[1]) < 0 ) return(0); /* accept */
+ }
+ if( yyn == 0 ){ /* error */
+ /* error ... attempt to resume parsing */
+ switch( yyerrflag ){
+ case 0: /* brand new error */
+#ifdef YYDEBUG
+ yyerror("syntax error\n%s", yystates[yystate]);
+ if(yytoknames[yychar])
+ yyerror("saw %s\n", yytoknames[yychar]);
+ else if(yychar >= ' ' && yychar < '\177')
+ yyerror("saw `%c'\n", yychar);
+ else if(yychar == 0)
+ yyerror("saw EOF\n");
+ else
+ yyerror("saw char 0%o\n", yychar);
+#else
+ yyerror( "syntax error" );
+#endif
+yyerrlab:
+ ++yynerrs;
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+ /* find a state where "error" is a legal shift action */
+ while ( yyps >= yys ) {
+ yyn = yypact[*yyps] + YYERRCODE;
+ if( yyn>= 0 && yyn < YYLAST && yychk[yyact[yyn]] == YYERRCODE ){
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ goto yystack;
+ }
+ yyn = yypact[*yyps];
+ /* the current yyps has no shift onn "error", pop stack */
+#ifdef YYDEBUG
+ if( yydebug ) printf( "error recovery pops state %d, uncovers %d\n", *yyps, yyps[-1] );
+#endif
+ --yyps;
+ --yypv;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+yyabort:
+ return(1);
+ case 3: /* no shift yet; clobber input char */
+#ifdef YYDEBUG
+ if( yydebug ) {
+ printf("error recovery discards ");
+ if(yytoknames[yychar])
+ printf("%s\n", yytoknames[yychar]);
+ else if(yychar >= ' ' && yychar < '\177')
+ printf("`%c'\n", yychar);
+ else if(yychar == 0)
+ printf("EOF\n");
+ else
+ printf("char 0%o\n", yychar);
+ }
+#endif
+ if( yychar == 0 ) goto yyabort; /* don't discard EOF, quit */
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+ /* reduction by production yyn */
+#ifdef YYDEBUG
+ if(yydebug) { char *s;
+ printf("reduce %d in:\n\t", yyn);
+ for(s = yystates[yystate]; *s; s++) {
+ putchar(*s);
+ if(*s == '\n' && *(s+1))
+ putchar('\t');
+ }
+ }
+#endif
+ yyps -= yyr2[yyn];
+ yypvt = yypv;
+ yypv -= yyr2[yyn];
+ yyval = yypv[1];
+ yym=yyn;
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyj = yypgo[yyn] + *yyps + 1;
+ if( yyj>=YYLAST || yychk[ yystate = yyact[yyj] ] != -yyn ) yystate = yyact[yypgo[yyn]];
+ switch(yym){
+
+case 3:
+/* # line 226 "gram.in" */
+{
+/* stat: is the nonterminal for Fortran statements */
+
+ lastwasbranch = NO; } break;
+case 5:
+/* # line 232 "gram.in" */
+{ /* forbid further statement function definitions... */
+ if (parstate == INDATA && laststfcn != thisstno)
+ parstate = INEXEC;
+ thisstno++;
+ if(yypvt[-1].labval && (yypvt[-1].labval->labelno==dorange))
+ enddo(yypvt[-1].labval->labelno);
+ if(lastwasbranch && thislabel==NULL)
+ warn("statement cannot be reached");
+ lastwasbranch = thiswasbranch;
+ thiswasbranch = NO;
+ if(yypvt[-1].labval)
+ {
+ if(yypvt[-1].labval->labtype == LABFORMAT)
+ err("label already that of a format");
+ else
+ yypvt[-1].labval->labtype = LABEXEC;
+ }
+ freetemps();
+ } break;
+case 6:
+/* # line 252 "gram.in" */
+{ if (can_include)
+ doinclude( yypvt[-0].charpval );
+ else {
+ fprintf(diagfile, "Cannot open file %s\n", yypvt[-0].charpval);
+ done(1);
+ }
+ } break;
+case 7:
+/* # line 260 "gram.in" */
+{ if (yypvt[-2].labval)
+ lastwasbranch = NO;
+ endproc(); /* lastwasbranch = NO; -- set in endproc() */
+ } break;
+case 8:
+/* # line 265 "gram.in" */
+{ extern void unclassifiable();
+ unclassifiable();
+
+/* flline flushes the current line, ignoring the rest of the text there */
+
+ flline(); } break;
+case 9:
+/* # line 272 "gram.in" */
+{ flline(); needkwd = NO; inioctl = NO;
+ yyerrok; yyclearin; } break;
+case 10:
+/* # line 277 "gram.in" */
+{
+ if(yystno != 0)
+ {
+ yyval.labval = thislabel = mklabel(yystno);
+ if( ! headerdone ) {
+ if (procclass == CLUNKNOWN)
+ procclass = CLMAIN;
+ puthead(CNULL, procclass);
+ }
+ if(thislabel->labdefined)
+ execerr("label %s already defined",
+ convic(thislabel->stateno) );
+ else {
+ if(thislabel->blklevel!=0 && thislabel->blklevel<blklevel
+ && thislabel->labtype!=LABFORMAT)
+ warn1("there is a branch to label %s from outside block",
+ convic( (ftnint) (thislabel->stateno) ) );
+ thislabel->blklevel = blklevel;
+ thislabel->labdefined = YES;
+ if(thislabel->labtype != LABFORMAT)
+ p1_label((long)(thislabel - labeltab));
+ }
+ }
+ else yyval.labval = thislabel = NULL;
+ } break;
+case 11:
+/* # line 305 "gram.in" */
+{startproc(yypvt[-0].extval, CLMAIN); } break;
+case 12:
+/* # line 307 "gram.in" */
+{ warn("ignoring arguments to main program");
+ /* hashclear(); */
+ startproc(yypvt[-1].extval, CLMAIN); } break;
+case 13:
+/* # line 311 "gram.in" */
+{ if(yypvt[-0].extval) NO66("named BLOCKDATA");
+ startproc(yypvt[-0].extval, CLBLOCK); } break;
+case 14:
+/* # line 314 "gram.in" */
+{ entrypt(CLPROC, TYSUBR, (ftnint) 0, yypvt[-1].extval, yypvt[-0].chval); } break;
+case 15:
+/* # line 316 "gram.in" */
+{ entrypt(CLPROC, TYUNKNOWN, (ftnint) 0, yypvt[-1].extval, yypvt[-0].chval); } break;
+case 16:
+/* # line 318 "gram.in" */
+{ entrypt(CLPROC, yypvt[-4].ival, varleng, yypvt[-1].extval, yypvt[-0].chval); } break;
+case 17:
+/* # line 320 "gram.in" */
+{ if(parstate==OUTSIDE || procclass==CLMAIN
+ || procclass==CLBLOCK)
+ execerr("misplaced entry statement", CNULL);
+ entrypt(CLENTRY, 0, (ftnint) 0, yypvt[-1].extval, yypvt[-0].chval);
+ } break;
+case 18:
+/* # line 328 "gram.in" */
+{ newproc(); } break;
+case 19:
+/* # line 332 "gram.in" */
+{ yyval.extval = newentry(yypvt[-0].namval, 1); } break;
+case 20:
+/* # line 336 "gram.in" */
+{ yyval.namval = mkname(token); } break;
+case 21:
+/* # line 339 "gram.in" */
+{ yyval.extval = NULL; } break;
+case 29:
+/* # line 357 "gram.in" */
+{ yyval.chval = 0; } break;
+case 30:
+/* # line 359 "gram.in" */
+{ NO66(" () argument list");
+ yyval.chval = 0; } break;
+case 31:
+/* # line 362 "gram.in" */
+{yyval.chval = yypvt[-1].chval; } break;
+case 32:
+/* # line 366 "gram.in" */
+{ yyval.chval = (yypvt[-0].namval ? mkchain((char *)yypvt[-0].namval,CHNULL) : CHNULL ); } break;
+case 33:
+/* # line 368 "gram.in" */
+{ if(yypvt[-0].namval) yypvt[-2].chval = yyval.chval = mkchain((char *)yypvt[-0].namval, yypvt[-2].chval); } break;
+case 34:
+/* # line 372 "gram.in" */
+{ if(yypvt[-0].namval->vstg!=STGUNKNOWN && yypvt[-0].namval->vstg!=STGARG)
+ dclerr("name declared as argument after use", yypvt[-0].namval);
+ yypvt[-0].namval->vstg = STGARG;
+ } break;
+case 35:
+/* # line 377 "gram.in" */
+{ NO66("altenate return argument");
+
+/* substars means that '*'ed formal parameters should be replaced.
+ This is used to specify alternate return labels; in theory, only
+ parameter slots which have '*' should accept the statement labels.
+ This compiler chooses to ignore the '*'s in the formal declaration, and
+ always return the proper value anyway.
+
+ This variable is only referred to in proc.c */
+
+ yyval.namval = 0; substars = YES; } break;
+case 36:
+/* # line 393 "gram.in" */
+{
+ char *s;
+ s = copyn(toklen+1, token);
+ s[toklen] = '\0';
+ yyval.charpval = s;
+ } break;
+case 45:
+/* # line 409 "gram.in" */
+{ NO66("SAVE statement");
+ saveall = YES; } break;
+case 46:
+/* # line 412 "gram.in" */
+{ NO66("SAVE statement"); } break;
+case 47:
+/* # line 414 "gram.in" */
+{ fmtstmt(thislabel); setfmt(thislabel); } break;
+case 48:
+/* # line 416 "gram.in" */
+{ NO66("PARAMETER statement"); } break;
+case 49:
+/* # line 420 "gram.in" */
+{ settype(yypvt[-4].namval, yypvt[-6].ival, yypvt[-0].lval);
+ if(ndim>0) setbound(yypvt[-4].namval,ndim,dims);
+ } break;
+case 50:
+/* # line 424 "gram.in" */
+{ settype(yypvt[-2].namval, yypvt[-4].ival, yypvt[-0].lval);
+ if(ndim>0) setbound(yypvt[-2].namval,ndim,dims);
+ } break;
+case 51:
+/* # line 428 "gram.in" */
+{ if (new_dcl == 2) {
+ err("attempt to give DATA in type-declaration");
+ new_dcl = 1;
+ }
+ } break;
+case 52:
+/* # line 435 "gram.in" */
+{ new_dcl = 2; } break;
+case 53:
+/* # line 438 "gram.in" */
+{ varleng = yypvt[-0].lval; } break;
+case 54:
+/* # line 442 "gram.in" */
+{ varleng = (yypvt[-0].ival<0 || ONEOF(yypvt[-0].ival,M(TYLOGICAL)|M(TYLONG))
+ ? 0 : typesize[yypvt[-0].ival]);
+ vartype = yypvt[-0].ival; } break;
+case 55:
+/* # line 447 "gram.in" */
+{ yyval.ival = TYLONG; } break;
+case 56:
+/* # line 448 "gram.in" */
+{ yyval.ival = tyreal; } break;
+case 57:
+/* # line 449 "gram.in" */
+{ ++complex_seen; yyval.ival = tycomplex; } break;
+case 58:
+/* # line 450 "gram.in" */
+{ yyval.ival = TYDREAL; } break;
+case 59:
+/* # line 451 "gram.in" */
+{ ++dcomplex_seen; NOEXT("DOUBLE COMPLEX statement"); yyval.ival = TYDCOMPLEX; } break;
+case 60:
+/* # line 452 "gram.in" */
+{ yyval.ival = TYLOGICAL; } break;
+case 61:
+/* # line 453 "gram.in" */
+{ NO66("CHARACTER statement"); yyval.ival = TYCHAR; } break;
+case 62:
+/* # line 454 "gram.in" */
+{ yyval.ival = TYUNKNOWN; } break;
+case 63:
+/* # line 455 "gram.in" */
+{ yyval.ival = TYUNKNOWN; } break;
+case 64:
+/* # line 456 "gram.in" */
+{ NOEXT("AUTOMATIC statement"); yyval.ival = - STGAUTO; } break;
+case 65:
+/* # line 457 "gram.in" */
+{ NOEXT("STATIC statement"); yyval.ival = - STGBSS; } break;
+case 66:
+/* # line 461 "gram.in" */
+{ yyval.lval = varleng; } break;
+case 67:
+/* # line 463 "gram.in" */
+{
+ expptr p;
+ p = yypvt[-1].expval;
+ NO66("length specification *n");
+ if( ! ISICON(p) || p->constblock.Const.ci <= 0 )
+ {
+ yyval.lval = 0;
+ dclerr("length must be a positive integer constant",
+ NPNULL);
+ }
+ else {
+ if (vartype == TYCHAR)
+ yyval.lval = p->constblock.Const.ci;
+ else switch((int)p->constblock.Const.ci) {
+ case 1: yyval.lval = 1; break;
+ case 2: yyval.lval = typesize[TYSHORT]; break;
+ case 4: yyval.lval = typesize[TYLONG]; break;
+ case 8: yyval.lval = typesize[TYDREAL]; break;
+ case 16: yyval.lval = typesize[TYDCOMPLEX]; break;
+ default:
+ dclerr("invalid length",NPNULL);
+ yyval.lval = varleng;
+ }
+ }
+ } break;
+case 68:
+/* # line 489 "gram.in" */
+{ NO66("length specification *(*)"); yyval.lval = -1; } break;
+case 69:
+/* # line 493 "gram.in" */
+{ incomm( yyval.extval = comblock("") , yypvt[-0].namval ); } break;
+case 70:
+/* # line 495 "gram.in" */
+{ yyval.extval = yypvt[-1].extval; incomm(yypvt[-1].extval, yypvt[-0].namval); } break;
+case 71:
+/* # line 497 "gram.in" */
+{ yyval.extval = yypvt[-2].extval; incomm(yypvt[-2].extval, yypvt[-0].namval); } break;
+case 72:
+/* # line 499 "gram.in" */
+{ incomm(yypvt[-2].extval, yypvt[-0].namval); } break;
+case 73:
+/* # line 503 "gram.in" */
+{ yyval.extval = comblock(""); } break;
+case 74:
+/* # line 505 "gram.in" */
+{ yyval.extval = comblock(token); } break;
+case 75:
+/* # line 509 "gram.in" */
+{ setext(yypvt[-0].namval); } break;
+case 76:
+/* # line 511 "gram.in" */
+{ setext(yypvt[-0].namval); } break;
+case 77:
+/* # line 515 "gram.in" */
+{ NO66("INTRINSIC statement"); setintr(yypvt[-0].namval); } break;
+case 78:
+/* # line 517 "gram.in" */
+{ setintr(yypvt[-0].namval); } break;
+case 81:
+/* # line 525 "gram.in" */
+{
+ struct Equivblock *p;
+ if(nequiv >= maxequiv)
+ many("equivalences", 'q', maxequiv);
+ p = & eqvclass[nequiv++];
+ p->eqvinit = NO;
+ p->eqvbottom = 0;
+ p->eqvtop = 0;
+ p->equivs = yypvt[-1].eqvval;
+ } break;
+case 82:
+/* # line 538 "gram.in" */
+{ yyval.eqvval=ALLOC(Eqvchain);
+ yyval.eqvval->eqvitem.eqvlhs = (struct Primblock *)yypvt[-0].expval;
+ } break;
+case 83:
+/* # line 542 "gram.in" */
+{ yyval.eqvval=ALLOC(Eqvchain);
+ yyval.eqvval->eqvitem.eqvlhs = (struct Primblock *) yypvt[-0].expval;
+ yyval.eqvval->eqvnextp = yypvt[-2].eqvval;
+ } break;
+case 86:
+/* # line 553 "gram.in" */
+{ if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+ if(parstate < INDATA)
+ {
+ enddcl();
+ parstate = INDATA;
+ datagripe = 1;
+ }
+ } break;
+case 87:
+/* # line 568 "gram.in" */
+{ ftnint junk;
+ if(nextdata(&junk) != NULL)
+ err("too few initializers");
+ frdata(yypvt[-4].chval);
+ frrpl();
+ } break;
+case 88:
+/* # line 576 "gram.in" */
+{ frchain(&datastack); curdtp = 0; } break;
+case 89:
+/* # line 578 "gram.in" */
+{ pop_datastack(); } break;
+case 90:
+/* # line 580 "gram.in" */
+{ toomanyinit = NO; } break;
+case 93:
+/* # line 585 "gram.in" */
+{ dataval(ENULL, yypvt[-0].expval); } break;
+case 94:
+/* # line 587 "gram.in" */
+{ dataval(yypvt[-2].expval, yypvt[-0].expval); } break;
+case 96:
+/* # line 592 "gram.in" */
+{ if( yypvt[-1].ival==OPMINUS && ISCONST(yypvt[-0].expval) )
+ consnegop((Constp)yypvt[-0].expval);
+ yyval.expval = yypvt[-0].expval;
+ } break;
+case 100:
+/* # line 604 "gram.in" */
+{ int k;
+ yypvt[-0].namval->vsave = YES;
+ k = yypvt[-0].namval->vstg;
+ if( ! ONEOF(k, M(STGUNKNOWN)|M(STGBSS)|M(STGINIT)) )
+ dclerr("can only save static variables", yypvt[-0].namval);
+ } break;
+case 104:
+/* # line 618 "gram.in" */
+{ if(yypvt[-2].namval->vclass == CLUNKNOWN)
+ make_param((struct Paramblock *)yypvt[-2].namval, yypvt[-0].expval);
+ else dclerr("cannot make into parameter", yypvt[-2].namval);
+ } break;
+case 105:
+/* # line 625 "gram.in" */
+{ if(ndim>0) setbound(yypvt[-1].namval, ndim, dims); } break;
+case 106:
+/* # line 629 "gram.in" */
+{ Namep np;
+ np = ( (struct Primblock *) yypvt[-0].expval) -> namep;
+ vardcl(np);
+ if(np->vstg == STGCOMMON)
+ extsymtab[np->vardesc.varno].extinit = YES;
+ else if(np->vstg==STGEQUIV)
+ eqvclass[np->vardesc.varno].eqvinit = YES;
+ else if(np->vstg!=STGINIT && np->vstg!=STGBSS)
+ dclerr("inconsistent storage classes", np);
+ yyval.chval = mkchain((char *)yypvt[-0].expval, CHNULL);
+ } break;
+case 107:
+/* # line 641 "gram.in" */
+{ chainp p; struct Impldoblock *q;
+ pop_datastack();
+ q = ALLOC(Impldoblock);
+ q->tag = TIMPLDO;
+ (q->varnp = (Namep) (yypvt[-1].chval->datap))->vimpldovar = 1;
+ p = yypvt[-1].chval->nextp;
+ if(p) { q->implb = (expptr)(p->datap); p = p->nextp; }
+ if(p) { q->impub = (expptr)(p->datap); p = p->nextp; }
+ if(p) { q->impstep = (expptr)(p->datap); }
+ frchain( & (yypvt[-1].chval) );
+ yyval.chval = mkchain((char *)q, CHNULL);
+ q->datalist = hookup(yypvt[-3].chval, yyval.chval);
+ } break;
+case 108:
+/* # line 657 "gram.in" */
+{ if (!datastack)
+ curdtp = 0;
+ datastack = mkchain((char *)curdtp, datastack);
+ curdtp = yypvt[-0].chval; curdtelt = 0;
+ } break;
+case 109:
+/* # line 663 "gram.in" */
+{ yyval.chval = hookup(yypvt[-2].chval, yypvt[-0].chval); } break;
+case 110:
+/* # line 667 "gram.in" */
+{ ndim = 0; } break;
+case 112:
+/* # line 671 "gram.in" */
+{ ndim = 0; } break;
+case 115:
+/* # line 676 "gram.in" */
+{
+ if(ndim == maxdim)
+ err("too many dimensions");
+ else if(ndim < maxdim)
+ { dims[ndim].lb = 0;
+ dims[ndim].ub = yypvt[-0].expval;
+ }
+ ++ndim;
+ } break;
+case 116:
+/* # line 686 "gram.in" */
+{
+ if(ndim == maxdim)
+ err("too many dimensions");
+ else if(ndim < maxdim)
+ { dims[ndim].lb = yypvt[-2].expval;
+ dims[ndim].ub = yypvt[-0].expval;
+ }
+ ++ndim;
+ } break;
+case 117:
+/* # line 698 "gram.in" */
+{ yyval.expval = 0; } break;
+case 119:
+/* # line 703 "gram.in" */
+{ nstars = 1; labarray[0] = yypvt[-0].labval; } break;
+case 120:
+/* # line 705 "gram.in" */
+{ if(nstars < maxlablist) labarray[nstars++] = yypvt[-0].labval; } break;
+case 121:
+/* # line 709 "gram.in" */
+{ yyval.labval = execlab( convci(toklen, token) ); } break;
+case 122:
+/* # line 713 "gram.in" */
+{ NO66("IMPLICIT statement"); } break;
+case 125:
+/* # line 719 "gram.in" */
+{ if (vartype != TYUNKNOWN)
+ dclerr("-- expected letter range",NPNULL);
+ setimpl(vartype, varleng, 'a', 'z'); } break;
+case 126:
+/* # line 724 "gram.in" */
+{ needkwd = 1; } break;
+case 130:
+/* # line 733 "gram.in" */
+{ setimpl(vartype, varleng, yypvt[-0].ival, yypvt[-0].ival); } break;
+case 131:
+/* # line 735 "gram.in" */
+{ setimpl(vartype, varleng, yypvt[-2].ival, yypvt[-0].ival); } break;
+case 132:
+/* # line 739 "gram.in" */
+{ if(toklen!=1 || token[0]<'a' || token[0]>'z')
+ {
+ dclerr("implicit item must be single letter", NPNULL);
+ yyval.ival = 0;
+ }
+ else yyval.ival = token[0];
+ } break;
+case 135:
+/* # line 753 "gram.in" */
+{
+ if(yypvt[-2].namval->vclass == CLUNKNOWN)
+ {
+ yypvt[-2].namval->vclass = CLNAMELIST;
+ yypvt[-2].namval->vtype = TYINT;
+ yypvt[-2].namval->vstg = STGBSS;
+ yypvt[-2].namval->varxptr.namelist = yypvt[-0].chval;
+ yypvt[-2].namval->vardesc.varno = ++lastvarno;
+ }
+ else dclerr("cannot be a namelist name", yypvt[-2].namval);
+ } break;
+case 136:
+/* # line 767 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].namval, CHNULL); } break;
+case 137:
+/* # line 769 "gram.in" */
+{ yyval.chval = hookup(yypvt[-2].chval, mkchain((char *)yypvt[-0].namval, CHNULL)); } break;
+case 138:
+/* # line 773 "gram.in" */
+{ switch(parstate)
+ {
+ case OUTSIDE: newproc();
+ startproc(ESNULL, CLMAIN);
+ case INSIDE: parstate = INDCL;
+ case INDCL: break;
+
+ case INDATA:
+ if (datagripe) {
+ errstr(
+ "Statement order error: declaration after DATA",
+ CNULL);
+ datagripe = 0;
+ }
+ break;
+
+ default:
+ dclerr("declaration among executables", NPNULL);
+ }
+ } break;
+case 139:
+/* # line 795 "gram.in" */
+{ yyval.chval = 0; } break;
+case 140:
+/* # line 797 "gram.in" */
+{ yyval.chval = revchain(yypvt[-0].chval); } break;
+case 141:
+/* # line 801 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, CHNULL); } break;
+case 142:
+/* # line 803 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, yypvt[-2].chval); } break;
+case 144:
+/* # line 808 "gram.in" */
+{ yyval.expval = yypvt[-1].expval; if (yyval.expval->tag == TPRIM)
+ yyval.expval->primblock.parenused = 1; } break;
+case 148:
+/* # line 816 "gram.in" */
+{ yyval.expval = mkexpr(yypvt[-1].ival, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 149:
+/* # line 818 "gram.in" */
+{ yyval.expval = mkexpr(OPSTAR, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 150:
+/* # line 820 "gram.in" */
+{ yyval.expval = mkexpr(OPSLASH, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 151:
+/* # line 822 "gram.in" */
+{ yyval.expval = mkexpr(OPPOWER, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 152:
+/* # line 824 "gram.in" */
+{ if(yypvt[-1].ival == OPMINUS)
+ yyval.expval = mkexpr(OPNEG, yypvt[-0].expval, ENULL);
+ else yyval.expval = yypvt[-0].expval;
+ } break;
+case 153:
+/* # line 829 "gram.in" */
+{ yyval.expval = mkexpr(yypvt[-1].ival, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 154:
+/* # line 831 "gram.in" */
+{ NO66(".EQV. operator");
+ yyval.expval = mkexpr(OPEQV, yypvt[-2].expval,yypvt[-0].expval); } break;
+case 155:
+/* # line 834 "gram.in" */
+{ NO66(".NEQV. operator");
+ yyval.expval = mkexpr(OPNEQV, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 156:
+/* # line 837 "gram.in" */
+{ yyval.expval = mkexpr(OPOR, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 157:
+/* # line 839 "gram.in" */
+{ yyval.expval = mkexpr(OPAND, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 158:
+/* # line 841 "gram.in" */
+{ yyval.expval = mkexpr(OPNOT, yypvt[-0].expval, ENULL); } break;
+case 159:
+/* # line 843 "gram.in" */
+{ NO66("concatenation operator //");
+ yyval.expval = mkexpr(OPCONCAT, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 160:
+/* # line 847 "gram.in" */
+{ yyval.ival = OPPLUS; } break;
+case 161:
+/* # line 848 "gram.in" */
+{ yyval.ival = OPMINUS; } break;
+case 162:
+/* # line 851 "gram.in" */
+{ yyval.ival = OPEQ; } break;
+case 163:
+/* # line 852 "gram.in" */
+{ yyval.ival = OPGT; } break;
+case 164:
+/* # line 853 "gram.in" */
+{ yyval.ival = OPLT; } break;
+case 165:
+/* # line 854 "gram.in" */
+{ yyval.ival = OPGE; } break;
+case 166:
+/* # line 855 "gram.in" */
+{ yyval.ival = OPLE; } break;
+case 167:
+/* # line 856 "gram.in" */
+{ yyval.ival = OPNE; } break;
+case 168:
+/* # line 860 "gram.in" */
+{ yyval.expval = mkprim(yypvt[-0].namval, LBNULL, CHNULL); } break;
+case 169:
+/* # line 862 "gram.in" */
+{ NO66("substring operator :");
+ yyval.expval = mkprim(yypvt[-1].namval, LBNULL, yypvt[-0].chval); } break;
+case 170:
+/* # line 865 "gram.in" */
+{ yyval.expval = mkprim(yypvt[-3].namval, mklist(yypvt[-1].chval), CHNULL); } break;
+case 171:
+/* # line 867 "gram.in" */
+{ NO66("substring operator :");
+ yyval.expval = mkprim(yypvt[-4].namval, mklist(yypvt[-2].chval), yypvt[-0].chval); } break;
+case 172:
+/* # line 872 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-3].expval, mkchain((char *)yypvt[-1].expval,CHNULL)); } break;
+case 173:
+/* # line 876 "gram.in" */
+{ yyval.expval = 0; } break;
+case 175:
+/* # line 881 "gram.in" */
+{ if(yypvt[-0].namval->vclass == CLPARAM)
+ yyval.expval = (expptr) cpexpr(
+ ( (struct Paramblock *) (yypvt[-0].namval) ) -> paramval);
+ } break;
+case 177:
+/* # line 888 "gram.in" */
+{ yyval.expval = mklogcon(1); } break;
+case 178:
+/* # line 889 "gram.in" */
+{ yyval.expval = mklogcon(0); } break;
+case 179:
+/* # line 890 "gram.in" */
+{ yyval.expval = mkstrcon(toklen, token); } break;
+case 180:
+/* # line 891 "gram.in" */
+ { yyval.expval = mkintcon( convci(toklen, token) ); } break;
+case 181:
+/* # line 892 "gram.in" */
+ { yyval.expval = mkrealcon(tyreal, token); } break;
+case 182:
+/* # line 893 "gram.in" */
+ { yyval.expval = mkrealcon(TYDREAL, token); } break;
+case 184:
+/* # line 898 "gram.in" */
+{ yyval.expval = mkcxcon(yypvt[-3].expval,yypvt[-1].expval); } break;
+case 185:
+/* # line 902 "gram.in" */
+{ NOEXT("hex constant");
+ yyval.expval = mkbitcon(4, toklen, token); } break;
+case 186:
+/* # line 905 "gram.in" */
+{ NOEXT("octal constant");
+ yyval.expval = mkbitcon(3, toklen, token); } break;
+case 187:
+/* # line 908 "gram.in" */
+{ NOEXT("binary constant");
+ yyval.expval = mkbitcon(1, toklen, token); } break;
+case 189:
+/* # line 914 "gram.in" */
+{ yyval.expval = yypvt[-1].expval; } break;
+case 192:
+/* # line 920 "gram.in" */
+{ yyval.expval = mkexpr(yypvt[-1].ival, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 193:
+/* # line 922 "gram.in" */
+{ yyval.expval = mkexpr(OPSTAR, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 194:
+/* # line 924 "gram.in" */
+{ yyval.expval = mkexpr(OPSLASH, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 195:
+/* # line 926 "gram.in" */
+{ yyval.expval = mkexpr(OPPOWER, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 196:
+/* # line 928 "gram.in" */
+{ if(yypvt[-1].ival == OPMINUS)
+ yyval.expval = mkexpr(OPNEG, yypvt[-0].expval, ENULL);
+ else yyval.expval = yypvt[-0].expval;
+ } break;
+case 197:
+/* # line 933 "gram.in" */
+{ NO66("concatenation operator //");
+ yyval.expval = mkexpr(OPCONCAT, yypvt[-2].expval, yypvt[-0].expval); } break;
+case 199:
+/* # line 938 "gram.in" */
+{
+ if(yypvt[-3].labval->labdefined)
+ execerr("no backward DO loops", CNULL);
+ yypvt[-3].labval->blklevel = blklevel+1;
+ exdo(yypvt[-3].labval->labelno, NPNULL, yypvt[-0].chval);
+ } break;
+case 200:
+/* # line 945 "gram.in" */
+{
+ exdo((int)(ctls - ctlstack - 2), NPNULL, yypvt[-0].chval);
+ NOEXT("DO without label");
+ } break;
+case 201:
+/* # line 950 "gram.in" */
+{ exenddo(NPNULL); } break;
+case 202:
+/* # line 952 "gram.in" */
+{ exendif(); thiswasbranch = NO; } break;
+case 204:
+/* # line 955 "gram.in" */
+{ exelif(yypvt[-2].expval); lastwasbranch = NO; } break;
+case 205:
+/* # line 957 "gram.in" */
+{ exelse(); lastwasbranch = NO; } break;
+case 206:
+/* # line 959 "gram.in" */
+{ exendif(); lastwasbranch = NO; } break;
+case 207:
+/* # line 963 "gram.in" */
+{ exif(yypvt[-1].expval); } break;
+case 208:
+/* # line 967 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-2].namval, yypvt[-0].chval); } break;
+case 210:
+/* # line 972 "gram.in" */
+{ yyval.chval = mkchain(CNULL, (chainp)yypvt[-1].expval); } break;
+case 211:
+/* # line 976 "gram.in" */
+{ exequals((struct Primblock *)yypvt[-2].expval, yypvt[-0].expval); } break;
+case 212:
+/* # line 978 "gram.in" */
+{ exassign(yypvt[-0].namval, yypvt[-2].labval); } break;
+case 215:
+/* # line 982 "gram.in" */
+{ inioctl = NO; } break;
+case 216:
+/* # line 984 "gram.in" */
+{ exarif(yypvt[-6].expval, yypvt[-4].labval, yypvt[-2].labval, yypvt[-0].labval); thiswasbranch = YES; } break;
+case 217:
+/* # line 986 "gram.in" */
+{ excall(yypvt[-0].namval, LBNULL, 0, labarray); } break;
+case 218:
+/* # line 988 "gram.in" */
+{ excall(yypvt[-2].namval, LBNULL, 0, labarray); } break;
+case 219:
+/* # line 990 "gram.in" */
+{ if(nstars < maxlablist)
+ excall(yypvt[-3].namval, mklist(revchain(yypvt[-1].chval)), nstars, labarray);
+ else
+ many("alternate returns", 'l', maxlablist);
+ } break;
+case 220:
+/* # line 996 "gram.in" */
+{ exreturn(yypvt[-0].expval); thiswasbranch = YES; } break;
+case 221:
+/* # line 998 "gram.in" */
+{ exstop(yypvt[-2].ival, yypvt[-0].expval); thiswasbranch = yypvt[-2].ival; } break;
+case 222:
+/* # line 1002 "gram.in" */
+{ yyval.labval = mklabel( convci(toklen, token) ); } break;
+case 223:
+/* # line 1006 "gram.in" */
+{ if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+ } break;
+case 224:
+/* # line 1015 "gram.in" */
+{ exgoto(yypvt[-0].labval); thiswasbranch = YES; } break;
+case 225:
+/* # line 1017 "gram.in" */
+{ exasgoto(yypvt[-0].namval); thiswasbranch = YES; } break;
+case 226:
+/* # line 1019 "gram.in" */
+{ exasgoto(yypvt[-4].namval); thiswasbranch = YES; } break;
+case 227:
+/* # line 1021 "gram.in" */
+{ if(nstars < maxlablist)
+ putcmgo(putx(fixtype(yypvt[-0].expval)), nstars, labarray);
+ else
+ many("labels in computed GOTO list", 'l', maxlablist);
+ } break;
+case 230:
+/* # line 1033 "gram.in" */
+{ nstars = 0; yyval.namval = yypvt[-0].namval; } break;
+case 231:
+/* # line 1037 "gram.in" */
+{ yyval.chval = yypvt[-0].expval ? mkchain((char *)yypvt[-0].expval,CHNULL) : CHNULL; } break;
+case 232:
+/* # line 1039 "gram.in" */
+{ yyval.chval = yypvt[-0].expval ? mkchain((char *)yypvt[-0].expval, yypvt[-2].chval) : yypvt[-2].chval; } break;
+case 234:
+/* # line 1044 "gram.in" */
+{ if(nstars < maxlablist) labarray[nstars++] = yypvt[-0].labval; yyval.expval = 0; } break;
+case 235:
+/* # line 1048 "gram.in" */
+{ yyval.ival = 0; } break;
+case 236:
+/* # line 1050 "gram.in" */
+{ yyval.ival = 2; } break;
+case 237:
+/* # line 1054 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, CHNULL); } break;
+case 238:
+/* # line 1056 "gram.in" */
+{ yyval.chval = hookup(yypvt[-2].chval, mkchain((char *)yypvt[-0].expval,CHNULL) ); } break;
+case 239:
+/* # line 1060 "gram.in" */
+{ if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+
+/* This next statement depends on the ordering of the state table encoding */
+
+ if(parstate < INDATA) enddcl();
+ } break;
+case 240:
+/* # line 1073 "gram.in" */
+{ intonly = YES; } break;
+case 241:
+/* # line 1077 "gram.in" */
+{ intonly = NO; } break;
+case 242:
+/* # line 1082 "gram.in" */
+{ endio(); } break;
+case 244:
+/* # line 1087 "gram.in" */
+{ ioclause(IOSUNIT, yypvt[-0].expval); endioctl(); } break;
+case 245:
+/* # line 1089 "gram.in" */
+{ ioclause(IOSUNIT, ENULL); endioctl(); } break;
+case 246:
+/* # line 1091 "gram.in" */
+{ ioclause(IOSUNIT, IOSTDERR); endioctl(); } break;
+case 248:
+/* # line 1094 "gram.in" */
+{ doio(CHNULL); } break;
+case 249:
+/* # line 1096 "gram.in" */
+{ doio(CHNULL); } break;
+case 250:
+/* # line 1098 "gram.in" */
+{ doio(revchain(yypvt[-0].chval)); } break;
+case 251:
+/* # line 1100 "gram.in" */
+{ doio(revchain(yypvt[-0].chval)); } break;
+case 252:
+/* # line 1102 "gram.in" */
+{ doio(revchain(yypvt[-0].chval)); } break;
+case 253:
+/* # line 1104 "gram.in" */
+{ doio(CHNULL); } break;
+case 254:
+/* # line 1106 "gram.in" */
+{ doio(revchain(yypvt[-0].chval)); } break;
+case 255:
+/* # line 1108 "gram.in" */
+{ doio(CHNULL); } break;
+case 256:
+/* # line 1110 "gram.in" */
+{ doio(revchain(yypvt[-0].chval)); } break;
+case 258:
+/* # line 1117 "gram.in" */
+{ iostmt = IOBACKSPACE; } break;
+case 259:
+/* # line 1119 "gram.in" */
+{ iostmt = IOREWIND; } break;
+case 260:
+/* # line 1121 "gram.in" */
+{ iostmt = IOENDFILE; } break;
+case 262:
+/* # line 1128 "gram.in" */
+{ iostmt = IOINQUIRE; } break;
+case 263:
+/* # line 1130 "gram.in" */
+{ iostmt = IOOPEN; } break;
+case 264:
+/* # line 1132 "gram.in" */
+{ iostmt = IOCLOSE; } break;
+case 265:
+/* # line 1136 "gram.in" */
+{
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, yypvt[-0].expval);
+ endioctl();
+ } break;
+case 266:
+/* # line 1142 "gram.in" */
+{
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, ENULL);
+ endioctl();
+ } break;
+case 267:
+/* # line 1150 "gram.in" */
+{
+ ioclause(IOSUNIT, yypvt[-1].expval);
+ endioctl();
+ } break;
+case 268:
+/* # line 1155 "gram.in" */
+{ endioctl(); } break;
+case 271:
+/* # line 1163 "gram.in" */
+{ ioclause(IOSPOSITIONAL, yypvt[-0].expval); } break;
+case 272:
+/* # line 1165 "gram.in" */
+{ ioclause(IOSPOSITIONAL, ENULL); } break;
+case 273:
+/* # line 1167 "gram.in" */
+{ ioclause(IOSPOSITIONAL, IOSTDERR); } break;
+case 274:
+/* # line 1169 "gram.in" */
+{ ioclause(yypvt[-1].ival, yypvt[-0].expval); } break;
+case 275:
+/* # line 1171 "gram.in" */
+{ ioclause(yypvt[-1].ival, ENULL); } break;
+case 276:
+/* # line 1173 "gram.in" */
+{ ioclause(yypvt[-1].ival, IOSTDERR); } break;
+case 277:
+/* # line 1177 "gram.in" */
+{ yyval.ival = iocname(); } break;
+case 278:
+/* # line 1181 "gram.in" */
+{ iostmt = IOREAD; } break;
+case 279:
+/* # line 1185 "gram.in" */
+{ iostmt = IOWRITE; } break;
+case 280:
+/* # line 1189 "gram.in" */
+{
+ iostmt = IOWRITE;
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, yypvt[-1].expval);
+ endioctl();
+ } break;
+case 281:
+/* # line 1196 "gram.in" */
+{
+ iostmt = IOWRITE;
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, ENULL);
+ endioctl();
+ } break;
+case 282:
+/* # line 1205 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, CHNULL); } break;
+case 283:
+/* # line 1207 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, yypvt[-2].chval); } break;
+case 284:
+/* # line 1211 "gram.in" */
+{ yyval.tagval = (tagptr) yypvt[-0].expval; } break;
+case 285:
+/* # line 1213 "gram.in" */
+{ yyval.tagval = (tagptr) mkiodo(yypvt[-1].chval,revchain(yypvt[-3].chval)); } break;
+case 286:
+/* # line 1217 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, CHNULL); } break;
+case 287:
+/* # line 1219 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, CHNULL); } break;
+case 289:
+/* # line 1224 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, mkchain((char *)yypvt[-2].expval, CHNULL) ); } break;
+case 290:
+/* # line 1226 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, mkchain((char *)yypvt[-2].expval, CHNULL) ); } break;
+case 291:
+/* # line 1228 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, mkchain((char *)yypvt[-2].tagval, CHNULL) ); } break;
+case 292:
+/* # line 1230 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, mkchain((char *)yypvt[-2].tagval, CHNULL) ); } break;
+case 293:
+/* # line 1232 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].expval, yypvt[-2].chval); } break;
+case 294:
+/* # line 1234 "gram.in" */
+{ yyval.chval = mkchain((char *)yypvt[-0].tagval, yypvt[-2].chval); } break;
+case 295:
+/* # line 1238 "gram.in" */
+{ yyval.tagval = (tagptr) yypvt[-0].expval; } break;
+case 296:
+/* # line 1240 "gram.in" */
+{ yyval.tagval = (tagptr) yypvt[-1].expval; } break;
+case 297:
+/* # line 1242 "gram.in" */
+{ yyval.tagval = (tagptr) mkiodo(yypvt[-1].chval, mkchain((char *)yypvt[-3].expval, CHNULL) ); } break;
+case 298:
+/* # line 1244 "gram.in" */
+{ yyval.tagval = (tagptr) mkiodo(yypvt[-1].chval, mkchain((char *)yypvt[-3].tagval, CHNULL) ); } break;
+case 299:
+/* # line 1246 "gram.in" */
+{ yyval.tagval = (tagptr) mkiodo(yypvt[-1].chval, revchain(yypvt[-3].chval)); } break;
+case 300:
+/* # line 1250 "gram.in" */
+{ startioctl(); } break;
+ }
+ goto yystack; /* stack new state and value */
+}
diff --git a/usr.bin/f2c/gram.dcl b/usr.bin/f2c/gram.dcl
new file mode 100644
index 0000000..fadbb5b
--- /dev/null
+++ b/usr.bin/f2c/gram.dcl
@@ -0,0 +1,404 @@
+spec: dcl
+ | common
+ | external
+ | intrinsic
+ | equivalence
+ | data
+ | implicit
+ | namelist
+ | SSAVE
+ { NO66("SAVE statement");
+ saveall = YES; }
+ | SSAVE savelist
+ { NO66("SAVE statement"); }
+ | SFORMAT
+ { fmtstmt(thislabel); setfmt(thislabel); }
+ | SPARAM in_dcl SLPAR paramlist SRPAR
+ { NO66("PARAMETER statement"); }
+ ;
+
+dcl: type opt_comma name in_dcl new_dcl dims lengspec
+ { settype($3, $1, $7);
+ if(ndim>0) setbound($3,ndim,dims);
+ }
+ | dcl SCOMMA name dims lengspec
+ { settype($3, $1, $5);
+ if(ndim>0) setbound($3,ndim,dims);
+ }
+ | dcl SSLASHD datainit vallist SSLASHD
+ { if (new_dcl == 2) {
+ err("attempt to give DATA in type-declaration");
+ new_dcl = 1;
+ }
+ }
+ ;
+
+new_dcl: { new_dcl = 2; } ;
+
+type: typespec lengspec
+ { varleng = $2; }
+ ;
+
+typespec: typename
+ { varleng = ($1<0 || ONEOF($1,M(TYLOGICAL)|M(TYLONG))
+ ? 0 : typesize[$1]);
+ vartype = $1; }
+ ;
+
+typename: SINTEGER { $$ = TYLONG; }
+ | SREAL { $$ = tyreal; }
+ | SCOMPLEX { ++complex_seen; $$ = tycomplex; }
+ | SDOUBLE { $$ = TYDREAL; }
+ | SDCOMPLEX { ++dcomplex_seen; NOEXT("DOUBLE COMPLEX statement"); $$ = TYDCOMPLEX; }
+ | SLOGICAL { $$ = TYLOGICAL; }
+ | SCHARACTER { NO66("CHARACTER statement"); $$ = TYCHAR; }
+ | SUNDEFINED { $$ = TYUNKNOWN; }
+ | SDIMENSION { $$ = TYUNKNOWN; }
+ | SAUTOMATIC { NOEXT("AUTOMATIC statement"); $$ = - STGAUTO; }
+ | SSTATIC { NOEXT("STATIC statement"); $$ = - STGBSS; }
+ | SBYTE { $$ = TYINT1; }
+ ;
+
+lengspec:
+ { $$ = varleng; }
+ | SSTAR intonlyon expr intonlyoff
+ {
+ expptr p;
+ p = $3;
+ NO66("length specification *n");
+ if( ! ISICON(p) || p->constblock.Const.ci <= 0 )
+ {
+ $$ = 0;
+ dclerr("length must be a positive integer constant",
+ NPNULL);
+ }
+ else {
+ if (vartype == TYCHAR)
+ $$ = p->constblock.Const.ci;
+ else switch((int)p->constblock.Const.ci) {
+ case 1: $$ = 1; break;
+ case 2: $$ = typesize[TYSHORT]; break;
+ case 4: $$ = typesize[TYLONG]; break;
+ case 8: $$ = typesize[TYDREAL]; break;
+ case 16: $$ = typesize[TYDCOMPLEX]; break;
+ default:
+ dclerr("invalid length",NPNULL);
+ $$ = varleng;
+ }
+ }
+ }
+ | SSTAR intonlyon SLPAR SSTAR SRPAR intonlyoff
+ { NO66("length specification *(*)"); $$ = -1; }
+ ;
+
+common: SCOMMON in_dcl var
+ { incomm( $$ = comblock("") , $3 ); }
+ | SCOMMON in_dcl comblock var
+ { $$ = $3; incomm($3, $4); }
+ | common opt_comma comblock opt_comma var
+ { $$ = $3; incomm($3, $5); }
+ | common SCOMMA var
+ { incomm($1, $3); }
+ ;
+
+comblock: SCONCAT
+ { $$ = comblock(""); }
+ | SSLASH SNAME SSLASH
+ { $$ = comblock(token); }
+ ;
+
+external: SEXTERNAL in_dcl name
+ { setext($3); }
+ | external SCOMMA name
+ { setext($3); }
+ ;
+
+intrinsic: SINTRINSIC in_dcl name
+ { NO66("INTRINSIC statement"); setintr($3); }
+ | intrinsic SCOMMA name
+ { setintr($3); }
+ ;
+
+equivalence: SEQUIV in_dcl equivset
+ | equivalence SCOMMA equivset
+ ;
+
+equivset: SLPAR equivlist SRPAR
+ {
+ struct Equivblock *p;
+ if(nequiv >= maxequiv)
+ many("equivalences", 'q', maxequiv);
+ p = & eqvclass[nequiv++];
+ p->eqvinit = NO;
+ p->eqvbottom = 0;
+ p->eqvtop = 0;
+ p->equivs = $2;
+ }
+ ;
+
+equivlist: lhs
+ { $$=ALLOC(Eqvchain);
+ $$->eqvitem.eqvlhs = (struct Primblock *)$1;
+ }
+ | equivlist SCOMMA lhs
+ { $$=ALLOC(Eqvchain);
+ $$->eqvitem.eqvlhs = (struct Primblock *) $3;
+ $$->eqvnextp = $1;
+ }
+ ;
+
+data: SDATA in_data datalist
+ | data opt_comma datalist
+ ;
+
+in_data:
+ { if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+ if(parstate < INDATA)
+ {
+ enddcl();
+ parstate = INDATA;
+ datagripe = 1;
+ }
+ }
+ ;
+
+datalist: datainit datavarlist SSLASH datapop vallist SSLASH
+ { ftnint junk;
+ if(nextdata(&junk) != NULL)
+ err("too few initializers");
+ frdata($2);
+ frrpl();
+ }
+ ;
+
+datainit: /* nothing */ { frchain(&datastack); curdtp = 0; } ;
+
+datapop: /* nothing */ { pop_datastack(); } ;
+
+vallist: { toomanyinit = NO; } val
+ | vallist SCOMMA val
+ ;
+
+val: value
+ { dataval(ENULL, $1); }
+ | simple SSTAR value
+ { dataval($1, $3); }
+ ;
+
+value: simple
+ | addop simple
+ { if( $1==OPMINUS && ISCONST($2) )
+ consnegop((Constp)$2);
+ $$ = $2;
+ }
+ | complex_const
+ ;
+
+savelist: saveitem
+ | savelist SCOMMA saveitem
+ ;
+
+saveitem: name
+ { int k;
+ $1->vsave = YES;
+ k = $1->vstg;
+ if( ! ONEOF(k, M(STGUNKNOWN)|M(STGBSS)|M(STGINIT)) )
+ dclerr("can only save static variables", $1);
+ }
+ | comblock
+ ;
+
+paramlist: paramitem
+ | paramlist SCOMMA paramitem
+ ;
+
+paramitem: name SEQUALS expr
+ { if($1->vclass == CLUNKNOWN)
+ make_param((struct Paramblock *)$1, $3);
+ else dclerr("cannot make into parameter", $1);
+ }
+ ;
+
+var: name dims
+ { if(ndim>0) setbound($1, ndim, dims); }
+ ;
+
+datavar: lhs
+ { Namep np;
+ int tt = $1->tag;
+ if (tt != TPRIM) {
+ if (tt == TCONST)
+ err("parameter in data statement");
+ else
+ erri("tag %d in data statement",tt);
+ $$ = 0;
+ break;
+ }
+ np = ( (struct Primblock *) $1) -> namep;
+ vardcl(np);
+ if(np->vstg == STGCOMMON)
+ extsymtab[np->vardesc.varno].extinit = YES;
+ else if(np->vstg==STGEQUIV)
+ eqvclass[np->vardesc.varno].eqvinit = YES;
+ else if(np->vstg!=STGINIT && np->vstg!=STGBSS)
+ dclerr("inconsistent storage classes", np);
+ $$ = mkchain((char *)$1, CHNULL);
+ }
+ | SLPAR datavarlist SCOMMA dospec SRPAR
+ { chainp p; struct Impldoblock *q;
+ pop_datastack();
+ q = ALLOC(Impldoblock);
+ q->tag = TIMPLDO;
+ (q->varnp = (Namep) ($4->datap))->vimpldovar = 1;
+ p = $4->nextp;
+ if(p) { q->implb = (expptr)(p->datap); p = p->nextp; }
+ if(p) { q->impub = (expptr)(p->datap); p = p->nextp; }
+ if(p) { q->impstep = (expptr)(p->datap); }
+ frchain( & ($4) );
+ $$ = mkchain((char *)q, CHNULL);
+ q->datalist = hookup($2, $$);
+ }
+ ;
+
+datavarlist: datavar
+ { if (!datastack)
+ curdtp = 0;
+ datastack = mkchain((char *)curdtp, datastack);
+ curdtp = $1; curdtelt = 0;
+ }
+ | datavarlist SCOMMA datavar
+ { $$ = hookup($1, $3); }
+ ;
+
+dims:
+ { ndim = 0; }
+ | SLPAR dimlist SRPAR
+ ;
+
+dimlist: { ndim = 0; } dim
+ | dimlist SCOMMA dim
+ ;
+
+dim: ubound
+ {
+ if(ndim == maxdim)
+ err("too many dimensions");
+ else if(ndim < maxdim)
+ { dims[ndim].lb = 0;
+ dims[ndim].ub = $1;
+ }
+ ++ndim;
+ }
+ | expr SCOLON ubound
+ {
+ if(ndim == maxdim)
+ err("too many dimensions");
+ else if(ndim < maxdim)
+ { dims[ndim].lb = $1;
+ dims[ndim].ub = $3;
+ }
+ ++ndim;
+ }
+ ;
+
+ubound: SSTAR
+ { $$ = 0; }
+ | expr
+ ;
+
+labellist: label
+ { nstars = 1; labarray[0] = $1; }
+ | labellist SCOMMA label
+ { if(nstars < maxlablist) labarray[nstars++] = $3; }
+ ;
+
+label: SICON
+ { $$ = execlab( convci(toklen, token) ); }
+ ;
+
+implicit: SIMPLICIT in_dcl implist
+ { NO66("IMPLICIT statement"); }
+ | implicit SCOMMA implist
+ ;
+
+implist: imptype SLPAR letgroups SRPAR
+ | imptype
+ { if (vartype != TYUNKNOWN)
+ dclerr("-- expected letter range",NPNULL);
+ setimpl(vartype, varleng, 'a', 'z'); }
+ ;
+
+imptype: { needkwd = 1; } type
+ /* { vartype = $2; } */
+ ;
+
+letgroups: letgroup
+ | letgroups SCOMMA letgroup
+ ;
+
+letgroup: letter
+ { setimpl(vartype, varleng, $1, $1); }
+ | letter SMINUS letter
+ { setimpl(vartype, varleng, $1, $3); }
+ ;
+
+letter: SNAME
+ { if(toklen!=1 || token[0]<'a' || token[0]>'z')
+ {
+ dclerr("implicit item must be single letter", NPNULL);
+ $$ = 0;
+ }
+ else $$ = token[0];
+ }
+ ;
+
+namelist: SNAMELIST
+ | namelist namelistentry
+ ;
+
+namelistentry: SSLASH name SSLASH namelistlist
+ {
+ if($2->vclass == CLUNKNOWN)
+ {
+ $2->vclass = CLNAMELIST;
+ $2->vtype = TYINT;
+ $2->vstg = STGBSS;
+ $2->varxptr.namelist = $4;
+ $2->vardesc.varno = ++lastvarno;
+ }
+ else dclerr("cannot be a namelist name", $2);
+ }
+ ;
+
+namelistlist: name
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | namelistlist SCOMMA name
+ { $$ = hookup($1, mkchain((char *)$3, CHNULL)); }
+ ;
+
+in_dcl:
+ { switch(parstate)
+ {
+ case OUTSIDE: newproc();
+ startproc(ESNULL, CLMAIN);
+ case INSIDE: parstate = INDCL;
+ case INDCL: break;
+
+ case INDATA:
+ if (datagripe) {
+ errstr(
+ "Statement order error: declaration after DATA",
+ CNULL);
+ datagripe = 0;
+ }
+ break;
+
+ default:
+ dclerr("declaration among executables", NPNULL);
+ }
+ }
+ ;
diff --git a/usr.bin/f2c/gram.exec b/usr.bin/f2c/gram.exec
new file mode 100644
index 0000000..0dc6010
--- /dev/null
+++ b/usr.bin/f2c/gram.exec
@@ -0,0 +1,143 @@
+exec: iffable
+ | SDO end_spec intonlyon label intonlyoff opt_comma dospecw
+ {
+ if($4->labdefined)
+ execerr("no backward DO loops", CNULL);
+ $4->blklevel = blklevel+1;
+ exdo($4->labelno, NPNULL, $7);
+ }
+ | SDO end_spec opt_comma dospecw
+ {
+ exdo((int)(ctls - ctlstack - 2), NPNULL, $4);
+ NOEXT("DO without label");
+ }
+ | SENDDO
+ { exenddo(NPNULL); }
+ | logif iffable
+ { exendif(); thiswasbranch = NO; }
+ | logif STHEN
+ | SELSEIF end_spec SLPAR expr SRPAR STHEN
+ { exelif($4); lastwasbranch = NO; }
+ | SELSE end_spec
+ { exelse(); lastwasbranch = NO; }
+ | SENDIF end_spec
+ { exendif(); lastwasbranch = NO; }
+ ;
+
+logif: SLOGIF end_spec SLPAR expr SRPAR
+ { exif($4); }
+ ;
+
+dospec: name SEQUALS exprlist
+ { $$ = mkchain((char *)$1, $3); }
+ ;
+
+dospecw: dospec
+ | SWHILE SLPAR expr SRPAR
+ { $$ = mkchain(CNULL, (chainp)$3); }
+ ;
+
+iffable: let lhs SEQUALS expr
+ { exequals((struct Primblock *)$2, $4); }
+ | SASSIGN end_spec assignlabel STO name
+ { exassign($5, $3); }
+ | SCONTINUE end_spec
+ | goto
+ | io
+ { inioctl = NO; }
+ | SARITHIF end_spec SLPAR expr SRPAR label SCOMMA label SCOMMA label
+ { exarif($4, $6, $8, $10); thiswasbranch = YES; }
+ | call
+ { excall($1, LBNULL, 0, labarray); }
+ | call SLPAR SRPAR
+ { excall($1, LBNULL, 0, labarray); }
+ | call SLPAR callarglist SRPAR
+ { if(nstars < maxlablist)
+ excall($1, mklist(revchain($3)), nstars, labarray);
+ else
+ many("alternate returns", 'l', maxlablist);
+ }
+ | SRETURN end_spec opt_expr
+ { exreturn($3); thiswasbranch = YES; }
+ | stop end_spec opt_expr
+ { exstop($1, $3); thiswasbranch = $1; }
+ ;
+
+assignlabel: SICON
+ { $$ = mklabel( convci(toklen, token) ); }
+ ;
+
+let: SLET
+ { if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+ }
+ ;
+
+goto: SGOTO end_spec label
+ { exgoto($3); thiswasbranch = YES; }
+ | SASGOTO end_spec name
+ { exasgoto($3); thiswasbranch = YES; }
+ | SASGOTO end_spec name opt_comma SLPAR labellist SRPAR
+ { exasgoto($3); thiswasbranch = YES; }
+ | SCOMPGOTO end_spec SLPAR labellist SRPAR opt_comma expr
+ { if(nstars < maxlablist)
+ putcmgo(putx(fixtype($7)), nstars, labarray);
+ else
+ many("labels in computed GOTO list", 'l', maxlablist);
+ }
+ ;
+
+opt_comma:
+ | SCOMMA
+ ;
+
+call: SCALL end_spec name
+ { nstars = 0; $$ = $3; }
+ ;
+
+callarglist: callarg
+ { $$ = $1 ? mkchain((char *)$1,CHNULL) : CHNULL; }
+ | callarglist SCOMMA callarg
+ { $$ = $3 ? mkchain((char *)$3, $1) : $1; }
+ ;
+
+callarg: expr
+ | SSTAR label
+ { if(nstars < maxlablist) labarray[nstars++] = $2; $$ = 0; }
+ ;
+
+stop: SPAUSE
+ { $$ = 0; }
+ | SSTOP
+ { $$ = 2; }
+ ;
+
+exprlist: expr
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | exprlist SCOMMA expr
+ { $$ = hookup($1, mkchain((char *)$3,CHNULL) ); }
+ ;
+
+end_spec:
+ { if(parstate == OUTSIDE)
+ {
+ newproc();
+ startproc(ESNULL, CLMAIN);
+ }
+
+/* This next statement depends on the ordering of the state table encoding */
+
+ if(parstate < INDATA) enddcl();
+ }
+ ;
+
+intonlyon:
+ { intonly = YES; }
+ ;
+
+intonlyoff:
+ { intonly = NO; }
+ ;
diff --git a/usr.bin/f2c/gram.expr b/usr.bin/f2c/gram.expr
new file mode 100644
index 0000000..1ef18e5
--- /dev/null
+++ b/usr.bin/f2c/gram.expr
@@ -0,0 +1,142 @@
+funarglist:
+ { $$ = 0; }
+ | funargs
+ { $$ = revchain($1); }
+ ;
+
+funargs: expr
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | funargs SCOMMA expr
+ { $$ = mkchain((char *)$3, $1); }
+ ;
+
+
+expr: uexpr
+ | SLPAR expr SRPAR { $$ = $2; if ($$->tag == TPRIM)
+ $$->primblock.parenused = 1; }
+ | complex_const
+ ;
+
+uexpr: lhs
+ | simple_const
+ | expr addop expr %prec SPLUS
+ { $$ = mkexpr($2, $1, $3); }
+ | expr SSTAR expr
+ { $$ = mkexpr(OPSTAR, $1, $3); }
+ | expr SSLASH expr
+ { $$ = mkexpr(OPSLASH, $1, $3); }
+ | expr SPOWER expr
+ { $$ = mkexpr(OPPOWER, $1, $3); }
+ | addop expr %prec SSTAR
+ { if($1 == OPMINUS)
+ $$ = mkexpr(OPNEG, $2, ENULL);
+ else $$ = $2;
+ }
+ | expr relop expr %prec SEQ
+ { $$ = mkexpr($2, $1, $3); }
+ | expr SEQV expr
+ { NO66(".EQV. operator");
+ $$ = mkexpr(OPEQV, $1,$3); }
+ | expr SNEQV expr
+ { NO66(".NEQV. operator");
+ $$ = mkexpr(OPNEQV, $1, $3); }
+ | expr SOR expr
+ { $$ = mkexpr(OPOR, $1, $3); }
+ | expr SAND expr
+ { $$ = mkexpr(OPAND, $1, $3); }
+ | SNOT expr
+ { $$ = mkexpr(OPNOT, $2, ENULL); }
+ | expr SCONCAT expr
+ { NO66("concatenation operator //");
+ $$ = mkexpr(OPCONCAT, $1, $3); }
+ ;
+
+addop: SPLUS { $$ = OPPLUS; }
+ | SMINUS { $$ = OPMINUS; }
+ ;
+
+relop: SEQ { $$ = OPEQ; }
+ | SGT { $$ = OPGT; }
+ | SLT { $$ = OPLT; }
+ | SGE { $$ = OPGE; }
+ | SLE { $$ = OPLE; }
+ | SNE { $$ = OPNE; }
+ ;
+
+lhs: name
+ { $$ = mkprim($1, LBNULL, CHNULL); }
+ | name substring
+ { NO66("substring operator :");
+ $$ = mkprim($1, LBNULL, $2); }
+ | name SLPAR funarglist SRPAR
+ { $$ = mkprim($1, mklist($3), CHNULL); }
+ | name SLPAR funarglist SRPAR substring
+ { NO66("substring operator :");
+ $$ = mkprim($1, mklist($3), $5); }
+ ;
+
+substring: SLPAR opt_expr SCOLON opt_expr SRPAR
+ { $$ = mkchain((char *)$2, mkchain((char *)$4,CHNULL)); }
+ ;
+
+opt_expr:
+ { $$ = 0; }
+ | expr
+ ;
+
+simple: name
+ { if($1->vclass == CLPARAM)
+ $$ = (expptr) cpexpr(
+ ( (struct Paramblock *) ($1) ) -> paramval);
+ }
+ | simple_const
+ ;
+
+simple_const: STRUE { $$ = mklogcon(1); }
+ | SFALSE { $$ = mklogcon(0); }
+ | SHOLLERITH { $$ = mkstrcon(toklen, token); }
+ | SICON = { $$ = mkintcon( convci(toklen, token) ); }
+ | SRCON = { $$ = mkrealcon(tyreal, token); }
+ | SDCON = { $$ = mkrealcon(TYDREAL, token); }
+ | bit_const
+ ;
+
+complex_const: SLPAR uexpr SCOMMA uexpr SRPAR
+ { $$ = mkcxcon($2,$4); }
+ ;
+
+bit_const: SHEXCON
+ { NOEXT("hex constant");
+ $$ = mkbitcon(4, toklen, token); }
+ | SOCTCON
+ { NOEXT("octal constant");
+ $$ = mkbitcon(3, toklen, token); }
+ | SBITCON
+ { NOEXT("binary constant");
+ $$ = mkbitcon(1, toklen, token); }
+ ;
+
+fexpr: unpar_fexpr
+ | SLPAR fexpr SRPAR
+ { $$ = $2; }
+ ;
+
+unpar_fexpr: lhs
+ | simple_const
+ | fexpr addop fexpr %prec SPLUS
+ { $$ = mkexpr($2, $1, $3); }
+ | fexpr SSTAR fexpr
+ { $$ = mkexpr(OPSTAR, $1, $3); }
+ | fexpr SSLASH fexpr
+ { $$ = mkexpr(OPSLASH, $1, $3); }
+ | fexpr SPOWER fexpr
+ { $$ = mkexpr(OPPOWER, $1, $3); }
+ | addop fexpr %prec SSTAR
+ { if($1 == OPMINUS)
+ $$ = mkexpr(OPNEG, $2, ENULL);
+ else $$ = $2;
+ }
+ | fexpr SCONCAT fexpr
+ { NO66("concatenation operator //");
+ $$ = mkexpr(OPCONCAT, $1, $3); }
+ ;
diff --git a/usr.bin/f2c/gram.head b/usr.bin/f2c/gram.head
new file mode 100644
index 0000000..dd822fd
--- /dev/null
+++ b/usr.bin/f2c/gram.head
@@ -0,0 +1,290 @@
+/****************************************************************
+Copyright 1990, 1993 by AT&T Bell Laboratories, Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+%{
+#include "defs.h"
+#include "p1defs.h"
+
+static int nstars; /* Number of labels in an
+ alternate return CALL */
+static int datagripe;
+static int ndim;
+static int vartype;
+int new_dcl;
+static ftnint varleng;
+static struct Dims dims[MAXDIM+1];
+extern struct Labelblock **labarray; /* Labels in an alternate
+ return CALL */
+extern int maxlablist;
+
+/* The next two variables are used to verify that each statement might be reached
+ during runtime. lastwasbranch is tested only in the defintion of the
+ stat: nonterminal. */
+
+int lastwasbranch = NO;
+static int thiswasbranch = NO;
+extern ftnint yystno;
+extern flag intonly;
+static chainp datastack;
+extern long laststfcn, thisstno;
+extern int can_include; /* for netlib */
+
+#define ESNULL (Extsym *)0
+#define NPNULL (Namep)0
+#define LBNULL (struct Listblock *)0
+
+ static void
+pop_datastack(Void) {
+ chainp d0 = datastack;
+ if (d0->datap)
+ curdtp = (chainp)d0->datap;
+ datastack = d0->nextp;
+ d0->nextp = 0;
+ frchain(&d0);
+ }
+
+%}
+
+/* Specify precedences and associativities. */
+
+%union {
+ int ival;
+ ftnint lval;
+ char *charpval;
+ chainp chval;
+ tagptr tagval;
+ expptr expval;
+ struct Labelblock *labval;
+ struct Nameblock *namval;
+ struct Eqvchain *eqvval;
+ Extsym *extval;
+ }
+
+%left SCOMMA
+%nonassoc SCOLON
+%right SEQUALS
+%left SEQV SNEQV
+%left SOR
+%left SAND
+%left SNOT
+%nonassoc SLT SGT SLE SGE SEQ SNE
+%left SCONCAT
+%left SPLUS SMINUS
+%left SSTAR SSLASH
+%right SPOWER
+
+%start program
+%type <labval> thislabel label assignlabel
+%type <tagval> other inelt
+%type <ival> type typespec typename dcl letter addop relop stop nameeq
+%type <lval> lengspec
+%type <charpval> filename
+%type <chval> datavar datavarlist namelistlist funarglist funargs
+%type <chval> dospec dospecw
+%type <chval> callarglist arglist args exprlist inlist outlist out2 substring
+%type <namval> name arg call var
+%type <expval> lhs expr uexpr opt_expr fexpr unpar_fexpr
+%type <expval> ubound simple value callarg complex_const simple_const bit_const
+%type <extval> common comblock entryname progname
+%type <eqvval> equivlist
+
+%%
+
+program:
+ | program stat SEOS
+ ;
+
+stat: thislabel entry
+ {
+/* stat: is the nonterminal for Fortran statements */
+
+ lastwasbranch = NO; }
+ | thislabel spec
+ | thislabel exec
+ { /* forbid further statement function definitions... */
+ if (parstate == INDATA && laststfcn != thisstno)
+ parstate = INEXEC;
+ thisstno++;
+ if($1 && ($1->labelno==dorange))
+ enddo($1->labelno);
+ if(lastwasbranch && thislabel==NULL)
+ warn("statement cannot be reached");
+ lastwasbranch = thiswasbranch;
+ thiswasbranch = NO;
+ if($1)
+ {
+ if($1->labtype == LABFORMAT)
+ err("label already that of a format");
+ else
+ $1->labtype = LABEXEC;
+ }
+ freetemps();
+ }
+ | thislabel SINCLUDE filename
+ { if (can_include)
+ doinclude( $3 );
+ else {
+ fprintf(diagfile, "Cannot open file %s\n", $3);
+ done(1);
+ }
+ }
+ | thislabel SEND end_spec
+ { if ($1)
+ lastwasbranch = NO;
+ endproc(); /* lastwasbranch = NO; -- set in endproc() */
+ }
+ | thislabel SUNKNOWN
+ { unclassifiable();
+
+/* flline flushes the current line, ignoring the rest of the text there */
+
+ flline(); }
+ | error
+ { flline(); needkwd = NO; inioctl = NO;
+ yyerrok; yyclearin; }
+ ;
+
+thislabel: SLABEL
+ {
+ if(yystno != 0)
+ {
+ $$ = thislabel = mklabel(yystno);
+ if( ! headerdone ) {
+ if (procclass == CLUNKNOWN)
+ procclass = CLMAIN;
+ puthead(CNULL, procclass);
+ }
+ if(thislabel->labdefined)
+ execerr("label %s already defined",
+ convic(thislabel->stateno) );
+ else {
+ if(thislabel->blklevel!=0 && thislabel->blklevel<blklevel
+ && thislabel->labtype!=LABFORMAT)
+ warn1("there is a branch to label %s from outside block",
+ convic( (ftnint) (thislabel->stateno) ) );
+ thislabel->blklevel = blklevel;
+ thislabel->labdefined = YES;
+ if(thislabel->labtype != LABFORMAT)
+ p1_label((long)(thislabel - labeltab));
+ }
+ }
+ else $$ = thislabel = NULL;
+ }
+ ;
+
+entry: SPROGRAM new_proc progname
+ {startproc($3, CLMAIN); }
+ | SPROGRAM new_proc progname progarglist
+ { warn("ignoring arguments to main program");
+ /* hashclear(); */
+ startproc($3, CLMAIN); }
+ | SBLOCK new_proc progname
+ { if($3) NO66("named BLOCKDATA");
+ startproc($3, CLBLOCK); }
+ | SSUBROUTINE new_proc entryname arglist
+ { entrypt(CLPROC, TYSUBR, (ftnint) 0, $3, $4); }
+ | SFUNCTION new_proc entryname arglist
+ { entrypt(CLPROC, TYUNKNOWN, (ftnint) 0, $3, $4); }
+ | type SFUNCTION new_proc entryname arglist
+ { entrypt(CLPROC, $1, varleng, $4, $5); }
+ | SENTRY entryname arglist
+ { if(parstate==OUTSIDE || procclass==CLMAIN
+ || procclass==CLBLOCK)
+ execerr("misplaced entry statement", CNULL);
+ entrypt(CLENTRY, 0, (ftnint) 0, $2, $3);
+ }
+ ;
+
+new_proc:
+ { newproc(); }
+ ;
+
+entryname: name
+ { $$ = newentry($1, 1); }
+ ;
+
+name: SNAME
+ { $$ = mkname(token); }
+ ;
+
+progname: { $$ = NULL; }
+ | entryname
+ ;
+
+progarglist:
+ SLPAR SRPAR
+ | SLPAR progargs SRPAR
+ ;
+
+progargs: progarg
+ | progargs SCOMMA progarg
+ ;
+
+progarg: SNAME
+ | SNAME SEQUALS SNAME
+ ;
+
+arglist:
+ { $$ = 0; }
+ | SLPAR SRPAR
+ { NO66(" () argument list");
+ $$ = 0; }
+ | SLPAR args SRPAR
+ {$$ = $2; }
+ ;
+
+args: arg
+ { $$ = ($1 ? mkchain((char *)$1,CHNULL) : CHNULL ); }
+ | args SCOMMA arg
+ { if($3) $1 = $$ = mkchain((char *)$3, $1); }
+ ;
+
+arg: name
+ { if($1->vstg!=STGUNKNOWN && $1->vstg!=STGARG)
+ dclerr("name declared as argument after use", $1);
+ $1->vstg = STGARG;
+ }
+ | SSTAR
+ { NO66("altenate return argument");
+
+/* substars means that '*'ed formal parameters should be replaced.
+ This is used to specify alternate return labels; in theory, only
+ parameter slots which have '*' should accept the statement labels.
+ This compiler chooses to ignore the '*'s in the formal declaration, and
+ always return the proper value anyway.
+
+ This variable is only referred to in proc.c */
+
+ $$ = 0; substars = YES; }
+ ;
+
+
+
+filename: SHOLLERITH
+ {
+ char *s;
+ s = copyn(toklen+1, token);
+ s[toklen] = '\0';
+ $$ = s;
+ }
+ ;
diff --git a/usr.bin/f2c/gram.io b/usr.bin/f2c/gram.io
new file mode 100644
index 0000000..f1a6649
--- /dev/null
+++ b/usr.bin/f2c/gram.io
@@ -0,0 +1,173 @@
+ /* Input/Output Statements */
+
+io: io1
+ { endio(); }
+ ;
+
+io1: iofmove ioctl
+ | iofmove unpar_fexpr
+ { ioclause(IOSUNIT, $2); endioctl(); }
+ | iofmove SSTAR
+ { ioclause(IOSUNIT, ENULL); endioctl(); }
+ | iofmove SPOWER
+ { ioclause(IOSUNIT, IOSTDERR); endioctl(); }
+ | iofctl ioctl
+ | read ioctl
+ { doio(CHNULL); }
+ | read infmt
+ { doio(CHNULL); }
+ | read ioctl inlist
+ { doio(revchain($3)); }
+ | read infmt SCOMMA inlist
+ { doio(revchain($4)); }
+ | read ioctl SCOMMA inlist
+ { doio(revchain($4)); }
+ | write ioctl
+ { doio(CHNULL); }
+ | write ioctl outlist
+ { doio(revchain($3)); }
+ | print
+ { doio(CHNULL); }
+ | print SCOMMA outlist
+ { doio(revchain($3)); }
+ ;
+
+iofmove: fmkwd end_spec in_ioctl
+ ;
+
+fmkwd: SBACKSPACE
+ { iostmt = IOBACKSPACE; }
+ | SREWIND
+ { iostmt = IOREWIND; }
+ | SENDFILE
+ { iostmt = IOENDFILE; }
+ ;
+
+iofctl: ctlkwd end_spec in_ioctl
+ ;
+
+ctlkwd: SINQUIRE
+ { iostmt = IOINQUIRE; }
+ | SOPEN
+ { iostmt = IOOPEN; }
+ | SCLOSE
+ { iostmt = IOCLOSE; }
+ ;
+
+infmt: unpar_fexpr
+ {
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, $1);
+ endioctl();
+ }
+ | SSTAR
+ {
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, ENULL);
+ endioctl();
+ }
+ ;
+
+ioctl: SLPAR fexpr SRPAR
+ {
+ ioclause(IOSUNIT, $2);
+ endioctl();
+ }
+ | SLPAR ctllist SRPAR
+ { endioctl(); }
+ ;
+
+ctllist: ioclause
+ | ctllist SCOMMA ioclause
+ ;
+
+ioclause: fexpr
+ { ioclause(IOSPOSITIONAL, $1); }
+ | SSTAR
+ { ioclause(IOSPOSITIONAL, ENULL); }
+ | SPOWER
+ { ioclause(IOSPOSITIONAL, IOSTDERR); }
+ | nameeq expr
+ { ioclause($1, $2); }
+ | nameeq SSTAR
+ { ioclause($1, ENULL); }
+ | nameeq SPOWER
+ { ioclause($1, IOSTDERR); }
+ ;
+
+nameeq: SNAMEEQ
+ { $$ = iocname(); }
+ ;
+
+read: SREAD end_spec in_ioctl
+ { iostmt = IOREAD; }
+ ;
+
+write: SWRITE end_spec in_ioctl
+ { iostmt = IOWRITE; }
+ ;
+
+print: SPRINT end_spec fexpr in_ioctl
+ {
+ iostmt = IOWRITE;
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, $3);
+ endioctl();
+ }
+ | SPRINT end_spec SSTAR in_ioctl
+ {
+ iostmt = IOWRITE;
+ ioclause(IOSUNIT, ENULL);
+ ioclause(IOSFMT, ENULL);
+ endioctl();
+ }
+ ;
+
+inlist: inelt
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | inlist SCOMMA inelt
+ { $$ = mkchain((char *)$3, $1); }
+ ;
+
+inelt: lhs
+ { $$ = (tagptr) $1; }
+ | SLPAR inlist SCOMMA dospec SRPAR
+ { $$ = (tagptr) mkiodo($4,revchain($2)); }
+ ;
+
+outlist: uexpr
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | other
+ { $$ = mkchain((char *)$1, CHNULL); }
+ | out2
+ ;
+
+out2: uexpr SCOMMA uexpr
+ { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); }
+ | uexpr SCOMMA other
+ { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); }
+ | other SCOMMA uexpr
+ { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); }
+ | other SCOMMA other
+ { $$ = mkchain((char *)$3, mkchain((char *)$1, CHNULL) ); }
+ | out2 SCOMMA uexpr
+ { $$ = mkchain((char *)$3, $1); }
+ | out2 SCOMMA other
+ { $$ = mkchain((char *)$3, $1); }
+ ;
+
+other: complex_const
+ { $$ = (tagptr) $1; }
+ | SLPAR expr SRPAR
+ { $$ = (tagptr) $2; }
+ | SLPAR uexpr SCOMMA dospec SRPAR
+ { $$ = (tagptr) mkiodo($4, mkchain((char *)$2, CHNULL) ); }
+ | SLPAR other SCOMMA dospec SRPAR
+ { $$ = (tagptr) mkiodo($4, mkchain((char *)$2, CHNULL) ); }
+ | SLPAR out2 SCOMMA dospec SRPAR
+ { $$ = (tagptr) mkiodo($4, revchain($2)); }
+ ;
+
+in_ioctl:
+ { startioctl(); }
+ ;
diff --git a/usr.bin/f2c/index b/usr.bin/f2c/index
new file mode 100644
index 0000000..5212535
--- /dev/null
+++ b/usr.bin/f2c/index
@@ -0,0 +1,127 @@
+# ====== index for f2c/src ======
+# NOTE: "all from f2c/src" is the complete f2c source (sans libraries).
+# The remaining files in this directory are the component modules
+# of "all from f2c/src", so you can request just the modules that
+# have changed since last you updated your f2c source. You can
+# tell what has changed by looking at the timestamps at the end
+# of "readme from f2c".
+file f2c/src/cds.c
+
+file f2c/src/data.c
+
+file f2c/src/defines.h
+
+file f2c/src/defs.h
+
+file f2c/src/equiv.c
+
+file f2c/src/error.c
+
+file f2c/src/exec.c
+
+file f2c/src/expr.c
+
+file f2c/src/f2c.1
+
+file f2c/src/f2c.1t
+
+file f2c/src/f2c.h
+
+file f2c/src/format.c
+
+file f2c/src/format.h
+
+file f2c/src/formatdata.c
+
+file f2c/src/ftypes.h
+
+file f2c/src/gram.c
+
+file f2c/src/gram.dcl
+
+file f2c/src/gram.exec
+
+file f2c/src/gram.expr
+
+file f2c/src/gram.head
+
+file f2c/src/gram.io
+
+file f2c/src/init.c
+
+file f2c/src/intr.c
+
+file f2c/src/io.c
+
+file f2c/src/iob.h
+
+file f2c/src/lex.c
+
+file f2c/src/machdefs.h
+
+file f2c/src/main.c
+
+file f2c/src/makefile
+
+file f2c/src/malloc.c
+
+file f2c/src/mem.c
+
+file f2c/src/memset.c
+
+file f2c/src/misc.c
+
+file f2c/src/names.c
+
+file f2c/src/names.h
+
+file f2c/src/niceprintf.c
+
+file f2c/src/niceprintf.h
+
+file f2c/src/notice
+
+file f2c/src/output.c
+
+file f2c/src/output.h
+
+file f2c/src/p1defs.h
+
+file f2c/src/p1output.c
+
+file f2c/src/parse.h
+
+file f2c/src/parse_args.c
+
+file f2c/src/pccdefs.h
+
+file f2c/src/pread.c
+
+file f2c/src/proc.c
+
+file f2c/src/put.c
+
+file f2c/src/putpcc.c
+
+file f2c/src/sysdep.c
+
+file f2c/src/sysdep.h
+
+file f2c/src/tokens
+
+file f2c/src/usignal.h
+
+file f2c/src/vax.c
+
+file f2c/src/version.c
+
+file f2c/src/xsum.c
+
+file f2c/src/xsum0.out
+
+file f2c/src/Notice
+
+file f2c/src/README
+
+file f2c/src/readme
+
diff --git a/usr.bin/f2c/index.html b/usr.bin/f2c/index.html
new file mode 100644
index 0000000..2265f2a
--- /dev/null
+++ b/usr.bin/f2c/index.html
@@ -0,0 +1,138 @@
+<HTML>
+<HEAD><TITLE>/netlib/f2c/src</TITLE></HEAD>
+<BODY>
+<H2>/netlib/f2c/src</H2>
+<UL>
+<PRE>
+====== index for f2c/src ======
+NOTE: "all from f2c/src" is the complete f2c source (sans libraries).
+The remaining files in this directory are the component modules
+of "all from f2c/src", so you can request just the modules that
+have changed since last you updated your f2c source. You can
+tell what has changed by looking at the timestamps at the end
+of "readme from f2c".
+</PRE>
+<LI><EM>file: </EM><A HREF="cds.c.Z">cds.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/cds.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="data.c.Z">data.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/data.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="defines.h.Z">defines.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/defines.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="defs.h.Z">defs.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/defs.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="equiv.c.Z">equiv.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/equiv.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="error.c.Z">error.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/error.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="exec.c.Z">exec.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/exec.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="expr.c.Z">expr.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/expr.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="f2c.1.Z">f2c.1</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/f2c.1.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="f2c.1t.Z">f2c.1t</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/f2c.1t.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="f2c.h.Z">f2c.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/f2c.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="format.c.Z">format.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/format.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="format.h.Z">format.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/format.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="formatdata.c.Z">formatdata.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/formatdata.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="ftypes.h.Z">ftypes.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/ftypes.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.c.Z">gram.c</A><MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.dcl.Z">gram.dcl</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/gram.dcl.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.exec.Z">gram.exec</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/gram.exec.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.expr.Z">gram.expr</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/gram.expr.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.head.Z">gram.head</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/gram.head.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="gram.io.Z">gram.io</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/gram.io.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="init.c.Z">init.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/init.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="intr.c.Z">intr.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/intr.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="io.c.Z">io.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/io.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="iob.h.Z">iob.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/iob.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="lex.c.Z">lex.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/lex.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="machdefs.h.Z">machdefs.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/machdefs.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="main.c.Z">main.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/main.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="makefile.Z">makefile</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/makefile.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="malloc.c.Z">malloc.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/malloc.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="mem.c.Z">mem.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/mem.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="memset.c.Z">memset.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/memset.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="misc.c.Z">misc.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/misc.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="names.c.Z">names.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/names.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="names.h.Z">names.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/names.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="niceprintf.c.Z">niceprintf.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/niceprintf.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="niceprintf.h.Z">niceprintf.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/niceprintf.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="notice.Z">notice</A><MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="output.c.Z">output.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/output.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="output.h.Z">output.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/output.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="p1defs.h.Z">p1defs.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/p1defs.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="p1output.c.Z">p1output.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/p1output.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="parse.h.Z">parse.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/parse.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="parse_args.c.Z">parse_args.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/parse_args.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="pccdefs.h.Z">pccdefs.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/pccdefs.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="pread.c.Z">pread.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/pread.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="proc.c.Z">proc.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/proc.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="put.c.Z">put.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/put.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="putpcc.c.Z">putpcc.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/putpcc.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="sysdep.c.Z">sysdep.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/sysdep.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="sysdep.h.Z">sysdep.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/sysdep.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="tokens.Z">tokens</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/tokens.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="usignal.h.Z">usignal.h</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/usignal.h.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="vax.c.Z">vax.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/vax.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="version.c.Z">version.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/version.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="xsum.c.Z">xsum.c</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/xsum.c.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="xsum0.out.Z">xsum0.out</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/xsum0.out.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="Notice.Z">Notice</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/Notice.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="README.Z">README</A> (<A HREF="ftp://netlib.att.com/netlib.depend/f2c/src/README.tar">with dependencies</A>)<MENU>
+</MENU>
+<LI><EM>file: </EM><A HREF="readme.Z">readme</A><MENU>
+</MENU>
+<P><LI><A HREF="/netlib/bib/thesaurus.html.Z">index help</A>
+</UL>
+<P><A HREF="/netlib/bib/ericjack.html.Z">Eric and Jack</EM>
+</BODY></HTML>
diff --git a/usr.bin/f2c/init.c b/usr.bin/f2c/init.c
new file mode 100644
index 0000000..c9a9702
--- /dev/null
+++ b/usr.bin/f2c/init.c
@@ -0,0 +1,516 @@
+/****************************************************************
+Copyright 1990, 1992 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "output.h"
+#include "iob.h"
+
+/* State required for the C output */
+char *fl_fmt_string; /* Float format string */
+char *db_fmt_string; /* Double format string */
+char *cm_fmt_string; /* Complex format string */
+char *dcm_fmt_string; /* Double complex format string */
+
+chainp new_vars = CHNULL; /* List of newly created locals in this
+ function. These may have identifiers
+ which have underscores and more than VL
+ characters */
+chainp used_builtins = CHNULL; /* List of builtins used by this function.
+ These are all Addrps with UNAM_EXTERN
+ */
+chainp assigned_fmts = CHNULL; /* assigned formats */
+chainp allargs; /* union of args in all entry points */
+chainp earlylabs; /* labels seen before enddcl() */
+char main_alias[52]; /* PROGRAM name, if any is given */
+int tab_size = 4;
+
+
+FILEP infile;
+FILEP diagfile;
+
+FILEP c_file;
+FILEP pass1_file;
+FILEP initfile;
+FILEP blkdfile;
+
+
+char *token;
+int maxtoklen, toklen;
+long lineno; /* Current line in the input file, NOT the
+ Fortran statement label number */
+char *infname;
+int needkwd;
+struct Labelblock *thislabel = NULL;
+int nerr;
+int nwarn;
+
+flag saveall;
+flag substars;
+int parstate = OUTSIDE;
+flag headerdone = NO;
+int blklevel;
+int doin_setbound;
+int impltype[26];
+ftnint implleng[26];
+int implstg[26];
+
+int tyint = TYLONG ;
+int tylogical = TYLONG;
+int tylog = TYLOGICAL;
+int typesize[NTYPES] = {
+ 1, SZADDR, 1, SZSHORT, SZLONG,
+#ifdef TYQUAD
+ 2*SZLONG,
+#endif
+ SZLONG, 2*SZLONG,
+ 2*SZLONG, 4*SZLONG, 1, SZSHORT, SZLONG, 1, 1, 0,
+ 4*SZLONG + SZADDR, /* sizeof(cilist) */
+ 4*SZLONG + 2*SZADDR, /* sizeof(icilist) */
+ 4*SZLONG + 5*SZADDR, /* sizeof(olist) */
+ 2*SZLONG + SZADDR, /* sizeof(cllist) */
+ 2*SZLONG, /* sizeof(alist) */
+ 11*SZLONG + 15*SZADDR /* sizeof(inlist) */
+ };
+
+int typealign[NTYPES] = {
+ 1, ALIADDR, 1, ALISHORT, ALILONG,
+#ifdef TYQUAD
+ ALIDOUBLE,
+#endif
+ ALILONG, ALIDOUBLE,
+ ALILONG, ALIDOUBLE, 1, ALISHORT, ALILONG, 1, 1, 1,
+ ALILONG, ALILONG, ALILONG, ALILONG, ALILONG, ALILONG};
+
+int type_choice[4] = { TYDREAL, TYSHORT, TYLONG, TYSHORT };
+
+char *typename[] = {
+ "<<unknown>>",
+ "address",
+ "integer1",
+ "shortint",
+ "integer",
+#ifdef TYQUAD
+ "longint",
+#endif
+ "real",
+ "doublereal",
+ "complex",
+ "doublecomplex",
+ "logical1",
+ "shortlogical",
+ "logical",
+ "char" /* character */
+ };
+
+int type_pref[NTYPES] = { 0, 0, 3, 5, 7,
+#ifdef TYQUAD
+ 10,
+#endif
+ 8, 11, 9, 12, 1, 4, 6, 2 };
+
+char *protorettypes[] = {
+ "?", "??", "integer1", "shortint", "integer",
+#ifdef TYQUAD
+ "longint",
+#endif
+ "real", "doublereal",
+ "C_f", "Z_f", "logical1", "shortlogical", "logical", "H_f", "int"
+ };
+
+char *casttypes[TYSUBR+1] = {
+ "U_fp", "??bug??", "I1_fp",
+ "J_fp", "I_fp",
+#ifdef TYQUAD
+ "Q_fp",
+#endif
+ "R_fp", "D_fp", "C_fp", "Z_fp",
+ "L1_fp", "L2_fp", "L_fp", "H_fp", "S_fp"
+ };
+char *usedcasts[TYSUBR+1];
+
+char *dfltarg[] = {
+ 0, 0, "(integer1 *)0",
+ "(shortint *)0", "(integer *)0",
+#ifdef TYQUAD
+ "(longint *)0",
+#endif
+ "(real *)0",
+ "(doublereal *)0", "(complex *)0", "(doublecomplex *)0",
+ "(logical1 *)0","(shortlogical *)0", "(logical *)0", "(char *)0"
+ };
+
+static char *dflt0proc[] = {
+ 0, 0, "(integer1 (*)())0",
+ "(shortint (*)())0", "(integer (*)())0",
+#ifdef TYQUAD
+ "(longint (*)())0",
+#endif
+ "(real (*)())0",
+ "(doublereal (*)())0", "(complex (*)())0", "(doublecomplex (*)())0",
+ "(logical1 (*)())0", "(shortlogical (*)())0",
+ "(logical (*)())0", "(char (*)())0", "(int (*)())0"
+ };
+
+char *dflt1proc[] = { "(U_fp)0", "(??bug??)0", "(I1_fp)0",
+ "(J_fp)0", "(I_fp)0",
+#ifdef TYQUAD
+ "(Q_fp)0",
+#endif
+ "(R_fp)0", "(D_fp)0", "(C_fp)0", "(Z_fp)0",
+ "(L1_fp)0","(L2_fp)0",
+ "(L_fp)0", "(H_fp)0", "(S_fp)0"
+ };
+
+char **dfltproc = dflt0proc;
+
+static char Bug[] = "bug";
+
+char *ftn_types[] = { "external", "??", "integer*1",
+ "integer*2", "integer",
+#ifdef TYQUAD
+ "integer*8",
+#endif
+ "real",
+ "double precision", "complex", "double complex",
+ "logical*1", "logical*2",
+ "logical", "character", "subroutine",
+ Bug,Bug,Bug,Bug,Bug,Bug,Bug,Bug,Bug, "ftnlen"
+ };
+
+int init_ac[TYSUBR+1] = { 0,0,0,0,0,0,0,
+#ifdef TYQUAD
+ 0,
+#endif
+ 1, 1, 0, 0, 0, 2};
+
+int proctype = TYUNKNOWN;
+char *procname;
+int rtvlabel[NTYPES0];
+Addrp retslot; /* Holds automatic variable which was
+ allocated the function return value
+ */
+Addrp xretslot[NTYPES0]; /* for multiple entry points */
+int cxslot = -1;
+int chslot = -1;
+int chlgslot = -1;
+int procclass = CLUNKNOWN;
+int nentry;
+int nallargs;
+int nallchargs;
+flag multitype;
+ftnint procleng;
+long lastiolabno;
+int lastlabno;
+int lastvarno;
+int lastargslot;
+int autonum[TYVOID];
+char *av_pfix[TYVOID] = {"??TYUNKNOWN??", "a","i1","s","i",
+#ifdef TYQUAD
+ "i8",
+#endif
+ "r","d","q","z","L1","L2","L","ch",
+ "??TYSUBR??", "??TYERROR??","ci", "ici",
+ "o", "cl", "al", "ioin" };
+
+extern int maxctl;
+struct Ctlframe *ctls;
+struct Ctlframe *ctlstack;
+struct Ctlframe *lastctl;
+
+Namep regnamep[MAXREGVAR];
+int highregvar;
+int nregvar;
+
+extern int maxext;
+Extsym *extsymtab;
+Extsym *nextext;
+Extsym *lastext;
+
+extern int maxequiv;
+struct Equivblock *eqvclass;
+
+extern int maxhash;
+struct Hashentry *hashtab;
+struct Hashentry *lasthash;
+
+extern int maxstno; /* Maximum number of statement labels */
+struct Labelblock *labeltab;
+struct Labelblock *labtabend;
+struct Labelblock *highlabtab;
+
+int maxdim = MAXDIM;
+struct Rplblock *rpllist = NULL;
+struct Chain *curdtp = NULL;
+flag toomanyinit;
+ftnint curdtelt;
+chainp templist[TYVOID];
+chainp holdtemps;
+int dorange = 0;
+struct Entrypoint *entries = NULL;
+
+chainp chains = NULL;
+
+flag inioctl;
+int iostmt;
+int nioctl;
+int nequiv = 0;
+int eqvstart = 0;
+int nintnames = 0;
+extern int maxlablist;
+struct Labelblock **labarray;
+
+struct Literal *litpool;
+int nliterals;
+
+char dflttype[26];
+char hextoi_tab[Table_size], Letters[Table_size];
+char *ei_first, *ei_next, *ei_last;
+char *wh_first, *wh_next, *wh_last;
+
+#define ALLOCN(n,x) (struct x *) ckalloc((n)*sizeof(struct x))
+
+ void
+fileinit(Void)
+{
+ register char *s;
+ register int i, j;
+
+ lastiolabno = 100000;
+ lastlabno = 0;
+ lastvarno = 0;
+ nliterals = 0;
+ nerr = 0;
+
+ infile = stdin;
+
+ maxtoklen = 502;
+ token = (char *)ckalloc(maxtoklen+2);
+ memset(dflttype, tyreal, 26);
+ memset(dflttype + 'i' - 'a', tyint, 6);
+ memset(hextoi_tab, 16, sizeof(hextoi_tab));
+ for(i = 0, s = "0123456789abcdef"; *s; i++, s++)
+ hextoi(*s) = i;
+ for(i = 10, s = "ABCDEF"; *s; i++, s++)
+ hextoi(*s) = i;
+ for(j = 0, s = "abcdefghijklmnopqrstuvwxyz"; i = *s++; j++)
+ Letters[i] = Letters[i+'A'-'a'] = j;
+
+ ctls = ALLOCN(maxctl+1, Ctlframe);
+ extsymtab = ALLOCN(maxext, Extsym);
+ eqvclass = ALLOCN(maxequiv, Equivblock);
+ hashtab = ALLOCN(maxhash, Hashentry);
+ labeltab = ALLOCN(maxstno, Labelblock);
+ litpool = ALLOCN(maxliterals, Literal);
+ labarray = (struct Labelblock **)ckalloc(maxlablist*
+ sizeof(struct Labelblock *));
+ fmt_init();
+ mem_init();
+ np_init();
+
+ ctlstack = ctls++;
+ lastctl = ctls + maxctl;
+ nextext = extsymtab;
+ lastext = extsymtab + maxext;
+ lasthash = hashtab + maxhash;
+ labtabend = labeltab + maxstno;
+ highlabtab = labeltab;
+ main_alias[0] = '\0';
+ if (forcedouble)
+ dfltproc[TYREAL] = dfltproc[TYDREAL];
+
+/* Initialize the routines for providing C output */
+
+ out_init ();
+}
+
+ void
+hashclear(Void) /* clear hash table */
+{
+ register struct Hashentry *hp;
+ register Namep p;
+ register struct Dimblock *q;
+ register int i;
+
+ for(hp = hashtab ; hp < lasthash ; ++hp)
+ if(p = hp->varp)
+ {
+ frexpr(p->vleng);
+ if(q = p->vdim)
+ {
+ for(i = 0 ; i < q->ndim ; ++i)
+ {
+ frexpr(q->dims[i].dimsize);
+ frexpr(q->dims[i].dimexpr);
+ }
+ frexpr(q->nelt);
+ frexpr(q->baseoffset);
+ frexpr(q->basexpr);
+ free( (charptr) q);
+ }
+ if(p->vclass == CLNAMELIST)
+ frchain( &(p->varxptr.namelist) );
+ free( (charptr) p);
+ hp->varp = NULL;
+ }
+ }
+
+ void
+procinit(Void)
+{
+ register struct Labelblock *lp;
+ struct Chain *cp;
+ int i;
+ struct memblock;
+ extern struct memblock *curmemblock, *firstmemblock;
+ extern char *mem_first, *mem_next, *mem_last, *mem0_last;
+
+ curmemblock = firstmemblock;
+ mem_next = mem_first;
+ mem_last = mem0_last;
+ ei_next = ei_first = ei_last = 0;
+ wh_next = wh_first = wh_last = 0;
+ iob_list = 0;
+ for(i = 0; i < 9; i++)
+ io_structs[i] = 0;
+
+ parstate = OUTSIDE;
+ headerdone = NO;
+ blklevel = 1;
+ saveall = NO;
+ substars = NO;
+ nwarn = 0;
+ thislabel = NULL;
+ needkwd = 0;
+
+ proctype = TYUNKNOWN;
+ procname = "MAIN_";
+ procclass = CLUNKNOWN;
+ nentry = 0;
+ nallargs = nallchargs = 0;
+ multitype = NO;
+ retslot = NULL;
+ for(i = 0; i < NTYPES0; i++) {
+ frexpr((expptr)xretslot[i]);
+ xretslot[i] = 0;
+ }
+ cxslot = -1;
+ chslot = -1;
+ chlgslot = -1;
+ procleng = 0;
+ blklevel = 1;
+ lastargslot = 0;
+
+ for(lp = labeltab ; lp < labtabend ; ++lp)
+ lp->stateno = 0;
+
+ hashclear();
+
+/* Clear the list of newly generated identifiers from the previous
+ function */
+
+ frexchain(&new_vars);
+ frexchain(&used_builtins);
+ frchain(&assigned_fmts);
+ frchain(&allargs);
+ frchain(&earlylabs);
+
+ nintnames = 0;
+ highlabtab = labeltab;
+
+ ctlstack = ctls - 1;
+ for(i = TYADDR; i < TYVOID; i++) {
+ for(cp = templist[i]; cp ; cp = cp->nextp)
+ free( (charptr) (cp->datap) );
+ frchain(templist + i);
+ autonum[i] = 0;
+ }
+ holdtemps = NULL;
+ dorange = 0;
+ nregvar = 0;
+ highregvar = 0;
+ entries = NULL;
+ rpllist = NULL;
+ inioctl = NO;
+ eqvstart += nequiv;
+ nequiv = 0;
+ dcomplex_seen = 0;
+
+ for(i = 0 ; i<NTYPES0 ; ++i)
+ rtvlabel[i] = 0;
+
+ if(undeftype)
+ setimpl(TYUNKNOWN, (ftnint) 0, 'a', 'z');
+ else
+ {
+ setimpl(tyreal, (ftnint) 0, 'a', 'z');
+ setimpl(tyint, (ftnint) 0, 'i', 'n');
+ }
+ setimpl(-STGBSS, (ftnint) 0, 'a', 'z'); /* set class */
+}
+
+
+
+ void
+#ifdef KR_headers
+setimpl(type, length, c1, c2)
+ int type;
+ ftnint length;
+ int c1;
+ int c2;
+#else
+setimpl(int type, ftnint length, int c1, int c2)
+#endif
+{
+ int i;
+ char buff[100];
+
+ if(c1==0 || c2==0)
+ return;
+
+ if(c1 > c2) {
+ sprintf(buff, "characters out of order in implicit:%c-%c", c1, c2);
+ err(buff);
+ }
+ else {
+ c1 = letter(c1);
+ c2 = letter(c2);
+ if(type < 0)
+ for(i = c1 ; i<=c2 ; ++i)
+ implstg[i] = - type;
+ else {
+ type = lengtype(type, length);
+ if(type == TYCHAR) {
+ if (length < 0) {
+ err("length (*) in implicit");
+ length = 1;
+ }
+ }
+ else if (type != TYLONG)
+ length = 0;
+ for(i = c1 ; i<=c2 ; ++i) {
+ impltype[i] = type;
+ implleng[i] = length;
+ }
+ }
+ }
+ }
diff --git a/usr.bin/f2c/intr.c b/usr.bin/f2c/intr.c
new file mode 100644
index 0000000..be4bcb7
--- /dev/null
+++ b/usr.bin/f2c/intr.c
@@ -0,0 +1,879 @@
+/****************************************************************
+Copyright 1990, 1992, 1994-5 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "names.h"
+
+union
+ {
+ int ijunk;
+ struct Intrpacked bits;
+ } packed;
+
+struct Intrbits
+ {
+ char intrgroup /* :3 */;
+ char intrstuff /* result type or number of generics */;
+ char intrno /* :7 */;
+ char dblcmplx;
+ char dblintrno; /* for -r8 */
+ };
+
+/* List of all intrinsic functions. */
+
+LOCAL struct Intrblock
+ {
+ char intrfname[8];
+ struct Intrbits intrval;
+ } intrtab[ ] =
+{
+"int", { INTRCONV, TYLONG },
+"real", { INTRCONV, TYREAL, 1 },
+ /* 1 ==> real(TYDCOMPLEX) yields TYDREAL */
+"dble", { INTRCONV, TYDREAL },
+"cmplx", { INTRCONV, TYCOMPLEX },
+"dcmplx", { INTRCONV, TYDCOMPLEX, 0, 1 },
+"ifix", { INTRCONV, TYLONG },
+"idint", { INTRCONV, TYLONG },
+"float", { INTRCONV, TYREAL },
+"dfloat", { INTRCONV, TYDREAL },
+"sngl", { INTRCONV, TYREAL },
+"ichar", { INTRCONV, TYLONG },
+"iachar", { INTRCONV, TYLONG },
+"char", { INTRCONV, TYCHAR },
+"achar", { INTRCONV, TYCHAR },
+
+/* any MAX or MIN can be used with any types; the compiler will cast them
+ correctly. So rules against bad syntax in these expressions are not
+ enforced */
+
+"max", { INTRMAX, TYUNKNOWN },
+"max0", { INTRMAX, TYLONG },
+"amax0", { INTRMAX, TYREAL },
+"max1", { INTRMAX, TYLONG },
+"amax1", { INTRMAX, TYREAL },
+"dmax1", { INTRMAX, TYDREAL },
+
+"and", { INTRBOOL, TYUNKNOWN, OPBITAND },
+"or", { INTRBOOL, TYUNKNOWN, OPBITOR },
+"xor", { INTRBOOL, TYUNKNOWN, OPBITXOR },
+"not", { INTRBOOL, TYUNKNOWN, OPBITNOT },
+"lshift", { INTRBOOL, TYUNKNOWN, OPLSHIFT },
+"rshift", { INTRBOOL, TYUNKNOWN, OPRSHIFT },
+
+"min", { INTRMIN, TYUNKNOWN },
+"min0", { INTRMIN, TYLONG },
+"amin0", { INTRMIN, TYREAL },
+"min1", { INTRMIN, TYLONG },
+"amin1", { INTRMIN, TYREAL },
+"dmin1", { INTRMIN, TYDREAL },
+
+"aint", { INTRGEN, 2, 0 },
+"dint", { INTRSPEC, TYDREAL, 1 },
+
+"anint", { INTRGEN, 2, 2 },
+"dnint", { INTRSPEC, TYDREAL, 3 },
+
+"nint", { INTRGEN, 4, 4 },
+"idnint", { INTRGEN, 2, 6 },
+
+"abs", { INTRGEN, 6, 8 },
+"iabs", { INTRGEN, 2, 9 },
+"dabs", { INTRSPEC, TYDREAL, 11 },
+"cabs", { INTRSPEC, TYREAL, 12, 0, 13 },
+"zabs", { INTRSPEC, TYDREAL, 13, 1 },
+
+"mod", { INTRGEN, 4, 14 },
+"amod", { INTRSPEC, TYREAL, 16, 0, 17 },
+"dmod", { INTRSPEC, TYDREAL, 17 },
+
+"sign", { INTRGEN, 4, 18 },
+"isign", { INTRGEN, 2, 19 },
+"dsign", { INTRSPEC, TYDREAL, 21 },
+
+"dim", { INTRGEN, 4, 22 },
+"idim", { INTRGEN, 2, 23 },
+"ddim", { INTRSPEC, TYDREAL, 25 },
+
+"dprod", { INTRSPEC, TYDREAL, 26 },
+
+"len", { INTRSPEC, TYLONG, 27 },
+"index", { INTRSPEC, TYLONG, 29 },
+
+"imag", { INTRGEN, 2, 31 },
+"aimag", { INTRSPEC, TYREAL, 31, 0, 32 },
+"dimag", { INTRSPEC, TYDREAL, 32 },
+
+"conjg", { INTRGEN, 2, 33 },
+"dconjg", { INTRSPEC, TYDCOMPLEX, 34, 1 },
+
+"sqrt", { INTRGEN, 4, 35 },
+"dsqrt", { INTRSPEC, TYDREAL, 36 },
+"csqrt", { INTRSPEC, TYCOMPLEX, 37, 0, 38 },
+"zsqrt", { INTRSPEC, TYDCOMPLEX, 38, 1 },
+
+"exp", { INTRGEN, 4, 39 },
+"dexp", { INTRSPEC, TYDREAL, 40 },
+"cexp", { INTRSPEC, TYCOMPLEX, 41, 0, 42 },
+"zexp", { INTRSPEC, TYDCOMPLEX, 42, 1 },
+
+"log", { INTRGEN, 4, 43 },
+"alog", { INTRSPEC, TYREAL, 43, 0, 44 },
+"dlog", { INTRSPEC, TYDREAL, 44 },
+"clog", { INTRSPEC, TYCOMPLEX, 45, 0, 46 },
+"zlog", { INTRSPEC, TYDCOMPLEX, 46, 1 },
+
+"log10", { INTRGEN, 2, 47 },
+"alog10", { INTRSPEC, TYREAL, 47, 0, 48 },
+"dlog10", { INTRSPEC, TYDREAL, 48 },
+
+"sin", { INTRGEN, 4, 49 },
+"dsin", { INTRSPEC, TYDREAL, 50 },
+"csin", { INTRSPEC, TYCOMPLEX, 51, 0, 52 },
+"zsin", { INTRSPEC, TYDCOMPLEX, 52, 1 },
+
+"cos", { INTRGEN, 4, 53 },
+"dcos", { INTRSPEC, TYDREAL, 54 },
+"ccos", { INTRSPEC, TYCOMPLEX, 55, 0, 56 },
+"zcos", { INTRSPEC, TYDCOMPLEX, 56, 1 },
+
+"tan", { INTRGEN, 2, 57 },
+"dtan", { INTRSPEC, TYDREAL, 58 },
+
+"asin", { INTRGEN, 2, 59 },
+"dasin", { INTRSPEC, TYDREAL, 60 },
+
+"acos", { INTRGEN, 2, 61 },
+"dacos", { INTRSPEC, TYDREAL, 62 },
+
+"atan", { INTRGEN, 2, 63 },
+"datan", { INTRSPEC, TYDREAL, 64 },
+
+"atan2", { INTRGEN, 2, 65 },
+"datan2", { INTRSPEC, TYDREAL, 66 },
+
+"sinh", { INTRGEN, 2, 67 },
+"dsinh", { INTRSPEC, TYDREAL, 68 },
+
+"cosh", { INTRGEN, 2, 69 },
+"dcosh", { INTRSPEC, TYDREAL, 70 },
+
+"tanh", { INTRGEN, 2, 71 },
+"dtanh", { INTRSPEC, TYDREAL, 72 },
+
+"lge", { INTRSPEC, TYLOGICAL, 73},
+"lgt", { INTRSPEC, TYLOGICAL, 75},
+"lle", { INTRSPEC, TYLOGICAL, 77},
+"llt", { INTRSPEC, TYLOGICAL, 79},
+
+#if 0
+"epbase", { INTRCNST, 4, 0 },
+"epprec", { INTRCNST, 4, 4 },
+"epemin", { INTRCNST, 2, 8 },
+"epemax", { INTRCNST, 2, 10 },
+"eptiny", { INTRCNST, 2, 12 },
+"ephuge", { INTRCNST, 4, 14 },
+"epmrsp", { INTRCNST, 2, 18 },
+#endif
+
+"fpexpn", { INTRGEN, 4, 81 },
+"fpabsp", { INTRGEN, 2, 85 },
+"fprrsp", { INTRGEN, 2, 87 },
+"fpfrac", { INTRGEN, 2, 89 },
+"fpmake", { INTRGEN, 2, 91 },
+"fpscal", { INTRGEN, 2, 93 },
+
+"" };
+
+
+LOCAL struct Specblock
+ {
+ char atype; /* Argument type; every arg must have
+ this type */
+ char rtype; /* Result type */
+ char nargs; /* Number of arguments */
+ char spxname[8]; /* Name of the function in Fortran */
+ char othername; /* index into callbyvalue table */
+ } spectab[ ] =
+{
+ { TYREAL,TYREAL,1,"r_int" },
+ { TYDREAL,TYDREAL,1,"d_int" },
+
+ { TYREAL,TYREAL,1,"r_nint" },
+ { TYDREAL,TYDREAL,1,"d_nint" },
+
+ { TYREAL,TYSHORT,1,"h_nint" },
+ { TYREAL,TYLONG,1,"i_nint" },
+
+ { TYDREAL,TYSHORT,1,"h_dnnt" },
+ { TYDREAL,TYLONG,1,"i_dnnt" },
+
+ { TYREAL,TYREAL,1,"r_abs" },
+ { TYSHORT,TYSHORT,1,"h_abs" },
+ { TYLONG,TYLONG,1,"i_abs" },
+ { TYDREAL,TYDREAL,1,"d_abs" },
+ { TYCOMPLEX,TYREAL,1,"c_abs" },
+ { TYDCOMPLEX,TYDREAL,1,"z_abs" },
+
+ { TYSHORT,TYSHORT,2,"h_mod" },
+ { TYLONG,TYLONG,2,"i_mod" },
+ { TYREAL,TYREAL,2,"r_mod" },
+ { TYDREAL,TYDREAL,2,"d_mod" },
+
+ { TYREAL,TYREAL,2,"r_sign" },
+ { TYSHORT,TYSHORT,2,"h_sign" },
+ { TYLONG,TYLONG,2,"i_sign" },
+ { TYDREAL,TYDREAL,2,"d_sign" },
+
+ { TYREAL,TYREAL,2,"r_dim" },
+ { TYSHORT,TYSHORT,2,"h_dim" },
+ { TYLONG,TYLONG,2,"i_dim" },
+ { TYDREAL,TYDREAL,2,"d_dim" },
+
+ { TYREAL,TYDREAL,2,"d_prod" },
+
+ { TYCHAR,TYSHORT,1,"h_len" },
+ { TYCHAR,TYLONG,1,"i_len" },
+
+ { TYCHAR,TYSHORT,2,"h_indx" },
+ { TYCHAR,TYLONG,2,"i_indx" },
+
+ { TYCOMPLEX,TYREAL,1,"r_imag" },
+ { TYDCOMPLEX,TYDREAL,1,"d_imag" },
+ { TYCOMPLEX,TYCOMPLEX,1,"r_cnjg" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"d_cnjg" },
+
+ { TYREAL,TYREAL,1,"r_sqrt", 1 },
+ { TYDREAL,TYDREAL,1,"d_sqrt", 1 },
+ { TYCOMPLEX,TYCOMPLEX,1,"c_sqrt" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"z_sqrt" },
+
+ { TYREAL,TYREAL,1,"r_exp", 2 },
+ { TYDREAL,TYDREAL,1,"d_exp", 2 },
+ { TYCOMPLEX,TYCOMPLEX,1,"c_exp" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"z_exp" },
+
+ { TYREAL,TYREAL,1,"r_log", 3 },
+ { TYDREAL,TYDREAL,1,"d_log", 3 },
+ { TYCOMPLEX,TYCOMPLEX,1,"c_log" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"z_log" },
+
+ { TYREAL,TYREAL,1,"r_lg10" },
+ { TYDREAL,TYDREAL,1,"d_lg10" },
+
+ { TYREAL,TYREAL,1,"r_sin", 4 },
+ { TYDREAL,TYDREAL,1,"d_sin", 4 },
+ { TYCOMPLEX,TYCOMPLEX,1,"c_sin" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"z_sin" },
+
+ { TYREAL,TYREAL,1,"r_cos", 5 },
+ { TYDREAL,TYDREAL,1,"d_cos", 5 },
+ { TYCOMPLEX,TYCOMPLEX,1,"c_cos" },
+ { TYDCOMPLEX,TYDCOMPLEX,1,"z_cos" },
+
+ { TYREAL,TYREAL,1,"r_tan", 6 },
+ { TYDREAL,TYDREAL,1,"d_tan", 6 },
+
+ { TYREAL,TYREAL,1,"r_asin", 7 },
+ { TYDREAL,TYDREAL,1,"d_asin", 7 },
+
+ { TYREAL,TYREAL,1,"r_acos", 8 },
+ { TYDREAL,TYDREAL,1,"d_acos", 8 },
+
+ { TYREAL,TYREAL,1,"r_atan", 9 },
+ { TYDREAL,TYDREAL,1,"d_atan", 9 },
+
+ { TYREAL,TYREAL,2,"r_atn2", 10 },
+ { TYDREAL,TYDREAL,2,"d_atn2", 10 },
+
+ { TYREAL,TYREAL,1,"r_sinh", 11 },
+ { TYDREAL,TYDREAL,1,"d_sinh", 11 },
+
+ { TYREAL,TYREAL,1,"r_cosh", 12 },
+ { TYDREAL,TYDREAL,1,"d_cosh", 12 },
+
+ { TYREAL,TYREAL,1,"r_tanh", 13 },
+ { TYDREAL,TYDREAL,1,"d_tanh", 13 },
+
+ { TYCHAR,TYLOGICAL,2,"hl_ge" },
+ { TYCHAR,TYLOGICAL,2,"l_ge" },
+
+ { TYCHAR,TYLOGICAL,2,"hl_gt" },
+ { TYCHAR,TYLOGICAL,2,"l_gt" },
+
+ { TYCHAR,TYLOGICAL,2,"hl_le" },
+ { TYCHAR,TYLOGICAL,2,"l_le" },
+
+ { TYCHAR,TYLOGICAL,2,"hl_lt" },
+ { TYCHAR,TYLOGICAL,2,"l_lt" },
+
+ { TYREAL,TYSHORT,1,"hr_expn" },
+ { TYREAL,TYLONG,1,"ir_expn" },
+ { TYDREAL,TYSHORT,1,"hd_expn" },
+ { TYDREAL,TYLONG,1,"id_expn" },
+
+ { TYREAL,TYREAL,1,"r_absp" },
+ { TYDREAL,TYDREAL,1,"d_absp" },
+
+ { TYREAL,TYDREAL,1,"r_rrsp" },
+ { TYDREAL,TYDREAL,1,"d_rrsp" },
+
+ { TYREAL,TYREAL,1,"r_frac" },
+ { TYDREAL,TYDREAL,1,"d_frac" },
+
+ { TYREAL,TYREAL,2,"r_make" },
+ { TYDREAL,TYDREAL,2,"d_make" },
+
+ { TYREAL,TYREAL,2,"r_scal" },
+ { TYDREAL,TYDREAL,2,"d_scal" },
+ { 0 }
+} ;
+
+#if 0
+LOCAL struct Incstblock
+ {
+ char atype;
+ char rtype;
+ char constno;
+ } consttab[ ] =
+{
+ { TYSHORT, TYLONG, 0 },
+ { TYLONG, TYLONG, 1 },
+ { TYREAL, TYLONG, 2 },
+ { TYDREAL, TYLONG, 3 },
+
+ { TYSHORT, TYLONG, 4 },
+ { TYLONG, TYLONG, 5 },
+ { TYREAL, TYLONG, 6 },
+ { TYDREAL, TYLONG, 7 },
+
+ { TYREAL, TYLONG, 8 },
+ { TYDREAL, TYLONG, 9 },
+
+ { TYREAL, TYLONG, 10 },
+ { TYDREAL, TYLONG, 11 },
+
+ { TYREAL, TYREAL, 0 },
+ { TYDREAL, TYDREAL, 1 },
+
+ { TYSHORT, TYLONG, 12 },
+ { TYLONG, TYLONG, 13 },
+ { TYREAL, TYREAL, 2 },
+ { TYDREAL, TYDREAL, 3 },
+
+ { TYREAL, TYREAL, 4 },
+ { TYDREAL, TYDREAL, 5 }
+};
+#endif
+
+char *callbyvalue[ ] =
+ {0,
+ "sqrt",
+ "exp",
+ "log",
+ "sin",
+ "cos",
+ "tan",
+ "asin",
+ "acos",
+ "atan",
+ "atan2",
+ "sinh",
+ "cosh",
+ "tanh"
+ };
+
+ void
+r8fix(Void) /* adjust tables for -r8 */
+{
+ register struct Intrblock *I;
+ register struct Specblock *S;
+
+ for(I = intrtab; I->intrfname[0]; I++)
+ if (I->intrval.intrgroup != INTRGEN)
+ switch(I->intrval.intrstuff) {
+ case TYREAL:
+ I->intrval.intrstuff = TYDREAL;
+ I->intrval.intrno = I->intrval.dblintrno;
+ break;
+ case TYCOMPLEX:
+ I->intrval.intrstuff = TYDCOMPLEX;
+ I->intrval.intrno = I->intrval.dblintrno;
+ I->intrval.dblcmplx = 1;
+ }
+
+ for(S = spectab; S->atype; S++)
+ switch(S->atype) {
+ case TYCOMPLEX:
+ S->atype = TYDCOMPLEX;
+ if (S->rtype == TYREAL)
+ S->rtype = TYDREAL;
+ else if (S->rtype == TYCOMPLEX)
+ S->rtype = TYDCOMPLEX;
+ switch(S->spxname[0]) {
+ case 'r':
+ S->spxname[0] = 'd';
+ break;
+ case 'c':
+ S->spxname[0] = 'z';
+ break;
+ default:
+ Fatal("r8fix bug");
+ }
+ break;
+ case TYREAL:
+ S->atype = TYDREAL;
+ switch(S->rtype) {
+ case TYREAL:
+ S->rtype = TYDREAL;
+ if (S->spxname[0] != 'r')
+ Fatal("r8fix bug");
+ S->spxname[0] = 'd';
+ case TYDREAL: /* d_prod */
+ break;
+
+ case TYSHORT:
+ if (!strcmp(S->spxname, "hr_expn"))
+ S->spxname[1] = 'd';
+ else if (!strcmp(S->spxname, "h_nint"))
+ strcpy(S->spxname, "h_dnnt");
+ else Fatal("r8fix bug");
+ break;
+
+ case TYLONG:
+ if (!strcmp(S->spxname, "ir_expn"))
+ S->spxname[1] = 'd';
+ else if (!strcmp(S->spxname, "i_nint"))
+ strcpy(S->spxname, "i_dnnt");
+ else Fatal("r8fix bug");
+ break;
+
+ default:
+ Fatal("r8fix bug");
+ }
+ }
+ }
+
+
+ expptr
+#ifdef KR_headers
+intrcall(np, argsp, nargs)
+ Namep np;
+ struct Listblock *argsp;
+ int nargs;
+#else
+intrcall(Namep np, struct Listblock *argsp, int nargs)
+#endif
+{
+ int i, rettype;
+ Addrp ap;
+ register struct Specblock *sp;
+ register struct Chain *cp;
+ expptr q, ep;
+ int mtype;
+ int op;
+ int f1field, f2field, f3field;
+
+ packed.ijunk = np->vardesc.varno;
+ f1field = packed.bits.f1;
+ f2field = packed.bits.f2;
+ f3field = packed.bits.f3;
+ if(nargs == 0)
+ goto badnargs;
+
+ mtype = 0;
+ for(cp = argsp->listp ; cp ; cp = cp->nextp)
+ {
+ ep = (expptr)cp->datap;
+ if( ISCONST(ep) && ep->headblock.vtype==TYSHORT )
+ cp->datap = (char *) mkconv(tyint, ep);
+ mtype = maxtype(mtype, ep->headblock.vtype);
+ }
+
+ switch(f1field)
+ {
+ case INTRBOOL:
+ op = f3field;
+ if( ! ONEOF(mtype, MSKINT|MSKLOGICAL) )
+ goto badtype;
+ if(op == OPBITNOT)
+ {
+ if(nargs != 1)
+ goto badnargs;
+ q = mkexpr(OPBITNOT, (expptr)argsp->listp->datap, ENULL);
+ }
+ else
+ {
+ if(nargs != 2)
+ goto badnargs;
+ q = mkexpr(op, (expptr)argsp->listp->datap,
+ (expptr)argsp->listp->nextp->datap);
+ }
+ frchain( &(argsp->listp) );
+ free( (charptr) argsp);
+ return(q);
+
+ case INTRCONV:
+ rettype = f2field;
+ switch(rettype) {
+ case TYLONG:
+ rettype = tyint;
+ break;
+ case TYLOGICAL:
+ rettype = tylog;
+ }
+ if( ISCOMPLEX(rettype) && nargs==2)
+ {
+ expptr qr, qi;
+ qr = (expptr) argsp->listp->datap;
+ qi = (expptr) argsp->listp->nextp->datap;
+ if (qr->headblock.vtype == TYDREAL
+ || qi->headblock.vtype == TYDREAL)
+ rettype = TYDCOMPLEX;
+ if(ISCONST(qr) && ISCONST(qi))
+ q = mkcxcon(qr,qi);
+ else q = mkexpr(OPCONV,mkconv(rettype-2,qr),
+ mkconv(rettype-2,qi));
+ }
+ else if(nargs == 1) {
+ if (f3field && ((Exprp)argsp->listp->datap)->vtype
+ == TYDCOMPLEX)
+ rettype = TYDREAL;
+ q = mkconv(rettype+100, (expptr)argsp->listp->datap);
+ if (q->tag == TADDR)
+ q->addrblock.parenused = 1;
+ }
+ else goto badnargs;
+
+ q->headblock.vtype = rettype;
+ frchain(&(argsp->listp));
+ free( (charptr) argsp);
+ return(q);
+
+
+#if 0
+ case INTRCNST:
+
+/* Machine-dependent f77 stuff that f2c omits:
+
+intcon contains
+ radix for short int
+ radix for long int
+ radix for single precision
+ radix for double precision
+ precision for short int
+ precision for long int
+ precision for single precision
+ precision for double precision
+ emin for single precision
+ emin for double precision
+ emax for single precision
+ emax for double prcision
+ largest short int
+ largest long int
+
+realcon contains
+ tiny for single precision
+ tiny for double precision
+ huge for single precision
+ huge for double precision
+ mrsp (epsilon) for single precision
+ mrsp (epsilon) for double precision
+*/
+ { register struct Incstblock *cstp;
+ extern ftnint intcon[14];
+ extern double realcon[6];
+
+ cstp = consttab + f3field;
+ for(i=0 ; i<f2field ; ++i)
+ if(cstp->atype == mtype)
+ goto foundconst;
+ else
+ ++cstp;
+ goto badtype;
+
+foundconst:
+ switch(cstp->rtype)
+ {
+ case TYLONG:
+ return(mkintcon(intcon[cstp->constno]));
+
+ case TYREAL:
+ case TYDREAL:
+ return(mkrealcon(cstp->rtype,
+ realcon[cstp->constno]) );
+
+ default:
+ Fatal("impossible intrinsic constant");
+ }
+ }
+#endif
+
+ case INTRGEN:
+ sp = spectab + f3field;
+ if(no66flag)
+ if(sp->atype == mtype)
+ goto specfunct;
+ else err66("generic function");
+
+ for(i=0; i<f2field ; ++i)
+ if(sp->atype == mtype)
+ goto specfunct;
+ else
+ ++sp;
+ warn1 ("bad argument type to intrinsic %s", np->fvarname);
+
+/* Made this a warning rather than an error so things like "log (5) ==>
+ log (5.0)" can be accommodated. When none of these cases matches, the
+ argument is cast up to the first type in the spectab list; this first
+ type is assumed to be the "smallest" type, e.g. REAL before DREAL
+ before COMPLEX, before DCOMPLEX */
+
+ sp = spectab + f3field;
+ mtype = sp -> atype;
+ goto specfunct;
+
+ case INTRSPEC:
+ sp = spectab + f3field;
+specfunct:
+ if(tyint==TYLONG && ONEOF(sp->rtype,M(TYSHORT)|M(TYLOGICAL))
+ && (sp+1)->atype==sp->atype)
+ ++sp;
+
+ if(nargs != sp->nargs)
+ goto badnargs;
+ if(mtype != sp->atype)
+ goto badtype;
+
+/* NOTE!! I moved fixargs (YES) into the ELSE branch so that constants in
+ the inline expression wouldn't get put into the constant table */
+
+ fixargs (NO, argsp);
+ cast_args (mtype, argsp -> listp);
+
+ if(q = Inline((int)(sp-spectab), mtype, argsp->listp))
+ {
+ frchain( &(argsp->listp) );
+ free( (charptr) argsp);
+ } else {
+
+ if(sp->othername) {
+ /* C library routines that return double... */
+ /* sp->rtype might be TYREAL */
+ ap = builtin(sp->rtype,
+ callbyvalue[sp->othername], 1);
+ q = fixexpr((Exprp)
+ mkexpr(OPCCALL, (expptr)ap, (expptr)argsp) );
+ } else {
+ fixargs(YES, argsp);
+ ap = builtin(sp->rtype, sp->spxname, 0);
+ q = fixexpr((Exprp)
+ mkexpr(OPCALL, (expptr)ap, (expptr)argsp) );
+ } /* else */
+ } /* else */
+ return(q);
+
+ case INTRMIN:
+ case INTRMAX:
+ if(nargs < 2)
+ goto badnargs;
+ if( ! ONEOF(mtype, MSKINT|MSKREAL) )
+ goto badtype;
+ argsp->vtype = mtype;
+ q = mkexpr( (f1field==INTRMIN ? OPMIN : OPMAX), (expptr)argsp, ENULL);
+
+ q->headblock.vtype = mtype;
+ rettype = f2field;
+ if(rettype == TYLONG)
+ rettype = tyint;
+ else if(rettype == TYUNKNOWN)
+ rettype = mtype;
+ return( mkconv(rettype, q) );
+
+ default:
+ fatali("intrcall: bad intrgroup %d", f1field);
+ }
+badnargs:
+ errstr("bad number of arguments to intrinsic %s", np->fvarname);
+ goto bad;
+
+badtype:
+ errstr("bad argument type to intrinsic %s", np->fvarname);
+
+bad:
+ return( errnode() );
+}
+
+
+
+ int
+#ifdef KR_headers
+intrfunct(s)
+ char *s;
+#else
+intrfunct(char *s)
+#endif
+{
+ register struct Intrblock *p;
+
+ for(p = intrtab; p->intrval.intrgroup!=INTREND ; ++p)
+ {
+ if( !strcmp(s, p->intrfname) )
+ {
+ packed.bits.f1 = p->intrval.intrgroup;
+ packed.bits.f2 = p->intrval.intrstuff;
+ packed.bits.f3 = p->intrval.intrno;
+ packed.bits.f4 = p->intrval.dblcmplx;
+ return(packed.ijunk);
+ }
+ }
+
+ return(0);
+}
+
+
+
+
+
+ Addrp
+#ifdef KR_headers
+intraddr(np)
+ Namep np;
+#else
+intraddr(Namep np)
+#endif
+{
+ Addrp q;
+ register struct Specblock *sp;
+ int f3field;
+
+ if(np->vclass!=CLPROC || np->vprocclass!=PINTRINSIC)
+ fatalstr("intraddr: %s is not intrinsic", np->fvarname);
+ packed.ijunk = np->vardesc.varno;
+ f3field = packed.bits.f3;
+
+ switch(packed.bits.f1)
+ {
+ case INTRGEN:
+ /* imag, log, and log10 arent specific functions */
+ if(f3field==31 || f3field==43 || f3field==47)
+ goto bad;
+
+ case INTRSPEC:
+ sp = spectab + f3field;
+ if (tyint == TYLONG
+ && (sp->rtype == TYSHORT || sp->rtype == TYLOGICAL))
+ ++sp;
+ q = builtin(sp->rtype, sp->spxname,
+ sp->othername ? 1 : 0);
+ return(q);
+
+ case INTRCONV:
+ case INTRMIN:
+ case INTRMAX:
+ case INTRBOOL:
+ case INTRCNST:
+bad:
+ errstr("cannot pass %s as actual", np->fvarname);
+ return((Addrp)errnode());
+ }
+ fatali("intraddr: impossible f1=%d\n", (int) packed.bits.f1);
+ /* NOT REACHED */ return 0;
+}
+
+
+
+ void
+#ifdef KR_headers
+cast_args(maxtype, args)
+ int maxtype;
+ chainp args;
+#else
+cast_args(int maxtype, chainp args)
+#endif
+{
+ for (; args; args = args -> nextp) {
+ expptr e = (expptr) args->datap;
+ if (e -> headblock.vtype != maxtype)
+ if (e -> tag == TCONST)
+ args->datap = (char *) mkconv(maxtype, e);
+ else {
+ Addrp temp = mktmp(maxtype, ENULL);
+
+ puteq(cpexpr((expptr)temp), e);
+ args->datap = (char *)temp;
+ } /* else */
+ } /* for */
+} /* cast_args */
+
+
+
+ expptr
+#ifdef KR_headers
+Inline(fno, type, args)
+ int fno;
+ int type;
+ struct Chain *args;
+#else
+Inline(int fno, int type, struct Chain *args)
+#endif
+{
+ register expptr q, t, t1;
+
+ switch(fno)
+ {
+ case 8: /* real abs */
+ case 9: /* short int abs */
+ case 10: /* long int abs */
+ case 11: /* double precision abs */
+ if( addressable(q = (expptr) args->datap) )
+ {
+ t = q;
+ q = NULL;
+ }
+ else
+ t = (expptr) mktmp(type,ENULL);
+ t1 = mkexpr(type == TYREAL && forcedouble ? OPDABS : OPABS,
+ cpexpr(t), ENULL);
+ if(q)
+ t1 = mkexpr(OPCOMMA, mkexpr(OPASSIGN, cpexpr(t),q), t1);
+ frexpr(t);
+ return(t1);
+
+ case 26: /* dprod */
+ q = mkexpr(OPSTAR, mkconv(TYDREAL,(expptr)args->datap),
+ (expptr)args->nextp->datap);
+ return(q);
+
+ case 27: /* len of character string */
+ q = (expptr) cpexpr(((tagptr)args->datap)->headblock.vleng);
+ frexpr((expptr)args->datap);
+ return mkconv(tyioint, q);
+
+ case 14: /* half-integer mod */
+ case 15: /* mod */
+ return mkexpr(OPMOD, (expptr) args->datap,
+ (expptr) args->nextp->datap);
+ }
+ return(NULL);
+}
diff --git a/usr.bin/f2c/io.c b/usr.bin/f2c/io.c
new file mode 100644
index 0000000..0e32f0e
--- /dev/null
+++ b/usr.bin/f2c/io.c
@@ -0,0 +1,1505 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* Routines to generate code for I/O statements.
+ Some corrections and improvements due to David Wasley, U. C. Berkeley
+*/
+
+/* TEMPORARY */
+#define TYIOINT TYLONG
+#define SZIOINT SZLONG
+
+#include "defs.h"
+#include "names.h"
+#include "iob.h"
+
+extern int inqmask;
+
+static void dofclose Argdcl((void));
+static void dofinquire Argdcl((void));
+static void dofmove Argdcl((char*));
+static void dofopen Argdcl((void));
+static void doiolist Argdcl((chainp));
+static void ioset Argdcl((int, int, expptr));
+static void ioseta Argdcl((int, Addrp));
+static void iosetc Argdcl((int, expptr));
+static void iosetip Argdcl((int, int));
+static void iosetlc Argdcl((int, int, int));
+static void putio Argdcl((expptr, expptr));
+static void putiocall Argdcl((expptr));
+
+iob_data *iob_list;
+Addrp io_structs[9];
+
+LOCAL char ioroutine[12];
+
+LOCAL long ioendlab;
+LOCAL long ioerrlab;
+LOCAL int endbit;
+LOCAL int errbit;
+LOCAL long jumplab;
+LOCAL long skiplab;
+LOCAL int ioformatted;
+LOCAL int statstruct = NO;
+LOCAL struct Labelblock *skiplabel;
+Addrp ioblkp;
+
+#define UNFORMATTED 0
+#define FORMATTED 1
+#define LISTDIRECTED 2
+#define NAMEDIRECTED 3
+
+#define V(z) ioc[z].iocval
+
+#define IOALL 07777
+
+LOCAL struct Ioclist
+{
+ char *iocname;
+ int iotype;
+ expptr iocval;
+}
+ioc[ ] =
+{
+ { "", 0 },
+ { "unit", IOALL },
+ { "fmt", M(IOREAD) | M(IOWRITE) },
+ { "err", IOALL },
+ { "end", M(IOREAD) },
+ { "iostat", IOALL },
+ { "rec", M(IOREAD) | M(IOWRITE) },
+ { "recl", M(IOOPEN) | M(IOINQUIRE) },
+ { "file", M(IOOPEN) | M(IOINQUIRE) },
+ { "status", M(IOOPEN) | M(IOCLOSE) },
+ { "access", M(IOOPEN) | M(IOINQUIRE) },
+ { "form", M(IOOPEN) | M(IOINQUIRE) },
+ { "blank", M(IOOPEN) | M(IOINQUIRE) },
+ { "exist", M(IOINQUIRE) },
+ { "opened", M(IOINQUIRE) },
+ { "number", M(IOINQUIRE) },
+ { "named", M(IOINQUIRE) },
+ { "name", M(IOINQUIRE) },
+ { "sequential", M(IOINQUIRE) },
+ { "direct", M(IOINQUIRE) },
+ { "formatted", M(IOINQUIRE) },
+ { "unformatted", M(IOINQUIRE) },
+ { "nextrec", M(IOINQUIRE) },
+ { "nml", M(IOREAD) | M(IOWRITE) }
+};
+
+#define NIOS (sizeof(ioc)/sizeof(struct Ioclist) - 1)
+
+/* #define IOSUNIT 1 */
+/* #define IOSFMT 2 */
+#define IOSERR 3
+#define IOSEND 4
+#define IOSIOSTAT 5
+#define IOSREC 6
+#define IOSRECL 7
+#define IOSFILE 8
+#define IOSSTATUS 9
+#define IOSACCESS 10
+#define IOSFORM 11
+#define IOSBLANK 12
+#define IOSEXISTS 13
+#define IOSOPENED 14
+#define IOSNUMBER 15
+#define IOSNAMED 16
+#define IOSNAME 17
+#define IOSSEQUENTIAL 18
+#define IOSDIRECT 19
+#define IOSFORMATTED 20
+#define IOSUNFORMATTED 21
+#define IOSNEXTREC 22
+#define IOSNML 23
+
+#define IOSTP V(IOSIOSTAT)
+
+
+/* offsets in generated structures */
+
+#define SZFLAG SZIOINT
+
+/* offsets for external READ and WRITE statements */
+
+#define XERR 0
+#define XUNIT SZFLAG
+#define XEND SZFLAG + SZIOINT
+#define XFMT 2*SZFLAG + SZIOINT
+#define XREC 2*SZFLAG + SZIOINT + SZADDR
+
+/* offsets for internal READ and WRITE statements */
+
+#define XIUNIT SZFLAG
+#define XIEND SZFLAG + SZADDR
+#define XIFMT 2*SZFLAG + SZADDR
+#define XIRLEN 2*SZFLAG + 2*SZADDR
+#define XIRNUM 2*SZFLAG + 2*SZADDR + SZIOINT
+#define XIREC 2*SZFLAG + 2*SZADDR + 2*SZIOINT
+
+/* offsets for OPEN statements */
+
+#define XFNAME SZFLAG + SZIOINT
+#define XFNAMELEN SZFLAG + SZIOINT + SZADDR
+#define XSTATUS SZFLAG + 2*SZIOINT + SZADDR
+#define XACCESS SZFLAG + 2*SZIOINT + 2*SZADDR
+#define XFORMATTED SZFLAG + 2*SZIOINT + 3*SZADDR
+#define XRECLEN SZFLAG + 2*SZIOINT + 4*SZADDR
+#define XBLANK SZFLAG + 3*SZIOINT + 4*SZADDR
+
+/* offset for CLOSE statement */
+
+#define XCLSTATUS SZFLAG + SZIOINT
+
+/* offsets for INQUIRE statement */
+
+#define XFILE SZFLAG + SZIOINT
+#define XFILELEN SZFLAG + SZIOINT + SZADDR
+#define XEXISTS SZFLAG + 2*SZIOINT + SZADDR
+#define XOPEN SZFLAG + 2*SZIOINT + 2*SZADDR
+#define XNUMBER SZFLAG + 2*SZIOINT + 3*SZADDR
+#define XNAMED SZFLAG + 2*SZIOINT + 4*SZADDR
+#define XNAME SZFLAG + 2*SZIOINT + 5*SZADDR
+#define XNAMELEN SZFLAG + 2*SZIOINT + 6*SZADDR
+#define XQACCESS SZFLAG + 3*SZIOINT + 6*SZADDR
+#define XQACCLEN SZFLAG + 3*SZIOINT + 7*SZADDR
+#define XSEQ SZFLAG + 4*SZIOINT + 7*SZADDR
+#define XSEQLEN SZFLAG + 4*SZIOINT + 8*SZADDR
+#define XDIRECT SZFLAG + 5*SZIOINT + 8*SZADDR
+#define XDIRLEN SZFLAG + 5*SZIOINT + 9*SZADDR
+#define XFORM SZFLAG + 6*SZIOINT + 9*SZADDR
+#define XFORMLEN SZFLAG + 6*SZIOINT + 10*SZADDR
+#define XFMTED SZFLAG + 7*SZIOINT + 10*SZADDR
+#define XFMTEDLEN SZFLAG + 7*SZIOINT + 11*SZADDR
+#define XUNFMT SZFLAG + 8*SZIOINT + 11*SZADDR
+#define XUNFMTLEN SZFLAG + 8*SZIOINT + 12*SZADDR
+#define XQRECL SZFLAG + 9*SZIOINT + 12*SZADDR
+#define XNEXTREC SZFLAG + 9*SZIOINT + 13*SZADDR
+#define XQBLANK SZFLAG + 9*SZIOINT + 14*SZADDR
+#define XQBLANKLEN SZFLAG + 9*SZIOINT + 15*SZADDR
+
+LOCAL char *cilist_names[] = {
+ "cilist",
+ "cierr",
+ "ciunit",
+ "ciend",
+ "cifmt",
+ "cirec"
+ };
+LOCAL char *icilist_names[] = {
+ "icilist",
+ "icierr",
+ "iciunit",
+ "iciend",
+ "icifmt",
+ "icirlen",
+ "icirnum"
+ };
+LOCAL char *olist_names[] = {
+ "olist",
+ "oerr",
+ "ounit",
+ "ofnm",
+ "ofnmlen",
+ "osta",
+ "oacc",
+ "ofm",
+ "orl",
+ "oblnk"
+ };
+LOCAL char *cllist_names[] = {
+ "cllist",
+ "cerr",
+ "cunit",
+ "csta"
+ };
+LOCAL char *alist_names[] = {
+ "alist",
+ "aerr",
+ "aunit"
+ };
+LOCAL char *inlist_names[] = {
+ "inlist",
+ "inerr",
+ "inunit",
+ "infile",
+ "infilen",
+ "inex",
+ "inopen",
+ "innum",
+ "innamed",
+ "inname",
+ "innamlen",
+ "inacc",
+ "inacclen",
+ "inseq",
+ "inseqlen",
+ "indir",
+ "indirlen",
+ "infmt",
+ "infmtlen",
+ "inform",
+ "informlen",
+ "inunf",
+ "inunflen",
+ "inrecl",
+ "innrec",
+ "inblank",
+ "inblanklen"
+ };
+
+LOCAL char **io_fields;
+
+#define zork(n,t) n, sizeof(n)/sizeof(char *) - 1, t
+
+LOCAL io_setup io_stuff[] = {
+ zork(cilist_names, TYCILIST), /* external read/write */
+ zork(inlist_names, TYINLIST), /* inquire */
+ zork(olist_names, TYOLIST), /* open */
+ zork(cllist_names, TYCLLIST), /* close */
+ zork(alist_names, TYALIST), /* rewind */
+ zork(alist_names, TYALIST), /* backspace */
+ zork(alist_names, TYALIST), /* endfile */
+ zork(icilist_names,TYICILIST), /* internal read */
+ zork(icilist_names,TYICILIST) /* internal write */
+ };
+
+#undef zork
+
+ int
+#ifdef KR_headers
+fmtstmt(lp)
+ register struct Labelblock *lp;
+#else
+fmtstmt(register struct Labelblock *lp)
+#endif
+{
+ if(lp == NULL)
+ {
+ execerr("unlabeled format statement" , CNULL);
+ return(-1);
+ }
+ if(lp->labtype == LABUNKNOWN)
+ {
+ lp->labtype = LABFORMAT;
+ lp->labelno = newlabel();
+ }
+ else if(lp->labtype != LABFORMAT)
+ {
+ execerr("bad format number", CNULL);
+ return(-1);
+ }
+ return(lp->labelno);
+}
+
+
+ void
+#ifdef KR_headers
+setfmt(lp)
+ struct Labelblock *lp;
+#else
+setfmt(struct Labelblock *lp)
+#endif
+{
+ int n, parity;
+ char *s0;
+ register char *s, *se, *t;
+ register k;
+
+ s0 = s = lexline(&n);
+ se = t = s + n;
+
+ /* warn of trivial errors, e.g. " 11 CONTINUE" (one too few spaces) */
+ /* following FORMAT... */
+
+ if (n <= 0)
+ warn("No (...) after FORMAT");
+ else if (*s != '(')
+ warni("%c rather than ( after FORMAT", *s);
+ else if (se[-1] != ')') {
+ *se = 0;
+ while(--t > s && *t != ')') ;
+ if (t <= s)
+ warn("No ) at end of FORMAT statement");
+ else if (se - t > 30)
+ warn1("Extraneous text at end of FORMAT: ...%s", se-12);
+ else
+ warn1("Extraneous text at end of FORMAT: %s", t+1);
+ t = se;
+ }
+
+ /* fix MYQUOTES (\002's) and \\'s */
+
+ parity = 1;
+ while(s < se)
+ switch(*s++) {
+ case 2:
+ if ((parity ^= 1) && *s == 2) {
+ t -= 2;
+ ++s;
+ }
+ else
+ t += 3;
+ break;
+ case '"':
+ case '\\':
+ t++; break;
+ }
+ s = s0;
+ parity = 1;
+ if (lp) {
+ lp->fmtstring = t = mem((int)(t - s + 1), 0);
+ while(s < se)
+ switch(k = *s++) {
+ case 2:
+ if ((parity ^= 1) && *s == 2)
+ s++;
+ else {
+ t[0] = '\\';
+ t[1] = '0';
+ t[2] = '0';
+ t[3] = '2';
+ t += 4;
+ }
+ break;
+ case '"':
+ case '\\':
+ *t++ = '\\';
+ /* no break */
+ default:
+ *t++ = k;
+ }
+ *t = 0;
+ }
+ flline();
+}
+
+
+ void
+#ifdef KR_headers
+startioctl()
+#else
+startioctl()
+#endif
+{
+ register int i;
+
+ inioctl = YES;
+ nioctl = 0;
+ ioformatted = UNFORMATTED;
+ for(i = 1 ; i<=NIOS ; ++i)
+ V(i) = NULL;
+}
+
+ static long
+newiolabel(Void) {
+ long rv;
+ rv = ++lastiolabno;
+ skiplabel = mklabel(rv);
+ skiplabel->labdefined = 1;
+ return rv;
+ }
+
+ void
+endioctl(Void)
+{
+ int i;
+ expptr p;
+ struct io_setup *ios;
+
+ inioctl = NO;
+
+ /* set up for error recovery */
+
+ ioerrlab = ioendlab = skiplab = jumplab = 0;
+
+ if(p = V(IOSEND))
+ if(ISICON(p))
+ execlab(ioendlab = p->constblock.Const.ci);
+ else
+ err("bad end= clause");
+
+ if(p = V(IOSERR))
+ if(ISICON(p))
+ execlab(ioerrlab = p->constblock.Const.ci);
+ else
+ err("bad err= clause");
+
+ if(IOSTP)
+ if(IOSTP->tag!=TADDR || ! ISINT(IOSTP->addrblock.vtype) )
+ {
+ err("iostat must be an integer variable");
+ frexpr(IOSTP);
+ IOSTP = NULL;
+ }
+
+ if(iostmt == IOREAD)
+ {
+ if(IOSTP)
+ {
+ if(ioerrlab && ioendlab && ioerrlab==ioendlab)
+ jumplab = ioerrlab;
+ else
+ skiplab = jumplab = newiolabel();
+ }
+ else {
+ if(ioerrlab && ioendlab && ioerrlab!=ioendlab)
+ {
+ IOSTP = (expptr) mktmp(TYINT, ENULL);
+ skiplab = jumplab = newiolabel();
+ }
+ else
+ jumplab = (ioerrlab ? ioerrlab : ioendlab);
+ }
+ }
+ else if(iostmt == IOWRITE)
+ {
+ if(IOSTP && !ioerrlab)
+ skiplab = jumplab = newiolabel();
+ else
+ jumplab = ioerrlab;
+ }
+ else
+ jumplab = ioerrlab;
+
+ endbit = IOSTP!=NULL || ioendlab!=0; /* for use in startrw() */
+ errbit = IOSTP!=NULL || ioerrlab!=0;
+ if (jumplab && !IOSTP)
+ IOSTP = (expptr) mktmp(TYINT, ENULL);
+
+ if(iostmt!=IOREAD && iostmt!=IOWRITE)
+ {
+ ios = io_stuff + iostmt;
+ io_fields = ios->fields;
+ ioblkp = io_structs[iostmt];
+ if(ioblkp == NULL)
+ io_structs[iostmt] = ioblkp =
+ autovar(1, ios->type, ENULL, "");
+ ioset(TYIOINT, XERR, ICON(errbit));
+ }
+
+ switch(iostmt)
+ {
+ case IOOPEN:
+ dofopen();
+ break;
+
+ case IOCLOSE:
+ dofclose();
+ break;
+
+ case IOINQUIRE:
+ dofinquire();
+ break;
+
+ case IOBACKSPACE:
+ dofmove("f_back");
+ break;
+
+ case IOREWIND:
+ dofmove("f_rew");
+ break;
+
+ case IOENDFILE:
+ dofmove("f_end");
+ break;
+
+ case IOREAD:
+ case IOWRITE:
+ startrw();
+ break;
+
+ default:
+ fatali("impossible iostmt %d", iostmt);
+ }
+ for(i = 1 ; i<=NIOS ; ++i)
+ if(i!=IOSIOSTAT && V(i)!=NULL)
+ frexpr(V(i));
+}
+
+
+ int
+iocname(Void)
+{
+ register int i;
+ int found, mask;
+
+ found = 0;
+ mask = M(iostmt);
+ for(i = 1 ; i <= NIOS ; ++i)
+ if(!strcmp(ioc[i].iocname, token))
+ if(ioc[i].iotype & mask)
+ return(i);
+ else {
+ found = i;
+ break;
+ }
+ if(found) {
+ if (iostmt == IOOPEN && !strcmp(ioc[i].iocname, "name")) {
+ NOEXT("open with \"name=\" treated as \"file=\"");
+ for(i = 1; strcmp(ioc[i].iocname, "file"); i++);
+ return i;
+ }
+ errstr("invalid control %s for statement", ioc[found].iocname);
+ }
+ else
+ errstr("unknown iocontrol %s", token);
+ return(IOSBAD);
+}
+
+
+ void
+#ifdef KR_headers
+ioclause(n, p)
+ register int n;
+ register expptr p;
+#else
+ioclause(register int n, register expptr p)
+#endif
+{
+ struct Ioclist *iocp;
+
+ ++nioctl;
+ if(n == IOSBAD)
+ return;
+ if(n == IOSPOSITIONAL)
+ {
+ n = nioctl;
+ if (n == IOSFMT) {
+ if (iostmt == IOOPEN) {
+ n = IOSFILE;
+ NOEXT("file= specifier omitted from open");
+ }
+ else if (iostmt < IOREAD)
+ goto illegal;
+ }
+ else if(n > IOSFMT)
+ {
+ illegal:
+ err("illegal positional iocontrol");
+ return;
+ }
+ }
+ else if (n == IOSNML)
+ n = IOSFMT;
+
+ if(p == NULL)
+ {
+ if(n == IOSUNIT)
+ p = (expptr) (iostmt==IOREAD ? IOSTDIN : IOSTDOUT);
+ else if(n != IOSFMT)
+ {
+ err("illegal * iocontrol");
+ return;
+ }
+ }
+ if(n == IOSFMT)
+ ioformatted = (p==NULL ? LISTDIRECTED : FORMATTED);
+
+ iocp = & ioc[n];
+ if(iocp->iocval == NULL)
+ {
+ if(n!=IOSFMT && ( n!=IOSUNIT || (p && p->headblock.vtype!=TYCHAR) ) )
+ p = fixtype(p);
+ else if (p && p->tag == TPRIM
+ && p->primblock.namep->vclass == CLUNKNOWN) {
+ /* kludge made necessary by attempt to infer types
+ * for untyped external parameters: given an error
+ * in calling sequences, an integer argument might
+ * tentatively be assumed TYCHAR; this would otherwise
+ * be corrected too late in startrw after startrw
+ * had decided this to be an internal file.
+ */
+ vardcl(p->primblock.namep);
+ p->primblock.vtype = p->primblock.namep->vtype;
+ }
+ iocp->iocval = p;
+ }
+ else
+ errstr("iocontrol %s repeated", iocp->iocname);
+}
+
+/* io list item */
+
+ void
+#ifdef KR_headers
+doio(list)
+ chainp list;
+#else
+doio(chainp list)
+#endif
+{
+ if(ioformatted == NAMEDIRECTED)
+ {
+ if(list)
+ err("no I/O list allowed in NAMELIST read/write");
+ }
+ else
+ {
+ doiolist(list);
+ ioroutine[0] = 'e';
+ if (skiplab)
+ jumplab = 0;
+ putiocall( call0(TYINT, ioroutine) );
+ }
+}
+
+
+
+
+
+ LOCAL void
+#ifdef KR_headers
+doiolist(p0)
+ chainp p0;
+#else
+doiolist(chainp p0)
+#endif
+{
+ chainp p;
+ register tagptr q;
+ register expptr qe;
+ register Namep qn;
+ Addrp tp;
+ int range;
+ extern char *ohalign;
+
+ for (p = p0 ; p ; p = p->nextp)
+ {
+ q = (tagptr)p->datap;
+ if(q->tag == TIMPLDO)
+ {
+ exdo(range=newlabel(), (Namep)0,
+ q->impldoblock.impdospec);
+ doiolist(q->impldoblock.datalist);
+ enddo(range);
+ free( (charptr) q);
+ }
+ else {
+ if(q->tag==TPRIM && q->primblock.argsp==NULL
+ && q->primblock.namep->vdim!=NULL)
+ {
+ vardcl(qn = q->primblock.namep);
+ if(qn->vdim->nelt) {
+ putio( fixtype(cpexpr(qn->vdim->nelt)),
+ (expptr)mkscalar(qn) );
+ qn->vlastdim = 0;
+ }
+ else
+ err("attempt to i/o array of unknown size");
+ }
+ else if(q->tag==TPRIM && q->primblock.argsp==NULL &&
+ (qe = (expptr) memversion(q->primblock.namep)) )
+ putio(ICON(1),qe);
+ else if (ISCONST(q) && q->constblock.vtype == TYCHAR) {
+ halign = 0;
+ putio(ICON(1), qe = fixtype(cpexpr(q)));
+ halign = ohalign;
+ }
+ else if(((qe = fixtype(cpexpr(q)))->tag==TADDR &&
+ (qe->addrblock.uname_tag != UNAM_CONST ||
+ !ISCOMPLEX(qe -> addrblock.vtype))) ||
+ (qe -> tag == TCONST && !ISCOMPLEX(qe ->
+ headblock.vtype))) {
+ if (qe -> tag == TCONST)
+ qe = (expptr) putconst((Constp)qe);
+ putio(ICON(1), qe);
+ }
+ else if(qe->headblock.vtype != TYERROR)
+ {
+ if(iostmt == IOWRITE)
+ {
+ expptr qvl;
+ qvl = NULL;
+ if( ISCHAR(qe) )
+ {
+ qvl = (expptr)
+ cpexpr(qe->headblock.vleng);
+ tp = mktmp(qe->headblock.vtype,
+ ICON(lencat(qe)));
+ }
+ else
+ tp = mktmp(qe->headblock.vtype,
+ qe->headblock.vleng);
+ puteq( cpexpr((expptr)tp), qe);
+ if(qvl) /* put right length on block */
+ {
+ frexpr(tp->vleng);
+ tp->vleng = qvl;
+ }
+ putio(ICON(1), (expptr)tp);
+ }
+ else
+ err("non-left side in READ list");
+ }
+ frexpr(q);
+ }
+ }
+ frchain( &p0 );
+}
+
+ int iocalladdr = TYADDR; /* for fixing TYADDR in saveargtypes */
+ int typeconv[TYERROR+1] = {
+#ifdef TYQUAD
+ 0, 1, 11, 2, 3, 14, 4, 5, 6, 7, 12, 13, 8, 9, 10, 15
+#else
+ 0, 1, 11, 2, 3, 4, 5, 6, 7, 12, 13, 8, 9, 10, 14
+#endif
+ };
+
+ LOCAL void
+#ifdef KR_headers
+putio(nelt, addr)
+ expptr nelt;
+ register expptr addr;
+#else
+putio(expptr nelt, register expptr addr)
+#endif
+{
+ int type;
+ register expptr q;
+ register Addrp c = 0;
+
+ type = addr->headblock.vtype;
+ if(ioformatted!=LISTDIRECTED && ISCOMPLEX(type) )
+ {
+ nelt = mkexpr(OPSTAR, ICON(2), nelt);
+ type -= (TYCOMPLEX-TYREAL);
+ }
+
+ /* pass a length with every item. for noncharacter data, fake one */
+ if(type != TYCHAR)
+ {
+
+ if( ISCONST(addr) )
+ addr = (expptr) putconst((Constp)addr);
+ c = ALLOC(Addrblock);
+ c->tag = TADDR;
+ c->vtype = TYLENG;
+ c->vstg = STGAUTO;
+ c->ntempelt = 1;
+ c->isarray = 1;
+ c->memoffset = ICON(0);
+ c->uname_tag = UNAM_IDENT;
+ c->charleng = 1;
+ sprintf(c->user.ident, "(ftnlen)sizeof(%s)", typename[type]);
+ addr = mkexpr(OPCHARCAST, addr, ENULL);
+ }
+
+ nelt = fixtype( mkconv(tyioint,nelt) );
+ if(ioformatted == LISTDIRECTED) {
+ expptr mc = mkconv(tyioint, ICON(typeconv[type]));
+ q = c ? call4(TYINT, "do_lio", mc, nelt, addr, (expptr)c)
+ : call3(TYINT, "do_lio", mc, nelt, addr);
+ }
+ else {
+ char *s = ioformatted==FORMATTED ? "do_fio" : "do_uio";
+ q = c ? call3(TYINT, s, nelt, addr, (expptr)c)
+ : call2(TYINT, s, nelt, addr);
+ }
+ iocalladdr = TYCHAR;
+ putiocall(q);
+ iocalladdr = TYADDR;
+}
+
+
+
+ void
+endio(Void)
+{
+ if(skiplab)
+ {
+ if (ioformatted != NAMEDIRECTED)
+ p1_label((long)(skiplabel - labeltab));
+ if(ioendlab) {
+ exif( mkexpr(OPLT, cpexpr(IOSTP), ICON(0)));
+ exgoto(execlab(ioendlab));
+ exendif();
+ }
+ if(ioerrlab) {
+ exif( mkexpr(iostmt==IOREAD||iostmt==IOWRITE
+ ? OPGT : OPNE,
+ cpexpr(IOSTP), ICON(0)));
+ exgoto(execlab(ioerrlab));
+ exendif();
+ }
+ }
+
+ if(IOSTP)
+ frexpr(IOSTP);
+}
+
+
+
+ LOCAL void
+#ifdef KR_headers
+putiocall(q)
+ register expptr q;
+#else
+putiocall(register expptr q)
+#endif
+{
+ int tyintsave;
+
+ tyintsave = tyint;
+ tyint = tyioint; /* for -I2 and -i2 */
+
+ if(IOSTP)
+ {
+ q->headblock.vtype = TYINT;
+ q = fixexpr((Exprp)mkexpr(OPASSIGN, cpexpr(IOSTP), q));
+ }
+ putexpr(q);
+ if(jumplab) {
+ exif(mkexpr(OPNE, cpexpr(IOSTP), ICON(0)));
+ exgoto(execlab(jumplab));
+ exendif();
+ }
+ tyint = tyintsave;
+}
+
+ void
+#ifdef KR_headers
+fmtname(np, q)
+ Namep np;
+ register Addrp q;
+#else
+fmtname(Namep np, register Addrp q)
+#endif
+{
+ register int k;
+ register char *s, *t;
+ extern chainp assigned_fmts;
+
+ if (!np->vfmt_asg) {
+ np->vfmt_asg = 1;
+ assigned_fmts = mkchain((char *)np, assigned_fmts);
+ }
+ k = strlen(s = np->fvarname);
+ if (k < IDENT_LEN - 4) {
+ q->uname_tag = UNAM_IDENT;
+ t = q->user.ident;
+ }
+ else {
+ q->uname_tag = UNAM_CHARP;
+ q->user.Charp = t = mem(k + 5,0);
+ }
+ sprintf(t, "%s_fmt", s);
+ }
+
+ LOCAL Addrp
+#ifdef KR_headers
+asg_addr(p)
+ union Expression *p;
+#else
+asg_addr(union Expression *p)
+#endif
+{
+ register Addrp q;
+
+ if (p->tag != TPRIM)
+ badtag("asg_addr", p->tag);
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ q->vtype = TYCHAR;
+ q->vstg = STGAUTO;
+ q->ntempelt = 1;
+ q->isarray = 0;
+ q->memoffset = ICON(0);
+ fmtname(p->primblock.namep, q);
+ return q;
+ }
+
+ void
+startrw(Void)
+{
+ register expptr p;
+ register Namep np;
+ register Addrp unitp, fmtp, recp;
+ register expptr nump;
+ int iostmt1;
+ flag intfile, sequential, ok, varfmt;
+ struct io_setup *ios;
+
+ /* First look at all the parameters and determine what is to be done */
+
+ ok = YES;
+ statstruct = YES;
+
+ intfile = NO;
+ if(p = V(IOSUNIT))
+ {
+ if( ISINT(p->headblock.vtype) ) {
+ int_unit:
+ unitp = (Addrp) cpexpr(p);
+ }
+ else if(p->headblock.vtype == TYCHAR)
+ {
+ if (nioctl == 1 && iostmt == IOREAD) {
+ /* kludge to recognize READ(format expr) */
+ V(IOSFMT) = p;
+ V(IOSUNIT) = p = (expptr) IOSTDIN;
+ ioformatted = FORMATTED;
+ goto int_unit;
+ }
+ intfile = YES;
+ if(p->tag==TPRIM && p->primblock.argsp==NULL &&
+ (np = p->primblock.namep)->vdim!=NULL)
+ {
+ vardcl(np);
+ if(nump = np->vdim->nelt)
+ {
+ nump = fixtype(cpexpr(nump));
+ if( ! ISCONST(nump) ) {
+ statstruct = NO;
+ np->vlastdim = 0;
+ }
+ }
+ else
+ {
+ err("attempt to use internal unit array of unknown size");
+ ok = NO;
+ nump = ICON(1);
+ }
+ unitp = mkscalar(np);
+ }
+ else {
+ nump = ICON(1);
+ unitp = (Addrp /*pjw */) fixtype(cpexpr(p));
+ }
+ if(! isstatic((expptr)unitp) )
+ statstruct = NO;
+ }
+ else {
+ err("unit specifier not of type integer or character");
+ ok = NO;
+ }
+ }
+ else
+ {
+ err("bad unit specifier");
+ ok = NO;
+ }
+
+ sequential = YES;
+ if(p = V(IOSREC))
+ if( ISINT(p->headblock.vtype) )
+ {
+ recp = (Addrp) cpexpr(p);
+ sequential = NO;
+ }
+ else {
+ err("bad REC= clause");
+ ok = NO;
+ }
+ else
+ recp = NULL;
+
+
+ varfmt = YES;
+ fmtp = NULL;
+ if(p = V(IOSFMT))
+ {
+ if(p->tag==TPRIM && p->primblock.argsp==NULL)
+ {
+ np = p->primblock.namep;
+ if(np->vclass == CLNAMELIST)
+ {
+ ioformatted = NAMEDIRECTED;
+ fmtp = (Addrp) fixtype(p);
+ V(IOSFMT) = (expptr)fmtp;
+ if (skiplab)
+ jumplab = 0;
+ goto endfmt;
+ }
+ vardcl(np);
+ if(np->vdim)
+ {
+ if( ! ONEOF(np->vstg, MSKSTATIC) )
+ statstruct = NO;
+ fmtp = mkscalar(np);
+ goto endfmt;
+ }
+ if( ISINT(np->vtype) ) /* ASSIGNed label */
+ {
+ statstruct = NO;
+ varfmt = YES;
+ fmtp = asg_addr(p);
+ goto endfmt;
+ }
+ }
+ p = V(IOSFMT) = fixtype(p);
+ if(p->headblock.vtype == TYCHAR
+ /* Since we allow write(6,n) */
+ /* we may as well allow write(6,n(2)) */
+ || p->tag == TADDR && ISINT(p->addrblock.vtype))
+ {
+ if( ! isstatic(p) )
+ statstruct = NO;
+ fmtp = (Addrp) cpexpr(p);
+ }
+ else if( ISICON(p) )
+ {
+ struct Labelblock *lp;
+ lp = mklabel(p->constblock.Const.ci);
+ if (fmtstmt(lp) > 0)
+ {
+ fmtp = (Addrp)mkaddcon(lp->stateno);
+ /* lp->stateno for names fmt_nnn */
+ lp->fmtlabused = 1;
+ varfmt = NO;
+ }
+ else
+ ioformatted = UNFORMATTED;
+ }
+ else {
+ err("bad format descriptor");
+ ioformatted = UNFORMATTED;
+ ok = NO;
+ }
+ }
+ else
+ fmtp = NULL;
+
+endfmt:
+ if(intfile) {
+ if (ioformatted==UNFORMATTED) {
+ err("unformatted internal I/O not allowed");
+ ok = NO;
+ }
+ if (recp) {
+ err("direct internal I/O not allowed");
+ ok = NO;
+ }
+ }
+ if(!sequential && ioformatted==LISTDIRECTED)
+ {
+ err("direct list-directed I/O not allowed");
+ ok = NO;
+ }
+ if(!sequential && ioformatted==NAMEDIRECTED)
+ {
+ err("direct namelist I/O not allowed");
+ ok = NO;
+ }
+
+ if( ! ok ) {
+ statstruct = NO;
+ return;
+ }
+
+ /*
+ Now put out the I/O structure, statically if all the clauses
+ are constants, dynamically otherwise
+*/
+
+ if (intfile) {
+ ios = io_stuff + iostmt;
+ iostmt1 = IOREAD;
+ }
+ else {
+ ios = io_stuff;
+ iostmt1 = 0;
+ }
+ io_fields = ios->fields;
+ if(statstruct)
+ {
+ ioblkp = ALLOC(Addrblock);
+ ioblkp->tag = TADDR;
+ ioblkp->vtype = ios->type;
+ ioblkp->vclass = CLVAR;
+ ioblkp->vstg = STGINIT;
+ ioblkp->memno = ++lastvarno;
+ ioblkp->memoffset = ICON(0);
+ ioblkp -> uname_tag = UNAM_IDENT;
+ new_iob_data(ios,
+ temp_name("io_", lastvarno, ioblkp->user.ident)); }
+ else if(!(ioblkp = io_structs[iostmt1]))
+ io_structs[iostmt1] = ioblkp =
+ autovar(1, ios->type, ENULL, "");
+
+ ioset(TYIOINT, XERR, ICON(errbit));
+ if(iostmt == IOREAD)
+ ioset(TYIOINT, (intfile ? XIEND : XEND), ICON(endbit) );
+
+ if(intfile)
+ {
+ ioset(TYIOINT, XIRNUM, nump);
+ ioset(TYIOINT, XIRLEN, cpexpr(unitp->vleng) );
+ ioseta(XIUNIT, unitp);
+ }
+ else
+ ioset(TYIOINT, XUNIT, (expptr) unitp);
+
+ if(recp)
+ ioset(TYIOINT, /* intfile ? XIREC : */ XREC, (expptr) recp);
+
+ if(varfmt)
+ ioseta( intfile ? XIFMT : XFMT , fmtp);
+ else
+ ioset(TYADDR, intfile ? XIFMT : XFMT, (expptr) fmtp);
+
+ ioroutine[0] = 's';
+ ioroutine[1] = '_';
+ ioroutine[2] = iostmt==IOREAD ? 'r' : 'w';
+ ioroutine[3] = "ds"[sequential];
+ ioroutine[4] = "ufln"[ioformatted];
+ ioroutine[5] = "ei"[intfile];
+ ioroutine[6] = '\0';
+
+ putiocall( call1(TYINT, ioroutine, cpexpr((expptr)ioblkp) ));
+
+ if(statstruct)
+ {
+ frexpr((expptr)ioblkp);
+ statstruct = NO;
+ ioblkp = 0; /* unnecessary */
+ }
+}
+
+
+
+ LOCAL void
+dofopen(Void)
+{
+ register expptr p;
+
+ if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) )
+ ioset(TYIOINT, XUNIT, cpexpr(p) );
+ else
+ err("bad unit in open");
+ if( (p = V(IOSFILE)) )
+ if(p->headblock.vtype == TYCHAR)
+ ioset(TYIOINT, XFNAMELEN, cpexpr(p->headblock.vleng) );
+ else
+ err("bad file in open");
+
+ iosetc(XFNAME, p);
+
+ if(p = V(IOSRECL))
+ if( ISINT(p->headblock.vtype) )
+ ioset(TYIOINT, XRECLEN, cpexpr(p) );
+ else
+ err("bad recl");
+ else
+ ioset(TYIOINT, XRECLEN, ICON(0) );
+
+ iosetc(XSTATUS, V(IOSSTATUS));
+ iosetc(XACCESS, V(IOSACCESS));
+ iosetc(XFORMATTED, V(IOSFORM));
+ iosetc(XBLANK, V(IOSBLANK));
+
+ putiocall( call1(TYINT, "f_open", cpexpr((expptr)ioblkp) ));
+}
+
+
+ LOCAL void
+dofclose(Void)
+{
+ register expptr p;
+
+ if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) )
+ {
+ ioset(TYIOINT, XUNIT, cpexpr(p) );
+ iosetc(XCLSTATUS, V(IOSSTATUS));
+ putiocall( call1(TYINT, "f_clos", cpexpr((expptr)ioblkp)) );
+ }
+ else
+ err("bad unit in close statement");
+}
+
+
+ LOCAL void
+dofinquire(Void)
+{
+ register expptr p;
+ if(p = V(IOSUNIT))
+ {
+ if( V(IOSFILE) )
+ err("inquire by unit or by file, not both");
+ ioset(TYIOINT, XUNIT, cpexpr(p) );
+ }
+ else if( ! V(IOSFILE) )
+ err("must inquire by unit or by file");
+ iosetlc(IOSFILE, XFILE, XFILELEN);
+ iosetip(IOSEXISTS, XEXISTS);
+ iosetip(IOSOPENED, XOPEN);
+ iosetip(IOSNUMBER, XNUMBER);
+ iosetip(IOSNAMED, XNAMED);
+ iosetlc(IOSNAME, XNAME, XNAMELEN);
+ iosetlc(IOSACCESS, XQACCESS, XQACCLEN);
+ iosetlc(IOSSEQUENTIAL, XSEQ, XSEQLEN);
+ iosetlc(IOSDIRECT, XDIRECT, XDIRLEN);
+ iosetlc(IOSFORM, XFORM, XFORMLEN);
+ iosetlc(IOSFORMATTED, XFMTED, XFMTEDLEN);
+ iosetlc(IOSUNFORMATTED, XUNFMT, XUNFMTLEN);
+ iosetip(IOSRECL, XQRECL);
+ iosetip(IOSNEXTREC, XNEXTREC);
+ iosetlc(IOSBLANK, XQBLANK, XQBLANKLEN);
+
+ putiocall( call1(TYINT, "f_inqu", cpexpr((expptr)ioblkp) ));
+}
+
+
+
+ LOCAL void
+#ifdef KR_headers
+dofmove(subname)
+ char *subname;
+#else
+dofmove(char *subname)
+#endif
+{
+ register expptr p;
+
+ if( (p = V(IOSUNIT)) && ISINT(p->headblock.vtype) )
+ {
+ ioset(TYIOINT, XUNIT, cpexpr(p) );
+ putiocall( call1(TYINT, subname, cpexpr((expptr)ioblkp) ));
+ }
+ else
+ err("bad unit in I/O motion statement");
+}
+
+static int ioset_assign = OPASSIGN;
+
+ LOCAL void
+#ifdef KR_headers
+ioset(type, offset, p)
+ int type;
+ int offset;
+ register expptr p;
+#else
+ioset(int type, int offset, register expptr p)
+#endif
+{
+ offset /= SZLONG;
+ if(statstruct && ISCONST(p)) {
+ register char *s;
+ switch(type) {
+ case TYADDR: /* stmt label */
+ s = "fmt_";
+ break;
+ case TYIOINT:
+ s = "";
+ break;
+ default:
+ badtype("ioset", type);
+ }
+ iob_list->fields[offset] =
+ string_num(s, p->constblock.Const.ci);
+ frexpr(p);
+ }
+ else {
+ register Addrp q;
+
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ q->vtype = type;
+ q->vstg = STGAUTO;
+ q->ntempelt = 1;
+ q->isarray = 0;
+ q->memoffset = ICON(0);
+ q->uname_tag = UNAM_IDENT;
+ sprintf(q->user.ident, "%s.%s",
+ statstruct ? iob_list->name : ioblkp->user.ident,
+ io_fields[offset + 1]);
+ if (type == TYADDR && p->tag == TCONST
+ && p->constblock.vtype == TYADDR) {
+ /* kludge */
+ register Addrp p1;
+ p1 = ALLOC(Addrblock);
+ p1->tag = TADDR;
+ p1->vtype = type;
+ p1->vstg = STGAUTO; /* wrong, but who cares? */
+ p1->ntempelt = 1;
+ p1->isarray = 0;
+ p1->memoffset = ICON(0);
+ p1->uname_tag = UNAM_IDENT;
+ sprintf(p1->user.ident, "fmt_%ld",
+ p->constblock.Const.ci);
+ frexpr(p);
+ p = (expptr)p1;
+ }
+ if (type == TYADDR && p->headblock.vtype == TYCHAR)
+ q->vtype = TYCHAR;
+ putexpr(mkexpr(ioset_assign, (expptr)q, p));
+ }
+}
+
+
+
+
+ LOCAL void
+#ifdef KR_headers
+iosetc(offset, p)
+ int offset;
+ register expptr p;
+#else
+iosetc(int offset, register expptr p)
+#endif
+{
+ if(p == NULL)
+ ioset(TYADDR, offset, ICON(0) );
+ else if(p->headblock.vtype == TYCHAR) {
+ p = putx(fixtype((expptr)putchop(cpexpr(p))));
+ ioset(TYADDR, offset, addrof(p));
+ }
+ else
+ err("non-character control clause");
+}
+
+
+
+ LOCAL void
+#ifdef KR_headers
+ioseta(offset, p)
+ int offset;
+ register Addrp p;
+#else
+ioseta(int offset, register Addrp p)
+#endif
+{
+ char *s, *s1;
+ static char who[] = "ioseta";
+ expptr e, mo;
+ Namep np;
+ ftnint ci;
+ int k;
+ char buf[24], buf1[24];
+ Extsym *comm;
+ extern int usedefsforcommon;
+
+ if(statstruct)
+ {
+ if (!p)
+ return;
+ if (p->tag != TADDR)
+ badtag(who, p->tag);
+ offset /= SZLONG;
+ switch(p->uname_tag) {
+ case UNAM_NAME:
+ mo = p->memoffset;
+ if (mo->tag != TCONST)
+ badtag("ioseta/memoffset", mo->tag);
+ np = p->user.name;
+ np->visused = 1;
+ ci = mo->constblock.Const.ci - np->voffset;
+ if (np->vstg == STGCOMMON
+ && !np->vcommequiv
+ && !usedefsforcommon) {
+ comm = &extsymtab[np->vardesc.varno];
+ sprintf(buf, "%d.", comm->curno);
+ k = strlen(buf) + strlen(comm->cextname)
+ + strlen(np->cvarname);
+ if (ci) {
+ sprintf(buf1, "+%ld", ci);
+ k += strlen(buf1);
+ }
+ else
+ buf1[0] = 0;
+ s = mem(k + 1, 0);
+ sprintf(s, "%s%s%s%s", comm->cextname, buf,
+ np->cvarname, buf1);
+ }
+ else if (ci) {
+ sprintf(buf,"%ld", ci);
+ s1 = p->user.name->cvarname;
+ k = strlen(buf) + strlen(s1);
+ sprintf(s = mem(k+2,0), "%s+%s", s1, buf);
+ }
+ else
+ s = cpstring(np->cvarname);
+ break;
+ case UNAM_CONST:
+ s = tostring(p->user.Const.ccp1.ccp0,
+ (int)p->vleng->constblock.Const.ci);
+ break;
+ default:
+ badthing("uname_tag", who, p->uname_tag);
+ }
+ /* kludge for Hollerith */
+ if (p->vtype != TYCHAR) {
+ s1 = mem(strlen(s)+10,0);
+ sprintf(s1, "(char *)%s%s", p->isarray ? "" : "&", s);
+ s = s1;
+ }
+ iob_list->fields[offset] = s;
+ }
+ else {
+ if (!p)
+ e = ICON(0);
+ else if (p->vtype != TYCHAR) {
+ NOEXT("non-character variable as format or internal unit");
+ e = mkexpr(OPCHARCAST, (expptr)p, ENULL);
+ }
+ else
+ e = addrof((expptr)p);
+ ioset(TYADDR, offset, e);
+ }
+}
+
+
+
+
+ LOCAL void
+#ifdef KR_headers
+iosetip(i, offset)
+ int i;
+ int offset;
+#else
+iosetip(int i, int offset)
+#endif
+{
+ register expptr p;
+
+ if(p = V(i))
+ if(p->tag==TADDR &&
+ ONEOF(p->addrblock.vtype, inqmask) ) {
+ ioset_assign = OPASSIGNI;
+ ioset(TYADDR, offset, addrof(cpexpr(p)) );
+ ioset_assign = OPASSIGN;
+ }
+ else
+ errstr("impossible inquire parameter %s", ioc[i].iocname);
+ else
+ ioset(TYADDR, offset, ICON(0) );
+}
+
+
+
+ LOCAL void
+#ifdef KR_headers
+iosetlc(i, offp, offl)
+ int i;
+ int offp;
+ int offl;
+#else
+iosetlc(int i, int offp, int offl)
+#endif
+{
+ register expptr p;
+ if( (p = V(i)) && p->headblock.vtype==TYCHAR)
+ ioset(TYIOINT, offl, cpexpr(p->headblock.vleng) );
+ iosetc(offp, p);
+}
diff --git a/usr.bin/f2c/iob.h b/usr.bin/f2c/iob.h
new file mode 100644
index 0000000..065d813
--- /dev/null
+++ b/usr.bin/f2c/iob.h
@@ -0,0 +1,26 @@
+struct iob_data {
+ struct iob_data *next;
+ char *type;
+ char *name;
+ char *fields[1];
+ };
+struct io_setup {
+ char **fields;
+ int nelt, type;
+ };
+
+struct defines {
+ struct defines *next;
+ char defname[1];
+ };
+
+typedef struct iob_data iob_data;
+typedef struct io_setup io_setup;
+typedef struct defines defines;
+
+extern iob_data *iob_list;
+extern struct Addrblock *io_structs[9];
+void def_start Argdcl((FILEP, char*, char*, char*));
+void new_iob_data Argdcl((io_setup*, char*));
+void other_undefs Argdcl((FILEP));
+char* tostring Argdcl((char*, int));
diff --git a/usr.bin/f2c/lex.c b/usr.bin/f2c/lex.c
new file mode 100644
index 0000000..0650e50
--- /dev/null
+++ b/usr.bin/f2c/lex.c
@@ -0,0 +1,1676 @@
+/****************************************************************
+Copyright 1990, 1992 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "tokdefs.h"
+#include "p1defs.h"
+
+#ifdef NO_EOF_CHAR_CHECK
+#undef EOF_CHAR
+#else
+#ifndef EOF_CHAR
+#define EOF_CHAR 26 /* ASCII control-Z */
+#endif
+#endif
+
+#define BLANK ' '
+#define MYQUOTE (2)
+#define SEOF 0
+
+/* card types */
+
+#define STEOF 1
+#define STINITIAL 2
+#define STCONTINUE 3
+
+/* lex states */
+
+#define NEWSTMT 1
+#define FIRSTTOKEN 2
+#define OTHERTOKEN 3
+#define RETEOS 4
+
+
+LOCAL int stkey; /* Type of the current statement (DO, END, IF, etc) */
+static int needwkey;
+ftnint yystno;
+flag intonly;
+extern int new_dcl;
+LOCAL long int stno;
+LOCAL long int nxtstno; /* Statement label */
+LOCAL int parlev; /* Parentheses level */
+LOCAL int parseen;
+LOCAL int expcom;
+LOCAL int expeql;
+LOCAL char *nextch;
+LOCAL char *lastch;
+LOCAL char *nextcd = NULL;
+LOCAL char *endcd;
+LOCAL long prevlin;
+LOCAL long thislin;
+LOCAL int code; /* Card type; INITIAL, CONTINUE or EOF */
+LOCAL int lexstate = NEWSTMT;
+LOCAL char *sbuf; /* Main buffer for Fortran source input. */
+LOCAL char *send; /* Was = sbuf+20*66 with sbuf[1390]. */
+LOCAL int maxcont;
+LOCAL int nincl = 0; /* Current number of include files */
+LOCAL long firstline;
+LOCAL char *laststb, *stb0;
+extern int addftnsrc;
+static char **linestart;
+LOCAL int ncont;
+LOCAL char comstart[Table_size];
+#define USC (unsigned char *)
+
+static char anum_buf[Table_size];
+#define isalnum_(x) anum_buf[x]
+#define isalpha_(x) (anum_buf[x] == 1)
+
+#define COMMENT_BUF_STORE 4088
+
+typedef struct comment_buf {
+ struct comment_buf *next;
+ char *last;
+ char buf[COMMENT_BUF_STORE];
+ } comment_buf;
+static comment_buf *cbfirst, *cbcur;
+static char *cbinit, *cbnext, *cblast;
+static void flush_comments Argdcl((void));
+extern flag use_bs;
+static char *lastfile = "??", *lastfile0 = "?";
+static char fbuf[P1_FILENAME_MAX];
+static long lastline;
+static void putlineno(Void);
+
+
+/* Comment buffering data
+
+ Comments are kept in a list until the statement before them has
+ been parsed. This list is implemented with the above comment_buf
+ structure and the pointers cbnext and cblast.
+
+ The comments are stored with terminating NULL, and no other
+ intervening space. The last few bytes of each block are likely to
+ remain unused.
+*/
+
+/* struct Inclfile holds the state information for each include file */
+struct Inclfile
+{
+ struct Inclfile *inclnext;
+ FILEP inclfp;
+ char *inclname;
+ int incllno;
+ char *incllinp;
+ int incllen;
+ int inclcode;
+ ftnint inclstno;
+};
+
+LOCAL struct Inclfile *inclp = NULL;
+struct Keylist {
+ char *keyname;
+ int keyval;
+ char notinf66;
+};
+struct Punctlist {
+ char punchar;
+ int punval;
+};
+struct Fmtlist {
+ char fmtchar;
+ int fmtval;
+};
+struct Dotlist {
+ char *dotname;
+ int dotval;
+ };
+LOCAL struct Keylist *keystart[26], *keyend[26];
+
+/* KEYWORD AND SPECIAL CHARACTER TABLES
+*/
+
+static struct Punctlist puncts[ ] =
+{
+ '(', SLPAR,
+ ')', SRPAR,
+ '=', SEQUALS,
+ ',', SCOMMA,
+ '+', SPLUS,
+ '-', SMINUS,
+ '*', SSTAR,
+ '/', SSLASH,
+ '$', SCURRENCY,
+ ':', SCOLON,
+ '<', SLT,
+ '>', SGT,
+ 0, 0 };
+
+LOCAL struct Dotlist dots[ ] =
+{
+ "and.", SAND,
+ "or.", SOR,
+ "not.", SNOT,
+ "true.", STRUE,
+ "false.", SFALSE,
+ "eq.", SEQ,
+ "ne.", SNE,
+ "lt.", SLT,
+ "le.", SLE,
+ "gt.", SGT,
+ "ge.", SGE,
+ "neqv.", SNEQV,
+ "eqv.", SEQV,
+ 0, 0 };
+
+LOCAL struct Keylist keys[ ] =
+{
+ { "assign", SASSIGN },
+ { "automatic", SAUTOMATIC, YES },
+ { "backspace", SBACKSPACE },
+ { "blockdata", SBLOCK },
+ { "byte", SBYTE },
+ { "call", SCALL },
+ { "character", SCHARACTER, YES },
+ { "close", SCLOSE, YES },
+ { "common", SCOMMON },
+ { "complex", SCOMPLEX },
+ { "continue", SCONTINUE },
+ { "data", SDATA },
+ { "dimension", SDIMENSION },
+ { "doubleprecision", SDOUBLE },
+ { "doublecomplex", SDCOMPLEX, YES },
+ { "elseif", SELSEIF, YES },
+ { "else", SELSE, YES },
+ { "endfile", SENDFILE },
+ { "endif", SENDIF, YES },
+ { "enddo", SENDDO, YES },
+ { "end", SEND },
+ { "entry", SENTRY, YES },
+ { "equivalence", SEQUIV },
+ { "external", SEXTERNAL },
+ { "format", SFORMAT },
+ { "function", SFUNCTION },
+ { "goto", SGOTO },
+ { "implicit", SIMPLICIT, YES },
+ { "include", SINCLUDE, YES },
+ { "inquire", SINQUIRE, YES },
+ { "intrinsic", SINTRINSIC, YES },
+ { "integer", SINTEGER },
+ { "logical", SLOGICAL },
+ { "namelist", SNAMELIST, YES },
+ { "none", SUNDEFINED, YES },
+ { "open", SOPEN, YES },
+ { "parameter", SPARAM, YES },
+ { "pause", SPAUSE },
+ { "print", SPRINT },
+ { "program", SPROGRAM, YES },
+ { "punch", SPUNCH, YES },
+ { "read", SREAD },
+ { "real", SREAL },
+ { "return", SRETURN },
+ { "rewind", SREWIND },
+ { "save", SSAVE, YES },
+ { "static", SSTATIC, YES },
+ { "stop", SSTOP },
+ { "subroutine", SSUBROUTINE },
+ { "then", STHEN, YES },
+ { "undefined", SUNDEFINED, YES },
+ { "while", SWHILE, YES },
+ { "write", SWRITE },
+ { 0, 0 }
+};
+
+static void analyz Argdcl((void));
+static void crunch Argdcl((void));
+static int getcd Argdcl((char*, int));
+static int getcds Argdcl((void));
+static int getkwd Argdcl((void));
+static int gettok Argdcl((void));
+static void store_comment Argdcl((char*));
+LOCAL char *stbuf[3];
+
+ int
+#ifdef KR_headers
+inilex(name)
+ char *name;
+#else
+inilex(char *name)
+#endif
+{
+ stbuf[0] = Alloc(3*P1_STMTBUFSIZE);
+ stbuf[1] = stbuf[0] + P1_STMTBUFSIZE;
+ stbuf[2] = stbuf[1] + P1_STMTBUFSIZE;
+ nincl = 0;
+ inclp = NULL;
+ doinclude(name);
+ lexstate = NEWSTMT;
+ return(NO);
+}
+
+
+
+/* throw away the rest of the current line */
+ void
+flline(Void)
+{
+ lexstate = RETEOS;
+}
+
+
+
+ char *
+#ifdef KR_headers
+lexline(n)
+ int *n;
+#else
+lexline(int *n)
+#endif
+{
+ *n = (lastch - nextch) + 1;
+ return(nextch);
+}
+
+
+
+
+ void
+#ifdef KR_headers
+doinclude(name)
+ char *name;
+#else
+doinclude(char *name)
+#endif
+{
+ FILEP fp;
+ struct Inclfile *t;
+ char *name0, *lastslash, *s, *s0, *temp;
+ int j, k;
+ chainp I;
+ extern chainp Iargs;
+
+ if(inclp)
+ {
+ inclp->incllno = thislin;
+ inclp->inclcode = code;
+ inclp->inclstno = nxtstno;
+ if(nextcd)
+ inclp->incllinp = copyn(inclp->incllen = endcd-nextcd , nextcd);
+ else
+ inclp->incllinp = 0;
+ }
+ nextcd = NULL;
+
+ if(++nincl >= MAXINCLUDES)
+ Fatal("includes nested too deep");
+ if(name[0] == '\0')
+ fp = stdin;
+ else if(name[0] == '/' || inclp == NULL
+#ifdef MSDOS
+ || name[0] == '\\'
+ || name[1] == ':'
+#endif
+ )
+ fp = fopen(name, textread);
+ else {
+ lastslash = NULL;
+ s = s0 = inclp->inclname;
+#ifdef MSDOS
+ if (s[1] == ':')
+ lastslash = s + 1;
+#endif
+ for(; *s ; ++s)
+ if(*s == '/'
+#ifdef MSDOS
+ || *s == '\\'
+#endif
+ )
+ lastslash = s;
+ name0 = name;
+ if(lastslash) {
+ k = lastslash - s0 + 1;
+ temp = Alloc(k + strlen(name) + 1);
+ strncpy(temp, s0, k);
+ strcpy(temp+k, name);
+ name = temp;
+ }
+ fp = fopen(name, textread);
+ if (!fp && (I = Iargs)) {
+ k = strlen(name0) + 2;
+ for(; I; I = I->nextp) {
+ j = strlen(s = I->datap);
+ name = Alloc(j + k);
+ strcpy(name, s);
+ switch(s[j-1]) {
+ case '/':
+#ifdef MSDOS
+ case ':':
+ case '\\':
+#endif
+ break;
+ default:
+ name[j++] = '/';
+ }
+ strcpy(name+j, name0);
+ if (fp = fopen(name, textread)) {
+ free(name0);
+ goto havefp;
+ }
+ free(name);
+ name = name0;
+ }
+ }
+ }
+ if (fp)
+ {
+ havefp:
+ t = inclp;
+ inclp = ALLOC(Inclfile);
+ inclp->inclnext = t;
+ prevlin = thislin = 0;
+ infname = inclp->inclname = name;
+ infile = inclp->inclfp = fp;
+ lastline = 0;
+ putlineno();
+ lastline = 0;
+ }
+ else
+ {
+ fprintf(diagfile, "Cannot open file %s\n", name);
+ done(1);
+ }
+}
+
+
+
+
+ LOCAL int
+popinclude(Void)
+{
+ struct Inclfile *t;
+ register char *p;
+ register int k;
+
+ if(infile != stdin)
+ clf(&infile, infname, 1); /* Close the input file */
+ free(infname);
+
+ --nincl;
+ t = inclp->inclnext;
+ free( (charptr) inclp);
+ inclp = t;
+ if(inclp == NULL) {
+ infname = 0;
+ return(NO);
+ }
+
+ infile = inclp->inclfp;
+ infname = inclp->inclname;
+ lineno = prevlin = thislin = inclp->incllno;
+ code = inclp->inclcode;
+ stno = nxtstno = inclp->inclstno;
+ if(inclp->incllinp)
+ {
+ lastline = 0;
+ putlineno();
+ lastline = lineno;
+ endcd = nextcd = sbuf;
+ k = inclp->incllen;
+ p = inclp->incllinp;
+ while(--k >= 0)
+ *endcd++ = *p++;
+ free( (charptr) (inclp->incllinp) );
+ }
+ else
+ nextcd = NULL;
+ return(YES);
+}
+
+
+ void
+#ifdef KR_headers
+p1_line_number(line_number)
+ long line_number;
+#else
+p1_line_number(long line_number)
+#endif
+{
+ if (lastfile != lastfile0) {
+ p1puts(P1_FILENAME, fbuf);
+ lastfile0 = lastfile;
+ }
+ fprintf(pass1_file, "%d: %ld\n", P1_SET_LINE, line_number);
+ }
+
+ static void
+putlineno(Void)
+{
+ extern int gflag;
+ register char *s0, *s1;
+
+ if (gflag) {
+ if (lastline)
+ p1_line_number(lastline);
+ lastline = firstline;
+ if (lastfile != infname)
+ if (lastfile = infname) {
+ strncpy(fbuf, lastfile, sizeof(fbuf));
+ fbuf[sizeof(fbuf)-1] = 0;
+ }
+ else
+ fbuf[0] = 0;
+ }
+ if (addftnsrc) {
+ if (laststb && *laststb) {
+ for(s1 = laststb; *s1; s1++) {
+ for(s0 = s1; *s1 != '\n'; s1++)
+ if (*s1 == '*' && s1[1] == '/')
+ *s1 = '+';
+ *s1 = 0;
+ p1puts(P1_FORTRAN, s0);
+ }
+ *laststb = 0; /* prevent trouble after EOF */
+ }
+ laststb = stb0;
+ }
+ }
+
+ int
+yylex(Void)
+{
+ static int tokno;
+ int retval;
+
+ switch(lexstate)
+ {
+ case NEWSTMT : /* need a new statement */
+ retval = getcds();
+ putlineno();
+ if(retval == STEOF) {
+ retval = SEOF;
+ break;
+ } /* if getcds() == STEOF */
+ crunch();
+ tokno = 0;
+ lexstate = FIRSTTOKEN;
+ yystno = stno;
+ stno = nxtstno;
+ toklen = 0;
+ retval = SLABEL;
+ break;
+
+first:
+ case FIRSTTOKEN : /* first step on a statement */
+ analyz();
+ lexstate = OTHERTOKEN;
+ tokno = 1;
+ retval = stkey;
+ break;
+
+ case OTHERTOKEN : /* return next token */
+ if(nextch > lastch)
+ goto reteos;
+ ++tokno;
+ if( (stkey==SLOGIF || stkey==SELSEIF) && parlev==0 && tokno>3)
+ goto first;
+
+ if(stkey==SASSIGN && tokno==3 && nextch<lastch &&
+ nextch[0]=='t' && nextch[1]=='o')
+ {
+ nextch+=2;
+ retval = STO;
+ break;
+ }
+ retval = gettok();
+ break;
+
+reteos:
+ case RETEOS:
+ lexstate = NEWSTMT;
+ retval = SEOS;
+ break;
+ default:
+ fatali("impossible lexstate %d", lexstate);
+ break;
+ }
+
+ if (retval == SEOF)
+ flush_comments ();
+
+ return retval;
+}
+
+ LOCAL void
+contmax(Void)
+{
+ lineno = thislin;
+ many("continuation lines", 'C', maxcontin);
+ }
+
+/* Get Cards.
+
+ Returns STEOF or STINITIAL, never STCONTINUE. Any continuation cards get
+merged into one long card (hence the size of the buffer named sbuf) */
+
+ LOCAL int
+getcds(Void)
+{
+ register char *p, *q;
+
+ flush_comments ();
+top:
+ if(nextcd == NULL)
+ {
+ code = getcd( nextcd = sbuf, 1 );
+ stno = nxtstno;
+ prevlin = thislin;
+ }
+ if(code == STEOF)
+ if( popinclude() )
+ goto top;
+ else
+ return(STEOF);
+
+ if(code == STCONTINUE)
+ {
+ lineno = thislin;
+ nextcd = NULL;
+ goto top;
+ }
+
+/* Get rid of unused space at the head of the buffer */
+
+ if(nextcd > sbuf)
+ {
+ q = nextcd;
+ p = sbuf;
+ while(q < endcd)
+ *p++ = *q++;
+ endcd = p;
+ }
+
+/* Be aware that the input (i.e. the string at the address nextcd) is NOT
+ NULL-terminated */
+
+/* This loop merges all continuations into one long statement, AND puts the next
+ card to be read at the end of the buffer (i.e. it stores the look-ahead card
+ when there's room) */
+
+ ncont = 0;
+ for(;;) {
+ nextcd = endcd;
+ if (ncont >= maxcont || nextcd+66 > send)
+ contmax();
+ linestart[ncont++] = nextcd;
+ if ((code = getcd(nextcd,0)) != STCONTINUE)
+ break;
+ if (ncont == 20 && noextflag) {
+ lineno = thislin;
+ errext("more than 19 continuation lines");
+ }
+ }
+ nextch = sbuf;
+ lastch = nextcd - 1;
+
+ lineno = prevlin;
+ prevlin = thislin;
+ return(STINITIAL);
+}
+
+ static void
+#ifdef KR_headers
+bang(a, b, c, d, e)
+ char *a;
+ char *b;
+ char *c;
+ register char *d;
+ register char *e;
+#else
+bang(char *a, char *b, char *c, register char *d, register char *e)
+#endif
+ /* save ! comments */
+{
+ char buf[COMMENT_BUFFER_SIZE + 1];
+ register char *p, *pe;
+
+ p = buf;
+ pe = buf + COMMENT_BUFFER_SIZE;
+ *pe = 0;
+ while(a < b)
+ if (!(*p++ = *a++))
+ p[-1] = 0;
+ if (b < c)
+ *p++ = '\t';
+ while(d < e) {
+ if (!(*p++ = *d++))
+ p[-1] = ' ';
+ if (p == pe) {
+ store_comment(buf);
+ p = buf;
+ }
+ }
+ if (p > buf) {
+ while(--p >= buf && *p == ' ');
+ p[1] = 0;
+ store_comment(buf);
+ }
+ }
+
+
+/* getcd - Get next input card
+
+ This function reads the next input card from global file pointer infile.
+It assumes that b points to currently empty storage somewhere in sbuf */
+
+ LOCAL int
+#ifdef KR_headers
+getcd(b, nocont)
+ register char *b;
+ int nocont;
+#else
+getcd(register char *b, int nocont)
+#endif
+{
+ register int c;
+ register char *p, *bend;
+ int speclin; /* Special line - true when the line is allowed
+ to have more than 66 characters (e.g. the
+ "&" shorthand for continuation, use of a "\t"
+ to skip part of the label columns) */
+ static char a[6]; /* Statement label buffer */
+ static char *aend = a+6;
+ static char *stb, *stbend;
+ static int nst;
+ char *atend, *endcd0;
+ extern int warn72;
+ char buf72[24];
+ int amp, i;
+ char storage[COMMENT_BUFFER_SIZE + 1];
+ char *pointer;
+ long L;
+
+top:
+ endcd = b;
+ bend = b+66;
+ amp = speclin = NO;
+ atend = aend;
+
+/* Handle the continuation shorthand of "&" in the first column, which stands
+ for " x" */
+
+ if( (c = getc(infile)) == '&')
+ {
+ a[0] = c;
+ a[1] = 0;
+ a[5] = 'x';
+ amp = speclin = YES;
+ bend = send;
+ p = aend;
+ }
+
+/* Handle the Comment cards (a 'C', 'c', '*', or '!' in the first column). */
+
+ else if(comstart[c & 0xfff])
+ {
+ if (feof (infile)
+#ifdef EOF_CHAR
+ || c == EOF_CHAR
+#endif
+ )
+ return STEOF;
+
+ if (c == '#') {
+ *endcd++ = c;
+ while((c = getc(infile)) != '\n')
+ if (c == EOF)
+ return STEOF;
+ else if (endcd < bend)
+ *endcd++ = c;
+ ++thislin;
+ *endcd = 0;
+ if (b[1] == ' ')
+ p = b + 2;
+ else if (!strncmp(b,"#line ",6))
+ p = b + 6;
+ else {
+ bad_cpp:
+ errstr("Bad # line: \"%s\"", b);
+ goto top;
+ }
+ if (*p < '1' || *p > '9')
+ goto bad_cpp;
+ L = *p - '0';
+ while((c = *++p) >= '0' && c <= '9')
+ L = 10*L + c - '0';
+ if (c != ' ' || *++p != '"')
+ goto bad_cpp;
+ bend = p;
+ while(*++p != '"')
+ if (!*p)
+ goto bad_cpp;
+ *p = 0;
+ i = p - bend++;
+ thislin = L - 1;
+ if (!infname || strcmp(infname, bend)) {
+ if (infname)
+ free(infname);
+ lastfile = 0;
+ infname = Alloc(i);
+ strcpy(infname, bend);
+ if (inclp)
+ inclp->inclname = infname;
+ }
+ goto top;
+ }
+
+ storage[COMMENT_BUFFER_SIZE] = c = '\0';
+ pointer = storage;
+ while( !feof (infile) && (*pointer++ = c = getc(infile)) != '\n') {
+
+/* Handle obscure end of file conditions on many machines */
+
+ if (feof (infile) && (c == '\377' || c == EOF)) {
+ pointer--;
+ break;
+ } /* if (feof (infile)) */
+
+ if (c == '\0')
+ *(pointer - 1) = ' ';
+
+ if (pointer == &storage[COMMENT_BUFFER_SIZE]) {
+ store_comment (storage);
+ pointer = storage;
+ } /* if (pointer == BUFFER_SIZE) */
+ } /* while */
+
+ if (pointer > storage) {
+ if (c == '\n')
+
+/* Get rid of the newline */
+
+ pointer[-1] = 0;
+ else
+ *pointer = 0;
+
+ store_comment (storage);
+ } /* if */
+
+ if (feof (infile))
+ if (c != '\n') /* To allow the line index to
+ increment correctly */
+ return STEOF;
+
+ ++thislin;
+ goto top;
+ }
+
+ else if(c != EOF)
+ {
+
+/* Load buffer a with the statement label */
+
+ /* a tab in columns 1-6 skips to column 7 */
+ ungetc(c, infile);
+ for(p=a; p<aend && (c=getc(infile)) != '\n' && c!=EOF; )
+ if(c == '\t')
+
+/* The tab character translates into blank characters in the statement label */
+
+ {
+ atend = p;
+ while(p < aend)
+ *p++ = BLANK;
+ speclin = YES;
+ bend = send;
+ }
+ else
+ *p++ = c;
+ }
+
+/* By now we've read either a continuation character or the statement label
+ field */
+
+ if(c == EOF)
+ return(STEOF);
+
+/* The next 'if' block handles lines that have fewer than 7 characters */
+
+ if(c == '\n')
+ {
+ while(p < aend)
+ *p++ = BLANK;
+
+/* Blank out the buffer on lines which are not longer than 66 characters */
+
+ endcd0 = endcd;
+ if( ! speclin )
+ while(endcd < bend)
+ *endcd++ = BLANK;
+ }
+ else { /* read body of line */
+ if (warn72 & 2) {
+ speclin = YES;
+ bend = send;
+ }
+ while( endcd<bend && (c=getc(infile)) != '\n' && c!=EOF )
+ *endcd++ = c;
+ if(c == EOF)
+ return(STEOF);
+
+/* Drop any extra characters on the input card; this usually means those after
+ column 72 */
+
+ if(c != '\n')
+ {
+ i = 0;
+ while( (c=getc(infile)) != '\n' && c != EOF)
+ if (i < 23)
+ buf72[i++] = c;
+ if (warn72 && i && !speclin) {
+ buf72[i] = 0;
+ if (i >= 23)
+ strcpy(buf72+20, "...");
+ lineno = thislin + 1;
+ errstr("text after column 72: %s", buf72);
+ }
+ if(c == EOF)
+ return(STEOF);
+ }
+
+ endcd0 = endcd;
+ if( ! speclin )
+ while(endcd < bend)
+ *endcd++ = BLANK;
+ }
+
+/* The flow of control usually gets to this line (unless an earlier RETURN has
+ been taken) */
+
+ ++thislin;
+
+ /* Fortran 77 specifies that a 0 in column 6 */
+ /* does not signify continuation */
+
+ if( !isspace(a[5]) && a[5]!='0') {
+ if (!amp)
+ for(p = a; p < aend;)
+ if (*p++ == '!' && p != aend)
+ goto initcheck;
+ if (addftnsrc && stb) {
+ if (stbend > stb + 7) { /* otherwise forget col 1-6 */
+ /* kludge around funny p1gets behavior */
+ *stb++ = '$';
+ if (amp)
+ *stb++ = '&';
+ else
+ for(p = a; p < atend;)
+ *stb++ = *p++;
+ }
+ if (endcd0 - b > stbend - stb) {
+ if (stb > stbend)
+ stb = stbend;
+ endcd0 = b + (stbend - stb);
+ }
+ for(p = b; p < endcd0;)
+ *stb++ = *p++;
+ *stb++ = '\n';
+ *stb = 0;
+ }
+ if (nocont) {
+ lineno = thislin;
+ errstr("illegal continuation card (starts \"%.6s\")",a);
+ }
+ else if (!amp && strncmp(a," ",5)) {
+ lineno = thislin;
+ errstr("labeled continuation line (starts \"%.6s\")",a);
+ }
+ return(STCONTINUE);
+ }
+initcheck:
+ for(p=a; p<atend; ++p)
+ if( !isspace(*p) ) {
+ if (*p++ != '!')
+ goto initline;
+ bang(p, atend, aend, b, endcd);
+ goto top;
+ }
+ for(p = b ; p<endcd ; ++p)
+ if( !isspace(*p) ) {
+ if (*p++ != '!')
+ goto initline;
+ bang(a, a, a, p, endcd);
+ goto top;
+ }
+
+/* Skip over blank cards by reading the next one right away */
+
+ goto top;
+
+initline:
+ if (!lastline)
+ lastline = thislin;
+ if (addftnsrc) {
+ nst = (nst+1)%3;
+ if (!laststb && stb0)
+ laststb = stb0;
+ stb0 = stb = stbuf[nst];
+ *stb++ = '$'; /* kludge around funny p1gets behavior */
+ stbend = stb + sizeof(stbuf[0])-2;
+ for(p = a; p < atend;)
+ *stb++ = *p++;
+ if (atend < aend)
+ *stb++ = '\t';
+ for(p = b; p < endcd0;)
+ *stb++ = *p++;
+ *stb++ = '\n';
+ *stb = 0;
+ }
+
+/* Set nxtstno equal to the integer value of the statement label */
+
+ nxtstno = 0;
+ bend = a + 5;
+ for(p = a ; p < bend ; ++p)
+ if( !isspace(*p) )
+ if(isdigit(*p))
+ nxtstno = 10*nxtstno + (*p - '0');
+ else if (*p == '!') {
+ if (!addftnsrc)
+ bang(p+1,atend,aend,b,endcd);
+ endcd = b;
+ break;
+ }
+ else {
+ lineno = thislin;
+ errstr(
+ "nondigit in statement label field \"%.5s\"", a);
+ nxtstno = 0;
+ break;
+ }
+ firstline = thislin;
+ return(STINITIAL);
+}
+
+ LOCAL void
+#ifdef KR_headers
+adjtoklen(newlen)
+ int newlen;
+#else
+adjtoklen(int newlen)
+#endif
+{
+ while(maxtoklen < newlen)
+ maxtoklen = 2*maxtoklen + 2;
+ if (token = (char *)realloc(token, maxtoklen))
+ return;
+ fprintf(stderr, "adjtoklen: realloc(%d) failure!\n", maxtoklen);
+ exit(2);
+ }
+
+/* crunch -- deletes all space characters, folds the backslash chars and
+ Hollerith strings, quotes the Fortran strings */
+
+ LOCAL void
+crunch(Void)
+{
+ register char *i, *j, *j0, *j1, *prvstr;
+ int k, ten, nh, nh0, quote;
+
+ /* i is the next input character to be looked at
+ j is the next output character */
+
+ new_dcl = needwkey = parlev = parseen = 0;
+ expcom = 0; /* exposed ','s */
+ expeql = 0; /* exposed equal signs */
+ j = sbuf;
+ prvstr = sbuf;
+ k = 0;
+ for(i=sbuf ; i<=lastch ; ++i)
+ {
+ if(isspace(*i) )
+ continue;
+ if (*i == '!') {
+ while(i >= linestart[k])
+ if (++k >= maxcont)
+ contmax();
+ j0 = linestart[k];
+ if (!addftnsrc)
+ bang(sbuf,sbuf,sbuf,i+1,j0);
+ i = j0-1;
+ continue;
+ }
+
+/* Keep everything in a quoted string */
+
+ if(*i=='\'' || *i=='"')
+ {
+ int len = 0;
+
+ quote = *i;
+ *j = MYQUOTE; /* special marker */
+ for(;;)
+ {
+ if(++i > lastch)
+ {
+ err("unbalanced quotes; closing quote supplied");
+ if (j >= lastch)
+ j = lastch - 1;
+ break;
+ }
+ if(*i == quote)
+ if(i<lastch && i[1]==quote) ++i;
+ else break;
+ else if(*i=='\\' && i<lastch && use_bs) {
+ ++i;
+ *i = escapes[*(unsigned char *)i];
+ }
+ *++j = *i;
+ len++;
+ } /* for (;;) */
+
+ if ((len = j - sbuf) > maxtoklen)
+ adjtoklen(len);
+ j[1] = MYQUOTE;
+ j += 2;
+ prvstr = j;
+ }
+ else if( (*i=='h' || *i=='H') && j>prvstr) /* test for Hollerith strings */
+ {
+ j0 = j - 1;
+ if( ! isdigit(*j0)) goto copychar;
+ nh = *j0 - '0';
+ ten = 10;
+ j1 = prvstr;
+ if (j1+4 < j)
+ j1 = j-4;
+ for(;;) {
+ if (j0-- <= j1)
+ goto copychar;
+ if( ! isdigit(*j0 ) ) break;
+ nh += ten * (*j0-'0');
+ ten*=10;
+ }
+ /* a hollerith must be preceded by a punctuation mark.
+ '*' is possible only as repetition factor in a data statement
+ not, in particular, in character*2h
+*/
+
+ if( !(*j0=='*'&&sbuf[0]=='d') && *j0!='/'
+ && *j0!='(' && *j0!=',' && *j0!='=' && *j0!='.')
+ goto copychar;
+ nh0 = nh;
+ if(i+nh > lastch)
+ {
+ erri("%dH too big", nh);
+ nh = lastch - i;
+ nh0 = -1;
+ }
+ if (nh > maxtoklen)
+ adjtoklen(nh);
+ j0[1] = MYQUOTE; /* special marker */
+ j = j0 + 1;
+ while(nh-- > 0)
+ {
+ if (++i > lastch) {
+ hol_overflow:
+ if (nh0 >= 0)
+ erri("escapes make %dH too big",
+ nh0);
+ break;
+ }
+ if(*i == '\\' && use_bs) {
+ if (++i > lastch)
+ goto hol_overflow;
+ *i = escapes[*(unsigned char *)i];
+ }
+ *++j = *i;
+ }
+ j[1] = MYQUOTE;
+ j+=2;
+ prvstr = j;
+ }
+ else {
+ if(*i == '(') parseen = ++parlev;
+ else if(*i == ')') --parlev;
+ else if(parlev == 0)
+ if(*i == '=') expeql = 1;
+ else if(*i == ',') expcom = 1;
+copychar: /*not a string or space -- copy, shifting case if necessary */
+ if(shiftcase && isupper(*i))
+ *j++ = tolower(*i);
+ else *j++ = *i;
+ }
+ }
+ lastch = j - 1;
+ nextch = sbuf;
+}
+
+ LOCAL void
+analyz(Void)
+{
+ register char *i;
+
+ if(parlev != 0)
+ {
+ err("unbalanced parentheses, statement skipped");
+ stkey = SUNKNOWN;
+ lastch = sbuf - 1; /* prevent double error msg */
+ return;
+ }
+ if(nextch+2<=lastch && nextch[0]=='i' && nextch[1]=='f' && nextch[2]=='(')
+ {
+ /* assignment or if statement -- look at character after balancing paren */
+ parlev = 1;
+ for(i=nextch+3 ; i<=lastch; ++i)
+ if(*i == (MYQUOTE))
+ {
+ while(*++i != MYQUOTE)
+ ;
+ }
+ else if(*i == '(')
+ ++parlev;
+ else if(*i == ')')
+ {
+ if(--parlev == 0)
+ break;
+ }
+ if(i >= lastch)
+ stkey = SLOGIF;
+ else if(i[1] == '=')
+ stkey = SLET;
+ else if( isdigit(i[1]) )
+ stkey = SARITHIF;
+ else stkey = SLOGIF;
+ if(stkey != SLET)
+ nextch += 2;
+ }
+ else if(expeql) /* may be an assignment */
+ {
+ if(expcom && nextch<lastch &&
+ nextch[0]=='d' && nextch[1]=='o')
+ {
+ stkey = SDO;
+ nextch += 2;
+ }
+ else stkey = SLET;
+ }
+ else if (parseen && nextch + 7 < lastch
+ && nextch[2] != 'u' /* screen out "double..." early */
+ && nextch[0] == 'd' && nextch[1] == 'o'
+ && ((nextch[2] >= '0' && nextch[2] <= '9')
+ || nextch[2] == ','
+ || nextch[2] == 'w'))
+ {
+ stkey = SDO;
+ nextch += 2;
+ needwkey = 1;
+ }
+ /* otherwise search for keyword */
+ else {
+ stkey = getkwd();
+ if(stkey==SGOTO && lastch>=nextch)
+ if(nextch[0]=='(')
+ stkey = SCOMPGOTO;
+ else if(isalpha_(* USC nextch))
+ stkey = SASGOTO;
+ }
+ parlev = 0;
+}
+
+
+
+ LOCAL int
+getkwd(Void)
+{
+ register char *i, *j;
+ register struct Keylist *pk, *pend;
+ int k;
+
+ if(! isalpha_(* USC nextch) )
+ return(SUNKNOWN);
+ k = letter(nextch[0]);
+ if(pk = keystart[k])
+ for(pend = keyend[k] ; pk<=pend ; ++pk )
+ {
+ i = pk->keyname;
+ j = nextch;
+ while(*++i==*++j && *i!='\0')
+ ;
+ if(*i=='\0' && j<=lastch+1)
+ {
+ nextch = j;
+ if(no66flag && pk->notinf66)
+ errstr("Not a Fortran 66 keyword: %s",
+ pk->keyname);
+ return(pk->keyval);
+ }
+ }
+ return(SUNKNOWN);
+}
+
+ void
+initkey(Void)
+{
+ register struct Keylist *p;
+ register int i,j;
+ register char *s;
+
+ for(i = 0 ; i<26 ; ++i)
+ keystart[i] = NULL;
+
+ for(p = keys ; p->keyname ; ++p) {
+ j = letter(p->keyname[0]);
+ if(keystart[j] == NULL)
+ keystart[j] = p;
+ keyend[j] = p;
+ }
+ i = (maxcontin + 2) * 66;
+ sbuf = (char *)ckalloc(i + 70);
+ send = sbuf + i;
+ maxcont = maxcontin + 1;
+ linestart = (char **)ckalloc(maxcont*sizeof(char*));
+ comstart['c'] = comstart['C'] = comstart['*'] = comstart['!'] =
+ comstart['#'] = 1;
+#ifdef EOF_CHAR
+ comstart[EOF_CHAR] = 1;
+#endif
+ s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
+ while(i = *s++)
+ anum_buf[i] = 1;
+ s = "0123456789";
+ while(i = *s++)
+ anum_buf[i] = 2;
+ }
+
+ LOCAL int
+#ifdef KR_headers
+hexcheck(key)
+ int key;
+#else
+hexcheck(int key)
+#endif
+{
+ register int radix;
+ register char *p;
+ char *kind;
+
+ switch(key) {
+ case 'z':
+ case 'Z':
+ case 'x':
+ case 'X':
+ radix = 16;
+ key = SHEXCON;
+ kind = "hexadecimal";
+ break;
+ case 'o':
+ case 'O':
+ radix = 8;
+ key = SOCTCON;
+ kind = "octal";
+ break;
+ case 'b':
+ case 'B':
+ radix = 2;
+ key = SBITCON;
+ kind = "binary";
+ break;
+ default:
+ err("bad bit identifier");
+ return(SNAME);
+ }
+ for(p = token; *p; p++)
+ if (hextoi(*p) >= radix) {
+ errstr("invalid %s character", kind);
+ break;
+ }
+ return key;
+ }
+
+/* gettok -- moves the right amount of text from nextch into the token
+ buffer. token initially contains garbage (leftovers from the prev token) */
+
+ LOCAL int
+gettok(Void)
+{
+ int havdot, havexp, havdbl;
+ int radix, val;
+ struct Punctlist *pp;
+ struct Dotlist *pd;
+ register int ch;
+
+ char *i, *j, *n1, *p;
+
+ ch = * USC nextch;
+ if(ch == (MYQUOTE))
+ {
+ ++nextch;
+ p = token;
+ while(*nextch != MYQUOTE)
+ *p++ = *nextch++;
+ toklen = p - token;
+ *p = 0;
+ /* allow octal, binary, hex constants of the form 'abc'x (etc.) */
+ if (++nextch <= lastch && isalpha_(val = * USC nextch)) {
+ ++nextch;
+ return hexcheck(val);
+ }
+ return (SHOLLERITH);
+ }
+
+ if(needkwd)
+ {
+ needkwd = 0;
+ return( getkwd() );
+ }
+
+ for(pp=puncts; pp->punchar; ++pp)
+ if(ch == pp->punchar) {
+ val = pp->punval;
+ if (++nextch <= lastch)
+ switch(ch) {
+ case '/':
+ if (*nextch == '/') {
+ nextch++;
+ val = SCONCAT;
+ }
+ else if (new_dcl && parlev == 0)
+ val = SSLASHD;
+ return val;
+ case '*':
+ if (*nextch == '*') {
+ nextch++;
+ return SPOWER;
+ }
+ break;
+ case '<':
+ if (*nextch == '=') {
+ nextch++;
+ val = SLE;
+ }
+ if (*nextch == '>') {
+ nextch++;
+ val = SNE;
+ }
+ goto extchk;
+ case '=':
+ if (*nextch == '=') {
+ nextch++;
+ val = SEQ;
+ goto extchk;
+ }
+ break;
+ case '>':
+ if (*nextch == '=') {
+ nextch++;
+ val = SGE;
+ }
+ extchk:
+ NOEXT("Fortran 8x comparison operator");
+ return val;
+ }
+ else if (ch == '/' && new_dcl && parlev == 0)
+ return SSLASHD;
+ switch(val) {
+ case SLPAR:
+ ++parlev;
+ break;
+ case SRPAR:
+ --parlev;
+ }
+ return(val);
+ }
+ if(ch == '.')
+ if(nextch >= lastch) goto badchar;
+ else if(isdigit(nextch[1])) goto numconst;
+ else {
+ for(pd=dots ; (j=pd->dotname) ; ++pd)
+ {
+ for(i=nextch+1 ; i<=lastch ; ++i)
+ if(*i != *j) break;
+ else if(*i != '.') ++j;
+ else {
+ nextch = i+1;
+ return(pd->dotval);
+ }
+ }
+ goto badchar;
+ }
+ if( isalpha_(ch) )
+ {
+ p = token;
+ *p++ = *nextch++;
+ while(nextch<=lastch)
+ if( isalnum_(* USC nextch) )
+ *p++ = *nextch++;
+ else break;
+ toklen = p - token;
+ *p = 0;
+ if (needwkey) {
+ needwkey = 0;
+ if (toklen == 5
+ && nextch <= lastch && *nextch == '(' /*)*/
+ && !strcmp(token,"while"))
+ return(SWHILE);
+ }
+ if(inioctl && nextch<=lastch && *nextch=='=')
+ {
+ ++nextch;
+ return(SNAMEEQ);
+ }
+ if(toklen>8 && eqn(8,token,"function")
+ && isalpha_(* USC (token+8)) &&
+ nextch<lastch && nextch[0]=='(' &&
+ (nextch[1]==')' || isalpha_(* USC (nextch+1))) )
+ {
+ nextch -= (toklen - 8);
+ return(SFUNCTION);
+ }
+
+ if(toklen > 50)
+ {
+ char buff[100];
+ sprintf(buff, toklen >= 60
+ ? "name %.56s... too long, truncated to %.*s"
+ : "name %s too long, truncated to %.*s",
+ token, 50, token);
+ err(buff);
+ toklen = 50;
+ token[50] = '\0';
+ }
+ if(toklen==1 && *nextch==MYQUOTE) {
+ val = token[0];
+ ++nextch;
+ for(p = token ; *nextch!=MYQUOTE ; )
+ *p++ = *nextch++;
+ ++nextch;
+ toklen = p - token;
+ *p = 0;
+ return hexcheck(val);
+ }
+ return(SNAME);
+ }
+
+ if (isdigit(ch)) {
+
+ /* Check for NAG's special hex constant */
+
+ if (nextch[1] == '#' && nextch < lastch
+ || nextch[2] == '#' && isdigit(nextch[1]
+ && lastch - nextch >= 2)) {
+
+ radix = atoi (nextch);
+ if (*++nextch != '#')
+ nextch++;
+ if (radix != 2 && radix != 8 && radix != 16) {
+ erri("invalid base %d for constant, defaulting to hex",
+ radix);
+ radix = 16;
+ } /* if */
+ if (++nextch > lastch)
+ goto badchar;
+ for (p = token; hextoi(*nextch) < radix;) {
+ *p++ = *nextch++;
+ if (nextch > lastch)
+ break;
+ }
+ toklen = p - token;
+ *p = 0;
+ return (radix == 16) ? SHEXCON : ((radix == 8) ? SOCTCON :
+ SBITCON);
+ }
+ }
+ else
+ goto badchar;
+numconst:
+ havdot = NO;
+ havexp = NO;
+ havdbl = NO;
+ for(n1 = nextch ; nextch<=lastch ; ++nextch)
+ {
+ if(*nextch == '.')
+ if(havdot) break;
+ else if(nextch+2<=lastch && isalpha_(* USC (nextch+1))
+ && isalpha_(* USC (nextch+2)))
+ break;
+ else havdot = YES;
+ else if( !intonly && (*nextch=='d' || *nextch=='e') )
+ {
+ p = nextch;
+ havexp = YES;
+ if(*nextch == 'd')
+ havdbl = YES;
+ if(nextch<lastch)
+ if(nextch[1]=='+' || nextch[1]=='-')
+ ++nextch;
+ if( ! isdigit(*++nextch) )
+ {
+ nextch = p;
+ havdbl = havexp = NO;
+ break;
+ }
+ for(++nextch ;
+ nextch<=lastch && isdigit(* USC nextch);
+ ++nextch);
+ break;
+ }
+ else if( ! isdigit(* USC nextch) )
+ break;
+ }
+ p = token;
+ i = n1;
+ while(i < nextch)
+ *p++ = *i++;
+ toklen = p - token;
+ *p = 0;
+ if(havdbl) return(SDCON);
+ if(havdot || havexp) return(SRCON);
+ return(SICON);
+badchar:
+ sbuf[0] = *nextch++;
+ return(SUNKNOWN);
+}
+
+/* Comment buffering code */
+
+ static void
+#ifdef KR_headers
+store_comment(str)
+ char *str;
+#else
+store_comment(char *str)
+#endif
+{
+ int len;
+ comment_buf *ncb;
+
+ if (nextcd == sbuf) {
+ flush_comments();
+ p1_comment(str);
+ return;
+ }
+ len = strlen(str) + 1;
+ if (cbnext + len > cblast) {
+ if (!cbcur || !(ncb = cbcur->next)) {
+ ncb = (comment_buf *) Alloc(sizeof(comment_buf));
+ if (cbcur) {
+ cbcur->last = cbnext;
+ cbcur->next = ncb;
+ }
+ else {
+ cbfirst = ncb;
+ cbinit = ncb->buf;
+ }
+ ncb->next = 0;
+ }
+ cbcur = ncb;
+ cbnext = ncb->buf;
+ cblast = cbnext + COMMENT_BUF_STORE;
+ }
+ strcpy(cbnext, str);
+ cbnext += len;
+ }
+
+ static void
+flush_comments(Void)
+{
+ register char *s, *s1;
+ register comment_buf *cb;
+ if (cbnext == cbinit)
+ return;
+ cbcur->last = cbnext;
+ for(cb = cbfirst;; cb = cb->next) {
+ for(s = cb->buf; s < cb->last; s = s1) {
+ /* compute s1 = new s value first, since */
+ /* p1_comment may insert nulls into s */
+ s1 = s + strlen(s) + 1;
+ p1_comment(s);
+ }
+ if (cb == cbcur)
+ break;
+ }
+ cbcur = cbfirst;
+ cbnext = cbinit;
+ cblast = cbnext + COMMENT_BUF_STORE;
+ }
+
+ void
+unclassifiable(Void)
+{
+ register char *s, *se;
+
+ s = sbuf;
+ se = lastch;
+ if (se < sbuf)
+ return;
+ lastch = s - 1;
+ if (se - s > 10)
+ se = s + 10;
+ for(; s < se; s++)
+ if (*s == MYQUOTE) {
+ se = s;
+ break;
+ }
+ *se = 0;
+ errstr("unclassifiable statement (starts \"%s\")", sbuf);
+ }
diff --git a/usr.bin/f2c/machdefs.h b/usr.bin/f2c/machdefs.h
new file mode 100644
index 0000000..3ab8961
--- /dev/null
+++ b/usr.bin/f2c/machdefs.h
@@ -0,0 +1,31 @@
+#define TYLENG TYLONG /* char string length field */
+
+#define TYINT TYLONG
+#define SZADDR 4
+#define SZSHORT 2
+#define SZINT 4
+
+#define SZLONG 4
+#define SZLENG SZLONG
+
+#define SZDREAL 8
+
+/* Alignment restrictions */
+
+#define ALIADDR SZADDR
+#define ALISHORT SZSHORT
+#define ALILONG 4
+#define ALIDOUBLE 8
+#define ALIINT ALILONG
+#define ALILENG ALILONG
+
+#define BLANKCOMMON "_BLNK__" /* Name for the unnamed
+ common block; this is unique
+ because of underscores */
+
+#define LABELFMT "%s:\n"
+
+#define MAXREGVAR 4
+#define TYIREG TYLONG
+#define MSKIREG (M(TYSHORT)|M(TYLONG)) /* allowed types of DO indicies
+ which can be put in registers */
diff --git a/usr.bin/f2c/main.c b/usr.bin/f2c/main.c
new file mode 100644
index 0000000..f58a177
--- /dev/null
+++ b/usr.bin/f2c/main.c
@@ -0,0 +1,683 @@
+/****************************************************************
+Copyright 1990 - 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+extern char F2C_version[];
+
+#include "defs.h"
+#include "parse.h"
+
+int complex_seen, dcomplex_seen;
+
+LOCAL int Max_ftn_files;
+
+char **ftn_files;
+int current_ftn_file = 0;
+
+flag ftn66flag = NO;
+flag nowarnflag = NO;
+flag noextflag = NO;
+flag no66flag = NO; /* Must also set noextflag to this
+ same value */
+flag zflag = YES; /* recognize double complex intrinsics */
+flag debugflag = NO;
+flag onetripflag = NO;
+flag shiftcase = YES;
+flag undeftype = NO;
+flag checksubs = NO;
+flag r8flag = NO;
+flag use_bs = YES;
+flag keepsubs = NO;
+#ifdef TYQUAD
+flag use_tyquad = YES;
+#endif
+int tyreal = TYREAL;
+int tycomplex = TYCOMPLEX;
+
+int maxregvar = MAXREGVAR; /* if maxregvar > MAXREGVAR, error */
+int maxequiv = MAXEQUIV;
+int maxext = MAXEXT;
+int maxstno = MAXSTNO;
+int maxctl = MAXCTL;
+int maxhash = MAXHASH;
+int maxliterals = MAXLITERALS;
+int maxcontin = MAXCONTIN;
+int maxlablist = MAXLABLIST;
+int extcomm, ext1comm, useauto;
+int can_include = YES; /* so we can disable includes for netlib */
+
+static char *def_i2 = "";
+
+static int useshortints = NO; /* YES => tyint = TYSHORT */
+static int uselongints = NO; /* YES => tyint = TYLONG */
+int addftnsrc = NO; /* Include ftn source in output */
+int usedefsforcommon = NO; /* Use #defines for common reference */
+int forcedouble = YES; /* force real functions to double */
+int Ansi = NO;
+int def_equivs = YES;
+int tyioint = TYLONG;
+int szleng = SZLENG;
+int inqmask = M(TYLONG)|M(TYLOGICAL);
+int wordalign = NO;
+int forcereal = NO;
+int warn72 = NO;
+static int skipC, skipversion;
+char *file_name, *filename0, *parens;
+int Castargs = 1;
+static int Castargs1;
+static int typedefs = 0;
+int chars_per_wd, gflag, protostatus;
+int infertypes = 1;
+char used_rets[TYSUBR+1];
+extern char *tmpdir;
+static int h0align = 0;
+char *halign, *ohalign;
+int krparens = NO;
+int hsize; /* for padding under -h */
+int htype; /* for wr_equiv_init under -h */
+chainp Iargs;
+char *o_coutput = 0;
+
+#define f2c_entry(swit,count,type,store,size) \
+ p_entry ("-", swit, 0, count, type, store, size)
+
+static arg_info table[] = {
+ f2c_entry ("o", P_ONE_ARG, P_STRING, &o_coutput, YES),
+ f2c_entry ("w66", P_NO_ARGS, P_INT, &ftn66flag, YES),
+ f2c_entry ("w", P_NO_ARGS, P_INT, &nowarnflag, YES),
+ f2c_entry ("66", P_NO_ARGS, P_INT, &no66flag, YES),
+ f2c_entry ("1", P_NO_ARGS, P_INT, &onetripflag, YES),
+ f2c_entry ("onetrip", P_NO_ARGS, P_INT, &onetripflag, YES),
+ f2c_entry ("I2", P_NO_ARGS, P_INT, &useshortints, YES),
+ f2c_entry ("I4", P_NO_ARGS, P_INT, &uselongints, YES),
+ f2c_entry ("U", P_NO_ARGS, P_INT, &shiftcase, NO),
+ f2c_entry ("u", P_NO_ARGS, P_INT, &undeftype, YES),
+ f2c_entry ("O", P_ONE_ARG, P_INT, &maxregvar, 0),
+ f2c_entry ("C", P_NO_ARGS, P_INT, &checksubs, YES),
+ f2c_entry ("Nq", P_ONE_ARG, P_INT, &maxequiv, 0),
+ f2c_entry ("Nx", P_ONE_ARG, P_INT, &maxext, 0),
+ f2c_entry ("Ns", P_ONE_ARG, P_INT, &maxstno, 0),
+ f2c_entry ("Nc", P_ONE_ARG, P_INT, &maxctl, 0),
+ f2c_entry ("Nn", P_ONE_ARG, P_INT, &maxhash, 0),
+ f2c_entry ("NL", P_ONE_ARG, P_INT, &maxliterals, 0),
+ f2c_entry ("NC", P_ONE_ARG, P_INT, &maxcontin, 0),
+ f2c_entry ("Nl", P_ONE_ARG, P_INT, &maxlablist, 0),
+ f2c_entry ("c", P_NO_ARGS, P_INT, &addftnsrc, YES),
+ f2c_entry ("p", P_NO_ARGS, P_INT, &usedefsforcommon, YES),
+ f2c_entry ("R", P_NO_ARGS, P_INT, &forcedouble, NO),
+ f2c_entry ("!R", P_NO_ARGS, P_INT, &forcedouble, YES),
+ f2c_entry ("A", P_NO_ARGS, P_INT, &Ansi, YES),
+ f2c_entry ("ext", P_NO_ARGS, P_INT, &noextflag, YES),
+ f2c_entry ("z", P_NO_ARGS, P_INT, &zflag, NO),
+ f2c_entry ("a", P_NO_ARGS, P_INT, &useauto, YES),
+ f2c_entry ("r8", P_NO_ARGS, P_INT, &r8flag, YES),
+ f2c_entry ("i2", P_NO_ARGS, P_INT, &tyioint, NO),
+ f2c_entry ("w8", P_NO_ARGS, P_INT, &wordalign, YES),
+ f2c_entry ("!I", P_NO_ARGS, P_INT, &can_include, NO),
+ f2c_entry ("W", P_ONE_ARG, P_INT, &chars_per_wd, 0),
+ f2c_entry ("g", P_NO_ARGS, P_INT, &gflag, YES),
+ f2c_entry ("T", P_ONE_ARG, P_STRING, &tmpdir, 0),
+ f2c_entry ("E", P_NO_ARGS, P_INT, &extcomm, 1),
+ f2c_entry ("e1c", P_NO_ARGS, P_INT, &ext1comm, 1),
+ f2c_entry ("ec", P_NO_ARGS, P_INT, &ext1comm, 2),
+ f2c_entry ("C++", P_NO_ARGS, P_INT, &Ansi, 2),
+ f2c_entry ("P", P_NO_ARGS, P_INT, &Castargs, 3),
+ f2c_entry ("Ps", P_NO_ARGS, P_INT, &protostatus, 1),
+ f2c_entry ("!P", P_NO_ARGS, P_INT, &Castargs, 0),
+ f2c_entry ("!c", P_NO_ARGS, P_INT, &skipC, 1),
+ f2c_entry ("!it", P_NO_ARGS, P_INT, &infertypes, 0),
+ f2c_entry ("h", P_NO_ARGS, P_INT, &h0align, 1),
+ f2c_entry ("hd", P_NO_ARGS, P_INT, &h0align, 2),
+ f2c_entry ("kr", P_NO_ARGS, P_INT, &krparens, 1),
+ f2c_entry ("krd", P_NO_ARGS, P_INT, &krparens, 2),
+ f2c_entry ("!bs", P_NO_ARGS, P_INT, &use_bs, NO),
+ f2c_entry ("r", P_NO_ARGS, P_INT, &forcereal, YES),
+ f2c_entry ("72", P_NO_ARGS, P_INT, &warn72, 1),
+ f2c_entry ("f", P_NO_ARGS, P_INT, &warn72, 2),
+ f2c_entry ("s", P_NO_ARGS, P_INT, &keepsubs, 1),
+ f2c_entry ("d", P_ONE_ARG, P_STRING, &outbuf, 0),
+#ifdef TYQUAD
+ f2c_entry ("!i8", P_NO_ARGS, P_INT, &use_tyquad, NO),
+#endif
+
+ /* options omitted from man pages */
+
+ /* -ev ==> implement equivalence with initialized pointers */
+ f2c_entry ("ev", P_NO_ARGS, P_INT, &def_equivs, NO),
+
+ /* -!it used to be the default when -it was more agressive */
+
+ f2c_entry ("it", P_NO_ARGS, P_INT, &infertypes, 1),
+
+ /* -Pd is similar to -P, but omits :ref: lines */
+ f2c_entry ("Pd", P_NO_ARGS, P_INT, &Castargs, 2),
+
+ /* -t ==> emit typedefs (under -A or -C++) for procedure
+ argument types used. This is meant for netlib's
+ f2c service, so -A and -C++ will work with older
+ versions of f2c.h
+ */
+ f2c_entry ("t", P_NO_ARGS, P_INT, &typedefs, 1),
+
+ /* -!V ==> omit version msg (to facilitate using diff in
+ regression testing)
+ */
+ f2c_entry ("!V", P_NO_ARGS, P_INT, &skipversion, 1),
+
+ /* -Dnnn = debug level nnn */
+
+ f2c_entry ("D", P_ONE_ARG, P_INT, &debugflag, YES)
+}; /* table */
+
+extern char *c_functions; /* "c_functions" */
+extern char *coutput; /* "c_output" */
+extern char *initfname; /* "raw_data" */
+extern char *blkdfname; /* "block_data" */
+extern char *p1_file; /* "p1_file" */
+extern char *p1_bakfile; /* "p1_file.BAK" */
+extern char *sortfname; /* "init_file" */
+extern char *proto_fname; /* "proto_file" */
+FILE *protofile;
+
+ void
+set_externs(Void)
+{
+ static char *hset[3] = { 0, "integer", "doublereal" };
+
+/* Adjust the global flags according to the command line parameters */
+
+ if (chars_per_wd > 0) {
+ typesize[TYADDR] = typesize[TYLONG] = typesize[TYREAL] =
+ typesize[TYLOGICAL] = chars_per_wd;
+ typesize[TYINT1] = typesize[TYLOGICAL1] = 1;
+ typesize[TYDREAL] = typesize[TYCOMPLEX] = chars_per_wd << 1;
+ typesize[TYDCOMPLEX] = chars_per_wd << 2;
+ typesize[TYSHORT] = typesize[TYLOGICAL2] = chars_per_wd >> 1;
+ typesize[TYCILIST] = 5*chars_per_wd;
+ typesize[TYICILIST] = 6*chars_per_wd;
+ typesize[TYOLIST] = 9*chars_per_wd;
+ typesize[TYCLLIST] = 3*chars_per_wd;
+ typesize[TYALIST] = 2*chars_per_wd;
+ typesize[TYINLIST] = 26*chars_per_wd;
+ }
+
+ if (wordalign)
+ typealign[TYDREAL] = typealign[TYDCOMPLEX] = typealign[TYREAL];
+ if (!tyioint) {
+ tyioint = TYSHORT;
+ szleng = typesize[TYSHORT];
+ def_i2 = "#define f2c_i2 1\n";
+ inqmask = M(TYSHORT)|M(TYLOGICAL2);
+ goto checklong;
+ }
+ else
+ szleng = typesize[TYLONG];
+ if (useshortints) {
+ /* inqmask = M(TYLONG); */
+ /* used to disallow LOGICAL in INQUIRE under -I2 */
+ checklong:
+ protorettypes[TYLOGICAL] = "shortlogical";
+ casttypes[TYLOGICAL] = "K_fp";
+ if (uselongints)
+ err ("Can't use both long and short ints");
+ else {
+ tyint = tylogical = TYSHORT;
+ tylog = TYLOGICAL2;
+ }
+ }
+ else if (uselongints)
+ tyint = TYLONG;
+
+ if (h0align) {
+ if (tyint == TYLONG && wordalign)
+ h0align = 1;
+ ohalign = halign = hset[h0align];
+ htype = h0align == 1 ? tyint : TYDREAL;
+ hsize = typesize[htype];
+ }
+
+ if (no66flag)
+ noextflag = no66flag;
+ if (noextflag)
+ zflag = 0;
+
+ if (r8flag) {
+ tyreal = TYDREAL;
+ tycomplex = TYDCOMPLEX;
+ r8fix();
+ }
+ if (forcedouble) {
+ protorettypes[TYREAL] = "E_f";
+ casttypes[TYREAL] = "E_fp";
+ }
+
+ if (maxregvar > MAXREGVAR) {
+ warni("-O%d: too many register variables", maxregvar);
+ maxregvar = MAXREGVAR;
+ } /* if maxregvar > MAXREGVAR */
+
+/* Check the list of input files */
+
+ {
+ int bad, i, cur_max = Max_ftn_files;
+
+ for (i = bad = 0; i < cur_max && ftn_files[i]; i++)
+ if (ftn_files[i][0] == '-') {
+ errstr ("Invalid flag '%s'", ftn_files[i]);
+ bad++;
+ }
+ if (bad)
+ exit(1);
+
+ } /* block */
+} /* set_externs */
+
+
+ static int
+comm2dcl(Void)
+{
+ Extsym *ext;
+ if (ext1comm)
+ for(ext = extsymtab; ext < nextext; ext++)
+ if (ext->extstg == STGCOMMON && !ext->extinit)
+ return ext1comm;
+ return 0;
+ }
+
+ static void
+#ifdef KR_headers
+write_typedefs(outfile)
+ FILE *outfile;
+#else
+write_typedefs(FILE *outfile)
+#endif
+{
+ register int i;
+ register char *s, *p = 0;
+ static char st[4] = { TYREAL, TYCOMPLEX, TYDCOMPLEX, TYCHAR };
+ static char stl[4] = { 'E', 'C', 'Z', 'H' };
+
+ for(i = 0; i <= TYSUBR; i++)
+ if (s = usedcasts[i]) {
+ if (!p) {
+ p = Ansi == 1 ? "()" : "(...)";
+ nice_printf(outfile,
+ "/* Types for casting procedure arguments: */\
+\n\n#ifndef F2C_proc_par_types\n");
+ if (i == 0) {
+ nice_printf(outfile,
+ "typedef int /* Unknown procedure type */ (*%s)%s;\n",
+ s, p);
+ continue;
+ }
+ }
+ nice_printf(outfile, "typedef %s (*%s)%s;\n",
+ c_type_decl(i,1), s, p);
+ }
+ for(i = !forcedouble; i < 4; i++)
+ if (used_rets[st[i]])
+ nice_printf(outfile,
+ "typedef %s %c_f; /* %s function */\n",
+ p = i ? "VOID" : "doublereal",
+ stl[i], ftn_types[st[i]]);
+ if (p)
+ nice_printf(outfile, "#endif\n\n");
+ }
+
+ static void
+#ifdef KR_headers
+commonprotos(outfile)
+ register FILE *outfile;
+#else
+commonprotos(register FILE *outfile)
+#endif
+{
+ register Extsym *e, *ee;
+ register Argtypes *at;
+ Atype *a, *ae;
+ int k;
+ extern int proc_protochanges;
+
+ if (!outfile)
+ return;
+ for (e = extsymtab, ee = nextext; e < ee; e++)
+ if (e->extstg == STGCOMMON && e->allextp)
+ nice_printf(outfile, "/* comlen %s %ld */\n",
+ e->cextname, e->maxleng);
+ if (Castargs1 < 3)
+ return;
+
+ /* -Pr: special comments conveying current knowledge
+ of external references */
+
+ k = proc_protochanges;
+ for (e = extsymtab, ee = nextext; e < ee; e++)
+ if (e->extstg == STGEXT
+ && e->cextname != e->fextname) /* not a library function */
+ if (at = e->arginfo) {
+ if ((!e->extinit || at->changes & 1)
+ /* not defined here or
+ changed since definition */
+ && at->nargs >= 0) {
+ nice_printf(outfile, "/*:ref: %s %d %d",
+ e->cextname, e->extype, at->nargs);
+ a = at->atypes;
+ for(ae = a + at->nargs; a < ae; a++)
+ nice_printf(outfile, " %d", a->type);
+ nice_printf(outfile, " */\n");
+ if (at->changes & 1)
+ k++;
+ }
+ }
+ else if (e->extype)
+ /* typed external, never invoked */
+ nice_printf(outfile, "/*:ref: %s %d :*/\n",
+ e->cextname, e->extype);
+ if (k) {
+ nice_printf(outfile,
+ "/* Rerunning f2c -P may change prototypes or declarations. */\n");
+ if (nerr)
+ return;
+ if (protostatus)
+ done(4);
+ if (protofile != stdout) {
+ fprintf(diagfile,
+ "Rerunning \"f2c -P ... %s %s\" may change prototypes or declarations.\n",
+ filename0, proto_fname);
+ fflush(diagfile);
+ }
+ }
+ }
+
+ static int
+#ifdef KR_headers
+I_args(argc, a)
+ int argc;
+ char **a;
+#else
+I_args(int argc, char **a)
+#endif
+{
+ char **a0, **a1, **ae, *s;
+
+ ae = a + argc;
+ a0 = a;
+ for(a1 = ++a; a < ae; a++) {
+ if (!(s = *a))
+ break;
+ if (*s == '-' && s[1] == 'I' && s[2]
+ && (s[3] || s[2] != '2' && s[2] != '4'))
+ Iargs = mkchain(s+2, Iargs);
+ else
+ *a1++ = s;
+ }
+ Iargs = revchain(Iargs);
+ *a1 = 0;
+ return a1 - a0;
+ }
+
+ int retcode = 0;
+
+ int
+#ifdef KR_headers
+main(argc, argv)
+ int argc;
+ char **argv;
+#else
+main(int argc, char **argv)
+#endif
+{
+ int c2d, k;
+ FILE *c_output;
+ char *cdfilename;
+ static char stderrbuf[BUFSIZ];
+ extern char **dfltproc, *dflt1proc[];
+ extern char link_msg[];
+
+ diagfile = stderr;
+ setbuf(stderr, stderrbuf); /* arrange for fast error msgs */
+
+ argc = I_args(argc, argv); /* extract -I args */
+ Max_ftn_files = argc - 1;
+ ftn_files = (char **)ckalloc((argc+1)*sizeof(char *));
+
+ parse_args (argc, argv, table, sizeof(table)/sizeof(arg_info),
+ ftn_files, Max_ftn_files);
+ if (keepsubs && checksubs) {
+ warn("-C suppresses -s\n");
+ keepsubs = 0;
+ }
+ if (!can_include && ext1comm == 2)
+ ext1comm = 1;
+ if (ext1comm && !extcomm)
+ extcomm = 2;
+ if (protostatus)
+ Castargs = 3;
+ Castargs1 = Castargs;
+ if (!Ansi) {
+ Castargs = 0;
+ parens = "()";
+ }
+ else if (!Castargs)
+ parens = Ansi == 1 ? "()" : "(...)";
+ else
+ dfltproc = dflt1proc;
+
+ outbuf_adjust();
+ set_externs();
+ fileinit();
+ read_Pfiles(ftn_files);
+
+ for(k = 1; ftn_files[k]; k++)
+ if (dofork())
+ break;
+ filename0 = file_name = ftn_files[current_ftn_file = k - 1];
+
+ set_tmp_names();
+ sigcatch(0);
+
+ c_file = opf(c_functions, textwrite);
+ pass1_file=opf(p1_file, binwrite);
+ initkey();
+ if (file_name && *file_name) {
+ cdfilename = coutput;
+ if (debugflag != 1) {
+ if (!o_coutput)
+ coutput = c_name(file_name,'c');
+ else
+ coutput = o_coutput;
+ cdfilename = copys(outbtail);
+ if (Castargs1 >= 2)
+ proto_fname = c_name(file_name,'P');
+ }
+ if (skipC)
+ coutput = 0;
+ if (coutput[0] == '-') {
+ c_output = stdout;
+ coutput = 0;
+ }
+ else if (!(c_output = fopen(coutput, textwrite))) {
+ file_name = coutput;
+ coutput = 0; /* don't delete read-only .c file */
+ fatalstr("can't open %.86s", file_name);
+ }
+
+ if (Castargs1 >= 2
+ && !(protofile = fopen(proto_fname, textwrite)))
+ fatalstr("Can't open %.84s\n", proto_fname);
+ }
+ else {
+ file_name = "";
+ cdfilename = "f2c_out.c";
+ c_output = stdout;
+ coutput = 0;
+ if (Castargs1 >= 2) {
+ protofile = stdout;
+ if (!skipC)
+ printf("#ifdef P_R_O_T_O_T_Y_P_E_S\n");
+ }
+ }
+
+ if(inilex( copys(file_name) ))
+ done(1);
+ if (filename0) {
+ fprintf(diagfile, "%s:\n", file_name);
+ fflush(diagfile);
+ }
+
+ procinit();
+ if(k = yyparse())
+ {
+ fprintf(diagfile, "Bad parse, return code %d\n", k);
+ done(1);
+ }
+
+ commonprotos(protofile);
+ if (protofile == stdout && !skipC)
+ printf("#endif\n\n");
+
+ if (nerr || skipC)
+ goto C_skipped;
+
+
+/* Write out the declarations which are global to this file */
+
+ if ((c2d = comm2dcl()) == 1)
+ nice_printf(c_output, "/*>>>'/dev/null'<<<*/\n\n\
+/* Split this into several files by piping it through\n\n\
+sed \"s/^\\/\\*>>>'\\(.*\\)'<<<\\*\\/\\$/cat >'\\1' <<'\\/*<<<\\1>>>*\\/'/\" | /bin/sh\n\
+ */\n\
+/*<<</dev/null>>>*/\n\
+/*>>>'%s'<<<*/\n", cdfilename);
+ if (gflag)
+ nice_printf (c_output, "#line 1 \"%s\"\n", file_name);
+ if (!skipversion) {
+ nice_printf (c_output, "/* %s -- translated by f2c ", file_name);
+ nice_printf (c_output, "(version %s).\n", F2C_version);
+ nice_printf (c_output,
+ " You must link the resulting object file with the libraries:\n\
+ %s (in that order)\n*/\n\n", link_msg);
+ }
+ if (Ansi == 2)
+ nice_printf(c_output,
+ "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+ nice_printf (c_output, "%s#include \"f2c.h\"\n\n", def_i2);
+ if (gflag)
+ nice_printf (c_output, "#line 1 \"%s\"\n", file_name);
+ if (Castargs && typedefs)
+ write_typedefs(c_output);
+ nice_printf (c_file, "\n");
+ fclose (c_file);
+ c_file = c_output; /* HACK to get the next indenting
+ to work */
+ wr_common_decls (c_output);
+ if (blkdfile)
+ list_init_data(&blkdfile, blkdfname, c_output);
+ wr_globals (c_output);
+ if ((c_file = fopen (c_functions, textread)) == (FILE *) NULL)
+ Fatal("main - couldn't reopen c_functions");
+ ffilecopy (c_file, c_output);
+ if (*main_alias) {
+ nice_printf (c_output, "/* Main program alias */ ");
+ nice_printf (c_output, "int %s () { MAIN__ ();%s }\n",
+ main_alias, Ansi ? " return 0;" : "");
+ }
+ if (Ansi == 2)
+ nice_printf(c_output,
+ "#ifdef __cplusplus\n\t}\n#endif\n");
+ if (c2d) {
+ if (c2d == 1)
+ fprintf(c_output, "/*<<<%s>>>*/\n", cdfilename);
+ else
+ fclose(c_output);
+ def_commons(c_output);
+ }
+ if (c2d != 2)
+ fclose (c_output);
+
+ C_skipped:
+ if(parstate != OUTSIDE)
+ {
+ warn("missing final end statement");
+ endproc();
+ }
+ done(nerr ? 1 : 0);
+ /* NOT REACHED */ return 0;
+}
+
+
+ FILEP
+#ifdef KR_headers
+opf(fn, mode)
+ char *fn;
+ char *mode;
+#else
+opf(char *fn, char *mode)
+#endif
+{
+ FILEP fp;
+ if( fp = fopen(fn, mode) )
+ return(fp);
+
+ fatalstr("cannot open intermediate file %s", fn);
+ /* NOT REACHED */ return 0;
+}
+
+
+ void
+#ifdef KR_headers
+clf(p, what, quit)
+ FILEP *p;
+ char *what;
+ int quit;
+#else
+clf(FILEP *p, char *what, int quit)
+#endif
+{
+ if(p!=NULL && *p!=NULL && *p!=stdout)
+ {
+ if(ferror(*p)) {
+ fprintf(stderr, "I/O error on %s\n", what);
+ if (quit)
+ done(3);
+ retcode = 3;
+ }
+ fclose(*p);
+ }
+ *p = NULL;
+}
+
+
+ void
+#ifdef KR_headers
+done(k)
+ int k;
+#else
+done(int k)
+#endif
+{
+ clf(&initfile, "initfile", 0);
+ clf(&c_file, "c_file", 0);
+ clf(&pass1_file, "pass1_file", 0);
+ Un_link_all(k);
+ exit(k|retcode);
+}
diff --git a/usr.bin/f2c/makefile b/usr.bin/f2c/makefile
new file mode 100644
index 0000000..d15fe2a
--- /dev/null
+++ b/usr.bin/f2c/makefile
@@ -0,0 +1,90 @@
+# Makefile for f2c, a Fortran 77 to C converter
+
+g = -g
+CFLAGS = $g
+SHELL = /bin/sh
+
+OBJECTSd = main.o init.o gram.o lex.o proc.o equiv.o data.o format.o \
+ expr.o exec.o intr.o io.o misc.o error.o mem.o names.o \
+ output.o p1output.o pread.o put.o putpcc.o vax.o formatdata.o \
+ parse_args.o niceprintf.o cds.o sysdep.o version.o
+OBJECTS = $(OBJECTSd) malloc.o
+
+all: xsum.out f2c
+
+f2c: $(OBJECTS)
+ $(CC) $(LDFLAGS) $(OBJECTS) -o f2c
+
+gram.c: gram.head gram.dcl gram.expr gram.exec gram.io defs.h tokdefs.h
+ ( sed <tokdefs.h "s/#define/%token/" ;\
+ cat gram.head gram.dcl gram.expr gram.exec gram.io ) >gram.in
+ $(YACC) $(YFLAGS) gram.in
+ echo "(expect 4 shift/reduce)"
+ sed 's/^# line.*/\/* & *\//' y.tab.c >gram.c
+ rm -f gram.in y.tab.c
+
+$(OBJECTSd): defs.h ftypes.h defines.h machdefs.h sysdep.h
+
+tokdefs.h: tokens
+ grep -n . <tokens | sed "s/\([^:]*\):\(.*\)/#define \2 \1/" >tokdefs.h
+
+cds.o: sysdep.h
+exec.o: p1defs.h names.h
+expr.o: output.h niceprintf.h names.h
+format.o: p1defs.h format.h output.h niceprintf.h names.h iob.h
+formatdata.o: format.h output.h niceprintf.h names.h
+gram.o: p1defs.h
+init.o: output.h niceprintf.h iob.h
+intr.o: names.h
+io.o: names.h iob.h
+lex.o : tokdefs.h p1defs.h
+main.o: parse.h usignal.h
+mem.o: iob.h
+names.o: iob.h names.h output.h niceprintf.h
+niceprintf.o: defs.h names.h output.h niceprintf.h
+output.o: output.h niceprintf.h names.h
+p1output.o: p1defs.h output.h niceprintf.h names.h
+parse_args.o: parse.h
+proc.o: tokdefs.h names.h niceprintf.h output.h p1defs.h
+put.o: names.h pccdefs.h p1defs.h
+putpcc.o: names.h
+vax.o: defs.h output.h pccdefs.h
+output.h: niceprintf.h
+
+put.o putpcc.o: pccdefs.h
+
+f2c.t: f2c.1t
+ troff -man f2c.1t >f2c.t
+
+f2c.1: f2c.1t
+ nroff -man f2c.1t | col -b | uniq >f2c.1
+
+clean:
+ rm -f gram.c *.o f2c tokdefs.h f2c.t
+
+b = Notice README cds.c data.c defines.h defs.h equiv.c error.c \
+ exec.c expr.c f2c.1 f2c.1t f2c.h format.c format.h formatdata.c \
+ ftypes.h gram.dcl gram.exec gram.expr gram.head gram.io \
+ init.c intr.c io.c iob.h lex.c machdefs.h main.c makefile \
+ malloc.c mem.c memset.c misc.c names.c names.h niceprintf.c \
+ niceprintf.h output.c output.h p1defs.h p1output.c \
+ parse.h parse_args.c pccdefs.h pread.c proc.c put.c putpcc.c \
+ sysdep.c sysdep.h tokens usignal.h vax.c version.c xsum.c
+
+bundle:
+ bundle $b xsum0.out >/tmp/f2c.bundle
+
+xsum: xsum.c
+ $(CC) -o xsum xsum.c
+
+#Check validity of transmitted source...
+xsum.out: xsum
+ ./xsum $b >xsum1.out
+ cmp xsum0.out xsum1.out && mv xsum1.out xsum.out
+
+#On non-Unix systems that end lines with carriage-return/newline pairs,
+#use "make xsumr.out" rather than "make xsum.out". The -r flag ignores
+#carriage-return characters.
+xsumr.out: xsum
+ ./xsum -r $b >xsum1.out
+ cmp xsum0.out xsum1.out && mv xsum1.out xsumr.out
diff --git a/usr.bin/f2c/malloc.c b/usr.bin/f2c/malloc.c
new file mode 100644
index 0000000..85bc5e3
--- /dev/null
+++ b/usr.bin/f2c/malloc.c
@@ -0,0 +1,166 @@
+/****************************************************************
+Copyright 1990, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#ifndef CRAY
+#define STACKMIN 512
+#define MINBLK (2*sizeof(struct mem) + 16)
+#define F _malloc_free_
+#define SBGULP 8192
+#include "string.h" /* for memcpy */
+
+#ifdef KR_headers
+#define Char char
+#define Unsigned unsigned
+#define Int /*int*/
+#else
+#define Char void
+#define Unsigned size_t
+#define Int int
+#endif
+
+typedef struct mem {
+ struct mem *next;
+ Unsigned len;
+ } mem;
+
+mem *F;
+
+ Char *
+#ifdef KR_headers
+malloc(size)
+ register Unsigned size;
+#else
+malloc(register Unsigned size)
+#endif
+{
+ register mem *p, *q, *r, *s;
+ unsigned register k, m;
+ extern Char *sbrk(Int);
+ char *top, *top1;
+
+ size = (size+7) & ~7;
+ r = (mem *) &F;
+ for (p = F, q = 0; p; r = p, p = p->next) {
+ if ((k = p->len) >= size && (!q || m > k)) {
+ m = k;
+ q = p;
+ s = r;
+ }
+ }
+ if (q) {
+ if (q->len - size >= MINBLK) { /* split block */
+ p = (mem *) (((char *) (q+1)) + size);
+ p->next = q->next;
+ p->len = q->len - size - sizeof(mem);
+ s->next = p;
+ q->len = size;
+ }
+ else
+ s->next = q->next;
+ }
+ else {
+ top = (Char *)(((long)sbrk(0) + 7) & ~7);
+ if (F && (char *)(F+1) + F->len == top) {
+ q = F;
+ F = F->next;
+ }
+ else
+ q = (mem *) top;
+ top1 = (char *)(q+1) + size;
+ if (sbrk((int)(top1-top+SBGULP)) == (Char *) -1)
+ return 0;
+ r = (mem *)top1;
+ r->len = SBGULP - sizeof(mem);
+ r->next = F;
+ F = r;
+ top1 += SBGULP;
+ q->len = size;
+ }
+ return (Char *) (q+1);
+ }
+
+ void
+#ifdef KR_headers
+free(f)
+ Char *f;
+#else
+free(Char *f)
+#endif
+{
+ mem *p, *q, *r;
+ char *pn, *qn;
+
+ if (!f) return;
+ q = (mem *) ((char *)f - sizeof(mem));
+ qn = (char *)f + q->len;
+ for (p = F, r = (mem *) &F; ; r = p, p = p->next) {
+ if (qn == (Char *) p) {
+ q->len += p->len + sizeof(mem);
+ p = p->next;
+ }
+ pn = p ? ((char *) (p+1)) + p->len : 0;
+ if (pn == (Char *) q) {
+ p->len += sizeof(mem) + q->len;
+ q->len = 0;
+ q->next = p;
+ r->next = p;
+ break;
+ }
+ if (pn < (char *) q) {
+ r->next = q;
+ q->next = p;
+ break;
+ }
+ }
+ }
+
+ Char *
+#ifdef KR_headers
+realloc(f, size)
+ Char *f;
+ Unsigned size;
+#else
+realloc(Char *f, Unsigned size)
+#endif
+{
+ mem *p;
+ Char *q, *f1;
+ Unsigned s1;
+
+ if (!f) return malloc(size);
+ p = (mem *) ((char *)f - sizeof(mem));
+ s1 = p->len;
+ free(f);
+ if (s1 > size)
+ s1 = size + 7 & ~7;
+ if (!p->len) {
+ f1 = (Char *)(p->next + 1);
+ memcpy(f1, f, s1);
+ f = f1;
+ }
+ q = malloc(size);
+ if (q && q != f)
+ memcpy(q, f, s1);
+ return q;
+ }
+#endif
diff --git a/usr.bin/f2c/mem.c b/usr.bin/f2c/mem.c
new file mode 100644
index 0000000..b8fc123
--- /dev/null
+++ b/usr.bin/f2c/mem.c
@@ -0,0 +1,268 @@
+/****************************************************************
+Copyright 1990, 1991, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "iob.h"
+
+#define MEMBSIZE 32000
+#define GMEMBSIZE 16000
+
+ char *
+#ifdef KR_headers
+gmem(n, round)
+ int n;
+ int round;
+#else
+gmem(int n, int round)
+#endif
+{
+ static char *last, *next;
+ char *rv;
+ if (round)
+#ifdef CRAY
+ if ((long)next & 0xe000000000000000)
+ next = (char *)(((long)next & 0x1fffffffffffffff) + 1);
+#else
+#ifdef MSDOS
+ if ((int)next & 1)
+ next++;
+#else
+ next = (char *)(((long)next + sizeof(char *)-1)
+ & ~((long)sizeof(char *)-1));
+#endif
+#endif
+ rv = next;
+ if ((next += n) > last) {
+ rv = Alloc(n + GMEMBSIZE);
+
+ next = rv + n;
+ last = next + GMEMBSIZE;
+ }
+ return rv;
+ }
+
+ struct memblock {
+ struct memblock *next;
+ char buf[MEMBSIZE];
+ };
+ typedef struct memblock memblock;
+
+ static memblock *mem0;
+ memblock *curmemblock, *firstmemblock;
+
+ char *mem_first, *mem_next, *mem_last, *mem0_last;
+
+ void
+mem_init(Void)
+{
+ curmemblock = firstmemblock = mem0
+ = (memblock *)Alloc(sizeof(memblock));
+ mem_first = mem0->buf;
+ mem_next = mem0->buf;
+ mem_last = mem0->buf + MEMBSIZE;
+ mem0_last = mem0->buf + MEMBSIZE;
+ mem0->next = 0;
+ }
+
+ char *
+#ifdef KR_headers
+mem(n, round)
+ int n;
+ int round;
+#else
+mem(int n, int round)
+#endif
+{
+ memblock *b;
+ register char *rv, *s;
+
+ if (round)
+#ifdef CRAY
+ if ((long)mem_next & 0xe000000000000000)
+ mem_next = (char *)(((long)mem_next & 0x1fffffffffffffff) + 1);
+#else
+#ifdef MSDOS
+ if ((int)mem_next & 1)
+ mem_next++;
+#else
+ mem_next = (char *)(((long)mem_next + sizeof(char *)-1)
+ & ~((long)sizeof(char *)-1));
+#endif
+#endif
+ rv = mem_next;
+ s = rv + n;
+ if (s >= mem_last) {
+ if (n > MEMBSIZE) {
+ fprintf(stderr, "mem(%d) failure!\n", n);
+ exit(1);
+ }
+ if (!(b = curmemblock->next)) {
+ b = (memblock *)Alloc(sizeof(memblock));
+ curmemblock->next = b;
+ b->next = 0;
+ }
+ curmemblock = b;
+ rv = b->buf;
+ mem_last = rv + sizeof(b->buf);
+ s = rv + n;
+ }
+ mem_next = s;
+ return rv;
+ }
+
+ char *
+#ifdef KR_headers
+tostring(s, n)
+ register char *s;
+ int n;
+#else
+tostring(register char *s, int n)
+#endif
+{
+ register char *s1, *se, **sf;
+ char *rv, *s0;
+ register int k = n + 2, t;
+
+ sf = str_fmt;
+ sf['%'] = "%";
+ s0 = s;
+ se = s + n;
+ for(; s < se; s++) {
+ t = *(unsigned char *)s;
+ s1 = sf[t];
+ while(*++s1)
+ k++;
+ }
+ sf['%'] = "%%";
+ rv = s1 = mem(k,0);
+ *s1++ = '"';
+ for(s = s0; s < se; s++) {
+ t = *(unsigned char *)s;
+ sprintf(s1, sf[t], t);
+ s1 += strlen(s1);
+ }
+ *s1 = 0;
+ return rv;
+ }
+
+ char *
+#ifdef KR_headers
+cpstring(s)
+ register char *s;
+#else
+cpstring(register char *s)
+#endif
+{
+ return strcpy(mem(strlen(s)+1,0), s);
+ }
+
+ void
+#ifdef KR_headers
+new_iob_data(ios, name)
+ register io_setup *ios;
+ char *name;
+#else
+new_iob_data(register io_setup *ios, char *name)
+#endif
+{
+ register iob_data *iod;
+ register char **s, **se;
+
+ iod = (iob_data *)
+ mem(sizeof(iob_data) + ios->nelt*sizeof(char *), 1);
+ iod->next = iob_list;
+ iob_list = iod;
+ iod->type = ios->fields[0];
+ iod->name = cpstring(name);
+ s = iod->fields;
+ se = s + ios->nelt;
+ while(s < se)
+ *s++ = "0";
+ *s = 0;
+ }
+
+ char *
+#ifdef KR_headers
+string_num(pfx, n)
+ char *pfx;
+ long n;
+#else
+string_num(char *pfx, long n)
+#endif
+{
+ char buf[32];
+ sprintf(buf, "%s%ld", pfx, n);
+ /* can't trust return type of sprintf -- BSD gets it wrong */
+ return strcpy(mem(strlen(buf)+1,0), buf);
+ }
+
+static defines *define_list;
+
+ void
+#ifdef KR_headers
+def_start(outfile, s1, s2, post)
+ FILE *outfile;
+ char *s1;
+ char *s2;
+ char *post;
+#else
+def_start(FILE *outfile, char *s1, char *s2, char *post)
+#endif
+{
+ defines *d;
+ int n, n1;
+ extern int in_define;
+
+ n = n1 = strlen(s1);
+ if (s2)
+ n += strlen(s2);
+ d = (defines *)mem(sizeof(defines)+n, 1);
+ d->next = define_list;
+ define_list = d;
+ strcpy(d->defname, s1);
+ if (s2)
+ strcpy(d->defname + n1, s2);
+ in_define = 1;
+ nice_printf(outfile, "#define %s", d->defname);
+ if (post)
+ nice_printf(outfile, " %s", post);
+ }
+
+ void
+#ifdef KR_headers
+other_undefs(outfile)
+ FILE *outfile;
+#else
+other_undefs(FILE *outfile)
+#endif
+{
+ defines *d;
+ if (d = define_list) {
+ define_list = 0;
+ nice_printf(outfile, "\n");
+ do
+ nice_printf(outfile, "#undef %s\n", d->defname);
+ while(d = d->next);
+ nice_printf(outfile, "\n");
+ }
+ }
diff --git a/usr.bin/f2c/memset.c b/usr.bin/f2c/memset.c
new file mode 100644
index 0000000..98a7ce7
--- /dev/null
+++ b/usr.bin/f2c/memset.c
@@ -0,0 +1,66 @@
+/****************************************************************
+Copyright 1990 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* This is for the benefit of people whose systems don't provide
+ * memset, memcpy, and memcmp. If yours is such a system, adjust
+ * the makefile by adding memset.o to the "OBJECTS =" assignment.
+ * WARNING: the memcpy below is adequate for f2c, but is not a
+ * general memcpy routine (which must correctly handle overlapping
+ * fields).
+ */
+
+ int
+memcmp(s1, s2, n)
+ register char *s1, *s2;
+ int n;
+{
+ register char *se;
+
+ for(se = s1 + n; s1 < se; s1++, s2++)
+ if (*s1 != *s2)
+ return *s1 - *s2;
+ return 0;
+ }
+
+ char *
+memcpy(s1, s2, n)
+ register char *s1, *s2;
+ int n;
+{
+ register char *s0 = s1, *se = s1 + n;
+
+ while(s1 < se)
+ *s1++ = *s2++;
+ return s0;
+ }
+
+memset(s, c, n)
+ register char *s;
+ register int c;
+ int n;
+{
+ register char *se = s + n;
+
+ while(s < se)
+ *s++ = c;
+ }
diff --git a/usr.bin/f2c/misc.c b/usr.bin/f2c/misc.c
new file mode 100644
index 0000000..bfaeb8a74
--- /dev/null
+++ b/usr.bin/f2c/misc.c
@@ -0,0 +1,1330 @@
+/****************************************************************
+Copyright 1990, 1992 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "limits.h"
+
+ int
+#ifdef KR_headers
+oneof_stg(name, stg, mask)
+ Namep name;
+ int stg;
+ int mask;
+#else
+oneof_stg(Namep name, int stg, int mask)
+#endif
+{
+ if (stg == STGCOMMON && name) {
+ if ((mask & M(STGEQUIV)))
+ return name->vcommequiv;
+ if ((mask & M(STGCOMMON)))
+ return !name->vcommequiv;
+ }
+ return ONEOF(stg, mask);
+ }
+
+
+/* op_assign -- given a binary opcode, return the associated assignment
+ operator */
+
+ int
+#ifdef KR_headers
+op_assign(opcode)
+ int opcode;
+#else
+op_assign(int opcode)
+#endif
+{
+ int retval = -1;
+
+ switch (opcode) {
+ case OPPLUS: retval = OPPLUSEQ; break;
+ case OPMINUS: retval = OPMINUSEQ; break;
+ case OPSTAR: retval = OPSTAREQ; break;
+ case OPSLASH: retval = OPSLASHEQ; break;
+ case OPMOD: retval = OPMODEQ; break;
+ case OPLSHIFT: retval = OPLSHIFTEQ; break;
+ case OPRSHIFT: retval = OPRSHIFTEQ; break;
+ case OPBITAND: retval = OPBITANDEQ; break;
+ case OPBITXOR: retval = OPBITXOREQ; break;
+ case OPBITOR: retval = OPBITOREQ; break;
+ default:
+ erri ("op_assign: bad opcode '%d'", opcode);
+ break;
+ } /* switch */
+
+ return retval;
+} /* op_assign */
+
+
+ char *
+#ifdef KR_headers
+Alloc(n)
+ int n;
+#else
+Alloc(int n)
+#endif
+ /* error-checking version of malloc */
+ /* ckalloc initializes memory to 0; Alloc does not */
+{
+ char errbuf[32];
+ register char *rv;
+
+ rv = malloc(n);
+ if (!rv) {
+ sprintf(errbuf, "malloc(%d) failure!", n);
+ Fatal(errbuf);
+ }
+ return rv;
+ }
+
+ void
+#ifdef KR_headers
+cpn(n, a, b)
+ register int n;
+ register char *a;
+ register char *b;
+#else
+cpn(register int n, register char *a, register char *b)
+#endif
+{
+ while(--n >= 0)
+ *b++ = *a++;
+}
+
+
+ int
+#ifdef KR_headers
+eqn(n, a, b)
+ register int n;
+ register char *a;
+ register char *b;
+#else
+eqn(register int n, register char *a, register char *b)
+#endif
+{
+ while(--n >= 0)
+ if(*a++ != *b++)
+ return(NO);
+ return(YES);
+}
+
+
+
+
+
+
+ int
+#ifdef KR_headers
+cmpstr(a, b, la, lb)
+ register char *a;
+ register char *b;
+ ftnint la;
+ ftnint lb;
+#else
+cmpstr(register char *a, register char *b, ftnint la, ftnint lb)
+#endif
+ /* compare two strings */
+{
+ register char *aend, *bend;
+ aend = a + la;
+ bend = b + lb;
+
+
+ if(la <= lb)
+ {
+ while(a < aend)
+ if(*a != *b)
+ return( *a - *b );
+ else
+ {
+ ++a;
+ ++b;
+ }
+
+ while(b < bend)
+ if(*b != ' ')
+ return(' ' - *b);
+ else
+ ++b;
+ }
+
+ else
+ {
+ while(b < bend)
+ if(*a != *b)
+ return( *a - *b );
+ else
+ {
+ ++a;
+ ++b;
+ }
+ while(a < aend)
+ if(*a != ' ')
+ return(*a - ' ');
+ else
+ ++a;
+ }
+ return(0);
+}
+
+
+/* hookup -- Same as LISP NCONC, that is a destructive append of two lists */
+
+ chainp
+#ifdef KR_headers
+hookup(x, y)
+ register chainp x;
+ register chainp y;
+#else
+hookup(register chainp x, register chainp y)
+#endif
+{
+ register chainp p;
+
+ if(x == NULL)
+ return(y);
+
+ for(p = x ; p->nextp ; p = p->nextp)
+ ;
+ p->nextp = y;
+ return(x);
+}
+
+
+
+ struct Listblock *
+#ifdef KR_headers
+mklist(p)
+ chainp p;
+#else
+mklist(chainp p)
+#endif
+{
+ register struct Listblock *q;
+
+ q = ALLOC(Listblock);
+ q->tag = TLIST;
+ q->listp = p;
+ return(q);
+}
+
+
+ chainp
+#ifdef KR_headers
+mkchain(p, q)
+ register char * p;
+ register chainp q;
+#else
+mkchain(register char * p, register chainp q)
+#endif
+{
+ register chainp r;
+
+ if(chains)
+ {
+ r = chains;
+ chains = chains->nextp;
+ }
+ else
+ r = ALLOC(Chain);
+
+ r->datap = p;
+ r->nextp = q;
+ return(r);
+}
+
+ chainp
+#ifdef KR_headers
+revchain(next)
+ register chainp next;
+#else
+revchain(register chainp next)
+#endif
+{
+ register chainp p, prev = 0;
+
+ while(p = next) {
+ next = p->nextp;
+ p->nextp = prev;
+ prev = p;
+ }
+ return prev;
+ }
+
+
+/* addunder -- turn a cvarname into an external name */
+/* The cvarname may already end in _ (to avoid C keywords); */
+/* if not, it has room for appending an _. */
+
+ char *
+#ifdef KR_headers
+addunder(s)
+ register char *s;
+#else
+addunder(register char *s)
+#endif
+{
+ register int c, i, j;
+ char *s0 = s;
+
+ i = j = 0;
+ while(c = *s++)
+ if (c == '_')
+ i++, j++;
+ else
+ i = 0;
+ if (!i) {
+ *s-- = 0;
+ *s = '_';
+ }
+ else if (j == 2)
+ s[-2] = 0;
+ return( s0 );
+ }
+
+
+/* copyn -- return a new copy of the input Fortran-string */
+
+ char *
+#ifdef KR_headers
+copyn(n, s)
+ register int n;
+ register char *s;
+#else
+copyn(register int n, register char *s)
+#endif
+{
+ register char *p, *q;
+
+ p = q = (char *) Alloc(n);
+ while(--n >= 0)
+ *q++ = *s++;
+ return(p);
+}
+
+
+
+/* copys -- return a new copy of the input C-string */
+
+ char *
+#ifdef KR_headers
+copys(s)
+ char *s;
+#else
+copys(char *s)
+#endif
+{
+ return( copyn( strlen(s)+1 , s) );
+}
+
+
+
+/* convci -- Convert Fortran-string to integer; assumes that input is a
+ legal number, with no trailing blanks */
+
+ ftnint
+#ifdef KR_headers
+convci(n, s)
+ register int n;
+ register char *s;
+#else
+convci(register int n, register char *s)
+#endif
+{
+ ftnint sum, t;
+ char buff[100], *s0;
+ int n0;
+
+ s0 = s;
+ n0 = n;
+ sum = 0;
+ while(n-- > 0) {
+ /* sum = 10*sum + (*s++ - '0'); */
+ t = *s++ - '0';
+ if (sum > LONG_MAX/10) {
+ ovfl:
+ if (n0 > 60)
+ n0 = 60;
+ sprintf(buff, "integer constant %.*s truncated.",
+ n0, s0);
+ err(buff);
+ return LONG_MAX;
+ }
+ sum *= 10;
+ if (sum > LONG_MAX - t)
+ goto ovfl;
+ sum += t;
+ }
+ return(sum);
+ }
+
+/* convic - Convert Integer constant to string */
+
+ char *
+#ifdef KR_headers
+convic(n)
+ ftnint n;
+#else
+convic(ftnint n)
+#endif
+{
+ static char s[20];
+ register char *t;
+
+ s[19] = '\0';
+ t = s+19;
+
+ do {
+ *--t = '0' + n%10;
+ n /= 10;
+ } while(n > 0);
+
+ return(t);
+}
+
+
+
+/* mkname -- add a new identifier to the environment, including the closed
+ hash table. */
+
+ Namep
+#ifdef KR_headers
+mkname(s)
+ register char *s;
+#else
+mkname(register char *s)
+#endif
+{
+ struct Hashentry *hp;
+ register Namep q;
+ register int c, hash, i;
+ register char *t;
+ char *s0;
+ char errbuf[64];
+
+ hash = i = 0;
+ s0 = s;
+ while(c = *s++) {
+ hash += c;
+ if (c == '_')
+ i = 2;
+ }
+ if (!i && in_vector(s0,c_keywords,n_keywords) >= 0)
+ i = 2;
+ hash %= maxhash;
+
+/* Add the name to the closed hash table */
+
+ hp = hashtab + hash;
+
+ while(q = hp->varp)
+ if( hash == hp->hashval && !strcmp(s0,q->fvarname) )
+ return(q);
+ else if(++hp >= lasthash)
+ hp = hashtab;
+
+ if(++nintnames >= maxhash-1)
+ many("names", 'n', maxhash); /* Fatal error */
+ hp->varp = q = ALLOC(Nameblock);
+ hp->hashval = hash;
+ q->tag = TNAME; /* TNAME means the tag type is NAME */
+ c = s - s0;
+ if (c > 7 && noextflag) {
+ sprintf(errbuf, "\"%.35s%s\" over 6 characters long", s0,
+ c > 36 ? "..." : "");
+ errext(errbuf);
+ }
+ q->fvarname = strcpy(mem(c,0), s0);
+ t = q->cvarname = mem(c + i + 1, 0);
+ s = s0;
+ /* add __ to the end of any name containing _ and to any C keyword */
+ while(*t = *s++)
+ t++;
+ if (i) {
+ do *t++ = '_';
+ while(--i > 0);
+ *t = 0;
+ }
+ return(q);
+}
+
+
+ struct Labelblock *
+#ifdef KR_headers
+mklabel(l)
+ ftnint l;
+#else
+mklabel(ftnint l)
+#endif
+{
+ register struct Labelblock *lp;
+
+ if(l <= 0)
+ return(NULL);
+
+ for(lp = labeltab ; lp < highlabtab ; ++lp)
+ if(lp->stateno == l)
+ return(lp);
+
+ if(++highlabtab > labtabend)
+ many("statement labels", 's', maxstno);
+
+ lp->stateno = l;
+ lp->labelno = newlabel();
+ lp->blklevel = 0;
+ lp->labused = NO;
+ lp->fmtlabused = NO;
+ lp->labdefined = NO;
+ lp->labinacc = NO;
+ lp->labtype = LABUNKNOWN;
+ lp->fmtstring = 0;
+ return(lp);
+}
+
+
+ int
+newlabel(Void)
+{
+ return( ++lastlabno );
+}
+
+
+/* this label appears in a branch context */
+
+ struct Labelblock *
+#ifdef KR_headers
+execlab(stateno)
+ ftnint stateno;
+#else
+execlab(ftnint stateno)
+#endif
+{
+ register struct Labelblock *lp;
+
+ if(lp = mklabel(stateno))
+ {
+ if(lp->labinacc)
+ warn1("illegal branch to inner block, statement label %s",
+ convic(stateno) );
+ else if(lp->labdefined == NO)
+ lp->blklevel = blklevel;
+ if(lp->labtype == LABFORMAT)
+ err("may not branch to a format");
+ else
+ lp->labtype = LABEXEC;
+ }
+ else
+ execerr("illegal label %s", convic(stateno));
+
+ return(lp);
+}
+
+
+/* find or put a name in the external symbol table */
+
+ Extsym *
+#ifdef KR_headers
+mkext1(f, s)
+ char *f;
+ char *s;
+#else
+mkext1(char *f, char *s)
+#endif
+{
+ Extsym *p;
+
+ for(p = extsymtab ; p<nextext ; ++p)
+ if(!strcmp(s,p->cextname))
+ return( p );
+
+ if(nextext >= lastext)
+ many("external symbols", 'x', maxext);
+
+ nextext->fextname = strcpy(gmem(strlen(f)+1,0), f);
+ nextext->cextname = f == s
+ ? nextext->fextname
+ : strcpy(gmem(strlen(s)+1,0), s);
+ nextext->extstg = STGUNKNOWN;
+ nextext->extp = 0;
+ nextext->allextp = 0;
+ nextext->extleng = 0;
+ nextext->maxleng = 0;
+ nextext->extinit = 0;
+ nextext->curno = nextext->maxno = 0;
+ return( nextext++ );
+}
+
+
+ Extsym *
+#ifdef KR_headers
+mkext(f, s)
+ char *f;
+ char *s;
+#else
+mkext(char *f, char *s)
+#endif
+{
+ Extsym *e = mkext1(f, s);
+ if (e->extstg == STGCOMMON)
+ errstr("%.52s cannot be a subprogram: it is a common block.",f);
+ return e;
+ }
+
+ Addrp
+#ifdef KR_headers
+builtin(t, s, dbi)
+ int t;
+ char *s;
+ int dbi;
+#else
+builtin(int t, char *s, int dbi)
+#endif
+{
+ register Extsym *p;
+ register Addrp q;
+ extern chainp used_builtins;
+
+ p = mkext(s,s);
+ if(p->extstg == STGUNKNOWN)
+ p->extstg = STGEXT;
+ else if(p->extstg != STGEXT)
+ {
+ errstr("improper use of builtin %s", s);
+ return(0);
+ }
+
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ q->vtype = t;
+ q->vclass = CLPROC;
+ q->vstg = STGEXT;
+ q->memno = p - extsymtab;
+ q->dbl_builtin = dbi;
+
+/* A NULL pointer here tells you to use memno to check the external
+ symbol table */
+
+ q -> uname_tag = UNAM_EXTERN;
+
+/* Add to the list of used builtins */
+
+ if (dbi >= 0)
+ add_extern_to_list (q, &used_builtins);
+ return(q);
+}
+
+
+ void
+#ifdef KR_headers
+add_extern_to_list(addr, list_store)
+ Addrp addr;
+ chainp *list_store;
+#else
+add_extern_to_list(Addrp addr, chainp *list_store)
+#endif
+{
+ chainp last = CHNULL;
+ chainp list;
+ int memno;
+
+ if (list_store == (chainp *) NULL || addr == (Addrp) NULL)
+ return;
+
+ list = *list_store;
+ memno = addr -> memno;
+
+ for (;list; last = list, list = list -> nextp) {
+ Addrp this = (Addrp) (list -> datap);
+
+ if (this -> tag == TADDR && this -> uname_tag == UNAM_EXTERN &&
+ this -> memno == memno)
+ return;
+ } /* for */
+
+ if (*list_store == CHNULL)
+ *list_store = mkchain((char *)cpexpr((expptr)addr), CHNULL);
+ else
+ last->nextp = mkchain((char *)cpexpr((expptr)addr), CHNULL);
+
+} /* add_extern_to_list */
+
+
+ void
+#ifdef KR_headers
+frchain(p)
+ register chainp *p;
+#else
+frchain(register chainp *p)
+#endif
+{
+ register chainp q;
+
+ if(p==0 || *p==0)
+ return;
+
+ for(q = *p; q->nextp ; q = q->nextp)
+ ;
+ q->nextp = chains;
+ chains = *p;
+ *p = 0;
+}
+
+ void
+#ifdef KR_headers
+frexchain(p)
+ register chainp *p;
+#else
+frexchain(register chainp *p)
+#endif
+{
+ register chainp q, r;
+
+ if (q = *p) {
+ for(;;q = r) {
+ frexpr((expptr)q->datap);
+ if (!(r = q->nextp))
+ break;
+ }
+ q->nextp = chains;
+ chains = *p;
+ *p = 0;
+ }
+ }
+
+
+ tagptr
+#ifdef KR_headers
+cpblock(n, p)
+ register int n;
+ register char *p;
+#else
+cpblock(register int n, register char *p)
+#endif
+{
+ register ptr q;
+
+ memcpy((char *)(q = ckalloc(n)), (char *)p, n);
+ return( (tagptr) q);
+}
+
+
+
+ ftnint
+#ifdef KR_headers
+lmax(a, b)
+ ftnint a;
+ ftnint b;
+#else
+lmax(ftnint a, ftnint b)
+#endif
+{
+ return( a>b ? a : b);
+}
+
+ ftnint
+#ifdef KR_headers
+lmin(a, b)
+ ftnint a;
+ ftnint b;
+#else
+lmin(ftnint a, ftnint b)
+#endif
+{
+ return(a < b ? a : b);
+}
+
+
+
+
+#ifdef KR_headers
+maxtype(t1, t2)
+ int t1;
+ int t2;
+#else
+maxtype(int t1, int t2)
+#endif
+{
+ int t;
+
+ t = t1 >= t2 ? t1 : t2;
+ if(t==TYCOMPLEX && (t1==TYDREAL || t2==TYDREAL) )
+ t = TYDCOMPLEX;
+ return(t);
+}
+
+
+
+/* return log base 2 of n if n a power of 2; otherwise -1 */
+ int
+#ifdef KR_headers
+log_2(n)
+ ftnint n;
+#else
+log_2(ftnint n)
+#endif
+{
+ int k;
+
+ /* trick based on binary representation */
+
+ if(n<=0 || (n & (n-1))!=0)
+ return(-1);
+
+ for(k = 0 ; n >>= 1 ; ++k)
+ ;
+ return(k);
+}
+
+
+ void
+frrpl(Void)
+{
+ struct Rplblock *rp;
+
+ while(rpllist)
+ {
+ rp = rpllist->rplnextp;
+ free( (charptr) rpllist);
+ rpllist = rp;
+ }
+}
+
+
+
+/* Call a Fortran function with an arbitrary list of arguments */
+
+int callk_kludge;
+
+ expptr
+#ifdef KR_headers
+callk(type, name, args)
+ int type;
+ char *name;
+ chainp args;
+#else
+callk(int type, char *name, chainp args)
+#endif
+{
+ register expptr p;
+
+ p = mkexpr(OPCALL,
+ (expptr)builtin(callk_kludge ? callk_kludge : type, name, 0),
+ (expptr)args);
+ p->exprblock.vtype = type;
+ return(p);
+}
+
+
+
+ expptr
+#ifdef KR_headers
+call4(type, name, arg1, arg2, arg3, arg4)
+ int type;
+ char *name;
+ expptr arg1;
+ expptr arg2;
+ expptr arg3;
+ expptr arg4;
+#else
+call4(int type, char *name, expptr arg1, expptr arg2, expptr arg3, expptr arg4)
+#endif
+{
+ struct Listblock *args;
+ args = mklist( mkchain((char *)arg1,
+ mkchain((char *)arg2,
+ mkchain((char *)arg3,
+ mkchain((char *)arg4, CHNULL)) ) ) );
+ return( callk(type, name, (chainp)args) );
+}
+
+
+
+
+ expptr
+#ifdef KR_headers
+call3(type, name, arg1, arg2, arg3)
+ int type;
+ char *name;
+ expptr arg1;
+ expptr arg2;
+ expptr arg3;
+#else
+call3(int type, char *name, expptr arg1, expptr arg2, expptr arg3)
+#endif
+{
+ struct Listblock *args;
+ args = mklist( mkchain((char *)arg1,
+ mkchain((char *)arg2,
+ mkchain((char *)arg3, CHNULL) ) ) );
+ return( callk(type, name, (chainp)args) );
+}
+
+
+
+
+
+ expptr
+#ifdef KR_headers
+call2(type, name, arg1, arg2)
+ int type;
+ char *name;
+ expptr arg1;
+ expptr arg2;
+#else
+call2(int type, char *name, expptr arg1, expptr arg2)
+#endif
+{
+ struct Listblock *args;
+
+ args = mklist( mkchain((char *)arg1, mkchain((char *)arg2, CHNULL) ) );
+ return( callk(type,name, (chainp)args) );
+}
+
+
+
+
+ expptr
+#ifdef KR_headers
+call1(type, name, arg)
+ int type;
+ char *name;
+ expptr arg;
+#else
+call1(int type, char *name, expptr arg)
+#endif
+{
+ return( callk(type,name, (chainp)mklist(mkchain((char *)arg,CHNULL)) ));
+}
+
+
+ expptr
+#ifdef KR_headers
+call0(type, name)
+ int type;
+ char *name;
+#else
+call0(int type, char *name)
+#endif
+{
+ return( callk(type, name, CHNULL) );
+}
+
+
+
+ struct Impldoblock *
+#ifdef KR_headers
+mkiodo(dospec, list)
+ chainp dospec;
+ chainp list;
+#else
+mkiodo(chainp dospec, chainp list)
+#endif
+{
+ register struct Impldoblock *q;
+
+ q = ALLOC(Impldoblock);
+ q->tag = TIMPLDO;
+ q->impdospec = dospec;
+ q->datalist = list;
+ return(q);
+}
+
+
+
+
+/* ckalloc -- Allocate 1 memory unit of size n, checking for out of
+ memory error */
+
+ ptr
+#ifdef KR_headers
+ckalloc(n)
+ register int n;
+#else
+ckalloc(register int n)
+#endif
+{
+ register ptr p;
+ p = (ptr)calloc(1, (unsigned) n);
+ if (p || !n)
+ return(p);
+ fprintf(stderr, "failing to get %d bytes\n",n);
+ Fatal("out of memory");
+ /* NOT REACHED */ return 0;
+}
+
+
+ int
+#ifdef KR_headers
+isaddr(p)
+ register expptr p;
+#else
+isaddr(register expptr p)
+#endif
+{
+ if(p->tag == TADDR)
+ return(YES);
+ if(p->tag == TEXPR)
+ switch(p->exprblock.opcode)
+ {
+ case OPCOMMA:
+ return( isaddr(p->exprblock.rightp) );
+
+ case OPASSIGN:
+ case OPASSIGNI:
+ case OPPLUSEQ:
+ case OPMINUSEQ:
+ case OPSLASHEQ:
+ case OPMODEQ:
+ case OPLSHIFTEQ:
+ case OPRSHIFTEQ:
+ case OPBITANDEQ:
+ case OPBITXOREQ:
+ case OPBITOREQ:
+ return( isaddr(p->exprblock.leftp) );
+ }
+ return(NO);
+}
+
+
+
+ int
+#ifdef KR_headers
+isstatic(p)
+ register expptr p;
+#else
+isstatic(register expptr p)
+#endif
+{
+ extern int useauto;
+ if(p->headblock.vleng && !ISCONST(p->headblock.vleng))
+ return(NO);
+
+ switch(p->tag)
+ {
+ case TCONST:
+ return(YES);
+
+ case TADDR:
+ if(ONEOF(p->addrblock.vstg,MSKSTATIC) &&
+ ISCONST(p->addrblock.memoffset) && !useauto)
+ return(YES);
+
+ default:
+ return(NO);
+ }
+}
+
+
+
+/* addressable -- return True iff it is a constant value, or can be
+ referenced by constant values */
+
+ int
+#ifdef KR_headers
+addressable(p)
+ register expptr p;
+#else
+addressable(register expptr p)
+#endif
+{
+ switch(p->tag)
+ {
+ case TCONST:
+ return(YES);
+
+ case TADDR:
+ return( addressable(p->addrblock.memoffset) );
+
+ default:
+ return(NO);
+ }
+}
+
+
+/* isnegative_const -- returns true if the constant is negative. Returns
+ false for imaginary and nonnumeric constants */
+
+ int
+#ifdef KR_headers
+isnegative_const(cp)
+ struct Constblock *cp;
+#else
+isnegative_const(struct Constblock *cp)
+#endif
+{
+ int retval;
+
+ if (cp == NULL)
+ return 0;
+
+ switch (cp -> vtype) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ retval = cp -> Const.ci < 0;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ retval = cp->vstg ? *cp->Const.cds[0] == '-'
+ : cp->Const.cd[0] < 0.0;
+ break;
+ default:
+
+ retval = 0;
+ break;
+ } /* switch */
+
+ return retval;
+} /* isnegative_const */
+
+ void
+#ifdef KR_headers
+negate_const(cp)
+ Constp cp;
+#else
+negate_const(Constp cp)
+#endif
+{
+ if (cp == (struct Constblock *) NULL)
+ return;
+
+ switch (cp -> vtype) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ cp -> Const.ci = - cp -> Const.ci;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if (cp->vstg)
+ switch(*cp->Const.cds[1]) {
+ case '-':
+ ++cp->Const.cds[1];
+ break;
+ case '0':
+ break;
+ default:
+ --cp->Const.cds[1];
+ }
+ else
+ cp->Const.cd[1] = -cp->Const.cd[1];
+ /* no break */
+ case TYREAL:
+ case TYDREAL:
+ if (cp->vstg)
+ switch(*cp->Const.cds[0]) {
+ case '-':
+ ++cp->Const.cds[0];
+ break;
+ case '0':
+ break;
+ default:
+ --cp->Const.cds[0];
+ }
+ else
+ cp->Const.cd[0] = -cp->Const.cd[0];
+ break;
+ case TYCHAR:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+ erri ("negate_const: can't negate type '%d'", cp -> vtype);
+ break;
+ default:
+ erri ("negate_const: bad type '%d'",
+ cp -> vtype);
+ break;
+ } /* switch */
+} /* negate_const */
+
+ void
+#ifdef KR_headers
+ffilecopy(infp, outfp)
+ FILE *infp;
+ FILE *outfp;
+#else
+ffilecopy(FILE *infp, FILE *outfp)
+#endif
+{
+ while (!feof (infp)) {
+ register c = getc (infp);
+ if (!feof (infp))
+ putc (c, outfp);
+ } /* while */
+} /* ffilecopy */
+
+
+/* in_vector -- verifies whether str is in c_keywords.
+ If so, the index is returned else -1 is returned.
+ c_keywords must be in alphabetical order (as defined by strcmp).
+*/
+
+ int
+#ifdef KR_headers
+in_vector(str, keywds, n)
+ char *str;
+ char **keywds;
+ register int n;
+#else
+in_vector(char *str, char **keywds, register int n)
+#endif
+{
+ register char **K = keywds;
+ register int n1, t;
+
+ do {
+ n1 = n >> 1;
+ if (!(t = strcmp(str, K[n1])))
+ return K - keywds + n1;
+ if (t < 0)
+ n = n1;
+ else {
+ n -= ++n1;
+ K += n1;
+ }
+ }
+ while(n > 0);
+
+ return -1;
+ } /* in_vector */
+
+
+ int
+#ifdef KR_headers
+is_negatable(Const)
+ Constp Const;
+#else
+is_negatable(Constp Const)
+#endif
+{
+ int retval = 0;
+ if (Const != (Constp) NULL)
+ switch (Const -> vtype) {
+ case TYINT1:
+ retval = Const -> Const.ci >= -BIGGEST_CHAR;
+ break;
+ case TYSHORT:
+ retval = Const -> Const.ci >= -BIGGEST_SHORT;
+ break;
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ retval = Const -> Const.ci >= -BIGGEST_LONG;
+ break;
+ case TYREAL:
+ case TYDREAL:
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ retval = 1;
+ break;
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+ case TYCHAR:
+ case TYSUBR:
+ default:
+ retval = 0;
+ break;
+ } /* switch */
+
+ return retval;
+} /* is_negatable */
+
+ void
+#ifdef KR_headers
+backup(fname, bname)
+ char *fname;
+ char *bname;
+#else
+backup(char *fname, char *bname)
+#endif
+{
+ FILE *b, *f;
+ static char couldnt[] = "Couldn't open %.80s";
+
+ if (!(f = fopen(fname, binread))) {
+ warn1(couldnt, fname);
+ return;
+ }
+ if (!(b = fopen(bname, binwrite))) {
+ warn1(couldnt, bname);
+ return;
+ }
+ ffilecopy(f, b);
+ fclose(f);
+ fclose(b);
+ }
+
+
+/* struct_eq -- returns YES if structures have the same field names and
+ types, NO otherwise */
+
+ int
+#ifdef KR_headers
+struct_eq(s1, s2)
+ chainp s1;
+ chainp s2;
+#else
+struct_eq(chainp s1, chainp s2)
+#endif
+{
+ struct Dimblock *d1, *d2;
+ Constp cp1, cp2;
+
+ if (s1 == CHNULL && s2 == CHNULL)
+ return YES;
+ for(; s1 && s2; s1 = s1->nextp, s2 = s2->nextp) {
+ register Namep v1 = (Namep) s1 -> datap;
+ register Namep v2 = (Namep) s2 -> datap;
+
+ if (v1 == (Namep) NULL || v1 -> tag != TNAME ||
+ v2 == (Namep) NULL || v2 -> tag != TNAME)
+ return NO;
+
+ if (v1->vtype != v2->vtype || v1->vclass != v2->vclass
+ || strcmp(v1->fvarname, v2->fvarname))
+ return NO;
+
+ /* compare dimensions (needed for comparing COMMON blocks) */
+
+ if (d1 = v1->vdim) {
+ if (!(cp1 = (Constp)d1->nelt) || cp1->tag != TCONST
+ || !(d2 = v2->vdim)
+ || !(cp2 = (Constp)d2->nelt) || cp2->tag != TCONST
+ || cp1->Const.ci != cp2->Const.ci)
+ return NO;
+ }
+ else if (v2->vdim)
+ return NO;
+ } /* while s1 != CHNULL && s2 != CHNULL */
+
+ return s1 == CHNULL && s2 == CHNULL;
+} /* struct_eq */
diff --git a/usr.bin/f2c/names.c b/usr.bin/f2c/names.c
new file mode 100644
index 0000000..ac84be4
--- /dev/null
+++ b/usr.bin/f2c/names.c
@@ -0,0 +1,822 @@
+/****************************************************************
+Copyright 1990, 1992 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "output.h"
+#include "names.h"
+#include "iob.h"
+
+
+/* Names generated by the translator are guaranteed to be unique from the
+ Fortan names because Fortran does not allow underscores in identifiers,
+ and all of the system generated names do have underscores. The various
+ naming conventions are outlined below:
+
+ FORMAT APPLICATION
+ ----------------------------------------------------------------------
+ io_# temporaries generated by IO calls; these will
+ contain the device number (e.g. 5, 6, 0)
+ ret_val function return value, required for complex and
+ character functions.
+ ret_val_len length of the return value in character functions
+
+ ssss_len length of character argument "ssss"
+
+ c_# member of the literal pool, where # is an
+ arbitrary label assigned by the system
+ cs_# short integer constant in the literal pool
+ t_# expression temporary, # is the depth of arguments
+ on the stack.
+ L# label "#", given by user in the Fortran program.
+ This is unique because Fortran labels are numeric
+ pad_# label on an init field required for alignment
+ xxx_init label on a common block union, if a block data
+ requires a separate declaration
+*/
+
+/* generate variable references */
+
+ char *
+#ifdef KR_headers
+c_type_decl(type, is_extern)
+ int type;
+ int is_extern;
+#else
+c_type_decl(int type, int is_extern)
+#endif
+{
+ static char buff[100];
+
+ switch (type) {
+ case TYREAL: if (!is_extern || !forcedouble)
+ { strcpy (buff, "real");break; }
+ case TYDREAL: strcpy (buff, "doublereal"); break;
+ case TYCOMPLEX: if (is_extern)
+ strcpy (buff, "/* Complex */ VOID");
+ else
+ strcpy (buff, "complex");
+ break;
+ case TYDCOMPLEX:if (is_extern)
+ strcpy (buff, "/* Double Complex */ VOID");
+ else
+ strcpy (buff, "doublecomplex");
+ break;
+ case TYADDR:
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL: strcpy(buff, typename[type]);
+ break;
+ case TYCHAR: if (is_extern)
+ strcpy (buff, "/* Character */ VOID");
+ else
+ strcpy (buff, "char");
+ break;
+
+ case TYUNKNOWN: strcpy (buff, "UNKNOWN");
+
+/* If a procedure's type is unknown, assume it's a subroutine */
+
+ if (!is_extern)
+ break;
+
+/* Subroutines must return an INT, because they might return a label
+ value. Even if one doesn't, the caller will EXPECT it to. */
+
+ case TYSUBR: strcpy (buff, "/* Subroutine */ int");
+ break;
+ case TYERROR: strcpy (buff, "ERROR"); break;
+ case TYVOID: strcpy (buff, "void"); break;
+ case TYCILIST: strcpy (buff, "cilist"); break;
+ case TYICILIST: strcpy (buff, "icilist"); break;
+ case TYOLIST: strcpy (buff, "olist"); break;
+ case TYCLLIST: strcpy (buff, "cllist"); break;
+ case TYALIST: strcpy (buff, "alist"); break;
+ case TYINLIST: strcpy (buff, "inlist"); break;
+ case TYFTNLEN: strcpy (buff, "ftnlen"); break;
+ default: sprintf (buff, "BAD DECL '%d'", type);
+ break;
+ } /* switch */
+
+ return buff;
+} /* c_type_decl */
+
+
+ char *
+new_func_length(Void)
+{ return "ret_val_len"; }
+
+ char *
+#ifdef KR_headers
+new_arg_length(arg)
+ Namep arg;
+#else
+new_arg_length(Namep arg)
+#endif
+{
+ static char buf[64];
+ sprintf (buf, "%s_len", arg->fvarname);
+
+ return buf;
+} /* new_arg_length */
+
+
+/* declare_new_addr -- Add a new local variable to the function, given a
+ pointer to an Addrblock structure (which must have the uname_tag set)
+ This list of idents will be printed in reverse (i.e., chronological)
+ order */
+
+ void
+#ifdef KR_headers
+declare_new_addr(addrp)
+ struct Addrblock *addrp;
+#else
+declare_new_addr(struct Addrblock *addrp)
+#endif
+{
+ extern chainp new_vars;
+
+ new_vars = mkchain((char *)cpexpr((expptr)addrp), new_vars);
+} /* declare_new_addr */
+
+
+ void
+#ifdef KR_headers
+wr_nv_ident_help(outfile, addrp)
+ FILE *outfile;
+ struct Addrblock *addrp;
+#else
+wr_nv_ident_help(FILE *outfile, struct Addrblock *addrp)
+#endif
+{
+ int eltcount = 0;
+
+ if (addrp == (struct Addrblock *) NULL)
+ return;
+
+ if (addrp -> isarray) {
+ frexpr (addrp -> memoffset);
+ addrp -> memoffset = ICON(0);
+ eltcount = addrp -> ntempelt;
+ addrp -> ntempelt = 0;
+ addrp -> isarray = 0;
+ } /* if */
+ out_addr (outfile, addrp);
+ if (eltcount)
+ nice_printf (outfile, "[%d]", eltcount);
+} /* wr_nv_ident_help */
+
+ int
+#ifdef KR_headers
+nv_type_help(addrp)
+ struct Addrblock *addrp;
+#else
+nv_type_help(struct Addrblock *addrp)
+#endif
+{
+ if (addrp == (struct Addrblock *) NULL)
+ return -1;
+
+ return addrp -> vtype;
+} /* nv_type_help */
+
+
+/* lit_name -- returns a unique identifier for the given literal. Make
+ the label useful, when possible. For example:
+
+ 1 -> c_1 (constant 1)
+ 2 -> c_2 (constant 2)
+ 1000 -> c_1000 (constant 1000)
+ 1000000 -> c_b<memno> (big constant number)
+ 1.2 -> c_1_2 (constant 1.2)
+ 1.234345 -> c_b<memno> (big constant number)
+ -1 -> c_n1 (constant -1)
+ -1.0 -> c_n1_0 (constant -1.0)
+ .true. -> c_true (constant true)
+ .false. -> c_false (constant false)
+ default -> c_b<memno> (default label)
+*/
+
+ char *
+#ifdef KR_headers
+lit_name(litp)
+ struct Literal *litp;
+#else
+lit_name(struct Literal *litp)
+#endif
+{
+ static char buf[CONST_IDENT_MAX];
+ ftnint val;
+ char *fmt;
+
+ if (litp == (struct Literal *) NULL)
+ return NULL;
+
+ switch (litp -> littype) {
+ case TYINT1:
+ val = litp -> litval.litival;
+ if (val >= 256 || val < -255)
+ sprintf (buf, "ci1_b%d", litp -> litnum);
+ else if (val < 0)
+ sprintf (buf, "ci1_n%ld", -val);
+ else
+ sprintf(buf, "ci1__%ld", val);
+ break;
+ case TYSHORT:
+ val = litp -> litval.litival;
+ if (val >= 32768 || val <= -32769)
+ sprintf (buf, "cs_b%d", litp -> litnum);
+ else if (val < 0)
+ sprintf (buf, "cs_n%ld", -val);
+ else
+ sprintf (buf, "cs__%ld", val);
+ break;
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ val = litp -> litval.litival;
+ if (val >= 100000 || val <= -10000)
+ sprintf (buf, "c_b%d", litp -> litnum);
+ else if (val < 0)
+ sprintf (buf, "c_n%ld", -val);
+ else
+ sprintf (buf, "c__%ld", val);
+ break;
+ case TYLOGICAL1:
+ fmt = "cl1_%s";
+ goto spr_logical;
+ case TYLOGICAL2:
+ fmt = "cl2_%s";
+ goto spr_logical;
+ case TYLOGICAL:
+ fmt = "c_%s";
+ spr_logical:
+ sprintf (buf, fmt, (litp -> litval.litival
+ ? "true" : "false"));
+ break;
+ case TYREAL:
+ case TYDREAL:
+ /* Given a limit of 6 or 8 character on external names, */
+ /* few f.p. values can be meaningfully encoded in the */
+ /* constant name. Just going with the default cb_# */
+ /* seems to be the best course for floating-point */
+ /* constants. */
+ case TYCHAR:
+ /* Shouldn't be any of these */
+ case TYADDR:
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ case TYSUBR:
+ default:
+ sprintf (buf, "c_b%d", litp -> litnum);
+ } /* switch */
+ return buf;
+} /* lit_name */
+
+
+
+ char *
+#ifdef KR_headers
+comm_union_name(count)
+ int count;
+#else
+comm_union_name(int count)
+#endif
+{
+ static char buf[12];
+
+ sprintf(buf, "%d", count);
+ return buf;
+ }
+
+
+
+
+/* wr_globals -- after every function has been translated, we need to
+ output the global declarations, such as the static table of constant
+ values */
+
+ void
+#ifdef KR_headers
+wr_globals(outfile)
+ FILE *outfile;
+#else
+wr_globals(FILE *outfile)
+#endif
+{
+ struct Literal *litp, *lastlit;
+ extern int hsize;
+ char *litname;
+ int did_one, t;
+ struct Constblock cb;
+ ftnint x, y;
+
+ if (nliterals == 0)
+ return;
+
+ lastlit = litpool + nliterals;
+ did_one = 0;
+ for (litp = litpool; litp < lastlit; litp++) {
+ if (!litp->lituse)
+ continue;
+ litname = lit_name(litp);
+ if (!did_one) {
+ margin_printf(outfile, "/* Table of constant values */\n\n");
+ did_one = 1;
+ }
+ cb.vtype = litp->littype;
+ if (litp->littype == TYCHAR) {
+ x = litp->litval.litival2[0] + litp->litval.litival2[1];
+ if (y = x % hsize)
+ x += y = hsize - y;
+ nice_printf(outfile,
+ "static struct { %s fill; char val[%ld+1];", halign, x);
+ nice_printf(outfile, " char fill2[%ld];", hsize - 1);
+ nice_printf(outfile, " } %s_st = { 0,", litname);
+ cb.vleng = ICON(litp->litval.litival2[0]);
+ cb.Const.ccp = litp->cds[0];
+ cb.Const.ccp1.blanks = litp->litval.litival2[1] + y;
+ cb.vtype = TYCHAR;
+ out_const(outfile, &cb);
+ frexpr(cb.vleng);
+ nice_printf(outfile, " };\n");
+ nice_printf(outfile, "#define %s %s_st.val\n", litname, litname);
+ continue;
+ }
+ nice_printf(outfile, "static %s %s = ",
+ c_type_decl(litp->littype,0), litname);
+
+ t = litp->littype;
+ if (ONEOF(t, MSKREAL|MSKCOMPLEX)) {
+ cb.vstg = 1;
+ cb.Const.cds[0] = litp->cds[0];
+ cb.Const.cds[1] = litp->cds[1];
+ }
+ else {
+ memcpy((char *)&cb.Const, (char *)&litp->litval,
+ sizeof(cb.Const));
+ cb.vstg = 0;
+ }
+ out_const(outfile, &cb);
+
+ nice_printf (outfile, ";\n");
+ } /* for */
+ if (did_one)
+ nice_printf (outfile, "\n");
+} /* wr_globals */
+
+ ftnint
+#ifdef KR_headers
+commlen(vl)
+ register chainp vl;
+#else
+commlen(register chainp vl)
+#endif
+{
+ ftnint size;
+ int type;
+ struct Dimblock *t;
+ Namep v;
+
+ while(vl->nextp)
+ vl = vl->nextp;
+ v = (Namep)vl->datap;
+ type = v->vtype;
+ if (type == TYCHAR)
+ size = v->vleng->constblock.Const.ci;
+ else
+ size = typesize[type];
+ if ((t = v->vdim) && ISCONST(t->nelt))
+ size *= t->nelt->constblock.Const.ci;
+ return size + v->voffset;
+ }
+
+ static void /* Pad common block if an EQUIVALENCE extended it. */
+#ifdef KR_headers
+pad_common(c)
+ Extsym *c;
+#else
+pad_common(Extsym *c)
+#endif
+{
+ register chainp cvl;
+ register Namep v;
+ long L = c->maxleng;
+ int type;
+ struct Dimblock *t;
+ int szshort = typesize[TYSHORT];
+
+ for(cvl = c->allextp; cvl; cvl = cvl->nextp)
+ if (commlen((chainp)cvl->datap) >= L)
+ return;
+ v = ALLOC(Nameblock);
+ v->vtype = type = L % szshort ? TYCHAR
+ : type_choice[L/szshort % 4];
+ v->vstg = STGCOMMON;
+ v->vclass = CLVAR;
+ v->tag = TNAME;
+ v->vdim = t = ALLOC(Dimblock);
+ t->ndim = 1;
+ t->dims[0].dimsize = ICON(L / typesize[type]);
+ v->fvarname = v->cvarname = "eqv_pad";
+ if (type == TYCHAR)
+ v->vleng = ICON(1);
+ c->allextp = mkchain((char *)mkchain((char *)v, CHNULL), c->allextp);
+ }
+
+
+/* wr_common_decls -- outputs the common declarations in one of three
+ formats. If all references to a common block look the same (field
+ names and types agree), only one actual declaration will appear.
+ Otherwise, the same block will require many structs. If there is no
+ block data, these structs will be union'ed together (so the linker
+ knows the size of the largest one). If there IS a block data, only
+ that version will be associated with the variable, others will only be
+ defined as types, so the pointer can be cast to it. e.g.
+
+ FORTRAN C
+----------------------------------------------------------------------
+ common /com1/ a, b, c struct { real a, b, c; } com1_;
+
+ common /com1/ a, b, c union {
+ common /com1/ i, j, k struct { real a, b, c; } _1;
+ struct { integer i, j, k; } _2;
+ } com1_;
+
+ common /com1/ a, b, c struct com1_1_ { real a, b, c; };
+ block data struct { integer i, j, k; } com1_ =
+ common /com1/ i, j, k { 1, 2, 3 };
+ data i/1/, j/2/, k/3/
+
+
+ All of these versions will be followed by #defines, since the code in
+ the function bodies can't know ahead of time which of these options
+ will be taken */
+
+/* Macros for deciding the output type */
+
+#define ONE_STRUCT 1
+#define UNION_STRUCT 2
+#define INIT_STRUCT 3
+
+ void
+#ifdef KR_headers
+wr_common_decls(outfile)
+ FILE *outfile;
+#else
+wr_common_decls(FILE *outfile)
+#endif
+{
+ Extsym *ext;
+ extern int extcomm;
+ static char *Extern[4] = {"", "Extern ", "extern "};
+ char *E, *E0 = Extern[extcomm];
+ int did_one = 0;
+
+ for (ext = extsymtab; ext < nextext; ext++) {
+ if (ext -> extstg == STGCOMMON && ext->allextp) {
+ chainp comm;
+ int count = 1;
+ int which; /* which display to use;
+ ONE_STRUCT, UNION or INIT */
+
+ if (!did_one)
+ nice_printf (outfile, "/* Common Block Declarations */\n\n");
+
+ pad_common(ext);
+
+/* Construct the proper, condensed list of structs; eliminate duplicates
+ from the initial list ext -> allextp */
+
+ comm = ext->allextp = revchain(ext->allextp);
+
+ if (ext -> extinit)
+ which = INIT_STRUCT;
+ else if (comm->nextp) {
+ which = UNION_STRUCT;
+ nice_printf (outfile, "%sunion {\n", E0);
+ next_tab (outfile);
+ E = "";
+ }
+ else {
+ which = ONE_STRUCT;
+ E = E0;
+ }
+
+ for (; comm; comm = comm -> nextp, count++) {
+
+ if (which == INIT_STRUCT)
+ nice_printf (outfile, "struct %s%d_ {\n",
+ ext->cextname, count);
+ else
+ nice_printf (outfile, "%sstruct {\n", E);
+
+ next_tab (c_file);
+
+ wr_struct (outfile, (chainp) comm -> datap);
+
+ prev_tab (c_file);
+ if (which == UNION_STRUCT)
+ nice_printf (outfile, "} _%d;\n", count);
+ else if (which == ONE_STRUCT)
+ nice_printf (outfile, "} %s;\n", ext->cextname);
+ else
+ nice_printf (outfile, "};\n");
+ } /* for */
+
+ if (which == UNION_STRUCT) {
+ prev_tab (c_file);
+ nice_printf (outfile, "} %s;\n", ext->cextname);
+ } /* if */
+ did_one = 1;
+ nice_printf (outfile, "\n");
+
+ for (count = 1, comm = ext -> allextp; comm;
+ comm = comm -> nextp, count++) {
+ def_start(outfile, ext->cextname,
+ comm_union_name(count), "");
+ switch (which) {
+ case ONE_STRUCT:
+ extern_out (outfile, ext);
+ break;
+ case UNION_STRUCT:
+ nice_printf (outfile, "(");
+ extern_out (outfile, ext);
+ nice_printf(outfile, "._%d)", count);
+ break;
+ case INIT_STRUCT:
+ nice_printf (outfile, "(*(struct ");
+ extern_out (outfile, ext);
+ nice_printf (outfile, "%d_ *) &", count);
+ extern_out (outfile, ext);
+ nice_printf (outfile, ")");
+ break;
+ } /* switch */
+ nice_printf (outfile, "\n");
+ } /* for count = 1, comm = ext -> allextp */
+ nice_printf (outfile, "\n");
+ } /* if ext -> extstg == STGCOMMON */
+ } /* for ext = extsymtab */
+} /* wr_common_decls */
+
+ void
+#ifdef KR_headers
+wr_struct(outfile, var_list)
+ FILE *outfile;
+ chainp var_list;
+#else
+wr_struct(FILE *outfile, chainp var_list)
+#endif
+{
+ int last_type = -1;
+ int did_one = 0;
+ chainp this_var;
+
+ for (this_var = var_list; this_var; this_var = this_var -> nextp) {
+ Namep var = (Namep) this_var -> datap;
+ int type;
+ char *comment = NULL;
+
+ if (var == (Namep) NULL)
+ err ("wr_struct: null variable");
+ else if (var -> tag != TNAME)
+ erri ("wr_struct: bad tag on variable '%d'",
+ var -> tag);
+
+ type = var -> vtype;
+
+ if (last_type == type && did_one)
+ nice_printf (outfile, ", ");
+ else {
+ if (did_one)
+ nice_printf (outfile, ";\n");
+ nice_printf (outfile, "%s ",
+ c_type_decl (type, var -> vclass == CLPROC));
+ } /* else */
+
+/* Character type is really a string type. Put out a '*' for parameters
+ with unknown length and functions returning character */
+
+ if (var -> vtype == TYCHAR && (!ISICON ((var -> vleng))
+ || var -> vclass == CLPROC))
+ nice_printf (outfile, "*");
+
+ var -> vstg = STGAUTO;
+ out_name (outfile, var);
+ if (var -> vclass == CLPROC)
+ nice_printf (outfile, "()");
+ else if (var -> vdim)
+ comment = wr_ardecls(outfile, var->vdim,
+ var->vtype == TYCHAR && ISICON(var->vleng)
+ ? var->vleng->constblock.Const.ci : 1L);
+ else if (var -> vtype == TYCHAR && var -> vclass != CLPROC &&
+ ISICON ((var -> vleng)))
+ nice_printf (outfile, "[%ld]",
+ var -> vleng -> constblock.Const.ci);
+
+ if (comment)
+ nice_printf (outfile, "%s", comment);
+ did_one = 1;
+ last_type = type;
+ } /* for this_var */
+
+ if (did_one)
+ nice_printf (outfile, ";\n");
+} /* wr_struct */
+
+
+ char *
+#ifdef KR_headers
+user_label(stateno)
+ ftnint stateno;
+#else
+user_label(ftnint stateno)
+#endif
+{
+ static char buf[USER_LABEL_MAX + 1];
+ static char *Lfmt[2] = { "L_%ld", "L%ld" };
+
+ if (stateno >= 0)
+ sprintf(buf, Lfmt[shiftcase], stateno);
+ else
+ sprintf(buf, "L_%s", extsymtab[-1-stateno].fextname);
+ return buf;
+} /* user_label */
+
+
+ char *
+#ifdef KR_headers
+temp_name(starter, num, storage)
+ char *starter;
+ int num;
+ char *storage;
+#else
+temp_name(char *starter, int num, char *storage)
+#endif
+{
+ static char buf[IDENT_LEN];
+ char *pointer = buf;
+ char *prefix = "t";
+
+ if (storage)
+ pointer = storage;
+
+ if (starter && *starter)
+ prefix = starter;
+
+ sprintf (pointer, "%s__%d", prefix, num);
+ return pointer;
+} /* temp_name */
+
+
+ char *
+#ifdef KR_headers
+equiv_name(memno, store)
+ int memno;
+ char *store;
+#else
+equiv_name(int memno, char *store)
+#endif
+{
+ static char buf[IDENT_LEN];
+ char *pointer = buf;
+
+ if (store)
+ pointer = store;
+
+ sprintf (pointer, "%s_%d", EQUIV_INIT_NAME, memno);
+ return pointer;
+} /* equiv_name */
+
+ void
+#ifdef KR_headers
+def_commons(of)
+ FILE *of;
+#else
+def_commons(FILE *of)
+#endif
+{
+ Extsym *ext;
+ int c, onefile, Union;
+ chainp comm;
+ extern int ext1comm;
+ FILE *c_filesave = c_file;
+
+ if (ext1comm == 1) {
+ onefile = 1;
+ c_file = of;
+ fprintf(of, "/*>>>'/dev/null'<<<*/\n\
+#ifdef Define_COMMONs\n\
+/*<<</dev/null>>>*/\n");
+ }
+ else
+ onefile = 0;
+ for(ext = extsymtab; ext < nextext; ext++)
+ if (ext->extstg == STGCOMMON
+ && !ext->extinit && (comm = ext->allextp)) {
+ sprintf(outbtail, "%scom.c", ext->cextname);
+ if (onefile)
+ fprintf(of, "/*>>>'%s'<<<*/\n",
+ outbtail);
+ else {
+ c_file = of = fopen(outbuf,textwrite);
+ if (!of)
+ fatalstr("can't open %s", outbuf);
+ }
+ fprintf(of, "#include \"f2c.h\"\n");
+ if (Ansi == 2)
+ fprintf(of,
+ "\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
+ if (comm->nextp) {
+ Union = 1;
+ nice_printf(of, "union {\n");
+ next_tab(of);
+ }
+ else
+ Union = 0;
+ for(c = 1; comm; comm = comm->nextp) {
+ nice_printf(of, "struct {\n");
+ next_tab(of);
+ wr_struct(of, (chainp)comm->datap);
+ prev_tab(of);
+ if (Union)
+ nice_printf(of, "} _%d;\n", c++);
+ }
+ if (Union)
+ prev_tab(of);
+ nice_printf(of, "} %s;\n", ext->cextname);
+ if (Ansi == 2)
+ fprintf(of,
+ "\n#ifdef __cplusplus\n}\n#endif\n");
+ if (onefile)
+ fprintf(of, "/*<<<%s>>>*/\n", outbtail);
+ else
+ fclose(of);
+ }
+ if (onefile)
+ fprintf(of, "/*>>>'/dev/null'<<<*/\n#endif\n\
+/*<<</dev/null>>>*/\n");
+ c_file = c_filesave;
+ }
+
+/* C Language keywords. Needed to filter unwanted fortran identifiers like
+ * "int", etc. Source: Kernighan & Ritchie, eds. 1 and 2; Stroustrup.
+ * Also includes C++ keywords and types used for I/O in f2c.h .
+ * These keywords must be in alphabetical order (as defined by strcmp()).
+ */
+
+char *c_keywords[] = {
+ "Long", "Multitype", "Namelist", "Vardesc", "abs", "acos",
+ "addr", "address", "aerr", "alist", "asin", "asm", "atan",
+ "atan2", "aunit", "auto", "break", "c", "case", "catch", "cerr",
+ "char", "ciend", "cierr", "cifmt", "cilist", "cirec", "ciunit",
+ "class", "cllist", "complex", "const", "continue", "cos",
+ "cosh", "csta", "cunit", "d", "dabs", "default", "defined",
+ "delete", "dims", "dmax", "dmin", "do", "double",
+ "doublecomplex", "doublereal", "else", "entry", "enum", "exp",
+ "extern", "far", "flag", "float", "for", "friend", "ftnint",
+ "ftnlen", "goto", "h", "huge", "i", "iciend", "icierr",
+ "icifmt", "icilist", "icirlen", "icirnum", "iciunit", "if",
+ "inacc", "inacclen", "inblank", "inblanklen", "include",
+ "indir", "indirlen", "inerr", "inex", "infile", "infilen",
+ "infmt", "infmtlen", "inform", "informlen", "inline", "inlist",
+ "inname", "innamed", "innamlen", "innrec", "innum", "inopen",
+ "inrecl", "inseq", "inseqlen", "int", "integer", "integer1",
+ "inunf", "inunflen", "inunit", "log", "logical", "logical1",
+ "long", "longint", "max", "min", "name", "near", "new", "nvars",
+ "oacc", "oblnk", "oerr", "ofm", "ofnm", "ofnmlen", "olist",
+ "operator", "orl", "osta", "ounit", "overload", "private",
+ "protected", "public", "r", "real", "register", "return",
+ "short", "shortint", "shortlogical", "signed", "sin", "sinh",
+ "sizeof", "sqrt", "static", "struct", "switch", "tan", "tanh",
+ "template", "this", "try", "type", "typedef", "union",
+ "unsigned", "vars", "virtual", "void", "volatile", "while", "z"
+ }; /* c_keywords */
+
+int n_keywords = sizeof(c_keywords)/sizeof(char *);
diff --git a/usr.bin/f2c/names.h b/usr.bin/f2c/names.h
new file mode 100644
index 0000000..16bcc0b
--- /dev/null
+++ b/usr.bin/f2c/names.h
@@ -0,0 +1,19 @@
+#define CONST_IDENT_MAX 30
+#define IO_IDENT_MAX 30
+#define ARGUMENT_MAX 30
+#define USER_LABEL_MAX 30
+
+#define EQUIV_INIT_NAME "equiv"
+
+#define write_nv_ident(fp,a) wr_nv_ident_help ((fp), (struct Addrblock *) (a))
+#define nv_type(x) nv_type_help ((struct Addrblock *) x)
+
+extern char *c_keywords[];
+
+char* c_type_decl Argdcl((int, int));
+void declare_new_addr Argdcl((Addrp));
+char* new_arg_length Argdcl((Namep));
+char* new_func_length Argdcl((void));
+int nv_type_help Argdcl((Addrp));
+char* temp_name Argdcl((char*, int, char*));
+char* user_label Argdcl((long int));
diff --git a/usr.bin/f2c/niceprintf.c b/usr.bin/f2c/niceprintf.c
new file mode 100644
index 0000000..af6d5d0
--- /dev/null
+++ b/usr.bin/f2c/niceprintf.c
@@ -0,0 +1,441 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "names.h"
+#include "output.h"
+#ifndef KR_headers
+#include "stdarg.h"
+#endif
+
+#define TOO_LONG_INDENT (2 * tab_size)
+#define MAX_INDENT 44
+#define MIN_INDENT 22
+static int last_was_newline = 0;
+int sharp_line = 0;
+int indent = 0;
+int in_comment = 0;
+int in_define = 0;
+ extern int gflag1;
+ extern char filename[];
+
+ static void ind_printf Argdcl((int, FILE*, char*, va_list));
+
+ static void
+#ifdef KR_headers
+write_indent(fp, use_indent, extra_indent, start, end)
+ FILE *fp;
+ int use_indent;
+ int extra_indent;
+ char *start;
+ char *end;
+#else
+write_indent(FILE *fp, int use_indent, int extra_indent, char *start, char *end)
+#endif
+{
+ int ind, tab;
+
+ if (sharp_line) {
+ fprintf(fp, "#line %ld \"%s\"\n", lineno, filename);
+ sharp_line = 0;
+ }
+ if (in_define == 1) {
+ in_define = 2;
+ use_indent = 0;
+ }
+ if (last_was_newline && use_indent) {
+ if (*start == '\n') do {
+ putc('\n', fp);
+ if (++start > end)
+ return;
+ }
+ while(*start == '\n');
+
+ ind = indent <= MAX_INDENT
+ ? indent
+ : MIN_INDENT + indent % (MAX_INDENT - MIN_INDENT);
+
+ tab = ind + extra_indent;
+
+ while (tab > 7) {
+ putc ('\t', fp);
+ tab -= 8;
+ } /* while */
+
+ while (tab-- > 0)
+ putc (' ', fp);
+ } /* if last_was_newline */
+
+ while (start <= end)
+ putc (*start++, fp);
+} /* write_indent */
+
+#ifdef KR_headers
+/*VARARGS2*/
+ void
+ margin_printf (fp, a, b, c, d, e, f, g)
+ FILE *fp;
+ char *a;
+ long b, c, d, e, f, g;
+{
+ ind_printf (0, fp, a, b, c, d, e, f, g);
+} /* margin_printf */
+
+/*VARARGS2*/
+ void
+ nice_printf (fp, a, b, c, d, e, f, g)
+ FILE *fp;
+ char *a;
+ long b, c, d, e, f, g;
+{
+ ind_printf (1, fp, a, b, c, d, e, f, g);
+} /* nice_printf */
+#define SPRINTF(x,a,b,c,d,e,f,g) sprintf(x,a,b,c,d,e,f,g)
+
+#else /* if (!defined(KR_HEADERS)) */
+
+#define SPRINTF(x,a,b,c,d,e,f,g) vsprintf(x,a,ap)
+
+ void
+ margin_printf(FILE *fp, char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap,fmt);
+ ind_printf(0, fp, fmt, ap);
+ va_end(ap);
+ }
+
+ void
+ nice_printf(FILE *fp, char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap,fmt);
+ ind_printf(1, fp, fmt, ap);
+ va_end(ap);
+ }
+#endif
+
+#define max_line_len c_output_line_length
+ /* 74Number of characters allowed on an output
+ line. This assumes newlines are handled
+ nicely, i.e. a newline after a full text
+ line on a terminal is ignored */
+
+/* output_buf holds the text of the next line to be printed. It gets
+ flushed when a newline is printed. next_slot points to the next
+ available location in the output buffer, i.e. where the next call to
+ nice_printf will have its output stored */
+
+static char *output_buf;
+static char *next_slot;
+static char *string_start;
+
+static char *word_start = NULL;
+static int cursor_pos = 0;
+static int In_string = 0;
+
+ void
+np_init(Void)
+{
+ next_slot = output_buf = Alloc(MAX_OUTPUT_SIZE);
+ memset(output_buf, 0, MAX_OUTPUT_SIZE);
+ }
+
+ static char *
+#ifdef KR_headers
+adjust_pointer_in_string(pointer)
+ register char *pointer;
+#else
+adjust_pointer_in_string(register char *pointer)
+#endif
+{
+ register char *s, *s1, *se, *s0;
+
+ /* arrange not to break \002 */
+ s1 = string_start ? string_start : output_buf;
+ for(s = s1; s < pointer; s++) {
+ s0 = s1;
+ s1 = s;
+ if (*s == '\\') {
+ se = s++ + 4;
+ if (se > pointer)
+ break;
+ if (*s < '0' || *s > '7')
+ continue;
+ while(++s < se)
+ if (*s < '0' || *s > '7')
+ break;
+ --s;
+ }
+ }
+ return s0 - 1;
+ }
+
+/* ANSI says strcpy's behavior is undefined for overlapping args,
+ * so we roll our own fwd_strcpy: */
+
+ static void
+#ifdef KR_headers
+fwd_strcpy(t, s)
+ register char *t;
+ register char *s;
+#else
+fwd_strcpy(register char *t, register char *s)
+#endif
+{ while(*t++ = *s++); }
+
+/* isident -- true iff character could belong to a unit. C allows
+ letters, numbers and underscores in identifiers. This also doubles as
+ a check for numeric constants, since we include the decimal point and
+ minus sign. The minus has to be here, since the constant "10e-2"
+ cannot be broken up. The '.' also prevents structure references from
+ being broken, which is a quite acceptable side effect */
+
+#define isident(x) (Tr[x] & 1)
+#define isntident(x) (!Tr[x])
+
+ static void
+#ifdef KR_headers
+ ind_printf (use_indent, fp, a, b, c, d, e, f, g)
+ int use_indent;
+ FILE *fp;
+ char *a;
+ long b, c, d, e, f, g;
+#else
+ ind_printf (int use_indent, FILE *fp, char *a, va_list ap)
+#endif
+{
+ extern int max_line_len;
+ extern FILEP c_file;
+ extern char tr_tab[]; /* in output.c */
+ register char *Tr = tr_tab;
+ int ch, inc, ind;
+ static int extra_indent, last_indent, set_cursor = 1;
+
+ cursor_pos += indent - last_indent;
+ last_indent = indent;
+ SPRINTF (next_slot, a, b, c, d, e, f, g);
+
+ if (fp != c_file) {
+ fprintf (fp,"%s", next_slot);
+ return;
+ } /* if fp != c_file */
+
+ do {
+ char *pointer;
+
+/* The for loop will parse one output line */
+
+ if (set_cursor) {
+ ind = indent <= MAX_INDENT
+ ? indent
+ : MIN_INDENT + indent % (MAX_INDENT - MIN_INDENT);
+ cursor_pos = ind + extra_indent;
+ set_cursor = 0;
+ }
+ if (in_comment)
+ for (pointer = next_slot; *pointer && *pointer != '\n' &&
+ cursor_pos <= max_line_len; pointer++)
+ cursor_pos++;
+ else
+ for (pointer = next_slot; *pointer && *pointer != '\n' &&
+ cursor_pos <= max_line_len; pointer++) {
+
+ /* Update state variables here */
+
+ if (In_string) {
+ switch(*pointer) {
+ case '\\':
+ if (++cursor_pos > max_line_len) {
+ cursor_pos -= 2;
+ --pointer;
+ goto overflow;
+ }
+ ++pointer;
+ break;
+ case '"':
+ In_string = 0;
+ word_start = 0;
+ }
+ }
+ else switch (*pointer) {
+ case '"':
+ if (cursor_pos + 5 > max_line_len) {
+ word_start = 0;
+ --pointer;
+ goto overflow;
+ }
+ In_string = 1;
+ string_start = word_start = pointer;
+ break;
+ case '\'':
+ if (pointer[1] == '\\')
+ if ((ch = pointer[2]) >= '0' && ch <= '7')
+ for(inc = 3; pointer[inc] != '\''
+ && ++inc < 5;);
+ else
+ inc = 3;
+ else
+ inc = 2;
+ /*debug*/ if (pointer[inc] != '\'')
+ /*debug*/ fatalstr("Bad character constant %.10s",
+ pointer);
+ if ((cursor_pos += inc) > max_line_len) {
+ cursor_pos -= inc;
+ word_start = 0;
+ --pointer;
+ goto overflow;
+ }
+ word_start = pointer;
+ pointer += inc;
+ break;
+ case '\t':
+ cursor_pos = 8 * ((cursor_pos + 8) / 8) - 1;
+ break;
+ default: {
+
+/* HACK Assumes that all characters in an atomic C token will be written
+ at the same time. Must check for tokens first, since '-' is considered
+ part of an identifier; checking isident first would mean breaking up "->" */
+
+ if (word_start) {
+ if (isntident(*(unsigned char *)pointer))
+ word_start = NULL;
+ }
+ else if (isident(*(unsigned char *)pointer))
+ word_start = pointer;
+ break;
+ } /* default */
+ } /* switch */
+ cursor_pos++;
+ } /* for pointer = next_slot */
+ overflow:
+ if (*pointer == '\0') {
+
+/* The output line is not complete, so break out and don't output
+ anything. The current line fragment will be stored in the buffer */
+
+ next_slot = pointer;
+ break;
+ } else {
+ char last_char;
+ int in_string0 = In_string;
+
+/* If the line was too long, move pointer back to the character before
+ the current word. This allows line breaking on word boundaries. Make
+ sure that 80 character comment lines get broken up somehow. We assume
+ that any non-string 80 character identifier must be in a comment.
+*/
+
+ if (*pointer == '\n')
+ in_define = 0;
+ else if (word_start && word_start > output_buf)
+ if (In_string)
+ if (string_start && pointer - string_start < 5)
+ pointer = string_start - 1;
+ else {
+ pointer = adjust_pointer_in_string(pointer);
+ string_start = 0;
+ }
+ else if (word_start == string_start
+ && pointer - string_start >= 5) {
+ pointer = adjust_pointer_in_string(next_slot);
+ In_string = 1;
+ string_start = 0;
+ }
+ else
+ pointer = word_start - 1;
+ else if (cursor_pos > max_line_len) {
+#ifndef ANSI_Libraries
+ extern char *strchr();
+#endif
+ if (In_string) {
+ pointer = adjust_pointer_in_string(pointer);
+ if (string_start && pointer > string_start)
+ string_start = 0;
+ }
+ else if (strchr("&*+-/<=>|", *pointer)
+ && strchr("!%&*+-/<=>^|", pointer[-1])) {
+ pointer -= 2;
+ if (strchr("<>", *pointer)) /* <<=, >>= */
+ pointer--;
+ }
+ else {
+ if (word_start)
+ while(isident(*(unsigned char *)pointer))
+ pointer++;
+ pointer--;
+ }
+ }
+ last_char = *pointer;
+ write_indent(fp, use_indent, extra_indent, output_buf, pointer);
+ next_slot = output_buf;
+ if (In_string && !string_start && Ansi == 1 && last_char != '\n')
+ *next_slot++ = '"';
+ fwd_strcpy(next_slot, pointer + 1);
+
+/* insert a line break */
+
+ if (last_char == '\n') {
+ if (In_string)
+ last_was_newline = 0;
+ else {
+ last_was_newline = 1;
+ extra_indent = 0;
+ sharp_line = gflag1;
+ }
+ }
+ else {
+ extra_indent = TOO_LONG_INDENT;
+ if (In_string && !string_start) {
+ if (Ansi == 1) {
+ fprintf(fp, gflag1 ? "\"\\\n" : "\"\n");
+ use_indent = 1;
+ last_was_newline = 1;
+ }
+ else {
+ fprintf(fp, "\\\n");
+ last_was_newline = 0;
+ }
+ In_string = in_string0;
+ }
+ else {
+ if (in_define/* | gflag1*/)
+ putc('\\', fp);
+ putc ('\n', fp);
+ last_was_newline = 1;
+ }
+ } /* if *pointer != '\n' */
+
+ if (In_string && Ansi != 1 && !string_start)
+ cursor_pos = 0;
+ else
+ set_cursor = 1;
+
+ string_start = word_start = NULL;
+
+ } /* else */
+
+ } while (*next_slot);
+
+} /* ind_printf */
diff --git a/usr.bin/f2c/niceprintf.h b/usr.bin/f2c/niceprintf.h
new file mode 100644
index 0000000..24c65d4
--- /dev/null
+++ b/usr.bin/f2c/niceprintf.h
@@ -0,0 +1,16 @@
+/* niceprintf.h -- contains constants and macros from the output filter
+ for the generated C code. We use macros for increased speed, less
+ function overhead. */
+
+#define MAX_OUTPUT_SIZE 6000 /* Number of chars on one output line PLUS
+ the length of the longest string
+ printed using nice_printf */
+
+
+
+#define next_tab(fp) (indent += tab_size)
+
+#define prev_tab(fp) (indent -= tab_size)
+
+
+
diff --git a/usr.bin/f2c/notice b/usr.bin/f2c/notice
new file mode 100644
index 0000000..9715a19
--- /dev/null
+++ b/usr.bin/f2c/notice
@@ -0,0 +1,23 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
diff --git a/usr.bin/f2c/output.c b/usr.bin/f2c/output.c
new file mode 100644
index 0000000..b495b26
--- /dev/null
+++ b/usr.bin/f2c/output.c
@@ -0,0 +1,1670 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "names.h"
+#include "output.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+char _assoc_table[] = { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 };
+
+/* Opcode table -- This array is indexed by the OP_____ macros defined in
+ defines.h; these macros are expected to be adjacent integers, so that
+ this table is as small as possible. */
+
+table_entry opcode_table[] = {
+ { 0, 0, NULL },
+ /* OPPLUS 1 */ { BINARY_OP, 12, "%l + %r" },
+ /* OPMINUS 2 */ { BINARY_OP, 12, "%l - %r" },
+ /* OPSTAR 3 */ { BINARY_OP, 13, "%l * %r" },
+ /* OPSLASH 4 */ { BINARY_OP, 13, "%l / %r" },
+ /* OPPOWER 5 */ { BINARY_OP, 0, "power (%l, %r)" },
+ /* OPNEG 6 */ { UNARY_OP, 14, "-%l" },
+ /* OPOR 7 */ { BINARY_OP, 4, "%l || %r" },
+ /* OPAND 8 */ { BINARY_OP, 5, "%l && %r" },
+ /* OPEQV 9 */ { BINARY_OP, 9, "%l == %r" },
+ /* OPNEQV 10 */ { BINARY_OP, 9, "%l != %r" },
+ /* OPNOT 11 */ { UNARY_OP, 14, "! %l" },
+ /* OPCONCAT 12 */ { BINARY_OP, 0, "concat (%l, %r)" },
+ /* OPLT 13 */ { BINARY_OP, 10, "%l < %r" },
+ /* OPEQ 14 */ { BINARY_OP, 9, "%l == %r" },
+ /* OPGT 15 */ { BINARY_OP, 10, "%l > %r" },
+ /* OPLE 16 */ { BINARY_OP, 10, "%l <= %r" },
+ /* OPNE 17 */ { BINARY_OP, 9, "%l != %r" },
+ /* OPGE 18 */ { BINARY_OP, 10, "%l >= %r" },
+ /* OPCALL 19 */ { BINARY_OP, 15, SPECIAL_FMT },
+ /* OPCCALL 20 */ { BINARY_OP, 15, SPECIAL_FMT },
+
+/* Left hand side of an assignment cannot have outermost parens */
+
+ /* OPASSIGN 21 */ { BINARY_OP, 2, "%l = %r" },
+ /* OPPLUSEQ 22 */ { BINARY_OP, 2, "%l += %r" },
+ /* OPSTAREQ 23 */ { BINARY_OP, 2, "%l *= %r" },
+ /* OPCONV 24 */ { BINARY_OP, 14, "%l" },
+ /* OPLSHIFT 25 */ { BINARY_OP, 11, "%l << %r" },
+ /* OPMOD 26 */ { BINARY_OP, 13, "%l %% %r" },
+ /* OPCOMMA 27 */ { BINARY_OP, 1, "%l, %r" },
+
+/* Don't want to nest the colon operator in parens */
+
+ /* OPQUEST 28 */ { BINARY_OP, 3, "%l ? %r" },
+ /* OPCOLON 29 */ { BINARY_OP, 3, "%l : %r" },
+ /* OPABS 30 */ { UNARY_OP, 0, "abs(%l)" },
+ /* OPMIN 31 */ { BINARY_OP, 0, SPECIAL_FMT },
+ /* OPMAX 32 */ { BINARY_OP, 0, SPECIAL_FMT },
+ /* OPADDR 33 */ { UNARY_OP, 14, "&%l" },
+
+ /* OPCOMMA_ARG 34 */ { BINARY_OP, 15, SPECIAL_FMT },
+ /* OPBITOR 35 */ { BINARY_OP, 6, "%l | %r" },
+ /* OPBITAND 36 */ { BINARY_OP, 8, "%l & %r" },
+ /* OPBITXOR 37 */ { BINARY_OP, 7, "%l ^ %r" },
+ /* OPBITNOT 38 */ { UNARY_OP, 14, "~ %l" },
+ /* OPRSHIFT 39 */ { BINARY_OP, 11, "%l >> %r" },
+
+/* This isn't quite right -- it doesn't handle arrays, for instance */
+
+ /* OPWHATSIN 40 */ { UNARY_OP, 14, "*%l" },
+ /* OPMINUSEQ 41 */ { BINARY_OP, 2, "%l -= %r" },
+ /* OPSLASHEQ 42 */ { BINARY_OP, 2, "%l /= %r" },
+ /* OPMODEQ 43 */ { BINARY_OP, 2, "%l %%= %r" },
+ /* OPLSHIFTEQ 44 */ { BINARY_OP, 2, "%l <<= %r" },
+ /* OPRSHIFTEQ 45 */ { BINARY_OP, 2, "%l >>= %r" },
+ /* OPBITANDEQ 46 */ { BINARY_OP, 2, "%l &= %r" },
+ /* OPBITXOREQ 47 */ { BINARY_OP, 2, "%l ^= %r" },
+ /* OPBITOREQ 48 */ { BINARY_OP, 2, "%l |= %r" },
+ /* OPPREINC 49 */ { UNARY_OP, 14, "++%l" },
+ /* OPPREDEC 50 */ { UNARY_OP, 14, "--%l" },
+ /* OPDOT 51 */ { BINARY_OP, 15, "%l.%r" },
+ /* OPARROW 52 */ { BINARY_OP, 15, "%l -> %r"},
+ /* OPNEG1 53 */ { UNARY_OP, 14, "-%l" },
+ /* OPDMIN 54 */ { BINARY_OP, 0, "dmin(%l,%r)" },
+ /* OPDMAX 55 */ { BINARY_OP, 0, "dmax(%l,%r)" },
+ /* OPASSIGNI 56 */ { BINARY_OP, 2, "%l = &%r" },
+ /* OPIDENTITY 57 */ { UNARY_OP, 15, "%l" },
+ /* OPCHARCAST 58 */ { UNARY_OP, 14, "(char *)&%l" },
+ /* OPDABS 59 */ { UNARY_OP, 0, "dabs(%l)" },
+ /* OPMIN2 60 */ { BINARY_OP, 0, "min(%l,%r)" },
+ /* OPMAX2 61 */ { BINARY_OP, 0, "max(%l,%r)" },
+
+/* kludge to imitate (under forcedouble) f77's bizarre treatement of OPNEG... */
+
+ /* OPNEG KLUDGE */ { UNARY_OP, 14, "-(doublereal)%l" }
+}; /* opcode_table */
+
+#define OPNEG_KLUDGE (sizeof(opcode_table)/sizeof(table_entry) - 1)
+
+static char opeqable[sizeof(opcode_table)/sizeof(table_entry)];
+
+
+static void output_arg_list Argdcl((FILEP, struct Listblock*));
+static void output_binary Argdcl((FILEP, Exprp));
+static void output_list Argdcl((FILEP, struct Listblock*));
+static void output_literal Argdcl((FILEP, int, Constp));
+static void output_prim Argdcl((FILEP, struct Primblock*));
+static void output_unary Argdcl((FILEP, Exprp));
+
+
+ void
+#ifdef KR_headers
+expr_out(fp, e)
+ FILE *fp;
+ expptr e;
+#else
+expr_out(FILE *fp, expptr e)
+#endif
+{
+ if (e == (expptr) NULL)
+ return;
+
+ switch (e -> tag) {
+ case TNAME: out_name (fp, (struct Nameblock *) e);
+ return;
+
+ case TCONST: out_const(fp, &e->constblock);
+ goto end_out;
+ case TEXPR:
+ break;
+
+ case TADDR: out_addr (fp, &(e -> addrblock));
+ goto end_out;
+
+ case TPRIM: if (!nerr)
+ warn ("expr_out: got TPRIM");
+ output_prim (fp, &(e -> primblock));
+ return;
+
+ case TLIST: output_list (fp, &(e -> listblock));
+ end_out: frexpr(e);
+ return;
+
+ case TIMPLDO: err ("expr_out: got TIMPLDO");
+ return;
+
+ case TERROR:
+ default:
+ erri ("expr_out: bad tag '%d'", e -> tag);
+ } /* switch */
+
+/* Now we know that the tag is TEXPR */
+
+/* Optimize on simple expressions, such as "a = a + b" ==> "a += b" */
+
+ if (e -> exprblock.opcode == OPASSIGN && e -> exprblock.rightp &&
+ e -> exprblock.rightp -> tag == TEXPR) {
+ int opcode;
+
+ opcode = e -> exprblock.rightp -> exprblock.opcode;
+
+ if (opeqable[opcode]) {
+ expptr leftp, rightp;
+
+ if ((leftp = e -> exprblock.leftp) &&
+ (rightp = e -> exprblock.rightp -> exprblock.leftp)) {
+
+ if (same_ident (leftp, rightp)) {
+ expptr temp = e -> exprblock.rightp;
+
+ e -> exprblock.opcode = op_assign(opcode);
+
+ e -> exprblock.rightp = temp -> exprblock.rightp;
+ temp->exprblock.rightp = 0;
+ frexpr(temp);
+ } /* if same_ident (leftp, rightp) */
+ } /* if leftp && rightp */
+ } /* if opcode == OPPLUS || */
+ } /* if e -> exprblock.opcode == OPASSIGN */
+
+
+/* Optimize on increment or decrement by 1 */
+
+ {
+ int opcode = e -> exprblock.opcode;
+ expptr leftp = e -> exprblock.leftp;
+ expptr rightp = e -> exprblock.rightp;
+
+ if (leftp && rightp && (leftp -> headblock.vstg == STGARG ||
+ ISINT (leftp -> headblock.vtype)) &&
+ (opcode == OPPLUSEQ || opcode == OPMINUSEQ) &&
+ ISINT (rightp -> headblock.vtype) &&
+ ISICON (e -> exprblock.rightp) &&
+ (ISONE (e -> exprblock.rightp) ||
+ e -> exprblock.rightp -> constblock.Const.ci == -1)) {
+
+/* Allow for the '-1' constant value */
+
+ if (!ISONE (e -> exprblock.rightp))
+ opcode = (opcode == OPPLUSEQ) ? OPMINUSEQ : OPPLUSEQ;
+
+/* replace the existing opcode */
+
+ if (opcode == OPPLUSEQ)
+ e -> exprblock.opcode = OPPREINC;
+ else
+ e -> exprblock.opcode = OPPREDEC;
+
+/* Free up storage used by the right hand side */
+
+ frexpr (e -> exprblock.rightp);
+ e->exprblock.rightp = 0;
+ } /* if opcode == OPPLUS */
+ } /* block */
+
+
+ if (is_unary_op (e -> exprblock.opcode))
+ output_unary (fp, &(e -> exprblock));
+ else if (is_binary_op (e -> exprblock.opcode))
+ output_binary (fp, &(e -> exprblock));
+ else
+ erri ("expr_out: bad opcode '%d'", (int) e -> exprblock.opcode);
+
+ free((char *)e);
+
+} /* expr_out */
+
+
+ void
+#ifdef KR_headers
+out_and_free_statement(outfile, expr)
+ FILE *outfile;
+ expptr expr;
+#else
+out_and_free_statement(FILE *outfile, expptr expr)
+#endif
+{
+ if (expr)
+ expr_out (outfile, expr);
+
+ nice_printf (outfile, ";\n");
+} /* out_and_free_statement */
+
+
+
+ int
+#ifdef KR_headers
+same_ident(left, right)
+ expptr left;
+ expptr right;
+#else
+same_ident(expptr left, expptr right)
+#endif
+{
+ if (!left || !right)
+ return 0;
+
+ if (left -> tag == TNAME && right -> tag == TNAME && left == right)
+ return 1;
+
+ if (left -> tag == TADDR && right -> tag == TADDR &&
+ left -> addrblock.uname_tag == right -> addrblock.uname_tag)
+ switch (left -> addrblock.uname_tag) {
+ case UNAM_REF:
+ case UNAM_NAME:
+
+/* Check for array subscripts */
+
+ if (left -> addrblock.user.name -> vdim ||
+ right -> addrblock.user.name -> vdim)
+ if (left -> addrblock.user.name !=
+ right -> addrblock.user.name ||
+ !same_expr (left -> addrblock.memoffset,
+ right -> addrblock.memoffset))
+ return 0;
+
+ return same_ident ((expptr) (left -> addrblock.user.name),
+ (expptr) right -> addrblock.user.name);
+ case UNAM_IDENT:
+ return strcmp(left->addrblock.user.ident,
+ right->addrblock.user.ident) == 0;
+ case UNAM_CHARP:
+ return strcmp(left->addrblock.user.Charp,
+ right->addrblock.user.Charp) == 0;
+ default:
+ return 0;
+ } /* switch */
+
+ if (left->tag == TEXPR && left->exprblock.opcode == OPWHATSIN
+ && right->tag == TEXPR && right->exprblock.opcode == OPWHATSIN)
+ return same_ident(left->exprblock.leftp,
+ right->exprblock.leftp);
+
+ return 0;
+} /* same_ident */
+
+ static int
+#ifdef KR_headers
+samefpconst(c1, c2, n)
+ register Constp c1;
+ register Constp c2;
+ register int n;
+#else
+samefpconst(register Constp c1, register Constp c2, register int n)
+#endif
+{
+ char *s1, *s2;
+ if (!c1->vstg && !c2->vstg)
+ return c1->Const.cd[n] == c2->Const.cd[n];
+ s1 = c1->vstg ? c1->Const.cds[n] : dtos(c1->Const.cd[n]);
+ s2 = c2->vstg ? c2->Const.cds[n] : dtos(c2->Const.cd[n]);
+ return !strcmp(s1, s2);
+ }
+
+ static int
+#ifdef KR_headers
+sameconst(c1, c2)
+ register Constp c1;
+ register Constp c2;
+#else
+sameconst(register Constp c1, register Constp c2)
+#endif
+{
+ switch(c1->vtype) {
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if (!samefpconst(c1,c2,1))
+ return 0;
+ case TYREAL:
+ case TYDREAL:
+ return samefpconst(c1,c2,0);
+ case TYCHAR:
+ return c1->Const.ccp1.blanks == c2->Const.ccp1.blanks
+ && c1->vleng->constblock.Const.ci
+ == c2->vleng->constblock.Const.ci
+ && !memcmp(c1->Const.ccp, c2->Const.ccp,
+ (int)c1->vleng->constblock.Const.ci);
+ case TYSHORT:
+ case TYINT:
+ case TYLOGICAL:
+ return c1->Const.ci == c2->Const.ci;
+ }
+ err("unexpected type in sameconst");
+ return 0;
+ }
+
+/* same_expr -- Returns true only if e1 and e2 match. This is
+ somewhat pessimistic, but can afford to be because it's just used to
+ optimize on the assignment operators (+=, -=, etc). */
+
+ int
+#ifdef KR_headers
+same_expr(e1, e2)
+ expptr e1;
+ expptr e2;
+#else
+same_expr(expptr e1, expptr e2)
+#endif
+{
+ if (!e1 || !e2)
+ return !e1 && !e2;
+
+ if (e1 -> tag != e2 -> tag || e1 -> headblock.vtype != e2 -> headblock.vtype)
+ return 0;
+
+ switch (e1 -> tag) {
+ case TEXPR:
+ if (e1 -> exprblock.opcode != e2 -> exprblock.opcode)
+ return 0;
+
+ return same_expr (e1 -> exprblock.leftp, e2 -> exprblock.leftp) &&
+ same_expr (e1 -> exprblock.rightp, e2 -> exprblock.rightp);
+ case TNAME:
+ case TADDR:
+ return same_ident (e1, e2);
+ case TCONST:
+ return sameconst(&e1->constblock, &e2->constblock);
+ default:
+ return 0;
+ } /* switch */
+} /* same_expr */
+
+
+
+ void
+#ifdef KR_headers
+out_name(fp, namep)
+ FILE *fp;
+ Namep namep;
+#else
+out_name(FILE *fp, Namep namep)
+#endif
+{
+ extern int usedefsforcommon;
+ Extsym *comm;
+
+ if (namep == NULL)
+ return;
+
+/* DON'T want to use oneof_stg() here; need to find the right common name
+ */
+
+ if (namep->vstg == STGCOMMON && !namep->vcommequiv && !usedefsforcommon) {
+ comm = &extsymtab[namep->vardesc.varno];
+ extern_out(fp, comm);
+ nice_printf(fp, "%d.", comm->curno);
+ } /* if namep -> vstg == STGCOMMON */
+
+ if (namep->vprocclass == PTHISPROC && namep->vtype != TYSUBR)
+ nice_printf(fp, xretslot[namep->vtype]->user.ident);
+ else
+ nice_printf (fp, "%s", namep->cvarname);
+} /* out_name */
+
+
+static char *Longfmt = "%ld";
+
+#define cpd(n) cp->vstg ? cp->Const.cds[n] : dtos(cp->Const.cd[n])
+
+ void
+#ifdef KR_headers
+out_const(fp, cp)
+ FILE *fp;
+ register Constp cp;
+#else
+out_const(FILE *fp, register Constp cp)
+#endif
+{
+ static char real_buf[50], imag_buf[50];
+ unsigned int k;
+ int type = cp->vtype;
+
+ switch (type) {
+ case TYINT1:
+ case TYSHORT:
+ nice_printf (fp, "%ld", cp->Const.ci); /* don't cast ci! */
+ break;
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ nice_printf (fp, Longfmt, cp->Const.ci); /* don't cast ci! */
+ break;
+ case TYREAL:
+ nice_printf(fp, "%s", flconst(real_buf, cpd(0)));
+ break;
+ case TYDREAL:
+ nice_printf(fp, "%s", cpd(0));
+ break;
+ case TYCOMPLEX:
+ nice_printf(fp, cm_fmt_string, flconst(real_buf, cpd(0)),
+ flconst(imag_buf, cpd(1)));
+ break;
+ case TYDCOMPLEX:
+ nice_printf(fp, dcm_fmt_string, cpd(0), cpd(1));
+ break;
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+ nice_printf (fp, "%s", cp->Const.ci ? "TRUE_" : "FALSE_");
+ break;
+ case TYCHAR: {
+ char *c = cp->Const.ccp, *ce;
+
+ if (c == NULL) {
+ nice_printf (fp, "\"\"");
+ break;
+ } /* if c == NULL */
+
+ nice_printf (fp, "\"");
+ ce = c + cp->vleng->constblock.Const.ci;
+ while(c < ce) {
+ k = *(unsigned char *)c++;
+ nice_printf(fp, str_fmt[k], k);
+ }
+ for(k = cp->Const.ccp1.blanks; k > 0; k--)
+ nice_printf(fp, " ");
+ nice_printf (fp, "\"");
+ break;
+ } /* case TYCHAR */
+ default:
+ erri ("out_const: bad type '%d'", (int) type);
+ break;
+ } /* switch */
+
+} /* out_const */
+#undef cpd
+
+ static void
+#ifdef KR_headers
+out_args(fp, ep)
+ FILE *fp;
+ expptr ep;
+#else
+out_args(FILE *fp, expptr ep)
+#endif
+{
+ chainp arglist;
+
+ if(ep->tag != TLIST)
+ badtag("out_args", ep->tag);
+ for(arglist = ep->listblock.listp;;) {
+ expr_out(fp, (expptr)arglist->datap);
+ arglist->datap = 0;
+ if (!(arglist = arglist->nextp))
+ break;
+ nice_printf(fp, ", ");
+ }
+ }
+
+
+/* out_addr -- this routine isn't local because it is called by the
+ system-generated identifier printing routines */
+
+ void
+#ifdef KR_headers
+out_addr(fp, addrp)
+ FILE *fp;
+ struct Addrblock *addrp;
+#else
+out_addr(FILE *fp, struct Addrblock *addrp)
+#endif
+{
+ extern Extsym *extsymtab;
+ int was_array = 0;
+ char *s;
+
+
+ if (addrp == NULL)
+ return;
+ if (doin_setbound
+ && addrp->vstg == STGARG
+ && addrp->vtype != TYCHAR
+ && ISICON(addrp->memoffset)
+ && !addrp->memoffset->constblock.Const.ci)
+ nice_printf(fp, "*");
+
+ switch (addrp -> uname_tag) {
+ case UNAM_REF:
+ nice_printf(fp, "%s_%s(", addrp->user.name->cvarname,
+ addrp->cmplx_sub ? "subscr" : "ref");
+ out_args(fp, addrp->memoffset);
+ nice_printf(fp, ")");
+ return;
+ case UNAM_NAME:
+ out_name (fp, addrp -> user.name);
+ break;
+ case UNAM_IDENT:
+ if (*(s = addrp->user.ident) == ' ') {
+ if (multitype)
+ nice_printf(fp, "%s",
+ xretslot[addrp->vtype]->user.ident);
+ else
+ nice_printf(fp, "%s", s+1);
+ }
+ else {
+ nice_printf(fp, "%s", s);
+ }
+ break;
+ case UNAM_CHARP:
+ nice_printf(fp, "%s", addrp->user.Charp);
+ break;
+ case UNAM_EXTERN:
+ extern_out (fp, &extsymtab[addrp -> memno]);
+ break;
+ case UNAM_CONST:
+ switch(addrp->vstg) {
+ case STGCONST:
+ out_const(fp, (Constp)addrp);
+ break;
+ case STGMEMNO:
+ output_literal (fp, (int)addrp->memno,
+ (Constp)addrp);
+ break;
+ default:
+ Fatal("unexpected vstg in out_addr");
+ }
+ break;
+ case UNAM_UNKNOWN:
+ default:
+ nice_printf (fp, "Unknown Addrp");
+ break;
+ } /* switch */
+
+/* It's okay to just throw in the brackets here because they have a
+ precedence level of 15, the highest value. */
+
+ if ((addrp->uname_tag == UNAM_NAME && addrp->user.name->vdim
+ || addrp->ntempelt > 1 || addrp->isarray)
+ && addrp->vtype != TYCHAR) {
+ expptr offset;
+
+ was_array = 1;
+
+ offset = addrp -> memoffset;
+ addrp->memoffset = 0;
+ if (ONEOF(addrp->vstg, M(STGCOMMON)|M(STGEQUIV))
+ && addrp -> uname_tag == UNAM_NAME
+ && !addrp->skip_offset)
+ offset = mkexpr (OPMINUS, offset, mkintcon (
+ addrp -> user.name -> voffset));
+
+ nice_printf (fp, "[");
+
+ offset = mkexpr (OPSLASH, offset,
+ ICON (typesize[addrp -> vtype] * (addrp -> Field ? 2 : 1)));
+ expr_out (fp, offset);
+ nice_printf (fp, "]");
+ }
+
+/* Check for structure field reference */
+
+ if (addrp -> Field && addrp -> uname_tag != UNAM_CONST &&
+ addrp -> uname_tag != UNAM_UNKNOWN) {
+ if (oneof_stg((addrp -> uname_tag == UNAM_NAME ? addrp -> user.name :
+ (Namep) NULL), addrp -> vstg, M(STGARG)|M(STGEQUIV))
+ && !was_array && (addrp->vclass != CLPROC || !multitype))
+ nice_printf (fp, "->%s", addrp -> Field);
+ else
+ nice_printf (fp, ".%s", addrp -> Field);
+ } /* if */
+
+/* Check for character subscripting */
+
+ if (addrp->vtype == TYCHAR &&
+ (addrp->vclass != CLPROC || addrp->uname_tag == UNAM_NAME
+ && addrp->user.name->vprocclass == PTHISPROC) &&
+ addrp -> memoffset &&
+ (addrp -> uname_tag != UNAM_NAME ||
+ addrp -> user.name -> vtype == TYCHAR) &&
+ (!ISICON (addrp -> memoffset) ||
+ (addrp -> memoffset -> constblock.Const.ci))) {
+
+ int use_paren = 0;
+ expptr e = addrp -> memoffset;
+
+ if (!e)
+ return;
+ addrp->memoffset = 0;
+
+ if (ONEOF(addrp->vstg, M(STGCOMMON)|M(STGEQUIV))
+ && addrp -> uname_tag == UNAM_NAME) {
+ e = mkexpr (OPMINUS, e, mkintcon (addrp -> user.name -> voffset));
+
+/* mkexpr will simplify it to zero if possible */
+ if (e->tag == TCONST && e->constblock.Const.ci == 0)
+ return;
+ } /* if addrp -> vstg == STGCOMMON */
+
+/* In the worst case, parentheses might be needed OUTSIDE the expression,
+ too. But since I think this subscripting can only appear as a
+ parameter in a procedure call, I don't think outside parens will ever
+ be needed. INSIDE parens are handled below */
+
+ nice_printf (fp, " + ");
+ if (e -> tag == TEXPR) {
+ int arg_prec = op_precedence (e -> exprblock.opcode);
+ int prec = op_precedence (OPPLUS);
+ use_paren = arg_prec && (arg_prec < prec || (arg_prec == prec &&
+ is_left_assoc (OPPLUS)));
+ } /* if e -> tag == TEXPR */
+ if (use_paren) nice_printf (fp, "(");
+ expr_out (fp, e);
+ if (use_paren) nice_printf (fp, ")");
+ } /* if */
+} /* out_addr */
+
+
+ static void
+#ifdef KR_headers
+output_literal(fp, memno, cp)
+ FILE *fp;
+ int memno;
+ Constp cp;
+#else
+output_literal(FILE *fp, int memno, Constp cp)
+#endif
+{
+ struct Literal *litp, *lastlit;
+
+ lastlit = litpool + nliterals;
+
+ for (litp = litpool; litp < lastlit; litp++) {
+ if (litp -> litnum == memno)
+ break;
+ } /* for litp */
+
+ if (litp >= lastlit)
+ out_const (fp, cp);
+ else {
+ nice_printf (fp, "%s", lit_name (litp));
+ litp->lituse++;
+ }
+} /* output_literal */
+
+
+ static void
+#ifdef KR_headers
+output_prim(fp, primp)
+ FILE *fp;
+ struct Primblock *primp;
+#else
+output_prim(FILE *fp, struct Primblock *primp)
+#endif
+{
+ if (primp == NULL)
+ return;
+
+ out_name (fp, primp -> namep);
+ if (primp -> argsp)
+ output_arg_list (fp, primp -> argsp);
+
+ if (primp -> fcharp != (expptr) NULL || primp -> lcharp != (expptr) NULL)
+ nice_printf (fp, "Sorry, no substrings yet");
+}
+
+
+
+ static void
+#ifdef KR_headers
+output_arg_list(fp, listp)
+ FILE *fp;
+ struct Listblock *listp;
+#else
+output_arg_list(FILE *fp, struct Listblock *listp)
+#endif
+{
+ chainp arg_list;
+
+ if (listp == (struct Listblock *) NULL || listp -> listp == (chainp) NULL)
+ return;
+
+ nice_printf (fp, "(");
+
+ for (arg_list = listp -> listp; arg_list; arg_list = arg_list -> nextp) {
+ expr_out (fp, (expptr) arg_list -> datap);
+ if (arg_list -> nextp != (chainp) NULL)
+
+/* Might want to add a hook in here to accomodate the style setting which
+ wants spaces after commas */
+
+ nice_printf (fp, ",");
+ } /* for arg_list */
+
+ nice_printf (fp, ")");
+} /* output_arg_list */
+
+
+
+ static void
+#ifdef KR_headers
+output_unary(fp, e)
+ FILE *fp;
+ struct Exprblock *e;
+#else
+output_unary(FILE *fp, struct Exprblock *e)
+#endif
+{
+ if (e == NULL)
+ return;
+
+ switch (e -> opcode) {
+ case OPNEG:
+ if (e->vtype == TYREAL && forcedouble) {
+ e->opcode = OPNEG_KLUDGE;
+ output_binary(fp,e);
+ e->opcode = OPNEG;
+ break;
+ }
+ case OPNEG1:
+ case OPNOT:
+ case OPABS:
+ case OPBITNOT:
+ case OPWHATSIN:
+ case OPPREINC:
+ case OPPREDEC:
+ case OPADDR:
+ case OPIDENTITY:
+ case OPCHARCAST:
+ case OPDABS:
+ output_binary (fp, e);
+ break;
+ case OPCALL:
+ case OPCCALL:
+ nice_printf (fp, "Sorry, no OPCALL yet");
+ break;
+ default:
+ erri ("output_unary: bad opcode", (int) e -> opcode);
+ break;
+ } /* switch */
+} /* output_unary */
+
+
+ static char *
+#ifdef KR_headers
+findconst(m)
+ register long m;
+#else
+findconst(register long m)
+#endif
+{
+ register struct Literal *litp, *litpe;
+
+ litp = litpool;
+ for(litpe = litp + nliterals; litp < litpe; litp++)
+ if (litp->litnum == m)
+ return litp->cds[0];
+ Fatal("findconst failure!");
+ return 0;
+ }
+
+ static int
+#ifdef KR_headers
+opconv_fudge(fp, e)
+ FILE *fp;
+ struct Exprblock *e;
+#else
+opconv_fudge(FILE *fp, struct Exprblock *e)
+#endif
+{
+ /* special handling for ichar and character*1 */
+ register expptr lp;
+ register union Expression *Offset;
+ register char *cp;
+ int lt;
+ char buf[8];
+ unsigned int k;
+ Namep np;
+
+ if (!(lp = e->leftp)) /* possible with erroneous Fortran */
+ return 1;
+ lt = lp->headblock.vtype;
+ if (lt == TYCHAR) {
+ switch(lp->tag) {
+ case TNAME:
+ nice_printf(fp, "*(unsigned char *)");
+ out_name(fp, (Namep)lp);
+ return 1;
+ case TCONST:
+ tconst:
+ cp = lp->constblock.Const.ccp;
+ tconst1:
+ k = *(unsigned char *)cp;
+ if (k < 128) { /* ASCII character */
+ sprintf(buf, chr_fmt[k], k);
+ nice_printf(fp, "'%s'", buf);
+ }
+ else
+ nice_printf(fp, "%d", k);
+ return 1;
+ case TADDR:
+ switch(lp->addrblock.vstg) {
+ case STGMEMNO:
+ if (halign && e->vtype != TYCHAR) {
+ nice_printf(fp, "*(%s *)",
+ c_type_decl(e->vtype,0));
+ expr_out(fp, lp);
+ return 1;
+ }
+ cp = findconst(lp->addrblock.memno);
+ goto tconst1;
+ case STGCONST:
+ goto tconst;
+ }
+ lp->addrblock.vtype = tyint;
+ Offset = lp->addrblock.memoffset;
+ switch(lp->addrblock.uname_tag) {
+ case UNAM_REF:
+ nice_printf(fp, "*(unsigned char *)");
+ return 0;
+ case UNAM_NAME:
+ np = lp->addrblock.user.name;
+ if (ONEOF(np->vstg,
+ M(STGCOMMON)|M(STGEQUIV)))
+ Offset = mkexpr(OPMINUS, Offset,
+ ICON(np->voffset));
+ }
+ lp->addrblock.memoffset = Offset ?
+ mkexpr(OPSTAR, Offset,
+ ICON(typesize[tyint]))
+ : ICON(0);
+ lp->addrblock.isarray = 1;
+ /* STGCOMMON or STGEQUIV would cause */
+ /* voffset to be added in a second time */
+ lp->addrblock.vstg = STGUNKNOWN;
+ nice_printf(fp, "*(unsigned char *)&");
+ return 0;
+ default:
+ badtag("opconv_fudge", lp->tag);
+ }
+ }
+ if (lt != e->vtype)
+ nice_printf(fp, "(%s) ",
+ c_type_decl(e->vtype, 0));
+ return 0;
+ }
+
+
+ static void
+#ifdef KR_headers
+output_binary(fp, e)
+ FILE *fp;
+ struct Exprblock *e;
+#else
+output_binary(FILE *fp, struct Exprblock *e)
+#endif
+{
+ char *format;
+ extern table_entry opcode_table[];
+ int prec;
+
+ if (e == NULL || e -> tag != TEXPR)
+ return;
+
+/* Instead of writing a huge switch, I've incorporated the output format
+ into a table. Things like "%l" and "%r" stand for the left and
+ right subexpressions. This should allow both prefix and infix
+ functions to be specified (e.g. "(%l * %r", "z_div (%l, %r"). Of
+ course, I should REALLY think out the ramifications of writing out
+ straight text, as opposed to some intermediate format, which could
+ figure out and optimize on the the number of required blanks (we don't
+ want "x - (-y)" to become "x --y", for example). Special cases (such as
+ incomplete implementations) could still be implemented as part of the
+ switch, they will just have some dummy value instead of the string
+ pattern. Another difficulty is the fact that the complex functions
+ will differ from the integer and real ones */
+
+/* Handle a special case. We don't want to output "x + - 4", or "y - - 3"
+*/
+ if ((e -> opcode == OPPLUS || e -> opcode == OPMINUS) &&
+ e -> rightp && e -> rightp -> tag == TCONST &&
+ isnegative_const (&(e -> rightp -> constblock)) &&
+ is_negatable (&(e -> rightp -> constblock))) {
+
+ e -> opcode = (e -> opcode == OPPLUS) ? OPMINUS : OPPLUS;
+ negate_const (&(e -> rightp -> constblock));
+ } /* if e -> opcode == PLUS or MINUS */
+
+ prec = op_precedence (e -> opcode);
+ format = op_format (e -> opcode);
+
+ if (format != SPECIAL_FMT) {
+ while (*format) {
+ if (*format == '%') {
+ int arg_prec, use_paren = 0;
+ expptr lp, rp;
+
+ switch (*(format + 1)) {
+ case 'l':
+ lp = e->leftp;
+ if (lp && lp->tag == TEXPR) {
+ arg_prec = op_precedence(lp->exprblock.opcode);
+
+ use_paren = arg_prec &&
+ (arg_prec < prec || (arg_prec == prec &&
+ is_right_assoc (prec)));
+ } /* if e -> leftp */
+ if (e->opcode == OPCONV && opconv_fudge(fp,e))
+ break;
+ if (use_paren)
+ nice_printf (fp, "(");
+ expr_out(fp, lp);
+ if (use_paren)
+ nice_printf (fp, ")");
+ break;
+ case 'r':
+ rp = e->rightp;
+ if (rp && rp->tag == TEXPR) {
+ arg_prec = op_precedence(rp->exprblock.opcode);
+
+ use_paren = arg_prec &&
+ (arg_prec < prec || (arg_prec == prec &&
+ is_left_assoc (prec)));
+ use_paren = use_paren ||
+ (rp->exprblock.opcode == OPNEG
+ && prec >= op_precedence(OPMINUS));
+ } /* if e -> rightp */
+ if (use_paren)
+ nice_printf (fp, "(");
+ expr_out(fp, rp);
+ if (use_paren)
+ nice_printf (fp, ")");
+ break;
+ case '\0':
+ case '%':
+ nice_printf (fp, "%%");
+ break;
+ default:
+ erri ("output_binary: format err: '%%%c' illegal",
+ (int) *(format + 1));
+ break;
+ } /* switch */
+ format += 2;
+ } else
+ nice_printf (fp, "%c", *format++);
+ } /* while *format */
+ } else {
+
+/* Handle Special cases of formatting */
+
+ switch (e -> opcode) {
+ case OPCCALL:
+ case OPCALL:
+ out_call (fp, (int) e -> opcode, e -> vtype,
+ e -> vleng, e -> leftp, e -> rightp);
+ break;
+
+ case OPCOMMA_ARG:
+ doin_setbound = 1;
+ nice_printf(fp, "(");
+ expr_out(fp, e->leftp);
+ nice_printf(fp, ", &");
+ doin_setbound = 0;
+ expr_out(fp, e->rightp);
+ nice_printf(fp, ")");
+ break;
+
+ case OPADDR:
+ default:
+ nice_printf (fp, "Sorry, can't format OPCODE '%d'",
+ e -> opcode);
+ break;
+ }
+
+ } /* else */
+} /* output_binary */
+
+ void
+#ifdef KR_headers
+out_call(outfile, op, ftype, len, name, args)
+ FILE *outfile;
+ int op;
+ int ftype;
+ expptr len;
+ expptr name;
+ expptr args;
+#else
+out_call(FILE *outfile, int op, int ftype, expptr len, expptr name, expptr args)
+#endif
+{
+ chainp arglist; /* Pointer to any actual arguments */
+ chainp cp; /* Iterator over argument lists */
+ Addrp ret_val = (Addrp) NULL;
+ /* Function return value buffer, if any is
+ required */
+ int byvalue; /* True iff we're calling a C library
+ routine */
+ int done_once; /* Used for writing commas to outfile */
+ int narg, t;
+ register expptr q;
+ long L;
+ Argtypes *at;
+ Atype *A, *Ac;
+ Namep np;
+ extern int forcereal;
+
+/* Don't use addresses if we're calling a C function */
+
+ byvalue = op == OPCCALL;
+
+ if (args)
+ arglist = args -> listblock.listp;
+ else
+ arglist = CHNULL;
+
+/* If this is a CHARACTER function, the first argument is the result */
+
+ if (ftype == TYCHAR)
+ if (ISICON (len)) {
+ ret_val = (Addrp) (arglist -> datap);
+ arglist = arglist -> nextp;
+ } else {
+ err ("adjustable character function");
+ return;
+ } /* else */
+
+/* If this is a COMPLEX function, the first argument is the result */
+
+ else if (ISCOMPLEX (ftype)) {
+ ret_val = (Addrp) (arglist -> datap);
+ arglist = arglist -> nextp;
+ } /* if ISCOMPLEX */
+
+/* Now we can actually start to write out the function invocation */
+
+ if (ftype == TYREAL && forcereal)
+ nice_printf(outfile, "(real)");
+ if (name -> tag == TEXPR && name -> exprblock.opcode == OPWHATSIN) {
+ nice_printf (outfile, "(");
+ np = (Namep)name->exprblock.leftp; /*expr_out will free name */
+ expr_out (outfile, name);
+ nice_printf (outfile, ")");
+ }
+ else {
+ np = (Namep)name;
+ expr_out(outfile, name);
+ }
+
+ /* prepare to cast procedure parameters -- set A if we know how */
+
+ A = Ac = 0;
+ if (np->tag == TNAME && (at = np->arginfo)) {
+ if (at->nargs > 0)
+ A = at->atypes;
+ if (Ansi && (at->defined || at->nargs > 0))
+ Ac = at->atypes;
+ }
+
+ nice_printf(outfile, "(");
+
+ if (ret_val) {
+ if (ISCOMPLEX (ftype))
+ nice_printf (outfile, "&");
+ expr_out (outfile, (expptr) ret_val);
+ if (Ac)
+ Ac++;
+
+/* The length of the result of a character function is the second argument */
+/* It should be in place from putcall(), so we won't touch it explicitly */
+
+ } /* if ret_val */
+ done_once = ret_val ? TRUE : FALSE;
+
+/* Now run through the named arguments */
+
+ narg = -1;
+ for (cp = arglist; cp; cp = cp -> nextp, done_once = TRUE) {
+
+ if (done_once)
+ nice_printf (outfile, ", ");
+ narg++;
+
+ if (!( q = (expptr)cp->datap) )
+ continue;
+
+ if (q->tag == TADDR) {
+ if (q->addrblock.vtype > TYERROR) {
+ /* I/O block */
+ nice_printf(outfile, "&%s", q->addrblock.user.ident);
+ continue;
+ }
+ if (!byvalue && q->addrblock.isarray
+ && q->addrblock.vtype != TYCHAR
+ && q->addrblock.memoffset->tag == TCONST) {
+
+ /* check for 0 offset -- after */
+ /* correcting for equivalence. */
+ L = q->addrblock.memoffset->constblock.Const.ci;
+ if (ONEOF(q->addrblock.vstg, M(STGCOMMON)|M(STGEQUIV))
+ && q->addrblock.uname_tag == UNAM_NAME)
+ L -= q->addrblock.user.name->voffset;
+ if (L)
+ goto skip_deref;
+
+ if (Ac && narg < at->dnargs
+ && q->headblock.vtype != (t = Ac[narg].type)
+ && t > TYADDR && t < TYSUBR)
+ nice_printf(outfile, "(%s*)", typename[t]);
+
+ /* &x[0] == x */
+ /* This also prevents &sizeof(doublereal)[0] */
+
+ switch(q->addrblock.uname_tag) {
+ case UNAM_NAME:
+ out_name(outfile, q->addrblock.user.name);
+ continue;
+ case UNAM_IDENT:
+ nice_printf(outfile, "%s",
+ q->addrblock.user.ident);
+ continue;
+ case UNAM_CHARP:
+ nice_printf(outfile, "%s",
+ q->addrblock.user.Charp);
+ continue;
+ case UNAM_EXTERN:
+ extern_out(outfile,
+ &extsymtab[q->addrblock.memno]);
+ continue;
+ }
+ }
+ }
+
+/* Skip over the dereferencing operator generated only for the
+ intermediate file */
+ skip_deref:
+ if (q -> tag == TEXPR && q -> exprblock.opcode == OPWHATSIN)
+ q = q -> exprblock.leftp;
+
+ if (q->headblock.vclass == CLPROC) {
+ if (Castargs && (q->tag != TNAME
+ || q->nameblock.vprocclass != PTHISPROC)
+ && (q->tag != TADDR
+ || q->addrblock.uname_tag != UNAM_NAME
+ || q->addrblock.user.name->vprocclass
+ != PTHISPROC))
+ {
+ if (A && (t = A[narg].type) >= 200)
+ t %= 100;
+ else {
+ t = q->headblock.vtype;
+ if (q->tag == TNAME && q->nameblock.vimpltype)
+ t = TYUNKNOWN;
+ }
+ nice_printf(outfile, "(%s)", usedcasts[t] = casttypes[t]);
+ }
+ }
+ else if (Ac && narg < at->dnargs
+ && q->headblock.vtype != (t = Ac[narg].type)
+ && t > TYADDR && t < TYSUBR)
+ nice_printf(outfile, "(%s*)", typename[t]);
+
+ if ((q -> tag == TADDR || q-> tag == TNAME) &&
+ (byvalue || q -> headblock.vstg != STGREG)) {
+ if (q -> headblock.vtype != TYCHAR)
+ if (byvalue) {
+
+ if (q -> tag == TADDR &&
+ q -> addrblock.uname_tag == UNAM_NAME &&
+ ! q -> addrblock.user.name -> vdim &&
+ oneof_stg(q -> addrblock.user.name, q -> addrblock.vstg,
+ M(STGARG)|M(STGEQUIV)) &&
+ ! ISCOMPLEX(q->addrblock.user.name->vtype))
+ nice_printf (outfile, "*");
+ else if (q -> tag == TNAME
+ && oneof_stg(&q->nameblock, q -> nameblock.vstg,
+ M(STGARG)|M(STGEQUIV))
+ && !(q -> nameblock.vdim))
+ nice_printf (outfile, "*");
+
+ } else {
+ expptr memoffset;
+
+ if (q->tag == TADDR &&
+ !ONEOF (q -> addrblock.vstg, M(STGEXT)|M(STGLENG))
+ && (
+ ONEOF(q->addrblock.vstg,
+ M(STGCOMMON)|M(STGEQUIV)|M(STGMEMNO))
+ || ((memoffset = q->addrblock.memoffset)
+ && (!ISICON(memoffset)
+ || memoffset->constblock.Const.ci)))
+ || ONEOF(q->addrblock.vstg,
+ M(STGINIT)|M(STGAUTO)|M(STGBSS))
+ && !q->addrblock.isarray)
+ nice_printf (outfile, "&");
+ else if (q -> tag == TNAME
+ && !oneof_stg(&q->nameblock, q -> nameblock.vstg,
+ M(STGARG)|M(STGEXT)|M(STGEQUIV)))
+ nice_printf (outfile, "&");
+ } /* else */
+
+ expr_out (outfile, q);
+ } /* if q -> tag == TADDR || q -> tag == TNAME */
+
+/* Might be a Constant expression, e.g. string length, character constants */
+
+ else if (q -> tag == TCONST) {
+ if (tyioint == TYLONG)
+ Longfmt = "%ldL";
+ out_const(outfile, &q->constblock);
+ Longfmt = "%ld";
+ }
+
+/* Must be some other kind of expression, or register var, or constant.
+ In particular, this is likely to be a temporary variable assignment
+ which was generated in p1put_call */
+
+ else if (!ISCOMPLEX (q -> headblock.vtype) && !ISCHAR (q)){
+ int use_paren = q -> tag == TEXPR &&
+ op_precedence (q -> exprblock.opcode) <=
+ op_precedence (OPCOMMA);
+
+ if (use_paren) nice_printf (outfile, "(");
+ expr_out (outfile, q);
+ if (use_paren) nice_printf (outfile, ")");
+ } /* if !ISCOMPLEX */
+ else
+ err ("out_call: unknown parameter");
+
+ } /* for (cp = arglist */
+
+ if (arglist)
+ frchain (&arglist);
+
+ nice_printf (outfile, ")");
+
+} /* out_call */
+
+
+ char *
+#ifdef KR_headers
+flconst(buf, x)
+ char *buf;
+ char *x;
+#else
+flconst(char *buf, char *x)
+#endif
+{
+ sprintf(buf, fl_fmt_string, x);
+ return buf;
+ }
+
+ char *
+#ifdef KR_headers
+dtos(x)
+ double x;
+#else
+dtos(double x)
+#endif
+{
+ static char buf[64];
+#ifdef USE_DTOA
+ g_fmt(buf, x);
+#else
+ sprintf(buf, db_fmt_string, x);
+#endif
+ return strcpy(mem(strlen(buf)+1,0), buf);
+ }
+
+char tr_tab[Table_size];
+
+/* out_init -- Initialize the data structures used by the routines in
+ output.c. These structures include the output format to be used for
+ Float, Double, Complex, and Double Complex constants. */
+
+ void
+out_init(Void)
+{
+ extern int tab_size;
+ register char *s;
+
+ s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+-.";
+ while(*s)
+ tr_tab[*s++] = 3;
+ tr_tab['>'] = 1;
+
+ opeqable[OPPLUS] = 1;
+ opeqable[OPMINUS] = 1;
+ opeqable[OPSTAR] = 1;
+ opeqable[OPSLASH] = 1;
+ opeqable[OPMOD] = 1;
+ opeqable[OPLSHIFT] = 1;
+ opeqable[OPBITAND] = 1;
+ opeqable[OPBITXOR] = 1;
+ opeqable[OPBITOR ] = 1;
+
+
+/* Set the output format for both types of floating point constants */
+
+ if (fl_fmt_string == NULL || *fl_fmt_string == '\0')
+ fl_fmt_string = Ansi == 1 ? "%sf" : "(float)%s";
+
+ if (db_fmt_string == NULL || *db_fmt_string == '\0')
+ db_fmt_string = "%.17g";
+
+/* Set the output format for both types of complex constants. They will
+ have string parameters rather than float or double so that the decimal
+ point may be added to the strings generated by the {db,fl}_fmt_string
+ formats above */
+
+ if (cm_fmt_string == NULL || *cm_fmt_string == '\0') {
+ cm_fmt_string = "{%s,%s}";
+ } /* if cm_fmt_string == NULL */
+
+ if (dcm_fmt_string == NULL || *dcm_fmt_string == '\0') {
+ dcm_fmt_string = "{%s,%s}";
+ } /* if dcm_fmt_string == NULL */
+
+ tab_size = 4;
+} /* out_init */
+
+
+ void
+#ifdef KR_headers
+extern_out(fp, extsym)
+ FILE *fp;
+ Extsym *extsym;
+#else
+extern_out(FILE *fp, Extsym *extsym)
+#endif
+{
+ if (extsym == (Extsym *) NULL)
+ return;
+
+ nice_printf (fp, "%s", extsym->cextname);
+
+} /* extern_out */
+
+
+
+ static void
+#ifdef KR_headers
+output_list(fp, listp)
+ FILE *fp;
+ struct Listblock *listp;
+#else
+output_list(FILE *fp, struct Listblock *listp)
+#endif
+{
+ int did_one = 0;
+ chainp elts;
+
+ nice_printf (fp, "(");
+ if (listp)
+ for (elts = listp -> listp; elts; elts = elts -> nextp) {
+ if (elts -> datap) {
+ if (did_one)
+ nice_printf (fp, ", ");
+ expr_out (fp, (expptr) elts -> datap);
+ did_one = 1;
+ } /* if elts -> datap */
+ } /* for elts */
+ nice_printf (fp, ")");
+} /* output_list */
+
+
+ void
+#ifdef KR_headers
+out_asgoto(outfile, expr)
+ FILE *outfile;
+ expptr expr;
+#else
+out_asgoto(FILE *outfile, expptr expr)
+#endif
+{
+ chainp value;
+ Namep namep;
+ int k;
+
+ if (expr == (expptr) NULL) {
+ err ("out_asgoto: NULL variable expr");
+ return;
+ } /* if expr */
+
+ nice_printf (outfile, Ansi ? "switch (" : "switch ((int)"); /*)*/
+ expr_out (outfile, expr);
+ nice_printf (outfile, ") {\n");
+ next_tab (outfile);
+
+/* The initial addrp value will be stored as a namep pointer */
+
+ switch(expr->tag) {
+ case TNAME:
+ /* local variable */
+ namep = &expr->nameblock;
+ break;
+ case TEXPR:
+ if (expr->exprblock.opcode == OPWHATSIN
+ && expr->exprblock.leftp->tag == TNAME)
+ /* argument */
+ namep = &expr->exprblock.leftp->nameblock;
+ else
+ goto bad;
+ break;
+ case TADDR:
+ if (expr->addrblock.uname_tag == UNAM_NAME) {
+ /* initialized local variable */
+ namep = expr->addrblock.user.name;
+ break;
+ }
+ default:
+ bad:
+ err("out_asgoto: bad expr");
+ return;
+ }
+
+ for(k = 0, value = namep -> varxptr.assigned_values; value;
+ value = value->nextp, k++) {
+ nice_printf (outfile, "case %d: goto %s;\n", k,
+ user_label((long)value->datap));
+ } /* for value */
+ prev_tab (outfile);
+
+ nice_printf (outfile, "}\n");
+} /* out_asgoto */
+
+ void
+#ifdef KR_headers
+out_if(outfile, expr)
+ FILE *outfile;
+ expptr expr;
+#else
+out_if(FILE *outfile, expptr expr)
+#endif
+{
+ nice_printf (outfile, "if (");
+ expr_out (outfile, expr);
+ nice_printf (outfile, ") {\n");
+ next_tab (outfile);
+} /* out_if */
+
+ static void
+#ifdef KR_headers
+output_rbrace(outfile, s)
+ FILE *outfile;
+ char *s;
+#else
+output_rbrace(FILE *outfile, char *s)
+#endif
+{
+ extern int last_was_label;
+ register char *fmt;
+
+ if (last_was_label) {
+ last_was_label = 0;
+ fmt = ";%s";
+ }
+ else
+ fmt = "%s";
+ nice_printf(outfile, fmt, s);
+ }
+
+ void
+#ifdef KR_headers
+out_else(outfile)
+ FILE *outfile;
+#else
+out_else(FILE *outfile)
+#endif
+{
+ prev_tab (outfile);
+ output_rbrace(outfile, "} else {\n");
+ next_tab (outfile);
+} /* out_else */
+
+ void
+#ifdef KR_headers
+elif_out(outfile, expr)
+ FILE *outfile;
+ expptr expr;
+#else
+elif_out(FILE *outfile, expptr expr)
+#endif
+{
+ prev_tab (outfile);
+ output_rbrace(outfile, "} else ");
+ out_if (outfile, expr);
+} /* elif_out */
+
+ void
+#ifdef KR_headers
+endif_out(outfile)
+ FILE *outfile;
+#else
+endif_out(FILE *outfile)
+#endif
+{
+ prev_tab (outfile);
+ output_rbrace(outfile, "}\n");
+} /* endif_out */
+
+ void
+#ifdef KR_headers
+end_else_out(outfile)
+ FILE *outfile;
+#else
+end_else_out(FILE *outfile)
+#endif
+{
+ prev_tab (outfile);
+ output_rbrace(outfile, "}\n");
+} /* end_else_out */
+
+
+
+ void
+#ifdef KR_headers
+compgoto_out(outfile, index, labels)
+ FILE *outfile;
+ expptr index;
+ expptr labels;
+#else
+compgoto_out(FILE *outfile, expptr index, expptr labels)
+#endif
+{
+ char *s1, *s2;
+
+ if (index == ENULL)
+ err ("compgoto_out: null index for computed goto");
+ else if (labels && labels -> tag != TLIST)
+ erri ("compgoto_out: expected label list, got tag '%d'",
+ labels -> tag);
+ else {
+ chainp elts;
+ int i = 1;
+
+ s2 = /*(*/ ") {\n"; /*}*/
+ if (Ansi)
+ s1 = "switch ("; /*)*/
+ else if (index->tag == TNAME || index->tag == TEXPR
+ && index->exprblock.opcode == OPWHATSIN)
+ s1 = "switch ((int)"; /*)*/
+ else {
+ s1 = "switch ((int)(";
+ s2 = ")) {\n"; /*}*/
+ }
+ nice_printf(outfile, s1);
+ expr_out (outfile, index);
+ nice_printf (outfile, s2);
+ next_tab (outfile);
+
+ for (elts = labels -> listblock.listp; elts; elts = elts -> nextp, i++) {
+ if (elts -> datap) {
+ if (ISICON(((expptr) (elts -> datap))))
+ nice_printf (outfile, "case %d: goto %s;\n", i,
+ user_label(((expptr)(elts->datap))->constblock.Const.ci));
+ else
+ err ("compgoto_out: bad label in label list");
+ } /* if (elts -> datap) */
+ } /* for elts */
+ prev_tab (outfile);
+ nice_printf (outfile, /*{*/ "}\n");
+ } /* else */
+} /* compgoto_out */
+
+
+ void
+#ifdef KR_headers
+out_for(outfile, init, test, inc)
+ FILE *outfile;
+ expptr init;
+ expptr test;
+ expptr inc;
+#else
+out_for(FILE *outfile, expptr init, expptr test, expptr inc)
+#endif
+{
+ nice_printf (outfile, "for (");
+ expr_out (outfile, init);
+ nice_printf (outfile, "; ");
+ expr_out (outfile, test);
+ nice_printf (outfile, "; ");
+ expr_out (outfile, inc);
+ nice_printf (outfile, ") {\n");
+ next_tab (outfile);
+} /* out_for */
+
+
+ void
+#ifdef KR_headers
+out_end_for(outfile)
+ FILE *outfile;
+#else
+out_end_for(FILE *outfile)
+#endif
+{
+ prev_tab (outfile);
+ nice_printf (outfile, "}\n");
+} /* out_end_for */
diff --git a/usr.bin/f2c/output.h b/usr.bin/f2c/output.h
new file mode 100644
index 0000000..97e3a0a
--- /dev/null
+++ b/usr.bin/f2c/output.h
@@ -0,0 +1,64 @@
+/* nice_printf -- same arguments as fprintf.
+
+ All output which is to become C code must be directed through this
+ function. For now, no buffering is done. Later on, every line of
+ output will be filtered to accomodate the style definitions (e.g. one
+ statement per line, spaces between function names and argument lists,
+ etc.)
+*/
+#include "niceprintf.h"
+
+
+/* Definitions for the opcode table. The table is indexed by the macros
+ which are #defined in defines.h */
+
+#define UNARY_OP 01
+#define BINARY_OP 02
+
+#define SPECIAL_FMT NULL
+
+#define is_unary_op(x) (opcode_table[x].type == UNARY_OP)
+#define is_binary_op(x) (opcode_table[x].type == BINARY_OP)
+#define op_precedence(x) (opcode_table[x].prec)
+#define op_format(x) (opcode_table[x].format)
+
+/* _assoc_table -- encodes left-associativity and right-associativity
+ information; indexed by precedence level. Only 2, 3, 14 are
+ right-associative. Source: Kernighan & Ritchie, p. 49 */
+
+extern char _assoc_table[];
+
+#define is_right_assoc(x) (_assoc_table [x])
+#define is_left_assoc(x) (! _assoc_table [x])
+
+
+typedef struct {
+ int type; /* UNARY_OP or BINARY_OP */
+ int prec; /* Precedence level, useful for adjusting
+ number of parens to insert. Zero is a
+ special level, and 2, 3, 14 are
+ right-associative */
+ char *format;
+} table_entry;
+
+
+extern char *fl_fmt_string; /* Float constant format string */
+extern char *db_fmt_string; /* Double constant format string */
+extern char *cm_fmt_string; /* Complex constant format string */
+extern char *dcm_fmt_string; /* Double Complex constant format string */
+
+extern int indent; /* Number of spaces to indent; this is a
+ temporary fix */
+extern int tab_size; /* Number of spaces in each tab */
+extern int in_string;
+
+extern table_entry opcode_table[];
+
+
+void compgoto_out Argdcl((FILEP, tagptr, tagptr));
+void endif_out Argdcl((FILEP));
+void expr_out Argdcl((FILEP, tagptr));
+void out_and_free_statement Argdcl((FILEP, tagptr));
+void out_end_for Argdcl((FILEP));
+void out_if Argdcl((FILEP, tagptr));
+void out_name Argdcl((FILEP, Namep));
diff --git a/usr.bin/f2c/p1defs.h b/usr.bin/f2c/p1defs.h
new file mode 100644
index 0000000..c76af22
--- /dev/null
+++ b/usr.bin/f2c/p1defs.h
@@ -0,0 +1,158 @@
+#define P1_UNKNOWN 0
+#define P1_COMMENT 1 /* Fortan comment string */
+#define P1_EOF 2 /* End of file dummy token */
+#define P1_SET_LINE 3 /* Reset the line counter */
+#define P1_FILENAME 4 /* Name of current input file */
+#define P1_NAME_POINTER 5 /* Pointer to hash table entry */
+#define P1_CONST 6 /* Some constant value */
+#define P1_EXPR 7 /* Followed by opcode */
+
+/* The next two tokens could be grouped together, since they always come
+ from an Addr structure */
+
+#define P1_IDENT 8 /* Char string identifier in addrp->user
+ field */
+#define P1_EXTERN 9 /* Pointer to external symbol entry */
+
+#define P1_HEAD 10 /* Function header info */
+#define P1_LIST 11 /* A list of data (e.g. arguments) will
+ follow the tag, type, and count */
+#define P1_LITERAL 12 /* Hold the index into the literal pool */
+#define P1_LABEL 13 /* label value */
+#define P1_ASGOTO 14 /* Store the hash table pointer of
+ variable used in assigned goto */
+#define P1_GOTO 15 /* Store the statement number */
+#define P1_IF 16 /* store the condition as an expression */
+#define P1_ELSE 17 /* No data */
+#define P1_ELIF 18 /* store the condition as an expression */
+#define P1_ENDIF 19 /* Marks the end of a block IF */
+#define P1_ENDELSE 20 /* Marks the end of a block ELSE */
+#define P1_ADDR 21 /* Addr data; used for arrays, common and
+ equiv addressing, NOT for names, idents
+ or externs */
+#define P1_SUBR_RET 22 /* Subroutine return; the return expression
+ follows */
+#define P1_COMP_GOTO 23 /* Computed goto; has expr, label list */
+#define P1_FOR 24 /* C FOR loop; three expressions follow */
+#define P1_ENDFOR 25 /* End of C FOR loop */
+#define P1_FORTRAN 26 /* original Fortran source */
+#define P1_CHARP 27 /* user.Charp field -- for long names */
+#define P1_WHILE1START 28 /* start of DO WHILE */
+#define P1_WHILE2START 29 /* rest of DO WHILE */
+#define P1_PROCODE 30 /* invoke procode() -- to adjust params */
+#define P1_ELSEIFSTART 31 /* handle extra code for abs, min, max
+ in else if() */
+
+#define P1_FILENAME_MAX 256 /* max filename length to retain (for -g) */
+#define P1_STMTBUFSIZE 1400
+
+
+
+#define COMMENT_BUFFER_SIZE 255 /* max number of chars in each comment */
+#define CONSTANT_STR_MAX 1000 /* max number of chars in string constant */
+
+void p1_asgoto Argdcl((Addrp));
+void p1_comment Argdcl((char*));
+void p1_elif Argdcl((tagptr));
+void p1_else Argdcl((void));
+void p1_endif Argdcl((void));
+void p1_expr Argdcl((tagptr));
+void p1_for Argdcl((tagptr, tagptr, tagptr));
+void p1_goto Argdcl((long int));
+void p1_head Argdcl((int, char*));
+void p1_if Argdcl((tagptr));
+void p1_label Argdcl((long int));
+void p1_line_number Argdcl((long int));
+void p1_subr_ret Argdcl((tagptr));
+void p1comp_goto Argdcl((tagptr, int, struct Labelblock**));
+void p1else_end Argdcl((void));
+void p1for_end Argdcl((void));
+void p1put Argdcl((int));
+void p1puts Argdcl((int, char*));
+
+/* The pass 1 intermediate file has the following format:
+
+ <ascii-integer-rep> [ : [ <sp> [ <data> ]]] \n
+
+ e.g. 1: This is a comment
+
+ This format is destined to change in the future, but for now a readable
+ form is more desirable than a compact form.
+
+ NOTES ABOUT THE P1 FORMAT
+ ----------------------------------------------------------------------
+
+ P1_COMMENT: The comment string (in <data>) may be at most
+ COMMENT_BUFFER_SIZE bytes long. It must contain no newlines
+ or null characters. A side effect of the way comments are
+ read in lex.c is that no '\377' chars may be in a
+ comment either.
+
+ P1_SET_LINE: <data> holds the line number in the current source file.
+
+ P1_INC_LINE: Increment the source line number; <data> is empty.
+
+ P1_NAME_POINTER: <data> holds the integer representation of a
+ pointer into a hash table entry.
+
+ P1_CONST: the first field in <data> is a type tag (one of the
+ TYxxxx macros), the next field holds the constant
+ value
+
+ P1_EXPR: <data> holds the opcode number of the expression,
+ followed by the type of the expression (required for
+ OPCONV). Next is the value of vleng.
+ The type of operation represented by the
+ opcode determines how many of the following data items
+ are part of this expression.
+
+ P1_IDENT: <data> holds the type, then storage, then the
+ char string identifier in the addrp->user field.
+
+ P1_EXTERN: <data> holds an offset into the external symbol
+ table entry
+
+ P1_HEAD: the first field in <data> is the procedure class, the
+ second is the name of the procedure
+
+ P1_LIST: the first field in <data> is the tag, the second the
+ type of the list, the third the number of elements in
+ the list
+
+ P1_LITERAL: <data> holds the litnum of a value in the
+ literal pool.
+
+ P1_LABEL: <data> holds the statement number of the current
+ line
+
+ P1_ASGOTO: <data> holds the hash table pointer of the variable
+
+ P1_GOTO: <data> holds the statement number to jump to
+
+ P1_IF: <data> is empty, the following expression is the IF
+ condition.
+
+ P1_ELSE: <data> is empty.
+
+ P1_ELIF: <data> is empty, the following expression is the IF
+ condition.
+
+ P1_ENDIF: <data> is empty.
+
+ P1_ENDELSE: <data> is empty.
+
+ P1_ADDR: <data> holds a direct copy of the structure. The
+ next expression is a copy of vleng, and the next a
+ copy of memoffset.
+
+ P1_SUBR_RET: The next token is an expression for the return value.
+
+ P1_COMP_GOTO: The next token is an integer expression, the
+ following one a list of labels.
+
+ P1_FOR: The next three expressions are the Init, Test, and
+ Increment expressions of a C FOR loop.
+
+ P1_ENDFOR: Marks the end of the body of a FOR loop
+
+*/
diff --git a/usr.bin/f2c/p1output.c b/usr.bin/f2c/p1output.c
new file mode 100644
index 0000000..fc0e9ff
--- /dev/null
+++ b/usr.bin/f2c/p1output.c
@@ -0,0 +1,723 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "p1defs.h"
+#include "output.h"
+#include "names.h"
+
+
+static void p1_addr Argdcl((Addrp));
+static void p1_big_addr Argdcl((Addrp));
+static void p1_binary Argdcl((Exprp));
+static void p1_const Argdcl((Constp));
+static void p1_list Argdcl((struct Listblock*));
+static void p1_literal Argdcl((long int));
+static void p1_name Argdcl((Namep));
+static void p1_unary Argdcl((Exprp));
+static void p1putd Argdcl((int, long int));
+static void p1putdd Argdcl((int, int, int));
+static void p1putddd Argdcl((int, int, int, int));
+static void p1putdds Argdcl((int, int, int, char*));
+static void p1putds Argdcl((int, int, char*));
+static void p1putn Argdcl((int, int, char*));
+
+
+/* p1_comment -- save the text of a Fortran comment in the intermediate
+ file. Make sure that there are no spurious "/ *" or "* /" characters by
+ mapping them onto "/+" and "+/". str is assumed to hold no newlines and be
+ null terminated; it may be modified by this function. */
+
+ void
+#ifdef KR_headers
+p1_comment(str)
+ char *str;
+#else
+p1_comment(char *str)
+#endif
+{
+ register unsigned char *pointer, *ustr;
+
+ if (!str)
+ return;
+
+/* Get rid of any open or close comment combinations that may be in the
+ Fortran input */
+
+ ustr = (unsigned char *)str;
+ for(pointer = ustr; *pointer; pointer++)
+ if (*pointer == '*' && (pointer[1] == '/'
+ || pointer > ustr && pointer[-1] == '/'))
+ *pointer = '+';
+ /* trim trailing white space */
+#ifdef isascii
+ while(--pointer >= ustr && (!isascii(*pointer) || isspace(*pointer)));
+#else
+ while(--pointer >= ustr && isspace(*pointer));
+#endif
+ pointer[1] = 0;
+ p1puts (P1_COMMENT, str);
+} /* p1_comment */
+
+/* p1_name -- Writes the address of a hash table entry into the
+ intermediate file */
+
+ static void
+#ifdef KR_headers
+p1_name(namep)
+ Namep namep;
+#else
+p1_name(Namep namep)
+#endif
+{
+ p1putd (P1_NAME_POINTER, (long) namep);
+ namep->visused = 1;
+} /* p1_name */
+
+
+
+ void
+#ifdef KR_headers
+p1_expr(expr)
+ expptr expr;
+#else
+p1_expr(expptr expr)
+#endif
+{
+/* An opcode of 0 means a null entry */
+
+ if (expr == ENULL) {
+ p1putdd (P1_EXPR, 0, TYUNKNOWN); /* Should this be TYERROR? */
+ return;
+ } /* if (expr == ENULL) */
+
+ switch (expr -> tag) {
+ case TNAME:
+ p1_name ((Namep) expr);
+ return;
+ case TCONST:
+ p1_const(&expr->constblock);
+ return;
+ case TEXPR:
+ /* Fall through the switch */
+ break;
+ case TADDR:
+ p1_addr (&(expr -> addrblock));
+ goto freeup;
+ case TPRIM:
+ warn ("p1_expr: got TPRIM");
+ return;
+ case TLIST:
+ p1_list (&(expr->listblock));
+ frchain( &(expr->listblock.listp) );
+ return;
+ case TERROR:
+ return;
+ default:
+ erri ("p1_expr: bad tag '%d'", (int) (expr -> tag));
+ return;
+ }
+
+/* Now we know that the tag is TEXPR */
+
+ if (is_unary_op (expr -> exprblock.opcode))
+ p1_unary (&(expr -> exprblock));
+ else if (is_binary_op (expr -> exprblock.opcode))
+ p1_binary (&(expr -> exprblock));
+ else
+ erri ("p1_expr: bad opcode '%d'", (int) expr -> exprblock.opcode);
+ freeup:
+ free((char *)expr);
+
+} /* p1_expr */
+
+
+
+ static void
+#ifdef KR_headers
+p1_const(cp)
+ register Constp cp;
+#else
+p1_const(register Constp cp)
+#endif
+{
+ int type = cp->vtype;
+ expptr vleng = cp->vleng;
+ union Constant *c = &cp->Const;
+ char cdsbuf0[64], cdsbuf1[64];
+ char *cds0, *cds1;
+
+ switch (type) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYLOGICAL:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ fprintf(pass1_file, "%d: %d %ld\n", P1_CONST, type, c->ci);
+ break;
+ case TYREAL:
+ case TYDREAL:
+ fprintf(pass1_file, "%d: %d %s\n", P1_CONST, type,
+ cp->vstg ? c->cds[0] : cds(dtos(c->cd[0]), cdsbuf0));
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ if (cp->vstg) {
+ cds0 = c->cds[0];
+ cds1 = c->cds[1];
+ }
+ else {
+ cds0 = cds(dtos(c->cd[0]), cdsbuf0);
+ cds1 = cds(dtos(c->cd[1]), cdsbuf1);
+ }
+ fprintf(pass1_file, "%d: %d %s %s\n", P1_CONST, type,
+ cds0, cds1);
+ break;
+ case TYCHAR:
+ if (vleng && !ISICON (vleng))
+ erri("p1_const: bad vleng '%d'\n", (int) vleng);
+ else
+ fprintf(pass1_file, "%d: %d %lx\n", P1_CONST, type,
+ cpexpr((expptr)cp));
+ break;
+ default:
+ erri ("p1_const: bad constant type '%d'", type);
+ break;
+ } /* switch */
+} /* p1_const */
+
+
+ void
+#ifdef KR_headers
+p1_asgoto(addrp)
+ Addrp addrp;
+#else
+p1_asgoto(Addrp addrp)
+#endif
+{
+ p1put (P1_ASGOTO);
+ p1_addr (addrp);
+} /* p1_asgoto */
+
+
+ void
+#ifdef KR_headers
+p1_goto(stateno)
+ ftnint stateno;
+#else
+p1_goto(ftnint stateno)
+#endif
+{
+ p1putd (P1_GOTO, stateno);
+} /* p1_goto */
+
+
+ static void
+#ifdef KR_headers
+p1_addr(addrp)
+ register struct Addrblock *addrp;
+#else
+p1_addr(register struct Addrblock *addrp)
+#endif
+{
+ int stg;
+
+ if (addrp == (struct Addrblock *) NULL)
+ return;
+
+ stg = addrp -> vstg;
+
+ if (ONEOF(stg, M(STGINIT)|M(STGREG))
+ || ONEOF(stg, M(STGCOMMON)|M(STGEQUIV)) &&
+ (!ISICON(addrp->memoffset)
+ || (addrp->uname_tag == UNAM_NAME
+ ? addrp->memoffset->constblock.Const.ci
+ != addrp->user.name->voffset
+ : addrp->memoffset->constblock.Const.ci))
+ || ONEOF(stg, M(STGBSS)|M(STGINIT)|M(STGAUTO)|M(STGARG)) &&
+ (!ISICON(addrp->memoffset)
+ || addrp->memoffset->constblock.Const.ci)
+ || addrp->Field || addrp->isarray || addrp->vstg == STGLENG)
+ {
+ p1_big_addr (addrp);
+ return;
+ }
+
+/* Write out a level of indirection for non-array arguments, which have
+ addrp -> memoffset set and are handled by p1_big_addr().
+ Lengths are passed by value, so don't check STGLENG
+ 28-Jun-89 (dmg) Added the check for != TYCHAR
+ */
+
+ if (oneof_stg ( addrp -> uname_tag == UNAM_NAME ? addrp -> user.name : NULL,
+ stg, M(STGARG)|M(STGEQUIV)) && addrp->vtype != TYCHAR) {
+ p1putdd (P1_EXPR, OPWHATSIN, addrp -> vtype);
+ p1_expr (ENULL); /* Put dummy vleng */
+ } /* if stg == STGARG */
+
+ switch (addrp -> uname_tag) {
+ case UNAM_NAME:
+ p1_name (addrp -> user.name);
+ break;
+ case UNAM_IDENT:
+ p1putdds(P1_IDENT, addrp->vtype, addrp->vstg,
+ addrp->user.ident);
+ break;
+ case UNAM_CHARP:
+ p1putdds(P1_CHARP, addrp->vtype, addrp->vstg,
+ addrp->user.Charp);
+ break;
+ case UNAM_EXTERN:
+ p1putd (P1_EXTERN, (long) addrp -> memno);
+ if (addrp->vclass == CLPROC)
+ extsymtab[addrp->memno].extype = addrp->vtype;
+ break;
+ case UNAM_CONST:
+ if (addrp -> memno != BAD_MEMNO)
+ p1_literal (addrp -> memno);
+ else
+ p1_const((struct Constblock *)addrp);
+ break;
+ case UNAM_UNKNOWN:
+ default:
+ erri ("p1_addr: unknown uname_tag '%d'", addrp -> uname_tag);
+ break;
+ } /* switch */
+} /* p1_addr */
+
+
+ static void
+#ifdef KR_headers
+p1_list(listp)
+ struct Listblock *listp;
+#else
+p1_list(struct Listblock *listp)
+#endif
+{
+ chainp lis;
+ int count = 0;
+
+ if (listp == (struct Listblock *) NULL)
+ return;
+
+/* Count the number of parameters in the list */
+
+ for (lis = listp -> listp; lis; lis = lis -> nextp)
+ count++;
+
+ p1putddd (P1_LIST, listp -> tag, listp -> vtype, count);
+
+ for (lis = listp -> listp; lis; lis = lis -> nextp)
+ p1_expr ((expptr) lis -> datap);
+
+} /* p1_list */
+
+
+ void
+#ifdef KR_headers
+p1_label(lab)
+ long lab;
+#else
+p1_label(long lab)
+#endif
+{
+ if (parstate < INDATA)
+ earlylabs = mkchain((char *)lab, earlylabs);
+ else
+ p1putd (P1_LABEL, lab);
+ }
+
+
+
+ static void
+#ifdef KR_headers
+p1_literal(memno)
+ long memno;
+#else
+p1_literal(long memno)
+#endif
+{
+ p1putd (P1_LITERAL, memno);
+} /* p1_literal */
+
+
+ void
+#ifdef KR_headers
+p1_if(expr)
+ expptr expr;
+#else
+p1_if(expptr expr)
+#endif
+{
+ p1put (P1_IF);
+ p1_expr (expr);
+} /* p1_if */
+
+
+
+
+ void
+#ifdef KR_headers
+p1_elif(expr)
+ expptr expr;
+#else
+p1_elif(expptr expr)
+#endif
+{
+ p1put (P1_ELIF);
+ p1_expr (expr);
+} /* p1_elif */
+
+
+
+
+ void
+p1_else(Void)
+{
+ p1put (P1_ELSE);
+} /* p1_else */
+
+
+
+
+ void
+p1_endif(Void)
+{
+ p1put (P1_ENDIF);
+} /* p1_endif */
+
+
+
+
+ void
+p1else_end(Void)
+{
+ p1put (P1_ENDELSE);
+} /* p1else_end */
+
+
+ static void
+#ifdef KR_headers
+p1_big_addr(addrp)
+ Addrp addrp;
+#else
+p1_big_addr(Addrp addrp)
+#endif
+{
+ if (addrp == (Addrp) NULL)
+ return;
+
+ p1putn (P1_ADDR, (int)sizeof(struct Addrblock), (char *) addrp);
+ p1_expr (addrp -> vleng);
+ p1_expr (addrp -> memoffset);
+ if (addrp->uname_tag == UNAM_NAME)
+ addrp->user.name->visused = 1;
+} /* p1_big_addr */
+
+
+
+ static void
+#ifdef KR_headers
+p1_unary(e)
+ struct Exprblock *e;
+#else
+p1_unary(struct Exprblock *e)
+#endif
+{
+ if (e == (struct Exprblock *) NULL)
+ return;
+
+ p1putdd (P1_EXPR, (int) e -> opcode, e -> vtype);
+ p1_expr (e -> vleng);
+
+ switch (e -> opcode) {
+ case OPNEG:
+ case OPNEG1:
+ case OPNOT:
+ case OPABS:
+ case OPBITNOT:
+ case OPPREINC:
+ case OPPREDEC:
+ case OPADDR:
+ case OPIDENTITY:
+ case OPCHARCAST:
+ case OPDABS:
+ p1_expr(e -> leftp);
+ break;
+ default:
+ erri ("p1_unary: bad opcode '%d'", (int) e -> opcode);
+ break;
+ } /* switch */
+
+} /* p1_unary */
+
+
+ static void
+#ifdef KR_headers
+p1_binary(e)
+ struct Exprblock *e;
+#else
+p1_binary(struct Exprblock *e)
+#endif
+{
+ if (e == (struct Exprblock *) NULL)
+ return;
+
+ p1putdd (P1_EXPR, e -> opcode, e -> vtype);
+ p1_expr (e -> vleng);
+ p1_expr (e -> leftp);
+ p1_expr (e -> rightp);
+} /* p1_binary */
+
+
+ void
+#ifdef KR_headers
+p1_head(class, name)
+ int class;
+ char *name;
+#else
+p1_head(int class, char *name)
+#endif
+{
+ p1putds (P1_HEAD, class, name ? name : "");
+} /* p1_head */
+
+
+ void
+#ifdef KR_headers
+p1_subr_ret(retexp)
+ expptr retexp;
+#else
+p1_subr_ret(expptr retexp)
+#endif
+{
+
+ p1put (P1_SUBR_RET);
+ p1_expr (cpexpr(retexp));
+} /* p1_subr_ret */
+
+
+
+ void
+#ifdef KR_headers
+p1comp_goto(index, count, labels)
+ expptr index;
+ int count;
+ struct Labelblock **labels;
+#else
+p1comp_goto(expptr index, int count, struct Labelblock **labels)
+#endif
+{
+ struct Constblock c;
+ int i;
+ register struct Labelblock *L;
+
+ p1put (P1_COMP_GOTO);
+ p1_expr (index);
+
+/* Write out a P1_LIST directly, to avoid the overhead of allocating a
+ list before it's needed HACK HACK HACK */
+
+ p1putddd (P1_LIST, TLIST, TYUNKNOWN, count);
+ c.vtype = TYLONG;
+ c.vleng = 0;
+
+ for (i = 0; i < count; i++) {
+ L = labels[i];
+ L->labused = 1;
+ c.Const.ci = L->stateno;
+ p1_const(&c);
+ } /* for i = 0 */
+} /* p1comp_goto */
+
+
+
+ void
+#ifdef KR_headers
+p1_for(init, test, inc)
+ expptr init;
+ expptr test;
+ expptr inc;
+#else
+p1_for(expptr init, expptr test, expptr inc)
+#endif
+{
+ p1put (P1_FOR);
+ p1_expr (init);
+ p1_expr (test);
+ p1_expr (inc);
+} /* p1_for */
+
+
+ void
+p1for_end(Void)
+{
+ p1put (P1_ENDFOR);
+} /* p1for_end */
+
+
+
+
+/* ----------------------------------------------------------------------
+ The intermediate file actually gets written ONLY by the routines below.
+ To change the format of the file, you need only change these routines.
+ ----------------------------------------------------------------------
+*/
+
+
+/* p1puts -- Put a typed string into the Pass 1 intermediate file. Assumes that
+ str contains no newlines and is null-terminated. */
+
+ void
+#ifdef KR_headers
+p1puts(type, str)
+ int type;
+ char *str;
+#else
+p1puts(int type, char *str)
+#endif
+{
+ fprintf (pass1_file, "%d: %s\n", type, str);
+} /* p1puts */
+
+
+/* p1putd -- Put a typed integer into the Pass 1 intermediate file. */
+
+ static void
+#ifdef KR_headers
+p1putd(type, value)
+ int type;
+ long value;
+#else
+p1putd(int type, long value)
+#endif
+{
+ fprintf (pass1_file, "%d: %ld\n", type, value);
+} /* p1_putd */
+
+
+/* p1putdd -- Put a typed pair of integers into the intermediate file. */
+
+ static void
+#ifdef KR_headers
+p1putdd(type, v1, v2)
+ int type;
+ int v1;
+ int v2;
+#else
+p1putdd(int type, int v1, int v2)
+#endif
+{
+ fprintf (pass1_file, "%d: %d %d\n", type, v1, v2);
+} /* p1putdd */
+
+
+/* p1putddd -- Put a typed triple of integers into the intermediate file. */
+
+ static void
+#ifdef KR_headers
+p1putddd(type, v1, v2, v3)
+ int type;
+ int v1;
+ int v2;
+ int v3;
+#else
+p1putddd(int type, int v1, int v2, int v3)
+#endif
+{
+ fprintf (pass1_file, "%d: %d %d %d\n", type, v1, v2, v3);
+} /* p1putddd */
+
+ union dL {
+ double d;
+ long L[2];
+ };
+
+ static void
+#ifdef KR_headers
+p1putn(type, count, str)
+ int type;
+ int count;
+ char *str;
+#else
+p1putn(int type, int count, char *str)
+#endif
+{
+ int i;
+
+ fprintf (pass1_file, "%d: ", type);
+
+ for (i = 0; i < count; i++)
+ putc (str[i], pass1_file);
+
+ putc ('\n', pass1_file);
+} /* p1putn */
+
+
+
+/* p1put -- Put a type marker into the intermediate file. */
+
+ void
+#ifdef KR_headers
+p1put(type)
+ int type;
+#else
+p1put(int type)
+#endif
+{
+ fprintf (pass1_file, "%d:\n", type);
+} /* p1put */
+
+
+
+ static void
+#ifdef KR_headers
+p1putds(type, i, str)
+ int type;
+ int i;
+ char *str;
+#else
+p1putds(int type, int i, char *str)
+#endif
+{
+ fprintf (pass1_file, "%d: %d %s\n", type, i, str);
+} /* p1putds */
+
+
+ static void
+#ifdef KR_headers
+p1putdds(token, type, stg, str)
+ int token;
+ int type;
+ int stg;
+ char *str;
+#else
+p1putdds(int token, int type, int stg, char *str)
+#endif
+{
+ fprintf (pass1_file, "%d: %d %d %s\n", token, type, stg, str);
+} /* p1putdds */
diff --git a/usr.bin/f2c/parse.h b/usr.bin/f2c/parse.h
new file mode 100644
index 0000000..6de2399
--- /dev/null
+++ b/usr.bin/f2c/parse.h
@@ -0,0 +1,47 @@
+#ifndef PARSE_INCLUDE
+#define PARSE_INCLUDE
+
+/* macros for the parse_args routine */
+
+#define P_STRING 1 /* Macros for the result_type attribute */
+#define P_CHAR 2
+#define P_SHORT 3
+#define P_INT 4
+#define P_LONG 5
+#define P_FILE 6
+#define P_OLD_FILE 7
+#define P_NEW_FILE 8
+#define P_FLOAT 9
+#define P_DOUBLE 10
+
+#define P_CASE_INSENSITIVE 01 /* Macros for the flags attribute */
+#define P_REQUIRED_PREFIX 02
+
+#define P_NO_ARGS 0 /* Macros for the arg_count attribute */
+#define P_ONE_ARG 1
+#define P_INFINITE_ARGS 2
+
+#define p_entry(pref,swit,flag,count,type,store,size) \
+ { (pref), (swit), (flag), (count), (type), (int *) (store), (size) }
+
+typedef struct {
+ char *prefix;
+ char *string;
+ int flags;
+ int count;
+ int result_type;
+ int *result_ptr;
+ int table_size;
+} arg_info;
+
+#ifdef KR_headers
+#define Argdcl(x) ()
+#else
+#define Argdcl(x) x
+#endif
+int arg_verify Argdcl((char**, arg_info*, int));
+void init_store Argdcl((arg_info*, int));
+int match_table Argdcl((char*, arg_info*, int, int, int*));
+int parse_args Argdcl((int, char**, arg_info*, int, char**, int));
+
+#endif
diff --git a/usr.bin/f2c/parse_args.c b/usr.bin/f2c/parse_args.c
new file mode 100644
index 0000000..98468e2
--- /dev/null
+++ b/usr.bin/f2c/parse_args.c
@@ -0,0 +1,542 @@
+/****************************************************************
+Copyright 1990, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* parse_args
+
+ This function will parse command line input into appropriate data
+ structures, output error messages when appropriate and provide some
+ minimal type conversion.
+
+ Input to the function consists of the standard argc,argv
+ values, and a table which directs the parser. Each table entry has the
+ following components:
+
+ prefix -- the (optional) switch character string, e.g. "-" "/" "="
+ switch -- the command string, e.g. "o" "data" "file" "F"
+ flags -- control flags, e.g. CASE_INSENSITIVE, REQUIRED_PREFIX
+ arg_count -- number of arguments this command requires, e.g. 0 for
+ booleans, 1 for filenames, INFINITY for input files
+ result_type -- how to interpret the switch arguments, e.g. STRING,
+ CHAR, FILE, OLD_FILE, NEW_FILE
+ result_ptr -- pointer to storage for the result, be it a table or
+ a string or whatever
+ table_size -- if the arguments fill a table, the maximum number of
+ entries; if there are no arguments, the value to
+ load into the result storage
+
+ Although the table can be used to hold a list of filenames, only
+ scalar values (e.g. pointers) can be stored in the table. No vector
+ processing will be done, only pointers to string storage will be moved.
+
+ An example entry, which could be used to parse input filenames, is:
+
+ "-", "o", 0, oo, OLD_FILE, infilenames, INFILE_TABLE_SIZE
+
+*/
+
+#include <stdio.h>
+#ifndef NULL
+/* ANSI C */
+#include <stddef.h>
+#endif
+#ifdef KR_headers
+extern double atof();
+#else
+#include "stdlib.h"
+#include "string.h"
+#endif
+#include "parse.h"
+#include <math.h> /* For atof */
+#include <ctype.h>
+
+#define MAX_INPUT_SIZE 1000
+
+#define arg_prefix(x) ((x).prefix)
+#define arg_string(x) ((x).string)
+#define arg_flags(x) ((x).flags)
+#define arg_count(x) ((x).count)
+#define arg_result_type(x) ((x).result_type)
+#define arg_result_ptr(x) ((x).result_ptr)
+#define arg_table_size(x) ((x).table_size)
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+typedef int boolean;
+
+
+static char *this_program = "";
+
+static int arg_parse Argdcl((char*, arg_info*));
+static char *lower_string Argdcl((char*, char*));
+static int match Argdcl((char*, char*, arg_info*, boolean));
+static int put_one_arg Argdcl((int, char*, char**, char*, char*));
+
+
+ boolean
+#ifdef KR_headers
+parse_args(argc, argv, table, entries, others, other_count)
+ int argc;
+ char **argv;
+ arg_info *table;
+ int entries;
+ char **others;
+ int other_count;
+#else
+parse_args(int argc, char **argv, arg_info *table, int entries, char **others, int other_count)
+#endif
+{
+ boolean result;
+
+ if (argv)
+ this_program = argv[0];
+
+/* Check the validity of the table and its parameters */
+
+ result = arg_verify (argv, table, entries);
+
+/* Initialize the storage values */
+
+ init_store (table, entries);
+
+ if (result) {
+ boolean use_prefix = TRUE;
+ char *argv0;
+
+ argc--;
+ argv0 = *++argv;
+ while (argc) {
+ int index, length;
+
+ index = match_table (*argv, table, entries, use_prefix, &length);
+ if (index < 0) {
+
+/* The argument doesn't match anything in the table */
+
+ if (others) {
+
+ if (*argv > argv0)
+ *--*argv = '-'; /* complain at invalid flag */
+
+ if (other_count > 0) {
+ *others++ = *argv;
+ other_count--;
+ } else {
+ fprintf (stderr, "%s: too many parameters: ",
+ this_program);
+ fprintf (stderr, "'%s' ignored\n", *argv);
+ } /* else */
+ } /* if (others) */
+ argv0 = *++argv;
+ argc--;
+ } else {
+
+/* A match was found */
+
+ if (length >= strlen (*argv)) {
+ argc--;
+ argv0 = *++argv;
+ use_prefix = TRUE;
+ } else {
+ (*argv) += length;
+ use_prefix = FALSE;
+ } /* else */
+
+/* Parse any necessary arguments */
+
+ if (arg_count (table[index]) != P_NO_ARGS) {
+
+/* Now length will be used to store the number of parsed characters */
+
+ length = arg_parse(*argv, &table[index]);
+ if (*argv == NULL)
+ argc = 0;
+ else if (length >= strlen (*argv)) {
+ argc--;
+ argv0 = *++argv;
+ use_prefix = TRUE;
+ } else {
+ (*argv) += length;
+ use_prefix = FALSE;
+ } /* else */
+ } /* if (argv_count != P_NO_ARGS) */
+ else
+ *arg_result_ptr(table[index]) =
+ arg_table_size(table[index]);
+ } /* else */
+ } /* while (argc) */
+ } /* if (result) */
+
+ return result;
+} /* parse_args */
+
+
+ boolean
+#ifdef KR_headers
+arg_verify(argv, table, entries)
+ char **argv;
+ arg_info *table;
+ int entries;
+#else
+arg_verify(char **argv, arg_info *table, int entries)
+#endif
+{
+ int i;
+ char *this_program = "";
+
+ if (argv)
+ this_program = argv[0];
+
+ for (i = 0; i < entries; i++) {
+ arg_info *arg = &table[i];
+
+/* Check the argument flags */
+
+ if (arg_flags (*arg) & ~(P_CASE_INSENSITIVE | P_REQUIRED_PREFIX)) {
+ fprintf (stderr, "%s [arg_verify]: too many ", this_program);
+ fprintf (stderr, "flags in entry %d: '%x' (hex)\n", i,
+ arg_flags (*arg));
+ } /* if */
+
+/* Check the argument count */
+
+ { int count = arg_count (*arg);
+
+ if (count != P_NO_ARGS && count != P_ONE_ARG && count !=
+ P_INFINITE_ARGS) {
+ fprintf (stderr, "%s [arg_verify]: invalid ", this_program);
+ fprintf (stderr, "argument count in entry %d: '%d'\n", i,
+ count);
+ } /* if count != P_NO_ARGS ... */
+
+/* Check the result field; want to be able to store results */
+
+ else
+ if (arg_result_ptr (*arg) == (int *) NULL) {
+ fprintf (stderr, "%s [arg_verify]: ", this_program);
+ fprintf (stderr, "no argument storage given for ");
+ fprintf (stderr, "entry %d\n", i);
+ } /* if arg_result_ptr */
+ }
+
+/* Check the argument type */
+
+ { int type = arg_result_type (*arg);
+
+ if (type < P_STRING || type > P_DOUBLE)
+ fprintf(stderr,
+ "%s [arg_verify]: bad arg type in entry %d: '%d'\n",
+ this_program, i, type);
+ }
+
+/* Check table size */
+
+ { int size = arg_table_size (*arg);
+
+ if (arg_count (*arg) == P_INFINITE_ARGS && size < 1) {
+ fprintf (stderr, "%s [arg_verify]: bad ", this_program);
+ fprintf (stderr, "table size in entry %d: '%d'\n", i,
+ size);
+ } /* if (arg_count == P_INFINITE_ARGS && size < 1) */
+ }
+
+ } /* for i = 0 */
+
+ return TRUE;
+} /* arg_verify */
+
+
+/* match_table -- returns the index of the best entry matching the input,
+ -1 if no match. The best match is the one of longest length which
+ appears lowest in the table. The length of the match will be returned
+ in length ONLY IF a match was found. */
+
+ int
+#ifdef KR_headers
+match_table(norm_input, table, entries, use_prefix, length)
+ register char *norm_input;
+ arg_info *table;
+ int entries;
+ boolean use_prefix;
+ int *length;
+#else
+match_table(register char *norm_input, arg_info *table, int entries, boolean use_prefix, int *length)
+#endif
+{
+ char low_input[MAX_INPUT_SIZE];
+ register int i;
+ int best_index = -1, best_length = 0;
+
+/* FUNCTION BODY */
+
+ (void) lower_string (low_input, norm_input);
+
+ for (i = 0; i < entries; i++) {
+ int this_length = match(norm_input, low_input, &table[i], use_prefix);
+
+ if (this_length > best_length) {
+ best_index = i;
+ best_length = this_length;
+ } /* if (this_length > best_length) */
+ } /* for (i = 0) */
+
+ if (best_index > -1 && length != (int *) NULL)
+ *length = best_length;
+
+ return best_index;
+} /* match_table */
+
+
+/* match -- takes an input string and table entry, and returns the length
+ of the longer match.
+
+ 0 ==> input doesn't match
+
+ For example:
+
+ INPUT PREFIX STRING RESULT
+----------------------------------------------------------------------
+ "abcd" "-" "d" 0
+ "-d" "-" "d" 2 (i.e. "-d")
+ "dout" "-" "d" 1 (i.e. "d")
+ "-d" "" "-d" 2 (i.e. "-d")
+ "dd" "d" "d" 2 <= here's the weird one
+*/
+
+ static int
+#ifdef KR_headers
+match(norm_input, low_input, entry, use_prefix)
+ char *norm_input;
+ char *low_input;
+ arg_info *entry;
+ boolean use_prefix;
+#else
+match(char *norm_input, char *low_input, arg_info *entry, boolean use_prefix)
+#endif
+{
+ char *norm_prefix = arg_prefix (*entry);
+ char *norm_string = arg_string (*entry);
+ boolean prefix_match = FALSE, string_match = FALSE;
+ int result = 0;
+
+/* Buffers for the lowercased versions of the strings being compared.
+ These are used when the switch is to be case insensitive */
+
+ static char low_prefix[MAX_INPUT_SIZE];
+ static char low_string[MAX_INPUT_SIZE];
+ int prefix_length = strlen (norm_prefix);
+ int string_length = strlen (norm_string);
+
+/* Pointers for the required strings (lowered or nonlowered) */
+
+ register char *input, *prefix, *string;
+
+/* FUNCTION BODY */
+
+/* Use the appropriate strings to handle case sensitivity */
+
+ if (arg_flags (*entry) & P_CASE_INSENSITIVE) {
+ input = low_input;
+ prefix = lower_string (low_prefix, norm_prefix);
+ string = lower_string (low_string, norm_string);
+ } else {
+ input = norm_input;
+ prefix = norm_prefix;
+ string = norm_string;
+ } /* else */
+
+/* First, check the string formed by concatenating the prefix onto the
+ switch string, but only when the prefix is not being ignored */
+
+ if (use_prefix && prefix != NULL && *prefix != '\0')
+ prefix_match = (strncmp (input, prefix, prefix_length) == 0) &&
+ (strncmp (input + prefix_length, string, string_length) == 0);
+
+/* Next, check just the switch string, if that's allowed */
+
+ if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0)
+ string_match = strncmp (input, string, string_length) == 0;
+
+ if (prefix_match)
+ result = prefix_length + string_length;
+ else if (string_match)
+ result = string_length;
+
+ return result;
+} /* match */
+
+
+ static char *
+#ifdef KR_headers
+lower_string(dest, src)
+ char *dest;
+ char *src;
+#else
+lower_string(char *dest, char *src)
+#endif
+{
+ char *result = dest;
+ register int c;
+
+ if (dest == NULL || src == NULL)
+ result = NULL;
+ else
+ while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c);
+
+ return result;
+} /* lower_string */
+
+
+/* arg_parse -- returns the number of characters parsed for this entry */
+
+ static int
+#ifdef KR_headers
+arg_parse(str, entry)
+ char *str;
+ arg_info *entry;
+#else
+arg_parse(char *str, arg_info *entry)
+#endif
+{
+ int length = 0;
+
+ if (arg_count (*entry) == P_ONE_ARG) {
+ char **store = (char **) arg_result_ptr (*entry);
+
+ length = put_one_arg (arg_result_type (*entry), str, store,
+ arg_prefix (*entry), arg_string (*entry));
+
+ } /* if (arg_count == P_ONE_ARG) */
+ else { /* Must be a table of arguments */
+ char **store = (char **) arg_result_ptr (*entry);
+
+ if (store) {
+ while (*store)
+ store++;
+
+ length = put_one_arg(arg_result_type (*entry), str, store++,
+ arg_prefix (*entry), arg_string (*entry));
+
+ *store = (char *) NULL;
+ } /* if (store) */
+ } /* else */
+
+ return length;
+} /* arg_parse */
+
+
+ static int
+#ifdef KR_headers
+put_one_arg(type, str, store, prefix, string)
+ int type;
+ char *str;
+ char **store;
+ char *prefix;
+ char *string;
+#else
+put_one_arg(int type, char *str, char **store, char *prefix, char *string)
+#endif
+{
+ int length = 0;
+ long L;
+
+ if (store) {
+ switch (type) {
+ case P_STRING:
+ case P_FILE:
+ case P_OLD_FILE:
+ case P_NEW_FILE:
+ *store = str;
+ if (str == NULL)
+ fprintf (stderr, "%s: Missing argument after '%s%s'\n",
+ this_program, prefix, string);
+ length = str ? strlen (str) : 0;
+ break;
+ case P_CHAR:
+ *((char *) store) = *str;
+ length = 1;
+ break;
+ case P_SHORT:
+ L = atol(str);
+ *(short *)store = (short) L;
+ if (L != *(short *)store)
+ fprintf(stderr,
+ "%s%s parameter '%ld' is not a SHORT INT (truncating to %d)\n",
+ prefix, string, L, *(short *)store);
+ length = strlen (str);
+ break;
+ case P_INT:
+ L = atol(str);
+ *(int *)store = (int)L;
+ if (L != *(int *)store)
+ fprintf(stderr,
+ "%s%s parameter '%ld' is not an INT (truncating to %d)\n",
+ prefix, string, L, *(int *)store);
+ length = strlen (str);
+ break;
+ case P_LONG:
+ *(long *)store = atol(str);
+ length = strlen (str);
+ break;
+ case P_FLOAT:
+ *((float *) store) = (float) atof(str);
+ length = strlen (str);
+ break;
+ case P_DOUBLE:
+ *((double *) store) = (double) atof(str);
+ length = strlen (str);
+ break;
+ default:
+ fprintf (stderr, "put_one_arg: bad type '%d'\n",
+ type);
+ break;
+ } /* switch */
+ } /* if (store) */
+
+ return length;
+} /* put_one_arg */
+
+
+ void
+#ifdef KR_headers
+init_store(table, entries)
+ arg_info *table;
+ int entries;
+#else
+init_store(arg_info *table, int entries)
+#endif
+{
+ int index;
+
+ for (index = 0; index < entries; index++)
+ if (arg_count (table[index]) == P_INFINITE_ARGS) {
+ char **place = (char **) arg_result_ptr (table[index]);
+
+ if (place)
+ *place = (char *) NULL;
+ } /* if arg_count == P_INFINITE_ARGS */
+
+} /* init_store */
diff --git a/usr.bin/f2c/pccdefs.h b/usr.bin/f2c/pccdefs.h
new file mode 100644
index 0000000..bde8117
--- /dev/null
+++ b/usr.bin/f2c/pccdefs.h
@@ -0,0 +1,64 @@
+/* The following numbers are strange, and implementation-dependent */
+
+#define P2BAD -1
+#define P2NAME 2
+#define P2ICON 4 /* Integer constant */
+#define P2PLUS 6
+#define P2PLUSEQ 7
+#define P2MINUS 8
+#define P2NEG 10
+#define P2STAR 11
+#define P2STAREQ 12
+#define P2INDIRECT 13
+#define P2BITAND 14
+#define P2BITOR 17
+#define P2BITXOR 19
+#define P2QUEST 21
+#define P2COLON 22
+#define P2ANDAND 23
+#define P2OROR 24
+#define P2GOTO 37
+#define P2LISTOP 56
+#define P2ASSIGN 58
+#define P2COMOP 59
+#define P2SLASH 60
+#define P2MOD 62
+#define P2LSHIFT 64
+#define P2RSHIFT 66
+#define P2CALL 70
+#define P2CALL0 72
+
+#define P2NOT 76
+#define P2BITNOT 77
+#define P2EQ 80
+#define P2NE 81
+#define P2LE 82
+#define P2LT 83
+#define P2GE 84
+#define P2GT 85
+#define P2REG 94
+#define P2OREG 95
+#define P2CONV 104
+#define P2FORCE 108
+#define P2CBRANCH 109
+
+/* special operators included only for fortran's use */
+
+#define P2PASS 200
+#define P2STMT 201
+#define P2SWITCH 202
+#define P2LBRACKET 203
+#define P2RBRACKET 204
+#define P2EOF 205
+#define P2ARIF 206
+#define P2LABEL 207
+
+#define P2SHORT 3
+#define P2INT 4
+#define P2LONG 4
+
+#define P2CHAR 2
+#define P2REAL 6
+#define P2DREAL 7
+#define P2PTR 020
+#define P2FUNCT 040
diff --git a/usr.bin/f2c/permission b/usr.bin/f2c/permission
new file mode 100644
index 0000000..cdee9a2
--- /dev/null
+++ b/usr.bin/f2c/permission
@@ -0,0 +1,23 @@
+/****************************************************************
+Copyright 1990 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
diff --git a/usr.bin/f2c/pread.c b/usr.bin/f2c/pread.c
new file mode 100644
index 0000000..f9cef59
--- /dev/null
+++ b/usr.bin/f2c/pread.c
@@ -0,0 +1,990 @@
+/****************************************************************
+Copyright 1990, 1992, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+
+ static char Ptok[128], Pct[Table_size];
+ static char *Pfname;
+ static long Plineno;
+ static int Pbad;
+ static int *tfirst, *tlast, *tnext, tmax;
+
+#define P_space 1
+#define P_anum 2
+#define P_delim 3
+#define P_slash 4
+
+#define TGULP 100
+
+ static void
+trealloc(Void)
+{
+ int k = tmax;
+ tfirst = (int *)realloc((char *)tfirst,
+ (tmax += TGULP)*sizeof(int));
+ if (!tfirst) {
+ fprintf(stderr,
+ "Pfile: realloc failure!\n");
+ exit(2);
+ }
+ tlast = tfirst + tmax;
+ tnext = tfirst + k;
+ }
+
+ static void
+#ifdef KR_headers
+badchar(c)
+ int c;
+#else
+badchar(int c)
+#endif
+{
+ fprintf(stderr,
+ "unexpected character 0x%.2x = '%c' on line %ld of %s\n",
+ c, c, Plineno, Pfname);
+ exit(2);
+ }
+
+ static void
+bad_type(Void)
+{
+ fprintf(stderr,
+ "unexpected type \"%s\" on line %ld of %s\n",
+ Ptok, Plineno, Pfname);
+ exit(2);
+ }
+
+ static void
+#ifdef KR_headers
+badflag(tname, option)
+ char *tname;
+ char *option;
+#else
+badflag(char *tname, char *option)
+#endif
+{
+ fprintf(stderr, "%s type from `f2c -%s` on line %ld of %s\n",
+ tname, option, Plineno, Pfname);
+ Pbad++;
+ }
+
+ static void
+#ifdef KR_headers
+detected(msg)
+ char *msg;
+#else
+detected(char *msg)
+#endif
+{
+ fprintf(stderr,
+ "%sdetected on line %ld of %s\n", msg, Plineno, Pfname);
+ Pbad++;
+ }
+
+#if 0
+ static void
+#ifdef KR_headers
+checklogical(k)
+ int k;
+#else
+checklogical(int k)
+#endif
+{
+ static int lastmsg = 0;
+ static int seen[2] = {0,0};
+
+ seen[k] = 1;
+ if (seen[1-k]) {
+ if (lastmsg < 3) {
+ lastmsg = 3;
+ detected(
+ "Illegal combination of LOGICAL types -- mixing -I4 with -I2 or -i2\n\t");
+ }
+ return;
+ }
+ if (k) {
+ if (tylogical == TYLONG || lastmsg >= 2)
+ return;
+ if (!lastmsg) {
+ lastmsg = 2;
+ badflag("LOGICAL", "I4");
+ }
+ }
+ else {
+ if (tylogical == TYSHORT || lastmsg & 1)
+ return;
+ if (!lastmsg) {
+ lastmsg = 1;
+ badflag("LOGICAL", "i2` or `f2c -I2");
+ }
+ }
+ }
+#else
+#define checklogical(n) /* */
+#endif
+
+ static void
+#ifdef KR_headers
+checkreal(k)
+ int k;
+#else
+checkreal(int k)
+#endif
+{
+ static int warned = 0;
+ static int seen[2] = {0,0};
+
+ seen[k] = 1;
+ if (seen[1-k]) {
+ if (warned < 2)
+ detected("Illegal mixture of -R and -!R ");
+ warned = 2;
+ return;
+ }
+ if (k == forcedouble || warned)
+ return;
+ warned = 1;
+ badflag("REAL return", k ? "!R" : "R");
+ }
+
+ static void
+#ifdef KR_headers
+Pnotboth(e)
+ Extsym *e;
+#else
+Pnotboth(Extsym *e)
+#endif
+{
+ if (e->curno)
+ return;
+ Pbad++;
+ e->curno = 1;
+ fprintf(stderr,
+ "%s cannot be both a procedure and a common block (line %ld of %s)\n",
+ e->fextname, Plineno, Pfname);
+ }
+
+ static int
+#ifdef KR_headers
+numread(pf, n)
+ register FILE *pf;
+ int *n;
+#else
+numread(register FILE *pf, int *n)
+#endif
+{
+ register int c, k;
+
+ if ((c = getc(pf)) < '0' || c > '9')
+ return c;
+ k = c - '0';
+ for(;;) {
+ if ((c = getc(pf)) == ' ') {
+ *n = k;
+ return c;
+ }
+ if (c < '0' || c > '9')
+ break;
+ k = 10*k + c - '0';
+ }
+ return c;
+ }
+
+ static void argverify Argdcl((int, Extsym*));
+ static void Pbadret Argdcl((int ftype, Extsym *p));
+
+ static int
+#ifdef KR_headers
+readref(pf, e, ftype)
+ register FILE *pf;
+ Extsym *e;
+ int ftype;
+#else
+readref(register FILE *pf, Extsym *e, int ftype)
+#endif
+{
+ register int c, *t;
+ int i, nargs, type;
+ Argtypes *at;
+ Atype *a, *ae;
+
+ if (ftype > TYSUBR)
+ return 0;
+ if ((c = numread(pf, &nargs)) != ' ') {
+ if (c != ':')
+ return c == EOF;
+ /* just a typed external */
+ if (e->extstg == STGUNKNOWN) {
+ at = 0;
+ goto justsym;
+ }
+ if (e->extstg == STGEXT) {
+ if (e->extype != ftype)
+ Pbadret(ftype, e);
+ }
+ else
+ Pnotboth(e);
+ return 0;
+ }
+
+ tnext = tfirst;
+ for(i = 0; i < nargs; i++) {
+ if ((c = numread(pf, &type)) != ' '
+ || type >= 500
+ || type != TYFTNLEN + 100 && type % 100 > TYSUBR)
+ return c == EOF;
+ if (tnext >= tlast)
+ trealloc();
+ *tnext++ = type;
+ }
+
+ if (e->extstg == STGUNKNOWN) {
+ save_at:
+ at = (Argtypes *)
+ gmem(sizeof(Argtypes) + (nargs-1)*sizeof(Atype), 1);
+ at->dnargs = at->nargs = nargs;
+ at->changes = 0;
+ t = tfirst;
+ a = at->atypes;
+ for(ae = a + nargs; a < ae; a++) {
+ a->type = *t++;
+ a->cp = 0;
+ }
+ justsym:
+ e->extstg = STGEXT;
+ e->extype = ftype;
+ e->arginfo = at;
+ }
+ else if (e->extstg != STGEXT) {
+ Pnotboth(e);
+ }
+ else if (!e->arginfo) {
+ if (e->extype != ftype)
+ Pbadret(ftype, e);
+ else
+ goto save_at;
+ }
+ else
+ argverify(ftype, e);
+ return 0;
+ }
+
+ static int
+#ifdef KR_headers
+comlen(pf)
+ register FILE *pf;
+#else
+comlen(register FILE *pf)
+#endif
+{
+ register int c;
+ register char *s, *se;
+ char buf[128], cbuf[128];
+ int refread;
+ long L;
+ Extsym *e;
+
+ if ((c = getc(pf)) == EOF)
+ return 1;
+ if (c == ' ') {
+ refread = 0;
+ s = "comlen ";
+ }
+ else if (c == ':') {
+ refread = 1;
+ s = "ref: ";
+ }
+ else {
+ ret0:
+ if (c == '*')
+ ungetc(c,pf);
+ return 0;
+ }
+ while(*s) {
+ if ((c = getc(pf)) == EOF)
+ return 1;
+ if (c != *s++)
+ goto ret0;
+ }
+ s = buf;
+ se = buf + sizeof(buf) - 1;
+ for(;;) {
+ if ((c = getc(pf)) == EOF)
+ return 1;
+ if (c == ' ')
+ break;
+ if (s >= se || Pct[c] != P_anum)
+ goto ret0;
+ *s++ = c;
+ }
+ *s-- = 0;
+ if (s <= buf || *s != '_')
+ return 0;
+ strcpy(cbuf,buf);
+ *s-- = 0;
+ if (*s == '_') {
+ *s-- = 0;
+ if (s <= buf)
+ return 0;
+ }
+ for(L = 0;;) {
+ if ((c = getc(pf)) == EOF)
+ return 1;
+ if (c == ' ')
+ break;
+ if (c < '0' && c > '9')
+ goto ret0;
+ L = 10*L + c - '0';
+ }
+ if (!L && !refread)
+ return 0;
+ e = mkext1(buf, cbuf);
+ if (refread)
+ return readref(pf, e, (int)L);
+ if (e->extstg == STGUNKNOWN) {
+ e->extstg = STGCOMMON;
+ e->maxleng = L;
+ }
+ else if (e->extstg != STGCOMMON)
+ Pnotboth(e);
+ else if (e->maxleng != L) {
+ fprintf(stderr,
+ "incompatible lengths for common block %s (line %ld of %s)\n",
+ buf, Plineno, Pfname);
+ if (e->maxleng < L)
+ e->maxleng = L;
+ }
+ return 0;
+ }
+
+ static int
+#ifdef KR_headers
+Ptoken(pf, canend)
+ FILE *pf;
+ int canend;
+#else
+Ptoken(FILE *pf, int canend)
+#endif
+{
+ register int c;
+ register char *s, *se;
+
+ top:
+ for(;;) {
+ c = getc(pf);
+ if (c == EOF) {
+ if (canend)
+ return 0;
+ goto badeof;
+ }
+ if (Pct[c] != P_space)
+ break;
+ if (c == '\n')
+ Plineno++;
+ }
+ switch(Pct[c]) {
+ case P_anum:
+ if (c == '_')
+ badchar(c);
+ s = Ptok;
+ se = s + sizeof(Ptok) - 1;
+ do {
+ if (s < se)
+ *s++ = c;
+ if ((c = getc(pf)) == EOF) {
+ badeof:
+ fprintf(stderr,
+ "unexpected end of file in %s\n",
+ Pfname);
+ exit(2);
+ }
+ }
+ while(Pct[c] == P_anum);
+ ungetc(c,pf);
+ *s = 0;
+ return P_anum;
+
+ case P_delim:
+ return c;
+
+ case P_slash:
+ if ((c = getc(pf)) != '*') {
+ if (c == EOF)
+ goto badeof;
+ badchar('/');
+ }
+ if (canend && comlen(pf))
+ goto badeof;
+ for(;;) {
+ while((c = getc(pf)) != '*') {
+ if (c == EOF)
+ goto badeof;
+ if (c == '\n')
+ Plineno++;
+ }
+ slashseek:
+ switch(getc(pf)) {
+ case '/':
+ goto top;
+ case EOF:
+ goto badeof;
+ case '*':
+ goto slashseek;
+ }
+ }
+ default:
+ badchar(c);
+ }
+ /* NOT REACHED */
+ return 0;
+ }
+
+ static int
+Pftype(Void)
+{
+ switch(Ptok[0]) {
+ case 'C':
+ if (!strcmp(Ptok+1, "_f"))
+ return TYCOMPLEX;
+ break;
+ case 'E':
+ if (!strcmp(Ptok+1, "_f")) {
+ /* TYREAL under forcedouble */
+ checkreal(1);
+ return TYREAL;
+ }
+ break;
+ case 'H':
+ if (!strcmp(Ptok+1, "_f"))
+ return TYCHAR;
+ break;
+ case 'Z':
+ if (!strcmp(Ptok+1, "_f"))
+ return TYDCOMPLEX;
+ break;
+ case 'd':
+ if (!strcmp(Ptok+1, "oublereal"))
+ return TYDREAL;
+ break;
+ case 'i':
+ if (!strcmp(Ptok+1, "nt"))
+ return TYSUBR;
+ if (!strcmp(Ptok+1, "nteger"))
+ return TYLONG;
+ if (!strcmp(Ptok+1, "nteger1"))
+ return TYINT1;
+ break;
+ case 'l':
+ if (!strcmp(Ptok+1, "ogical")) {
+ checklogical(1);
+ return TYLOGICAL;
+ }
+ if (!strcmp(Ptok+1, "ogical1"))
+ return TYLOGICAL1;
+#ifdef TYQUAD
+ if (!strcmp(Ptok+1, "ongint"))
+ return TYQUAD;
+#endif
+ break;
+ case 'r':
+ if (!strcmp(Ptok+1, "eal")) {
+ checkreal(0);
+ return TYREAL;
+ }
+ break;
+ case 's':
+ if (!strcmp(Ptok+1, "hortint"))
+ return TYSHORT;
+ if (!strcmp(Ptok+1, "hortlogical")) {
+ checklogical(0);
+ return TYLOGICAL2;
+ }
+ break;
+ }
+ bad_type();
+ /* NOT REACHED */
+ return 0;
+ }
+
+ static void
+#ifdef KR_headers
+wanted(i, what)
+ int i;
+ char *what;
+#else
+wanted(int i, char *what)
+#endif
+{
+ if (i != P_anum) {
+ Ptok[0] = i;
+ Ptok[1] = 0;
+ }
+ fprintf(stderr,"Error: expected %s, not \"%s\" (line %ld of %s)\n",
+ what, Ptok, Plineno, Pfname);
+ exit(2);
+ }
+
+ static int
+#ifdef KR_headers
+Ptype(pf)
+ FILE *pf;
+#else
+Ptype(FILE *pf)
+#endif
+{
+ int i, rv;
+
+ i = Ptoken(pf,0);
+ if (i == ')')
+ return 0;
+ if (i != P_anum)
+ badchar(i);
+
+ rv = 0;
+ switch(Ptok[0]) {
+ case 'C':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYCOMPLEX+200;
+ break;
+ case 'D':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYDREAL+200;
+ break;
+ case 'E':
+ case 'R':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYREAL+200;
+ break;
+ case 'H':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYCHAR+200;
+ break;
+ case 'I':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYLONG+200;
+ else if (!strcmp(Ptok+1, "1_fp"))
+ rv = TYINT1+200;
+#ifdef TYQUAD
+ else if (!strcmp(Ptok+1, "8_fp"))
+ rv = TYQUAD+200;
+#endif
+ break;
+ case 'J':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYSHORT+200;
+ break;
+ case 'K':
+ checklogical(0);
+ goto Logical;
+ case 'L':
+ checklogical(1);
+ Logical:
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYLOGICAL+200;
+ else if (!strcmp(Ptok+1, "1_fp"))
+ rv = TYLOGICAL1+200;
+ else if (!strcmp(Ptok+1, "2_fp"))
+ rv = TYLOGICAL2+200;
+ break;
+ case 'S':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYSUBR+200;
+ break;
+ case 'U':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYUNKNOWN+300;
+ break;
+ case 'Z':
+ if (!strcmp(Ptok+1, "_fp"))
+ rv = TYDCOMPLEX+200;
+ break;
+ case 'c':
+ if (!strcmp(Ptok+1, "har"))
+ rv = TYCHAR;
+ else if (!strcmp(Ptok+1, "omplex"))
+ rv = TYCOMPLEX;
+ break;
+ case 'd':
+ if (!strcmp(Ptok+1, "oublereal"))
+ rv = TYDREAL;
+ else if (!strcmp(Ptok+1, "oublecomplex"))
+ rv = TYDCOMPLEX;
+ break;
+ case 'f':
+ if (!strcmp(Ptok+1, "tnlen"))
+ rv = TYFTNLEN+100;
+ break;
+ case 'i':
+ if (!strncmp(Ptok+1, "nteger", 6)) {
+ if (!Ptok[7])
+ rv = TYLONG;
+ else if (Ptok[7] == '1' && !Ptok[8])
+ rv = TYINT1;
+ }
+ break;
+ case 'l':
+ if (!strncmp(Ptok+1, "ogical", 6)) {
+ if (!Ptok[7]) {
+ checklogical(1);
+ rv = TYLOGICAL;
+ }
+ else if (Ptok[7] == '1' && !Ptok[8])
+ rv = TYLOGICAL1;
+ }
+#ifdef TYQUAD
+ else if (!strcmp(Ptok+1,"ongint"))
+ rv = TYQUAD;
+#endif
+ break;
+ case 'r':
+ if (!strcmp(Ptok+1, "eal"))
+ rv = TYREAL;
+ break;
+ case 's':
+ if (!strcmp(Ptok+1, "hortint"))
+ rv = TYSHORT;
+ else if (!strcmp(Ptok+1, "hortlogical")) {
+ checklogical(0);
+ rv = TYLOGICAL2;
+ }
+ break;
+ case 'v':
+ if (tnext == tfirst && !strcmp(Ptok+1, "oid")) {
+ if ((i = Ptoken(pf,0)) != /*(*/ ')')
+ wanted(i, /*(*/ "\")\"");
+ return 0;
+ }
+ }
+ if (!rv)
+ bad_type();
+ if (rv < 100 && (i = Ptoken(pf,0)) != '*')
+ wanted(i, "\"*\"");
+ if ((i = Ptoken(pf,0)) == P_anum)
+ i = Ptoken(pf,0); /* skip variable name */
+ switch(i) {
+ case ')':
+ ungetc(i,pf);
+ break;
+ case ',':
+ break;
+ default:
+ wanted(i, "\",\" or \")\"");
+ }
+ return rv;
+ }
+
+ static char *
+trimunder(Void)
+{
+ register char *s;
+ register int n;
+ static char buf[128];
+
+ s = Ptok + strlen(Ptok) - 1;
+ if (*s != '_') {
+ fprintf(stderr,
+ "warning: %s does not end in _ (line %ld of %s)\n",
+ Ptok, Plineno, Pfname);
+ return Ptok;
+ }
+ if (s[-1] == '_')
+ s--;
+ strncpy(buf, Ptok, n = s - Ptok);
+ buf[n] = 0;
+ return buf;
+ }
+
+ static void
+#ifdef KR_headers
+Pbadmsg(msg, p)
+ char *msg;
+ Extsym *p;
+#else
+Pbadmsg(char *msg, Extsym *p)
+#endif
+{
+ Pbad++;
+ fprintf(stderr, "%s for %s (line %ld of %s):\n\t", msg,
+ p->fextname, Plineno, Pfname);
+ p->arginfo->nargs = -1;
+ }
+
+ static void
+#ifdef KR_headers
+Pbadret(ftype, p)
+ int ftype;
+ Extsym *p;
+#else
+Pbadret(int ftype, Extsym *p)
+#endif
+{
+ char buf1[32], buf2[32];
+
+ Pbadmsg("inconsistent types",p);
+ fprintf(stderr, "here %s, previously %s\n",
+ Argtype(ftype+200,buf1),
+ Argtype(p->extype+200,buf2));
+ }
+
+ static void
+#ifdef KR_headers
+argverify(ftype, p)
+ int ftype;
+ Extsym *p;
+#else
+argverify(int ftype, Extsym *p)
+#endif
+{
+ Argtypes *at;
+ register Atype *aty;
+ int i, j, k;
+ register int *t, *te;
+ char buf1[32], buf2[32];
+
+ at = p->arginfo;
+ if (at->nargs < 0)
+ return;
+ if (p->extype != ftype) {
+ Pbadret(ftype, p);
+ return;
+ }
+ t = tfirst;
+ te = tnext;
+ i = te - t;
+ if (at->nargs != i) {
+ j = at->nargs;
+ Pbadmsg("differing numbers of arguments",p);
+ fprintf(stderr, "here %d, previously %d\n",
+ i, j);
+ return;
+ }
+ for(aty = at->atypes; t < te; t++, aty++) {
+ if (*t == aty->type)
+ continue;
+ j = aty->type;
+ k = *t;
+ if (k >= 300 || k == j)
+ continue;
+ if (j >= 300) {
+ if (k >= 200) {
+ if (k == TYUNKNOWN + 200)
+ continue;
+ if (j % 100 != k - 200
+ && k != TYSUBR + 200
+ && j != TYUNKNOWN + 300
+ && !type_fixup(at,aty,k))
+ goto badtypes;
+ }
+ else if (j % 100 % TYSUBR != k % TYSUBR
+ && !type_fixup(at,aty,k))
+ goto badtypes;
+ }
+ else if (k < 200 || j < 200)
+ goto badtypes;
+ else if (k == TYUNKNOWN+200)
+ continue;
+ else if (j != TYUNKNOWN+200)
+ {
+ badtypes:
+ Pbadmsg("differing calling sequences",p);
+ i = t - tfirst + 1;
+ fprintf(stderr,
+ "arg %d: here %s, prevously %s\n",
+ i, Argtype(k,buf1), Argtype(j,buf2));
+ return;
+ }
+ /* We've subsequently learned the right type,
+ as in the call on zoo below...
+
+ subroutine foo(x, zap)
+ external zap
+ call goo(zap)
+ x = zap(3)
+ call zoo(zap)
+ end
+ */
+ aty->type = k;
+ at->changes = 1;
+ }
+ }
+
+ static void
+#ifdef KR_headers
+newarg(ftype, p)
+ int ftype;
+ Extsym *p;
+#else
+newarg(int ftype, Extsym *p)
+#endif
+{
+ Argtypes *at;
+ register Atype *aty;
+ register int *t, *te;
+ int i, k;
+
+ if (p->extstg == STGCOMMON) {
+ Pnotboth(p);
+ return;
+ }
+ p->extstg = STGEXT;
+ p->extype = ftype;
+ p->exproto = 1;
+ t = tfirst;
+ te = tnext;
+ i = te - t;
+ k = sizeof(Argtypes) + (i-1)*sizeof(Atype);
+ at = p->arginfo = (Argtypes *)gmem(k,1);
+ at->dnargs = at->nargs = i;
+ at->defined = at->changes = 0;
+ for(aty = at->atypes; t < te; aty++) {
+ aty->type = *t++;
+ aty->cp = 0;
+ }
+ }
+
+ static int
+#ifdef KR_headers
+Pfile(fname)
+ char *fname;
+#else
+Pfile(char *fname)
+#endif
+{
+ char *s;
+ int ftype, i;
+ FILE *pf;
+ Extsym *p;
+
+ for(s = fname; *s; s++);
+ if (s - fname < 2
+ || s[-2] != '.'
+ || (s[-1] != 'P' && s[-1] != 'p'))
+ return 0;
+
+ if (!(pf = fopen(fname, textread))) {
+ fprintf(stderr, "can't open %s\n", fname);
+ exit(2);
+ }
+ Pfname = fname;
+ Plineno = 1;
+ if (!Pct[' ']) {
+ for(s = " \t\n\r\v\f"; *s; s++)
+ Pct[*s] = P_space;
+ for(s = "*,();"; *s; s++)
+ Pct[*s] = P_delim;
+ for(i = '0'; i <= '9'; i++)
+ Pct[i] = P_anum;
+ for(s = "abcdefghijklmnopqrstuvwxyz"; i = *s; s++)
+ Pct[i] = Pct[i+'A'-'a'] = P_anum;
+ Pct['_'] = P_anum;
+ Pct['/'] = P_slash;
+ }
+
+ for(;;) {
+ if (!(i = Ptoken(pf,1)))
+ break;
+ if (i != P_anum
+ || !strcmp(Ptok, "extern") && (i = Ptoken(pf,0)) != P_anum)
+ badchar(i);
+ ftype = Pftype();
+ getname:
+ if ((i = Ptoken(pf,0)) != P_anum)
+ badchar(i);
+ p = mkext1(trimunder(), Ptok);
+
+ if ((i = Ptoken(pf,0)) != '(')
+ badchar(i);
+ tnext = tfirst;
+ while(i = Ptype(pf)) {
+ if (tnext >= tlast)
+ trealloc();
+ *tnext++ = i;
+ }
+ if (p->arginfo) {
+ argverify(ftype, p);
+ if (p->arginfo->nargs < 0)
+ newarg(ftype, p);
+ }
+ else
+ newarg(ftype, p);
+ p->arginfo->defined = 1;
+ i = Ptoken(pf,0);
+ switch(i) {
+ case ';':
+ break;
+ case ',':
+ goto getname;
+ default:
+ wanted(i, "\";\" or \",\"");
+ }
+ }
+ fclose(pf);
+ return 1;
+ }
+
+ void
+#ifdef KR_headers
+read_Pfiles(ffiles)
+ char **ffiles;
+#else
+read_Pfiles(char **ffiles)
+#endif
+{
+ char **f1files, **f1files0, *s;
+ int k;
+ register Extsym *e, *ee;
+ register Argtypes *at;
+ extern int retcode;
+
+ f1files0 = f1files = ffiles;
+ while(s = *ffiles++)
+ if (!Pfile(s))
+ *f1files++ = s;
+ if (Pbad)
+ retcode = 8;
+ if (tfirst) {
+ free((char *)tfirst);
+ /* following should be unnecessary, as we won't be back here */
+ tfirst = tnext = tlast = 0;
+ tmax = 0;
+ }
+ *f1files = 0;
+ if (f1files == f1files0)
+ f1files[1] = 0;
+
+ k = 0;
+ ee = nextext;
+ for (e = extsymtab; e < ee; e++)
+ if (e->extstg == STGEXT
+ && (at = e->arginfo)) {
+ if (at->nargs < 0 || at->changes)
+ k++;
+ at->changes = 2;
+ }
+ if (k) {
+ fprintf(diagfile,
+ "%d prototype%s updated while reading prototypes.\n", k,
+ k > 1 ? "s" : "");
+ }
+ fflush(diagfile);
+ }
diff --git a/usr.bin/f2c/proc.c b/usr.bin/f2c/proc.c
new file mode 100644
index 0000000..656d9bb
--- /dev/null
+++ b/usr.bin/f2c/proc.c
@@ -0,0 +1,1805 @@
+/****************************************************************
+Copyright 1990, 1994, 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "names.h"
+#include "output.h"
+#include "p1defs.h"
+
+/* round a up to the nearest multiple of b:
+
+ a = b * floor ( (a + (b - 1)) / b )*/
+
+#undef roundup
+#define roundup(a,b) ( b * ( (a+b-1)/b) )
+
+#define EXNULL (union Expression *)0
+
+static void dobss Argdcl((void));
+static void docomleng Argdcl((void));
+static void docommon Argdcl((void));
+static void doentry Argdcl((struct Entrypoint*));
+static void epicode Argdcl((void));
+static int nextarg Argdcl((int));
+static void retval Argdcl((int));
+
+static char Blank[] = BLANKCOMMON;
+
+ static char *postfix[] = { "g", "h", "i",
+#ifdef TYQUAD
+ "j",
+#endif
+ "r", "d", "c", "z", "g", "h", "i" };
+
+ chainp new_procs;
+ int prev_proc, proc_argchanges, proc_protochanges;
+
+ void
+#ifdef KR_headers
+changedtype(q)
+ Namep q;
+#else
+changedtype(Namep q)
+#endif
+{
+ char buf[200];
+ int qtype, type1;
+ register Extsym *e;
+ Argtypes *at;
+
+ if (q->vtypewarned)
+ return;
+ q->vtypewarned = 1;
+ qtype = q->vtype;
+ e = &extsymtab[q->vardesc.varno];
+ if (!(at = e->arginfo)) {
+ if (!e->exused)
+ return;
+ }
+ else if (at->changes & 2 && qtype != TYUNKNOWN && !at->defined)
+ proc_protochanges++;
+ type1 = e->extype;
+ if (type1 == TYUNKNOWN)
+ return;
+ if (qtype == TYUNKNOWN)
+ /* e.g.,
+ subroutine foo
+ end
+ external foo
+ call goo(foo)
+ end
+ */
+ return;
+ sprintf(buf, "%.90s: inconsistent declarations:\n\
+ here %s%s, previously %s%s.", q->fvarname, ftn_types[qtype],
+ qtype == TYSUBR ? "" : " function",
+ ftn_types[type1], type1 == TYSUBR ? "" : " function");
+ warn(buf);
+ }
+
+ void
+#ifdef KR_headers
+unamstring(q, s)
+ register Addrp q;
+ register char *s;
+#else
+unamstring(register Addrp q, register char *s)
+#endif
+{
+ register int k;
+ register char *t;
+
+ k = strlen(s);
+ if (k < IDENT_LEN) {
+ q->uname_tag = UNAM_IDENT;
+ t = q->user.ident;
+ }
+ else {
+ q->uname_tag = UNAM_CHARP;
+ q->user.Charp = t = mem(k+1, 0);
+ }
+ strcpy(t, s);
+ }
+
+ static void
+fix_entry_returns(Void) /* for multiple entry points */
+{
+ Addrp a;
+ int i;
+ struct Entrypoint *e;
+ Namep np;
+
+ e = entries = (struct Entrypoint *)revchain((chainp)entries);
+ allargs = revchain(allargs);
+ if (!multitype)
+ return;
+
+ /* TYLOGICAL should have been turned into TYLONG or TYSHORT by now */
+
+ for(i = TYINT1; i <= TYLOGICAL; i++)
+ if (a = xretslot[i])
+ sprintf(a->user.ident, "(*ret_val).%s",
+ postfix[i-TYINT1]);
+
+ do {
+ np = e->enamep;
+ switch(np->vtype) {
+ case TYINT1:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYREAL:
+ case TYDREAL:
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+ np->vstg = STGARG;
+ }
+ }
+ while(e = e->entnextp);
+ }
+
+ static void
+#ifdef KR_headers
+putentries(outfile)
+ FILE *outfile;
+#else
+putentries(FILE *outfile)
+#endif
+ /* put out wrappers for multiple entries */
+{
+ char base[IDENT_LEN];
+ struct Entrypoint *e;
+ Namep *A, *Ae, *Ae1, **Alp, *a, **a1, np;
+ chainp args, lengths;
+ int i, k, mt, nL, t, type;
+ extern char *dfltarg[], **dfltproc;
+
+ e = entries;
+ if (!e->enamep) /* only possible with erroneous input */
+ return;
+ nL = (nallargs + nallchargs) * sizeof(Namep *);
+ A = (Namep *)ckalloc(nL + nallargs*sizeof(Namep **));
+ Ae = A + nallargs;
+ Alp = (Namep **)(Ae1 = Ae + nallchargs);
+ i = k = 0;
+ for(a1 = Alp, args = allargs; args; a1++, args = args->nextp) {
+ np = (Namep)args->datap;
+ if (np->vtype == TYCHAR && np->vclass != CLPROC)
+ *a1 = &Ae[i++];
+ }
+
+ mt = multitype;
+ multitype = 0;
+ sprintf(base, "%s0_", e->enamep->cvarname);
+ do {
+ np = e->enamep;
+ lengths = length_comp(e, 0);
+ proctype = type = np->vtype;
+ if (protofile)
+ protowrite(protofile, type, np->cvarname, e, lengths);
+ nice_printf(outfile, "\n%s ", c_type_decl(type, 1));
+ nice_printf(outfile, "%s", np->cvarname);
+ if (!Ansi) {
+ listargs(outfile, e, 0, lengths);
+ nice_printf(outfile, "\n");
+ }
+ list_arg_types(outfile, e, lengths, 0, "\n");
+ nice_printf(outfile, "{\n");
+ frchain(&lengths);
+ next_tab(outfile);
+ if (mt)
+ nice_printf(outfile,
+ "Multitype ret_val;\n%s(%d, &ret_val",
+ base, k); /*)*/
+ else if (ISCOMPLEX(type))
+ nice_printf(outfile, "%s(%d,%s", base, k,
+ xretslot[type]->user.ident); /*)*/
+ else if (type == TYCHAR)
+ nice_printf(outfile,
+ "%s(%d, ret_val, ret_val_len", base, k); /*)*/
+ else
+ nice_printf(outfile, "return %s(%d", base, k); /*)*/
+ k++;
+ memset((char *)A, 0, nL);
+ for(args = e->arglist; args; args = args->nextp) {
+ np = (Namep)args->datap;
+ A[np->argno] = np;
+ if (np->vtype == TYCHAR && np->vclass != CLPROC)
+ *Alp[np->argno] = np;
+ }
+ args = allargs;
+ for(a = A; a < Ae; a++, args = args->nextp) {
+ t = ((Namep)args->datap)->vtype;
+ nice_printf(outfile, ", %s", (np = *a)
+ ? np->cvarname
+ : ((Namep)args->datap)->vclass == CLPROC
+ ? dfltproc[((Namep)args->datap)->vimpltype
+ ? (Castargs ? TYUNKNOWN : TYSUBR)
+ : t == TYREAL && forcedouble && !Castargs
+ ? TYDREAL : t]
+ : dfltarg[((Namep)args->datap)->vtype]);
+ }
+ for(; a < Ae1; a++)
+ if (np = *a)
+ nice_printf(outfile, ", %s_len", np->fvarname);
+ else
+ nice_printf(outfile, ", (ftnint)0");
+ nice_printf(outfile, /*(*/ ");\n");
+ if (mt) {
+ if (type == TYCOMPLEX)
+ nice_printf(outfile,
+ "r_v->r = ret_val.c.r; r_v->i = ret_val.c.i;\n");
+ else if (type == TYDCOMPLEX)
+ nice_printf(outfile,
+ "r_v->r = ret_val.z.r; r_v->i = ret_val.z.i;\n");
+ else if (type <= TYLOGICAL)
+ nice_printf(outfile, "return ret_val.%s;\n",
+ postfix[type-TYINT1]);
+ }
+ nice_printf(outfile, "}\n");
+ prev_tab(outfile);
+ }
+ while(e = e->entnextp);
+ free((char *)A);
+ }
+
+ static void
+#ifdef KR_headers
+entry_goto(outfile)
+ FILE *outfile;
+#else
+entry_goto(FILE *outfile)
+#endif
+{
+ struct Entrypoint *e = entries;
+ int k = 0;
+
+ nice_printf(outfile, "switch(n__) {\n");
+ next_tab(outfile);
+ while(e = e->entnextp)
+ nice_printf(outfile, "case %d: goto %s;\n", ++k,
+ user_label((long)(extsymtab - e->entryname - 1)));
+ nice_printf(outfile, "}\n\n");
+ prev_tab(outfile);
+ }
+
+/* start a new procedure */
+
+ void
+newproc(Void)
+{
+ if(parstate != OUTSIDE)
+ {
+ execerr("missing end statement", CNULL);
+ endproc();
+ }
+
+ parstate = INSIDE;
+ procclass = CLMAIN; /* default */
+}
+
+ static void
+zap_changes(Void)
+{
+ register chainp cp;
+ register Argtypes *at;
+
+ /* arrange to get correct count of prototypes that would
+ change by running f2c again */
+
+ if (prev_proc && proc_argchanges)
+ proc_protochanges++;
+ prev_proc = proc_argchanges = 0;
+ for(cp = new_procs; cp; cp = cp->nextp)
+ if (at = ((Namep)cp->datap)->arginfo)
+ at->changes &= ~1;
+ frchain(&new_procs);
+ }
+
+/* end of procedure. generate variables, epilogs, and prologs */
+
+ void
+endproc(Void)
+{
+ struct Labelblock *lp;
+ Extsym *ext;
+
+ if(parstate < INDATA)
+ enddcl();
+ if(ctlstack >= ctls)
+ err("DO loop or BLOCK IF not closed");
+ for(lp = labeltab ; lp < labtabend ; ++lp)
+ if(lp->stateno!=0 && lp->labdefined==NO)
+ errstr("missing statement label %s",
+ convic(lp->stateno) );
+
+/* Save copies of the common variables in extptr -> allextp */
+
+ for (ext = extsymtab; ext < nextext; ext++)
+ if (ext -> extstg == STGCOMMON && ext -> extp) {
+ extern int usedefsforcommon;
+
+/* Write out the abbreviations for common block reference */
+
+ copy_data (ext -> extp);
+ if (usedefsforcommon) {
+ wr_abbrevs (c_file, 1, ext -> extp);
+ ext -> used_here = 1;
+ }
+ else
+ ext -> extp = CHNULL;
+
+ }
+
+ if (nentry > 1)
+ fix_entry_returns();
+ epicode();
+ donmlist();
+ dobss();
+ start_formatting ();
+ if (nentry > 1)
+ putentries(c_file);
+
+ zap_changes();
+ procinit(); /* clean up for next procedure */
+}
+
+
+
+/* End of declaration section of procedure. Allocate storage. */
+
+ void
+enddcl(Void)
+{
+ register struct Entrypoint *ep;
+ struct Entrypoint *ep0;
+ chainp cp;
+ extern char *err_proc;
+ static char comblks[] = "common blocks";
+
+ err_proc = comblks;
+ docommon();
+
+/* Now the hash table entries for fields of common blocks have STGCOMMON,
+ vdcldone, voffset, and varno. And the common blocks themselves have
+ their full sizes in extleng. */
+
+ err_proc = "equivalences";
+ doequiv();
+
+ err_proc = comblks;
+ docomleng();
+
+/* This implies that entry points in the declarations are buffered in
+ entries but not written out */
+
+ err_proc = "entries";
+ if (ep = ep0 = (struct Entrypoint *)revchain((chainp)entries)) {
+ /* entries could be 0 in case of an error */
+ do doentry(ep);
+ while(ep = ep->entnextp);
+ entries = (struct Entrypoint *)revchain((chainp)ep0);
+ }
+
+ err_proc = 0;
+ parstate = INEXEC;
+ p1put(P1_PROCODE);
+ freetemps();
+ if (earlylabs) {
+ for(cp = earlylabs = revchain(earlylabs); cp; cp = cp->nextp)
+ p1_label((long)cp->datap);
+ frchain(&earlylabs);
+ }
+ p1_line_number(lineno); /* for files that start with a MAIN program */
+ /* that starts with an executable statement */
+}
+
+/* ROUTINES CALLED WHEN ENCOUNTERING ENTRY POINTS */
+
+/* Main program or Block data */
+
+ void
+#ifdef KR_headers
+startproc(progname, class)
+ Extsym *progname;
+ int class;
+#else
+startproc(Extsym *progname, int class)
+#endif
+{
+ register struct Entrypoint *p;
+
+ p = ALLOC(Entrypoint);
+ if(class == CLMAIN) {
+ puthead(CNULL, CLMAIN);
+ if (progname)
+ strcpy (main_alias, progname->cextname);
+ } else
+ puthead(CNULL, CLBLOCK);
+ if(class == CLMAIN)
+ newentry( mkname(" MAIN"), 0 )->extinit = 1;
+ p->entryname = progname;
+ entries = p;
+
+ procclass = class;
+ fprintf(diagfile, " %s", (class==CLMAIN ? "MAIN" : "BLOCK DATA") );
+ if(progname) {
+ fprintf(diagfile, " %s", progname->fextname);
+ procname = progname->cextname;
+ }
+ fprintf(diagfile, ":\n");
+ fflush(diagfile);
+}
+
+/* subroutine or function statement */
+
+ Extsym *
+#ifdef KR_headers
+newentry(v, substmsg)
+ register Namep v;
+ int substmsg;
+#else
+newentry(register Namep v, int substmsg)
+#endif
+{
+ register Extsym *p;
+ char buf[128], badname[64];
+ static int nbad = 0;
+ static char already[] = "external name already used";
+
+ p = mkext(v->fvarname, addunder(v->cvarname));
+
+ if(p->extinit || ! ONEOF(p->extstg, M(STGUNKNOWN)|M(STGEXT)) )
+ {
+ sprintf(badname, "%s_bad%d", v->fvarname, ++nbad);
+ if (substmsg) {
+ sprintf(buf,"%s\n\tsubstituting \"%s\"",
+ already, badname);
+ dclerr(buf, v);
+ }
+ else
+ dclerr(already, v);
+ p = mkext(v->fvarname, badname);
+ }
+ v->vstg = STGAUTO;
+ v->vprocclass = PTHISPROC;
+ v->vclass = CLPROC;
+ if (p->extstg == STGEXT)
+ prev_proc = 1;
+ else
+ p->extstg = STGEXT;
+ p->extinit = YES;
+ v->vardesc.varno = p - extsymtab;
+ return(p);
+}
+
+ void
+#ifdef KR_headers
+entrypt(class, type, length, entry, args)
+ int class;
+ int type;
+ ftnint length;
+ Extsym *entry;
+ chainp args;
+#else
+entrypt(int class, int type, ftnint length, Extsym *entry, chainp args)
+#endif
+{
+ register Namep q;
+ register struct Entrypoint *p;
+
+ if(class != CLENTRY)
+ puthead( procname = entry->cextname, class);
+ else
+ fprintf(diagfile, " entry ");
+ fprintf(diagfile, " %s:\n", entry->fextname);
+ fflush(diagfile);
+ q = mkname(entry->fextname);
+ if (type == TYSUBR)
+ q->vstg = STGEXT;
+
+ type = lengtype(type, length);
+ if(class == CLPROC)
+ {
+ procclass = CLPROC;
+ proctype = type;
+ procleng = type == TYCHAR ? length : 0;
+ }
+
+ p = ALLOC(Entrypoint);
+
+ p->entnextp = entries;
+ entries = p;
+
+ p->entryname = entry;
+ p->arglist = revchain(args);
+ p->enamep = q;
+
+ if(class == CLENTRY)
+ {
+ class = CLPROC;
+ if(proctype == TYSUBR)
+ type = TYSUBR;
+ }
+
+ q->vclass = class;
+ q->vprocclass = 0;
+ settype(q, type, length);
+ q->vprocclass = PTHISPROC;
+ /* hold all initial entry points till end of declarations */
+ if(parstate >= INDATA)
+ doentry(p);
+}
+
+/* generate epilogs */
+
+/* epicode -- write out the proper function return mechanism at the end of
+ the procedure declaration. Handles multiple return value types, as
+ well as cooercion into the proper value */
+
+ LOCAL void
+epicode(Void)
+{
+ extern int lastwasbranch;
+
+ if(procclass==CLPROC)
+ {
+ if(proctype==TYSUBR)
+ {
+
+/* Return a zero only when the alternate return mechanism has been
+ specified in the function header */
+
+ if ((substars || Ansi) && lastwasbranch != YES)
+ p1_subr_ret (ICON(0));
+ }
+ else if (!multitype && lastwasbranch != YES)
+ retval(proctype);
+ }
+ else if (procclass == CLMAIN && Ansi && lastwasbranch != YES)
+ p1_subr_ret (ICON(0));
+ lastwasbranch = NO;
+}
+
+
+/* generate code to return value of type t */
+
+ LOCAL void
+#ifdef KR_headers
+retval(t)
+ register int t;
+#else
+retval(register int t)
+#endif
+{
+ register Addrp p;
+
+ switch(t)
+ {
+ case TYCHAR:
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ break;
+
+ case TYLOGICAL:
+ t = tylogical;
+ case TYINT1:
+ case TYADDR:
+ case TYSHORT:
+ case TYLONG:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYREAL:
+ case TYDREAL:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ p = (Addrp) cpexpr((expptr)retslot);
+ p->vtype = t;
+ p1_subr_ret (mkconv (t, fixtype((expptr)p)));
+ break;
+
+ default:
+ badtype("retval", t);
+ }
+}
+
+
+/* Do parameter adjustments */
+
+ void
+#ifdef KR_headers
+procode(outfile)
+ FILE *outfile;
+#else
+procode(FILE *outfile)
+#endif
+{
+ prolog(outfile, allargs);
+
+ if (nentry > 1)
+ entry_goto(outfile);
+ }
+
+ static void
+#ifdef KR_headers
+bad_dimtype(q) Namep q;
+#else
+bad_dimtype(Namep q)
+#endif
+{
+ errstr("bad dimension type for %.70s", q->fvarname);
+ }
+
+/* Finish bound computations now that all variables are declared.
+ * This used to be in setbound(), but under -u the following incurred
+ * an erroneous error message:
+ * subroutine foo(x,n)
+ * real x(n)
+ * integer n
+ */
+
+ static void
+#ifdef KR_headers
+dim_finish(v)
+ Namep v;
+#else
+dim_finish(Namep v)
+#endif
+{
+ register struct Dimblock *p;
+ register expptr q;
+ register int i, nd;
+
+ p = v->vdim;
+ v->vdimfinish = 0;
+ nd = p->ndim;
+ doin_setbound = 1;
+ for(i = 0; i < nd; i++)
+ if (q = p->dims[i].dimexpr) {
+ q = p->dims[i].dimexpr = make_int_expr(putx(fixtype(q)));
+ if (!ONEOF(q->headblock.vtype, MSKINT|MSKREAL))
+ bad_dimtype(v);
+ }
+ if (q = p->basexpr)
+ p->basexpr = make_int_expr(putx(fixtype(q)));
+ doin_setbound = 0;
+ }
+
+ static void
+#ifdef KR_headers
+duparg(q)
+ Namep q;
+#else
+duparg(Namep q)
+#endif
+{ errstr("duplicate argument %.80s", q->fvarname); }
+
+/*
+ manipulate argument lists (allocate argument slot positions)
+ * keep track of return types and labels
+ */
+
+ LOCAL void
+#ifdef KR_headers
+doentry(ep)
+ struct Entrypoint *ep;
+#else
+doentry(struct Entrypoint *ep)
+#endif
+{
+ register int type;
+ register Namep np;
+ chainp p, p1;
+ register Namep q;
+ Addrp rs;
+ int it, k;
+ extern char dflttype[26];
+ Extsym *entryname = ep->entryname;
+
+ if (++nentry > 1)
+ p1_label((long)(extsymtab - entryname - 1));
+
+/* The main program isn't allowed to have parameters, so any given
+ parameters are ignored */
+
+ if(procclass == CLMAIN || procclass == CLBLOCK)
+ return;
+
+/* So now we're working with something other than CLMAIN or CLBLOCK.
+ Determine the type of its return value. */
+
+ impldcl( np = mkname(entryname->fextname) );
+ type = np->vtype;
+ proc_argchanges = prev_proc && type != entryname->extype;
+ entryname->extseen = 1;
+ if(proctype == TYUNKNOWN)
+ if( (proctype = type) == TYCHAR)
+ procleng = np->vleng ? np->vleng->constblock.Const.ci
+ : (ftnint) (-1);
+
+ if(proctype == TYCHAR)
+ {
+ if(type != TYCHAR)
+ err("noncharacter entry of character function");
+
+/* Functions returning type char can only have multiple entries if all
+ entries return the same length */
+
+ else if( (np->vleng ? np->vleng->constblock.Const.ci :
+ (ftnint) (-1)) != procleng)
+ err("mismatched character entry lengths");
+ }
+ else if(type == TYCHAR)
+ err("character entry of noncharacter function");
+ else if(type != proctype)
+ multitype = YES;
+ if(rtvlabel[type] == 0)
+ rtvlabel[type] = newlabel();
+ ep->typelabel = rtvlabel[type];
+
+ if(type == TYCHAR)
+ {
+ if(chslot < 0)
+ {
+ chslot = nextarg(TYADDR);
+ chlgslot = nextarg(TYLENG);
+ }
+ np->vstg = STGARG;
+
+/* Put a new argument in the function, one which will hold the result of
+ a character function. This will have to be named sometime, probably in
+ mkarg(). */
+
+ if(procleng < 0) {
+ np->vleng = (expptr) mkarg(TYLENG, chlgslot);
+ np->vleng->addrblock.uname_tag = UNAM_IDENT;
+ strcpy (np -> vleng -> addrblock.user.ident,
+ new_func_length());
+ }
+ if (!xretslot[TYCHAR]) {
+ xretslot[TYCHAR] = rs =
+ autovar(0, type, ISCONST(np->vleng)
+ ? np->vleng : ICON(0), "");
+ strcpy(rs->user.ident, "ret_val");
+ }
+ }
+
+/* Handle a complex return type -- declare a new parameter (pointer to
+ a complex value) */
+
+ else if( ISCOMPLEX(type) ) {
+ if (!xretslot[type])
+ xretslot[type] =
+ autovar(0, type, EXNULL, " ret_val");
+ /* the blank is for use in out_addr */
+ np->vstg = STGARG;
+ if(cxslot < 0)
+ cxslot = nextarg(TYADDR);
+ }
+ else if (type != TYSUBR) {
+ if (type == TYUNKNOWN) {
+ dclerr("untyped function", np);
+ proctype = type = np->vtype =
+ dflttype[letter(np->fvarname[0])];
+ }
+ if (!xretslot[type])
+ xretslot[type] = retslot =
+ autovar(1, type, EXNULL, " ret_val");
+ /* the blank is for use in out_addr */
+ np->vstg = STGAUTO;
+ }
+
+ for(p = ep->arglist ; p ; p = p->nextp)
+ if(! (( q = (Namep) (p->datap) )->vknownarg) ) {
+ q->vknownarg = 1;
+ q->vardesc.varno = nextarg(TYADDR);
+ allargs = mkchain((char *)q, allargs);
+ q->argno = nallargs++;
+ }
+ else if (nentry == 1)
+ duparg(q);
+ else for(p1 = ep->arglist ; p1 != p; p1 = p1->nextp)
+ if ((Namep)p1->datap == q)
+ duparg(q);
+
+ k = 0;
+ for(p = ep->arglist ; p ; p = p->nextp) {
+ if(! (( q = (Namep) (p->datap) )->vdcldone) )
+ {
+ impldcl(q);
+ q->vdcldone = YES;
+ if(q->vtype == TYCHAR)
+ {
+
+/* If we don't know the length of a char*(*) (i.e. a string), we must add
+ in this additional length argument. */
+
+ ++nallchargs;
+ if (q->vclass == CLPROC)
+ nallchargs--;
+ else if (q->vleng == NULL) {
+ /* character*(*) */
+ q->vleng = (expptr)
+ mkarg(TYLENG, nextarg(TYLENG) );
+ unamstring((Addrp)q->vleng,
+ new_arg_length(q));
+ }
+ }
+ }
+ if (q->vdimfinish)
+ dim_finish(q);
+ if (q->vtype == TYCHAR && q->vclass != CLPROC)
+ k++;
+ }
+
+ if (entryname->extype != type)
+ changedtype(np);
+
+ /* save information for checking consistency of arg lists */
+
+ it = infertypes;
+ if (entryname->exproto)
+ infertypes = 1;
+ save_argtypes(ep->arglist, &entryname->arginfo, &np->arginfo,
+ 0, np->fvarname, STGEXT, k, np->vtype, 2);
+ infertypes = it;
+}
+
+
+
+ LOCAL int
+#ifdef KR_headers
+nextarg(type)
+ int type;
+#else
+nextarg(int type)
+#endif
+{
+ type = type; /* shut up warning */
+ return(lastargslot++);
+ }
+
+ LOCAL void
+#ifdef KR_headers
+dim_check(q)
+ Namep q;
+#else
+dim_check(Namep q)
+#endif
+{
+ register struct Dimblock *vdim = q->vdim;
+ register expptr nelt;
+
+ if(!(nelt = vdim->nelt) || !ISCONST(nelt))
+ dclerr("adjustable dimension on non-argument", q);
+ else if (!ONEOF(nelt->headblock.vtype, MSKINT|MSKREAL))
+ bad_dimtype(q);
+ else if (ISINT(nelt->headblock.vtype)
+ && nelt->constblock.Const.ci <= 0
+ || nelt->constblock.Const.cd[0] <= 0)
+ dclerr("nonpositive dimension", q);
+ }
+
+ LOCAL void
+dobss(Void)
+{
+ register struct Hashentry *p;
+ register Namep q;
+ int qstg, qclass, qtype;
+ Extsym *e;
+
+ for(p = hashtab ; p<lasthash ; ++p)
+ if(q = p->varp)
+ {
+ qstg = q->vstg;
+ qtype = q->vtype;
+ qclass = q->vclass;
+
+ if( (qclass==CLUNKNOWN && qstg!=STGARG) ||
+ (qclass==CLVAR && qstg==STGUNKNOWN) ) {
+ if (!(q->vis_assigned | q->vimpldovar))
+ warn1("local variable %s never used",
+ q->fvarname);
+ }
+ else if(qclass==CLVAR && qstg==STGBSS)
+ { ; }
+
+/* Give external procedures the proper storage class */
+
+ else if(qclass==CLPROC && q->vprocclass==PEXTERNAL
+ && qstg!=STGARG) {
+ e = mkext(q->fvarname,addunder(q->cvarname));
+ e->extstg = STGEXT;
+ q->vardesc.varno = e - extsymtab;
+ if (e->extype != qtype)
+ changedtype(q);
+ }
+ if(qclass==CLVAR) {
+ if (qstg != STGARG && q->vdim)
+ dim_check(q);
+ } /* if qclass == CLVAR */
+ }
+
+}
+
+
+ void
+donmlist(Void)
+{
+ register struct Hashentry *p;
+ register Namep q;
+
+ for(p=hashtab; p<lasthash; ++p)
+ if( (q = p->varp) && q->vclass==CLNAMELIST)
+ namelist(q);
+}
+
+
+/* iarrlen -- Returns the size of the array in bytes, or -1 */
+
+ ftnint
+#ifdef KR_headers
+iarrlen(q)
+ register Namep q;
+#else
+iarrlen(register Namep q)
+#endif
+{
+ ftnint leng;
+
+ leng = typesize[q->vtype];
+ if(leng <= 0)
+ return(-1);
+ if(q->vdim)
+ if( ISICON(q->vdim->nelt) )
+ leng *= q->vdim->nelt->constblock.Const.ci;
+ else return(-1);
+ if(q->vleng)
+ if( ISICON(q->vleng) )
+ leng *= q->vleng->constblock.Const.ci;
+ else return(-1);
+ return(leng);
+}
+
+ void
+#ifdef KR_headers
+namelist(np)
+ Namep np;
+#else
+namelist(Namep np)
+#endif
+{
+ register chainp q;
+ register Namep v;
+ int y;
+
+ if (!np->visused)
+ return;
+ y = 0;
+
+ for(q = np->varxptr.namelist ; q ; q = q->nextp)
+ {
+ vardcl( v = (Namep) (q->datap) );
+ if( !ONEOF(v->vstg, MSKSTATIC) )
+ dclerr("may not appear in namelist", v);
+ else {
+ v->vnamelist = 1;
+ v->visused = 1;
+ v->vsave = 1;
+ y = 1;
+ }
+ np->visused = y;
+ }
+}
+
+/* docommon -- called at the end of procedure declarations, before
+ equivalences and the procedure body */
+
+ LOCAL void
+docommon(Void)
+{
+ register Extsym *extptr;
+ register chainp q, q1;
+ struct Dimblock *t;
+ expptr neltp;
+ register Namep comvar;
+ ftnint size;
+ int i, k, pref, type;
+ extern int type_pref[];
+
+ for(extptr = extsymtab ; extptr<nextext ; ++extptr)
+ if (extptr->extstg == STGCOMMON && (q = extptr->extp)) {
+
+/* If a common declaration also had a list of variables ... */
+
+ q = extptr->extp = revchain(q);
+ pref = 1;
+ for(k = TYCHAR; q ; q = q->nextp)
+ {
+ comvar = (Namep) (q->datap);
+
+ if(comvar->vdcldone == NO)
+ vardcl(comvar);
+ type = comvar->vtype;
+ if (pref < type_pref[type])
+ pref = type_pref[k = type];
+ if(extptr->extleng % typealign[type] != 0) {
+ dclerr("common alignment", comvar);
+ --nerr; /* don't give bad return code for this */
+#if 0
+ extptr->extleng = roundup(extptr->extleng, typealign[type]);
+#endif
+ } /* if extptr -> extleng % */
+
+/* Set the offset into the common block */
+
+ comvar->voffset = extptr->extleng;
+ comvar->vardesc.varno = extptr - extsymtab;
+ if(type == TYCHAR)
+ size = comvar->vleng->constblock.Const.ci;
+ else
+ size = typesize[type];
+ if(t = comvar->vdim)
+ if( (neltp = t->nelt) && ISCONST(neltp) )
+ size *= neltp->constblock.Const.ci;
+ else
+ dclerr("adjustable array in common", comvar);
+
+/* Adjust the length of the common block so far */
+
+ extptr->extleng += size;
+ } /* for */
+
+ extptr->extype = k;
+
+/* Determine curno and, if new, save this identifier chain */
+
+ q1 = extptr->extp;
+ for (q = extptr->allextp, i = 0; q; i++, q = q->nextp)
+ if (struct_eq((chainp)q->datap, q1))
+ break;
+ if (q)
+ extptr->curno = extptr->maxno - i;
+ else {
+ extptr->curno = ++extptr->maxno;
+ extptr->allextp = mkchain((char *)extptr->extp,
+ extptr->allextp);
+ }
+ } /* if extptr -> extstg == STGCOMMON */
+
+/* Now the hash table entries have STGCOMMON, vdcldone, voffset, and
+ varno. And the common block itself has its full size in extleng. */
+
+} /* docommon */
+
+
+/* copy_data -- copy the Namep entries so they are available even after
+ the hash table is empty */
+
+ void
+#ifdef KR_headers
+copy_data(list)
+ chainp list;
+#else
+copy_data(chainp list)
+#endif
+{
+ for (; list; list = list -> nextp) {
+ Namep namep = ALLOC (Nameblock);
+ int size, nd, i;
+ struct Dimblock *dp;
+
+ cpn(sizeof(struct Nameblock), list->datap, (char *)namep);
+ namep->fvarname = strcpy(gmem(strlen(namep->fvarname)+1,0),
+ namep->fvarname);
+ namep->cvarname = strcmp(namep->fvarname, namep->cvarname)
+ ? strcpy(gmem(strlen(namep->cvarname)+1,0), namep->cvarname)
+ : namep->fvarname;
+ if (namep -> vleng)
+ namep -> vleng = (expptr) cpexpr (namep -> vleng);
+ if (namep -> vdim) {
+ nd = namep -> vdim -> ndim;
+ size = sizeof(int) + (3 + 2 * nd) * sizeof (expptr);
+ dp = (struct Dimblock *) ckalloc (size);
+ cpn(size, (char *)namep->vdim, (char *)dp);
+ namep -> vdim = dp;
+ dp->nelt = (expptr)cpexpr(dp->nelt);
+ for (i = 0; i < nd; i++) {
+ dp -> dims[i].dimsize = (expptr) cpexpr (dp -> dims[i].dimsize);
+ } /* for */
+ } /* if */
+ list -> datap = (char *) namep;
+ } /* for */
+} /* copy_data */
+
+
+
+ LOCAL void
+docomleng(Void)
+{
+ register Extsym *p;
+
+ for(p = extsymtab ; p < nextext ; ++p)
+ if(p->extstg == STGCOMMON)
+ {
+ if(p->maxleng!=0 && p->extleng!=0 && p->maxleng!=p->extleng
+ && strcmp(Blank, p->cextname) )
+ warn1("incompatible lengths for common block %.60s",
+ p->fextname);
+ if(p->maxleng < p->extleng)
+ p->maxleng = p->extleng;
+ p->extleng = 0;
+ }
+}
+
+
+/* ROUTINES DEALING WITH AUTOMATIC AND TEMPORARY STORAGE */
+
+ void
+#ifdef KR_headers
+frtemp(p)
+ Addrp p;
+#else
+frtemp(Addrp p)
+#endif
+{
+ /* put block on chain of temps to be reclaimed */
+ holdtemps = mkchain((char *)p, holdtemps);
+}
+
+ void
+freetemps(Void)
+{
+ register chainp p, p1;
+ register Addrp q;
+ register int t;
+
+ p1 = holdtemps;
+ while(p = p1) {
+ q = (Addrp)p->datap;
+ t = q->vtype;
+ if (t == TYCHAR && q->varleng != 0) {
+ /* restore clobbered character string lengths */
+ frexpr(q->vleng);
+ q->vleng = ICON(q->varleng);
+ }
+ p1 = p->nextp;
+ p->nextp = templist[t];
+ templist[t] = p;
+ }
+ holdtemps = 0;
+ }
+
+/* allocate an automatic variable slot for each of nelt variables */
+
+ Addrp
+#ifdef KR_headers
+autovar(nelt0, t, lengp, name)
+ register int nelt0;
+ register int t;
+ expptr lengp;
+ char *name;
+#else
+autovar(register int nelt0, register int t, expptr lengp, char *name)
+#endif
+{
+ ftnint leng;
+ register Addrp q;
+ register int nelt = nelt0 > 0 ? nelt0 : 1;
+ extern char *av_pfix[];
+
+ if(t == TYCHAR)
+ if( ISICON(lengp) )
+ leng = lengp->constblock.Const.ci;
+ else {
+ Fatal("automatic variable of nonconstant length");
+ }
+ else
+ leng = typesize[t];
+
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ q->vtype = t;
+ if(t == TYCHAR)
+ {
+ q->vleng = ICON(leng);
+ q->varleng = leng;
+ }
+ q->vstg = STGAUTO;
+ q->ntempelt = nelt;
+ q->isarray = (nelt > 1);
+ q->memoffset = ICON(0);
+
+ /* kludge for nls so we can have ret_val rather than ret_val_4 */
+ if (*name == ' ')
+ unamstring(q, name);
+ else {
+ q->uname_tag = UNAM_IDENT;
+ temp_name(av_pfix[t], ++autonum[t], q->user.ident);
+ }
+ if (nelt0 > 0)
+ declare_new_addr (q);
+ return(q);
+}
+
+
+/* Returns a temporary of the appropriate type. Will reuse existing
+ temporaries when possible */
+
+ Addrp
+#ifdef KR_headers
+mktmpn(nelt, type, lengp)
+ int nelt;
+ register int type;
+ expptr lengp;
+#else
+mktmpn(int nelt, register int type, expptr lengp)
+#endif
+{
+ ftnint leng;
+ chainp p, oldp;
+ register Addrp q;
+ extern int krparens;
+
+ if(type==TYUNKNOWN || type==TYERROR)
+ badtype("mktmpn", type);
+
+ if(type==TYCHAR)
+ if(lengp && ISICON(lengp) )
+ leng = lengp->constblock.Const.ci;
+ else {
+ err("adjustable length");
+ return( (Addrp) errnode() );
+ }
+ else if (type > TYCHAR || type < TYADDR) {
+ erri("mktmpn: unexpected type %d", type);
+ exit(1);
+ }
+/*
+ * if a temporary of appropriate shape is on the templist,
+ * remove it from the list and return it
+ */
+ if (krparens == 2 && ONEOF(type,M(TYREAL)|M(TYCOMPLEX)))
+ type++;
+ for(oldp=CHNULL, p=templist[type]; p ; oldp=p, p=p->nextp)
+ {
+ q = (Addrp) (p->datap);
+ if(q->ntempelt==nelt &&
+ (type!=TYCHAR || q->vleng->constblock.Const.ci==leng) )
+ {
+ if(oldp)
+ oldp->nextp = p->nextp;
+ else
+ templist[type] = p->nextp;
+ free( (charptr) p);
+ return(q);
+ }
+ }
+ q = autovar(nelt, type, lengp, "");
+ return(q);
+}
+
+
+
+
+/* mktmp -- create new local variable; call it something like name
+ lengp is taken directly, not copied */
+
+ Addrp
+#ifdef KR_headers
+mktmp(type, lengp)
+ int type;
+ expptr lengp;
+#else
+mktmp(int type, expptr lengp)
+#endif
+{
+ Addrp rv;
+ /* arrange for temporaries to be recycled */
+ /* at the end of this statement... */
+ rv = mktmpn(1,type,lengp);
+ frtemp((Addrp)cpexpr((expptr)rv));
+ return rv;
+}
+
+/* mktmp0 omits frtemp() */
+ Addrp
+#ifdef KR_headers
+mktmp0(type, lengp)
+ int type;
+ expptr lengp;
+#else
+mktmp0(int type, expptr lengp)
+#endif
+{
+ Addrp rv;
+ /* arrange for temporaries to be recycled */
+ /* when this Addrp is freed */
+ rv = mktmpn(1,type,lengp);
+ rv->istemp = YES;
+ return rv;
+}
+
+/* VARIOUS ROUTINES FOR PROCESSING DECLARATIONS */
+
+/* comblock -- Declare a new common block. Input parameters name the block;
+ s will be NULL if the block is unnamed */
+
+ Extsym *
+#ifdef KR_headers
+comblock(s)
+ register char *s;
+#else
+comblock(register char *s)
+#endif
+{
+ Extsym *p;
+ register char *t;
+ register int c, i;
+ char cbuf[256], *s0;
+
+/* Give the unnamed common block a unique name */
+
+ if(*s == 0)
+ p = mkext1(s0 = Blank, Blank);
+ else {
+ s0 = s;
+ t = cbuf;
+ for(i = 0; c = *t = *s++; t++)
+ if (c == '_')
+ i = 1;
+ if (i)
+ *t++ = '_';
+ t[0] = '_';
+ t[1] = 0;
+ p = mkext1(s0,cbuf);
+ }
+ if(p->extstg == STGUNKNOWN)
+ p->extstg = STGCOMMON;
+ else if(p->extstg != STGCOMMON)
+ {
+ errstr("%.52s cannot be a common block: it is a subprogram.",
+ s0);
+ return(0);
+ }
+
+ return( p );
+}
+
+
+/* incomm -- add a new variable to a common declaration */
+
+ void
+#ifdef KR_headers
+incomm(c, v)
+ Extsym *c;
+ Namep v;
+#else
+incomm(Extsym *c, Namep v)
+#endif
+{
+ if (!c)
+ return;
+ if(v->vstg != STGUNKNOWN && !v->vimplstg)
+ dclerr(v->vstg == STGARG
+ ? "dummy arguments cannot be in common"
+ : "incompatible common declaration", v);
+ else
+ {
+ v->vstg = STGCOMMON;
+ c->extp = mkchain((char *)v, c->extp);
+ }
+}
+
+
+
+
+/* settype -- set the type or storage class of a Namep object. If
+ v -> vstg == STGUNKNOWN && type < 0, attempt to reset vstg to be
+ -type. This function will not change any earlier definitions in v,
+ in will only attempt to fill out more information give the other params */
+
+ void
+#ifdef KR_headers
+settype(v, type, length)
+ register Namep v;
+ register int type;
+ register ftnint length;
+#else
+settype(register Namep v, register int type, register ftnint length)
+#endif
+{
+ int type1;
+
+ if(type == TYUNKNOWN)
+ return;
+
+ if(type==TYSUBR && v->vtype!=TYUNKNOWN && v->vstg==STGARG)
+ {
+ v->vtype = TYSUBR;
+ frexpr(v->vleng);
+ v->vleng = 0;
+ v->vimpltype = 0;
+ }
+ else if(type < 0) /* storage class set */
+ {
+ if(v->vstg == STGUNKNOWN)
+ v->vstg = - type;
+ else if(v->vstg != -type)
+ dclerr("incompatible storage declarations", v);
+ }
+ else if(v->vtype == TYUNKNOWN || v->vimpltype && v->vtype != type)
+ {
+ if( (v->vtype = lengtype(type, length))==TYCHAR )
+ if (length>=0)
+ v->vleng = ICON(length);
+ else if (parstate >= INDATA)
+ v->vleng = ICON(1); /* avoid a memory fault */
+ v->vimpltype = 0;
+
+ if (v->vclass == CLPROC) {
+ if (v->vstg == STGEXT
+ && (type1 = extsymtab[v->vardesc.varno].extype)
+ && type1 != v->vtype)
+ changedtype(v);
+ else if (v->vprocclass == PTHISPROC
+ && (parstate >= INDATA
+ || procclass == CLMAIN)
+ && !xretslot[type]) {
+ xretslot[type] = autovar(ONEOF(type,
+ MSKCOMPLEX|MSKCHAR) ? 0 : 1, type,
+ v->vleng, " ret_val");
+ if (procclass == CLMAIN)
+ errstr(
+ "illegal use of %.60s (main program name)",
+ v->fvarname);
+ /* not completely right, but enough to */
+ /* avoid memory faults; we won't */
+ /* emit any C as we have illegal Fortran */
+ }
+ }
+ }
+ else if(v->vtype!=type) {
+ incompat:
+ dclerr("incompatible type declarations", v);
+ }
+ else if (type==TYCHAR)
+ if (v->vleng && v->vleng->constblock.Const.ci != length)
+ goto incompat;
+ else if (parstate >= INDATA)
+ v->vleng = ICON(1); /* avoid a memory fault */
+}
+
+
+
+
+
+/* lengtype -- returns the proper compiler type, given input of Fortran
+ type and length specifier */
+
+ int
+#ifdef KR_headers
+lengtype(type, len)
+ register int type;
+ ftnint len;
+#else
+lengtype(register int type, ftnint len)
+#endif
+{
+ register int length = (int)len;
+ switch(type)
+ {
+ case TYREAL:
+ if(length == typesize[TYDREAL])
+ return(TYDREAL);
+ if(length == typesize[TYREAL])
+ goto ret;
+ break;
+
+ case TYCOMPLEX:
+ if(length == typesize[TYDCOMPLEX])
+ return(TYDCOMPLEX);
+ if(length == typesize[TYCOMPLEX])
+ goto ret;
+ break;
+
+ case TYINT1:
+ case TYSHORT:
+ case TYDREAL:
+ case TYDCOMPLEX:
+ case TYCHAR:
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYUNKNOWN:
+ case TYSUBR:
+ case TYERROR:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ goto ret;
+
+ case TYLOGICAL:
+ switch(length) {
+ case 0: return tylog;
+ case 1: return TYLOGICAL1;
+ case 2: return TYLOGICAL2;
+ case 4: goto ret;
+ }
+#if 0 /*!!??!!*/
+ if(length == typesize[TYLOGICAL])
+ goto ret;
+#endif
+ break;
+
+ case TYLONG:
+ if(length == 0)
+ return(tyint);
+ if (length == 1)
+ return TYINT1;
+ if(length == typesize[TYSHORT])
+ return(TYSHORT);
+#ifdef TYQUAD
+ if(length == typesize[TYQUAD] && use_tyquad)
+ return(TYQUAD);
+#endif
+ if(length == typesize[TYLONG])
+ goto ret;
+ break;
+ default:
+ badtype("lengtype", type);
+ }
+
+ if(len != 0)
+ err("incompatible type-length combination");
+
+ret:
+ return(type);
+}
+
+
+
+
+
+/* setintr -- Set Intrinsic function */
+
+ void
+#ifdef KR_headers
+setintr(v)
+ register Namep v;
+#else
+setintr(register Namep v)
+#endif
+{
+ int k;
+
+ if(k = intrfunct(v->fvarname)) {
+ if ((*(struct Intrpacked *)&k).f4)
+ if (noextflag)
+ goto unknown;
+ else
+ dcomplex_seen++;
+ v->vardesc.varno = k;
+ }
+ else {
+ unknown:
+ dclerr("unknown intrinsic function", v);
+ return;
+ }
+ if(v->vstg == STGUNKNOWN)
+ v->vstg = STGINTR;
+ else if(v->vstg!=STGINTR)
+ dclerr("incompatible use of intrinsic function", v);
+ if(v->vclass==CLUNKNOWN)
+ v->vclass = CLPROC;
+ if(v->vprocclass == PUNKNOWN)
+ v->vprocclass = PINTRINSIC;
+ else if(v->vprocclass != PINTRINSIC)
+ dclerr("invalid intrinsic declaration", v);
+}
+
+
+
+/* setext -- Set External declaration -- assume that unknowns will become
+ procedures */
+
+ void
+#ifdef KR_headers
+setext(v)
+ register Namep v;
+#else
+setext(register Namep v)
+#endif
+{
+ if(v->vclass == CLUNKNOWN)
+ v->vclass = CLPROC;
+ else if(v->vclass != CLPROC)
+ dclerr("invalid external declaration", v);
+
+ if(v->vprocclass == PUNKNOWN)
+ v->vprocclass = PEXTERNAL;
+ else if(v->vprocclass != PEXTERNAL)
+ dclerr("invalid external declaration", v);
+} /* setext */
+
+
+
+
+/* create dimensions block for array variable */
+
+ void
+#ifdef KR_headers
+setbound(v, nd, dims)
+ register Namep v;
+ int nd;
+ struct Dims *dims;
+#else
+setbound(register Namep v, int nd, struct Dims *dims)
+#endif
+{
+ register expptr q, t;
+ register struct Dimblock *p;
+ int i;
+ extern chainp new_vars;
+ char buf[256];
+
+ if(v->vclass == CLUNKNOWN)
+ v->vclass = CLVAR;
+ else if(v->vclass != CLVAR)
+ {
+ dclerr("only variables may be arrays", v);
+ return;
+ }
+
+ v->vdim = p = (struct Dimblock *)
+ ckalloc( sizeof(int) + (3+2*nd)*sizeof(expptr) );
+ p->ndim = nd--;
+ p->nelt = ICON(1);
+ doin_setbound = 1;
+
+ if (noextflag)
+ for(i = 0; i <= nd; i++)
+ if (((q = dims[i].lb) && !ISINT(q->headblock.vtype))
+ || ((q = dims[i].ub) && !ISINT(q->headblock.vtype))) {
+ sprintf(buf, "dimension %d of %s is not an integer.",
+ i+1, v->fvarname);
+ errext(buf);
+ break;
+ }
+
+ for(i = 0; i <= nd; i++) {
+ if (((q = dims[i].lb) && !ISINT(q->headblock.vtype)))
+ dims[i].lb = mkconv(TYINT, q);
+ if (((q = dims[i].ub) && !ISINT(q->headblock.vtype)))
+ dims[i].ub = mkconv(TYINT, q);
+ }
+
+ for(i = 0; i <= nd; ++i)
+ {
+ if( (q = dims[i].ub) == NULL)
+ {
+ if(i == nd)
+ {
+ frexpr(p->nelt);
+ p->nelt = NULL;
+ }
+ else
+ err("only last bound may be asterisk");
+ p->dims[i].dimsize = ICON(1);
+ p->dims[i].dimexpr = NULL;
+ }
+ else
+ {
+
+ if(dims[i].lb)
+ {
+ q = mkexpr(OPMINUS, q, cpexpr(dims[i].lb));
+ q = mkexpr(OPPLUS, q, ICON(1) );
+ }
+ if( ISCONST(q) )
+ {
+ p->dims[i].dimsize = q;
+ p->dims[i].dimexpr = (expptr) PNULL;
+ }
+ else {
+ sprintf(buf, " %s_dim%d", v->fvarname, i+1);
+ p->dims[i].dimsize = (expptr)
+ autovar(1, tyint, EXNULL, buf);
+ p->dims[i].dimexpr = q;
+ if (i == nd)
+ v->vlastdim = new_vars;
+ v->vdimfinish = 1;
+ }
+ if(p->nelt)
+ p->nelt = mkexpr(OPSTAR, p->nelt,
+ cpexpr(p->dims[i].dimsize) );
+ }
+ }
+
+ q = dims[nd].lb;
+ if(q == NULL)
+ q = ICON(1);
+
+ for(i = nd-1 ; i>=0 ; --i)
+ {
+ t = dims[i].lb;
+ if(t == NULL)
+ t = ICON(1);
+ if(p->dims[i].dimsize)
+ q = mkexpr(OPPLUS, t,
+ mkexpr(OPSTAR, cpexpr(p->dims[i].dimsize), q));
+ }
+
+ if( ISCONST(q) )
+ {
+ p->baseoffset = q;
+ p->basexpr = NULL;
+ }
+ else
+ {
+ sprintf(buf, " %s_offset", v->fvarname);
+ p->baseoffset = (expptr) autovar(1, tyint, EXNULL, buf);
+ p->basexpr = q;
+ v->vdimfinish = 1;
+ }
+ doin_setbound = 0;
+}
+
+
+ void
+#ifdef KR_headers
+wr_abbrevs(outfile, function_head, vars)
+ FILE *outfile;
+ int function_head;
+ chainp vars;
+#else
+wr_abbrevs(FILE *outfile, int function_head, chainp vars)
+#endif
+{
+ for (; vars; vars = vars -> nextp) {
+ Namep name = (Namep) vars -> datap;
+ if (!name->visused)
+ continue;
+
+ if (function_head)
+ nice_printf (outfile, "#define ");
+ else
+ nice_printf (outfile, "#undef ");
+ out_name (outfile, name);
+
+ if (function_head) {
+ Extsym *comm = &extsymtab[name -> vardesc.varno];
+
+ nice_printf (outfile, " (");
+ extern_out (outfile, comm);
+ nice_printf (outfile, "%d.", comm->curno);
+ nice_printf (outfile, "%s)", name->cvarname);
+ } /* if function_head */
+ nice_printf (outfile, "\n");
+ } /* for */
+} /* wr_abbrevs */
diff --git a/usr.bin/f2c/put.c b/usr.bin/f2c/put.c
new file mode 100644
index 0000000..6520ed5
--- /dev/null
+++ b/usr.bin/f2c/put.c
@@ -0,0 +1,440 @@
+/****************************************************************
+Copyright 1990, 1991, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/*
+ * INTERMEDIATE CODE GENERATION PROCEDURES COMMON TO BOTH
+ * JOHNSON (PORTABLE) AND RITCHIE FAMILIES OF SECOND PASSES
+*/
+
+#include "defs.h"
+#include "names.h" /* For LOCAL_CONST_NAME */
+#include "pccdefs.h"
+#include "p1defs.h"
+
+/* Definitions for putconst() */
+
+#define LIT_CHAR 1
+#define LIT_FLOAT 2
+#define LIT_INT 3
+
+
+/*
+char *ops [ ] =
+ {
+ "??", "+", "-", "*", "/", "**", "-",
+ "OR", "AND", "EQV", "NEQV", "NOT",
+ "CONCAT",
+ "<", "==", ">", "<=", "!=", ">=",
+ " of ", " ofC ", " = ", " += ", " *= ", " CONV ", " << ", " % ",
+ " , ", " ? ", " : "
+ " abs ", " min ", " max ", " addr ", " indirect ",
+ " bitor ", " bitand ", " bitxor ", " bitnot ", " >> ",
+ };
+*/
+
+/* Each of these values is defined in pccdefs */
+
+int ops2 [ ] =
+{
+ P2BAD, P2PLUS, P2MINUS, P2STAR, P2SLASH, P2BAD, P2NEG,
+ P2OROR, P2ANDAND, P2EQ, P2NE, P2NOT,
+ P2BAD,
+ P2LT, P2EQ, P2GT, P2LE, P2NE, P2GE,
+ P2CALL, P2CALL, P2ASSIGN, P2PLUSEQ, P2STAREQ, P2CONV, P2LSHIFT, P2MOD,
+ P2COMOP, P2QUEST, P2COLON,
+ 1, P2BAD, P2BAD, P2BAD, P2BAD,
+ P2BITOR, P2BITAND, P2BITXOR, P2BITNOT, P2RSHIFT,
+ P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD, P2BAD,
+ P2BAD, P2BAD, P2BAD, P2BAD,
+ 1,1,1,1,1, /* OPNEG1, OPDMIN, OPDMAX, OPASSIGNI, OPIDENTITY */
+ 1,1,1,1 /* OPCHARCAST, OPDABS, OPMIN2, OPMAX2 */
+};
+
+
+ void
+#ifdef KR_headers
+putexpr(p)
+ expptr p;
+#else
+putexpr(expptr p)
+#endif
+{
+/* Write the expression to the p1 file */
+
+ p = (expptr) putx (fixtype (p));
+ p1_expr (p);
+}
+
+
+
+
+
+ expptr
+#ifdef KR_headers
+putassign(lp, rp)
+ expptr lp;
+ expptr rp;
+#else
+putassign(expptr lp, expptr rp)
+#endif
+{
+ return putx(fixexpr((Exprp)mkexpr(OPASSIGN, lp, rp)));
+}
+
+
+
+
+ void
+#ifdef KR_headers
+puteq(lp, rp)
+ expptr lp;
+ expptr rp;
+#else
+puteq(expptr lp, expptr rp)
+#endif
+{
+ putexpr(mkexpr(OPASSIGN, lp, rp) );
+}
+
+
+
+
+/* put code for a *= b */
+
+ expptr
+#ifdef KR_headers
+putsteq(a, b)
+ Addrp a;
+ Addrp b;
+#else
+putsteq(Addrp a, Addrp b)
+#endif
+{
+ return putx( fixexpr((Exprp)
+ mkexpr(OPSTAREQ, cpexpr((expptr)a), cpexpr((expptr)b))));
+}
+
+
+
+
+ Addrp
+#ifdef KR_headers
+mkfield(res, f, ty)
+ register Addrp res;
+ char *f;
+ int ty;
+#else
+mkfield(register Addrp res, char *f, int ty)
+#endif
+{
+ res -> vtype = ty;
+ res -> Field = f;
+ return res;
+} /* mkfield */
+
+
+ Addrp
+#ifdef KR_headers
+realpart(p)
+ register Addrp p;
+#else
+realpart(register Addrp p)
+#endif
+{
+ register Addrp q;
+
+ if (p->tag == TADDR
+ && p->uname_tag == UNAM_CONST
+ && ISCOMPLEX (p->vtype))
+ return (Addrp)mkrealcon (p -> vtype + TYREAL - TYCOMPLEX,
+ p->user.kludge.vstg1 ? p->user.Const.cds[0]
+ : cds(dtos(p->user.Const.cd[0]),CNULL));
+
+ q = (Addrp) cpexpr((expptr) p);
+ if( ISCOMPLEX(p->vtype) )
+ q = mkfield (q, "r", p -> vtype + TYREAL - TYCOMPLEX);
+
+ return(q);
+}
+
+
+
+
+ expptr
+#ifdef KR_headers
+imagpart(p)
+ register Addrp p;
+#else
+imagpart(register Addrp p)
+#endif
+{
+ register Addrp q;
+
+ if( ISCOMPLEX(p->vtype) )
+ {
+ if (p->tag == TADDR && p->uname_tag == UNAM_CONST)
+ return mkrealcon (p -> vtype + TYREAL - TYCOMPLEX,
+ p->user.kludge.vstg1 ? p->user.Const.cds[1]
+ : cds(dtos(p->user.Const.cd[1]),CNULL));
+ q = (Addrp) cpexpr((expptr) p);
+ q = mkfield (q, "i", p -> vtype + TYREAL - TYCOMPLEX);
+ return( (expptr) q );
+ }
+ else
+
+/* Cast an integer type onto a Double Real type */
+
+ return( mkrealcon( ISINT(p->vtype) ? TYDREAL : p->vtype , "0"));
+}
+
+
+
+
+
+/* ncat -- computes the number of adjacent concatenation operations */
+
+ int
+#ifdef KR_headers
+ncat(p)
+ register expptr p;
+#else
+ncat(register expptr p)
+#endif
+{
+ if(p->tag==TEXPR && p->exprblock.opcode==OPCONCAT)
+ return( ncat(p->exprblock.leftp) + ncat(p->exprblock.rightp) );
+ else return(1);
+}
+
+
+
+
+/* lencat -- returns the length of the concatenated string. Each
+ substring must have a static (i.e. compile-time) fixed length */
+
+ ftnint
+#ifdef KR_headers
+lencat(p)
+ register expptr p;
+#else
+lencat(register expptr p)
+#endif
+{
+ if(p->tag==TEXPR && p->exprblock.opcode==OPCONCAT)
+ return( lencat(p->exprblock.leftp) + lencat(p->exprblock.rightp) );
+ else if( p->headblock.vleng!=NULL && ISICON(p->headblock.vleng) )
+ return(p->headblock.vleng->constblock.Const.ci);
+ else if(p->tag==TADDR && p->addrblock.varleng!=0)
+ return(p->addrblock.varleng);
+ else
+ {
+ err("impossible element in concatenation");
+ return(0);
+ }
+}
+
+/* putconst -- Creates a new Addrp value which maps onto the input
+ constant value. The Addrp doesn't retain the value of the constant,
+ instead that value is copied into a table of constants (called
+ litpool, for pool of literal values). The only way to retrieve the
+ actual value of the constant is to look at the memno field of the
+ Addrp result. You know that the associated literal is the one referred
+ to by q when (q -> memno == litp -> litnum).
+*/
+
+ Addrp
+#ifdef KR_headers
+putconst(p)
+ register Constp p;
+#else
+putconst(register Constp p)
+#endif
+{
+ register Addrp q;
+ struct Literal *litp, *lastlit;
+ int k, len, type;
+ int litflavor;
+ double cd[2];
+ ftnint nblanks;
+ char *strp;
+ char cdsbuf0[64], cdsbuf1[64], *ds[2];
+
+ if (p->tag != TCONST)
+ badtag("putconst", p->tag);
+
+ q = ALLOC(Addrblock);
+ q->tag = TADDR;
+ type = p->vtype;
+ q->vtype = ( type==TYADDR ? tyint : type );
+ q->vleng = (expptr) cpexpr(p->vleng);
+ q->vstg = STGCONST;
+
+/* Create the new label for the constant. This is wasteful of labels
+ because when the constant value already exists in the literal pool,
+ this label gets thrown away and is never reclaimed. It might be
+ cleaner to move this down past the first switch() statement below */
+
+ q->memno = newlabel();
+ q->memoffset = ICON(0);
+ q -> uname_tag = UNAM_CONST;
+
+/* Copy the constant info into the Addrblock; do this by copying the
+ largest storage elts */
+
+ q -> user.Const = p -> Const;
+ q->user.kludge.vstg1 = p->vstg; /* distinguish string from binary fp */
+
+ /* check for value in literal pool, and update pool if necessary */
+
+ k = 1;
+ switch(type)
+ {
+ case TYCHAR:
+ if (halign) {
+ strp = p->Const.ccp;
+ nblanks = p->Const.ccp1.blanks;
+ len = p->vleng->constblock.Const.ci;
+ litflavor = LIT_CHAR;
+ goto loop;
+ }
+ else
+ q->memno = BAD_MEMNO;
+ break;
+ case TYCOMPLEX:
+ case TYDCOMPLEX:
+ k = 2;
+ if (p->vstg)
+ cd[1] = atof(ds[1] = p->Const.cds[1]);
+ else
+ ds[1] = cds(dtos(cd[1] = p->Const.cd[1]), cdsbuf1);
+ case TYREAL:
+ case TYDREAL:
+ litflavor = LIT_FLOAT;
+ if (p->vstg)
+ cd[0] = atof(ds[0] = p->Const.cds[0]);
+ else
+ ds[0] = cds(dtos(cd[0] = p->Const.cd[0]), cdsbuf0);
+ goto loop;
+
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+ case TYLONG:
+ case TYSHORT:
+ case TYINT1:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ lit_int_flavor:
+ litflavor = LIT_INT;
+
+/* Scan the literal pool for this constant value. If this same constant
+ has been assigned before, use the same label. Note that this routine
+ does NOT consider two differently-typed constants with the same bit
+ pattern to be the same constant */
+
+ loop:
+ lastlit = litpool + nliterals;
+ for(litp = litpool ; litp<lastlit ; ++litp)
+
+/* Remove this type checking to ensure that all bit patterns are reused */
+
+ if(type == litp->littype) switch(litflavor)
+ {
+ case LIT_CHAR:
+ if (len == (int)litp->litval.litival2[0]
+ && nblanks == litp->litval.litival2[1]
+ && !memcmp(strp, litp->cds[0], len)) {
+ q->memno = litp->litnum;
+ frexpr((expptr)p);
+ q->user.Const.ccp1.ccp0 = litp->cds[0];
+ return(q);
+ }
+ break;
+ case LIT_FLOAT:
+ if(cd[0] == litp->litval.litdval[0]
+ && !strcmp(ds[0], litp->cds[0])
+ && (k == 1 ||
+ cd[1] == litp->litval.litdval[1]
+ && !strcmp(ds[1], litp->cds[1]))) {
+ret:
+ q->memno = litp->litnum;
+ frexpr((expptr)p);
+ return(q);
+ }
+ break;
+
+ case LIT_INT:
+ if(p->Const.ci == litp->litval.litival)
+ goto ret;
+ break;
+ }
+
+/* If there's room in the literal pool, add this new value to the pool */
+
+ if(nliterals < maxliterals)
+ {
+ ++nliterals;
+
+ /* litp now points to the next free elt */
+
+ litp->littype = type;
+ litp->litnum = q->memno;
+ switch(litflavor)
+ {
+ case LIT_CHAR:
+ litp->litval.litival2[0] = len;
+ litp->litval.litival2[1] = nblanks;
+ q->user.Const.ccp = litp->cds[0] =
+ memcpy(gmem(len,0), strp, len);
+ break;
+
+ case LIT_FLOAT:
+ litp->litval.litdval[0] = cd[0];
+ litp->cds[0] = copys(ds[0]);
+ if (k == 2) {
+ litp->litval.litdval[1] = cd[1];
+ litp->cds[1] = copys(ds[1]);
+ }
+ break;
+
+ case LIT_INT:
+ litp->litval.litival = p->Const.ci;
+ break;
+ } /* switch (litflavor) */
+ }
+ else
+ many("literal constants", 'L', maxliterals);
+
+ break;
+ case TYADDR:
+ break;
+ default:
+ badtype ("putconst", p -> vtype);
+ break;
+ } /* switch */
+
+ if (type != TYCHAR || halign)
+ frexpr((expptr)p);
+ return( q );
+}
diff --git a/usr.bin/f2c/putpcc.c b/usr.bin/f2c/putpcc.c
new file mode 100644
index 0000000..6d87d3c
--- /dev/null
+++ b/usr.bin/f2c/putpcc.c
@@ -0,0 +1,2018 @@
+/****************************************************************
+Copyright 1990 - 1995 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* INTERMEDIATE CODE GENERATION FOR S. C. JOHNSON C COMPILERS */
+/* NEW VERSION USING BINARY POLISH POSTFIX INTERMEDIATE */
+
+#include "defs.h"
+#include "pccdefs.h"
+#include "output.h" /* for nice_printf */
+#include "names.h"
+#include "p1defs.h"
+
+static Addrp intdouble Argdcl((Addrp));
+static Addrp putcx1 Argdcl((tagptr));
+static tagptr putaddr Argdcl((tagptr));
+static tagptr putcall Argdcl((tagptr, Addrp*));
+static tagptr putcat Argdcl((tagptr, tagptr));
+static Addrp putch1 Argdcl((tagptr));
+static tagptr putchcmp Argdcl((tagptr));
+static tagptr putcheq Argdcl((tagptr));
+static void putct1 Argdcl((tagptr, Addrp, Addrp, ptr));
+static tagptr putcxcmp Argdcl((tagptr));
+static Addrp putcxeq Argdcl((tagptr));
+static tagptr putmnmx Argdcl((tagptr));
+static tagptr putop Argdcl((tagptr));
+static tagptr putpower Argdcl((tagptr));
+
+#define FOUR 4
+extern int ops2[];
+extern int proc_argchanges, proc_protochanges;
+extern int krparens;
+
+#define P2BUFFMAX 128
+
+/* Puthead -- output the header information about subroutines, functions
+ and entry points */
+
+ void
+#ifdef KR_headers
+puthead(s, class)
+ char *s;
+ int class;
+#else
+puthead(char *s, int class)
+#endif
+{
+ if (headerdone == NO) {
+ if (class == CLMAIN)
+ s = "MAIN__";
+ p1_head (class, s);
+ headerdone = YES;
+ }
+}
+
+ void
+#ifdef KR_headers
+putif(p, else_if_p)
+ register expptr p;
+ int else_if_p;
+#else
+putif(register expptr p, int else_if_p)
+#endif
+{
+ register int k;
+ int n;
+ long where;
+
+ if (else_if_p) {
+ p1put(P1_ELSEIFSTART);
+ where = ftell(pass1_file);
+ }
+ if( !ISLOGICAL((k = (p = fixtype(p))->headblock.vtype )) )
+ {
+ if(k != TYERROR)
+ err("non-logical expression in IF statement");
+ }
+ else {
+ if (else_if_p) {
+ if (ei_next >= ei_last)
+ {
+ k = ei_last - ei_first;
+ n = k + 100;
+ ei_next = mem(n,0);
+ ei_last = ei_first + n;
+ if (k)
+ memcpy(ei_next, ei_first, k);
+ ei_first = ei_next;
+ ei_next += k;
+ ei_last = ei_first + n;
+ }
+ p = putx(p);
+ if (*ei_next++ = ftell(pass1_file) > where) {
+ p1_if(p);
+ new_endif();
+ }
+ else
+ p1_elif(p);
+ }
+ else {
+ p = putx(p);
+ p1_if(p);
+ }
+ }
+ }
+
+ void
+#ifdef KR_headers
+putout(p)
+ expptr p;
+#else
+putout(expptr p)
+#endif
+{
+ p1_expr (p);
+
+/* Used to make temporaries in holdtemps available here, but they */
+/* may be reused too soon (e.g. when multiple **'s are involved). */
+}
+
+
+ void
+#ifdef KR_headers
+putcmgo(index, nlab, labs)
+ expptr index;
+ int nlab;
+ struct Labelblock **labs;
+#else
+putcmgo(expptr index, int nlab, struct Labelblock **labs)
+#endif
+{
+ if(! ISINT(index->headblock.vtype) )
+ {
+ execerr("computed goto index must be integer", CNULL);
+ return;
+ }
+
+ p1comp_goto (index, nlab, labs);
+}
+
+ static expptr
+#ifdef KR_headers
+krput(p)
+ register expptr p;
+#else
+krput(register expptr p)
+#endif
+{
+ register expptr e, e1;
+ register unsigned op;
+ int t = krparens == 2 ? TYDREAL : p->exprblock.vtype;
+
+ op = p->exprblock.opcode;
+ e = p->exprblock.leftp;
+ if (e->tag == TEXPR && e->exprblock.opcode == op) {
+ e1 = (expptr)mktmp(t, ENULL);
+ putout(putassign(cpexpr(e1), e));
+ p->exprblock.leftp = e1;
+ }
+ else
+ p->exprblock.leftp = putx(e);
+
+ e = p->exprblock.rightp;
+ if (e->tag == TEXPR && e->exprblock.opcode == op) {
+ e1 = (expptr)mktmp(t, ENULL);
+ putout(putassign(cpexpr(e1), e));
+ p->exprblock.rightp = e1;
+ }
+ else
+ p->exprblock.rightp = putx(e);
+ return p;
+ }
+
+ expptr
+#ifdef KR_headers
+putx(p)
+ register expptr p;
+#else
+putx(register expptr p)
+#endif
+{
+ int opc;
+ int k;
+
+ if (p)
+ switch(p->tag)
+ {
+ case TERROR:
+ break;
+
+ case TCONST:
+ switch(p->constblock.vtype)
+ {
+ case TYLOGICAL1:
+ case TYLOGICAL2:
+ case TYLOGICAL:
+#ifdef TYQUAD
+ case TYQUAD:
+#endif
+ case TYLONG:
+ case TYSHORT:
+ case TYINT1:
+ break;
+
+ case TYADDR:
+ break;
+ case TYREAL:
+ case TYDREAL:
+
+/* Don't write it out to the p2 file, since you'd need to call putconst,
+ which is just what we need to avoid in the translator */
+
+ break;
+ default:
+ p = putx( (expptr)putconst((Constp)p) );
+ break;
+ }
+ break;
+
+ case TEXPR:
+ switch(opc = p->exprblock.opcode)
+ {
+ case OPCALL:
+ case OPCCALL:
+ if( ISCOMPLEX(p->exprblock.vtype) )
+ p = putcxop(p);
+ else p = putcall(p, (Addrp *)NULL);
+ break;
+
+ case OPMIN:
+ case OPMAX:
+ p = putmnmx(p);
+ break;
+
+
+ case OPASSIGN:
+ if(ISCOMPLEX(p->exprblock.leftp->headblock.vtype)
+ || ISCOMPLEX(p->exprblock.rightp->headblock.vtype)) {
+ (void) putcxeq(p);
+ p = ENULL;
+ } else if( ISCHAR(p) )
+ p = putcheq(p);
+ else
+ goto putopp;
+ break;
+
+ case OPEQ:
+ case OPNE:
+ if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) ||
+ ISCOMPLEX(p->exprblock.rightp->headblock.vtype) )
+ {
+ p = putcxcmp(p);
+ break;
+ }
+ case OPLT:
+ case OPLE:
+ case OPGT:
+ case OPGE:
+ if(ISCHAR(p->exprblock.leftp))
+ {
+ p = putchcmp(p);
+ break;
+ }
+ goto putopp;
+
+ case OPPOWER:
+ p = putpower(p);
+ break;
+
+ case OPSTAR:
+ /* m * (2**k) -> m<<k */
+ if(INT(p->exprblock.leftp->headblock.vtype) &&
+ ISICON(p->exprblock.rightp) &&
+ ( (k = log_2(p->exprblock.rightp->constblock.Const.ci))>0) )
+ {
+ p->exprblock.opcode = OPLSHIFT;
+ frexpr(p->exprblock.rightp);
+ p->exprblock.rightp = ICON(k);
+ goto putopp;
+ }
+ if (krparens && ISREAL(p->exprblock.vtype))
+ return krput(p);
+
+ case OPMOD:
+ goto putopp;
+ case OPPLUS:
+ if (krparens && ISREAL(p->exprblock.vtype))
+ return krput(p);
+ case OPMINUS:
+ case OPSLASH:
+ case OPNEG:
+ case OPNEG1:
+ case OPABS:
+ case OPDABS:
+ if( ISCOMPLEX(p->exprblock.vtype) )
+ p = putcxop(p);
+ else goto putopp;
+ break;
+
+ case OPCONV:
+ if( ISCOMPLEX(p->exprblock.vtype) )
+ p = putcxop(p);
+ else if( ISCOMPLEX(p->exprblock.leftp->headblock.vtype) )
+ {
+ p = putx( mkconv(p->exprblock.vtype,
+ (expptr)realpart(putcx1(p->exprblock.leftp))));
+ }
+ else goto putopp;
+ break;
+
+ case OPNOT:
+ case OPOR:
+ case OPAND:
+ case OPEQV:
+ case OPNEQV:
+ case OPADDR:
+ case OPPLUSEQ:
+ case OPSTAREQ:
+ case OPCOMMA:
+ case OPQUEST:
+ case OPCOLON:
+ case OPBITOR:
+ case OPBITAND:
+ case OPBITXOR:
+ case OPBITNOT:
+ case OPLSHIFT:
+ case OPRSHIFT:
+ case OPASSIGNI:
+ case OPIDENTITY:
+ case OPCHARCAST:
+ case OPMIN2:
+ case OPMAX2:
+ case OPDMIN:
+ case OPDMAX:
+putopp:
+ p = putop(p);
+ break;
+
+ case OPCONCAT:
+ /* weird things like ichar(a//a) */
+ p = (expptr)putch1(p);
+ break;
+
+ default:
+ badop("putx", opc);
+ p = errnode ();
+ }
+ break;
+
+ case TADDR:
+ p = putaddr(p);
+ break;
+
+ default:
+ badtag("putx", p->tag);
+ p = errnode ();
+ }
+
+ return p;
+}
+
+
+
+ LOCAL expptr
+#ifdef KR_headers
+putop(p)
+ expptr p;
+#else
+putop(expptr p)
+#endif
+{
+ expptr lp, tp;
+ int pt, lt, lt1;
+ int comma;
+
+ switch(p->exprblock.opcode) /* check for special cases and rewrite */
+ {
+ case OPCONV:
+ pt = p->exprblock.vtype;
+ lp = p->exprblock.leftp;
+ lt = lp->headblock.vtype;
+
+/* Simplify nested type casts */
+
+ while(p->tag==TEXPR && p->exprblock.opcode==OPCONV &&
+ ( (ISREAL(pt)&&ONEOF(lt,MSKREAL|MSKCOMPLEX)) ||
+ (INT(pt)&&(ONEOF(lt,MSKINT|MSKADDR|MSKCHAR|M(TYSUBR)))) ))
+ {
+ if(pt==TYDREAL && lt==TYREAL)
+ {
+ if(lp->tag==TEXPR
+ && lp->exprblock.opcode == OPCONV) {
+ lt1 = lp->exprblock.leftp->headblock.vtype;
+ if (lt1 == TYDREAL) {
+ lp->exprblock.leftp =
+ putx(lp->exprblock.leftp);
+ return p;
+ }
+ if (lt1 == TYDCOMPLEX) {
+ lp->exprblock.leftp = putx(
+ (expptr)realpart(
+ putcx1(lp->exprblock.leftp)));
+ return p;
+ }
+ }
+ break;
+ }
+ else if (ISREAL(pt) && ISCOMPLEX(lt)) {
+ p->exprblock.leftp = putx(mkconv(pt,
+ (expptr)realpart(
+ putcx1(p->exprblock.leftp))));
+ break;
+ }
+ if(lt==TYCHAR && lp->tag==TEXPR &&
+ lp->exprblock.opcode==OPCALL)
+ {
+
+/* May want to make a comma expression here instead. I had one, but took
+ it out for my convenience, not for the convenience of the end user */
+
+ putout (putcall (lp, (Addrp *) &(p ->
+ exprblock.leftp)));
+ return putop (p);
+ }
+ if (lt == TYCHAR) {
+ p->exprblock.leftp = putx(p->exprblock.leftp);
+ return p;
+ }
+ if (pt < lt && ONEOF(lt,MSKINT|MSKREAL))
+ break;
+ frexpr(p->exprblock.vleng);
+ free( (charptr) p );
+ p = lp;
+ if (p->tag != TEXPR)
+ goto retputx;
+ pt = lt;
+ lp = p->exprblock.leftp;
+ lt = lp->headblock.vtype;
+ } /* while */
+ if(p->tag==TEXPR && p->exprblock.opcode==OPCONV)
+ break;
+ retputx:
+ return putx(p);
+
+ case OPADDR:
+ comma = NO;
+ lp = p->exprblock.leftp;
+ free( (charptr) p );
+ if(lp->tag != TADDR)
+ {
+ tp = (expptr)
+ mktmp(lp->headblock.vtype,lp->headblock.vleng);
+ p = putx( mkexpr(OPASSIGN, cpexpr(tp), lp) );
+ lp = tp;
+ comma = YES;
+ }
+ if(comma)
+ p = mkexpr(OPCOMMA, p, putaddr(lp));
+ else
+ p = (expptr)putaddr(lp);
+ return p;
+
+ case OPASSIGN:
+ case OPASSIGNI:
+ case OPLT:
+ case OPLE:
+ case OPGT:
+ case OPGE:
+ case OPEQ:
+ case OPNE:
+ ;
+ }
+
+ if( ops2[p->exprblock.opcode] <= 0)
+ badop("putop", p->exprblock.opcode);
+ lp = p->exprblock.leftp = putx(p->exprblock.leftp);
+ if (p -> exprblock.rightp) {
+ tp = p->exprblock.rightp = putx(p->exprblock.rightp);
+ if (ISCONST(tp) && ISCONST(lp))
+ p = fold(p);
+ }
+ return p;
+}
+
+ LOCAL expptr
+#ifdef KR_headers
+putpower(p)
+ expptr p;
+#else
+putpower(expptr p)
+#endif
+{
+ expptr base;
+ Addrp t1, t2;
+ ftnint k;
+ int type;
+ char buf[80]; /* buffer for text of comment */
+
+ if(!ISICON(p->exprblock.rightp) ||
+ (k = p->exprblock.rightp->constblock.Const.ci)<2)
+ Fatal("putpower: bad call");
+ base = p->exprblock.leftp;
+ type = base->headblock.vtype;
+ t1 = mktmp(type, ENULL);
+ t2 = NULL;
+
+ free ((charptr) p);
+ p = putassign (cpexpr((expptr) t1), base);
+
+ sprintf (buf, "Computing %ld%s power", k,
+ k == 2 ? "nd" : k == 3 ? "rd" : "th");
+ p1_comment (buf);
+
+ for( ; (k&1)==0 && k>2 ; k>>=1 )
+ {
+ p = mkexpr (OPCOMMA, p, putsteq(t1, t1));
+ }
+
+ if(k == 2) {
+
+/* Write the power computation out immediately */
+ putout (p);
+ p = putx( mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1)));
+ } else {
+ t2 = mktmp(type, ENULL);
+ p = mkexpr (OPCOMMA, p, putassign(cpexpr((expptr)t2),
+ cpexpr((expptr)t1)));
+
+ for(k>>=1 ; k>1 ; k>>=1)
+ {
+ p = mkexpr (OPCOMMA, p, putsteq(t1, t1));
+ if(k & 1)
+ {
+ p = mkexpr (OPCOMMA, p, putsteq(t2, t1));
+ }
+ }
+/* Write the power computation out immediately */
+ putout (p);
+ p = putx( mkexpr(OPSTAR, cpexpr((expptr)t2),
+ mkexpr(OPSTAR, cpexpr((expptr)t1), cpexpr((expptr)t1))));
+ }
+ frexpr((expptr)t1);
+ if(t2)
+ frexpr((expptr)t2);
+ return p;
+}
+
+
+
+
+ LOCAL Addrp
+#ifdef KR_headers
+intdouble(p)
+ Addrp p;
+#else
+intdouble(Addrp p)
+#endif
+{
+ register Addrp t;
+
+ t = mktmp(TYDREAL, ENULL);
+ putout (putassign(cpexpr((expptr)t), (expptr)p));
+ return(t);
+}
+
+
+
+
+
+/* Complex-type variable assignment */
+
+ LOCAL Addrp
+#ifdef KR_headers
+putcxeq(p)
+ register expptr p;
+#else
+putcxeq(register expptr p)
+#endif
+{
+ register Addrp lp, rp;
+ expptr code;
+
+ if(p->tag != TEXPR)
+ badtag("putcxeq", p->tag);
+
+ lp = putcx1(p->exprblock.leftp);
+ rp = putcx1(p->exprblock.rightp);
+ code = putassign ( (expptr)realpart(lp), (expptr)realpart(rp));
+
+ if( ISCOMPLEX(p->exprblock.vtype) )
+ {
+ code = mkexpr (OPCOMMA, code, putassign
+ (imagpart(lp), imagpart(rp)));
+ }
+ putout (code);
+ frexpr((expptr)rp);
+ free ((charptr) p);
+ return lp;
+}
+
+
+
+/* putcxop -- used to write out embedded calls to complex functions, and
+ complex arguments to procedures */
+
+ expptr
+#ifdef KR_headers
+putcxop(p)
+ expptr p;
+#else
+putcxop(expptr p)
+#endif
+{
+ return (expptr)putaddr((expptr)putcx1(p));
+}
+
+#define PAIR(x,y) mkexpr (OPCOMMA, (x), (y))
+
+ LOCAL Addrp
+#ifdef KR_headers
+putcx1(p)
+ register expptr p;
+#else
+putcx1(register expptr p)
+#endif
+{
+ expptr q;
+ Addrp lp, rp;
+ register Addrp resp;
+ int opcode;
+ int ltype, rtype;
+ long ts, tskludge;
+
+ if(p == NULL)
+ return(NULL);
+
+ switch(p->tag)
+ {
+ case TCONST:
+ if( ISCOMPLEX(p->constblock.vtype) )
+ p = (expptr) putconst((Constp)p);
+ return( (Addrp) p );
+
+ case TADDR:
+ resp = &p->addrblock;
+ if (addressable(p))
+ return (Addrp) p;
+ ts = tskludge = 0;
+ if (q = resp->memoffset) {
+ if (resp->uname_tag == UNAM_REF) {
+ q = cpexpr((tagptr)resp);
+ q->addrblock.vtype = tyint;
+ q->addrblock.cmplx_sub = 1;
+ p->addrblock.skip_offset = 1;
+ resp->user.name->vsubscrused = 1;
+ resp->uname_tag = UNAM_NAME;
+ tskludge = typesize[resp->vtype]
+ * (resp->Field ? 2 : 1);
+ }
+ else if (resp->isarray
+ && resp->vtype != TYCHAR) {
+ if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV))
+ && resp->uname_tag == UNAM_NAME)
+ q = mkexpr(OPMINUS, q,
+ mkintcon(resp->user.name->voffset));
+ ts = typesize[resp->vtype]
+ * (resp->Field ? 2 : 1);
+ q = resp->memoffset = mkexpr(OPSLASH, q,
+ ICON(ts));
+ }
+ }
+ resp = mktmp(tyint, ENULL);
+ putout(putassign(cpexpr((expptr)resp), q));
+ p->addrblock.memoffset = tskludge
+ ? mkexpr(OPSTAR, (expptr)resp, ICON(tskludge))
+ : (expptr)resp;
+ if (ts) {
+ resp = &p->addrblock;
+ q = mkexpr(OPSTAR, resp->memoffset, ICON(ts));
+ if (ONEOF(resp->vstg, M(STGCOMMON)|M(STGEQUIV))
+ && resp->uname_tag == UNAM_NAME)
+ q = mkexpr(OPPLUS, q,
+ mkintcon(resp->user.name->voffset));
+ resp->memoffset = q;
+ }
+ return (Addrp) p;
+
+ case TEXPR:
+ if( ISCOMPLEX(p->exprblock.vtype) )
+ break;
+ resp = mktmp(p->exprblock.vtype, ENULL);
+ /*first arg of above mktmp call was TYDREAL before 19950102 */
+ putout (putassign( cpexpr((expptr)resp), p));
+ return(resp);
+
+ case TERROR:
+ return NULL;
+
+ default:
+ badtag("putcx1", p->tag);
+ }
+
+ opcode = p->exprblock.opcode;
+ if(opcode==OPCALL || opcode==OPCCALL)
+ {
+ Addrp t;
+ p = putcall(p, &t);
+ putout(p);
+ return t;
+ }
+ else if(opcode == OPASSIGN)
+ {
+ return putcxeq (p);
+ }
+
+/* BUG (inefficient) Generates too many temporary variables */
+
+ resp = mktmp(p->exprblock.vtype, ENULL);
+ if(lp = putcx1(p->exprblock.leftp) )
+ ltype = lp->vtype;
+ if(rp = putcx1(p->exprblock.rightp) )
+ rtype = rp->vtype;
+
+ switch(opcode)
+ {
+ case OPCOMMA:
+ frexpr((expptr)resp);
+ resp = rp;
+ rp = NULL;
+ break;
+
+ case OPNEG:
+ case OPNEG1:
+ putout (PAIR (
+ putassign( (expptr)realpart(resp),
+ mkexpr(OPNEG, (expptr)realpart(lp), ENULL)),
+ putassign( imagpart(resp),
+ mkexpr(OPNEG, imagpart(lp), ENULL))));
+ break;
+
+ case OPPLUS:
+ case OPMINUS: { expptr r;
+ r = putassign( (expptr)realpart(resp),
+ mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp) ));
+ if(rtype < TYCOMPLEX)
+ q = putassign( imagpart(resp), imagpart(lp) );
+ else if(ltype < TYCOMPLEX)
+ {
+ if(opcode == OPPLUS)
+ q = putassign( imagpart(resp), imagpart(rp) );
+ else
+ q = putassign( imagpart(resp),
+ mkexpr(OPNEG, imagpart(rp), ENULL) );
+ }
+ else
+ q = putassign( imagpart(resp),
+ mkexpr(opcode, imagpart(lp), imagpart(rp) ));
+ r = PAIR (r, q);
+ putout (r);
+ break;
+ } /* case OPPLUS, OPMINUS: */
+ case OPSTAR:
+ if(ltype < TYCOMPLEX)
+ {
+ if( ISINT(ltype) )
+ lp = intdouble(lp);
+ putout (PAIR (
+ putassign( (expptr)realpart(resp),
+ mkexpr(OPSTAR, cpexpr((expptr)lp),
+ (expptr)realpart(rp))),
+ putassign( imagpart(resp),
+ mkexpr(OPSTAR, cpexpr((expptr)lp), imagpart(rp)))));
+ }
+ else if(rtype < TYCOMPLEX)
+ {
+ if( ISINT(rtype) )
+ rp = intdouble(rp);
+ putout (PAIR (
+ putassign( (expptr)realpart(resp),
+ mkexpr(OPSTAR, cpexpr((expptr)rp),
+ (expptr)realpart(lp))),
+ putassign( imagpart(resp),
+ mkexpr(OPSTAR, cpexpr((expptr)rp), imagpart(lp)))));
+ }
+ else {
+ putout (PAIR (
+ putassign( (expptr)realpart(resp), mkexpr(OPMINUS,
+ mkexpr(OPSTAR, (expptr)realpart(lp),
+ (expptr)realpart(rp)),
+ mkexpr(OPSTAR, imagpart(lp), imagpart(rp)))),
+ putassign( imagpart(resp), mkexpr(OPPLUS,
+ mkexpr(OPSTAR, (expptr)realpart(lp), imagpart(rp)),
+ mkexpr(OPSTAR, imagpart(lp),
+ (expptr)realpart(rp))))));
+ }
+ break;
+
+ case OPSLASH:
+ /* fixexpr has already replaced all divisions
+ * by a complex by a function call
+ */
+ if( ISINT(rtype) )
+ rp = intdouble(rp);
+ putout (PAIR (
+ putassign( (expptr)realpart(resp),
+ mkexpr(OPSLASH, (expptr)realpart(lp), cpexpr((expptr)rp))),
+ putassign( imagpart(resp),
+ mkexpr(OPSLASH, imagpart(lp), cpexpr((expptr)rp)))));
+ break;
+
+ case OPCONV:
+ if (!lp)
+ break;
+ if(ISCOMPLEX(lp->vtype) )
+ q = imagpart(lp);
+ else if(rp != NULL)
+ q = (expptr) realpart(rp);
+ else
+ q = mkrealcon(TYDREAL, "0");
+ putout (PAIR (
+ putassign( (expptr)realpart(resp), (expptr)realpart(lp)),
+ putassign( imagpart(resp), q)));
+ break;
+
+ default:
+ badop("putcx1", opcode);
+ }
+
+ frexpr((expptr)lp);
+ frexpr((expptr)rp);
+ free( (charptr) p );
+ return(resp);
+}
+
+
+
+
+/* Only .EQ. and .NE. may be performed on COMPLEX data, other relations
+ are not defined */
+
+ LOCAL expptr
+#ifdef KR_headers
+putcxcmp(p)
+ register expptr p;
+#else
+putcxcmp(register expptr p)
+#endif
+{
+ int opcode;
+ register Addrp lp, rp;
+ expptr q;
+
+ if(p->tag != TEXPR)
+ badtag("putcxcmp", p->tag);
+
+ opcode = p->exprblock.opcode;
+ lp = putcx1(p->exprblock.leftp);
+ rp = putcx1(p->exprblock.rightp);
+
+ q = mkexpr( opcode==OPEQ ? OPAND : OPOR ,
+ mkexpr(opcode, (expptr)realpart(lp), (expptr)realpart(rp)),
+ mkexpr(opcode, imagpart(lp), imagpart(rp)) );
+
+ free( (charptr) lp);
+ free( (charptr) rp);
+ free( (charptr) p );
+ if (ISCONST(q))
+ return q;
+ return putx( fixexpr((Exprp)q) );
+}
+
+/* putch1 -- Forces constants into the literal pool, among other things */
+
+ LOCAL Addrp
+#ifdef KR_headers
+putch1(p)
+ register expptr p;
+#else
+putch1(register expptr p)
+#endif
+{
+ Addrp t;
+ expptr e;
+
+ switch(p->tag)
+ {
+ case TCONST:
+ return( putconst((Constp)p) );
+
+ case TADDR:
+ return( (Addrp) p );
+
+ case TEXPR:
+ switch(p->exprblock.opcode)
+ {
+ expptr q;
+
+ case OPCALL:
+ case OPCCALL:
+
+ p = putcall(p, &t);
+ putout (p);
+ break;
+
+ case OPCONCAT:
+ t = mktmp(TYCHAR, ICON(lencat(p)));
+ q = (expptr) cpexpr(p->headblock.vleng);
+ p = putcat( cpexpr((expptr)t), p );
+ /* put the correct length on the block */
+ frexpr(t->vleng);
+ t->vleng = q;
+ putout (p);
+ break;
+
+ case OPCONV:
+ if(!ISICON(p->exprblock.vleng)
+ || p->exprblock.vleng->constblock.Const.ci!=1
+ || ! INT(p->exprblock.leftp->headblock.vtype) )
+ Fatal("putch1: bad character conversion");
+ t = mktmp(TYCHAR, ICON(1));
+ e = mkexpr(OPCONV, (expptr)t, ENULL);
+ e->headblock.vtype = TYCHAR;
+ p = putop( mkexpr(OPASSIGN, cpexpr(e), p));
+ putout (p);
+ break;
+ default:
+ badop("putch1", p->exprblock.opcode);
+ }
+ return(t);
+
+ default:
+ badtag("putch1", p->tag);
+ }
+ /* NOT REACHED */ return 0;
+}
+
+
+/* putchop -- Write out a character actual parameter; that is, this is
+ part of a procedure invocation */
+
+ Addrp
+#ifdef KR_headers
+putchop(p)
+ expptr p;
+#else
+putchop(expptr p)
+#endif
+{
+ p = putaddr((expptr)putch1(p));
+ return (Addrp)p;
+}
+
+
+
+
+ LOCAL expptr
+#ifdef KR_headers
+putcheq(p)
+ register expptr p;
+#else
+putcheq(register expptr p)
+#endif
+{
+ expptr lp, rp;
+ int nbad;
+
+ if(p->tag != TEXPR)
+ badtag("putcheq", p->tag);
+
+ lp = p->exprblock.leftp;
+ rp = p->exprblock.rightp;
+ frexpr(p->exprblock.vleng);
+ free( (charptr) p );
+
+/* If s = t // u, don't bother copying the result, write it directly into
+ this buffer */
+
+ nbad = badchleng(lp) + badchleng(rp);
+ if( rp->tag==TEXPR && rp->exprblock.opcode==OPCONCAT )
+ p = putcat(lp, rp);
+ else if( !nbad
+ && ISONE(lp->headblock.vleng)
+ && ISONE(rp->headblock.vleng) ) {
+ lp = mkexpr(OPCONV, lp, ENULL);
+ rp = mkexpr(OPCONV, rp, ENULL);
+ lp->headblock.vtype = rp->headblock.vtype = TYCHAR;
+ p = putop(mkexpr(OPASSIGN, lp, rp));
+ }
+ else
+ p = putx( call2(TYSUBR, "s_copy", lp, rp) );
+ return p;
+}
+
+
+
+
+ LOCAL expptr
+#ifdef KR_headers
+putchcmp(p)
+ register expptr p;
+#else
+putchcmp(register expptr p)
+#endif
+{
+ expptr lp, rp;
+
+ if(p->tag != TEXPR)
+ badtag("putchcmp", p->tag);
+
+ lp = p->exprblock.leftp;
+ rp = p->exprblock.rightp;
+
+ if(ISONE(lp->headblock.vleng) && ISONE(rp->headblock.vleng) ) {
+ lp = mkexpr(OPCONV, lp, ENULL);
+ rp = mkexpr(OPCONV, rp, ENULL);
+ lp->headblock.vtype = rp->headblock.vtype = TYCHAR;
+ }
+ else {
+ lp = call2(TYINT,"s_cmp", lp, rp);
+ rp = ICON(0);
+ }
+ p->exprblock.leftp = lp;
+ p->exprblock.rightp = rp;
+ p = putop(p);
+ return p;
+}
+
+
+
+
+
+/* putcat -- Writes out a concatenation operation. Two temporary arrays
+ are allocated, putct1() is called to initialize them, and then a
+ call to runtime library routine s_cat() is inserted.
+
+ This routine generates code which will perform an (nconc lhs rhs)
+ at runtime. The runtime funciton does not return a value, the routine
+ that calls this putcat must remember the name of lhs.
+*/
+
+
+ LOCAL expptr
+#ifdef KR_headers
+putcat(lhs0, rhs)
+ expptr lhs0;
+ register expptr rhs;
+#else
+putcat(expptr lhs0, register expptr rhs)
+#endif
+{
+ register Addrp lhs = (Addrp)lhs0;
+ int n, tyi;
+ Addrp length_var, string_var;
+ expptr p;
+ static char Writing_concatenation[] = "Writing concatenation";
+
+/* Create the temporary arrays */
+
+ n = ncat(rhs);
+ length_var = mktmpn(n, tyioint, ENULL);
+ string_var = mktmpn(n, TYADDR, ENULL);
+ frtemp((Addrp)cpexpr((expptr)length_var));
+ frtemp((Addrp)cpexpr((expptr)string_var));
+
+/* Initialize the arrays */
+
+ n = 0;
+ /* p1_comment scribbles on its argument, so we
+ * cannot safely pass a string literal here. */
+ p1_comment(Writing_concatenation);
+ putct1(rhs, length_var, string_var, &n);
+
+/* Create the invocation */
+
+ tyi = tyint;
+ tyint = tyioint; /* for -I2 */
+ p = putx (call4 (TYSUBR, "s_cat",
+ (expptr)lhs,
+ (expptr)string_var,
+ (expptr)length_var,
+ (expptr)putconst((Constp)ICON(n))));
+ tyint = tyi;
+
+ return p;
+}
+
+
+
+
+
+ LOCAL void
+#ifdef KR_headers
+putct1(q, length_var, string_var, ip)
+ register expptr q;
+ register Addrp length_var;
+ register Addrp string_var;
+ int *ip;
+#else
+putct1(register expptr q, register Addrp length_var, register Addrp string_var, int *ip)
+#endif
+{
+ int i;
+ Addrp length_copy, string_copy;
+ expptr e;
+ extern int szleng;
+
+ if(q->tag==TEXPR && q->exprblock.opcode==OPCONCAT)
+ {
+ putct1(q->exprblock.leftp, length_var, string_var,
+ ip);
+ putct1(q->exprblock.rightp, length_var, string_var,
+ ip);
+ frexpr (q -> exprblock.vleng);
+ free ((charptr) q);
+ }
+ else
+ {
+ i = (*ip)++;
+ e = cpexpr(q->headblock.vleng);
+ if (!e)
+ return; /* error -- character*(*) */
+ length_copy = (Addrp) cpexpr((expptr)length_var);
+ length_copy->memoffset =
+ mkexpr(OPPLUS,length_copy->memoffset, ICON(i*szleng));
+ string_copy = (Addrp) cpexpr((expptr)string_var);
+ string_copy->memoffset =
+ mkexpr(OPPLUS, string_copy->memoffset,
+ ICON(i*typesize[TYADDR]));
+ putout (PAIR (putassign((expptr)length_copy, e),
+ putassign((expptr)string_copy, addrof((expptr)putch1(q)))));
+ }
+}
+
+/* putaddr -- seems to write out function invocation actual parameters */
+
+ LOCAL expptr
+#ifdef KR_headers
+putaddr(p0)
+ expptr p0;
+#else
+putaddr(expptr p0)
+#endif
+{
+ register Addrp p;
+ chainp cp;
+
+ if (!(p = (Addrp)p0))
+ return ENULL;
+
+ if( p->tag==TERROR || (p->memoffset!=NULL && ISERROR(p->memoffset)) )
+ {
+ frexpr((expptr)p);
+ return ENULL;
+ }
+ if (p->isarray && p->memoffset)
+ if (p->uname_tag == UNAM_REF) {
+ cp = p->memoffset->listblock.listp;
+ for(; cp; cp = cp->nextp)
+ cp->datap = (char *)fixtype((tagptr)cp->datap);
+ }
+ else
+ p->memoffset = putx(p->memoffset);
+ return (expptr) p;
+}
+
+ LOCAL expptr
+#ifdef KR_headers
+addrfix(e)
+ expptr e;
+#else
+addrfix(expptr e)
+#endif
+ /* fudge character string length if it's a TADDR */
+{
+ return e->tag == TADDR ? mkexpr(OPIDENTITY, e, ENULL) : e;
+ }
+
+ LOCAL int
+#ifdef KR_headers
+typekludge(ccall, q, at, j)
+ int ccall;
+ register expptr q;
+ Atype *at;
+ int j;
+#else
+typekludge(int ccall, register expptr q, Atype *at, int j)
+#endif
+ /* j = alternate type */
+{
+ register int i, k;
+ extern int iocalladdr;
+ register Namep np;
+
+ /* Return value classes:
+ * < 100 ==> Fortran arg (pointer to type)
+ * < 200 ==> C arg
+ * < 300 ==> procedure arg
+ * < 400 ==> external, no explicit type
+ * < 500 ==> arg that may turn out to be
+ * either a variable or a procedure
+ */
+
+ k = q->headblock.vtype;
+ if (ccall) {
+ if (k == TYREAL)
+ k = TYDREAL; /* force double for library routines */
+ return k + 100;
+ }
+ if (k == TYADDR)
+ return iocalladdr;
+ i = q->tag;
+ if ((i == TEXPR && q->exprblock.opcode != OPCOMMA_ARG)
+ || (i == TADDR && q->addrblock.charleng)
+ || i == TCONST)
+ k = TYFTNLEN + 100;
+ else if (i == TADDR)
+ switch(q->addrblock.vclass) {
+ case CLPROC:
+ if (q->addrblock.uname_tag != UNAM_NAME)
+ k += 200;
+ else if ((np = q->addrblock.user.name)->vprocclass
+ != PTHISPROC) {
+ if (k && !np->vimpltype)
+ k += 200;
+ else {
+ if (j > 200 && infertypes && j < 300) {
+ k = j;
+ inferdcl(np, j-200);
+ }
+ else k = (np->vstg == STGEXT
+ ? extsymtab[np->vardesc.varno].extype
+ : 0) + 200;
+ at->cp = mkchain((char *)np, at->cp);
+ }
+ }
+ else if (k == TYSUBR)
+ k += 200;
+ break;
+
+ case CLUNKNOWN:
+ if (q->addrblock.vstg == STGARG
+ && q->addrblock.uname_tag == UNAM_NAME) {
+ k += 400;
+ at->cp = mkchain((char *)q->addrblock.user.name,
+ at->cp);
+ }
+ }
+ else if (i == TNAME && q->nameblock.vstg == STGARG) {
+ np = &q->nameblock;
+ switch(np->vclass) {
+ case CLPROC:
+ if (!np->vimpltype)
+ k += 200;
+ else if (j <= 200 || !infertypes || j >= 300)
+ k += 300;
+ else {
+ k = j;
+ inferdcl(np, j-200);
+ }
+ goto add2chain;
+
+ case CLUNKNOWN:
+ /* argument may be a scalar variable or a function */
+ if (np->vimpltype && j && infertypes
+ && j < 300) {
+ inferdcl(np, j % 100);
+ k = j;
+ }
+ else
+ k += 400;
+
+ /* to handle procedure args only so far known to be
+ * external, save a pointer to the symbol table entry...
+ */
+ add2chain:
+ at->cp = mkchain((char *)np, at->cp);
+ }
+ }
+ return k;
+ }
+
+ char *
+#ifdef KR_headers
+Argtype(k, buf)
+ int k;
+ char *buf;
+#else
+Argtype(int k, char *buf)
+#endif
+{
+ if (k < 100) {
+ sprintf(buf, "%s variable", ftn_types[k]);
+ return buf;
+ }
+ if (k < 200) {
+ k -= 100;
+ return ftn_types[k];
+ }
+ if (k < 300) {
+ k -= 200;
+ if (k == TYSUBR)
+ return ftn_types[TYSUBR];
+ sprintf(buf, "%s function", ftn_types[k]);
+ return buf;
+ }
+ if (k < 400)
+ return "external argument";
+ k -= 400;
+ sprintf(buf, "%s argument", ftn_types[k]);
+ return buf;
+ }
+
+ static void
+#ifdef KR_headers
+atype_squawk(at, msg)
+ Argtypes *at;
+ char *msg;
+#else
+atype_squawk(Argtypes *at, char *msg)
+#endif
+{
+ register Atype *a, *ae;
+ warn(msg);
+ for(a = at->atypes, ae = a + at->nargs; a < ae; a++)
+ frchain(&a->cp);
+ at->nargs = -1;
+ if (at->changes & 2 && !at->defined)
+ proc_protochanges++;
+ }
+
+ static char inconsist[] = "inconsistent calling sequences for ";
+
+ void
+#ifdef KR_headers
+bad_atypes(at, fname, i, j, k, here, prev)
+ Argtypes *at;
+ char *fname;
+ int i;
+ int j;
+ int k;
+ char *here;
+ char *prev;
+#else
+bad_atypes(Argtypes *at, char *fname, int i, int j, int k, char *here, char *prev)
+#endif
+{
+ char buf[208], buf1[32], buf2[32];
+
+ sprintf(buf, "%s%.90s,\n\targ %d: %s%s%s %s.",
+ inconsist, fname, i, here, Argtype(k, buf1),
+ prev, Argtype(j, buf2));
+ atype_squawk(at, buf);
+ }
+
+ int
+#ifdef KR_headers
+type_fixup(at, a, k)
+ Argtypes *at;
+ Atype *a;
+ int k;
+#else
+type_fixup(Argtypes *at, Atype *a, int k)
+#endif
+{
+ register struct Entrypoint *ep;
+ if (!infertypes)
+ return 0;
+ for(ep = entries; ep; ep = ep->entnextp)
+ if (at == ep->entryname->arginfo) {
+ a->type = k % 100;
+ return proc_argchanges = 1;
+ }
+ return 0;
+ }
+
+
+ void
+#ifdef KR_headers
+save_argtypes(arglist, at0, at1, ccall, fname, stg, nchargs, type, zap)
+ chainp arglist;
+ Argtypes **at0;
+ Argtypes **at1;
+ int ccall;
+ char *fname;
+ int stg;
+ int nchargs;
+ int type;
+ int zap;
+#else
+save_argtypes(chainp arglist, Argtypes **at0, Argtypes **at1, int ccall, char *fname, int stg, int nchargs, int type, int zap)
+#endif
+{
+ Argtypes *at;
+ chainp cp;
+ int i, i0, j, k, nargs, nbad, *t, *te;
+ Atype *atypes;
+ expptr q;
+ char buf[208], buf1[32], buf2[32];
+ static int initargs[4] = {TYCOMPLEX, TYDCOMPLEX, TYCHAR, TYFTNLEN+100};
+ static int *init_ap[TYSUBR+1] = {0,0,0,0,0,0,0,
+#ifdef TYQUAD
+ 0,
+#endif
+ initargs, initargs+1,0,0,0,initargs+2};
+ extern int init_ac[TYSUBR+1];
+
+ i0 = init_ac[type];
+ t = init_ap[type];
+ te = t + i0;
+ if (at = *at0) {
+ *at1 = at;
+ nargs = at->nargs;
+ if (nargs < 0 && type && at->changes & 2 && !at->defined)
+ --proc_protochanges;
+ if (at->dnargs >= 0 && zap != 2)
+ type = 0;
+ if (nargs < 0) { /* inconsistent usage seen */
+ if (type)
+ goto newlist;
+ return;
+ }
+ atypes = at->atypes;
+ i = nchargs;
+ for(nbad = 0; t < te; atypes++) {
+ if (++i > nargs) {
+ toomany:
+ i = nchargs + i0;
+ for(cp = arglist; cp; cp = cp->nextp)
+ i++;
+ toofew:
+ switch(zap) {
+ case 2: zap = 6; break;
+ case 1: if (at->defined & 4)
+ return;
+ }
+ sprintf(buf,
+ "%s%.90s:\n\there %d, previously %d args and string lengths.",
+ inconsist, fname, i, nargs);
+ atype_squawk(at, buf);
+ if (type) {
+ t = init_ap[type];
+ goto newlist;
+ }
+ return;
+ }
+ j = atypes->type;
+ k = *t++;
+ if (j != k && j-400 != k) {
+ cp = 0;
+ goto badtypes;
+ }
+ }
+ for(cp = arglist; cp; atypes++, cp = cp->nextp) {
+ if (++i > nargs)
+ goto toomany;
+ j = atypes->type;
+ if (!(q = (expptr)cp->datap))
+ continue;
+ k = typekludge(ccall, q, atypes, j);
+ if (k >= 300 || k == j)
+ continue;
+ if (j >= 300) {
+ if (k >= 200) {
+ if (k == TYUNKNOWN + 200)
+ continue;
+ if (j % 100 != k - 200
+ && k != TYSUBR + 200
+ && j != TYUNKNOWN + 300
+ && !type_fixup(at,atypes,k))
+ goto badtypes;
+ }
+ else if (j % 100 % TYSUBR != k % TYSUBR
+ && !type_fixup(at,atypes,k))
+ goto badtypes;
+ }
+ else if (k < 200 || j < 200)
+ if (j) {
+ if (k == TYUNKNOWN
+ && q->tag == TNAME
+ && q->nameblock.vinfproc) {
+ q->nameblock.vdcldone = 0;
+ impldcl((Namep)q);
+ }
+ goto badtypes;
+ }
+ else ; /* fall through to update */
+ else if (k == TYUNKNOWN+200)
+ continue;
+ else if (j != TYUNKNOWN+200)
+ {
+ badtypes:
+ if (++nbad == 1)
+ bad_atypes(at, fname, i - nchargs,
+ j, k, "here ", ", previously");
+ else
+ fprintf(stderr,
+ "\targ %d: here %s, previously %s.\n",
+ i - nchargs, Argtype(k,buf1),
+ Argtype(j,buf2));
+ if (!cp)
+ break;
+ continue;
+ }
+ /* We've subsequently learned the right type,
+ as in the call on zoo below...
+
+ subroutine foo(x, zap)
+ external zap
+ call goo(zap)
+ x = zap(3)
+ call zoo(zap)
+ end
+ */
+ if (!nbad) {
+ atypes->type = k;
+ at->changes |= 1;
+ }
+ }
+ if (i < nargs)
+ goto toofew;
+ if (nbad) {
+ if (type) {
+ /* we're defining the procedure */
+ t = init_ap[type];
+ te = t + i0;
+ proc_argchanges = 1;
+ goto newlist;
+ }
+ return;
+ }
+ if (zap == 1 && (at->changes & 5) != 5)
+ at->changes = 0;
+ return;
+ }
+ newlist:
+ i = i0 + nchargs;
+ for(cp = arglist; cp; cp = cp->nextp)
+ i++;
+ k = sizeof(Argtypes) + (i-1)*sizeof(Atype);
+ *at0 = *at1 = at = stg == STGEXT ? (Argtypes *)gmem(k,1)
+ : (Argtypes *) mem(k,1);
+ at->dnargs = at->nargs = i;
+ at->defined = zap & 6;
+ at->changes = type ? 0 : 4;
+ atypes = at->atypes;
+ for(; t < te; atypes++) {
+ atypes->type = *t++;
+ atypes->cp = 0;
+ }
+ for(cp = arglist; cp; atypes++, cp = cp->nextp) {
+ atypes->cp = 0;
+ atypes->type = (q = (expptr)cp->datap)
+ ? typekludge(ccall, q, atypes, 0)
+ : 0;
+ }
+ for(; --nchargs >= 0; atypes++) {
+ atypes->type = TYFTNLEN + 100;
+ atypes->cp = 0;
+ }
+ }
+
+ void
+#ifdef KR_headers
+saveargtypes(p)
+ register Exprp p;
+#else
+saveargtypes(register Exprp p)
+#endif
+ /* for writing prototypes */
+{
+ Addrp a;
+ Argtypes **at0, **at1;
+ Namep np;
+ chainp arglist;
+ expptr rp;
+ Extsym *e;
+ char *fname;
+
+ a = (Addrp)p->leftp;
+ switch(a->vstg) {
+ case STGEXT:
+ switch(a->uname_tag) {
+ case UNAM_EXTERN: /* e.g., sqrt() */
+ e = extsymtab + a->memno;
+ at0 = at1 = &e->arginfo;
+ fname = e->fextname;
+ break;
+ case UNAM_NAME:
+ np = a->user.name;
+ at0 = &extsymtab[np->vardesc.varno].arginfo;
+ at1 = &np->arginfo;
+ fname = np->fvarname;
+ break;
+ default:
+ goto bug;
+ }
+ break;
+ case STGARG:
+ if (a->uname_tag != UNAM_NAME)
+ goto bug;
+ np = a->user.name;
+ at0 = at1 = &np->arginfo;
+ fname = np->fvarname;
+ break;
+ default:
+ bug:
+ Fatal("Confusion in saveargtypes");
+ }
+ rp = p->rightp;
+ arglist = rp && rp->tag == TLIST ? rp->listblock.listp : 0;
+ save_argtypes(arglist, at0, at1, p->opcode == OPCCALL,
+ fname, a->vstg, 0, 0, 0);
+ }
+
+/* putcall - fix up the argument list, and write out the invocation. p
+ is expected to be initialized and point to an OPCALL or OPCCALL
+ expression. The return value is a pointer to a temporary holding the
+ result of a COMPLEX or CHARACTER operation, or NULL. */
+
+ LOCAL expptr
+#ifdef KR_headers
+putcall(p0, temp)
+ expptr p0;
+ Addrp *temp;
+#else
+putcall(expptr p0, Addrp *temp)
+#endif
+{
+ register Exprp p = (Exprp)p0;
+ chainp arglist; /* Pointer to actual arguments, if any */
+ chainp charsp; /* List of copies of the variables which
+ hold the lengths of character
+ parameters (other than procedure
+ parameters) */
+ chainp cp; /* Iterator over argument lists */
+ register expptr q; /* Pointer to the current argument */
+ Addrp fval; /* Function return value */
+ int type; /* type of the call - presumably this was
+ set elsewhere */
+ int byvalue; /* True iff we don't want to massage the
+ parameter list, since we're calling a C
+ library routine */
+ char *s;
+
+ type = p -> vtype;
+ charsp = NULL;
+ byvalue = (p->opcode == OPCCALL);
+
+/* Verify the actual parameters */
+
+ if (p == (Exprp) NULL)
+ err ("putcall: NULL call expression");
+ else if (p -> tag != TEXPR)
+ erri ("putcall: expected TEXPR, got '%d'", p -> tag);
+
+/* Find the argument list */
+
+ if(p->rightp && p -> rightp -> tag == TLIST)
+ arglist = p->rightp->listblock.listp;
+ else
+ arglist = NULL;
+
+/* Count the number of explicit arguments, including lengths of character
+ variables */
+
+ for(cp = arglist ; cp ; cp = cp->nextp)
+ if(!byvalue) {
+ q = (expptr) cp->datap;
+ if( ISCONST(q) )
+ {
+
+/* Even constants are passed by reference, so we need to put them in the
+ literal table */
+
+ q = (expptr) putconst((Constp)q);
+ cp->datap = (char *) q;
+ }
+
+/* Save the length expression of character variables (NOT character
+ procedures) for the end of the argument list */
+
+ if( ISCHAR(q) &&
+ (q->headblock.vclass != CLPROC
+ || q->headblock.vstg == STGARG
+ && q->tag == TADDR
+ && q->addrblock.uname_tag == UNAM_NAME
+ && q->addrblock.user.name->vprocclass == PTHISPROC))
+ {
+ p0 = cpexpr(q->headblock.vleng);
+ charsp = mkchain((char *)p0, charsp);
+ if (q->headblock.vclass == CLUNKNOWN
+ && q->headblock.vstg == STGARG)
+ q->addrblock.user.name->vpassed = 1;
+ else if (q->tag == TADDR
+ && q->addrblock.uname_tag == UNAM_CONST)
+ p0->constblock.Const.ci
+ += q->addrblock.user.Const.ccp1.blanks;
+ }
+ }
+ charsp = revchain(charsp);
+
+/* If the routine is a CHARACTER function ... */
+
+ if(type == TYCHAR)
+ {
+ if( ISICON(p->vleng) )
+ {
+
+/* Allocate a temporary to hold the return value of the function */
+
+ fval = mktmp(TYCHAR, p->vleng);
+ }
+ else {
+ err("adjustable character function");
+ if (temp)
+ *temp = 0;
+ return 0;
+ }
+ }
+
+/* If the routine is a COMPLEX function ... */
+
+ else if( ISCOMPLEX(type) )
+ fval = mktmp(type, ENULL);
+ else
+ fval = NULL;
+
+/* Write the function name, without taking its address */
+
+ p -> leftp = putx(fixtype(putaddr(p->leftp)));
+
+ if(fval)
+ {
+ chainp prepend;
+
+/* Prepend a copy of the function return value buffer out as the first
+ argument. */
+
+ prepend = mkchain((char *)putx(putaddr(cpexpr((expptr)fval))), arglist);
+
+/* If it's a character function, also prepend the length of the result */
+
+ if(type==TYCHAR)
+ {
+
+ prepend->nextp = mkchain((char *)putx(mkconv(TYLENG,
+ p->vleng)), arglist);
+ }
+ if (!(q = p->rightp))
+ p->rightp = q = (expptr)mklist(CHNULL);
+ q->listblock.listp = prepend;
+ }
+
+/* Scan through the fortran argument list */
+
+ for(cp = arglist ; cp ; cp = cp->nextp)
+ {
+ q = (expptr) (cp->datap);
+ if (q == ENULL)
+ err ("putcall: NULL argument");
+
+/* call putaddr only when we've got a parameter for a C routine or a
+ memory resident parameter */
+
+ if (q -> tag == TCONST && !byvalue)
+ q = (expptr) putconst ((Constp)q);
+
+ if(q->tag==TADDR && (byvalue || q->addrblock.vstg!=STGREG) ) {
+ if (q->addrblock.parenused
+ && !byvalue && q->headblock.vtype != TYCHAR)
+ goto make_copy;
+ cp->datap = (char *)putaddr(q);
+ }
+ else if( ISCOMPLEX(q->headblock.vtype) )
+ cp -> datap = (char *) putx (fixtype(putcxop(q)));
+ else if (ISCHAR(q) )
+ cp -> datap = (char *) putx (fixtype((expptr)putchop(q)));
+ else if( ! ISERROR(q) )
+ {
+ if(byvalue
+ || q->tag == TEXPR && q->exprblock.opcode == OPCHARCAST)
+ cp -> datap = (char *) putx(q);
+ else {
+ expptr t, t1;
+
+/* If we've got a register parameter, or (maybe?) a constant, save it in a
+ temporary first */
+ make_copy:
+ t = (expptr) mktmp(q->headblock.vtype, q->headblock.vleng);
+
+/* Assign to temporary variables before invoking the subroutine or
+ function */
+
+ t1 = putassign( cpexpr(t), q );
+ if (doin_setbound)
+ t = mkexpr(OPCOMMA_ARG, t1, t);
+ else
+ putout(t1);
+ cp -> datap = (char *) t;
+ } /* else */
+ } /* if !ISERROR(q) */
+ }
+
+/* Now adjust the lengths of the CHARACTER parameters */
+
+ for(cp = charsp ; cp ; cp = cp->nextp)
+ cp->datap = (char *)addrfix(putx(
+ /* in case MAIN has a character*(*)... */
+ (s = cp->datap) ? mkconv(TYLENG,(expptr)s)
+ : ICON(0)));
+
+/* ... and add them to the end of the argument list */
+
+ hookup (arglist, charsp);
+
+/* Return the name of the temporary used to hold the results, if any was
+ necessary. */
+
+ if (temp) *temp = fval;
+ else frexpr ((expptr)fval);
+
+ saveargtypes(p);
+
+ return (expptr) p;
+}
+
+
+
+/* putmnmx -- Put min or max. p must point to an EXPR, not just a
+ CONST */
+
+ LOCAL expptr
+#ifdef KR_headers
+putmnmx(p)
+ register expptr p;
+#else
+putmnmx(register expptr p)
+#endif
+{
+ int op, op2, type;
+ expptr arg, qp, temp;
+ chainp p0, p1;
+ Addrp sp, tp;
+ char comment_buf[80];
+ char *what;
+
+ if(p->tag != TEXPR)
+ badtag("putmnmx", p->tag);
+
+ type = p->exprblock.vtype;
+ op = p->exprblock.opcode;
+ op2 = op == OPMIN ? OPMIN2 : OPMAX2;
+ p0 = p->exprblock.leftp->listblock.listp;
+ free( (charptr) (p->exprblock.leftp) );
+ free( (charptr) p );
+
+ /* special case for two addressable operands */
+
+ if (addressable((expptr)p0->datap)
+ && (p1 = p0->nextp)
+ && addressable((expptr)p1->datap)
+ && !p1->nextp) {
+ if (type == TYREAL && forcedouble)
+ op2 = op == OPMIN ? OPDMIN : OPDMAX;
+ p = mkexpr(op2, mkconv(type, cpexpr((expptr)p0->datap)),
+ mkconv(type, cpexpr((expptr)p1->datap)));
+ frchain(&p0);
+ return p;
+ }
+
+ /* general case */
+
+ sp = mktmp(type, ENULL);
+
+/* We only need a second temporary if the arg list has an unaddressable
+ value */
+
+ tp = (Addrp) NULL;
+ qp = ENULL;
+ for (p1 = p0 -> nextp; p1; p1 = p1 -> nextp)
+ if (!addressable ((expptr) p1 -> datap)) {
+ tp = mktmp(type, ENULL);
+ qp = mkexpr(op2, cpexpr((expptr)sp), cpexpr((expptr)tp));
+ qp = fixexpr((Exprp)qp);
+ break;
+ } /* if */
+
+/* Now output the appropriate number of assignments and comparisons. Min
+ and max are implemented by the simple O(n) algorithm:
+
+ min (a, b, c, d) ==>
+ { <type> t1, t2;
+
+ t1 = a;
+ t2 = b; t1 = (t1 < t2) ? t1 : t2;
+ t2 = c; t1 = (t1 < t2) ? t1 : t2;
+ t2 = d; t1 = (t1 < t2) ? t1 : t2;
+ }
+*/
+
+ if (!doin_setbound) {
+ switch(op) {
+ case OPLT:
+ case OPMIN:
+ case OPDMIN:
+ case OPMIN2:
+ what = "IN";
+ break;
+ default:
+ what = "AX";
+ }
+ sprintf (comment_buf, "Computing M%s", what);
+ p1_comment (comment_buf);
+ }
+
+ p1 = p0->nextp;
+ temp = (expptr)p0->datap;
+ if (addressable(temp) && addressable((expptr)p1->datap)) {
+ p = mkconv(type, cpexpr(temp));
+ arg = mkconv(type, cpexpr((expptr)p1->datap));
+ temp = mkexpr(op2, p, arg);
+ if (!ISCONST(temp))
+ temp = fixexpr((Exprp)temp);
+ p1 = p1->nextp;
+ }
+ p = putassign (cpexpr((expptr)sp), temp);
+
+ for(; p1 ; p1 = p1->nextp)
+ {
+ if (addressable ((expptr) p1 -> datap)) {
+ arg = mkconv(type, cpexpr((expptr)p1->datap));
+ temp = mkexpr(op2, cpexpr((expptr)sp), arg);
+ temp = fixexpr((Exprp)temp);
+ } else {
+ temp = (expptr) cpexpr (qp);
+ p = mkexpr(OPCOMMA, p,
+ putassign(cpexpr((expptr)tp), (expptr)p1->datap));
+ } /* else */
+
+ if(p1->nextp)
+ p = mkexpr(OPCOMMA, p,
+ putassign(cpexpr((expptr)sp), temp));
+ else {
+ if (type == TYREAL && forcedouble)
+ temp->exprblock.opcode =
+ op == OPMIN ? OPDMIN : OPDMAX;
+ if (doin_setbound)
+ p = mkexpr(OPCOMMA, p, temp);
+ else {
+ putout (p);
+ p = putx(temp);
+ }
+ if (qp)
+ frexpr (qp);
+ } /* else */
+ } /* for */
+
+ frchain( &p0 );
+ return p;
+}
+
+
+ void
+#ifdef KR_headers
+putwhile(p)
+ expptr p;
+#else
+putwhile(expptr p)
+#endif
+{
+ long where;
+ int k, n;
+
+ if (wh_next >= wh_last)
+ {
+ k = wh_last - wh_first;
+ n = k + 100;
+ wh_next = mem(n,0);
+ wh_last = wh_first + n;
+ if (k)
+ memcpy(wh_next, wh_first, k);
+ wh_first = wh_next;
+ wh_next += k;
+ wh_last = wh_first + n;
+ }
+ p1put(P1_WHILE1START);
+ where = ftell(pass1_file);
+ if( !ISLOGICAL((k = (p = fixtype(p))->headblock.vtype)))
+ {
+ if(k != TYERROR)
+ err("non-logical expression in DO WHILE statement");
+ }
+ else {
+ p = putx(p);
+ *wh_next++ = ftell(pass1_file) > where;
+ p1put(P1_WHILE2START);
+ p1_expr(p);
+ }
+ }
diff --git a/usr.bin/f2c/readme b/usr.bin/f2c/readme
new file mode 100644
index 0000000..b8e5a67
--- /dev/null
+++ b/usr.bin/f2c/readme
@@ -0,0 +1,145 @@
+Type "make" to check the validity of the f2c source and compile f2c.
+
+On a PC, you may need to compile xsum.c with -DMSDOS (i.e., with
+MSDOS #defined).
+
+If your compiler does not understand ANSI/ISO C syntax (i.e., if
+you have a K&R C compiler), compile with -DKR_headers .
+
+On non-Unix systems where files have separate binary and text modes,
+you may need to "make xsumr.out" rather than "make xsum.out".
+
+If (in accordance with what follows) you need to modify the makefile
+or any of the source files, first issue a "make xsum.out" (or, if
+appropriate, "make xsumr.out") to check the validity of the f2c source,
+then make your changes, then type "make f2c".
+
+The file usignal.h is for the benefit of strictly ANSI include files
+on a UNIX system -- the ANSI signal.h does not define SIGHUP or SIGQUIT.
+You may need to modify usignal.h if you are not running f2c on a UNIX
+system.
+
+Should you get the message "xsum0.out xsum1.out differ", see what lines
+are different (`diff xsum0.out xsum1.out`) and ask netlib
+(e.g., netlib@research.att.com) to send you the files in question,
+plus the current xsum0.out (which may have changed) "from f2c/src".
+For example, if exec.c and expr.c have incorrect check sums, you would
+send netlib the message
+ send exec.c expr.c xsum0.out from f2c/src
+You can also ftp these files from netlib.att.com; for more details, ask
+netlib@research.att.com to "send readme from f2c".
+
+On some systems, the malloc and free in malloc.c let f2c run faster
+than do the standard malloc and free. Other systems may not tolerate
+redefinition of malloc and free (though changes of 8 Nov. 1994 may
+render this less of a problem than hitherto). If yours is such a
+system, you may either modify the makefile appropriately (remove
+"malloc.o" from the "OBJECTS =" assignment), or simply execute
+ cc -c -DCRAY malloc.c
+before typing "make". Still other systems have a -lmalloc that
+provides performance competitive with that from malloc.c; you may
+wish to compare the two on your system.
+
+On some BSD systems, you may need to create a file named "string.h"
+whose single line is
+#include <strings.h>
+you may need to add " -Dstrchr=index" to the "CFLAGS =" assignment
+in the makefile, and you may need to add " memset.o" to the "OBJECTS ="
+assignment in the makefile -- see the comments in memset.c .
+
+For non-UNIX systems, you may need to change some things in sysdep.c,
+such as the choice of intermediate file names.
+
+On some systems, you may need to modify parts of sysdep.h (which is
+included by defs.h). In particular, for Sun 4.1 systems and perhaps
+some others, you need to comment out the typedef of size_t. For some
+systems (e.g., IRIX 4.0.1 and AIX) it is better to add
+#define ANSI_Libraries
+to the beginning of sysdep.h (or to supply -DANSI_Libraries in the
+makefile).
+
+Alas, some systems #define __STDC__ but do not provide a true standard
+(ANSI or ISO) C environment, e.g. do not provide stdlib.h . If yours
+is such a system, then (a) you should complain loudly to your vendor
+about __STDC__ being erroneously defined, and (b) you should insert
+#undef __STDC__
+at the beginning of sysdep.h . You may need to make other adjustments.
+
+For some non-ANSI versions of stdio, you must change the values given
+to binread and binwrite in sysdep.c from "rb" and "wb" to "r" and "w".
+You may need to make this change if you run f2c and get an error
+message of the form
+ Compiler error ... cannot open intermediate file ...
+
+On many systems, it is best to combine libF77 and libI77 into a single
+library, say libf2c, as suggested in "readme from f2c". If you do not
+do this, then you should adjust the definition of link_msg in sysdep.c
+appropriately (e.g., replacing "-lf2c" by "-lF77 -lI77"). On Unix
+systems, the easiest way to create libf2c.a is to make libF77/libF77.a
+and libI77/libI77.a (after reading and heeding libF77/README and
+libI77/README), and then to say
+
+ cp libF77/libF77.a libf2c.a
+ ar ruv libf2c.a libI77/*.o
+ ranlib libf2c.a
+
+The last step, ranlib, may not be necessary on your system. On
+other systems, just compile all the .c files in libF77 and libI77,
+and put the resulting objects (except one or both of the Version
+objects) into a library, called perhaps f2c.lib .
+
+Some older C compilers object to
+ typedef void (*foo)();
+or to
+ typedef void zap;
+ zap (*foo)();
+If yours is such a compiler, change the definition of VOID in
+f2c.h from void to int.
+
+For convenience with systems that use control-Z to denote end-of-file,
+f2c treats control-Z characters (ASCII 26, '\x1a') that appear at the
+beginning of a line as an end-of-file indicator. You can disable this
+test by compiling lex.c with NO_EOF_CHAR_CHECK #defined, or can
+change control-Z to some other character by #defining EOF_CHAR to
+be the desired value.
+
+
+If your machine has IEEE, VAX, or IBM-mainframe arithmetic, but your
+printf is inaccurate (e.g., with Symantec C++ version 6.0,
+printf("%.17g",12.) prints 12.000000000000001), you can make f2c print
+correctly rounded numbers by compiling with -DUSE_DTOA and adding
+dtoa.o g_fmt.o to the makefile's OBJECTS = line, so it becomes
+
+ OBJECTS = $(OBJECTSd) malloc.o dtoa.o g_fmt.o
+
+Also add the rule
+
+ dtoa.o: dtoa.c
+ $(CC) -c $(CFLAGS) -DMALLOC=ckalloc -DIEEE... dtoa.c
+
+(without the initial tab) to the makefile, where IEEE... is one of
+IEEE_MC68k, IEEE_8087, VAX, or IBM, depending on your machine's
+arithmetic. See the comments near the start of dtoa.c.
+
+The relevant source files, dtoa.c and g_fmt.c, are available
+separately from netlib's fp directory. For example, you could
+send the E-mail message
+
+ send dtoa.c g_fmt.c from fp
+
+to netlib@research.att.com (or use anonymous ftp from netlib.att.com
+and look in directory /netlib/fp).
+
+The makefile has a rule for creating tokdefs.h. If you cannot use the
+makefile, an alternative is to extract tokdefs.h from the beginning of
+gram.c: it's the first 100 lines.
+
+
+Please send bug reports to dmg@research.att.com . The old index file
+(now called "readme" due to unfortunate changes in netlib conventions:
+"send readme from f2c") will report recent changes in the recent-change
+log at its end; all changes will be shown in the "changes" file
+("send changes from f2c"). To keep current source, you will need to
+request xsum0.out and version.c, in addition to the changed source
+files. Changes first appear on netlib@research.att.com, and in due
+time propagate to the other netlib sites that are kept current.
diff --git a/usr.bin/f2c/sysdep.c b/usr.bin/f2c/sysdep.c
new file mode 100644
index 0000000..29b1a05
--- /dev/null
+++ b/usr.bin/f2c/sysdep.c
@@ -0,0 +1,519 @@
+/****************************************************************
+Copyright 1990 - 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+#include "defs.h"
+#include "usignal.h"
+
+char binread[] = "rb", textread[] = "r";
+char binwrite[] = "wb", textwrite[] = "w";
+char *c_functions = "c_functions";
+char *coutput = "c_output";
+char *initfname = "raw_data";
+char *initbname = "raw_data.b";
+char *blkdfname = "block_data";
+char *p1_file = "p1_file";
+char *p1_bakfile = "p1_file.BAK";
+char *sortfname = "init_file";
+char *proto_fname = "proto_file";
+
+char link_msg[] = "-lf2c -lm"; /* was "-lF77 -lI77 -lm -lc"; */
+
+char *outbuf = "", *outbtail;
+
+#ifndef TMPDIR
+#ifdef MSDOS
+#define TMPDIR ""
+#else
+#define TMPDIR "/tmp"
+#endif
+#endif
+
+char *tmpdir = TMPDIR;
+#ifndef MSDOS
+#ifndef KR_headers
+extern int getpid(void);
+#endif
+#endif
+
+ void
+#ifdef KR_headers
+Un_link_all(cdelete)
+ int cdelete;
+#else
+Un_link_all(int cdelete)
+#endif
+{
+#ifndef KR_headers
+ extern int unlink(const char *);
+#endif
+ if (!debugflag) {
+ unlink(c_functions);
+ unlink(initfname);
+ unlink(p1_file);
+ unlink(sortfname);
+ unlink(blkdfname);
+ if (cdelete && coutput)
+ unlink(coutput);
+ }
+ }
+
+ void
+set_tmp_names(Void)
+{
+ int k;
+ if (debugflag == 1)
+ return;
+ k = strlen(tmpdir) + 16;
+ c_functions = (char *)ckalloc(7*k);
+ initfname = c_functions + k;
+ initbname = initfname + k;
+ blkdfname = initbname + k;
+ p1_file = blkdfname + k;
+ p1_bakfile = p1_file + k;
+ sortfname = p1_bakfile + k;
+ {
+#ifdef MSDOS
+ char buf[64], *s, *t;
+ if (!*tmpdir || *tmpdir == '.' && !tmpdir[1])
+ t = "";
+ else {
+ /* substitute \ for / to avoid confusion with a
+ * switch indicator in the system("sort ...")
+ * call in formatdata.c
+ */
+ for(s = tmpdir, t = buf; *s; s++, t++)
+ if ((*t = *s) == '/')
+ *t = '\\';
+ if (t[-1] != '\\')
+ *t++ = '\\';
+ *t = 0;
+ t = buf;
+ }
+ sprintf(c_functions, "%sf2c_func", t);
+ sprintf(initfname, "%sf2c_rd", t);
+ sprintf(blkdfname, "%sf2c_blkd", t);
+ sprintf(p1_file, "%sf2c_p1f", t);
+ sprintf(p1_bakfile, "%sf2c_p1fb", t);
+ sprintf(sortfname, "%sf2c_sort", t);
+#else
+ int pid = getpid();
+ sprintf(c_functions, "%s/f2c%d_func", tmpdir, pid);
+ sprintf(initfname, "%s/f2c%d_rd", tmpdir, pid);
+ sprintf(blkdfname, "%s/f2c%d_blkd", tmpdir, pid);
+ sprintf(p1_file, "%s/f2c%d_p1f", tmpdir, pid);
+ sprintf(p1_bakfile, "%s/f2c%d_p1fb", tmpdir, pid);
+ sprintf(sortfname, "%s/f2c%d_sort", tmpdir, pid);
+#endif
+ sprintf(initbname, "%s.b", initfname);
+ }
+ if (debugflag)
+ fprintf(diagfile, "%s %s %s %s %s %s\n", c_functions,
+ initfname, blkdfname, p1_file, p1_bakfile, sortfname);
+ }
+
+ char *
+#ifdef KR_headers
+c_name(s, ft)
+ char *s;
+ int ft;
+#else
+c_name(char *s, int ft)
+#endif
+{
+ char *b, *s0;
+ int c;
+
+ b = s0 = s;
+ while(c = *s++)
+ if (c == '/')
+ b = s;
+ if (--s < s0 + 3 || s[-2] != '.'
+ || ((c = *--s) != 'f' && c != 'F')) {
+ infname = s0;
+ Fatal("file name must end in .f or .F");
+ }
+ strcpy(outbtail, b);
+ outbtail[s-b] = ft;
+ b = copys(outbuf);
+ return b;
+ }
+
+ static void
+#ifdef KR_headers
+killed(sig)
+ int sig;
+#else
+killed(int sig)
+#endif
+{
+ sig = sig; /* shut up warning */
+ signal(SIGINT, SIG_IGN);
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_IGN);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+ signal(SIGTERM, SIG_IGN);
+ Un_link_all(1);
+ exit(126);
+ }
+
+ static void
+#ifdef KR_headers
+sig1catch(sig)
+ int sig;
+#else
+sig1catch(int sig)
+#endif
+{
+ sig = sig; /* shut up warning */
+ if (signal(sig, SIG_IGN) != SIG_IGN)
+ signal(sig, killed);
+ }
+
+ static void
+#ifdef KR_headers
+flovflo(sig)
+ int sig;
+#else
+flovflo(int sig)
+#endif
+{
+ sig = sig; /* shut up warning */
+ Fatal("floating exception during constant evaluation; cannot recover");
+ /* vax returns a reserved operand that generates
+ an illegal operand fault on next instruction,
+ which if ignored causes an infinite loop.
+ */
+ signal(SIGFPE, flovflo);
+}
+
+ void
+#ifdef KR_headers
+sigcatch(sig)
+ int sig;
+#else
+sigcatch(int sig)
+#endif
+{
+ sig = sig; /* shut up warning */
+ sig1catch(SIGINT);
+#ifdef SIGQUIT
+ sig1catch(SIGQUIT);
+#endif
+#ifdef SIGHUP
+ sig1catch(SIGHUP);
+#endif
+ sig1catch(SIGTERM);
+ signal(SIGFPE, flovflo); /* catch overflows */
+ }
+
+
+dofork(Void)
+{
+#ifdef MSDOS
+ Fatal("Only one Fortran input file allowed under MS-DOS");
+#else
+#ifndef KR_headers
+ extern int fork(void), wait(int*);
+#endif
+ int pid, status, w;
+ extern int retcode;
+
+ if (!(pid = fork()))
+ return 1;
+ if (pid == -1)
+ Fatal("bad fork");
+ while((w = wait(&status)) != pid)
+ if (w == -1)
+ Fatal("bad wait code");
+ retcode |= status >> 8;
+#endif
+ return 0;
+ }
+
+/* Initialization of tables that change with the character set... */
+
+char escapes[Table_size];
+
+#ifdef non_ASCII
+char *str_fmt[Table_size];
+static char *str0fmt[127] = { /*}*/
+#else
+char *str_fmt[Table_size] = {
+#endif
+ "\\000", "\\001", "\\002", "\\003", "\\004", "\\005", "\\006", "\\007",
+ "\\b", "\\t", "\\n", "\\013", "\\f", "\\r", "\\016", "\\017",
+ "\\020", "\\021", "\\022", "\\023", "\\024", "\\025", "\\026", "\\027",
+ "\\030", "\\031", "\\032", "\\033", "\\034", "\\035", "\\036", "\\037",
+ " ", "!", "\\\"", "#", "$", "%%", "&", "'",
+ "(", ")", "*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "<", "=", ">", "?",
+ "@", "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", "[", "\\\\", "]", "^", "_",
+ "`", "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", "{", "|", "}", "~"
+ };
+
+#ifdef non_ASCII
+char *chr_fmt[Table_size];
+static char *chr0fmt[127] = { /*}*/
+#else
+char *chr_fmt[Table_size] = {
+#endif
+ "\\0", "\\1", "\\2", "\\3", "\\4", "\\5", "\\6", "\\7",
+ "\\b", "\\t", "\\n", "\\13", "\\f", "\\r", "\\16", "\\17",
+ "\\20", "\\21", "\\22", "\\23", "\\24", "\\25", "\\26", "\\27",
+ "\\30", "\\31", "\\32", "\\33", "\\34", "\\35", "\\36", "\\37",
+ " ", "!", "\"", "#", "$", "%%", "&", "\\'",
+ "(", ")", "*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "<", "=", ">", "?",
+ "@", "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", "[", "\\\\", "]", "^", "_",
+ "`", "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", "{", "|", "}", "~"
+ };
+
+ void
+fmt_init(Void)
+{
+ static char *str1fmt[6] =
+ { "\\b", "\\t", "\\n", "\\f", "\\r", "\\%03o" };
+ register int i, j;
+ register char *s;
+
+ /* str_fmt */
+
+#ifdef non_ASCII
+ i = 0;
+#else
+ i = 127;
+#endif
+ for(; i < Table_size; i++)
+ str_fmt[i] = "\\%03o";
+#ifdef non_ASCII
+ for(i = 32; i < 127; i++) {
+ s = str0fmt[i];
+ str_fmt[*(unsigned char *)s] = s;
+ }
+ str_fmt['"'] = "\\\"";
+#else
+ if (Ansi == 1)
+ str_fmt[7] = chr_fmt[7] = "\\a";
+#endif
+
+ /* chr_fmt */
+
+#ifdef non_ASCII
+ for(i = 0; i < 32; i++)
+ chr_fmt[i] = chr0fmt[i];
+#else
+ i = 127;
+#endif
+ for(; i < Table_size; i++)
+ chr_fmt[i] = "\\%o";
+#ifdef non_ASCII
+ for(i = 32; i < 127; i++) {
+ s = chr0fmt[i];
+ j = *(unsigned char *)s;
+ if (j == '\\')
+ j = *(unsigned char *)(s+1);
+ chr_fmt[j] = s;
+ }
+#endif
+
+ /* escapes (used in lex.c) */
+
+ for(i = 0; i < Table_size; i++)
+ escapes[i] = i;
+ for(s = "btnfr0", i = 0; i < 6; i++)
+ escapes[*(unsigned char *)s++] = "\b\t\n\f\r"[i];
+ /* finish str_fmt and chr_fmt */
+
+ if (Ansi)
+ str1fmt[5] = "\\v";
+ if ('\v' == 'v') { /* ancient C compiler */
+ str1fmt[5] = "v";
+#ifndef non_ASCII
+ escapes['v'] = 11;
+#endif
+ }
+ else
+ escapes['v'] = '\v';
+ for(s = "\b\t\n\f\r\v", i = 0; j = *(unsigned char *)s++;)
+ str_fmt[j] = chr_fmt[j] = str1fmt[i++];
+ /* '\v' = 11 for both EBCDIC and ASCII... */
+ chr_fmt[11] = Ansi ? "\\v" : "\\13";
+ }
+
+ void
+outbuf_adjust(Void)
+{
+ int n, n1;
+ char *s;
+
+ n = n1 = strlen(outbuf);
+ if (*outbuf && outbuf[n-1] != '/')
+ n1++;
+ s = Alloc(n+64);
+ outbtail = s + n1;
+ strcpy(s, outbuf);
+ if (n != n1)
+ strcpy(s+n, "/");
+ outbuf = s;
+ }
+
+
+/* Unless SYSTEM_SORT is defined, the following gives a simple
+ * in-core version of dsort(). On Fortran source with huge DATA
+ * statements, the in-core version may exhaust the available memory,
+ * in which case you might either recompile this source file with
+ * SYSTEM_SORT defined (if that's reasonable on your system), or
+ * replace the dsort below with a more elaborate version that
+ * does a merging sort with the help of auxiliary files.
+ */
+
+#ifdef SYSTEM_SORT
+
+ int
+#ifdef KR_headers
+dsort(from, to)
+ char *from;
+ char *to;
+#else
+dsort(char *from, char *to)
+#endif
+{
+ char buf[200];
+ sprintf(buf, "sort <%s >%s", from, to);
+ return system(buf) >> 8;
+ }
+#else
+
+ static int
+#ifdef KR_headers
+ compare(a,b)
+ char *a, *b;
+#else
+ compare(const void *a, const void *b)
+#endif
+{ return strcmp(*(char **)a, *(char **)b); }
+
+#ifdef KR_headers
+dsort(from, to)
+ char *from;
+ char *to;
+#else
+dsort(char *from, char *to)
+#endif
+{
+ struct Memb {
+ struct Memb *next;
+ int n;
+ char buf[32000];
+ };
+ typedef struct Memb memb;
+ memb *mb, *mb1;
+ register char *x, *x0, *xe;
+ register int c, n;
+ FILE *f;
+ char **z, **z0;
+ int nn = 0;
+
+ f = opf(from, textread);
+ mb = (memb *)Alloc(sizeof(memb));
+ mb->next = 0;
+ x0 = x = mb->buf;
+ xe = x + sizeof(mb->buf);
+ n = 0;
+ for(;;) {
+ c = getc(f);
+ if (x >= xe && (c != EOF || x != x0)) {
+ if (!n)
+ return 126;
+ nn += n;
+ mb->n = n;
+ mb1 = (memb *)Alloc(sizeof(memb));
+ mb1->next = mb;
+ mb = mb1;
+ memcpy(mb->buf, x0, n = x-x0);
+ x0 = mb->buf;
+ x = x0 + n;
+ xe = x0 + sizeof(mb->buf);
+ n = 0;
+ }
+ if (c == EOF)
+ break;
+ if (c == '\n') {
+ ++n;
+ *x++ = 0;
+ x0 = x;
+ }
+ else
+ *x++ = c;
+ }
+ clf(&f, from, 1);
+ f = opf(to, textwrite);
+ if (x > x0) { /* shouldn't happen */
+ *x = 0;
+ ++n;
+ }
+ mb->n = n;
+ nn += n;
+ if (!nn) /* shouldn't happen */
+ goto done;
+ z = z0 = (char **)Alloc(nn*sizeof(char *));
+ for(mb1 = mb; mb1; mb1 = mb1->next) {
+ x = mb1->buf;
+ n = mb1->n;
+ for(;;) {
+ *z++ = x;
+ if (--n <= 0)
+ break;
+ while(*x++);
+ }
+ }
+ qsort((char *)z0, nn, sizeof(char *), compare);
+ for(n = nn, z = z0; n > 0; n--)
+ fprintf(f, "%s\n", *z++);
+ free((char *)z0);
+ done:
+ clf(&f, to, 1);
+ do {
+ mb1 = mb->next;
+ free((char *)mb);
+ }
+ while(mb = mb1);
+ return 0;
+ }
+#endif
diff --git a/usr.bin/f2c/sysdep.h b/usr.bin/f2c/sysdep.h
new file mode 100644
index 0000000..192e3a9
--- /dev/null
+++ b/usr.bin/f2c/sysdep.h
@@ -0,0 +1,98 @@
+/****************************************************************
+Copyright 1990, 1991, 1994 by AT&T Bell Laboratories, Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+/* This file is included at the start of defs.h; this file
+ * is an initial attempt to gather in one place some declarations
+ * that may need to be tweaked on some systems.
+ */
+
+#ifdef __STDC__
+#undef KR_headers
+#endif
+
+#ifndef KR_headers
+#ifndef ANSI_Libraries
+#define ANSI_Libraries
+#endif
+#ifndef ANSI_Prototypes
+#define ANSI_Prototypes
+#endif
+#endif
+
+#ifdef __BORLANDC__
+#define MSDOS
+#endif
+
+#ifdef __ZTC__ /* Zortech */
+#define MSDOS
+#endif
+
+#ifdef MSDOS
+#define ANSI_Libraries
+#define ANSI_Prototypes
+#define LONG_CAST (long)
+#else
+#define LONG_CAST
+#endif
+
+#include <stdio.h>
+
+#ifdef ANSI_Libraries
+#include <stddef.h>
+#include <stdlib.h>
+#else
+char *calloc(), *malloc(), *memcpy(), *memset(), *realloc();
+typedef int size_t;
+#ifndef atol
+ long atol();
+#endif
+
+#ifdef ANSI_Prototypes
+extern double atof(const char *);
+extern double strtod(const char*, char**);
+#else
+extern double atof(), strtod();
+#endif
+#endif
+
+/* On systems like VMS where fopen might otherwise create
+ * multiple versions of intermediate files, you may wish to
+ * #define scrub(x) unlink(x)
+ */
+#ifndef scrub
+#define scrub(x) /* do nothing */
+#endif
+
+/* On systems that severely limit the total size of statically
+ * allocated arrays, you may need to change the following to
+ * extern char **chr_fmt, *escapes, **str_fmt;
+ * and to modify sysdep.c appropriately
+ */
+extern char *chr_fmt[], escapes[], *str_fmt[];
+
+#include <string.h>
+
+#include "ctype.h"
+
+#define Table_size 256
+/* Table_size should be 1 << (bits/byte) */
diff --git a/usr.bin/f2c/tokens b/usr.bin/f2c/tokens
new file mode 100644
index 0000000..07b1881
--- /dev/null
+++ b/usr.bin/f2c/tokens
@@ -0,0 +1,100 @@
+SEOS
+SCOMMENT
+SLABEL
+SUNKNOWN
+SHOLLERITH
+SICON
+SRCON
+SDCON
+SBITCON
+SOCTCON
+SHEXCON
+STRUE
+SFALSE
+SNAME
+SNAMEEQ
+SFIELD
+SSCALE
+SINCLUDE
+SLET
+SASSIGN
+SAUTOMATIC
+SBACKSPACE
+SBLOCK
+SCALL
+SCHARACTER
+SCLOSE
+SCOMMON
+SCOMPLEX
+SCONTINUE
+SDATA
+SDCOMPLEX
+SDIMENSION
+SDO
+SDOUBLE
+SELSE
+SELSEIF
+SEND
+SENDFILE
+SENDIF
+SENTRY
+SEQUIV
+SEXTERNAL
+SFORMAT
+SFUNCTION
+SGOTO
+SASGOTO
+SCOMPGOTO
+SARITHIF
+SLOGIF
+SIMPLICIT
+SINQUIRE
+SINTEGER
+SINTRINSIC
+SLOGICAL
+SNAMELIST
+SOPEN
+SPARAM
+SPAUSE
+SPRINT
+SPROGRAM
+SPUNCH
+SREAD
+SREAL
+SRETURN
+SREWIND
+SSAVE
+SSTATIC
+SSTOP
+SSUBROUTINE
+STHEN
+STO
+SUNDEFINED
+SWRITE
+SLPAR
+SRPAR
+SEQUALS
+SCOLON
+SCOMMA
+SCURRENCY
+SPLUS
+SMINUS
+SSTAR
+SSLASH
+SPOWER
+SCONCAT
+SAND
+SOR
+SNEQV
+SEQV
+SNOT
+SEQ
+SLT
+SGT
+SLE
+SGE
+SNE
+SENDDO
+SWHILE
+SSLASHD
+SBYTE
diff --git a/usr.bin/f2c/usignal.h b/usr.bin/f2c/usignal.h
new file mode 100644
index 0000000..ba4ee6a
--- /dev/null
+++ b/usr.bin/f2c/usignal.h
@@ -0,0 +1,7 @@
+#include <signal.h>
+#ifndef SIGHUP
+#define SIGHUP 1 /* hangup */
+#endif
+#ifndef SIGQUIT
+#define SIGQUIT 3 /* quit */
+#endif
diff --git a/usr.bin/f2c/vax.c b/usr.bin/f2c/vax.c
new file mode 100644
index 0000000..57c4be9
--- /dev/null
+++ b/usr.bin/f2c/vax.c
@@ -0,0 +1,566 @@
+/****************************************************************
+Copyright 1990, 1992, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#include "defs.h"
+#include "pccdefs.h"
+#include "output.h"
+
+int regnum[] = {
+ 11, 10, 9, 8, 7, 6 };
+
+/* Put out a constant integer */
+
+ void
+#ifdef KR_headers
+prconi(fp, n)
+ FILEP fp;
+ ftnint n;
+#else
+prconi(FILEP fp, ftnint n)
+#endif
+{
+ fprintf(fp, "\t%ld\n", n);
+}
+
+
+
+/* Put out a constant address */
+
+ void
+#ifdef KR_headers
+prcona(fp, a)
+ FILEP fp;
+ ftnint a;
+#else
+prcona(FILEP fp, ftnint a)
+#endif
+{
+ fprintf(fp, "\tL%ld\n", a);
+}
+
+
+ void
+#ifdef KR_headers
+prconr(fp, x, k)
+ FILEP fp;
+ Constp x;
+ int k;
+#else
+prconr(FILEP fp, Constp x, int k)
+#endif
+{
+ char *x0, *x1;
+ char cdsbuf0[64], cdsbuf1[64];
+
+ if (k > 1) {
+ if (x->vstg) {
+ x0 = x->Const.cds[0];
+ x1 = x->Const.cds[1];
+ }
+ else {
+ x0 = cds(dtos(x->Const.cd[0]), cdsbuf0);
+ x1 = cds(dtos(x->Const.cd[1]), cdsbuf1);
+ }
+ fprintf(fp, "\t%s %s\n", x0, x1);
+ }
+ else
+ fprintf(fp, "\t%s\n", x->vstg ? x->Const.cds[0]
+ : cds(dtos(x->Const.cd[0]), cdsbuf0));
+}
+
+
+ char *
+#ifdef KR_headers
+memname(stg, mem)
+ int stg;
+ long mem;
+#else
+memname(int stg, long mem)
+#endif
+{
+ static char s[20];
+
+ switch(stg)
+ {
+ case STGCOMMON:
+ case STGEXT:
+ sprintf(s, "_%s", extsymtab[mem].cextname);
+ break;
+
+ case STGBSS:
+ case STGINIT:
+ sprintf(s, "v.%ld", mem);
+ break;
+
+ case STGCONST:
+ sprintf(s, "L%ld", mem);
+ break;
+
+ case STGEQUIV:
+ sprintf(s, "q.%ld", mem+eqvstart);
+ break;
+
+ default:
+ badstg("memname", stg);
+ }
+ return(s);
+}
+
+extern void addrlit Argdcl((Addrp));
+
+/* make_int_expr -- takes an arbitrary expression, and replaces all
+ occurrences of arguments with indirection */
+
+ expptr
+#ifdef KR_headers
+make_int_expr(e)
+ expptr e;
+#else
+make_int_expr(expptr e)
+#endif
+{
+ chainp listp;
+ Addrp ap;
+
+ if (e != ENULL)
+ switch (e -> tag) {
+ case TADDR:
+ if (e -> addrblock.vstg == STGARG
+ && !e->addrblock.isarray)
+ e = mkexpr (OPWHATSIN, e, ENULL);
+ break;
+ case TEXPR:
+ e -> exprblock.leftp = make_int_expr (e -> exprblock.leftp);
+ e -> exprblock.rightp = make_int_expr (e -> exprblock.rightp);
+ break;
+ case TLIST:
+ for(listp = e->listblock.listp; listp; listp = listp->nextp)
+ if ((ap = (Addrp)listp->datap)
+ && ap->tag == TADDR
+ && ap->uname_tag == UNAM_CONST)
+ addrlit(ap);
+ break;
+ default:
+ break;
+ } /* switch */
+
+ return e;
+} /* make_int_expr */
+
+
+
+/* prune_left_conv -- used in prolog() to strip type cast away from
+ left-hand side of parameter adjustments. This is necessary to avoid
+ error messages from cktype() */
+
+ expptr
+#ifdef KR_headers
+prune_left_conv(e)
+ expptr e;
+#else
+prune_left_conv(expptr e)
+#endif
+{
+ struct Exprblock *leftp;
+
+ if (e && e -> tag == TEXPR && e -> exprblock.leftp &&
+ e -> exprblock.leftp -> tag == TEXPR) {
+ leftp = &(e -> exprblock.leftp -> exprblock);
+ if (leftp -> opcode == OPCONV) {
+ e -> exprblock.leftp = leftp -> leftp;
+ free ((charptr) leftp);
+ }
+ }
+
+ return e;
+} /* prune_left_conv */
+
+
+ static int wrote_comment;
+ static FILE *comment_file;
+
+ static void
+write_comment(Void)
+{
+ if (!wrote_comment) {
+ wrote_comment = 1;
+ nice_printf (comment_file, "/* Parameter adjustments */\n");
+ }
+ }
+
+ static int *
+count_args(Void)
+{
+ register int *ac;
+ register chainp cp;
+ register struct Entrypoint *ep;
+ register Namep q;
+
+ ac = (int *)ckalloc(nallargs*sizeof(int));
+
+ for(ep = entries; ep; ep = ep->entnextp)
+ for(cp = ep->arglist; cp; cp = cp->nextp)
+ if (q = (Namep)cp->datap)
+ ac[q->argno]++;
+ return ac;
+ }
+
+ static int nu, *refs, *used;
+ static void awalk Argdcl((expptr));
+
+ static void
+#ifdef KR_headers
+aawalk(P)
+ struct Primblock *P;
+#else
+aawalk(struct Primblock *P)
+#endif
+{
+ chainp p;
+ expptr q;
+
+ if (P->argsp)
+ for(p = P->argsp->listp; p; p = p->nextp) {
+ q = (expptr)p->datap;
+ if (q->tag != TCONST)
+ awalk(q);
+ }
+ if (P->namep->vtype == TYCHAR) {
+ if (q = P->fcharp)
+ awalk(q);
+ if (q = P->lcharp)
+ awalk(q);
+ }
+ }
+
+ static void
+#ifdef KR_headers
+afwalk(P)
+ struct Primblock *P;
+#else
+afwalk(struct Primblock *P)
+#endif
+{
+ chainp p;
+ expptr q;
+ Namep np;
+
+ for(p = P->argsp->listp; p; p = p->nextp) {
+ q = (expptr)p->datap;
+ switch(q->tag) {
+ case TPRIM:
+ np = q->primblock.namep;
+ if (np->vknownarg)
+ if (!refs[np->argno]++)
+ used[nu++] = np->argno;
+ if (q->primblock.argsp == 0) {
+ if (q->primblock.namep->vclass == CLPROC
+ && q->primblock.namep->vprocclass
+ != PTHISPROC
+ || q->primblock.namep->vdim != NULL)
+ continue;
+ }
+ default:
+ awalk(q);
+ /* no break */
+ case TCONST:
+ continue;
+ }
+ }
+ }
+
+ static void
+#ifdef KR_headers
+awalk(e)
+ expptr e;
+#else
+awalk(expptr e)
+#endif
+{
+ Namep np;
+ top:
+ if (!e)
+ return;
+ switch(e->tag) {
+ default:
+ badtag("awalk", e->tag);
+ case TCONST:
+ case TERROR:
+ case TLIST:
+ return;
+ case TADDR:
+ if (e->addrblock.uname_tag == UNAM_NAME) {
+ np = e->addrblock.user.name;
+ if (np->vknownarg && !refs[np->argno]++)
+ used[nu++] = np->argno;
+ }
+ e = e->addrblock.memoffset;
+ goto top;
+ case TPRIM:
+ np = e->primblock.namep;
+ if (np->vknownarg && !refs[np->argno]++)
+ used[nu++] = np->argno;
+ if (e->primblock.argsp && np->vclass != CLVAR)
+ afwalk((struct Primblock *)e);
+ else
+ aawalk((struct Primblock *)e);
+ return;
+ case TEXPR:
+ awalk(e->exprblock.rightp);
+ e = e->exprblock.leftp;
+ goto top;
+ }
+ }
+
+ static chainp
+#ifdef KR_headers
+argsort(p0)
+ chainp p0;
+#else
+argsort(chainp p0)
+#endif
+{
+ Namep *args, q, *stack;
+ int i, nargs, nout, nst;
+ chainp *d, *da, p, rv, *rvp;
+ struct Dimblock *dp;
+
+ if (!p0)
+ return p0;
+ for(nargs = 0, p = p0; p; p = p->nextp)
+ nargs++;
+ args = (Namep *)ckalloc(i = nargs*(sizeof(Namep) + 2*sizeof(chainp)
+ + 2*sizeof(int)));
+ memset((char *)args, 0, i);
+ stack = args + nargs;
+ d = (chainp *)(stack + nargs);
+ refs = (int *)(d + nargs);
+ used = refs + nargs;
+
+ for(p = p0; p; p = p->nextp) {
+ q = (Namep) p->datap;
+ args[q->argno] = q;
+ }
+ for(p = p0; p; p = p->nextp) {
+ q = (Namep) p->datap;
+ if (!(dp = q->vdim))
+ continue;
+ i = dp->ndim;
+ while(--i >= 0)
+ awalk(dp->dims[i].dimexpr);
+ awalk(dp->basexpr);
+ while(nu > 0) {
+ refs[i = used[--nu]] = 0;
+ d[i] = mkchain((char *)q, d[i]);
+ }
+ }
+ for(i = nst = 0; i < nargs; i++)
+ for(p = d[i]; p; p = p->nextp)
+ refs[((Namep)p->datap)->argno]++;
+ while(--i >= 0)
+ if (!refs[i])
+ stack[nst++] = args[i];
+ if (nst == nargs) {
+ rv = p0;
+ goto done;
+ }
+ nout = 0;
+ rv = 0;
+ rvp = &rv;
+ while(nst > 0) {
+ nout++;
+ q = stack[--nst];
+ *rvp = p = mkchain((char *)q, CHNULL);
+ rvp = &p->nextp;
+ da = d + q->argno;
+ for(p = *da; p; p = p->nextp)
+ if (!--refs[(q = (Namep)p->datap)->argno])
+ stack[nst++] = q;
+ frchain(da);
+ }
+ if (nout < nargs)
+ for(i = 0; i < nargs; i++)
+ if (refs[i]) {
+ q = args[i];
+ errstr("Can't adjust %.38s correctly\n\
+ due to dependencies among arguments.",
+ q->fvarname);
+ *rvp = p = mkchain((char *)q, CHNULL);
+ rvp = &p->nextp;
+ frchain(d+i);
+ }
+ done:
+ free((char *)args);
+ return rv;
+ }
+
+ void
+#ifdef KR_headers
+prolog(outfile, p)
+ FILE *outfile;
+ register chainp p;
+#else
+prolog(FILE *outfile, register chainp p)
+#endif
+{
+ int addif, addif0, i, nd, size;
+ int *ac;
+ register Namep q;
+ register struct Dimblock *dp;
+ chainp p0, p1;
+
+ if(procclass == CLBLOCK)
+ return;
+ p0 = p;
+ p1 = p = argsort(p);
+ wrote_comment = 0;
+ comment_file = outfile;
+ ac = 0;
+
+/* Compute the base addresses and offsets for the array parameters, and
+ assign these values to local variables */
+
+ addif = addif0 = nentry > 1;
+ for(; p ; p = p->nextp)
+ {
+ q = (Namep) p->datap;
+ if(dp = q->vdim) /* if this param is an array ... */
+ {
+ expptr Q, expr;
+
+ /* See whether to protect the following with an if. */
+ /* This only happens when there are multiple entries. */
+
+ nd = dp->ndim - 1;
+ if (addif0) {
+ if (!ac)
+ ac = count_args();
+ if (ac[q->argno] == nentry)
+ addif = 0;
+ else if (dp->basexpr
+ || dp->baseoffset->constblock.Const.ci)
+ addif = 1;
+ else for(addif = i = 0; i <= nd; i++)
+ if (dp->dims[i].dimexpr
+ && (i < nd || !q->vlastdim)) {
+ addif = 1;
+ break;
+ }
+ if (addif) {
+ write_comment();
+ nice_printf(outfile, "if (%s) {\n", /*}*/
+ q->cvarname);
+ next_tab(outfile);
+ }
+ }
+ for(i = 0 ; i <= nd; ++i)
+
+/* Store the variable length of each dimension (which is fixed upon
+ runtime procedure entry) into a local variable */
+
+ if ((Q = dp->dims[i].dimexpr)
+ && (i < nd || !q->vlastdim)) {
+ expr = (expptr)cpexpr(Q);
+ write_comment();
+ out_and_free_statement (outfile, mkexpr (OPASSIGN,
+ fixtype(cpexpr(dp->dims[i].dimsize)), expr));
+ } /* if dp -> dims[i].dimexpr */
+
+/* size will equal the size of a single element, or -1 if the type is
+ variable length character type */
+
+ size = typesize[ q->vtype ];
+ if(q->vtype == TYCHAR)
+ if( ISICON(q->vleng) )
+ size *= q->vleng->constblock.Const.ci;
+ else
+ size = -1;
+
+ /* Fudge the argument pointers for arrays so subscripts
+ * are 0-based. Not done if array bounds are being checked.
+ */
+ if(dp->basexpr) {
+
+/* Compute the base offset for this procedure */
+
+ write_comment();
+ out_and_free_statement (outfile, mkexpr (OPASSIGN,
+ cpexpr(fixtype(dp->baseoffset)),
+ cpexpr(fixtype(dp->basexpr))));
+ } /* if dp -> basexpr */
+
+ if(! checksubs) {
+ if(dp->basexpr) {
+ expptr tp;
+
+/* If the base of this array has a variable adjustment ... */
+
+ tp = (expptr) cpexpr (dp -> baseoffset);
+ if(size < 0 || q -> vtype == TYCHAR)
+ tp = mkexpr (OPSTAR, tp, cpexpr (q -> vleng));
+
+ write_comment();
+ tp = mkexpr (OPMINUSEQ,
+ mkconv (TYADDR, (expptr)p->datap),
+ mkconv(TYINT, fixtype
+ (fixtype (tp))));
+/* Avoid type clash by removing the type conversion */
+ tp = prune_left_conv (tp);
+ out_and_free_statement (outfile, tp);
+ } else if(dp->baseoffset->constblock.Const.ci != 0) {
+
+/* if the base of this array has a nonzero constant adjustment ... */
+
+ expptr tp;
+
+ write_comment();
+ if(size > 0 && q -> vtype != TYCHAR) {
+ tp = prune_left_conv (mkexpr (OPMINUSEQ,
+ mkconv (TYADDR, (expptr)p->datap),
+ mkconv (TYINT, fixtype
+ (cpexpr (dp->baseoffset)))));
+ out_and_free_statement (outfile, tp);
+ } else {
+ tp = prune_left_conv (mkexpr (OPMINUSEQ,
+ mkconv (TYADDR, (expptr)p->datap),
+ mkconv (TYINT, fixtype
+ (mkexpr (OPSTAR, cpexpr (dp -> baseoffset),
+ cpexpr (q -> vleng))))));
+ out_and_free_statement (outfile, tp);
+ } /* else */
+ } /* if dp -> baseoffset -> const */
+ } /* if !checksubs */
+
+ if (addif) {
+ nice_printf(outfile, /*{*/ "}\n");
+ prev_tab(outfile);
+ }
+ }
+ }
+ if (wrote_comment)
+ nice_printf (outfile, "\n/* Function Body */\n");
+ if (ac)
+ free((char *)ac);
+ if (p0 != p1)
+ frchain(&p1);
+} /* prolog */
diff --git a/usr.bin/f2c/version.c b/usr.bin/f2c/version.c
new file mode 100644
index 0000000..e82655b
--- /dev/null
+++ b/usr.bin/f2c/version.c
@@ -0,0 +1,2 @@
+char F2C_version[] = "19950920";
+char xxxvers[] = "\n@(#) FORTRAN 77 to C Translator, VERSION 19950920\n";
diff --git a/usr.bin/f2c/xsum.c b/usr.bin/f2c/xsum.c
new file mode 100644
index 0000000..b87ace7
--- /dev/null
+++ b/usr.bin/f2c/xsum.c
@@ -0,0 +1,237 @@
+/****************************************************************
+Copyright 1990, 1993, 1994 by AT&T Bell Laboratories and Bellcore.
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the names of AT&T Bell Laboratories or
+Bellcore or any of their entities not be used in advertising or
+publicity pertaining to distribution of the software without
+specific, written prior permission.
+
+AT&T and Bellcore disclaim all warranties with regard to this
+software, including all implied warranties of merchantability
+and fitness. In no event shall AT&T or Bellcore be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether
+in an action of contract, negligence or other tortious action,
+arising out of or in connection with the use or performance of
+this software.
+****************************************************************/
+
+#define _POSIX_SOURCE
+#include "stdio.h"
+#ifndef KR_headers
+#include "stdlib.h"
+#include "sys/types.h"
+#include "fcntl.h" /* for declaration of open, O_RDONLY */
+#include "unistd.h" /* for read, close */
+#endif
+#ifdef MSDOS
+#include "io.h"
+#endif
+#ifndef O_RDONLY
+#define O_RDONLY 0
+#endif
+#ifndef O_BINARY
+#define O_BINARY O_RDONLY
+#endif
+
+ char *progname;
+ static int ignore_cr;
+
+ void
+#ifdef KR_headers
+usage(rc)
+#else
+usage(int rc)
+#endif
+{
+ fprintf(stderr, "usage: %s [-r] [file [file...]]\n\
+ option -r ignores carriage return characters\n", progname);
+ exit(rc);
+ }
+
+typedef unsigned char Uchar;
+
+ long
+#ifdef KR_headers
+sum32(sum, x, n)
+ register long sum;
+ register Uchar *x;
+ int n;
+#else
+sum32(register long sum, register Uchar *x, int n)
+#endif
+{
+ register Uchar *xe;
+ static long crc_table[256] = {
+ 0, 151466134, 302932268, 453595578,
+ -9583591, -160762737, -312236747, -463170141,
+ -19167182, -136529756, -321525474, -439166584,
+ 28724267, 145849533, 330837255, 448732561,
+ -38334364, -189783822, -273059512, -423738914,
+ 47895677, 199091435, 282375505, 433292743,
+ 57448534, 174827712, 291699066, 409324012,
+ -67019697, -184128295, -300991133, -418902539,
+ -76668728, -227995554, -379567644, -530091662,
+ 67364049, 218420295, 369985021, 520795499,
+ 95791354, 213031020, 398182870, 515701056,
+ -86479645, -203465611, -388624945, -506380967,
+ 114897068, 266207290, 349655424, 500195606,
+ -105581387, -256654301, -340093543, -490887921,
+ -134039394, -251295736, -368256590, -485758684,
+ 124746887, 241716241, 358686123, 476458301,
+ -153337456, -2395898, -455991108, -304803798,
+ 162629001, 11973919, 465560741, 314102835,
+ 134728098, 16841012, 436840590, 319723544,
+ -144044613, -26395347, -446403433, -329032703,
+ 191582708, 40657250, 426062040, 274858062,
+ -200894995, -50223749, -435620671, -284179369,
+ -172959290, -55056048, -406931222, -289830788,
+ 182263263, 64630089, 416513267, 299125861,
+ 229794136, 78991822, 532414580, 381366498,
+ -220224191, -69691945, -523123603, -371788549,
+ -211162774, -93398532, -513308602, -396314416,
+ 201600371, 84090341, 503991391, 386759881,
+ -268078788, -117292630, -502591472, -351526778,
+ 258520357, 107972019, 493278217, 341959839,
+ 249493774, 131713432, 483432482, 366454964,
+ -239911657, -122417791, -474129349, -356881235,
+ -306674912, -457198666, -4791796, -156118374,
+ 315967289, 466778031, 14362133, 165418627,
+ 325258002, 442776452, 23947838, 141187752,
+ -334573813, -452329571, -33509849, -150495567,
+ 269456196, 419996626, 33682024, 184992510,
+ -278767779, -429561909, -43239823, -194312473,
+ -288089226, -405591072, -52790694, -170046772,
+ 297394031, 415166457, 62373443, 179343061,
+ 383165416, 533828478, 81314500, 232780370,
+ -373594127, -524527769, -72022307, -223201717,
+ -401789990, -519431348, -100447498, -217810336,
+ 392228803, 510123861, 91131631, 208256633,
+ -345918580, -496598246, -110112096, -261561802,
+ 336361365, 487278339, 100800185, 251995695,
+ 364526526, 482151208, 129260178, 246639108,
+ -354943065, -472854735, -119955829, -237064675,
+ 459588272, 308539942, 157983644, 7181066,
+ -469170519, -317835713, -167286907, -16754925,
+ -440448382, -323454444, -139383890, -21619912,
+ 450006683, 332774925, 148697015, 31186721,
+ -422325548, -271261118, -186797064, -36011154,
+ 431888077, 280569435, 196114401, 45565815,
+ 403200742, 286222960, 168180682, 50400092,
+ -412770561, -295522711, -177471533, -59977915,
+ -536157576, -384970002, -234585260, -83643454,
+ 526853729, 375396087, 225003341, 74348507,
+ 517040714, 399923932, 215944038, 98057200,
+ -507728301, -390357307, -206385281, -88735767,
+ 498987548, 347783818, 263426864, 112501670,
+ -489671163, -338229613, -253864151, -103192641,
+ -479823314, -362722632, -244835582, -126932076,
+ 470531639, 353144481, 235265819, 117632909
+ };
+
+ xe = x + n;
+ while(x < xe)
+ sum = crc_table[(sum ^ *x++) & 0xff] ^ (sum >> 8 & 0xffffff);
+ return sum;
+ }
+
+ int
+#ifdef KR_headers
+cr_purge(buf, n)
+ Uchar *buf;
+ int n;
+#else
+cr_purge(Uchar *buf, int n)
+#endif
+{
+ register Uchar *b, *b1, *be;
+ b = buf;
+ be = b + n;
+ while(b < be)
+ if (*b++ == '\r') {
+ b1 = b - 1;
+ while(b < be)
+ if ((*b1 = *b++) != '\r')
+ b1++;
+ return b1 - buf;
+ }
+ return n;
+ }
+
+static Uchar Buf[16*1024];
+
+ void
+#ifdef KR_headers
+process(s, x)
+ char *s;
+ int x;
+#else
+process(char *s, int x)
+#endif
+{
+ register int n;
+ long fsize, sum;
+
+ sum = 0;
+ fsize = 0;
+ while((n = read(x, (char *)Buf, sizeof(Buf))) > 0) {
+ if (ignore_cr)
+ n = cr_purge(Buf, n);
+ fsize += n;
+ sum = sum32(sum, Buf, n);
+ }
+ sum &= 0xffffffff;
+ if (n==0)
+ printf("%s\t%lx\t%ld\n", s, sum & 0xffffffff, fsize);
+ else { perror(s); }
+ close(x);
+ }
+
+#ifdef KR_headers
+main(argc, argv)
+ char **argv;
+#else
+main(int argc, char **argv)
+#endif
+{
+ int x;
+ char *s;
+ static int rc;
+
+ progname = *argv;
+ argc = argc; /* turn off "not used" warning */
+ s = *++argv;
+ if (s && *s == '-') {
+ switch(s[1]) {
+ case '?':
+ usage(0);
+ case 'r':
+ ignore_cr = 1;
+ case '-':
+ break;
+ default:
+ fprintf(stderr, "invalid option %s\n", s);
+ usage(1);
+ }
+ s = *++argv;
+ }
+ if (s) do {
+ x = open(s, O_RDONLY|O_BINARY);
+ if (x < 0) {
+ fprintf(stderr, "%s: can't open %s\n", progname, s);
+ rc |= 1;
+ }
+ else
+ process(s, x);
+ }
+ while(s = *++argv);
+ else {
+ process("/dev/stdin", fileno(stdin));
+ }
+ return rc;
+ }
diff --git a/usr.bin/f2c/xsum0.out b/usr.bin/f2c/xsum0.out
new file mode 100644
index 0000000..0eecb1c
--- /dev/null
+++ b/usr.bin/f2c/xsum0.out
@@ -0,0 +1,56 @@
+Notice 1211689a 1195
+README 110fc3e8 4398
+cds.c 38ec751 4076
+data.c fa8cecd6 9370
+defines.h e500bb1a 8464
+defs.h 72515cc 24172
+equiv.c f6b65bcc 8831
+error.c 7e4ede 3648
+exec.c e279d99 17980
+expr.c 1d64bc48 60705
+f2c.1 fa354030 6042
+f2c.1t e571e717 5988
+f2c.h 1be46b90 4271
+format.c ed4c1a9 52848
+format.h e861ad39 300
+formatdata.c eb45f76a 24861
+ftypes.h 18b86a27 1377
+gram.dcl 11121871 7977
+gram.exec e190cb8e 3026
+gram.expr e3da3320 3137
+gram.head ecf8a5e0 7554
+gram.io 1b7c281c 3294
+init.c ffd3616 11452
+intr.c e9519537 19813
+io.c feb30d5a 29027
+iob.h fe479ed3 459
+lex.c ffae6a9f 31482
+machdefs.h 4950e5b 659
+main.c 54cb955 17040
+makefile f3877062 2766
+malloc.c 5c2be2a 3422
+mem.c 133c066 4839
+memset.c 17404d52 1964
+misc.c fe327633 18006
+names.c 3123927 19947
+names.h f25436a3 689
+niceprintf.c f976e7dd 9781
+niceprintf.h c31f08c 412
+output.c f0627d49 38529
+output.h edfe9e59 2113
+p1defs.h e4e11c4e 5776
+p1output.c 157a2c7e 12175
+parse.h e457df2e 855
+parse_args.c e01b1fe9 13035
+pccdefs.h 1b4fbbee 1195
+pread.c 5ac0d2 16490
+proc.c 116a13d2 34930
+put.c fe8a1281 9480
+putpcc.c 1cebcba8 40081
+sysdep.c 174741bc 10939
+sysdep.h 1021aa5e 2834
+tokens 194fccfe 727
+usignal.h 1c4ce909 124
+vax.c cf2e339 11030
+version.c 3351b7b 107
+xsum.c e2d50e0b 6437
diff --git a/usr.bin/false/Makefile b/usr.bin/false/Makefile
new file mode 100644
index 0000000..ed511ab
--- /dev/null
+++ b/usr.bin/false/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= false
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/false/false.1 b/usr.bin/false/false.1
new file mode 100644
index 0000000..bf9a9eb
--- /dev/null
+++ b/usr.bin/false/false.1
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)false.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt FALSE 1
+.Os BSD 4.2
+.Sh NAME
+.Nm false
+.Nd return false value
+.Sh SYNOPSIS
+.Nm false
+.Sh DESCRIPTION
+.Nm False
+is usually used in a Bourne shell script.
+It tests for the appropriate status "false" before running
+(or failing to run) a list of commands.
+.Pp
+The
+.Nm false
+utility always exits with a value other than zero.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr true 1
+.Sh STANDARDS
+The
+.Nm false
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/false/false.c b/usr.bin/false/false.c
new file mode 100644
index 0000000..3bb8c00
--- /dev/null
+++ b/usr.bin/false/false.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)false.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+main()
+{
+ exit(1);
+}
diff --git a/usr.bin/fib/Makefile b/usr.bin/fib/Makefile
new file mode 100644
index 0000000..66d8177
--- /dev/null
+++ b/usr.bin/fib/Makefile
@@ -0,0 +1,11 @@
+PROG= fib
+NOMAN=notyet
+
+SRCS = fib.c parser.c lex.l
+LDADD = -ll
+DPADD = ${LIBL} ${LIBFORMS}
+CLEANFILES= parser.c lex.c y.tab.h
+
+CFLAGS += -I${.CURDIR} -I. -g
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fib/fib.c b/usr.bin/fib/fib.c
new file mode 100644
index 0000000..98f7d32
--- /dev/null
+++ b/usr.bin/fib/fib.c
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1995
+ * Paul Richards. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Richards.
+ * 4. The name Paul Richards may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL RICHARDS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <stdio.h>
+
+main(int argc, char **argv)
+{
+ FILE *outf;
+
+ if (argc != 1)
+ if (!(freopen(*++argv, "r", stdin))) {
+ perror(*argv);
+ exit(1);
+ }
+ if (yyparse())
+ exit(1);
+
+ outf = fopen("frm.tab.h", "w");
+ if (!outf) {
+ perror("Couldn't open output file:");
+ exit(1);
+ }
+ output_forms(outf);
+ if (fclose(outf) == EOF) {
+ perror("Error closing output file:");
+ exit (1);
+ }
+}
diff --git a/usr.bin/fib/lex.l b/usr.bin/fib/lex.l
new file mode 100644
index 0000000..18c96d5
--- /dev/null
+++ b/usr.bin/fib/lex.l
@@ -0,0 +1,93 @@
+%{
+/*-
+ * Copyright (c) 1995
+ * Paul Richards. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Richards.
+ * 4. The name Paul Richards may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL RICHARDS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include "y.tab.h"
+extern int lineno;
+extern int charno;
+extern int off;
+%}
+
+%%
+colortable { return COLORTABLE; }
+colourtable { return COLORTABLE; }
+Colors { return COLOR; }
+Colours { return COLOR; }
+black { return BLACK; }
+red { return RED; }
+green { return GREEN; }
+yellow { return YELLOW; }
+blue { return BLUE; }
+magenta { return MAGENTA; }
+cyan { return CYAN; }
+white { return WHITE; }
+pair { return PAIR; }
+Form { return FORM; }
+at { return AT; }
+as { return AS; }
+height { return HEIGHT; }
+= { return EQUALS; }
+width { return WIDTH; }
+start { return STARTFIELD; }
+text { return TEXT; }
+attributes { return ATTR; }
+highlight { return SELATTR; }
+label { return LABEL; }
+default { return DEFAULT; }
+limit { return LIMIT; }
+selected { return SELECTED; }
+options { return OPTIONS; }
+action { return ACTION; }
+function { return FUNC; }
+up { return UP; }
+down { return DOWN; }
+left { return LEFT; }
+right { return RIGHT; }
+next { return NEXT; }
+, { return COMMA; }
+\{ { return LBRACE; }
+\} { return RBRACE; }
+[0-9]+ { yylval.ival = atoi(yytext); return NUMBER; }
+[A-Za-z_][A-Za-z0-9_()|&]* { yylval.sval = yytext; return NAME; }
+\"[^"]* {
+ if (yytext[yyleng-1] == '\\') {
+ yymore();
+ } else {
+ input();
+ yylval.sval = yytext+1;
+ return STRING;
+ }
+ }
+\n { lineno++; }
+#.* { /* Ignored (comment) */; }
+[ \t\f]* { /* Ignored (white space) */; }
diff --git a/usr.bin/fib/parser.y b/usr.bin/fib/parser.y
new file mode 100644
index 0000000..f450b9e
--- /dev/null
+++ b/usr.bin/fib/parser.y
@@ -0,0 +1,853 @@
+%{
+/*-
+ * Copyright (c) 1995
+ * Paul Richards. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Richards.
+ * 4. The name Paul Richards may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL RICHARDS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <forms.h>
+
+char *cpstr(char *);
+
+extern int yyleng;
+int lineno = 1;
+int charno = 1;
+int off;
+
+char *fieldname;
+char *defname;
+char *formname;
+char *startname;
+char *colortable;
+char *formattr;
+char *text;
+char *label;
+char *function;
+char *up, *down, *left, *right, *next;
+int height, width;
+int y, x;
+int width;
+int limit;
+char *attr;
+char *selattr;
+int type;
+int lbl_flag;
+int selected, no_options=0;
+
+extern FILE *outf;
+
+struct field_list {
+ char *fieldname;
+ char *defname;
+ char *attr;
+ char *selattr;
+ char *up;
+ char *down;
+ char *left;
+ char *right;
+ char *next;
+ struct field field;
+ struct field_list *next_field;
+};
+
+struct field_list *cur_field_def;
+struct field_list *field_defs;
+struct field_list *field;
+struct field_list *cur_form_field;
+struct field_list *form_field_list;
+
+struct form_list {
+ char *formname;
+ struct form form;
+ struct form_list *next;
+ struct field_list *fields;
+ char *startfield;
+ char *formattr;
+ char *colortable;
+};
+
+struct form_list *cur_form;
+struct form_list *form_list;
+struct form_list *form;
+
+struct menu_list {
+ char *option;
+ struct menu_list *next;
+};
+
+struct menu_list *cur_menu;
+struct menu_list *menu_list;
+struct menu_list *menu;
+
+struct color_list {
+ char *foreground;
+ char *background;
+ struct color_list *next;
+};
+struct color_list *pair;
+struct color_list *cur_pair;
+struct color_list *color_list;
+
+struct color_table {
+ char *tablename;
+ struct color_list *pairs;
+ struct color_table *next;
+};
+
+struct color_table *color_table;
+struct color_table *cur_table;
+struct color_table *color_tables;
+
+%}
+
+%union {
+ int ival;
+ char *sval;
+}
+
+%token <ival> FORM
+%token <ival> COLORTABLE
+%token <ival> COLOR
+%token <ival> BLACK
+%token <ival> RED
+%token <ival> GREEN
+%token <ival> YELLOW
+%token <ival> BLUE
+%token <ival> MAGENTA
+%token <ival> CYAN
+%token <ival> WHITE
+%token <ival> PAIR
+%token <sval> NAME
+%token <sval> STRING
+%token <ival> AT
+%token <ival> AS
+%token <ival> HEIGHT
+%token <ival> EQUALS
+%token <ival> NUMBER
+%token <ival> WIDTH
+%token <ival> STARTFIELD
+%token <ival> COMMA
+%token <ival> LBRACE
+%token <ival> RBRACE
+%token <ival> TEXT
+%token <ival> ATTR
+%token <ival> SELATTR
+%token <ival> DEFAULT
+%token <ival> LABEL
+%token <ival> LIMIT
+%token <ival> SELECTED
+%token <ival> OPTIONS
+%token <ival> ACTION
+%token <ival> FUNC
+%token <ival> LINK
+%token <ival> UP
+%token <ival> DOWN
+%token <ival> LEFT
+%token <ival> RIGHT
+%token <ival> NEXT
+%token <ival> DEF
+
+%type <sval> a_color
+
+%start spec
+
+%%
+
+spec: /* empty */
+ | spec fields
+ | spec forms
+ | spec colours
+ ;
+
+colours: COLOR NAME
+ {
+ color_table = malloc(sizeof (struct color_table));
+ if (!color_table) {
+ fprintf(stderr, "Couldn't allocate memory for a color table\n");
+ exit (1);
+ }
+ color_table->tablename = cpstr($2);
+ }
+ LBRACE color_pairs RBRACE
+ {
+ color_table->pairs = color_list;
+ cur_pair = 0;
+ if (!cur_table) {
+ color_tables = color_table;
+ cur_table = color_table;
+ } else {
+ cur_table->next = color_table;
+ cur_table = color_table;
+ }
+ }
+ ;
+
+color_pairs: /* empty */
+ | color_pairs pair
+ ;
+
+pair: PAIR EQUALS a_color
+ {
+ pair = malloc(sizeof (struct color_list));
+ if (!pair) {
+ fprintf(stderr, "Couldn't allocate memory for a color pair\n");
+ exit(1);
+ }
+ pair->foreground = cpstr($3);
+ }
+ COMMA a_color
+ {
+ pair->background = cpstr($6);
+ if (!cur_pair) {
+ color_list = pair;
+ cur_pair = pair;
+ } else {
+ cur_pair->next = pair;
+ cur_pair = pair;
+ }
+ }
+ ;
+
+a_color: BLACK
+ { $$ = "COLOR_BLACK"; }
+ | RED
+ { $$ = "COLOR_RED"; }
+ | GREEN
+ { $$ = "COLOR_GREEN"; }
+ | YELLOW
+ { $$ = "COLOR_YELLOW"; }
+ | BLUE
+ { $$ = "COLOR_BLUE"; }
+ | MAGENTA
+ { $$ = "COLOR_MAGENTA"; }
+ | CYAN
+ { $$ = "COLOR_CYAN"; }
+ | WHITE
+ { $$ = "COLOR_WHITE"; }
+ ;
+
+forms: FORM NAME
+ { formname = cpstr($2); }
+ AT coord
+ {
+ form = malloc(sizeof (struct form_list));
+ if (!form) {
+ fprintf(stderr,"Failed to allocate memory for form\n");
+ exit(1);
+ }
+ form->form.y = y;
+ form->form.x = x;
+ }
+ LBRACE formspec RBRACE
+ {
+ form->startfield = startname;
+ form->formname = formname;
+ form->colortable = colortable;
+ form->form.height = height;
+ form->form.width = width;
+ form->formattr = formattr;
+ form->fields = form_field_list;
+ cur_form_field = 0;
+ if (!cur_form) {
+ form_list = form;
+ cur_form = form;
+ } else {
+ cur_form->next = form;
+ cur_form = form;
+ }
+ }
+ ;
+
+formspec: height width startfield colortable formattr fieldlocs
+ ;
+
+startfield: /* empty */
+ { startname = 0;
+ printf("Warning: No start field specified for form %s\n", formname);
+ }
+ | STARTFIELD EQUALS NAME
+ { startname = cpstr($3); }
+ ;
+
+colortable: /*empty */
+ { colortable = 0; }
+ | COLORTABLE EQUALS NAME
+ { colortable = cpstr($3); }
+ ;
+
+formattr: /* empty */
+ { formattr = 0; }
+ | ATTR EQUALS NAME
+ { formattr = cpstr($3); }
+ ;
+
+fieldlocs: /* empty */
+ | fieldlocs field_at
+ ;
+
+field_at: NAME
+ { fieldname = cpstr($1); }
+ field_def AT coord
+ {
+ field = malloc(sizeof (struct field_list));
+ if (!field) {
+ fprintf(stderr,"Failed to allocate memory for form field\n");
+ exit(1);
+ }
+ field->fieldname = fieldname;
+ if (!defname)
+ field->defname = fieldname;
+ else
+ field->defname = defname;
+ field->field.y = y;
+ field->field.x = x;
+ }
+ links
+ {
+ field->up = up;
+ field->down = down;
+ field->left = left;
+ field->right = right;
+ field->next = next;
+ up = 0;
+ down = 0;
+ left = 0;
+ right = 0;
+ next = 0;
+ if (!cur_form_field) {
+ form_field_list = field;
+ cur_form_field = field;
+ } else {
+ cur_form_field->next_field = field;
+ cur_form_field = field;
+ }
+ }
+ ;
+
+fields: NAME
+ { defname = cpstr($1); }
+ field_spec
+ { define_field(defname, 0); }
+ ;
+
+field_def: /* empty */
+ { defname = 0; }
+ | LBRACE NAME
+ { defname = cpstr($2); }
+ RBRACE
+ | field_spec
+ { defname = fieldname; define_field(defname, 0); }
+ ;
+
+field_spec: LBRACE height width attr selattr type RBRACE
+ ;
+
+links: /* empty */
+ | links COMMA conns
+ ;
+
+conns: UP EQUALS NAME
+ { up = cpstr($3); }
+ | DOWN EQUALS NAME
+ { down = cpstr($3); }
+ | LEFT EQUALS NAME
+ { left = cpstr($3); }
+ | RIGHT EQUALS NAME
+ { right = cpstr($3); }
+ | NEXT EQUALS NAME
+ { next = cpstr($3); }
+ ;
+
+type: textfield
+ | inputfield
+ | menufield
+ | actionfield
+ ;
+
+textfield: TEXT EQUALS STRING
+ { type = F_TEXT; text = cpstr($3); }
+ ;
+
+inputfield: inputspec
+ { type = F_INPUT; }
+ ;
+
+inputspec: LABEL EQUALS STRING limit
+ { lbl_flag = 1; label = cpstr($3); }
+ | DEFAULT EQUALS STRING limit
+ { lbl_flag = 0; label = cpstr($3); }
+ ;
+
+limit: /* empty */
+ | LIMIT EQUALS NUMBER
+ { limit = $3; }
+
+menufield: SELECTED EQUALS NUMBER OPTIONS EQUALS menuoptions
+ { type = F_MENU; selected = $3; }
+ ;
+
+menuoptions: menuoption
+ | menuoptions COMMA menuoption
+ ;
+
+menuoption: STRING
+ { menu = malloc(sizeof(struct menu_list));
+ if (!menu) {
+ fprintf(stderr,
+ "Couldn't allocate memory for menu option\n");
+ exit (1);
+ }
+ menu->option = cpstr($1);
+ ++no_options;
+ if (!cur_menu) {
+ menu_list = menu;
+ cur_menu = menu;
+ } else {
+ cur_menu->next = menu;
+ cur_menu = menu;
+ }
+ }
+ ;
+
+actionfield: ACTION EQUALS STRING FUNC EQUALS NAME
+ { type = F_ACTION; text = cpstr($3); function = cpstr($6); }
+ ;
+
+height: /* empty */
+ { height = 0; }
+ | HEIGHT EQUALS NUMBER
+ { height = $3; }
+ ;
+
+width: /* empty */
+ { width = 0; }
+ | WIDTH EQUALS NUMBER
+ { width = $3; }
+ ;
+
+attr: /* empty */
+ { attr = 0; }
+ | ATTR EQUALS NAME
+ { attr = cpstr($3); }
+ ;
+
+selattr: /* empty */
+ { selattr = 0; }
+ | SELATTR EQUALS NAME
+ { selattr = cpstr($3); }
+ ;
+
+coord: NUMBER COMMA NUMBER
+ { y = $1; x = $3; }
+ ;
+
+%%
+
+yyerror (char *error)
+{
+ fprintf(stderr, "%s at line %d\n",error, lineno);
+ exit(1);
+}
+
+yywrap()
+{
+ return(1);
+}
+
+char *
+cpstr(char *ostr)
+{
+ char *nstr;
+
+ nstr = malloc(strlen(ostr)+1);
+ if (!nstr) {
+ fprintf(stderr, "Couldn't allocate memory for string\n");
+ exit(1);
+ }
+ strcpy(nstr, ostr);
+ return (nstr);
+}
+
+output_field(struct field_list *fields, char *fieldname, FILE *outf)
+{
+ struct menu_list *menu;
+ int i, lim, len;
+
+ switch(fields->field.type) {
+ case F_TEXT:
+ if (!fieldname) {
+ if (!fields->field.width)
+ fields->field.width = strlen(fields->field.field.text->text);
+ if (!fields->field.height)
+ calc_field_height(&fields->field, fields->field.field.text->text);
+
+ fprintf(outf, "struct text_field %s = {\"%s\"};\n",
+ fields->defname,
+ fields->field.field.text->text);
+ }
+ break;
+ case F_INPUT:
+ /* Force height to one regardless */
+ fields->field.height = 1;
+ if (!fields->field.width && !fields->field.field.input->limit) {
+ fields->field.width = strlen(fields->field.field.input->label);
+ fields->field.field.input->limit = fields->field.width;
+ } else if (!fields->field.width)
+ fields->field.width = fields->field.field.input->limit;
+ else if (!fields->field.field.input->limit)
+ fields->field.field.input->limit = fields->field.width;
+ if (fields->field.field.input->limit < fields->field.width)
+ fields->field.width = fields->field.field.input->limit;
+ fprintf(outf, "struct input_field %s = {%d, \"%s\", 0, %d};\n",
+ (fieldname)?fieldname:fields->defname,
+ (fields->field.field.input->lbl_flag ? 1 : 0),
+ fields->field.field.input->label,
+ fields->field.field.input->limit);
+ break;
+ case F_MENU:
+ /* Force height to one regardless */
+ fields->field.height = 1;
+ if (!fieldname) {
+ fprintf(outf, "char *%s_options[] = {",
+ fields->defname);
+ menu = (struct menu_list *)fields->field.field.menu->options;
+ lim = 0;
+ for (i=0; i < fields->field.field.menu->no_options - 1; i++) {
+ fprintf(outf, "\"%s\", ", menu->option);
+ len = strlen(menu->option);
+ if (len > lim)
+ lim = len;
+ menu = menu->next;
+ }
+ if (!fields->field.width)
+ fields->field.width = lim;
+ fprintf(outf, "\"%s\"};\n", menu->option);
+ }
+ fprintf(outf, "struct menu_field %s = {%d, %d, %s_options};\n",
+ (fieldname)?fieldname:fields->defname,
+ fields->field.field.menu->no_options,
+ fields->field.field.menu->selected,
+ fields->defname);
+ break;
+ case F_ACTION:
+ if (!fields->field.width)
+ fields->field.width = strlen(fields->field.field.action->text);
+ if (!fields->field.height)
+ calc_field_height(&fields->field, fields->field.field.action->text);
+ fprintf(outf, "struct action_field %s = {\"%s\", &%s};\n",
+ (fieldname)?fieldname:fields->defname,
+ fields->field.field.action->text,
+ fields->field.field.action->fn);
+ break;
+ default:
+ break;
+ }
+}
+
+output_forms(FILE *outf)
+{
+ struct form_list *forms;
+ struct field_list *fields;
+
+ /* Output the general field definitions */
+ for (fields = field_defs; fields; fields=fields->next_field)
+ output_field(fields, 0, outf);
+
+ for(forms=form_list; forms; forms = forms->next) {
+ parse_form(forms, outf);
+ }
+}
+
+output_coltab(struct color_table *coltab, FILE *outf)
+{
+ struct color_list *pairs;
+
+ /* Output the color pair table */
+
+ fprintf(outf, "struct col_pair %s_coltab[] = {\n",coltab->tablename);
+
+ if (color_list) {
+ for(pairs=coltab->pairs; pairs; pairs=pairs->next)
+ fprintf(outf, "\t{%s, %s},\n",
+ pairs->foreground, pairs->background);
+ } else {
+ /* Output a default color table */
+ fprintf(outf, "\t{7, 0}\n};");
+ }
+ fprintf(outf, "\t{-1, -1}\n};\n\n");
+}
+
+parse_form(struct form_list *form, FILE *outf)
+{
+ struct field_list *fields;
+ struct field_list *defs;
+ struct field_list *info;
+ struct color_table *coltab;
+ char *fieldname;
+ int no_fields = 0;
+
+ /* If there's a color table defined find it and output it */
+ if (form->colortable) {
+ for (coltab = color_tables; coltab; coltab = coltab->next)
+ if (!strcmp(form->colortable, coltab->tablename)) {
+ output_coltab(coltab, outf);
+ break;
+ }
+ if (!coltab) {
+ fprintf(stderr, "Color table for form %s not found\n",
+ form->formname);
+ form->colortable = 0;
+ }
+ }
+
+ /*
+ * Run through the specific instances of the fields referenced by
+ * this form, filling in the link structures and outputing a field
+ * definition for this particular instance.
+ */
+ for(fields=form->fields; fields; fields=fields->next_field) {
+
+ /* Fill in link values */
+
+ fields->field.up = field_id(fields->up, form->fields);
+ fields->field.down = field_id(fields->down, form->fields);
+ fields->field.left = field_id(fields->left, form->fields);
+ fields->field.right = field_id(fields->right, form->fields);
+ fields->field.next = field_id(fields->next, form->fields);
+
+ /* Search field list for definition of this field */
+
+ for(defs=field_defs; defs; defs=defs->next_field) {
+ if (!strcmp(fields->defname, defs->defname))
+ break;
+ }
+
+ if (!defs) {
+ fprintf(stderr,
+ "%s not found in field definitions, field not output\n",
+ fields->defname);
+ fields->fieldname = 0;
+ } else {
+ fields->field.type = defs->field.type;
+ fields->field.height = defs->field.height;
+ fields->field.width = defs->field.width;
+ fields->attr = defs->attr;
+ fields->selattr = defs->selattr;
+ if (strcmp(fields->defname, fields->fieldname))
+ output_field(defs, fields->fieldname, outf);
+ }
+ }
+
+ /* Output the field table for this form */
+
+ fprintf(outf, "struct field %s_fields[] = {\n", form->formname);
+
+ for(fields=form->fields; fields; fields=fields->next_field) {
+ if (fields->fieldname) {
+ ++no_fields;
+ fprintf(outf, "\t{%d, %d, %d, %d, %d, %s, %s, ",
+ fields->field.type, fields->field.y, fields->field.x,
+ fields->field.height, fields->field.width,
+ (fields->attr ? fields->attr : "F_DEFATTR"),
+ (fields->selattr ? fields->selattr : "F_SELATTR"));
+ fprintf(outf, "%d, %d, %d, %d, %d, ",
+ fields->field.next,
+ fields->field.up,
+ fields->field.down,
+ fields->field.left,
+ fields->field.right);
+ fprintf(outf, "(struct text_field *)&%s},\n",
+ fields->fieldname);
+ }
+ }
+
+ fprintf(outf, "\t{%d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}\n};\n", F_END);
+
+ /* If no start field set then find first non-text field */
+ if (!form->startfield)
+ for (fields = form->fields; fields; fields = fields->next_field)
+ if (fields->field.type != F_TEXT) {
+ form->startfield = fields->fieldname;
+ break;
+ }
+
+ fprintf(outf,
+ "struct form %s = {%d, %d, %d, %s_fields, %d, %d, %d, %d, %s, ",
+ form->formname, no_fields,
+ field_id(form->startfield, form->fields),
+ field_id(form->startfield, form->fields),
+ form->formname, form->form.height, form->form.width,
+ form->form.y, form->form.x,
+ (form->formattr ? form->formattr : "F_DEFATTR"));
+ if (form->colortable)
+ fprintf(outf, "%s_coltab, ", form->colortable);
+ else
+ fprintf(outf, "0, ");
+ fprintf(outf, "0};\n\n");
+
+}
+
+/*
+ * Search a field list for a particular fieldname
+ * and return an integer id for that field based on
+ * it's position in the list [0..n]. Return -1 if
+ * there's no match.
+ */
+
+int
+field_id(char *fieldname, struct field_list *field_list)
+{
+ struct field_list *fields;
+ int id=0;
+
+ /* If the strings null then fail immediately */
+
+ if (!fieldname)
+ return (-1);
+
+ for (fields = field_list; fields; fields = fields->next_field, id++) {
+ if (!strcmp(fieldname, fields->fieldname))
+ return (id);
+ }
+
+ fprintf(stderr, "Field %s, not found in specification\n", fieldname);
+ return (-1);
+}
+
+/* Calculate a default height for a field */
+
+calc_field_height(struct field *field, char *string)
+{
+
+ int len;
+
+ len = strlen(string);
+
+ if (!field->width) {
+ /*
+ * This is a failsafe, this routine shouldn't be called
+ * with a width of 0, the width should be determined
+ * first.
+ */
+ field->height = 1;
+ return;
+ }
+
+ if (len < field->width) {
+ field->height = 1;
+ return;
+ } else
+ field->height = len / field->width;
+
+ if ((field->height*field->width) < len)
+ field->height++;
+
+ return;
+}
+
+/* Add a field to the pre-defined fields list */
+
+define_field(char *defname, char *fieldname)
+{
+ field = malloc(sizeof (struct field_list));
+ if (!field) {
+ fprintf(stderr,"Failed to allocate memory for form field\n");
+ exit(1);
+ }
+ field->fieldname = fieldname;
+ field->defname = defname;
+ field->field.type = type;
+ field->field.height = height;
+ field->field.width = width;
+ field->attr = attr;
+ field->selattr = selattr;
+ switch (type) {
+ case F_TEXT:
+ field->field.field.text = malloc(sizeof (struct text_field));
+ if (!field->field.field.text) {
+ fprintf(stderr,
+ "Failed to allocate memory for text field\n");
+ exit (1);
+ }
+ field->field.field.text->text = text;
+ break;
+ case F_INPUT:
+ field->field.field.input = malloc(sizeof (struct input_field));
+ if (!field->field.field.input) {
+ fprintf(stderr,
+ "Failed to allocate memory for input field\n");
+ exit (1);
+ }
+ field->field.field.input->lbl_flag = lbl_flag;
+ field->field.field.input->label = label;
+ field->field.field.input->limit = limit;
+ break;
+ case F_MENU:
+ field->field.field.menu = malloc(sizeof (struct menu_field));
+ if (!field->field.field.menu) {
+ fprintf(stderr,
+ "Failed to allocate memory for menu field\n");
+ exit (1);
+ }
+ field->field.field.menu->selected = selected;
+ field->field.field.menu->no_options = no_options;
+ no_options = 0;
+ field->field.field.menu->options = (char **)menu_list;
+ cur_menu = 0;
+ break;
+ case F_ACTION:
+ field->field.field.action = malloc(sizeof (struct action_field));
+ if (!field->field.field.action) {
+ fprintf(stderr,
+ "Failed to allocate memory for action field\n");
+ exit (1);
+ }
+ field->field.field.action->text = text;
+ field->field.field.action->fn = (void *) function;
+ break;
+ default:
+ break;
+ }
+ if (!cur_field_def) {
+ field_defs = field;
+ cur_field_def = field;
+ } else {
+ cur_field_def->next_field = field;
+ cur_field_def = field;
+ }
+ width=0;
+ height = 0;
+ attr=0;
+ selattr=0;
+ limit=0;
+}
diff --git a/usr.bin/file/LEGAL.NOTICE b/usr.bin/file/LEGAL.NOTICE
new file mode 100644
index 0000000..a8255b9
--- /dev/null
+++ b/usr.bin/file/LEGAL.NOTICE
@@ -0,0 +1,37 @@
+Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992.
+Written by Ian F. Darwin and others.
+$Id: LEGAL.NOTICE,v 1.8 1993/03/17 11:35:30 ian Exp $
+
+This software is not subject to and may not be made subject to any
+license of the American Telephone and Telegraph Company (AT&T Inc.),
+UNIX System Laboratories (USL Inc.), Novell Inc., Sun Microsystems
+Inc., Digital Equipment Inc., Lotus Development Inc., the Regents of
+the University of California, The X Consortium or MIT, or The Free
+Software Foundation.
+
+This software is not subject to any export provision of the United States
+Department of Commerce, and may be exported to any country or planet.
+
+Permission is granted to anyone to use this software for any purpose on
+any computer system, and to alter it and redistribute it freely, subject
+to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+ software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission. Since few users ever read sources,
+ credits must appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software. Since few users
+ ever read sources, credits must appear in the documentation.
+
+4. This notice may not be removed or altered.
+
+UNIX is a trademark of UNIX System Laboratories (which is probably a
+subsidiary of Novell, Inc., by the time you read this). The name "UNIX"
+may not be used by commercial undertakings without permission in
+writing from USL. Just ask BSDI (Berkeley Software Design Inc.), a
+commercial venture not officially connected with the University of
+California at Berkeley.
diff --git a/usr.bin/file/MAINT b/usr.bin/file/MAINT
new file mode 100644
index 0000000..7fc5e36
--- /dev/null
+++ b/usr.bin/file/MAINT
@@ -0,0 +1,33 @@
+$Id: MAINT,v 1.2 1993/09/23 21:47:01 christos Exp $
+
+Maintenance notes:
+
+I am continuing to maintain the file command. I welcome your help,
+but to make my life easier I'd like to request the following:
+
+- Don't change the version numbers!
+
+If your changes are extensive, I will have to work hard to
+integrate them into my version. If you check it into SCCS locally,
+the version numbers will likely be kept. IF you check it into RCS
+or CVS locally, please use -k to keep the version numbers, and
+please use branch deltas (1.21.1, 1.21.2, ...). If you don't do
+this, I will likely be unable to use your changes; life's just too
+short.
+
+- Do not distribute changed versions.
+
+People trying to be helpful occasionally put up their hacked versions
+of the file command for FTP, then the "archie" server finds and publishes
+the hacked version, and people all over the world get copies of it.
+Within a day or two I am getting email from around the world
+asking me why "my" file command won't compile!!! Needless to say this
+detracts from the limited time I have available to work on the actual
+software. Therefore I ask you again to please NOT distribute
+your changed version.
+
+
+Thank you for your assistance and cooperation.
+
+Mark Moraes Christos Zoulas
+moraes@deshaw.com christos@deshaw.com
diff --git a/usr.bin/file/Magdir/Header b/usr.bin/file/Magdir/Header
new file mode 100644
index 0000000..0c97bae
--- /dev/null
+++ b/usr.bin/file/Magdir/Header
@@ -0,0 +1,5 @@
+#! file
+# Magic data for file(1) command.
+# Machine-genererated from src/cmd/file/magdir/*; edit there only!
+# Format is described in magic(files), where:
+# files is 4 on V7 and BSD, 4 on SV, and ?? in the SVID.
diff --git a/usr.bin/file/Magdir/Localstuff b/usr.bin/file/Magdir/Localstuff
new file mode 100644
index 0000000..224940b
--- /dev/null
+++ b/usr.bin/file/Magdir/Localstuff
@@ -0,0 +1,3 @@
+# $Id: Localstuff,v 1.2 1993/01/05 13:22:25 ian Exp $
+# Add any locally-observed files here. Remember:
+# text if readable, executable if runnable binary, data if unreadable.
diff --git a/usr.bin/file/Magdir/alliant b/usr.bin/file/Magdir/alliant
new file mode 100644
index 0000000..52ed628
--- /dev/null
+++ b/usr.bin/file/Magdir/alliant
@@ -0,0 +1,15 @@
+#
+# Alliant FX series a.out files:
+# If the FX series is the one that had a processor with a 68K-derived
+# instruction set, the "short" should probably become "beshort" and the
+# "long" should probably become "belong".
+# If it's the i860-based one, they should probably become either the
+# big-endian or little-endian versions, depending on the mode they ran
+# the 860 in....
+#
+0 short 0420 0420 Alliant virtual executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
+0 short 0421 0421 Alliant compact executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
diff --git a/usr.bin/file/Magdir/apl b/usr.bin/file/Magdir/apl
new file mode 100644
index 0000000..6ede926
--- /dev/null
+++ b/usr.bin/file/Magdir/apl
@@ -0,0 +1,4 @@
+#
+# magic.apl:
+#
+0 long 0100554 APL workspace (Ken's original?)
diff --git a/usr.bin/file/Magdir/apple b/usr.bin/file/Magdir/apple
new file mode 100644
index 0000000..eb48673
--- /dev/null
+++ b/usr.bin/file/Magdir/apple
@@ -0,0 +1,10 @@
+#
+# Apple II file formats
+#
+0 string FiLeStArTfIlEsTaRt binscii (apple ][) text
+0 string \x0aGL Binary II (apple ][) data
+0 string \x76\xff Squeezed (apple ][) data
+0 string SIT! StuffIt (macintosh) text
+0 string NuFile NuFile archive (apple ][) data
+0 string N\xf5F\xe9l\xe5 NuFile archive (apple ][) data
+
diff --git a/usr.bin/file/Magdir/ar b/usr.bin/file/Magdir/ar
new file mode 100644
index 0000000..583ec30
--- /dev/null
+++ b/usr.bin/file/Magdir/ar
@@ -0,0 +1,104 @@
+#
+# "ar", for all kinds of archives.
+#
+# XXX - why are there multiple <ar> thingies? Note that 0x213c6172 is
+# "!<ar", so, for new-style (4.xBSD/SVR2andup) archives, we have:
+#
+# 0 string !<arch> current ar archive
+# 0 long 0x213c6172 archive file
+#
+# and for SVR3.1 archives, we have:
+#
+# 0 string \<ar> System V Release 1 ar archive
+# 0 string =<ar> archive
+# 0 string =<ar> archive
+#
+# XXX - did Aegis really store shared libraries, breakpointed modules,
+# and absolute code program modules in the same format as new-style
+# "ar" archives?
+#
+0 string !<arch> current ar archive
+>8 string __.SYMDEF random library
+>0 belong =65538 - pre SR9.5
+>0 belong =65539 - post SR9.5
+>0 beshort 2 - object archive
+>0 beshort 3 - shared library module
+>0 beshort 4 - debug break-pointed module
+>0 beshort 5 - absolute code program module
+0 string \<ar> System V Release 1 ar archive
+0 string =<ar> archive
+#
+# XXX - from "vax", which appears to collect a bunch of byte-swapped
+# thingies, to help you recognize VAX files on big-endian machines;
+# with "leshort", "lelong", and "string", that's no longer necessary....
+#
+# 0 long 0x3c61723e VAX 5.0 archive
+#
+0 long 0x213c6172 archive file
+0 lelong 0177555 very old VAX archive
+0 leshort 0177555 very old PDP-11 archive
+#
+# XXX - "pdp" claims that 0177545 can have an __.SYMDEF member and thus
+# be a random library (it said 0xff65 rather than 0177545).
+#
+0 lelong 0177545 old VAX archive
+>8 string __.SYMDEF random library
+0 leshort 0177545 old PDP-11 archive
+>8 string __.SYMDEF random library
+#
+0 string =<ar> archive
+#
+# From "pdp":
+#
+0 lelong 0x39bed PDP-11 old archive
+0 lelong 0x39bee PDP-11 4.0 archive
+#
+0 string -h- Software Tools format archive text
+# "arc" archiver
+0 byte 26 'arc' archive
+>1 byte 0 (empty)
+>1 byte 1 (old format)
+# Rahul Dhesi's zoo archive format, from keith@cerberus.uchicago.edu.
+20 long 0xdca7c4fd Rahul Dhesi's "zoo" archive
+# ZIP archiver
+0 string PK zip archive file
+>4 byte x - version
+>4 byte 10 1.0
+>4 byte 20 2.0
+
+2 string -lh LHarc archive data
+>6 byte x type %c
+>20 byte x - header level %d
+
+# From: <u31b3hs@pool.informatik.rwth-aachen.de> (Michael Haardt)
+2 string -lh0- Lharc 1.x archive
+2 string -lh1- Lharc 1.x archive
+2 string -lz4- Lharc 1.x archive
+2 string -lz5- Lharc 1.x archive
+2 string -lzs- LHa 2.x? archive [lzs]
+2 string -lh - LHa 2.x? archive [lh ]
+2 string -lhd- LHa 2.x? archive [lhd]
+2 string -lh2- LHa 2.x? archive [lh2]
+2 string -lh3- LHa 2.x? archive [lh3]
+2 string -lh4- LHa 2.x? archive [lh4]
+2 string -lh5- LHa (2.x) archive
+
+# ARJ archive data from jason@jarthur.Claremont.EDU
+0 leshort 0xea60 ARJ archive data
+>5 byte x - version %d,
+>8 byte >0 flags:
+>>8 byte &0x04 multi-volume,
+>>8 byte &0x10 slash switched,
+>>8 byte &0x20 backup,
+>34 string x original name: %s,
+>7 byte 0 os: MS/DOS
+>7 byte 1 os: PRIMOS
+>7 byte 2 os: UNIX
+>7 byte 3 os: Amiga
+>7 byte 4 os: Macintosh
+>7 byte 5 os: OS/2
+>7 byte 6 os: Apple ][ GS
+>7 byte 7 os: Atari ST
+>7 byte 8 os: NeXT
+>7 byte 9 os: VAX/VMS
+>3 byte >0 %d]
diff --git a/usr.bin/file/Magdir/arc b/usr.bin/file/Magdir/arc
new file mode 100644
index 0000000..1ebb260
--- /dev/null
+++ b/usr.bin/file/Magdir/arc
@@ -0,0 +1,3 @@
+0 byte 26 'arc' archive
+>1 byte 0 (empty)
+>1 byte 1 (old format)
diff --git a/usr.bin/file/Magdir/archive b/usr.bin/file/Magdir/archive
new file mode 100644
index 0000000..8999c2f
--- /dev/null
+++ b/usr.bin/file/Magdir/archive
@@ -0,0 +1,15 @@
+# A collection of various "ar" and "cpio" archive formats.
+# "Tar" archives are handled in the C code.
+0 short 070707 cpio archive
+0 string 070707 ASCII cpio archive
+0 long 0177555 very old archive
+0 short 0177555 very old PDP-11 archive
+0 long 0177545 old archive
+0 short 0177545 old PDP-11 archive
+0 long 0100554 apl workspace
+0 string =<ar> archive
+0 string !<arch> archive
+>8 string __.SYMDEF random library
+0 string -h- Software Tools format archive text
+# Rahul Dhesi's zoo archive format, from keith@cerberus.uchicago.edu.
+20 long 0xdca7c4fd Rahul Dhesi's "zoo" archive
diff --git a/usr.bin/file/Magdir/att3b b/usr.bin/file/Magdir/att3b
new file mode 100644
index 0000000..c1e9e39
--- /dev/null
+++ b/usr.bin/file/Magdir/att3b
@@ -0,0 +1,38 @@
+#
+# AT&T 3B machines
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+# 3B20
+#
+0 beshort 0550 3b20 COFF executable
+>12 belong >0 not stripped
+#>22 beshort >0 - version %ld
+0 beshort 0551 3b20 COFF executable (TV)
+>12 belong >0 not stripped
+#>22 beshort >0 - version %ld
+#
+# WE32K
+#
+0 beshort 0560 WE32000 COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>18 beshort ^00010000 N/A on 3b2/300 w/paging
+>18 beshort &00020000 32100 required
+>18 beshort &00040000 and mau hardware required
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0443 (target shared library)
+>22 beshort >0 - version %ld
+0 beshort 0561 WE32000 COFF executable (TV)
+>12 belong >0 not stripped
+#>18 beshort &00020000 - 32100 required
+#>18 beshort &00040000 and mau hardware required
+#>22 beshort >0 - version %ld
+#
+# core file for 3b2
+0 string \000\004\036\212\200 3b2 core file
+>364 string >\0 of '%s'
diff --git a/usr.bin/file/Magdir/audio b/usr.bin/file/Magdir/audio
new file mode 100644
index 0000000..6b4cb36
--- /dev/null
+++ b/usr.bin/file/Magdir/audio
@@ -0,0 +1,43 @@
+#
+# Sound formats, from Jan Nicolai Langfeldt <janl@ifi.uio.no>,
+#
+
+# Sun/NeXT audio data
+0 string .snd audio data:
+>12 belong 1 8-bit u-law,
+>12 belong 2 8-bit linear PCM,
+>12 belong 3 16-bit linear PCM,
+>12 belong 4 24-bit linear PCM,
+>12 belong 5 32-bit linear PCM,
+>12 belong 6 32-bit floating point,
+>12 belong 7 64-bit floating point,
+>12 belong 23 compressed (G.721 ADPCM),
+>20 belong 1 mono,
+>20 belong 2 stereo,
+>20 belong 4 quad,
+>16 belong x %d Hz
+# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format
+# that uses little-endian encoding and has a different magic number
+# (0x0064732E in little-endian encoding).
+0 lelong 0x0064732E DEC audio data:
+>12 lelong 1 8-bit u-law,
+>12 lelong 2 8-bit linear PCM,
+>12 lelong 3 16-bit linear PCM,
+>12 lelong 4 24-bit linear PCM,
+>12 lelong 5 32-bit linear PCM,
+>12 lelong 6 32-bit floating point,
+>12 lelong 7 64-bit floating point,
+>12 lelong 23 compressed (G.721 ADPCM),
+>20 lelong 1 mono,
+>20 lelong 2 stereo,
+>20 lelong 4 quad,
+>16 lelong x %d Hz
+# Bytes 0-3 of AIFF, AIFF-C, & 8SVX audio files are "FORM"
+8 string AIFF AIFF audio data
+8 string AIFC AIFF-C audio data
+8 string 8SVX IFF/8SVX audio data
+# Bytes 0-3 of Waveform (*.wav) audio files are "RIFF"
+8 string WAVE Waveform audio data
+0 string Creative\ Voice\ File Soundblaster audio data
+0 long 0x4e54524b MultiTrack sound data file
+>4 long x - version %ld
diff --git a/usr.bin/file/Magdir/blit b/usr.bin/file/Magdir/blit
new file mode 100644
index 0000000..0f4804d
--- /dev/null
+++ b/usr.bin/file/Magdir/blit
@@ -0,0 +1,16 @@
+# 68K Blit stuff as seen from 680x0 machine
+# Note that this 0407 conflicts with several other a.out formats...
+#
+# XXX - should this be redone with "be" and "le", so that it works on
+# little-endian machines as well? If so, what's the deal with
+# "VAX-order" and "VAX-order2"?
+#
+#0 long 0407 68K Blit (standalone) executable
+#0 short 0407 VAX-order2 68K Blit (standalone) executable
+0 short 03401 VAX-order 68K Blit (standalone) executable
+0 long 0406 68k Blit mpx/mux executable
+0 short 0406 VAX-order2 68k Blit mpx/mux executable
+0 short 03001 VAX-order 68k Blit mpx/mux executable
+# Need more values for WE32 DMD executables.
+# Note that 0520 is the same as COFF
+#0 short 0520 tty630 layers executable
diff --git a/usr.bin/file/Magdir/bsdi b/usr.bin/file/Magdir/bsdi
new file mode 100644
index 0000000..faf0a60
--- /dev/null
+++ b/usr.bin/file/Magdir/bsdi
@@ -0,0 +1,2 @@
+# BSDI BSD/386
+0 long 0314 BSD/386 demand paged (first page unmapped) pure executable
diff --git a/usr.bin/file/Magdir/c-lang b/usr.bin/file/Magdir/c-lang
new file mode 100644
index 0000000..79b2254
--- /dev/null
+++ b/usr.bin/file/Magdir/c-lang
@@ -0,0 +1,3 @@
+# this first will upset you if you're a PL/1 shop...
+# in which case rm it; ascmagic will catch real C programs
+0 string /* c program text
diff --git a/usr.bin/file/Magdir/chi b/usr.bin/file/Magdir/chi
new file mode 100644
index 0000000..2dfbfcf
--- /dev/null
+++ b/usr.bin/file/Magdir/chi
@@ -0,0 +1,4 @@
+# ChiWriter files
+0 string \\1cw\ ChiWriter file
+>5 string >\0 version %s
+0 string \\1cw ChiWriter file
diff --git a/usr.bin/file/Magdir/clipper b/usr.bin/file/Magdir/clipper
new file mode 100644
index 0000000..f4e9fa7
--- /dev/null
+++ b/usr.bin/file/Magdir/clipper
@@ -0,0 +1,63 @@
+#
+# Intergraph, formerly Fairchild, Clipper.
+#
+# XXX - what byte order does the Clipper use?
+#
+# XXX - what's the "!" stuff:
+#
+# >18 short !074000,000000 C1 R1
+# >18 short !074000,004000 C2 R1
+# >18 short !074000,010000 C3 R1
+# >18 short !074000,074000 TEST
+#
+# I shall assume it's ANDing the field with the first value and
+# comparing it with the second, and rewrite it as:
+#
+# >18 short&074000 000000 C1 R1
+# >18 short&074000 004000 C2 R1
+# >18 short&074000 010000 C3 R1
+# >18 short&074000 074000 TEST
+#
+# as SVR3.1's "file" doesn't support anything of the "!074000,000000"
+# sort, nor does SunOS 4.x, so either it's something Intergraph added
+# in CLIX, or something AT&T added in SVR3.2 or later, or something
+# somebody else thought was a good idea; it's not documented in the
+# man page for this version of "magic", nor does it appear to be
+# implemented (at least not after I blew off the bogus code to turn
+# old-style "&"s into new-style "&"s, which just didn't work at all).
+#
+0 short 0575 CLIPPER COFF executable (VAX #)
+>20 short 0407 (impure)
+>20 short 0410 (5.2 compatible)
+>20 short 0411 (pure)
+>20 short 0413 (demand paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %ld
+0 short 0577 CLIPPER COFF executable
+>18 short&074000 000000 C1 R1
+>18 short&074000 004000 C2 R1
+>18 short&074000 010000 C3 R1
+>18 short&074000 074000 TEST
+>20 short 0407 (impure)
+>20 short 0410 (pure)
+>20 short 0411 (separate I&D)
+>20 short 0413 (paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %ld
+>48 long&01 01 alignment trap enabled
+>52 byte 1 -Ctnc
+>52 byte 2 -Ctsw
+>52 byte 3 -Ctpw
+>52 byte 4 -Ctcb
+>53 byte 1 -Cdnc
+>53 byte 2 -Cdsw
+>53 byte 3 -Cdpw
+>53 byte 4 -Cdcb
+>54 byte 1 -Csnc
+>54 byte 2 -Cssw
+>54 byte 3 -Cspw
+>54 byte 4 -Cscb
+4 string pipe CLIPPER instruction trace
+4 string prof CLIPPER instruction profile
diff --git a/usr.bin/file/Magdir/commands b/usr.bin/file/Magdir/commands
new file mode 100644
index 0000000..d7c04c1
--- /dev/null
+++ b/usr.bin/file/Magdir/commands
@@ -0,0 +1,40 @@
+#
+# "Commands": stuff for various shells and interpreters.
+#
+0 string :\ shell archive or commands for antique kernel text
+0 string #!/bin/sh Bourne Shell script text
+0 string #!\ /bin/sh Bourne Shell script text
+0 string #!/bin/csh C Shell script text
+0 string #!\ /bin/csh C Shell script text
+# korn shell magic, sent by George Wu, gwu@clyde.att.com
+0 string #!/bin/ksh Korn Shell script text
+0 string #!\ /bin/ksh Korn Shell script text
+0 string #!/bin/tcsh Tenex C Shell script text
+0 string #!\ /bin/tcsh Tenex C Shell script text
+0 string #!/usr/local/tcsh Tenex C Shell script text
+0 string #!\ /usr/local/tcsh Tenex C Shell script text
+0 string #!/usr/local/bin/tcsh Tenex C Shell script text
+0 string #!\ /usr/local/bin/tcsh Tenex C Shell script text
+0 string #!/bin/awk Awk Commands text
+0 string #!\ /bin/awk Awk Commands text
+0 string #!\ / a
+>3 string >\0 %s script text
+0 string #!/ a
+>2 string >\0 %s script text
+0 string #!\ commands text
+>3 string >\0 for %s
+
+# For Larry Wall's perl language. The ``eval'' line recognizes an
+# outrageously clever hack for USG systems.
+# Keith Waclena <keith@cerberus.uchicago.edu>
+0 string #!/bin/perl perl commands text
+0 string #!\ /bin/perl perl commands text
+0 string eval\ "exec\ /bin/perl perl commands text
+0 string #!/usr/bin/perl perl commands text
+0 string #!\ /usr/bin/perl perl commands text
+0 string eval\ "exec\ /usr/bin/perl perl commands text
+0 string #!/usr/local/bin/perl perl commands text
+0 string #!\ /usr/local/bin/perl perl commands text
+0 string eval\ "exec\ /usr/local/bin/perl perl commands text
+0 string #!/bin/rc Plan 9 rc Shell script text
+0 string #!\ /bin/rc Plan 9 rc Shell script text
diff --git a/usr.bin/file/Magdir/compress b/usr.bin/file/Magdir/compress
new file mode 100644
index 0000000..6309c180
--- /dev/null
+++ b/usr.bin/file/Magdir/compress
@@ -0,0 +1,40 @@
+#
+# Formats for various forms of compressed data
+# Formats for "compress" proper have been moved into "compress.c",
+# because it tries to uncompress it to figure out what's inside.
+
+# According to gzip.h, this is the correct byte order for packed data.
+#
+0 string \037\036 packed data
+
+#
+# This magic number is byte-order-independent.
+#
+0 short 017437 old packed data
+
+#
+# XXX - why *two* entries for "compacted data", one of which is
+# byte-order independent, and one of which is byte-order dependent?
+#
+0 short 0x1fff compacted data
+0 string \377\037 compacted data
+0 short 0145405 huf output
+#
+# Squeeze and Crunch...
+# These numbers were gleaned from the Unix versions of the programs to
+# handle these formats. Note that I can only uncrunch, not crunch, and
+# I didn't have a crunched file handy, so the crunch number is untested.
+# Keith Waclena <keith@cerberus.uchicago.edu>
+0 leshort 0x76FF squeezed data (CP/M, DOS)
+0 leshort 0x76FE crunched data (CP/M, DOS)
+# Freeze
+0 string \037\237 Frozen file 2.1
+0 string \037\236 Frozen file 1.0 (or gzip 0.5)
+#
+# Standard unix compress
+#
+0 string \037\235 compressed data
+>2 byte&0x80 >0 block compressed
+>2 byte&0x1f x %d bits
+
+0 string \037\240 LZH compressed data
diff --git a/usr.bin/file/Magdir/convex b/usr.bin/file/Magdir/convex
new file mode 100644
index 0000000..0220a81
--- /dev/null
+++ b/usr.bin/file/Magdir/convex
@@ -0,0 +1,4 @@
+#
+# XXX - what byte order does a Convex use?
+#
+0 long 0513 Convex executable
diff --git a/usr.bin/file/Magdir/cpio b/usr.bin/file/Magdir/cpio
new file mode 100644
index 0000000..d1805cc
--- /dev/null
+++ b/usr.bin/file/Magdir/cpio
@@ -0,0 +1,16 @@
+#
+# Yes, the two "cpio archive" formats *are* supposed to just be "short".
+# The idea is to indicate archives produced on machines with the same
+# byte order as the machine running "file" with "cpio archive", and
+# to indicate archives produced on machines with the opposite byte order
+# from the machine running "file" with "byte-swapped cpio archive".
+#
+# The SVR4 "cpio(4)" hints that there are additional formats, but they
+# are defined as "short"s; I think all the new formats are
+# character-header formats, and thus are strings not numbers.
+#
+0 short 070707 cpio archive
+0 short 0143561 byte-swapped cpio archive
+0 string 070707 ASCII cpio archive (pre-SVR4 or odc)
+0 string 070701 ASCII cpio archive (SVR4 with no CRC)
+0 string 070702 ASCII cpio archive (SVR4 with CRC)
diff --git a/usr.bin/file/Magdir/diamond b/usr.bin/file/Magdir/diamond
new file mode 100644
index 0000000..d515d61
--- /dev/null
+++ b/usr.bin/file/Magdir/diamond
@@ -0,0 +1,8 @@
+#
+# ... diamond is a multi-media mail and electronic conferencing system....
+#
+# XXX - I think it was either renamed Slate, or replaced by Slate....
+#
+# The full deal is too long...
+#0 string <list>\n<protocol\ bbn-multimedia-format> Diamond Multimedia Document
+0 string =<list>\n<protocol\ bbn-m Diamond Multimedia Document
diff --git a/usr.bin/file/Magdir/diff b/usr.bin/file/Magdir/diff
new file mode 100644
index 0000000..abf1b87
--- /dev/null
+++ b/usr.bin/file/Magdir/diff
@@ -0,0 +1,6 @@
+#
+# magic file lines for output from "diff"...
+0 string diff\ 'diff' output text
+0 string ***\ 'diff' output text
+0 string Only\ in\ 'diff' output text
+0 string Common\ subdirectories:\ 'diff' output text
diff --git a/usr.bin/file/Magdir/ditroff b/usr.bin/file/Magdir/ditroff
new file mode 100644
index 0000000..1fff178
--- /dev/null
+++ b/usr.bin/file/Magdir/ditroff
@@ -0,0 +1,4 @@
+# Magic numbers for ditroff intermediate language
+0 string x\ T\ cat titroff output for the C/A/T text
+0 string x\ T\ ps titroff output for PostScript
+0 string x\ T titroff output text
diff --git a/usr.bin/file/Magdir/dump b/usr.bin/file/Magdir/dump
new file mode 100644
index 0000000..d641747
--- /dev/null
+++ b/usr.bin/file/Magdir/dump
@@ -0,0 +1,80 @@
+#
+# magic.dump, dump file format. For new and old dump filesystems
+#
+# We specify both byte orders in order to recognize byte-swapped dumps.
+#
+24 belong 60012 new-fs dump file (big endian),
+>4 bedate x Previous dump %s,
+>8 bedate x This dump %s,
+>12 belong >0 Volume %ld,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+24 belong 60011 old-fs dump file (big endian),
+#>4 bedate x Previous dump %s,
+#>8 bedate x This dump %s,
+>12 belong >0 Volume %ld,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+24 lelong 60012 new-fs dump file (little endian),
+>4 ledate x Previous dump %s,
+>8 ledate x This dump %s,
+>12 lelong >0 Volume %ld,
+>692 lelong 0 Level zero, type:
+>692 lelong >0 Level %d, type:
+>0 lelong 1 tape header,
+>0 lelong 2 beginning of file record,
+>0 lelong 3 map of inodes on tape,
+>0 lelong 4 continuation of file record,
+>0 lelong 5 end of volume,
+>0 lelong 6 map of inodes deleted,
+>0 lelong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 lelong >0 Flags %x
+
+24 lelong 60011 old-fs dump file (little endian),
+#>4 ledate x Previous dump %s,
+#>8 ledate x This dump %s,
+>12 lelong >0 Volume %ld,
+>692 lelong 0 Level zero, type:
+>692 lelong >0 Level %d, type:
+>0 lelong 1 tape header,
+>0 lelong 2 beginning of file record,
+>0 lelong 3 map of inodes on tape,
+>0 lelong 4 continuation of file record,
+>0 lelong 5 end of volume,
+>0 lelong 6 map of inodes deleted,
+>0 lelong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 lelong >0 Flags %x
diff --git a/usr.bin/file/Magdir/elf b/usr.bin/file/Magdir/elf
new file mode 100644
index 0000000..e34332d
--- /dev/null
+++ b/usr.bin/file/Magdir/elf
@@ -0,0 +1,44 @@
+#
+# ELF
+# Missing MIPS image type and flags
+#
+# Question marks on processor types flag "should not happen because the
+# byte order is wrong". We have to check the byte order flag to see what
+# byte order all the other stuff in the header is in.
+#
+0 string \177ELF ELF
+>4 byte 0 invalid class
+>4 byte 1 32-bit
+>4 byte 2 64-bit
+>5 byte 0 invalid byte order
+>5 byte 1 LSB
+>>16 leshort 0 unknown type
+>>16 leshort 1 relocatable
+>>16 leshort 2 executable
+>>16 leshort 3 dynamic lib
+>>16 leshort 4 core file
+>>18 leshort 0 unknown machine
+>>18 leshort 1 WE32100 and up
+>>18 leshort 2 SPARC?
+>>18 leshort 3 i386 (386 and up)
+>>18 leshort 4 M68000?
+>>18 leshort 5 M88000?
+>>18 leshort 7 i860
+>>20 lelong 1 Version 1
+>>36 lelong 1 MathCoPro/FPU/MAU Required
+>5 byte 2 MSB
+>>16 beshort 0 unknown type
+>>16 beshort 1 relocatable
+>>16 beshort 2 executable
+>>16 beshort 3 dynamic lib
+>>16 beshort 4 core file
+>>18 beshort 0 unknown machine
+>>18 beshort 1 WE32100 and up
+>>18 beshort 2 SPARC
+>>18 beshort 3 i386 (386 and up)?
+>>18 beshort 4 M68000
+>>18 beshort 5 M88000
+>>18 beshort 7 i860
+>>20 belong 1 Version 1
+>>36 belong 1 MathCoPro/FPU/MAU Required
+
diff --git a/usr.bin/file/Magdir/encore b/usr.bin/file/Magdir/encore
new file mode 100644
index 0000000..7244f87
--- /dev/null
+++ b/usr.bin/file/Magdir/encore
@@ -0,0 +1,20 @@
+#
+# magic.encore: Recognize encore machines
+#
+# XXX - needs to have the byte order specified (NS32K was little-endian,
+# dunno whether they run the 88K in little-endian mode or not).
+#
+0 short 0x154 Encore
+>20 short 0x107 executable
+>20 short 0x108 pure executable
+>20 short 0x10b demand-paged executable
+>20 short 0x10f unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %ld
+>22 short 0 -
+#>4 date x stamp %s
+0 short 0x155 Encore unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %ld
+>22 short 0 -
+#>4 date x stamp %s
diff --git a/usr.bin/file/Magdir/floppy.raw b/usr.bin/file/Magdir/floppy.raw
new file mode 100644
index 0000000..75091b9
--- /dev/null
+++ b/usr.bin/file/Magdir/floppy.raw
@@ -0,0 +1 @@
+0 string \366\366\366\366 Formatted floppy w/ no filesystem data
diff --git a/usr.bin/file/Magdir/fonts b/usr.bin/file/Magdir/fonts
new file mode 100644
index 0000000..594db76
--- /dev/null
+++ b/usr.bin/file/Magdir/fonts
@@ -0,0 +1,3 @@
+0 string FONT ASCII vfont text
+0 short 0436 Berkeley vfont data
+0 short 017001 byte-swapped Berkeley vfont data
diff --git a/usr.bin/file/Magdir/frame b/usr.bin/file/Magdir/frame
new file mode 100644
index 0000000..46e8648
--- /dev/null
+++ b/usr.bin/file/Magdir/frame
@@ -0,0 +1,28 @@
+#
+# Magic number for FrameMaker files
+# This stuff came on a FrameMaker demo tape, most of which is
+# copyright, but this file is "published" as witness the following:
+#
+0 string \<MakerFile FrameMaker document
+>11 string 3.0 (3.0
+>11 string 2.0 (2.0
+>11 string 1.0 (1.0
+>14 byte x %c)
+0 string \<MIFFile FrameMaker MIF file
+>9 string 3.0 (3.0)
+>9 string 2.0 (2.0)
+>9 string 1.0 (1.x)
+0 string \<MakerDictionary FraneMaker Dictionary text
+>17 string 3.0 (3.0)
+>17 string 2.0 (2.0)
+>17 string 1.0 (1.x)
+0 string \<MakerScreenFon FrameMaker Font file
+>17 string 1.01 (%s)
+0 string \<MML FrameMaker MML file
+0 string \<Book FrameMaker Book file
+>10 string 3.0 (3.0
+>10 string 2.0 (2.0
+>10 string 1.0 (1.0
+>13 byte x %c)
+0 string \<Maker Intermediate Print File FrameMaker IPL file
+0 string \<MakerDictionary FraneMaker Dictionary text
diff --git a/usr.bin/file/Magdir/freebsd b/usr.bin/file/Magdir/freebsd
new file mode 100644
index 0000000..79471d7
--- /dev/null
+++ b/usr.bin/file/Magdir/freebsd
@@ -0,0 +1,26 @@
+# the following are for 386BSD/FreeBSD
+
+0 lelong 0410 pure executable
+0 lelong 0413 demand paged executable
+0 lelong&077777777 041400314 FreeBSD/i386 demand paged
+>3 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>3 byte ^0x80 executable
+>16 lelong >0 not stripped
+
+# This covers object files, and is better than "PDP-11 executable"
+0 lelong 000000407 impure format
+>16 lelong >0 not stripped
+
+# XXX gross hack to identify core files
+# cores start with a struct tss; we take advantage of the following:
+# byte 7: highest byte of the kernel stack pointer, always 0xfe
+# 8/9: kernel (ring 0) ss value, always 0x0010
+# 10 - 27: ring 1 and 2 ss/esp, unused, thus always 0
+# 28: low order byte of the current PTD entry, always 0 since the
+# PTD is page-aligned
+#
+7 string \357\020\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 i386 a.out core file
+>1047 string >\0 from "%s"
diff --git a/usr.bin/file/Magdir/gzip b/usr.bin/file/Magdir/gzip
new file mode 100644
index 0000000..a19a654
--- /dev/null
+++ b/usr.bin/file/Magdir/gzip
@@ -0,0 +1,21 @@
+0 string \037\213 gzip compressed data
+>2 byte <8 - reserved method
+>2 byte 8 - deflate method
+>3 byte &0x01 , ascii
+>3 byte &0x02 , continuation
+>3 byte &0x04 , extra field
+>3 byte &0x08 , original file name
+>3 byte &0x10 , comment
+>3 byte &0x20 , encrypted
+>4 ledate x , last modified: %s
+>8 byte 2 , max compression
+>8 byte 4 , max speed
+>9 byte =0x00 os: MS/DOS
+>9 byte =0x01 os: Amiga
+>9 byte =0x02 os: VMS
+>9 byte =0x03 os: Unix
+>9 byte =0x05 os: Atari
+>9 byte =0x06 os: OS/2
+>9 byte =0x07 os: MacOS
+>9 byte =0x0A os: Tops/20
+>9 byte =0x0B os: Win/32
diff --git a/usr.bin/file/Magdir/hp b/usr.bin/file/Magdir/hp
new file mode 100644
index 0000000..db02e54
--- /dev/null
+++ b/usr.bin/file/Magdir/hp
@@ -0,0 +1,191 @@
+#
+# magic.hp: Hewlett Packard Magic
+#
+# XXX - somebody should figure out whether any byte order needs to be
+# applied to the "TML" stuff; I'm assuming the Apollo stuff is
+# big-endian as it was mostly 68K-based.
+#
+# HP-PA is big-endian, so it (and "800", which is *also* HP-PA-based; I
+# assume "HPPA-RISC1.1" really means "HP-PA Version 1.1", which first
+# showed up in the 700 series, although later 800 series machines are,
+# I think, based on the PA7100 which implements HP-PA 1.1) are flagged
+# as big-endian.
+#
+# I think the 500 series was the old stack-based machines, running a
+# UNIX environment atop the "SUN kernel"; dunno whether it was
+# big-endian or little-endian.
+#
+# I'm guessing that the 200 series was 68K-based; the 300 and 400 series
+# are.
+#
+# The "misc" stuff needs a byte order; the archives look suspiciously
+# like the old 177545 archives (0xff65 = 0177545).
+#
+#### Old Apollo stuff
+0 beshort 0627 Apollo m68k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %ld
+0 beshort 0624 apollo a88k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %ld
+0 long 01203604016 TML 0123 byte-order format
+0 long 01702407010 TML 1032 byte-order format
+0 long 01003405017 TML 2301 byte-order format
+0 long 01602007412 TML 3210 byte-order format
+#### HPPA
+0 belong 0x02100106 HPPA-RISC1.1 relocatable object
+0 belong 0x02100107 HPPA-RISC1.1 executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x02100108 HPPA-RISC1.1 shared executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x0210010b HPPA-RISC1.1 demand-load executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x0210010e HPPA-RISC1.1 shared library
+>96 belong >0 -not stripped
+
+0 belong 0x0210010d HPPA-RISC1.1 dynamic load library
+>96 belong >0 -not stripped
+
+#### 800
+0 belong 0x020b0106 HP s800 relocatable object
+
+0 belong 0x020b0107 HP s800 executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x020b0108 HP s800 shared executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x020b010b HP s800 demand-load executable
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 -not stripped
+
+0 belong 0x020b010e HP s800 shared library
+>96 belong >0 -not stripped
+
+0 belong 0x020b010d HP s800 dynamic load library
+>96 belong >0 -not stripped
+
+0 belong 0x213c6172 archive file
+>68 belong 0x020b0619 -HP s800 relocatable library
+
+#### 500
+0 long 0x02080106 HP s500 relocatable executable
+>16 long >0 -version %ld
+
+0 long 0x02080107 HP s500 executable
+>16 long >0 -version %ld
+
+0 long 0x02080108 HP s500 pure executable
+>16 long >0 -version %ld
+
+#### 200
+0 belong 0x020c0108 HP s200 pure executable
+>4 beshort >0 -version %ld
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0107 HP s200 executable
+>4 beshort >0 -version %ld
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c010b HP s200 demand-load executable
+>4 beshort >0 -version %ld
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0106 HP s200 relocatable executable
+>4 beshort >0 -version %ld
+>6 beshort >0 -highwater %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x20000000 debuggable
+>8 belong &0x10000000 PIC
+
+0 belong 0x020a0108 HP s200 (2.x release) pure executable
+>4 beshort >0 -version %ld
+>36 belong >0 not stripped
+
+0 belong 0x020a0107 HP s200 (2.x release) executable
+>4 beshort >0 -version %ld
+>36 belong >0 not stripped
+
+0 belong 0x020c010e HP s200 shared library
+>4 beshort >0 -version %ld
+>6 beshort >0 -highwater %d
+>36 belong >0 not stripped
+
+0 belong 0x020c010d HP s200 dynamic load library
+>4 beshort >0 -version %ld
+>6 beshort >0 -highwater %d
+>36 belong >0 not stripped
+
+#### MISC
+0 long 0x0000ff65 HP old archive
+0 long 0x020aff65 HP s200 old archive
+0 long 0x020cff65 HP s200 old archive
+0 long 0x0208ff65 HP s500 old archive
+
+0 long 0x015821a6 HP core file
+
+0 long 0x4da7eee8 HP-WINDOWS font
+>8 byte >0 -version %ld
+0 string Bitmapfile HP Bitmapfile
+
+0 string IMGfile CIS compimg HP Bitmapfile
+0 short 0x8000 lif file
+0 long 0x020c010c compiled Lisp
+
+0 string msgcat01 HP NLS message catalog,
+>8 long >0 %d messages
+
+# addendum to /etc/magic with HP-48sx file-types by phk@data.fls.dk 1jan92
+0 string HPHP48- HP48 binary
+>7 byte >0 - Rev %c
+>8 short 0x1129 (ADR)
+>8 short 0x3329 (REAL)
+>8 short 0x5529 (LREAL)
+>8 short 0x7729 (COMPLX)
+>8 short 0x9d29 (LCOMPLX)
+>8 short 0xbf29 (CHAR)
+>8 short 0xe829 (ARRAY)
+>8 short 0x0a2a (LNKARRAY)
+>8 short 0x2c2a (STRING)
+>8 short 0x4e2a (HXS)
+>8 short 0x742a (LIST)
+>8 short 0x962a (DIR)
+>8 short 0xb82a (ALG)
+>8 short 0xda2a (UNIT)
+>8 short 0xfc2a (TAGGED)
+>8 short 0x1e2b (GROB)
+>8 short 0x402b (LIB)
+>8 short 0x622b (BACKUP)
+>8 short 0x882b (LIBDATA)
+>8 short 0x9d2d (PROG)
+>8 short 0xcc2d (CODE)
+>8 short 0x482e (GNAME)
+>8 short 0x6d2e (LNAME)
+>8 short 0x922e (XLIB)
+0 string %%HP: HP48 text
+>6 string T(0) - T(0)
+>6 string T(1) - T(1)
+>6 string T(2) - T(2)
+>6 string T(3) - T(3)
+>10 string A(D) A(D)
+>10 string A(R) A(R)
+>10 string A(G) A(G)
+>14 string F(.) F(.);
+>14 string F(,) F(,);
diff --git a/usr.bin/file/Magdir/ibm370 b/usr.bin/file/Magdir/ibm370
new file mode 100644
index 0000000..b40fbf4
--- /dev/null
+++ b/usr.bin/file/Magdir/ibm370
@@ -0,0 +1,19 @@
+#
+# IBM 370 and compatibles.
+#
+# "ibm370" said that 0x15d == 0535 was "ibm 370 pure executable".
+# What the heck *is* "USS/370"?
+#
+0 beshort 0531 SVR2 executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %ld
+0 beshort 0534 SVR2 pure executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %ld
+0 beshort 0530 SVR2 pure executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %ld
+0 beshort 0535 SVR2 executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %ld
+
diff --git a/usr.bin/file/Magdir/ibm6000 b/usr.bin/file/Magdir/ibm6000
new file mode 100644
index 0000000..e4d73df
--- /dev/null
+++ b/usr.bin/file/Magdir/ibm6000
@@ -0,0 +1,17 @@
+#
+# magic.rs6000:
+#
+# RS/6000 and the RT PC.
+#
+0 beshort 0x01df executable (RISC System/6000 V3.1) or obj module
+>12 belong >0 not stripped
+# Breaks sun4 statically linked execs.
+#0 beshort 0x0103 executable (RT Version 2) or obj module
+#>2 byte 0x50 pure
+#>28 belong >0 not stripped
+#>6 beshort >0 - version %ld
+0 beshort 0x0104 shared library
+0 beshort 0x0105 ctab data
+0 beshort 0xfe04 structured file
+0 string 0xabcdef message catalog
+#0 string <aiaff> archive
diff --git a/usr.bin/file/Magdir/iff b/usr.bin/file/Magdir/iff
new file mode 100644
index 0000000..2d2f95b
--- /dev/null
+++ b/usr.bin/file/Magdir/iff
@@ -0,0 +1,5 @@
+# image file format
+# From Robert Potter, potter@cs.rochester.edu
+0 string Imagefile\ version- iff image data
+# this adds the whole header (inc. version number), informative but longish
+>10 string >\0 %s
diff --git a/usr.bin/file/Magdir/imagen b/usr.bin/file/Magdir/imagen
new file mode 100644
index 0000000..5fa4f6c
--- /dev/null
+++ b/usr.bin/file/Magdir/imagen
@@ -0,0 +1,14 @@
+# Tell file about magic for IMAGEN printer-ready files:
+0 string @document( Imagen printer
+# this only works if "language xxx" is first item in Imagen header.
+>10 string language\ impress (imPRESS data)
+>10 string language\ daisy (daisywheel text)
+>10 string language\ diablo (daisywheel text)
+>10 string language\ printer (line printer emulation)
+>10 string language\ tektronix (Tektronix 4014 emulation)
+# Add any other languages that your Imagen uses - remember
+# to keep the word `text' if the file is human-readable.
+#
+# Now magic for IMAGEN font files...
+0 string Rast RST-format raster font data
+>45 string >0 face %
diff --git a/usr.bin/file/Magdir/images b/usr.bin/file/Magdir/images
new file mode 100644
index 0000000..074c743
--- /dev/null
+++ b/usr.bin/file/Magdir/images
@@ -0,0 +1,69 @@
+# image formats, originally from jef@helios.ee.lbl.gov (Jef Poskanzer),
+# additions by janl@ifi.uio.no as well as others. Jan also suggested
+# merging several one- and two-line files into here.
+#
+# XXX - byte order for GIF and TIFF fields?
+#
+
+0 string xbtoa btoa'd file
+
+# PBMPLUS
+0 string P1 PBM file
+0 string P2 PGM file
+0 string P3 PPM file
+0 string P4 PBM "rawbits" file
+0 string P5 PGM "rawbits" file
+0 string P6 PPM "rawbits" file
+
+# TIFF and friends
+0 string \115\115 TIFF file, big-endian
+>2 short >0 version %d
+0 string \111\111 TIFF file, little-endian
+>2 short >0 version %d
+#
+# NIFF (Navy Interchange File Format, a modification of TIFF)
+0 string IIN1 NIFF raster data
+
+# GIF
+0 string GIF GIF picture
+>3 string 87a - version %s
+>3 string 89a - version %s
+>6 leshort >0 %hd x
+>8 leshort >0 %hd,
+>10 byte &0x40 interlaced,
+>10 byte&0x07 =0x00 2 colors
+>10 byte&0x07 =0x01 4 colors
+>10 byte&0x07 =0x02 8 colors
+>10 byte&0x07 =0x03 16 colors
+>10 byte&0x07 =0x04 32 colors
+>10 byte&0x07 =0x05 64 colors
+>10 byte&0x07 =0x06 128 colors
+>10 byte&0x07 =0x07 256 colors
+
+# Miscellany
+0 long 1123028772 Artisan image file
+>4 long 1 rectangular 24-bit image
+>4 long 2 rectangular 8-bit image with colormap
+>4 long 3 rectangular 32-bit image (24-bit with matte)
+0 string \361\0\100\273 CMU window manager bitmap
+0 string #FIG FIG graphics savefile text
+>6 string 2.1 Version 2.1
+>6 string 2.0 Version 2.0
+0 string GKSM GKS Metafile
+8 string ILBM IFF ILBM file
+6 string JFIF JPEG picture
+0 string ARF_BEGARF PHIGS clear text archive
+
+# From: <u31b3hs@pool.informatik.rwth-aachen.de> (Michael Haardt)
+0 string yz MGR bitmap, modern format, 8 bit aligned
+0 string zz MGR bitmap, old format, 1 bit deep, 16 bit aligned
+0 string xz MGR bitmap, old format, 1 bit deep, 32 bit aligned
+0 string yx MGR bitmap, modern format, squeezed
+
+0 string %bitmap FBM pixmap
+>30 long 0x31 (mono)
+>30 long 0x33 (color)
+
+4 string Research, Digifax-G3-File
+>29 byte 1 , fine resolution
+>29 byte 0 , normal resolution
diff --git a/usr.bin/file/Magdir/intel b/usr.bin/file/Magdir/intel
new file mode 100644
index 0000000..3f7ade7
--- /dev/null
+++ b/usr.bin/file/Magdir/intel
@@ -0,0 +1,31 @@
+#
+# Various flavors of x86 UNIX executable/object (other than Xenix, which
+# is in "microsoft"). DOS is in "ms-dos"; the ambitious soul can do
+# Windows as well.
+#
+# Windows NT belongs elsewhere, as you need x86 and MIPS and Alpha and
+# whatever comes next (HP-PA Hummingbird?). OS/2 may also go elsewhere
+# as well, if, as, and when IBM makes it portable.
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0502 basic-16 executable
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %ld
+0 leshort 0503 basic-16 executable (TV)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %ld
+0 leshort 0510 x86 executable
+>12 lelong >0 not stripped
+0 leshort 0511 x86 executable (TV)
+>12 lelong >0 not stripped
+0 leshort =0512 iAPX 286 executable small model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %ld
+0 leshort =0522 iAPX 286 executable large model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %ld
+0 leshort =0514 80386 COFF executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %ld
diff --git a/usr.bin/file/Magdir/interleaf b/usr.bin/file/Magdir/interleaf
new file mode 100644
index 0000000..ab5f3c6
--- /dev/null
+++ b/usr.bin/file/Magdir/interleaf
@@ -0,0 +1,7 @@
+#
+# magic for InterLeaf TPS:
+0 string =\210OPS Interleaf saved data
+0 string =<!OPS Interleaf document text
+>5 string ,\ Version\ (version
+>>14 string >\0 %s)
+
diff --git a/usr.bin/file/Magdir/iris b/usr.bin/file/Magdir/iris
new file mode 100644
index 0000000..952a5f1
--- /dev/null
+++ b/usr.bin/file/Magdir/iris
@@ -0,0 +1,57 @@
+#
+# magic.iris: Magic for mips from an iris4d
+#
+# Dunno what byte-order munging is needed; all of SGI's *current*
+# machines and OSes run in big-endian mode on the MIPS machines,
+# as far as I know, but they do have the MIPSEB and MIPSEL stuff
+# here....
+#
+0 short 0x0160 mipseb
+>20 short 0407 executable
+>20 short 0410 pure
+>20 short 0413 demand paged
+>8 long >0 not stripped
+>8 long 0 stripped
+>22 byte >0 - version %ld.
+>23 byte >0 %ld
+0 short 0x0162 mipsel
+>20 short 0407 executable
+>20 short 0410 pure
+>20 short 0413 demand paged
+>8 long >0 not stripped
+>8 long 0 stripped
+>23 byte >0 - version %ld.
+>22 byte >0 %ld
+0 short 0x6001 swapped mipseb
+>20 short 03401 executable
+>20 short 04001 pure
+>20 short 05401 demand paged
+>8 long >0 not stripped
+>8 long 0 stripped
+>22 byte >0 - version %ld.
+>23 byte >0 %ld
+0 short 0x6201 swapped mipsel
+>20 short 03401 executable
+>20 short 04001 pure
+>20 short 05401 demand paged
+>8 long >0 not stripped
+>8 long 0 stripped
+>22 byte >0 - version %ld.
+>23 byte >0 %ld
+0 short 0x180 mipseb ucode
+0 short 0x182 mipsel ucode
+#
+# IRIX core format version 1 (from /usr/include/core.out.h)
+0 long 0xdeadadb0 IRIX core dump
+>4 long 1 of
+>16 string >\0 '%s'
+#
+# Archives - This handles archive subtypes
+#
+0 string !<arch>\n__________E MIPS archive
+>20 string U with mipsucode members
+>21 string L with mipsel members
+>21 string B with mipseb members
+>19 string L and a EL hash table
+>19 string B and a EB hash table
+>22 string X -- out of date
diff --git a/usr.bin/file/Magdir/ispell b/usr.bin/file/Magdir/ispell
new file mode 100644
index 0000000..04814b8
--- /dev/null
+++ b/usr.bin/file/Magdir/ispell
@@ -0,0 +1,23 @@
+#
+# magic.ispell
+#
+# XXX - byte order?
+#
+0 short 0xffff9601 ispell hash file
+>2 short 0x00 - 8-bit, no capitalization, 26 flags
+>2 short 0x01 - 7-bit, no capitalization, 26 flags
+>2 short 0x02 - 8-bit, capitalization, 26 flags
+>2 short 0x03 - 7-bit, capitalization, 26 flags
+>2 short 0x04 - 8-bit, no capitalization, 52 flags
+>2 short 0x05 - 7-bit, no capitalization, 52 flags
+>2 short 0x06 - 8-bit, capitalization, 52 flags
+>2 short 0x07 - 7-bit, capitalization, 52 flags
+>2 short 0x08 - 8-bit, no capitalization, 128 flags
+>2 short 0x09 - 7-bit, no capitalization, 128 flags
+>2 short 0x0A - 8-bit, capitalization, 128 flags
+>2 short 0x0B - 7-bit, capitalization, 128 flags
+>2 short 0x0C - 8-bit, no capitalization, 256 flags
+>2 short 0x0D - 7-bit, no capitalization, 256 flags
+>2 short 0x0E - 8-bit, capitalization, 256 flags
+>2 short 0x0F - 7-bit, capitalization, 256 flags
+>4 short >0 and %d string characters
diff --git a/usr.bin/file/Magdir/lex b/usr.bin/file/Magdir/lex
new file mode 100644
index 0000000..929e68d
--- /dev/null
+++ b/usr.bin/file/Magdir/lex
@@ -0,0 +1,3 @@
+# derived empirically, your offsets may vary!
+53 string yyprevious c program text (from lex)
+>3 string >\0 for %s
diff --git a/usr.bin/file/Magdir/lif b/usr.bin/file/Magdir/lif
new file mode 100644
index 0000000..9ac80b9
--- /dev/null
+++ b/usr.bin/file/Magdir/lif
@@ -0,0 +1,6 @@
+#
+# magic.lif:
+#
+# XXX - byte order?
+#
+0 short 0x8000 lif file
diff --git a/usr.bin/file/Magdir/linux b/usr.bin/file/Magdir/linux
new file mode 100644
index 0000000..7c2855e
--- /dev/null
+++ b/usr.bin/file/Magdir/linux
@@ -0,0 +1,11 @@
+# Values for Linux/i386 binaries, From: Rik Faith <faith@cs.unc.edu>
+2 leshort 100 Linux/i386
+>0 leshort 0407 executable or impure executable (OMAGIC)
+>0 leshort 0410 pure executable (NMAGIC)
+>0 leshort 0413 demand-paged executable (ZMAGIC)
+>0 leshort 0314 demand-paged executable (QMAGIC)
+>16 lelong >0 not stripped
+>0 string Jump jump
+# core dump file
+216 lelong 0421 core file (Linux)
+>220 string >\0 core file (Linux) of %s
diff --git a/usr.bin/file/Magdir/magic b/usr.bin/file/Magdir/magic
new file mode 100644
index 0000000..20ee340
--- /dev/null
+++ b/usr.bin/file/Magdir/magic
@@ -0,0 +1 @@
+0 string #\ Magic magic text file for file(1) cmd
diff --git a/usr.bin/file/Magdir/mail.news b/usr.bin/file/Magdir/mail.news
new file mode 100644
index 0000000..d55b108
--- /dev/null
+++ b/usr.bin/file/Magdir/mail.news
@@ -0,0 +1,13 @@
+# Unfortunately, saved netnews also has From line added in some news software.
+#0 string From mail text
+# There are tests to ascmagic.c to cope with mail and news.
+0 string Relay-Version: old news text
+0 string #!\ rnews batched news text
+0 string N#!\ rnews mailed, batched news text
+0 string Forward\ to mail forwarding text
+0 string Pipe\ to mail piping text
+0 string Return-Path: smtp mail text
+0 string Path: news text
+0 string Xref: news text
+0 string From: news or mail text
+0 string Article saved news text
diff --git a/usr.bin/file/Magdir/microsoft b/usr.bin/file/Magdir/microsoft
new file mode 100644
index 0000000..72bb0df
--- /dev/null
+++ b/usr.bin/file/Magdir/microsoft
@@ -0,0 +1,68 @@
+#
+# Microsoft (Xenix, not DOS)
+#
+# "Middle model" stuff, and "Xenix 8086 relocatable or 80286 small
+# model" lifted from "magic.xenix", with comment "derived empirically;
+# treat as folklore until proven"
+#
+# "small model", "large model", "huge model" stuff lifted from XXX
+#
+# XXX - "x.out" collides with PDP-11 archives....
+#
+0 string core core file (Xenix)
+0 byte 0x80 8086 relocatable (Microsoft)
+0 leshort 0xff65 x.out
+>2 string __.SYMDEF randomized
+>0 byte x archive
+0 leshort 0x206 Microsoft a.out
+>8 leshort 1 Middle model
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x8 fixed-stack
+>0x1c byte &0x80 byte-swapped
+>0x1c byte &0x40 word-swapped
+>0x10 lelong >0 not-stripped
+>0x1e leshort ^0xc000 pre-SysV
+>0x1c byte &0x4 86
+>0x1c byte &0x9 286
+>0x1c byte &0xa 386
+>0x1f byte <0x040 small model
+>0x1f byte =0x048 large model
+>0x1f byte =0x049 huge model
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x40 Large Text
+>0x1e leshort &0x20 Large Data
+>0x1e leshort &0x120 Huge Objects Enabled
+>0x10 lelong >0 not stripped
+
+0 leshort 0x140 old Microsoft 8086 x.out
+>0x3 byte &0x4 separate
+>0x3 byte &0x2 pure
+>0 byte &0x1 executable
+>0 byte ^0x1 relocatable
+>0x14 lelong >0 not stripped
+
+0 lelong 0x206 b.out
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>0x1c byte &0x9 286
+>0x1c byte &0x29 286
+>0x1c byte &0xa 386
+>0x1e leshort &0x4 Large Text
+>0x1e leshort &0x2 Large Data
+>0x1e leshort &0x102 Huge Objects Enabled
+
+0 leshort 0x580 XENIX 8086 relocatable or 80286 small model
diff --git a/usr.bin/file/Magdir/mips b/usr.bin/file/Magdir/mips
new file mode 100644
index 0000000..ae17cbd
--- /dev/null
+++ b/usr.bin/file/Magdir/mips
@@ -0,0 +1,8 @@
+#
+# RISC MIPS decstation
+# Should this be "leshort", given that DEC ran the DECstations in
+# little-endian mode?
+#
+# Where is the non-SGI, non-DEC MIPS stuff?
+#
+0 short 0x6201 MIPS executable
diff --git a/usr.bin/file/Magdir/mirage b/usr.bin/file/Magdir/mirage
new file mode 100644
index 0000000..8a2dbcbe
--- /dev/null
+++ b/usr.bin/file/Magdir/mirage
@@ -0,0 +1,4 @@
+#
+# XXX - byte order?
+#
+0 long 31415 Mirage Assembler m.out executable
diff --git a/usr.bin/file/Magdir/mkid b/usr.bin/file/Magdir/mkid
new file mode 100644
index 0000000..cd2cf86
--- /dev/null
+++ b/usr.bin/file/Magdir/mkid
@@ -0,0 +1,7 @@
+#
+# ID is the binary tags database produced by mkid(1).
+#
+# XXX - byte order?
+#
+0 string \311\304 ID tags data
+>2 short >0 version %d
diff --git a/usr.bin/file/Magdir/mmdf b/usr.bin/file/Magdir/mmdf
new file mode 100644
index 0000000..236c721
--- /dev/null
+++ b/usr.bin/file/Magdir/mmdf
@@ -0,0 +1 @@
+0 string \001\001\001\001 MMDF mailbox
diff --git a/usr.bin/file/Magdir/motorola b/usr.bin/file/Magdir/motorola
new file mode 100644
index 0000000..b892794
--- /dev/null
+++ b/usr.bin/file/Magdir/motorola
@@ -0,0 +1,28 @@
+#
+# Motorola
+#
+# 68K
+#
+0 beshort 0520 mc68k COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>168 string .lowmem Apple toolbox
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0421 (standalone)
+0 beshort 0521 mc68k executable (shared)
+>12 belong >0 not stripped
+0 beshort 0522 mc68k executable (shared demand paged)
+>12 belong >0 not stripped
+#
+# Motorola/UniSoft 68K Binary Compatibility Standard (BCS)
+#
+0 beshort 0554 68K BCS executable
+#
+# 88K
+#
+# Motorola/88Open BCS
+#
+0 beshort 0555 88K BCS executable
diff --git a/usr.bin/file/Magdir/ms-dos b/usr.bin/file/Magdir/ms-dos
new file mode 100644
index 0000000..830493a
--- /dev/null
+++ b/usr.bin/file/Magdir/ms-dos
@@ -0,0 +1,8 @@
+#
+# Various MS-DOS magic numbers
+#
+0 string MZ DOS executable (EXE)
+0 string LZ DOS executable (built-in)
+0 byte 0xe9 DOS executable (COM)
+0 byte 0xeb DOS executable (COM)
+0 byte 0xf0 MS-DOS program library
diff --git a/usr.bin/file/Magdir/ncr b/usr.bin/file/Magdir/ncr
new file mode 100644
index 0000000..89df750
--- /dev/null
+++ b/usr.bin/file/Magdir/ncr
@@ -0,0 +1,47 @@
+#
+# magic.tower:
+#
+# NCR Tower objects, contributed by
+# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa
+# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP
+#
+0 beshort 000610 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %ld
+0 beshort 000615 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %ld
+0 beshort 000620 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %ld
+0 beshort 000625 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %ld
+0 beshort 000630 Tower32/600/400 68020 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %ld
+0 beshort 000640 Tower32/800 68020
+>18 beshort &020000 w/68881 object
+>18 beshort &040000 compatible object
+>18 beshort &~060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %ld
+0 beshort 000645 Tower32/800 68010
+>18 beshort &040000 compatible object
+>18 beshort &~060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %ld
diff --git a/usr.bin/file/Magdir/netbsd b/usr.bin/file/Magdir/netbsd
new file mode 100644
index 0000000..c89f6fa
--- /dev/null
+++ b/usr.bin/file/Magdir/netbsd
@@ -0,0 +1,117 @@
+#
+# All new-style magic numbers are in network byte order.
+#
+0 lelong 000000413 386BSD demand paged executable
+>16 lelong >0 not stripped
+0 lelong 000000314 BSDI demand paged executable
+>16 lelong >0 not stripped
+
+0 lelong 000000407 NetBSD little-endian object file
+>16 lelong >0 not stripped
+0 belong 000000407 NetBSD big-endian object file
+>16 belong >0 not stripped
+
+0 belong&0377777777 041400413 NetBSD/i386 demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 041400410 NetBSD/i386 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 041400407 NetBSD/i386
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+
+0 belong&0377777777 041600413 NetBSD/m68k demand paged
+>0 byte &0x80
+>>20 belong <8192 shared library
+>>20 belong =8192 dynamically linked executable
+>>20 belong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 041600410 NetBSD/m68k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 041600407 NetBSD/m68k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+
+0 belong&0377777777 042000413 NetBSD/m68k4k demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042000410 NetBSD/m68k4k pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042000407 NetBSD/m68k4k
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+
+0 belong&0377777777 042200413 NetBSD/ns32532 demand paged
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042200410 NetBSD/ns32532 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+0 belong&0377777777 042200407 NetBSD/ns32532
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+
+0 belong&0377777777 042400413 NetBSD/sparc demand paged
+>0 byte &0x80
+>>20 belong <8192 shared library
+>>20 belong =8192 dynamically linked executable
+>>20 belong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042400410 NetBSD/sparc pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&0377777777 042400407 NetBSD/sparc
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>20 belong !0 executable
+>>20 belong =0 object file
+>16 belong >0 not stripped
+
+0 belong&0377777777 041400507 NetBSD/i386 core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 041600507 NetBSD/m68k core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 042000507 NetBSD/m68k4k core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 042200507 NetBSD/ns32532 core
+>12 string >\0 from '%s'
+
+0 belong&0377777777 042400507 NetBSD/sparc core
+>12 string >\0 from '%s'
diff --git a/usr.bin/file/Magdir/news b/usr.bin/file/Magdir/news
new file mode 100644
index 0000000..df7b659
--- /dev/null
+++ b/usr.bin/file/Magdir/news
@@ -0,0 +1,5 @@
+# NeWS, not "news" as in "netnews"
+0 string StartFontMetrics ASCII font metrics
+0 string StartFont ASCII font bits
+0 long 0x137A2944 NeWS bitmap font
+0 long 0x137A2947 NeWS font family
diff --git a/usr.bin/file/Magdir/pbm b/usr.bin/file/Magdir/pbm
new file mode 100644
index 0000000..bed702e
--- /dev/null
+++ b/usr.bin/file/Magdir/pbm
@@ -0,0 +1,4 @@
+#
+# XXX - byte order?
+#
+0 short 0x2a17 "compact bitmap" format (Poskanzer)
diff --git a/usr.bin/file/Magdir/pdp b/usr.bin/file/Magdir/pdp
new file mode 100644
index 0000000..cf1129d
--- /dev/null
+++ b/usr.bin/file/Magdir/pdp
@@ -0,0 +1,22 @@
+#
+# magic.pdp: PDP-11 executable/object and APL workspace
+#
+0 lelong 0101555 PDP-11 single precision APL workspace
+0 lelong 0101554 PDP-11 double precision APL workspace
+
+#
+# PDP-11 a.out
+#
+0 leshort 0407 PDP-11 executable
+>8 leshort >0 not stripped
+
+0 leshort 0401 PDP-11 UNIX/RT ldp
+0 leshort 0405 PDP-11 old overlay
+
+0 leshort 0410 PDP-11 pure executable
+>8 leshort >0 not stripped
+
+0 leshort 0411 PDP-11 separate I&D executable
+>8 leshort >0 not stripped
+
+0 leshort 0437 PDP-11 kernel overlay
diff --git a/usr.bin/file/Magdir/pgp b/usr.bin/file/Magdir/pgp
new file mode 100644
index 0000000..eb10f21
--- /dev/null
+++ b/usr.bin/file/Magdir/pgp
@@ -0,0 +1,11 @@
+#
+# PGP (Pretty Good Privacy)
+#
+0 beshort 0x9900 PGP key public ring
+0 beshort 0x9501 PGP key security ring
+0 beshort 0x9500 PGP key security ring
+0 string -----BEGIN\040PGP PGP armored data
+>15 string PUBLIC\040KEY\040BLOCK- (public key block)
+>15 string MESSAGE- (message)
+>15 string SIGNED\040MESSAGE- (signed message)
+>15 string PGP\040SIGNATURE- (signature)
diff --git a/usr.bin/file/Magdir/pjl b/usr.bin/file/Magdir/pjl
new file mode 100644
index 0000000..fa7d298
--- /dev/null
+++ b/usr.bin/file/Magdir/pjl
@@ -0,0 +1,5 @@
+#
+# magic.pjl: HP Printer Job Language (PJL)
+#
+0 string %-12345X@PJL HP PJL (printer job language) commands
+0 string @PJL HP PJL (printer job language) commands
diff --git a/usr.bin/file/Magdir/pkgadd b/usr.bin/file/Magdir/pkgadd
new file mode 100644
index 0000000..2cdb5e5
--- /dev/null
+++ b/usr.bin/file/Magdir/pkgadd
@@ -0,0 +1,4 @@
+#
+# SysV R4 PKG Datastreams:
+#
+0 string #\ PaCkAgE\ DaTaStReAm pkg Datastream (SVR4)
diff --git a/usr.bin/file/Magdir/plus5 b/usr.bin/file/Magdir/plus5
new file mode 100644
index 0000000..a5fa4e7
--- /dev/null
+++ b/usr.bin/file/Magdir/plus5
@@ -0,0 +1,16 @@
+#
+#/etc/magic entries for Plus Five's UNIX MUMPS
+#
+# XXX - byte order? Paging Hokey....
+#
+0 short 0x259 mumps avl global
+>2 byte >0 (V%d)
+>6 byte >0 with %d byte name
+>7 byte >0 and %d byte data cells
+0 short 0x25a mumps blt global
+>2 byte >0 (V%d)
+>8 short >0 - %d byte blocks
+>15 byte 0x00 - P/D format
+>15 byte 0x01 - P/K/D format
+>15 byte 0x02 - K/D format
+>15 byte >0x02 - Bad Flags
diff --git a/usr.bin/file/Magdir/postscript b/usr.bin/file/Magdir/postscript
new file mode 100644
index 0000000..7d5992b
--- /dev/null
+++ b/usr.bin/file/Magdir/postscript
@@ -0,0 +1,20 @@
+#
+# magic.postscript: Magic for postscript files
+#
+# XXX - should we match only versions 1.0 and 2.0, or should we wildcard
+# it?
+#
+0 string %! PostScript document
+>2 string PS-Adobe- conforming
+>>11 string 1.0 at level %s
+>>11 string 2.0 at level %s
+>>11 string 3.0 at level %s
+# Some pc's have the annoying habit of adding a ^D
+0 string \004%! PostScript document
+>3 string PS-Adobe- conforming
+>>12 string 1.0 at level %s
+>>12 string 2.0 at level %s
+>>12 string 3.0 at level %s
+0 string %PDF Adobe Acrobat document
+>5 string x at level %s
+
diff --git a/usr.bin/file/Magdir/psdbms b/usr.bin/file/Magdir/psdbms
new file mode 100644
index 0000000..b1fc2e1
--- /dev/null
+++ b/usr.bin/file/Magdir/psdbms
@@ -0,0 +1,6 @@
+#
+# magic.ps: psdatabase magic
+#
+0 belong&0xff00ffff 0x56000000 ps database
+>1 string >\0 version %s
+>4 string >\0 from kernel %s
diff --git a/usr.bin/file/Magdir/pyramid b/usr.bin/file/Magdir/pyramid
new file mode 100644
index 0000000..b9c9e56
--- /dev/null
+++ b/usr.bin/file/Magdir/pyramid
@@ -0,0 +1,10 @@
+#
+# magic.pyramid: Magic for pyramids
+#
+# XXX - byte order?
+#
+0 long 0x50900107 Pyramid 90x family executable
+0 long 0x50900108 Pyramid 90x family pure executable
+>16 long >0 not stripped
+0 long 0x5090010b Pyramid 90x family demand paged pure executable
+>16 long >0 not stripped
diff --git a/usr.bin/file/Magdir/rle b/usr.bin/file/Magdir/rle
new file mode 100644
index 0000000..f45605b
--- /dev/null
+++ b/usr.bin/file/Magdir/rle
@@ -0,0 +1,19 @@
+# From <janl@ifi.uio.no>
+# I made this with the help of the man page for rle(5). Ihey missing
+# from the magic numbers I have:
+
+#
+# rle
+#
+0 short 0xcc52 Utah Raster Toolkit RLE
+>2 short >0 lower left corner: %d
+>4 short >0 lower right corner: %d
+>6 short >0 %d x
+>8 short >0 %d
+>10 byte&0x1 =0x1 CLEARFIRST
+>10 byte&0x2 =0x2 NO_BACKGROUND
+>10 byte&0x4 =0x4 ALPHA
+>10 byte&0x8 =0x8 COMMENT
+>11 byte >0 %d colour channels
+>12 byte >0 %d bits pr. pixel
+>13 byte >0 %d colour map channels
diff --git a/usr.bin/file/Magdir/sc b/usr.bin/file/Magdir/sc
new file mode 100644
index 0000000..9bc9199
--- /dev/null
+++ b/usr.bin/file/Magdir/sc
@@ -0,0 +1,2 @@
+# for SC
+38 string Spreadsheet sc file
diff --git a/usr.bin/file/Magdir/sccs b/usr.bin/file/Magdir/sccs
new file mode 100644
index 0000000..d27c5a7
--- /dev/null
+++ b/usr.bin/file/Magdir/sccs
@@ -0,0 +1,17 @@
+# SCCS archive structure:
+# \001h01207
+# \001s 00276/00000/00000
+# \001d D 1.1 87/09/23 08:09:20 ian 1 0
+# \001c date and time created 87/09/23 08:09:20 by ian
+# \001e
+# \001u
+# \001U
+# ... etc.
+# Now '\001h' happens to be the same as the 3B20's a.out magic number (0550).
+# *Sigh*. And these both came from various parts of the USG.
+# Maybe we should just switch everybody from SCCS to RCS!
+# Further, you can't just say '\001h0', because the five-digit number
+# is a checksum that could (presumably) have any leading digit,
+# and we don't have regular expression matching yet.
+# Hence the following official kludge:
+8 string \001s\ SCCS archive.
diff --git a/usr.bin/file/Magdir/sendmail b/usr.bin/file/Magdir/sendmail
new file mode 100644
index 0000000..7eea31d
--- /dev/null
+++ b/usr.bin/file/Magdir/sendmail
@@ -0,0 +1,9 @@
+#
+# magic.sendmail:
+#
+# XXX - byte order?
+#
+0 byte 046 Sendmail frozen configuration
+>16 string >\0 - version %s
+0 short 0x271c Sendmail frozen configuration
+>16 string >\0 - version %s
diff --git a/usr.bin/file/Magdir/sequent b/usr.bin/file/Magdir/sequent
new file mode 100644
index 0000000..bcb7061
--- /dev/null
+++ b/usr.bin/file/Magdir/sequent
@@ -0,0 +1,30 @@
+# Sequent information updated by Don Dwiggins <atsun!dwiggins>.
+# For Sequent's multiprocessor systems (incomplete).
+0 lelong 0x00ea BALANCE NS32000 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 lelong 0x10ea BALANCE NS32000 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 lelong 0x20ea BALANCE NS32000 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 lelong 0x30ea BALANCE NS32000 standalone executable
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+#
+# Symmetry information added by Jason Merrill <jason@jarthur.claremont.edu>.
+# Symmetry magic nums will not be reached if DOS COM comes before them;
+# byte 0xeb is matched before these get a chance.
+0 leshort 0x12eb SYMMETRY i386 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 leshort 0x22eb SYMMETRY i386 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 leshort 0x32eb SYMMETRY i386 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
+0 leshort 0x42eb SYMMETRY i386 standalone executable
+>16 lelong >0 not stripped
+>124 lelong >0 version %ld
diff --git a/usr.bin/file/Magdir/sgml b/usr.bin/file/Magdir/sgml
new file mode 100644
index 0000000..bb8b5dd
--- /dev/null
+++ b/usr.bin/file/Magdir/sgml
@@ -0,0 +1,6 @@
+# $Id: sgml,v 1.3 1993/01/05 12:52:44 ian Exp $
+# SGML goop, mostly from rph@sq.
+0 string \<!DOCTYPE Exported SGML document
+0 string \<!doctype Exported SGML document
+0 string \<!SUBDOC Exported SGML subdocument
+0 string \<!subdoc Exported SGML subdocument
diff --git a/usr.bin/file/Magdir/softquad b/usr.bin/file/Magdir/softquad
new file mode 100644
index 0000000..e49d052
--- /dev/null
+++ b/usr.bin/file/Magdir/softquad
@@ -0,0 +1,27 @@
+# SoftQuad Publishing Software magic numbers
+# $Id: softquad,v 1.8 1993/02/19 14:36:43 ian Exp $
+# Author/Editor and RulesBuilder
+#
+# XXX - byte order?
+#
+0 string \<!SQ\ DTD> Compiled SGML rules file
+>9 string >\0 Type %s
+0 string \<!SQ\ A/E> A/E SGML Document binary
+>9 string >\0 Type %s
+0 string \<!SQ\ STS> A/E SGML binary styles file
+>9 string >\0 Type %s
+0 short 0xc0de Compiled PSI (v1) data
+0 short 0xc0da Compiled PSI (v2) data
+>3 string >\0 (%s)
+# Binary sqtroff font/desc files...
+0 short 0125252 SoftQuad DESC or font file binary
+>2 short >0 - version %d
+# Bitmaps...
+0 string SQ\ BITMAP1 SoftQuad Raster Format text
+#0 string SQ\ BITMAP2 SoftQuad Raster Format data
+# sqtroff intermediate language (replacement for ditroff int. lang.)
+0 string X\ SoftQuad troff Context intermediate
+>2 string 495 for AT&T 495 laser printer
+>2 string hp for Hewlett-Packard LaserJet
+>2 string impr for IMAGEN imPRESS
+>2 string ps for PostScript
diff --git a/usr.bin/file/Magdir/sun b/usr.bin/file/Magdir/sun
new file mode 100644
index 0000000..d6ce809
--- /dev/null
+++ b/usr.bin/file/Magdir/sun
@@ -0,0 +1,84 @@
+#
+# Values for big-endian Sun (MC680x0, SPARC) binaries on pre-5.x
+# releases.
+# (5.x uses ELF.)
+#
+0 belong&077777777 0600413 sparc demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&077777777 0600410 sparc pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&077777777 0600407 sparc
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400413 mc68020 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>16 belong >0 not stripped
+0 belong&077777777 0400410 mc68020 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&077777777 0400407 mc68020
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200413 mc68010 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>16 belong >0 not stripped
+0 belong&077777777 0200410 mc68010 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+0 belong&077777777 0200407 mc68010
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+# reworked these to avoid anything beginning with zero becoming "old sun-2"
+0 belong 0407 old sun-2 executable
+>16 belong >0 not stripped
+0 belong 0410 old sun-2 pure executable
+>16 belong >0 not stripped
+0 belong 0413 old sun-2 demand paged executable
+>16 belong >0 not stripped
+
+#
+# Core files. "SPARC 4.x BCP" means "core file from a SunOS 4.x SPARC
+# binary executed in compatibility mode under SunOS 5.x".
+#
+0 belong 0x080456 SunOS core file
+>4 belong 432 (SPARC)
+>>132 string >\0 from '%s'
+>>116 belong =3 (quit)
+>>116 belong =4 (illegal instruction)
+>>116 belong =5 (trace trap)
+>>116 belong =6 (abort)
+>>116 belong =7 (emulator trap)
+>>116 belong =8 (arithmetic exception)
+>>116 belong =9 (kill)
+>>116 belong =10 (bus error)
+>>116 belong =11 (segmentation violation)
+>>116 belong =12 (bad argument to system call)
+>>116 belong =29 (resource lost)
+>>120 belong x (T=%dK,
+>>124 belong x D=%dK,
+>>128 belong x S=%dK)
+>4 belong 826 (68K)
+>>128 string >\0 from '%s'
+>4 belong 456 (SPARC 4.x BCP)
+>>152 string >\0 from '%s'
diff --git a/usr.bin/file/Magdir/sunraster b/usr.bin/file/Magdir/sunraster
new file mode 100644
index 0000000..39db3a3
--- /dev/null
+++ b/usr.bin/file/Magdir/sunraster
@@ -0,0 +1,12 @@
+#
+# Sun rasterfiles
+#
+# XXX - byte order? What about the 386i?
+#
+0 string \x59\xa6\x6a\x95 rasterfile
+>4 belong >0 %d
+>8 belong >0 x %d
+>12 belong >0 x %d
+>20 belong 0 old format
+>20 belong 2 compressed
+>24 belong 1 with color map
diff --git a/usr.bin/file/Magdir/terminfo b/usr.bin/file/Magdir/terminfo
new file mode 100644
index 0000000..76e171d
--- /dev/null
+++ b/usr.bin/file/Magdir/terminfo
@@ -0,0 +1,8 @@
+#
+# Terminfo
+#
+# XXX - byte order for screen images?
+#
+0 string \032\001 Compiled terminfo entry
+0 short 0433 Curses screen image
+0 short 0434 Curses screen image
diff --git a/usr.bin/file/Magdir/tex b/usr.bin/file/Magdir/tex
new file mode 100644
index 0000000..734bedd
--- /dev/null
+++ b/usr.bin/file/Magdir/tex
@@ -0,0 +1,25 @@
+#
+# magic.tex:
+#
+# XXX - needs byte-endian stuff (big-endian and little-endian DVI?)
+#
+# From <conklin@talisman.kaleida.com>
+
+# Although we may know the offset of certain text fields in TeX DVI
+# and font files, we can't use them reliably because they are not
+# zero terminated. [but we do anyway, christos]
+0 string \367\002 TeX DVI file
+>16 string >\0 (%s)
+0 string \367\203 TeX generic font data
+0 string \367\131 TeX packed font data
+>4 string >\0 (%s)
+0 string \367\312 TeX virtual font data
+0 string This\ is\ TeX, TeX transcript text
+0 string This\ is\ METAFONT, METAFONT transcript text
+
+# There is no way to detect TeX Font Metric (*.tfm) files without
+# breaking them apart and reading the data. The following patterns
+# match most *.tfm files generated by METAFONT or afm2tfm.
+2 string \000\021 TeX font metric data
+2 string \000\022 TeX font metric data
+>34 string >\0 (%s)
diff --git a/usr.bin/file/Magdir/troff b/usr.bin/file/Magdir/troff
new file mode 100644
index 0000000..d414c16
--- /dev/null
+++ b/usr.bin/file/Magdir/troff
@@ -0,0 +1,6 @@
+#
+# magic.troff:
+#
+0 string \100\357 very old (C/A/T) troff output data
+0 string ' [nt]roff, tbl, or eqn input text
+
diff --git a/usr.bin/file/Magdir/typeset b/usr.bin/file/Magdir/typeset
new file mode 100644
index 0000000..5064563
--- /dev/null
+++ b/usr.bin/file/Magdir/typeset
@@ -0,0 +1,5 @@
+# other typesetting magic
+0 string \100\357 very old (C/A/T) troff output data
+0 string Interpress/Xerox Xerox InterPress data
+>16 string / (version
+>>17 string >\0 %s)
diff --git a/usr.bin/file/Magdir/unknown b/usr.bin/file/Magdir/unknown
new file mode 100644
index 0000000..1ea5f06
--- /dev/null
+++ b/usr.bin/file/Magdir/unknown
@@ -0,0 +1,35 @@
+#
+# magic.unknown: Unknown machine magic
+#
+# XXX - this probably should be pruned, as it'll match PDP-11 and
+# VAX image formats.
+#
+# 0x107 is 0407; 0x108 is 0410; both are PDP-11 (executable and pure,
+# respectively).
+#
+# 0x109 is 0411; that's PDP-11 split I&D, but the PDP-11 version doesn't
+# have the "version %ld", which may be a bogus COFFism (I don't think
+# there ever was COFF for the PDP-11).
+#
+# 0x10B is 0413; that's VAX demand-paged, but this is a short, not a
+# long, as it would be on a VAX.
+#
+# 0x10C is 0414, 0x10D is 0415, and 0x10E is 416; those *are* unknown.
+#
+0 short 0x107 unknown machine executable
+>8 short >0 not stripped
+>15 byte >0 - version %ld
+0 short 0x108 unknown pure executable
+>8 short >0 not stripped
+>15 byte >0 - version %ld
+0 short 0x109 PDP-11 separate I&D
+>8 short >0 not stripped
+>15 byte >0 - version %ld
+0 short 0x10b unknown pure executable
+>8 short >0 not stripped
+>15 byte >0 - version %ld
+0 long 0x10c unknown demand paged pure executable
+>16 long >0 not stripped
+0 long 0x10d unknown demand paged pure executable
+>16 long >0 not stripped
+0 long 0x10e unknown readable demand paged pure executable
diff --git a/usr.bin/file/Magdir/uuencode b/usr.bin/file/Magdir/uuencode
new file mode 100644
index 0000000..add48a8
--- /dev/null
+++ b/usr.bin/file/Magdir/uuencode
@@ -0,0 +1,3 @@
+0 string begin uuencoded mail text
+# Btoa(1) is an alternative to uuencode that requires less space.
+0 string xbtoa\ Begin btoa'd text
diff --git a/usr.bin/file/Magdir/varied.out b/usr.bin/file/Magdir/varied.out
new file mode 100644
index 0000000..3a997d1c
--- /dev/null
+++ b/usr.bin/file/Magdir/varied.out
@@ -0,0 +1,5 @@
+# Herewith many of the object file formats used by USG systems.
+# Most have been moved to files for a particular processor,
+# and deleted if they duplicate other entries.
+#
+0 short 0610 Perkin-Elmer executable
diff --git a/usr.bin/file/Magdir/vax b/usr.bin/file/Magdir/vax
new file mode 100644
index 0000000..af665fa
--- /dev/null
+++ b/usr.bin/file/Magdir/vax
@@ -0,0 +1,33 @@
+#
+# magic.pdp: VAX executable/object and APL workspace
+#
+0 lelong 0101557 VAX single precision APL workspace
+0 lelong 0101556 VAX double precision APL workspace
+
+#
+# VAX a.out (32V, BSD)
+#
+0 lelong 0407 VAX executable
+>16 lelong >0 not stripped
+
+0 lelong 0410 VAX pure executable
+>16 lelong >0 not stripped
+
+0 lelong 0413 VAX demand paged pure executable
+>16 lelong >0 not stripped
+
+0 lelong 0420 VAX demand paged (first page unmapped) pure executable
+>16 lelong >0 not stripped
+
+#
+# VAX COFF
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0570 VAX COFF executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %ld
+0 leshort 0575 VAX COFF pure executable
+>12 lelong >0 not stripped
+>22 leshort >0 - version %ld
diff --git a/usr.bin/file/Magdir/visx b/usr.bin/file/Magdir/visx
new file mode 100644
index 0000000..e95cc5e
--- /dev/null
+++ b/usr.bin/file/Magdir/visx
@@ -0,0 +1,30 @@
+#
+# magic.visx: Visx format file
+#
+0 short 0x5555 VISX image file
+>2 byte 0 (zero)
+>2 byte 1 (unsigned char)
+>2 byte 2 (short integer)
+>2 byte 3 (float 32)
+>2 byte 4 (float 64)
+>2 byte 5 (signed char)
+>2 byte 6 (bit-plane)
+>2 byte 7 (classes)
+>2 byte 8 (statistics)
+>2 byte 10 (ascii text)
+>2 byte 15 (image segments)
+>2 byte 100 (image set)
+>2 byte 101 (unsigned char vector)
+>2 byte 102 (short integer vector)
+>2 byte 103 (float 32 vector)
+>2 byte 104 (float 64 vector)
+>2 byte 105 (signed char vector)
+>2 byte 106 (bit plane vector)
+>2 byte 121 (feature vector)
+>2 byte 122 (feature vector library)
+>2 byte 124 (chain code)
+>2 byte 126 (bit vector)
+>2 byte 130 (graph)
+>2 byte 131 (adjacency graph)
+>2 byte 132 (adjacency graph library)
+>2 string .VISIX (ascii text)
diff --git a/usr.bin/file/Magdir/x11 b/usr.bin/file/Magdir/x11
new file mode 100644
index 0000000..a0f3b92
--- /dev/null
+++ b/usr.bin/file/Magdir/x11
@@ -0,0 +1,11 @@
+#
+# magic.x11
+#
+# I think this is byte-order-dependent; if so, it should become:
+#
+# 0 belong 00000004 X11 big-endian snf font
+# 0 lelong 00000004 X11 little-endian snf font
+#
+0 long 00000004 X11 snf font
+0 string STARTFONT X11 bdf font text
+0 string /*\040XPM\040*/ X11 XPM pixmap text
diff --git a/usr.bin/file/Magdir/zilog b/usr.bin/file/Magdir/zilog
new file mode 100644
index 0000000..c7503cd
--- /dev/null
+++ b/usr.bin/file/Magdir/zilog
@@ -0,0 +1,11 @@
+#
+# Zilog Z8000.
+#
+# Was it big-endian or little-endian? My Product Specification doesn't
+# say.
+#
+0 long 0xe807 object file (z8000 a.out)
+0 long 0xe808 pure object file (z8000 a.out)
+0 long 0xe809 separate object file (z8000 a.out)
+0 long 0xe805 overlay object file (z8000 a.out)
+
diff --git a/usr.bin/file/Magdir/zyxel b/usr.bin/file/Magdir/zyxel
new file mode 100644
index 0000000..410e93c
--- /dev/null
+++ b/usr.bin/file/Magdir/zyxel
@@ -0,0 +1,11 @@
+# From <rob@pe1chl.ampr.org>
+# These are the /etc/magic entries to decode datafiles as used for the
+# ZyXEL U-1496E DATA/FAX/VOICE modems. (This header conforms to a
+# ZyXEL-defined standard)
+
+0 string ZyXEL\002 ZyXEL voice data
+>10 byte 0 - CELP encoding
+>10 byte 1 - ADPCM2 encoding
+>10 byte 2 - ADPCM3 encoding
+>10 byte 3 - ADPCM4 encoding
+
diff --git a/usr.bin/file/Makefile b/usr.bin/file/Makefile
new file mode 100644
index 0000000..d2cd561
--- /dev/null
+++ b/usr.bin/file/Makefile
@@ -0,0 +1,58 @@
+# Makefile for file(1) cmd.
+# Copyright (c) Ian F. Darwin 86/09/01 - see LEGAL.NOTICE.
+# @(#)$Id: Makefile,v 1.3 1995/05/30 06:29:57 rgrimes Exp $
+#
+# This software is not subject to any license of the American Telephone
+# and Telegraph Company or of the Regents of the University of California.
+#
+# Permission is granted to anyone to use this software for any purpose on
+# any computer system, and to alter it and redistribute it freely, subject
+# to the following restrictions:
+#
+# 1. The author is not responsible for the consequences of use of this
+# software, no matter how awful, even if they arise from flaws in it.
+#
+# 2. The origin of this software must not be misrepresented, either by
+# explicit claim or by omission. Since few users ever read sources,
+# credits must appear in the documentation.
+#
+# 3. Altered versions must be plainly marked as such, and must not be
+# misrepresented as being the original software. Since few users
+# ever read sources, credits must appear in the documentation.
+#
+# 4. This notice may not be removed or altered.
+#
+# Hacked and dismembered for bmake (Geoff Rehmet).
+MAGIC= /etc/magic
+MAGICOWN= bin
+MAGICGRP= bin
+MAGICMODE= 444
+
+
+CFLAGS+= -DMAGIC='"$(MAGIC)"'
+
+PROG= file
+SRCS= file.c apprentice.c fsmagic.c softmagic.c ascmagic.c \
+ compress.c is_tar.c print.c
+
+MAN1= file.1
+MAN5= magic.5
+
+CLEANFILES+= magic
+
+MAGFILES= $(.CURDIR)/Magdir/Header\
+ $(.CURDIR)/Magdir/Localstuff\
+ $(.CURDIR)/Magdir/[a-z]*
+
+
+all: file magic
+
+magic: $(MAGFILES)
+ cat $(MAGFILES) > $(.TARGET)
+
+afterinstall:
+ ${INSTALL} -c -o $(MAGICOWN) -g $(MAGICGRP) -m $(MAGICMODE) magic \
+ $(DESTDIR)$(MAGIC)
+
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/file/PORTING b/usr.bin/file/PORTING
new file mode 100644
index 0000000..06790e6
--- /dev/null
+++ b/usr.bin/file/PORTING
@@ -0,0 +1,76 @@
+Portability of the new file(1) command.
+@(#) $Id: PORTING,v 1.11 1993/09/23 21:47:23 christos Exp $
+
+Read this file only if the program doesn't compile on your system.
+
+This release has been around UNIX; it has been compiled and tested
+in the following environments:
+
+SunOS sqarc 4.1.1 8 sun4
+ No problems.
+ULTRIX squint 4.2 0 RISC
+ No problems.
+A/UX sqmac 3.0a9 SVR22 mc68020
+ No problems.
+AIX sqibm 2 3 000XXXXXX100
+ Had weird "make" problems making "magic" file automatically; just
+ built it by hand. Your mileage may vary.
+SCO sqwang 3.2 2 i386
+ Compiles fine; their weird make can't handle "[a-z]*" as a dependancy,
+ so build magic by hand. Runs fine.
+sqzme sqzme 3.1.1 3 3B2
+ The 3B2 SVR3 needed a few tweaks as well as COPTS = -Ilocalinc
+ in order to compile.
+
+This version, reluctanly, includes <stdlib.h>, which won't exist
+on older systems or those that aren't even close to the ANSI C
+standard. There is a null "stdlib.h", and some other bogus headers,
+in subdirectory "localinc"; if you get complaints about missing
+stdlib.h and others, uncomment the line with COPTS=-Ilocalinc
+in the Makefile, and try again.
+
+You must have either <stdarg.h> or the older <varargs.h>, otherwise you'll
+have to butcher some routines in print.c.
+
+Beyond that, I have tried to make a program that doesn't need any
+command-line defines (-D) to specify what version of UNIX is in use,
+by using the definitions available in the system #include
+files. For example, the lstat(2) call is normally found in
+4BSD systems, but might be grafted into some other variant
+of UNIX. If it's done right (ie., using the same definitions),
+my program will compile and work correctly. Look at the #ifdefs
+to see how it's done.
+
+I've also tried to include source for all the non-portable library routines
+I used (getopt, str*). Non-portable here means `not in every
+reasonably standard UNIX out there: V7, System V, 4BSD'.
+These are in subdirectory "localsrc", and not used unless you
+need them; again, see the Makefile.
+
+There is one area that just might cause problems. On System
+V, they moved the definition of major() and minor() out of
+<sys/types.h> into <sys/sysmacros.h>. Hence, if major isn't
+defined after including types.h, I automatically include sys/sysmacros.h.
+This will work for 99% of the systems out there. ONLY if you
+have a system in which neither types.h nor sysmacros.h defines
+`major' will this automatic include fail (I hope). On such
+systems, you will get a compilation error in trying to compile
+a warning message. Please do the following:
+
+ 1) change the appropriate #include at the start of fsmagic.c
+and 2) let me know the name of the system, the release number,
+ and the name of the header file that *does* include
+ this "standard" definition.
+
+If you are running the old Ritchie PDP-11 C compiler or
+some other compiler that doesn't know about `void', you will have
+to include `-Dvoid=int' in the variable COPTS in the Makefile.
+
+Other than this, there should be no portability problems,
+but one never knows these days. Please let me know of any
+other problems you find porting to a UNIX system. I don't much
+care about non-UNIX systems but will collect widely-used magic
+numbers for them as well as for UNIX systems.
+
+Mark Moraes and Christos Zoulas
+(address in README)
diff --git a/usr.bin/file/README b/usr.bin/file/README
new file mode 100644
index 0000000..2ee8e67
--- /dev/null
+++ b/usr.bin/file/README
@@ -0,0 +1,79 @@
+** README for file(1) Command **
+@(#) $Id: README,v 1.20 1993/09/23 21:47:01 christos Exp $
+
+This is Release 3.x of Ian Darwin's (copyright but distributable)
+file(1) command. Release 3.x is scheduled for inclusion in the
+4.4 BSD (Berkeley Software Distribution) of UNIX-like
+software, and is the standard "file" command for Linux, 386bsd,
+and other systems. (See "patchlevel.h" for the exact release number).
+
+UNIX is a trademark of UNIX System Laboratories.
+
+The prime contributor to Release 3.8 was Guy Harris, who put in megachanges
+including byte-order independance.
+
+The prime contributor to Release 3.0 was Christos Zoulas, who put
+in hundreds of lines of source code changes, including his own
+ANSIfication of the code (I liked my own ANSIfication better, but
+his (__P()) is the "Berkeley standard" way of doing it, and I wanted UCB
+to include the code...), his HP-like "indirection" (a feature of
+the HP file command, I think), and his mods that finally got the
+uncompress (-z) mode finished and working.
+
+This release has compiled in numerous environments; see PORTING
+for a list and problems.
+
+This fine freeware file(1) follows the USG (System V) model of the file
+command, rather than the Research (V7) version or the V7-derived 4.[23]
+Berkeley one. That is, the file /etc/magic contains much of the ritual
+information that is the source of this program's power. My version
+knows a little more magic (including tar archives) than System V; the
+/etc/magic parsing seems to be compatible with the (poorly documented)
+System V /etc/magic format (with one exception; see the man page).
+
+In addition, the /etc/magic file is built from a subdirectory
+for easier(?) maintenance. I will act as a clearinghouse for
+magic numbers assigned to all sorts of data files that
+are in reasonable circulation. Send your magic numbers,
+in magic(4) format please, to the maintainer, Christos Zoulas.
+
+LEGAL.NOTICE - read this first.
+README - read this second (you are currently reading this file).
+PORTING - read this only if the program won't compile.
+Makefile - read this next, adapt it as needed (particularly
+ the location of the old existing file command and
+ the man page layouts), type "make" to compile,
+ "make try" to try it out against your old version.
+ Expect some diffs, particularly since your original
+ file(1) may not grok the imbedded-space ("\ ") in
+ the current magic file, or may even not use the
+ magic file.
+apprentice.c - parses /etc/magic to learn magic
+ascmagic.c - third & last set of tests, based on hardwired assumptions.
+core - not included in distribution due to mailer limitations.
+debug.c - includes -c printout routine
+file.1 - man page for the command
+magic.4 - man page for the magic file, courtesy Guy Harris.
+ Install as magic.4 on USG and magic.5 on V7 or Berkeley; cf Makefile.
+file.c - main program
+file.h - header file
+fsmagic.c - first set of tests the program runs, based on filesystem info
+is_tar.c - knows about tarchives (courtesy John Gilmore).
+magdir - directory of /etc/magic pieces
+ magdir/Makefile - ADJUST THIS FOR YOUR CONFIGURATION
+names.h - header file for ascmagic.c
+softmagic.c - 2nd set of tests, based on /etc/magic
+strtok.c, getopt.c - in case you them (courtesy of Henry Spencer).
+strtol.c, strchr.c - in case you need them - public domain.
+tst - simple test suite, built from tst/Makefile
+
+
+E-mail: christos@deshaw.com, moraes@deshaw.com
+
+Phone: Do not even think of telephoning me about this program. Send cash first!
+
+Parts of this software were developed at SoftQuad Inc., 56 Aberfoyle
+Cres, # 810, Toronto, Ontario CANADA M8X 2W4. Phone: 416-239-4801 or
+800-387-2777. Email: mail@sq.com. Call for information on SGML editing
+and browsing, Unix text processing, and customised products on Unix,
+DOS and Mac.
diff --git a/usr.bin/file/apprentice.c b/usr.bin/file/apprentice.c
new file mode 100644
index 0000000..6cdacaa
--- /dev/null
+++ b/usr.bin/file/apprentice.c
@@ -0,0 +1,551 @@
+/*
+ * apprentice - make one pass through /etc/magic, learning its secrets.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: apprentice.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $";
+#endif /* lint */
+
+#define EATAB {while (isascii((unsigned char) *l) && \
+ isspace((unsigned char) *l)) ++l;}
+
+
+static int getvalue __P((struct magic *, char **));
+static int hextoint __P((int));
+static char *getstr __P((char *, char *, int, int *));
+static int parse __P((char *, int *, int));
+
+static int maxmagic = 0;
+
+int
+apprentice(fn, check)
+char *fn; /* name of magic file */
+int check; /* non-zero? checking-only run. */
+{
+ FILE *f;
+ char line[BUFSIZ+1];
+ int errs = 0;
+
+ f = fopen(fn, "r");
+ if (f==NULL) {
+ (void) fprintf(stderr, "%s: can't read magic file %s\n",
+ progname, fn);
+ if (check)
+ return -1;
+ else
+ exit(1);
+ }
+
+ maxmagic = MAXMAGIS;
+ if ((magic = (struct magic *) calloc(sizeof(struct magic), maxmagic))
+ == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory.\n", progname);
+ if (check)
+ return -1;
+ else
+ exit(1);
+ }
+
+ /* parse it */
+ if (check) /* print silly verbose header for USG compat. */
+ (void) printf("cont\toffset\ttype\topcode\tmask\tvalue\tdesc\n");
+
+ for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) {
+ if (line[0]=='#') /* comment, do not parse */
+ continue;
+ if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */
+ continue;
+ line[strlen(line)-1] = '\0'; /* delete newline */
+ if (parse(line, &nmagic, check) != 0)
+ ++errs;
+ }
+
+ (void) fclose(f);
+ return errs ? -1 : 0;
+}
+
+/*
+ * extend the sign bit if the comparison is to be signed
+ */
+unsigned long
+signextend(m, v)
+struct magic *m;
+unsigned long v;
+{
+ if (!(m->flag & UNSIGNED))
+ switch(m->type) {
+ /*
+ * Do not remove the casts below. They are
+ * vital. When later compared with the data,
+ * the sign extension must have happened.
+ */
+ case BYTE:
+ v = (char) v;
+ break;
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = (short) v;
+ break;
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ case LONG:
+ case BELONG:
+ case LELONG:
+ v = (long) v;
+ break;
+ case STRING:
+ break;
+ default:
+ magwarn("can't happen: m->type=%d\n",
+ m->type);
+ return -1;
+ }
+ return v;
+}
+
+/*
+ * parse one line from magic file, put into magic[index++] if valid
+ */
+static int
+parse(l, ndx, check)
+char *l;
+int *ndx, check;
+{
+ int i = 0, nd = *ndx;
+ struct magic *m;
+ char *t, *s;
+
+ if (nd+1 >= maxmagic){
+ maxmagic += 20;
+ if ((magic = (struct magic *) realloc(magic,
+ sizeof(struct magic) *
+ maxmagic)) == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory.\n", progname);
+ if (check)
+ return -1;
+ else
+ exit(1);
+ }
+ }
+ m = &magic[*ndx];
+ m->flag = 0;
+ m->cont_level = 0;
+
+ while (*l == '>') {
+ ++l; /* step over */
+ m->cont_level++;
+ }
+
+ if (m->cont_level != 0 && *l == '(') {
+ ++l; /* step over */
+ m->flag |= INDIR;
+ }
+
+ /* get offset, then skip over it */
+ m->offset = (int) strtol(l,&t,0);
+ if (l == t)
+ magwarn("offset %s invalid", l);
+ l = t;
+
+ if (m->flag & INDIR) {
+ m->in.type = LONG;
+ m->in.offset = 0;
+ /*
+ * read [.lbs][+-]nnnnn)
+ */
+ if (*l == '.') {
+ switch (*++l) {
+ case 'l':
+ m->in.type = LONG;
+ break;
+ case 's':
+ m->in.type = SHORT;
+ break;
+ case 'b':
+ m->in.type = BYTE;
+ break;
+ default:
+ magwarn("indirect offset type %c invalid", *l);
+ break;
+ }
+ l++;
+ }
+ s = l;
+ if (*l == '+' || *l == '-') l++;
+ if (isdigit((unsigned char)*l)) {
+ m->in.offset = strtol(l, &t, 0);
+ if (*s == '-') m->in.offset = - m->in.offset;
+ }
+ else
+ t = l;
+ if (*t++ != ')')
+ magwarn("missing ')' in indirect offset");
+ l = t;
+ }
+
+
+ while (isascii((unsigned char)*l) && isdigit((unsigned char)*l))
+ ++l;
+ EATAB;
+
+#define NBYTE 4
+#define NSHORT 5
+#define NLONG 4
+#define NSTRING 6
+#define NDATE 4
+#define NBESHORT 7
+#define NBELONG 6
+#define NBEDATE 6
+#define NLESHORT 7
+#define NLELONG 6
+#define NLEDATE 6
+
+ if (*l == 'u') {
+ ++l;
+ m->flag |= UNSIGNED;
+ }
+
+ /* get type, skip it */
+ if (strncmp(l, "byte", NBYTE)==0) {
+ m->type = BYTE;
+ l += NBYTE;
+ } else if (strncmp(l, "short", NSHORT)==0) {
+ m->type = SHORT;
+ l += NSHORT;
+ } else if (strncmp(l, "long", NLONG)==0) {
+ m->type = LONG;
+ l += NLONG;
+ } else if (strncmp(l, "string", NSTRING)==0) {
+ m->type = STRING;
+ l += NSTRING;
+ } else if (strncmp(l, "date", NDATE)==0) {
+ m->type = DATE;
+ l += NDATE;
+ } else if (strncmp(l, "beshort", NBESHORT)==0) {
+ m->type = BESHORT;
+ l += NBESHORT;
+ } else if (strncmp(l, "belong", NBELONG)==0) {
+ m->type = BELONG;
+ l += NBELONG;
+ } else if (strncmp(l, "bedate", NBEDATE)==0) {
+ m->type = BEDATE;
+ l += NBEDATE;
+ } else if (strncmp(l, "leshort", NLESHORT)==0) {
+ m->type = LESHORT;
+ l += NLESHORT;
+ } else if (strncmp(l, "lelong", NLELONG)==0) {
+ m->type = LELONG;
+ l += NLELONG;
+ } else if (strncmp(l, "ledate", NLEDATE)==0) {
+ m->type = LEDATE;
+ l += NLEDATE;
+ } else {
+ magwarn("type %s invalid", l);
+ return -1;
+ }
+ /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
+ if (*l == '&') {
+ ++l;
+ m->mask = signextend(m, strtol(l, &l, 0));
+ } else
+ m->mask = ~0L;
+ EATAB;
+
+ switch (*l) {
+ case '>':
+ case '<':
+ /* Old-style anding: "0 byte &0x80 dynamically linked" */
+ case '&':
+ case '^':
+ case '=':
+ m->reln = *l;
+ ++l;
+ break;
+ case '!':
+ if (m->type != STRING) {
+ m->reln = *l;
+ ++l;
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ if (*l == 'x' && isascii((unsigned char)l[1]) &&
+ isspace((unsigned char)l[1])) {
+ m->reln = *l;
+ ++l;
+ goto GetDesc; /* Bill The Cat */
+ }
+ m->reln = '=';
+ break;
+ }
+ EATAB;
+
+ if (getvalue(m, &l))
+ return -1;
+ /*
+ * TODO finish this macro and start using it!
+ * #define offsetcheck {if (offset > HOWMANY-1)
+ * magwarn("offset too big"); }
+ */
+
+ /*
+ * now get last part - the description
+ */
+GetDesc:
+ EATAB;
+ if (l[0] == '\b') {
+ ++l;
+ m->nospflag = 1;
+ } else if ((l[0] == '\\') && (l[1] == 'b')) {
+ ++l;
+ ++l;
+ m->nospflag = 1;
+ } else
+ m->nospflag = 0;
+ while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
+ /* NULLBODY */;
+
+ if (check) {
+ mdump(m);
+ }
+ ++(*ndx); /* make room for next */
+ return 0;
+}
+
+/*
+ * Read a numeric value from a pointer, into the value union of a magic
+ * pointer, according to the magic type. Update the string pointer to point
+ * just after the number read. Return 0 for success, non-zero for failure.
+ */
+static int
+getvalue(m, p)
+struct magic *m;
+char **p;
+{
+ int slen;
+
+ if (m->type == STRING) {
+ *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
+ m->vallen = slen;
+ } else
+ if (m->reln != 'x')
+ m->value.l = signextend(m, strtol(*p, p, 0));
+ return 0;
+}
+
+/*
+ * Convert a string containing C character escapes. Stop at an unescaped
+ * space or tab.
+ * Copy the converted version to "p", returning its length in *slen.
+ * Return updated scan pointer as function result.
+ */
+static char *
+getstr(s, p, plen, slen)
+register char *s;
+register char *p;
+int plen, *slen;
+{
+ char *origs = s, *origp = p;
+ char *pmax = p + plen - 1;
+ register int c;
+ register int val;
+
+ while ((c = *s++) != '\0') {
+ if (isspace((unsigned char) c))
+ break;
+ if (p >= pmax) {
+ fprintf(stderr, "String too long: %s\n", origs);
+ break;
+ }
+ if(c == '\\') {
+ switch(c = *s++) {
+
+ case '\0':
+ goto out;
+
+ default:
+ *p++ = (char) c;
+ break;
+
+ case 'n':
+ *p++ = '\n';
+ break;
+
+ case 'r':
+ *p++ = '\r';
+ break;
+
+ case 'b':
+ *p++ = '\b';
+ break;
+
+ case 't':
+ *p++ = '\t';
+ break;
+
+ case 'f':
+ *p++ = '\f';
+ break;
+
+ case 'v':
+ *p++ = '\v';
+ break;
+
+ /* \ and up to 3 octal digits */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = c - '0';
+ c = *s++; /* try for 2 */
+ if(c >= '0' && c <= '7') {
+ val = (val<<3) | (c - '0');
+ c = *s++; /* try for 3 */
+ if(c >= '0' && c <= '7')
+ val = (val<<3) | (c-'0');
+ else
+ --s;
+ }
+ else
+ --s;
+ *p++ = (char)val;
+ break;
+
+ /* \x and up to 3 hex digits */
+ case 'x':
+ val = 'x'; /* Default if no digits */
+ c = hextoint(*s++); /* Get next char */
+ if (c >= 0) {
+ val = c;
+ c = hextoint(*s++);
+ if (c >= 0) {
+ val = (val << 4) + c;
+ c = hextoint(*s++);
+ if (c >= 0) {
+ val = (val << 4) + c;
+ } else
+ --s;
+ } else
+ --s;
+ } else
+ --s;
+ *p++ = (char)val;
+ break;
+ }
+ } else
+ *p++ = (char)c;
+ }
+out:
+ *p = '\0';
+ *slen = p - origp;
+ return s;
+}
+
+
+/* Single hex char to int; -1 if not a hex char. */
+static int
+hextoint(c)
+int c;
+{
+ if (!isascii((unsigned char) c)) return -1;
+ if (isdigit((unsigned char) c)) return c - '0';
+ if ((c>='a')&&(c<='f')) return c + 10 - 'a';
+ if ((c>='A')&&(c<='F')) return c + 10 - 'A';
+ return -1;
+}
+
+
+/*
+ * Print a string containing C character escapes.
+ */
+void
+showstr(fp, s, len)
+FILE *fp;
+const char *s;
+int len;
+{
+ register char c;
+
+ for (;;) {
+ c = *s++;
+ if (len == -1) {
+ if (c == '\0')
+ break;
+ }
+ else {
+ if (len-- == 0)
+ break;
+ }
+ if(c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */
+ (void) fputc(c, fp);
+ else {
+ (void) fputc('\\', fp);
+ switch (c) {
+
+ case '\n':
+ (void) fputc('n', fp);
+ break;
+
+ case '\r':
+ (void) fputc('r', fp);
+ break;
+
+ case '\b':
+ (void) fputc('b', fp);
+ break;
+
+ case '\t':
+ (void) fputc('t', fp);
+ break;
+
+ case '\f':
+ (void) fputc('f', fp);
+ break;
+
+ case '\v':
+ (void) fputc('v', fp);
+ break;
+
+ default:
+ (void) fprintf(fp, "%.3o", c & 0377);
+ break;
+ }
+ }
+ }
+}
diff --git a/usr.bin/file/ascmagic.c b/usr.bin/file/ascmagic.c
new file mode 100644
index 0000000..0edb0ca
--- /dev/null
+++ b/usr.bin/file/ascmagic.c
@@ -0,0 +1,120 @@
+/*
+ * Ascii magic -- file types that we know based on keywords
+ * that can appear anywhere in the file.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "file.h"
+#include "names.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: ascmagic.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $";
+#endif /* lint */
+
+ /* an optimisation over plain strcmp() */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+int
+ascmagic(buf, nbytes)
+unsigned char *buf;
+int nbytes; /* size actually read */
+{
+ int i, isblock, has_escapes = 0;
+ unsigned char *s;
+ char nbuf[HOWMANY+1]; /* one extra for terminating '\0' */
+ char *token;
+ register struct names *p;
+
+ /* these are easy, do them first */
+
+ /*
+ * for troff, look for . + letter + letter or .\";
+ * this must be done to disambiguate tar archives' ./file
+ * and other trash from real troff input.
+ */
+ if (*buf == '.') {
+ unsigned char *tp = buf + 1;
+
+ while (isascii(*tp) && isspace(*tp))
+ ++tp; /* skip leading whitespace */
+ if ((isascii(*tp) && (isalnum(*tp) || *tp=='\\') &&
+ isascii(*(tp+1)) && (isalnum(*(tp+1)) || *tp=='"'))) {
+ ckfputs("troff or preprocessor input text", stdout);
+ return 1;
+ }
+ }
+ if ((*buf == 'c' || *buf == 'C') &&
+ isascii(*(buf + 1)) && isspace(*(buf + 1))) {
+ ckfputs("fortran program text", stdout);
+ return 1;
+ }
+
+ /* look for tokens from names.h - this is expensive! */
+ /* make a copy of the buffer here because strtok() will destroy it */
+ s = (unsigned char*) memcpy(nbuf, buf, nbytes);
+ s[nbytes] = '\0';
+ has_escapes = (memchr(s, '\033', nbytes) != NULL);
+ while ((token = strtok((char*)s, " \t\n\r\f")) != NULL) {
+ s = NULL; /* make strtok() keep on tokin' */
+ for (p = names; p < names + NNAMES; p++) {
+ if (STREQ(p->name, token)) {
+ ckfputs(types[p->type], stdout);
+ if (has_escapes)
+ ckfputs(" (with escape sequences)",
+ stdout);
+ return 1;
+ }
+ }
+ }
+
+ switch (is_tar(buf, nbytes)) {
+ case 1:
+ ckfputs("tar archive", stdout);
+ return 1;
+ case 2:
+ ckfputs("POSIX tar archive", stdout);
+ return 1;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ if (!isascii(*(buf+i)))
+ return 0; /* not all ascii */
+ }
+
+ /* all else fails, but it is ascii... */
+ ckfputs("ascii text", stdout);
+ if (has_escapes) {
+ ckfputs(" (with escape sequences)", stdout);
+ }
+ return 1;
+}
+
+
diff --git a/usr.bin/file/compress.c b/usr.bin/file/compress.c
new file mode 100644
index 0000000..1c0d824
--- /dev/null
+++ b/usr.bin/file/compress.c
@@ -0,0 +1,125 @@
+/*
+ * compress routines:
+ * zmagic() - returns 0 if not recognized, uncompresses and prints
+ * information if recognized
+ * uncompress(method, old, n, newch) - uncompress old into new,
+ * using method, return sizeof new
+ * $Id: compress.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include "file.h"
+
+static struct {
+ char *magic;
+ int maglen;
+ char *argv[3];
+ int silent;
+} compr[] = {
+ { "\037\235", 2, { "uncompress", "-c", NULL }, 0 },
+ { "\037\213", 2, { "gzip", "-dq", NULL }, 1 },
+ /*
+ * XXX pcat does not work, cause I don't know how to make it read stdin,
+ * so we use gzip
+ */
+ { "\037\036", 2, { "gzip", "-dq", NULL }, 0 },
+};
+
+static int ncompr = sizeof(compr) / sizeof(compr[0]);
+
+
+static int uncompress __P((int, const unsigned char *, unsigned char **, int));
+
+int
+zmagic(buf, nbytes)
+unsigned char *buf;
+int nbytes;
+{
+ unsigned char *newbuf;
+ int newsize;
+ int i;
+
+ for (i = 0; i < ncompr; i++) {
+ if (nbytes < compr[i].maglen)
+ continue;
+ if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0)
+ break;
+ }
+
+ if (i == ncompr)
+ return 0;
+
+ if ((newsize = uncompress(i, buf, &newbuf, nbytes)) != 0) {
+ tryit(newbuf, newsize, 1);
+ free(newbuf);
+ printf(" (");
+ tryit(buf, nbytes, 0);
+ printf(")");
+ }
+ return 1;
+}
+
+
+static int
+uncompress(method, old, newch, n)
+int method;
+const unsigned char *old;
+unsigned char **newch;
+int n;
+{
+ int fdin[2], fdout[2];
+
+ if (pipe(fdin) == -1 || pipe(fdout) == -1) {
+ error("cannot create pipe (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+ }
+ switch (fork()) {
+ case 0: /* child */
+ (void) close(0);
+ (void) dup(fdin[0]);
+ (void) close(fdin[0]);
+ (void) close(fdin[1]);
+
+ (void) close(1);
+ (void) dup(fdout[1]);
+ (void) close(fdout[0]);
+ (void) close(fdout[1]);
+ if (compr[method].silent)
+ (void) close(2);
+
+ execvp(compr[method].argv[0], compr[method].argv);
+ error("could not execute `%s' (%s).\n",
+ compr[method].argv[0], strerror(errno));
+ /*NOTREACHED*/
+ case -1:
+ error("could not fork (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+
+ default: /* parent */
+ (void) close(fdin[0]);
+ (void) close(fdout[1]);
+ if (write(fdin[1], old, n) != n) {
+ error("write failed (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+ }
+ (void) close(fdin[1]);
+ if ((*newch = (unsigned char *) malloc(n)) == NULL) {
+ error("out of memory.\n");
+ /*NOTREACHED*/
+ }
+ if ((n = read(fdout[0], *newch, n)) <= 0) {
+ free(*newch);
+ error("read failed (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+ }
+ (void) close(fdout[0]);
+ (void) wait(NULL);
+ return n;
+ }
+}
+
+
diff --git a/usr.bin/file/cvsimport.sh b/usr.bin/file/cvsimport.sh
new file mode 100644
index 0000000..6112e96
--- /dev/null
+++ b/usr.bin/file/cvsimport.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# import sequence for file(1)
+# This shell script can be used in order to handle future imports
+# of newer versions of file(1)
+#
+# $Id$
+if [ $# -ne 2 ] ; then
+ echo "usage: $0 <major> <minor>" 1>&2
+ exit 1
+fi
+version=$1.$2
+tar xzf file-$version.tar.gz
+cd file-$version
+mv file.man file.1
+mv magic.man magic.5
+rm Magdir/Makefile
+cvs -n import src/usr.bin/file DARWIN file_$1_$2
diff --git a/usr.bin/file/file.1 b/usr.bin/file/file.1
new file mode 100644
index 0000000..555261f
--- /dev/null
+++ b/usr.bin/file/file.1
@@ -0,0 +1,351 @@
+.TH FILE 1 "Copyright but distributable"
+.\# $Id: file.man,v 1.23 1993/09/24 18:50:48 christos Exp $
+.SH NAME
+.I file
+\- determine file type
+.SH SYNOPSIS
+.B file
+[
+.B \-c
+]
+[
+.B \-z
+]
+[
+.B \-L
+]
+[
+.B \-f
+namefile ]
+[
+.B \-m
+magicfile ]
+file ...
+.SH DESCRIPTION
+.I File
+tests each argument in an attempt to classify it.
+There are three sets of tests, performed in this order:
+filesystem tests, magic number tests, and language tests.
+The
+.I first
+test that succeeds causes the file type to be printed.
+.PP
+The type printed will usually contain one of the words
+.B text
+(the file contains only ASCII characters and is
+probably safe to read on an ASCII terminal),
+.B executable
+(the file contains the result of compiling a program
+in a form understandable to some \s-1UNIX\s0 kernel or another),
+or
+.B data
+meaning anything else (data is usually `binary' or non-printable).
+Exceptions are well-known file formats (core files, tar archives)
+that are known to contain binary data.
+When modifying the file
+.I /etc/magic
+or the program itself,
+.B "preserve these keywords" .
+People depend on knowing that all the readable files in a directory
+have the word ``text'' printed.
+Don't do as Berkeley did \- change ``shell commands text''
+to ``shell script''.
+.PP
+The filesystem tests are based on examining the return from a
+.IR stat (2)
+system call.
+The program checks to see if the file is empty,
+or if it's some sort of special file.
+Any known file types appropriate to the system you are running on
+(sockets, symbolic links, or named pipes (FIFOs) on those systems that
+implement them)
+are intuited if they are defined in
+the system header file
+.BR sys/stat.h .
+.PP
+The magic number tests are used to check for files with data in
+particular fixed formats.
+The canonical example of this is a binary executable (compiled program)
+.B a.out
+file, whose format is defined in
+.B a.out.h
+and possibly
+.B exec.h
+in the standard include directory.
+These files have a `magic number' stored in a particular place
+near the beginning of the file that tells the \s-1UNIX\s0 operating system
+that the file is a binary executable, and which of several types thereof.
+The concept of `magic number' has been applied by extension to data files.
+Any file with some invariant identifier at a small fixed
+offset into the file can usually be described in this way.
+The information in these files is read from the magic file
+.I /etc/magic.
+.PP
+If an argument appears to be an
+.SM ASCII
+file,
+.I file
+attempts to guess its language.
+The language tests look for particular strings (cf \fInames.h\fP)
+that can appear anywhere in the first few blocks of a file.
+For example, the keyword
+.B .br
+indicates that the file is most likely a troff input file,
+just as the keyword
+.B struct
+indicates a C program.
+These tests are less reliable than the previous
+two groups, so they are performed last.
+The language test routines also test for some miscellany
+(such as
+.I tar
+archives) and determine whether an unknown file should be
+labelled as `ascii text' or `data'.
+.PP
+Use
+.B \-m
+.I file
+to specify an alternate file of magic numbers.
+.PP
+The
+.B \-z
+tries to look inside compressed files.
+.PP
+The
+.B \-c
+option causes a checking printout of the parsed form of the magic file.
+This is usually used in conjunction with
+.B \-m
+to debug a new magic file before installing it.
+.PP
+The
+.B \-f
+.I namefile
+option specifies that the names of the files to be examined
+are to be read (one per line) from
+.I namefile
+before the argument list.
+Either
+.I namefile
+or at least one filename argument must be present;
+to test the standard input, use ``-'' as a filename argument.
+.PP
+The
+.B \-L
+option causes symlinks to be followed, as the like-named option in
+.IR ls (1).
+.SH FILES
+.I /etc/magic
+\- default list of magic numbers
+.SH SEE ALSO
+.IR magic (5)
+\- description of magic file format.
+.br
+.IR Strings (1), " od" (1)
+\- tools for examining non-textfiles.
+.SH STANDARDS CONFORMANCE
+This program is believed to exceed the System V Interface Definition
+of FILE(CMD), as near as one can determine from the vague language
+contained therein.
+Its behaviour is mostly compatible with the System V program of the same name.
+This version knows more magic, however, so it will produce
+different (albeit more accurate) output in many cases.
+.PP
+The one significant difference
+between this version and System V
+is that this version treats any white space
+as a delimiter, so that spaces in pattern strings must be escaped.
+For example,
+.br
+>10 string language impress\ (imPRESS data)
+.br
+in an existing magic file would have to be changed to
+.br
+>10 string language\e impress (imPRESS data)
+.br
+In addition, in this version, if a pattern string contains a backslash,
+it must be escaped. For example
+.br
+0 string \ebegindata Andrew Toolkit document
+.br
+in an existing magic file would have to be changed to
+.br
+0 string \e\ebegindata Andrew Toolkit document
+.br
+.PP
+SunOS releases 3.2 and later from Sun Microsystems include a
+.IR file (1)
+command derived from the System V one, but with some extensions.
+My version differs from Sun's only in minor ways.
+It includes the extension of the `&' operator, used as,
+for example,
+.br
+>16 long&0x7fffffff >0 not stripped
+.SH MAGIC DIRECTORY
+The magic file entries have been collected from various sources,
+mainly USENET, and contributed by various authors.
+Christos Zoulas (address below) will collect additional
+or corrected magic file entries.
+A consolidation of magic file entries
+will be distributed periodically.
+.PP
+The order of entries in the magic file is significant.
+Depending on what system you are using, the order that
+they are put together may be incorrect.
+If your old
+.I file
+command uses a magic file,
+keep the old magic file around for comparison purposes
+(rename it to
+.IR /etc/magic.orig ).
+.SH HISTORY
+There has been a
+.I file
+command in every UNIX since at least Research Version 6
+(man page dated January, 1975).
+The System V version introduced one significant major change:
+the external list of magic number types.
+This slowed the program down slightly but made it a lot more flexible.
+.PP
+This program, based on the System V version,
+was written by Ian Darwin without looking at anybody else's source code.
+.PP
+John Gilmore revised the code extensively, making it better than
+the first version.
+Geoff Collyer found several inadequacies
+and provided some magic file entries.
+The program has undergone continued evolution since.
+.SH AUTHOR
+Written by Ian F. Darwin, UUCP address {utzoo | ihnp4}!darwin!ian,
+Internet address ian@sq.com,
+postal address: P.O. Box 603, Station F, Toronto, Ontario, CANADA M4Y 2L8.
+.PP
+Altered by Rob McMahon, cudcv@warwick.ac.uk, 1989, to extend the `&' operator
+from simple `x&y != 0' to `x&y op z'.
+.PP
+Altered by Guy Harris, guy@auspex.com, 1993, to:
+.RS
+.PP
+put the ``old-style'' `&'
+operator back the way it was, because 1) Rob McMahon's change broke the
+previous style of usage, 2) the SunOS ``new-style'' `&' operator,
+which this version of
+.I file
+supports, also handles `x&y op z', and 3) Rob's change wasn't documented
+in any case;
+.PP
+put in multiple levels of `>';
+.PP
+put in ``beshort'', ``leshort'', etc. keywords to look at numbers in the
+file in a specific byte order, rather than in the native byte order of
+the process running
+.IR file .
+.RE
+.PP
+Changes by Ian Darwin and various authors including
+Christos Zoulas (christos@ee.cornell.edu), 1990-1992.
+.SH LEGAL NOTICE
+Copyright (c) Ian F. Darwin, Toronto, Canada,
+1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993.
+.PP
+This software is not subject to and may not be made subject to any
+license of the American Telephone and Telegraph Company, Sun
+Microsystems Inc., Digital Equipment Inc., Lotus Development Inc., the
+Regents of the University of California, The X Consortium or MIT, or
+The Free Software Foundation.
+.PP
+This software is not subject to any export provision of the United States
+Department of Commerce, and may be exported to any country or planet.
+.PP
+Permission is granted to anyone to use this software for any purpose on
+any computer system, and to alter it and redistribute it freely, subject
+to the following restrictions:
+.PP
+1. The author is not responsible for the consequences of use of this
+software, no matter how awful, even if they arise from flaws in it.
+.PP
+2. The origin of this software must not be misrepresented, either by
+explicit claim or by omission. Since few users ever read sources,
+credits must appear in the documentation.
+.PP
+3. Altered versions must be plainly marked as such, and must not be
+misrepresented as being the original software. Since few users
+ever read sources, credits must appear in the documentation.
+.PP
+4. This notice may not be removed or altered.
+.PP
+A few support files (\fIgetopt\fP, \fIstrtok\fP)
+distributed with this package
+are by Henry Spencer and are subject to the same terms as above.
+.PP
+A few simple support files (\fIstrtol\fP, \fIstrchr\fP)
+distributed with this package
+are in the public domain; they are so marked.
+.PP
+The files
+.I tar.h
+and
+.I is_tar.c
+were written by John Gilmore from his public-domain
+.I tar
+program, and are not covered by the above restrictions.
+.SH BUGS
+There must be a better way to automate the construction of the Magic
+file from all the glop in Magdir. What is it?
+Better yet, the magic file should be compiled into binary (say,
+.IR ndbm (3)
+or, better yet, fixed-length ASCII strings
+for use in heterogenous network environments) for faster startup.
+Then the program would run as fast as the Version 7 program of the same name,
+with the flexibility of the System V version.
+.PP
+.I File
+uses several algorithms that favor speed over accuracy,
+thus it can be misled about the contents of ASCII files.
+.PP
+The support for ASCII files (primarily for programming languages)
+is simplistic, inefficient and requires recompilation to update.
+.PP
+There should be an ``else'' clause to follow a series of continuation lines.
+.PP
+The magic file and keywords should have regular expression support.
+Their use of ASCII TAB as a field delimiter is ugly and makes
+it hard to edit the files, but is entrenched.
+.PP
+It might be advisable to allow upper-case letters in keywords
+for e.g., troff commands vs man page macros.
+Regular expression support would make this easy.
+.PP
+The program doesn't grok \s-2FORTRAN\s0.
+It should be able to figure \s-2FORTRAN\s0 by seeing some keywords which
+appear indented at the start of line.
+Regular expression support would make this easy.
+.PP
+The list of keywords in
+.I ascmagic
+probably belongs in the Magic file.
+This could be done by using some keyword like `*' for the offset value.
+.PP
+Another optimisation would be to sort
+the magic file so that we can just run down all the
+tests for the first byte, first word, first long, etc, once we
+have fetched it. Complain about conflicts in the magic file entries.
+Make a rule that the magic entries sort based on file offset rather
+than position within the magic file?
+.PP
+The program should provide a way to give an estimate
+of ``how good'' a guess is.
+We end up removing guesses (e.g. ``From '' as first 5 chars of file) because
+they are not as good as other guesses (e.g. ``Newsgroups:'' versus
+"Return-Path:"). Still, if the others don't pan out, it should be
+possible to use the first guess.
+.PP
+This program is slower than some vendors' file commands.
+.PP
+This manual page, and particularly this section, is too long.
+.SH AVAILABILITY
+You can obtain the original author's latest version by anonymous FTP
+on
+.B tesla.ee.cornell.edu
+in the directory
+.BR /pub/file-X.YY.tar.gz
diff --git a/usr.bin/file/file.c b/usr.bin/file/file.c
new file mode 100644
index 0000000..4b12a9a
--- /dev/null
+++ b/usr.bin/file/file.c
@@ -0,0 +1,276 @@
+/*
+ * file - find type of a file or files - main program.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: file.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $";
+#endif /* lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/stat.h>
+#include <fcntl.h> /* for open() */
+#include <utime.h>
+#include <unistd.h> /* for read() */
+
+#include "file.h"
+
+#ifdef S_IFLNK
+# define USAGE "Usage: %s [-czL] [-f namefile] [-m magicfile] file...\n"
+#else
+# define USAGE "Usage: %s [-cz] [-f namefile] [-m magicfile] file...\n"
+#endif
+
+#ifndef MAGIC
+# define MAGIC "/etc/magic"
+#endif
+
+int /* Global command-line options */
+ debug = 0, /* debugging */
+ lflag = 0, /* follow Symlinks (BSD only) */
+ zflag = 0; /* follow (uncompress) compressed files */
+
+int /* Misc globals */
+ nmagic = 0; /* number of valid magic[]s */
+
+struct magic *magic; /* array of magic entries */
+
+char *magicfile = MAGIC;/* where magic be found */
+
+char *progname; /* used throughout */
+int lineno; /* line number in the magic file */
+
+
+static void unwrap __P((char *fn));
+
+/*
+ * main - parse arguments and handle options
+ */
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int check = 0, didsomefiles = 0, errflg = 0, ret = 0;
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ while ((c = getopt(argc, argv, "cdf:Lm:z")) != EOF)
+ switch (c) {
+ case 'c':
+ ++check;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ unwrap(optarg);
+ ++didsomefiles;
+ break;
+#ifdef S_IFLNK
+ case 'L':
+ ++lflag;
+ break;
+#endif
+ case 'm':
+ magicfile = optarg;
+ break;
+ case 'z':
+ zflag++;
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, USAGE, progname);
+ exit(2);
+ }
+
+ ret = apprentice(magicfile, check);
+ if (check)
+ exit(ret);
+
+ if (optind == argc) {
+ if (!didsomefiles) {
+ (void)fprintf(stderr, USAGE, progname);
+ exit(2);
+ }
+ }
+ else {
+ int i, wid, nw;
+ for (wid = 0, i = optind; i < argc; i++) {
+ nw = strlen(argv[i]);
+ if (nw > wid)
+ wid = nw;
+ }
+ for (; optind < argc; optind++)
+ process(argv[optind], wid);
+ }
+
+ return 0;
+}
+
+
+/*
+ * unwrap -- read a file of filenames, do each one.
+ */
+static void
+unwrap(fn)
+char *fn;
+{
+ char buf[MAXPATHLEN];
+ FILE *f;
+ int wid = 0, cwid;
+
+ if ((f = fopen(fn, "r")) == NULL) {
+ error("Cannot open `%s' (%s).\n", fn, strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ while (fgets(buf, MAXPATHLEN, f) != NULL) {
+ cwid = strlen(buf) - 1;
+ if (cwid > wid)
+ wid = cwid;
+ }
+
+ rewind(f);
+
+ while (fgets(buf, MAXPATHLEN, f) != NULL) {
+ buf[strlen(buf)-1] = '\0';
+ process(buf, wid);
+ }
+
+ (void) fclose(f);
+}
+
+
+/*
+ * process - process input file
+ */
+void
+process(inname, wid)
+const char *inname;
+int wid;
+{
+ int fd = 0;
+ static const char stdname[] = "standard input";
+ unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */
+ struct utimbuf utbuf;
+ struct stat sb;
+ int nbytes = 0; /* number of bytes read from a datafile */
+
+ if (strcmp("-", inname) == 0) {
+ if (fstat(0, &sb)<0) {
+ error("cannot fstat `%s' (%s).\n", stdname,
+ strerror(errno));
+ /*NOTREACHED*/
+ }
+ inname = stdname;
+ }
+
+ if (wid > 0)
+ (void) printf("%s:%*s ", inname,
+ (int) (wid - strlen(inname)), "");
+
+ if (inname != stdname) {
+ /*
+ * first try judging the file based on its filesystem status
+ */
+ if (fsmagic(inname, &sb) != 0) {
+ putchar('\n');
+ return;
+ }
+
+ if ((fd = open(inname, O_RDONLY)) < 0) {
+ /* We can't open it, but we were able to stat it. */
+ if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
+ if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
+ ckfprintf(stdout, "can't read `%s' (%s).\n",
+ inname, strerror(errno));
+ return;
+ }
+ }
+
+
+ /*
+ * try looking at the first HOWMANY bytes
+ */
+ if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
+ error("read failed (%s).\n", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (nbytes == 0)
+ ckfputs("empty", stdout);
+ else {
+ buf[nbytes++] = '\0'; /* null-terminate it */
+ tryit(buf, nbytes, zflag);
+ }
+
+ if (inname != stdname) {
+ /*
+ * Try to restore access, modification times if read it.
+ */
+ utbuf.actime = sb.st_atime;
+ utbuf.modtime = sb.st_mtime;
+ (void) utime(inname, &utbuf); /* don't care if loses */
+ (void) close(fd);
+ }
+ (void) putchar('\n');
+}
+
+
+void
+tryit(buf, nb, zflag)
+unsigned char *buf;
+int nb, zflag;
+{
+ /*
+ * Try compression stuff
+ */
+ if (!zflag || zmagic(buf, nb) != 1)
+ /*
+ * try tests in /etc/magic (or surrogate magic file)
+ */
+ if (softmagic(buf, nb) != 1)
+ /*
+ * try known keywords, check for ascii-ness too.
+ */
+ if (ascmagic(buf, nb) != 1)
+ /*
+ * abandon hope, all ye who remain here
+ */
+ ckfputs("data", stdout);
+}
diff --git a/usr.bin/file/file.h b/usr.bin/file/file.h
new file mode 100644
index 0000000..a92c9ff
--- /dev/null
+++ b/usr.bin/file/file.h
@@ -0,0 +1,128 @@
+/*
+ * file.h - definitions for file(1) program
+ * @(#)$Id: file.h,v 1.1.1.1 1994/09/03 19:16:23 csgr Exp $
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#define HOWMANY 8192 /* how much of the file to look at */
+#define MAXMAGIS 1000 /* max entries in /etc/magic */
+#define MAXDESC 50 /* max leng of text description */
+#define MAXstring 32 /* max leng of "string" types */
+
+struct magic {
+ short flag;
+#define INDIR 1 /* if '>(...)' appears, */
+#define UNSIGNED 2 /* comparison is unsigned */
+ short cont_level; /* level of ">" */
+ struct {
+ char type; /* byte short long */
+ long offset; /* offset from indirection */
+ } in;
+ long offset; /* offset to magic number */
+ unsigned char reln; /* relation (0=eq, '>'=gt, etc) */
+ char type; /* int, short, long or string. */
+ char vallen; /* length of string value, if any */
+#define BYTE 1
+#define SHORT 2
+#define LONG 4
+#define STRING 5
+#define DATE 6
+#define BESHORT 7
+#define BELONG 8
+#define BEDATE 9
+#define LESHORT 10
+#define LELONG 11
+#define LEDATE 12
+ union VALUETYPE {
+ unsigned char b;
+ unsigned short h;
+ unsigned long l;
+ char s[MAXstring];
+ unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */
+ unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */
+ } value; /* either number or string */
+ unsigned long mask; /* mask before comparison with value */
+ char nospflag; /* supress space character */
+ char desc[MAXDESC]; /* description */
+};
+
+#include <stdio.h> /* Include that here, to make sure __P gets defined */
+
+#ifndef __P
+# if __STDC__ || __cplusplus
+# define __P(a) a
+# else
+# define __P(a) ()
+# define const
+# endif
+#endif
+
+extern int apprentice __P((char *, int));
+extern int ascmagic __P((unsigned char *, int));
+extern void error __P((const char *, ...));
+extern void ckfputs __P((const char *, FILE *));
+struct stat;
+extern int fsmagic __P((const char *, struct stat *));
+extern int is_compress __P((const unsigned char *, int *));
+extern int is_tar __P((unsigned char *, int));
+extern void magwarn __P((const char *, ...));
+extern void mdump __P((struct magic *));
+extern void process __P((const char *, int));
+extern void showstr __P((FILE *, const char *, int));
+extern int softmagic __P((unsigned char *, int));
+extern void tryit __P((unsigned char *, int, int));
+extern int zmagic __P((unsigned char *, int));
+extern void ckfprintf __P((FILE *, const char *, ...));
+extern unsigned long signextend __P((struct magic *, unsigned long));
+
+
+
+extern int errno; /* Some unixes don't define this.. */
+
+extern char *progname; /* the program name */
+extern char *magicfile; /* name of the magic file */
+extern int lineno; /* current line number in magic file */
+
+extern struct magic *magic; /* array of magic entries */
+extern int nmagic; /* number of valid magic[]s */
+
+
+extern int debug; /* enable debugging? */
+extern int zflag; /* process compressed files? */
+extern int lflag; /* follow symbolic links? */
+
+extern int optind; /* From getopt(3) */
+extern char *optarg;
+
+#if !defined(__STDC__) || defined(sun) || defined(__sun__) || defined(__convex__)
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define strerror(e) \
+ (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 512
+#endif
diff --git a/usr.bin/file/fsmagic.c b/usr.bin/file/fsmagic.c
new file mode 100644
index 0000000..5a66d31
--- /dev/null
+++ b/usr.bin/file/fsmagic.c
@@ -0,0 +1,174 @@
+/*
+ * fsmagic - magic based on filesystem info - directory, special files, etc.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef major /* if `major' not defined in types.h, */
+#include <sys/sysmacros.h> /* try this one. */
+#endif
+#ifndef major /* still not defined? give up, manual intervention needed */
+ /* If cc tries to compile this, read and act on it. */
+ /* On most systems cpp will discard it automatically */
+ Congratulations, you have found a portability bug.
+ Please grep /usr/include/sys and edit the above #include
+ to point at the file that defines the "major" macro.
+#endif /*major*/
+
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: fsmagic.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $";
+#endif /* lint */
+
+int
+fsmagic(fn, sb)
+const char *fn;
+struct stat *sb;
+{
+ int ret = 0;
+
+ /*
+ * Fstat is cheaper but fails for files you don't have read perms on.
+ * On 4.2BSD and similar systems, use lstat() to identify symlinks.
+ */
+#ifdef S_IFLNK
+ if (!lflag)
+ ret = lstat(fn, sb);
+ else
+#endif
+ ret = stat(fn, sb); /* don't merge into if; see "ret =" above */
+
+ if (ret) {
+ ckfprintf(stdout,
+ /* Yes, I do mean stdout. */
+ /* No \n, caller will provide. */
+ "can't stat `%s' (%s).", fn, strerror(errno));
+ return 1;
+ }
+
+ if (sb->st_mode & S_ISUID) ckfputs("setuid ", stdout);
+ if (sb->st_mode & S_ISGID) ckfputs("setgid ", stdout);
+ if (sb->st_mode & S_ISVTX) ckfputs("sticky ", stdout);
+
+ switch (sb->st_mode & S_IFMT) {
+ case S_IFDIR:
+ ckfputs("directory", stdout);
+ return 1;
+ case S_IFCHR:
+ (void) printf("character special (%d/%d)",
+ major(sb->st_rdev), minor(sb->st_rdev));
+ return 1;
+ case S_IFBLK:
+ (void) printf("block special (%d/%d)",
+ major(sb->st_rdev), minor(sb->st_rdev));
+ return 1;
+ /* TODO add code to handle V7 MUX and Blit MUX files */
+#ifdef S_IFIFO
+ case S_IFIFO:
+ ckfputs("fifo (named pipe)", stdout);
+ return 1;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ {
+ char buf[BUFSIZ+4];
+ register int nch;
+ struct stat tstatbuf;
+
+ if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
+ ckfprintf(stdout, "unreadable symlink (%s).",
+ strerror(errno));
+ return 1;
+ }
+ buf[nch] = '\0'; /* readlink(2) forgets this */
+
+ /* If broken symlink, say so and quit early. */
+ if (*buf == '/') {
+ if (stat(buf, &tstatbuf) < 0) {
+ ckfprintf(stdout,
+ "broken symbolic link to %s", buf);
+ return 1;
+ }
+ }
+ else {
+ char *tmp;
+ char buf2[BUFSIZ+BUFSIZ+4];
+
+ if ((tmp = strrchr(fn, '/')) == NULL) {
+ tmp = buf; /* in current directory anyway */
+ }
+ else {
+ strcpy (buf2, fn); /* take directory part */
+ buf2[tmp-fn+1] = '\0';
+ strcat (buf2, buf); /* plus (relative) symlink */
+ tmp = buf2;
+ }
+ if (stat(tmp, &tstatbuf) < 0) {
+ ckfprintf(stdout,
+ "broken symbolic link to %s", buf);
+ return 1;
+ }
+ }
+
+ /* Otherwise, handle it. */
+ if (lflag) {
+ process(buf, strlen(buf));
+ return 1;
+ } else { /* just print what it points to */
+ ckfputs("symbolic link to ", stdout);
+ ckfputs(buf, stdout);
+ }
+ }
+ return 1;
+#endif
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ ckfputs("socket", stdout);
+ return 1;
+#endif
+ case S_IFREG:
+ break;
+ default:
+ error("invalid mode 0%o.\n", sb->st_mode);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * regular file, check next possibility
+ */
+ if (sb->st_size == 0) {
+ ckfputs("empty", stdout);
+ return 1;
+ }
+ return 0;
+}
+
diff --git a/usr.bin/file/is_tar.c b/usr.bin/file/is_tar.c
new file mode 100644
index 0000000..fc9cce2
--- /dev/null
+++ b/usr.bin/file/is_tar.c
@@ -0,0 +1,100 @@
+/*
+ * is_tar() -- figure out whether file is a tar archive.
+ *
+ * Stolen (by the author!) from the public domain tar program:
+ * Pubic Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
+ *
+ * @(#)list.c 1.18 9/23/86 Public Domain - gnu
+ * $Id: is_tar.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $
+ *
+ * Comments changed and some code/comments reformatted
+ * for file command by Ian Darwin.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "tar.h"
+
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
+#if defined(__STDC__) || defined(__cplusplus)
+static long from_oct(int, char*); /* Decode octal number */
+#else
+static long from_oct();
+#endif
+
+/*
+ * Return
+ * 0 if the checksum is bad (i.e., probably not a tar archive),
+ * 1 for old UNIX tar file,
+ * 2 for Unix Std (POSIX) tar file.
+ */
+int
+is_tar(buf, nbytes)
+unsigned char *buf;
+int nbytes;
+{
+ register union record *header = (union record *)buf;
+ register int i;
+ register long sum, recsum;
+ register char *p;
+
+ if (nbytes < sizeof(union record))
+ return 0;
+
+ recsum = from_oct(8, header->header.chksum);
+
+ sum = 0;
+ p = header->charptr;
+ for (i = sizeof(union record); --i >= 0;) {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ sum += 0xFF & *p++;
+ }
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = sizeof(header->header.chksum); --i >= 0;)
+ sum -= 0xFF & header->header.chksum[i];
+ sum += ' '* sizeof header->header.chksum;
+
+ if (sum != recsum)
+ return 0; /* Not a tar archive */
+
+ if (0==strcmp(header->header.magic, TMAGIC))
+ return 2; /* Unix Standard tar archive */
+
+ return 1; /* Old fashioned tar archive */
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or nonoctal).
+ */
+static long
+from_oct(digs, where)
+ register int digs;
+ register char *where;
+{
+ register long value;
+
+ while (isspace(*where)) { /* Skip spaces */
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where && !isspace(*where))
+ return -1; /* Ended on non-space/nul */
+
+ return value;
+}
diff --git a/usr.bin/file/magic.5 b/usr.bin/file/magic.5
new file mode 100644
index 0000000..6c6f3ef
--- /dev/null
+++ b/usr.bin/file/magic.5
@@ -0,0 +1,194 @@
+.TH MAGIC 5 "Public Domain"
+.\" install as magic.4 on USG, magic.5 on V7 or Berkeley systems.
+.SH NAME
+magic \- file command's magic number file
+.SH DESCRIPTION
+The
+.IR file (1)
+command identifies the type of a file using,
+among other tests,
+a test for whether the file begins with a certain
+.IR "magic number" .
+The file
+.B /etc/magic
+specifies what magic numbers are to be tested for,
+what message to print if a particular magic number is found,
+and additional information to extract from the file.
+.PP
+Each line of the file specifies a test to be performed.
+A test compares the data starting at a particular offset
+in the file with a 1-byte, 2-byte, or 4-byte numeric value or
+a string. If the test succeeds, a message is printed.
+The line consists of the following fields:
+.IP offset \w'message'u+2n
+A number specifying the offset, in bytes, into the file of the data
+which is to be tested.
+.IP type
+The type of the data to be tested. The possible values are:
+.RS
+.IP byte \w'message'u+2n
+A one-byte value.
+.IP short
+A two-byte value (on most systems) in this machine's native byte order.
+.IP long
+A four-byte value (on most systems) in this machine's native byte order.
+.IP string
+A string of bytes.
+.IP date
+A four-byte value interpreted as a unix date.
+.IP beshort
+A two-byte value (on most systems) in big-endian byte order.
+.IP belong
+A four-byte value (on most systems) in big-endian byte order.
+.IP bedate
+A four-byte value (on most systems) in big-endian byte order,
+interpreted as a unix date.
+.IP leshort
+A two-byte value (on most systems) in little-endian byte order.
+.IP lelong
+A four-byte value (on most systems) in little-endian byte order.
+.IP ledate
+A four-byte value (on most systems) in little-endian byte order,
+interpreted as a unix date.
+.RE
+.PP
+The numeric types may optionally be followed by
+.B &
+and a numeric value,
+to specify that the value is to be AND'ed with the
+numeric value before any comparisons are done. Prepending a
+.B u
+to the type indicates that ordered comparisons should be unsigned.
+.IP test
+The value to be compared with the value from the file. If the type is
+numeric, this value
+is specified in C form; if it is a string, it is specified as a C string
+with the usual escapes permitted (e.g. \en for new-line).
+.IP
+Numeric values
+may be preceded by a character indicating the operation to be performed.
+It may be
+.BR = ,
+to specify that the value from the file must equal the specified value,
+.BR < ,
+to specify that the value from the file must be less than the specified
+value,
+.BR > ,
+to specify that the value from the file must be greater than the specified
+value,
+.BR & ,
+to specify that the value from the file must have set all of the bits
+that are set in the specified value,
+or
+.BR ^ ,
+to specify that the value from the file must have clear any of the bits
+that are set in the specified value.
+.IP
+Numeric values are specified in C form; e.g.
+.B 13
+is decimal,
+.B 013
+is octal, and
+.B 0x13
+is hexadecimal.
+to specify that any value will match. If the character
+is omitted, it is assumed to be
+.BR = .
+.IP
+For string values, the byte string from the
+file must match the specified byte string.
+The operators
+.BR = ,
+.B <
+and
+.B >
+(but not
+.BR & )
+can be applied to strings.
+The length used for matching is that of the string argument
+in the magic file. This means that a line can match any string, and
+then presumably print that string, by doing
+.B >\e0
+(because all strings are greater than the null string).
+.IP message
+The message to be printed if the comparison succeeds. If the string
+contains a
+.IR printf (3S)
+format specification, the value from the file (with any specified masking
+performed) is printed using the message as the format string.
+.PP
+Some file formats contain additional information which is to be printed
+along with the file type. A line which begins with the character
+.B >
+indicates additional tests and messages to be printed. The number of
+.B >
+on the line indicates the level of the test; a line with no
+.B >
+at the beginning is considered to be at level 0.
+Each line at level
+.IB n \(pl1
+is under the control of the line at level
+.IB n
+most closely preceding it in the magic file.
+If the test on a line at level
+.I n
+succeeds, the tests specified in all the subsequent lines at level
+.IB n \(pl1
+are performed, and the messages printed if the tests succeed. The next
+line at level
+.I n
+terminates this.
+If the first character following the last
+.B >
+is a
+.B (
+then the string after the parenthesis is interpreted as an indirect offset.
+That means that the number after the parenthesis is used as a offset in
+the file. The value at that offset is read, and is used again as an offset
+in the file. Indirect offsets are of the form:
+.BI (( x [.[bsl]][+-][ y ]).
+The value of
+.I x
+is used as an offset in the file. A byte, short or long is read at that offset
+depending on the
+.B [bsl]
+type specifier. To that number the value of
+.I y
+is added and the result is used as an offset in the file. The default type
+if one is not specified is long.
+.SH BUGS
+The formats
+.IR long ,
+.IR belong ,
+.IR lelong ,
+.IR short ,
+.IR beshort ,
+.IR leshort ,
+.IR date ,
+.IR bedate ,
+and
+.I ledate
+are system-dependant; perhaps they should be specified as a number
+of bytes (2B, 4B, etc),
+since the files being recognized typically come from
+a system on which the lengths are invariant.
+.PP
+There is (currently) no support for specified-endian data to be used in
+indirect offsets.
+.SH SEE ALSO
+.IR file (1)
+\- the command that reads this file.
+.\"
+.\" From: guy@sun.uucp (Guy Harris)
+.\" Newsgroups: net.bugs.usg
+.\" Subject: /etc/magic's format isn't well documented
+.\" Message-ID: <2752@sun.uucp>
+.\" Date: 3 Sep 85 08:19:07 GMT
+.\" Organization: Sun Microsystems, Inc.
+.\" Lines: 136
+.\"
+.\" Here's a manual page for the format accepted by the "file" made by adding
+.\" the changes I posted to the S5R2 version.
+.\"
+.\" Modified for Ian Darwin's version of the file command.
+.\" @(#)$Id: magic.man,v 1.11 1994/05/03 17:58:23 christos Exp $
diff --git a/usr.bin/file/names.h b/usr.bin/file/names.h
new file mode 100644
index 0000000..5d989ee
--- /dev/null
+++ b/usr.bin/file/names.h
@@ -0,0 +1,90 @@
+/*
+ * Names.h - names and types used by ascmagic in file(1).
+ * These tokens are here because they can appear anywhere in
+ * the first HOWMANY bytes, while tokens in /etc/magic must
+ * appear at fixed offsets into the file. Don't make HOWMANY
+ * too high unless you have a very fast CPU.
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * See LEGAL.NOTICE
+ *
+ * $Id: names.h,v 1.11 1993/09/16 21:14:20 christos Exp $
+ */
+
+/* these types are used to index the table 'types': keep em in sync! */
+#define L_C 0 /* first and foremost on UNIX */
+#define L_FORT 1 /* the oldest one */
+#define L_MAKE 2 /* Makefiles */
+#define L_PLI 3 /* PL/1 */
+#define L_MACH 4 /* some kinda assembler */
+#define L_ENG 5 /* English */
+#define L_PAS 6 /* Pascal */
+#define L_MAIL 7 /* Electronic mail */
+#define L_NEWS 8 /* Usenet Netnews */
+
+static char *types[] = {
+ "c program text",
+ "fortran program text",
+ "make commands text" ,
+ "pl/1 program text",
+ "assembler program text",
+ "English text",
+ "pascal program text",
+ "mail text",
+ "news text",
+ "can't happen error on names.h/types",
+ 0};
+
+static struct names {
+ char *name;
+ short type;
+} names[] = {
+ /* These must be sorted by eye for optimal hit rate */
+ /* Add to this list only after substantial meditation */
+ {"/*", L_C}, /* must preced "The", "the", etc. */
+ {"#include", L_C},
+ {"char", L_C},
+ {"The", L_ENG},
+ {"the", L_ENG},
+ {"double", L_C},
+ {"extern", L_C},
+ {"float", L_C},
+ {"real", L_C},
+ {"struct", L_C},
+ {"union", L_C},
+ {"CFLAGS", L_MAKE},
+ {"LDFLAGS", L_MAKE},
+ {"all:", L_MAKE},
+ {".PRECIOUS", L_MAKE},
+/* Too many files of text have these words in them. Find another way
+ * to recognize Fortrash.
+ */
+#ifdef NOTDEF
+ {"subroutine", L_FORT},
+ {"function", L_FORT},
+ {"block", L_FORT},
+ {"common", L_FORT},
+ {"dimension", L_FORT},
+ {"integer", L_FORT},
+ {"data", L_FORT},
+#endif /*NOTDEF*/
+ {".ascii", L_MACH},
+ {".asciiz", L_MACH},
+ {".byte", L_MACH},
+ {".even", L_MACH},
+ {".globl", L_MACH},
+ {"clr", L_MACH},
+ {"(input,", L_PAS},
+ {"dcl", L_PLI},
+ {"Received:", L_MAIL},
+ {">From", L_MAIL},
+ {"Return-Path:",L_MAIL},
+ {"Cc:", L_MAIL},
+ {"Newsgroups:", L_NEWS},
+ {"Path:", L_NEWS},
+ {"Organization:",L_NEWS},
+ {NULL, 0}
+};
+#define NNAMES ((sizeof(names)/sizeof(struct names)) - 1)
diff --git a/usr.bin/file/patchlevel.h b/usr.bin/file/patchlevel.h
new file mode 100644
index 0000000..b33a113
--- /dev/null
+++ b/usr.bin/file/patchlevel.h
@@ -0,0 +1,63 @@
+#define FILE_VERSION_MAJOR 3
+#define patchlevel 14
+
+/*
+ * Patchlevel file for Ian Darwin's MAGIC command.
+ * $Id: patchlevel.h,v 1.1.1.1 1994/09/03 19:16:23 csgr Exp $
+ *
+ * $Log: patchlevel.h,v $
+ * Revision 1.1.1.1 1994/09/03 19:16:23 csgr
+ * Bring in file 3.14 by Ian Darwin (and Christos Zoulas)
+ *
+ * The following files were moved to different names:
+ * - file.man -> file.1
+ * - magic.man -> magic.5
+ *
+ * The following file was removed:
+ * - Magdir/Makefile
+ *
+ * Revision 1.14 1994/05/03 17:58:23 christos
+ * changes from mycroft@gnu.ai.mit.edu (Charles Hannum) for unsigned
+ *
+ * Revision 1.13 1994/01/21 01:27:01 christos
+ * Fixed null termination bug from Don Seeley at BSDI in ascmagic.c
+ *
+ * Revision 1.12 1993/10/27 20:59:05 christos
+ * Changed -z flag to understand gzip format too.
+ * Moved builtin compression detection to a table, and move
+ * the compress magic entry out of the source.
+ * Made printing of numbers unsigned, and added the mask to it.
+ * Changed the buffer size to 8k, because gzip will refuse to
+ * unzip just a few bytes.
+ *
+ * Revision 1.11 1993/09/24 18:49:06 christos
+ * Fixed small bug in softmagic.c introduced by
+ * copying the data to be examined out of the input
+ * buffer. Changed the Makefile to use sed to create
+ * the correct man pages.
+ *
+ * Revision 1.10 1993/09/23 21:56:23 christos
+ * Passed purify. Fixed indirections. Fixed byte order printing.
+ * Fixed segmentation faults caused by referencing past the end
+ * of the magic buffer. Fixed bus errors caused by referencing
+ * unaligned shorts or longs.
+ *
+ * Revision 1.9 1993/03/24 14:23:40 ian
+ * Batch of minor changes from several contributors.
+ *
+ * Revision 1.8 93/02/19 15:01:26 ian
+ * Numerous changes from Guy Harris too numerous to mention but including
+ * byte-order independance, fixing "old-style masking", etc. etc. A bugfix
+ * for broken symlinks from martin@@d255s004.zfe.siemens.de.
+ *
+ * Revision 1.7 93/01/05 14:57:27 ian
+ * Couple of nits picked by Christos (again, thanks).
+ *
+ * Revision 1.6 93/01/05 13:51:09 ian
+ * Lotsa work on the Magic directory.
+ *
+ * Revision 1.5 92/09/14 14:54:51 ian
+ * Fix a tiny null-pointer bug in previous fix for tar archive + uncompress.
+ *
+ */
+
diff --git a/usr.bin/file/print.c b/usr.bin/file/print.c
new file mode 100644
index 0000000..44a079e
--- /dev/null
+++ b/usr.bin/file/print.c
@@ -0,0 +1,204 @@
+/*
+ * print.c - debugging printout routines
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#if __STDC__
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: print.c,v 1.1.1.1 1994/09/03 19:16:22 csgr Exp $";
+#endif /* lint */
+
+#define SZOF(a) (sizeof(a) / sizeof(a[0]))
+
+void
+mdump(m)
+struct magic *m;
+{
+ static char *typ[] = { "invalid", "byte", "short", "invalid",
+ "long", "string", "date", "beshort",
+ "belong", "bedate", "leshort", "lelong",
+ "ledate" };
+ (void) fputc('[', stderr);
+ (void) fprintf(stderr, ">>>>>>>> %d" + 8 - (m->cont_level & 7),
+ m->offset);
+
+ if (m->flag & INDIR)
+ (void) fprintf(stderr, "(%s,%d),",
+ (m->in.type >= 0 && m->in.type < SZOF(typ)) ?
+ typ[(unsigned char) m->in.type] :
+ "*bad*",
+ m->in.offset);
+
+ (void) fprintf(stderr, " %s%s", (m->flag & UNSIGNED) ? "u" : "",
+ (m->type >= 0 && m->type < SZOF(typ)) ?
+ typ[(unsigned char) m->type] :
+ "*bad*");
+ if (m->mask != ~0L)
+ (void) fprintf(stderr, " & %.8x", m->mask);
+
+ (void) fprintf(stderr, ",%c", m->reln);
+
+ if (m->reln != 'x') {
+ switch (m->type) {
+ case BYTE:
+ case SHORT:
+ case LONG:
+ case LESHORT:
+ case LELONG:
+ case BESHORT:
+ case BELONG:
+ (void) fprintf(stderr, "%d", m->value.l);
+ break;
+ case STRING:
+ showstr(stderr, m->value.s, -1);
+ break;
+ case DATE:
+ case LEDATE:
+ case BEDATE:
+ {
+ char *rt, *pp = ctime((time_t*) &m->value.l);
+ if ((rt = strchr(pp, '\n')) != NULL)
+ *rt = '\0';
+ (void) fprintf(stderr, "%s,", pp);
+ if (rt)
+ *rt = '\n';
+ }
+ break;
+ default:
+ (void) fputs("*bad*", stderr);
+ break;
+ }
+ }
+ (void) fprintf(stderr, ",\"%s\"]\n", m->desc);
+}
+
+/*
+ * ckfputs - futs, but with error checking
+ * ckfprintf - fprintf, but with error checking
+ */
+void
+ckfputs(str, fil)
+ const char *str;
+ FILE *fil;
+{
+ if (fputs(str,fil) == EOF)
+ error("write failed.\n");
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+ckfprintf(FILE *f, const char *fmt, ...)
+#else
+ckfprintf(va_alist)
+ va_dcl
+#endif
+{
+ va_list va;
+#if __STDC__
+ va_start(va, fmt);
+#else
+ FILE *f;
+ const char *fmt;
+ va_start(va);
+ f = va_arg(va, FILE *);
+ fmt = va_arg(va, const char *);
+#endif
+ (void) vfprintf(f, fmt, va);
+ if (ferror(f))
+ error("write failed.\n");
+ va_end(va);
+}
+
+/*
+ * error - print best error message possible and exit
+ */
+/*VARARGS*/
+void
+#if __STDC__
+error(const char *f, ...)
+#else
+error(va_alist)
+ va_dcl
+#endif
+{
+ va_list va;
+#if __STDC__
+ va_start(va, f);
+#else
+ const char *f;
+ va_start(va);
+ f = va_arg(va, const char *);
+#endif
+ /* cuz we use stdout for most, stderr here */
+ (void) fflush(stdout);
+
+ if (progname != NULL)
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) vfprintf(stderr, f, va);
+ va_end(va);
+ exit(1);
+}
+
+/*VARARGS*/
+void
+#if __STDC__
+magwarn(const char *f, ...)
+#else
+magwarn(va_alist)
+ va_dcl
+#endif
+{
+ va_list va;
+#if __STDC__
+ va_start(va, f);
+#else
+ const char *f;
+ va_start(va);
+ f = va_arg(va, const char *);
+#endif
+ /* cuz we use stdout for most, stderr here */
+ (void) fflush(stdout);
+
+ if (progname != NULL)
+ (void) fprintf(stderr, "%s: %s, %d: ",
+ progname, magicfile, lineno);
+ (void) vfprintf(stderr, f, va);
+ va_end(va);
+ fputc('\n', stderr);
+}
diff --git a/usr.bin/file/softmagic.c b/usr.bin/file/softmagic.c
new file mode 100644
index 0000000..737e6d6
--- /dev/null
+++ b/usr.bin/file/softmagic.c
@@ -0,0 +1,463 @@
+/*
+ * softmagic - interpret variable magic from /etc/magic
+ *
+ * Copyright (c) Ian F. Darwin, 1987.
+ * Written by Ian F. Darwin.
+ *
+ * This software is not subject to any license of the American Telephone
+ * and Telegraph Company or of the Regents of the University of California.
+ *
+ * Permission is granted to anyone to use this software for any purpose on
+ * any computer system, and to alter it and redistribute it freely, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits must appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits must appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include "file.h"
+
+#ifndef lint
+static char *moduleid =
+ "@(#)$Id: softmagic.c,v 1.2 1995/05/24 02:54:30 ache Exp $";
+#endif /* lint */
+
+static int match __P((unsigned char *, int));
+static int mget __P((union VALUETYPE *,
+ unsigned char *, struct magic *, int));
+static int mcheck __P((union VALUETYPE *, struct magic *));
+static void mprint __P((union VALUETYPE *, struct magic *));
+static void mdebug __P((long, char *, int));
+static int mconvert __P((union VALUETYPE *, struct magic *));
+
+/*
+ * softmagic - lookup one file in database
+ * (already read from /etc/magic by apprentice.c).
+ * Passed the name and FILE * of one file to be typed.
+ */
+/*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */
+int
+softmagic(buf, nbytes)
+unsigned char *buf;
+int nbytes;
+{
+ if (match(buf, nbytes))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Go through the whole list, stopping if you find a match. Process all
+ * the continuations of that match before returning.
+ *
+ * We support multi-level continuations:
+ *
+ * At any time when processing a successful top-level match, there is a
+ * current continuation level; it represents the level of the last
+ * successfully matched continuation.
+ *
+ * Continuations above that level are skipped as, if we see one, it
+ * means that the continuation that controls them - i.e, the
+ * lower-level continuation preceding them - failed to match.
+ *
+ * Continuations below that level are processed as, if we see one,
+ * it means we've finished processing or skipping higher-level
+ * continuations under the control of a successful or unsuccessful
+ * lower-level continuation, and are now seeing the next lower-level
+ * continuation and should process it. The current continuation
+ * level reverts to the level of the one we're seeing.
+ *
+ * Continuations at the current level are processed as, if we see
+ * one, there's no lower-level continuation that may have failed.
+ *
+ * If a continuation matches, we bump the current continuation level
+ * so that higher-level continuations are processed.
+ */
+static int
+match(s, nbytes)
+unsigned char *s;
+int nbytes;
+{
+ int magindex = 0;
+ int cont_level = 0;
+ int need_separator = 0;
+ union VALUETYPE p;
+
+ for (magindex = 0; magindex < nmagic; magindex++) {
+ /* if main entry matches, print it... */
+ if (!mget(&p, s, &magic[magindex], nbytes) ||
+ !mcheck(&p, &magic[magindex])) {
+ /*
+ * main entry didn't match,
+ * flush its continuations
+ */
+ while (magindex < nmagic &&
+ magic[magindex + 1].cont_level != 0)
+ magindex++;
+ continue;
+ }
+
+ mprint(&p, &magic[magindex]);
+ /*
+ * If we printed something, we'll need to print
+ * a blank before we print something else.
+ */
+ if (magic[magindex].desc[0])
+ need_separator = 1;
+ /* and any continuations that match */
+ cont_level++;
+ while (magic[magindex+1].cont_level != 0 &&
+ ++magindex < nmagic) {
+ if (cont_level >= magic[magindex].cont_level) {
+ if (cont_level > magic[magindex].cont_level) {
+ /*
+ * We're at the end of the level
+ * "cont_level" continuations.
+ */
+ cont_level = magic[magindex].cont_level;
+ }
+ if (mget(&p, s, &magic[magindex], nbytes) &&
+ mcheck(&p, &magic[magindex])) {
+ /*
+ * This continuation matched.
+ * Print its message, with
+ * a blank before it if
+ * the previous item printed
+ * and this item isn't empty.
+ */
+ /* space if previous printed */
+ if (need_separator
+ && (magic[magindex].nospflag == 0)
+ && (magic[magindex].desc[0] != '\0')
+ ) {
+ (void) putchar(' ');
+ need_separator = 0;
+ }
+ mprint(&p, &magic[magindex]);
+ if (magic[magindex].desc[0])
+ need_separator = 1;
+
+ /*
+ * If we see any continuations
+ * at a higher level,
+ * process them.
+ */
+ cont_level++;
+ }
+ }
+ }
+ return 1; /* all through */
+ }
+ return 0; /* no match at all */
+}
+
+static void
+mprint(p, m)
+union VALUETYPE *p;
+struct magic *m;
+{
+ char *pp, *rt;
+ unsigned long v;
+
+
+ switch (m->type) {
+ case BYTE:
+ v = p->b;
+ break;
+
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = p->h;
+ break;
+
+ case LONG:
+ case BELONG:
+ case LELONG:
+ v = p->l;
+ break;
+
+ case STRING:
+ if (m->reln == '=') {
+ (void) printf(m->desc, m->value.s);
+ }
+ else {
+ (void) printf(m->desc, p->s);
+ }
+ return;
+
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ pp = ctime((time_t*) &p->l);
+ if ((rt = strchr(pp, '\n')) != NULL)
+ *rt = '\0';
+ (void) printf(m->desc, pp);
+ return;
+ default:
+ error("invalid m->type (%d) in mprint().\n", m->type);
+ /*NOTREACHED*/
+ }
+
+ v = signextend(m, v) & m->mask;
+ (void) printf(m->desc, v);
+}
+
+/*
+ * Convert the byte order of the data we are looking at
+ */
+static int
+mconvert(p, m)
+union VALUETYPE *p;
+struct magic *m;
+{
+ char *rt;
+
+ switch (m->type) {
+ case BYTE:
+ case SHORT:
+ case LONG:
+ case DATE:
+ return 1;
+ case STRING:
+ /* Null terminate and eat the return */
+ p->s[sizeof(p->s) - 1] = '\0';
+ if ((rt = strchr(p->s, '\n')) != NULL)
+ *rt = '\0';
+ return 1;
+ case BESHORT:
+ p->h = (short)((p->hs[0]<<8)|(p->hs[1]));
+ return 1;
+ case BELONG:
+ case BEDATE:
+ p->l = (long)
+ ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3]));
+ return 1;
+ case LESHORT:
+ p->h = (short)((p->hs[1]<<8)|(p->hs[0]));
+ return 1;
+ case LELONG:
+ case LEDATE:
+ p->l = (long)
+ ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0]));
+ return 1;
+ default:
+ error("invalid type %d in mconvert().\n", m->type);
+ return 0;
+ }
+}
+
+
+static void
+mdebug(offset, str, len)
+long offset;
+char *str;
+int len;
+{
+ (void) fprintf(stderr, "mget @%d: ", offset);
+ showstr(stderr, (char *) str, len);
+ (void) fputc('\n', stderr);
+ (void) fputc('\n', stderr);
+}
+
+static int
+mget(p, s, m, nbytes)
+union VALUETYPE* p;
+unsigned char *s;
+struct magic *m;
+int nbytes;
+{
+ long offset = m->offset;
+ if (offset + sizeof(union VALUETYPE) > nbytes)
+ return 0;
+
+
+ memcpy(p, s + offset, sizeof(union VALUETYPE));
+
+ if (debug) {
+ mdebug(offset, (char *) p, sizeof(union VALUETYPE));
+ mdump(m);
+ }
+
+ if (!mconvert(p, m))
+ return 0;
+
+ if (m->flag & INDIR) {
+
+ switch (m->in.type) {
+ case BYTE:
+ offset = p->b + m->in.offset;
+ break;
+ case SHORT:
+ offset = p->h + m->in.offset;
+ break;
+ case LONG:
+ offset = p->l + m->in.offset;
+ break;
+ }
+
+ if (offset + sizeof(union VALUETYPE) > nbytes)
+ return 0;
+
+ memcpy(p, s + offset, sizeof(union VALUETYPE));
+
+ if (debug) {
+ mdebug(offset, (char *) p, sizeof(union VALUETYPE));
+ mdump(m);
+ }
+
+ if (!mconvert(p, m))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+mcheck(p, m)
+union VALUETYPE* p;
+struct magic *m;
+{
+ register unsigned long l = m->value.l;
+ register unsigned long v;
+ int matched;
+
+ if ( (m->value.s[0] == 'x') && (m->value.s[1] == '\0') ) {
+ fprintf(stderr, "BOINK");
+ return 1;
+ }
+
+
+ switch (m->type) {
+ case BYTE:
+ v = p->b;
+ break;
+
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = p->h;
+ break;
+
+ case LONG:
+ case BELONG:
+ case LELONG:
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ v = p->l;
+ break;
+
+ case STRING:
+ l = 0;
+ /* What we want here is:
+ * v = strncmp(m->value.s, p->s, m->vallen);
+ * but ignoring any nulls. bcmp doesn't give -/+/0
+ * and isn't universally available anyway.
+ */
+ v = 0;
+ {
+ register unsigned char *a = (unsigned char*)m->value.s;
+ register unsigned char *b = (unsigned char*)p->s;
+ register int len = m->vallen;
+
+ while (--len >= 0)
+ if ((v = *b++ - *a++) != 0)
+ break;
+ }
+ break;
+ default:
+ error("invalid type %d in mcheck().\n", m->type);
+ return 0;/*NOTREACHED*/
+ }
+
+ v = signextend(m, v) & m->mask;
+
+ switch (m->reln) {
+ case 'x':
+ if (debug)
+ (void) fprintf(stderr, "%lu == *any* = 1\n", v);
+ matched = 1;
+ break;
+
+ case '!':
+ matched = v != l;
+ if (debug)
+ (void) fprintf(stderr, "%lu != %lu = %d\n",
+ v, l, matched);
+ break;
+
+ case '=':
+ matched = v == l;
+ if (debug)
+ (void) fprintf(stderr, "%lu == %lu = %d\n",
+ v, l, matched);
+ break;
+
+ case '>':
+ if (m->flag & UNSIGNED) {
+ matched = v > l;
+ if (debug)
+ (void) fprintf(stderr, "%lu > %lu = %d\n",
+ v, l, matched);
+ }
+ else {
+ matched = (long) v > (long) l;
+ if (debug)
+ (void) fprintf(stderr, "%ld > %ld = %d\n",
+ v, l, matched);
+ }
+ break;
+
+ case '<':
+ if (m->flag & UNSIGNED) {
+ matched = v < l;
+ if (debug)
+ (void) fprintf(stderr, "%lu < %lu = %d\n",
+ v, l, matched);
+ }
+ else {
+ matched = (long) v < (long) l;
+ if (debug)
+ (void) fprintf(stderr, "%ld < %ld = %d\n",
+ v, l, matched);
+ }
+ break;
+
+ case '&':
+ matched = (v & l) == l;
+ if (debug)
+ (void) fprintf(stderr, "((%lx & %lx) == %lx) = %d\n",
+ v, l, l, matched);
+ break;
+
+ case '^':
+ matched = (v & l) != l;
+ if (debug)
+ (void) fprintf(stderr, "((%lx & %lx) != %lx) = %d\n",
+ v, l, l, matched);
+ break;
+
+ default:
+ matched = 0;
+ error("mcheck: can't happen: invalid relation %d.\n", m->reln);
+ break;/*NOTREACHED*/
+ }
+
+ return matched;
+}
diff --git a/usr.bin/file/tar.h b/usr.bin/file/tar.h
new file mode 100644
index 0000000..fd6f0c9
--- /dev/null
+++ b/usr.bin/file/tar.h
@@ -0,0 +1,179 @@
+/*
+ * Header file for public domain tar (tape archive) program.
+ *
+ * @(#)tar.h 1.20 86/10/29 Public Domain.
+ *
+ * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu.
+ *
+ * $Id: tar.h,v 1.3 1992/09/08 15:32:41 ian Exp $ # checkin only
+ */
+
+/*
+ * Kludge for handling systems that can't cope with multiple
+ * external definitions of a variable. In ONE routine (tar.c),
+ * we #define TAR_EXTERN to null; here, we set it to "extern" if
+ * it is not already set.
+ */
+#ifndef TAR_EXTERN
+#define TAR_EXTERN extern
+#endif
+
+/*
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here.
+ * A "block" is a big chunk of stuff that we do I/O on.
+ * A "record" is a piece of info that we care about.
+ * Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+
+union record {
+ char charptr[RECORDSIZE];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ } header;
+};
+
+/* The checksum field is filled with this while the checksum is computed. */
+#define CHKBLANKS " " /* 8 blanks, no null */
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar " /* 7 chars and a null */
+
+/* The linkflag defines the type of file */
+#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
+#define LF_NORMAL '0' /* Normal disk file */
+#define LF_LINK '1' /* Link to previously dumped file */
+#define LF_SYMLINK '2' /* Symbolic link */
+#define LF_CHR '3' /* Character special file */
+#define LF_BLK '4' /* Block special file */
+#define LF_DIR '5' /* Directory */
+#define LF_FIFO '6' /* FIFO special file */
+#define LF_CONTIG '7' /* Contiguous file */
+/* Further link types may be defined later. */
+
+/*
+ * Exit codes from the "tar" program
+ */
+#define EX_SUCCESS 0 /* success! */
+#define EX_ARGSBAD 1 /* invalid args */
+#define EX_BADFILE 2 /* invalid filename */
+#define EX_BADARCH 3 /* bad archive */
+#define EX_SYSTEM 4 /* system gave unexpected error */
+
+
+/*
+ * Global variables
+ */
+TAR_EXTERN union record *ar_block; /* Start of block of archive */
+TAR_EXTERN union record *ar_record; /* Current record of archive */
+TAR_EXTERN union record *ar_last; /* Last+1 record of archive block */
+TAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */
+TAR_EXTERN int blocking; /* Size of each block, in records */
+TAR_EXTERN int blocksize; /* Size of each block, in bytes */
+TAR_EXTERN char *ar_file; /* File containing archive */
+TAR_EXTERN char *name_file; /* File containing names to work on */
+TAR_EXTERN char *tar; /* Name of this program */
+
+/*
+ * Flags from the command line
+ */
+TAR_EXTERN char f_reblock; /* -B */
+TAR_EXTERN char f_create; /* -c */
+TAR_EXTERN char f_debug; /* -d */
+TAR_EXTERN char f_sayblock; /* -D */
+TAR_EXTERN char f_follow_links; /* -h */
+TAR_EXTERN char f_ignorez; /* -i */
+TAR_EXTERN char f_keep; /* -k */
+TAR_EXTERN char f_modified; /* -m */
+TAR_EXTERN char f_oldarch; /* -o */
+TAR_EXTERN char f_use_protection; /* -p */
+TAR_EXTERN char f_sorted_names; /* -s */
+TAR_EXTERN char f_list; /* -t */
+TAR_EXTERN char f_namefile; /* -T */
+TAR_EXTERN char f_verbose; /* -v */
+TAR_EXTERN char f_extract; /* -x */
+TAR_EXTERN char f_compress; /* -z */
+
+/*
+ * We now default to Unix Standard format rather than 4.2BSD tar format.
+ * The code can actually produce all three:
+ * f_standard ANSI standard
+ * f_oldarch V7
+ * neither 4.2BSD
+ * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
+ * The only advantage to the "neither" option is that we can cmp(1) our
+ * output to the output of 4.2BSD tar, for debugging.
+ */
+#define f_standard (!f_oldarch)
+
+/*
+ * Structure for keeping track of filenames and lists thereof.
+ */
+struct name {
+ struct name *next;
+ short length;
+ char found;
+ char name[NAMSIZ+1];
+};
+
+TAR_EXTERN struct name *namelist; /* Points to first name in list */
+TAR_EXTERN struct name *namelast; /* Points to last name in list */
+
+TAR_EXTERN int archive; /* File descriptor for archive file */
+TAR_EXTERN int errors; /* # of files in error */
+
+/*
+ *
+ * Due to the next struct declaration, each routine that includes
+ * "tar.h" must also include <sys/types.h>. I tried to make it automatic,
+ * but System V has no defines in <sys/types.h>, so there is no way of
+ * knowing when it has been included. In addition, it cannot be included
+ * twice, but must be included exactly once. Argghh!
+ *
+ * Thanks, typedef. Thanks, USG.
+ */
+struct link {
+ struct link *next;
+ dev_t dev;
+ ino_t ino;
+ short linkcount;
+ char name[NAMSIZ+1];
+};
+
+TAR_EXTERN struct link *linklist; /* Points to first link in list */
+
+
+/*
+ * Error recovery stuff
+ */
+TAR_EXTERN char read_error_flag;
+
+
+/*
+ * Declarations of functions available to the world.
+ */
+/*LINTLIBRARY*/
+union record *findrec();
+void userec();
+union record *endofrecs();
+void anno();
+#define annorec(stream, msg) anno(stream, msg, 0) /* Cur rec */
+#define annofile(stream, msg) anno(stream, msg, 1) /* Saved rec */
diff --git a/usr.bin/file2c/Makefile b/usr.bin/file2c/Makefile
new file mode 100644
index 0000000..f19c125
--- /dev/null
+++ b/usr.bin/file2c/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.3 1994/09/24 02:55:45 davidg Exp $
+
+PROG= file2c
+MAN1= file2c.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/file2c/file2c.1 b/usr.bin/file2c/file2c.1
new file mode 100644
index 0000000..c2dda08
--- /dev/null
+++ b/usr.bin/file2c/file2c.1
@@ -0,0 +1,48 @@
+.\"----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@freebsd.org> wrote this file. As long as you retain this notice, you
+.\" can do whatever you want with this file. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+.\" ---------------------------------------------------------------------------
+.\"
+.\" $Id$
+.\"
+.Dd Jan 28, 1995
+.Dt FILE2C 1
+.Os
+.Sh NAME
+.Nm file2c
+.Nd convert file to c-source.
+.Sh SYNOPSIS
+.Nm file2c
+.Op "string"
+.Op "string"
+.Sh DESCRIPTION
+The
+.Nm file2c
+utility reads a file from stdin and writes it to stdout, converting each
+byte to its decimal representation on the fly.
+.Pp
+If the first
+.Op string
+is present, it is printed before the data, if the second
+.Op string
+is present, it is printed after the data.
+.Pp
+This program is used to embedd binary or other files into C source files,
+for instance as a char[].
+.Sh EXAMPLE
+The command:
+.Bd -literal -offset indent
+date | file2c 'const char date[] = {' ',0};'
+.Ed
+.Pp
+will produce:
+.Bd -literal -offset indent
+const char date[] = {
+83,97,116,32,74,97,110,32,50,56,32,49,54,58,50,56,58,48,53,
+32,80,83,84,32,49,57,57,53,10
+,0};
+.Ed
+
+
diff --git a/usr.bin/file2c/file2c.c b/usr.bin/file2c/file2c.c
new file mode 100644
index 0000000..0bad904
--- /dev/null
+++ b/usr.bin/file2c/file2c.c
@@ -0,0 +1,46 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ int i,j,k;
+ char s[10];
+
+ if (argc > 1)
+ printf("%s\n",argv[1]);
+ k = 0;
+ j = 0;
+ while((i = getchar()) != EOF) {
+ if(k++) {
+ putchar(',');
+ j++;
+ }
+ if (j > 70) {
+ putchar('\n');
+ j = 0;
+ }
+ printf("%d",i);
+ if (i > 99)
+ j += 3;
+ else if (i > 9)
+ j += 2;
+ else
+ j++;
+ }
+ putchar('\n');
+ if (argc > 2)
+ printf("%s\n",argv[2]);
+ return 0;
+}
diff --git a/usr.bin/find/Makefile b/usr.bin/find/Makefile
new file mode 100644
index 0000000..c305d6a
--- /dev/null
+++ b/usr.bin/find/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= find
+SRCS= find.c function.c ls.c main.c misc.c operator.c option.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h
new file mode 100644
index 0000000..c9aba6f
--- /dev/null
+++ b/usr.bin/find/extern.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 4/16/94
+ */
+
+#include <sys/cdefs.h>
+
+void brace_subst __P((char *, char **, char *, int));
+void *emalloc __P((unsigned int));
+PLAN *find_create __P((char ***));
+int find_execute __P((PLAN *, char **));
+PLAN *find_formplan __P((char **));
+PLAN *not_squish __P((PLAN *));
+PLAN *or_squish __P((PLAN *));
+PLAN *paren_squish __P((PLAN *));
+struct stat;
+void printlong __P((char *, char *, struct stat *));
+int queryuser __P((char **));
+
+PLAN *c_atime __P((char *));
+PLAN *c_ctime __P((char *));
+PLAN *c_depth __P((void));
+PLAN *c_exec __P((char ***, int));
+PLAN *c_follow __P((void));
+PLAN *c_fstype __P((char *));
+PLAN *c_group __P((char *));
+PLAN *c_inum __P((char *));
+PLAN *c_links __P((char *));
+PLAN *c_ls __P((void));
+PLAN *c_name __P((char *));
+PLAN *c_newer __P((char *));
+PLAN *c_nogroup __P((void));
+PLAN *c_nouser __P((void));
+PLAN *c_path __P((char *));
+PLAN *c_perm __P((char *));
+PLAN *c_print __P((void));
+PLAN *c_print0 __P((void));
+PLAN *c_prune __P((void));
+PLAN *c_size __P((char *));
+PLAN *c_type __P((char *));
+PLAN *c_user __P((char *));
+PLAN *c_xdev __P((void));
+PLAN *c_openparen __P((void));
+PLAN *c_closeparen __P((void));
+PLAN *c_mtime __P((char *));
+PLAN *c_not __P((void));
+PLAN *c_or __P((void));
+
+extern int ftsoptions, isdeprecated, isdepth, isoutput, isxargs;
diff --git a/usr.bin/find/find.1 b/usr.bin/find/find.1
new file mode 100644
index 0000000..b92f95c
--- /dev/null
+++ b/usr.bin/find/find.1
@@ -0,0 +1,462 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)find.1 8.4 (Berkeley) 4/1/94
+.\"
+.Dd April 1, 1994
+.Dt FIND 1
+.Os
+.Sh NAME
+.Nm find
+.Nd walk a file hierarchy
+.Sh SYNOPSIS
+.Nm find
+.Op Fl H | Fl L | Fl P
+.Op Fl Xdx
+.Op Fl f Ar file
+.Op Ar file ...
+.Ar expression
+.Sh DESCRIPTION
+.Nm Find
+recursively descends the directory tree for each
+.Ar file
+listed, evaluating an
+.Ar expression
+(composed of the ``primaries'' and ``operands'' listed below) in terms
+of each file in the tree.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width Ds
+.It Fl H
+The
+.Fl H
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link specified on the command line to be
+those of the file referenced by the link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself. File information of all symbolic links not on
+the command line is that of the link itself.
+.It Fl L
+The
+.Fl L
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link to be those of the file referenced by the
+link, not the link itself.
+If the referenced file does not exist, the file information and type will
+be for the link itself.
+.It Fl P
+The
+.Fl P
+option causes the file information and file type (see
+.Xr stat 2)
+returned for each symbolic link to be those of the link itself.
+.It Fl X
+The
+.Fl X
+option is a modification to permit
+.Nm
+to be safely used in conjunction with
+.Xr xargs 1 .
+If a file name contains any of the delimiting characters used by
+.Xr xargs ,
+a diagnostic message is displayed on standard error, and the file
+is skipped.
+The delimiting characters include single (`` ' '') and double (`` " '')
+quotes, backslash (``\e''), space, tab and newline characters.
+.It Fl d
+The
+.Fl d
+option causes
+.Nm find
+to perform a depth\-first traversal, i.e. directories
+are visited in post\-order and all entries in a directory will be acted
+on before the directory itself.
+By default,
+.Nm find
+visits directories in pre\-order, i.e. before their contents.
+Note, the default is
+.Ar not
+a breadth\-first traversal.
+.It Fl f
+The
+.Fl f
+option specifies a file hierarchy for
+.Nm find
+to traverse.
+File hierarchies may also be specified as the operands immediately
+following the options.
+.It Fl x
+The
+.Fl x
+option prevents
+.Nm find
+from descending into directories that have a device number different
+than that of the file from which the descent began.
+.El
+.Sh PRIMARIES
+.Bl -tag -width Ds
+.It Ic -atime Ar n
+True if the difference between the file last access time and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic -ctime Ar n
+True if the difference between the time of last change of file status
+information and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic -exec Ar utility Op argument ... ;
+True if the program named
+.Ar utility
+returns a zero value as its exit status.
+Optional arguments may be passed to the utility.
+The expression must be terminated by a semicolon (``;'').
+If the string ``{}'' appears anywhere in the utility name or the
+arguments it is replaced by the pathname of the current file.
+.Ar Utility
+will be executed from the directory from which
+.Nm find
+was executed.
+.It Ic -fstype Ar type
+True if the file is contained in a file system of type
+.Ar type .
+Currently supported types are ``local'', ``mfs'', ``nfs'', ``msdos'',
+``rdonly'' and ``ufs''.
+The types ``local'' and ``rdonly'' are not specific file system types.
+The former matches any file system physically mounted on the system where
+the
+.Nm find
+is being executed and the latter matches any file system which is
+mounted read-only.
+.It Ic -group Ar gname
+True if the file belongs to the group
+.Ar gname .
+If
+.Ar gname
+is numeric and there is no such group name, then
+.Ar gname
+is treated as a group id.
+.It Ic -inum Ar n
+True if the file has inode number
+.Ar n .
+.It Ic -links Ar n
+True if the file has
+.Ar n
+links.
+.It Ic -ls
+This primary always evaluates to true.
+The following information for the current file is written to standard output:
+its inode number, size in 512\-byte blocks, file permissions, number of hard
+links, owner, group, size in bytes, last modification time, and pathname.
+If the file is a block or character special file, the major and minor numbers
+will be displayed instead of the size in bytes.
+If the file is a symbolic link, the pathname of the linked\-to file will be
+displayed preceded by ``\->''.
+The format is identical to that produced by ``ls \-dgils''.
+.It Ic -mtime Ar n
+True if the difference between the file last modification time and the time
+.Nm find
+was started, rounded up to the next full 24\-hour period, is
+.Ar n
+24\-hour periods.
+.It Ic \&-ok Ar utility Ns Op argument ... ;
+The
+.Ic \&-ok
+primary is identical to the
+.Ic -exec
+primary with the exception that
+.Nm find
+requests user affirmation for the execution of the utility by printing
+a message to the terminal and reading a response.
+If the response is other than ``y'' the command is not executed and the
+value of the
+.Ar \&ok
+expression is false.
+.It Ic -name Ar pattern
+True if the last component of the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters (``['', ``]'', ``*'', and ``?'')
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash (``\e'').
+.It Ic -newer Ar file
+True if the current file has a more recent last modification time than
+.Ar file .
+.It Ic -nouser
+True if the file belongs to an unknown user.
+.It Ic -nogroup
+True if the file belongs to an unknown group.
+.It Ic -path Ar pattern
+True if the pathname being examined matches
+.Ar pattern .
+Special shell pattern matching characters (``['', ``]'', ``*'', and ``?'')
+may be used as part of
+.Ar pattern .
+These characters may be matched explicitly by escaping them with a
+backslash (``\e'').
+Slashes (``/'') are treated as normal characters and do not have to be
+matched explicitly.
+.It Ic -perm Op Fl Ns Ar mode
+The
+.Ar mode
+may be either symbolic (see
+.Xr chmod 1 )
+or an octal number.
+If the mode is symbolic, a starting value of zero is assumed and the
+mode sets or clears permissions without regard to the process' file mode
+creation mask.
+If the mode is octal, only bits 07777
+.Pf ( Dv S_ISUID
+|
+.Dv S_ISGID
+|
+.Dv S_ISTXT
+|
+.Dv S_IRWXU
+|
+.Dv S_IRWXG
+|
+.Dv S_IRWXO )
+of the file's mode bits participate
+in the comparison.
+If the mode is preceded by a dash (``\-''), this primary evaluates to true
+if at least all of the bits in the mode are set in the file's mode bits.
+If the mode is not preceded by a dash, this primary evaluates to true if
+the bits in the mode exactly match the file's mode bits.
+Note, the first character of a symbolic mode may not be a dash (``\-'').
+.It Ic -print
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output.
+The expression is appended to the user specified expression if none of
+.Ic -exec ,
+.Ic -ls ,
+.Ic -print0 ,
+or
+.Ic \&-ok
+are specified.
+.It Ic -print0
+This primary always evaluates to true.
+It prints the pathname of the current file to standard output, followed by a
+.Tn ASCII
+.Tn NUL
+character (character code 0).
+.It Ic -prune
+This primary always evaluates to true.
+It causes
+.Nm find
+to not descend into the current file.
+Note, the
+.Ic -prune
+primary has no effect if the
+.Fl d
+option was specified.
+.It Ic -size Ar n Ns Op Cm c
+True if the file's size, rounded up, in 512\-byte blocks is
+.Ar n .
+If
+.Ar n
+is followed by a ``c'', then the primary is true if the
+file's size is
+.Ar n
+bytes.
+.It Ic -type Ar t
+True if the file is of the specified type.
+Possible file types are as follows:
+.Pp
+.Bl -tag -width flag -offset indent -compact
+.It Cm b
+block special
+.It Cm c
+character special
+.It Cm d
+directory
+.It Cm f
+regular file
+.It Cm l
+symbolic link
+.It Cm p
+FIFO
+.It Cm s
+socket
+.El
+.Pp
+.It Ic -user Ar uname
+True if the file belongs to the user
+.Ar uname .
+If
+.Ar uname
+is numeric and there is no such user name, then
+.Ar uname
+is treated as a user id.
+.El
+.Pp
+All primaries which take a numeric argument allow the number to be
+preceded by a plus sign (``+'') or a minus sign (``\-'').
+A preceding plus sign means ``more than n'', a preceding minus sign means
+``less than n'' and neither means ``exactly n'' .
+.Sh OPERATORS
+The primaries may be combined using the following operators.
+The operators are listed in order of decreasing precedence.
+.Bl -tag -width (expression)
+.It Cm \&( Ns Ar expression Ns Cm \&)
+This evaluates to true if the parenthesized expression evaluates to
+true.
+.Pp
+.It Cm \&! Ns Ar expression
+This is the unary
+.Tn NOT
+operator.
+It evaluates to true if the expression is false.
+.Pp
+.It Ar expression Cm -and Ar expression
+.It Ar expression expression
+The
+.Cm -and
+operator is the logical
+.Tn AND
+operator.
+As it is implied by the juxtaposition of two expressions it does not
+have to be specified.
+The expression evaluates to true if both expressions are true.
+The second expression is not evaluated if the first expression is false.
+.Pp
+.It Ar expression Cm -or Ar expression
+The
+.Cm -or
+operator is the logical
+.Tn OR
+operator.
+The expression evaluates to true if either the first or the second expression
+is true.
+The second expression is not evaluated if the first expression is true.
+.El
+.Pp
+All operands and primaries must be separate arguments to
+.Nm find .
+Primaries which themselves take arguments expect each argument
+to be a separate argument to
+.Nm find .
+.Sh EXAMPLES
+.Pp
+The following examples are shown as given to the shell:
+.Bl -tag -width findx
+.It Li "find / \e! -name \*q*.c\*q -print"
+Print out a list of all the files whose names do not end in ``.c''.
+.It Li "find / -newer ttt -user wnj -print"
+Print out a list of all the files owned by user ``wnj'' that are newer
+than the file ``ttt''.
+.It Li "find / \e! \e( -newer ttt -user wnj \e) -print"
+Print out a list of all the files which are not both newer than ``ttt''
+and owned by ``wnj''.
+.It Li "find / \e( -newer ttt -or -user wnj \e) -print"
+Print out a list of all the files that are either owned by ``wnj'' or
+that are newer than ``ttt''.
+.El
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr locate 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr getgrent 3 ,
+.Xr getpwent 3 ,
+.Xr strmode 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm find
+utility syntax is a superset of the syntax specified by the
+.St -p1003.2
+standard.
+.Pp
+The
+.Fl s
+and
+.Fl X
+options and the
+.Ic -inum ,
+.Ic -print0 ,
+and
+.Ic -ls
+primaries are extensions to
+.St -p1003.2 .
+.Pp
+Historically, the
+.Fl d ,
+.Fl h
+and
+.Fl x
+options were implemented using the primaries ``\-depth'', ``\-follow'',
+and ``\-xdev''.
+These primaries always evaluated to true.
+As they were really global variables that took effect before the traversal
+began, some legal expressions could have unexpected results.
+An example is the expression ``\-print \-o \-depth''.
+As \-print always evaluates to true, the standard order of evaluation
+implies that \-depth would never be evaluated.
+This is not the case.
+.Pp
+The operator ``-or'' was implemented as ``\-o'', and the operator ``-and''
+was implemented as ``\-a''.
+.Pp
+Historic implementations of the
+.Ic exec
+and
+.Ic ok
+primaries did not replace the string ``{}'' in the utility name or the
+utility arguments if it had preceding or following non-whitespace characters.
+This version replaces it no matter where in the utility name or arguments
+it appears.
+.Sh BUGS
+The special characters used by
+.Nm find
+are also special characters to many shell programs.
+In particular, the characters ``*'', ``['', ``]'', ``?'', ``('', ``)'',
+``!'', ``\e'' and ``;'' may have to be escaped from the shell.
+.Pp
+As there is no delimiter separating options and file names or file
+names and the
+.Ar expression ,
+it is difficult to specify files named ``-xdev'' or ``!''.
+These problems are handled by the
+.Fl f
+option and the
+.Xr getopt 3
+``--'' construct.
diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c
new file mode 100644
index 0000000..d69b098
--- /dev/null
+++ b/usr.bin/find/find.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)find.c 8.3 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "find.h"
+
+/*
+ * find_formplan --
+ * process the command line and create a "plan" corresponding to the
+ * command arguments.
+ */
+PLAN *
+find_formplan(argv)
+ char **argv;
+{
+ PLAN *plan, *tail, *new;
+
+ /*
+ * for each argument in the command line, determine what kind of node
+ * it is, create the appropriate node type and add the new plan node
+ * to the end of the existing plan. The resulting plan is a linked
+ * list of plan nodes. For example, the string:
+ *
+ * % find . -name foo -newer bar -print
+ *
+ * results in the plan:
+ *
+ * [-name foo]--> [-newer bar]--> [-print]
+ *
+ * in this diagram, `[-name foo]' represents the plan node generated
+ * by c_name() with an argument of foo and `-->' represents the
+ * plan->next pointer.
+ */
+ for (plan = tail = NULL; *argv;) {
+ if (!(new = find_create(&argv)))
+ continue;
+ if (plan == NULL)
+ tail = plan = new;
+ else {
+ tail->next = new;
+ tail = new;
+ }
+ }
+
+ /*
+ * if the user didn't specify one of -print, -ok or -exec, then -print
+ * is assumed so we add a -print node on the end. It is possible that
+ * the user might want the -print someplace else on the command line,
+ * but there's no way to know that.
+ */
+ if (!isoutput) {
+ new = c_print();
+ if (plan == NULL)
+ tail = plan = new;
+ else {
+ new = c_openparen();
+ new->next = plan;
+ plan = new;
+ new = c_closeparen();
+ tail->next = new;
+ tail = new;
+ new = c_print();
+ tail->next = new;
+ tail = new;
+ }
+ }
+
+ /*
+ * the command line has been completely processed into a search plan
+ * except for the (, ), !, and -o operators. Rearrange the plan so
+ * that the portions of the plan which are affected by the operators
+ * are moved into operator nodes themselves. For example:
+ *
+ * [!]--> [-name foo]--> [-print]
+ *
+ * becomes
+ *
+ * [! [-name foo] ]--> [-print]
+ *
+ * and
+ *
+ * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
+ *
+ * becomes
+ *
+ * [expr [-depth]-->[-name foo] ]--> [-print]
+ *
+ * operators are handled in order of precedence.
+ */
+
+ plan = paren_squish(plan); /* ()'s */
+ plan = not_squish(plan); /* !'s */
+ plan = or_squish(plan); /* -o's */
+ return (plan);
+}
+
+FTS *tree; /* pointer to top of FTS hierarchy */
+
+/*
+ * find_execute --
+ * take a search plan and an array of search paths and executes the plan
+ * over all FTSENT's returned for the given search paths.
+ */
+int
+find_execute(plan, paths)
+ PLAN *plan; /* search plan */
+ char **paths; /* array of pathnames to traverse */
+{
+ register FTSENT *entry;
+ PLAN *p;
+ int rval;
+
+ if ((tree = fts_open(paths, ftsoptions, (int (*)())NULL)) == NULL)
+ err(1, "ftsopen");
+
+ for (rval = 0; (entry = fts_read(tree)) != NULL;) {
+ switch (entry->fts_info) {
+ case FTS_D:
+ if (isdepth)
+ continue;
+ break;
+ case FTS_DP:
+ if (!isdepth)
+ continue;
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ (void)fflush(stdout);
+ warnx("%s: %s",
+ entry->fts_path, strerror(entry->fts_errno));
+ rval = 1;
+ continue;
+ }
+#define BADCH " \t\n\\'\""
+ if (isxargs && strpbrk(entry->fts_path, BADCH)) {
+ (void)fflush(stdout);
+ warnx("%s: illegal path", entry->fts_path);
+ rval = 1;
+ continue;
+ }
+
+ /*
+ * Call all the functions in the execution plan until one is
+ * false or all have been executed. This is where we do all
+ * the work specified by the user on the command line.
+ */
+ for (p = plan; p && (p->eval)(p, entry); p = p->next);
+ }
+ if (errno)
+ err(1, "fts_read");
+ return (rval);
+}
diff --git a/usr.bin/find/find.h b/usr.bin/find/find.h
new file mode 100644
index 0000000..8066d37
--- /dev/null
+++ b/usr.bin/find/find.h
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)find.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* node type */
+enum ntype {
+ N_AND = 1, /* must start > 0 */
+ N_ATIME, N_CLOSEPAREN, N_CTIME, N_DEPTH, N_EXEC, N_EXPR, N_FOLLOW,
+ N_FSTYPE, N_GROUP, N_INUM, N_LINKS, N_LS, N_MTIME, N_NAME, N_NEWER,
+ N_NOGROUP, N_NOT, N_NOUSER, N_OK, N_OPENPAREN, N_OR, N_PATH,
+ N_PERM, N_PRINT, N_PRUNE, N_SIZE, N_TYPE, N_USER, N_XDEV,
+ N_PRINT0
+};
+
+/* node definition */
+typedef struct _plandata {
+ struct _plandata *next; /* next node */
+ int (*eval) /* node evaluation function */
+ __P((struct _plandata *, FTSENT *));
+#define F_EQUAL 1 /* [acm]time inum links size */
+#define F_LESSTHAN 2
+#define F_GREATER 3
+#define F_NEEDOK 1 /* exec ok */
+#define F_MTFLAG 1 /* fstype */
+#define F_MTTYPE 2
+#define F_ATLEAST 1 /* perm */
+ int flags; /* private flags */
+ enum ntype type; /* plan node type */
+ union {
+ gid_t _g_data; /* gid */
+ ino_t _i_data; /* inode */
+ mode_t _m_data; /* mode mask */
+ nlink_t _l_data; /* link count */
+ off_t _o_data; /* file size */
+ time_t _t_data; /* time value */
+ uid_t _u_data; /* uid */
+ short _mt_data; /* mount flags */
+ struct _plandata *_p_data[2]; /* PLAN trees */
+ struct _ex {
+ char **_e_argv; /* argv array */
+ char **_e_orig; /* original strings */
+ int *_e_len; /* allocated length */
+ } ex;
+ char *_a_data[2]; /* array of char pointers */
+ char *_c_data; /* char pointer */
+ } p_un;
+} PLAN;
+#define a_data p_un._a_data
+#define c_data p_un._c_data
+#define i_data p_un._i_data
+#define g_data p_un._g_data
+#define l_data p_un._l_data
+#define m_data p_un._m_data
+#define mt_data p_un._mt_data
+#define o_data p_un._o_data
+#define p_data p_un._p_data
+#define t_data p_un._t_data
+#define u_data p_un._u_data
+#define e_argv p_un.ex._e_argv
+#define e_orig p_un.ex._e_orig
+#define e_len p_un.ex._e_len
+
+typedef struct _option {
+ char *name; /* option name */
+ enum ntype token; /* token type */
+ PLAN *(*create)(); /* create function: DON'T PROTOTYPE! */
+#define O_NONE 0x01 /* no call required */
+#define O_ZERO 0x02 /* pass: nothing */
+#define O_ARGV 0x04 /* pass: argv, increment argv */
+#define O_ARGVP 0x08 /* pass: *argv, N_OK || N_EXEC */
+ int flags;
+} OPTION;
+
+#include "extern.h"
diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c
new file mode 100644
index 0000000..92892ba
--- /dev/null
+++ b/usr.bin/find/function.c
@@ -0,0 +1,1095 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)function.c 8.6 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "find.h"
+
+#define COMPARE(a, b) { \
+ switch (plan->flags) { \
+ case F_EQUAL: \
+ return (a == b); \
+ case F_LESSTHAN: \
+ return (a < b); \
+ case F_GREATER: \
+ return (a > b); \
+ default: \
+ abort(); \
+ } \
+}
+
+static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *))));
+
+/*
+ * find_parsenum --
+ * Parse a string of the form [+-]# and return the value.
+ */
+static long
+find_parsenum(plan, option, vp, endch)
+ PLAN *plan;
+ char *option, *vp, *endch;
+{
+ long value;
+ char *endchar, *str; /* Pointer to character ending conversion. */
+
+ /* Determine comparison from leading + or -. */
+ str = vp;
+ switch (*str) {
+ case '+':
+ ++str;
+ plan->flags = F_GREATER;
+ break;
+ case '-':
+ ++str;
+ plan->flags = F_LESSTHAN;
+ break;
+ default:
+ plan->flags = F_EQUAL;
+ break;
+ }
+
+ /*
+ * Convert the string with strtol(). Note, if strtol() returns zero
+ * and endchar points to the beginning of the string we know we have
+ * a syntax error.
+ */
+ value = strtol(str, &endchar, 10);
+ if (value == 0 && endchar == str)
+ errx(1, "%s: %s: illegal numeric value", option, vp);
+ if (endchar[0] && (endch == NULL || endchar[0] != *endch))
+ errx(1, "%s: %s: illegal trailing character", option, vp);
+ if (endch)
+ *endch = endchar[0];
+ return (value);
+}
+
+/*
+ * The value of n for the inode times (atime, ctime, and mtime) is a range,
+ * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
+ * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
+ * user wanted. Correct so that -1 is "less than 1".
+ */
+#define TIME_CORRECT(p, ttype) \
+ if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
+ ++((p)->t_data);
+
+/*
+ * -atime n functions --
+ *
+ * True if the difference between the file access time and the
+ * current time is n 24 hour periods.
+ */
+int
+f_atime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_atime +
+ 86400 - 1) / 86400, plan->t_data);
+}
+
+PLAN *
+c_atime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_ATIME, f_atime);
+ new->t_data = find_parsenum(new, "-atime", arg, NULL);
+ TIME_CORRECT(new, N_ATIME);
+ return (new);
+}
+/*
+ * -ctime n functions --
+ *
+ * True if the difference between the last change of file
+ * status information and the current time is n 24 hour periods.
+ */
+int
+f_ctime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_ctime +
+ 86400 - 1) / 86400, plan->t_data);
+}
+
+PLAN *
+c_ctime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_CTIME, f_ctime);
+ new->t_data = find_parsenum(new, "-ctime", arg, NULL);
+ TIME_CORRECT(new, N_CTIME);
+ return (new);
+}
+
+/*
+ * -depth functions --
+ *
+ * Always true, causes descent of the directory hierarchy to be done
+ * so that all entries in a directory are acted on before the directory
+ * itself.
+ */
+int
+f_always_true(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (1);
+}
+
+PLAN *
+c_depth()
+{
+ isdepth = 1;
+
+ return (palloc(N_DEPTH, f_always_true));
+}
+
+/*
+ * [-exec | -ok] utility [arg ... ] ; functions --
+ *
+ * True if the executed utility returns a zero value as exit status.
+ * The end of the primary expression is delimited by a semicolon. If
+ * "{}" occurs anywhere, it gets replaced by the current pathname.
+ * The current directory for the execution of utility is the same as
+ * the current directory when the find utility was started.
+ *
+ * The primary -ok is different in that it requests affirmation of the
+ * user before executing the utility.
+ */
+int
+f_exec(plan, entry)
+ register PLAN *plan;
+ FTSENT *entry;
+{
+ extern int dotfd;
+ register int cnt;
+ pid_t pid;
+ int status;
+
+ for (cnt = 0; plan->e_argv[cnt]; ++cnt)
+ if (plan->e_len[cnt])
+ brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
+ entry->fts_path, plan->e_len[cnt]);
+
+ if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv))
+ return (0);
+
+ /* make sure find output is interspersed correctly with subprocesses */
+ fflush(stdout);
+
+ switch (pid = vfork()) {
+ case -1:
+ err(1, "fork");
+ /* NOTREACHED */
+ case 0:
+ if (fchdir(dotfd)) {
+ warn("chdir");
+ _exit(1);
+ }
+ execvp(plan->e_argv[0], plan->e_argv);
+ warn("%s", plan->e_argv[0]);
+ _exit(1);
+ }
+ pid = waitpid(pid, &status, 0);
+ return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
+}
+
+/*
+ * c_exec --
+ * build three parallel arrays, one with pointers to the strings passed
+ * on the command line, one with (possibly duplicated) pointers to the
+ * argv array, and one with integer values that are lengths of the
+ * strings, but also flags meaning that the string has to be massaged.
+ */
+PLAN *
+c_exec(argvp, isok)
+ char ***argvp;
+ int isok;
+{
+ PLAN *new; /* node returned */
+ register int cnt;
+ register char **argv, **ap, *p;
+
+ isoutput = 1;
+
+ new = palloc(N_EXEC, f_exec);
+ if (isok)
+ new->flags = F_NEEDOK;
+
+ for (ap = argv = *argvp;; ++ap) {
+ if (!*ap)
+ errx(1,
+ "%s: no terminating \";\"", isok ? "-ok" : "-exec");
+ if (**ap == ';')
+ break;
+ }
+
+ cnt = ap - *argvp + 1;
+ new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *));
+ new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *));
+ new->e_len = (int *)emalloc((u_int)cnt * sizeof(int));
+
+ for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
+ new->e_orig[cnt] = *argv;
+ for (p = *argv; *p; ++p)
+ if (p[0] == '{' && p[1] == '}') {
+ new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN);
+ new->e_len[cnt] = MAXPATHLEN;
+ break;
+ }
+ if (!*p) {
+ new->e_argv[cnt] = *argv;
+ new->e_len[cnt] = 0;
+ }
+ }
+ new->e_argv[cnt] = new->e_orig[cnt] = NULL;
+
+ *argvp = argv + 1;
+ return (new);
+}
+
+/*
+ * -follow functions --
+ *
+ * Always true, causes symbolic links to be followed on a global
+ * basis.
+ */
+PLAN *
+c_follow()
+{
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+
+ return (palloc(N_FOLLOW, f_always_true));
+}
+
+/*
+ * -fstype functions --
+ *
+ * True if the file is of a certain type.
+ */
+int
+f_fstype(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ static dev_t curdev; /* need a guaranteed illegal dev value */
+ static int first = 1;
+ struct statfs sb;
+ static short val;
+ char *p, save[2];
+
+ /* Only check when we cross mount point. */
+ if (first || curdev != entry->fts_statp->st_dev) {
+ curdev = entry->fts_statp->st_dev;
+
+ /*
+ * Statfs follows symlinks; find wants the link's file system,
+ * not where it points.
+ */
+ if (entry->fts_info == FTS_SL ||
+ entry->fts_info == FTS_SLNONE) {
+ if ((p = strrchr(entry->fts_accpath, '/')) != NULL)
+ ++p;
+ else
+ p = entry->fts_accpath;
+ save[0] = p[0];
+ p[0] = '.';
+ save[1] = p[1];
+ p[1] = '\0';
+
+ } else
+ p = NULL;
+
+ if (statfs(entry->fts_accpath, &sb))
+ err(1, "%s", entry->fts_accpath);
+
+ if (p) {
+ p[0] = save[0];
+ p[1] = save[1];
+ }
+
+ first = 0;
+ switch (plan->flags) {
+ case F_MTFLAG:
+ val = sb.f_flags;
+ break;
+ case F_MTTYPE:
+ val = sb.f_type;
+ break;
+ default:
+ abort();
+ }
+ }
+ switch(plan->flags) {
+ case F_MTFLAG:
+ return (val & plan->mt_data);
+ case F_MTTYPE:
+ return (val == plan->mt_data);
+ default:
+ abort();
+ }
+}
+
+PLAN *
+c_fstype(arg)
+ char *arg;
+{
+ register PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_FSTYPE, f_fstype);
+ switch (*arg) {
+ case 'l':
+ if (!strcmp(arg, "local")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_LOCAL;
+ return (new);
+ }
+ break;
+ case 'm':
+ if (!strcmp(arg, "mfs")) {
+ new->flags = F_MTTYPE;
+ new->mt_data = MOUNT_MFS;
+ return (new);
+ }
+ break;
+ case 'n':
+ if (!strcmp(arg, "nfs")) {
+ new->flags = F_MTTYPE;
+ new->mt_data = MOUNT_NFS;
+ return (new);
+ }
+ break;
+ case 'p':
+ if (!strcmp(arg, "msdos")) {
+ new->flags = F_MTTYPE;
+ new->mt_data = MOUNT_MSDOS;
+ return (new);
+ }
+ break;
+ case 'r':
+ if (!strcmp(arg, "rdonly")) {
+ new->flags = F_MTFLAG;
+ new->mt_data = MNT_RDONLY;
+ return (new);
+ }
+ break;
+ case 'u':
+ if (!strcmp(arg, "ufs")) {
+ new->flags = F_MTTYPE;
+ new->mt_data = MOUNT_UFS;
+ return (new);
+ }
+ break;
+ }
+ errx(1, "%s: unknown file type", arg);
+ /* NOTREACHED */
+}
+
+/*
+ * -group gname functions --
+ *
+ * True if the file belongs to the group gname. If gname is numeric and
+ * an equivalent of the getgrnam() function does not return a valid group
+ * name, gname is taken as a group ID.
+ */
+int
+f_group(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_gid == plan->g_data);
+}
+
+PLAN *
+c_group(gname)
+ char *gname;
+{
+ PLAN *new;
+ struct group *g;
+ gid_t gid;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ g = getgrnam(gname);
+ if (g == NULL) {
+ gid = atoi(gname);
+ if (gid == 0 && gname[0] != '0')
+ errx(1, "-group: %s: no such group", gname);
+ } else
+ gid = g->gr_gid;
+
+ new = palloc(N_GROUP, f_group);
+ new->g_data = gid;
+ return (new);
+}
+
+/*
+ * -inum n functions --
+ *
+ * True if the file has inode # n.
+ */
+int
+f_inum(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ COMPARE(entry->fts_statp->st_ino, plan->i_data);
+}
+
+PLAN *
+c_inum(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_INUM, f_inum);
+ new->i_data = find_parsenum(new, "-inum", arg, NULL);
+ return (new);
+}
+
+/*
+ * -links n functions --
+ *
+ * True if the file has n links.
+ */
+int
+f_links(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ COMPARE(entry->fts_statp->st_nlink, plan->l_data);
+}
+
+PLAN *
+c_links(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_LINKS, f_links);
+ new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL);
+ return (new);
+}
+
+/*
+ * -ls functions --
+ *
+ * Always true - prints the current entry to stdout in "ls" format.
+ */
+int
+f_ls(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
+ return (1);
+}
+
+PLAN *
+c_ls()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+ isoutput = 1;
+
+ return (palloc(N_LS, f_ls));
+}
+
+/*
+ * -mtime n functions --
+ *
+ * True if the difference between the file modification time and the
+ * current time is n 24 hour periods.
+ */
+int
+f_mtime(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern time_t now;
+
+ COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) /
+ 86400, plan->t_data);
+}
+
+PLAN *
+c_mtime(arg)
+ char *arg;
+{
+ PLAN *new;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_MTIME, f_mtime);
+ new->t_data = find_parsenum(new, "-mtime", arg, NULL);
+ TIME_CORRECT(new, N_MTIME);
+ return (new);
+}
+
+/*
+ * -name functions --
+ *
+ * True if the basename of the filename being examined
+ * matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_name(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (!fnmatch(plan->c_data, entry->fts_name, 0));
+}
+
+PLAN *
+c_name(pattern)
+ char *pattern;
+{
+ PLAN *new;
+
+ new = palloc(N_NAME, f_name);
+ new->c_data = pattern;
+ return (new);
+}
+
+/*
+ * -newer file functions --
+ *
+ * True if the current file has been modified more recently
+ * then the modification time of the file named by the pathname
+ * file.
+ */
+int
+f_newer(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_mtime > plan->t_data);
+}
+
+PLAN *
+c_newer(filename)
+ char *filename;
+{
+ PLAN *new;
+ struct stat sb;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ if (stat(filename, &sb))
+ err(1, "%s", filename);
+ new = palloc(N_NEWER, f_newer);
+ new->t_data = sb.st_mtime;
+ return (new);
+}
+
+/*
+ * -nogroup functions --
+ *
+ * True if file belongs to a user ID for which the equivalent
+ * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
+ */
+int
+f_nogroup(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ char *group_from_gid();
+
+ return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nogroup()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return (palloc(N_NOGROUP, f_nogroup));
+}
+
+/*
+ * -nouser functions --
+ *
+ * True if file belongs to a user ID for which the equivalent
+ * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
+ */
+int
+f_nouser(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ char *user_from_uid();
+
+ return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
+}
+
+PLAN *
+c_nouser()
+{
+ ftsoptions &= ~FTS_NOSTAT;
+
+ return (palloc(N_NOUSER, f_nouser));
+}
+
+/*
+ * -path functions --
+ *
+ * True if the path of the filename being examined
+ * matches pattern using Pattern Matching Notation S3.14
+ */
+int
+f_path(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (!fnmatch(plan->c_data, entry->fts_path, 0));
+}
+
+PLAN *
+c_path(pattern)
+ char *pattern;
+{
+ PLAN *new;
+
+ new = palloc(N_NAME, f_path);
+ new->c_data = pattern;
+ return (new);
+}
+
+/*
+ * -perm functions --
+ *
+ * The mode argument is used to represent file mode bits. If it starts
+ * with a leading digit, it's treated as an octal mode, otherwise as a
+ * symbolic mode.
+ */
+int
+f_perm(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ mode_t mode;
+
+ mode = entry->fts_statp->st_mode &
+ (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
+ if (plan->flags == F_ATLEAST)
+ return ((plan->m_data | mode) == mode);
+ else
+ return (mode == plan->m_data);
+ /* NOTREACHED */
+}
+
+PLAN *
+c_perm(perm)
+ char *perm;
+{
+ PLAN *new;
+ mode_t *set;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_PERM, f_perm);
+
+ if (*perm == '-') {
+ new->flags = F_ATLEAST;
+ ++perm;
+ }
+
+ if ((set = setmode(perm)) == NULL)
+ err(1, "-perm: %s: illegal mode string", perm);
+
+ new->m_data = getmode(set, 0);
+ return (new);
+}
+
+/*
+ * -print functions --
+ *
+ * Always true, causes the current pathame to be written to
+ * standard output.
+ */
+int
+f_print(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ (void)puts(entry->fts_path);
+ return (1);
+}
+
+PLAN *
+c_print()
+{
+ isoutput = 1;
+
+ return (palloc(N_PRINT, f_print));
+}
+
+/*
+ * -print0 functions --
+ *
+ * Always true, causes the current pathame to be written to
+ * standard output followed by a NUL character
+ */
+int
+f_print0(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ fputs(entry->fts_path, stdout);
+ fputc('\0', stdout);
+ return (1);
+}
+
+PLAN *
+c_print0()
+{
+ isoutput = 1;
+
+ return (palloc(N_PRINT0, f_print0));
+}
+
+/*
+ * -prune functions --
+ *
+ * Prune a portion of the hierarchy.
+ */
+int
+f_prune(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ extern FTS *tree;
+
+ if (fts_set(tree, entry, FTS_SKIP))
+ err(1, "%s", entry->fts_path);
+ return (1);
+}
+
+PLAN *
+c_prune()
+{
+ return (palloc(N_PRUNE, f_prune));
+}
+
+/*
+ * -size n[c] functions --
+ *
+ * True if the file size in bytes, divided by an implementation defined
+ * value and rounded up to the next integer, is n. If n is followed by
+ * a c, the size is in bytes.
+ */
+#define FIND_SIZE 512
+static int divsize = 1;
+
+int
+f_size(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ off_t size;
+
+ size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
+ FIND_SIZE : entry->fts_statp->st_size;
+ COMPARE(size, plan->o_data);
+}
+
+PLAN *
+c_size(arg)
+ char *arg;
+{
+ PLAN *new;
+ char endch;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ new = palloc(N_SIZE, f_size);
+ endch = 'c';
+ new->o_data = find_parsenum(new, "-size", arg, &endch);
+ if (endch == 'c')
+ divsize = 0;
+ return (new);
+}
+
+/*
+ * -type c functions --
+ *
+ * True if the type of the file is c, where c is b, c, d, p, or f for
+ * block special file, character special file, directory, FIFO, or
+ * regular file, respectively.
+ */
+int
+f_type(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
+}
+
+PLAN *
+c_type(typestring)
+ char *typestring;
+{
+ PLAN *new;
+ mode_t mask;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ switch (typestring[0]) {
+ case 'b':
+ mask = S_IFBLK;
+ break;
+ case 'c':
+ mask = S_IFCHR;
+ break;
+ case 'd':
+ mask = S_IFDIR;
+ break;
+ case 'f':
+ mask = S_IFREG;
+ break;
+ case 'l':
+ mask = S_IFLNK;
+ break;
+ case 'p':
+ mask = S_IFIFO;
+ break;
+ case 's':
+ mask = S_IFSOCK;
+ break;
+ default:
+ errx(1, "-type: %s: unknown type", typestring);
+ }
+
+ new = palloc(N_TYPE, f_type);
+ new->m_data = mask;
+ return (new);
+}
+
+/*
+ * -user uname functions --
+ *
+ * True if the file belongs to the user uname. If uname is numeric and
+ * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
+ * return a valid user name, uname is taken as a user ID.
+ */
+int
+f_user(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ return (entry->fts_statp->st_uid == plan->u_data);
+}
+
+PLAN *
+c_user(username)
+ char *username;
+{
+ PLAN *new;
+ struct passwd *p;
+ uid_t uid;
+
+ ftsoptions &= ~FTS_NOSTAT;
+
+ p = getpwnam(username);
+ if (p == NULL) {
+ uid = atoi(username);
+ if (uid == 0 && username[0] != '0')
+ errx(1, "-user: %s: no such user", username);
+ } else
+ uid = p->pw_uid;
+
+ new = palloc(N_USER, f_user);
+ new->u_data = uid;
+ return (new);
+}
+
+/*
+ * -xdev functions --
+ *
+ * Always true, causes find not to decend past directories that have a
+ * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
+ */
+PLAN *
+c_xdev()
+{
+ ftsoptions |= FTS_XDEV;
+
+ return (palloc(N_XDEV, f_always_true));
+}
+
+/*
+ * ( expression ) functions --
+ *
+ * True if expression is true.
+ */
+int
+f_expr(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (state);
+}
+
+/*
+ * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
+ * eliminated during phase 2 of find_formplan() --- the '(' node is converted
+ * to a N_EXPR node containing the expression and the ')' node is discarded.
+ */
+PLAN *
+c_openparen()
+{
+ return (palloc(N_OPENPAREN, (int (*)())-1));
+}
+
+PLAN *
+c_closeparen()
+{
+ return (palloc(N_CLOSEPAREN, (int (*)())-1));
+}
+
+/*
+ * ! expression functions --
+ *
+ * Negation of a primary; the unary NOT operator.
+ */
+int
+f_not(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (!state);
+}
+
+PLAN *
+c_not()
+{
+ return (palloc(N_NOT, f_not));
+}
+
+/*
+ * expression -o expression functions --
+ *
+ * Alternation of primaries; the OR operator. The second expression is
+ * not evaluated if the first expression is true.
+ */
+int
+f_or(plan, entry)
+ PLAN *plan;
+ FTSENT *entry;
+{
+ register PLAN *p;
+ register int state;
+
+ for (p = plan->p_data[0];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+
+ if (state)
+ return (1);
+
+ for (p = plan->p_data[1];
+ p && (state = (p->eval)(p, entry)); p = p->next);
+ return (state);
+}
+
+PLAN *
+c_or()
+{
+ return (palloc(N_OR, f_or));
+}
+
+static PLAN *
+palloc(t, f)
+ enum ntype t;
+ int (*f) __P((PLAN *, FTSENT *));
+{
+ PLAN *new;
+
+ if ((new = malloc(sizeof(PLAN))) == NULL)
+ err(1, NULL);
+ new->type = t;
+ new->eval = f;
+ new->flags = 0;
+ new->next = NULL;
+ return (new);
+}
diff --git a/usr.bin/find/ls.c b/usr.bin/find/ls.c
new file mode 100644
index 0000000..929ef79
--- /dev/null
+++ b/usr.bin/find/ls.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ls.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/* Derived from the print routines in the ls(1) source code. */
+
+static void printlink __P((char *));
+static void printtime __P((time_t));
+
+void
+printlong(name, accpath, sb)
+ char *name; /* filename to print */
+ char *accpath; /* current valid path to filename */
+ struct stat *sb; /* stat buffer */
+{
+ char modep[15], *user_from_uid(), *group_from_gid();
+
+ (void)printf("%6lu %4qd ", sb->st_ino, sb->st_blocks);
+ (void)strmode(sb->st_mode, modep);
+ (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, UT_NAMESIZE,
+ user_from_uid(sb->st_uid, 0), UT_NAMESIZE,
+ group_from_gid(sb->st_gid, 0));
+
+ if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode))
+ (void)printf("%3d, %3d ", major(sb->st_rdev),
+ minor(sb->st_rdev));
+ else
+ (void)printf("%8qd ", sb->st_size);
+ printtime(sb->st_mtime);
+ (void)printf("%s", name);
+ if (S_ISLNK(sb->st_mode))
+ printlink(accpath);
+ (void)putchar('\n');
+}
+
+static void
+printtime(ftime)
+ time_t ftime;
+{
+ int i;
+ char *longstring, *ctime();
+ time_t time();
+
+ longstring = ctime((long *)&ftime);
+ for (i = 4; i < 11; ++i)
+ (void)putchar(longstring[i]);
+
+#define SIXMONTHS ((365 / 2) * 86400)
+ if (ftime + SIXMONTHS > time((time_t *)NULL))
+ for (i = 11; i < 16; ++i)
+ (void)putchar(longstring[i]);
+ else {
+ (void)putchar(' ');
+ for (i = 20; i < 24; ++i)
+ (void)putchar(longstring[i]);
+ }
+ (void)putchar(' ');
+}
+
+static void
+printlink(name)
+ char *name;
+{
+ int lnklen;
+ char path[MAXPATHLEN + 1];
+
+ if ((lnklen = readlink(name, path, MAXPATHLEN)) == -1) {
+ warn("%s", name);
+ return;
+ }
+ path[lnklen] = '\0';
+ (void)printf(" -> %s", path);
+}
diff --git a/usr.bin/find/main.c b/usr.bin/find/main.c
new file mode 100644
index 0000000..6cc6435
--- /dev/null
+++ b/usr.bin/find/main.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "find.h"
+
+time_t now; /* time find was run */
+int dotfd; /* starting directory */
+int ftsoptions; /* options for the ftsopen(3) call */
+int isdeprecated; /* using deprecated syntax */
+int isdepth; /* do directories on post-order visit */
+int isoutput; /* user specified output operator */
+int isxargs; /* don't permit xargs delimiting chars */
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char **p, **start;
+ int Hflag, Lflag, Pflag, ch;
+
+ (void)time(&now); /* initialize the time-of-day */
+
+ p = start = argv;
+ Hflag = Lflag = Pflag = 0;
+ ftsoptions = FTS_NOSTAT | FTS_PHYSICAL;
+ while ((ch = getopt(argc, argv, "HLPXdf:x")) != EOF)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'X':
+ isxargs = 1;
+ break;
+ case 'd':
+ isdepth = 1;
+ break;
+ case 'f':
+ *p++ = optarg;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (Hflag)
+ ftsoptions |= FTS_COMFOLLOW;
+ if (Lflag) {
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ }
+
+ /*
+ * Find first option to delimit the file list. The first argument
+ * that starts with a -, or is a ! or a ( must be interpreted as a
+ * part of the find expression, according to POSIX .2.
+ */
+ for (; *argv != NULL; *p++ = *argv++) {
+ if (argv[0][0] == '-')
+ break;
+ if ((argv[0][0] == '!' || argv[0][0] == '(') &&
+ argv[0][1] == '\0')
+ break;
+ }
+
+ if (p == start)
+ usage();
+ *p = NULL;
+
+ if ((dotfd = open(".", O_RDONLY, 0)) < 0)
+ err(1, ".");
+
+ exit(find_execute(find_formplan(argv), start));
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: find [-H | -L | -P] [-Xdx] [-f file] [file ...] [expression]\n");
+ exit(1);
+}
diff --git a/usr.bin/find/misc.c b/usr.bin/find/misc.c
new file mode 100644
index 0000000..71316ee
--- /dev/null
+++ b/usr.bin/find/misc.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.2 (Berkeley) 4/1/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "find.h"
+
+/*
+ * brace_subst --
+ * Replace occurrences of {} in s1 with s2 and return the result string.
+ */
+void
+brace_subst(orig, store, path, len)
+ char *orig, **store, *path;
+ int len;
+{
+ register int plen;
+ register char ch, *p;
+
+ plen = strlen(path);
+ for (p = *store; (ch = *orig) != '\0'; ++orig)
+ if (ch == '{' && orig[1] == '}') {
+ while ((p - *store) + plen > len)
+ if (!(*store = realloc(*store, len *= 2)))
+ err(1, NULL);
+ memmove(p, path, plen);
+ p += plen;
+ ++orig;
+ } else
+ *p++ = ch;
+ *p = '\0';
+}
+
+/*
+ * queryuser --
+ * print a message to standard error and then read input from standard
+ * input. If the input is 'y' then 1 is returned.
+ */
+int
+queryuser(argv)
+ register char **argv;
+{
+ int ch, first, nl;
+
+ (void)fprintf(stderr, "\"%s", *argv);
+ while (*++argv)
+ (void)fprintf(stderr, " %s", *argv);
+ (void)fprintf(stderr, "\"? ");
+ (void)fflush(stderr);
+
+ first = ch = getchar();
+ for (nl = 0;;) {
+ if (ch == '\n') {
+ nl = 1;
+ break;
+ }
+ if (ch == EOF)
+ break;
+ ch = getchar();
+ }
+
+ if (!nl) {
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+ }
+ return (first == 'y');
+}
+
+/*
+ * emalloc --
+ * malloc with error checking.
+ */
+void *
+emalloc(len)
+ u_int len;
+{
+ void *p;
+
+ if ((p = malloc(len)) == NULL)
+ err(1, NULL);
+ return (p);
+}
diff --git a/usr.bin/find/operator.c b/usr.bin/find/operator.c
new file mode 100644
index 0000000..d7c6015
--- /dev/null
+++ b/usr.bin/find/operator.c
@@ -0,0 +1,270 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)operator.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+
+#include "find.h"
+
+/*
+ * yanknode --
+ * destructively removes the top from the plan
+ */
+static PLAN *
+yanknode(planp)
+ PLAN **planp; /* pointer to top of plan (modified) */
+{
+ PLAN *node; /* top node removed from the plan */
+
+ if ((node = (*planp)) == NULL)
+ return (NULL);
+ (*planp) = (*planp)->next;
+ node->next = NULL;
+ return (node);
+}
+
+/*
+ * yankexpr --
+ * Removes one expression from the plan. This is used mainly by
+ * paren_squish. In comments below, an expression is either a
+ * simple node or a N_EXPR node containing a list of simple nodes.
+ */
+static PLAN *
+yankexpr(planp)
+ PLAN **planp; /* pointer to top of plan (modified) */
+{
+ register PLAN *next; /* temp node holding subexpression results */
+ PLAN *node; /* pointer to returned node or expression */
+ PLAN *tail; /* pointer to tail of subplan */
+ PLAN *subplan; /* pointer to head of ( ) expression */
+ int f_expr();
+
+ /* first pull the top node from the plan */
+ if ((node = yanknode(planp)) == NULL)
+ return (NULL);
+
+ /*
+ * If the node is an '(' then we recursively slurp up expressions
+ * until we find its associated ')'. If it's a closing paren we
+ * just return it and unwind our recursion; all other nodes are
+ * complete expressions, so just return them.
+ */
+ if (node->type == N_OPENPAREN)
+ for (tail = subplan = NULL;;) {
+ if ((next = yankexpr(planp)) == NULL)
+ err(1, "(: missing closing ')'");
+ /*
+ * If we find a closing ')' we store the collected
+ * subplan in our '(' node and convert the node to
+ * a N_EXPR. The ')' we found is ignored. Otherwise,
+ * we just continue to add whatever we get to our
+ * subplan.
+ */
+ if (next->type == N_CLOSEPAREN) {
+ if (subplan == NULL)
+ errx(1, "(): empty inner expression");
+ node->p_data[0] = subplan;
+ node->type = N_EXPR;
+ node->eval = f_expr;
+ break;
+ } else {
+ if (subplan == NULL)
+ tail = subplan = next;
+ else {
+ tail->next = next;
+ tail = next;
+ }
+ tail->next = NULL;
+ }
+ }
+ return (node);
+}
+
+/*
+ * paren_squish --
+ * replaces "parentheisized" plans in our search plan with "expr" nodes.
+ */
+PLAN *
+paren_squish(plan)
+ PLAN *plan; /* plan with ( ) nodes */
+{
+ register PLAN *expr; /* pointer to next expression */
+ register PLAN *tail; /* pointer to tail of result plan */
+ PLAN *result; /* pointer to head of result plan */
+
+ result = tail = NULL;
+
+ /*
+ * the basic idea is to have yankexpr do all our work and just
+ * collect it's results together.
+ */
+ while ((expr = yankexpr(&plan)) != NULL) {
+ /*
+ * if we find an unclaimed ')' it means there is a missing
+ * '(' someplace.
+ */
+ if (expr->type == N_CLOSEPAREN)
+ errx(1, "): no beginning '('");
+
+ /* add the expression to our result plan */
+ if (result == NULL)
+ tail = result = expr;
+ else {
+ tail->next = expr;
+ tail = expr;
+ }
+ tail->next = NULL;
+ }
+ return (result);
+}
+
+/*
+ * not_squish --
+ * compresses "!" expressions in our search plan.
+ */
+PLAN *
+not_squish(plan)
+ PLAN *plan; /* plan to process */
+{
+ register PLAN *next; /* next node being processed */
+ register PLAN *node; /* temporary node used in N_NOT processing */
+ register PLAN *tail; /* pointer to tail of result plan */
+ PLAN *result; /* pointer to head of result plan */
+
+ tail = result = next = NULL;
+
+ while ((next = yanknode(&plan)) != NULL) {
+ /*
+ * if we encounter a ( expression ) then look for nots in
+ * the expr subplan.
+ */
+ if (next->type == N_EXPR)
+ next->p_data[0] = not_squish(next->p_data[0]);
+
+ /*
+ * if we encounter a not, then snag the next node and place
+ * it in the not's subplan. As an optimization we compress
+ * several not's to zero or one not.
+ */
+ if (next->type == N_NOT) {
+ int notlevel = 1;
+
+ node = yanknode(&plan);
+ while (node->type == N_NOT) {
+ ++notlevel;
+ node = yanknode(&plan);
+ }
+ if (node == NULL)
+ errx(1, "!: no following expression");
+ if (node->type == N_OR)
+ errx(1, "!: nothing between ! and -o");
+ if (notlevel % 2 != 1)
+ next = node;
+ else
+ next->p_data[0] = node;
+ }
+
+ /* add the node to our result plan */
+ if (result == NULL)
+ tail = result = next;
+ else {
+ tail->next = next;
+ tail = next;
+ }
+ tail->next = NULL;
+ }
+ return (result);
+}
+
+/*
+ * or_squish --
+ * compresses -o expressions in our search plan.
+ */
+PLAN *
+or_squish(plan)
+ PLAN *plan; /* plan with ors to be squished */
+{
+ register PLAN *next; /* next node being processed */
+ register PLAN *tail; /* pointer to tail of result plan */
+ PLAN *result; /* pointer to head of result plan */
+
+ tail = result = next = NULL;
+
+ while ((next = yanknode(&plan)) != NULL) {
+ /*
+ * if we encounter a ( expression ) then look for or's in
+ * the expr subplan.
+ */
+ if (next->type == N_EXPR)
+ next->p_data[0] = or_squish(next->p_data[0]);
+
+ /* if we encounter a not then look for not's in the subplan */
+ if (next->type == N_NOT)
+ next->p_data[0] = or_squish(next->p_data[0]);
+
+ /*
+ * if we encounter an or, then place our collected plan in the
+ * or's first subplan and then recursively collect the
+ * remaining stuff into the second subplan and return the or.
+ */
+ if (next->type == N_OR) {
+ if (result == NULL)
+ errx(1, "-o: no expression before -o");
+ next->p_data[0] = result;
+ next->p_data[1] = or_squish(plan);
+ if (next->p_data[1] == NULL)
+ errx(1, "-o: no expression after -o");
+ return (next);
+ }
+
+ /* add the node to our result plan */
+ if (result == NULL)
+ tail = result = next;
+ else {
+ tail->next = next;
+ tail = next;
+ }
+ tail->next = NULL;
+ }
+ return (result);
+}
diff --git a/usr.bin/find/option.c b/usr.bin/find/option.c
new file mode 100644
index 0000000..66596c3
--- /dev/null
+++ b/usr.bin/find/option.c
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Cimarron D. Taylor of the University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)option.c 8.2 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "find.h"
+
+static OPTION *option __P((char *));
+
+/* NB: the following table must be sorted lexically. */
+static OPTION const options[] = {
+ { "!", N_NOT, c_not, O_ZERO },
+ { "(", N_OPENPAREN, c_openparen, O_ZERO },
+ { ")", N_CLOSEPAREN, c_closeparen, O_ZERO },
+ { "-a", N_AND, NULL, O_NONE },
+ { "-and", N_AND, NULL, O_NONE },
+ { "-atime", N_ATIME, c_atime, O_ARGV },
+ { "-ctime", N_CTIME, c_ctime, O_ARGV },
+ { "-depth", N_DEPTH, c_depth, O_ZERO },
+ { "-exec", N_EXEC, c_exec, O_ARGVP },
+ { "-follow", N_FOLLOW, c_follow, O_ZERO },
+ { "-fstype", N_FSTYPE, c_fstype, O_ARGV },
+ { "-group", N_GROUP, c_group, O_ARGV },
+ { "-inum", N_INUM, c_inum, O_ARGV },
+ { "-links", N_LINKS, c_links, O_ARGV },
+ { "-ls", N_LS, c_ls, O_ZERO },
+ { "-mtime", N_MTIME, c_mtime, O_ARGV },
+ { "-name", N_NAME, c_name, O_ARGV },
+ { "-newer", N_NEWER, c_newer, O_ARGV },
+ { "-nogroup", N_NOGROUP, c_nogroup, O_ZERO },
+ { "-nouser", N_NOUSER, c_nouser, O_ZERO },
+ { "-o", N_OR, c_or, O_ZERO },
+ { "-ok", N_OK, c_exec, O_ARGVP },
+ { "-or", N_OR, c_or, O_ZERO },
+ { "-path", N_PATH, c_path, O_ARGV },
+ { "-perm", N_PERM, c_perm, O_ARGV },
+ { "-print", N_PRINT, c_print, O_ZERO },
+ { "-print0", N_PRINT0, c_print0, O_ZERO },
+ { "-prune", N_PRUNE, c_prune, O_ZERO },
+ { "-size", N_SIZE, c_size, O_ARGV },
+ { "-type", N_TYPE, c_type, O_ARGV },
+ { "-user", N_USER, c_user, O_ARGV },
+ { "-xdev", N_XDEV, c_xdev, O_ZERO },
+};
+
+/*
+ * find_create --
+ * create a node corresponding to a command line argument.
+ *
+ * TODO:
+ * add create/process function pointers to node, so we can skip
+ * this switch stuff.
+ */
+PLAN *
+find_create(argvp)
+ char ***argvp;
+{
+ register OPTION *p;
+ PLAN *new;
+ char **argv;
+
+ argv = *argvp;
+
+ if ((p = option(*argv)) == NULL)
+ errx(1, "%s: unknown option", *argv);
+ ++argv;
+ if (p->flags & (O_ARGV|O_ARGVP) && !*argv)
+ errx(1, "%s: requires additional arguments", *--argv);
+
+ switch(p->flags) {
+ case O_NONE:
+ new = NULL;
+ break;
+ case O_ZERO:
+ new = (p->create)();
+ break;
+ case O_ARGV:
+ new = (p->create)(*argv++);
+ break;
+ case O_ARGVP:
+ new = (p->create)(&argv, p->token == N_OK);
+ break;
+ default:
+ abort();
+ }
+ *argvp = argv;
+ return (new);
+}
+
+static OPTION *
+option(name)
+ char *name;
+{
+ OPTION tmp;
+ int typecompare __P((const void *, const void *));
+
+ tmp.name = name;
+ return ((OPTION *)bsearch(&tmp, options,
+ sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare));
+}
+
+int
+typecompare(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((OPTION *)a)->name, ((OPTION *)b)->name));
+}
diff --git a/usr.bin/finger/Makefile b/usr.bin/finger/Makefile
new file mode 100644
index 0000000..944f1d0
--- /dev/null
+++ b/usr.bin/finger/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= finger
+SRCS= finger.c lprint.c net.c sprint.c util.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/finger/extern.h b/usr.bin/finger/extern.h
new file mode 100644
index 0000000..2b675f1
--- /dev/null
+++ b/usr.bin/finger/extern.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+extern char tbuf[1024]; /* Temp buffer for anybody. */
+extern int entries; /* Number of people. */
+extern DB *db; /* Database. */
+
+void enter_lastlog __P((PERSON *));
+PERSON *enter_person __P((struct passwd *));
+void enter_where __P((struct utmp *, PERSON *));
+void err __P((const char *, ...));
+PERSON *find_person __P((char *));
+void lflag_print __P((void));
+int match __P((struct passwd *, char *));
+void netfinger __P((char *));
+PERSON *palloc __P((void));
+char *prphone __P((char *));
+void sflag_print __P((void));
diff --git a/usr.bin/finger/finger.1 b/usr.bin/finger/finger.1
new file mode 100644
index 0000000..6c64920
--- /dev/null
+++ b/usr.bin/finger/finger.1
@@ -0,0 +1,198 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)finger.1 8.2 (Berkeley) 2/16/94
+.\"
+.Dd February 16, 1994
+.Dt FINGER 1
+.Os BSD 4
+.Sh NAME
+.Nm finger
+.Nd user information lookup program
+.Sh SYNOPSIS
+.Nm finger
+.Op Fl lmpsho
+.Op Ar user ...
+.Op Ar user@host ...
+.Sh DESCRIPTION
+The
+.Nm finger
+displays information about the system users.
+.Pp
+Options are:
+.Bl -tag -width flag
+.It Fl s
+.Nm Finger
+displays the user's login name, real name, terminal name and write
+status (as a ``*'' before the terminal name if write permission is
+denied), idle time, login time, and either office location and office
+phone number, or the remote host. If
+.Fl h
+is given, the remote host is printed (the default.) If
+.Fl o
+is given, the office location and office phone number is printed
+instead.
+.Pp
+Idle time is in minutes if it is a single integer, hours and minutes
+if a ``:'' is present, or days if a ``d'' is present.
+Login time is displayed as the dayname if less than 6 days, else month, day;
+hours and minutes, unless more than six months ago, in which case the year
+is displayed rather than the hours and minutes.
+.Pp
+Unknown devices as well as nonexistent idle and login times are
+displayed as single asterisks.
+.Pp
+.It Fl h
+When used in conjunction with the
+.Fl s
+option, the name of the remote host is displayed instead of the office
+location and office phone.
+.Pp
+.It Fl o
+When used in conjunction with the
+.Fl s
+option, the office location and office phone information is displayed
+instead of the name of the remote host.
+.Pp
+.It Fl l
+Produces a multi-line format displaying all of the information
+described for the
+.Fl s
+option as well as the user's home directory, home phone number, login
+shell, mail status, and the contents of the files
+.Dq Pa .forward ,
+.Dq Pa .plan
+and
+.Dq Pa .project
+from the user's home directory.
+.Pp
+If idle time is at least a minute and less than a day, it is
+presented in the form ``hh:mm''.
+Idle times greater than a day are presented as ``d day[s]hh:mm''.
+.Pp
+Phone numbers specified as eleven digits are printed as ``+N-NNN-NNN-NNNN''.
+Numbers specified as ten or seven digits are printed as the appropriate
+subset of that string.
+Numbers specified as five digits are printed as ``xN-NNNN''.
+Numbers specified as four digits are printed as ``xNNNN''.
+.Pp
+If write permission is denied to the device, the phrase ``(messages off)''
+is appended to the line containing the device name.
+One entry per user is displayed with the
+.Fl l
+option; if a user is logged on multiple times, terminal information
+is repeated once per login.
+.Pp
+Mail status is shown as ``No Mail.'' if there is no mail at all, ``Mail
+last read DDD MMM ## HH:MM YYYY (TZ)'' if the person has looked at their
+mailbox since new mail arriving, or ``New mail received ...'', ``Unread
+since ...'' if they have new mail.
+.Pp
+.It Fl p
+Prevents
+the
+.Fl l
+option of
+.Nm finger
+from displaying the contents of the
+.Dq Pa .forward ,
+.Dq Pa .plan
+and
+.Dq Pa .project
+files.
+.It Fl m
+Prevent matching of
+.Ar user
+names.
+.Ar User
+is usually a login name; however, matching will also be done on the
+users' real names, unless the
+.Fl m
+option is supplied.
+All name matching performed by
+.Nm finger
+is case insensitive.
+.El
+.Pp
+If no options are specified,
+.Nm finger
+defaults to the
+.Fl l
+style output if operands are provided, otherwise to the
+.Fl s
+style.
+Note that some fields may be missing, in either format, if information
+is not available for them.
+.Pp
+If no arguments are specified,
+.Nm finger
+will print an entry for each user currently logged into the system.
+.Pp
+.Nm Finger
+may be used to look up users on a remote machine.
+The format is to specify a
+.Ar user
+as
+.Dq Li user@host ,
+or
+.Dq Li @host ,
+where the default output
+format for the former is the
+.Fl l
+style, and the default output format for the latter is the
+.Fl s
+style.
+The
+.Fl l
+option is the only option that may be passed to a remote machine.
+.Pp
+If the file
+.Dq Pa .nofinger
+exists in the user's home directory,
+.Nm finger
+behaves as if the user in question does not exist.
+.Sh ENVIRONMENT
+.Nm Finger
+utilizes the following environment variable, if it exists:
+.Bl -tag -width Fl
+.It Ev FINGER
+This variable may be set with favored options to
+.Nm finger .
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr w 1 ,
+.Xr who 1 ,
+.Sh HISTORY
+The
+.Nm finger
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/finger/finger.c b/usr.bin/finger/finger.c
new file mode 100644
index 0000000..bae5fe7
--- /dev/null
+++ b/usr.bin/finger/finger.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Luke Mewburn <lm@rmit.edu.au> added the following on 940622:
+ * - mail status ("No Mail", "Mail read:...", or "New Mail ...,
+ * Unread since ...".)
+ * - 4 digit phone extensions (3210 is printed as x3210.)
+ * - host/office toggling in short format with -h & -o.
+ * - short day names (`Tue' printed instead of `Jun 21' if the
+ * login time is < 6 days.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)finger.c 8.2 (Berkeley) 9/30/93";
+#endif /* not lint */
+
+/*
+ * Finger prints out information about users. It is not portable since
+ * certain fields (e.g. the full user name, office, and phone numbers) are
+ * extracted from the gecos field of the passwd file which other UNIXes
+ * may not have or may use for other things.
+ *
+ * There are currently two output formats; the short format is one line
+ * per user and displays login name, tty, login time, real name, idle time,
+ * and either remote host information (default) or office location/phone
+ * number, depending on if -h or -o is used respectively.
+ * The long format gives the same information (in a more legible format) as
+ * well as home directory, shell, mail info, and .plan/.project files.
+ */
+
+#include <sys/param.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <db.h>
+#include "finger.h"
+
+DB *db;
+time_t now;
+int entries, lflag, mflag, pplan, sflag, oflag;
+char tbuf[1024];
+
+static void loginlist __P((void));
+static void userlist __P((int, char **));
+
+int
+option(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ optind = 1; /* reset getopt */
+
+ while ((ch = getopt(argc, argv, "lmpsho")) != EOF)
+ switch(ch) {
+ case 'l':
+ lflag = 1; /* long format */
+ break;
+ case 'm':
+ mflag = 1; /* force exact match of names */
+ break;
+ case 'p':
+ pplan = 1; /* don't show .plan/.project */
+ break;
+ case 's':
+ sflag = 1; /* short format */
+ break;
+ case 'h':
+ oflag = 0; /* remote host info */
+ break;
+ case 'o':
+ oflag = 1; /* office info */
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: finger [-lmpsho] [login ...]\n");
+ exit(1);
+ }
+
+ return optind;
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, envargc, argcnt;
+ char *envargv[3];
+
+ /* remove this line to get remote host */
+ oflag = 1; /* default to old "office" behavior */
+
+ /*
+ * Process environment variables followed by command line arguments.
+ */
+ if ((envargv[1] = getenv("FINGER"))) {
+ envargc = 2;
+ envargv[0] = "finger";
+ envargv[2] = NULL;
+ (void) option(envargc, envargv);
+ }
+
+ argcnt = option(argc, argv);
+ argc -= argcnt;
+ argv += argcnt;
+
+ (void)time(&now);
+ setpassent(1);
+ if (!*argv) {
+ /*
+ * Assign explicit "small" format if no names given and -l
+ * not selected. Force the -s BEFORE we get names so proper
+ * screening will be done.
+ */
+ if (!lflag)
+ sflag = 1; /* if -l not explicit, force -s */
+ loginlist();
+ if (entries == 0)
+ (void)printf("No one logged on.\n");
+ } else {
+ userlist(argc, argv);
+ /*
+ * Assign explicit "large" format if names given and -s not
+ * explicitly stated. Force the -l AFTER we get names so any
+ * remote finger attempts specified won't be mishandled.
+ */
+ if (!sflag)
+ lflag = 1; /* if -s not explicit, force -l */
+ }
+ if (entries)
+ if (lflag)
+ lflag_print();
+ else
+ sflag_print();
+ exit(0);
+}
+
+static void
+loginlist()
+{
+ register PERSON *pn;
+ DBT data, key;
+ struct passwd *pw;
+ struct utmp user;
+ int r, sflag;
+ char name[UT_NAMESIZE + 1];
+
+ if (!freopen(_PATH_UTMP, "r", stdin))
+ err("%s: %s", _PATH_UTMP, strerror(errno));
+ name[UT_NAMESIZE] = NULL;
+ while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
+ if (!user.ut_name[0])
+ continue;
+ if ((pn = find_person(user.ut_name)) == NULL) {
+ bcopy(user.ut_name, name, UT_NAMESIZE);
+ if ((pw = getpwnam(name)) == NULL)
+ continue;
+ if (hide(pw))
+ continue;
+ pn = enter_person(pw);
+ }
+ enter_where(&user, pn);
+ }
+ if (db && lflag)
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err("db seq: %s", strerror(errno));
+ if (r == 1)
+ break;
+ enter_lastlog(*(PERSON **)data.data);
+ }
+}
+
+static void
+userlist(argc, argv)
+ register int argc;
+ register char **argv;
+{
+ register PERSON *pn;
+ DBT data, key;
+ struct utmp user;
+ struct passwd *pw;
+ int r, sflag, *used, *ip;
+ char **ap, **nargv, **np, **p;
+
+ if ((nargv = malloc((argc+1) * sizeof(char *))) == NULL ||
+ (used = calloc(argc, sizeof(int))) == NULL)
+ err("%s", strerror(errno));
+
+ /* Pull out all network requests. */
+ for (ap = p = argv, np = nargv; *p; ++p)
+ if (index(*p, '@'))
+ *np++ = *p;
+ else
+ *ap++ = *p;
+
+ *np++ = NULL;
+ *ap++ = NULL;
+
+ if (!*argv)
+ goto net;
+
+ /*
+ * Traverse the list of possible login names and check the login name
+ * and real name against the name specified by the user.
+ */
+ if (mflag)
+ for (p = argv; *p; ++p)
+ if ((pw = getpwnam(*p)) && !hide(pw))
+ enter_person(pw);
+ else
+ (void)fprintf(stderr,
+ "finger: %s: no such user\n", *p);
+ else {
+ while (pw = getpwent()) {
+ for (p = argv, ip = used; *p; ++p, ++ip)
+ if (match(pw, *p) && !hide(pw)) {
+ enter_person(pw);
+ *ip = 1;
+ }
+ }
+ for (p = argv, ip = used; *p; ++p, ++ip)
+ if (!*ip)
+ (void)fprintf(stderr,
+ "finger: %s: no such user\n", *p);
+ }
+
+ /* Handle network requests. */
+net: for (p = nargv; *p;) {
+ netfinger(*p++);
+ if (*p || entries)
+ printf("\n");
+ }
+
+ if (entries == 0)
+ return;
+
+ /*
+ * Scan thru the list of users currently logged in, saving
+ * appropriate data whenever a match occurs.
+ */
+ if (!freopen(_PATH_UTMP, "r", stdin))
+ err("%s: %s", _PATH_UTMP, strerror(errno));
+ while (fread((char *)&user, sizeof(user), 1, stdin) == 1) {
+ if (!user.ut_name[0])
+ continue;
+ if ((pn = find_person(user.ut_name)) == NULL)
+ continue;
+ enter_where(&user, pn);
+ }
+ if (db)
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err("db seq: %s", strerror(errno));
+ if (r == 1)
+ break;
+ enter_lastlog(*(PERSON **)data.data);
+ }
+}
diff --git a/usr.bin/finger/finger.h b/usr.bin/finger/finger.h
new file mode 100644
index 0000000..b8538fa
--- /dev/null
+++ b/usr.bin/finger/finger.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)finger.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef struct person {
+ uid_t uid; /* user id */
+ char *dir; /* user's home directory */
+ char *homephone; /* pointer to home phone no. */
+ char *name; /* login name */
+ char *office; /* pointer to office name */
+ char *officephone; /* pointer to office phone no. */
+ char *realname; /* pointer to full name */
+ char *shell; /* user's shell */
+ time_t mailread; /* last time mail was read */
+ time_t mailrecv; /* last time mail was received */
+ struct where *whead, *wtail; /* list of where user is or has been */
+} PERSON;
+
+enum status { LASTLOG, LOGGEDIN };
+
+typedef struct where {
+ struct where *next; /* next place user is or has been */
+ enum status info; /* type/status of request */
+ short writable; /* tty is writable */
+ time_t loginat; /* time of (last) login */
+ time_t idletime; /* how long idle (if logged in) */
+ char tty[UT_LINESIZE+1]; /* null terminated tty line */
+ char host[UT_HOSTSIZE+1]; /* null terminated remote host name */
+} WHERE;
+
+#include "extern.h"
diff --git a/usr.bin/finger/lprint.c b/usr.bin/finger/lprint.c
new file mode 100644
index 0000000..93e4a9b
--- /dev/null
+++ b/usr.bin/finger/lprint.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lprint.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <time.h>
+#include <db.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <paths.h>
+#include "finger.h"
+
+#define LINE_LEN 80
+#define TAB_LEN 8 /* 8 spaces between tabs */
+#define _PATH_FORWARD ".forward"
+#define _PATH_PLAN ".plan"
+#define _PATH_PROJECT ".project"
+
+static int demi_print __P((char *, int));
+static void lprint __P((PERSON *));
+static int show_text __P((char *, char *, char *));
+static void vputc __P((int));
+
+void
+lflag_print()
+{
+ extern int pplan;
+ register PERSON *pn;
+ register int sflag, r;
+ DBT data, key;
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err("db seq: %s", strerror(errno));
+ if (r == 1)
+ break;
+ pn = *(PERSON **)data.data;
+ if (sflag != R_FIRST)
+ putchar('\n');
+ lprint(pn);
+ if (!pplan) {
+ (void)show_text(pn->dir,
+ _PATH_FORWARD, "Mail forwarded to");
+ (void)show_text(pn->dir, _PATH_PROJECT, "Project");
+ if (!show_text(pn->dir, _PATH_PLAN, "Plan"))
+ (void)printf("No Plan.\n");
+ }
+ }
+}
+
+static void
+lprint(pn)
+ register PERSON *pn;
+{
+ extern time_t now;
+ register struct tm *delta;
+ register WHERE *w;
+ register int cpr, len, maxlen;
+ struct tm *tp;
+ int oddfield;
+ char *tzn;
+ char t[80];
+
+ /*
+ * long format --
+ * login name
+ * real name
+ * home directory
+ * shell
+ * office, office phone, home phone if available
+ * mail status
+ */
+ (void)printf("Login: %-15s\t\t\tName: %s\nDirectory: %-25s",
+ pn->name, pn->realname, pn->dir);
+ (void)printf("\tShell: %-s\n", *pn->shell ? pn->shell : _PATH_BSHELL);
+
+ /*
+ * try and print office, office phone, and home phone on one line;
+ * if that fails, do line filling so it looks nice.
+ */
+#define OFFICE_TAG "Office"
+#define OFFICE_PHONE_TAG "Office Phone"
+ oddfield = 0;
+ if (pn->office && pn->officephone &&
+ strlen(pn->office) + strlen(pn->officephone) +
+ sizeof(OFFICE_TAG) + 2 <= 5 * TAB_LEN) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s, %s",
+ OFFICE_TAG, pn->office, prphone(pn->officephone));
+ oddfield = demi_print(tbuf, oddfield);
+ } else {
+ if (pn->office) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s",
+ OFFICE_TAG, pn->office);
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ if (pn->officephone) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s",
+ OFFICE_PHONE_TAG, prphone(pn->officephone));
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ }
+ if (pn->homephone) {
+ (void)snprintf(tbuf, sizeof(tbuf), "%s: %s", "Home Phone",
+ prphone(pn->homephone));
+ oddfield = demi_print(tbuf, oddfield);
+ }
+ if (oddfield)
+ putchar('\n');
+
+ /*
+ * long format con't:
+ * if logged in
+ * terminal
+ * idle time
+ * if messages allowed
+ * where logged in from
+ * if not logged in
+ * when last logged in
+ */
+ /* find out longest device name for this user for formatting */
+ for (w = pn->whead, maxlen = -1; w != NULL; w = w->next)
+ if ((len = strlen(w->tty)) > maxlen)
+ maxlen = len;
+ /* find rest of entries for user */
+ for (w = pn->whead; w != NULL; w = w->next) {
+ switch (w->info) {
+ case LOGGEDIN:
+ tp = localtime(&w->loginat);
+ strftime(t, sizeof(t), "%c", tp);
+ tzn = tp->tm_zone;
+ cpr = printf("On since %.16s (%s) on %s",
+ t, tzn, w->tty);
+ /*
+ * idle time is tough; if have one, print a comma,
+ * then spaces to pad out the device name, then the
+ * idle time. Follow with a comma if a remote login.
+ */
+ delta = gmtime(&w->idletime);
+ if (delta->tm_yday || delta->tm_hour || delta->tm_min) {
+ cpr += printf("%-*s idle ",
+ maxlen - strlen(w->tty) + 1, ",");
+ if (delta->tm_yday > 0) {
+ cpr += printf("%d day%s ",
+ delta->tm_yday,
+ delta->tm_yday == 1 ? "" : "s");
+ }
+ cpr += printf("%d:%02d",
+ delta->tm_hour, delta->tm_min);
+ if (*w->host) {
+ putchar(',');
+ ++cpr;
+ }
+ }
+ if (!w->writable)
+ cpr += printf(" (messages off)");
+ break;
+ case LASTLOG:
+ if (w->loginat == 0) {
+ (void)printf("Never logged in.");
+ break;
+ }
+ tp = localtime(&w->loginat);
+ strftime(t, sizeof(t), "%c", tp);
+ tzn = tp->tm_zone;
+ if (now - w->loginat > 86400 * 365 / 2)
+ cpr =
+ printf("Last login %.16s %.4s (%s) on %s",
+ t, t + 20, tzn, w->tty);
+ else
+ cpr = printf("Last login %.16s (%s) on %s",
+ t, tzn, w->tty);
+ break;
+ }
+ if (*w->host) {
+ if (LINE_LEN < (cpr + 6 + strlen(w->host)))
+ (void)printf("\n ");
+ (void)printf(" from %s", w->host);
+ }
+ putchar('\n');
+ }
+ if (pn->mailrecv == -1)
+ printf("No Mail.\n");
+ else if (pn->mailrecv > pn->mailread) {
+ tp = localtime(&pn->mailrecv);
+ strftime(t, sizeof(t), "%c", tp);
+ tzn = tp->tm_zone;
+ printf("New mail received %.16s %.4s (%s)\n", t, t + 20, tzn);
+ tp = localtime(&pn->mailread);
+ strftime(t, sizeof(t), "%c", tp);
+ tzn = tp->tm_zone;
+ printf(" Unread since %.16s %.4s (%s)\n", t, t + 20, tzn);
+ } else {
+ tp = localtime(&pn->mailread);
+ strftime(t, sizeof(t), "%c", tp);
+ tzn = tp->tm_zone;
+ printf("Mail last read %.16s %.4s (%s)\n", t, t + 20, tzn);
+ }
+}
+
+static int
+demi_print(str, oddfield)
+ char *str;
+ int oddfield;
+{
+ static int lenlast;
+ int lenthis, maxlen;
+
+ lenthis = strlen(str);
+ if (oddfield) {
+ /*
+ * We left off on an odd number of fields. If we haven't
+ * crossed the midpoint of the screen, and we have room for
+ * the next field, print it on the same line; otherwise,
+ * print it on a new line.
+ *
+ * Note: we insist on having the right hand fields start
+ * no less than 5 tabs out.
+ */
+ maxlen = 5 * TAB_LEN;
+ if (maxlen < lenlast)
+ maxlen = lenlast;
+ if (((((maxlen / TAB_LEN) + 1) * TAB_LEN) +
+ lenthis) <= LINE_LEN) {
+ while(lenlast < (4 * TAB_LEN)) {
+ putchar('\t');
+ lenlast += TAB_LEN;
+ }
+ (void)printf("\t%s\n", str); /* force one tab */
+ } else {
+ (void)printf("\n%s", str); /* go to next line */
+ oddfield = !oddfield; /* this'll be undone below */
+ }
+ } else
+ (void)printf("%s", str);
+ oddfield = !oddfield; /* toggle odd/even marker */
+ lenlast = lenthis;
+ return(oddfield);
+}
+
+static int
+show_text(directory, file_name, header)
+ char *directory, *file_name, *header;
+{
+ struct stat sb;
+ register FILE *fp;
+ register int ch, cnt, lastc;
+ register char *p;
+ int fd, nr;
+
+ (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", directory, file_name);
+ if ((fd = open(tbuf, O_RDONLY)) < 0 || fstat(fd, &sb) ||
+ sb.st_size == 0)
+ return(0);
+
+ /* If short enough, and no newlines, show it on a single line.*/
+ if (sb.st_size <= LINE_LEN - strlen(header) - 5) {
+ nr = read(fd, tbuf, sizeof(tbuf));
+ if (nr <= 0) {
+ (void)close(fd);
+ return(0);
+ }
+ for (p = tbuf, cnt = nr; cnt--; ++p)
+ if (*p == '\n')
+ break;
+ if (cnt <= 1) {
+ (void)printf("%s: ", header);
+ for (p = tbuf, cnt = nr; cnt--; ++p)
+ vputc(lastc = *p);
+ if (lastc != '\n')
+ (void)putchar('\n');
+ (void)close(fd);
+ return(1);
+ }
+ else
+ (void)lseek(fd, 0L, SEEK_SET);
+ }
+ if ((fp = fdopen(fd, "r")) == NULL)
+ return(0);
+ (void)printf("%s:\n", header);
+ while ((ch = getc(fp)) != EOF)
+ vputc(lastc = ch);
+ if (lastc != '\n')
+ (void)putchar('\n');
+ (void)fclose(fp);
+ return(1);
+}
+
+static void
+vputc(ch)
+ register int ch;
+{
+ int meta;
+
+ if (!isprint(ch) && !isascii(ch)) {
+ (void)putchar('M');
+ (void)putchar('-');
+ ch = toascii(ch);
+ meta = 1;
+ } else
+ meta = 0;
+ if (isprint(ch) || !meta && (ch == ' ' || ch == '\t' || ch == '\n'))
+ (void)putchar(ch);
+ else {
+ (void)putchar('^');
+ (void)putchar(ch == '\177' ? '?' : ch | 0100);
+ }
+}
diff --git a/usr.bin/finger/net.c b/usr.bin/finger/net.c
new file mode 100644
index 0000000..ee83d58
--- /dev/null
+++ b/usr.bin/finger/net.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)net.c 8.3 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <db.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "finger.h"
+
+void
+netfinger(name)
+ char *name;
+{
+ extern int lflag;
+ register FILE *fp;
+ register int c, lastc;
+ struct in_addr defaddr;
+ struct hostent *hp, def;
+ struct servent *sp;
+ struct sockaddr_in sin;
+ int s;
+ char *alist[1], *host;
+
+ if (!(host = rindex(name, '@')))
+ return;
+ *host++ = NULL;
+ if (isdigit(*host) && (defaddr.s_addr = inet_addr(host)) != -1) {
+ def.h_name = host;
+ def.h_addr_list = alist;
+ def.h_addr = (char *)&defaddr;
+ def.h_length = sizeof(struct in_addr);
+ def.h_addrtype = AF_INET;
+ def.h_aliases = 0;
+ hp = &def;
+ } else if (!(hp = gethostbyname(host))) {
+ (void)fprintf(stderr,
+ "finger: unknown host: %s\n", host);
+ return;
+ }
+ if (!(sp = getservbyname("finger", "tcp"))) {
+ (void)fprintf(stderr, "finger: tcp/finger: unknown service\n");
+ return;
+ }
+ sin.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
+ sin.sin_port = sp->s_port;
+ if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
+ perror("finger: socket");
+ return;
+ }
+
+ /* have network connection; identify the host connected with */
+ (void)printf("[%s]\n", hp->h_name);
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("finger: connect");
+ (void)close(s);
+ return;
+ }
+
+ /* -l flag for remote fingerd */
+ if (lflag)
+ write(s, "/W ", 3);
+ /* send the name followed by <CR><LF> */
+ (void)write(s, name, strlen(name));
+ (void)write(s, "\r\n", 2);
+
+ /*
+ * Read from the remote system; once we're connected, we assume some
+ * data. If none arrives, we hang until the user interrupts.
+ *
+ * If we see a <CR> or a <CR> with the high bit set, treat it as
+ * a newline; if followed by a newline character, only output one
+ * newline.
+ *
+ * Otherwise, all high bits are stripped; if it isn't printable and
+ * it isn't a space, we can simply set the 7th bit. Every ASCII
+ * character with bit 7 set is printable.
+ */
+ if (fp = fdopen(s, "r"))
+ while ((c = getc(fp)) != EOF) {
+ c &= 0x7f;
+ if (c == 0x0d) {
+ if (lastc == '\r') /* ^M^M - skip dupes */
+ continue;
+ c = '\n';
+ lastc = '\r';
+ } else {
+ if (!isprint(c) && !isspace(c))
+ c |= 0x40;
+ if (lastc != '\r' || c != '\n')
+ lastc = c;
+ else {
+ lastc = '\n';
+ continue;
+ }
+ }
+ putchar(c);
+ }
+ if (lastc != '\n')
+ putchar('\n');
+ (void)fclose(fp);
+}
diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c
new file mode 100644
index 0000000..3f1fc91
--- /dev/null
+++ b/usr.bin/finger/sprint.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sprint.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <db.h>
+#include <pwd.h>
+#include <errno.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "finger.h"
+
+static void stimeprint __P((WHERE *));
+
+void
+sflag_print()
+{
+ extern time_t now;
+ extern int oflag;
+ register PERSON *pn;
+ register WHERE *w;
+ register int sflag, r, namelen;
+ char p[80];
+ DBT data, key;
+
+ /*
+ * short format --
+ * login name
+ * real name
+ * terminal name (the XX of ttyXX)
+ * if terminal writeable (add an '*' to the terminal name
+ * if not)
+ * if logged in show idle time and day logged in, else
+ * show last login date and time.
+ * If > 6 months, show year instead of time.
+ * if (-o)
+ * office location
+ * office phone
+ * else
+ * remote host
+ */
+#define MAXREALNAME 20
+#define MAXHOSTNAME 20 /* in reality, hosts are never longer than 16 */
+ (void)printf("%-*s %-*s%s %s\n", UT_NAMESIZE, "Login", MAXREALNAME,
+ "Name", " TTY Idle Login Time",
+ oflag ? " Office Office Phone" : " Where");
+
+ for (sflag = R_FIRST;; sflag = R_NEXT) {
+ r = (*db->seq)(db, &key, &data, sflag);
+ if (r == -1)
+ err("db seq: %s", strerror(errno));
+ if (r == 1)
+ break;
+ pn = *(PERSON **)data.data;
+
+ for (w = pn->whead; w != NULL; w = w->next) {
+ namelen = MAXREALNAME;
+ if (w->info == LOGGEDIN && !w->writable)
+ --namelen; /* leave space before `*' */
+ (void)printf("%-*.*s %-*.*s", UT_NAMESIZE, UT_NAMESIZE,
+ pn->name, MAXREALNAME, namelen,
+ pn->realname ? pn->realname : "");
+ if (!w->loginat) {
+ (void)printf(" * * No logins ");
+ goto office;
+ }
+ (void)putchar(w->info == LOGGEDIN && !w->writable ?
+ '*' : ' ');
+ if (*w->tty)
+ (void)printf("%-3.3s ",
+ (strncmp(w->tty, "tty", 3)
+ && strncmp(w->tty, "cua", 3))
+ ? w->tty : w->tty + 3);
+ else
+ (void)printf(" ");
+ if (w->info == LOGGEDIN) {
+ stimeprint(w);
+ (void)printf(" ");
+ } else
+ (void)printf(" * ");
+ strftime(p, sizeof(p), "%c", localtime(&w->loginat));
+#define SECSPERDAY 86400
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+ if (now - w->loginat < SECSPERDAY * (DAYSPERWEEK - 1))
+ (void)printf("%.3s ", p);
+ else
+ (void)printf("%.6s", p + 4);
+ if (now - w->loginat >= SECSPERDAY * DAYSPERNYEAR / 2)
+ (void)printf(" %.4s", p + 20);
+ else
+ (void)printf(" %.5s", p + 11);
+office: if (oflag) {
+ if (pn->office)
+ (void)printf(" %-10.10s", pn->office);
+ else if (pn->officephone)
+ (void)printf(" %-10.10s", " ");
+ if (pn->officephone)
+ (void)printf(" %-.15s",
+ prphone(pn->officephone));
+ } else
+ (void)printf(" %.*s", MAXHOSTNAME, w->host);
+ putchar('\n');
+ }
+ }
+}
+
+static void
+stimeprint(w)
+ WHERE *w;
+{
+ register struct tm *delta;
+
+ delta = gmtime(&w->idletime);
+ if (!delta->tm_yday)
+ if (!delta->tm_hour)
+ if (!delta->tm_min)
+ (void)printf(" ");
+ else
+ (void)printf("%5d", delta->tm_min);
+ else
+ (void)printf("%2d:%02d",
+ delta->tm_hour, delta->tm_min);
+ else
+ (void)printf("%4dd", delta->tm_yday);
+}
diff --git a/usr.bin/finger/util.c b/usr.bin/finger/util.c
new file mode 100644
index 0000000..da1bb40
--- /dev/null
+++ b/usr.bin/finger/util.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <db.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+#include <errno.h>
+#include "finger.h"
+
+static void find_idle_and_ttywrite __P((WHERE *));
+static void userinfo __P((PERSON *, struct passwd *));
+static WHERE *walloc __P((PERSON *));
+
+int
+match(pw, user)
+ struct passwd *pw;
+ char *user;
+{
+ register char *p, *t;
+ char name[1024];
+
+ if (!strcasecmp(pw->pw_name, user))
+ return(1);
+
+ /*
+ * XXX
+ * Why do we skip asterisks!?!?
+ */
+ (void)strcpy(p = tbuf, pw->pw_gecos);
+ if (*p == '*')
+ ++p;
+
+ /* Ampersands get replaced by the login name. */
+ if ((p = strtok(p, ",")) == NULL)
+ return(0);
+
+ for (t = name; *t = *p; ++p)
+ if (*t == '&') {
+ (void)strcpy(t, pw->pw_name);
+ while (*++t);
+ }
+ else
+ ++t;
+ for (t = name; p = strtok(t, "\t "); t = NULL)
+ if (!strcasecmp(p, user))
+ return(1);
+ return(0);
+}
+
+void
+enter_lastlog(pn)
+ register PERSON *pn;
+{
+ register WHERE *w;
+ static int opened, fd;
+ struct lastlog ll;
+ char doit = 0;
+
+ /* some systems may not maintain lastlog, don't report errors. */
+ if (!opened) {
+ fd = open(_PATH_LASTLOG, O_RDONLY, 0);
+ opened = 1;
+ }
+ if (fd == -1 ||
+ lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) !=
+ (long)pn->uid * sizeof(ll) ||
+ read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
+ /* as if never logged in */
+ ll.ll_line[0] = ll.ll_host[0] = NULL;
+ ll.ll_time = 0;
+ }
+ if ((w = pn->whead) == NULL)
+ doit = 1;
+ else if (ll.ll_time != 0) {
+ /* if last login is earlier than some current login */
+ for (; !doit && w != NULL; w = w->next)
+ if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
+ doit = 1;
+ /*
+ * and if it's not any of the current logins
+ * can't use time comparison because there may be a small
+ * discrepency since login calls time() twice
+ */
+ for (w = pn->whead; doit && w != NULL; w = w->next)
+ if (w->info == LOGGEDIN &&
+ strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
+ doit = 0;
+ }
+ if (doit) {
+ w = walloc(pn);
+ w->info = LASTLOG;
+ bcopy(ll.ll_line, w->tty, UT_LINESIZE);
+ w->tty[UT_LINESIZE] = 0;
+ bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
+ w->host[UT_HOSTSIZE] = 0;
+ w->loginat = ll.ll_time;
+ }
+}
+
+void
+enter_where(ut, pn)
+ struct utmp *ut;
+ PERSON *pn;
+{
+ register WHERE *w;
+
+ w = walloc(pn);
+ w->info = LOGGEDIN;
+ bcopy(ut->ut_line, w->tty, UT_LINESIZE);
+ w->tty[UT_LINESIZE] = 0;
+ bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
+ w->host[UT_HOSTSIZE] = 0;
+ w->loginat = (time_t)ut->ut_time;
+ find_idle_and_ttywrite(w);
+}
+
+PERSON *
+enter_person(pw)
+ register struct passwd *pw;
+{
+ DBT data, key;
+ PERSON *pn;
+
+ if (db == NULL &&
+ (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
+ err("%s", strerror(errno));
+
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ switch((*db->get)(db, &key, &data, 0)) {
+ case 0:
+ return(*(PERSON **)data.data);
+ default:
+ case -1:
+ err("db get: %s", strerror(errno));
+ /* NOTREACHED */
+ case 1:
+ ++entries;
+ pn = palloc();
+ userinfo(pn, pw);
+ pn->whead = NULL;
+
+ data.size = sizeof(PERSON *);
+ data.data = &pn;
+ if ((*db->put)(db, &key, &data, 0))
+ err("%s", strerror(errno));
+ return(pn);
+ }
+}
+
+PERSON *
+find_person(name)
+ char *name;
+{
+ struct passwd *pw;
+
+ register int cnt;
+ DBT data, key;
+ char buf[UT_NAMESIZE + 1];
+
+ if (!db)
+ return(NULL);
+
+ if ((pw = getpwnam(name)) && hide(pw))
+ return(NULL);
+
+ /* Name may be only UT_NAMESIZE long and not NUL terminated. */
+ for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt)
+ buf[cnt] = *name;
+ buf[cnt] = '\0';
+ key.data = buf;
+ key.size = cnt;
+
+ return((*db->get)(db, &key, &data, 0) ? NULL : *(PERSON **)data.data);
+}
+
+PERSON *
+palloc()
+{
+ PERSON *p;
+
+ if ((p = malloc((u_int) sizeof(PERSON))) == NULL)
+ err("%s", strerror(errno));
+ return(p);
+}
+
+static WHERE *
+walloc(pn)
+ register PERSON *pn;
+{
+ register WHERE *w;
+
+ if ((w = malloc((u_int) sizeof(WHERE))) == NULL)
+ err("%s", strerror(errno));
+ if (pn->whead == NULL)
+ pn->whead = pn->wtail = w;
+ else {
+ pn->wtail->next = w;
+ pn->wtail = w;
+ }
+ w->next = NULL;
+ return(w);
+}
+
+char *
+prphone(num)
+ char *num;
+{
+ register char *p;
+ int len;
+ static char pbuf[15];
+
+ /* don't touch anything if the user has their own formatting */
+ for (p = num; *p; ++p)
+ if (!isdigit(*p))
+ return(num);
+ len = p - num;
+ p = pbuf;
+ switch(len) {
+ case 11: /* +0-123-456-7890 */
+ *p++ = '+';
+ *p++ = *num++;
+ *p++ = '-';
+ /* FALLTHROUGH */
+ case 10: /* 012-345-6789 */
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = '-';
+ /* FALLTHROUGH */
+ case 7: /* 012-3456 */
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ break;
+ case 5: /* x0-1234 */
+ case 4: /* x1234 */
+ *p++ = 'x';
+ *p++ = *num++;
+ break;
+ default:
+ return(num);
+ }
+ if (len != 4) {
+ *p++ = '-';
+ *p++ = *num++;
+ }
+ *p++ = *num++;
+ *p++ = *num++;
+ *p++ = *num++;
+ *p = '\0';
+ return(pbuf);
+}
+
+static void
+find_idle_and_ttywrite(w)
+ register WHERE *w;
+{
+ extern time_t now;
+ struct stat sb;
+
+ (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
+ if (stat(tbuf, &sb) < 0) {
+ (void)fprintf(stderr,
+ "finger: %s: %s\n", tbuf, strerror(errno));
+ return;
+ }
+ w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
+
+#define TALKABLE 0220 /* tty is writable if 220 mode */
+ w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
+}
+
+static void
+userinfo(pn, pw)
+ register PERSON *pn;
+ register struct passwd *pw;
+{
+ register char *p, *t;
+ char *bp, name[1024];
+ struct stat sb;
+
+ pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
+
+ pn->uid = pw->pw_uid;
+ pn->name = strdup(pw->pw_name);
+ pn->dir = strdup(pw->pw_dir);
+ pn->shell = strdup(pw->pw_shell);
+
+ /* why do we skip asterisks!?!? */
+ (void)strcpy(bp = tbuf, pw->pw_gecos);
+ if (*bp == '*')
+ ++bp;
+
+ /* ampersands get replaced by the login name */
+ if (!(p = strsep(&bp, ",")))
+ return;
+ for (t = name; *t = *p; ++p)
+ if (*t == '&') {
+ (void)strcpy(t, pw->pw_name);
+ if (islower(*t))
+ *t = toupper(*t);
+ while (*++t);
+ }
+ else
+ ++t;
+ pn->realname = strdup(name);
+ pn->office = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+ pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+ pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
+ strdup(p) : NULL;
+ (void)sprintf(tbuf,"%s/%s", _PATH_MAILDIR, pw->pw_name);
+ pn->mailrecv = -1; /* -1 == not_valid */
+ if (stat(tbuf, &sb) < 0) {
+ if (errno != ENOENT) {
+ (void)fprintf(stderr,
+ "finger: %s: %s\n", tbuf, strerror(errno));
+ return;
+ }
+ } else if (sb.st_size != 0) {
+ pn->mailrecv = sb.st_mtime;
+ pn->mailread = sb.st_atime;
+ }
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "finger: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+/*
+ * Is this user hiding from finger?
+ * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide).
+ */
+
+int
+hide(pw)
+ struct passwd *pw;
+{
+ int fd;
+ char buf[MAXPATHLEN+1];
+
+ if (!pw->pw_dir)
+ return 0;
+
+ sprintf (buf, "%s/.nofinger", pw->pw_dir);
+
+ if (access (buf, F_OK) == 0)
+ return 1;
+
+ return 0;
+}
diff --git a/usr.bin/fmt/Makefile b/usr.bin/fmt/Makefile
new file mode 100644
index 0000000..7a51add
--- /dev/null
+++ b/usr.bin/fmt/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= fmt
+SRCS= fmt.c head.c
+.PATH: ${.CURDIR}/../mail
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fmt/fmt.1 b/usr.bin/fmt/fmt.1
new file mode 100644
index 0000000..4c76cc7
--- /dev/null
+++ b/usr.bin/fmt/fmt.1
@@ -0,0 +1,89 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fmt.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt FMT 1
+.Os
+.Sh NAME
+.Nm fmt
+.Nd simple text formatter
+.Sh SYNOPSIS
+.Nm fmt
+.Oo
+.Ar goal
+.Op Ar maximum
+.Oc
+.Op name ...
+.Sh DESCRIPTION
+.Nm Fmt
+is a simple text formatter which reads the concatenation of input
+files (or standard input if none are given) and produces on standard
+output a version of its input with lines as close to the
+.Ar goal
+length
+as possible without exceeding the maximum. The
+.Ar goal
+length defaults
+to 65 and the maximum to 75. The spacing at the beginning of the
+input lines is preserved in the output, as are blank lines and
+interword spacing.
+.Pp
+.Nm Fmt
+is meant to format mail messages prior to sending, but may also be useful
+for other simple tasks.
+For instance,
+within visual mode of the
+.Xr ex 1
+editor (e.g.
+.Xr vi 1 )
+the command
+.Pp
+.Dl \&!}fmt
+.Pp
+will reformat a paragraph,
+evening the lines.
+.Sh SEE ALSO
+.Xr nroff 1 ,
+.Xr mail 1
+.Sh HISTORY
+The
+.Nm fmt
+command appeared in
+.Bx 3 .
+.\" .Sh AUTHOR
+.\" Kurt Shoens
+.\" .br
+.\" Liz Allen (added goal length concept)
+.Sh BUGS
+The program was designed to be simple and fast \- for more complex
+operations, the standard text processors are likely to be more appropriate.
diff --git a/usr.bin/fmt/fmt.c b/usr.bin/fmt/fmt.c
new file mode 100644
index 0000000..6d689c1
--- /dev/null
+++ b/usr.bin/fmt/fmt.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fmt.c 8.1 (Berkeley) 7/20/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * fmt -- format the concatenation of input files or standard input
+ * onto standard output. Designed for use with Mail ~|
+ *
+ * Syntax : fmt [ goal [ max ] ] [ name ... ]
+ * Authors: Kurt Shoens (UCB) 12/7/78;
+ * Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
+ */
+
+/* LIZ@UOM 6/18/85 -- Don't need LENGTH any more.
+ * #define LENGTH 72 Max line length in output
+ */
+#define NOSTR ((char *) 0) /* Null string pointer for lint */
+
+/* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
+#define GOAL_LENGTH 65
+#define MAX_LENGTH 75
+int goal_length; /* Target or goal line length in output */
+int max_length; /* Max line length in output */
+int pfx; /* Current leading blank count */
+int lineno; /* Current input line */
+int mark; /* Last place we saw a head line */
+
+char *malloc(); /* for lint . . . */
+char *headnames[] = {"To", "Subject", "Cc", 0};
+
+/*
+ * Drive the whole formatter by managing input files. Also,
+ * cause initialization of the output stuff and flush it out
+ * at the end.
+ */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register FILE *fi;
+ register int errs = 0;
+ int number; /* LIZ@UOM 6/18/85 */
+
+ goal_length = GOAL_LENGTH;
+ max_length = MAX_LENGTH;
+ setout();
+ lineno = 1;
+ mark = -10;
+ /*
+ * LIZ@UOM 6/18/85 -- Check for goal and max length arguments
+ */
+ if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
+ argv++;
+ argc--;
+ goal_length = number;
+ if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
+ argv++;
+ argc--;
+ max_length = number;
+ }
+ }
+ if (max_length <= goal_length) {
+ fprintf(stderr, "Max length must be greater than %s\n",
+ "goal length");
+ exit(1);
+ }
+ if (argc < 2) {
+ fmt(stdin);
+ oflush();
+ exit(0);
+ }
+ while (--argc) {
+ if ((fi = fopen(*++argv, "r")) == NULL) {
+ perror(*argv);
+ errs++;
+ continue;
+ }
+ fmt(fi);
+ fclose(fi);
+ }
+ oflush();
+ exit(errs);
+}
+
+/*
+ * Read up characters from the passed input file, forming lines,
+ * doing ^H processing, expanding tabs, stripping trailing blanks,
+ * and sending each line down for analysis.
+ */
+fmt(fi)
+ FILE *fi;
+{
+ char linebuf[BUFSIZ], canonb[BUFSIZ];
+ register char *cp, *cp2, cc;
+ register int c, col;
+
+ c = getc(fi);
+ while (c != EOF) {
+ /*
+ * Collect a line, doing ^H processing.
+ * Leave tabs for now.
+ */
+ cp = linebuf;
+ while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
+ if (c == '\b') {
+ if (cp > linebuf)
+ cp--;
+ c = getc(fi);
+ continue;
+ }
+ if (!isprint(c) && c != '\t') {
+ c = getc(fi);
+ continue;
+ }
+ *cp++ = c;
+ c = getc(fi);
+ }
+ *cp = '\0';
+
+ /*
+ * Toss anything remaining on the input line.
+ */
+ while (c != '\n' && c != EOF)
+ c = getc(fi);
+
+ /*
+ * Expand tabs on the way to canonb.
+ */
+ col = 0;
+ cp = linebuf;
+ cp2 = canonb;
+ while (cc = *cp++) {
+ if (cc != '\t') {
+ col++;
+ if (cp2-canonb < BUFSIZ-1)
+ *cp2++ = cc;
+ continue;
+ }
+ do {
+ if (cp2-canonb < BUFSIZ-1)
+ *cp2++ = ' ';
+ col++;
+ } while ((col & 07) != 0);
+ }
+
+ /*
+ * Swipe trailing blanks from the line.
+ */
+ for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
+ ;
+ *++cp2 = '\0';
+ prefix(canonb);
+ if (c != EOF)
+ c = getc(fi);
+ }
+}
+
+/*
+ * Take a line devoid of tabs and other garbage and determine its
+ * blank prefix. If the indent changes, call for a linebreak.
+ * If the input line is blank, echo the blank line on the output.
+ * Finally, if the line minus the prefix is a mail header, try to keep
+ * it on a line by itself.
+ */
+prefix(line)
+ char line[];
+{
+ register char *cp, **hp;
+ register int np, h;
+
+ if (!*line) {
+ oflush();
+ putchar('\n');
+ return;
+ }
+ for (cp = line; *cp == ' '; cp++)
+ ;
+ np = cp - line;
+
+ /*
+ * The following horrible expression attempts to avoid linebreaks
+ * when the indent changes due to a paragraph.
+ */
+ if (np != pfx && (np > pfx || abs(pfx-np) > 8))
+ oflush();
+ if (h = ishead(cp))
+ oflush(), mark = lineno;
+ if (lineno - mark < 3 && lineno - mark > 0)
+ for (hp = &headnames[0]; *hp != (char *) 0; hp++)
+ if (ispref(*hp, cp)) {
+ h = 1;
+ oflush();
+ break;
+ }
+ if (!h && (h = (*cp == '.')))
+ oflush();
+ pfx = np;
+ if (h)
+ pack(cp, strlen(cp));
+ else split(cp);
+ if (h)
+ oflush();
+ lineno++;
+}
+
+/*
+ * Split up the passed line into output "words" which are
+ * maximal strings of non-blanks with the blank separation
+ * attached at the end. Pass these words along to the output
+ * line packer.
+ */
+split(line)
+ char line[];
+{
+ register char *cp, *cp2;
+ char word[BUFSIZ];
+ int wordl; /* LIZ@UOM 6/18/85 */
+
+ cp = line;
+ while (*cp) {
+ cp2 = word;
+ wordl = 0; /* LIZ@UOM 6/18/85 */
+
+ /*
+ * Collect a 'word,' allowing it to contain escaped white
+ * space.
+ */
+ while (*cp && *cp != ' ') {
+ if (*cp == '\\' && isspace(cp[1]))
+ *cp2++ = *cp++;
+ *cp2++ = *cp++;
+ wordl++;/* LIZ@UOM 6/18/85 */
+ }
+
+ /*
+ * Guarantee a space at end of line. Two spaces after end of
+ * sentence punctuation.
+ */
+ if (*cp == '\0') {
+ *cp2++ = ' ';
+ if (index(".:!", cp[-1]))
+ *cp2++ = ' ';
+ }
+ while (*cp == ' ')
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+ /*
+ * LIZ@UOM 6/18/85 pack(word);
+ */
+ pack(word, wordl);
+ }
+}
+
+/*
+ * Output section.
+ * Build up line images from the words passed in. Prefix
+ * each line with correct number of blanks. The buffer "outbuf"
+ * contains the current partial line image, including prefixed blanks.
+ * "outp" points to the next available space therein. When outp is NOSTR,
+ * there ain't nothing in there yet. At the bottom of this whole mess,
+ * leading tabs are reinserted.
+ */
+char outbuf[BUFSIZ]; /* Sandbagged output line image */
+char *outp; /* Pointer in above */
+
+/*
+ * Initialize the output section.
+ */
+setout()
+{
+ outp = NOSTR;
+}
+
+/*
+ * Pack a word onto the output line. If this is the beginning of
+ * the line, push on the appropriately-sized string of blanks first.
+ * If the word won't fit on the current line, flush and begin a new
+ * line. If the word is too long to fit all by itself on a line,
+ * just give it its own and hope for the best.
+ *
+ * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
+ * goal length, take it. If not, then check to see if the line
+ * will be over the max length; if so put the word on the next
+ * line. If not, check to see if the line will be closer to the
+ * goal length with or without the word and take it or put it on
+ * the next line accordingly.
+ */
+
+/*
+ * LIZ@UOM 6/18/85 -- pass in the length of the word as well
+ * pack(word)
+ * char word[];
+ */
+pack(word,wl)
+ char word[];
+ int wl;
+{
+ register char *cp;
+ register int s, t;
+
+ if (outp == NOSTR)
+ leadin();
+ /*
+ * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
+ * length of the line before the word is added; t is now the length
+ * of the line after the word is added
+ * t = strlen(word);
+ * if (t+s <= LENGTH)
+ */
+ s = outp - outbuf;
+ t = wl + s;
+ if ((t <= goal_length) ||
+ ((t <= max_length) && (t - goal_length <= goal_length - s))) {
+ /*
+ * In like flint!
+ */
+ for (cp = word; *cp; *outp++ = *cp++);
+ return;
+ }
+ if (s > pfx) {
+ oflush();
+ leadin();
+ }
+ for (cp = word; *cp; *outp++ = *cp++);
+}
+
+/*
+ * If there is anything on the current output line, send it on
+ * its way. Set outp to NOSTR to indicate the absence of the current
+ * line prefix.
+ */
+oflush()
+{
+ if (outp == NOSTR)
+ return;
+ *outp = '\0';
+ tabulate(outbuf);
+ outp = NOSTR;
+}
+
+/*
+ * Take the passed line buffer, insert leading tabs where possible, and
+ * output on standard output (finally).
+ */
+tabulate(line)
+ char line[];
+{
+ register char *cp;
+ register int b, t;
+
+ /*
+ * Toss trailing blanks in the output line.
+ */
+ cp = line + strlen(line) - 1;
+ while (cp >= line && *cp == ' ')
+ cp--;
+ *++cp = '\0';
+
+ /*
+ * Count the leading blank space and tabulate.
+ */
+ for (cp = line; *cp == ' '; cp++)
+ ;
+ b = cp-line;
+ t = b >> 3;
+ b &= 07;
+ if (t > 0)
+ do
+ putc('\t', stdout);
+ while (--t);
+ if (b > 0)
+ do
+ putc(' ', stdout);
+ while (--b);
+ while (*cp)
+ putc(*cp++, stdout);
+ putc('\n', stdout);
+}
+
+/*
+ * Initialize the output line with the appropriate number of
+ * leading blanks.
+ */
+leadin()
+{
+ register int b;
+ register char *cp;
+
+ for (b = 0, cp = outbuf; b < pfx; b++)
+ *cp++ = ' ';
+ outp = cp;
+}
+
+/*
+ * Save a string in dynamic space.
+ * This little goodie is needed for
+ * a headline detector in head.c
+ */
+char *
+savestr(str)
+ char str[];
+{
+ register char *top;
+
+ top = malloc(strlen(str) + 1);
+ if (top == NOSTR) {
+ fprintf(stderr, "fmt: Ran out of memory\n");
+ exit(1);
+ }
+ strcpy(top, str);
+ return (top);
+}
+
+/*
+ * Is s1 a prefix of s2??
+ */
+ispref(s1, s2)
+ register char *s1, *s2;
+{
+
+ while (*s1++ == *s2)
+ ;
+ return (*s1 == '\0');
+}
diff --git a/usr.bin/fold/Makefile b/usr.bin/fold/Makefile
new file mode 100644
index 0000000..4d80007
--- /dev/null
+++ b/usr.bin/fold/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= fold
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fold/fold.1 b/usr.bin/fold/fold.1
new file mode 100644
index 0000000..1ebb523
--- /dev/null
+++ b/usr.bin/fold/fold.1
@@ -0,0 +1,64 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fold.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt FOLD 1
+.Os
+.Sh NAME
+.Nm fold
+.Nd "fold long lines for finite width output device"
+.Sh SYNOPSIS
+.Nm fold
+.Op Fl w Ar width
+.Ar
+.Sh DESCRIPTION
+.Nm Fold
+is a filter which folds the contents of the specified files,
+or the standard input if no files are specified,
+breaking the lines to have maximum of 80 characters.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl w
+Specifies a line width to use instead of the default 80 characters.
+.Ar Width
+should be a multiple of 8 if tabs are present, or the tabs should
+be expanded using
+.Xr expand 1
+before using
+.Nm fold .
+.El
+.Sh SEE ALSO
+.Xr expand 1
+.Sh BUGS
+If underlining is present it may be messed up by folding.
diff --git a/usr.bin/fold/fold.c b/usr.bin/fold/fold.c
new file mode 100644
index 0000000..983a492
--- /dev/null
+++ b/usr.bin/fold/fold.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Ruddy.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fold.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+
+#define DEFLINEWIDTH 80
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int errno, optind;
+ extern char *optarg;
+ register int ch;
+ int width;
+ char *p;
+
+ width = -1;
+ while ((ch = getopt(argc, argv, "0123456789w:")) != EOF)
+ switch (ch) {
+ case 'w':
+ if ((width = atoi(optarg)) <= 0) {
+ (void)fprintf(stderr,
+ "fold: illegal width value.\n");
+ exit(1);
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (width == -1) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ width = atoi(++p);
+ else
+ width = atoi(argv[optind] + 1);
+ }
+ break;
+ default:
+ (void)fprintf(stderr,
+ "usage: fold [-w width] [file ...]\n");
+ exit(1);
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (width == -1)
+ width = DEFLINEWIDTH;
+ if (!*argv)
+ fold(width);
+ else for (; *argv; ++argv)
+ if (!freopen(*argv, "r", stdin)) {
+ (void)fprintf(stderr,
+ "fold: %s: %s\n", *argv, strerror(errno));
+ exit(1);
+ } else
+ fold(width);
+ exit(0);
+}
+
+fold(width)
+ register int width;
+{
+ register int ch, col, new;
+
+ for (col = 0;;) {
+ switch (ch = getchar()) {
+ case EOF:
+ return;
+ case '\b':
+ new = col ? col - 1 : 0;
+ break;
+ case '\n':
+ case '\r':
+ new = 0;
+ break;
+ case '\t':
+ new = (col + 8) & ~7;
+ break;
+ default:
+ new = col + 1;
+ break;
+ }
+
+ if (new > width) {
+ putchar('\n');
+ col = 0;
+ }
+ putchar(ch);
+
+ switch (ch) {
+ case '\b':
+ if (col > 0)
+ --col;
+ break;
+ case '\n':
+ case '\r':
+ col = 0;
+ break;
+ case '\t':
+ col += 8;
+ col &= ~7;
+ break;
+ default:
+ ++col;
+ break;
+ }
+ }
+}
diff --git a/usr.bin/fpr/Makefile b/usr.bin/fpr/Makefile
new file mode 100644
index 0000000..b5d12f0
--- /dev/null
+++ b/usr.bin/fpr/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= fpr
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fpr/fpr.1 b/usr.bin/fpr/fpr.1
new file mode 100644
index 0000000..0234c48
--- /dev/null
+++ b/usr.bin/fpr/fpr.1
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Corbett.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fpr.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt FPR 1
+.Os BSD 4.2
+.Sh NAME
+.Nm fpr
+.Nd print Fortran file
+.Sh SYNOPSIS
+.Nm fpr
+.Sh DESCRIPTION
+.Nm Fpr
+is a filter that transforms files formatted according to
+Fortran's carriage control conventions into files formatted
+according to
+.Ux
+line printer conventions.
+.Pp
+.Nm Fpr
+copies its input onto its output, replacing the carriage
+control characters with characters that will produce the intended
+effects when printed using
+.Xr lpr 1 .
+The first character of each line determines the vertical spacing as follows:
+.Bd -ragged -offset indent -compact
+.Bl -column Character
+.It Blank One line
+.It 0 Two lines
+.It 1 To first line of next page
+.It + No advance
+.El
+.Ed
+.Pp
+A blank line is treated as if its first
+character is a blank. A blank that appears as a carriage control
+character is deleted. A zero is changed to a newline. A one is
+changed to a form feed. The effects of a "+" are simulated using
+backspaces.
+.Sh EXAMPLES
+.Dl a.out \&| fpr \&| lpr
+.Pp
+.Dl fpr \&< f77.output \&| lpr
+.Sh HISTORY
+The
+.Nm fpr
+command
+appeared in
+.Bx 4.2 .
+.Sh BUGS
+Results are undefined for input lines longer than 170 characters.
diff --git a/usr.bin/fpr/fpr.c b/usr.bin/fpr/fpr.c
new file mode 100644
index 0000000..b8fdf9c
--- /dev/null
+++ b/usr.bin/fpr/fpr.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fpr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#define BLANK ' '
+#define TAB '\t'
+#define NUL '\000'
+#define FF '\f'
+#define BS '\b'
+#define CR '\r'
+#define VTAB '\013'
+#define EOL '\n'
+
+#define TRUE 1
+#define FALSE 0
+
+#define MAXCOL 170
+#define TABSIZE 8
+#define INITWIDTH 8
+
+typedef
+ struct column
+ {
+ int count;
+ int width;
+ char *str;
+ }
+ COLUMN;
+
+char cc;
+char saved;
+int length;
+char *text;
+int highcol;
+COLUMN *line;
+int maxpos;
+int maxcol;
+
+extern char *malloc();
+extern char *calloc();
+extern char *realloc();
+
+
+
+main()
+{
+ register int ch;
+ register char ateof;
+ register int i;
+ register int errorcount;
+
+
+ init();
+ errorcount = 0;
+ ateof = FALSE;
+
+ ch = getchar();
+ if (ch == EOF)
+ exit(0);
+
+ if (ch == EOL)
+ {
+ cc = NUL;
+ ungetc((int) EOL, stdin);
+ }
+ else if (ch == BLANK)
+ cc = NUL;
+ else if (ch == '1')
+ cc = FF;
+ else if (ch == '0')
+ cc = EOL;
+ else if (ch == '+')
+ cc = CR;
+ else
+ {
+ errorcount = 1;
+ cc = NUL;
+ ungetc(ch, stdin);
+ }
+
+ while ( ! ateof)
+ {
+ gettext();
+ ch = getchar();
+ if (ch == EOF)
+ {
+ flush();
+ ateof = TRUE;
+ }
+ else if (ch == EOL)
+ {
+ flush();
+ cc = NUL;
+ ungetc((int) EOL, stdin);
+ }
+ else if (ch == BLANK)
+ {
+ flush();
+ cc = NUL;
+ }
+ else if (ch == '1')
+ {
+ flush();
+ cc = FF;
+ }
+ else if (ch == '0')
+ {
+ flush();
+ cc = EOL;
+ }
+ else if (ch == '+')
+ {
+ for (i = 0; i < length; i++)
+ savech(i);
+ }
+ else
+ {
+ errorcount++;
+ flush();
+ cc = NUL;
+ ungetc(ch, stdin);
+ }
+ }
+
+ if (errorcount == 1)
+ fprintf(stderr, "Illegal carriage control - 1 line.\n");
+ else if (errorcount > 1)
+ fprintf(stderr, "Illegal carriage control - %d lines.\n", errorcount);
+
+ exit(0);
+}
+
+
+
+init()
+{
+ register COLUMN *cp;
+ register COLUMN *cend;
+ register char *sp;
+
+
+ length = 0;
+ maxpos = MAXCOL;
+ sp = malloc((unsigned) maxpos);
+ if (sp == NULL)
+ nospace();
+ text = sp;
+
+ highcol = -1;
+ maxcol = MAXCOL;
+ line = (COLUMN *) calloc(maxcol, (unsigned) sizeof(COLUMN));
+ if (line == NULL)
+ nospace();
+ cp = line;
+ cend = line + (maxcol-1);
+ while (cp <= cend)
+ {
+ cp->width = INITWIDTH;
+ sp = calloc(INITWIDTH, (unsigned) sizeof(char));
+ if (sp == NULL)
+ nospace();
+ cp->str = sp;
+ cp++;
+ }
+}
+
+
+
+gettext()
+{
+ register int i;
+ register char ateol;
+ register int ch;
+ register int pos;
+
+
+ i = 0;
+ ateol = FALSE;
+
+ while ( ! ateol)
+ {
+ ch = getchar();
+ if (ch == EOL || ch == EOF)
+ ateol = TRUE;
+ else if (ch == TAB)
+ {
+ pos = (1 + i/TABSIZE) * TABSIZE;
+ if (pos > maxpos)
+ {
+ maxpos = pos + 10;
+ text = realloc(text, (unsigned) maxpos);
+ if (text == NULL)
+ nospace();
+ }
+ while (i < pos)
+ {
+ text[i] = BLANK;
+ i++;
+ }
+ }
+ else if (ch == BS)
+ {
+ if (i > 0)
+ {
+ i--;
+ savech(i);
+ }
+ }
+ else if (ch == CR)
+ {
+ while (i > 0)
+ {
+ i--;
+ savech(i);
+ }
+ }
+ else if (ch == FF || ch == VTAB)
+ {
+ flush();
+ cc = ch;
+ i = 0;
+ }
+ else
+ {
+ if (i >= maxpos)
+ {
+ maxpos = i + 10;
+ text = realloc(text, (unsigned) maxpos);
+ if (text == NULL)
+ nospace();
+ }
+ text[i] = ch;
+ i++;
+ }
+ }
+
+ length = i;
+}
+
+
+
+savech(col)
+int col;
+{
+ register char ch;
+ register int oldmax;
+ register COLUMN *cp;
+ register COLUMN *cend;
+ register char *sp;
+ register int newcount;
+
+
+ ch = text[col];
+ if (ch == BLANK)
+ return;
+
+ saved = TRUE;
+
+ if (col >= highcol)
+ highcol = col;
+
+ if (col >= maxcol)
+ {
+ oldmax = maxcol;
+ maxcol = col + 10;
+ line = (COLUMN *) realloc(line, (unsigned) maxcol*sizeof(COLUMN));
+ if (line == NULL)
+ nospace();
+ cp = line + oldmax;
+ cend = line + (maxcol - 1);
+ while (cp <= cend)
+ {
+ cp->width = INITWIDTH;
+ cp->count = 0;
+ sp = calloc(INITWIDTH, (unsigned) sizeof(char));
+ if (sp == NULL)
+ nospace();
+ cp->str = sp;
+ cp++;
+ }
+ }
+
+ cp = line + col;
+ newcount = cp->count + 1;
+ if (newcount > cp->width)
+ {
+ cp->width = newcount;
+ sp = realloc(cp->str, (unsigned) newcount*sizeof(char));
+ if (sp == NULL)
+ nospace();
+ cp->str = sp;
+ }
+ cp->count = newcount;
+ cp->str[newcount-1] = ch;
+}
+
+
+
+flush()
+{
+ register int i;
+ register int anchor;
+ register int height;
+ register int j;
+
+
+ if (cc != NUL)
+ putchar(cc);
+
+ if ( ! saved)
+ {
+ i = length;
+ while (i > 0 && text[i-1] == BLANK)
+ i--;
+ length = i;
+ for (i = 0; i < length; i++)
+ putchar(text[i]);
+ putchar(EOL);
+ return;
+ }
+
+ for (i =0; i < length; i++)
+ savech(i);
+
+ anchor = 0;
+ while (anchor <= highcol)
+ {
+ height = line[anchor].count;
+ if (height == 0)
+ {
+ putchar(BLANK);
+ anchor++;
+ }
+ else if (height == 1)
+ {
+ putchar( *(line[anchor].str) );
+ line[anchor].count = 0;
+ anchor++;
+ }
+ else
+ {
+ i = anchor;
+ while (i < highcol && line[i+1].count > 1)
+ i++;
+ for (j = anchor; j <= i; j++)
+ {
+ height = line[j].count - 1;
+ putchar(line[j].str[height]);
+ line[j].count = height;
+ }
+ for (j = anchor; j <= i; j++)
+ putchar(BS);
+ }
+ }
+
+ putchar(EOL);
+ highcol = -1;
+}
+
+
+
+nospace()
+{
+ fputs("Storage limit exceeded.\n", stderr);
+ exit(1);
+}
diff --git a/usr.bin/from/Makefile b/usr.bin/from/Makefile
new file mode 100644
index 0000000..a802dc6
--- /dev/null
+++ b/usr.bin/from/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= from
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/from/from.1 b/usr.bin/from/from.1
new file mode 100644
index 0000000..f935b89
--- /dev/null
+++ b/usr.bin/from/from.1
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)from.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt FROM 1
+.Os BSD 4
+.Sh NAME
+.Nm from
+.Nd print names of those who have sent mail
+.Sh SYNOPSIS
+.Nm from
+.Op Fl s Ar sender
+.Op Fl f Ar file
+.Op Ar user
+.Sh DESCRIPTION
+.Nm From
+prints
+out the mail header lines from the invoker's mailbox.
+.Pp
+Options:
+.Bl -tag -width Fl
+.It Fl f Ar file
+The supplied file
+is examined instead of the invoker's mailbox.
+If the
+.Fl f
+option is used, the
+.Ar user
+argument should not be used.
+.It Fl s Ar sender
+Only mail from addresses containing
+the
+supplied string are printed.
+.El
+.Pp
+If
+.Ar user
+is given, the
+.Ar user Ns 's
+mailbox, is examined instead of the invoker's own mailbox.
+(Privileges are required.)
+.Sh FILES
+.Bl -tag -width /var/mail/* -compact
+.It Pa /var/mail/*
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr mail 1
+.Sh HISTORY
+The
+.Nm from
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/from/from.c b/usr.bin/from/from.c
new file mode 100644
index 0000000..324bceb
--- /dev/null
+++ b/usr.bin/from/from.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)from.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <paths.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ struct passwd *pwd;
+ int ch, newline;
+ char *file, *sender, *p;
+#if MAXPATHLEN > BUFSIZ
+ char buf[MAXPATHLEN];
+#else
+ char buf[BUFSIZ];
+#endif
+
+ file = sender = NULL;
+ while ((ch = getopt(argc, argv, "f:s:")) != EOF)
+ switch((char)ch) {
+ case 'f':
+ file = optarg;
+ break;
+ case 's':
+ sender = optarg;
+ for (p = sender; *p; ++p)
+ if (isupper(*p))
+ *p = tolower(*p);
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "usage: from [-f file] [-s sender] [user]\n");
+ exit(1);
+ }
+ argv += optind;
+
+ if (!file) {
+ if (!(file = *argv)) {
+ if (!(pwd = getpwuid(getuid()))) {
+ fprintf(stderr,
+ "from: no password file entry for you.\n");
+ exit(1);
+ }
+ file = pwd->pw_name;
+ }
+ (void)sprintf(buf, "%s/%s", _PATH_MAILDIR, file);
+ file = buf;
+ }
+ if (!freopen(file, "r", stdin)) {
+ fprintf(stderr, "from: can't read %s.\n", file);
+ exit(1);
+ }
+ for (newline = 1; fgets(buf, sizeof(buf), stdin);) {
+ if (*buf == '\n') {
+ newline = 1;
+ continue;
+ }
+ if (newline && !strncmp(buf, "From ", 5) &&
+ (!sender || match(buf + 5, sender)))
+ printf("%s", buf);
+ newline = 0;
+ }
+ exit(0);
+}
+
+match(line, sender)
+ register char *line, *sender;
+{
+ register char ch, pch, first, *p, *t;
+
+ for (first = *sender++;;) {
+ if (isspace(ch = *line))
+ return(0);
+ ++line;
+ if (isupper(ch))
+ ch = tolower(ch);
+ if (ch != first)
+ continue;
+ for (p = sender, t = line;;) {
+ if (!(pch = *p++))
+ return(1);
+ if (isupper(ch = *t++))
+ ch = tolower(ch);
+ if (ch != pch)
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/fsplit/Makefile b/usr.bin/fsplit/Makefile
new file mode 100644
index 0000000..f731a0d
--- /dev/null
+++ b/usr.bin/fsplit/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= fsplit
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fsplit/fsplit.1 b/usr.bin/fsplit/fsplit.1
new file mode 100644
index 0000000..2494495
--- /dev/null
+++ b/usr.bin/fsplit/fsplit.1
@@ -0,0 +1,103 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Asa Romberger and Jerry Berkman.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fsplit.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt FSPLIT 1
+.Os BSD 4.2
+.Sh NAME
+.Nm fsplit
+.Nd split a multi-routine Fortran file into individual files
+.Sh SYNOPSIS
+.Nm fsplit
+.Op Fl e Ar efile
+\&...
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Fsplit
+takes as input either a file or standard input containing Fortran source code.
+It attempts to split the input into separate routine files of the
+form
+.Ar name.f ,
+where
+.Ar name
+is the name of the program unit (e.g. function, subroutine, block data or
+program). The name for unnamed block data subprograms has the form
+.Ar blkdtaNNN.f
+where NNN is three digits and a file of this name does not already exist.
+For unnamed main programs the name has the form
+.Ar mainNNN.f .
+If there is an error in classifying a program unit, or if
+.Ar name.f
+already exists,
+the program unit will be put in a file of the form
+.Ar zzzNNN.f
+where
+.Ar zzzNNN.f
+does not already exist.
+.Pp
+.Bl -tag -width Fl
+.It Fl e Ar efile
+Normally each subprogram unit is split into a separate file. When the
+.Fl e
+option is used, only the specified subprogram units are split into separate
+files. E.g.:
+.Pp
+.Dl fsplit -e readit -e doit prog.f
+.Pp
+will split readit and doit into separate files.
+.El
+.Sh DIAGNOSTICS
+If names specified via the
+.Fl e
+option are not found, a diagnostic is written to
+standard error.
+.Sh HISTORY
+The
+.Nm fsplit
+command
+appeared in
+.Bx 4.2 .
+.Sh AUTHORS
+Asa Romberger and Jerry Berkman
+.Sh BUGS
+.Nm Fsplit
+assumes the subprogram name is on the first noncomment line of the subprogram
+unit. Nonstandard source formats may confuse
+.Nm fsplit .
+.Pp
+It is hard to use
+.Fl e
+for unnamed main programs and block data subprograms since you must
+predict the created file name.
diff --git a/usr.bin/fsplit/fsplit.c b/usr.bin/fsplit/fsplit.c
new file mode 100644
index 0000000..a6135cc
--- /dev/null
+++ b/usr.bin/fsplit/fsplit.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Asa Romberger and Jerry Berkman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fsplit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * usage: fsplit [-e efile] ... [file]
+ *
+ * split single file containing source for several fortran programs
+ * and/or subprograms into files each containing one
+ * subprogram unit.
+ * each separate file will be named using the corresponding subroutine,
+ * function, block data or program name if one is found; otherwise
+ * the name will be of the form mainNNN.f or blkdtaNNN.f .
+ * If a file of that name exists, it is saved in a name of the
+ * form zzz000.f .
+ * If -e option is used, then only those subprograms named in the -e
+ * option are split off; e.g.:
+ * fsplit -esub1 -e sub2 prog.f
+ * isolates sub1 and sub2 in sub1.f and sub2.f. The space
+ * after -e is optional.
+ *
+ * Modified Feb., 1983 by Jerry Berkman, Computing Services, U.C. Berkeley.
+ * - added comments
+ * - more function types: double complex, character*(*), etc.
+ * - fixed minor bugs
+ * - instead of all unnamed going into zNNN.f, put mains in
+ * mainNNN.f, block datas in blkdtaNNN.f, dups in zzzNNN.f .
+ */
+
+#define BSZ 512
+char buf[BSZ];
+FILE *ifp;
+char x[]="zzz000.f",
+ mainp[]="main000.f",
+ blkp[]="blkdta000.f";
+char *look(), *skiplab(), *functs();
+
+#define TRUE 1
+#define FALSE 0
+int extr = FALSE,
+ extrknt = -1,
+ extrfnd[100];
+char extrbuf[1000],
+ *extrnames[100];
+struct stat sbuf;
+
+#define trim(p) while (*p == ' ' || *p == '\t') p++
+
+main(argc, argv)
+char **argv;
+{
+ register FILE *ofp; /* output file */
+ register rv; /* 1 if got card in output file, 0 otherwise */
+ register char *ptr;
+ int nflag, /* 1 if got name of subprog., 0 otherwise */
+ retval,
+ i;
+ char name[20],
+ *extrptr = extrbuf;
+
+ /* scan -e options */
+ while ( argc > 1 && argv[1][0] == '-' && argv[1][1] == 'e') {
+ extr = TRUE;
+ ptr = argv[1] + 2;
+ if(!*ptr) {
+ argc--;
+ argv++;
+ if(argc <= 1) badparms();
+ ptr = argv[1];
+ }
+ extrknt = extrknt + 1;
+ extrnames[extrknt] = extrptr;
+ extrfnd[extrknt] = FALSE;
+ while(*ptr) *extrptr++ = *ptr++;
+ *extrptr++ = 0;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 2)
+ badparms();
+ else if (argc == 2) {
+ if ((ifp = fopen(argv[1], "r")) == NULL) {
+ fprintf(stderr, "fsplit: cannot open %s\n", argv[1]);
+ exit(1);
+ }
+ }
+ else
+ ifp = stdin;
+ for(;;) {
+ /* look for a temp file that doesn't correspond to an existing file */
+ get_name(x, 3);
+ ofp = fopen(x, "w");
+ nflag = 0;
+ rv = 0;
+ while (getline() > 0) {
+ rv = 1;
+ fprintf(ofp, "%s", buf);
+ if (lend()) /* look for an 'end' statement */
+ break;
+ if (nflag == 0) /* if no name yet, try and find one */
+ nflag = lname(name);
+ }
+ fclose(ofp);
+ if (rv == 0) { /* no lines in file, forget the file */
+ unlink(x);
+ retval = 0;
+ for ( i = 0; i <= extrknt; i++ )
+ if(!extrfnd[i]) {
+ retval = 1;
+ fprintf( stderr, "fsplit: %s not found\n",
+ extrnames[i]);
+ }
+ exit( retval );
+ }
+ if (nflag) { /* rename the file */
+ if(saveit(name)) {
+ if (stat(name, &sbuf) < 0 ) {
+ link(x, name);
+ unlink(x);
+ printf("%s\n", name);
+ continue;
+ } else if (strcmp(name, x) == 0) {
+ printf("%s\n", x);
+ continue;
+ }
+ printf("%s already exists, put in %s\n", name, x);
+ continue;
+ } else
+ unlink(x);
+ continue;
+ }
+ if(!extr)
+ printf("%s\n", x);
+ else
+ unlink(x);
+ }
+}
+
+badparms()
+{
+ fprintf(stderr, "fsplit: usage: fsplit [-e efile] ... [file] \n");
+ exit(1);
+}
+
+saveit(name)
+char *name;
+{
+ int i;
+ char fname[50],
+ *fptr = fname;
+
+ if(!extr) return(1);
+ while(*name) *fptr++ = *name++;
+ *--fptr = 0;
+ *--fptr = 0;
+ for ( i=0 ; i<=extrknt; i++ )
+ if( strcmp(fname, extrnames[i]) == 0 ) {
+ extrfnd[i] = TRUE;
+ return(1);
+ }
+ return(0);
+}
+
+get_name(name, letters)
+char *name;
+int letters;
+{
+ register char *ptr;
+
+ while (stat(name, &sbuf) >= 0) {
+ for (ptr = name + letters + 2; ptr >= name + letters; ptr--) {
+ (*ptr)++;
+ if (*ptr <= '9')
+ break;
+ *ptr = '0';
+ }
+ if(ptr < name + letters) {
+ fprintf( stderr, "fsplit: ran out of file names\n");
+ exit(1);
+ }
+ }
+}
+
+getline()
+{
+ register char *ptr;
+
+ for (ptr = buf; ptr < &buf[BSZ]; ) {
+ *ptr = getc(ifp);
+ if (feof(ifp))
+ return (-1);
+ if (*ptr++ == '\n') {
+ *ptr = 0;
+ return (1);
+ }
+ }
+ while (getc(ifp) != '\n' && feof(ifp) == 0) ;
+ fprintf(stderr, "line truncated to %d characters\n", BSZ);
+ return (1);
+}
+
+/* return 1 for 'end' alone on card (up to col. 72), 0 otherwise */
+lend()
+{
+ register char *p;
+
+ if ((p = skiplab(buf)) == 0)
+ return (0);
+ trim(p);
+ if (*p != 'e' && *p != 'E') return(0);
+ p++;
+ trim(p);
+ if (*p != 'n' && *p != 'N') return(0);
+ p++;
+ trim(p);
+ if (*p != 'd' && *p != 'D') return(0);
+ p++;
+ trim(p);
+ if (p - buf >= 72 || *p == '\n')
+ return (1);
+ return (0);
+}
+
+/* check for keywords for subprograms
+ return 0 if comment card, 1 if found
+ name and put in arg string. invent name for unnamed
+ block datas and main programs. */
+lname(s)
+char *s;
+{
+# define LINESIZE 80
+ register char *ptr, *p, *sptr;
+ char line[LINESIZE], *iptr = line;
+
+ /* first check for comment cards */
+ if(buf[0] == 'c' || buf[0] == 'C' || buf[0] == '*') return(0);
+ ptr = buf;
+ while (*ptr == ' ' || *ptr == '\t') ptr++;
+ if(*ptr == '\n') return(0);
+
+
+ ptr = skiplab(buf);
+ if (ptr == 0)
+ return (0);
+
+
+ /* copy to buffer and converting to lower case */
+ p = ptr;
+ while (*p && p <= &buf[71] ) {
+ *iptr = isupper(*p) ? tolower(*p) : *p;
+ iptr++;
+ p++;
+ }
+ *iptr = '\n';
+
+ if ((ptr = look(line, "subroutine")) != 0 ||
+ (ptr = look(line, "function")) != 0 ||
+ (ptr = functs(line)) != 0) {
+ if(scan_name(s, ptr)) return(1);
+ strcpy( s, x);
+ } else if((ptr = look(line, "program")) != 0) {
+ if(scan_name(s, ptr)) return(1);
+ get_name( mainp, 4);
+ strcpy( s, mainp);
+ } else if((ptr = look(line, "blockdata")) != 0) {
+ if(scan_name(s, ptr)) return(1);
+ get_name( blkp, 6);
+ strcpy( s, blkp);
+ } else if((ptr = functs(line)) != 0) {
+ if(scan_name(s, ptr)) return(1);
+ strcpy( s, x);
+ } else {
+ get_name( mainp, 4);
+ strcpy( s, mainp);
+ }
+ return(1);
+}
+
+scan_name(s, ptr)
+char *s, *ptr;
+{
+ char *sptr;
+
+ /* scan off the name */
+ trim(ptr);
+ sptr = s;
+ while (*ptr != '(' && *ptr != '\n') {
+ if (*ptr != ' ' && *ptr != '\t')
+ *sptr++ = *ptr;
+ ptr++;
+ }
+
+ if (sptr == s) return(0);
+
+ *sptr++ = '.';
+ *sptr++ = 'f';
+ *sptr++ = 0;
+ return(1);
+}
+
+char *functs(p)
+char *p;
+{
+ register char *ptr;
+
+/* look for typed functions such as: real*8 function,
+ character*16 function, character*(*) function */
+
+ if((ptr = look(p,"character")) != 0 ||
+ (ptr = look(p,"logical")) != 0 ||
+ (ptr = look(p,"real")) != 0 ||
+ (ptr = look(p,"integer")) != 0 ||
+ (ptr = look(p,"doubleprecision")) != 0 ||
+ (ptr = look(p,"complex")) != 0 ||
+ (ptr = look(p,"doublecomplex")) != 0 ) {
+ while ( *ptr == ' ' || *ptr == '\t' || *ptr == '*'
+ || (*ptr >= '0' && *ptr <= '9')
+ || *ptr == '(' || *ptr == ')') ptr++;
+ ptr = look(ptr,"function");
+ return(ptr);
+ }
+ else
+ return(0);
+}
+
+/* if first 6 col. blank, return ptr to col. 7,
+ if blanks and then tab, return ptr after tab,
+ else return 0 (labelled statement, comment or continuation */
+char *skiplab(p)
+char *p;
+{
+ register char *ptr;
+
+ for (ptr = p; ptr < &p[6]; ptr++) {
+ if (*ptr == ' ')
+ continue;
+ if (*ptr == '\t') {
+ ptr++;
+ break;
+ }
+ return (0);
+ }
+ return (ptr);
+}
+
+/* return 0 if m doesn't match initial part of s;
+ otherwise return ptr to next char after m in s */
+char *look(s, m)
+char *s, *m;
+{
+ register char *sp, *mp;
+
+ sp = s; mp = m;
+ while (*mp) {
+ trim(sp);
+ if (*sp++ != *mp++)
+ return (0);
+ }
+ return (sp);
+}
diff --git a/usr.bin/fstat/Makefile b/usr.bin/fstat/Makefile
new file mode 100644
index 0000000..a8b50c2
--- /dev/null
+++ b/usr.bin/fstat/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= fstat
+CFLAGS+=-I/sys
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/fstat/fstat.1 b/usr.bin/fstat/fstat.1
new file mode 100644
index 0000000..715e09a
--- /dev/null
+++ b/usr.bin/fstat/fstat.1
@@ -0,0 +1,219 @@
+.\" Copyright (c) 1987, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)fstat.1 8.3 (Berkeley) 2/25/94
+.\"
+.Dd February 25, 1994
+.Dt FSTAT 1
+.Os BSD 4
+.Sh NAME
+.Nm fstat
+.Nd file status
+.Sh SYNOPSIS
+.Nm fstat
+.Op Fl fnv
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl p Ar pid
+.Op Fl u Ar user
+.Op Ar filename...
+.Sh DESCRIPTION
+.Nm Fstat
+identifies open files.
+A file is considered open by a process if it was explicitly opened,
+is the working directory, root directory, active pure text, or kernel
+trace file for that process.
+If no options are specified,
+.Nm fstat
+reports on all open files in the system.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl f
+Restrict examination to files open in the same filesystems as
+the named file arguments, or to the filesystem containing the
+current directory if there are no additional filename arguments.
+For example, to find all files open in the filesystem where the
+directory
+.Pa /usr/src
+resides, type
+.Dq Li fstat -f /usr/src .
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Pa /kernel .
+.It Fl n
+Numerical format. Print the device number (maj,min) of the filesystem
+the file resides in rather than the mount point name; for special
+files, print the
+device number that the special device refers to rather than the filename
+in
+.Pa /dev ;
+and print the mode of the file in octal instead of symbolic form.
+.It Fl p
+Report all files open by the specified process.
+.It Fl u
+Report all files open by the specified user.
+.It Fl v
+Verbose mode. Print error messages upon failures to locate particular
+system data structures rather than silently ignoring them. Most of
+these data structures are dynamically created or deleted and it is
+possible for them to disappear while
+.Nm fstat
+is running. This
+is normal and unavoidable since the rest of the system is running while
+.Nm fstat
+itself is running.
+.It Ar filename ...
+Restrict reports to the specified files.
+.El
+.Pp
+The following fields are printed:
+.Bl -tag -width MOUNT
+.It Li USER
+The username of the owner of the process (effective uid).
+.It Li CMD
+The command name of the process.
+.It Li PID
+The process id.
+.It Li FD
+The file number in the per-process open file table or one of the following
+special names:
+.Pp
+.Bd -ragged -offset indent -compact
+text - pure text inode
+wd - current working directory
+root - root inode
+tr - kernel trace file
+.Ed
+.Pp
+If the file number is followed by an asterisk (``*''), the file is
+not an inode, but rather a socket,
+.Tn FIFO ,
+or there is an error.
+In this case the remainder of the line doesn't
+correspond to the remaining headers -- the format of the line
+is described later under
+.Sx Sockets .
+.It Li MOUNT
+If the
+.Fl n
+flag wasn't specified, this header is present and is the
+pathname that the filesystem the file resides in is mounted on.
+.It Li DEV
+If the
+.Fl n
+flag is specified, this header is present and is the
+major/minor number of the device that this file resides in.
+.It Li INUM
+The inode number of the file.
+.It Li MODE
+The mode of the file. If the
+.Fl n
+flag isn't specified, the mode is printed
+using a symbolic format (see
+.Xr strmode 3 ) ;
+otherwise, the mode is printed
+as an octal number.
+.It Li SZ\&|DV
+If the file is not a character or block special, prints the size of
+the file in bytes. Otherwise, if the
+.Fl n
+flag is not specified, prints
+the name of the special file as located in
+.Pa /dev .
+If that cannot be
+located, or the
+.Fl n
+flag is specified, prints the major/minor device
+number that the special device refers to.
+.It Li R/W
+This column describes the access mode that the file allows.
+The letter ``r'' indicates open for reading;
+the letter ``w'' indicates open for writing.
+This field is useful when trying to find the processes that are
+preventing a filesystem from being down graded to read-only.
+.It Li NAME
+If filename arguments are specified and the
+.Fl f
+flag is not, then
+this field is present and is the name associated with the given file.
+Normally the name cannot be determined since there is no mapping
+from an open file back to the directory entry that was used to open
+that file. Also, since different directory entries may reference
+the same file (via
+.Xr ln 2 ) ,
+the name printed may not be the actual
+name that the process originally used to open that file.
+.El
+.Sh SOCKETS
+The formating of open sockets depends on the protocol domain.
+In all cases the first field is the domain name, the second field
+is the socket type (stream, dgram, etc), and the third is the socket
+flags field (in hex).
+The remaining fields are protocol dependent.
+For tcp, it is the address of the tcpcb, and for udp, the inpcb (socket pcb).
+For unix domain sockets, its the address of the socket pcb and the address
+of the connected pcb (if connected).
+Otherwise the protocol number and address of the socket itself are printed.
+The attempt is to make enough information available to
+permit further analysis without duplicating
+.Xr netstat 1 .
+.Pp
+For example, the addresses mentioned above are the addresses which the
+.Dq Li netstat -A
+command would print for tcp, udp, and unixdomain.
+Note that since pipes are implemented using sockets, a pipe appears as a
+connected unix domain stream socket.
+A unidirectional unix domain socket indicates the direction of flow with
+an arrow (``<-'' or ``->''), and a full duplex socket shows a double arrow
+(``<->'').
+.Sh BUGS
+Since
+.Nm fstat
+takes a snapshot of the system, it is only correct for a very short period
+of time.
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr vmstat 1 ,
+.Xr iostat 8 ,
+.Xr pstat 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 tahoe .
diff --git a/usr.bin/fstat/fstat.c b/usr.bin/fstat/fstat.c
new file mode 100644
index 0000000..96116f9
--- /dev/null
+++ b/usr.bin/fstat/fstat.c
@@ -0,0 +1,747 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)fstat.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/unpcb.h>
+#include <sys/sysctl.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#define KERNEL
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#undef KERNEL
+#define NFS
+#include <sys/mount.h>
+#include <nfs/nfsproto.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#undef NFS
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TEXT -1
+#define CDIR -2
+#define RDIR -3
+#define TRACE -4
+
+typedef struct devs {
+ struct devs *next;
+ long fsid;
+ ino_t ino;
+ char *name;
+} DEVS;
+DEVS *devs;
+
+struct filestat {
+ long fsid;
+ long fileid;
+ mode_t mode;
+ u_long size;
+ dev_t rdev;
+};
+
+#ifdef notdef
+struct nlist nl[] = {
+ { "" },
+};
+#endif
+
+int fsflg, /* show files on same filesystem as file(s) argument */
+ pflg, /* show files open by a particular pid */
+ uflg; /* show files open by a particular (effective) user */
+int checkfile; /* true if restricting to particular files or filesystems */
+int nflg; /* (numerical) display f.s. and rdev as dev_t */
+int vflg; /* display errors in locating kernel data objects etc... */
+
+#define dprintf if (vflg) fprintf
+
+struct file **ofiles; /* buffer of pointers to file structures */
+int maxfiles;
+#define ALLOC_OFILES(d) \
+ if ((d) > maxfiles) { \
+ free(ofiles); \
+ ofiles = malloc((d) * sizeof(struct file *)); \
+ if (ofiles == NULL) { \
+ fprintf(stderr, "fstat: %s\n", strerror(errno)); \
+ exit(1); \
+ } \
+ maxfiles = (d); \
+ }
+
+/*
+ * a kvm_read that returns true if everything is read
+ */
+#define KVM_READ(kaddr, paddr, len) \
+ (kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (len))
+
+kvm_t *kd;
+
+int ufs_filestat(), nfs_filestat();
+void dofiles(), getinetproto(), socktrans();
+void usage(), vtrans();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register struct passwd *passwd;
+ struct kinfo_proc *p, *plast;
+ int arg, ch, what;
+ char *memf, *nlistf;
+ int cnt;
+
+ arg = 0;
+ what = KERN_PROC_ALL;
+ nlistf = memf = NULL;
+ while ((ch = getopt(argc, argv, "fnp:u:vNM")) != EOF)
+ switch((char)ch) {
+ case 'f':
+ fsflg = 1;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ nflg = 1;
+ break;
+ case 'p':
+ if (pflg++)
+ usage();
+ if (!isdigit(*optarg)) {
+ fprintf(stderr,
+ "fstat: -p requires a process id\n");
+ usage();
+ }
+ what = KERN_PROC_PID;
+ arg = atoi(optarg);
+ break;
+ case 'u':
+ if (uflg++)
+ usage();
+ if (!(passwd = getpwnam(optarg))) {
+ fprintf(stderr, "%s: unknown uid\n",
+ optarg);
+ exit(1);
+ }
+ what = KERN_PROC_UID;
+ arg = passwd->pw_uid;
+ break;
+ case 'v':
+ vflg = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (*(argv += optind)) {
+ for (; *argv; ++argv) {
+ if (getfname(*argv))
+ checkfile = 1;
+ }
+ if (!checkfile) /* file(s) specified, but none accessable */
+ exit(1);
+ }
+
+ ALLOC_OFILES(256); /* reserve space for file pointers */
+
+ if (fsflg && !checkfile) {
+ /* -f with no files means use wd */
+ if (getfname(".") == 0)
+ exit(1);
+ checkfile = 1;
+ }
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ if ((kd = kvm_open(nlistf, memf, NULL, O_RDONLY, NULL)) == NULL) {
+ fprintf(stderr, "fstat: %s\n", kvm_geterr(kd));
+ exit(1);
+ }
+#ifdef notdef
+ if (kvm_nlist(kd, nl) != 0) {
+ fprintf(stderr, "fstat: no namelist: %s\n", kvm_geterr(kd));
+ exit(1);
+ }
+#endif
+ if ((p = kvm_getprocs(kd, what, arg, &cnt)) == NULL) {
+ fprintf(stderr, "fstat: %s\n", kvm_geterr(kd));
+ exit(1);
+ }
+ if (nflg)
+ printf("%s",
+"USER CMD PID FD DEV INUM MODE SZ|DV R/W");
+ else
+ printf("%s",
+"USER CMD PID FD MOUNT INUM MODE SZ|DV R/W");
+ if (checkfile && fsflg == 0)
+ printf(" NAME\n");
+ else
+ putchar('\n');
+
+ for (plast = &p[cnt]; p < plast; ++p) {
+ if (p->kp_proc.p_stat == SZOMB)
+ continue;
+ dofiles(p);
+ }
+ exit(0);
+}
+
+char *Uname, *Comm;
+int Pid;
+
+#define PREFIX(i) printf("%-8.8s %-10s %5d", Uname, Comm, Pid); \
+ switch(i) { \
+ case TEXT: \
+ printf(" text"); \
+ break; \
+ case CDIR: \
+ printf(" wd"); \
+ break; \
+ case RDIR: \
+ printf(" root"); \
+ break; \
+ case TRACE: \
+ printf(" tr"); \
+ break; \
+ default: \
+ printf(" %4d", i); \
+ break; \
+ }
+
+/*
+ * print open files attributed to this process
+ */
+void
+dofiles(kp)
+ struct kinfo_proc *kp;
+{
+ int i, last;
+ struct file file;
+ struct filedesc0 filed0;
+#define filed filed0.fd_fd
+ struct proc *p = &kp->kp_proc;
+ struct eproc *ep = &kp->kp_eproc;
+
+ extern char *user_from_uid();
+
+ Uname = user_from_uid(ep->e_ucred.cr_uid, 0);
+ Pid = p->p_pid;
+ Comm = p->p_comm;
+
+ if (p->p_fd == NULL)
+ return;
+ if (!KVM_READ(p->p_fd, &filed0, sizeof (filed0))) {
+ dprintf(stderr, "can't read filedesc at %x for pid %d\n",
+ p->p_fd, Pid);
+ return;
+ }
+ /*
+ * root directory vnode, if one
+ */
+ if (filed.fd_rdir)
+ vtrans(filed.fd_rdir, RDIR, FREAD);
+ /*
+ * current working directory vnode
+ */
+ vtrans(filed.fd_cdir, CDIR, FREAD);
+ /*
+ * ktrace vnode, if one
+ */
+ if (p->p_tracep)
+ vtrans(p->p_tracep, TRACE, FREAD|FWRITE);
+ /*
+ * open files
+ */
+#define FPSIZE (sizeof (struct file *))
+ ALLOC_OFILES(filed.fd_lastfile+1);
+ if (filed.fd_nfiles > NDFILE) {
+ if (!KVM_READ(filed.fd_ofiles, ofiles,
+ (filed.fd_lastfile+1) * FPSIZE)) {
+ dprintf(stderr,
+ "can't read file structures at %x for pid %d\n",
+ filed.fd_ofiles, Pid);
+ return;
+ }
+ } else
+ bcopy(filed0.fd_dfiles, ofiles, (filed.fd_lastfile+1) * FPSIZE);
+ for (i = 0; i <= filed.fd_lastfile; i++) {
+ if (ofiles[i] == NULL)
+ continue;
+ if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
+ dprintf(stderr, "can't read file %d at %x for pid %d\n",
+ i, ofiles[i], Pid);
+ continue;
+ }
+ if (file.f_type == DTYPE_VNODE)
+ vtrans((struct vnode *)file.f_data, i, file.f_flag);
+ else if (file.f_type == DTYPE_SOCKET) {
+ if (checkfile == 0)
+ socktrans((struct socket *)file.f_data, i);
+ }
+ else {
+ dprintf(stderr,
+ "unknown file type %d for file %d of pid %d\n",
+ file.f_type, i, Pid);
+ }
+ }
+}
+
+void
+vtrans(vp, i, flag)
+ struct vnode *vp;
+ int i;
+ int flag;
+{
+ struct vnode vn;
+ struct filestat fst;
+ char rw[3], mode[15];
+ char *badtype = NULL, *filename, *getmnton();
+
+ filename = badtype = NULL;
+ if (!KVM_READ(vp, &vn, sizeof (struct vnode))) {
+ dprintf(stderr, "can't read vnode at %x for pid %d\n",
+ vp, Pid);
+ return;
+ }
+ if (vn.v_type == VNON || vn.v_tag == VT_NON)
+ badtype = "none";
+ else if (vn.v_type == VBAD)
+ badtype = "bad";
+ else
+ switch (vn.v_tag) {
+ case VT_UFS:
+ if (!ufs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ case VT_MFS:
+ if (!ufs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ case VT_NFS:
+ if (!nfs_filestat(&vn, &fst))
+ badtype = "error";
+ break;
+ default: {
+ static char unknown[10];
+ sprintf(badtype = unknown, "?(%x)", vn.v_tag);
+ break;;
+ }
+ }
+ if (checkfile) {
+ int fsmatch = 0;
+ register DEVS *d;
+
+ if (badtype)
+ return;
+ for (d = devs; d != NULL; d = d->next)
+ if (d->fsid == fst.fsid) {
+ fsmatch = 1;
+ if (d->ino == fst.fileid) {
+ filename = d->name;
+ break;
+ }
+ }
+ if (fsmatch == 0 || (filename == NULL && fsflg == 0))
+ return;
+ }
+ PREFIX(i);
+ if (badtype) {
+ (void)printf(" - - %10s -\n", badtype);
+ return;
+ }
+ if (nflg)
+ (void)printf(" %2d,%-2d", major(fst.fsid), minor(fst.fsid));
+ else
+ (void)printf(" %-8s", getmnton(vn.v_mount));
+ if (nflg)
+ (void)sprintf(mode, "%o", fst.mode);
+ else
+ strmode(fst.mode, mode);
+ (void)printf(" %6d %10s", fst.fileid, mode);
+ switch (vn.v_type) {
+ case VBLK:
+ case VCHR: {
+ char *name;
+
+ if (nflg || ((name = devname(fst.rdev, vn.v_type == VCHR ?
+ S_IFCHR : S_IFBLK)) == NULL))
+ printf(" %2d,%-2d", major(fst.rdev), minor(fst.rdev));
+ else
+ printf(" %6s", name);
+ break;
+ }
+ default:
+ printf(" %6d", fst.size);
+ }
+ rw[0] = '\0';
+ if (flag & FREAD)
+ strcat(rw, "r");
+ if (flag & FWRITE)
+ strcat(rw, "w");
+ printf(" %2s", rw);
+ if (filename && !fsflg)
+ printf(" %s", filename);
+ putchar('\n');
+}
+
+int
+ufs_filestat(vp, fsp)
+ struct vnode *vp;
+ struct filestat *fsp;
+{
+ struct inode inode;
+
+ if (!KVM_READ(VTOI(vp), &inode, sizeof (inode))) {
+ dprintf(stderr, "can't read inode at %x for pid %d\n",
+ VTOI(vp), Pid);
+ return 0;
+ }
+ fsp->fsid = inode.i_dev & 0xffff;
+ fsp->fileid = (long)inode.i_number;
+ fsp->mode = (mode_t)inode.i_mode;
+ fsp->size = (u_long)inode.i_size;
+ fsp->rdev = inode.i_rdev;
+
+ return 1;
+}
+
+int
+nfs_filestat(vp, fsp)
+ struct vnode *vp;
+ struct filestat *fsp;
+{
+ struct nfsnode nfsnode;
+ register mode_t mode;
+
+ if (!KVM_READ(VTONFS(vp), &nfsnode, sizeof (nfsnode))) {
+ dprintf(stderr, "can't read nfsnode at %x for pid %d\n",
+ VTONFS(vp), Pid);
+ return 0;
+ }
+ fsp->fsid = nfsnode.n_vattr.va_fsid;
+ fsp->fileid = nfsnode.n_vattr.va_fileid;
+ fsp->size = nfsnode.n_size;
+ fsp->rdev = nfsnode.n_vattr.va_rdev;
+ mode = (mode_t)nfsnode.n_vattr.va_mode;
+ switch (vp->v_type) {
+ case VREG:
+ mode |= S_IFREG;
+ break;
+ case VDIR:
+ mode |= S_IFDIR;
+ break;
+ case VBLK:
+ mode |= S_IFBLK;
+ break;
+ case VCHR:
+ mode |= S_IFCHR;
+ break;
+ case VLNK:
+ mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ mode |= S_IFIFO;
+ break;
+ };
+ fsp->mode = mode;
+
+ return 1;
+}
+
+
+char *
+getmnton(m)
+ struct mount *m;
+{
+ static struct mount mount;
+ static struct mtab {
+ struct mtab *next;
+ struct mount *m;
+ char mntonname[MNAMELEN];
+ } *mhead = NULL;
+ register struct mtab *mt;
+
+ for (mt = mhead; mt != NULL; mt = mt->next)
+ if (m == mt->m)
+ return (mt->mntonname);
+ if (!KVM_READ(m, &mount, sizeof(struct mount))) {
+ fprintf(stderr, "can't read mount table at %x\n", m);
+ return (NULL);
+ }
+ if ((mt = malloc(sizeof (struct mtab))) == NULL) {
+ fprintf(stderr, "fstat: %s\n", strerror(errno));
+ exit(1);
+ }
+ mt->m = m;
+ bcopy(&mount.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN);
+ mt->next = mhead;
+ mhead = mt;
+ return (mt->mntonname);
+}
+
+void
+socktrans(sock, i)
+ struct socket *sock;
+ int i;
+{
+ static char *stypename[] = {
+ "unused", /* 0 */
+ "stream", /* 1 */
+ "dgram", /* 2 */
+ "raw", /* 3 */
+ "rdm", /* 4 */
+ "seqpak" /* 5 */
+ };
+#define STYPEMAX 5
+ struct socket so;
+ struct protosw proto;
+ struct domain dom;
+ struct inpcb inpcb;
+ struct unpcb unpcb;
+ int len;
+ char dname[32], *strcpy();
+
+ PREFIX(i);
+
+ /* fill in socket */
+ if (!KVM_READ(sock, &so, sizeof(struct socket))) {
+ dprintf(stderr, "can't read sock at %x\n", sock);
+ goto bad;
+ }
+
+ /* fill in protosw entry */
+ if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
+ dprintf(stderr, "can't read protosw at %x", so.so_proto);
+ goto bad;
+ }
+
+ /* fill in domain */
+ if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
+ dprintf(stderr, "can't read domain at %x\n", proto.pr_domain);
+ goto bad;
+ }
+
+ if ((len = kvm_read(kd, (u_long)dom.dom_name, dname,
+ sizeof(dname) - 1)) < 0) {
+ dprintf(stderr, "can't read domain name at %x\n",
+ dom.dom_name);
+ dname[0] = '\0';
+ }
+ else
+ dname[len] = '\0';
+
+ if ((u_short)so.so_type > STYPEMAX)
+ printf("* %s ?%d", dname, so.so_type);
+ else
+ printf("* %s %s", dname, stypename[so.so_type]);
+
+ /*
+ * protocol specific formatting
+ *
+ * Try to find interesting things to print. For tcp, the interesting
+ * thing is the address of the tcpcb, for udp and others, just the
+ * inpcb (socket pcb). For unix domain, its the address of the socket
+ * pcb and the address of the connected pcb (if connected). Otherwise
+ * just print the protocol number and address of the socket itself.
+ * The idea is not to duplicate netstat, but to make available enough
+ * information for further analysis.
+ */
+ switch(dom.dom_family) {
+ case AF_INET:
+ getinetproto(proto.pr_protocol);
+ if (proto.pr_protocol == IPPROTO_TCP ) {
+ if (so.so_pcb) {
+ if (kvm_read(kd, (u_long)so.so_pcb,
+ (char *)&inpcb, sizeof(struct inpcb))
+ != sizeof(struct inpcb)) {
+ dprintf(stderr,
+ "can't read inpcb at %x\n",
+ so.so_pcb);
+ goto bad;
+ }
+ printf(" %x", (int)inpcb.inp_ppcb);
+ }
+ }
+ else if (so.so_pcb)
+ printf(" %x", (int)so.so_pcb);
+ break;
+ case AF_UNIX:
+ /* print address of pcb and connected pcb */
+ if (so.so_pcb) {
+ printf(" %x", (int)so.so_pcb);
+ if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb,
+ sizeof(struct unpcb)) != sizeof(struct unpcb)){
+ dprintf(stderr, "can't read unpcb at %x\n",
+ so.so_pcb);
+ goto bad;
+ }
+ if (unpcb.unp_conn) {
+ char shoconn[4], *cp;
+
+ cp = shoconn;
+ if (!(so.so_state & SS_CANTRCVMORE))
+ *cp++ = '<';
+ *cp++ = '-';
+ if (!(so.so_state & SS_CANTSENDMORE))
+ *cp++ = '>';
+ *cp = '\0';
+ printf(" %s %x", shoconn,
+ (int)unpcb.unp_conn);
+ }
+ }
+ break;
+ default:
+ /* print protocol number and socket address */
+ printf(" %d %x", proto.pr_protocol, (int)sock);
+ }
+ printf("\n");
+ return;
+bad:
+ printf("* error\n");
+}
+
+/*
+ * getinetproto --
+ * print name of protocol number
+ */
+void
+getinetproto(number)
+ int number;
+{
+ char *cp;
+
+ switch(number) {
+ case IPPROTO_IP:
+ cp = "ip"; break;
+ case IPPROTO_ICMP:
+ cp ="icmp"; break;
+ case IPPROTO_GGP:
+ cp ="ggp"; break;
+ case IPPROTO_TCP:
+ cp ="tcp"; break;
+ case IPPROTO_EGP:
+ cp ="egp"; break;
+ case IPPROTO_PUP:
+ cp ="pup"; break;
+ case IPPROTO_UDP:
+ cp ="udp"; break;
+ case IPPROTO_IDP:
+ cp ="idp"; break;
+ case IPPROTO_RAW:
+ cp ="raw"; break;
+ default:
+ printf(" %d", number);
+ return;
+ }
+ printf(" %s", cp);
+}
+
+getfname(filename)
+ char *filename;
+{
+ struct stat statbuf;
+ DEVS *cur;
+
+ if (stat(filename, &statbuf)) {
+ fprintf(stderr, "fstat: %s: %s\n", filename, strerror(errno));
+ return(0);
+ }
+ if ((cur = malloc(sizeof(DEVS))) == NULL) {
+ fprintf(stderr, "fstat: %s\n", strerror(errno));
+ exit(1);
+ }
+ cur->next = devs;
+ devs = cur;
+
+ cur->ino = statbuf.st_ino;
+ cur->fsid = statbuf.st_dev & 0xffff;
+ cur->name = filename;
+ return(1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: fstat [-fnv] [-p pid] [-u user] [-N system] [-M core] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile
new file mode 100644
index 0000000..e9d0a62
--- /dev/null
+++ b/usr.bin/ftp/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 4/3/94
+
+PROG= ftp
+SRCS= cmds.c cmdtab.c ftp.c main.c ruserpass.c domacro.c
+LINKS= ${BINDIR}/ftp ${BINDIR}/pftp
+MLINKS= ftp.1 pftp.1
+CFLAGS+=-DFTP_DATA_BOTTOM=40000 -DFTP_DATA_TOP=44999
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c
new file mode 100644
index 0000000..9756042
--- /dev/null
+++ b/usr.bin/ftp/cmds.c
@@ -0,0 +1,2230 @@
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.5 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <glob.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+jmp_buf jabort;
+char *mname;
+char *home = "/";
+
+/*
+ * `Another' gets another argument, and stores the new argc and argv.
+ * It reverts to the top level (via main.c's intr()) on EOF/error.
+ *
+ * Returns false if no new arguments have been added.
+ */
+int
+another(pargc, pargv, prompt)
+ int *pargc;
+ char ***pargv;
+ char *prompt;
+{
+ int len = strlen(line), ret;
+
+ if (len >= sizeof(line) - 3) {
+ printf("sorry, arguments too long\n");
+ intr();
+ }
+ printf("(%s) ", prompt);
+ line[len++] = ' ';
+ if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
+ intr();
+ len += strlen(&line[len]);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ makeargv();
+ ret = margc > *pargc;
+ *pargc = margc;
+ *pargv = margv;
+ return (ret);
+}
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+void
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *host;
+ short port;
+
+ if (connected) {
+ printf("Already connected to %s, use close first.\n",
+ hostname);
+ code = -1;
+ return;
+ }
+ if (argc < 2)
+ (void) another(&argc, &argv, "to");
+ if (argc < 2 || argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = sp->s_port;
+ if (argc > 2) {
+ port = atoi(argv[2]);
+ if (port <= 0) {
+ printf("%s: bad port number-- %s\n", argv[1], argv[2]);
+ printf ("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = htons(port);
+ }
+ host = hookup(argv[1], port);
+ if (host) {
+ int overbose;
+
+ connected = 1;
+ /*
+ * Set up defaults for FTP.
+ */
+ (void) strcpy(typename, "ascii"), type = TYPE_A;
+ curtype = TYPE_A;
+ (void) strcpy(formname, "non-print"), form = FORM_N;
+ (void) strcpy(modename, "stream"), mode = MODE_S;
+ (void) strcpy(structname, "file"), stru = STRU_F;
+ (void) strcpy(bytename, "8"), bytesize = 8;
+ if (autologin)
+ (void) login(argv[1]);
+
+#if defined(unix) && NBBY == 8
+/*
+ * this ifdef is to keep someone form "porting" this to an incompatible
+ * system and not checking this out. This way they have to think about it.
+ */
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ char *cp, c;
+ cp = strchr(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = strchr(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
+ if (proxy)
+ unix_proxy = 1;
+ else
+ unix_server = 1;
+ /*
+ * Set type to 0 (not specified by user),
+ * meaning binary by default, but don't bother
+ * telling server. We can use binary
+ * for text files unless changed by the user.
+ */
+ type = 0;
+ (void) strcpy(typename, "binary");
+ if (overbose)
+ printf("Using %s mode to transfer files.\n",
+ typename);
+ } else {
+ if (proxy)
+ unix_proxy = 0;
+ else
+ unix_server = 0;
+ if (overbose &&
+ !strncmp(reply_string, "215 TOPS20", 10))
+ printf(
+"Remember to set tenex mode when transfering binary files from this machine.\n");
+ }
+ verbose = overbose;
+#endif /* unix */
+ }
+}
+
+struct types {
+ char *t_name;
+ char *t_mode;
+ int t_type;
+ char *t_arg;
+} types[] = {
+ { "ascii", "A", TYPE_A, 0 },
+ { "binary", "I", TYPE_I, 0 },
+ { "image", "I", TYPE_I, 0 },
+ { "ebcdic", "E", TYPE_E, 0 },
+ { "tenex", "L", TYPE_L, bytename },
+ { NULL }
+};
+
+/*
+ * Set transfer type.
+ */
+void
+settype(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct types *p;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = types; p->t_name; p++) {
+ printf("%s%s", sep, p->t_name);
+ sep = " | ";
+ }
+ printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", typename);
+ code = 0;
+ return;
+ }
+ for (p = types; p->t_name; p++)
+ if (strcmp(argv[1], p->t_name) == 0)
+ break;
+ if (p->t_name == 0) {
+ printf("%s: unknown mode\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
+ comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE) {
+ (void) strcpy(typename, p->t_name);
+ curtype = type = p->t_type;
+ }
+}
+
+/*
+ * Internal form of settype; changes current type in use with server
+ * without changing our notion of the type for data transfers.
+ * Used to change to and from ascii for listings.
+ */
+void
+changetype(newtype, show)
+ int newtype, show;
+{
+ struct types *p;
+ int comret, oldverbose = verbose;
+
+ if (newtype == 0)
+ newtype = TYPE_I;
+ if (newtype == curtype)
+ return;
+ if (debug == 0 && show == 0)
+ verbose = 0;
+ for (p = types; p->t_name; p++)
+ if (newtype == p->t_type)
+ break;
+ if (p->t_name == 0) {
+ printf("ftp: internal error: unknown type %d\n", newtype);
+ return;
+ }
+ if (newtype == TYPE_L && bytename[0] != '\0')
+ comret = command("TYPE %s %s", p->t_mode, bytename);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE)
+ curtype = newtype;
+ verbose = oldverbose;
+}
+
+char *stype[] = {
+ "type",
+ "",
+ 0
+};
+
+/*
+ * Set binary transfer type.
+ */
+/*VARARGS*/
+void
+setbinary(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ stype[1] = "binary";
+ settype(2, stype);
+}
+
+/*
+ * Set ascii transfer type.
+ */
+/*VARARGS*/
+void
+setascii(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ stype[1] = "ascii";
+ settype(2, stype);
+}
+
+/*
+ * Set tenex transfer type.
+ */
+/*VARARGS*/
+void
+settenex(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ stype[1] = "tenex";
+ settype(2, stype);
+}
+
+/*
+ * Set file transfer mode.
+ */
+/*ARGSUSED*/
+void
+setftmode(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s mode, sorry.\n", modename);
+ code = -1;
+}
+
+/*
+ * Set file transfer format.
+ */
+/*ARGSUSED*/
+void
+setform(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s format, sorry.\n", formname);
+ code = -1;
+}
+
+/*
+ * Set file transfer structure.
+ */
+/*ARGSUSED*/
+void
+setstruct(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s structure, sorry.\n", structname);
+ code = -1;
+}
+
+/*
+ * Send a single file.
+ */
+void
+put(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "local-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "remote-file")) {
+usage:
+ printf("usage: %s local-file remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ /*
+ * If "globulize" modifies argv[1], and argv[2] is a copy of
+ * the old argv[1], make it a copy of the new argv[1].
+ */
+ if (argv[1] != oldargv1 && argv[2] == oldargv1) {
+ argv[2] = argv[1];
+ }
+ cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
+ if (loc && ntflag) {
+ argv[2] = dotrans(argv[2]);
+ }
+ if (loc && mapflag) {
+ argv[2] = domap(argv[2]);
+ }
+ sendrequest(cmd, argv[1], argv[2],
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+}
+
+/*
+ * Send multiple files.
+ */
+void
+mput(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ sig_t oldintr;
+ int ointer;
+ char *tp;
+
+ if (argc < 2 && !another(&argc, &argv, "local-files")) {
+ printf("usage: %s local-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ if (proxy) {
+ char *cp, *tp2, tmpbuf[MAXPATHLEN];
+
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == 0) {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != NULL) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ }
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ sendrequest((sunique) ? "STOU" : "STOR",
+ cp, tp, cp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+ return;
+ }
+ for (i = 1; i < argc; i++) {
+ char **cpp, **gargs;
+ glob_t gl;
+ int flags;
+
+ if (!doglob) {
+ if (mflag && confirm(argv[0], argv[i])) {
+ tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ argv[i], tp, tp != argv[i] || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ continue;
+ }
+
+ memset(&gl, 0, sizeof(gl));
+ flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) {
+ warnx("%s: not found", argv[i]);
+ globfree(&gl);
+ continue;
+ }
+ for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) {
+ if (mflag && confirm(argv[0], *cpp)) {
+ tp = (ntflag) ? dotrans(*cpp) : *cpp;
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ *cpp, tp, *cpp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ globfree(&gl);
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+void
+reget(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) getit(argc, argv, 1, "r+w");
+}
+
+void
+get(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" );
+}
+
+/*
+ * Receive one file.
+ */
+int
+getit(argc, argv, restartit, mode)
+ int argc;
+ char *argv[];
+ char *mode;
+ int restartit;
+{
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "remote-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-file [ local-file ]\n", argv[0]);
+ code = -1;
+ return (0);
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[2])) {
+ code = -1;
+ return (0);
+ }
+ if (loc && mcase) {
+ char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
+
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = argv[2];
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != NULL) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ argv[2] = tmpbuf;
+ }
+ }
+ if (loc && ntflag)
+ argv[2] = dotrans(argv[2]);
+ if (loc && mapflag)
+ argv[2] = domap(argv[2]);
+ if (restartit) {
+ struct stat stbuf;
+ int ret;
+
+ ret = stat(argv[2], &stbuf);
+ if (restartit == 1) {
+ if (ret < 0) {
+ warn("local: %s", argv[2]);
+ return (0);
+ }
+ restart_point = stbuf.st_size;
+ } else {
+ if (ret == 0) {
+ int overbose;
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ struct tm *tm;
+ verbose = overbose;
+ sscanf(reply_string,
+ "%*s %04d%02d%02d%02d%02d%02d",
+ &yy, &mo, &day, &hour, &min, &sec);
+ tm = gmtime(&stbuf.st_mtime);
+ tm->tm_mon++;
+ if (tm->tm_year > yy%100)
+ return (1);
+ if ((tm->tm_year == yy%100 &&
+ tm->tm_mon > mo) ||
+ (tm->tm_mon == mo &&
+ tm->tm_mday > day) ||
+ (tm->tm_mday == day &&
+ tm->tm_hour > hour) ||
+ (tm->tm_hour == hour &&
+ tm->tm_min > min) ||
+ (tm->tm_min == min &&
+ tm->tm_sec > sec))
+ return (1);
+ } else {
+ printf("%s\n", reply_string);
+ verbose = overbose;
+ return (0);
+ }
+ }
+ }
+ }
+
+ recvrequest("RETR", argv[2], argv[1], mode,
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+ restart_point = 0;
+ return (0);
+}
+
+/* ARGSUSED */
+void
+mabort(signo)
+ int signo;
+{
+ int ointer;
+
+ printf("\n");
+ (void) fflush(stdout);
+ if (mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", mname)) {
+ interactive = ointer;
+ longjmp(jabort,0);
+ }
+ interactive = ointer;
+ }
+ mflag = 0;
+ longjmp(jabort,0);
+}
+
+/*
+ * Get multiple files.
+ */
+void
+mget(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ch, ointer;
+ char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,proxy)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ for (tp2 = tmpbuf; ch = *tp++;)
+ *tp2++ = isupper(ch) ? tolower(ch) : ch;
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ recvrequest("RETR", tp, cp, "w",
+ tp != cp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mget")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT,oldintr);
+ mflag = 0;
+}
+
+char *
+remglob(argv,doswitch)
+ char *argv[];
+ int doswitch;
+{
+ char temp[16];
+ static char buf[MAXPATHLEN];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int oldverbose, oldhash;
+ char *cp, *mode;
+
+ if (!mflag) {
+ if (!doglob) {
+ args = NULL;
+ }
+ else {
+ if (ftemp) {
+ (void) fclose(ftemp);
+ ftemp = NULL;
+ }
+ }
+ return (NULL);
+ }
+ if (!doglob) {
+ if (args == NULL)
+ args = argv;
+ if ((cp = *++args) == NULL)
+ args = NULL;
+ return (cp);
+ }
+ if (ftemp == NULL) {
+ (void) strcpy(temp, _PATH_TMP);
+ (void) mktemp(temp);
+ oldverbose = verbose, verbose = 0;
+ oldhash = hash, hash = 0;
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ for (mode = "w"; *++argv != NULL; mode = "a")
+ recvrequest ("NLST", temp, *argv, mode, 0);
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ verbose = oldverbose; hash = oldhash;
+ ftemp = fopen(temp, "r");
+ (void) unlink(temp);
+ if (ftemp == NULL) {
+ printf("can't find list of remote files, oops\n");
+ return (NULL);
+ }
+ }
+ if (fgets(buf, sizeof (buf), ftemp) == NULL) {
+ (void) fclose(ftemp), ftemp = NULL;
+ return (NULL);
+ }
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ return (buf);
+}
+
+char *
+onoff(bool)
+ int bool;
+{
+
+ return (bool ? "on" : "off");
+}
+
+/*
+ * Show status.
+ */
+/*ARGSUSED*/
+void
+status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (connected)
+ printf("Connected to %s.\n", hostname);
+ else
+ printf("Not connected.\n");
+ if (!proxy) {
+ pswitch(1);
+ if (connected) {
+ printf("Connected for proxy commands to %s.\n", hostname);
+ }
+ else {
+ printf("No proxy connection.\n");
+ }
+ pswitch(0);
+ }
+ printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
+ modename, typename, formname, structname);
+ printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
+ onoff(verbose), onoff(bell), onoff(interactive),
+ onoff(doglob));
+ printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
+ onoff(runique));
+ printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
+ if (ntflag) {
+ printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
+ }
+ else {
+ printf("Ntrans: off\n");
+ }
+ if (mapflag) {
+ printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+ }
+ else {
+ printf("Nmap: off\n");
+ }
+ printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
+ onoff(hash), onoff(sendport));
+ if (macnum > 0) {
+ printf("Macros:\n");
+ for (i=0; i<macnum; i++) {
+ printf("\t%s\n",macros[i].mac_name);
+ }
+ }
+ code = 0;
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*VARARGS*/
+void
+setbell(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ bell = !bell;
+ printf("Bell mode %s.\n", onoff(bell));
+ code = bell;
+}
+
+/*
+ * Turn on packet tracing.
+ */
+/*VARARGS*/
+void
+settrace(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ trace = !trace;
+ printf("Packet tracing %s.\n", onoff(trace));
+ code = trace;
+}
+
+/*
+ * Toggle hash mark printing during transfers.
+ */
+/*VARARGS*/
+void
+sethash(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ hash = !hash;
+ printf("Hash mark printing %s", onoff(hash));
+ code = hash;
+ if (hash)
+ printf(" (%d bytes/hash mark)", 1024);
+ printf(".\n");
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*VARARGS*/
+void
+setverbose(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", onoff(verbose));
+ code = verbose;
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*VARARGS*/
+void
+setport(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ sendport = !sendport;
+ printf("Use of PORT cmds %s.\n", onoff(sendport));
+ code = sendport;
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*VARARGS*/
+void
+setprompt(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ interactive = !interactive;
+ printf("Interactive mode %s.\n", onoff(interactive));
+ code = interactive;
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*VARARGS*/
+void
+setglob(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ doglob = !doglob;
+ printf("Globbing %s.\n", onoff(doglob));
+ code = doglob;
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+/*VARARGS*/
+void
+setdebug(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int val;
+
+ if (argc > 1) {
+ val = atoi(argv[1]);
+ if (val < 0) {
+ printf("%s: bad debugging value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ } else
+ val = !debug;
+ debug = val;
+ if (debug)
+ options |= SO_DEBUG;
+ else
+ options &= ~SO_DEBUG;
+ printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+ code = debug > 0;
+}
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+void
+cd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-directory")) {
+ printf("usage: %s remote-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("CWD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("CWD command not recognized, trying XCWD\n");
+ (void) command("XCWD %s", argv[1]);
+ }
+}
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+void
+lcd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[MAXPATHLEN];
+
+ if (argc < 2)
+ argc++, argv[1] = home;
+ if (argc != 2) {
+ printf("usage: %s local-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ if (chdir(argv[1]) < 0) {
+ warn("local: %s", argv[1]);
+ code = -1;
+ return;
+ }
+ if (getwd(buf) != NULL)
+ printf("Local directory now %s\n", buf);
+ else
+ warnx("getwd: %s", buf);
+ code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+void
+delete(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-file")) {
+ printf("usage: %s remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("DELE %s", argv[1]);
+}
+
+/*
+ * Delete multiple files.
+ */
+void
+mdelete(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ointer;
+ char *cp;
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ (void) command("DELE %s", cp);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mdelete")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Rename a remote file.
+ */
+void
+renamefile(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "from-name"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "to-name")) {
+usage:
+ printf("%s from-name to-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RNFR %s", argv[1]) == CONTINUE)
+ (void) command("RNTO %s", argv[2]);
+}
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+void
+ls(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+
+ if (argc < 2)
+ argc++, argv[1] = NULL;
+ if (argc < 3)
+ argc++, argv[2] = "-";
+ if (argc > 3) {
+ printf("usage: %s remote-directory local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
+ if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
+ code = -1;
+ return;
+ }
+ if (strcmp(argv[2], "-") && *argv[2] != '|')
+ if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
+ code = -1;
+ return;
+ }
+ recvrequest(cmd, argv[2], argv[1], "w", 0);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+void
+mls(argc, argv)
+ int argc;
+ char **argv;
+{
+ sig_t oldintr;
+ int ointer, i;
+ char *cmd, mode[1], *dest;
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-files local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ dest = argv[argc - 1];
+ argv[argc - 1] = NULL;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (!globulize(&dest) ||
+ !confirm("output to local-file:", dest)) {
+ code = -1;
+ return;
+ }
+ cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ for (i = 1; mflag && i < argc-1; ++i) {
+ *mode = (i == 1) ? 'w' : 'a';
+ recvrequest(cmd, dest, argv[i], mode, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", argv[0])) {
+ mflag ++;
+ }
+ interactive = ointer;
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+void
+shell(argc, argv)
+ int argc;
+ char **argv;
+{
+ pid_t pid;
+ sig_t old1, old2;
+ char shellnam[40], *shell, *namep;
+ union wait status;
+
+ old1 = signal (SIGINT, SIG_IGN);
+ old2 = signal (SIGQUIT, SIG_IGN);
+ if ((pid = fork()) == 0) {
+ for (pid = 3; pid < 20; pid++)
+ (void) close(pid);
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = _PATH_BSHELL;
+ namep = strrchr(shell,'/');
+ if (namep == NULL)
+ namep = shell;
+ (void) strcpy(shellnam,"-");
+ (void) strcat(shellnam, ++namep);
+ if (strcmp(namep, "sh") != 0)
+ shellnam[0] = '+';
+ if (debug) {
+ printf ("%s\n", shell);
+ (void) fflush (stdout);
+ }
+ if (argc > 1) {
+ execl(shell,shellnam,"-c",altarg,(char *)0);
+ }
+ else {
+ execl(shell,shellnam,(char *)0);
+ }
+ warn("%s", shell);
+ code = -1;
+ exit(1);
+ }
+ if (pid > 0)
+ while (wait((int *)&status) != pid)
+ ;
+ (void) signal(SIGINT, old1);
+ (void) signal(SIGQUIT, old2);
+ if (pid == -1) {
+ warn("%s", "Try again later");
+ code = -1;
+ }
+ else {
+ code = 0;
+ }
+}
+
+/*
+ * Send new user information (re-login)
+ */
+void
+user(argc, argv)
+ int argc;
+ char **argv;
+{
+ char acct[80];
+ int n, aflag = 0;
+
+ if (argc < 2)
+ (void) another(&argc, &argv, "username");
+ if (argc < 2 || argc > 4) {
+ printf("usage: %s username [password] [account]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ n = command("USER %s", argv[1]);
+ if (n == CONTINUE) {
+ if (argc < 3 )
+ argv[2] = getpass("Password: "), argc++;
+ n = command("PASS %s", argv[2]);
+ }
+ if (n == CONTINUE) {
+ if (argc < 4) {
+ printf("Account: "); (void) fflush(stdout);
+ (void) fgets(acct, sizeof(acct) - 1, stdin);
+ acct[strlen(acct) - 1] = '\0';
+ argv[3] = acct; argc++;
+ }
+ n = command("ACCT %s", argv[3]);
+ aflag++;
+ }
+ if (n != COMPLETE) {
+ fprintf(stdout, "Login failed.\n");
+ return;
+ }
+ if (!aflag && argc == 4) {
+ (void) command("ACCT %s", argv[3]);
+ }
+}
+
+/*
+ * Print working directory.
+ */
+/*VARARGS*/
+void
+pwd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ /*
+ * If we aren't verbose, this doesn't do anything!
+ */
+ verbose = 1;
+ if (command("PWD") == ERROR && code == 500) {
+ printf("PWD command not recognized, trying XPWD\n");
+ (void) command("XPWD");
+ }
+ verbose = oldverbose;
+}
+
+/*
+ * Make a directory.
+ */
+void
+makedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("MKD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("MKD command not recognized, trying XMKD\n");
+ (void) command("XMKD %s", argv[1]);
+ }
+}
+
+/*
+ * Remove a directory.
+ */
+void
+removedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RMD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("RMD command not recognized, trying XRMD\n");
+ (void) command("XRMD %s", argv[1]);
+ }
+}
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+void
+quote(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "command line to send")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("", argc, argv);
+}
+
+/*
+ * Send a SITE command to the remote machine. The line
+ * is sent verbatim to the remote machine, except that the
+ * word "SITE" is added at the front.
+ */
+void
+site(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("SITE ", argc, argv);
+}
+
+/*
+ * Turn argv[1..argc) into a space-separated string, then prepend initial text.
+ * Send the result as a one-line command and get response.
+ */
+void
+quote1(initial, argc, argv)
+ char *initial;
+ int argc;
+ char **argv;
+{
+ int i, len;
+ char buf[BUFSIZ]; /* must be >= sizeof(line) */
+
+ (void) strcpy(buf, initial);
+ if (argc > 1) {
+ len = strlen(buf);
+ len += strlen(strcpy(&buf[len], argv[1]));
+ for (i = 2; i < argc; i++) {
+ buf[len++] = ' ';
+ len += strlen(strcpy(&buf[len], argv[i]));
+ }
+ }
+ if (command(buf) == PRELIM) {
+ while (getreply(0) == PRELIM)
+ continue;
+ }
+}
+
+void
+do_chmod(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "mode"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "file-name")) {
+usage:
+ printf("usage: %s mode file-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
+}
+
+void
+do_umask(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
+ verbose = oldverbose;
+}
+
+void
+idle(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Ask the other side for help.
+ */
+void
+rmthelp(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Terminate session and exit.
+ */
+/*VARARGS*/
+void
+quit(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (connected)
+ disconnect(0, 0);
+ pswitch(1);
+ if (connected) {
+ disconnect(0, 0);
+ }
+ exit(0);
+}
+
+/*
+ * Terminate session, but don't exit.
+ */
+void
+disconnect(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (!connected)
+ return;
+ (void) command("QUIT");
+ if (cout) {
+ (void) fclose(cout);
+ }
+ cout = NULL;
+ connected = 0;
+ data = -1;
+ if (!proxy) {
+ macnum = 0;
+ }
+}
+
+int
+confirm(cmd, file)
+ char *cmd, *file;
+{
+ char line[BUFSIZ];
+
+ if (!interactive)
+ return (1);
+ printf("%s %s? ", cmd, file);
+ (void) fflush(stdout);
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return (0);
+ return (*line != 'n' && *line != 'N');
+}
+
+void
+fatal(msg)
+ char *msg;
+{
+
+ errx(1, "%s", msg);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+int
+globulize(cpp)
+ char **cpp;
+{
+ glob_t gl;
+ int flags;
+
+ if (!doglob)
+ return (1);
+
+ flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ memset(&gl, 0, sizeof(gl));
+ if (glob(*cpp, flags, NULL, &gl) ||
+ gl.gl_pathc == 0) {
+ warnx("%s: not found", *cpp);
+ globfree(&gl);
+ return (0);
+ }
+ *cpp = strdup(gl.gl_pathv[0]); /* XXX - wasted memory */
+ globfree(&gl);
+ return (1);
+}
+
+void
+account(argc,argv)
+ int argc;
+ char **argv;
+{
+ char acct[50], *ap;
+
+ if (argc > 1) {
+ ++argv;
+ --argc;
+ (void) strncpy(acct,*argv,49);
+ acct[49] = '\0';
+ while (argc > 1) {
+ --argc;
+ ++argv;
+ (void) strncat(acct,*argv, 49-strlen(acct));
+ }
+ ap = acct;
+ }
+ else {
+ ap = getpass("Account:");
+ }
+ (void) command("ACCT %s", ap);
+}
+
+jmp_buf abortprox;
+
+void
+proxabort()
+{
+
+ if (!proxy) {
+ pswitch(1);
+ }
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ longjmp(abortprox,1);
+}
+
+void
+doproxy(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct cmd *c;
+ sig_t oldintr;
+
+ if (argc < 2 && !another(&argc, &argv, "command")) {
+ printf("usage: %s command\n", argv[0]);
+ code = -1;
+ return;
+ }
+ c = getcmd(argv[1]);
+ if (c == (struct cmd *) -1) {
+ printf("?Ambiguous command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (!c->c_proxy) {
+ printf("?Invalid proxy command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (setjmp(abortprox)) {
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, proxabort);
+ pswitch(1);
+ if (c->c_conn && !connected) {
+ printf("Not connected\n");
+ (void) fflush(stdout);
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ (*c->c_handler)(argc-1, argv+1);
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+}
+
+void
+setcase(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ mcase = !mcase;
+ printf("Case mapping %s.\n", onoff(mcase));
+ code = mcase;
+}
+
+void
+setcr(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ crflag = !crflag;
+ printf("Carriage Return stripping %s.\n", onoff(crflag));
+ code = crflag;
+}
+
+void
+setntrans(argc,argv)
+ int argc;
+ char *argv[];
+{
+ if (argc == 1) {
+ ntflag = 0;
+ printf("Ntrans off.\n");
+ code = ntflag;
+ return;
+ }
+ ntflag++;
+ code = ntflag;
+ (void) strncpy(ntin, argv[1], 16);
+ ntin[16] = '\0';
+ if (argc == 2) {
+ ntout[0] = '\0';
+ return;
+ }
+ (void) strncpy(ntout, argv[2], 16);
+ ntout[16] = '\0';
+}
+
+char *
+dotrans(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ char *cp1, *cp2 = new;
+ int i, ostop, found;
+
+ for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++)
+ continue;
+ for (cp1 = name; *cp1; cp1++) {
+ found = 0;
+ for (i = 0; *(ntin + i) && i < 16; i++) {
+ if (*cp1 == *(ntin + i)) {
+ found++;
+ if (i < ostop) {
+ *cp2++ = *(ntout + i);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ *cp2++ = *cp1;
+ }
+ }
+ *cp2 = '\0';
+ return (new);
+}
+
+void
+setnmap(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+
+ if (argc == 1) {
+ mapflag = 0;
+ printf("Nmap off.\n");
+ code = mapflag;
+ return;
+ }
+ if (argc < 3 && !another(&argc, &argv, "mapout")) {
+ printf("Usage: %s [mapin mapout]\n",argv[0]);
+ code = -1;
+ return;
+ }
+ mapflag = 1;
+ code = 1;
+ cp = strchr(altarg, ' ');
+ if (proxy) {
+ while(*++cp == ' ')
+ continue;
+ altarg = cp;
+ cp = strchr(altarg, ' ');
+ }
+ *cp = '\0';
+ (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
+ while (*++cp == ' ')
+ continue;
+ (void) strncpy(mapout, cp, MAXPATHLEN - 1);
+}
+
+char *
+domap(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ char *cp1 = name, *cp2 = mapin;
+ char *tp[9], *te[9];
+ int i, toks[9], toknum = 0, match = 1;
+
+ for (i=0; i < 9; ++i) {
+ toks[i] = 0;
+ }
+ while (match && *cp1 && *cp2) {
+ switch (*cp2) {
+ case '\\':
+ if (*++cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ case '$':
+ if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
+ if (*cp1 != *(++cp2+1)) {
+ toks[toknum = *cp2 - '1']++;
+ tp[toknum] = cp1;
+ while (*++cp1 && *(cp2+1)
+ != *cp1);
+ te[toknum] = cp1;
+ }
+ cp2++;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (*cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ }
+ if (match && *cp1) {
+ cp1++;
+ }
+ if (match && *cp2) {
+ cp2++;
+ }
+ }
+ if (!match && *cp1) /* last token mismatch */
+ {
+ toks[toknum] = 0;
+ }
+ cp1 = new;
+ *cp1 = '\0';
+ cp2 = mapout;
+ while (*cp2) {
+ match = 0;
+ switch (*cp2) {
+ case '\\':
+ if (*(cp2 + 1)) {
+ *cp1++ = *++cp2;
+ }
+ break;
+ case '[':
+LOOP:
+ if (*++cp2 == '$' && isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ }
+ else {
+ while (*cp2 && *cp2 != ',' &&
+ *cp2 != ']') {
+ if (*cp2 == '\\') {
+ cp2++;
+ }
+ else if (*cp2 == '$' &&
+ isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum =
+ *cp2 - '1']) {
+ char *cp3=tp[toknum];
+
+ while (cp3 !=
+ te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ }
+ else if (*cp2) {
+ *cp1++ = *cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return (name);
+ }
+ match = 1;
+ cp2--;
+ }
+ if (match) {
+ while (*++cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return (name);
+ }
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ break;
+ case '$':
+ if (isdigit(*(cp2 + 1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp1++ = *cp2;
+ break;
+ }
+ cp2++;
+ }
+ *cp1 = '\0';
+ if (!*new) {
+ return (name);
+ }
+ return (new);
+}
+
+void
+setsunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ sunique = !sunique;
+ printf("Store unique %s.\n", onoff(sunique));
+ code = sunique;
+}
+
+void
+setrunique(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ runique = !runique;
+ printf("Receive unique %s.\n", onoff(runique));
+ code = runique;
+}
+
+/* change directory to perent directory */
+void
+cdup(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (command("CDUP") == ERROR && code == 500) {
+ if (verbose)
+ printf("CDUP command not recognized, trying XCUP\n");
+ (void) command("XCUP");
+ }
+}
+
+/* restart transfer at specific point */
+void
+restart(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc != 2)
+ printf("restart: offset not specified\n");
+ else {
+ restart_point = atol(argv[1]);
+ printf("restarting at %qd. %s\n", restart_point,
+ "execute get, put or append to initiate transfer");
+ }
+}
+
+/* show remote system type */
+void
+syst(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) command("SYST");
+}
+
+void
+macdef(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *tmp;
+ int c;
+
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name\n",argv[0]);
+ code = -1;
+ return;
+ }
+ if (interactive) {
+ printf("Enter macro line by line, terminating it with a null line\n");
+ }
+ (void) strncpy(macros[macnum].mac_name, argv[1], 8);
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf+4096) {
+ if ((c = getchar()) == EOF) {
+ printf("macdef:end of file encountered\n");
+ code = -1;
+ return;
+ }
+ if ((*tmp = c) == '\n') {
+ if (tmp == macros[macnum].mac_start) {
+ macros[macnum++].mac_end = tmp;
+ code = 0;
+ return;
+ }
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ code = 0;
+ return;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ while (1) {
+ while ((c = getchar()) != '\n' && c != EOF)
+ /* LOOP */;
+ if (c == EOF || getchar() == '\n') {
+ printf("Macro not defined - 4k buffer exceeded\n");
+ code = -1;
+ return;
+ }
+ }
+}
+
+/*
+ * Start up passive mode interaction
+ */
+void
+setpassive()
+{
+ passivemode = !passivemode;
+ printf("Passive mode %s.\n", onoff(passivemode));
+ code = passivemode;
+}
+
+/*
+ * Restrict FTP data port range to a high group of "safe" ports
+ */
+void
+setrestrict()
+{
+ restricted_data_ports = !restricted_data_ports;
+ printf("Data port range restrictions %s.\n",
+ onoff(restricted_data_ports));
+ code = restricted_data_ports;
+}
+
+/*
+ * get size of file on remote machine
+ */
+/*VARARGS*/
+void
+sizecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SIZE %s", argv[1]);
+}
+
+/*
+ * get last modification time of file on remote machine
+ */
+void
+modtime(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int overbose;
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+ &day, &hour, &min, &sec);
+ /* might want to print this in local time */
+ printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
+ mo, day, yy, hour, min, sec);
+ } else
+ printf("%s\n", reply_string);
+ verbose = overbose;
+}
+
+/*
+ * show status on reomte machine
+ */
+void
+rmtstatus(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
+}
+
+/*
+ * get file if modtime is more recent than current file
+ */
+void
+newer(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (getit(argc, argv, -1, "w"))
+ printf("Local file \"%s\" is newer than remote file \"%s\"\n",
+ argv[2], argv[1]);
+}
diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c
new file mode 100644
index 0000000..fd9afb2
--- /dev/null
+++ b/usr.bin/ftp/cmdtab.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "ftp_var.h"
+
+/*
+ * User FTP -- Command Tables.
+ */
+
+char accounthelp[] = "send account command to remote server";
+char appendhelp[] = "append to a file";
+char asciihelp[] = "set ascii transfer type";
+char beephelp[] = "beep when command completed";
+char binaryhelp[] = "set binary transfer type";
+char casehelp[] = "toggle mget upper/lower case id mapping";
+char cdhelp[] = "change remote working directory";
+char cduphelp[] = "change remote working directory to parent directory";
+char chmodhelp[] = "change file permissions of remote file";
+char connecthelp[] = "connect to remote tftp";
+char crhelp[] = "toggle carriage return stripping on ascii gets";
+char deletehelp[] = "delete remote file";
+char debughelp[] = "toggle/set debugging mode";
+char dirhelp[] = "list contents of remote directory";
+char disconhelp[] = "terminate ftp session";
+char domachelp[] = "execute macro";
+char formhelp[] = "set file transfer format";
+char globhelp[] = "toggle metacharacter expansion of local file names";
+char hashhelp[] = "toggle printing `#' for each buffer transferred";
+char helphelp[] = "print local help information";
+char idlehelp[] = "get (set) idle timer on remote side";
+char lcdhelp[] = "change local working directory";
+char lshelp[] = "list contents of remote directory";
+char macdefhelp[] = "define a macro";
+char mdeletehelp[] = "delete multiple files";
+char mdirhelp[] = "list contents of multiple remote directories";
+char mgethelp[] = "get multiple files";
+char mkdirhelp[] = "make directory on the remote machine";
+char mlshelp[] = "list contents of multiple remote directories";
+char modtimehelp[] = "show last modification time of remote file";
+char modehelp[] = "set file transfer mode";
+char mputhelp[] = "send multiple files";
+char newerhelp[] = "get file if remote file is newer than local file ";
+char nlisthelp[] = "nlist contents of remote directory";
+char nmaphelp[] = "set templates for default file name mapping";
+char ntranshelp[] = "set translation table for default file name mapping";
+char passivehelp[] = "enter passive transfer mode";
+char porthelp[] = "toggle use of PORT cmd for each data connection";
+char prompthelp[] = "force interactive prompting on multiple commands";
+char proxyhelp[] = "issue command on alternate connection";
+char pwdhelp[] = "print working directory on remote machine";
+char quithelp[] = "terminate ftp session and exit";
+char quotehelp[] = "send arbitrary ftp command";
+char receivehelp[] = "receive file";
+char regethelp[] = "get file restarting at end of local file";
+char remotehelp[] = "get help from remote server";
+char renamehelp[] = "rename file";
+char restarthelp[]= "restart file transfer at bytecount";
+char restricthelp[]= "toggle restriction of data port range";
+char rmdirhelp[] = "remove directory on the remote machine";
+char rmtstatushelp[]="show status of remote machine";
+char runiquehelp[] = "toggle store unique for local files";
+char resethelp[] = "clear queued command replies";
+char sendhelp[] = "send one file";
+char sitehelp[] = "send site specific command to remote server\n\t\tTry \"rhelp site\" or \"site help\" for more information";
+char shellhelp[] = "escape to the shell";
+char sizecmdhelp[] = "show size of remote file";
+char statushelp[] = "show current status";
+char structhelp[] = "set file transfer structure";
+char suniquehelp[] = "toggle store unique on remote machine";
+char systemhelp[] = "show remote system type";
+char tenexhelp[] = "set tenex file transfer type";
+char tracehelp[] = "toggle packet tracing";
+char typehelp[] = "set file transfer type";
+char umaskhelp[] = "get (set) umask on remote side";
+char userhelp[] = "send new user information";
+char verbosehelp[] = "toggle verbose mode";
+
+struct cmd cmdtab[] = {
+ { "!", shellhelp, 0, 0, 0, shell },
+ { "$", domachelp, 1, 0, 0, domacro },
+ { "account", accounthelp, 0, 1, 1, account},
+ { "append", appendhelp, 1, 1, 1, put },
+ { "ascii", asciihelp, 0, 1, 1, setascii },
+ { "bell", beephelp, 0, 0, 0, setbell },
+ { "binary", binaryhelp, 0, 1, 1, setbinary },
+ { "bye", quithelp, 0, 0, 0, quit },
+ { "case", casehelp, 0, 0, 1, setcase },
+ { "cd", cdhelp, 0, 1, 1, cd },
+ { "cdup", cduphelp, 0, 1, 1, cdup },
+ { "chmod", chmodhelp, 0, 1, 1, do_chmod },
+ { "close", disconhelp, 0, 1, 1, disconnect },
+ { "cr", crhelp, 0, 0, 0, setcr },
+ { "delete", deletehelp, 0, 1, 1, delete },
+ { "debug", debughelp, 0, 0, 0, setdebug },
+ { "dir", dirhelp, 1, 1, 1, ls },
+ { "disconnect", disconhelp, 0, 1, 1, disconnect },
+ { "form", formhelp, 0, 1, 1, setform },
+ { "get", receivehelp, 1, 1, 1, get },
+ { "glob", globhelp, 0, 0, 0, setglob },
+ { "hash", hashhelp, 0, 0, 0, sethash },
+ { "help", helphelp, 0, 0, 1, help },
+ { "idle", idlehelp, 0, 1, 1, idle },
+ { "image", binaryhelp, 0, 1, 1, setbinary },
+ { "lcd", lcdhelp, 0, 0, 0, lcd },
+ { "ls", lshelp, 1, 1, 1, ls },
+ { "macdef", macdefhelp, 0, 0, 0, macdef },
+ { "mdelete", mdeletehelp, 1, 1, 1, mdelete },
+ { "mdir", mdirhelp, 1, 1, 1, mls },
+ { "mget", mgethelp, 1, 1, 1, mget },
+ { "mkdir", mkdirhelp, 0, 1, 1, makedir },
+ { "mls", mlshelp, 1, 1, 1, mls },
+ { "mode", modehelp, 0, 1, 1, setftmode },
+ { "modtime", modtimehelp, 0, 1, 1, modtime },
+ { "mput", mputhelp, 1, 1, 1, mput },
+ { "newer", newerhelp, 1, 1, 1, newer },
+ { "nmap", nmaphelp, 0, 0, 1, setnmap },
+ { "nlist", nlisthelp, 1, 1, 1, ls },
+ { "ntrans", ntranshelp, 0, 0, 1, setntrans },
+ { "open", connecthelp, 0, 0, 1, setpeer },
+ { "prompt", prompthelp, 0, 0, 0, setprompt },
+ { "passive", passivehelp, 0, 0, 0, setpassive },
+ { "proxy", proxyhelp, 0, 0, 1, doproxy },
+ { "sendport", porthelp, 0, 0, 0, setport },
+ { "put", sendhelp, 1, 1, 1, put },
+ { "pwd", pwdhelp, 0, 1, 1, pwd },
+ { "quit", quithelp, 0, 0, 0, quit },
+ { "quote", quotehelp, 1, 1, 1, quote },
+ { "recv", receivehelp, 1, 1, 1, get },
+ { "reget", regethelp, 1, 1, 1, reget },
+ { "rstatus", rmtstatushelp, 0, 1, 1, rmtstatus },
+ { "rhelp", remotehelp, 0, 1, 1, rmthelp },
+ { "rename", renamehelp, 0, 1, 1, renamefile },
+ { "reset", resethelp, 0, 1, 1, reset },
+ { "restart", restarthelp, 1, 1, 1, restart },
+ { "restrict", restricthelp, 0, 0, 0, setrestrict },
+ { "rmdir", rmdirhelp, 0, 1, 1, removedir },
+ { "runique", runiquehelp, 0, 0, 1, setrunique },
+ { "send", sendhelp, 1, 1, 1, put },
+ { "site", sitehelp, 0, 1, 1, site },
+ { "size", sizecmdhelp, 1, 1, 1, sizecmd },
+ { "status", statushelp, 0, 0, 1, status },
+ { "struct", structhelp, 0, 1, 1, setstruct },
+ { "system", systemhelp, 0, 1, 1, syst },
+ { "sunique", suniquehelp, 0, 0, 1, setsunique },
+ { "tenex", tenexhelp, 0, 1, 1, settenex },
+ { "trace", tracehelp, 0, 0, 0, settrace },
+ { "type", typehelp, 0, 1, 1, settype },
+ { "user", userhelp, 0, 1, 1, user },
+ { "umask", umaskhelp, 0, 1, 1, do_umask },
+ { "verbose", verbosehelp, 0, 0, 0, setverbose },
+ { "?", helphelp, 0, 0, 1, help },
+ { 0 },
+};
+
+int NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1;
diff --git a/usr.bin/ftp/domacro.c b/usr.bin/ftp/domacro.c
new file mode 100644
index 0000000..9644487
--- /dev/null
+++ b/usr.bin/ftp/domacro.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1985, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "ftp_var.h"
+
+void
+domacro(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j, count = 2, loopflg = 0;
+ char *cp1, *cp2, line2[200];
+ struct cmd *c;
+
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name.\n", argv[0]);
+ code = -1;
+ return;
+ }
+ for (i = 0; i < macnum; ++i) {
+ if (!strncmp(argv[1], macros[i].mac_name, 9)) {
+ break;
+ }
+ }
+ if (i == macnum) {
+ printf("'%s' macro not found.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ (void) strcpy(line2, line);
+TOP:
+ cp1 = macros[i].mac_start;
+ while (cp1 != macros[i].mac_end) {
+ while (isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch(*cp1) {
+ case '\\':
+ *cp2++ = *++cp1;
+ break;
+ case '$':
+ if (isdigit(*(cp1+1))) {
+ j = 0;
+ while (isdigit(*++cp1)) {
+ j = 10*j + *cp1 - '0';
+ }
+ cp1--;
+ if (argc - 2 >= j) {
+ (void) strcpy(cp2, argv[j+1]);
+ cp2 += strlen(argv[j+1]);
+ }
+ break;
+ }
+ if (*(cp1+1) == 'i') {
+ loopflg = 1;
+ cp1++;
+ if (count < argc) {
+ (void) strcpy(cp2, argv[count]);
+ cp2 += strlen(argv[count]);
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp2++ = *cp1;
+ break;
+ }
+ if (*cp1 != '\0') {
+ cp1++;
+ }
+ }
+ *cp2 = '\0';
+ makeargv();
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ code = -1;
+ }
+ else if (c == 0) {
+ printf("?Invalid command\n");
+ code = -1;
+ }
+ else if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ code = -1;
+ }
+ else {
+ if (verbose) {
+ printf("%s\n",line);
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell) {
+ (void) putchar('\007');
+ }
+ (void) strcpy(line, line2);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (cp1 != macros[i].mac_end) {
+ cp1++;
+ }
+ }
+ if (loopflg && ++count < argc) {
+ goto TOP;
+ }
+}
diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h
new file mode 100644
index 0000000..fb9aecf
--- /dev/null
+++ b/usr.bin/ftp/extern.h
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 1994 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.2 (Berkeley) 4/3/94
+ */
+
+struct timeval;
+struct fd_set;
+
+void abort_remote __P((FILE *));
+void abortpt __P(());
+void abortrecv __P(());
+void abortsend __P(());
+void account __P((int, char **));
+int another __P((int *, char ***, char *));
+void blkfree __P((char **));
+void cd __P((int, char **));
+void cdup __P((int, char **));
+void changetype __P((int, int));
+void cmdabort __P(());
+void cmdscanner __P((int));
+int command __P(());
+int confirm __P((char *, char *));
+FILE *dataconn __P((char *));
+void delete __P((int, char **));
+void disconnect __P((int, char **));
+void do_chmod __P((int, char **));
+void do_umask __P((int, char **));
+void domacro __P((int, char **));
+char *domap __P((char *));
+void doproxy __P((int, char **));
+char *dotrans __P((char *));
+int empty __P((struct fd_set *, int));
+void fatal __P((char *));
+void get __P((int, char **));
+struct cmd *getcmd __P((char *));
+int getit __P((int, char **, int, char *));
+int getreply __P((int));
+int globulize __P((char **));
+char *gunique __P((char *));
+void help __P((int, char **));
+char *hookup __P((char *, int));
+void idle __P((int, char **));
+int initconn __P((void));
+void intr __P(());
+void lcd __P((int, char **));
+int login __P((char *));
+void lostpeer __P(());
+void ls __P((int, char **));
+void mabort __P((int));
+void macdef __P((int, char **));
+void makeargv __P((void));
+void makedir __P((int, char **));
+void mdelete __P((int, char **));
+void mget __P((int, char **));
+void mls __P((int, char **));
+void modtime __P((int, char **));
+void mput __P((int, char **));
+char *onoff __P((int));
+void newer __P((int, char **));
+void proxabort __P(());
+void proxtrans __P((char *, char *, char *));
+void psabort __P(());
+void pswitch __P((int));
+void ptransfer __P((char *, long, struct timeval *, struct timeval *));
+void put __P((int, char **));
+void pwd __P((int, char **));
+void quit __P((int, char **));
+void quote __P((int, char **));
+void quote1 __P((char *, int, char **));
+void recvrequest __P((char *, char *, char *, char *, int));
+void reget __P((int, char **));
+char *remglob __P((char **, int));
+void removedir __P((int, char **));
+void renamefile __P((int, char **));
+void reset __P((int, char **));
+void restart __P((int, char **));
+void rmthelp __P((int, char **));
+void rmtstatus __P((int, char **));
+int ruserpass __P((char *, char **, char **, char **));
+void sendrequest __P((char *, char *, char *, int));
+void setascii __P((int, char **));
+void setbell __P((int, char **));
+void setbinary __P((int, char **));
+void setcase __P((int, char **));
+void setcr __P((int, char **));
+void setdebug __P((int, char **));
+void setform __P((int, char **));
+void setftmode __P((int, char **));
+void setglob __P((int, char **));
+void sethash __P((int, char **));
+void setnmap __P((int, char **));
+void setntrans __P((int, char **));
+void setpassive __P(());
+void setpeer __P((int, char **));
+void setport __P((int, char **));
+void setprompt __P((int, char **));
+void setrestrict __P(());
+void setrunique __P((int, char **));
+void setstruct __P((int, char **));
+void setsunique __P((int, char **));
+void settenex __P((int, char **));
+void settrace __P((int, char **));
+void settype __P((int, char **));
+void setverbose __P((int, char **));
+void shell __P((int, char **));
+void site __P((int, char **));
+void sizecmd __P((int, char **));
+char *slurpstring __P((void));
+void status __P((int, char **));
+void syst __P((int, char **));
+void tvsub __P((struct timeval *, struct timeval *, struct timeval *));
+void user __P((int, char **));
+
+extern jmp_buf abortprox;
+extern int abrtflag;
+extern struct cmd cmdtab[];
+extern FILE *cout;
+extern int data;
+extern char *home;
+extern jmp_buf jabort;
+extern int proxy;
+extern char reply_string[];
+extern off_t restart_point;
+extern int NCMDS;
diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1
new file mode 100644
index 0000000..4a3df6a
--- /dev/null
+++ b/usr.bin/ftp/ftp.1
@@ -0,0 +1,1154 @@
+.\" Copyright (c) 1985, 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ftp.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt FTP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ftp
+.Nd
+.Tn ARPANET
+file transfer program
+.Sh SYNOPSIS
+.Nm ftp
+.Op Fl v
+.Op Fl d
+.Op Fl i
+.Op Fl n
+.Op Fl U
+.Op Fl p
+.Op Fl g
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Ftp
+is the user interface to the
+.Tn ARPANET
+standard File Transfer Protocol.
+The program allows a user to transfer files to and from a
+remote network site.
+.Pp
+Options may be specified at the command line, or to the
+command interpreter.
+.Bl -tag -width flag
+.It Fl v
+Verbose option forces
+.Nm ftp
+to show all responses from the remote server, as well
+as report on data transfer statistics.
+.It Fl n
+Restrains
+.Nm ftp
+from attempting \*(Lqauto-login\*(Rq upon initial connection.
+If auto-login is enabled,
+.Nm ftp
+will check the
+.Pa .netrc
+(see below) file in the user's home directory for an entry describing
+an account on the remote machine.
+If no entry exists,
+.Nm ftp
+will prompt for the remote machine login name (default is the user
+identity on the local machine), and, if necessary, prompt for a password
+and an account with which to login.
+.It Fl i
+Turns off interactive prompting during
+multiple file transfers.
+.It Fl d
+Enables debugging.
+.It Fl g
+Disables file name globbing.
+.It Fl U
+Disable data port range restrictions.
+.It Fl p
+Enable passive mode operation for use behind connection filtering firewalls.
+.El
+.Pp
+The client host with which
+.Nm ftp
+is to communicate may be specified on the command line.
+If this is done,
+.Nm ftp
+will immediately attempt to establish a connection to an
+.Tn FTP
+server on that host; otherwise,
+.Nm ftp
+will enter its command interpreter and await instructions
+from the user.
+When
+.Nm ftp
+is awaiting commands from the user the prompt
+.Ql ftp>
+is provided to the user.
+The following commands are recognized
+by
+.Nm ftp :
+.Bl -tag -width Fl
+.It Ic \&! Op Ar command Op Ar args
+Invoke an interactive shell on the local machine.
+If there are arguments, the first is taken to be a command to execute
+directly, with the rest of the arguments as its arguments.
+.It Ic \&$ Ar macro-name Op Ar args
+Execute the macro
+.Ar macro-name
+that was defined with the
+.Ic macdef
+command.
+Arguments are passed to the macro unglobbed.
+.It Ic account Op Ar passwd
+Supply a supplemental password required by a remote system for access
+to resources once a login has been successfully completed.
+If no argument is included, the user will be prompted for an account
+password in a non-echoing input mode.
+.It Ic append Ar local-file Op Ar remote-file
+Append a local file to a file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used in naming the
+remote file after being altered by any
+.Ic ntrans
+or
+.Ic nmap
+setting.
+File transfer uses the current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic ascii
+Set the file transfer
+.Ic type
+to network
+.Tn ASCII .
+This is the default type.
+.It Ic bell
+Arrange that a bell be sounded after each file transfer
+command is completed.
+.It Ic binary
+Set the file transfer
+.Ic type
+to support binary image transfer.
+.It Ic bye
+Terminate the
+.Tn FTP
+session with the remote server
+and exit
+.Nm ftp .
+An end of file will also terminate the session and exit.
+.It Ic case
+Toggle remote computer file name case mapping during
+.Ic mget
+commands.
+When
+.Ic case
+is on (default is off), remote computer file names with all letters in
+upper case are written in the local directory with the letters mapped
+to lower case.
+.It Ic \&cd Ar remote-directory
+Change the working directory on the remote machine
+to
+.Ar remote-directory .
+.It Ic cdup
+Change the remote machine working directory to the parent of the
+current remote machine working directory.
+.It Ic chmod Ar mode file-name
+Change the permission modes of the file
+.Ar file-name
+on the remote
+sytem to
+.Ar mode .
+.It Ic close
+Terminate the
+.Tn FTP
+session with the remote server, and
+return to the command interpreter.
+Any defined macros are erased.
+.It Ic \&cr
+Toggle carriage return stripping during
+ascii type file retrieval.
+Records are denoted by a carriage return/linefeed sequence
+during ascii type file transfer.
+When
+.Ic \&cr
+is on (the default), carriage returns are stripped from this
+sequence to conform with the
+.Ux
+single linefeed record
+delimiter.
+Records on
+.Pf non\- Ns Ux
+remote systems may contain single linefeeds;
+when an ascii type transfer is made, these linefeeds may be
+distinguished from a record delimiter only when
+.Ic \&cr
+is off.
+.It Ic delete Ar remote-file
+Delete the file
+.Ar remote-file
+on the remote machine.
+.It Ic debug Op Ar debug-value
+Toggle debugging mode.
+If an optional
+.Ar debug-value
+is specified it is used to set the debugging level.
+When debugging is on,
+.Nm ftp
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\->
+.It Xo
+.Ic dir
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the directory contents in the
+directory,
+.Ar remote-directory ,
+and, optionally, placing the output in
+.Ar local-file .
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic dir
+output.
+If no directory is specified, the current working
+directory on the remote machine is used.
+If no local
+file is specified, or
+.Ar local-file
+is
+.Fl ,
+output comes to the terminal.
+.It Ic disconnect
+A synonym for
+.Ar close .
+.It Ic form Ar format
+Set the file transfer
+.Ic form
+to
+.Ar format .
+The default format is \*(Lqfile\*(Rq.
+.It Ic get Ar remote-file Op Ar local-file
+Retrieve the
+.Ar remote-file
+and store it on the local machine.
+If the local
+file name is not specified, it is given the same
+name it has on the remote machine, subject to
+alteration by the current
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+The current settings for
+.Ic type ,
+.Ic form ,
+.Ic mode ,
+and
+.Ic structure
+are used while transferring the file.
+.It Ic glob
+Toggle filename expansion for
+.Ic mdelete ,
+.Ic mget
+and
+.Ic mput .
+If globbing is turned off with
+.Ic glob ,
+the file name arguments
+are taken literally and not expanded.
+Globbing for
+.Ic mput
+is done as in
+.Xr csh 1 .
+For
+.Ic mdelete
+and
+.Ic mget ,
+each remote file name is expanded
+separately on the remote machine and the lists are not merged.
+Expansion of a directory name is likely to be
+different from expansion of the name of an ordinary file:
+the exact result depends on the foreign operating system and ftp server,
+and can be previewed by doing
+.Ql mls remote-files \-
+Note:
+.Ic mget
+and
+.Ic mput
+are not meant to transfer
+entire directory subtrees of files.
+That can be done by
+transferring a
+.Xr tar 1
+archive of the subtree (in binary mode).
+.It Ic hash
+Toggle hash-sign (``#'') printing for each data block
+transferred.
+The size of a data block is 1024 bytes.
+.It Ic help Op Ar command
+Print an informative message about the meaning of
+.Ar command .
+If no argument is given,
+.Nm ftp
+prints a list of the known commands.
+.It Ic idle Op Ar seconds
+Set the inactivity timer on the remote server to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is omitted, the current inactivity timer is printed.
+.It Ic lcd Op Ar directory
+Change the working directory on the local machine.
+If
+no
+.Ar directory
+is specified, the user's home directory is used.
+.It Xo
+.Ic \&ls
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the contents of a
+directory on the remote machine.
+The listing includes any system-dependent information that the server
+chooses to include; for example, most
+.Ux
+systems will produce
+output from the command
+.Ql ls \-l .
+(See also
+.Ic nlist . )
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic \&ls
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Sq Fl ,
+the output is sent to the terminal.
+.It Ic macdefNs Ar macro-name
+Define a macro.
+Subsequent lines are stored as the macro
+.Ar macro-name ;
+a null line (consecutive newline characters
+in a file or
+carriage returns from the terminal) terminates macro input mode.
+There is a limit of 16 macros and 4096 total characters in all
+defined macros.
+Macros remain defined until a
+.Ic close
+command is executed.
+The macro processor interprets `$' and `\e' as special characters.
+A `$' followed by a number (or numbers) is replaced by the
+corresponding argument on the macro invocation command line.
+A `$' followed by an `i' signals that macro processor that the
+executing macro is to be looped.
+On the first pass `$i' is
+replaced by the first argument on the macro invocation command line,
+on the second pass it is replaced by the second argument, and so on.
+A `\e' followed by any character is replaced by that character.
+Use the `\e' to prevent special treatment of the `$'.
+.It Ic mdelete Op Ar remote-files
+Delete the
+.Ar remote-files
+on the remote machine.
+.It Ic mdir Ar remote-files local-file
+Like
+.Ic dir ,
+except multiple remote files may be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mdir
+output.
+.It Ic mget Ar remote-files
+Expand the
+.Ar remote-files
+on the remote machine
+and do a
+.Ic get
+for each file name thus produced.
+See
+.Ic glob
+for details on the filename expansion.
+Resulting file names will then be processed according to
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+Files are transferred into the local working directory,
+which can be changed with
+.Ql lcd directory ;
+new local directories can be created with
+.Ql "\&! mkdir directory" .
+.It Ic mkdir Ar directory-name
+Make a directory on the remote machine.
+.It Ic mls Ar remote-files local-file
+Like
+.Ic nlist ,
+except multiple remote files may be specified,
+and the
+.Ar local-file
+must be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mls
+output.
+.It Ic mode Op Ar mode-name
+Set the file transfer
+.Ic mode
+to
+.Ar mode-name .
+The default mode is \*(Lqstream\*(Rq mode.
+.It Ic modtime Ar file-name
+Show the last modification time of the file on the remote machine.
+.It Ic mput Ar local-files
+Expand wild cards in the list of local files given as arguments
+and do a
+.Ic put
+for each file in the resulting list.
+See
+.Ic glob
+for details of filename expansion.
+Resulting file names will then be processed according to
+.Ic ntrans
+and
+.Ic nmap
+settings.
+.It Ic newer Ar file-name
+Get the file only if the modification time of the remote file is more
+recent that the file on the current system.
+If the file does not
+exist on the current system, the remote file is considered
+.Ic newer .
+Otherwise, this command is identical to
+.Ar get .
+.It Xo
+.Ic nlist
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a list of the files in a
+directory on the remote machine.
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic nlist
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Fl ,
+the output is sent to the terminal.
+.It Ic nmap Op Ar inpattern outpattern
+Set or unset the filename mapping mechanism.
+If no arguments are specified, the filename mapping mechanism is unset.
+If arguments are specified, remote filenames are mapped during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, local filenames are mapped during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+The mapping follows the pattern set by
+.Ar inpattern
+and
+.Ar outpattern .
+.Op Ar Inpattern
+is a template for incoming filenames (which may have already been
+processed according to the
+.Ic ntrans
+and
+.Ic case
+settings).
+Variable templating is accomplished by including the
+sequences `$1', `$2', ..., `$9' in
+.Ar inpattern .
+Use `\\' to prevent this special treatment of the `$' character.
+All other characters are treated literally, and are used to determine the
+.Ic nmap
+.Op Ar inpattern
+variable values.
+For example, given
+.Ar inpattern
+$1.$2 and the remote file name "mydata.data", $1 would have the value
+"mydata", and $2 would have the value "data".
+The
+.Ar outpattern
+determines the resulting mapped filename.
+The sequences `$1', `$2', ...., `$9' are replaced by any value resulting
+from the
+.Ar inpattern
+template.
+The sequence `$0' is replace by the original filename.
+Additionally, the sequence
+.Ql Op Ar seq1 , Ar seq2
+is replaced by
+.Op Ar seq1
+if
+.Ar seq1
+is not a null string; otherwise it is replaced by
+.Ar seq2 .
+For example, the command
+.Pp
+.Bd -literal -offset indent -compact
+nmap $1.$2.$3 [$1,$2].[$2,file]
+.Ed
+.Pp
+would yield
+the output filename "myfile.data" for input filenames "myfile.data" and
+"myfile.data.old", "myfile.file" for the input filename "myfile", and
+"myfile.myfile" for the input filename ".myfile".
+Spaces may be included in
+.Ar outpattern ,
+as in the example: `nmap $1 sed "s/ *$//" > $1' .
+Use the `\e' character to prevent special treatment
+of the `$','[','[', and `,' characters.
+.It Ic ntrans Op Ar inchars Op Ar outchars
+Set or unset the filename character translation mechanism.
+If no arguments are specified, the filename character
+translation mechanism is unset.
+If arguments are specified, characters in
+remote filenames are translated during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, characters in
+local filenames are translated during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+Characters in a filename matching a character in
+.Ar inchars
+are replaced with the corresponding character in
+.Ar outchars .
+If the character's position in
+.Ar inchars
+is longer than the length of
+.Ar outchars ,
+the character is deleted from the file name.
+.It Ic open Ar host Op Ar port
+Establish a connection to the specified
+.Ar host
+.Tn FTP
+server.
+An optional port number may be supplied,
+in which case,
+.Nm ftp
+will attempt to contact an
+.Tn FTP
+server at that port.
+If the
+.Ic auto-login
+option is on (default),
+.Nm ftp
+will also attempt to automatically log the user in to
+the
+.Tn FTP
+server (see below).
+.It Ic prompt
+Toggle interactive prompting.
+Interactive prompting
+occurs during multiple file transfers to allow the
+user to selectively retrieve or store files.
+If prompting is turned off (default is on), any
+.Ic mget
+or
+.Ic mput
+will transfer all files, and any
+.Ic mdelete
+will delete all files.
+.It Ic proxy Ar ftp-command
+Execute an ftp command on a secondary control connection.
+This command allows simultaneous connection to two remote ftp
+servers for transferring files between the two servers.
+The first
+.Ic proxy
+command should be an
+.Ic open ,
+to establish the secondary control connection.
+Enter the command "proxy ?" to see other ftp commands executable on the
+secondary connection.
+The following commands behave differently when prefaced by
+.Ic proxy :
+.Ic open
+will not define new macros during the auto-login process,
+.Ic close
+will not erase existing macro definitions,
+.Ic get
+and
+.Ic mget
+transfer files from the host on the primary control connection
+to the host on the secondary control connection, and
+.Ic put ,
+.Ic mput ,
+and
+.Ic append
+transfer files from the host on the secondary control connection
+to the host on the primary control connection.
+Third party file transfers depend upon support of the ftp protocol
+.Dv PASV
+command by the server on the secondary control connection.
+.It Ic put Ar local-file Op Ar remote-file
+Store a local file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used
+after processing according to any
+.Ic ntrans
+or
+.Ic nmap
+settings
+in naming the remote file.
+File transfer uses the
+current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic pwd
+Print the name of the current working directory on the remote
+machine.
+.It Ic quit
+A synonym for
+.Ic bye .
+.It Ic quote Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server.
+.It Ic recv Ar remote-file Op Ar local-file
+A synonym for get.
+.It Ic reget Ar remote-file Op Ar local-file
+Reget acts like get, except that if
+.Ar local-file
+exists and is
+smaller than
+.Ar remote-file ,
+.Ar local-file
+is presumed to be
+a partially transferred copy of
+.Ar remote-file
+and the transfer
+is continued from the apparent point of failure.
+This command
+is useful when transferring very large files over networks that
+are prone to dropping connections.
+.It Ic remotehelp Op Ar command-name
+Request help from the remote
+.Tn FTP
+server.
+If a
+.Ar command-name
+is specified it is supplied to the server as well.
+.It Ic remotestatus Op Ar file-name
+With no arguments, show status of remote machine.
+If
+.Ar file-name
+is specified, show status of
+.Ar file-name
+on remote machine.
+.It Xo
+.Ic rename
+.Op Ar from
+.Op Ar to
+.Xc
+Rename the file
+.Ar from
+on the remote machine, to the file
+.Ar to .
+.It Ic reset
+Clear reply queue.
+This command re-synchronizes command/reply sequencing with the remote
+ftp server.
+Resynchronization may be necessary following a violation of the ftp protocol
+by the remote server.
+.It Ic restart Ar marker
+Restart the immediately following
+.Ic get
+or
+.Ic put
+at the
+indicated
+.Ar marker .
+On
+.Ux
+systems, marker is usually a byte
+offset into the file.
+.It Ic restrict
+Toggle data port range restrictions.
+When not operating in passive mode, the
+.Nm ftp ,
+client program requests that the remote server open a connection back
+to the client host on a separate data port. In previous versions, that
+remote port fell in the range 1024..4999. However, most firewall setups
+filter that range of TCP ports because other services reside there.
+The default behavior now is for the client to request that the server
+connect back to the client using the port range 40000..44999. Firewall
+administrators can chose to allow TCP connections in that range, if they
+deem it to not be a security risk.
+.It Ic rmdir Ar directory-name
+Delete a directory on the remote machine.
+.It Ic runique
+Toggle storing of files on the local system with unique filenames.
+If a file already exists with a name equal to the target
+local filename for a
+.Ic get
+or
+.Ic mget
+command, a ".1" is appended to the name.
+If the resulting name matches another existing file,
+a ".2" is appended to the original name.
+If this process continues up to ".99", an error
+message is printed, and the transfer does not take place.
+The generated unique filename will be reported.
+Note that
+.Ic runique
+will not affect local files generated from a shell command
+(see below).
+The default value is off.
+.It Ic send Ar local-file Op Ar remote-file
+A synonym for put.
+.It Ic sendport
+Toggle the use of
+.Dv PORT
+commands.
+By default,
+.Nm ftp
+will attempt to use a
+.Dv PORT
+command when establishing
+a connection for each data transfer.
+The use of
+.Dv PORT
+commands can prevent delays
+when performing multiple file transfers.
+If the
+.Dv PORT
+command fails,
+.Nm ftp
+will use the default data port.
+When the use of
+.Dv PORT
+commands is disabled, no attempt will be made to use
+.Dv PORT
+commands for each data transfer.
+This is useful
+for certain
+.Tn FTP
+implementations which do ignore
+.Dv PORT
+commands but, incorrectly, indicate they've been accepted.
+.It Ic site Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server as a
+.Dv SITE
+command.
+.It Ic size Ar file-name
+Return size of
+.Ar file-name
+on remote machine.
+.It Ic status
+Show the current status of
+.Nm ftp .
+.It Ic struct Op Ar struct-name
+Set the file transfer
+.Ar structure
+to
+.Ar struct-name .
+By default \*(Lqstream\*(Rq structure is used.
+.It Ic sunique
+Toggle storing of files on remote machine under unique file names.
+Remote ftp server must support ftp protocol
+.Dv STOU
+command for
+successful completion.
+The remote server will report unique name.
+Default value is off.
+.It Ic system
+Show the type of operating system running on the remote machine.
+.It Ic tenex
+Set the file transfer type to that needed to
+talk to
+.Tn TENEX
+machines.
+.It Ic trace
+Toggle packet tracing.
+.It Ic type Op Ar type-name
+Set the file transfer
+.Ic type
+to
+.Ar type-name .
+If no type is specified, the current type
+is printed.
+The default type is network
+.Tn ASCII .
+.It Ic umask Op Ar newmask
+Set the default umask on the remote server to
+.Ar newmask .
+If
+.Ar newmask
+is omitted, the current umask is printed.
+.It Xo
+.Ic user Ar user-name
+.Op Ar password
+.Op Ar account
+.Xc
+Identify yourself to the remote
+.Tn FTP
+server.
+If the
+.Ar password
+is not specified and the server requires it,
+.Nm ftp
+will prompt the user for it (after disabling local echo).
+If an
+.Ar account
+field is not specified, and the
+.Tn FTP
+server
+requires it, the user will be prompted for it.
+If an
+.Ar account
+field is specified, an account command will
+be relayed to the remote server after the login sequence
+is completed if the remote server did not require it
+for logging in.
+Unless
+.Nm ftp
+is invoked with \*(Lqauto-login\*(Rq disabled, this
+process is done automatically on initial connection to
+the
+.Tn FTP
+server.
+.It Ic verbose
+Toggle verbose mode.
+In verbose mode, all responses from
+the
+.Tn FTP
+server are displayed to the user.
+In addition,
+if verbose is on, when a file transfer completes, statistics
+regarding the efficiency of the transfer are reported.
+By default,
+verbose is on.
+.It Ic ? Op Ar command
+A synonym for help.
+.El
+.Pp
+Command arguments which have embedded spaces may be quoted with
+quote `"' marks.
+.Sh ABORTING A FILE TRANSFER
+To abort a file transfer, use the terminal interrupt key
+(usually Ctrl-C).
+Sending transfers will be immediately halted.
+Receiving transfers will be halted by sending a ftp protocol
+.Dv ABOR
+command to the remote server, and discarding any further data received.
+The speed at which this is accomplished depends upon the remote
+server's support for
+.Dv ABOR
+processing.
+If the remote server does not support the
+.Dv ABOR
+command, an
+.Ql ftp>
+prompt will not appear until the remote server has completed
+sending the requested file.
+.Pp
+The terminal interrupt key sequence will be ignored when
+.Nm ftp
+has completed any local processing and is awaiting a reply
+from the remote server.
+A long delay in this mode may result from the ABOR processing described
+above, or from unexpected behavior by the remote server, including
+violations of the ftp protocol.
+If the delay results from unexpected remote server behavior, the local
+.Nm ftp
+program must be killed by hand.
+.Sh FILE NAMING CONVENTIONS
+Files specified as arguments to
+.Nm ftp
+commands are processed according to the following rules.
+.Bl -enum
+.It
+If the file name
+.Sq Fl
+is specified, the
+.Ar stdin
+(for reading) or
+.Ar stdout
+(for writing) is used.
+.It
+If the first character of the file name is
+.Sq \&| ,
+the
+remainder of the argument is interpreted as a shell command.
+.Nm Ftp
+then forks a shell, using
+.Xr popen 3
+with the argument supplied, and reads (writes) from the stdout
+(stdin).
+If the shell command includes spaces, the argument
+must be quoted; e.g.
+\*(Lq" ls -lt"\*(Rq.
+A particularly
+useful example of this mechanism is: \*(Lqdir more\*(Rq.
+.It
+Failing the above checks, if ``globbing'' is enabled,
+local file names are expanded
+according to the rules used in the
+.Xr csh 1 ;
+c.f. the
+.Ic glob
+command.
+If the
+.Nm ftp
+command expects a single local file (.e.g.
+.Ic put ) ,
+only the first filename generated by the "globbing" operation is used.
+.It
+For
+.Ic mget
+commands and
+.Ic get
+commands with unspecified local file names, the local filename is
+the remote filename, which may be altered by a
+.Ic case ,
+.Ic ntrans ,
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered if
+.Ic runique
+is on.
+.It
+For
+.Ic mput
+commands and
+.Ic put
+commands with unspecified remote file names, the remote filename is
+the local filename, which may be altered by a
+.Ic ntrans
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered by the remote server if
+.Ic sunique
+is on.
+.El
+.Sh FILE TRANSFER PARAMETERS
+The FTP specification specifies many parameters which may
+affect a file transfer.
+The
+.Ic type
+may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary),
+\*(Lqebcdic\*(Rq, and \*(Lqlocal byte size\*(Rq (for
+.Tn PDP Ns -10's
+and
+.Tn PDP Ns -20's
+mostly).
+.Nm Ftp
+supports the ascii and image types of file transfer,
+plus local byte size 8 for
+.Ic tenex
+mode transfers.
+.Pp
+.Nm Ftp
+supports only the default values for the remaining
+file transfer parameters:
+.Ic mode ,
+.Ic form ,
+and
+.Ic struct .
+.Sh THE .netrc FILE
+The
+.Pa .netrc
+file contains login and initialization information
+used by the auto-login process.
+It resides in the user's home directory.
+The following tokens are recognized; they may be separated by spaces,
+tabs, or new-lines:
+.Bl -tag -width password
+.It Ic machine Ar name
+Identify a remote machine
+.Ar name .
+The auto-login process searches the
+.Pa .netrc
+file for a
+.Ic machine
+token that matches the remote machine specified on the
+.Nm ftp
+command line or as an
+.Ic open
+command argument.
+Once a match is made, the subsequent
+.Pa .netrc
+tokens are processed,
+stopping when the end of file is reached or another
+.Ic machine
+or a
+.Ic default
+token is encountered.
+.It Ic default
+This is the same as
+.Ic machine
+.Ar name
+except that
+.Ic default
+matches any name.
+There can be only one
+.Ic default
+token, and it must be after all
+.Ic machine
+tokens.
+This is normally used as:
+.Pp
+.Dl default login anonymous password user@site
+.Pp
+thereby giving the user
+.Ar automatic
+anonymous ftp login to
+machines not specified in
+.Pa .netrc .
+This can be overridden
+by using the
+.Fl n
+flag to disable auto-login.
+.It Ic login Ar name
+Identify a user on the remote machine.
+If this token is present, the auto-login process will initiate
+a login using the specified
+.Ar name .
+.It Ic password Ar string
+Supply a password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires a password as part
+of the login process.
+Note that if this token is present in the
+.Pa .netrc
+file for any user other
+than
+.Ar anonymous ,
+.Nm ftp
+will abort the auto-login process if the
+.Pa .netrc
+is readable by
+anyone besides the user.
+.It Ic account Ar string
+Supply an additional account password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires an additional
+account password, or the auto-login process will initiate an
+.Dv ACCT
+command if it does not.
+.It Ic macdef Ar name
+Define a macro.
+This token functions like the
+.Nm ftp
+.Ic macdef
+command functions.
+A macro is defined with the specified name; its contents begin with the
+next
+.Pa .netrc
+line and continue until a null line (consecutive new-line
+characters) is encountered.
+If a macro named
+.Ic init
+is defined, it is automatically executed as the last step in the
+auto-login process.
+.El
+.Sh ENVIRONMENT
+.Nm Ftp
+utilizes the following environment variables.
+.Bl -tag -width Fl
+.It Ev HOME
+For default location of a
+.Pa .netrc
+file, if one exists.
+.It Ev SHELL
+For default shell.
+.El
+.Sh SEE ALSO
+.Xr ftpd 8
+.Sh HISTORY
+The
+.Nm ftp
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+Correct execution of many commands depends upon proper behavior
+by the remote server.
+.Pp
+An error in the treatment of carriage returns
+in the
+.Bx 4.2
+ascii-mode transfer code
+has been corrected.
+This correction may result in incorrect transfers of binary files
+to and from
+.Bx 4.2
+servers using the ascii type.
+Avoid this problem by using the binary image type.
diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c
new file mode 100644
index 0000000..137e8c2
--- /dev/null
+++ b/usr.bin/ftp/ftp.c
@@ -0,0 +1,1559 @@
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftp.c 8.4 (Berkeley) 4/6/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <arpa/ftp.h>
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <varargs.h>
+
+#include "ftp_var.h"
+
+extern int h_errno;
+
+struct sockaddr_in hisctladdr;
+struct sockaddr_in data_addr;
+int data = -1;
+int abrtflag = 0;
+jmp_buf ptabort;
+int ptabflg;
+int ptflag = 0;
+struct sockaddr_in myctladdr;
+off_t restart_point = 0;
+
+
+FILE *cin, *cout;
+
+char *
+hookup(host, port)
+ char *host;
+ int port;
+{
+ struct hostent *hp = 0;
+ int s, len, tos;
+ static char hostnamebuf[80];
+
+ memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ hisctladdr.sin_family = AF_INET;
+ (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf));
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ code = -1;
+ return ((char *) 0);
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+ memmove((caddr_t)&hisctladdr.sin_addr,
+ hp->h_addr_list[0], hp->h_length);
+ (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
+ }
+ hostname = hostnamebuf;
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ code = -1;
+ return (0);
+ }
+ hisctladdr.sin_port = port;
+ while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
+ if (hp && hp->h_addr_list[1]) {
+ int oerrno = errno;
+ char *ia;
+
+ ia = inet_ntoa(hisctladdr.sin_addr);
+ errno = oerrno;
+ warn("connect to address %s", ia);
+ hp->h_addr_list++;
+ memmove((caddr_t)&hisctladdr.sin_addr,
+ hp->h_addr_list[0], hp->h_length);
+ fprintf(stdout, "Trying %s...\n",
+ inet_ntoa(hisctladdr.sin_addr));
+ (void) close(s);
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ code = -1;
+ return (0);
+ }
+ continue;
+ }
+ warn("connect");
+ code = -1;
+ goto bad;
+ }
+ len = sizeof (myctladdr);
+ if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+ warn("getsockname");
+ code = -1;
+ goto bad;
+ }
+#ifdef IP_TOS
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ cin = fdopen(s, "r");
+ cout = fdopen(s, "w");
+ if (cin == NULL || cout == NULL) {
+ warnx("fdopen failed.");
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+ if (verbose)
+ printf("Connected to %s.\n", hostname);
+ if (getreply(0) > 2) { /* read startup message from server */
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+#ifdef SO_OOBINLINE
+ {
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))
+ < 0 && debug) {
+ warn("setsockopt");
+ }
+ }
+#endif /* SO_OOBINLINE */
+
+ return (hostname);
+bad:
+ (void) close(s);
+ return ((char *)0);
+}
+
+int
+login(host)
+ char *host;
+{
+ char tmp[80];
+ char *user, *pass, *acct;
+ int n, aflag = 0;
+
+ user = pass = acct = 0;
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ code = -1;
+ return (0);
+ }
+ while (user == NULL) {
+ char *myname = getlogin();
+
+ if (myname == NULL) {
+ struct passwd *pp = getpwuid(getuid());
+
+ if (pp != NULL)
+ myname = pp->pw_name;
+ }
+ if (myname)
+ printf("Name (%s:%s): ", host, myname);
+ else
+ printf("Name (%s): ", host);
+ (void) fgets(tmp, sizeof(tmp) - 1, stdin);
+ tmp[strlen(tmp) - 1] = '\0';
+ if (*tmp == '\0')
+ user = myname;
+ else
+ user = tmp;
+ }
+ n = command("USER %s", user);
+ if (n == CONTINUE) {
+ if (pass == NULL)
+ pass = getpass("Password:");
+ n = command("PASS %s", pass);
+ }
+ if (n == CONTINUE) {
+ aflag++;
+ acct = getpass("Account:");
+ n = command("ACCT %s", acct);
+ }
+ if (n != COMPLETE) {
+ warnx("Login failed.");
+ return (0);
+ }
+ if (!aflag && acct != NULL)
+ (void) command("ACCT %s", acct);
+ if (proxy)
+ return (1);
+ for (n = 0; n < macnum; ++n) {
+ if (!strcmp("init", macros[n].mac_name)) {
+ (void) strcpy(line, "$init");
+ makeargv();
+ domacro(margc, margv);
+ break;
+ }
+ }
+ return (1);
+}
+
+void
+cmdabort()
+{
+
+ printf("\n");
+ (void) fflush(stdout);
+ abrtflag++;
+ if (ptflag)
+ longjmp(ptabort,1);
+}
+
+/*VARARGS*/
+int
+command(va_alist)
+va_dcl
+{
+ va_list ap;
+ char *fmt;
+ int r;
+ sig_t oldintr;
+
+ abrtflag = 0;
+ if (debug) {
+ printf("---> ");
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ if (strncmp("PASS ", fmt, 5) == 0)
+ printf("PASS XXXX");
+ else
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf("\n");
+ (void) fflush(stdout);
+ }
+ if (cout == NULL) {
+ warn("No control connection for command");
+ code = -1;
+ return (0);
+ }
+ oldintr = signal(SIGINT, cmdabort);
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ vfprintf(cout, fmt, ap);
+ va_end(ap);
+ fprintf(cout, "\r\n");
+ (void) fflush(cout);
+ cpend = 1;
+ r = getreply(!strcmp(fmt, "QUIT"));
+ if (abrtflag && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ (void) signal(SIGINT, oldintr);
+ return (r);
+}
+
+char reply_string[BUFSIZ]; /* last line of previous reply */
+
+int
+getreply(expecteof)
+ int expecteof;
+{
+ int c, n;
+ int dig;
+ int originalcode = 0, continuation = 0;
+ sig_t oldintr;
+ int pflag = 0;
+ char *cp, *pt = pasv;
+
+ oldintr = signal(SIGINT, cmdabort);
+ for (;;) {
+ dig = n = code = 0;
+ cp = reply_string;
+ while ((c = getc(cin)) != '\n') {
+ if (c == IAC) { /* handle telnet commands */
+ switch (c = getc(cin)) {
+ case WILL:
+ case WONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, DONT, c);
+ (void) fflush(cout);
+ break;
+ case DO:
+ case DONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, WONT, c);
+ (void) fflush(cout);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ (void) signal(SIGINT,oldintr);
+ code = 221;
+ return (0);
+ }
+ lostpeer();
+ if (verbose) {
+ printf("421 Service not available, remote server has closed connection\n");
+ (void) fflush(stdout);
+ }
+ code = 421;
+ return (4);
+ }
+ if (c != '\r' && (verbose > 0 ||
+ (verbose > -1 && n == '5' && dig > 4))) {
+ if (proxflag &&
+ (dig == 1 || dig == 5 && verbose == 0))
+ printf("%s:",hostname);
+ (void) putchar(c);
+ }
+ if (dig < 4 && isdigit(c))
+ code = code * 10 + (c - '0');
+ if (!pflag && code == 227)
+ pflag = 1;
+ if (dig > 4 && pflag == 1 && isdigit(c))
+ pflag = 2;
+ if (pflag == 2) {
+ if (c != '\r' && c != ')')
+ *pt++ = c;
+ else {
+ *pt = '\0';
+ pflag = 3;
+ }
+ }
+ if (dig == 4 && c == '-') {
+ if (continuation)
+ code = 0;
+ continuation++;
+ }
+ if (n == 0)
+ n = c;
+ if (cp < &reply_string[sizeof(reply_string) - 1])
+ *cp++ = c;
+ }
+ if (verbose > 0 || verbose > -1 && n == '5') {
+ (void) putchar(c);
+ (void) fflush (stdout);
+ }
+ if (continuation && code != originalcode) {
+ if (originalcode == 0)
+ originalcode = code;
+ continue;
+ }
+ *cp = '\0';
+ if (n != '1')
+ cpend = 0;
+ (void) signal(SIGINT,oldintr);
+ if (code == 421 || originalcode == 421)
+ lostpeer();
+ if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ return (n - '0');
+ }
+}
+
+int
+empty(mask, sec)
+ struct fd_set *mask;
+ int sec;
+{
+ struct timeval t;
+
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0;
+ return (select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t));
+}
+
+jmp_buf sendabort;
+
+void
+abortsend()
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nsend aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(sendabort, 1);
+}
+
+#define HASHBYTES 1024
+
+void
+sendrequest(cmd, local, remote, printnames)
+ char *cmd, *local, *remote;
+ int printnames;
+{
+ struct stat st;
+ struct timeval start, stop;
+ int c, d;
+ FILE *fin, *dout = 0, *popen();
+ int (*closefunc) __P((FILE *));
+ sig_t oldintr, oldintp;
+ long bytes = 0, hashbytes = HASHBYTES;
+ char *lmode, buf[BUFSIZ], *bufp;
+
+ if (verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ if (curtype != type)
+ changetype(type, 0);
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ lmode = "w";
+ if (setjmp(sendabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT,oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE,oldintp);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortsend);
+ if (strcmp(local, "-") == 0)
+ fin = stdin;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE,SIG_IGN);
+ fin = popen(local + 1, "r");
+ if (fin == NULL) {
+ warn("%s", local + 1);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ return;
+ }
+ closefunc = pclose;
+ } else {
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ closefunc = fclose;
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode&S_IFMT) != S_IFREG) {
+ fprintf(stdout, "%s: not a plain file.\n", local);
+ (void) signal(SIGINT, oldintr);
+ fclose(fin);
+ code = -1;
+ return;
+ }
+ }
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (setjmp(sendabort))
+ goto abort;
+
+ if (restart_point &&
+ (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+ int rc;
+
+ switch (curtype) {
+ case TYPE_A:
+ rc = fseek(fin, (long) restart_point, SEEK_SET);
+ break;
+ case TYPE_I:
+ case TYPE_L:
+ rc = lseek(fileno(fin), restart_point, SEEK_SET);
+ break;
+ }
+ if (rc < 0) {
+ warn("local: %s", local);
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (command("REST %ld", (long) restart_point)
+ != CONTINUE) {
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ restart_point = 0;
+ lmode = "r+w";
+ }
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ } else
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ dout = dataconn(lmode);
+ if (dout == NULL)
+ goto abort;
+ (void) gettimeofday(&start, (struct timezone *)0);
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ errno = d = 0;
+ while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) {
+ bytes += c;
+ for (bufp = buf; c > 0; c -= d, bufp += d)
+ if ((d = write(fileno(dout), bufp, c)) <= 0)
+ break;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0)
+ warn("local: %s", local);
+ if (d < 0) {
+ if (errno != EPIPE)
+ warn("netout");
+ bytes = -1;
+ }
+ break;
+
+ case TYPE_A:
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ if (ferror(dout))
+ break;
+ (void) putc('\r', dout);
+ bytes++;
+ }
+ (void) putc(c, dout);
+ bytes++;
+ /* if (c == '\r') { */
+ /* (void) putc('\0', dout); // this violates rfc */
+ /* bytes++; */
+ /* } */
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(fin))
+ warn("local: %s", local);
+ if (ferror(dout)) {
+ if (errno != EPIPE)
+ warn("netout");
+ bytes = -1;
+ }
+ break;
+ }
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ (void) fclose(dout);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+ return;
+abort:
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (!cpend) {
+ code = -1;
+ return;
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (dout)
+ (void) fclose(dout);
+ (void) getreply(0);
+ code = -1;
+ if (closefunc != NULL && fin != NULL)
+ (*closefunc)(fin);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+}
+
+jmp_buf recvabort;
+
+void
+abortrecv()
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nreceive aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(recvabort, 1);
+}
+
+void
+recvrequest(cmd, local, remote, lmode, printnames)
+ char *cmd, *local, *remote, *lmode;
+ int printnames;
+{
+ FILE *fout, *din = 0;
+ int (*closefunc) __P((FILE *));
+ sig_t oldintr, oldintp;
+ int c, d, is_retr, tcrflag, bare_lfs = 0;
+ static int bufsize;
+ static char *buf;
+ long bytes = 0, hashbytes = HASHBYTES;
+ struct timeval start, stop;
+ struct stat st;
+
+ is_retr = strcmp(cmd, "RETR") == 0;
+ if (is_retr && verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy && is_retr) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ tcrflag = !crflag && is_retr;
+ if (setjmp(recvabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortrecv);
+ if (strcmp(local, "-") && *local != '|') {
+ if (access(local, 2) < 0) {
+ char *dir = strrchr(local, '/');
+
+ if (errno != ENOENT && errno != EACCES) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (dir != NULL)
+ *dir = 0;
+ d = access(dir == local ? "/" : dir ? local : ".", 2);
+ if (dir != NULL)
+ *dir = '/';
+ if (d < 0) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (!runique && errno == EACCES &&
+ chmod(local, 0600) < 0) {
+ warn("local: %s", local);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (runique && errno == EACCES &&
+ (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ else if (runique && (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ if (!is_retr) {
+ if (curtype != TYPE_A)
+ changetype(TYPE_A, 0);
+ } else if (curtype != type)
+ changetype(type, 0);
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (setjmp(recvabort))
+ goto abort;
+ if (is_retr && restart_point &&
+ command("REST %ld", (long) restart_point) != CONTINUE)
+ return;
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ } else {
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ din = dataconn("r");
+ if (din == NULL)
+ goto abort;
+ if (strcmp(local, "-") == 0)
+ fout = stdout;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fout = popen(local + 1, "w");
+ if (fout == NULL) {
+ warn("%s", local+1);
+ goto abort;
+ }
+ closefunc = pclose;
+ } else {
+ fout = fopen(local, lmode);
+ if (fout == NULL) {
+ warn("local: %s", local);
+ goto abort;
+ }
+ closefunc = fclose;
+ }
+ if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0)
+ st.st_blksize = BUFSIZ;
+ if (st.st_blksize > bufsize) {
+ if (buf)
+ (void) free(buf);
+ buf = malloc((unsigned)st.st_blksize);
+ if (buf == NULL) {
+ warn("malloc");
+ bufsize = 0;
+ goto abort;
+ }
+ bufsize = st.st_blksize;
+ }
+ (void) gettimeofday(&start, (struct timezone *)0);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ if (restart_point &&
+ lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
+ warn("local: %s", local);
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ errno = d = 0;
+ while ((c = read(fileno(din), buf, bufsize)) > 0) {
+ if ((d = write(fileno(fout), buf, c)) != c)
+ break;
+ bytes += c;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0) {
+ if (errno != EPIPE)
+ warn("netin");
+ bytes = -1;
+ }
+ if (d < c) {
+ if (d < 0)
+ warn("local: %s", local);
+ else
+ warnx("%s: short write", local);
+ }
+ break;
+
+ case TYPE_A:
+ if (restart_point) {
+ int i, n, ch;
+
+ if (fseek(fout, 0L, SEEK_SET) < 0)
+ goto done;
+ n = restart_point;
+ for (i = 0; i++ < n;) {
+ if ((ch = getc(fout)) == EOF)
+ goto done;
+ if (ch == '\n')
+ i++;
+ }
+ if (fseek(fout, 0L, SEEK_CUR) < 0) {
+done:
+ warn("local: %s", local);
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ }
+ while ((c = getc(din)) != EOF) {
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ bytes++;
+ if ((c = getc(din)) != '\n' || tcrflag) {
+ if (ferror(fout))
+ goto break2;
+ (void) putc('\r', fout);
+ if (c == '\0') {
+ bytes++;
+ goto contin2;
+ }
+ if (c == EOF)
+ goto contin2;
+ }
+ }
+ (void) putc(c, fout);
+ bytes++;
+ contin2: ;
+ }
+break2:
+ if (bare_lfs) {
+ printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs);
+ printf("File may not have transferred correctly.\n");
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(din)) {
+ if (errno != EPIPE)
+ warn("netin");
+ bytes = -1;
+ }
+ if (ferror(fout))
+ warn("local: %s", local);
+ break;
+ }
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) fclose(din);
+ (void) getreply(0);
+ if (bytes > 0 && is_retr)
+ ptransfer("received", bytes, &start, &stop);
+ return;
+abort:
+
+/* abort using RFC959 recommended IP,SYNC sequence */
+
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintr);
+ (void) signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+
+ abort_remote(din);
+ code = -1;
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (closefunc != NULL && fout != NULL)
+ (*closefunc)(fout);
+ if (din)
+ (void) fclose(din);
+ if (bytes > 0)
+ ptransfer("received", bytes, &start, &stop);
+ (void) signal(SIGINT, oldintr);
+}
+
+/*
+ * Need to start a listen on the data channel before we send the command,
+ * otherwise the server's connect may fail.
+ */
+int
+initconn()
+{
+ char *p, *a;
+ int result, len, tmpno = 0;
+ int on = 1;
+ int count;
+ u_long a1,a2,a3,a4,p1,p2;
+ static u_short last_port = FTP_DATA_BOTTOM;
+
+ if (passivemode) {
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("ftp: socket");
+ return(1);
+ }
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on,
+ sizeof (on)) < 0)
+ perror("ftp: setsockopt (ignored)");
+ if (command("PASV") != COMPLETE) {
+ printf("Passive mode refused.\n");
+ return(1);
+ }
+
+ /*
+ * What we've got at this point is a string of comma separated
+ * one-byte unsigned integer values, separated by commas.
+ * The first four are the an IP address. The fifth is the MSB
+ * of the port number, the sixth is the LSB. From that we'll
+ * prepare a sockaddr_in.
+ */
+
+ if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2)
+ != 6) {
+ printf("Passive mode address scan failure. Shouldn't happen!\n");
+ return(1);
+ };
+
+ data_addr.sin_family = AF_INET;
+ data_addr.sin_addr.s_addr = htonl((a1 << 24) | (a2 << 16) |
+ (a3 << 8) | a4);
+ data_addr.sin_port = htons((p1 << 8) | p2);
+
+ if (connect(data, (struct sockaddr *) &data_addr,
+ sizeof(data_addr))<0) {
+ perror("ftp: connect");
+ return(1);
+ }
+#ifdef IP_TOS
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on,
+ sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+ return(0);
+ }
+
+noport:
+ if (data != -1)
+ (void) close(data);
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ warn("socket");
+ if (tmpno)
+ sendport = 1;
+ return (1);
+ }
+ data_addr = myctladdr;
+ if (sendport) {
+ if (restricted_data_ports) {
+ for (count = 0;
+ count < FTP_DATA_TOP-FTP_DATA_BOTTOM; count++) {
+ last_port++;
+ if (last_port < FTP_DATA_BOTTOM ||
+ last_port > FTP_DATA_TOP)
+ last_port = FTP_DATA_BOTTOM;
+
+ data_addr.sin_port = htons(last_port);
+ if (bind(data, (struct sockaddr *)&data_addr,
+ sizeof(data_addr)) < 0) {
+ if (errno == EADDRINUSE)
+ continue;
+ else {
+ warn("bind");
+ goto bad;
+ }
+ }
+ break;
+ }
+ if (count >= FTP_DATA_TOP-FTP_DATA_BOTTOM) {
+ perror("ftp: all data ports in use");
+ goto bad;
+ }
+ } else {
+ data_addr.sin_port = 0; /* use any port */
+ if (bind(data, (struct sockaddr *)&data_addr,
+ sizeof(data_addr)) < 0) {
+ warn("bind");
+ goto bad;
+ }
+ }
+ } else {
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on)) < 0) {
+ warn("setsockopt (reuse address)");
+ goto bad;
+ }
+ if (bind(data, (struct sockaddr *)&data_addr,
+ sizeof (data_addr)) < 0) {
+ warn("bind");
+ goto bad;
+ }
+ }
+
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+ warn("setsockopt (ignored)");
+ len = sizeof (data_addr);
+ if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
+ warn("getsockname");
+ goto bad;
+ }
+ if (listen(data, 1) < 0)
+ warn("listen");
+ if (sendport) {
+ a = (char *)&data_addr.sin_addr;
+ p = (char *)&data_addr.sin_port;
+#define UC(b) (((int)b)&0xff)
+ result =
+ command("PORT %d,%d,%d,%d,%d,%d",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (result == ERROR && sendport == -1) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ return (result != COMPLETE);
+ }
+ if (tmpno)
+ sendport = 1;
+#ifdef IP_TOS
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ return (0);
+bad:
+ (void) close(data), data = -1;
+ if (tmpno)
+ sendport = 1;
+ return (1);
+}
+
+FILE *
+dataconn(lmode)
+ char *lmode;
+{
+ struct sockaddr_in from;
+ int s, fromlen = sizeof (from), tos;
+
+ if (passivemode)
+ return (fdopen(data, lmode));
+
+ s = accept(data, (struct sockaddr *) &from, &fromlen);
+ if (s < 0) {
+ warn("accept");
+ (void) close(data), data = -1;
+ return (NULL);
+ }
+ (void) close(data);
+ data = s;
+#ifdef IP_TOS
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ warn("setsockopt TOS (ignored)");
+#endif
+ return (fdopen(data, lmode));
+}
+
+void
+ptransfer(direction, bytes, t0, t1)
+ char *direction;
+ long bytes;
+ struct timeval *t0, *t1;
+{
+ struct timeval td;
+ float s, bs;
+
+ if (verbose) {
+ tvsub(&td, t1, t0);
+ s = td.tv_sec + (td.tv_usec / 1000000.);
+#define nz(x) ((x) == 0 ? 1 : (x))
+ bs = bytes / nz(s);
+ printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
+ bytes, direction, s, bs / 1024.);
+ }
+}
+
+/*
+void
+tvadd(tsum, t0)
+ struct timeval *tsum, *t0;
+{
+
+ tsum->tv_sec += t0->tv_sec;
+ tsum->tv_usec += t0->tv_usec;
+ if (tsum->tv_usec > 1000000)
+ tsum->tv_sec++, tsum->tv_usec -= 1000000;
+}
+*/
+
+void
+tvsub(tdiff, t1, t0)
+ struct timeval *tdiff, *t1, *t0;
+{
+
+ tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+ tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+ if (tdiff->tv_usec < 0)
+ tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}
+
+void
+psabort()
+{
+
+ abrtflag++;
+}
+
+void
+pswitch(flag)
+ int flag;
+{
+ sig_t oldintr;
+ static struct comvars {
+ int connect;
+ char name[MAXHOSTNAMELEN];
+ struct sockaddr_in mctl;
+ struct sockaddr_in hctl;
+ FILE *in;
+ FILE *out;
+ int tpe;
+ int curtpe;
+ int cpnd;
+ int sunqe;
+ int runqe;
+ int mcse;
+ int ntflg;
+ char nti[17];
+ char nto[17];
+ int mapflg;
+ char mi[MAXPATHLEN];
+ char mo[MAXPATHLEN];
+ } proxstruct, tmpstruct;
+ struct comvars *ip, *op;
+
+ abrtflag = 0;
+ oldintr = signal(SIGINT, psabort);
+ if (flag) {
+ if (proxy)
+ return;
+ ip = &tmpstruct;
+ op = &proxstruct;
+ proxy++;
+ } else {
+ if (!proxy)
+ return;
+ ip = &proxstruct;
+ op = &tmpstruct;
+ proxy = 0;
+ }
+ ip->connect = connected;
+ connected = op->connect;
+ if (hostname) {
+ (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1);
+ ip->name[strlen(ip->name)] = '\0';
+ } else
+ ip->name[0] = 0;
+ hostname = op->name;
+ ip->hctl = hisctladdr;
+ hisctladdr = op->hctl;
+ ip->mctl = myctladdr;
+ myctladdr = op->mctl;
+ ip->in = cin;
+ cin = op->in;
+ ip->out = cout;
+ cout = op->out;
+ ip->tpe = type;
+ type = op->tpe;
+ ip->curtpe = curtype;
+ curtype = op->curtpe;
+ ip->cpnd = cpend;
+ cpend = op->cpnd;
+ ip->sunqe = sunique;
+ sunique = op->sunqe;
+ ip->runqe = runique;
+ runique = op->runqe;
+ ip->mcse = mcase;
+ mcase = op->mcse;
+ ip->ntflg = ntflag;
+ ntflag = op->ntflg;
+ (void) strncpy(ip->nti, ntin, 16);
+ (ip->nti)[strlen(ip->nti)] = '\0';
+ (void) strcpy(ntin, op->nti);
+ (void) strncpy(ip->nto, ntout, 16);
+ (ip->nto)[strlen(ip->nto)] = '\0';
+ (void) strcpy(ntout, op->nto);
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1);
+ (ip->mi)[strlen(ip->mi)] = '\0';
+ (void) strcpy(mapin, op->mi);
+ (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1);
+ (ip->mo)[strlen(ip->mo)] = '\0';
+ (void) strcpy(mapout, op->mo);
+ (void) signal(SIGINT, oldintr);
+ if (abrtflag) {
+ abrtflag = 0;
+ (*oldintr)(SIGINT);
+ }
+}
+
+void
+abortpt()
+{
+
+ printf("\n");
+ (void) fflush(stdout);
+ ptabflg++;
+ mflag = 0;
+ abrtflag = 0;
+ longjmp(ptabort, 1);
+}
+
+void
+proxtrans(cmd, local, remote)
+ char *cmd, *local, *remote;
+{
+ sig_t oldintr;
+ int secndflag = 0, prox_type, nfnd;
+ char *cmd2;
+ struct fd_set mask;
+
+ if (strcmp(cmd, "RETR"))
+ cmd2 = "RETR";
+ else
+ cmd2 = runique ? "STOU" : "STOR";
+ if ((prox_type = type) == 0) {
+ if (unix_server && unix_proxy)
+ prox_type = TYPE_I;
+ else
+ prox_type = TYPE_A;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PASV") != COMPLETE) {
+ printf("proxy server does not support third party transfers.\n");
+ return;
+ }
+ pswitch(0);
+ if (!connected) {
+ printf("No primary connection\n");
+ pswitch(1);
+ code = -1;
+ return;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PORT %s", pasv) != COMPLETE) {
+ pswitch(1);
+ return;
+ }
+ if (setjmp(ptabort))
+ goto abort;
+ oldintr = signal(SIGINT, abortpt);
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ return;
+ }
+ sleep(2);
+ pswitch(1);
+ secndflag++;
+ if (command("%s %s", cmd2, local) != PRELIM)
+ goto abort;
+ ptflag++;
+ (void) getreply(0);
+ pswitch(0);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ ptflag = 0;
+ printf("local: %s remote: %s\n", local, remote);
+ return;
+abort:
+ (void) signal(SIGINT, SIG_IGN);
+ ptflag = 0;
+ if (strcmp(cmd, "RETR") && !proxy)
+ pswitch(1);
+ else if (!strcmp(cmd, "RETR") && proxy)
+ pswitch(0);
+ if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (cpend) {
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ warn("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ if (proxy)
+ pswitch(0);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+}
+
+void
+reset(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct fd_set mask;
+ int nfnd = 1;
+
+ FD_ZERO(&mask);
+ while (nfnd > 0) {
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask,0)) < 0) {
+ warn("reset");
+ code = -1;
+ lostpeer();
+ }
+ else if (nfnd) {
+ (void) getreply(0);
+ }
+ }
+}
+
+char *
+gunique(local)
+ char *local;
+{
+ static char new[MAXPATHLEN];
+ char *cp = strrchr(local, '/');
+ int d, count=0;
+ char ext = '1';
+
+ if (cp)
+ *cp = '\0';
+ d = access(cp == local ? "/" : cp ? local : ".", 2);
+ if (cp)
+ *cp = '/';
+ if (d < 0) {
+ warn("local: %s", local);
+ return ((char *) 0);
+ }
+ (void) strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ while (!d) {
+ if (++count == 100) {
+ printf("runique: can't find unique file name.\n");
+ return ((char *) 0);
+ }
+ *cp++ = ext;
+ *cp = '\0';
+ if (ext == '9')
+ ext = '0';
+ else
+ ext++;
+ if ((d = access(new, 0)) < 0)
+ break;
+ if (ext != '0')
+ cp--;
+ else if (*(cp - 2) == '.')
+ *(cp - 1) = '1';
+ else {
+ *(cp - 2) = *(cp - 2) + 1;
+ cp--;
+ }
+ }
+ return (new);
+}
+
+void
+abort_remote(din)
+ FILE *din;
+{
+ char buf[BUFSIZ];
+ int nfnd;
+ struct fd_set mask;
+
+ /*
+ * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
+ * after urgent byte rather than before as is protocol now
+ */
+ sprintf(buf, "%c%c%c", IAC, IP, IAC);
+ if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
+ warn("abort");
+ fprintf(cout,"%cABOR\r\n", DM);
+ (void) fflush(cout);
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if (din) {
+ FD_SET(fileno(din), &mask);
+ }
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ warn("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ if (din && FD_ISSET(fileno(din), &mask)) {
+ while (read(fileno(din), buf, BUFSIZ) > 0)
+ /* LOOP */;
+ }
+ if (getreply(0) == ERROR && code == 552) {
+ /* 552 needed for nic style abort */
+ (void) getreply(0);
+ }
+ (void) getreply(0);
+}
diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h
new file mode 100644
index 0000000..85895a8
--- /dev/null
+++ b/usr.bin/ftp/ftp_var.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftp_var.h 8.3 (Berkeley) 4/2/94
+ */
+
+/*
+ * FTP global variables.
+ */
+
+#include <sys/param.h>
+#include <setjmp.h>
+
+#include "extern.h"
+
+/*
+ * Options and other state info.
+ */
+int trace; /* trace packets exchanged */
+int hash; /* print # for each buffer transferred */
+int sendport; /* use PORT cmd for each data connection */
+int verbose; /* print messages coming back from server */
+int connected; /* connected to server */
+int fromatty; /* input is from a terminal */
+int interactive; /* interactively prompt on m* cmds */
+int debug; /* debugging level */
+int bell; /* ring bell on cmd completion */
+int doglob; /* glob local file names */
+int autologin; /* establish user account on connection */
+int proxy; /* proxy server connection active */
+int proxflag; /* proxy connection exists */
+int sunique; /* store files on server with unique name */
+int runique; /* store local files with unique name */
+int mcase; /* map upper to lower case for mget names */
+int ntflag; /* use ntin ntout tables for name translation */
+int mapflag; /* use mapin mapout templates on file names */
+int code; /* return/reply code for ftp command */
+int crflag; /* if 1, strip car. rets. on ascii gets */
+char pasv[64]; /* passive port for proxy data connection */
+int passivemode; /* passive mode enabled */
+int restricted_data_ports; /* restrict data port range */
+char *altarg; /* argv[1] with no shell-like preprocessing */
+char ntin[17]; /* input translation table */
+char ntout[17]; /* output translation table */
+char mapin[MAXPATHLEN]; /* input map template */
+char mapout[MAXPATHLEN]; /* output map template */
+char typename[32]; /* name of file transfer type */
+int type; /* requested file transfer type */
+int curtype; /* current file transfer type */
+char structname[32]; /* name of file transfer structure */
+int stru; /* file transfer structure */
+char formname[32]; /* name of file transfer format */
+int form; /* file transfer format */
+char modename[32]; /* name of file transfer mode */
+int mode; /* file transfer mode */
+char bytename[32]; /* local byte size in ascii */
+int bytesize; /* local byte size in binary */
+
+char *hostname; /* name of host connected to */
+int unix_server; /* server is unix, can use binary for ascii */
+int unix_proxy; /* proxy is unix, can use binary for ascii */
+
+struct servent *sp; /* service spec for tcp/ftp */
+
+jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+
+char line[200]; /* input line buffer */
+char *stringbase; /* current scan point in line buffer */
+char argbuf[200]; /* argument storage buffer */
+char *argbase; /* current storage point in arg buffer */
+int margc; /* count of arguments on input line */
+char *margv[20]; /* args parsed from input line */
+int cpend; /* flag: if != 0, then pending server reply */
+int mflag; /* flag: if != 0, then active multi command */
+
+int options; /* used during socket creation */
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+ char *c_name; /* name of command */
+ char *c_help; /* help string */
+ char c_bell; /* give bell when command completes */
+ char c_conn; /* must be connected to use command */
+ char c_proxy; /* proxy server may execute */
+ void (*c_handler) __P((int, char **)); /* function to call */
+};
+
+struct macel {
+ char mac_name[9]; /* macro name */
+ char *mac_start; /* start of macro in macbuf */
+ char *mac_end; /* end of macro in macbuf */
+};
+
+int macnum; /* number of defined macros */
+struct macel macros[16];
+char macbuf[4096];
diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c
new file mode 100644
index 0000000..cca7a919
--- /dev/null
+++ b/usr.bin/ftp/main.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 1985, 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 4/3/94";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+/*#include <sys/ioctl.h>*/
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/ftp.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "ftp_var.h"
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, top;
+ struct passwd *pw = NULL;
+ char *cp, homedir[MAXPATHLEN];
+ struct servent sp_default;
+
+ sp = getservbyname("ftp", "tcp");
+ if (sp == 0) {
+ sp = &sp_default;
+ memset(sp, 0, sizeof *sp);
+ sp->s_port = htons(21);
+ }
+ doglob = 1;
+ interactive = 1;
+ autologin = 1;
+ passivemode = 0;
+ restricted_data_ports = 1;
+
+ cp = strrchr(argv[0], '/');
+ cp = (cp == NULL) ? argv[0] : cp+1;
+ if (strcmp(cp, "pftp") == 0)
+ passivemode = 1;
+
+ while ((ch = getopt(argc, argv, "dginptvU")) != EOF) {
+ switch (ch) {
+ case 'd':
+ options |= SO_DEBUG;
+ debug++;
+ break;
+
+ case 'g':
+ doglob = 0;
+ break;
+
+ case 'i':
+ interactive = 0;
+ break;
+
+ case 'n':
+ autologin = 0;
+ break;
+
+ case 'p':
+ passivemode = 1;
+ break;
+
+ case 't':
+ trace++;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
+ default:
+ (void)fprintf(stderr,
+ "usage: ftp [-dginptv] [host [port]]\n");
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ fromatty = isatty(fileno(stdin));
+ if (fromatty)
+ verbose++;
+ cpend = 0; /* no pending replies */
+ proxy = 0; /* proxy not active */
+ crflag = 1; /* strip c.r. on ascii gets */
+ sendport = -1; /* not using ports */
+ /*
+ * Set up the home directory in case we're globbing.
+ */
+ cp = getlogin();
+ if (cp != NULL) {
+ pw = getpwnam(cp);
+ }
+ if (pw == NULL)
+ pw = getpwuid(getuid());
+ if (pw != NULL) {
+ home = homedir;
+ (void) strcpy(home, pw->pw_dir);
+ }
+ if (argc > 0) {
+ char *xargv[5];
+ extern char *__progname;
+
+ if (setjmp(toplevel))
+ exit(0);
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ xargv[0] = __progname;
+ xargv[1] = argv[0];
+ xargv[2] = argv[1];
+ xargv[3] = argv[2];
+ xargv[4] = NULL;
+ setpeer(argc+1, xargv);
+ }
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ }
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+void
+intr()
+{
+
+ longjmp(toplevel, 1);
+}
+
+void
+lostpeer()
+{
+
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ if (data >= 0) {
+ (void) shutdown(data, 1+1);
+ (void) close(data);
+ data = -1;
+ }
+ connected = 0;
+ }
+ pswitch(1);
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ connected = 0;
+ }
+ proxflag = 0;
+ pswitch(0);
+}
+
+/*
+char *
+tail(filename)
+ char *filename;
+{
+ char *s;
+
+ while (*filename) {
+ s = strrchr(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ if (s == filename)
+ break; XXX
+ *s = '\0';
+ }
+ return (filename);
+}
+*/
+
+/*
+ * Command parser.
+ */
+void
+cmdscanner(top)
+ int top;
+{
+ struct cmd *c;
+ int l;
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ if (fromatty) {
+ printf("ftp> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(line, sizeof line, stdin) == NULL)
+ quit(0, 0);
+ l = strlen(line);
+ if (l == 0)
+ break;
+ if (line[--l] == '\n') {
+ if (l == 0)
+ break;
+ line[l] = '\0';
+ } else if (l == sizeof(line) - 2) {
+ printf("sorry, input line too long\n");
+ while ((l = getchar()) != '\n' && l != EOF)
+ /* void */;
+ break;
+ } /* else it was a line without a newline */
+ makeargv();
+ if (margc == 0) {
+ continue;
+ }
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell)
+ (void) putchar('\007');
+ if (c->c_handler != help)
+ break;
+ }
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+}
+
+struct cmd *
+getcmd(name)
+ char *name;
+{
+ char *p, *q;
+ struct cmd *c, *found;
+ int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; p = c->c_name; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+
+int slrflag;
+
+void
+makeargv()
+{
+ char **argp;
+
+ margc = 0;
+ argp = margv;
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+ while (*argp++ = slurpstring())
+ margc++;
+}
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+char *
+slurpstring()
+{
+ int got_one = 0;
+ char *sb = stringbase;
+ char *ap = argbase;
+ char *tmp = argbase; /* will return this if token found */
+
+ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
+ switch (slrflag) { /* and $ as token for macro invoke */
+ case 0:
+ slrflag++;
+ stringbase++;
+ return ((*sb == '!') ? "!" : "$");
+ /* NOTREACHED */
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ sb++; goto S0;
+
+ default:
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = sb;
+ break;
+ default:
+ break;
+ }
+ goto S1;
+ }
+
+S1:
+ switch (*sb) {
+
+ case ' ':
+ case '\t':
+ case '\0':
+ goto OUT; /* end of token */
+
+ case '\\':
+ sb++; goto S2; /* slurp next character */
+
+ case '"':
+ sb++; goto S3; /* slurp quoted string */
+
+ default:
+ *ap++ = *sb++; /* add character to token */
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ sb++; goto S1;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S3;
+ }
+
+OUT:
+ if (got_one)
+ *ap++ = '\0';
+ argbase = ap; /* update storage pointer */
+ stringbase = sb; /* update scan pointer */
+ if (got_one) {
+ return (tmp);
+ }
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = (char *) 0;
+ break;
+ default:
+ break;
+ }
+ return ((char *)0);
+}
+
+#define HELPINDENT ((int) sizeof ("directory"))
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct cmd *c;
+
+ if (argc == 1) {
+ int i, j, w, k;
+ int columns, width = 0, lines;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name && (!proxy || c->c_proxy)) {
+ printf("%s", c->c_name);
+ }
+ else if (c->c_name) {
+ for (k=0; k < strlen(c->c_name); k++) {
+ (void) putchar(' ');
+ }
+ }
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void) putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
diff --git a/usr.bin/ftp/pathnames.h b/usr.bin/ftp/pathnames.h
new file mode 100644
index 0000000..be72b7e
--- /dev/null
+++ b/usr.bin/ftp/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/ftpXXXXXX"
diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c
new file mode 100644
index 0000000..026773c
--- /dev/null
+++ b/usr.bin/ftp/ruserpass.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1985, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ruserpass.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ftp_var.h"
+
+static int token __P((void));
+static FILE *cfile;
+
+#define DEFAULT 1
+#define LOGIN 2
+#define PASSWD 3
+#define ACCOUNT 4
+#define MACDEF 5
+#define ID 10
+#define MACH 11
+
+static char tokval[100];
+
+static struct toktab {
+ char *tokstr;
+ int tval;
+} toktab[]= {
+ { "default", DEFAULT },
+ { "login", LOGIN },
+ { "password", PASSWD },
+ { "passwd", PASSWD },
+ { "account", ACCOUNT },
+ { "machine", MACH },
+ { "macdef", MACDEF },
+ { NULL, 0 }
+};
+
+int
+ruserpass(host, aname, apass, aacct)
+ char *host, **aname, **apass, **aacct;
+{
+ char *hdir, buf[BUFSIZ], *tmp;
+ char myname[MAXHOSTNAMELEN], *mydomain;
+ int t, i, c, usedefault = 0;
+ struct stat stb;
+
+ hdir = getenv("HOME");
+ if (hdir == NULL)
+ hdir = ".";
+ (void) sprintf(buf, "%s/.netrc", hdir);
+ cfile = fopen(buf, "r");
+ if (cfile == NULL) {
+ if (errno != ENOENT)
+ warn("%s", buf);
+ return (0);
+ }
+ if (gethostname(myname, sizeof(myname)) < 0)
+ myname[0] = '\0';
+ if ((mydomain = strchr(myname, '.')) == NULL)
+ mydomain = "";
+next:
+ while ((t = token())) switch(t) {
+
+ case DEFAULT:
+ usedefault = 1;
+ /* FALL THROUGH */
+
+ case MACH:
+ if (!usedefault) {
+ if (token() != ID)
+ continue;
+ /*
+ * Allow match either for user's input host name
+ * or official hostname. Also allow match of
+ * incompletely-specified host in local domain.
+ */
+ if (strcasecmp(host, tokval) == 0)
+ goto match;
+ if (strcasecmp(hostname, tokval) == 0)
+ goto match;
+ if ((tmp = strchr(hostname, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(hostname, tokval, tmp-hostname) == 0 &&
+ tokval[tmp - hostname] == '\0')
+ goto match;
+ if ((tmp = strchr(host, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(host, tokval, tmp - host) == 0 &&
+ tokval[tmp - host] == '\0')
+ goto match;
+ continue;
+ }
+ match:
+ while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
+
+ case LOGIN:
+ if (token())
+ if (*aname == 0) {
+ *aname = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aname, tokval);
+ } else {
+ if (strcmp(*aname, tokval))
+ goto next;
+ }
+ break;
+ case PASSWD:
+ if ((*aname == NULL || strcmp(*aname, "anonymous")) &&
+ fstat(fileno(cfile), &stb) >= 0 &&
+ (stb.st_mode & 077) != 0) {
+ warnx("Error: .netrc file is readable by others.");
+ warnx("Remove password or make file unreadable by others.");
+ goto bad;
+ }
+ if (token() && *apass == 0) {
+ *apass = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*apass, tokval);
+ }
+ break;
+ case ACCOUNT:
+ if (fstat(fileno(cfile), &stb) >= 0
+ && (stb.st_mode & 077) != 0) {
+ warnx("Error: .netrc file is readable by others.");
+ warnx("Remove account or make file unreadable by others.");
+ goto bad;
+ }
+ if (token() && *aacct == 0) {
+ *aacct = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aacct, tokval);
+ }
+ break;
+ case MACDEF:
+ if (proxy) {
+ (void) fclose(cfile);
+ return (0);
+ }
+ while ((c=getc(cfile)) != EOF && c == ' ' || c == '\t');
+ if (c == EOF || c == '\n') {
+ printf("Missing macdef name argument.\n");
+ goto bad;
+ }
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ goto bad;
+ }
+ tmp = macros[macnum].mac_name;
+ *tmp++ = c;
+ for (i=0; i < 8 && (c=getc(cfile)) != EOF &&
+ !isspace(c); ++i) {
+ *tmp++ = c;
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = '\0';
+ if (c != '\n') {
+ while ((c=getc(cfile)) != EOF && c != '\n');
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum-1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf + 4096) {
+ if ((c=getc(cfile)) == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = c;
+ if (*tmp == '\n') {
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ break;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ if (tmp == macbuf + 4096) {
+ printf("4K macro buffer exceeded\n");
+ goto bad;
+ }
+ break;
+ default:
+ warnx("Unknown .netrc keyword %s", tokval);
+ break;
+ }
+ goto done;
+ }
+done:
+ (void) fclose(cfile);
+ return (0);
+bad:
+ (void) fclose(cfile);
+ return (-1);
+}
+
+static int
+token()
+{
+ char *cp;
+ int c;
+ struct toktab *t;
+
+ if (feof(cfile) || ferror(cfile))
+ return (0);
+ while ((c = getc(cfile)) != EOF &&
+ (c == '\n' || c == '\t' || c == ' ' || c == ','))
+ continue;
+ if (c == EOF)
+ return (0);
+ cp = tokval;
+ if (c == '"') {
+ while ((c = getc(cfile)) != EOF && c != '"') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ } else {
+ *cp++ = c;
+ while ((c = getc(cfile)) != EOF
+ && c != '\n' && c != '\t' && c != ' ' && c != ',') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ }
+ *cp = 0;
+ if (tokval[0] == 0)
+ return (0);
+ for (t = toktab; t->tokstr; t++)
+ if (!strcmp(t->tokstr, tokval))
+ return (t->tval);
+ return (ID);
+}
diff --git a/usr.bin/gcore/Makefile b/usr.bin/gcore/Makefile
new file mode 100644
index 0000000..664813f
--- /dev/null
+++ b/usr.bin/gcore/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= gcore
+SRCS= gcore.c
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.if ${MACHINE} != "sparc"
+SRCS+= md-nop.c
+.else
+SRCS+= md-${MACHINDE}.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/gcore/aoutcore.c b/usr.bin/gcore/aoutcore.c
new file mode 100644
index 0000000..49149e8
--- /dev/null
+++ b/usr.bin/gcore/aoutcore.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)gcore.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+/*
+ * Originally written by Eric Cooper in Fall 1981.
+ * Inspired by a version 6 program by Len Levin, 1978.
+ * Several pieces of code lifted from Bill Joy's 4BSD ps.
+ * Most recently, hacked beyond recognition for 4.4BSD by Steven McCanne,
+ * Lawrence Berkeley Laboratory.
+ *
+ * Portions of this software were developed by the Computer Systems
+ * Engineering group at Lawrence Berkeley Laboratory under DARPA
+ * contract BG 91-66 and contributed to Berkeley.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+#include <machine/vmparam.h>
+
+#include <a.out.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+void core __P((int, int, struct kinfo_proc *));
+void datadump __P((int, int, struct proc *, u_long, int));
+void usage __P((void));
+void userdump __P((int, struct proc *, u_long, int));
+
+kvm_t *kd;
+/* XXX undocumented routine, should be in kvm.h? */
+ssize_t kvm_uread __P((kvm_t *, struct proc *, u_long, char *, size_t));
+
+static int data_offset;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct proc *p;
+ struct kinfo_proc *ki;
+ struct exec exec;
+ int ch, cnt, efd, fd, pid, sflag, uid;
+ char *corefile, errbuf[_POSIX2_LINE_MAX], fname[MAXPATHLEN + 1];
+
+ sflag = 0;
+ corefile = NULL;
+ while ((ch = getopt(argc, argv, "c:s")) != EOF) {
+ switch (ch) {
+ case 'c':
+ corefile = optarg;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 2)
+ usage();
+
+ kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
+ if (kd == NULL)
+ err(1, "%s", errbuf);
+
+ uid = getuid();
+ pid = atoi(argv[1]);
+
+ ki = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt);
+ if (ki == NULL || cnt != 1)
+ err(1, "%d: not found", pid);
+
+ p = &ki->kp_proc;
+ if (ki->kp_eproc.e_pcred.p_ruid != uid && uid != 0)
+ err(1, "%d: not owner", pid);
+
+ if (p->p_stat == SZOMB)
+ err(1, "%d: zombie", pid);
+
+ if (p->p_flag & P_WEXIT)
+ err(0, "process exiting");
+ if (p->p_flag & P_SYSTEM) /* Swapper or pagedaemon. */
+ err(1, "%d: system process");
+
+ if (corefile == NULL) {
+ (void)snprintf(fname, sizeof(fname), "core.%d", pid);
+ corefile = fname;
+ }
+ fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE);
+ if (fd < 0)
+ err(1, "%s: %s\n", corefile, strerror(errno));
+
+ efd = open(argv[0], O_RDONLY, 0);
+ if (efd < 0)
+ err(1, "%s: %s\n", argv[0], strerror(errno));
+
+ cnt = read(efd, &exec, sizeof(exec));
+ if (cnt != sizeof(exec))
+ err(1, "%s exec header: %s",
+ argv[0], cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ data_offset = N_DATOFF(exec);
+
+ if (sflag && kill(pid, SIGSTOP) < 0)
+ err(0, "%d: stop signal: %s", pid, strerror(errno));
+
+ core(efd, fd, ki);
+
+ if (sflag && kill(pid, SIGCONT) < 0)
+ err(0, "%d: continue signal: %s", pid, strerror(errno));
+ (void)close(fd);
+
+ exit(0);
+}
+
+/*
+ * core --
+ * Build the core file.
+ */
+void
+core(efd, fd, ki)
+ int efd;
+ int fd;
+ struct kinfo_proc *ki;
+{
+ union {
+ struct user user;
+ char ubytes[ctob(UPAGES)];
+ } uarea;
+ struct proc *p = &ki->kp_proc;
+ int tsize = ki->kp_eproc.e_vm.vm_tsize;
+ int dsize = ki->kp_eproc.e_vm.vm_dsize;
+ int ssize = ki->kp_eproc.e_vm.vm_ssize;
+ int cnt;
+
+ /* Read in user struct */
+ cnt = kvm_read(kd, (u_long)p->p_addr, &uarea, sizeof(uarea));
+ if (cnt != sizeof(uarea))
+ err(1, "read user structure: %s",
+ cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ /*
+ * Fill in the eproc vm parameters, since these are garbage unless
+ * the kernel is dumping core or something.
+ */
+ uarea.user.u_kproc = *ki;
+
+ /* Dump user area */
+ cnt = write(fd, &uarea, sizeof(uarea));
+ if (cnt != sizeof(uarea))
+ err(1, "write user structure: %s",
+ cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ /* Dump data segment */
+ datadump(efd, fd, p, USRTEXT + ctob(tsize), dsize);
+
+ /* Dump stack segment */
+ userdump(fd, p, USRSTACK - ctob(ssize), ssize);
+
+ /* Dump machine dependent portions of the core. */
+ md_core(kd, fd, ki);
+}
+
+void
+datadump(efd, fd, p, addr, npage)
+ register int efd;
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc, delta;
+ char buffer[NBPG];
+
+ delta = data_offset - addr;
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, NBPG);
+ if (cc != NBPG) {
+ /* Try to read the page from the executable. */
+ if (lseek(efd, (off_t)addr + delta, SEEK_SET) == -1)
+ err(1, "seek executable: %s", strerror(errno));
+ cc = read(efd, buffer, sizeof(buffer));
+ if (cc != sizeof(buffer))
+ if (cc < 0)
+ err(1, "read executable: %s",
+ strerror(errno));
+ else /* Assume untouched bss page. */
+ bzero(buffer, sizeof(buffer));
+ }
+ cc = write(fd, buffer, NBPG);
+ if (cc != NBPG)
+ err(1, "write data segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += NBPG;
+ }
+}
+
+void
+userdump(fd, p, addr, npage)
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc;
+ char buffer[NBPG];
+
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, NBPG);
+ if (cc != NBPG)
+ /* Could be an untouched fill-with-zero page. */
+ bzero(buffer, NBPG);
+ cc = write(fd, buffer, NBPG);
+ if (cc != NBPG)
+ err(1, "write stack segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += NBPG;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: gcore [-s] [-c core] executable pid\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "gcore: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/gcore/extern.h b/usr.bin/gcore/extern.h
new file mode 100644
index 0000000..3dccafe
--- /dev/null
+++ b/usr.bin/gcore/extern.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+void err __P((int, const char *, ...));
+void md_core __P((kvm_t *, int, struct kinfo_proc *));
diff --git a/usr.bin/gcore/gcore.1 b/usr.bin/gcore/gcore.1
new file mode 100644
index 0000000..42fddbc
--- /dev/null
+++ b/usr.bin/gcore/gcore.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1983, 1990, 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)gcore.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd "April 18, 1994"
+.Dt GCORE 1
+.Os BSD 4.2
+.Sh NAME
+.Nm gcore
+.Nd get core images of running process
+.Sh SYNOPSIS
+.Nm gcore
+.Op Fl s
+.Op Fl c Ar core
+.Ar exec pid
+.Sh DESCRIPTION
+.Nm Gcore
+creates a core image of the specified process,
+suitable for use with
+.Xr gdb 1 .
+By default, the core is written to the file
+.Dq Pa core.<pid> .
+Both the executable image,
+.Ar exec ,
+and the process identifier,
+.Ar pid ,
+must be given on the command line.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl c
+Write the core file to the specified file instead of
+.Dq Pa core.<pid> .
+.It Fl s
+Stop the process while gathering the core image, and resume it
+when done. This guarantees that the resulting core dump will
+be in a consistent state. The process is resumed even if it was
+already stopped.
+The same effect can be achieved manually with
+.Xr kill 1 .
+.El
+.Sh FILES
+.Bl -tag -width /var/log/messages -compact
+.It Pa core.<pid>
+The core image.
+.EL
+.Dp
+.Sh HISTORY
+.Nm Gcore
+appeared in 4.2BSD.
+.Sh BUGS
+Context switches or paging activity that occur while
+.Nm gcore
+is running may cause the program to become confused.
+For best results, use -s to temporarily stop the target process.
+.Pp
+.Nm Gcore
+is not compatible with the original 4.2BSD version.
+In particular, 4.4BSD requires the
+.Ar exec
+argument.
diff --git a/usr.bin/gcore/gcore.c b/usr.bin/gcore/gcore.c
new file mode 100644
index 0000000..49149e8
--- /dev/null
+++ b/usr.bin/gcore/gcore.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)gcore.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+/*
+ * Originally written by Eric Cooper in Fall 1981.
+ * Inspired by a version 6 program by Len Levin, 1978.
+ * Several pieces of code lifted from Bill Joy's 4BSD ps.
+ * Most recently, hacked beyond recognition for 4.4BSD by Steven McCanne,
+ * Lawrence Berkeley Laboratory.
+ *
+ * Portions of this software were developed by the Computer Systems
+ * Engineering group at Lawrence Berkeley Laboratory under DARPA
+ * contract BG 91-66 and contributed to Berkeley.
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+#include <machine/vmparam.h>
+
+#include <a.out.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+void core __P((int, int, struct kinfo_proc *));
+void datadump __P((int, int, struct proc *, u_long, int));
+void usage __P((void));
+void userdump __P((int, struct proc *, u_long, int));
+
+kvm_t *kd;
+/* XXX undocumented routine, should be in kvm.h? */
+ssize_t kvm_uread __P((kvm_t *, struct proc *, u_long, char *, size_t));
+
+static int data_offset;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct proc *p;
+ struct kinfo_proc *ki;
+ struct exec exec;
+ int ch, cnt, efd, fd, pid, sflag, uid;
+ char *corefile, errbuf[_POSIX2_LINE_MAX], fname[MAXPATHLEN + 1];
+
+ sflag = 0;
+ corefile = NULL;
+ while ((ch = getopt(argc, argv, "c:s")) != EOF) {
+ switch (ch) {
+ case 'c':
+ corefile = optarg;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 2)
+ usage();
+
+ kd = kvm_openfiles(0, 0, 0, O_RDONLY, errbuf);
+ if (kd == NULL)
+ err(1, "%s", errbuf);
+
+ uid = getuid();
+ pid = atoi(argv[1]);
+
+ ki = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt);
+ if (ki == NULL || cnt != 1)
+ err(1, "%d: not found", pid);
+
+ p = &ki->kp_proc;
+ if (ki->kp_eproc.e_pcred.p_ruid != uid && uid != 0)
+ err(1, "%d: not owner", pid);
+
+ if (p->p_stat == SZOMB)
+ err(1, "%d: zombie", pid);
+
+ if (p->p_flag & P_WEXIT)
+ err(0, "process exiting");
+ if (p->p_flag & P_SYSTEM) /* Swapper or pagedaemon. */
+ err(1, "%d: system process");
+
+ if (corefile == NULL) {
+ (void)snprintf(fname, sizeof(fname), "core.%d", pid);
+ corefile = fname;
+ }
+ fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE);
+ if (fd < 0)
+ err(1, "%s: %s\n", corefile, strerror(errno));
+
+ efd = open(argv[0], O_RDONLY, 0);
+ if (efd < 0)
+ err(1, "%s: %s\n", argv[0], strerror(errno));
+
+ cnt = read(efd, &exec, sizeof(exec));
+ if (cnt != sizeof(exec))
+ err(1, "%s exec header: %s",
+ argv[0], cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ data_offset = N_DATOFF(exec);
+
+ if (sflag && kill(pid, SIGSTOP) < 0)
+ err(0, "%d: stop signal: %s", pid, strerror(errno));
+
+ core(efd, fd, ki);
+
+ if (sflag && kill(pid, SIGCONT) < 0)
+ err(0, "%d: continue signal: %s", pid, strerror(errno));
+ (void)close(fd);
+
+ exit(0);
+}
+
+/*
+ * core --
+ * Build the core file.
+ */
+void
+core(efd, fd, ki)
+ int efd;
+ int fd;
+ struct kinfo_proc *ki;
+{
+ union {
+ struct user user;
+ char ubytes[ctob(UPAGES)];
+ } uarea;
+ struct proc *p = &ki->kp_proc;
+ int tsize = ki->kp_eproc.e_vm.vm_tsize;
+ int dsize = ki->kp_eproc.e_vm.vm_dsize;
+ int ssize = ki->kp_eproc.e_vm.vm_ssize;
+ int cnt;
+
+ /* Read in user struct */
+ cnt = kvm_read(kd, (u_long)p->p_addr, &uarea, sizeof(uarea));
+ if (cnt != sizeof(uarea))
+ err(1, "read user structure: %s",
+ cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ /*
+ * Fill in the eproc vm parameters, since these are garbage unless
+ * the kernel is dumping core or something.
+ */
+ uarea.user.u_kproc = *ki;
+
+ /* Dump user area */
+ cnt = write(fd, &uarea, sizeof(uarea));
+ if (cnt != sizeof(uarea))
+ err(1, "write user structure: %s",
+ cnt > 0 ? strerror(EIO) : strerror(errno));
+
+ /* Dump data segment */
+ datadump(efd, fd, p, USRTEXT + ctob(tsize), dsize);
+
+ /* Dump stack segment */
+ userdump(fd, p, USRSTACK - ctob(ssize), ssize);
+
+ /* Dump machine dependent portions of the core. */
+ md_core(kd, fd, ki);
+}
+
+void
+datadump(efd, fd, p, addr, npage)
+ register int efd;
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc, delta;
+ char buffer[NBPG];
+
+ delta = data_offset - addr;
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, NBPG);
+ if (cc != NBPG) {
+ /* Try to read the page from the executable. */
+ if (lseek(efd, (off_t)addr + delta, SEEK_SET) == -1)
+ err(1, "seek executable: %s", strerror(errno));
+ cc = read(efd, buffer, sizeof(buffer));
+ if (cc != sizeof(buffer))
+ if (cc < 0)
+ err(1, "read executable: %s",
+ strerror(errno));
+ else /* Assume untouched bss page. */
+ bzero(buffer, sizeof(buffer));
+ }
+ cc = write(fd, buffer, NBPG);
+ if (cc != NBPG)
+ err(1, "write data segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += NBPG;
+ }
+}
+
+void
+userdump(fd, p, addr, npage)
+ register int fd;
+ struct proc *p;
+ register u_long addr;
+ register int npage;
+{
+ register int cc;
+ char buffer[NBPG];
+
+ while (--npage >= 0) {
+ cc = kvm_uread(kd, p, addr, buffer, NBPG);
+ if (cc != NBPG)
+ /* Could be an untouched fill-with-zero page. */
+ bzero(buffer, NBPG);
+ cc = write(fd, buffer, NBPG);
+ if (cc != NBPG)
+ err(1, "write stack segment: %s",
+ cc > 0 ? strerror(EIO) : strerror(errno));
+ addr += NBPG;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: gcore [-s] [-c core] executable pid\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "gcore: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/gcore/md-nop.c b/usr.bin/gcore/md-nop.c
new file mode 100644
index 0000000..46e90c4
--- /dev/null
+++ b/usr.bin/gcore/md-nop.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)md-nop.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <stdio.h>
+#include <kvm.h>
+#include "extern.h"
+
+void
+md_core(kd, fd, ki)
+ kvm_t *kd;
+ int fd;
+ struct kinfo_proc *ki;
+{
+ /* Don't need to fix anything for this architecture. */
+ return;
+}
diff --git a/usr.bin/gcore/md-sparc.c b/usr.bin/gcore/md-sparc.c
new file mode 100644
index 0000000..5df8a57
--- /dev/null
+++ b/usr.bin/gcore/md-sparc.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)md-sparc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <machine/vmparam.h>
+
+#include <kvm.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+
+#ifndef offsetof
+#define offsetof(s, f) ((int)&((s *)0)->f)
+#endif
+
+static void
+shift_page(fd, off, ssize)
+ register int fd;
+ register off_t off;
+ register int ssize;
+{
+ char buffer[NBPG];
+
+ (void)lseek(fd, (off_t)-NBPG, SEEK_END);
+ for (; ssize > 0; ssize -= NBPG) {
+ (void)read(fd, buffer, NBPG);
+ (void)write(fd, buffer, NBPG);
+ (void)lseek(fd, (off_t)-2 * NBPG, SEEK_CUR);
+ }
+}
+
+/*
+ * Fix up the core image for the sparc. We need to flush any register
+ * windows that are cached in the pcb out to the user stack.
+ * Also, we need to get the trap frame and possible floating point state
+ * from the top of the kernel stack and store it in the pcb.
+ */
+void
+md_core(kd, fd, ki)
+ kvm_t *kd;
+ int fd;
+ struct kinfo_proc *ki;
+{
+ register struct rwindow *rw;
+ register int nsaved, cc, ssize;
+ register off_t off, s;
+ register u_long sp;
+ struct pcb pcb;
+ struct trapframe tf;
+
+ /*
+ * Before anything else read the trapframe. Synchronizing here
+ * is impossible if the process is running.
+ */
+ cc = kvm_read(kd, (u_long)ki->kp_proc.p_md.md_tf,
+ /* XXX */
+ (void *)&tf, sizeof(tf));
+ if (cc < 0)
+ err(1, "kvm_read: %s (reading kernel trapframe)",
+ kvm_geterr(kd));
+ if (cc != sizeof(tf))
+ err(1, "cannot read kernel trapframe");
+
+ /*
+ * Write out the real trap frame.
+ */
+ off = offsetof(struct user, u_md);
+ off += offsetof(struct md_coredump, md_tf);
+ if (lseek(fd, off, SEEK_SET) == -1)
+ err(1, "lseek: %s", strerror(errno));
+ (void)write(fd, &tf, sizeof(tf));
+
+ if (ki->kp_proc.p_md.md_fpstate != 0) {
+ /*
+ * If floating point state is present, write it out too.
+ * It comes right after the trapframe so we don't need to seek.
+ */
+ struct fpstate fs;
+ cc = kvm_read(kd, (u_long)ki->kp_proc.p_md.md_fpstate,
+ (void *)&fs, sizeof(fs));
+ if (cc < 0)
+ err(1, "kvm_read: %s (fpu state)", kvm_geterr(kd));
+ if (cc != sizeof(fs))
+ err(1, "cannot read fpu state");
+ (void)write(fd, (char *)&fs, sizeof(fs));
+ }
+ /*
+ * Read pcb.
+ */
+ if (lseek(fd, (off_t)offsetof(struct user, u_pcb), SEEK_SET) == -1)
+ err(1, "lseek: %s", strerror(errno));
+ cc = read(fd, (char *)&pcb, sizeof(pcb));
+ if (cc != sizeof(pcb)) {
+ if (cc < 0)
+ err(1, "read: %s", strerror(errno));
+ err(1, "couldn't read pcb from core file");
+ }
+
+ /*
+ * Write any unsaved windows to the appropriate stack locations.
+ */
+ nsaved = pcb.pcb_nsaved;
+ if (nsaved == 0)
+ return;
+
+ rw = &pcb.pcb_rw[0];
+ off = ctob(UPAGES + ki->kp_eproc.e_vm.vm_dsize);
+ ssize = ctob(ki->kp_eproc.e_vm.vm_ssize);
+ sp = tf.tf_out[6];
+ for (; --nsaved >= 0; ++rw) {
+ /*
+ * Copy register window into appropriate stack location.
+ */
+ s = ssize - (USRSTACK - sp);
+ if (s < 0) {
+ if (s < -NBPG)
+ err(1, "cannot copy pcb windows to stack");
+ /*
+ * It's possible to be missing the bottomost
+ * page because a stack page hasn't been allocated
+ * for the register save area. Shift over
+ * the stack segment by a page, and update
+ * the u-area to reflect the new stack size. YECH!
+ */
+ shift_page(fd, off, ssize);
+ ssize += NBPG;
+ s += NBPG;
+ ++ki->kp_eproc.e_vm.vm_ssize;
+ (void)lseek(fd,
+ (off_t)offsetof(struct user, u_kproc.kp_eproc.e_vm),
+ SEEK_SET);
+ (void)write(fd,
+ &ki->kp_eproc.e_vm, sizeof(ki->kp_eproc.e_vm));
+ }
+ if (lseek(fd, off + s, SEEK_SET) == -1)
+ err(1, "cannot copy pcb windows to stack");
+
+ (void)write(fd, rw, sizeof(*rw));
+ sp = rw->rw_in[6];
+ }
+}
diff --git a/usr.bin/gencat/Makefile b/usr.bin/gencat/Makefile
new file mode 100644
index 0000000..b738deb
--- /dev/null
+++ b/usr.bin/gencat/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= gencat
+SRCS= gencat.c genlib.c
+NOMAN= yes
+
+CFLAGS+= -I${.CURDIR}/../../lib/libc/nls
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/gencat/gencat.c b/usr.bin/gencat/gencat.c
new file mode 100644
index 0000000..37c7990
--- /dev/null
+++ b/usr.bin/gencat/gencat.c
@@ -0,0 +1,255 @@
+
+/***********************************************************
+Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that Alfalfa's name not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+If you make any modifications, bugfixes or other changes to this software
+we'd appreciate it if you could send a copy to us so we can keep things
+up-to-date. Many thanks.
+ Kee Hinckley
+ Alfalfa Software, Inc.
+ 267 Allston St., #3
+ Cambridge, MA 02139 USA
+ nazgul@alfalfa.com
+
+******************************************************************/
+
+/* Edit History
+
+01/18/91 3 hamilton #if not reparsed
+01/12/91 2 schulert conditionally use prototypes
+12/23/90 2 hamilton Fix fd == NULL to fd < 0
+11/03/90 1 hamilton Alphalpha->Alfalfa & OmegaMail->Poste
+08/13/90 1 schulert move from ua to omu
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef SYSV
+#include <sys/fcntl.h>
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#endif
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "gencat.h"
+
+/*
+ * The spec says the syntax is "gencat catfile msgfile...".
+ * We extend it to:
+ * gencat [-lang C|C++|ANSIC] catfile msgfile [-h <header-file>]...
+ * Flags are order dependant, we'll take whatever lang was most recently chosen
+ * and use it to generate the next header file. The header files are generated
+ * at the point in the command line they are listed. Thus the sequence:
+ * gencat -lang C foo.cat foo.mcs -h foo.h -lang C++ bar.mcs -h bar.H
+ * will put constants from foo.mcs into foo.h and constants from bar.mcs into
+ * bar.h. Constants are not saved in the catalog file, so nothing will come
+ * from that, even if things have been defined before. The constants in foo.h
+ * will be in C syntax, in bar.H in C++ syntax.
+ */
+
+#if ANSI_C || defined(__cplusplus)
+# define P_(x) x
+#else
+# define P_(x) /**/
+#endif
+
+static void writeIfChanged P_((char *fname, int lang, int orConsts));
+
+#undef P_
+
+void usage() {
+ fprintf(stderr, "Use: gencat [-new] [-or] [-lang C|C++|ANSIC]\n");
+ fprintf(stderr, " catfile msgfile [-h <header-file>]...\n");
+}
+
+void main(
+#if ANSI_C || defined(__cplusplus)
+ int argc, char *argv[])
+#else
+ argc, argv)
+int argc;
+char *argv[];
+#endif
+{
+ int ofd, ifd, i;
+ FILE *fptr;
+ char *catfile = NULL;
+ char *input = NULL;
+ int lang = MCLangC;
+ int new = False;
+ int orConsts = False;
+
+ for (i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-lang") == 0) {
+ ++i;
+ if (strcmp(argv[i], "C") == 0) lang = MCLangC;
+ else if (strcmp(argv[i], "C++") == 0) lang = MCLangCPlusPlus;
+ else if (strcmp(argv[i], "ANSIC") == 0) lang = MCLangANSIC;
+ else {
+ fprintf(stderr, "gencat: Unrecognized language: %s\n", argv[i]);
+ exit(1);
+ }
+ } else if (strcmp(argv[i], "-h") == 0) {
+ if (!input) {
+ fprintf(stderr, "gencat: Can't write to a header before reading something.\n");
+ exit(1);
+ }
+ ++i;
+ writeIfChanged(argv[i], lang, orConsts);
+ } else if (strcmp(argv[i], "-new") == 0) {
+ if (catfile) {
+ fprintf(stderr, "gencat: You must specify -new before the catalog file name\n");
+ exit(1);
+ }
+ new = True;
+ } else if (strcmp(argv[i], "-or") == 0) {
+ orConsts = ~orConsts;
+ } else {
+ usage();
+ exit(1);
+ }
+ } else {
+ if (!catfile) {
+ catfile = argv[i];
+ if (new) {
+ if ((ofd = open(catfile, O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
+ fprintf(stderr, "gencat: Unable to create a new %s.\n", catfile);
+ exit(1);
+ }
+ } else if ((ofd = open(catfile, O_RDONLY)) < 0) {
+ if ((ofd = open(catfile, O_WRONLY|O_CREAT, 0666)) < 0) {
+ fprintf(stderr, "gencat: Unable to create %s.\n", catfile);
+ exit(1);
+ }
+ } else {
+ MCReadCat(ofd);
+ close(ofd);
+ if ((ofd = open(catfile, O_WRONLY|O_TRUNC)) < 0) {
+ fprintf(stderr, "gencat: Unable to truncate %s.\n", catfile);
+ exit(1);
+ }
+ }
+ } else {
+ input = argv[i];
+ if ((ifd = open(input, O_RDONLY)) < 0) {
+ fprintf(stderr, "gencat: Unable to read %s\n", input);
+ exit(1);
+ }
+ MCParse(ifd);
+ close(ifd);
+ }
+ }
+ }
+ if (catfile) {
+ MCWriteCat(ofd);
+ exit(0);
+ } else {
+ usage();
+ exit(1);
+ }
+}
+
+static void writeIfChanged(
+#if ANSI_C || defined(__cplusplus)
+ char *fname, int lang, int orConsts)
+#else
+ fname, lang, orConsts)
+char *fname;
+int lang;
+int orConsts;
+#endif
+{
+ char tmpname[32];
+ char buf[BUFSIZ], tbuf[BUFSIZ], *cptr, *tptr;
+ int fd, tfd;
+ int diff = False;
+ int c, len, tlen;
+ struct stat sbuf;
+
+ /* If it doesn't exist, just create it */
+ if (stat(fname, &sbuf)) {
+ if ((fd = open(fname, O_WRONLY|O_CREAT, 0666)) < 0) {
+ fprintf(stderr, "gencat: Unable to create header file %s.\n", fname);
+ exit(1);
+ }
+ MCWriteConst(fd, lang, orConsts);
+ close(fd);
+ return;
+ }
+
+ /* If it does exist, create a temp file for now */
+ sprintf(tmpname, "/tmp/gencat.%d", (int) getpid());
+ if ((tfd = open(tmpname, O_RDWR|O_CREAT, 0666)) < 0) {
+ fprintf(stderr, "gencat: Unable to open temporary file: %s\n", tmpname);
+ exit(1);
+ }
+ unlink(tmpname);
+
+ /* Write to the temp file and rewind */
+ MCWriteConst(tfd, lang, orConsts);
+
+ /* Open the real header file */
+ if ((fd = open(fname, O_RDONLY)) < 0) {
+ fprintf(stderr, "gencat: Unable to read header file: %s\n", fname);
+ exit(1);
+ }
+
+ /* Backup to the start of the temp file */
+ if (lseek(tfd, 0L, L_SET) < 0) {
+ fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname);
+ exit(1);
+ }
+
+ /* Now compare them */
+ while ((tlen = read(tfd, tbuf, BUFSIZ)) > 0) {
+ if ((len = read(fd, buf, BUFSIZ)) != tlen) {
+ diff = True;
+ goto done;
+ }
+ for (cptr = buf, tptr = tbuf; cptr < buf+len; ++cptr, ++tptr) {
+ if (*tptr != *cptr) {
+ diff = True;
+ goto done;
+ }
+ }
+ }
+done:
+ if (diff) {
+ if (lseek(tfd, 0L, L_SET) < 0) {
+ fprintf(stderr, "gencat: Unable to seek in tempfile: %s\n", tmpname);
+ exit(1);
+ }
+ close(fd);
+ if ((fd = open(fname, O_WRONLY|O_TRUNC)) < 0) {
+ fprintf(stderr, "gencat: Unable to truncate header file: %s\n", fname);
+ exit(1);
+ }
+ while ((len = read(tfd, buf, BUFSIZ)) > 0) {
+ if (write(fd, buf, len) != len) {
+ fprintf(stderr, "gencat: Error writing to header file: %s\n", fname);
+ }
+ }
+ }
+ close(fd);
+ close(tfd);
+}
diff --git a/usr.bin/gencat/gencat.h b/usr.bin/gencat/gencat.h
new file mode 100644
index 0000000..c12a194
--- /dev/null
+++ b/usr.bin/gencat/gencat.h
@@ -0,0 +1,107 @@
+
+/***********************************************************
+Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that Alfalfa's name not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+If you make any modifications, bugfixes or other changes to this software
+we'd appreciate it if you could send a copy to us so we can keep things
+up-to-date. Many thanks.
+ Kee Hinckley
+ Alfalfa Software, Inc.
+ 267 Allston St., #3
+ Cambridge, MA 02139 USA
+ nazgul@alfalfa.com
+
+******************************************************************/
+
+/* Edit History
+
+02/25/91 2 nazgul Added MCGetByteOrder
+01/18/91 2 hamilton #if not reparsed
+01/12/91 2 schulert conditionally use prototypes
+11/03/90 1 hamilton Alphalpha->Alfalfa & OmegaMail->Poste
+08/13/90 1 schulert move from ua to omu
+*/
+
+#ifndef gencat_h
+#define gencat_h
+
+/*
+ * $set n comment
+ * My extension: If the comment begins with # treat the next string
+ * as a constant identifier.
+ * $delset n comment
+ * n goes from 1 to NL_SETMAX
+ * Deletes a set from the MC
+ * $ comment
+ * My extension: If comment begins with # treat the next string as
+ * a constant identifier for the next message.
+ * m message-text
+ * m goes from 1 to NL_MSGMAX
+ * If message-text is empty, and a space or tab is present, put
+ * empty string in catalog.
+ * If message-text is empty, delete the message.
+ * Length of text is 0 to NL_TEXTMAX
+ * My extension: If '#' is used instead of a number, the number
+ * is generated automatically. A # followed by anything is an empty message.
+ * $quote c
+ * Optional quote character which can suround message-text to
+ * show where spaces are.
+ *
+ * Escape Characters
+ * \n (newline), \t (horiz tab), \v (vert tab), \b (backspace),
+ * \r (carriage return), \f (formfeed), \\ (backslash), \ddd (bitpattern
+ * in octal).
+ * Also, \ at end of line is a continuation.
+ *
+ */
+
+#define MCLangC 0
+#define MCLangCPlusPlus 1
+#define MCLangANSIC 2
+
+#define MAXTOKEN 1024
+
+#if !defined(ANSI_C) && (defined(__STDC__) || defined(_AIX))
+# define ANSI_C 1
+#endif
+
+#if ANSI_C || defined(__cplusplus)
+# define P_(x) x
+#else
+# define P_(x) /**/
+#endif
+
+extern void MCAddSet P_((int setId, char *c));
+extern void MCDelSet P_((int setId));
+extern void MCAddMsg P_((int msgId, char *msg, char *c));
+extern void MCDelMsg P_((int msgId));
+extern void MCParse P_((int fd));
+extern void MCReadCat P_((int fd));
+extern void MCWriteConst P_((int fd, int type, int orConsts));
+extern void MCWriteCat P_((int fd));
+extern long MCGetByteOrder P_((void));
+
+#ifndef True
+# define True ~0
+# define False 0
+#endif
+
+#endif
diff --git a/usr.bin/gencat/genlib.c b/usr.bin/gencat/genlib.c
new file mode 100644
index 0000000..0827ede
--- /dev/null
+++ b/usr.bin/gencat/genlib.c
@@ -0,0 +1,892 @@
+/* -*-c++-*- */
+
+
+/***********************************************************
+Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that Alfalfa's name not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+If you make any modifications, bugfixes or other changes to this software
+we'd appreciate it if you could send a copy to us so we can keep things
+up-to-date. Many thanks.
+ Kee Hinckley
+ Alfalfa Software, Inc.
+ 267 Allston St., #3
+ Cambridge, MA 02139 USA
+ nazgul@alfalfa.com
+
+******************************************************************/
+
+/* Edit History
+
+02/25/91 5 nazgul Added flag for MS byteorder
+01/14/91 4 nazgul Off by one on number specified entries
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef SYSV
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#include <memory.h>
+static int bcopy(src, dst, length)
+char *src, *dst;
+int length;
+{
+ memcpy(dst, src, length);
+}
+static int bzero(b, length)
+char *b;
+int length;
+{
+ memset(b, '\0', length);
+}
+#endif
+#include <sys/file.h>
+#include <ctype.h>
+#include "msgcat.h"
+#include "gencat.h"
+
+static char *curline = NULL;
+static long lineno = 0;
+
+static void warning(cptr, msg)
+char *cptr;
+char *msg;
+{
+ fprintf(stderr, "gencat: %s on line %d\n", msg, lineno);
+ fprintf(stderr, "%s\n", curline);
+ if (cptr) {
+ char *tptr;
+ for (tptr = curline; tptr < cptr; ++tptr) putc(' ', stderr);
+ fprintf(stderr, "^\n");
+ }
+}
+
+static void error(cptr, msg)
+char *cptr;
+char *msg;
+{
+ warning(cptr, msg);
+ exit(1);
+}
+
+static void corrupt() {
+ error(NULL, "corrupt message catalog");
+}
+static void nomem() {
+ error(NULL, "out of memory");
+}
+
+static char *getline(fd)
+int fd;
+{
+ static long len = 0, curlen = BUFSIZ;
+ static char buf[BUFSIZ], *bptr = buf, *bend = buf;
+ char *cptr, *cend;
+ long buflen;
+
+ if (!curline) {
+ curline = (char *) malloc(curlen);
+ if (!curline) nomem();
+ }
+ ++lineno;
+
+ cptr = curline;
+ cend = curline + curlen;
+ while (True) {
+ for (; bptr < bend && cptr < cend; ++cptr, ++bptr) {
+ if (*bptr == '\n') {
+ *cptr = '\0';
+ ++bptr;
+ return(curline);
+ } else *cptr = *bptr;
+ }
+ if (bptr == bend) {
+ buflen = read(fd, buf, BUFSIZ);
+ if (buflen <= 0) {
+ if (cptr > curline) {
+ *cptr = '\0';
+ return(curline);
+ }
+ return(NULL);
+ }
+ bend = buf + buflen;
+ bptr = buf;
+ }
+ if (cptr == cend) {
+ cptr = curline = (char *) realloc(curline, curlen *= 2);
+ cend = curline + curlen;
+ }
+ }
+}
+
+
+static char *token(cptr)
+char *cptr;
+{
+ static char tok[MAXTOKEN+1];
+ char *tptr = tok;
+
+ while (*cptr && isspace(*cptr)) ++cptr;
+ while (*cptr && !isspace(*cptr)) *tptr++ = *cptr++;
+ *tptr = '\0';
+ return(tok);
+}
+static char *wskip(cptr)
+char *cptr;
+{
+ if (!*cptr || !isspace(*cptr)) {
+ warning(cptr, "expected a space");
+ return(cptr);
+ }
+ while (*cptr && isspace(*cptr)) ++cptr;
+ return(cptr);
+}
+static char *cskip(cptr)
+char *cptr;
+{
+ if (!*cptr || isspace(*cptr)) {
+ warning(cptr, "wasn't expecting a space");
+ return(cptr);
+ }
+ while (*cptr && !isspace(*cptr)) ++cptr;
+ return(cptr);
+}
+
+static char *getmsg(fd, cptr, quote)
+int fd;
+char *cptr;
+char quote;
+{
+ static char *msg = NULL;
+ static long msglen = 0;
+ long clen, i;
+ char *tptr;
+
+ int needq;
+
+ if (quote && *cptr == quote) {
+ needq = True;
+ ++cptr;
+ } else needq = False;
+
+ clen = strlen(cptr) + 1;
+ if (clen > msglen) {
+ if (msglen) msg = (char *) realloc(msg, clen);
+ else msg = (char *) malloc(clen);
+ msglen = clen;
+ }
+ tptr = msg;
+
+ while (*cptr) {
+ if (quote && *cptr == quote) {
+ char *tmp;
+ tmp = cptr+1;
+ if (*tmp && (!isspace(*tmp) || *wskip(tmp))) {
+ warning(cptr, "unexpected quote character, ignoreing");
+ *tptr++ = *cptr++;
+ } else {
+ *cptr = '\0';
+ }
+ } else if (*cptr == '\\') {
+ ++cptr;
+ switch (*cptr) {
+ case '\0':
+ cptr = getline(fd);
+ if (!cptr) error(NULL, "premature end of file");
+ msglen += strlen(cptr);
+ i = tptr - msg;
+ msg = (char *) realloc(msg, msglen);
+ tptr = msg + i;
+ break;
+ case 'n':
+ *tptr++ = '\n';
+ ++cptr;
+ break;
+ case 't':
+ *tptr++ = '\t';
+ ++cptr;
+ break;
+ case 'v':
+ *tptr++ = '\v';
+ ++cptr;
+ break;
+ case 'b':
+ *tptr++ = '\b';
+ ++cptr;
+ break;
+ case 'r':
+ *tptr++ = '\r';
+ ++cptr;
+ break;
+ case 'f':
+ *tptr++ = '\f';
+ ++cptr;
+ break;
+ case '"':
+ *tptr++ = '"';
+ ++cptr;
+ break;
+ case '\\':
+ *tptr++ = '\\';
+ ++cptr;
+ break;
+ default:
+ if (isdigit(*cptr)) {
+ *tptr = 0;
+ for (i = 0; i < 3; ++i) {
+ if (!isdigit(*cptr)) break;
+ if (*cptr > '7') warning(cptr, "octal number greater than 7?!");
+ *tptr *= 8;
+ *tptr += (*cptr - '0');
+ ++cptr;
+ }
+ } else {
+ warning(cptr, "unrecognized escape sequence");
+ }
+ }
+ } else {
+ *tptr++ = *cptr++;
+ }
+ }
+ *tptr = '\0';
+ return(msg);
+}
+
+
+
+static char *dupstr(ostr)
+char *ostr;
+{
+ char *nstr;
+
+ nstr = (char *) malloc(strlen(ostr) + 1);
+ if (!nstr) error(NULL, "unable to allocate storage");
+ strcpy(nstr, ostr);
+ return(nstr);
+}
+
+
+/*
+ * The Global Stuff
+ */
+
+
+typedef struct _msgT {
+ long msgId;
+ char *str;
+ char *hconst;
+ long offset;
+ struct _msgT *prev, *next;
+} msgT;
+typedef struct _setT {
+ long setId;
+ char *hconst;
+ msgT *first, *last;
+ struct _setT *prev, *next;
+} setT;
+typedef struct {
+ setT *first, *last;
+} catT;
+
+static setT *curSet;
+static catT *cat;
+
+/*
+ * Find the current byte order. There are of course some others, but this will do
+ * for now. Note that all we care about is "long".
+ */
+long MCGetByteOrder() {
+ long l = 0x00010203;
+ char *cptr = (char *) &l;
+
+ if (cptr[0] == 0 && cptr[1] == 1 && cptr[2] == 2 && cptr[3] == 3)
+ return MC68KByteOrder;
+ else return MCn86ByteOrder;
+}
+
+
+void MCParse(
+#if PROTO
+ int fd)
+#else
+ fd)
+int fd;
+#endif
+{
+ char *cptr, *str;
+ int setid, msgid = 0;
+ char hconst[MAXTOKEN+1];
+ char quote = 0;
+ int i;
+
+ if (!cat) {
+ cat = (catT *) malloc(sizeof(catT));
+ if (!cat) nomem();
+ bzero(cat, sizeof(catT));
+ }
+
+ hconst[0] = '\0';
+
+ while (cptr = getline(fd)) {
+ if (*cptr == '$') {
+ ++cptr;
+ if (strncmp(cptr, "set", 3) == 0) {
+ cptr += 3;
+ cptr = wskip(cptr);
+ setid = atoi(cptr);
+ cptr = cskip(cptr);
+ if (*cptr) cptr = wskip(cptr);
+ if (*cptr == '#') {
+ ++cptr;
+ MCAddSet(setid, token(cptr));
+ } else MCAddSet(setid, NULL);
+ msgid = 0;
+ } else if (strncmp(cptr, "delset", 6) == 0) {
+ cptr += 6;
+ cptr = wskip(cptr);
+ setid = atoi(cptr);
+ MCDelSet(setid);
+ } else if (strncmp(cptr, "quote", 5) == 0) {
+ cptr += 5;
+ if (!*cptr) quote = 0;
+ else {
+ cptr = wskip(cptr);
+ if (!*cptr) quote = 0;
+ else quote = *cptr;
+ }
+ } else if (isspace(*cptr)) {
+ cptr = wskip(cptr);
+ if (*cptr == '#') {
+ ++cptr;
+ strcpy(hconst, token(cptr));
+ }
+ } else {
+ if (*cptr) {
+ cptr = wskip(cptr);
+ if (*cptr) warning(cptr, "unrecognized line");
+ }
+ }
+ } else {
+ if (isdigit(*cptr) || *cptr == '#') {
+ if (*cptr == '#') {
+ ++msgid;
+ ++cptr;
+ if (!*cptr) {
+ MCAddMsg(msgid, "", hconst);
+ hconst[0] = '\0';
+ continue;
+ }
+ if (!isspace(*cptr)) warning(cptr, "expected a space");
+ ++cptr;
+ if (!*cptr) {
+ MCAddMsg(msgid, "", hconst);
+ hconst[0] = '\0';
+ continue;
+ }
+ } else {
+ msgid = atoi(cptr);
+ cptr = cskip(cptr);
+ cptr = wskip(cptr);
+ /* if (*cptr) ++cptr; */
+ }
+ if (!*cptr) MCDelMsg(msgid);
+ else {
+ str = getmsg(fd, cptr, quote);
+ MCAddMsg(msgid, str, hconst);
+ hconst[0] = '\0';
+ }
+ }
+ }
+ }
+}
+
+void MCReadCat(
+#if PROTO
+ int fd)
+#else
+ fd)
+int fd;
+#endif
+{
+ MCHeaderT mcHead;
+ MCMsgT mcMsg;
+ MCSetT mcSet;
+ msgT *msg;
+ setT *set;
+ int i;
+ char *data;
+
+ cat = (catT *) malloc(sizeof(catT));
+ if (!cat) nomem();
+ bzero(cat, sizeof(catT));
+
+ if (read(fd, &mcHead, sizeof(mcHead)) != sizeof(mcHead)) corrupt();
+ if (strncmp(mcHead.magic, MCMagic, MCMagicLen) != 0) corrupt();
+ if (mcHead.majorVer != MCMajorVer) error(NULL, "unrecognized catalog version");
+ if ((mcHead.flags & MCGetByteOrder()) == 0) error(NULL, "wrong byte order");
+
+ if (lseek(fd, mcHead.firstSet, L_SET) == -1) corrupt();
+
+ while (True) {
+ if (read(fd, &mcSet, sizeof(mcSet)) != sizeof(mcSet)) corrupt();
+ if (mcSet.invalid) continue;
+
+ set = (setT *) malloc(sizeof(setT));
+ if (!set) nomem();
+ bzero(set, sizeof(*set));
+ if (cat->first) {
+ cat->last->next = set;
+ set->prev = cat->last;
+ cat->last = set;
+ } else cat->first = cat->last = set;
+
+ set->setId = mcSet.setId;
+
+ /* Get the data */
+ if (mcSet.dataLen) {
+ data = (char *) malloc(mcSet.dataLen);
+ if (!data) nomem();
+ if (lseek(fd, mcSet.data.off, L_SET) == -1) corrupt();
+ if (read(fd, data, mcSet.dataLen) != mcSet.dataLen) corrupt();
+ if (lseek(fd, mcSet.u.firstMsg, L_SET) == -1) corrupt();
+
+ for (i = 0; i < mcSet.numMsgs; ++i) {
+ if (read(fd, &mcMsg, sizeof(mcMsg)) != sizeof(mcMsg)) corrupt();
+ if (mcMsg.invalid) {
+ --i;
+ continue;
+ }
+
+ msg = (msgT *) malloc(sizeof(msgT));
+ if (!msg) nomem();
+ bzero(msg, sizeof(*msg));
+ if (set->first) {
+ set->last->next = msg;
+ msg->prev = set->last;
+ set->last = msg;
+ } else set->first = set->last = msg;
+
+ msg->msgId = mcMsg.msgId;
+ msg->str = dupstr((char *) (data + mcMsg.msg.off));
+ }
+ free(data);
+ }
+ if (!mcSet.nextSet) break;
+ if (lseek(fd, mcSet.nextSet, L_SET) == -1) corrupt();
+ }
+}
+
+
+static void printS(fd, str)
+int fd;
+char *str;
+{
+ write(fd, str, strlen(str));
+}
+static void printL(fd, l)
+int fd;
+long l;
+{
+ char buf[32];
+ sprintf(buf, "%ld", l);
+ write(fd, buf, strlen(buf));
+}
+static void printLX(fd, l)
+int fd;
+long l;
+{
+ char buf[32];
+ sprintf(buf, "%lx", l);
+ write(fd, buf, strlen(buf));
+}
+
+static void genconst(fd, type, setConst, msgConst, val)
+int fd;
+int type;
+char *setConst;
+char *msgConst;
+long val;
+{
+ switch (type) {
+ case MCLangC:
+ if (!msgConst) {
+ printS(fd, "\n#define ");
+ printS(fd, setConst);
+ printS(fd, "Set");
+ } else {
+ printS(fd, "#define ");
+ printS(fd, setConst);
+ printS(fd, msgConst);
+ }
+ printS(fd, "\t0x");
+ printLX(fd, val);
+ printS(fd, "\n");
+ break;
+ case MCLangCPlusPlus:
+ case MCLangANSIC:
+ if (!msgConst) {
+ printS(fd, "\nconst long ");
+ printS(fd, setConst);
+ printS(fd, "Set");
+ } else {
+ printS(fd, "const long ");
+ printS(fd, setConst);
+ printS(fd, msgConst);
+ }
+ printS(fd, "\t= ");
+ printL(fd, val);
+ printS(fd, ";\n");
+ break;
+ default:
+ error(NULL, "not a recognized (programming) language type");
+ }
+}
+
+void MCWriteConst(
+#if PROTO
+ int fd, int type, int orConsts)
+#else
+ fd, type, orConsts)
+int fd;
+int type;
+int orConsts;
+#endif
+{
+ msgT *msg;
+ setT *set;
+ long id;
+
+ if (orConsts && (type == MCLangC || type == MCLangCPlusPlus || type == MCLangANSIC)) {
+ printS(fd, "/* Use these Macros to compose and decompose setId's and msgId's */\n");
+ printS(fd, "#ifndef MCMakeId\n");
+ printS(fd, "# define MCMakeId(s,m)\t(unsigned long)(((unsigned short)s<<(sizeof(short)*8))\\\n");
+ printS(fd, "\t\t\t\t\t|(unsigned short)m)\n");
+ printS(fd, "# define MCSetId(id)\t(unsigned int) (id >> (sizeof(short) * 8))\n");
+ printS(fd, "# define MCMsgId(id)\t(unsigned int) ((id << (sizeof(short) * 8))\\\n");
+ printS(fd, "\t\t\t\t\t>> (sizeof(short) * 8))\n");
+ printS(fd, "#endif\n");
+ }
+
+ for (set = cat->first; set; set = set->next) {
+ if (set->hconst) genconst(fd, type, set->hconst, NULL, set->setId);
+
+ for (msg = set->first; msg; msg = msg->next) {
+ if (msg->hconst) {
+ if (orConsts) id = MCMakeId(set->setId, msg->msgId);
+ else id = msg->msgId;
+ genconst(fd, type, set->hconst, msg->hconst, id);
+ free(msg->hconst);
+ msg->hconst = NULL;
+ }
+ }
+ if (set->hconst) {
+ free(set->hconst);
+ set->hconst = NULL;
+ }
+ }
+}
+
+void MCWriteCat(
+#if PROTO
+ int fd)
+#else
+ fd)
+int fd;
+#endif
+{
+ MCHeaderT mcHead;
+ int cnt;
+ setT *set;
+ msgT *msg;
+ MCSetT mcSet;
+ MCMsgT mcMsg;
+ off_t pos;
+
+ bcopy(MCMagic, mcHead.magic, MCMagicLen);
+ mcHead.majorVer = MCMajorVer;
+ mcHead.minorVer = MCMinorVer;
+ mcHead.flags = MCGetByteOrder();
+ mcHead.firstSet = 0; /* We'll be back to set this in a minute */
+
+ for (cnt = 0, set = cat->first; set; set = set->next) ++cnt;
+ mcHead.numSets = cnt;
+
+ lseek(fd, 0L, L_SET);
+ write(fd, &mcHead, sizeof(mcHead));
+ mcHead.firstSet = lseek(fd, 0, L_INCR);
+ lseek(fd, 0L, L_SET);
+ write(fd, &mcHead, sizeof(mcHead));
+
+ for (set = cat->first; set; set = set->next) {
+ bzero(&mcSet, sizeof(mcSet));
+
+ mcSet.setId = set->setId;
+ mcSet.invalid = False;
+
+ /* The rest we'll have to come back and change in a moment */
+ pos = lseek(fd, 0, L_INCR);
+ write(fd, &mcSet, sizeof(mcSet));
+
+ /* Now write all the string data */
+ mcSet.data.off = lseek(fd, 0, L_INCR);
+ cnt = 0;
+ for (msg = set->first; msg; msg = msg->next) {
+ msg->offset = lseek(fd, 0, L_INCR) - mcSet.data.off;
+ mcSet.dataLen += write(fd, msg->str, strlen(msg->str) + 1);
+ ++cnt;
+ }
+ mcSet.u.firstMsg = lseek(fd, 0, L_INCR);
+ mcSet.numMsgs = cnt;
+
+ /* Now write the message headers */
+ for (msg = set->first; msg; msg = msg->next) {
+ mcMsg.msgId = msg->msgId;
+ mcMsg.msg.off = msg->offset;
+ mcMsg.invalid = False;
+ write(fd, &mcMsg, sizeof(mcMsg));
+ }
+
+ /* Go back and fix things up */
+
+ if (set == cat->last) {
+ mcSet.nextSet = 0;
+ lseek(fd, pos, L_SET);
+ write(fd, &mcSet, sizeof(mcSet));
+ } else {
+ mcSet.nextSet = lseek(fd, 0, L_INCR);
+ lseek(fd, pos, L_SET);
+ write(fd, &mcSet, sizeof(mcSet));
+ lseek(fd, mcSet.nextSet, L_SET);
+ }
+ }
+}
+
+
+void MCAddSet(
+#if PROTO
+ int setId, char *hconst)
+#else
+ setId, hconst)
+int setId;
+char *hconst;
+#endif
+{
+ setT *set;
+
+ if (setId <= 0) {
+ error(NULL, "setId's must be greater than zero");
+ return;
+ }
+
+ if (hconst && !*hconst) hconst = NULL;
+ for (set = cat->first; set; set = set->next) {
+ if (set->setId == setId) {
+ if (set->hconst && hconst) free(set->hconst);
+ set->hconst = NULL;
+ break;
+ } else if (set->setId > setId) {
+ setT *newSet;
+
+ newSet = (setT *) malloc(sizeof(setT));
+ if (!newSet) nomem();
+ bzero(newSet, sizeof(setT));
+ newSet->prev = set->prev;
+ newSet->next = set;
+ if (set->prev) set->prev->next = newSet;
+ else cat->first = newSet;
+ set->prev = newSet;
+ set = newSet;
+ break;
+ }
+ }
+ if (!set) {
+ set = (setT *) malloc(sizeof(setT));
+ if (!set) nomem();
+ bzero(set, sizeof(setT));
+
+ if (cat->first) {
+ set->prev = cat->last;
+ set->next = NULL;
+ cat->last->next = set;
+ cat->last = set;
+ } else {
+ set->prev = set->next = NULL;
+ cat->first = cat->last = set;
+ }
+ }
+ set->setId = setId;
+ if (hconst) set->hconst = dupstr(hconst);
+ curSet = set;
+}
+
+void MCAddMsg(
+#if PROTO
+ int msgId, char *str, char *hconst)
+#else
+ msgId, str, hconst)
+int msgId;
+char *str;
+char *hconst;
+#endif
+{
+ msgT *msg;
+
+ if (!curSet) error(NULL, "can't specify a message when no set exists");
+
+ if (msgId <= 0) {
+ error(NULL, "msgId's must be greater than zero");
+ return;
+ }
+
+ if (hconst && !*hconst) hconst = NULL;
+ for (msg = curSet->first; msg; msg = msg->next) {
+ if (msg->msgId == msgId) {
+ if (msg->hconst && hconst) free(msg->hconst);
+ if (msg->str) free(msg->str);
+ msg->hconst = msg->str = NULL;
+ break;
+ } else if (msg->msgId > msgId) {
+ msgT *newMsg;
+
+ newMsg = (msgT *) malloc(sizeof(msgT));
+ if (!newMsg) nomem();
+ bzero(newMsg, sizeof(msgT));
+ newMsg->prev = msg->prev;
+ newMsg->next = msg;
+ if (msg->prev) msg->prev->next = newMsg;
+ else curSet->first = newMsg;
+ msg->prev = newMsg;
+ msg = newMsg;
+ break;
+ }
+ }
+ if (!msg) {
+ msg = (msgT *) malloc(sizeof(msgT));
+ if (!msg) nomem();
+ bzero(msg, sizeof(msgT));
+
+ if (curSet->first) {
+ msg->prev = curSet->last;
+ msg->next = NULL;
+ curSet->last->next = msg;
+ curSet->last = msg;
+ } else {
+ msg->prev = msg->next = NULL;
+ curSet->first = curSet->last = msg;
+ }
+ }
+ msg->msgId = msgId;
+ if (hconst) msg->hconst = dupstr(hconst);
+ msg->str = dupstr(str);
+}
+
+void MCDelSet(
+#if PROTO
+ int setId)
+#else
+ setId)
+int setId;
+#endif
+{
+ setT *set;
+ msgT *msg;
+
+ for (set = cat->first; set; set = set->next) {
+ if (set->setId == setId) {
+ for (msg = set->first; msg; msg = msg->next) {
+ if (msg->hconst) free(msg->hconst);
+ if (msg->str) free(msg->str);
+ free(msg);
+ }
+ if (set->hconst) free(set->hconst);
+
+ if (set->prev) set->prev->next = set->next;
+ else cat->first = set->next;
+
+ if (set->next) set->next->prev = set->prev;
+ else cat->last = set->prev;
+
+ free(set);
+ return;
+ } else if (set->setId > setId) break;
+ }
+ warning(NULL, "specified set doesn't exist");
+}
+
+void MCDelMsg(
+#if PROTO
+ int msgId)
+#else
+ msgId)
+int msgId;
+#endif
+{
+ msgT *msg;
+
+ if (!curSet) error(NULL, "you can't delete a message before defining the set");
+
+ for (msg = curSet->first; msg; msg = msg->next) {
+ if (msg->msgId == msgId) {
+ if (msg->hconst) free(msg->hconst);
+ if (msg->str) free(msg->str);
+
+ if (msg->prev) msg->prev->next = msg->next;
+ else curSet->first = msg->next;
+
+ if (msg->next) msg->next->prev = msg->prev;
+ else curSet->last = msg->prev;
+
+ free(msg);
+ return;
+ } else if (msg->msgId > msgId) break;
+ }
+ warning(NULL, "specified msg doesn't exist");
+}
+
+void MCDumpcat(fp)
+FILE *fp;
+{
+ msgT *msg;
+ setT *set;
+
+ if (!cat) {
+ fprintf(stderr, "No catalog open\n");
+ exit (1);
+ }
+
+ for (set = cat->first; set; set = set->next) {
+ fprintf(fp, "$set %d", set->setId);
+ if (set->hconst)
+ fprintf(fp, " # %s", set->hconst);
+ fprintf(fp, "\n\n");
+
+ for (msg = set->first; msg; msg = msg->next) {
+ if (msg->hconst)
+ fprintf(fp, "# %s\n", msg->hconst);
+ fprintf(fp, "%d\t%s\n", msg->msgId, msg->str);
+ }
+ fprintf(fp, "\n");
+ }
+
+}
diff --git a/usr.bin/getopt/Makefile b/usr.bin/getopt/Makefile
new file mode 100644
index 0000000..21dde95
--- /dev/null
+++ b/usr.bin/getopt/Makefile
@@ -0,0 +1,7 @@
+# $Header: /b/source/CVS/src/usr.bin/getopt/Makefile,v 1.1 1993/06/21 12:43:58 brezak Exp $
+#
+
+PROG = getopt
+MAN1 = getopt.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/getopt/README b/usr.bin/getopt/README
new file mode 100644
index 0000000..55e6998
--- /dev/null
+++ b/usr.bin/getopt/README
@@ -0,0 +1,57 @@
+/***** unido:mod.std.unix / ut-sally!jsq / 8:54 pm Jul 4, 1985*/
+From: John Quarterman (moderator) <ut-sally!std-unix>
+
+Topic: yet more on getopt (command line arguments)
+
+Two more messages, the first a followup to a previous posting, and
+the second public domain sources and man pages for getopt(3) and getopt(1).
+ -mod
+
+----------------------------------------------------------------------
+
+From: ihnp4!utzoo!henry
+Date: 3 Jul 85 18:34:41 CDT (Wed)
+To: ihnp4!ut-sally!std-unix
+Subject: Re: command line arguments
+
+> > A group of bundled options may end with an option that has an argument.
+>
+> This creates confusion in using C-Kermit when you want to send an image
+> file. For example:
+>
+> send -is filename < --- works fine
+> send -si filename < --- bombs the program
+
+The AT&T syntax standard (which getopt does not completely enforce)
+actually forbids both of these usages. Options with arguments are not
+allowed to be bundled, and they must be separated from their arguments
+by a space.
+
+> I would *much* prefer to bundle the flags, then
+> have those with arguments pick them up in the same order as the flags are
+> listed.
+
+The few existing commands that use such a convention, notably tar(1), are
+(in my experience) the worse for it. It's seriously error-prone. I think
+the AT&T people did the right thing.
+
+------------------------------
+
+Date: Tue, 2 Jul 85 13:07:09 edt
+From: ihnp4!utcs!ian (Ian F. Darwin)
+To: ihnp4!ut-sally!jsq@tzec.UTEXAS.ARPA
+Subject: here is getopt
+
+Here is the source for getopt(3), the function that should be in
+everybody's C program, and getopt(1), a program that uses it to
+make shell programs comprehensible and consistent. There are man
+pages for both. Please send these on to the mod. group. Thanks.
+
+[ I have hacked the following shell script slightly so that
+it doesn't extract directly into system source directories,
+rather into the current directory. It should be assumed that
+this code comes with no warranty from me, Ian Darwin, or anyone
+else as to whether it accurately represents getopt as distributed
+with System V, or any command line standard, or that it works
+at all, or that it will cause no damage when extracted or used. -mod]
+
diff --git a/usr.bin/getopt/getopt.1 b/usr.bin/getopt/getopt.1
new file mode 100644
index 0000000..16a50f0
--- /dev/null
+++ b/usr.bin/getopt/getopt.1
@@ -0,0 +1,103 @@
+.Dd June 21, 1993
+.Dt GETOPT 1
+.Os
+.Sh NAME
+.Nm getopt
+.Nd parse command options
+.Sh SYNOPSIS
+.Nm set \-\- \`getopt optstring $*\`
+.Sh DESCRIPTION
+.Nm Getopt
+is used to break up options in command lines for easy parsing by
+shell procedures, and to check for legal options.
+.Op Optstring
+is a string of recognized option letters (see
+.Xr getopt 3
+);
+if a letter is followed by a colon, the option
+is expected to have an argument which may or may not be
+separated from it by white space.
+The special option
+.B \-\-
+is used to delimit the end of the options.
+.Nm Getopt
+will place
+.B \-\-
+in the arguments at the end of the options,
+or recognize it if used explicitly.
+The shell arguments
+(\fB$1 $2\fR ...) are reset so that each option is
+preceded by a
+.B \-
+and in its own shell argument;
+each option argument is also in its own shell argument.
+.Sh EXAMPLE
+The following code fragment shows how one might process the arguments
+for a command that can take the options
+.Op a
+and
+.Op b ,
+and the option
+.Op o ,
+which requires an argument.
+.Pp
+.Bd -literal -offset indent
+set \-\- \`getopt abo: $*\`
+if test $? != 0
+then
+ echo 'Usage: ...'
+ exit 2
+fi
+for i
+do
+ case "$i"
+ in
+ \-a|\-b)
+ flag=$i; shift;;
+ \-o)
+ oarg=$2; shift; shift;;
+ \-\-)
+ shift; break;;
+ esac
+done
+.Ed
+.Pp
+This code will accept any of the following as equivalent:
+.Pp
+.Bd -literal -offset indent
+cmd \-aoarg file file
+cmd \-a \-o arg file file
+cmd \-oarg -a file file
+cmd \-a \-oarg \-\- file file
+.Ed
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr getopt 3
+.Sh DIAGNOSTICS
+.Nm Getopt
+prints an error message on the standard error output when it
+encounters an option letter not included in
+.Op optstring .
+.Sh HISTORY
+Written by Henry Spencer, working from a Bell Labs manual page.
+Behavior believed identical to the Bell version.
+.Sh BUGS
+Whatever
+.Xr getopt 3
+has.
+.Pp
+Arguments containing white space or imbedded shell metacharacters
+generally will not survive intact; this looks easy to fix but isn't.
+.Pp
+The error message for an invalid option is identified as coming
+from
+.Nm getopt
+rather than from the shell procedure containing the invocation
+of
+.Nm getopt ;
+this again is hard to fix.
+.Pp
+The precise best way to use the
+.Nm set
+command to set the arguments without disrupting the value(s) of
+shell options varies from one shell version to another.
diff --git a/usr.bin/getopt/getopt.c b/usr.bin/getopt/getopt.c
new file mode 100644
index 0000000..03b0987
--- /dev/null
+++ b/usr.bin/getopt/getopt.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ int c;
+ int status = 0;
+
+ optind = 2; /* Past the program name and the option letters. */
+ while ((c = getopt(argc, argv, argv[1])) != EOF)
+ switch (c) {
+ case '?':
+ status = 1; /* getopt routine gave message */
+ break;
+ default:
+ if (optarg != NULL)
+ printf(" -%c %s", c, optarg);
+ else
+ printf(" -%c", c);
+ break;
+ }
+ printf(" --");
+ for (; optind < argc; optind++)
+ printf(" %s", argv[optind]);
+ printf("\n");
+ exit(status);
+}
diff --git a/usr.bin/gprof/Makefile b/usr.bin/gprof/Makefile
new file mode 100644
index 0000000..f8aa1f2
--- /dev/null
+++ b/usr.bin/gprof/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/29/93
+
+PROG= gprof
+SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE}.c hertz.c \
+ printgprof.c printlist.c
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${.CURDIR}/gprof.flat ${.CURDIR}/gprof.callg \
+ ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/gprof/PSD.doc/Makefile b/usr.bin/gprof/PSD.doc/Makefile
new file mode 100644
index 0000000..12f9c39
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 8/14/93
+
+DIR= psd/18.gprof
+SRCS= header.me abstract.me intro.me profiling.me gathering.me \
+ postp.me present.me refs.me
+DPADD= postp1.pic postp2.pic postp3.pic pres1.pic pres2.pic
+MACROS= -me
+
+paper.ps: ${SRCS} ${DPADD}
+ ${SOELIM} ${SRCS} | ${PIC} | ${TBL} | ${EQN} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/gprof/PSD.doc/abstract.me b/usr.bin/gprof/PSD.doc/abstract.me
new file mode 100644
index 0000000..28e8066
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/abstract.me
@@ -0,0 +1,66 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)abstract.me 8.1 (Berkeley) 6/8/93
+.\"
+.sp 1
+\fB\s+2gprof: a Call Graph Execution Profiler\s-2\fP\**
+.(f
+\**This work was supported by grant MCS80-05144
+from the National Science Foundation.
+.)f
+.sp 1
+by
+\fISusan L. Graham\fP
+\fIPeter B. Kessler\fP
+\fIMarshall K. McKusick\fP
+.sp 1
+Computer Science Division
+Electrical Engineering and Computer Science Department
+University of California, Berkeley
+Berkeley, California 94720
+.ce 0
+.sp 1
+.sp 0.5i
+.sh 0 "Abstract"
+.pp
+Large complex programs are composed of many small routines
+that implement abstractions for the routines that call them.
+To be useful, an execution profiler must attribute
+execution time in a way that is significant for the
+logical structure of a program
+as well as for its textual decomposition.
+This data must then be displayed to the user
+in a convenient and informative way.
+The \fBgprof\fP profiler
+accounts for the running time of called routines
+in the running time of the routines that call them.
+The design and use of this profiler is described.
diff --git a/usr.bin/gprof/PSD.doc/gathering.me b/usr.bin/gprof/PSD.doc/gathering.me
new file mode 100644
index 0000000..17130c3
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/gathering.me
@@ -0,0 +1,231 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)gathering.me 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Gathering Profile Data"
+.pp
+Routine calls or statement executions can be measured by having a
+compiler augment the code at strategic points.
+The additions can be inline increments to counters [Knuth71]
+[Satterthwaite72] [Joy79] or calls to
+monitoring routines [Unix].
+The counter increment overhead is low, and is suitable for
+profiling statements.
+A call of the monitoring routine has an overhead comparable with a
+call of a regular routine, and is therefore only suited
+to profiling on a routine by routine basis.
+However, the monitoring routine solution has certain advantages.
+Whatever counters are needed by the monitoring routine can be
+managed by the monitoring routine itself, rather than being
+distributed around the code.
+In particular, a monitoring routine can easily be called from separately
+compiled programs.
+In addition, different monitoring routines can be linked into the
+program
+being measured
+to assemble different profiling data without having to
+change the compiler or recompile the program.
+We have exploited this approach;
+our compilers for C, Fortran77, and Pascal can insert calls to a
+monitoring routine in the prologue for each routine.
+Use of the monitoring routine requires no planning on part of a
+programmer other than to request that augmented routine
+prologues be produced during compilation.
+.pp
+We are interested in gathering three pieces of information during
+program execution: call counts and execution times for
+each profiled routine, and the arcs of the dynamic call graph
+traversed by this execution of the program.
+By post-processing of this data we can build the dynamic call
+graph for this execution of the program and propagate times along
+the edges of this graph to attribute times for routines to the
+routines that invoke them.
+.pp
+Gathering of the profiling information should not greatly
+interfere with the running of the program.
+Thus, the monitoring routine must not produce trace output each
+time it is invoked.
+The volume of data thus produced would be unmanageably large,
+and the time required to record it would overwhelm the running
+time of most programs.
+Similarly, the monitoring routine can not do the analysis of
+the profiling data (e.g. assembling the call graph, propagating
+times around it, discovering cycles, etc.) during program
+execution.
+Our solution is to gather profiling data in memory during program
+execution and to condense it to a file as the profiled
+program exits.
+This file is then processed by a separate program to produce the
+listing of the profile data.
+An advantage of this approach is that the profile data for
+several executions of a program can be combined by the
+post-processing to provide a profile of many
+executions.
+.pp
+The execution time monitoring consists of three parts.
+The first part allocates and initializes the runtime monitoring data
+structures before the program begins execution.
+The second part is the monitoring routine invoked from the
+prologue of each profiled routine.
+The third part condenses the data structures and writes them
+to a file as the program terminates.
+The monitoring routine is discussed in detail in the following sections.
+.sh 2 "Execution Counts"
+.pp
+The \fBgprof\fP monitoring routine counts the number of times
+each profiled routine is called.
+The monitoring routine also records the arc in the call graph
+that activated the profiled routine.
+The count is associated with the arc in the call graph
+rather than with the routine.
+Call counts for routines can then be determined by summing the counts
+on arcs directed into that routine.
+In a machine-dependent fashion, the monitoring routine notes its
+own return address.
+This address is in the prologue of some profiled routine that is
+the destination of an arc in the dynamic call graph.
+The monitoring routine also discovers the return address for that
+routine, thus identifying the call site, or source of the arc.
+The source of the arc is in the \fIcaller\fP, and the destination is in
+the \fIcallee\fP.
+For example, if a routine A calls a routine B, A is the caller,
+and B is the callee.
+The prologue of B will include a call to the monitoring routine
+that will note the arc from A to B and either initialize or
+increment a counter for that arc.
+.pp
+One can not afford to have the monitoring routine output tracing
+information as each arc is identified.
+Therefore, the monitoring routine maintains a table of all the
+arcs discovered, with counts of the numbers of times each is
+traversed during execution.
+This table is accessed once per routine call.
+Access to it
+must be as fast as possible so as not to overwhelm the time
+required to execute the program.
+.pp
+Our solution is to access the table through a hash table.
+We use the call site as the primary key with the callee
+address being the secondary key.
+Since each call site typically calls only one callee, we can
+reduce (usually to one) the number of minor lookups based on the callee.
+Another alternative would use the callee as the primary key and the
+call site as the secondary key.
+Such an organization has the advantage of associating callers with
+callees, at the expense of longer lookups in the monitoring
+routine.
+We are fortunate to be running in a virtual memory environment,
+and (for the sake of speed) were able to allocate enough space
+for the primary hash table to allow a one-to-one mapping from
+call site addresses to the primary hash table.
+Thus our hash function is trivial to calculate and collisions
+occur only for call sites that call multiple
+destinations (e.g. functional parameters and functional variables).
+A one level hash function using both call site and callee would
+result in an unreasonably large hash table.
+Further, the number of dynamic call sites and callees is not known during
+execution of the profiled program.
+.pp
+Not all callers and callees can be identified by the monitoring
+routine.
+Routines that were compiled without the profiling augmentations
+will not call the monitoring routine as part of their prologue,
+and thus no arcs will be recorded whose destinations are in these
+routines.
+One need not profile all the routines in a program.
+Routines that are not profiled run at full speed.
+Certain routines, notably exception handlers, are invoked by
+non-standard calling sequences.
+Thus the monitoring routine may know the destination of an arc
+(the callee),
+but find it difficult or
+impossible to determine the source of the arc (the caller).
+Often in these cases the apparent source of the arc is not a call
+site at all.
+Such anomalous invocations are declared ``spontaneous''.
+.sh 2 "Execution Times"
+.pp
+The execution times for routines can be gathered in at least two
+ways.
+One method measures the execution time of a routine by measuring
+the elapsed time from routine entry to routine exit.
+Unfortunately, time measurement is complicated on time-sharing
+systems by the time-slicing of the program.
+A second method samples the value of the program counter at some
+interval, and infers execution time from the distribution of the
+samples within the program.
+This technique is particularly suited to time-sharing systems,
+where the time-slicing can serve as the basis for sampling
+the program counter.
+Notice that, whereas the first method could provide exact timings,
+the second is inherently a statistical approximation.
+.pp
+The sampling method need not require support from the operating
+system: all that is needed is the ability to set and respond to
+``alarm clock'' interrupts that run relative to program time.
+It is imperative that the intervals be uniform since the
+sampling of the program counter rather than the duration of the
+interval is the basis of the distribution.
+If sampling is done too often, the interruptions to sample the
+program counter will overwhelm the running of the profiled program.
+On the other hand, the program must run for enough sampled
+intervals that the distribution of the samples accurately
+represents the distribution of time for the execution of the
+program.
+As with routine call tracing, the monitoring routine can not
+afford to output information for each program counter
+sample.
+In our computing environment, the operating system can provide a
+histogram of the location of the program counter at the end of
+each clock tick (1/60th of a second) in which a program runs.
+The histogram is assembled in memory as the program runs.
+This facility is enabled by our monitoring routine.
+We have adjusted the granularity of the histogram so that
+program counter values map one-to-one onto the histogram.
+We make the simplifying assumption that all calls to a specific
+routine require the same amount of time to execute.
+This assumption may disguise that some calls
+(or worse, some call sites) always invoke a routine
+such that its execution is faster (or slower)
+than the average time for that routine.
+.pp
+When the profiled program terminates,
+the arc table and the histogram of
+program counter samples is written to a file.
+The arc table is condensed to consist of the source and destination
+addresses of the arc and the count of the number of times the arc
+was traversed by this execution of the program.
+The recorded histogram consists of counters of the number of
+times the program counter was found to be in each of the ranges covered
+by the histogram.
+The ranges themselves are summarized as a
+lower and upper bound and a step size.
diff --git a/usr.bin/gprof/PSD.doc/header.me b/usr.bin/gprof/PSD.doc/header.me
new file mode 100644
index 0000000..aef606d
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/header.me
@@ -0,0 +1,38 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)header.me 8.1 (Berkeley) 8/14/93
+.\"
+.\"he 'gprof''Graham, Kessler, McKusick'
+.\"fo 'Draft of \*(td''%'
+.\"ls 2
+.eh 'PSD:18-%''gprof \*- a Call Graph Execution Profiler'
+.oh 'gprof \*- A Call Graph Execution Profiler''PSD:18-%'
diff --git a/usr.bin/gprof/PSD.doc/intro.me b/usr.bin/gprof/PSD.doc/intro.me
new file mode 100644
index 0000000..3a872b2
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/intro.me
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)intro.me 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Programs to be Profiled"
+.pp
+Software research environments
+normally include many large programs
+both for production use and for experimental investigation.
+These programs are typically modular,
+in accordance with generally accepted principles
+of good program design.
+Often they consist of numerous small routines
+that implement various abstractions.
+Sometimes such large programs are written
+by one programmer
+who has understood the requirements for
+these abstractions, and has programmed them
+appropriately.
+More frequently the program has
+had multiple authors and has
+evolved over time, changing the demands placed
+on the implementation of the abstractions without
+changing the implementation itself.
+Finally, the program may be assembled from a library
+of abstraction implementations
+unexamined by the programmer.
+.pp
+Once a large program is executable,
+it is often desirable to increase its speed,
+especially if small portions of the program
+are found to dominate its execution time.
+The purpose of the \fBgprof\fP profiling tool is to
+help the user evaluate alternative implementations
+of abstractions.
+We developed this tool in response to our efforts
+to improve a code generator we were writing [Graham82].
+.pp
+The \fBgprof\fP design takes advantage of the fact that the programs
+to be measured are large, structured and hierarchical.
+We provide a profile in which the execution time
+for a set of routines that implement an
+abstraction is collected and charged
+to that abstraction.
+The profile can be used to compare and assess the costs of
+various implementations.
+.pp
+The profiler can be linked into a program without
+special planning by the programmer.
+The overhead for using \fBgprof\fP is low;
+both in terms of added execution time and in the
+volume of profiling information recorded.
diff --git a/usr.bin/gprof/PSD.doc/postp.me b/usr.bin/gprof/PSD.doc/postp.me
new file mode 100644
index 0000000..d71fefb
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/postp.me
@@ -0,0 +1,190 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)postp.me 8.1 (Berkeley) 6/8/93
+.\"
+.EQ
+delim $$
+gsize 11
+.EN
+.sh 1 "Post Processing"
+.pp
+Having gathered the arcs of the call graph and timing information
+for an execution of the program,
+we are interested in attributing the time for each routine to the
+routines that call it.
+We build a dynamic call graph with arcs from caller to callee,
+and propagate time from descendants to ancestors
+by topologically sorting the call graph.
+Time propagation is performed from the leaves of the
+call graph toward the roots, according to the order
+assigned by a topological numbering algorithm.
+The topological numbering ensures that
+all edges in the graph go from higher numbered nodes to lower
+numbered nodes.
+An example is given in Figure 1.
+If we propagate time from nodes in the
+order assigned by the algorithm,
+execution time can be propagated from descendants to ancestors
+after a single traversal of each arc in the call graph.
+Each parent receives some fraction of a child's time.
+Thus time is charged to the
+caller in addition to being charged to the callee.
+.(z
+.so postp1.pic
+.ce 2
+Topological ordering
+Figure 1.
+.ce 0
+.)z
+.pp
+Let $C sub e$ be the number of calls to some routine,
+$e$, and $C sub e sup r$ be the number of
+calls from a caller $r$ to a callee $e$.
+Since we are assuming each call to a routine takes the
+average amount of time for all calls to that routine,
+the caller is accountable for
+$C sub e sup r / C sub e$
+of the time spent by the callee.
+Let the $S sub e$ be the $selftime$ of a routine, $e$.
+The selftime of a routine can be determined from the
+timing information gathered during profiled program execution.
+The total time, $T sub r$, we wish to account to a routine
+$r$, is then given by the recurrence equation:
+.EQ
+T sub r ~ = ~ {S sub r} ~ + ~
+ sum from {r ~ roman CALLS ~ e}
+ {T sub e times {{C sub e sup r} over {C sub e}}}
+.EN
+where $r ~ roman CALLS ~ e$ is a relation showing all routines
+$e$ called by a routine $r$.
+This relation is easily available from the call graph.
+.pp
+However, if the execution contains recursive calls,
+the call graph has cycles that
+cannot be topologically sorted.
+In these cases, we discover strongly-connected
+components in the call graph,
+treat each such component as a single node,
+and then sort the resulting graph.
+We use a variation of Tarjan's strongly-connected
+components algorithm
+that discovers strongly-connected components as it is assigning
+topological order numbers [Tarjan72].
+.pp
+Time propagation within strongly connected
+components is a problem.
+For example, a self-recursive routine
+(a trivial cycle in the call graph)
+is accountable for all the time it
+uses in all its recursive instantiations.
+In our scheme, this time should be
+shared among its call graph parents.
+The arcs from a routine to itself are of interest,
+but do not participate in time propagation.
+Thus the simple equation for time propagation
+does not work within strongly connected components.
+Time is not propagated from one member of a cycle to another,
+since, by definition, this involves propagating time from a routine
+to itself.
+In addition, children of one member of a cycle
+must be considered children of all members of the cycle.
+Similarly, parents of one member of the cycle must inherit
+all members of the cycle as descendants.
+It is for these reasons that we collapse connected components.
+Our solution collects all members of a cycle together,
+summing the time and call counts for all members.
+All calls into the cycle are made to share the total
+time of the cycle, and all descendants of the cycle
+propagate time into the cycle as a whole.
+Calls among the members of the cycle
+do not propagate any time,
+though they are listed in the call graph profile.
+.pp
+Figure 2 shows a modified version of the call graph of Figure 1,
+in which the nodes labelled 3 and 7 in Figure 1 are mutually
+recursive.
+The topologically sorted graph after the cycle is collapsed is
+given in Figure 3.
+.(z
+.so postp2.pic
+.ce 2
+Cycle to be collapsed.
+Figure 2.
+.ce 0
+.)z
+.(z
+.so postp3.pic
+.ce 2
+Topological numbering after cycle collapsing.
+Figure 3.
+.ce 0
+.)z
+.pp
+Since the technique described above only collects the
+dynamic call graph,
+and the program typically does not call every routine
+on each execution,
+different executions can introduce different cycles in the
+dynamic call graph.
+Since cycles often have a significant effect on time propagation,
+it is desirable to incorporate the static call graph so that cycles
+will have the same members regardless of how the program runs.
+.pp
+The static call graph can be constructed from the source text
+of the program.
+However, discovering the static call graph from the source text
+would require two moderately difficult steps:
+finding the source text for the program
+(which may not be available),
+and scanning and parsing that text,
+which may be in any one of several languages.
+.pp
+In our programming system,
+the static calling information is also contained in the
+executable version of the program,
+which we already have available,
+and which is in language-independent form.
+One can examine the instructions
+in the object program,
+looking for calls to routines, and note which
+routines can be called.
+This technique allows us to add arcs to those already in the
+dynamic call graph.
+If a statically discovered arc already exists in the dynamic call
+graph, no action is required.
+Statically discovered arcs that do not exist in the dynamic call
+graph are added to the graph with a traversal count of zero.
+Thus they are never responsible for any time propagation.
+However, they may affect the structure of the graph.
+Since they may complete strongly connected components,
+the static call graph construction is
+done before topological ordering.
diff --git a/usr.bin/gprof/PSD.doc/postp1.pic b/usr.bin/gprof/PSD.doc/postp1.pic
new file mode 100644
index 0000000..1446092
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/postp1.pic
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)postp1.pic 8.1 (Berkeley) 6/8/93
+.\"
+.PS
+circle diam .3i "8"
+circle diam .3i "9" at 1st circle + (2i,0i)
+circle diam .3i "3" at 1st circle + (0.5i,-0.5i)
+circle diam .3i "7" at 2nd circle - (0.5i, 0.5i)
+circle diam .3i "2" at 1st circle - (0i,1i)
+circle diam .3i "5" at 5th circle + (1i,0i)
+circle diam .3i "6" at 2nd circle - (0i,1i)
+circle diam .3i "1" at 3rd circle - (0i,1i)
+circle diam .3i "4" at 4th circle - (0i,1i)
+arrow from 1st circle to 3rd circle chop .15i chop .15i
+arrow from 1st circle to 4th circle chop .15i chop .15i
+arrow from 2nd circle to 4th circle chop .15i chop .15i
+arrow from 3rd circle to 5th circle chop .15i chop .15i
+arrow from 4th circle to 5th circle chop .15i chop .15i
+arrow from 4th circle to 6th circle chop .15i chop .15i
+arrow from 4th circle to 7th circle chop .15i chop .15i
+arrow from 5th circle to 8th circle chop .15i chop .15i
+arrow from 6th circle to 8th circle chop .15i chop .15i
+arrow from 6th circle to 9th circle chop .15i chop .15i
+.PE
diff --git a/usr.bin/gprof/PSD.doc/postp2.pic b/usr.bin/gprof/PSD.doc/postp2.pic
new file mode 100644
index 0000000..3b31736
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/postp2.pic
@@ -0,0 +1,56 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)postp2.pic 8.1 (Berkeley) 6/8/93
+.\"
+.PS
+circle diam .3i "\(ci"
+circle diam .3i "\(ci" at 1st circle + (2i,0i)
+circle diam .3i "\(bu" at 1st circle + (0.5i,-0.5i)
+circle diam .3i "\(bu" at 2nd circle - (0.5i, 0.5i)
+circle diam .3i "\(ci" at 1st circle - (0i,1i)
+circle diam .3i "\(ci" at 5th circle + (1i,0i)
+circle diam .3i "\(ci" at 2nd circle - (0i,1i)
+circle diam .3i "\(ci" at 3rd circle - (0i,1i)
+circle diam .3i "\(ci" at 4th circle - (0i,1i)
+arrow from 1st circle to 3rd circle chop .15i chop .15i
+arrow from 1st circle to 4th circle chop .15i chop .15i
+arrow from 2nd circle to 4th circle chop .15i chop .15i
+spline -> from 3rd circle right .5i up .075i then right .5i down .075i chop .15i chop .15i
+spline -> from 4th circle left .5i down .075i then left .5i up .075i chop .15i chop .15i
+arrow from 3rd circle to 5th circle chop .15i chop .15i
+arrow from 4th circle to 5th circle chop .15i chop .15i
+arrow from 4th circle to 6th circle chop .15i chop .15i
+arrow from 4th circle to 7th circle chop .15i chop .15i
+arrow from 5th circle to 8th circle chop .15i chop .15i
+arrow from 6th circle to 8th circle chop .15i chop .15i
+arrow from 6th circle to 9th circle chop .15i chop .15i
+.PE
diff --git a/usr.bin/gprof/PSD.doc/postp3.pic b/usr.bin/gprof/PSD.doc/postp3.pic
new file mode 100644
index 0000000..65eb2a7
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/postp3.pic
@@ -0,0 +1,51 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)postp3.pic 8.1 (Berkeley) 6/8/93
+.\"
+.PS
+circle diam .3i "7"
+circle diam .3i "8" at 1st circle + (2i,0i)
+EL: ellipse wid 1i ht .3i "\fB6\fR\h'.7i'\fB6\fR" at 1st circle + (1i,-0.5i)
+circle diam .3i "2" at 1st circle - (0i,1i)
+circle diam .3i "4" at 3th circle + (1i,0i)
+circle diam .3i "5" at 2nd circle - (0i,1i)
+circle diam .3i "1" at 3rd circle + (0.5i,-0.5i)
+circle diam .3i "3" at 5th circle - (0.5i,0.5i)
+arrow from 1st circle to EL.nw chop .15i chop 0i
+arrow from 2nd circle to EL.ne chop .15i chop 0i
+arrow from EL.sw to 3rd circle chop 0i chop .15i
+arrow from EL.s to 4th circle chop 0i chop .15i
+arrow from EL.se to 5th circle chop 0i chop .15i
+arrow from 3rd circle to 6th circle chop .15i chop .15i
+arrow from 4th circle to 6th circle chop .15i chop .15i
+arrow from 4th circle to 7th circle chop .15i chop .15i
+.PE
diff --git a/usr.bin/gprof/PSD.doc/pres1.pic b/usr.bin/gprof/PSD.doc/pres1.pic
new file mode 100644
index 0000000..0c311a1
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/pres1.pic
@@ -0,0 +1,56 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pres1.pic 8.1 (Berkeley) 6/8/93
+.\"
+.PS
+ellipse ht .3i wid .75i "\s-1CALLER1\s+1"
+ellipse ht .3i wid .75i "\s-1CALLER2\s+1" at 1st ellipse + (2i,0i)
+ellipse ht .3i wid .8i "\s-1EXAMPLE\s+1" at 1st ellipse + (1i,-.5i)
+ellipse ht .3i wid .5i "\s-1SUB1\s+1" at 1st ellipse - (0i,1i)
+ellipse ht .3i wid .5i "\s-1SUB2\s+1" at 3rd ellipse - (0i,.5i)
+ellipse ht .3i wid .5i "\s-1SUB3\s+1" at 2nd ellipse - (0i,1i)
+line <- from 1st ellipse up .5i left .5i chop .1875i
+line <- from 1st ellipse up .5i right .5i chop .1875i
+line <- from 2nd ellipse up .5i left .5i chop .1875i
+line <- from 2nd ellipse up .5i right .5i chop .1875i
+arrow from 1st ellipse to 3rd ellipse chop
+arrow from 2nd ellipse to 3rd ellipse chop
+arrow from 3rd ellipse to 4th ellipse chop
+arrow from 3rd ellipse to 5th ellipse chop .15i chop .15i
+arrow from 3rd ellipse to 6th ellipse chop
+arrow from 4th ellipse down .5i left .5i chop .1875i
+arrow from 4th ellipse down .5i right .5i chop .1875i
+arrow from 5th ellipse down .5i left .5i chop .1875i
+arrow from 5th ellipse down .5i right .5i chop .1875i
+arrow from 6th ellipse down .5i left .5i chop .1875i
+arrow from 6th ellipse down .5i right .5i chop .1875i
+.PE
diff --git a/usr.bin/gprof/PSD.doc/pres2.pic b/usr.bin/gprof/PSD.doc/pres2.pic
new file mode 100644
index 0000000..c3a4ea0
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/pres2.pic
@@ -0,0 +1,52 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pres2.pic 8.1 (Berkeley) 6/8/93
+.\"
+.PS
+ellipse ht .3i wid .6i "\s-1CALC1\s+1"
+ellipse ht .3i wid .6i "\s-1CALC2\s+1" at 1st ellipse + (.75i,0i)
+ellipse ht .3i wid .6i "\s-1CALC3\s+1" at 1st ellipse + (1.5i,0i)
+ellipse ht .3i wid .8i "\s-1FORMAT1\s+1" at 1st ellipse - (0i,.5i)
+ellipse ht .3i wid .8i "\s-1FORMAT2\s+1" at 3rd ellipse - (0i,.5i)
+ellipse ht .3i wid .75i "\s-1\"WRITE\"\s+1" at 5th ellipse - (.75i,.5i)
+line <- from 1st ellipse up .5i left .4i chop .1825i
+line <- from 1st ellipse up .5i right .4i chop .1825i
+line <- from 2nd ellipse up .5i left .4i chop .1825i
+line <- from 2nd ellipse up .5i right .4i chop .1825i
+line <- from 3rd ellipse up .5i left .4i chop .1825i
+line <- from 3rd ellipse up .5i right .4i chop .1825i
+arrow from 1st ellipse to 4th ellipse chop .15i
+arrow from 2nd ellipse to 5th ellipse chop
+arrow from 3rd ellipse to 5th ellipse chop .15i
+arrow from 4th ellipse to 6th ellipse chop
+arrow from 5th ellipse to 6th ellipse chop
+.PE
diff --git a/usr.bin/gprof/PSD.doc/present.me b/usr.bin/gprof/PSD.doc/present.me
new file mode 100644
index 0000000..1dd7f62
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/present.me
@@ -0,0 +1,306 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)present.me 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Data Presentation"
+.pp
+The data is presented to the user in two different formats.
+The first presentation simply lists the routines
+without regard to the amount of time their descendants use.
+The second presentation incorporates the call graph of the
+program.
+.sh 2 "The Flat Profile
+.pp
+The flat profile consists of a list of all the routines
+that are called during execution of the program,
+with the count of the number of times they are called
+and the number of seconds of execution time for which they
+are themselves accountable.
+The routines are listed in decreasing order of execution time.
+A list of the routines that are never called during execution of
+the program is also available
+to verify that nothing important is omitted by
+this execution.
+The flat profile gives a quick overview of the routines that are used,
+and shows the routines that are themselves responsible
+for large fractions of the execution time.
+In practice,
+this profile usually shows that no single function
+is overwhelmingly responsible for
+the total time of the program.
+Notice that for this profile,
+the individual times sum to the total execution time.
+.sh 2 "The Call Graph Profile"
+.sz 10
+.(z
+.TS
+box center;
+c c c c c l l
+c c c c c l l
+c c c c c l l
+l n n n c l l.
+ called/total \ \ parents
+index %time self descendants called+self name index
+ called/total \ \ children
+_
+ 0.20 1.20 4/10 \ \ \s-1CALLER1\s+1 [7]
+ 0.30 1.80 6/10 \ \ \s-1CALLER2\s+1 [1]
+[2] 41.5 0.50 3.00 10+4 \s-1EXAMPLE\s+1 [2]
+ 1.50 1.00 20/40 \ \ \s-1SUB1\s+1 <cycle1> [4]
+ 0.00 0.50 1/5 \ \ \s-1SUB2\s+1 [9]
+ 0.00 0.00 0/5 \ \ \s-1SUB3\s+1 [11]
+.TE
+.ce 2
+Profile entry for \s-1EXAMPLE\s+1.
+Figure 4.
+.)z
+.pp
+Ideally, we would like to print the call graph of the program,
+but we are limited by the two-dimensional nature of our output
+devices.
+We cannot assume that a call graph is planar,
+and even if it is, that we can print a planar version of it.
+Instead, we choose to list each routine,
+together with information about
+the routines that are its direct parents and children.
+This listing presents a window into the call graph.
+Based on our experience,
+both parent information and child information
+is important,
+and should be available without searching
+through the output.
+.pp
+The major entries of the call graph profile are the entries from the
+flat profile, augmented by the time propagated to each
+routine from its descendants.
+This profile is sorted by the sum of the time for the routine
+itself plus the time inherited from its descendants.
+The profile shows which of the higher level routines
+spend large portions of the total execution time
+in the routines that they call.
+For each routine, we show the amount of time passed by each child
+to the routine, which includes time for the child itself
+and for the descendants of the child
+(and thus the descendants of the routine).
+We also show the percentage these times represent of the total time
+accounted to the child.
+Similarly, the parents of each routine are listed,
+along with time,
+and percentage of total routine time,
+propagated to each one.
+.pp
+Cycles are handled as single entities.
+The cycle as a whole is shown as though it were a single routine,
+except that members of the cycle are listed in place of the children.
+Although the number of calls of each member
+from within the cycle are shown,
+they do not affect time propagation.
+When a child is a member of a cycle,
+the time shown is the appropriate fraction of the time
+for the whole cycle.
+Self-recursive routines have their calls broken
+down into calls from the outside and self-recursive calls.
+Only the outside calls affect the propagation of time.
+.pp
+The following example is a typical fragment of a call graph.
+.(b
+.so pres1.pic
+.)b
+The entry in the call graph profile listing for this example is
+shown in Figure 4.
+.pp
+The entry is for routine \s-1EXAMPLE\s+1, which has
+the Caller routines as its parents,
+and the Sub routines as its children.
+The reader should keep in mind that all information
+is given \fIwith respect to \s-1EXAMPLE\s+1\fP.
+The index in the first column shows that \s-1EXAMPLE\s+1
+is the second entry in the profile listing.
+The \s-1EXAMPLE\s+1 routine is called ten times, four times by \s-1CALLER1\s+1,
+and six times by \s-1CALLER2\s+1.
+Consequently 40% of \s-1EXAMPLE\s+1's time is propagated to \s-1CALLER1\s+1,
+and 60% of \s-1EXAMPLE\s+1's time is propagated to \s-1CALLER2\s+1.
+The self and descendant fields of the parents
+show the amount of self and descendant time \s-1EXAMPLE\s+1
+propagates to them (but not the time used by
+the parents directly).
+Note that \s-1EXAMPLE\s+1 calls itself recursively four times.
+The routine \s-1EXAMPLE\s+1 calls routine \s-1SUB1\s+1 twenty times, \s-1SUB2\s+1 once,
+and never calls \s-1SUB3\s+1.
+Since \s-1SUB2\s+1 is called a total of five times,
+20% of its self and descendant time is propagated to \s-1EXAMPLE\s+1's
+descendant time field.
+Because \s-1SUB1\s+1 is a member of \fIcycle 1\fR,
+the self and descendant times
+and call count fraction
+are those for the cycle as a whole.
+Since cycle 1 is called a total of forty times
+(not counting calls among members of the cycle),
+it propagates 50% of the cycle's self and descendant
+time to \s-1EXAMPLE\s+1's descendant time field.
+Finally each name is followed by an index that shows
+where on the listing to find the entry for that routine.
+.sh 1 "Using the Profiles"
+.pp
+The profiler is a useful tool for improving
+a set of routines that implement an abstraction.
+It can be helpful in identifying poorly coded routines,
+and in evaluating the new algorithms and code that replace them.
+Taking full advantage of the profiler
+requires a careful examination of the call graph profile,
+and a thorough knowledge of the abstractions underlying
+the program.
+.pp
+The easiest optimization that can be performed
+is a small change
+to a control construct or data structure that improves the
+running time of the program.
+An obvious starting point
+is a routine that is called many times.
+For example, suppose an output
+routine is the only parent
+of a routine that formats the data.
+If this format routine is expanded inline in the
+output routine, the overhead of a function call and
+return can be saved for each datum that needs to be formatted.
+.pp
+The drawback to inline expansion is that the data abstractions
+in the program may become less parameterized,
+hence less clearly defined.
+The profiling will also become less useful since the loss of
+routines will make its output more granular.
+For example,
+if the symbol table functions ``lookup'', ``insert'', and ``delete''
+are all merged into a single parameterized routine,
+it will be impossible to determine the costs
+of any one of these individual functions from the profile.
+.pp
+Further potential for optimization lies in routines that
+implement data abstractions whose total execution
+time is long.
+For example, a lookup routine might be called only a few
+times, but use an inefficient linear search algorithm,
+that might be replaced with a binary search.
+Alternately, the discovery that a rehashing function is being
+called excessively, can lead to a different
+hash function or a larger hash table.
+If the data abstraction function cannot easily be speeded up,
+it may be advantageous to cache its results,
+and eliminate the need to rerun
+it for identical inputs.
+These and other ideas for program improvement are discussed in
+[Bentley81].
+.pp
+This tool is best used in an iterative approach:
+profiling the program,
+eliminating one bottleneck,
+then finding some other part of the program
+that begins to dominate execution time.
+For instance, we have used \fBgprof\fR on itself;
+eliminating, rewriting, and inline expanding routines,
+until reading
+data files (hardly a target for optimization!)
+represents the dominating factor in its execution time.
+.pp
+Certain types of programs are not easily analyzed by \fBgprof\fR.
+They are typified by programs that exhibit a large degree of
+recursion, such as recursive descent compilers.
+The problem is that most of the major routines are grouped
+into a single monolithic cycle.
+As in the symbol table abstraction that is placed
+in one routine,
+it is impossible to distinguish which members of the cycle are
+responsible for the execution time.
+Unfortunately there are no easy modifications to these programs that
+make them amenable to analysis.
+.pp
+A completely different use of the profiler is to analyze the control
+flow of an unfamiliar program.
+If you receive a program from another user that you need to modify
+in some small way,
+it is often unclear where the changes need to be made.
+By running the program on an example and then using \fBgprof\fR,
+you can get a view of the structure of the program.
+.pp
+Consider an example in which you need to change the output format
+of the program.
+For purposes of this example suppose that the call graph
+of the output portion of the program has the following structure:
+.(b
+.so pres2.pic
+.)b
+Initially you look through the \fBgprof\fR
+output for the system call ``\s-1WRITE\s+1''.
+The format routine you will need to change is probably
+among the parents of the ``\s-1WRITE\s+1'' procedure.
+The next step is to look at the profile entry for each
+of parents of ``\s-1WRITE\s+1'',
+in this example either ``\s-1FORMAT1\s+1'' or ``\s-1FORMAT2\s+1'',
+to determine which one to change.
+Each format routine will have one or more parents,
+in this example ``\s-1CALC1\s+1'', ``\s-1CALC2\s+1'', and ``\s-1CALC3\s+1''.
+By inspecting the source code for each of these routines
+you can determine which format routine generates the output that
+you wish to modify.
+Since the \fBgprof\fR entry shows all the
+potential calls to the format routine you intend to change,
+you can determine if your modifications will affect output that
+should be left alone.
+If you desire to change the output of ``\s-1CALC2\s+1'', but not ``\s-1CALC3\s+1'',
+then formatting routine ``\s-1FORMAT2\s+1'' needs to be split
+into two separate routines,
+one of which implements the new format.
+You can then retarget just the call by ``\s-1CALC2\s+1''
+that needs the new format.
+It should be noted that the static call information is particularly
+useful here since the test case you run probably will not
+exercise the entire program.
+.sh 1 "Conclusions"
+.pp
+We have created a profiler that aids in the evaluation
+of modular programs.
+For each routine in the program,
+the profile shows the extent to which that routine
+helps support various abstractions,
+and how that routine uses other abstractions.
+The profile accurately assesses the cost of routines
+at all levels of the program decomposition.
+The profiler is easily used,
+and can be compiled into the program without any prior planning by
+the programmer.
+It adds only five to thirty percent execution overhead to the program
+being profiled,
+produces no additional output until after the program finishes,
+and allows the program to be measured in its actual environment.
+Finally, the profiler runs on a time-sharing system
+using only the normal services provided by the operating system
+and compilers.
diff --git a/usr.bin/gprof/PSD.doc/profiling.me b/usr.bin/gprof/PSD.doc/profiling.me
new file mode 100644
index 0000000..227aedf
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/profiling.me
@@ -0,0 +1,115 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)profiling.me 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Types of Profiling"
+.pp
+There are several different uses for program profiles,
+and each may require different information from the profiles,
+or different presentation of the information.
+We distinguish two broad categories of profiles:
+those that present counts of statement or routine invocations,
+and those that display timing information about statements
+or routines.
+Counts are typically presented in tabular form,
+often in parallel with a listing of the source code.
+Timing information could be similarly presented;
+but more than one measure of time might be associated with each
+statement or routine.
+For example,
+in the framework used by \fBgprof\fP
+each profiled segment would display two times:
+one for the time used by the segment itself, and another for the
+time inherited from code segments it invokes.
+.pp
+Execution counts are used in many different contexts.
+The exact number of times a routine or statement is activated
+can be used to determine if an algorithm is performing as
+expected.
+Cursory inspection of such counters may show algorithms whose
+complexity is unsuited to the task at hand.
+Careful interpretation of counters can often suggest
+improvements to acceptable algorithms.
+Precise examination can uncover subtle errors in an
+algorithm.
+At this level, profiling counters are similar to
+debugging statements whose purpose is to show the number of times
+a piece of code is executed.
+Another view of such counters is as boolean values.
+One may be interested that a portion of code has executed at
+all, for exhaustive testing, or to check that one implementation
+of an abstraction completely replaces a previous one.
+.pp
+Execution counts are not necessarily proportional to the amount
+of time required to execute the routine or statement.
+Further, the execution time of a routine will not be the same for
+all calls on the routine.
+The criteria for establishing execution time
+must be decided.
+If a routine implements an abstraction by invoking other abstractions,
+the time spent in the routine will not accurately reflect the
+time required by the abstraction it implements.
+Similarly, if an abstraction is implemented by several
+routines the time required by the abstraction will be distributed
+across those routines.
+.pp
+Given the execution time of individual routines,
+\fBgprof\fP accounts to each routine the time spent
+for it by the routines it invokes.
+This accounting is done by assembling a \fIcall graph\fP with nodes that
+are the routines of the program and directed arcs that represent
+calls from call sites to routines.
+We distinguish among three different call graphs for a program.
+The \fIcomplete call graph\fP incorporates all routines and all
+potential arcs,
+including arcs that represent calls to functional parameters
+or functional variables.
+This graph contains the other two graphs as subgraphs.
+The \fIstatic call graph\fP includes all routines and all possible arcs
+that are not calls to functional parameters or variables.
+The \fIdynamic call graph\fP includes only those routines and
+arcs traversed by the profiled execution of the program.
+This graph need not include all routines, nor need it include all
+potential arcs between the routines it covers.
+It may, however, include arcs to functional parameters or
+variables that the static call graph may omit.
+The static call graph can be determined from the (static) program text.
+The dynamic call graph is determined only by profiling an
+execution of the program.
+The complete call graph for a monolithic program could be determined
+by data flow analysis techniques.
+The complete call graph for programs that change
+during execution, by modifying themselves or dynamically loading
+or overlaying code, may never be determinable.
+Both the static call graph and the dynamic call graph are used
+by \fBgprof\fP, but it does not search for the complete call
+graph.
diff --git a/usr.bin/gprof/PSD.doc/refs.me b/usr.bin/gprof/PSD.doc/refs.me
new file mode 100644
index 0000000..580d080
--- /dev/null
+++ b/usr.bin/gprof/PSD.doc/refs.me
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1982, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)refs.me 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "References"
+.ls 1
+.ip [Bentley81]
+Bentley, J. L.,
+``Writing Efficient Code'',
+Department of Computer Science,
+Carnegie-Mellon University,
+Pittsburgh, Pennsylvania,
+CMU-CS-81-116, 1981.
+.ip [Graham82]
+Graham, S. L., Henry, R. R., Schulman, R. A.,
+``An Experiment in Table Driven Code Generation'',
+SIGPLAN '82 Symposium on Compiler Construction,
+June, 1982.
+.ip [Joy79]
+Joy, W. N., Graham, S. L., Haley, C. B. ``Berkeley Pascal User's Manual'',
+Version 1.1, Computer Science Division
+University of California, Berkeley, CA. April 1979.
+.ip [Knuth71]
+Knuth, D. E. ``An empirical study of FORTRAN programs'',
+Software - Practice and Experience, 1, 105-133. 1971
+.ip [Satterthwaite72]
+Satterthwaite, E. ``Debugging Tools for High Level Languages'',
+Software - Practice and Experience, 2, 197-217, 1972
+.ip [Tarjan72]
+Tarjan, R. E., ``Depth first search and linear graph algorithm,''
+\fISIAM J. Computing\fP \fB1\fP:2, 146-160, 1972.
+.ip [Unix]
+Unix Programmer's Manual, ``\fBprof\fR command'', section 1,
+Bell Laboratories, Murray Hill, NJ. January 1979.
diff --git a/usr.bin/gprof/amd64.c b/usr.bin/gprof/amd64.c
new file mode 100644
index 0000000..6a47408
--- /dev/null
+++ b/usr.bin/gprof/amd64.c
@@ -0,0 +1,11 @@
+#include "gprof.h"
+
+/*
+ * gprof -c isn't currently supported...
+ */
+findcall( parentp , p_lowpc , p_highpc )
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+}
diff --git a/usr.bin/gprof/amd64.h b/usr.bin/gprof/amd64.h
new file mode 100644
index 0000000..067e019
--- /dev/null
+++ b/usr.bin/gprof/amd64.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)i386.h 8.1 (Berkeley) 6/6/93
+ */
+
+ /*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 0
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+enum opermodes { dummy };
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/arcs.c b/usr.bin/gprof/arcs.c
new file mode 100644
index 0000000..2c22469
--- /dev/null
+++ b/usr.bin/gprof/arcs.c
@@ -0,0 +1,950 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arcs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+#ifdef DEBUG
+int visited;
+int viable;
+int newcycle;
+int oldcycle;
+#endif DEBUG
+
+ /*
+ * add (or just increment) an arc
+ */
+addarc( parentp , childp , count )
+ nltype *parentp;
+ nltype *childp;
+ long count;
+{
+ arctype *arcp;
+
+# ifdef DEBUG
+ if ( debug & TALLYDEBUG ) {
+ printf( "[addarc] %d arcs from %s to %s\n" ,
+ count , parentp -> name , childp -> name );
+ }
+# endif DEBUG
+ arcp = arclookup( parentp , childp );
+ if ( arcp != 0 ) {
+ /*
+ * a hit: just increment the count.
+ */
+# ifdef DEBUG
+ if ( debug & TALLYDEBUG ) {
+ printf( "[tally] hit %d += %d\n" ,
+ arcp -> arc_count , count );
+ }
+# endif DEBUG
+ arcp -> arc_count += count;
+ return;
+ }
+ arcp = (arctype *)calloc( 1 , sizeof *arcp );
+ arcp -> arc_parentp = parentp;
+ arcp -> arc_childp = childp;
+ arcp -> arc_count = count;
+ /*
+ * prepend this child to the children of this parent
+ */
+ arcp -> arc_childlist = parentp -> children;
+ parentp -> children = arcp;
+ /*
+ * prepend this parent to the parents of this child
+ */
+ arcp -> arc_parentlist = childp -> parents;
+ childp -> parents = arcp;
+}
+
+ /*
+ * the code below topologically sorts the graph (collapsing cycles),
+ * and propagates time bottom up and flags top down.
+ */
+
+ /*
+ * the topologically sorted name list pointers
+ */
+nltype **topsortnlp;
+
+topcmp( npp1 , npp2 )
+ nltype **npp1;
+ nltype **npp2;
+{
+ return (*npp1) -> toporder - (*npp2) -> toporder;
+}
+
+nltype **
+doarcs()
+{
+ nltype *parentp, **timesortnlp;
+ arctype *arcp;
+ long index;
+ long pass;
+
+ /*
+ * initialize various things:
+ * zero out child times.
+ * count self-recursive calls.
+ * indicate that nothing is on cycles.
+ */
+ for ( parentp = nl ; parentp < npe ; parentp++ ) {
+ parentp -> childtime = 0.0;
+ arcp = arclookup( parentp , parentp );
+ if ( arcp != 0 ) {
+ parentp -> ncall -= arcp -> arc_count;
+ parentp -> selfcalls = arcp -> arc_count;
+ } else {
+ parentp -> selfcalls = 0;
+ }
+ parentp -> npropcall = parentp -> ncall;
+ parentp -> propfraction = 0.0;
+ parentp -> propself = 0.0;
+ parentp -> propchild = 0.0;
+ parentp -> printflag = FALSE;
+ parentp -> toporder = DFN_NAN;
+ parentp -> cycleno = 0;
+ parentp -> cyclehead = parentp;
+ parentp -> cnext = 0;
+ if ( cflag ) {
+ findcall( parentp , parentp -> value , (parentp+1) -> value );
+ }
+ }
+ for ( pass = 1 ; ; pass++ ) {
+ /*
+ * topologically order things
+ * if any node is unnumbered,
+ * number it and any of its descendents.
+ */
+ for ( dfn_init() , parentp = nl ; parentp < npe ; parentp++ ) {
+ if ( parentp -> toporder == DFN_NAN ) {
+ dfn( parentp );
+ }
+ }
+ /*
+ * link together nodes on the same cycle
+ */
+ cyclelink();
+ /*
+ * if no cycles to break up, proceed
+ */
+ if ( ! Cflag )
+ break;
+ /*
+ * analyze cycles to determine breakup
+ */
+# ifdef DEBUG
+ if ( debug & BREAKCYCLE ) {
+ printf("[doarcs] pass %d, cycle(s) %d\n" , pass , ncycle );
+ }
+# endif DEBUG
+ if ( pass == 1 ) {
+ printf( "\n\n%s %s\n%s %d:\n" ,
+ "The following arcs were deleted" ,
+ "from the propagation calculation" ,
+ "to reduce the maximum cycle size to", cyclethreshold );
+ }
+ if ( cycleanalyze() )
+ break;
+ free ( cyclenl );
+ ncycle = 0;
+ for ( parentp = nl ; parentp < npe ; parentp++ ) {
+ parentp -> toporder = DFN_NAN;
+ parentp -> cycleno = 0;
+ parentp -> cyclehead = parentp;
+ parentp -> cnext = 0;
+ }
+ }
+ if ( pass > 1 ) {
+ printf( "\f\n" );
+ } else {
+ printf( "\tNone\n\n" );
+ }
+ /*
+ * Sort the symbol table in reverse topological order
+ */
+ topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) );
+ if ( topsortnlp == (nltype **) 0 ) {
+ fprintf( stderr , "[doarcs] ran out of memory for topo sorting\n" );
+ }
+ for ( index = 0 ; index < nname ; index += 1 ) {
+ topsortnlp[ index ] = &nl[ index ];
+ }
+ qsort( topsortnlp , nname , sizeof(nltype *) , topcmp );
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[doarcs] topological sort listing\n" );
+ for ( index = 0 ; index < nname ; index += 1 ) {
+ printf( "[doarcs] " );
+ printf( "%d:" , topsortnlp[ index ] -> toporder );
+ printname( topsortnlp[ index ] );
+ printf( "\n" );
+ }
+ }
+# endif DEBUG
+ /*
+ * starting from the topological top,
+ * propagate print flags to children.
+ * also, calculate propagation fractions.
+ * this happens before time propagation
+ * since time propagation uses the fractions.
+ */
+ doflags();
+ /*
+ * starting from the topological bottom,
+ * propogate children times up to parents.
+ */
+ dotime();
+ /*
+ * Now, sort by propself + propchild.
+ * sorting both the regular function names
+ * and cycle headers.
+ */
+ timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
+ if ( timesortnlp == (nltype **) 0 ) {
+ fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
+ }
+ for ( index = 0 ; index < nname ; index++ ) {
+ timesortnlp[index] = &nl[index];
+ }
+ for ( index = 1 ; index <= ncycle ; index++ ) {
+ timesortnlp[nname+index-1] = &cyclenl[index];
+ }
+ qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp );
+ for ( index = 0 ; index < nname + ncycle ; index++ ) {
+ timesortnlp[ index ] -> index = index + 1;
+ }
+ return( timesortnlp );
+}
+
+dotime()
+{
+ int index;
+
+ cycletime();
+ for ( index = 0 ; index < nname ; index += 1 ) {
+ timepropagate( topsortnlp[ index ] );
+ }
+}
+
+timepropagate( parentp )
+ nltype *parentp;
+{
+ arctype *arcp;
+ nltype *childp;
+ double share;
+ double propshare;
+
+ if ( parentp -> propfraction == 0.0 ) {
+ return;
+ }
+ /*
+ * gather time from children of this parent.
+ */
+ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+ childp = arcp -> arc_childp;
+ if ( arcp -> arc_flags & DEADARC ) {
+ continue;
+ }
+ if ( arcp -> arc_count == 0 ) {
+ continue;
+ }
+ if ( childp == parentp ) {
+ continue;
+ }
+ if ( childp -> propfraction == 0.0 ) {
+ continue;
+ }
+ if ( childp -> cyclehead != childp ) {
+ if ( parentp -> cycleno == childp -> cycleno ) {
+ continue;
+ }
+ if ( parentp -> toporder <= childp -> toporder ) {
+ fprintf( stderr , "[propagate] toporder botches\n" );
+ }
+ childp = childp -> cyclehead;
+ } else {
+ if ( parentp -> toporder <= childp -> toporder ) {
+ fprintf( stderr , "[propagate] toporder botches\n" );
+ continue;
+ }
+ }
+ if ( childp -> npropcall == 0 ) {
+ continue;
+ }
+ /*
+ * distribute time for this arc
+ */
+ arcp -> arc_time = childp -> time
+ * ( ( (double) arcp -> arc_count ) /
+ ( (double) childp -> npropcall ) );
+ arcp -> arc_childtime = childp -> childtime
+ * ( ( (double) arcp -> arc_count ) /
+ ( (double) childp -> npropcall ) );
+ share = arcp -> arc_time + arcp -> arc_childtime;
+ parentp -> childtime += share;
+ /*
+ * ( 1 - propfraction ) gets lost along the way
+ */
+ propshare = parentp -> propfraction * share;
+ /*
+ * fix things for printing
+ */
+ parentp -> propchild += propshare;
+ arcp -> arc_time *= parentp -> propfraction;
+ arcp -> arc_childtime *= parentp -> propfraction;
+ /*
+ * add this share to the parent's cycle header, if any.
+ */
+ if ( parentp -> cyclehead != parentp ) {
+ parentp -> cyclehead -> childtime += share;
+ parentp -> cyclehead -> propchild += propshare;
+ }
+# ifdef DEBUG
+ if ( debug & PROPDEBUG ) {
+ printf( "[dotime] child \t" );
+ printname( childp );
+ printf( " with %f %f %d/%d\n" ,
+ childp -> time , childp -> childtime ,
+ arcp -> arc_count , childp -> npropcall );
+ printf( "[dotime] parent\t" );
+ printname( parentp );
+ printf( "\n[dotime] share %f\n" , share );
+ }
+# endif DEBUG
+ }
+}
+
+cyclelink()
+{
+ register nltype *nlp;
+ register nltype *cyclenlp;
+ int cycle;
+ nltype *memberp;
+ arctype *arcp;
+
+ /*
+ * Count the number of cycles, and initialze the cycle lists
+ */
+ ncycle = 0;
+ for ( nlp = nl ; nlp < npe ; nlp++ ) {
+ /*
+ * this is how you find unattached cycles
+ */
+ if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) {
+ ncycle += 1;
+ }
+ }
+ /*
+ * cyclenl is indexed by cycle number:
+ * i.e. it is origin 1, not origin 0.
+ */
+ cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) );
+ if ( cyclenl == 0 ) {
+ fprintf( stderr , "%s: No room for %d bytes of cycle headers\n" ,
+ whoami , ( ncycle + 1 ) * sizeof( nltype ) );
+ done();
+ }
+ /*
+ * now link cycles to true cycleheads,
+ * number them, accumulate the data for the cycle
+ */
+ cycle = 0;
+ for ( nlp = nl ; nlp < npe ; nlp++ ) {
+ if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) {
+ continue;
+ }
+ cycle += 1;
+ cyclenlp = &cyclenl[cycle];
+ cyclenlp -> name = 0; /* the name */
+ cyclenlp -> value = 0; /* the pc entry point */
+ cyclenlp -> time = 0.0; /* ticks in this routine */
+ cyclenlp -> childtime = 0.0; /* cumulative ticks in children */
+ cyclenlp -> ncall = 0; /* how many times called */
+ cyclenlp -> selfcalls = 0; /* how many calls to self */
+ cyclenlp -> propfraction = 0.0; /* what % of time propagates */
+ cyclenlp -> propself = 0.0; /* how much self time propagates */
+ cyclenlp -> propchild = 0.0; /* how much child time propagates */
+ cyclenlp -> printflag = TRUE; /* should this be printed? */
+ cyclenlp -> index = 0; /* index in the graph list */
+ cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */
+ cyclenlp -> cycleno = cycle; /* internal number of cycle on */
+ cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */
+ cyclenlp -> cnext = nlp; /* pointer to next member of cycle */
+ cyclenlp -> parents = 0; /* list of caller arcs */
+ cyclenlp -> children = 0; /* list of callee arcs */
+# ifdef DEBUG
+ if ( debug & CYCLEDEBUG ) {
+ printf( "[cyclelink] " );
+ printname( nlp );
+ printf( " is the head of cycle %d\n" , cycle );
+ }
+# endif DEBUG
+ /*
+ * link members to cycle header
+ */
+ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
+ memberp -> cycleno = cycle;
+ memberp -> cyclehead = cyclenlp;
+ }
+ /*
+ * count calls from outside the cycle
+ * and those among cycle members
+ */
+ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
+ for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) {
+ if ( arcp -> arc_parentp == memberp ) {
+ continue;
+ }
+ if ( arcp -> arc_parentp -> cycleno == cycle ) {
+ cyclenlp -> selfcalls += arcp -> arc_count;
+ } else {
+ cyclenlp -> npropcall += arcp -> arc_count;
+ }
+ }
+ }
+ }
+}
+
+ /*
+ * analyze cycles to determine breakup
+ */
+cycleanalyze()
+{
+ arctype **cyclestack;
+ arctype **stkp;
+ arctype **arcpp;
+ arctype **endlist;
+ arctype *arcp;
+ nltype *nlp;
+ cltype *clp;
+ bool ret;
+ bool done;
+ int size;
+ int cycleno;
+
+ /*
+ * calculate the size of the cycle, and find nodes that
+ * exit the cycle as they are desirable targets to cut
+ * some of their parents
+ */
+ for ( done = TRUE , cycleno = 1 ; cycleno <= ncycle ; cycleno++ ) {
+ size = 0;
+ for (nlp = cyclenl[ cycleno ] . cnext; nlp; nlp = nlp -> cnext) {
+ size += 1;
+ nlp -> parentcnt = 0;
+ nlp -> flags &= ~HASCYCLEXIT;
+ for ( arcp = nlp -> parents; arcp; arcp = arcp -> arc_parentlist ) {
+ nlp -> parentcnt += 1;
+ if ( arcp -> arc_parentp -> cycleno != cycleno )
+ nlp -> flags |= HASCYCLEXIT;
+ }
+ }
+ if ( size <= cyclethreshold )
+ continue;
+ done = FALSE;
+ cyclestack = (arctype **) calloc( size + 1 , sizeof( arctype *) );
+ if ( cyclestack == 0 ) {
+ fprintf( stderr , "%s: No room for %d bytes of cycle stack\n" ,
+ whoami , ( size + 1 ) * sizeof( arctype * ) );
+ return;
+ }
+# ifdef DEBUG
+ if ( debug & BREAKCYCLE ) {
+ printf( "[cycleanalyze] starting cycle %d of %d, size %d\n" ,
+ cycleno , ncycle , size );
+ }
+# endif DEBUG
+ for ( nlp = cyclenl[ cycleno ] . cnext ; nlp ; nlp = nlp -> cnext ) {
+ stkp = &cyclestack[0];
+ nlp -> flags |= CYCLEHEAD;
+ ret = descend ( nlp , cyclestack , stkp );
+ nlp -> flags &= ~CYCLEHEAD;
+ if ( ret == FALSE )
+ break;
+ }
+ free( cyclestack );
+ if ( cyclecnt > 0 ) {
+ compresslist();
+ for ( clp = cyclehead ; clp ; ) {
+ endlist = &clp -> list[ clp -> size ];
+ for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ )
+ (*arcpp) -> arc_cyclecnt--;
+ cyclecnt--;
+ clp = clp -> next;
+ free( clp );
+ }
+ cyclehead = 0;
+ }
+ }
+# ifdef DEBUG
+ if ( debug & BREAKCYCLE ) {
+ printf("%s visited %d, viable %d, newcycle %d, oldcycle %d\n",
+ "[doarcs]" , visited , viable , newcycle , oldcycle);
+ }
+# endif DEBUG
+ return( done );
+}
+
+descend( node , stkstart , stkp )
+ nltype *node;
+ arctype **stkstart;
+ arctype **stkp;
+{
+ arctype *arcp;
+ bool ret;
+
+ for ( arcp = node -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+# ifdef DEBUG
+ visited++;
+# endif DEBUG
+ if ( arcp -> arc_childp -> cycleno != node -> cycleno
+ || ( arcp -> arc_childp -> flags & VISITED )
+ || ( arcp -> arc_flags & DEADARC ) )
+ continue;
+# ifdef DEBUG
+ viable++;
+# endif DEBUG
+ *stkp = arcp;
+ if ( arcp -> arc_childp -> flags & CYCLEHEAD ) {
+ if ( addcycle( stkstart , stkp ) == FALSE )
+ return( FALSE );
+ continue;
+ }
+ arcp -> arc_childp -> flags |= VISITED;
+ ret = descend( arcp -> arc_childp , stkstart , stkp + 1 );
+ arcp -> arc_childp -> flags &= ~VISITED;
+ if ( ret == FALSE )
+ return( FALSE );
+ }
+}
+
+addcycle( stkstart , stkend )
+ arctype **stkstart;
+ arctype **stkend;
+{
+ arctype **arcpp;
+ arctype **stkloc;
+ arctype **stkp;
+ arctype **endlist;
+ arctype *minarc;
+ arctype *arcp;
+ cltype *clp;
+ int size;
+
+ size = stkend - stkstart + 1;
+ if ( size <= 1 )
+ return( TRUE );
+ for ( arcpp = stkstart , minarc = *arcpp ; arcpp <= stkend ; arcpp++ ) {
+ if ( *arcpp > minarc )
+ continue;
+ minarc = *arcpp;
+ stkloc = arcpp;
+ }
+ for ( clp = cyclehead ; clp ; clp = clp -> next ) {
+ if ( clp -> size != size )
+ continue;
+ stkp = stkloc;
+ endlist = &clp -> list[ size ];
+ for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) {
+ if ( *stkp++ != *arcpp )
+ break;
+ if ( stkp > stkend )
+ stkp = stkstart;
+ }
+ if ( arcpp == endlist ) {
+# ifdef DEBUG
+ oldcycle++;
+# endif DEBUG
+ return( TRUE );
+ }
+ }
+ clp = (cltype *)
+ calloc( 1 , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) );
+ if ( clp == 0 ) {
+ fprintf( stderr , "%s: No room for %d bytes of subcycle storage\n" ,
+ whoami , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) );
+ return( FALSE );
+ }
+ stkp = stkloc;
+ endlist = &clp -> list[ size ];
+ for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) {
+ arcp = *arcpp = *stkp++;
+ if ( stkp > stkend )
+ stkp = stkstart;
+ arcp -> arc_cyclecnt++;
+ if ( ( arcp -> arc_flags & ONLIST ) == 0 ) {
+ arcp -> arc_flags |= ONLIST;
+ arcp -> arc_next = archead;
+ archead = arcp;
+ }
+ }
+ clp -> size = size;
+ clp -> next = cyclehead;
+ cyclehead = clp;
+# ifdef DEBUG
+ newcycle++;
+ if ( debug & SUBCYCLELIST ) {
+ printsubcycle( clp );
+ }
+# endif DEBUG
+ cyclecnt++;
+ if ( cyclecnt >= CYCLEMAX )
+ return( FALSE );
+ return( TRUE );
+}
+
+compresslist()
+{
+ cltype *clp;
+ cltype **prev;
+ arctype **arcpp;
+ arctype **endlist;
+ arctype *arcp;
+ arctype *maxarcp;
+ arctype *maxexitarcp;
+ arctype *maxwithparentarcp;
+ arctype *maxnoparentarcp;
+ int maxexitcnt;
+ int maxwithparentcnt;
+ int maxnoparentcnt;
+ char *type;
+
+ maxexitcnt = 0;
+ maxwithparentcnt = 0;
+ maxnoparentcnt = 0;
+ for ( endlist = &archead , arcp = archead ; arcp ; ) {
+ if ( arcp -> arc_cyclecnt == 0 ) {
+ arcp -> arc_flags &= ~ONLIST;
+ *endlist = arcp -> arc_next;
+ arcp -> arc_next = 0;
+ arcp = *endlist;
+ continue;
+ }
+ if ( arcp -> arc_childp -> flags & HASCYCLEXIT ) {
+ if ( arcp -> arc_cyclecnt > maxexitcnt ||
+ ( arcp -> arc_cyclecnt == maxexitcnt &&
+ arcp -> arc_cyclecnt < maxexitarcp -> arc_count ) ) {
+ maxexitcnt = arcp -> arc_cyclecnt;
+ maxexitarcp = arcp;
+ }
+ } else if ( arcp -> arc_childp -> parentcnt > 1 ) {
+ if ( arcp -> arc_cyclecnt > maxwithparentcnt ||
+ ( arcp -> arc_cyclecnt == maxwithparentcnt &&
+ arcp -> arc_cyclecnt < maxwithparentarcp -> arc_count ) ) {
+ maxwithparentcnt = arcp -> arc_cyclecnt;
+ maxwithparentarcp = arcp;
+ }
+ } else {
+ if ( arcp -> arc_cyclecnt > maxnoparentcnt ||
+ ( arcp -> arc_cyclecnt == maxnoparentcnt &&
+ arcp -> arc_cyclecnt < maxnoparentarcp -> arc_count ) ) {
+ maxnoparentcnt = arcp -> arc_cyclecnt;
+ maxnoparentarcp = arcp;
+ }
+ }
+ endlist = &arcp -> arc_next;
+ arcp = arcp -> arc_next;
+ }
+ if ( maxexitcnt > 0 ) {
+ /*
+ * first choice is edge leading to node with out-of-cycle parent
+ */
+ maxarcp = maxexitarcp;
+# ifdef DEBUG
+ type = "exit";
+# endif DEBUG
+ } else if ( maxwithparentcnt > 0 ) {
+ /*
+ * second choice is edge leading to node with at least one
+ * other in-cycle parent
+ */
+ maxarcp = maxwithparentarcp;
+# ifdef DEBUG
+ type = "internal";
+# endif DEBUG
+ } else {
+ /*
+ * last choice is edge leading to node with only this arc as
+ * a parent (as it will now be orphaned)
+ */
+ maxarcp = maxnoparentarcp;
+# ifdef DEBUG
+ type = "orphan";
+# endif DEBUG
+ }
+ maxarcp -> arc_flags |= DEADARC;
+ maxarcp -> arc_childp -> parentcnt -= 1;
+ maxarcp -> arc_childp -> npropcall -= maxarcp -> arc_count;
+# ifdef DEBUG
+ if ( debug & BREAKCYCLE ) {
+ printf( "%s delete %s arc: %s (%d) -> %s from %d cycle(s)\n" ,
+ "[compresslist]" , type , maxarcp -> arc_parentp -> name ,
+ maxarcp -> arc_count , maxarcp -> arc_childp -> name ,
+ maxarcp -> arc_cyclecnt );
+ }
+# endif DEBUG
+ printf( "\t%s to %s with %d calls\n" , maxarcp -> arc_parentp -> name ,
+ maxarcp -> arc_childp -> name , maxarcp -> arc_count );
+ prev = &cyclehead;
+ for ( clp = cyclehead ; clp ; ) {
+ endlist = &clp -> list[ clp -> size ];
+ for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ )
+ if ( (*arcpp) -> arc_flags & DEADARC )
+ break;
+ if ( arcpp == endlist ) {
+ prev = &clp -> next;
+ clp = clp -> next;
+ continue;
+ }
+ for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ )
+ (*arcpp) -> arc_cyclecnt--;
+ cyclecnt--;
+ *prev = clp -> next;
+ clp = clp -> next;
+ free( clp );
+ }
+}
+
+#ifdef DEBUG
+printsubcycle( clp )
+ cltype *clp;
+{
+ arctype **arcpp;
+ arctype **endlist;
+
+ arcpp = clp -> list;
+ printf( "%s <cycle %d>\n" , (*arcpp) -> arc_parentp -> name ,
+ (*arcpp) -> arc_parentp -> cycleno ) ;
+ for ( endlist = &clp -> list[ clp -> size ]; arcpp < endlist ; arcpp++ )
+ printf( "\t(%d) -> %s\n" , (*arcpp) -> arc_count ,
+ (*arcpp) -> arc_childp -> name ) ;
+}
+#endif DEBUG
+
+cycletime()
+{
+ int cycle;
+ nltype *cyclenlp;
+ nltype *childp;
+
+ for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) {
+ cyclenlp = &cyclenl[ cycle ];
+ for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) {
+ if ( childp -> propfraction == 0.0 ) {
+ /*
+ * all members have the same propfraction except those
+ * that were excluded with -E
+ */
+ continue;
+ }
+ cyclenlp -> time += childp -> time;
+ }
+ cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time;
+ }
+}
+
+ /*
+ * in one top to bottom pass over the topologically sorted namelist
+ * propagate:
+ * printflag as the union of parents' printflags
+ * propfraction as the sum of fractional parents' propfractions
+ * and while we're here, sum time for functions.
+ */
+doflags()
+{
+ int index;
+ nltype *childp;
+ nltype *oldhead;
+
+ oldhead = 0;
+ for ( index = nname-1 ; index >= 0 ; index -= 1 ) {
+ childp = topsortnlp[ index ];
+ /*
+ * if we haven't done this function or cycle,
+ * inherit things from parent.
+ * this way, we are linear in the number of arcs
+ * since we do all members of a cycle (and the cycle itself)
+ * as we hit the first member of the cycle.
+ */
+ if ( childp -> cyclehead != oldhead ) {
+ oldhead = childp -> cyclehead;
+ inheritflags( childp );
+ }
+# ifdef DEBUG
+ if ( debug & PROPDEBUG ) {
+ printf( "[doflags] " );
+ printname( childp );
+ printf( " inherits printflag %d and propfraction %f\n" ,
+ childp -> printflag , childp -> propfraction );
+ }
+# endif DEBUG
+ if ( ! childp -> printflag ) {
+ /*
+ * printflag is off
+ * it gets turned on by
+ * being on -f list,
+ * or there not being any -f list and not being on -e list.
+ */
+ if ( onlist( flist , childp -> name )
+ || ( !fflag && !onlist( elist , childp -> name ) ) ) {
+ childp -> printflag = TRUE;
+ }
+ } else {
+ /*
+ * this function has printing parents:
+ * maybe someone wants to shut it up
+ * by putting it on -e list. (but favor -f over -e)
+ */
+ if ( ( !onlist( flist , childp -> name ) )
+ && onlist( elist , childp -> name ) ) {
+ childp -> printflag = FALSE;
+ }
+ }
+ if ( childp -> propfraction == 0.0 ) {
+ /*
+ * no parents to pass time to.
+ * collect time from children if
+ * its on -F list,
+ * or there isn't any -F list and its not on -E list.
+ */
+ if ( onlist( Flist , childp -> name )
+ || ( !Fflag && !onlist( Elist , childp -> name ) ) ) {
+ childp -> propfraction = 1.0;
+ }
+ } else {
+ /*
+ * it has parents to pass time to,
+ * but maybe someone wants to shut it up
+ * by puttting it on -E list. (but favor -F over -E)
+ */
+ if ( !onlist( Flist , childp -> name )
+ && onlist( Elist , childp -> name ) ) {
+ childp -> propfraction = 0.0;
+ }
+ }
+ childp -> propself = childp -> time * childp -> propfraction;
+ printtime += childp -> propself;
+# ifdef DEBUG
+ if ( debug & PROPDEBUG ) {
+ printf( "[doflags] " );
+ printname( childp );
+ printf( " ends up with printflag %d and propfraction %f\n" ,
+ childp -> printflag , childp -> propfraction );
+ printf( "time %f propself %f printtime %f\n" ,
+ childp -> time , childp -> propself , printtime );
+ }
+# endif DEBUG
+ }
+}
+
+ /*
+ * check if any parent of this child
+ * (or outside parents of this cycle)
+ * have their print flags on and set the
+ * print flag of the child (cycle) appropriately.
+ * similarly, deal with propagation fractions from parents.
+ */
+inheritflags( childp )
+ nltype *childp;
+{
+ nltype *headp;
+ arctype *arcp;
+ nltype *parentp;
+ nltype *memp;
+
+ headp = childp -> cyclehead;
+ if ( childp == headp ) {
+ /*
+ * just a regular child, check its parents
+ */
+ childp -> printflag = FALSE;
+ childp -> propfraction = 0.0;
+ for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) {
+ parentp = arcp -> arc_parentp;
+ if ( childp == parentp ) {
+ continue;
+ }
+ childp -> printflag |= parentp -> printflag;
+ /*
+ * if the child was never actually called
+ * (e.g. this arc is static (and all others are, too))
+ * no time propagates along this arc.
+ */
+ if ( arcp -> arc_flags & DEADARC ) {
+ continue;
+ }
+ if ( childp -> npropcall ) {
+ childp -> propfraction += parentp -> propfraction
+ * ( ( (double) arcp -> arc_count )
+ / ( (double) childp -> npropcall ) );
+ }
+ }
+ } else {
+ /*
+ * its a member of a cycle, look at all parents from
+ * outside the cycle
+ */
+ headp -> printflag = FALSE;
+ headp -> propfraction = 0.0;
+ for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) {
+ for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) {
+ if ( arcp -> arc_parentp -> cyclehead == headp ) {
+ continue;
+ }
+ parentp = arcp -> arc_parentp;
+ headp -> printflag |= parentp -> printflag;
+ /*
+ * if the cycle was never actually called
+ * (e.g. this arc is static (and all others are, too))
+ * no time propagates along this arc.
+ */
+ if ( arcp -> arc_flags & DEADARC ) {
+ continue;
+ }
+ if ( headp -> npropcall ) {
+ headp -> propfraction += parentp -> propfraction
+ * ( ( (double) arcp -> arc_count )
+ / ( (double) headp -> npropcall ) );
+ }
+ }
+ }
+ for ( memp = headp ; memp ; memp = memp -> cnext ) {
+ memp -> printflag = headp -> printflag;
+ memp -> propfraction = headp -> propfraction;
+ }
+ }
+}
diff --git a/usr.bin/gprof/dfn.c b/usr.bin/gprof/dfn.c
new file mode 100644
index 0000000..169a47f
--- /dev/null
+++ b/usr.bin/gprof/dfn.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dfn.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "gprof.h"
+
+#define DFN_DEPTH 100
+struct dfnstruct {
+ nltype *nlentryp;
+ int cycletop;
+};
+typedef struct dfnstruct dfntype;
+
+dfntype dfn_stack[ DFN_DEPTH ];
+int dfn_depth;
+
+int dfn_counter;
+
+dfn_init()
+{
+
+ dfn_depth = 0;
+ dfn_counter = DFN_NAN;
+}
+
+ /*
+ * given this parent, depth first number its children.
+ */
+dfn( parentp )
+ nltype *parentp;
+{
+ arctype *arcp;
+
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn] dfn(" );
+ printname( parentp );
+ printf( ")\n" );
+ }
+# endif DEBUG
+ /*
+ * if we're already numbered, no need to look any furthur.
+ */
+ if ( dfn_numbered( parentp ) ) {
+ return;
+ }
+ /*
+ * if we're already busy, must be a cycle
+ */
+ if ( dfn_busy( parentp ) ) {
+ dfn_findcycle( parentp );
+ return;
+ }
+ /*
+ * visit yourself before your children
+ */
+ dfn_pre_visit( parentp );
+ /*
+ * visit children
+ */
+ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+ if ( arcp -> arc_flags & DEADARC )
+ continue;
+ dfn( arcp -> arc_childp );
+ }
+ /*
+ * visit yourself after your children
+ */
+ dfn_post_visit( parentp );
+}
+
+ /*
+ * push a parent onto the stack and mark it busy
+ */
+dfn_pre_visit( parentp )
+ nltype *parentp;
+{
+
+ dfn_depth += 1;
+ if ( dfn_depth >= DFN_DEPTH ) {
+ fprintf( stderr , "[dfn] out of my depth (dfn_stack overflow)\n" );
+ exit( 1 );
+ }
+ dfn_stack[ dfn_depth ].nlentryp = parentp;
+ dfn_stack[ dfn_depth ].cycletop = dfn_depth;
+ parentp -> toporder = DFN_BUSY;
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_pre_visit]\t\t%d:" , dfn_depth );
+ printname( parentp );
+ printf( "\n" );
+ }
+# endif DEBUG
+}
+
+ /*
+ * are we already numbered?
+ */
+bool
+dfn_numbered( childp )
+ nltype *childp;
+{
+
+ return ( childp -> toporder != DFN_NAN && childp -> toporder != DFN_BUSY );
+}
+
+ /*
+ * are we already busy?
+ */
+bool
+dfn_busy( childp )
+ nltype *childp;
+{
+
+ if ( childp -> toporder == DFN_NAN ) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+ /*
+ * MISSING: an explanation
+ */
+dfn_findcycle( childp )
+ nltype *childp;
+{
+ int cycletop;
+ nltype *cycleheadp;
+ nltype *tailp;
+ int index;
+
+ for ( cycletop = dfn_depth ; cycletop > 0 ; cycletop -= 1 ) {
+ cycleheadp = dfn_stack[ cycletop ].nlentryp;
+ if ( childp == cycleheadp ) {
+ break;
+ }
+ if ( childp -> cyclehead != childp &&
+ childp -> cyclehead == cycleheadp ) {
+ break;
+ }
+ }
+ if ( cycletop <= 0 ) {
+ fprintf( stderr , "[dfn_findcycle] couldn't find head of cycle\n" );
+ exit( 1 );
+ }
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_findcycle] dfn_depth %d cycletop %d " ,
+ dfn_depth , cycletop );
+ printname( cycleheadp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ if ( cycletop == dfn_depth ) {
+ /*
+ * this is previous function, e.g. this calls itself
+ * sort of boring
+ */
+ dfn_self_cycle( childp );
+ } else {
+ /*
+ * glom intervening functions that aren't already
+ * glommed into this cycle.
+ * things have been glommed when their cyclehead field
+ * points to the head of the cycle they are glommed into.
+ */
+ for ( tailp = cycleheadp ; tailp -> cnext ; tailp = tailp -> cnext ) {
+ /* void: chase down to tail of things already glommed */
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_findcycle] tail " );
+ printname( tailp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ }
+ /*
+ * if what we think is the top of the cycle
+ * has a cyclehead field, then it's not really the
+ * head of the cycle, which is really what we want
+ */
+ if ( cycleheadp -> cyclehead != cycleheadp ) {
+ cycleheadp = cycleheadp -> cyclehead;
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_findcycle] new cyclehead " );
+ printname( cycleheadp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ }
+ for ( index = cycletop + 1 ; index <= dfn_depth ; index += 1 ) {
+ childp = dfn_stack[ index ].nlentryp;
+ if ( childp -> cyclehead == childp ) {
+ /*
+ * not yet glommed anywhere, glom it
+ * and fix any children it has glommed
+ */
+ tailp -> cnext = childp;
+ childp -> cyclehead = cycleheadp;
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_findcycle] glomming " );
+ printname( childp );
+ printf( " onto " );
+ printname( cycleheadp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ for ( tailp = childp ; tailp->cnext ; tailp = tailp->cnext ) {
+ tailp -> cnext -> cyclehead = cycleheadp;
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_findcycle] and its tail " );
+ printname( tailp -> cnext );
+ printf( " onto " );
+ printname( cycleheadp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ }
+ } else if ( childp -> cyclehead != cycleheadp /* firewall */ ) {
+ fprintf( stderr ,
+ "[dfn_busy] glommed, but not to cyclehead\n" );
+ }
+ }
+ }
+}
+
+ /*
+ * deal with self-cycles
+ * for lint: ARGSUSED
+ */
+dfn_self_cycle( parentp )
+ nltype *parentp;
+{
+ /*
+ * since we are taking out self-cycles elsewhere
+ * no need for the special case, here.
+ */
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_self_cycle] " );
+ printname( parentp );
+ printf( "\n" );
+ }
+# endif DEBUG
+}
+
+ /*
+ * visit a node after all its children
+ * [MISSING: an explanation]
+ * and pop it off the stack
+ */
+dfn_post_visit( parentp )
+ nltype *parentp;
+{
+ nltype *memberp;
+
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_post_visit]\t%d: " , dfn_depth );
+ printname( parentp );
+ printf( "\n" );
+ }
+# endif DEBUG
+ /*
+ * number functions and things in their cycles
+ * unless the function is itself part of a cycle
+ */
+ if ( parentp -> cyclehead == parentp ) {
+ dfn_counter += 1;
+ for ( memberp = parentp ; memberp ; memberp = memberp -> cnext ) {
+ memberp -> toporder = dfn_counter;
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_post_visit]\t\tmember " );
+ printname( memberp );
+ printf( " -> toporder = %d\n" , dfn_counter );
+ }
+# endif DEBUG
+ }
+ } else {
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "[dfn_post_visit]\t\tis part of a cycle\n" );
+ }
+# endif DEBUG
+ }
+ dfn_depth -= 1;
+}
diff --git a/usr.bin/gprof/gprof.1 b/usr.bin/gprof/gprof.1
new file mode 100644
index 0000000..c212656
--- /dev/null
+++ b/usr.bin/gprof/gprof.1
@@ -0,0 +1,292 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)gprof.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt GPROF 1
+.Os BSD 4.2
+.Sh NAME
+.Nm gprof
+.Nd display call graph profile data
+.Sh SYNOPSIS
+.Nm gprof
+.Op options
+.Op Ar a.out Op Ar gmon.out ...
+.Sh DESCRIPTION
+.Nm Gprof
+produces an execution profile of C, Pascal, or Fortran77 programs.
+The effect of called routines is incorporated in the profile of each caller.
+The profile data is taken from the call graph profile file
+.Pf ( Pa gmon.out
+default) which is created by programs
+that are compiled with the
+.Fl pg
+option of
+.Xr cc 1 ,
+.Xr pc 1 ,
+and
+.Xr f77 1 .
+The
+.Fl pg
+option also links in versions of the library routines
+that are compiled for profiling.
+.Nm Gprof
+reads the given object file (the default is
+.Pa a.out)
+and establishes the relation between it's symbol table
+and the call graph profile from
+.Pa gmon.out .
+If more than one profile file is specified,
+the
+.Nm gprof
+output shows the sum of the profile information in the given profile files.
+.Pp
+.Nm Gprof
+calculates the amount of time spent in each routine.
+Next, these times are propagated along the edges of the call graph.
+Cycles are discovered, and calls into a cycle are made to share the time
+of the cycle.
+The first listing shows the functions
+sorted according to the time they represent
+including the time of their call graph descendents.
+Below each function entry is shown its (direct) call graph children,
+and how their times are propagated to this function.
+A similar display above the function shows how this function's time and the
+time of its descendents is propagated to its (direct) call graph parents.
+.Pp
+Cycles are also shown, with an entry for the cycle as a whole and
+a listing of the members of the cycle and their contributions to the
+time and call counts of the cycle.
+.Pp
+Second, a flat profile is given,
+similar to that provided by
+.Xr prof 1 .
+This listing gives the total execution times, the call counts,
+the time in msec or usec the call spent in the routine itself, and
+the time in msec or usec the call spent in the routine itself including
+its descendents.
+.Pp
+Finally, an index of the function names is provided.
+.Pp
+The following options are available:
+.Bl -tag -width Fl
+.It Fl a
+Suppresses the printing of statically declared functions.
+If this option is given, all relevant information about the static function
+(e.g., time samples, calls to other functions, calls from other functions)
+belongs to the function loaded just before the static function in the
+.Pa a.out
+file.
+.It Fl b
+Suppresses the printing of a description of each field in the profile.
+.It Fl c
+The static call graph of the program is discovered by a heuristic
+that examines the text space of the object file.
+Static-only parents or children are shown
+with call counts of 0.
+.It Fl C Ar count
+Find a minimal set of arcs that can be broken to eliminate all cycles with
+.Ar count
+or more members.
+Caution: the algorithm used to break cycles is exponential,
+so using this option may cause
+.Nm gprof
+to run for a very long time.
+.It Fl e Ar name
+Suppresses the printing of the graph profile entry for routine
+.Ar name
+and all its descendants
+(unless they have other ancestors that aren't suppressed).
+More than one
+.Fl e
+option may be given.
+Only one
+.Ar name
+may be given with each
+.Fl e
+option.
+.It Fl E Ar name
+Suppresses the printing of the graph profile entry for routine
+.Ar name
+(and its descendants) as
+.Fl e ,
+above, and also excludes the time spent in
+.Ar name
+(and its descendants) from the total and percentage time computations.
+(For example,
+.Fl E
+.Ar mcount
+.Fl E
+.Ar mcleanup
+is the default.)
+.It Fl f Ar name
+Prints the graph profile entry of only the specified routine
+.Ar name
+and its descendants.
+More than one
+.Fl f
+option may be given.
+Only one
+.Ar name
+may be given with each
+.Fl f
+option.
+.It Fl F Ar name
+Prints the graph profile entry of only the routine
+.Ar name
+and its descendants (as
+.Fl f ,
+above) and also uses only the times of the printed routines
+in total time and percentage computations.
+More than one
+.Fl F
+option may be given.
+Only one
+.Ar name
+may be given with each
+.Fl F
+option.
+The
+.Fl F
+option
+overrides
+the
+.Fl E
+option.
+.It Fl k Ar fromname Ar toname
+Will delete any arcs from routine
+.Ar fromname
+to routine
+.Ar toname .
+This can be used to break undesired cycles.
+More than one
+.Fl k
+option may be given.
+Only one pair of routine names may be given with each
+.Fl k
+option.
+.It Fl l
+Suppresses the printing of the call-graph profile.
+.It Fl L
+Suppresses the printing of the flat profile.
+.It Fl s
+A profile file
+.Pa gmon.sum
+is produced that represents
+the sum of the profile information in all the specified profile files.
+This summary profile file may be given to later
+executions of gprof (probably also with a
+.Fl s )
+to accumulate profile data across several runs of an
+.Pa a.out
+file.
+.It Fl u
+Suppresses the printing of functions whose name does not begin with
+an underscore.
+All relevant information about such functions belongs to the
+(non-suppressed) function with the next lowest address.
+This is useful for eliminating "functions" that are just labels
+inside other functions.
+.It Fl z
+Displays routines that have zero usage (as shown by call counts
+and accumulated time).
+This is useful with the
+.Fl c
+option for discovering which routines were never called.
+.El
+.Sh FILES
+.Bl -tag -width gmon.sum -compact
+.It Pa a.out
+The namelist and text space.
+.It Pa gmon.out
+Dynamic call graph and profile.
+.It Pa gmon.sum
+Summarized dynamic call graph and profile.
+.El
+.Sh SEE ALSO
+.Xr monitor 3 ,
+.Xr profil 2 ,
+.Xr cc 1 ,
+.Xr prof 1
+.Rs
+.%T "An Execution Profiler for Modular Programs"
+.%A S. Graham
+.%A P. Kessler
+.%A M. McKusick
+.%J "Software - Practice and Experience"
+.%V 13
+.%P pp. 671-685
+.%D 1983
+.Re
+.Rs
+.%T "gprof: A Call Graph Execution Profiler"
+.%A S. Graham
+.%A P. Kessler
+.%A M. McKusick
+.%J "Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, SIGPLAN Notices"
+.%V 17
+.%N 6
+.%P pp. 120-126
+.%D June 1982
+.Re
+.Sh HISTORY
+The
+.Nm gprof
+profiler
+appeared in
+.Bx 4.2 .
+.Sh BUGS
+The granularity of the sampling is shown, but remains
+statistical at best.
+We assume that the time for each execution of a function
+can be expressed by the total time for the function divided
+by the number of times the function is called.
+Thus the time propagated along the call graph arcs to the function's
+parents is directly proportional to the number of times that
+arc is traversed.
+.Pp
+Parents that are not themselves profiled will have the time of
+their profiled children propagated to them, but they will appear
+to be spontaneously invoked in the call graph listing, and will
+not have their time propagated further.
+Similarly, signal catchers, even though profiled, will appear
+to be spontaneous (although for more obscure reasons).
+Any profiled children of signal catchers should have their times
+propagated properly, unless the signal catcher was invoked during
+the execution of the profiling routine, in which case all is lost.
+.Pp
+The profiled program must call
+.Xr exit 2
+or return normally for the profiling information to be saved
+in the
+.Pa gmon.out
+file.
diff --git a/usr.bin/gprof/gprof.c b/usr.bin/gprof/gprof.c
new file mode 100644
index 0000000..0dbb187
--- /dev/null
+++ b/usr.bin/gprof/gprof.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)gprof.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+char *whoami = "gprof";
+
+ /*
+ * things which get -E excluded by default.
+ */
+char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
+
+static struct gmonhdr gmonhdr;
+static bool uflag;
+static int lflag;
+static int Lflag;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char **sp;
+ nltype **timesortnlp;
+
+ --argc;
+ argv++;
+ debug = 0;
+ bflag = TRUE;
+ while ( *argv != 0 && **argv == '-' ) {
+ (*argv)++;
+ switch ( **argv ) {
+ case 'a':
+ aflag = TRUE;
+ break;
+ case 'b':
+ bflag = FALSE;
+ break;
+ case 'C':
+ Cflag = TRUE;
+ cyclethreshold = atoi( *++argv );
+ break;
+ case 'c':
+#if defined(vax) || defined(tahoe)
+ cflag = TRUE;
+#else
+ fprintf(stderr, "gprof: -c isn't supported on this architecture yet\n");
+ exit(1);
+#endif
+ break;
+ case 'd':
+ dflag = TRUE;
+ setlinebuf(stdout);
+ debug |= atoi( *++argv );
+ debug |= ANYDEBUG;
+# ifdef DEBUG
+ printf("[main] debug = %d\n", debug);
+# else not DEBUG
+ printf("%s: -d ignored\n", whoami);
+# endif DEBUG
+ break;
+ case 'E':
+ ++argv;
+ addlist( Elist , *argv );
+ Eflag = TRUE;
+ addlist( elist , *argv );
+ eflag = TRUE;
+ break;
+ case 'e':
+ addlist( elist , *++argv );
+ eflag = TRUE;
+ break;
+ case 'F':
+ ++argv;
+ addlist( Flist , *argv );
+ Fflag = TRUE;
+ addlist( flist , *argv );
+ fflag = TRUE;
+ break;
+ case 'f':
+ addlist( flist , *++argv );
+ fflag = TRUE;
+ break;
+ case 'k':
+ addlist( kfromlist , *++argv );
+ addlist( ktolist , *++argv );
+ kflag = TRUE;
+ break;
+ case 'l':
+ lflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ lflag = 0;
+ break;
+ case 's':
+ sflag = TRUE;
+ break;
+ case 'u':
+ uflag = TRUE;
+ break;
+ case 'z':
+ zflag = TRUE;
+ break;
+ }
+ argv++;
+ }
+ if ( *argv != 0 ) {
+ a_outname = *argv;
+ argv++;
+ } else {
+ a_outname = A_OUTNAME;
+ }
+ if ( *argv != 0 ) {
+ gmonname = *argv;
+ argv++;
+ } else {
+ gmonname = GMONNAME;
+ }
+ /*
+ * turn off default functions
+ */
+ for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
+ Eflag = TRUE;
+ addlist( Elist , *sp );
+ eflag = TRUE;
+ addlist( elist , *sp );
+ }
+ /*
+ * get information about a.out file.
+ */
+ getnfile();
+ /*
+ * get information about mon.out file(s).
+ */
+ do {
+ getpfile( gmonname );
+ if ( *argv != 0 ) {
+ gmonname = *argv;
+ }
+ } while ( *argv++ != 0 );
+ /*
+ * how many ticks per second?
+ * if we can't tell, report time in ticks.
+ */
+ if (hz == 0) {
+ hz = 1;
+ fprintf(stderr, "time is in ticks, not seconds\n");
+ }
+ /*
+ * dump out a gmon.sum file if requested
+ */
+ if ( sflag ) {
+ dumpsum( GMONSUM );
+ }
+ /*
+ * assign samples to procedures
+ */
+ asgnsamples();
+ /*
+ * assemble the dynamic profile
+ */
+ timesortnlp = doarcs();
+ /*
+ * print the dynamic profile
+ */
+ if(!lflag) {
+ printgprof( timesortnlp );
+ }
+ /*
+ * print the flat profile
+ */
+ if(!Lflag) {
+ printprof();
+ }
+ /*
+ * print the index
+ */
+ printindex();
+ done();
+}
+
+ /*
+ * Set up string and symbol tables from a.out.
+ * and optionally the text space.
+ * On return symbol table is sorted by value.
+ */
+getnfile()
+{
+ FILE *nfile;
+ int valcmp();
+
+ nfile = fopen( a_outname ,"r");
+ if (nfile == NULL) {
+ perror( a_outname );
+ done();
+ }
+ fread(&xbuf, 1, sizeof(xbuf), nfile);
+ if (N_BADMAG(xbuf)) {
+ fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
+ done();
+ }
+ getstrtab(nfile);
+ getsymtab(nfile);
+ gettextspace( nfile );
+ qsort(nl, nname, sizeof(nltype), valcmp);
+ fclose(nfile);
+# ifdef DEBUG
+ if ( debug & AOUTDEBUG ) {
+ register int j;
+
+ for (j = 0; j < nname; j++){
+ printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
+ }
+ }
+# endif DEBUG
+}
+
+getstrtab(nfile)
+ FILE *nfile;
+{
+
+ fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
+ if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
+ fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
+ whoami , a_outname );
+ done();
+ }
+ strtab = calloc(ssiz, 1);
+ if (strtab == NULL) {
+ fprintf(stderr, "%s: %s: no room for %d bytes of string table\n",
+ whoami , a_outname , ssiz);
+ done();
+ }
+ if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
+ fprintf(stderr, "%s: %s: error reading string table\n",
+ whoami , a_outname );
+ done();
+ }
+}
+
+ /*
+ * Read in symbol table
+ */
+getsymtab(nfile)
+ FILE *nfile;
+{
+ register long i;
+ int askfor;
+ struct nlist nbuf;
+
+ /* pass1 - count symbols */
+ fseek(nfile, (long)N_SYMOFF(xbuf), 0);
+ nname = 0;
+ for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
+ fread(&nbuf, sizeof(nbuf), 1, nfile);
+ if ( ! funcsymbol( &nbuf ) ) {
+ continue;
+ }
+ nname++;
+ }
+ if (nname == 0) {
+ fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
+ done();
+ }
+ askfor = nname + 1;
+ nl = (nltype *) calloc( askfor , sizeof(nltype) );
+ if (nl == 0) {
+ fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
+ whoami, askfor * sizeof(nltype) );
+ done();
+ }
+
+ /* pass2 - read symbols */
+ fseek(nfile, (long)N_SYMOFF(xbuf), 0);
+ npe = nl;
+ nname = 0;
+ for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
+ fread(&nbuf, sizeof(nbuf), 1, nfile);
+ if ( ! funcsymbol( &nbuf ) ) {
+# ifdef DEBUG
+ if ( debug & AOUTDEBUG ) {
+ printf( "[getsymtab] rejecting: 0x%x %s\n" ,
+ nbuf.n_type , strtab + nbuf.n_un.n_strx );
+ }
+# endif DEBUG
+ continue;
+ }
+ npe->value = nbuf.n_value;
+ npe->name = strtab+nbuf.n_un.n_strx;
+# ifdef DEBUG
+ if ( debug & AOUTDEBUG ) {
+ printf( "[getsymtab] %d %s 0x%08x\n" ,
+ nname , npe -> name , npe -> value );
+ }
+# endif DEBUG
+ npe++;
+ nname++;
+ }
+ npe->value = -1;
+}
+
+ /*
+ * read in the text space of an a.out file
+ */
+gettextspace( nfile )
+ FILE *nfile;
+{
+
+ if ( cflag == 0 ) {
+ return;
+ }
+ textspace = (u_char *) malloc( xbuf.a_text );
+ if ( textspace == 0 ) {
+ fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
+ whoami , xbuf.a_text );
+ fprintf( stderr , "can't do -c\n" );
+ return;
+ }
+ (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
+ if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
+ fprintf( stderr , "%s: couldn't read text space: " , whoami );
+ fprintf( stderr , "can't do -c\n" );
+ free( textspace );
+ textspace = 0;
+ return;
+ }
+}
+ /*
+ * information from a gmon.out file is in two parts:
+ * an array of sampling hits within pc ranges,
+ * and the arcs.
+ */
+getpfile(filename)
+ char *filename;
+{
+ FILE *pfile;
+ FILE *openpfile();
+ struct rawarc arc;
+
+ pfile = openpfile(filename);
+ readsamples(pfile);
+ /*
+ * the rest of the file consists of
+ * a bunch of <from,self,count> tuples.
+ */
+ while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
+# ifdef DEBUG
+ if ( debug & SAMPLEDEBUG ) {
+ printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
+ arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
+ }
+# endif DEBUG
+ /*
+ * add this arc
+ */
+ tally( &arc );
+ }
+ fclose(pfile);
+}
+
+FILE *
+openpfile(filename)
+ char *filename;
+{
+ struct gmonhdr tmp;
+ FILE *pfile;
+ int size;
+ int rate;
+
+ if((pfile = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ done();
+ }
+ fread(&tmp, sizeof(struct gmonhdr), 1, pfile);
+ if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc ||
+ tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) ) {
+ fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
+ done();
+ }
+ gmonhdr = tmp;
+ if ( gmonhdr.version == GMONVERSION ) {
+ rate = gmonhdr.profrate;
+ size = sizeof(struct gmonhdr);
+ } else {
+ fseek(pfile, sizeof(struct ophdr), SEEK_SET);
+ size = sizeof(struct ophdr);
+ gmonhdr.profrate = rate = hertz();
+ gmonhdr.version = GMONVERSION;
+ }
+ if (hz == 0) {
+ hz = rate;
+ } else if (hz != rate) {
+ fprintf(stderr,
+ "%s: profile clock rate (%d) %s (%d) in first gmon file\n",
+ filename, rate, "incompatible with clock rate", hz);
+ done();
+ }
+ s_lowpc = (unsigned long) gmonhdr.lpc;
+ s_highpc = (unsigned long) gmonhdr.hpc;
+ lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT);
+ highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT);
+ sampbytes = gmonhdr.ncnt - size;
+ nsamples = sampbytes / sizeof (UNIT);
+# ifdef DEBUG
+ if ( debug & SAMPLEDEBUG ) {
+ printf( "[openpfile] hdr.lpc 0x%x hdr.hpc 0x%x hdr.ncnt %d\n",
+ gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt );
+ printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
+ s_lowpc , s_highpc );
+ printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
+ lowpc , highpc );
+ printf( "[openpfile] sampbytes %d nsamples %d\n" ,
+ sampbytes , nsamples );
+ printf( "[openpfile] sample rate %d\n" , hz );
+ }
+# endif DEBUG
+ return(pfile);
+}
+
+tally( rawp )
+ struct rawarc *rawp;
+{
+ nltype *parentp;
+ nltype *childp;
+
+ parentp = nllookup( rawp -> raw_frompc );
+ childp = nllookup( rawp -> raw_selfpc );
+ if ( parentp == 0 || childp == 0 )
+ return;
+ if ( kflag
+ && onlist( kfromlist , parentp -> name )
+ && onlist( ktolist , childp -> name ) ) {
+ return;
+ }
+ childp -> ncall += rawp -> raw_count;
+# ifdef DEBUG
+ if ( debug & TALLYDEBUG ) {
+ printf( "[tally] arc from %s to %s traversed %d times\n" ,
+ parentp -> name , childp -> name , rawp -> raw_count );
+ }
+# endif DEBUG
+ addarc( parentp , childp , rawp -> raw_count );
+}
+
+/*
+ * dump out the gmon.sum file
+ */
+dumpsum( sumfile )
+ char *sumfile;
+{
+ register nltype *nlp;
+ register arctype *arcp;
+ struct rawarc arc;
+ FILE *sfile;
+
+ if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
+ perror( sumfile );
+ done();
+ }
+ /*
+ * dump the header; use the last header read in
+ */
+ if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) {
+ perror( sumfile );
+ done();
+ }
+ /*
+ * dump the samples
+ */
+ if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
+ perror( sumfile );
+ done();
+ }
+ /*
+ * dump the normalized raw arc information
+ */
+ for ( nlp = nl ; nlp < npe ; nlp++ ) {
+ for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+ arc.raw_frompc = arcp -> arc_parentp -> value;
+ arc.raw_selfpc = arcp -> arc_childp -> value;
+ arc.raw_count = arcp -> arc_count;
+ if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
+ perror( sumfile );
+ done();
+ }
+# ifdef DEBUG
+ if ( debug & SAMPLEDEBUG ) {
+ printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
+ arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
+ }
+# endif DEBUG
+ }
+ }
+ fclose( sfile );
+}
+
+valcmp(p1, p2)
+ nltype *p1, *p2;
+{
+ if ( p1 -> value < p2 -> value ) {
+ return LESSTHAN;
+ }
+ if ( p1 -> value > p2 -> value ) {
+ return GREATERTHAN;
+ }
+ return EQUALTO;
+}
+
+readsamples(pfile)
+ FILE *pfile;
+{
+ register i;
+ UNIT sample;
+
+ if (samples == 0) {
+ samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
+ if (samples == 0) {
+ fprintf( stderr , "%s: No room for %d sample pc's\n",
+ whoami , sampbytes / sizeof (UNIT));
+ done();
+ }
+ }
+ for (i = 0; i < nsamples; i++) {
+ fread(&sample, sizeof (UNIT), 1, pfile);
+ if (feof(pfile))
+ break;
+ samples[i] += sample;
+ }
+ if (i != nsamples) {
+ fprintf(stderr,
+ "%s: unexpected EOF after reading %d/%d samples\n",
+ whoami , --i , nsamples );
+ done();
+ }
+}
+
+/*
+ * Assign samples to the procedures to which they belong.
+ *
+ * There are three cases as to where pcl and pch can be
+ * with respect to the routine entry addresses svalue0 and svalue1
+ * as shown in the following diagram. overlap computes the
+ * distance between the arrows, the fraction of the sample
+ * that is to be credited to the routine which starts at svalue0.
+ *
+ * svalue0 svalue1
+ * | |
+ * v v
+ *
+ * +-----------------------------------------------+
+ * | |
+ * | ->| |<- ->| |<- ->| |<- |
+ * | | | | | |
+ * +---------+ +---------+ +---------+
+ *
+ * ^ ^ ^ ^ ^ ^
+ * | | | | | |
+ * pcl pch pcl pch pcl pch
+ *
+ * For the vax we assert that samples will never fall in the first
+ * two bytes of any routine, since that is the entry mask,
+ * thus we give call alignentries() to adjust the entry points if
+ * the entry mask falls in one bucket but the code for the routine
+ * doesn't start until the next bucket. In conjunction with the
+ * alignment of routine addresses, this should allow us to have
+ * only one sample for every four bytes of text space and never
+ * have any overlap (the two end cases, above).
+ */
+asgnsamples()
+{
+ register int j;
+ UNIT ccnt;
+ double time;
+ unsigned long pcl, pch;
+ register int i;
+ unsigned long overlap;
+ unsigned long svalue0, svalue1;
+
+ /* read samples and assign to namelist symbols */
+ scale = highpc - lowpc;
+ scale /= nsamples;
+ alignentries();
+ for (i = 0, j = 1; i < nsamples; i++) {
+ ccnt = samples[i];
+ if (ccnt == 0)
+ continue;
+ pcl = lowpc + scale * i;
+ pch = lowpc + scale * (i + 1);
+ time = ccnt;
+# ifdef DEBUG
+ if ( debug & SAMPLEDEBUG ) {
+ printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
+ pcl , pch , ccnt );
+ }
+# endif DEBUG
+ totime += time;
+ for (j = j - 1; j < nname; j++) {
+ svalue0 = nl[j].svalue;
+ svalue1 = nl[j+1].svalue;
+ /*
+ * if high end of tick is below entry address,
+ * go for next tick.
+ */
+ if (pch < svalue0)
+ break;
+ /*
+ * if low end of tick into next routine,
+ * go for next routine.
+ */
+ if (pcl >= svalue1)
+ continue;
+ overlap = min(pch, svalue1) - max(pcl, svalue0);
+ if (overlap > 0) {
+# ifdef DEBUG
+ if (debug & SAMPLEDEBUG) {
+ printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
+ nl[j].value/sizeof(UNIT), svalue0, svalue1,
+ nl[j].name,
+ overlap * time / scale, overlap);
+ }
+# endif DEBUG
+ nl[j].time += overlap * time / scale;
+ }
+ }
+ }
+# ifdef DEBUG
+ if (debug & SAMPLEDEBUG) {
+ printf("[asgnsamples] totime %f\n", totime);
+ }
+# endif DEBUG
+}
+
+
+unsigned long
+min(a, b)
+ unsigned long a,b;
+{
+ if (a<b)
+ return(a);
+ return(b);
+}
+
+unsigned long
+max(a, b)
+ unsigned long a,b;
+{
+ if (a>b)
+ return(a);
+ return(b);
+}
+
+ /*
+ * calculate scaled entry point addresses (to save time in asgnsamples),
+ * and possibly push the scaled entry points over the entry mask,
+ * if it turns out that the entry point is in one bucket and the code
+ * for a routine is in the next bucket.
+ */
+alignentries()
+{
+ register struct nl *nlp;
+ unsigned long bucket_of_entry;
+ unsigned long bucket_of_code;
+
+ for (nlp = nl; nlp < npe; nlp++) {
+ nlp -> svalue = nlp -> value / sizeof(UNIT);
+ bucket_of_entry = (nlp->svalue - lowpc) / scale;
+ bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
+ if (bucket_of_entry < bucket_of_code) {
+# ifdef DEBUG
+ if (debug & SAMPLEDEBUG) {
+ printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
+ nlp->svalue, nlp->svalue + UNITS_TO_CODE);
+ }
+# endif DEBUG
+ nlp->svalue += UNITS_TO_CODE;
+ }
+ }
+}
+
+bool
+funcsymbol( nlistp )
+ struct nlist *nlistp;
+{
+ char *name, c;
+
+ /*
+ * must be a text symbol,
+ * and static text symbols don't qualify if aflag set.
+ */
+ if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
+ || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
+ return FALSE;
+ }
+ /*
+ * name must start with an underscore if uflag is set.
+ * can't have any `funny' characters in name,
+ * where `funny' includes `.', .o file names
+ * and `$', pascal labels.
+ * need to make an exception for sparc .mul & co.
+ * perhaps we should just drop this code entirely...
+ */
+ name = strtab + nlistp -> n_un.n_strx;
+ if ( uflag && *name != '_' )
+ return FALSE;
+#ifdef sparc
+ if ( *name == '.' ) {
+ char *p = name + 1;
+ if ( *p == 'u' )
+ p++;
+ if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 ||
+ strcmp ( p, "rem" ) == 0 )
+ return TRUE;
+ }
+#endif
+ while ( c = *name++ ) {
+ if ( c == '.' || c == '$' ) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+done()
+{
+
+ exit(0);
+}
diff --git a/usr.bin/gprof/gprof.callg b/usr.bin/gprof/gprof.callg
new file mode 100644
index 0000000..533c96c
--- /dev/null
+++ b/usr.bin/gprof/gprof.callg
@@ -0,0 +1,108 @@
+
+
+
+call graph profile:
+ The sum of self and descendents is the major sort
+ for this listing.
+
+ function entries:
+
+index the index of the function in the call graph
+ listing, as an aid to locating it (see below).
+
+%time the percentage of the total time of the program
+ accounted for by this function and its
+ descendents.
+
+self the number of seconds spent in this function
+ itself.
+
+descendents
+ the number of seconds spent in the descendents of
+ this function on behalf of this function.
+
+called the number of times this function is called (other
+ than recursive calls).
+
+self the number of times this function calls itself
+ recursively.
+
+name the name of the function, with an indication of
+ its membership in a cycle, if any.
+
+index the index of the function in the call graph
+ listing, as an aid to locating it.
+
+
+
+ parent listings:
+
+self* the number of seconds of this function's self time
+ which is due to calls from this parent.
+
+descendents*
+ the number of seconds of this function's
+ descendent time which is due to calls from this
+ parent.
+
+called** the number of times this function is called by
+ this parent. This is the numerator of the
+ fraction which divides up the function's time to
+ its parents.
+
+total* the number of times this function was called by
+ all of its parents. This is the denominator of
+ the propagation fraction.
+
+parents the name of this parent, with an indication of the
+ parent's membership in a cycle, if any.
+
+index the index of this parent in the call graph
+ listing, as an aid in locating it.
+
+
+
+ children listings:
+
+self* the number of seconds of this child's self time
+ which is due to being called by this function.
+
+descendent*
+ the number of seconds of this child's descendent's
+ time which is due to being called by this
+ function.
+
+called** the number of times this child is called by this
+ function. This is the numerator of the
+ propagation fraction for this child.
+
+total* the number of times this child is called by all
+ functions. This is the denominator of the
+ propagation fraction.
+
+children the name of this child, and an indication of its
+ membership in a cycle, if any.
+
+index the index of this child in the call graph listing,
+ as an aid to locating it.
+
+
+
+ * these fields are omitted for parents (or
+ children) in the same cycle as the function. If
+ the function (or child) is a member of a cycle,
+ the propagated times and propagation denominator
+ represent the self time and descendent time of the
+ cycle as a whole.
+
+ ** static-only parents and children are indicated
+ by a call count of 0.
+
+
+
+ cycle listings:
+ the cycle as a whole is listed with the same
+ fields as a function entry. Below it are listed
+ the members of the cycle, and their contributions
+ to the time and call counts of the cycle.
+
diff --git a/usr.bin/gprof/gprof.flat b/usr.bin/gprof/gprof.flat
new file mode 100644
index 0000000..60999a3
--- /dev/null
+++ b/usr.bin/gprof/gprof.flat
@@ -0,0 +1,32 @@
+
+
+
+flat profile:
+
+ % the percentage of the total running time of the
+time program used by this function.
+
+cumulative a running sum of the number of seconds accounted
+ seconds for by this function and those listed above it.
+
+ self the number of seconds accounted for by this
+seconds function alone. This is the major sort for this
+ listing.
+
+calls the number of times this function was invoked, if
+ this function is profiled, else blank.
+
+ self the average number of milliseconds spent in this
+ms/call function per call, if this function is profiled,
+ else blank.
+
+ total the average number of milliseconds spent in this
+ms/call function and its descendents per call, if this
+ function is profiled, else blank.
+
+name the name of the function. This is the minor sort
+ for this listing. The index shows the location of
+ the function in the gprof listing. If the index is
+ in parenthesis it shows where it would appear in
+ the gprof listing if it were to be printed.
+
diff --git a/usr.bin/gprof/gprof.h b/usr.bin/gprof/gprof.h
new file mode 100644
index 0000000..31bada9
--- /dev/null
+++ b/usr.bin/gprof/gprof.h
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gprof.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/gmon.h>
+
+#include <a.out.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if vax
+# include "vax.h"
+#endif
+#if sparc
+# include "sparc.h"
+#endif
+#if tahoe
+# include "tahoe.h"
+#endif
+#if hp300
+# include "hp300.h"
+#endif
+#if luna68k
+# include "luna68k.h"
+#endif
+#if i386
+# include "i386.h"
+#endif
+#if mips
+# include "mips.h"
+#endif
+
+
+ /*
+ * who am i, for error messages.
+ */
+char *whoami;
+
+ /*
+ * booleans
+ */
+typedef int bool;
+#define FALSE 0
+#define TRUE 1
+
+ /*
+ * ticks per second
+ */
+long hz;
+
+typedef u_short UNIT; /* unit of profiling */
+char *a_outname;
+#define A_OUTNAME "a.out"
+
+char *gmonname;
+#define GMONNAME "gmon.out"
+#define GMONSUM "gmon.sum"
+
+ /*
+ * a constructed arc,
+ * with pointers to the namelist entry of the parent and the child,
+ * a count of how many times this arc was traversed,
+ * and pointers to the next parent of this child and
+ * the next child of this parent.
+ */
+struct arcstruct {
+ struct nl *arc_parentp; /* pointer to parent's nl entry */
+ struct nl *arc_childp; /* pointer to child's nl entry */
+ long arc_count; /* num calls from parent to child */
+ double arc_time; /* time inherited along arc */
+ double arc_childtime; /* childtime inherited along arc */
+ struct arcstruct *arc_parentlist; /* parents-of-this-child list */
+ struct arcstruct *arc_childlist; /* children-of-this-parent list */
+ struct arcstruct *arc_next; /* list of arcs on cycle */
+ unsigned short arc_cyclecnt; /* num cycles involved in */
+ unsigned short arc_flags; /* see below */
+};
+typedef struct arcstruct arctype;
+
+ /*
+ * arc flags
+ */
+#define DEADARC 0x01 /* time should not propagate across the arc */
+#define ONLIST 0x02 /* arc is on list of arcs in cycles */
+
+ /*
+ * The symbol table;
+ * for each external in the specified file we gather
+ * its address, the number of calls and compute its share of cpu time.
+ */
+struct nl {
+ char *name; /* the name */
+ unsigned long value; /* the pc entry point */
+ unsigned long svalue; /* entry point aligned to histograms */
+ double time; /* ticks in this routine */
+ double childtime; /* cumulative ticks in children */
+ long ncall; /* how many times called */
+ long npropcall; /* times called by live arcs */
+ long selfcalls; /* how many calls to self */
+ double propfraction; /* what % of time propagates */
+ double propself; /* how much self time propagates */
+ double propchild; /* how much child time propagates */
+ short printflag; /* should this be printed? */
+ short flags; /* see below */
+ int index; /* index in the graph list */
+ int toporder; /* graph call chain top-sort order */
+ int cycleno; /* internal number of cycle on */
+ int parentcnt; /* number of live parent arcs */
+ struct nl *cyclehead; /* pointer to head of cycle */
+ struct nl *cnext; /* pointer to next member of cycle */
+ arctype *parents; /* list of caller arcs */
+ arctype *children; /* list of callee arcs */
+};
+typedef struct nl nltype;
+
+nltype *nl; /* the whole namelist */
+nltype *npe; /* the virtual end of the namelist */
+int nname; /* the number of function names */
+
+#define HASCYCLEXIT 0x08 /* node has arc exiting from cycle */
+#define CYCLEHEAD 0x10 /* node marked as head of a cycle */
+#define VISITED 0x20 /* node visited during a cycle */
+
+ /*
+ * The cycle list.
+ * for each subcycle within an identified cycle, we gather
+ * its size and the list of included arcs.
+ */
+struct cl {
+ int size; /* length of cycle */
+ struct cl *next; /* next member of list */
+ arctype *list[1]; /* list of arcs in cycle */
+ /* actually longer */
+};
+typedef struct cl cltype;
+
+arctype *archead; /* the head of arcs in current cycle list */
+cltype *cyclehead; /* the head of the list */
+int cyclecnt; /* the number of cycles found */
+#define CYCLEMAX 100 /* maximum cycles before cutting one of them */
+
+ /*
+ * flag which marks a nl entry as topologically ``busy''
+ * flag which marks a nl entry as topologically ``not_numbered''
+ */
+#define DFN_BUSY -1
+#define DFN_NAN 0
+
+ /*
+ * namelist entries for cycle headers.
+ * the number of discovered cycles.
+ */
+nltype *cyclenl; /* cycle header namelist */
+int ncycle; /* number of cycles discovered */
+
+ /*
+ * The header on the gmon.out file.
+ * gmon.out consists of a struct phdr (defined in gmon.h)
+ * and then an array of ncnt samples representing the
+ * discretized program counter values.
+ *
+ * Backward compatible old style header
+ */
+struct ophdr {
+ UNIT *lpc;
+ UNIT *hpc;
+ int ncnt;
+};
+
+int debug;
+
+ /*
+ * Each discretized pc sample has
+ * a count of the number of samples in its range
+ */
+UNIT *samples;
+
+unsigned long s_lowpc; /* lowpc from the profile file */
+unsigned long s_highpc; /* highpc from the profile file */
+unsigned lowpc, highpc; /* range profiled, in UNIT's */
+unsigned sampbytes; /* number of bytes of samples */
+int nsamples; /* number of samples */
+double actime; /* accumulated time thus far for putprofline */
+double totime; /* total time for all routines */
+double printtime; /* total of time being printed */
+double scale; /* scale factor converting samples to pc
+ values: each sample covers scale bytes */
+char *strtab; /* string table in core */
+long ssiz; /* size of the string table */
+struct exec xbuf; /* exec header of a.out */
+unsigned char *textspace; /* text space of a.out in core */
+int cyclethreshold; /* with -C, minimum cycle size to ignore */
+
+ /*
+ * option flags, from a to z.
+ */
+bool aflag; /* suppress static functions */
+bool bflag; /* blurbs, too */
+bool cflag; /* discovered call graph, too */
+bool Cflag; /* find cut-set to eliminate cycles */
+bool dflag; /* debugging options */
+bool eflag; /* specific functions excluded */
+bool Eflag; /* functions excluded with time */
+bool fflag; /* specific functions requested */
+bool Fflag; /* functions requested with time */
+bool kflag; /* arcs to be deleted */
+bool sflag; /* sum multiple gmon.out files */
+bool zflag; /* zero time/called functions, too */
+
+ /*
+ * structure for various string lists
+ */
+struct stringlist {
+ struct stringlist *next;
+ char *string;
+};
+struct stringlist *elist;
+struct stringlist *Elist;
+struct stringlist *flist;
+struct stringlist *Flist;
+struct stringlist *kfromlist;
+struct stringlist *ktolist;
+
+ /*
+ * function declarations
+ */
+/*
+ addarc();
+*/
+int arccmp();
+arctype *arclookup();
+/*
+ asgnsamples();
+ printblurb();
+ cyclelink();
+ dfn();
+*/
+bool dfn_busy();
+/*
+ dfn_findcycle();
+*/
+bool dfn_numbered();
+/*
+ dfn_post_visit();
+ dfn_pre_visit();
+ dfn_self_cycle();
+*/
+nltype **doarcs();
+/*
+ done();
+ findcalls();
+ flatprofheader();
+ flatprofline();
+*/
+bool funcsymbol();
+/*
+ getnfile();
+ getpfile();
+ getstrtab();
+ getsymtab();
+ gettextspace();
+ gprofheader();
+ gprofline();
+ main();
+*/
+unsigned long max();
+int membercmp();
+unsigned long min();
+nltype *nllookup();
+FILE *openpfile();
+long operandlength();
+operandenum operandmode();
+char *operandname();
+/*
+ printchildren();
+ printcycle();
+ printgprof();
+ printmembers();
+ printname();
+ printparents();
+ printprof();
+ readsamples();
+*/
+unsigned long reladdr();
+/*
+ sortchildren();
+ sortmembers();
+ sortparents();
+ tally();
+ timecmp();
+ topcmp();
+*/
+int totalcmp();
+/*
+ valcmp();
+*/
+
+#define LESSTHAN -1
+#define EQUALTO 0
+#define GREATERTHAN 1
+
+#define DFNDEBUG 1
+#define CYCLEDEBUG 2
+#define ARCDEBUG 4
+#define TALLYDEBUG 8
+#define TIMEDEBUG 16
+#define SAMPLEDEBUG 32
+#define AOUTDEBUG 64
+#define CALLDEBUG 128
+#define LOOKUPDEBUG 256
+#define PROPDEBUG 512
+#define BREAKCYCLE 1024
+#define SUBCYCLELIST 2048
+#define ANYDEBUG 4096
diff --git a/usr.bin/gprof/hertz.c b/usr.bin/gprof/hertz.c
new file mode 100644
index 0000000..41b455f
--- /dev/null
+++ b/usr.bin/gprof/hertz.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hertz.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/time.h>
+
+ /*
+ * discover the tick frequency of the machine
+ * if something goes wrong, we return 0, an impossible hertz.
+ */
+#define HZ_WRONG 0
+
+hertz()
+{
+ struct itimerval tim;
+
+ tim.it_interval.tv_sec = 0;
+ tim.it_interval.tv_usec = 1;
+ tim.it_value.tv_sec = 0;
+ tim.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &tim, 0);
+ setitimer(ITIMER_REAL, 0, &tim);
+ if (tim.it_interval.tv_usec < 2)
+ return(HZ_WRONG);
+ return (1000000 / tim.it_interval.tv_usec);
+}
diff --git a/usr.bin/gprof/hp300.c b/usr.bin/gprof/hp300.c
new file mode 100644
index 0000000..6a47408
--- /dev/null
+++ b/usr.bin/gprof/hp300.c
@@ -0,0 +1,11 @@
+#include "gprof.h"
+
+/*
+ * gprof -c isn't currently supported...
+ */
+findcall( parentp , p_lowpc , p_highpc )
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+}
diff --git a/usr.bin/gprof/hp300.h b/usr.bin/gprof/hp300.h
new file mode 100644
index 0000000..15b9597
--- /dev/null
+++ b/usr.bin/gprof/hp300.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hp300.h 8.1 (Berkeley) 6/6/93
+ */
+
+ /*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 0
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+enum opermodes { dummy };
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/i386.c b/usr.bin/gprof/i386.c
new file mode 100644
index 0000000..6a47408
--- /dev/null
+++ b/usr.bin/gprof/i386.c
@@ -0,0 +1,11 @@
+#include "gprof.h"
+
+/*
+ * gprof -c isn't currently supported...
+ */
+findcall( parentp , p_lowpc , p_highpc )
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+}
diff --git a/usr.bin/gprof/i386.h b/usr.bin/gprof/i386.h
new file mode 100644
index 0000000..067e019
--- /dev/null
+++ b/usr.bin/gprof/i386.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)i386.h 8.1 (Berkeley) 6/6/93
+ */
+
+ /*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 0
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+enum opermodes { dummy };
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/lookup.c b/usr.bin/gprof/lookup.c
new file mode 100644
index 0000000..c276346
--- /dev/null
+++ b/usr.bin/gprof/lookup.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lookup.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * look up an address in a sorted-by-address namelist
+ * this deals with misses by mapping them to the next lower
+ * entry point.
+ */
+nltype *
+nllookup( address )
+ unsigned long address;
+{
+ register long low;
+ register long middle;
+ register long high;
+# ifdef DEBUG
+ register int probes;
+
+ probes = 0;
+# endif DEBUG
+ for ( low = 0 , high = nname - 1 ; low != high ; ) {
+# ifdef DEBUG
+ probes += 1;
+# endif DEBUG
+ middle = ( high + low ) >> 1;
+ if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address ) {
+# ifdef DEBUG
+ if ( debug & LOOKUPDEBUG ) {
+ printf( "[nllookup] %d (%d) probes\n" , probes , nname-1 );
+ }
+# endif DEBUG
+ return &nl[ middle ];
+ }
+ if ( nl[ middle ].value > address ) {
+ high = middle;
+ } else {
+ low = middle + 1;
+ }
+ }
+# ifdef DEBUG
+ if ( debug & LOOKUPDEBUG ) {
+ fprintf( stderr , "[nllookup] (%d) binary search fails\n" ,
+ nname-1 );
+ }
+# endif DEBUG
+ return 0;
+}
+
+arctype *
+arclookup( parentp , childp )
+ nltype *parentp;
+ nltype *childp;
+{
+ arctype *arcp;
+
+ if ( parentp == 0 || childp == 0 ) {
+ fprintf( stderr, "[arclookup] parentp == 0 || childp == 0\n" );
+ return 0;
+ }
+# ifdef DEBUG
+ if ( debug & LOOKUPDEBUG ) {
+ printf( "[arclookup] parent %s child %s\n" ,
+ parentp -> name , childp -> name );
+ }
+# endif DEBUG
+ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+# ifdef DEBUG
+ if ( debug & LOOKUPDEBUG ) {
+ printf( "[arclookup]\t arc_parent %s arc_child %s\n" ,
+ arcp -> arc_parentp -> name ,
+ arcp -> arc_childp -> name );
+ }
+# endif DEBUG
+ if ( arcp -> arc_childp == childp ) {
+ return arcp;
+ }
+ }
+ return 0;
+}
diff --git a/usr.bin/gprof/mips.c b/usr.bin/gprof/mips.c
new file mode 100644
index 0000000..295d64e
--- /dev/null
+++ b/usr.bin/gprof/mips.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley. Modified by Ralph Campbell for mips.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: sparc.c 5.1 (Berkeley) 7/7/92
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mips.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * a namelist entry to be the child of indirect calls
+ */
+nltype indirectchild = {
+ "(*)" , /* the name */
+ (unsigned long) 0 , /* the pc entry point */
+ (unsigned long) 0 , /* entry point aligned to histogram */
+ (double) 0.0 , /* ticks in this routine */
+ (double) 0.0 , /* cumulative ticks in children */
+ (long) 0 , /* how many times called */
+ (long) 0 , /* times called by live arcs */
+ (long) 0 , /* how many calls to self */
+ (double) 1.0 , /* propagation fraction */
+ (double) 0.0 , /* self propagation time */
+ (double) 0.0 , /* child propagation time */
+ (short) 0 , /* print flag */
+ (short) 0 , /* flags */
+ (int) 0 , /* index in the graph list */
+ (int) 0 , /* graph call chain top-sort order */
+ (int) 0 , /* internal number of cycle on */
+ (int) 0 , /* number of live parent arcs */
+ (struct nl *) &indirectchild , /* pointer to head of cycle */
+ (struct nl *) 0 , /* pointer to next member of cycle */
+ (arctype *) 0 , /* list of caller arcs */
+ (arctype *) 0 /* list of callee arcs */
+};
+
+findcall(parentp, p_lowpc, p_highpc)
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+ register u_long pc;
+ nltype *childp;
+ unsigned long destpc;
+ register long op;
+ register int off;
+
+ if (textspace == 0)
+ return;
+ if (p_lowpc < s_lowpc)
+ p_lowpc = s_lowpc;
+ if (p_highpc > s_highpc)
+ p_highpc = s_highpc;
+
+ for (pc = p_lowpc; pc < p_highpc; pc += 4) {
+ off = pc - s_lowpc;
+ op = *(u_long *)&textspace[off];
+ if ((op & 0xfc000000) == 0x0c000000) {
+ /*
+ * a jal insn -- check that this
+ * is the address of a function.
+ */
+ off = (op & 0x03ffffff) << 2;
+ destpc = (pc & 0xf0000000) | off;
+ if (destpc >= s_lowpc && destpc <= s_highpc) {
+ childp = nllookup(destpc);
+ if (childp != 0 && childp->value == destpc)
+ addarc(parentp, childp, 0L);
+ }
+ } else if ((op & 0xfc00f83f) == 0x0000f809)
+ /*
+ * A jalr -- an indirect call.
+ */
+ addarc(parentp, &indirectchild, 0L);
+ }
+}
diff --git a/usr.bin/gprof/mips.h b/usr.bin/gprof/mips.h
new file mode 100644
index 0000000..cd67a43
--- /dev/null
+++ b/usr.bin/gprof/mips.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley. Modified by Ralph Campbell for mips.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mips.h 8.1 (Berkeley) 6/6/93
+ *
+ * From: @(#)sparc.h 5.1 (Berkeley) 7/8/92
+ */
+
+/*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 0
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+enum opermodes { dummy };
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/pathnames.h b/usr.bin/gprof/pathnames.h
new file mode 100644
index 0000000..ea554c6
--- /dev/null
+++ b/usr.bin/gprof/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_FLAT_BLURB "/usr/share/misc/gprof.flat"
+#define _PATH_CALLG_BLURB "/usr/share/misc/gprof.callg"
+
diff --git a/usr.bin/gprof/printgprof.c b/usr.bin/gprof/printgprof.c
new file mode 100644
index 0000000..716fd8b
--- /dev/null
+++ b/usr.bin/gprof/printgprof.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printgprof.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+#include "pathnames.h"
+
+printprof()
+{
+ register nltype *np;
+ nltype **sortednlp;
+ int index, timecmp();
+
+ actime = 0.0;
+ printf( "\f\n" );
+ flatprofheader();
+ /*
+ * Sort the symbol table in by time
+ */
+ sortednlp = (nltype **) calloc( nname , sizeof(nltype *) );
+ if ( sortednlp == (nltype **) 0 ) {
+ fprintf( stderr , "[printprof] ran out of memory for time sorting\n" );
+ }
+ for ( index = 0 ; index < nname ; index += 1 ) {
+ sortednlp[ index ] = &nl[ index ];
+ }
+ qsort( sortednlp , nname , sizeof(nltype *) , timecmp );
+ for ( index = 0 ; index < nname ; index += 1 ) {
+ np = sortednlp[ index ];
+ flatprofline( np );
+ }
+ actime = 0.0;
+ free( sortednlp );
+}
+
+timecmp( npp1 , npp2 )
+ nltype **npp1, **npp2;
+{
+ double timediff;
+ long calldiff;
+
+ timediff = (*npp2) -> time - (*npp1) -> time;
+ if ( timediff > 0.0 )
+ return 1 ;
+ if ( timediff < 0.0 )
+ return -1;
+ calldiff = (*npp2) -> ncall - (*npp1) -> ncall;
+ if ( calldiff > 0 )
+ return 1;
+ if ( calldiff < 0 )
+ return -1;
+ return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
+}
+
+ /*
+ * header for flatprofline
+ */
+flatprofheader()
+{
+
+ if ( bflag ) {
+ printblurb( _PATH_FLAT_BLURB );
+ }
+ printf( "\ngranularity: each sample hit covers %d byte(s)" ,
+ (long) scale * sizeof(UNIT) );
+ if ( totime > 0.0 ) {
+ printf( " for %.2f%% of %.2f seconds\n\n" ,
+ 100.0/totime , totime / hz );
+ } else {
+ printf( " no time accumulated\n\n" );
+ /*
+ * this doesn't hurt sinc eall the numerators will be zero.
+ */
+ totime = 1.0;
+ }
+ printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
+ "% " , "cumulative" , "self " , "" , "self " , "total " , "" );
+ printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
+ "time" , "seconds " , "seconds" , "calls" ,
+ hz >= 10000 ? "us/call" : "ms/call" ,
+ hz >= 10000 ? "us/call" : "ms/call" , "name" );
+}
+
+flatprofline( np )
+ register nltype *np;
+{
+
+ if ( zflag == 0 && np -> ncall == 0 && np -> time == 0 ) {
+ return;
+ }
+ actime += np -> time;
+ if (hz >= 10000)
+ printf( "%5.1f %10.3f %8.3f" ,
+ 100 * np -> time / totime , actime / hz , np -> time / hz );
+ else
+ printf( "%5.1f %10.2f %8.2f" ,
+ 100 * np -> time / totime , actime / hz , np -> time / hz );
+ if ( np -> ncall != 0 ) {
+ if (hz >= 10000)
+ printf( " %8d %8.0f %8.0f " , np -> ncall ,
+ 1e6 * np -> time / hz / np -> ncall ,
+ 1e6 * ( np -> time + np -> childtime ) / hz / np -> ncall );
+ else
+ printf( " %8d %8.2f %8.2f " , np -> ncall ,
+ 1000 * np -> time / hz / np -> ncall ,
+ 1000 * ( np -> time + np -> childtime ) / hz / np -> ncall );
+ } else {
+ printf( " %8.8s %8.8s %8.8s " , "" , "" , "" );
+ }
+ printname( np );
+ printf( "\n" );
+}
+
+gprofheader()
+{
+
+ if ( bflag ) {
+ printblurb( _PATH_CALLG_BLURB );
+ }
+ printf( "\ngranularity: each sample hit covers %d byte(s)" ,
+ (long) scale * sizeof(UNIT) );
+ if ( printtime > 0.0 ) {
+ printf( " for %.2f%% of %.2f seconds\n\n" ,
+ 100.0/printtime , printtime / hz );
+ } else {
+ printf( " no time propagated\n\n" );
+ /*
+ * this doesn't hurt, since all the numerators will be 0.0
+ */
+ printtime = 1.0;
+ }
+ printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
+ "" , "" , "" , "" , "called" , "total" , "parents");
+ printf( "%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n" ,
+ "index" , "%time" , "self" , "descendents" ,
+ "called" , "self" , "name" , "index" );
+ printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
+ "" , "" , "" , "" , "called" , "total" , "children");
+ printf( "\n" );
+}
+
+gprofline( np )
+ register nltype *np;
+{
+ char kirkbuffer[ BUFSIZ ];
+
+ sprintf( kirkbuffer , "[%d]" , np -> index );
+ printf( "%-6.6s %5.1f %7.2f %11.2f" ,
+ kirkbuffer ,
+ 100 * ( np -> propself + np -> propchild ) / printtime ,
+ np -> propself / hz ,
+ np -> propchild / hz );
+ if ( ( np -> ncall + np -> selfcalls ) != 0 ) {
+ printf( " %7d" , np -> npropcall );
+ if ( np -> selfcalls != 0 ) {
+ printf( "+%-7d " , np -> selfcalls );
+ } else {
+ printf( " %7.7s " , "" );
+ }
+ } else {
+ printf( " %7.7s %7.7s " , "" , "" );
+ }
+ printname( np );
+ printf( "\n" );
+}
+
+printgprof(timesortnlp)
+ nltype **timesortnlp;
+{
+ int index;
+ nltype *parentp;
+
+ /*
+ * Print out the structured profiling list
+ */
+ gprofheader();
+ for ( index = 0 ; index < nname + ncycle ; index ++ ) {
+ parentp = timesortnlp[ index ];
+ if ( zflag == 0 &&
+ parentp -> ncall == 0 &&
+ parentp -> selfcalls == 0 &&
+ parentp -> propself == 0 &&
+ parentp -> propchild == 0 ) {
+ continue;
+ }
+ if ( ! parentp -> printflag ) {
+ continue;
+ }
+ if ( parentp -> name == 0 && parentp -> cycleno != 0 ) {
+ /*
+ * cycle header
+ */
+ printcycle( parentp );
+ printmembers( parentp );
+ } else {
+ printparents( parentp );
+ gprofline( parentp );
+ printchildren( parentp );
+ }
+ printf( "\n" );
+ printf( "-----------------------------------------------\n" );
+ printf( "\n" );
+ }
+ free( timesortnlp );
+}
+
+ /*
+ * sort by decreasing propagated time
+ * if times are equal, but one is a cycle header,
+ * say that's first (e.g. less, i.e. -1).
+ * if one's name doesn't have an underscore and the other does,
+ * say the one is first.
+ * all else being equal, sort by names.
+ */
+int
+totalcmp( npp1 , npp2 )
+ nltype **npp1;
+ nltype **npp2;
+{
+ register nltype *np1 = *npp1;
+ register nltype *np2 = *npp2;
+ double diff;
+
+ diff = ( np1 -> propself + np1 -> propchild )
+ - ( np2 -> propself + np2 -> propchild );
+ if ( diff < 0.0 )
+ return 1;
+ if ( diff > 0.0 )
+ return -1;
+ if ( np1 -> name == 0 && np1 -> cycleno != 0 )
+ return -1;
+ if ( np2 -> name == 0 && np2 -> cycleno != 0 )
+ return 1;
+ if ( np1 -> name == 0 )
+ return -1;
+ if ( np2 -> name == 0 )
+ return 1;
+ if ( *(np1 -> name) != '_' && *(np2 -> name) == '_' )
+ return -1;
+ if ( *(np1 -> name) == '_' && *(np2 -> name) != '_' )
+ return 1;
+ if ( np1 -> ncall > np2 -> ncall )
+ return -1;
+ if ( np1 -> ncall < np2 -> ncall )
+ return 1;
+ return strcmp( np1 -> name , np2 -> name );
+}
+
+printparents( childp )
+ nltype *childp;
+{
+ nltype *parentp;
+ arctype *arcp;
+ nltype *cycleheadp;
+
+ if ( childp -> cyclehead != 0 ) {
+ cycleheadp = childp -> cyclehead;
+ } else {
+ cycleheadp = childp;
+ }
+ if ( childp -> parents == 0 ) {
+ printf( "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n" ,
+ "" , "" , "" , "" , "" , "" );
+ return;
+ }
+ sortparents( childp );
+ for ( arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist ) {
+ parentp = arcp -> arc_parentp;
+ if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) ||
+ ( childp->cycleno != 0 && parentp->cycleno == childp->cycleno ) ) {
+ /*
+ * selfcall or call among siblings
+ */
+ printf( "%6.6s %5.5s %7.7s %11.11s %7d %7.7s " ,
+ "" , "" , "" , "" ,
+ arcp -> arc_count , "" );
+ printname( parentp );
+ printf( "\n" );
+ } else {
+ /*
+ * regular parent of child
+ */
+ printf( "%6.6s %5.5s %7.2f %11.2f %7d/%-7d " ,
+ "" , "" ,
+ arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
+ arcp -> arc_count , cycleheadp -> npropcall );
+ printname( parentp );
+ printf( "\n" );
+ }
+ }
+}
+
+printchildren( parentp )
+ nltype *parentp;
+{
+ nltype *childp;
+ arctype *arcp;
+
+ sortchildren( parentp );
+ arcp = parentp -> children;
+ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
+ childp = arcp -> arc_childp;
+ if ( childp == parentp || ( arcp -> arc_flags & DEADARC ) ||
+ ( childp->cycleno != 0 && childp->cycleno == parentp->cycleno ) ) {
+ /*
+ * self call or call to sibling
+ */
+ printf( "%6.6s %5.5s %7.7s %11.11s %7d %7.7s " ,
+ "" , "" , "" , "" , arcp -> arc_count , "" );
+ printname( childp );
+ printf( "\n" );
+ } else {
+ /*
+ * regular child of parent
+ */
+ printf( "%6.6s %5.5s %7.2f %11.2f %7d/%-7d " ,
+ "" , "" ,
+ arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
+ arcp -> arc_count , childp -> cyclehead -> npropcall );
+ printname( childp );
+ printf( "\n" );
+ }
+ }
+}
+
+printname( selfp )
+ nltype *selfp;
+{
+
+ if ( selfp -> name != 0 ) {
+ printf( "%s" , selfp -> name );
+# ifdef DEBUG
+ if ( debug & DFNDEBUG ) {
+ printf( "{%d} " , selfp -> toporder );
+ }
+ if ( debug & PROPDEBUG ) {
+ printf( "%5.2f%% " , selfp -> propfraction );
+ }
+# endif DEBUG
+ }
+ if ( selfp -> cycleno != 0 ) {
+ printf( " <cycle %d>" , selfp -> cycleno );
+ }
+ if ( selfp -> index != 0 ) {
+ if ( selfp -> printflag ) {
+ printf( " [%d]" , selfp -> index );
+ } else {
+ printf( " (%d)" , selfp -> index );
+ }
+ }
+}
+
+sortchildren( parentp )
+ nltype *parentp;
+{
+ arctype *arcp;
+ arctype *detachedp;
+ arctype sorted;
+ arctype *prevp;
+
+ /*
+ * unlink children from parent,
+ * then insertion sort back on to sorted's children.
+ * *arcp the arc you have detached and are inserting.
+ * *detachedp the rest of the arcs to be sorted.
+ * sorted arc list onto which you insertion sort.
+ * *prevp arc before the arc you are comparing.
+ */
+ sorted.arc_childlist = 0;
+ for ( (arcp = parentp -> children)&&(detachedp = arcp -> arc_childlist);
+ arcp ;
+ (arcp = detachedp)&&(detachedp = detachedp -> arc_childlist)) {
+ /*
+ * consider *arcp as disconnected
+ * insert it into sorted
+ */
+ for ( prevp = &sorted ;
+ prevp -> arc_childlist ;
+ prevp = prevp -> arc_childlist ) {
+ if ( arccmp( arcp , prevp -> arc_childlist ) != LESSTHAN ) {
+ break;
+ }
+ }
+ arcp -> arc_childlist = prevp -> arc_childlist;
+ prevp -> arc_childlist = arcp;
+ }
+ /*
+ * reattach sorted children to parent
+ */
+ parentp -> children = sorted.arc_childlist;
+}
+
+sortparents( childp )
+ nltype *childp;
+{
+ arctype *arcp;
+ arctype *detachedp;
+ arctype sorted;
+ arctype *prevp;
+
+ /*
+ * unlink parents from child,
+ * then insertion sort back on to sorted's parents.
+ * *arcp the arc you have detached and are inserting.
+ * *detachedp the rest of the arcs to be sorted.
+ * sorted arc list onto which you insertion sort.
+ * *prevp arc before the arc you are comparing.
+ */
+ sorted.arc_parentlist = 0;
+ for ( (arcp = childp -> parents)&&(detachedp = arcp -> arc_parentlist);
+ arcp ;
+ (arcp = detachedp)&&(detachedp = detachedp -> arc_parentlist)) {
+ /*
+ * consider *arcp as disconnected
+ * insert it into sorted
+ */
+ for ( prevp = &sorted ;
+ prevp -> arc_parentlist ;
+ prevp = prevp -> arc_parentlist ) {
+ if ( arccmp( arcp , prevp -> arc_parentlist ) != GREATERTHAN ) {
+ break;
+ }
+ }
+ arcp -> arc_parentlist = prevp -> arc_parentlist;
+ prevp -> arc_parentlist = arcp;
+ }
+ /*
+ * reattach sorted arcs to child
+ */
+ childp -> parents = sorted.arc_parentlist;
+}
+
+ /*
+ * print a cycle header
+ */
+printcycle( cyclep )
+ nltype *cyclep;
+{
+ char kirkbuffer[ BUFSIZ ];
+
+ sprintf( kirkbuffer , "[%d]" , cyclep -> index );
+ printf( "%-6.6s %5.1f %7.2f %11.2f %7d" ,
+ kirkbuffer ,
+ 100 * ( cyclep -> propself + cyclep -> propchild ) / printtime ,
+ cyclep -> propself / hz ,
+ cyclep -> propchild / hz ,
+ cyclep -> npropcall );
+ if ( cyclep -> selfcalls != 0 ) {
+ printf( "+%-7d" , cyclep -> selfcalls );
+ } else {
+ printf( " %7.7s" , "" );
+ }
+ printf( " <cycle %d as a whole>\t[%d]\n" ,
+ cyclep -> cycleno , cyclep -> index );
+}
+
+ /*
+ * print the members of a cycle
+ */
+printmembers( cyclep )
+ nltype *cyclep;
+{
+ nltype *memberp;
+
+ sortmembers( cyclep );
+ for ( memberp = cyclep -> cnext ; memberp ; memberp = memberp -> cnext ) {
+ printf( "%6.6s %5.5s %7.2f %11.2f %7d" ,
+ "" , "" , memberp -> propself / hz , memberp -> propchild / hz ,
+ memberp -> npropcall );
+ if ( memberp -> selfcalls != 0 ) {
+ printf( "+%-7d" , memberp -> selfcalls );
+ } else {
+ printf( " %7.7s" , "" );
+ }
+ printf( " " );
+ printname( memberp );
+ printf( "\n" );
+ }
+}
+
+ /*
+ * sort members of a cycle
+ */
+sortmembers( cyclep )
+ nltype *cyclep;
+{
+ nltype *todo;
+ nltype *doing;
+ nltype *prev;
+
+ /*
+ * detach cycle members from cyclehead,
+ * and insertion sort them back on.
+ */
+ todo = cyclep -> cnext;
+ cyclep -> cnext = 0;
+ for ( (doing = todo)&&(todo = doing -> cnext);
+ doing ;
+ (doing = todo )&&(todo = doing -> cnext )){
+ for ( prev = cyclep ; prev -> cnext ; prev = prev -> cnext ) {
+ if ( membercmp( doing , prev -> cnext ) == GREATERTHAN ) {
+ break;
+ }
+ }
+ doing -> cnext = prev -> cnext;
+ prev -> cnext = doing;
+ }
+}
+
+ /*
+ * major sort is on propself + propchild,
+ * next is sort on ncalls + selfcalls.
+ */
+int
+membercmp( this , that )
+ nltype *this;
+ nltype *that;
+{
+ double thistime = this -> propself + this -> propchild;
+ double thattime = that -> propself + that -> propchild;
+ long thiscalls = this -> ncall + this -> selfcalls;
+ long thatcalls = that -> ncall + that -> selfcalls;
+
+ if ( thistime > thattime ) {
+ return GREATERTHAN;
+ }
+ if ( thistime < thattime ) {
+ return LESSTHAN;
+ }
+ if ( thiscalls > thatcalls ) {
+ return GREATERTHAN;
+ }
+ if ( thiscalls < thatcalls ) {
+ return LESSTHAN;
+ }
+ return EQUALTO;
+}
+ /*
+ * compare two arcs to/from the same child/parent.
+ * - if one arc is a self arc, it's least.
+ * - if one arc is within a cycle, it's less than.
+ * - if both arcs are within a cycle, compare arc counts.
+ * - if neither arc is within a cycle, compare with
+ * arc_time + arc_childtime as major key
+ * arc count as minor key
+ */
+int
+arccmp( thisp , thatp )
+ arctype *thisp;
+ arctype *thatp;
+{
+ nltype *thisparentp = thisp -> arc_parentp;
+ nltype *thischildp = thisp -> arc_childp;
+ nltype *thatparentp = thatp -> arc_parentp;
+ nltype *thatchildp = thatp -> arc_childp;
+ double thistime;
+ double thattime;
+
+# ifdef DEBUG
+ if ( debug & TIMEDEBUG ) {
+ printf( "[arccmp] " );
+ printname( thisparentp );
+ printf( " calls " );
+ printname ( thischildp );
+ printf( " %f + %f %d/%d\n" ,
+ thisp -> arc_time , thisp -> arc_childtime ,
+ thisp -> arc_count , thischildp -> ncall );
+ printf( "[arccmp] " );
+ printname( thatparentp );
+ printf( " calls " );
+ printname( thatchildp );
+ printf( " %f + %f %d/%d\n" ,
+ thatp -> arc_time , thatp -> arc_childtime ,
+ thatp -> arc_count , thatchildp -> ncall );
+ printf( "\n" );
+ }
+# endif DEBUG
+ if ( thisparentp == thischildp ) {
+ /* this is a self call */
+ return LESSTHAN;
+ }
+ if ( thatparentp == thatchildp ) {
+ /* that is a self call */
+ return GREATERTHAN;
+ }
+ if ( thisparentp -> cycleno != 0 && thischildp -> cycleno != 0 &&
+ thisparentp -> cycleno == thischildp -> cycleno ) {
+ /* this is a call within a cycle */
+ if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
+ thatparentp -> cycleno == thatchildp -> cycleno ) {
+ /* that is a call within the cycle, too */
+ if ( thisp -> arc_count < thatp -> arc_count ) {
+ return LESSTHAN;
+ }
+ if ( thisp -> arc_count > thatp -> arc_count ) {
+ return GREATERTHAN;
+ }
+ return EQUALTO;
+ } else {
+ /* that isn't a call within the cycle */
+ return LESSTHAN;
+ }
+ } else {
+ /* this isn't a call within a cycle */
+ if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
+ thatparentp -> cycleno == thatchildp -> cycleno ) {
+ /* that is a call within a cycle */
+ return GREATERTHAN;
+ } else {
+ /* neither is a call within a cycle */
+ thistime = thisp -> arc_time + thisp -> arc_childtime;
+ thattime = thatp -> arc_time + thatp -> arc_childtime;
+ if ( thistime < thattime )
+ return LESSTHAN;
+ if ( thistime > thattime )
+ return GREATERTHAN;
+ if ( thisp -> arc_count < thatp -> arc_count )
+ return LESSTHAN;
+ if ( thisp -> arc_count > thatp -> arc_count )
+ return GREATERTHAN;
+ return EQUALTO;
+ }
+ }
+}
+
+printblurb( blurbname )
+ char *blurbname;
+{
+ FILE *blurbfile;
+ int input;
+
+ blurbfile = fopen( blurbname , "r" );
+ if ( blurbfile == NULL ) {
+ perror( blurbname );
+ return;
+ }
+ while ( ( input = getc( blurbfile ) ) != EOF ) {
+ putchar( input );
+ }
+ fclose( blurbfile );
+}
+
+int
+namecmp( npp1 , npp2 )
+ nltype **npp1, **npp2;
+{
+ return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
+}
+
+printindex()
+{
+ nltype **namesortnlp;
+ register nltype *nlp;
+ int index, nnames, todo, i, j;
+ char peterbuffer[ BUFSIZ ];
+
+ /*
+ * Now, sort regular function name alphbetically
+ * to create an index.
+ */
+ namesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
+ if ( namesortnlp == (nltype **) 0 ) {
+ fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
+ }
+ for ( index = 0 , nnames = 0 ; index < nname ; index++ ) {
+ if ( zflag == 0 && nl[index].ncall == 0 && nl[index].time == 0 )
+ continue;
+ namesortnlp[nnames++] = &nl[index];
+ }
+ qsort( namesortnlp , nnames , sizeof(nltype *) , namecmp );
+ for ( index = 1 , todo = nnames ; index <= ncycle ; index++ ) {
+ namesortnlp[todo++] = &cyclenl[index];
+ }
+ printf( "\f\nIndex by function name\n\n" );
+ index = ( todo + 2 ) / 3;
+ for ( i = 0; i < index ; i++ ) {
+ for ( j = i; j < todo ; j += index ) {
+ nlp = namesortnlp[ j ];
+ if ( nlp -> printflag ) {
+ sprintf( peterbuffer , "[%d]" , nlp -> index );
+ } else {
+ sprintf( peterbuffer , "(%d)" , nlp -> index );
+ }
+ if ( j < nnames ) {
+ printf( "%6.6s %-19.19s" , peterbuffer , nlp -> name );
+ } else {
+ printf( "%6.6s " , peterbuffer );
+ sprintf( peterbuffer , "<cycle %d>" , nlp -> cycleno );
+ printf( "%-19.19s" , peterbuffer );
+ }
+ }
+ printf( "\n" );
+ }
+ free( namesortnlp );
+}
diff --git a/usr.bin/gprof/printlist.c b/usr.bin/gprof/printlist.c
new file mode 100644
index 0000000..f29ecc9
--- /dev/null
+++ b/usr.bin/gprof/printlist.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printlist.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * these are the lists of names:
+ * there is the list head and then the listname
+ * is a pointer to the list head
+ * (for ease of passing to stringlist functions).
+ */
+struct stringlist kfromhead = { 0 , 0 };
+struct stringlist *kfromlist = &kfromhead;
+struct stringlist ktohead = { 0 , 0 };
+struct stringlist *ktolist = &ktohead;
+struct stringlist fhead = { 0 , 0 };
+struct stringlist *flist = &fhead;
+struct stringlist Fhead = { 0 , 0 };
+struct stringlist *Flist = &Fhead;
+struct stringlist ehead = { 0 , 0 };
+struct stringlist *elist = &ehead;
+struct stringlist Ehead = { 0 , 0 };
+struct stringlist *Elist = &Ehead;
+
+addlist( listp , funcname )
+ struct stringlist *listp;
+ char *funcname;
+{
+ struct stringlist *slp;
+
+ slp = (struct stringlist *) malloc( sizeof(struct stringlist));
+ if ( slp == (struct stringlist *) 0 ) {
+ fprintf( stderr, "gprof: ran out room for printlist\n" );
+ done();
+ }
+ slp -> next = listp -> next;
+ slp -> string = funcname;
+ listp -> next = slp;
+}
+
+bool
+onlist( listp , funcname )
+ struct stringlist *listp;
+ char *funcname;
+{
+ struct stringlist *slp;
+
+ for ( slp = listp -> next ; slp ; slp = slp -> next ) {
+ if ( ! strcmp( slp -> string , funcname ) ) {
+ return TRUE;
+ }
+ if ( funcname[0] == '_' && ! strcmp( slp -> string , &funcname[1] ) ) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/usr.bin/gprof/sparc.c b/usr.bin/gprof/sparc.c
new file mode 100644
index 0000000..513a525
--- /dev/null
+++ b/usr.bin/gprof/sparc.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sparc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * a namelist entry to be the child of indirect calls
+ */
+nltype indirectchild = {
+ "(*)" , /* the name */
+ (unsigned long) 0 , /* the pc entry point */
+ (unsigned long) 0 , /* entry point aligned to histogram */
+ (double) 0.0 , /* ticks in this routine */
+ (double) 0.0 , /* cumulative ticks in children */
+ (long) 0 , /* how many times called */
+ (long) 0 , /* times called by live arcs */
+ (long) 0 , /* how many calls to self */
+ (double) 1.0 , /* propagation fraction */
+ (double) 0.0 , /* self propagation time */
+ (double) 0.0 , /* child propagation time */
+ (short) 0 , /* print flag */
+ (short) 0 , /* flags */
+ (int) 0 , /* index in the graph list */
+ (int) 0 , /* graph call chain top-sort order */
+ (int) 0 , /* internal number of cycle on */
+ (int) 0 , /* number of live parent arcs */
+ (struct nl *) &indirectchild , /* pointer to head of cycle */
+ (struct nl *) 0 , /* pointer to next member of cycle */
+ (arctype *) 0 , /* list of caller arcs */
+ (arctype *) 0 /* list of callee arcs */
+};
+
+findcall(parentp, p_lowpc, p_highpc)
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+ register u_long pc;
+ nltype *childp;
+ unsigned long destpc;
+ register long op;
+ register int off;
+
+ if (textspace == 0)
+ return;
+ if (p_lowpc < s_lowpc)
+ p_lowpc = s_lowpc;
+ if (p_highpc > s_highpc)
+ p_highpc = s_highpc;
+
+ for (pc = p_lowpc; pc < p_highpc; pc += 4) {
+ off = pc - s_lowpc;
+ op = *(u_long *)&textspace[off];
+ if ((op & 0xc0000000) == 0x40000000) {
+ /*
+ * a pc relative call insn -- check that this
+ * is the address of a function.
+ */
+ off = (op & 0x3fffffff) << 2;
+ destpc = pc + off;
+ if (destpc >= s_lowpc && destpc <= s_highpc) {
+ childp = nllookup(destpc);
+ if (childp != 0 && childp->value == destpc)
+ addarc(parentp, childp, 0L);
+ }
+ } else if ((op & 0xfff80000) == 0x9fc00000)
+ /*
+ * A jmpl with rd = 15 (%o7) -- an indirect call.
+ */
+ addarc(parentp, &indirectchild, 0L);
+ }
+}
diff --git a/usr.bin/gprof/sparc.h b/usr.bin/gprof/sparc.h
new file mode 100644
index 0000000..e2455ea
--- /dev/null
+++ b/usr.bin/gprof/sparc.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sparc.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 0
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+enum opermodes { dummy };
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/tahoe.c b/usr.bin/gprof/tahoe.c
new file mode 100644
index 0000000..839ee29
--- /dev/null
+++ b/usr.bin/gprof/tahoe.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tahoe.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * a namelist entry to be the child of indirect callf
+ */
+nltype indirectchild = {
+ "(*)" , /* the name */
+ (unsigned long) 0 , /* the pc entry point */
+ (unsigned long) 0 , /* entry point aligned to histogram */
+ (double) 0.0 , /* ticks in this routine */
+ (double) 0.0 , /* cumulative ticks in children */
+ (long) 0 , /* how many times called */
+ (long) 0 , /* how many calls to self */
+ (double) 1.0 , /* propagation fraction */
+ (double) 0.0 , /* self propagation time */
+ (double) 0.0 , /* child propagation time */
+ (bool) 0 , /* print flag */
+ (int) 0 , /* index in the graph list */
+ (int) 0 , /* graph call chain top-sort order */
+ (int) 0 , /* internal number of cycle on */
+ (struct nl *) &indirectchild , /* pointer to head of cycle */
+ (struct nl *) 0 , /* pointer to next member of cycle */
+ (arctype *) 0 , /* list of caller arcs */
+ (arctype *) 0 /* list of callee arcs */
+ };
+
+operandenum
+operandmode( modep )
+ unsigned char *modep;
+{
+ long usesreg = ((long)*modep) & 0xf;
+
+ switch ( ((long)*modep) >> 4 ) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return literal;
+ case 4:
+ return indexed;
+ case 5:
+ return reg;
+ case 6:
+ return regdef;
+ case 7:
+ return autodec;
+ case 8:
+ return ( usesreg != 0xe ? autoinc : immediate );
+ case 9:
+ return ( usesreg != PC ? autoincdef : absolute );
+ case 10:
+ return ( usesreg != PC ? bytedisp : byterel );
+ case 11:
+ return ( usesreg != PC ? bytedispdef : bytereldef );
+ case 12:
+ return ( usesreg != PC ? worddisp : wordrel );
+ case 13:
+ return ( usesreg != PC ? worddispdef : wordreldef );
+ case 14:
+ return ( usesreg != PC ? longdisp : longrel );
+ case 15:
+ return ( usesreg != PC ? longdispdef : longreldef );
+ }
+ /* NOTREACHED */
+}
+
+char *
+operandname( mode )
+ operandenum mode;
+{
+
+ switch ( mode ) {
+ case literal:
+ return "literal";
+ case indexed:
+ return "indexed";
+ case reg:
+ return "register";
+ case regdef:
+ return "register deferred";
+ case autodec:
+ return "autodecrement";
+ case autoinc:
+ return "autoincrement";
+ case autoincdef:
+ return "autoincrement deferred";
+ case bytedisp:
+ return "byte displacement";
+ case bytedispdef:
+ return "byte displacement deferred";
+ case byterel:
+ return "byte relative";
+ case bytereldef:
+ return "byte relative deferred";
+ case worddisp:
+ return "word displacement";
+ case worddispdef:
+ return "word displacement deferred";
+ case wordrel:
+ return "word relative";
+ case wordreldef:
+ return "word relative deferred";
+ case immediate:
+ return "immediate";
+ case absolute:
+ return "absolute";
+ case longdisp:
+ return "long displacement";
+ case longdispdef:
+ return "long displacement deferred";
+ case longrel:
+ return "long relative";
+ case longreldef:
+ return "long relative deferred";
+ }
+ /* NOTREACHED */
+}
+
+long
+operandlength( modep )
+ unsigned char *modep;
+{
+
+ switch ( operandmode( modep ) ) {
+ case literal:
+ case reg:
+ case regdef:
+ case autodec:
+ case autoinc:
+ case autoincdef:
+ return 1;
+ case bytedisp:
+ case bytedispdef:
+ case byterel:
+ case bytereldef:
+ return 2;
+ case worddisp:
+ case worddispdef:
+ case wordrel:
+ case wordreldef:
+ return 3;
+ case immediate:
+ case absolute:
+ case longdisp:
+ case longdispdef:
+ case longrel:
+ case longreldef:
+ return 5;
+ case indexed:
+ return 1+operandlength( modep + 1 );
+ }
+ /* NOTREACHED */
+}
+
+unsigned long
+reladdr( modep )
+ char *modep;
+{
+ operandenum mode = operandmode( modep );
+ char *cp;
+ short *sp;
+ long *lp;
+ int i;
+ long value = 0;
+
+ cp = modep;
+ cp += 1; /* skip over the mode */
+ switch ( mode ) {
+ default:
+ fprintf( stderr , "[reladdr] not relative address\n" );
+ return (unsigned long) modep;
+ case byterel:
+ return (unsigned long) ( cp + sizeof *cp + *cp );
+ case wordrel:
+ for (i = 0; i < sizeof *sp; i++)
+ value = (value << 8) + (cp[i] & 0xff);
+ return (unsigned long) ( cp + sizeof *sp + value );
+ case longrel:
+ for (i = 0; i < sizeof *lp; i++)
+ value = (value << 8) + (cp[i] & 0xff);
+ return (unsigned long) ( cp + sizeof *lp + value );
+ }
+}
+
+findcall( parentp , p_lowpc , p_highpc )
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+ unsigned char *instructp;
+ long length;
+ nltype *childp;
+ operandenum mode;
+ operandenum firstmode;
+ unsigned long destpc;
+
+ if ( textspace == 0 ) {
+ return;
+ }
+ if ( p_lowpc < s_lowpc ) {
+ p_lowpc = s_lowpc;
+ }
+ if ( p_highpc > s_highpc ) {
+ p_highpc = s_highpc;
+ }
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall] %s: 0x%x to 0x%x\n" ,
+ parentp -> name , p_lowpc , p_highpc );
+ }
+# endif DEBUG
+ for ( instructp = textspace + p_lowpc ;
+ instructp < textspace + p_highpc ;
+ instructp += length ) {
+ length = 1;
+ if ( *instructp == CALLF ) {
+ /*
+ * maybe a callf, better check it out.
+ * skip the count of the number of arguments.
+ */
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\t0x%x:callf" , instructp - textspace );
+ }
+# endif DEBUG
+ firstmode = operandmode( instructp+length );
+ switch ( firstmode ) {
+ case literal:
+ case immediate:
+ break;
+ default:
+ goto botched;
+ }
+ length += operandlength( instructp+length );
+ mode = operandmode( instructp + length );
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "\tfirst operand is %s", operandname( firstmode ) );
+ printf( "\tsecond operand is %s\n" , operandname( mode ) );
+ }
+# endif DEBUG
+ switch ( mode ) {
+ case regdef:
+ case bytedispdef:
+ case worddispdef:
+ case longdispdef:
+ case bytereldef:
+ case wordreldef:
+ case longreldef:
+ /*
+ * indirect call: call through pointer
+ * either *d(r) as a parameter or local
+ * (r) as a return value
+ * *f as a global pointer
+ * [are there others that we miss?,
+ * e.g. arrays of pointers to functions???]
+ */
+ addarc( parentp , &indirectchild , (long) 0 );
+ length += operandlength( instructp + length );
+ continue;
+ case byterel:
+ case wordrel:
+ case longrel:
+ /*
+ * regular pc relative addressing
+ * check that this is the address of
+ * a function.
+ */
+ destpc = reladdr( instructp+length )
+ - (unsigned long) textspace;
+ if ( destpc >= s_lowpc && destpc <= s_highpc ) {
+ childp = nllookup( destpc );
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\tdestpc 0x%x" , destpc );
+ printf( " childp->name %s" , childp -> name );
+ printf( " childp->value 0x%x\n" ,
+ childp -> value );
+ }
+# endif DEBUG
+ if ( childp -> value == destpc ) {
+ /*
+ * a hit
+ */
+ addarc( parentp , childp , (long) 0 );
+ length += operandlength( instructp + length );
+ continue;
+ }
+ goto botched;
+ }
+ /*
+ * else:
+ * it looked like a callf,
+ * but it wasn't to anywhere.
+ */
+ goto botched;
+ default:
+ botched:
+ /*
+ * something funny going on.
+ */
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\tbut it's a botch\n" );
+ }
+# endif DEBUG
+ length = 1;
+ continue;
+ }
+ }
+ }
+}
diff --git a/usr.bin/gprof/tahoe.h b/usr.bin/gprof/tahoe.h
new file mode 100644
index 0000000..d82359a
--- /dev/null
+++ b/usr.bin/gprof/tahoe.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tahoe.h 8.1 (Berkeley) 6/6/93
+ */
+
+ /*
+ * opcode of the `callf' instruction
+ */
+#define CALLF 0xfe
+
+ /*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 2
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+ /*
+ * register for pc relative addressing
+ */
+#define PC 0xf
+
+enum opermodes {
+ literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
+ bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
+ immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
+ longrel, longreldef
+};
+typedef enum opermodes operandenum;
diff --git a/usr.bin/gprof/vax.c b/usr.bin/gprof/vax.c
new file mode 100644
index 0000000..03e3495
--- /dev/null
+++ b/usr.bin/gprof/vax.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vax.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "gprof.h"
+
+ /*
+ * a namelist entry to be the child of indirect calls
+ */
+nltype indirectchild = {
+ "(*)" , /* the name */
+ (unsigned long) 0 , /* the pc entry point */
+ (unsigned long) 0 , /* entry point aligned to histogram */
+ (double) 0.0 , /* ticks in this routine */
+ (double) 0.0 , /* cumulative ticks in children */
+ (long) 0 , /* how many times called */
+ (long) 0 , /* how many calls to self */
+ (double) 1.0 , /* propagation fraction */
+ (double) 0.0 , /* self propagation time */
+ (double) 0.0 , /* child propagation time */
+ (bool) 0 , /* print flag */
+ (int) 0 , /* index in the graph list */
+ (int) 0 , /* graph call chain top-sort order */
+ (int) 0 , /* internal number of cycle on */
+ (struct nl *) &indirectchild , /* pointer to head of cycle */
+ (struct nl *) 0 , /* pointer to next member of cycle */
+ (arctype *) 0 , /* list of caller arcs */
+ (arctype *) 0 /* list of callee arcs */
+ };
+
+operandenum
+operandmode( modep )
+ struct modebyte *modep;
+{
+ long usesreg = modep -> regfield;
+
+ switch ( modep -> modefield ) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return literal;
+ case 4:
+ return indexed;
+ case 5:
+ return reg;
+ case 6:
+ return regdef;
+ case 7:
+ return autodec;
+ case 8:
+ return ( usesreg != PC ? autoinc : immediate );
+ case 9:
+ return ( usesreg != PC ? autoincdef : absolute );
+ case 10:
+ return ( usesreg != PC ? bytedisp : byterel );
+ case 11:
+ return ( usesreg != PC ? bytedispdef : bytereldef );
+ case 12:
+ return ( usesreg != PC ? worddisp : wordrel );
+ case 13:
+ return ( usesreg != PC ? worddispdef : wordreldef );
+ case 14:
+ return ( usesreg != PC ? longdisp : longrel );
+ case 15:
+ return ( usesreg != PC ? longdispdef : longreldef );
+ }
+ /* NOTREACHED */
+}
+
+char *
+operandname( mode )
+ operandenum mode;
+{
+
+ switch ( mode ) {
+ case literal:
+ return "literal";
+ case indexed:
+ return "indexed";
+ case reg:
+ return "register";
+ case regdef:
+ return "register deferred";
+ case autodec:
+ return "autodecrement";
+ case autoinc:
+ return "autoincrement";
+ case autoincdef:
+ return "autoincrement deferred";
+ case bytedisp:
+ return "byte displacement";
+ case bytedispdef:
+ return "byte displacement deferred";
+ case byterel:
+ return "byte relative";
+ case bytereldef:
+ return "byte relative deferred";
+ case worddisp:
+ return "word displacement";
+ case worddispdef:
+ return "word displacement deferred";
+ case wordrel:
+ return "word relative";
+ case wordreldef:
+ return "word relative deferred";
+ case immediate:
+ return "immediate";
+ case absolute:
+ return "absolute";
+ case longdisp:
+ return "long displacement";
+ case longdispdef:
+ return "long displacement deferred";
+ case longrel:
+ return "long relative";
+ case longreldef:
+ return "long relative deferred";
+ }
+ /* NOTREACHED */
+}
+
+long
+operandlength( modep )
+ struct modebyte *modep;
+{
+
+ switch ( operandmode( modep ) ) {
+ case literal:
+ case reg:
+ case regdef:
+ case autodec:
+ case autoinc:
+ case autoincdef:
+ return 1;
+ case bytedisp:
+ case bytedispdef:
+ case byterel:
+ case bytereldef:
+ return 2;
+ case worddisp:
+ case worddispdef:
+ case wordrel:
+ case wordreldef:
+ return 3;
+ case immediate:
+ case absolute:
+ case longdisp:
+ case longdispdef:
+ case longrel:
+ case longreldef:
+ return 5;
+ case indexed:
+ return 1+operandlength( (struct modebyte *) ((char *) modep) + 1 );
+ }
+ /* NOTREACHED */
+}
+
+unsigned long
+reladdr( modep )
+ struct modebyte *modep;
+{
+ operandenum mode = operandmode( modep );
+ char *cp;
+ short *sp;
+ long *lp;
+
+ cp = (char *) modep;
+ cp += 1; /* skip over the mode */
+ switch ( mode ) {
+ default:
+ fprintf( stderr , "[reladdr] not relative address\n" );
+ return (unsigned long) modep;
+ case byterel:
+ return (unsigned long) ( cp + sizeof *cp + *cp );
+ case wordrel:
+ sp = (short *) cp;
+ return (unsigned long) ( cp + sizeof *sp + *sp );
+ case longrel:
+ lp = (long *) cp;
+ return (unsigned long) ( cp + sizeof *lp + *lp );
+ }
+}
+
+findcall( parentp , p_lowpc , p_highpc )
+ nltype *parentp;
+ unsigned long p_lowpc;
+ unsigned long p_highpc;
+{
+ unsigned char *instructp;
+ long length;
+ nltype *childp;
+ operandenum mode;
+ operandenum firstmode;
+ unsigned long destpc;
+
+ if ( textspace == 0 ) {
+ return;
+ }
+ if ( p_lowpc < s_lowpc ) {
+ p_lowpc = s_lowpc;
+ }
+ if ( p_highpc > s_highpc ) {
+ p_highpc = s_highpc;
+ }
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall] %s: 0x%x to 0x%x\n" ,
+ parentp -> name , p_lowpc , p_highpc );
+ }
+# endif DEBUG
+ for ( instructp = textspace + p_lowpc ;
+ instructp < textspace + p_highpc ;
+ instructp += length ) {
+ length = 1;
+ if ( *instructp == CALLS ) {
+ /*
+ * maybe a calls, better check it out.
+ * skip the count of the number of arguments.
+ */
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\t0x%x:calls" , instructp - textspace );
+ }
+# endif DEBUG
+ firstmode = operandmode( (struct modebyte *) (instructp+length) );
+ switch ( firstmode ) {
+ case literal:
+ case immediate:
+ break;
+ default:
+ goto botched;
+ }
+ length += operandlength( (struct modebyte *) (instructp+length) );
+ mode = operandmode( (struct modebyte *) ( instructp + length ) );
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "\tfirst operand is %s", operandname( firstmode ) );
+ printf( "\tsecond operand is %s\n" , operandname( mode ) );
+ }
+# endif DEBUG
+ switch ( mode ) {
+ case regdef:
+ case bytedispdef:
+ case worddispdef:
+ case longdispdef:
+ case bytereldef:
+ case wordreldef:
+ case longreldef:
+ /*
+ * indirect call: call through pointer
+ * either *d(r) as a parameter or local
+ * (r) as a return value
+ * *f as a global pointer
+ * [are there others that we miss?,
+ * e.g. arrays of pointers to functions???]
+ */
+ addarc( parentp , &indirectchild , (long) 0 );
+ length += operandlength(
+ (struct modebyte *) ( instructp + length ) );
+ continue;
+ case byterel:
+ case wordrel:
+ case longrel:
+ /*
+ * regular pc relative addressing
+ * check that this is the address of
+ * a function.
+ */
+ destpc = reladdr( (struct modebyte *) (instructp+length) )
+ - (unsigned long) textspace;
+ if ( destpc >= s_lowpc && destpc <= s_highpc ) {
+ childp = nllookup( destpc );
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\tdestpc 0x%x" , destpc );
+ printf( " childp->name %s" , childp -> name );
+ printf( " childp->value 0x%x\n" ,
+ childp -> value );
+ }
+# endif DEBUG
+ if ( childp -> value == destpc ) {
+ /*
+ * a hit
+ */
+ addarc( parentp , childp , (long) 0 );
+ length += operandlength( (struct modebyte *)
+ ( instructp + length ) );
+ continue;
+ }
+ goto botched;
+ }
+ /*
+ * else:
+ * it looked like a calls,
+ * but it wasn't to anywhere.
+ */
+ goto botched;
+ default:
+ botched:
+ /*
+ * something funny going on.
+ */
+# ifdef DEBUG
+ if ( debug & CALLDEBUG ) {
+ printf( "[findcall]\tbut it's a botch\n" );
+ }
+# endif DEBUG
+ length = 1;
+ continue;
+ }
+ }
+ }
+}
diff --git a/usr.bin/gprof/vax.h b/usr.bin/gprof/vax.h
new file mode 100644
index 0000000..33c5822
--- /dev/null
+++ b/usr.bin/gprof/vax.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vax.h 8.1 (Berkeley) 6/6/93
+ */
+
+ /*
+ * opcode of the `calls' instruction
+ */
+#define CALLS 0xfb
+
+ /*
+ * offset (in bytes) of the code from the entry address of a routine.
+ * (see asgnsamples for use and explanation.)
+ */
+#define OFFSET_OF_CODE 2
+#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+
+ /*
+ * register for pc relative addressing
+ */
+#define PC 0xf
+
+enum opermodes {
+ literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
+ bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
+ immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
+ longrel, longreldef
+};
+typedef enum opermodes operandenum;
+
+struct modebyte {
+ unsigned int regfield:4;
+ unsigned int modefield:4;
+};
+
diff --git a/usr.bin/grep/egrep/Makefile b/usr.bin/grep/egrep/Makefile
new file mode 100644
index 0000000..5df3e8f
--- /dev/null
+++ b/usr.bin/grep/egrep/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/27/93
+
+# -DSLOWSYS invoke xread() for system time quirk on PDP, others?
+# -DNOKANJI default is for Japanese Unix. undef only for raw
+# parity-marked search capability, not standard w/grep.
+# -DCHINESE for systems using EUC Chinese2 codes
+# -Dstrrchr=rindex, -Dstrchr=index as necessary
+
+PROG= egrep
+CFLAGS+=-Dstrrchr=rindex -Dstrchr=index -DNOKANJI
+DPADD= ${LIBCOMPAT}
+LDADD= -lcompat # must search compat to get spencers early regexp package
+MAN1= grep.1
+LINKS= ${BINDIR}/egrep ${BINDIR}/grep ${BINDIR}/egrep ${BINDIR}/fgrep
+MLINKS= grep.1 egrep.1 grep.1 fgrep.1
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.bin/grep/egrep/egrep.c b/usr.bin/grep/egrep/egrep.c
new file mode 100644
index 0000000..885965c
--- /dev/null
+++ b/usr.bin/grep/egrep/egrep.c
@@ -0,0 +1,924 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)egrep.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ Hybrid Boyer/Moore/Gosper-assisted 'grep/egrep/fgrep' search, with delta0
+ table as in original paper (CACM, October, 1977). No delta1 or delta2.
+ According to experiment (Horspool, Soft. Prac. Exp., 1982), delta2 is of
+ minimal practical value. However, to improve for worst case input,
+ integrating the improved Galil strategies (Apostolico/Giancarlo, SIAM. J.
+ Comput., Feb. 1986) deserves consideration.
+
+ Method: extract longest metacharacter-free string from expression.
+ this is done using a side-effect from henry spencer's regcomp().
+ use boyer-moore to match such, then pass submatching lines
+ to either regexp() or standard 'egrep', depending on certain
+ criteria within execstrategy() below. [this tradeoff is due
+ to the general slowness of the regexp() nondeterministic
+ machine on complex expressions, as well as the startup time
+ of standard 'egrep' on short files.] alternatively, one may
+ change the vendor-supplied 'egrep' automaton to include
+ boyer-moore directly. see accompanying writeup for discussion
+ of kanji expression treatment.
+
+ late addition: apply trickbag for fast match of simple
+ alternations (sublinear, in common low-cardinality cases).
+ trap fgrep into this lair.
+
+ gnu additions: -f, newline as |, \< and \> [in regexec()], more
+ comments. inspire better dfa exec() strategy.
+ serious testing and help with special cases.
+
+ Algorithm amalgam summary:
+
+ dfa e?grep (aho/thompson)
+ ndfa regexp() (spencer/aho)
+ bmg (boyer/moore/gosper)
+ "superimposed" bmg (jaw)
+ fgrep (aho/corrasick)
+
+ sorry, but the knuth/morris/pratt machine, horspool's
+ "frequentist" code, and the rabin/karp matcher, however cute,
+ just don't cut it for this production.
+
+ James A. Woods Copyright (c) 1986
+ NASA Ames Research Center
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <regexp.h> /* must be henry spencer's version */
+#include <stdio.h>
+#include <ctype.h>
+#include "pathnames.h"
+
+#define MIN(A, B) ((A) > (B) ? (B) : (A))
+
+#ifdef SLOWSYS
+#define read xread
+#endif
+
+#define BUFSIZE 8192 /* make higher for cray */
+#define PATSIZE 6000
+#define LARGE BUFSIZE + PATSIZE
+
+#define NALT 7 /* tied to scanf() size in alternate() */
+#define NMUSH 6 /* loosely relates to expected alt length */
+
+#define FIRSTFEW 33 /* Always do FIRSTFEW matches with regexec() */
+#define PUNTPERCENT 10 /* After FIRSTFEW, if PUNTPERCENT of the input
+ * was processed by regexp(), exec std egrep. */
+#define NL '\n'
+#define EOS '\0'
+#define NONASCII 0200 /* Bit mask for Kanji non-ascii chars */
+#define META "\n^$.[]()?+*|\\" /* egrep meta-characters */
+#define SS2 '\216' /* EUC Katakana (or Chinese2) prefix */
+#define SS3 '\217' /* EUC Kanji2 (or Chinese3) prefix */
+
+extern char *optarg;
+extern int optind;
+char *progname;
+
+int cflag, iflag, eflag, fflag, lflag, nflag; /* SVID flags */
+int sflag, hflag; /* v7, v8, bsd */
+
+int firstflag; /* Stop at first match */
+int grepflag; /* Called as "grep" */
+int fgrepflag; /* Called as "fgrep" */
+int altflag; /* Simple alternation in pattern */
+int boyonly; /* No regexp needed -- all simple */
+int flushflag;
+int grepold, egrepold, fgrepold;
+
+int nalt; /* Number of alternatives */
+int nsuccess; /* 1 for match, 2 for error */
+int altmin; /* Minimum length of all the alternate
+ * strings */
+int firstfile; /* argv index of first file argument */
+int patind; /* argv index of pattern */
+long nmatch; /* Number of matches in this file */
+long incount, counted; /* Amount of input consumed */
+long rxcount; /* Bytes of input processed by regexec() */
+int boyfound; /* accumulated partial matches (tripped by
+ * FIRSTFEW) */
+int prevmatch; /* next three lines aid fast -n */
+long nline, prevnline;
+char *prevloc;
+
+regexp *rspencer;
+char *pattern;
+char *patboy; /* Pattern for simple Boyer-Moore */
+char *patfile; /* Filename containing pattern(s) */
+
+int delta0[256]; /* Boyer-Moore algorithm core */
+char cmap[256]; /* Usually 0-255, but if -i, maps upper to
+ * lower case */
+char str[BUFSIZE + 2];
+int nleftover;
+char linetemp[BUFSIZE];
+char *altpat[NALT]; /* alternation component storage */
+int altlen[NALT];
+short altset[NMUSH + 1][256];
+char preamble[200]; /* match prefix (filename, line no.) */
+
+int fd;
+char *
+strchr(), *strrchr(), *strcpy(), *strncpy(), *strpbrk(), *malloc();
+char *
+grepxlat(), *fold(), *pfile(), *alternate(), *isolate();
+char *gotamatch(), *kanji(), *linesave(), *submatch();
+char **args;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, oflag;
+ int errflag = 0;
+
+ args = argv;
+
+ if ((progname = strrchr(argv[0], '/')) != 0)
+ progname++;
+ else
+ progname = argv[0];
+ if (strcmp(progname, "grep") == 0)
+ grepflag++;
+ else if (strcmp(progname, "fgrep") == 0)
+ fgrepflag++;
+
+ oflag = 0;
+ while ((c = getopt(argc, argv, "bchie:f:lnosvwxy1")) != EOF) {
+ switch (c) {
+
+ case 'f':
+ fflag++;
+ patfile = optarg;
+ continue;
+ case 'b':
+ case 'v':
+ egrepold++; /* boyer-moore of little help here */
+ continue;
+ case 'c':
+ cflag++;
+ continue;
+ case 'e':
+ eflag++;
+ pattern = optarg;
+ continue;
+ case 'h':
+ hflag++;
+ continue;
+ case 'o':
+ oflag++;
+ continue;
+ case '1': /* Stop at very first match */
+ firstflag++; /* spead freaks only */
+ continue;
+ case 'i':
+ iflag++;
+ continue;
+ case 'l':
+ lflag++;
+ continue;
+ case 'n':
+ nflag++;
+ continue;
+ case 's':
+ sflag++;
+ continue;
+ case 'w':
+ case 'y':
+ if (!grepflag)
+ errflag++;
+ grepold++;
+ continue;
+ case 'x': /* needs more work, like -b above */
+ if (!fgrepflag)
+ errflag++;
+ fgrepold++;
+ continue;
+ case '?':
+ errflag++;
+ }
+ }
+ if (errflag || ((argc <= optind) && !fflag && !eflag)) {
+ if (grepflag)
+oops("usage: grep [-bchilnosvwy] [-e] pattern [file ...]");
+ else if (fgrepflag)
+oops("usage: fgrep [-bchilnosvx] {-f patfile | [-e] strings} [file ...]");
+ else /* encourage SVID options, though we provide
+ * others */
+oops("usage: egrep [-bchilnosv] {-f patfile | [-e] pattern} [file ...]");
+ }
+ if (fflag)
+ pattern = pfile(patfile);
+ else if (!eflag) {
+ patind = optind;
+ pattern = argv[optind++];
+ }
+
+ if (!oflag && (argc - optind) <= 1) /* Filename invisible given < 2 files */
+ hflag++;
+ if (pattern[0] == EOS)
+ kernighan(argv); /* same as it ever was */
+ /*
+ * 'grep/egrep' merger -- "old" grep is called to handle: tagged
+ * exprs \( \), word matches \< and \>, -w and -y options, char
+ * classes with '-' at end (egrep bug?), and patterns beginning with
+ * an asterisk (don't ask why). otherwise, characters meaningful to
+ * 'egrep' but not to 'grep' are escaped; the entire expr is then
+ * passed to 'egrep'.
+ */
+ if (grepflag && !grepold) {
+ if (strindex(pattern, "\\(") >= 0 ||
+ strindex(pattern, "\\<") >= 0 ||
+ strindex(pattern, "\\>") >= 0 ||
+ strindex(pattern, "-]") >= 0 ||
+ pattern[0] == '*') /* grep bug */
+ grepold++;
+ else
+ pattern = grepxlat(pattern);
+ }
+ if (grepold || egrepold || fgrepold)
+ kernighan(argv);
+
+ if (iflag)
+ strcpy(pattern, fold(pattern));
+ /*
+ * If the pattern is a plain string, just run boyer-moore. If it
+ * consists of meta-free alternatives, run "superimposed" bmg.
+ * Otherwise, find best string, and compile pattern for regexec().
+ */
+ if (strpbrk(pattern, META) == NULL) { /* do boyer-moore only */
+ boyonly++;
+ patboy = pattern;
+ } else {
+ if ((patboy = alternate(pattern)) != NULL)
+ boyonly++;
+ else {
+ if ((patboy = isolate(pattern)) == NULL)
+ kernighan(argv); /* expr too involved */
+#ifndef NOKANJI
+ for (c = 0; pattern[c] != EOS; c++)
+ if (pattern[c] & NONASCII) /* kanji + meta */
+ kernighan(argv);
+#endif
+ if ((rspencer = regcomp(pattern)) == NULL)
+ oops("regcomp failure");
+ }
+ }
+ gosper(patboy); /* "pre-conditioning is wonderful"
+ * -- v. strassen */
+
+ if ((firstfile = optind) >= argc) {
+ /* Grep standard input */
+ if (lflag) /* We don't know its name! */
+ exit(1);
+ egsecute((char *) NULL);
+ } else {
+ while (optind < argc) {
+ egsecute(argv[optind]);
+ optind++;
+ if (firstflag && (nsuccess == 1))
+ break;
+ }
+ }
+ exit((nsuccess == 2) ? 2 : (nsuccess == 0));
+}
+
+char *
+pfile(pfname) /* absorb expression from file */
+ char *pfname;
+{
+ int fd;
+ struct stat patstat;
+ static char *pat;
+
+ if ((fd = open(pfname, O_RDONLY, 0)) < 0)
+ oops("can't read pattern file");
+ if (fstat(fd, &patstat) != 0)
+ oops("can't stat pattern file");
+ if (patstat.st_size > PATSIZE) {
+ if (fgrepflag) { /* defer to unix version */
+ fgrepold++;
+ return "dummy";
+ } else
+ oops("pattern file too big");
+ }
+ if ((pat = malloc((unsigned) patstat.st_size + 1)) == NULL)
+ oops("out of memory to read pattern file");
+ if (patstat.st_size != read(fd, pat, (int)patstat.st_size))
+ oops("error reading pattern file");
+ (void) close(fd);
+
+ pat[patstat.st_size] = EOS;
+ if (pat[patstat.st_size - 1] == NL) /* NOP for egrep; helps grep */
+ pat[patstat.st_size - 1] = EOS;
+
+ if (nlcount(pat, &pat[patstat.st_size]) > NALT) {
+ if (fgrepflag)
+ fgrepold++; /* "what's it all about, alfie?" */
+ else
+ egrepold++;
+ }
+ return (pat);
+}
+
+egsecute(file)
+ char *file;
+{
+ extern int errno;
+
+ if (file == NULL)
+ fd = 0;
+ else if ((fd = open(file, O_RDONLY, 0)) <= 0) {
+ fprintf(stderr,
+ "%s: %s: %s\n", progname, file, strerror(errno));
+ nsuccess = 2;
+ return;
+ }
+ chimaera(file, patboy);
+
+ if (!boyonly && !flushflag && file != NULL)
+ flushmatches();
+ if (file != NULL)
+ close(fd);
+}
+
+chimaera(file, pat) /* "reach out and boyer-moore search someone" */
+ char *file, *pat; /* -- soon-to-be-popular bumper sticker */
+{
+ register char *k, *strend, *s;
+ register int j, count;
+ register int *deltazero = delta0;
+ int patlen = altmin;
+ char *t;
+
+ nleftover = boyfound = flushflag = 0;
+ nline = 1L;
+ prevmatch = 0;
+ nmatch = counted = rxcount = 0L;
+
+ while ((count = read(fd, str + nleftover, BUFSIZE - nleftover)) > 0) {
+
+ counted += count;
+ strend = linesave(str, count);
+
+ for (k = str + patlen - 1; k < strend;) {
+ /*
+ * for a large class of patterns, upwards of 80% of
+ * match time is spent on the next line. we beat
+ * existing microcode (vax 'matchc') this way.
+ */
+ while ((k += deltazero[*(unsigned char *) k]) < strend);
+ if (k < (str + LARGE))
+ break;
+ k -= LARGE;
+
+ if (altflag) {
+ /*
+ * Parallel Boyer-Moore. Check whether each
+ * of the previous <altmin> chars COULD be
+ * from one of the alternative strings.
+ */
+ s = k - 1;
+ j = altmin;
+ while (altset[--j][(unsigned char)
+ cmap[*(unsigned char *) s--]]);
+ /*
+ * quick test fails. in this life, compare
+ * 'em all. but, a "reverse trie" would
+ * attenuate worst case (linear w/delta2?).
+ */
+ if (--j < 0) {
+ count = nalt - 1;
+ do {
+ s = k;
+ j = altlen[count];
+ t = altpat[count];
+
+ while
+ (cmap[*(unsigned char *) s--]
+ == t[--j]);
+ if (j < 0)
+ break;
+ }
+ while (count--);
+ }
+ } else {
+ /* One string -- check it */
+ j = patlen - 1;
+ s = k - 1;
+ while (cmap[*(unsigned char *) s--] == pat[--j]);
+ }
+ /*
+ * delta-less shortcut for literati. short shrift for
+ * genetic engineers?
+ */
+ if (j >= 0) {
+ k++; /* no match; restart next char */
+ continue;
+ }
+ k = submatch(file, pat, str, strend, k, count);
+ if (k == NULL)
+ return;
+ }
+ if (nflag) {
+ if (prevmatch)
+ nline = prevnline + nlcount(prevloc, k);
+ else
+ nline = nline + nlcount(str, k);
+ prevmatch = 0;
+ }
+ strncpy(str, linetemp, nleftover);
+ }
+ if (cflag) {
+ /* Bug from old grep: -c overrides -h. We fix the bug. */
+ if (!hflag)
+ printf("%s:", file);
+ printf("%ld\n", nmatch);
+ }
+}
+
+char *
+linesave(str, count) /* accumulate partial line at end of buffer */
+ char str[];
+ register int count;
+{
+ register int j;
+
+ count += nleftover;
+ if (count != BUFSIZE && fd != 0)
+ str[count++] = NL; /* insurance for broken last line */
+ str[count] = EOS;
+ for (j = count - 1; str[j] != NL && j >= 0;)
+ j--;
+ /*
+ * break up these lines: long line (> BUFSIZE), last line of file, or
+ * short return from read(), as from tee(1) input
+ */
+ if (j < 0 && (count == (BUFSIZE - nleftover))) {
+ str[count++] = NL;
+ str[count] = EOS;
+ linetemp[0] = EOS;
+ nleftover = 0;
+ return (str + count);
+ } else {
+ nleftover = count - j - 1;
+ strncpy(linetemp, str + j + 1, nleftover);
+ return (str + j);
+ }
+}
+
+/*
+ * Process partial match. First check for mis-aligned Kanji, then match line
+ * against full compiled r.e. if statistics do not warrant handing off to
+ * standard egrep.
+ */
+char *
+submatch(file, pat, str, strend, k, altindex)
+ char file[], pat[], str[];
+ register char *strend, *k;
+ int altindex;
+{
+ register char *s;
+ char *t, c;
+
+ t = k;
+ s = ((altflag) ? k - altlen[altindex] + 1 : k - altmin + 1);
+#ifndef NOKANJI
+ c = ((altflag) ? altpat[altindex][0] : pat[0]);
+ if (c & NONASCII)
+ if ((s = kanji(str, s, k)) == NULL)
+ return (++k); /* reject false kanji */
+#endif
+ do;
+ while (*s != NL && --s >= str);
+ k = s + 1; /* now at line start */
+
+ if (boyonly)
+ return (gotamatch(file, k));
+
+ incount = counted - (strend - k);
+ if (boyfound++ == FIRSTFEW)
+ execstrategy(file);
+
+ s = t;
+ do
+ rxcount++;
+ while (*s++ != NL);
+ *--s = EOS;
+ /*
+ * "quick henry -- the flit" (after theodor geisel)
+ */
+ if (regexec(rspencer, ((iflag) ? fold(k) : k)) == 1) {
+ *s = NL;
+ if (gotamatch(file, k) == NULL)
+ return (NULL);
+ }
+ *s = NL;
+ return (s + 1);
+}
+
+#ifndef NOKANJI
+/*
+ * EUC code disambiguation -- scan backwards to first 7-bit code, while
+ * counting intervening 8-bit codes. If odd, reject unaligned Kanji pattern.
+ * SS2/3 checks are for intermixed Japanase Katakana or Kanji2.
+ */
+char *
+kanji(str, s, k)
+ register char *str, *s, *k;
+{
+ register int j = 0;
+
+ for (s--; s >= str; s--) {
+ if (*s == SS2 || *s == SS3 || (*s & NONASCII) == 0)
+ break;
+ j++;
+ }
+#ifndef CHINESE
+ if (*s == SS2)
+ j -= 1;
+#endif CHINESE
+ return ((j & 01) ? NULL : k);
+}
+#endif
+
+/*
+ * Compute "Boyer-Moore" delta table -- put skip distance in delta0[c]
+ */
+gosper(pattern)
+ char *pattern; /* ... HAKMEM lives ... */
+{
+ register int i, j;
+ unsigned char c;
+
+ /* Make one-string case look like simple alternatives case */
+ if (!altflag) {
+ nalt = 1;
+ altmin = altlen[0] = strlen(pattern);
+ altpat[0] = pattern;
+ }
+ /* For chars that aren't in any string, skip by string length. */
+ for (j = 0; j < 256; j++) {
+ delta0[j] = altmin;
+ cmap[j] = j; /* Sneak in initialization of cmap */
+ }
+
+ /* For chars in a string, skip distance from char to end of string. */
+ /* (If char appears more than once, skip minimum distance.) */
+ for (i = 0; i < nalt; i++)
+ for (j = 0; j < altlen[i] - 1; j++) {
+ c = altpat[i][j];
+ delta0[c] = MIN(delta0[c], altlen[i] - j - 1);
+ if (iflag && islower((int) c))
+ delta0[toupper((int) c)] = delta0[c];
+ }
+
+ /* For last char of each string, fall out of search loop. */
+ for (i = 0; i < nalt; i++) {
+ c = altpat[i][altlen[i] - 1];
+ delta0[c] = LARGE;
+ if (iflag && islower((int) c))
+ delta0[toupper((int) c)] = LARGE;
+ }
+ if (iflag)
+ for (j = 'A'; j <= 'Z'; j++)
+ cmap[j] = tolower((int) j);
+}
+
+/*
+ * Print, count, or stop on full match. Result is either the location for
+ * continued search, or NULL to stop.
+ */
+char *
+gotamatch(file, s)
+ register char *file, *s;
+{
+ char *savematch();
+ int squirrel = 0; /* nonzero to squirrel away FIRSTFEW matches */
+
+ nmatch++;
+ nsuccess = 1;
+ if (!boyonly && boyfound <= FIRSTFEW && file != NULL)
+ squirrel = 1;
+
+ if (sflag)
+ return (NULL); /* -s usurps all flags (unlike some versions) */
+ if (cflag) { /* -c overrides -l, we guess */
+ do;
+ while (*s++ != NL);
+ } else if (lflag) {
+ puts(file);
+ return (NULL);
+ } else {
+ if (!hflag)
+ if (!squirrel)
+ printf("%s:", file);
+ else
+ (void)sprintf(preamble, "%s:", file);
+ if (nflag) {
+ if (prevmatch)
+ prevnline = prevnline + nlcount(prevloc, s);
+ else
+ prevnline = nline + nlcount(str, s);
+ prevmatch = 1;
+
+ if (!squirrel)
+ printf("%ld:", prevnline);
+ else
+ (void)sprintf(preamble + strlen(preamble),
+ "%ld:", prevnline);
+ }
+ if (!squirrel) {
+ do
+ putchar(*s);
+ while (*s++ != NL);
+ } else
+ s = savematch(s);
+
+ if (nflag)
+ prevloc = s - 1;
+ }
+ return ((firstflag && !cflag) ? NULL : s);
+}
+
+char *
+fold(line)
+ char *line;
+{
+ static char fline[BUFSIZE];
+ register char *s, *t = fline;
+
+ for (s = line; *s != EOS; s++)
+ *t++ = (isupper((int) *s) ? (char) tolower((int) *s) : *s);
+ *t = EOS;
+ return (fline);
+}
+
+strindex(s, t) /* the easy way, as in K&P, p. 192 */
+ char *s, *t;
+{
+ int i, n;
+
+ n = strlen(t);
+ for (i = 0; s[i] != '\0'; i++)
+ if (strncmp(s + i, t, n) == 0)
+ return (i);
+ return (-1);
+}
+
+char *
+grepxlat(pattern) /* grep pattern meta conversion */
+ char *pattern;
+{
+ register char *p, *s;
+ static char newpat[BUFSIZE];
+
+ for (s = newpat, p = pattern; *p != EOS;) {
+ if (*p == '\\') { /* skip escapes ... */
+ *s++ = *p++;
+ if (*p)
+ *s++ = *p++;
+ } else if (*p == '[') { /* ... and char classes */
+ while (*p != EOS && *p != ']')
+ *s++ = *p++;
+ } else if (strchr("+?|()", *p) != NULL) {
+ *s++ = '\\'; /* insert protection */
+ *s++ = *p++;
+ } else
+ *s++ = *p++;
+ }
+ *s = EOS;
+ grepflag = ((patind) ? 0 : 1);
+ return (newpat);
+}
+
+/*
+ * Test for simple alternation. Result is NULL if it's not so simple, or is
+ * a pointer to the first string if it is. Warning: sscanf size is a
+ * fixpoint, beyond which the speedup linearity starts to break down. In the
+ * wake of the elegant aho/corrasick "trie"-based fgrep, generalizing
+ * altpat[] to arbitrary size is not useful.
+ */
+char *
+alternate(regexpr)
+ char *regexpr;
+{
+ register int i, j;
+ register char *start, *stop;
+ unsigned char c;
+
+ if (fgrepflag && strchr(regexpr, '|'))
+ return (NULL);
+
+ /*
+ * break pattern up into altpat array; delimit on newline, bar,
+ * or EOS. We know we won't overflow, we've already checked the
+ * number of patterns we're going to find against NALT.
+ * Also, set length of pattern and find minimum pattern length.
+ */
+ nalt = 0;
+ altmin = NMUSH;
+ for (start = stop = regexpr;; ++stop)
+ if (!*stop || *stop == '|' || *stop == NL) {
+ altlen[nalt] = j = stop - start;
+ if (j < altmin)
+ altmin = j;
+ if (!(altpat[nalt] = malloc((u_int)(j + 1))))
+ oops("out of memory");
+ bcopy(start, altpat[nalt], j);
+ altpat[nalt][j] = EOS;
+ ++nalt;
+ if (!*stop)
+ break;
+ if (nalt == NALT)
+ return(NULL);
+ if (*stop == NL)
+ *stop = '|';
+ start = stop + 1;
+ }
+ if (!fgrepflag) {
+ if (strchr(regexpr, '|') == NULL || regexpr[0] == '|')
+ return (NULL);
+ if (strpbrk(regexpr, "^$.[]()?+*\\") != NULL
+ || strindex(regexpr, "||") >= 0)
+ return (NULL);
+ }
+
+ if (nalt > 1) { /* build superimposed "pre-match" sets per
+ * char */
+ altflag++;
+ for (j = 0; j < nalt; j++)
+ for (i = 0; i < altmin; i++) {
+ c = altpat[j][altlen[j] - altmin + i];
+ altset[i + 1][c] = 1; /* offset for sentinel */
+ }
+ }
+ return (altpat[0]);
+}
+
+/*
+ * Grapple with the dfa (std egrep) vs. ndfa (regexp) tradeoff. Criteria to
+ * determine whether to use dfa-based egrep: We do FIRSTFEW matches with
+ * regexec(). If Boyer-Moore up to now matched more than PUNTPERCENT
+ * of the input, the r.e. is likely to be underspecified, so do old *grep,
+ * which is faster on complex patterns than regexp(). At FIRSTFEW,
+ * dump the saved matches collected by savematch(). They are saved
+ * so that a "PUNT" can "rewind" to ignore them. Stdin is problematic,
+ * since it's hard to rewind.
+ */
+
+execstrategy(file)
+ char *file;
+{
+ int pctmatch;
+
+ pctmatch = (100 * rxcount) / incount;
+ if (pctmatch > PUNTPERCENT && file != NULL)
+ kernighan(args);
+ if (file != NULL)
+ flushmatches();
+}
+
+nlcount(bstart, bstop) /* flail interval to totalize newlines. */
+ char *bstart, *bstop;
+{
+ register char *s = bstart;
+ register char *t = bstop;
+ register int count = 0;
+
+ do { /* loop unroll for older architectures */
+ if (*t == NL) /* ... ask ames!jaw for sample code */
+ count++;
+ } while (t-- > s);
+
+ return (count);
+}
+
+char *
+isolate(regexpr) /* isolate longest metacharacter-free string */
+ char *regexpr;
+{
+ char *dummyexpr;
+
+ /*
+ * We add (.)* because Henry's regcomp only figures regmust if it
+ * sees a leading * pattern. Foo!
+ */
+ dummyexpr = malloc((unsigned) strlen(regexpr) + 5);
+ (void)sprintf(dummyexpr, "(.)*%s", regexpr);
+ if ((rspencer = regcomp(dummyexpr)) == NULL)
+ kernighan(args);
+ return (rspencer->regmust);
+}
+
+char *matches[FIRSTFEW];
+static int mcount = 0;
+
+char *
+savematch(s) /* horde matches during statistics gathering */
+ register char *s;
+{
+ char *p;
+ char *start = s;
+ int msize = 0;
+ int psize = strlen(preamble);
+
+ while (*s++ != NL)
+ msize++;
+ *--s = EOS;
+
+ p = malloc((unsigned) msize + 1 + psize);
+ strcpy(p, preamble);
+ strcpy(p + psize, start);
+ matches[mcount++] = p;
+
+ preamble[0] = 0;
+ *s = NL;
+ return (s);
+}
+
+flushmatches()
+{
+ int n;
+
+ flushflag = 1;
+ for (n = 0; n < mcount; n++)
+ printf("%s\n", matches[n]);
+ mcount = 0;
+}
+
+oops(message)
+ char *message;
+{
+ fprintf(stderr, "%s: %s\n", progname, message);
+ exit(2);
+}
+
+kernighan(args) /* "let others do the hard part ..." */
+ char *args[];
+{
+ /*
+ * We may have already run grep on some of the files; remove them
+ * from the arg list we pass on. Note that we can't delete them
+ * totally because the number of file names affects the output
+ * (automatic -h).
+ */
+ /* better would be fork/exec per punted file -- jaw */
+
+ while (firstfile && optind > firstfile)
+ args[firstfile++] = _PATH_DEVNULL;
+ if (patind)
+ args[patind] = pattern;
+ (void) fflush(stdout);
+
+ if (grepflag)
+ execvp(_PATH_GREPSTD, args), oops("can't exec old 'grep'");
+ else if (fgrepflag)
+ execvp(_PATH_FGREPSTD, args), oops("can't exec old 'fgrep'");
+ else
+ execvp(_PATH_EGREPSTD, args), oops("can't exec old 'egrep'");
+}
diff --git a/usr.bin/grep/egrep/grep.1 b/usr.bin/grep/egrep/grep.1
new file mode 100644
index 0000000..771cc8c
--- /dev/null
+++ b/usr.bin/grep/egrep/grep.1
@@ -0,0 +1,250 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)grep.1 8.3 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt GREP 1
+.Os
+.Sh NAME
+.Nm grep
+.Nd file pattern searcher
+.Sh SYNOPSIS
+.Nm grep
+.Op Fl bchilnosvw
+.Op Fl e Ar pattern
+.Op Ar
+.Nm egrep
+.Op Fl bchilnosv
+.Op Fl e Ar pattern
+.Op Fl f Ar pattern_file
+.Op Ar
+.Nm fgrep
+.Op Fl bchilnosvx
+.Op Fl e Ar pattern
+.Op Fl f Ar pattern_file
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm grep
+utilities search the given input files selecting lines
+which match one or more patterns; the type of patterns is controlled
+by the options specified.
+By default, a pattern
+matches an input line if any regular expression (RE) in the
+pattern matches the input line without its trailing <new-line>.
+A null RE matches every line.
+Each input line that matches at
+least one of the patterns is written to the standard output.
+.Pp
+For simple patterns or
+.Xr ex 1
+or
+.Xr ed 1
+style regular expressions, the
+.Nm grep
+utility is used.
+The
+.Nm egrep
+utility
+can handle extended regular expressions and
+embedded <newline>s in patterns.
+The
+.Nm fgrep
+utility is quick but can handle only fixed strings.
+A fixed string
+is a string of characters,
+each character
+is matched only by itself.
+The pattern
+value can consist of multiple lines with
+embedded <newline>s.
+In this case, the <newline>s
+act as alternation characters, allowing any of the
+pattern lines to match a portion of the input.
+.Pp
+The following options are available:
+.Pp
+.Bl -tag -width indent
+.It Fl b
+The block number on the disk in which a matched pattern is located
+is displayed in front of the respective matched line.
+.It Fl c
+Only a count of selected lines is written to standard
+output.
+.It Fl e Ar expression
+Specify a pattern used during the search of the
+input.
+Multiple
+.Fl e
+options can be used to specify
+multiple patterns; an input line is selected if it
+matches any of the specified patterns.
+.It Fl f Ar pattern_file
+The pattern is read from the file named by the
+pathname pattern_file.
+Trailing newlines
+in the pattern_file are ignored.
+.Pf ( Nm Egrep
+and
+.Nm fgrep
+only).
+.It Fl h
+Never print filename headers with output lines.
+.It Fl i
+The case of letters is ignored in making comparisons \- that is, upper and
+lower case are considered identical.
+.It Fl l
+Only the names of files containing selected lines
+are written to standard output.
+Pathnames are
+listed once per file searched.
+If the standard
+input is searched, the pathname
+.Sq Fl
+is written.
+.It Fl n
+Each output line is preceded by its relative line
+number in the file; each file starting at line 1.
+The line number counter is reset for each file processed.
+This option is ignored if
+.Fl c ,
+.Fl l ,
+or
+.Fl s
+is
+specified.
+.It Fl o
+Always print filename headers with output lines.
+.It Fl s
+Silent mode. Nothing is printed (except error messages).
+This is useful for checking the error status.
+.It Fl v
+Selected lines are those
+.Em not
+matching the specified
+patterns.
+.It Fl x
+Only input lines selected against an entire fixed
+string or regular expression are considered to be
+matching lines.
+.Pf ( Nm Fgrep
+only).
+.It Fl w
+The expression is searched for as a word
+(as if surrounded by `\e<' and `\e>', see
+.Xr ex 1 . )
+.Pf ( Nm Grep
+only)
+.Pp
+.El
+If no file arguments are specified, the
+standard input is used.
+.Pp
+The
+.Nm grep
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width flag -compact
+.It Li 0
+One or more lines were selected.
+.It Li 1
+No lines were selected.
+.It Li >1
+An error occurred.
+.El
+.Sh EXTENDED REGULAR EXPRESSIONS
+The following characters are interpreted by
+.Nm egrep :
+.Pp
+.Bl -tag -width flag -compact
+.It Cm \&$
+Align the match from the end of the line.
+.It Cm \&^
+Align the match from the beginning of the line.
+.It Cm \&|
+Add another pattern (see example below).
+.It Cm \&?
+Match 1 or less sequential repetitions of the pattern.
+.It Cm \&+
+Match 1 or more sequential repetitions of the pattern.
+.It Cm \&*
+Match 0 or more sequential repetitions of the pattern.
+.It Cm \&[]
+Match any single character or range of characters
+enclosed in the brackets.
+.It Cm \&\e
+Escape special characters which have meaning to
+.Nm egrep ,
+the set of {$,.,^,[,],|,?,+,*,(,)}.
+.El
+.Sh EXAMPLES
+To find all occurrences of the word patricia in a file:
+.Pp
+.Dl grep patricia myfile
+.Pp
+To find all occurrences of the pattern
+.Ql \&.Pp
+at the beginning of a line:
+.Pp
+.Dl grep '^\e.Pp'
+.Pp
+The apostrophes assure the entire expression is evaluated by
+.Nm grep
+instead of by the
+users shell.
+The carat or hat
+.Ql Li \&^
+means
+.Em from the beginning of a line ,
+and the
+.Ql Li \&\e
+escapes the
+.Ql Li \&.
+which would otherwise match any character.
+.Pp
+A simple example of an extended regular expression:
+.Pp
+.Dl egrep '19|20|25' calendar
+.Pp
+Peruses the file calendar looking for either 19, 20
+or 25.
+.Sh SEE ALSO
+.Xr ed 1 ,
+.Xr ex 1 ,
+.Xr sed 1
+.Sh HISTORY
+The
+.Nm grep
+command appeared in
+.At v6 .
+.Sh BUGS
+Lines are limited to 256 characters; longer lines are truncated.
diff --git a/usr.bin/grep/egrep/pathnames.h b/usr.bin/grep/egrep/pathnames.h
new file mode 100644
index 0000000..a25b3dc
--- /dev/null
+++ b/usr.bin/grep/egrep/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_EGREPSTD "/usr/libexec/old.egrep"
+#define _PATH_GREPSTD "/usr/libexec/old.bin.grep"
+#define _PATH_FGREPSTD "/usr/libexec/old.fgrep"
diff --git a/usr.bin/head/Makefile b/usr.bin/head/Makefile
new file mode 100644
index 0000000..b0b911e
--- /dev/null
+++ b/usr.bin/head/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= head
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/head/head.1 b/usr.bin/head/head.1
new file mode 100644
index 0000000..2e47515
--- /dev/null
+++ b/usr.bin/head/head.1
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)head.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt HEAD 1
+.Os BSD 3
+.Sh NAME
+.Nm head
+.Nd display first lines of a file
+.Sh SYNOPSIS
+.Nm head
+.Op Fl n Ar count
+.Op Ar file ...
+.Sh DESCRIPTION
+This filter displays the first
+.Ar count
+lines of each of the specified files, or of the standard input if no
+files are specified.
+If
+.Ar count
+is omitted it defaults to 10.
+.Pp
+If more than a single file is specified, each file is preceded by a
+header consisting of the string
+.Dq ==> XXX <==
+where
+.Dq XXX
+is the name of the file.
+.Pp
+The
+.Nm head
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr tail 1
+.Sh HISTORY
+The
+.Nm head
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/head/head.c b/usr.bin/head/head.c
new file mode 100644
index 0000000..3c4865b
--- /dev/null
+++ b/usr.bin/head/head.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1980, 1987, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)head.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * head - give the first few lines of a stream or of each of a set of files
+ *
+ * Bill Joy UCB August 24, 1977
+ */
+
+void err __P((int, const char *, ...));
+void head __P((FILE *, int));
+void obsolete __P((char *[]));
+void usage __P((void));
+
+int eval;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch;
+ FILE *fp;
+ int first, linecnt;
+ char *ep;
+
+ obsolete(argv);
+ linecnt = 10;
+ while ((ch = getopt(argc, argv, "n:")) != EOF)
+ switch(ch) {
+ case 'n':
+ linecnt = strtol(optarg, &ep, 10);
+ if (*ep || linecnt <= 0)
+ err(1, "illegal line count -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ for (first = 1; *argv; ++argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ err(0, "%s: %s", *argv, strerror(errno));
+ continue;
+ }
+ if (argc > 1) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", *argv);
+ first = 0;
+ }
+ head(fp, linecnt);
+ (void)fclose(fp);
+ }
+ else
+ head(stdin, linecnt);
+ exit(eval);
+}
+
+void
+head(fp, cnt)
+ FILE *fp;
+ register int cnt;
+{
+ register int ch;
+
+ while ((ch = getc(fp)) != EOF && cnt) {
+ if (putchar(ch) == EOF)
+ err(1, "stdout: %s", strerror(errno));
+ if (ch == '\n')
+ cnt--;
+ }
+}
+
+void
+obsolete(argv)
+ char *argv[];
+{
+ char *ap;
+
+ while (ap = *++argv) {
+ /* Return if "--" or not "-[0-9]*". */
+ if (ap[0] != '-' || ap[1] == '-' || !isdigit(ap[1]))
+ return;
+ if ((ap = malloc(strlen(*argv) + 2)) == NULL)
+ err(1, "%s", strerror(errno));
+ ap[0] = '-';
+ ap[1] = 'n';
+ (void)strcpy(ap + 2, *argv + 1);
+ *argv = ap;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: head [-n lines] [file ...]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "head: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+ eval = 1;
+}
diff --git a/usr.bin/hexdump/Makefile b/usr.bin/hexdump/Makefile
new file mode 100644
index 0000000..1e0543b
--- /dev/null
+++ b/usr.bin/hexdump/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= hexdump
+SRCS= conv.c display.c hexdump.c hexsyntax.c odsyntax.c parse.c
+MAN1= hexdump.1 od.1
+LINKS= ${BINDIR}/hexdump ${BINDIR}/od
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/hexdump/conv.c b/usr.bin/hexdump/conv.c
new file mode 100644
index 0000000..daae174
--- /dev/null
+++ b/usr.bin/hexdump/conv.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)conv.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include "hexdump.h"
+
+void
+conv_c(pr, p)
+ PR *pr;
+ u_char *p;
+{
+ extern int deprecated;
+ char buf[10], *str;
+
+ switch(*p) {
+ case '\0':
+ str = "\\0";
+ goto strpr;
+ /* case '\a': */
+ case '\007':
+ if (deprecated) /* od didn't know about \a */
+ break;
+ str = "\\a";
+ goto strpr;
+ case '\b':
+ str = "\\b";
+ goto strpr;
+ case '\f':
+ str = "\\f";
+ goto strpr;
+ case '\n':
+ str = "\\n";
+ goto strpr;
+ case '\r':
+ str = "\\r";
+ goto strpr;
+ case '\t':
+ str = "\\t";
+ goto strpr;
+ case '\v':
+ if (deprecated)
+ break;
+ str = "\\v";
+ goto strpr;
+ default:
+ break;
+ }
+ if (isprint(*p)) {
+ *pr->cchar = 'c';
+ (void)printf(pr->fmt, *p);
+ } else {
+ (void)sprintf(str = buf, "%03o", (int)*p);
+strpr: *pr->cchar = 's';
+ (void)printf(pr->fmt, str);
+ }
+}
+
+void
+conv_u(pr, p)
+ PR *pr;
+ u_char *p;
+{
+ extern int deprecated;
+ static char *list[] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs", "ht", "lf", "vt", "ff", "cr", "so", "si",
+ "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em", "sub", "esc", "fs", "gs", "rs", "us",
+ };
+
+ /* od used nl, not lf */
+ if (*p <= 0x1f) {
+ *pr->cchar = 's';
+ if (deprecated && *p == 0x0a)
+ (void)printf(pr->fmt, "nl");
+ else
+ (void)printf(pr->fmt, list[*p]);
+ } else if (*p == 0x7f) {
+ *pr->cchar = 's';
+ (void)printf(pr->fmt, "del");
+ } else if (deprecated && *p == 0x20) { /* od replaced space with sp */
+ *pr->cchar = 's';
+ (void)printf(pr->fmt, " sp");
+ } else if (isprint(*p)) {
+ *pr->cchar = 'c';
+ (void)printf(pr->fmt, *p);
+ } else {
+ *pr->cchar = 'x';
+ (void)printf(pr->fmt, (int)*p);
+ }
+}
diff --git a/usr.bin/hexdump/display.c b/usr.bin/hexdump/display.c
new file mode 100644
index 0000000..8078e71
--- /dev/null
+++ b/usr.bin/hexdump/display.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hexdump.h"
+
+enum _vflag vflag = FIRST;
+
+static off_t address; /* address/offset in stream */
+static off_t eaddress; /* end address */
+
+static inline void print __P((PR *, u_char *));
+
+void
+display()
+{
+ extern FU *endfu;
+ register FS *fs;
+ register FU *fu;
+ register PR *pr;
+ register int cnt;
+ register u_char *bp;
+ off_t saveaddress;
+ u_char savech, *savebp;
+
+ while (bp = get())
+ for (fs = fshead, savebp = bp, saveaddress = address; fs;
+ fs = fs->nextfs, bp = savebp, address = saveaddress)
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->flags&F_IGNORE)
+ break;
+ for (cnt = fu->reps; cnt; --cnt)
+ for (pr = fu->nextpr; pr; address += pr->bcnt,
+ bp += pr->bcnt, pr = pr->nextpr) {
+ if (eaddress && address >= eaddress &&
+ !(pr->flags & (F_TEXT|F_BPAD)))
+ bpad(pr);
+ if (cnt == 1 && pr->nospace) {
+ savech = *pr->nospace;
+ *pr->nospace = '\0';
+ }
+ print(pr, bp);
+ if (cnt == 1 && pr->nospace)
+ *pr->nospace = savech;
+ }
+ }
+ if (endfu) {
+ /*
+ * If eaddress not set, error or file size was multiple of
+ * blocksize, and no partial block ever found.
+ */
+ if (!eaddress) {
+ if (!address)
+ return;
+ eaddress = address;
+ }
+ for (pr = endfu->nextpr; pr; pr = pr->nextpr)
+ switch(pr->flags) {
+ case F_ADDRESS:
+ (void)printf(pr->fmt, (quad_t)eaddress);
+ break;
+ case F_TEXT:
+ (void)printf(pr->fmt);
+ break;
+ }
+ }
+}
+
+static inline void
+print(pr, bp)
+ PR *pr;
+ u_char *bp;
+{
+ double f8;
+ float f4;
+ int16_t s2;
+ int8_t s8;
+ int32_t s4;
+ u_int16_t u2;
+ u_int32_t u4;
+ u_int64_t u8;
+
+ switch(pr->flags) {
+ case F_ADDRESS:
+ (void)printf(pr->fmt, (quad_t)address);
+ break;
+ case F_BPAD:
+ (void)printf(pr->fmt, "");
+ break;
+ case F_C:
+ conv_c(pr, bp);
+ break;
+ case F_CHAR:
+ (void)printf(pr->fmt, *bp);
+ break;
+ case F_DBL:
+ switch(pr->bcnt) {
+ case 4:
+ bcopy(bp, &f4, sizeof(f4));
+ (void)printf(pr->fmt, f4);
+ break;
+ case 8:
+ bcopy(bp, &f8, sizeof(f8));
+ (void)printf(pr->fmt, f8);
+ break;
+ }
+ break;
+ case F_INT:
+ switch(pr->bcnt) {
+ case 1:
+ (void)printf(pr->fmt, (quad_t)*bp);
+ break;
+ case 2:
+ bcopy(bp, &s2, sizeof(s2));
+ (void)printf(pr->fmt, (quad_t)s2);
+ break;
+ case 4:
+ bcopy(bp, &s4, sizeof(s4));
+ (void)printf(pr->fmt, (quad_t)s4);
+ break;
+ case 8:
+ bcopy(bp, &s8, sizeof(s8));
+ (void)printf(pr->fmt, s8);
+ break;
+ }
+ break;
+ case F_P:
+ (void)printf(pr->fmt, isprint(*bp) ? *bp : '.');
+ break;
+ case F_STR:
+ (void)printf(pr->fmt, (char *)bp);
+ break;
+ case F_TEXT:
+ (void)printf(pr->fmt);
+ break;
+ case F_U:
+ conv_u(pr, bp);
+ break;
+ case F_UINT:
+ switch(pr->bcnt) {
+ case 1:
+ (void)printf(pr->fmt, (u_quad_t)*bp);
+ break;
+ case 2:
+ bcopy(bp, &u2, sizeof(u2));
+ (void)printf(pr->fmt, (u_quad_t)u2);
+ break;
+ case 4:
+ bcopy(bp, &u4, sizeof(u4));
+ (void)printf(pr->fmt, (u_quad_t)u4);
+ break;
+ case 8:
+ bcopy(bp, &u8, sizeof(u8));
+ (void)printf(pr->fmt, u8);
+ break;
+ }
+ break;
+ }
+}
+
+void
+bpad(pr)
+ PR *pr;
+{
+ static char *spec = " -0+#";
+ register char *p1, *p2;
+
+ /*
+ * Remove all conversion flags; '-' is the only one valid
+ * with %s, and it's not useful here.
+ */
+ pr->flags = F_BPAD;
+ pr->cchar[0] = 's';
+ pr->cchar[1] = '\0';
+ for (p1 = pr->fmt; *p1 != '%'; ++p1);
+ for (p2 = ++p1; *p1 && index(spec, *p1); ++p1);
+ while (*p2++ = *p1++);
+}
+
+static char **_argv;
+
+u_char *
+get()
+{
+ extern enum _vflag vflag;
+ extern int length;
+ static int ateof = 1;
+ static u_char *curp, *savp;
+ register int n;
+ int need, nread;
+ u_char *tmpp;
+
+ if (!curp) {
+ curp = emalloc(blocksize);
+ savp = emalloc(blocksize);
+ } else {
+ tmpp = curp;
+ curp = savp;
+ savp = tmpp;
+ address += blocksize;
+ }
+ for (need = blocksize, nread = 0;;) {
+ /*
+ * if read the right number of bytes, or at EOF for one file,
+ * and no other files are available, zero-pad the rest of the
+ * block and set the end flag.
+ */
+ if (!length || ateof && !next((char **)NULL)) {
+ if (need == blocksize)
+ return((u_char *)NULL);
+ if (vflag != ALL && !bcmp(curp, savp, nread)) {
+ if (vflag != DUP)
+ (void)printf("*\n");
+ return((u_char *)NULL);
+ }
+ bzero((char *)curp + nread, need);
+ eaddress = address + nread;
+ return(curp);
+ }
+ n = fread((char *)curp + nread, sizeof(u_char),
+ length == -1 ? need : MIN(length, need), stdin);
+ if (!n) {
+ if (ferror(stdin))
+ (void)fprintf(stderr, "hexdump: %s: %s\n",
+ _argv[-1], strerror(errno));
+ ateof = 1;
+ continue;
+ }
+ ateof = 0;
+ if (length != -1)
+ length -= n;
+ if (!(need -= n)) {
+ if (vflag == ALL || vflag == FIRST ||
+ bcmp(curp, savp, blocksize)) {
+ if (vflag == DUP || vflag == FIRST)
+ vflag = WAIT;
+ return(curp);
+ }
+ if (vflag == WAIT)
+ (void)printf("*\n");
+ vflag = DUP;
+ address += blocksize;
+ need = blocksize;
+ nread = 0;
+ }
+ else
+ nread += n;
+ }
+}
+
+extern off_t skip; /* bytes to skip */
+
+int
+next(argv)
+ char **argv;
+{
+ extern int exitval;
+ static int done;
+ int statok;
+
+ if (argv) {
+ _argv = argv;
+ return(1);
+ }
+ for (;;) {
+ if (*_argv) {
+ if (!(freopen(*_argv, "r", stdin))) {
+ (void)fprintf(stderr, "hexdump: %s: %s\n",
+ *_argv, strerror(errno));
+ exitval = 1;
+ ++_argv;
+ continue;
+ }
+ statok = done = 1;
+ } else {
+ if (done++)
+ return(0);
+ statok = 0;
+ }
+ if (skip)
+ doskip(statok ? *_argv : "stdin", statok);
+ if (*_argv)
+ ++_argv;
+ if (!skip)
+ return(1);
+ }
+ /* NOTREACHED */
+}
+
+void
+doskip(fname, statok)
+ char *fname;
+ int statok;
+{
+ register int cnt;
+ struct stat sb;
+
+ if (statok) {
+ if (fstat(fileno(stdin), &sb))
+ err("%s: %s", fname, strerror(errno));
+ if (S_ISREG(sb.st_mode) && skip >= sb.st_size) {
+ address += sb.st_size;
+ skip -= sb.st_size;
+ return;
+ }
+ }
+ if (S_ISREG(sb.st_mode)) {
+ if (fseek(stdin, skip, SEEK_SET))
+ err("%s: %s", fname, strerror(errno));
+ address += skip;
+ skip = 0;
+ } else {
+ for (cnt = 0; cnt < skip; ++cnt)
+ if (getchar() == EOF)
+ break;
+ address += cnt;
+ skip -= cnt;
+ }
+}
+
+void *
+emalloc(size)
+ int size;
+{
+ void *p;
+
+ if ((p = malloc((u_int)size)) == NULL)
+ nomem();
+ bzero(p, size);
+ return(p);
+}
+
+void
+nomem()
+{
+ err("%s", strerror(errno));
+}
diff --git a/usr.bin/hexdump/hexdump.1 b/usr.bin/hexdump/hexdump.1
new file mode 100644
index 0000000..7161beb
--- /dev/null
+++ b/usr.bin/hexdump/hexdump.1
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)hexdump.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt HEXDUMP 1
+.Os
+.Sh NAME
+.Nm hexdump
+.Nd ascii, decimal, hexadecimal, octal dump
+.Sh SYNOPSIS
+.Nm hexdump
+.Op Fl bcdovx
+.Op Fl e Ar format_string
+.Op Fl f Ar format_file
+.Op Fl n Ar length
+.Bk -words
+.Op Fl s Ar skip
+.Ek
+.Ar file ...
+.Sh DESCRIPTION
+The hexdump utility is a filter which displays the specified files, or
+the standard input, if no files are specified, in a user specified
+format.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl b
+.Em One-byte octal display .
+Display the input offset in hexadecimal, followed by sixteen
+space-separated, three column, zero-filled, bytes of input data,
+in octal, per line.
+.It Fl c
+.Em One-byte character display .
+Display the input offset in hexadecimal, followed by sixteen
+space-separated, three column, space-filled, characters of input
+data per line.
+.It Fl d
+.Em Two-byte decimal display.
+Display the input offset in hexadecimal, followed by eight
+space-separated, five column, zero-filled, two-byte units
+of input data, in unsigned decimal, per line.
+.It Fl e Ar format_string
+Specify a format string to be used for displaying data.
+.It Fl f Ar format_file
+Specify a file that contains one or more newline separated format strings.
+Empty lines and lines whose first non-blank character is a hash mark
+.Pf ( Cm \&# )
+are ignored.
+.It Fl n Ar length
+Interpret only
+.Ar length
+bytes of input.
+.It Fl o
+.Em Two-byte octal display.
+Display the input offset in hexadecimal, followed by eight
+space-separated, six column, zero-filled, two byte quantities of
+input data, in octal, per line.
+.It Fl s Ar offset
+Skip
+.Ar offset
+bytes from the beginning of the input.
+By default,
+.Ar offset
+is interpreted as a decimal number.
+With a leading
+.Cm 0x
+or
+.Cm 0X ,
+.Ar offset
+is interpreted as a hexadecimal number,
+otherwise, with a leading
+.Cm 0 ,
+.Ar offset
+is interpreted as an octal number.
+Appending the character
+.Cm b ,
+.Cm k ,
+or
+.Cm m
+to
+.Ar offset
+causes it to be interpreted as a multiple of
+.Li 512 ,
+.Li 1024 ,
+or
+.Li 1048576 ,
+respectively.
+.It Fl v
+The
+.Fl v
+option causes hexdump to display all input data.
+Without the
+.Fl v
+option, any number of groups of output lines, which would be
+identical to the immediately preceding group of output lines (except
+for the input offsets), are replaced with a line comprised of a
+single asterisk.
+.It Fl x
+.Em Two-byte hexadecimal display.
+Display the input offset in hexadecimal, followed by eight, space
+separated, four column, zero-filled, two-byte quantities of input
+data, in hexadecimal, per line.
+.El
+.Pp
+For each input file,
+.Nm hexdump
+sequentially copies the input to standard output, transforming the
+data according to the format strings specified by the
+.Fl e
+and
+.Fl f
+options, in the order that they were specified.
+.Ss Formats
+A format string contains any number of format units, separated by
+whitespace.
+A format unit contains up to three items: an iteration count, a byte
+count, and a format.
+.Pp
+The iteration count is an optional positive integer, which defaults to
+one.
+Each format is applied iteration count times.
+.Pp
+The byte count is an optional positive integer.
+If specified it defines the number of bytes to be interpreted by
+each iteration of the format.
+.Pp
+If an iteration count and/or a byte count is specified, a single slash
+must be placed after the iteration count and/or before the byte count
+to disambiguate them.
+Any whitespace before or after the slash is ignored.
+.Pp
+The format is required and must be surrounded by double quote
+(" ") marks.
+It is interpreted as a fprintf-style format string (see
+.Xr fprintf 3 ) ,
+with the
+following exceptions:
+.Bl -bullet -offset indent
+.It
+An asterisk (*) may not be used as a field width or precision.
+.It
+A byte count or field precision
+.Em is
+required for each ``s'' conversion
+character (unlike the
+.Xr fprintf 3
+default which prints the entire string if the precision is unspecified).
+.It
+The conversion characters ``h'', ``l'', ``n'', ``p'' and ``q'' are
+not supported.
+.It
+The single character escape sequences
+described in the C standard are supported:
+.Bd -ragged -offset indent -compact
+.Bl -column <alert_character>
+.It NUL \e0
+.It <alert character> \ea
+.It <backspace> \eb
+.It <form-feed> \ef
+.It <newline> \en
+.It <carriage return> \er
+.It <tab> \et
+.It <vertical tab> \ev
+.El
+.Ed
+.El
+.Pp
+Hexdump also supports the the following additional conversion strings:
+.Bl -tag -width Fl
+.It Cm \&_a Ns Op Cm dox
+Display the input offset, cumulative across input files, of the
+next byte to be displayed.
+The appended characters
+.Cm d ,
+.Cm o ,
+and
+.Cm x
+specify the display base
+as decimal, octal or hexadecimal respectively.
+.It Cm \&_A Ns Op Cm dox
+Identical to the
+.Cm \&_a
+conversion string except that it is only performed
+once, when all of the input data has been processed.
+.It Cm \&_c
+Output characters in the default character set.
+Nonprinting characters are displayed in three character, zero-padded
+octal, except for those representable by standard escape notation
+(see above),
+which are displayed as two character strings.
+.It Cm _p
+Output characters in the default character set.
+Nonprinting characters are displayed as a single
+.Dq Cm \&. .
+.It Cm _u
+Output US ASCII characters, with the exception that control characters are
+displayed using the following, lower-case, names.
+Characters greater than 0xff, hexadecimal, are displayed as hexadecimal
+strings.
+.Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo
+.It \&000\ nul\t001\ soh\t002\ stx\t003\ etx\t004\ eot\t005\ enq
+.It \&006\ ack\t007\ bel\t008\ bs\t009\ ht\t00A\ lf\t00B\ vt
+.It \&00C\ ff\t00D\ cr\t00E\ so\t00F\ si\t010\ dle\t011\ dc1
+.It \&012\ dc2\t013\ dc3\t014\ dc4\t015\ nak\t016\ syn\t017\ etb
+.It \&018\ can\t019\ em\t01A\ sub\t01B\ esc\t01C\ fs\t01D\ gs
+.It \&01E\ rs\t01F\ us\t0FF\ del
+.El
+.El
+.Pp
+The default and supported byte counts for the conversion characters
+are as follows:
+.Bl -tag -width "Xc,_Xc,_Xc,_Xc,_Xc,_Xc" -offset indent
+.It Li \&%_c , \&%_p , \&%_u , \&%c
+One byte counts only.
+.It Xo
+.Li \&%d , \&%i , \&%o ,
+.Li \&%u , \&%X , \&%x
+.Xc
+Four byte default, one, two and four byte counts supported.
+.It Xo
+.Li \&%E , \&%e , \&%f ,
+.Li \&%G , \&%g
+.Xc
+Eight byte default, four byte counts supported.
+.El
+.Pp
+The amount of data interpreted by each format string is the sum of the
+data required by each format unit, which is the iteration count times the
+byte count, or the iteration count times the number of bytes required by
+the format if the byte count is not specified.
+.Pp
+The input is manipulated in ``blocks'', where a block is defined as the
+largest amount of data specified by any format string.
+Format strings interpreting less than an input block's worth of data,
+whose last format unit both interprets some number of bytes and does
+not have a specified iteration count, have the iteration count
+incremented until the entire input block has been processed or there
+is not enough data remaining in the block to satisfy the format string.
+.Pp
+If, either as a result of user specification or hexdump modifying
+the iteration count as described above, an iteration count is
+greater than one, no trailing whitespace characters are output
+during the last iteration.
+.Pp
+It is an error to specify a byte count as well as multiple conversion
+characters or strings unless all but one of the conversion characters
+or strings is
+.Cm \&_a
+or
+.Cm \&_A .
+.Pp
+If, as a result of the specification of the
+.Fl n
+option or end-of-file being reached, input data only partially
+satisfies a format string, the input block is zero-padded sufficiently
+to display all available data (i.e. any format units overlapping the
+end of data will display some number of the zero bytes).
+.Pp
+Further output by such format strings is replaced by an equivalent
+number of spaces.
+An equivalent number of spaces is defined as the number of spaces
+output by an
+.Cm s
+conversion character with the same field width
+and precision as the original conversion character or conversion
+string but with any
+.Dq Li \&+ ,
+.Dq \&\ \& ,
+.Dq Li \&#
+conversion flag characters
+removed, and referencing a NULL string.
+.Pp
+If no format strings are specified, the default display is equivalent
+to specifying the
+.Fl x
+option.
+.Pp
+.Nm hexdump
+exits 0 on success and >0 if an error occurred.
+.Sh EXAMPLES
+Display the input in perusal format:
+.Bd -literal -offset indent
+"%06.6_ao " 12/1 "%3_u "
+"\et\et" "%_p "
+"\en"
+.Ed
+.Pp
+Implement the \-x option:
+.Bd -literal -offset indent
+"%07.7_Ax\en"
+"%07.7_ax " 8/2 "%04x " "\en"
+.Ed
+.Sh SEE ALSO
+.Xr adb 1
diff --git a/usr.bin/hexdump/hexdump.c b/usr.bin/hexdump/hexdump.c
new file mode 100644
index 0000000..fec0b10
--- /dev/null
+++ b/usr.bin/hexdump/hexdump.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)hexdump.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "hexdump.h"
+
+FS *fshead; /* head of format strings */
+int blocksize; /* data block size */
+int exitval; /* final exit value */
+int length = -1; /* max bytes to read */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register FS *tfs;
+ char *p;
+
+ if (!(p = rindex(argv[0], 'o')) || strcmp(p, "od"))
+ newsyntax(argc, &argv);
+ else
+ oldsyntax(argc, &argv);
+
+ /* figure out the data block size */
+ for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) {
+ tfs->bcnt = size(tfs);
+ if (blocksize < tfs->bcnt)
+ blocksize = tfs->bcnt;
+ }
+ /* rewrite the rules, do syntax checking */
+ for (tfs = fshead; tfs; tfs = tfs->nextfs)
+ rewrite(tfs);
+
+ (void)next(argv);
+ display();
+ exit(exitval);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "hexdump: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/hexdump/hexdump.h b/usr.bin/hexdump/hexdump.h
new file mode 100644
index 0000000..82ec51d
--- /dev/null
+++ b/usr.bin/hexdump/hexdump.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hexdump.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef struct _pr {
+ struct _pr *nextpr; /* next print unit */
+#define F_ADDRESS 0x001 /* print offset */
+#define F_BPAD 0x002 /* blank pad */
+#define F_C 0x004 /* %_c */
+#define F_CHAR 0x008 /* %c */
+#define F_DBL 0x010 /* %[EefGf] */
+#define F_INT 0x020 /* %[di] */
+#define F_P 0x040 /* %_p */
+#define F_STR 0x080 /* %s */
+#define F_U 0x100 /* %_u */
+#define F_UINT 0x200 /* %[ouXx] */
+#define F_TEXT 0x400 /* no conversions */
+ u_int flags; /* flag values */
+ int bcnt; /* byte count */
+ char *cchar; /* conversion character */
+ char *fmt; /* printf format */
+ char *nospace; /* no whitespace version */
+} PR;
+
+typedef struct _fu {
+ struct _fu *nextfu; /* next format unit */
+ struct _pr *nextpr; /* next print unit */
+#define F_IGNORE 0x01 /* %_A */
+#define F_SETREP 0x02 /* rep count set, not default */
+ u_int flags; /* flag values */
+ int reps; /* repetition count */
+ int bcnt; /* byte count */
+ char *fmt; /* format string */
+} FU;
+
+typedef struct _fs { /* format strings */
+ struct _fs *nextfs; /* linked list of format strings */
+ struct _fu *nextfu; /* linked list of format units */
+ int bcnt;
+} FS;
+
+extern FS *fshead; /* head of format strings list */
+extern int blocksize; /* data block size */
+enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
+
+void add __P((char *));
+void addfile __P((char *));
+void badcnt __P((char *));
+void badconv __P((char *));
+void badfmt __P((char *));
+void badsfmt __P((void));
+void bpad __P((PR *));
+void conv_c __P((PR *, u_char *));
+void conv_u __P((PR *, u_char *));
+void display __P((void));
+void doskip __P((char *, int));
+void err __P((const char *, ...));
+void *emalloc __P((int));
+void escape __P((char *));
+u_char *get __P((void));
+void newsyntax __P((int, char ***));
+int next __P((char **));
+void nomem __P((void));
+void oldsyntax __P((int, char ***));
+void rewrite __P((FS *));
+int size __P((FS *));
+void usage __P((void));
diff --git a/usr.bin/hexdump/hexsyntax.c b/usr.bin/hexdump/hexsyntax.c
new file mode 100644
index 0000000..68abbe6
--- /dev/null
+++ b/usr.bin/hexdump/hexsyntax.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hexsyntax.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hexdump.h"
+
+off_t skip; /* bytes to skip */
+
+void
+newsyntax(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern enum _vflag vflag;
+ extern FS *fshead;
+ extern int length;
+ int ch;
+ char *p, **argv;
+
+ argv = *argvp;
+ while ((ch = getopt(argc, argv, "bcde:f:n:os:vx")) != EOF)
+ switch (ch) {
+ case 'b':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"");
+ break;
+ case 'c':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"");
+ break;
+ case 'd':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %05u \" \"\\n\"");
+ break;
+ case 'e':
+ add(optarg);
+ break;
+ case 'f':
+ addfile(optarg);
+ break;
+ case 'n':
+ if ((length = atoi(optarg)) < 0)
+ err("%s: bad length value", optarg);
+ break;
+ case 'o':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"");
+ break;
+ case 's':
+ if ((skip = strtol(optarg, &p, 0)) < 0)
+ err("%s: bad skip value", optarg);
+ switch(*p) {
+ case 'b':
+ skip *= 512;
+ break;
+ case 'k':
+ skip *= 1024;
+ break;
+ case 'm':
+ skip *= 1048576;
+ break;
+ }
+ break;
+ case 'v':
+ vflag = ALL;
+ break;
+ case 'x':
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \" %04x \" \"\\n\"");
+ break;
+ case '?':
+ usage();
+ }
+
+ if (!fshead) {
+ add("\"%07.7_Ax\n\"");
+ add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+ }
+
+ *argvp += optind;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"hexdump: [-bcdovx] [-e fmt] [-f fmt_file] [-n length] [-s skip] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/hexdump/od.1 b/usr.bin/hexdump/od.1
new file mode 100644
index 0000000..dab6da7
--- /dev/null
+++ b/usr.bin/hexdump/od.1
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)od.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd %Q
+.Os
+.Dt OD 1
+.Sh NAME
+.Nm od
+.Nd octal, decimal, hex, ascii dump
+.Sh SYNOPSIS
+.Nm od
+.Op Fl aBbcDdeFfHhIiLlOovXx
+.Sm off
+.Oo
+.Op Cm \&+
+.Li offset
+.Op Cm \&.
+.Op Cm Bb
+.Oc
+.Ar file
+.Sh DESCRIPTION
+.Nm Od
+has been deprecated in favor of
+.Xr hexdump 1 .
+.Pp
+.Xr Hexdump ,
+if called as
+.Nm od ,
+provides compatibility for the options listed above.
+.Pp
+It does not provide compatibility for the
+.Fl s
+option (see
+.Xr strings 1 )
+or the
+.Fl P ,
+.Fl p ,
+or
+.Fl w
+options, nor is compatibility provided for the ``label'' component
+of the offset syntax.
+.Sh SEE ALSO
+.Xr hexdump 1 ,
+.Xr strings 1
+.Sh BUGS
+Quite a few.
diff --git a/usr.bin/hexdump/odsyntax.c b/usr.bin/hexdump/odsyntax.c
new file mode 100644
index 0000000..642f6c2
--- /dev/null
+++ b/usr.bin/hexdump/odsyntax.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)odsyntax.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "hexdump.h"
+
+int deprecated;
+
+static void odoffset __P((int, char ***));
+static void odprecede __P((void));
+
+void
+oldsyntax(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern enum _vflag vflag;
+ extern FS *fshead;
+ int ch;
+ char **argv;
+
+ deprecated = 1;
+ argv = *argvp;
+ while ((ch = getopt(argc, argv, "aBbcDdeFfHhIiLlOoPpswvXx")) != EOF)
+ switch (ch) {
+ case 'a':
+ odprecede();
+ add("16/1 \"%3_u \" \"\\n\"");
+ break;
+ case 'B':
+ case 'o':
+ odprecede();
+ add("8/2 \" %06o \" \"\\n\"");
+ break;
+ case 'b':
+ odprecede();
+ add("16/1 \"%03o \" \"\\n\"");
+ break;
+ case 'c':
+ odprecede();
+ add("16/1 \"%3_c \" \"\\n\"");
+ break;
+ case 'd':
+ odprecede();
+ add("8/2 \" %05u \" \"\\n\"");
+ break;
+ case 'D':
+ odprecede();
+ add("4/4 \" %010u \" \"\\n\"");
+ break;
+ case 'e': /* undocumented in od */
+ case 'F':
+ odprecede();
+ add("2/8 \" %21.14e \" \"\\n\"");
+ break;
+
+ case 'f':
+ odprecede();
+ add("4/4 \" %14.7e \" \"\\n\"");
+ break;
+ case 'H':
+ case 'X':
+ odprecede();
+ add("4/4 \" %08x \" \"\\n\"");
+ break;
+ case 'h':
+ case 'x':
+ odprecede();
+ add("8/2 \" %04x \" \"\\n\"");
+ break;
+ case 'I':
+ case 'L':
+ case 'l':
+ odprecede();
+ add("4/4 \" %11d \" \"\\n\"");
+ break;
+ case 'i':
+ odprecede();
+ add("8/2 \" %6d \" \"\\n\"");
+ break;
+ case 'O':
+ odprecede();
+ add("4/4 \" %011o \" \"\\n\"");
+ break;
+ case 'v':
+ vflag = ALL;
+ break;
+ case 'P':
+ case 'p':
+ case 's':
+ case 'w':
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "od: od(1) has been deprecated for hexdump(1).\n");
+ if (ch != '?')
+ (void)fprintf(stderr,
+"od: hexdump(1) compatibility doesn't support the -%c option%s\n",
+ ch, ch == 's' ? "; see strings(1)." : ".");
+ usage();
+ }
+
+ if (!fshead) {
+ add("\"%07.7_Ao\n\"");
+ add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\"");
+ }
+
+ argc -= optind;
+ *argvp += optind;
+
+ if (argc)
+ odoffset(argc, argvp);
+}
+
+static void
+odoffset(argc, argvp)
+ int argc;
+ char ***argvp;
+{
+ extern off_t skip;
+ register char *num, *p;
+ int base;
+ char *end;
+
+ /*
+ * The offset syntax of od(1) was genuinely bizarre. First, if
+ * it started with a plus it had to be an offset. Otherwise, if
+ * there were at least two arguments, a number or lower-case 'x'
+ * followed by a number makes it an offset. By default it was
+ * octal; if it started with 'x' or '0x' it was hex. If it ended
+ * in a '.', it was decimal. If a 'b' or 'B' was appended, it
+ * multiplied the number by 512 or 1024 byte units. There was
+ * no way to assign a block count to a hex offset.
+ *
+ * We assume it's a file if the offset is bad.
+ */
+ p = argc == 1 ? (*argvp)[0] : (*argvp)[1];
+
+ if (*p != '+' && (argc < 2 ||
+ (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1])))))
+ return;
+
+ base = 0;
+ /*
+ * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+ * set base.
+ */
+ if (p[0] == '+')
+ ++p;
+ if (p[0] == 'x' && isxdigit(p[1])) {
+ ++p;
+ base = 16;
+ } else if (p[0] == '0' && p[1] == 'x') {
+ p += 2;
+ base = 16;
+ }
+
+ /* skip over the number */
+ if (base == 16)
+ for (num = p; isxdigit(*p); ++p);
+ else
+ for (num = p; isdigit(*p); ++p);
+
+ /* check for no number */
+ if (num == p)
+ return;
+
+ /* if terminates with a '.', base is decimal */
+ if (*p == '.') {
+ if (base)
+ return;
+ base = 10;
+ }
+
+ skip = strtol(num, &end, base ? base : 8);
+
+ /* if end isn't the same as p, we got a non-octal digit */
+ if (end != p) {
+ skip = 0;
+ return;
+ }
+
+ if (*p)
+ if (*p == 'B') {
+ skip *= 1024;
+ ++p;
+ } else if (*p == 'b') {
+ skip *= 512;
+ ++p;
+ }
+
+ if (*p) {
+ skip = 0;
+ return;
+ }
+
+ /*
+ * If the offset uses a non-octal base, the base of the offset
+ * is changed as well. This isn't pretty, but it's easy.
+ */
+#define TYPE_OFFSET 7
+ if (base == 16) {
+ fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
+ fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
+ } else if (base == 10) {
+ fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
+ fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
+ }
+
+ /* Terminate file list. */
+ (*argvp)[1] = NULL;
+}
+
+static void
+odprecede()
+{
+ static int first = 1;
+
+ if (first) {
+ first = 0;
+ add("\"%07.7_Ao\n\"");
+ add("\"%07.7_ao \"");
+ } else
+ add("\" \"");
+}
diff --git a/usr.bin/hexdump/parse.c b/usr.bin/hexdump/parse.c
new file mode 100644
index 0000000..99c207a
--- /dev/null
+++ b/usr.bin/hexdump/parse.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "hexdump.h"
+
+FU *endfu; /* format at end-of-data */
+
+void
+addfile(name)
+ char *name;
+{
+ register char *p;
+ FILE *fp;
+ int ch;
+ char buf[2048 + 1];
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err("%s: %s\n", name, strerror(errno));
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (!(p = index(buf, '\n'))) {
+ (void)fprintf(stderr, "hexdump: line too long.\n");
+ while ((ch = getchar()) != '\n' && ch != EOF);
+ continue;
+ }
+ *p = '\0';
+ for (p = buf; *p && isspace(*p); ++p);
+ if (!*p || *p == '#')
+ continue;
+ add(p);
+ }
+ (void)fclose(fp);
+}
+
+void
+add(fmt)
+ char *fmt;
+{
+ register char *p;
+ static FS **nextfs;
+ FS *tfs;
+ FU *tfu, **nextfu;
+ char *savep;
+
+ /* start new linked list of format units */
+ tfs = emalloc(sizeof(FS));
+ if (!fshead)
+ fshead = tfs;
+ else
+ *nextfs = tfs;
+ nextfs = &tfs->nextfs;
+ nextfu = &tfs->nextfu;
+
+ /* take the format string and break it up into format units */
+ for (p = fmt;;) {
+ /* skip leading white space */
+ for (; isspace(*p); ++p);
+ if (!*p)
+ break;
+
+ /* allocate a new format unit and link it in */
+ tfu = emalloc(sizeof(FU));
+ *nextfu = tfu;
+ nextfu = &tfu->nextfu;
+ tfu->reps = 1;
+
+ /* if leading digit, repetition count */
+ if (isdigit(*p)) {
+ for (savep = p; isdigit(*p); ++p);
+ if (!isspace(*p) && *p != '/')
+ badfmt(fmt);
+ /* may overwrite either white space or slash */
+ tfu->reps = atoi(savep);
+ tfu->flags = F_SETREP;
+ /* skip trailing white space */
+ for (++p; isspace(*p); ++p);
+ }
+
+ /* skip slash and trailing white space */
+ if (*p == '/')
+ while (isspace(*++p));
+
+ /* byte count */
+ if (isdigit(*p)) {
+ for (savep = p; isdigit(*p); ++p);
+ if (!isspace(*p))
+ badfmt(fmt);
+ tfu->bcnt = atoi(savep);
+ /* skip trailing white space */
+ for (++p; isspace(*p); ++p);
+ }
+
+ /* format */
+ if (*p != '"')
+ badfmt(fmt);
+ for (savep = ++p; *p != '"';)
+ if (*p++ == 0)
+ badfmt(fmt);
+ if (!(tfu->fmt = malloc(p - savep + 1)))
+ nomem();
+ (void) strncpy(tfu->fmt, savep, p - savep);
+ tfu->fmt[p - savep] = '\0';
+ escape(tfu->fmt);
+ p++;
+ }
+}
+
+static char *spec = ".#-+ 0123456789";
+
+int
+size(fs)
+ FS *fs;
+{
+ register FU *fu;
+ register int bcnt, cursize;
+ register char *fmt;
+ int prec;
+
+ /* figure out the data block size needed for each format unit */
+ for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+ if (fu->bcnt) {
+ cursize += fu->bcnt * fu->reps;
+ continue;
+ }
+ for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+ if (*fmt != '%')
+ continue;
+ /*
+ * skip any special chars -- save precision in
+ * case it's a %s format.
+ */
+ while (index(spec + 1, *++fmt));
+ if (*fmt == '.' && isdigit(*++fmt)) {
+ prec = atoi(fmt);
+ while (isdigit(*++fmt));
+ }
+ switch(*fmt) {
+ case 'c':
+ bcnt += 1;
+ break;
+ case 'd': case 'i': case 'o': case 'u':
+ case 'x': case 'X':
+ bcnt += 4;
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ bcnt += 8;
+ break;
+ case 's':
+ bcnt += prec;
+ break;
+ case '_':
+ switch(*++fmt) {
+ case 'c': case 'p': case 'u':
+ bcnt += 1;
+ break;
+ }
+ }
+ }
+ cursize += bcnt * fu->reps;
+ }
+ return (cursize);
+}
+
+void
+rewrite(fs)
+ FS *fs;
+{
+ enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+ register PR *pr, **nextpr;
+ register FU *fu;
+ register char *p1, *p2;
+ char savech, *fmtp, cs[3];
+ int nconv, prec;
+
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ /*
+ * Break each format unit into print units; each conversion
+ * character gets its own.
+ */
+ for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+ pr = emalloc(sizeof(PR));
+ if (!fu->nextpr)
+ fu->nextpr = pr;
+ else
+ *nextpr = pr;
+
+ /* Skip preceding text and up to the next % sign. */
+ for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
+
+ /* Only text in the string. */
+ if (!*p1) {
+ pr->fmt = fmtp;
+ pr->flags = F_TEXT;
+ break;
+ }
+
+ /*
+ * Get precision for %s -- if have a byte count, don't
+ * need it.
+ */
+ if (fu->bcnt) {
+ sokay = USEBCNT;
+ /* Skip to conversion character. */
+ for (++p1; index(spec, *p1); ++p1);
+ } else {
+ /* Skip any special chars, field width. */
+ while (index(spec + 1, *++p1));
+ if (*p1 == '.' && isdigit(*++p1)) {
+ sokay = USEPREC;
+ prec = atoi(p1);
+ while (isdigit(*++p1));
+ } else
+ sokay = NOTOKAY;
+ }
+
+ p2 = p1 + 1; /* Set end pointer. */
+ cs[0] = *p1; /* Set conversion string. */
+ cs[1] = '\0';
+
+ /*
+ * Figure out the byte count for each conversion;
+ * rewrite the format as necessary, set up blank-
+ * padding for end of data.
+ */
+ switch(cs[0]) {
+ case 'c':
+ pr->flags = F_CHAR;
+ switch(fu->bcnt) {
+ case 0: case 1:
+ pr->bcnt = 1;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 'd': case 'i':
+ pr->flags = F_INT;
+ goto isint;
+ case 'o': case 'u': case 'x': case 'X':
+ pr->flags = F_UINT;
+isint: cs[2] = '\0';
+ cs[1] = cs[0];
+ cs[0] = 'q';
+ switch(fu->bcnt) {
+ case 0: case 4:
+ pr->bcnt = 4;
+ break;
+ case 1:
+ pr->bcnt = 1;
+ break;
+ case 2:
+ pr->bcnt = 2;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ pr->flags = F_DBL;
+ switch(fu->bcnt) {
+ case 0: case 8:
+ pr->bcnt = 8;
+ break;
+ case 4:
+ pr->bcnt = 4;
+ break;
+ default:
+ p1[1] = '\0';
+ badcnt(p1);
+ }
+ break;
+ case 's':
+ pr->flags = F_STR;
+ switch(sokay) {
+ case NOTOKAY:
+ badsfmt();
+ case USEBCNT:
+ pr->bcnt = fu->bcnt;
+ break;
+ case USEPREC:
+ pr->bcnt = prec;
+ break;
+ }
+ break;
+ case '_':
+ ++p2;
+ switch(p1[1]) {
+ case 'A':
+ endfu = fu;
+ fu->flags |= F_IGNORE;
+ /* FALLTHROUGH */
+ case 'a':
+ pr->flags = F_ADDRESS;
+ ++p2;
+ switch(p1[2]) {
+ case 'd': case 'o': case'x':
+ cs[0] = 'q';
+ cs[1] = p1[2];
+ cs[2] = '\0';
+ break;
+ default:
+ p1[3] = '\0';
+ badconv(p1);
+ }
+ break;
+ case 'c':
+ pr->flags = F_C;
+ /* cs[0] = 'c'; set in conv_c */
+ goto isint2;
+ case 'p':
+ pr->flags = F_P;
+ cs[0] = 'c';
+ goto isint2;
+ case 'u':
+ pr->flags = F_U;
+ /* cs[0] = 'c'; set in conv_u */
+isint2: switch(fu->bcnt) {
+ case 0: case 1:
+ pr->bcnt = 1;
+ break;
+ default:
+ p1[2] = '\0';
+ badcnt(p1);
+ }
+ break;
+ default:
+ p1[2] = '\0';
+ badconv(p1);
+ }
+ break;
+ default:
+ p1[1] = '\0';
+ badconv(p1);
+ }
+
+ /*
+ * Copy to PR format string, set conversion character
+ * pointer, update original.
+ */
+ savech = *p2;
+ p1[0] = '\0';
+ pr->fmt = emalloc(strlen(fmtp) + 2);
+ (void)strcpy(pr->fmt, fmtp);
+ (void)strcat(pr->fmt, cs);
+ *p2 = savech;
+ pr->cchar = pr->fmt + (p1 - fmtp);
+ fmtp = p2;
+
+ /* Only one conversion character if byte count. */
+ if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++)
+ err("byte count with multiple conversion characters");
+ }
+ /*
+ * If format unit byte count not specified, figure it out
+ * so can adjust rep count later.
+ */
+ if (!fu->bcnt)
+ for (pr = fu->nextpr; pr; pr = pr->nextpr)
+ fu->bcnt += pr->bcnt;
+ }
+ /*
+ * If the format string interprets any data at all, and it's
+ * not the same as the blocksize, and its last format unit
+ * interprets any data at all, and has no iteration count,
+ * repeat it as necessary.
+ *
+ * If, rep count is greater than 1, no trailing whitespace
+ * gets output from the last iteration of the format unit.
+ */
+ for (fu = fs->nextfu;; fu = fu->nextfu) {
+ if (!fu->nextfu && fs->bcnt < blocksize &&
+ !(fu->flags&F_SETREP) && fu->bcnt)
+ fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
+ if (fu->reps > 1) {
+ for (pr = fu->nextpr;; pr = pr->nextpr)
+ if (!pr->nextpr)
+ break;
+ for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+ p2 = isspace(*p1) ? p1 : NULL;
+ if (p2)
+ pr->nospace = p2;
+ }
+ if (!fu->nextfu)
+ break;
+ }
+#ifdef DEBUG
+ for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+ (void)printf("fmt:");
+ for (pr = fu->nextpr; pr; pr = pr->nextpr)
+ (void)printf(" {%s}", pr->fmt);
+ (void)printf("\n");
+ }
+#endif
+}
+
+void
+escape(p1)
+ register char *p1;
+{
+ register char *p2;
+
+ /* alphabetic escape sequences have to be done in place */
+ for (p2 = p1;; ++p1, ++p2) {
+ if (!*p1) {
+ *p2 = *p1;
+ break;
+ }
+ if (*p1 == '\\')
+ switch(*++p1) {
+ case 'a':
+ /* *p2 = '\a'; */
+ *p2 = '\007';
+ break;
+ case 'b':
+ *p2 = '\b';
+ break;
+ case 'f':
+ *p2 = '\f';
+ break;
+ case 'n':
+ *p2 = '\n';
+ break;
+ case 'r':
+ *p2 = '\r';
+ break;
+ case 't':
+ *p2 = '\t';
+ break;
+ case 'v':
+ *p2 = '\v';
+ break;
+ default:
+ *p2 = *p1;
+ break;
+ }
+ }
+}
+
+void
+badcnt(s)
+ char *s;
+{
+ err("%s: bad byte count", s);
+}
+
+void
+badsfmt()
+{
+ err("%%s: requires a precision or a byte count\n");
+}
+
+void
+badfmt(fmt)
+ char *fmt;
+{
+ err("\"%s\": bad format\n", fmt);
+}
+
+void
+badconv(ch)
+ char *ch;
+{
+ err("%%%s: bad conversion character\n", ch);
+}
diff --git a/usr.bin/host/Makefile b/usr.bin/host/Makefile
new file mode 100644
index 0000000..dd408a3
--- /dev/null
+++ b/usr.bin/host/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= host
+MAN1= host.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/host/host.1 b/usr.bin/host/host.1
index 9e1827d..29f9411 100644
--- a/usr.bin/host/host.1
+++ b/usr.bin/host/host.1
@@ -50,7 +50,7 @@
.\" SOFTWARE.
.\" -
.\" --Copyright--
-.\" $Id: host.1,v 8.1 1994/12/15 06:24:10 vixie Exp $
+.\" $Id: host.1,v 1.2 1994/09/22 21:52:02 pst Exp $
.TH HOST 1
.SH NAME
host \- look up host names using domain server
diff --git a/usr.bin/host/host.c b/usr.bin/host/host.c
index ec79455..22839c3 100644
--- a/usr.bin/host/host.c
+++ b/usr.bin/host/host.c
@@ -3,7 +3,7 @@
* -
* Copyright (c) 1986
* The Regents of the University of California. All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -19,7 +19,7 @@
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -33,14 +33,14 @@
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
- *
+ *
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
@@ -61,13 +61,13 @@ char copyright[] =
#endif /* not lint */
/*
- * Actually, this program is from Rutgers University, however it is
+ * Actually, this program is from Rutgers University, however it is
* based on nslookup and other pieces of named tools, so it needs
* that copyright notice.
*/
#ifndef lint
-static char rcsid[] = "$Id: host.c,v 8.7 1995/08/22 05:01:49 vixie Exp $";
+static char rcsid[] = "$Id: host.c,v 1.3 1995/05/30 06:30:50 rgrimes Exp $";
#endif /* not lint */
#include <sys/types.h>
@@ -83,7 +83,7 @@ static char rcsid[] = "$Id: host.c,v 8.7 1995/08/22 05:01:49 vixie Exp $";
#include <resolv.h>
#include <ctype.h>
-#include "../conf/portability.h"
+#include <string.h>
extern int h_errno;
@@ -125,7 +125,6 @@ char *pr_class(), *pr_rr(), *pr_type();
extern char *hostalias();
main(c, v)
- int c;
char **v;
{
unsigned addr;
@@ -190,12 +189,12 @@ main(c, v)
gettype = T_ANY;
v++;
c--;
- }
+ }
}
if (c > 2) {
s = v[2];
server_specified++;
-
+
if (!inet_aton(s, (struct in_addr *)&addr)) {
hp = gethostbyname(s);
if (hp == NULL) {
@@ -243,7 +242,6 @@ main(c, v)
printf("Too many cnames. Possible loop.\n");
exit(1);
}
- strcat(cname, ".");
oldcname = cname;
hp = NULL;
h_errno = TRY_AGAIN;
@@ -482,7 +480,7 @@ gethostinfo(name)
if (n == 0 && (cp = hostalias(name))) {
if (verbose)
printf("Aliased to \"%s\"\n", cp);
- _res.options |= RES_DEFNAMES;
+ _res.options |= RES_DEFNAMES;
return (getdomaininfo(cp, (char *)NULL));
}
if (n >= _res.ndots) {
@@ -541,7 +539,6 @@ getdomaininfo(name, domain)
getinfo(name, domain, type)
char *name, *domain;
- int type;
{
HEADER *hp;
@@ -595,7 +592,7 @@ printinfo(answer, eom, filter, isls)
nscount = ntohs(hp->nscount);
arcount = ntohs(hp->arcount);
if (_res.options & RES_DEBUG || (verbose && isls == 0))
- printf("rcode = %d (%s), ancount=%d\n",
+ printf("rcode = %d (%s), ancount=%d\n",
hp->rcode, DecodeError(hp->rcode), ancount);
if (hp->rcode != NOERROR || (ancount+nscount+arcount) == 0) {
switch (hp->rcode) {
@@ -749,9 +746,9 @@ pr_rr(cp, msg, file, filter)
}
break;
case T_CNAME:
- if (dn_expand(msg, msg + 512, cp, cnamebuf,
+ if (dn_expand(msg, msg + 512, cp, cnamebuf,
sizeof(cnamebuf)) >= 0)
- cname = cnamebuf;
+ cname = cnamebuf;
case T_MB:
#ifdef OLDRR
case T_MD:
@@ -847,7 +844,7 @@ pr_rr(cp, msg, file, filter)
{
int n,j;
u_char * end = cp + dlen;
-
+
if (doprint)
(void) fputs(" \"", file);
while (cp < end) {
@@ -1072,7 +1069,7 @@ char *resultcodes[] = {
*
* Results:
* SUCCESS the listing was successful.
- * ERROR the server could not be contacted because
+ * ERROR the server could not be contacted because
* a socket could not be obtained or an error
* occured while receiving, or the output file
* could not be opened.
@@ -1116,8 +1113,8 @@ ListHosts(namePtr, queryType)
int thisns;
struct hostent *hp;
enum {
- NO_ERRORS,
- ERR_READING_LEN,
+ NO_ERRORS,
+ ERR_READING_LEN,
ERR_READING_MSG,
ERR_PRINTING
} error = NO_ERRORS;
@@ -1150,13 +1147,13 @@ ListHosts(namePtr, queryType)
}
msglen = res_send(buf.qb2, msglen, answer.qb2, sizeof answer);
-
+
if (msglen < 0) {
printf("Unable to get to nameserver -- try again later\n");
return (ERROR);
}
if (_res.options & RES_DEBUG || verbose)
- printf("rcode = %d (%s), ancount=%d\n",
+ printf("rcode = %d (%s), ancount=%d\n",
answer.qb1.rcode, DecodeError(answer.qb1.rcode),
ntohs(answer.qb1.ancount));
@@ -1211,7 +1208,7 @@ ListHosts(namePtr, queryType)
dlen = _getshort(cp);
cp += INT16SZ;
if (type == T_NS) {
- if (dn_expand(answer.qb2, answer.qb2 + msglen, cp,
+ if (dn_expand(answer.qb2, answer.qb2 + msglen, cp,
name, sizeof(name)) >= 0) {
if (numns < NUMNS && strcasecmp((char *)domain, namePtr) == 0) {
for (i = 0; i < numns; i++)
@@ -1316,7 +1313,7 @@ again:
perror("Connection failed, trying next server");
(void) close(sockFD);
sockFD = -1;
- }
+ }
if (thisns >= numnsaddr) {
printf("No server for that domain responded\n");
if (!verbose)
@@ -1325,7 +1322,7 @@ again:
}
/*
- * Send length & message for zone transfer
+ * Send length & message for zone transfer
*/
__putshort(msglen, (u_char *)&len);
@@ -1355,7 +1352,7 @@ again:
if (numRead <= 0) {
error = ERR_READING_LEN;
break;
- }
+ }
if ((len = _getshort((u_char*)&buf)) == 0) {
break; /* nothing left to read */
@@ -1382,7 +1379,7 @@ again:
(i == SERVFAIL || i == NOTIMP || i == REFUSED)) {
if (_res.options & RES_DEBUG || verbose)
printf("Server failed, trying next server: %s\n",
- i != NOERROR ?
+ i != NOERROR ?
DecodeError(i) : "Premature end of data");
(void) close(sockFD);
sockFD = -1;
@@ -1429,7 +1426,7 @@ again:
return(ERROR);
case ERR_PRINTING:
- fprintf(stderr,"*** Error during listing of %s: %s\n",
+ fprintf(stderr,"*** Error during listing of %s: %s\n",
namePtr, DecodeError(result));
return(result);
@@ -1437,9 +1434,9 @@ again:
headerPtr = (HEADER *) &buf;
fprintf(stderr,"ListHosts: error receiving zone transfer:\n");
fprintf(stderr,
- " result: %s, answers = %d, authority = %d, additional = %d\n",
- resultcodes[headerPtr->rcode],
- ntohs(headerPtr->ancount), ntohs(headerPtr->nscount),
+ " result: %s, answers = %d, authority = %d, additional = %d\n",
+ resultcodes[headerPtr->rcode],
+ ntohs(headerPtr->ancount), ntohs(headerPtr->nscount),
ntohs(headerPtr->arcount));
return(ERROR);
default:
@@ -1467,5 +1464,5 @@ DecodeError(result)
case NONAUTH: return("Non-authoritative answer"); break;
default: break;
}
- return("BAD ERROR VALUE");
+ return("BAD ERROR VALUE");
}
diff --git a/usr.bin/id/Makefile b/usr.bin/id/Makefile
new file mode 100644
index 0000000..e828d69
--- /dev/null
+++ b/usr.bin/id/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= id
+MAN1= id.1 groups.1 whoami.1
+
+# XXX BROKEN: afterinstall:
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/groups.sh ${DESTDIR}/usr/bin/groups
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/whoami.sh ${DESTDIR}/usr/bin/whoami
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/id/groups.1 b/usr.bin/id/groups.1
new file mode 100644
index 0000000..35653dc
--- /dev/null
+++ b/usr.bin/id/groups.1
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)groups.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd "June 6, 1993"
+.Dt GROUPS 1
+.UC
+.Sh NAME
+.Nm groups
+.Nd show group memberships
+.Sh SYNOPSIS
+.Nm groups
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm groups
+utility has been obsoleted by the
+.Xr id 1
+utility, and is equivalent to
+.Dq Nm id Fl Gn Op Ar user .
+The command
+.Dq Nm id Fl p
+is suggested for normal interactive use.
+.Pp
+The
+.Nm groups
+utility displays the groups to which you (or the optionally specified user)
+belong.
+.Pp
+The
+.Nm groups
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr id 1
diff --git a/usr.bin/id/groups.sh b/usr.bin/id/groups.sh
new file mode 100644
index 0000000..734fcb2
--- /dev/null
+++ b/usr.bin/id/groups.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -
+exec id -Gn $*
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)groups.sh 8.1 (Berkeley) 6/6/93
+#
+
diff --git a/usr.bin/id/id.1 b/usr.bin/id/id.1
new file mode 100644
index 0000000..99010fc
--- /dev/null
+++ b/usr.bin/id/id.1
@@ -0,0 +1,139 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)id.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd "June 6, 1993"
+.Dt ID 1
+.Os BSD 4.4
+.Sh NAME
+.Nm id
+.Nd return user identity
+.Sh SYNOPSIS
+.Nm id
+.Op Ar user
+.Nm id
+.Fl G Op Fl n
+.Op Ar user
+.Nm id
+.Fl g Op Fl nr
+.Op Ar user
+.Nm id
+.Fl p
+.Nm id
+.Fl u Op Fl nr
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm id
+utility displays the user and group names and numeric IDs, of the
+calling process, to the standard output.
+If the real and effective IDs are different, both are displayed,
+otherwise only the real ID is displayed.
+.Pp
+If a
+.Ar user
+(login name or user ID)
+is specified, the user and group IDs of that user are displayed.
+In this case, the real and effective IDs are assumed to be the same.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl G
+Display the different group IDs (effective, real and supplementary)
+as white-space separated numbers, in no particular order.
+.It Fl g
+Display the effective group ID as a number.
+.It Fl n
+Display the name of the user or group ID for the
+.Fl G ,
+.Fl g
+and
+.Fl u
+options instead of the number.
+If any of the ID numbers cannot be mapped into names, the number will be
+displayed as usual.
+.It Fl p
+Make the output human-readable.
+If the user name returned by
+.Xr getlogin 2
+is different from the login name referenced by the user ID, the name
+returned by
+.Xr getlogin 2
+is displayed, preceded by the keyword ``login''.
+The user ID as a name is displayed, preceded by the keyword ``uid''.
+If the effective user ID is different from the real user ID, the real user
+ID is displayed as a name, preceded by the keyword ``euid''.
+If the effective group ID is different from the real group ID, the real group
+ID is displayed as a name, preceded by the keyword ``rgid''.
+The list of groups to which the user belongs is then displayed as names,
+preceded by the keyword ``groups''.
+Each display is on a separate line.
+.It Fl r
+Display the real ID for the
+.Fl g
+and
+.Fl u
+options instead of the effective ID.
+.It Fl u
+Display the effective user ID as a number.
+.El
+.Pp
+The
+.Nm id
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr who 1
+.Sh STANDARDS
+The
+.Nm id
+function is expected to conform to
+.St -p1003.2 .
+.Sh HISTORY
+The
+historic
+.Xr groups 1
+command is equivalent to
+.Dq Nm id Fl Gn Op Ar user .
+.Pp
+The
+historic
+.Xr whoami 1
+command is equivalent to
+.Dq Nm id Fl un .
+.Pp
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/id/id.c b/usr.bin/id/id.c
new file mode 100644
index 0000000..61cae11
--- /dev/null
+++ b/usr.bin/id/id.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)id.c 8.2 (Berkeley) 2/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void current __P((void));
+void err __P((const char *, ...));
+void pretty __P((struct passwd *));
+void group __P((struct passwd *, int));
+void usage __P((void));
+void user __P((struct passwd *));
+struct passwd *
+ who __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct group *gr;
+ struct passwd *pw;
+ int Gflag, ch, gflag, id, nflag, pflag, rflag, uflag;
+
+ Gflag = gflag = nflag = pflag = rflag = uflag = 0;
+ while ((ch = getopt(argc, argv, "Ggnpru")) != EOF)
+ switch(ch) {
+ case 'G':
+ Gflag = 1;
+ break;
+ case 'g':
+ gflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(Gflag + gflag + pflag + uflag) {
+ case 1:
+ break;
+ case 0:
+ if (!nflag && !rflag)
+ break;
+ /* FALLTHROUGH */
+ default:
+ usage();
+ }
+
+ pw = *argv ? who(*argv) : NULL;
+
+ if (gflag) {
+ id = pw ? pw->pw_gid : rflag ? getgid() : getegid();
+ if (nflag && (gr = getgrgid(id)))
+ (void)printf("%s\n", gr->gr_name);
+ else
+ (void)printf("%u\n", id);
+ exit(0);
+ }
+
+ if (uflag) {
+ id = pw ? pw->pw_uid : rflag ? getuid() : geteuid();
+ if (nflag && (pw = getpwuid(id)))
+ (void)printf("%s\n", pw->pw_name);
+ else
+ (void)printf("%u\n", id);
+ exit(0);
+ }
+
+ if (Gflag) {
+ group(pw, nflag);
+ exit(0);
+ }
+
+ if (pflag) {
+ pretty(pw);
+ exit(0);
+ }
+
+ if (pw)
+ user(pw);
+ else
+ current();
+ exit(0);
+}
+
+void
+pretty(pw)
+ struct passwd *pw;
+{
+ struct group *gr;
+ u_int eid, rid;
+ char *login;
+
+ if (pw) {
+ (void)printf("uid\t%s\n", pw->pw_name);
+ (void)printf("groups\t");
+ group(pw, 1);
+ } else {
+ if ((login = getlogin()) == NULL)
+ err("getlogin: %s", strerror(errno));
+
+ pw = getpwuid(rid = getuid());
+ if (pw == NULL || strcmp(login, pw->pw_name))
+ (void)printf("login\t%s\n", login);
+ if (pw)
+ (void)printf("uid\t%s\n", pw->pw_name);
+ else
+ (void)printf("uid\t%u\n", rid);
+
+ if ((eid = geteuid()) != rid)
+ if (pw = getpwuid(eid))
+ (void)printf("euid\t%s", pw->pw_name);
+ else
+ (void)printf("euid\t%u", eid);
+ if ((rid = getgid()) != (eid = getegid()))
+ if (gr = getgrgid(rid))
+ (void)printf("rgid\t%s\n", gr->gr_name);
+ else
+ (void)printf("rgid\t%u\n", rid);
+ (void)printf("groups\t");
+ group(NULL, 1);
+ }
+}
+
+void
+current()
+{
+ struct group *gr;
+ struct passwd *pw;
+ int cnt, id, eid, lastid, ngroups;
+ gid_t groups[NGROUPS];
+ char *fmt;
+
+ id = getuid();
+ (void)printf("uid=%u", id);
+ if (pw = getpwuid(id))
+ (void)printf("(%s)", pw->pw_name);
+ if ((eid = geteuid()) != id) {
+ (void)printf(" euid=%u", eid);
+ if (pw = getpwuid(eid))
+ (void)printf("(%s)", pw->pw_name);
+ }
+ id = getgid();
+ (void)printf(" gid=%u", id);
+ if (gr = getgrgid(id))
+ (void)printf("(%s)", gr->gr_name);
+ if ((eid = getegid()) != id) {
+ (void)printf(" egid=%u", eid);
+ if (gr = getgrgid(eid))
+ (void)printf("(%s)", gr->gr_name);
+ }
+ if (ngroups = getgroups(NGROUPS, groups)) {
+ for (fmt = " groups=%u", lastid = -1, cnt = 0; cnt < ngroups;
+ fmt = ", %u", lastid = id) {
+ id = groups[cnt++];
+ if (lastid == id)
+ continue;
+ (void)printf(fmt, id);
+ if (gr = getgrgid(id))
+ (void)printf("(%s)", gr->gr_name);
+ }
+ }
+ (void)printf("\n");
+}
+
+void
+user(pw)
+ register struct passwd *pw;
+{
+ register struct group *gr;
+ register char *fmt, **p;
+ int cnt, gid, lastgid, ngroups, groups[NGROUPS + 1];
+
+ (void)printf("uid=%u(%s)", pw->pw_uid, pw->pw_name);
+ gid = pw->pw_gid;
+ (void)printf(" gid=%u", gid);
+ if (gr = getgrgid(gid))
+ (void)printf("(%s)", gr->gr_name);
+ ngroups = NGROUPS + 1;
+ (void) getgrouplist(pw->pw_name, gid, groups, &ngroups);
+ fmt = " groups=%u";
+ for (lastgid = -1, cnt = 0; cnt < ngroups; ++cnt) {
+ if (lastgid == (gid = groups[cnt]))
+ continue;
+ (void)printf(fmt, gid);
+ fmt = " %u";
+ if (gr = getgrgid(gid))
+ (void)printf("(%s)", gr->gr_name);
+ lastgid = gid;
+ }
+ (void)printf("\n");
+}
+
+void
+group(pw, nflag)
+ struct passwd *pw;
+ int nflag;
+{
+ struct group *gr;
+ int cnt, id, lastid, ngroups;
+ gid_t groups[NGROUPS + 1];
+ char *fmt;
+
+ if (pw) {
+ ngroups = NGROUPS + 1;
+ (void) getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);
+ } else {
+ groups[0] = getgid();
+ ngroups = getgroups(NGROUPS, groups + 1) + 1;
+ }
+ fmt = nflag ? "%s" : "%u";
+ for (lastid = -1, cnt = 0; cnt < ngroups; ++cnt) {
+ if (lastid == (id = groups[cnt]))
+ continue;
+ if (nflag) {
+ if (gr = getgrgid(id))
+ (void)printf(fmt, gr->gr_name);
+ else
+ (void)printf(*fmt == ' ' ? " %u" : "%u",
+ id);
+ fmt = " %s";
+ } else {
+ (void)printf(fmt, id);
+ fmt = " %u";
+ }
+ lastid = id;
+ }
+ (void)printf("\n");
+}
+
+struct passwd *
+who(u)
+ char *u;
+{
+ struct passwd *pw;
+ long id;
+ char *ep;
+
+ /*
+ * Translate user argument into a pw pointer. First, try to
+ * get it as specified. If that fails, try it as a number.
+ */
+ if (pw = getpwnam(u))
+ return(pw);
+ id = strtol(u, &ep, 10);
+ if (*u && !*ep && (pw = getpwuid(id)))
+ return(pw);
+ err("%s: No such user", u);
+ /* NOTREACHED */
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "id: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: id [user]\n");
+ (void)fprintf(stderr, " id -G [-n] [user]\n");
+ (void)fprintf(stderr, " id -g [-nr] [user]\n");
+ (void)fprintf(stderr, " id -u [-nr] [user]\n");
+ exit(1);
+}
diff --git a/usr.bin/id/whoami.1 b/usr.bin/id/whoami.1
new file mode 100644
index 0000000..7cf7b43
--- /dev/null
+++ b/usr.bin/id/whoami.1
@@ -0,0 +1,61 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whoami.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd "June 6, 1993"
+.Dt WHOAMI 1
+.UC
+.Sh NAME
+.Nm whoami
+.Nd display effective user id
+.Sh SYNOPSIS
+.Nm whoami
+.Sh DESCRIPTION
+The
+.Nm whoami
+utility has been obsoleted by the
+.Xr id 1
+utility, and is equivalent to
+.Dq Nm id Fl un .
+The command
+.Dq Nm id Fl p
+is suggested for normal interactive use.
+.Pp
+The
+.Nm whoami
+utility displays your effective user ID as a name.
+.Pp
+The
+.Nm whoami
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr id 1
diff --git a/usr.bin/id/whoami.sh b/usr.bin/id/whoami.sh
new file mode 100644
index 0000000..a5b9887
--- /dev/null
+++ b/usr.bin/id/whoami.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -
+exec id -un
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)whoami.sh 8.1 (Berkeley) 6/6/93
+#
+
diff --git a/usr.bin/indent/Makefile b/usr.bin/indent/Makefile
new file mode 100644
index 0000000..8d9ff94
--- /dev/null
+++ b/usr.bin/indent/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= indent
+SRCS= indent.c io.c lexi.c parse.c pr_comment.c args.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/indent/README b/usr.bin/indent/README
new file mode 100644
index 0000000..618f1de
--- /dev/null
+++ b/usr.bin/indent/README
@@ -0,0 +1,97 @@
+This is the C indenter, it originally came from the University of Illinois
+via some distribution tape for PDP-11 Unix. It has subsequently been
+hacked upon by James Gosling @ CMU. It isn't very pretty, and really needs
+to be completely redone, but it is probably the nicest C pretty printer
+around.
+
+Further additions to provide "Kernel Normal Form" were contributed
+by the folks at Sun Microsystems.
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+> From mnetor!yunexus!oz@uunet.UU.NET Wed Mar 9 15:30:55 1988
+> Date: Tue, 8 Mar 88 18:36:25 EST
+> From: yunexus!oz@uunet.UU.NET (Ozan Yigit)
+> To: bostic@okeeffe.berkeley.edu
+> Cc: ccvaxa!willcox@uunet.UU.NET, jag@sun.com, rsalz@uunet.UU.NET
+> In-Reply-To: Keith Bostic's message of Tue, 16 Feb 88 16:09:06 PST
+> Subject: Re: Indent...
+
+Thank you for your response about indent. I was wrong in my original
+observation (or mis-observation :-). UCB did keep the Illinois
+copyright intact.
+
+The issue still is whether we can distribute indent, and if we can, which
+version. David Willcox (the author) states that:
+
+| Several people have asked me on what basis I claim that indent is in
+| the public domain. I knew I would be sorry I made that posting.
+|
+| Some history. Way back in 1976, the project I worked on at the
+| University of Illinois Center for Advanced Computation had a huge
+| battle about how to format C code. After about a week of fighting, I
+| got disgusted and wrote a program, which I called indent, to reformat C
+| code. It had a bunch of different options that would let you format
+| the output the way you liked. In particular, all of the different
+| formats being championed were supported.
+|
+| It was my first big C program. It was ugly. It wasn't designed, it
+| just sort of grew. But it pretty much worked, and it stopped most of
+| the fighting.
+|
+| As a matter of form, I included a University of Illinois Copyright
+| notice. However, my understanding was that, since the work was done
+| on an ARPA contract, it was in the public domain.
+|
+| Time passed. Some years later, indent showed up on one of the early
+| emacs distributions.
+|
+| Later still, someone from UC Berlekey called the UofI and asked if
+| indent was in the public domain. They wanted to include it in their
+| UNIX distributions, along with the emacs stuff. I was no longer at the
+| UofI, but Rob Kolstad, who was, asked me about it. I told him I didn't
+| care if they used it, and since then it has been on the BSD distributions.
+|
+| Somewhere along the way, several other unnamed people have had their
+| hands in it. It was converted to understand version 7 C. (The
+| original was version 6.) It was converted from its original filter
+| interface to its current "blow away the user's file" interface.
+| The $HOME/.indent.pro file parsing was added. Some more formatting
+| options were added.
+|
+| The source I have right now has two copyright notices. One is the
+| original from the UofI. One is from Berkeley.
+|
+| I am not a lawyer, and I certainly do not understand copyright law. As
+| far as I am concerned, the bulk of this program, everything covered by
+| the UofI copyright, is in the public domain, and worth every penny.
+| Berkeley's copyright probably should only cover their changes, and I
+| don't know their feelings about sending it out.
+
+In any case, there appears to be noone at UofI to clarify/and change
+that copyright, but I am confident (based on the statements of its
+author) that the code, as it stands with its copyright, is
+distributable, and will not cause any legal problems.
+
+Hence, the issue reduces to *which* one to distribute through
+comp.sources.unix. I would suggest that with the permission of you
+folks (given that you have parts copyrighted), we distribute the 4.3
+version of indent, which appears to be the most up-to-date version. I
+happen to have just about every known version of indent, including the
+very original submission from the author to a unix tape, later the
+G-Emacs version, any 4.n version, sun version and the Unipress
+version. I still think we should not have to "go-back-in-time" and
+re-do all the work you people have done.
+
+I hope to hear from you as to what you think about this. You may of
+course send 4.3 version to the moderator directly, or you can let me
+know of your permission, and I will send the sources, or you can let
+me know that 4.3 version is off-limits, in which case we would probably
+have to revert to an older version. One way or another, I hope to get
+a version of indent to comp.sources.unix.
+
+regards.. oz
+
+cc: ccvaxa!willcox
+ sun.com!jar
+ uunet!rsalz
+
diff --git a/usr.bin/indent/args.c b/usr.bin/indent/args.c
new file mode 100644
index 0000000..e62f038
--- /dev/null
+++ b/usr.bin/indent/args.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)args.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Argument scanning and profile reading code. Default parameters are set
+ * here as well.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "indent_globs.h"
+
+/* profile types */
+#define PRO_SPECIAL 1 /* special case */
+#define PRO_BOOL 2 /* boolean */
+#define PRO_INT 3 /* integer */
+#define PRO_FONT 4 /* troff font */
+
+/* profile specials for booleans */
+#define ON 1 /* turn it on */
+#define OFF 0 /* turn it off */
+
+/* profile specials for specials */
+#define IGN 1 /* ignore it */
+#define CLI 2 /* case label indent (float) */
+#define STDIN 3 /* use stdin */
+#define KEY 4 /* type (keyword) */
+
+char *option_source = "?";
+
+/*
+ * N.B.: because of the way the table here is scanned, options whose names are
+ * substrings of other options must occur later; that is, with -lp vs -l, -lp
+ * must be first. Also, while (most) booleans occur more than once, the last
+ * default value is the one actually assigned.
+ */
+struct pro {
+ char *p_name; /* name, eg -bl, -cli */
+ int p_type; /* type (int, bool, special) */
+ int p_default; /* the default value (if int) */
+ int p_special; /* depends on type */
+ int *p_obj; /* the associated variable */
+} pro[] = {
+
+ "T", PRO_SPECIAL, 0, KEY, 0,
+ "bacc", PRO_BOOL, false, ON, &blanklines_around_conditional_compilation,
+ "badp", PRO_BOOL, false, ON, &blanklines_after_declarations_at_proctop,
+ "bad", PRO_BOOL, false, ON, &blanklines_after_declarations,
+ "bap", PRO_BOOL, false, ON, &blanklines_after_procs,
+ "bbb", PRO_BOOL, false, ON, &blanklines_before_blockcomments,
+ "bc", PRO_BOOL, true, OFF, &ps.leave_comma,
+ "bl", PRO_BOOL, true, OFF, &btype_2,
+ "br", PRO_BOOL, true, ON, &btype_2,
+ "bs", PRO_BOOL, false, ON, &Bill_Shannon,
+ "cdb", PRO_BOOL, true, ON, &comment_delimiter_on_blankline,
+ "cd", PRO_INT, 0, 0, &ps.decl_com_ind,
+ "ce", PRO_BOOL, true, ON, &cuddle_else,
+ "ci", PRO_INT, 0, 0, &continuation_indent,
+ "cli", PRO_SPECIAL, 0, CLI, 0,
+ "c", PRO_INT, 33, 0, &ps.com_ind,
+ "di", PRO_INT, 16, 0, &ps.decl_indent,
+ "dj", PRO_BOOL, false, ON, &ps.ljust_decl,
+ "d", PRO_INT, 0, 0, &ps.unindent_displace,
+ "eei", PRO_BOOL, false, ON, &extra_expression_indent,
+ "ei", PRO_BOOL, true, ON, &ps.else_if,
+ "fbc", PRO_FONT, 0, 0, (int *) &blkcomf,
+ "fbx", PRO_FONT, 0, 0, (int *) &boxcomf,
+ "fb", PRO_FONT, 0, 0, (int *) &bodyf,
+ "fc1", PRO_BOOL, true, ON, &format_col1_comments,
+ "fc", PRO_FONT, 0, 0, (int *) &scomf,
+ "fk", PRO_FONT, 0, 0, (int *) &keywordf,
+ "fs", PRO_FONT, 0, 0, (int *) &stringf,
+ "ip", PRO_BOOL, true, ON, &ps.indent_parameters,
+ "i", PRO_INT, 8, 0, &ps.ind_size,
+ "lc", PRO_INT, 0, 0, &block_comment_max_col,
+ "lp", PRO_BOOL, true, ON, &lineup_to_parens,
+ "l", PRO_INT, 78, 0, &max_col,
+ "nbacc", PRO_BOOL, false, OFF, &blanklines_around_conditional_compilation,
+ "nbadp", PRO_BOOL, false, OFF, &blanklines_after_declarations_at_proctop,
+ "nbad", PRO_BOOL, false, OFF, &blanklines_after_declarations,
+ "nbap", PRO_BOOL, false, OFF, &blanklines_after_procs,
+ "nbbb", PRO_BOOL, false, OFF, &blanklines_before_blockcomments,
+ "nbc", PRO_BOOL, true, ON, &ps.leave_comma,
+ "nbs", PRO_BOOL, false, OFF, &Bill_Shannon,
+ "ncdb", PRO_BOOL, true, OFF, &comment_delimiter_on_blankline,
+ "nce", PRO_BOOL, true, OFF, &cuddle_else,
+ "ndj", PRO_BOOL, false, OFF, &ps.ljust_decl,
+ "neei", PRO_BOOL, false, OFF, &extra_expression_indent,
+ "nei", PRO_BOOL, true, OFF, &ps.else_if,
+ "nfc1", PRO_BOOL, true, OFF, &format_col1_comments,
+ "nip", PRO_BOOL, true, OFF, &ps.indent_parameters,
+ "nlp", PRO_BOOL, true, OFF, &lineup_to_parens,
+ "npcs", PRO_BOOL, false, OFF, &proc_calls_space,
+ "npro", PRO_SPECIAL, 0, IGN, 0,
+ "npsl", PRO_BOOL, true, OFF, &procnames_start_line,
+ "nps", PRO_BOOL, false, OFF, &pointer_as_binop,
+ "nsc", PRO_BOOL, true, OFF, &star_comment_cont,
+ "nsob", PRO_BOOL, false, OFF, &swallow_optional_blanklines,
+ "nv", PRO_BOOL, false, OFF, &verbose,
+ "pcs", PRO_BOOL, false, ON, &proc_calls_space,
+ "psl", PRO_BOOL, true, ON, &procnames_start_line,
+ "ps", PRO_BOOL, false, ON, &pointer_as_binop,
+ "sc", PRO_BOOL, true, ON, &star_comment_cont,
+ "sob", PRO_BOOL, false, ON, &swallow_optional_blanklines,
+ "st", PRO_SPECIAL, 0, STDIN, 0,
+ "troff", PRO_BOOL, false, ON, &troff,
+ "v", PRO_BOOL, false, ON, &verbose,
+ /* whew! */
+ 0, 0, 0, 0, 0
+};
+
+/*
+ * set_profile reads $HOME/.indent.pro and ./.indent.pro and handles arguments
+ * given in these files.
+ */
+set_profile()
+{
+ register FILE *f;
+ char fname[BUFSIZ];
+ static char prof[] = ".indent.pro";
+
+ sprintf(fname, "%s/%s", getenv("HOME"), prof);
+ if ((f = fopen(option_source = fname, "r")) != NULL) {
+ scan_profile(f);
+ (void) fclose(f);
+ }
+ if ((f = fopen(option_source = prof, "r")) != NULL) {
+ scan_profile(f);
+ (void) fclose(f);
+ }
+ option_source = "Command line";
+}
+
+scan_profile(f)
+ register FILE *f;
+{
+ register int i;
+ register char *p;
+ char buf[BUFSIZ];
+
+ while (1) {
+ for (p = buf; (i = getc(f)) != EOF && (*p = i) > ' '; ++p);
+ if (p != buf) {
+ *p++ = 0;
+ if (verbose)
+ printf("profile: %s\n", buf);
+ set_option(buf);
+ }
+ else if (i == EOF)
+ return;
+ }
+}
+
+char *param_start;
+
+eqin(s1, s2)
+ register char *s1;
+ register char *s2;
+{
+ while (*s1) {
+ if (*s1++ != *s2++)
+ return (false);
+ }
+ param_start = s2;
+ return (true);
+}
+
+/*
+ * Set the defaults.
+ */
+set_defaults()
+{
+ register struct pro *p;
+
+ /*
+ * Because ps.case_indent is a float, we can't initialize it from the
+ * table:
+ */
+ ps.case_indent = 0.0; /* -cli0.0 */
+ for (p = pro; p->p_name; p++)
+ if (p->p_type != PRO_SPECIAL && p->p_type != PRO_FONT)
+ *p->p_obj = p->p_default;
+}
+
+set_option(arg)
+ register char *arg;
+{
+ register struct pro *p;
+ extern double atof();
+
+ arg++; /* ignore leading "-" */
+ for (p = pro; p->p_name; p++)
+ if (*p->p_name == *arg && eqin(p->p_name, arg))
+ goto found;
+ fprintf(stderr, "indent: %s: unknown parameter \"%s\"\n", option_source, arg - 1);
+ exit(1);
+found:
+ switch (p->p_type) {
+
+ case PRO_SPECIAL:
+ switch (p->p_special) {
+
+ case IGN:
+ break;
+
+ case CLI:
+ if (*param_start == 0)
+ goto need_param;
+ ps.case_indent = atof(param_start);
+ break;
+
+ case STDIN:
+ if (input == 0)
+ input = stdin;
+ if (output == 0)
+ output = stdout;
+ break;
+
+ case KEY:
+ if (*param_start == 0)
+ goto need_param;
+ {
+ register char *str = (char *) malloc(strlen(param_start) + 1);
+ strcpy(str, param_start);
+ addkey(str, 4);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "\
+indent: set_option: internal error: p_special %d\n", p->p_special);
+ exit(1);
+ }
+ break;
+
+ case PRO_BOOL:
+ if (p->p_special == OFF)
+ *p->p_obj = false;
+ else
+ *p->p_obj = true;
+ break;
+
+ case PRO_INT:
+ if (!isdigit(*param_start)) {
+ need_param:
+ fprintf(stderr, "indent: %s: ``%s'' requires a parameter\n",
+ option_source, arg - 1);
+ exit(1);
+ }
+ *p->p_obj = atoi(param_start);
+ break;
+
+ case PRO_FONT:
+ parsefont((struct fstate *) p->p_obj, param_start);
+ break;
+
+ default:
+ fprintf(stderr, "indent: set_option: internal error: p_type %d\n",
+ p->p_type);
+ exit(1);
+ }
+}
diff --git a/usr.bin/indent/indent.1 b/usr.bin/indent/indent.1
new file mode 100644
index 0000000..c44964a
--- /dev/null
+++ b/usr.bin/indent/indent.1
@@ -0,0 +1,452 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 1976 Board of Trustees of the University of Illinois.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)indent.1 8.1 (Berkeley) 7/1/93
+.\"
+.Dd July 1, 1993
+.Dt INDENT 1
+.Os BSD 4.2
+.Sh NAME
+.Nm indent
+.Nd indent and format C program source
+.Sh SYNOPSIS
+.Nm indent
+.Op Ar input-file Op Ar output-file
+.Op Fl bad | Fl nbad
+.Op Fl bap | Fl nbap
+.Bk -words
+.Op Fl bbb | Fl nbbb
+.Ek
+.Op Fl \&bc | Fl nbc
+.Op Fl \&bl
+.Op Fl \&br
+.Op Fl c Ns Ar n
+.Op Fl \&cd Ns Ar n
+.Bk -words
+.Op Fl cdb | Fl ncdb
+.Ek
+.Op Fl \&ce | Fl nce
+.Op Fl \&ci Ns Ar n
+.Op Fl cli Ns Ar n
+.Op Fl d Ns Ar n
+.Op Fl \&di Ns Ar n
+.Bk -words
+.Op Fl fc1 | Fl nfc1
+.Ek
+.Op Fl i Ns Ar n
+.Op Fl \&ip | Fl nip
+.Op Fl l Ns Ar n
+.Op Fl \&lc Ns Ar n
+.Op Fl \&lp | Fl nlp
+.Op Fl npro
+.Op Fl pcs | Fl npcs
+.Op Fl psl | Fl npsl
+.Op Fl \&sc | Fl nsc
+.Bk -words
+.Op Fl sob | Fl nsob
+.Ek
+.Op Fl \&st
+.Op Fl troff
+.Op Fl v | Fl \&nv
+.Sh DESCRIPTION
+.Nm Indent
+is a
+.Ar C
+program formatter. It reformats the
+.Ar C
+program in the
+.Ar input-file
+according to the switches. The switches which can be
+specified are described below. They may appear before or after the file
+names.
+.Pp
+.Sy NOTE :
+If you only specify an
+.Ar input-file ,
+the formatting is
+done `in-place', that is, the formatted file is written back into
+.Ar input-file
+and a backup copy of
+.Ar input-file
+is written in the current directory. If
+.Ar input-file
+is named
+.Sq Pa /blah/blah/file ,
+the backup file is named
+.Pa file.BAK .
+.Pp
+If
+.Ar output-file
+is specified,
+.Nm indent
+checks to make sure it is different from
+.Ar input-file .
+.Pp
+The options listed below control the formatting style imposed by
+.Nm indent .
+.Bl -tag -width Op
+.It Fl bad , nbad
+If
+.Fl bad
+is specified, a blank line is forced after every block of
+declarations. Default:
+.Fl nbad .
+.It Fl bap , nbap
+If
+.Fl bap
+is specified, a blank line is forced after every procedure body. Default:
+.Fl nbap .
+.It Fl bbb , nbbb
+If
+.Fl bbb
+is specified, a blank line is forced before every block comment. Default:
+.Fl nbbb .
+.It Fl \&bc , nbc
+If
+.Fl \&bc
+is specified, then a newline is forced after each comma in a declaration.
+.Fl nbc
+turns off this option. The default is
+.Fl \&bc .
+.It Fl \&br , \&bl
+Specifying
+.Fl \&bl
+lines up compound statements like this:
+.ne 4
+.Bd -literal -offset indent
+if (...)
+{
+ code
+}
+.Ed
+.Pp
+Specifying
+.Fl \&br
+(the default) makes them look like this:
+.ne 3
+.Bd -literal -offset indent
+if (...) {
+ code
+}
+.Ed
+.Pp
+.It Fl c n
+The column in which comments on code start. The default is 33.
+.It Fl cd n
+The column in which comments on declarations start. The default
+is for these comments to start in the same column as those on code.
+.It Fl cdb , ncdb
+Enables (disables) the placement of comment delimiters on blank lines. With
+this option enabled, comments look like this:
+.Bd -literal -offset indent
+.ne 3
+ /*
+ * this is a comment
+ */
+.Ed
+.Pp
+Rather than like this:
+.Bd -literal -offset indent
+ /* this is a comment */
+.Ed
+.Pp
+This only affects block comments, not comments to the right of
+code. The default is
+.Fl cdb .
+.It Fl ce , nce
+Enables (disables) forcing `else's to cuddle up to the immediately preceding
+`}'. The default is
+.Fl \&ce .
+.It Fl \&ci Ns Ar n
+Sets the continuation indent to be
+.Ar n .
+Continuation
+lines will be indented that far from the beginning of the first line of the
+statement. Parenthesized expressions have extra indentation added to
+indicate the nesting, unless
+.Fl \&lp
+is in effect.
+.Fl \&ci
+defaults to the same value as
+.Fl i .
+.It Fl cli Ns Ar n
+Causes case labels to be indented
+.Ar n
+tab stops to the right of the containing
+.Ic switch
+statement.
+.Fl cli0 .5
+causes case labels to be indented half a tab stop. The
+default is
+.Fl cli0 .
+.It Fl d Ns Ar n
+Controls the placement of comments which are not to the
+right of code. The default
+.Fl \&d\&1
+means that such comments are placed one indentation level to the
+left of code. Specifying
+.Fl \&d\&0
+lines up these comments with the code. See the section on comment
+indentation below.
+.It Fl \&di Ns Ar n
+Specifies the indentation, in character positions, from a declaration keyword
+to the following identifier. The default is
+.Fl di16 .
+.It Fl dj , ndj
+.Fl \&dj
+left justifies declarations.
+.Fl ndj
+indents declarations the same as code. The default is
+.Fl ndj .
+.It Fl \&ei , nei
+Enables (disables) special
+.Ic else-if
+processing. If it's enabled, an
+.Ic if
+following an
+.Ic else
+will have the same indentation as the preceding
+.Ic \&if
+statement.
+.It Fl fc1 , nfc1
+Enables (disables) the formatting of comments that start in column 1.
+Often, comments whose leading `/' is in column 1 have been carefully
+hand formatted by the programmer. In such cases,
+.Fl nfc1
+should be
+used. The default is
+.Fl fc1 .
+.It Fl i Ns Ar n
+The number of spaces for one indentation level. The default is 4.
+.It Fl \&ip , nip
+Enables (disables) the indentation of parameter declarations from the left
+margin. The default is
+.Fl \&ip .
+.It Fl l Ns Ar n
+Maximum length of an output line. The default is 75.
+.It Fl \&lp , nlp
+Lines up code surrounded by parenthesis in continuation lines. If a line
+has a left paren which is not closed on that line, then continuation lines
+will be lined up to start at the character position just after the left
+paren. For example, here is how a piece of continued code looks with
+.Fl nlp
+in effect:
+.ne 2
+.Bd -literal -offset indent
+p1 = first_procedure(second_procedure(p2, p3),
+\ \ third_procedure(p4,p5));
+.Ed
+.Pp
+.ne 5
+With
+.Fl lp
+in effect (the default) the code looks somewhat clearer:
+.Bd -literal -offset indent
+p1\ =\ first_procedure(second_procedure(p2,\ p3),
+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4,p5));
+.Ed
+.Pp
+.ne 5
+Inserting two more newlines we get:
+.Bd -literal -offset indent
+p1\ =\ first_procedure(second_procedure(p2,
+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p3),
+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ third_procedure(p4
+\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ p5));
+.Ed
+.It Fl npro
+Causes the profile files,
+.Sq Pa ./.indent.pro
+and
+.Sq Pa ~/.indent.pro ,
+to be ignored.
+.It Fl pcs , npcs
+If true
+.Pq Fl pcs
+all procedure calls will have a space inserted between
+the name and the `('. The default is
+.Fl npcs .
+.It Fl psl , npsl
+If true
+.Pq Fl psl
+the names of procedures being defined are placed in
+column 1 \- their types, if any, will be left on the previous lines. The
+default is
+.Fl psl .
+.It Fl \&sc , nsc
+Enables (disables) the placement of asterisks (`*'s) at the left edge of all
+comments.
+.It Fl sob , nsob
+If
+.Fl sob
+is specified, indent will swallow optional blank lines. You can use this to
+get rid of blank lines after declarations. Default:
+.Fl nsob .
+.It Fl \&st
+Causes
+.Nm indent
+to take its input from stdin, and put its output to stdout.
+.It Fl T Ns Ar typename
+Adds
+.Ar typename
+to the list of type keywords. Names accumulate:
+.Fl T
+can be specified more than once. You need to specify all the typenames that
+appear in your program that are defined by
+.Ic typedef
+\- nothing will be
+harmed if you miss a few, but the program won't be formatted as nicely as
+it should. This sounds like a painful thing to have to do, but it's really
+a symptom of a problem in C:
+.Ic typedef
+causes a syntactic change in the
+language and
+.Nm indent
+can't find all
+instances of
+.Ic typedef .
+.It Fl troff
+Causes
+.Nm indent
+to format the program for processing by
+.Xr troff 1 .
+It will produce a fancy
+listing in much the same spirit as
+.Xr vgrind 1 .
+If the output file is not specified, the default is standard output,
+rather than formatting in place.
+.It Fl v , \&nv
+.Fl v
+turns on `verbose' mode;
+.Fl \&nv
+turns it off. When in verbose mode,
+.Nm indent
+reports when it splits one line of input into two or more lines of output,
+and gives some size statistics at completion. The default is
+.Fl \&nv .
+.El
+.Pp
+You may set up your own `profile' of defaults to
+.Nm indent
+by creating a file called
+.Pa .indent.pro
+in your login directory and/or the current directory and including
+whatever switches you like. A `.indent.pro' in the current directory takes
+precedence over the one in your login directory. If
+.Nm indent
+is run and a profile file exists, then it is read to set up the program's
+defaults. Switches on the command line, though, always override profile
+switches. The switches should be separated by spaces, tabs or newlines.
+.Pp
+.Ss Comments
+.Sq Em Box
+.Em comments .
+.Nm Indent
+assumes that any comment with a dash or star immediately after the start of
+comment (that is, `/*\-' or `/**') is a comment surrounded by a box of stars.
+Each line of such a comment is left unchanged, except that its indentation
+may be adjusted to account for the change in indentation of the first line
+of the comment.
+.Pp
+.Em Straight text .
+All other comments are treated as straight text.
+.Nm Indent
+fits as many words (separated by blanks, tabs, or newlines) on a
+line as possible. Blank lines break paragraphs.
+.Pp
+.Ss Comment indentation
+If a comment is on a line with code it is started in the `comment column',
+which is set by the
+.Fl c Ns Ns Ar n
+command line parameter. Otherwise, the comment is started at
+.Ar n
+indentation levels less than where code is currently being placed, where
+.Ar n
+is specified by the
+.Fl d Ns Ns Ar n
+command line parameter. If the code on a line extends past the comment
+column, the comment starts further to the right, and the right margin may be
+automatically extended in extreme cases.
+.Pp
+.Ss Preprocessor lines
+In general,
+.Nm indent
+leaves preprocessor lines alone. The only
+reformatting that it will do is to straighten up trailing comments. It
+leaves embedded comments alone. Conditional compilation
+.Pq Ic #ifdef...#endif
+is recognized and
+.Nm indent
+attempts to correctly
+compensate for the syntactic peculiarities introduced.
+.Pp
+.Ss C syntax
+.Nm Indent
+understands a substantial amount about the syntax of C, but it
+has a `forgiving' parser. It attempts to cope with the usual sorts of
+incomplete and misformed syntax. In particular, the use of macros like:
+.Pp
+.Dl #define forever for(;;)
+.Pp
+is handled properly.
+.Sh ENVIRONMENT
+.Nm Indent
+uses the
+.Ev HOME
+environment variable.
+.Sh FILES
+.Bl -tag -width "./.indent.pro" -compact
+.It Pa ./.indent.pro
+profile file
+.It Pa ~/.indent.pro
+profile file
+.El
+.Sh HISTORY
+The
+.Nm indent
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+.Nm Indent
+has even more switches than
+.Xr ls 1 .
+.Pp
+.ne 5
+A common mistake that often causes grief is typing:
+.Pp
+.Dl indent *.c
+.Pp
+to the shell in an attempt to indent all the
+.Nm C
+programs in a directory.
+This is probably a bug, not a feature.
diff --git a/usr.bin/indent/indent.c b/usr.bin/indent/indent.c
new file mode 100644
index 0000000..132d21f
--- /dev/null
+++ b/usr.bin/indent/indent.c
@@ -0,0 +1,1181 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1976 Board of Trustees of the University of Illinois.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985 Sun Microsystems, Inc.\n\
+@(#) Copyright (c) 1976 Board of Trustees of the University of Illinois.\n\
+@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)indent.c 5.17 (Berkeley) 6/7/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "indent_globs.h"
+#include "indent_codes.h"
+#include <ctype.h>
+
+char *in_name = "Standard Input"; /* will always point to name of input
+ * file */
+char *out_name = "Standard Output"; /* will always point to name
+ * of output file */
+char bakfile[MAXPATHLEN] = "";
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ extern int found_err; /* flag set in diag() on error */
+ int dec_ind; /* current indentation for declarations */
+ int di_stack[20]; /* a stack of structure indentation levels */
+ int flushed_nl; /* used when buffering up comments to remember
+ * that a newline was passed over */
+ int force_nl; /* when true, code must be broken */
+ int hd_type; /* used to store type of stmt for if (...),
+ * for (...), etc */
+ register int i; /* local loop counter */
+ int scase; /* set to true when we see a case, so we will
+ * know what to do with the following colon */
+ int sp_sw; /* when true, we are in the expressin of
+ * if(...), while(...), etc. */
+ int squest; /* when this is positive, we have seen a ?
+ * without the matching : in a <c>?<s>:<s>
+ * construct */
+ register char *t_ptr; /* used for copying tokens */
+ int type_code; /* the type of token, returned by lexi */
+
+ int last_else = 0; /* true iff last keyword was an else */
+
+
+ /*-----------------------------------------------*\
+ | INITIALIZATION |
+ \*-----------------------------------------------*/
+
+
+ ps.p_stack[0] = stmt; /* this is the parser's stack */
+ ps.last_nl = true; /* this is true if the last thing scanned was
+ * a newline */
+ ps.last_token = semicolon;
+ combuf = (char *) malloc(bufsize);
+ labbuf = (char *) malloc(bufsize);
+ codebuf = (char *) malloc(bufsize);
+ tokenbuf = (char *) malloc(bufsize);
+ l_com = combuf + bufsize - 5;
+ l_lab = labbuf + bufsize - 5;
+ l_code = codebuf + bufsize - 5;
+ l_token = tokenbuf + bufsize - 5;
+ combuf[0] = codebuf[0] = labbuf[0] = ' '; /* set up code, label, and
+ * comment buffers */
+ combuf[1] = codebuf[1] = labbuf[1] = '\0';
+ ps.else_if = 1; /* Default else-if special processing to on */
+ s_lab = e_lab = labbuf + 1;
+ s_code = e_code = codebuf + 1;
+ s_com = e_com = combuf + 1;
+ s_token = e_token = tokenbuf + 1;
+
+ in_buffer = (char *) malloc(10);
+ in_buffer_limit = in_buffer + 8;
+ buf_ptr = buf_end = in_buffer;
+ line_no = 1;
+ had_eof = ps.in_decl = ps.decl_on_line = break_comma = false;
+ sp_sw = force_nl = false;
+ ps.in_or_st = false;
+ ps.bl_line = true;
+ dec_ind = 0;
+ di_stack[ps.dec_nest = 0] = 0;
+ ps.want_blank = ps.in_stmt = ps.ind_stmt = false;
+
+
+ scase = ps.pcase = false;
+ squest = 0;
+ sc_end = 0;
+ bp_save = 0;
+ be_save = 0;
+
+ output = 0;
+
+
+
+ /*--------------------------------------------------*\
+ | COMMAND LINE SCAN |
+ \*--------------------------------------------------*/
+
+#ifdef undef
+ max_col = 78; /* -l78 */
+ lineup_to_parens = 1; /* -lp */
+ ps.ljust_decl = 0; /* -ndj */
+ ps.com_ind = 33; /* -c33 */
+ star_comment_cont = 1; /* -sc */
+ ps.ind_size = 8; /* -i8 */
+ verbose = 0;
+ ps.decl_indent = 16; /* -di16 */
+ ps.indent_parameters = 1; /* -ip */
+ ps.decl_com_ind = 0; /* if this is not set to some positive value
+ * by an arg, we will set this equal to
+ * ps.com_ind */
+ btype_2 = 1; /* -br */
+ cuddle_else = 1; /* -ce */
+ ps.unindent_displace = 0; /* -d0 */
+ ps.case_indent = 0; /* -cli0 */
+ format_col1_comments = 1; /* -fc1 */
+ procnames_start_line = 1; /* -psl */
+ proc_calls_space = 0; /* -npcs */
+ comment_delimiter_on_blankline = 1; /* -cdb */
+ ps.leave_comma = 1; /* -nbc */
+#endif
+
+ for (i = 1; i < argc; ++i)
+ if (strcmp(argv[i], "-npro") == 0)
+ break;
+ set_defaults();
+ if (i >= argc)
+ set_profile();
+
+ for (i = 1; i < argc; ++i) {
+
+ /*
+ * look thru args (if any) for changes to defaults
+ */
+ if (argv[i][0] != '-') {/* no flag on parameter */
+ if (input == 0) { /* we must have the input file */
+ in_name = argv[i]; /* remember name of input file */
+ input = fopen(in_name, "r");
+ if (input == 0) /* check for open error */
+ err(in_name);
+ continue;
+ }
+ else if (output == 0) { /* we have the output file */
+ out_name = argv[i]; /* remember name of output file */
+ if (strcmp(in_name, out_name) == 0) { /* attempt to overwrite
+ * the file */
+ fprintf(stderr, "indent: input and output files must be different\n");
+ exit(1);
+ }
+ output = fopen(out_name, "w");
+ if (output == 0) /* check for create error */
+ err(out_name);
+ continue;
+ }
+ fprintf(stderr, "indent: unknown parameter: %s\n", argv[i]);
+ exit(1);
+ }
+ else
+ set_option(argv[i]);
+ } /* end of for */
+ if (input == 0) {
+ fprintf(stderr, "indent: usage: indent file [ outfile ] [ options ]\n");
+ exit(1);
+ }
+ if (output == 0)
+ if (troff)
+ output = stdout;
+ else {
+ out_name = in_name;
+ bakcopy();
+ }
+ if (ps.com_ind <= 1)
+ ps.com_ind = 2; /* dont put normal comments before column 2 */
+ if (troff) {
+ if (bodyf.font[0] == 0)
+ parsefont(&bodyf, "R");
+ if (scomf.font[0] == 0)
+ parsefont(&scomf, "I");
+ if (blkcomf.font[0] == 0)
+ blkcomf = scomf, blkcomf.size += 2;
+ if (boxcomf.font[0] == 0)
+ boxcomf = blkcomf;
+ if (stringf.font[0] == 0)
+ parsefont(&stringf, "L");
+ if (keywordf.font[0] == 0)
+ parsefont(&keywordf, "B");
+ writefdef(&bodyf, 'B');
+ writefdef(&scomf, 'C');
+ writefdef(&blkcomf, 'L');
+ writefdef(&boxcomf, 'X');
+ writefdef(&stringf, 'S');
+ writefdef(&keywordf, 'K');
+ }
+ if (block_comment_max_col <= 0)
+ block_comment_max_col = max_col;
+ if (ps.decl_com_ind <= 0) /* if not specified by user, set this */
+ ps.decl_com_ind = ps.ljust_decl ? (ps.com_ind <= 10 ? 2 : ps.com_ind - 8) : ps.com_ind;
+ if (continuation_indent == 0)
+ continuation_indent = ps.ind_size;
+ fill_buffer(); /* get first batch of stuff into input buffer */
+
+ parse(semicolon);
+ {
+ register char *p = buf_ptr;
+ register col = 1;
+
+ while (1) {
+ if (*p == ' ')
+ col++;
+ else if (*p == '\t')
+ col = ((col - 1) & ~7) + 9;
+ else
+ break;
+ p++;
+ }
+ if (col > ps.ind_size)
+ ps.ind_level = ps.i_l_follow = col / ps.ind_size;
+ }
+ if (troff) {
+ register char *p = in_name,
+ *beg = in_name;
+
+ while (*p)
+ if (*p++ == '/')
+ beg = p;
+ fprintf(output, ".Fn \"%s\"\n", beg);
+ }
+ /*
+ * START OF MAIN LOOP
+ */
+
+ while (1) { /* this is the main loop. it will go until we
+ * reach eof */
+ int is_procname;
+
+ type_code = lexi(); /* lexi reads one token. The actual
+ * characters read are stored in "token". lexi
+ * returns a code indicating the type of token */
+ is_procname = ps.procname[0];
+
+ /*
+ * The following code moves everything following an if (), while (),
+ * else, etc. up to the start of the following stmt to a buffer. This
+ * allows proper handling of both kinds of brace placement.
+ */
+
+ flushed_nl = false;
+ while (ps.search_brace) { /* if we scanned an if(), while(),
+ * etc., we might need to copy stuff
+ * into a buffer we must loop, copying
+ * stuff into save_com, until we find
+ * the start of the stmt which follows
+ * the if, or whatever */
+ switch (type_code) {
+ case newline:
+ ++line_no;
+ flushed_nl = true;
+ case form_feed:
+ break; /* form feeds and newlines found here will be
+ * ignored */
+
+ case lbrace: /* this is a brace that starts the compound
+ * stmt */
+ if (sc_end == 0) { /* ignore buffering if a comment wasnt
+ * stored up */
+ ps.search_brace = false;
+ goto check_type;
+ }
+ if (btype_2) {
+ save_com[0] = '{'; /* we either want to put the brace
+ * right after the if */
+ goto sw_buffer; /* go to common code to get out of
+ * this loop */
+ }
+ case comment: /* we have a comment, so we must copy it into
+ * the buffer */
+ if (!flushed_nl || sc_end != 0) {
+ if (sc_end == 0) { /* if this is the first comment, we
+ * must set up the buffer */
+ save_com[0] = save_com[1] = ' ';
+ sc_end = &(save_com[2]);
+ }
+ else {
+ *sc_end++ = '\n'; /* add newline between
+ * comments */
+ *sc_end++ = ' ';
+ --line_no;
+ }
+ *sc_end++ = '/'; /* copy in start of comment */
+ *sc_end++ = '*';
+
+ for (;;) { /* loop until we get to the end of the comment */
+ *sc_end = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+
+ if (*sc_end++ == '*' && *buf_ptr == '/')
+ break; /* we are at end of comment */
+
+ if (sc_end >= &(save_com[sc_size])) { /* check for temp buffer
+ * overflow */
+ diag(1, "Internal buffer overflow - Move big comment from right after if, while, or whatever.");
+ fflush(output);
+ exit(1);
+ }
+ }
+ *sc_end++ = '/'; /* add ending slash */
+ if (++buf_ptr >= buf_end) /* get past / in buffer */
+ fill_buffer();
+ break;
+ }
+ default: /* it is the start of a normal statment */
+ if (flushed_nl) /* if we flushed a newline, make sure it is
+ * put back */
+ force_nl = true;
+ if (type_code == sp_paren && *token == 'i'
+ && last_else && ps.else_if
+ || type_code == sp_nparen && *token == 'e'
+ && e_code != s_code && e_code[-1] == '}')
+ force_nl = false;
+
+ if (sc_end == 0) { /* ignore buffering if comment wasnt
+ * saved up */
+ ps.search_brace = false;
+ goto check_type;
+ }
+ if (force_nl) { /* if we should insert a nl here, put it into
+ * the buffer */
+ force_nl = false;
+ --line_no; /* this will be re-increased when the nl is
+ * read from the buffer */
+ *sc_end++ = '\n';
+ *sc_end++ = ' ';
+ if (verbose && !flushed_nl) /* print error msg if the line
+ * was not already broken */
+ diag(0, "Line broken");
+ flushed_nl = false;
+ }
+ for (t_ptr = token; *t_ptr; ++t_ptr)
+ *sc_end++ = *t_ptr; /* copy token into temp buffer */
+ ps.procname[0] = 0;
+
+ sw_buffer:
+ ps.search_brace = false; /* stop looking for start of
+ * stmt */
+ bp_save = buf_ptr; /* save current input buffer */
+ be_save = buf_end;
+ buf_ptr = save_com; /* fix so that subsequent calls to
+ * lexi will take tokens out of
+ * save_com */
+ *sc_end++ = ' ';/* add trailing blank, just in case */
+ buf_end = sc_end;
+ sc_end = 0;
+ break;
+ } /* end of switch */
+ if (type_code != 0) /* we must make this check, just in case there
+ * was an unexpected EOF */
+ type_code = lexi(); /* read another token */
+ /* if (ps.search_brace) ps.procname[0] = 0; */
+ if ((is_procname = ps.procname[0]) && flushed_nl
+ && !procnames_start_line && ps.in_decl
+ && type_code == ident)
+ flushed_nl = 0;
+ } /* end of while (search_brace) */
+ last_else = 0;
+check_type:
+ if (type_code == 0) { /* we got eof */
+ if (s_lab != e_lab || s_code != e_code
+ || s_com != e_com) /* must dump end of line */
+ dump_line();
+ if (ps.tos > 1) /* check for balanced braces */
+ diag(1, "Stuff missing from end of file.");
+
+ if (verbose) {
+ printf("There were %d output lines and %d comments\n",
+ ps.out_lines, ps.out_coms);
+ printf("(Lines with comments)/(Lines with code): %6.3f\n",
+ (1.0 * ps.com_lines) / code_lines);
+ }
+ fflush(output);
+ exit(found_err);
+ }
+ if (
+ (type_code != comment) &&
+ (type_code != newline) &&
+ (type_code != preesc) &&
+ (type_code != form_feed)) {
+ if (force_nl &&
+ (type_code != semicolon) &&
+ (type_code != lbrace || !btype_2)) {
+ /* we should force a broken line here */
+ if (verbose && !flushed_nl)
+ diag(0, "Line broken");
+ flushed_nl = false;
+ dump_line();
+ ps.want_blank = false; /* dont insert blank at line start */
+ force_nl = false;
+ }
+ ps.in_stmt = true; /* turn on flag which causes an extra level of
+ * indentation. this is turned off by a ; or
+ * '}' */
+ if (s_com != e_com) { /* the turkey has embedded a comment
+ * in a line. fix it */
+ *e_code++ = ' ';
+ for (t_ptr = s_com; *t_ptr; ++t_ptr) {
+ CHECK_SIZE_CODE;
+ *e_code++ = *t_ptr;
+ }
+ *e_code++ = ' ';
+ *e_code = '\0'; /* null terminate code sect */
+ ps.want_blank = false;
+ e_com = s_com;
+ }
+ }
+ else if (type_code != comment) /* preserve force_nl thru a comment */
+ force_nl = false; /* cancel forced newline after newline, form
+ * feed, etc */
+
+
+
+ /*-----------------------------------------------------*\
+ | do switch on type of token scanned |
+ \*-----------------------------------------------------*/
+ CHECK_SIZE_CODE;
+ switch (type_code) { /* now, decide what to do with the token */
+
+ case form_feed: /* found a form feed in line */
+ ps.use_ff = true; /* a form feed is treated much like a newline */
+ dump_line();
+ ps.want_blank = false;
+ break;
+
+ case newline:
+ if (ps.last_token != comma || ps.p_l_follow > 0
+ || !ps.leave_comma || ps.block_init || !break_comma || s_com != e_com) {
+ dump_line();
+ ps.want_blank = false;
+ }
+ ++line_no; /* keep track of input line number */
+ break;
+
+ case lparen: /* got a '(' or '[' */
+ ++ps.p_l_follow; /* count parens to make Healy happy */
+ if (ps.want_blank && *token != '[' &&
+ (ps.last_token != ident || proc_calls_space
+ || (ps.its_a_keyword && (!ps.sizeof_keyword || Bill_Shannon))))
+ *e_code++ = ' ';
+ if (ps.in_decl && !ps.block_init)
+ if (troff && !ps.dumped_decl_indent && !is_procname && ps.last_token == decl) {
+ ps.dumped_decl_indent = 1;
+ sprintf(e_code, "\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
+ e_code += strlen(e_code);
+ }
+ else {
+ while ((e_code - s_code) < dec_ind) {
+ CHECK_SIZE_CODE;
+ *e_code++ = ' ';
+ }
+ *e_code++ = token[0];
+ }
+ else
+ *e_code++ = token[0];
+ ps.paren_indents[ps.p_l_follow - 1] = e_code - s_code;
+ if (sp_sw && ps.p_l_follow == 1 && extra_expression_indent
+ && ps.paren_indents[0] < 2 * ps.ind_size)
+ ps.paren_indents[0] = 2 * ps.ind_size;
+ ps.want_blank = false;
+ if (ps.in_or_st && *token == '(' && ps.tos <= 2) {
+ /*
+ * this is a kluge to make sure that declarations will be
+ * aligned right if proc decl has an explicit type on it, i.e.
+ * "int a(x) {..."
+ */
+ parse(semicolon); /* I said this was a kluge... */
+ ps.in_or_st = false; /* turn off flag for structure decl or
+ * initialization */
+ }
+ if (ps.sizeof_keyword)
+ ps.sizeof_mask |= 1 << ps.p_l_follow;
+ break;
+
+ case rparen: /* got a ')' or ']' */
+ rparen_count--;
+ if (ps.cast_mask & (1 << ps.p_l_follow) & ~ps.sizeof_mask) {
+ ps.last_u_d = true;
+ ps.cast_mask &= (1 << ps.p_l_follow) - 1;
+ }
+ ps.sizeof_mask &= (1 << ps.p_l_follow) - 1;
+ if (--ps.p_l_follow < 0) {
+ ps.p_l_follow = 0;
+ diag(0, "Extra %c", *token);
+ }
+ if (e_code == s_code) /* if the paren starts the line */
+ ps.paren_level = ps.p_l_follow; /* then indent it */
+
+ *e_code++ = token[0];
+ ps.want_blank = true;
+
+ if (sp_sw && (ps.p_l_follow == 0)) { /* check for end of if
+ * (...), or some such */
+ sp_sw = false;
+ force_nl = true;/* must force newline after if */
+ ps.last_u_d = true; /* inform lexi that a following
+ * operator is unary */
+ ps.in_stmt = false; /* dont use stmt continuation
+ * indentation */
+
+ parse(hd_type); /* let parser worry about if, or whatever */
+ }
+ ps.search_brace = btype_2; /* this should insure that constructs
+ * such as main(){...} and int[]{...}
+ * have their braces put in the right
+ * place */
+ break;
+
+ case unary_op: /* this could be any unary operation */
+ if (ps.want_blank)
+ *e_code++ = ' ';
+
+ if (troff && !ps.dumped_decl_indent && ps.in_decl && !is_procname) {
+ sprintf(e_code, "\n.Du %dp+\200p \"%s\"\n", dec_ind * 7, token);
+ ps.dumped_decl_indent = 1;
+ e_code += strlen(e_code);
+ }
+ else {
+ char *res = token;
+
+ if (ps.in_decl && !ps.block_init) { /* if this is a unary op
+ * in a declaration, we
+ * should indent this
+ * token */
+ for (i = 0; token[i]; ++i); /* find length of token */
+ while ((e_code - s_code) < (dec_ind - i)) {
+ CHECK_SIZE_CODE;
+ *e_code++ = ' '; /* pad it */
+ }
+ }
+ if (troff && token[0] == '-' && token[1] == '>')
+ res = "\\(->";
+ for (t_ptr = res; *t_ptr; ++t_ptr) {
+ CHECK_SIZE_CODE;
+ *e_code++ = *t_ptr;
+ }
+ }
+ ps.want_blank = false;
+ break;
+
+ case binary_op: /* any binary operation */
+ if (ps.want_blank)
+ *e_code++ = ' ';
+ {
+ char *res = token;
+
+ if (troff)
+ switch (token[0]) {
+ case '<':
+ if (token[1] == '=')
+ res = "\\(<=";
+ break;
+ case '>':
+ if (token[1] == '=')
+ res = "\\(>=";
+ break;
+ case '!':
+ if (token[1] == '=')
+ res = "\\(!=";
+ break;
+ case '|':
+ if (token[1] == '|')
+ res = "\\(br\\(br";
+ else if (token[1] == 0)
+ res = "\\(br";
+ break;
+ }
+ for (t_ptr = res; *t_ptr; ++t_ptr) {
+ CHECK_SIZE_CODE;
+ *e_code++ = *t_ptr; /* move the operator */
+ }
+ }
+ ps.want_blank = true;
+ break;
+
+ case postop: /* got a trailing ++ or -- */
+ *e_code++ = token[0];
+ *e_code++ = token[1];
+ ps.want_blank = true;
+ break;
+
+ case question: /* got a ? */
+ squest++; /* this will be used when a later colon
+ * appears so we can distinguish the
+ * <c>?<n>:<n> construct */
+ if (ps.want_blank)
+ *e_code++ = ' ';
+ *e_code++ = '?';
+ ps.want_blank = true;
+ break;
+
+ case casestmt: /* got word 'case' or 'default' */
+ scase = true; /* so we can process the later colon properly */
+ goto copy_id;
+
+ case colon: /* got a ':' */
+ if (squest > 0) { /* it is part of the <c>?<n>: <n> construct */
+ --squest;
+ if (ps.want_blank)
+ *e_code++ = ' ';
+ *e_code++ = ':';
+ ps.want_blank = true;
+ break;
+ }
+ if (ps.in_decl) {
+ *e_code++ = ':';
+ ps.want_blank = false;
+ break;
+ }
+ ps.in_stmt = false; /* seeing a label does not imply we are in a
+ * stmt */
+ for (t_ptr = s_code; *t_ptr; ++t_ptr)
+ *e_lab++ = *t_ptr; /* turn everything so far into a label */
+ e_code = s_code;
+ *e_lab++ = ':';
+ *e_lab++ = ' ';
+ *e_lab = '\0';
+
+ force_nl = ps.pcase = scase; /* ps.pcase will be used by
+ * dump_line to decide how to
+ * indent the label. force_nl
+ * will force a case n: to be
+ * on a line by itself */
+ scase = false;
+ ps.want_blank = false;
+ break;
+
+ case semicolon: /* got a ';' */
+ ps.in_or_st = false;/* we are not in an initialization or
+ * structure declaration */
+ scase = false; /* these will only need resetting in a error */
+ squest = 0;
+ if (ps.last_token == rparen && rparen_count == 0)
+ ps.in_parameter_declaration = 0;
+ ps.cast_mask = 0;
+ ps.sizeof_mask = 0;
+ ps.block_init = 0;
+ ps.block_init_level = 0;
+ ps.just_saw_decl--;
+
+ if (ps.in_decl && s_code == e_code && !ps.block_init)
+ while ((e_code - s_code) < (dec_ind - 1)) {
+ CHECK_SIZE_CODE;
+ *e_code++ = ' ';
+ }
+
+ ps.in_decl = (ps.dec_nest > 0); /* if we were in a first level
+ * structure declaration, we
+ * arent any more */
+
+ if ((!sp_sw || hd_type != forstmt) && ps.p_l_follow > 0) {
+
+ /*
+ * This should be true iff there were unbalanced parens in the
+ * stmt. It is a bit complicated, because the semicolon might
+ * be in a for stmt
+ */
+ diag(1, "Unbalanced parens");
+ ps.p_l_follow = 0;
+ if (sp_sw) { /* this is a check for a if, while, etc. with
+ * unbalanced parens */
+ sp_sw = false;
+ parse(hd_type); /* dont lose the if, or whatever */
+ }
+ }
+ *e_code++ = ';';
+ ps.want_blank = true;
+ ps.in_stmt = (ps.p_l_follow > 0); /* we are no longer in the
+ * middle of a stmt */
+
+ if (!sp_sw) { /* if not if for (;;) */
+ parse(semicolon); /* let parser know about end of stmt */
+ force_nl = true;/* force newline after a end of stmt */
+ }
+ break;
+
+ case lbrace: /* got a '{' */
+ ps.in_stmt = false; /* dont indent the {} */
+ if (!ps.block_init)
+ force_nl = true;/* force other stuff on same line as '{' onto
+ * new line */
+ else if (ps.block_init_level <= 0)
+ ps.block_init_level = 1;
+ else
+ ps.block_init_level++;
+
+ if (s_code != e_code && !ps.block_init) {
+ if (!btype_2) {
+ dump_line();
+ ps.want_blank = false;
+ }
+ else if (ps.in_parameter_declaration && !ps.in_or_st) {
+ ps.i_l_follow = 0;
+ dump_line();
+ ps.want_blank = false;
+ }
+ }
+ if (ps.in_parameter_declaration)
+ prefix_blankline_requested = 0;
+
+ if (ps.p_l_follow > 0) { /* check for preceeding unbalanced
+ * parens */
+ diag(1, "Unbalanced parens");
+ ps.p_l_follow = 0;
+ if (sp_sw) { /* check for unclosed if, for, etc. */
+ sp_sw = false;
+ parse(hd_type);
+ ps.ind_level = ps.i_l_follow;
+ }
+ }
+ if (s_code == e_code)
+ ps.ind_stmt = false; /* dont put extra indentation on line
+ * with '{' */
+ if (ps.in_decl && ps.in_or_st) { /* this is either a structure
+ * declaration or an init */
+ di_stack[ps.dec_nest++] = dec_ind;
+ /* ? dec_ind = 0; */
+ }
+ else {
+ ps.decl_on_line = false; /* we cant be in the middle of
+ * a declaration, so dont do
+ * special indentation of
+ * comments */
+ if (blanklines_after_declarations_at_proctop
+ && ps.in_parameter_declaration)
+ postfix_blankline_requested = 1;
+ ps.in_parameter_declaration = 0;
+ }
+ dec_ind = 0;
+ parse(lbrace); /* let parser know about this */
+ if (ps.want_blank) /* put a blank before '{' if '{' is not at
+ * start of line */
+ *e_code++ = ' ';
+ ps.want_blank = false;
+ *e_code++ = '{';
+ ps.just_saw_decl = 0;
+ break;
+
+ case rbrace: /* got a '}' */
+ if (ps.p_stack[ps.tos] == decl && !ps.block_init) /* semicolons can be
+ * omitted in
+ * declarations */
+ parse(semicolon);
+ if (ps.p_l_follow) {/* check for unclosed if, for, else. */
+ diag(1, "Unbalanced parens");
+ ps.p_l_follow = 0;
+ sp_sw = false;
+ }
+ ps.just_saw_decl = 0;
+ ps.block_init_level--;
+ if (s_code != e_code && !ps.block_init) { /* '}' must be first on
+ * line */
+ if (verbose)
+ diag(0, "Line broken");
+ dump_line();
+ }
+ *e_code++ = '}';
+ ps.want_blank = true;
+ ps.in_stmt = ps.ind_stmt = false;
+ if (ps.dec_nest > 0) { /* we are in multi-level structure
+ * declaration */
+ dec_ind = di_stack[--ps.dec_nest];
+ if (ps.dec_nest == 0 && !ps.in_parameter_declaration)
+ ps.just_saw_decl = 2;
+ ps.in_decl = true;
+ }
+ prefix_blankline_requested = 0;
+ parse(rbrace); /* let parser know about this */
+ ps.search_brace = cuddle_else && ps.p_stack[ps.tos] == ifhead
+ && ps.il[ps.tos] >= ps.ind_level;
+ if (ps.tos <= 1 && blanklines_after_procs && ps.dec_nest <= 0)
+ postfix_blankline_requested = 1;
+ break;
+
+ case swstmt: /* got keyword "switch" */
+ sp_sw = true;
+ hd_type = swstmt; /* keep this for when we have seen the
+ * expression */
+ goto copy_id; /* go move the token into buffer */
+
+ case sp_paren: /* token is if, while, for */
+ sp_sw = true; /* the interesting stuff is done after the
+ * expression is scanned */
+ hd_type = (*token == 'i' ? ifstmt :
+ (*token == 'w' ? whilestmt : forstmt));
+
+ /*
+ * remember the type of header for later use by parser
+ */
+ goto copy_id; /* copy the token into line */
+
+ case sp_nparen: /* got else, do */
+ ps.in_stmt = false;
+ if (*token == 'e') {
+ if (e_code != s_code && (!cuddle_else || e_code[-1] != '}')) {
+ if (verbose)
+ diag(0, "Line broken");
+ dump_line();/* make sure this starts a line */
+ ps.want_blank = false;
+ }
+ force_nl = true;/* also, following stuff must go onto new line */
+ last_else = 1;
+ parse(elselit);
+ }
+ else {
+ if (e_code != s_code) { /* make sure this starts a line */
+ if (verbose)
+ diag(0, "Line broken");
+ dump_line();
+ ps.want_blank = false;
+ }
+ force_nl = true;/* also, following stuff must go onto new line */
+ last_else = 0;
+ parse(dolit);
+ }
+ goto copy_id; /* move the token into line */
+
+ case decl: /* we have a declaration type (int, register,
+ * etc.) */
+ parse(decl); /* let parser worry about indentation */
+ if (ps.last_token == rparen && ps.tos <= 1) {
+ ps.in_parameter_declaration = 1;
+ if (s_code != e_code) {
+ dump_line();
+ ps.want_blank = 0;
+ }
+ }
+ if (ps.in_parameter_declaration && ps.indent_parameters && ps.dec_nest == 0) {
+ ps.ind_level = ps.i_l_follow = 1;
+ ps.ind_stmt = 0;
+ }
+ ps.in_or_st = true; /* this might be a structure or initialization
+ * declaration */
+ ps.in_decl = ps.decl_on_line = true;
+ if ( /* !ps.in_or_st && */ ps.dec_nest <= 0)
+ ps.just_saw_decl = 2;
+ prefix_blankline_requested = 0;
+ for (i = 0; token[i++];); /* get length of token */
+
+ /*
+ * dec_ind = e_code - s_code + (ps.decl_indent>i ? ps.decl_indent
+ * : i);
+ */
+ dec_ind = ps.decl_indent > 0 ? ps.decl_indent : i;
+ goto copy_id;
+
+ case ident: /* got an identifier or constant */
+ if (ps.in_decl) { /* if we are in a declaration, we must indent
+ * identifier */
+ if (ps.want_blank)
+ *e_code++ = ' ';
+ ps.want_blank = false;
+ if (is_procname == 0 || !procnames_start_line) {
+ if (!ps.block_init)
+ if (troff && !ps.dumped_decl_indent) {
+ sprintf(e_code, "\n.De %dp+\200p\n", dec_ind * 7);
+ ps.dumped_decl_indent = 1;
+ e_code += strlen(e_code);
+ }
+ else
+ while ((e_code - s_code) < dec_ind) {
+ CHECK_SIZE_CODE;
+ *e_code++ = ' ';
+ }
+ }
+ else {
+ if (dec_ind && s_code != e_code)
+ dump_line();
+ dec_ind = 0;
+ ps.want_blank = false;
+ }
+ }
+ else if (sp_sw && ps.p_l_follow == 0) {
+ sp_sw = false;
+ force_nl = true;
+ ps.last_u_d = true;
+ ps.in_stmt = false;
+ parse(hd_type);
+ }
+ copy_id:
+ if (ps.want_blank)
+ *e_code++ = ' ';
+ if (troff && ps.its_a_keyword) {
+ e_code = chfont(&bodyf, &keywordf, e_code);
+ for (t_ptr = token; *t_ptr; ++t_ptr) {
+ CHECK_SIZE_CODE;
+ *e_code++ = keywordf.allcaps && islower(*t_ptr)
+ ? toupper(*t_ptr) : *t_ptr;
+ }
+ e_code = chfont(&keywordf, &bodyf, e_code);
+ }
+ else
+ for (t_ptr = token; *t_ptr; ++t_ptr) {
+ CHECK_SIZE_CODE;
+ *e_code++ = *t_ptr;
+ }
+ ps.want_blank = true;
+ break;
+
+ case period: /* treat a period kind of like a binary
+ * operation */
+ *e_code++ = '.'; /* move the period into line */
+ ps.want_blank = false; /* dont put a blank after a period */
+ break;
+
+ case comma:
+ ps.want_blank = (s_code != e_code); /* only put blank after comma
+ * if comma does not start the
+ * line */
+ if (ps.in_decl && is_procname == 0 && !ps.block_init)
+ while ((e_code - s_code) < (dec_ind - 1)) {
+ CHECK_SIZE_CODE;
+ *e_code++ = ' ';
+ }
+
+ *e_code++ = ',';
+ if (ps.p_l_follow == 0) {
+ if (ps.block_init_level <= 0)
+ ps.block_init = 0;
+ if (break_comma && (!ps.leave_comma || compute_code_target() + (e_code - s_code) > max_col - 8))
+ force_nl = true;
+ }
+ break;
+
+ case preesc: /* got the character '#' */
+ if ((s_com != e_com) ||
+ (s_lab != e_lab) ||
+ (s_code != e_code))
+ dump_line();
+ *e_lab++ = '#'; /* move whole line to 'label' buffer */
+ {
+ int in_comment = 0;
+ int com_start = 0;
+ char quote = 0;
+ int com_end = 0;
+
+ while (*buf_ptr == ' ' || *buf_ptr == '\t') {
+ buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ while (*buf_ptr != '\n' || in_comment) {
+ CHECK_SIZE_LAB;
+ *e_lab = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ switch (*e_lab++) {
+ case BACKSLASH:
+ if (troff)
+ *e_lab++ = BACKSLASH;
+ if (!in_comment) {
+ *e_lab++ = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ break;
+ case '/':
+ if (*buf_ptr == '*' && !in_comment && !quote) {
+ in_comment = 1;
+ *e_lab++ = *buf_ptr++;
+ com_start = e_lab - s_lab - 2;
+ }
+ break;
+ case '"':
+ if (quote == '"')
+ quote = 0;
+ break;
+ case '\'':
+ if (quote == '\'')
+ quote = 0;
+ break;
+ case '*':
+ if (*buf_ptr == '/' && in_comment) {
+ in_comment = 0;
+ *e_lab++ = *buf_ptr++;
+ com_end = e_lab - s_lab;
+ }
+ break;
+ }
+ }
+
+ while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
+ e_lab--;
+ if (e_lab - s_lab == com_end && bp_save == 0) { /* comment on
+ * preprocessor line */
+ if (sc_end == 0) /* if this is the first comment, we
+ * must set up the buffer */
+ sc_end = &(save_com[0]);
+ else {
+ *sc_end++ = '\n'; /* add newline between
+ * comments */
+ *sc_end++ = ' ';
+ --line_no;
+ }
+ bcopy(s_lab + com_start, sc_end, com_end - com_start);
+ sc_end += com_end - com_start;
+ if (sc_end >= &save_com[sc_size])
+ abort();
+ e_lab = s_lab + com_start;
+ while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
+ e_lab--;
+ bp_save = buf_ptr; /* save current input buffer */
+ be_save = buf_end;
+ buf_ptr = save_com; /* fix so that subsequent calls to
+ * lexi will take tokens out of
+ * save_com */
+ *sc_end++ = ' '; /* add trailing blank, just in case */
+ buf_end = sc_end;
+ sc_end = 0;
+ }
+ *e_lab = '\0'; /* null terminate line */
+ ps.pcase = false;
+ }
+
+ if (strncmp(s_lab, "#if", 3) == 0) {
+ if (blanklines_around_conditional_compilation) {
+ register c;
+ prefix_blankline_requested++;
+ while ((c = getc(input)) == '\n');
+ ungetc(c, input);
+ }
+ if (ifdef_level < sizeof state_stack / sizeof state_stack[0]) {
+ match_state[ifdef_level].tos = -1;
+ state_stack[ifdef_level++] = ps;
+ }
+ else
+ diag(1, "#if stack overflow");
+ }
+ else if (strncmp(s_lab, "#else", 5) == 0)
+ if (ifdef_level <= 0)
+ diag(1, "Unmatched #else");
+ else {
+ match_state[ifdef_level - 1] = ps;
+ ps = state_stack[ifdef_level - 1];
+ }
+ else if (strncmp(s_lab, "#endif", 6) == 0) {
+ if (ifdef_level <= 0)
+ diag(1, "Unmatched #endif");
+ else {
+ ifdef_level--;
+
+#ifdef undef
+ /*
+ * This match needs to be more intelligent before the
+ * message is useful
+ */
+ if (match_state[ifdef_level].tos >= 0
+ && bcmp(&ps, &match_state[ifdef_level], sizeof ps))
+ diag(0, "Syntactically inconsistant #ifdef alternatives.");
+#endif
+ }
+ if (blanklines_around_conditional_compilation) {
+ postfix_blankline_requested++;
+ n_real_blanklines = 0;
+ }
+ }
+ break; /* subsequent processing of the newline
+ * character will cause the line to be printed */
+
+ case comment: /* we have gotten a /* this is a biggie */
+ if (flushed_nl) { /* we should force a broken line here */
+ flushed_nl = false;
+ dump_line();
+ ps.want_blank = false; /* dont insert blank at line start */
+ force_nl = false;
+ }
+ pr_comment();
+ break;
+ } /* end of big switch stmt */
+
+ *e_code = '\0'; /* make sure code section is null terminated */
+ if (type_code != comment && type_code != newline && type_code != preesc)
+ ps.last_token = type_code;
+ } /* end of main while (1) loop */
+}
+
+/*
+ * copy input file to backup file if in_name is /blah/blah/blah/file, then
+ * backup file will be ".Bfile" then make the backup file the input and
+ * original input file the output
+ */
+bakcopy()
+{
+ int n,
+ bakchn;
+ char buff[8 * 1024];
+ register char *p;
+
+ /* construct file name .Bfile */
+ for (p = in_name; *p; p++); /* skip to end of string */
+ while (p > in_name && *p != '/') /* find last '/' */
+ p--;
+ if (*p == '/')
+ p++;
+ sprintf(bakfile, "%s.BAK", p);
+
+ /* copy in_name to backup file */
+ bakchn = creat(bakfile, 0600);
+ if (bakchn < 0)
+ err(bakfile);
+ while (n = read(fileno(input), buff, sizeof buff))
+ if (write(bakchn, buff, n) != n)
+ err(bakfile);
+ if (n < 0)
+ err(in_name);
+ close(bakchn);
+ fclose(input);
+
+ /* re-open backup file as the input file */
+ input = fopen(bakfile, "r");
+ if (input == 0)
+ err(bakfile);
+ /* now the original input file will be the output */
+ output = fopen(in_name, "w");
+ if (output == 0) {
+ unlink(bakfile);
+ err(in_name);
+ }
+}
+
+err(msg)
+ char *msg;
+{
+ extern int errno;
+ char *strerror();
+
+ (void)fprintf(stderr, "indent: %s: %s\n", msg, strerror(errno));
+ exit(1);
+}
diff --git a/usr.bin/indent/indent_codes.h b/usr.bin/indent/indent_codes.h
new file mode 100644
index 0000000..8373d4e
--- /dev/null
+++ b/usr.bin/indent/indent_codes.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)indent_codes.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define newline 1
+#define lparen 2
+#define rparen 3
+#define unary_op 4
+#define binary_op 5
+#define postop 6
+#define question 7
+#define casestmt 8
+#define colon 9
+#define semicolon 10
+#define lbrace 11
+#define rbrace 12
+#define ident 13
+#define comma 14
+#define comment 15
+#define swstmt 16
+#define preesc 17
+#define form_feed 18
+#define decl 19
+#define sp_paren 20
+#define sp_nparen 21
+#define ifstmt 22
+#define whilestmt 23
+#define forstmt 24
+#define stmt 25
+#define stmtl 26
+#define elselit 27
+#define dolit 28
+#define dohead 29
+#define ifhead 30
+#define elsehead 31
+#define period 32
diff --git a/usr.bin/indent/indent_globs.h b/usr.bin/indent/indent_globs.h
new file mode 100644
index 0000000..8f911ec
--- /dev/null
+++ b/usr.bin/indent/indent_globs.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)indent_globs.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define BACKSLASH '\\'
+#define bufsize 200 /* size of internal buffers */
+#define sc_size 5000 /* size of save_com buffer */
+#define label_offset 2 /* number of levels a label is placed to left
+ * of code */
+
+#define tabsize 8 /* the size of a tab */
+#define tabmask 0177770 /* mask used when figuring length of lines
+ * with tabs */
+
+
+#define false 0
+#define true 1
+
+
+FILE *input; /* the fid for the input file */
+FILE *output; /* the output file */
+
+#define CHECK_SIZE_CODE \
+ if (e_code >= l_code) { \
+ register nsize = l_code-s_code+400; \
+ codebuf = (char *) realloc(codebuf, nsize); \
+ e_code = codebuf + (e_code-s_code) + 1; \
+ l_code = codebuf + nsize - 5; \
+ s_code = codebuf + 1; \
+ }
+#define CHECK_SIZE_COM \
+ if (e_com >= l_com) { \
+ register nsize = l_com-s_com+400; \
+ combuf = (char *) realloc(combuf, nsize); \
+ e_com = combuf + (e_com-s_com) + 1; \
+ l_com = combuf + nsize - 5; \
+ s_com = combuf + 1; \
+ }
+#define CHECK_SIZE_LAB \
+ if (e_lab >= l_lab) { \
+ register nsize = l_lab-s_lab+400; \
+ labbuf = (char *) realloc(labbuf, nsize); \
+ e_lab = labbuf + (e_lab-s_lab) + 1; \
+ l_lab = labbuf + nsize - 5; \
+ s_lab = labbuf + 1; \
+ }
+#define CHECK_SIZE_TOKEN \
+ if (e_token >= l_token) { \
+ register nsize = l_token-s_token+400; \
+ tokenbuf = (char *) realloc(tokenbuf, nsize); \
+ e_token = tokenbuf + (e_token-s_token) + 1; \
+ l_token = tokenbuf + nsize - 5; \
+ s_token = tokenbuf + 1; \
+ }
+
+char *labbuf; /* buffer for label */
+char *s_lab; /* start ... */
+char *e_lab; /* .. and end of stored label */
+char *l_lab; /* limit of label buffer */
+
+char *codebuf; /* buffer for code section */
+char *s_code; /* start ... */
+char *e_code; /* .. and end of stored code */
+char *l_code; /* limit of code section */
+
+char *combuf; /* buffer for comments */
+char *s_com; /* start ... */
+char *e_com; /* ... and end of stored comments */
+char *l_com; /* limit of comment buffer */
+
+#define token s_token
+char *tokenbuf; /* the last token scanned */
+char *s_token;
+char *e_token;
+char *l_token;
+
+char *in_buffer; /* input buffer */
+char *in_buffer_limit; /* the end of the input buffer */
+char *buf_ptr; /* ptr to next character to be taken from
+ * in_buffer */
+char *buf_end; /* ptr to first after last char in in_buffer */
+
+char save_com[sc_size]; /* input text is saved here when looking for
+ * the brace after an if, while, etc */
+char *sc_end; /* pointer into save_com buffer */
+
+char *bp_save; /* saved value of buf_ptr when taking input
+ * from save_com */
+char *be_save; /* similarly saved value of buf_end */
+
+
+int pointer_as_binop;
+int blanklines_after_declarations;
+int blanklines_before_blockcomments;
+int blanklines_after_procs;
+int blanklines_around_conditional_compilation;
+int swallow_optional_blanklines;
+int n_real_blanklines;
+int prefix_blankline_requested;
+int postfix_blankline_requested;
+int break_comma; /* when true and not in parens, break after a
+ * comma */
+int btype_2; /* when true, brace should be on same line as
+ * if, while, etc */
+float case_ind; /* indentation level to be used for a "case
+ * n:" */
+int code_lines; /* count of lines with code */
+int had_eof; /* set to true when input is exhausted */
+int line_no; /* the current line number. */
+int max_col; /* the maximum allowable line length */
+int verbose; /* when true, non-essential error messages are
+ * printed */
+int cuddle_else; /* true if else should cuddle up to '}' */
+int star_comment_cont; /* true iff comment continuation lines should
+ * have stars at the beginning of each line. */
+int comment_delimiter_on_blankline;
+int troff; /* true iff were generating troff input */
+int procnames_start_line; /* if true, the names of procedures
+ * being defined get placed in column
+ * 1 (ie. a newline is placed between
+ * the type of the procedure and its
+ * name) */
+int proc_calls_space; /* If true, procedure calls look like:
+ * foo(bar) rather than foo (bar) */
+int format_col1_comments; /* If comments which start in column 1
+ * are to be magically reformatted
+ * (just like comments that begin in
+ * later columns) */
+int inhibit_formatting; /* true if INDENT OFF is in effect */
+int suppress_blanklines;/* set iff following blanklines should be
+ * suppressed */
+int continuation_indent;/* set to the indentation between the edge of
+ * code and continuation lines */
+int lineup_to_parens; /* if true, continued code within parens will
+ * be lined up to the open paren */
+int Bill_Shannon; /* true iff a blank should always be inserted
+ * after sizeof */
+int blanklines_after_declarations_at_proctop; /* This is vaguely
+ * similar to
+ * blanklines_after_decla
+ * rations except that
+ * it only applies to
+ * the first set of
+ * declarations in a
+ * procedure (just after
+ * the first '{') and it
+ * causes a blank line
+ * to be generated even
+ * if there are no
+ * declarations */
+int block_comment_max_col;
+int extra_expression_indent; /* True if continuation lines from the
+ * expression part of "if(e)",
+ * "while(e)", "for(e;e;e)" should be
+ * indented an extra tab stop so that
+ * they don't conflict with the code
+ * that follows */
+
+/* -troff font state information */
+
+struct fstate {
+ char font[4];
+ char size;
+ int allcaps:1;
+};
+char *chfont();
+
+struct fstate
+ keywordf, /* keyword font */
+ stringf, /* string font */
+ boxcomf, /* Box comment font */
+ blkcomf, /* Block comment font */
+ scomf, /* Same line comment font */
+ bodyf; /* major body font */
+
+
+#define STACKSIZE 150
+
+struct parser_state {
+ int last_token;
+ struct fstate cfont; /* Current font */
+ int p_stack[STACKSIZE]; /* this is the parsers stack */
+ int il[STACKSIZE]; /* this stack stores indentation levels */
+ float cstk[STACKSIZE];/* used to store case stmt indentation levels */
+ int box_com; /* set to true when we are in a "boxed"
+ * comment. In that case, the first non-blank
+ * char should be lined up with the / in /* */
+ int comment_delta,
+ n_comment_delta;
+ int cast_mask; /* indicates which close parens close off
+ * casts */
+ int sizeof_mask; /* indicates which close parens close off
+ * sizeof''s */
+ int block_init; /* true iff inside a block initialization */
+ int block_init_level; /* The level of brace nesting in an
+ * initialization */
+ int last_nl; /* this is true if the last thing scanned was
+ * a newline */
+ int in_or_st; /* Will be true iff there has been a
+ * declarator (e.g. int or char) and no left
+ * paren since the last semicolon. When true,
+ * a '{' is starting a structure definition or
+ * an initialization list */
+ int bl_line; /* set to 1 by dump_line if the line is blank */
+ int col_1; /* set to true if the last token started in
+ * column 1 */
+ int com_col; /* this is the column in which the current
+ * coment should start */
+ int com_ind; /* the column in which comments to the right
+ * of code should start */
+ int com_lines; /* the number of lines with comments, set by
+ * dump_line */
+ int dec_nest; /* current nesting level for structure or init */
+ int decl_com_ind; /* the column in which comments after
+ * declarations should be put */
+ int decl_on_line; /* set to true if this line of code has part
+ * of a declaration on it */
+ int i_l_follow; /* the level to which ind_level should be set
+ * after the current line is printed */
+ int in_decl; /* set to true when we are in a declaration
+ * stmt. The processing of braces is then
+ * slightly different */
+ int in_stmt; /* set to 1 while in a stmt */
+ int ind_level; /* the current indentation level */
+ int ind_size; /* the size of one indentation level */
+ int ind_stmt; /* set to 1 if next line should have an extra
+ * indentation level because we are in the
+ * middle of a stmt */
+ int last_u_d; /* set to true after scanning a token which
+ * forces a following operator to be unary */
+ int leave_comma; /* if true, never break declarations after
+ * commas */
+ int ljust_decl; /* true if declarations should be left
+ * justified */
+ int out_coms; /* the number of comments processed, set by
+ * pr_comment */
+ int out_lines; /* the number of lines written, set by
+ * dump_line */
+ int p_l_follow; /* used to remember how to indent following
+ * statement */
+ int paren_level; /* parenthesization level. used to indent
+ * within stmts */
+ short paren_indents[20]; /* column positions of each paren */
+ int pcase; /* set to 1 if the current line label is a
+ * case. It is printed differently from a
+ * regular label */
+ int search_brace; /* set to true by parse when it is necessary
+ * to buffer up all info up to the start of a
+ * stmt after an if, while, etc */
+ int unindent_displace; /* comments not to the right of code
+ * will be placed this many
+ * indentation levels to the left of
+ * code */
+ int use_ff; /* set to one if the current line should be
+ * terminated with a form feed */
+ int want_blank; /* set to true when the following token should
+ * be prefixed by a blank. (Said prefixing is
+ * ignored in some cases.) */
+ int else_if; /* True iff else if pairs should be handled
+ * specially */
+ int decl_indent; /* column to indent declared identifiers to */
+ int its_a_keyword;
+ int sizeof_keyword;
+ int dumped_decl_indent;
+ float case_indent; /* The distance to indent case labels from the
+ * switch statement */
+ int in_parameter_declaration;
+ int indent_parameters;
+ int tos; /* pointer to top of stack */
+ char procname[100]; /* The name of the current procedure */
+ int just_saw_decl;
+} ps;
+
+int ifdef_level;
+int rparen_count;
+struct parser_state state_stack[5];
+struct parser_state match_state[5];
diff --git a/usr.bin/indent/io.c b/usr.bin/indent/io.c
new file mode 100644
index 0000000..ae98242
--- /dev/null
+++ b/usr.bin/indent/io.c
@@ -0,0 +1,625 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "indent_globs.h"
+
+
+int comment_open;
+static paren_target;
+
+dump_line()
+{ /* dump_line is the routine that actually
+ * effects the printing of the new source. It
+ * prints the label section, followed by the
+ * code section with the appropriate nesting
+ * level, followed by any comments */
+ register int cur_col,
+ target_col;
+ static not_first_line;
+
+ if (ps.procname[0]) {
+ if (troff) {
+ if (comment_open) {
+ comment_open = 0;
+ fprintf(output, ".*/\n");
+ }
+ fprintf(output, ".Pr \"%s\"\n", ps.procname);
+ }
+ ps.ind_level = 0;
+ ps.procname[0] = 0;
+ }
+ if (s_code == e_code && s_lab == e_lab && s_com == e_com) {
+ if (suppress_blanklines > 0)
+ suppress_blanklines--;
+ else {
+ ps.bl_line = true;
+ n_real_blanklines++;
+ }
+ }
+ else if (!inhibit_formatting) {
+ suppress_blanklines = 0;
+ ps.bl_line = false;
+ if (prefix_blankline_requested && not_first_line)
+ if (swallow_optional_blanklines) {
+ if (n_real_blanklines == 1)
+ n_real_blanklines = 0;
+ }
+ else {
+ if (n_real_blanklines == 0)
+ n_real_blanklines = 1;
+ }
+ while (--n_real_blanklines >= 0)
+ putc('\n', output);
+ n_real_blanklines = 0;
+ if (ps.ind_level == 0)
+ ps.ind_stmt = 0; /* this is a class A kludge. dont do
+ * additional statement indentation if we are
+ * at bracket level 0 */
+
+ if (e_lab != s_lab || e_code != s_code)
+ ++code_lines; /* keep count of lines with code */
+
+
+ if (e_lab != s_lab) { /* print lab, if any */
+ if (comment_open) {
+ comment_open = 0;
+ fprintf(output, ".*/\n");
+ }
+ while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
+ e_lab--;
+ cur_col = pad_output(1, compute_label_target());
+ if (s_lab[0] == '#' && (strncmp(s_lab, "#else", 5) == 0
+ || strncmp(s_lab, "#endif", 6) == 0)) {
+ register char *s = s_lab;
+ if (e_lab[-1] == '\n') e_lab--;
+ do putc(*s++, output);
+ while (s < e_lab && 'a' <= *s && *s<='z');
+ while ((*s == ' ' || *s == '\t') && s < e_lab)
+ s++;
+ if (s < e_lab)
+ fprintf(output, s[0]=='/' && s[1]=='*' ? "\t%.*s" : "\t/* %.*s */",
+ e_lab - s, s);
+ }
+ else fprintf(output, "%.*s", e_lab - s_lab, s_lab);
+ cur_col = count_spaces(cur_col, s_lab);
+ }
+ else
+ cur_col = 1; /* there is no label section */
+
+ ps.pcase = false;
+
+ if (s_code != e_code) { /* print code section, if any */
+ register char *p;
+
+ if (comment_open) {
+ comment_open = 0;
+ fprintf(output, ".*/\n");
+ }
+ target_col = compute_code_target();
+ {
+ register i;
+
+ for (i = 0; i < ps.p_l_follow; i++)
+ if (ps.paren_indents[i] >= 0)
+ ps.paren_indents[i] = -(ps.paren_indents[i] + target_col);
+ }
+ cur_col = pad_output(cur_col, target_col);
+ for (p = s_code; p < e_code; p++)
+ if (*p == (char) 0200)
+ fprintf(output, "%d", target_col * 7);
+ else
+ putc(*p, output);
+ cur_col = count_spaces(cur_col, s_code);
+ }
+ if (s_com != e_com)
+ if (troff) {
+ int all_here = 0;
+ register char *p;
+
+ if (e_com[-1] == '/' && e_com[-2] == '*')
+ e_com -= 2, all_here++;
+ while (e_com > s_com && e_com[-1] == ' ')
+ e_com--;
+ *e_com = 0;
+ p = s_com;
+ while (*p == ' ')
+ p++;
+ if (p[0] == '/' && p[1] == '*')
+ p += 2, all_here++;
+ else if (p[0] == '*')
+ p += p[1] == '/' ? 2 : 1;
+ while (*p == ' ')
+ p++;
+ if (*p == 0)
+ goto inhibit_newline;
+ if (comment_open < 2 && ps.box_com) {
+ comment_open = 0;
+ fprintf(output, ".*/\n");
+ }
+ if (comment_open == 0) {
+ if ('a' <= *p && *p <= 'z')
+ *p = *p + 'A' - 'a';
+ if (e_com - p < 50 && all_here == 2) {
+ register char *follow = p;
+ fprintf(output, "\n.nr C! \\w\1");
+ while (follow < e_com) {
+ switch (*follow) {
+ case '\n':
+ putc(' ', output);
+ case 1:
+ break;
+ case '\\':
+ putc('\\', output);
+ default:
+ putc(*follow, output);
+ }
+ follow++;
+ }
+ putc(1, output);
+ }
+ fprintf(output, "\n./* %dp %d %dp\n",
+ ps.com_col * 7,
+ (s_code != e_code || s_lab != e_lab) - ps.box_com,
+ target_col * 7);
+ }
+ comment_open = 1 + ps.box_com;
+ while (*p) {
+ if (*p == BACKSLASH)
+ putc(BACKSLASH, output);
+ putc(*p++, output);
+ }
+ }
+ else { /* print comment, if any */
+ register target = ps.com_col;
+ register char *com_st = s_com;
+
+ target += ps.comment_delta;
+ while (*com_st == '\t')
+ com_st++, target += 8; /* ? */
+ while (target <= 0)
+ if (*com_st == ' ')
+ target++, com_st++;
+ else if (*com_st == '\t')
+ target = ((target - 1) & ~7) + 9, com_st++;
+ else
+ target = 1;
+ if (cur_col > target) { /* if comment cant fit on this line,
+ * put it on next line */
+ putc('\n', output);
+ cur_col = 1;
+ ++ps.out_lines;
+ }
+ while (e_com > com_st && isspace(e_com[-1]))
+ e_com--;
+ cur_col = pad_output(cur_col, target);
+ if (!ps.box_com) {
+ if (star_comment_cont && (com_st[1] != '*' || e_com <= com_st + 1))
+ if (com_st[1] == ' ' && com_st[0] == ' ' && e_com > com_st + 1)
+ com_st[1] = '*';
+ else
+ fwrite(" * ", com_st[0] == '\t' ? 2 : com_st[0] == '*' ? 1 : 3, 1, output);
+ }
+ fwrite(com_st, e_com - com_st, 1, output);
+ ps.comment_delta = ps.n_comment_delta;
+ cur_col = count_spaces(cur_col, com_st);
+ ++ps.com_lines; /* count lines with comments */
+ }
+ if (ps.use_ff)
+ putc('\014', output);
+ else
+ putc('\n', output);
+inhibit_newline:
+ ++ps.out_lines;
+ if (ps.just_saw_decl == 1 && blanklines_after_declarations) {
+ prefix_blankline_requested = 1;
+ ps.just_saw_decl = 0;
+ }
+ else
+ prefix_blankline_requested = postfix_blankline_requested;
+ postfix_blankline_requested = 0;
+ }
+ ps.decl_on_line = ps.in_decl; /* if we are in the middle of a
+ * declaration, remember that fact for
+ * proper comment indentation */
+ ps.ind_stmt = ps.in_stmt & ~ps.in_decl; /* next line should be
+ * indented if we have not
+ * completed this stmt and if
+ * we are not in the middle of
+ * a declaration */
+ ps.use_ff = false;
+ ps.dumped_decl_indent = 0;
+ *(e_lab = s_lab) = '\0'; /* reset buffers */
+ *(e_code = s_code) = '\0';
+ *(e_com = s_com) = '\0';
+ ps.ind_level = ps.i_l_follow;
+ ps.paren_level = ps.p_l_follow;
+ paren_target = -ps.paren_indents[ps.paren_level - 1];
+ not_first_line = 1;
+ return;
+}
+
+compute_code_target()
+{
+ register target_col = ps.ind_size * ps.ind_level + 1;
+
+ if (ps.paren_level)
+ if (!lineup_to_parens)
+ target_col += continuation_indent * ps.paren_level;
+ else {
+ register w;
+ register t = paren_target;
+
+ if ((w = count_spaces(t, s_code) - max_col) > 0
+ && count_spaces(target_col, s_code) <= max_col) {
+ t -= w + 1;
+ if (t > target_col)
+ target_col = t;
+ }
+ else
+ target_col = t;
+ }
+ else if (ps.ind_stmt)
+ target_col += continuation_indent;
+ return target_col;
+}
+
+compute_label_target()
+{
+ return
+ ps.pcase ? (int) (case_ind * ps.ind_size) + 1
+ : *s_lab == '#' ? 1
+ : ps.ind_size * (ps.ind_level - label_offset) + 1;
+}
+
+
+/*
+ * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
+ *
+ * All rights reserved
+ *
+ *
+ * NAME: fill_buffer
+ *
+ * FUNCTION: Reads one block of input into input_buffer
+ *
+ * HISTORY: initial coding November 1976 D A Willcox of CAC 1/7/77 A
+ * Willcox of CAC Added check for switch back to partly full input
+ * buffer from temporary buffer
+ *
+ */
+int
+fill_buffer()
+{ /* this routine reads stuff from the input */
+ register char *p;
+ register int i;
+ register FILE *f = input;
+
+ if (bp_save != 0) { /* there is a partly filled input buffer left */
+ buf_ptr = bp_save; /* dont read anything, just switch buffers */
+ buf_end = be_save;
+ bp_save = be_save = 0;
+ if (buf_ptr < buf_end)
+ return; /* only return if there is really something in
+ * this buffer */
+ }
+ for (p = in_buffer;;) {
+ if (p >= in_buffer_limit) {
+ register size = (in_buffer_limit - in_buffer) * 2 + 10;
+ register offset = p - in_buffer;
+ in_buffer = (char *) realloc(in_buffer, size);
+ if (in_buffer == 0)
+ err("input line too long");
+ p = in_buffer + offset;
+ in_buffer_limit = in_buffer + size - 2;
+ }
+ if ((i = getc(f)) == EOF) {
+ *p++ = ' ';
+ *p++ = '\n';
+ had_eof = true;
+ break;
+ }
+ *p++ = i;
+ if (i == '\n')
+ break;
+ }
+ buf_ptr = in_buffer;
+ buf_end = p;
+ if (p[-2] == '/' && p[-3] == '*') {
+ if (in_buffer[3] == 'I' && strncmp(in_buffer, "/**INDENT**", 11) == 0)
+ fill_buffer(); /* flush indent error message */
+ else {
+ int com = 0;
+
+ p = in_buffer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '/' && p[1] == '*') {
+ p += 2;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E'
+ && p[4] == 'N' && p[5] == 'T') {
+ p += 6;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '*')
+ com = 1;
+ else if (*p == 'O')
+ if (*++p == 'N')
+ p++, com = 1;
+ else if (*p == 'F' && *++p == 'F')
+ p++, com = 2;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) {
+ if (s_com != e_com || s_lab != e_lab || s_code != e_code)
+ dump_line();
+ if (!(inhibit_formatting = com - 1)) {
+ n_real_blanklines = 0;
+ postfix_blankline_requested = 0;
+ prefix_blankline_requested = 0;
+ suppress_blanklines = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (inhibit_formatting) {
+ p = in_buffer;
+ do
+ putc(*p, output);
+ while (*p++ != '\n');
+ }
+ return;
+}
+
+/*
+ * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
+ *
+ * All rights reserved
+ *
+ *
+ * NAME: pad_output
+ *
+ * FUNCTION: Writes tabs and spaces to move the current column up to the desired
+ * position.
+ *
+ * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf.
+ *
+ * PARAMETERS: current integer The current column target
+ * nteger The desired column
+ *
+ * RETURNS: Integer value of the new column. (If current >= target, no action is
+ * taken, and current is returned.
+ *
+ * GLOBALS: None
+ *
+ * CALLS: write (sys)
+ *
+ * CALLED BY: dump_line
+ *
+ * HISTORY: initial coding November 1976 D A Willcox of CAC
+ *
+ */
+pad_output(current, target) /* writes tabs and blanks (if necessary) to
+ * get the current output position up to the
+ * target column */
+ int current; /* the current column value */
+ int target; /* position we want it at */
+{
+ register int curr; /* internal column pointer */
+ register int tcur;
+
+ if (troff)
+ fprintf(output, "\\h'|%dp'", (target - 1) * 7);
+ else {
+ if (current >= target)
+ return (current); /* line is already long enough */
+ curr = current;
+ while ((tcur = ((curr - 1) & tabmask) + tabsize + 1) <= target) {
+ putc('\t', output);
+ curr = tcur;
+ }
+ while (curr++ < target)
+ putc(' ', output); /* pad with final blanks */
+ }
+ return (target);
+}
+
+/*
+ * Copyright (C) 1976 by the Board of Trustees of the University of Illinois
+ *
+ * All rights reserved
+ *
+ *
+ * NAME: count_spaces
+ *
+ * FUNCTION: Find out where printing of a given string will leave the current
+ * character position on output.
+ *
+ * ALGORITHM: Run thru input string and add appropriate values to current
+ * position.
+ *
+ * RETURNS: Integer value of position after printing "buffer" starting in column
+ * "current".
+ *
+ * HISTORY: initial coding November 1976 D A Willcox of CAC
+ *
+ */
+int
+count_spaces(current, buffer)
+/*
+ * this routine figures out where the character position will be after
+ * printing the text in buffer starting at column "current"
+ */
+ int current;
+ char *buffer;
+{
+ register char *buf; /* used to look thru buffer */
+ register int cur; /* current character counter */
+
+ cur = current;
+
+ for (buf = buffer; *buf != '\0'; ++buf) {
+ switch (*buf) {
+
+ case '\n':
+ case 014: /* form feed */
+ cur = 1;
+ break;
+
+ case '\t':
+ cur = ((cur - 1) & tabmask) + tabsize + 1;
+ break;
+
+ case 010: /* backspace */
+ --cur;
+ break;
+
+ default:
+ ++cur;
+ break;
+ } /* end of switch */
+ } /* end of for loop */
+ return (cur);
+}
+
+int found_err;
+/* VARARGS2 */
+diag(level, msg, a, b)
+ char *msg;
+{
+ if (level)
+ found_err = 1;
+ if (output == stdout) {
+ fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
+ fprintf(stdout, msg, a, b);
+ fprintf(stdout, " */\n");
+ }
+ else {
+ fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no);
+ fprintf(stderr, msg, a, b);
+ fprintf(stderr, "\n");
+ }
+}
+
+writefdef(f, nm)
+ register struct fstate *f;
+{
+ fprintf(output, ".ds f%c %s\n.nr s%c %d\n",
+ nm, f->font, nm, f->size);
+}
+
+char *
+chfont(of, nf, s)
+ register struct fstate *of,
+ *nf;
+ char *s;
+{
+ if (of->font[0] != nf->font[0]
+ || of->font[1] != nf->font[1]) {
+ *s++ = '\\';
+ *s++ = 'f';
+ if (nf->font[1]) {
+ *s++ = '(';
+ *s++ = nf->font[0];
+ *s++ = nf->font[1];
+ }
+ else
+ *s++ = nf->font[0];
+ }
+ if (nf->size != of->size) {
+ *s++ = '\\';
+ *s++ = 's';
+ if (nf->size < of->size) {
+ *s++ = '-';
+ *s++ = '0' + of->size - nf->size;
+ }
+ else {
+ *s++ = '+';
+ *s++ = '0' + nf->size - of->size;
+ }
+ }
+ return s;
+}
+
+
+parsefont(f, s0)
+ register struct fstate *f;
+ char *s0;
+{
+ register char *s = s0;
+ int sizedelta = 0;
+ bzero(f, sizeof *f);
+ while (*s) {
+ if (isdigit(*s))
+ f->size = f->size * 10 + *s - '0';
+ else if (isupper(*s))
+ if (f->font[0])
+ f->font[1] = *s;
+ else
+ f->font[0] = *s;
+ else if (*s == 'c')
+ f->allcaps = 1;
+ else if (*s == '+')
+ sizedelta++;
+ else if (*s == '-')
+ sizedelta--;
+ else {
+ fprintf(stderr, "indent: bad font specification: %s\n", s0);
+ exit(1);
+ }
+ s++;
+ }
+ if (f->font[0] == 0)
+ f->font[0] = 'R';
+ if (bodyf.size == 0)
+ bodyf.size = 11;
+ if (f->size == 0)
+ f->size = bodyf.size + sizedelta;
+ else if (sizedelta > 0)
+ f->size += bodyf.size;
+ else
+ f->size = bodyf.size - f->size;
+}
diff --git a/usr.bin/indent/lexi.c b/usr.bin/indent/lexi.c
new file mode 100644
index 0000000..69f2431
--- /dev/null
+++ b/usr.bin/indent/lexi.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lexi.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Here we have the token scanner for indent. It scans off one token and puts
+ * it in the global variable "token". It returns a code, indicating the type
+ * of token scanned.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "indent_globs.h"
+#include "indent_codes.h"
+
+#define alphanum 1
+#define opchar 3
+
+struct templ {
+ char *rwd;
+ int rwcode;
+};
+
+struct templ specials[100] =
+{
+ "switch", 1,
+ "case", 2,
+ "break", 0,
+ "struct", 3,
+ "union", 3,
+ "enum", 3,
+ "default", 2,
+ "int", 4,
+ "char", 4,
+ "float", 4,
+ "double", 4,
+ "long", 4,
+ "short", 4,
+ "typdef", 4,
+ "unsigned", 4,
+ "register", 4,
+ "static", 4,
+ "global", 4,
+ "extern", 4,
+ "void", 4,
+ "goto", 0,
+ "return", 0,
+ "if", 5,
+ "while", 5,
+ "for", 5,
+ "else", 6,
+ "do", 6,
+ "sizeof", 7,
+ 0, 0
+};
+
+char chartype[128] =
+{ /* this is used to facilitate the decision of
+ * what type (alphanumeric, operator) each
+ * character is */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 1, 3, 3, 0,
+ 0, 0, 3, 3, 0, 3, 0, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0, 0, 3, 3, 3, 3,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 0, 0, 3, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 3, 0, 3, 0
+};
+
+
+
+
+int
+lexi()
+{
+ int unary_delim; /* this is set to 1 if the current token
+ *
+ * forces a following operator to be unary */
+ static int last_code; /* the last token type returned */
+ static int l_struct; /* set to 1 if the last token was 'struct' */
+ int code; /* internal code to be returned */
+ char qchar; /* the delimiter character for a string */
+
+ e_token = s_token; /* point to start of place to save token */
+ unary_delim = false;
+ ps.col_1 = ps.last_nl; /* tell world that this token started in
+ * column 1 iff the last thing scanned was nl */
+ ps.last_nl = false;
+
+ while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */
+ ps.col_1 = false; /* leading blanks imply token is not in column
+ * 1 */
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ }
+
+ /* Scan an alphanumeric token */
+ if (chartype[*buf_ptr] == alphanum || buf_ptr[0] == '.' && isdigit(buf_ptr[1])) {
+ /*
+ * we have a character or number
+ */
+ register char *j; /* used for searching thru list of
+ *
+ * reserved words */
+ register struct templ *p;
+
+ if (isdigit(*buf_ptr) || buf_ptr[0] == '.' && isdigit(buf_ptr[1])) {
+ int seendot = 0,
+ seenexp = 0;
+ if (*buf_ptr == '0' &&
+ (buf_ptr[1] == 'x' || buf_ptr[1] == 'X')) {
+ *e_token++ = *buf_ptr++;
+ *e_token++ = *buf_ptr++;
+ while (isxdigit(*buf_ptr)) {
+ CHECK_SIZE_TOKEN;
+ *e_token++ = *buf_ptr++;
+ }
+ }
+ else
+ while (1) {
+ if (*buf_ptr == '.')
+ if (seendot)
+ break;
+ else
+ seendot++;
+ CHECK_SIZE_TOKEN;
+ *e_token++ = *buf_ptr++;
+ if (!isdigit(*buf_ptr) && *buf_ptr != '.')
+ if ((*buf_ptr != 'E' && *buf_ptr != 'e') || seenexp)
+ break;
+ else {
+ seenexp++;
+ seendot++;
+ CHECK_SIZE_TOKEN;
+ *e_token++ = *buf_ptr++;
+ if (*buf_ptr == '+' || *buf_ptr == '-')
+ *e_token++ = *buf_ptr++;
+ }
+ }
+ if (*buf_ptr == 'L' || *buf_ptr == 'l')
+ *e_token++ = *buf_ptr++;
+ }
+ else
+ while (chartype[*buf_ptr] == alphanum) { /* copy it over */
+ CHECK_SIZE_TOKEN;
+ *e_token++ = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ *e_token++ = '\0';
+ while (*buf_ptr == ' ' || *buf_ptr == '\t') { /* get rid of blanks */
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ ps.its_a_keyword = false;
+ ps.sizeof_keyword = false;
+ if (l_struct) { /* if last token was 'struct', then this token
+ * should be treated as a declaration */
+ l_struct = false;
+ last_code = ident;
+ ps.last_u_d = true;
+ return (decl);
+ }
+ ps.last_u_d = false; /* Operator after indentifier is binary */
+ last_code = ident; /* Remember that this is the code we will
+ * return */
+
+ /*
+ * This loop will check if the token is a keyword.
+ */
+ for (p = specials; (j = p->rwd) != 0; p++) {
+ register char *p = s_token; /* point at scanned token */
+ if (*j++ != *p++ || *j++ != *p++)
+ continue; /* This test depends on the fact that
+ * identifiers are always at least 1 character
+ * long (ie. the first two bytes of the
+ * identifier are always meaningful) */
+ if (p[-1] == 0)
+ break; /* If its a one-character identifier */
+ while (*p++ == *j)
+ if (*j++ == 0)
+ goto found_keyword; /* I wish that C had a multi-level
+ * break... */
+ }
+ if (p->rwd) { /* we have a keyword */
+ found_keyword:
+ ps.its_a_keyword = true;
+ ps.last_u_d = true;
+ switch (p->rwcode) {
+ case 1: /* it is a switch */
+ return (swstmt);
+ case 2: /* a case or default */
+ return (casestmt);
+
+ case 3: /* a "struct" */
+ if (ps.p_l_follow)
+ break; /* inside parens: cast */
+ l_struct = true;
+
+ /*
+ * Next time around, we will want to know that we have had a
+ * 'struct'
+ */
+ case 4: /* one of the declaration keywords */
+ if (ps.p_l_follow) {
+ ps.cast_mask |= 1 << ps.p_l_follow;
+ break; /* inside parens: cast */
+ }
+ last_code = decl;
+ return (decl);
+
+ case 5: /* if, while, for */
+ return (sp_paren);
+
+ case 6: /* do, else */
+ return (sp_nparen);
+
+ case 7:
+ ps.sizeof_keyword = true;
+ default: /* all others are treated like any other
+ * identifier */
+ return (ident);
+ } /* end of switch */
+ } /* end of if (found_it) */
+ if (*buf_ptr == '(' && ps.tos <= 1 && ps.ind_level == 0) {
+ register char *tp = buf_ptr;
+ while (tp < buf_end)
+ if (*tp++ == ')' && (*tp == ';' || *tp == ','))
+ goto not_proc;
+ strncpy(ps.procname, token, sizeof ps.procname - 1);
+ ps.in_parameter_declaration = 1;
+ rparen_count = 1;
+ not_proc:;
+ }
+ /*
+ * The following hack attempts to guess whether or not the current
+ * token is in fact a declaration keyword -- one that has been
+ * typedefd
+ */
+ if (((*buf_ptr == '*' && buf_ptr[1] != '=') || isalpha(*buf_ptr) || *buf_ptr == '_')
+ && !ps.p_l_follow
+ && !ps.block_init
+ && (ps.last_token == rparen || ps.last_token == semicolon ||
+ ps.last_token == decl ||
+ ps.last_token == lbrace || ps.last_token == rbrace)) {
+ ps.its_a_keyword = true;
+ ps.last_u_d = true;
+ last_code = decl;
+ return decl;
+ }
+ if (last_code == decl) /* if this is a declared variable, then
+ * following sign is unary */
+ ps.last_u_d = true; /* will make "int a -1" work */
+ last_code = ident;
+ return (ident); /* the ident is not in the list */
+ } /* end of procesing for alpanum character */
+
+ /* Scan a non-alphanumeric token */
+
+ *e_token++ = *buf_ptr; /* if it is only a one-character token, it is
+ * moved here */
+ *e_token = '\0';
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+
+ switch (*token) {
+ case '\n':
+ unary_delim = ps.last_u_d;
+ ps.last_nl = true; /* remember that we just had a newline */
+ code = (had_eof ? 0 : newline);
+
+ /*
+ * if data has been exausted, the newline is a dummy, and we should
+ * return code to stop
+ */
+ break;
+
+ case '\'': /* start of quoted character */
+ case '"': /* start of string */
+ qchar = *token;
+ if (troff) {
+ e_token[-1] = '`';
+ if (qchar == '"')
+ *e_token++ = '`';
+ e_token = chfont(&bodyf, &stringf, e_token);
+ }
+ do { /* copy the string */
+ while (1) { /* move one character or [/<char>]<char> */
+ if (*buf_ptr == '\n') {
+ printf("%d: Unterminated literal\n", line_no);
+ goto stop_lit;
+ }
+ CHECK_SIZE_TOKEN; /* Only have to do this once in this loop,
+ * since CHECK_SIZE guarantees that there
+ * are at least 5 entries left */
+ *e_token = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ if (*e_token == BACKSLASH) { /* if escape, copy extra char */
+ if (*buf_ptr == '\n') /* check for escaped newline */
+ ++line_no;
+ if (troff) {
+ *++e_token = BACKSLASH;
+ if (*buf_ptr == BACKSLASH)
+ *++e_token = BACKSLASH;
+ }
+ *++e_token = *buf_ptr++;
+ ++e_token; /* we must increment this again because we
+ * copied two chars */
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ else
+ break; /* we copied one character */
+ } /* end of while (1) */
+ } while (*e_token++ != qchar);
+ if (troff) {
+ e_token = chfont(&stringf, &bodyf, e_token - 1);
+ if (qchar == '"')
+ *e_token++ = '\'';
+ }
+stop_lit:
+ code = ident;
+ break;
+
+ case ('('):
+ case ('['):
+ unary_delim = true;
+ code = lparen;
+ break;
+
+ case (')'):
+ case (']'):
+ code = rparen;
+ break;
+
+ case '#':
+ unary_delim = ps.last_u_d;
+ code = preesc;
+ break;
+
+ case '?':
+ unary_delim = true;
+ code = question;
+ break;
+
+ case (':'):
+ code = colon;
+ unary_delim = true;
+ break;
+
+ case (';'):
+ unary_delim = true;
+ code = semicolon;
+ break;
+
+ case ('{'):
+ unary_delim = true;
+
+ /*
+ * if (ps.in_or_st) ps.block_init = 1;
+ */
+ /* ? code = ps.block_init ? lparen : lbrace; */
+ code = lbrace;
+ break;
+
+ case ('}'):
+ unary_delim = true;
+ /* ? code = ps.block_init ? rparen : rbrace; */
+ code = rbrace;
+ break;
+
+ case 014: /* a form feed */
+ unary_delim = ps.last_u_d;
+ ps.last_nl = true; /* remember this so we can set 'ps.col_1'
+ * right */
+ code = form_feed;
+ break;
+
+ case (','):
+ unary_delim = true;
+ code = comma;
+ break;
+
+ case '.':
+ unary_delim = false;
+ code = period;
+ break;
+
+ case '-':
+ case '+': /* check for -, +, --, ++ */
+ code = (ps.last_u_d ? unary_op : binary_op);
+ unary_delim = true;
+
+ if (*buf_ptr == token[0]) {
+ /* check for doubled character */
+ *e_token++ = *buf_ptr++;
+ /* buffer overflow will be checked at end of loop */
+ if (last_code == ident || last_code == rparen) {
+ code = (ps.last_u_d ? unary_op : postop);
+ /* check for following ++ or -- */
+ unary_delim = false;
+ }
+ }
+ else if (*buf_ptr == '=')
+ /* check for operator += */
+ *e_token++ = *buf_ptr++;
+ else if (*buf_ptr == '>') {
+ /* check for operator -> */
+ *e_token++ = *buf_ptr++;
+ if (!pointer_as_binop) {
+ unary_delim = false;
+ code = unary_op;
+ ps.want_blank = false;
+ }
+ }
+ break; /* buffer overflow will be checked at end of
+ * switch */
+
+ case '=':
+ if (ps.in_or_st)
+ ps.block_init = 1;
+#ifdef undef
+ if (chartype[*buf_ptr] == opchar) { /* we have two char assignment */
+ e_token[-1] = *buf_ptr++;
+ if ((e_token[-1] == '<' || e_token[-1] == '>') && e_token[-1] == *buf_ptr)
+ *e_token++ = *buf_ptr++;
+ *e_token++ = '='; /* Flip =+ to += */
+ *e_token = 0;
+ }
+#else
+ if (*buf_ptr == '=') {/* == */
+ *e_token++ = '='; /* Flip =+ to += */
+ buf_ptr++;
+ *e_token = 0;
+ }
+#endif
+ code = binary_op;
+ unary_delim = true;
+ break;
+ /* can drop thru!!! */
+
+ case '>':
+ case '<':
+ case '!': /* ops like <, <<, <=, !=, etc */
+ if (*buf_ptr == '>' || *buf_ptr == '<' || *buf_ptr == '=') {
+ *e_token++ = *buf_ptr;
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ if (*buf_ptr == '=')
+ *e_token++ = *buf_ptr++;
+ code = (ps.last_u_d ? unary_op : binary_op);
+ unary_delim = true;
+ break;
+
+ default:
+ if (token[0] == '/' && *buf_ptr == '*') {
+ /* it is start of comment */
+ *e_token++ = '*';
+
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+
+ code = comment;
+ unary_delim = ps.last_u_d;
+ break;
+ }
+ while (*(e_token - 1) == *buf_ptr || *buf_ptr == '=') {
+ /*
+ * handle ||, &&, etc, and also things as in int *****i
+ */
+ *e_token++ = *buf_ptr;
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ }
+ code = (ps.last_u_d ? unary_op : binary_op);
+ unary_delim = true;
+
+
+ } /* end of switch */
+ if (code != newline) {
+ l_struct = false;
+ last_code = code;
+ }
+ if (buf_ptr >= buf_end) /* check for input buffer empty */
+ fill_buffer();
+ ps.last_u_d = unary_delim;
+ *e_token = '\0'; /* null terminate the token */
+ return (code);
+}
+
+/*
+ * Add the given keyword to the keyword table, using val as the keyword type
+ */
+addkey(key, val)
+ char *key;
+{
+ register struct templ *p = specials;
+ while (p->rwd)
+ if (p->rwd[0] == key[0] && strcmp(p->rwd, key) == 0)
+ return;
+ else
+ p++;
+ if (p >= specials + sizeof specials / sizeof specials[0])
+ return; /* For now, table overflows are silently
+ * ignored */
+ p->rwd = key;
+ p->rwcode = val;
+ p[1].rwd = 0;
+ p[1].rwcode = 0;
+ return;
+}
diff --git a/usr.bin/indent/parse.c b/usr.bin/indent/parse.c
new file mode 100644
index 0000000..f3a4b44
--- /dev/null
+++ b/usr.bin/indent/parse.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "indent_globs.h"
+#include "indent_codes.h"
+
+parse(tk)
+ int tk; /* the code for the construct scanned */
+{
+ int i;
+
+#ifdef debug
+ printf("%2d - %s\n", tk, token);
+#endif
+
+ while (ps.p_stack[ps.tos] == ifhead && tk != elselit) {
+ /* true if we have an if without an else */
+ ps.p_stack[ps.tos] = stmt; /* apply the if(..) stmt ::= stmt
+ * reduction */
+ reduce(); /* see if this allows any reduction */
+ }
+
+
+ switch (tk) { /* go on and figure out what to do with the
+ * input */
+
+ case decl: /* scanned a declaration word */
+ ps.search_brace = btype_2;
+ /* indicate that following brace should be on same line */
+ if (ps.p_stack[ps.tos] != decl) { /* only put one declaration
+ * onto stack */
+ break_comma = true; /* while in declaration, newline should be
+ * forced after comma */
+ ps.p_stack[++ps.tos] = decl;
+ ps.il[ps.tos] = ps.i_l_follow;
+
+ if (ps.ljust_decl) {/* only do if we want left justified
+ * declarations */
+ ps.ind_level = 0;
+ for (i = ps.tos - 1; i > 0; --i)
+ if (ps.p_stack[i] == decl)
+ ++ps.ind_level; /* indentation is number of
+ * declaration levels deep we are */
+ ps.i_l_follow = ps.ind_level;
+ }
+ }
+ break;
+
+ case ifstmt: /* scanned if (...) */
+ if (ps.p_stack[ps.tos] == elsehead && ps.else_if) /* "else if ..." */
+ ps.i_l_follow = ps.il[ps.tos];
+ case dolit: /* 'do' */
+ case forstmt: /* for (...) */
+ ps.p_stack[++ps.tos] = tk;
+ ps.il[ps.tos] = ps.ind_level = ps.i_l_follow;
+ ++ps.i_l_follow; /* subsequent statements should be indented 1 */
+ ps.search_brace = btype_2;
+ break;
+
+ case lbrace: /* scanned { */
+ break_comma = false; /* don't break comma in an initial list */
+ if (ps.p_stack[ps.tos] == stmt || ps.p_stack[ps.tos] == decl
+ || ps.p_stack[ps.tos] == stmtl)
+ ++ps.i_l_follow; /* it is a random, isolated stmt group or a
+ * declaration */
+ else {
+ if (s_code == e_code) {
+ /*
+ * only do this if there is nothing on the line
+ */
+ --ps.ind_level;
+ /*
+ * it is a group as part of a while, for, etc.
+ */
+ if (ps.p_stack[ps.tos] == swstmt && ps.case_indent >= 1)
+ --ps.ind_level;
+ /*
+ * for a switch, brace should be two levels out from the code
+ */
+ }
+ }
+
+ ps.p_stack[++ps.tos] = lbrace;
+ ps.il[ps.tos] = ps.ind_level;
+ ps.p_stack[++ps.tos] = stmt;
+ /* allow null stmt between braces */
+ ps.il[ps.tos] = ps.i_l_follow;
+ break;
+
+ case whilestmt: /* scanned while (...) */
+ if (ps.p_stack[ps.tos] == dohead) {
+ /* it is matched with do stmt */
+ ps.ind_level = ps.i_l_follow = ps.il[ps.tos];
+ ps.p_stack[++ps.tos] = whilestmt;
+ ps.il[ps.tos] = ps.ind_level = ps.i_l_follow;
+ }
+ else { /* it is a while loop */
+ ps.p_stack[++ps.tos] = whilestmt;
+ ps.il[ps.tos] = ps.i_l_follow;
+ ++ps.i_l_follow;
+ ps.search_brace = btype_2;
+ }
+
+ break;
+
+ case elselit: /* scanned an else */
+
+ if (ps.p_stack[ps.tos] != ifhead)
+ diag(1, "Unmatched 'else'");
+ else {
+ ps.ind_level = ps.il[ps.tos]; /* indentation for else should
+ * be same as for if */
+ ps.i_l_follow = ps.ind_level + 1; /* everything following should
+ * be in 1 level */
+ ps.p_stack[ps.tos] = elsehead;
+ /* remember if with else */
+ ps.search_brace = btype_2 | ps.else_if;
+ }
+ break;
+
+ case rbrace: /* scanned a } */
+ /* stack should have <lbrace> <stmt> or <lbrace> <stmtl> */
+ if (ps.p_stack[ps.tos - 1] == lbrace) {
+ ps.ind_level = ps.i_l_follow = ps.il[--ps.tos];
+ ps.p_stack[ps.tos] = stmt;
+ }
+ else
+ diag(1, "Stmt nesting error.");
+ break;
+
+ case swstmt: /* had switch (...) */
+ ps.p_stack[++ps.tos] = swstmt;
+ ps.cstk[ps.tos] = case_ind;
+ /* save current case indent level */
+ ps.il[ps.tos] = ps.i_l_follow;
+ case_ind = ps.i_l_follow + ps.case_indent; /* cases should be one
+ * level down from
+ * switch */
+ ps.i_l_follow += ps.case_indent + 1; /* statements should be two
+ * levels in */
+ ps.search_brace = btype_2;
+ break;
+
+ case semicolon: /* this indicates a simple stmt */
+ break_comma = false; /* turn off flag to break after commas in a
+ * declaration */
+ ps.p_stack[++ps.tos] = stmt;
+ ps.il[ps.tos] = ps.ind_level;
+ break;
+
+ default: /* this is an error */
+ diag(1, "Unknown code to parser");
+ return;
+
+
+ } /* end of switch */
+
+ reduce(); /* see if any reduction can be done */
+
+#ifdef debug
+ for (i = 1; i <= ps.tos; ++i)
+ printf("(%d %d)", ps.p_stack[i], ps.il[i]);
+ printf("\n");
+#endif
+
+ return;
+}
+
+/*
+ * NAME: reduce
+ *
+ * FUNCTION: Implements the reduce part of the parsing algorithm
+ *
+ * ALGORITHM: The following reductions are done. Reductions are repeated
+ * until no more are possible.
+ *
+ * Old TOS New TOS
+ * <stmt> <stmt> <stmtl>
+ * <stmtl> <stmt> <stmtl>
+ * do <stmt> "dostmt"
+ * if <stmt> "ifstmt"
+ * switch <stmt> <stmt>
+ * decl <stmt> <stmt>
+ * "ifelse" <stmt> <stmt>
+ * for <stmt> <stmt>
+ * while <stmt> <stmt>
+ * "dostmt" while <stmt>
+ *
+ * On each reduction, ps.i_l_follow (the indentation for the following line)
+ * is set to the indentation level associated with the old TOS.
+ *
+ * PARAMETERS: None
+ *
+ * RETURNS: Nothing
+ *
+ * GLOBALS: ps.cstk ps.i_l_follow = ps.il ps.p_stack = ps.tos =
+ *
+ * CALLS: None
+ *
+ * CALLED BY: parse
+ *
+ * HISTORY: initial coding November 1976 D A Willcox of CAC
+ *
+ */
+/*----------------------------------------------*\
+| REDUCTION PHASE |
+\*----------------------------------------------*/
+reduce()
+{
+
+ register int i;
+
+ for (;;) { /* keep looping until there is nothing left to
+ * reduce */
+
+ switch (ps.p_stack[ps.tos]) {
+
+ case stmt:
+ switch (ps.p_stack[ps.tos - 1]) {
+
+ case stmt:
+ case stmtl:
+ /* stmtl stmt or stmt stmt */
+ ps.p_stack[--ps.tos] = stmtl;
+ break;
+
+ case dolit: /* <do> <stmt> */
+ ps.p_stack[--ps.tos] = dohead;
+ ps.i_l_follow = ps.il[ps.tos];
+ break;
+
+ case ifstmt:
+ /* <if> <stmt> */
+ ps.p_stack[--ps.tos] = ifhead;
+ for (i = ps.tos - 1;
+ (
+ ps.p_stack[i] != stmt
+ &&
+ ps.p_stack[i] != stmtl
+ &&
+ ps.p_stack[i] != lbrace
+ );
+ --i);
+ ps.i_l_follow = ps.il[i];
+ /*
+ * for the time being, we will assume that there is no else on
+ * this if, and set the indentation level accordingly. If an
+ * else is scanned, it will be fixed up later
+ */
+ break;
+
+ case swstmt:
+ /* <switch> <stmt> */
+ case_ind = ps.cstk[ps.tos - 1];
+
+ case decl: /* finish of a declaration */
+ case elsehead:
+ /* <<if> <stmt> else> <stmt> */
+ case forstmt:
+ /* <for> <stmt> */
+ case whilestmt:
+ /* <while> <stmt> */
+ ps.p_stack[--ps.tos] = stmt;
+ ps.i_l_follow = ps.il[ps.tos];
+ break;
+
+ default: /* <anything else> <stmt> */
+ return;
+
+ } /* end of section for <stmt> on top of stack */
+ break;
+
+ case whilestmt: /* while (...) on top */
+ if (ps.p_stack[ps.tos - 1] == dohead) {
+ /* it is termination of a do while */
+ ps.p_stack[--ps.tos] = stmt;
+ break;
+ }
+ else
+ return;
+
+ default: /* anything else on top */
+ return;
+
+ }
+ }
+}
diff --git a/usr.bin/indent/pr_comment.c b/usr.bin/indent/pr_comment.c
new file mode 100644
index 0000000..2d4bc90
--- /dev/null
+++ b/usr.bin/indent/pr_comment.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1985 Sun Microsystems, Inc.
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pr_comment.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "indent_globs.h"
+
+/*
+ * NAME:
+ * pr_comment
+ *
+ * FUNCTION:
+ * This routine takes care of scanning and printing comments.
+ *
+ * ALGORITHM:
+ * 1) Decide where the comment should be aligned, and if lines should
+ * be broken.
+ * 2) If lines should not be broken and filled, just copy up to end of
+ * comment.
+ * 3) If lines should be filled, then scan thru input_buffer copying
+ * characters to com_buf. Remember where the last blank, tab, or
+ * newline was. When line is filled, print up to last blank and
+ * continue copying.
+ *
+ * HISTORY:
+ * November 1976 D A Willcox of CAC Initial coding
+ * 12/6/76 D A Willcox of CAC Modification to handle
+ * UNIX-style comments
+ *
+ */
+
+/*
+ * this routine processes comments. It makes an attempt to keep comments from
+ * going over the max line length. If a line is too long, it moves everything
+ * from the last blank to the next comment line. Blanks and tabs from the
+ * beginning of the input line are removed
+ */
+
+
+pr_comment()
+{
+ int now_col; /* column we are in now */
+ int adj_max_col; /* Adjusted max_col for when we decide to
+ * spill comments over the right margin */
+ char *last_bl; /* points to the last blank in the output
+ * buffer */
+ char *t_ptr; /* used for moving string */
+ int unix_comment; /* tri-state variable used to decide if it is
+ * a unix-style comment. 0 means only blanks
+ * since /*, 1 means regular style comment, 2
+ * means unix style comment */
+ int break_delim = comment_delimiter_on_blankline;
+ int l_just_saw_decl = ps.just_saw_decl;
+ /*
+ * int ps.last_nl = 0; /* true iff the last significant thing
+ * weve seen is a newline
+ */
+ int one_liner = 1; /* true iff this comment is a one-liner */
+ adj_max_col = max_col;
+ ps.just_saw_decl = 0;
+ last_bl = 0; /* no blanks found so far */
+ ps.box_com = false; /* at first, assume that we are not in
+ * a boxed comment or some other
+ * comment that should not be touched */
+ ++ps.out_coms; /* keep track of number of comments */
+ unix_comment = 1; /* set flag to let us figure out if there is a
+ * unix-style comment ** DISABLED: use 0 to
+ * reenable this hack! */
+
+ /* Figure where to align and how to treat the comment */
+
+ if (ps.col_1 && !format_col1_comments) { /* if comment starts in column
+ * 1 it should not be touched */
+ ps.box_com = true;
+ ps.com_col = 1;
+ }
+ else {
+ if (*buf_ptr == '-' || *buf_ptr == '*') {
+ ps.box_com = true; /* a comment with a '-' or '*' immediately
+ * after the /* is assumed to be a boxed
+ * comment */
+ break_delim = 0;
+ }
+ if ( /* ps.bl_line && */ (s_lab == e_lab) && (s_code == e_code)) {
+ /* klg: check only if this line is blank */
+ /*
+ * If this (*and previous lines are*) blank, dont put comment way
+ * out at left
+ */
+ ps.com_col = (ps.ind_level - ps.unindent_displace) * ps.ind_size + 1;
+ adj_max_col = block_comment_max_col;
+ if (ps.com_col <= 1)
+ ps.com_col = 1 + !format_col1_comments;
+ }
+ else {
+ register target_col;
+ break_delim = 0;
+ if (s_code != e_code)
+ target_col = count_spaces(compute_code_target(), s_code);
+ else {
+ target_col = 1;
+ if (s_lab != e_lab)
+ target_col = count_spaces(compute_label_target(), s_lab);
+ }
+ ps.com_col = ps.decl_on_line || ps.ind_level == 0 ? ps.decl_com_ind : ps.com_ind;
+ if (ps.com_col < target_col)
+ ps.com_col = ((target_col + 7) & ~7) + 1;
+ if (ps.com_col + 24 > adj_max_col)
+ adj_max_col = ps.com_col + 24;
+ }
+ }
+ if (ps.box_com) {
+ buf_ptr[-2] = 0;
+ ps.n_comment_delta = 1 - count_spaces(1, in_buffer);
+ buf_ptr[-2] = '/';
+ }
+ else {
+ ps.n_comment_delta = 0;
+ while (*buf_ptr == ' ' || *buf_ptr == '\t')
+ buf_ptr++;
+ }
+ ps.comment_delta = 0;
+ *e_com++ = '/'; /* put '/*' into buffer */
+ *e_com++ = '*';
+ if (*buf_ptr != ' ' && !ps.box_com)
+ *e_com++ = ' ';
+
+ *e_com = '\0';
+ if (troff) {
+ now_col = 1;
+ adj_max_col = 80;
+ }
+ else
+ now_col = count_spaces(ps.com_col, s_com); /* figure what column we
+ * would be in if we
+ * printed the comment
+ * now */
+
+ /* Start to copy the comment */
+
+ while (1) { /* this loop will go until the comment is
+ * copied */
+ if (*buf_ptr > 040 && *buf_ptr != '*')
+ ps.last_nl = 0;
+ CHECK_SIZE_COM;
+ switch (*buf_ptr) { /* this checks for various spcl cases */
+ case 014: /* check for a form feed */
+ if (!ps.box_com) { /* in a text comment, break the line here */
+ ps.use_ff = true;
+ /* fix so dump_line uses a form feed */
+ dump_line();
+ last_bl = 0;
+ *e_com++ = ' ';
+ *e_com++ = '*';
+ *e_com++ = ' ';
+ while (*++buf_ptr == ' ' || *buf_ptr == '\t');
+ }
+ else {
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ *e_com++ = 014;
+ }
+ break;
+
+ case '\n':
+ if (had_eof) { /* check for unexpected eof */
+ printf("Unterminated comment\n");
+ *e_com = '\0';
+ dump_line();
+ return;
+ }
+ one_liner = 0;
+ if (ps.box_com || ps.last_nl) { /* if this is a boxed comment,
+ * we dont ignore the newline */
+ if (s_com == e_com) {
+ *e_com++ = ' ';
+ *e_com++ = ' ';
+ }
+ *e_com = '\0';
+ if (!ps.box_com && e_com - s_com > 3) {
+ if (break_delim == 1 && s_com[0] == '/'
+ && s_com[1] == '*' && s_com[2] == ' ') {
+ char *t = e_com;
+ break_delim = 2;
+ e_com = s_com + 2;
+ *e_com = 0;
+ if (blanklines_before_blockcomments)
+ prefix_blankline_requested = 1;
+ dump_line();
+ e_com = t;
+ s_com[0] = s_com[1] = s_com[2] = ' ';
+ }
+ dump_line();
+ CHECK_SIZE_COM;
+ *e_com++ = ' ';
+ *e_com++ = ' ';
+ }
+ dump_line();
+ now_col = ps.com_col;
+ }
+ else {
+ ps.last_nl = 1;
+ if (unix_comment != 1) { /* we not are in unix_style
+ * comment */
+ if (unix_comment == 0 && s_code == e_code) {
+ /*
+ * if it is a UNIX-style comment, ignore the
+ * requirement that previous line be blank for
+ * unindention
+ */
+ ps.com_col = (ps.ind_level - ps.unindent_displace) * ps.ind_size + 1;
+ if (ps.com_col <= 1)
+ ps.com_col = 2;
+ }
+ unix_comment = 2; /* permanently remember that we are in
+ * this type of comment */
+ dump_line();
+ ++line_no;
+ now_col = ps.com_col;
+ *e_com++ = ' ';
+ /*
+ * fix so that the star at the start of the line will line
+ * up
+ */
+ do /* flush leading white space */
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ while (*buf_ptr == ' ' || *buf_ptr == '\t');
+ break;
+ }
+ if (*(e_com - 1) == ' ' || *(e_com - 1) == '\t')
+ last_bl = e_com - 1;
+ /*
+ * if there was a space at the end of the last line, remember
+ * where it was
+ */
+ else { /* otherwise, insert one */
+ last_bl = e_com;
+ CHECK_SIZE_COM;
+ *e_com++ = ' ';
+ ++now_col;
+ }
+ }
+ ++line_no; /* keep track of input line number */
+ if (!ps.box_com) {
+ int nstar = 1;
+ do { /* flush any blanks and/or tabs at start of
+ * next line */
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ if (*buf_ptr == '*' && --nstar >= 0) {
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+ if (*buf_ptr == '/')
+ goto end_of_comment;
+ }
+ } while (*buf_ptr == ' ' || *buf_ptr == '\t');
+ }
+ else if (++buf_ptr >= buf_end)
+ fill_buffer();
+ break; /* end of case for newline */
+
+ case '*': /* must check for possibility of being at end
+ * of comment */
+ if (++buf_ptr >= buf_end) /* get to next char after * */
+ fill_buffer();
+
+ if (unix_comment == 0) /* set flag to show we are not in
+ * unix-style comment */
+ unix_comment = 1;
+
+ if (*buf_ptr == '/') { /* it is the end!!! */
+ end_of_comment:
+ if (++buf_ptr >= buf_end)
+ fill_buffer();
+
+ if (*(e_com - 1) != ' ' && !ps.box_com) { /* insure blank before
+ * end */
+ *e_com++ = ' ';
+ ++now_col;
+ }
+ if (break_delim == 1 && !one_liner && s_com[0] == '/'
+ && s_com[1] == '*' && s_com[2] == ' ') {
+ char *t = e_com;
+ break_delim = 2;
+ e_com = s_com + 2;
+ *e_com = 0;
+ if (blanklines_before_blockcomments)
+ prefix_blankline_requested = 1;
+ dump_line();
+ e_com = t;
+ s_com[0] = s_com[1] = s_com[2] = ' ';
+ }
+ if (break_delim == 2 && e_com > s_com + 3
+ /* now_col > adj_max_col - 2 && !ps.box_com */ ) {
+ *e_com = '\0';
+ dump_line();
+ now_col = ps.com_col;
+ }
+ CHECK_SIZE_COM;
+ *e_com++ = '*';
+ *e_com++ = '/';
+ *e_com = '\0';
+ ps.just_saw_decl = l_just_saw_decl;
+ return;
+ }
+ else { /* handle isolated '*' */
+ *e_com++ = '*';
+ ++now_col;
+ }
+ break;
+ default: /* we have a random char */
+ if (unix_comment == 0 && *buf_ptr != ' ' && *buf_ptr != '\t')
+ unix_comment = 1; /* we are not in unix-style comment */
+
+ *e_com = *buf_ptr++;
+ if (buf_ptr >= buf_end)
+ fill_buffer();
+
+ if (*e_com == '\t') /* keep track of column */
+ now_col = ((now_col - 1) & tabmask) + tabsize + 1;
+ else if (*e_com == '\b') /* this is a backspace */
+ --now_col;
+ else
+ ++now_col;
+
+ if (*e_com == ' ' || *e_com == '\t')
+ last_bl = e_com;
+ /* remember we saw a blank */
+
+ ++e_com;
+ if (now_col > adj_max_col && !ps.box_com && unix_comment == 1 && e_com[-1] > ' ') {
+ /*
+ * the comment is too long, it must be broken up
+ */
+ if (break_delim == 1 && s_com[0] == '/'
+ && s_com[1] == '*' && s_com[2] == ' ') {
+ char *t = e_com;
+ break_delim = 2;
+ e_com = s_com + 2;
+ *e_com = 0;
+ if (blanklines_before_blockcomments)
+ prefix_blankline_requested = 1;
+ dump_line();
+ e_com = t;
+ s_com[0] = s_com[1] = s_com[2] = ' ';
+ }
+ if (last_bl == 0) { /* we have seen no blanks */
+ last_bl = e_com; /* fake it */
+ *e_com++ = ' ';
+ }
+ *e_com = '\0'; /* print what we have */
+ *last_bl = '\0';
+ while (last_bl > s_com && last_bl[-1] < 040)
+ *--last_bl = 0;
+ e_com = last_bl;
+ dump_line();
+
+ *e_com++ = ' '; /* add blanks for continuation */
+ *e_com++ = ' ';
+ *e_com++ = ' ';
+
+ t_ptr = last_bl + 1;
+ last_bl = 0;
+ if (t_ptr >= e_com) {
+ while (*t_ptr == ' ' || *t_ptr == '\t')
+ t_ptr++;
+ while (*t_ptr != '\0') { /* move unprinted part of
+ * comment down in buffer */
+ if (*t_ptr == ' ' || *t_ptr == '\t')
+ last_bl = e_com;
+ *e_com++ = *t_ptr++;
+ }
+ }
+ *e_com = '\0';
+ now_col = count_spaces(ps.com_col, s_com); /* recompute current
+ * position */
+ }
+ break;
+ }
+ }
+}
diff --git a/usr.bin/ipcrm/Makefile b/usr.bin/ipcrm/Makefile
new file mode 100644
index 0000000..98fd8f8
--- /dev/null
+++ b/usr.bin/ipcrm/Makefile
@@ -0,0 +1,5 @@
+# $Id: Makefile,v 1.3.2.1 1994/08/09 13:47:15 mycroft Exp $
+
+PROG= ipcrm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ipcrm/ipcrm.1 b/usr.bin/ipcrm/ipcrm.1
new file mode 100644
index 0000000..19bb6e6
--- /dev/null
+++ b/usr.bin/ipcrm/ipcrm.1
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1994 Adam Glass
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the Author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Adam Glass BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ipcrm.1,v 1.1 1994/08/09 02:26:30 glass Exp $
+.\""
+.Dd August 8th, 1994
+.Dt ipcrm 1
+.Os
+.Sh NAME
+.Nm ipcrm
+.Nd remove the specified message queues, semaphore sets, and shared memory
+segments
+.Sh SYNOPSIS
+.Nm ipcrm
+.Op Fl q Ar msqid
+.Op Fl m Ar shmid
+.Op Fl s Ar semid
+.Op Fl Q Ar msgkey
+.Op Fl M Ar shmkey
+.Op Fl S Ar semkey
+.Ar ...
+.Sh DESCRIPTION
+.Nm ipcrm
+removes the specified message queues, semaphores and shared memory
+segments. These System V IPC objects can be specified by their
+creation id or any associated key.
+.Pp
+The following options are used to specify which IPC objects will be removed. Any number and combination of these options can be used:
+.Bl -tag -width indent
+.It Fl q Ar msqid
+Remove the message queue associated with the id
+.Nm msqid
+from the system.
+.It Fl m Ar shmid
+Mark the shared memory segment associated with id
+.Nm shmid
+for removal.
+This marked segment will be destroyed after the last detach.
+.It Fl s Ar semid
+Removes the semaphore set associated with id
+.Nm semid
+from the system.
+.It Fl Q Ar msgkey
+Remove the message queue associated with key
+.Nm msgkey
+from the system.
+.It Fl M Ar shmkey
+Mark the shared memory segment associated with key
+.Nm shmkey
+for removal.
+This marked segment will be destroyed after the last detach.
+.It Fl S Ar semkey
+Remove the semaphore set associated with key
+.Nm semkey
+from the system.
+.El
+.Pp
+The identifiers and keys associated with these System V IPC objects can be
+determined by using
+.Xr ipcs 1
+.
+.Sh SEE ALSO
+.Xr ipcs 1
diff --git a/usr.bin/ipcrm/ipcrm.c b/usr.bin/ipcrm/ipcrm.c
new file mode 100644
index 0000000..ff8a99e
--- /dev/null
+++ b/usr.bin/ipcrm/ipcrm.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1994 Adam Glass
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam Glass.
+ * 4. The name of the Author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Adam Glass BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ipcrm.c,v 1.1 1994/09/13 16:52:12 dfr Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+
+#define IPC_TO_STR(x) (x == 'Q' ? "msq" : (x == 'M' ? "shm" : "sem"))
+#define IPC_TO_STRING(x) (x == 'Q' ? "message queue" : \
+ (x == 'M' ? "shared memory segment" : "semaphore"))
+
+int signaled;
+
+void usage()
+{
+ fprintf(stderr, "usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]\n");
+ fprintf(stderr, " [-Q msgkey] [-M shmkey] [-S semkey] ...]\n");
+ exit(1);
+}
+
+int msgrm(key, id)
+ key_t key;
+ int id;
+{
+ if (key) {
+ id = msgget(key, 0);
+ if (id == -1)
+ return -1;
+ }
+ return msgctl(id, IPC_RMID, NULL);
+}
+
+int shmrm(key, id)
+ key_t key;
+ int id;
+{
+ if (key) {
+ id = shmget(key, 0, 0);
+ if (id == -1)
+ return -1;
+ }
+ return shmctl(id, IPC_RMID, NULL);
+}
+
+int semrm(key, id)
+ key_t key;
+ int id;
+{
+ union semun arg;
+
+ if (key) {
+ id = semget(key, 0, 0);
+ if (id == -1)
+ return -1;
+ }
+ return semctl(id, 0, IPC_RMID, arg);
+}
+
+void not_configured()
+{
+ signaled++;
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int c, result, errflg, target_id;
+ key_t target_key;
+
+ errflg = 0;
+ signal(SIGSYS, not_configured);
+ while ((c = getopt(argc, argv, ":q:m:s:Q:M:S:")) != -1) {
+
+ signaled = 0;
+ switch (c) {
+ case 'q':
+ case 'm':
+ case 's':
+ target_id = atoi(optarg);
+ if (c == 'q')
+ result = msgrm(0, target_id);
+ else if (c == 'm')
+ result = shmrm(0, target_id);
+ else
+ result = semrm(0, target_id);
+ if (result < 0) {
+ errflg++;
+ if (!signaled)
+ warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id);
+ else
+ warnx("%ss are not configured in the running kernel",
+ IPC_TO_STRING(toupper(c)));
+ }
+ break;
+ case 'Q':
+ case 'M':
+ case 'S':
+ target_key = atol(optarg);
+ if (target_key == IPC_PRIVATE) {
+ warnx("can't remove private %ss", IPC_TO_STRING(c));
+ continue;
+ }
+ if (c == 'Q')
+ result = msgrm(target_key, 0);
+ else if (c == 'M')
+ result = shmrm(target_key, 0);
+ else
+ result = semrm(target_key, 0);
+ if (result < 0) {
+ errflg++;
+ if (!signaled)
+ warn("%key(%ld): ", IPC_TO_STR(c), target_key);
+ else
+ warnx("%ss are not configured in the running kernel",
+ IPC_TO_STRING(c));
+ }
+ break;
+ case ':':
+ fprintf(stderr, "option -%c requires an argument\n", optopt);
+ usage();
+ case '?':
+ fprintf(stderr, "unrecognized option: -%c\n", optopt);
+ usage();
+ }
+ }
+
+ if (optind != argc) {
+ fprintf(stderr, "unknown argument: %s\n", argv[optind]);
+ usage();
+ }
+ exit(errflg);
+}
+
diff --git a/usr.bin/ipcs/Makefile b/usr.bin/ipcs/Makefile
new file mode 100644
index 0000000..eab4100
--- /dev/null
+++ b/usr.bin/ipcs/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.6 1994/06/18 21:09:40 cgd Exp $
+
+PROG= ipcs
+BINGRP= kmem
+BINMODE= 2555
+DPADD+= ${LIBKVM}
+LDADD+= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ipcs/ipcs.1 b/usr.bin/ipcs/ipcs.1
new file mode 100644
index 0000000..a6d0344
--- /dev/null
+++ b/usr.bin/ipcs/ipcs.1
@@ -0,0 +1,151 @@
+.\"
+.\" Copyright (c) 1994 SigmaSoft, Th. Lockert
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by SigmaSoft, Th. Lockert.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ipcs.1,v 1.1 1994/09/13 16:59:28 dfr Exp $
+.\"
+.Dd June 18, 1994
+.Dt "IPCS" 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm ipcs
+.Nd report System V interprocess communication facilities status
+.Sh SYNOPSIS
+.Nm ipcs
+.Op Fl abcmopqstMQST
+.Op Fl C Ar system
+.Op Fl N Ar core
+.Sh DESCRIPTION
+The
+.Nm ipcs
+program provides information on System V interprocess communication
+(IPC) facilities on the system.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Show the maximum amount of information possible when
+displaying active semaphores, message queues,
+and shared memory segments.
+(This is shorthand for specifying the
+.Fl b ,
+.Fl c ,
+.Fl o ,
+.Fl p ,
+and
+.Fl t
+options.)
+.It Fl b
+Show the maximum allowed sizes for active semaphores, message queues,
+and shared memory segments. The
+.Dq maximum allowed size
+is the maximum number of bytes in a message on a message queue,
+the size of a shared memory segment,
+or the number number of semaphores in a set of semaphores.
+.It Fl c
+Show the creator's name and group for active semaphores, message queues,
+and shared memory segments.
+.It Fl m
+Display information about active shared memory segments.
+.It Fl o
+Show outstanding usage for active message queues,
+and shared memory segments. The
+.Dq outstanding usage
+is the number of messages in a message queue, or the number
+of processes attached to a shared memory segment.
+.It Fl p
+Show the process ID information for active semaphores, message queues,
+and shared memory segments. The
+.Dq process ID information
+is the last process to send a message to or receive a message from
+a message queue,
+the process that created a semaphore, or the last process to attach
+or detach a shared memory segment.
+.It Fl q
+Display information about active message queues.
+.It Fl s
+Display information about active semaphores.
+.It Fl t
+Show access times for active semaphores, message queues,
+and shared memory segments. The access times is the time
+of the last control operation on an IPC object,
+the last send or receive of a message,
+the last attach or detach of a shared memory segment,
+or the last operation on a semaphore.
+.It Fl C Ar system
+Extract the name list from the specified system instead of the
+default
+.Dq Pa /kernel .
+.It Fl M
+Display system information about shared memory.
+.It Fl N Ar core
+Extract values associated with the name list from the specified
+core instead of the default
+.Dq Pa /dev/kmem .
+and semaphores.
+.It Fl Q
+Display system information about messages queues.
+.It Fl S
+Display system information about semaphores.
+.It Fl T
+Display system information about shared memory, message queues
+.El
+.Pp
+If none of the
+.Fl M ,
+.Fl m ,
+.Fl Q ,
+.Fl q ,
+.Fl S ,
+or
+.Fl s
+options are specified, information about all active IPC facilities is
+listed.
+.Sh RESTRICTIONS
+System data structures may change while
+.Nm ipcs
+is running; the output of
+.Nm ipcs
+is not guaranteed to be consistent.
+.Sh BUGS
+This manual page is woefully incomplete, because it does not
+at all attempt to explain the information printed by
+.Nm ipcs .
+.Sh FILES
+.Bl -tag -width /etc/passwd -compact
+.It Pa /dev/kmem
+default kernel memory
+.It Pa /kernel
+default system name list
+.El
+.Sh SEE ALSO
+.Xr ipcrm 1
+.Sh AUTHOR
+.Bl -tag
+Thorsten Lockert <tholo@sigmasoft.com>
+.El
diff --git a/usr.bin/ipcs/ipcs.c b/usr.bin/ipcs/ipcs.c
new file mode 100644
index 0000000..cd4e5cc
--- /dev/null
+++ b/usr.bin/ipcs/ipcs.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (c) 1994 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ipcs.c,v 1.3 1994/09/19 10:24:38 davidg Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <nlist.h>
+#include <kvm.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#define KERNEL
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/msg.h>
+
+struct semid_ds *sema;
+struct seminfo seminfo;
+struct msginfo msginfo;
+struct msqid_ds *msqids;
+struct shminfo shminfo;
+struct shmid_ds *shmsegs;
+
+int semconfig __P((int,...));
+void usage __P((void));
+
+static struct nlist symbols[] = {
+ {"_sema"},
+#define X_SEMA 0
+ {"_seminfo"},
+#define X_SEMINFO 1
+ {"_semu"},
+#define X_SEMU 2
+ {"_msginfo"},
+#define X_MSGINFO 3
+ {"_msqids"},
+#define X_MSQIDS 4
+ {"_shminfo"},
+#define X_SHMINFO 5
+ {"_shmsegs"},
+#define X_SHMSEGS 6
+ {NULL}
+};
+
+static kvm_t *kd;
+
+char *
+fmt_perm(mode)
+ u_short mode;
+{
+ static char buffer[100];
+
+ buffer[0] = '-';
+ buffer[1] = '-';
+ buffer[2] = ((mode & 0400) ? 'r' : '-');
+ buffer[3] = ((mode & 0200) ? 'w' : '-');
+ buffer[4] = ((mode & 0100) ? 'a' : '-');
+ buffer[5] = ((mode & 0040) ? 'r' : '-');
+ buffer[6] = ((mode & 0020) ? 'w' : '-');
+ buffer[7] = ((mode & 0010) ? 'a' : '-');
+ buffer[8] = ((mode & 0004) ? 'r' : '-');
+ buffer[9] = ((mode & 0002) ? 'w' : '-');
+ buffer[10] = ((mode & 0001) ? 'a' : '-');
+ buffer[11] = '\0';
+ return (&buffer[0]);
+}
+
+void
+cvt_time(t, buf)
+ time_t t;
+ char *buf;
+{
+ struct tm *tm;
+
+ if (t == 0) {
+ strcpy(buf, "no-entry");
+ } else {
+ tm = localtime(&t);
+ sprintf(buf, "%2d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+}
+#define SHMINFO 1
+#define SHMTOTAL 2
+#define MSGINFO 4
+#define MSGTOTAL 8
+#define SEMINFO 16
+#define SEMTOTAL 32
+
+#define BIGGEST 1
+#define CREATOR 2
+#define OUTSTANDING 4
+#define PID 8
+#define TIME 16
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int display = SHMINFO | MSGINFO | SEMINFO;
+ int option = 0;
+ char *core = NULL, *namelist = NULL;
+ int i;
+
+ while ((i = getopt(argc, argv, "MmQqSsabC:cN:optT")) != EOF)
+ switch (i) {
+ case 'M':
+ display = SHMTOTAL;
+ break;
+ case 'm':
+ display = SHMINFO;
+ break;
+ case 'Q':
+ display = MSGTOTAL;
+ break;
+ case 'q':
+ display = MSGINFO;
+ break;
+ case 'S':
+ display = SEMTOTAL;
+ break;
+ case 's':
+ display = SEMINFO;
+ break;
+ case 'T':
+ display = SHMTOTAL | MSGTOTAL | SEMTOTAL;
+ break;
+ case 'a':
+ option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME;
+ break;
+ case 'b':
+ option |= BIGGEST;
+ break;
+ case 'C':
+ core = optarg;
+ break;
+ case 'c':
+ option |= CREATOR;
+ break;
+ case 'N':
+ namelist = optarg;
+ break;
+ case 'o':
+ option |= OUTSTANDING;
+ break;
+ case 'p':
+ option |= PID;
+ break;
+ case 't':
+ option |= TIME;
+ break;
+ default:
+ usage();
+ }
+ if ((kd = kvm_open(namelist, core, NULL, O_RDONLY, "ipcs")) == NULL)
+ exit(1);
+
+ switch (kvm_nlist(kd, symbols)) {
+ case 0:
+ break;
+ case -1:
+ errx(1, "unable to read kernel symbol table.");
+ default:
+#ifdef notdef /* they'll be told more civilly later */
+ warnx("nlist failed");
+ for (i = 0; symbols[i].n_name != NULL; i++)
+ if (symbols[i].n_value == 0)
+ warnx("symbol %s not found",
+ symbols[i].n_name);
+ break;
+#endif
+ }
+
+ if ((display & (MSGINFO | MSGTOTAL)) &&
+ kvm_read(kd, symbols[X_MSGINFO].n_value, &msginfo, sizeof(msginfo))) {
+
+ if (display & MSGTOTAL) {
+ printf("msginfo:\n");
+ printf("\tmsgmax: %6d\t(max characters in a message)\n",
+ msginfo.msgmax);
+ printf("\tmsgmni: %6d\t(# of message queues)\n",
+ msginfo.msgmni);
+ printf("\tmsgmnb: %6d\t(max characters in a message queue)\n",
+ msginfo.msgmnb);
+ printf("\tmsgtql: %6d\t(max # of messages in system)\n",
+ msginfo.msgtql);
+ printf("\tmsgssz: %6d\t(size of a message segment)\n",
+ msginfo.msgssz);
+ printf("\tmsgseg: %6d\t(# of message segments in system)\n\n",
+ msginfo.msgseg);
+ }
+ if (display & MSGINFO) {
+ struct msqid_ds *xmsqids;
+
+ kvm_read(kd, symbols[X_MSQIDS].n_value, &msqids, sizeof(msqids));
+ xmsqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni);
+ kvm_read(kd, (u_long) msqids, xmsqids, sizeof(struct msqid_ds) * msginfo.msgmni);
+
+ printf("Message Queues:\n");
+ printf("T ID KEY MODE OWNER GROUP");
+ if (option & CREATOR)
+ printf(" CREATOR CGROUP");
+ if (option & OUTSTANDING)
+ printf(" CBYTES QNUM");
+ if (option & BIGGEST)
+ printf(" QBYTES");
+ if (option & PID)
+ printf(" LSPID LRPID");
+ if (option & TIME)
+ printf(" STIME RTIME CTIME");
+ printf("\n");
+ for (i = 0; i < msginfo.msgmni; i += 1) {
+ if (xmsqids[i].msg_qbytes != 0) {
+ char stime_buf[100], rtime_buf[100],
+ ctime_buf[100];
+ struct msqid_ds *msqptr = &xmsqids[i];
+
+ cvt_time(msqptr->msg_stime, stime_buf);
+ cvt_time(msqptr->msg_rtime, rtime_buf);
+ cvt_time(msqptr->msg_ctime, ctime_buf);
+
+ printf("q %6d %10d %s %8s %8s",
+ IXSEQ_TO_IPCID(i, msqptr->msg_perm),
+ msqptr->msg_perm.key,
+ fmt_perm(msqptr->msg_perm.mode),
+ user_from_uid(msqptr->msg_perm.uid, 0),
+ group_from_gid(msqptr->msg_perm.gid, 0));
+
+ if (option & CREATOR)
+ printf(" %8s %8s",
+ user_from_uid(msqptr->msg_perm.cuid, 0),
+ group_from_gid(msqptr->msg_perm.cgid, 0));
+
+ if (option & OUTSTANDING)
+ printf(" %6d %6d",
+ msqptr->msg_cbytes,
+ msqptr->msg_qnum);
+
+ if (option & BIGGEST)
+ printf(" %6d",
+ msqptr->msg_qbytes);
+
+ if (option & PID)
+ printf(" %6d %6d",
+ msqptr->msg_lspid,
+ msqptr->msg_lrpid);
+
+ if (option & TIME)
+ printf("%s %s %s",
+ stime_buf,
+ rtime_buf,
+ ctime_buf);
+
+ printf("\n");
+ }
+ }
+ printf("\n");
+ }
+ } else
+ if (display & (MSGINFO | MSGTOTAL)) {
+ fprintf(stderr,
+ "SVID messages facility not configured in the system\n");
+ }
+ if ((display & (SHMINFO | SHMTOTAL)) &&
+ kvm_read(kd, symbols[X_SHMINFO].n_value, &shminfo, sizeof(shminfo))) {
+ if (display & SHMTOTAL) {
+ printf("shminfo:\n");
+ printf("\tshmmax: %7d\t(max shared memory segment size)\n",
+ shminfo.shmmax);
+ printf("\tshmmin: %7d\t(min shared memory segment size)\n",
+ shminfo.shmmin);
+ printf("\tshmmni: %7d\t(max number of shared memory identifiers)\n",
+ shminfo.shmmni);
+ printf("\tshmseg: %7d\t(max shared memory segments per process)\n",
+ shminfo.shmseg);
+ printf("\tshmall: %7d\t(max amount of shared memory in pages)\n\n",
+ shminfo.shmall);
+ }
+ if (display & SHMINFO) {
+ struct shmid_ds *xshmids;
+
+ kvm_read(kd, symbols[X_SHMSEGS].n_value, &shmsegs, sizeof(shmsegs));
+ xshmids = malloc(sizeof(struct shmid_ds) * msginfo.msgmni);
+ kvm_read(kd, (u_long) shmsegs, xshmids, sizeof(struct shmid_ds) *
+ shminfo.shmmni);
+
+ printf("Shared Memory:\n");
+ printf("T ID KEY MODE OWNER GROUP");
+ if (option & CREATOR)
+ printf(" CREATOR CGROUP");
+ if (option & OUTSTANDING)
+ printf(" NATTCH");
+ if (option & BIGGEST)
+ printf(" SEGSZ");
+ if (option & PID)
+ printf(" CPID LPID");
+ if (option & TIME)
+ printf(" ATIME DTIME CTIME");
+ printf("\n");
+ for (i = 0; i < shminfo.shmmni; i += 1) {
+ if (xshmids[i].shm_perm.mode & 0x0800) {
+ char atime_buf[100], dtime_buf[100],
+ ctime_buf[100];
+ struct shmid_ds *shmptr = &xshmids[i];
+
+ cvt_time(shmptr->shm_atime, atime_buf);
+ cvt_time(shmptr->shm_dtime, dtime_buf);
+ cvt_time(shmptr->shm_ctime, ctime_buf);
+
+ printf("m %6d %10d %s %8s %8s",
+ IXSEQ_TO_IPCID(i, shmptr->shm_perm),
+ shmptr->shm_perm.key,
+ fmt_perm(shmptr->shm_perm.mode),
+ user_from_uid(shmptr->shm_perm.uid, 0),
+ group_from_gid(shmptr->shm_perm.gid, 0));
+
+ if (option & CREATOR)
+ printf(" %8s %8s",
+ user_from_uid(shmptr->shm_perm.cuid, 0),
+ group_from_gid(shmptr->shm_perm.cgid, 0));
+
+ if (option & OUTSTANDING)
+ printf(" %6d",
+ shmptr->shm_nattch);
+
+ if (option & BIGGEST)
+ printf(" %6d",
+ shmptr->shm_segsz);
+
+ if (option & PID)
+ printf(" %6d %6d",
+ shmptr->shm_cpid,
+ shmptr->shm_lpid);
+
+ if (option & TIME)
+ printf("%s %s %s",
+ atime_buf,
+ dtime_buf,
+ ctime_buf);
+
+ printf("\n");
+ }
+ }
+ printf("\n");
+ }
+ } else
+ if (display & (SHMINFO | SHMTOTAL)) {
+ fprintf(stderr,
+ "SVID shared memory facility not configured in the system\n");
+ }
+ if ((display & (SEMINFO | SEMTOTAL)) &&
+ kvm_read(kd, symbols[X_SEMINFO].n_value, &seminfo, sizeof(seminfo))) {
+ struct semid_ds *xsema;
+
+ if (display & SEMTOTAL) {
+ printf("seminfo:\n");
+ printf("\tsemmap: %6d\t(# of entries in semaphore map)\n",
+ seminfo.semmap);
+ printf("\tsemmni: %6d\t(# of semaphore identifiers)\n",
+ seminfo.semmni);
+ printf("\tsemmns: %6d\t(# of semaphores in system)\n",
+ seminfo.semmns);
+ printf("\tsemmnu: %6d\t(# of undo structures in system)\n",
+ seminfo.semmnu);
+ printf("\tsemmsl: %6d\t(max # of semaphores per id)\n",
+ seminfo.semmsl);
+ printf("\tsemopm: %6d\t(max # of operations per semop call)\n",
+ seminfo.semopm);
+ printf("\tsemume: %6d\t(max # of undo entries per process)\n",
+ seminfo.semume);
+ printf("\tsemusz: %6d\t(size in bytes of undo structure)\n",
+ seminfo.semusz);
+ printf("\tsemvmx: %6d\t(semaphore maximum value)\n",
+ seminfo.semvmx);
+ printf("\tsemaem: %6d\t(adjust on exit max value)\n\n",
+ seminfo.semaem);
+ }
+ if (display & SEMINFO) {
+ if (semconfig(SEM_CONFIG_FREEZE) != 0) {
+ perror("semconfig");
+ fprintf(stderr,
+ "Can't lock semaphore facility - winging it...\n");
+ }
+ kvm_read(kd, symbols[X_SEMA].n_value, &sema, sizeof(sema));
+ xsema = malloc(sizeof(struct semid_ds) * seminfo.semmni);
+ kvm_read(kd, (u_long) sema, xsema, sizeof(struct semid_ds) * seminfo.semmni);
+
+ printf("Semaphores:\n");
+ printf("T ID KEY MODE OWNER GROUP");
+ if (option & CREATOR)
+ printf(" CREATOR CGROUP");
+ if (option & BIGGEST)
+ printf(" NSEMS");
+ if (option & TIME)
+ printf(" OTIME CTIME");
+ printf("\n");
+ for (i = 0; i < seminfo.semmni; i += 1) {
+ if ((xsema[i].sem_perm.mode & SEM_ALLOC) != 0) {
+ char ctime_buf[100], otime_buf[100];
+ struct semid_ds *semaptr = &xsema[i];
+ int j, value;
+ union semun junk;
+
+ cvt_time(semaptr->sem_otime, otime_buf);
+ cvt_time(semaptr->sem_ctime, ctime_buf);
+
+ printf("s %6d %10d %s %8s %8s",
+ IXSEQ_TO_IPCID(i, semaptr->sem_perm),
+ semaptr->sem_perm.key,
+ fmt_perm(semaptr->sem_perm.mode),
+ user_from_uid(semaptr->sem_perm.uid, 0),
+ group_from_gid(semaptr->sem_perm.gid, 0));
+
+ if (option & CREATOR)
+ printf(" %8s %8s",
+ user_from_uid(semaptr->sem_perm.cuid, 0),
+ group_from_gid(semaptr->sem_perm.cgid, 0));
+
+ if (option & BIGGEST)
+ printf(" %6d",
+ semaptr->sem_nsems);
+
+ if (option & TIME)
+ printf("%s %s",
+ otime_buf,
+ ctime_buf);
+
+ printf("\n");
+ }
+ }
+
+ (void) semconfig(SEM_CONFIG_THAW);
+
+ printf("\n");
+ }
+ } else
+ if (display & (SEMINFO | SEMTOTAL)) {
+ fprintf(stderr, "SVID semaphores facility not configured in the system\n");
+ }
+ kvm_close(kd);
+
+ exit(0);
+}
+
+void
+usage()
+{
+
+ fprintf(stderr,
+ "usage: ipcs [-abcmopqst] [-C corefile] [-N namelist]\n");
+ exit(1);
+}
diff --git a/usr.bin/join/Makefile b/usr.bin/join/Makefile
new file mode 100644
index 0000000..3fa8629
--- /dev/null
+++ b/usr.bin/join/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= join
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/join/join.1 b/usr.bin/join/join.1
new file mode 100644
index 0000000..fa383c6
--- /dev/null
+++ b/usr.bin/join/join.1
@@ -0,0 +1,206 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)join.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt JOIN 1
+.Os
+.Sh NAME
+.Nm join
+.Nd relational database operator
+.Sh SYNOPSIS
+.Nm join
+.Oo
+.Fl a Ar file_number | Fl v Ar file_number
+.Oc
+.Op Fl e Ar string
+.Op Fl j Ar file_number field
+.Op Fl o Ar list
+.Bk -words
+.Ek
+.Op Fl t Ar char
+.Op Fl \&1 Ar field
+.Op Fl \&2 Ar field
+.Ar file1
+.Ar file2
+.Sh DESCRIPTION
+The join utility performs an ``equality join'' on the specified files
+and writes the result to the standard output.
+The ``join field'' is the field in each file by which the files are compared.
+The first field in each line is used by default.
+There is one line in the output for each pair of lines in
+.Ar file1
+and
+.Ar file2
+which have identical join fields.
+Each output line consists of the join field, the remaining fields from
+.Ar file1
+and then the remaining fields from
+.Ar file2 .
+.Pp
+The default field separators are tab and space characters.
+In this case, multiple tabs and spaces count as a single field separator,
+and leading tabs and spaces are ignored.
+The default output field separator is a single space character.
+.Pp
+Many of the options use file and field numbers.
+Both file numbers and field numbers are 1 based, i.e. the first file on
+the command line is file number 1 and the first field is field number 1.
+The following options are available:
+.Bl -tag -width Fl
+.It Fl a Ar file_number
+In addition to the default output, produce a line for each unpairable
+line in file
+.Ar file_number .
+.It Fl e Ar string
+Replace empty output fields with
+.Ar string .
+.It Fl o Ar list
+The
+.Fl o
+option specifies the fields that will be output from each file for
+each line with matching join fields.
+Each element of
+.Ar list
+has the form
+.Ql file_number.field ,
+where
+.Ar file_number
+is a file number and
+.Ar field
+is a field number.
+The elements of list must be either comma (``,'') or whitespace separated.
+(The latter requires quoting to protect it from the shell, or, a simpler
+approach is to use multiple
+.Fl o
+options.)
+.It Fl t Ar char
+Use character
+.Ar char
+as a field delimiter for both input and output.
+Every occurrence of
+.Ar char
+in a line is significant.
+.It Fl v Ar file_number
+Do not display the default output, but display a line for each unpairable
+line in file
+.Ar file_number .
+The options
+.Fl v Ar 1
+and
+.Fl v Ar 2
+may be specified at the same time.
+.It Fl 1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl 2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.El
+.Pp
+When the default field delimiter characters are used, the files to be joined
+should be ordered in the collating sequence of
+.Xr sort 1 ,
+using the
+.Fl b
+option, on the fields on which they are to be joined, otherwise
+.Nm join
+may not report all field matches.
+When the field delimiter characters are specified by the
+.Fl t
+option, the collating sequence should be the same as
+.Xr sort
+without the
+.Fl b
+option.
+.Pp
+If one of the arguments
+.Ar file1
+or
+.Ar file2
+is ``-'', the standard input is used.
+.Pp
+The
+.Nm join
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+For compatibility with historic versions of
+.Nm join ,
+the following options are available:
+.Bl -tag -width Fl
+.It Fl a
+In addition to the default output, produce a line for each unpairable line
+in both file 1 and file 2.
+.It Fl j1 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 1.
+.It Fl j2 Ar field
+Join on the
+.Ar field Ns 'th
+field of file 2.
+.It Fl j Ar field
+Join on the
+.Ar field Ns 'th
+field of both file 1 and file 2.
+.It Fl o Ar list ...
+Historical implementations of
+.Nm join
+permitted multiple arguments to the
+.Fl o
+option.
+These arguments were of the form ``file_number.field_number'' as described
+for the current
+.Fl o
+option.
+This has obvious difficulties in the presence of files named ``1.2''.
+.El
+.Pp
+These options are available only so historic shellscripts don't require
+modification and should not be used.
+.Sh STANDARDS
+The
+.Nm join
+command is expected to be
+.St -p1003.2
+compatible.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr comm 1 ,
+.Xr paste 1 ,
+.Xr sort 1 ,
+.Xr uniq 1
diff --git a/usr.bin/join/join.c b/usr.bin/join/join.c
new file mode 100644
index 0000000..d717835
--- /dev/null
+++ b/usr.bin/join/join.c
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Steve Hayman of the Computer Science Department, Indiana University,
+ * Michiro Hikida and David Goodenough.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)join.c 8.3 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * There's a structure per input file which encapsulates the state of the
+ * file. We repeatedly read lines from each file until we've read in all
+ * the consecutive lines from the file with a common join field. Then we
+ * compare the set of lines with an equivalent set from the other file.
+ */
+typedef struct {
+ char *line; /* line */
+ u_long linealloc; /* line allocated count */
+ char **fields; /* line field(s) */
+ u_long fieldcnt; /* line field(s) count */
+ u_long fieldalloc; /* line field(s) allocated count */
+} LINE;
+
+typedef struct {
+ FILE *fp; /* file descriptor */
+ u_long joinf; /* join field (-1, -2, -j) */
+ int unpair; /* output unpairable lines (-a) */
+ int number; /* 1 for file 1, 2 for file 2 */
+
+ LINE *set; /* set of lines with same field */
+ int pushbool; /* if pushback is set */
+ u_long pushback; /* line on the stack */
+ u_long setcnt; /* set count */
+ u_long setalloc; /* set allocated count */
+} INPUT;
+INPUT input1 = { NULL, 0, 0, 1, NULL, 0, 0, 0, },
+ input2 = { NULL, 0, 0, 2, NULL, 0, 0, 0, };
+
+typedef struct {
+ u_long filenum; /* file number */
+ u_long fieldno; /* field number */
+} OLIST;
+OLIST *olist; /* output field list */
+u_long olistcnt; /* output field list count */
+u_long olistalloc; /* output field allocated count */
+
+int joinout = 1; /* show lines with matched join fields (-v) */
+int needsep; /* need separator character */
+int spans = 1; /* span multiple delimiters (-t) */
+char *empty; /* empty field replacement string (-e) */
+char *tabchar = " \t"; /* delimiter characters (-t) */
+
+int cmp __P((LINE *, u_long, LINE *, u_long));
+void fieldarg __P((char *));
+void joinlines __P((INPUT *, INPUT *));
+void obsolete __P((char **));
+void outfield __P((LINE *, u_long, int));
+void outoneline __P((INPUT *, LINE *));
+void outtwoline __P((INPUT *, LINE *, INPUT *, LINE *));
+void slurp __P((INPUT *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ INPUT *F1, *F2;
+ int aflag, ch, cval, vflag;
+ char *end;
+
+ F1 = &input1;
+ F2 = &input2;
+
+ aflag = vflag = 0;
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != EOF) {
+ switch (ch) {
+ case '\01': /* See comment in obsolete(). */
+ aflag = 1;
+ F1->unpair = F2->unpair = 1;
+ break;
+ case '1':
+ if ((F1->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-1 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ break;
+ case '2':
+ if ((F2->joinf = strtol(optarg, &end, 10)) < 1)
+ errx(1, "-2 option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F2->joinf;
+ break;
+ case 'a':
+ aflag = 1;
+ switch(strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-a option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case 'e':
+ empty = optarg;
+ break;
+ case 'j':
+ if ((F1->joinf = F2->joinf =
+ strtol(optarg, &end, 10)) < 1)
+ errx(1, "-j option field number less than 1");
+ if (*end)
+ errx(1, "illegal field number -- %s", optarg);
+ --F1->joinf;
+ --F2->joinf;
+ break;
+ case 'o':
+ fieldarg(optarg);
+ break;
+ case 't':
+ spans = 0;
+ if (strlen(tabchar = optarg) != 1)
+ errx(1, "illegal tab character specification");
+ break;
+ case 'v':
+ vflag = 1;
+ joinout = 0;
+ switch (strtol(optarg, &end, 10)) {
+ case 1:
+ F1->unpair = 1;
+ break;
+ case 2:
+ F2->unpair = 1;
+ break;
+ default:
+ errx(1, "-v option file number not 1 or 2");
+ break;
+ }
+ if (*end)
+ errx(1, "illegal file number -- %s", optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag && vflag)
+ errx(1, "the -a and -v options are mutually exclusive");
+
+ if (argc != 2)
+ usage();
+
+ /* Open the files; "-" means stdin. */
+ if (!strcmp(*argv, "-"))
+ F1->fp = stdin;
+ else if ((F1->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ ++argv;
+ if (!strcmp(*argv, "-"))
+ F2->fp = stdin;
+ else if ((F2->fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ if (F1->fp == stdin && F2->fp == stdin)
+ errx(1, "only one input file may be stdin");
+
+ slurp(F1);
+ slurp(F2);
+ while (F1->setcnt && F2->setcnt) {
+ cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf);
+ if (cval == 0) {
+ /* Oh joy, oh rapture, oh beauty divine! */
+ if (joinout)
+ joinlines(F1, F2);
+ slurp(F1);
+ slurp(F2);
+ } else if (cval < 0) {
+ /* File 1 takes the lead... */
+ if (F1->unpair)
+ joinlines(F1, NULL);
+ slurp(F1);
+ } else {
+ /* File 2 takes the lead... */
+ if (F2->unpair)
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ }
+
+ /*
+ * Now that one of the files is used up, optionally output any
+ * remaining lines from the other file.
+ */
+ if (F1->unpair)
+ while (F1->setcnt) {
+ joinlines(F1, NULL);
+ slurp(F1);
+ }
+ if (F2->unpair)
+ while (F2->setcnt) {
+ joinlines(F2, NULL);
+ slurp(F2);
+ }
+ exit(0);
+}
+
+void
+slurp(F)
+ INPUT *F;
+{
+ LINE *lp, *lastlp, tmp;
+ size_t len;
+ int cnt;
+ char *bp, *fieldp;
+
+ /*
+ * Read all of the lines from an input file that have the same
+ * join field.
+ */
+ F->setcnt = 0;
+ for (lastlp = NULL;; ++F->setcnt, lastlp = lp) {
+ /*
+ * If we're out of space to hold line structures, allocate
+ * more. Initialize the structure so that we know that this
+ * is new space.
+ */
+ if (F->setcnt == F->setalloc) {
+ cnt = F->setalloc;
+ F->setalloc += 50;
+ if ((F->set = realloc(F->set,
+ F->setalloc * sizeof(LINE))) == NULL)
+ err(1, NULL);
+ memset(F->set + cnt, 0, 50 * sizeof(LINE));
+ }
+
+ /*
+ * Get any pushed back line, else get the next line. Allocate
+ * space as necessary. If taking the line from the stack swap
+ * the two structures so that we don't lose space allocated to
+ * either structure. This could be avoided by doing another
+ * level of indirection, but it's probably okay as is.
+ * but it's probably okay as is.
+ */
+ lp = &F->set[F->setcnt];
+ if (F->pushbool) {
+ tmp = F->set[F->setcnt];
+ F->set[F->setcnt] = F->set[F->pushback];
+ F->set[F->pushback] = tmp;
+ F->pushbool = 0;
+ continue;
+ }
+ if ((bp = fgetln(F->fp, &len)) == NULL)
+ return;
+ if (lp->linealloc <= len + 1) {
+ lp->linealloc += MAX(100, len + 1);
+ if ((lp->line =
+ realloc(lp->line, lp->linealloc)) == NULL)
+ err(1, NULL);
+ }
+ memmove(lp->line, bp, len);
+
+ /* Replace trailing newline, if it exists. */
+ if (bp[len - 1] == '\n')
+ lp->line[len - 1] = '\0';
+ else
+ lp->line[len] = '\0';
+ bp = lp->line;
+
+ /* Split the line into fields, allocate space as necessary. */
+ lp->fieldcnt = 0;
+ while ((fieldp = strsep(&bp, tabchar)) != NULL) {
+ if (spans && *fieldp == '\0')
+ continue;
+ if (lp->fieldcnt == lp->fieldalloc) {
+ lp->fieldalloc += 50;
+ if ((lp->fields = realloc(lp->fields,
+ lp->fieldalloc * sizeof(char *))) == NULL)
+ err(1, NULL);
+ }
+ lp->fields[lp->fieldcnt++] = fieldp;
+ }
+
+ /* See if the join field value has changed. */
+ if (lastlp != NULL && cmp(lp, F->joinf, lastlp, F->joinf)) {
+ F->pushbool = 1;
+ F->pushback = F->setcnt;
+ break;
+ }
+ }
+}
+
+int
+cmp(lp1, fieldno1, lp2, fieldno2)
+ LINE *lp1, *lp2;
+ u_long fieldno1, fieldno2;
+{
+ if (lp1->fieldcnt < fieldno1)
+ return (lp2->fieldcnt < fieldno2 ? 0 : 1);
+ if (lp2->fieldcnt < fieldno2)
+ return (-1);
+ return (strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2]));
+}
+
+void
+joinlines(F1, F2)
+ INPUT *F1, *F2;
+{
+ int cnt1, cnt2;
+
+ /*
+ * Output the results of a join comparison. The output may be from
+ * either file 1 or file 2 (in which case the first argument is the
+ * file from which to output) or from both.
+ */
+ if (F2 == NULL) {
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ outoneline(F1, &F1->set[cnt1]);
+ return;
+ }
+ for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1)
+ for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2)
+ outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]);
+}
+
+void
+outoneline(F, lp)
+ INPUT *F;
+ LINE *lp;
+{
+ int cnt;
+
+ /*
+ * Output a single line from one of the files, according to the
+ * join rules. This happens when we are writing unmatched single
+ * lines. Output empty fields in the right places.
+ */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt) {
+ if (olist[cnt].filenum == F->number)
+ outfield(lp, olist[cnt].fieldno, 0);
+ else
+ outfield(lp, 0, 1);
+ }
+ else
+ for (cnt = 0; cnt < lp->fieldcnt; ++cnt)
+ outfield(lp, cnt, 0);
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outtwoline(F1, lp1, F2, lp2)
+ INPUT *F1, *F2;
+ LINE *lp1, *lp2;
+{
+ int cnt;
+
+ /* Output a pair of lines according to the join list (if any). */
+ if (olist)
+ for (cnt = 0; cnt < olistcnt; ++cnt)
+ if (olist[cnt].filenum == 1)
+ outfield(lp1, olist[cnt].fieldno, 0);
+ else /* if (olist[cnt].filenum == 2) */
+ outfield(lp2, olist[cnt].fieldno, 0);
+ else {
+ /*
+ * Output the join field, then the remaining fields from F1
+ * and F2.
+ */
+ outfield(lp1, F1->joinf, 0);
+ for (cnt = 0; cnt < lp1->fieldcnt; ++cnt)
+ if (F1->joinf != cnt)
+ outfield(lp1, cnt, 0);
+ for (cnt = 0; cnt < lp2->fieldcnt; ++cnt)
+ if (F2->joinf != cnt)
+ outfield(lp2, cnt, 0);
+ }
+ (void)printf("\n");
+ if (ferror(stdout))
+ err(1, "stdout");
+ needsep = 0;
+}
+
+void
+outfield(lp, fieldno, out_empty)
+ LINE *lp;
+ u_long fieldno;
+ int out_empty;
+{
+ if (needsep++)
+ (void)printf("%c", *tabchar);
+ if (!ferror(stdout))
+ if (lp->fieldcnt < fieldno || out_empty) {
+ if (empty != NULL)
+ (void)printf("%s", empty);
+ } else {
+ if (*lp->fields[fieldno] == '\0')
+ return;
+ (void)printf("%s", lp->fields[fieldno]);
+ }
+ if (ferror(stdout))
+ err(1, "stdout");
+}
+
+/*
+ * Convert an output list argument "2.1, 1.3, 2.4" into an array of output
+ * fields.
+ */
+void
+fieldarg(option)
+ char *option;
+{
+ u_long fieldno;
+ char *end, *token;
+
+ while ((token = strsep(&option, " \t")) != NULL) {
+ if (*token == '\0')
+ continue;
+ if (token[0] != '1' && token[0] != '2' || token[1] != '.')
+ errx(1, "malformed -o option field");
+ fieldno = strtol(token + 2, &end, 10);
+ if (*end)
+ errx(1, "malformed -o option field");
+ if (fieldno == 0)
+ errx(1, "field numbers are 1 based");
+ if (olistcnt == olistalloc) {
+ olistalloc += 50;
+ if ((olist = realloc(olist,
+ olistalloc * sizeof(OLIST))) == NULL)
+ err(1, NULL);
+ }
+ olist[olistcnt].filenum = token[0] - '0';
+ olist[olistcnt].fieldno = fieldno - 1;
+ ++olistcnt;
+ }
+}
+
+void
+obsolete(argv)
+ char **argv;
+{
+ int len;
+ char **p, *ap, *t;
+
+ while ((ap = *++argv) != NULL) {
+ /* Return if "--". */
+ if (ap[0] == '-' && ap[1] == '-')
+ return;
+ switch (ap[1]) {
+ case 'a':
+ /*
+ * The original join allowed "-a", which meant the
+ * same as -a1 plus -a2. POSIX 1003.2, Draft 11.2
+ * only specifies this as "-a 1" and "a -2", so we
+ * have to use another option flag, one that is
+ * unlikely to ever be used or accidentally entered
+ * on the command line. (Well, we could reallocate
+ * the argv array, but that hardly seems worthwhile.)
+ */
+ if (ap[2] == '\0')
+ ap[1] = '\01';
+ break;
+ case 'j':
+ /*
+ * The original join allowed "-j[12] arg" and "-j arg".
+ * Convert the former to "-[12] arg". Don't convert
+ * the latter since getopt(3) can handle it.
+ */
+ switch(ap[2]) {
+ case '1':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '1';
+ ap[2] = '\0';
+ break;
+ case '2':
+ if (ap[3] != '\0')
+ goto jbad;
+ ap[1] = '2';
+ ap[2] = '\0';
+ break;
+ case '\0':
+ break;
+ default:
+jbad: errx(1, "illegal option -- %s", ap);
+ usage();
+ }
+ break;
+ case 'o':
+ /*
+ * The original join allowed "-o arg arg".
+ * Convert to "-o arg -o arg".
+ */
+ if (ap[2] != '\0')
+ break;
+ for (p = argv + 2; *p; ++p) {
+ if (p[0][0] != '1' &&
+ p[0][0] != '2' || p[0][1] != '.')
+ break;
+ len = strlen(*p);
+ if (len - 2 != strspn(*p + 2, "0123456789"))
+ break;
+ if ((t = malloc(len + 3)) == NULL)
+ err(1, NULL);
+ t[0] = '-';
+ t[1] = 'o';
+ memmove(t + 2, *p, len + 1);
+ *p = t;
+ }
+ argv = p - 1;
+ break;
+ }
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s%s\n",
+ "usage: join [-a fileno | -v fileno ] [-e string] [-1 field] ",
+ "[-2 field]\n [-o list] [-t char] file1 file2");
+ exit(1);
+}
diff --git a/usr.bin/jot/Makefile b/usr.bin/jot/Makefile
new file mode 100644
index 0000000..83db4c0
--- /dev/null
+++ b/usr.bin/jot/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= jot
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/jot/jot.1 b/usr.bin/jot/jot.1
new file mode 100644
index 0000000..c7dba58
--- /dev/null
+++ b/usr.bin/jot/jot.1
@@ -0,0 +1,195 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)jot.1 8.1 (Berkeley) 6/6/93
+.\"
+.TH JOT 1 "June 6, 1993"
+.UC 4
+.SH NAME
+jot \- print sequential or random data
+.SH SYNOPSIS
+.B jot [
+options
+.B ] [
+\fRreps \fB[\fP begin \fB[\fP end \fB[\fP s \fB] ] ] ]\fP
+.SH DESCRIPTION
+.I Jot
+is used to print out increasing, decreasing, random,
+or redundant data, usually numbers, one per line.
+The
+.I options
+are understood as follows.
+.IP \fB\-r\fP
+Generate random data instead of sequential data, the default.
+.IP \fB\-b\fP\ word
+Just print
+.I word
+repetitively.
+.IP \fB\-w\fP\ word
+Print
+.IR word
+with the generated data appended to it.
+Octal, hexadecimal, exponential, ASCII, zero padded,
+and right-adjusted representations
+are possible by using the appropriate
+.IR printf (3)
+conversion specification inside
+.IR word ,
+in which case the data are inserted rather than appended.
+.IP \fB\-c\fP
+This is an abbreviation for \fB\-w %c\fP.
+.IP \fB\-s\fP\ string
+Print data separated by
+.IR string .
+Normally, newlines separate data.
+.IP \fB\-n\fP
+Do not print the final newline normally appended to the output.
+.IP \fB\-p\fP\ precision
+Print only as many digits or characters of the data
+as indicated by the integer
+.IR precision .
+In the absence of
+.BR \-p ,
+the precision is the greater of the precisions of
+.I begin
+and
+.IR end .
+The
+.B \-p
+option is overridden by whatever appears in a
+.IR printf (3)
+conversion following
+.BR \-w .
+.PP
+The last four arguments indicate, respectively,
+the number of data, the lower bound, the upper bound,
+and the step size or, for random data, the seed.
+While at least one of them must appear,
+any of the other three may be omitted, and
+will be considered as such if given as
+.BR \- .
+Any three of these arguments determines the fourth.
+If four are specified and the given and computed values of
+.I reps
+conflict, the lower value is used.
+If fewer than three are specified, defaults are assigned
+left to right, except for
+.IR s ,
+which assumes its default unless both
+.I begin
+and
+.I end
+are given.
+.PP
+Defaults for the four arguments are, respectively,
+100, 1, 100, and 1, except that when random data are requested,
+.I s
+defaults to a seed depending upon the time of day.
+.I Reps
+is expected to be an unsigned integer,
+and if given as zero is taken to be infinite.
+.I Begin
+and
+.I end
+may be given as real numbers or as characters
+representing the corresponding value in ASCII.
+The last argument must be a real number.
+.PP
+Random numbers are obtained through
+.IR random (3).
+The name
+.I jot
+derives in part from
+.IR iota ,
+a function in APL.
+.SH EXAMPLES
+.de IC
+.IP
+.ss 36
+.ft B
+..
+.de NC
+.br
+.ss 12
+.PP
+..
+.PP
+The command
+.IC
+jot 21 \-1 1.00
+.NC
+prints 21 evenly spaced numbers increasing from \-1 to 1.
+The ASCII character set is generated with
+.IC
+jot \-c 128 0
+.NC
+and the strings xaa through xaz with
+.IC
+jot \-w xa%c 26 a
+.NC
+while 20 random 8-letter strings are produced with
+.IC
+jot \-r \-c 160 a z | rs \-g 0 8
+.NC
+Infinitely many
+.IR yes 's
+may be obtained through
+.IC
+jot \-b yes 0
+.NC
+and thirty
+.IR ed (1)
+substitution commands applying to lines 2, 7, 12, etc. is
+the result of
+.IC
+jot \-w %ds/old/new/ 30 2 \- 5
+.NC
+The stuttering sequence 9, 9, 8, 8, 7, etc. can be
+produced by suitable choice of precision and step size,
+as in
+.IC
+jot 0 9 \- \-.5
+.NC
+and a file containing exactly 1024 bytes is created with
+.IC
+jot \-b x 512 > block
+.NC
+Finally, to set tabs four spaces apart starting
+from column 10 and ending in column 132, use
+.IC
+expand \-\`jot \-s, \- 10 132 4\`
+.NC
+and to print all lines 80 characters or longer,
+.IC
+grep \`jot \-s "" \-b . 80\`
+.NC
+.SH SEE ALSO
+ed(1), expand(1), rs(1), yes(1), printf(3), random(3), expand(1)
diff --git a/usr.bin/jot/jot.c b/usr.bin/jot/jot.c
new file mode 100644
index 0000000..442d083
--- /dev/null
+++ b/usr.bin/jot/jot.c
@@ -0,0 +1,393 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * jot - print sequential or random data
+ *
+ * Author: John Kunze, Office of Comp. Affairs, UCB
+ */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define REPS_DEF 100
+#define BEGIN_DEF 1
+#define ENDER_DEF 100
+#define STEP_DEF 1
+
+#define isdefault(s) (strcmp((s), "-") == 0)
+
+double begin;
+double ender;
+double s;
+long reps;
+int randomize;
+int infinity;
+int boring;
+int prec;
+int dox;
+int chardata;
+int nofinalnl;
+char *sepstring = "\n";
+char format[BUFSIZ];
+
+void error __P((char *, char *));
+void getargs __P((int, char *[]));
+void getformat __P((void));
+int getprec __P((char *));
+void putdata __P((double, long));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ double xd, yd;
+ long id;
+ register double *x = &xd;
+ register double *y = &yd;
+ register long *i = &id;
+
+ getargs(argc, argv);
+ if (randomize) {
+ *x = (ender - begin) * (ender > begin ? 1 : -1);
+ srandom((int) s);
+ for (*i = 1; *i <= reps || infinity; (*i)++) {
+ *y = (double) random() / INT_MAX;
+ putdata(*y * *x + begin, reps - *i);
+ }
+ }
+ else
+ for (*i = 1, *x = begin; *i <= reps || infinity; (*i)++, *x += s)
+ putdata(*x, reps - *i);
+ if (!nofinalnl)
+ putchar('\n');
+ exit(0);
+}
+
+void
+getargs(ac, av)
+ int ac;
+ char *av[];
+{
+ register unsigned int mask = 0;
+ register int n = 0;
+
+ while (--ac && **++av == '-' && !isdefault(*av))
+ switch ((*av)[1]) {
+ case 'r':
+ randomize = 1;
+ break;
+ case 'c':
+ chardata = 1;
+ break;
+ case 'n':
+ nofinalnl = 1;
+ break;
+ case 'b':
+ boring = 1;
+ case 'w':
+ if ((*av)[2])
+ strcpy(format, *av + 2);
+ else if (!--ac)
+ error("Need context word after -w or -b", "");
+ else
+ strcpy(format, *++av);
+ break;
+ case 's':
+ if ((*av)[2])
+ strcpy(sepstring, *av + 2);
+ else if (!--ac)
+ error("Need string after -s", "");
+ else
+ strcpy(sepstring, *++av);
+ break;
+ case 'p':
+ if ((*av)[2])
+ prec = atoi(*av + 2);
+ else if (!--ac)
+ error("Need number after -p", "");
+ else
+ prec = atoi(*++av);
+ if (prec <= 0)
+ error("Bad precision value", "");
+ break;
+ default:
+ error("Unknown option %s", *av);
+ }
+
+ switch (ac) { /* examine args right to left, falling thru cases */
+ case 4:
+ if (!isdefault(av[3])) {
+ if (!sscanf(av[3], "%lf", &s))
+ error("Bad s value: %s", av[3]);
+ mask |= 01;
+ }
+ case 3:
+ if (!isdefault(av[2])) {
+ if (!sscanf(av[2], "%lf", &ender))
+ ender = av[2][strlen(av[2])-1];
+ mask |= 02;
+ if (!prec)
+ n = getprec(av[2]);
+ }
+ case 2:
+ if (!isdefault(av[1])) {
+ if (!sscanf(av[1], "%lf", &begin))
+ begin = av[1][strlen(av[1])-1];
+ mask |= 04;
+ if (!prec)
+ prec = getprec(av[1]);
+ if (n > prec) /* maximum precision */
+ prec = n;
+ }
+ case 1:
+ if (!isdefault(av[0])) {
+ if (!sscanf(av[0], "%ld", &reps))
+ error("Bad reps value: %s", av[0]);
+ mask |= 010;
+ }
+ break;
+ case 0:
+ error("jot - print sequential or random data", "");
+ default:
+ error("Too many arguments. What do you mean by %s?", av[4]);
+ }
+ getformat();
+ while (mask) /* 4 bit mask has 1's where last 4 args were given */
+ switch (mask) { /* fill in the 0's by default or computation */
+ case 001:
+ reps = REPS_DEF;
+ mask = 011;
+ break;
+ case 002:
+ reps = REPS_DEF;
+ mask = 012;
+ break;
+ case 003:
+ reps = REPS_DEF;
+ mask = 013;
+ break;
+ case 004:
+ reps = REPS_DEF;
+ mask = 014;
+ break;
+ case 005:
+ reps = REPS_DEF;
+ mask = 015;
+ break;
+ case 006:
+ reps = REPS_DEF;
+ mask = 016;
+ break;
+ case 007:
+ if (randomize) {
+ reps = REPS_DEF;
+ mask = 0;
+ break;
+ }
+ if (s == 0.0) {
+ reps = 0;
+ mask = 0;
+ break;
+ }
+ reps = (ender - begin + s) / s;
+ if (reps <= 0)
+ error("Impossible stepsize", "");
+ mask = 0;
+ break;
+ case 010:
+ begin = BEGIN_DEF;
+ mask = 014;
+ break;
+ case 011:
+ begin = BEGIN_DEF;
+ mask = 015;
+ break;
+ case 012:
+ s = (randomize ? time(0) : STEP_DEF);
+ mask = 013;
+ break;
+ case 013:
+ if (randomize)
+ begin = BEGIN_DEF;
+ else if (reps == 0)
+ error("Must specify begin if reps == 0", "");
+ begin = ender - reps * s + s;
+ mask = 0;
+ break;
+ case 014:
+ s = (randomize ? time(0) : STEP_DEF);
+ mask = 015;
+ break;
+ case 015:
+ if (randomize)
+ ender = ENDER_DEF;
+ else
+ ender = begin + reps * s - s;
+ mask = 0;
+ break;
+ case 016:
+ if (randomize)
+ s = time(0);
+ else if (reps == 0)
+ error("Infinite sequences cannot be bounded",
+ "");
+ else if (reps == 1)
+ s = 0.0;
+ else
+ s = (ender - begin) / (reps - 1);
+ mask = 0;
+ break;
+ case 017: /* if reps given and implied, */
+ if (!randomize && s != 0.0) {
+ long t = (ender - begin + s) / s;
+ if (t <= 0)
+ error("Impossible stepsize", "");
+ if (t < reps) /* take lesser */
+ reps = t;
+ }
+ mask = 0;
+ break;
+ default:
+ error("Bad mask", "");
+ }
+ if (reps == 0)
+ infinity = 1;
+}
+
+void
+putdata(x, notlast)
+ double x;
+ long notlast;
+{
+ long d = x;
+ register long *dp = &d;
+
+ if (boring) /* repeated word */
+ printf(format);
+ else if (dox) /* scalar */
+ printf(format, *dp);
+ else /* real */
+ printf(format, x);
+ if (notlast != 0)
+ fputs(sepstring, stdout);
+}
+
+void
+error(msg, s)
+ char *msg, *s;
+{
+ fprintf(stderr, "jot: ");
+ fprintf(stderr, msg, s);
+ fprintf(stderr,
+ "\nusage: jot [ options ] [ reps [ begin [ end [ s ] ] ] ]\n");
+ if (strncmp("jot - ", msg, 6) == 0)
+ fprintf(stderr, "Options:\n\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
+ "-r random data\n",
+ "-c character data\n",
+ "-n no final newline\n",
+ "-b word repeated word\n",
+ "-w word context word\n",
+ "-s string data separator\n",
+ "-p precision number of characters\n");
+ exit(1);
+}
+
+int
+getprec(s)
+ char *s;
+{
+ register char *p;
+ register char *q;
+
+ for (p = s; *p; p++)
+ if (*p == '.')
+ break;
+ if (!*p)
+ return (0);
+ for (q = ++p; *p; p++)
+ if (!isdigit(*p))
+ break;
+ return (p - q);
+}
+
+void
+getformat()
+{
+ register char *p;
+
+ if (boring) /* no need to bother */
+ return;
+ for (p = format; *p; p++) /* look for '%' */
+ if (*p == '%' && *(p+1) != '%') /* leave %% alone */
+ break;
+ if (!*p && !chardata)
+ sprintf(p, "%%.%df", prec);
+ else if (!*p && chardata) {
+ strcpy(p, "%c");
+ dox = 1;
+ }
+ else if (!*(p+1))
+ strcat(format, "%"); /* cannot end in single '%' */
+ else {
+ while (!isalpha(*p))
+ p++;
+ switch (*p) {
+ case 'f': case 'e': case 'g': case '%':
+ break;
+ case 's':
+ error("Cannot convert numeric data to strings", "");
+ break;
+ /* case 'd': case 'o': case 'x': case 'D': case 'O': case 'X':
+ case 'c': case 'u': */
+ default:
+ dox = 1;
+ break;
+ }
+ }
+}
diff --git a/usr.bin/kdump/Makefile b/usr.bin/kdump/Makefile
new file mode 100644
index 0000000..5cc1042
--- /dev/null
+++ b/usr.bin/kdump/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= kdump
+CFLAGS+=-I${.CURDIR}/../ktrace -I${.CURDIR}/../..
+SRCS= kdump.c ioctl.c subr.c
+.PATH: ${.CURDIR}/../ktrace
+CLEANFILES+=ioctl.c
+
+ioctl.c: mkioctls
+ /bin/sh ${.CURDIR}/mkioctls > ioctl.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/kdump/kdump.1 b/usr.bin/kdump/kdump.1
new file mode 100644
index 0000000..5db704f
--- /dev/null
+++ b/usr.bin/kdump/kdump.1
@@ -0,0 +1,100 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)kdump.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt KDUMP 1
+.Os BSD 4.4
+.Sh NAME
+.Nm kdump
+.Nd display kernel trace data
+.Sh SYNOPSIS
+.Nm kdump
+.Op Fl dnlRT
+.Op Fl f Ar file
+.Op Fl m Ar maxdata
+.Op Fl t Op cnis
+.Sh DESCRIPTION
+.Nm Kdump
+displays the kernel trace files produced with
+.Xr ktrace 1
+in human readable format.
+By default, the file
+.Pa ktrace.out
+in the current directory is displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d
+Display all numbers in decimal.
+.It Fl f Ar file
+Display the specified file instead of
+.Pa ktrace.out .
+.It Fl l
+Loop reading the trace file, once the end-of-file is reached, waiting for
+more data.
+.It Fl m Ar maxdata
+Display at most
+.Ar maxdata
+bytes when decoding
+.Tn I/O .
+.It Fl n
+Suppress ad hoc translations.
+Normally
+.Nm kdump
+tries to decode many system calls into a more human readable format.
+For example,
+.Xr ioctl 2
+values are replaced with the macro name and
+.Va errno
+values are replaced with the
+.Xr strerror 3
+string.
+Suppressing this feature yields a more consistent output format and is
+easily amenable to further processing.
+.It Fl R
+Display relative timestamps (time since previous entry).
+.It Fl T
+Display absolute timestamps for each entry (seconds since epoch).
+.It Fl t Ar cnis
+See the
+.Fl t
+option of
+.Xr ktrace 1 .
+.El
+.Sh SEE ALSO
+.Xr ktrace 1
+.Sh HISTORY
+The
+.Nm kdump
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c
new file mode 100644
index 0000000..d5b1654
--- /dev/null
+++ b/usr.bin/kdump/kdump.c
@@ -0,0 +1,438 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)kdump.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#define KERNEL
+extern int errno;
+#include <sys/errno.h>
+#undef KERNEL
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+#include <sys/ioctl.h>
+#include <sys/ptrace.h>
+#include <vis.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ktrace.h"
+
+int timestamp, decimal, fancy = 1, tail, maxdata;
+char *tracefile = DEF_TRACEFILE;
+struct ktr_header ktr_header;
+
+#define eqs(s1, s2) (strcmp((s1), (s2)) == 0)
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ int ch, ktrlen, size;
+ register void *m;
+ int trpoints = ALL_POINTS;
+
+ while ((ch = getopt(argc,argv,"f:dlm:nRTt:")) != EOF)
+ switch((char)ch) {
+ case 'f':
+ tracefile = optarg;
+ break;
+ case 'd':
+ decimal = 1;
+ break;
+ case 'l':
+ tail = 1;
+ break;
+ case 'm':
+ maxdata = atoi(optarg);
+ break;
+ case 'n':
+ fancy = 0;
+ break;
+ case 'R':
+ timestamp = 2; /* relative timestamp */
+ break;
+ case 'T':
+ timestamp = 1;
+ break;
+ case 't':
+ trpoints = getpoints(optarg);
+ if (trpoints < 0) {
+ (void)fprintf(stderr,
+ "kdump: unknown trace point in %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc > 1)
+ usage();
+
+ m = (void *)malloc(size = 1025);
+ if (m == NULL) {
+ (void)fprintf(stderr, "kdump: %s.\n", strerror(ENOMEM));
+ exit(1);
+ }
+ if (!freopen(tracefile, "r", stdin)) {
+ (void)fprintf(stderr,
+ "kdump: %s: %s.\n", tracefile, strerror(errno));
+ exit(1);
+ }
+ while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) {
+ if (trpoints & (1<<ktr_header.ktr_type))
+ dumpheader(&ktr_header);
+ if ((ktrlen = ktr_header.ktr_len) < 0) {
+ (void)fprintf(stderr,
+ "kdump: bogus length 0x%x\n", ktrlen);
+ exit(1);
+ }
+ if (ktrlen > size) {
+ m = (void *)realloc(m, ktrlen+1);
+ if (m == NULL) {
+ (void)fprintf(stderr,
+ "kdump: %s.\n", strerror(ENOMEM));
+ exit(1);
+ }
+ size = ktrlen;
+ }
+ if (ktrlen && fread_tail(m, ktrlen, 1) == 0) {
+ (void)fprintf(stderr, "kdump: data too short.\n");
+ exit(1);
+ }
+ if ((trpoints & (1<<ktr_header.ktr_type)) == 0)
+ continue;
+ switch (ktr_header.ktr_type) {
+ case KTR_SYSCALL:
+ ktrsyscall((struct ktr_syscall *)m);
+ break;
+ case KTR_SYSRET:
+ ktrsysret((struct ktr_sysret *)m);
+ break;
+ case KTR_NAMEI:
+ ktrnamei(m, ktrlen);
+ break;
+ case KTR_GENIO:
+ ktrgenio((struct ktr_genio *)m, ktrlen);
+ break;
+ case KTR_PSIG:
+ ktrpsig((struct ktr_psig *)m);
+ break;
+ case KTR_CSW:
+ ktrcsw((struct ktr_csw *)m);
+ break;
+ }
+ if (tail)
+ (void)fflush(stdout);
+ }
+}
+
+fread_tail(buf, size, num)
+ char *buf;
+ int num, size;
+{
+ int i;
+
+ while ((i = fread(buf, size, num, stdin)) == 0 && tail) {
+ (void)sleep(1);
+ clearerr(stdin);
+ }
+ return (i);
+}
+
+dumpheader(kth)
+ struct ktr_header *kth;
+{
+ static char unknown[64];
+ static struct timeval prevtime, temp;
+ char *type;
+
+ switch (kth->ktr_type) {
+ case KTR_SYSCALL:
+ type = "CALL";
+ break;
+ case KTR_SYSRET:
+ type = "RET ";
+ break;
+ case KTR_NAMEI:
+ type = "NAMI";
+ break;
+ case KTR_GENIO:
+ type = "GIO ";
+ break;
+ case KTR_PSIG:
+ type = "PSIG";
+ break;
+ case KTR_CSW:
+ type = "CSW";
+ break;
+ default:
+ (void)sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type);
+ type = unknown;
+ }
+
+ (void)printf("%6d %-8s ", kth->ktr_pid, kth->ktr_comm);
+ if (timestamp) {
+ if (timestamp == 2) {
+ temp = kth->ktr_time;
+ timevalsub(&kth->ktr_time, &prevtime);
+ prevtime = temp;
+ }
+ (void)printf("%ld.%06ld ",
+ kth->ktr_time.tv_sec, kth->ktr_time.tv_usec);
+ }
+ (void)printf("%s ", type);
+}
+
+#include <sys/syscall.h>
+#define KTRACE
+#include <sys/kern/syscalls.c>
+#undef KTRACE
+int nsyscalls = sizeof (syscallnames) / sizeof (syscallnames[0]);
+
+static char *ptrace_ops[] = {
+ "PT_TRACE_ME", "PT_READ_I", "PT_READ_D", "PT_READ_U",
+ "PT_WRITE_I", "PT_WRITE_D", "PT_WRITE_U", "PT_CONTINUE",
+ "PT_KILL", "PT_STEP",
+};
+
+ktrsyscall(ktr)
+ register struct ktr_syscall *ktr;
+{
+ register narg = ktr->ktr_narg;
+ register int *ip;
+ char *ioctlname();
+
+ if (ktr->ktr_code >= nsyscalls || ktr->ktr_code < 0)
+ (void)printf("[%d]", ktr->ktr_code);
+ else
+ (void)printf("%s", syscallnames[ktr->ktr_code]);
+ ip = (int *)((char *)ktr + sizeof(struct ktr_syscall));
+ if (narg) {
+ char c = '(';
+ if (fancy) {
+ if (ktr->ktr_code == SYS_ioctl) {
+ char *cp;
+ if (decimal)
+ (void)printf("(%d", *ip);
+ else
+ (void)printf("(%#x", *ip);
+ ip++;
+ narg--;
+ if ((cp = ioctlname(*ip)) != NULL)
+ (void)printf(",%s", cp);
+ else {
+ if (decimal)
+ (void)printf(",%d", *ip);
+ else
+ (void)printf(",%#x ", *ip);
+ }
+ c = ',';
+ ip++;
+ narg--;
+ } else if (ktr->ktr_code == SYS_ptrace) {
+ if (*ip <= PT_STEP && *ip >= 0)
+ (void)printf("(%s", ptrace_ops[*ip]);
+ else
+ (void)printf("(%d", *ip);
+ c = ',';
+ ip++;
+ narg--;
+ }
+ }
+ while (narg) {
+ if (decimal)
+ (void)printf("%c%d", c, *ip);
+ else
+ (void)printf("%c%#x", c, *ip);
+ c = ',';
+ ip++;
+ narg--;
+ }
+ (void)putchar(')');
+ }
+ (void)putchar('\n');
+}
+
+ktrsysret(ktr)
+ struct ktr_sysret *ktr;
+{
+ register int ret = ktr->ktr_retval;
+ register int error = ktr->ktr_error;
+ register int code = ktr->ktr_code;
+
+ if (code >= nsyscalls || code < 0)
+ (void)printf("[%d] ", code);
+ else
+ (void)printf("%s ", syscallnames[code]);
+
+ if (error == 0) {
+ if (fancy) {
+ (void)printf("%d", ret);
+ if (ret < 0 || ret > 9)
+ (void)printf("/%#x", ret);
+ } else {
+ if (decimal)
+ (void)printf("%d", ret);
+ else
+ (void)printf("%#x", ret);
+ }
+ } else if (error == ERESTART)
+ (void)printf("RESTART");
+ else if (error == EJUSTRETURN)
+ (void)printf("JUSTRETURN");
+ else {
+ (void)printf("-1 errno %d", ktr->ktr_error);
+ if (fancy)
+ (void)printf(" %s", strerror(ktr->ktr_error));
+ }
+ (void)putchar('\n');
+}
+
+ktrnamei(cp, len)
+ char *cp;
+{
+ (void)printf("\"%.*s\"\n", len, cp);
+}
+
+ktrgenio(ktr, len)
+ struct ktr_genio *ktr;
+{
+ register int datalen = len - sizeof (struct ktr_genio);
+ register char *dp = (char *)ktr + sizeof (struct ktr_genio);
+ register char *cp;
+ register int col = 0;
+ register width;
+ char visbuf[5];
+ static screenwidth = 0;
+
+ if (screenwidth == 0) {
+ struct winsize ws;
+
+ if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 &&
+ ws.ws_col > 8)
+ screenwidth = ws.ws_col;
+ else
+ screenwidth = 80;
+ }
+ printf("fd %d %s %d bytes\n", ktr->ktr_fd,
+ ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen);
+ if (maxdata && datalen > maxdata)
+ datalen = maxdata;
+ (void)printf(" \"");
+ col = 8;
+ for (;datalen > 0; datalen--, dp++) {
+ (void) vis(visbuf, *dp, VIS_CSTYLE, *(dp+1));
+ cp = visbuf;
+ /*
+ * Keep track of printables and
+ * space chars (like fold(1)).
+ */
+ if (col == 0) {
+ (void)putchar('\t');
+ col = 8;
+ }
+ switch(*cp) {
+ case '\n':
+ col = 0;
+ (void)putchar('\n');
+ continue;
+ case '\t':
+ width = 8 - (col&07);
+ break;
+ default:
+ width = strlen(cp);
+ }
+ if (col + width > (screenwidth-2)) {
+ (void)printf("\\\n\t");
+ col = 8;
+ }
+ col += width;
+ do {
+ (void)putchar(*cp++);
+ } while (*cp);
+ }
+ if (col == 0)
+ (void)printf(" ");
+ (void)printf("\"\n");
+}
+
+char *signames[] = {
+ "NULL", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", /* 1 - 6 */
+ "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */
+ "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", /* 13 - 18 */
+ "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", /* 19 - 24 */
+ "XFSZ", "VTALRM", "PROF", "WINCH", "29", "USR1", /* 25 - 30 */
+ "USR2", NULL, /* 31 - 32 */
+};
+
+ktrpsig(psig)
+ struct ktr_psig *psig;
+{
+ (void)printf("SIG%s ", signames[psig->signo]);
+ if (psig->action == SIG_DFL)
+ (void)printf("SIG_DFL\n");
+ else
+ (void)printf("caught handler=0x%x mask=0x%x code=0x%x\n",
+ (u_int)psig->action, psig->mask, psig->code);
+}
+
+ktrcsw(cs)
+ struct ktr_csw *cs;
+{
+ (void)printf("%s %s\n", cs->out ? "stop" : "resume",
+ cs->user ? "user" : "kernel");
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: kdump [-dnlRT] [-f trfile] [-m maxdata] [-t [cnis]]\n");
+ exit(1);
+}
diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls
new file mode 100644
index 0000000..e93bddd
--- /dev/null
+++ b/usr.bin/kdump/mkioctls
@@ -0,0 +1,33 @@
+awk '
+BEGIN {
+ print "#include <sys/param.h>"
+ print "#include <sys/socket.h>"
+ print "#include <sys/socketvar.h>"
+ print "#include <net/route.h>"
+ print "#include <net/if.h>"
+ print "#include <sys/termios.h>"
+ print "#define COMPAT_43"
+ print "#include <sys/ioctl.h>"
+ print ""
+ print "char *"
+ print "ioctlname(val)"
+ print "{"
+ print ""
+}
+
+/^#[ ]*define[ ]*(TIO|FIO|SIO|OSIO)[A-Z]*[ ]*_IO/ {
+
+ # find where the name starts
+ for (i = 1; i <= NF; i++)
+ if ($i ~ /define/)
+ break;
+ ++i;
+ #
+ printf("\tif (val == %s)\n\t\treturn(\"%s\");\n", $i, $i);
+
+}
+END {
+ print "\n\treturn(NULL);"
+ print "}"
+}
+' $DESTDIR/usr/include/sys/ioctl.h $DESTDIR/usr/include/sys/ioctl_compat.h
diff --git a/usr.bin/key/Makefile b/usr.bin/key/Makefile
new file mode 100644
index 0000000..9612051
--- /dev/null
+++ b/usr.bin/key/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+
+PROG= key
+SRCS= skey.c
+MAN1= key.1
+
+DPADD= ${LIBSKEY} ${LIBMD}
+LDADD= -lskey -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/key/README.WZV b/usr.bin/key/README.WZV
new file mode 100644
index 0000000..a13f3b5
--- /dev/null
+++ b/usr.bin/key/README.WZV
@@ -0,0 +1,100 @@
+One of the nice things of S/Key is that it still leaves you the option
+to use regular UNIX passwords. In fact, the presence of S/Key support
+is completely invisible for a user until she has set up a password with
+the keyinit command. You can permit regular UNIX passwords for local
+logins, while at the same time insisting on S/Key passwords for logins
+from outside.
+
+ORIGIN
+
+These files are modified versions of the s/key files found on
+thumper.bellcore.com at 21 oct 1993. They have been fixed to
+run on top of SunOS 4.1.3 and Solaris 2.3.
+
+Installation is described at the end of this file.
+
+USAGE
+
+Use the keyinit command to set up a new series of s/key passwords.
+
+ wzv_6% keyinit
+ Updating wietse:
+ Old key: wz173500
+ Reminder - Only use this method if you are direct connected.
+ If you are using telnet or dial-in exit with no password and use keyinit -s.
+ Enter secret password:
+ Again secret password:
+
+ ID wietse s/key is 99 wz173501
+ BLAH BLA BLAH BLAH BLAH BLA
+
+Be sure to make your secret password sufficiently long. Try using a
+full sentence instead of just one single word.
+
+You will have to do a "keyinit" on every system that you want to login
+on using one-time passwords.
+
+Whenever you log into an s/key protected system you will see
+something like:
+
+ login: wietse
+ s/key 98 wz173501
+ Password:
+
+In this case you can either enter your regular UNIX password or
+your one-time s/key password. For example, I open a local window
+to compute the password:
+
+ local% key 98 wz173501
+ Reminder - Do not use key while logged in via telnet or rlogin.
+ Enter secret password:
+ BLAH BLA BLAH BLAH BLAH BLA
+
+The "BLAH BLA BLAH BLAH BLAH BLA" is the one-time s/key password.
+
+If you have to type the one-time password in by hand, it is convenient
+to have echo turned on so that you can correct typing errors. Just type
+a newline at the "Password:" prompt:
+
+ login: wietse
+ s/key 98 wz173501
+ Password: (turning echo on)
+ Password:BLAH BLA BLAH BLAH BLAH BLA
+
+The 98 in the challenge will be 97 the next time, and so on. You'll get
+a warning when you are about to run out of s/key passwords, so that you
+will have to run the keyinit command again.
+
+Sometimes it is more practical to carry a piece of paper with a small
+series of one-time passwords. You can generate the list with:
+
+ % key -n 10 98 wz173501
+ 98: BLAH BLA BLAH BLAH BLAH BLA
+ 97: ...
+ 96: ...
+
+Be careful when printing material like this!
+
+INSTALLATION
+
+To install, do: make sunos4 (or whatever), then: make install.
+
+The UNIX password is always permitted with non-network logins. By
+default, UNIX passwords are always permitted (the Bellcore code by
+default disallows UNIX passwords but I think that is too painful). In
+order to permit UNIX passwords only with logins from specific networks,
+create a file /etc/skey.access. For example,
+
+ # First word says if UNIX passwords are to be permitted or denied.
+ # remainder of the rule is a networknumber and mask. A rule matches a
+ # host if any of its addresses satisfies:
+ #
+ # network = (address & mask)
+ #
+ #what network mask
+ permit 131.155.210.0 255.255.255.0
+ deny 0.0.0.0 0.0.0.0
+
+This particular example will permit UNIX passwords with logins from any
+host on network 131.155.210, but will insist on one-time passwords in
+all other cases.
diff --git a/usr.bin/key/key.1 b/usr.bin/key/key.1
new file mode 100644
index 0000000..d9da463
--- /dev/null
+++ b/usr.bin/key/key.1
@@ -0,0 +1,49 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)key.1 1.0 (Bellcore) 12/2/91
+.\"
+.lt 6.0i
+.TH KEY 1 "2 December 1991"
+.AT 3
+.SH NAME
+key \- Stand\-alone program for computing responses to S/Key challenges.
+.SH SYNOPSIS
+.B key [\-n <count>] <Sequence> <key>
+.SH DESCRIPTION
+.I key
+Takes the optional count of the number of one time access
+passwords to print
+along with a (maximum) sequence number and key as command line args,
+it prompts for the user's secret password, and produces both word
+and hex format responses.
+.SH EXAMPLE
+.sh
+ Usage example:
+.sp 0
+ >key \-n 5 99 th91334
+.sp 0
+ Enter password: <your secret password is entered here>
+.sp 0
+ OMEN US HORN OMIT BACK AHOY
+.sp 0
+ .... 4 more passwords.
+.sp 0
+ >
+.LP
+.SH OPTIONS
+.LP
+.B \-n <count>
+the number of one time access passwords to print.
+The default is one.
+.SH DIAGNOSTICS
+.SH BUGS
+.LP
+.SH SEE ALSO
+.BR skey(1),
+.BR keyinit(1),
+.BR keysu(1),
+.BR keyinfo(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
+.SH CONTACT
+staff@thumper.bellcore.com
diff --git a/usr.bin/key/skey.1 b/usr.bin/key/skey.1
new file mode 100644
index 0000000..b4e0455
--- /dev/null
+++ b/usr.bin/key/skey.1
@@ -0,0 +1,59 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)skey.1 1.1 10/28/93
+.\"
+.lt 6.0i
+.TH KEY 1 "28 October 1993"
+.AT 3
+.SH NAME
+S/key \- A procedure to use one time passwords for accessing computer systems.
+.SH DESCRIPTION
+.I S/key
+is a procedure for using one time password to authenticate access to
+computer systems. It uses 64 bits of information transformed by the
+MD4 algorithm. The user supplies the 64 bits in the form of 6 English
+words that are generated by a secure computer.
+Example use of the S/key program
+.I key
+.sp
+ Usage example:
+.sp 0
+ >key 99 th91334
+.sp 0
+ Enter password: <your secret password is entered here>
+.sp 0
+ OMEN US HORN OMIT BACK AHOY
+.sp 0
+ >
+.sp
+The programs that are part of the S/Key system are keyinit, key, and
+keyinfo. Keyinit is used to get your ID set up, key is
+used to get the one time password each time,
+keyinfo is used to extract information from the S/Key database.
+.sp
+When you run "keyinit" you inform the system of your
+secret password. Running "key" then generates the
+one-time passwords, and also requires your secret
+password. If however, you misspell your password
+while running "key", you will get a list of passwords
+that will not work, and no indication about the problem.
+.sp
+Password sequence numbers count backward from 99. If you
+don't know this, the syntax for "key" will be confusing.
+.sp
+You can enter the passwords using small letters, even
+though the "key" program gives them in caps.
+.sp
+Macintosh and a general purpose PC use
+are available.
+.sp
+Under FreeBSD, you can control, with /etc/skey.access, from which
+hosts and/or networks the use of S/Key passwords is obligated.
+.LP
+.SH SEE ALSO
+.BR keyinit(1),
+.BR key(1),
+.BR keyinfo(1)
+.BR skey.access(5)
+.SH AUTHOR
+Phil Karn, Neil M. Haller, John S. Walden, Scott Chasin
diff --git a/usr.bin/key/skey.c b/usr.bin/key/skey.c
new file mode 100644
index 0000000..954b94e
--- /dev/null
+++ b/usr.bin/key/skey.c
@@ -0,0 +1,129 @@
+/* Stand-alone program for computing responses to S/Key challenges.
+ * Takes the iteration count and seed as command line args, prompts
+ * for the user's key, and produces both word and hex format responses.
+ *
+ * Usage example:
+ * >skey 88 ka9q2
+ * Enter password:
+ * OMEN US HORN OMIT BACK AHOY
+ * C848 666B 6435 0A93
+ * >
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __MSDOS__
+#include <dos.h>
+#else /* Assume BSD unix */
+#include <fcntl.h>
+#endif
+
+#include <skey.h>
+
+char *readpass();
+void usage();
+int getopt();
+extern int optind;
+extern char *optarg;
+
+int
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ int n,cnt,i;
+ char passwd[256],passwd2[256];
+ char key[8];
+ char *seed;
+ char buf[33];
+ char *slash;
+
+ cnt = 1;
+ while((i = getopt(argc,argv,"n:")) != EOF){
+ switch(i){
+ case 'n':
+ cnt = atoi(optarg);
+ break;
+ }
+ }
+ /* could be in the form <number>/<seed> */
+ if(argc <= optind + 1){
+ /*look for / in it */
+ if(argc <= optind){
+ usage(argv[0]);
+ return 1;
+ }
+
+ slash = strchr(argv[optind], '/');
+ if(slash == NULL){
+ usage(argv[0]);
+ return 1;
+ }
+ *slash++ = '\0';
+ seed = slash;
+
+ if((n = atoi(argv[optind])) < 0){
+ fprintf(stderr,"%s not positive\n",argv[optind]);
+ usage(argv[0]);
+ return 1;
+ }
+ }
+ else {
+
+ if((n = atoi(argv[optind])) < 0){
+ fprintf(stderr,"%s not positive\n",argv[optind]);
+ usage(argv[0]);
+ return 1;
+ }
+ seed = argv[++optind];
+ }
+ fprintf(stderr,"Reminder - Do not use this program while logged in via telnet or rlogin.\n");
+
+ /* Get user's secret password */
+ for(;;){
+ fprintf(stderr,"Enter secret password: ");
+ readpass(passwd,sizeof(passwd));
+ break;
+ /************
+ fprintf(stderr,"Again secret password: ");
+ readpass(passwd2,sizeof(passwd));
+ if(strcmp(passwd,passwd2) == 0) break;
+ fprintf(stderr, "Sorry no match\n");
+ **************/
+
+ }
+
+ /* Crunch seed and password into starting key */
+ if(keycrunch(key,seed,passwd) != 0){
+ fprintf(stderr,"%s: key crunch failed\n",argv[0]);
+ return 1;
+ }
+ if(cnt == 1){
+ while(n-- != 0)
+ f(key);
+ printf("%s\n",btoe(buf,key));
+#ifdef HEXIN
+ printf("%s\n",put8(buf,key));
+#endif
+ } else {
+ for(i=0;i<=n-cnt;i++)
+ f(key);
+ for(;i<=n;i++){
+#ifdef HEXIN
+ printf("%d: %-29s %s\n",i,btoe(buf,key),put8(buf,key));
+#else
+ printf("%d: %-29s\n",i,btoe(buf,key));
+#endif
+ f(key);
+ }
+ }
+ return 0;
+}
+
+void
+usage(s)
+char *s;
+{
+ fprintf(stderr,"Usage: %s [-n count] <sequence #>[/] <key> \n",s);
+}
diff --git a/usr.bin/keyinfo/Makefile b/usr.bin/keyinfo/Makefile
new file mode 100644
index 0000000..3227586
--- /dev/null
+++ b/usr.bin/keyinfo/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 5.5 (Berkeley) 7/1/90
+
+MAN1= keyinfo.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/keyinfo.sh ${DESTDIR}${BINDIR}/keyinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keyinfo/keyinfo.1 b/usr.bin/keyinfo/keyinfo.1
new file mode 100644
index 0000000..b12aa96
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.1
@@ -0,0 +1,40 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)keyinfo.1 1.1 (Bellcore) 7/20/93
+.\"
+.lt 6.0i
+.TH KEYINFO 1 "20 July 1993"
+.AT 3
+.SH NAME
+keyinfo \- display current S/Key sequence number and seed
+.SH SYNOPSIS
+.B keyinfo [username]
+.SH DESCRIPTION
+.I keyinfo
+takes an optional user name and displays the user\'s current sequence
+number and seed found in the S/Key database /etc/skeykeys.
+.sp 1
+The command can be useful when generating a list of passwords for use
+on a field trip, by combining with the command
+.I key
+in the form:
+.sp
+ >key \-n <number of passwords to print> `keyinfo`|lpr
+.SH EXAMPLE
+.sh
+Usage example:
+.sp 0
+ >keyinfo
+.sp 0
+ 0098 ws91340
+.LP
+.SH ARGUMENTS
+.TP
+.B username
+The S/key user to display the information for. The default is
+to display S/Key information on the user who invokes the command.
+.SH SEE ALSO
+.BR keyinit(1),
+.BR key(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
diff --git a/usr.bin/keyinfo/keyinfo.sh b/usr.bin/keyinfo/keyinfo.sh
new file mode 100644
index 0000000..5879442
--- /dev/null
+++ b/usr.bin/keyinfo/keyinfo.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# search /etc/skeykeys for the skey string for this user OR user specified
+# in 1st parameter
+
+PATH=/bin:/usr/bin
+
+test -f /etc/skeykeys && {
+ WHO=${1-`id | sed 's/^[^(]*(\([^)]*\).*/\1/'`}
+ awk '/^'${WHO}'[ ]/ { print $2-1, $3 }' /etc/skeykeys
+}
diff --git a/usr.bin/keyinit/Makefile b/usr.bin/keyinit/Makefile
new file mode 100644
index 0000000..a9ed54e
--- /dev/null
+++ b/usr.bin/keyinit/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 5.6 (Berkeley) 3/5/91
+#
+PROG= keyinit
+MAN1= keyinit.1
+SRCS= skeyinit.c
+
+BINOWN= root
+BINMODE=4555
+
+DPADD= ${LIBSKEY} ${LIBMD}
+LDADD= -lskey -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/keyinit/keyinit.1 b/usr.bin/keyinit/keyinit.1
new file mode 100644
index 0000000..2fe2d03
--- /dev/null
+++ b/usr.bin/keyinit/keyinit.1
@@ -0,0 +1,64 @@
+.ll 6i
+.pl 10.5i
+.\" @(#)keyinit.1 1.0 (Bellcore) 7/20/93
+.\"
+.lt 6.0i
+.TH KEYINIT 1 "20 July 1993"
+.AT 3
+.SH NAME
+keyinit \- Change password or add user to S/Key authentication system.
+.SH SYNOPSIS
+.B keyinit [\-s] [<user ID >]
+.SH DESCRIPTION
+.I keyinit
+initializes the system so you can use S/Key one-time passwords to
+login. The program will ask you to enter a secret pass phrase; enter a
+phrase of several words in response. After the S/Key database has been
+updated you can login using either your regular UNIX password or using
+S/Key one-time passwords.
+.PP
+When logging in from another machine you can avoid typing a real
+password over the network, by typing your S/Key pass phrase to the
+\fIkey\fR command on the local machine: the program will respond with
+the one-time password that you should use to log into the remote
+machine. This is most conveniently done with cut-and-paste operations
+using a mouse. Alternatively, you can pre-compute one-time passwords
+using the \fIkey\fR command and carry them with you on a piece of paper.
+.PP
+\fIkeyinit\fR requires you to type your secret password, so it should
+be used only on a secure terminal. For example, on the console of a
+workstation. If you are using \fIkeyinit\fR while logged in over an
+untrusted network, follow the instructions given below with the \-s
+option.
+.SH OPTIONS
+.IP \-s
+Set secure mode where the user is expected to have used a secure
+machine to generate the first one time password. Without the \-s the
+system will assume you are direct connected over secure communications
+and prompt you for your secret password.
+The \-s option also allows one to set the seed and count for complete
+control of the parameters. You can use keyinit -s in compination with
+the
+.I key
+command to set the seed and count if you do not like the defaults.
+To do this run keyinit in one window and put in your count and seed
+then run key in another window to generate the correct 6 english words
+for that count and seed. You can then
+"cut" and "paste" them or copy them into the keyinit window.
+.sp
+.LP
+.B <user ID>
+the ID for the user to be changed/added
+.SH DIAGNOSTICS
+.SH FILES
+.TP
+/etc/skeykeys data base of information for S/Key system.
+.SH BUGS
+.LP
+.SH SEE ALSO
+.BR skey(1),
+.BR key(1),
+.BR keysu(1),
+.BR keyinfo(1)
+.SH AUTHOR
+Command by Phil Karn, Neil M. Haller, John S. Walden
diff --git a/usr.bin/keyinit/skeyinit.c b/usr.bin/keyinit/skeyinit.c
new file mode 100644
index 0000000..30320ed
--- /dev/null
+++ b/usr.bin/keyinit/skeyinit.c
@@ -0,0 +1,191 @@
+/* change password or add user to S/KEY authentication system.
+ * S/KEY is a tradmark of Bellcore */
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <skey.h>
+
+extern int optind;
+extern char *optarg;
+
+#define NAMELEN 2
+
+int
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ struct skey skey;
+ int rval,n,nn,i,defaultsetup;
+ char seed[18],tmp[80],key[8];
+ struct passwd *ppuser,*pp;
+ char defaultseed[17], passwd[256],passwd2[256] ;
+
+
+ time_t now;
+ struct tm *tm;
+ char tbuf[27],buf[60];
+ char lastc, me[80];
+ int l;
+
+ time(&now);
+#if 0 /* Choose a more random seed */
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), "%M%j", tm);
+#else
+ sprintf(tbuf, "%05ld", (long) (now % 100000));
+#endif
+ gethostname(defaultseed,NAMELEN);
+ strcpy(&defaultseed[NAMELEN],tbuf);
+
+ pp = ppuser = getpwuid(getuid());
+ strcpy(me,pp->pw_name);
+ defaultsetup = 1;
+ if( argc > 1){
+ if(strcmp("-s", argv[1]) == 0)
+ defaultsetup = 0;
+ else
+ pp = getpwnam(argv[1]);
+ if(argc > 2)
+ pp = getpwnam(argv[2]);
+
+ }
+ if(pp == NULL){
+ printf("User unknown\n");
+ return 1;
+ }
+ if(strcmp( pp->pw_name,me) != 0){
+ if(getuid() != 0){
+ /* Only root can change other's passwds */
+ printf("Permission denied.\n");
+ return(1);
+ }
+ }
+
+
+
+ rval = skeylookup(&skey,pp->pw_name);
+ switch(rval){
+ case -1:
+ perror("error in opening database");
+ return 1;
+ case 0:
+ printf("Updating %s:\n",pp->pw_name);
+ printf("Old key: %s\n",skey.seed);
+ /* lets be nice if they have a skey.seed that ends in 0-8 just add one*/
+ l = strlen(skey.seed);
+ if( l > 0){
+ lastc = skey.seed[l-1];
+ if( isdigit(lastc) && lastc != '9' ){
+ strcpy(defaultseed, skey.seed);
+ defaultseed[l-1] = lastc + 1;
+ }
+ if( isdigit(lastc) && lastc == '9' && l < 16){
+ strcpy(defaultseed, skey.seed);
+ defaultseed[l-1] = '0';
+ defaultseed[l] = '0';
+ defaultseed[l+1] = '\0';
+ }
+ }
+ break;
+ case 1:
+ skey.val = 0; /* XXX */
+ printf("Adding %s:\n",pp->pw_name);
+ break;
+ }
+ n = 99;
+ if( ! defaultsetup){
+ printf("Reminder you need the 6 english words from the skey command.\n");
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ printf("Enter sequence count from 1 to 9999: ");
+ fgets(tmp,sizeof(tmp),stdin);
+ n = atoi(tmp);
+ if(n > 0 && n < 10000)
+ break; /* Valid range */
+ printf("Count must be > 0 and < 10000\n");
+ }
+ }
+ if( !defaultsetup){
+ printf("Enter new key [default %s]: ", defaultseed);
+ fflush(stdout);
+ fgets(seed,sizeof(seed),stdin);
+ rip(seed);
+ if(strlen(seed) > 16){
+ printf("Seed truncated to 16 chars\n");
+ seed[16] = '\0';
+ }
+ if( seed[0] == '\0') strcpy(seed,defaultseed);
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ printf("s/key %d %s\ns/key access password: ",n,seed);
+ fgets(tmp,sizeof(tmp),stdin);
+ rip(tmp);
+ if(tmp[0] == '?'){
+ printf("Enter 6 English words from secure S/Key calculation.\n");
+ continue;
+ }
+ if(tmp[0] == '\0'){
+ exit(1);
+ }
+ if(etob(key,tmp) == 1 || atob8(key,tmp) == 0)
+ break; /* Valid format */
+ printf("Invalid format, try again with 6 English words.\n");
+ }
+ } else {
+ /* Get user's secret password */
+ fprintf(stderr,"Reminder - Only use this method if you are directly connected.\n");
+ fprintf(stderr,"If you are using telnet or rlogin exit with no password and use keyinit -s.\n");
+ for(i=0;;i++){
+ if(i >= 2) exit(1);
+ fprintf(stderr,"Enter secret password: ");
+ readpass(passwd,sizeof(passwd));
+ if(passwd[0] == '\0'){
+ exit(1);
+ }
+ fprintf(stderr,"Again secret password: ");
+ readpass(passwd2,sizeof(passwd));
+ if(passwd2[0] == '\0'){
+ exit(1);
+ }
+ if(strlen(passwd) < 4 && strlen(passwd2) < 4) {
+ fprintf(stderr, "Sorry your password must be longer\n\r");
+ exit(1);
+ }
+ if(strcmp(passwd,passwd2) == 0) break;
+ fprintf(stderr, "Sorry no match\n");
+
+
+ }
+ strcpy(seed,defaultseed);
+
+ /* Crunch seed and password into starting key */
+ if(keycrunch(key,seed,passwd) != 0){
+ fprintf(stderr,"%s: key crunch failed\n",argv[0]);
+ return 1;
+ }
+ nn = n;
+ while(nn-- != 0)
+ f(key);
+ }
+ time(&now);
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
+ if (skey.val == NULL)
+ skey.val = (char *) malloc(16+1);
+
+
+ btoa8(skey.val,key);
+ fprintf(skey.keyfile,"%s %04d %-16s %s %-21s\n",pp->pw_name,n,
+ seed,skey.val, tbuf);
+ fclose(skey.keyfile);
+ printf("\nID %s s/key is %d %s\n",pp->pw_name,n,seed);
+ printf("%s\n",btoe(buf,key));
+#ifdef HEXIN
+ printf("%s\n",put8(buf,key));
+#endif
+ return 0;
+}
diff --git a/usr.bin/killall/Makefile b/usr.bin/killall/Makefile
new file mode 100644
index 0000000..17cfc53
--- /dev/null
+++ b/usr.bin/killall/Makefile
@@ -0,0 +1,7 @@
+MAN1= killall.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/killall.pl ${DESTDIR}${BINDIR}/killall
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/killall/killall.1 b/usr.bin/killall/killall.1
new file mode 100644
index 0000000..f08a649
--- /dev/null
+++ b/usr.bin/killall/killall.1
@@ -0,0 +1,130 @@
+.\" Copyright (C) 1995 by Joerg Wunsch, Dresden
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd June 25, 1995
+.Os FreeBSD 2.2
+.Dt KILLALL 1
+.Sh NAME
+.Nm killall
+.Nd kill processes by name
+.Sh SYNOPSIS
+.Nm killall
+.Op Fl d \&| Ns Fl v
+.Op Fl h \&| Ns Fl \&?
+.Op Fl help
+.Op Fl l
+.Op Fl m
+.Op Fl s
+.Op Fl SIGNAL
+.Ar procname
+.Sh DESCRIPTION
+.Nm Killall
+kills processes selected by name, as opposed to the selection by pid
+as done by
+.Xr kill 1 .
+By default, it will send a
+.Dv TERM
+signal to all processes with an effective UID identical to the
+caller of
+.Nm
+that match the name
+.Ar procname .
+The super-user is allowed to kill any process.
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl d \&| Ns Fl v
+Be more verbose about what will be done. For a single
+.Fl d
+option, a list of the processes that will be sent the signal will be
+printed, or a message indicating that no matching processes have been
+found. If the option
+.Fl d
+has been specified at least twice, the effective UID, PID, and name
+of all processes found in
+.Xr procfs 5
+will be listed in addition.
+.It Fl h \&| Ns Fl \&?
+.It Fl help
+Give a help on the command usage and exit.
+.It Fl l
+List the names of the available signals and exit, like in
+.Xr kill 1 .
+.It Fl m
+Match the argument
+.Ar procname
+as a (case insensitive) regular expression against the names
+of processes found in
+.Xr procfs 5 .
+CAUTION! This is dangerous, a single dot will match any process
+running under the effective UID of the caller. The regular expression
+syntax in effect is that used by
+.Xr perl 1 .
+.It Fl s
+Show only what would be done, but do net send any signal.
+.It Fl SIGNAL
+Send a different signal instead of the default
+.Dv TERM .
+The signal may be specified either as a name
+.Pq with \&or without a leading Dv SIG ,
+or numerically.
+.El
+
+.Sh DIAGNOSTICS
+The
+.Nm
+command will respond with a short usage message and exit with a status
+of 2 in case of a command error. A status of 1 will be returned if
+either no matching process has been found or not all processes have
+been signalled succesfully. Otherwise, a status of 0 will be
+returned.
+.Pp
+Diagnostic messages will only be printed if requested by
+.Fl d
+options.
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr perl 1 ,
+.Xr procfs 5 .
+.Sh HISTORY
+The
+.Nm
+command appeared in FreeBSD 2.2. It has been featured after the
+.Nm
+command as available on other platforms.
+.Sh AUTHOR
+The program has been contributed by Wolfram Schneider, this manual
+page has been written by
+.if n Joerg Wunsch.
+.if t J\(:org Wunsch.
+.Sh BUGS
+Due to limitations in the current implementation of
+.Xr procfs 5 ,
+it is only possible to figure out the effective UID of a process.
+Hence it is impossible to find processes that run setuid, thus a
+regular user will not be able to use
+.Nm
+to send signals to such processes.
diff --git a/usr.bin/killall/killall.pl b/usr.bin/killall/killall.pl
new file mode 100755
index 0000000..e6a58b5
--- /dev/null
+++ b/usr.bin/killall/killall.pl
@@ -0,0 +1,103 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1995 Wolfram Schneider <wosch@cs.tu-berlin.de>
+# All rights reserved. Alle Rechte vorbehalten.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Wolfram Schneider
+# 4. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# killall - kill all processes
+#
+# Note: work only with effective uid due the limit of procfs
+# (eg. not with suid programs)
+#
+# $Id: killall,v 1.2 1995/06/24 11:12:38 w Exp w $
+#
+
+$ENV{'PATH'} = "/bin:/usr/bin";
+$procfs = '/proc';
+$signal = 'SIGTERM'; # default signal for kill
+$debug = 0;
+$match = 0; # 0 match exactly program name
+$show = 0;
+
+$PROC_NAME = 0 + $[;
+$PROC_EUID = 11 + $[;
+
+sub usage {
+ $! = 2;
+ die "killall [-v] [-?|-help] [-l] [-m] [-s] [-SIGNAL] program\n";
+}
+
+while ($_ = $ARGV[0], /^-/) {
+ shift @ARGV;
+ if (/^--$/) { $_ = $ARGV[0]; last }
+ elsif (/^-[vd]$/) { $debug++ }
+ elsif (/^-(h|help|\?)$/) { do usage }
+ elsif (/^-l$/) { exec 'kill', '-l' }
+ elsif (/^-m$/) { $match = 1 }
+ elsif (/^-s$/) { $show = 1 }
+ elsif (/^-([A-Za-z]+|[0-9]+)$/) { $signal = $1 }
+}
+
+$program = $_; &usage unless $program;
+
+die "Maybe $procfs is not mounted\n" unless -e "$procfs/0/status";
+opendir(PROCFS, "$procfs") || die "$procfs $!\n";
+
+foreach (sort{$a <=> $b} grep(/^[0-9]/, readdir(PROCFS))) {
+ $status = "$procfs/$_/status";
+ $pid = $_;
+
+ next if $pid == $$; # don't kill yourself
+
+ open(STATUS, "$status") || next; # process maybe already terminated
+ while(<STATUS>) {
+ @proc = split;
+ printf "%5d $proc[$PROC_NAME] $proc[$PROC_EUID]\n", $pid
+ if $debug > 1;
+
+ if (($proc[$PROC_NAME] eq $program ||
+ ($match && $proc[$PROC_NAME] =~ /$program/i)
+ ) && # test program name
+ ($proc[$PROC_EUID] eq $< || $< == 0)) { # test uid
+ push(@kill, "$pid");
+ }
+ }
+ close STATUS;
+}
+closedir PROCFS;
+
+if ($#kill < 0) { # nothing found
+ print "No matching process.\n" if $debug || $show;
+ exit(1);
+}
+$signal =~ y/[a-z]/[A-Z]/; # signal name in upper case
+$signal =~ s/^SIG//; # strip a leading SIG if present
+print "kill -$signal @kill\n" if $debug || $show;
+
+$cnt = kill ($signal, @kill) unless $show; # kill processes
+exit(0) if $show || $cnt == $#kill + 1;
+exit(1);
diff --git a/usr.bin/ktrace/Makefile b/usr.bin/ktrace/Makefile
new file mode 100644
index 0000000..53a253f
--- /dev/null
+++ b/usr.bin/ktrace/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ktrace
+SRCS= ktrace.c subr.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ktrace/ktrace.1 b/usr.bin/ktrace/ktrace.1
new file mode 100644
index 0000000..d080b4e
--- /dev/null
+++ b/usr.bin/ktrace/ktrace.1
@@ -0,0 +1,163 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ktrace.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt KTRACE 1
+.Os BSD 4.4
+.Sh NAME
+.Nm ktrace
+.Nd enable kernel process tracing
+.Sh SYNOPSIS
+.Nm ktrace
+.Op Fl aCcdi
+.Op Fl f Ar trfile
+.Op Fl g Ar pgrp
+.Op Fl p Ar pid
+.Op Fl t Ar trstr
+.Nm ktrace
+.Op Fl adi
+.Op Fl f Ar trfile
+.Op Fl t Ar trstr
+command
+.Sh DESCRIPTION
+.Nm Ktrace
+enables kernel trace logging for the specified processes.
+Kernel trace data is logged to the file
+.Pa ktrace.out .
+The kernel operations that are traced include system calls, namei
+translations, signal processing, and
+.Tn I/O .
+.Pp
+Once tracing is enabled on a process, trace data will be logged until
+either the process exits or the trace point is cleared.
+A traced process can generate enormous amounts of log data quickly;
+It is strongly suggested that users memorize how to disable tracing before
+attempting to trace a process.
+The following command is sufficient to disable tracing on all user owned
+processes, and, if executed by root, all processes:
+.Pp
+.Dl \&$ trace -C
+.Pp
+The trace file is not human readable; use
+.Xr kdump 1
+to decode it.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Append to the trace file instead of truncating it.
+.It Fl C
+Disable tracing on all user owned processes, and, if executed by root, all
+processes in the system.
+.It Fl c
+Clear the trace points associated with the specified file or processes.
+.It Fl d
+Descendants; perform the operation for all current children of the
+designated processes.
+.It Fl f Ar file
+Log trace records to
+.Ar file
+instead of
+.Pa ktrace.out .
+.It Fl g Ar pgid
+Enable (disable) tracing on all processes in the process group (only one
+.Fl g
+flag is permitted).
+.It Fl i
+Inherit; pass the trace flags to all future children of the designated
+processes.
+.It Fl p Ar pid
+Enable (disable) tracing on the indicated process id (only one
+.Fl p
+flag is permitted).
+.It Fl t Ar trstr
+The string argument represents the kernel trace points, one per letter.
+The following table equates the letters with the tracepoints:
+.Pp
+.Bl -tag -width flag -compact
+.It Cm c
+trace system calls
+.It Cm n
+trace namei translations
+.It Cm i
+trace
+.Tn I/O
+.It Cm s
+trace signal processing
+.El
+.It Ar command
+Execute
+.Ar command
+with the specified trace flags.
+.El
+.Pp
+The
+.Fl p ,
+.Fl g ,
+and
+.Ar command
+options are mutually exclusive.
+.Sh EXAMPLES
+# trace all kernel operations of process id 34
+.Dl $ ktrace -p 34
+.Pp
+# trace all kernel operations of processes in process group 15 and
+# pass the trace flags to all current and future children
+.Dl $ ktrace -idg 15
+.Pp
+# disable all tracing of process 65
+.Dl $ ktrace -cp 65
+.Pp
+# disable tracing signals on process 70 and all current children
+.Dl $ ktrace -t s -cdp 70
+.Pp
+# enable tracing of
+.Tn I/O
+on process 67
+.Dl $ ktrace -ti -p 67
+.Pp
+# run the command "w", tracing only system calls
+.Dl $ ktrace -tc w
+.Pp
+# disable all tracing to the file "tracedata"
+.Dl $ ktrace -c -f tracedata
+.Pp
+# disable tracing of all processes owned by the user
+.Dl $ ktrace -C
+.Sh SEE ALSO
+.Xr kdump 1
+.Sh HISTORY
+The
+.Nm ktrace
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/ktrace/ktrace.c b/usr.bin/ktrace/ktrace.c
new file mode 100644
index 0000000..e584265
--- /dev/null
+++ b/usr.bin/ktrace/ktrace.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ktrace.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+#include <stdio.h>
+#include <signal.h>
+#include "ktrace.h"
+
+void noktrace() {
+ (void)fprintf(stderr, "ktrace: ktrace not enabled in kernel,to use ktrace\n");
+ (void)fprintf(stderr, "you need to add a line \"options KTRACE\" to your kernel\n");
+ exit(1);
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ enum { NOTSET, CLEAR, CLEARALL } clear;
+ int append, ch, fd, inherit, ops, pid, pidset, trpoints;
+ char *tracefile;
+
+ clear = NOTSET;
+ append = ops = pidset = inherit = 0;
+ trpoints = DEF_POINTS;
+ tracefile = DEF_TRACEFILE;
+ /* set up a signal handler for SIGSYS, this indicates that ktrace
+ is not enabled in the kernel */
+ signal(SIGSYS, noktrace);
+ while ((ch = getopt(argc,argv,"aCcdf:g:ip:t:")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ append = 1;
+ break;
+ case 'C':
+ clear = CLEARALL;
+ pidset = 1;
+ break;
+ case 'c':
+ clear = CLEAR;
+ break;
+ case 'd':
+ ops |= KTRFLAG_DESCEND;
+ break;
+ case 'f':
+ tracefile = optarg;
+ break;
+ case 'g':
+ pid = -rpid(optarg);
+ pidset = 1;
+ break;
+ case 'i':
+ inherit = 1;
+ break;
+ case 'p':
+ pid = rpid(optarg);
+ pidset = 1;
+ break;
+ case 't':
+ trpoints = getpoints(optarg);
+ if (trpoints < 0) {
+ (void)fprintf(stderr,
+ "ktrace: unknown facility in %s\n", optarg);
+ usage();
+ }
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (pidset && *argv || !pidset && !*argv)
+ usage();
+
+ if (inherit)
+ trpoints |= KTRFAC_INHERIT;
+
+ if (clear != NOTSET) {
+ if (clear == CLEARALL) {
+ ops = KTROP_CLEAR | KTRFLAG_DESCEND;
+ trpoints = ALL_POINTS;
+ pid = 1;
+ } else
+ ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
+
+ if (ktrace(tracefile, ops, trpoints, pid) < 0)
+ error(tracefile);
+ exit(0);
+ }
+
+ if ((fd = open(tracefile, O_CREAT | O_WRONLY | (append ? 0 : O_TRUNC),
+ DEFFILEMODE)) < 0)
+ error(tracefile);
+ (void)close(fd);
+
+ if (*argv) {
+ if (ktrace(tracefile, ops, trpoints, getpid()) < 0)
+ error();
+ execvp(argv[0], &argv[0]);
+ error(argv[0]);
+ exit(1);
+ }
+ else if (ktrace(tracefile, ops, trpoints, pid) < 0)
+ error(tracefile);
+ exit(0);
+}
+
+rpid(p)
+ char *p;
+{
+ static int first;
+
+ if (first++) {
+ (void)fprintf(stderr,
+ "ktrace: only one -g or -p flag is permitted.\n");
+ usage();
+ }
+ if (!*p) {
+ (void)fprintf(stderr, "ktrace: illegal process id.\n");
+ usage();
+ }
+ return(atoi(p));
+}
+
+error(name)
+ char *name;
+{
+ (void)fprintf(stderr, "ktrace: %s: %s.\n", name, strerror(errno));
+ exit(1);
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+"usage:\tktrace [-aCcid] [-f trfile] [-g pgid] [-p pid] [-t [acgn]\n\tktrace [-aCcid] [-f trfile] [-t [acgn] command\n");
+ exit(1);
+}
diff --git a/usr.bin/ktrace/ktrace.h b/usr.bin/ktrace/ktrace.h
new file mode 100644
index 0000000..595b8bc
--- /dev/null
+++ b/usr.bin/ktrace/ktrace.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ktrace.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define DEF_POINTS (KTRFAC_SYSCALL | KTRFAC_SYSRET | KTRFAC_NAMEI | \
+ KTRFAC_GENIO | KTRFAC_PSIG)
+
+#define ALL_POINTS (DEF_POINTS | KTRFAC_CSW)
+
+#define DEF_TRACEFILE "ktrace.out"
diff --git a/usr.bin/ktrace/subr.c b/usr.bin/ktrace/subr.c
new file mode 100644
index 0000000..6076c24
--- /dev/null
+++ b/usr.bin/ktrace/subr.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)subr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/ktrace.h>
+#include <stdio.h>
+#include "ktrace.h"
+
+getpoints(s)
+ char *s;
+{
+ int facs = 0;
+
+ while (*s) {
+ switch(*s) {
+ case 'c':
+ facs |= KTRFAC_SYSCALL | KTRFAC_SYSRET;
+ break;
+ case 'n':
+ facs |= KTRFAC_NAMEI;
+ break;
+ case 'i':
+ facs |= KTRFAC_GENIO;
+ break;
+ case 's':
+ facs |= KTRFAC_PSIG;
+ break;
+ case 'w':
+ facs |= KTRFAC_CSW;
+ break;
+ case '+':
+ facs |= DEF_POINTS;
+ break;
+ default:
+ return (-1);
+ }
+ s++;
+ }
+ return (facs);
+}
+
+timevaladd(t1, t2)
+ struct timeval *t1, *t2;
+{
+ t1->tv_sec += t2->tv_sec;
+ t1->tv_usec += t2->tv_usec;
+ timevalfix(t1);
+}
+
+timevalsub(t1, t2)
+ struct timeval *t1, *t2;
+{
+ t1->tv_sec -= t2->tv_sec;
+ t1->tv_usec -= t2->tv_usec;
+ timevalfix(t1);
+}
+
+timevalfix(t1)
+ struct timeval *t1;
+{
+ if (t1->tv_usec < 0) {
+ t1->tv_sec--;
+ t1->tv_usec += 1000000;
+ }
+ if (t1->tv_usec >= 1000000) {
+ t1->tv_sec++;
+ t1->tv_usec -= 1000000;
+ }
+}
diff --git a/usr.bin/kzip/Makefile b/usr.bin/kzip/Makefile
new file mode 100644
index 0000000..bdd05e1
--- /dev/null
+++ b/usr.bin/kzip/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.1 1995/04/15 08:18:16 phk Exp $
+
+PROG= kzip
+NOMAN= toobad
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/kzip/kzip.c b/usr.bin/kzip/kzip.c
new file mode 100644
index 0000000..bf4681f
--- /dev/null
+++ b/usr.bin/kzip/kzip.c
@@ -0,0 +1,348 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Copyright (C) 1993 Hannu Savolainen
+ * Ported to 386bsd by Serge Vakulenko
+ * based on tools/build.c by Linus Torvalds
+ * $Id: kzip.c,v 1.3 1995/05/30 06:31:03 rgrimes Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <a.out.h>
+#include <string.h>
+
+#define MAXIMAGE (2*1024*1024)
+ /* This is the limit because a kzip'ed kernel loads at 3Mb and
+ * ends up at 1Mb
+ */
+void
+Usage(char *prog)
+{
+ fprintf(stderr,"usage:\n\t%s [ -l loadaddr] kernel\n", prog);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ pid_t Pext, Pgzip, Ppiggy, Pld;
+ int pipe1[2], pipe2[2];
+ int status, fdi, fdo, fdn, c, verbose;
+ int size;
+ struct exec hdr;
+ int zip_size, offset;
+ struct stat st;
+ u_long forceaddr = 0, addr, entry;
+ char *kernname;
+ char obj[BUFSIZ];
+ char out[BUFSIZ];
+ char base[32];
+
+ while ((c = getopt(argc, argv, "l:v")) != EOF) {
+ switch (c) {
+ case 'l':
+ forceaddr = strtoul(optarg, NULL, 0);
+ if (forceaddr == 0) {
+ fprintf(stderr, "Invalid load address!\n");
+ exit(1);
+ }
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ Usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1)
+ Usage(argv[0]);
+
+ argc -= optind;
+ argv += optind;
+
+ kernname = argv[0];
+
+ strcpy(obj, kernname); strcat(obj,".o");
+ strcpy(out, kernname); strcat(out,".kz");
+
+ fdi = open(kernname ,O_RDONLY);
+ if(fdi<0) {
+ perror(kernname);
+ return 2;
+ }
+
+ /* figure out how big the uncompressed image will be */
+ if (read (fdi, (char *)&hdr, sizeof(hdr)) != sizeof(hdr)) {
+ perror(argv[1]);
+ exit(2);
+ }
+
+ size = hdr.a_text + hdr.a_data + hdr.a_bss;
+ entry = hdr.a_entry - 0xf0000000; /* replace KZBASE */
+
+ lseek (fdi, 0, SEEK_SET);
+
+ if (verbose) {
+ printf("real kernel start address will be: 0x%x\n", entry);
+ printf("real kernel end address will be: 0x%x\n", entry+size);
+ }
+
+
+ fdo = open(obj,O_WRONLY|O_TRUNC|O_CREAT,0666);
+ if(fdo<0) {
+ perror(obj);
+ return 2;
+ }
+
+ if (pipe(pipe1) < 0) { perror("pipe()"); return 1; }
+
+ if (pipe(pipe2) < 0) { perror("pipe()"); return 1; }
+
+ Pext = fork();
+ if (Pext < 0) { perror("fork()"); return 1; }
+ if (!Pext) {
+ dup2(fdi,0);
+ dup2(pipe1[1],1);
+ close(pipe1[0]); close(pipe1[1]);
+ close(pipe2[0]); close(pipe2[1]);
+ close(fdi); close(fdo);
+ extract(kernname);
+ exit(0);
+ }
+
+ Pgzip = fork();
+ if (Pgzip < 0) { perror("fork()"); return 1; }
+ if (!Pgzip) {
+ dup2(pipe1[0],0);
+ dup2(pipe2[1],1);
+ close(pipe1[0]); close(pipe1[1]);
+ close(pipe2[0]); close(pipe2[1]);
+ close(fdi); close(fdo);
+ execlp("gzip", "gzip", "-9", "-n", 0);
+ exit (0);
+ }
+
+ Ppiggy = fork();
+ if (Ppiggy < 0) { perror("fork()"); return 1; }
+ if (!Ppiggy) {
+ dup2(pipe2[0],0);
+ dup2(fdo,1);
+ close(pipe1[0]); close(pipe1[1]);
+ close(pipe2[0]); close(pipe2[1]);
+ close(fdi); close(fdo);
+ piggyback(obj);
+ exit(0);
+ }
+
+ close(pipe1[0]); close(pipe1[1]);
+ close(pipe2[0]); close(pipe2[1]);
+ close(fdi); close(fdo);
+
+ if (waitpid(Pext, &status,0) < 0)
+ { perror("waitpid(Pextract)"); return 1; }
+
+ if(status) {
+ fprintf(stderr,"extract returned %x\n",status);
+ return 3;
+ }
+
+ if (waitpid(Pgzip, &status,0) < 0)
+ { perror("waitpid(Pgzip)"); return 1; }
+
+ if(status) {
+ fprintf(stderr,"gzip returned %x\n",status);
+ return 3;
+ }
+
+ if (waitpid(Ppiggy, &status,0) < 0)
+ { perror("waitpid(Ppiggy)"); return 1; }
+
+ if(status) {
+ fprintf(stderr,"piggyback returned %x\n",status);
+ return 3;
+ }
+
+ if (forceaddr)
+ offset = forceaddr;
+ else {
+ /* a kludge to dynamically figure out where to start it */
+ if (stat (obj, &st) < 0) {
+ perror("cannot get size of compressed data");
+ return 3;
+ }
+ zip_size = (int)st.st_size;
+ offset = entry + size - zip_size + 0x8000; /* fudge factor */
+ }
+ sprintf(base, "0x%x", roundup(offset, 4096));
+
+ Pld = fork();
+ if (Pld < 0) { perror("fork()"); return 1; }
+ if (!Pld) {
+ execlp("ld",
+ "ld",
+ "-Bstatic",
+ "-Z",
+ "-T",
+ base,
+ "-o",
+ out,
+ "/usr/lib/kzhead.o",
+ obj,
+ "/usr/lib/kztail.o",
+ 0);
+ exit(2);
+ }
+
+ if (waitpid(Pld, &status,0) < 0)
+ { perror("waitpid(Pld)"); return 1; }
+
+ if(status) {
+ fprintf(stderr,"ld returned %x\n",status);
+ return 3;
+ }
+
+ if (verbose) {
+
+ fdn = open(obj ,O_RDONLY);
+ if(fdn<0) {
+ perror(obj);
+ return 3;
+ }
+
+ /* figure out how big the compressed image is */
+ if (read (fdn, (char *)&hdr, sizeof(hdr)) != sizeof(hdr)) {
+ perror(obj);
+ return 3;
+ }
+ close(fdn);
+
+ size = hdr.a_text + hdr.a_data + hdr.a_bss;
+
+ printf("kzip data start address will be: 0x%x\n",offset);
+ printf("kzip data end address will be: 0x%x\n",offset+size);
+ }
+
+ unlink(obj);
+ exit(0);
+}
+
+int
+extract (char *file)
+{
+ int sz;
+ char buf[BUFSIZ];
+ struct exec hdr;
+
+ if (read (0, (char *)&hdr, sizeof(hdr)) != sizeof(hdr)) {
+ perror(file);
+ exit(2);
+ }
+ if (hdr.a_magic != ZMAGIC) {
+ fprintf(stderr,"Bad magic in file %s, probably not a kernel\n",
+ file);
+ exit(2);
+ }
+ if (lseek (0, N_TXTOFF(hdr), 0) < 0) {
+ perror(file);
+ exit(2);
+ }
+
+ sz = N_SYMOFF (hdr) - N_TXTOFF (hdr);
+
+ while (sz) {
+ int l, n;
+
+ l = sz;
+ if (l > sizeof(buf))
+ l = sizeof(buf);
+
+ n = read (0, buf, l);
+ if (n != l) {
+ if (n == -1)
+ perror (file);
+ else
+ fprintf (stderr, "Unexpected EOF\n");
+
+ exit(1);
+ }
+
+ write (1, buf, l);
+ sz -= l;
+ }
+ exit(0);
+}
+
+
+char string_names[] = {"_input_data\0_input_len\0"};
+
+struct nlist var_names[2] = { /* Symbol table */
+ { { (char*) 4 }, N_EXT|N_TEXT, 0, 0, 0 }, /* _input_data */
+ { { (char*) 16 }, N_EXT|N_TEXT, 0, 0, 0 }, /* _input_len */
+};
+
+int
+piggyback(char *file)
+{
+ int n, len;
+ struct exec hdr; /* object header */
+ char image[MAXIMAGE]; /* kernel image buffer */
+
+ len = 0;
+ while ((n = read (0, &image[len], sizeof(image)-len+1)) > 0)
+ len += n;
+
+ if (n < 0) {
+ perror ("stdin");
+ exit (1);
+ }
+
+ if (len >= sizeof(image)) {
+ fprintf (stderr,"Input too large\n");
+ exit (1);
+ }
+
+ /*
+ * Output object header
+ */
+ memset(&hdr,0,sizeof hdr);
+ hdr.a_magic = OMAGIC;
+ hdr.a_text = len + sizeof(long);
+ hdr.a_syms = sizeof(var_names);
+ write (1, (char *)&hdr, sizeof(hdr));
+
+ /*
+ * Output text segment (compressed system & len)
+ */
+ write (1, image, len);
+ write (1, (char *)&len, sizeof(len));
+
+ /*
+ * Output symbol table
+ */
+ var_names[1].n_value = len;
+ write (1, (char *)&var_names, sizeof(var_names));
+
+ /*
+ * Output string table
+ */
+ len = sizeof(string_names) + sizeof(len);
+ write (1, (char *)&len, sizeof(len));
+ write (1, string_names, sizeof(string_names));
+
+ return (0);
+}
diff --git a/usr.bin/lam/Makefile b/usr.bin/lam/Makefile
new file mode 100644
index 0000000..a24fc92
--- /dev/null
+++ b/usr.bin/lam/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lam
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lam/lam.1 b/usr.bin/lam/lam.1
new file mode 100644
index 0000000..660e5dc
--- /dev/null
+++ b/usr.bin/lam/lam.1
@@ -0,0 +1,127 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lam.1 8.1 (Berkeley) 6/6/93
+.\"
+.TH LAM 1 "June 6, 1993"
+.UC 4
+.SH NAME
+lam \- laminate files
+.SH SYNOPSIS
+.B lam [ \-[fp]
+min.max
+.B ] [ \-s
+sepstring
+.B ] [ \-t
+c
+.B ]
+file ...
+.SH DESCRIPTION
+.I Lam
+copies the named files side by side onto the standard output.
+The
+.IR n -th
+input lines from the input
+.IR file s
+are considered fragments of the single long
+.IR n -th
+output line into which they are assembled.
+The name `\fB\-\fP' means the standard input, and may be repeated.
+.PP
+Normally, each option affects only the
+.I file
+after it.
+If the option letter is capitalized it affects all subsequent files
+until it appears again uncapitalized.
+The options are described below.
+.IP \fB\-f\fP\ min.max
+Print line fragments according to the format string
+.IR min.max ,
+where
+.I min
+is the minimum field width and
+.I max
+the maximum field width.
+If
+.I min
+begins with a zero, zeros will be added to make up the field width,
+and if it begins with a `\-', the fragment will be left-adjusted
+within the field.
+.IP \fB\-p\fP\ min.max
+Like \fB\-f\fP,
+but pad this file's field when end-of-file is reached
+and other files are still active.
+.IP \fB\-s\fP\ sepstring
+Print
+.I sepstring
+before printing line fragments from the next file.
+This option may appear after the last file.
+.IP \fB\-t\fP\ c
+The input line terminator is
+.I c
+instead of a newline.
+The newline normally appended to each output line is omitted.
+.PP
+To print files simultaneously for easy viewing use
+.IR pr (1).
+.SH EXAMPLES
+.de IC
+.IP
+.ss 36
+.ft B
+..
+.de NC
+.br
+.ss 12
+.PP
+..
+.PP
+The command
+.IC
+lam file1 file2 file3 file4
+.NC
+joins 4 files together along each line.
+To merge the lines from four different files use
+.IC
+lam file1 \-S "\\
+.br
+" file2 file3 file4
+.NC
+Every 2 lines of a file may be joined on one line with
+.IC
+lam \- \- < file
+.NC
+and a form letter with substitutions keyed by `@' can be done with
+.IC
+lam \-t @ letter changes
+.NC
+.SH SEE ALSO
+join(1), pr(1), printf(3)
diff --git a/usr.bin/lam/lam.c b/usr.bin/lam/lam.c
new file mode 100644
index 0000000..07d6abb
--- /dev/null
+++ b/usr.bin/lam/lam.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lam.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * lam - laminate files
+ * Author: John Kunze, UCB
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXOFILES 20
+#define BIGBUFSIZ 5 * BUFSIZ
+
+struct openfile { /* open file structure */
+ FILE *fp; /* file pointer */
+ short eof; /* eof flag */
+ short pad; /* pad flag for missing columns */
+ char eol; /* end of line character */
+ char *sepstring; /* string to print before each line */
+ char *format; /* printf(3) style string spec. */
+} input[MAXOFILES];
+
+int morefiles; /* set by getargs(), changed by gatherline() */
+int nofinalnl; /* normally append \n to each output line */
+char line[BIGBUFSIZ];
+char *linep;
+
+void error __P((char *, char *));
+char *gatherline __P((struct openfile *));
+void getargs __P((char *[]));
+char *pad __P((struct openfile *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct openfile *ip;
+
+ getargs(argv);
+ if (!morefiles)
+ error("lam - laminate files", "");
+ for (;;) {
+ linep = line;
+ for (ip = input; ip->fp != NULL; ip++)
+ linep = gatherline(ip);
+ if (!morefiles)
+ exit(0);
+ fputs(line, stdout);
+ fputs(ip->sepstring, stdout);
+ if (!nofinalnl)
+ putchar('\n');
+ }
+}
+
+void
+getargs(av)
+ char *av[];
+{
+ register struct openfile *ip = input;
+ register char *p;
+ register char *c;
+ static char fmtbuf[BUFSIZ];
+ char *fmtp = fmtbuf;
+ int P, S, F, T;
+
+ P = S = F = T = 0; /* capitalized options */
+ while ((p = *++av) != NULL) {
+ if (*p != '-' || !p[1]) {
+ morefiles++;
+ if (*p == '-')
+ ip->fp = stdin;
+ else if ((ip->fp = fopen(p, "r")) == NULL) {
+ perror(p);
+ exit(1);
+ }
+ ip->pad = P;
+ if (!ip->sepstring)
+ ip->sepstring = (S ? (ip-1)->sepstring : "");
+ if (!ip->format)
+ ip->format = ((P || F) ? (ip-1)->format : "%s");
+ if (!ip->eol)
+ ip->eol = (T ? (ip-1)->eol : '\n');
+ ip++;
+ continue;
+ }
+ switch (*(c = ++p) | 040) {
+ case 's':
+ if (*++p || (p = *++av))
+ ip->sepstring = p;
+ else
+ error("Need string after -%s", c);
+ S = (*c == 'S' ? 1 : 0);
+ break;
+ case 't':
+ if (*++p || (p = *++av))
+ ip->eol = *p;
+ else
+ error("Need character after -%s", c);
+ T = (*c == 'T' ? 1 : 0);
+ nofinalnl = 1;
+ break;
+ case 'p':
+ ip->pad = 1;
+ P = (*c == 'P' ? 1 : 0);
+ case 'f':
+ F = (*c == 'F' ? 1 : 0);
+ if (*++p || (p = *++av)) {
+ fmtp += strlen(fmtp) + 1;
+ if (fmtp > fmtbuf + BUFSIZ)
+ error("No more format space", "");
+ sprintf(fmtp, "%%%ss", p);
+ ip->format = fmtp;
+ }
+ else
+ error("Need string after -%s", c);
+ break;
+ default:
+ error("What do you mean by -%s?", c);
+ break;
+ }
+ }
+ ip->fp = NULL;
+ if (!ip->sepstring)
+ ip->sepstring = "";
+}
+
+char *
+pad(ip)
+ struct openfile *ip;
+{
+ register char *p = ip->sepstring;
+ register char *lp = linep;
+
+ while (*p)
+ *lp++ = *p++;
+ if (ip->pad) {
+ sprintf(lp, ip->format, "");
+ lp += strlen(lp);
+ }
+ return (lp);
+}
+
+char *
+gatherline(ip)
+ struct openfile *ip;
+{
+ char s[BUFSIZ];
+ register int c;
+ register char *p;
+ register char *lp = linep;
+ char *end = s + BUFSIZ;
+
+ if (ip->eof)
+ return (pad(ip));
+ for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
+ if ((*p = c) == ip->eol)
+ break;
+ *p = '\0';
+ if (c == EOF) {
+ ip->eof = 1;
+ if (ip->fp == stdin)
+ fclose(stdin);
+ morefiles--;
+ return (pad(ip));
+ }
+ p = ip->sepstring;
+ while (*p)
+ *lp++ = *p++;
+ sprintf(lp, ip->format, s);
+ lp += strlen(lp);
+ return (lp);
+}
+
+void
+error(msg, s)
+ char *msg, *s;
+{
+ fprintf(stderr, "lam: ");
+ fprintf(stderr, msg, s);
+ fprintf(stderr,
+"\nUsage: lam [ -[fp] min.max ] [ -s sepstring ] [ -t c ] file ...\n");
+ if (strncmp("lam - ", msg, 6) == 0)
+ fprintf(stderr, "Options:\n\t%s\t%s\t%s\t%s\t%s",
+ "-f min.max field widths for file fragments\n",
+ "-p min.max like -f, but pad missing fragments\n",
+ "-s sepstring fragment separator\n",
+"-t c input line terminator is c, no \\n after output lines\n",
+ "Capitalized options affect more than one file.\n");
+ exit(1);
+}
diff --git a/usr.bin/last/Makefile b/usr.bin/last/Makefile
new file mode 100644
index 0000000..edbb66b
--- /dev/null
+++ b/usr.bin/last/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= last
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/last/last.1 b/usr.bin/last/last.1
new file mode 100644
index 0000000..c746d5b
--- /dev/null
+++ b/usr.bin/last/last.1
@@ -0,0 +1,128 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)last.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LAST 1
+.Os BSD 4
+.Sh NAME
+.Nm last
+.Nd indicate last logins of users and ttys
+.Sh SYNOPSIS
+.Nm last
+.Op Fl Ns Ar n
+.Op Fl f Ar file
+.Op Fl h Ar host
+.Op Fl t Ar tty
+.Op user ...
+.Sh DESCRIPTION
+.Nm Last
+will list the sessions of specified
+.Ar users ,
+.Ar ttys ,
+and
+.Ar hosts ,
+in reverse time order. Each line of output contains
+the user name, the tty from which the session was conducted, any
+hostname, the start and stop times for the session, and the duration
+of the session. If the session is still continuing or was cut short by
+a crash or shutdown,
+.Nm last
+will so indicate.
+.Pp
+.Bl -tag -width indent-two
+.It Fl f Ar file
+.Nm Last
+reads the file
+.Ar file
+instead of the default,
+.Pa /var/log/wtmp .
+.It Fl Ar n
+Limits the report to
+.Ar n
+lines.
+.It Fl t Ar tty
+Specify the
+.Ar tty .
+Tty names may be given fully or abbreviated, for example,
+.Dq Li "last -t 03"
+is
+equivalent to
+.Dq Li "last -t tty03" .
+.It Fl h Ar host
+.Ar Host
+names may be names or internet numbers.
+.El
+.Pp
+If
+multiple arguments are given, the information which applies to any of the
+arguments is printed, e.g.,
+.Dq Li "last root -t console"
+would list all of
+.Dq Li root Ns 's
+sessions as well as all sessions on the console terminal. If no
+users, hostnames or terminals are specified,
+.Nm last
+prints a record of
+all logins and logouts.
+.Pp
+The pseudo-user
+.Ar reboot
+logs in at reboots of the system, thus
+.Dq Li last reboot
+will give an indication of mean time between reboot.
+.Pp
+If
+.Nm last
+is interrupted, it indicates to what date the search has
+progressed. If interrupted with a quit signal
+.Nm last
+indicates how
+far the search has progressed and then continues.
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa /var/log/wtmp
+login data base
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr utmp 5 ,
+.Xr ac 8
+.Sh BUGS
+If a login shell should terminate abnormally for some reason, it is likely
+that a logout record won't be written to the wtmp file. In this case,
+.Nm last
+will indicate the logout time as "shutdown".
+.Sh HISTORY
+.Nm Last
+appeared in
+.Bx 3.0 .
diff --git a/usr.bin/last/last.c b/usr.bin/last/last.c
new file mode 100644
index 0000000..ba7214f
--- /dev/null
+++ b/usr.bin/last/last.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <sys/queue.h>
+
+#define NO 0 /* false/no */
+#define YES 1 /* true/yes */
+
+static struct utmp buf[1024]; /* utmp read buffer */
+
+typedef struct arg {
+ char *name; /* argument */
+#define HOST_TYPE -2
+#define TTY_TYPE -3
+#define USER_TYPE -4
+ int type; /* type of arg */
+ struct arg *next; /* linked list pointer */
+} ARG;
+ARG *arglist; /* head of linked list */
+
+LIST_HEAD(ttylisthead, ttytab) ttylist;
+
+struct ttytab {
+ long logout; /* log out time */
+ char tty[UT_LINESIZE + 1]; /* terminal name */
+ LIST_ENTRY(ttytab) list;
+};
+
+static long currentout, /* current logout value */
+ maxrec; /* records to display */
+static char *file = _PATH_WTMP; /* wtmp file */
+
+void addarg __P((int, char *));
+void hostconv __P((char *));
+void onintr __P((int));
+char *ttyconv __P((char *));
+int want __P((struct utmp *));
+void wtmp __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ int ch;
+ char *p;
+
+ maxrec = -1;
+ while ((ch = getopt(argc, argv, "0123456789f:h:t:")) != EOF)
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * kludge: last was originally designed to take
+ * a number after a dash.
+ */
+ if (maxrec == -1) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ maxrec = atol(++p);
+ else
+ maxrec = atol(argv[optind] + 1);
+ if (!maxrec)
+ exit(0);
+ }
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 'h':
+ hostconv(optarg);
+ addarg(HOST_TYPE, optarg);
+ break;
+ case 't':
+ addarg(TTY_TYPE, ttyconv(optarg));
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n");
+ exit(1);
+ }
+
+ if (argc) {
+ setlinebuf(stdout);
+ for (argv += optind; *argv; ++argv) {
+#define COMPATIBILITY
+#ifdef COMPATIBILITY
+ /* code to allow "last p5" to work */
+ addarg(TTY_TYPE, ttyconv(*argv));
+#endif
+ addarg(USER_TYPE, *argv);
+ }
+ }
+ wtmp();
+ exit(0);
+}
+
+/*
+ * wtmp --
+ * read through the wtmp file
+ */
+void
+wtmp()
+{
+ struct utmp *bp; /* current structure */
+ struct ttytab *tt; /* ttylist entry */
+ struct stat stb; /* stat of file for size */
+ long bl, delta; /* time difference */
+ int bytes, wfd;
+ char *ct, *crmsg;
+
+ LIST_INIT(&ttylist);
+
+ if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1)
+ err(1, "%s", file);
+ bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
+
+ (void)time(&buf[0].ut_time);
+ (void)signal(SIGINT, onintr);
+ (void)signal(SIGQUIT, onintr);
+
+ while (--bl >= 0) {
+ if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 ||
+ (bytes = read(wfd, buf, sizeof(buf))) == -1)
+ err(1, "%s", file);
+ for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
+ /*
+ * if the terminal line is '~', the machine stopped.
+ * see utmp(5) for more info.
+ */
+ if (bp->ut_line[0] == '~' && !bp->ut_line[1]) {
+ /* everybody just logged out */
+ for (tt = ttylist.lh_first; tt; tt = tt->list.le_next) {
+ LIST_REMOVE(tt, list);
+ free(tt);
+ }
+ currentout = -bp->ut_time;
+ crmsg = strncmp(bp->ut_name, "shutdown",
+ UT_NAMESIZE) ? "crash" : "shutdown";
+ if (want(bp)) {
+ ct = ctime(&bp->ut_time);
+ printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
+ UT_NAMESIZE, UT_NAMESIZE,
+ bp->ut_name, UT_LINESIZE,
+ UT_LINESIZE, bp->ut_line,
+ UT_HOSTSIZE, UT_HOSTSIZE,
+ bp->ut_host, ct, ct + 11);
+ if (maxrec != -1 && !--maxrec)
+ return;
+ }
+ continue;
+ }
+ /*
+ * if the line is '{' or '|', date got set; see
+ * utmp(5) for more info.
+ */
+ if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|')
+ && !bp->ut_line[1]) {
+ if (want(bp)) {
+ ct = ctime(&bp->ut_time);
+ printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s \n",
+ UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
+ UT_LINESIZE, UT_LINESIZE, bp->ut_line,
+ UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
+ ct, ct + 11);
+ if (maxrec && !--maxrec)
+ return;
+ }
+ continue;
+ }
+ if (bp->ut_name[0] == '\0' || want(bp)) {
+ /* find associated tty */
+ for (tt = ttylist.lh_first; ; tt = tt->list.le_next) {
+ if (tt == NULL) {
+ /* add new one */
+ tt = malloc(sizeof(struct ttytab));
+ if (tt == NULL)
+ err(1, "malloc failure");
+ tt->logout = currentout;
+ strncpy(tt->tty, bp->ut_line, UT_LINESIZE);
+ LIST_INSERT_HEAD(&ttylist, tt, list);
+ break;
+ }
+ if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE))
+ break;
+ }
+ if (bp->ut_name[0]) {
+ /*
+ * when uucp and ftp log in over a network, the entry in
+ * the utmp file is the name plus their process id. See
+ * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
+ */
+ if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
+ bp->ut_line[3] = '\0';
+ else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
+ bp->ut_line[4] = '\0';
+ ct = ctime(&bp->ut_time);
+ printf("%-*.*s %-*.*s %-*.*s %10.10s %5.5s ",
+ UT_NAMESIZE, UT_NAMESIZE, bp->ut_name,
+ UT_LINESIZE, UT_LINESIZE, bp->ut_line,
+ UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host,
+ ct, ct + 11);
+ if (!tt->logout)
+ puts(" still logged in");
+ else {
+ if (tt->logout < 0) {
+ tt->logout = -tt->logout;
+ printf("- %s", crmsg);
+ }
+ else
+ printf("- %5.5s",
+ ctime(&tt->logout)+11);
+ delta = tt->logout - bp->ut_time;
+ if (delta < 86400)
+ printf(" (%5.5s)\n",
+ asctime(gmtime(&delta))+11);
+ else
+ printf(" (%ld+%5.5s)\n",
+ delta / 86400,
+ asctime(gmtime(&delta))+11);
+ }
+ LIST_REMOVE(tt, list);
+ free(tt);
+ if (maxrec != -1 && !--maxrec)
+ return;
+ } else {
+ tt->logout = bp->ut_time;
+ }
+ }
+ }
+ }
+ ct = ctime(&buf[0].ut_time);
+ printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
+}
+
+/*
+ * want --
+ * see if want this entry
+ */
+int
+want(bp)
+ struct utmp *bp;
+{
+ ARG *step;
+
+ if (!arglist)
+ return (YES);
+
+ for (step = arglist; step; step = step->next)
+ switch(step->type) {
+ case HOST_TYPE:
+ if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE))
+ return (YES);
+ break;
+ case TTY_TYPE:
+ if (!strncmp(step->name, bp->ut_line, UT_LINESIZE))
+ return (YES);
+ break;
+ case USER_TYPE:
+ if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE))
+ return (YES);
+ break;
+ }
+ return (NO);
+}
+
+/*
+ * addarg --
+ * add an entry to a linked list of arguments
+ */
+void
+addarg(type, arg)
+ int type;
+ char *arg;
+{
+ ARG *cur;
+
+ if (!(cur = (ARG *)malloc((u_int)sizeof(ARG))))
+ err(1, "malloc failure");
+ cur->next = arglist;
+ cur->type = type;
+ cur->name = arg;
+ arglist = cur;
+}
+
+/*
+ * hostconv --
+ * convert the hostname to search pattern; if the supplied host name
+ * has a domain attached that is the same as the current domain, rip
+ * off the domain suffix since that's what login(1) does.
+ */
+void
+hostconv(arg)
+ char *arg;
+{
+ static int first = 1;
+ static char *hostdot, name[MAXHOSTNAMELEN];
+ char *argdot;
+
+ if (!(argdot = strchr(arg, '.')))
+ return;
+ if (first) {
+ first = 0;
+ if (gethostname(name, sizeof(name)))
+ err(1, "gethostname");
+ hostdot = strchr(name, '.');
+ }
+ if (hostdot && !strcasecmp(hostdot, argdot))
+ *argdot = '\0';
+}
+
+/*
+ * ttyconv --
+ * convert tty to correct name.
+ */
+char *
+ttyconv(arg)
+ char *arg;
+{
+ char *mval;
+
+ /*
+ * kludge -- we assume that all tty's end with
+ * a two character suffix.
+ */
+ if (strlen(arg) == 2) {
+ /* either 6 for "ttyxx" or 8 for "console" */
+ if (!(mval = malloc((u_int)8)))
+ err(1, "malloc failure");
+ if (!strcmp(arg, "co"))
+ (void)strcpy(mval, "console");
+ else {
+ (void)strcpy(mval, "tty");
+ (void)strcpy(mval + 3, arg);
+ }
+ return (mval);
+ }
+ if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1))
+ return (arg + 5);
+ return (arg);
+}
+
+/*
+ * onintr --
+ * on interrupt, we inform the user how far we've gotten
+ */
+void
+onintr(signo)
+ int signo;
+{
+ char *ct;
+
+ ct = ctime(&buf[0].ut_time);
+ printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
+ if (signo == SIGINT)
+ exit(1);
+ (void)fflush(stdout); /* fix required for rsh */
+}
diff --git a/usr.bin/lastcomm/Makefile b/usr.bin/lastcomm/Makefile
new file mode 100644
index 0000000..9061573
--- /dev/null
+++ b/usr.bin/lastcomm/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lastcomm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lastcomm/lastcomm.1 b/usr.bin/lastcomm/lastcomm.1
new file mode 100644
index 0000000..1542b0d
--- /dev/null
+++ b/usr.bin/lastcomm/lastcomm.1
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lastcomm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LASTCOMM 1
+.Os BSD 3
+.Sh NAME
+.Nm lastcomm
+.Nd show last commands executed in reverse order
+.Sh SYNOPSIS
+.Nm lastcomm
+.Op Fl f Ar file
+.Op Ar command ...
+.Op Ar user ...
+.Op Ar terminal ...
+.Sh DESCRIPTION
+.Nm Lastcomm
+gives information on previously executed commands.
+With no arguments,
+.Nm lastcomm
+prints information about all the commands recorded
+during the current accounting file's lifetime.
+.Pp
+Option:
+.Pp
+.Bl -tag -width Fl
+.It Fl f Ar file
+Read from
+.Ar file
+rather than the default
+accounting file.
+.El
+.Pp
+If called with arguments, only accounting entries with a
+matching
+.Ar command
+name,
+.Ar user
+name,
+or
+.Ar terminal
+name
+are printed.
+So, for example:
+.Pp
+.Dl lastcomm a.out root ttyd0
+.Pp
+would produce a listing of all the
+executions of commands named
+.Pa a.out
+by user
+.Ar root
+on the terminal
+.Ar ttyd0 .
+.Pp
+For each process entry, the following are printed.
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+The name of the user who ran the process.
+.It
+Flags, as accumulated by the accounting facilities in the system.
+.It
+The command name under which the process was called.
+.It
+The amount of cpu time used by the process (in seconds).
+.It
+The time the process exited.
+.El
+.Pp
+The flags are encoded as follows: ``S'' indicates the command was
+executed by the super-user, ``F'' indicates the command ran after
+a fork, but without a following
+.Xr exec ,
+``C'' indicates the command was run in PDP-11 compatibility mode
+(VAX only),
+``D'' indicates the command terminated with the generation of a
+.Pa core
+file, and ``X'' indicates the command was terminated with a signal.
+.Sh FILES
+.Bl -tag -width /var/account/acct -compact
+.It Pa /var/account/acct
+Default accounting file.
+.El
+.Sh SEE ALSO
+.Xr last 1 ,
+.Xr sigvec 2 ,
+.Xr acct 5 ,
+.Xr core 5
+.Sh HISTORY
+The
+.Nm lastcomm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/lastcomm/lastcomm.c b/usr.bin/lastcomm/lastcomm.c
new file mode 100644
index 0000000..08f971d
--- /dev/null
+++ b/usr.bin/lastcomm/lastcomm.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lastcomm.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/acct.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <struct.h>
+#include <unistd.h>
+#include <utmp.h>
+#include "pathnames.h"
+
+time_t expand __P((u_int));
+char *flagbits __P((int));
+char *getdev __P((dev_t));
+int requested __P((char *[], struct acct *));
+void usage __P((void));
+char *user_from_uid();
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *p;
+ struct acct ab;
+ struct stat sb;
+ FILE *fp;
+ off_t size;
+ time_t t;
+ int ch;
+ char *acctfile;
+
+ acctfile = _PATH_ACCT;
+ while ((ch = getopt(argc, argv, "f:")) != EOF)
+ switch((char)ch) {
+ case 'f':
+ acctfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Open the file. */
+ if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb))
+ err(1, "%s", acctfile);
+
+ /*
+ * Round off to integral number of accounting records, probably
+ * not necessary, but it doesn't hurt.
+ */
+ size = sb.st_size - sb.st_size % sizeof(struct acct);
+
+ /* Check if any records to display. */
+ if (size < sizeof(struct acct))
+ exit(0);
+
+ /*
+ * Seek to before the last entry in the file; use lseek(2) in case
+ * the file is bigger than a "long".
+ */
+ size -= sizeof(struct acct);
+ if (lseek(fileno(fp), size, SEEK_SET) == -1)
+ err(1, "%s", acctfile);
+
+ for (;;) {
+ if (fread(&ab, sizeof(struct acct), 1, fp) != 1)
+ err(1, "%s", acctfile);
+
+ if (fseek(fp, 2 * -(long)sizeof(struct acct), SEEK_CUR) == -1)
+ err(1, "%s", acctfile);
+
+ if (size == 0)
+ break;
+ size -= sizeof(struct acct);
+
+ if (ab.ac_comm[0] == '\0') {
+ ab.ac_comm[0] = '?';
+ ab.ac_comm[1] = '\0';
+ } else
+ for (p = &ab.ac_comm[0];
+ p < &ab.ac_comm[fldsiz(acct, ac_comm)] && *p; ++p)
+ if (!isprint(*p))
+ *p = '?';
+ if (*argv && !requested(argv, &ab))
+ continue;
+
+ t = expand(ab.ac_utime) + expand(ab.ac_stime);
+ (void)printf("%-*.*s %-7s %-*s %-*s %6.2f secs %.16s\n",
+ fldsiz(acct, ac_comm), fldsiz(acct, ac_comm),
+ ab.ac_comm, flagbits(ab.ac_flag),
+ UT_NAMESIZE, user_from_uid(ab.ac_uid, 0),
+ UT_LINESIZE, getdev(ab.ac_tty),
+ t / (double)AHZ, ctime(&ab.ac_btime));
+ }
+ exit(0);
+}
+
+time_t
+expand(t)
+ u_int t;
+{
+ register time_t nt;
+
+ nt = t & 017777;
+ t >>= 13;
+ while (t) {
+ t--;
+ nt <<= 3;
+ }
+ return (nt);
+}
+
+char *
+flagbits(f)
+ register int f;
+{
+ static char flags[20] = "-";
+ char *p;
+
+#define BIT(flag, ch) if (f & flag) *p++ = ch
+
+ p = flags + 1;
+ BIT(ASU, 'S');
+ BIT(AFORK, 'F');
+ BIT(ACOMPAT, 'C');
+ BIT(ACORE, 'D');
+ BIT(AXSIG, 'X');
+ *p = '\0';
+ return (flags);
+}
+
+int
+requested(argv, acp)
+ register char *argv[];
+ register struct acct *acp;
+{
+ register char *p;
+
+ do {
+ p = user_from_uid(acp->ac_uid, 0);
+ if (!strcmp(p, *argv))
+ return (1);
+ if ((p = getdev(acp->ac_tty)) && !strcmp(p, *argv))
+ return (1);
+ if (!strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm)))
+ return (1);
+ } while (*++argv);
+ return (0);
+}
+
+char *
+getdev(dev)
+ dev_t dev;
+{
+ static dev_t lastdev = (dev_t)-1;
+ static char *lastname;
+
+ if (dev == NODEV) /* Special case. */
+ return ("__");
+ if (dev == lastdev) /* One-element cache. */
+ return (lastname);
+ lastdev = dev;
+ lastname = devname(dev, S_IFCHR);
+ return (lastname);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "lastcomm [ -f file ] [command ...] [user ...] [tty ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/lastcomm/pathnames.h b/usr.bin/lastcomm/pathnames.h
new file mode 100644
index 0000000..0737970
--- /dev/null
+++ b/usr.bin/lastcomm/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_ACCT "/var/account/acct"
diff --git a/usr.bin/ld/Makefile b/usr.bin/ld/Makefile
new file mode 100644
index 0000000..834a5c9
--- /dev/null
+++ b/usr.bin/ld/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ld
+SRCS= ld.c cplus-dem.c
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ld/cplus-dem.c b/usr.bin/ld/cplus-dem.c
new file mode 100644
index 0000000..b2e3050
--- /dev/null
+++ b/usr.bin/ld/cplus-dem.c
@@ -0,0 +1,970 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cplus-dem.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* Demangler for GNU C++
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ written by James Clark (jjc@jclark.uucp)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* This is for g++ 1.36.1 (November 6 version). It will probably
+ require changes for any other version.
+
+ Modified for g++ 1.36.2 (November 18 version). */
+
+/* This file exports one function
+
+ char *cplus_demangle (const char *name)
+
+ If `name' is a mangled function name produced by g++, then
+ a pointer to a malloced string giving a C++ representation
+ of the name will be returned; otherwise NULL will be returned.
+ It is the caller's responsibility to free the string which
+ is returned.
+
+ For example,
+
+ cplus_demangle ("_foo__1Ai")
+
+ returns
+
+ "A::foo(int)"
+
+ This file imports xmalloc and xrealloc, which are like malloc and
+ realloc except that they generate a fatal error if there is no
+ available memory. */
+
+/* #define nounderscore 1 /* define this is names don't start with _ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#ifdef USG
+#include <memory.h>
+#include <string.h>
+#else
+#include <strings.h>
+#define memcpy(s1, s2, n) bcopy ((s2), (s1), (n))
+#define memcmp(s1, s2, n) bcmp ((s2), (s1), (n))
+#define strchr index
+#define strrchr rindex
+#endif
+
+#ifdef __STDC__
+extern char *cplus_demangle (const char *type);
+#else
+extern char *cplus_demangle ();
+#endif
+
+#ifdef __STDC__
+extern char *xmalloc (int);
+extern char *xrealloc (char *, int);
+#else
+extern char *xmalloc ();
+extern char *xrealloc ();
+#endif
+
+static char **typevec = 0;
+static int ntypes = 0;
+static int typevec_size = 0;
+
+static struct {
+ const char *in;
+ const char *out;
+} optable[] = {
+ "new", " new",
+ "delete", " delete",
+ "ne", "!=",
+ "eq", "==",
+ "ge", ">=",
+ "gt", ">",
+ "le", "<=",
+ "lt", "<",
+ "plus", "+",
+ "minus", "-",
+ "mult", "*",
+ "convert", "+", /* unary + */
+ "negate", "-", /* unary - */
+ "trunc_mod", "%",
+ "trunc_div", "/",
+ "truth_andif", "&&",
+ "truth_orif", "||",
+ "truth_not", "!",
+ "postincrement", "++",
+ "postdecrement", "--",
+ "bit_ior", "|",
+ "bit_xor", "^",
+ "bit_and", "&",
+ "bit_not", "~",
+ "call", "()",
+ "cond", "?:",
+ "alshift", "<<",
+ "arshift", ">>",
+ "component", "->",
+ "indirect", "*",
+ "method_call", "->()",
+ "addr", "&", /* unary & */
+ "array", "[]",
+ "nop", "", /* for operator= */
+};
+
+/* Beware: these aren't '\0' terminated. */
+
+typedef struct {
+ char *b; /* pointer to start of string */
+ char *p; /* pointer after last character */
+ char *e; /* pointer after end of allocated space */
+} string;
+
+#ifdef __STDC__
+static void string_need (string *s, int n);
+static void string_delete (string *s);
+static void string_init (string *s);
+static void string_clear (string *s);
+static int string_empty (string *s);
+static void string_append (string *p, const char *s);
+static void string_appends (string *p, string *s);
+static void string_appendn (string *p, const char *s, int n);
+static void string_prepend (string *p, const char *s);
+#if 0
+static void string_prepends (string *p, string *s);
+#endif
+static void string_prependn (string *p, const char *s, int n);
+static int get_count (const char **type, int *count);
+static int do_args (const char **type, string *decl);
+static int do_type (const char **type, string *result);
+static int do_arg (const char **type, string *result);
+static int do_args (const char **type, string *decl);
+static void munge_function_name (string *name);
+static void remember_type (const char *type, int len);
+#else
+static void string_need ();
+static void string_delete ();
+static void string_init ();
+static void string_clear ();
+static int string_empty ();
+static void string_append ();
+static void string_appends ();
+static void string_appendn ();
+static void string_prepend ();
+static void string_prepends ();
+static void string_prependn ();
+static int get_count ();
+static int do_args ();
+static int do_type ();
+static int do_arg ();
+static int do_args ();
+static void munge_function_name ();
+static void remember_type ();
+#endif
+
+char *
+cplus_demangle (type)
+ const char *type;
+{
+ string decl;
+ int n;
+ int success = 0;
+ int constructor = 0;
+ int const_flag = 0;
+ int i;
+ const char *p;
+#ifndef LONGERNAMES
+ const char *premangle;
+#endif
+
+ if (type == NULL || *type == '\0')
+ return NULL;
+#ifndef nounderscore
+ if (*type++ != '_')
+ return NULL;
+#endif
+ p = type;
+ while (*p != '\0' && !(*p == '_' && p[1] == '_'))
+ p++;
+ if (*p == '\0')
+ {
+ /* destructor */
+ if (type[0] == '_' && type[1] == '$' && type[2] == '_')
+ {
+ int n = (strlen (type) - 3)*2 + 3 + 2 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 3);
+ strcat (tem, "::~");
+ strcat (tem, type + 3);
+ strcat (tem, "()");
+ return tem;
+ }
+ /* static data member */
+ if (*type != '_' && (p = strchr (type, '$')) != NULL)
+ {
+ int n = strlen (type) + 2;
+ char *tem = (char *) xmalloc (n);
+ memcpy (tem, type, p - type);
+ strcpy (tem + (p - type), "::");
+ strcpy (tem + (p - type) + 2, p + 1);
+ return tem;
+ }
+ /* virtual table */
+ if (type[0] == '_' && type[1] == 'v' && type[2] == 't' && type[3] == '$')
+ {
+ int n = strlen (type + 4) + 14 + 1;
+ char *tem = (char *) xmalloc (n);
+ strcpy (tem, type + 4);
+ strcat (tem, " virtual table");
+ return tem;
+ }
+ return NULL;
+ }
+
+ string_init (&decl);
+
+ if (p == type)
+ {
+ if (!isdigit (p[2]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ constructor = 1;
+ }
+ else
+ {
+ string_appendn (&decl, type, p - type);
+ munge_function_name (&decl);
+ }
+ p += 2;
+
+#ifndef LONGERNAMES
+ premangle = p;
+#endif
+ switch (*p)
+ {
+ case 'C':
+ /* a const member function */
+ if (!isdigit (p[1]))
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ p += 1;
+ const_flag = 1;
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (strlen (p) < n)
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+ if (constructor)
+ {
+ string_appendn (&decl, p, n);
+ string_append (&decl, "::");
+ string_appendn (&decl, p, n);
+ }
+ else
+ {
+ string_prepend (&decl, "::");
+ string_prependn (&decl, p, n);
+ }
+ p += n;
+#ifndef LONGERNAMES
+ remember_type (premangle, p - premangle);
+#endif
+ success = do_args (&p, &decl);
+ if (const_flag)
+ string_append (&decl, " const");
+ break;
+ case 'F':
+ p += 1;
+ success = do_args (&p, &decl);
+ break;
+ }
+
+ for (i = 0; i < ntypes; i++)
+ if (typevec[i] != NULL)
+ free (typevec[i]);
+ ntypes = 0;
+ if (typevec != NULL)
+ {
+ free ((char *)typevec);
+ typevec = NULL;
+ typevec_size = 0;
+ }
+
+ if (success)
+ {
+ string_appendn (&decl, "", 1);
+ return decl.b;
+ }
+ else
+ {
+ string_delete (&decl);
+ return NULL;
+ }
+}
+
+static int
+get_count (type, count)
+ const char **type;
+ int *count;
+{
+ if (!isdigit (**type))
+ return 0;
+ *count = **type - '0';
+ *type += 1;
+ /* see flush_repeats in cplus-method.c */
+ if (isdigit (**type))
+ {
+ const char *p = *type;
+ int n = *count;
+ do
+ {
+ n *= 10;
+ n += *p - '0';
+ p += 1;
+ }
+ while (isdigit (*p));
+ if (*p == '_')
+ {
+ *type = p + 1;
+ *count = n;
+ }
+ }
+ return 1;
+}
+
+/* result will be initialised here; it will be freed on failure */
+
+static int
+do_type (type, result)
+ const char **type;
+ string *result;
+{
+ int n;
+ int done;
+ int non_empty = 0;
+ int success;
+ string decl;
+ const char *remembered_type;
+
+ string_init (&decl);
+ string_init (result);
+
+ done = 0;
+ success = 1;
+ while (success && !done)
+ {
+ int member;
+ switch (**type)
+ {
+ case 'P':
+ *type += 1;
+ string_prepend (&decl, "*");
+ break;
+
+ case 'R':
+ *type += 1;
+ string_prepend (&decl, "&");
+ break;
+
+ case 'T':
+ *type += 1;
+ if (!get_count (type, &n) || n >= ntypes)
+ success = 0;
+ else
+ {
+ remembered_type = typevec[n];
+ type = &remembered_type;
+ }
+ break;
+
+ case 'F':
+ *type += 1;
+ if (!string_empty (&decl) && decl.b[0] == '*')
+ {
+ string_prepend (&decl, "(");
+ string_append (&decl, ")");
+ }
+ if (!do_args (type, &decl) || **type != '_')
+ success = 0;
+ else
+ *type += 1;
+ break;
+
+ case 'M':
+ case 'O':
+ {
+ int constp = 0;
+ int volatilep = 0;
+
+ member = **type == 'M';
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ string_append (&decl, ")");
+ string_prepend (&decl, "::");
+ string_prependn (&decl, *type, n);
+ string_prepend (&decl, "(");
+ *type += n;
+ if (member)
+ {
+ if (**type == 'C')
+ {
+ *type += 1;
+ constp = 1;
+ }
+ if (**type == 'V')
+ {
+ *type += 1;
+ volatilep = 1;
+ }
+ if (*(*type)++ != 'F')
+ {
+ success = 0;
+ break;
+ }
+ }
+ if ((member && !do_args (type, &decl)) || **type != '_')
+ {
+ success = 0;
+ break;
+ }
+ *type += 1;
+ if (constp)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "const");
+ }
+ if (volatilep)
+ {
+ if (non_empty)
+ string_append (&decl, " ");
+ else
+ non_empty = 1;
+ string_append (&decl, "volatilep");
+ }
+ break;
+ }
+
+ case 'C':
+ if ((*type)[1] == 'P')
+ {
+ *type += 1;
+ if (!string_empty (&decl))
+ string_prepend (&decl, " ");
+ string_prepend (&decl, "const");
+ break;
+ }
+
+ /* fall through */
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ done = 0;
+ non_empty = 0;
+ while (success && !done)
+ {
+ switch (**type)
+ {
+ case 'C':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "const");
+ break;
+ case 'U':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "unsigned");
+ break;
+ case 'V':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ else
+ non_empty = 1;
+ string_append (result, "volatile");
+ break;
+ default:
+ done = 1;
+ break;
+ }
+ }
+
+ if (success)
+ switch (**type)
+ {
+ case '\0':
+ case '_':
+ break;
+ case 'v':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "void");
+ break;
+ case 'x':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long long");
+ break;
+ case 'l':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long");
+ break;
+ case 'i':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "int");
+ break;
+ case 's':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "short");
+ break;
+ case 'c':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "char");
+ break;
+ case 'r':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "long double");
+ break;
+ case 'd':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "double");
+ break;
+ case 'f':
+ *type += 1;
+ if (non_empty)
+ string_append (result, " ");
+ string_append (result, "float");
+ break;
+ case 'G':
+ *type += 1;
+ if (!isdigit (**type))
+ {
+ success = 0;
+ break;
+ }
+ /* fall through */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ do
+ {
+ n *= 10;
+ n += **type - '0';
+ *type += 1;
+ }
+ while (isdigit (**type));
+ if (strlen (*type) < n)
+ {
+ success = 0;
+ break;
+ }
+ if (non_empty)
+ string_append (result, " ");
+ string_appendn (result, *type, n);
+ *type += n;
+ break;
+ default:
+ success = 0;
+ break;
+ }
+
+ if (success)
+ {
+ if (!string_empty (&decl))
+ {
+ string_append (result, " ");
+ string_appends (result, &decl);
+ }
+ string_delete (&decl);
+ return 1;
+ }
+ else
+ {
+ string_delete (&decl);
+ string_delete (result);
+ return 0;
+ }
+}
+
+/* `result' will be initialised in do_type; it will be freed on failure */
+
+static int
+do_arg (type, result)
+ const char **type;
+ string *result;
+{
+ const char *start = *type;
+
+ if (!do_type (type, result))
+ return 0;
+ remember_type (start, *type - start);
+ return 1;
+}
+
+static void
+remember_type (start, len)
+ const char *start;
+ int len;
+{
+ char *tem;
+
+ if (ntypes >= typevec_size)
+ {
+ if (typevec_size == 0)
+ {
+ typevec_size = 3;
+ typevec = (char **) xmalloc (sizeof (char*)*typevec_size);
+ }
+ else
+ {
+ typevec_size *= 2;
+ typevec = (char **) xrealloc ((char *)typevec, sizeof (char*)*typevec_size);
+ }
+ }
+ tem = (char *) xmalloc (len + 1);
+ memcpy (tem, start, len);
+ tem[len] = '\0';
+ typevec[ntypes++] = tem;
+}
+
+/* `decl' must be already initialised, usually non-empty;
+ it won't be freed on failure */
+
+static int
+do_args (type, decl)
+ const char **type;
+ string *decl;
+{
+ string arg;
+ int need_comma = 0;
+
+ string_append (decl, "(");
+
+ while (**type != '_' && **type != '\0' && **type != 'e' && **type != 'v')
+ {
+ if (**type == 'N')
+ {
+ int r;
+ int t;
+ *type += 1;
+ if (!get_count (type, &r) || !get_count (type, &t) || t >= ntypes)
+ return 0;
+ while (--r >= 0)
+ {
+ const char *tem = typevec[t];
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (&tem, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+ else
+ {
+ if (need_comma)
+ string_append (decl, ", ");
+ if (!do_arg (type, &arg))
+ return 0;
+ string_appends (decl, &arg);
+ string_delete (&arg);
+ need_comma = 1;
+ }
+ }
+
+ if (**type == 'v')
+ *type += 1;
+ else if (**type == 'e')
+ {
+ *type += 1;
+ if (need_comma)
+ string_append (decl, ",");
+ string_append (decl, "...");
+ }
+
+ string_append (decl, ")");
+ return 1;
+}
+
+static void
+munge_function_name (name)
+ string *name;
+{
+ if (!string_empty (name) && name->p - name->b >= 3
+ && name->b[0] == 'o' && name->b[1] == 'p' && name->b[2] == '$')
+ {
+ int i;
+ /* see if it's an assignment expression */
+ if (name->p - name->b >= 10 /* op$assign_ */
+ && memcmp (name->b + 3, "assign_", 7) == 0)
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 10;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 10, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ string_append (name, "=");
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < sizeof (optable)/sizeof (optable[0]); i++)
+ {
+ int len = name->p - name->b - 3;
+ if (strlen (optable[i].in) == len
+ && memcmp (optable[i].in, name->b + 3, len) == 0)
+ {
+ string_clear (name);
+ string_append (name, "operator");
+ string_append (name, optable[i].out);
+ return;
+ }
+ }
+ }
+ return;
+ }
+ else if (!string_empty (name) && name->p - name->b >= 5
+ && memcmp (name->b, "type$", 5) == 0)
+ {
+ /* type conversion operator */
+ string type;
+ const char *tem = name->b + 5;
+ if (do_type (&tem, &type))
+ {
+ string_clear (name);
+ string_append (name, "operator ");
+ string_appends (name, &type);
+ string_delete (&type);
+ return;
+ }
+ }
+}
+
+/* a mini string-handling package */
+
+static void
+string_need (s, n)
+ string *s;
+ int n;
+{
+ if (s->b == NULL)
+ {
+ if (n < 32)
+ n = 32;
+ s->p = s->b = (char *) xmalloc (n);
+ s->e = s->b + n;
+ }
+ else if (s->e - s->p < n)
+ {
+ int tem = s->p - s->b;
+ n += tem;
+ n *= 2;
+ s->b = (char *) xrealloc (s->b, n);
+ s->p = s->b + tem;
+ s->e = s->b + n;
+ }
+}
+
+static void
+string_delete (s)
+ string *s;
+{
+ if (s->b != NULL)
+ {
+ free (s->b);
+ s->b = s->e = s->p = NULL;
+ }
+}
+
+static void
+string_init (s)
+ string *s;
+{
+ s->b = s->p = s->e = NULL;
+}
+
+static void
+string_clear (s)
+ string *s;
+{
+ s->p = s->b;
+}
+
+static int
+string_empty (s)
+ string *s;
+{
+ return s->b == s->p;
+}
+
+static void
+string_append (p, s)
+ string *p;
+ const char *s;
+{
+ int n;
+ if (s == NULL || *s == '\0')
+ return;
+ n = strlen (s);
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_appends (p, s)
+ string *p, *s;
+{
+ int n;
+ if (s->b == s->p)
+ return;
+ n = s->p - s->b;
+ string_need (p, n);
+ memcpy (p->p, s->b, n);
+ p->p += n;
+}
+
+static void
+string_appendn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ if (n == 0)
+ return;
+ string_need (p, n);
+ memcpy (p->p, s, n);
+ p->p += n;
+}
+
+static void
+string_prepend (p, s)
+ string *p;
+ const char *s;
+{
+ if (s == NULL || *s == '\0')
+ return;
+ string_prependn (p, s, strlen (s));
+}
+
+#if 0
+static void
+string_prepends (p, s)
+ string *p, *s;
+{
+ if (s->b == s->p)
+ return;
+ string_prependn (p, s->b, s->p - s->b);
+}
+#endif
+
+static void
+string_prependn (p, s, n)
+ string *p;
+ const char *s;
+ int n;
+{
+ char *q;
+
+ if (n == 0)
+ return;
+ string_need (p, n);
+ for (q = p->p - 1; q >= p->b; q--)
+ q[n] = q[0];
+ memcpy (p->b, s, n);
+ p->p += n;
+}
diff --git a/usr.bin/ld/ld.c b/usr.bin/ld/ld.c
new file mode 100644
index 0000000..f0b35f2
--- /dev/null
+++ b/usr.bin/ld/ld.c
@@ -0,0 +1,4718 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ld.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* Linker `ld' for GNU
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Written by Richard Stallman with some help from Eric Albert.
+ Set, indirect, and warning symbol features added by Randy Smith. */
+
+/* Define how to initialize system-dependent header fields. */
+
+#include <ar.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <stab.h>
+#include <string.h>
+
+/* symseg.h defines the obsolete GNU debugging format; we should nuke it. */
+#define CORE_ADDR unsigned long /* For symseg.h */
+#include "symseg.h"
+
+#define N_SET_MAGIC(exec, val) ((exec).a_magic = val)
+
+/* If compiled with GNU C, use the built-in alloca */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#endif
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+/* Macro to control the number of undefined references printed */
+#define MAX_UREFS_PRINTED 10
+
+/* Size of a page; obtained from the operating system. */
+
+int page_size;
+
+/* Name this program was invoked by. */
+
+char *progname;
+
+/* System dependencies */
+
+/* Define this to specify the default executable format. */
+
+#ifndef DEFAULT_MAGIC
+#define DEFAULT_MAGIC ZMAGIC
+#endif
+
+#if defined(hp300) || defined(luna68k)
+#define INITIALIZE_HEADER outheader.a_mid = MID_HP300
+#endif
+
+#ifdef sparc
+#ifndef sun
+#define sun 1
+#endif
+#define INITIALIZE_HEADER \
+ (outheader.a_mid = MID_SUN_SPARC, outheader.a_toolversion = 1)
+#endif
+
+/*
+ * Ok. Following are the relocation information macros. If your
+ * system should not be able to use the default set (below), you must
+ * define the following:
+
+ * relocation_info: This must be typedef'd (or #define'd) to the type
+ * of structure that is stored in the relocation info section of your
+ * a.out files. Often this is defined in the a.out.h for your system.
+ *
+ * RELOC_ADDRESS (rval): Offset into the current section of the
+ * <whatever> to be relocated. *Must be an lvalue*.
+ *
+ * RELOC_EXTERN_P (rval): Is this relocation entry based on an
+ * external symbol (1), or was it fully resolved upon entering the
+ * loader (0) in which case some combination of the value in memory
+ * (if RELOC_MEMORY_ADD_P) and the extra (if RELOC_ADD_EXTRA) contains
+ * what the value of the relocation actually was. *Must be an lvalue*.
+ *
+ * RELOC_TYPE (rval): If this entry was fully resolved upon
+ * entering the loader, what type should it be relocated as?
+ *
+ * RELOC_SYMBOL (rval): If this entry was not fully resolved upon
+ * entering the loader, what is the index of it's symbol in the symbol
+ * table? *Must be a lvalue*.
+ *
+ * RELOC_MEMORY_ADD_P (rval): This should return true if the final
+ * relocation value output here should be added to memory, or if the
+ * section of memory described should simply be set to the relocation
+ * value.
+ *
+ * RELOC_ADD_EXTRA (rval): (Optional) This macro, if defined, gives
+ * an extra value to be added to the relocation value based on the
+ * individual relocation entry. *Must be an lvalue if defined*.
+ *
+ * RELOC_PCREL_P (rval): True if the relocation value described is
+ * pc relative.
+ *
+ * RELOC_VALUE_RIGHTSHIFT (rval): Number of bits right to shift the
+ * final relocation value before putting it where it belongs.
+ *
+ * RELOC_TARGET_SIZE (rval): log to the base 2 of the number of
+ * bytes of size this relocation entry describes; 1 byte == 0; 2 bytes
+ * == 1; 4 bytes == 2, and etc. This is somewhat redundant (we could
+ * do everything in terms of the bit operators below), but having this
+ * macro could end up producing better code on machines without fancy
+ * bit twiddling. Also, it's easier to understand/code big/little
+ * endian distinctions with this macro.
+ *
+ * RELOC_TARGET_BITPOS (rval): The starting bit position within the
+ * object described in RELOC_TARGET_SIZE in which the relocation value
+ * will go.
+ *
+ * RELOC_TARGET_BITSIZE (rval): How many bits are to be replaced
+ * with the bits of the relocation value. It may be assumed by the
+ * code that the relocation value will fit into this many bits. This
+ * may be larger than RELOC_TARGET_SIZE if such be useful.
+ *
+ *
+ * Things I haven't implemented
+ * ----------------------------
+ *
+ * Values for RELOC_TARGET_SIZE other than 0, 1, or 2.
+ *
+ * Pc relative relocation for External references.
+ *
+ *
+ */
+
+/* The following #if has been modifed for cross compilation */
+/* It originally read: #if defined(sun) && defined(sparc) */
+/* Marc Ullman, Stanford University Nov. 1 1989 */
+#if defined(sun) && (TARGET == SUN4)
+/* Sparc (Sun 4) macros */
+#undef relocation_info
+#define relocation_info reloc_info_sparc
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_TYPE(r) ((r)->r_index)
+#define RELOC_SYMBOL(r) ((r)->r_index)
+#define RELOC_MEMORY_SUB_P(r) 0
+#define RELOC_MEMORY_ADD_P(r) 0
+#define RELOC_ADD_EXTRA(r) ((r)->r_addend)
+#define RELOC_PCREL_P(r) \
+ ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
+#define RELOC_VALUE_RIGHTSHIFT(r) (reloc_target_rightshift[(r)->r_type])
+#define RELOC_TARGET_SIZE(r) (reloc_target_size[(r)->r_type])
+#define RELOC_TARGET_BITPOS(r) 0
+#define RELOC_TARGET_BITSIZE(r) (reloc_target_bitsize[(r)->r_type])
+
+/* Note that these are very dependent on the order of the enums in
+ enum reloc_type (in a.out.h); if they change the following must be
+ changed */
+/* Also note that the last few may be incorrect; I have no information */
+static int reloc_target_rightshift[] = {
+ 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
+};
+static int reloc_target_size[] = {
+ 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+};
+static int reloc_target_bitsize[] = {
+ 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
+};
+
+#define MAX_ALIGNMENT (sizeof (double))
+#endif
+
+/* Default macros */
+#ifndef RELOC_ADDRESS
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_TYPE(r) ((r)->r_symbolnum)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) 0
+#define RELOC_MEMORY_ADD_P(r) 1
+#undef RELOC_ADD_EXTRA
+#define RELOC_PCREL_P(r) ((r)->r_pcrel)
+#define RELOC_VALUE_RIGHTSHIFT(r) 0
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#define RELOC_TARGET_BITPOS(r) 0
+#define RELOC_TARGET_BITSIZE(r) 32
+#endif
+
+#ifndef MAX_ALIGNMENT
+#define MAX_ALIGNMENT (sizeof (int))
+#endif
+
+#ifdef nounderscore
+#define LPREFIX '.'
+#else
+#define LPREFIX 'L'
+#endif
+
+#ifndef TEXT_START
+#define TEXT_START(x) N_TXTADDR(x)
+#endif
+
+/* Special global symbol types understood by GNU LD. */
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition.
+
+ So, for example, the following two lines placed in an assembler
+ input file would result in an object file which would direct gnu ld
+ to resolve all references to symbol "foo" as references to symbol
+ "bar".
+
+ .stabs "_foo",11,0,0,0
+ .stabs "_bar",1,0,0,0
+
+ Note that (11 == (N_INDR | N_EXT)) and (1 == (N_UNDF | N_EXT)). */
+
+#ifndef N_INDR
+#define N_INDR 0xa
+#endif
+
+/* The following symbols refer to set elements. These are expected
+ only in input to the loader; they should not appear in loader
+ output (unless relocatable output is requested). To be recognized
+ by the loader, the input symbols must have their N_EXT bit set.
+ All the N_SET[ATDB] symbols with the same name form one set. The
+ loader collects all of these elements at load time and outputs a
+ vector for each name.
+ Space (an array of 32 bit words) is allocated for the set in the
+ data section, and the n_value field of each set element value is
+ stored into one word of the array.
+ The first word of the array is the length of the set (number of
+ elements). The last word of the vector is set to zero for possible
+ use by incremental loaders. The array is ordered by the linkage
+ order; the first symbols which the linker encounters will be first
+ in the array.
+
+ In C syntax this looks like:
+
+ struct set_vector {
+ unsigned int length;
+ unsigned int vector[length];
+ unsigned int always_zero;
+ };
+
+ Before being placed into the array, each element is relocated
+ according to its type. This allows the loader to create an array
+ of pointers to objects automatically. N_SETA type symbols will not
+ be relocated.
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references.
+
+ For the purposes of determining whether or not to load in a library
+ file, set element definitions are not considered "real
+ definitions"; they will not cause the loading of a library
+ member.
+
+ If relocatable output is requested, none of this processing is
+ done. The symbols are simply relocated and passed through to the
+ output file.
+
+ So, for example, the following three lines of assembler code
+ (whether in one file or scattered between several different ones)
+ will produce a three element vector (total length is five words;
+ see above), referenced by the symbol "_xyzzy", which will have the
+ addresses of the routines _init1, _init2, and _init3.
+
+ *NOTE*: If symbolic addresses are used in the n_value field of the
+ defining .stabs, those symbols must be defined in the same file as
+ that containing the .stabs.
+
+ .stabs "_xyzzy",23,0,0,_init1
+ .stabs "_xyzzy",23,0,0,_init2
+ .stabs "_xyzzy",23,0,0,_init3
+
+ Note that (23 == (N_SETT | N_EXT)). */
+
+#ifndef N_SETA
+#define N_SETA 0x14 /* Absolute set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETT
+#define N_SETT 0x16 /* Text set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETD
+#define N_SETD 0x18 /* Data set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+#ifndef N_SETB
+#define N_SETB 0x1A /* Bss set element symbol */
+#endif /* This is input to LD, in a .o file. */
+
+/* Macros dealing with the set element symbols defined in a.out.h */
+#define SET_ELEMENT_P(x) ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT))
+#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS)
+
+#ifndef N_SETV
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+#endif /* This is output from LD. */
+
+/* If a this type of symbol is encountered, its name is a warning
+ message to print each time the symbol referenced by the next symbol
+ table entry is referenced.
+
+ This feature may be used to allow backwards compatibility with
+ certain functions (eg. gets) but to discourage programmers from
+ their use.
+
+ So if, for example, you wanted to have ld print a warning whenever
+ the function "gets" was used in their C program, you would add the
+ following to the assembler file in which gets is defined:
+
+ .stabs "Obsolete function \"gets\" referenced",30,0,0,0
+ .stabs "_gets",1,0,0,0
+
+ These .stabs do not necessarily have to be in the same file as the
+ gets function, they simply must exist somewhere in the compilation. */
+
+#ifndef N_WARNING
+#define N_WARNING 0x1E /* Warning message to print if symbol
+ included */
+#endif /* This is input to ld */
+
+#ifndef __GNU_STAB__
+
+/* Line number for the data section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_DSLINE
+#define N_DSLINE (N_SLINE+N_DATA-N_TEXT)
+#endif
+
+/* Line number for the bss section. This is to be used to describe
+ the source location of a variable declaration. */
+#ifndef N_BSLINE
+#define N_BSLINE (N_SLINE+N_BSS-N_TEXT)
+#endif
+
+#endif /* not __GNU_STAB__ */
+
+/* Symbol table */
+
+/* Global symbol data is recorded in these structures,
+ one for each global symbol.
+ They are found via hashing in 'symtab', which points to a vector of buckets.
+ Each bucket is a chain of these structures through the link field. */
+
+typedef
+ struct glosym
+ {
+ /* Pointer to next symbol in this symbol's hash bucket. */
+ struct glosym *link;
+ /* Name of this symbol. */
+ char *name;
+ /* Value of this symbol as a global symbol. */
+ long value;
+ /* Chain of external 'nlist's in files for this symbol, both defs
+ and refs. */
+ struct nlist *refs;
+ /* Any warning message that might be associated with this symbol
+ from an N_WARNING symbol encountered. */
+ char *warning;
+ /* Nonzero means definitions of this symbol as common have been seen,
+ and the value here is the largest size specified by any of them. */
+ int max_common_size;
+ /* For relocatable_output, records the index of this global sym in the
+ symbol table to be written, with the first global sym given index 0.*/
+ int def_count;
+ /* Nonzero means a definition of this global symbol is known to exist.
+ Library members should not be loaded on its account. */
+ char defined;
+ /* Nonzero means a reference to this global symbol has been seen
+ in a file that is surely being loaded.
+ A value higher than 1 is the n_type code for the symbol's
+ definition. */
+ char referenced;
+ /* A count of the number of undefined references printed for a
+ specific symbol. If a symbol is unresolved at the end of
+ digest_symbols (and the loading run is supposed to produce
+ relocatable output) do_file_warnings keeps track of how many
+ unresolved reference error messages have been printed for
+ each symbol here. When the number hits MAX_UREFS_PRINTED,
+ messages stop. */
+ unsigned char undef_refs;
+ /* 1 means that this symbol has multiple definitions. 2 means
+ that it has multiple definitions, and some of them are set
+ elements, one of which has been printed out already. */
+ unsigned char multiply_defined;
+ /* Nonzero means print a message at all refs or defs of this symbol */
+ char trace;
+ }
+ symbol;
+
+/* Demangler for C++. */
+extern char *cplus_demangle ();
+
+/* Demangler function to use. */
+char *(*demangler)() = NULL;
+
+/* Number of buckets in symbol hash table */
+#define TABSIZE 1009
+
+/* The symbol hash table: a vector of TABSIZE pointers to struct glosym. */
+symbol *symtab[TABSIZE];
+
+/* Number of symbols in symbol hash table. */
+int num_hash_tab_syms = 0;
+
+/* Count the number of nlist entries that are for local symbols.
+ This count and the three following counts
+ are incremented as as symbols are entered in the symbol table. */
+int local_sym_count;
+
+/* Count number of nlist entries that are for local symbols
+ whose names don't start with L. */
+int non_L_local_sym_count;
+
+/* Count the number of nlist entries for debugger info. */
+int debugger_sym_count;
+
+/* Count the number of global symbols referenced and not defined. */
+int undefined_global_sym_count;
+
+/* Count the number of global symbols multiply defined. */
+int multiple_def_count;
+
+/* Count the number of defined global symbols.
+ Each symbol is counted only once
+ regardless of how many different nlist entries refer to it,
+ since the output file will need only one nlist entry for it.
+ This count is computed by `digest_symbols';
+ it is undefined while symbols are being loaded. */
+int defined_global_sym_count;
+
+/* Count the number of symbols defined through common declarations.
+ This count is kept in symdef_library, linear_library, and
+ enter_global_ref. It is incremented when the defined flag is set
+ in a symbol because of a common definition, and decremented when
+ the symbol is defined "for real" (ie. by something besides a common
+ definition). */
+int common_defined_global_count;
+
+/* Count the number of set element type symbols and the number of
+ separate vectors which these symbols will fit into. See the
+ GNU a.out.h for more info.
+ This count is computed by 'enter_file_symbols' */
+int set_symbol_count;
+int set_vector_count;
+
+/* Define a linked list of strings which define symbols which should
+ be treated as set elements even though they aren't. Any symbol
+ with a prefix matching one of these should be treated as a set
+ element.
+
+ This is to make up for deficiencies in many assemblers which aren't
+ willing to pass any stabs through to the loader which they don't
+ understand. */
+struct string_list_element {
+ char *str;
+ struct string_list_element *next;
+};
+
+struct string_list_element *set_element_prefixes;
+
+/* Count the number of definitions done indirectly (ie. done relative
+ to the value of some other symbol. */
+int global_indirect_count;
+
+/* Count the number of warning symbols encountered. */
+int warning_count;
+
+/* Total number of symbols to be written in the output file.
+ Computed by digest_symbols from the variables above. */
+int nsyms;
+
+
+/* Nonzero means ptr to symbol entry for symbol to use as start addr.
+ -e sets this. */
+symbol *entry_symbol;
+
+symbol *edata_symbol; /* the symbol _edata */
+symbol *etext_symbol; /* the symbol _etext */
+symbol *end_symbol; /* the symbol _end */
+
+/* Each input file, and each library member ("subfile") being loaded,
+ has a `file_entry' structure for it.
+
+ For files specified by command args, these are contained in the vector
+ which `file_table' points to.
+
+ For library members, they are dynamically allocated,
+ and chained through the `chain' field.
+ The chain is found in the `subfiles' field of the `file_entry'.
+ The `file_entry' objects for the members have `superfile' fields pointing
+ to the one for the library. */
+
+struct file_entry {
+ /* Name of this file. */
+ char *filename;
+ /* Name to use for the symbol giving address of text start */
+ /* Usually the same as filename, but for a file spec'd with -l
+ this is the -l switch itself rather than the filename. */
+ char *local_sym_name;
+
+ /* Describe the layout of the contents of the file */
+
+ /* The file's a.out header. */
+ struct exec header;
+ /* Offset in file of GDB symbol segment, or 0 if there is none. */
+ int symseg_offset;
+
+ /* Describe data from the file loaded into core */
+
+ /* Symbol table of the file. */
+ struct nlist *symbols;
+ /* Size in bytes of string table. */
+ int string_size;
+ /* Pointer to the string table.
+ The string table is not kept in core all the time,
+ but when it is in core, its address is here. */
+ char *strings;
+
+ /* Next two used only if `relocatable_output' or if needed for */
+ /* output of undefined reference line numbers. */
+
+ /* Text reloc info saved by `write_text' for `coptxtrel'. */
+ struct relocation_info *textrel;
+ /* Data reloc info saved by `write_data' for `copdatrel'. */
+ struct relocation_info *datarel;
+
+ /* Relation of this file's segments to the output file */
+
+ /* Start of this file's text seg in the output file core image. */
+ int text_start_address;
+ /* Start of this file's data seg in the output file core image. */
+ int data_start_address;
+ /* Start of this file's bss seg in the output file core image. */
+ int bss_start_address;
+ /* Offset in bytes in the output file symbol table
+ of the first local symbol for this file. Set by `write_file_symbols'. */
+ int local_syms_offset;
+
+ /* For library members only */
+
+ /* For a library, points to chain of entries for the library members. */
+ struct file_entry *subfiles;
+ /* For a library member, offset of the member within the archive.
+ Zero for files that are not library members. */
+ int starting_offset;
+ /* Size of contents of this file, if library member. */
+ int total_size;
+ /* For library member, points to the library's own entry. */
+ struct file_entry *superfile;
+ /* For library member, points to next entry for next member. */
+ struct file_entry *chain;
+
+ /* 1 if file is a library. */
+ char library_flag;
+
+ /* 1 if file's header has been read into this structure. */
+ char header_read_flag;
+
+ /* 1 means search a set of directories for this file. */
+ char search_dirs_flag;
+
+ /* 1 means this is base file of incremental load.
+ Do not load this file's text or data.
+ Also default text_start to after this file's bss. */
+ char just_syms_flag;
+};
+
+/* Vector of entries for input files specified by arguments.
+ These are all the input files except for members of specified libraries. */
+struct file_entry *file_table;
+
+/* Length of that vector. */
+int number_of_files;
+
+/* When loading the text and data, we can avoid doing a close
+ and another open between members of the same library.
+
+ These two variables remember the file that is currently open.
+ Both are zero if no file is open.
+
+ See `each_file' and `file_close'. */
+
+struct file_entry *input_file;
+int input_desc;
+
+/* The name of the file to write; "a.out" by default. */
+
+char *output_filename;
+
+/* Descriptor for writing that file with `mywrite'. */
+
+int outdesc;
+
+/* Header for that file (filled in by `write_header'). */
+
+struct exec outheader;
+
+#ifdef COFF_ENCAPSULATE
+struct coffheader coffheader;
+int need_coff_header;
+#endif
+
+/* The following are computed by `digest_symbols'. */
+
+int text_size; /* total size of text of all input files. */
+int data_size; /* total size of data of all input files. */
+int bss_size; /* total size of bss of all input files. */
+int text_reloc_size; /* total size of text relocation of all input files. */
+int data_reloc_size; /* total size of data relocation of all input */
+ /* files. */
+
+/* Specifications of start and length of the area reserved at the end
+ of the text segment for the set vectors. Computed in 'digest_symbols' */
+int set_sect_start;
+int set_sect_size;
+
+/* Pointer for in core storage for the above vectors, before they are
+ written. */
+unsigned long *set_vectors;
+
+/* Amount of cleared space to leave between the text and data segments. */
+
+int text_pad;
+
+/* Amount of bss segment to include as part of the data segment. */
+
+int data_pad;
+
+/* Format of __.SYMDEF:
+ First, a longword containing the size of the 'symdef' data that follows.
+ Second, zero or more 'symdef' structures.
+ Third, a longword containing the length of symbol name strings.
+ Fourth, zero or more symbol name strings (each followed by a null). */
+
+struct symdef {
+ int symbol_name_string_index;
+ int library_member_offset;
+};
+
+/* Record most of the command options. */
+
+/* Address we assume the text section will be loaded at.
+ We relocate symbols and text and data for this, but we do not
+ write any padding in the output file for it. */
+int text_start;
+
+/* Offset of default entry-pc within the text section. */
+int entry_offset;
+
+/* Address we decide the data section will be loaded at. */
+int data_start;
+
+/* `text-start' address is normally this much plus a page boundary.
+ This is not a user option; it is fixed for each system. */
+int text_start_alignment;
+
+/* Nonzero if -T was specified in the command line.
+ This prevents text_start from being set later to default values. */
+int T_flag_specified;
+
+/* Nonzero if -Tdata was specified in the command line.
+ This prevents data_start from being set later to default values. */
+int Tdata_flag_specified;
+
+/* Size to pad data section up to.
+ We simply increase the size of the data section, padding with zeros,
+ and reduce the size of the bss section to match. */
+int specified_data_size;
+
+/* Magic number to use for the output file, set by switch. */
+int magic;
+
+/* Nonzero means print names of input files as processed. */
+int trace_files;
+
+/* Which symbols should be stripped (omitted from the output):
+ none, all, or debugger symbols. */
+enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols;
+
+/* Which local symbols should be omitted:
+ none, all, or those starting with L.
+ This is irrelevant if STRIP_NONE. */
+enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals;
+
+/* Do we want to pad the text to a page boundary? */
+int padtext;
+
+/* 1 => write load map. */
+int write_map;
+
+/* 1 => write relocation into output file so can re-input it later. */
+int relocatable_output;
+
+/* 1 => assign space to common symbols even if `relocatable_output'. */
+int force_common_definition;
+
+/* Standard directories to search for files specified by -l. */
+char *standard_search_dirs[] =
+#ifdef STANDARD_SEARCH_DIRS
+ {STANDARD_SEARCH_DIRS};
+#else
+#ifdef NON_NATIVE
+ {"/usr/local/lib/gnu"};
+#else
+ {"/lib", "/usr/lib", "/usr/local/lib"};
+#endif
+#endif
+
+/* Actual vector of directories to search;
+ this contains those specified with -L plus the standard ones. */
+char **search_dirs;
+
+/* Length of the vector `search_dirs'. */
+int n_search_dirs;
+
+/* Non zero means to create the output executable. */
+/* Cleared by nonfatal errors. */
+int make_executable;
+
+/* Force the executable to be output, even if there are non-fatal
+ errors */
+int force_executable;
+
+/* Keep a list of any symbols referenced from the command line (so
+ that error messages for these guys can be generated). This list is
+ zero terminated. */
+struct glosym **cmdline_references;
+int cl_refs_allocated;
+
+void bcopy (), bzero ();
+int malloc (), realloc ();
+#ifndef alloca
+int alloca ();
+#endif
+int free ();
+
+int xmalloc ();
+int xrealloc ();
+void fatal ();
+void fatal_with_file ();
+void perror_name ();
+void perror_file ();
+void error ();
+
+void digest_symbols ();
+void print_symbols ();
+void load_symbols ();
+void decode_command ();
+void list_undefined_symbols ();
+void list_unresolved_references ();
+void write_output ();
+void write_header ();
+void write_text ();
+void read_file_relocation ();
+void write_data ();
+void write_rel ();
+void write_syms ();
+void write_symsegs ();
+void mywrite ();
+void symtab_init ();
+void padfile ();
+char *concat ();
+char *get_file_name ();
+symbol *getsym (), *getsym_soft ();
+
+int
+main (argc, argv)
+ char **argv;
+ int argc;
+{
+/* Added this to stop ld core-dumping on very large .o files. */
+#ifdef RLIMIT_STACK
+ /* Get rid of any avoidable limit on stack size. */
+ {
+ struct rlimit rlim;
+
+ /* Set the stack limit huge so that alloca does not fail. */
+ getrlimit (RLIMIT_STACK, &rlim);
+ rlim.rlim_cur = rlim.rlim_max;
+ setrlimit (RLIMIT_STACK, &rlim);
+ }
+#endif /* RLIMIT_STACK */
+
+ page_size = getpagesize ();
+ progname = argv[0];
+
+ /* Clear the cumulative info on the output file. */
+
+ text_size = 0;
+ data_size = 0;
+ bss_size = 0;
+ text_reloc_size = 0;
+ data_reloc_size = 0;
+
+ data_pad = 0;
+ text_pad = 0;
+
+ /* Initialize the data about options. */
+
+ specified_data_size = 0;
+ strip_symbols = STRIP_NONE;
+ trace_files = 0;
+ discard_locals = DISCARD_NONE;
+ padtext = 0;
+ entry_symbol = 0;
+ write_map = 0;
+ relocatable_output = 0;
+ force_common_definition = 0;
+ T_flag_specified = 0;
+ Tdata_flag_specified = 0;
+ magic = DEFAULT_MAGIC;
+ make_executable = 1;
+ force_executable = 0;
+ set_element_prefixes = 0;
+
+ /* Initialize the cumulative counts of symbols. */
+
+ local_sym_count = 0;
+ non_L_local_sym_count = 0;
+ debugger_sym_count = 0;
+ undefined_global_sym_count = 0;
+ set_symbol_count = 0;
+ set_vector_count = 0;
+ global_indirect_count = 0;
+ warning_count = 0;
+ multiple_def_count = 0;
+ common_defined_global_count = 0;
+
+ /* Keep a list of symbols referenced from the command line */
+ cl_refs_allocated = 10;
+ cmdline_references
+ = (struct glosym **) xmalloc (cl_refs_allocated
+ * sizeof(struct glosym *));
+ *cmdline_references = 0;
+
+ /* Completely decode ARGV. */
+
+ decode_command (argc, argv);
+
+ /* Create the symbols `etext', `edata' and `end'. */
+
+ if (!relocatable_output)
+ symtab_init ();
+
+ /* Determine whether to count the header as part of
+ the text size, and initialize the text size accordingly.
+ This depends on the kind of system and on the output format selected. */
+
+ N_SET_MAGIC (outheader, magic);
+#ifdef INITIALIZE_HEADER
+ INITIALIZE_HEADER;
+#endif
+
+ text_size = sizeof (struct exec);
+#ifdef COFF_ENCAPSULATE
+ if (relocatable_output == 0 && file_table[0].just_syms_flag == 0)
+ {
+ need_coff_header = 1;
+ /* set this flag now, since it will change the values of N_TXTOFF, etc */
+ N_SET_FLAGS (outheader, N_FLAGS_COFF_ENCAPSULATE);
+ text_size += sizeof (struct coffheader);
+ }
+#endif
+
+ text_size -= N_TXTOFF (outheader);
+
+ if (text_size < 0)
+ text_size = 0;
+ entry_offset = text_size;
+
+ if (!T_flag_specified && !relocatable_output)
+ text_start = TEXT_START (outheader);
+
+ /* The text-start address is normally this far past a page boundary. */
+ text_start_alignment = text_start % page_size;
+
+ /* Load symbols of all input files.
+ Also search all libraries and decide which library members to load. */
+
+ load_symbols ();
+
+ /* Compute where each file's sections go, and relocate symbols. */
+
+ digest_symbols ();
+
+ /* Print error messages for any missing symbols, for any warning
+ symbols, and possibly multiple definitions */
+
+ do_warnings (stderr);
+
+ /* Print a map, if requested. */
+
+ if (write_map) print_symbols (stdout);
+
+ /* Write the output file. */
+
+ if (make_executable || force_executable)
+ write_output ();
+
+ exit (!make_executable);
+}
+
+void decode_option ();
+
+/* Analyze a command line argument.
+ Return 0 if the argument is a filename.
+ Return 1 if the argument is a option complete in itself.
+ Return 2 if the argument is a option which uses an argument.
+
+ Thus, the value is the number of consecutive arguments
+ that are part of options. */
+
+int
+classify_arg (arg)
+ register char *arg;
+{
+ if (*arg != '-') return 0;
+ switch (arg[1])
+ {
+ case 'A':
+ case 'D':
+ case 'e':
+ case 'L':
+ case 'l':
+ case 'o':
+ case 'u':
+ case 'V':
+ case 'y':
+ if (arg[2])
+ return 1;
+ return 2;
+
+ case 'B':
+ if (! strcmp (&arg[2], "static"))
+ return 1;
+
+ case 'T':
+ if (arg[2] == 0)
+ return 2;
+ if (! strcmp (&arg[2], "text"))
+ return 2;
+ if (! strcmp (&arg[2], "data"))
+ return 2;
+ return 1;
+ }
+
+ return 1;
+}
+
+/* Process the command arguments,
+ setting up file_table with an entry for each input file,
+ and setting variables according to the options. */
+
+void
+decode_command (argc, argv)
+ char **argv;
+ int argc;
+{
+ register int i;
+ register struct file_entry *p;
+ char *cp;
+
+ number_of_files = 0;
+ output_filename = "a.out";
+
+ n_search_dirs = 0;
+ search_dirs = (char **) xmalloc (sizeof (char *));
+
+ /* First compute number_of_files so we know how long to make file_table. */
+ /* Also process most options completely. */
+
+ for (i = 1; i < argc; i++)
+ {
+ register int code = classify_arg (argv[i]);
+ if (code)
+ {
+ if (i + code > argc)
+ fatal ("no argument following %s\n", argv[i]);
+
+ decode_option (argv[i], argv[i+1]);
+
+ if (argv[i][1] == 'l' || argv[i][1] == 'A')
+ number_of_files++;
+
+ i += code - 1;
+ }
+ else
+ number_of_files++;
+ }
+
+ if (!number_of_files)
+ fatal ("no input files", 0);
+
+ p = file_table
+ = (struct file_entry *) xmalloc (number_of_files * sizeof (struct file_entry));
+ bzero (p, number_of_files * sizeof (struct file_entry));
+
+ /* Now scan again and fill in file_table. */
+ /* All options except -A and -l are ignored here. */
+
+ for (i = 1; i < argc; i++)
+ {
+ register int code = classify_arg (argv[i]);
+
+ if (code)
+ {
+ char *string;
+ if (code == 2)
+ string = argv[i+1];
+ else
+ string = &argv[i][2];
+
+ if (argv[i][1] == 'A')
+ {
+ if (p != file_table)
+ fatal ("-A specified before an input file other than the first");
+
+ p->filename = string;
+ p->local_sym_name = string;
+ p->just_syms_flag = 1;
+ p++;
+ }
+ if (argv[i][1] == 'l')
+ {
+ if (cp = rindex(string, '/'))
+ {
+ *cp++ = '\0';
+ cp = concat (string, "/lib", cp);
+ p->filename = concat (cp, ".a", "");
+ }
+ else
+ p->filename = concat ("lib", string, ".a");
+
+ p->local_sym_name = concat ("-l", string, "");
+ p->search_dirs_flag = 1;
+ p++;
+ }
+ i += code - 1;
+ }
+ else
+ {
+ p->filename = argv[i];
+ p->local_sym_name = argv[i];
+ p++;
+ }
+ }
+
+ /* Now check some option settings for consistency. */
+
+#ifdef NMAGIC
+ if ((magic == ZMAGIC || magic == NMAGIC)
+#else
+ if ((magic == ZMAGIC)
+#endif
+ && (text_start - text_start_alignment) & (page_size - 1))
+ fatal ("-T argument not multiple of page size, with sharable output", 0);
+
+ /* Append the standard search directories to the user-specified ones. */
+ {
+ int n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
+ n_search_dirs += n;
+ search_dirs
+ = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *));
+ bcopy (standard_search_dirs, &search_dirs[n_search_dirs - n],
+ n * sizeof (char *));
+ }
+}
+
+
+void
+add_cmdline_ref (sp)
+ struct glosym *sp;
+{
+ struct glosym **ptr;
+
+ for (ptr = cmdline_references;
+ ptr < cmdline_references + cl_refs_allocated && *ptr;
+ ptr++)
+ ;
+
+ if (ptr >= cmdline_references + cl_refs_allocated - 1)
+ {
+ int diff = ptr - cmdline_references;
+
+ cl_refs_allocated *= 2;
+ cmdline_references = (struct glosym **)
+ xrealloc (cmdline_references,
+ cl_refs_allocated * sizeof (struct glosym *));
+ ptr = cmdline_references + diff;
+ }
+
+ *ptr++ = sp;
+ *ptr = (struct glosym *) 0;
+}
+
+int
+set_element_prefixed_p (name)
+ char *name;
+{
+ struct string_list_element *p;
+ int i;
+
+ for (p = set_element_prefixes; p; p = p->next)
+ {
+ for (i = 0; p->str[i] != '\0' && (p->str[i] == name[i]); i++)
+ ;
+
+ if (p->str[i] == '\0')
+ return 1;
+ }
+ return 0;
+}
+
+int parse ();
+
+/* Record an option and arrange to act on it later.
+ ARG should be the following command argument,
+ which may or may not be used by this option.
+
+ The `l' and `A' options are ignored here since they actually
+ specify input files. */
+
+void
+decode_option (swt, arg)
+ register char *swt, *arg;
+{
+ /* We get Bstatic from gcc on suns. */
+ if (! strcmp (swt + 1, "Bstatic"))
+ return;
+ if (! strcmp (swt + 1, "Ttext"))
+ {
+ text_start = parse (arg, "%x", "invalid argument to -Ttext");
+ T_flag_specified = 1;
+ return;
+ }
+ if (! strcmp (swt + 1, "Tdata"))
+ {
+ data_start = parse (arg, "%x", "invalid argument to -Tdata");
+ Tdata_flag_specified = 1;
+ return;
+ }
+ if (! strcmp (swt + 1, "noinhibit-exec"))
+ {
+ force_executable = 1;
+ return;
+ }
+
+ if (swt[2] != 0)
+ arg = &swt[2];
+
+ switch (swt[1])
+ {
+ case 'A':
+ return;
+
+ case 'D':
+ specified_data_size = parse (arg, "%x", "invalid argument to -D");
+ return;
+
+ case 'd':
+ force_common_definition = 1;
+ return;
+
+ case 'e':
+ entry_symbol = getsym (arg);
+ if (!entry_symbol->defined && !entry_symbol->referenced)
+ undefined_global_sym_count++;
+ entry_symbol->referenced = 1;
+ add_cmdline_ref (entry_symbol);
+ return;
+
+ case 'l':
+ /* If linking with libg++, use the C++ demangler. */
+ if (arg != NULL && strcmp (arg, "g++") == 0)
+ demangler = cplus_demangle;
+ return;
+
+ case 'L':
+ n_search_dirs++;
+ search_dirs
+ = (char **) xrealloc (search_dirs, n_search_dirs * sizeof (char *));
+ search_dirs[n_search_dirs - 1] = arg;
+ return;
+
+ case 'M':
+ write_map = 1;
+ return;
+
+ case 'N':
+ magic = OMAGIC;
+ return;
+
+#ifdef NMAGIC
+ case 'n':
+ magic = NMAGIC;
+ return;
+#endif
+
+ case 'o':
+ output_filename = arg;
+ return;
+
+ case 'p':
+ padtext = 1;
+ return;
+
+ case 'r':
+ relocatable_output = 1;
+ magic = OMAGIC;
+ text_start = 0;
+ return;
+
+ case 'S':
+ strip_symbols = STRIP_DEBUGGER;
+ return;
+
+ case 's':
+ strip_symbols = STRIP_ALL;
+ return;
+
+ case 'T':
+ text_start = parse (arg, "%x", "invalid argument to -T");
+ T_flag_specified = 1;
+ return;
+
+ case 't':
+ trace_files = 1;
+ return;
+
+ case 'u':
+ {
+ register symbol *sp = getsym (arg);
+ if (!sp->defined && !sp->referenced)
+ undefined_global_sym_count++;
+ sp->referenced = 1;
+ add_cmdline_ref (sp);
+ }
+ return;
+
+ case 'V':
+ {
+ struct string_list_element *new
+ = (struct string_list_element *)
+ xmalloc (sizeof (struct string_list_element));
+
+ new->str = arg;
+ new->next = set_element_prefixes;
+ set_element_prefixes = new;
+ return;
+ }
+
+ case 'X':
+ discard_locals = DISCARD_L;
+ return;
+
+ case 'x':
+ discard_locals = DISCARD_ALL;
+ return;
+
+ case 'y':
+ {
+ register symbol *sp = getsym (&swt[2]);
+ sp->trace = 1;
+ }
+ return;
+
+ case 'z':
+ magic = ZMAGIC;
+ return;
+
+ default:
+ fatal ("invalid command option `%s'", swt);
+ }
+}
+
+/** Convenient functions for operating on one or all files being */
+ /** loaded. */
+void print_file_name ();
+
+/* Call FUNCTION on each input file entry.
+ Do not call for entries for libraries;
+ instead, call once for each library member that is being loaded.
+
+ FUNCTION receives two arguments: the entry, and ARG. */
+
+void
+each_file (function, arg)
+ register void (*function)();
+ register int arg;
+{
+ register int i;
+
+ for (i = 0; i < number_of_files; i++)
+ {
+ register struct file_entry *entry = &file_table[i];
+ if (entry->library_flag)
+ {
+ register struct file_entry *subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain)
+ (*function) (subentry, arg);
+ }
+ else
+ (*function) (entry, arg);
+ }
+}
+
+/* Call FUNCTION on each input file entry until it returns a non-zero
+ value. Return this value.
+ Do not call for entries for libraries;
+ instead, call once for each library member that is being loaded.
+
+ FUNCTION receives two arguments: the entry, and ARG. It must be a
+ function returning unsigned long (though this can probably be fudged). */
+
+unsigned long
+check_each_file (function, arg)
+ register unsigned long (*function)();
+ register int arg;
+{
+ register int i;
+ register unsigned long return_val;
+
+ for (i = 0; i < number_of_files; i++)
+ {
+ register struct file_entry *entry = &file_table[i];
+ if (entry->library_flag)
+ {
+ register struct file_entry *subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain)
+ if (return_val = (*function) (subentry, arg))
+ return return_val;
+ }
+ else
+ if (return_val = (*function) (entry, arg))
+ return return_val;
+ }
+ return 0;
+}
+
+/* Like `each_file' but ignore files that were just for symbol definitions. */
+
+void
+each_full_file (function, arg)
+ register void (*function)();
+ register int arg;
+{
+ register int i;
+
+ for (i = 0; i < number_of_files; i++)
+ {
+ register struct file_entry *entry = &file_table[i];
+ if (entry->just_syms_flag)
+ continue;
+ if (entry->library_flag)
+ {
+ register struct file_entry *subentry = entry->subfiles;
+ for (; subentry; subentry = subentry->chain)
+ (*function) (subentry, arg);
+ }
+ else
+ (*function) (entry, arg);
+ }
+}
+
+/* Close the input file that is now open. */
+
+void
+file_close ()
+{
+ close (input_desc);
+ input_desc = 0;
+ input_file = 0;
+}
+
+/* Open the input file specified by 'entry', and return a descriptor.
+ The open file is remembered; if the same file is opened twice in a row,
+ a new open is not actually done. */
+
+int
+file_open (entry)
+ register struct file_entry *entry;
+{
+ register int desc;
+
+ if (entry->superfile)
+ return file_open (entry->superfile);
+
+ if (entry == input_file)
+ return input_desc;
+
+ if (input_file) file_close ();
+
+ if (entry->search_dirs_flag)
+ {
+ int i;
+
+ for (i = 0; i < n_search_dirs; i++)
+ {
+ register char *string
+ = concat (search_dirs[i], "/", entry->filename);
+ desc = open (string, O_RDONLY, 0);
+ if (desc > 0)
+ {
+ entry->filename = string;
+ entry->search_dirs_flag = 0;
+ break;
+ }
+ free (string);
+ }
+ }
+ else
+ desc = open (entry->filename, O_RDONLY, 0);
+
+ if (desc > 0)
+ {
+ input_file = entry;
+ input_desc = desc;
+ return desc;
+ }
+
+ perror_file (entry);
+ /* NOTREACHED */
+}
+
+/* Print the filename of ENTRY on OUTFILE (a stdio stream),
+ and then a newline. */
+
+void
+prline_file_name (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ print_file_name (entry, outfile);
+ fprintf (outfile, "\n");
+}
+
+/* Print the filename of ENTRY on OUTFILE (a stdio stream). */
+
+void
+print_file_name (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ if (entry->superfile)
+ {
+ print_file_name (entry->superfile, outfile);
+ fprintf (outfile, "(%s)", entry->filename);
+ }
+ else
+ fprintf (outfile, "%s", entry->filename);
+}
+
+/* Return the filename of entry as a string (malloc'd for the purpose) */
+
+char *
+get_file_name (entry)
+ struct file_entry *entry;
+{
+ char *result, *supfile;
+ if (entry->superfile)
+ {
+ supfile = get_file_name (entry->superfile);
+ result = (char *) xmalloc (strlen (supfile)
+ + strlen (entry->filename) + 3);
+ sprintf (result, "%s(%s)", supfile, entry->filename);
+ free (supfile);
+ }
+ else
+ {
+ result = (char *) xmalloc (strlen (entry->filename) + 1);
+ strcpy (result, entry->filename);
+ }
+ return result;
+}
+
+/* Medium-level input routines for rel files. */
+
+/* Read a file's header into the proper place in the file_entry.
+ DESC is the descriptor on which the file is open.
+ ENTRY is the file's entry. */
+
+void
+read_header (desc, entry)
+ int desc;
+ register struct file_entry *entry;
+{
+ register int len;
+ struct exec *loc = (struct exec *) &entry->header;
+
+ lseek (desc, entry->starting_offset, 0);
+#ifdef COFF_ENCAPSULATE
+ if (entry->just_syms_flag)
+ lseek (desc, sizeof(coffheader), 1);
+#endif
+ len = read (desc, loc, sizeof (struct exec));
+ if (len != sizeof (struct exec))
+ fatal_with_file ("failure reading header of ", entry);
+ if (N_BADMAG (*loc))
+ fatal_with_file ("bad magic number in ", entry);
+
+ entry->header_read_flag = 1;
+}
+
+/* Read the symbols of file ENTRY into core.
+ Assume it is already open, on descriptor DESC.
+ Also read the length of the string table, which follows the symbol table,
+ but don't read the contents of the string table. */
+
+void
+read_entry_symbols (desc, entry)
+ struct file_entry *entry;
+ int desc;
+{
+ int str_size;
+
+ if (!entry->header_read_flag)
+ read_header (desc, entry);
+
+ entry->symbols = (struct nlist *) xmalloc (entry->header.a_syms);
+
+ lseek (desc, N_SYMOFF (entry->header) + entry->starting_offset, 0);
+ if (entry->header.a_syms != read (desc, entry->symbols, entry->header.a_syms))
+ fatal_with_file ("premature end of file in symbols of ", entry);
+
+ lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0);
+ if (sizeof str_size != read (desc, &str_size, sizeof str_size))
+ fatal_with_file ("bad string table size in ", entry);
+
+ entry->string_size = str_size;
+}
+
+/* Read the string table of file ENTRY into core.
+ Assume it is already open, on descriptor DESC.
+ Also record whether a GDB symbol segment follows the string table. */
+
+void
+read_entry_strings (desc, entry)
+ struct file_entry *entry;
+ int desc;
+{
+ int buffer;
+
+ if (!entry->header_read_flag)
+ read_header (desc, entry);
+
+ lseek (desc, N_STROFF (entry->header) + entry->starting_offset, 0);
+ if (entry->string_size != read (desc, entry->strings, entry->string_size))
+ fatal_with_file ("premature end of file in strings of ", entry);
+
+ /* While we are here, see if the file has a symbol segment at the end.
+ For a separate file, just try reading some more.
+ For a library member, compare current pos against total size. */
+ if (entry->superfile)
+ {
+ if (entry->total_size == N_STROFF (entry->header) + entry->string_size)
+ return;
+ }
+ else
+ {
+ buffer = read (desc, &buffer, sizeof buffer);
+ if (buffer == 0)
+ return;
+ if (buffer != sizeof buffer)
+ fatal_with_file ("premature end of file in GDB symbol segment of ", entry);
+ }
+ /* Don't try to do anything with symsegs. */
+ return;
+#if 0
+ /* eliminate warning of `statement not reached'. */
+ entry->symseg_offset = N_STROFF (entry->header) + entry->string_size;
+#endif
+}
+
+/* Read in the symbols of all input files. */
+
+void read_file_symbols (), read_entry_symbols (), read_entry_strings ();
+void enter_file_symbols (), enter_global_ref (), search_library ();
+
+void
+load_symbols ()
+{
+ register int i;
+
+ if (trace_files) fprintf (stderr, "Loading symbols:\n\n");
+
+ for (i = 0; i < number_of_files; i++)
+ {
+ register struct file_entry *entry = &file_table[i];
+ read_file_symbols (entry);
+ }
+
+ if (trace_files) fprintf (stderr, "\n");
+}
+
+/* If ENTRY is a rel file, read its symbol and string sections into core.
+ If it is a library, search it and load the appropriate members
+ (which means calling this function recursively on those members). */
+
+void
+read_file_symbols (entry)
+ register struct file_entry *entry;
+{
+ register int desc;
+ register int len;
+ struct exec hdr;
+
+ desc = file_open (entry);
+
+#ifdef COFF_ENCAPSULATE
+ if (entry->just_syms_flag)
+ lseek (desc, sizeof(coffheader),0);
+#endif
+
+ len = read (desc, &hdr, sizeof hdr);
+ if (len != sizeof hdr)
+ fatal_with_file ("failure reading header of ", entry);
+
+ if (!N_BADMAG (hdr))
+ {
+ read_entry_symbols (desc, entry);
+ entry->strings = (char *) alloca (entry->string_size);
+ read_entry_strings (desc, entry);
+ enter_file_symbols (entry);
+ entry->strings = 0;
+ }
+ else
+ {
+ char armag[SARMAG];
+
+ lseek (desc, 0, 0);
+ if (SARMAG != read (desc, armag, SARMAG) || strncmp (armag, ARMAG, SARMAG))
+ fatal_with_file ("malformed input file (not rel or archive) ", entry);
+ entry->library_flag = 1;
+ search_library (desc, entry);
+ }
+
+ file_close ();
+}
+
+/* Enter the external symbol defs and refs of ENTRY in the hash table. */
+
+void
+enter_file_symbols (entry)
+ struct file_entry *entry;
+{
+ register struct nlist
+ *p,
+ *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
+
+ if (trace_files) prline_file_name (entry, stderr);
+
+ for (p = entry->symbols; p < end; p++)
+ {
+ if (p->n_type == (N_SETV | N_EXT)) continue;
+ if (set_element_prefixes
+ && set_element_prefixed_p (p->n_un.n_strx + entry->strings))
+ p->n_type += (N_SETA - N_ABS);
+
+ if (SET_ELEMENT_P (p->n_type))
+ {
+ set_symbol_count++;
+ if (!relocatable_output)
+ enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
+ }
+ else if (p->n_type == N_WARNING)
+ {
+ char *name = p->n_un.n_strx + entry->strings;
+
+ /* Grab the next entry. */
+ p++;
+ if (p->n_type != (N_UNDF | N_EXT))
+ {
+ fprintf (stderr, "%s: Warning symbol found in %s without external reference following.\n",
+ progname, entry->filename);
+ make_executable = 0;
+ p--; /* Process normally. */
+ }
+ else
+ {
+ symbol *sp;
+ char *sname = p->n_un.n_strx + entry->strings;
+ /* Deal with the warning symbol. */
+ enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
+ sp = getsym (sname);
+ sp->warning = (char *) xmalloc (strlen(name) + 1);
+ strcpy (sp->warning, name);
+ warning_count++;
+ }
+ }
+ else if (p->n_type & N_EXT)
+ enter_global_ref (p, p->n_un.n_strx + entry->strings, entry);
+ else if (p->n_un.n_strx && !(p->n_type & (N_STAB | N_EXT)))
+ {
+ if ((p->n_un.n_strx + entry->strings)[0] != LPREFIX)
+ non_L_local_sym_count++;
+ local_sym_count++;
+ }
+ else debugger_sym_count++;
+ }
+
+ /* Count one for the local symbol that we generate,
+ whose name is the file's name (usually) and whose address
+ is the start of the file's text. */
+
+ local_sym_count++;
+ non_L_local_sym_count++;
+}
+
+/* Enter one global symbol in the hash table.
+ NLIST_P points to the `struct nlist' read from the file
+ that describes the global symbol. NAME is the symbol's name.
+ ENTRY is the file entry for the file the symbol comes from.
+
+ The `struct nlist' is modified by placing it on a chain of
+ all such structs that refer to the same global symbol.
+ This chain starts in the `refs' field of the symbol table entry
+ and is chained through the `n_name'. */
+
+void
+enter_global_ref (nlist_p, name, entry)
+ register struct nlist *nlist_p;
+ char *name;
+ struct file_entry *entry;
+{
+ register symbol *sp = getsym (name);
+ register int type = nlist_p->n_type;
+ int oldref = sp->referenced;
+ int olddef = sp->defined;
+ int com = sp->defined && sp->max_common_size;
+
+ nlist_p->n_un.n_name = (char *) sp->refs;
+ sp->refs = nlist_p;
+
+ sp->referenced = 1;
+ if (type != (N_UNDF | N_EXT) || nlist_p->n_value)
+ {
+ if (!sp->defined || sp->defined == (N_UNDF | N_EXT))
+ sp->defined = type;
+
+ if (oldref && !olddef)
+ /* It used to be undefined and we're defining it. */
+ undefined_global_sym_count--;
+
+ if (!olddef && type == (N_UNDF | N_EXT) && nlist_p->n_value)
+ {
+ /* First definition and it's common. */
+ common_defined_global_count++;
+ sp->max_common_size = nlist_p->n_value;
+ }
+ else if (com && type != (N_UNDF | N_EXT))
+ {
+ /* It used to be common and we're defining it as
+ something else. */
+ common_defined_global_count--;
+ sp->max_common_size = 0;
+ }
+ else if (com && type == (N_UNDF | N_EXT)
+ && sp->max_common_size < nlist_p->n_value)
+ /* It used to be common and this is a new common entry to
+ which we need to pay attention. */
+ sp->max_common_size = nlist_p->n_value;
+
+ /* Are we defining it as a set element? */
+ if (SET_ELEMENT_P (type) && (!olddef || com))
+ set_vector_count++;
+ /* As an indirection? */
+ else if (type == (N_INDR | N_EXT))
+ {
+ /* Indirect symbols value should be modified to point
+ a symbol being equivalenced to. */
+ nlist_p->n_value
+ = (unsigned int) getsym ((nlist_p + 1)->n_un.n_strx
+ + entry->strings);
+ if ((symbol *) nlist_p->n_value == sp)
+ {
+ /* Somebody redefined a symbol to be itself. */
+ fprintf (stderr, "%s: Symbol %s indirected to itself.\n",
+ entry->filename, name);
+ /* Rewrite this symbol as being a global text symbol
+ with value 0. */
+ nlist_p->n_type = sp->defined = N_TEXT | N_EXT;
+ nlist_p->n_value = 0;
+ /* Don't make the output executable. */
+ make_executable = 0;
+ }
+ else
+ global_indirect_count++;
+ }
+ }
+ else
+ if (!oldref)
+#ifndef DOLLAR_KLUDGE
+ undefined_global_sym_count++;
+#else
+ {
+ if (entry->superfile && type == (N_UNDF | N_EXT) && name[1] == '$')
+ {
+ /* This is an (ISI?) $-conditional; skip it */
+ sp->referenced = 0;
+ if (sp->trace)
+ {
+ fprintf (stderr, "symbol %s is a $-conditional ignored in ", sp->name);
+ print_file_name (entry, stderr);
+ fprintf (stderr, "\n");
+ }
+ return;
+ }
+ else
+ undefined_global_sym_count++;
+ }
+#endif
+
+ if (sp == end_symbol && entry->just_syms_flag && !T_flag_specified)
+ text_start = nlist_p->n_value;
+
+ if (sp->trace)
+ {
+ register char *reftype;
+ switch (type & N_TYPE)
+ {
+ case N_UNDF:
+ if (nlist_p->n_value)
+ reftype = "defined as common";
+ else reftype = "referenced";
+ break;
+
+ case N_ABS:
+ reftype = "defined as absolute";
+ break;
+
+ case N_TEXT:
+ reftype = "defined in text section";
+ break;
+
+ case N_DATA:
+ reftype = "defined in data section";
+ break;
+
+ case N_BSS:
+ reftype = "defined in BSS section";
+ break;
+
+ case N_SETT:
+ reftype = "is a text set element";
+ break;
+
+ case N_SETD:
+ reftype = "is a data set element";
+ break;
+
+ case N_SETB:
+ reftype = "is a BSS set element";
+ break;
+
+ case N_SETA:
+ reftype = "is an absolute set element";
+ break;
+
+ case N_SETV:
+ reftype = "defined in data section as vector";
+ break;
+
+ case N_INDR:
+ reftype = (char *) alloca (23
+ + strlen ((nlist_p + 1)->n_un.n_strx
+ + entry->strings));
+ sprintf (reftype, "defined equivalent to %s",
+ (nlist_p + 1)->n_un.n_strx + entry->strings);
+ break;
+
+#ifdef sequent
+ case N_SHUNDF:
+ reftype = "shared undf";
+ break;
+
+/* These conflict with cases above.
+ case N_SHDATA:
+ reftype = "shared data";
+ break;
+
+ case N_SHBSS:
+ reftype = "shared BSS";
+ break;
+*/
+ default:
+ reftype = "I don't know this type";
+ break;
+#endif
+ }
+
+ fprintf (stderr, "symbol %s %s in ", sp->name, reftype);
+ print_file_name (entry, stderr);
+ fprintf (stderr, "\n");
+ }
+}
+
+/* This return 0 if the given file entry's symbol table does *not*
+ contain the nlist point entry, and it returns the files entry
+ pointer (cast to unsigned long) if it does. */
+
+unsigned long
+contains_symbol (entry, n_ptr)
+ struct file_entry *entry;
+ register struct nlist *n_ptr;
+{
+ if (n_ptr >= entry->symbols &&
+ n_ptr < (entry->symbols
+ + (entry->header.a_syms / sizeof (struct nlist))))
+ return (unsigned long) entry;
+ return 0;
+}
+
+
+/* Searching libraries */
+
+struct file_entry *decode_library_subfile ();
+void linear_library (), symdef_library ();
+
+/* Search the library ENTRY, already open on descriptor DESC.
+ This means deciding which library members to load,
+ making a chain of `struct file_entry' for those members,
+ and entering their global symbols in the hash table. */
+
+void
+search_library (desc, entry)
+ int desc;
+ struct file_entry *entry;
+{
+ int member_length;
+ register char *name;
+ register struct file_entry *subentry;
+
+ if (!undefined_global_sym_count) return;
+
+ /* Examine its first member, which starts SARMAG bytes in. */
+ subentry = decode_library_subfile (desc, entry, SARMAG, &member_length);
+ if (!subentry) return;
+
+ name = subentry->filename;
+ free (subentry);
+
+ /* Search via __.SYMDEF if that exists, else linearly. */
+
+ if (!strcmp (name, "__.SYMDEF"))
+ symdef_library (desc, entry, member_length);
+ else
+ linear_library (desc, entry);
+}
+
+/* Construct and return a file_entry for a library member.
+ The library's file_entry is library_entry, and the library is open on DESC.
+ SUBFILE_OFFSET is the byte index in the library of this member's header.
+ We store the length of the member into *LENGTH_LOC. */
+
+struct file_entry *
+decode_library_subfile (desc, library_entry, subfile_offset, length_loc)
+ int desc;
+ struct file_entry *library_entry;
+ int subfile_offset;
+ int *length_loc;
+{
+ int bytes_read;
+ register int namelen;
+ int member_length;
+ register char *name;
+ struct ar_hdr hdr1;
+ register struct file_entry *subentry;
+
+ lseek (desc, subfile_offset, 0);
+
+ bytes_read = read (desc, &hdr1, sizeof hdr1);
+ if (!bytes_read)
+ return 0; /* end of archive */
+
+ if (sizeof hdr1 != bytes_read)
+ fatal_with_file ("malformed library archive ", library_entry);
+
+ if (sscanf (hdr1.ar_size, "%d", &member_length) != 1)
+ fatal_with_file ("malformatted header of archive member in ", library_entry);
+
+ subentry = (struct file_entry *) xmalloc (sizeof (struct file_entry));
+ bzero (subentry, sizeof (struct file_entry));
+
+ for (namelen = 0;
+ namelen < sizeof hdr1.ar_name
+ && hdr1.ar_name[namelen] != 0 && hdr1.ar_name[namelen] != ' '
+ && hdr1.ar_name[namelen] != '/';
+ namelen++);
+
+ name = (char *) xmalloc (namelen+1);
+ strncpy (name, hdr1.ar_name, namelen);
+ name[namelen] = 0;
+
+ subentry->filename = name;
+ subentry->local_sym_name = name;
+ subentry->symbols = 0;
+ subentry->strings = 0;
+ subentry->subfiles = 0;
+ subentry->starting_offset = subfile_offset + sizeof hdr1;
+ subentry->superfile = library_entry;
+ subentry->library_flag = 0;
+ subentry->header_read_flag = 0;
+ subentry->just_syms_flag = 0;
+ subentry->chain = 0;
+ subentry->total_size = member_length;
+
+ (*length_loc) = member_length;
+
+ return subentry;
+}
+
+int subfile_wanted_p ();
+
+/* Search a library that has a __.SYMDEF member.
+ DESC is a descriptor on which the library is open.
+ The file pointer is assumed to point at the __.SYMDEF data.
+ ENTRY is the library's file_entry.
+ MEMBER_LENGTH is the length of the __.SYMDEF data. */
+
+void
+symdef_library (desc, entry, member_length)
+ int desc;
+ struct file_entry *entry;
+ int member_length;
+{
+ int *symdef_data = (int *) xmalloc (member_length);
+ register struct symdef *symdef_base;
+ char *sym_name_base;
+ int number_of_symdefs;
+ int length_of_strings;
+ int not_finished;
+ int bytes_read;
+ register int i;
+ struct file_entry *prev = 0;
+ int prev_offset = 0;
+
+ bytes_read = read (desc, symdef_data, member_length);
+ if (bytes_read != member_length)
+ fatal_with_file ("malformatted __.SYMDEF in ", entry);
+
+ number_of_symdefs = *symdef_data / sizeof (struct symdef);
+ if (number_of_symdefs < 0 ||
+ number_of_symdefs * sizeof (struct symdef) + 2 * sizeof (int) > member_length)
+ fatal_with_file ("malformatted __.SYMDEF in ", entry);
+
+ symdef_base = (struct symdef *) (symdef_data + 1);
+ length_of_strings = *(int *) (symdef_base + number_of_symdefs);
+
+ if (length_of_strings < 0
+ || number_of_symdefs * sizeof (struct symdef) + length_of_strings
+ + 2 * sizeof (int) > member_length)
+ fatal_with_file ("malformatted __.SYMDEF in ", entry);
+
+ sym_name_base = sizeof (int) + (char *) (symdef_base + number_of_symdefs);
+
+ /* Check all the string indexes for validity. */
+
+ for (i = 0; i < number_of_symdefs; i++)
+ {
+ register int index = symdef_base[i].symbol_name_string_index;
+ if (index < 0 || index >= length_of_strings
+ || (index && *(sym_name_base + index - 1)))
+ fatal_with_file ("malformatted __.SYMDEF in ", entry);
+ }
+
+ /* Search the symdef data for members to load.
+ Do this until one whole pass finds nothing to load. */
+
+ not_finished = 1;
+ while (not_finished)
+ {
+ not_finished = 0;
+
+ /* Scan all the symbols mentioned in the symdef for ones that we need.
+ Load the library members that contain such symbols. */
+
+ for (i = 0;
+ (i < number_of_symdefs
+ && (undefined_global_sym_count || common_defined_global_count));
+ i++)
+ if (symdef_base[i].symbol_name_string_index >= 0)
+ {
+ register symbol *sp;
+
+ sp = getsym_soft (sym_name_base
+ + symdef_base[i].symbol_name_string_index);
+
+ /* If we find a symbol that appears to be needed, think carefully
+ about the archive member that the symbol is in. */
+
+ /*
+ * Per Mike Karels' recommendation, we no longer load library
+ * files if the only reference(s) that would be satisfied are
+ * 'common' references. This prevents some problems with name
+ * pollution (e.g. a global common 'utime' linked to a function).
+ */
+ if (sp && sp->referenced && !sp->defined)
+ {
+ int junk;
+ register int j;
+ register int offset = symdef_base[i].library_member_offset;
+ struct file_entry *subentry;
+
+ /* Don't think carefully about any archive member
+ more than once in a given pass. */
+
+ if (prev_offset == offset)
+ continue;
+ prev_offset = offset;
+
+ /* Read the symbol table of the archive member. */
+
+ subentry = decode_library_subfile (desc, entry, offset, &junk);
+ if (subentry == 0)
+ fatal ("invalid offset for %s in symbol table of %s",
+ sym_name_base
+ + symdef_base[i].symbol_name_string_index,
+ entry->filename);
+ read_entry_symbols (desc, subentry);
+ subentry->strings = (char *) malloc (subentry->string_size);
+ read_entry_strings (desc, subentry);
+
+ /* Now scan the symbol table and decide whether to load. */
+
+ if (!subfile_wanted_p (subentry))
+ {
+ free (subentry->symbols);
+ free (subentry);
+ }
+ else
+ {
+ /* This member is needed; load it.
+ Since we are loading something on this pass,
+ we must make another pass through the symdef data. */
+
+ not_finished = 1;
+
+ enter_file_symbols (subentry);
+
+ if (prev)
+ prev->chain = subentry;
+ else entry->subfiles = subentry;
+ prev = subentry;
+
+ /* Clear out this member's symbols from the symdef data
+ so that following passes won't waste time on them. */
+
+ for (j = 0; j < number_of_symdefs; j++)
+ {
+ if (symdef_base[j].library_member_offset == offset)
+ symdef_base[j].symbol_name_string_index = -1;
+ }
+ }
+
+ /* We'll read the strings again if we need them again. */
+ free (subentry->strings);
+ subentry->strings = 0;
+ }
+ }
+ }
+
+ free (symdef_data);
+}
+
+/* Search a library that has no __.SYMDEF.
+ ENTRY is the library's file_entry.
+ DESC is the descriptor it is open on. */
+
+void
+linear_library (desc, entry)
+ int desc;
+ struct file_entry *entry;
+{
+ register struct file_entry *prev = 0;
+ register int this_subfile_offset = SARMAG;
+
+ while (undefined_global_sym_count || common_defined_global_count)
+ {
+ int member_length;
+ register struct file_entry *subentry;
+
+ subentry = decode_library_subfile (desc, entry, this_subfile_offset,
+ &member_length);
+
+ if (!subentry) return;
+
+ read_entry_symbols (desc, subentry);
+ subentry->strings = (char *) alloca (subentry->string_size);
+ read_entry_strings (desc, subentry);
+
+ if (!subfile_wanted_p (subentry))
+ {
+ free (subentry->symbols);
+ free (subentry);
+ }
+ else
+ {
+ enter_file_symbols (subentry);
+
+ if (prev)
+ prev->chain = subentry;
+ else entry->subfiles = subentry;
+ prev = subentry;
+ subentry->strings = 0; /* Since space will dissapear on return */
+ }
+
+ this_subfile_offset += member_length + sizeof (struct ar_hdr);
+ if (this_subfile_offset & 1) this_subfile_offset++;
+ }
+}
+
+/* ENTRY is an entry for a library member.
+ Its symbols have been read into core, but not entered.
+ Return nonzero if we ought to load this member. */
+
+int
+subfile_wanted_p (entry)
+ struct file_entry *entry;
+{
+ register struct nlist *p;
+ register struct nlist *end
+ = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
+#ifdef DOLLAR_KLUDGE
+ register int dollar_cond = 0;
+#endif
+
+ for (p = entry->symbols; p < end; p++)
+ {
+ register int type = p->n_type;
+ register char *name = p->n_un.n_strx + entry->strings;
+
+ /* If the symbol has an interesting definition, we could
+ potentially want it. */
+ if (type & N_EXT
+ && (type != (N_UNDF | N_EXT) || p->n_value
+
+#ifdef DOLLAR_KLUDGE
+ || name[1] == '$'
+#endif
+ )
+ && !SET_ELEMENT_P (type)
+ && !set_element_prefixed_p (name))
+ {
+ register symbol *sp = getsym_soft (name);
+
+#ifdef DOLLAR_KLUDGE
+ if (name[1] == '$')
+ {
+ sp = getsym_soft (&name[2]);
+ dollar_cond = 1;
+ if (!sp) continue;
+ if (sp->referenced)
+ {
+ if (write_map)
+ {
+ print_file_name (entry, stdout);
+ fprintf (stdout, " needed due to $-conditional %s\n", name);
+ }
+ return 1;
+ }
+ continue;
+ }
+#endif
+
+ /* If this symbol has not been hashed, we can't be looking for it. */
+
+ if (!sp) continue;
+
+ /*
+ * We don't load a file if it merely satisfies a common reference
+ * (see explanation above in symdef_library()).
+ */
+ if (sp->referenced && !sp->defined)
+ {
+ /* This is a symbol we are looking for. It is either
+ not yet defined or defined as a common. */
+#ifdef DOLLAR_KLUDGE
+ if (dollar_cond) continue;
+#endif
+ if (type == (N_UNDF | N_EXT))
+ {
+ /* Symbol being defined as common.
+ Remember this, but don't load subfile just for this. */
+
+ /* If it didn't used to be common, up the count of
+ common symbols. */
+ if (!sp->max_common_size)
+ common_defined_global_count++;
+
+ if (sp->max_common_size < p->n_value)
+ sp->max_common_size = p->n_value;
+ if (!sp->defined)
+ undefined_global_sym_count--;
+ sp->defined = 1;
+ continue;
+ }
+
+ if (write_map)
+ {
+ print_file_name (entry, stdout);
+ fprintf (stdout, " needed due to %s\n", sp->name);
+ }
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void consider_file_section_lengths (), relocate_file_addresses ();
+
+/* Having entered all the global symbols and found the sizes of sections
+ of all files to be linked, make all appropriate deductions from this data.
+
+ We propagate global symbol values from definitions to references.
+ We compute the layout of the output file and where each input file's
+ contents fit into it. */
+
+void
+digest_symbols ()
+{
+ register int i;
+ int setv_fill_count;
+
+ if (trace_files)
+ fprintf (stderr, "Digesting symbol information:\n\n");
+
+ /* Compute total size of sections */
+
+ each_file (consider_file_section_lengths, 0);
+
+ /* If necessary, pad text section to full page in the file.
+ Include the padding in the text segment size. */
+
+ if (magic == ZMAGIC)
+ {
+ int text_end = text_size + N_TXTOFF (outheader);
+ text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end;
+ text_size += text_pad;
+ }
+ if (padtext)
+ {
+ int text_end = text_size;
+ text_pad = ((text_end + page_size - 1) & (- page_size)) - text_end;
+ text_size += text_pad;
+ }
+
+#ifdef _N_BASEADDR
+ /* SunOS 4.1 N_TXTADDR depends on the value of outheader.a_entry. */
+ outheader.a_entry = N_PAGSIZ (outheader);
+#endif
+
+ outheader.a_text = text_size;
+#ifdef sequent
+ outheader.a_text += N_ADDRADJ (outheader);
+#endif
+
+ /* Make the data segment address start in memory on a suitable boundary. */
+
+ if (! Tdata_flag_specified)
+ data_start = N_DATADDR (outheader) + text_start - TEXT_START (outheader);
+
+ /* Set up the set element vector */
+
+ if (!relocatable_output)
+ {
+ /* The set sector size is the number of set elements + a word
+ for each symbol for the length word at the beginning of the
+ vector, plus a word for each symbol for a zero at the end of
+ the vector (for incremental linking). */
+ set_sect_size
+ = (2 * set_symbol_count + set_vector_count) * sizeof (unsigned long);
+ set_sect_start = data_start + data_size;
+ data_size += set_sect_size;
+ set_vectors = (unsigned long *) xmalloc (set_sect_size);
+ setv_fill_count = 0;
+ }
+
+ /* Compute start addresses of each file's sections and symbols. */
+
+ each_full_file (relocate_file_addresses, 0);
+
+ /* Now, for each symbol, verify that it is defined globally at most once.
+ Put the global value into the symbol entry.
+ Common symbols are allocated here, in the BSS section.
+ Each defined symbol is given a '->defined' field
+ which is the correct N_ code for its definition,
+ except in the case of common symbols with -r.
+ Then make all the references point at the symbol entry
+ instead of being chained together. */
+
+ defined_global_sym_count = 0;
+
+ for (i = 0; i < TABSIZE; i++)
+ {
+ register symbol *sp;
+ for (sp = symtab[i]; sp; sp = sp->link)
+ {
+ /* For each symbol */
+ register struct nlist *p, *next;
+ int defs = 0, com = sp->max_common_size;
+ struct nlist *first_definition;
+ for (p = sp->refs; p; p = next)
+ {
+ register int type = p->n_type;
+
+ if (SET_ELEMENT_P (type))
+ {
+ if (relocatable_output)
+ fatal ("internal: global ref to set element with -r");
+ if (!defs++)
+ {
+ sp->value = set_sect_start
+ + setv_fill_count++ * sizeof (unsigned long);
+ sp->defined = N_SETV | N_EXT;
+ first_definition = p;
+ }
+ else if ((sp->defined & ~N_EXT) != N_SETV)
+ {
+ sp->multiply_defined = 1;
+ multiple_def_count++;
+ }
+ set_vectors[setv_fill_count++] = p->n_value;
+ }
+ else if ((type & N_EXT) && type != (N_UNDF | N_EXT))
+ {
+ /* non-common definition */
+ if (defs++ && sp->value != p->n_value)
+ {
+ sp->multiply_defined = 1;
+ multiple_def_count++;
+ }
+ sp->value = p->n_value;
+ sp->defined = type;
+ first_definition = p;
+ }
+ next = (struct nlist *) p->n_un.n_name;
+ p->n_un.n_name = (char *) sp;
+ }
+ /* Allocate as common if defined as common and not defined for real */
+ if (com && !defs)
+ {
+ if (!relocatable_output || force_common_definition)
+ {
+ int align = sizeof (int);
+
+ /* Round up to nearest sizeof (int). I don't know
+ whether this is necessary or not (given that
+ alignment is taken care of later), but it's
+ traditional, so I'll leave it in. Note that if
+ this size alignment is ever removed, ALIGN above
+ will have to be initialized to 1 instead of
+ sizeof (int). */
+
+ com = (com + sizeof (int) - 1) & (- sizeof (int));
+
+ while (!(com & align))
+ align <<= 1;
+
+ align = align > MAX_ALIGNMENT ? MAX_ALIGNMENT : align;
+
+ bss_size = ((((bss_size + data_size + data_start)
+ + (align - 1)) & (- align))
+ - data_size - data_start);
+
+ sp->value = data_start + data_size + bss_size;
+ sp->defined = N_BSS | N_EXT;
+ bss_size += com;
+ if (write_map)
+ printf ("Allocating common %s: %x at %x\n",
+ sp->name, com, sp->value);
+ }
+ else
+ {
+ sp->defined = 0;
+ undefined_global_sym_count++;
+ }
+ }
+ /* Set length word at front of vector and zero byte at end.
+ Reverse the vector itself to put it in file order. */
+ if ((sp->defined & ~N_EXT) == N_SETV)
+ {
+ unsigned long length_word_index
+ = (sp->value - set_sect_start) / sizeof (unsigned long);
+ unsigned long i, tmp;
+
+ set_vectors[length_word_index]
+ = setv_fill_count - 1 - length_word_index;
+
+ /* Reverse the vector. */
+ for (i = 1;
+ i < (setv_fill_count - length_word_index - 1) / 2 + 1;
+ i++)
+ {
+ tmp = set_vectors[length_word_index + i];
+ set_vectors[length_word_index + i]
+ = set_vectors[setv_fill_count - i];
+ set_vectors[setv_fill_count - i] = tmp;
+ }
+
+ set_vectors[setv_fill_count++] = 0;
+ }
+ if (sp->defined)
+ defined_global_sym_count++;
+ }
+ }
+
+ if (end_symbol) /* These are null if -r. */
+ {
+ etext_symbol->value = text_size + text_start;
+ edata_symbol->value = data_start + data_size;
+ end_symbol->value = data_start + data_size + bss_size;
+ }
+
+ /* Figure the data_pad now, so that it overlaps with the bss addresses. */
+
+ if (specified_data_size && specified_data_size > data_size)
+ data_pad = specified_data_size - data_size;
+
+ if (magic == ZMAGIC)
+ data_pad = ((data_pad + data_size + page_size - 1) & (- page_size))
+ - data_size;
+
+ bss_size -= data_pad;
+ if (bss_size < 0) bss_size = 0;
+
+ data_size += data_pad;
+}
+
+/* Accumulate the section sizes of input file ENTRY
+ into the section sizes of the output file. */
+
+void
+consider_file_section_lengths (entry)
+ register struct file_entry *entry;
+{
+ if (entry->just_syms_flag)
+ return;
+
+ entry->text_start_address = text_size;
+ /* If there were any vectors, we need to chop them off */
+ text_size += entry->header.a_text;
+ entry->data_start_address = data_size;
+ data_size += entry->header.a_data;
+ entry->bss_start_address = bss_size;
+ bss_size += entry->header.a_bss;
+
+ text_reloc_size += entry->header.a_trsize;
+ data_reloc_size += entry->header.a_drsize;
+}
+
+/* Determine where the sections of ENTRY go into the output file,
+ whose total section sizes are already known.
+ Also relocate the addresses of the file's local and debugger symbols. */
+
+void
+relocate_file_addresses (entry)
+ register struct file_entry *entry;
+{
+ entry->text_start_address += text_start;
+ /* Note that `data_start' and `data_size' have not yet been
+ adjusted for `data_pad'. If they had been, we would get the wrong
+ results here. */
+ entry->data_start_address += data_start;
+ entry->bss_start_address += data_start + data_size;
+
+ {
+ register struct nlist *p;
+ register struct nlist *end
+ = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
+
+ for (p = entry->symbols; p < end; p++)
+ {
+ /* If this belongs to a section, update it by the section's start address */
+ register int type = p->n_type & N_TYPE;
+
+ switch (type)
+ {
+ case N_TEXT:
+ case N_SETT:
+ p->n_value += entry->text_start_address;
+ break;
+ case N_DATA:
+ case N_SETV:
+ case N_SETD:
+ /* A symbol whose value is in the data section
+ is present in the input file as if the data section
+ started at an address equal to the length of the file's text. */
+ p->n_value += entry->data_start_address - entry->header.a_text;
+ break;
+ case N_BSS:
+ case N_SETB:
+ /* likewise for symbols with value in BSS. */
+ p->n_value += entry->bss_start_address
+ - entry->header.a_text - entry->header.a_data;
+ break;
+ }
+ }
+ }
+}
+
+void describe_file_sections (), list_file_locals ();
+
+/* Print a complete or partial map of the output file. */
+
+void
+print_symbols (outfile)
+ FILE *outfile;
+{
+ register int i;
+
+ fprintf (outfile, "\nFiles:\n\n");
+
+ each_file (describe_file_sections, outfile);
+
+ fprintf (outfile, "\nGlobal symbols:\n\n");
+
+ for (i = 0; i < TABSIZE; i++)
+ {
+ register symbol *sp;
+ for (sp = symtab[i]; sp; sp = sp->link)
+ {
+ if (sp->defined == 1)
+ fprintf (outfile, " %s: common, length 0x%x\n", sp->name, sp->max_common_size);
+ if (sp->defined)
+ fprintf (outfile, " %s: 0x%x\n", sp->name, sp->value);
+ else if (sp->referenced)
+ fprintf (outfile, " %s: undefined\n", sp->name);
+ }
+ }
+
+ each_file (list_file_locals, outfile);
+}
+
+void
+describe_file_sections (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ fprintf (outfile, " ");
+ print_file_name (entry, outfile);
+ if (entry->just_syms_flag)
+ fprintf (outfile, " symbols only\n", 0);
+ else
+ fprintf (outfile, " text %x(%x), data %x(%x), bss %x(%x) hex\n",
+ entry->text_start_address, entry->header.a_text,
+ entry->data_start_address, entry->header.a_data,
+ entry->bss_start_address, entry->header.a_bss);
+}
+
+void
+list_file_locals (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ register struct nlist
+ *p,
+ *end = entry->symbols + entry->header.a_syms / sizeof (struct nlist);
+
+ entry->strings = (char *) alloca (entry->string_size);
+ read_entry_strings (file_open (entry), entry);
+
+ fprintf (outfile, "\nLocal symbols of ");
+ print_file_name (entry, outfile);
+ fprintf (outfile, ":\n\n");
+
+ for (p = entry->symbols; p < end; p++)
+ /* If this is a definition,
+ update it if necessary by this file's start address. */
+ if (!(p->n_type & (N_STAB | N_EXT)))
+ fprintf (outfile, " %s: 0x%x\n",
+ entry->strings + p->n_un.n_strx, p->n_value);
+
+ entry->strings = 0; /* All done with them. */
+}
+
+
+/* Static vars for do_warnings and subroutines of it */
+int list_unresolved_refs; /* List unresolved refs */
+int list_warning_symbols; /* List warning syms */
+int list_multiple_defs; /* List multiple definitions */
+
+/*
+ * Structure for communication between do_file_warnings and it's
+ * helper routines. Will in practice be an array of three of these:
+ * 0) Current line, 1) Next line, 2) Source file info.
+ */
+struct line_debug_entry
+{
+ int line;
+ char *filename;
+ struct nlist *sym;
+};
+
+void qsort ();
+/*
+ * Helper routines for do_file_warnings.
+ */
+
+/* Return an integer less than, equal to, or greater than 0 as per the
+ relation between the two relocation entries. Used by qsort. */
+
+int
+relocation_entries_relation (rel1, rel2)
+ struct relocation_info *rel1, *rel2;
+{
+ return RELOC_ADDRESS(rel1) - RELOC_ADDRESS(rel2);
+}
+
+/* Moves to the next debugging symbol in the file. USE_DATA_SYMBOLS
+ determines the type of the debugging symbol to look for (DSLINE or
+ SLINE). STATE_POINTER keeps track of the old and new locatiosn in
+ the file. It assumes that state_pointer[1] is valid; ie
+ that it.sym points into some entry in the symbol table. If
+ state_pointer[1].sym == 0, this routine should not be called. */
+
+int
+next_debug_entry (use_data_symbols, state_pointer)
+ register int use_data_symbols;
+ /* Next must be passed by reference! */
+ struct line_debug_entry state_pointer[3];
+{
+ register struct line_debug_entry
+ *current = state_pointer,
+ *next = state_pointer + 1,
+ /* Used to store source file */
+ *source = state_pointer + 2;
+ struct file_entry *entry = (struct file_entry *) source->sym;
+
+ current->sym = next->sym;
+ current->line = next->line;
+ current->filename = next->filename;
+
+ while (++(next->sym) < (entry->symbols
+ + entry->header.a_syms/sizeof (struct nlist)))
+ {
+ /* n_type is a char, and N_SOL, N_EINCL and N_BINCL are > 0x80, so
+ * may look negative...therefore, must mask to low bits
+ */
+ switch (next->sym->n_type & 0xff)
+ {
+ case N_SLINE:
+ if (use_data_symbols) continue;
+ next->line = next->sym->n_desc;
+ return 1;
+ case N_DSLINE:
+ if (!use_data_symbols) continue;
+ next->line = next->sym->n_desc;
+ return 1;
+#ifdef HAVE_SUN_STABS
+ case N_EINCL:
+ next->filename = source->filename;
+ continue;
+#endif
+ case N_SO:
+ source->filename = next->sym->n_un.n_strx + entry->strings;
+ source->line++;
+#ifdef HAVE_SUN_STABS
+ case N_BINCL:
+#endif
+ case N_SOL:
+ next->filename
+ = next->sym->n_un.n_strx + entry->strings;
+ default:
+ continue;
+ }
+ }
+ next->sym = (struct nlist *) 0;
+ return 0;
+}
+
+/* Create a structure to save the state of a scan through the debug
+ symbols. USE_DATA_SYMBOLS is set if we should be scanning for
+ DSLINE's instead of SLINE's. entry is the file entry which points
+ at the symbols to use. */
+
+struct line_debug_entry *
+init_debug_scan (use_data_symbols, entry)
+ int use_data_symbols;
+ struct file_entry *entry;
+{
+ struct line_debug_entry
+ *state_pointer
+ = (struct line_debug_entry *)
+ xmalloc (3 * sizeof (struct line_debug_entry));
+ register struct line_debug_entry
+ *current = state_pointer,
+ *next = state_pointer + 1,
+ *source = state_pointer + 2; /* Used to store source file */
+
+ struct nlist *tmp;
+
+ for (tmp = entry->symbols;
+ tmp < (entry->symbols
+ + entry->header.a_syms/sizeof (struct nlist));
+ tmp++)
+ if (tmp->n_type == (int) N_SO)
+ break;
+
+ if (tmp >= (entry->symbols
+ + entry->header.a_syms/sizeof (struct nlist)))
+ {
+ /* I believe this translates to "We lose" */
+ current->filename = next->filename = entry->filename;
+ current->line = next->line = -1;
+ current->sym = next->sym = (struct nlist *) 0;
+ return state_pointer;
+ }
+
+ next->line = source->line = 0;
+ next->filename = source->filename
+ = (tmp->n_un.n_strx + entry->strings);
+ source->sym = (struct nlist *) entry;
+ next->sym = tmp;
+
+ next_debug_entry (use_data_symbols, state_pointer); /* To setup next */
+
+ if (!next->sym) /* No line numbers for this section; */
+ /* setup output results as appropriate */
+ {
+ if (source->line)
+ {
+ current->filename = source->filename = entry->filename;
+ current->line = -1; /* Don't print lineno */
+ }
+ else
+ {
+ current->filename = source->filename;
+ current->line = 0;
+ }
+ return state_pointer;
+ }
+
+
+ next_debug_entry (use_data_symbols, state_pointer); /* To setup current */
+
+ return state_pointer;
+}
+
+/* Takes an ADDRESS (in either text or data space) and a STATE_POINTER
+ which describes the current location in the implied scan through
+ the debug symbols within the file which ADDRESS is within, and
+ returns the source line number which corresponds to ADDRESS. */
+
+int
+address_to_line (address, state_pointer)
+ unsigned long address;
+ /* Next must be passed by reference! */
+ struct line_debug_entry state_pointer[3];
+{
+ struct line_debug_entry
+ *current = state_pointer,
+ *next = state_pointer + 1;
+ struct line_debug_entry *tmp_pointer;
+
+ int use_data_symbols;
+
+ if (next->sym)
+ use_data_symbols = (next->sym->n_type & N_TYPE) == N_DATA;
+ else
+ return current->line;
+
+ /* Go back to the beginning if we've already passed it. */
+ if (current->sym->n_value > address)
+ {
+ tmp_pointer = init_debug_scan (use_data_symbols,
+ (struct file_entry *)
+ ((state_pointer + 2)->sym));
+ state_pointer[0] = tmp_pointer[0];
+ state_pointer[1] = tmp_pointer[1];
+ state_pointer[2] = tmp_pointer[2];
+ free (tmp_pointer);
+ }
+
+ /* If we're still in a bad way, return -1, meaning invalid line. */
+ if (current->sym->n_value > address)
+ return -1;
+
+ while (next->sym
+ && next->sym->n_value <= address
+ && next_debug_entry (use_data_symbols, state_pointer))
+ ;
+ return current->line;
+}
+
+
+/* Macros for manipulating bitvectors. */
+#define BIT_SET_P(bv, index) ((bv)[(index) >> 3] & 1 << ((index) & 0x7))
+#define SET_BIT(bv, index) ((bv)[(index) >> 3] |= 1 << ((index) & 0x7))
+
+/* This routine will scan through the relocation data of file ENTRY,
+ printing out references to undefined symbols and references to
+ symbols defined in files with N_WARNING symbols. If DATA_SEGMENT
+ is non-zero, it will scan the data relocation segment (and use
+ N_DSLINE symbols to track line number); otherwise it will scan the
+ text relocation segment. Warnings will be printed on the output
+ stream OUTFILE. Eventually, every nlist symbol mapped through will
+ be marked in the NLIST_BITVECTOR, so we don't repeat ourselves when
+ we scan the nlists themselves. */
+
+do_relocation_warnings (entry, data_segment, outfile, nlist_bitvector)
+ struct file_entry *entry;
+ int data_segment;
+ FILE *outfile;
+ unsigned char *nlist_bitvector;
+{
+ struct relocation_info
+ *reloc_start = data_segment ? entry->datarel : entry->textrel,
+ *reloc;
+ int reloc_size
+ = ((data_segment ? entry->header.a_drsize : entry->header.a_trsize)
+ / sizeof (struct relocation_info));
+ int start_of_segment
+ = (data_segment ? entry->data_start_address : entry->text_start_address);
+ struct nlist *start_of_syms = entry->symbols;
+ struct line_debug_entry *state_pointer
+ = init_debug_scan (data_segment != 0, entry);
+ register struct line_debug_entry
+ *current = state_pointer;
+ /* Assigned to generally static values; should not be written into. */
+ char *errfmt;
+ /* Assigned to alloca'd values cand copied into; should be freed
+ when done. */
+ char *errmsg;
+ int invalidate_line_number;
+
+ /* We need to sort the relocation info here. Sheesh, so much effort
+ for one lousy error optimization. */
+
+ qsort (reloc_start, reloc_size, sizeof (struct relocation_info),
+ relocation_entries_relation);
+
+ for (reloc = reloc_start;
+ reloc < (reloc_start + reloc_size);
+ reloc++)
+ {
+ register struct nlist *s;
+ register symbol *g;
+
+ /* If the relocation isn't resolved through a symbol, continue */
+ if (!RELOC_EXTERN_P(reloc))
+ continue;
+
+ s = &(entry->symbols[RELOC_SYMBOL(reloc)]);
+
+ /* Local symbols shouldn't ever be used by relocation info, so
+ the next should be safe.
+ This is, of course, wrong. References to local BSS symbols can be
+ the targets of relocation info, and they can (must) be
+ resolved through symbols. However, these must be defined properly,
+ (the assembler would have caught it otherwise), so we can
+ ignore these cases. */
+ if (!(s->n_type & N_EXT))
+ continue;
+
+ g = (symbol *) s->n_un.n_name;
+ errmsg = 0;
+
+ if (!g->defined && list_unresolved_refs) /* Reference */
+ {
+ /* Mark as being noted by relocation warning pass. */
+ SET_BIT (nlist_bitvector, s - start_of_syms);
+
+ if (g->undef_refs >= MAX_UREFS_PRINTED) /* Listed too many */
+ continue;
+
+ /* Undefined symbol which we should mention */
+
+ if (++(g->undef_refs) == MAX_UREFS_PRINTED)
+ {
+ errfmt = "More undefined symbol %s refs follow";
+ invalidate_line_number = 1;
+ }
+ else
+ {
+ errfmt = "Undefined symbol %s referenced from %s segment";
+ invalidate_line_number = 0;
+ }
+ }
+ else /* Defined */
+ {
+ /* Potential symbol warning here */
+ if (!g->warning) continue;
+
+ /* Mark as being noted by relocation warning pass. */
+ SET_BIT (nlist_bitvector, s - start_of_syms);
+
+ errfmt = 0;
+ errmsg = g->warning;
+ invalidate_line_number = 0;
+ }
+
+
+ /* If errfmt == 0, errmsg has already been defined. */
+ if (errfmt != 0)
+ {
+ char *nm;
+
+ if (demangler == NULL || (nm = (*demangler)(g->name)) == NULL)
+ nm = g->name;
+ errmsg = (char *) xmalloc (strlen (errfmt) + strlen (nm) + 1);
+ sprintf (errmsg, errfmt, nm, data_segment ? "data" : "text");
+ if (nm != g->name)
+ free (nm);
+ }
+
+ address_to_line (RELOC_ADDRESS (reloc) + start_of_segment,
+ state_pointer);
+
+ if (current->line >=0)
+ fprintf (outfile, "%s:%d: %s\n", current->filename,
+ invalidate_line_number ? 0 : current->line, errmsg);
+ else
+ fprintf (outfile, "%s: %s\n", current->filename, errmsg);
+
+ if (errfmt != 0)
+ free (errmsg);
+ }
+
+ free (state_pointer);
+}
+
+/* Print on OUTFILE a list of all warnings generated by references
+ and/or definitions in the file ENTRY. List source file and line
+ number if possible, just the .o file if not. */
+
+void
+do_file_warnings (entry, outfile)
+ struct file_entry *entry;
+ FILE *outfile;
+{
+ int number_of_syms = entry->header.a_syms / sizeof (struct nlist);
+ unsigned char *nlist_bitvector
+ = (unsigned char *) alloca ((number_of_syms >> 3) + 1);
+ struct line_debug_entry *text_scan, *data_scan;
+ int i;
+ char *errfmt, *file_name;
+ int line_number;
+ int dont_allow_symbol_name;
+
+ bzero (nlist_bitvector, (number_of_syms >> 3) + 1);
+
+ /* Read in the files strings if they aren't available */
+ if (!entry->strings)
+ {
+ int desc;
+
+ entry->strings = (char *) alloca (entry->string_size);
+ desc = file_open (entry);
+ read_entry_strings (desc, entry);
+ }
+
+ read_file_relocation (entry);
+
+ /* Do text warnings based on a scan through the relocation info. */
+ do_relocation_warnings (entry, 0, outfile, nlist_bitvector);
+
+ /* Do data warnings based on a scan through the relocation info. */
+ do_relocation_warnings (entry, 1, outfile, nlist_bitvector);
+
+ /* Scan through all of the nlist entries in this file and pick up
+ anything that the scan through the relocation stuff didn't. */
+
+ text_scan = init_debug_scan (0, entry);
+ data_scan = init_debug_scan (1, entry);
+
+ for (i = 0; i < number_of_syms; i++)
+ {
+ struct nlist *s;
+ struct glosym *g;
+
+ s = entry->symbols + i;
+
+ if (!(s->n_type & N_EXT))
+ continue;
+
+ g = (symbol *) s->n_un.n_name;
+ dont_allow_symbol_name = 0;
+
+ if (list_multiple_defs && g->multiply_defined)
+ {
+ errfmt = "Definition of symbol %s (multiply defined)";
+ switch (s->n_type)
+ {
+ case N_TEXT | N_EXT:
+ line_number = address_to_line (s->n_value, text_scan);
+ file_name = text_scan[0].filename;
+ break;
+ case N_DATA | N_EXT:
+ line_number = address_to_line (s->n_value, data_scan);
+ file_name = data_scan[0].filename;
+ break;
+ case N_SETA | N_EXT:
+ case N_SETT | N_EXT:
+ case N_SETD | N_EXT:
+ case N_SETB | N_EXT:
+ if (g->multiply_defined == 2)
+ continue;
+ errfmt = "First set element definition of symbol %s (multiply defined)";
+ break;
+ default:
+ continue; /* Don't print out multiple defs
+ at references. */
+ }
+ }
+ else if (BIT_SET_P (nlist_bitvector, i))
+ continue;
+ else if (list_unresolved_refs && !g->defined)
+ {
+ if (g->undef_refs >= MAX_UREFS_PRINTED)
+ continue;
+
+ if (++(g->undef_refs) == MAX_UREFS_PRINTED)
+ errfmt = "More undefined \"%s\" refs follow";
+ else
+ errfmt = "Undefined symbol \"%s\" referenced";
+ line_number = -1;
+ }
+ else if (g->warning)
+ {
+ /* There are two cases in which we don't want to
+ do this. The first is if this is a definition instead of
+ a reference. The second is if it's the reference used by
+ the warning stabs itself. */
+ if (s->n_type != (N_EXT | N_UNDF)
+ || (i && (s-1)->n_type == N_WARNING))
+ continue;
+
+ errfmt = g->warning;
+ line_number = -1;
+ dont_allow_symbol_name = 1;
+ }
+ else
+ continue;
+
+ if (line_number == -1)
+ fprintf (outfile, "%s: ", entry->filename);
+ else
+ fprintf (outfile, "%s:%d: ", file_name, line_number);
+
+ if (dont_allow_symbol_name)
+ fprintf (outfile, "%s", errfmt);
+ else
+ {
+ char *nm;
+ if (demangler != NULL && (nm = (*demangler)(g->name)) != NULL)
+ {
+ fprintf (outfile, errfmt, nm);
+ free (nm);
+ }
+ else
+ fprintf (outfile, errfmt, g->name);
+ }
+
+ fputc ('\n', outfile);
+ }
+ free (text_scan);
+ free (data_scan);
+ entry->strings = 0; /* Since it will dissapear anyway. */
+}
+
+do_warnings (outfile)
+ FILE *outfile;
+{
+ list_unresolved_refs = !relocatable_output && undefined_global_sym_count;
+ list_warning_symbols = warning_count;
+ list_multiple_defs = multiple_def_count != 0;
+
+ if (!(list_unresolved_refs ||
+ list_warning_symbols ||
+ list_multiple_defs ))
+ /* No need to run this routine */
+ return;
+
+ each_file (do_file_warnings, outfile);
+
+ if (list_unresolved_refs || list_multiple_defs)
+ make_executable = 0;
+}
+
+/* Write the output file */
+
+void
+write_output ()
+{
+ struct stat statbuf;
+ int filemode;
+
+ (void) unlink (output_filename);
+ outdesc = open (output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (outdesc < 0) perror_name (output_filename);
+
+ if (fstat (outdesc, &statbuf) < 0)
+ perror_name (output_filename);
+
+ (void) fchflags(outdesc, statbuf.st_flags | UF_NODUMP);
+
+ filemode = statbuf.st_mode;
+ (void) fchmod (outdesc, filemode & ~0111);
+
+ /* Output the a.out header. */
+ write_header ();
+
+ /* Output the text and data segments, relocating as we go. */
+ write_text ();
+ write_data ();
+
+ /* Output the merged relocation info, if requested with `-r'. */
+ if (relocatable_output)
+ write_rel ();
+
+ /* Output the symbol table (both globals and locals). */
+ write_syms ();
+
+ /* Copy any GDB symbol segments from input files. */
+ write_symsegs ();
+
+ if (fchmod (outdesc, filemode | 0111) == -1)
+ perror_name (output_filename);
+
+ close (outdesc);
+}
+
+void modify_location (), perform_relocation (), copy_text (), copy_data ();
+
+void
+write_header ()
+{
+ N_SET_MAGIC (outheader, magic);
+ outheader.a_text = text_size;
+#ifdef sequent
+ outheader.a_text += N_ADDRADJ (outheader);
+ if (entry_symbol == 0)
+ entry_symbol = getsym("start");
+#endif
+ outheader.a_data = data_size;
+ outheader.a_bss = bss_size;
+ outheader.a_entry = (entry_symbol ? entry_symbol->value
+ : text_start + entry_offset);
+#ifdef COFF_ENCAPSULATE
+ if (need_coff_header)
+ {
+ /* We are encapsulating BSD format within COFF format. */
+ struct coffscn *tp, *dp, *bp;
+
+ tp = &coffheader.scns[0];
+ dp = &coffheader.scns[1];
+ bp = &coffheader.scns[2];
+
+ strcpy (tp->s_name, ".text");
+ tp->s_paddr = text_start;
+ tp->s_vaddr = text_start;
+ tp->s_size = text_size;
+ tp->s_scnptr = sizeof (struct coffheader) + sizeof (struct exec);
+ tp->s_relptr = 0;
+ tp->s_lnnoptr = 0;
+ tp->s_nreloc = 0;
+ tp->s_nlnno = 0;
+ tp->s_flags = 0x20;
+ strcpy (dp->s_name, ".data");
+ dp->s_paddr = data_start;
+ dp->s_vaddr = data_start;
+ dp->s_size = data_size;
+ dp->s_scnptr = tp->s_scnptr + tp->s_size;
+ dp->s_relptr = 0;
+ dp->s_lnnoptr = 0;
+ dp->s_nreloc = 0;
+ dp->s_nlnno = 0;
+ dp->s_flags = 0x40;
+ strcpy (bp->s_name, ".bss");
+ bp->s_paddr = dp->s_vaddr + dp->s_size;
+ bp->s_vaddr = bp->s_paddr;
+ bp->s_size = bss_size;
+ bp->s_scnptr = 0;
+ bp->s_relptr = 0;
+ bp->s_lnnoptr = 0;
+ bp->s_nreloc = 0;
+ bp->s_nlnno = 0;
+ bp->s_flags = 0x80;
+
+ coffheader.f_magic = COFF_MAGIC;
+ coffheader.f_nscns = 3;
+ /* store an unlikely time so programs can
+ * tell that there is a bsd header
+ */
+ coffheader.f_timdat = 1;
+ coffheader.f_symptr = 0;
+ coffheader.f_nsyms = 0;
+ coffheader.f_opthdr = 28;
+ coffheader.f_flags = 0x103;
+ /* aouthdr */
+ coffheader.magic = ZMAGIC;
+ coffheader.vstamp = 0;
+ coffheader.tsize = tp->s_size;
+ coffheader.dsize = dp->s_size;
+ coffheader.bsize = bp->s_size;
+ coffheader.entry = outheader.a_entry;
+ coffheader.text_start = tp->s_vaddr;
+ coffheader.data_start = dp->s_vaddr;
+ }
+#endif
+
+#ifdef INITIALIZE_HEADER
+ INITIALIZE_HEADER;
+#endif
+
+ if (strip_symbols == STRIP_ALL)
+ nsyms = 0;
+ else
+ {
+ nsyms = (defined_global_sym_count
+ + undefined_global_sym_count);
+ if (discard_locals == DISCARD_L)
+ nsyms += non_L_local_sym_count;
+ else if (discard_locals == DISCARD_NONE)
+ nsyms += local_sym_count;
+ /* One extra for following reference on indirects */
+ if (relocatable_output)
+ nsyms += set_symbol_count + global_indirect_count;
+ }
+
+ if (strip_symbols == STRIP_NONE)
+ nsyms += debugger_sym_count;
+
+ outheader.a_syms = nsyms * sizeof (struct nlist);
+
+ if (relocatable_output)
+ {
+ outheader.a_trsize = text_reloc_size;
+ outheader.a_drsize = data_reloc_size;
+ }
+ else
+ {
+ outheader.a_trsize = 0;
+ outheader.a_drsize = 0;
+ }
+
+#ifdef COFF_ENCAPSULATE
+ if (need_coff_header)
+ mywrite (&coffheader, sizeof coffheader, 1, outdesc);
+#endif
+ mywrite (&outheader, sizeof (struct exec), 1, outdesc);
+
+ /* Output whatever padding is required in the executable file
+ between the header and the start of the text. */
+
+#ifndef COFF_ENCAPSULATE
+ padfile (N_TXTOFF (outheader) - sizeof outheader, outdesc);
+#endif
+}
+
+/* Relocate the text segment of each input file
+ and write to the output file. */
+
+void
+write_text ()
+{
+ if (trace_files)
+ fprintf (stderr, "Copying and relocating text:\n\n");
+
+ each_full_file (copy_text, 0);
+ file_close ();
+
+ if (trace_files)
+ fprintf (stderr, "\n");
+
+ padfile (text_pad, outdesc);
+}
+
+int
+text_offset (entry)
+ struct file_entry *entry;
+{
+ return entry->starting_offset + N_TXTOFF (entry->header);
+}
+
+/* Read in all of the relocation information */
+
+void
+read_relocation ()
+{
+ each_full_file (read_file_relocation, 0);
+}
+
+/* Read in the relocation sections of ENTRY if necessary */
+
+void
+read_file_relocation (entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *reloc;
+ int desc;
+ int read_return;
+
+ desc = -1;
+ if (!entry->textrel)
+ {
+ reloc = (struct relocation_info *) xmalloc (entry->header.a_trsize);
+ desc = file_open (entry);
+ lseek (desc,
+ text_offset (entry) + entry->header.a_text + entry->header.a_data,
+ L_SET);
+ if (entry->header.a_trsize != (read_return = read (desc, reloc, entry->header.a_trsize)))
+ {
+ fprintf (stderr, "Return from read: %d\n", read_return);
+ fatal_with_file ("premature eof in text relocation of ", entry);
+ }
+ entry->textrel = reloc;
+ }
+
+ if (!entry->datarel)
+ {
+ reloc = (struct relocation_info *) xmalloc (entry->header.a_drsize);
+ if (desc == -1) desc = file_open (entry);
+ lseek (desc,
+ text_offset (entry) + entry->header.a_text
+ + entry->header.a_data + entry->header.a_trsize,
+ L_SET);
+ if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize))
+ fatal_with_file ("premature eof in data relocation of ", entry);
+ entry->datarel = reloc;
+ }
+}
+
+/* Read the text segment contents of ENTRY, relocate them,
+ and write the result to the output file.
+ If `-r', save the text relocation for later reuse. */
+
+void
+copy_text (entry)
+ struct file_entry *entry;
+{
+ register char *bytes;
+ register int desc;
+ register struct relocation_info *reloc;
+
+ if (trace_files)
+ prline_file_name (entry, stderr);
+
+ desc = file_open (entry);
+
+ /* Allocate space for the file's text section */
+
+ bytes = (char *) alloca (entry->header.a_text);
+
+ /* Deal with relocation information however is appropriate */
+
+ if (entry->textrel) reloc = entry->textrel;
+ else if (relocatable_output)
+ {
+ read_file_relocation (entry);
+ reloc = entry->textrel;
+ }
+ else
+ {
+ reloc = (struct relocation_info *) alloca (entry->header.a_trsize);
+ lseek (desc, text_offset (entry) + entry->header.a_text + entry->header.a_data, 0);
+ if (entry->header.a_trsize != read (desc, reloc, entry->header.a_trsize))
+ fatal_with_file ("premature eof in text relocation of ", entry);
+ }
+
+ /* Read the text section into core. */
+
+ lseek (desc, text_offset (entry), 0);
+ if (entry->header.a_text != read (desc, bytes, entry->header.a_text))
+ fatal_with_file ("premature eof in text section of ", entry);
+
+
+ /* Relocate the text according to the text relocation. */
+
+ perform_relocation (bytes, entry->text_start_address, entry->header.a_text,
+ reloc, entry->header.a_trsize, entry);
+
+ /* Write the relocated text to the output file. */
+
+ mywrite (bytes, 1, entry->header.a_text, outdesc);
+}
+
+/* Relocate the data segment of each input file
+ and write to the output file. */
+
+void
+write_data ()
+{
+ if (trace_files)
+ fprintf (stderr, "Copying and relocating data:\n\n");
+
+ each_full_file (copy_data, 0);
+ file_close ();
+
+ /* Write out the set element vectors. See digest symbols for
+ description of length of the set vector section. */
+
+ if (set_vector_count)
+ mywrite (set_vectors, 2 * set_symbol_count + set_vector_count,
+ sizeof (unsigned long), outdesc);
+
+ if (trace_files)
+ fprintf (stderr, "\n");
+
+ padfile (data_pad, outdesc);
+}
+
+/* Read the data segment contents of ENTRY, relocate them,
+ and write the result to the output file.
+ If `-r', save the data relocation for later reuse.
+ See comments in `copy_text'. */
+
+void
+copy_data (entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *reloc;
+ register char *bytes;
+ register int desc;
+
+ if (trace_files)
+ prline_file_name (entry, stderr);
+
+ desc = file_open (entry);
+
+ bytes = (char *) alloca (entry->header.a_data);
+
+ if (entry->datarel) reloc = entry->datarel;
+ else if (relocatable_output) /* Will need this again */
+ {
+ read_file_relocation (entry);
+ reloc = entry->datarel;
+ }
+ else
+ {
+ reloc = (struct relocation_info *) alloca (entry->header.a_drsize);
+ lseek (desc, text_offset (entry) + entry->header.a_text
+ + entry->header.a_data + entry->header.a_trsize,
+ 0);
+ if (entry->header.a_drsize != read (desc, reloc, entry->header.a_drsize))
+ fatal_with_file ("premature eof in data relocation of ", entry);
+ }
+
+ lseek (desc, text_offset (entry) + entry->header.a_text, 0);
+ if (entry->header.a_data != read (desc, bytes, entry->header.a_data))
+ fatal_with_file ("premature eof in data section of ", entry);
+
+ perform_relocation (bytes, entry->data_start_address - entry->header.a_text,
+ entry->header.a_data, reloc, entry->header.a_drsize, entry);
+
+ mywrite (bytes, 1, entry->header.a_data, outdesc);
+}
+
+/* Relocate ENTRY's text or data section contents.
+ DATA is the address of the contents, in core.
+ DATA_SIZE is the length of the contents.
+ PC_RELOCATION is the difference between the address of the contents
+ in the output file and its address in the input file.
+ RELOC_INFO is the address of the relocation info, in core.
+ RELOC_SIZE is its length in bytes. */
+/* This version is about to be severly hacked by Randy. Hope it
+ works afterwards. */
+void
+perform_relocation (data, pc_relocation, data_size, reloc_info, reloc_size, entry)
+ char *data;
+ struct relocation_info *reloc_info;
+ struct file_entry *entry;
+ int pc_relocation;
+ int data_size;
+ int reloc_size;
+{
+ register struct relocation_info *p = reloc_info;
+ struct relocation_info *end
+ = reloc_info + reloc_size / sizeof (struct relocation_info);
+ int text_relocation = entry->text_start_address;
+ int data_relocation = entry->data_start_address - entry->header.a_text;
+ int bss_relocation
+ = entry->bss_start_address - entry->header.a_text - entry->header.a_data;
+
+ for (; p < end; p++)
+ {
+ register int relocation = 0;
+ register int addr = RELOC_ADDRESS(p);
+ register unsigned int mask = 0;
+
+ if (addr >= data_size)
+ fatal_with_file ("relocation address out of range in ", entry);
+
+ if (RELOC_EXTERN_P(p))
+ {
+ int symindex = RELOC_SYMBOL (p) * sizeof (struct nlist);
+ symbol *sp = ((symbol *)
+ (((struct nlist *)
+ (((char *)entry->symbols) + symindex))
+ ->n_un.n_name));
+
+#ifdef N_INDR
+ /* Resolve indirection */
+ if ((sp->defined & ~N_EXT) == N_INDR)
+ sp = (symbol *) sp->value;
+#endif
+
+ if (symindex >= entry->header.a_syms)
+ fatal_with_file ("relocation symbolnum out of range in ", entry);
+
+ /* If the symbol is undefined, leave it at zero. */
+ if (! sp->defined)
+ relocation = 0;
+ else
+ relocation = sp->value;
+ }
+ else switch (RELOC_TYPE(p))
+ {
+ case N_TEXT:
+ case N_TEXT | N_EXT:
+ relocation = text_relocation;
+ break;
+
+ case N_DATA:
+ case N_DATA | N_EXT:
+ /* A word that points to beginning of the the data section
+ initially contains not 0 but rather the "address" of that section
+ in the input file, which is the length of the file's text. */
+ relocation = data_relocation;
+ break;
+
+ case N_BSS:
+ case N_BSS | N_EXT:
+ /* Similarly, an input word pointing to the beginning of the bss
+ initially contains the length of text plus data of the file. */
+ relocation = bss_relocation;
+ break;
+
+ case N_ABS:
+ case N_ABS | N_EXT:
+ /* Don't know why this code would occur, but apparently it does. */
+ break;
+
+ default:
+ fatal_with_file ("nonexternal relocation code invalid in ", entry);
+ }
+
+#ifdef RELOC_ADD_EXTRA
+ relocation += RELOC_ADD_EXTRA(p);
+ if (relocatable_output)
+ {
+ /* Non-PC relative relocations which are absolute
+ or which have become non-external now have fixed
+ relocations. Set the ADD_EXTRA of this relocation
+ to be the relocation we have now determined. */
+ if (! RELOC_PCREL_P (p))
+ {
+ if ((int)p->r_type <= RELOC_32
+ || RELOC_EXTERN_P (p) == 0)
+ RELOC_ADD_EXTRA (p) = relocation;
+ }
+ /* External PC-relative relocations continue to move around;
+ update their relocations by the amount they have moved
+ so far. */
+ else if (RELOC_EXTERN_P (p))
+ RELOC_ADD_EXTRA (p) -= pc_relocation;
+ continue;
+ }
+#endif
+
+ if (RELOC_PCREL_P(p))
+ relocation -= pc_relocation;
+
+ relocation >>= RELOC_VALUE_RIGHTSHIFT(p);
+
+ /* Unshifted mask for relocation */
+ mask = 1 << RELOC_TARGET_BITSIZE(p) - 1;
+ mask |= mask - 1;
+ relocation &= mask;
+
+ /* Shift everything up to where it's going to be used */
+ relocation <<= RELOC_TARGET_BITPOS(p);
+ mask <<= RELOC_TARGET_BITPOS(p);
+
+ switch (RELOC_TARGET_SIZE(p))
+ {
+ case 0:
+ if (RELOC_MEMORY_SUB_P(p))
+ relocation -= mask & *(char *) (data + addr);
+ else if (RELOC_MEMORY_ADD_P(p))
+ relocation += mask & *(char *) (data + addr);
+ *(char *) (data + addr) &= ~mask;
+ *(char *) (data + addr) |= relocation;
+ break;
+
+ case 1:
+#ifdef tahoe
+ if (((int) data + addr & 1) == 0)
+ {
+#endif
+ if (RELOC_MEMORY_SUB_P(p))
+ relocation -= mask & *(short *) (data + addr);
+ else if (RELOC_MEMORY_ADD_P(p))
+ relocation += mask & *(short *) (data + addr);
+ *(short *) (data + addr) &= ~mask;
+ *(short *) (data + addr) |= relocation;
+#ifdef tahoe
+ }
+ /*
+ * The CCI Power 6 (aka Tahoe) architecture has byte-aligned
+ * instruction operands but requires data accesses to be aligned.
+ * Brain-damage...
+ */
+ else
+ {
+ unsigned char *da = (unsigned char *) (data + addr);
+ unsigned short s = da[0] << 8 | da[1];
+
+ if (RELOC_MEMORY_SUB_P(p))
+ relocation -= mask & s;
+ else if (RELOC_MEMORY_ADD_P(p))
+ relocation += mask & s;
+ s &= ~mask;
+ s |= relocation;
+ da[0] = s >> 8;
+ da[1] = s;
+ }
+#endif
+ break;
+
+ case 2:
+#ifndef _CROSS_TARGET_ARCH
+#ifdef tahoe
+ if (((int) data + addr & 3) == 0)
+ {
+#endif
+ if (RELOC_MEMORY_SUB_P(p))
+ relocation -= mask & *(long *) (data + addr);
+ else if (RELOC_MEMORY_ADD_P(p))
+ relocation += mask & *(long *) (data + addr);
+ *(long *) (data + addr) &= ~mask;
+ *(long *) (data + addr) |= relocation;
+#ifdef tahoe
+ }
+ else
+ {
+ unsigned char *da = (unsigned char *) (data + addr);
+ unsigned long l = da[0] << 24 | da[1] << 16 | da[2] << 8 | da[3];
+
+ if (RELOC_MEMORY_SUB_P(p))
+ relocation -= mask & l;
+ else if (RELOC_MEMORY_ADD_P(p))
+ relocation += mask & l;
+ l &= ~mask;
+ l |= relocation;
+ da[0] = l >> 24;
+ da[1] = l >> 16;
+ da[2] = l >> 8;
+ da[3] = l;
+ }
+#endif
+#else
+ /* Handle long word alignment requirements of SPARC architecture */
+ /* WARNING: This fix makes an assumption on byte ordering */
+ /* Marc Ullman, Stanford University Nov. 1 1989 */
+ if (RELOC_MEMORY_SUB_P(p)) {
+ relocation -= mask &
+ ((*(unsigned short *) (data + addr) << 16) |
+ *(unsigned short *) (data + addr + 2));
+ } else if (RELOC_MEMORY_ADD_P(p)) {
+ relocation += mask &
+ ((*(unsigned short *) (data + addr) << 16) |
+ *(unsigned short *) (data + addr + 2));
+ }
+ *(unsigned short *) (data + addr) &= (~mask >> 16);
+ *(unsigned short *) (data + addr + 2) &= (~mask & 0xffff);
+ *(unsigned short *) (data + addr) |= (relocation >> 16);
+ *(unsigned short *) (data + addr + 2) |= (relocation & 0xffff);
+#endif
+ break;
+
+ default:
+ fatal_with_file ("Unimplemented relocation field length in ", entry);
+ }
+ }
+}
+
+/* For relocatable_output only: write out the relocation,
+ relocating the addresses-to-be-relocated. */
+
+void coptxtrel (), copdatrel ();
+
+void
+write_rel ()
+{
+ register int i;
+ register int count = 0;
+
+ if (trace_files)
+ fprintf (stderr, "Writing text relocation:\n\n");
+
+ /* Assign each global symbol a sequence number, giving the order
+ in which `write_syms' will write it.
+ This is so we can store the proper symbolnum fields
+ in relocation entries we write. */
+
+ for (i = 0; i < TABSIZE; i++)
+ {
+ symbol *sp;
+ for (sp = symtab[i]; sp; sp = sp->link)
+ if (sp->referenced || sp->defined)
+ {
+ sp->def_count = count++;
+ /* Leave room for the reference required by N_INDR, if
+ necessary. */
+ if ((sp->defined & ~N_EXT) == N_INDR)
+ count++;
+ }
+ }
+ /* Correct, because if (relocatable_output), we will also be writing
+ whatever indirect blocks we have. */
+ if (count != defined_global_sym_count
+ + undefined_global_sym_count + global_indirect_count)
+ fatal ("internal error");
+
+ /* Write out the relocations of all files, remembered from copy_text. */
+
+ each_full_file (coptxtrel, 0);
+
+ if (trace_files)
+ fprintf (stderr, "\nWriting data relocation:\n\n");
+
+ each_full_file (copdatrel, 0);
+
+ if (trace_files)
+ fprintf (stderr, "\n");
+}
+
+void
+coptxtrel (entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *p, *end;
+ register int reloc = entry->text_start_address;
+
+ p = entry->textrel;
+ end = (struct relocation_info *) (entry->header.a_trsize + (char *) p);
+ while (p < end)
+ {
+ RELOC_ADDRESS(p) += reloc;
+ if (RELOC_EXTERN_P(p))
+ {
+ register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist);
+ symbol *symptr = ((symbol *)
+ (((struct nlist *)
+ (((char *)entry->symbols) + symindex))
+ ->n_un.n_name));
+
+ if (symindex >= entry->header.a_syms)
+ fatal_with_file ("relocation symbolnum out of range in ", entry);
+
+#ifdef N_INDR
+ /* Resolve indirection. */
+ if ((symptr->defined & ~N_EXT) == N_INDR)
+ symptr = (symbol *) symptr->value;
+#endif
+
+ /* If the symbol is now defined, change the external relocation
+ to an internal one. */
+
+ if (symptr->defined)
+ {
+ RELOC_EXTERN_P(p) = 0;
+ RELOC_SYMBOL(p) = (symptr->defined & N_TYPE);
+#ifdef RELOC_ADD_EXTRA
+ /* If we aren't going to be adding in the value in
+ memory on the next pass of the loader, then we need
+ to add it in from the relocation entry. Otherwise
+ the work we did in this pass is lost. */
+ if (!RELOC_MEMORY_ADD_P(p))
+ RELOC_ADD_EXTRA (p) += symptr->value;
+#endif
+ }
+ else
+ /* Debugger symbols come first, so have to start this
+ after them. */
+ RELOC_SYMBOL(p) = (symptr->def_count + nsyms
+ - defined_global_sym_count
+ - undefined_global_sym_count
+ - global_indirect_count);
+ }
+ p++;
+ }
+ mywrite (entry->textrel, 1, entry->header.a_trsize, outdesc);
+}
+
+void
+copdatrel (entry)
+ struct file_entry *entry;
+{
+ register struct relocation_info *p, *end;
+ /* Relocate the address of the relocation.
+ Old address is relative to start of the input file's data section.
+ New address is relative to start of the output file's data section. */
+ register int reloc = entry->data_start_address - text_size;
+
+ p = entry->datarel;
+ end = (struct relocation_info *) (entry->header.a_drsize + (char *) p);
+ while (p < end)
+ {
+ RELOC_ADDRESS(p) += reloc;
+ if (RELOC_EXTERN_P(p))
+ {
+ register int symindex = RELOC_SYMBOL(p) * sizeof (struct nlist);
+ symbol *symptr = ((symbol *)
+ (((struct nlist *)
+ (((char *)entry->symbols) + symindex))
+ ->n_un.n_name));
+ int symtype;
+
+ if (symindex >= entry->header.a_syms)
+ fatal_with_file ("relocation symbolnum out of range in ", entry);
+
+#ifdef N_INDR
+ /* Resolve indirection. */
+ if ((symptr->defined & ~N_EXT) == N_INDR)
+ symptr = (symbol *) symptr->value;
+#endif
+
+ symtype = symptr->defined & N_TYPE;
+
+ if (force_common_definition
+ || symtype == N_DATA || symtype == N_TEXT || symtype == N_ABS)
+ {
+ RELOC_EXTERN_P(p) = 0;
+ RELOC_SYMBOL(p) = symtype;
+ }
+ else
+ /* Debugger symbols come first, so have to start this
+ after them. */
+ RELOC_SYMBOL(p)
+ = (((symbol *)
+ (((struct nlist *)
+ (((char *)entry->symbols) + symindex))
+ ->n_un.n_name))
+ ->def_count
+ + nsyms - defined_global_sym_count
+ - undefined_global_sym_count
+ - global_indirect_count);
+ }
+ p++;
+ }
+ mywrite (entry->datarel, 1, entry->header.a_drsize, outdesc);
+}
+
+void write_file_syms ();
+void write_string_table ();
+
+/* Offsets and current lengths of symbol and string tables in output file. */
+
+int symbol_table_offset;
+int symbol_table_len;
+
+/* Address in output file where string table starts. */
+int string_table_offset;
+
+/* Offset within string table
+ where the strings in `strtab_vector' should be written. */
+int string_table_len;
+
+/* Total size of string table strings allocated so far,
+ including strings in `strtab_vector'. */
+int strtab_size;
+
+/* Vector whose elements are strings to be added to the string table. */
+char **strtab_vector;
+
+/* Vector whose elements are the lengths of those strings. */
+int *strtab_lens;
+
+/* Index in `strtab_vector' at which the next string will be stored. */
+int strtab_index;
+
+/* Add the string NAME to the output file string table.
+ Record it in `strtab_vector' to be output later.
+ Return the index within the string table that this string will have. */
+
+int
+assign_string_table_index (name)
+ char *name;
+{
+ register int index = strtab_size;
+ register int len = strlen (name) + 1;
+
+ strtab_size += len;
+ strtab_vector[strtab_index] = name;
+ strtab_lens[strtab_index++] = len;
+
+ return index;
+}
+
+FILE *outstream = (FILE *) 0;
+
+/* Write the contents of `strtab_vector' into the string table.
+ This is done once for each file's local&debugger symbols
+ and once for the global symbols. */
+
+void
+write_string_table ()
+{
+ register int i;
+
+ lseek (outdesc, string_table_offset + string_table_len, 0);
+
+ if (!outstream)
+ outstream = fdopen (outdesc, "w");
+
+ for (i = 0; i < strtab_index; i++)
+ {
+ fwrite (strtab_vector[i], 1, strtab_lens[i], outstream);
+ string_table_len += strtab_lens[i];
+ }
+
+ fflush (outstream);
+
+ /* Report I/O error such as disk full. */
+ if (ferror (outstream))
+ perror_name (output_filename);
+}
+
+/* Write the symbol table and string table of the output file. */
+
+void
+write_syms ()
+{
+ /* Number of symbols written so far. */
+ int syms_written = 0;
+ register int i;
+ register symbol *sp;
+
+ /* Buffer big enough for all the global symbols. One
+ extra struct for each indirect symbol to hold the extra reference
+ following. */
+ struct nlist *buf
+ = (struct nlist *) alloca ((defined_global_sym_count
+ + undefined_global_sym_count
+ + global_indirect_count)
+ * sizeof (struct nlist));
+ /* Pointer for storing into BUF. */
+ register struct nlist *bufp = buf;
+
+ /* Size of string table includes the bytes that store the size. */
+ strtab_size = sizeof strtab_size;
+
+ symbol_table_offset = N_SYMOFF (outheader);
+ symbol_table_len = 0;
+ string_table_offset = N_STROFF (outheader);
+ string_table_len = strtab_size;
+
+ if (strip_symbols == STRIP_ALL)
+ return;
+
+ /* Write the local symbols defined by the various files. */
+
+ each_file (write_file_syms, &syms_written);
+ file_close ();
+
+ /* Now write out the global symbols. */
+
+ /* Allocate two vectors that record the data to generate the string
+ table from the global symbols written so far. This must include
+ extra space for the references following indirect outputs. */
+
+ strtab_vector = (char **) alloca ((num_hash_tab_syms
+ + global_indirect_count) * sizeof (char *));
+ strtab_lens = (int *) alloca ((num_hash_tab_syms
+ + global_indirect_count) * sizeof (int));
+ strtab_index = 0;
+
+ /* Scan the symbol hash table, bucket by bucket. */
+
+ for (i = 0; i < TABSIZE; i++)
+ for (sp = symtab[i]; sp; sp = sp->link)
+ {
+ struct nlist nl;
+
+ nl.n_other = 0;
+ nl.n_desc = 0;
+
+ /* Compute a `struct nlist' for the symbol. */
+
+ if (sp->defined || sp->referenced)
+ {
+ /* common condition needs to be before undefined condition */
+ /* because unallocated commons are set undefined in */
+ /* digest_symbols */
+ if (sp->defined > 1) /* defined with known type */
+ {
+ /* If the target of an indirect symbol has been
+ defined and we are outputting an executable,
+ resolve the indirection; it's no longer needed */
+ if (!relocatable_output
+ && ((sp->defined & N_TYPE) == N_INDR)
+ && (((symbol *) sp->value)->defined > 1))
+ {
+ symbol *newsp = (symbol *) sp->value;
+ nl.n_type = newsp->defined;
+ nl.n_value = newsp->value;
+ }
+ else
+ {
+ nl.n_type = sp->defined;
+ if (sp->defined != (N_INDR | N_EXT))
+ nl.n_value = sp->value;
+ else
+ nl.n_value = 0;
+ }
+ }
+ else if (sp->max_common_size) /* defined as common but not allocated. */
+ {
+ /* happens only with -r and not -d */
+ /* write out a common definition */
+ nl.n_type = N_UNDF | N_EXT;
+ nl.n_value = sp->max_common_size;
+ }
+ else if (!sp->defined) /* undefined -- legit only if -r */
+ {
+ nl.n_type = N_UNDF | N_EXT;
+ nl.n_value = 0;
+ }
+ else
+ fatal ("internal error: %s defined in mysterious way", sp->name);
+
+ /* Allocate string table space for the symbol name. */
+
+ nl.n_un.n_strx = assign_string_table_index (sp->name);
+
+ /* Output to the buffer and count it. */
+
+ *bufp++ = nl;
+ syms_written++;
+ if (nl.n_type == (N_INDR | N_EXT))
+ {
+ struct nlist xtra_ref;
+ xtra_ref.n_type = N_EXT | N_UNDF;
+ xtra_ref.n_un.n_strx
+ = assign_string_table_index (((symbol *) sp->value)->name);
+ xtra_ref.n_other = 0;
+ xtra_ref.n_desc = 0;
+ xtra_ref.n_value = 0;
+ *bufp++ = xtra_ref;
+ syms_written++;
+ }
+ }
+ }
+
+ /* Output the buffer full of `struct nlist's. */
+
+ lseek (outdesc, symbol_table_offset + symbol_table_len, 0);
+ mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc);
+ symbol_table_len += sizeof (struct nlist) * (bufp - buf);
+
+ if (syms_written != nsyms)
+ fatal ("internal error: wrong number of symbols written into output file", 0);
+
+ if (symbol_table_offset + symbol_table_len != string_table_offset)
+ fatal ("internal error: inconsistent symbol table length", 0);
+
+ /* Now the total string table size is known, so write it.
+ We are already positioned at the right place in the file. */
+
+ mywrite (&strtab_size, sizeof (int), 1, outdesc); /* we're at right place */
+
+ /* Write the strings for the global symbols. */
+
+ write_string_table ();
+}
+
+/* Write the local and debugger symbols of file ENTRY.
+ Increment *SYMS_WRITTEN_ADDR for each symbol that is written. */
+
+/* Note that we do not combine identical names of local symbols.
+ dbx or gdb would be confused if we did that. */
+
+void
+write_file_syms (entry, syms_written_addr)
+ struct file_entry *entry;
+ int *syms_written_addr;
+{
+ register struct nlist *p = entry->symbols;
+ register struct nlist *end = p + entry->header.a_syms / sizeof (struct nlist);
+
+ /* Buffer to accumulate all the syms before writing them.
+ It has one extra slot for the local symbol we generate here. */
+ struct nlist *buf
+ = (struct nlist *) alloca (entry->header.a_syms + sizeof (struct nlist));
+ register struct nlist *bufp = buf;
+
+ /* Upper bound on number of syms to be written here. */
+ int max_syms = (entry->header.a_syms / sizeof (struct nlist)) + 1;
+
+ /* Make tables that record, for each symbol, its name and its name's length.
+ The elements are filled in by `assign_string_table_index'. */
+
+ strtab_vector = (char **) alloca (max_syms * sizeof (char *));
+ strtab_lens = (int *) alloca (max_syms * sizeof (int));
+ strtab_index = 0;
+
+ /* Generate a local symbol for the start of this file's text. */
+
+ if (discard_locals != DISCARD_ALL)
+ {
+ struct nlist nl;
+
+ nl.n_type = N_FN | N_EXT;
+ nl.n_un.n_strx = assign_string_table_index (entry->local_sym_name);
+ nl.n_value = entry->text_start_address;
+ nl.n_desc = 0;
+ nl.n_other = 0;
+ *bufp++ = nl;
+ (*syms_written_addr)++;
+ entry->local_syms_offset = *syms_written_addr * sizeof (struct nlist);
+ }
+
+ /* Read the file's string table. */
+
+ entry->strings = (char *) alloca (entry->string_size);
+ read_entry_strings (file_open (entry), entry);
+
+ for (; p < end; p++)
+ {
+ register int type = p->n_type;
+ register int write = 0;
+
+ /* WRITE gets 1 for a non-global symbol that should be written. */
+
+
+ if (SET_ELEMENT_P (type)) /* This occurs even if global. These */
+ /* types of symbols are never written */
+ /* globally, though they are stored */
+ /* globally. */
+ write = relocatable_output;
+ else if (!(type & (N_STAB | N_EXT)))
+ /* ordinary local symbol */
+ write = ((discard_locals != DISCARD_ALL)
+ && !(discard_locals == DISCARD_L &&
+ (p->n_un.n_strx + entry->strings)[0] == LPREFIX)
+ && type != N_WARNING);
+ else if (!(type & N_EXT))
+ /* debugger symbol */
+ write = (strip_symbols == STRIP_NONE);
+
+ if (write)
+ {
+ /* If this symbol has a name,
+ allocate space for it in the output string table. */
+
+ if (p->n_un.n_strx)
+ p->n_un.n_strx = assign_string_table_index (p->n_un.n_strx
+ + entry->strings);
+
+ /* Output this symbol to the buffer and count it. */
+
+ *bufp++ = *p;
+ (*syms_written_addr)++;
+ }
+ }
+
+ /* All the symbols are now in BUF; write them. */
+
+ lseek (outdesc, symbol_table_offset + symbol_table_len, 0);
+ mywrite (buf, sizeof (struct nlist), bufp - buf, outdesc);
+ symbol_table_len += sizeof (struct nlist) * (bufp - buf);
+
+ /* Write the string-table data for the symbols just written,
+ using the data in vectors `strtab_vector' and `strtab_lens'. */
+
+ write_string_table ();
+ entry->strings = 0; /* Since it will dissapear anyway. */
+}
+
+/* Copy any GDB symbol segments from the input files to the output file.
+ The contents of the symbol segment is copied without change
+ except that we store some information into the beginning of it. */
+
+void write_file_symseg ();
+
+void
+write_symsegs ()
+{
+ each_file (write_file_symseg, 0);
+}
+
+void
+write_file_symseg (entry)
+ struct file_entry *entry;
+{
+ char buffer[4096];
+ struct symbol_root root;
+ int indesc;
+ int len;
+
+ if (entry->symseg_offset == 0)
+ return;
+
+ /* This entry has a symbol segment. Read the root of the segment. */
+
+ indesc = file_open (entry);
+ lseek (indesc, entry->symseg_offset + entry->starting_offset, 0);
+ if (sizeof root != read (indesc, &root, sizeof root))
+ fatal_with_file ("premature end of file in symbol segment of ", entry);
+
+ /* Store some relocation info into the root. */
+
+ root.ldsymoff = entry->local_syms_offset;
+ root.textrel = entry->text_start_address;
+ root.datarel = entry->data_start_address - entry->header.a_text;
+ root.bssrel = entry->bss_start_address
+ - entry->header.a_text - entry->header.a_data;
+ root.databeg = entry->data_start_address - root.datarel;
+ root.bssbeg = entry->bss_start_address - root.bssrel;
+
+ /* Write the modified root into the output file. */
+
+ mywrite (&root, sizeof root, 1, outdesc);
+
+ /* Copy the rest of the symbol segment unchanged. */
+
+ if (entry->superfile)
+ {
+ /* Library member: number of bytes to copy is determined
+ from the member's total size. */
+
+ int total = entry->total_size - entry->symseg_offset - sizeof root;
+
+ while (total > 0)
+ {
+ len = read (indesc, buffer, min (sizeof buffer, total));
+
+ if (len != min (sizeof buffer, total))
+ fatal_with_file ("premature end of file in symbol segment of ", entry);
+ total -= len;
+ mywrite (buffer, len, 1, outdesc);
+ }
+ }
+ else
+ {
+ /* A separate file: copy until end of file. */
+
+ while (len = read (indesc, buffer, sizeof buffer))
+ {
+ mywrite (buffer, len, 1, outdesc);
+ if (len < sizeof buffer)
+ break;
+ }
+ }
+
+ file_close ();
+}
+
+/* Create the symbol table entries for `etext', `edata' and `end'. */
+
+void
+symtab_init ()
+{
+#ifndef nounderscore
+ edata_symbol = getsym ("_edata");
+ etext_symbol = getsym ("_etext");
+ end_symbol = getsym ("_end");
+#else
+ edata_symbol = getsym ("edata");
+ etext_symbol = getsym ("etext");
+ end_symbol = getsym ("end");
+#endif
+
+#ifdef sun
+ {
+ symbol *dynamic_symbol = getsym ("__DYNAMIC");
+ dynamic_symbol->defined = N_ABS | N_EXT;
+ dynamic_symbol->referenced = 1;
+ dynamic_symbol->value = 0;
+ }
+#endif
+
+#ifdef sequent
+ {
+ symbol *_387_flt_symbol = getsym ("_387_flt");
+ _387_flt_symbol->defined = N_ABS | N_EXT;
+ _387_flt_symbol->referenced = 1;
+ _387_flt_symbol->value = 0;
+ }
+#endif
+
+ edata_symbol->defined = N_DATA | N_EXT;
+ etext_symbol->defined = N_TEXT | N_EXT;
+ end_symbol->defined = N_BSS | N_EXT;
+
+ edata_symbol->referenced = 1;
+ etext_symbol->referenced = 1;
+ end_symbol->referenced = 1;
+}
+
+/* Compute the hash code for symbol name KEY. */
+
+int
+hash_string (key)
+ char *key;
+{
+ register char *cp;
+ register int k;
+
+ cp = key;
+ k = 0;
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ return k;
+}
+
+/* Get the symbol table entry for the global symbol named KEY.
+ Create one if there is none. */
+
+symbol *
+getsym (key)
+ char *key;
+{
+ register int hashval;
+ register symbol *bp;
+
+ /* Determine the proper bucket. */
+
+ hashval = hash_string (key) % TABSIZE;
+
+ /* Search the bucket. */
+
+ for (bp = symtab[hashval]; bp; bp = bp->link)
+ if (! strcmp (key, bp->name))
+ return bp;
+
+ /* Nothing was found; create a new symbol table entry. */
+
+ bp = (symbol *) xmalloc (sizeof (symbol));
+ bp->refs = 0;
+ bp->name = (char *) xmalloc (strlen (key) + 1);
+ strcpy (bp->name, key);
+ bp->defined = 0;
+ bp->referenced = 0;
+ bp->trace = 0;
+ bp->value = 0;
+ bp->max_common_size = 0;
+ bp->warning = 0;
+ bp->undef_refs = 0;
+ bp->multiply_defined = 0;
+
+ /* Add the entry to the bucket. */
+
+ bp->link = symtab[hashval];
+ symtab[hashval] = bp;
+
+ ++num_hash_tab_syms;
+
+ return bp;
+}
+
+/* Like `getsym' but return 0 if the symbol is not already known. */
+
+symbol *
+getsym_soft (key)
+ char *key;
+{
+ register int hashval;
+ register symbol *bp;
+
+ /* Determine which bucket. */
+
+ hashval = hash_string (key) % TABSIZE;
+
+ /* Search the bucket. */
+
+ for (bp = symtab[hashval]; bp; bp = bp->link)
+ if (! strcmp (key, bp->name))
+ return bp;
+
+ return 0;
+}
+
+/* Report a fatal error.
+ STRING is a printf format string and ARG is one arg for it. */
+
+void
+fatal (string, arg)
+ char *string, *arg;
+{
+ fprintf (stderr, "ld: ");
+ fprintf (stderr, string, arg);
+ fprintf (stderr, "\n");
+ exit (1);
+}
+
+/* Report a fatal error. The error message is STRING
+ followed by the filename of ENTRY. */
+
+void
+fatal_with_file (string, entry)
+ char *string;
+ struct file_entry *entry;
+{
+ fprintf (stderr, "ld: ");
+ fprintf (stderr, string);
+ print_file_name (entry, stderr);
+ fprintf (stderr, "\n");
+ exit (1);
+}
+
+/* Report a fatal error using the message for the last failed system call,
+ followed by the string NAME. */
+
+void
+perror_name (name)
+ char *name;
+{
+ extern int errno;
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("", sys_errlist[errno], " for %s");
+ else
+ s = "cannot open %s";
+ fatal (s, name);
+}
+
+/* Report a fatal error using the message for the last failed system call,
+ followed by the name of file ENTRY. */
+
+void
+perror_file (entry)
+ struct file_entry *entry;
+{
+ extern int errno;
+ char *s;
+
+ if (errno < sys_nerr)
+ s = concat ("", sys_errlist[errno], " for ");
+ else
+ s = "cannot open ";
+ fatal_with_file (s, entry);
+}
+
+/* Report a nonfatal error.
+ STRING is a format for printf, and ARG1 ... ARG3 are args for it. */
+
+void
+error (string, arg1, arg2, arg3)
+ char *string, *arg1, *arg2, *arg3;
+{
+ fprintf (stderr, "%s: ", progname);
+ fprintf (stderr, string, arg1, arg2, arg3);
+ fprintf (stderr, "\n");
+}
+
+
+/* Output COUNT*ELTSIZE bytes of data at BUF
+ to the descriptor DESC. */
+
+void
+mywrite (buf, count, eltsize, desc)
+ char *buf;
+ int count;
+ int eltsize;
+ int desc;
+{
+ register int val;
+ register int bytes = count * eltsize;
+
+ while (bytes > 0)
+ {
+ val = write (desc, buf, bytes);
+ if (val <= 0)
+ perror_name (output_filename);
+ buf += val;
+ bytes -= val;
+ }
+}
+
+/* Output PADDING zero-bytes to descriptor OUTDESC.
+ PADDING may be negative; in that case, do nothing. */
+
+void
+padfile (padding, outdesc)
+ int padding;
+ int outdesc;
+{
+ register char *buf;
+ if (padding <= 0)
+ return;
+
+ buf = (char *) alloca (padding);
+ bzero (buf, padding);
+ mywrite (buf, padding, 1, outdesc);
+}
+
+/* Return a newly-allocated string
+ whose contents concatenate the strings S1, S2, S3. */
+
+char *
+concat (s1, s2, s3)
+ char *s1, *s2, *s3;
+{
+ register int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+ register char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+ strcpy (result, s1);
+ strcpy (result + len1, s2);
+ strcpy (result + len1 + len2, s3);
+ result[len1 + len2 + len3] = 0;
+
+ return result;
+}
+
+/* Parse the string ARG using scanf format FORMAT, and return the result.
+ If it does not parse, report fatal error
+ generating the error message using format string ERROR and ARG as arg. */
+
+int
+parse (arg, format, error)
+ char *arg, *format;
+{
+ int x;
+ if (1 != sscanf (arg, format, &x))
+ fatal (error, arg);
+ return x;
+}
+
+/* Like malloc but get fatal error if memory is exhausted. */
+
+int
+xmalloc (size)
+ int size;
+{
+ register int result = malloc (size);
+ if (!result)
+ fatal ("virtual memory exhausted", 0);
+ return result;
+}
+
+/* Like realloc but get fatal error if memory is exhausted. */
+
+int
+xrealloc (ptr, size)
+ char *ptr;
+ int size;
+{
+ register int result = realloc (ptr, size);
+ if (!result)
+ fatal ("virtual memory exhausted", 0);
+ return result;
+}
+
+#ifdef USG
+
+void
+bzero (p, n)
+ char *p;
+{
+ memset (p, 0, n);
+}
+
+void
+bcopy (from, to, n)
+ char *from, *to;
+{
+ memcpy (to, from, n);
+}
+
+getpagesize ()
+{
+ return (4096);
+}
+
+#endif
+
+#if defined(sun) && (TARGET == SUN4)
+
+/* Don't use local pagesize to build for Sparc. */
+
+getpagesize ()
+{
+ return (8192);
+}
+#endif
diff --git a/usr.bin/ld/symseg.h b/usr.bin/ld/symseg.h
new file mode 100644
index 0000000..978d8be
--- /dev/null
+++ b/usr.bin/ld/symseg.h
@@ -0,0 +1,358 @@
+/*-
+ *
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * @(#)symseg.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* GDB symbol table format definitions.
+ Copyright (C) 1987, 1988 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Format of GDB symbol table data.
+ There is one symbol segment for each source file or
+ independant compilation. These segments are simply concatenated
+ to form the GDB symbol table. A zero word where the beginning
+ of a segment is expected indicates there are no more segments.
+
+Format of a symbol segment:
+
+ The symbol segment begins with a word containing 1
+ if it is in the format described here. Other formats may
+ be designed, with other code numbers.
+
+ The segment contains many objects which point at each other.
+ The pointers are offsets in bytes from the beginning of the segment.
+ Thus, each segment can be loaded into core and its pointers relocated
+ to make valid in-core pointers.
+
+ All the data objects in the segment can be found indirectly from
+ one of them, the root object, of type `struct symbol_root'.
+ It appears at the beginning of the segment.
+
+ The total size of the segment, in bytes, appears as the `length'
+ field of this object. This size includes the size of the
+ root object.
+
+ All the object data types are defined here to contain pointer types
+ appropriate for in-core use on a relocated symbol segment.
+ Casts to and from type int are required for working with
+ unrelocated symbol segments such as are found in the file.
+
+ The ldsymaddr word is filled in by the loader to contain
+ the offset (in bytes) within the ld symbol table
+ of the first nonglobal symbol from this compilation.
+ This makes it possible to match those symbols
+ (which contain line number information) reliably with
+ the segment they go with.
+
+ Core addresses within the program that appear in the symbol segment
+ are not relocated by the loader. They are inserted by the assembler
+ and apply to addresses as output by the assembler, so GDB must
+ relocate them when it loads the symbol segment. It gets the information
+ on how to relocate from the textrel, datarel, bssrel, databeg and bssbeg
+ words of the root object.
+
+ The words textrel, datarel and bssrel
+ are filled in by ld with the amounts to relocate within-the-file
+ text, data and bss addresses by; databeg and bssbeg can be
+ used to tell which kind of relocation an address needs. */
+
+enum language {language_c};
+
+struct symbol_root
+{
+ int format; /* Data format version */
+ int length; /* # bytes in this symbol segment */
+ int ldsymoff; /* Offset in ld symtab of this file's syms */
+ int textrel; /* Relocation for text addresses */
+ int datarel; /* Relocation for data addresses */
+ int bssrel; /* Relocation for bss addresses */
+ char *filename; /* Name of main source file compiled */
+ char *filedir; /* Name of directory it was reached from */
+ struct blockvector *blockvector; /* Vector of all symbol-naming blocks */
+ struct typevector *typevector; /* Vector of all data types */
+ enum language language; /* Code identifying the language used */
+ char *version; /* Version info. Not fully specified */
+ char *compilation; /* Compilation info. Not fully specified */
+ int databeg; /* Address within the file of data start */
+ int bssbeg; /* Address within the file of bss start */
+ struct sourcevector *sourcevector; /* Vector of line-number info */
+};
+
+/* All data types of symbols in the compiled program
+ are represented by `struct type' objects.
+ All of these objects are pointed to by the typevector.
+ The type vector may have empty slots that contain zero. */
+
+struct typevector
+{
+ int length; /* Number of types described */
+ struct type *type[1];
+};
+
+/* Different kinds of data types are distinguished by the `code' field. */
+
+enum type_code
+{
+ TYPE_CODE_UNDEF, /* Not used; catches errors */
+ TYPE_CODE_PTR, /* Pointer type */
+ TYPE_CODE_ARRAY, /* Array type, lower bound zero */
+ TYPE_CODE_STRUCT, /* C struct or Pascal record */
+ TYPE_CODE_UNION, /* C union or Pascal variant part */
+ TYPE_CODE_ENUM, /* Enumeration type */
+ TYPE_CODE_FUNC, /* Function type */
+ TYPE_CODE_INT, /* Integer type */
+ TYPE_CODE_FLT, /* Floating type */
+ TYPE_CODE_VOID, /* Void type (values zero length) */
+ TYPE_CODE_SET, /* Pascal sets */
+ TYPE_CODE_RANGE, /* Range (integers within spec'd bounds) */
+ TYPE_CODE_PASCAL_ARRAY, /* Array with explicit type of index */
+};
+
+/* This appears in a type's flags word for an unsigned integer type. */
+#define TYPE_FLAG_UNSIGNED 1
+
+/* Other flag bits are used with GDB. */
+
+struct type
+{
+ /* Code for kind of type */
+ enum type_code code;
+ /* Name of this type, or zero if none.
+ This is used for printing only.
+ Type names specified as input are defined by symbols. */
+ char *name;
+ /* Length in bytes of storage for a value of this type */
+ int length;
+ /* For a pointer type, describes the type of object pointed to.
+ For an array type, describes the type of the elements.
+ For a function type, describes the type of the value.
+ Unused otherwise. */
+ struct type *target_type;
+ /* Type that is a pointer to this type.
+ Zero if no such pointer-to type is known yet.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *pointer_type;
+ /* Type that is a function returning this type.
+ Zero if no such function type is known here.
+ The debugger may add the address of such a type
+ if it has to construct one later. */
+ struct type *function_type;
+ /* Flags about this type. */
+ short flags;
+ /* Number of fields described for this type */
+ short nfields;
+ /* For structure and union types, a description of each field.
+ For set and pascal array types, there is one "field",
+ whose type is the domain type of the set or array.
+ For range types, there are two "fields",
+ the minimum and maximum values (both inclusive).
+ For enum types, each possible value is described by one "field".
+ For range types, there are two "fields", that record constant values
+ (inclusive) for the minimum and maximum.
+
+ Using a pointer to a separate array of fields
+ allows all types to have the same size, which is useful
+ because we can allocate the space for a type before
+ we know what to put in it. */
+ struct field
+ {
+ /* Position of this field, counting in bits from start of
+ containing structure. For a function type, this is the
+ position in the argument list of this argument.
+ For a range bound or enum value, this is the value itself. */
+ int bitpos;
+ /* Size of this field, in bits, or zero if not packed.
+ For an unpacked field, the field's type's length
+ says how many bytes the field occupies. */
+ int bitsize;
+ /* In a struct or enum type, type of this field.
+ In a function type, type of this argument.
+ In an array type, the domain-type of the array. */
+ struct type *type;
+ /* Name of field, value or argument.
+ Zero for range bounds and array domains. */
+ char *name;
+ } *fields;
+};
+
+/* All of the name-scope contours of the program
+ are represented by `struct block' objects.
+ All of these objects are pointed to by the blockvector.
+
+ Each block represents one name scope.
+ Each lexical context has its own block.
+
+ The first two blocks in the blockvector are special.
+ The first one contains all the symbols defined in this compilation
+ whose scope is the entire program linked together.
+ The second one contains all the symbols whose scope is the
+ entire compilation excluding other separate compilations.
+ In C, these correspond to global symbols and static symbols.
+
+ Each block records a range of core addresses for the code that
+ is in the scope of the block. The first two special blocks
+ give, for the range of code, the entire range of code produced
+ by the compilation that the symbol segment belongs to.
+
+ The blocks appear in the blockvector
+ in order of increasing starting-address,
+ and, within that, in order of decreasing ending-address.
+
+ This implies that within the body of one function
+ the blocks appear in the order of a depth-first tree walk. */
+
+struct blockvector
+{
+ /* Number of blocks in the list. */
+ int nblocks;
+ /* The blocks themselves. */
+ struct block *block[1];
+};
+
+struct block
+{
+ /* Addresses in the executable code that are in this block.
+ Note: in an unrelocated symbol segment in a file,
+ these are always zero. They can be filled in from the
+ N_LBRAC and N_RBRAC symbols in the loader symbol table. */
+ int startaddr, endaddr;
+ /* The symbol that names this block,
+ if the block is the body of a function;
+ otherwise, zero.
+ Note: In an unrelocated symbol segment in an object file,
+ this field may be zero even when the block has a name.
+ That is because the block is output before the name
+ (since the name resides in a higher block).
+ Since the symbol does point to the block (as its value),
+ it is possible to find the block and set its name properly. */
+ struct symbol *function;
+ /* The `struct block' for the containing block, or 0 if none. */
+ /* Note that in an unrelocated symbol segment in an object file
+ this pointer may be zero when the correct value should be
+ the second special block (for symbols whose scope is one compilation).
+ This is because the compiler ouptuts the special blocks at the
+ very end, after the other blocks. */
+ struct block *superblock;
+ /* Number of local symbols. */
+ int nsyms;
+ /* The symbols. */
+ struct symbol *sym[1];
+};
+
+/* Represent one symbol name; a variable, constant, function or typedef. */
+
+/* Different name spaces for symbols. Looking up a symbol specifies
+ a namespace and ignores symbol definitions in other name spaces.
+
+ VAR_NAMESPACE is the usual namespace.
+ In C, this contains variables, function names, typedef names
+ and enum type values.
+
+ STRUCT_NAMESPACE is used in C to hold struct, union and enum type names.
+ Thus, if `struct foo' is used in a C program,
+ it produces a symbol named `foo' in the STRUCT_NAMESPACE.
+
+ LABEL_NAMESPACE may be used for names of labels (for gotos);
+ currently it is not used and labels are not recorded at all. */
+
+/* For a non-global symbol allocated statically,
+ the correct core address cannot be determined by the compiler.
+ The compiler puts an index number into the symbol's value field.
+ This index number can be matched with the "desc" field of
+ an entry in the loader symbol table. */
+
+enum namespace
+{
+ UNDEF_NAMESPACE, VAR_NAMESPACE, STRUCT_NAMESPACE, LABEL_NAMESPACE,
+};
+
+/* An address-class says where to find the value of the symbol in core. */
+
+enum address_class
+{
+ LOC_UNDEF, /* Not used; catches errors */
+ LOC_CONST, /* Value is constant int */
+ LOC_STATIC, /* Value is at fixed address */
+ LOC_REGISTER, /* Value is in register */
+ LOC_ARG, /* Value is at spec'd position in arglist */
+ LOC_LOCAL, /* Value is at spec'd pos in stack frame */
+ LOC_TYPEDEF, /* Value not used; definition in SYMBOL_TYPE
+ Symbols in the namespace STRUCT_NAMESPACE
+ all have this class. */
+ LOC_LABEL, /* Value is address in the code */
+ LOC_BLOCK, /* Value is address of a `struct block'.
+ Function names have this class. */
+ LOC_EXTERNAL, /* Value is at address not in this compilation.
+ This is used for .comm symbols
+ and for extern symbols within functions.
+ Inside GDB, this is changed to LOC_STATIC once the
+ real address is obtained from a loader symbol. */
+ LOC_CONST_BYTES /* Value is a constant byte-sequence. */
+};
+
+struct symbol
+{
+ /* Symbol name */
+ char *name;
+ /* Name space code. */
+ enum namespace namespace;
+ /* Address class */
+ enum address_class class;
+ /* Data type of value */
+ struct type *type;
+ /* constant value, or address if static, or register number,
+ or offset in arguments, or offset in stack frame. */
+ union
+ {
+ long value;
+ struct block *block; /* for LOC_BLOCK */
+ char *bytes; /* for LOC_CONST_BYTES */
+ }
+ value;
+};
+
+/* Source-file information.
+ This describes the relation between source files and line numbers
+ and addresses in the program text. */
+
+struct sourcevector
+{
+ int length; /* Number of source files described */
+ struct source *source[1]; /* Descriptions of the files */
+};
+
+/* Line number and address of one line. */
+
+struct line
+{
+ int linenum;
+ int address;
+};
+
+/* All the information on one source file. */
+
+struct source
+{
+ char *name; /* Name of file */
+ int nlines; /* Number of lines that follow */
+ struct line lines[1]; /* Information on each line */
+};
diff --git a/usr.bin/ldd/Makefile b/usr.bin/ldd/Makefile
new file mode 100644
index 0000000..282a8fd
--- /dev/null
+++ b/usr.bin/ldd/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.2 1993/11/09 04:19:24 paul Exp $
+
+PROG= ldd
+SRCS= ldd.c
+BINDIR= /usr/bin
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ldd/ldd.1 b/usr.bin/ldd/ldd.1
new file mode 100644
index 0000000..0c3b5e4
--- /dev/null
+++ b/usr.bin/ldd/ldd.1
@@ -0,0 +1,25 @@
+.Dd October 22, 1993
+.Dt LDD 1
+.Os FreeBSD
+.Sh NAME
+.Nm ldd
+.Nd list dynamic object dependencies
+.Sh SYNOPSIS
+.Nm ldd
+.Op Ar filename Ar ...
+.Sh DESCRIPTION
+.Nm ldd
+displays all shared objects that are needed to run the given program.
+Contrary to nm(1), the list includes
+.Dq indirect
+depedencies that are the result of needed shared objects which themselves
+depend on yet other shared objects.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr ld.so 1 ,
+.Xr nm 1
+.Sh HISTORY
+A
+.Nm ldd
+utility first appeared in SunOS 4.0, it appeared in its current form
+in FreeBSD 1.1.
diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c
new file mode 100644
index 0000000..2f54806
--- /dev/null
+++ b/usr.bin/ldd/ldd.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ldd.c,v 1.4 1994/06/15 22:41:03 rich Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <a.out.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void
+usage()
+{
+ extern char *__progname;
+
+ fprintf(stderr, "Usage: %s <filename> ...\n", __progname);
+ exit(1);
+}
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int rval;
+ int c;
+
+ while ((c = getopt(argc, argv, "")) != EOF) {
+ switch (c) {
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc <= 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ /* ld.so magic */
+ setenv("LD_TRACE_LOADED_OBJECTS", "", 1);
+
+ rval = 0;
+ while (argc--) {
+ int fd;
+ struct exec hdr;
+ int status;
+
+ if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
+ warn("%s", *argv);
+ rval |= 1;
+ argv++;
+ continue;
+ }
+ if (read(fd, &hdr, sizeof hdr) != sizeof hdr
+ || (N_GETFLAG(hdr) & EX_DPMASK) != EX_DYNAMIC
+#if 1 /* Compatibility */
+ || hdr.a_entry < __LDPGSZ
+#endif
+ ) {
+
+ warnx("%s: not a dynamic executable", *argv);
+ (void)close(fd);
+ rval |= 1;
+ argv++;
+ continue;
+ }
+ (void)close(fd);
+
+ printf("%s:\n", *argv);
+ fflush(stdout);
+
+ switch (fork()) {
+ case -1:
+ err(1, "fork");
+ break;
+ default:
+ if (wait(&status) <= 0) {
+ warn("wait");
+ rval |= 1;
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: signal %d\n",
+ *argv, WTERMSIG(status));
+ rval |= 1;
+ } else if (WIFEXITED(status) && WEXITSTATUS(status)) {
+ fprintf(stderr, "%s: exit status %d\n",
+ *argv, WEXITSTATUS(status));
+ rval |= 1;
+ }
+ break;
+ case 0:
+ rval |= execl(*argv, *argv, NULL) != 0;
+ perror(*argv);
+ _exit(1);
+ }
+ argv++;
+ }
+
+ return rval;
+}
diff --git a/usr.bin/leave/Makefile b/usr.bin/leave/Makefile
new file mode 100644
index 0000000..87a6ac3
--- /dev/null
+++ b/usr.bin/leave/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= leave
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/leave/leave.1 b/usr.bin/leave/leave.1
new file mode 100644
index 0000000..68c8b4b
--- /dev/null
+++ b/usr.bin/leave/leave.1
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)leave.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt LEAVE 1
+.Os
+.Sh NAME
+.Nm leave
+.Nd remind you when you have to leave
+.Sh SYNOPSIS
+.Nm leave
+.Sm off
+.Oo
+.Op Cm \&+
+.Ns Ar hhmm
+.Oc
+.Sm on
+.Sh DESCRIPTION
+.Nm Leave
+waits until the specified time, then reminds you that you
+have to leave.
+You are reminded 5 minutes and 1 minute before the actual
+time, at the time, and every minute thereafter.
+When you log off,
+.Nm leave
+exits just before it would have
+printed the next message.
+.Pp
+Options:
+.Pp
+.Bl -tag -width flag
+.It Ar hhmm
+The time of day is in the form
+.Ar hhmm
+where
+.Ar hh
+is a time in
+hours (on a 12 or 24 hour clock), and
+.Ar mm
+are minutes.
+All times are converted to a 12 hour clock, and assumed to
+be in the next 12 hours.
+.It Cm \&+
+If the time is preceded by
+.Ql Cm \&+ ,
+the alarm will go off in hours and minutes
+from the current time.
+.El
+.Pp
+If no argument is given,
+.Nm leave
+prompts with "When do you
+have to leave?". A reply of newline causes
+.Nm leave
+to exit,
+otherwise the reply is assumed to be a time.
+This form is suitable for inclusion in a
+.Pa .login
+or
+.Pa .profile.
+.Pp
+Leave ignores interrupts, quits, and terminates.
+To get rid of it you should either log off or use
+.Ql kill \-9
+giving its process id.
+.Sh SEE ALSO
+.Xr calendar 1
+.Sh HISTORY
+The
+.Nm leave
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/leave/leave.c b/usr.bin/leave/leave.c
new file mode 100644
index 0000000..56d9d19
--- /dev/null
+++ b/usr.bin/leave/leave.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)leave.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * leave [[+]hhmm]
+ *
+ * Reminds you when you have to leave.
+ * Leave prompts for input and goes away if you hit return.
+ * It nags you like a mother hen.
+ */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register u_int secs;
+ register int hours, minutes;
+ register char c, *cp;
+ struct tm *t, *localtime();
+ time_t now, time();
+ int plusnow;
+ char buf[50];
+
+ if (argc < 2) {
+#define MSG1 "When do you have to leave? "
+ (void)write(1, MSG1, sizeof(MSG1) - 1);
+ cp = fgets(buf, sizeof(buf), stdin);
+ if (*cp == '\n')
+ exit(0);
+ } else
+ cp = argv[1];
+
+ if (*cp == '+') {
+ plusnow = 1;
+ ++cp;
+ } else {
+ plusnow = 0;
+ (void)time(&now);
+ t = localtime(&now);
+ }
+
+ for (hours = 0; (c = *cp) && c != '\n'; ++cp) {
+ if (!isdigit(c))
+ usage();
+ hours = hours * 10 + (c - '0');
+ }
+ minutes = hours % 100;
+ hours /= 100;
+
+ if (minutes < 0 || minutes > 59)
+ usage();
+ if (plusnow)
+ secs = hours * 60 * 60 + minutes * 60;
+ else {
+ if (hours > 23 || t->tm_hour > hours ||
+ t->tm_hour == hours && minutes <= t->tm_min)
+ usage();
+ secs = (hours - t->tm_hour) * 60 * 60;
+ secs += (minutes - t->tm_min) * 60;
+ }
+ doalarm(secs);
+ exit(0);
+}
+
+doalarm(secs)
+ u_int secs;
+{
+ register int bother;
+ time_t daytime, time();
+ int pid;
+ char *ctime();
+
+ if (pid = fork()) {
+ (void)time(&daytime);
+ daytime += secs;
+ printf("Alarm set for %.16s. (pid %d)\n",
+ ctime(&daytime), pid);
+ exit(0);
+ }
+ sleep((u_int)2); /* let parent print set message */
+
+ /*
+ * if write fails, we've lost the terminal through someone else
+ * causing a vhangup by logging in.
+ */
+#define FIVEMIN (5 * 60)
+#define MSG2 "\07\07You have to leave in 5 minutes.\n"
+ if (secs >= FIVEMIN) {
+ sleep(secs - FIVEMIN);
+ if (write(1, MSG2, sizeof(MSG2) - 1) != sizeof(MSG2) - 1)
+ exit(0);
+ secs = FIVEMIN;
+ }
+
+#define ONEMIN (60)
+#define MSG3 "\07\07Just one more minute!\n"
+ if (secs >= ONEMIN) {
+ sleep(secs - ONEMIN);
+ if (write(1, MSG3, sizeof(MSG3) - 1) != sizeof(MSG3) - 1)
+ exit(0);
+ }
+
+#define MSG4 "\07\07Time to leave!\n"
+ for (bother = 10; bother--;) {
+ sleep((u_int)ONEMIN);
+ if (write(1, MSG4, sizeof(MSG4) - 1) != sizeof(MSG4) - 1)
+ exit(0);
+ }
+
+#define MSG5 "\07\07That was the last time I'll tell you. Bye.\n"
+ (void)write(1, MSG5, sizeof(MSG5) - 1);
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr, "usage: leave [[+]hhmm]\n");
+ exit(1);
+}
diff --git a/usr.bin/lex/COPYING b/usr.bin/lex/COPYING
new file mode 100644
index 0000000..dcb775e
--- /dev/null
+++ b/usr.bin/lex/COPYING
@@ -0,0 +1,38 @@
+Flex carries the copyright used for BSD software, slightly modified
+because it originated at the Lawrence Berkeley (not Livermore!) Laboratory,
+which operates under a contract with the Department of Energy:
+
+ Copyright (c) 1990 The Regents of the University of California.
+ All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Vern Paxson.
+
+ The United States Government has rights in this work pursuant
+ to contract no. DE-AC03-76SF00098 between the United States
+ Department of Energy and the University of California.
+
+ Redistribution and use in source and binary forms are permitted
+ provided that: (1) source distributions retain this entire
+ copyright notice and comment, and (2) distributions including
+ binaries display the following acknowledgement: ``This product
+ includes software developed by the University of California,
+ Berkeley and its contributors'' in the documentation or other
+ materials provided with the distribution and in all advertising
+ materials mentioning features or use of this software. Neither the
+ name of the University nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+This basically says "do whatever you please with this software except
+remove this notice or take advantage of the University's (or the flex
+authors') name".
+
+Note that the "flex.skl" scanner skeleton carries no copyright notice.
+You are free to do whatever you please with scanners generated using flex;
+for them, you are not even bound by the above copyright.
diff --git a/usr.bin/lex/FlexLexer.h b/usr.bin/lex/FlexLexer.h
new file mode 100644
index 0000000..feb40b6
--- /dev/null
+++ b/usr.bin/lex/FlexLexer.h
@@ -0,0 +1,175 @@
+// $Header: FlexLexer.h,v 1.2 94/01/04 14:57:26 vern Exp $
+
+// FlexLexer.h -- define classes for lexical analyzers generated by flex
+
+// Copyright (c) 1993 The Regents of the University of California.
+// All rights reserved.
+//
+// This code is derived from software contributed to Berkeley by
+// Kent Williams and Tom Epperly.
+//
+// Redistribution and use in source and binary forms are permitted provided
+// that: (1) source distributions retain this entire copyright notice and
+// comment, and (2) distributions including binaries display the following
+// acknowledgement: ``This product includes software developed by the
+// University of California, Berkeley and its contributors'' in the
+// documentation or other materials provided with the distribution and in
+// all advertising materials mentioning features or use of this software.
+// Neither the name of the University nor the names of its contributors may
+// be used to endorse or promote products derived from this software without
+// specific prior written permission.
+// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+#ifndef __FLEX_LEXER_H
+#define __FLEX_LEXER_H
+
+
+// This file defines two classes. The first, FlexLexer, is an abstract
+// class which specifies the external interface provided to flex C++
+// lexer objects. The second, yyFlexLexer, fills out most of the meat
+// of the lexer class; its internals may vary from lexer to lexer
+// depending on things like whether REJECT is used.
+//
+// If you want to create multiple lexer classes, you use the -P flag
+// to rename each yyFlexLexer to some other xxFlexLexer.
+
+#include <iostream.h>
+
+extern "C++" {
+
+struct yy_buffer_state;
+typedef int yy_state_type;
+
+class FlexLexer {
+public:
+ virtual ~FlexLexer() { }
+
+ const char* YYText() { return yytext; }
+ int YYLeng() { return yyleng; }
+
+ virtual void
+ yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0;
+ virtual struct yy_buffer_state*
+ yy_create_buffer( istream* s, int size ) = 0;
+ virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0;
+ virtual void yyrestart( istream* s ) = 0;
+
+ virtual int yylex() = 0;
+
+protected:
+ char* yytext;
+ int yyleng;
+};
+
+
+class yyFlexLexer : public FlexLexer {
+public:
+ // arg_yyin and arg_yyout default to the cin and cout, but we
+ // only make that assignment when initializing in yylex().
+ yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
+ {
+ yyin = arg_yyin;
+ yyout = arg_yyout;
+ yy_c_buf_p = 0;
+ yy_init = 1;
+ yy_start = 0;
+
+ yy_did_buffer_switch_on_eof = 0;
+
+ yy_looking_for_trail_begin = 0;
+ yy_more_flag = 0;
+ yy_more_len = 0;
+
+ yy_start_stack_ptr = yy_start_stack_depth = 0;
+ yy_start_stack = 0;
+
+ yy_current_buffer = 0;
+
+#ifdef YY_USES_REJECT
+ yy_state_buf = new yy_state_type[YY_BUF_SIZE + 2];
+#else
+ yy_state_buf = 0;
+#endif
+ }
+
+ virtual ~yyFlexLexer()
+ {
+ delete yy_state_buf;
+ }
+
+ void yy_switch_to_buffer( struct yy_buffer_state* new_buffer );
+ struct yy_buffer_state* yy_create_buffer( istream* s, int size );
+ void yy_delete_buffer( struct yy_buffer_state* b );
+ void yyrestart( istream* s );
+
+ virtual int yylex();
+
+protected:
+ virtual int LexerInput( char* buf, int max_size );
+ virtual void LexerOutput( const char* buf, int size );
+ virtual void LexerError( const char* msg );
+
+ void yyunput( int c, char* buf_ptr );
+ int yyinput();
+
+ void yy_load_buffer_state();
+ void yy_init_buffer( struct yy_buffer_state* b, istream* s );
+
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int* yy_start_stack;
+
+ void yy_push_state( int new_state );
+ void yy_pop_state();
+ int yy_top_state();
+
+ yy_state_type yy_get_previous_state();
+ yy_state_type yy_try_NUL_trans( yy_state_type current_state );
+ int yy_get_next_buffer();
+
+ istream* yyin; // input source for default LexerInput
+ ostream* yyout; // output sink for default LexerOutput
+
+ struct yy_buffer_state* yy_current_buffer;
+
+ // yy_hold_char holds the character lost when yytext is formed.
+ char yy_hold_char;
+
+ // Number of characters read into yy_ch_buf.
+ int yy_n_chars;
+
+ // Points to current character in buffer.
+ char* yy_c_buf_p;
+
+ int yy_init; // whether we need to initialize
+ int yy_start; // start state number
+
+ // Flag which is used to allow yywrap()'s to do buffer switches
+ // instead of setting up a fresh yyin. A bit of a hack ...
+ int yy_did_buffer_switch_on_eof;
+
+ // The following are not always needed, but may be depending
+ // on use of certain flex features (like REJECT or yymore()).
+
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ yy_state_type* yy_state_buf;
+ yy_state_type* yy_state_ptr;
+
+ char* yy_full_match;
+ int* yy_full_state;
+ int yy_full_lp;
+
+ int yy_lp;
+ int yy_looking_for_trail_begin;
+
+ int yy_more_flag;
+ int yy_more_len;
+};
+
+}
+
+#endif
diff --git a/usr.bin/lex/Makefile b/usr.bin/lex/Makefile
new file mode 100644
index 0000000..41d4afd
--- /dev/null
+++ b/usr.bin/lex/Makefile
@@ -0,0 +1,55 @@
+# $Id: Makefile,v 1.1.1.1 1994/08/24 13:10:33 csgr Exp $
+#
+# By default, flex will be configured to generate 8-bit scanners only if the
+# -8 flag is given. If you want it to always generate 8-bit scanners, add
+# "-DDEFAULT_CSIZE=256" to CFLAGS. Note that doing so will double the size
+# of all uncompressed scanners.
+#
+# Bootstrapping of lex is handled automatically.
+# Also note that flex.skel no longer gets installed.
+#
+# XXX Todo:
+# Install as lex++, and install FlexLexer.h
+
+PROG= lex
+#LINKS+= ${BINDIR}/lex ${BINDIR}/lex++ ${BINDIR}/flex ${BINDIR}/flex++
+
+SRCS= ccl.c dfa.c ecs.c gen.c main.c misc.c nfa.c parse.y \
+ skel.c sym.c tblcmp.c yylex.c
+OBJS+= scan.o
+LFLAGS+= -is
+CFLAGS+= -I. -I${.CURDIR}
+MAN1= lex.1 lexdoc.1
+
+CLEANFILES+= parse.c parse.h scan.c y.tab.h y.tab.c
+
+
+SUBDIR= lib
+
+.depend: parse.h
+
+parse.c parse.h: parse.y
+ $(YACC) -d $(.CURDIR)/parse.y
+ mv y.tab.c parse.c
+ mv y.tab.h parse.h
+
+.if exists(/usr/bin/lex)
+scan.o: parse.c
+.else
+# We must bootstrap
+scan.o: scan.c parse.h
+
+scan.c:
+ @echo "Bootstrapping flex"
+ @rm -f scan.c
+ @cp -pf ${.CURDIR}/initscan.c scan.c
+.endif
+
+test: check
+check: $(PROG)
+ ./$(PROG) $(LFLAGS) -t $(COMPRESSION) $(.CURDIR)/scan.l \
+ | sed s,\"$(.CURDIR)/scan.l",\"scan.l", \
+ | diff $(.CURDIR)/initscan.c -
+ @echo "Check successful"
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lex/NEWS b/usr.bin/lex/NEWS
new file mode 100644
index 0000000..aff90a3
--- /dev/null
+++ b/usr.bin/lex/NEWS
@@ -0,0 +1,703 @@
+Changes between release 2.4.7 (03Aug94) and release 2.4.6:
+
+ - Fixed serious bug in reading multiple files.
+
+ - Fixed bug in scanning NUL's.
+
+ - Fixed bug in input() returning 8-bit characters.
+
+ - Fixed bug in matching text with embedded NUL's when
+ using %array or lex compatibility.
+
+ - Fixed multiple invocations of YY_USER_ACTION when using '|'
+ continuation action.
+
+ - Minor prototyping fixes.
+
+Changes between release 2.4.6 (04Jan94) and release 2.4.5:
+
+ - Linking with -lfl no longer required if your program includes
+ its own yywrap() and main() functions. (This change will cause
+ problems if you have a non-ANSI compiler on a system for which
+ sizeof(int) != sizeof(void*) or sizeof(int) != sizeof(size_t).)
+
+ - The use of 'extern "C++"' in FlexLexer.h has been modified to
+ get around an incompatibility with g++'s header files.
+
+
+Changes between release 2.4.5 (11Dec93) and release 2.4.4:
+
+ - Fixed bug breaking C++ scanners that use REJECT or variable
+ trailing context.
+
+ - Fixed serious input problem for interactive scanners on
+ systems for which char is unsigned.
+
+ - Fixed bug in incorrectly treating '$' operator as variable
+ trailing context.
+
+ - Fixed bug in -CF table representation that could lead to
+ corrupt tables.
+
+ - Fixed fairly benign memory leak.
+
+ - Added `extern "C++"' wrapper to FlexLexer.h header. This
+ should overcome the g++ 2.5.X problems mentioned in the
+ NEWS for release 2.4.3.
+
+ - Changed #include of FlexLexer.h to use <> instead of "".
+
+ - Added feature to control whether the scanner attempts to
+ refill the input buffer once it's exhausted. This feature
+ will be documented in the 2.5 release.
+
+
+Changes between release 2.4.4 (07Dec93) and release 2.4.3:
+
+ - Fixed two serious bugs in scanning 8-bit characters.
+
+ - Fixed bug in YY_USER_ACTION that caused it to be executed
+ inappropriately (on the scanner's own internal actions, and
+ with incorrect yytext/yyleng values).
+
+ - Fixed bug in pointing yyin at a new file and resuming scanning.
+
+ - Portability fix regarding min/max/abs macros conflicting with
+ function definitions in standard header files.
+
+ - Added a virtual LexerError() method to the C++ yyFlexLexer class
+ for reporting error messages instead of always using cerr.
+
+ - Added warning in flexdoc that the C++ scanning class is presently
+ experimental and subject to considerable change between major
+ releases.
+
+
+Changes between release 2.4.3 (03Dec93) and release 2.4.2:
+
+ - Fixed bug causing fatal scanner messages to fail to print.
+
+ - Fixed things so FlexLexer.h can be included in other C++
+ sources. One side-effect of this change is that -+ and -CF
+ are now incompatible.
+
+ - libfl.a now supplies private versions of the the <string.h>/
+ <strings.h> string routines needed by flex and the scanners
+ it generates, to enhance portability to some BSD systems.
+
+ - More robust solution to 2.4.2's flexfatal() bug fix.
+
+ - Added ranlib of installed libfl.a.
+
+ - Some lint tweaks.
+
+ - NOTE: problems have been encountered attempting to build flex
+ C++ scanners using g++ version 2.5.X. The problem is due to an
+ unfortunate heuristic in g++ 2.5.X that attempts to discern between
+ C and C++ headers. Because FlexLexer.h is installed (by default)
+ in /usr/local/include and not /usr/local/lib/g++-include, g++ 2.5.X
+ decides that it's a C header :-(. So if you have problems, install
+ the header in /usr/local/lib/g++-include instead.
+
+
+Changes between release 2.4.2 (01Dec93) and release 2.4.1:
+
+ - Fixed bug in libfl.a referring to non-existent "flexfatal" function.
+
+ - Modified to produce both compress'd and gzip'd tar files for
+ distributions (you probably don't care about this change!).
+
+
+Changes between release 2.4.1 (30Nov93) and release 2.3.8:
+
+ - The new '-+' flag instructs flex to generate a C++ scanner class
+ (thanks to Kent Williams). flex writes an implementation of the
+ class defined in FlexLexer.h to lex.yy.cc. You may include
+ multiple scanner classes in your program using the -P flag. Note
+ that the scanner class also provides a mechanism for creating
+ reentrant scanners. The scanner class uses C++ streams for I/O
+ instead of FILE*'s (thanks to Tom Epperly). If the flex executable's
+ name ends in '+' then the '-+' flag is automatically on, so creating
+ a symlink or copy of "flex" to "flex++" results in a version of
+ flex that can be used exclusively for C++ scanners.
+
+ Note that without the '-+' flag, flex-generated scanners can still
+ be compiled using C++ compilers, though they use FILE*'s for I/O
+ instead of streams.
+
+ See the "GENERATING C++ SCANNERS" section of flexdoc for details.
+
+ - The new '-l' flag turns on maximum AT&T lex compatibility. In
+ particular, -l includes support for "yylineno" and makes yytext
+ be an array instead of a pointer. It does not, however, do away
+ with all incompatibilities. See the "INCOMPATIBILITIES WITH LEX
+ AND POSIX" section of flexdoc for details.
+
+ - The new '-P' option specifies a prefix to use other than "yy"
+ for the scanner's globally-visible variables, and for the
+ "lex.yy.c" filename. Using -P you can link together multiple
+ flex scanners in the same executable.
+
+ - The distribution includes a "texinfo" version of flexdoc.1,
+ contributed by Roland Pesch (thanks also to Marq Kole, who
+ contributed another version). It has not been brought up to
+ date, but reflects version 2.3. See MISC/flex.texinfo.
+
+ The flex distribution will soon include G.T. Nicol's flex
+ manual; he is presently bringing it up-to-date for version 2.4.
+
+ - yywrap() is now a function, and you now *must* link flex scanners
+ with libfl.a.
+
+ - Site-configuration is now done via an autoconf-generated
+ "configure" script contributed by Francois Pinard.
+
+ - Scanners now use fread() (or getc(), if interactive) and not
+ read() for input. A new "table compression" option, -Cr,
+ overrides this change and causes the scanner to use read()
+ (because read() is a bit faster than fread()). -f and -F
+ are now equivalent to -Cfr and -CFr; i.e., they imply the
+ -Cr option.
+
+ - In the blessed name of POSIX compliance, flex supports "%array"
+ and "%pointer" directives in the definitions (first) section of
+ the scanner specification. The former specifies that yytext
+ should be an array (of size YYLMAX), the latter, that it should
+ be a pointer. The array version of yytext is universally slower
+ than the pointer version, but has the advantage that its contents
+ remain unmodified across calls to input() and unput() (the pointer
+ version of yytext is, still, trashed by such calls).
+
+ "%array" cannot be used with the '-+' C++ scanner class option.
+
+ - The new '-Ca' option directs flex to trade off memory for
+ natural alignment when generating a scanner's tables. In
+ particular, table entries that would otherwise be "short"
+ become "long".
+
+ - The new '-h' option produces a summary of the flex flags.
+
+ - The new '-V' option reports the flex version number and exits.
+
+ - The new scanner macro YY_START returns an integer value
+ corresponding to the current start condition. You can return
+ to that start condition by passing the value to a subsequent
+ "BEGIN" action. You also can implement "start condition stacks"
+ by storing the values in an integer stack.
+
+ - You can now redefine macros such as YY_INPUT by just #define'ing
+ them to some other value in the first section of the flex input;
+ no need to first #undef them.
+
+ - flex now generates warnings for rules that can't be matched.
+ These warnings can be turned off using the new '-w' flag. If
+ your scanner uses REJECT then you will not get these warnings.
+
+ - If you specify the '-s' flag but the default rule can be matched,
+ flex now generates a warning.
+
+ - "yyleng" is now a global, and may be modified by the user (though
+ doing so and then using yymore() will yield weird results).
+
+ - Name definitions in the first section of a scanner specification
+ can now include a leading '^' or trailing '$' operator. In this
+ case, the definition is *not* pushed back inside of parentheses.
+
+ - Scanners with compressed tables are now "interactive" (-I option)
+ by default. You can suppress this attribute (which makes them
+ run slightly slower) using the new '-B' flag.
+
+ - Flex now generates 8-bit scanners by default, unless you use the
+ -Cf or -CF compression options (-Cfe and -CFe result in 8-bit
+ scanners). You can force it to generate a 7-bit scanner using
+ the new '-7' flag. You can build flex to generate 8-bit scanners
+ for -Cf and -CF, too, by adding -DDEFAULT_CSIZE=256 to CFLAGS
+ in the Makefile.
+
+ - You no longer need to call the scanner routine yyrestart() to
+ inform the scanner that you have switched to a new file after
+ having seen an EOF on the current input file. Instead, just
+ point yyin at the new file and continue scanning.
+
+ - You no longer need to invoke YY_NEW_FILE in an <<EOF>> action
+ to indicate you wish to continue scanning. Simply point yyin
+ at a new file.
+
+ - A leading '#' no longer introduces a comment in a flex input.
+
+ - flex no longer considers formfeed ('\f') a whitespace character.
+
+ - %t, I'm happy to report, has been nuked.
+
+ - The '-p' option may be given twice ('-pp') to instruct flex to
+ report minor performance problems as well as major ones.
+
+ - The '-v' verbose output no longer includes start/finish time
+ information.
+
+ - Newlines in flex inputs can optionally include leading or
+ trailing carriage-returns ('\r'), in support of several PC/Mac
+ run-time libraries that automatically include these.
+
+ - A start condition of the form "<*>" makes the following rule
+ active in every start condition, whether exclusive or inclusive.
+
+ - The following items have been corrected in the flex documentation:
+
+ - '-C' table compression options *are* cumulative.
+
+ - You may modify yytext but not lengthen it by appending
+ characters to the end. Modifying its final character
+ will affect '^' anchoring for the next rule matched
+ if the character is changed to or from a newline.
+
+ - The term "backtracking" has been renamed "backing up",
+ since it is a one-time repositioning and not a repeated
+ search. What used to be the "lex.backtrack" file is now
+ "lex.backup".
+
+ - Unindented "/* ... */" comments are allowed in the first
+ flex input section, but not in the second.
+
+ - yyless() can only be used in the flex input source, not
+ externally.
+
+ - You can use "yyrestart(yyin)" to throw away the
+ current contents of the input buffer.
+
+ - To write high-speed scanners, attempt to match as much
+ text as possible with each rule. See MISC/fastwc/README
+ for more information.
+
+ - Using the beginning-of-line operator ('^') is fairly
+ cheap. Using unput() is expensive. Using yyless() is
+ cheap.
+
+ - An example of scanning strings with embedded escape
+ sequences has been added.
+
+ - The example of backing-up in flexdoc was erroneous; it
+ has been corrected.
+
+ - A flex scanner's internal buffer now dynamically grows if needed
+ to match large tokens. Note that growing the buffer presently
+ requires rescanning the (large) token, so consuming a lot of
+ text this way is a slow process. Also note that presently the
+ buffer does *not* grow if you unput() more text than can fit
+ into the buffer.
+
+ - The MISC/ directory has been reorganized; see MISC/README for
+ details.
+
+ - yyless() can now be used in the third (user action) section
+ of a scanner specification, thanks to Ceriel Jacobs. yyless()
+ remains a macro and cannot be used outside of the scanner source.
+
+ - The skeleton file is no longer opened at run-time, but instead
+ compiled into a large string array (thanks to John Gilmore and
+ friends at Cygnus). You can still use the -S flag to point flex
+ at a different skeleton file, though if you use this option let
+ me know, as I plan to otherwise do away with -S in the near
+ future.
+
+ - flex no longer uses a temporary file to store the scanner's
+ actions.
+
+ - A number of changes have been made to decrease porting headaches.
+ In particular, flex no longer uses memset() or ctime(), and
+ provides a single simple mechanism for dealing with C compilers
+ that still define malloc() as returning char* instead of void*.
+
+ - Flex now detects if the scanner specification requires the -8 flag
+ but the flag was not given or on by default.
+
+ - A number of table-expansion fencepost bugs have been fixed,
+ making flex more robust for generating large scanners.
+
+ - flex more consistently identifies the location of errors in
+ its input.
+
+ - YY_USER_ACTION is now invoked only for "real" actions, not for
+ internal actions used by the scanner for things like filling
+ the buffer or handling EOF.
+
+ - The rule "[^]]" now matches any character other than a ']';
+ formerly it matched any character at all followed by a ']'.
+ This change was made for compatibility with AT&T lex.
+
+ - A large number of miscellaneous bugs have been found and fixed
+ thanks to Gerhard Wilhelms.
+
+ - The source code has been heavily reformatted, making patches
+ relative to previous flex releases no longer accurate.
+
+
+Changes between 2.3 Patch #8 (21Feb93) and 2.3 Patch #7:
+
+ - Fixed bugs in dynamic memory allocation leading to grievous
+ fencepost problems when generating large scanners.
+ - Fixed bug causing infinite loops on character classes with 8-bit
+ characters in them.
+ - Fixed bug in matching repetitions with a lower bound of 0.
+ - Fixed bug in scanning NUL characters using an "interactive" scanner.
+ - Fixed bug in using yymore() at the end of a file.
+ - Fixed bug in misrecognizing rules with variable trailing context.
+ - Fixed bug compiling flex on Suns using gcc 2.
+ - Fixed bug in not recognizing that input files with the character
+ ASCII 128 in them require the -8 flag.
+ - Fixed bug that could cause an infinite loop writing out
+ error messages.
+ - Fixed bug in not recognizing old-style lex % declarations if
+ followed by a tab instead of a space.
+ - Fixed potential crash when flex terminated early (usually due
+ to a bad flag) and the -v flag had been given.
+ - Added some missing declarations of void functions.
+ - Changed to only use '\a' for __STDC__ compilers.
+ - Updated mailing addresses.
+
+
+Changes between 2.3 Patch #7 (28Mar91) and 2.3 Patch #6:
+
+ - Fixed out-of-bounds array access that caused bad tables
+ to be produced on machines where the bad reference happened
+ to yield a 1. This caused problems installing or running
+ flex on some Suns, in particular.
+
+
+Changes between 2.3 Patch #6 (29Aug90) and 2.3 Patch #5:
+
+ - Fixed a serious bug in yymore() which basically made it
+ completely broken. Thanks goes to Jean Christophe of
+ the Nethack development team for finding the problem
+ and passing along the fix.
+
+
+Changes between 2.3 Patch #5 (16Aug90) and 2.3 Patch #4:
+
+ - An up-to-date version of initscan.c so "make test" will
+ work after applying the previous patches
+
+
+Changes between 2.3 Patch #4 (14Aug90) and 2.3 Patch #3:
+
+ - Fixed bug in hexadecimal escapes which allowed only digits,
+ not letters, in escapes
+ - Fixed bug in previous "Changes" file!
+
+
+Changes between 2.3 Patch #3 (03Aug90) and 2.3 Patch #2:
+
+ - Correction to patch #2 for gcc compilation; thanks goes to
+ Paul Eggert for catching this.
+
+
+Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release:
+
+ - Fixed (hopefully) headaches involving declaring malloc()
+ and free() for gcc, which defines __STDC__ but (often) doesn't
+ come with the standard include files such as <stdlib.h>.
+ Reordered #ifdef maze in the scanner skeleton in the hope of
+ getting the declarations right for cfront and g++, too.
+
+ - Note that this patch supercedes patch #1 for release 2.3,
+ which was never announced but was available briefly for
+ anonymous ftp.
+
+
+Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release:
+
+ User-visible:
+
+ - A lone <<EOF>> rule (that is, one which is not qualified with
+ a list of start conditions) now specifies the EOF action for
+ *all* start conditions which haven't already had <<EOF>> actions
+ given. To specify an end-of-file action for just the initial
+ state, use <INITIAL><<EOF>>.
+
+ - -d debug output is now contigent on the global yy_flex_debug
+ being set to a non-zero value, which it is by default.
+
+ - A new macro, YY_USER_INIT, is provided for the user to specify
+ initialization action to be taken on the first call to the
+ scanner. This action is done before the scanner does its
+ own initialization.
+
+ - yy_new_buffer() has been added as an alias for yy_create_buffer()
+
+ - Comments beginning with '#' and extending to the end of the line
+ now work, but have been deprecated (in anticipation of making
+ flex recognize #line directives).
+
+ - The funky restrictions on when semi-colons could follow the
+ YY_NEW_FILE and yyless macros have been removed. They now
+ behave identically to functions.
+
+ - A bug in the sample redefinition of YY_INPUT in the documentation
+ has been corrected.
+
+ - A bug in the sample simple tokener in the documentation has
+ been corrected.
+
+ - The documentation on the incompatibilities between flex and
+ lex has been reordered so that the discussion of yylineno
+ and input() come first, as it's anticipated that these will
+ be the most common source of headaches.
+
+
+ Things which didn't used to be documented but now are:
+
+ - flex interprets "^foo|bar" differently from lex. flex interprets
+ it as "match either a 'foo' or a 'bar', providing it comes at the
+ beginning of a line", whereas lex interprets it as "match either
+ a 'foo' at the beginning of a line, or a 'bar' anywhere".
+
+ - flex initializes the global "yyin" on the first call to the
+ scanner, while lex initializes it at compile-time.
+
+ - yy_switch_to_buffer() can be used in the yywrap() macro/routine.
+
+ - flex scanners do not use stdio for their input, and hence when
+ writing an interactive scanner one must explictly call fflush()
+ after writing out a prompt.
+
+ - flex scanner can be made reentrant (after a fashion) by using
+ "yyrestart( yyin );". This is useful for interactive scanners
+ which have interrupt handlers that long-jump out of the scanner.
+
+ - a defense of why yylineno is not supported is included, along
+ with a suggestion on how to convert scanners which rely on it.
+
+
+ Other changes:
+
+ - Prototypes and proper declarations of void routines have
+ been added to the flex source code, courtesy of Kevin B. Kenny.
+
+ - Routines dealing with memory allocation now use void* pointers
+ instead of char* - see Makefile for porting implications.
+
+ - Error-checking is now done when flex closes a file.
+
+ - Various lint tweaks were added to reduce the number of gripes.
+
+ - Makefile has been further parameterized to aid in porting.
+
+ - Support for SCO Unix added.
+
+ - Flex now sports the latest & greatest UC copyright notice
+ (which is only slightly different from the previous one).
+
+ - A note has been added to flexdoc.1 mentioning work in progress
+ on modifying flex to generate straight C code rather than a
+ table-driven automaton, with an email address of whom to contact
+ if you are working along similar lines.
+
+
+Changes between 2.2 Patch #3 (30Mar90) and 2.2 Patch #2:
+
+ - fixed bug which caused -I scanners to bomb
+
+
+Changes between 2.2 Patch #2 (27Mar90) and 2.2 Patch #1:
+
+ - fixed bug writing past end of input buffer in yyunput()
+ - fixed bug detecting NUL's at the end of a buffer
+
+
+Changes between 2.2 Patch #1 (23Mar90) and 2.2 (alpha) release:
+
+ - Makefile fixes: definition of MAKE variable for systems
+ which don't have it; installation of flexdoc.1 along with
+ flex.1; fixed two bugs which could cause "bigtest" to fail.
+
+ - flex.skel fix for compiling with g++.
+
+ - README and flexdoc.1 no longer list an out-of-date BITNET address
+ for contacting me.
+
+ - minor typos and formatting changes to flex.1 and flexdoc.1.
+
+
+Changes between 2.2 (alpha) release of March '90 and previous release:
+
+ User-visible:
+
+ - Full user documentation now available.
+
+ - Support for 8-bit scanners.
+
+ - Scanners now accept NUL's.
+
+ - A facility has been added for dealing with multiple
+ input buffers.
+
+ - Two manual entries now. One which fully describes flex
+ (rather than just its differences from lex), and the
+ other for quick(er) reference.
+
+ - A number of changes to bring flex closer into compliance
+ with the latest POSIX lex draft:
+
+ %t support
+ flex now accepts multiple input files and concatenates
+ them together to form its input
+ previous -c (compress) flag renamed -C
+ do-nothing -c and -n flags added
+ Any indented code or code within %{}'s in section 2 is
+ now copied to the output
+
+ - yyleng is now a bona fide global integer.
+
+ - -d debug information now gives the line number of the
+ matched rule instead of which number rule it was from
+ the beginning of the file.
+
+ - -v output now includes a summary of the flags used to generate
+ the scanner.
+
+ - unput() and yyrestart() are now globally callable.
+
+ - yyrestart() no longer closes the previous value of yyin.
+
+ - C++ support; generated scanners can be compiled with C++ compiler.
+
+ - Primitive -lfl library added, containing default main()
+ which calls yylex(). A number of routines currently living
+ in the scanner skeleton will probably migrate to here
+ in the future (in particular, yywrap() will probably cease
+ to be a macro and instead be a function in the -lfl library).
+
+ - Hexadecimal (\x) escape sequences added.
+
+ - Support for MS-DOS, VMS, and Turbo-C integrated.
+
+ - The %used/%unused operators have been deprecated. They
+ may go away soon.
+
+
+ Other changes:
+
+ - Makefile enhanced for easier testing and installation.
+ - The parser has been tweaked to detect some erroneous
+ constructions which previously were missed.
+ - Scanner input buffer overflow is now detected.
+ - Bugs with missing "const" declarations fixed.
+ - Out-of-date Minix/Atari patches provided.
+ - Scanners no longer require printf() unless FLEX_DEBUG is being used.
+ - A subtle input() bug has been fixed.
+ - Line numbers for "continued action" rules (those following
+ the special '|' action) are now correct.
+ - unput() bug fixed; had been causing problems porting flex to VMS.
+ - yymore() handling rewritten to fix bug with interaction
+ between yymore() and trailing context.
+ - EOF in actions now generates an error message.
+ - Bug involving -CFe and generating equivalence classes fixed.
+ - Bug which made -CF be treated as -Cf fixed.
+ - Support for SysV tmpnam() added.
+ - Unused #define's for scanner no longer generated.
+ - Error messages which are associated with a particular input
+ line are now all identified with their input line in standard
+ format.
+ - % directives which are valid to lex but not to flex are
+ now ignored instead of generating warnings.
+ - -DSYS_V flag can now also be specified -DUSG for System V
+ compilation.
+
+
+Changes between 2.1 beta-test release of June '89 and previous release:
+
+ User-visible:
+
+ - -p flag generates a performance report to stderr. The report
+ consists of comments regarding features of the scanner rules
+ which result in slower scanners.
+
+ - -b flag generates backtracking information to lex.backtrack.
+ This is a list of scanner states which require backtracking
+ and the characters on which they do so. By adding rules
+ one can remove backtracking states. If all backtracking states
+ are eliminated, the generated scanner will run faster.
+ Backtracking is not yet documented in the manual entry.
+
+ - Variable trailing context now works, i.e., one can have
+ rules like "(foo)*/[ \t]*bletch". Some trailing context
+ patterns still cannot be properly matched and generate
+ error messages. These are patterns where the ending of the
+ first part of the rule matches the beginning of the second
+ part, such as "zx*/xy*", where the 'x*' matches the 'x' at
+ the beginning of the trailing context. Lex won't get these
+ patterns right either.
+
+ - Faster scanners.
+
+ - End-of-file rules. The special rule "<<EOF>>" indicates
+ actions which are to be taken when an end-of-file is
+ encountered and yywrap() returns non-zero (i.e., indicates
+ no further files to process). See manual entry for example.
+
+ - The -r (reject used) flag is gone. flex now scans the input
+ for occurrences of the string "REJECT" to determine if the
+ action is needed. It tries to be intelligent about this but
+ can be fooled. One can force the presence or absence of
+ REJECT by adding a line in the first section of the form
+ "%used REJECT" or "%unused REJECT".
+
+ - yymore() has been implemented. Similarly to REJECT, flex
+ detects the use of yymore(), which can be overridden using
+ "%used" or "%unused".
+
+ - Patterns like "x{0,3}" now work (i.e., with lower-limit == 0).
+
+ - Removed '\^x' for ctrl-x misfeature.
+
+ - Added '\a' and '\v' escape sequences.
+
+ - \<digits> now works for octal escape sequences; previously
+ \0<digits> was required.
+
+ - Better error reporting; line numbers are associated with rules.
+
+ - yyleng is a macro; it cannot be accessed outside of the
+ scanner source file.
+
+ - yytext and yyleng should not be modified within a flex action.
+
+ - Generated scanners #define the name FLEX_SCANNER.
+
+ - Rules are internally separated by YY_BREAK in lex.yy.c rather
+ than break, to allow redefinition.
+
+ - The macro YY_USER_ACTION can be redefined to provide an action
+ which is always executed prior to the matched rule's action.
+
+ - yyrestart() is a new action which can be used to restart
+ the scanner after it has seen an end-of-file (a "real" one,
+ that is, one for which yywrap() returned non-zero). It takes
+ a FILE* argument indicating a new file to scan and sets
+ things up so that a subsequent call to yylex() will start
+ scanning that file.
+
+ - Internal scanner names all preceded by "yy_"
+
+ - lex.yy.c is deleted if errors are encountered during processing.
+
+ - Comments may be put in the first section of the input by preceding
+ them with '#'.
+
+
+
+ Other changes:
+
+ - Some portability-related bugs fixed, in particular for machines
+ with unsigned characters or sizeof( int* ) != sizeof( int ).
+ Also, tweaks for VMS and Microsoft C (MS-DOS), and identifiers all
+ trimmed to be 31 or fewer characters. Shortened file names
+ for dinosaur OS's. Checks for allocating > 64K memory
+ on 16 bit'ers. Amiga tweaks. Compiles using gcc on a Sun-3.
+ - Compressed and fast scanner skeletons merged.
+ - Skeleton header files done away with.
+ - Generated scanner uses prototypes and "const" for __STDC__.
+ - -DSV flag is now -DSYS_V for System V compilation.
+ - Removed all references to FTL language.
+ - Software now covered by BSD Copyright.
+ - flex will replace lex in subsequent BSD releases.
diff --git a/usr.bin/lex/README b/usr.bin/lex/README
new file mode 100644
index 0000000..339e9bb
--- /dev/null
+++ b/usr.bin/lex/README
@@ -0,0 +1,67 @@
+This is release 2.4 of flex. See "version.h" for the exact patch-level.
+
+See the file "NEWS" to find out what is new in this Flex release.
+
+Read the file "INSTALL" for general installation directives. Peek near
+the beginning of the file "Makefile.in" for special DEFS values. On most
+systems, you can just run the "configure" script and type "make" to build
+flex; then "make check" to test whether it built correctly; and if it did,
+then "make install" to install it.
+
+If you're feeling adventurous, you can also issue "make bigcheck" (be
+prepared to wait a while).
+
+Note that flex is distributed under a copyright very similar to that of
+BSD Unix, and not under the GNU General Public License (GPL), except for
+the "configure" script, which is covered by the GPL.
+
+Many thanks to the 2.4 pre-testers for finding a bunch of bugs and helping
+increase/test portability: Francois Pinard, Nathan Zelle, Gavin Nicol,
+Chris Thewalt, and Matthew Jacob.
+
+Please send bug reports and feedback to:
+
+ Vern Paxson
+ ICSD, 46A/1123
+ Lawrence Berkeley Laboratory
+ 1 Cyclotron Rd.
+ Berkeley, CA 94720
+
+ vern@ee.lbl.gov
+
+
+The flex distribution consists of the following files:
+
+ README This message
+
+ NEWS Differences between the various releases
+
+ INSTALL General installation information
+
+ COPYING flex's copyright
+
+ configure.in, configure, Makefile.in, install.sh, mkinstalldirs
+ elements of the "autoconf" auto-configuration process
+
+ flexdef.h, parse.y, scan.l, ccl.c, dfa.c, ecs.c, gen.c, main.c,
+ misc.c, nfa.c, sym.c, tblcmp.c, yylex.c
+ source files
+
+ version.h version of this flex release
+
+ flex.skl flex scanner skeleton
+ mkskel.sh script for converting flex.skl to C source file skel.c
+
+ liballoc.c
+ libmain.c flex library (-lfl) sources
+ libyywrap.c
+
+ initscan.c pre-flex'd version of scan.l
+
+ FlexLexer.h header file for C++ lexer class
+
+ flexdoc.1 full user documentation
+ flex.1 reference documentation
+
+ MISC/ a directory containing miscellaneous contributions.
+ See MISC/README for details.
diff --git a/usr.bin/lex/ccl.c b/usr.bin/lex/ccl.c
new file mode 100644
index 0000000..5f613b4
--- /dev/null
+++ b/usr.bin/lex/ccl.c
@@ -0,0 +1,149 @@
+/* ccl - routines for character classes */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/ccl.c,v 1.1.1.1 1994/08/24 13:10:33 csgr Exp $ */
+
+#include "flexdef.h"
+
+/* ccladd - add a single character to a ccl */
+
+void ccladd( cclp, ch )
+int cclp;
+int ch;
+ {
+ int ind, len, newpos, i;
+
+ check_char( ch );
+
+ len = ccllen[cclp];
+ ind = cclmap[cclp];
+
+ /* check to see if the character is already in the ccl */
+
+ for ( i = 0; i < len; ++i )
+ if ( ccltbl[ind + i] == ch )
+ return;
+
+ newpos = ind + len;
+
+ if ( newpos >= current_max_ccl_tbl_size )
+ {
+ current_max_ccl_tbl_size += MAX_CCL_TBL_SIZE_INCREMENT;
+
+ ++num_reallocs;
+
+ ccltbl = reallocate_Character_array( ccltbl,
+ current_max_ccl_tbl_size );
+ }
+
+ ccllen[cclp] = len + 1;
+ ccltbl[newpos] = ch;
+ }
+
+
+/* cclinit - return an empty ccl */
+
+int cclinit()
+ {
+ if ( ++lastccl >= current_maxccls )
+ {
+ current_maxccls += MAX_CCLS_INCREMENT;
+
+ ++num_reallocs;
+
+ cclmap = reallocate_integer_array( cclmap, current_maxccls );
+ ccllen = reallocate_integer_array( ccllen, current_maxccls );
+ cclng = reallocate_integer_array( cclng, current_maxccls );
+ }
+
+ if ( lastccl == 1 )
+ /* we're making the first ccl */
+ cclmap[lastccl] = 0;
+
+ else
+ /* The new pointer is just past the end of the last ccl.
+ * Since the cclmap points to the \first/ character of a
+ * ccl, adding the length of the ccl to the cclmap pointer
+ * will produce a cursor to the first free space.
+ */
+ cclmap[lastccl] = cclmap[lastccl - 1] + ccllen[lastccl - 1];
+
+ ccllen[lastccl] = 0;
+ cclng[lastccl] = 0; /* ccl's start out life un-negated */
+
+ return lastccl;
+ }
+
+
+/* cclnegate - negate the given ccl */
+
+void cclnegate( cclp )
+int cclp;
+ {
+ cclng[cclp] = 1;
+ }
+
+
+/* list_character_set - list the members of a set of characters in CCL form
+ *
+ * Writes to the given file a character-class representation of those
+ * characters present in the given CCL. A character is present if it
+ * has a non-zero value in the cset array.
+ */
+
+void list_character_set( file, cset )
+FILE *file;
+int cset[];
+ {
+ register int i;
+
+ putc( '[', file );
+
+ for ( i = 0; i < csize; ++i )
+ {
+ if ( cset[i] )
+ {
+ register int start_char = i;
+
+ putc( ' ', file );
+
+ fputs( readable_form( i ), file );
+
+ while ( ++i < csize && cset[i] )
+ ;
+
+ if ( i - 1 > start_char )
+ /* this was a run */
+ fprintf( file, "-%s", readable_form( i - 1 ) );
+
+ putc( ' ', file );
+ }
+ }
+
+ putc( ']', file );
+ }
diff --git a/usr.bin/lex/dfa.c b/usr.bin/lex/dfa.c
new file mode 100644
index 0000000..83eb454
--- /dev/null
+++ b/usr.bin/lex/dfa.c
@@ -0,0 +1,1085 @@
+/* dfa - DFA construction routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/dfa.c,v 1.1.1.1 1994/08/24 13:10:33 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+/* declare functions that have forward references */
+
+void dump_associated_rules PROTO((FILE*, int));
+void dump_transitions PROTO((FILE*, int[]));
+void sympartition PROTO((int[], int, int[], int[]));
+int symfollowset PROTO((int[], int, int, int[]));
+
+
+/* check_for_backing_up - check a DFA state for backing up
+ *
+ * synopsis
+ * void check_for_backing_up( int ds, int state[numecs] );
+ *
+ * ds is the number of the state to check and state[] is its out-transitions,
+ * indexed by equivalence class.
+ */
+
+void check_for_backing_up( ds, state )
+int ds;
+int state[];
+ {
+ if ( (reject && ! dfaacc[ds].dfaacc_set) ||
+ (! reject && ! dfaacc[ds].dfaacc_state) )
+ { /* state is non-accepting */
+ ++num_backing_up;
+
+ if ( backing_up_report )
+ {
+ fprintf( backing_up_file,
+ "State #%d is non-accepting -\n", ds );
+
+ /* identify the state */
+ dump_associated_rules( backing_up_file, ds );
+
+ /* Now identify it further using the out- and
+ * jam-transitions.
+ */
+ dump_transitions( backing_up_file, state );
+
+ putc( '\n', backing_up_file );
+ }
+ }
+ }
+
+
+/* check_trailing_context - check to see if NFA state set constitutes
+ * "dangerous" trailing context
+ *
+ * synopsis
+ * void check_trailing_context( int nfa_states[num_states+1], int num_states,
+ * int accset[nacc+1], int nacc );
+ *
+ * NOTES
+ * Trailing context is "dangerous" if both the head and the trailing
+ * part are of variable size \and/ there's a DFA state which contains
+ * both an accepting state for the head part of the rule and NFA states
+ * which occur after the beginning of the trailing context.
+ *
+ * When such a rule is matched, it's impossible to tell if having been
+ * in the DFA state indicates the beginning of the trailing context or
+ * further-along scanning of the pattern. In these cases, a warning
+ * message is issued.
+ *
+ * nfa_states[1 .. num_states] is the list of NFA states in the DFA.
+ * accset[1 .. nacc] is the list of accepting numbers for the DFA state.
+ */
+
+void check_trailing_context( nfa_states, num_states, accset, nacc )
+int *nfa_states, num_states;
+int *accset;
+register int nacc;
+ {
+ register int i, j;
+
+ for ( i = 1; i <= num_states; ++i )
+ {
+ int ns = nfa_states[i];
+ register int type = state_type[ns];
+ register int ar = assoc_rule[ns];
+
+ if ( type == STATE_NORMAL || rule_type[ar] != RULE_VARIABLE )
+ { /* do nothing */
+ }
+
+ else if ( type == STATE_TRAILING_CONTEXT )
+ {
+ /* Potential trouble. Scan set of accepting numbers
+ * for the one marking the end of the "head". We
+ * assume that this looping will be fairly cheap
+ * since it's rare that an accepting number set
+ * is large.
+ */
+ for ( j = 1; j <= nacc; ++j )
+ if ( accset[j] & YY_TRAILING_HEAD_MASK )
+ {
+ line_warning(
+ "dangerous trailing context",
+ rule_linenum[ar] );
+ return;
+ }
+ }
+ }
+ }
+
+
+/* dump_associated_rules - list the rules associated with a DFA state
+ *
+ * Goes through the set of NFA states associated with the DFA and
+ * extracts the first MAX_ASSOC_RULES unique rules, sorts them,
+ * and writes a report to the given file.
+ */
+
+void dump_associated_rules( file, ds )
+FILE *file;
+int ds;
+ {
+ register int i, j;
+ register int num_associated_rules = 0;
+ int rule_set[MAX_ASSOC_RULES + 1];
+ int *dset = dss[ds];
+ int size = dfasiz[ds];
+
+ for ( i = 1; i <= size; ++i )
+ {
+ register int rule_num = rule_linenum[assoc_rule[dset[i]]];
+
+ for ( j = 1; j <= num_associated_rules; ++j )
+ if ( rule_num == rule_set[j] )
+ break;
+
+ if ( j > num_associated_rules )
+ { /* new rule */
+ if ( num_associated_rules < MAX_ASSOC_RULES )
+ rule_set[++num_associated_rules] = rule_num;
+ }
+ }
+
+ bubble( rule_set, num_associated_rules );
+
+ fprintf( file, " associated rule line numbers:" );
+
+ for ( i = 1; i <= num_associated_rules; ++i )
+ {
+ if ( i % 8 == 1 )
+ putc( '\n', file );
+
+ fprintf( file, "\t%d", rule_set[i] );
+ }
+
+ putc( '\n', file );
+ }
+
+
+/* dump_transitions - list the transitions associated with a DFA state
+ *
+ * synopsis
+ * dump_transitions( FILE *file, int state[numecs] );
+ *
+ * Goes through the set of out-transitions and lists them in human-readable
+ * form (i.e., not as equivalence classes); also lists jam transitions
+ * (i.e., all those which are not out-transitions, plus EOF). The dump
+ * is done to the given file.
+ */
+
+void dump_transitions( file, state )
+FILE *file;
+int state[];
+ {
+ register int i, ec;
+ int out_char_set[CSIZE];
+
+ for ( i = 0; i < csize; ++i )
+ {
+ ec = ABS( ecgroup[i] );
+ out_char_set[i] = state[ec];
+ }
+
+ fprintf( file, " out-transitions: " );
+
+ list_character_set( file, out_char_set );
+
+ /* now invert the members of the set to get the jam transitions */
+ for ( i = 0; i < csize; ++i )
+ out_char_set[i] = ! out_char_set[i];
+
+ fprintf( file, "\n jam-transitions: EOF " );
+
+ list_character_set( file, out_char_set );
+
+ putc( '\n', file );
+ }
+
+
+/* epsclosure - construct the epsilon closure of a set of ndfa states
+ *
+ * synopsis
+ * int *epsclosure( int t[num_states], int *numstates_addr,
+ * int accset[num_rules+1], int *nacc_addr,
+ * int *hashval_addr );
+ *
+ * NOTES
+ * The epsilon closure is the set of all states reachable by an arbitrary
+ * number of epsilon transitions, which themselves do not have epsilon
+ * transitions going out, unioned with the set of states which have non-null
+ * accepting numbers. t is an array of size numstates of nfa state numbers.
+ * Upon return, t holds the epsilon closure and *numstates_addr is updated.
+ * accset holds a list of the accepting numbers, and the size of accset is
+ * given by *nacc_addr. t may be subjected to reallocation if it is not
+ * large enough to hold the epsilon closure.
+ *
+ * hashval is the hash value for the dfa corresponding to the state set.
+ */
+
+int *epsclosure( t, ns_addr, accset, nacc_addr, hv_addr )
+int *t, *ns_addr, accset[], *nacc_addr, *hv_addr;
+ {
+ register int stkpos, ns, tsp;
+ int numstates = *ns_addr, nacc, hashval, transsym, nfaccnum;
+ int stkend, nstate;
+ static int did_stk_init = false, *stk;
+
+#define MARK_STATE(state) \
+trans1[state] = trans1[state] - MARKER_DIFFERENCE;
+
+#define IS_MARKED(state) (trans1[state] < 0)
+
+#define UNMARK_STATE(state) \
+trans1[state] = trans1[state] + MARKER_DIFFERENCE;
+
+#define CHECK_ACCEPT(state) \
+{ \
+nfaccnum = accptnum[state]; \
+if ( nfaccnum != NIL ) \
+accset[++nacc] = nfaccnum; \
+}
+
+#define DO_REALLOCATION \
+{ \
+current_max_dfa_size += MAX_DFA_SIZE_INCREMENT; \
+++num_reallocs; \
+t = reallocate_integer_array( t, current_max_dfa_size ); \
+stk = reallocate_integer_array( stk, current_max_dfa_size ); \
+} \
+
+#define PUT_ON_STACK(state) \
+{ \
+if ( ++stkend >= current_max_dfa_size ) \
+DO_REALLOCATION \
+stk[stkend] = state; \
+MARK_STATE(state) \
+}
+
+#define ADD_STATE(state) \
+{ \
+if ( ++numstates >= current_max_dfa_size ) \
+DO_REALLOCATION \
+t[numstates] = state; \
+hashval += state; \
+}
+
+#define STACK_STATE(state) \
+{ \
+PUT_ON_STACK(state) \
+CHECK_ACCEPT(state) \
+if ( nfaccnum != NIL || transchar[state] != SYM_EPSILON ) \
+ADD_STATE(state) \
+}
+
+
+ if ( ! did_stk_init )
+ {
+ stk = allocate_integer_array( current_max_dfa_size );
+ did_stk_init = true;
+ }
+
+ nacc = stkend = hashval = 0;
+
+ for ( nstate = 1; nstate <= numstates; ++nstate )
+ {
+ ns = t[nstate];
+
+ /* The state could be marked if we've already pushed it onto
+ * the stack.
+ */
+ if ( ! IS_MARKED(ns) )
+ {
+ PUT_ON_STACK(ns)
+ CHECK_ACCEPT(ns)
+ hashval += ns;
+ }
+ }
+
+ for ( stkpos = 1; stkpos <= stkend; ++stkpos )
+ {
+ ns = stk[stkpos];
+ transsym = transchar[ns];
+
+ if ( transsym == SYM_EPSILON )
+ {
+ tsp = trans1[ns] + MARKER_DIFFERENCE;
+
+ if ( tsp != NO_TRANSITION )
+ {
+ if ( ! IS_MARKED(tsp) )
+ STACK_STATE(tsp)
+
+ tsp = trans2[ns];
+
+ if ( tsp != NO_TRANSITION && ! IS_MARKED(tsp) )
+ STACK_STATE(tsp)
+ }
+ }
+ }
+
+ /* Clear out "visit" markers. */
+
+ for ( stkpos = 1; stkpos <= stkend; ++stkpos )
+ {
+ if ( IS_MARKED(stk[stkpos]) )
+ UNMARK_STATE(stk[stkpos])
+ else
+ flexfatal( "consistency check failed in epsclosure()" );
+ }
+
+ *ns_addr = numstates;
+ *hv_addr = hashval;
+ *nacc_addr = nacc;
+
+ return t;
+ }
+
+
+/* increase_max_dfas - increase the maximum number of DFAs */
+
+void increase_max_dfas()
+ {
+ current_max_dfas += MAX_DFAS_INCREMENT;
+
+ ++num_reallocs;
+
+ base = reallocate_integer_array( base, current_max_dfas );
+ def = reallocate_integer_array( def, current_max_dfas );
+ dfasiz = reallocate_integer_array( dfasiz, current_max_dfas );
+ accsiz = reallocate_integer_array( accsiz, current_max_dfas );
+ dhash = reallocate_integer_array( dhash, current_max_dfas );
+ dss = reallocate_int_ptr_array( dss, current_max_dfas );
+ dfaacc = reallocate_dfaacc_union( dfaacc, current_max_dfas );
+
+ if ( nultrans )
+ nultrans =
+ reallocate_integer_array( nultrans, current_max_dfas );
+ }
+
+
+/* ntod - convert an ndfa to a dfa
+ *
+ * Creates the dfa corresponding to the ndfa we've constructed. The
+ * dfa starts out in state #1.
+ */
+
+void ntod()
+ {
+ int *accset, ds, nacc, newds;
+ int sym, hashval, numstates, dsize;
+ int num_full_table_rows; /* used only for -f */
+ int *nset, *dset;
+ int targptr, totaltrans, i, comstate, comfreq, targ;
+ int *epsclosure(), snstods(), symlist[CSIZE + 1];
+ int num_start_states;
+ int todo_head, todo_next;
+
+ /* Note that the following are indexed by *equivalence classes*
+ * and not by characters. Since equivalence classes are indexed
+ * beginning with 1, even if the scanner accepts NUL's, this
+ * means that (since every character is potentially in its own
+ * equivalence class) these arrays must have room for indices
+ * from 1 to CSIZE, so their size must be CSIZE + 1.
+ */
+ int duplist[CSIZE + 1], state[CSIZE + 1];
+ int targfreq[CSIZE + 1], targstate[CSIZE + 1];
+
+ accset = allocate_integer_array( num_rules + 1 );
+ nset = allocate_integer_array( current_max_dfa_size );
+
+ /* The "todo" queue is represented by the head, which is the DFA
+ * state currently being processed, and the "next", which is the
+ * next DFA state number available (not in use). We depend on the
+ * fact that snstods() returns DFA's \in increasing order/, and thus
+ * need only know the bounds of the dfas to be processed.
+ */
+ todo_head = todo_next = 0;
+
+ for ( i = 0; i <= csize; ++i )
+ {
+ duplist[i] = NIL;
+ symlist[i] = false;
+ }
+
+ for ( i = 0; i <= num_rules; ++i )
+ accset[i] = NIL;
+
+ if ( trace )
+ {
+ dumpnfa( scset[1] );
+ fputs( "\n\nDFA Dump:\n\n", stderr );
+ }
+
+ inittbl();
+
+ /* Check to see whether we should build a separate table for
+ * transitions on NUL characters. We don't do this for full-speed
+ * (-F) scanners, since for them we don't have a simple state
+ * number lying around with which to index the table. We also
+ * don't bother doing it for scanners unless (1) NUL is in its own
+ * equivalence class (indicated by a positive value of
+ * ecgroup[NUL]), (2) NUL's equivalence class is the last
+ * equivalence class, and (3) the number of equivalence classes is
+ * the same as the number of characters. This latter case comes
+ * about when useecs is false or when it's true but every character
+ * still manages to land in its own class (unlikely, but it's
+ * cheap to check for). If all these things are true then the
+ * character code needed to represent NUL's equivalence class for
+ * indexing the tables is going to take one more bit than the
+ * number of characters, and therefore we won't be assured of
+ * being able to fit it into a YY_CHAR variable. This rules out
+ * storing the transitions in a compressed table, since the code
+ * for interpreting them uses a YY_CHAR variable (perhaps it
+ * should just use an integer, though; this is worth pondering ...
+ * ###).
+ *
+ * Finally, for full tables, we want the number of entries in the
+ * table to be a power of two so the array references go fast (it
+ * will just take a shift to compute the major index). If
+ * encoding NUL's transitions in the table will spoil this, we
+ * give it its own table (note that this will be the case if we're
+ * not using equivalence classes).
+ */
+
+ /* Note that the test for ecgroup[0] == numecs below accomplishes
+ * both (1) and (2) above
+ */
+ if ( ! fullspd && ecgroup[0] == numecs )
+ {
+ /* NUL is alone in its equivalence class, which is the
+ * last one.
+ */
+ int use_NUL_table = (numecs == csize);
+
+ if ( fulltbl && ! use_NUL_table )
+ {
+ /* We still may want to use the table if numecs
+ * is a power of 2.
+ */
+ int power_of_two;
+
+ for ( power_of_two = 1; power_of_two <= csize;
+ power_of_two *= 2 )
+ if ( numecs == power_of_two )
+ {
+ use_NUL_table = true;
+ break;
+ }
+ }
+
+ if ( use_NUL_table )
+ nultrans = allocate_integer_array( current_max_dfas );
+
+ /* From now on, nultrans != nil indicates that we're
+ * saving null transitions for later, separate encoding.
+ */
+ }
+
+
+ if ( fullspd )
+ {
+ for ( i = 0; i <= numecs; ++i )
+ state[i] = 0;
+
+ place_state( state, 0, 0 );
+ dfaacc[i].dfaacc_state = 0;
+ }
+
+ else if ( fulltbl )
+ {
+ if ( nultrans )
+ /* We won't be including NUL's transitions in the
+ * table, so build it for entries from 0 .. numecs - 1.
+ */
+ num_full_table_rows = numecs;
+
+ else
+ /* Take into account the fact that we'll be including
+ * the NUL entries in the transition table. Build it
+ * from 0 .. numecs.
+ */
+ num_full_table_rows = numecs + 1;
+
+ /* Unless -Ca, declare it "short" because it's a real
+ * long-shot that that won't be large enough.
+ */
+ printf( "static const %s yy_nxt[][%d] =\n {\n",
+ /* '}' so vi doesn't get too confused */
+ long_align ? "long" : "short", num_full_table_rows );
+
+ /* Generate 0 entries for state #0. */
+ for ( i = 0; i < num_full_table_rows; ++i )
+ mk2data( 0 );
+
+ /* Force ',' and dataflush() next call to mk2data().*/
+ datapos = NUMDATAITEMS;
+
+ /* Force extra blank line next dataflush(). */
+ dataline = NUMDATALINES;
+ }
+
+ /* Create the first states. */
+
+ num_start_states = lastsc * 2;
+
+ for ( i = 1; i <= num_start_states; ++i )
+ {
+ numstates = 1;
+
+ /* For each start condition, make one state for the case when
+ * we're at the beginning of the line (the '^' operator) and
+ * one for the case when we're not.
+ */
+ if ( i % 2 == 1 )
+ nset[numstates] = scset[(i / 2) + 1];
+ else
+ nset[numstates] =
+ mkbranch( scbol[i / 2], scset[i / 2] );
+
+ nset = epsclosure( nset, &numstates, accset, &nacc, &hashval );
+
+ if ( snstods( nset, numstates, accset, nacc, hashval, &ds ) )
+ {
+ numas += nacc;
+ totnst += numstates;
+ ++todo_next;
+
+ if ( variable_trailing_context_rules && nacc > 0 )
+ check_trailing_context( nset, numstates,
+ accset, nacc );
+ }
+ }
+
+ if ( ! fullspd )
+ {
+ if ( ! snstods( nset, 0, accset, 0, 0, &end_of_buffer_state ) )
+ flexfatal(
+ "could not create unique end-of-buffer state" );
+
+ ++numas;
+ ++num_start_states;
+ ++todo_next;
+ }
+
+ while ( todo_head < todo_next )
+ {
+ targptr = 0;
+ totaltrans = 0;
+
+ for ( i = 1; i <= numecs; ++i )
+ state[i] = 0;
+
+ ds = ++todo_head;
+
+ dset = dss[ds];
+ dsize = dfasiz[ds];
+
+ if ( trace )
+ fprintf( stderr, "state # %d:\n", ds );
+
+ sympartition( dset, dsize, symlist, duplist );
+
+ for ( sym = 1; sym <= numecs; ++sym )
+ {
+ if ( symlist[sym] )
+ {
+ symlist[sym] = 0;
+
+ if ( duplist[sym] == NIL )
+ {
+ /* Symbol has unique out-transitions. */
+ numstates = symfollowset( dset, dsize,
+ sym, nset );
+ nset = epsclosure( nset, &numstates,
+ accset, &nacc, &hashval );
+
+ if ( snstods( nset, numstates, accset,
+ nacc, hashval, &newds ) )
+ {
+ totnst = totnst + numstates;
+ ++todo_next;
+ numas += nacc;
+
+ if (
+ variable_trailing_context_rules &&
+ nacc > 0 )
+ check_trailing_context(
+ nset, numstates,
+ accset, nacc );
+ }
+
+ state[sym] = newds;
+
+ if ( trace )
+ fprintf( stderr, "\t%d\t%d\n",
+ sym, newds );
+
+ targfreq[++targptr] = 1;
+ targstate[targptr] = newds;
+ ++numuniq;
+ }
+
+ else
+ {
+ /* sym's equivalence class has the same
+ * transitions as duplist(sym)'s
+ * equivalence class.
+ */
+ targ = state[duplist[sym]];
+ state[sym] = targ;
+
+ if ( trace )
+ fprintf( stderr, "\t%d\t%d\n",
+ sym, targ );
+
+ /* Update frequency count for
+ * destination state.
+ */
+
+ i = 0;
+ while ( targstate[++i] != targ )
+ ;
+
+ ++targfreq[i];
+ ++numdup;
+ }
+
+ ++totaltrans;
+ duplist[sym] = NIL;
+ }
+ }
+
+ numsnpairs = numsnpairs + totaltrans;
+
+ if ( caseins && ! useecs )
+ {
+ register int j;
+
+ for ( i = 'A', j = 'a'; i <= 'Z'; ++i, ++j )
+ state[i] = state[j];
+ }
+
+ if ( ds > num_start_states )
+ check_for_backing_up( ds, state );
+
+ if ( nultrans )
+ {
+ nultrans[ds] = state[NUL_ec];
+ state[NUL_ec] = 0; /* remove transition */
+ }
+
+ if ( fulltbl )
+ {
+ /* Supply array's 0-element. */
+ if ( ds == end_of_buffer_state )
+ mk2data( -end_of_buffer_state );
+ else
+ mk2data( end_of_buffer_state );
+
+ for ( i = 1; i < num_full_table_rows; ++i )
+ /* Jams are marked by negative of state
+ * number.
+ */
+ mk2data( state[i] ? state[i] : -ds );
+
+ /* Force ',' and dataflush() next call to mk2data().*/
+ datapos = NUMDATAITEMS;
+
+ /* Force extra blank line next dataflush(). */
+ dataline = NUMDATALINES;
+ }
+
+ else if ( fullspd )
+ place_state( state, ds, totaltrans );
+
+ else if ( ds == end_of_buffer_state )
+ /* Special case this state to make sure it does what
+ * it's supposed to, i.e., jam on end-of-buffer.
+ */
+ stack1( ds, 0, 0, JAMSTATE );
+
+ else /* normal, compressed state */
+ {
+ /* Determine which destination state is the most
+ * common, and how many transitions to it there are.
+ */
+
+ comfreq = 0;
+ comstate = 0;
+
+ for ( i = 1; i <= targptr; ++i )
+ if ( targfreq[i] > comfreq )
+ {
+ comfreq = targfreq[i];
+ comstate = targstate[i];
+ }
+
+ bldtbl( state, ds, totaltrans, comstate, comfreq );
+ }
+ }
+
+ if ( fulltbl )
+ dataend();
+
+ else if ( ! fullspd )
+ {
+ cmptmps(); /* create compressed template entries */
+
+ /* Create tables for all the states with only one
+ * out-transition.
+ */
+ while ( onesp > 0 )
+ {
+ mk1tbl( onestate[onesp], onesym[onesp], onenext[onesp],
+ onedef[onesp] );
+ --onesp;
+ }
+
+ mkdeftbl();
+ }
+
+ flex_free( (void *) accset );
+ flex_free( (void *) nset );
+ }
+
+
+/* snstods - converts a set of ndfa states into a dfa state
+ *
+ * synopsis
+ * is_new_state = snstods( int sns[numstates], int numstates,
+ * int accset[num_rules+1], int nacc,
+ * int hashval, int *newds_addr );
+ *
+ * On return, the dfa state number is in newds.
+ */
+
+int snstods( sns, numstates, accset, nacc, hashval, newds_addr )
+int sns[], numstates, accset[], nacc, hashval, *newds_addr;
+ {
+ int didsort = 0;
+ register int i, j;
+ int newds, *oldsns;
+
+ for ( i = 1; i <= lastdfa; ++i )
+ if ( hashval == dhash[i] )
+ {
+ if ( numstates == dfasiz[i] )
+ {
+ oldsns = dss[i];
+
+ if ( ! didsort )
+ {
+ /* We sort the states in sns so we
+ * can compare it to oldsns quickly.
+ * We use bubble because there probably
+ * aren't very many states.
+ */
+ bubble( sns, numstates );
+ didsort = 1;
+ }
+
+ for ( j = 1; j <= numstates; ++j )
+ if ( sns[j] != oldsns[j] )
+ break;
+
+ if ( j > numstates )
+ {
+ ++dfaeql;
+ *newds_addr = i;
+ return 0;
+ }
+
+ ++hshcol;
+ }
+
+ else
+ ++hshsave;
+ }
+
+ /* Make a new dfa. */
+
+ if ( ++lastdfa >= current_max_dfas )
+ increase_max_dfas();
+
+ newds = lastdfa;
+
+ dss[newds] = allocate_integer_array( numstates + 1 );
+
+ /* If we haven't already sorted the states in sns, we do so now,
+ * so that future comparisons with it can be made quickly.
+ */
+
+ if ( ! didsort )
+ bubble( sns, numstates );
+
+ for ( i = 1; i <= numstates; ++i )
+ dss[newds][i] = sns[i];
+
+ dfasiz[newds] = numstates;
+ dhash[newds] = hashval;
+
+ if ( nacc == 0 )
+ {
+ if ( reject )
+ dfaacc[newds].dfaacc_set = (int *) 0;
+ else
+ dfaacc[newds].dfaacc_state = 0;
+
+ accsiz[newds] = 0;
+ }
+
+ else if ( reject )
+ {
+ /* We sort the accepting set in increasing order so the
+ * disambiguating rule that the first rule listed is considered
+ * match in the event of ties will work. We use a bubble
+ * sort since the list is probably quite small.
+ */
+
+ bubble( accset, nacc );
+
+ dfaacc[newds].dfaacc_set = allocate_integer_array( nacc + 1 );
+
+ /* Save the accepting set for later */
+ for ( i = 1; i <= nacc; ++i )
+ {
+ dfaacc[newds].dfaacc_set[i] = accset[i];
+
+ if ( accset[i] <= num_rules )
+ /* Who knows, perhaps a REJECT can yield
+ * this rule.
+ */
+ rule_useful[accset[i]] = true;
+ }
+
+ accsiz[newds] = nacc;
+ }
+
+ else
+ {
+ /* Find lowest numbered rule so the disambiguating rule
+ * will work.
+ */
+ j = num_rules + 1;
+
+ for ( i = 1; i <= nacc; ++i )
+ if ( accset[i] < j )
+ j = accset[i];
+
+ dfaacc[newds].dfaacc_state = j;
+
+ if ( j <= num_rules )
+ rule_useful[j] = true;
+ }
+
+ *newds_addr = newds;
+
+ return 1;
+ }
+
+
+/* symfollowset - follow the symbol transitions one step
+ *
+ * synopsis
+ * numstates = symfollowset( int ds[current_max_dfa_size], int dsize,
+ * int transsym, int nset[current_max_dfa_size] );
+ */
+
+int symfollowset( ds, dsize, transsym, nset )
+int ds[], dsize, transsym, nset[];
+ {
+ int ns, tsp, sym, i, j, lenccl, ch, numstates, ccllist;
+
+ numstates = 0;
+
+ for ( i = 1; i <= dsize; ++i )
+ { /* for each nfa state ns in the state set of ds */
+ ns = ds[i];
+ sym = transchar[ns];
+ tsp = trans1[ns];
+
+ if ( sym < 0 )
+ { /* it's a character class */
+ sym = -sym;
+ ccllist = cclmap[sym];
+ lenccl = ccllen[sym];
+
+ if ( cclng[sym] )
+ {
+ for ( j = 0; j < lenccl; ++j )
+ {
+ /* Loop through negated character
+ * class.
+ */
+ ch = ccltbl[ccllist + j];
+
+ if ( ch == 0 )
+ ch = NUL_ec;
+
+ if ( ch > transsym )
+ /* Transsym isn't in negated
+ * ccl.
+ */
+ break;
+
+ else if ( ch == transsym )
+ /* next 2 */ goto bottom;
+ }
+
+ /* Didn't find transsym in ccl. */
+ nset[++numstates] = tsp;
+ }
+
+ else
+ for ( j = 0; j < lenccl; ++j )
+ {
+ ch = ccltbl[ccllist + j];
+
+ if ( ch == 0 )
+ ch = NUL_ec;
+
+ if ( ch > transsym )
+ break;
+ else if ( ch == transsym )
+ {
+ nset[++numstates] = tsp;
+ break;
+ }
+ }
+ }
+
+ else if ( sym >= 'A' && sym <= 'Z' && caseins )
+ flexfatal( "consistency check failed in symfollowset" );
+
+ else if ( sym == SYM_EPSILON )
+ { /* do nothing */
+ }
+
+ else if ( ABS( ecgroup[sym] ) == transsym )
+ nset[++numstates] = tsp;
+
+ bottom: ;
+ }
+
+ return numstates;
+ }
+
+
+/* sympartition - partition characters with same out-transitions
+ *
+ * synopsis
+ * sympartition( int ds[current_max_dfa_size], int numstates,
+ * int symlist[numecs], int duplist[numecs] );
+ */
+
+void sympartition( ds, numstates, symlist, duplist )
+int ds[], numstates;
+int symlist[], duplist[];
+ {
+ int tch, i, j, k, ns, dupfwd[CSIZE + 1], lenccl, cclp, ich;
+
+ /* Partitioning is done by creating equivalence classes for those
+ * characters which have out-transitions from the given state. Thus
+ * we are really creating equivalence classes of equivalence classes.
+ */
+
+ for ( i = 1; i <= numecs; ++i )
+ { /* initialize equivalence class list */
+ duplist[i] = i - 1;
+ dupfwd[i] = i + 1;
+ }
+
+ duplist[1] = NIL;
+ dupfwd[numecs] = NIL;
+
+ for ( i = 1; i <= numstates; ++i )
+ {
+ ns = ds[i];
+ tch = transchar[ns];
+
+ if ( tch != SYM_EPSILON )
+ {
+ if ( tch < -lastccl || tch >= csize )
+ {
+ flexfatal(
+ "bad transition character detected in sympartition()" );
+ }
+
+ if ( tch >= 0 )
+ { /* character transition */
+ int ec = ecgroup[tch];
+
+ mkechar( ec, dupfwd, duplist );
+ symlist[ec] = 1;
+ }
+
+ else
+ { /* character class */
+ tch = -tch;
+
+ lenccl = ccllen[tch];
+ cclp = cclmap[tch];
+ mkeccl( ccltbl + cclp, lenccl, dupfwd,
+ duplist, numecs, NUL_ec );
+
+ if ( cclng[tch] )
+ {
+ j = 0;
+
+ for ( k = 0; k < lenccl; ++k )
+ {
+ ich = ccltbl[cclp + k];
+
+ if ( ich == 0 )
+ ich = NUL_ec;
+
+ for ( ++j; j < ich; ++j )
+ symlist[j] = 1;
+ }
+
+ for ( ++j; j <= numecs; ++j )
+ symlist[j] = 1;
+ }
+
+ else
+ for ( k = 0; k < lenccl; ++k )
+ {
+ ich = ccltbl[cclp + k];
+
+ if ( ich == 0 )
+ ich = NUL_ec;
+
+ symlist[ich] = 1;
+ }
+ }
+ }
+ }
+ }
diff --git a/usr.bin/lex/ecs.c b/usr.bin/lex/ecs.c
new file mode 100644
index 0000000..7aed68f
--- /dev/null
+++ b/usr.bin/lex/ecs.c
@@ -0,0 +1,225 @@
+/* ecs - equivalence class routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/ecs.c,v 1.1.1.1 1994/08/24 13:10:33 csgr Exp $ */
+
+#include "flexdef.h"
+
+/* ccl2ecl - convert character classes to set of equivalence classes */
+
+void ccl2ecl()
+ {
+ int i, ich, newlen, cclp, ccls, cclmec;
+
+ for ( i = 1; i <= lastccl; ++i )
+ {
+ /* We loop through each character class, and for each character
+ * in the class, add the character's equivalence class to the
+ * new "character" class we are creating. Thus when we are all
+ * done, character classes will really consist of collections
+ * of equivalence classes
+ */
+
+ newlen = 0;
+ cclp = cclmap[i];
+
+ for ( ccls = 0; ccls < ccllen[i]; ++ccls )
+ {
+ ich = ccltbl[cclp + ccls];
+ cclmec = ecgroup[ich];
+
+ if ( cclmec > 0 )
+ {
+ ccltbl[cclp + newlen] = cclmec;
+ ++newlen;
+ }
+ }
+
+ ccllen[i] = newlen;
+ }
+ }
+
+
+/* cre8ecs - associate equivalence class numbers with class members
+ *
+ * fwd is the forward linked-list of equivalence class members. bck
+ * is the backward linked-list, and num is the number of class members.
+ *
+ * Returned is the number of classes.
+ */
+
+int cre8ecs( fwd, bck, num )
+int fwd[], bck[], num;
+ {
+ int i, j, numcl;
+
+ numcl = 0;
+
+ /* Create equivalence class numbers. From now on, ABS( bck(x) )
+ * is the equivalence class number for object x. If bck(x)
+ * is positive, then x is the representative of its equivalence
+ * class.
+ */
+ for ( i = 1; i <= num; ++i )
+ if ( bck[i] == NIL )
+ {
+ bck[i] = ++numcl;
+ for ( j = fwd[i]; j != NIL; j = fwd[j] )
+ bck[j] = -numcl;
+ }
+
+ return numcl;
+ }
+
+
+/* mkeccl - update equivalence classes based on character class xtions
+ *
+ * synopsis
+ * Char ccls[];
+ * int lenccl, fwd[llsiz], bck[llsiz], llsiz, NUL_mapping;
+ * void mkeccl( Char ccls[], int lenccl, int fwd[llsiz], int bck[llsiz],
+ * int llsiz, int NUL_mapping );
+ *
+ * ccls contains the elements of the character class, lenccl is the
+ * number of elements in the ccl, fwd is the forward link-list of equivalent
+ * characters, bck is the backward link-list, and llsiz size of the link-list.
+ *
+ * NUL_mapping is the value which NUL (0) should be mapped to.
+ */
+
+void mkeccl( ccls, lenccl, fwd, bck, llsiz, NUL_mapping )
+Char ccls[];
+int lenccl, fwd[], bck[], llsiz, NUL_mapping;
+ {
+ int cclp, oldec, newec;
+ int cclm, i, j;
+ static unsigned char cclflags[CSIZE]; /* initialized to all '\0' */
+
+ /* Note that it doesn't matter whether or not the character class is
+ * negated. The same results will be obtained in either case.
+ */
+
+ cclp = 0;
+
+ while ( cclp < lenccl )
+ {
+ cclm = ccls[cclp];
+
+ if ( NUL_mapping && cclm == 0 )
+ cclm = NUL_mapping;
+
+ oldec = bck[cclm];
+ newec = cclm;
+
+ j = cclp + 1;
+
+ for ( i = fwd[cclm]; i != NIL && i <= llsiz; i = fwd[i] )
+ { /* look for the symbol in the character class */
+ for ( ; j < lenccl; ++j )
+ {
+ register int ccl_char;
+
+ if ( NUL_mapping && ccls[j] == 0 )
+ ccl_char = NUL_mapping;
+ else
+ ccl_char = ccls[j];
+
+ if ( ccl_char > i )
+ break;
+
+ if ( ccl_char == i && ! cclflags[j] )
+ {
+ /* We found an old companion of cclm
+ * in the ccl. Link it into the new
+ * equivalence class and flag it as
+ * having been processed.
+ */
+
+ bck[i] = newec;
+ fwd[newec] = i;
+ newec = i;
+ /* Set flag so we don't reprocess. */
+ cclflags[j] = 1;
+
+ /* Get next equivalence class member. */
+ /* continue 2 */
+ goto next_pt;
+ }
+ }
+
+ /* Symbol isn't in character class. Put it in the old
+ * equivalence class.
+ */
+
+ bck[i] = oldec;
+
+ if ( oldec != NIL )
+ fwd[oldec] = i;
+
+ oldec = i;
+
+ next_pt: ;
+ }
+
+ if ( bck[cclm] != NIL || oldec != bck[cclm] )
+ {
+ bck[cclm] = NIL;
+ fwd[oldec] = NIL;
+ }
+
+ fwd[newec] = NIL;
+
+ /* Find next ccl member to process. */
+
+ for ( ++cclp; cclflags[cclp] && cclp < lenccl; ++cclp )
+ {
+ /* Reset "doesn't need processing" flag. */
+ cclflags[cclp] = 0;
+ }
+ }
+ }
+
+
+/* mkechar - create equivalence class for single character */
+
+void mkechar( tch, fwd, bck )
+int tch, fwd[], bck[];
+ {
+ /* If until now the character has been a proper subset of
+ * an equivalence class, break it away to create a new ec
+ */
+
+ if ( fwd[tch] != NIL )
+ bck[fwd[tch]] = bck[tch];
+
+ if ( bck[tch] != NIL )
+ fwd[bck[tch]] = fwd[tch];
+
+ fwd[tch] = NIL;
+ bck[tch] = NIL;
+ }
diff --git a/usr.bin/lex/flex.1 b/usr.bin/lex/flex.1
new file mode 100644
index 0000000..6aba4d6
--- /dev/null
+++ b/usr.bin/lex/flex.1
@@ -0,0 +1,1001 @@
+.TH FLEX 1 "November 1993" "Version 2.4"
+.SH NAME
+flex \- fast lexical analyzer generator
+.SH SYNOPSIS
+.B flex
+.B [\-bcdfhilnpstvwBFILTV78+ \-C[aefFmr] \-Pprefix \-Sskeleton]
+.I [filename ...]
+.SH DESCRIPTION
+.I flex
+is a tool for generating
+.I scanners:
+programs which recognized lexical patterns in text.
+.I flex
+reads
+the given input files, or its standard input if no file names are given,
+for a description of a scanner to generate. The description is in
+the form of pairs
+of regular expressions and C code, called
+.I rules. flex
+generates as output a C source file,
+.B lex.yy.c,
+which defines a routine
+.B yylex().
+This file is compiled and linked with the
+.B \-lfl
+library to produce an executable. When the executable is run,
+it analyzes its input for occurrences
+of the regular expressions. Whenever it finds one, it executes
+the corresponding C code.
+.PP
+For full documentation, see
+.B flexdoc(1).
+This manual entry is intended for use as a quick reference.
+.SH OPTIONS
+.I flex
+has the following options:
+.TP
+.B \-b
+generate backing-up information to
+.I lex.backup.
+This is a list of scanner states which require backing up and the input
+characters on which they do so. By adding rules one can remove
+backing-up states. If all backing-up states are eliminated and
+.B \-Cf
+or
+.B \-CF
+is used, the generated scanner will run faster.
+.TP
+.B \-c
+is a do-nothing, deprecated option included for POSIX compliance.
+.IP
+.B NOTE:
+in previous releases of
+.I flex
+.B \-c
+specified table-compression options. This functionality is
+now given by the
+.B \-C
+flag. To ease the the impact of this change, when
+.I flex
+encounters
+.B \-c,
+it currently issues a warning message and assumes that
+.B \-C
+was desired instead. In the future this "promotion" of
+.B \-c
+to
+.B \-C
+will go away in the name of full POSIX compliance (unless
+the POSIX meaning is removed first).
+.TP
+.B \-d
+makes the generated scanner run in
+.I debug
+mode. Whenever a pattern is recognized and the global
+.B yy_flex_debug
+is non-zero (which is the default), the scanner will
+write to
+.I stderr
+a line of the form:
+.nf
+
+ --accepting rule at line 53 ("the matched text")
+
+.fi
+The line number refers to the location of the rule in the file
+defining the scanner (i.e., the file that was fed to flex). Messages
+are also generated when the scanner backs up, accepts the
+default rule, reaches the end of its input buffer (or encounters
+a NUL; the two look the same as far as the scanner's concerned),
+or reaches an end-of-file.
+.TP
+.B \-f
+specifies
+.I fast scanner.
+No table compression is done and stdio is bypassed.
+The result is large but fast. This option is equivalent to
+.B \-Cfr
+(see below).
+.TP
+.B \-h
+generates a "help" summary of
+.I flex's
+options to
+.I stderr
+and then exits.
+.TP
+.B \-i
+instructs
+.I flex
+to generate a
+.I case-insensitive
+scanner. The case of letters given in the
+.I flex
+input patterns will
+be ignored, and tokens in the input will be matched regardless of case. The
+matched text given in
+.I yytext
+will have the preserved case (i.e., it will not be folded).
+.TP
+.B \-l
+turns on maximum compatibility with the original AT&T lex implementation,
+at a considerable performance cost. This option is incompatible with
+.B \-+, \-f, \-F, \-Cf,
+or
+.B \-CF.
+See
+.I flexdoc(1)
+for details.
+.TP
+.B \-n
+is another do-nothing, deprecated option included only for
+POSIX compliance.
+.TP
+.B \-p
+generates a performance report to stderr. The report
+consists of comments regarding features of the
+.I flex
+input file which will cause a loss of performance in the resulting scanner.
+If you give the flag twice, you will also get comments regarding
+features that lead to minor performance losses.
+.TP
+.B \-s
+causes the
+.I default rule
+(that unmatched scanner input is echoed to
+.I stdout)
+to be suppressed. If the scanner encounters input that does not
+match any of its rules, it aborts with an error.
+.TP
+.B \-t
+instructs
+.I flex
+to write the scanner it generates to standard output instead
+of
+.B lex.yy.c.
+.TP
+.B \-v
+specifies that
+.I flex
+should write to
+.I stderr
+a summary of statistics regarding the scanner it generates.
+.TP
+.B \-w
+suppresses warning messages.
+.TP
+.B \-B
+instructs
+.I flex
+to generate a
+.I batch
+scanner instead of an
+.I interactive
+scanner (see
+.B \-I
+below). See
+.I flexdoc(1)
+for details. Scanners using
+.B \-Cf
+or
+.B \-CF
+compression options automatically specify this option, too.
+.TP
+.B \-F
+specifies that the
+.ul
+fast
+scanner table representation should be used (and stdio bypassed).
+This representation is about as fast as the full table representation
+.B (-f),
+and for some sets of patterns will be considerably smaller (and for
+others, larger). It cannot be used with the
+.B \-+
+option. See
+.B flexdoc(1)
+for more details.
+.IP
+This option is equivalent to
+.B \-CFr
+(see below).
+.TP
+.B \-I
+instructs
+.I flex
+to generate an
+.I interactive
+scanner, that is, a scanner which stops immediately rather than
+looking ahead if it knows
+that the currently scanned text cannot be part of a longer rule's match.
+This is the opposite of
+.I batch
+scanners (see
+.B \-B
+above). See
+.B flexdoc(1)
+for details.
+.IP
+Note,
+.B \-I
+cannot be used in conjunction with
+.I full
+or
+.I fast tables,
+i.e., the
+.B \-f, \-F, \-Cf,
+or
+.B \-CF
+flags. For other table compression options,
+.B \-I
+is the default.
+.TP
+.B \-L
+instructs
+.I flex
+not to generate
+.B #line
+directives in
+.B lex.yy.c.
+The default is to generate such directives so error
+messages in the actions will be correctly
+located with respect to the original
+.I flex
+input file, and not to
+the fairly meaningless line numbers of
+.B lex.yy.c.
+.TP
+.B \-T
+makes
+.I flex
+run in
+.I trace
+mode. It will generate a lot of messages to
+.I stderr
+concerning
+the form of the input and the resultant non-deterministic and deterministic
+finite automata. This option is mostly for use in maintaining
+.I flex.
+.TP
+.B \-V
+prints the version number to
+.I stderr
+and exits.
+.TP
+.B \-7
+instructs
+.I flex
+to generate a 7-bit scanner, which can save considerable table space,
+especially when using
+.B \-Cf
+or
+.B \-CF
+(and, at most sites,
+.B \-7
+is on by default for these options. To see if this is the case, use the
+.B -v
+verbose flag and check the flag summary it reports).
+.TP
+.B \-8
+instructs
+.I flex
+to generate an 8-bit scanner. This is the default except for the
+.B \-Cf
+and
+.B \-CF
+compression options, for which the default is site-dependent, and
+can be checked by inspecting the flag summary generated by the
+.B \-v
+option.
+.TP
+.B \-+
+specifies that you want flex to generate a C++
+scanner class. See the section on Generating C++ Scanners in
+.I flexdoc(1)
+for details.
+.TP
+.B \-C[aefFmr]
+controls the degree of table compression and scanner optimization.
+.IP
+.B \-Ca
+trade off larger tables in the generated scanner for faster performance
+because the elements of the tables are better aligned for memory access
+and computation. This option can double the size of the tables used by
+your scanner.
+.IP
+.B \-Ce
+directs
+.I flex
+to construct
+.I equivalence classes,
+i.e., sets of characters
+which have identical lexical properties.
+Equivalence classes usually give
+dramatic reductions in the final table/object file sizes (typically
+a factor of 2-5) and are pretty cheap performance-wise (one array
+look-up per character scanned).
+.IP
+.B \-Cf
+specifies that the
+.I full
+scanner tables should be generated -
+.I flex
+should not compress the
+tables by taking advantages of similar transition functions for
+different states.
+.IP
+.B \-CF
+specifies that the alternate fast scanner representation (described in
+.B flexdoc(1))
+should be used. This option cannot be used with
+.B \-+.
+.IP
+.B \-Cm
+directs
+.I flex
+to construct
+.I meta-equivalence classes,
+which are sets of equivalence classes (or characters, if equivalence
+classes are not being used) that are commonly used together. Meta-equivalence
+classes are often a big win when using compressed tables, but they
+have a moderate performance impact (one or two "if" tests and one
+array look-up per character scanned).
+.IP
+.B \-Cr
+causes the generated scanner to
+.I bypass
+using stdio for input. In general this option results in a minor
+performance gain only worthwhile if used in conjunction with
+.B \-Cf
+or
+.B \-CF.
+It can cause surprising behavior if you use stdio yourself to
+read from
+.I yyin
+prior to calling the scanner.
+.IP
+A lone
+.B \-C
+specifies that the scanner tables should be compressed but neither
+equivalence classes nor meta-equivalence classes should be used.
+.IP
+The options
+.B \-Cf
+or
+.B \-CF
+and
+.B \-Cm
+do not make sense together - there is no opportunity for meta-equivalence
+classes if the table is not being compressed. Otherwise the options
+may be freely mixed.
+.IP
+The default setting is
+.B \-Cem,
+which specifies that
+.I flex
+should generate equivalence classes
+and meta-equivalence classes. This setting provides the highest
+degree of table compression. You can trade off
+faster-executing scanners at the cost of larger tables with
+the following generally being true:
+.nf
+
+ slowest & smallest
+ -Cem
+ -Cm
+ -Ce
+ -C
+ -C{f,F}e
+ -C{f,F}
+ -C{f,F}a
+ fastest & largest
+
+.fi
+.IP
+.B \-C
+options are cumulative.
+.TP
+.B \-Pprefix
+changes the default
+.I "yy"
+prefix used by
+.I flex
+to be
+.I prefix
+instead. See
+.I flexdoc(1)
+for a description of all the global variables and file names that
+this affects.
+.TP
+.B \-Sskeleton_file
+overrides the default skeleton file from which
+.I flex
+constructs its scanners. You'll never need this option unless you are doing
+.I flex
+maintenance or development.
+.SH SUMMARY OF FLEX REGULAR EXPRESSIONS
+The patterns in the input are written using an extended set of regular
+expressions. These are:
+.nf
+
+ x match the character 'x'
+ . any character except newline
+ [xyz] a "character class"; in this case, the pattern
+ matches either an 'x', a 'y', or a 'z'
+ [abj-oZ] a "character class" with a range in it; matches
+ an 'a', a 'b', any letter from 'j' through 'o',
+ or a 'Z'
+ [^A-Z] a "negated character class", i.e., any character
+ but those in the class. In this case, any
+ character EXCEPT an uppercase letter.
+ [^A-Z\\n] any character EXCEPT an uppercase letter or
+ a newline
+ r* zero or more r's, where r is any regular expression
+ r+ one or more r's
+ r? zero or one r's (that is, "an optional r")
+ r{2,5} anywhere from two to five r's
+ r{2,} two or more r's
+ r{4} exactly 4 r's
+ {name} the expansion of the "name" definition
+ (see above)
+ "[xyz]\\"foo"
+ the literal string: [xyz]"foo
+ \\X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+ then the ANSI-C interpretation of \\x.
+ Otherwise, a literal 'X' (used to escape
+ operators such as '*')
+ \\123 the character with octal value 123
+ \\x2a the character with hexadecimal value 2a
+ (r) match an r; parentheses are used to override
+ precedence (see below)
+
+
+ rs the regular expression r followed by the
+ regular expression s; called "concatenation"
+
+
+ r|s either an r or an s
+
+
+ r/s an r but only if it is followed by an s. The
+ s is not part of the matched text. This type
+ of pattern is called as "trailing context".
+ ^r an r, but only at the beginning of a line
+ r$ an r, but only at the end of a line. Equivalent
+ to "r/\\n".
+
+
+ <s>r an r, but only in start condition s (see
+ below for discussion of start conditions)
+ <s1,s2,s3>r
+ same, but in any of start conditions s1,
+ s2, or s3
+ <*>r an r in any start condition, even an exclusive one.
+
+
+ <<EOF>> an end-of-file
+ <s1,s2><<EOF>>
+ an end-of-file when in start condition s1 or s2
+
+.fi
+The regular expressions listed above are grouped according to
+precedence, from highest precedence at the top to lowest at the bottom.
+Those grouped together have equal precedence.
+.PP
+Some notes on patterns:
+.IP -
+Negated character classes
+.I match newlines
+unless "\\n" (or an equivalent escape sequence) is one of the
+characters explicitly present in the negated character class
+(e.g., "[^A-Z\\n]").
+.IP -
+A rule can have at most one instance of trailing context (the '/' operator
+or the '$' operator). The start condition, '^', and "<<EOF>>" patterns
+can only occur at the beginning of a pattern, and, as well as with '/' and '$',
+cannot be grouped inside parentheses. The following are all illegal:
+.nf
+
+ foo/bar$
+ foo|(bar$)
+ foo|^bar
+ <sc1>foo<sc2>bar
+
+.fi
+.SH SUMMARY OF SPECIAL ACTIONS
+In addition to arbitrary C code, the following can appear in actions:
+.IP -
+.B ECHO
+copies yytext to the scanner's output.
+.IP -
+.B BEGIN
+followed by the name of a start condition places the scanner in the
+corresponding start condition.
+.IP -
+.B REJECT
+directs the scanner to proceed on to the "second best" rule which matched the
+input (or a prefix of the input).
+.B yytext
+and
+.B yyleng
+are set up appropriately. Note that
+.B REJECT
+is a particularly expensive feature in terms scanner performance;
+if it is used in
+.I any
+of the scanner's actions it will slow down
+.I all
+of the scanner's matching. Furthermore,
+.B REJECT
+cannot be used with the
+.B \-f
+or
+.B \-F
+options.
+.IP
+Note also that unlike the other special actions,
+.B REJECT
+is a
+.I branch;
+code immediately following it in the action will
+.I not
+be executed.
+.IP -
+.B yymore()
+tells the scanner that the next time it matches a rule, the corresponding
+token should be
+.I appended
+onto the current value of
+.B yytext
+rather than replacing it.
+.IP -
+.B yyless(n)
+returns all but the first
+.I n
+characters of the current token back to the input stream, where they
+will be rescanned when the scanner looks for the next match.
+.B yytext
+and
+.B yyleng
+are adjusted appropriately (e.g.,
+.B yyleng
+will now be equal to
+.I n
+).
+.IP -
+.B unput(c)
+puts the character
+.I c
+back onto the input stream. It will be the next character scanned.
+.IP -
+.B input()
+reads the next character from the input stream (this routine is called
+.B yyinput()
+if the scanner is compiled using
+.B C++).
+.IP -
+.B yyterminate()
+can be used in lieu of a return statement in an action. It terminates
+the scanner and returns a 0 to the scanner's caller, indicating "all done".
+.IP
+By default,
+.B yyterminate()
+is also called when an end-of-file is encountered. It is a macro and
+may be redefined.
+.IP -
+.B YY_NEW_FILE
+is an action available only in <<EOF>> rules. It means "Okay, I've
+set up a new input file, continue scanning". It is no longer required;
+you can just assign
+.I yyin
+to point to a new file in the <<EOF>> action.
+.IP -
+.B yy_create_buffer( file, size )
+takes a
+.I FILE
+pointer and an integer
+.I size.
+It returns a YY_BUFFER_STATE
+handle to a new input buffer large enough to accomodate
+.I size
+characters and associated with the given file. When in doubt, use
+.B YY_BUF_SIZE
+for the size.
+.IP -
+.B yy_switch_to_buffer( new_buffer )
+switches the scanner's processing to scan for tokens from
+the given buffer, which must be a YY_BUFFER_STATE.
+.IP -
+.B yy_delete_buffer( buffer )
+deletes the given buffer.
+.SH VALUES AVAILABLE TO THE USER
+.IP -
+.B char *yytext
+holds the text of the current token. It may be modified but not lengthened
+(you cannot append characters to the end). Modifying the last character
+may affect the activity of rules anchored using '^' during the next scan;
+see
+.B flexdoc(1)
+for details.
+.IP
+If the special directive
+.B %array
+appears in the first section of the scanner description, then
+.B yytext
+is instead declared
+.B char yytext[YYLMAX],
+where
+.B YYLMAX
+is a macro definition that you can redefine in the first section
+if you don't like the default value (generally 8KB). Using
+.B %array
+results in somewhat slower scanners, but the value of
+.B yytext
+becomes immune to calls to
+.I input()
+and
+.I unput(),
+which potentially destroy its value when
+.B yytext
+is a character pointer. The opposite of
+.B %array
+is
+.B %pointer,
+which is the default.
+.IP
+You cannot use
+.B %array
+when generating C++ scanner classes
+(the
+.B \-+
+flag).
+.IP -
+.B int yyleng
+holds the length of the current token.
+.IP -
+.B FILE *yyin
+is the file which by default
+.I flex
+reads from. It may be redefined but doing so only makes sense before
+scanning begins or after an EOF has been encountered. Changing it in
+the midst of scanning will have unexpected results since
+.I flex
+buffers its input; use
+.B yyrestart()
+instead.
+Once scanning terminates because an end-of-file
+has been seen,
+.B
+you can assign
+.I yyin
+at the new input file and then call the scanner again to continue scanning.
+.IP -
+.B void yyrestart( FILE *new_file )
+may be called to point
+.I yyin
+at the new input file. The switch-over to the new file is immediate
+(any previously buffered-up input is lost). Note that calling
+.B yyrestart()
+with
+.I yyin
+as an argument thus throws away the current input buffer and continues
+scanning the same input file.
+.IP -
+.B FILE *yyout
+is the file to which
+.B ECHO
+actions are done. It can be reassigned by the user.
+.IP -
+.B YY_CURRENT_BUFFER
+returns a
+.B YY_BUFFER_STATE
+handle to the current buffer.
+.IP -
+.B YY_START
+returns an integer value corresponding to the current start
+condition. You can subsequently use this value with
+.B BEGIN
+to return to that start condition.
+.SH MACROS AND FUNCTIONS YOU CAN REDEFINE
+.IP -
+.B YY_DECL
+controls how the scanning routine is declared.
+By default, it is "int yylex()", or, if prototypes are being
+used, "int yylex(void)". This definition may be changed by redefining
+the "YY_DECL" macro. Note that
+if you give arguments to the scanning routine using a
+K&R-style/non-prototyped function declaration, you must terminate
+the definition with a semi-colon (;).
+.IP -
+The nature of how the scanner
+gets its input can be controlled by redefining the
+.B YY_INPUT
+macro.
+YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its
+action is to place up to
+.I max_size
+characters in the character array
+.I buf
+and return in the integer variable
+.I result
+either the
+number of characters read or the constant YY_NULL (0 on Unix systems)
+to indicate EOF. The default YY_INPUT reads from the
+global file-pointer "yyin".
+A sample redefinition of YY_INPUT (in the definitions
+section of the input file):
+.nf
+
+ %{
+ #undef YY_INPUT
+ #define YY_INPUT(buf,result,max_size) \\
+ { \\
+ int c = getchar(); \\
+ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\
+ }
+ %}
+
+.fi
+.IP -
+When the scanner receives an end-of-file indication from YY_INPUT,
+it then checks the function
+.B yywrap()
+function. If
+.B yywrap()
+returns false (zero), then it is assumed that the
+function has gone ahead and set up
+.I yyin
+to point to another input file, and scanning continues. If it returns
+true (non-zero), then the scanner terminates, returning 0 to its
+caller.
+.IP
+The default
+.B yywrap()
+always returns 1.
+.IP -
+YY_USER_ACTION
+can be redefined to provide an action
+which is always executed prior to the matched rule's action.
+.IP -
+The macro
+.B YY_USER_INIT
+may be redefined to provide an action which is always executed before
+the first scan.
+.IP -
+In the generated scanner, the actions are all gathered in one large
+switch statement and separated using
+.B YY_BREAK,
+which may be redefined. By default, it is simply a "break", to separate
+each rule's action from the following rule's.
+.SH FILES
+.TP
+.B \-lfl
+library with which to link scanners to obtain the default versions
+of
+.I yywrap()
+and/or
+.I main().
+.TP
+.I lex.yy.c
+generated scanner (called
+.I lexyy.c
+on some systems).
+.TP
+.I lex.yy.cc
+generated C++ scanner class, when using
+.B -+.
+.TP
+.I <FlexLexer.h>
+header file defining the C++ scanner base class,
+.B FlexLexer,
+and its derived class,
+.B yyFlexLexer.
+.TP
+.I flex.skl
+skeleton scanner. This file is only used when building flex, not when
+flex executes.
+.TP
+.I lex.backup
+backing-up information for
+.B \-b
+flag (called
+.I lex.bck
+on some systems).
+.SH "SEE ALSO"
+.PP
+flexdoc(1), lex(1), yacc(1), sed(1), awk(1).
+.PP
+M. E. Lesk and E. Schmidt,
+.I LEX \- Lexical Analyzer Generator
+.SH DIAGNOSTICS
+.PP
+.I reject_used_but_not_detected undefined
+or
+.PP
+.I yymore_used_but_not_detected undefined -
+These errors can occur at compile time. They indicate that the
+scanner uses
+.B REJECT
+or
+.B yymore()
+but that
+.I flex
+failed to notice the fact, meaning that
+.I flex
+scanned the first two sections looking for occurrences of these actions
+and failed to find any, but somehow you snuck some in (via a #include
+file, for example). Make an explicit reference to the action in your
+.I flex
+input file. (Note that previously
+.I flex
+supported a
+.B %used/%unused
+mechanism for dealing with this problem; this feature is still supported
+but now deprecated, and will go away soon unless the author hears from
+people who can argue compellingly that they need it.)
+.PP
+.I flex scanner jammed -
+a scanner compiled with
+.B \-s
+has encountered an input string which wasn't matched by
+any of its rules.
+.PP
+.I warning, rule cannot be matched
+indicates that the given rule
+cannot be matched because it follows other rules that will
+always match the same text as it. See
+.I flexdoc(1)
+for an example.
+.PP
+.I warning,
+.B \-s
+.I
+option given but default rule can be matched
+means that it is possible (perhaps only in a particular start condition)
+that the default rule (match any single character) is the only one
+that will match a particular input. Since
+.PP
+.I scanner input buffer overflowed -
+a scanner rule matched more text than the available dynamic memory.
+.PP
+.I token too large, exceeds YYLMAX -
+your scanner uses
+.B %array
+and one of its rules matched a string longer than the
+.B YYLMAX
+constant (8K bytes by default). You can increase the value by
+#define'ing
+.B YYLMAX
+in the definitions section of your
+.I flex
+input.
+.PP
+.I scanner requires \-8 flag to
+.I use the character 'x' -
+Your scanner specification includes recognizing the 8-bit character
+.I 'x'
+and you did not specify the \-8 flag, and your scanner defaulted to 7-bit
+because you used the
+.B \-Cf
+or
+.B \-CF
+table compression options.
+.PP
+.I flex scanner push-back overflow -
+you used
+.B unput()
+to push back so much text that the scanner's buffer could not hold
+both the pushed-back text and the current token in
+.B yytext.
+Ideally the scanner should dynamically resize the buffer in this case, but at
+present it does not.
+.PP
+.I
+input buffer overflow, can't enlarge buffer because scanner uses REJECT -
+the scanner was working on matching an extremely large token and needed
+to expand the input buffer. This doesn't work with scanners that use
+.B
+REJECT.
+.PP
+.I
+fatal flex scanner internal error--end of buffer missed -
+This can occur in an scanner which is reentered after a long-jump
+has jumped out (or over) the scanner's activation frame. Before
+reentering the scanner, use:
+.nf
+
+ yyrestart( yyin );
+
+.fi
+or use C++ scanner classes (the
+.B \-+
+option), which are fully reentrant.
+.SH AUTHOR
+Vern Paxson, with the help of many ideas and much inspiration from
+Van Jacobson. Original version by Jef Poskanzer.
+.PP
+See flexdoc(1) for additional credits and the address to send comments to.
+.SH DEFICIENCIES / BUGS
+.PP
+Some trailing context
+patterns cannot be properly matched and generate
+warning messages ("dangerous trailing context"). These are
+patterns where the ending of the
+first part of the rule matches the beginning of the second
+part, such as "zx*/xy*", where the 'x*' matches the 'x' at
+the beginning of the trailing context. (Note that the POSIX draft
+states that the text matched by such patterns is undefined.)
+.PP
+For some trailing context rules, parts which are actually fixed-length are
+not recognized as such, leading to the abovementioned performance loss.
+In particular, parts using '|' or {n} (such as "foo{3}") are always
+considered variable-length.
+.PP
+Combining trailing context with the special '|' action can result in
+.I fixed
+trailing context being turned into the more expensive
+.I variable
+trailing context. For example, in the following:
+.nf
+
+ %%
+ abc |
+ xyz/def
+
+.fi
+.PP
+Use of
+.B unput()
+or
+.B input()
+invalidates yytext and yyleng, unless the
+.B %array
+directive
+or the
+.B \-l
+option has been used.
+.PP
+Use of unput() to push back more text than was matched can
+result in the pushed-back text matching a beginning-of-line ('^')
+rule even though it didn't come at the beginning of the line
+(though this is rare!).
+.PP
+Pattern-matching of NUL's is substantially slower than matching other
+characters.
+.PP
+Dynamic resizing of the input buffer is slow, as it entails rescanning
+all the text matched so far by the current (generally huge) token.
+.PP
+.I flex
+does not generate correct #line directives for code internal
+to the scanner; thus, bugs in
+.I flex.skl
+yield bogus line numbers.
+.PP
+Due to both buffering of input and read-ahead, you cannot intermix
+calls to <stdio.h> routines, such as, for example,
+.B getchar(),
+with
+.I flex
+rules and expect it to work. Call
+.B input()
+instead.
+.PP
+The total table entries listed by the
+.B \-v
+flag excludes the number of table entries needed to determine
+what rule has been matched. The number of entries is equal
+to the number of DFA states if the scanner does not use
+.B REJECT,
+and somewhat greater than the number of states if it does.
+.PP
+.B REJECT
+cannot be used with the
+.B \-f
+or
+.B \-F
+options.
+.PP
+The
+.I flex
+internal algorithms need documentation.
diff --git a/usr.bin/lex/flex.skl b/usr.bin/lex/flex.skl
new file mode 100644
index 0000000..01ff7a1
--- /dev/null
+++ b/usr.bin/lex/flex.skl
@@ -0,0 +1,1225 @@
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/flex-2.4.7/RCS/flex.skl,v 1.2 94/08/03 11:13:24 vern Exp $
+ */
+
+#define FLEX_SCANNER
+
+%-
+#include <stdio.h>
+%*
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+%+
+class istream;
+%*
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#ifdef __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+
+#ifdef __TURBOC__
+#define YY_USE_CONST
+#endif
+
+
+#ifndef YY_USE_CONST
+#ifndef const
+#define const
+#endif
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.
+ */
+#define YY_START ((yy_start - 1) / 2)
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". Now included
+ * only for backward compatibility with previous versions of flex.
+ */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+%-
+extern FILE *yyin, *yyout;
+%*
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern int yywrap YY_PROTO(( void ));
+#ifdef __cplusplus
+ }
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+
+struct yy_buffer_state
+ {
+%-
+ FILE *yy_input_file;
+%+
+ istream* yy_input_file;
+%*
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+%- Standard (non-C++) definition
+static YY_BUFFER_STATE yy_current_buffer = 0;
+%*
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+%- Standard (non-C++) definition
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+void yyrestart YY_PROTO(( FILE *input_file ));
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+static void yy_push_state YY_PROTO(( int new_state ));
+static void yy_pop_state YY_PROTO(( void ));
+static int yy_top_state YY_PROTO(( void ));
+%*
+
+static void *yy_flex_alloc YY_PROTO(( unsigned int ));
+static void *yy_flex_realloc YY_PROTO(( void *, unsigned int ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+%% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, const char *, int ));
+#endif
+
+%- Standard (non-C++) definition
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+%*
+
+%- Standard (non-C++) definition
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( const char msg[] ));
+%*
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+%% code to fiddle yytext and yyleng for yymore() goes here
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+%% code to copy yytext_ptr to yytext[] goes here, if %array
+ yy_c_buf_p = yy_cp;
+
+%% data tables for the DFA and the user's section 1 definitions go here
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+%- Standard (non-C++) definition
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+%+ C++ definition
+#define ECHO LexerOutput( yytext, yyleng )
+%*
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+%% fread()/read() definition of YY_INPUT goes here unless we're doing C++
+%+ C++ definition
+ if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+%*
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+%-
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+%+
+#define YY_FATAL_ERROR(msg) LexerError( msg )
+%*
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+%- Standard (non-C++) definition
+#define YY_DECL int yylex YY_PROTO(( void ))
+%+ C++ definition
+#define YY_DECL int yyFlexLexer::yylex()
+%*
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+%% user's declarations go here
+
+ if ( yy_init )
+ {
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+%-
+ yyin = stdin;
+%+
+ yyin = &cin;
+%*
+
+ if ( ! yyout )
+%-
+ yyout = stdout;
+%+
+ yyout = &cout;
+%*
+
+ if ( yy_current_buffer )
+ yy_init_buffer( yy_current_buffer, yyin );
+ else
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+
+ yy_init = 0;
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+%% yymore()-related code goes here
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+%% code to set up and find next match goes here
+
+yy_find_action:
+%% code to find the action number goes here
+
+ YY_DO_BEFORE_ACTION;
+
+%% code for yylineno update goes here, if -l option
+
+do_action: /* This label is used only to access EOF actions. */
+
+%% debug code goes here
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+%% actions go here
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = yy_cp - yytext_ptr - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+%% code to do back-up for compressed tables and set up yy_cp goes here
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+%+
+#ifdef YY_INTERACTIVE
+int yyFlexLexer::LexerInput( char* buf, int /* max_size */ )
+#else
+int yyFlexLexer::LexerInput( char* buf, int max_size )
+#endif
+ {
+ if ( yyin->eof() || yyin->fail() )
+ return 0;
+
+#ifdef YY_INTERACTIVE
+ yyin->get( buf[0] );
+
+ if ( yyin->eof() )
+ return 0;
+
+ if ( yyin->bad() )
+ return -1;
+
+ return 1;
+
+#else
+ (void) yyin->read( buf, max_size );
+
+ if ( yyin->bad() )
+ return -1;
+ else
+ return yyin->gcount();
+#endif
+ }
+
+void yyFlexLexer::LexerOutput( const char* buf, int size )
+ {
+ (void) yyout->write( buf, size );
+ }
+%*
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+%-
+static int yy_get_next_buffer()
+%+
+int yyFlexLexer::yy_get_next_buffer()
+%*
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr - 1; /* copy prev. char, too */
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a singled characater, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = yy_c_buf_p - yytext_ptr;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset = yy_c_buf_p - b->yy_ch_buf;
+
+ b->yy_buf_size *= 2;
+ b->yy_ch_buf = (char *)
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move - YY_MORE_ADJ == 1 )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ /* yytext begins at the second character in yy_ch_buf; the first
+ * character is the one which preceded it before reading in the latest
+ * buffer; it needs to be kept around in case it's a newline, so
+ * yy_get_previous_state() will have with '^' rules active.
+ */
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[1];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+%-
+static yy_state_type yy_get_previous_state()
+%+
+yy_state_type yyFlexLexer::yy_get_previous_state()
+%*
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+%% code to get the start state into yy_current_state goes here
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+%% code to find the next state goes here
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+%-
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+%+
+yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state )
+%*
+ {
+ register int yy_is_jam;
+%% code to find the next state, and perhaps do backing up, goes here
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+%+
+void yyFlexLexer::yyunput( int c, register char* yy_bp )
+%*
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += dest - source;
+ yy_bp += dest - source;
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ if ( yy_cp > yy_bp && yy_cp[-1] == '\n' )
+ yy_cp[-2] = '\n';
+
+ *--yy_cp = (char) c;
+
+%% update yylineno here, if doing -l
+
+ /* Note: the formal parameter *must* be called "yy_bp" for this
+ * macro to now work correctly.
+ */
+ YY_DO_BEFORE_ACTION; /* set up yytext again */
+ }
+
+
+%-
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+%+
+int yyFlexLexer::yyinput()
+%*
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yytext_ptr = yy_c_buf_p;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ {
+ yy_c_buf_p =
+ yytext_ptr + YY_MORE_ADJ;
+ return EOF;
+ }
+
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+ break;
+
+ case EOB_ACT_LAST_MATCH:
+#ifdef __cplusplus
+ YY_FATAL_ERROR(
+ "unexpected last match in yyinput()" );
+#else
+ YY_FATAL_ERROR(
+ "unexpected last match in input()" );
+#endif
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+ return c;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+%+
+void yyFlexLexer::yyrestart( istream* input_file )
+%*
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+%+
+void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+%*
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+%+
+void yyFlexLexer::yy_load_buffer_state()
+%*
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+%+
+YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( istream* file, int size )
+%*
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+%+
+void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b )
+%*
+ {
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ yy_flex_free( (void *) b->yy_ch_buf );
+ yy_flex_free( (void *) b );
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+%+
+void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )
+%*
+ {
+ b->yy_input_file = file;
+
+ /* We put in the '\n' and start reading from [1] so that an
+ * initial match-at-newline will be true.
+ */
+
+ b->yy_ch_buf[0] = '\n';
+ b->yy_n_chars = 1;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[1];
+
+%-
+ b->yy_is_interactive = file ? isatty( fileno(file) ) : 0;
+%+
+ b->yy_is_interactive = 0;
+%*
+
+ b->yy_fill_buffer = 1;
+
+ b->yy_buffer_status = YY_BUFFER_NEW;
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+%+
+void yyFlexLexer::yy_push_state( int new_state )
+%*
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ int new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+
+
+%-
+static void yy_pop_state()
+%+
+void yyFlexLexer::yy_pop_state()
+%*
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+
+
+%-
+static int yy_top_state()
+%+
+int yyFlexLexer::yy_top_state()
+%*
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+
+
+%-
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( const char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( 1 );
+ }
+
+%+
+
+void yyFlexLexer::LexerError( const char msg[] )
+ {
+ cerr << msg << '\n';
+ exit( 1 );
+ }
+%*
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n - YY_MORE_ADJ; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, const char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+const char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( unsigned int size )
+#else
+static void *yy_flex_alloc( size )
+unsigned int size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, unsigned int size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+unsigned int size;
+#endif
+ {
+ return (void *) realloc( ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
diff --git a/usr.bin/lex/flexdef.h b/usr.bin/lex/flexdef.h
new file mode 100644
index 0000000..bf5c10d
--- /dev/null
+++ b/usr.bin/lex/flexdef.h
@@ -0,0 +1,902 @@
+/* flexdef - definitions file for flex */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* @(#) $Header: /home/ncvs/src/usr.bin/lex/flexdef.h,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ (LBL) */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#if __STDC__
+#include <stdlib.h>
+#endif
+
+/* Always be prepared to generate an 8-bit scanner. */
+#define CSIZE 256
+#define Char unsigned char
+
+/* Size of input alphabet - should be size of ASCII set. */
+#ifndef DEFAULT_CSIZE
+#define DEFAULT_CSIZE 128
+#endif
+
+#ifndef PROTO
+#ifdef __STDC__
+#define PROTO(proto) proto
+#else
+#define PROTO(proto) ()
+#endif
+#endif
+
+#ifdef VMS
+#define unlink delete
+#define SHORT_FILE_NAMES
+#endif
+
+#ifdef MS_DOS
+#define SHORT_FILE_NAMES
+#endif
+
+
+/* Maximum line length we'll have to deal with. */
+#define MAXLINE 2048
+
+#ifndef MIN
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef ABS
+#define ABS(x) ((x) < 0 ? -(x) : (x))
+#endif
+
+
+/* ANSI C does not guarantee that isascii() is defined */
+#ifndef isascii
+#define isascii(c) ((c) <= 0177)
+#endif
+
+
+#define true 1
+#define false 0
+
+
+/* Special chk[] values marking the slots taking by end-of-buffer and action
+ * numbers.
+ */
+#define EOB_POSITION -1
+#define ACTION_POSITION -2
+
+/* Number of data items per line for -f output. */
+#define NUMDATAITEMS 10
+
+/* Number of lines of data in -f output before inserting a blank line for
+ * readability.
+ */
+#define NUMDATALINES 10
+
+/* Transition_struct_out() definitions. */
+#define TRANS_STRUCT_PRINT_LENGTH 15
+
+/* Returns true if an nfa state has an epsilon out-transition slot
+ * that can be used. This definition is currently not used.
+ */
+#define FREE_EPSILON(state) \
+ (transchar[state] == SYM_EPSILON && \
+ trans2[state] == NO_TRANSITION && \
+ finalst[state] != state)
+
+/* Returns true if an nfa state has an epsilon out-transition character
+ * and both slots are free
+ */
+#define SUPER_FREE_EPSILON(state) \
+ (transchar[state] == SYM_EPSILON && \
+ trans1[state] == NO_TRANSITION) \
+
+/* Maximum number of NFA states that can comprise a DFA state. It's real
+ * big because if there's a lot of rules, the initial state will have a
+ * huge epsilon closure.
+ */
+#define INITIAL_MAX_DFA_SIZE 750
+#define MAX_DFA_SIZE_INCREMENT 750
+
+
+/* A note on the following masks. They are used to mark accepting numbers
+ * as being special. As such, they implicitly limit the number of accepting
+ * numbers (i.e., rules) because if there are too many rules the rule numbers
+ * will overload the mask bits. Fortunately, this limit is \large/ (0x2000 ==
+ * 8192) so unlikely to actually cause any problems. A check is made in
+ * new_rule() to ensure that this limit is not reached.
+ */
+
+/* Mask to mark a trailing context accepting number. */
+#define YY_TRAILING_MASK 0x2000
+
+/* Mask to mark the accepting number of the "head" of a trailing context
+ * rule.
+ */
+#define YY_TRAILING_HEAD_MASK 0x4000
+
+/* Maximum number of rules, as outlined in the above note. */
+#define MAX_RULE (YY_TRAILING_MASK - 1)
+
+
+/* NIL must be 0. If not, its special meaning when making equivalence classes
+ * (it marks the representative of a given e.c.) will be unidentifiable.
+ */
+#define NIL 0
+
+#define JAM -1 /* to mark a missing DFA transition */
+#define NO_TRANSITION NIL
+#define UNIQUE -1 /* marks a symbol as an e.c. representative */
+#define INFINITY -1 /* for x{5,} constructions */
+
+#define INITIAL_MAX_CCLS 100 /* max number of unique character classes */
+#define MAX_CCLS_INCREMENT 100
+
+/* Size of table holding members of character classes. */
+#define INITIAL_MAX_CCL_TBL_SIZE 500
+#define MAX_CCL_TBL_SIZE_INCREMENT 250
+
+#define INITIAL_MAX_RULES 100 /* default maximum number of rules */
+#define MAX_RULES_INCREMENT 100
+
+#define INITIAL_MNS 2000 /* default maximum number of nfa states */
+#define MNS_INCREMENT 1000 /* amount to bump above by if it's not enough */
+
+#define INITIAL_MAX_DFAS 1000 /* default maximum number of dfa states */
+#define MAX_DFAS_INCREMENT 1000
+
+#define JAMSTATE -32766 /* marks a reference to the state that always jams */
+
+/* Enough so that if it's subtracted from an NFA state number, the result
+ * is guaranteed to be negative.
+ */
+#define MARKER_DIFFERENCE 32000
+#define MAXIMUM_MNS 31999
+
+/* Maximum number of nxt/chk pairs for non-templates. */
+#define INITIAL_MAX_XPAIRS 2000
+#define MAX_XPAIRS_INCREMENT 2000
+
+/* Maximum number of nxt/chk pairs needed for templates. */
+#define INITIAL_MAX_TEMPLATE_XPAIRS 2500
+#define MAX_TEMPLATE_XPAIRS_INCREMENT 2500
+
+#define SYM_EPSILON (CSIZE + 1) /* to mark transitions on the symbol epsilon */
+
+#define INITIAL_MAX_SCS 40 /* maximum number of start conditions */
+#define MAX_SCS_INCREMENT 40 /* amount to bump by if it's not enough */
+
+#define ONE_STACK_SIZE 500 /* stack of states with only one out-transition */
+#define SAME_TRANS -1 /* transition is the same as "default" entry for state */
+
+/* The following percentages are used to tune table compression:
+
+ * The percentage the number of out-transitions a state must be of the
+ * number of equivalence classes in order to be considered for table
+ * compaction by using protos.
+ */
+#define PROTO_SIZE_PERCENTAGE 15
+
+/* The percentage the number of homogeneous out-transitions of a state
+ * must be of the number of total out-transitions of the state in order
+ * that the state's transition table is first compared with a potential
+ * template of the most common out-transition instead of with the first
+ * proto in the proto queue.
+ */
+#define CHECK_COM_PERCENTAGE 50
+
+/* The percentage the number of differences between a state's transition
+ * table and the proto it was first compared with must be of the total
+ * number of out-transitions of the state in order to keep the first
+ * proto as a good match and not search any further.
+ */
+#define FIRST_MATCH_DIFF_PERCENTAGE 10
+
+/* The percentage the number of differences between a state's transition
+ * table and the most similar proto must be of the state's total number
+ * of out-transitions to use the proto as an acceptable close match.
+ */
+#define ACCEPTABLE_DIFF_PERCENTAGE 50
+
+/* The percentage the number of homogeneous out-transitions of a state
+ * must be of the number of total out-transitions of the state in order
+ * to consider making a template from the state.
+ */
+#define TEMPLATE_SAME_PERCENTAGE 60
+
+/* The percentage the number of differences between a state's transition
+ * table and the most similar proto must be of the state's total number
+ * of out-transitions to create a new proto from the state.
+ */
+#define NEW_PROTO_DIFF_PERCENTAGE 20
+
+/* The percentage the total number of out-transitions of a state must be
+ * of the number of equivalence classes in order to consider trying to
+ * fit the transition table into "holes" inside the nxt/chk table.
+ */
+#define INTERIOR_FIT_PERCENTAGE 15
+
+/* Size of region set aside to cache the complete transition table of
+ * protos on the proto queue to enable quick comparisons.
+ */
+#define PROT_SAVE_SIZE 2000
+
+#define MSP 50 /* maximum number of saved protos (protos on the proto queue) */
+
+/* Maximum number of out-transitions a state can have that we'll rummage
+ * around through the interior of the internal fast table looking for a
+ * spot for it.
+ */
+#define MAX_XTIONS_FULL_INTERIOR_FIT 4
+
+/* Maximum number of rules which will be reported as being associated
+ * with a DFA state.
+ */
+#define MAX_ASSOC_RULES 100
+
+/* Number that, if used to subscript an array, has a good chance of producing
+ * an error; should be small enough to fit into a short.
+ */
+#define BAD_SUBSCRIPT -32767
+
+/* Absolute value of largest number that can be stored in a short, with a
+ * bit of slop thrown in for general paranoia.
+ */
+#define MAX_SHORT 32700
+
+
+/* Declarations for global variables. */
+
+/* Variables for symbol tables:
+ * sctbl - start-condition symbol table
+ * ndtbl - name-definition symbol table
+ * ccltab - character class text symbol table
+ */
+
+struct hash_entry
+ {
+ struct hash_entry *prev, *next;
+ char *name;
+ char *str_val;
+ int int_val;
+ } ;
+
+typedef struct hash_entry **hash_table;
+
+#define NAME_TABLE_HASH_SIZE 101
+#define START_COND_HASH_SIZE 101
+#define CCL_HASH_SIZE 101
+
+extern struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE];
+extern struct hash_entry *sctbl[START_COND_HASH_SIZE];
+extern struct hash_entry *ccltab[CCL_HASH_SIZE];
+
+
+/* Variables for flags:
+ * printstats - if true (-v), dump statistics
+ * syntaxerror - true if a syntax error has been found
+ * eofseen - true if we've seen an eof in the input file
+ * ddebug - if true (-d), make a "debug" scanner
+ * trace - if true (-T), trace processing
+ * nowarn - if true (-w), do not generate warnings
+ * spprdflt - if true (-s), suppress the default rule
+ * interactive - if true (-I), generate an interactive scanner
+ * caseins - if true (-i), generate a case-insensitive scanner
+ * lex_compat - if true (-l), maximize compatibility with AT&T lex
+ * useecs - if true (-Ce flag), use equivalence classes
+ * fulltbl - if true (-Cf flag), don't compress the DFA state table
+ * usemecs - if true (-Cm flag), use meta-equivalence classes
+ * fullspd - if true (-F flag), use Jacobson method of table representation
+ * gen_line_dirs - if true (i.e., no -L flag), generate #line directives
+ * performance_report - if > 0 (i.e., -p flag), generate a report relating
+ * to scanner performance; if > 1 (-p -p), report on minor performance
+ * problems, too
+ * backing_up_report - if true (i.e., -b flag), generate "lex.backup" file
+ * listing backing-up states
+ * C_plus_plus - if true (i.e., -+ flag), generate a C++ scanner class;
+ * otherwise, a standard C scanner
+ * long_align - if true (-Ca flag), favor long-word alignment.
+ * use_read - if true (-f, -F, or -Cr) then use read() for scanner input;
+ * otherwise, use fread().
+ * yytext_is_array - if true (i.e., %array directive), then declare
+ * yytext as a array instead of a character pointer. Nice and inefficient.
+ * csize - size of character set for the scanner we're generating;
+ * 128 for 7-bit chars and 256 for 8-bit
+ * yymore_used - if true, yymore() is used in input rules
+ * reject - if true, generate back-up tables for REJECT macro
+ * real_reject - if true, scanner really uses REJECT (as opposed to just
+ * having "reject" set for variable trailing context)
+ * continued_action - true if this rule's action is to "fall through" to
+ * the next rule's action (i.e., the '|' action)
+ * yymore_really_used - has a REALLY_xxx value indicating whether a
+ * %used or %notused was used with yymore()
+ * reject_really_used - same for REJECT
+ */
+
+extern int printstats, syntaxerror, eofseen, ddebug, trace, nowarn, spprdflt;
+extern int interactive, caseins, lex_compat, useecs, fulltbl, usemecs;
+extern int fullspd, gen_line_dirs, performance_report, backing_up_report;
+extern int C_plus_plus, long_align, use_read, yytext_is_array, csize;
+extern int yymore_used, reject, real_reject, continued_action;
+
+#define REALLY_NOT_DETERMINED 0
+#define REALLY_USED 1
+#define REALLY_NOT_USED 2
+extern int yymore_really_used, reject_really_used;
+
+
+/* Variables used in the flex input routines:
+ * datapos - characters on current output line
+ * dataline - number of contiguous lines of data in current data
+ * statement. Used to generate readable -f output
+ * linenum - current input line number
+ * skelfile - the skeleton file
+ * skel - compiled-in skeleton array
+ * skel_ind - index into "skel" array, if skelfile is nil
+ * yyin - input file
+ * backing_up_file - file to summarize backing-up states to
+ * infilename - name of input file
+ * input_files - array holding names of input files
+ * num_input_files - size of input_files array
+ * program_name - name with which program was invoked
+ *
+ * action_array - array to hold the rule actions
+ * action_size - size of action_array
+ * defs1_offset - index where the user's section 1 definitions start
+ * in action_array
+ * prolog_offset - index where the prolog starts in action_array
+ * action_offset - index where the non-prolog starts in action_array
+ * action_index - index where the next action should go, with respect
+ * to "action_array"
+ */
+
+extern int datapos, dataline, linenum;
+extern FILE *skelfile, *yyin, *backing_up_file;
+extern char *skel[];
+extern int skel_ind;
+extern char *infilename;
+extern char **input_files;
+extern int num_input_files;
+extern char *program_name;
+
+extern char *action_array;
+extern int action_size;
+extern int defs1_offset, prolog_offset, action_offset, action_index;
+
+
+/* Variables for stack of states having only one out-transition:
+ * onestate - state number
+ * onesym - transition symbol
+ * onenext - target state
+ * onedef - default base entry
+ * onesp - stack pointer
+ */
+
+extern int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE];
+extern int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp;
+
+
+/* Variables for nfa machine data:
+ * current_mns - current maximum on number of NFA states
+ * num_rules - number of the last accepting state; also is number of
+ * rules created so far
+ * num_eof_rules - number of <<EOF>> rules
+ * default_rule - number of the default rule
+ * current_max_rules - current maximum number of rules
+ * lastnfa - last nfa state number created
+ * firstst - physically the first state of a fragment
+ * lastst - last physical state of fragment
+ * finalst - last logical state of fragment
+ * transchar - transition character
+ * trans1 - transition state
+ * trans2 - 2nd transition state for epsilons
+ * accptnum - accepting number
+ * assoc_rule - rule associated with this NFA state (or 0 if none)
+ * state_type - a STATE_xxx type identifying whether the state is part
+ * of a normal rule, the leading state in a trailing context
+ * rule (i.e., the state which marks the transition from
+ * recognizing the text-to-be-matched to the beginning of
+ * the trailing context), or a subsequent state in a trailing
+ * context rule
+ * rule_type - a RULE_xxx type identifying whether this a ho-hum
+ * normal rule or one which has variable head & trailing
+ * context
+ * rule_linenum - line number associated with rule
+ * rule_useful - true if we've determined that the rule can be matched
+ */
+
+extern int current_mns, num_rules, num_eof_rules, default_rule;
+extern int current_max_rules, lastnfa;
+extern int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2;
+extern int *accptnum, *assoc_rule, *state_type;
+extern int *rule_type, *rule_linenum, *rule_useful;
+
+/* Different types of states; values are useful as masks, as well, for
+ * routines like check_trailing_context().
+ */
+#define STATE_NORMAL 0x1
+#define STATE_TRAILING_CONTEXT 0x2
+
+/* Global holding current type of state we're making. */
+
+extern int current_state_type;
+
+/* Different types of rules. */
+#define RULE_NORMAL 0
+#define RULE_VARIABLE 1
+
+/* True if the input rules include a rule with both variable-length head
+ * and trailing context, false otherwise.
+ */
+extern int variable_trailing_context_rules;
+
+
+/* Variables for protos:
+ * numtemps - number of templates created
+ * numprots - number of protos created
+ * protprev - backlink to a more-recently used proto
+ * protnext - forward link to a less-recently used proto
+ * prottbl - base/def table entry for proto
+ * protcomst - common state of proto
+ * firstprot - number of the most recently used proto
+ * lastprot - number of the least recently used proto
+ * protsave contains the entire state array for protos
+ */
+
+extern int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP];
+extern int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE];
+
+
+/* Variables for managing equivalence classes:
+ * numecs - number of equivalence classes
+ * nextecm - forward link of Equivalence Class members
+ * ecgroup - class number or backward link of EC members
+ * nummecs - number of meta-equivalence classes (used to compress
+ * templates)
+ * tecfwd - forward link of meta-equivalence classes members
+ * tecbck - backward link of MEC's
+ */
+
+/* Reserve enough room in the equivalence class arrays so that we
+ * can use the CSIZE'th element to hold equivalence class information
+ * for the NUL character. Later we'll move this information into
+ * the 0th element.
+ */
+extern int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs;
+
+/* Meta-equivalence classes are indexed starting at 1, so it's possible
+ * that they will require positions from 1 .. CSIZE, i.e., CSIZE + 1
+ * slots total (since the arrays are 0-based). nextecm[] and ecgroup[]
+ * don't require the extra position since they're indexed from 1 .. CSIZE - 1.
+ */
+extern int tecfwd[CSIZE + 1], tecbck[CSIZE + 1];
+
+
+/* Variables for start conditions:
+ * lastsc - last start condition created
+ * current_max_scs - current limit on number of start conditions
+ * scset - set of rules active in start condition
+ * scbol - set of rules active only at the beginning of line in a s.c.
+ * scxclu - true if start condition is exclusive
+ * sceof - true if start condition has EOF rule
+ * scname - start condition name
+ * actvsc - stack of active start conditions for the current rule;
+ * a negative entry means that the start condition is *not*
+ * active for the current rule. Start conditions may appear
+ * multiple times on the stack; the entry for it closest
+ * to the top of the stack (i.e., actvsc[actvp]) is the
+ * one to use. Others are present from "<sc>{" scoping
+ * constructs.
+ */
+
+extern int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc;
+extern char **scname;
+
+
+/* Variables for dfa machine data:
+ * current_max_dfa_size - current maximum number of NFA states in DFA
+ * current_max_xpairs - current maximum number of non-template xtion pairs
+ * current_max_template_xpairs - current maximum number of template pairs
+ * current_max_dfas - current maximum number DFA states
+ * lastdfa - last dfa state number created
+ * nxt - state to enter upon reading character
+ * chk - check value to see if "nxt" applies
+ * tnxt - internal nxt table for templates
+ * base - offset into "nxt" for given state
+ * def - where to go if "chk" disallows "nxt" entry
+ * nultrans - NUL transition for each state
+ * NUL_ec - equivalence class of the NUL character
+ * tblend - last "nxt/chk" table entry being used
+ * firstfree - first empty entry in "nxt/chk" table
+ * dss - nfa state set for each dfa
+ * dfasiz - size of nfa state set for each dfa
+ * dfaacc - accepting set for each dfa state (if using REJECT), or accepting
+ * number, if not
+ * accsiz - size of accepting set for each dfa state
+ * dhash - dfa state hash value
+ * numas - number of DFA accepting states created; note that this
+ * is not necessarily the same value as num_rules, which is the analogous
+ * value for the NFA
+ * numsnpairs - number of state/nextstate transition pairs
+ * jambase - position in base/def where the default jam table starts
+ * jamstate - state number corresponding to "jam" state
+ * end_of_buffer_state - end-of-buffer dfa state number
+ */
+
+extern int current_max_dfa_size, current_max_xpairs;
+extern int current_max_template_xpairs, current_max_dfas;
+extern int lastdfa, *nxt, *chk, *tnxt;
+extern int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz;
+extern union dfaacc_union
+ {
+ int *dfaacc_set;
+ int dfaacc_state;
+ } *dfaacc;
+extern int *accsiz, *dhash, numas;
+extern int numsnpairs, jambase, jamstate;
+extern int end_of_buffer_state;
+
+/* Variables for ccl information:
+ * lastccl - ccl index of the last created ccl
+ * current_maxccls - current limit on the maximum number of unique ccl's
+ * cclmap - maps a ccl index to its set pointer
+ * ccllen - gives the length of a ccl
+ * cclng - true for a given ccl if the ccl is negated
+ * cclreuse - counts how many times a ccl is re-used
+ * current_max_ccl_tbl_size - current limit on number of characters needed
+ * to represent the unique ccl's
+ * ccltbl - holds the characters in each ccl - indexed by cclmap
+ */
+
+extern int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse;
+extern int current_max_ccl_tbl_size;
+extern Char *ccltbl;
+
+
+/* Variables for miscellaneous information:
+ * nmstr - last NAME scanned by the scanner
+ * sectnum - section number currently being parsed
+ * nummt - number of empty nxt/chk table entries
+ * hshcol - number of hash collisions detected by snstods
+ * dfaeql - number of times a newly created dfa was equal to an old one
+ * numeps - number of epsilon NFA states created
+ * eps2 - number of epsilon states which have 2 out-transitions
+ * num_reallocs - number of times it was necessary to realloc() a group
+ * of arrays
+ * tmpuses - number of DFA states that chain to templates
+ * totnst - total number of NFA states used to make DFA states
+ * peakpairs - peak number of transition pairs we had to store internally
+ * numuniq - number of unique transitions
+ * numdup - number of duplicate transitions
+ * hshsave - number of hash collisions saved by checking number of states
+ * num_backing_up - number of DFA states requiring backing up
+ * bol_needed - whether scanner needs beginning-of-line recognition
+ */
+
+extern char nmstr[MAXLINE];
+extern int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs;
+extern int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave;
+extern int num_backing_up, bol_needed;
+
+void *allocate_array PROTO((int, int));
+void *reallocate_array PROTO((void*, int, int));
+
+void *flex_alloc PROTO((unsigned int));
+void *flex_realloc PROTO((void*, unsigned int));
+void flex_free PROTO((void*));
+
+#define allocate_integer_array(size) \
+ (int *) allocate_array( size, sizeof( int ) )
+
+#define reallocate_integer_array(array,size) \
+ (int *) reallocate_array( (void *) array, size, sizeof( int ) )
+
+#define allocate_int_ptr_array(size) \
+ (int **) allocate_array( size, sizeof( int * ) )
+
+#define allocate_char_ptr_array(size) \
+ (char **) allocate_array( size, sizeof( char * ) )
+
+#define allocate_dfaacc_union(size) \
+ (union dfaacc_union *) \
+ allocate_array( size, sizeof( union dfaacc_union ) )
+
+#define reallocate_int_ptr_array(array,size) \
+ (int **) reallocate_array( (void *) array, size, sizeof( int * ) )
+
+#define reallocate_char_ptr_array(array,size) \
+ (char **) reallocate_array( (void *) array, size, sizeof( char * ) )
+
+#define reallocate_dfaacc_union(array, size) \
+ (union dfaacc_union *) \
+ reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) )
+
+#define allocate_character_array(size) \
+ (char *) allocate_array( size, sizeof( char ) )
+
+#define reallocate_character_array(array,size) \
+ (char *) reallocate_array( (void *) array, size, sizeof( char ) )
+
+#define allocate_Character_array(size) \
+ (Char *) allocate_array( size, sizeof( Char ) )
+
+#define reallocate_Character_array(array,size) \
+ (Char *) reallocate_array( (void *) array, size, sizeof( Char ) )
+
+
+/* Used to communicate between scanner and parser. The type should really
+ * be YYSTYPE, but we can't easily get our hands on it.
+ */
+extern int yylval;
+
+
+/* External functions that are cross-referenced among the flex source files. */
+
+
+/* from file ccl.c */
+
+extern void ccladd PROTO((int, int)); /* add a single character to a ccl */
+extern int cclinit PROTO((void)); /* make an empty ccl */
+extern void cclnegate PROTO((int)); /* negate a ccl */
+
+/* List the members of a set of characters in CCL form. */
+extern void list_character_set PROTO((FILE*, int[]));
+
+
+/* from file dfa.c */
+
+/* Increase the maximum number of dfas. */
+extern void increase_max_dfas PROTO((void));
+
+extern void ntod PROTO((void)); /* convert a ndfa to a dfa */
+
+
+/* from file ecs.c */
+
+/* Convert character classes to set of equivalence classes. */
+extern void ccl2ecl PROTO((void));
+
+/* Associate equivalence class numbers with class members. */
+extern int cre8ecs PROTO((int[], int[], int));
+
+/* Update equivalence classes based on character class transitions. */
+extern void mkeccl PROTO((Char[], int, int[], int[], int, int));
+
+/* Create equivalence class for single character. */
+extern void mkechar PROTO((int, int[], int[]));
+
+
+/* from file gen.c */
+
+extern void make_tables PROTO((void)); /* generate transition tables */
+
+
+/* from file main.c */
+
+extern void flexend PROTO((int));
+extern void usage PROTO((void));
+
+
+/* from file misc.c */
+
+/* Add the given text to the stored actions. */
+extern void add_action PROTO(( char *new_text ));
+
+/* True if a string is all lower case. */
+extern int all_lower PROTO((register char *));
+
+/* True if a string is all upper case. */
+extern int all_upper PROTO((register char *));
+
+/* Bubble sort an integer array. */
+extern void bubble PROTO((int [], int));
+
+/* Check a character to make sure it's in the expected range. */
+extern void check_char PROTO((int c));
+
+/* Shell sort a character array. */
+extern void cshell PROTO((Char [], int, int));
+
+/* Finish up a block of data declarations. */
+extern void dataend PROTO((void));
+
+/* Report an error message and terminate. */
+extern void flexerror PROTO((char[]));
+
+/* Report a fatal error message and terminate. */
+extern void flexfatal PROTO((char[]));
+
+/* Report an error message formatted with one integer argument. */
+extern void lerrif PROTO((char[], int));
+
+/* Report an error message formatted with one string argument. */
+extern void lerrsf PROTO((char[], char[]));
+
+/* Spit out a "# line" statement. */
+extern void line_directive_out PROTO((FILE*));
+
+/* Mark the current position in the action array as the end of the section 1
+ * user defs.
+ */
+extern void mark_defs1 PROTO((void));
+
+/* Mark the current position in the action array as the end of the prolog. */
+extern void mark_prolog PROTO((void));
+
+/* Generate a data statment for a two-dimensional array. */
+extern void mk2data PROTO((int));
+
+extern void mkdata PROTO((int)); /* generate a data statement */
+
+/* Return the integer represented by a string of digits. */
+extern int myctoi PROTO((char []));
+
+/* Return a printable version of the given character, which might be
+ * 8-bit.
+ */
+extern char *readable_form PROTO((int));
+
+/* Write out one section of the skeleton file. */
+extern void skelout PROTO((void));
+
+/* Output a yy_trans_info structure. */
+extern void transition_struct_out PROTO((int, int));
+
+/* Only needed when using certain broken versions of bison to build parse.c. */
+extern void *yy_flex_xmalloc PROTO(( int ));
+
+/* Set a region of memory to 0. */
+extern void zero_out PROTO((char *, int));
+
+
+/* from file nfa.c */
+
+/* Add an accepting state to a machine. */
+extern void add_accept PROTO((int, int));
+
+/* Make a given number of copies of a singleton machine. */
+extern int copysingl PROTO((int, int));
+
+/* Debugging routine to write out an nfa. */
+extern void dumpnfa PROTO((int));
+
+/* Finish up the processing for a rule. */
+extern void finish_rule PROTO((int, int, int, int));
+
+/* Connect two machines together. */
+extern int link_machines PROTO((int, int));
+
+/* Mark each "beginning" state in a machine as being a "normal" (i.e.,
+ * not trailing context associated) state.
+ */
+extern void mark_beginning_as_normal PROTO((register int));
+
+/* Make a machine that branches to two machines. */
+extern int mkbranch PROTO((int, int));
+
+extern int mkclos PROTO((int)); /* convert a machine into a closure */
+extern int mkopt PROTO((int)); /* make a machine optional */
+
+/* Make a machine that matches either one of two machines. */
+extern int mkor PROTO((int, int));
+
+/* Convert a machine into a positive closure. */
+extern int mkposcl PROTO((int));
+
+extern int mkrep PROTO((int, int, int)); /* make a replicated machine */
+
+/* Create a state with a transition on a given symbol. */
+extern int mkstate PROTO((int));
+
+extern void new_rule PROTO((void)); /* initialize for a new rule */
+
+
+/* from file parse.y */
+
+/* Write out a message formatted with one string, pinpointing its location. */
+extern void format_pinpoint_message PROTO((char[], char[]));
+
+/* Write out a message, pinpointing its location. */
+extern void pinpoint_message PROTO((char[]));
+
+/* Write out a warning, pinpointing it at the given line. */
+void line_warning PROTO(( char[], int ));
+
+/* Write out a message, pinpointing it at the given line. */
+void line_pinpoint PROTO(( char[], int ));
+
+/* Report a formatted syntax error. */
+extern void format_synerr PROTO((char [], char[]));
+extern void synerr PROTO((char [])); /* report a syntax error */
+extern void warn PROTO((char [])); /* report a warning */
+extern int yyparse PROTO((void)); /* the YACC parser */
+
+
+/* from file scan.l */
+
+/* The Flex-generated scanner for flex. */
+extern int flexscan PROTO((void));
+
+/* Open the given file (if NULL, stdin) for scanning. */
+extern void set_input_file PROTO((char*));
+
+/* Wrapup a file in the lexical analyzer. */
+extern int yywrap PROTO((void));
+
+
+/* from file sym.c */
+
+/* Save the text of a character class. */
+extern void cclinstal PROTO ((Char [], int));
+
+/* Lookup the number associated with character class. */
+extern int ccllookup PROTO((Char []));
+
+extern void ndinstal PROTO((char[], Char[])); /* install a name definition */
+/* Increase maximum number of SC's. */
+extern void scextend PROTO((void));
+extern void scinstal PROTO((char[], int)); /* make a start condition */
+
+/* Lookup the number associated with a start condition. */
+extern int sclookup PROTO((char[]));
+
+
+/* from file tblcmp.c */
+
+/* Build table entries for dfa state. */
+extern void bldtbl PROTO((int[], int, int, int, int));
+
+extern void cmptmps PROTO((void)); /* compress template table entries */
+extern void expand_nxt_chk PROTO((void)); /* increase nxt/chk arrays */
+extern void inittbl PROTO((void)); /* initialize transition tables */
+/* Make the default, "jam" table entries. */
+extern void mkdeftbl PROTO((void));
+
+/* Create table entries for a state (or state fragment) which has
+ * only one out-transition.
+ */
+extern void mk1tbl PROTO((int, int, int, int));
+
+/* Place a state into full speed transition table. */
+extern void place_state PROTO((int*, int, int));
+
+/* Save states with only one out-transition to be processed later. */
+extern void stack1 PROTO((int, int, int, int));
+
+
+/* from file yylex.c */
+
+extern int yylex PROTO((void));
diff --git a/usr.bin/lex/flexdoc.1 b/usr.bin/lex/flexdoc.1
new file mode 100644
index 0000000..b80d569
--- /dev/null
+++ b/usr.bin/lex/flexdoc.1
@@ -0,0 +1,3045 @@
+.TH FLEXDOC 1 "November 1993" "Version 2.4"
+.SH NAME
+flexdoc \- documentation for flex, fast lexical analyzer generator
+.SH SYNOPSIS
+.B flex
+.B [\-bcdfhilnpstvwBFILTV78+ \-C[aefFmr] \-Pprefix \-Sskeleton]
+.I [filename ...]
+.SH DESCRIPTION
+.I flex
+is a tool for generating
+.I scanners:
+programs which recognized lexical patterns in text.
+.I flex
+reads
+the given input files, or its standard input if no file names are given,
+for a description of a scanner to generate. The description is in
+the form of pairs
+of regular expressions and C code, called
+.I rules. flex
+generates as output a C source file,
+.B lex.yy.c,
+which defines a routine
+.B yylex().
+This file is compiled and linked with the
+.B \-lfl
+library to produce an executable. When the executable is run,
+it analyzes its input for occurrences
+of the regular expressions. Whenever it finds one, it executes
+the corresponding C code.
+.SH SOME SIMPLE EXAMPLES
+.PP
+First some simple examples to get the flavor of how one uses
+.I flex.
+The following
+.I flex
+input specifies a scanner which whenever it encounters the string
+"username" will replace it with the user's login name:
+.nf
+
+ %%
+ username printf( "%s", getlogin() );
+
+.fi
+By default, any text not matched by a
+.I flex
+scanner
+is copied to the output, so the net effect of this scanner is
+to copy its input file to its output with each occurrence
+of "username" expanded.
+In this input, there is just one rule. "username" is the
+.I pattern
+and the "printf" is the
+.I action.
+The "%%" marks the beginning of the rules.
+.PP
+Here's another simple example:
+.nf
+
+ int num_lines = 0, num_chars = 0;
+
+ %%
+ \\n ++num_lines; ++num_chars;
+ . ++num_chars;
+
+ %%
+ main()
+ {
+ yylex();
+ printf( "# of lines = %d, # of chars = %d\\n",
+ num_lines, num_chars );
+ }
+
+.fi
+This scanner counts the number of characters and the number
+of lines in its input (it produces no output other than the
+final report on the counts). The first line
+declares two globals, "num_lines" and "num_chars", which are accessible
+both inside
+.B yylex()
+and in the
+.B main()
+routine declared after the second "%%". There are two rules, one
+which matches a newline ("\\n") and increments both the line count and
+the character count, and one which matches any character other than
+a newline (indicated by the "." regular expression).
+.PP
+A somewhat more complicated example:
+.nf
+
+ /* scanner for a toy Pascal-like language */
+
+ %{
+ /* need this for the call to atof() below */
+ #include <math.h>
+ %}
+
+ DIGIT [0-9]
+ ID [a-z][a-z0-9]*
+
+ %%
+
+ {DIGIT}+ {
+ printf( "An integer: %s (%d)\\n", yytext,
+ atoi( yytext ) );
+ }
+
+ {DIGIT}+"."{DIGIT}* {
+ printf( "A float: %s (%g)\\n", yytext,
+ atof( yytext ) );
+ }
+
+ if|then|begin|end|procedure|function {
+ printf( "A keyword: %s\\n", yytext );
+ }
+
+ {ID} printf( "An identifier: %s\\n", yytext );
+
+ "+"|"-"|"*"|"/" printf( "An operator: %s\\n", yytext );
+
+ "{"[^}\\n]*"}" /* eat up one-line comments */
+
+ [ \\t\\n]+ /* eat up whitespace */
+
+ . printf( "Unrecognized character: %s\\n", yytext );
+
+ %%
+
+ main( argc, argv )
+ int argc;
+ char **argv;
+ {
+ ++argv, --argc; /* skip over program name */
+ if ( argc > 0 )
+ yyin = fopen( argv[0], "r" );
+ else
+ yyin = stdin;
+
+ yylex();
+ }
+
+.fi
+This is the beginnings of a simple scanner for a language like
+Pascal. It identifies different types of
+.I tokens
+and reports on what it has seen.
+.PP
+The details of this example will be explained in the following
+sections.
+.SH FORMAT OF THE INPUT FILE
+The
+.I flex
+input file consists of three sections, separated by a line with just
+.B %%
+in it:
+.nf
+
+ definitions
+ %%
+ rules
+ %%
+ user code
+
+.fi
+The
+.I definitions
+section contains declarations of simple
+.I name
+definitions to simplify the scanner specification, and declarations of
+.I start conditions,
+which are explained in a later section.
+.PP
+Name definitions have the form:
+.nf
+
+ name definition
+
+.fi
+The "name" is a word beginning with a letter or an underscore ('_')
+followed by zero or more letters, digits, '_', or '-' (dash).
+The definition is taken to begin at the first non-white-space character
+following the name and continuing to the end of the line.
+The definition can subsequently be referred to using "{name}", which
+will expand to "(definition)". For example,
+.nf
+
+ DIGIT [0-9]
+ ID [a-z][a-z0-9]*
+
+.fi
+defines "DIGIT" to be a regular expression which matches a
+single digit, and
+"ID" to be a regular expression which matches a letter
+followed by zero-or-more letters-or-digits.
+A subsequent reference to
+.nf
+
+ {DIGIT}+"."{DIGIT}*
+
+.fi
+is identical to
+.nf
+
+ ([0-9])+"."([0-9])*
+
+.fi
+and matches one-or-more digits followed by a '.' followed
+by zero-or-more digits.
+.PP
+The
+.I rules
+section of the
+.I flex
+input contains a series of rules of the form:
+.nf
+
+ pattern action
+
+.fi
+where the pattern must be unindented and the action must begin
+on the same line.
+.PP
+See below for a further description of patterns and actions.
+.PP
+Finally, the user code section is simply copied to
+.B lex.yy.c
+verbatim.
+It is used for companion routines which call or are called
+by the scanner. The presence of this section is optional;
+if it is missing, the second
+.B %%
+in the input file may be skipped, too.
+.PP
+In the definitions and rules sections, any
+.I indented
+text or text enclosed in
+.B %{
+and
+.B %}
+is copied verbatim to the output (with the %{}'s removed).
+The %{}'s must appear unindented on lines by themselves.
+.PP
+In the rules section,
+any indented or %{} text appearing before the
+first rule may be used to declare variables
+which are local to the scanning routine and (after the declarations)
+code which is to be executed whenever the scanning routine is entered.
+Other indented or %{} text in the rule section is still copied to the output,
+but its meaning is not well-defined and it may well cause compile-time
+errors (this feature is present for
+.I POSIX
+compliance; see below for other such features).
+.PP
+In the definitions section (but not in the rules section),
+an unindented comment (i.e., a line
+beginning with "/*") is also copied verbatim to the output up
+to the next "*/".
+.SH PATTERNS
+The patterns in the input are written using an extended set of regular
+expressions. These are:
+.nf
+
+ x match the character 'x'
+ . any character except newline
+ [xyz] a "character class"; in this case, the pattern
+ matches either an 'x', a 'y', or a 'z'
+ [abj-oZ] a "character class" with a range in it; matches
+ an 'a', a 'b', any letter from 'j' through 'o',
+ or a 'Z'
+ [^A-Z] a "negated character class", i.e., any character
+ but those in the class. In this case, any
+ character EXCEPT an uppercase letter.
+ [^A-Z\\n] any character EXCEPT an uppercase letter or
+ a newline
+ r* zero or more r's, where r is any regular expression
+ r+ one or more r's
+ r? zero or one r's (that is, "an optional r")
+ r{2,5} anywhere from two to five r's
+ r{2,} two or more r's
+ r{4} exactly 4 r's
+ {name} the expansion of the "name" definition
+ (see above)
+ "[xyz]\\"foo"
+ the literal string: [xyz]"foo
+ \\X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+ then the ANSI-C interpretation of \\x.
+ Otherwise, a literal 'X' (used to escape
+ operators such as '*')
+ \\123 the character with octal value 123
+ \\x2a the character with hexadecimal value 2a
+ (r) match an r; parentheses are used to override
+ precedence (see below)
+
+
+ rs the regular expression r followed by the
+ regular expression s; called "concatenation"
+
+
+ r|s either an r or an s
+
+
+ r/s an r but only if it is followed by an s. The
+ s is not part of the matched text. This type
+ of pattern is called as "trailing context".
+ ^r an r, but only at the beginning of a line
+ r$ an r, but only at the end of a line. Equivalent
+ to "r/\\n".
+
+
+ <s>r an r, but only in start condition s (see
+ below for discussion of start conditions)
+ <s1,s2,s3>r
+ same, but in any of start conditions s1,
+ s2, or s3
+ <*>r an r in any start condition, even an exclusive one.
+
+
+ <<EOF>> an end-of-file
+ <s1,s2><<EOF>>
+ an end-of-file when in start condition s1 or s2
+
+.fi
+Note that inside of a character class, all regular expression operators
+lose their special meaning except escape ('\\') and the character class
+operators, '-', ']', and, at the beginning of the class, '^'.
+.PP
+The regular expressions listed above are grouped according to
+precedence, from highest precedence at the top to lowest at the bottom.
+Those grouped together have equal precedence. For example,
+.nf
+
+ foo|bar*
+
+.fi
+is the same as
+.nf
+
+ (foo)|(ba(r*))
+
+.fi
+since the '*' operator has higher precedence than concatenation,
+and concatenation higher than alternation ('|'). This pattern
+therefore matches
+.I either
+the string "foo"
+.I or
+the string "ba" followed by zero-or-more r's.
+To match "foo" or zero-or-more "bar"'s, use:
+.nf
+
+ foo|(bar)*
+
+.fi
+and to match zero-or-more "foo"'s-or-"bar"'s:
+.nf
+
+ (foo|bar)*
+
+.fi
+.PP
+Some notes on patterns:
+.IP -
+A negated character class such as the example "[^A-Z]"
+above
+.I will match a newline
+unless "\\n" (or an equivalent escape sequence) is one of the
+characters explicitly present in the negated character class
+(e.g., "[^A-Z\\n]"). This is unlike how many other regular
+expression tools treat negated character classes, but unfortunately
+the inconsistency is historically entrenched.
+Matching newlines means that a pattern like [^"]* can match the entire
+input unless there's another quote in the input.
+.IP -
+A rule can have at most one instance of trailing context (the '/' operator
+or the '$' operator). The start condition, '^', and "<<EOF>>" patterns
+can only occur at the beginning of a pattern, and, as well as with '/' and '$',
+cannot be grouped inside parentheses. A '^' which does not occur at
+the beginning of a rule or a '$' which does not occur at the end of
+a rule loses its special properties and is treated as a normal character.
+.IP
+The following are illegal:
+.nf
+
+ foo/bar$
+ <sc1>foo<sc2>bar
+
+.fi
+Note that the first of these, can be written "foo/bar\\n".
+.IP
+The following will result in '$' or '^' being treated as a normal character:
+.nf
+
+ foo|(bar$)
+ foo|^bar
+
+.fi
+If what's wanted is a "foo" or a bar-followed-by-a-newline, the following
+could be used (the special '|' action is explained below):
+.nf
+
+ foo |
+ bar$ /* action goes here */
+
+.fi
+A similar trick will work for matching a foo or a
+bar-at-the-beginning-of-a-line.
+.SH HOW THE INPUT IS MATCHED
+When the generated scanner is run, it analyzes its input looking
+for strings which match any of its patterns. If it finds more than
+one match, it takes the one matching the most text (for trailing
+context rules, this includes the length of the trailing part, even
+though it will then be returned to the input). If it finds two
+or more matches of the same length, the
+rule listed first in the
+.I flex
+input file is chosen.
+.PP
+Once the match is determined, the text corresponding to the match
+(called the
+.I token)
+is made available in the global character pointer
+.B yytext,
+and its length in the global integer
+.B yyleng.
+The
+.I action
+corresponding to the matched pattern is then executed (a more
+detailed description of actions follows), and then the remaining
+input is scanned for another match.
+.PP
+If no match is found, then the
+.I default rule
+is executed: the next character in the input is considered matched and
+copied to the standard output. Thus, the simplest legal
+.I flex
+input is:
+.nf
+
+ %%
+
+.fi
+which generates a scanner that simply copies its input (one character
+at a time) to its output.
+.PP
+Note that
+.B yytext
+can be defined in two different ways: either as a character
+.I pointer
+or as a character
+.I array.
+You can control which definition
+.I flex
+uses by including one of the special directives
+.B %pointer
+or
+.B %array
+in the first (definitions) section of your flex input. The default is
+.B %pointer,
+unless you use the
+.B -l
+lex compatibility option, in which case
+.B yytext
+will be an array.
+The advantage of using
+.B %pointer
+is substantially faster scanning and no buffer overflow when matching
+very large tokens (unless you run out of dynamic memory). The disadvantage
+is that you are restricted in how your actions can modify
+.B yytext
+(see the next section), and calls to the
+.B input()
+and
+.B unput()
+functions destroy the present contents of
+.B yytext,
+which can be a considerable porting headache when moving between different
+.I lex
+versions.
+.PP
+The advantage of
+.B %array
+is that you can then modify
+.B yytext
+to your heart's content, and calls to
+.B input()
+and
+.B unput()
+do not destroy
+.B yytext
+(see below). Furthermore, existing
+.I lex
+programs sometimes access
+.B yytext
+externally using declarations of the form:
+.nf
+ extern char yytext[];
+.fi
+This definition is erroneous when used with
+.B %pointer,
+but correct for
+.B %array.
+.PP
+.B %array
+defines
+.B yytext
+to be an array of
+.B YYLMAX
+characters, which defaults to a fairly large value. You can change
+the size by simply #define'ing
+.B YYLMAX
+to a different value in the first section of your
+.I flex
+input. As mentioned above, with
+.B %pointer
+yytext grows dynamically to accomodate large tokens. While this means your
+.B %pointer
+scanner can accomodate very large tokens (such as matching entire blocks
+of comments), bear in mind that each time the scanner must resize
+.B yytext
+it also must rescan the entire token from the beginning, so matching such
+tokens can prove slow.
+.B yytext
+presently does
+.I not
+dynamically grow if a call to
+.B unput()
+results in too much text being pushed back; instead, a run-time error results.
+.PP
+Also note that you cannot use
+.B %array
+with C++ scanner classes
+(the
+.B \-+
+option; see below).
+.SH ACTIONS
+Each pattern in a rule has a corresponding action, which can be any
+arbitrary C statement. The pattern ends at the first non-escaped
+whitespace character; the remainder of the line is its action. If the
+action is empty, then when the pattern is matched the input token
+is simply discarded. For example, here is the specification for a program
+which deletes all occurrences of "zap me" from its input:
+.nf
+
+ %%
+ "zap me"
+
+.fi
+(It will copy all other characters in the input to the output since
+they will be matched by the default rule.)
+.PP
+Here is a program which compresses multiple blanks and tabs down to
+a single blank, and throws away whitespace found at the end of a line:
+.nf
+
+ %%
+ [ \\t]+ putchar( ' ' );
+ [ \\t]+$ /* ignore this token */
+
+.fi
+.PP
+If the action contains a '{', then the action spans till the balancing '}'
+is found, and the action may cross multiple lines.
+.I flex
+knows about C strings and comments and won't be fooled by braces found
+within them, but also allows actions to begin with
+.B %{
+and will consider the action to be all the text up to the next
+.B %}
+(regardless of ordinary braces inside the action).
+.PP
+An action consisting solely of a vertical bar ('|') means "same as
+the action for the next rule." See below for an illustration.
+.PP
+Actions can include arbitrary C code, including
+.B return
+statements to return a value to whatever routine called
+.B yylex().
+Each time
+.B yylex()
+is called it continues processing tokens from where it last left
+off until it either reaches
+the end of the file or executes a return.
+.PP
+Actions are free to modify
+.B yytext
+except for lengthening it (adding
+characters to its end--these will overwrite later characters in the
+input stream). Modifying the final character of yytext may alter
+whether when scanning resumes rules anchored with '^' are active.
+Specifically, changing the final character of yytext to a newline will
+activate such rules on the next scan, and changing it to anything else
+will deactivate the rules. Users should not rely on this behavior being
+present in future releases. Finally, note that none of this paragraph
+applies when using
+.B %array
+(see above).
+.PP
+Actions are free to modify
+.B yyleng
+except they should not do so if the action also includes use of
+.B yymore()
+(see below).
+.PP
+There are a number of special directives which can be included within
+an action:
+.IP -
+.B ECHO
+copies yytext to the scanner's output.
+.IP -
+.B BEGIN
+followed by the name of a start condition places the scanner in the
+corresponding start condition (see below).
+.IP -
+.B REJECT
+directs the scanner to proceed on to the "second best" rule which matched the
+input (or a prefix of the input). The rule is chosen as described
+above in "How the Input is Matched", and
+.B yytext
+and
+.B yyleng
+set up appropriately.
+It may either be one which matched as much text
+as the originally chosen rule but came later in the
+.I flex
+input file, or one which matched less text.
+For example, the following will both count the
+words in the input and call the routine special() whenever "frob" is seen:
+.nf
+
+ int word_count = 0;
+ %%
+
+ frob special(); REJECT;
+ [^ \\t\\n]+ ++word_count;
+
+.fi
+Without the
+.B REJECT,
+any "frob"'s in the input would not be counted as words, since the
+scanner normally executes only one action per token.
+Multiple
+.B REJECT's
+are allowed, each one finding the next best choice to the currently
+active rule. For example, when the following scanner scans the token
+"abcd", it will write "abcdabcaba" to the output:
+.nf
+
+ %%
+ a |
+ ab |
+ abc |
+ abcd ECHO; REJECT;
+ .|\\n /* eat up any unmatched character */
+
+.fi
+(The first three rules share the fourth's action since they use
+the special '|' action.)
+.B REJECT
+is a particularly expensive feature in terms scanner performance;
+if it is used in
+.I any
+of the scanner's actions it will slow down
+.I all
+of the scanner's matching. Furthermore,
+.B REJECT
+cannot be used with the
+.I -Cf
+or
+.I -CF
+options (see below).
+.IP
+Note also that unlike the other special actions,
+.B REJECT
+is a
+.I branch;
+code immediately following it in the action will
+.I not
+be executed.
+.IP -
+.B yymore()
+tells the scanner that the next time it matches a rule, the corresponding
+token should be
+.I appended
+onto the current value of
+.B yytext
+rather than replacing it. For example, given the input "mega-kludge"
+the following will write "mega-mega-kludge" to the output:
+.nf
+
+ %%
+ mega- ECHO; yymore();
+ kludge ECHO;
+
+.fi
+First "mega-" is matched and echoed to the output. Then "kludge"
+is matched, but the previous "mega-" is still hanging around at the
+beginning of
+.B yytext
+so the
+.B ECHO
+for the "kludge" rule will actually write "mega-kludge".
+The presence of
+.B yymore()
+in the scanner's action entails a minor performance penalty in the
+scanner's matching speed.
+.IP -
+.B yyless(n)
+returns all but the first
+.I n
+characters of the current token back to the input stream, where they
+will be rescanned when the scanner looks for the next match.
+.B yytext
+and
+.B yyleng
+are adjusted appropriately (e.g.,
+.B yyleng
+will now be equal to
+.I n
+). For example, on the input "foobar" the following will write out
+"foobarbar":
+.nf
+
+ %%
+ foobar ECHO; yyless(3);
+ [a-z]+ ECHO;
+
+.fi
+An argument of 0 to
+.B yyless
+will cause the entire current input string to be scanned again. Unless you've
+changed how the scanner will subsequently process its input (using
+.B BEGIN,
+for example), this will result in an endless loop.
+.PP
+Note that
+.B yyless
+is a macro and can only be used in the flex input file, not from
+other source files.
+.IP -
+.B unput(c)
+puts the character
+.I c
+back onto the input stream. It will be the next character scanned.
+The following action will take the current token and cause it
+to be rescanned enclosed in parentheses.
+.nf
+
+ {
+ int i;
+ unput( ')' );
+ for ( i = yyleng - 1; i >= 0; --i )
+ unput( yytext[i] );
+ unput( '(' );
+ }
+
+.fi
+Note that since each
+.B unput()
+puts the given character back at the
+.I beginning
+of the input stream, pushing back strings must be done back-to-front.
+Also note that you cannot put back
+.B EOF
+to attempt to mark the input stream with an end-of-file.
+.IP -
+.B input()
+reads the next character from the input stream. For example,
+the following is one way to eat up C comments:
+.nf
+
+ %%
+ "/*" {
+ register int c;
+
+ for ( ; ; )
+ {
+ while ( (c = input()) != '*' &&
+ c != EOF )
+ ; /* eat up text of comment */
+
+ if ( c == '*' )
+ {
+ while ( (c = input()) == '*' )
+ ;
+ if ( c == '/' )
+ break; /* found the end */
+ }
+
+ if ( c == EOF )
+ {
+ error( "EOF in comment" );
+ break;
+ }
+ }
+ }
+
+.fi
+(Note that if the scanner is compiled using
+.B C++,
+then
+.B input()
+is instead referred to as
+.B yyinput(),
+in order to avoid a name clash with the
+.B C++
+stream by the name of
+.I input.)
+.IP -
+.B yyterminate()
+can be used in lieu of a return statement in an action. It terminates
+the scanner and returns a 0 to the scanner's caller, indicating "all done".
+By default,
+.B yyterminate()
+is also called when an end-of-file is encountered. It is a macro and
+may be redefined.
+.SH THE GENERATED SCANNER
+The output of
+.I flex
+is the file
+.B lex.yy.c,
+which contains the scanning routine
+.B yylex(),
+a number of tables used by it for matching tokens, and a number
+of auxiliary routines and macros. By default,
+.B yylex()
+is declared as follows:
+.nf
+
+ int yylex()
+ {
+ ... various definitions and the actions in here ...
+ }
+
+.fi
+(If your environment supports function prototypes, then it will
+be "int yylex( void )".) This definition may be changed by defining
+the "YY_DECL" macro. For example, you could use:
+.nf
+
+ #define YY_DECL float lexscan( a, b ) float a, b;
+
+.fi
+to give the scanning routine the name
+.I lexscan,
+returning a float, and taking two floats as arguments. Note that
+if you give arguments to the scanning routine using a
+K&R-style/non-prototyped function declaration, you must terminate
+the definition with a semi-colon (;).
+.PP
+Whenever
+.B yylex()
+is called, it scans tokens from the global input file
+.I yyin
+(which defaults to stdin). It continues until it either reaches
+an end-of-file (at which point it returns the value 0) or
+one of its actions executes a
+.I return
+statement.
+.PP
+If the scanner reaches an end-of-file, subsequent calls are undefined
+unless either
+.I yyin
+is pointed at a new input file (in which case scanning continues from
+that file), or
+.B yyrestart()
+is called.
+.B yyrestart()
+takes one argument, a
+.B FILE *
+pointer, and initializes
+.I yyin
+for scanning from that file. Essentially there is no difference between
+just assigning
+.I yyin
+to a new input file or using
+.B yyrestart()
+to do so; the latter is available for compatibility with previous versions
+of
+.I flex,
+and because it can be used to switch input files in the middle of scanning.
+It can also be used to throw away the current input buffer, by calling
+it with an argument of
+.I yyin.
+.PP
+If
+.B yylex()
+stops scanning due to executing a
+.I return
+statement in one of the actions, the scanner may then be called again and it
+will resume scanning where it left off.
+.PP
+By default (and for purposes of efficiency), the scanner uses
+block-reads rather than simple
+.I getc()
+calls to read characters from
+.I yyin.
+The nature of how it gets its input can be controlled by defining the
+.B YY_INPUT
+macro.
+YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its
+action is to place up to
+.I max_size
+characters in the character array
+.I buf
+and return in the integer variable
+.I result
+either the
+number of characters read or the constant YY_NULL (0 on Unix systems)
+to indicate EOF. The default YY_INPUT reads from the
+global file-pointer "yyin".
+.PP
+A sample definition of YY_INPUT (in the definitions
+section of the input file):
+.nf
+
+ %{
+ #define YY_INPUT(buf,result,max_size) \\
+ { \\
+ int c = getchar(); \\
+ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\
+ }
+ %}
+
+.fi
+This definition will change the input processing to occur
+one character at a time.
+.PP
+You also can add in things like keeping track of the
+input line number this way; but don't expect your scanner to
+go very fast.
+.PP
+When the scanner receives an end-of-file indication from YY_INPUT,
+it then checks the
+.B yywrap()
+function. If
+.B yywrap()
+returns false (zero), then it is assumed that the
+function has gone ahead and set up
+.I yyin
+to point to another input file, and scanning continues. If it returns
+true (non-zero), then the scanner terminates, returning 0 to its
+caller.
+.PP
+The default
+.B yywrap()
+always returns 1.
+.PP
+The scanner writes its
+.B ECHO
+output to the
+.I yyout
+global (default, stdout), which may be redefined by the user simply
+by assigning it to some other
+.B FILE
+pointer.
+.SH START CONDITIONS
+.I flex
+provides a mechanism for conditionally activating rules. Any rule
+whose pattern is prefixed with "<sc>" will only be active when
+the scanner is in the start condition named "sc". For example,
+.nf
+
+ <STRING>[^"]* { /* eat up the string body ... */
+ ...
+ }
+
+.fi
+will be active only when the scanner is in the "STRING" start
+condition, and
+.nf
+
+ <INITIAL,STRING,QUOTE>\\. { /* handle an escape ... */
+ ...
+ }
+
+.fi
+will be active only when the current start condition is
+either "INITIAL", "STRING", or "QUOTE".
+.PP
+Start conditions
+are declared in the definitions (first) section of the input
+using unindented lines beginning with either
+.B %s
+or
+.B %x
+followed by a list of names.
+The former declares
+.I inclusive
+start conditions, the latter
+.I exclusive
+start conditions. A start condition is activated using the
+.B BEGIN
+action. Until the next
+.B BEGIN
+action is executed, rules with the given start
+condition will be active and
+rules with other start conditions will be inactive.
+If the start condition is
+.I inclusive,
+then rules with no start conditions at all will also be active.
+If it is
+.I exclusive,
+then
+.I only
+rules qualified with the start condition will be active.
+A set of rules contingent on the same exclusive start condition
+describe a scanner which is independent of any of the other rules in the
+.I flex
+input. Because of this,
+exclusive start conditions make it easy to specify "mini-scanners"
+which scan portions of the input that are syntactically different
+from the rest (e.g., comments).
+.PP
+If the distinction between inclusive and exclusive start conditions
+is still a little vague, here's a simple example illustrating the
+connection between the two. The set of rules:
+.nf
+
+ %s example
+ %%
+ <example>foo /* do something */
+
+.fi
+is equivalent to
+.nf
+
+ %x example
+ %%
+ <INITIAL,example>foo /* do something */
+
+.fi
+.PP
+Also note that the special start-condition specifier
+.B <*>
+matches every start condition. Thus, the above example could also
+have been written;
+.nf
+
+ %x example
+ %%
+ <*>foo /* do something */
+
+.fi
+.PP
+The default rule (to
+.B ECHO
+any unmatched character) remains active in start conditions.
+.PP
+.B BEGIN(0)
+returns to the original state where only the rules with
+no start conditions are active. This state can also be
+referred to as the start-condition "INITIAL", so
+.B BEGIN(INITIAL)
+is equivalent to
+.B BEGIN(0).
+(The parentheses around the start condition name are not required but
+are considered good style.)
+.PP
+.B BEGIN
+actions can also be given as indented code at the beginning
+of the rules section. For example, the following will cause
+the scanner to enter the "SPECIAL" start condition whenever
+.I yylex()
+is called and the global variable
+.I enter_special
+is true:
+.nf
+
+ int enter_special;
+
+ %x SPECIAL
+ %%
+ if ( enter_special )
+ BEGIN(SPECIAL);
+
+ <SPECIAL>blahblahblah
+ ...more rules follow...
+
+.fi
+.PP
+To illustrate the uses of start conditions,
+here is a scanner which provides two different interpretations
+of a string like "123.456". By default it will treat it as
+as three tokens, the integer "123", a dot ('.'), and the integer "456".
+But if the string is preceded earlier in the line by the string
+"expect-floats"
+it will treat it as a single token, the floating-point number
+123.456:
+.nf
+
+ %{
+ #include <math.h>
+ %}
+ %s expect
+
+ %%
+ expect-floats BEGIN(expect);
+
+ <expect>[0-9]+"."[0-9]+ {
+ printf( "found a float, = %f\\n",
+ atof( yytext ) );
+ }
+ <expect>\\n {
+ /* that's the end of the line, so
+ * we need another "expect-number"
+ * before we'll recognize any more
+ * numbers
+ */
+ BEGIN(INITIAL);
+ }
+
+ [0-9]+ {
+ printf( "found an integer, = %d\\n",
+ atoi( yytext ) );
+ }
+
+ "." printf( "found a dot\\n" );
+
+.fi
+Here is a scanner which recognizes (and discards) C comments while
+maintaining a count of the current input line.
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+This scanner goes to a bit of trouble to match as much
+text as possible with each rule. In general, when attempting to write
+a high-speed scanner try to match as much possible in each rule, as
+it's a big win.
+.PP
+Note that start-conditions names are really integer values and
+can be stored as such. Thus, the above could be extended in the
+following fashion:
+.nf
+
+ %x comment foo
+ %%
+ int line_num = 1;
+ int comment_caller;
+
+ "/*" {
+ comment_caller = INITIAL;
+ BEGIN(comment);
+ }
+
+ ...
+
+ <foo>"/*" {
+ comment_caller = foo;
+ BEGIN(comment);
+ }
+
+ <comment>[^*\\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(comment_caller);
+
+.fi
+Furthermore, you can access the current start condition using
+the integer-valued
+.B YY_START
+macro. For example, the above assignments to
+.I comment_caller
+could instead be written
+.nf
+
+ comment_caller = YY_START;
+.fi
+.PP
+Note that start conditions do not have their own name-space; %s's and %x's
+declare names in the same fashion as #define's.
+.PP
+Finally, here's an example of how to match C-style quoted strings using
+exclusive start conditions, including expanded escape sequences (but
+not including checking for a string that's too long):
+.nf
+
+ %x str
+
+ %%
+ char string_buf[MAX_STR_CONST];
+ char *string_buf_ptr;
+
+
+ \\" string_buf_ptr = string_buf; BEGIN(str);
+
+ <str>\\" { /* saw closing quote - all done */
+ BEGIN(INITIAL);
+ *string_buf_ptr = '\\0';
+ /* return string constant token type and
+ * value to parser
+ */
+ }
+
+ <str>\\n {
+ /* error - unterminated string constant */
+ /* generate error message */
+ }
+
+ <str>\\\\[0-7]{1,3} {
+ /* octal escape sequence */
+ int result;
+
+ (void) sscanf( yytext + 1, "%o", &result );
+
+ if ( result > 0xff )
+ /* error, constant is out-of-bounds */
+
+ *string_buf_ptr++ = result;
+ }
+
+ <str>\\\\[0-9]+ {
+ /* generate error - bad escape sequence; something
+ * like '\\48' or '\\0777777'
+ */
+ }
+
+ <str>\\\\n *string_buf_ptr++ = '\\n';
+ <str>\\\\t *string_buf_ptr++ = '\\t';
+ <str>\\\\r *string_buf_ptr++ = '\\r';
+ <str>\\\\b *string_buf_ptr++ = '\\b';
+ <str>\\\\f *string_buf_ptr++ = '\\f';
+
+ <str>\\\\(.|\\n) *string_buf_ptr++ = yytext[1];
+
+ <str>[^\\\\\\n\\"]+ {
+ char *yytext_ptr = yytext;
+
+ while ( *yytext_ptr )
+ *string_buf_ptr++ = *yytext_ptr++;
+ }
+
+.fi
+.SH MULTIPLE INPUT BUFFERS
+Some scanners (such as those which support "include" files)
+require reading from several input streams. As
+.I flex
+scanners do a large amount of buffering, one cannot control
+where the next input will be read from by simply writing a
+.B YY_INPUT
+which is sensitive to the scanning context.
+.B YY_INPUT
+is only called when the scanner reaches the end of its buffer, which
+may be a long time after scanning a statement such as an "include"
+which requires switching the input source.
+.PP
+To negotiate these sorts of problems,
+.I flex
+provides a mechanism for creating and switching between multiple
+input buffers. An input buffer is created by using:
+.nf
+
+ YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+
+.fi
+which takes a
+.I FILE
+pointer and a size and creates a buffer associated with the given
+file and large enough to hold
+.I size
+characters (when in doubt, use
+.B YY_BUF_SIZE
+for the size). It returns a
+.B YY_BUFFER_STATE
+handle, which may then be passed to other routines:
+.nf
+
+ void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+
+.fi
+switches the scanner's input buffer so subsequent tokens will
+come from
+.I new_buffer.
+Note that
+.B yy_switch_to_buffer()
+may be used by yywrap() to set things up for continued scanning, instead
+of opening a new file and pointing
+.I yyin
+at it.
+.nf
+
+ void yy_delete_buffer( YY_BUFFER_STATE buffer )
+
+.fi
+is used to reclaim the storage associated with a buffer.
+.PP
+.B yy_new_buffer()
+is an alias for
+.B yy_create_buffer(),
+provided for compatibility with the C++ use of
+.I new
+and
+.I delete
+for creating and destroying dynamic objects.
+.PP
+Finally, the
+.B YY_CURRENT_BUFFER
+macro returns a
+.B YY_BUFFER_STATE
+handle to the current buffer.
+.PP
+Here is an example of using these features for writing a scanner
+which expands include files (the
+.B <<EOF>>
+feature is discussed below):
+.nf
+
+ /* the "incl" state is used for picking up the name
+ * of an include file
+ */
+ %x incl
+
+ %{
+ #define MAX_INCLUDE_DEPTH 10
+ YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+ int include_stack_ptr = 0;
+ %}
+
+ %%
+ include BEGIN(incl);
+
+ [a-z]+ ECHO;
+ [^a-z\\n]*\\n? ECHO;
+
+ <incl>[ \\t]* /* eat the whitespace */
+ <incl>[^ \\t\\n]+ { /* got the include file name */
+ if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
+ {
+ fprintf( stderr, "Includes nested too deeply" );
+ exit( 1 );
+ }
+
+ include_stack[include_stack_ptr++] =
+ YY_CURRENT_BUFFER;
+
+ yyin = fopen( yytext, "r" );
+
+ if ( ! yyin )
+ error( ... );
+
+ yy_switch_to_buffer(
+ yy_create_buffer( yyin, YY_BUF_SIZE ) );
+
+ BEGIN(INITIAL);
+ }
+
+ <<EOF>> {
+ if ( --include_stack_ptr < 0 )
+ {
+ yyterminate();
+ }
+
+ else
+ {
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ yy_switch_to_buffer(
+ include_stack[include_stack_ptr] );
+ }
+ }
+
+.fi
+.SH END-OF-FILE RULES
+The special rule "<<EOF>>" indicates
+actions which are to be taken when an end-of-file is
+encountered and yywrap() returns non-zero (i.e., indicates
+no further files to process). The action must finish
+by doing one of four things:
+.IP -
+assigning
+.I yyin
+to a new input file (in previous versions of flex, after doing the
+assignment you had to call the special action
+.B YY_NEW_FILE;
+this is no longer necessary);
+.IP -
+executing a
+.I return
+statement;
+.IP -
+executing the special
+.B yyterminate()
+action;
+.IP -
+or, switching to a new buffer using
+.B yy_switch_to_buffer()
+as shown in the example above.
+.PP
+<<EOF>> rules may not be used with other
+patterns; they may only be qualified with a list of start
+conditions. If an unqualified <<EOF>> rule is given, it
+applies to
+.I all
+start conditions which do not already have <<EOF>> actions. To
+specify an <<EOF>> rule for only the initial start condition, use
+.nf
+
+ <INITIAL><<EOF>>
+
+.fi
+.PP
+These rules are useful for catching things like unclosed comments.
+An example:
+.nf
+
+ %x quote
+ %%
+
+ ...other rules for dealing with quotes...
+
+ <quote><<EOF>> {
+ error( "unterminated quote" );
+ yyterminate();
+ }
+ <<EOF>> {
+ if ( *++filelist )
+ yyin = fopen( *filelist, "r" );
+ else
+ yyterminate();
+ }
+
+.fi
+.SH MISCELLANEOUS MACROS
+The macro
+.bd
+YY_USER_ACTION
+can be defined to provide an action
+which is always executed prior to the matched rule's action. For example,
+it could be #define'd to call a routine to convert yytext to lower-case.
+.PP
+The macro
+.B YY_USER_INIT
+may be defined to provide an action which is always executed before
+the first scan (and before the scanner's internal initializations are done).
+For example, it could be used to call a routine to read
+in a data table or open a logging file.
+.PP
+In the generated scanner, the actions are all gathered in one large
+switch statement and separated using
+.B YY_BREAK,
+which may be redefined. By default, it is simply a "break", to separate
+each rule's action from the following rule's.
+Redefining
+.B YY_BREAK
+allows, for example, C++ users to
+#define YY_BREAK to do nothing (while being very careful that every
+rule ends with a "break" or a "return"!) to avoid suffering from
+unreachable statement warnings where because a rule's action ends with
+"return", the
+.B YY_BREAK
+is inaccessible.
+.SH INTERFACING WITH YACC
+One of the main uses of
+.I flex
+is as a companion to the
+.I yacc
+parser-generator.
+.I yacc
+parsers expect to call a routine named
+.B yylex()
+to find the next input token. The routine is supposed to
+return the type of the next token as well as putting any associated
+value in the global
+.B yylval.
+To use
+.I flex
+with
+.I yacc,
+one specifies the
+.B \-d
+option to
+.I yacc
+to instruct it to generate the file
+.B y.tab.h
+containing definitions of all the
+.B %tokens
+appearing in the
+.I yacc
+input. This file is then included in the
+.I flex
+scanner. For example, if one of the tokens is "TOK_NUMBER",
+part of the scanner might look like:
+.nf
+
+ %{
+ #include "y.tab.h"
+ %}
+
+ %%
+
+ [0-9]+ yylval = atoi( yytext ); return TOK_NUMBER;
+
+.fi
+.SH OPTIONS
+.I flex
+has the following options:
+.TP
+.B \-b
+Generate backing-up information to
+.I lex.backup.
+This is a list of scanner states which require backing up
+and the input characters on which they do so. By adding rules one
+can remove backing-up states. If all backing-up states
+are eliminated and
+.B \-Cf
+or
+.B \-CF
+is used, the generated scanner will run faster (see the
+.B \-p
+flag). Only users who wish to squeeze every last cycle out of their
+scanners need worry about this option. (See the section on Performance
+Considerations below.)
+.TP
+.B \-c
+is a do-nothing, deprecated option included for POSIX compliance.
+.IP
+.B NOTE:
+in previous releases of
+.I flex
+.B \-c
+specified table-compression options. This functionality is
+now given by the
+.B \-C
+flag. To ease the the impact of this change, when
+.I flex
+encounters
+.B \-c,
+it currently issues a warning message and assumes that
+.B \-C
+was desired instead. In the future this "promotion" of
+.B \-c
+to
+.B \-C
+will go away in the name of full POSIX compliance (unless
+the POSIX meaning is removed first).
+.TP
+.B \-d
+makes the generated scanner run in
+.I debug
+mode. Whenever a pattern is recognized and the global
+.B yy_flex_debug
+is non-zero (which is the default),
+the scanner will write to
+.I stderr
+a line of the form:
+.nf
+
+ --accepting rule at line 53 ("the matched text")
+
+.fi
+The line number refers to the location of the rule in the file
+defining the scanner (i.e., the file that was fed to flex). Messages
+are also generated when the scanner backs up, accepts the
+default rule, reaches the end of its input buffer (or encounters
+a NUL; at this point, the two look the same as far as the scanner's concerned),
+or reaches an end-of-file.
+.TP
+.B \-f
+specifies
+.I fast scanner.
+No table compression is done and stdio is bypassed.
+The result is large but fast. This option is equivalent to
+.B \-Cfr
+(see below).
+.TP
+.B \-h
+generates a "help" summary of
+.I flex's
+options to
+.I stderr
+and then exits.
+.TP
+.B \-i
+instructs
+.I flex
+to generate a
+.I case-insensitive
+scanner. The case of letters given in the
+.I flex
+input patterns will
+be ignored, and tokens in the input will be matched regardless of case. The
+matched text given in
+.I yytext
+will have the preserved case (i.e., it will not be folded).
+.TP
+.B \-l
+turns on maximum compatibility with the original AT&T
+.I lex
+implementation. Note that this does not mean
+.I full
+compatibility. Use of this option costs a considerable amount of
+performance, and it cannot be used with the
+.B \-+, -f, -F, -Cf,
+or
+.B -CF
+options. For details on the compatibilities it provides, see the section
+"Incompatibilities With Lex And POSIX" below.
+.TP
+.B \-n
+is another do-nothing, deprecated option included only for
+POSIX compliance.
+.TP
+.B \-p
+generates a performance report to stderr. The report
+consists of comments regarding features of the
+.I flex
+input file which will cause a serious loss of performance in the resulting
+scanner. If you give the flag twice, you will also get comments regarding
+features that lead to minor performance losses.
+.IP
+Note that the use of
+.B REJECT
+and variable trailing context (see the Bugs section in flex(1))
+entails a substantial performance penalty; use of
+.I yymore(),
+the
+.B ^
+operator,
+and the
+.B \-I
+flag entail minor performance penalties.
+.TP
+.B \-s
+causes the
+.I default rule
+(that unmatched scanner input is echoed to
+.I stdout)
+to be suppressed. If the scanner encounters input that does not
+match any of its rules, it aborts with an error. This option is
+useful for finding holes in a scanner's rule set.
+.TP
+.B \-t
+instructs
+.I flex
+to write the scanner it generates to standard output instead
+of
+.B lex.yy.c.
+.TP
+.B \-v
+specifies that
+.I flex
+should write to
+.I stderr
+a summary of statistics regarding the scanner it generates.
+Most of the statistics are meaningless to the casual
+.I flex
+user, but the first line identifies the version of
+.I flex
+(same as reported by
+.B \-V),
+and the next line the flags used when generating the scanner, including
+those that are on by default.
+.TP
+.B \-w
+suppresses warning messages.
+.TP
+.B \-B
+instructs
+.I flex
+to generate a
+.I batch
+scanner, the opposite of
+.I interactive
+scanners generated by
+.B \-I
+(see below). In general, you use
+.B \-B
+when you are
+.I certain
+that your scanner will never be used interactively, and you want to
+squeeze a
+.I little
+more performance out of it. If your goal is instead to squeeze out a
+.I lot
+more performance, you should be using the
+.B \-Cf
+or
+.B \-CF
+options (discussed below), which turn on
+.B \-B
+automatically anyway.
+.TP
+.B \-F
+specifies that the
+.ul
+fast
+scanner table representation should be used (and stdio
+bypassed). This representation is
+about as fast as the full table representation
+.B (-f),
+and for some sets of patterns will be considerably smaller (and for
+others, larger). In general, if the pattern set contains both "keywords"
+and a catch-all, "identifier" rule, such as in the set:
+.nf
+
+ "case" return TOK_CASE;
+ "switch" return TOK_SWITCH;
+ ...
+ "default" return TOK_DEFAULT;
+ [a-z]+ return TOK_ID;
+
+.fi
+then you're better off using the full table representation. If only
+the "identifier" rule is present and you then use a hash table or some such
+to detect the keywords, you're better off using
+.B -F.
+.IP
+This option is equivalent to
+.B \-CFr
+(see below). It cannot be used with
+.B \-+.
+.TP
+.B \-I
+instructs
+.I flex
+to generate an
+.I interactive
+scanner. An interactive scanner is one that only looks ahead to decide
+what token has been matched if it absolutely must. It turns out that
+always looking one extra character ahead, even if the scanner has already
+seen enough text to disambiguate the current token, is a bit faster than
+only looking ahead when necessary. But scanners that always look ahead
+give dreadful interactive performance; for example, when a user types
+a newline, it is not recognized as a newline token until they enter
+.I another
+token, which often means typing in another whole line.
+.IP
+.I Flex
+scanners default to
+.I interactive
+unless you use the
+.B \-Cf
+or
+.B \-CF
+table-compression options (see below). That's because if you're looking
+for high-performance you should be using one of these options, so if you
+didn't,
+.I flex
+assumes you'd rather trade off a bit of run-time performance for intuitive
+interactive behavior. Note also that you
+.I cannot
+use
+.B \-I
+in conjunction with
+.B \-Cf
+or
+.B \-CF.
+Thus, this option is not really needed; it is on by default for all those
+cases in which it is allowed.
+.IP
+You can force a scanner to
+.I not
+be interactive by using
+.B \-B
+(see above).
+.TP
+.B \-L
+instructs
+.I flex
+not to generate
+.B #line
+directives. Without this option,
+.I flex
+peppers the generated scanner
+with #line directives so error messages in the actions will be correctly
+located with respect to the original
+.I flex
+input file, and not to
+the fairly meaningless line numbers of
+.B lex.yy.c.
+(Unfortunately
+.I flex
+does not presently generate the necessary directives
+to "retarget" the line numbers for those parts of
+.B lex.yy.c
+which it generated. So if there is an error in the generated code,
+a meaningless line number is reported.)
+.TP
+.B \-T
+makes
+.I flex
+run in
+.I trace
+mode. It will generate a lot of messages to
+.I stderr
+concerning
+the form of the input and the resultant non-deterministic and deterministic
+finite automata. This option is mostly for use in maintaining
+.I flex.
+.TP
+.B \-V
+prints the version number to
+.I stderr
+and exits.
+.TP
+.B \-7
+instructs
+.I flex
+to generate a 7-bit scanner, i.e., one which can only recognized 7-bit
+characters in its input. The advantage of using
+.B \-7
+is that the scanner's tables can be up to half the size of those generated
+using the
+.B \-8
+option (see below). The disadvantage is that such scanners often hang
+or crash if their input contains an 8-bit character.
+.IP
+Note, however, that unless you generate your scanner using the
+.B \-Cf
+or
+.B \-CF
+table compression options, use of
+.B \-7
+will save only a small amount of table space, and make your scanner
+considerably less portable.
+.I Flex's
+default behavior is to generate an 8-bit scanner unless you use the
+.B \-Cf
+or
+.B \-CF,
+in which case
+.I flex
+defaults to generating 7-bit scanners unless your site was always
+configured to generate 8-bit scanners (as will often be the case
+with non-USA sites). You can tell whether flex generated a 7-bit
+or an 8-bit scanner by inspecting the flag summary in the
+.B \-v
+output as described above.
+.IP
+Note that if you use
+.B \-Cfe
+or
+.B \-CFe
+(those table compression options, but also using equivalence classes as
+discussed see below), flex still defaults to generating an 8-bit
+scanner, since usually with these compression options full 8-bit tables
+are not much more expensive than 7-bit tables.
+.TP
+.B \-8
+instructs
+.I flex
+to generate an 8-bit scanner, i.e., one which can recognize 8-bit
+characters. This flag is only needed for scanners generated using
+.B \-Cf
+or
+.B \-CF,
+as otherwise flex defaults to generating an 8-bit scanner anyway.
+.IP
+See the discussion of
+.B \-7
+above for flex's default behavior and the tradeoffs between 7-bit
+and 8-bit scanners.
+.TP
+.B \-+
+specifies that you want flex to generate a C++
+scanner class. See the section on Generating C++ Scanners below for
+details.
+.TP
+.B \-C[aefFmr]
+controls the degree of table compression and, more generally, trade-offs
+between small scanners and fast scanners.
+.IP
+.B \-Ca
+("align") instructs flex to trade off larger tables in the
+generated scanner for faster performance because the elements of
+the tables are better aligned for memory access and computation. On some
+RISC architectures, fetching and manipulating longwords is more efficient
+than with smaller-sized datums such as shortwords. This option can
+double the size of the tables used by your scanner.
+.IP
+.B \-Ce
+directs
+.I flex
+to construct
+.I equivalence classes,
+i.e., sets of characters
+which have identical lexical properties (for example, if the only
+appearance of digits in the
+.I flex
+input is in the character class
+"[0-9]" then the digits '0', '1', ..., '9' will all be put
+in the same equivalence class). Equivalence classes usually give
+dramatic reductions in the final table/object file sizes (typically
+a factor of 2-5) and are pretty cheap performance-wise (one array
+look-up per character scanned).
+.IP
+.B \-Cf
+specifies that the
+.I full
+scanner tables should be generated -
+.I flex
+should not compress the
+tables by taking advantages of similar transition functions for
+different states.
+.IP
+.B \-CF
+specifies that the alternate fast scanner representation (described
+above under the
+.B \-F
+flag)
+should be used. This option cannot be used with
+.B \-+.
+.IP
+.B \-Cm
+directs
+.I flex
+to construct
+.I meta-equivalence classes,
+which are sets of equivalence classes (or characters, if equivalence
+classes are not being used) that are commonly used together. Meta-equivalence
+classes are often a big win when using compressed tables, but they
+have a moderate performance impact (one or two "if" tests and one
+array look-up per character scanned).
+.IP
+.B \-Cr
+causes the generated scanner to
+.I bypass
+use of the standard I/O library (stdio) for input. Instead of calling
+.B fread()
+or
+.B getc(),
+the scanner will use the
+.B read()
+system call, resulting in a performance gain which varies from system
+to system, but in general is probably negligible unless you are also using
+.B \-Cf
+or
+.B \-CF.
+Using
+.B \-Cr
+can cause strange behavior if, for example, you read from
+.I yyin
+using stdio prior to calling the scanner (because the scanner will miss
+whatever text your previous reads left in the stdio input buffer).
+.IP
+.B \-Cr
+has no effect if you define
+.B YY_INPUT
+(see The Generated Scanner above).
+.IP
+A lone
+.B \-C
+specifies that the scanner tables should be compressed but neither
+equivalence classes nor meta-equivalence classes should be used.
+.IP
+The options
+.B \-Cf
+or
+.B \-CF
+and
+.B \-Cm
+do not make sense together - there is no opportunity for meta-equivalence
+classes if the table is not being compressed. Otherwise the options
+may be freely mixed, and are cumulative.
+.IP
+The default setting is
+.B \-Cem,
+which specifies that
+.I flex
+should generate equivalence classes
+and meta-equivalence classes. This setting provides the highest
+degree of table compression. You can trade off
+faster-executing scanners at the cost of larger tables with
+the following generally being true:
+.nf
+
+ slowest & smallest
+ -Cem
+ -Cm
+ -Ce
+ -C
+ -C{f,F}e
+ -C{f,F}
+ -C{f,F}a
+ fastest & largest
+
+.fi
+Note that scanners with the smallest tables are usually generated and
+compiled the quickest, so
+during development you will usually want to use the default, maximal
+compression.
+.IP
+.B \-Cfe
+is often a good compromise between speed and size for production
+scanners.
+.TP
+.B \-Pprefix
+changes the default
+.I "yy"
+prefix used by
+.I flex
+for all globally-visible variable and function names to instead be
+.I prefix.
+For example,
+.B \-Pfoo
+changes the name of
+.B yytext
+to
+.B footext.
+It also changes the name of the default output file from
+.B lex.yy.c
+to
+.B lex.foo.c.
+Here are all of the names affected:
+.nf
+
+ yyFlexLexer
+ yy_create_buffer
+ yy_delete_buffer
+ yy_flex_debug
+ yy_init_buffer
+ yy_load_buffer_state
+ yy_switch_to_buffer
+ yyin
+ yyleng
+ yylex
+ yyout
+ yyrestart
+ yytext
+ yywrap
+
+.fi
+Within your scanner itself, you can still refer to the global variables
+and functions using either version of their name; but eternally, they
+have the modified name.
+.IP
+This option lets you easily link together multiple
+.I flex
+programs into the same executable. Note, though, that using this
+option also renames
+.B yywrap(),
+so you now
+.I must
+provide your own (appropriately-named) version of the routine for your
+scanner, as linking with
+.B \-lfl
+no longer provides one for you by default.
+.TP
+.B \-Sskeleton_file
+overrides the default skeleton file from which
+.I flex
+constructs its scanners. You'll never need this option unless you are doing
+.I flex
+maintenance or development.
+.SH PERFORMANCE CONSIDERATIONS
+The main design goal of
+.I flex
+is that it generate high-performance scanners. It has been optimized
+for dealing well with large sets of rules. Aside from the effects on
+scanner speed of the table compression
+.B \-C
+options outlined above,
+there are a number of options/actions which degrade performance. These
+are, from most expensive to least:
+.nf
+
+ REJECT
+
+ pattern sets that require backing up
+ arbitrary trailing context
+
+ yymore()
+ '^' beginning-of-line operator
+
+.fi
+with the first three all being quite expensive and the last two
+being quite cheap. Note also that
+.B unput()
+is implemented as a routine call that potentially does quite a bit of
+work, while
+.B yyless()
+is a quite-cheap macro; so if just putting back some excess text you
+scanned, use
+.B yyless().
+.PP
+.B REJECT
+should be avoided at all costs when performance is important.
+It is a particularly expensive option.
+.PP
+Getting rid of backing up is messy and often may be an enormous
+amount of work for a complicated scanner. In principal, one begins
+by using the
+.B \-b
+flag to generate a
+.I lex.backup
+file. For example, on the input
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+.fi
+the file looks like:
+.nf
+
+ State #6 is non-accepting -
+ associated rule line numbers:
+ 2 3
+ out-transitions: [ o ]
+ jam-transitions: EOF [ \\001-n p-\\177 ]
+
+ State #8 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ a ]
+ jam-transitions: EOF [ \\001-` b-\\177 ]
+
+ State #9 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ r ]
+ jam-transitions: EOF [ \\001-q s-\\177 ]
+
+ Compressed tables always back up.
+
+.fi
+The first few lines tell us that there's a scanner state in
+which it can make a transition on an 'o' but not on any other
+character, and that in that state the currently scanned text does not match
+any rule. The state occurs when trying to match the rules found
+at lines 2 and 3 in the input file.
+If the scanner is in that state and then reads
+something other than an 'o', it will have to back up to find
+a rule which is matched. With
+a bit of headscratching one can see that this must be the
+state it's in when it has seen "fo". When this has happened,
+if anything other than another 'o' is seen, the scanner will
+have to back up to simply match the 'f' (by the default rule).
+.PP
+The comment regarding State #8 indicates there's a problem
+when "foob" has been scanned. Indeed, on any character other
+than an 'a', the scanner will have to back up to accept "foo".
+Similarly, the comment for State #9 concerns when "fooba" has
+been scanned and an 'r' does not follow.
+.PP
+The final comment reminds us that there's no point going to
+all the trouble of removing backing up from the rules unless
+we're using
+.B \-Cf
+or
+.B \-CF,
+since there's no performance gain doing so with compressed scanners.
+.PP
+The way to remove the backing up is to add "error" rules:
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+ fooba |
+ foob |
+ fo {
+ /* false alarm, not really a keyword */
+ return TOK_ID;
+ }
+
+.fi
+.PP
+Eliminating backing up among a list of keywords can also be
+done using a "catch-all" rule:
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+ [a-z]+ return TOK_ID;
+
+.fi
+This is usually the best solution when appropriate.
+.PP
+Backing up messages tend to cascade.
+With a complicated set of rules it's not uncommon to get hundreds
+of messages. If one can decipher them, though, it often
+only takes a dozen or so rules to eliminate the backing up (though
+it's easy to make a mistake and have an error rule accidentally match
+a valid token. A possible future
+.I flex
+feature will be to automatically add rules to eliminate backing up).
+.PP
+.I Variable
+trailing context (where both the leading and trailing parts do not have
+a fixed length) entails almost the same performance loss as
+.B REJECT
+(i.e., substantial). So when possible a rule like:
+.nf
+
+ %%
+ mouse|rat/(cat|dog) run();
+
+.fi
+is better written:
+.nf
+
+ %%
+ mouse/cat|dog run();
+ rat/cat|dog run();
+
+.fi
+or as
+.nf
+
+ %%
+ mouse|rat/cat run();
+ mouse|rat/dog run();
+
+.fi
+Note that here the special '|' action does
+.I not
+provide any savings, and can even make things worse (see
+.PP
+A final note regarding performance: as mentioned above in the section
+How the Input is Matched, dynamically resizing
+.B yytext
+to accomodate huge tokens is a slow process because it presently requires that
+the (huge) token be rescanned from the beginning. Thus if performance is
+vital, you should attempt to match "large" quantities of text but not
+"huge" quantities, where the cutoff between the two is at about 8K
+characters/token.
+.PP
+Another area where the user can increase a scanner's performance
+(and one that's easier to implement) arises from the fact that
+the longer the tokens matched, the faster the scanner will run.
+This is because with long tokens the processing of most input
+characters takes place in the (short) inner scanning loop, and
+does not often have to go through the additional work of setting up
+the scanning environment (e.g.,
+.B yytext)
+for the action. Recall the scanner for C comments:
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]*
+ <comment>"*"+[^*/\\n]*
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+This could be sped up by writing it as:
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]*
+ <comment>[^*\\n]*\\n ++line_num;
+ <comment>"*"+[^*/\\n]*
+ <comment>"*"+[^*/\\n]*\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+Now instead of each newline requiring the processing of another
+action, recognizing the newlines is "distributed" over the other rules
+to keep the matched text as long as possible. Note that
+.I adding
+rules does
+.I not
+slow down the scanner! The speed of the scanner is independent
+of the number of rules or (modulo the considerations given at the
+beginning of this section) how complicated the rules are with
+regard to operators such as '*' and '|'.
+.PP
+A final example in speeding up a scanner: suppose you want to scan
+through a file containing identifiers and keywords, one per line
+and with no other extraneous characters, and recognize all the
+keywords. A natural first approach is:
+.nf
+
+ %%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+ .|\\n /* it's not a keyword */
+
+.fi
+To eliminate the back-tracking, introduce a catch-all rule:
+.nf
+
+ %%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+ [a-z]+ |
+ .|\\n /* it's not a keyword */
+
+.fi
+Now, if it's guaranteed that there's exactly one word per line,
+then we can reduce the total number of matches by a half by
+merging in the recognition of newlines with that of the other
+tokens:
+.nf
+
+ %%
+ asm\\n |
+ auto\\n |
+ break\\n |
+ ... etc ...
+ volatile\\n |
+ while\\n /* it's a keyword */
+
+ [a-z]+\\n |
+ .|\\n /* it's not a keyword */
+
+.fi
+One has to be careful here, as we have now reintroduced backing up
+into the scanner. In particular, while
+.I we
+know that there will never be any characters in the input stream
+other than letters or newlines,
+.I flex
+can't figure this out, and it will plan for possibly needing to back up
+when it has scanned a token like "auto" and then the next character
+is something other than a newline or a letter. Previously it would
+then just match the "auto" rule and be done, but now it has no "auto"
+rule, only a "auto\\n" rule. To eliminate the possibility of backing up,
+we could either duplicate all rules but without final newlines, or,
+since we never expect to encounter such an input and therefore don't
+how it's classified, we can introduce one more catch-all rule, this
+one which doesn't include a newline:
+.nf
+
+ %%
+ asm\\n |
+ auto\\n |
+ break\\n |
+ ... etc ...
+ volatile\\n |
+ while\\n /* it's a keyword */
+
+ [a-z]+\\n |
+ [a-z]+ |
+ .|\\n /* it's not a keyword */
+
+.fi
+Compiled with
+.B \-Cf,
+this is about as fast as one can get a
+.I flex
+scanner to go for this particular problem.
+.PP
+A final note:
+.I flex
+is slow when matching NUL's, particularly when a token contains
+multiple NUL's.
+It's best to write rules which match
+.I short
+amounts of text if it's anticipated that the text will often include NUL's.
+.SH GENERATING C++ SCANNERS
+.I flex
+provides two different ways to generate scanners for use with C++. The
+first way is to simply compile a scanner generated by
+.I flex
+using a C++ compiler instead of a C compiler. You should not encounter
+any compilations errors (please report any you find to the email address
+given in the Author section below). You can then use C++ code in your
+rule actions instead of C code. Note that the default input source for
+your scanner remains
+.I yyin,
+and default echoing is still done to
+.I yyout.
+Both of these remain
+.I FILE *
+variables and not C++
+.I streams.
+.PP
+You can also use
+.I flex
+to generate a C++ scanner class, using the
+.B \-+
+option, which is automatically specified if the name of the flex
+executable ends in a '+', such as
+.I flex++.
+When using this option, flex defaults to generating the scanner to the file
+.B lex.yy.cc
+instead of
+.B lex.yy.c.
+The generated scanner includes the header file
+.I FlexLexer.h,
+which defines the interface to two C++ classes.
+.PP
+The first class,
+.B FlexLexer,
+provides an abstract base class defining the general scanner class
+interface. It provides the following member functions:
+.TP
+.B const char* YYText()
+returns the text of the most recently matched token, the equivalent of
+.B yytext.
+.TP
+.B int YYLeng()
+returns the length of the most recently matched token, the equivalent of
+.B yyleng.
+.PP
+Also provided are member functions equivalent to
+.B yy_switch_to_buffer(),
+.B yy_create_buffer()
+(though the first argument is an
+.B istream*
+object pointer and not a
+.B FILE*),
+.B yy_delete_buffer(),
+and
+.B yyrestart()
+(again, the first argument is a
+.B istream*
+object pointer).
+.PP
+The second class defined in
+.I FlexLexer.h
+is
+.B yyFlexLexer,
+which is derived from
+.B FlexLexer.
+It defines the following additional member functions:
+.TP
+.B
+yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
+constructs a
+.B yyFlexLexer
+object using the given streams for input and output. If not specified,
+the streams default to
+.B cin
+and
+.B cout,
+respectively.
+.TP
+.B virtual int yylex()
+performs the same role is
+.B yylex()
+does for ordinary flex scanners: it scans the input stream, consuming
+tokens, until a rule's action returns a value.
+.PP
+In addition,
+.B yyFlexLexer
+defines the following protected virtual functions which you can redefine
+in derived classes to tailor the scanner:
+.TP
+.B
+virtual int LexerInput( char* buf, int max_size )
+reads up to
+.B max_size
+characters into
+.B buf
+and returns the number of characters read. To indicate end-of-input,
+return 0 characters. Note that "interactive" scanners (see the
+.B \-B
+and
+.B \-I
+flags) define the macro
+.B YY_INTERACTIVE.
+If you redefine
+.B LexerInput()
+and need to take different actions depending on whether or not
+the scanner might be scanning an interactive input source, you can
+test for the presence of this name via
+.B #ifdef.
+.TP
+.B
+virtual void LexerOutput( const char* buf, int size )
+writes out
+.B size
+characters from the buffer
+.B buf,
+which, while NUL-terminated, may also contain "internal" NUL's if
+the scanner's rules can match text with NUL's in them.
+.TP
+.B
+virtual void LexerError( const char* msg )
+reports a fatal error message. The default version of this function
+writes the message to the stream
+.B cerr
+and exits.
+.PP
+Note that a
+.B yyFlexLexer
+object contains its
+.I entire
+scanning state. Thus you can use such objects to create reentrant
+scanners. You can instantiate multiple instances of the same
+.B yyFlexLexer
+class, and you can also combine multiple C++ scanner classes together
+in the same program using the
+.B \-P
+option discussed above.
+.PP
+Finally, note that the
+.B %array
+feature is not available to C++ scanner classes; you must use
+.B %pointer
+(the default).
+.PP
+Here is an example of a simple C++ scanner:
+.nf
+
+ // An example of using the flex C++ scanner class.
+
+ %{
+ int mylineno = 0;
+ %}
+
+ string \\"[^\\n"]+\\"
+
+ ws [ \\t]+
+
+ alpha [A-Za-z]
+ dig [0-9]
+ name ({alpha}|{dig}|\\$)({alpha}|{dig}|[_.\\-/$])*
+ num1 [-+]?{dig}+\\.?([eE][-+]?{dig}+)?
+ num2 [-+]?{dig}*\\.{dig}+([eE][-+]?{dig}+)?
+ number {num1}|{num2}
+
+ %%
+
+ {ws} /* skip blanks and tabs */
+
+ "/*" {
+ int c;
+
+ while((c = yyinput()) != 0)
+ {
+ if(c == '\\n')
+ ++mylineno;
+
+ else if(c == '*')
+ {
+ if((c = yyinput()) == '/')
+ break;
+ else
+ unput(c);
+ }
+ }
+ }
+
+ {number} cout << "number " << YYText() << '\\n';
+
+ \\n mylineno++;
+
+ {name} cout << "name " << YYText() << '\\n';
+
+ {string} cout << "string " << YYText() << '\\n';
+
+ %%
+
+ int main( int /* argc */, char** /* argv */ )
+ {
+ FlexLexer* lexer = new yyFlexLexer;
+ while(lexer->yylex() != 0)
+ ;
+ return 0;
+ }
+.fi
+IMPORTANT: the present form of the scanning class is
+.I experimental
+and may change considerably between major releases.
+.SH INCOMPATIBILITIES WITH LEX AND POSIX
+.I flex
+is a rewrite of the AT&T Unix
+.I lex
+tool (the two implementations do not share any code, though),
+with some extensions and incompatibilities, both of which
+are of concern to those who wish to write scanners acceptable
+to either implementation. The POSIX
+.I lex
+specification is closer to
+.I flex's
+behavior than that of the original
+.I lex
+implementation, but there also remain some incompatibilities between
+.I flex
+and POSIX. The intent is that ultimately
+.I flex
+will be fully POSIX-conformant. In this section we discuss all of
+the known areas of incompatibility.
+.PP
+.I flex's
+.B \-l
+option turns on maximum compatibility with the original AT&T
+.I lex
+implementation, at the cost of a major loss in the generated scanner's
+performance. We note below which incompatibilities can be overcome
+using the
+.B \-l
+option.
+.PP
+.I flex
+is fully compatible with
+.I lex
+with the following exceptions:
+.IP -
+The undocumented
+.I lex
+scanner internal variable
+.B yylineno
+is not supported unless
+.B \-l
+is used.
+.IP
+yylineno is not part of the POSIX specification.
+.IP -
+The
+.B input()
+routine is not redefinable, though it may be called to read characters
+following whatever has been matched by a rule. If
+.B input()
+encounters an end-of-file the normal
+.B yywrap()
+processing is done. A ``real'' end-of-file is returned by
+.B input()
+as
+.I EOF.
+.IP
+Input is instead controlled by defining the
+.B YY_INPUT
+macro.
+.IP
+The
+.I flex
+restriction that
+.B input()
+cannot be redefined is in accordance with the POSIX specification,
+which simply does not specify any way of controlling the
+scanner's input other than by making an initial assignment to
+.I yyin.
+.IP -
+.I flex
+scanners are not as reentrant as
+.I lex
+scanners. In particular, if you have an interactive scanner and
+an interrupt handler which long-jumps out of the scanner, and
+the scanner is subsequently called again, you may get the following
+message:
+.nf
+
+ fatal flex scanner internal error--end of buffer missed
+
+.fi
+To reenter the scanner, first use
+.nf
+
+ yyrestart( yyin );
+
+.fi
+Note that this call will throw away any buffered input; usually this
+isn't a problem with an interactive scanner.
+.IP
+Also note that flex C++ scanner classes
+.I are
+reentrant, so if using C++ is an option for you, you should use
+them instead. See "Generating C++ Scanners" above for details.
+.IP -
+.B output()
+is not supported.
+Output from the
+.B ECHO
+macro is done to the file-pointer
+.I yyout
+(default
+.I stdout).
+.IP
+.B output()
+is not part of the POSIX specification.
+.IP -
+.I lex
+does not support exclusive start conditions (%x), though they
+are in the POSIX specification.
+.IP -
+When definitions are expanded,
+.I flex
+encloses them in parentheses.
+With lex, the following:
+.nf
+
+ NAME [A-Z][A-Z0-9]*
+ %%
+ foo{NAME}? printf( "Found it\\n" );
+ %%
+
+.fi
+will not match the string "foo" because when the macro
+is expanded the rule is equivalent to "foo[A-Z][A-Z0-9]*?"
+and the precedence is such that the '?' is associated with
+"[A-Z0-9]*". With
+.I flex,
+the rule will be expanded to
+"foo([A-Z][A-Z0-9]*)?" and so the string "foo" will match.
+.IP
+Note that if the definition begins with
+.B ^
+or ends with
+.B $
+then it is
+.I not
+expanded with parentheses, to allow these operators to appear in
+definitions without losing their special meanings. But the
+.B <s>, /,
+and
+.B <<EOF>>
+operators cannot be used in a
+.I flex
+definition.
+.IP
+Using
+.B \-l
+results in the
+.I lex
+behavior of no parentheses around the definition.
+.IP
+The POSIX specification is that the definition be enclosed in parentheses.
+.IP -
+The
+.I lex
+.B %r
+(generate a Ratfor scanner) option is not supported. It is not part
+of the POSIX specification.
+.IP -
+After a call to
+.B unput(),
+.I yytext
+and
+.I yyleng
+are undefined until the next token is matched, unless the scanner
+was built using
+.B %array.
+This is not the case with
+.I lex
+or the POSIX specification. The
+.B \-l
+option does away with this incompatibility.
+.IP -
+The precedence of the
+.B {}
+(numeric range) operator is different.
+.I lex
+interprets "abc{1,3}" as "match one, two, or
+three occurrences of 'abc'", whereas
+.I flex
+interprets it as "match 'ab'
+followed by one, two, or three occurrences of 'c'". The latter is
+in agreement with the POSIX specification.
+.IP -
+The precedence of the
+.B ^
+operator is different.
+.I lex
+interprets "^foo|bar" as "match either 'foo' at the beginning of a line,
+or 'bar' anywhere", whereas
+.I flex
+interprets it as "match either 'foo' or 'bar' if they come at the beginning
+of a line". The latter is in agreement with the POSIX specification.
+.IP -
+.I yyin
+is
+.I initialized
+by
+.I lex
+to be
+.I stdin;
+.I flex,
+on the other hand,
+initializes
+.I yyin
+to NULL
+and then
+.I assigns
+it to
+.I stdin
+the first time the scanner is called, providing
+.I yyin
+has not already been assigned to a non-NULL value. The difference is
+subtle, but the net effect is that with
+.I flex
+scanners,
+.I yyin
+does not have a valid value until the scanner has been called.
+.IP
+The
+.B \-l
+option does away with this incompatibility.
+.IP -
+The special table-size declarations such as
+.B %a
+supported by
+.I lex
+are not required by
+.I flex
+scanners;
+.I flex
+ignores them.
+.IP -
+The name
+.bd
+FLEX_SCANNER
+is #define'd so scanners may be written for use with either
+.I flex
+or
+.I lex.
+.PP
+The following
+.I flex
+features are not included in
+.I lex
+or the POSIX specification:
+.nf
+
+ yyterminate()
+ <<EOF>>
+ <*>
+ YY_DECL
+ YY_START
+ YY_USER_ACTION
+ #line directives
+ %{}'s around actions
+ multiple actions on a line
+
+.fi
+plus almost all of the flex flags.
+The last feature in the list refers to the fact that with
+.I flex
+you can put multiple actions on the same line, separated with
+semi-colons, while with
+.I lex,
+the following
+.nf
+
+ foo handle_foo(); ++num_foos_seen;
+
+.fi
+is (rather surprisingly) truncated to
+.nf
+
+ foo handle_foo();
+
+.fi
+.I flex
+does not truncate the action. Actions that are not enclosed in
+braces are simply terminated at the end of the line.
+.SH DIAGNOSTICS
+.PP
+.I warning, rule cannot be matched
+indicates that the given rule
+cannot be matched because it follows other rules that will
+always match the same text as it. For
+example, in the following "foo" cannot be matched because it comes after
+an identifier "catch-all" rule:
+.nf
+
+ [a-z]+ got_identifier();
+ foo got_foo();
+
+.fi
+Using
+.B REJECT
+in a scanner suppresses this warning.
+.PP
+.I warning,
+.B \-s
+.I
+option given but default rule can be matched
+means that it is possible (perhaps only in a particular start condition)
+that the default rule (match any single character) is the only one
+that will match a particular input. Since
+.B \-s
+was given, presumably this is not intended.
+.PP
+.I reject_used_but_not_detected undefined
+or
+.I yymore_used_but_not_detected undefined -
+These errors can occur at compile time. They indicate that the
+scanner uses
+.B REJECT
+or
+.B yymore()
+but that
+.I flex
+failed to notice the fact, meaning that
+.I flex
+scanned the first two sections looking for occurrences of these actions
+and failed to find any, but somehow you snuck some in (via a #include
+file, for example). Make an explicit reference to the action in your
+.I flex
+input file. (Note that previously
+.I flex
+supported a
+.B %used/%unused
+mechanism for dealing with this problem; this feature is still supported
+but now deprecated, and will go away soon unless the author hears from
+people who can argue compellingly that they need it.)
+.PP
+.I flex scanner jammed -
+a scanner compiled with
+.B \-s
+has encountered an input string which wasn't matched by
+any of its rules. This error can also occur due to internal problems.
+.PP
+.I token too large, exceeds YYLMAX -
+your scanner uses
+.B %array
+and one of its rules matched a string longer than the
+.B YYLMAX
+constant (8K bytes by default). You can increase the value by
+#define'ing
+.B YYLMAX
+in the definitions section of your
+.I flex
+input.
+.PP
+.I scanner requires \-8 flag to
+.I use the character 'x' -
+Your scanner specification includes recognizing the 8-bit character
+.I 'x'
+and you did not specify the \-8 flag, and your scanner defaulted to 7-bit
+because you used the
+.B \-Cf
+or
+.B \-CF
+table compression options. See the discussion of the
+.B \-7
+flag for details.
+.PP
+.I flex scanner push-back overflow -
+you used
+.B unput()
+to push back so much text that the scanner's buffer could not hold
+both the pushed-back text and the current token in
+.B yytext.
+Ideally the scanner should dynamically resize the buffer in this case, but at
+present it does not.
+.PP
+.I
+input buffer overflow, can't enlarge buffer because scanner uses REJECT -
+the scanner was working on matching an extremely large token and needed
+to expand the input buffer. This doesn't work with scanners that use
+.B
+REJECT.
+.PP
+.I
+fatal flex scanner internal error--end of buffer missed -
+This can occur in an scanner which is reentered after a long-jump
+has jumped out (or over) the scanner's activation frame. Before
+reentering the scanner, use:
+.nf
+
+ yyrestart( yyin );
+
+.fi
+or, as noted above, switch to using the C++ scanner class.
+.PP
+.I too many start conditions in <> construct! -
+you listed more start conditions in a <> construct than exist (so
+you must have listed at least one of them twice).
+.SH FILES
+See flex(1).
+.SH DEFICIENCIES / BUGS
+Again, see flex(1).
+.SH "SEE ALSO"
+.PP
+flex(1), lex(1), yacc(1), sed(1), awk(1).
+.PP
+M. E. Lesk and E. Schmidt,
+.I LEX \- Lexical Analyzer Generator
+.SH AUTHOR
+Vern Paxson, with the help of many ideas and much inspiration from
+Van Jacobson. Original version by Jef Poskanzer. The fast table
+representation is a partial implementation of a design done by Van
+Jacobson. The implementation was done by Kevin Gong and Vern Paxson.
+.PP
+Thanks to the many
+.I flex
+beta-testers, feedbackers, and contributors, especially Francois Pinard,
+Casey Leedom,
+Nelson H.F. Beebe, benson@odi.com, Peter A. Bigot, Keith Bostic, Frederic
+Brehm, Nick Christopher, Jason Coughlin, Bill Cox, Dave Curtis, Scott David
+Daniels, Chris G. Demetriou, Mike Donahue, Chuck Doucette, Tom Epperly, Leo
+Eskin, Chris Faylor, Jon Forrest, Kaveh R. Ghazi,
+Eric Goldman, Ulrich Grepel, Jan Hajic,
+Jarkko Hietaniemi, Eric Hughes, John Interrante,
+Ceriel Jacobs, Jeffrey R. Jones, Henry
+Juengst, Amir Katz, ken@ken.hilco.com, Kevin B. Kenny, Marq Kole, Ronald
+Lamprecht, Greg Lee, Craig Leres, John Levine, Steve Liddle,
+Mohamed el Lozy, Brian Madsen, Chris
+Metcalf, Luke Mewburn, Jim Meyering, G.T. Nicol, Landon Noll, Marc Nozell,
+Richard Ohnemus, Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
+Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Frederic Raimbault,
+Rick Richardson,
+Kevin Rodgers, Jim Roskind,
+Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
+Alex Siegel, Mike Stump, Paul Stuart, Dave Tallman, Chris Thewalt,
+Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
+Yap, Nathan Zelle, David Zuhn, and those whose names have slipped my marginal
+mail-archiving skills but whose contributions are appreciated all the
+same.
+.PP
+Thanks to Keith Bostic, Jon Forrest, Noah Friedman,
+John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T.
+Nicol, Francois Pinard, Rich Salz, and Richard Stallman for help with various
+distribution headaches.
+.PP
+Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to
+Benson Margulies and Fred Burke for C++ support; to Kent Williams and Tom
+Epperly for C++ class support; to Ove Ewerlid for support of NUL's; and to
+Eric Hughes for support of multiple buffers.
+.PP
+This work was primarily done when I was with the Real Time Systems Group
+at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all there
+for the support I received.
+.PP
+Send comments to:
+.nf
+
+ Vern Paxson
+ Systems Engineering
+ Bldg. 46A, Room 1123
+ Lawrence Berkeley Laboratory
+ University of California
+ Berkeley, CA 94720
+
+ vern@ee.lbl.gov
+
+.fi
diff --git a/usr.bin/lex/gen.c b/usr.bin/lex/gen.c
new file mode 100644
index 0000000..9115a7c
--- /dev/null
+++ b/usr.bin/lex/gen.c
@@ -0,0 +1,1461 @@
+/* gen - actual generation (writing) of flex scanners */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/gen.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+/* declare functions that have forward references */
+
+void gen_next_state PROTO((int));
+void genecs PROTO((void));
+void indent_put2s PROTO((char [], char []));
+void indent_puts PROTO((char []));
+
+
+static int indent_level = 0; /* each level is 8 spaces */
+
+#define indent_up() (++indent_level)
+#define indent_down() (--indent_level)
+#define set_indent(indent_val) indent_level = indent_val
+
+/* Almost everything is done in terms of arrays starting at 1, so provide
+ * a null entry for the zero element of all C arrays. (The exception
+ * to this is that the fast table representation generally uses the
+ * 0 elements of its arrays, too.)
+ */
+static char C_int_decl[] = "static const int %s[%d] =\n { 0,\n";
+static char C_short_decl[] = "static const short int %s[%d] =\n { 0,\n";
+static char C_long_decl[] = "static const long int %s[%d] =\n { 0,\n";
+static char C_state_decl[] =
+ "static const yy_state_type %s[%d] =\n { 0,\n";
+
+
+/* Indent to the current level. */
+
+void do_indent()
+ {
+ register int i = indent_level * 8;
+
+ while ( i >= 8 )
+ {
+ putchar( '\t' );
+ i -= 8;
+ }
+
+ while ( i > 0 )
+ {
+ putchar( ' ' );
+ --i;
+ }
+ }
+
+
+/* Generate the code to keep backing-up information. */
+
+void gen_backing_up()
+ {
+ if ( reject || num_backing_up == 0 )
+ return;
+
+ if ( fullspd )
+ indent_puts( "if ( yy_current_state[-1].yy_nxt )" );
+ else
+ indent_puts( "if ( yy_accept[yy_current_state] )" );
+
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_last_accepting_state = yy_current_state;" );
+ indent_puts( "yy_last_accepting_cpos = yy_cp;" );
+ indent_puts( "}" );
+ indent_down();
+ }
+
+
+/* Generate the code to perform the backing up. */
+
+void gen_bu_action()
+ {
+ if ( reject || num_backing_up == 0 )
+ return;
+
+ set_indent( 3 );
+
+ indent_puts( "case 0: /* must back up */" );
+ indent_puts( "/* undo the effects of YY_DO_BEFORE_ACTION */" );
+ indent_puts( "*yy_cp = yy_hold_char;" );
+
+ if ( fullspd || fulltbl )
+ indent_puts( "yy_cp = yy_last_accepting_cpos + 1;" );
+ else
+ /* Backing-up info for compressed tables is taken \after/
+ * yy_cp has been incremented for the next state.
+ */
+ indent_puts( "yy_cp = yy_last_accepting_cpos;" );
+
+ indent_puts( "yy_current_state = yy_last_accepting_state;" );
+ indent_puts( "goto yy_find_action;" );
+ putchar( '\n' );
+
+ set_indent( 0 );
+ }
+
+
+/* genctbl - generates full speed compressed transition table */
+
+void genctbl()
+ {
+ register int i;
+ int end_of_buffer_action = num_rules + 1;
+
+ /* Table of verify for transition and offset to next state. */
+ printf( "static const struct yy_trans_info yy_transition[%d] =\n",
+ tblend + numecs + 1 );
+ printf( " {\n" );
+
+ /* We want the transition to be represented as the offset to the
+ * next state, not the actual state number, which is what it currently
+ * is. The offset is base[nxt[i]] - (base of current state)]. That's
+ * just the difference between the starting points of the two involved
+ * states (to - from).
+ *
+ * First, though, we need to find some way to put in our end-of-buffer
+ * flags and states. We do this by making a state with absolutely no
+ * transitions. We put it at the end of the table.
+ */
+
+ /* We need to have room in nxt/chk for two more slots: One for the
+ * action and one for the end-of-buffer transition. We now *assume*
+ * that we're guaranteed the only character we'll try to index this
+ * nxt/chk pair with is EOB, i.e., 0, so we don't have to make sure
+ * there's room for jam entries for other characters.
+ */
+
+ while ( tblend + 2 >= current_max_xpairs )
+ expand_nxt_chk();
+
+ while ( lastdfa + 1 >= current_max_dfas )
+ increase_max_dfas();
+
+ base[lastdfa + 1] = tblend + 2;
+ nxt[tblend + 1] = end_of_buffer_action;
+ chk[tblend + 1] = numecs + 1;
+ chk[tblend + 2] = 1; /* anything but EOB */
+
+ /* So that "make test" won't show arb. differences. */
+ nxt[tblend + 2] = 0;
+
+ /* Make sure every state has an end-of-buffer transition and an
+ * action #.
+ */
+ for ( i = 0; i <= lastdfa; ++i )
+ {
+ int anum = dfaacc[i].dfaacc_state;
+ int offset = base[i];
+
+ chk[offset] = EOB_POSITION;
+ chk[offset - 1] = ACTION_POSITION;
+ nxt[offset - 1] = anum; /* action number */
+ }
+
+ for ( i = 0; i <= tblend; ++i )
+ {
+ if ( chk[i] == EOB_POSITION )
+ transition_struct_out( 0, base[lastdfa + 1] - i );
+
+ else if ( chk[i] == ACTION_POSITION )
+ transition_struct_out( 0, nxt[i] );
+
+ else if ( chk[i] > numecs || chk[i] == 0 )
+ transition_struct_out( 0, 0 ); /* unused slot */
+
+ else /* verify, transition */
+ transition_struct_out( chk[i],
+ base[nxt[i]] - (i - chk[i]) );
+ }
+
+
+ /* Here's the final, end-of-buffer state. */
+ transition_struct_out( chk[tblend + 1], nxt[tblend + 1] );
+ transition_struct_out( chk[tblend + 2], nxt[tblend + 2] );
+
+ printf( " };\n" );
+ printf( "\n" );
+
+ /* Table of pointers to start states. */
+ printf(
+ "static const struct yy_trans_info *yy_start_state_list[%d] =\n",
+ lastsc * 2 + 1 );
+ printf( " {\n" ); /* } so vi doesn't get confused */
+
+ for ( i = 0; i <= lastsc * 2; ++i )
+ printf( " &yy_transition[%d],\n", base[i] );
+
+ dataend();
+
+ if ( useecs )
+ genecs();
+ }
+
+
+/* Generate equivalence-class tables. */
+
+void genecs()
+ {
+ Char clower();
+ register int i, j;
+ int numrows;
+
+ printf( C_int_decl, "yy_ec", csize );
+
+ for ( i = 1; i < csize; ++i )
+ {
+ if ( caseins && (i >= 'A') && (i <= 'Z') )
+ ecgroup[i] = ecgroup[clower( i )];
+
+ ecgroup[i] = ABS( ecgroup[i] );
+ mkdata( ecgroup[i] );
+ }
+
+ dataend();
+
+ if ( trace )
+ {
+ fputs( "\n\nEquivalence Classes:\n\n", stderr );
+
+ numrows = csize / 8;
+
+ for ( j = 0; j < numrows; ++j )
+ {
+ for ( i = j; i < csize; i = i + numrows )
+ {
+ fprintf( stderr, "%4s = %-2d",
+ readable_form( i ), ecgroup[i] );
+
+ putc( ' ', stderr );
+ }
+
+ putc( '\n', stderr );
+ }
+ }
+ }
+
+
+/* Generate the code to find the action number. */
+
+void gen_find_action()
+ {
+ if ( fullspd )
+ indent_puts( "yy_act = yy_current_state[-1].yy_nxt;" );
+
+ else if ( fulltbl )
+ indent_puts( "yy_act = yy_accept[yy_current_state];" );
+
+ else if ( reject )
+ {
+ indent_puts( "yy_current_state = *--yy_state_ptr;" );
+ indent_puts( "yy_lp = yy_accept[yy_current_state];" );
+
+ puts(
+ "find_rule: /* we branch to this label when backing up */" );
+
+ indent_puts(
+ "for ( ; ; ) /* until we find what rule we matched */" );
+
+ indent_up();
+
+ indent_puts( "{" );
+
+ indent_puts(
+ "if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_act = yy_acclist[yy_lp];" );
+
+ if ( variable_trailing_context_rules )
+ {
+ indent_puts( "if ( yy_act & YY_TRAILING_HEAD_MASK ||" );
+ indent_puts( " yy_looking_for_trail_begin )" );
+ indent_up();
+ indent_puts( "{" );
+
+ indent_puts(
+ "if ( yy_act == yy_looking_for_trail_begin )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_looking_for_trail_begin = 0;" );
+ indent_puts( "yy_act &= ~YY_TRAILING_HEAD_MASK;" );
+ indent_puts( "break;" );
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts( "else if ( yy_act & YY_TRAILING_MASK )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts(
+ "yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK;" );
+ indent_puts(
+ "yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK;" );
+
+ if ( real_reject )
+ {
+ /* Remember matched text in case we back up
+ * due to REJECT.
+ */
+ indent_puts( "yy_full_match = yy_cp;" );
+ indent_puts( "yy_full_state = yy_state_ptr;" );
+ indent_puts( "yy_full_lp = yy_lp;" );
+ }
+
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts( "else" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_full_match = yy_cp;" );
+ indent_puts( "yy_full_state = yy_state_ptr;" );
+ indent_puts( "yy_full_lp = yy_lp;" );
+ indent_puts( "break;" );
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts( "++yy_lp;" );
+ indent_puts( "goto find_rule;" );
+ }
+
+ else
+ {
+ /* Remember matched text in case we back up due to trailing
+ * context plus REJECT.
+ */
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_full_match = yy_cp;" );
+ indent_puts( "break;" );
+ indent_puts( "}" );
+ indent_down();
+ }
+
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts( "--yy_cp;" );
+
+ /* We could consolidate the following two lines with those at
+ * the beginning, but at the cost of complaints that we're
+ * branching inside a loop.
+ */
+ indent_puts( "yy_current_state = *--yy_state_ptr;" );
+ indent_puts( "yy_lp = yy_accept[yy_current_state];" );
+
+ indent_puts( "}" );
+
+ indent_down();
+ }
+
+ else
+ /* compressed */
+ indent_puts( "yy_act = yy_accept[yy_current_state];" );
+ }
+
+
+/* genftbl - generates full transition table */
+
+void genftbl()
+ {
+ register int i;
+ int end_of_buffer_action = num_rules + 1;
+
+ printf( long_align ? C_long_decl : C_short_decl,
+ "yy_accept", lastdfa + 1 );
+
+ dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action;
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ register int anum = dfaacc[i].dfaacc_state;
+
+ mkdata( anum );
+
+ if ( trace && anum )
+ fprintf( stderr, "state # %d accepts: [%d]\n",
+ i, anum );
+ }
+
+ dataend();
+
+ if ( useecs )
+ genecs();
+
+ /* Don't have to dump the actual full table entries - they were
+ * created on-the-fly.
+ */
+ }
+
+
+/* Generate the code to find the next compressed-table state. */
+
+void gen_next_compressed_state( char_map )
+char *char_map;
+ {
+ indent_put2s( "register YY_CHAR yy_c = %s;", char_map );
+
+ /* Save the backing-up info \before/ computing the next state
+ * because we always compute one more state than needed - we
+ * always proceed until we reach a jam state
+ */
+ gen_backing_up();
+
+ indent_puts(
+"while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_current_state = (int) yy_def[yy_current_state];" );
+
+ if ( usemecs )
+ {
+ /* We've arrange it so that templates are never chained
+ * to one another. This means we can afford to make a
+ * very simple test to see if we need to convert to
+ * yy_c's meta-equivalence class without worrying
+ * about erroneously looking up the meta-equivalence
+ * class twice
+ */
+ do_indent();
+
+ /* lastdfa + 2 is the beginning of the templates */
+ printf( "if ( yy_current_state >= %d )\n", lastdfa + 2 );
+
+ indent_up();
+ indent_puts( "yy_c = yy_meta[(unsigned int) yy_c];" );
+ indent_down();
+ }
+
+ indent_puts( "}" );
+ indent_down();
+
+ indent_puts(
+"yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];" );
+ }
+
+
+/* Generate the code to find the next match. */
+
+void gen_next_match()
+ {
+ /* NOTE - changes in here should be reflected in gen_next_state() and
+ * gen_NUL_trans().
+ */
+ char *char_map = useecs ?
+ "yy_ec[YY_SC_TO_UI(*yy_cp)]" :
+ "YY_SC_TO_UI(*yy_cp)";
+
+ char *char_map_2 = useecs ?
+ "yy_ec[YY_SC_TO_UI(*++yy_cp)]" :
+ "YY_SC_TO_UI(*++yy_cp)";
+
+ if ( fulltbl )
+ {
+ indent_put2s(
+ "while ( (yy_current_state = yy_nxt[yy_current_state][%s]) > 0 )",
+ char_map );
+
+ indent_up();
+
+ if ( num_backing_up > 0 )
+ {
+ indent_puts( "{" ); /* } for vi */
+ gen_backing_up();
+ putchar( '\n' );
+ }
+
+ indent_puts( "++yy_cp;" );
+
+ if ( num_backing_up > 0 )
+ /* { for vi */
+ indent_puts( "}" );
+
+ indent_down();
+
+ putchar( '\n' );
+ indent_puts( "yy_current_state = -yy_current_state;" );
+ }
+
+ else if ( fullspd )
+ {
+ indent_puts( "{" ); /* } for vi */
+ indent_puts(
+ "register const struct yy_trans_info *yy_trans_info;\n" );
+ indent_puts( "register YY_CHAR yy_c;\n" );
+ indent_put2s( "for ( yy_c = %s;", char_map );
+ indent_puts(
+ " (yy_trans_info = &yy_current_state[(unsigned int) yy_c])->" );
+ indent_puts( "yy_verify == yy_c;" );
+ indent_put2s( " yy_c = %s )", char_map_2 );
+
+ indent_up();
+
+ if ( num_backing_up > 0 )
+ indent_puts( "{" ); /* } for vi */
+
+ indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" );
+
+ if ( num_backing_up > 0 )
+ {
+ putchar( '\n' );
+ gen_backing_up(); /* { for vi */
+ indent_puts( "}" );
+ }
+
+ indent_down(); /* { for vi */
+ indent_puts( "}" );
+ }
+
+ else
+ { /* compressed */
+ indent_puts( "do" );
+
+ indent_up();
+ indent_puts( "{" ); /* } for vi */
+
+ gen_next_state( false );
+
+ indent_puts( "++yy_cp;" );
+
+ /* { for vi */
+ indent_puts( "}" );
+ indent_down();
+
+ do_indent();
+
+ if ( interactive )
+ printf( "while ( yy_base[yy_current_state] != %d );\n",
+ jambase );
+ else
+ printf( "while ( yy_current_state != %d );\n",
+ jamstate );
+
+ if ( ! reject && ! interactive )
+ {
+ /* Do the guaranteed-needed backing up to figure out
+ * the match.
+ */
+ indent_puts( "yy_cp = yy_last_accepting_cpos;" );
+ indent_puts(
+ "yy_current_state = yy_last_accepting_state;" );
+ }
+ }
+ }
+
+
+/* Generate the code to find the next state. */
+
+void gen_next_state( worry_about_NULs )
+int worry_about_NULs;
+ { /* NOTE - changes in here should be reflected in get_next_match() */
+ char char_map[256];
+
+ if ( worry_about_NULs && ! nultrans )
+ {
+ if ( useecs )
+ (void) sprintf( char_map,
+ "(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : %d)",
+ NUL_ec );
+ else
+ (void) sprintf( char_map,
+ "(*yy_cp ? YY_SC_TO_UI(*yy_cp) : %d)", NUL_ec );
+ }
+
+ else
+ strcpy( char_map, useecs ? "yy_ec[YY_SC_TO_UI(*yy_cp)]" :
+ "YY_SC_TO_UI(*yy_cp)" );
+
+ if ( worry_about_NULs && nultrans )
+ {
+ if ( ! fulltbl && ! fullspd )
+ /* Compressed tables back up *before* they match. */
+ gen_backing_up();
+
+ indent_puts( "if ( *yy_cp )" );
+ indent_up();
+ indent_puts( "{" ); /* } for vi */
+ }
+
+ if ( fulltbl )
+ indent_put2s(
+ "yy_current_state = yy_nxt[yy_current_state][%s];",
+ char_map );
+
+ else if ( fullspd )
+ indent_put2s(
+ "yy_current_state += yy_current_state[%s].yy_nxt;",
+ char_map );
+
+ else
+ gen_next_compressed_state( char_map );
+
+ if ( worry_about_NULs && nultrans )
+ {
+ /* { for vi */
+ indent_puts( "}" );
+ indent_down();
+ indent_puts( "else" );
+ indent_up();
+ indent_puts(
+ "yy_current_state = yy_NUL_trans[yy_current_state];" );
+ indent_down();
+ }
+
+ if ( fullspd || fulltbl )
+ gen_backing_up();
+
+ if ( reject )
+ indent_puts( "*yy_state_ptr++ = yy_current_state;" );
+ }
+
+
+/* Generate the code to make a NUL transition. */
+
+void gen_NUL_trans()
+ { /* NOTE - changes in here should be reflected in get_next_match() */
+ int need_backing_up = (num_backing_up > 0 && ! reject);
+
+ if ( need_backing_up )
+ /* We'll need yy_cp lying around for the gen_backing_up(). */
+ indent_puts( "register char *yy_cp = yy_c_buf_p;" );
+
+ putchar( '\n' );
+
+ if ( nultrans )
+ {
+ indent_puts(
+ "yy_current_state = yy_NUL_trans[yy_current_state];" );
+ indent_puts( "yy_is_jam = (yy_current_state == 0);" );
+ }
+
+ else if ( fulltbl )
+ {
+ do_indent();
+ printf( "yy_current_state = yy_nxt[yy_current_state][%d];\n",
+ NUL_ec );
+ indent_puts( "yy_is_jam = (yy_current_state <= 0);" );
+ }
+
+ else if ( fullspd )
+ {
+ do_indent();
+ printf( "register int yy_c = %d;\n", NUL_ec );
+
+ indent_puts(
+ "register const struct yy_trans_info *yy_trans_info;\n" );
+ indent_puts(
+ "yy_trans_info = &yy_current_state[(unsigned int) yy_c];" );
+ indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" );
+
+ indent_puts(
+ "yy_is_jam = (yy_trans_info->yy_verify != yy_c);" );
+ }
+
+ else
+ {
+ char NUL_ec_str[20];
+
+ (void) sprintf( NUL_ec_str, "%d", NUL_ec );
+ gen_next_compressed_state( NUL_ec_str );
+
+ if ( reject )
+ indent_puts( "*yy_state_ptr++ = yy_current_state;" );
+
+ do_indent();
+
+ printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate );
+ }
+
+ /* If we've entered an accepting state, back up; note that
+ * compressed tables have *already* done such backing up, so
+ * we needn't bother with it again.
+ */
+ if ( need_backing_up && (fullspd || fulltbl) )
+ {
+ putchar( '\n' );
+ indent_puts( "if ( ! yy_is_jam )" );
+ indent_up();
+ indent_puts( "{" );
+ gen_backing_up();
+ indent_puts( "}" );
+ indent_down();
+ }
+ }
+
+
+/* Generate the code to find the start state. */
+
+void gen_start_state()
+ {
+ if ( fullspd )
+ indent_put2s(
+ "yy_current_state = yy_start_state_list[yy_start%s];",
+ bol_needed ? " + (yy_bp[-1] == '\\n' ? 1 : 0)" : "" );
+
+ else
+ {
+ indent_puts( "yy_current_state = yy_start;" );
+
+ if ( bol_needed )
+ {
+ indent_puts( "if ( yy_bp[-1] == '\\n' )" );
+ indent_up();
+ indent_puts( "++yy_current_state;" );
+ indent_down();
+ }
+
+ if ( reject )
+ {
+ /* Set up for storing up states. */
+ indent_puts( "yy_state_ptr = yy_state_buf;" );
+ indent_puts( "*yy_state_ptr++ = yy_current_state;" );
+ }
+ }
+ }
+
+
+/* gentabs - generate data statements for the transition tables */
+
+void gentabs()
+ {
+ int i, j, k, *accset, nacc, *acc_array, total_states;
+ int end_of_buffer_action = num_rules + 1;
+
+ /* *Everything* is done in terms of arrays starting at 1, so provide
+ * a null entry for the zero element of all C arrays.
+ */
+ static char C_char_decl[] =
+ "static const YY_CHAR %s[%d] =\n { 0,\n"; /* } for vi */
+
+ acc_array = allocate_integer_array( current_max_dfas );
+ nummt = 0;
+
+ /* The compressed table format jams by entering the "jam state",
+ * losing information about the previous state in the process.
+ * In order to recover the previous state, we effectively need
+ * to keep backing-up information.
+ */
+ ++num_backing_up;
+
+ if ( reject )
+ {
+ /* Write out accepting list and pointer list.
+ *
+ * First we generate the "yy_acclist" array. In the process,
+ * we compute the indices that will go into the "yy_accept"
+ * array, and save the indices in the dfaacc array.
+ */
+ int EOB_accepting_list[2];
+
+ /* Set up accepting structures for the End Of Buffer state. */
+ EOB_accepting_list[0] = 0;
+ EOB_accepting_list[1] = end_of_buffer_action;
+ accsiz[end_of_buffer_state] = 1;
+ dfaacc[end_of_buffer_state].dfaacc_set = EOB_accepting_list;
+
+ printf( long_align ? C_long_decl : C_short_decl,
+ "yy_acclist", MAX( numas, 1 ) + 1 );
+
+ j = 1; /* index into "yy_acclist" array */
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ acc_array[i] = j;
+
+ if ( accsiz[i] != 0 )
+ {
+ accset = dfaacc[i].dfaacc_set;
+ nacc = accsiz[i];
+
+ if ( trace )
+ fprintf( stderr,
+ "state # %d accepts: ", i );
+
+ for ( k = 1; k <= nacc; ++k )
+ {
+ int accnum = accset[k];
+
+ ++j;
+
+ if ( variable_trailing_context_rules &&
+ ! (accnum & YY_TRAILING_HEAD_MASK) &&
+ accnum > 0 && accnum <= num_rules &&
+ rule_type[accnum] == RULE_VARIABLE )
+ {
+ /* Special hack to flag
+ * accepting number as part
+ * of trailing context rule.
+ */
+ accnum |= YY_TRAILING_MASK;
+ }
+
+ mkdata( accnum );
+
+ if ( trace )
+ {
+ fprintf( stderr, "[%d]",
+ accset[k] );
+
+ if ( k < nacc )
+ fputs( ", ", stderr );
+ else
+ putc( '\n', stderr );
+ }
+ }
+ }
+ }
+
+ /* add accepting number for the "jam" state */
+ acc_array[i] = j;
+
+ dataend();
+ }
+
+ else
+ {
+ dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action;
+
+ for ( i = 1; i <= lastdfa; ++i )
+ acc_array[i] = dfaacc[i].dfaacc_state;
+
+ /* add accepting number for jam state */
+ acc_array[i] = 0;
+ }
+
+ /* Spit out "yy_accept" array. If we're doing "reject", it'll be
+ * pointers into the "yy_acclist" array. Otherwise it's actual
+ * accepting numbers. In either case, we just dump the numbers.
+ */
+
+ /* "lastdfa + 2" is the size of "yy_accept"; includes room for C arrays
+ * beginning at 0 and for "jam" state.
+ */
+ k = lastdfa + 2;
+
+ if ( reject )
+ /* We put a "cap" on the table associating lists of accepting
+ * numbers with state numbers. This is needed because we tell
+ * where the end of an accepting list is by looking at where
+ * the list for the next state starts.
+ */
+ ++k;
+
+ printf( long_align ? C_long_decl : C_short_decl, "yy_accept", k );
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ mkdata( acc_array[i] );
+
+ if ( ! reject && trace && acc_array[i] )
+ fprintf( stderr, "state # %d accepts: [%d]\n",
+ i, acc_array[i] );
+ }
+
+ /* Add entry for "jam" state. */
+ mkdata( acc_array[i] );
+
+ if ( reject )
+ /* Add "cap" for the list. */
+ mkdata( acc_array[i] );
+
+ dataend();
+
+ if ( useecs )
+ genecs();
+
+ if ( usemecs )
+ {
+ /* Write out meta-equivalence classes (used to index
+ * templates with).
+ */
+
+ if ( trace )
+ fputs( "\n\nMeta-Equivalence Classes:\n", stderr );
+
+ printf( C_int_decl, "yy_meta", numecs + 1 );
+
+ for ( i = 1; i <= numecs; ++i )
+ {
+ if ( trace )
+ fprintf( stderr, "%d = %d\n",
+ i, ABS( tecbck[i] ) );
+
+ mkdata( ABS( tecbck[i] ) );
+ }
+
+ dataend();
+ }
+
+ total_states = lastdfa + numtemps;
+
+ printf( (tblend >= MAX_SHORT || long_align) ?
+ C_long_decl : C_short_decl,
+ "yy_base", total_states + 1 );
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ register int d = def[i];
+
+ if ( base[i] == JAMSTATE )
+ base[i] = jambase;
+
+ if ( d == JAMSTATE )
+ def[i] = jamstate;
+
+ else if ( d < 0 )
+ {
+ /* Template reference. */
+ ++tmpuses;
+ def[i] = lastdfa - d + 1;
+ }
+
+ mkdata( base[i] );
+ }
+
+ /* Generate jam state's base index. */
+ mkdata( base[i] );
+
+ for ( ++i /* skip jam state */; i <= total_states; ++i )
+ {
+ mkdata( base[i] );
+ def[i] = jamstate;
+ }
+
+ dataend();
+
+ printf( (total_states >= MAX_SHORT || long_align) ?
+ C_long_decl : C_short_decl,
+ "yy_def", total_states + 1 );
+
+ for ( i = 1; i <= total_states; ++i )
+ mkdata( def[i] );
+
+ dataend();
+
+ printf( (total_states >= MAX_SHORT || long_align) ?
+ C_long_decl : C_short_decl,
+ "yy_nxt", tblend + 1 );
+
+ for ( i = 1; i <= tblend; ++i )
+ {
+ if ( nxt[i] == 0 || chk[i] == 0 )
+ nxt[i] = jamstate; /* new state is the JAM state */
+
+ mkdata( nxt[i] );
+ }
+
+ dataend();
+
+ printf( (total_states >= MAX_SHORT || long_align) ?
+ C_long_decl : C_short_decl,
+ "yy_chk", tblend + 1 );
+
+ for ( i = 1; i <= tblend; ++i )
+ {
+ if ( chk[i] == 0 )
+ ++nummt;
+
+ mkdata( chk[i] );
+ }
+
+ dataend();
+ }
+
+
+/* Write out a formatted string (with a secondary string argument) at the
+ * current indentation level, adding a final newline.
+ */
+
+void indent_put2s( fmt, arg )
+char fmt[], arg[];
+ {
+ do_indent();
+ printf( fmt, arg );
+ putchar( '\n' );
+ }
+
+
+/* Write out a string at the current indentation level, adding a final
+ * newline.
+ */
+
+void indent_puts( str )
+char str[];
+ {
+ do_indent();
+ puts( str );
+ }
+
+
+/* make_tables - generate transition tables and finishes generating output file
+ */
+
+void make_tables()
+ {
+ register int i;
+ int did_eof_rule = false;
+
+ skelout();
+
+ /* First, take care of YY_DO_BEFORE_ACTION depending on yymore
+ * being used.
+ */
+ set_indent( 1 );
+
+ if ( yymore_used )
+ {
+ indent_puts( "yytext_ptr -= yy_more_len; \\" );
+ indent_puts( "yyleng = yy_cp - yytext_ptr; \\" );
+ }
+
+ else
+ indent_puts( "yyleng = yy_cp - yy_bp; \\" );
+
+ /* Now also deal with copying yytext_ptr to yytext if needed. */
+ skelout();
+ if ( yytext_is_array )
+ {
+ indent_puts( "if ( yyleng >= YYLMAX ) \\" );
+ indent_up();
+ indent_puts(
+ "YY_FATAL_ERROR( \"token too large, exceeds YYLMAX\" ); \\" );
+ indent_down();
+ indent_puts(
+ "yy_flex_strncpy( yytext, yytext_ptr, yyleng + 1 ); \\" );
+ }
+
+ set_indent( 0 );
+
+ skelout();
+
+
+ printf( "#define YY_END_OF_BUFFER %d\n", num_rules + 1 );
+
+ if ( fullspd )
+ {
+ /* Need to define the transet type as a size large
+ * enough to hold the biggest offset.
+ */
+ int total_table_size = tblend + numecs + 1;
+ char *trans_offset_type =
+ (total_table_size >= MAX_SHORT || long_align) ?
+ "long" : "short";
+
+ set_indent( 0 );
+ indent_puts( "struct yy_trans_info" );
+ indent_up();
+ indent_puts( "{" ); /* } for vi */
+
+ if ( long_align )
+ indent_puts( "long yy_verify;" );
+ else
+ indent_puts( "short yy_verify;" );
+
+ /* In cases where its sister yy_verify *is* a "yes, there is
+ * a transition", yy_nxt is the offset (in records) to the
+ * next state. In most cases where there is no transition,
+ * the value of yy_nxt is irrelevant. If yy_nxt is the -1th
+ * record of a state, though, then yy_nxt is the action number
+ * for that state.
+ */
+
+ indent_put2s( "%s yy_nxt;", trans_offset_type );
+ indent_puts( "};" );
+ indent_down();
+ }
+
+ if ( fullspd )
+ genctbl();
+ else if ( fulltbl )
+ genftbl();
+ else
+ gentabs();
+
+ /* Definitions for backing up. We don't need them if REJECT
+ * is being used because then we use an alternative backin-up
+ * technique instead.
+ */
+ if ( num_backing_up > 0 && ! reject )
+ {
+ if ( ! C_plus_plus )
+ {
+ indent_puts(
+ "static yy_state_type yy_last_accepting_state;" );
+ indent_puts(
+ "static char *yy_last_accepting_cpos;\n" );
+ }
+ }
+
+ if ( nultrans )
+ {
+ printf( C_state_decl, "yy_NUL_trans", lastdfa + 1 );
+
+ for ( i = 1; i <= lastdfa; ++i )
+ {
+ if ( fullspd )
+ printf( " &yy_transition[%d],\n", base[i] );
+ else
+ mkdata( nultrans[i] );
+ }
+
+ dataend();
+ }
+
+ if ( ddebug )
+ { /* Spit out table mapping rules to line numbers. */
+ indent_puts( "extern int yy_flex_debug;" );
+ indent_puts( "int yy_flex_debug = 1;\n" );
+
+ printf( long_align ? C_long_decl : C_short_decl,
+ "yy_rule_linenum", num_rules );
+ for ( i = 1; i < num_rules; ++i )
+ mkdata( rule_linenum[i] );
+ dataend();
+ }
+
+ if ( reject )
+ {
+ /* Declare state buffer variables. */
+ if ( ! C_plus_plus )
+ {
+ puts(
+ "static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;" );
+ puts( "static char *yy_full_match;" );
+ puts( "static int yy_lp;" );
+ }
+
+ if ( variable_trailing_context_rules )
+ {
+ if ( ! C_plus_plus )
+ {
+ puts(
+ "static int yy_looking_for_trail_begin = 0;" );
+ puts( "static int yy_full_lp;" );
+ puts( "static int *yy_full_state;" );
+ }
+
+ printf( "#define YY_TRAILING_MASK 0x%x\n",
+ (unsigned int) YY_TRAILING_MASK );
+ printf( "#define YY_TRAILING_HEAD_MASK 0x%x\n",
+ (unsigned int) YY_TRAILING_HEAD_MASK );
+ }
+
+ puts( "#define REJECT \\" );
+ puts( "{ \\" ); /* } for vi */
+ puts(
+ "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \\" );
+ puts(
+ "yy_cp = yy_full_match; /* restore poss. backed-over text */ \\" );
+
+ if ( variable_trailing_context_rules )
+ {
+ puts(
+ "yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \\" );
+ puts(
+ "yy_state_ptr = yy_full_state; /* restore orig. state */ \\" );
+ puts(
+ "yy_current_state = *yy_state_ptr; /* restore curr. state */ \\" );
+ }
+
+ puts( "++yy_lp; \\" );
+ puts( "goto find_rule; \\" );
+ /* { for vi */
+ puts( "}" );
+ }
+
+ else
+ {
+ puts(
+ "/* The intent behind this definition is that it'll catch" );
+ puts( " * any uses of REJECT which flex missed." );
+ puts( " */" );
+ puts( "#define REJECT reject_used_but_not_detected" );
+ }
+
+ if ( yymore_used )
+ {
+ if ( ! C_plus_plus )
+ {
+ indent_puts( "static int yy_more_flag = 0;" );
+ indent_puts( "static int yy_more_len = 0;" );
+ }
+
+ indent_puts( "#define yymore() (yy_more_flag = 1)" );
+ indent_puts( "#define YY_MORE_ADJ yy_more_len" );
+ }
+
+ else
+ {
+ indent_puts( "#define yymore() yymore_used_but_not_detected" );
+ indent_puts( "#define YY_MORE_ADJ 0" );
+ }
+
+ if ( ! C_plus_plus )
+ {
+ if ( yytext_is_array )
+ {
+ puts( "#ifndef YYLMAX" );
+ puts( "#define YYLMAX 8192" );
+ puts( "#endif\n" );
+ puts( "char yytext[YYLMAX];" );
+ puts( "char *yytext_ptr;" );
+ }
+
+ else
+ puts( "char *yytext;" );
+ }
+
+ fputs( &action_array[defs1_offset], stdout );
+
+ skelout();
+
+ if ( ! C_plus_plus )
+ {
+ if ( use_read )
+ {
+ printf(
+"\tif ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \\\n" );
+ printf(
+ "\t\tYY_FATAL_ERROR( \"input in flex scanner failed\" );\n" );
+ }
+
+ else
+ {
+ printf(
+ "\tif ( yy_current_buffer->yy_is_interactive ) \\\n" );
+ printf( "\t\t{ \\\n" );
+ printf( "\t\tint c = getc( yyin ); \\\n" );
+ printf( "\t\tresult = c == EOF ? 0 : 1; \\\n" );
+ printf( "\t\tbuf[0] = (char) c; \\\n" );
+ printf( "\t\t} \\\n" );
+ printf(
+ "\telse if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \\\n" );
+ printf( "\t\t && ferror( yyin ) ) \\\n" );
+ printf(
+ "\t\tYY_FATAL_ERROR( \"input in flex scanner failed\" );\n" );
+ }
+ }
+
+ skelout();
+
+ /* Copy prolog to output file. */
+ fputs( &action_array[prolog_offset], stdout );
+
+ skelout();
+
+ set_indent( 2 );
+
+ if ( yymore_used )
+ {
+ indent_puts( "yy_more_len = 0;" );
+ indent_puts( "if ( yy_more_flag )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "yy_more_len = yyleng;" );
+ indent_puts( "yy_more_flag = 0;" );
+ indent_puts( "}" );
+ indent_down();
+ }
+
+ skelout();
+
+ gen_start_state();
+
+ /* Note, don't use any indentation. */
+ puts( "yy_match:" );
+ gen_next_match();
+
+ skelout();
+ set_indent( 2 );
+ gen_find_action();
+
+ skelout();
+ if ( lex_compat )
+ {
+ indent_puts( "if ( yy_act != YY_END_OF_BUFFER )" );
+ indent_up();
+ indent_puts( "{" );
+ indent_puts( "int yyl;" );
+ indent_puts( "for ( yyl = 0; yyl < yyleng; ++yyl )" );
+ indent_up();
+ indent_puts( "if ( yytext[yyl] == '\\n' )" );
+ indent_up();
+ indent_puts( "++yylineno;" );
+ indent_down();
+ indent_down();
+ indent_puts( "}" );
+ indent_down();
+ }
+
+ skelout();
+ if ( ddebug )
+ {
+ indent_puts( "if ( yy_flex_debug )" );
+ indent_up();
+
+ indent_puts( "{" );
+ indent_puts( "if ( yy_act == 0 )" );
+ indent_up();
+ indent_puts(
+ "fprintf( stderr, \"--scanner backing up\\n\" );" );
+ indent_down();
+
+ do_indent();
+ printf( "else if ( yy_act < %d )\n", num_rules );
+ indent_up();
+ indent_puts(
+ "fprintf( stderr, \"--accepting rule at line %d (\\\"%s\\\")\\n\"," );
+ indent_puts( " yy_rule_linenum[yy_act], yytext );" );
+ indent_down();
+
+ do_indent();
+ printf( "else if ( yy_act == %d )\n", num_rules );
+ indent_up();
+ indent_puts(
+ "fprintf( stderr, \"--accepting default rule (\\\"%s\\\")\\n\"," );
+ indent_puts( " yytext );" );
+ indent_down();
+
+ do_indent();
+ printf( "else if ( yy_act == %d )\n", num_rules + 1 );
+ indent_up();
+ indent_puts(
+ "fprintf( stderr, \"--(end of buffer or a NUL)\\n\" );" );
+ indent_down();
+
+ do_indent();
+ printf( "else\n" );
+ indent_up();
+ indent_puts(
+ "fprintf( stderr, \"--EOF (start condition %d)\\n\", YY_START );" );
+ indent_down();
+
+ indent_puts( "}" );
+ indent_down();
+ }
+
+ /* Copy actions to output file. */
+ skelout();
+ indent_up();
+ gen_bu_action();
+ fputs( &action_array[action_offset], stdout );
+
+ /* generate cases for any missing EOF rules */
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! sceof[i] )
+ {
+ do_indent();
+ printf( "case YY_STATE_EOF(%s):\n", scname[i] );
+ did_eof_rule = true;
+ }
+
+ if ( did_eof_rule )
+ {
+ indent_up();
+ indent_puts( "yyterminate();" );
+ indent_down();
+ }
+
+
+ /* Generate code for handling NUL's, if needed. */
+
+ /* First, deal with backing up and setting up yy_cp if the scanner
+ * finds that it should JAM on the NUL.
+ */
+ skelout();
+ set_indent( 7 );
+
+ if ( fullspd || fulltbl )
+ indent_puts( "yy_cp = yy_c_buf_p;" );
+
+ else
+ { /* compressed table */
+ if ( ! reject && ! interactive )
+ {
+ /* Do the guaranteed-needed backing up to figure
+ * out the match.
+ */
+ indent_puts( "yy_cp = yy_last_accepting_cpos;" );
+ indent_puts(
+ "yy_current_state = yy_last_accepting_state;" );
+ }
+
+ else
+ /* Still need to initialize yy_cp, though
+ * yy_current_state was set up by
+ * yy_get_previous_state().
+ */
+ indent_puts( "yy_cp = yy_c_buf_p;" );
+ }
+
+
+ /* Generate code for yy_get_previous_state(). */
+ set_indent( 1 );
+ skelout();
+
+ if ( bol_needed )
+ indent_puts( "register char *yy_bp = yytext_ptr;\n" );
+
+ gen_start_state();
+
+ set_indent( 2 );
+ skelout();
+ gen_next_state( true );
+
+ set_indent( 1 );
+ skelout();
+ gen_NUL_trans();
+
+ skelout();
+ if ( lex_compat )
+ { /* update yylineno inside of unput() */
+ indent_puts( "if ( c == '\\n' )" );
+ indent_up();
+ indent_puts( "--yylineno;" );
+ indent_down();
+ }
+
+ skelout();
+
+ /* Copy remainder of input to output. */
+
+ line_directive_out( stdout );
+
+ if ( sectnum == 3 )
+ (void) flexscan(); /* copy remainder of input to output */
+ }
diff --git a/usr.bin/lex/initscan.c b/usr.bin/lex/initscan.c
new file mode 100644
index 0000000..f608d4c
--- /dev/null
+++ b/usr.bin/lex/initscan.c
@@ -0,0 +1,2723 @@
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/ncvs/src/usr.bin/lex/initscan.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $
+ */
+
+#define FLEX_SCANNER
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#ifdef __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+
+#ifdef __TURBOC__
+#define YY_USE_CONST
+#endif
+
+
+#ifndef YY_USE_CONST
+#ifndef const
+#define const
+#endif
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.
+ */
+#define YY_START ((yy_start - 1) / 2)
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". Now included
+ * only for backward compatibility with previous versions of flex.
+ */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern int yywrap YY_PROTO(( void ));
+#ifdef __cplusplus
+ }
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+void yyrestart YY_PROTO(( FILE *input_file ));
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+static void yy_push_state YY_PROTO(( int new_state ));
+static void yy_pop_state YY_PROTO(( void ));
+static int yy_top_state YY_PROTO(( void ));
+
+static void *yy_flex_alloc YY_PROTO(( unsigned int ));
+static void *yy_flex_realloc YY_PROTO(( void *, unsigned int ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define INITIAL 0
+#define SECT2 1
+#define SECT2PROLOG 2
+#define SECT3 3
+#define CODEBLOCK 4
+#define PICKUPDEF 5
+#define SC 6
+#define CARETISBOL 7
+#define NUM 8
+#define QUOTE 9
+#define FIRSTCCL 10
+#define CCL 11
+#define ACTION 12
+#define RECOVER 13
+#define BRACEERROR 14
+#define C_COMMENT 15
+#define ACTION_COMMENT 16
+#define ACTION_STRING 17
+#define PERCENT_BRACE_ACTION 18
+#define USED_LIST 19
+#define CODEBLOCK_2 20
+typedef unsigned char YY_CHAR;
+typedef int yy_state_type;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+extern char *yytext;
+#define yytext_ptr yytext
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, const char *, int ));
+#endif
+
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( const char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = yy_cp - yy_bp; \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_END_OF_BUFFER 113
+static const short int yy_accept[408] =
+ { 0,
+ 0, 0, 0, 0, 41, 41, 110, 110, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 113, 111, 6, 17, 111, 15, 1, 16,
+ 111, 111, 111, 14, 60, 53, 54, 60, 47, 60,
+ 59, 60, 60, 60, 60, 44, 43, 60, 60, 45,
+ 46, 41, 42, 41, 40, 39, 40, 40, 110, 110,
+ 26, 27, 26, 26, 26, 26, 26, 26, 29, 28,
+ 30, 29, 65, 61, 62, 64, 66, 80, 81, 80,
+
+ 78, 77, 79, 67, 69, 67, 68, 67, 72, 72,
+ 72, 74, 76, 74, 74, 74, 75, 92, 97, 92,
+ 96, 98, 98, 93, 93, 93, 90, 91, 111, 31,
+ 111, 83, 111, 82, 20, 22, 20, 21, 101, 102,
+ 101, 100, 103, 105, 103, 106, 107, 88, 88, 89,
+ 88, 88, 88, 88, 88, 88, 36, 33, 32, 36,
+ 36, 36, 88, 6, 17, 0, 17, 15, 1, 16,
+ 0, 16, 13, 7, 0, 0, 0, 3, 0, 4,
+ 0, 2, 14, 53, 54, 0, 0, 0, 54, 50,
+ 50, 0, 0, 57, 0, 108, 108, 108, 49, 48,
+
+ 49, 44, 43, 0, 43, 56, 44, 41, 42, 40,
+ 39, 39, 37, 38, 110, 110, 26, 27, 26, 26,
+ 26, 26, 29, 28, 30, 63, 64, 81, 77, 69,
+ 109, 109, 109, 70, 71, 76, 73, 92, 97, 0,
+ 95, 0, 94, 93, 93, 93, 0, 31, 0, 31,
+ 31, 83, 20, 22, 18, 101, 102, 101, 102, 102,
+ 99, 103, 105, 104, 88, 88, 88, 89, 85, 88,
+ 88, 88, 36, 33, 32, 36, 36, 84, 13, 7,
+ 0, 12, 0, 0, 0, 0, 3, 0, 0, 4,
+ 0, 5, 0, 51, 0, 52, 0, 0, 57, 0,
+
+ 57, 57, 108, 108, 49, 49, 58, 56, 37, 38,
+ 26, 26, 26, 23, 26, 0, 109, 109, 93, 93,
+ 0, 19, 0, 85, 85, 88, 88, 36, 36, 12,
+ 0, 0, 0, 3, 0, 0, 4, 5, 5, 52,
+ 52, 0, 57, 57, 57, 57, 108, 26, 26, 23,
+ 23, 0, 109, 93, 93, 19, 19, 88, 88, 36,
+ 36, 0, 0, 0, 10, 0, 57, 57, 57, 57,
+ 26, 26, 93, 93, 88, 88, 36, 36, 0, 0,
+ 0, 0, 57, 57, 24, 25, 86, 87, 86, 87,
+ 34, 35, 0, 9, 0, 0, 11, 55, 9, 9,
+
+ 0, 0, 8, 0, 8, 8, 0
+ } ;
+
+static const int yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 5, 1, 6, 7, 1, 8, 9,
+ 9, 10, 9, 11, 12, 9, 13, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 1, 1, 15,
+ 1, 16, 9, 1, 22, 23, 24, 25, 26, 27,
+ 21, 21, 28, 29, 30, 21, 31, 32, 33, 34,
+ 21, 35, 36, 37, 38, 21, 21, 39, 40, 21,
+ 17, 18, 19, 20, 21, 1, 22, 23, 24, 25,
+
+ 26, 27, 21, 21, 28, 29, 30, 21, 31, 32,
+ 33, 34, 21, 35, 36, 37, 38, 21, 21, 39,
+ 40, 21, 41, 42, 43, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static const int yy_meta[44] =
+ { 0,
+ 1, 2, 3, 1, 4, 1, 1, 5, 1, 6,
+ 1, 7, 5, 8, 1, 1, 1, 9, 10, 1,
+ 11, 12, 12, 12, 12, 12, 12, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 13, 11, 11, 11,
+ 5, 1, 14
+ } ;
+
+static const short int yy_base[470] =
+ { 0,
+ 0, 43, 85, 126, 89, 102, 1611, 1610, 168, 1605,
+ 108, 111, 211, 0, 1591, 1590, 252, 254, 116, 119,
+ 98, 122, 144, 146, 297, 0, 93, 104, 338, 340,
+ 149, 151, 257, 266, 268, 274, 383, 0, 425, 428,
+ 1596, 1595, 1607, 1615, 278, 1602, 1602, 0, 281, 1600,
+ 1600, 462, 1592, 0, 1615, 431, 1597, 1597, 1615, 285,
+ 1615, 1584, 1580, 331, 503, 437, 1593, 1593, 110, 1580,
+ 1615, 0, 1590, 1590, 0, 1590, 1588, 221, 1587, 1615,
+ 0, 1585, 1585, 1615, 0, 1561, 1546, 1511, 0, 1551,
+ 1543, 1543, 1615, 1615, 1498, 0, 1615, 1615, 1500, 1487,
+
+ 1615, 1463, 1615, 1615, 1466, 1460, 1615, 332, 1615, 333,
+ 126, 1615, 1411, 1398, 0, 334, 1615, 0, 1383, 1383,
+ 1615, 341, 1371, 0, 1354, 1336, 1615, 1615, 271, 1371,
+ 287, 1370, 1366, 1615, 0, 1362, 1349, 1331, 290, 1335,
+ 347, 1325, 0, 1323, 1310, 1615, 0, 0, 350, 1306,
+ 1287, 1246, 1615, 0, 1249, 1227, 0, 1264, 1261, 1255,
+ 1225, 1197, 1213, 351, 1213, 1213, 1615, 0, 358, 1198,
+ 1193, 1615, 0, 0, 443, 361, 447, 0, 342, 0,
+ 363, 1615, 0, 451, 1191, 1188, 1149, 365, 1615, 1615,
+ 1185, 1181, 1155, 1135, 423, 1615, 1125, 0, 0, 1615,
+
+ 546, 588, 1121, 1108, 1615, 0, 1615, 0, 1615, 0,
+ 0, 1095, 0, 0, 1088, 1615, 0, 1615, 0, 1061,
+ 1041, 630, 0, 1069, 1615, 1615, 0, 1615, 838, 1615,
+ 1615, 837, 0, 1615, 1615, 1615, 1615, 0, 1615, 434,
+ 1615, 0, 1615, 0, 821, 817, 373, 843, 376, 1615,
+ 842, 1615, 0, 1615, 463, 467, 834, 471, 1615, 833,
+ 1615, 0, 1615, 1615, 0, 441, 793, 1615, 673, 0,
+ 805, 802, 0, 830, 1615, 795, 792, 1615, 0, 0,
+ 594, 818, 817, 597, 784, 790, 0, 775, 786, 0,
+ 473, 807, 478, 1615, 486, 806, 590, 776, 790, 588,
+
+ 469, 703, 793, 0, 0, 0, 1615, 0, 0, 0,
+ 780, 772, 0, 800, 800, 730, 788, 0, 775, 767,
+ 600, 795, 602, 0, 773, 772, 764, 770, 762, 1615,
+ 610, 772, 751, 0, 740, 745, 0, 1615, 765, 1615,
+ 764, 740, 488, 803, 601, 817, 1615, 742, 730, 1615,
+ 760, 760, 1615, 738, 726, 1615, 756, 735, 723, 733,
+ 721, 714, 716, 726, 1615, 724, 602, 831, 715, 658,
+ 512, 473, 454, 459, 435, 438, 422, 430, 606, 410,
+ 357, 342, 338, 278, 0, 0, 0, 0, 0, 0,
+ 0, 0, 614, 255, 618, 131, 1615, 1615, 1615, 156,
+
+ 620, 622, 153, 625, 1615, 95, 1615, 858, 872, 886,
+ 900, 914, 928, 942, 956, 970, 984, 998, 1012, 1026,
+ 1040, 1054, 1062, 1075, 1081, 1094, 1108, 1122, 1136, 1150,
+ 1164, 1178, 1186, 1199, 1207, 1220, 1234, 1248, 1262, 1272,
+ 1280, 1293, 1307, 1321, 1335, 1349, 1363, 1371, 1384, 1398,
+ 1412, 1416, 1419, 1432, 1446, 1460, 710, 1474, 1487, 1501,
+ 1515, 711, 1529, 1537, 1544, 712, 743, 1557, 1571
+ } ;
+
+static const short int yy_def[470] =
+ { 0,
+ 407, 407, 408, 408, 409, 410, 411, 411, 407, 9,
+ 412, 412, 407, 13, 413, 413, 414, 414, 415, 415,
+ 416, 416, 417, 417, 407, 25, 418, 418, 413, 413,
+ 419, 419, 420, 420, 421, 421, 407, 37, 422, 422,
+ 37, 37, 407, 407, 407, 407, 407, 423, 407, 407,
+ 407, 424, 407, 425, 407, 407, 407, 407, 407, 407,
+ 407, 407, 426, 427, 407, 407, 407, 407, 407, 407,
+ 407, 428, 407, 428, 429, 430, 429, 429, 431, 407,
+ 432, 407, 432, 407, 433, 433, 433, 432, 434, 407,
+ 407, 434, 407, 407, 407, 435, 407, 407, 407, 407,
+
+ 407, 407, 407, 407, 407, 407, 407, 427, 407, 436,
+ 437, 407, 407, 407, 438, 427, 407, 439, 407, 439,
+ 407, 440, 407, 441, 441, 441, 407, 407, 442, 407,
+ 442, 407, 407, 407, 443, 407, 443, 407, 444, 407,
+ 444, 407, 445, 407, 445, 407, 446, 447, 447, 407,
+ 447, 447, 407, 448, 448, 448, 449, 407, 407, 449,
+ 449, 449, 447, 407, 407, 407, 407, 423, 407, 407,
+ 407, 407, 450, 451, 407, 407, 407, 452, 407, 453,
+ 454, 407, 425, 407, 407, 407, 407, 455, 407, 407,
+ 407, 407, 407, 456, 426, 407, 407, 457, 458, 407,
+
+ 407, 407, 407, 407, 407, 459, 407, 428, 407, 429,
+ 430, 430, 460, 461, 431, 407, 432, 407, 433, 433,
+ 433, 407, 434, 407, 407, 407, 435, 407, 407, 407,
+ 407, 407, 462, 407, 407, 407, 407, 439, 407, 440,
+ 407, 440, 407, 441, 441, 441, 442, 407, 442, 407,
+ 407, 407, 443, 407, 463, 444, 407, 444, 407, 407,
+ 407, 445, 407, 407, 447, 447, 447, 407, 407, 448,
+ 448, 448, 449, 407, 407, 449, 449, 407, 450, 451,
+ 407, 407, 407, 407, 407, 407, 464, 407, 407, 465,
+ 454, 407, 454, 407, 455, 407, 455, 407, 456, 456,
+
+ 456, 456, 407, 466, 458, 201, 407, 459, 460, 461,
+ 433, 433, 222, 407, 222, 222, 407, 467, 441, 441,
+ 463, 407, 463, 269, 269, 448, 448, 449, 449, 407,
+ 407, 407, 407, 464, 407, 407, 465, 407, 407, 407,
+ 407, 407, 456, 456, 456, 456, 407, 433, 433, 407,
+ 407, 316, 407, 441, 441, 407, 407, 448, 448, 449,
+ 449, 407, 407, 407, 407, 407, 456, 456, 456, 456,
+ 433, 433, 441, 441, 448, 448, 449, 449, 468, 407,
+ 407, 407, 456, 456, 433, 433, 441, 441, 448, 448,
+ 449, 449, 468, 407, 468, 407, 407, 407, 407, 407,
+
+ 469, 469, 407, 469, 407, 407, 0, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407
+ } ;
+
+static const short int yy_nxt[1659] =
+ { 0,
+ 44, 45, 46, 47, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 44, 44, 44, 44, 49, 50, 51, 44, 44, 52,
+ 44, 44, 44, 44, 44, 53, 44, 44, 44, 44,
+ 44, 44, 44, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 44, 44, 44, 56, 57, 58, 59,
+ 60, 73, 74, 61, 61, 130, 131, 61, 405, 62,
+
+ 44, 63, 64, 76, 73, 77, 130, 131, 78, 90,
+ 91, 92, 90, 91, 92, 110, 206, 111, 105, 106,
+ 107, 105, 106, 107, 44, 65, 61, 66, 67, 68,
+ 59, 60, 69, 108, 61, 61, 108, 235, 61, 110,
+ 70, 111, 63, 64, 235, 71, 113, 114, 113, 114,
+ 207, 136, 137, 136, 137, 115, 405, 115, 138, 399,
+ 138, 116, 117, 116, 117, 401, 65, 61, 81, 81,
+ 82, 83, 81, 81, 81, 81, 81, 84, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+
+ 85, 85, 86, 85, 85, 85, 85, 87, 81, 81,
+ 81, 93, 93, 44, 93, 93, 93, 93, 93, 93,
+ 94, 94, 93, 93, 93, 93, 95, 93, 93, 93,
+ 93, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 93, 93, 93, 99, 100, 99, 100, 399, 140,
+ 141, 213, 101, 214, 101, 102, 142, 102, 140, 141,
+ 144, 145, 146, 248, 249, 142, 144, 145, 146, 164,
+ 165, 166, 169, 170, 171, 147, 190, 191, 192, 251,
+ 249, 147, 257, 258, 103, 300, 103, 118, 118, 119,
+
+ 120, 121, 118, 118, 122, 118, 118, 118, 118, 123,
+ 118, 118, 118, 118, 118, 118, 118, 124, 124, 124,
+ 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+ 124, 125, 124, 124, 124, 124, 126, 127, 118, 128,
+ 132, 133, 132, 133, 197, 197, 232, 197, 241, 260,
+ 258, 266, 164, 165, 166, 300, 267, 398, 242, 169,
+ 170, 171, 281, 282, 283, 292, 293, 296, 297, 198,
+ 198, 233, 198, 288, 284, 248, 249, 289, 251, 249,
+ 134, 397, 134, 148, 149, 150, 151, 148, 148, 152,
+ 148, 148, 153, 148, 148, 148, 148, 148, 148, 148,
+
+ 148, 148, 148, 154, 154, 154, 154, 154, 154, 154,
+ 154, 154, 154, 154, 154, 154, 154, 155, 154, 154,
+ 154, 154, 156, 148, 148, 148, 158, 159, 160, 158,
+ 159, 160, 184, 185, 186, 396, 301, 187, 202, 203,
+ 204, 241, 266, 187, 281, 282, 283, 267, 281, 282,
+ 283, 242, 184, 185, 186, 392, 284, 187, 391, 161,
+ 284, 302, 161, 390, 162, 322, 323, 162, 174, 257,
+ 258, 389, 188, 260, 258, 292, 293, 285, 188, 286,
+ 339, 293, 345, 175, 388, 176, 300, 176, 296, 297,
+ 387, 176, 188, 176, 176, 177, 176, 178, 386, 179,
+
+ 180, 367, 181, 199, 199, 300, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 200, 199, 199, 199,
+ 199, 199, 199, 201, 201, 201, 201, 201, 201, 201,
+ 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
+ 201, 201, 201, 199, 199, 199, 305, 305, 385, 305,
+ 305, 305, 305, 305, 305, 305, 305, 306, 305, 306,
+ 305, 305, 305, 305, 305, 305, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, 306, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, 306, 305, 305, 307, 202,
+ 203, 204, 341, 297, 187, 281, 282, 283, 331, 282,
+
+ 283, 343, 322, 323, 357, 323, 299, 284, 394, 395,
+ 284, 331, 282, 283, 369, 383, 394, 395, 300, 300,
+ 400, 395, 403, 404, 403, 404, 344, 406, 404, 188,
+ 313, 313, 314, 315, 313, 313, 313, 313, 313, 316,
+ 313, 313, 313, 313, 313, 313, 313, 313, 313, 313,
+ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316,
+ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316,
+ 313, 313, 313, 324, 324, 300, 324, 324, 324, 324,
+ 324, 324, 325, 324, 324, 324, 324, 324, 324, 324,
+ 324, 324, 324, 325, 325, 325, 325, 325, 325, 325,
+
+ 325, 325, 325, 325, 325, 325, 325, 325, 325, 325,
+ 325, 325, 325, 324, 324, 324, 346, 304, 318, 347,
+ 300, 304, 318, 347, 346, 346, 346, 346, 346, 346,
+ 316, 316, 300, 352, 316, 316, 316, 316, 316, 382,
+ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316,
+ 353, 381, 380, 379, 353, 378, 377, 376, 375, 356,
+ 374, 373, 351, 350, 372, 371, 366, 340, 338, 365,
+ 316, 316, 316, 325, 325, 364, 325, 325, 325, 325,
+ 325, 325, 363, 325, 325, 325, 325, 325, 325, 325,
+ 325, 325, 325, 362, 361, 360, 359, 358, 356, 355,
+
+ 354, 231, 351, 350, 349, 348, 196, 300, 342, 340,
+ 338, 336, 335, 325, 325, 325, 368, 333, 332, 330,
+ 300, 330, 329, 328, 368, 368, 368, 368, 368, 368,
+ 370, 274, 327, 326, 300, 269, 259, 259, 370, 370,
+ 370, 370, 370, 370, 384, 250, 250, 320, 300, 319,
+ 317, 229, 384, 384, 384, 384, 384, 384, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+
+ 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+ 79, 79, 79, 79, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 104, 104, 104, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
+ 109, 109, 109, 109, 109, 109, 109, 109, 109, 109,
+ 109, 109, 109, 109, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112, 129, 129,
+
+ 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+ 129, 129, 135, 135, 135, 135, 135, 135, 135, 135,
+ 135, 135, 135, 135, 135, 135, 139, 139, 139, 139,
+ 139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 157, 157, 157, 157, 157, 157,
+ 157, 157, 157, 157, 157, 157, 157, 157, 168, 168,
+ 224, 312, 168, 168, 168, 173, 173, 173, 173, 173,
+ 173, 173, 173, 173, 173, 173, 173, 183, 183, 311,
+ 216, 183, 183, 183, 194, 194, 212, 194, 194, 194,
+
+ 194, 194, 194, 194, 194, 194, 194, 194, 196, 196,
+ 205, 196, 196, 196, 196, 196, 196, 196, 196, 196,
+ 196, 196, 208, 208, 205, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 210, 210, 303, 210,
+ 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
+ 211, 211, 300, 211, 211, 211, 211, 211, 211, 211,
+ 211, 211, 211, 211, 215, 215, 215, 215, 215, 215,
+ 215, 215, 215, 215, 215, 215, 215, 215, 217, 217,
+ 298, 217, 217, 190, 217, 217, 217, 217, 190, 294,
+ 189, 217, 219, 219, 189, 172, 219, 219, 219, 223,
+
+ 223, 172, 223, 223, 223, 223, 223, 223, 223, 223,
+ 223, 223, 223, 227, 227, 167, 167, 227, 227, 227,
+ 231, 231, 278, 231, 231, 231, 231, 231, 231, 231,
+ 231, 231, 231, 231, 234, 234, 277, 234, 234, 234,
+ 234, 234, 234, 234, 234, 234, 234, 234, 237, 237,
+ 276, 237, 237, 237, 237, 237, 237, 275, 237, 237,
+ 237, 237, 238, 238, 275, 274, 272, 238, 238, 238,
+ 238, 238, 240, 240, 271, 240, 240, 240, 240, 240,
+ 240, 240, 240, 240, 240, 240, 244, 244, 269, 268,
+ 244, 244, 244, 247, 247, 247, 247, 247, 247, 247,
+
+ 247, 247, 247, 247, 247, 247, 247, 253, 253, 268,
+ 253, 253, 263, 253, 253, 253, 253, 253, 253, 253,
+ 253, 256, 256, 256, 256, 256, 263, 256, 256, 256,
+ 256, 256, 256, 256, 256, 262, 262, 261, 259, 262,
+ 262, 262, 262, 255, 262, 262, 262, 262, 262, 264,
+ 264, 254, 264, 264, 264, 264, 264, 264, 264, 264,
+ 264, 264, 264, 265, 265, 254, 265, 265, 252, 265,
+ 265, 265, 265, 252, 250, 246, 265, 270, 270, 245,
+ 243, 270, 270, 270, 273, 239, 239, 273, 273, 273,
+ 273, 273, 273, 273, 273, 273, 273, 273, 279, 279,
+
+ 236, 279, 279, 279, 279, 279, 279, 279, 279, 279,
+ 279, 279, 280, 280, 236, 280, 280, 280, 280, 280,
+ 280, 280, 280, 280, 280, 280, 287, 287, 287, 290,
+ 290, 290, 291, 291, 291, 291, 291, 291, 291, 291,
+ 291, 291, 291, 291, 291, 291, 295, 295, 295, 295,
+ 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,
+ 299, 299, 230, 299, 299, 299, 299, 299, 299, 230,
+ 299, 299, 299, 299, 305, 305, 229, 305, 305, 305,
+ 305, 305, 305, 305, 305, 305, 305, 308, 308, 228,
+ 308, 308, 308, 308, 308, 308, 308, 308, 308, 308,
+
+ 308, 309, 309, 228, 309, 309, 309, 309, 309, 309,
+ 309, 309, 309, 309, 309, 310, 310, 226, 310, 310,
+ 310, 310, 310, 310, 310, 310, 310, 310, 310, 321,
+ 321, 321, 321, 321, 321, 321, 321, 321, 321, 321,
+ 321, 321, 321, 334, 334, 225, 225, 334, 334, 334,
+ 337, 337, 224, 222, 337, 337, 337, 393, 393, 393,
+ 393, 393, 393, 393, 393, 393, 393, 393, 393, 393,
+ 393, 402, 402, 402, 402, 402, 402, 402, 402, 402,
+ 402, 402, 402, 402, 402, 221, 220, 218, 218, 216,
+ 209, 212, 209, 209, 193, 205, 205, 195, 193, 189,
+
+ 189, 182, 172, 172, 167, 167, 407, 163, 163, 97,
+ 97, 88, 80, 80, 43, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407
+ } ;
+
+static const short int yy_chk[1659] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 5, 5, 3, 3, 27, 27, 3, 406, 3,
+
+ 21, 3, 3, 6, 6, 6, 28, 28, 6, 11,
+ 11, 11, 12, 12, 12, 21, 69, 21, 19, 19,
+ 19, 20, 20, 20, 22, 3, 3, 4, 4, 4,
+ 4, 4, 4, 19, 4, 4, 20, 111, 4, 22,
+ 4, 22, 4, 4, 111, 4, 23, 23, 24, 24,
+ 69, 31, 31, 32, 32, 23, 403, 24, 31, 400,
+ 32, 23, 23, 24, 24, 396, 4, 4, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 17, 17, 18, 18, 394, 33,
+ 33, 78, 17, 78, 18, 17, 33, 18, 34, 34,
+ 35, 35, 35, 129, 129, 34, 36, 36, 36, 45,
+ 45, 45, 49, 49, 49, 35, 60, 60, 60, 131,
+ 131, 36, 139, 139, 17, 384, 18, 25, 25, 25,
+
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 29, 29, 30, 30, 64, 108, 110, 116, 122, 141,
+ 141, 149, 164, 164, 164, 383, 149, 382, 122, 169,
+ 169, 169, 176, 176, 176, 181, 181, 188, 188, 64,
+ 108, 110, 116, 179, 176, 247, 247, 179, 249, 249,
+ 29, 381, 30, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 39, 39, 39, 40,
+ 40, 40, 56, 56, 56, 380, 195, 56, 66, 66,
+ 66, 240, 266, 66, 175, 175, 175, 266, 177, 177,
+ 177, 240, 184, 184, 184, 378, 175, 184, 377, 39,
+ 177, 195, 40, 376, 39, 255, 255, 40, 52, 256,
+ 256, 375, 56, 258, 258, 291, 291, 175, 66, 177,
+ 293, 293, 301, 52, 374, 52, 301, 52, 295, 295,
+ 373, 52, 184, 52, 52, 52, 52, 52, 372, 52,
+
+ 52, 343, 52, 65, 65, 343, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 201, 201, 371, 201,
+ 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
+ 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
+ 201, 201, 201, 201, 201, 201, 201, 201, 201, 201,
+ 201, 201, 201, 201, 201, 201, 201, 201, 201, 202,
+ 202, 202, 297, 297, 202, 281, 281, 281, 284, 284,
+
+ 284, 300, 321, 321, 323, 323, 300, 281, 379, 379,
+ 284, 331, 331, 331, 345, 367, 393, 393, 345, 367,
+ 395, 395, 401, 401, 402, 402, 300, 404, 404, 202,
+ 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 222, 222, 222, 222, 222, 222, 222,
+ 222, 222, 222, 269, 269, 370, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 269, 269, 269, 269,
+
+ 269, 269, 269, 269, 269, 269, 269, 269, 269, 269,
+ 269, 269, 269, 269, 269, 269, 302, 457, 462, 466,
+ 302, 457, 462, 466, 302, 302, 302, 302, 302, 302,
+ 316, 316, 369, 316, 316, 316, 316, 316, 316, 366,
+ 316, 316, 316, 316, 316, 316, 316, 316, 316, 316,
+ 467, 364, 363, 362, 467, 361, 360, 359, 358, 357,
+ 355, 354, 352, 351, 349, 348, 342, 341, 339, 336,
+ 316, 316, 316, 325, 325, 335, 325, 325, 325, 325,
+ 325, 325, 333, 325, 325, 325, 325, 325, 325, 325,
+ 325, 325, 325, 332, 329, 328, 327, 326, 322, 320,
+
+ 319, 317, 315, 314, 312, 311, 303, 299, 298, 296,
+ 292, 289, 288, 325, 325, 325, 344, 286, 285, 283,
+ 344, 282, 277, 276, 344, 344, 344, 344, 344, 344,
+ 346, 274, 272, 271, 346, 267, 260, 257, 346, 346,
+ 346, 346, 346, 346, 368, 251, 248, 246, 368, 245,
+ 232, 229, 368, 368, 368, 368, 368, 368, 408, 408,
+ 408, 408, 408, 408, 408, 408, 408, 408, 408, 408,
+ 408, 408, 409, 409, 409, 409, 409, 409, 409, 409,
+ 409, 409, 409, 409, 409, 409, 410, 410, 410, 410,
+ 410, 410, 410, 410, 410, 410, 410, 410, 410, 410,
+
+ 411, 411, 411, 411, 411, 411, 411, 411, 411, 411,
+ 411, 411, 411, 411, 412, 412, 412, 412, 412, 412,
+ 412, 412, 412, 412, 412, 412, 412, 412, 413, 413,
+ 413, 413, 413, 413, 413, 413, 413, 413, 413, 413,
+ 413, 413, 414, 414, 414, 414, 414, 414, 414, 414,
+ 414, 414, 414, 414, 414, 414, 415, 415, 415, 415,
+ 415, 415, 415, 415, 415, 415, 415, 415, 415, 415,
+ 416, 416, 416, 416, 416, 416, 416, 416, 416, 416,
+ 416, 416, 416, 416, 417, 417, 417, 417, 417, 417,
+ 417, 417, 417, 417, 417, 417, 417, 417, 418, 418,
+
+ 418, 418, 418, 418, 418, 418, 418, 418, 418, 418,
+ 418, 418, 419, 419, 419, 419, 419, 419, 419, 419,
+ 419, 419, 419, 419, 419, 419, 420, 420, 420, 420,
+ 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
+ 421, 421, 421, 421, 421, 421, 421, 421, 421, 421,
+ 421, 421, 421, 421, 422, 422, 422, 422, 422, 422,
+ 422, 422, 422, 422, 422, 422, 422, 422, 423, 423,
+ 224, 221, 423, 423, 423, 424, 424, 424, 424, 424,
+ 424, 424, 424, 424, 424, 424, 424, 425, 425, 220,
+ 215, 425, 425, 425, 426, 426, 212, 426, 426, 426,
+
+ 426, 426, 426, 426, 426, 426, 426, 426, 427, 427,
+ 204, 427, 427, 427, 427, 427, 427, 427, 427, 427,
+ 427, 427, 428, 428, 203, 428, 428, 428, 428, 428,
+ 428, 428, 428, 428, 428, 428, 429, 429, 197, 429,
+ 429, 429, 429, 429, 429, 429, 429, 429, 429, 429,
+ 430, 430, 194, 430, 430, 430, 430, 430, 430, 430,
+ 430, 430, 430, 430, 431, 431, 431, 431, 431, 431,
+ 431, 431, 431, 431, 431, 431, 431, 431, 432, 432,
+ 193, 432, 432, 192, 432, 432, 432, 432, 191, 187,
+ 186, 432, 433, 433, 185, 171, 433, 433, 433, 434,
+
+ 434, 170, 434, 434, 434, 434, 434, 434, 434, 434,
+ 434, 434, 434, 435, 435, 166, 165, 435, 435, 435,
+ 436, 436, 163, 436, 436, 436, 436, 436, 436, 436,
+ 436, 436, 436, 436, 437, 437, 162, 437, 437, 437,
+ 437, 437, 437, 437, 437, 437, 437, 437, 438, 438,
+ 161, 438, 438, 438, 438, 438, 438, 160, 438, 438,
+ 438, 438, 439, 439, 159, 158, 156, 439, 439, 439,
+ 439, 439, 440, 440, 155, 440, 440, 440, 440, 440,
+ 440, 440, 440, 440, 440, 440, 441, 441, 152, 151,
+ 441, 441, 441, 442, 442, 442, 442, 442, 442, 442,
+
+ 442, 442, 442, 442, 442, 442, 442, 443, 443, 150,
+ 443, 443, 145, 443, 443, 443, 443, 443, 443, 443,
+ 443, 444, 444, 444, 444, 444, 144, 444, 444, 444,
+ 444, 444, 444, 444, 444, 445, 445, 142, 140, 445,
+ 445, 445, 445, 138, 445, 445, 445, 445, 445, 446,
+ 446, 137, 446, 446, 446, 446, 446, 446, 446, 446,
+ 446, 446, 446, 447, 447, 136, 447, 447, 133, 447,
+ 447, 447, 447, 132, 130, 126, 447, 448, 448, 125,
+ 123, 448, 448, 448, 449, 120, 119, 449, 449, 449,
+ 449, 449, 449, 449, 449, 449, 449, 449, 450, 450,
+
+ 114, 450, 450, 450, 450, 450, 450, 450, 450, 450,
+ 450, 450, 451, 451, 113, 451, 451, 451, 451, 451,
+ 451, 451, 451, 451, 451, 451, 452, 452, 452, 453,
+ 453, 453, 454, 454, 454, 454, 454, 454, 454, 454,
+ 454, 454, 454, 454, 454, 454, 455, 455, 455, 455,
+ 455, 455, 455, 455, 455, 455, 455, 455, 455, 455,
+ 456, 456, 106, 456, 456, 456, 456, 456, 456, 105,
+ 456, 456, 456, 456, 458, 458, 102, 458, 458, 458,
+ 458, 458, 458, 458, 458, 458, 458, 459, 459, 100,
+ 459, 459, 459, 459, 459, 459, 459, 459, 459, 459,
+
+ 459, 460, 460, 99, 460, 460, 460, 460, 460, 460,
+ 460, 460, 460, 460, 460, 461, 461, 95, 461, 461,
+ 461, 461, 461, 461, 461, 461, 461, 461, 461, 463,
+ 463, 463, 463, 463, 463, 463, 463, 463, 463, 463,
+ 463, 463, 463, 464, 464, 92, 91, 464, 464, 464,
+ 465, 465, 90, 88, 465, 465, 465, 468, 468, 468,
+ 468, 468, 468, 468, 468, 468, 468, 468, 468, 468,
+ 468, 469, 469, 469, 469, 469, 469, 469, 469, 469,
+ 469, 469, 469, 469, 469, 87, 86, 83, 82, 79,
+ 77, 76, 74, 73, 70, 68, 67, 63, 62, 58,
+
+ 57, 53, 51, 50, 47, 46, 43, 42, 41, 16,
+ 15, 10, 8, 7, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407, 407, 407,
+ 407, 407, 407, 407, 407, 407, 407, 407
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+char *yytext;
+# line 1 "scan.l"
+/* scan.l - scanner for flex input */
+# line 4 "scan.l"
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/initscan.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ */
+
+#include "flexdef.h"
+#include "parse.h"
+
+#define ACTION_ECHO add_action( yytext )
+#define MARK_END_OF_PROLOG mark_prolog();
+
+#define YY_DECL \
+ int flexscan()
+
+#define RETURNCHAR \
+ yylval = (unsigned char) yytext[0]; \
+ return CHAR;
+
+#define RETURNNAME \
+ strcpy( nmstr, yytext ); \
+ return NAME;
+
+#define PUT_BACK_STRING(str, start) \
+ for ( i = strlen( str ) - 1; i >= start; --i ) \
+ unput((str)[i])
+
+#define CHECK_REJECT(str) \
+ if ( all_upper( str ) ) \
+ reject = true;
+
+#define CHECK_YYMORE(str) \
+ if ( all_lower( str ) ) \
+ yymore_used = true;
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy_current_buffer->yy_is_interactive ) \
+ { \
+ int c = getc( yyin ); \
+ result = c == EOF ? 0 : 1; \
+ buf[0] = (char) c; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+# line 82 "scan.l"
+
+ static int bracelevel, didadef, indented_code, checking_used;
+
+ int doing_codeblock = false;
+ int i;
+ Char nmdef[MAXLINE], myesc();
+
+
+
+ if ( yy_init )
+ {
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( yy_current_buffer )
+ yy_init_buffer( yy_current_buffer, yyin );
+ else
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+
+ yy_init = 0;
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+ if ( yy_bp[-1] == '\n' )
+ ++yy_current_state;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 1615 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_USER_ACTION
+# line 90 "scan.l"
+indented_code = true; BEGIN(CODEBLOCK);
+ YY_BREAK
+case 2:
+YY_USER_ACTION
+# line 91 "scan.l"
+ACTION_ECHO; BEGIN(C_COMMENT);
+ YY_BREAK
+case 3:
+YY_USER_ACTION
+# line 92 "scan.l"
+return SCDECL;
+ YY_BREAK
+case 4:
+YY_USER_ACTION
+# line 93 "scan.l"
+return XSCDECL;
+ YY_BREAK
+case 5:
+YY_USER_ACTION
+# line 94 "scan.l"
+{
+ ++linenum;
+ line_directive_out( (FILE *) 0 );
+ indented_code = false;
+ BEGIN(CODEBLOCK);
+ }
+ YY_BREAK
+case 6:
+YY_USER_ACTION
+# line 101 "scan.l"
+return WHITESPACE;
+ YY_BREAK
+case 7:
+YY_USER_ACTION
+# line 103 "scan.l"
+{
+ sectnum = 2;
+ bracelevel = 0;
+ mark_defs1();
+ line_directive_out( (FILE *) 0 );
+ BEGIN(SECT2PROLOG);
+ return SECTEND;
+ }
+ YY_BREAK
+case 8:
+YY_USER_ACTION
+# line 112 "scan.l"
+{
+ if ( lex_compat )
+ warn( "%pointer incompatible with -l option" );
+ else
+ yytext_is_array = false;
+ ++linenum;
+ }
+ YY_BREAK
+case 9:
+YY_USER_ACTION
+# line 119 "scan.l"
+{
+ if ( C_plus_plus )
+ warn( "%array incompatible with -+ option" );
+ else
+ yytext_is_array = true;
+ ++linenum;
+ }
+ YY_BREAK
+case 10:
+YY_USER_ACTION
+# line 127 "scan.l"
+{
+ warn( "%used/%unused have been deprecated" );
+ checking_used = REALLY_USED; BEGIN(USED_LIST);
+ }
+ YY_BREAK
+case 11:
+YY_USER_ACTION
+# line 131 "scan.l"
+{
+ warn( "%used/%unused have been deprecated" );
+ checking_used = REALLY_NOT_USED; BEGIN(USED_LIST);
+ }
+ YY_BREAK
+case 12:
+YY_USER_ACTION
+# line 137 "scan.l"
+++linenum; /* ignore */
+ YY_BREAK
+case 13:
+YY_USER_ACTION
+# line 139 "scan.l"
+synerr( "unrecognized '%' directive" );
+ YY_BREAK
+case 14:
+YY_USER_ACTION
+# line 141 "scan.l"
+{
+ strcpy( nmstr, yytext );
+ didadef = false;
+ BEGIN(PICKUPDEF);
+ }
+ YY_BREAK
+case 15:
+YY_USER_ACTION
+# line 147 "scan.l"
+RETURNNAME;
+ YY_BREAK
+case 16:
+YY_USER_ACTION
+# line 148 "scan.l"
+++linenum; /* allows blank lines in section 1 */
+ YY_BREAK
+case 17:
+YY_USER_ACTION
+# line 149 "scan.l"
+++linenum; return '\n';
+ YY_BREAK
+case 18:
+YY_USER_ACTION
+# line 152 "scan.l"
+ACTION_ECHO; BEGIN(INITIAL);
+ YY_BREAK
+case 19:
+YY_USER_ACTION
+# line 153 "scan.l"
+++linenum; ACTION_ECHO; BEGIN(INITIAL);
+ YY_BREAK
+case 20:
+YY_USER_ACTION
+# line 154 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 21:
+YY_USER_ACTION
+# line 155 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 22:
+YY_USER_ACTION
+# line 156 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case 23:
+YY_USER_ACTION
+# line 159 "scan.l"
+++linenum; BEGIN(INITIAL);
+ YY_BREAK
+case 24:
+YY_USER_ACTION
+# line 160 "scan.l"
+ACTION_ECHO; CHECK_REJECT(yytext);
+ YY_BREAK
+case 25:
+YY_USER_ACTION
+# line 161 "scan.l"
+ACTION_ECHO; CHECK_YYMORE(yytext);
+ YY_BREAK
+case 26:
+YY_USER_ACTION
+# line 162 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 27:
+YY_USER_ACTION
+# line 163 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( indented_code )
+ BEGIN(INITIAL);
+ }
+ YY_BREAK
+case 28:
+YY_USER_ACTION
+# line 171 "scan.l"
+/* separates name and definition */
+ YY_BREAK
+case 29:
+YY_USER_ACTION
+# line 173 "scan.l"
+{
+ strcpy( (char *) nmdef, yytext );
+
+ /* Skip trailing whitespace. */
+ for ( i = strlen( (char *) nmdef ) - 1;
+ i >= 0 && (nmdef[i] == ' ' || nmdef[i] == '\t');
+ --i )
+ ;
+
+ nmdef[i + 1] = '\0';
+
+ ndinstal( nmstr, nmdef );
+ didadef = true;
+ }
+ YY_BREAK
+case 30:
+YY_USER_ACTION
+# line 188 "scan.l"
+{
+ if ( ! didadef )
+ synerr( "incomplete name definition" );
+ BEGIN(INITIAL);
+ ++linenum;
+ }
+ YY_BREAK
+case 31:
+YY_USER_ACTION
+# line 195 "scan.l"
+++linenum; BEGIN(INITIAL); RETURNNAME;
+ YY_BREAK
+case 32:
+YY_USER_ACTION
+# line 198 "scan.l"
+++linenum; BEGIN(INITIAL);
+ YY_BREAK
+case 33:
+YY_USER_ACTION
+# line 199 "scan.l"
+
+ YY_BREAK
+case 34:
+YY_USER_ACTION
+# line 200 "scan.l"
+{
+ if ( all_upper( yytext ) )
+ reject_really_used = checking_used;
+ else
+ synerr(
+ "unrecognized %used/%unused construct" );
+ }
+ YY_BREAK
+case 35:
+YY_USER_ACTION
+# line 207 "scan.l"
+{
+ if ( all_lower( yytext ) )
+ yymore_really_used = checking_used;
+ else
+ synerr(
+ "unrecognized %used/%unused construct" );
+ }
+ YY_BREAK
+case 36:
+YY_USER_ACTION
+# line 214 "scan.l"
+synerr( "unrecognized %used/%unused construct" );
+ YY_BREAK
+case 37:
+YY_USER_ACTION
+# line 217 "scan.l"
+++bracelevel; yyless( 2 ); /* eat only %{ */
+ YY_BREAK
+case 38:
+YY_USER_ACTION
+# line 218 "scan.l"
+--bracelevel; yyless( 2 ); /* eat only %} */
+ YY_BREAK
+case 39:
+YY_USER_ACTION
+# line 220 "scan.l"
+ACTION_ECHO; /* indented code in prolog */
+ YY_BREAK
+case 40:
+YY_USER_ACTION
+# line 222 "scan.l"
+{ /* non-indented code */
+ if ( bracelevel <= 0 )
+ { /* not in %{ ... %} */
+ yyless( 0 ); /* put it all back */
+ mark_prolog();
+ BEGIN(SECT2);
+ }
+ else
+ ACTION_ECHO;
+ }
+ YY_BREAK
+case 41:
+YY_USER_ACTION
+# line 233 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 42:
+YY_USER_ACTION
+# line 234 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case YY_STATE_EOF(SECT2PROLOG):
+# line 236 "scan.l"
+{
+ mark_prolog();
+ sectnum = 0;
+ yyterminate(); /* to stop the parser */
+ }
+ YY_BREAK
+case 43:
+YY_USER_ACTION
+# line 242 "scan.l"
+++linenum; /* allow blank lines in section 2 */
+ YY_BREAK
+case 44:
+YY_USER_ACTION
+# line 244 "scan.l"
+{
+ indented_code = (yytext[0] != '%');
+ doing_codeblock = true;
+ bracelevel = 1;
+
+ if ( indented_code )
+ ACTION_ECHO;
+
+ BEGIN(CODEBLOCK_2);
+ }
+ YY_BREAK
+case 45:
+YY_USER_ACTION
+# line 255 "scan.l"
+BEGIN(SC); return '<';
+ YY_BREAK
+case 46:
+YY_USER_ACTION
+# line 256 "scan.l"
+return '^';
+ YY_BREAK
+case 47:
+YY_USER_ACTION
+# line 257 "scan.l"
+BEGIN(QUOTE); return '"';
+ YY_BREAK
+case 48:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 258 "scan.l"
+BEGIN(NUM); return '{';
+ YY_BREAK
+case 49:
+YY_USER_ACTION
+# line 259 "scan.l"
+BEGIN(BRACEERROR);
+ YY_BREAK
+case 50:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 260 "scan.l"
+return '$';
+ YY_BREAK
+case 51:
+YY_USER_ACTION
+# line 262 "scan.l"
+{
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+ return '\n';
+ }
+ YY_BREAK
+case 52:
+YY_USER_ACTION
+# line 267 "scan.l"
+continued_action = true; ++linenum; return '\n';
+ YY_BREAK
+case 53:
+YY_USER_ACTION
+# line 269 "scan.l"
+{
+ /* This rule is separate from the one below because
+ * otherwise we get variable trailing context, so
+ * we can't build the scanner using -{f,F}.
+ */
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ return '\n';
+ }
+ YY_BREAK
+case 54:
+YY_USER_ACTION
+# line 280 "scan.l"
+{
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ unput( '\n' ); /* so <ACTION> sees it */
+ return '\n';
+ }
+ YY_BREAK
+case 55:
+YY_USER_ACTION
+# line 288 "scan.l"
+return EOF_OP;
+ YY_BREAK
+case 56:
+YY_USER_ACTION
+# line 290 "scan.l"
+{
+ sectnum = 3;
+ BEGIN(SECT3);
+ yyterminate(); /* to stop the parser */
+ }
+ YY_BREAK
+case 57:
+YY_USER_ACTION
+# line 296 "scan.l"
+{
+ int cclval;
+
+ strcpy( nmstr, yytext );
+
+ /* Check to see if we've already encountered this
+ * ccl.
+ */
+ if ( (cclval = ccllookup( (Char *) nmstr )) )
+ {
+ if ( input() != ']' )
+ synerr( "bad character class" );
+
+ yylval = cclval;
+ ++cclreuse;
+ return PREVCCL;
+ }
+ else
+ {
+ /* We fudge a bit. We know that this ccl will
+ * soon be numbered as lastccl + 1 by cclinit.
+ */
+ cclinstal( (Char *) nmstr, lastccl + 1 );
+
+ /* Push back everything but the leading bracket
+ * so the ccl can be rescanned.
+ */
+ yyless( 1 );
+
+ BEGIN(FIRSTCCL);
+ return '[';
+ }
+ }
+ YY_BREAK
+case 58:
+YY_USER_ACTION
+# line 330 "scan.l"
+{
+ register Char *nmdefptr;
+ Char *ndlookup();
+
+ strcpy( nmstr, yytext + 1 );
+ nmstr[yyleng - 2] = '\0'; /* chop trailing brace */
+
+ if ( ! (nmdefptr = ndlookup( nmstr )) )
+ format_synerr( "undefined definition {%s}",
+ nmstr );
+
+ else
+ { /* push back name surrounded by ()'s */
+ int len = strlen( (char *) nmdefptr );
+
+ if ( lex_compat || nmdefptr[0] == '^' ||
+ (len > 0 && nmdefptr[len - 1] == '$') )
+ { /* don't use ()'s after all */
+ PUT_BACK_STRING((char *) nmdefptr, 0);
+
+ if ( nmdefptr[0] == '^' )
+ BEGIN(CARETISBOL);
+ }
+
+ else
+ {
+ unput(')');
+ PUT_BACK_STRING((char *) nmdefptr, 0);
+ unput('(');
+ }
+ }
+ }
+ YY_BREAK
+case 59:
+YY_USER_ACTION
+# line 363 "scan.l"
+return (unsigned char) yytext[0];
+ YY_BREAK
+case 60:
+YY_USER_ACTION
+# line 364 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+case 61:
+YY_USER_ACTION
+# line 367 "scan.l"
+return (unsigned char) yytext[0];
+ YY_BREAK
+case 62:
+YY_USER_ACTION
+# line 368 "scan.l"
+BEGIN(SECT2); return '>';
+ YY_BREAK
+case 63:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 369 "scan.l"
+BEGIN(CARETISBOL); return '>';
+ YY_BREAK
+case 64:
+YY_USER_ACTION
+# line 370 "scan.l"
+RETURNNAME;
+ YY_BREAK
+case 65:
+YY_USER_ACTION
+# line 371 "scan.l"
+{
+ format_synerr( "bad <start condition>: %s", yytext );
+ }
+ YY_BREAK
+case 66:
+YY_USER_ACTION
+# line 375 "scan.l"
+BEGIN(SECT2); return '^';
+ YY_BREAK
+case 67:
+YY_USER_ACTION
+# line 378 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+case 68:
+YY_USER_ACTION
+# line 379 "scan.l"
+BEGIN(SECT2); return '"';
+ YY_BREAK
+case 69:
+YY_USER_ACTION
+# line 381 "scan.l"
+{
+ synerr( "missing quote" );
+ BEGIN(SECT2);
+ ++linenum;
+ return '"';
+ }
+ YY_BREAK
+case 70:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 389 "scan.l"
+BEGIN(CCL); return '^';
+ YY_BREAK
+case 71:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 390 "scan.l"
+return '^';
+ YY_BREAK
+case 72:
+YY_USER_ACTION
+# line 391 "scan.l"
+BEGIN(CCL); RETURNCHAR;
+ YY_BREAK
+case 73:
+*yy_cp = yy_hold_char; /* undo effects of setting up yytext */
+yy_c_buf_p = yy_cp = yy_bp + 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_USER_ACTION
+# line 393 "scan.l"
+return '-';
+ YY_BREAK
+case 74:
+YY_USER_ACTION
+# line 394 "scan.l"
+RETURNCHAR;
+ YY_BREAK
+case 75:
+YY_USER_ACTION
+# line 395 "scan.l"
+BEGIN(SECT2); return ']';
+ YY_BREAK
+case 76:
+YY_USER_ACTION
+# line 396 "scan.l"
+{
+ synerr( "bad character class" );
+ BEGIN(SECT2);
+ return ']';
+ }
+ YY_BREAK
+case 77:
+YY_USER_ACTION
+# line 403 "scan.l"
+{
+ yylval = myctoi( yytext );
+ return NUMBER;
+ }
+ YY_BREAK
+case 78:
+YY_USER_ACTION
+# line 408 "scan.l"
+return ',';
+ YY_BREAK
+case 79:
+YY_USER_ACTION
+# line 409 "scan.l"
+BEGIN(SECT2); return '}';
+ YY_BREAK
+case 80:
+YY_USER_ACTION
+# line 411 "scan.l"
+{
+ synerr( "bad character inside {}'s" );
+ BEGIN(SECT2);
+ return '}';
+ }
+ YY_BREAK
+case 81:
+YY_USER_ACTION
+# line 417 "scan.l"
+{
+ synerr( "missing }" );
+ BEGIN(SECT2);
+ ++linenum;
+ return '}';
+ }
+ YY_BREAK
+case 82:
+YY_USER_ACTION
+# line 425 "scan.l"
+synerr( "bad name in {}'s" ); BEGIN(SECT2);
+ YY_BREAK
+case 83:
+YY_USER_ACTION
+# line 426 "scan.l"
+synerr( "missing }" ); ++linenum; BEGIN(SECT2);
+ YY_BREAK
+case 84:
+YY_USER_ACTION
+# line 429 "scan.l"
+ACTION_ECHO; BEGIN(ACTION_COMMENT);
+ YY_BREAK
+case 85:
+YY_USER_ACTION
+# line 430 "scan.l"
+bracelevel = 0;
+ YY_BREAK
+case 86:
+YY_USER_ACTION
+# line 431 "scan.l"
+{
+ ACTION_ECHO;
+ CHECK_REJECT(yytext);
+ }
+ YY_BREAK
+case 87:
+YY_USER_ACTION
+# line 435 "scan.l"
+{
+ ACTION_ECHO;
+ CHECK_YYMORE(yytext);
+ }
+ YY_BREAK
+case 88:
+YY_USER_ACTION
+# line 439 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 89:
+YY_USER_ACTION
+# line 440 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 ||
+ (doing_codeblock && indented_code) )
+ {
+ if ( ! doing_codeblock )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_codeblock = false;
+ BEGIN(SECT2);
+ }
+ }
+ YY_BREAK
+ /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */
+case 90:
+YY_USER_ACTION
+# line 456 "scan.l"
+ACTION_ECHO; ++bracelevel;
+ YY_BREAK
+case 91:
+YY_USER_ACTION
+# line 457 "scan.l"
+ACTION_ECHO; --bracelevel;
+ YY_BREAK
+case 92:
+YY_USER_ACTION
+# line 458 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 93:
+YY_USER_ACTION
+# line 459 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 94:
+YY_USER_ACTION
+# line 460 "scan.l"
+ACTION_ECHO; BEGIN(ACTION_COMMENT);
+ YY_BREAK
+case 95:
+YY_USER_ACTION
+# line 461 "scan.l"
+ACTION_ECHO; /* character constant */
+ YY_BREAK
+case 96:
+YY_USER_ACTION
+# line 462 "scan.l"
+ACTION_ECHO; BEGIN(ACTION_STRING);
+ YY_BREAK
+case 97:
+YY_USER_ACTION
+# line 463 "scan.l"
+{
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 )
+ {
+ add_action( "\tYY_BREAK\n" );
+ BEGIN(SECT2);
+ }
+ }
+ YY_BREAK
+case 98:
+YY_USER_ACTION
+# line 472 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 99:
+YY_USER_ACTION
+# line 474 "scan.l"
+{
+ ACTION_ECHO;
+ if ( doing_codeblock )
+ BEGIN(CODEBLOCK_2);
+ else
+ BEGIN(ACTION);
+ }
+ YY_BREAK
+case 100:
+YY_USER_ACTION
+# line 482 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 101:
+YY_USER_ACTION
+# line 483 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 102:
+YY_USER_ACTION
+# line 484 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case 103:
+YY_USER_ACTION
+# line 486 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 104:
+YY_USER_ACTION
+# line 487 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case 105:
+YY_USER_ACTION
+# line 488 "scan.l"
+++linenum; ACTION_ECHO;
+ YY_BREAK
+case 106:
+YY_USER_ACTION
+# line 489 "scan.l"
+ACTION_ECHO; BEGIN(ACTION);
+ YY_BREAK
+case 107:
+YY_USER_ACTION
+# line 490 "scan.l"
+ACTION_ECHO;
+ YY_BREAK
+case YY_STATE_EOF(ACTION):
+case YY_STATE_EOF(ACTION_COMMENT):
+case YY_STATE_EOF(ACTION_STRING):
+# line 492 "scan.l"
+{
+ synerr( "EOF encountered inside an action" );
+ yyterminate();
+ }
+ YY_BREAK
+case 108:
+YY_USER_ACTION
+# line 498 "scan.l"
+{
+ yylval = myesc( (Char *) yytext );
+ return CHAR;
+ }
+ YY_BREAK
+case 109:
+YY_USER_ACTION
+# line 503 "scan.l"
+{
+ yylval = myesc( (Char *) yytext );
+ BEGIN(CCL);
+ return CHAR;
+ }
+ YY_BREAK
+case 110:
+YY_USER_ACTION
+# line 510 "scan.l"
+ECHO;
+ YY_BREAK
+case YY_STATE_EOF(SECT3):
+# line 511 "scan.l"
+sectnum = 0; yyterminate();
+ YY_BREAK
+case 111:
+YY_USER_ACTION
+# line 513 "scan.l"
+format_synerr( "bad character: %s", yytext );
+ YY_BREAK
+case 112:
+YY_USER_ACTION
+# line 515 "scan.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(SECT2):
+case YY_STATE_EOF(CODEBLOCK):
+case YY_STATE_EOF(PICKUPDEF):
+case YY_STATE_EOF(SC):
+case YY_STATE_EOF(CARETISBOL):
+case YY_STATE_EOF(NUM):
+case YY_STATE_EOF(QUOTE):
+case YY_STATE_EOF(FIRSTCCL):
+case YY_STATE_EOF(CCL):
+case YY_STATE_EOF(RECOVER):
+case YY_STATE_EOF(BRACEERROR):
+case YY_STATE_EOF(C_COMMENT):
+case YY_STATE_EOF(PERCENT_BRACE_ACTION):
+case YY_STATE_EOF(USED_LIST):
+case YY_STATE_EOF(CODEBLOCK_2):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = yy_cp - yytext_ptr - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr - 1; /* copy prev. char, too */
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a singled characater, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = yy_c_buf_p - yytext_ptr;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset = yy_c_buf_p - b->yy_ch_buf;
+
+ b->yy_buf_size *= 2;
+ b->yy_ch_buf = (char *)
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move - YY_MORE_ADJ == 1 )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ /* yytext begins at the second character in yy_ch_buf; the first
+ * character is the one which preceded it before reading in the latest
+ * buffer; it needs to be kept around in case it's a newline, so
+ * yy_get_previous_state() will have with '^' rules active.
+ */
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[1];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ register char *yy_bp = yytext_ptr;
+
+ yy_current_state = yy_start;
+ if ( yy_bp[-1] == '\n' )
+ ++yy_current_state;
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ register int yy_is_jam;
+ register char *yy_cp = yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 408 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 407);
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += dest - source;
+ yy_bp += dest - source;
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ if ( yy_cp > yy_bp && yy_cp[-1] == '\n' )
+ yy_cp[-2] = '\n';
+
+ *--yy_cp = (char) c;
+
+
+ /* Note: the formal parameter *must* be called "yy_bp" for this
+ * macro to now work correctly.
+ */
+ YY_DO_BEFORE_ACTION; /* set up yytext again */
+ }
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yytext_ptr = yy_c_buf_p;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ {
+ yy_c_buf_p =
+ yytext_ptr + YY_MORE_ADJ;
+ return EOF;
+ }
+
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+ break;
+
+ case EOB_ACT_LAST_MATCH:
+#ifdef __cplusplus
+ YY_FATAL_ERROR(
+ "unexpected last match in yyinput()" );
+#else
+ YY_FATAL_ERROR(
+ "unexpected last match in input()" );
+#endif
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+ return c;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ yy_flex_free( (void *) b->yy_ch_buf );
+ yy_flex_free( (void *) b );
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+ {
+ b->yy_input_file = file;
+
+ /* We put in the '\n' and start reading from [1] so that an
+ * initial match-at-newline will be true.
+ */
+
+ b->yy_ch_buf[0] = '\n';
+ b->yy_n_chars = 1;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[1];
+
+ b->yy_is_interactive = file ? isatty( fileno(file) ) : 0;
+
+ b->yy_fill_buffer = 1;
+
+ b->yy_buffer_status = YY_BUFFER_NEW;
+ }
+
+
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ int new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+
+
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+
+
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( const char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( 1 );
+ }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n - YY_MORE_ADJ; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, const char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+const char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( unsigned int size )
+#else
+static void *yy_flex_alloc( size )
+unsigned int size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, unsigned int size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+unsigned int size;
+#endif
+ {
+ return (void *) realloc( ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+# line 515 "scan.l"
+
+
+
+int yywrap()
+ {
+ if ( --num_input_files > 0 )
+ {
+ set_input_file( *++input_files );
+ return 0;
+ }
+
+ else
+ return 1;
+ }
+
+
+/* set_input_file - open the given file (if NULL, stdin) for scanning */
+
+void set_input_file( file )
+char *file;
+ {
+ if ( file )
+ {
+ infilename = file;
+ yyin = fopen( infilename, "r" );
+
+ if ( yyin == NULL )
+ lerrsf( "can't open %s", file );
+ }
+
+ else
+ {
+ yyin = stdin;
+ infilename = "<stdin>";
+ }
+ }
+
+
+/* Wrapper routines for accessing the scanner's malloc routines. */
+
+void *flex_alloc( size )
+unsigned int size;
+ {
+ return yy_flex_alloc( size );
+ }
+
+void *flex_realloc( ptr, size )
+void *ptr;
+unsigned int size;
+ {
+ return yy_flex_realloc( ptr, size );
+ }
+
+void flex_free( ptr )
+void *ptr;
+ {
+ yy_flex_free( ptr );
+ }
diff --git a/usr.bin/lex/lex.1 b/usr.bin/lex/lex.1
new file mode 100644
index 0000000..af72f35
--- /dev/null
+++ b/usr.bin/lex/lex.1
@@ -0,0 +1,1001 @@
+.TH FLEX 1 "November 1993" "Version 2.4"
+.SH NAME
+flex \- fast lexical analyzer generator
+.SH SYNOPSIS
+.B flex
+.B [\-bcdfhilnpstvwBFILTV78+ \-C[aefFmr] \-Pprefix \-Sskeleton]
+.I [filename ...]
+.SH DESCRIPTION
+.I flex
+is a tool for generating
+.I scanners:
+programs which recognized lexical patterns in text.
+.I flex
+reads
+the given input files, or its standard input if no file names are given,
+for a description of a scanner to generate. The description is in
+the form of pairs
+of regular expressions and C code, called
+.I rules. flex
+generates as output a C source file,
+.B lex.yy.c,
+which defines a routine
+.B yylex().
+This file is compiled and linked with the
+.B \-ll
+library to produce an executable. When the executable is run,
+it analyzes its input for occurrences
+of the regular expressions. Whenever it finds one, it executes
+the corresponding C code.
+.PP
+For full documentation, see
+.B lexdoc(1).
+This manual entry is intended for use as a quick reference.
+.SH OPTIONS
+.I flex
+has the following options:
+.TP
+.B \-b
+generate backing-up information to
+.I lex.backup.
+This is a list of scanner states which require backing up and the input
+characters on which they do so. By adding rules one can remove
+backing-up states. If all backing-up states are eliminated and
+.B \-Cf
+or
+.B \-CF
+is used, the generated scanner will run faster.
+.TP
+.B \-c
+is a do-nothing, deprecated option included for POSIX compliance.
+.IP
+.B NOTE:
+in previous releases of
+.I flex
+.B \-c
+specified table-compression options. This functionality is
+now given by the
+.B \-C
+flag. To ease the the impact of this change, when
+.I flex
+encounters
+.B \-c,
+it currently issues a warning message and assumes that
+.B \-C
+was desired instead. In the future this "promotion" of
+.B \-c
+to
+.B \-C
+will go away in the name of full POSIX compliance (unless
+the POSIX meaning is removed first).
+.TP
+.B \-d
+makes the generated scanner run in
+.I debug
+mode. Whenever a pattern is recognized and the global
+.B yy_flex_debug
+is non-zero (which is the default), the scanner will
+write to
+.I stderr
+a line of the form:
+.nf
+
+ --accepting rule at line 53 ("the matched text")
+
+.fi
+The line number refers to the location of the rule in the file
+defining the scanner (i.e., the file that was fed to flex). Messages
+are also generated when the scanner backs up, accepts the
+default rule, reaches the end of its input buffer (or encounters
+a NUL; the two look the same as far as the scanner's concerned),
+or reaches an end-of-file.
+.TP
+.B \-f
+specifies
+.I fast scanner.
+No table compression is done and stdio is bypassed.
+The result is large but fast. This option is equivalent to
+.B \-Cfr
+(see below).
+.TP
+.B \-h
+generates a "help" summary of
+.I flex's
+options to
+.I stderr
+and then exits.
+.TP
+.B \-i
+instructs
+.I flex
+to generate a
+.I case-insensitive
+scanner. The case of letters given in the
+.I flex
+input patterns will
+be ignored, and tokens in the input will be matched regardless of case. The
+matched text given in
+.I yytext
+will have the preserved case (i.e., it will not be folded).
+.TP
+.B \-l
+turns on maximum compatibility with the original AT&T lex implementation,
+at a considerable performance cost. This option is incompatible with
+.B \-+, \-f, \-F, \-Cf,
+or
+.B \-CF.
+See
+.I lexdoc(1)
+for details.
+.TP
+.B \-n
+is another do-nothing, deprecated option included only for
+POSIX compliance.
+.TP
+.B \-p
+generates a performance report to stderr. The report
+consists of comments regarding features of the
+.I flex
+input file which will cause a loss of performance in the resulting scanner.
+If you give the flag twice, you will also get comments regarding
+features that lead to minor performance losses.
+.TP
+.B \-s
+causes the
+.I default rule
+(that unmatched scanner input is echoed to
+.I stdout)
+to be suppressed. If the scanner encounters input that does not
+match any of its rules, it aborts with an error.
+.TP
+.B \-t
+instructs
+.I flex
+to write the scanner it generates to standard output instead
+of
+.B lex.yy.c.
+.TP
+.B \-v
+specifies that
+.I flex
+should write to
+.I stderr
+a summary of statistics regarding the scanner it generates.
+.TP
+.B \-w
+suppresses warning messages.
+.TP
+.B \-B
+instructs
+.I flex
+to generate a
+.I batch
+scanner instead of an
+.I interactive
+scanner (see
+.B \-I
+below). See
+.I lexdoc(1)
+for details. Scanners using
+.B \-Cf
+or
+.B \-CF
+compression options automatically specify this option, too.
+.TP
+.B \-F
+specifies that the
+.ul
+fast
+scanner table representation should be used (and stdio bypassed).
+This representation is about as fast as the full table representation
+.B (-f),
+and for some sets of patterns will be considerably smaller (and for
+others, larger). It cannot be used with the
+.B \-+
+option. See
+.B lexdoc(1)
+for more details.
+.IP
+This option is equivalent to
+.B \-CFr
+(see below).
+.TP
+.B \-I
+instructs
+.I flex
+to generate an
+.I interactive
+scanner, that is, a scanner which stops immediately rather than
+looking ahead if it knows
+that the currently scanned text cannot be part of a longer rule's match.
+This is the opposite of
+.I batch
+scanners (see
+.B \-B
+above). See
+.B lexdoc(1)
+for details.
+.IP
+Note,
+.B \-I
+cannot be used in conjunction with
+.I full
+or
+.I fast tables,
+i.e., the
+.B \-f, \-F, \-Cf,
+or
+.B \-CF
+flags. For other table compression options,
+.B \-I
+is the default.
+.TP
+.B \-L
+instructs
+.I flex
+not to generate
+.B #line
+directives in
+.B lex.yy.c.
+The default is to generate such directives so error
+messages in the actions will be correctly
+located with respect to the original
+.I flex
+input file, and not to
+the fairly meaningless line numbers of
+.B lex.yy.c.
+.TP
+.B \-T
+makes
+.I flex
+run in
+.I trace
+mode. It will generate a lot of messages to
+.I stderr
+concerning
+the form of the input and the resultant non-deterministic and deterministic
+finite automata. This option is mostly for use in maintaining
+.I flex.
+.TP
+.B \-V
+prints the version number to
+.I stderr
+and exits.
+.TP
+.B \-7
+instructs
+.I flex
+to generate a 7-bit scanner, which can save considerable table space,
+especially when using
+.B \-Cf
+or
+.B \-CF
+(and, at most sites,
+.B \-7
+is on by default for these options. To see if this is the case, use the
+.B -v
+verbose flag and check the flag summary it reports).
+.TP
+.B \-8
+instructs
+.I flex
+to generate an 8-bit scanner. This is the default except for the
+.B \-Cf
+and
+.B \-CF
+compression options, for which the default is site-dependent, and
+can be checked by inspecting the flag summary generated by the
+.B \-v
+option.
+.TP
+.B \-+
+specifies that you want flex to generate a C++
+scanner class. See the section on Generating C++ Scanners in
+.I lexdoc(1)
+for details.
+.TP
+.B \-C[aefFmr]
+controls the degree of table compression and scanner optimization.
+.IP
+.B \-Ca
+trade off larger tables in the generated scanner for faster performance
+because the elements of the tables are better aligned for memory access
+and computation. This option can double the size of the tables used by
+your scanner.
+.IP
+.B \-Ce
+directs
+.I flex
+to construct
+.I equivalence classes,
+i.e., sets of characters
+which have identical lexical properties.
+Equivalence classes usually give
+dramatic reductions in the final table/object file sizes (typically
+a factor of 2-5) and are pretty cheap performance-wise (one array
+look-up per character scanned).
+.IP
+.B \-Cf
+specifies that the
+.I full
+scanner tables should be generated -
+.I flex
+should not compress the
+tables by taking advantages of similar transition functions for
+different states.
+.IP
+.B \-CF
+specifies that the alternate fast scanner representation (described in
+.B lexdoc(1))
+should be used. This option cannot be used with
+.B \-+.
+.IP
+.B \-Cm
+directs
+.I flex
+to construct
+.I meta-equivalence classes,
+which are sets of equivalence classes (or characters, if equivalence
+classes are not being used) that are commonly used together. Meta-equivalence
+classes are often a big win when using compressed tables, but they
+have a moderate performance impact (one or two "if" tests and one
+array look-up per character scanned).
+.IP
+.B \-Cr
+causes the generated scanner to
+.I bypass
+using stdio for input. In general this option results in a minor
+performance gain only worthwhile if used in conjunction with
+.B \-Cf
+or
+.B \-CF.
+It can cause surprising behavior if you use stdio yourself to
+read from
+.I yyin
+prior to calling the scanner.
+.IP
+A lone
+.B \-C
+specifies that the scanner tables should be compressed but neither
+equivalence classes nor meta-equivalence classes should be used.
+.IP
+The options
+.B \-Cf
+or
+.B \-CF
+and
+.B \-Cm
+do not make sense together - there is no opportunity for meta-equivalence
+classes if the table is not being compressed. Otherwise the options
+may be freely mixed.
+.IP
+The default setting is
+.B \-Cem,
+which specifies that
+.I flex
+should generate equivalence classes
+and meta-equivalence classes. This setting provides the highest
+degree of table compression. You can trade off
+faster-executing scanners at the cost of larger tables with
+the following generally being true:
+.nf
+
+ slowest & smallest
+ -Cem
+ -Cm
+ -Ce
+ -C
+ -C{f,F}e
+ -C{f,F}
+ -C{f,F}a
+ fastest & largest
+
+.fi
+.IP
+.B \-C
+options are cumulative.
+.TP
+.B \-Pprefix
+changes the default
+.I "yy"
+prefix used by
+.I flex
+to be
+.I prefix
+instead. See
+.I lexdoc(1)
+for a description of all the global variables and file names that
+this affects.
+.TP
+.B \-Sskeleton_file
+overrides the default skeleton file from which
+.I flex
+constructs its scanners. You'll never need this option unless you are doing
+.I flex
+maintenance or development.
+.SH SUMMARY OF FLEX REGULAR EXPRESSIONS
+The patterns in the input are written using an extended set of regular
+expressions. These are:
+.nf
+
+ x match the character 'x'
+ . any character except newline
+ [xyz] a "character class"; in this case, the pattern
+ matches either an 'x', a 'y', or a 'z'
+ [abj-oZ] a "character class" with a range in it; matches
+ an 'a', a 'b', any letter from 'j' through 'o',
+ or a 'Z'
+ [^A-Z] a "negated character class", i.e., any character
+ but those in the class. In this case, any
+ character EXCEPT an uppercase letter.
+ [^A-Z\\n] any character EXCEPT an uppercase letter or
+ a newline
+ r* zero or more r's, where r is any regular expression
+ r+ one or more r's
+ r? zero or one r's (that is, "an optional r")
+ r{2,5} anywhere from two to five r's
+ r{2,} two or more r's
+ r{4} exactly 4 r's
+ {name} the expansion of the "name" definition
+ (see above)
+ "[xyz]\\"foo"
+ the literal string: [xyz]"foo
+ \\X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+ then the ANSI-C interpretation of \\x.
+ Otherwise, a literal 'X' (used to escape
+ operators such as '*')
+ \\123 the character with octal value 123
+ \\x2a the character with hexadecimal value 2a
+ (r) match an r; parentheses are used to override
+ precedence (see below)
+
+
+ rs the regular expression r followed by the
+ regular expression s; called "concatenation"
+
+
+ r|s either an r or an s
+
+
+ r/s an r but only if it is followed by an s. The
+ s is not part of the matched text. This type
+ of pattern is called as "trailing context".
+ ^r an r, but only at the beginning of a line
+ r$ an r, but only at the end of a line. Equivalent
+ to "r/\\n".
+
+
+ <s>r an r, but only in start condition s (see
+ below for discussion of start conditions)
+ <s1,s2,s3>r
+ same, but in any of start conditions s1,
+ s2, or s3
+ <*>r an r in any start condition, even an exclusive one.
+
+
+ <<EOF>> an end-of-file
+ <s1,s2><<EOF>>
+ an end-of-file when in start condition s1 or s2
+
+.fi
+The regular expressions listed above are grouped according to
+precedence, from highest precedence at the top to lowest at the bottom.
+Those grouped together have equal precedence.
+.PP
+Some notes on patterns:
+.IP -
+Negated character classes
+.I match newlines
+unless "\\n" (or an equivalent escape sequence) is one of the
+characters explicitly present in the negated character class
+(e.g., "[^A-Z\\n]").
+.IP -
+A rule can have at most one instance of trailing context (the '/' operator
+or the '$' operator). The start condition, '^', and "<<EOF>>" patterns
+can only occur at the beginning of a pattern, and, as well as with '/' and '$',
+cannot be grouped inside parentheses. The following are all illegal:
+.nf
+
+ foo/bar$
+ foo|(bar$)
+ foo|^bar
+ <sc1>foo<sc2>bar
+
+.fi
+.SH SUMMARY OF SPECIAL ACTIONS
+In addition to arbitrary C code, the following can appear in actions:
+.IP -
+.B ECHO
+copies yytext to the scanner's output.
+.IP -
+.B BEGIN
+followed by the name of a start condition places the scanner in the
+corresponding start condition.
+.IP -
+.B REJECT
+directs the scanner to proceed on to the "second best" rule which matched the
+input (or a prefix of the input).
+.B yytext
+and
+.B yyleng
+are set up appropriately. Note that
+.B REJECT
+is a particularly expensive feature in terms scanner performance;
+if it is used in
+.I any
+of the scanner's actions it will slow down
+.I all
+of the scanner's matching. Furthermore,
+.B REJECT
+cannot be used with the
+.B \-f
+or
+.B \-F
+options.
+.IP
+Note also that unlike the other special actions,
+.B REJECT
+is a
+.I branch;
+code immediately following it in the action will
+.I not
+be executed.
+.IP -
+.B yymore()
+tells the scanner that the next time it matches a rule, the corresponding
+token should be
+.I appended
+onto the current value of
+.B yytext
+rather than replacing it.
+.IP -
+.B yyless(n)
+returns all but the first
+.I n
+characters of the current token back to the input stream, where they
+will be rescanned when the scanner looks for the next match.
+.B yytext
+and
+.B yyleng
+are adjusted appropriately (e.g.,
+.B yyleng
+will now be equal to
+.I n
+).
+.IP -
+.B unput(c)
+puts the character
+.I c
+back onto the input stream. It will be the next character scanned.
+.IP -
+.B input()
+reads the next character from the input stream (this routine is called
+.B yyinput()
+if the scanner is compiled using
+.B C++).
+.IP -
+.B yyterminate()
+can be used in lieu of a return statement in an action. It terminates
+the scanner and returns a 0 to the scanner's caller, indicating "all done".
+.IP
+By default,
+.B yyterminate()
+is also called when an end-of-file is encountered. It is a macro and
+may be redefined.
+.IP -
+.B YY_NEW_FILE
+is an action available only in <<EOF>> rules. It means "Okay, I've
+set up a new input file, continue scanning". It is no longer required;
+you can just assign
+.I yyin
+to point to a new file in the <<EOF>> action.
+.IP -
+.B yy_create_buffer( file, size )
+takes a
+.I FILE
+pointer and an integer
+.I size.
+It returns a YY_BUFFER_STATE
+handle to a new input buffer large enough to accomodate
+.I size
+characters and associated with the given file. When in doubt, use
+.B YY_BUF_SIZE
+for the size.
+.IP -
+.B yy_switch_to_buffer( new_buffer )
+switches the scanner's processing to scan for tokens from
+the given buffer, which must be a YY_BUFFER_STATE.
+.IP -
+.B yy_delete_buffer( buffer )
+deletes the given buffer.
+.SH VALUES AVAILABLE TO THE USER
+.IP -
+.B char *yytext
+holds the text of the current token. It may be modified but not lengthened
+(you cannot append characters to the end). Modifying the last character
+may affect the activity of rules anchored using '^' during the next scan;
+see
+.B lexdoc(1)
+for details.
+.IP
+If the special directive
+.B %array
+appears in the first section of the scanner description, then
+.B yytext
+is instead declared
+.B char yytext[YYLMAX],
+where
+.B YYLMAX
+is a macro definition that you can redefine in the first section
+if you don't like the default value (generally 8KB). Using
+.B %array
+results in somewhat slower scanners, but the value of
+.B yytext
+becomes immune to calls to
+.I input()
+and
+.I unput(),
+which potentially destroy its value when
+.B yytext
+is a character pointer. The opposite of
+.B %array
+is
+.B %pointer,
+which is the default.
+.IP
+You cannot use
+.B %array
+when generating C++ scanner classes
+(the
+.B \-+
+flag).
+.IP -
+.B int yyleng
+holds the length of the current token.
+.IP -
+.B FILE *yyin
+is the file which by default
+.I flex
+reads from. It may be redefined but doing so only makes sense before
+scanning begins or after an EOF has been encountered. Changing it in
+the midst of scanning will have unexpected results since
+.I flex
+buffers its input; use
+.B yyrestart()
+instead.
+Once scanning terminates because an end-of-file
+has been seen,
+.B
+you can assign
+.I yyin
+at the new input file and then call the scanner again to continue scanning.
+.IP -
+.B void yyrestart( FILE *new_file )
+may be called to point
+.I yyin
+at the new input file. The switch-over to the new file is immediate
+(any previously buffered-up input is lost). Note that calling
+.B yyrestart()
+with
+.I yyin
+as an argument thus throws away the current input buffer and continues
+scanning the same input file.
+.IP -
+.B FILE *yyout
+is the file to which
+.B ECHO
+actions are done. It can be reassigned by the user.
+.IP -
+.B YY_CURRENT_BUFFER
+returns a
+.B YY_BUFFER_STATE
+handle to the current buffer.
+.IP -
+.B YY_START
+returns an integer value corresponding to the current start
+condition. You can subsequently use this value with
+.B BEGIN
+to return to that start condition.
+.SH MACROS AND FUNCTIONS YOU CAN REDEFINE
+.IP -
+.B YY_DECL
+controls how the scanning routine is declared.
+By default, it is "int yylex()", or, if prototypes are being
+used, "int yylex(void)". This definition may be changed by redefining
+the "YY_DECL" macro. Note that
+if you give arguments to the scanning routine using a
+K&R-style/non-prototyped function declaration, you must terminate
+the definition with a semi-colon (;).
+.IP -
+The nature of how the scanner
+gets its input can be controlled by redefining the
+.B YY_INPUT
+macro.
+YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its
+action is to place up to
+.I max_size
+characters in the character array
+.I buf
+and return in the integer variable
+.I result
+either the
+number of characters read or the constant YY_NULL (0 on Unix systems)
+to indicate EOF. The default YY_INPUT reads from the
+global file-pointer "yyin".
+A sample redefinition of YY_INPUT (in the definitions
+section of the input file):
+.nf
+
+ %{
+ #undef YY_INPUT
+ #define YY_INPUT(buf,result,max_size) \\
+ { \\
+ int c = getchar(); \\
+ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\
+ }
+ %}
+
+.fi
+.IP -
+When the scanner receives an end-of-file indication from YY_INPUT,
+it then checks the function
+.B yywrap()
+function. If
+.B yywrap()
+returns false (zero), then it is assumed that the
+function has gone ahead and set up
+.I yyin
+to point to another input file, and scanning continues. If it returns
+true (non-zero), then the scanner terminates, returning 0 to its
+caller.
+.IP
+The default
+.B yywrap()
+always returns 1.
+.IP -
+YY_USER_ACTION
+can be redefined to provide an action
+which is always executed prior to the matched rule's action.
+.IP -
+The macro
+.B YY_USER_INIT
+may be redefined to provide an action which is always executed before
+the first scan.
+.IP -
+In the generated scanner, the actions are all gathered in one large
+switch statement and separated using
+.B YY_BREAK,
+which may be redefined. By default, it is simply a "break", to separate
+each rule's action from the following rule's.
+.SH FILES
+.TP
+.B \-ll
+library with which to link scanners to obtain the default versions
+of
+.I yywrap()
+and/or
+.I main().
+.TP
+.I lex.yy.c
+generated scanner (called
+.I lexyy.c
+on some systems).
+.TP
+.I lex.yy.cc
+generated C++ scanner class, when using
+.B -+.
+.TP
+.I <FlexLexer.h>
+header file defining the C++ scanner base class,
+.B FlexLexer,
+and its derived class,
+.B yyFlexLexer.
+.TP
+.I flex.skl
+skeleton scanner. This file is only used when building flex, not when
+flex executes.
+.TP
+.I lex.backup
+backing-up information for
+.B \-b
+flag (called
+.I lex.bck
+on some systems).
+.SH "SEE ALSO"
+.PP
+lexdoc(1), lex(1), yacc(1), sed(1), awk(1).
+.PP
+M. E. Lesk and E. Schmidt,
+.I LEX \- Lexical Analyzer Generator
+.SH DIAGNOSTICS
+.PP
+.I reject_used_but_not_detected undefined
+or
+.PP
+.I yymore_used_but_not_detected undefined -
+These errors can occur at compile time. They indicate that the
+scanner uses
+.B REJECT
+or
+.B yymore()
+but that
+.I flex
+failed to notice the fact, meaning that
+.I flex
+scanned the first two sections looking for occurrences of these actions
+and failed to find any, but somehow you snuck some in (via a #include
+file, for example). Make an explicit reference to the action in your
+.I flex
+input file. (Note that previously
+.I flex
+supported a
+.B %used/%unused
+mechanism for dealing with this problem; this feature is still supported
+but now deprecated, and will go away soon unless the author hears from
+people who can argue compellingly that they need it.)
+.PP
+.I flex scanner jammed -
+a scanner compiled with
+.B \-s
+has encountered an input string which wasn't matched by
+any of its rules.
+.PP
+.I warning, rule cannot be matched
+indicates that the given rule
+cannot be matched because it follows other rules that will
+always match the same text as it. See
+.I lexdoc(1)
+for an example.
+.PP
+.I warning,
+.B \-s
+.I
+option given but default rule can be matched
+means that it is possible (perhaps only in a particular start condition)
+that the default rule (match any single character) is the only one
+that will match a particular input. Since
+.PP
+.I scanner input buffer overflowed -
+a scanner rule matched more text than the available dynamic memory.
+.PP
+.I token too large, exceeds YYLMAX -
+your scanner uses
+.B %array
+and one of its rules matched a string longer than the
+.B YYLMAX
+constant (8K bytes by default). You can increase the value by
+#define'ing
+.B YYLMAX
+in the definitions section of your
+.I flex
+input.
+.PP
+.I scanner requires \-8 flag to
+.I use the character 'x' -
+Your scanner specification includes recognizing the 8-bit character
+.I 'x'
+and you did not specify the \-8 flag, and your scanner defaulted to 7-bit
+because you used the
+.B \-Cf
+or
+.B \-CF
+table compression options.
+.PP
+.I flex scanner push-back overflow -
+you used
+.B unput()
+to push back so much text that the scanner's buffer could not hold
+both the pushed-back text and the current token in
+.B yytext.
+Ideally the scanner should dynamically resize the buffer in this case, but at
+present it does not.
+.PP
+.I
+input buffer overflow, can't enlarge buffer because scanner uses REJECT -
+the scanner was working on matching an extremely large token and needed
+to expand the input buffer. This doesn't work with scanners that use
+.B
+REJECT.
+.PP
+.I
+fatal flex scanner internal error--end of buffer missed -
+This can occur in an scanner which is reentered after a long-jump
+has jumped out (or over) the scanner's activation frame. Before
+reentering the scanner, use:
+.nf
+
+ yyrestart( yyin );
+
+.fi
+or use C++ scanner classes (the
+.B \-+
+option), which are fully reentrant.
+.SH AUTHOR
+Vern Paxson, with the help of many ideas and much inspiration from
+Van Jacobson. Original version by Jef Poskanzer.
+.PP
+See lexdoc(1) for additional credits and the address to send comments to.
+.SH DEFICIENCIES / BUGS
+.PP
+Some trailing context
+patterns cannot be properly matched and generate
+warning messages ("dangerous trailing context"). These are
+patterns where the ending of the
+first part of the rule matches the beginning of the second
+part, such as "zx*/xy*", where the 'x*' matches the 'x' at
+the beginning of the trailing context. (Note that the POSIX draft
+states that the text matched by such patterns is undefined.)
+.PP
+For some trailing context rules, parts which are actually fixed-length are
+not recognized as such, leading to the abovementioned performance loss.
+In particular, parts using '|' or {n} (such as "foo{3}") are always
+considered variable-length.
+.PP
+Combining trailing context with the special '|' action can result in
+.I fixed
+trailing context being turned into the more expensive
+.I variable
+trailing context. For example, in the following:
+.nf
+
+ %%
+ abc |
+ xyz/def
+
+.fi
+.PP
+Use of
+.B unput()
+or
+.B input()
+invalidates yytext and yyleng, unless the
+.B %array
+directive
+or the
+.B \-l
+option has been used.
+.PP
+Use of unput() to push back more text than was matched can
+result in the pushed-back text matching a beginning-of-line ('^')
+rule even though it didn't come at the beginning of the line
+(though this is rare!).
+.PP
+Pattern-matching of NUL's is substantially slower than matching other
+characters.
+.PP
+Dynamic resizing of the input buffer is slow, as it entails rescanning
+all the text matched so far by the current (generally huge) token.
+.PP
+.I flex
+does not generate correct #line directives for code internal
+to the scanner; thus, bugs in
+.I flex.skl
+yield bogus line numbers.
+.PP
+Due to both buffering of input and read-ahead, you cannot intermix
+calls to <stdio.h> routines, such as, for example,
+.B getchar(),
+with
+.I flex
+rules and expect it to work. Call
+.B input()
+instead.
+.PP
+The total table entries listed by the
+.B \-v
+flag excludes the number of table entries needed to determine
+what rule has been matched. The number of entries is equal
+to the number of DFA states if the scanner does not use
+.B REJECT,
+and somewhat greater than the number of states if it does.
+.PP
+.B REJECT
+cannot be used with the
+.B \-f
+or
+.B \-F
+options.
+.PP
+The
+.I flex
+internal algorithms need documentation.
diff --git a/usr.bin/lex/lexdoc.1 b/usr.bin/lex/lexdoc.1
new file mode 100644
index 0000000..7241c22
--- /dev/null
+++ b/usr.bin/lex/lexdoc.1
@@ -0,0 +1,3045 @@
+.TH FLEXDOC 1 "November 1993" "Version 2.4"
+.SH NAME
+flexdoc \- documentation for flex, fast lexical analyzer generator
+.SH SYNOPSIS
+.B flex
+.B [\-bcdfhilnpstvwBFILTV78+ \-C[aefFmr] \-Pprefix \-Sskeleton]
+.I [filename ...]
+.SH DESCRIPTION
+.I flex
+is a tool for generating
+.I scanners:
+programs which recognized lexical patterns in text.
+.I flex
+reads
+the given input files, or its standard input if no file names are given,
+for a description of a scanner to generate. The description is in
+the form of pairs
+of regular expressions and C code, called
+.I rules. flex
+generates as output a C source file,
+.B lex.yy.c,
+which defines a routine
+.B yylex().
+This file is compiled and linked with the
+.B \-lfl
+library to produce an executable. When the executable is run,
+it analyzes its input for occurrences
+of the regular expressions. Whenever it finds one, it executes
+the corresponding C code.
+.SH SOME SIMPLE EXAMPLES
+.PP
+First some simple examples to get the flavor of how one uses
+.I flex.
+The following
+.I flex
+input specifies a scanner which whenever it encounters the string
+"username" will replace it with the user's login name:
+.nf
+
+ %%
+ username printf( "%s", getlogin() );
+
+.fi
+By default, any text not matched by a
+.I flex
+scanner
+is copied to the output, so the net effect of this scanner is
+to copy its input file to its output with each occurrence
+of "username" expanded.
+In this input, there is just one rule. "username" is the
+.I pattern
+and the "printf" is the
+.I action.
+The "%%" marks the beginning of the rules.
+.PP
+Here's another simple example:
+.nf
+
+ int num_lines = 0, num_chars = 0;
+
+ %%
+ \\n ++num_lines; ++num_chars;
+ . ++num_chars;
+
+ %%
+ main()
+ {
+ yylex();
+ printf( "# of lines = %d, # of chars = %d\\n",
+ num_lines, num_chars );
+ }
+
+.fi
+This scanner counts the number of characters and the number
+of lines in its input (it produces no output other than the
+final report on the counts). The first line
+declares two globals, "num_lines" and "num_chars", which are accessible
+both inside
+.B yylex()
+and in the
+.B main()
+routine declared after the second "%%". There are two rules, one
+which matches a newline ("\\n") and increments both the line count and
+the character count, and one which matches any character other than
+a newline (indicated by the "." regular expression).
+.PP
+A somewhat more complicated example:
+.nf
+
+ /* scanner for a toy Pascal-like language */
+
+ %{
+ /* need this for the call to atof() below */
+ #include <math.h>
+ %}
+
+ DIGIT [0-9]
+ ID [a-z][a-z0-9]*
+
+ %%
+
+ {DIGIT}+ {
+ printf( "An integer: %s (%d)\\n", yytext,
+ atoi( yytext ) );
+ }
+
+ {DIGIT}+"."{DIGIT}* {
+ printf( "A float: %s (%g)\\n", yytext,
+ atof( yytext ) );
+ }
+
+ if|then|begin|end|procedure|function {
+ printf( "A keyword: %s\\n", yytext );
+ }
+
+ {ID} printf( "An identifier: %s\\n", yytext );
+
+ "+"|"-"|"*"|"/" printf( "An operator: %s\\n", yytext );
+
+ "{"[^}\\n]*"}" /* eat up one-line comments */
+
+ [ \\t\\n]+ /* eat up whitespace */
+
+ . printf( "Unrecognized character: %s\\n", yytext );
+
+ %%
+
+ main( argc, argv )
+ int argc;
+ char **argv;
+ {
+ ++argv, --argc; /* skip over program name */
+ if ( argc > 0 )
+ yyin = fopen( argv[0], "r" );
+ else
+ yyin = stdin;
+
+ yylex();
+ }
+
+.fi
+This is the beginnings of a simple scanner for a language like
+Pascal. It identifies different types of
+.I tokens
+and reports on what it has seen.
+.PP
+The details of this example will be explained in the following
+sections.
+.SH FORMAT OF THE INPUT FILE
+The
+.I flex
+input file consists of three sections, separated by a line with just
+.B %%
+in it:
+.nf
+
+ definitions
+ %%
+ rules
+ %%
+ user code
+
+.fi
+The
+.I definitions
+section contains declarations of simple
+.I name
+definitions to simplify the scanner specification, and declarations of
+.I start conditions,
+which are explained in a later section.
+.PP
+Name definitions have the form:
+.nf
+
+ name definition
+
+.fi
+The "name" is a word beginning with a letter or an underscore ('_')
+followed by zero or more letters, digits, '_', or '-' (dash).
+The definition is taken to begin at the first non-white-space character
+following the name and continuing to the end of the line.
+The definition can subsequently be referred to using "{name}", which
+will expand to "(definition)". For example,
+.nf
+
+ DIGIT [0-9]
+ ID [a-z][a-z0-9]*
+
+.fi
+defines "DIGIT" to be a regular expression which matches a
+single digit, and
+"ID" to be a regular expression which matches a letter
+followed by zero-or-more letters-or-digits.
+A subsequent reference to
+.nf
+
+ {DIGIT}+"."{DIGIT}*
+
+.fi
+is identical to
+.nf
+
+ ([0-9])+"."([0-9])*
+
+.fi
+and matches one-or-more digits followed by a '.' followed
+by zero-or-more digits.
+.PP
+The
+.I rules
+section of the
+.I flex
+input contains a series of rules of the form:
+.nf
+
+ pattern action
+
+.fi
+where the pattern must be unindented and the action must begin
+on the same line.
+.PP
+See below for a further description of patterns and actions.
+.PP
+Finally, the user code section is simply copied to
+.B lex.yy.c
+verbatim.
+It is used for companion routines which call or are called
+by the scanner. The presence of this section is optional;
+if it is missing, the second
+.B %%
+in the input file may be skipped, too.
+.PP
+In the definitions and rules sections, any
+.I indented
+text or text enclosed in
+.B %{
+and
+.B %}
+is copied verbatim to the output (with the %{}'s removed).
+The %{}'s must appear unindented on lines by themselves.
+.PP
+In the rules section,
+any indented or %{} text appearing before the
+first rule may be used to declare variables
+which are local to the scanning routine and (after the declarations)
+code which is to be executed whenever the scanning routine is entered.
+Other indented or %{} text in the rule section is still copied to the output,
+but its meaning is not well-defined and it may well cause compile-time
+errors (this feature is present for
+.I POSIX
+compliance; see below for other such features).
+.PP
+In the definitions section (but not in the rules section),
+an unindented comment (i.e., a line
+beginning with "/*") is also copied verbatim to the output up
+to the next "*/".
+.SH PATTERNS
+The patterns in the input are written using an extended set of regular
+expressions. These are:
+.nf
+
+ x match the character 'x'
+ . any character except newline
+ [xyz] a "character class"; in this case, the pattern
+ matches either an 'x', a 'y', or a 'z'
+ [abj-oZ] a "character class" with a range in it; matches
+ an 'a', a 'b', any letter from 'j' through 'o',
+ or a 'Z'
+ [^A-Z] a "negated character class", i.e., any character
+ but those in the class. In this case, any
+ character EXCEPT an uppercase letter.
+ [^A-Z\\n] any character EXCEPT an uppercase letter or
+ a newline
+ r* zero or more r's, where r is any regular expression
+ r+ one or more r's
+ r? zero or one r's (that is, "an optional r")
+ r{2,5} anywhere from two to five r's
+ r{2,} two or more r's
+ r{4} exactly 4 r's
+ {name} the expansion of the "name" definition
+ (see above)
+ "[xyz]\\"foo"
+ the literal string: [xyz]"foo
+ \\X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+ then the ANSI-C interpretation of \\x.
+ Otherwise, a literal 'X' (used to escape
+ operators such as '*')
+ \\123 the character with octal value 123
+ \\x2a the character with hexadecimal value 2a
+ (r) match an r; parentheses are used to override
+ precedence (see below)
+
+
+ rs the regular expression r followed by the
+ regular expression s; called "concatenation"
+
+
+ r|s either an r or an s
+
+
+ r/s an r but only if it is followed by an s. The
+ s is not part of the matched text. This type
+ of pattern is called as "trailing context".
+ ^r an r, but only at the beginning of a line
+ r$ an r, but only at the end of a line. Equivalent
+ to "r/\\n".
+
+
+ <s>r an r, but only in start condition s (see
+ below for discussion of start conditions)
+ <s1,s2,s3>r
+ same, but in any of start conditions s1,
+ s2, or s3
+ <*>r an r in any start condition, even an exclusive one.
+
+
+ <<EOF>> an end-of-file
+ <s1,s2><<EOF>>
+ an end-of-file when in start condition s1 or s2
+
+.fi
+Note that inside of a character class, all regular expression operators
+lose their special meaning except escape ('\\') and the character class
+operators, '-', ']', and, at the beginning of the class, '^'.
+.PP
+The regular expressions listed above are grouped according to
+precedence, from highest precedence at the top to lowest at the bottom.
+Those grouped together have equal precedence. For example,
+.nf
+
+ foo|bar*
+
+.fi
+is the same as
+.nf
+
+ (foo)|(ba(r*))
+
+.fi
+since the '*' operator has higher precedence than concatenation,
+and concatenation higher than alternation ('|'). This pattern
+therefore matches
+.I either
+the string "foo"
+.I or
+the string "ba" followed by zero-or-more r's.
+To match "foo" or zero-or-more "bar"'s, use:
+.nf
+
+ foo|(bar)*
+
+.fi
+and to match zero-or-more "foo"'s-or-"bar"'s:
+.nf
+
+ (foo|bar)*
+
+.fi
+.PP
+Some notes on patterns:
+.IP -
+A negated character class such as the example "[^A-Z]"
+above
+.I will match a newline
+unless "\\n" (or an equivalent escape sequence) is one of the
+characters explicitly present in the negated character class
+(e.g., "[^A-Z\\n]"). This is unlike how many other regular
+expression tools treat negated character classes, but unfortunately
+the inconsistency is historically entrenched.
+Matching newlines means that a pattern like [^"]* can match the entire
+input unless there's another quote in the input.
+.IP -
+A rule can have at most one instance of trailing context (the '/' operator
+or the '$' operator). The start condition, '^', and "<<EOF>>" patterns
+can only occur at the beginning of a pattern, and, as well as with '/' and '$',
+cannot be grouped inside parentheses. A '^' which does not occur at
+the beginning of a rule or a '$' which does not occur at the end of
+a rule loses its special properties and is treated as a normal character.
+.IP
+The following are illegal:
+.nf
+
+ foo/bar$
+ <sc1>foo<sc2>bar
+
+.fi
+Note that the first of these, can be written "foo/bar\\n".
+.IP
+The following will result in '$' or '^' being treated as a normal character:
+.nf
+
+ foo|(bar$)
+ foo|^bar
+
+.fi
+If what's wanted is a "foo" or a bar-followed-by-a-newline, the following
+could be used (the special '|' action is explained below):
+.nf
+
+ foo |
+ bar$ /* action goes here */
+
+.fi
+A similar trick will work for matching a foo or a
+bar-at-the-beginning-of-a-line.
+.SH HOW THE INPUT IS MATCHED
+When the generated scanner is run, it analyzes its input looking
+for strings which match any of its patterns. If it finds more than
+one match, it takes the one matching the most text (for trailing
+context rules, this includes the length of the trailing part, even
+though it will then be returned to the input). If it finds two
+or more matches of the same length, the
+rule listed first in the
+.I flex
+input file is chosen.
+.PP
+Once the match is determined, the text corresponding to the match
+(called the
+.I token)
+is made available in the global character pointer
+.B yytext,
+and its length in the global integer
+.B yyleng.
+The
+.I action
+corresponding to the matched pattern is then executed (a more
+detailed description of actions follows), and then the remaining
+input is scanned for another match.
+.PP
+If no match is found, then the
+.I default rule
+is executed: the next character in the input is considered matched and
+copied to the standard output. Thus, the simplest legal
+.I flex
+input is:
+.nf
+
+ %%
+
+.fi
+which generates a scanner that simply copies its input (one character
+at a time) to its output.
+.PP
+Note that
+.B yytext
+can be defined in two different ways: either as a character
+.I pointer
+or as a character
+.I array.
+You can control which definition
+.I flex
+uses by including one of the special directives
+.B %pointer
+or
+.B %array
+in the first (definitions) section of your flex input. The default is
+.B %pointer,
+unless you use the
+.B -l
+lex compatibility option, in which case
+.B yytext
+will be an array.
+The advantage of using
+.B %pointer
+is substantially faster scanning and no buffer overflow when matching
+very large tokens (unless you run out of dynamic memory). The disadvantage
+is that you are restricted in how your actions can modify
+.B yytext
+(see the next section), and calls to the
+.B input()
+and
+.B unput()
+functions destroy the present contents of
+.B yytext,
+which can be a considerable porting headache when moving between different
+.I lex
+versions.
+.PP
+The advantage of
+.B %array
+is that you can then modify
+.B yytext
+to your heart's content, and calls to
+.B input()
+and
+.B unput()
+do not destroy
+.B yytext
+(see below). Furthermore, existing
+.I lex
+programs sometimes access
+.B yytext
+externally using declarations of the form:
+.nf
+ extern char yytext[];
+.fi
+This definition is erroneous when used with
+.B %pointer,
+but correct for
+.B %array.
+.PP
+.B %array
+defines
+.B yytext
+to be an array of
+.B YYLMAX
+characters, which defaults to a fairly large value. You can change
+the size by simply #define'ing
+.B YYLMAX
+to a different value in the first section of your
+.I flex
+input. As mentioned above, with
+.B %pointer
+yytext grows dynamically to accomodate large tokens. While this means your
+.B %pointer
+scanner can accomodate very large tokens (such as matching entire blocks
+of comments), bear in mind that each time the scanner must resize
+.B yytext
+it also must rescan the entire token from the beginning, so matching such
+tokens can prove slow.
+.B yytext
+presently does
+.I not
+dynamically grow if a call to
+.B unput()
+results in too much text being pushed back; instead, a run-time error results.
+.PP
+Also note that you cannot use
+.B %array
+with C++ scanner classes
+(the
+.B \-+
+option; see below).
+.SH ACTIONS
+Each pattern in a rule has a corresponding action, which can be any
+arbitrary C statement. The pattern ends at the first non-escaped
+whitespace character; the remainder of the line is its action. If the
+action is empty, then when the pattern is matched the input token
+is simply discarded. For example, here is the specification for a program
+which deletes all occurrences of "zap me" from its input:
+.nf
+
+ %%
+ "zap me"
+
+.fi
+(It will copy all other characters in the input to the output since
+they will be matched by the default rule.)
+.PP
+Here is a program which compresses multiple blanks and tabs down to
+a single blank, and throws away whitespace found at the end of a line:
+.nf
+
+ %%
+ [ \\t]+ putchar( ' ' );
+ [ \\t]+$ /* ignore this token */
+
+.fi
+.PP
+If the action contains a '{', then the action spans till the balancing '}'
+is found, and the action may cross multiple lines.
+.I flex
+knows about C strings and comments and won't be fooled by braces found
+within them, but also allows actions to begin with
+.B %{
+and will consider the action to be all the text up to the next
+.B %}
+(regardless of ordinary braces inside the action).
+.PP
+An action consisting solely of a vertical bar ('|') means "same as
+the action for the next rule." See below for an illustration.
+.PP
+Actions can include arbitrary C code, including
+.B return
+statements to return a value to whatever routine called
+.B yylex().
+Each time
+.B yylex()
+is called it continues processing tokens from where it last left
+off until it either reaches
+the end of the file or executes a return.
+.PP
+Actions are free to modify
+.B yytext
+except for lengthening it (adding
+characters to its end--these will overwrite later characters in the
+input stream). Modifying the final character of yytext may alter
+whether when scanning resumes rules anchored with '^' are active.
+Specifically, changing the final character of yytext to a newline will
+activate such rules on the next scan, and changing it to anything else
+will deactivate the rules. Users should not rely on this behavior being
+present in future releases. Finally, note that none of this paragraph
+applies when using
+.B %array
+(see above).
+.PP
+Actions are free to modify
+.B yyleng
+except they should not do so if the action also includes use of
+.B yymore()
+(see below).
+.PP
+There are a number of special directives which can be included within
+an action:
+.IP -
+.B ECHO
+copies yytext to the scanner's output.
+.IP -
+.B BEGIN
+followed by the name of a start condition places the scanner in the
+corresponding start condition (see below).
+.IP -
+.B REJECT
+directs the scanner to proceed on to the "second best" rule which matched the
+input (or a prefix of the input). The rule is chosen as described
+above in "How the Input is Matched", and
+.B yytext
+and
+.B yyleng
+set up appropriately.
+It may either be one which matched as much text
+as the originally chosen rule but came later in the
+.I flex
+input file, or one which matched less text.
+For example, the following will both count the
+words in the input and call the routine special() whenever "frob" is seen:
+.nf
+
+ int word_count = 0;
+ %%
+
+ frob special(); REJECT;
+ [^ \\t\\n]+ ++word_count;
+
+.fi
+Without the
+.B REJECT,
+any "frob"'s in the input would not be counted as words, since the
+scanner normally executes only one action per token.
+Multiple
+.B REJECT's
+are allowed, each one finding the next best choice to the currently
+active rule. For example, when the following scanner scans the token
+"abcd", it will write "abcdabcaba" to the output:
+.nf
+
+ %%
+ a |
+ ab |
+ abc |
+ abcd ECHO; REJECT;
+ .|\\n /* eat up any unmatched character */
+
+.fi
+(The first three rules share the fourth's action since they use
+the special '|' action.)
+.B REJECT
+is a particularly expensive feature in terms scanner performance;
+if it is used in
+.I any
+of the scanner's actions it will slow down
+.I all
+of the scanner's matching. Furthermore,
+.B REJECT
+cannot be used with the
+.I -Cf
+or
+.I -CF
+options (see below).
+.IP
+Note also that unlike the other special actions,
+.B REJECT
+is a
+.I branch;
+code immediately following it in the action will
+.I not
+be executed.
+.IP -
+.B yymore()
+tells the scanner that the next time it matches a rule, the corresponding
+token should be
+.I appended
+onto the current value of
+.B yytext
+rather than replacing it. For example, given the input "mega-kludge"
+the following will write "mega-mega-kludge" to the output:
+.nf
+
+ %%
+ mega- ECHO; yymore();
+ kludge ECHO;
+
+.fi
+First "mega-" is matched and echoed to the output. Then "kludge"
+is matched, but the previous "mega-" is still hanging around at the
+beginning of
+.B yytext
+so the
+.B ECHO
+for the "kludge" rule will actually write "mega-kludge".
+The presence of
+.B yymore()
+in the scanner's action entails a minor performance penalty in the
+scanner's matching speed.
+.IP -
+.B yyless(n)
+returns all but the first
+.I n
+characters of the current token back to the input stream, where they
+will be rescanned when the scanner looks for the next match.
+.B yytext
+and
+.B yyleng
+are adjusted appropriately (e.g.,
+.B yyleng
+will now be equal to
+.I n
+). For example, on the input "foobar" the following will write out
+"foobarbar":
+.nf
+
+ %%
+ foobar ECHO; yyless(3);
+ [a-z]+ ECHO;
+
+.fi
+An argument of 0 to
+.B yyless
+will cause the entire current input string to be scanned again. Unless you've
+changed how the scanner will subsequently process its input (using
+.B BEGIN,
+for example), this will result in an endless loop.
+.PP
+Note that
+.B yyless
+is a macro and can only be used in the flex input file, not from
+other source files.
+.IP -
+.B unput(c)
+puts the character
+.I c
+back onto the input stream. It will be the next character scanned.
+The following action will take the current token and cause it
+to be rescanned enclosed in parentheses.
+.nf
+
+ {
+ int i;
+ unput( ')' );
+ for ( i = yyleng - 1; i >= 0; --i )
+ unput( yytext[i] );
+ unput( '(' );
+ }
+
+.fi
+Note that since each
+.B unput()
+puts the given character back at the
+.I beginning
+of the input stream, pushing back strings must be done back-to-front.
+Also note that you cannot put back
+.B EOF
+to attempt to mark the input stream with an end-of-file.
+.IP -
+.B input()
+reads the next character from the input stream. For example,
+the following is one way to eat up C comments:
+.nf
+
+ %%
+ "/*" {
+ register int c;
+
+ for ( ; ; )
+ {
+ while ( (c = input()) != '*' &&
+ c != EOF )
+ ; /* eat up text of comment */
+
+ if ( c == '*' )
+ {
+ while ( (c = input()) == '*' )
+ ;
+ if ( c == '/' )
+ break; /* found the end */
+ }
+
+ if ( c == EOF )
+ {
+ error( "EOF in comment" );
+ break;
+ }
+ }
+ }
+
+.fi
+(Note that if the scanner is compiled using
+.B C++,
+then
+.B input()
+is instead referred to as
+.B yyinput(),
+in order to avoid a name clash with the
+.B C++
+stream by the name of
+.I input.)
+.IP -
+.B yyterminate()
+can be used in lieu of a return statement in an action. It terminates
+the scanner and returns a 0 to the scanner's caller, indicating "all done".
+By default,
+.B yyterminate()
+is also called when an end-of-file is encountered. It is a macro and
+may be redefined.
+.SH THE GENERATED SCANNER
+The output of
+.I flex
+is the file
+.B lex.yy.c,
+which contains the scanning routine
+.B yylex(),
+a number of tables used by it for matching tokens, and a number
+of auxiliary routines and macros. By default,
+.B yylex()
+is declared as follows:
+.nf
+
+ int yylex()
+ {
+ ... various definitions and the actions in here ...
+ }
+
+.fi
+(If your environment supports function prototypes, then it will
+be "int yylex( void )".) This definition may be changed by defining
+the "YY_DECL" macro. For example, you could use:
+.nf
+
+ #define YY_DECL float lexscan( a, b ) float a, b;
+
+.fi
+to give the scanning routine the name
+.I lexscan,
+returning a float, and taking two floats as arguments. Note that
+if you give arguments to the scanning routine using a
+K&R-style/non-prototyped function declaration, you must terminate
+the definition with a semi-colon (;).
+.PP
+Whenever
+.B yylex()
+is called, it scans tokens from the global input file
+.I yyin
+(which defaults to stdin). It continues until it either reaches
+an end-of-file (at which point it returns the value 0) or
+one of its actions executes a
+.I return
+statement.
+.PP
+If the scanner reaches an end-of-file, subsequent calls are undefined
+unless either
+.I yyin
+is pointed at a new input file (in which case scanning continues from
+that file), or
+.B yyrestart()
+is called.
+.B yyrestart()
+takes one argument, a
+.B FILE *
+pointer, and initializes
+.I yyin
+for scanning from that file. Essentially there is no difference between
+just assigning
+.I yyin
+to a new input file or using
+.B yyrestart()
+to do so; the latter is available for compatibility with previous versions
+of
+.I flex,
+and because it can be used to switch input files in the middle of scanning.
+It can also be used to throw away the current input buffer, by calling
+it with an argument of
+.I yyin.
+.PP
+If
+.B yylex()
+stops scanning due to executing a
+.I return
+statement in one of the actions, the scanner may then be called again and it
+will resume scanning where it left off.
+.PP
+By default (and for purposes of efficiency), the scanner uses
+block-reads rather than simple
+.I getc()
+calls to read characters from
+.I yyin.
+The nature of how it gets its input can be controlled by defining the
+.B YY_INPUT
+macro.
+YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its
+action is to place up to
+.I max_size
+characters in the character array
+.I buf
+and return in the integer variable
+.I result
+either the
+number of characters read or the constant YY_NULL (0 on Unix systems)
+to indicate EOF. The default YY_INPUT reads from the
+global file-pointer "yyin".
+.PP
+A sample definition of YY_INPUT (in the definitions
+section of the input file):
+.nf
+
+ %{
+ #define YY_INPUT(buf,result,max_size) \\
+ { \\
+ int c = getchar(); \\
+ result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \\
+ }
+ %}
+
+.fi
+This definition will change the input processing to occur
+one character at a time.
+.PP
+You also can add in things like keeping track of the
+input line number this way; but don't expect your scanner to
+go very fast.
+.PP
+When the scanner receives an end-of-file indication from YY_INPUT,
+it then checks the
+.B yywrap()
+function. If
+.B yywrap()
+returns false (zero), then it is assumed that the
+function has gone ahead and set up
+.I yyin
+to point to another input file, and scanning continues. If it returns
+true (non-zero), then the scanner terminates, returning 0 to its
+caller.
+.PP
+The default
+.B yywrap()
+always returns 1.
+.PP
+The scanner writes its
+.B ECHO
+output to the
+.I yyout
+global (default, stdout), which may be redefined by the user simply
+by assigning it to some other
+.B FILE
+pointer.
+.SH START CONDITIONS
+.I flex
+provides a mechanism for conditionally activating rules. Any rule
+whose pattern is prefixed with "<sc>" will only be active when
+the scanner is in the start condition named "sc". For example,
+.nf
+
+ <STRING>[^"]* { /* eat up the string body ... */
+ ...
+ }
+
+.fi
+will be active only when the scanner is in the "STRING" start
+condition, and
+.nf
+
+ <INITIAL,STRING,QUOTE>\\. { /* handle an escape ... */
+ ...
+ }
+
+.fi
+will be active only when the current start condition is
+either "INITIAL", "STRING", or "QUOTE".
+.PP
+Start conditions
+are declared in the definitions (first) section of the input
+using unindented lines beginning with either
+.B %s
+or
+.B %x
+followed by a list of names.
+The former declares
+.I inclusive
+start conditions, the latter
+.I exclusive
+start conditions. A start condition is activated using the
+.B BEGIN
+action. Until the next
+.B BEGIN
+action is executed, rules with the given start
+condition will be active and
+rules with other start conditions will be inactive.
+If the start condition is
+.I inclusive,
+then rules with no start conditions at all will also be active.
+If it is
+.I exclusive,
+then
+.I only
+rules qualified with the start condition will be active.
+A set of rules contingent on the same exclusive start condition
+describe a scanner which is independent of any of the other rules in the
+.I flex
+input. Because of this,
+exclusive start conditions make it easy to specify "mini-scanners"
+which scan portions of the input that are syntactically different
+from the rest (e.g., comments).
+.PP
+If the distinction between inclusive and exclusive start conditions
+is still a little vague, here's a simple example illustrating the
+connection between the two. The set of rules:
+.nf
+
+ %s example
+ %%
+ <example>foo /* do something */
+
+.fi
+is equivalent to
+.nf
+
+ %x example
+ %%
+ <INITIAL,example>foo /* do something */
+
+.fi
+.PP
+Also note that the special start-condition specifier
+.B <*>
+matches every start condition. Thus, the above example could also
+have been written;
+.nf
+
+ %x example
+ %%
+ <*>foo /* do something */
+
+.fi
+.PP
+The default rule (to
+.B ECHO
+any unmatched character) remains active in start conditions.
+.PP
+.B BEGIN(0)
+returns to the original state where only the rules with
+no start conditions are active. This state can also be
+referred to as the start-condition "INITIAL", so
+.B BEGIN(INITIAL)
+is equivalent to
+.B BEGIN(0).
+(The parentheses around the start condition name are not required but
+are considered good style.)
+.PP
+.B BEGIN
+actions can also be given as indented code at the beginning
+of the rules section. For example, the following will cause
+the scanner to enter the "SPECIAL" start condition whenever
+.I yylex()
+is called and the global variable
+.I enter_special
+is true:
+.nf
+
+ int enter_special;
+
+ %x SPECIAL
+ %%
+ if ( enter_special )
+ BEGIN(SPECIAL);
+
+ <SPECIAL>blahblahblah
+ ...more rules follow...
+
+.fi
+.PP
+To illustrate the uses of start conditions,
+here is a scanner which provides two different interpretations
+of a string like "123.456". By default it will treat it as
+as three tokens, the integer "123", a dot ('.'), and the integer "456".
+But if the string is preceded earlier in the line by the string
+"expect-floats"
+it will treat it as a single token, the floating-point number
+123.456:
+.nf
+
+ %{
+ #include <math.h>
+ %}
+ %s expect
+
+ %%
+ expect-floats BEGIN(expect);
+
+ <expect>[0-9]+"."[0-9]+ {
+ printf( "found a float, = %f\\n",
+ atof( yytext ) );
+ }
+ <expect>\\n {
+ /* that's the end of the line, so
+ * we need another "expect-number"
+ * before we'll recognize any more
+ * numbers
+ */
+ BEGIN(INITIAL);
+ }
+
+ [0-9]+ {
+ printf( "found an integer, = %d\\n",
+ atoi( yytext ) );
+ }
+
+ "." printf( "found a dot\\n" );
+
+.fi
+Here is a scanner which recognizes (and discards) C comments while
+maintaining a count of the current input line.
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+This scanner goes to a bit of trouble to match as much
+text as possible with each rule. In general, when attempting to write
+a high-speed scanner try to match as much possible in each rule, as
+it's a big win.
+.PP
+Note that start-conditions names are really integer values and
+can be stored as such. Thus, the above could be extended in the
+following fashion:
+.nf
+
+ %x comment foo
+ %%
+ int line_num = 1;
+ int comment_caller;
+
+ "/*" {
+ comment_caller = INITIAL;
+ BEGIN(comment);
+ }
+
+ ...
+
+ <foo>"/*" {
+ comment_caller = foo;
+ BEGIN(comment);
+ }
+
+ <comment>[^*\\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(comment_caller);
+
+.fi
+Furthermore, you can access the current start condition using
+the integer-valued
+.B YY_START
+macro. For example, the above assignments to
+.I comment_caller
+could instead be written
+.nf
+
+ comment_caller = YY_START;
+.fi
+.PP
+Note that start conditions do not have their own name-space; %s's and %x's
+declare names in the same fashion as #define's.
+.PP
+Finally, here's an example of how to match C-style quoted strings using
+exclusive start conditions, including expanded escape sequences (but
+not including checking for a string that's too long):
+.nf
+
+ %x str
+
+ %%
+ char string_buf[MAX_STR_CONST];
+ char *string_buf_ptr;
+
+
+ \\" string_buf_ptr = string_buf; BEGIN(str);
+
+ <str>\\" { /* saw closing quote - all done */
+ BEGIN(INITIAL);
+ *string_buf_ptr = '\\0';
+ /* return string constant token type and
+ * value to parser
+ */
+ }
+
+ <str>\\n {
+ /* error - unterminated string constant */
+ /* generate error message */
+ }
+
+ <str>\\\\[0-7]{1,3} {
+ /* octal escape sequence */
+ int result;
+
+ (void) sscanf( yytext + 1, "%o", &result );
+
+ if ( result > 0xff )
+ /* error, constant is out-of-bounds */
+
+ *string_buf_ptr++ = result;
+ }
+
+ <str>\\\\[0-9]+ {
+ /* generate error - bad escape sequence; something
+ * like '\\48' or '\\0777777'
+ */
+ }
+
+ <str>\\\\n *string_buf_ptr++ = '\\n';
+ <str>\\\\t *string_buf_ptr++ = '\\t';
+ <str>\\\\r *string_buf_ptr++ = '\\r';
+ <str>\\\\b *string_buf_ptr++ = '\\b';
+ <str>\\\\f *string_buf_ptr++ = '\\f';
+
+ <str>\\\\(.|\\n) *string_buf_ptr++ = yytext[1];
+
+ <str>[^\\\\\\n\\"]+ {
+ char *yytext_ptr = yytext;
+
+ while ( *yytext_ptr )
+ *string_buf_ptr++ = *yytext_ptr++;
+ }
+
+.fi
+.SH MULTIPLE INPUT BUFFERS
+Some scanners (such as those which support "include" files)
+require reading from several input streams. As
+.I flex
+scanners do a large amount of buffering, one cannot control
+where the next input will be read from by simply writing a
+.B YY_INPUT
+which is sensitive to the scanning context.
+.B YY_INPUT
+is only called when the scanner reaches the end of its buffer, which
+may be a long time after scanning a statement such as an "include"
+which requires switching the input source.
+.PP
+To negotiate these sorts of problems,
+.I flex
+provides a mechanism for creating and switching between multiple
+input buffers. An input buffer is created by using:
+.nf
+
+ YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+
+.fi
+which takes a
+.I FILE
+pointer and a size and creates a buffer associated with the given
+file and large enough to hold
+.I size
+characters (when in doubt, use
+.B YY_BUF_SIZE
+for the size). It returns a
+.B YY_BUFFER_STATE
+handle, which may then be passed to other routines:
+.nf
+
+ void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+
+.fi
+switches the scanner's input buffer so subsequent tokens will
+come from
+.I new_buffer.
+Note that
+.B yy_switch_to_buffer()
+may be used by yywrap() to set things up for continued scanning, instead
+of opening a new file and pointing
+.I yyin
+at it.
+.nf
+
+ void yy_delete_buffer( YY_BUFFER_STATE buffer )
+
+.fi
+is used to reclaim the storage associated with a buffer.
+.PP
+.B yy_new_buffer()
+is an alias for
+.B yy_create_buffer(),
+provided for compatibility with the C++ use of
+.I new
+and
+.I delete
+for creating and destroying dynamic objects.
+.PP
+Finally, the
+.B YY_CURRENT_BUFFER
+macro returns a
+.B YY_BUFFER_STATE
+handle to the current buffer.
+.PP
+Here is an example of using these features for writing a scanner
+which expands include files (the
+.B <<EOF>>
+feature is discussed below):
+.nf
+
+ /* the "incl" state is used for picking up the name
+ * of an include file
+ */
+ %x incl
+
+ %{
+ #define MAX_INCLUDE_DEPTH 10
+ YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
+ int include_stack_ptr = 0;
+ %}
+
+ %%
+ include BEGIN(incl);
+
+ [a-z]+ ECHO;
+ [^a-z\\n]*\\n? ECHO;
+
+ <incl>[ \\t]* /* eat the whitespace */
+ <incl>[^ \\t\\n]+ { /* got the include file name */
+ if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
+ {
+ fprintf( stderr, "Includes nested too deeply" );
+ exit( 1 );
+ }
+
+ include_stack[include_stack_ptr++] =
+ YY_CURRENT_BUFFER;
+
+ yyin = fopen( yytext, "r" );
+
+ if ( ! yyin )
+ error( ... );
+
+ yy_switch_to_buffer(
+ yy_create_buffer( yyin, YY_BUF_SIZE ) );
+
+ BEGIN(INITIAL);
+ }
+
+ <<EOF>> {
+ if ( --include_stack_ptr < 0 )
+ {
+ yyterminate();
+ }
+
+ else
+ {
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ yy_switch_to_buffer(
+ include_stack[include_stack_ptr] );
+ }
+ }
+
+.fi
+.SH END-OF-FILE RULES
+The special rule "<<EOF>>" indicates
+actions which are to be taken when an end-of-file is
+encountered and yywrap() returns non-zero (i.e., indicates
+no further files to process). The action must finish
+by doing one of four things:
+.IP -
+assigning
+.I yyin
+to a new input file (in previous versions of flex, after doing the
+assignment you had to call the special action
+.B YY_NEW_FILE;
+this is no longer necessary);
+.IP -
+executing a
+.I return
+statement;
+.IP -
+executing the special
+.B yyterminate()
+action;
+.IP -
+or, switching to a new buffer using
+.B yy_switch_to_buffer()
+as shown in the example above.
+.PP
+<<EOF>> rules may not be used with other
+patterns; they may only be qualified with a list of start
+conditions. If an unqualified <<EOF>> rule is given, it
+applies to
+.I all
+start conditions which do not already have <<EOF>> actions. To
+specify an <<EOF>> rule for only the initial start condition, use
+.nf
+
+ <INITIAL><<EOF>>
+
+.fi
+.PP
+These rules are useful for catching things like unclosed comments.
+An example:
+.nf
+
+ %x quote
+ %%
+
+ ...other rules for dealing with quotes...
+
+ <quote><<EOF>> {
+ error( "unterminated quote" );
+ yyterminate();
+ }
+ <<EOF>> {
+ if ( *++filelist )
+ yyin = fopen( *filelist, "r" );
+ else
+ yyterminate();
+ }
+
+.fi
+.SH MISCELLANEOUS MACROS
+The macro
+.bd
+YY_USER_ACTION
+can be defined to provide an action
+which is always executed prior to the matched rule's action. For example,
+it could be #define'd to call a routine to convert yytext to lower-case.
+.PP
+The macro
+.B YY_USER_INIT
+may be defined to provide an action which is always executed before
+the first scan (and before the scanner's internal initializations are done).
+For example, it could be used to call a routine to read
+in a data table or open a logging file.
+.PP
+In the generated scanner, the actions are all gathered in one large
+switch statement and separated using
+.B YY_BREAK,
+which may be redefined. By default, it is simply a "break", to separate
+each rule's action from the following rule's.
+Redefining
+.B YY_BREAK
+allows, for example, C++ users to
+#define YY_BREAK to do nothing (while being very careful that every
+rule ends with a "break" or a "return"!) to avoid suffering from
+unreachable statement warnings where because a rule's action ends with
+"return", the
+.B YY_BREAK
+is inaccessible.
+.SH INTERFACING WITH YACC
+One of the main uses of
+.I flex
+is as a companion to the
+.I yacc
+parser-generator.
+.I yacc
+parsers expect to call a routine named
+.B yylex()
+to find the next input token. The routine is supposed to
+return the type of the next token as well as putting any associated
+value in the global
+.B yylval.
+To use
+.I flex
+with
+.I yacc,
+one specifies the
+.B \-d
+option to
+.I yacc
+to instruct it to generate the file
+.B y.tab.h
+containing definitions of all the
+.B %tokens
+appearing in the
+.I yacc
+input. This file is then included in the
+.I flex
+scanner. For example, if one of the tokens is "TOK_NUMBER",
+part of the scanner might look like:
+.nf
+
+ %{
+ #include "y.tab.h"
+ %}
+
+ %%
+
+ [0-9]+ yylval = atoi( yytext ); return TOK_NUMBER;
+
+.fi
+.SH OPTIONS
+.I flex
+has the following options:
+.TP
+.B \-b
+Generate backing-up information to
+.I lex.backup.
+This is a list of scanner states which require backing up
+and the input characters on which they do so. By adding rules one
+can remove backing-up states. If all backing-up states
+are eliminated and
+.B \-Cf
+or
+.B \-CF
+is used, the generated scanner will run faster (see the
+.B \-p
+flag). Only users who wish to squeeze every last cycle out of their
+scanners need worry about this option. (See the section on Performance
+Considerations below.)
+.TP
+.B \-c
+is a do-nothing, deprecated option included for POSIX compliance.
+.IP
+.B NOTE:
+in previous releases of
+.I flex
+.B \-c
+specified table-compression options. This functionality is
+now given by the
+.B \-C
+flag. To ease the the impact of this change, when
+.I flex
+encounters
+.B \-c,
+it currently issues a warning message and assumes that
+.B \-C
+was desired instead. In the future this "promotion" of
+.B \-c
+to
+.B \-C
+will go away in the name of full POSIX compliance (unless
+the POSIX meaning is removed first).
+.TP
+.B \-d
+makes the generated scanner run in
+.I debug
+mode. Whenever a pattern is recognized and the global
+.B yy_flex_debug
+is non-zero (which is the default),
+the scanner will write to
+.I stderr
+a line of the form:
+.nf
+
+ --accepting rule at line 53 ("the matched text")
+
+.fi
+The line number refers to the location of the rule in the file
+defining the scanner (i.e., the file that was fed to flex). Messages
+are also generated when the scanner backs up, accepts the
+default rule, reaches the end of its input buffer (or encounters
+a NUL; at this point, the two look the same as far as the scanner's concerned),
+or reaches an end-of-file.
+.TP
+.B \-f
+specifies
+.I fast scanner.
+No table compression is done and stdio is bypassed.
+The result is large but fast. This option is equivalent to
+.B \-Cfr
+(see below).
+.TP
+.B \-h
+generates a "help" summary of
+.I flex's
+options to
+.I stderr
+and then exits.
+.TP
+.B \-i
+instructs
+.I flex
+to generate a
+.I case-insensitive
+scanner. The case of letters given in the
+.I flex
+input patterns will
+be ignored, and tokens in the input will be matched regardless of case. The
+matched text given in
+.I yytext
+will have the preserved case (i.e., it will not be folded).
+.TP
+.B \-l
+turns on maximum compatibility with the original AT&T
+.I lex
+implementation. Note that this does not mean
+.I full
+compatibility. Use of this option costs a considerable amount of
+performance, and it cannot be used with the
+.B \-+, -f, -F, -Cf,
+or
+.B -CF
+options. For details on the compatibilities it provides, see the section
+"Incompatibilities With Lex And POSIX" below.
+.TP
+.B \-n
+is another do-nothing, deprecated option included only for
+POSIX compliance.
+.TP
+.B \-p
+generates a performance report to stderr. The report
+consists of comments regarding features of the
+.I flex
+input file which will cause a serious loss of performance in the resulting
+scanner. If you give the flag twice, you will also get comments regarding
+features that lead to minor performance losses.
+.IP
+Note that the use of
+.B REJECT
+and variable trailing context (see the Bugs section in lex(1))
+entails a substantial performance penalty; use of
+.I yymore(),
+the
+.B ^
+operator,
+and the
+.B \-I
+flag entail minor performance penalties.
+.TP
+.B \-s
+causes the
+.I default rule
+(that unmatched scanner input is echoed to
+.I stdout)
+to be suppressed. If the scanner encounters input that does not
+match any of its rules, it aborts with an error. This option is
+useful for finding holes in a scanner's rule set.
+.TP
+.B \-t
+instructs
+.I flex
+to write the scanner it generates to standard output instead
+of
+.B lex.yy.c.
+.TP
+.B \-v
+specifies that
+.I flex
+should write to
+.I stderr
+a summary of statistics regarding the scanner it generates.
+Most of the statistics are meaningless to the casual
+.I flex
+user, but the first line identifies the version of
+.I flex
+(same as reported by
+.B \-V),
+and the next line the flags used when generating the scanner, including
+those that are on by default.
+.TP
+.B \-w
+suppresses warning messages.
+.TP
+.B \-B
+instructs
+.I flex
+to generate a
+.I batch
+scanner, the opposite of
+.I interactive
+scanners generated by
+.B \-I
+(see below). In general, you use
+.B \-B
+when you are
+.I certain
+that your scanner will never be used interactively, and you want to
+squeeze a
+.I little
+more performance out of it. If your goal is instead to squeeze out a
+.I lot
+more performance, you should be using the
+.B \-Cf
+or
+.B \-CF
+options (discussed below), which turn on
+.B \-B
+automatically anyway.
+.TP
+.B \-F
+specifies that the
+.ul
+fast
+scanner table representation should be used (and stdio
+bypassed). This representation is
+about as fast as the full table representation
+.B (-f),
+and for some sets of patterns will be considerably smaller (and for
+others, larger). In general, if the pattern set contains both "keywords"
+and a catch-all, "identifier" rule, such as in the set:
+.nf
+
+ "case" return TOK_CASE;
+ "switch" return TOK_SWITCH;
+ ...
+ "default" return TOK_DEFAULT;
+ [a-z]+ return TOK_ID;
+
+.fi
+then you're better off using the full table representation. If only
+the "identifier" rule is present and you then use a hash table or some such
+to detect the keywords, you're better off using
+.B -F.
+.IP
+This option is equivalent to
+.B \-CFr
+(see below). It cannot be used with
+.B \-+.
+.TP
+.B \-I
+instructs
+.I flex
+to generate an
+.I interactive
+scanner. An interactive scanner is one that only looks ahead to decide
+what token has been matched if it absolutely must. It turns out that
+always looking one extra character ahead, even if the scanner has already
+seen enough text to disambiguate the current token, is a bit faster than
+only looking ahead when necessary. But scanners that always look ahead
+give dreadful interactive performance; for example, when a user types
+a newline, it is not recognized as a newline token until they enter
+.I another
+token, which often means typing in another whole line.
+.IP
+.I Flex
+scanners default to
+.I interactive
+unless you use the
+.B \-Cf
+or
+.B \-CF
+table-compression options (see below). That's because if you're looking
+for high-performance you should be using one of these options, so if you
+didn't,
+.I flex
+assumes you'd rather trade off a bit of run-time performance for intuitive
+interactive behavior. Note also that you
+.I cannot
+use
+.B \-I
+in conjunction with
+.B \-Cf
+or
+.B \-CF.
+Thus, this option is not really needed; it is on by default for all those
+cases in which it is allowed.
+.IP
+You can force a scanner to
+.I not
+be interactive by using
+.B \-B
+(see above).
+.TP
+.B \-L
+instructs
+.I flex
+not to generate
+.B #line
+directives. Without this option,
+.I flex
+peppers the generated scanner
+with #line directives so error messages in the actions will be correctly
+located with respect to the original
+.I flex
+input file, and not to
+the fairly meaningless line numbers of
+.B lex.yy.c.
+(Unfortunately
+.I flex
+does not presently generate the necessary directives
+to "retarget" the line numbers for those parts of
+.B lex.yy.c
+which it generated. So if there is an error in the generated code,
+a meaningless line number is reported.)
+.TP
+.B \-T
+makes
+.I flex
+run in
+.I trace
+mode. It will generate a lot of messages to
+.I stderr
+concerning
+the form of the input and the resultant non-deterministic and deterministic
+finite automata. This option is mostly for use in maintaining
+.I flex.
+.TP
+.B \-V
+prints the version number to
+.I stderr
+and exits.
+.TP
+.B \-7
+instructs
+.I flex
+to generate a 7-bit scanner, i.e., one which can only recognized 7-bit
+characters in its input. The advantage of using
+.B \-7
+is that the scanner's tables can be up to half the size of those generated
+using the
+.B \-8
+option (see below). The disadvantage is that such scanners often hang
+or crash if their input contains an 8-bit character.
+.IP
+Note, however, that unless you generate your scanner using the
+.B \-Cf
+or
+.B \-CF
+table compression options, use of
+.B \-7
+will save only a small amount of table space, and make your scanner
+considerably less portable.
+.I Flex's
+default behavior is to generate an 8-bit scanner unless you use the
+.B \-Cf
+or
+.B \-CF,
+in which case
+.I flex
+defaults to generating 7-bit scanners unless your site was always
+configured to generate 8-bit scanners (as will often be the case
+with non-USA sites). You can tell whether flex generated a 7-bit
+or an 8-bit scanner by inspecting the flag summary in the
+.B \-v
+output as described above.
+.IP
+Note that if you use
+.B \-Cfe
+or
+.B \-CFe
+(those table compression options, but also using equivalence classes as
+discussed see below), flex still defaults to generating an 8-bit
+scanner, since usually with these compression options full 8-bit tables
+are not much more expensive than 7-bit tables.
+.TP
+.B \-8
+instructs
+.I flex
+to generate an 8-bit scanner, i.e., one which can recognize 8-bit
+characters. This flag is only needed for scanners generated using
+.B \-Cf
+or
+.B \-CF,
+as otherwise flex defaults to generating an 8-bit scanner anyway.
+.IP
+See the discussion of
+.B \-7
+above for flex's default behavior and the tradeoffs between 7-bit
+and 8-bit scanners.
+.TP
+.B \-+
+specifies that you want flex to generate a C++
+scanner class. See the section on Generating C++ Scanners below for
+details.
+.TP
+.B \-C[aefFmr]
+controls the degree of table compression and, more generally, trade-offs
+between small scanners and fast scanners.
+.IP
+.B \-Ca
+("align") instructs flex to trade off larger tables in the
+generated scanner for faster performance because the elements of
+the tables are better aligned for memory access and computation. On some
+RISC architectures, fetching and manipulating longwords is more efficient
+than with smaller-sized datums such as shortwords. This option can
+double the size of the tables used by your scanner.
+.IP
+.B \-Ce
+directs
+.I flex
+to construct
+.I equivalence classes,
+i.e., sets of characters
+which have identical lexical properties (for example, if the only
+appearance of digits in the
+.I flex
+input is in the character class
+"[0-9]" then the digits '0', '1', ..., '9' will all be put
+in the same equivalence class). Equivalence classes usually give
+dramatic reductions in the final table/object file sizes (typically
+a factor of 2-5) and are pretty cheap performance-wise (one array
+look-up per character scanned).
+.IP
+.B \-Cf
+specifies that the
+.I full
+scanner tables should be generated -
+.I flex
+should not compress the
+tables by taking advantages of similar transition functions for
+different states.
+.IP
+.B \-CF
+specifies that the alternate fast scanner representation (described
+above under the
+.B \-F
+flag)
+should be used. This option cannot be used with
+.B \-+.
+.IP
+.B \-Cm
+directs
+.I flex
+to construct
+.I meta-equivalence classes,
+which are sets of equivalence classes (or characters, if equivalence
+classes are not being used) that are commonly used together. Meta-equivalence
+classes are often a big win when using compressed tables, but they
+have a moderate performance impact (one or two "if" tests and one
+array look-up per character scanned).
+.IP
+.B \-Cr
+causes the generated scanner to
+.I bypass
+use of the standard I/O library (stdio) for input. Instead of calling
+.B fread()
+or
+.B getc(),
+the scanner will use the
+.B read()
+system call, resulting in a performance gain which varies from system
+to system, but in general is probably negligible unless you are also using
+.B \-Cf
+or
+.B \-CF.
+Using
+.B \-Cr
+can cause strange behavior if, for example, you read from
+.I yyin
+using stdio prior to calling the scanner (because the scanner will miss
+whatever text your previous reads left in the stdio input buffer).
+.IP
+.B \-Cr
+has no effect if you define
+.B YY_INPUT
+(see The Generated Scanner above).
+.IP
+A lone
+.B \-C
+specifies that the scanner tables should be compressed but neither
+equivalence classes nor meta-equivalence classes should be used.
+.IP
+The options
+.B \-Cf
+or
+.B \-CF
+and
+.B \-Cm
+do not make sense together - there is no opportunity for meta-equivalence
+classes if the table is not being compressed. Otherwise the options
+may be freely mixed, and are cumulative.
+.IP
+The default setting is
+.B \-Cem,
+which specifies that
+.I flex
+should generate equivalence classes
+and meta-equivalence classes. This setting provides the highest
+degree of table compression. You can trade off
+faster-executing scanners at the cost of larger tables with
+the following generally being true:
+.nf
+
+ slowest & smallest
+ -Cem
+ -Cm
+ -Ce
+ -C
+ -C{f,F}e
+ -C{f,F}
+ -C{f,F}a
+ fastest & largest
+
+.fi
+Note that scanners with the smallest tables are usually generated and
+compiled the quickest, so
+during development you will usually want to use the default, maximal
+compression.
+.IP
+.B \-Cfe
+is often a good compromise between speed and size for production
+scanners.
+.TP
+.B \-Pprefix
+changes the default
+.I "yy"
+prefix used by
+.I flex
+for all globally-visible variable and function names to instead be
+.I prefix.
+For example,
+.B \-Pfoo
+changes the name of
+.B yytext
+to
+.B footext.
+It also changes the name of the default output file from
+.B lex.yy.c
+to
+.B lex.foo.c.
+Here are all of the names affected:
+.nf
+
+ yyFlexLexer
+ yy_create_buffer
+ yy_delete_buffer
+ yy_flex_debug
+ yy_init_buffer
+ yy_load_buffer_state
+ yy_switch_to_buffer
+ yyin
+ yyleng
+ yylex
+ yyout
+ yyrestart
+ yytext
+ yywrap
+
+.fi
+Within your scanner itself, you can still refer to the global variables
+and functions using either version of their name; but eternally, they
+have the modified name.
+.IP
+This option lets you easily link together multiple
+.I flex
+programs into the same executable. Note, though, that using this
+option also renames
+.B yywrap(),
+so you now
+.I must
+provide your own (appropriately-named) version of the routine for your
+scanner, as linking with
+.B \-lfl
+no longer provides one for you by default.
+.TP
+.B \-Sskeleton_file
+overrides the default skeleton file from which
+.I flex
+constructs its scanners. You'll never need this option unless you are doing
+.I flex
+maintenance or development.
+.SH PERFORMANCE CONSIDERATIONS
+The main design goal of
+.I flex
+is that it generate high-performance scanners. It has been optimized
+for dealing well with large sets of rules. Aside from the effects on
+scanner speed of the table compression
+.B \-C
+options outlined above,
+there are a number of options/actions which degrade performance. These
+are, from most expensive to least:
+.nf
+
+ REJECT
+
+ pattern sets that require backing up
+ arbitrary trailing context
+
+ yymore()
+ '^' beginning-of-line operator
+
+.fi
+with the first three all being quite expensive and the last two
+being quite cheap. Note also that
+.B unput()
+is implemented as a routine call that potentially does quite a bit of
+work, while
+.B yyless()
+is a quite-cheap macro; so if just putting back some excess text you
+scanned, use
+.B yyless().
+.PP
+.B REJECT
+should be avoided at all costs when performance is important.
+It is a particularly expensive option.
+.PP
+Getting rid of backing up is messy and often may be an enormous
+amount of work for a complicated scanner. In principal, one begins
+by using the
+.B \-b
+flag to generate a
+.I lex.backup
+file. For example, on the input
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+.fi
+the file looks like:
+.nf
+
+ State #6 is non-accepting -
+ associated rule line numbers:
+ 2 3
+ out-transitions: [ o ]
+ jam-transitions: EOF [ \\001-n p-\\177 ]
+
+ State #8 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ a ]
+ jam-transitions: EOF [ \\001-` b-\\177 ]
+
+ State #9 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ r ]
+ jam-transitions: EOF [ \\001-q s-\\177 ]
+
+ Compressed tables always back up.
+
+.fi
+The first few lines tell us that there's a scanner state in
+which it can make a transition on an 'o' but not on any other
+character, and that in that state the currently scanned text does not match
+any rule. The state occurs when trying to match the rules found
+at lines 2 and 3 in the input file.
+If the scanner is in that state and then reads
+something other than an 'o', it will have to back up to find
+a rule which is matched. With
+a bit of headscratching one can see that this must be the
+state it's in when it has seen "fo". When this has happened,
+if anything other than another 'o' is seen, the scanner will
+have to back up to simply match the 'f' (by the default rule).
+.PP
+The comment regarding State #8 indicates there's a problem
+when "foob" has been scanned. Indeed, on any character other
+than an 'a', the scanner will have to back up to accept "foo".
+Similarly, the comment for State #9 concerns when "fooba" has
+been scanned and an 'r' does not follow.
+.PP
+The final comment reminds us that there's no point going to
+all the trouble of removing backing up from the rules unless
+we're using
+.B \-Cf
+or
+.B \-CF,
+since there's no performance gain doing so with compressed scanners.
+.PP
+The way to remove the backing up is to add "error" rules:
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+ fooba |
+ foob |
+ fo {
+ /* false alarm, not really a keyword */
+ return TOK_ID;
+ }
+
+.fi
+.PP
+Eliminating backing up among a list of keywords can also be
+done using a "catch-all" rule:
+.nf
+
+ %%
+ foo return TOK_KEYWORD;
+ foobar return TOK_KEYWORD;
+
+ [a-z]+ return TOK_ID;
+
+.fi
+This is usually the best solution when appropriate.
+.PP
+Backing up messages tend to cascade.
+With a complicated set of rules it's not uncommon to get hundreds
+of messages. If one can decipher them, though, it often
+only takes a dozen or so rules to eliminate the backing up (though
+it's easy to make a mistake and have an error rule accidentally match
+a valid token. A possible future
+.I flex
+feature will be to automatically add rules to eliminate backing up).
+.PP
+.I Variable
+trailing context (where both the leading and trailing parts do not have
+a fixed length) entails almost the same performance loss as
+.B REJECT
+(i.e., substantial). So when possible a rule like:
+.nf
+
+ %%
+ mouse|rat/(cat|dog) run();
+
+.fi
+is better written:
+.nf
+
+ %%
+ mouse/cat|dog run();
+ rat/cat|dog run();
+
+.fi
+or as
+.nf
+
+ %%
+ mouse|rat/cat run();
+ mouse|rat/dog run();
+
+.fi
+Note that here the special '|' action does
+.I not
+provide any savings, and can even make things worse (see
+.PP
+A final note regarding performance: as mentioned above in the section
+How the Input is Matched, dynamically resizing
+.B yytext
+to accomodate huge tokens is a slow process because it presently requires that
+the (huge) token be rescanned from the beginning. Thus if performance is
+vital, you should attempt to match "large" quantities of text but not
+"huge" quantities, where the cutoff between the two is at about 8K
+characters/token.
+.PP
+Another area where the user can increase a scanner's performance
+(and one that's easier to implement) arises from the fact that
+the longer the tokens matched, the faster the scanner will run.
+This is because with long tokens the processing of most input
+characters takes place in the (short) inner scanning loop, and
+does not often have to go through the additional work of setting up
+the scanning environment (e.g.,
+.B yytext)
+for the action. Recall the scanner for C comments:
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]*
+ <comment>"*"+[^*/\\n]*
+ <comment>\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+This could be sped up by writing it as:
+.nf
+
+ %x comment
+ %%
+ int line_num = 1;
+
+ "/*" BEGIN(comment);
+
+ <comment>[^*\\n]*
+ <comment>[^*\\n]*\\n ++line_num;
+ <comment>"*"+[^*/\\n]*
+ <comment>"*"+[^*/\\n]*\\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+.fi
+Now instead of each newline requiring the processing of another
+action, recognizing the newlines is "distributed" over the other rules
+to keep the matched text as long as possible. Note that
+.I adding
+rules does
+.I not
+slow down the scanner! The speed of the scanner is independent
+of the number of rules or (modulo the considerations given at the
+beginning of this section) how complicated the rules are with
+regard to operators such as '*' and '|'.
+.PP
+A final example in speeding up a scanner: suppose you want to scan
+through a file containing identifiers and keywords, one per line
+and with no other extraneous characters, and recognize all the
+keywords. A natural first approach is:
+.nf
+
+ %%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+ .|\\n /* it's not a keyword */
+
+.fi
+To eliminate the back-tracking, introduce a catch-all rule:
+.nf
+
+ %%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+ [a-z]+ |
+ .|\\n /* it's not a keyword */
+
+.fi
+Now, if it's guaranteed that there's exactly one word per line,
+then we can reduce the total number of matches by a half by
+merging in the recognition of newlines with that of the other
+tokens:
+.nf
+
+ %%
+ asm\\n |
+ auto\\n |
+ break\\n |
+ ... etc ...
+ volatile\\n |
+ while\\n /* it's a keyword */
+
+ [a-z]+\\n |
+ .|\\n /* it's not a keyword */
+
+.fi
+One has to be careful here, as we have now reintroduced backing up
+into the scanner. In particular, while
+.I we
+know that there will never be any characters in the input stream
+other than letters or newlines,
+.I flex
+can't figure this out, and it will plan for possibly needing to back up
+when it has scanned a token like "auto" and then the next character
+is something other than a newline or a letter. Previously it would
+then just match the "auto" rule and be done, but now it has no "auto"
+rule, only a "auto\\n" rule. To eliminate the possibility of backing up,
+we could either duplicate all rules but without final newlines, or,
+since we never expect to encounter such an input and therefore don't
+how it's classified, we can introduce one more catch-all rule, this
+one which doesn't include a newline:
+.nf
+
+ %%
+ asm\\n |
+ auto\\n |
+ break\\n |
+ ... etc ...
+ volatile\\n |
+ while\\n /* it's a keyword */
+
+ [a-z]+\\n |
+ [a-z]+ |
+ .|\\n /* it's not a keyword */
+
+.fi
+Compiled with
+.B \-Cf,
+this is about as fast as one can get a
+.I flex
+scanner to go for this particular problem.
+.PP
+A final note:
+.I flex
+is slow when matching NUL's, particularly when a token contains
+multiple NUL's.
+It's best to write rules which match
+.I short
+amounts of text if it's anticipated that the text will often include NUL's.
+.SH GENERATING C++ SCANNERS
+.I flex
+provides two different ways to generate scanners for use with C++. The
+first way is to simply compile a scanner generated by
+.I flex
+using a C++ compiler instead of a C compiler. You should not encounter
+any compilations errors (please report any you find to the email address
+given in the Author section below). You can then use C++ code in your
+rule actions instead of C code. Note that the default input source for
+your scanner remains
+.I yyin,
+and default echoing is still done to
+.I yyout.
+Both of these remain
+.I FILE *
+variables and not C++
+.I streams.
+.PP
+You can also use
+.I flex
+to generate a C++ scanner class, using the
+.B \-+
+option, which is automatically specified if the name of the flex
+executable ends in a '+', such as
+.I flex++.
+When using this option, flex defaults to generating the scanner to the file
+.B lex.yy.cc
+instead of
+.B lex.yy.c.
+The generated scanner includes the header file
+.I FlexLexer.h,
+which defines the interface to two C++ classes.
+.PP
+The first class,
+.B FlexLexer,
+provides an abstract base class defining the general scanner class
+interface. It provides the following member functions:
+.TP
+.B const char* YYText()
+returns the text of the most recently matched token, the equivalent of
+.B yytext.
+.TP
+.B int YYLeng()
+returns the length of the most recently matched token, the equivalent of
+.B yyleng.
+.PP
+Also provided are member functions equivalent to
+.B yy_switch_to_buffer(),
+.B yy_create_buffer()
+(though the first argument is an
+.B istream*
+object pointer and not a
+.B FILE*),
+.B yy_delete_buffer(),
+and
+.B yyrestart()
+(again, the first argument is a
+.B istream*
+object pointer).
+.PP
+The second class defined in
+.I FlexLexer.h
+is
+.B yyFlexLexer,
+which is derived from
+.B FlexLexer.
+It defines the following additional member functions:
+.TP
+.B
+yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
+constructs a
+.B yyFlexLexer
+object using the given streams for input and output. If not specified,
+the streams default to
+.B cin
+and
+.B cout,
+respectively.
+.TP
+.B virtual int yylex()
+performs the same role is
+.B yylex()
+does for ordinary flex scanners: it scans the input stream, consuming
+tokens, until a rule's action returns a value.
+.PP
+In addition,
+.B yyFlexLexer
+defines the following protected virtual functions which you can redefine
+in derived classes to tailor the scanner:
+.TP
+.B
+virtual int LexerInput( char* buf, int max_size )
+reads up to
+.B max_size
+characters into
+.B buf
+and returns the number of characters read. To indicate end-of-input,
+return 0 characters. Note that "interactive" scanners (see the
+.B \-B
+and
+.B \-I
+flags) define the macro
+.B YY_INTERACTIVE.
+If you redefine
+.B LexerInput()
+and need to take different actions depending on whether or not
+the scanner might be scanning an interactive input source, you can
+test for the presence of this name via
+.B #ifdef.
+.TP
+.B
+virtual void LexerOutput( const char* buf, int size )
+writes out
+.B size
+characters from the buffer
+.B buf,
+which, while NUL-terminated, may also contain "internal" NUL's if
+the scanner's rules can match text with NUL's in them.
+.TP
+.B
+virtual void LexerError( const char* msg )
+reports a fatal error message. The default version of this function
+writes the message to the stream
+.B cerr
+and exits.
+.PP
+Note that a
+.B yyFlexLexer
+object contains its
+.I entire
+scanning state. Thus you can use such objects to create reentrant
+scanners. You can instantiate multiple instances of the same
+.B yyFlexLexer
+class, and you can also combine multiple C++ scanner classes together
+in the same program using the
+.B \-P
+option discussed above.
+.PP
+Finally, note that the
+.B %array
+feature is not available to C++ scanner classes; you must use
+.B %pointer
+(the default).
+.PP
+Here is an example of a simple C++ scanner:
+.nf
+
+ // An example of using the flex C++ scanner class.
+
+ %{
+ int mylineno = 0;
+ %}
+
+ string \\"[^\\n"]+\\"
+
+ ws [ \\t]+
+
+ alpha [A-Za-z]
+ dig [0-9]
+ name ({alpha}|{dig}|\\$)({alpha}|{dig}|[_.\\-/$])*
+ num1 [-+]?{dig}+\\.?([eE][-+]?{dig}+)?
+ num2 [-+]?{dig}*\\.{dig}+([eE][-+]?{dig}+)?
+ number {num1}|{num2}
+
+ %%
+
+ {ws} /* skip blanks and tabs */
+
+ "/*" {
+ int c;
+
+ while((c = yyinput()) != 0)
+ {
+ if(c == '\\n')
+ ++mylineno;
+
+ else if(c == '*')
+ {
+ if((c = yyinput()) == '/')
+ break;
+ else
+ unput(c);
+ }
+ }
+ }
+
+ {number} cout << "number " << YYText() << '\\n';
+
+ \\n mylineno++;
+
+ {name} cout << "name " << YYText() << '\\n';
+
+ {string} cout << "string " << YYText() << '\\n';
+
+ %%
+
+ int main( int /* argc */, char** /* argv */ )
+ {
+ FlexLexer* lexer = new yyFlexLexer;
+ while(lexer->yylex() != 0)
+ ;
+ return 0;
+ }
+.fi
+IMPORTANT: the present form of the scanning class is
+.I experimental
+and may change considerably between major releases.
+.SH INCOMPATIBILITIES WITH LEX AND POSIX
+.I flex
+is a rewrite of the AT&T Unix
+.I lex
+tool (the two implementations do not share any code, though),
+with some extensions and incompatibilities, both of which
+are of concern to those who wish to write scanners acceptable
+to either implementation. The POSIX
+.I lex
+specification is closer to
+.I flex's
+behavior than that of the original
+.I lex
+implementation, but there also remain some incompatibilities between
+.I flex
+and POSIX. The intent is that ultimately
+.I flex
+will be fully POSIX-conformant. In this section we discuss all of
+the known areas of incompatibility.
+.PP
+.I flex's
+.B \-l
+option turns on maximum compatibility with the original AT&T
+.I lex
+implementation, at the cost of a major loss in the generated scanner's
+performance. We note below which incompatibilities can be overcome
+using the
+.B \-l
+option.
+.PP
+.I flex
+is fully compatible with
+.I lex
+with the following exceptions:
+.IP -
+The undocumented
+.I lex
+scanner internal variable
+.B yylineno
+is not supported unless
+.B \-l
+is used.
+.IP
+yylineno is not part of the POSIX specification.
+.IP -
+The
+.B input()
+routine is not redefinable, though it may be called to read characters
+following whatever has been matched by a rule. If
+.B input()
+encounters an end-of-file the normal
+.B yywrap()
+processing is done. A ``real'' end-of-file is returned by
+.B input()
+as
+.I EOF.
+.IP
+Input is instead controlled by defining the
+.B YY_INPUT
+macro.
+.IP
+The
+.I flex
+restriction that
+.B input()
+cannot be redefined is in accordance with the POSIX specification,
+which simply does not specify any way of controlling the
+scanner's input other than by making an initial assignment to
+.I yyin.
+.IP -
+.I flex
+scanners are not as reentrant as
+.I lex
+scanners. In particular, if you have an interactive scanner and
+an interrupt handler which long-jumps out of the scanner, and
+the scanner is subsequently called again, you may get the following
+message:
+.nf
+
+ fatal flex scanner internal error--end of buffer missed
+
+.fi
+To reenter the scanner, first use
+.nf
+
+ yyrestart( yyin );
+
+.fi
+Note that this call will throw away any buffered input; usually this
+isn't a problem with an interactive scanner.
+.IP
+Also note that flex C++ scanner classes
+.I are
+reentrant, so if using C++ is an option for you, you should use
+them instead. See "Generating C++ Scanners" above for details.
+.IP -
+.B output()
+is not supported.
+Output from the
+.B ECHO
+macro is done to the file-pointer
+.I yyout
+(default
+.I stdout).
+.IP
+.B output()
+is not part of the POSIX specification.
+.IP -
+.I lex
+does not support exclusive start conditions (%x), though they
+are in the POSIX specification.
+.IP -
+When definitions are expanded,
+.I flex
+encloses them in parentheses.
+With lex, the following:
+.nf
+
+ NAME [A-Z][A-Z0-9]*
+ %%
+ foo{NAME}? printf( "Found it\\n" );
+ %%
+
+.fi
+will not match the string "foo" because when the macro
+is expanded the rule is equivalent to "foo[A-Z][A-Z0-9]*?"
+and the precedence is such that the '?' is associated with
+"[A-Z0-9]*". With
+.I flex,
+the rule will be expanded to
+"foo([A-Z][A-Z0-9]*)?" and so the string "foo" will match.
+.IP
+Note that if the definition begins with
+.B ^
+or ends with
+.B $
+then it is
+.I not
+expanded with parentheses, to allow these operators to appear in
+definitions without losing their special meanings. But the
+.B <s>, /,
+and
+.B <<EOF>>
+operators cannot be used in a
+.I flex
+definition.
+.IP
+Using
+.B \-l
+results in the
+.I lex
+behavior of no parentheses around the definition.
+.IP
+The POSIX specification is that the definition be enclosed in parentheses.
+.IP -
+The
+.I lex
+.B %r
+(generate a Ratfor scanner) option is not supported. It is not part
+of the POSIX specification.
+.IP -
+After a call to
+.B unput(),
+.I yytext
+and
+.I yyleng
+are undefined until the next token is matched, unless the scanner
+was built using
+.B %array.
+This is not the case with
+.I lex
+or the POSIX specification. The
+.B \-l
+option does away with this incompatibility.
+.IP -
+The precedence of the
+.B {}
+(numeric range) operator is different.
+.I lex
+interprets "abc{1,3}" as "match one, two, or
+three occurrences of 'abc'", whereas
+.I flex
+interprets it as "match 'ab'
+followed by one, two, or three occurrences of 'c'". The latter is
+in agreement with the POSIX specification.
+.IP -
+The precedence of the
+.B ^
+operator is different.
+.I lex
+interprets "^foo|bar" as "match either 'foo' at the beginning of a line,
+or 'bar' anywhere", whereas
+.I flex
+interprets it as "match either 'foo' or 'bar' if they come at the beginning
+of a line". The latter is in agreement with the POSIX specification.
+.IP -
+.I yyin
+is
+.I initialized
+by
+.I lex
+to be
+.I stdin;
+.I flex,
+on the other hand,
+initializes
+.I yyin
+to NULL
+and then
+.I assigns
+it to
+.I stdin
+the first time the scanner is called, providing
+.I yyin
+has not already been assigned to a non-NULL value. The difference is
+subtle, but the net effect is that with
+.I flex
+scanners,
+.I yyin
+does not have a valid value until the scanner has been called.
+.IP
+The
+.B \-l
+option does away with this incompatibility.
+.IP -
+The special table-size declarations such as
+.B %a
+supported by
+.I lex
+are not required by
+.I flex
+scanners;
+.I flex
+ignores them.
+.IP -
+The name
+.bd
+FLEX_SCANNER
+is #define'd so scanners may be written for use with either
+.I flex
+or
+.I lex.
+.PP
+The following
+.I flex
+features are not included in
+.I lex
+or the POSIX specification:
+.nf
+
+ yyterminate()
+ <<EOF>>
+ <*>
+ YY_DECL
+ YY_START
+ YY_USER_ACTION
+ #line directives
+ %{}'s around actions
+ multiple actions on a line
+
+.fi
+plus almost all of the flex flags.
+The last feature in the list refers to the fact that with
+.I flex
+you can put multiple actions on the same line, separated with
+semi-colons, while with
+.I lex,
+the following
+.nf
+
+ foo handle_foo(); ++num_foos_seen;
+
+.fi
+is (rather surprisingly) truncated to
+.nf
+
+ foo handle_foo();
+
+.fi
+.I flex
+does not truncate the action. Actions that are not enclosed in
+braces are simply terminated at the end of the line.
+.SH DIAGNOSTICS
+.PP
+.I warning, rule cannot be matched
+indicates that the given rule
+cannot be matched because it follows other rules that will
+always match the same text as it. For
+example, in the following "foo" cannot be matched because it comes after
+an identifier "catch-all" rule:
+.nf
+
+ [a-z]+ got_identifier();
+ foo got_foo();
+
+.fi
+Using
+.B REJECT
+in a scanner suppresses this warning.
+.PP
+.I warning,
+.B \-s
+.I
+option given but default rule can be matched
+means that it is possible (perhaps only in a particular start condition)
+that the default rule (match any single character) is the only one
+that will match a particular input. Since
+.B \-s
+was given, presumably this is not intended.
+.PP
+.I reject_used_but_not_detected undefined
+or
+.I yymore_used_but_not_detected undefined -
+These errors can occur at compile time. They indicate that the
+scanner uses
+.B REJECT
+or
+.B yymore()
+but that
+.I flex
+failed to notice the fact, meaning that
+.I flex
+scanned the first two sections looking for occurrences of these actions
+and failed to find any, but somehow you snuck some in (via a #include
+file, for example). Make an explicit reference to the action in your
+.I flex
+input file. (Note that previously
+.I flex
+supported a
+.B %used/%unused
+mechanism for dealing with this problem; this feature is still supported
+but now deprecated, and will go away soon unless the author hears from
+people who can argue compellingly that they need it.)
+.PP
+.I flex scanner jammed -
+a scanner compiled with
+.B \-s
+has encountered an input string which wasn't matched by
+any of its rules. This error can also occur due to internal problems.
+.PP
+.I token too large, exceeds YYLMAX -
+your scanner uses
+.B %array
+and one of its rules matched a string longer than the
+.B YYLMAX
+constant (8K bytes by default). You can increase the value by
+#define'ing
+.B YYLMAX
+in the definitions section of your
+.I flex
+input.
+.PP
+.I scanner requires \-8 flag to
+.I use the character 'x' -
+Your scanner specification includes recognizing the 8-bit character
+.I 'x'
+and you did not specify the \-8 flag, and your scanner defaulted to 7-bit
+because you used the
+.B \-Cf
+or
+.B \-CF
+table compression options. See the discussion of the
+.B \-7
+flag for details.
+.PP
+.I flex scanner push-back overflow -
+you used
+.B unput()
+to push back so much text that the scanner's buffer could not hold
+both the pushed-back text and the current token in
+.B yytext.
+Ideally the scanner should dynamically resize the buffer in this case, but at
+present it does not.
+.PP
+.I
+input buffer overflow, can't enlarge buffer because scanner uses REJECT -
+the scanner was working on matching an extremely large token and needed
+to expand the input buffer. This doesn't work with scanners that use
+.B
+REJECT.
+.PP
+.I
+fatal flex scanner internal error--end of buffer missed -
+This can occur in an scanner which is reentered after a long-jump
+has jumped out (or over) the scanner's activation frame. Before
+reentering the scanner, use:
+.nf
+
+ yyrestart( yyin );
+
+.fi
+or, as noted above, switch to using the C++ scanner class.
+.PP
+.I too many start conditions in <> construct! -
+you listed more start conditions in a <> construct than exist (so
+you must have listed at least one of them twice).
+.SH FILES
+See lex(1).
+.SH DEFICIENCIES / BUGS
+Again, see lex(1).
+.SH "SEE ALSO"
+.PP
+lex(1), yacc(1), sed(1), awk(1).
+.PP
+M. E. Lesk and E. Schmidt,
+.I LEX \- Lexical Analyzer Generator
+.SH AUTHOR
+Vern Paxson, with the help of many ideas and much inspiration from
+Van Jacobson. Original version by Jef Poskanzer. The fast table
+representation is a partial implementation of a design done by Van
+Jacobson. The implementation was done by Kevin Gong and Vern Paxson.
+.PP
+Thanks to the many
+.I flex
+beta-testers, feedbackers, and contributors, especially Francois Pinard,
+Casey Leedom,
+Nelson H.F. Beebe, benson@odi.com, Peter A. Bigot, Keith Bostic, Frederic
+Brehm, Nick Christopher, Jason Coughlin, Bill Cox, Dave Curtis, Scott David
+Daniels, Chris G. Demetriou, Mike Donahue, Chuck Doucette, Tom Epperly, Leo
+Eskin, Chris Faylor, Jon Forrest, Kaveh R. Ghazi,
+Eric Goldman, Ulrich Grepel, Jan Hajic,
+Jarkko Hietaniemi, Eric Hughes, John Interrante,
+Ceriel Jacobs, Jeffrey R. Jones, Henry
+Juengst, Amir Katz, ken@ken.hilco.com, Kevin B. Kenny, Marq Kole, Ronald
+Lamprecht, Greg Lee, Craig Leres, John Levine, Steve Liddle,
+Mohamed el Lozy, Brian Madsen, Chris
+Metcalf, Luke Mewburn, Jim Meyering, G.T. Nicol, Landon Noll, Marc Nozell,
+Richard Ohnemus, Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
+Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Frederic Raimbault,
+Rick Richardson,
+Kevin Rodgers, Jim Roskind,
+Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
+Alex Siegel, Mike Stump, Paul Stuart, Dave Tallman, Chris Thewalt,
+Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
+Yap, Nathan Zelle, David Zuhn, and those whose names have slipped my marginal
+mail-archiving skills but whose contributions are appreciated all the
+same.
+.PP
+Thanks to Keith Bostic, Jon Forrest, Noah Friedman,
+John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T.
+Nicol, Francois Pinard, Rich Salz, and Richard Stallman for help with various
+distribution headaches.
+.PP
+Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to
+Benson Margulies and Fred Burke for C++ support; to Kent Williams and Tom
+Epperly for C++ class support; to Ove Ewerlid for support of NUL's; and to
+Eric Hughes for support of multiple buffers.
+.PP
+This work was primarily done when I was with the Real Time Systems Group
+at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all there
+for the support I received.
+.PP
+Send comments to:
+.nf
+
+ Vern Paxson
+ Systems Engineering
+ Bldg. 46A, Room 1123
+ Lawrence Berkeley Laboratory
+ University of California
+ Berkeley, CA 94720
+
+ vern@ee.lbl.gov
+
+.fi
diff --git a/usr.bin/lex/lib/Makefile b/usr.bin/lex/lib/Makefile
new file mode 100644
index 0000000..689b460
--- /dev/null
+++ b/usr.bin/lex/lib/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.1.1.1 1994/08/24 13:10:34 csgr Exp $
+
+LIB= ln
+SRCS= libmain.c libyywrap.c
+LINKS= ${LIBDIR}/libln.a ${LIBDIR}/libl.a \
+
+.if !defined(NOPROFILE)
+LINKS+= ${LIBDIR}/libln_p.a ${LIBDIR}/libl_p.a
+.endif
+
+NOSHARED=
+
+.include <bsd.lib.mk>
+
diff --git a/usr.bin/lex/lib/libmain.c b/usr.bin/lex/lib/libmain.c
new file mode 100644
index 0000000..a893c7a
--- /dev/null
+++ b/usr.bin/lex/lib/libmain.c
@@ -0,0 +1,12 @@
+/* libmain - flex run-time support library "main" function */
+
+/* $Header: /home/daffy/u0/vern/flex/RCS/libmain.c,v 1.3 93/04/14 22:41:55 vern Exp $ */
+
+extern int yylex();
+
+int main( argc, argv )
+int argc;
+char *argv[];
+ {
+ return yylex();
+ }
diff --git a/usr.bin/lex/lib/libyywrap.c b/usr.bin/lex/lib/libyywrap.c
new file mode 100644
index 0000000..b18f54e
--- /dev/null
+++ b/usr.bin/lex/lib/libyywrap.c
@@ -0,0 +1,8 @@
+/* libyywrap - flex run-time support library "yywrap" function */
+
+/* $Header: /home/daffy/u0/vern/flex/RCS/libyywrap.c,v 1.1 93/10/02 15:23:09 vern Exp $ */
+
+int yywrap()
+ {
+ return 1;
+ }
diff --git a/usr.bin/lex/main.c b/usr.bin/lex/main.c
new file mode 100644
index 0000000..2139daa
--- /dev/null
+++ b/usr.bin/lex/main.c
@@ -0,0 +1,989 @@
+/* flex - tool to generate fast lexical analyzers */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/main.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ */
+
+
+#include "flexdef.h"
+#include "version.h"
+
+static char flex_version[] = FLEX_VERSION;
+
+
+/* declare functions that have forward references */
+
+void flexinit PROTO((int, char**));
+void readin PROTO((void));
+void set_up_initial_allocations PROTO((void));
+
+
+/* these globals are all defined and commented in flexdef.h */
+int printstats, syntaxerror, eofseen, ddebug, trace, nowarn, spprdflt;
+int interactive, caseins, lex_compat, useecs, fulltbl, usemecs;
+int fullspd, gen_line_dirs, performance_report, backing_up_report;
+int C_plus_plus, long_align, use_read, yytext_is_array, csize;
+int yymore_used, reject, real_reject, continued_action;
+int yymore_really_used, reject_really_used;
+int datapos, dataline, linenum;
+FILE *skelfile = NULL;
+int skel_ind = 0;
+char *action_array;
+int action_size, defs1_offset, prolog_offset, action_offset, action_index;
+char *infilename = NULL;
+int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE];
+int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp;
+int current_mns, num_rules, num_eof_rules, default_rule;
+int current_max_rules, lastnfa;
+int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2;
+int *accptnum, *assoc_rule, *state_type;
+int *rule_type, *rule_linenum, *rule_useful;
+int current_state_type;
+int variable_trailing_context_rules;
+int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP];
+int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE];
+int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs, tecfwd[CSIZE + 1];
+int tecbck[CSIZE + 1];
+int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc;
+char **scname;
+int current_max_dfa_size, current_max_xpairs;
+int current_max_template_xpairs, current_max_dfas;
+int lastdfa, *nxt, *chk, *tnxt;
+int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz;
+union dfaacc_union *dfaacc;
+int *accsiz, *dhash, numas;
+int numsnpairs, jambase, jamstate;
+int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse;
+int current_max_ccl_tbl_size;
+Char *ccltbl;
+char nmstr[MAXLINE];
+int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs;
+int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave;
+int num_backing_up, bol_needed;
+FILE *backing_up_file;
+int end_of_buffer_state;
+char **input_files;
+int num_input_files;
+char *program_name;
+
+#ifndef SHORT_FILE_NAMES
+static char *outfile_template = "lex.%s.%s";
+#else
+static char *outfile_template = "lex%s.%s";
+#endif
+static char outfile_path[64];
+
+static int outfile_created = 0;
+static int use_stdout;
+static char *skelname = NULL;
+static char *prefix = "yy";
+
+
+int main( argc, argv )
+int argc;
+char **argv;
+ {
+ int i;
+
+ flexinit( argc, argv );
+
+ readin();
+
+ ntod();
+
+ for ( i = 1; i <= num_rules; ++i )
+ if ( ! rule_useful[i] && i != default_rule )
+ line_warning( "rule cannot be matched",
+ rule_linenum[i] );
+
+ if ( spprdflt && ! reject && rule_useful[default_rule] )
+ line_warning( "-s option given but default rule can be matched",
+ rule_linenum[default_rule] );
+
+ /* Generate the C state transition tables from the DFA. */
+ make_tables();
+
+ /* Note, flexend does not return. It exits with its argument
+ * as status.
+ */
+ flexend( 0 );
+
+ return 0; /* keep compilers/lint happy */
+ }
+
+
+/* flexend - terminate flex
+ *
+ * note
+ * This routine does not return.
+ */
+
+void flexend( exit_status )
+int exit_status;
+
+ {
+ int tblsiz;
+ int unlink();
+
+ if ( skelfile != NULL )
+ {
+ if ( ferror( skelfile ) )
+ flexfatal(
+ "error occurred when reading skeleton file" );
+
+ else if ( fclose( skelfile ) )
+ flexfatal(
+ "error occurred when closing skeleton file" );
+ }
+
+ if ( exit_status != 0 && outfile_created )
+ {
+ if ( ferror( stdout ) )
+ flexfatal( "error occurred when writing output file" );
+
+ else if ( fclose( stdout ) )
+ flexfatal( "error occurred when closing output file" );
+
+ else if ( unlink( outfile_path ) )
+ flexfatal( "error occurred when deleting output file" );
+ }
+
+ if ( backing_up_report && backing_up_file )
+ {
+ if ( num_backing_up == 0 )
+ fprintf( backing_up_file, "No backing up.\n" );
+ else if ( fullspd || fulltbl )
+ fprintf( backing_up_file,
+ "%d backing up (non-accepting) states.\n",
+ num_backing_up );
+ else
+ fprintf( backing_up_file,
+ "Compressed tables always back up.\n" );
+
+ if ( ferror( backing_up_file ) )
+ flexfatal( "error occurred when writing backup file" );
+
+ else if ( fclose( backing_up_file ) )
+ flexfatal( "error occurred when closing backup file" );
+ }
+
+ if ( printstats )
+ {
+ fprintf( stderr, "%s version %s usage statistics:\n",
+ program_name, flex_version );
+
+ fprintf( stderr, " scanner options: -" );
+
+ if ( C_plus_plus )
+ putc( '+', stderr );
+ if ( backing_up_report )
+ putc( 'b', stderr );
+ if ( ddebug )
+ putc( 'd', stderr );
+ if ( caseins )
+ putc( 'i', stderr );
+ if ( lex_compat )
+ putc( 'l', stderr );
+ if ( performance_report > 0 )
+ putc( 'p', stderr );
+ if ( performance_report > 1 )
+ putc( 'p', stderr );
+ if ( spprdflt )
+ putc( 's', stderr );
+ if ( use_stdout )
+ putc( 't', stderr );
+ if ( printstats )
+ putc( 'v', stderr ); /* always true! */
+ if ( nowarn )
+ putc( 'w', stderr );
+ if ( ! interactive )
+ putc( 'B', stderr );
+ if ( interactive )
+ putc( 'I', stderr );
+ if ( ! gen_line_dirs )
+ putc( 'L', stderr );
+ if ( trace )
+ putc( 'T', stderr );
+ if ( csize == 128 )
+ putc( '7', stderr );
+ else
+ putc( '8', stderr );
+
+ fprintf( stderr, " -C" );
+
+ if ( long_align )
+ putc( 'a', stderr );
+ if ( fulltbl )
+ putc( 'f', stderr );
+ if ( fullspd )
+ putc( 'F', stderr );
+ if ( useecs )
+ putc( 'e', stderr );
+ if ( usemecs )
+ putc( 'm', stderr );
+ if ( use_read )
+ putc( 'r', stderr );
+
+ if ( skelname )
+ fprintf( stderr, " -S%s", skelname );
+
+ if ( strcmp( prefix, "yy" ) )
+ fprintf( stderr, " -P%s", prefix );
+
+ putc( '\n', stderr );
+
+ fprintf( stderr, " %d/%d NFA states\n", lastnfa, current_mns );
+ fprintf( stderr, " %d/%d DFA states (%d words)\n", lastdfa,
+ current_max_dfas, totnst );
+ fprintf( stderr, " %d rules\n",
+ num_rules + num_eof_rules - 1 /* - 1 for def. rule */ );
+
+ if ( num_backing_up == 0 )
+ fprintf( stderr, " No backing up\n" );
+ else if ( fullspd || fulltbl )
+ fprintf( stderr,
+ " %d backing-up (non-accepting) states\n",
+ num_backing_up );
+ else
+ fprintf( stderr,
+ " Compressed tables always back-up\n" );
+
+ if ( bol_needed )
+ fprintf( stderr,
+ " Beginning-of-line patterns used\n" );
+
+ fprintf( stderr, " %d/%d start conditions\n", lastsc,
+ current_max_scs );
+ fprintf( stderr,
+ " %d epsilon states, %d double epsilon states\n",
+ numeps, eps2 );
+
+ if ( lastccl == 0 )
+ fprintf( stderr, " no character classes\n" );
+ else
+ fprintf( stderr,
+ " %d/%d character classes needed %d/%d words of storage, %d reused\n",
+ lastccl, current_maxccls,
+ cclmap[lastccl] + ccllen[lastccl],
+ current_max_ccl_tbl_size, cclreuse );
+
+ fprintf( stderr, " %d state/nextstate pairs created\n",
+ numsnpairs );
+ fprintf( stderr, " %d/%d unique/duplicate transitions\n",
+ numuniq, numdup );
+
+ if ( fulltbl )
+ {
+ tblsiz = lastdfa * numecs;
+ fprintf( stderr, " %d table entries\n", tblsiz );
+ }
+
+ else
+ {
+ tblsiz = 2 * (lastdfa + numtemps) + 2 * tblend;
+
+ fprintf( stderr, " %d/%d base-def entries created\n",
+ lastdfa + numtemps, current_max_dfas );
+ fprintf( stderr,
+ " %d/%d (peak %d) nxt-chk entries created\n",
+ tblend, current_max_xpairs, peakpairs );
+ fprintf( stderr,
+ " %d/%d (peak %d) template nxt-chk entries created\n",
+ numtemps * nummecs, current_max_template_xpairs,
+ numtemps * numecs );
+ fprintf( stderr, " %d empty table entries\n", nummt );
+ fprintf( stderr, " %d protos created\n", numprots );
+ fprintf( stderr, " %d templates created, %d uses\n",
+ numtemps, tmpuses );
+ }
+
+ if ( useecs )
+ {
+ tblsiz = tblsiz + csize;
+ fprintf( stderr,
+ " %d/%d equivalence classes created\n",
+ numecs, csize );
+ }
+
+ if ( usemecs )
+ {
+ tblsiz = tblsiz + numecs;
+ fprintf( stderr,
+ " %d/%d meta-equivalence classes created\n",
+ nummecs, csize );
+ }
+
+ fprintf( stderr,
+ " %d (%d saved) hash collisions, %d DFAs equal\n",
+ hshcol, hshsave, dfaeql );
+ fprintf( stderr, " %d sets of reallocations needed\n",
+ num_reallocs );
+ fprintf( stderr, " %d total table entries needed\n", tblsiz );
+ }
+
+#ifndef VMS
+ exit( exit_status );
+#else
+ exit( exit_status + 1 );
+#endif
+ }
+
+
+/* flexinit - initialize flex */
+
+void flexinit( argc, argv )
+int argc;
+char **argv;
+ {
+ int i, sawcmpflag;
+ int csize_given, interactive_given;
+ char *arg, *mktemp();
+
+ printstats = syntaxerror = trace = spprdflt = caseins = false;
+ lex_compat = false;
+ C_plus_plus = backing_up_report = ddebug = fulltbl = fullspd = false;
+ long_align = nowarn = yymore_used = continued_action = reject = false;
+ yytext_is_array = yymore_really_used = reject_really_used = false;
+ gen_line_dirs = usemecs = useecs = true;
+ performance_report = 0;
+
+ sawcmpflag = false;
+ use_read = use_stdout = false;
+ csize_given = false;
+ interactive_given = false;
+
+ /* Initialize dynamic array for holding the rule actions. */
+ action_size = 2048; /* default size of action array in bytes */
+ action_array = allocate_character_array( action_size );
+ defs1_offset = prolog_offset = action_offset = action_index = 0;
+ action_array[0] = '\0';
+
+ program_name = argv[0];
+
+ if ( program_name[0] != '\0' &&
+ program_name[strlen( program_name ) - 1] == '+' )
+ C_plus_plus = true;
+
+ /* read flags */
+ for ( --argc, ++argv; argc ; --argc, ++argv )
+ {
+ if ( argv[0][0] != '-' || argv[0][1] == '\0' )
+ break;
+
+ arg = argv[0];
+
+ for ( i = 1; arg[i] != '\0'; ++i )
+ switch ( arg[i] )
+ {
+ case '+':
+ C_plus_plus = true;
+ break;
+
+ case 'B':
+ interactive = false;
+ interactive_given = true;
+ break;
+
+ case 'b':
+ backing_up_report = true;
+ break;
+
+ case 'c':
+ fprintf( stderr,
+ "%s: Assuming use of deprecated -c flag is really intended to be -C\n",
+ program_name );
+
+ /* fall through */
+
+ case 'C':
+ if ( i != 1 )
+ flexerror(
+ "-C flag must be given separately" );
+
+ if ( ! sawcmpflag )
+ {
+ useecs = false;
+ usemecs = false;
+ fulltbl = false;
+ sawcmpflag = true;
+ }
+
+ for ( ++i; arg[i] != '\0'; ++i )
+ switch ( arg[i] )
+ {
+ case 'a':
+ long_align =
+ true;
+ break;
+
+ case 'e':
+ useecs = true;
+ break;
+
+ case 'F':
+ fullspd = true;
+ break;
+
+ case 'f':
+ fulltbl = true;
+ break;
+
+ case 'm':
+ usemecs = true;
+ break;
+
+ case 'r':
+ use_read = true;
+ break;
+
+ default:
+ lerrif(
+ "unknown -C option '%c'",
+ (int) arg[i] );
+ break;
+ }
+
+ goto get_next_arg;
+
+ case 'd':
+ ddebug = true;
+ break;
+
+ case 'f':
+ useecs = usemecs = false;
+ use_read = fulltbl = true;
+ break;
+
+ case 'F':
+ useecs = usemecs = false;
+ use_read = fullspd = true;
+ break;
+
+ case 'h':
+ usage();
+ exit( 0 );
+
+ case 'I':
+ interactive = true;
+ interactive_given = true;
+ break;
+
+ case 'i':
+ caseins = true;
+ break;
+
+ case 'l':
+ lex_compat = true;
+ break;
+
+ case 'L':
+ gen_line_dirs = false;
+ break;
+
+ case 'n':
+ /* Stupid do-nothing deprecated
+ * option.
+ */
+ break;
+
+ case 'P':
+ if ( i != 1 )
+ flexerror(
+ "-P flag must be given separately" );
+
+ prefix = arg + i + 1;
+ goto get_next_arg;
+
+ case 'p':
+ ++performance_report;
+ break;
+
+ case 'S':
+ if ( i != 1 )
+ flexerror(
+ "-S flag must be given separately" );
+
+ skelname = arg + i + 1;
+ goto get_next_arg;
+
+ case 's':
+ spprdflt = true;
+ break;
+
+ case 't':
+ use_stdout = true;
+ break;
+
+ case 'T':
+ trace = true;
+ break;
+
+ case 'v':
+ printstats = true;
+ break;
+
+ case 'V':
+ fprintf( stderr, "%s version %s\n",
+ program_name, flex_version );
+ exit( 0 );
+
+ case 'w':
+ nowarn = true;
+ break;
+
+ case '7':
+ csize = 128;
+ csize_given = true;
+ break;
+
+ case '8':
+ csize = CSIZE;
+ csize_given = true;
+ break;
+
+ default:
+ fprintf( stderr,
+ "%s: unknown flag '%c'\n",
+ program_name, (int) arg[i] );
+ usage();
+ exit( 1 );
+ }
+
+ /* Used by -C, -S and -P flags in lieu of a "continue 2"
+ * control.
+ */
+ get_next_arg: ;
+ }
+
+ if ( ! csize_given )
+ {
+ if ( (fulltbl || fullspd) && ! useecs )
+ csize = DEFAULT_CSIZE;
+ else
+ csize = CSIZE;
+ }
+
+ if ( ! interactive_given )
+ {
+ if ( fulltbl || fullspd )
+ interactive = false;
+ else
+ interactive = true;
+ }
+
+ if ( lex_compat )
+ {
+ if ( C_plus_plus )
+ flexerror( "Can't use -+ with -l option" );
+
+ if ( fulltbl || fullspd )
+ flexerror( "Can't use -f or -F with -l option" );
+
+ /* Don't rely on detecting use of yymore() and REJECT,
+ * just assume they'll be used.
+ */
+ yymore_really_used = reject_really_used = true;
+
+ yytext_is_array = true;
+ use_read = false;
+ }
+
+ if ( (fulltbl || fullspd) && usemecs )
+ flexerror( "-Cf/-CF and -Cm don't make sense together" );
+
+ if ( (fulltbl || fullspd) && interactive )
+ flexerror( "-Cf/-CF and -I are incompatible" );
+
+ if ( fulltbl && fullspd )
+ flexerror( "-Cf and -CF are mutually exclusive" );
+
+ if ( C_plus_plus && fullspd )
+ flexerror( "Can't use -+ with -CF option" );
+
+ if ( ! use_stdout )
+ {
+ FILE *prev_stdout;
+ char *suffix;
+
+ if ( C_plus_plus )
+ suffix = "cc";
+ else
+ suffix = "c";
+
+ sprintf( outfile_path, outfile_template, prefix, suffix );
+
+ prev_stdout = freopen( outfile_path, "w", stdout );
+
+ if ( prev_stdout == NULL )
+ lerrsf( "could not create %s", outfile_path );
+
+ outfile_created = 1;
+ }
+
+ num_input_files = argc;
+ input_files = argv;
+ set_input_file( num_input_files > 0 ? input_files[0] : NULL );
+
+ if ( backing_up_report )
+ {
+#ifndef SHORT_FILE_NAMES
+ backing_up_file = fopen( "lex.backup", "w" );
+#else
+ backing_up_file = fopen( "lex.bck", "w" );
+#endif
+
+ if ( backing_up_file == NULL )
+ flexerror( "could not create lex.backup" );
+ }
+
+ else
+ backing_up_file = NULL;
+
+
+ lastccl = 0;
+ lastsc = 0;
+
+ if ( skelname && (skelfile = fopen( skelname, "r" )) == NULL )
+ lerrsf( "can't open skeleton file %s", skelname );
+
+ if ( strcmp( prefix, "yy" ) )
+ {
+#define GEN_PREFIX(name) printf( "#define yy%s %s%s\n", name, prefix, name );
+ GEN_PREFIX( "FlexLexer" );
+ GEN_PREFIX( "_create_buffer" );
+ GEN_PREFIX( "_delete_buffer" );
+ GEN_PREFIX( "_flex_debug" );
+ GEN_PREFIX( "_init_buffer" );
+ GEN_PREFIX( "_load_buffer_state" );
+ GEN_PREFIX( "_switch_to_buffer" );
+ GEN_PREFIX( "in" );
+ GEN_PREFIX( "leng" );
+ GEN_PREFIX( "lex" );
+ GEN_PREFIX( "out" );
+ GEN_PREFIX( "restart" );
+ GEN_PREFIX( "text" );
+ GEN_PREFIX( "wrap" );
+ printf( "\n" );
+ }
+
+
+ lastdfa = lastnfa = 0;
+ num_rules = num_eof_rules = default_rule = 0;
+ numas = numsnpairs = tmpuses = 0;
+ numecs = numeps = eps2 = num_reallocs = hshcol = dfaeql = totnst = 0;
+ numuniq = numdup = hshsave = eofseen = datapos = dataline = 0;
+ num_backing_up = onesp = numprots = 0;
+ variable_trailing_context_rules = bol_needed = false;
+
+ linenum = sectnum = 1;
+ firstprot = NIL;
+
+ /* Used in mkprot() so that the first proto goes in slot 1
+ * of the proto queue.
+ */
+ lastprot = 1;
+
+ if ( useecs )
+ {
+ /* Set up doubly-linked equivalence classes. */
+
+ /* We loop all the way up to csize, since ecgroup[csize] is
+ * the position used for NUL characters.
+ */
+ ecgroup[1] = NIL;
+
+ for ( i = 2; i <= csize; ++i )
+ {
+ ecgroup[i] = i - 1;
+ nextecm[i - 1] = i;
+ }
+
+ nextecm[csize] = NIL;
+ }
+
+ else
+ {
+ /* Put everything in its own equivalence class. */
+ for ( i = 1; i <= csize; ++i )
+ {
+ ecgroup[i] = i;
+ nextecm[i] = BAD_SUBSCRIPT; /* to catch errors */
+ }
+ }
+
+ set_up_initial_allocations();
+ }
+
+
+/* readin - read in the rules section of the input file(s) */
+
+void readin()
+ {
+ skelout();
+
+ line_directive_out( (FILE *) 0 );
+
+ if ( yyparse() )
+ {
+ pinpoint_message( "fatal parse error" );
+ flexend( 1 );
+ }
+
+ if ( syntaxerror )
+ flexend( 1 );
+
+ if ( yymore_really_used == REALLY_USED )
+ yymore_used = true;
+ else if ( yymore_really_used == REALLY_NOT_USED )
+ yymore_used = false;
+
+ if ( reject_really_used == REALLY_USED )
+ reject = true;
+ else if ( reject_really_used == REALLY_NOT_USED )
+ reject = false;
+
+ if ( performance_report > 0 )
+ {
+ if ( lex_compat )
+ {
+ fprintf( stderr,
+"-l AT&T lex compatibility option entails a large performance penalty\n" );
+ fprintf( stderr,
+" and may be the actual source of other reported performance penalties\n" );
+ }
+
+ if ( performance_report > 1 )
+ {
+ if ( interactive )
+ fprintf( stderr,
+ "-I (interactive) entails a minor performance penalty\n" );
+
+ if ( yymore_used )
+ fprintf( stderr,
+ "yymore() entails a minor performance penalty\n" );
+ }
+
+ if ( reject )
+ fprintf( stderr,
+ "REJECT entails a large performance penalty\n" );
+
+ if ( variable_trailing_context_rules )
+ fprintf( stderr,
+"Variable trailing context rules entail a large performance penalty\n" );
+ }
+
+ if ( reject )
+ real_reject = true;
+
+ if ( variable_trailing_context_rules )
+ reject = true;
+
+ if ( (fulltbl || fullspd) && reject )
+ {
+ if ( real_reject )
+ flexerror( "REJECT cannot be used with -f or -F" );
+ else
+ flexerror(
+ "variable trailing context rules cannot be used with -f or -F" );
+ }
+
+ if ( csize == 256 )
+ puts( "typedef unsigned char YY_CHAR;" );
+ else
+ puts( "typedef char YY_CHAR;" );
+
+ if ( C_plus_plus )
+ {
+ puts( "#define yytext_ptr yytext" );
+
+ if ( interactive )
+ puts( "#define YY_INTERACTIVE" );
+ }
+
+ if ( fullspd )
+ printf(
+ "typedef const struct yy_trans_info *yy_state_type;\n" );
+ else if ( ! C_plus_plus )
+ printf( "typedef int yy_state_type;\n" );
+
+ if ( reject )
+ printf( "\n#define YY_USES_REJECT\n" );
+
+ if ( ddebug )
+ puts( "\n#define FLEX_DEBUG" );
+
+ if ( lex_compat )
+ {
+ printf( "FILE *yyin = stdin, *yyout = stdout;\n" );
+ printf( "extern int yylineno;\n" );
+ printf( "int yylineno = 1;\n" );
+ }
+ else if ( ! C_plus_plus )
+ printf( "FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;\n" );
+
+ if ( C_plus_plus )
+ printf( "\n#include <FlexLexer.h>\n" );
+
+ else
+ {
+ if ( yytext_is_array )
+ puts( "extern char yytext[];\n" );
+
+ else
+ {
+ puts( "extern char *yytext;" );
+ puts( "#define yytext_ptr yytext" );
+ }
+ }
+
+ if ( useecs )
+ numecs = cre8ecs( nextecm, ecgroup, csize );
+ else
+ numecs = csize;
+
+ /* Now map the equivalence class for NUL to its expected place. */
+ ecgroup[0] = ecgroup[csize];
+ NUL_ec = ABS( ecgroup[0] );
+
+ if ( useecs )
+ ccl2ecl();
+ }
+
+
+/* set_up_initial_allocations - allocate memory for internal tables */
+
+void set_up_initial_allocations()
+ {
+ current_mns = INITIAL_MNS;
+ firstst = allocate_integer_array( current_mns );
+ lastst = allocate_integer_array( current_mns );
+ finalst = allocate_integer_array( current_mns );
+ transchar = allocate_integer_array( current_mns );
+ trans1 = allocate_integer_array( current_mns );
+ trans2 = allocate_integer_array( current_mns );
+ accptnum = allocate_integer_array( current_mns );
+ assoc_rule = allocate_integer_array( current_mns );
+ state_type = allocate_integer_array( current_mns );
+
+ current_max_rules = INITIAL_MAX_RULES;
+ rule_type = allocate_integer_array( current_max_rules );
+ rule_linenum = allocate_integer_array( current_max_rules );
+ rule_useful = allocate_integer_array( current_max_rules );
+
+ current_max_scs = INITIAL_MAX_SCS;
+ scset = allocate_integer_array( current_max_scs );
+ scbol = allocate_integer_array( current_max_scs );
+ scxclu = allocate_integer_array( current_max_scs );
+ sceof = allocate_integer_array( current_max_scs );
+ scname = allocate_char_ptr_array( current_max_scs );
+ actvsc = allocate_integer_array( current_max_scs );
+
+ current_maxccls = INITIAL_MAX_CCLS;
+ cclmap = allocate_integer_array( current_maxccls );
+ ccllen = allocate_integer_array( current_maxccls );
+ cclng = allocate_integer_array( current_maxccls );
+
+ current_max_ccl_tbl_size = INITIAL_MAX_CCL_TBL_SIZE;
+ ccltbl = allocate_Character_array( current_max_ccl_tbl_size );
+
+ current_max_dfa_size = INITIAL_MAX_DFA_SIZE;
+
+ current_max_xpairs = INITIAL_MAX_XPAIRS;
+ nxt = allocate_integer_array( current_max_xpairs );
+ chk = allocate_integer_array( current_max_xpairs );
+
+ current_max_template_xpairs = INITIAL_MAX_TEMPLATE_XPAIRS;
+ tnxt = allocate_integer_array( current_max_template_xpairs );
+
+ current_max_dfas = INITIAL_MAX_DFAS;
+ base = allocate_integer_array( current_max_dfas );
+ def = allocate_integer_array( current_max_dfas );
+ dfasiz = allocate_integer_array( current_max_dfas );
+ accsiz = allocate_integer_array( current_max_dfas );
+ dhash = allocate_integer_array( current_max_dfas );
+ dss = allocate_int_ptr_array( current_max_dfas );
+ dfaacc = allocate_dfaacc_union( current_max_dfas );
+
+ nultrans = (int *) 0;
+ }
+
+
+void usage()
+ {
+ fprintf( stderr,
+"%s [-bcdfhilnpstvwBFILTV78+ -C[aefFmr] -Pprefix -Sskeleton] [file ...]\n",
+ program_name );
+
+ fprintf( stderr,
+ "\t-b generate backing-up information to lex.backup\n" );
+ fprintf( stderr, "\t-c do-nothing POSIX option\n" );
+ fprintf( stderr, "\t-d turn on debug mode in generated scanner\n" );
+ fprintf( stderr, "\t-f generate fast, large scanner\n" );
+ fprintf( stderr, "\t-h produce this help message\n" );
+ fprintf( stderr, "\t-i generate case-insensitive scanner\n" );
+ fprintf( stderr, "\t-l maximal compatibility with original lex\n" );
+ fprintf( stderr, "\t-n do-nothing POSIX option\n" );
+ fprintf( stderr, "\t-p generate performance report to stderr\n" );
+ fprintf( stderr,
+ "\t-s suppress default rule to ECHO unmatched text\n" );
+ fprintf( stderr,
+ "\t-t write generated scanner on stdout instead of lex.yy.c\n" );
+ fprintf( stderr,
+ "\t-v write summary of scanner statistics to stderr\n" );
+ fprintf( stderr, "\t-w do not generate warnings\n" );
+ fprintf( stderr, "\t-B generate batch scanner (opposite of -I)\n" );
+ fprintf( stderr,
+ "\t-F use alternative fast scanner representation\n" );
+ fprintf( stderr,
+ "\t-I generate interactive scanner (opposite of -B)\n" );
+ fprintf( stderr, "\t-L suppress #line directives in scanner\n" );
+ fprintf( stderr, "\t-T %s should run in trace mode\n", program_name );
+ fprintf( stderr, "\t-V report %s version\n", program_name );
+ fprintf( stderr, "\t-7 generate 7-bit scanner\n" );
+ fprintf( stderr, "\t-8 generate 8-bit scanner\n" );
+ fprintf( stderr, "\t-+ generate C++ scanner class\n" );
+ fprintf( stderr,
+ "\t-C specify degree of table compression (default is -Cem):\n" );
+ fprintf( stderr,
+ "\t\t-Ca trade off larger tables for better memory alignment\n" );
+ fprintf( stderr, "\t\t-Ce construct equivalence classes\n" );
+ fprintf( stderr,
+ "\t\t-Cf do not compress scanner tables; use -f representation\n" );
+ fprintf( stderr,
+ "\t\t-CF do not compress scanner tables; use -F representation\n" );
+ fprintf( stderr, "\t\t-Cm construct meta-equivalence classes\n" );
+ fprintf( stderr,
+ "\t\t-Cr use read() instead of stdio for scanner input\n" );
+ fprintf( stderr, "\t-P specify scanner prefix other than \"yy\"\n" );
+ fprintf( stderr, "\t-S specify skeleton file\n" );
+ }
diff --git a/usr.bin/lex/misc.c b/usr.bin/lex/misc.c
new file mode 100644
index 0000000..1cbdbd6
--- /dev/null
+++ b/usr.bin/lex/misc.c
@@ -0,0 +1,773 @@
+/* misc - miscellaneous flex routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/misc.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+
+/* declare functions that have forward references */
+
+void dataflush PROTO((void));
+int otoi PROTO((Char []));
+
+
+void add_action( new_text )
+char *new_text;
+ {
+ int len = strlen( new_text );
+
+ while ( len + action_index >= action_size - 10 /* slop */ )
+ {
+ action_size *= 2;
+ action_array =
+ reallocate_character_array( action_array, action_size );
+ }
+
+ strcpy( &action_array[action_index], new_text );
+
+ action_index += len;
+ }
+
+
+/* allocate_array - allocate memory for an integer array of the given size */
+
+void *allocate_array( size, element_size )
+int size, element_size;
+ {
+ register void *mem;
+
+ /* On 16-bit int machines (e.g., 80286) we might be trying to
+ * allocate more than a signed int can hold, and that won't
+ * work. Cheap test:
+ */
+ if ( element_size * size <= 0 )
+ flexfatal( "request for < 1 byte in allocate_array()" );
+
+ mem = flex_alloc( element_size * size );
+
+ if ( mem == NULL )
+ flexfatal( "memory allocation failed in allocate_array()" );
+
+ return mem;
+ }
+
+
+/* all_lower - true if a string is all lower-case */
+
+int all_lower( str )
+register char *str;
+ {
+ while ( *str )
+ {
+ if ( ! isascii( (Char) *str ) || ! islower( *str ) )
+ return 0;
+ ++str;
+ }
+
+ return 1;
+ }
+
+
+/* all_upper - true if a string is all upper-case */
+
+int all_upper( str )
+register char *str;
+ {
+ while ( *str )
+ {
+ if ( ! isascii( (Char) *str ) || ! isupper( *str ) )
+ return 0;
+ ++str;
+ }
+
+ return 1;
+ }
+
+
+/* bubble - bubble sort an integer array in increasing order
+ *
+ * synopsis
+ * int v[n], n;
+ * void bubble( v, n );
+ *
+ * description
+ * sorts the first n elements of array v and replaces them in
+ * increasing order.
+ *
+ * passed
+ * v - the array to be sorted
+ * n - the number of elements of 'v' to be sorted
+ */
+
+void bubble( v, n )
+int v[], n;
+ {
+ register int i, j, k;
+
+ for ( i = n; i > 1; --i )
+ for ( j = 1; j < i; ++j )
+ if ( v[j] > v[j + 1] ) /* compare */
+ {
+ k = v[j]; /* exchange */
+ v[j] = v[j + 1];
+ v[j + 1] = k;
+ }
+ }
+
+
+/* check_char - checks a character to make sure it's within the range
+ * we're expecting. If not, generates fatal error message
+ * and exits.
+ */
+
+void check_char( c )
+int c;
+ {
+ if ( c >= CSIZE )
+ lerrsf( "bad character '%s' detected in check_char()",
+ readable_form( c ) );
+
+ if ( c >= csize )
+ lerrsf( "scanner requires -8 flag to use the character '%s'",
+ readable_form( c ) );
+ }
+
+
+
+/* clower - replace upper-case letter to lower-case */
+
+Char clower( c )
+register int c;
+ {
+ return (Char) ((isascii( c ) && isupper( c )) ? tolower( c ) : c);
+ }
+
+
+/* copy_string - returns a dynamically allocated copy of a string */
+
+char *copy_string( str )
+register char *str;
+ {
+ register char *c;
+ char *copy;
+
+ /* find length */
+ for ( c = str; *c; ++c )
+ ;
+
+ copy = (char *) flex_alloc( (c - str + 1) * sizeof( char ) );
+
+ if ( copy == NULL )
+ flexfatal( "dynamic memory failure in copy_string()" );
+
+ for ( c = copy; (*c++ = *str++); )
+ ;
+
+ return copy;
+ }
+
+
+/* copy_unsigned_string -
+ * returns a dynamically allocated copy of a (potentially) unsigned string
+ */
+
+Char *copy_unsigned_string( str )
+register Char *str;
+ {
+ register Char *c;
+ Char *copy;
+
+ /* find length */
+ for ( c = str; *c; ++c )
+ ;
+
+ copy = allocate_Character_array( c - str + 1 );
+
+ for ( c = copy; (*c++ = *str++); )
+ ;
+
+ return copy;
+ }
+
+
+/* cshell - shell sort a character array in increasing order
+ *
+ * synopsis
+ *
+ * Char v[n];
+ * int n, special_case_0;
+ * cshell( v, n, special_case_0 );
+ *
+ * description
+ * Does a shell sort of the first n elements of array v.
+ * If special_case_0 is true, then any element equal to 0
+ * is instead assumed to have infinite weight.
+ *
+ * passed
+ * v - array to be sorted
+ * n - number of elements of v to be sorted
+ */
+
+void cshell( v, n, special_case_0 )
+Char v[];
+int n, special_case_0;
+ {
+ int gap, i, j, jg;
+ Char k;
+
+ for ( gap = n / 2; gap > 0; gap = gap / 2 )
+ for ( i = gap; i < n; ++i )
+ for ( j = i - gap; j >= 0; j = j - gap )
+ {
+ jg = j + gap;
+
+ if ( special_case_0 )
+ {
+ if ( v[jg] == 0 )
+ break;
+
+ else if ( v[j] != 0 && v[j] <= v[jg] )
+ break;
+ }
+
+ else if ( v[j] <= v[jg] )
+ break;
+
+ k = v[j];
+ v[j] = v[jg];
+ v[jg] = k;
+ }
+ }
+
+
+/* dataend - finish up a block of data declarations */
+
+void dataend()
+ {
+ if ( datapos > 0 )
+ dataflush();
+
+ /* add terminator for initialization; { for vi */
+ puts( " } ;\n" );
+
+ dataline = 0;
+ datapos = 0;
+ }
+
+
+/* dataflush - flush generated data statements */
+
+void dataflush()
+ {
+ putchar( '\n' );
+
+ if ( ++dataline >= NUMDATALINES )
+ {
+ /* Put out a blank line so that the table is grouped into
+ * large blocks that enable the user to find elements easily.
+ */
+ putchar( '\n' );
+ dataline = 0;
+ }
+
+ /* Reset the number of characters written on the current line. */
+ datapos = 0;
+ }
+
+
+/* flexerror - report an error message and terminate */
+
+void flexerror( msg )
+char msg[];
+ {
+ fprintf( stderr, "%s: %s\n", program_name, msg );
+ flexend( 1 );
+ }
+
+
+/* flexfatal - report a fatal error message and terminate */
+
+void flexfatal( msg )
+char msg[];
+ {
+ fprintf( stderr, "%s: fatal internal error, %s\n", program_name, msg );
+ exit( 1 );
+ }
+
+
+/* lerrif - report an error message formatted with one integer argument */
+
+void lerrif( msg, arg )
+char msg[];
+int arg;
+ {
+ char errmsg[MAXLINE];
+ (void) sprintf( errmsg, msg, arg );
+ flexerror( errmsg );
+ }
+
+
+/* lerrsf - report an error message formatted with one string argument */
+
+void lerrsf( msg, arg )
+char msg[], arg[];
+ {
+ char errmsg[MAXLINE];
+
+ (void) sprintf( errmsg, msg, arg );
+ flexerror( errmsg );
+ }
+
+
+/* htoi - convert a hexadecimal digit string to an integer value */
+
+int htoi( str )
+Char str[];
+ {
+ unsigned int result;
+
+ (void) sscanf( (char *) str, "%x", &result );
+
+ return result;
+ }
+
+
+/* is_hex_digit - returns true if a character is a valid hex digit, false
+ * otherwise
+ */
+
+int is_hex_digit( ch )
+int ch;
+ {
+ if ( isdigit( ch ) )
+ return 1;
+
+ switch ( clower( ch ) )
+ {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ return 1;
+
+ default:
+ return 0;
+ }
+ }
+
+
+/* line_directive_out - spit out a "# line" statement */
+
+void line_directive_out( output_file )
+FILE *output_file;
+ {
+ if ( infilename && gen_line_dirs )
+ {
+ char directive[MAXLINE];
+ sprintf( directive, "# line %d \"%s\"\n", linenum, infilename );
+
+ /* If output_file is nil then we should put the directive in
+ * the accumulated actions.
+ */
+ if ( output_file )
+ fputs( directive, output_file );
+ else
+ add_action( directive );
+ }
+ }
+
+
+/* mark_defs1 - mark the current position in the action array as
+ * representing where the user's section 1 definitions end
+ * and the prolog begins
+ */
+void mark_defs1()
+ {
+ defs1_offset = 0;
+ action_array[action_index++] = '\0';
+ action_offset = prolog_offset = action_index;
+ action_array[action_index] = '\0';
+ }
+
+
+/* mark_prolog - mark the current position in the action array as
+ * representing the end of the action prolog
+ */
+void mark_prolog()
+ {
+ action_array[action_index++] = '\0';
+ action_offset = action_index;
+ action_array[action_index] = '\0';
+ }
+
+
+/* mk2data - generate a data statement for a two-dimensional array
+ *
+ * Generates a data statement initializing the current 2-D array to "value".
+ */
+void mk2data( value )
+int value;
+ {
+ if ( datapos >= NUMDATAITEMS )
+ {
+ putchar( ',' );
+ dataflush();
+ }
+
+ if ( datapos == 0 )
+ /* Indent. */
+ fputs( " ", stdout );
+
+ else
+ putchar( ',' );
+
+ ++datapos;
+
+ printf( "%5d", value );
+ }
+
+
+/* mkdata - generate a data statement
+ *
+ * Generates a data statement initializing the current array element to
+ * "value".
+ */
+void mkdata( value )
+int value;
+ {
+ if ( datapos >= NUMDATAITEMS )
+ {
+ putchar( ',' );
+ dataflush();
+ }
+
+ if ( datapos == 0 )
+ /* Indent. */
+ fputs( " ", stdout );
+ else
+ putchar( ',' );
+
+ ++datapos;
+
+ printf( "%5d", value );
+ }
+
+
+/* myctoi - return the integer represented by a string of digits */
+
+int myctoi( array )
+char array[];
+ {
+ int val = 0;
+
+ (void) sscanf( array, "%d", &val );
+
+ return val;
+ }
+
+
+/* myesc - return character corresponding to escape sequence */
+
+Char myesc( array )
+Char array[];
+ {
+ Char c, esc_char;
+
+ switch ( array[1] )
+ {
+ case 'b': return '\b';
+ case 'f': return '\f';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ case 't': return '\t';
+
+#ifdef __STDC__
+ case 'a': return '\a';
+ case 'v': return '\v';
+#else
+ case 'a': return '\007';
+ case 'v': return '\013';
+#endif
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ { /* \<octal> */
+ int sptr = 1;
+
+ while ( isascii( array[sptr] ) &&
+ isdigit( array[sptr] ) )
+ /* Don't increment inside loop control
+ * because if isdigit() is a macro it might
+ * expand into multiple increments ...
+ */
+ ++sptr;
+
+ c = array[sptr];
+ array[sptr] = '\0';
+
+ esc_char = otoi( array + 1 );
+
+ array[sptr] = c;
+
+ return esc_char;
+ }
+
+ case 'x':
+ { /* \x<hex> */
+ int sptr = 2;
+
+ while ( isascii( array[sptr] ) &&
+ is_hex_digit( (char) array[sptr] ) )
+ /* Don't increment inside loop control
+ * because if isdigit() is a macro it might
+ * expand into multiple increments ...
+ */
+ ++sptr;
+
+ c = array[sptr];
+ array[sptr] = '\0';
+
+ esc_char = htoi( array + 2 );
+
+ array[sptr] = c;
+
+ return esc_char;
+ }
+
+ default:
+ return array[1];
+ }
+ }
+
+
+/* otoi - convert an octal digit string to an integer value */
+
+int otoi( str )
+Char str[];
+ {
+ unsigned int result;
+
+ (void) sscanf( (char *) str, "%o", &result );
+ return result;
+ }
+
+
+/* readable_form - return the the human-readable form of a character
+ *
+ * The returned string is in static storage.
+ */
+
+char *readable_form( c )
+register int c;
+ {
+ static char rform[10];
+
+ if ( (c >= 0 && c < 32) || c >= 127 )
+ {
+ switch ( c )
+ {
+ case '\b': return "\\b";
+ case '\f': return "\\f";
+ case '\n': return "\\n";
+ case '\r': return "\\r";
+ case '\t': return "\\t";
+
+#ifdef __STDC__
+ case '\a': return "\\a";
+ case '\v': return "\\v";
+#endif
+
+ default:
+ (void) sprintf( rform, "\\%.3o",
+ (unsigned int) c );
+ return rform;
+ }
+ }
+
+ else if ( c == ' ' )
+ return "' '";
+
+ else
+ {
+ rform[0] = c;
+ rform[1] = '\0';
+
+ return rform;
+ }
+ }
+
+
+/* reallocate_array - increase the size of a dynamic array */
+
+void *reallocate_array( array, size, element_size )
+void *array;
+int size, element_size;
+ {
+ register void *new_array;
+
+ /* Same worry as in allocate_array(): */
+ if ( size * element_size <= 0 )
+ flexfatal(
+ "attempt to increase array size by less than 1 byte" );
+
+ new_array = flex_realloc( array, size * element_size );
+
+ if ( new_array == NULL )
+ flexfatal( "attempt to increase array size failed" );
+
+ return new_array;
+ }
+
+
+/* skelout - write out one section of the skeleton file
+ *
+ * Description
+ * Copies skelfile or skel array to stdout until a line beginning with
+ * "%%" or EOF is found.
+ */
+void skelout()
+ {
+ char buf_storage[MAXLINE];
+ char *buf = buf_storage;
+ int do_copy = 1;
+
+ /* Loop pulling lines either from the skelfile, if we're using
+ * one, or from the skel[] array.
+ */
+ while ( skelfile ?
+ (fgets( buf, MAXLINE, skelfile ) != NULL) :
+ ((buf = skel[skel_ind++]) != 0) )
+ { /* copy from skel array */
+ if ( buf[0] == '%' )
+ { /* control line */
+ switch ( buf[1] )
+ {
+ case '%':
+ return;
+
+ case '+':
+ do_copy = C_plus_plus;
+ break;
+
+ case '-':
+ do_copy = ! C_plus_plus;
+ break;
+
+ case '*':
+ do_copy = 1;
+ break;
+
+ default:
+ flexfatal(
+ "bad line in skeleton file" );
+ }
+ }
+
+ else if ( do_copy )
+ {
+ if ( skelfile )
+ /* Skeleton file reads include final
+ * newline, skel[] array does not.
+ */
+ fputs( buf, stdout );
+ else
+ printf( "%s\n", buf );
+ }
+ }
+ }
+
+
+/* transition_struct_out - output a yy_trans_info structure
+ *
+ * outputs the yy_trans_info structure with the two elements, element_v and
+ * element_n. Formats the output with spaces and carriage returns.
+ */
+
+void transition_struct_out( element_v, element_n )
+int element_v, element_n;
+ {
+ printf( "%7d, %5d,", element_v, element_n );
+
+ datapos += TRANS_STRUCT_PRINT_LENGTH;
+
+ if ( datapos >= 75 )
+ {
+ putchar( '\n' );
+
+ if ( ++dataline % 10 == 0 )
+ putchar( '\n' );
+
+ datapos = 0;
+ }
+ }
+
+
+/* The following is only needed when building flex's parser using certain
+ * broken versions of bison.
+ */
+void *yy_flex_xmalloc( size )
+int size;
+ {
+ void *result = flex_alloc( size );
+
+ if ( ! result )
+ flexfatal( "memory allocation failed in yy_flex_xmalloc()" );
+
+ return result;
+ }
+
+
+/* zero_out - set a region of memory to 0
+ *
+ * Sets region_ptr[0] through region_ptr[size_in_bytes - 1] to zero.
+ */
+
+void zero_out( region_ptr, size_in_bytes )
+char *region_ptr;
+int size_in_bytes;
+ {
+ register char *rp, *rp_end;
+
+ rp = region_ptr;
+ rp_end = region_ptr + size_in_bytes;
+
+ while ( rp < rp_end )
+ *rp++ = 0;
+ }
diff --git a/usr.bin/lex/mkskel.sh b/usr.bin/lex/mkskel.sh
new file mode 100755
index 0000000..ffc7c7f
--- /dev/null
+++ b/usr.bin/lex/mkskel.sh
@@ -0,0 +1,16 @@
+#! /bin/sh
+
+cat <<!
+/* File created from flex.skel via mkskel.sh */
+
+#include "flexdef.h"
+
+char *skel[] = {
+!
+
+sed 's/\\/&&/g' $* | sed 's/"/\\"/g' | sed 's/.*/ "&",/'
+
+cat <<!
+ 0
+};
+!
diff --git a/usr.bin/lex/nfa.c b/usr.bin/lex/nfa.c
new file mode 100644
index 0000000..7820ce9
--- /dev/null
+++ b/usr.bin/lex/nfa.c
@@ -0,0 +1,709 @@
+/* nfa - NFA construction routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/nfa.c,v 1.1.1.1 1994/08/24 13:10:32 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+/* declare functions that have forward references */
+
+int dupmachine PROTO((int));
+void mkxtion PROTO((int, int));
+
+
+/* add_accept - add an accepting state to a machine
+ *
+ * accepting_number becomes mach's accepting number.
+ */
+
+void add_accept( mach, accepting_number )
+int mach, accepting_number;
+ {
+ /* Hang the accepting number off an epsilon state. if it is associated
+ * with a state that has a non-epsilon out-transition, then the state
+ * will accept BEFORE it makes that transition, i.e., one character
+ * too soon.
+ */
+
+ if ( transchar[finalst[mach]] == SYM_EPSILON )
+ accptnum[finalst[mach]] = accepting_number;
+
+ else
+ {
+ int astate = mkstate( SYM_EPSILON );
+ accptnum[astate] = accepting_number;
+ (void) link_machines( mach, astate );
+ }
+ }
+
+
+/* copysingl - make a given number of copies of a singleton machine
+ *
+ * synopsis
+ *
+ * newsng = copysingl( singl, num );
+ *
+ * newsng - a new singleton composed of num copies of singl
+ * singl - a singleton machine
+ * num - the number of copies of singl to be present in newsng
+ */
+
+int copysingl( singl, num )
+int singl, num;
+ {
+ int copy, i;
+
+ copy = mkstate( SYM_EPSILON );
+
+ for ( i = 1; i <= num; ++i )
+ copy = link_machines( copy, dupmachine( singl ) );
+
+ return copy;
+ }
+
+
+/* dumpnfa - debugging routine to write out an nfa */
+
+void dumpnfa( state1 )
+int state1;
+
+ {
+ int sym, tsp1, tsp2, anum, ns;
+
+ fprintf( stderr,
+ "\n\n********** beginning dump of nfa with start state %d\n",
+ state1 );
+
+ /* We probably should loop starting at firstst[state1] and going to
+ * lastst[state1], but they're not maintained properly when we "or"
+ * all of the rules together. So we use our knowledge that the machine
+ * starts at state 1 and ends at lastnfa.
+ */
+
+ /* for ( ns = firstst[state1]; ns <= lastst[state1]; ++ns ) */
+ for ( ns = 1; ns <= lastnfa; ++ns )
+ {
+ fprintf( stderr, "state # %4d\t", ns );
+
+ sym = transchar[ns];
+ tsp1 = trans1[ns];
+ tsp2 = trans2[ns];
+ anum = accptnum[ns];
+
+ fprintf( stderr, "%3d: %4d, %4d", sym, tsp1, tsp2 );
+
+ if ( anum != NIL )
+ fprintf( stderr, " [%d]", anum );
+
+ fprintf( stderr, "\n" );
+ }
+
+ fprintf( stderr, "********** end of dump\n" );
+ }
+
+
+/* dupmachine - make a duplicate of a given machine
+ *
+ * synopsis
+ *
+ * copy = dupmachine( mach );
+ *
+ * copy - holds duplicate of mach
+ * mach - machine to be duplicated
+ *
+ * note that the copy of mach is NOT an exact duplicate; rather, all the
+ * transition states values are adjusted so that the copy is self-contained,
+ * as the original should have been.
+ *
+ * also note that the original MUST be contiguous, with its low and high
+ * states accessible by the arrays firstst and lastst
+ */
+
+int dupmachine( mach )
+int mach;
+ {
+ int i, init, state_offset;
+ int state = 0;
+ int last = lastst[mach];
+
+ for ( i = firstst[mach]; i <= last; ++i )
+ {
+ state = mkstate( transchar[i] );
+
+ if ( trans1[i] != NO_TRANSITION )
+ {
+ mkxtion( finalst[state], trans1[i] + state - i );
+
+ if ( transchar[i] == SYM_EPSILON &&
+ trans2[i] != NO_TRANSITION )
+ mkxtion( finalst[state],
+ trans2[i] + state - i );
+ }
+
+ accptnum[state] = accptnum[i];
+ }
+
+ if ( state == 0 )
+ flexfatal( "empty machine in dupmachine()" );
+
+ state_offset = state - i + 1;
+
+ init = mach + state_offset;
+ firstst[init] = firstst[mach] + state_offset;
+ finalst[init] = finalst[mach] + state_offset;
+ lastst[init] = lastst[mach] + state_offset;
+
+ return init;
+ }
+
+
+/* finish_rule - finish up the processing for a rule
+ *
+ * An accepting number is added to the given machine. If variable_trail_rule
+ * is true then the rule has trailing context and both the head and trail
+ * are variable size. Otherwise if headcnt or trailcnt is non-zero then
+ * the machine recognizes a pattern with trailing context and headcnt is
+ * the number of characters in the matched part of the pattern, or zero
+ * if the matched part has variable length. trailcnt is the number of
+ * trailing context characters in the pattern, or zero if the trailing
+ * context has variable length.
+ */
+
+void finish_rule( mach, variable_trail_rule, headcnt, trailcnt )
+int mach, variable_trail_rule, headcnt, trailcnt;
+ {
+ char action_text[MAXLINE];
+
+ add_accept( mach, num_rules );
+
+ /* We did this in new_rule(), but it often gets the wrong
+ * number because we do it before we start parsing the current rule.
+ */
+ rule_linenum[num_rules] = linenum;
+
+ /* If this is a continued action, then the line-number has already
+ * been updated, giving us the wrong number.
+ */
+ if ( continued_action )
+ --rule_linenum[num_rules];
+
+ sprintf( action_text, "case %d:\n", num_rules );
+ add_action( action_text );
+
+ if ( variable_trail_rule )
+ {
+ rule_type[num_rules] = RULE_VARIABLE;
+
+ if ( performance_report > 0 )
+ fprintf( stderr,
+ "Variable trailing context rule at line %d\n",
+ rule_linenum[num_rules] );
+
+ variable_trailing_context_rules = true;
+ }
+
+ else
+ {
+ rule_type[num_rules] = RULE_NORMAL;
+
+ if ( headcnt > 0 || trailcnt > 0 )
+ {
+ /* Do trailing context magic to not match the trailing
+ * characters.
+ */
+ char *scanner_cp = "yy_c_buf_p = yy_cp";
+ char *scanner_bp = "yy_bp";
+
+ add_action(
+ "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */\n" );
+
+ if ( headcnt > 0 )
+ {
+ sprintf( action_text, "%s = %s + %d;\n",
+ scanner_cp, scanner_bp, headcnt );
+ add_action( action_text );
+ }
+
+ else
+ {
+ sprintf( action_text, "%s -= %d;\n",
+ scanner_cp, trailcnt );
+ add_action( action_text );
+ }
+
+ add_action(
+ "YY_DO_BEFORE_ACTION; /* set up yytext again */\n" );
+ }
+ }
+
+ /* Okay, in the action code at this point yytext and yyleng have
+ * their proper final values for this rule, so here's the point
+ * to do any user action. But don't do it for continued actions,
+ * as that'll result in multiple YY_USER_ACTION's.
+ */
+ if ( ! continued_action )
+ add_action( "YY_USER_ACTION\n" );
+
+ line_directive_out( (FILE *) 0 );
+ }
+
+
+/* link_machines - connect two machines together
+ *
+ * synopsis
+ *
+ * new = link_machines( first, last );
+ *
+ * new - a machine constructed by connecting first to last
+ * first - the machine whose successor is to be last
+ * last - the machine whose predecessor is to be first
+ *
+ * note: this routine concatenates the machine first with the machine
+ * last to produce a machine new which will pattern-match first first
+ * and then last, and will fail if either of the sub-patterns fails.
+ * FIRST is set to new by the operation. last is unmolested.
+ */
+
+int link_machines( first, last )
+int first, last;
+ {
+ if ( first == NIL )
+ return last;
+
+ else if ( last == NIL )
+ return first;
+
+ else
+ {
+ mkxtion( finalst[first], last );
+ finalst[first] = finalst[last];
+ lastst[first] = MAX( lastst[first], lastst[last] );
+ firstst[first] = MIN( firstst[first], firstst[last] );
+
+ return first;
+ }
+ }
+
+
+/* mark_beginning_as_normal - mark each "beginning" state in a machine
+ * as being a "normal" (i.e., not trailing context-
+ * associated) states
+ *
+ * The "beginning" states are the epsilon closure of the first state
+ */
+
+void mark_beginning_as_normal( mach )
+register int mach;
+ {
+ switch ( state_type[mach] )
+ {
+ case STATE_NORMAL:
+ /* Oh, we've already visited here. */
+ return;
+
+ case STATE_TRAILING_CONTEXT:
+ state_type[mach] = STATE_NORMAL;
+
+ if ( transchar[mach] == SYM_EPSILON )
+ {
+ if ( trans1[mach] != NO_TRANSITION )
+ mark_beginning_as_normal(
+ trans1[mach] );
+
+ if ( trans2[mach] != NO_TRANSITION )
+ mark_beginning_as_normal(
+ trans2[mach] );
+ }
+ break;
+
+ default:
+ flexerror(
+ "bad state type in mark_beginning_as_normal()" );
+ break;
+ }
+ }
+
+
+/* mkbranch - make a machine that branches to two machines
+ *
+ * synopsis
+ *
+ * branch = mkbranch( first, second );
+ *
+ * branch - a machine which matches either first's pattern or second's
+ * first, second - machines whose patterns are to be or'ed (the | operator)
+ *
+ * Note that first and second are NEITHER destroyed by the operation. Also,
+ * the resulting machine CANNOT be used with any other "mk" operation except
+ * more mkbranch's. Compare with mkor()
+ */
+
+int mkbranch( first, second )
+int first, second;
+ {
+ int eps;
+
+ if ( first == NO_TRANSITION )
+ return second;
+
+ else if ( second == NO_TRANSITION )
+ return first;
+
+ eps = mkstate( SYM_EPSILON );
+
+ mkxtion( eps, first );
+ mkxtion( eps, second );
+
+ return eps;
+ }
+
+
+/* mkclos - convert a machine into a closure
+ *
+ * synopsis
+ * new = mkclos( state );
+ *
+ * new - a new state which matches the closure of "state"
+ */
+
+int mkclos( state )
+int state;
+ {
+ return mkopt( mkposcl( state ) );
+ }
+
+
+/* mkopt - make a machine optional
+ *
+ * synopsis
+ *
+ * new = mkopt( mach );
+ *
+ * new - a machine which optionally matches whatever mach matched
+ * mach - the machine to make optional
+ *
+ * notes:
+ * 1. mach must be the last machine created
+ * 2. mach is destroyed by the call
+ */
+
+int mkopt( mach )
+int mach;
+ {
+ int eps;
+
+ if ( ! SUPER_FREE_EPSILON(finalst[mach]) )
+ {
+ eps = mkstate( SYM_EPSILON );
+ mach = link_machines( mach, eps );
+ }
+
+ /* Can't skimp on the following if FREE_EPSILON(mach) is true because
+ * some state interior to "mach" might point back to the beginning
+ * for a closure.
+ */
+ eps = mkstate( SYM_EPSILON );
+ mach = link_machines( eps, mach );
+
+ mkxtion( mach, finalst[mach] );
+
+ return mach;
+ }
+
+
+/* mkor - make a machine that matches either one of two machines
+ *
+ * synopsis
+ *
+ * new = mkor( first, second );
+ *
+ * new - a machine which matches either first's pattern or second's
+ * first, second - machines whose patterns are to be or'ed (the | operator)
+ *
+ * note that first and second are both destroyed by the operation
+ * the code is rather convoluted because an attempt is made to minimize
+ * the number of epsilon states needed
+ */
+
+int mkor( first, second )
+int first, second;
+ {
+ int eps, orend;
+
+ if ( first == NIL )
+ return second;
+
+ else if ( second == NIL )
+ return first;
+
+ else
+ {
+ /* See comment in mkopt() about why we can't use the first
+ * state of "first" or "second" if they satisfy "FREE_EPSILON".
+ */
+ eps = mkstate( SYM_EPSILON );
+
+ first = link_machines( eps, first );
+
+ mkxtion( first, second );
+
+ if ( SUPER_FREE_EPSILON(finalst[first]) &&
+ accptnum[finalst[first]] == NIL )
+ {
+ orend = finalst[first];
+ mkxtion( finalst[second], orend );
+ }
+
+ else if ( SUPER_FREE_EPSILON(finalst[second]) &&
+ accptnum[finalst[second]] == NIL )
+ {
+ orend = finalst[second];
+ mkxtion( finalst[first], orend );
+ }
+
+ else
+ {
+ eps = mkstate( SYM_EPSILON );
+
+ first = link_machines( first, eps );
+ orend = finalst[first];
+
+ mkxtion( finalst[second], orend );
+ }
+ }
+
+ finalst[first] = orend;
+ return first;
+ }
+
+
+/* mkposcl - convert a machine into a positive closure
+ *
+ * synopsis
+ * new = mkposcl( state );
+ *
+ * new - a machine matching the positive closure of "state"
+ */
+
+int mkposcl( state )
+int state;
+ {
+ int eps;
+
+ if ( SUPER_FREE_EPSILON(finalst[state]) )
+ {
+ mkxtion( finalst[state], state );
+ return state;
+ }
+
+ else
+ {
+ eps = mkstate( SYM_EPSILON );
+ mkxtion( eps, state );
+ return link_machines( state, eps );
+ }
+ }
+
+
+/* mkrep - make a replicated machine
+ *
+ * synopsis
+ * new = mkrep( mach, lb, ub );
+ *
+ * new - a machine that matches whatever "mach" matched from "lb"
+ * number of times to "ub" number of times
+ *
+ * note
+ * if "ub" is INFINITY then "new" matches "lb" or more occurrences of "mach"
+ */
+
+int mkrep( mach, lb, ub )
+int mach, lb, ub;
+ {
+ int base_mach, tail, copy, i;
+
+ base_mach = copysingl( mach, lb - 1 );
+
+ if ( ub == INFINITY )
+ {
+ copy = dupmachine( mach );
+ mach = link_machines( mach,
+ link_machines( base_mach, mkclos( copy ) ) );
+ }
+
+ else
+ {
+ tail = mkstate( SYM_EPSILON );
+
+ for ( i = lb; i < ub; ++i )
+ {
+ copy = dupmachine( mach );
+ tail = mkopt( link_machines( copy, tail ) );
+ }
+
+ mach = link_machines( mach, link_machines( base_mach, tail ) );
+ }
+
+ return mach;
+ }
+
+
+/* mkstate - create a state with a transition on a given symbol
+ *
+ * synopsis
+ *
+ * state = mkstate( sym );
+ *
+ * state - a new state matching sym
+ * sym - the symbol the new state is to have an out-transition on
+ *
+ * note that this routine makes new states in ascending order through the
+ * state array (and increments LASTNFA accordingly). The routine DUPMACHINE
+ * relies on machines being made in ascending order and that they are
+ * CONTIGUOUS. Change it and you will have to rewrite DUPMACHINE (kludge
+ * that it admittedly is)
+ */
+
+int mkstate( sym )
+int sym;
+ {
+ if ( ++lastnfa >= current_mns )
+ {
+ if ( (current_mns += MNS_INCREMENT) >= MAXIMUM_MNS )
+ lerrif(
+ "input rules are too complicated (>= %d NFA states)",
+ current_mns );
+
+ ++num_reallocs;
+
+ firstst = reallocate_integer_array( firstst, current_mns );
+ lastst = reallocate_integer_array( lastst, current_mns );
+ finalst = reallocate_integer_array( finalst, current_mns );
+ transchar = reallocate_integer_array( transchar, current_mns );
+ trans1 = reallocate_integer_array( trans1, current_mns );
+ trans2 = reallocate_integer_array( trans2, current_mns );
+ accptnum = reallocate_integer_array( accptnum, current_mns );
+ assoc_rule =
+ reallocate_integer_array( assoc_rule, current_mns );
+ state_type =
+ reallocate_integer_array( state_type, current_mns );
+ }
+
+ firstst[lastnfa] = lastnfa;
+ finalst[lastnfa] = lastnfa;
+ lastst[lastnfa] = lastnfa;
+ transchar[lastnfa] = sym;
+ trans1[lastnfa] = NO_TRANSITION;
+ trans2[lastnfa] = NO_TRANSITION;
+ accptnum[lastnfa] = NIL;
+ assoc_rule[lastnfa] = num_rules;
+ state_type[lastnfa] = current_state_type;
+
+ /* Fix up equivalence classes base on this transition. Note that any
+ * character which has its own transition gets its own equivalence
+ * class. Thus only characters which are only in character classes
+ * have a chance at being in the same equivalence class. E.g. "a|b"
+ * puts 'a' and 'b' into two different equivalence classes. "[ab]"
+ * puts them in the same equivalence class (barring other differences
+ * elsewhere in the input).
+ */
+
+ if ( sym < 0 )
+ {
+ /* We don't have to update the equivalence classes since
+ * that was already done when the ccl was created for the
+ * first time.
+ */
+ }
+
+ else if ( sym == SYM_EPSILON )
+ ++numeps;
+
+ else
+ {
+ check_char( sym );
+
+ if ( useecs )
+ /* Map NUL's to csize. */
+ mkechar( sym ? sym : csize, nextecm, ecgroup );
+ }
+
+ return lastnfa;
+ }
+
+
+/* mkxtion - make a transition from one state to another
+ *
+ * synopsis
+ *
+ * mkxtion( statefrom, stateto );
+ *
+ * statefrom - the state from which the transition is to be made
+ * stateto - the state to which the transition is to be made
+ */
+
+void mkxtion( statefrom, stateto )
+int statefrom, stateto;
+ {
+ if ( trans1[statefrom] == NO_TRANSITION )
+ trans1[statefrom] = stateto;
+
+ else if ( (transchar[statefrom] != SYM_EPSILON) ||
+ (trans2[statefrom] != NO_TRANSITION) )
+ flexfatal( "found too many transitions in mkxtion()" );
+
+ else
+ { /* second out-transition for an epsilon state */
+ ++eps2;
+ trans2[statefrom] = stateto;
+ }
+ }
+
+/* new_rule - initialize for a new rule */
+
+void new_rule()
+ {
+ if ( ++num_rules >= current_max_rules )
+ {
+ ++num_reallocs;
+ current_max_rules += MAX_RULES_INCREMENT;
+ rule_type = reallocate_integer_array( rule_type,
+ current_max_rules );
+ rule_linenum = reallocate_integer_array( rule_linenum,
+ current_max_rules );
+ rule_useful = reallocate_integer_array( rule_useful,
+ current_max_rules );
+ }
+
+ if ( num_rules > MAX_RULE )
+ lerrif( "too many rules (> %d)!", MAX_RULE );
+
+ rule_linenum[num_rules] = linenum;
+ rule_useful[num_rules] = false;
+ }
diff --git a/usr.bin/lex/parse.y b/usr.bin/lex/parse.y
new file mode 100644
index 0000000..c14f245
--- /dev/null
+++ b/usr.bin/lex/parse.y
@@ -0,0 +1,817 @@
+/* parse.y - parser for flex input */
+
+%token CHAR NUMBER SECTEND SCDECL XSCDECL WHITESPACE NAME PREVCCL EOF_OP
+
+%{
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/daffy/u0/vern/flex/RCS/parse.y,v 2.15 93/12/09 13:57:23 vern Exp $ */
+
+
+/* Some versions of bison are broken in that they use alloca() but don't
+ * declare it properly. The following is the patented (just kidding!)
+ * #ifdef chud to fix the problem, courtesy of Francois Pinard.
+ */
+#ifdef YYBISON
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not __GNUC__ */
+#if HAVE_ALLOCA_H
+#include <alloca.h>
+#else /* not HAVE_ALLOCA_H */
+#ifdef _AIX
+ #pragma alloca
+#else /* not _AIX */
+char *alloca ();
+#endif /* not _AIX */
+#endif /* not HAVE_ALLOCA_H */
+#endif /* not __GNUC__ */
+#endif /* YYBISON */
+
+/* Bletch, ^^^^ that was ugly! */
+
+
+#include "flexdef.h"
+
+int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, actvp, rulelen;
+int trlcontxt, xcluflg, cclsorted, varlength, variable_trail_rule;
+int *active_ss;
+Char clower();
+void build_eof_action();
+void yyerror();
+
+static int madeany = false; /* whether we've made the '.' character class */
+int previous_continued_action; /* whether the previous rule's action was '|' */
+
+/* On some over-ambitious machines, such as DEC Alpha's, the default
+ * token type is "long" instead of "int"; this leads to problems with
+ * declaring yylval in flexdef.h. But so far, all the yacc's I've seen
+ * wrap their definitions of YYSTYPE with "#ifndef YYSTYPE"'s, so the
+ * following should ensure that the default token type is "int".
+ */
+#define YYSTYPE int
+
+%}
+
+%%
+goal : initlex sect1 sect1end sect2 initforrule
+ { /* add default rule */
+ int def_rule;
+
+ pat = cclinit();
+ cclnegate( pat );
+
+ def_rule = mkstate( -pat );
+
+ /* Remember the number of the default rule so we
+ * don't generate "can't match" warnings for it.
+ */
+ default_rule = num_rules;
+
+ finish_rule( def_rule, false, 0, 0 );
+
+ for ( i = 1; i <= lastsc; ++i )
+ scset[i] = mkbranch( scset[i], def_rule );
+
+ if ( spprdflt )
+ add_action(
+ "YY_FATAL_ERROR( \"flex scanner jammed\" )" );
+ else
+ add_action( "ECHO" );
+
+ add_action( ";\n\tYY_BREAK\n" );
+ }
+ ;
+
+initlex :
+ { /* initialize for processing rules */
+
+ /* Create default DFA start condition. */
+ scinstal( "INITIAL", false );
+
+ /* Initially, the start condition scoping is
+ * "no start conditions active".
+ */
+ actvp = 0;
+ }
+ ;
+
+sect1 : sect1 startconddecl WHITESPACE namelist1 '\n'
+ |
+ | error '\n'
+ { synerr( "unknown error processing section 1" ); }
+ ;
+
+sect1end : SECTEND
+ {
+ /* We now know how many start conditions there
+ * are, so create the "activity" map indicating
+ * which conditions are active.
+ */
+ active_ss = allocate_integer_array( lastsc + 1 );
+
+ for ( i = 1; i <= lastsc; ++i )
+ active_ss[i] = 0;
+ }
+ ;
+
+startconddecl : SCDECL
+ { xcluflg = false; }
+
+ | XSCDECL
+ { xcluflg = true; }
+ ;
+
+namelist1 : namelist1 WHITESPACE NAME
+ { scinstal( nmstr, xcluflg ); }
+
+ | NAME
+ { scinstal( nmstr, xcluflg ); }
+
+ | error
+ { synerr( "bad start condition list" ); }
+ ;
+
+sect2 : sect2 initforrule flexrule '\n'
+ |
+ ;
+
+initforrule :
+ {
+ /* Initialize for a parse of one rule. */
+ trlcontxt = variable_trail_rule = varlength = false;
+ trailcnt = headcnt = rulelen = 0;
+ current_state_type = STATE_NORMAL;
+ previous_continued_action = continued_action;
+ new_rule();
+ }
+ ;
+
+flexrule : scon '^' rule
+ {
+ pat = $3;
+ finish_rule( pat, variable_trail_rule,
+ headcnt, trailcnt );
+
+ for ( i = 1; i <= actvp; ++i )
+ scbol[actvsc[i]] =
+ mkbranch( scbol[actvsc[i]], pat );
+
+ if ( ! bol_needed )
+ {
+ bol_needed = true;
+
+ if ( performance_report > 1 )
+ pinpoint_message(
+ "'^' operator results in sub-optimal performance" );
+ }
+ }
+
+ | scon rule
+ {
+ pat = $2;
+ finish_rule( pat, variable_trail_rule,
+ headcnt, trailcnt );
+
+ for ( i = 1; i <= actvp; ++i )
+ scset[actvsc[i]] =
+ mkbranch( scset[actvsc[i]], pat );
+ }
+
+ | '^' rule
+ {
+ pat = $2;
+ finish_rule( pat, variable_trail_rule,
+ headcnt, trailcnt );
+
+ /* Add to all non-exclusive start conditions,
+ * including the default (0) start condition.
+ */
+
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! scxclu[i] )
+ scbol[i] = mkbranch( scbol[i], pat );
+
+ if ( ! bol_needed )
+ {
+ bol_needed = true;
+
+ if ( performance_report > 1 )
+ pinpoint_message(
+ "'^' operator results in sub-optimal performance" );
+ }
+ }
+
+ | rule
+ {
+ pat = $1;
+ finish_rule( pat, variable_trail_rule,
+ headcnt, trailcnt );
+
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! scxclu[i] )
+ scset[i] = mkbranch( scset[i], pat );
+ }
+
+ | scon EOF_OP
+ { build_eof_action(); }
+
+ | EOF_OP
+ {
+ /* This EOF applies to all start conditions
+ * which don't already have EOF actions.
+ */
+ actvp = 0;
+
+ for ( i = 1; i <= lastsc; ++i )
+ if ( ! sceof[i] )
+ actvsc[++actvp] = i;
+
+ if ( actvp == 0 )
+ warn(
+ "all start conditions already have <<EOF>> rules" );
+
+ else
+ build_eof_action();
+ }
+
+ | error
+ { synerr( "unrecognized rule" ); }
+ ;
+
+scon : '<' namelist2 '>'
+
+ | '<' '*' '>'
+ {
+ actvp = 0;
+
+ for ( i = 1; i <= lastsc; ++i )
+ actvsc[++actvp] = i;
+ }
+ ;
+
+namelist2 : namelist2 ',' sconname
+
+ | { actvp = 0; } sconname
+
+ | error
+ { synerr( "bad start condition list" ); }
+ ;
+
+sconname : NAME
+ {
+ if ( (scnum = sclookup( nmstr )) == 0 )
+ format_pinpoint_message(
+ "undeclared start condition %s",
+ nmstr );
+ else
+ {
+ if ( ++actvp >= current_max_scs )
+ /* Some bozo has included multiple
+ * instances of start condition names.
+ */
+ pinpoint_message(
+ "too many start conditions in <> construct!" );
+
+ else
+ actvsc[actvp] = scnum;
+ }
+ }
+ ;
+
+rule : re2 re
+ {
+ if ( transchar[lastst[$2]] != SYM_EPSILON )
+ /* Provide final transition \now/ so it
+ * will be marked as a trailing context
+ * state.
+ */
+ $2 = link_machines( $2,
+ mkstate( SYM_EPSILON ) );
+
+ mark_beginning_as_normal( $2 );
+ current_state_type = STATE_NORMAL;
+
+ if ( previous_continued_action )
+ {
+ /* We need to treat this as variable trailing
+ * context so that the backup does not happen
+ * in the action but before the action switch
+ * statement. If the backup happens in the
+ * action, then the rules "falling into" this
+ * one's action will *also* do the backup,
+ * erroneously.
+ */
+ if ( ! varlength || headcnt != 0 )
+ warn(
+ "trailing context made variable due to preceding '|' action" );
+
+ /* Mark as variable. */
+ varlength = true;
+ headcnt = 0;
+ }
+
+ if ( lex_compat || (varlength && headcnt == 0) )
+ { /* variable trailing context rule */
+ /* Mark the first part of the rule as the
+ * accepting "head" part of a trailing
+ * context rule.
+ *
+ * By the way, we didn't do this at the
+ * beginning of this production because back
+ * then current_state_type was set up for a
+ * trail rule, and add_accept() can create
+ * a new state ...
+ */
+ add_accept( $1,
+ num_rules | YY_TRAILING_HEAD_MASK );
+ variable_trail_rule = true;
+ }
+
+ else
+ trailcnt = rulelen;
+
+ $$ = link_machines( $1, $2 );
+ }
+
+ | re2 re '$'
+ { synerr( "trailing context used twice" ); }
+
+ | re '$'
+ {
+ headcnt = 0;
+ trailcnt = 1;
+ rulelen = 1;
+ varlength = false;
+
+ current_state_type = STATE_TRAILING_CONTEXT;
+
+ if ( trlcontxt )
+ {
+ synerr( "trailing context used twice" );
+ $$ = mkstate( SYM_EPSILON );
+ }
+
+ else if ( previous_continued_action )
+ {
+ /* See the comment in the rule for "re2 re"
+ * above.
+ */
+ warn(
+ "trailing context made variable due to preceding '|' action" );
+
+ varlength = true;
+ }
+
+ if ( lex_compat || varlength )
+ {
+ /* Again, see the comment in the rule for
+ * "re2 re" above.
+ */
+ add_accept( $1,
+ num_rules | YY_TRAILING_HEAD_MASK );
+ variable_trail_rule = true;
+ }
+
+ trlcontxt = true;
+
+ eps = mkstate( SYM_EPSILON );
+ $$ = link_machines( $1,
+ link_machines( eps, mkstate( '\n' ) ) );
+ }
+
+ | re
+ {
+ $$ = $1;
+
+ if ( trlcontxt )
+ {
+ if ( lex_compat || (varlength && headcnt == 0) )
+ /* Both head and trail are
+ * variable-length.
+ */
+ variable_trail_rule = true;
+ else
+ trailcnt = rulelen;
+ }
+ }
+ ;
+
+
+re : re '|' series
+ {
+ varlength = true;
+ $$ = mkor( $1, $3 );
+ }
+
+ | series
+ { $$ = $1; }
+ ;
+
+
+re2 : re '/'
+ {
+ /* This rule is written separately so the
+ * reduction will occur before the trailing
+ * series is parsed.
+ */
+
+ if ( trlcontxt )
+ synerr( "trailing context used twice" );
+ else
+ trlcontxt = true;
+
+ if ( varlength )
+ /* We hope the trailing context is
+ * fixed-length.
+ */
+ varlength = false;
+ else
+ headcnt = rulelen;
+
+ rulelen = 0;
+
+ current_state_type = STATE_TRAILING_CONTEXT;
+ $$ = $1;
+ }
+ ;
+
+series : series singleton
+ {
+ /* This is where concatenation of adjacent patterns
+ * gets done.
+ */
+ $$ = link_machines( $1, $2 );
+ }
+
+ | singleton
+ { $$ = $1; }
+ ;
+
+singleton : singleton '*'
+ {
+ varlength = true;
+
+ $$ = mkclos( $1 );
+ }
+
+ | singleton '+'
+ {
+ varlength = true;
+ $$ = mkposcl( $1 );
+ }
+
+ | singleton '?'
+ {
+ varlength = true;
+ $$ = mkopt( $1 );
+ }
+
+ | singleton '{' NUMBER ',' NUMBER '}'
+ {
+ varlength = true;
+
+ if ( $3 > $5 || $3 < 0 )
+ {
+ synerr( "bad iteration values" );
+ $$ = $1;
+ }
+ else
+ {
+ if ( $3 == 0 )
+ {
+ if ( $5 <= 0 )
+ {
+ synerr(
+ "bad iteration values" );
+ $$ = $1;
+ }
+ else
+ $$ = mkopt(
+ mkrep( $1, 1, $5 ) );
+ }
+ else
+ $$ = mkrep( $1, $3, $5 );
+ }
+ }
+
+ | singleton '{' NUMBER ',' '}'
+ {
+ varlength = true;
+
+ if ( $3 <= 0 )
+ {
+ synerr( "iteration value must be positive" );
+ $$ = $1;
+ }
+
+ else
+ $$ = mkrep( $1, $3, INFINITY );
+ }
+
+ | singleton '{' NUMBER '}'
+ {
+ /* The singleton could be something like "(foo)",
+ * in which case we have no idea what its length
+ * is, so we punt here.
+ */
+ varlength = true;
+
+ if ( $3 <= 0 )
+ {
+ synerr( "iteration value must be positive" );
+ $$ = $1;
+ }
+
+ else
+ $$ = link_machines( $1,
+ copysingl( $1, $3 - 1 ) );
+ }
+
+ | '.'
+ {
+ if ( ! madeany )
+ {
+ /* Create the '.' character class. */
+ anyccl = cclinit();
+ ccladd( anyccl, '\n' );
+ cclnegate( anyccl );
+
+ if ( useecs )
+ mkeccl( ccltbl + cclmap[anyccl],
+ ccllen[anyccl], nextecm,
+ ecgroup, csize, csize );
+
+ madeany = true;
+ }
+
+ ++rulelen;
+
+ $$ = mkstate( -anyccl );
+ }
+
+ | fullccl
+ {
+ if ( ! cclsorted )
+ /* Sort characters for fast searching. We
+ * use a shell sort since this list could
+ * be large.
+ */
+ cshell( ccltbl + cclmap[$1], ccllen[$1], true );
+
+ if ( useecs )
+ mkeccl( ccltbl + cclmap[$1], ccllen[$1],
+ nextecm, ecgroup, csize, csize );
+
+ ++rulelen;
+
+ $$ = mkstate( -$1 );
+ }
+
+ | PREVCCL
+ {
+ ++rulelen;
+
+ $$ = mkstate( -$1 );
+ }
+
+ | '"' string '"'
+ { $$ = $2; }
+
+ | '(' re ')'
+ { $$ = $2; }
+
+ | CHAR
+ {
+ ++rulelen;
+
+ if ( caseins && $1 >= 'A' && $1 <= 'Z' )
+ $1 = clower( $1 );
+
+ $$ = mkstate( $1 );
+ }
+ ;
+
+fullccl : '[' ccl ']'
+ { $$ = $2; }
+
+ | '[' '^' ccl ']'
+ {
+ cclnegate( $3 );
+ $$ = $3;
+ }
+ ;
+
+ccl : ccl CHAR '-' CHAR
+ {
+ if ( caseins )
+ {
+ if ( $2 >= 'A' && $2 <= 'Z' )
+ $2 = clower( $2 );
+ if ( $4 >= 'A' && $4 <= 'Z' )
+ $4 = clower( $4 );
+ }
+
+ if ( $2 > $4 )
+ synerr( "negative range in character class" );
+
+ else
+ {
+ for ( i = $2; i <= $4; ++i )
+ ccladd( $1, i );
+
+ /* Keep track if this ccl is staying in
+ * alphabetical order.
+ */
+ cclsorted = cclsorted && ($2 > lastchar);
+ lastchar = $4;
+ }
+
+ $$ = $1;
+ }
+
+ | ccl CHAR
+ {
+ if ( caseins && $2 >= 'A' && $2 <= 'Z' )
+ $2 = clower( $2 );
+
+ ccladd( $1, $2 );
+ cclsorted = cclsorted && ($2 > lastchar);
+ lastchar = $2;
+ $$ = $1;
+ }
+
+ |
+ {
+ cclsorted = true;
+ lastchar = 0;
+ $$ = cclinit();
+ }
+ ;
+
+string : string CHAR
+ {
+ if ( caseins && $2 >= 'A' && $2 <= 'Z' )
+ $2 = clower( $2 );
+
+ ++rulelen;
+
+ $$ = link_machines( $1, mkstate( $2 ) );
+ }
+
+ |
+ { $$ = mkstate( SYM_EPSILON ); }
+ ;
+
+%%
+
+
+/* build_eof_action - build the "<<EOF>>" action for the active start
+ * conditions
+ */
+
+void build_eof_action()
+ {
+ register int i;
+ char action_text[MAXLINE];
+
+ for ( i = 1; i <= actvp; ++i )
+ {
+ if ( sceof[actvsc[i]] )
+ format_pinpoint_message(
+ "multiple <<EOF>> rules for start condition %s",
+ scname[actvsc[i]] );
+
+ else
+ {
+ sceof[actvsc[i]] = true;
+ sprintf( action_text, "case YY_STATE_EOF(%s):\n",
+ scname[actvsc[i]] );
+ add_action( action_text );
+ }
+ }
+
+ line_directive_out( (FILE *) 0 );
+
+ /* This isn't a normal rule after all - don't count it as
+ * such, so we don't have any holes in the rule numbering
+ * (which make generating "rule can never match" warnings
+ * more difficult.
+ */
+ --num_rules;
+ ++num_eof_rules;
+ }
+
+
+/* format_synerr - write out formatted syntax error */
+
+void format_synerr( msg, arg )
+char msg[], arg[];
+ {
+ char errmsg[MAXLINE];
+
+ (void) sprintf( errmsg, msg, arg );
+ synerr( errmsg );
+ }
+
+
+/* synerr - report a syntax error */
+
+void synerr( str )
+char str[];
+ {
+ syntaxerror = true;
+ pinpoint_message( str );
+ }
+
+
+/* warn - report a warning, unless -w was given */
+
+void warn( str )
+char str[];
+ {
+ line_warning( str, linenum );
+ }
+
+/* format_pinpoint_message - write out a message formatted with one string,
+ * pinpointing its location
+ */
+
+void format_pinpoint_message( msg, arg )
+char msg[], arg[];
+ {
+ char errmsg[MAXLINE];
+
+ (void) sprintf( errmsg, msg, arg );
+ pinpoint_message( errmsg );
+ }
+
+
+/* pinpoint_message - write out a message, pinpointing its location */
+
+void pinpoint_message( str )
+char str[];
+ {
+ line_pinpoint( str, linenum );
+ }
+
+
+/* line_warning - report a warning at a given line, unless -w was given */
+
+void line_warning( str, line )
+char str[];
+int line;
+ {
+ char warning[MAXLINE];
+
+ if ( ! nowarn )
+ {
+ sprintf( warning, "warning, %s", str );
+ line_pinpoint( warning, line );
+ }
+ }
+
+
+/* line_pinpoint - write out a message, pinpointing it at the given line */
+
+void line_pinpoint( str, line )
+char str[];
+int line;
+ {
+ fprintf( stderr, "\"%s\", line %d: %s\n", infilename, line, str );
+ }
+
+
+/* yyerror - eat up an error message from the parser;
+ * currently, messages are ignore
+ */
+
+void yyerror( msg )
+char msg[];
+ {
+ }
diff --git a/usr.bin/lex/scan.l b/usr.bin/lex/scan.l
new file mode 100644
index 0000000..74b589c
--- /dev/null
+++ b/usr.bin/lex/scan.l
@@ -0,0 +1,572 @@
+/* scan.l - scanner for flex input */
+
+%{
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: scan.l,v 1.2 94/01/04 14:33:09 vern Exp $ */
+
+#include "flexdef.h"
+#include "parse.h"
+
+#define ACTION_ECHO add_action( yytext )
+#define MARK_END_OF_PROLOG mark_prolog();
+
+#define YY_DECL \
+ int flexscan()
+
+#define RETURNCHAR \
+ yylval = (unsigned char) yytext[0]; \
+ return CHAR;
+
+#define RETURNNAME \
+ strcpy( nmstr, yytext ); \
+ return NAME;
+
+#define PUT_BACK_STRING(str, start) \
+ for ( i = strlen( str ) - 1; i >= start; --i ) \
+ unput((str)[i])
+
+#define CHECK_REJECT(str) \
+ if ( all_upper( str ) ) \
+ reject = true;
+
+#define CHECK_YYMORE(str) \
+ if ( all_lower( str ) ) \
+ yymore_used = true;
+%}
+
+%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE
+%x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT ACTION_COMMENT
+%x ACTION_STRING PERCENT_BRACE_ACTION USED_LIST CODEBLOCK_2
+
+WS [ \t]+
+OPTWS [ \t]*
+NOT_WS [^ \t\n]
+
+NL (\n|\r\n|\n\r)
+
+NAME ([a-z_][a-z_0-9-]*)
+NOT_NAME [^a-z_*\n]+
+
+SCNAME {NAME}
+
+ESCSEQ (\\([^\n]|[0-9]{1,3}|x[0-9a-f]{1,2}))
+
+FIRST_CCL_CHAR ([^\\\n]|{ESCSEQ})
+CCL_CHAR ([^\\\n\]]|{ESCSEQ})
+
+%%
+ static int bracelevel, didadef, indented_code, checking_used;
+
+ int doing_codeblock = false;
+ int i;
+ Char nmdef[MAXLINE], myesc();
+
+
+^{WS} indented_code = true; BEGIN(CODEBLOCK);
+^"/*" ACTION_ECHO; BEGIN(C_COMMENT);
+^"%s"{NAME}? return SCDECL;
+^"%x"{NAME}? return XSCDECL;
+^"%{".*{NL} {
+ ++linenum;
+ line_directive_out( (FILE *) 0 );
+ indented_code = false;
+ BEGIN(CODEBLOCK);
+ }
+
+{WS} return WHITESPACE;
+
+^"%%".* {
+ sectnum = 2;
+ bracelevel = 0;
+ mark_defs1();
+ line_directive_out( (FILE *) 0 );
+ BEGIN(SECT2PROLOG);
+ return SECTEND;
+ }
+
+^"%pointer".*{NL} {
+ if ( lex_compat )
+ warn( "%pointer incompatible with -l option" );
+ else
+ yytext_is_array = false;
+ ++linenum;
+ }
+^"%array".*{NL} {
+ if ( C_plus_plus )
+ warn( "%array incompatible with -+ option" );
+ else
+ yytext_is_array = true;
+ ++linenum;
+ }
+
+^"%used" {
+ warn( "%used/%unused have been deprecated" );
+ checking_used = REALLY_USED; BEGIN(USED_LIST);
+ }
+^"%unused" {
+ warn( "%used/%unused have been deprecated" );
+ checking_used = REALLY_NOT_USED; BEGIN(USED_LIST);
+ }
+
+
+^"%"[aceknopr]{OPTWS}[0-9]*{OPTWS}{NL} ++linenum; /* ignore */
+
+^"%"[^sxanpekotcru{}].* synerr( "unrecognized '%' directive" );
+
+^{NAME} {
+ strcpy( nmstr, yytext );
+ didadef = false;
+ BEGIN(PICKUPDEF);
+ }
+
+{SCNAME} RETURNNAME;
+^{OPTWS}{NL} ++linenum; /* allows blank lines in section 1 */
+{OPTWS}{NL} ++linenum; return '\n';
+
+
+<C_COMMENT>"*/" ACTION_ECHO; BEGIN(INITIAL);
+<C_COMMENT>"*/".*{NL} ++linenum; ACTION_ECHO; BEGIN(INITIAL);
+<C_COMMENT>[^*\n]+ ACTION_ECHO;
+<C_COMMENT>"*" ACTION_ECHO;
+<C_COMMENT>{NL} ++linenum; ACTION_ECHO;
+
+
+<CODEBLOCK>^"%}".*{NL} ++linenum; BEGIN(INITIAL);
+<CODEBLOCK>"reject" ACTION_ECHO; CHECK_REJECT(yytext);
+<CODEBLOCK>"yymore" ACTION_ECHO; CHECK_YYMORE(yytext);
+<CODEBLOCK>{NAME}|{NOT_NAME}|. ACTION_ECHO;
+<CODEBLOCK>{NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( indented_code )
+ BEGIN(INITIAL);
+ }
+
+
+<PICKUPDEF>{WS} /* separates name and definition */
+
+<PICKUPDEF>{NOT_WS}.* {
+ strcpy( (char *) nmdef, yytext );
+
+ /* Skip trailing whitespace. */
+ for ( i = strlen( (char *) nmdef ) - 1;
+ i >= 0 && (nmdef[i] == ' ' || nmdef[i] == '\t');
+ --i )
+ ;
+
+ nmdef[i + 1] = '\0';
+
+ ndinstal( nmstr, nmdef );
+ didadef = true;
+ }
+
+<PICKUPDEF>{NL} {
+ if ( ! didadef )
+ synerr( "incomplete name definition" );
+ BEGIN(INITIAL);
+ ++linenum;
+ }
+
+<RECOVER>.*{NL} ++linenum; BEGIN(INITIAL); RETURNNAME;
+
+
+<USED_LIST>{NL} ++linenum; BEGIN(INITIAL);
+<USED_LIST>{WS}
+<USED_LIST>"reject" {
+ if ( all_upper( yytext ) )
+ reject_really_used = checking_used;
+ else
+ synerr(
+ "unrecognized %used/%unused construct" );
+ }
+<USED_LIST>"yymore" {
+ if ( all_lower( yytext ) )
+ yymore_really_used = checking_used;
+ else
+ synerr(
+ "unrecognized %used/%unused construct" );
+ }
+<USED_LIST>{NOT_WS}+ synerr( "unrecognized %used/%unused construct" );
+
+
+<SECT2PROLOG>^"%{".* ++bracelevel; yyless( 2 ); /* eat only %{ */
+<SECT2PROLOG>^"%}".* --bracelevel; yyless( 2 ); /* eat only %} */
+
+<SECT2PROLOG>^{WS}.* ACTION_ECHO; /* indented code in prolog */
+
+<SECT2PROLOG>^{NOT_WS}.* { /* non-indented code */
+ if ( bracelevel <= 0 )
+ { /* not in %{ ... %} */
+ yyless( 0 ); /* put it all back */
+ mark_prolog();
+ BEGIN(SECT2);
+ }
+ else
+ ACTION_ECHO;
+ }
+
+<SECT2PROLOG>.* ACTION_ECHO;
+<SECT2PROLOG>{NL} ++linenum; ACTION_ECHO;
+
+<SECT2PROLOG><<EOF>> {
+ mark_prolog();
+ sectnum = 0;
+ yyterminate(); /* to stop the parser */
+ }
+
+<SECT2>^{OPTWS}{NL} ++linenum; /* allow blank lines in section 2 */
+
+<SECT2>^({WS}|"%{") {
+ indented_code = (yytext[0] != '%');
+ doing_codeblock = true;
+ bracelevel = 1;
+
+ if ( indented_code )
+ ACTION_ECHO;
+
+ BEGIN(CODEBLOCK_2);
+ }
+
+<SECT2>^"<" BEGIN(SC); return '<';
+<SECT2>^"^" return '^';
+<SECT2>\" BEGIN(QUOTE); return '"';
+<SECT2>"{"/[0-9] BEGIN(NUM); return '{';
+<SECT2>"{"[^0-9\n][^}\n]* BEGIN(BRACEERROR);
+<SECT2>"$"/([ \t]|{NL}) return '$';
+
+<SECT2>{WS}"%{" {
+ bracelevel = 1;
+ BEGIN(PERCENT_BRACE_ACTION);
+ return '\n';
+ }
+<SECT2>{WS}"|".*{NL} continued_action = true; ++linenum; return '\n';
+
+<SECT2>{WS} {
+ /* This rule is separate from the one below because
+ * otherwise we get variable trailing context, so
+ * we can't build the scanner using -{f,F}.
+ */
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ return '\n';
+ }
+
+<SECT2>{OPTWS}{NL} {
+ bracelevel = 0;
+ continued_action = false;
+ BEGIN(ACTION);
+ unput( '\n' ); /* so <ACTION> sees it */
+ return '\n';
+ }
+
+<SECT2>"<<EOF>>" return EOF_OP;
+
+<SECT2>^"%%".* {
+ sectnum = 3;
+ BEGIN(SECT3);
+ yyterminate(); /* to stop the parser */
+ }
+
+<SECT2>"["{FIRST_CCL_CHAR}{CCL_CHAR}* {
+ int cclval;
+
+ strcpy( nmstr, yytext );
+
+ /* Check to see if we've already encountered this
+ * ccl.
+ */
+ if ( (cclval = ccllookup( (Char *) nmstr )) )
+ {
+ if ( input() != ']' )
+ synerr( "bad character class" );
+
+ yylval = cclval;
+ ++cclreuse;
+ return PREVCCL;
+ }
+ else
+ {
+ /* We fudge a bit. We know that this ccl will
+ * soon be numbered as lastccl + 1 by cclinit.
+ */
+ cclinstal( (Char *) nmstr, lastccl + 1 );
+
+ /* Push back everything but the leading bracket
+ * so the ccl can be rescanned.
+ */
+ yyless( 1 );
+
+ BEGIN(FIRSTCCL);
+ return '[';
+ }
+ }
+
+<SECT2>"{"{NAME}"}" {
+ register Char *nmdefptr;
+ Char *ndlookup();
+
+ strcpy( nmstr, yytext + 1 );
+ nmstr[yyleng - 2] = '\0'; /* chop trailing brace */
+
+ if ( ! (nmdefptr = ndlookup( nmstr )) )
+ format_synerr( "undefined definition {%s}",
+ nmstr );
+
+ else
+ { /* push back name surrounded by ()'s */
+ int len = strlen( (char *) nmdefptr );
+
+ if ( lex_compat || nmdefptr[0] == '^' ||
+ (len > 0 && nmdefptr[len - 1] == '$') )
+ { /* don't use ()'s after all */
+ PUT_BACK_STRING((char *) nmdefptr, 0);
+
+ if ( nmdefptr[0] == '^' )
+ BEGIN(CARETISBOL);
+ }
+
+ else
+ {
+ unput(')');
+ PUT_BACK_STRING((char *) nmdefptr, 0);
+ unput('(');
+ }
+ }
+ }
+
+<SECT2>[/|*+?.()] return (unsigned char) yytext[0];
+<SECT2>. RETURNCHAR;
+
+
+<SC>[,*] return (unsigned char) yytext[0];
+<SC>">" BEGIN(SECT2); return '>';
+<SC>">"/^ BEGIN(CARETISBOL); return '>';
+<SC>{SCNAME} RETURNNAME;
+<SC>. {
+ format_synerr( "bad <start condition>: %s", yytext );
+ }
+
+<CARETISBOL>"^" BEGIN(SECT2); return '^';
+
+
+<QUOTE>[^"\n] RETURNCHAR;
+<QUOTE>\" BEGIN(SECT2); return '"';
+
+<QUOTE>{NL} {
+ synerr( "missing quote" );
+ BEGIN(SECT2);
+ ++linenum;
+ return '"';
+ }
+
+
+<FIRSTCCL>"^"/[^-\]\n] BEGIN(CCL); return '^';
+<FIRSTCCL>"^"/("-"|"]") return '^';
+<FIRSTCCL>. BEGIN(CCL); RETURNCHAR;
+
+<CCL>-/[^\]\n] return '-';
+<CCL>[^\]\n] RETURNCHAR;
+<CCL>"]" BEGIN(SECT2); return ']';
+<CCL>.|{NL} {
+ synerr( "bad character class" );
+ BEGIN(SECT2);
+ return ']';
+ }
+
+
+<NUM>[0-9]+ {
+ yylval = myctoi( yytext );
+ return NUMBER;
+ }
+
+<NUM>"," return ',';
+<NUM>"}" BEGIN(SECT2); return '}';
+
+<NUM>. {
+ synerr( "bad character inside {}'s" );
+ BEGIN(SECT2);
+ return '}';
+ }
+
+<NUM>{NL} {
+ synerr( "missing }" );
+ BEGIN(SECT2);
+ ++linenum;
+ return '}';
+ }
+
+
+<BRACEERROR>"}" synerr( "bad name in {}'s" ); BEGIN(SECT2);
+<BRACEERROR>{NL} synerr( "missing }" ); ++linenum; BEGIN(SECT2);
+
+
+<CODEBLOCK_2>"/*" ACTION_ECHO; BEGIN(ACTION_COMMENT);
+<PERCENT_BRACE_ACTION,CODEBLOCK_2>{OPTWS}"%}".* bracelevel = 0;
+<PERCENT_BRACE_ACTION,CODEBLOCK_2,ACTION>"reject" {
+ ACTION_ECHO;
+ CHECK_REJECT(yytext);
+ }
+<PERCENT_BRACE_ACTION,CODEBLOCK_2,ACTION>"yymore" {
+ ACTION_ECHO;
+ CHECK_YYMORE(yytext);
+ }
+<PERCENT_BRACE_ACTION,CODEBLOCK_2>{NAME}|{NOT_NAME}|. ACTION_ECHO;
+<PERCENT_BRACE_ACTION,CODEBLOCK_2>{NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 ||
+ (doing_codeblock && indented_code) )
+ {
+ if ( ! doing_codeblock )
+ add_action( "\tYY_BREAK\n" );
+
+ doing_codeblock = false;
+ BEGIN(SECT2);
+ }
+ }
+
+
+ /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */
+<ACTION>"{" ACTION_ECHO; ++bracelevel;
+<ACTION>"}" ACTION_ECHO; --bracelevel;
+<ACTION>[^a-z_{}"'/\n]+ ACTION_ECHO;
+<ACTION>{NAME} ACTION_ECHO;
+<ACTION>"/*" ACTION_ECHO; BEGIN(ACTION_COMMENT);
+<ACTION>"'"([^'\\\n]|\\.)*"'" ACTION_ECHO; /* character constant */
+<ACTION>\" ACTION_ECHO; BEGIN(ACTION_STRING);
+<ACTION>{NL} {
+ ++linenum;
+ ACTION_ECHO;
+ if ( bracelevel == 0 )
+ {
+ add_action( "\tYY_BREAK\n" );
+ BEGIN(SECT2);
+ }
+ }
+<ACTION>. ACTION_ECHO;
+
+<ACTION_COMMENT>"*/" {
+ ACTION_ECHO;
+ if ( doing_codeblock )
+ BEGIN(CODEBLOCK_2);
+ else
+ BEGIN(ACTION);
+ }
+
+<ACTION_COMMENT>"*" ACTION_ECHO;
+<ACTION_COMMENT>[^*\n]+ ACTION_ECHO;
+<ACTION_COMMENT>[^*\n]*{NL} ++linenum; ACTION_ECHO;
+
+<ACTION_STRING>[^"\\\n]+ ACTION_ECHO;
+<ACTION_STRING>\\. ACTION_ECHO;
+<ACTION_STRING>{NL} ++linenum; ACTION_ECHO;
+<ACTION_STRING>\" ACTION_ECHO; BEGIN(ACTION);
+<ACTION_STRING>. ACTION_ECHO;
+
+<ACTION,ACTION_COMMENT,ACTION_STRING><<EOF>> {
+ synerr( "EOF encountered inside an action" );
+ yyterminate();
+ }
+
+
+<SECT2,QUOTE,CCL>{ESCSEQ} {
+ yylval = myesc( (Char *) yytext );
+ return CHAR;
+ }
+
+<FIRSTCCL>{ESCSEQ} {
+ yylval = myesc( (Char *) yytext );
+ BEGIN(CCL);
+ return CHAR;
+ }
+
+
+<SECT3>.*(\n?) ECHO;
+<SECT3><<EOF>> sectnum = 0; yyterminate();
+
+<*>.|\n format_synerr( "bad character: %s", yytext );
+
+%%
+
+
+int yywrap()
+ {
+ if ( --num_input_files > 0 )
+ {
+ set_input_file( *++input_files );
+ return 0;
+ }
+
+ else
+ return 1;
+ }
+
+
+/* set_input_file - open the given file (if NULL, stdin) for scanning */
+
+void set_input_file( file )
+char *file;
+ {
+ if ( file )
+ {
+ infilename = file;
+ yyin = fopen( infilename, "r" );
+
+ if ( yyin == NULL )
+ lerrsf( "can't open %s", file );
+ }
+
+ else
+ {
+ yyin = stdin;
+ infilename = "<stdin>";
+ }
+ }
+
+
+/* Wrapper routines for accessing the scanner's malloc routines. */
+
+void *flex_alloc( size )
+unsigned int size;
+ {
+ return yy_flex_alloc( size );
+ }
+
+void *flex_realloc( ptr, size )
+void *ptr;
+unsigned int size;
+ {
+ return yy_flex_realloc( ptr, size );
+ }
+
+void flex_free( ptr )
+void *ptr;
+ {
+ yy_flex_free( ptr );
+ }
diff --git a/usr.bin/lex/skel.c b/usr.bin/lex/skel.c
new file mode 100644
index 0000000..4ab49d1
--- /dev/null
+++ b/usr.bin/lex/skel.c
@@ -0,0 +1,1232 @@
+/* File created from flex.skel via mkskel.sh */
+
+#include "flexdef.h"
+
+char *skel[] = {
+ "/* A lexical scanner generated by flex */",
+ "",
+ "/* Scanner skeleton version:",
+ " * $Header: /home/daffy/u0/vern/flex/flex-2.4.7/RCS/flex.skl,v 1.2 94/08/03 11:13:24 vern Exp $",
+ " */",
+ "",
+ "#define FLEX_SCANNER",
+ "",
+ "%-",
+ "#include <stdio.h>",
+ "%*",
+ "",
+ "",
+ "/* cfront 1.2 defines \"c_plusplus\" instead of \"__cplusplus\" */",
+ "#ifdef c_plusplus",
+ "#ifndef __cplusplus",
+ "#define __cplusplus",
+ "#endif",
+ "#endif",
+ "",
+ "",
+ "#ifdef __cplusplus",
+ "",
+ "#include <stdlib.h>",
+ "%+",
+ "class istream;",
+ "%*",
+ "#include <unistd.h>",
+ "",
+ "/* Use prototypes in function declarations. */",
+ "#define YY_USE_PROTOS",
+ "",
+ "/* The \"const\" storage-class-modifier is valid. */",
+ "#define YY_USE_CONST",
+ "",
+ "#else /* ! __cplusplus */",
+ "",
+ "#ifdef __STDC__",
+ "",
+ "#define YY_USE_PROTOS",
+ "#define YY_USE_CONST",
+ "",
+ "#endif /* __STDC__ */",
+ "#endif /* ! __cplusplus */",
+ "",
+ "",
+ "#ifdef __TURBOC__",
+ "#define YY_USE_CONST",
+ "#endif",
+ "",
+ "",
+ "#ifndef YY_USE_CONST",
+ "#ifndef const",
+ "#define const",
+ "#endif",
+ "#endif",
+ "",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "#define YY_PROTO(proto) proto",
+ "#else",
+ "#define YY_PROTO(proto) ()",
+ "#endif",
+ "",
+ "/* Returned upon end-of-file. */",
+ "#define YY_NULL 0",
+ "",
+ "/* Promotes a possibly negative, possibly signed char to an unsigned",
+ " * integer for use as an array index. If the signed char is negative,",
+ " * we want to instead treat it as an 8-bit unsigned char, hence the",
+ " * double cast.",
+ " */",
+ "#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)",
+ "",
+ "/* Enter a start condition. This macro really ought to take a parameter,",
+ " * but we do it the disgusting crufty way forced on us by the ()-less",
+ " * definition of BEGIN.",
+ " */",
+ "#define BEGIN yy_start = 1 + 2 *",
+ "",
+ "/* Translate the current start state into a value that can be later handed",
+ " * to BEGIN to return to the state.",
+ " */",
+ "#define YY_START ((yy_start - 1) / 2)",
+ "",
+ "/* Action number for EOF rule of a given start state. */",
+ "#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)",
+ "",
+ "/* Special action meaning \"start processing a new file\". Now included",
+ " * only for backward compatibility with previous versions of flex.",
+ " */",
+ "#define YY_NEW_FILE yyrestart( yyin )",
+ "",
+ "#define YY_END_OF_BUFFER_CHAR 0",
+ "",
+ "/* Size of default input buffer. */",
+ "#define YY_BUF_SIZE 16384",
+ "",
+ "typedef struct yy_buffer_state *YY_BUFFER_STATE;",
+ "",
+ "extern int yyleng;",
+ "%-",
+ "extern FILE *yyin, *yyout;",
+ "%*",
+ "",
+ "#ifdef __cplusplus",
+ "extern \"C\" {",
+ "#endif",
+ " extern int yywrap YY_PROTO(( void ));",
+ "#ifdef __cplusplus",
+ " }",
+ "#endif",
+ "",
+ "#define EOB_ACT_CONTINUE_SCAN 0",
+ "#define EOB_ACT_END_OF_FILE 1",
+ "#define EOB_ACT_LAST_MATCH 2",
+ "",
+ "/* The funky do-while in the following #define is used to turn the definition",
+ " * int a single C statement (which needs a semi-colon terminator). This",
+ " * avoids problems with code like:",
+ " *",
+ " * if ( condition_holds )",
+ " * yyless( 5 );",
+ " * else",
+ " * do_something_else();",
+ " *",
+ " * Prior to using the do-while the compiler would get upset at the",
+ " * \"else\" because it interpreted the \"if\" statement as being all",
+ " * done when it reached the ';' after the yyless() call.",
+ " */",
+ "",
+ "/* Return all but the first 'n' matched characters back to the input stream. */",
+ "",
+ "#define yyless(n) \\",
+ " do \\",
+ " { \\",
+ " /* Undo effects of setting up yytext. */ \\",
+ " *yy_cp = yy_hold_char; \\",
+ " yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \\",
+ " YY_DO_BEFORE_ACTION; /* set up yytext again */ \\",
+ " } \\",
+ " while ( 0 )",
+ "",
+ "#define unput(c) yyunput( c, yytext_ptr )",
+ "",
+ "",
+ "struct yy_buffer_state",
+ " {",
+ "%-",
+ " FILE *yy_input_file;",
+ "%+",
+ " istream* yy_input_file;",
+ "%*",
+ "",
+ " char *yy_ch_buf; /* input buffer */",
+ " char *yy_buf_pos; /* current position in input buffer */",
+ "",
+ " /* Size of input buffer in bytes, not including room for EOB",
+ " * characters.",
+ " */",
+ " int yy_buf_size;",
+ "",
+ " /* Number of characters read into yy_ch_buf, not including EOB",
+ " * characters.",
+ " */",
+ " int yy_n_chars;",
+ "",
+ " /* Whether this is an \"interactive\" input source; if so, and",
+ " * if we're using stdio for input, then we want to use getc()",
+ " * instead of fread(), to make sure we stop fetching input after",
+ " * each newline.",
+ " */",
+ " int yy_is_interactive;",
+ "",
+ " /* Whether to try to fill the input buffer when we reach the",
+ " * end of it.",
+ " */",
+ " int yy_fill_buffer;",
+ "",
+ " int yy_buffer_status;",
+ "#define YY_BUFFER_NEW 0",
+ "#define YY_BUFFER_NORMAL 1",
+ " /* When an EOF's been seen but there's still some text to process",
+ " * then we mark the buffer as YY_EOF_PENDING, to indicate that we",
+ " * shouldn't try reading from the input source any more. We might",
+ " * still have a bunch of tokens to match, though, because of",
+ " * possible backing-up.",
+ " *",
+ " * When we actually see the EOF, we change the status to \"new\"",
+ " * (via yyrestart()), so that the user can continue scanning by",
+ " * just pointing yyin at a new input file.",
+ " */",
+ "#define YY_BUFFER_EOF_PENDING 2",
+ " };",
+ "",
+ "%- Standard (non-C++) definition",
+ "static YY_BUFFER_STATE yy_current_buffer = 0;",
+ "%*",
+ "",
+ "/* We provide macros for accessing buffer states in case in the",
+ " * future we want to put the buffer states in a more general",
+ " * \"scanner state\".",
+ " */",
+ "#define YY_CURRENT_BUFFER yy_current_buffer",
+ "",
+ "",
+ "%- Standard (non-C++) definition",
+ "/* yy_hold_char holds the character lost when yytext is formed. */",
+ "static char yy_hold_char;",
+ "",
+ "static int yy_n_chars; /* number of characters read into yy_ch_buf */",
+ "",
+ "",
+ "int yyleng;",
+ "",
+ "/* Points to current character in buffer. */",
+ "static char *yy_c_buf_p = (char *) 0;",
+ "static int yy_init = 1; /* whether we need to initialize */",
+ "static int yy_start = 0; /* start state number */",
+ "",
+ "/* Flag which is used to allow yywrap()'s to do buffer switches",
+ " * instead of setting up a fresh yyin. A bit of a hack ...",
+ " */",
+ "static int yy_did_buffer_switch_on_eof;",
+ "",
+ "static void yyunput YY_PROTO(( int c, char *buf_ptr ));",
+ "void yyrestart YY_PROTO(( FILE *input_file ));",
+ "void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));",
+ "void yy_load_buffer_state YY_PROTO(( void ));",
+ "YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));",
+ "void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));",
+ "void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));",
+ "",
+ "static int yy_start_stack_ptr = 0;",
+ "static int yy_start_stack_depth = 0;",
+ "static int *yy_start_stack = 0;",
+ "static void yy_push_state YY_PROTO(( int new_state ));",
+ "static void yy_pop_state YY_PROTO(( void ));",
+ "static int yy_top_state YY_PROTO(( void ));",
+ "%*",
+ "",
+ "static void *yy_flex_alloc YY_PROTO(( unsigned int ));",
+ "static void *yy_flex_realloc YY_PROTO(( void *, unsigned int ));",
+ "static void yy_flex_free YY_PROTO(( void * ));",
+ "",
+ "#define yy_new_buffer yy_create_buffer",
+ "",
+ "%% yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here",
+ "",
+ "#ifndef yytext_ptr",
+ "static void yy_flex_strncpy YY_PROTO(( char *, const char *, int ));",
+ "#endif",
+ "",
+ "%- Standard (non-C++) definition",
+ "#ifdef __cplusplus",
+ "static int yyinput YY_PROTO(( void ));",
+ "#else",
+ "static int input YY_PROTO(( void ));",
+ "#endif",
+ "%*",
+ "",
+ "%- Standard (non-C++) definition",
+ "static yy_state_type yy_get_previous_state YY_PROTO(( void ));",
+ "static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));",
+ "static int yy_get_next_buffer YY_PROTO(( void ));",
+ "static void yy_fatal_error YY_PROTO(( const char msg[] ));",
+ "%*",
+ "",
+ "/* Done after the current pattern has been matched and before the",
+ " * corresponding action - sets up yytext.",
+ " */",
+ "#define YY_DO_BEFORE_ACTION \\",
+ " yytext_ptr = yy_bp; \\",
+ "%% code to fiddle yytext and yyleng for yymore() goes here",
+ " yy_hold_char = *yy_cp; \\",
+ " *yy_cp = '\\0'; \\",
+ "%% code to copy yytext_ptr to yytext[] goes here, if %array",
+ " yy_c_buf_p = yy_cp;",
+ "",
+ "%% data tables for the DFA and the user's section 1 definitions go here",
+ "",
+ "/* Macros after this point can all be overridden by user definitions in",
+ " * section 1.",
+ " */",
+ "",
+ "#ifdef YY_MALLOC_DECL",
+ "YY_MALLOC_DECL",
+ "#else",
+ "#if __STDC__",
+ "#ifndef __cplusplus",
+ "#include <stdlib.h>",
+ "#endif",
+ "#else",
+ "/* Just try to get by without declaring the routines. This will fail",
+ " * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)",
+ " * or sizeof(void*) != sizeof(int).",
+ " */",
+ "#endif",
+ "#endif",
+ "",
+ "/* Amount of stuff to slurp up with each read. */",
+ "#ifndef YY_READ_BUF_SIZE",
+ "#define YY_READ_BUF_SIZE 8192",
+ "#endif",
+ "",
+ "/* Copy whatever the last rule matched to the standard output. */",
+ "",
+ "#ifndef ECHO",
+ "%- Standard (non-C++) definition",
+ "/* This used to be an fputs(), but since the string might contain NUL's,",
+ " * we now use fwrite().",
+ " */",
+ "#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )",
+ "%+ C++ definition",
+ "#define ECHO LexerOutput( yytext, yyleng )",
+ "%*",
+ "#endif",
+ "",
+ "/* Gets input and stuffs it into \"buf\". number of characters read, or YY_NULL,",
+ " * is returned in \"result\".",
+ " */",
+ "#ifndef YY_INPUT",
+ "#define YY_INPUT(buf,result,max_size) \\",
+ "%% fread()/read() definition of YY_INPUT goes here unless we're doing C++",
+ "%+ C++ definition",
+ " if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \\",
+ " YY_FATAL_ERROR( \"input in flex scanner failed\" );",
+ "%*",
+ "#endif",
+ "",
+ "/* No semi-colon after return; correct usage is to write \"yyterminate();\" -",
+ " * we don't want an extra ';' after the \"return\" because that will cause",
+ " * some compilers to complain about unreachable statements.",
+ " */",
+ "#ifndef yyterminate",
+ "#define yyterminate() return YY_NULL",
+ "#endif",
+ "",
+ "/* Number of entries by which start-condition stack grows. */",
+ "#ifndef YY_START_STACK_INCR",
+ "#define YY_START_STACK_INCR 25",
+ "#endif",
+ "",
+ "/* Report a fatal error. */",
+ "#ifndef YY_FATAL_ERROR",
+ "%-",
+ "#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )",
+ "%+",
+ "#define YY_FATAL_ERROR(msg) LexerError( msg )",
+ "%*",
+ "#endif",
+ "",
+ "/* Default declaration of generated scanner - a define so the user can",
+ " * easily add parameters.",
+ " */",
+ "#ifndef YY_DECL",
+ "%- Standard (non-C++) definition",
+ "#define YY_DECL int yylex YY_PROTO(( void ))",
+ "%+ C++ definition",
+ "#define YY_DECL int yyFlexLexer::yylex()",
+ "%*",
+ "#endif",
+ "",
+ "/* Code executed at the beginning of each rule, after yytext and yyleng",
+ " * have been set up.",
+ " */",
+ "#ifndef YY_USER_ACTION",
+ "#define YY_USER_ACTION",
+ "#endif",
+ "",
+ "/* Code executed at the end of each rule. */",
+ "#ifndef YY_BREAK",
+ "#define YY_BREAK break;",
+ "#endif",
+ "",
+ "YY_DECL",
+ " {",
+ " register yy_state_type yy_current_state;",
+ " register char *yy_cp, *yy_bp;",
+ " register int yy_act;",
+ "",
+ "%% user's declarations go here",
+ "",
+ " if ( yy_init )",
+ " {",
+ "#ifdef YY_USER_INIT",
+ " YY_USER_INIT;",
+ "#endif",
+ "",
+ " if ( ! yy_start )",
+ " yy_start = 1; /* first start state */",
+ "",
+ " if ( ! yyin )",
+ "%-",
+ " yyin = stdin;",
+ "%+",
+ " yyin = &cin;",
+ "%*",
+ "",
+ " if ( ! yyout )",
+ "%-",
+ " yyout = stdout;",
+ "%+",
+ " yyout = &cout;",
+ "%*",
+ "",
+ " if ( yy_current_buffer )",
+ " yy_init_buffer( yy_current_buffer, yyin );",
+ " else",
+ " yy_current_buffer =",
+ " yy_create_buffer( yyin, YY_BUF_SIZE );",
+ "",
+ " yy_load_buffer_state();",
+ "",
+ " yy_init = 0;",
+ " }",
+ "",
+ " while ( 1 ) /* loops until end-of-file is reached */",
+ " {",
+ "%% yymore()-related code goes here",
+ " yy_cp = yy_c_buf_p;",
+ "",
+ " /* Support of yytext. */",
+ " *yy_cp = yy_hold_char;",
+ "",
+ " /* yy_bp points to the position in yy_ch_buf of the start of",
+ " * the current run.",
+ " */",
+ " yy_bp = yy_cp;",
+ "",
+ "%% code to set up and find next match goes here",
+ "",
+ "yy_find_action:",
+ "%% code to find the action number goes here",
+ "",
+ " YY_DO_BEFORE_ACTION;",
+ "",
+ "%% code for yylineno update goes here, if -l option",
+ "",
+ "do_action: /* This label is used only to access EOF actions. */",
+ "",
+ "%% debug code goes here",
+ "",
+ " switch ( yy_act )",
+ " { /* beginning of action switch */",
+ "%% actions go here",
+ "",
+ " case YY_END_OF_BUFFER:",
+ " {",
+ " /* Amount of text matched not including the EOB char. */",
+ " int yy_amount_of_matched_text = yy_cp - yytext_ptr - 1;",
+ "",
+ " /* Undo the effects of YY_DO_BEFORE_ACTION. */",
+ " *yy_cp = yy_hold_char;",
+ "",
+ " if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )",
+ " {",
+ " /* We're scanning a new file or input source. It's",
+ " * possible that this happened because the user",
+ " * just pointed yyin at a new source and called",
+ " * yylex(). If so, then we have to assure",
+ " * consistency between yy_current_buffer and our",
+ " * globals. Here is the right place to do so, because",
+ " * this is the first action (other than possibly a",
+ " * back-up) that will match for the new input source.",
+ " */",
+ " yy_n_chars = yy_current_buffer->yy_n_chars;",
+ " yy_current_buffer->yy_input_file = yyin;",
+ " yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;",
+ " }",
+ "",
+ " /* Note that here we test for yy_c_buf_p \"<=\" to the position",
+ " * of the first EOB in the buffer, since yy_c_buf_p will",
+ " * already have been incremented past the NUL character",
+ " * (since all states make transitions on EOB to the",
+ " * end-of-buffer state). Contrast this with the test",
+ " * in input().",
+ " */",
+ " if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )",
+ " { /* This was really a NUL. */",
+ " yy_state_type yy_next_state;",
+ "",
+ " yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;",
+ "",
+ " yy_current_state = yy_get_previous_state();",
+ "",
+ " /* Okay, we're now positioned to make the NUL",
+ " * transition. We couldn't have",
+ " * yy_get_previous_state() go ahead and do it",
+ " * for us because it doesn't know how to deal",
+ " * with the possibility of jamming (and we don't",
+ " * want to build jamming into it because then it",
+ " * will run more slowly).",
+ " */",
+ "",
+ " yy_next_state = yy_try_NUL_trans( yy_current_state );",
+ "",
+ " yy_bp = yytext_ptr + YY_MORE_ADJ;",
+ "",
+ " if ( yy_next_state )",
+ " {",
+ " /* Consume the NUL. */",
+ " yy_cp = ++yy_c_buf_p;",
+ " yy_current_state = yy_next_state;",
+ " goto yy_match;",
+ " }",
+ "",
+ " else",
+ " {",
+ "%% code to do back-up for compressed tables and set up yy_cp goes here",
+ " goto yy_find_action;",
+ " }",
+ " }",
+ "",
+ " else switch ( yy_get_next_buffer() )",
+ " {",
+ " case EOB_ACT_END_OF_FILE:",
+ " {",
+ " yy_did_buffer_switch_on_eof = 0;",
+ "",
+ " if ( yywrap() )",
+ " {",
+ " /* Note: because we've taken care in",
+ " * yy_get_next_buffer() to have set up",
+ " * yytext, we can now set up",
+ " * yy_c_buf_p so that if some total",
+ " * hoser (like flex itself) wants to",
+ " * call the scanner after we return the",
+ " * YY_NULL, it'll still work - another",
+ " * YY_NULL will get returned.",
+ " */",
+ " yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;",
+ "",
+ " yy_act = YY_STATE_EOF(YY_START);",
+ " goto do_action;",
+ " }",
+ "",
+ " else",
+ " {",
+ " if ( ! yy_did_buffer_switch_on_eof )",
+ " YY_NEW_FILE;",
+ " }",
+ " break;",
+ " }",
+ "",
+ " case EOB_ACT_CONTINUE_SCAN:",
+ " yy_c_buf_p =",
+ " yytext_ptr + yy_amount_of_matched_text;",
+ "",
+ " yy_current_state = yy_get_previous_state();",
+ "",
+ " yy_cp = yy_c_buf_p;",
+ " yy_bp = yytext_ptr + YY_MORE_ADJ;",
+ " goto yy_match;",
+ "",
+ " case EOB_ACT_LAST_MATCH:",
+ " yy_c_buf_p =",
+ " &yy_current_buffer->yy_ch_buf[yy_n_chars];",
+ "",
+ " yy_current_state = yy_get_previous_state();",
+ "",
+ " yy_cp = yy_c_buf_p;",
+ " yy_bp = yytext_ptr + YY_MORE_ADJ;",
+ " goto yy_find_action;",
+ " }",
+ " break;",
+ " }",
+ "",
+ " default:",
+ " YY_FATAL_ERROR(",
+ " \"fatal flex scanner internal error--no action found\" );",
+ " } /* end of action switch */",
+ " } /* end of scanning one token */",
+ " } /* end of yylex */",
+ "",
+ "%+",
+ "#ifdef YY_INTERACTIVE",
+ "int yyFlexLexer::LexerInput( char* buf, int /* max_size */ )",
+ "#else",
+ "int yyFlexLexer::LexerInput( char* buf, int max_size )",
+ "#endif",
+ " {",
+ " if ( yyin->eof() || yyin->fail() )",
+ " return 0;",
+ "",
+ "#ifdef YY_INTERACTIVE",
+ " yyin->get( buf[0] );",
+ "",
+ " if ( yyin->eof() )",
+ " return 0;",
+ "",
+ " if ( yyin->bad() )",
+ " return -1;",
+ "",
+ " return 1;",
+ "",
+ "#else",
+ " (void) yyin->read( buf, max_size );",
+ "",
+ " if ( yyin->bad() )",
+ " return -1;",
+ " else",
+ " return yyin->gcount();",
+ "#endif",
+ " }",
+ "",
+ "void yyFlexLexer::LexerOutput( const char* buf, int size )",
+ " {",
+ " (void) yyout->write( buf, size );",
+ " }",
+ "%*",
+ "",
+ "/* yy_get_next_buffer - try to read in a new buffer",
+ " *",
+ " * Returns a code representing an action:",
+ " * EOB_ACT_LAST_MATCH -",
+ " * EOB_ACT_CONTINUE_SCAN - continue scanning from current position",
+ " * EOB_ACT_END_OF_FILE - end of file",
+ " */",
+ "",
+ "%-",
+ "static int yy_get_next_buffer()",
+ "%+",
+ "int yyFlexLexer::yy_get_next_buffer()",
+ "%*",
+ " {",
+ " register char *dest = yy_current_buffer->yy_ch_buf;",
+ " register char *source = yytext_ptr - 1; /* copy prev. char, too */",
+ " register int number_to_move, i;",
+ " int ret_val;",
+ "",
+ " if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )",
+ " YY_FATAL_ERROR(",
+ " \"fatal flex scanner internal error--end of buffer missed\" );",
+ "",
+ " if ( yy_current_buffer->yy_fill_buffer == 0 )",
+ " { /* Don't try to fill the buffer, so this is an EOF. */",
+ " if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )",
+ " {",
+ " /* We matched a singled characater, the EOB, so",
+ " * treat this as a final EOF.",
+ " */",
+ " return EOB_ACT_END_OF_FILE;",
+ " }",
+ "",
+ " else",
+ " {",
+ " /* We matched some text prior to the EOB, first",
+ " * process it.",
+ " */",
+ " return EOB_ACT_LAST_MATCH;",
+ " }",
+ " }",
+ "",
+ " /* Try to read more data. */",
+ "",
+ " /* First move last chars to start of buffer. */",
+ " number_to_move = yy_c_buf_p - yytext_ptr;",
+ "",
+ " for ( i = 0; i < number_to_move; ++i )",
+ " *(dest++) = *(source++);",
+ "",
+ " if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )",
+ " /* don't do the read, it's not guaranteed to return an EOF,",
+ " * just force an EOF",
+ " */",
+ " yy_n_chars = 0;",
+ "",
+ " else",
+ " {",
+ " int num_to_read =",
+ " yy_current_buffer->yy_buf_size - number_to_move - 1;",
+ "",
+ " while ( num_to_read <= 0 )",
+ " { /* Not enough room in the buffer - grow it. */",
+ "#ifdef YY_USES_REJECT",
+ " YY_FATAL_ERROR(",
+ "\"input buffer overflow, can't enlarge buffer because scanner uses REJECT\" );",
+ "#else",
+ "",
+ " /* just a shorter name for the current buffer */",
+ " YY_BUFFER_STATE b = yy_current_buffer;",
+ "",
+ " int yy_c_buf_p_offset = yy_c_buf_p - b->yy_ch_buf;",
+ "",
+ " b->yy_buf_size *= 2;",
+ " b->yy_ch_buf = (char *)",
+ " yy_flex_realloc( (void *) b->yy_ch_buf,",
+ " b->yy_buf_size );",
+ "",
+ " if ( ! b->yy_ch_buf )",
+ " YY_FATAL_ERROR(",
+ " \"fatal error - scanner input buffer overflow\" );",
+ "",
+ " yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];",
+ "",
+ " num_to_read = yy_current_buffer->yy_buf_size -",
+ " number_to_move - 1;",
+ "#endif",
+ " }",
+ "",
+ " if ( num_to_read > YY_READ_BUF_SIZE )",
+ " num_to_read = YY_READ_BUF_SIZE;",
+ "",
+ " /* Read in more data. */",
+ " YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),",
+ " yy_n_chars, num_to_read );",
+ " }",
+ "",
+ " if ( yy_n_chars == 0 )",
+ " {",
+ " if ( number_to_move - YY_MORE_ADJ == 1 )",
+ " {",
+ " ret_val = EOB_ACT_END_OF_FILE;",
+ " yyrestart( yyin );",
+ " }",
+ "",
+ " else",
+ " {",
+ " ret_val = EOB_ACT_LAST_MATCH;",
+ " yy_current_buffer->yy_buffer_status =",
+ " YY_BUFFER_EOF_PENDING;",
+ " }",
+ " }",
+ "",
+ " else",
+ " ret_val = EOB_ACT_CONTINUE_SCAN;",
+ "",
+ " yy_n_chars += number_to_move;",
+ " yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;",
+ " yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;",
+ "",
+ " /* yytext begins at the second character in yy_ch_buf; the first",
+ " * character is the one which preceded it before reading in the latest",
+ " * buffer; it needs to be kept around in case it's a newline, so",
+ " * yy_get_previous_state() will have with '^' rules active.",
+ " */",
+ "",
+ " yytext_ptr = &yy_current_buffer->yy_ch_buf[1];",
+ "",
+ " return ret_val;",
+ " }",
+ "",
+ "",
+ "/* yy_get_previous_state - get the state just before the EOB char was reached */",
+ "",
+ "%-",
+ "static yy_state_type yy_get_previous_state()",
+ "%+",
+ "yy_state_type yyFlexLexer::yy_get_previous_state()",
+ "%*",
+ " {",
+ " register yy_state_type yy_current_state;",
+ " register char *yy_cp;",
+ "",
+ "%% code to get the start state into yy_current_state goes here",
+ "",
+ " for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )",
+ " {",
+ "%% code to find the next state goes here",
+ " }",
+ "",
+ " return yy_current_state;",
+ " }",
+ "",
+ "",
+ "/* yy_try_NUL_trans - try to make a transition on the NUL character",
+ " *",
+ " * synopsis",
+ " * next_state = yy_try_NUL_trans( current_state );",
+ " */",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )",
+ "#else",
+ "static yy_state_type yy_try_NUL_trans( yy_current_state )",
+ "yy_state_type yy_current_state;",
+ "#endif",
+ "%+",
+ "yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state )",
+ "%*",
+ " {",
+ " register int yy_is_jam;",
+ "%% code to find the next state, and perhaps do backing up, goes here",
+ "",
+ " return yy_is_jam ? 0 : yy_current_state;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "static void yyunput( int c, register char *yy_bp )",
+ "#else",
+ "static void yyunput( c, yy_bp )",
+ "int c;",
+ "register char *yy_bp;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yyunput( int c, register char* yy_bp )",
+ "%*",
+ " {",
+ " register char *yy_cp = yy_c_buf_p;",
+ "",
+ " /* undo effects of setting up yytext */",
+ " *yy_cp = yy_hold_char;",
+ "",
+ " if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )",
+ " { /* need to shift things up to make room */",
+ " /* +2 for EOB chars. */",
+ " register int number_to_move = yy_n_chars + 2;",
+ " register char *dest = &yy_current_buffer->yy_ch_buf[",
+ " yy_current_buffer->yy_buf_size + 2];",
+ " register char *source =",
+ " &yy_current_buffer->yy_ch_buf[number_to_move];",
+ "",
+ " while ( source > yy_current_buffer->yy_ch_buf )",
+ " *--dest = *--source;",
+ "",
+ " yy_cp += dest - source;",
+ " yy_bp += dest - source;",
+ " yy_n_chars = yy_current_buffer->yy_buf_size;",
+ "",
+ " if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )",
+ " YY_FATAL_ERROR( \"flex scanner push-back overflow\" );",
+ " }",
+ "",
+ " if ( yy_cp > yy_bp && yy_cp[-1] == '\\n' )",
+ " yy_cp[-2] = '\\n';",
+ "",
+ " *--yy_cp = (char) c;",
+ "",
+ "%% update yylineno here, if doing -l",
+ "",
+ " /* Note: the formal parameter *must* be called \"yy_bp\" for this",
+ " * macro to now work correctly.",
+ " */",
+ " YY_DO_BEFORE_ACTION; /* set up yytext again */",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef __cplusplus",
+ "static int yyinput()",
+ "#else",
+ "static int input()",
+ "#endif",
+ "%+",
+ "int yyFlexLexer::yyinput()",
+ "%*",
+ " {",
+ " int c;",
+ "",
+ " *yy_c_buf_p = yy_hold_char;",
+ "",
+ " if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )",
+ " {",
+ " /* yy_c_buf_p now points to the character we want to return.",
+ " * If this occurs *before* the EOB characters, then it's a",
+ " * valid NUL; if not, then we've hit the end of the buffer.",
+ " */",
+ " if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )",
+ " /* This was really a NUL. */",
+ " *yy_c_buf_p = '\\0';",
+ "",
+ " else",
+ " { /* need more input */",
+ " yytext_ptr = yy_c_buf_p;",
+ " ++yy_c_buf_p;",
+ "",
+ " switch ( yy_get_next_buffer() )",
+ " {",
+ " case EOB_ACT_END_OF_FILE:",
+ " {",
+ " if ( yywrap() )",
+ " {",
+ " yy_c_buf_p =",
+ " yytext_ptr + YY_MORE_ADJ;",
+ " return EOF;",
+ " }",
+ "",
+ " YY_NEW_FILE;",
+ "#ifdef __cplusplus",
+ " return yyinput();",
+ "#else",
+ " return input();",
+ "#endif",
+ " }",
+ "",
+ " case EOB_ACT_CONTINUE_SCAN:",
+ " yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;",
+ " break;",
+ "",
+ " case EOB_ACT_LAST_MATCH:",
+ "#ifdef __cplusplus",
+ " YY_FATAL_ERROR(",
+ " \"unexpected last match in yyinput()\" );",
+ "#else",
+ " YY_FATAL_ERROR(",
+ " \"unexpected last match in input()\" );",
+ "#endif",
+ " }",
+ " }",
+ " }",
+ "",
+ " c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */",
+ " *yy_c_buf_p = '\\0'; /* preserve yytext */",
+ " yy_hold_char = *++yy_c_buf_p;",
+ "",
+ " return c;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yyrestart( FILE *input_file )",
+ "#else",
+ "void yyrestart( input_file )",
+ "FILE *input_file;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yyrestart( istream* input_file )",
+ "%*",
+ " {",
+ " if ( ! yy_current_buffer )",
+ " yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );",
+ "",
+ " yy_init_buffer( yy_current_buffer, input_file );",
+ " yy_load_buffer_state();",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )",
+ "#else",
+ "void yy_switch_to_buffer( new_buffer )",
+ "YY_BUFFER_STATE new_buffer;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )",
+ "%*",
+ " {",
+ " if ( yy_current_buffer == new_buffer )",
+ " return;",
+ "",
+ " if ( yy_current_buffer )",
+ " {",
+ " /* Flush out information for old buffer. */",
+ " *yy_c_buf_p = yy_hold_char;",
+ " yy_current_buffer->yy_buf_pos = yy_c_buf_p;",
+ " yy_current_buffer->yy_n_chars = yy_n_chars;",
+ " }",
+ "",
+ " yy_current_buffer = new_buffer;",
+ " yy_load_buffer_state();",
+ "",
+ " /* We don't actually know whether we did this switch during",
+ " * EOF (yywrap()) processing, but the only time this flag",
+ " * is looked at is after yywrap() is called, so it's safe",
+ " * to go ahead and always set it.",
+ " */",
+ " yy_did_buffer_switch_on_eof = 1;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yy_load_buffer_state( void )",
+ "#else",
+ "void yy_load_buffer_state()",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yy_load_buffer_state()",
+ "%*",
+ " {",
+ " yy_n_chars = yy_current_buffer->yy_n_chars;",
+ " yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;",
+ " yyin = yy_current_buffer->yy_input_file;",
+ " yy_hold_char = *yy_c_buf_p;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )",
+ "#else",
+ "YY_BUFFER_STATE yy_create_buffer( file, size )",
+ "FILE *file;",
+ "int size;",
+ "#endif",
+ "%+",
+ "YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( istream* file, int size )",
+ "%*",
+ " {",
+ " YY_BUFFER_STATE b;",
+ "",
+ " b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );",
+ "",
+ " if ( ! b )",
+ " YY_FATAL_ERROR( \"out of dynamic memory in yy_create_buffer()\" );",
+ "",
+ " b->yy_buf_size = size;",
+ "",
+ " /* yy_ch_buf has to be 2 characters longer than the size given because",
+ " * we need to put in 2 end-of-buffer characters.",
+ " */",
+ " b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );",
+ "",
+ " if ( ! b->yy_ch_buf )",
+ " YY_FATAL_ERROR( \"out of dynamic memory in yy_create_buffer()\" );",
+ "",
+ " yy_init_buffer( b, file );",
+ "",
+ " return b;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yy_delete_buffer( YY_BUFFER_STATE b )",
+ "#else",
+ "void yy_delete_buffer( b )",
+ "YY_BUFFER_STATE b;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b )",
+ "%*",
+ " {",
+ " if ( b == yy_current_buffer )",
+ " yy_current_buffer = (YY_BUFFER_STATE) 0;",
+ "",
+ " yy_flex_free( (void *) b->yy_ch_buf );",
+ " yy_flex_free( (void *) b );",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )",
+ "#else",
+ "void yy_init_buffer( b, file )",
+ "YY_BUFFER_STATE b;",
+ "FILE *file;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file )",
+ "%*",
+ " {",
+ " b->yy_input_file = file;",
+ "",
+ " /* We put in the '\\n' and start reading from [1] so that an",
+ " * initial match-at-newline will be true.",
+ " */",
+ "",
+ " b->yy_ch_buf[0] = '\\n';",
+ " b->yy_n_chars = 1;",
+ "",
+ " /* We always need two end-of-buffer characters. The first causes",
+ " * a transition to the end-of-buffer state. The second causes",
+ " * a jam in that state.",
+ " */",
+ " b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;",
+ " b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;",
+ "",
+ " b->yy_buf_pos = &b->yy_ch_buf[1];",
+ "",
+ "%-",
+ " b->yy_is_interactive = file ? isatty( fileno(file) ) : 0;",
+ "%+",
+ " b->yy_is_interactive = 0;",
+ "%*",
+ "",
+ " b->yy_fill_buffer = 1;",
+ "",
+ " b->yy_buffer_status = YY_BUFFER_NEW;",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "static void yy_push_state( int new_state )",
+ "#else",
+ "static void yy_push_state( new_state )",
+ "int new_state;",
+ "#endif",
+ "%+",
+ "void yyFlexLexer::yy_push_state( int new_state )",
+ "%*",
+ " {",
+ " if ( yy_start_stack_ptr >= yy_start_stack_depth )",
+ " {",
+ " int new_size;",
+ "",
+ " yy_start_stack_depth += YY_START_STACK_INCR;",
+ " new_size = yy_start_stack_depth * sizeof( int );",
+ "",
+ " if ( ! yy_start_stack )",
+ " yy_start_stack = (int *) yy_flex_alloc( new_size );",
+ "",
+ " else",
+ " yy_start_stack = (int *) yy_flex_realloc(",
+ " (void *) yy_start_stack, new_size );",
+ "",
+ " if ( ! yy_start_stack )",
+ " YY_FATAL_ERROR(",
+ " \"out of memory expanding start-condition stack\" );",
+ " }",
+ "",
+ " yy_start_stack[yy_start_stack_ptr++] = YY_START;",
+ "",
+ " BEGIN(new_state);",
+ " }",
+ "",
+ "",
+ "%-",
+ "static void yy_pop_state()",
+ "%+",
+ "void yyFlexLexer::yy_pop_state()",
+ "%*",
+ " {",
+ " if ( --yy_start_stack_ptr < 0 )",
+ " YY_FATAL_ERROR( \"start-condition stack underflow\" );",
+ "",
+ " BEGIN(yy_start_stack[yy_start_stack_ptr]);",
+ " }",
+ "",
+ "",
+ "%-",
+ "static int yy_top_state()",
+ "%+",
+ "int yyFlexLexer::yy_top_state()",
+ "%*",
+ " {",
+ " return yy_start_stack[yy_start_stack_ptr - 1];",
+ " }",
+ "",
+ "",
+ "%-",
+ "#ifdef YY_USE_PROTOS",
+ "static void yy_fatal_error( const char msg[] )",
+ "#else",
+ "static void yy_fatal_error( msg )",
+ "char msg[];",
+ "#endif",
+ " {",
+ " (void) fprintf( stderr, \"%s\\n\", msg );",
+ " exit( 1 );",
+ " }",
+ "",
+ "%+",
+ "",
+ "void yyFlexLexer::LexerError( const char msg[] )",
+ " {",
+ " cerr << msg << '\\n';",
+ " exit( 1 );",
+ " }",
+ "%*",
+ "",
+ "",
+ "/* Redefine yyless() so it works in section 3 code. */",
+ "",
+ "#undef yyless",
+ "#define yyless(n) \\",
+ " do \\",
+ " { \\",
+ " /* Undo effects of setting up yytext. */ \\",
+ " yytext[yyleng] = yy_hold_char; \\",
+ " yy_c_buf_p = yytext + n - YY_MORE_ADJ; \\",
+ " yy_hold_char = *yy_c_buf_p; \\",
+ " *yy_c_buf_p = '\\0'; \\",
+ " yyleng = n; \\",
+ " } \\",
+ " while ( 0 )",
+ "",
+ "",
+ "/* Internal utility routines. */",
+ "",
+ "#ifndef yytext_ptr",
+ "#ifdef YY_USE_PROTOS",
+ "static void yy_flex_strncpy( char *s1, const char *s2, int n )",
+ "#else",
+ "static void yy_flex_strncpy( s1, s2, n )",
+ "char *s1;",
+ "const char *s2;",
+ "int n;",
+ "#endif",
+ " {",
+ " register int i;",
+ " for ( i = 0; i < n; ++i )",
+ " s1[i] = s2[i];",
+ " }",
+ "#endif",
+ "",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "static void *yy_flex_alloc( unsigned int size )",
+ "#else",
+ "static void *yy_flex_alloc( size )",
+ "unsigned int size;",
+ "#endif",
+ " {",
+ " return (void *) malloc( size );",
+ " }",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "static void *yy_flex_realloc( void *ptr, unsigned int size )",
+ "#else",
+ "static void *yy_flex_realloc( ptr, size )",
+ "void *ptr;",
+ "unsigned int size;",
+ "#endif",
+ " {",
+ " return (void *) realloc( ptr, size );",
+ " }",
+ "",
+ "#ifdef YY_USE_PROTOS",
+ "static void yy_flex_free( void *ptr )",
+ "#else",
+ "static void yy_flex_free( ptr )",
+ "void *ptr;",
+ "#endif",
+ " {",
+ " free( ptr );",
+ " }",
+ 0
+};
diff --git a/usr.bin/lex/sym.c b/usr.bin/lex/sym.c
new file mode 100644
index 0000000..3a55b68
--- /dev/null
+++ b/usr.bin/lex/sym.c
@@ -0,0 +1,262 @@
+/* sym - symbol table routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/sym.c,v 1.1.1.1 1994/08/24 13:10:31 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+/* declare functions that have forward references */
+
+int hashfunct PROTO((register char[], int));
+
+
+struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE];
+struct hash_entry *sctbl[START_COND_HASH_SIZE];
+struct hash_entry *ccltab[CCL_HASH_SIZE];
+
+struct hash_entry *findsym();
+
+
+/* addsym - add symbol and definitions to symbol table
+ *
+ * -1 is returned if the symbol already exists, and the change not made.
+ */
+
+int addsym( sym, str_def, int_def, table, table_size )
+register char sym[];
+char *str_def;
+int int_def;
+hash_table table;
+int table_size;
+ {
+ int hash_val = hashfunct( sym, table_size );
+ register struct hash_entry *sym_entry = table[hash_val];
+ register struct hash_entry *new_entry;
+ register struct hash_entry *successor;
+
+ while ( sym_entry )
+ {
+ if ( ! strcmp( sym, sym_entry->name ) )
+ { /* entry already exists */
+ return -1;
+ }
+
+ sym_entry = sym_entry->next;
+ }
+
+ /* create new entry */
+ new_entry = (struct hash_entry *)
+ flex_alloc( sizeof( struct hash_entry ) );
+
+ if ( new_entry == NULL )
+ flexfatal( "symbol table memory allocation failed" );
+
+ if ( (successor = table[hash_val]) )
+ {
+ new_entry->next = successor;
+ successor->prev = new_entry;
+ }
+ else
+ new_entry->next = NULL;
+
+ new_entry->prev = NULL;
+ new_entry->name = sym;
+ new_entry->str_val = str_def;
+ new_entry->int_val = int_def;
+
+ table[hash_val] = new_entry;
+
+ return 0;
+ }
+
+
+/* cclinstal - save the text of a character class */
+
+void cclinstal( ccltxt, cclnum )
+Char ccltxt[];
+int cclnum;
+ {
+ /* We don't bother checking the return status because we are not
+ * called unless the symbol is new.
+ */
+ Char *copy_unsigned_string();
+
+ (void) addsym( (char *) copy_unsigned_string( ccltxt ),
+ (char *) 0, cclnum,
+ ccltab, CCL_HASH_SIZE );
+ }
+
+
+/* ccllookup - lookup the number associated with character class text
+ *
+ * Returns 0 if there's no CCL associated with the text.
+ */
+
+int ccllookup( ccltxt )
+Char ccltxt[];
+ {
+ return findsym( (char *) ccltxt, ccltab, CCL_HASH_SIZE )->int_val;
+ }
+
+
+/* findsym - find symbol in symbol table */
+
+struct hash_entry *findsym( sym, table, table_size )
+register char sym[];
+hash_table table;
+int table_size;
+ {
+ static struct hash_entry empty_entry =
+ {
+ (struct hash_entry *) 0, (struct hash_entry *) 0,
+ (char *) 0, (char *) 0, 0,
+ } ;
+ register struct hash_entry *sym_entry =
+ table[hashfunct( sym, table_size )];
+
+ while ( sym_entry )
+ {
+ if ( ! strcmp( sym, sym_entry->name ) )
+ return sym_entry;
+ sym_entry = sym_entry->next;
+ }
+
+ return &empty_entry;
+ }
+
+
+/* hashfunct - compute the hash value for "str" and hash size "hash_size" */
+
+int hashfunct( str, hash_size )
+register char str[];
+int hash_size;
+ {
+ register int hashval;
+ register int locstr;
+
+ hashval = 0;
+ locstr = 0;
+
+ while ( str[locstr] )
+ {
+ hashval = (hashval << 1) + (unsigned char) str[locstr++];
+ hashval %= hash_size;
+ }
+
+ return hashval;
+ }
+
+
+/* ndinstal - install a name definition */
+
+void ndinstal( name, definition )
+char name[];
+Char definition[];
+ {
+ char *copy_string();
+ Char *copy_unsigned_string();
+
+ if ( addsym( copy_string( name ),
+ (char *) copy_unsigned_string( definition ), 0,
+ ndtbl, NAME_TABLE_HASH_SIZE ) )
+ synerr( "name defined twice" );
+ }
+
+
+/* ndlookup - lookup a name definition
+ *
+ * Returns a nil pointer if the name definition does not exist.
+ */
+
+Char *ndlookup( nd )
+char nd[];
+ {
+ return (Char *) findsym( nd, ndtbl, NAME_TABLE_HASH_SIZE )->str_val;
+ }
+
+
+/* scextend - increase the maximum number of start conditions */
+
+void scextend()
+ {
+ current_max_scs += MAX_SCS_INCREMENT;
+
+ ++num_reallocs;
+
+ scset = reallocate_integer_array( scset, current_max_scs );
+ scbol = reallocate_integer_array( scbol, current_max_scs );
+ scxclu = reallocate_integer_array( scxclu, current_max_scs );
+ sceof = reallocate_integer_array( sceof, current_max_scs );
+ scname = reallocate_char_ptr_array( scname, current_max_scs );
+ actvsc = reallocate_integer_array( actvsc, current_max_scs );
+ }
+
+
+/* scinstal - make a start condition
+ *
+ * NOTE
+ * The start condition is "exclusive" if xcluflg is true.
+ */
+
+void scinstal( str, xcluflg )
+char str[];
+int xcluflg;
+ {
+ char *copy_string();
+
+ /* Generate start condition definition, for use in BEGIN et al. */
+ printf( "#define %s %d\n", str, lastsc );
+
+ if ( ++lastsc >= current_max_scs )
+ scextend();
+
+ scname[lastsc] = copy_string( str );
+
+ if ( addsym( scname[lastsc], (char *) 0, lastsc,
+ sctbl, START_COND_HASH_SIZE ) )
+ format_pinpoint_message( "start condition %s declared twice",
+ str );
+
+ scset[lastsc] = mkstate( SYM_EPSILON );
+ scbol[lastsc] = mkstate( SYM_EPSILON );
+ scxclu[lastsc] = xcluflg;
+ sceof[lastsc] = false;
+ }
+
+
+/* sclookup - lookup the number associated with a start condition
+ *
+ * Returns 0 if no such start condition.
+ */
+
+int sclookup( str )
+char str[];
+ {
+ return findsym( str, sctbl, START_COND_HASH_SIZE )->int_val;
+ }
diff --git a/usr.bin/lex/tblcmp.c b/usr.bin/lex/tblcmp.c
new file mode 100644
index 0000000..655717b
--- /dev/null
+++ b/usr.bin/lex/tblcmp.c
@@ -0,0 +1,888 @@
+/* tblcmp - table compression routines */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/tblcmp.c,v 1.1.1.1 1994/08/24 13:10:31 csgr Exp $ */
+
+#include "flexdef.h"
+
+
+/* declarations for functions that have forward references */
+
+void mkentry PROTO((register int*, int, int, int, int));
+void mkprot PROTO((int[], int, int));
+void mktemplate PROTO((int[], int, int));
+void mv2front PROTO((int));
+int tbldiff PROTO((int[], int, int[]));
+
+
+/* bldtbl - build table entries for dfa state
+ *
+ * synopsis
+ * int state[numecs], statenum, totaltrans, comstate, comfreq;
+ * bldtbl( state, statenum, totaltrans, comstate, comfreq );
+ *
+ * State is the statenum'th dfa state. It is indexed by equivalence class and
+ * gives the number of the state to enter for a given equivalence class.
+ * totaltrans is the total number of transitions out of the state. Comstate
+ * is that state which is the destination of the most transitions out of State.
+ * Comfreq is how many transitions there are out of State to Comstate.
+ *
+ * A note on terminology:
+ * "protos" are transition tables which have a high probability of
+ * either being redundant (a state processed later will have an identical
+ * transition table) or nearly redundant (a state processed later will have
+ * many of the same out-transitions). A "most recently used" queue of
+ * protos is kept around with the hope that most states will find a proto
+ * which is similar enough to be usable, and therefore compacting the
+ * output tables.
+ * "templates" are a special type of proto. If a transition table is
+ * homogeneous or nearly homogeneous (all transitions go to the same
+ * destination) then the odds are good that future states will also go
+ * to the same destination state on basically the same character set.
+ * These homogeneous states are so common when dealing with large rule
+ * sets that they merit special attention. If the transition table were
+ * simply made into a proto, then (typically) each subsequent, similar
+ * state will differ from the proto for two out-transitions. One of these
+ * out-transitions will be that character on which the proto does not go
+ * to the common destination, and one will be that character on which the
+ * state does not go to the common destination. Templates, on the other
+ * hand, go to the common state on EVERY transition character, and therefore
+ * cost only one difference.
+ */
+
+void bldtbl( state, statenum, totaltrans, comstate, comfreq )
+int state[], statenum, totaltrans, comstate, comfreq;
+ {
+ int extptr, extrct[2][CSIZE + 1];
+ int mindiff, minprot, i, d;
+
+ /* If extptr is 0 then the first array of extrct holds the result
+ * of the "best difference" to date, which is those transitions
+ * which occur in "state" but not in the proto which, to date,
+ * has the fewest differences between itself and "state". If
+ * extptr is 1 then the second array of extrct hold the best
+ * difference. The two arrays are toggled between so that the
+ * best difference to date can be kept around and also a difference
+ * just created by checking against a candidate "best" proto.
+ */
+
+ extptr = 0;
+
+ /* If the state has too few out-transitions, don't bother trying to
+ * compact its tables.
+ */
+
+ if ( (totaltrans * 100) < (numecs * PROTO_SIZE_PERCENTAGE) )
+ mkentry( state, numecs, statenum, JAMSTATE, totaltrans );
+
+ else
+ {
+ /* "checkcom" is true if we should only check "state" against
+ * protos which have the same "comstate" value.
+ */
+ int checkcom =
+ comfreq * 100 > totaltrans * CHECK_COM_PERCENTAGE;
+
+ minprot = firstprot;
+ mindiff = totaltrans;
+
+ if ( checkcom )
+ {
+ /* Find first proto which has the same "comstate". */
+ for ( i = firstprot; i != NIL; i = protnext[i] )
+ if ( protcomst[i] == comstate )
+ {
+ minprot = i;
+ mindiff = tbldiff( state, minprot,
+ extrct[extptr] );
+ break;
+ }
+ }
+
+ else
+ {
+ /* Since we've decided that the most common destination
+ * out of "state" does not occur with a high enough
+ * frequency, we set the "comstate" to zero, assuring
+ * that if this state is entered into the proto list,
+ * it will not be considered a template.
+ */
+ comstate = 0;
+
+ if ( firstprot != NIL )
+ {
+ minprot = firstprot;
+ mindiff = tbldiff( state, minprot,
+ extrct[extptr] );
+ }
+ }
+
+ /* We now have the first interesting proto in "minprot". If
+ * it matches within the tolerances set for the first proto,
+ * we don't want to bother scanning the rest of the proto list
+ * to see if we have any other reasonable matches.
+ */
+
+ if ( mindiff * 100 > totaltrans * FIRST_MATCH_DIFF_PERCENTAGE )
+ {
+ /* Not a good enough match. Scan the rest of the
+ * protos.
+ */
+ for ( i = minprot; i != NIL; i = protnext[i] )
+ {
+ d = tbldiff( state, i, extrct[1 - extptr] );
+ if ( d < mindiff )
+ {
+ extptr = 1 - extptr;
+ mindiff = d;
+ minprot = i;
+ }
+ }
+ }
+
+ /* Check if the proto we've decided on as our best bet is close
+ * enough to the state we want to match to be usable.
+ */
+
+ if ( mindiff * 100 > totaltrans * ACCEPTABLE_DIFF_PERCENTAGE )
+ {
+ /* No good. If the state is homogeneous enough,
+ * we make a template out of it. Otherwise, we
+ * make a proto.
+ */
+
+ if ( comfreq * 100 >=
+ totaltrans * TEMPLATE_SAME_PERCENTAGE )
+ mktemplate( state, statenum, comstate );
+
+ else
+ {
+ mkprot( state, statenum, comstate );
+ mkentry( state, numecs, statenum,
+ JAMSTATE, totaltrans );
+ }
+ }
+
+ else
+ { /* use the proto */
+ mkentry( extrct[extptr], numecs, statenum,
+ prottbl[minprot], mindiff );
+
+ /* If this state was sufficiently different from the
+ * proto we built it from, make it, too, a proto.
+ */
+
+ if ( mindiff * 100 >=
+ totaltrans * NEW_PROTO_DIFF_PERCENTAGE )
+ mkprot( state, statenum, comstate );
+
+ /* Since mkprot added a new proto to the proto queue,
+ * it's possible that "minprot" is no longer on the
+ * proto queue (if it happened to have been the last
+ * entry, it would have been bumped off). If it's
+ * not there, then the new proto took its physical
+ * place (though logically the new proto is at the
+ * beginning of the queue), so in that case the
+ * following call will do nothing.
+ */
+
+ mv2front( minprot );
+ }
+ }
+ }
+
+
+/* cmptmps - compress template table entries
+ *
+ * Template tables are compressed by using the 'template equivalence
+ * classes', which are collections of transition character equivalence
+ * classes which always appear together in templates - really meta-equivalence
+ * classes.
+ */
+
+void cmptmps()
+ {
+ int tmpstorage[CSIZE + 1];
+ register int *tmp = tmpstorage, i, j;
+ int totaltrans, trans;
+
+ peakpairs = numtemps * numecs + tblend;
+
+ if ( usemecs )
+ {
+ /* Create equivalence classes based on data gathered on
+ * template transitions.
+ */
+ nummecs = cre8ecs( tecfwd, tecbck, numecs );
+ }
+
+ else
+ nummecs = numecs;
+
+ while ( lastdfa + numtemps + 1 >= current_max_dfas )
+ increase_max_dfas();
+
+ /* Loop through each template. */
+
+ for ( i = 1; i <= numtemps; ++i )
+ {
+ /* Number of non-jam transitions out of this template. */
+ totaltrans = 0;
+
+ for ( j = 1; j <= numecs; ++j )
+ {
+ trans = tnxt[numecs * i + j];
+
+ if ( usemecs )
+ {
+ /* The absolute value of tecbck is the
+ * meta-equivalence class of a given
+ * equivalence class, as set up by cre8ecs().
+ */
+ if ( tecbck[j] > 0 )
+ {
+ tmp[tecbck[j]] = trans;
+
+ if ( trans > 0 )
+ ++totaltrans;
+ }
+ }
+
+ else
+ {
+ tmp[j] = trans;
+
+ if ( trans > 0 )
+ ++totaltrans;
+ }
+ }
+
+ /* It is assumed (in a rather subtle way) in the skeleton
+ * that if we're using meta-equivalence classes, the def[]
+ * entry for all templates is the jam template, i.e.,
+ * templates never default to other non-jam table entries
+ * (e.g., another template)
+ */
+
+ /* Leave room for the jam-state after the last real state. */
+ mkentry( tmp, nummecs, lastdfa + i + 1, JAMSTATE, totaltrans );
+ }
+ }
+
+
+
+/* expand_nxt_chk - expand the next check arrays */
+
+void expand_nxt_chk()
+ {
+ register int old_max = current_max_xpairs;
+
+ current_max_xpairs += MAX_XPAIRS_INCREMENT;
+
+ ++num_reallocs;
+
+ nxt = reallocate_integer_array( nxt, current_max_xpairs );
+ chk = reallocate_integer_array( chk, current_max_xpairs );
+
+ zero_out( (char *) (chk + old_max),
+ MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) );
+ }
+
+
+/* find_table_space - finds a space in the table for a state to be placed
+ *
+ * synopsis
+ * int *state, numtrans, block_start;
+ * int find_table_space();
+ *
+ * block_start = find_table_space( state, numtrans );
+ *
+ * State is the state to be added to the full speed transition table.
+ * Numtrans is the number of out-transitions for the state.
+ *
+ * find_table_space() returns the position of the start of the first block (in
+ * chk) able to accommodate the state
+ *
+ * In determining if a state will or will not fit, find_table_space() must take
+ * into account the fact that an end-of-buffer state will be added at [0],
+ * and an action number will be added in [-1].
+ */
+
+int find_table_space( state, numtrans )
+int *state, numtrans;
+ {
+ /* Firstfree is the position of the first possible occurrence of two
+ * consecutive unused records in the chk and nxt arrays.
+ */
+ register int i;
+ register int *state_ptr, *chk_ptr;
+ register int *ptr_to_last_entry_in_state;
+
+ /* If there are too many out-transitions, put the state at the end of
+ * nxt and chk.
+ */
+ if ( numtrans > MAX_XTIONS_FULL_INTERIOR_FIT )
+ {
+ /* If table is empty, return the first available spot in
+ * chk/nxt, which should be 1.
+ */
+ if ( tblend < 2 )
+ return 1;
+
+ /* Start searching for table space near the end of
+ * chk/nxt arrays.
+ */
+ i = tblend - numecs;
+ }
+
+ else
+ /* Start searching for table space from the beginning
+ * (skipping only the elements which will definitely not
+ * hold the new state).
+ */
+ i = firstfree;
+
+ while ( 1 ) /* loops until a space is found */
+ {
+ while ( i + numecs >= current_max_xpairs )
+ expand_nxt_chk();
+
+ /* Loops until space for end-of-buffer and action number
+ * are found.
+ */
+ while ( 1 )
+ {
+ /* Check for action number space. */
+ if ( chk[i - 1] == 0 )
+ {
+ /* Check for end-of-buffer space. */
+ if ( chk[i] == 0 )
+ break;
+
+ else
+ /* Since i != 0, there is no use
+ * checking to see if (++i) - 1 == 0,
+ * because that's the same as i == 0,
+ * so we skip a space.
+ */
+ i += 2;
+ }
+
+ else
+ ++i;
+
+ while ( i + numecs >= current_max_xpairs )
+ expand_nxt_chk();
+ }
+
+ /* If we started search from the beginning, store the new
+ * firstfree for the next call of find_table_space().
+ */
+ if ( numtrans <= MAX_XTIONS_FULL_INTERIOR_FIT )
+ firstfree = i + 1;
+
+ /* Check to see if all elements in chk (and therefore nxt)
+ * that are needed for the new state have not yet been taken.
+ */
+
+ state_ptr = &state[1];
+ ptr_to_last_entry_in_state = &chk[i + numecs + 1];
+
+ for ( chk_ptr = &chk[i + 1];
+ chk_ptr != ptr_to_last_entry_in_state; ++chk_ptr )
+ if ( *(state_ptr++) != 0 && *chk_ptr != 0 )
+ break;
+
+ if ( chk_ptr == ptr_to_last_entry_in_state )
+ return i;
+
+ else
+ ++i;
+ }
+ }
+
+
+/* inittbl - initialize transition tables
+ *
+ * Initializes "firstfree" to be one beyond the end of the table. Initializes
+ * all "chk" entries to be zero.
+ */
+void inittbl()
+ {
+ register int i;
+
+ zero_out( (char *) chk,
+ current_max_xpairs * sizeof( int ) / sizeof( char ) );
+
+ tblend = 0;
+ firstfree = tblend + 1;
+ numtemps = 0;
+
+ if ( usemecs )
+ {
+ /* Set up doubly-linked meta-equivalence classes; these
+ * are sets of equivalence classes which all have identical
+ * transitions out of TEMPLATES.
+ */
+
+ tecbck[1] = NIL;
+
+ for ( i = 2; i <= numecs; ++i )
+ {
+ tecbck[i] = i - 1;
+ tecfwd[i - 1] = i;
+ }
+
+ tecfwd[numecs] = NIL;
+ }
+ }
+
+
+/* mkdeftbl - make the default, "jam" table entries */
+
+void mkdeftbl()
+ {
+ int i;
+
+ jamstate = lastdfa + 1;
+
+ ++tblend; /* room for transition on end-of-buffer character */
+
+ while ( tblend + numecs >= current_max_xpairs )
+ expand_nxt_chk();
+
+ /* Add in default end-of-buffer transition. */
+ nxt[tblend] = end_of_buffer_state;
+ chk[tblend] = jamstate;
+
+ for ( i = 1; i <= numecs; ++i )
+ {
+ nxt[tblend + i] = 0;
+ chk[tblend + i] = jamstate;
+ }
+
+ jambase = tblend;
+
+ base[jamstate] = jambase;
+ def[jamstate] = 0;
+
+ tblend += numecs;
+ ++numtemps;
+ }
+
+
+/* mkentry - create base/def and nxt/chk entries for transition array
+ *
+ * synopsis
+ * int state[numchars + 1], numchars, statenum, deflink, totaltrans;
+ * mkentry( state, numchars, statenum, deflink, totaltrans );
+ *
+ * "state" is a transition array "numchars" characters in size, "statenum"
+ * is the offset to be used into the base/def tables, and "deflink" is the
+ * entry to put in the "def" table entry. If "deflink" is equal to
+ * "JAMSTATE", then no attempt will be made to fit zero entries of "state"
+ * (i.e., jam entries) into the table. It is assumed that by linking to
+ * "JAMSTATE" they will be taken care of. In any case, entries in "state"
+ * marking transitions to "SAME_TRANS" are treated as though they will be
+ * taken care of by whereever "deflink" points. "totaltrans" is the total
+ * number of transitions out of the state. If it is below a certain threshold,
+ * the tables are searched for an interior spot that will accommodate the
+ * state array.
+ */
+
+void mkentry( state, numchars, statenum, deflink, totaltrans )
+register int *state;
+int numchars, statenum, deflink, totaltrans;
+ {
+ register int minec, maxec, i, baseaddr;
+ int tblbase, tbllast;
+
+ if ( totaltrans == 0 )
+ { /* there are no out-transitions */
+ if ( deflink == JAMSTATE )
+ base[statenum] = JAMSTATE;
+ else
+ base[statenum] = 0;
+
+ def[statenum] = deflink;
+ return;
+ }
+
+ for ( minec = 1; minec <= numchars; ++minec )
+ {
+ if ( state[minec] != SAME_TRANS )
+ if ( state[minec] != 0 || deflink != JAMSTATE )
+ break;
+ }
+
+ if ( totaltrans == 1 )
+ {
+ /* There's only one out-transition. Save it for later to fill
+ * in holes in the tables.
+ */
+ stack1( statenum, minec, state[minec], deflink );
+ return;
+ }
+
+ for ( maxec = numchars; maxec > 0; --maxec )
+ {
+ if ( state[maxec] != SAME_TRANS )
+ if ( state[maxec] != 0 || deflink != JAMSTATE )
+ break;
+ }
+
+ /* Whether we try to fit the state table in the middle of the table
+ * entries we have already generated, or if we just take the state
+ * table at the end of the nxt/chk tables, we must make sure that we
+ * have a valid base address (i.e., non-negative). Note that
+ * negative base addresses dangerous at run-time (because indexing
+ * the nxt array with one and a low-valued character will access
+ * memory before the start of the array.
+ */
+
+ /* Find the first transition of state that we need to worry about. */
+ if ( totaltrans * 100 <= numchars * INTERIOR_FIT_PERCENTAGE )
+ {
+ /* Attempt to squeeze it into the middle of the tables. */
+ baseaddr = firstfree;
+
+ while ( baseaddr < minec )
+ {
+ /* Using baseaddr would result in a negative base
+ * address below; find the next free slot.
+ */
+ for ( ++baseaddr; chk[baseaddr] != 0; ++baseaddr )
+ ;
+ }
+
+ while ( baseaddr + maxec - minec + 1 >= current_max_xpairs )
+ expand_nxt_chk();
+
+ for ( i = minec; i <= maxec; ++i )
+ if ( state[i] != SAME_TRANS &&
+ (state[i] != 0 || deflink != JAMSTATE) &&
+ chk[baseaddr + i - minec] != 0 )
+ { /* baseaddr unsuitable - find another */
+ for ( ++baseaddr;
+ baseaddr < current_max_xpairs &&
+ chk[baseaddr] != 0; ++baseaddr )
+ ;
+
+ while ( baseaddr + maxec - minec + 1 >=
+ current_max_xpairs )
+ expand_nxt_chk();
+
+ /* Reset the loop counter so we'll start all
+ * over again next time it's incremented.
+ */
+
+ i = minec - 1;
+ }
+ }
+
+ else
+ {
+ /* Ensure that the base address we eventually generate is
+ * non-negative.
+ */
+ baseaddr = MAX( tblend + 1, minec );
+ }
+
+ tblbase = baseaddr - minec;
+ tbllast = tblbase + maxec;
+
+ while ( tbllast + 1 >= current_max_xpairs )
+ expand_nxt_chk();
+
+ base[statenum] = tblbase;
+ def[statenum] = deflink;
+
+ for ( i = minec; i <= maxec; ++i )
+ if ( state[i] != SAME_TRANS )
+ if ( state[i] != 0 || deflink != JAMSTATE )
+ {
+ nxt[tblbase + i] = state[i];
+ chk[tblbase + i] = statenum;
+ }
+
+ if ( baseaddr == firstfree )
+ /* Find next free slot in tables. */
+ for ( ++firstfree; chk[firstfree] != 0; ++firstfree )
+ ;
+
+ tblend = MAX( tblend, tbllast );
+ }
+
+
+/* mk1tbl - create table entries for a state (or state fragment) which
+ * has only one out-transition
+ */
+
+void mk1tbl( state, sym, onenxt, onedef )
+int state, sym, onenxt, onedef;
+ {
+ if ( firstfree < sym )
+ firstfree = sym;
+
+ while ( chk[firstfree] != 0 )
+ if ( ++firstfree >= current_max_xpairs )
+ expand_nxt_chk();
+
+ base[state] = firstfree - sym;
+ def[state] = onedef;
+ chk[firstfree] = state;
+ nxt[firstfree] = onenxt;
+
+ if ( firstfree > tblend )
+ {
+ tblend = firstfree++;
+
+ if ( firstfree >= current_max_xpairs )
+ expand_nxt_chk();
+ }
+ }
+
+
+/* mkprot - create new proto entry */
+
+void mkprot( state, statenum, comstate )
+int state[], statenum, comstate;
+ {
+ int i, slot, tblbase;
+
+ if ( ++numprots >= MSP || numecs * numprots >= PROT_SAVE_SIZE )
+ {
+ /* Gotta make room for the new proto by dropping last entry in
+ * the queue.
+ */
+ slot = lastprot;
+ lastprot = protprev[lastprot];
+ protnext[lastprot] = NIL;
+ }
+
+ else
+ slot = numprots;
+
+ protnext[slot] = firstprot;
+
+ if ( firstprot != NIL )
+ protprev[firstprot] = slot;
+
+ firstprot = slot;
+ prottbl[slot] = statenum;
+ protcomst[slot] = comstate;
+
+ /* Copy state into save area so it can be compared with rapidly. */
+ tblbase = numecs * (slot - 1);
+
+ for ( i = 1; i <= numecs; ++i )
+ protsave[tblbase + i] = state[i];
+ }
+
+
+/* mktemplate - create a template entry based on a state, and connect the state
+ * to it
+ */
+
+void mktemplate( state, statenum, comstate )
+int state[], statenum, comstate;
+ {
+ int i, numdiff, tmpbase, tmp[CSIZE + 1];
+ Char transset[CSIZE + 1];
+ int tsptr;
+
+ ++numtemps;
+
+ tsptr = 0;
+
+ /* Calculate where we will temporarily store the transition table
+ * of the template in the tnxt[] array. The final transition table
+ * gets created by cmptmps().
+ */
+
+ tmpbase = numtemps * numecs;
+
+ if ( tmpbase + numecs >= current_max_template_xpairs )
+ {
+ current_max_template_xpairs += MAX_TEMPLATE_XPAIRS_INCREMENT;
+
+ ++num_reallocs;
+
+ tnxt = reallocate_integer_array( tnxt,
+ current_max_template_xpairs );
+ }
+
+ for ( i = 1; i <= numecs; ++i )
+ if ( state[i] == 0 )
+ tnxt[tmpbase + i] = 0;
+ else
+ {
+ transset[tsptr++] = i;
+ tnxt[tmpbase + i] = comstate;
+ }
+
+ if ( usemecs )
+ mkeccl( transset, tsptr, tecfwd, tecbck, numecs, 0 );
+
+ mkprot( tnxt + tmpbase, -numtemps, comstate );
+
+ /* We rely on the fact that mkprot adds things to the beginning
+ * of the proto queue.
+ */
+
+ numdiff = tbldiff( state, firstprot, tmp );
+ mkentry( tmp, numecs, statenum, -numtemps, numdiff );
+ }
+
+
+/* mv2front - move proto queue element to front of queue */
+
+void mv2front( qelm )
+int qelm;
+ {
+ if ( firstprot != qelm )
+ {
+ if ( qelm == lastprot )
+ lastprot = protprev[lastprot];
+
+ protnext[protprev[qelm]] = protnext[qelm];
+
+ if ( protnext[qelm] != NIL )
+ protprev[protnext[qelm]] = protprev[qelm];
+
+ protprev[qelm] = NIL;
+ protnext[qelm] = firstprot;
+ protprev[firstprot] = qelm;
+ firstprot = qelm;
+ }
+ }
+
+
+/* place_state - place a state into full speed transition table
+ *
+ * State is the statenum'th state. It is indexed by equivalence class and
+ * gives the number of the state to enter for a given equivalence class.
+ * Transnum is the number of out-transitions for the state.
+ */
+
+void place_state( state, statenum, transnum )
+int *state, statenum, transnum;
+ {
+ register int i;
+ register int *state_ptr;
+ int position = find_table_space( state, transnum );
+
+ /* "base" is the table of start positions. */
+ base[statenum] = position;
+
+ /* Put in action number marker; this non-zero number makes sure that
+ * find_table_space() knows that this position in chk/nxt is taken
+ * and should not be used for another accepting number in another
+ * state.
+ */
+ chk[position - 1] = 1;
+
+ /* Put in end-of-buffer marker; this is for the same purposes as
+ * above.
+ */
+ chk[position] = 1;
+
+ /* Place the state into chk and nxt. */
+ state_ptr = &state[1];
+
+ for ( i = 1; i <= numecs; ++i, ++state_ptr )
+ if ( *state_ptr != 0 )
+ {
+ chk[position + i] = i;
+ nxt[position + i] = *state_ptr;
+ }
+
+ if ( position + numecs > tblend )
+ tblend = position + numecs;
+ }
+
+
+/* stack1 - save states with only one out-transition to be processed later
+ *
+ * If there's room for another state on the "one-transition" stack, the
+ * state is pushed onto it, to be processed later by mk1tbl. If there's
+ * no room, we process the sucker right now.
+ */
+
+void stack1( statenum, sym, nextstate, deflink )
+int statenum, sym, nextstate, deflink;
+ {
+ if ( onesp >= ONE_STACK_SIZE - 1 )
+ mk1tbl( statenum, sym, nextstate, deflink );
+
+ else
+ {
+ ++onesp;
+ onestate[onesp] = statenum;
+ onesym[onesp] = sym;
+ onenext[onesp] = nextstate;
+ onedef[onesp] = deflink;
+ }
+ }
+
+
+/* tbldiff - compute differences between two state tables
+ *
+ * "state" is the state array which is to be extracted from the pr'th
+ * proto. "pr" is both the number of the proto we are extracting from
+ * and an index into the save area where we can find the proto's complete
+ * state table. Each entry in "state" which differs from the corresponding
+ * entry of "pr" will appear in "ext".
+ *
+ * Entries which are the same in both "state" and "pr" will be marked
+ * as transitions to "SAME_TRANS" in "ext". The total number of differences
+ * between "state" and "pr" is returned as function value. Note that this
+ * number is "numecs" minus the number of "SAME_TRANS" entries in "ext".
+ */
+
+int tbldiff( state, pr, ext )
+int state[], pr, ext[];
+ {
+ register int i, *sp = state, *ep = ext, *protp;
+ register int numdiff = 0;
+
+ protp = &protsave[numecs * (pr - 1)];
+
+ for ( i = numecs; i > 0; --i )
+ {
+ if ( *++protp == *++sp )
+ *++ep = SAME_TRANS;
+ else
+ {
+ *++ep = *sp;
+ ++numdiff;
+ }
+ }
+
+ return numdiff;
+ }
diff --git a/usr.bin/lex/version.h b/usr.bin/lex/version.h
new file mode 100644
index 0000000..633a31e
--- /dev/null
+++ b/usr.bin/lex/version.h
@@ -0,0 +1 @@
+#define FLEX_VERSION "2.4.7"
diff --git a/usr.bin/lex/yylex.c b/usr.bin/lex/yylex.c
new file mode 100644
index 0000000..6edb130
--- /dev/null
+++ b/usr.bin/lex/yylex.c
@@ -0,0 +1,199 @@
+/* yylex - scanner front-end for flex */
+
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Vern Paxson.
+ *
+ * The United States Government has rights in this work pursuant
+ * to contract no. DE-AC03-76SF00098 between the United States
+ * Department of Energy and the University of California.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement: ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* $Header: /home/ncvs/src/usr.bin/lex/yylex.c,v 1.1.1.1 1994/08/24 13:10:34 csgr Exp $ */
+
+#include <ctype.h>
+#include "flexdef.h"
+#include "parse.h"
+
+
+/* yylex - scan for a regular expression token */
+
+int yylex()
+ {
+ int toktype;
+ static int beglin = false;
+
+ if ( eofseen )
+ toktype = EOF;
+ else
+ toktype = flexscan();
+
+ if ( toktype == EOF || toktype == 0 )
+ {
+ eofseen = 1;
+
+ if ( sectnum == 1 )
+ {
+ synerr( "premature EOF" );
+ sectnum = 2;
+ toktype = SECTEND;
+ }
+
+ else
+ toktype = 0;
+ }
+
+ if ( trace )
+ {
+ if ( beglin )
+ {
+ fprintf( stderr, "%d\t", num_rules + 1 );
+ beglin = 0;
+ }
+
+ switch ( toktype )
+ {
+ case '<':
+ case '>':
+ case '^':
+ case '$':
+ case '"':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '|':
+ case '(':
+ case ')':
+ case '-':
+ case '/':
+ case '\\':
+ case '?':
+ case '.':
+ case '*':
+ case '+':
+ case ',':
+ (void) putc( toktype, stderr );
+ break;
+
+ case '\n':
+ (void) putc( '\n', stderr );
+
+ if ( sectnum == 2 )
+ beglin = 1;
+
+ break;
+
+ case SCDECL:
+ fputs( "%s", stderr );
+ break;
+
+ case XSCDECL:
+ fputs( "%x", stderr );
+ break;
+
+ case WHITESPACE:
+ (void) putc( ' ', stderr );
+ break;
+
+ case SECTEND:
+ fputs( "%%\n", stderr );
+
+ /* We set beglin to be true so we'll start
+ * writing out numbers as we echo rules.
+ * flexscan() has already assigned sectnum.
+ */
+
+ if ( sectnum == 2 )
+ beglin = 1;
+
+ break;
+
+ case NAME:
+ fprintf( stderr, "'%s'", nmstr );
+ break;
+
+ case CHAR:
+ switch ( yylval )
+ {
+ case '<':
+ case '>':
+ case '^':
+ case '$':
+ case '"':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '|':
+ case '(':
+ case ')':
+ case '-':
+ case '/':
+ case '\\':
+ case '?':
+ case '.':
+ case '*':
+ case '+':
+ case ',':
+ fprintf( stderr, "\\%c",
+ yylval );
+ break;
+
+ default:
+ if ( ! isascii( yylval ) ||
+ ! isprint( yylval ) )
+ fprintf( stderr,
+ "\\%.3o",
+ (unsigned int) yylval );
+ else
+ (void) putc( yylval,
+ stderr );
+ break;
+ }
+
+ break;
+
+ case NUMBER:
+ fprintf( stderr, "%d", yylval );
+ break;
+
+ case PREVCCL:
+ fprintf( stderr, "[%d]", yylval );
+ break;
+
+ case EOF_OP:
+ fprintf( stderr, "<<EOF>>" );
+ break;
+
+ case 0:
+ fprintf( stderr, "End Marker" );
+ break;
+
+ default:
+ fprintf( stderr,
+ "*Something Weird* - tok: %d val: %d\n",
+ toktype, yylval );
+ break;
+ }
+ }
+
+ return toktype;
+ }
diff --git a/usr.bin/locate/Makefile b/usr.bin/locate/Makefile
new file mode 100644
index 0000000..bc55dab
--- /dev/null
+++ b/usr.bin/locate/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+SUBDIR= bigram code locate
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/locate/bigram/Makefile b/usr.bin/locate/bigram/Makefile
new file mode 100644
index 0000000..d7d4348
--- /dev/null
+++ b/usr.bin/locate/bigram/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= locate.bigram
+NOMAN= noman
+BINDIR= /usr/libexec
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/locate/bigram/locate.bigram.c b/usr.bin/locate/bigram/locate.bigram.c
new file mode 100644
index 0000000..149e437
--- /dev/null
+++ b/usr.bin/locate/bigram/locate.bigram.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)locate.bigram.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * bigram < text > bigrams
+ *
+ * List bigrams for 'updatedb' script.
+ * Use 'code' to encode a file using this output.
+ */
+
+#include <stdio.h>
+#include <sys/param.h> /* for MAXPATHLEN */
+
+char buf1[MAXPATHLEN] = " ";
+char buf2[MAXPATHLEN];
+
+main ( )
+{
+ register char *cp;
+ register char *oldpath = buf1, *path = buf2;
+
+ while ( fgets ( path, sizeof(buf2), stdin ) != NULL ) {
+
+ /* skip longest common prefix */
+ for ( cp = path; *cp == *oldpath; cp++, oldpath++ )
+ if ( *oldpath == NULL )
+ break;
+ /*
+ * output post-residue bigrams only
+ */
+ while ( *cp != NULL && *(cp + 1) != NULL ) {
+ putchar ( *cp++ );
+ putchar ( *cp++ );
+ putchar ( '\n' );
+ }
+ if ( path == buf1 ) /* swap pointers */
+ path = buf2, oldpath = buf1;
+ else
+ path = buf1, oldpath = buf2;
+ }
+}
diff --git a/usr.bin/locate/code/Makefile b/usr.bin/locate/code/Makefile
new file mode 100644
index 0000000..743e968
--- /dev/null
+++ b/usr.bin/locate/code/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= locate.code
+CFLAGS+=-I${.CURDIR}/../locate
+NOMAN= noman
+BINDIR= /usr/libexec
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/locate/code/locate.code.c b/usr.bin/locate/code/locate.code.c
new file mode 100644
index 0000000..a7506ec
--- /dev/null
+++ b/usr.bin/locate/code/locate.code.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)locate.code.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * PURPOSE: sorted list compressor (works with a modified 'find'
+ * to encode/decode a filename database)
+ *
+ * USAGE: bigram < list > bigrams
+ * process bigrams (see updatedb) > common_bigrams
+ * code common_bigrams < list > squozen_list
+ *
+ * METHOD: Uses 'front compression' (see ";login:", Volume 8, Number 1
+ * February/March 1983, p. 8). Output format is, per line, an
+ * offset differential count byte followed by a partially bigram-
+ * encoded ascii residue. A bigram is a two-character sequence,
+ * the first 128 most common of which are encoded in one byte.
+ *
+ * EXAMPLE: For simple front compression with no bigram encoding,
+ * if the input is... then the output is...
+ *
+ * /usr/src 0 /usr/src
+ * /usr/src/cmd/aardvark.c 8 /cmd/aardvark.c
+ * /usr/src/cmd/armadillo.c 14 armadillo.c
+ * /usr/tmp/zoo 5 tmp/zoo
+ *
+ * The codes are:
+ *
+ * 0-28 likeliest differential counts + offset to make nonnegative
+ * 30 switch code for out-of-range count to follow in next word
+ * 128-255 bigram codes (128 most common, as determined by 'updatedb')
+ * 32-127 single character (printable) ascii residue (ie, literal)
+ *
+ * SEE ALSO: updatedb.csh, bigram.c
+ *
+ * AUTHOR: James A. Woods, Informatics General Corp.,
+ * NASA Ames Research Center, 10/82
+ */
+
+#include <sys/param.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "locate.h"
+
+#define BGBUFSIZE (NBG * 2) /* size of bigram buffer */
+
+char buf1[MAXPATHLEN + 1] = " ";
+char buf2[MAXPATHLEN + 1];
+char bigrams[BGBUFSIZE + 1] = { 0 };
+
+int bgindex __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *cp, *oldpath, *path;
+ int ch, code, count, diffcount, oldcount;
+ FILE *fp;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if ((fp = fopen(argv[0], "r")) == NULL)
+ err(1, "%s", argv[0]);
+
+ /* First copy bigram array to stdout. */
+ (void)fgets(bigrams, BGBUFSIZE + 1, fp);
+ if (fwrite(bigrams, 1, BGBUFSIZE, stdout) != BGBUFSIZE)
+ err(1, "stdout");
+ (void)fclose(fp);
+
+ oldpath = buf1;
+ path = buf2;
+ oldcount = 0;
+ while (fgets(path, sizeof(buf2) - 1, stdin) != NULL) {
+ /* Truncate newline. */
+ cp = path + strlen(path) - 1;
+ if (cp > path && *cp == '\n')
+ *cp = '\0';
+
+ /* Squelch characters that would botch the decoding. */
+ for (cp = path; *cp != NULL; cp++) {
+ if ((u_char)*cp >= PARITY)
+ *cp &= PARITY-1;
+ if (*cp <= SWITCH)
+ *cp = '?';
+ }
+
+ /* Skip longest common prefix. */
+ for (cp = path; *cp == *oldpath; cp++, oldpath++)
+ if (*oldpath == NULL)
+ break;
+ count = cp - path;
+ diffcount = count - oldcount + OFFSET;
+ oldcount = count;
+ if (diffcount < 0 || diffcount > 2 * OFFSET) {
+ if (putchar(SWITCH) == EOF ||
+ putw(diffcount, stdout) == EOF)
+ err(1, "stdout");
+ } else
+ if (putchar(diffcount) == EOF)
+ err(1, "stdout");
+
+ while (*cp != NULL) {
+ if (*(cp + 1) == NULL) {
+ if (putchar(*cp) == EOF)
+ err(1, "stdout");
+ break;
+ }
+ if ((code = bgindex(cp)) < 0) {
+ if (putchar(*cp++) == EOF ||
+ putchar(*cp++) == EOF)
+ err(1, "stdout");
+ } else {
+ /* Found, so mark byte with parity bit. */
+ if (putchar((code / 2) | PARITY) == EOF)
+ err(1, "stdout");
+ cp += 2;
+ }
+ }
+ if (path == buf1) { /* swap pointers */
+ path = buf2;
+ oldpath = buf1;
+ } else {
+ path = buf1;
+ oldpath = buf2;
+ }
+ }
+ /* Non-zero status if there were errors */
+ if (fflush(stdout) != 0 || ferror(stdout))
+ exit(1);
+ exit(0);
+}
+
+int
+bgindex(bg) /* Return location of bg in bigrams or -1. */
+ char *bg;
+{
+ register char bg0, bg1, *p;
+
+ bg0 = bg[0];
+ bg1 = bg[1];
+ for (p = bigrams; *p != NULL; p++)
+ if (*p++ == bg0 && *p == bg1)
+ break;
+ return (*p == NULL ? -1 : --p - bigrams);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: locate.code common_bigrams < list > squozen_list\n");
+ exit(1);
+}
diff --git a/usr.bin/locate/locate/Makefile b/usr.bin/locate/locate/Makefile
new file mode 100644
index 0000000..928bc71
--- /dev/null
+++ b/usr.bin/locate/locate/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= locate
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/updatedb.csh ${DESTDIR}/usr/libexec/locate.updatedb
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.bin/locate/locate/locate.1 b/usr.bin/locate/locate/locate.1
new file mode 100644
index 0000000..89cc2e8
--- /dev/null
+++ b/usr.bin/locate/locate/locate.1
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)locate.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LOCATE 1
+.Os BSD 4.4
+.Sh NAME
+.Nm locate
+.Nd find files
+.Sh SYNOPSIS
+.Ar locate
+pattern
+.Sh DESCRIPTION
+.Nm Locate
+searches a database for all pathnames which match the specified
+.Ar pattern .
+The database is recomputed periodically, and contains the pathnames
+of all files which are publicly accessible.
+.Pp
+Shell globbing and quoting characters (``*'', ``?'', ``\e'', ``[''
+and ``]'')
+may be used in
+.Ar pattern ,
+although they will have to be escaped from the shell.
+Preceding any character with a backslash (``\e'') eliminates any special
+meaning which it may have.
+The matching differs in that no characters must be matched explicitly,
+including slashes (``/'').
+.Pp
+As a special case, a pattern containing no globbing characters (``foo'')
+is matched as though it were ``*foo*''.
+.Sh FILES
+.Bl -tag -width /var/db/locate.database -compact
+.It Pa /var/db/locate.database
+.El
+.Sh SEE ALSO
+.Xr find 1 ,
+.Xr fnmatch 3
+.Rs
+.%A Woods, James A.
+.%D 1983
+.%T "Finding Files Fast"
+.%J ";login"
+.%V 8:1
+.%P pp. 8-10
+.Re
+.Sh HISTORY
+The
+.Nm locate
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/locate/locate/locate.c b/usr.bin/locate/locate/locate.c
new file mode 100644
index 0000000..0b33188
--- /dev/null
+++ b/usr.bin/locate/locate/locate.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)locate.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Ref: Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8.
+ *
+ * Locate scans a file list for the full pathname of a file given only part
+ * of the name. The list has been processed with with "front-compression"
+ * and bigram coding. Front compression reduces space by a factor of 4-5,
+ * bigram coding by a further 20-25%.
+ *
+ * The codes are:
+ *
+ * 0-28 likeliest differential counts + offset to make nonnegative
+ * 30 switch code for out-of-range count to follow in next word
+ * 128-255 bigram codes (128 most common, as determined by 'updatedb')
+ * 32-127 single character (printable) ascii residue (ie, literal)
+ *
+ * A novel two-tiered string search technique is employed:
+ *
+ * First, a metacharacter-free subpattern and partial pathname is matched
+ * BACKWARDS to avoid full expansion of the pathname list. The time savings
+ * is 40-50% over forward matching, which cannot efficiently handle
+ * overlapped search patterns and compressed path residue.
+ *
+ * Then, the actual shell glob-style regular expression (if in this form) is
+ * matched against the candidate pathnames using the slower routines provided
+ * in the standard 'find'.
+ */
+
+#include <sys/param.h>
+
+#include <fnmatch.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "locate.h"
+#include "pathnames.h"
+
+FILE *fp;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc != 2) {
+ (void)fprintf(stderr, "usage: locate pattern\n");
+ exit(1);
+ }
+ if (!(fp = fopen(_PATH_FCODES, "r"))) {
+ (void)fprintf(stderr, "locate: no database file %s.\n",
+ _PATH_FCODES);
+ exit(1);
+ }
+ while (*++argv)
+ fastfind(*argv);
+ exit(0);
+}
+
+fastfind(pathpart)
+ char *pathpart;
+{
+ register char *p, *s;
+ register int c;
+ int count, found, globflag;
+ char *cutoff, *patend, *q, *patprep();
+ char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN];
+
+ for (c = 0, p = bigram1, s = bigram2; c < NBG; c++)
+ p[c] = getc(fp), s[c] = getc(fp);
+
+ p = pathpart;
+ globflag = index(p, '*') || index(p, '?') || index(p, '[');
+ patend = patprep(p);
+
+ found = 0;
+ for (c = getc(fp), count = 0; c != EOF;) {
+ count += ((c == SWITCH) ? getw(fp) : c) - OFFSET;
+ /* overlay old path */
+ for (p = path + count; (c = getc(fp)) > SWITCH;)
+ if (c < PARITY)
+ *p++ = c;
+ else { /* bigrams are parity-marked */
+ c &= PARITY - 1;
+ *p++ = bigram1[c], *p++ = bigram2[c];
+ }
+ *p-- = NULL;
+ cutoff = (found ? path : path + count);
+ for (found = 0, s = p; s >= cutoff; s--)
+ if (*s == *patend) { /* fast first char check */
+ for (p = patend - 1, q = s - 1; *p != NULL;
+ p--, q--)
+ if (*q != *p)
+ break;
+ if (*p == NULL) { /* fast match success */
+ found = 1;
+ if (!globflag ||
+ !fnmatch(pathpart, path, 0))
+ (void)printf("%s\n", path);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * extract last glob-free subpattern in name for fast pre-match; prepend
+ * '\0' for backwards match; return end of new pattern
+ */
+static char globfree[100];
+
+char *
+patprep(name)
+ char *name;
+{
+ register char *endmark, *p, *subp;
+
+ subp = globfree;
+ *subp++ = '\0';
+ p = name + strlen(name) - 1;
+ /* skip trailing metacharacters (and [] ranges) */
+ for (; p >= name; p--)
+ if (index("*?", *p) == 0)
+ break;
+ if (p < name)
+ p = name;
+ if (*p == ']')
+ for (p--; p >= name; p--)
+ if (*p == '[') {
+ p--;
+ break;
+ }
+ if (p < name)
+ p = name;
+ /*
+ * if pattern has only metacharacters, check every path (force '/'
+ * search)
+ */
+ if ((p == name) && index("?*[]", *p) != 0)
+ *subp++ = '/';
+ else {
+ for (endmark = p; p >= name; p--)
+ if (index("]*?", *p) != 0)
+ break;
+ for (++p;
+ (p <= endmark) && subp < (globfree + sizeof(globfree));)
+ *subp++ = *p++;
+ }
+ *subp = '\0';
+ return(--subp);
+}
diff --git a/usr.bin/locate/locate/locate.h b/usr.bin/locate/locate/locate.h
new file mode 100644
index 0000000..fe4da28
--- /dev/null
+++ b/usr.bin/locate/locate/locate.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)locate.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* Symbolic constants shared by locate.c and code.c */
+
+#define NBG 128 /* number of bigrams considered */
+#define OFFSET 14 /* abs value of max likely diff */
+#define PARITY 0200 /* parity bit */
+#define SWITCH 30 /* switch code */
diff --git a/usr.bin/locate/locate/pathnames.h b/usr.bin/locate/locate/pathnames.h
new file mode 100644
index 0000000..8fb0e8c
--- /dev/null
+++ b/usr.bin/locate/locate/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_FCODES "/var/db/locate.database"
diff --git a/usr.bin/locate/locate/updatedb.csh b/usr.bin/locate/locate/updatedb.csh
new file mode 100644
index 0000000..fef2d6d
--- /dev/null
+++ b/usr.bin/locate/locate/updatedb.csh
@@ -0,0 +1,77 @@
+#!/bin/csh -f
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# James A. Woods.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)updatedb.csh 8.3 (Berkeley) 3/19/94
+#
+
+set SRCHPATHS = "/" # directories to be put in the database
+set LIBDIR = /usr/libexec # for subprograms
+ # for temp files
+if (! $?TMPDIR) setenv TMPDIR /var/tmp
+set FCODES = /var/db/locate.database # the database
+
+set path = ( /bin /usr/bin )
+set bigrams = $TMPDIR/locate.bigrams.$$
+set filelist = $TMPDIR/locate.list.$$
+set errs = $TMPDIR/locate.errs.$$
+
+# Make a file list and compute common bigrams.
+# Alphabetize '/' before any other char with 'tr'.
+# If the system is very short of sort space, 'bigram' can be made
+# smarter to accumulate common bigrams directly without sorting
+# ('awk', with its associative memory capacity, can do this in several
+# lines, but is too slow, and runs out of string space on small machines).
+
+# search locally or everything
+# find ${SRCHPATHS} -print | \
+find ${SRCHPATHS} \! -fstype local -prune -or -print | \
+ tr '/' '\001' | \
+ (sort -T $TMPDIR -f; echo $status > $errs) | tr '\001' '/' > $filelist
+
+$LIBDIR/locate.bigram < $filelist | \
+ (sort -T /$TMPDIR; echo $status >> $errs) | \
+ uniq -c | sort -T /$TMPDIR -nr | \
+ awk '{ if (NR <= 128) print $2 }' | tr -d '\012' > $bigrams
+
+# code the file list
+
+if { grep -s -v 0 $errs } then
+ printf 'locate: updatedb failed\n\n'
+else
+ $LIBDIR/locate.code $bigrams < $filelist > $FCODES
+ chmod 644 $FCODES
+ rm $bigrams $filelist $errs
+endif
diff --git a/usr.bin/lock/Makefile b/usr.bin/lock/Makefile
new file mode 100644
index 0000000..1a55d41
--- /dev/null
+++ b/usr.bin/lock/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lock
+BINOWN= root
+BINMODE=4555
+DPADD= ${LIBCRYPT}
+LDADD= -lcrypt
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lock/lock.1 b/usr.bin/lock/lock.1
new file mode 100644
index 0000000..ad8575c
--- /dev/null
+++ b/usr.bin/lock/lock.1
@@ -0,0 +1,68 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lock.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LOCK 1
+.Os
+.Sh NAME
+.Nm lock
+.Nd reserve a terminal
+.Sh SYNOPSIS
+.Nm lock
+.Op Fl p
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+.Nm Lock
+requests a password from the user, reads it again for verification
+and then will normally not relinquish the terminal until the password is
+repeated.
+There are two other conditions under which it will terminate: it
+will timeout after some interval of time and it may be killed by someone
+with the appropriate permission.
+.Pp
+Options:
+.Pp
+.Bl -tag -width Fl
+.It Fl p
+A password is not requested, instead the user's current login password
+is used.
+.It Fl t Ar timeout
+The time limit (default 15 minutes) is changed to
+.Ar timeout
+minutes.
+.El
+.Sh HISTORY
+The
+.Nm lock
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/lock/lock.c b/usr.bin/lock/lock.c
new file mode 100644
index 0000000..774ebc9
--- /dev/null
+++ b/usr.bin/lock/lock.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1980, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Bob Toxen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Lock a terminal up until the given key is entered, until the root
+ * password is entered, or the given interval times out.
+ *
+ * Timeout interval is by default TIMEOUT, it can be changed with
+ * an argument of the form -time where time is in minutes
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sgtty.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#define TIMEOUT 15
+
+void quit(), bye(), hi();
+
+struct timeval timeout;
+struct timeval zerotime;
+struct sgttyb tty, ntty;
+long nexttime; /* keep the timeout time */
+
+/*ARGSUSED*/
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int errno, optind;
+ struct passwd *pw;
+ struct timeval timval;
+ struct itimerval ntimer, otimer;
+ struct tm *timp;
+ int ch, sectimeout, usemine;
+ char *ap, *mypw, *ttynam, *tzn;
+ char hostname[MAXHOSTNAMELEN], s[BUFSIZ], s1[BUFSIZ];
+ char *crypt(), *ttyname();
+
+ sectimeout = TIMEOUT;
+ mypw = NULL;
+ usemine = 0;
+ while ((ch = getopt(argc, argv, "pt:")) != EOF)
+ switch((char)ch) {
+ case 't':
+ if ((sectimeout = atoi(optarg)) <= 0) {
+ (void)fprintf(stderr,
+ "lock: illegal timeout value.\n");
+ exit(1);
+ }
+ break;
+ case 'p':
+ usemine = 1;
+ if (!(pw = getpwuid(getuid()))) {
+ (void)fprintf(stderr,
+ "lock: unknown uid %d.\n", getuid());
+ exit(1);
+ }
+ mypw = strdup(pw->pw_passwd);
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr,
+ "usage: lock [-p] [-t timeout]\n");
+ exit(1);
+ }
+ timeout.tv_sec = sectimeout * 60;
+
+ setuid(getuid()); /* discard privs */
+
+ if (ioctl(0, TIOCGETP, &tty)) /* get information for header */
+ exit(1);
+ gethostname(hostname, sizeof(hostname));
+ if (!(ttynam = ttyname(0))) {
+ (void)printf("lock: not a terminal?\n");
+ exit(1);
+ }
+ if (gettimeofday(&timval, (struct timezone *)NULL)) {
+ (void)fprintf(stderr,
+ "lock: gettimeofday: %s\n", strerror(errno));
+ exit(1);
+ }
+ nexttime = timval.tv_sec + (sectimeout * 60);
+ timp = localtime(&timval.tv_sec);
+ ap = asctime(timp);
+ tzn = timp->tm_zone;
+
+ (void)signal(SIGINT, quit);
+ (void)signal(SIGQUIT, quit);
+ ntty = tty; ntty.sg_flags &= ~ECHO;
+ (void)ioctl(0, TIOCSETP, &ntty);
+
+ if (!mypw) {
+ /* get key and check again */
+ (void)printf("Key: ");
+ if (!fgets(s, sizeof(s), stdin) || *s == '\n')
+ quit();
+ (void)printf("\nAgain: ");
+ /*
+ * Don't need EOF test here, if we get EOF, then s1 != s
+ * and the right things will happen.
+ */
+ (void)fgets(s1, sizeof(s1), stdin);
+ (void)putchar('\n');
+ if (strcmp(s1, s)) {
+ (void)printf("\07lock: passwords didn't match.\n");
+ ioctl(0, TIOCSETP, &tty);
+ exit(1);
+ }
+ s[0] = NULL;
+ mypw = s1;
+ }
+
+ /* set signal handlers */
+ (void)signal(SIGINT, hi);
+ (void)signal(SIGQUIT, hi);
+ (void)signal(SIGTSTP, hi);
+ (void)signal(SIGALRM, bye);
+
+ ntimer.it_interval = zerotime;
+ ntimer.it_value = timeout;
+ setitimer(ITIMER_REAL, &ntimer, &otimer);
+
+ /* header info */
+(void)printf("lock: %s on %s. timeout in %d minutes\ntime now is %.20s%s%s",
+ ttynam, hostname, sectimeout, ap, tzn, ap + 19);
+
+ for (;;) {
+ (void)printf("Key: ");
+ if (!fgets(s, sizeof(s), stdin)) {
+ clearerr(stdin);
+ hi();
+ continue;
+ }
+ if (usemine) {
+ s[strlen(s) - 1] = '\0';
+ if (!strcmp(mypw, crypt(s, mypw)))
+ break;
+ }
+ else if (!strcmp(s, s1))
+ break;
+ (void)printf("\07\n");
+ if (ioctl(0, TIOCGETP, &ntty))
+ exit(1);
+ }
+ quit();
+}
+
+void
+hi()
+{
+ struct timeval timval;
+
+ if (!gettimeofday(&timval, (struct timezone *)NULL))
+(void)printf("lock: type in the unlock key. timeout in %ld:%ld minutes\n",
+ (nexttime - timval.tv_sec) / 60, (nexttime - timval.tv_sec) % 60);
+}
+
+void
+quit()
+{
+ (void)putchar('\n');
+ (void)ioctl(0, TIOCSETP, &tty);
+ exit(0);
+}
+
+void
+bye()
+{
+ (void)ioctl(0, TIOCSETP, &tty);
+ (void)printf("lock: timeout\n");
+ exit(1);
+}
diff --git a/usr.bin/logger/Makefile b/usr.bin/logger/Makefile
new file mode 100644
index 0000000..a5d143b
--- /dev/null
+++ b/usr.bin/logger/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= logger
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/logger/logger.1 b/usr.bin/logger/logger.1
new file mode 100644
index 0000000..0621dba
--- /dev/null
+++ b/usr.bin/logger/logger.1
@@ -0,0 +1,100 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)logger.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LOGGER 1
+.Os BSD 4.3
+.Sh NAME
+.Nm logger
+.Nd make entries in the system log
+.Sh SYNOPSIS
+.Nm logger
+.Op Fl is
+.Op Fl f Ar file
+.Op Fl p Ar pri
+.Op Fl t Ar tag
+.Op Ar message ...
+.Sh DESCRIPTION
+.Nm Logger
+provides a shell command interface to the
+.Xr syslog 3
+system log module.
+.Pp
+Options:
+.Pp
+.Bl -tag -width "message"
+.It Fl i
+Log the process id of the logger process
+with each line.
+.It Fl s
+Log the message to standard error, as well as the system log.
+.It Fl f Ar file
+Log the specified file.
+.It Fl p Ar pri
+Enter the message with the specified priority.
+The priority may be specified numerically or as a ``facility.level''
+pair.
+For example, ``\-p local3.info'' logs the message(s) as
+.Ar info Ns rmational
+level in the
+.Ar local3
+facility.
+The default is ``user.notice.''
+.It Fl t Ar tag
+Mark every line in the log with the specified
+.Ar tag .
+.It Ar message
+Write the message to log; if not specified, and the
+.Fl f
+flag is not
+provided, standard input is logged.
+.El
+.Pp
+The
+.Nm logger
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+.Bd -literal -offset indent -compact
+logger System rebooted
+
+logger \-p local0.notice \-t HOSTIDM \-f /dev/idmc
+.Ed
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
+.Sh STANDARDS
+The
+.Nm logger
+command is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/logger/logger.c b/usr.bin/logger/logger.c
new file mode 100644
index 0000000..3fd3b6b
--- /dev/null
+++ b/usr.bin/logger/logger.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)logger.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#define SYSLOG_NAMES
+#include <syslog.h>
+
+int decode __P((char *, CODE *));
+int pencode __P((char *));
+void usage __P((void));
+
+/*
+ * logger -- read and log utility
+ *
+ * Reads from an input and arranges to write the result on the system
+ * log.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, logflags, pri;
+ char *tag, buf[1024];
+
+ tag = NULL;
+ pri = LOG_NOTICE;
+ logflags = 0;
+ while ((ch = getopt(argc, argv, "f:ip:st:")) != EOF)
+ switch((char)ch) {
+ case 'f': /* file to log */
+ if (freopen(optarg, "r", stdin) == NULL) {
+ (void)fprintf(stderr, "logger: %s: %s.\n",
+ optarg, strerror(errno));
+ exit(1);
+ }
+ break;
+ case 'i': /* log process id also */
+ logflags |= LOG_PID;
+ break;
+ case 'p': /* priority */
+ pri = pencode(optarg);
+ break;
+ case 's': /* log to standard error */
+ logflags |= LOG_PERROR;
+ break;
+ case 't': /* tag */
+ tag = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* setup for logging */
+ openlog(tag ? tag : getlogin(), logflags, 0);
+ (void) fclose(stdout);
+
+ /* log input line if appropriate */
+ if (argc > 0) {
+ register char *p, *endp;
+ int len;
+
+ for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
+ len = strlen(*argv);
+ if (p + len > endp && p > buf) {
+ syslog(pri, "%s", buf);
+ p = buf;
+ }
+ if (len > sizeof(buf) - 1)
+ syslog(pri, "%s", *argv++);
+ else {
+ if (p != buf)
+ *p++ = ' ';
+ bcopy(*argv++, p, len);
+ *(p += len) = '\0';
+ }
+ }
+ if (p != buf)
+ syslog(pri, "%s", buf);
+ } else
+ while (fgets(buf, sizeof(buf), stdin) != NULL)
+ syslog(pri, "%s", buf);
+ exit(0);
+}
+
+/*
+ * Decode a symbolic name to a numeric value
+ */
+int
+pencode(s)
+ register char *s;
+{
+ char *save;
+ int fac, lev;
+
+ for (save = s; *s && *s != '.'; ++s);
+ if (*s) {
+ *s = '\0';
+ fac = decode(save, facilitynames);
+ if (fac < 0) {
+ (void)fprintf(stderr,
+ "logger: unknown facility name: %s.\n", save);
+ exit(1);
+ }
+ *s++ = '.';
+ }
+ else {
+ fac = 0;
+ s = save;
+ }
+ lev = decode(s, prioritynames);
+ if (lev < 0) {
+ (void)fprintf(stderr,
+ "logger: unknown priority name: %s.\n", save);
+ exit(1);
+ }
+ return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+int
+decode(name, codetab)
+ char *name;
+ CODE *codetab;
+{
+ register CODE *c;
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (c = codetab; c->c_name; c++)
+ if (!strcasecmp(name, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "logger: [-is] [-f file] [-p pri] [-t tag] [ message ... ]\n");
+ exit(1);
+}
diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile
new file mode 100644
index 0000000..9b9a7ed
--- /dev/null
+++ b/usr.bin/login/Makefile
@@ -0,0 +1,30 @@
+# From: @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $Id: Makefile,v 1.12 1995/03/18 17:18:15 nate Exp $
+
+PROG= login
+MAN1= login.1
+MAN5= login.access.5
+SRCS= login.c login_access.c login_fbtab.c
+
+CFLAGS+=-DLOGIN_ACCESS -DSKEY -DLOGALL
+
+.if defined(KLOGIN_PARANOID)
+CFLAGS+=-DKLOGIN_PARANOID
+.endif
+
+DPADD= ${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
+LDADD= -lutil -lskey -lmd -lcrypt
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && defined(MAKE_EBONES)
+CFLAGS+=-DKERBEROS
+SRCS+= klogin.c
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/login/README b/usr.bin/login/README
new file mode 100644
index 0000000..d7c964d
--- /dev/null
+++ b/usr.bin/login/README
@@ -0,0 +1,20 @@
+This login has additional functionalities. They are all based on (part of)
+Wietse Venema's logdaemon package.
+
+
+The following defines can be used:
+1) LOGIN_ACCESS to allow access control on a per tty/user combination
+2) SKEY to allow the use of s/key one time passwords
+3) LOGALL to log all logins
+
+-Guido
+
+This login has some of Berkeley's paranoid/broken (depending on your point
+of view) Kerberos code conditionalized out, so that by default it works like
+klogin does at MIT-LCS. You can define KLOGIN_PARANOID to re-enable this code.
+This define also controls whether a warning message is printed when logging
+into a system with no krb.conf file, which usually means that Kerberos is
+not configured.
+
+-GAWollman
+
diff --git a/usr.bin/login/klogin.c b/usr.bin/login/klogin.c
new file mode 100644
index 0000000..953d35a
--- /dev/null
+++ b/usr.bin/login/klogin.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)klogin.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#ifdef KERBEROS
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define INITIAL_TICKET "krbtgt"
+#define VERIFY_SERVICE "rcmd"
+
+extern int notickets;
+extern char *krbtkfile_env;
+
+/*
+ * Attempt to log the user in using Kerberos authentication
+ *
+ * return 0 on success (will be logged in)
+ * 1 if Kerberos failed (try local password in login)
+ */
+int
+klogin(pw, instance, localhost, password)
+ struct passwd *pw;
+ char *instance, *localhost, *password;
+{
+ int kerror;
+ AUTH_DAT authdata;
+ KTEXT_ST ticket;
+ struct hostent *hp;
+ unsigned long faddr;
+ char realm[REALM_SZ], savehost[MAXHOSTNAMELEN];
+ char tkt_location[MAXPATHLEN];
+ char *krb_get_phost();
+ extern int noticketsdontcomplain;
+
+#ifdef KLOGIN_PARANOID
+ noticketsdontcomplain = 0; /* enable warning message */
+#endif
+ /*
+ * Root logins don't use Kerberos.
+ * If we have a realm, try getting a ticket-granting ticket
+ * and using it to authenticate. Otherwise, return
+ * failure so that we can try the normal passwd file
+ * for a password. If that's ok, log the user in
+ * without issuing any tickets.
+ */
+ if (strcmp(pw->pw_name, "root") == 0 ||
+ krb_get_lrealm(realm, 0) != KSUCCESS)
+ return (1);
+
+ noticketsdontcomplain = 0; /* enable warning message */
+
+ /*
+ * get TGT for local realm
+ * tickets are stored in a file named TKT_ROOT plus uid
+ * except for user.root tickets.
+ */
+
+ if (strcmp(instance, "root") != 0)
+ (void)sprintf(tkt_location, "%s%d", TKT_ROOT, pw->pw_uid);
+ else {
+ (void)sprintf(tkt_location, "%s_root_%d", TKT_ROOT, pw->pw_uid);
+ krbtkfile_env = tkt_location;
+ }
+ (void)krb_set_tkt_string(tkt_location);
+
+ /*
+ * Set real as well as effective ID to 0 for the moment,
+ * to make the kerberos library do the right thing.
+ */
+ if (setuid(0) < 0) {
+ warnx("setuid");
+ return (1);
+ }
+ kerror = krb_get_pw_in_tkt(pw->pw_name, instance,
+ realm, INITIAL_TICKET, realm, DEFAULT_TKT_LIFE, password);
+
+ /*
+ * If we got a TGT, get a local "rcmd" ticket and check it so as to
+ * ensure that we are not talking to a bogus Kerberos server.
+ *
+ * There are 2 cases where we still allow a login:
+ * 1: the VERIFY_SERVICE doesn't exist in the KDC
+ * 2: local host has no srvtab, as (hopefully) indicated by a
+ * return value of RD_AP_UNDEC from krb_rd_req().
+ */
+ if (kerror != INTK_OK) {
+ if (kerror != INTK_BADPW && kerror != KDC_PR_UNKNOWN) {
+ syslog(LOG_ERR, "Kerberos intkt error: %s",
+ krb_err_txt[kerror]);
+ dest_tkt();
+ }
+ return (1);
+ }
+
+ if (chown(TKT_FILE, pw->pw_uid, pw->pw_gid) < 0)
+ syslog(LOG_ERR, "chown tkfile (%s): %m", TKT_FILE);
+
+ (void)strncpy(savehost, krb_get_phost(localhost), sizeof(savehost));
+ savehost[sizeof(savehost)-1] = NULL;
+
+#ifdef KLOGIN_PARANOID
+ /*
+ * if the "VERIFY_SERVICE" doesn't exist in the KDC for this host,
+ * still allow login with tickets, but log the error condition.
+ */
+
+ kerror = krb_mk_req(&ticket, VERIFY_SERVICE, savehost, realm, 33);
+ if (kerror == KDC_PR_UNKNOWN) {
+ syslog(LOG_NOTICE,
+ "warning: TGT not verified (%s); %s.%s not registered, or srvtab is wrong?",
+ krb_err_txt[kerror], VERIFY_SERVICE, savehost);
+ notickets = 0;
+ return (0);
+ }
+
+ if (kerror != KSUCCESS) {
+ warnx("unable to use TGT: (%s)", krb_err_txt[kerror]);
+ syslog(LOG_NOTICE, "unable to use TGT: (%s)",
+ krb_err_txt[kerror]);
+ dest_tkt();
+ return (1);
+ }
+
+ if (!(hp = gethostbyname(localhost))) {
+ syslog(LOG_ERR, "couldn't get local host address");
+ dest_tkt();
+ return (1);
+ }
+
+ memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr));
+
+ kerror = krb_rd_req(&ticket, VERIFY_SERVICE, savehost, faddr,
+ &authdata, "");
+
+ if (kerror == KSUCCESS) {
+ notickets = 0;
+ return (0);
+ }
+
+ /* undecipherable: probably didn't have a srvtab on the local host */
+ if (kerror = RD_AP_UNDEC) {
+ syslog(LOG_NOTICE, "krb_rd_req: (%s)\n", krb_err_txt[kerror]);
+ dest_tkt();
+ return (1);
+ }
+ /* failed for some other reason */
+ warnx("unable to verify %s ticket: (%s)", VERIFY_SERVICE,
+ krb_err_txt[kerror]);
+ syslog(LOG_NOTICE, "couldn't verify %s ticket: %s", VERIFY_SERVICE,
+ krb_err_txt[kerror]);
+ dest_tkt();
+ return (1);
+#else
+ notickets = 0;
+ return (0);
+#endif
+}
+#endif
diff --git a/usr.bin/login/login.1 b/usr.bin/login/login.1
new file mode 100644
index 0000000..5ae5dba
--- /dev/null
+++ b/usr.bin/login/login.1
@@ -0,0 +1,156 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)login.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt LOGIN 1
+.Os BSD 4
+.Sh NAME
+.Nm login
+.Nd log into the computer
+.Sh SYNOPSIS
+.Nm login
+.Op Fl fp
+.Op Fl h Ar hostname
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm login
+utility logs users (and pseudo-users) into the computer system.
+.Pp
+If no user is specified, or if a user is specified and authentication
+of the user fails,
+.Nm login
+prompts for a user name.
+Authentication of users is done via passwords.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f
+The
+.Fl f
+option is used when a user name is specified to indicate that proper
+authentication has already been done and that no password need be
+requested.
+This option may only be used by the super-user or when an already
+logged in user is logging in as themselves.
+.It Fl h
+The
+.Fl h
+option specifies the host from which the connection was received.
+It is used by various daemons such as
+.Xr telnetd 8 .
+This option may only be used by the super-user.
+.It Fl p
+By default,
+.Nm login
+discards any previous environment.
+The
+.Fl p
+option disables this behavior.
+.El
+.Pp
+If the file
+.Pa /etc/nologin
+exists,
+.Nm login
+dislays its contents to the user and exits.
+This is used by
+.Xr shutdown 8
+to prevent users from logging in when the system is about to go down.
+.Pp
+If the file
+.Pa /etc/fbtab
+exists,
+.Nm login
+changes the protection and ownership of certain devices specified in this
+file.
+.Pp
+Immediately after logging a user in,
+.Nm login
+displays the system copyright notice, the date and time the user last
+logged in, the message of the day as well as other information.
+If the file
+.Dq Pa .hushlogin
+exists in the user's home directory, all of these messages are suppressed.
+This is to simplify logins for non-human users, such as
+.Xr uucp 1 .
+.Nm Login
+then records an entry in the
+.Xr wtmp 5
+and
+.Xr utmp 5
+files and executes the user's command interpretor.
+.Pp
+Login enters information into the environment (see
+.Xr environ 7 )
+specifying the user's home directory (HOME), command interpreter (SHELL),
+search path (PATH), terminal type (TERM) and user name (both LOGNAME and
+USER).
+.Pp
+The standard shells,
+.Xr csh 1
+and
+.Xr sh 1 ,
+do not fork before executing the
+.Nm login
+utility.
+.Sh FILES
+.Bl -tag -width /var/mail/userXXX -compact
+.It Pa /etc/fbtab
+changes device protections
+.It Pa /etc/motd
+message-of-the-day
+.It Pa /etc/nologin
+disallows logins
+.It Pa /var/run/utmp
+current logins
+.It Pa /var/log/wtmp
+login account records
+.It Pa /var/mail/user
+system mailboxes
+.It Pa \&.hushlogin
+makes login quieter
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr rlogin 1 ,
+.Xr getpass 3 ,
+.Xr fbtab 5 ,
+.Xr utmp 5 ,
+.Xr environ 7
+.Sh HISTORY
+A
+.Nm login
+appeared in
+.At v6 .
diff --git a/usr.bin/login/login.access.5 b/usr.bin/login/login.access.5
new file mode 100644
index 0000000..28d423c
--- /dev/null
+++ b/usr.bin/login/login.access.5
@@ -0,0 +1,50 @@
+.\" this is comment
+.Dd April 30, 1994
+.Dt SKEY.ACCESS 5
+.Os FreeBSD 1.2
+.Sh NAME
+.Nm login.access
+.Nd Login access control table
+.Sh DESCRIPTION
+The
+.Nm login.access
+file specifies (user, host) combinations and/or (user, tty)
+combinations for which a login will be either accepted or refused.
+.Pp
+When someone logs in, the
+.Nm login.access
+is scanned for the first entry that
+matches the (user, host) combination, or, in case of non-networked
+logins, the first entry that matches the (user, tty) combination. The
+permissions field of that table entry determines whether the login will
+be accepted or refused.
+.Pp
+Each line of the login access control table has three fields separated by a
+":" character: permission : users : origins
+
+The first field should be a "+" (access granted) or "-" (access denied)
+character. The second field should be a list of one or more login names,
+group names, or ALL (always matches). The third field should be a list
+of one or more tty names (for non-networked logins), host names, domain
+names (begin with "."), host addresses, internet network numbers (end
+with "."), ALL (always matches) or LOCAL (matches any string that does
+not contain a "." character). If you run NIS you can use @netgroupname
+in host or user patterns.
+
+The EXCEPT operator makes it possible to write very compact rules.
+
+The group file is searched only when a name does not match that of the
+logged-in user. Only groups are matched in which users are explicitly
+listed: the program does not look at a user's primary group id value.
+.Sh FILES
+.Bl -tag -width /etc/login.access -compact
+.It Pa /etc/login.access
+The
+.Nm login.access
+file resides in
+.Pa /etc .
+.El
+.Sh SEE ALSO
+.Xr login 1
+.Sh AUTHOR
+Guido van Rooij
diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c
new file mode 100644
index 0000000..f3f9ae6
--- /dev/null
+++ b/usr.bin/login/login.c
@@ -0,0 +1,678 @@
+/*-
+ * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * login [ name ]
+ * login -h hostname (for telnetd, etc.)
+ * login -f name (for pre-authenticated login: datakit, xterm, etc.)
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <ttyent.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#ifdef SKEY
+#include <skey.h>
+#endif
+
+#include "pathnames.h"
+
+void badlogin __P((char *));
+void checknologin __P((void));
+void dolastlog __P((int));
+void getloginname __P((void));
+void motd __P((void));
+int rootterm __P((char *));
+void sigint __P((int));
+void sleepexit __P((int));
+char *stypeof __P((char *));
+void timedout __P((int));
+void login_fbtab __P((char *, uid_t, gid_t));
+#ifdef KERBEROS
+int klogin __P((struct passwd *, char *, char *, char *));
+#endif
+
+extern void login __P((struct utmp *));
+
+#define TTYGRPNAME "tty" /* name of group to own ttys */
+
+/*
+ * This bounds the time given to login. Not a define so it can
+ * be patched on machines where it's too small.
+ */
+u_int timeout = 300;
+
+#ifdef KERBEROS
+int notickets = 1;
+int noticketsdontcomplain = 1;
+char *instance;
+char *krbtkfile_env;
+int authok;
+#endif
+
+struct passwd *pwd;
+int failures;
+char term[64], *envinit[1], *hostname, *username, *tty;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char **environ;
+ struct group *gr;
+ struct stat st;
+ struct timeval tp;
+ struct utmp utmp;
+ int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
+ int changepass;
+ uid_t uid;
+ char *domain, *p, *ep, *salt, *ttyn;
+ char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
+ char localhost[MAXHOSTNAMELEN];
+ char full_hostname[MAXHOSTNAMELEN];
+#ifdef SKEY
+ int permit_passwd = 0;
+#endif
+
+ (void)signal(SIGALRM, timedout);
+ (void)alarm(timeout);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog("login", LOG_ODELAY, LOG_AUTH);
+
+ /*
+ * -p is used by getty to tell login not to destroy the environment
+ * -f is used to skip a second login authentication
+ * -h is used by other servers to pass the name of the remote
+ * host to login so that it may be placed in utmp and wtmp
+ */
+ *full_hostname = '\0';
+ domain = NULL;
+ if (gethostname(localhost, sizeof(localhost)) < 0)
+ syslog(LOG_ERR, "couldn't get local hostname: %m");
+ else
+ domain = strchr(localhost, '.');
+
+ fflag = hflag = pflag = 0;
+ uid = getuid();
+ while ((ch = getopt(argc, argv, "fh:p")) != EOF)
+ switch (ch) {
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ if (uid)
+ errx(1, "-h option: %s", strerror(EPERM));
+ hflag = 1;
+ strncpy(full_hostname, optarg, sizeof(full_hostname)-1);
+ if (domain && (p = strchr(optarg, '.')) &&
+ strcasecmp(p, domain) == 0)
+ *p = 0;
+ hostname = optarg;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case '?':
+ default:
+ if (!uid)
+ syslog(LOG_ERR, "invalid flag %c", ch);
+ (void)fprintf(stderr,
+ "usage: login [-fp] [-h hostname] [username]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv) {
+ username = *argv;
+ ask = 0;
+ } else
+ ask = 1;
+
+ for (cnt = getdtablesize(); cnt > 2; cnt--)
+ (void)close(cnt);
+
+ ttyn = ttyname(STDIN_FILENO);
+ if (ttyn == NULL || *ttyn == '\0') {
+ (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
+ ttyn = tname;
+ }
+ if (tty = strrchr(ttyn, '/'))
+ ++tty;
+ else
+ tty = ttyn;
+
+ for (cnt = 0;; ask = 1) {
+ if (ask) {
+ fflag = 0;
+ getloginname();
+ }
+ rootlogin = 0;
+#ifdef KERBEROS
+ if ((instance = strchr(username, '.')) != NULL) {
+ if (strncmp(instance, ".root", 5) == 0)
+ rootlogin = 1;
+ *instance++ = '\0';
+ } else
+ instance = "";
+#endif
+ if (strlen(username) > UT_NAMESIZE)
+ username[UT_NAMESIZE] = '\0';
+
+ /*
+ * Note if trying multiple user names; log failures for
+ * previous user name, but don't bother logging one failure
+ * for nonexistent name (mistyped username).
+ */
+ if (failures && strcmp(tbuf, username)) {
+ if (failures > (pwd ? 0 : 1))
+ badlogin(tbuf);
+ failures = 0;
+ }
+ (void)strcpy(tbuf, username);
+
+ if (pwd = getpwnam(username))
+ salt = pwd->pw_passwd;
+ else
+ salt = "xx";
+
+ /*
+ * if we have a valid account name, and it doesn't have a
+ * password, or the -f option was specified and the caller
+ * is root or the caller isn't changing their uid, don't
+ * authenticate.
+ */
+ if (pwd) {
+ if (pwd->pw_uid == 0)
+ rootlogin = 1;
+
+ if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
+ /* already authenticated */
+ break;
+ } else if (pwd->pw_passwd[0] == '\0') {
+ /* pretend password okay */
+ rval = 0;
+ goto ttycheck;
+ }
+ }
+
+ fflag = 0;
+
+ (void)setpriority(PRIO_PROCESS, 0, -4);
+
+#ifdef SKEY
+ permit_passwd = skeyaccess(username, tty,
+ hostname ? full_hostname : NULL,
+ NULL);
+ p = skey_getpass("Password:", pwd, permit_passwd);
+ ep = skey_crypt(p, salt, pwd, permit_passwd);
+#else
+ p = getpass("Password:");
+ ep = crypt(p, salt);
+#endif
+
+ if (pwd) {
+#ifdef KERBEROS
+#ifdef SKEY
+ /*
+ * Do not allow user to type in kerberos password
+ * over the net (actually, this is ok for encrypted
+ * links, but we have no way of determining if the
+ * link is encrypted.
+ */
+ if (!permit_passwd) {
+ rval = 1; /* failed */
+ } else
+#endif
+ rval = klogin(pwd, instance, localhost, p);
+ if (rval != 0 && rootlogin && pwd->pw_uid != 0)
+ rootlogin = 0;
+ if (rval == 0)
+ authok = 1;
+ else if (rval == 1)
+ rval = strcmp(ep, pwd->pw_passwd);
+#else
+ rval = strcmp(ep, pwd->pw_passwd);
+#endif
+ }
+ memset(p, 0, strlen(p));
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ ttycheck:
+ /*
+ * If trying to log in as root without Kerberos,
+ * but with insecure terminal, refuse the login attempt.
+ */
+#ifdef KERBEROS
+ if (authok == 0)
+#endif
+ if (pwd && !rval && rootlogin && !rootterm(tty)) {
+ (void)fprintf(stderr,
+ "%s login refused on this terminal.\n",
+ pwd->pw_name);
+ if (hostname)
+ syslog(LOG_NOTICE,
+ "LOGIN %s REFUSED FROM %s ON TTY %s",
+ pwd->pw_name, hostname, tty);
+ else
+ syslog(LOG_NOTICE,
+ "LOGIN %s REFUSED ON TTY %s",
+ pwd->pw_name, tty);
+ continue;
+ }
+
+ if (pwd && !rval)
+ break;
+
+ (void)printf("Login incorrect\n");
+ failures++;
+ /* we allow 10 tries, but after 3 we start backing off */
+ if (++cnt > 3) {
+ if (cnt >= 10) {
+ badlogin(username);
+ sleepexit(1);
+ }
+ sleep((u_int)((cnt - 3) * 5));
+ }
+ }
+
+ /* committed to login -- turn off timeout */
+ (void)alarm((u_int)0);
+
+ endpwent();
+
+ /* if user not super-user, check for disabled logins */
+ if (!rootlogin)
+ checknologin();
+
+ if (chdir(pwd->pw_dir) < 0) {
+ (void)printf("No home directory %s!\n", pwd->pw_dir);
+ if (chdir("/"))
+ exit(0);
+ pwd->pw_dir = "/";
+ (void)printf("Logging in with home = \"/\".\n");
+ }
+
+ quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
+
+ if (pwd->pw_change || pwd->pw_expire)
+ (void)gettimeofday(&tp, (struct timezone *)NULL);
+
+ changepass=0;
+ if (pwd->pw_change)
+ if (tp.tv_sec >= pwd->pw_change) {
+ (void)printf("Sorry -- your password has expired.\n");
+ changepass=1;
+ } else if (pwd->pw_change - tp.tv_sec <
+ 2 * 7 * 86400 && !quietlog)
+ (void)printf("Warning: your password expires on %s",
+ ctime(&pwd->pw_change));
+ if (pwd->pw_expire)
+ if (tp.tv_sec >= pwd->pw_expire) {
+ (void)printf("Sorry -- your account has expired.\n");
+ sleepexit(1);
+ } else if (pwd->pw_expire - tp.tv_sec <
+ 2 * 7 * 86400 && !quietlog)
+ (void)printf("Warning: your account expires on %s",
+ ctime(&pwd->pw_expire));
+
+ /* Nothing else left to fail -- really log in. */
+ memset((void *)&utmp, 0, sizeof(utmp));
+ (void)time(&utmp.ut_time);
+ (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
+ if (hostname)
+ (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
+ (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+ login(&utmp);
+
+ dolastlog(quietlog);
+
+ /*
+ * Set device protections, depending on what terminal the
+ * user is logged in. This feature is used on Suns to give
+ * console users better privacy.
+ */
+ login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
+
+ (void)chown(ttyn, pwd->pw_uid,
+ (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
+ (void)setgid(pwd->pw_gid);
+
+ initgroups(username, pwd->pw_gid);
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = _PATH_BSHELL;
+
+ /* Destroy environment unless user has requested its preservation. */
+ if (!pflag)
+ environ = envinit;
+ (void)setenv("HOME", pwd->pw_dir, 1);
+ (void)setenv("SHELL", pwd->pw_shell, 1);
+ if (term[0] == '\0')
+ (void)strncpy(term, stypeof(tty), sizeof(term));
+ (void)setenv("TERM", term, 0);
+ (void)setenv("LOGNAME", pwd->pw_name, 1);
+ (void)setenv("USER", pwd->pw_name, 1);
+ (void)setenv("PATH", _PATH_DEFPATH, 0);
+#ifdef KERBEROS
+ if (krbtkfile_env)
+ (void)setenv("KRBTKFILE", krbtkfile_env, 1);
+#endif
+
+ if (tty[sizeof("tty")-1] == 'd')
+ syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
+
+ /* If fflag is on, assume caller/authenticator has logged root login. */
+ if (rootlogin && fflag == 0)
+ if (hostname)
+ syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
+ username, tty, hostname);
+ else
+ syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
+
+#ifdef KERBEROS
+ if (!quietlog && notickets == 1 && !noticketsdontcomplain)
+ (void)printf("Warning: no Kerberos tickets issued.\n");
+#endif
+
+#ifdef LOGALL
+ /*
+ * Syslog each successful login, so we don't have to watch hundreds
+ * of wtmp or lastlogin files.
+ */
+ if (hostname) {
+ syslog(LOG_INFO, "login from %s as %s", hostname, pwd->pw_name);
+ } else {
+ syslog(LOG_INFO, "login on %s as %s", tty, pwd->pw_name);
+ }
+#endif
+
+ if (!quietlog) {
+ (void)printf("%s\n\t%s %s\n\n",
+ "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
+ "The Regents of the University of California. ",
+ "All rights reserved.");
+ motd();
+ (void)snprintf(tbuf,
+ sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ if (stat(tbuf, &st) == 0 && st.st_size != 0)
+ (void)printf("You have %smail.\n",
+ (st.st_mtime > st.st_atime) ? "new " : "");
+ }
+
+#ifdef LOGIN_ACCESS
+ if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0) {
+ printf("Permission denied\n");
+ if (hostname)
+ syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s",
+ pwd->pw_name, hostname);
+ else
+ syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s",
+ pwd->pw_name, tty);
+ sleepexit(1);
+ }
+#endif
+
+ (void)signal(SIGALRM, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGTSTP, SIG_IGN);
+
+ tbuf[0] = '-';
+ (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
+ p + 1 : pwd->pw_shell);
+
+ if (setlogin(pwd->pw_name) < 0)
+ syslog(LOG_ERR, "setlogin() failure: %m");
+
+ /* Discard permissions last so can't get killed and drop core. */
+ if (rootlogin)
+ (void) setuid(0);
+ else
+ (void) setuid(pwd->pw_uid);
+
+ if (changepass) {
+ int res;
+ if ((res=system(_PATH_CHPASS)))
+ sleepexit(1);
+ }
+
+ execlp(pwd->pw_shell, tbuf, 0);
+ err(1, "%s", pwd->pw_shell);
+}
+
+#ifdef KERBEROS
+#define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */
+#else
+#define NBUFSIZ (UT_NAMESIZE + 1)
+#endif
+
+void
+getloginname()
+{
+ int ch;
+ char *p;
+ static char nbuf[NBUFSIZ];
+
+ for (;;) {
+ (void)printf("login: ");
+ for (p = nbuf; (ch = getchar()) != '\n'; ) {
+ if (ch == EOF) {
+ badlogin(username);
+ exit(0);
+ }
+ if (p < nbuf + (NBUFSIZ - 1))
+ *p++ = ch;
+ }
+ if (p > nbuf)
+ if (nbuf[0] == '-')
+ (void)fprintf(stderr,
+ "login names may not start with '-'.\n");
+ else {
+ *p = '\0';
+ username = nbuf;
+ break;
+ }
+ }
+}
+
+int
+rootterm(ttyn)
+ char *ttyn;
+{
+ struct ttyent *t;
+
+ return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
+}
+
+jmp_buf motdinterrupt;
+
+void
+motd()
+{
+ int fd, nchars;
+ sig_t oldint;
+ char tbuf[8192];
+
+ if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
+ return;
+ oldint = signal(SIGINT, sigint);
+ if (setjmp(motdinterrupt) == 0)
+ while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+ (void)write(fileno(stdout), tbuf, nchars);
+ (void)signal(SIGINT, oldint);
+ (void)close(fd);
+}
+
+/* ARGSUSED */
+void
+sigint(signo)
+ int signo;
+{
+
+ longjmp(motdinterrupt, 1);
+}
+
+/* ARGSUSED */
+void
+timedout(signo)
+ int signo;
+{
+
+ (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
+ exit(0);
+}
+
+void
+checknologin()
+{
+ int fd, nchars;
+ char tbuf[8192];
+
+ if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
+ while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+ (void)write(fileno(stdout), tbuf, nchars);
+ sleepexit(0);
+ }
+}
+
+void
+dolastlog(quiet)
+ int quiet;
+{
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+ if (!quiet) {
+ if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
+ ll.ll_time != 0) {
+ (void)printf("Last login: %.*s ",
+ 24-5, (char *)ctime(&ll.ll_time));
+ if (*ll.ll_host != '\0')
+ (void)printf("from %.*s\n",
+ (int)sizeof(ll.ll_host),
+ ll.ll_host);
+ else
+ (void)printf("on %.*s\n",
+ (int)sizeof(ll.ll_line),
+ ll.ll_line);
+ }
+ (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+ }
+ memset((void *)&ll, 0, sizeof(ll));
+ (void)time(&ll.ll_time);
+ (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+ if (hostname)
+ (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
+ (void)write(fd, (char *)&ll, sizeof(ll));
+ (void)close(fd);
+ }
+}
+
+void
+badlogin(name)
+ char *name;
+{
+
+ if (failures == 0)
+ return;
+ if (hostname) {
+ syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
+ failures, failures > 1 ? "S" : "", hostname);
+ syslog(LOG_AUTHPRIV|LOG_NOTICE,
+ "%d LOGIN FAILURE%s FROM %s, %s",
+ failures, failures > 1 ? "S" : "", hostname, name);
+ } else {
+ syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
+ failures, failures > 1 ? "S" : "", tty);
+ syslog(LOG_AUTHPRIV|LOG_NOTICE,
+ "%d LOGIN FAILURE%s ON %s, %s",
+ failures, failures > 1 ? "S" : "", tty, name);
+ }
+}
+
+#undef UNKNOWN
+#define UNKNOWN "su"
+
+char *
+stypeof(ttyid)
+ char *ttyid;
+{
+ struct ttyent *t;
+
+ return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
+}
+
+void
+sleepexit(eval)
+ int eval;
+{
+
+ (void)sleep(5);
+ exit(eval);
+}
+
+
diff --git a/usr.bin/login/login_access.c b/usr.bin/login/login_access.c
new file mode 100644
index 0000000..6284216
--- /dev/null
+++ b/usr.bin/login/login_access.c
@@ -0,0 +1,236 @@
+ /*
+ * This module implements a simple but effective form of login access
+ * control based on login names and on host (or domain) names, internet
+ * addresses (or network numbers), or on terminal line names in case of
+ * non-networked logins. Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifdef LOGIN_ACCESS
+#ifndef lint
+static char sccsid[] = "%Z% %M% %I% %E% %U%";
+#endif
+
+#include <stdio.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <grp.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "pathnames.h"
+
+ /* Delimiters for fields and for lists of users, ttys or hosts. */
+
+static char fs[] = ":"; /* field separator */
+static char sep[] = ", \t"; /* list-element separator */
+
+ /* Constants to be used in assignments only, not in comparisons... */
+
+#define YES 1
+#define NO 0
+
+static int list_match();
+static int user_match();
+static int from_match();
+static int string_match();
+
+/* login_access - match username/group and host/tty with access control file */
+
+login_access(user, from)
+char *user;
+char *from;
+{
+ FILE *fp;
+ char line[BUFSIZ];
+ char *perm; /* becomes permission field */
+ char *users; /* becomes list of login names */
+ char *froms; /* becomes list of terminals or hosts */
+ int match = NO;
+ int end;
+ int lineno = 0; /* for diagnostics */
+
+ /*
+ * Process the table one line at a time and stop at the first match.
+ * Blank lines and lines that begin with a '#' character are ignored.
+ * Non-comment lines are broken at the ':' character. All fields are
+ * mandatory. The first field should be a "+" or "-" character. A
+ * non-existing table means no access control.
+ */
+
+ if (fp = fopen(_PATH_LOGACCESS, "r")) {
+ while (!match && fgets(line, sizeof(line), fp)) {
+ lineno++;
+ if (line[end = strlen(line) - 1] != '\n') {
+ syslog(LOG_ERR, "%s: line %d: missing newline or line too long",
+ _PATH_LOGACCESS, lineno);
+ continue;
+ }
+ if (line[0] == '#')
+ continue; /* comment line */
+ while (end > 0 && isspace(line[end - 1]))
+ end--;
+ line[end] = 0; /* strip trailing whitespace */
+ if (line[0] == 0) /* skip blank lines */
+ continue;
+ if (!(perm = strtok(line, fs))
+ || !(users = strtok((char *) 0, fs))
+ || !(froms = strtok((char *) 0, fs))
+ || strtok((char *) 0, fs)) {
+ syslog(LOG_ERR, "%s: line %d: bad field count", _PATH_LOGACCESS,
+ lineno);
+ continue;
+ }
+ if (perm[0] != '+' && perm[0] != '-') {
+ syslog(LOG_ERR, "%s: line %d: bad first field", _PATH_LOGACCESS,
+ lineno);
+ continue;
+ }
+ match = (list_match(froms, from, from_match)
+ && list_match(users, user, user_match));
+ }
+ (void) fclose(fp);
+ } else if (errno != ENOENT) {
+ syslog(LOG_ERR, "cannot open %s: %m", _PATH_LOGACCESS);
+ }
+ return (match == 0 || (line[0] == '+'));
+}
+
+/* list_match - match an item against a list of tokens with exceptions */
+
+static int list_match(list, item, match_fn)
+char *list;
+char *item;
+int (*match_fn) ();
+{
+ char *tok;
+ int match = NO;
+
+ /*
+ * Process tokens one at a time. We have exhausted all possible matches
+ * when we reach an "EXCEPT" token or the end of the list. If we do find
+ * a match, look for an "EXCEPT" list and recurse to determine whether
+ * the match is affected by any exceptions.
+ */
+
+ for (tok = strtok(list, sep); tok != 0; tok = strtok((char *) 0, sep)) {
+ if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */
+ break;
+ if (match = (*match_fn) (tok, item)) /* YES */
+ break;
+ }
+ /* Process exceptions to matches. */
+
+ if (match != NO) {
+ while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT"))
+ /* VOID */ ;
+ if (tok == 0 || list_match((char *) 0, item, match_fn) == NO)
+ return (match);
+ }
+ return (NO);
+}
+
+/* netgroup_match - match group against machine or user */
+
+static int netgroup_match(group, machine, user)
+char *machine;
+char *user;
+{
+#ifdef NIS
+ static char *mydomain = 0;
+
+ if (mydomain == 0)
+ yp_get_default_domain(&mydomain);
+ return (innetgr(group, machine, user, mydomain));
+#else
+ syslog(LOG_ERR, "NIS netgroup support not configured");
+#endif
+}
+
+/* user_match - match a username against one token */
+
+static int user_match(tok, string)
+char *tok;
+char *string;
+{
+ struct group *group;
+ int i;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the username, or if
+ * the token is a group that contains the username.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, (char *) 0, string));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if (group = getgrnam(tok)) { /* try group membership */
+ for (i = 0; group->gr_mem[i]; i++)
+ if (strcasecmp(string, group->gr_mem[i]) == 0)
+ return (YES);
+ }
+ return (NO);
+}
+
+/* from_match - match a host or tty against a list of tokens */
+
+static int from_match(tok, string)
+char *tok;
+char *string;
+{
+ int tok_len;
+ int str_len;
+
+ /*
+ * If a token has the magic value "ALL" the match always succeeds. Return
+ * YES if the token fully matches the string. If the token is a domain
+ * name, return YES if it matches the last fields of the string. If the
+ * token has the magic value "LOCAL", return YES if the string does not
+ * contain a "." character. If the token is a network number, return YES
+ * if it matches the head of the string.
+ */
+
+ if (tok[0] == '@') { /* netgroup */
+ return (netgroup_match(tok + 1, string, (char *) 0));
+ } else if (string_match(tok, string)) { /* ALL or exact match */
+ return (YES);
+ } else if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(string)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, string + str_len - tok_len) == 0)
+ return (YES);
+ } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */
+ if (strchr(string, '.') == 0)
+ return (YES);
+ } else if (tok[(tok_len = strlen(tok)) - 1] == '.' /* network */
+ && strncmp(tok, string, tok_len) == 0) {
+ return (YES);
+ }
+ return (NO);
+}
+
+/* string_match - match a string against one token */
+
+static int string_match(tok, string)
+char *tok;
+char *string;
+{
+
+ /*
+ * If the token has the magic value "ALL" the match always succeeds.
+ * Otherwise, return YES if the token fully matches the string.
+ */
+
+ if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
+ return (YES);
+ } else if (strcasecmp(tok, string) == 0) { /* try exact match */
+ return (YES);
+ }
+ return (NO);
+}
+#endif /* LOGIN_ACCES */
diff --git a/usr.bin/login/login_fbtab.c b/usr.bin/login/login_fbtab.c
new file mode 100644
index 0000000..37cfe7a
--- /dev/null
+++ b/usr.bin/login/login_fbtab.c
@@ -0,0 +1,153 @@
+/************************************************************************
+* Copyright 1995 by Wietse Venema. All rights reserved.
+*
+* This material was originally written and compiled by Wietse Venema at
+* Eindhoven University of Technology, The Netherlands, in 1990, 1991,
+* 1992, 1993, 1994 and 1995.
+*
+* Redistribution and use in source and binary forms are permitted
+* provided that this entire copyright notice is duplicated in all such
+* copies.
+*
+* This software is provided "as is" and without any expressed or implied
+* warranties, including, without limitation, the implied warranties of
+* merchantibility and fitness for any particular purpose.
+************************************************************************/
+/*
+ SYNOPSIS
+ void login_fbtab(tty, uid, gid)
+ char *tty;
+ uid_t uid;
+ gid_t gid;
+
+ DESCRIPTION
+ This module implements device security as described in the
+ SunOS 4.1.x fbtab(5) and SunOS 5.x logindevperm(4) manual
+ pages. The program first looks for /etc/fbtab. If that file
+ cannot be opened it attempts to process /etc/logindevperm.
+ We expect entries with the folowing format:
+
+ Comments start with a # and extend to the end of the line.
+
+ Blank lines or lines with only a comment are ignored.
+
+ All other lines consist of three fields delimited by
+ whitespace: a login device (/dev/console), an octal
+ permission number (0600), and a ":"-delimited list of
+ devices (/dev/kbd:/dev/mouse). All device names are
+ absolute paths. A path that ends in "/*" refers to all
+ directory entries except "." and "..".
+
+ If the tty argument (relative path) matches a login device
+ name (absolute path), the permissions of the devices in the
+ ":"-delimited list are set as specified in the second
+ field, and their ownership is changed to that of the uid
+ and gid arguments.
+
+ DIAGNOSTICS
+ Problems are reported via the syslog daemon with severity
+ LOG_ERR.
+
+ BUGS
+ This module uses strtok(3), which may cause conflicts with other
+ uses of that same routine.
+
+ AUTHOR
+ Wietse Venema (wietse@wzv.win.tue.nl)
+ Eindhoven University of Technology
+ The Netherlands
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include "pathnames.h"
+
+void login_protect __P((char *, char *, int, uid_t, gid_t));
+void login_fbtab __P((char *tty, uid_t uid, gid_t gid));
+
+#define WSPACE " \t\n"
+
+/* login_fbtab - apply protections specified in /etc/fbtab or logindevperm */
+
+void
+login_fbtab(tty, uid, gid)
+char *tty;
+uid_t uid;
+gid_t gid;
+{
+ FILE *fp;
+ char buf[BUFSIZ];
+ char *devname;
+ char *cp;
+ int prot;
+ char *table;
+
+ if ((fp = fopen(table = _PATH_FBTAB, "r")) == 0
+ && (fp = fopen(table = _PATH_LOGINDEVPERM, "r")) == 0)
+ return;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (cp = strchr(buf, '#'))
+ *cp = 0; /* strip comment */
+ if ((cp = devname = strtok(buf, WSPACE)) == 0)
+ continue; /* empty or comment */
+ if (strncmp(devname, "/dev/", 5) != 0
+ || (cp = strtok((char *) 0, WSPACE)) == 0
+ || *cp != '0'
+ || sscanf(cp, "%o", &prot) == 0
+ || prot == 0
+ || (prot & 0777) != prot
+ || (cp = strtok((char *) 0, WSPACE)) == 0) {
+ syslog(LOG_ERR, "%s: bad entry: %s", table, cp ? cp : "(null)");
+ continue;
+ }
+ if (strcmp(devname + 5, tty) == 0) {
+ for (cp = strtok(cp, ":"); cp; cp = strtok((char *) 0, ":")) {
+ login_protect(table, cp, prot, uid, gid);
+ }
+ }
+ }
+ fclose(fp);
+}
+
+/* login_protect - protect one device entry */
+
+void
+login_protect(table, path, mask, uid, gid)
+char *table;
+char *path;
+int mask;
+uid_t uid;
+gid_t gid;
+{
+ char buf[BUFSIZ];
+ int pathlen = strlen(path);
+ struct dirent *ent;
+ DIR *dir;
+
+ if (strcmp("/*", path + pathlen - 2) != 0) {
+ if (chmod(path, mask) && errno != ENOENT)
+ syslog(LOG_ERR, "%s: chmod(%s): %m", table, path);
+ if (chown(path, uid, gid) && errno != ENOENT)
+ syslog(LOG_ERR, "%s: chown(%s): %m", table, path);
+ } else {
+ strcpy(buf, path);
+ buf[pathlen - 1] = 0;
+ if ((dir = opendir(buf)) == 0) {
+ syslog(LOG_ERR, "%s: opendir(%s): %m", table, path);
+ } else {
+ while ((ent = readdir(dir)) != 0) {
+ if (strcmp(ent->d_name, ".") != 0
+ && strcmp(ent->d_name, "..") != 0) {
+ strcpy(buf + pathlen - 1, ent->d_name);
+ login_protect(table, buf, mask, uid, gid);
+ }
+ }
+ closedir(dir);
+ }
+ }
+}
diff --git a/usr.bin/login/login_skey.c b/usr.bin/login/login_skey.c
new file mode 100644
index 0000000..b94bd28
--- /dev/null
+++ b/usr.bin/login/login_skey.c
@@ -0,0 +1,105 @@
+ /* Portions taken from the skey distribution on Oct 21 1993 */
+#ifdef SKEY
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <termios.h>
+#include <pwd.h>
+#include <syslog.h>
+
+#include <skey.h>
+
+/* skey_getpass - read regular or s/key password */
+
+char *skey_getpass(prompt, pwd, pwok)
+char *prompt;
+struct passwd *pwd;
+int pwok;
+{
+ static char buf[128];
+ struct skey skey;
+ char *cp;
+ void rip();
+ struct termios saved_ttymode;
+ struct termios noecho_ttymode;
+ char *username = pwd ? pwd->pw_name : "nope";
+ int sflag;
+
+ /* Attempt an s/key challenge. */
+
+ if ((sflag = skeychallenge(&skey, username, buf)) == 0) {
+ printf("%s\n", buf);
+ }
+ if (!pwok) {
+ printf("(s/key required)\n");
+ }
+ fputs(prompt, stdout);
+ fflush(stdout);
+
+ /* Save current input modes and turn echo off. */
+
+ tcgetattr(0, &saved_ttymode);
+ tcgetattr(0, &noecho_ttymode);
+ noecho_ttymode.c_lflag &= ~ECHO;
+ tcsetattr(0, TCSANOW, &noecho_ttymode);
+
+ /* Read password. */
+
+ buf[0] = 0;
+ fgets(buf, sizeof(buf), stdin);
+ rip(buf);
+
+ /* Restore previous input modes. */
+
+ tcsetattr(0, TCSANOW, &saved_ttymode);
+
+ /* Give S/Key users a chance to do it with echo on. */
+
+ if (sflag == 0 && feof(stdin) == 0 && buf[0] == 0) {
+ fputs(" (turning echo on)\n", stdout);
+ fputs(prompt, stdout);
+ fflush(stdout);
+ fgets(buf, sizeof(buf), stdin);
+ rip(buf);
+ } else {
+ putchar('\n');
+ }
+ return (buf);
+}
+
+/* skey_crypt - return encrypted UNIX passwd if s/key or regular password ok */
+
+char *skey_crypt(pp, salt, pwd, pwok)
+char *pp;
+char *salt;
+struct passwd *pwd;
+int pwok;
+{
+ struct skey skey;
+ char *p;
+ char *crypt();
+
+ /* Try s/key authentication even when the UNIX password is permitted. */
+
+ if (pwd != 0 && skeylookup(&skey, pwd->pw_name) == 0
+ && skeyverify(&skey, pp) == 0) {
+ /* s/key authentication succeeded */
+ if (skey.n < 5)
+ printf("Warning! Change s/key password soon\n");
+ return (pwd->pw_passwd);
+ }
+
+ /* When s/key authentication does not work, always invoke crypt(). */
+
+ p = crypt(pp, salt);
+ if (pwok && pwd != 0 && strcmp(p, pwd->pw_passwd) == 0)
+ return (pwd->pw_passwd);
+
+ /* The user does not exist or entered bad input. */
+
+ return (":");
+}
+#endif SKEY
diff --git a/usr.bin/login/pathnames.h b/usr.bin/login/pathnames.h
new file mode 100644
index 0000000..9154012
--- /dev/null
+++ b/usr.bin/login/pathnames.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ */
+
+#include <paths.h>
+
+#define _PATH_HUSHLOGIN ".hushlogin"
+#define _PATH_MOTDFILE "/etc/motd"
+#define _PATH_LOGACCESS "/etc/login.access"
+#define _PATH_FBTAB "/etc/fbtab"
+#define _PATH_LOGINDEVPERM "/etc/logindevperm"
+#define _PATH_CHPASS "/usr/bin/passwd"
diff --git a/usr.bin/logname/Makefile b/usr.bin/logname/Makefile
new file mode 100644
index 0000000..b8471ce
--- /dev/null
+++ b/usr.bin/logname/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= logname
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/logname/logname.1 b/usr.bin/logname/logname.1
new file mode 100644
index 0000000..fbcdb3c
--- /dev/null
+++ b/usr.bin/logname/logname.1
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)logname.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd "June 9, 1993"
+.Dt LOGNAME 1
+.Os BSD 4.4
+.Sh NAME
+.Nm logname
+.Nd display user's login name
+.Sh SYNOPSIS
+.Nm logname
+.Sh DESCRIPTION
+The
+.Nm logname
+utility writes the user's login name to standard output followed by
+a newline.
+.Pp
+The
+.Nm logname
+utility explicitly ignores the
+.Ev LOGNAME
+and
+.Ev USER
+environment variables
+because the environment cannot be trusted.
+.Pp
+The
+.Nm logname
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr who 1 ,
+.Xr whoami 1 ,
+.Xr getlogin 3
+.Sh STANDARDS
+The
+.Nm logname
+function is expected to conform to
+.St -p1003.2 .
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/logname/logname.c b/usr.bin/logname/logname.c
new file mode 100644
index 0000000..98b948a
--- /dev/null
+++ b/usr.bin/logname/logname.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)logname.c 8.2 (Berkeley) 4/3/94";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *p;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((p = getlogin()) == NULL)
+ err(1, NULL);
+ (void)printf("%s\n", p);
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: logname\n");
+ exit(1);
+}
diff --git a/usr.bin/look/Makefile b/usr.bin/look/Makefile
new file mode 100644
index 0000000..64d7788
--- /dev/null
+++ b/usr.bin/look/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= look
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/look/look.1 b/usr.bin/look/look.1
new file mode 100644
index 0000000..3d8a247
--- /dev/null
+++ b/usr.bin/look/look.1
@@ -0,0 +1,104 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)look.1 8.1 (Berkeley) 6/14/93
+.\"
+.Dd June 14, 1993
+.Dt LOOK 1
+.Os
+.Sh NAME
+.Nm look
+.Nd display lines beginning with a given string
+.Sh SYNOPSIS
+.Nm look
+.Op Fl df
+.Op Fl t Ar termchar
+.Ar string
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm look
+utility displays any lines in
+.Ar file
+which contain
+.Ar string
+as a prefix.
+As
+.Nm look
+performs a binary search, the lines in
+.Ar file
+must be sorted.
+.Pp
+If
+.Ar file
+is not specified, the file
+.Pa /usr/share/dict/words
+is used, only alphanumeric characters are compared and the case of
+alphabetic characters is ignored.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl d
+Dictionary character set and order, i.e. only alphanumeric characters
+are compared.
+.It Fl f
+Ignore the case of alphabetic characters.
+.It Fl t
+Specify a string termination character, i.e. only the characters
+in
+.Ar string
+up to and including the first occurrence of
+.Ar termchar
+are compared.
+.El
+.Pp
+The
+.Nm look
+utility exits 0 if one or more lines were found and displayed,
+1 if no lines were found, and >1 if an error occurred.
+.Sh FILES
+.Bl -tag -width /usr/share/dict/words -compact
+.It Pa /usr/share/dict/words
+the dictionary
+.El
+.Sh SEE ALSO
+.Xr grep 1 ,
+.Xr sort 1
+.Sh COMPATIBILITY
+The original manual page stated that tabs and blank characters participated
+in comparisons when the
+.Fl d
+option was specified.
+This was incorrect and the current man page matches the historic
+implementation.
+.Sh HISTORY
+.Nm Look
+appeared in Version 7 AT&T Unix.
diff --git a/usr.bin/look/look.c b/usr.bin/look/look.c
new file mode 100644
index 0000000..be16909
--- /dev/null
+++ b/usr.bin/look/look.c
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)look.c 8.1 (Berkeley) 6/14/93";
+#endif /* not lint */
+
+/*
+ * look -- find lines in a sorted list.
+ *
+ * The man page said that TABs and SPACEs participate in -d comparisons.
+ * In fact, they were ignored. This implements historic practice, not
+ * the manual page.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "pathnames.h"
+
+/*
+ * FOLD and DICT convert characters to a normal form for comparison,
+ * according to the user specified flags.
+ *
+ * DICT expects integers because it uses a non-character value to
+ * indicate a character which should not participate in comparisons.
+ */
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+#define NO_COMPARE (-2)
+
+#define FOLD(c) (isascii(c) && isupper(c) ? tolower(c) : (c))
+#define DICT(c) (isascii(c) && isalnum(c) ? (c) : NO_COMPARE)
+
+int dflag, fflag;
+
+char *binary_search __P((char *, char *, char *));
+int compare __P((char *, char *, char *));
+void err __P((const char *fmt, ...));
+char *linear_search __P((char *, char *, char *));
+int look __P((char *, char *, char *));
+void print_from __P((char *, char *, char *));
+
+static void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ int ch, fd, termchar;
+ char *back, *file, *front, *string, *p;
+
+ file = _PATH_WORDS;
+ termchar = '\0';
+ while ((ch = getopt(argc, argv, "dft:")) != EOF)
+ switch(ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 't':
+ termchar = *optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 2: /* Don't set -df for user. */
+ string = *argv++;
+ file = *argv;
+ break;
+ case 1: /* But set -df by default. */
+ dflag = fflag = 1;
+ string = *argv;
+ break;
+ default:
+ usage();
+ }
+
+ if (termchar != '\0' && (p = strchr(string, termchar)) != NULL)
+ *++p = '\0';
+
+ if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ err("%s: %s", file, strerror(errno));
+ if (sb.st_size > SIZE_T_MAX)
+ err("%s: %s", file, strerror(EFBIG));
+ if ((front = mmap(NULL,
+ (size_t)sb.st_size, PROT_READ, 0, fd, (off_t)0)) == NULL)
+ err("%s: %s", file, strerror(errno));
+ back = front + sb.st_size;
+ exit(look(string, front, back));
+}
+
+look(string, front, back)
+ char *string, *front, *back;
+{
+ register int ch;
+ register char *readp, *writep;
+
+ /* Reformat string string to avoid doing it multiple times later. */
+ for (readp = writep = string; ch = *readp++;) {
+ if (fflag)
+ ch = FOLD(ch);
+ if (dflag)
+ ch = DICT(ch);
+ if (ch != NO_COMPARE)
+ *(writep++) = ch;
+ }
+ *writep = '\0';
+
+ front = binary_search(string, front, back);
+ front = linear_search(string, front, back);
+
+ if (front)
+ print_from(string, front, back);
+ return (front ? 0 : 1);
+}
+
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Base of the Invariants.
+ * front = NULL;
+ * back = EOF;
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) \
+ while (p < back && *p++ != '\n');
+
+char *
+binary_search(string, front, back)
+ register char *string, *front, *back;
+{
+ register char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ /*
+ * If the file changes underneath us, make sure we don't
+ * infinitely loop.
+ */
+ while (p < back && back > front) {
+ if (compare(string, p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+char *
+linear_search(string, front, back)
+ char *string, *front, *back;
+{
+ while (front < back) {
+ switch (compare(string, front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ break;
+ case LESS: /* No such string. */
+ return (NULL);
+ break;
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Print as many lines as match string, starting at front.
+ */
+void
+print_from(string, front, back)
+ register char *string, *front, *back;
+{
+ for (; front < back && compare(string, front, back) == EQUAL; ++front) {
+ for (; front < back && *front != '\n'; ++front)
+ if (putchar(*front) == EOF)
+ err("stdout: %s", strerror(errno));
+ if (putchar('\n') == EOF)
+ err("stdout: %s", strerror(errno));
+ }
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares with
+ * string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * Compare understands about the -f and -d flags, and treats comparisons
+ * appropriately.
+ *
+ * The string "s1" is null terminated. The string s2 is '\n' terminated (or
+ * "back" terminated).
+ */
+int
+compare(s1, s2, back)
+ register char *s1, *s2, *back;
+{
+ register int ch;
+
+ for (; *s1 && s2 < back && *s2 != '\n'; ++s1, ++s2) {
+ ch = *s2;
+ if (fflag)
+ ch = FOLD(ch);
+ if (dflag)
+ ch = DICT(ch);
+
+ if (ch == NO_COMPARE) {
+ ++s2; /* Ignore character in comparison. */
+ continue;
+ }
+ if (*s1 != ch)
+ return (*s1 < ch ? LESS : GREATER);
+ }
+ return (*s1 ? GREATER : EQUAL);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: look [-df] [-t char] string [file]\n");
+ exit(2);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "look: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(2);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/look/pathnames.h b/usr.bin/look/pathnames.h
new file mode 100644
index 0000000..586e36a
--- /dev/null
+++ b/usr.bin/look/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ */
+
+#define _PATH_WORDS "/usr/share/dict/words"
diff --git a/usr.bin/lorder/Makefile b/usr.bin/lorder/Makefile
new file mode 100644
index 0000000..a1682d1
--- /dev/null
+++ b/usr.bin/lorder/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+MAN1= lorder.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/lorder.sh ${DESTDIR}${BINDIR}/lorder
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lorder/lorder.1 b/usr.bin/lorder/lorder.1
new file mode 100644
index 0000000..b5bfd57
--- /dev/null
+++ b/usr.bin/lorder/lorder.1
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lorder.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LORDER 1
+.Os
+.Sh NAME
+.Nm lorder
+.Nd list dependencies for object files
+.Sh SYNOPSIS
+.Nm lorder
+.Ar
+.Sh DESCRIPTION
+The
+.Nm lorder
+utility uses
+.Xr nm 1
+to determine interdependencies in the list of object files
+specified on the command line.
+.Nm Lorder
+outputs a list of file names where the first file contains a symbol
+which is defined by the second file.
+.Pp
+The output is normally used with
+.Xr tsort 1
+when a library is created to determine the optimum ordering of the
+object modules so that all references may be resolved in a single
+pass of the loader.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+ar cr library.a `lorder ${OBJS} tsort`
+.Ed
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ld 1 ,
+.Xr nm 1 ,
+.Xr ranlib 1 ,
+.Xr tsort 1
+.Sh HISTORY
+An
+.Nm lorder
+utility appeared in
+.At v7 .
diff --git a/usr.bin/lorder/lorder.sh b/usr.bin/lorder/lorder.sh
new file mode 100644
index 0000000..dea1e10
--- /dev/null
+++ b/usr.bin/lorder/lorder.sh
@@ -0,0 +1,89 @@
+#!/bin/sh -
+#
+# Copyright (c) 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)lorder.sh 8.1 (Berkeley) 6/6/93
+#
+
+PATH=/bin:/usr/bin
+export PATH
+
+# only one argument is a special case, just output the name twice
+case $# in
+ 0)
+ echo "usage: lorder file ...";
+ exit ;;
+ 1)
+ echo $1 $1;
+ exit ;;
+esac
+
+# temporary files
+R=/tmp/_reference_$$
+S=/tmp/_symbol_$$
+
+# remove temporary files on HUP, INT, QUIT, PIPE, TERM
+trap "rm -f $R $S; exit 1" 1 2 3 13 15
+
+# if the line ends in a colon, assume it's the first occurrence of a new
+# object file. Echo it twice, just to make sure it gets into the output.
+#
+# if the line has " T " or " D " it's a globally defined symbol, put it
+# into the symbol file.
+#
+# if the line has " U " it's a globally undefined symbol, put it into
+# the reference file.
+nm -go $* | sed "
+ /:$/ {
+ s/://
+ s/.*/& &/
+ p
+ d
+ }
+ / [TD] / {
+ s/:.* [TD] / /
+ w $S
+ d
+ }
+ / U / {
+ s/:.* U / /
+ w $R
+ }
+ d
+"
+
+# sort symbols and references on the first field (the symbol)
+# join on that field, and print out the file names.
+sort +1 $R -o $R
+sort +1 $S -o $S
+join -j 2 -o 1.1 2.1 $R $S
+rm -f $R $S
diff --git a/usr.bin/lsvfs/Makefile b/usr.bin/lsvfs/Makefile
new file mode 100644
index 0000000..3b25443
--- /dev/null
+++ b/usr.bin/lsvfs/Makefile
@@ -0,0 +1,4 @@
+# $Id$
+PROG= lsvfs
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/lsvfs/lsvfs.1 b/usr.bin/lsvfs/lsvfs.1
new file mode 100644
index 0000000..35ac03b
--- /dev/null
+++ b/usr.bin/lsvfs/lsvfs.1
@@ -0,0 +1,55 @@
+.\" $Id: lsvfs.1,v 1.1 1994/09/22 01:25:56 wollman Exp $
+.\" Garrett A. Wollman, September 1994
+.\" This file is in the public domain.
+.\"
+.Dd March 16, 1995
+.Dt LSVFS 1
+.Os
+.Sh NAME
+.Nm lsvfs
+.Nd list installed virtual file systems
+.Sh SYNOPSIS
+.Nm lsvfs
+.Op Ar vfsname Ar ...
+.Sh DESCRIPTION
+The
+.Nm lsvfs
+command lists information about the currently loaded virtual filesystem
+modules. When
+.Ar vfsname
+arguments are given,
+.Nm lsvfs
+lists information about the specified VFS modules. Otherwise,
+.Nm lsvfs
+lists all currently loaded modules.
+The information is as follows:
+.Pp
+.Bl -tag -compact -width Filesystem
+.It Filesystem
+the name of the filesystem, as would be used in the
+.Fl t
+option to
+.Xr mount 8
+.It Index
+the kernel filesystem switch slot number for this filesystem, as would be
+used in the
+.Ar type
+parameter to
+.Xr mount 2
+.It Refs
+the number of references to this VFS; i.e., the number of currently
+mounted filesystems of this type
+.It Flags
+flag bits (only
+.Dq static
+is currently defined).
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr mount 8
+.Sh HISTORY
+A
+.Nm
+comand appeared in
+.Tn FreeBSD
+2.0.
diff --git a/usr.bin/lsvfs/lsvfs.c b/usr.bin/lsvfs/lsvfs.c
new file mode 100644
index 0000000..ccfc038
--- /dev/null
+++ b/usr.bin/lsvfs/lsvfs.c
@@ -0,0 +1,93 @@
+/*
+ * lsvfs - lsit loaded VFSes
+ * Garrett A. Wollman, September 1994
+ * This file is in the public domain.
+ *
+ * $Id: lsvfs.c,v 1.4 1995/03/16 20:29:11 wollman Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <stdio.h>
+#include <err.h>
+
+#define FMT "%-32.32s %5d %5d %s\n"
+#define HDRFMT "%-32.32s %5.5s %5.5s %s\n"
+#define DASHES "-------------------------------- ----- ----- ---------------\n"
+
+static const char *fmt_flags(int);
+
+int
+main(int argc, char **argv)
+{
+ int rv = 0;
+ struct vfsconf *vfc;
+ argc--, argv++;
+
+ setvfsent(1);
+
+ printf(HDRFMT, "Filesystem", "Index", "Refs", "Flags");
+ fputs(DASHES, stdout);
+
+ if(argc) {
+ for(; argc; argc--, argv++) {
+ vfc = getvfsbyname(*argv);
+ if(vfc) {
+ printf(FMT, vfc->vfc_name, vfc->vfc_index, vfc->vfc_refcount,
+ fmt_flags(vfc->vfc_flags));
+ } else {
+ warnx("VFS %s unknown or not loaded", *argv);
+ rv++;
+ }
+ }
+ } else {
+ while(vfc = getvfsent()) {
+ printf(FMT, vfc->vfc_name, vfc->vfc_index, vfc->vfc_refcount,
+ fmt_flags(vfc->vfc_flags));
+ }
+ }
+
+ endvfsent();
+ return rv;
+}
+
+static const char *
+fmt_flags(int flags)
+{
+ /*
+ * NB: if you add new flags, don't forget to add them here vvvvvv too.
+ */
+ static char buf[sizeof "static, network, read-only, synthetic, loopback"];
+ int comma = 0;
+
+ buf[0] = '\0';
+
+ if(flags & VFCF_STATIC) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "static");
+ }
+
+ if(flags & VFCF_NETWORK) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "network");
+ }
+
+ if(flags & VFCF_READONLY) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "read-only");
+ }
+
+ if(flags & VFCF_SYNTHETIC) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "synthetic");
+ }
+
+ if(flags & VFCF_LOOPBACK) {
+ if(comma++) strcat(buf, ", ");
+ strcat(buf, "loopback");
+ }
+
+ return buf;
+}
+
diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile
new file mode 100644
index 0000000..7a57e83
--- /dev/null
+++ b/usr.bin/m4/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+# -DEXTENDED
+# if you want the paste & spaste macros.
+
+PROG= m4
+CFLAGS+=-DEXTENDED
+SRCS= eval.c expr.c look.c main.c misc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/m4/NOTES b/usr.bin/m4/NOTES
new file mode 100644
index 0000000..d60f80e
--- /dev/null
+++ b/usr.bin/m4/NOTES
@@ -0,0 +1,64 @@
+m4 - macro processor
+
+PD m4 is based on the macro tool distributed with the software
+tools (VOS) package, and described in the "SOFTWARE TOOLS" and
+"SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include
+most of the command set of SysV m4, the standard UN*X macro processor.
+
+Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
+there may be certain implementation similarities between
+the two. The PD m4 was produced without ANY references to m4
+sources.
+
+written by: Ozan S. Yigit
+
+References:
+
+ Software Tools distribution: macro
+
+ Kernighan, Brian W. and P. J. Plauger, SOFTWARE
+ TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
+
+ Kernighan, Brian W. and P. J. Plauger, SOFTWARE
+ TOOLS, Addison-Wesley, Mass. 1976
+
+ Kernighan, Brian W. and Dennis M. Ritchie,
+ THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
+ Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
+
+ System V man page for M4
+
+
+Implementation Notes:
+
+[1] PD m4 uses a different (and simpler) stack mechanism than the one
+ described in Software Tools and Software Tools in Pascal books.
+ The triple stack thing is replaced with a single stack containing
+ the call frames and the arguments. Each frame is back-linked to a
+ previous stack frame, which enables us to rewind the stack after
+ each nested call is completed. Each argument is a character pointer
+ to the beginning of the argument string within the string space.
+ The only exceptions to this are (*) arg 0 and arg 1, which are
+ the macro definition and macro name strings, stored dynamically
+ for the hash table.
+
+ . .
+ | . | <-- sp | . |
+ +-------+ +-----+
+ | arg 3 ------------------------------->| str |
+ +-------+ | . |
+ | arg 2 --------------+ .
+ +-------+ |
+ * | | |
+ +-------+ | +-----+
+ | plev | <-- fp +---------------->| str |
+ +-------+ | . |
+ | type | .
+ +-------+
+ | prcf -----------+ plev: paren level
+ +-------+ | type: call type
+ | . | | prcf: prev. call frame
+ . |
+ +-------+ |
+ | <----------+
+ +-------+
diff --git a/usr.bin/m4/PSD.doc/Makefile b/usr.bin/m4/PSD.doc/Makefile
new file mode 100644
index 0000000..0613e08
--- /dev/null
+++ b/usr.bin/m4/PSD.doc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= psd/17.m4
+SRCS= m4.ms
+MACROS= -msU
+
+paper.ps: ${SRCS}
+ ${ROFF} ${SRCS} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/m4/TEST/ack.m4 b/usr.bin/m4/TEST/ack.m4
new file mode 100644
index 0000000..de914bd
--- /dev/null
+++ b/usr.bin/m4/TEST/ack.m4
@@ -0,0 +1,40 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)ack.m4 8.1 (Berkeley) 6/6/93
+#
+
+define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)',
+`ack(DECR($1), ack($1,DECR($2)))')')
diff --git a/usr.bin/m4/TEST/hanoi.m4 b/usr.bin/m4/TEST/hanoi.m4
new file mode 100644
index 0000000..9371b34
--- /dev/null
+++ b/usr.bin/m4/TEST/hanoi.m4
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)hanoi.m4 8.1 (Berkeley) 6/6/93
+#
+
+define(hanoi, `trans(A, B, C, $1)')
+
+define(moved,`move disk from $1 to $2
+')
+
+define(trans, `ifelse($4,1,`moved($1,$2)',
+ `trans($1,$3,$2,DECR($4))moved($1,$2)trans($3,$2,$1,DECR($4))')')
diff --git a/usr.bin/m4/TEST/hash.m4 b/usr.bin/m4/TEST/hash.m4
new file mode 100644
index 0000000..85d5aa8
--- /dev/null
+++ b/usr.bin/m4/TEST/hash.m4
@@ -0,0 +1,55 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)hash.m4 8.1 (Berkeley) 6/6/93
+#
+
+dnl This probably will not run on any m4 that cannot
+dnl handle char constants in eval.
+dnl
+changequote(<,>) define(HASHVAL,99) dnl
+define(hash,<eval(str(substr($1,1),0)%HASHVAL)>) dnl
+define(str,
+ <ifelse($1,",$2,
+ <str(substr(<$1>,1),<eval($2+'substr($1,0,1)')>)>)
+ >) dnl
+define(KEYWORD,<$1,hash($1),>) dnl
+define(TSTART,
+<struct prehash {
+ char *keyword;
+ int hashval;
+} keytab[] = {>) dnl
+define(TEND,< "",0
+};>) dnl
diff --git a/usr.bin/m4/TEST/sqroot.m4 b/usr.bin/m4/TEST/sqroot.m4
new file mode 100644
index 0000000..3c7501f
--- /dev/null
+++ b/usr.bin/m4/TEST/sqroot.m4
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)sqroot.m4 8.1 (Berkeley) 6/6/93
+#
+
+define(square_root,
+ `ifelse(eval($1<0),1,negative-square-root,
+ `square_root_aux($1, 1, eval(($1+1)/2))')')
+define(square_root_aux,
+ `ifelse($3, $2, $3,
+ $3, eval($1/$2), $3,
+ `square_root_aux($1, $3, eval(($3+($1/$3))/2))')')
diff --git a/usr.bin/m4/TEST/string.m4 b/usr.bin/m4/TEST/string.m4
new file mode 100644
index 0000000..bff741a
--- /dev/null
+++ b/usr.bin/m4/TEST/string.m4
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)string.m4 8.1 (Berkeley) 6/6/93
+#
+
+define(string,`integer $1(len(substr($2,1)))
+str($1,substr($2,1),0)
+data $1(len(substr($2,1)))/EOS/
+')
+
+define(str,`ifelse($2,",,data $1(incr($3))/`LET'substr($2,0,1)/
+`str($1,substr($2,1),incr($3))')')
diff --git a/usr.bin/m4/TEST/test.m4 b/usr.bin/m4/TEST/test.m4
new file mode 100644
index 0000000..df8b78c
--- /dev/null
+++ b/usr.bin/m4/TEST/test.m4
@@ -0,0 +1,243 @@
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Ozan Yigit.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)test.m4 8.1 (Berkeley) 6/6/93
+#
+
+# test file for mp (not comprehensive)
+#
+# v7 m4 does not have `decr'.
+#
+define(DECR,`eval($1-1)')
+#
+# include string macros
+#
+include(string.m4)
+#
+# create some fortrash strings for an even uglier language
+#
+string(TEXT, "text")
+string(DATA, "data")
+string(BEGIN, "begin")
+string(END, "end")
+string(IF, "if")
+string(THEN, "then")
+string(ELSE, "else")
+string(CASE, "case")
+string(REPEAT, "repeat")
+string(WHILE, "while")
+string(DEFAULT, "default")
+string(UNTIL, "until")
+string(FUNCTION, "function")
+string(PROCEDURE, "procedure")
+string(EXTERNAL, "external")
+string(FORWARD, "forward")
+string(TYPE, "type")
+string(VAR, "var")
+string(CONST, "const")
+string(PROGRAM, "program")
+string(INPUT, "input")
+string(OUTPUT, "output")
+#
+divert(2)
+diversion #1
+divert(3)
+diversion #2
+divert(4)
+diversion #3
+divert(5)
+diversion #4
+divert(0)
+define(abc,xxx)
+ifdef(`abc',defined,undefined)
+#
+# v7 m4 does this wrong. The right output is
+# this is A vEry lon sEntEnCE
+# see m4 documentation for translit.
+#
+translit(`this is a very long sentence', abcdefg, ABCDEF)
+#
+# include towers-of-hanoi
+#
+include(hanoi.m4)
+#
+# some reasonable set of disks
+#
+hanoi(6)
+#
+# include ackermann's function
+#
+include(ack.m4)
+#
+# something like (3,3) will blow away un*x m4.
+#
+ack(2,3)
+#
+# include a square_root function for fixed nums
+#
+include(sqroot.m4)
+#
+# some square roots.
+#
+square_root(15)
+square_root(100)
+square_root(-4)
+square_root(21372)
+#
+# some textual material for enjoyment.
+#
+[taken from the 'Clemson University Computer Newsletter',
+ September 1981, pp. 6-7]
+
+I am a wizard in the magical Kingdom of Transformation and I
+slay dragons for a living. Actually, I am a systems programmer.
+One of the problems with systems programming is explaining to
+non-computer enthusiasts what that is. All of the terms I use to
+describe my job are totally meaningless to them. Usually my response
+to questions about my work is to say as little as possible. For
+instance, if someone asks what happened at work this week, I say
+"Nothing much" and then I change the subject.
+
+With the assistance of my brother, a mechanical engineer, I have devised
+an analogy that everyone can understand. The analogy describes the
+"Kingdom of Transformation" where travelers wander and are magically
+transformed. This kingdom is the computer and the travelers are information.
+The purpose of the computer is to change information to a more meaningful
+forma. The law of conservation applies here: The computer never creates
+and never intentionally destroys data. With no further ado, let us travel
+to the Kingdom of Transformation:
+
+In a land far, far away, there is a magical kingdom called the Kingdom of
+Transformation. A king rules over this land and employs a Council of
+Wizardry. The main purpose of this kingdom is to provide a way for
+neighboring kingdoms to transform citizens into more useful citizens. This
+is done by allowing the citizens to enter the kingdom at one of its ports
+and to travel any of the many routes in the kingdom. They are magically
+transformed along the way. The income of the Kingdom of Transformation
+comes from the many toll roads within its boundaries.
+
+The Kingdom of Transformation was created when several kingdoms got
+together and discovered a mutual need for new talents and abilities for
+citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to
+create this kingdom. CTK designed the country, its transportation routes,
+and its laws of transformation, and created the major highway system.
+
+Hazards
+=======
+
+Because magic is not truly controllable, CTK invariably, but unknowingly,
+creates dragons. Dragons are huge fire-breathing beasts which sometimes
+injure or kill travelers. Fortunately, they do not travel, but always
+remain near their den.
+
+Other hazards also exist which are potentially harmful. As the roads
+become older and more weatherbeaten, pot-holes will develop, trees will
+fall on travelers, etc. CTK maintenance men are called to fix these
+problems.
+
+Wizards
+=======
+
+The wizards play a major role in creating and maintaining the kingdom but
+get little credit for their work because it is performed secretly. The
+wizards do not wan the workers or travelers to learn their incantations
+because many laws would be broken and chaos would result.
+
+CTK's grand design is always general enough to be applicable in many
+different situations. As a result, it is often difficult to use. The
+first duty of the wizards is to tailor the transformation laws so as to be
+more beneficial and easier to use in their particular environment.
+
+After creation of the kingdom, a major duty of the wizards is to search for
+and kill dragons. If travelers do not return on time or if they return
+injured, the ruler of the country contacts the wizards. If the wizards
+determine that the injury or death occurred due to the traveler's
+negligence, they provide the traveler's country with additional warnings.
+If not, they must determine if the cause was a road hazard or a dragon. If
+the suspect a road hazard, they call in a CTK maintenance man to locate the
+hazard and to eliminate it, as in repairing the pothole in the road. If
+they think that cause was a dragon, then they must find and slay it.
+
+The most difficult part of eliminating a dragon is finding it. Sometimes
+the wizard magically knows where the dragon's lair it, but often the wizard
+must send another traveler along the same route and watch to see where he
+disappears. This sounds like a failsafe method for finding dragons (and a
+suicide mission for thr traveler) but the second traveler does not always
+disappear. Some dragons eat any traveler who comes too close; others are
+very picky.
+
+The wizards may call in CTK who designed the highway system and
+transformation laws to help devise a way to locate the dragon. CTK also
+helps provide the right spell or incantation to slay the dragon. (There is
+no general spell to slay dragons; each dragon must be eliminated with a
+different spell.)
+
+Because neither CTK nor wizards are perfect, spells to not always work
+correctly. At best, nothing happens when the wrong spell is uttered. At
+worst, the dragon becomes a much larger dragon or multiplies into several
+smaller ones. In either case, new spells must be found.
+
+If all existing dragons are quiet (i.e. have eaten sufficiently), wizards
+have time to do other things. They hide in castles and practice spells and
+incatations. They also devise shortcuts for travelers and new laws of
+transformation.
+
+Changes in the Kingdom
+======================
+
+As new transformation kingdoms are created and old ones are maintained,
+CTK, Inc. is constantly learning new things. It learns ways to avoid
+creating some of the dragons that they have previously created. It also
+discovers new and better laws of transformation. As a result, CTK will
+periodically create a new grand design which is far better than the old.
+The wizards determine when is a good time to implement this new design.
+This is when the tourist season is slow or when no important travelers
+(VIPs) are to arrive. The kingdom must be closed for the actual
+implementation and is leter reopened as a new and better place to go.
+
+A final question you might ask is what happens when the number of tourists
+becomes too great for the kingdom to handle in a reasonable period of time
+(i.e., the tourist lines at the ports are too long). The Kingdom of
+Transformation has three options: (1) shorten the paths that a tourist must
+travel, or (2) convince CTK to develop a faster breed of horses so that the
+travelers can finish sooner, or (3) annex more territories so that the
+kingdom can handle more travelers.
+
+Thus ends the story of the Kingdom of Transformation. I hope this has
+explained my job to you: I slay dragons for a living.
+
+#
+#should do an automatic undivert..
+#
diff --git a/usr.bin/m4/eval.c b/usr.bin/m4/eval.c
new file mode 100644
index 0000000..b1b2a39
--- /dev/null
+++ b/usr.bin/m4/eval.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)eval.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * eval.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * eval - evaluate built-in macros.
+ * argc - number of elements in argv.
+ * argv - element vector :
+ * argv[0] = definition of a user
+ * macro or nil if built-in.
+ * argv[1] = name of the macro or
+ * built-in.
+ * argv[2] = parameters to user-defined
+ * . macro or built-in.
+ * .
+ *
+ * Note that the minimum value for argc is 3. A call in the form
+ * of macro-or-builtin() will result in:
+ * argv[0] = nullstr
+ * argv[1] = macro-or-builtin
+ * argv[2] = nullstr
+ */
+
+void
+eval(argv, argc, td)
+register char *argv[];
+register int argc;
+register int td;
+{
+ register int c, n;
+ static int sysval = 0;
+
+#ifdef DEBUG
+ printf("argc = %d\n", argc);
+ for (n = 0; n < argc; n++)
+ printf("argv[%d] = %s\n", n, argv[n]);
+#endif
+ /*
+ * if argc == 3 and argv[2] is null, then we
+ * have macro-or-builtin() type call. We adjust
+ * argc to avoid further checking..
+ */
+ if (argc == 3 && !*(argv[2]))
+ argc--;
+
+ switch (td & ~STATIC) {
+
+ case DEFITYPE:
+ if (argc > 2)
+ dodefine(argv[2], (argc > 3) ? argv[3] : null);
+ break;
+
+ case PUSDTYPE:
+ if (argc > 2)
+ dopushdef(argv[2], (argc > 3) ? argv[3] : null);
+ break;
+
+ case DUMPTYPE:
+ dodump(argv, argc);
+ break;
+
+ case EXPRTYPE:
+ /*
+ * doexpr - evaluate arithmetic
+ * expression
+ */
+ if (argc > 2)
+ pbnum(expr(argv[2]));
+ break;
+
+ case IFELTYPE:
+ if (argc > 4)
+ doifelse(argv, argc);
+ break;
+
+ case IFDFTYPE:
+ /*
+ * doifdef - select one of two
+ * alternatives based on the existence of
+ * another definition
+ */
+ if (argc > 3) {
+ if (lookup(argv[2]) != nil)
+ pbstr(argv[3]);
+ else if (argc > 4)
+ pbstr(argv[4]);
+ }
+ break;
+
+ case LENGTYPE:
+ /*
+ * dolen - find the length of the
+ * argument
+ */
+ if (argc > 2)
+ pbnum((argc > 2) ? strlen(argv[2]) : 0);
+ break;
+
+ case INCRTYPE:
+ /*
+ * doincr - increment the value of the
+ * argument
+ */
+ if (argc > 2)
+ pbnum(atoi(argv[2]) + 1);
+ break;
+
+ case DECRTYPE:
+ /*
+ * dodecr - decrement the value of the
+ * argument
+ */
+ if (argc > 2)
+ pbnum(atoi(argv[2]) - 1);
+ break;
+
+ case SYSCTYPE:
+ /*
+ * dosys - execute system command
+ */
+ /* Make sure m4 output is NOT interrupted */
+ fflush(stdout);
+ fflush(stderr);
+ if (argc > 2)
+ sysval = system(argv[2]);
+ break;
+
+ case SYSVTYPE:
+ /*
+ * dosysval - return value of the last
+ * system call.
+ *
+ */
+ pbnum(sysval);
+ break;
+
+ case INCLTYPE:
+ if (argc > 2)
+ if (!doincl(argv[2]))
+ oops("%s: %s", argv[2], strerror(errno));
+ break;
+
+ case SINCTYPE:
+ if (argc > 2)
+ (void) doincl(argv[2]);
+ break;
+#ifdef EXTENDED
+ case PASTTYPE:
+ if (argc > 2)
+ if (!dopaste(argv[2]))
+ oops("%s: %s", argv[2], strerror(errno));
+ break;
+
+ case SPASTYPE:
+ if (argc > 2)
+ (void) dopaste(argv[2]);
+ break;
+#endif
+ case CHNQTYPE:
+ dochq(argv, argc);
+ break;
+
+ case CHNCTYPE:
+ dochc(argv, argc);
+ break;
+
+ case SUBSTYPE:
+ /*
+ * dosub - select substring
+ *
+ */
+ if (argc > 3)
+ dosub(argv, argc);
+ break;
+
+ case SHIFTYPE:
+ /*
+ * doshift - push back all arguments
+ * except the first one (i.e. skip
+ * argv[2])
+ */
+ if (argc > 3) {
+ for (n = argc - 1; n > 3; n--) {
+ putback(rquote);
+ pbstr(argv[n]);
+ putback(lquote);
+ putback(',');
+ }
+ putback(rquote);
+ pbstr(argv[3]);
+ putback(lquote);
+ }
+ break;
+
+ case DIVRTYPE:
+ if (argc > 2 && (n = atoi(argv[2])) != 0)
+ dodiv(n);
+ else {
+ active = stdout;
+ oindex = 0;
+ }
+ break;
+
+ case UNDVTYPE:
+ doundiv(argv, argc);
+ break;
+
+ case DIVNTYPE:
+ /*
+ * dodivnum - return the number of
+ * current output diversion
+ */
+ pbnum(oindex);
+ break;
+
+ case UNDFTYPE:
+ /*
+ * doundefine - undefine a previously
+ * defined macro(s) or m4 keyword(s).
+ */
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ remhash(argv[n], ALL);
+ break;
+
+ case POPDTYPE:
+ /*
+ * dopopdef - remove the topmost
+ * definitions of macro(s) or m4
+ * keyword(s).
+ */
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ remhash(argv[n], TOP);
+ break;
+
+ case MKTMTYPE:
+ /*
+ * dotemp - create a temporary file
+ */
+ if (argc > 2)
+ pbstr(mktemp(argv[2]));
+ break;
+
+ case TRNLTYPE:
+ /*
+ * dotranslit - replace all characters in
+ * the source string that appears in the
+ * "from" string with the corresponding
+ * characters in the "to" string.
+ */
+ if (argc > 3) {
+ char temp[MAXTOK];
+ if (argc > 4)
+ map(temp, argv[2], argv[3], argv[4]);
+ else
+ map(temp, argv[2], argv[3], null);
+ pbstr(temp);
+ }
+ else if (argc > 2)
+ pbstr(argv[2]);
+ break;
+
+ case INDXTYPE:
+ /*
+ * doindex - find the index of the second
+ * argument string in the first argument
+ * string. -1 if not present.
+ */
+ pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
+ break;
+
+ case ERRPTYPE:
+ /*
+ * doerrp - print the arguments to stderr
+ * file
+ */
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ fprintf(stderr, "%s ", argv[n]);
+ fprintf(stderr, "\n");
+ }
+ break;
+
+ case DNLNTYPE:
+ /*
+ * dodnl - eat-up-to and including
+ * newline
+ */
+ while ((c = gpbc()) != '\n' && c != EOF)
+ ;
+ break;
+
+ case M4WRTYPE:
+ /*
+ * dom4wrap - set up for
+ * wrap-up/wind-down activity
+ */
+ m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
+ break;
+
+ case EXITTYPE:
+ /*
+ * doexit - immediate exit from m4.
+ */
+ killdiv();
+ exit((argc > 2) ? atoi(argv[2]) : 0);
+ break;
+
+ case DEFNTYPE:
+ if (argc > 2)
+ for (n = 2; n < argc; n++)
+ dodefn(argv[n]);
+ break;
+
+ default:
+ oops("%s: major botch.", "eval");
+ break;
+ }
+}
+
+char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */
+
+/*
+ * expand - user-defined macro expansion
+ */
+void
+expand(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register unsigned char *t;
+ register unsigned char *p;
+ register int n;
+ register int argno;
+
+ t = argv[0]; /* defn string as a whole */
+ p = t;
+ while (*p)
+ p++;
+ p--; /* last character of defn */
+ while (p > t) {
+ if (*(p - 1) != ARGFLAG)
+ putback(*p);
+ else {
+ switch (*p) {
+
+ case '#':
+ pbnum(argc - 2);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((argno = *p - '0') < argc - 1)
+ pbstr(argv[argno + 1]);
+ break;
+ case '*':
+ for (n = argc - 1; n > 2; n--) {
+ pbstr(argv[n]);
+ putback(',');
+ }
+ pbstr(argv[2]);
+ break;
+ default:
+ putback(*p);
+ putback('$');
+ break;
+ }
+ p--;
+ }
+ p--;
+ }
+ if (p == t) /* do last character */
+ putback(*p);
+}
+
+/*
+ * dodefine - install definition in the table
+ */
+void
+dodefine(name, defn)
+register char *name;
+register char *defn;
+{
+ register ndptr p;
+
+ if (!*name)
+ oops("null definition.");
+ if (STREQ(name, defn))
+ oops("%s: recursive definition.", name);
+ if ((p = lookup(name)) == nil)
+ p = addent(name);
+ else if (p->defn != null)
+ free((char *) p->defn);
+ if (!*defn)
+ p->defn = null;
+ else
+ p->defn = xstrdup(defn);
+ p->type = MACRTYPE;
+}
+
+/*
+ * dodefn - push back a quoted definition of
+ * the given name.
+ */
+void
+dodefn(name)
+char *name;
+{
+ register ndptr p;
+
+ if ((p = lookup(name)) != nil && p->defn != null) {
+ putback(rquote);
+ pbstr(p->defn);
+ putback(lquote);
+ }
+}
+
+/*
+ * dopushdef - install a definition in the hash table
+ * without removing a previous definition. Since
+ * each new entry is entered in *front* of the
+ * hash bucket, it hides a previous definition from
+ * lookup.
+ */
+void
+dopushdef(name, defn)
+register char *name;
+register char *defn;
+{
+ register ndptr p;
+
+ if (!*name)
+ oops("null definition");
+ if (STREQ(name, defn))
+ oops("%s: recursive definition.", name);
+ p = addent(name);
+ if (!*defn)
+ p->defn = null;
+ else
+ p->defn = xstrdup(defn);
+ p->type = MACRTYPE;
+}
+
+/*
+ * dodumpdef - dump the specified definitions in the hash
+ * table to stderr. If nothing is specified, the entire
+ * hash table is dumped.
+ */
+void
+dodump(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register int n;
+ ndptr p;
+
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ if ((p = lookup(argv[n])) != nil)
+ fprintf(stderr, dumpfmt, p->name,
+ p->defn);
+ }
+ else {
+ for (n = 0; n < HASHSIZE; n++)
+ for (p = hashtab[n]; p != nil; p = p->nxtptr)
+ fprintf(stderr, dumpfmt, p->name,
+ p->defn);
+ }
+}
+
+/*
+ * doifelse - select one of two alternatives - loop.
+ */
+void
+doifelse(argv, argc)
+register char *argv[];
+register int argc;
+{
+ cycle {
+ if (STREQ(argv[2], argv[3]))
+ pbstr(argv[4]);
+ else if (argc == 6)
+ pbstr(argv[5]);
+ else if (argc > 6) {
+ argv += 3;
+ argc -= 3;
+ continue;
+ }
+ break;
+ }
+}
+
+/*
+ * doinclude - include a given file.
+ */
+int
+doincl(ifile)
+char *ifile;
+{
+ if (ilevel + 1 == MAXINP)
+ oops("too many include files.");
+ if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) {
+ ilevel++;
+ bbase[ilevel] = bufbase = bp;
+ return (1);
+ }
+ else
+ return (0);
+}
+
+#ifdef EXTENDED
+/*
+ * dopaste - include a given file without any
+ * macro processing.
+ */
+int
+dopaste(pfile)
+char *pfile;
+{
+ FILE *pf;
+ register int c;
+
+ if ((pf = fopen(pfile, "r")) != NULL) {
+ while ((c = getc(pf)) != EOF)
+ putc(c, active);
+ (void) fclose(pf);
+ return (1);
+ }
+ else
+ return (0);
+}
+#endif
+
+/*
+ * dochq - change quote characters
+ */
+void
+dochq(argv, argc)
+register char *argv[];
+register int argc;
+{
+ if (argc > 2) {
+ if (*argv[2])
+ lquote = *argv[2];
+ if (argc > 3) {
+ if (*argv[3])
+ rquote = *argv[3];
+ }
+ else
+ rquote = lquote;
+ }
+ else {
+ lquote = LQUOTE;
+ rquote = RQUOTE;
+ }
+}
+
+/*
+ * dochc - change comment characters
+ */
+void
+dochc(argv, argc)
+register char *argv[];
+register int argc;
+{
+ if (argc > 2) {
+ if (*argv[2])
+ scommt = *argv[2];
+ if (argc > 3) {
+ if (*argv[3])
+ ecommt = *argv[3];
+ }
+ else
+ ecommt = ECOMMT;
+ }
+ else {
+ scommt = SCOMMT;
+ ecommt = ECOMMT;
+ }
+}
+
+/*
+ * dodivert - divert the output to a temporary file
+ */
+void
+dodiv(n)
+register int n;
+{
+ if (n < 0 || n >= MAXOUT)
+ n = 0; /* bitbucket */
+ if (outfile[n] == NULL) {
+ m4temp[UNIQUE] = n + '0';
+ if ((outfile[n] = fopen(m4temp, "w")) == NULL)
+ oops("%s: cannot divert.", m4temp);
+ }
+ oindex = n;
+ active = outfile[n];
+}
+
+/*
+ * doundivert - undivert a specified output, or all
+ * other outputs, in numerical order.
+ */
+void
+doundiv(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register int ind;
+ register int n;
+
+ if (argc > 2) {
+ for (ind = 2; ind < argc; ind++) {
+ n = atoi(argv[ind]);
+ if (n > 0 && n < MAXOUT && outfile[n] != NULL)
+ getdiv(n);
+
+ }
+ }
+ else
+ for (n = 1; n < MAXOUT; n++)
+ if (outfile[n] != NULL)
+ getdiv(n);
+}
+
+/*
+ * dosub - select substring
+ */
+void
+dosub(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register unsigned char *ap, *fc, *k;
+ register int nc;
+
+ if (argc < 5)
+ nc = MAXTOK;
+ else
+#ifdef EXPR
+ nc = expr(argv[4]);
+#else
+ nc = atoi(argv[4]);
+#endif
+ ap = argv[2]; /* target string */
+#ifdef EXPR
+ fc = ap + expr(argv[3]); /* first char */
+#else
+ fc = ap + atoi(argv[3]); /* first char */
+#endif
+ if (fc >= ap && fc < ap + strlen(ap))
+ for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--)
+ putback(*k);
+}
+
+/*
+ * map:
+ * map every character of s1 that is specified in from
+ * into s3 and replace in s. (source s1 remains untouched)
+ *
+ * This is a standard implementation of map(s,from,to) function of ICON
+ * language. Within mapvec, we replace every character of "from" with
+ * the corresponding character in "to". If "to" is shorter than "from",
+ * than the corresponding entries are null, which means that those
+ * characters dissapear altogether. Furthermore, imagine
+ * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
+ * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
+ * ultimately maps to `*'. In order to achieve this effect in an efficient
+ * manner (i.e. without multiple passes over the destination string), we
+ * loop over mapvec, starting with the initial source character. if the
+ * character value (dch) in this location is different than the source
+ * character (sch), sch becomes dch, once again to index into mapvec, until
+ * the character value stabilizes (i.e. sch = dch, in other words
+ * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
+ * character, it will stabilize, since mapvec[0] == 0 at all times. At the
+ * end, we restore mapvec* back to normal where mapvec[n] == n for
+ * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
+ * about 5 times faster than any algorithm that makes multiple passes over
+ * destination string.
+ */
+void
+map(dest, src, from, to)
+register char *dest;
+register char *src;
+register char *from;
+register char *to;
+{
+ register char *tmp;
+ register char sch, dch;
+ static char mapvec[128] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127
+ };
+
+ if (*src) {
+ tmp = from;
+ /*
+ * create a mapping between "from" and
+ * "to"
+ */
+ while (*from)
+ mapvec[*from++] = (*to) ? *to++ : (char) 0;
+
+ while (*src) {
+ sch = *src++;
+ dch = mapvec[sch];
+ while (dch != sch) {
+ sch = dch;
+ dch = mapvec[sch];
+ }
+ if (*dest = dch)
+ dest++;
+ }
+ /*
+ * restore all the changed characters
+ */
+ while (*tmp) {
+ mapvec[*tmp] = *tmp;
+ tmp++;
+ }
+ }
+ *dest = (char) 0;
+}
diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c
new file mode 100644
index 0000000..4b98e01
--- /dev/null
+++ b/usr.bin/m4/expr.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)expr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <stdio.h>
+
+/*
+ * expression evaluator: performs a standard recursive
+ * descent parse to evaluate any expression permissible
+ * within the following grammar:
+ *
+ * expr : query EOS
+ * query : lor
+ * | lor "?" query ":" query
+ * lor : land { "||" land }
+ * land : not { "&&" not }
+ * not : eqrel
+ * | '!' not
+ * eqrel : shift { eqrelop shift }
+ * shift : primary { shop primary }
+ * primary : term { addop term }
+ * term : exp { mulop exp }
+ * exp : unary { expop unary }
+ * unary : factor
+ * | unop unary
+ * factor : constant
+ * | "(" query ")"
+ * constant: num
+ * | "'" CHAR "'"
+ * num : DIGIT
+ * | DIGIT num
+ * shop : "<<"
+ * | ">>"
+ * eqrel : "="
+ * | "=="
+ * | "!="
+ * | "<"
+ * | ">"
+ * | "<="
+ * | ">="
+ *
+ *
+ * This expression evaluator is lifted from a public-domain
+ * C Pre-Processor included with the DECUS C Compiler distribution.
+ * It is hacked somewhat to be suitable for m4.
+ *
+ * Originally by: Mike Lutz
+ * Bob Harper
+ */
+
+#define TRUE 1
+#define FALSE 0
+#define EOS (char) 0
+#define EQL 0
+#define NEQ 1
+#define LSS 2
+#define LEQ 3
+#define GTR 4
+#define GEQ 5
+#define OCTAL 8
+#define DECIMAL 10
+
+static char *nxtch; /* Parser scan pointer */
+
+static int query __P((void));
+static int lor __P((void));
+static int land __P((void));
+static int not __P((void));
+static int eqrel __P((void));
+static int shift __P((void));
+static int primary __P((void));
+static int term __P((void));
+static int exp __P((void));
+static int unary __P((void));
+static int factor __P((void));
+static int constant __P((void));
+static int num __P((void));
+static int geteqrel __P((void));
+static int skipws __P((void));
+static void experr __P((char *));
+
+/*
+ * For longjmp
+ */
+#include <setjmp.h>
+static jmp_buf expjump;
+
+/*
+ * macros:
+ * ungetch - Put back the last character examined.
+ * getch - return the next character from expr string.
+ */
+#define ungetch() nxtch--
+#define getch() *nxtch++
+
+int
+expr(expbuf)
+char *expbuf;
+{
+ register int rval;
+
+ nxtch = expbuf;
+ if (setjmp(expjump) != 0)
+ return FALSE;
+
+ rval = query();
+ if (skipws() == EOS)
+ return rval;
+
+ printf("m4: ill-formed expression.\n");
+ return FALSE;
+}
+
+/*
+ * query : lor | lor '?' query ':' query
+ */
+static int
+query()
+{
+ register int bool, true_val, false_val;
+
+ bool = lor();
+ if (skipws() != '?') {
+ ungetch();
+ return bool;
+ }
+
+ true_val = query();
+ if (skipws() != ':')
+ experr("bad query");
+
+ false_val = query();
+ return bool ? true_val : false_val;
+}
+
+/*
+ * lor : land { '||' land }
+ */
+static int
+lor()
+{
+ register int c, vl, vr;
+
+ vl = land();
+ while ((c = skipws()) == '|') {
+ if (getch() != '|')
+ ungetch();
+ vr = land();
+ vl = vl || vr;
+ }
+
+ ungetch();
+ return vl;
+}
+
+/*
+ * land : not { '&&' not }
+ */
+static int
+land()
+{
+ register int c, vl, vr;
+
+ vl = not();
+ while ((c = skipws()) == '&') {
+ if (getch() != '&')
+ ungetch();
+ vr = not();
+ vl = vl && vr;
+ }
+
+ ungetch();
+ return vl;
+}
+
+/*
+ * not : eqrel | '!' not
+ */
+static int
+not()
+{
+ register int val, c;
+
+ if ((c = skipws()) == '!' && getch() != '=') {
+ ungetch();
+ val = not();
+ return !val;
+ }
+
+ if (c == '!')
+ ungetch();
+ ungetch();
+ return eqrel();
+}
+
+/*
+ * eqrel : shift { eqrelop shift }
+ */
+static int
+eqrel()
+{
+ register int vl, vr, eqrel;
+
+ vl = shift();
+ while ((eqrel = geteqrel()) != -1) {
+ vr = shift();
+
+ switch (eqrel) {
+
+ case EQL:
+ vl = (vl == vr);
+ break;
+ case NEQ:
+ vl = (vl != vr);
+ break;
+
+ case LEQ:
+ vl = (vl <= vr);
+ break;
+ case LSS:
+ vl = (vl < vr);
+ break;
+ case GTR:
+ vl = (vl > vr);
+ break;
+ case GEQ:
+ vl = (vl >= vr);
+ break;
+ }
+ }
+ return vl;
+}
+
+/*
+ * shift : primary { shop primary }
+ */
+static int
+shift()
+{
+ register int vl, vr, c;
+
+ vl = primary();
+ while (((c = skipws()) == '<' || c == '>') && getch() == c) {
+ vr = primary();
+
+ if (c == '<')
+ vl <<= vr;
+ else
+ vl >>= vr;
+ }
+
+ if (c == '<' || c == '>')
+ ungetch();
+ ungetch();
+ return vl;
+}
+
+/*
+ * primary : term { addop term }
+ */
+static int
+primary()
+{
+ register int c, vl, vr;
+
+ vl = term();
+ while ((c = skipws()) == '+' || c == '-') {
+ vr = term();
+
+ if (c == '+')
+ vl += vr;
+ else
+ vl -= vr;
+ }
+
+ ungetch();
+ return vl;
+}
+
+/*
+ * <term> := <exp> { <mulop> <exp> }
+ */
+static int
+term()
+{
+ register int c, vl, vr;
+
+ vl = exp();
+ while ((c = skipws()) == '*' || c == '/' || c == '%') {
+ vr = exp();
+
+ switch (c) {
+ case '*':
+ vl *= vr;
+ break;
+ case '/':
+ vl /= vr;
+ break;
+ case '%':
+ vl %= vr;
+ break;
+ }
+ }
+ ungetch();
+ return vl;
+}
+
+/*
+ * <term> := <unary> { <expop> <unary> }
+ */
+static int
+exp()
+{
+ register c, vl, vr, n;
+
+ vl = unary();
+ switch (c = skipws()) {
+
+ case '*':
+ if (getch() != '*') {
+ ungetch();
+ break;
+ }
+
+ case '^':
+ vr = exp();
+ n = 1;
+ while (vr-- > 0)
+ n *= vl;
+ return n;
+ }
+
+ ungetch();
+ return vl;
+}
+
+/*
+ * unary : factor | unop unary
+ */
+static int
+unary()
+{
+ register int val, c;
+
+ if ((c = skipws()) == '+' || c == '-' || c == '~') {
+ val = unary();
+
+ switch (c) {
+ case '+':
+ return val;
+ case '-':
+ return -val;
+ case '~':
+ return ~val;
+ }
+ }
+
+ ungetch();
+ return factor();
+}
+
+/*
+ * factor : constant | '(' query ')'
+ */
+static int
+factor()
+{
+ register int val;
+
+ if (skipws() == '(') {
+ val = query();
+ if (skipws() != ')')
+ experr("bad factor");
+ return val;
+ }
+
+ ungetch();
+ return constant();
+}
+
+/*
+ * constant: num | 'char'
+ * Note: constant() handles multi-byte constants
+ */
+static int
+constant()
+{
+ register int i;
+ register int value;
+ register char c;
+ int v[sizeof(int)];
+
+ if (skipws() != '\'') {
+ ungetch();
+ return num();
+ }
+ for (i = 0; i < sizeof(int); i++) {
+ if ((c = getch()) == '\'') {
+ ungetch();
+ break;
+ }
+ if (c == '\\') {
+ switch (c = getch()) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ ungetch();
+ c = num();
+ break;
+ case 'n':
+ c = 012;
+ break;
+ case 'r':
+ c = 015;
+ break;
+ case 't':
+ c = 011;
+ break;
+ case 'b':
+ c = 010;
+ break;
+ case 'f':
+ c = 014;
+ break;
+ }
+ }
+ v[i] = c;
+ }
+ if (i == 0 || getch() != '\'')
+ experr("illegal character constant");
+ for (value = 0; --i >= 0;) {
+ value <<= 8;
+ value += v[i];
+ }
+ return value;
+}
+
+/*
+ * num : digit | num digit
+ */
+static int
+num()
+{
+ register int rval, c, base;
+ int ndig;
+
+ base = ((c = skipws()) == '0') ? OCTAL : DECIMAL;
+ rval = 0;
+ ndig = 0;
+ while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) {
+ rval *= base;
+ rval += (c - '0');
+ c = getch();
+ ndig++;
+ }
+ ungetch();
+
+ if (ndig == 0)
+ experr("bad constant");
+
+ return rval;
+
+}
+
+/*
+ * eqrel : '=' | '==' | '!=' | '<' | '>' | '<=' | '>='
+ */
+static int
+geteqrel()
+{
+ register int c1, c2;
+
+ c1 = skipws();
+ c2 = getch();
+
+ switch (c1) {
+
+ case '=':
+ if (c2 != '=')
+ ungetch();
+ return EQL;
+
+ case '!':
+ if (c2 == '=')
+ return NEQ;
+ ungetch();
+ ungetch();
+ return -1;
+
+ case '<':
+ if (c2 == '=')
+ return LEQ;
+ ungetch();
+ return LSS;
+
+ case '>':
+ if (c2 == '=')
+ return GEQ;
+ ungetch();
+ return GTR;
+
+ default:
+ ungetch();
+ ungetch();
+ return -1;
+ }
+}
+
+/*
+ * Skip over any white space and return terminating char.
+ */
+static int
+skipws()
+{
+ register char c;
+
+ while ((c = getch()) <= ' ' && c > EOS)
+ ;
+ return c;
+}
+
+/*
+ * resets environment to eval(), prints an error
+ * and forces eval to return FALSE.
+ */
+static void
+experr(msg)
+char *msg;
+{
+ printf("m4: %s in expr.\n", msg);
+ longjmp(expjump, -1);
+}
diff --git a/usr.bin/m4/extern.h b/usr.bin/m4/extern.h
new file mode 100644
index 0000000..a8df3eb
--- /dev/null
+++ b/usr.bin/m4/extern.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+char *basename __P((char *));
+char *xalloc __P((unsigned long));
+int expr __P((char *));
+ndptr addent __P((char *));
+void chrsave __P((int));
+void dochc __P((char *[], int));
+void dochq __P((char *[], int));
+void dodefine __P((char *, char *));
+void dodefn __P((char *));
+void dodiv __P((int));
+void dodump __P((char *[], int));
+void doifelse __P((char *[], int));
+int doincl __P((char *));
+int dopaste __P((char *));
+void dopushdef __P((char *, char *));
+void dosub __P((char *[], int));
+void doundiv __P((char *[], int));
+void eval __P((char *[], int, int));
+void expand __P((char *[], int));
+void getdiv __P((int));
+char *xstrdup __P((const char *));
+int hash __P((char *));
+int indx __P((char *, char *));
+void killdiv __P((void));
+ndptr lookup __P((char *));
+void map __P((char *, char *, char *, char *));
+void onintr __P((int));
+void oops __P((const char *, ...));
+void pbnum __P((int));
+void pbstr __P((unsigned char *));
+void putback __P((int));
+void remhash __P((char *, int));
+void usage __P((void));
+
+extern ndptr hashtab[]; /* hash table for macros etc. */
+extern stae mstack[]; /* stack of m4 machine */
+extern FILE *active; /* active output file pointer */
+extern FILE *infile[]; /* input file stack (0=stdin) */
+extern FILE *outfile[]; /* diversion array(0=bitbucket) */
+extern int fp; /* m4 call frame pointer */
+extern int ilevel; /* input file stack pointer */
+extern int oindex; /* diversion index. */
+extern int sp; /* current m4 stack pointer */
+extern unsigned char *bp; /* first available character */
+extern unsigned char buf[]; /* push-back buffer */
+extern unsigned char *bufbase; /* buffer base for this ilevel */
+extern unsigned char *bbase[]; /* buffer base per ilevel */
+extern char ecommt; /* end character for comment */
+extern char *endest; /* end of string space */
+extern unsigned char *endpbb; /* end of push-back buffer */
+extern char *ep; /* first free char in strspace */
+extern char lquote; /* left quote character (`) */
+extern char *m4temp; /* filename for diversions */
+extern char *m4wraps; /* m4wrap string default. */
+extern char *null; /* as it says.. just a null. */
+extern char *progname; /* program name */
+extern char rquote; /* right quote character (') */
+extern char scommt; /* start character for comment */
diff --git a/usr.bin/m4/look.c b/usr.bin/m4/look.c
new file mode 100644
index 0000000..3635e1b
--- /dev/null
+++ b/usr.bin/m4/look.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)look.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * look.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+
+int
+hash(name)
+register char *name;
+{
+ register unsigned long h = 0;
+ while (*name)
+ h = (h << 5) + h + *name++;
+ return (h % HASHSIZE);
+}
+
+/*
+ * find name in the hash table
+ */
+ndptr
+lookup(name)
+char *name;
+{
+ register ndptr p;
+
+ for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr)
+ if (STREQ(name, p->name))
+ break;
+ return (p);
+}
+
+/*
+ * hash and create an entry in the hash table.
+ * The new entry is added in front of a hash bucket.
+ */
+ndptr
+addent(name)
+char *name;
+{
+ register int h;
+ ndptr p;
+
+ h = hash(name);
+ p = (ndptr) xalloc(sizeof(struct ndblock));
+ p->nxtptr = hashtab[h];
+ hashtab[h] = p;
+ p->name = xstrdup(name);
+ return p;
+}
+
+static void
+freent(p)
+ndptr p;
+{
+ if (!(p->type & STATIC)) {
+ free((char *) p->name);
+ if (p->defn != null)
+ free((char *) p->defn);
+ }
+ free((char *) p);
+}
+
+/*
+ * remove an entry from the hashtable
+ */
+void
+remhash(name, all)
+char *name;
+int all;
+{
+ register int h;
+ register ndptr xp, tp, mp;
+
+ h = hash(name);
+ mp = hashtab[h];
+ tp = nil;
+ while (mp != nil) {
+ if (STREQ(mp->name, name)) {
+ mp = mp->nxtptr;
+ if (tp == nil) {
+ freent(hashtab[h]);
+ hashtab[h] = mp;
+ }
+ else {
+ xp = tp->nxtptr;
+ tp->nxtptr = mp;
+ freent(xp);
+ }
+ if (!all)
+ break;
+ }
+ else {
+ tp = mp;
+ mp = mp->nxtptr;
+ }
+ }
+}
diff --git a/usr.bin/m4/m4.1 b/usr.bin/m4/m4.1
new file mode 100644
index 0000000..5318019
--- /dev/null
+++ b/usr.bin/m4/m4.1
@@ -0,0 +1,181 @@
+.\"
+.\" @(#) $Id: m4.1,v 1.4 1994/01/16 00:30:31 swallace Exp $
+.\"
+.Dd January 26, 1993
+.Dt m4 1
+.Os
+.Sh NAME
+.Nm m4
+.Nd macro language processor
+.Sh SYNOPSIS
+.Nm m4
+.Oo
+.Fl D Ns Ar name Ns Op Ar =value
+.Oc
+.Op Fl U Ns Ar name
+.Op Ar filename
+\|.\|.\|.
+.Sh DESCRIPTION
+The
+.Nm m4
+utility is a macro processor that can be used as a front end to any
+language (e.g., C, ratfor, fortran, lex, and yacc).
+Each of the argument files is processed in order.
+If there are no files, or if a filename is \`-\', the standard input is read.
+The processed text is sent to the standard output.
+.Pp
+Macro calls have the form name(argument1[, argument2, ...,] argumentN).
+.Pp
+There cannot be any space following the macro name and the open
+parentheses '('. If the macro name is not followed by an open
+parentheses it is processed with no arguments.
+.Pp
+Macro names consist of a leading alphabetic or underscore
+possibly followed by alphanumeric or underscore characters, therefore
+valid macro names match this pattern [a-zA-Z_][a-zA-Z0-9_]*.
+.Pp
+In arguments to macros, leading unquoted space, tab and newline
+characters are ignored. To quote strings use left and right single
+quotes (e.g., ` this is a string with a leading space'). You can change
+the quote characters with the changequote built-in macro.
+.Pp
+The options are as follows:
+.Bl -tag -width "-Dname[=value]xxx"
+.It Fl D Ns Ar name Ns Oo
+.Ar =value
+.Oc
+Define the symbol
+.Ar name
+to have some value (or NULL).
+.It Fl "U" Ns Ar "name"
+Undefine the symbol
+.Ar name .
+.El
+.Sh SYNTAX
+.Nm m4
+provides the following built-in macros. They may be
+redefined, loosing their original meaning.
+Return values are NULL unless otherwise stated.
+.Bl -tag -width changequotexxx
+.It changecom
+Change the start and end comment sequences. The default is
+the pound sign `#' and the newline character. With no arguments
+comments are turned off. The maximum length for a comment marker is
+five characters.
+.It changequote
+Defines the quote symbols to be the first and second arguments.
+The symbols may be up to five characters long. If no arguments are
+given it restores the default open and close single quotes.
+.It decr
+Decrements the argument by 1. The argument must be a valid numeric string.
+.It define
+Define a new macro named by the first argument to have the
+value of the second argument. Each occurrence of $n (where n
+is 0 through 9) is replaced by the n'th argument. $0 is the name
+of the calling macro. Undefined arguments are replaced by a
+NULL string. $# is replaced by the number of arguments; $*
+is replaced by all arguments comma separated; $@ is the same
+as $* but all arguments are quoted against further expansion.
+.It defn
+Returns the quoted definition for each argument. This can be used to rename
+macro definitions (even for built-in macros).
+.It divert
+There are 10 output queues (numbered 0-9).
+At the end of processing
+.Nm m4
+concatenates all the queues in numerical order to produce the
+final output. Initially the output queue is 0. The divert
+macro allows you to select a new output queue (an invalid argument
+passed to divert causes output to be discarded).
+.It divnum
+Returns the current output queue number.
+.It dnl
+Discard input characters up to and including the next newline.
+.It dumpdef
+Prints the names and definitions for the named items, or for everything
+if no arguments are passed.
+.It errprint
+Prints the first argument on the standard error output stream.
+.It eval
+Computes the first argument as an arithmetic expression using 32-bit
+arithmetic. Operators are the standard C ternary, arithmetic, logical,
+shift, relational, bitwise, and parentheses operators. You can specify
+octal, decimal, and hexadecimal numbers as in C. The second argument (if
+any) specifies the radix for the result and the third argument (if
+any) specifies the minimum number of digits in the result.
+.It expr
+This is an alias for eval.
+.It ifdef
+If the macro named by the first argument is defined then return the second
+argument, otherwise the third. If there is no third argument,
+the value is NULL. The word `unix' is predefined.
+.It ifelse
+If the first argument matches the second argument then ifelse returns
+the third argument. If the match fails the three arguments are
+discarded and the next three arguments are used until there is
+zero or one arguments left, either this last argument or NULL is
+returned if no other matches were found.
+.It include
+Returns the contents of the file specified in the first argument.
+Include aborts with an error message if the file cannot be included.
+.It incr
+Increments the argument by 1. The argument must be a valid numeric string.
+.It index
+Returns the index of the second argument in the first argument (e.g.,
+index(the quick brown fox jumped, fox) returns 16). If the second
+argument is not found index returns -1.
+.It len
+Returns the number of characters in the first argument. Extra arguments
+are ignored.
+.It m4exit
+Immediately exits with the return value specified by the first argument,
+0 if none.
+.It m4wrap
+Allows you to define what happens at the final EOF, usually for cleanup
+purposes (e.g., m4wrap("cleanup(tempfile)") causes the macro cleanup to
+invoked after all other processing is done.)
+.It maketemp
+Translates the string XXXXX in the first argument with the current process
+ID leaving other characters alone. This can be used to create unique
+temporary file names.
+.It paste
+Includes the contents of the file specified by the first argument without
+any macro processing. Aborts with an error message if the file cannot be
+included.
+.It popdef
+Restores the pushdef'ed definition for each argument.
+.It pushdef
+Takes the same arguments as define, but it saves the definition on a
+stack for later retrieval by popdef.
+.It shift
+Returns all but the first argument, the remaining arguments are
+quoted and pushed back with commas in between. The quoting
+nullifies the effect of the extra scan that will subsequently be
+performed.
+.It sinclude
+Similar to include, except it ignores any errors.
+.It spaste
+Similar to spaste, except it ignores any errors.
+.It substr
+Returns a substring of the first argument starting at the offset specified
+by the second argument and the length specified by the third argument.
+If no third argument is present it returns the rest of the string.
+.It syscmd
+Passes the first argument to the shell. Nothing is returned.
+.It sysval
+Returns the return value from the last syscmd.
+.It translit
+Transliterate the characters in the first argument from the set
+given by the second argument to the set given by the third. You cannot
+use
+.Xr tr 1
+style abbreviations.
+.It undefine
+Removes the definition for the macro specified by the first argument.
+.It undivert
+Flushes the named output queues (or all queues if no arguments).
+.It unix
+A pre-defined macro for testing the OS platform.
+.El
+.Sh AUTHOR
+Ozan Yigit <oz@sis.yorku.ca> and Richard A. O'Keefe (ok@goanna.cs.rmit.OZ.AU)
diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c
new file mode 100644
index 0000000..395e04d
--- /dev/null
+++ b/usr.bin/m4/main.c
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * main.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
+unsigned char buf[BUFSIZE]; /* push-back buffer */
+unsigned char *bufbase = buf; /* the base for current ilevel */
+unsigned char *bbase[MAXINP]; /* the base for each ilevel */
+unsigned char *bp = buf; /* first available character */
+unsigned char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
+stae mstack[STACKMAX+1]; /* stack of m4 machine */
+char strspace[STRSPMAX+1]; /* string space for evaluation */
+char *ep = strspace; /* first free char in strspace */
+char *endest= strspace+STRSPMAX;/* end of string space */
+int sp; /* current m4 stack pointer */
+int fp; /* m4 call frame pointer */
+FILE *infile[MAXINP]; /* input file stack (0=stdin) */
+FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/
+FILE *active; /* active output file pointer */
+char *m4temp; /* filename for diversions */
+int ilevel = 0; /* input file stack pointer */
+int oindex = 0; /* diversion index.. */
+char *null = ""; /* as it says.. just a null.. */
+char *m4wraps = ""; /* m4wrap string default.. */
+char *progname; /* name of this program */
+char lquote = LQUOTE; /* left quote character (`) */
+char rquote = RQUOTE; /* right quote character (') */
+char scommt = SCOMMT; /* start character for comment */
+char ecommt = ECOMMT; /* end character for comment */
+
+struct keyblk keywrds[] = { /* m4 keywords to be installed */
+ "include", INCLTYPE,
+ "sinclude", SINCTYPE,
+ "define", DEFITYPE,
+ "defn", DEFNTYPE,
+ "divert", DIVRTYPE,
+ "expr", EXPRTYPE,
+ "eval", EXPRTYPE,
+ "substr", SUBSTYPE,
+ "ifelse", IFELTYPE,
+ "ifdef", IFDFTYPE,
+ "len", LENGTYPE,
+ "incr", INCRTYPE,
+ "decr", DECRTYPE,
+ "dnl", DNLNTYPE,
+ "changequote", CHNQTYPE,
+ "changecom", CHNCTYPE,
+ "index", INDXTYPE,
+#ifdef EXTENDED
+ "paste", PASTTYPE,
+ "spaste", SPASTYPE,
+#endif
+ "popdef", POPDTYPE,
+ "pushdef", PUSDTYPE,
+ "dumpdef", DUMPTYPE,
+ "shift", SHIFTYPE,
+ "translit", TRNLTYPE,
+ "undefine", UNDFTYPE,
+ "undivert", UNDVTYPE,
+ "divnum", DIVNTYPE,
+ "maketemp", MKTMTYPE,
+ "errprint", ERRPTYPE,
+ "m4wrap", M4WRTYPE,
+ "m4exit", EXITTYPE,
+ "syscmd", SYSCTYPE,
+ "sysval", SYSVTYPE,
+
+#ifdef unix
+ "unix", MACRTYPE,
+#else
+#ifdef vms
+ "vms", MACRTYPE,
+#endif
+#endif
+};
+
+#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
+
+extern int optind;
+extern char *optarg;
+
+void macro();
+void initkwds();
+extern int getopt();
+
+int
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ register int c;
+ register int n;
+ char *p;
+ register FILE *ifp;
+
+ progname = basename(argv[0]);
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+
+ initkwds();
+
+ while ((c = getopt(argc, argv, "tD:U:o:")) != EOF)
+ switch(c) {
+
+ case 'D': /* define something..*/
+ for (p = optarg; *p; p++)
+ if (*p == '=')
+ break;
+ if (*p)
+ *p++ = EOS;
+ dodefine(optarg, p);
+ break;
+ case 'U': /* undefine... */
+ remhash(optarg, TOP);
+ break;
+ case 'o': /* specific output */
+ case '?':
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ active = stdout; /* default active output */
+ /* filename for diversions */
+ m4temp = mktemp(xstrdup(_PATH_DIVNAME));
+
+ bbase[0] = bufbase;
+ if (!argc) {
+ sp = -1; /* stack pointer initialized */
+ fp = 0; /* frame pointer initialized */
+ infile[0] = stdin; /* default input (naturally) */
+ macro();
+ } else
+ for (; argc--; ++argv) {
+ p = *argv;
+ if (p[0] == '-' && p[1] == '\0')
+ ifp = stdin;
+ else if ((ifp = fopen(p, "r")) == NULL)
+ oops("%s: %s", p, strerror(errno));
+ sp = -1;
+ fp = 0;
+ infile[0] = ifp;
+ macro();
+ if (ifp != stdin)
+ (void)fclose(ifp);
+ }
+
+ if (*m4wraps) { /* anything for rundown ?? */
+ ilevel = 0; /* in case m4wrap includes.. */
+ bufbase = bp = buf; /* use the entire buffer */
+ putback(EOF); /* eof is a must !! */
+ pbstr(m4wraps); /* user-defined wrapup act */
+ macro(); /* last will and testament */
+ }
+
+ if (active != stdout)
+ active = stdout; /* reset output just in case */
+ for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */
+ if (outfile[n] != NULL)
+ getdiv(n);
+ /* remove bitbucket if used */
+ if (outfile[0] != NULL) {
+ (void) fclose(outfile[0]);
+ m4temp[UNIQUE] = '0';
+#ifdef vms
+ (void) remove(m4temp);
+#else
+ (void) unlink(m4temp);
+#endif
+ }
+
+ return 0;
+}
+
+ndptr inspect();
+
+/*
+ * macro - the work horse..
+ */
+void
+macro() {
+ char token[MAXTOK];
+ register char *s;
+ register int t, l;
+ register ndptr p;
+ register int nlpar;
+
+ cycle {
+ if ((t = gpbc()) == '_' || (t != EOF && isalpha(t))) {
+ putback(t);
+ if ((p = inspect(s = token)) == nil) {
+ if (sp < 0)
+ while (*s)
+ putc(*s++, active);
+ else
+ while (*s)
+ chrsave(*s++);
+ }
+ else {
+ /*
+ * real thing.. First build a call frame:
+ */
+ pushf(fp); /* previous call frm */
+ pushf(p->type); /* type of the call */
+ pushf(0); /* parenthesis level */
+ fp = sp; /* new frame pointer */
+ /*
+ * now push the string arguments:
+ */
+ pushs(p->defn); /* defn string */
+ pushs(p->name); /* macro name */
+ pushs(ep); /* start next..*/
+
+ putback(l = gpbc());
+ if (l != LPAREN) { /* add bracks */
+ putback(RPAREN);
+ putback(LPAREN);
+ }
+ }
+ }
+ else if (t == EOF) {
+ if (sp > -1)
+ oops("unexpected end of input", "");
+ if (ilevel <= 0)
+ break; /* all done thanks.. */
+ --ilevel;
+ (void) fclose(infile[ilevel+1]);
+ bufbase = bbase[ilevel];
+ continue;
+ }
+ /*
+ * non-alpha single-char token seen..
+ * [the order of else if .. stmts is important.]
+ */
+ else if (t == lquote) { /* strip quotes */
+ nlpar = 1;
+ do {
+ if ((l = gpbc()) == rquote)
+ nlpar--;
+ else if (l == lquote)
+ nlpar++;
+ else if (l == EOF)
+ oops("missing right quote", "");
+ if (nlpar > 0) {
+ if (sp < 0)
+ putc(l, active);
+ else
+ chrsave(l);
+ }
+ }
+ while (nlpar != 0);
+ }
+
+ else if (sp < 0) { /* not in a macro at all */
+ if (t == scommt) { /* comment handling here */
+ putc(t, active);
+ while ((t = gpbc()) != ecommt)
+ putc(t, active);
+ }
+ putc(t, active); /* output directly.. */
+ }
+
+ else switch(t) {
+
+ case LPAREN:
+ if (PARLEV > 0)
+ chrsave(t);
+ while ((l = gpbc()) != EOF && isspace(l))
+ ; /* skip blank, tab, nl.. */
+ putback(l);
+ PARLEV++;
+ break;
+
+ case RPAREN:
+ if (--PARLEV > 0)
+ chrsave(t);
+ else { /* end of argument list */
+ chrsave(EOS);
+
+ if (sp == STACKMAX)
+ oops("internal stack overflow", "");
+
+ if (CALTYP == MACRTYPE)
+ expand((char **) mstack+fp+1, sp-fp);
+ else
+ eval((char **) mstack+fp+1, sp-fp, CALTYP);
+
+ ep = PREVEP; /* flush strspace */
+ sp = PREVSP; /* previous sp.. */
+ fp = PREVFP; /* rewind stack...*/
+ }
+ break;
+
+ case COMMA:
+ if (PARLEV == 1) {
+ chrsave(EOS); /* new argument */
+ while ((l = gpbc()) != EOF && isspace(l))
+ ;
+ putback(l);
+ pushs(ep);
+ } else
+ chrsave(t);
+ break;
+
+ default:
+ chrsave(t); /* stack the char */
+ break;
+ }
+ }
+}
+
+/*
+ * build an input token..
+ * consider only those starting with _ or A-Za-z. This is a
+ * combo with lookup to speed things up.
+ */
+ndptr
+inspect(tp)
+register char *tp;
+{
+ register int c;
+ register char *name = tp;
+ register char *etp = tp+MAXTOK;
+ register ndptr p;
+ register unsigned long h = 0;
+
+ while ((c = gpbc()) != EOF && (isalnum(c) || c == '_') && tp < etp)
+ h = (h << 5) + h + (*tp++ = c);
+ putback(c);
+ if (tp == etp)
+ oops("token too long", "");
+
+ *tp = EOS;
+
+ for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
+ if (STREQ(name, p->name))
+ break;
+ return p;
+}
+
+/*
+ * initkwds - initialise m4 keywords as fast as possible.
+ * This very similar to install, but without certain overheads,
+ * such as calling lookup. Malloc is not used for storing the
+ * keyword strings, since we simply use the static pointers
+ * within keywrds block.
+ */
+void
+initkwds() {
+ register int i;
+ register int h;
+ register ndptr p;
+
+ for (i = 0; i < MAXKEYS; i++) {
+ h = hash(keywrds[i].knam);
+ p = (ndptr) xalloc(sizeof(struct ndblock));
+ p->nxtptr = hashtab[h];
+ hashtab[h] = p;
+ p->name = keywrds[i].knam;
+ p->defn = null;
+ p->type = keywrds[i].ktyp | STATIC;
+ }
+}
diff --git a/usr.bin/m4/mdef.h b/usr.bin/m4/mdef.h
new file mode 100644
index 0000000..dd23bde
--- /dev/null
+++ b/usr.bin/m4/mdef.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mdef.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define MACRTYPE 1
+#define DEFITYPE 2
+#define EXPRTYPE 3
+#define SUBSTYPE 4
+#define IFELTYPE 5
+#define LENGTYPE 6
+#define CHNQTYPE 7
+#define SYSCTYPE 8
+#define UNDFTYPE 9
+#define INCLTYPE 10
+#define SINCTYPE 11
+#define PASTTYPE 12
+#define SPASTYPE 13
+#define INCRTYPE 14
+#define IFDFTYPE 15
+#define PUSDTYPE 16
+#define POPDTYPE 17
+#define SHIFTYPE 18
+#define DECRTYPE 19
+#define DIVRTYPE 20
+#define UNDVTYPE 21
+#define DIVNTYPE 22
+#define MKTMTYPE 23
+#define ERRPTYPE 24
+#define M4WRTYPE 25
+#define TRNLTYPE 26
+#define DNLNTYPE 27
+#define DUMPTYPE 28
+#define CHNCTYPE 29
+#define INDXTYPE 30
+#define SYSVTYPE 31
+#define EXITTYPE 32
+#define DEFNTYPE 33
+
+#define STATIC 128
+
+/*
+ * m4 special characters
+ */
+
+#define ARGFLAG '$'
+#define LPAREN '('
+#define RPAREN ')'
+#define LQUOTE '`'
+#define RQUOTE '\''
+#define COMMA ','
+#define SCOMMT '#'
+#define ECOMMT '\n'
+
+#ifdef msdos
+#define system(str) (-1)
+#endif
+
+/*
+ * other important constants
+ */
+
+#define EOS (char) 0
+#define MAXINP 10 /* maximum include files */
+#define MAXOUT 10 /* maximum # of diversions */
+#define MAXSTR 512 /* maximum size of string */
+#define BUFSIZE 4096 /* size of pushback buffer */
+#define STACKMAX 1024 /* size of call stack */
+#define STRSPMAX 4096 /* size of string space */
+#define MAXTOK MAXSTR /* maximum chars in a tokn */
+#define HASHSIZE 199 /* maximum size of hashtab */
+
+#define ALL 1
+#define TOP 0
+
+#define TRUE 1
+#define FALSE 0
+#define cycle for(;;)
+
+/*
+ * m4 data structures
+ */
+
+typedef struct ndblock *ndptr;
+
+struct ndblock { /* hastable structure */
+ char *name; /* entry name.. */
+ char *defn; /* definition.. */
+ int type; /* type of the entry.. */
+ ndptr nxtptr; /* link to next entry.. */
+};
+
+#define nil ((ndptr) 0)
+
+struct keyblk {
+ char *knam; /* keyword name */
+ int ktyp; /* keyword type */
+};
+
+typedef union { /* stack structure */
+ int sfra; /* frame entry */
+ char *sstr; /* string entry */
+} stae;
+
+/*
+ * macros for readibility and/or speed
+ *
+ * gpbc() - get a possibly pushed-back character
+ * pushf() - push a call frame entry onto stack
+ * pushs() - push a string pointer onto stack
+ */
+#define gpbc() (bp > bufbase) ? (*--bp ? *bp : EOF) : getc(infile[ilevel])
+#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x)
+#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x)
+
+/*
+ * . .
+ * | . | <-- sp | . |
+ * +-------+ +-----+
+ * | arg 3 ----------------------->| str |
+ * +-------+ | . |
+ * | arg 2 ---PREVEP-----+ .
+ * +-------+ |
+ * . | | |
+ * +-------+ | +-----+
+ * | plev | PARLEV +-------->| str |
+ * +-------+ | . |
+ * | type | CALTYP .
+ * +-------+
+ * | prcf ---PREVFP--+
+ * +-------+ |
+ * | . | PREVSP |
+ * . |
+ * +-------+ |
+ * | <----------+
+ * +-------+
+ *
+ */
+#define PARLEV (mstack[fp].sfra)
+#define CALTYP (mstack[fp-1].sfra)
+#define PREVEP (mstack[fp+3].sstr)
+#define PREVSP (fp-3)
+#define PREVFP (mstack[fp-2].sfra)
diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c
new file mode 100644
index 0000000..5f12e47
--- /dev/null
+++ b/usr.bin/m4/misc.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mdef.h"
+#include "stdd.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * find the index of second str in the first str.
+ */
+int
+indx(s1, s2)
+char *s1;
+char *s2;
+{
+ register char *t;
+ register char *p;
+ register char *m;
+
+ for (p = s1; *p; p++) {
+ for (t = p, m = s2; *m && *m == *t; m++, t++);
+ if (!*m)
+ return (p - s1);
+ }
+ return (-1);
+}
+/*
+ * putback - push character back onto input
+ */
+void
+putback(c)
+int c;
+{
+ if (c == EOF)
+ c = 0;
+ else if (c == 0)
+ return;
+ if (bp < endpbb)
+ *bp++ = c;
+ else
+ oops("too many characters pushed back");
+}
+
+/*
+ * pbstr - push string back onto input
+ * putback is replicated to improve
+ * performance.
+ */
+void
+pbstr(s)
+register unsigned char *s;
+{
+ register unsigned char *es;
+ register unsigned char *zp;
+
+ es = s;
+ zp = bp;
+
+ while (*es)
+ es++;
+ es--;
+ while (es >= s)
+ if (zp < endpbb)
+ *zp++ = *es--;
+ if ((bp = zp) == endpbb)
+ oops("too many characters pushed back");
+}
+
+/*
+ * pbnum - convert number to string, push back on input.
+ */
+void
+pbnum(n)
+int n;
+{
+ register int num;
+
+ num = (n < 0) ? -n : n;
+ do {
+ putback(num % 10 + '0');
+ }
+ while ((num /= 10) > 0);
+
+ if (n < 0)
+ putback('-');
+}
+
+/*
+ * chrsave - put single char on string space
+ */
+void
+chrsave(c)
+char c;
+{
+ if (ep < endest)
+ *ep++ = c;
+ else
+ oops("string space overflow");
+}
+
+/*
+ * read in a diversion file, and dispose it.
+ */
+void
+getdiv(n)
+int n;
+{
+ register int c;
+ register FILE *dfil;
+
+ if (active == outfile[n])
+ oops("%s: diversion still active.", "undivert");
+ (void) fclose(outfile[n]);
+ outfile[n] = NULL;
+ m4temp[UNIQUE] = n + '0';
+ if ((dfil = fopen(m4temp, "r")) == NULL)
+ oops("%s: cannot undivert.", m4temp);
+ else
+ while ((c = getc(dfil)) != EOF)
+ putc(c, active);
+ (void) fclose(dfil);
+
+#ifdef vms
+ if (remove(m4temp))
+#else
+ if (unlink(m4temp) == -1)
+#endif
+ oops("%s: cannot unlink.", m4temp);
+}
+
+void
+onintr(signo)
+ int signo;
+{
+ oops("interrupted.");
+}
+
+/*
+ * killdiv - get rid of the diversion files
+ */
+void
+killdiv()
+{
+ register int n;
+
+ for (n = 0; n < MAXOUT; n++)
+ if (outfile[n] != NULL) {
+ (void) fclose(outfile[n]);
+ m4temp[UNIQUE] = n + '0';
+#ifdef vms
+ (void) remove(m4temp);
+#else
+ (void) unlink(m4temp);
+#endif
+ }
+}
+
+char *
+xalloc(n)
+unsigned long n;
+{
+ register char *p = malloc(n);
+
+ if (p == NULL)
+ oops("malloc: %s", strerror(errno));
+ return p;
+}
+
+char *
+xstrdup(s)
+const char *s;
+{
+ register char *p = strdup(s);
+ if (p == NULL)
+ oops("strdup: %s", strerror(errno));
+ return p;
+}
+
+char *
+basename(s)
+register char *s;
+{
+ register char *p;
+ extern char *strrchr();
+
+ if ((p = strrchr(s, '/')) == NULL)
+ return s;
+
+ return ++p;
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: m4 [-Dname[=val]] [-Uname]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+oops(const char *fmt, ...)
+#else
+oops(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "%s: ", progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/m4/pathnames.h b/usr.bin/m4/pathnames.h
new file mode 100644
index 0000000..72f66d3
--- /dev/null
+++ b/usr.bin/m4/pathnames.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Definitions of diversion files. If the name of the file is changed,
+ * adjust UNIQUE to point to the wildcard (*) character in the filename.
+ */
+
+#ifdef msdos
+#define _PATH_DIVNAME "\\M4*XXXXXX" /* msdos diversion files */
+#define UNIQUE 3 /* unique char location */
+#endif
+
+#ifdef unix
+#define _PATH_DIVNAME "/tmp/m4.0XXXXXX" /* unix diversion files */
+#define UNIQUE 8 /* unique char location */
+#endif
+
+#ifdef vms
+#define _PATH_DIVNAME "sys$login:m4*XXXXXX" /* vms diversion files */
+#define UNIQUE 12 /* unique char location */
+#endif
diff --git a/usr.bin/m4/serv.c b/usr.bin/m4/serv.c
new file mode 100644
index 0000000..54a2e59
--- /dev/null
+++ b/usr.bin/m4/serv.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 1989
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)serv.c 5.4 (Berkeley) 1/21/94";
+#endif /* not lint */
+
+/*
+ * serv.c
+ * Facility: m4 macro processor
+ * by: oz
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mdef.h"
+#include "extr.h"
+#include "pathnames.h"
+
+extern ndptr lookup();
+extern ndptr addent();
+
+char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */
+
+/*
+ * expand - user-defined macro expansion
+ *
+ */
+expand(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register char *t;
+ register char *p;
+ register int n;
+ register int argno;
+
+ t = argv[0]; /* defn string as a whole */
+ p = t;
+ while (*p)
+ p++;
+ p--; /* last character of defn */
+ while (p > t) {
+ if (*(p-1) != ARGFLAG)
+ putback(*p);
+ else {
+ switch (*p) {
+
+ case '#':
+ pbnum(argc-2);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ((argno = *p - '0') < argc-1)
+ pbstr(argv[argno+1]);
+ break;
+ case '*':
+ for (n = argc - 1; n > 2; n--) {
+ pbstr(argv[n]);
+ putback(',');
+ }
+ pbstr(argv[2]);
+ break;
+ default :
+ putback(*p);
+ break;
+ }
+ p--;
+ }
+ p--;
+ }
+ if (p == t) /* do last character */
+ putback(*p);
+}
+
+/*
+ * dodefine - install definition in the table
+ *
+ */
+dodefine(name, defn)
+register char *name;
+register char *defn;
+{
+ register ndptr p;
+
+ if (!*name)
+ error("m4: null definition.");
+ if (strcmp(name, defn) == 0)
+ error("m4: recursive definition.");
+ if ((p = lookup(name)) == nil)
+ p = addent(name);
+ else if (p->defn != null)
+ free(p->defn);
+ if (!*defn)
+ p->defn = null;
+ else
+ p->defn = strdup(defn);
+ p->type = MACRTYPE;
+}
+
+/*
+ * dodefn - push back a quoted definition of
+ * the given name.
+ */
+
+dodefn(name)
+char *name;
+{
+ register ndptr p;
+
+ if ((p = lookup(name)) != nil && p->defn != null) {
+ putback(rquote);
+ pbstr(p->defn);
+ putback(lquote);
+ }
+}
+
+/*
+ * dopushdef - install a definition in the hash table
+ * without removing a previous definition. Since
+ * each new entry is entered in *front* of the
+ * hash bucket, it hides a previous definition from
+ * lookup.
+ */
+dopushdef(name, defn)
+register char *name;
+register char *defn;
+{
+ register ndptr p;
+
+ if (!*name)
+ error("m4: null definition");
+ if (strcmp(name, defn) == 0)
+ error("m4: recursive definition.");
+ p = addent(name);
+ if (!*defn)
+ p->defn = null;
+ else
+ p->defn = strdup(defn);
+ p->type = MACRTYPE;
+}
+
+/*
+ * dodumpdef - dump the specified definitions in the hash
+ * table to stderr. If nothing is specified, the entire
+ * hash table is dumped.
+ *
+ */
+dodump(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register int n;
+ ndptr p;
+
+ if (argc > 2) {
+ for (n = 2; n < argc; n++)
+ if ((p = lookup(argv[n])) != nil)
+ fprintf(stderr, dumpfmt, p->name,
+ p->defn);
+ }
+ else {
+ for (n = 0; n < HASHSIZE; n++)
+ for (p = hashtab[n]; p != nil; p = p->nxtptr)
+ fprintf(stderr, dumpfmt, p->name,
+ p->defn);
+ }
+}
+
+/*
+ * doifelse - select one of two alternatives - loop.
+ *
+ */
+doifelse(argv,argc)
+register char *argv[];
+register int argc;
+{
+ cycle {
+ if (strcmp(argv[2], argv[3]) == 0)
+ pbstr(argv[4]);
+ else if (argc == 6)
+ pbstr(argv[5]);
+ else if (argc > 6) {
+ argv += 3;
+ argc -= 3;
+ continue;
+ }
+ break;
+ }
+}
+
+/*
+ * doinclude - include a given file.
+ *
+ */
+doincl(ifile)
+char *ifile;
+{
+ if (ilevel+1 == MAXINP)
+ error("m4: too many include files.");
+ if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) {
+ ilevel++;
+ return (1);
+ }
+ else
+ return (0);
+}
+
+#ifdef EXTENDED
+/*
+ * dopaste - include a given file without any
+ * macro processing.
+ */
+dopaste(pfile)
+char *pfile;
+{
+ FILE *pf;
+ register int c;
+
+ if ((pf = fopen(pfile, "r")) != NULL) {
+ while((c = getc(pf)) != EOF)
+ putc(c, active);
+ (void) fclose(pf);
+ return(1);
+ }
+ else
+ return(0);
+}
+#endif
+
+/*
+ * dochq - change quote characters
+ *
+ */
+dochq(argv, argc)
+register char *argv[];
+register int argc;
+{
+ if (argc > 2) {
+ if (*argv[2])
+ lquote = *argv[2];
+ if (argc > 3) {
+ if (*argv[3])
+ rquote = *argv[3];
+ }
+ else
+ rquote = lquote;
+ }
+ else {
+ lquote = LQUOTE;
+ rquote = RQUOTE;
+ }
+}
+
+/*
+ * dochc - change comment characters
+ *
+ */
+dochc(argv, argc)
+register char *argv[];
+register int argc;
+{
+ if (argc > 2) {
+ if (*argv[2])
+ scommt = *argv[2];
+ if (argc > 3) {
+ if (*argv[3])
+ ecommt = *argv[3];
+ }
+ else
+ ecommt = ECOMMT;
+ }
+ else {
+ scommt = SCOMMT;
+ ecommt = ECOMMT;
+ }
+}
+
+/*
+ * dodivert - divert the output to a temporary file
+ *
+ */
+dodiv(n)
+register int n;
+{
+ if (n < 0 || n >= MAXOUT)
+ n = 0; /* bitbucket */
+ if (outfile[n] == NULL) {
+ m4temp[UNIQUE] = n + '0';
+ if ((outfile[n] = fopen(m4temp, "w")) == NULL)
+ error("m4: cannot divert.");
+ }
+ oindex = n;
+ active = outfile[n];
+}
+
+/*
+ * doundivert - undivert a specified output, or all
+ * other outputs, in numerical order.
+ */
+doundiv(argv, argc)
+register char *argv[];
+register int argc;
+{
+ register int ind;
+ register int n;
+
+ if (argc > 2) {
+ for (ind = 2; ind < argc; ind++) {
+ n = atoi(argv[ind]);
+ if (n > 0 && n < MAXOUT && outfile[n] != NULL)
+ getdiv(n);
+
+ }
+ }
+ else
+ for (n = 1; n < MAXOUT; n++)
+ if (outfile[n] != NULL)
+ getdiv(n);
+}
+
+/*
+ * dosub - select substring
+ *
+ */
+dosub (argv, argc)
+register char *argv[];
+register int argc;
+{
+ register char *ap, *fc, *k;
+ register int nc;
+
+ if (argc < 5)
+ nc = MAXTOK;
+ else
+#ifdef EXPR
+ nc = expr(argv[4]);
+#else
+ nc = atoi(argv[4]);
+#endif
+ ap = argv[2]; /* target string */
+#ifdef EXPR
+ fc = ap + expr(argv[3]); /* first char */
+#else
+ fc = ap + atoi(argv[3]); /* first char */
+#endif
+ if (fc >= ap && fc < ap+strlen(ap))
+ for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--)
+ putback(*k);
+}
+
+/*
+ * map:
+ * map every character of s1 that is specified in from
+ * into s3 and replace in s. (source s1 remains untouched)
+ *
+ * This is a standard implementation of map(s,from,to) function of ICON
+ * language. Within mapvec, we replace every character of "from" with
+ * the corresponding character in "to". If "to" is shorter than "from",
+ * than the corresponding entries are null, which means that those
+ * characters dissapear altogether. Furthermore, imagine
+ * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
+ * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
+ * ultimately maps to `*'. In order to achieve this effect in an efficient
+ * manner (i.e. without multiple passes over the destination string), we
+ * loop over mapvec, starting with the initial source character. if the
+ * character value (dch) in this location is different than the source
+ * character (sch), sch becomes dch, once again to index into mapvec, until
+ * the character value stabilizes (i.e. sch = dch, in other words
+ * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
+ * character, it will stabilize, since mapvec[0] == 0 at all times. At the
+ * end, we restore mapvec* back to normal where mapvec[n] == n for
+ * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
+ * about 5 times faster than any algorithm that makes multiple passes over
+ * destination string.
+ *
+ */
+
+map(dest,src,from,to)
+register char *dest;
+register char *src;
+register char *from;
+register char *to;
+{
+ register char *tmp;
+ register char sch, dch;
+ static char mapvec[128] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127
+ };
+
+ if (*src) {
+ tmp = from;
+ /*
+ * create a mapping between "from" and "to"
+ */
+ while (*from)
+ mapvec[*from++] = (*to) ? *to++ : (char) 0;
+
+ while (*src) {
+ sch = *src++;
+ dch = mapvec[sch];
+ while (dch != sch) {
+ sch = dch;
+ dch = mapvec[sch];
+ }
+ if (*dest = dch)
+ dest++;
+ }
+ /*
+ * restore all the changed characters
+ */
+ while (*tmp) {
+ mapvec[*tmp] = *tmp;
+ tmp++;
+ }
+ }
+ *dest = (char) 0;
+}
diff --git a/usr.bin/m4/stdd.h b/usr.bin/m4/stdd.h
new file mode 100644
index 0000000..8d4312e
--- /dev/null
+++ b/usr.bin/m4/stdd.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ozan Yigit at York University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)stdd.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * standard defines
+ */
+
+#define max(a,b) ((a) > (b)? (a): (b))
+#define min(a,b) ((a) < (b)? (a): (b))
+
+#define iswhite(c) ((c) == ' ' || (c) == '\t')
+
+/*
+ * STREQ is an optimised strcmp(a,b)==0
+ * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0
+ */
+#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
+#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)
+
+#define YES 1
+#define NO 0
diff --git a/usr.bin/mail/Makefile b/usr.bin/mail/Makefile
new file mode 100644
index 0000000..ac2f642
--- /dev/null
+++ b/usr.bin/mail/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.2 (Berkeley) 1/25/94
+
+PROG= mail
+CFLAGS+=-DUSE_OLD_TTY
+SRCS= version.c aux.c cmd1.c cmd2.c cmd3.c cmdtab.c collect.c edit.c fio.c \
+ getname.c head.c v7.local.c lex.c list.c main.c names.c popen.c \
+ quit.c send.c strings.c temp.c tty.c vars.c
+SFILES= mail.help mail.tildehelp
+EFILES= mail.rc
+LINKS= ${BINDIR}/mail ${BINDIR}/Mail
+MLINKS= mail.1 Mail.1
+
+beforeinstall:
+ cd ${.CURDIR}/misc; ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} \
+ -m 444 ${SFILES} ${DESTDIR}/usr/share/misc
+ cd ${.CURDIR}/misc; ${INSTALL} -c -o root -g wheel \
+ -m 644 ${EFILES} ${DESTDIR}/etc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mail/USD.doc/Makefile b/usr.bin/mail/USD.doc/Makefile
new file mode 100644
index 0000000..b31b448
--- /dev/null
+++ b/usr.bin/mail/USD.doc/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= usd/07.mail
+SRCS= mail0.nr mail1.nr mail2.nr mail3.nr mail4.nr mail5.nr mail6.nr \
+ mail7.nr mail8.nr mail9.nr maila.nr
+MACROS= -me
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/mail/USD.doc/mail0.nr b/usr.bin/mail/USD.doc/mail0.nr
new file mode 100644
index 0000000..15955be
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail0.nr
@@ -0,0 +1,71 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail0.nr 8.1 (Berkeley) 6/8/93
+.\"
+.eh 'USD:7-%''Mail Reference Manual'
+.oh 'Mail Reference Manual''USD:7-%'
+.if n \
+.nr fs .5v
+.\".he 'Mail Reference Manual'\n(mo/\n(dy/\n(yr'%'
+.tp
+.sp 1.0i
+.sz 12
+.rb
+.(l C
+MAIL REFERENCE MANUAL
+.)l
+.sz 10
+.sp 2
+.i
+.(l C
+Kurt Shoens
+.)l
+.r
+.(l C
+Revised by
+.)l
+.(l C
+.i
+Craig Leres\ \c
+.r
+and\ \c
+.i
+Mark Andrews
+.)l
+.r
+.(l C
+Version 5.5
+
+
+\*(td
+.)l
+.pn 2
diff --git a/usr.bin/mail/USD.doc/mail1.nr b/usr.bin/mail/USD.doc/mail1.nr
new file mode 100644
index 0000000..bbb920d
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail1.nr
@@ -0,0 +1,92 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail1.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 Introduction
+.pp
+.i Mail
+provides a simple and friendly environment for sending and receiving mail.
+It divides incoming mail into
+its constituent messages and allows the user to deal with them
+in any order. In addition, it provides a set of
+.i ed -\c
+like commands for manipulating messages and sending mail.
+.i Mail
+offers the user simple editing capabilities to ease the composition
+of outgoing messages, as well as providing the ability to define and send
+to names which address groups of users. Finally,
+.i Mail
+is able to send and receive messages across such networks as the
+ARPANET, UUCP, and Berkeley network.
+.pp
+This document describes how to use the
+.i Mail
+program to send and receive messages. The reader is not assumed to
+be familiar with other message handling systems, but should be
+familiar with the \s-2UNIX\s0\**
+.(f
+\** \s-1UNIX\s0 is a trademark of Bell Laboratories.
+.)f
+shell, the text editor, and some of the common \s-2UNIX\s0 commands.
+.q "The \s-2UNIX\s0 Programmer's Manual,"
+.q "An Introduction to Csh,"
+and
+.q "Text Editing with Ex and Vi"
+can be consulted for more information on these topics.
+.pp
+Here is how messages are handled:
+the mail system accepts incoming
+.i messages
+for you from other people
+and collects them in a file, called your
+.i "system mailbox" .
+When you login, the system notifies you if there are any messages
+waiting in your system mailbox. If you are a
+.i csh
+user, you will be notified when new mail arrives if you inform
+the shell of the location of your mailbox. On version 7 systems,
+your system mailbox is located in the directory /usr/spool/mail
+in a file with your login name. If your login name is
+.q sam,
+then you can make
+.i csh
+notify you of new mail by including the following line in your .cshrc
+file:
+.(l
+set mail=/usr/spool/mail/sam
+.)l
+When you read your mail using
+.i Mail ,
+it reads your system mailbox and separates that file into the
+individual messages that have been sent to you. You can then
+read, reply to, delete, or save these messages.
+Each message is marked with its author and the date they sent it.
diff --git a/usr.bin/mail/USD.doc/mail2.nr b/usr.bin/mail/USD.doc/mail2.nr
new file mode 100644
index 0000000..f64aaa6
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail2.nr
@@ -0,0 +1,617 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail2.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "Common usage"
+.pp
+The
+.i Mail
+command has two distinct usages, according to whether one
+wants to send or receive mail. Sending mail is simple: to send a
+message to a user whose login name is, say,
+\*(lqroot,\*(rq
+use the shell
+command:
+.(l
+% Mail root
+.)l
+then type your message. When you reach the end of the message, type
+an EOT (control\-d) at the beginning of a line, which will cause
+.i Mail
+to echo \*(lqEOT\*(rq and return you to the Shell. When the user you sent mail
+to next logs in, he will receive the message:
+.(l
+You have mail.
+.)l
+to alert him to the existence of your message.
+.pp
+If, while you are composing the message
+you decide that you do not wish to send it after all, you can
+abort the letter with a \s-2RUBOUT\s0. Typing a single \s-2RUBOUT\s0
+causes
+.i Mail
+to print
+.(l
+(Interrupt -- one more to kill letter)
+.)l
+Typing a second
+\s-2RUBOUT\s0 causes
+.i Mail
+to save your partial letter on the file
+.q dead.letter
+in your home directory and abort the letter.
+Once you have
+sent mail to someone, there is no way to undo the act, so be
+careful.
+.pp
+The message your recipient reads will consist of the message you
+typed, preceded by a line telling who sent the message (your login name)
+and the date and time it
+was sent.
+.pp
+If you want to send the same message to several other people, you can list
+their login names on the command line.
+Thus,
+.(l
+% Mail sam bob john
+Tuition fees are due next Friday. Don't forget!!
+<Control\-d>
+EOT
+%
+.)l
+will send the reminder to sam, bob, and john.
+.pp
+If, when you log in, you see the message,
+.(l
+You have mail.
+.)l
+you can read the mail by typing simply:
+.(l
+% Mail
+.)l
+.i Mail
+will respond by typing its version number and date and then listing
+the messages you have waiting. Then it will type a prompt and await
+your command. The messages are assigned numbers starting with 1 \*- you
+refer to the messages with these numbers.
+.i Mail
+keeps track of which messages are
+.i new
+(have been sent since you last read your mail) and
+.i read
+(have been read by you). New messages have an
+.b N
+next to them in the header listing and old, but unread messages have
+a
+.b U
+next to them.
+.i Mail
+keeps track of new/old and read/unread messages by putting a
+header field called
+.q Status
+into your messages.
+.pp
+To look at a specific message, use the
+.b type
+command, which may be abbreviated to simply
+.b t .
+For example, if you had the following messages:
+.(l
+N 1 root Wed Sep 21 09:21 "Tuition fees"
+N 2 sam Tue Sep 20 22:55
+.)l
+you could examine the first message by giving the command:
+.(l
+type 1
+.)l
+which might cause
+.i Mail
+to respond with, for example:
+.(l
+Message 1:
+From root Wed Sep 21 09:21:45 1978
+Subject: Tuition fees
+Status: R
+
+Tuition fees are due next Wednesday. Don't forget!!
+
+.)l
+Many
+.i Mail
+commands that operate on messages take a message number as an
+argument like the
+.b type
+command. For these commands, there is a notion of a current
+message. When you enter the
+.i Mail
+program, the current message is initially the first one. Thus,
+you can often omit the message number and use, for example,
+.(l
+t
+.)l
+to type the current message. As a further shorthand, you can type a message
+by simply giving its message number. Hence,
+.(l
+1
+.)l
+would type the first message.
+.pp
+Frequently, it is useful to read the messages in your mailbox in order,
+one after another. You can read the next message in
+.i Mail
+by simply typing a newline. As a special case, you can type a newline
+as your first command to
+.i Mail
+to type the first message.
+.pp
+If, after typing a message, you wish to immediately send a reply,
+you can do so with the
+.b reply
+command.
+.b Reply ,
+like
+.b type ,
+takes a message number as an argument.
+.i Mail
+then begins a message addressed to the user who sent you the message.
+You may then type in your letter in reply, followed by a <control-d>
+at the beginning of a line, as before.
+.i Mail
+will type EOT, then type the ampersand prompt to indicate its readiness
+to accept another command. In our example, if, after typing the
+first message, you wished to reply to it, you might give the command:
+.(l
+reply
+.)l
+.i Mail
+responds by typing:
+.(l
+To: root
+Subject: Re: Tuition fees
+.)l
+and waiting for you to enter your letter.
+You are now in the message collection mode described at the beginning
+of this section and
+.i Mail
+will gather up your message up to a control\-d.
+Note that it copies the subject
+header from the original message. This is useful in that correspondence
+about a particular matter will tend to retain the same subject heading,
+making it easy to recognize. If there are other header fields in
+the message, the information found will also be used.
+For example, if the letter had a
+.q "To:"
+header listing several recipients,
+.i Mail
+would arrange to send your replay to the same people as well.
+Similarly, if the original message contained a
+.q "Cc:"
+(carbon copies to) field,
+.i Mail
+would send your reply to
+.i those
+users, too.
+.i Mail
+is careful, though, not too send the message to
+.i you ,
+even if you appear in the
+.q "To:"
+or
+.q "Cc:"
+field, unless you ask to be included explicitly. See section 4 for more
+details.
+.pp
+After typing in your letter, the dialog with
+.i Mail
+might look like the following:
+.(l
+reply
+To: root
+Subject: Tuition fees
+
+Thanks for the reminder
+EOT
+&
+.)l
+.pp
+The
+.b reply
+command is especially useful for sustaining extended conversations
+over the message system, with other
+.q listening
+users receiving copies of the conversation. The
+.b reply
+command can be abbreviated to
+.b r .
+.pp
+Sometimes you will receive a message that has been sent to
+several people and wish to reply
+.i only
+to the person who sent it.
+.b Reply
+with a capital
+.b R
+replies to a message, but sends a copy to the sender only.
+.pp
+If you wish, while reading your mail, to send a message to someone,
+but not as a reply to one of your messages, you can send the message
+directly with the
+.b mail
+command, which takes as arguments the names of the recipients you wish
+to send to. For example, to send a message to
+.q frank,
+you would do:
+.(l
+mail frank
+This is to confirm our meeting next Friday at 4.
+EOT
+&
+.)l
+The
+.b mail
+command can be abbreviated to
+.b m .
+.pp
+Normally, each message you receive is saved in the file
+.i mbox
+in your login directory at the time you leave
+.i Mail .
+Often,
+however, you will not want to save a particular message you
+have received because it is only of passing interest. To avoid
+saving a message in
+.i mbox
+you can delete it using the
+.b delete
+command. In our example,
+.(l
+delete 1
+.)l
+will prevent
+.i Mail
+from saving message 1 (from root) in
+.i mbox .
+In addition to not saving deleted messages,
+.i Mail
+will not let
+you type them, either. The effect is to make the message disappear
+altogether, along with its number. The
+.b delete
+command can be abbreviated to simply
+.b d .
+.pp
+Many features of
+.i Mail
+can be tailored to your liking with the
+.b set
+command. The
+.b set
+command has two forms, depending on whether you are setting
+a
+.i binary
+option or a
+.i valued
+option.
+Binary options are either on or off. For example, the
+.q ask
+option informs
+.i Mail
+that each time you send a message, you want it to prompt you for
+a subject header, to be included in the message.
+To set the
+.q ask
+option, you would type
+.(l
+set ask
+.)l
+.pp
+Another useful
+.i Mail
+option is
+.q hold.
+Unless told otherwise,
+.i Mail
+moves the messages from your system mailbox to the file
+.i mbox
+in your home directory when you leave
+.i Mail .
+If you want
+.i Mail
+to keep your letters in the system mailbox instead, you can set the
+.q hold
+option.
+.pp
+Valued options are values which
+.i Mail
+uses to adapt to your tastes. For example, the
+.q SHELL
+option tells
+.i Mail
+which shell you like to use, and is specified by
+.(l
+set SHELL=/bin/csh
+.)l
+for example. Note that no spaces are allowed in
+.q "SHELL=/bin/csh."
+A complete list of the
+.i Mail
+options appears in section 5.
+.pp
+Another important valued option is
+.q crt.
+If you use a fast video terminal, you will find that when you
+print long messages, they fly by too quickly for you to read them.
+With the
+.q crt
+option, you can make
+.i Mail
+print any message larger than a given number of lines by sending
+it through a paging program. This program is specified by the
+valued option \fBPAGER\fP.
+If \fBPAGER\fP is not set, a default paginator is used.
+For example, most CRT users with 24-line screens should do:
+.(l
+set crt=24
+.)l
+to paginate messages that will not fit on their screens.
+In the default state, \fImore\fP (default paginator) prints a screenful of
+information, then types --More--. Type a space to see the next screenful.
+.pp
+Another adaptation to user needs that
+.i Mail
+provides is that of
+.i aliases .
+An alias is simply a name which stands for one or more
+real user names.
+.i Mail
+sent to an alias is really sent to the list of real users
+associated with it. For example, an alias can be defined for the
+members of a project, so that you can send mail to the whole project
+by sending mail to just a single name. The
+.b alias
+command in
+.i Mail
+defines an alias. Suppose that the users in a project are
+named Sam, Sally, Steve, and Susan. To define an alias called
+.q project
+for them, you would use the
+.i Mail
+command:
+.(l
+alias project sam sally steve susan
+.)l
+The
+.b alias
+command can also be used to provide a convenient name for someone
+whose user name is inconvenient. For example, if a user named
+.q "Bob Anderson"
+had the login name
+.q anderson,"
+you might want to use:
+.(l
+alias bob anderson
+.)l
+so that you could send mail to the shorter name,
+.q bob.
+.pp
+While the
+.b alias
+and
+.b set
+commands allow you to customize
+.i Mail ,
+they have the drawback that they must be retyped each time you enter
+.i Mail .
+To make them more convenient to use,
+.i Mail
+always looks for two files when it is invoked. It first reads
+a system wide file
+.q /usr/lib/Mail.rc,
+then a user specific file,
+.q .mailrc,
+which is found in the user's home directory.
+The system wide file
+is maintained by the system administrator and
+contains
+.b set
+commands that are applicable to all users of the system.
+The
+.q .mailrc
+file is usually used by each user to set options the way he likes
+and define individual aliases.
+For example, my .mailrc file looks like this:
+.(l
+set ask nosave SHELL=/bin/csh
+.)l
+As you can see, it is possible to set many options in the
+same
+.b set
+command. The
+.q nosave
+option is described in section 5.
+.pp
+Mail aliasing is implemented
+at the system-wide level
+by the mail delivery
+system
+.i sendmail .
+These aliases are stored in the file /usr/lib/aliases and are
+accessible to all users of the system.
+The lines in /usr/lib/aliases are of
+the form:
+.(l
+alias: name\*<1\*>, name\*<2\*>, name\*<3\*>
+.)l
+where
+.i alias
+is the mailing list name and the
+.i name\*<i\*>
+are the members of the list. Long lists can be continued onto the next
+line by starting the next line with a space or tab. Remember that you
+must execute the shell command
+.i newaliases
+after editing /usr/lib/aliases since the delivery system
+uses an indexed file created by
+.i newaliases .
+.pp
+We have seen that
+.i Mail
+can be invoked with command line arguments which are people
+to send the message to, or with no arguments to read mail.
+Specifying the
+.rb \-f
+flag on the command line causes
+.i Mail
+to read messages from a file other than your system mailbox.
+For example, if you have a collection of messages in
+the file
+.q letters
+you can use
+.i Mail
+to read them with:
+.(l
+% Mail \-f letters
+.)l
+You can use all
+the
+.i Mail
+commands described in this document to examine, modify, or delete
+messages from your
+.q letters
+file, which will be rewritten when you leave
+.i Mail
+with the
+.b quit
+command described below.
+.pp
+Since mail that you read is saved in the file
+.i mbox
+in your home directory by default, you can read
+.i mbox
+in your home directory by using simply
+.(l
+% Mail \-f
+.)l
+.pp
+Normally, messages that you examine using the
+.b type
+command are saved in the file
+.q mbox
+in your home directory if you leave
+.i Mail
+with the
+.b quit
+command described below.
+If you wish to retain a message in your system mailbox
+you can use the
+.b preserve
+command to tell
+.i Mail
+to leave it there.
+The
+.b preserve
+command accepts a list of message numbers, just like
+.b type
+and may be abbreviated to
+.b pre .
+.pp
+Messages in your system mailbox that you do not examine are
+normally retained in your system mailbox automatically.
+If you wish to have such a message saved in
+.i mbox
+without reading it, you may use the
+.b mbox
+command to have them so saved. For example,
+.(l
+mbox 2
+.)l
+in our example would cause the second message (from sam)
+to be saved in
+.i mbox
+when the
+.b quit
+command is executed.
+.b Mbox
+is also the way to direct messages to your
+.i mbox
+file if you have set the
+.q hold
+option described above.
+.b Mbox
+can be abbreviated to
+.b mb .
+.pp
+When you have perused all the messages of interest, you can leave
+.i Mail
+with the
+.b quit
+command, which saves the messages you have typed but not
+deleted in the file
+.i mbox
+in your login directory. Deleted messages are discarded irretrievably,
+and messages left untouched are preserved in your system mailbox so
+that you will see them the next time you type:
+.(l
+% Mail
+.)l
+The
+.b quit
+command can be abbreviated to simply
+.b q .
+.pp
+If you wish for some reason to leave
+.i Mail
+quickly without altering either your system mailbox or
+.i mbox ,
+you can type the
+.b x
+command (short for
+.b exit ),
+which will immediately return you to the Shell without changing anything.
+.pp
+If, instead, you want to execute a Shell command without leaving
+.i Mail ,
+you
+can type the command preceded by an exclamation point, just as in the
+text editor. Thus, for instance:
+.(l
+!date
+.)l
+will print the current date without leaving
+.i Mail .
+.pp
+Finally, the
+.b help
+command is available to print out a brief summary of the
+.i Mail
+commands, using only the single character command abbreviations.
diff --git a/usr.bin/mail/USD.doc/mail3.nr b/usr.bin/mail/USD.doc/mail3.nr
new file mode 100644
index 0000000..64f7634
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail3.nr
@@ -0,0 +1,133 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail3.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Maintaining folders"
+.pp
+.i Mail
+includes a simple facility for maintaining groups of messages together
+in folders. This section describes this facility.
+.pp
+To use the folder facility, you must tell
+.i Mail
+where you wish to keep your folders. Each folder of messages will
+be a single file. For convenience, all of your folders are kept in
+a single directory of your choosing. To tell
+.i Mail
+where your folder directory is, put a line of the form
+.(l
+set folder=letters
+.)l
+in your
+.i .mailrc
+file. If, as in the example above, your folder directory does not
+begin with a `/,'
+.i Mail
+will assume that your folder directory is to be found starting from
+your home directory. Thus, if your home directory is
+.b /usr/person
+the above example told
+.i Mail
+to find your folder directory in
+.b /usr/person/letters .
+.pp
+Anywhere a file name is expected, you can use a folder name, preceded
+with `+.' For example, to put a message into a folder with the
+.b save
+command, you can use:
+.(l
+save +classwork
+.)l
+to save the current message in the
+.i classwork
+folder. If the
+.i classwork
+folder does not yet exist, it will be created. Note that messages
+which are saved with the
+.b save
+command are automatically removed from your system mailbox.
+.pp
+In order to make a copy of a message in a folder without causing
+that message to be removed from your system mailbox, use the
+.b copy
+command, which is identical in all other respects to the
+.b save
+command. For example,
+.(l
+copy +classwork
+.)l
+copies the current message into the
+.i classwork
+folder and leaves a copy in your system mailbox.
+.pp
+The
+.b folder
+command
+can be used to direct
+.i Mail
+to the contents of a different folder.
+For example,
+.(l
+folder +classwork
+.)l
+directs
+.i Mail
+to read the contents of the
+.i classwork
+folder. All of the commands that you can use on your system
+mailbox are also applicable to folders, including
+.b type ,
+.b delete ,
+and
+.b reply .
+To inquire which folder you are currently editing, use simply:
+.(l
+folder
+.)l
+.pp
+To list your current set of folders, use the
+.b folders
+command.
+.pp
+To start
+.i Mail
+reading one of your folders, you can use the
+.b \-f
+option described in section 2. For example:
+.(l
+% Mail \-f +classwork
+.)l
+will cause
+.i Mail
+to read your
+.i classwork
+folder without looking at your system mailbox.
diff --git a/usr.bin/mail/USD.doc/mail4.nr b/usr.bin/mail/USD.doc/mail4.nr
new file mode 100644
index 0000000..b67bf03
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail4.nr
@@ -0,0 +1,437 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail4.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "More about sending mail"
+.sh 2 "Tilde escapes"
+.pp
+While typing in a message to be sent to others, it is often
+useful to be able to invoke the text editor on the partial message,
+print the message, execute a shell command, or do some other
+auxiliary function.
+.i Mail
+provides these capabilities through
+.i "tilde escapes" ,
+which consist of a tilde (~) at the beginning of a line, followed by
+a single character which indicates the function to be performed. For
+example, to print the text of the message so far, use:
+.(l
+~p
+.)l
+which will print a line of dashes, the recipients of your message, and
+the text of the message so far.
+Since
+.i Mail
+requires two consecutive \s-2RUBOUT\s0's to abort a letter, you
+can use a single \s-2RUBOUT\s0 to abort the output of ~p or any other
+~ escape without killing your letter.
+.pp
+If you are dissatisfied with the message as
+it stands, you can invoke the text editor on it using the escape
+.(l
+~e
+.)l
+which causes the message to be copied into a temporary file and an
+instance of the editor to be spawned. After modifying the message to
+your satisfaction, write it out and quit the editor.
+.i Mail
+will respond
+by typing
+.(l
+(continue)
+.)l
+after which you may continue typing text which will be appended to your
+message, or type <control-d> to end the message.
+A standard text editor is provided by
+.i Mail .
+You can override this default by setting the valued option
+.q EDITOR
+to something else. For example, you might prefer:
+.(l
+set EDITOR=/usr/ucb/ex
+.)l
+.pp
+Many systems offer a screen editor as an alternative to the standard
+text editor, such as the
+.i vi
+editor from UC Berkeley.
+To use the screen, or
+.i visual
+editor, on your current message, you can use the escape,
+.(l
+~v
+.)l
+~v works like ~e, except that the screen editor is invoked instead.
+A default screen editor is defined by
+.i Mail .
+If it does not suit you, you can set the valued option
+.q VISUAL
+to the path name of a different editor.
+.pp
+It is often useful to be able to include the contents of some
+file in your message; the escape
+.(l
+~r filename
+.)l
+is provided for this purpose, and causes the named file to be appended
+to your current message.
+.i Mail
+complains if the file doesn't exist
+or can't be read. If the read is successful, the number of lines and
+characters appended to your message is printed, after which you may continue
+appending text. The filename may contain shell metacharacters like * and ?
+which are expanded according to the conventions of your shell.
+.pp
+As a special case of ~r, the escape
+.(l
+~d
+.)l
+reads in the file
+.q dead.letter
+in your home directory. This is often useful since
+.i Mail
+copies the text
+of your message there when you abort a message with \s-2RUBOUT\s0.
+.pp
+To save the current text of your message on a file you may use the
+.(l
+~w filename
+.)l
+escape.
+.i Mail
+will print out the number of lines and characters written
+to the file, after which you may continue appending text to your message.
+Shell metacharacters may be used in the filename, as in ~r and are expanded
+with the conventions of your shell.
+.pp
+If you are sending mail from within
+.i Mail's
+command mode
+you can read a message sent to you into the message
+you are constructing with the escape:
+.(l
+~m 4
+.)l
+which will read message 4 into the current message, shifted right by
+one tab stop. You can name any non-deleted message, or list of messages.
+Messages can also be forwarded without shifting by a tab stop with ~f.
+This is the usual way to forward a message.
+.pp
+If, in the process of composing a message, you decide to add additional
+people to the list of message recipients, you can do so with the escape
+.(l
+~t name1 name2 ...
+.)l
+You may name as few or many additional recipients as you wish. Note
+that the users originally on the recipient list will still receive
+the message; you cannot remove someone from the recipient
+list with ~t.
+.pp
+If you wish, you can associate a subject with your message by using the
+escape
+.(l
+~s Arbitrary string of text
+.)l
+which replaces any previous subject with
+.q "Arbitrary string of text."
+The subject, if given, is sent near the
+top of the message prefixed with
+.q "Subject:"
+You can see what the message will look like by using ~p.
+.pp
+For political reasons, one occasionally prefers to list certain
+people as recipients of carbon copies of a message rather than
+direct recipients. The escape
+.(l
+~c name1 name2 ...
+.)l
+adds the named people to the
+.q "Cc:"
+list, similar to ~t.
+Again, you can execute ~p to see what the message will look like.
+.pp
+The escape
+.(l
+~b name1 name2 ...
+.)l
+adds the named people to the
+.q "Cc:"
+list, but does not make the names visible in the
+.q "Cc:"
+line ("blind" carbon copy).
+.pp
+The recipients of the message together constitute the
+.q "To:"
+field, the subject the
+.q "Subject:"
+field, and the carbon copies the
+.q "Cc:"
+field. If you wish to edit these in ways impossible with the ~t, ~s, ~c
+and ~b escapes, you can use the escape
+.(l
+~h
+.)l
+which prints
+.q "To:"
+followed by the current list of recipients and leaves the cursor
+(or printhead) at the end of the line. If you type in ordinary
+characters, they are appended to the end of the current list of
+recipients. You can also use your erase character to erase back into
+the list of recipients, or your kill character to erase them altogether.
+Thus, for example, if your erase and kill characters are the standard
+(on printing terminals) # and @ symbols,
+.(l
+~h
+To: root kurt####bill
+.)l
+would change the initial recipients
+.q "root kurt"
+to
+.q "root bill."
+When you type a newline,
+.i Mail
+advances to the
+.q "Subject:"
+field, where the same rules apply. Another newline brings you to
+the
+.q "Cc:"
+field, which may be edited in the same fashion. Another newline
+brings you to the
+.q "Bcc:"
+("blind" carbon copy) field, which follows the same rules as the "Cc:"
+field. Another newline
+leaves you appending text to the end of your message. You can use
+~p to print the current text of the header fields and the body
+of the message.
+.pp
+To effect a temporary escape to the shell, the escape
+.(l
+~!command
+.)l
+is used, which executes
+.i command
+and returns you to mailing mode without altering the text of
+your message. If you wish, instead, to filter the body of your
+message through a shell command, then you can use
+.(l
+~|command
+.)l
+which pipes your message through the command and uses the output
+as the new text of your message. If the command produces no output,
+.i Mail
+assumes that something is amiss and retains the old version
+of your message. A frequently-used filter is the command
+.i fmt ,
+designed to format outgoing mail.
+.pp
+To effect a temporary escape to
+.i Mail
+command mode instead, you can use the
+.(l
+~:\fIMail command\fP
+.)l
+escape. This is especially useful for retyping the message you are
+replying to, using, for example:
+.(l
+~:t
+.)l
+It is also useful for setting options and modifying aliases.
+.pp
+If you wish abort the current message, you can use the escape
+.(l
+~q
+.)l
+This will terminate the current message and return you to the
+shell (or \fIMail\fP if you were using the \fBmail\fP command).
+If the \fBsave\fP option is set, the message will be copied
+to the file
+.q dead.letter
+in your home directory.
+.pp
+If you wish (for some reason) to send a message that contains
+a line beginning with a tilde, you must double it. Thus, for example,
+.(l
+~~This line begins with a tilde.
+.)l
+sends the line
+.(l
+~This line begins with a tilde.
+.)l
+.pp
+Finally, the escape
+.(l
+~?
+.)l
+prints out a brief summary of the available tilde escapes.
+.pp
+On some terminals (particularly ones with no lower case)
+tilde's are difficult to type.
+.i Mail
+allows you to change the escape character with the
+.q escape
+option. For example, I set
+.(l
+set escape=]
+.)l
+and use a right bracket instead of a tilde. If I ever need to
+send a line beginning with right bracket, I double it, just as for ~.
+Changing the escape character removes the special meaning of ~.
+.sh 2 "Network access"
+.pp
+This section describes how to send mail to people on other machines.
+Recall that sending to a plain login name sends mail to that person
+on your machine. If your machine is directly (or sometimes, even,
+indirectly) connected to the Arpanet, you can send messages to people
+on the Arpanet using a name of the form
+.(l
+name@host.domain
+.)l
+where
+.i name
+is the login name of the person you're trying to reach,
+.i host
+is the name of the machine on the Arpanet,
+and
+.i domain
+is the higher-level scope within which the hostname is known, e.g. EDU (for educational
+institutions), COM (for commercial entities), GOV (for governmental agencies),
+ARPA for many other things, BITNET or CSNET for those networks.
+.pp
+If your recipient logs in on a machine connected to yours by
+UUCP (the Bell Laboratories supplied network that communicates
+over telephone lines), sending mail can be a bit more complicated.
+You must know the list of machines through which your message must
+travel to arrive at his site. So, if his machine is directly connected
+to yours, you can send mail to him using the syntax:
+.(l
+host!name
+.)l
+where, again,
+.i host
+is the name of the machine and
+.i name
+is the login name.
+If your message must go through an intermediary machine first, you
+must use the syntax:
+.(l
+intermediary!host!name
+.)l
+and so on. It is actually a feature of UUCP that the map of all
+the systems in the network is not known anywhere (except where people
+decide to write it down for convenience). Talk to your system administrator
+about good ways to get places; the
+.i uuname
+command will tell you systems whose names are recognized, but not which
+ones are frequently called or well-connected.
+.pp
+When you use the
+.b reply
+command to respond to a letter, there is a problem of figuring out the
+names of the users in the
+.q "To:"
+and
+.q "Cc:"
+lists
+.i "relative to the current machine" .
+If the original letter was sent to you by someone on the local machine,
+then this problem does not exist, but if the message came from a remote
+machine, the problem must be dealt with.
+.i Mail
+uses a heuristic to build the correct name for each user relative
+to the local machine. So, when you
+.b reply
+to remote mail, the names in the
+.q "To:"
+and
+.q "Cc:"
+lists may change somewhat.
+.sh 2 "Special recipients"
+.pp
+As described previously, you can send mail to either user names or
+.b alias
+names. It is also possible to send messages directly to files or to
+programs, using special conventions. If a recipient name has a
+`/' in it or begins with a `+', it is assumed to be the
+path name of a file into which
+to send the message. If the file already exists, the message is
+appended to the end of the file. If you want to name a file in
+your current directory (ie, one for which a `/' would not usually
+be needed) you can precede the name with `./'
+So, to send mail to the file
+.q memo
+in the current directory, you can give the command:
+.(l
+% Mail ./memo
+.)l
+If the name begins with a `+,' it is expanded into the full path name
+of the folder name in your folder directory.
+This ability to send mail to files can be used for a variety of
+purposes, such as maintaining a journal and keeping a record of
+mail sent to a certain group of users. The second example can be
+done automatically by including the full pathname of the record
+file in the
+.b alias
+command for the group. Using our previous
+.b alias
+example, you might give the command:
+.(l
+alias project sam sally steve susan /usr/project/mail_record
+.)l
+Then, all mail sent to "project" would be saved on the file
+.q /usr/project/mail_record
+as well as being sent to the members of the project. This file
+can be examined using
+.i "Mail \-f" .
+.pp
+It is sometimes useful to send mail directly to a program, for
+example one might write a project billboard program and want to access
+it using
+.i Mail .
+To send messages to the billboard program, one can send mail
+to the special name `|billboard' for example.
+.i Mail
+treats recipient names that begin with a `|' as a program to send
+the mail to. An
+.b alias
+can be set up to reference a `|' prefaced name if desired.
+.i Caveats :
+the shell treats `|' specially, so it must be quoted on the command
+line. Also, the `| program' must be presented as a single argument to
+mail. The safest course is to surround the entire name with double
+quotes. This also applies to usage in the
+.b alias
+command. For example, if we wanted to alias `rmsgs' to `rmsgs \-s'
+we would need to say:
+.(l
+alias rmsgs "| rmsgs -s"
+.)l
diff --git a/usr.bin/mail/USD.doc/mail5.nr b/usr.bin/mail/USD.doc/mail5.nr
new file mode 100644
index 0000000..b70ce95
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail5.nr
@@ -0,0 +1,1041 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail5.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "Additional features"
+.pp
+This section describes some additional commands useful for
+reading your mail, setting options, and handling lists of messages.
+.sh 2 "Message lists"
+.pp
+Several
+.i Mail
+commands accept a list of messages as an argument.
+Along with
+.b type
+and
+.b delete ,
+described in section 2,
+there is the
+.b from
+command, which prints the message headers associated with the
+message list passed to it.
+The
+.b from
+command is particularly useful in conjunction with some of the
+message list features described below.
+.pp
+A
+.i "message list"
+consists of a list of message numbers, ranges, and names,
+separated by spaces or tabs. Message numbers may be either
+decimal numbers, which directly specify messages, or one of the
+special characters
+.q \(ua
+.q "."
+or
+.q "$"
+to specify the first relevant, current, or last
+relevant message, respectively.
+.i Relevant
+here means, for most commands
+.q "not deleted"
+and
+.q "deleted"
+for the
+.b undelete
+command.
+.pp
+A range of messages consists of two message numbers (of the form
+described in the previous paragraph) separated by a dash.
+Thus, to print the first four messages, use
+.(l
+type 1\-4
+.)l
+and to print all the messages from the current message to the last
+message, use
+.(l
+type .\-$
+.)l
+.pp
+A
+.i name
+is a user name. The user names given in the message list are
+collected together and each message selected by other means
+is checked to make sure it was sent by one of the named users.
+If the message consists entirely of user names, then every
+message sent by one of those users that is
+.i relevant
+(in the sense described earlier)
+is selected. Thus, to print every message sent to you by
+.q root,
+do
+.(l
+type root
+.)l
+.pp
+As a shorthand notation, you can specify simply
+.q *
+to get every
+.i relevant
+(same sense)
+message. Thus,
+.(l
+type *
+.)l
+prints all undeleted messages,
+.(l
+delete *
+.)l
+deletes all undeleted messages, and
+.(l
+undelete *
+.)l
+undeletes all deleted messages.
+.pp
+You can search for the presence of a word in subject lines with
+.b / .
+For example, to print the headers of all messages that contain the
+word
+.q PASCAL,
+do:
+.(l
+from /pascal
+.)l
+Note that subject searching ignores upper/lower case differences.
+.sh 2 "List of commands"
+.pp
+This section describes all the
+.i Mail
+commands available when
+receiving mail.
+.ip "\fB\-\fP\ \ "
+The
+.rb \-
+command goes to the previous message and prints it. The
+.rb \-
+command may be given a decimal number
+.i n
+as an argument, in which case the
+.i n th
+previous message is gone to and printed.
+.ip "\fB?\fP\ \ "
+Prints a brief summary of commands.
+.ip "\fB!\fP\ \ "
+Used to preface a command to be executed by the shell.
+.ip "\fBPrint\fP\ \ "
+Like
+.b print ,
+but also print out ignored header fields. See also
+\fBprint\fP, \fBignore\fP and \fBretain\fP.
+\fBPrint\fP can be abbreviated to \fBP\fP.
+.ip "\fBReply\fP or \fBRespond\fP\ \ "
+Note the capital \fBR\fP in the name.
+Frame a reply to a one or more messages.
+The reply (or replies if you are using this on multiple messages)
+will be sent ONLY to the person who sent you the message
+(respectively, the set of people who sent the messages you are
+replying to).
+You can
+add people using the \fB~t\fP, \fB~c\fP and \fB~b\fP
+tilde escapes. The subject in your reply is formed by prefacing the
+subject in the original message with
+.q "Re:"
+unless it already began thus.
+If the original message included a
+.q "reply-to"
+header field, the reply will go
+.i only
+to the recipient named by
+.q "reply-to."
+You type in your message using the same conventions available to you
+through the
+.b mail
+command.
+The
+.b Reply
+command is especially useful for replying to messages that were sent
+to enormous distribution groups when you really just want to
+send a message to the originator. Use it often.
+\fBReply\fP (and \fBRespond\fP) can be abbreviated to \fBR\fP.
+.ip "\fBType\fP\ \ "
+Identical to the
+.b Print
+command.
+\fBType\fP can be abbreviated to \fBT\fP.
+.ip "\fBalias\fP\ \ "
+Define a name to stand for a set of other names.
+This is used when you want to send messages to a certain
+group of people and want to avoid retyping their names.
+For example
+.(l
+alias project john sue willie kathryn
+.)l
+creates an alias
+.i project
+which expands to the four people John, Sue, Willie, and Kathryn.
+If no arguments are given, all currently-defined aliases are printed.
+If one argument is given, that alias is printed (if it exists).
+\fBAlias\fP can be abbreviated to \fBa\fP.
+.ip "\fBalternates\fP\ \ "
+If you have accounts on several machines, you may find it convenient
+to use the /usr/lib/aliases on all the machines except one to direct
+your mail to a single account.
+The
+.b alternates
+command is used to inform
+.i Mail
+that each of these other addresses is really
+.i you .
+.i Alternates
+takes a list of user names and remembers that they are all actually you.
+When you
+.b reply
+to messages that were sent to one of these alternate names,
+.i Mail
+will not bother to send a copy of the message to this other address (which
+would simply be directed back to you by the alias mechanism).
+If
+.i alternates
+is given no argument, it lists the current set of alternate names.
+.b Alternates
+is usually used in the .mailrc file.
+\fBAlternates\fP can be abbreviated to \fBalt\fP.
+.ip "\fBchdir\fP\ \ "
+The
+.b chdir
+command allows you to change your current directory.
+.b Chdir
+takes a single argument, which is taken to be the pathname of
+the directory to change to. If no argument is given,
+.b chdir
+changes to your home directory.
+\fBChdir\fP can be abbreviated to \fBc\fP.
+.ip "\fBcopy\fP\ \ "
+The
+.b copy
+command does the same thing that
+.b save
+does, except that it does not mark the messages it is used on
+for deletion when you quit.
+\fBCopy\fP can be abbreviated to \fBco\fP.
+.ip "\fBdelete\fP\ \ "
+Deletes a list of messages. Deleted messages can be reclaimed
+with the
+.b undelete
+command.
+\fBDelete\fP can be abbreviated to \fBd\fP.
+.ip "\fBdp\fP or \fBdt\fP\ \ "
+These
+commands delete the current message and print the next message.
+They are useful for quickly reading and disposing of mail.
+If there is no next message, \fImail\fP says ``at EOF.''
+.ip "\fBedit\fP\ \ "
+To edit individual messages using the text editor, the
+.b edit
+command is provided. The
+.b edit
+command takes a list of messages as described under the
+.b type
+command and processes each by writing it into the file
+Message\c
+.i x
+where
+.i x
+is the message number being edited and executing the text editor on it.
+When you have edited the message to your satisfaction, write the message
+out and quit, upon which
+.i Mail
+will read the message back and remove the file.
+.b Edit
+can be abbreviated to
+.b e .
+.ip "\fBelse\fP\ \ "
+Marks the end of the then-part of an
+.b if
+statement and the beginning of the
+part to take effect if the condition of the
+.b if
+statement is false.
+.ip "\fBendif\fP\ \ "
+Marks the end of an
+.b if
+statement.
+.ip "\fBexit\fP or \fBxit\fP\ \ "
+Leave
+.i Mail
+without updating the system mailbox or the file your were reading.
+Thus, if you accidentally delete several messages, you can use
+.b exit
+to avoid scrambling your mailbox.
+\fBExit\fP can be abbreviated to \fBex\fP or \fBx\fP.
+.ip "\fBfile\fP\ \ "
+The same as
+.b folder .
+\fBFile\fP can be abbreviated to \fBfi\fP.
+.ip "\fBfolders\fP\ \ "
+List the names of the folders in your folder directory.
+.ip "\fBfolder\fP\ \ "
+The
+.b folder
+command switches to a new mail file or folder. With no arguments, it
+tells you which file you are currently reading. If you give
+it an argument, it will write out changes (such as deletions)
+you have made in the current file and read the new file.
+Some special conventions are recognized for the name:
+.(b
+.TS
+center;
+c c
+l a.
+Name Meaning
+_
+# Previous file read
+% Your system mailbox
+%name \fIName\fP's system mailbox
+& Your ~/mbox file
++folder A file in your folder directory
+.TE
+.)b
+\fBFolder\fP can be abbreviated to \fBfo\fP.
+.ip "\fBfrom\fP\ \ "
+The
+.b from
+command takes a list of messages and prints out the header lines for each one;
+hence
+.(l
+from joe
+.)l
+is the easy way to display all the message headers from \*(lqjoe.\*(rq
+\fBFrom\fP can be abbreviated to \fBf\fP.
+.ip "\fBheaders\fP\ \ "
+When you start up
+.i Mail
+to read your mail, it lists the message headers that you have.
+These headers tell you who each message is from, when they were
+received, how many lines and characters each message is, and the
+.q "Subject:"
+header field of each message, if present. In addition,
+.i Mail
+tags the message header of each message that has been the object
+of the
+.b preserve
+command with a
+.q P.
+Messages that have been
+.b saved
+or
+.b written
+are flagged with a
+.q *.
+Finally,
+.b deleted
+messages are not printed at all. If you wish to reprint the current
+list of message headers, you can do so with the
+.b headers
+command. The
+.b headers
+command (and thus the initial header listing)
+only lists the first so many message headers.
+The number of headers listed depends on the speed of your
+terminal.
+This can be overridden by specifying the number of headers you
+want with the
+.i window
+option.
+.i Mail
+maintains a notion of the current
+.q window
+into your messages for the purposes of printing headers.
+Use the
+.b z
+command to move forward and back a window.
+You can move
+.i Mail's
+notion of the current window directly to a particular message by
+using, for example,
+.(l
+headers 40
+.)l
+to move
+.i Mail's
+attention to the messages around message 40.
+If a ``+'' argument is given, then the next screenful of message headers is
+printed, and if a ``\-'' argument is given, the previous screenful of message
+headers is printed.
+\fBHeaders\fP can be abbreviated to \fBh\fP.
+.ip "\fBhelp\fP\ \ "
+Print a brief and usually out of date help message about the commands
+in
+.i Mail .
+The
+.i man
+page for
+.i mail
+is usually more up-to-date than either the help message or this manual.
+It is also a synonym for \fB?\fP.
+.ip "\fBhold\fP\ \ "
+Arrange to hold a list of messages in the system mailbox, instead
+of moving them to the file
+.i mbox
+in your home directory. If you set the binary option
+.i hold ,
+this will happen by default.
+It does not override the \fBdelete\fP command.
+\fBHold\fP can be abbreviated to \fBho\fP.
+.ip "\fBif\fP\ \ "
+Commands in your
+.q .mailrc
+file can be executed conditionally depending on whether you are
+sending or receiving mail with the
+.b if
+command. For example, you can do:
+.(l
+if receive
+ \fIcommands\fP...
+endif
+.)l
+An
+.b else
+form is also available:
+.(l
+if send
+ \fIcommands\fP...
+else
+ \fIcommands\fP...
+endif
+.)l
+Note that the only allowed conditions are
+.b receive
+and
+.b send .
+.ip "\fBignore\fP \ \ "
+.b N.B.:
+.i Ignore
+has been superseded by
+.i retain.
+.br
+Add the list of header fields named to the
+.i "ignore list" .
+Header fields in the ignore list are not printed on your
+terminal when you print a message. This allows you to suppress
+printing of certain machine-generated header fields, such as
+.i Via
+which are not usually of interest. The
+.b Type
+and
+.b Print
+commands can be used to print a message in its entirety, including
+ignored fields.
+If
+.b ignore
+is executed with no arguments, it lists the current set of ignored fields.
+.ip "\fBlist\fP\ \ "
+List the valid
+.i Mail
+commands.
+\fBList\fP can be abbreviated to \fBl\fP.
+.. .ip \fBlocal\fP
+.. Define a list of local names for this host. This command is useful
+.. when the host is known by more than one name. Names in the list
+.. may be qualified be the domain of the host. The first name on the local
+.. list is the
+.. .i distinguished
+.. name of the host.
+.. The names on the local list are used by
+.. .i Mail
+.. to decide which addresses are local to the host.
+.. For example:
+.. .(l
+.. local ucbarpa.BERKELEY.ARPA arpa.BERKELEY.ARPA \\
+.. arpavax.BERKELEY.ARPA r.BERKELEY.ARPA \\
+.. ucb-arpa.ARPA
+.. .)l
+.. From this list we see that
+.. .i "fred@ucbarpa.BERKELEY.ARPA",
+.. .i "harold@arpa.BERKELEY",
+.. and
+.. .i "larry@r"
+.. are all addresses of users on the local host.
+.. The
+.. .b local
+.. command is usually not used be general users since it is designed for
+.. local configuration; it is usually found in the file /usr/lib/Mail.rc.
+.ip "\fBmail\fP\ \ "
+Send mail to one or more people. If you have the
+.i ask
+option set,
+.i Mail
+will prompt you for a subject to your message. Then you
+can type in your message, using tilde escapes as described in
+section 4 to edit, print, or modify your message. To signal your
+satisfaction with the message and send it, type control-d at the
+beginning of a line, or a . alone on a line if you set the option
+.i dot .
+To abort the message, type two interrupt characters (\s-2RUBOUT\s0
+by default) in a row or use the
+.b ~q
+escape.
+The \fBmail\fP command can be abbreviated to \fBm\fP.
+.ip "\fBmbox\fP\ \ "
+Indicate that a list of messages be sent to
+.i mbox
+in your home directory when you quit. This is the default
+action for messages if you do
+.i not
+have the
+.i hold
+option set.
+.ip "\fBnext\fP or \fB+\fP\ \ "
+The
+.b next
+command goes to the next message and types it. If given a message list,
+.b next
+goes to the first such message and types it. Thus,
+.(l
+next root
+.)l
+goes to the next message sent by
+.q root
+and types it. The
+.b next
+command can be abbreviated to simply a newline, which means that one
+can go to and type a message by simply giving its message number or
+one of the magic characters
+.q "^"
+.q "."
+or
+.q "$".
+Thus,
+.(l
+\&.
+.)l
+prints the current message and
+.(l
+4
+.)l
+prints message 4, as described previously.
+\fBNext\fP can be abbreviated to \fBn\fP.
+.ip "\fBpreserve\fP\ \ "
+Same as
+.b hold .
+Cause a list of messages to be held in your system mailbox when you quit.
+\fBPreserve\fP can be abbreviated to \fBpre\fP.
+.ip "\fBprint\fP\ \ "
+Print the specified messages. If the
+.b crt
+variable is set, messages longer than the number of lines it indicates
+are paged through the command specified by the \fBPAGER\fP variable.
+The \fBprint\fP command can be abbreviated to \fBp\fP.
+.ip "\fBquit\fP\ \ "
+Terminates the session, saving all undeleted, unsaved and unwritten messages
+in the user's \fImbox\fP file in their login directory
+(messages marked as having been read), preserving all
+messages marked with \fBhold\fP or \fBpreserve\fP or never referenced
+in their system mailbox.
+Any messages that were deleted, saved, written or saved to \fImbox\fP are
+removed from their system mailbox.
+If new mail has arrived during the session, the message
+``You have new mail'' is given. If given while editing a mailbox file
+with the \fB\-f\fP flag, then the edit file is rewritten.
+A return to the Shell is effected, unless the rewrite of edit file fails,
+in which case the user can escape with the \fBexit\fP command.
+\fBQuit\fP can be abbreviated to \fBq\fP.
+.ip "\fBreply\fP or \fBrespond\fP\ \ "
+Frame a reply to a single message.
+The reply will be sent to the
+person who sent you the message (to which you are replying), plus all
+the people who received the original message, except you. You can
+add people using the \fB~t\fP, \fB~c\fP and \fB~b\fP
+tilde escapes. The subject in your reply is formed by prefacing the
+subject in the original message with
+.q "Re:"
+unless it already began thus.
+If the original message included a
+.q "reply-to"
+header field, the reply will go
+.i only
+to the recipient named by
+.q "reply-to."
+You type in your message using the same conventions available to you
+through the
+.b mail
+command.
+The \fBreply\fP (and \fBrespond\fP) command can be abbreviated to \fBr\fP.
+.ip "\fBretain\fP\ \ "
+Add the list of header fields named to the \fIretained list\fP.
+Only the header fields in the retain list
+are shown on your terminal when you print a message.
+All other header fields are suppressed.
+The
+.b Type
+and
+.b Print
+commands can be used to print a message in its entirety.
+If
+.b retain
+is executed with no arguments, it lists the current set of
+retained fields.
+.ip "\fBsave\fP\ \ "
+It is often useful to be able to save messages on related topics
+in a file. The
+.b save
+command gives you the ability to do this. The
+.b save
+command takes as an argument a list of message numbers, followed by
+the name of the file in which to save the messages. The messages
+are appended to the named file, thus allowing one to keep several
+messages in the file, stored in the order they were put there.
+The filename in quotes, followed by the line
+count and character count is echoed on the user's terminal.
+An example of the
+.b save
+command relative to our running example is:
+.(l
+s 1 2 tuitionmail
+.)l
+.b Saved
+messages are not automatically saved in
+.i mbox
+at quit time, nor are they selected by the
+.b next
+command described above, unless explicitly specified.
+\fBSave\fP can be abbreviated to \fBs\fP.
+.ip "\fBset\fP\ \ "
+Set an option or give an option a value. Used to customize
+.i Mail .
+Section 5.3 contains a list of the options. Options can be
+.i binary ,
+in which case they are
+.i on
+or
+.i off ,
+or
+.i valued .
+To set a binary option
+.i option
+.i on ,
+do
+.(l
+set option
+.)l
+To give the valued option
+.i option
+the value
+.i value ,
+do
+.(l
+set option=value
+.)l
+There must be no space before or after the ``='' sign.
+If no arguments are given, all variable values are printed.
+Several options can be specified in a single
+.b set
+command.
+\fBSet\fP can be abbreviated to \fBse\fP.
+.ip "\fBshell\fP\ \ "
+The
+.b shell
+command allows you to
+escape to the shell.
+.b Shell
+invokes an interactive shell and allows you to type commands to it.
+When you leave the shell, you will return to
+.i Mail .
+The shell used is a default assumed by
+.i Mail ;
+you can override this default by setting the valued option
+.q SHELL,
+eg:
+.(l
+set SHELL=/bin/csh
+.)l
+\fBShell\fP can be abbreviated to \fBsh\fP.
+.ip "\fBsize\fP\ \ "
+Takes a message list and prints out the size in characters of each
+message.
+.ip "\fBsource\fP\ \ "
+The
+.b source
+command reads
+.i mail
+commands from a file. It is useful when you are trying to fix your
+.q .mailrc
+file and you need to re-read it.
+\fBSource\fP can be abbreviated to \fBso\fP.
+.ip "\fBtop\fP\ \ "
+The
+.b top
+command takes a message list and prints the first five lines
+of each addressed message.
+If you wish, you can change the number of lines that
+.b top
+prints out by setting the valued option
+.q "toplines."
+On a CRT terminal,
+.(l
+set toplines=10
+.)l
+might be preferred.
+\fBTop\fP can be abbreviated to \fBto\fP.
+.ip "\fBtype\fP\ \ "
+Same as \fBprint\fP.
+Takes a message list and types out each message on the terminal.
+The \fBtype\fP command can be abbreviated to \fBt\fP.
+.ip "\fBundelete\fP \ \"
+Takes a message list and marks each message as \fInot\fP
+being deleted.
+\fBUndelete\fP can be abbreviated to \fBu\fP.
+.ip "\fBunread\fP\ \ "
+Takes a message list and marks each message as
+.i not
+having been read.
+\fBUnread\fP can be abbreviated to \fBU\fP.
+.ip "\fBunset\fP\ \ "
+Takes a list of option names and discards their remembered values;
+the inverse of \fBset\fP .
+.ip "\fBvisual\fP\ \ "
+It is often useful to be able to invoke one of two editors,
+based on the type of terminal one is using. To invoke
+a display oriented editor, you can use the
+.b visual
+command. The operation of the
+.b visual
+command is otherwise identical to that of the
+.b edit
+command.
+.ne 2v+\n(psu
+.sp \n(psu
+Both the
+.b edit
+and
+.b visual
+commands assume some default text editors. These default editors
+can be overridden by the valued options
+.q EDITOR
+and
+.q VISUAL
+for the standard and screen editors. You might want to do:
+.(l
+set EDITOR=/usr/ucb/ex VISUAL=/usr/ucb/vi
+.)l
+\fBVisual\fP can be abbreviated to \fBv\fP.
+.ip "\fBwrite\fP\ \ "
+The
+.b save
+command always writes the entire message, including the headers,
+into the file. If you want to write just the message itself, you
+can use the
+.b write
+command. The
+.b write
+command has the same syntax as the
+.b save
+command, and can be abbreviated to simply
+.b w .
+Thus, we could write the second message by doing:
+.(l
+w 2 file.c
+.)l
+As suggested by this example, the
+.b write
+command is useful for such tasks as sending and receiving
+source program text over the message system.
+The filename in quotes, followed by the line
+count and character count is echoed on the user's terminal.
+.ip "\fBz\fP\ \ "
+.i Mail
+presents message headers in windowfuls as described under
+the
+.b headers
+command.
+You can move
+.i Mail's
+attention forward to the next window by giving the
+.(l
+z+
+.)l
+command. Analogously, you can move to the previous window with:
+.(l
+z\-
+.)l
+.sh 2 "Custom options"
+.pp
+Throughout this manual, we have seen examples of binary and valued options.
+This section describes each of the options in alphabetical order, including
+some that you have not seen yet.
+To avoid confusion, please note that the options are either
+all lower case letters or all upper case letters. When I start a sentence
+such as:
+.q "Ask"
+causes
+.i Mail
+to prompt you for a subject header,
+I am only capitalizing
+.q ask
+as a courtesy to English.
+.ip "\fBEDITOR\fP\ \ "
+The valued option
+.q EDITOR
+defines the pathname of the text editor to be used in the
+.b edit
+command and ~e. If not defined, a standard editor is used.
+.ip "\fBPAGER\fP\ \ "
+Pathname of the program to use for paginating output when
+it exceeds \fIcrt\fP lines.
+A default paginator is used if this option is not defined.
+.ip "\fBSHELL\fP\ \ "
+The valued option
+.q SHELL
+gives the path name of your shell. This shell is used for the
+.b !
+command and ~! escape. In addition, this shell expands
+file names with shell metacharacters like * and ? in them.
+.ip "\fBVISUAL\fP\ \ "
+The valued option
+.q VISUAL
+defines the pathname of the screen editor to be used in the
+.b visual
+command
+and ~v escape. A standard screen editor is used if you do not define one.
+.ip "\fBappend\fP\ \ "
+The
+.q append
+option is binary and
+causes messages saved in
+.i mbox
+to be appended to the end rather than prepended.
+Normally, \fIMail\fP will put messages in \fImbox\fP
+in the same order that the system puts messages in your system mailbox.
+By setting
+.q append,
+you are requesting that
+.i mbox
+be appended to regardless. It is in any event quicker to append.
+.ip "\fBask\fP\ \ "
+.q "Ask"
+is a binary option which
+causes
+.i Mail
+to prompt you for the subject of each message you send.
+If you respond with simply a newline, no subject field will be sent.
+.ip "\fBaskcc\fP\ \ "
+.q Askcc
+is a binary option which
+causes you to be prompted for additional carbon copy recipients at the
+end of each message. Responding with a newline shows your
+satisfaction with the current list.
+.ip "\fBautoprint\fP\ \ "
+.q Autoprint
+is a binary option which
+causes the
+.b delete
+command to behave like
+.b dp
+\*- thus, after deleting a message, the next one will be typed
+automatically. This is useful when quickly scanning and deleting
+messages in your mailbox.
+.ip "\fBcrt\fP \ \ "
+The valued option
+.I crt
+is used as a threshold to determine how long a message must
+be before
+.b PAGER
+is used to read it.
+.ip "\fBdebug\fP \ \ "
+The binary option
+.q debug
+causes debugging information to be displayed. Use of this
+option is the same as using the \fB\-d\fP command line flag.
+.ip "\fBdot\fP\ \ "
+.q Dot
+is a binary option which, if set, causes
+.i Mail
+to interpret a period alone on a line as the terminator
+of the message you are sending.
+.ip "\fBescape\fP\ \ "
+To allow you to change the escape character used when sending
+mail, you can set the valued option
+.q escape.
+Only the first character of the
+.q escape
+option is used, and it must be doubled if it is to appear as
+the first character of a line of your message. If you change your escape
+character, then ~ loses all its special meaning, and need no longer be doubled
+at the beginning of a line.
+.ip "\fBfolder\fP\ \ "
+The name of the directory to use for storing folders of messages.
+If this name begins with a `/'
+.i Mail
+considers it to be an absolute pathname; otherwise, the folder directory
+is found relative to your home directory.
+.ip "\fBhold\fP\ \ "
+The binary option
+.q hold
+causes messages that have been read but not manually dealt with
+to be held in the system mailbox. This prevents such messages from
+being automatically swept into your \fImbox\fP file.
+.ip "\fBignore\fP\ \ "
+The binary option
+.q ignore
+causes \s-2RUBOUT\s0 characters from your terminal to be ignored and echoed
+as @'s while you are sending mail. \s-2RUBOUT\s0 characters retain their
+original meaning in
+.i Mail
+command mode.
+Setting the
+.q ignore
+option is equivalent to supplying the
+.b \-i
+flag on the command line as described in section 6.
+.ip "\fBignoreeof\fP\ \ "
+An option related to
+.q dot
+is
+.q ignoreeof
+which makes
+.i Mail
+refuse to accept a control\-d as the end of a message.
+.q Ignoreeof
+also applies to
+.i Mail
+command mode.
+.ip "\fBkeep\fP\ \ "
+The
+.q keep
+option causes
+.i Mail
+to truncate your system mailbox instead of deleting it when it
+is empty. This is useful if you elect to protect your mailbox, which
+you would do with the shell command:
+.(l
+chmod 600 /usr/spool/mail/yourname
+.)l
+where
+.i yourname
+is your login name. If you do not do this, anyone can probably read
+your mail, although people usually don't.
+.ip "\fBkeepsave\fP\ \ "
+When you
+.b save
+a message,
+.i Mail
+usually discards it when you
+.b quit .
+To retain all saved messages, set the
+.q keepsave
+option.
+.ip "\fBmetoo\fP\ \ "
+When sending mail to an alias,
+.i Mail
+makes sure that if you are included in the alias, that mail will not
+be sent to you. This is useful if a single alias is being used by
+all members of the group. If however, you wish to receive a copy of
+all the messages you send to the alias, you can set the binary option
+.q metoo.
+.ip "\fBnoheader\fP\ \ "
+The binary option
+.q noheader
+suppresses the printing of the version and headers when
+.i Mail
+is first invoked. Setting this option is the same as using
+.b \-N
+on the command line.
+.ip "\fBnosave\fP\ \ "
+Normally,
+when you abort a message with two \s-2RUBOUTs\s0,
+.i Mail
+copies the partial letter to the file
+.q dead.letter
+in your home directory. Setting the binary option
+.q nosave
+prevents this.
+.ip "\fBReplyall\fP\ \ "
+Reverses the sense of
+.i reply
+and
+.i Reply
+commands.
+.ip "\fBquiet\fP\ \ "
+The binary option
+.q quiet
+suppresses the printing of the version when
+.i Mail
+is first invoked,
+as well as printing the for example
+.q "Message 4:"
+from the
+.b type
+command.
+.ip "\fBrecord\fP\ \ "
+If you love to keep records, then the
+valued option
+.q record
+can be set to the name of a file to save your outgoing mail.
+Each new message you send is appended to the end of the file.
+.ip "\fBscreen\fP\ \ "
+When
+.i Mail
+initially prints the message headers, it determines the number to
+print by looking at the speed of your terminal. The faster your
+terminal, the more it prints.
+The valued option
+.q screen
+overrides this calculation and
+specifies how many message headers you want printed.
+This number is also used for scrolling with the
+.b z
+command.
+.ip "\fBsendmail\fP\ \ "
+To use an alternate mail delivery system, set the
+.q sendmail
+option to the full pathname of the program to use. Note: this is not
+for everyone! Most people should use the default delivery system.
+.ip "\fBtoplines\fP\ \ "
+The valued option
+.q toplines
+defines the number of lines that the
+.q top
+command will print out instead of the default five lines.
+.ip "\fBverbose\fP\ \ "
+The binary option "verbose" causes
+.i Mail
+to invoke sendmail with the
+.b \-v
+flag, which causes it to go into verbose mode and announce expansion
+of aliases, etc. Setting the "verbose" option is equivalent to
+invoking
+.i Mail
+with the
+.b \-v
+flag as described in section 6.
diff --git a/usr.bin/mail/USD.doc/mail6.nr b/usr.bin/mail/USD.doc/mail6.nr
new file mode 100644
index 0000000..e016234
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail6.nr
@@ -0,0 +1,125 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail6.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "Command line options"
+.pp
+This section describes command line options for
+.i Mail
+and what they are used for.
+.ip \-N
+Suppress the initial printing of headers.
+.ip \-d
+Turn on debugging information. Not of general interest.
+.ip "\-f file\ \ "
+Show the messages in
+.i file
+instead of your system mailbox. If
+.i file
+is omitted,
+.i Mail
+reads
+.i mbox
+in your home directory.
+.ip \-i
+Ignore tty interrupt signals. Useful on noisy phone lines, which
+generate spurious RUBOUT or DELETE characters. It's usually
+more effective to change your interrupt character to control\-c,
+for which see the
+.i stty
+shell command.
+.ip \-n
+Inhibit reading of /usr/lib/Mail.rc. Not generally useful, since
+/usr/lib/Mail.rc is usually empty.
+.ip "\-s string"
+Used for sending mail.
+.i String
+is used as the subject of the message being composed. If
+.i string
+contains blanks, you must surround it with quote marks.
+.ip "\-u name"
+Read
+.i names's
+mail instead of your own. Unwitting others often neglect to protect
+their mailboxes, but discretion is advised. Essentially,
+.b "\-u user"
+is a shorthand way of doing
+.b "\-f /usr/spool/mail/user".
+.ip "\-v"
+Use the
+.b \-v
+flag when invoking sendmail. This feature may also be enabled
+by setting the the option "verbose".
+.pp
+The following command line flags are also recognized, but are
+intended for use by programs invoking
+.i Mail
+and not for people.
+.ip "\-T file"
+Arrange to print on
+.i file
+the contents of the
+.i article-id
+fields of all messages that were either read or deleted.
+.b \-T
+is for the
+.i readnews
+program and should NOT be used for reading your mail.
+.ip "\-h number"
+Pass on hop count information.
+.i Mail
+will take the number, increment it, and pass it with
+.b \-h
+to the mail delivery system.
+.b \-h
+only has effect when sending mail and is used for network mail
+forwarding.
+.ip "\-r name"
+Used for network mail forwarding: interpret
+.i name
+as the sender of the message. The
+.i name
+and
+.b \-r
+are simply sent along to the mail delivery system. Also,
+.i Mail
+will wait for the message to be sent and return the exit status.
+Also restricts formatting of message.
+.pp
+Note that
+.b \-h
+and
+.b \-r ,
+which are for network mail forwarding, are not used in practice
+since mail forwarding is now handled separately. They may
+disappear soon.
diff --git a/usr.bin/mail/USD.doc/mail7.nr b/usr.bin/mail/USD.doc/mail7.nr
new file mode 100644
index 0000000..0b2590b
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail7.nr
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail7.nr 8.1 (Berkeley) 6/8/93
+.\"
+.sh 1 "Format of messages"
+.pp
+This section describes the format of messages.
+Messages begin with a
+.i from
+line, which consists of the word
+.q From
+followed by a user name, followed by anything, followed by
+a date in the format returned by the
+.i ctime
+library routine described in section 3 of the Unix Programmer's
+Manual. A possible
+.i ctime
+format date is:
+.(l
+Tue Dec 1 10:58:23 1981
+.)l
+The
+.i ctime
+date may be optionally followed by a single space and a
+time zone indication, which
+should be three capital letters, such as PDT.
+.pp
+Following the
+.i from
+line are zero or more
+.i "header field"
+lines.
+Each header field line is of the form:
+.(l
+name: information
+.)l
+.i Name
+can be anything, but only certain header fields are recognized as
+having any meaning. The recognized header fields are:
+.i article-id ,
+.i bcc ,
+.i cc ,
+.i from ,
+.i reply-to ,
+.i sender ,
+.i subject ,
+and
+.i to .
+Other header fields are also significant to other systems; see,
+for example, the current Arpanet message standard for much more
+information on this topic.
+A header field can be continued onto following lines by making the
+first character on the following line a space or tab character.
+.pp
+If any headers are present, they must be followed by a blank line.
+The part that follows is called the
+.i body
+of the message, and must be ASCII text, not containing null characters.
+Each line in the message body must be no longer than 512 characters and
+terminated with an ASCII newline character.
+If binary data must be passed through the mail system, it is suggested
+that this data be encoded in a system which encodes six bits into
+a printable character (i.e.: uuencode).
+For example, one could use the upper and lower case letters, the digits,
+and the characters comma and period to make up the 64 characters.
+Then, one can send a 16-bit binary number
+as three characters. These characters should be packed into lines,
+preferably lines about 70 characters long as long lines are transmitted
+more efficiently.
+.pp
+The message delivery system always adds a blank line to the end of
+each message. This blank line must not be deleted.
+.pp
+The UUCP message delivery system sometimes adds a blank line to
+the end of a message each time it is forwarded through a machine.
+.pp
+It should be noted that some network transport protocols enforce
+limits to the lengths of messages.
diff --git a/usr.bin/mail/USD.doc/mail8.nr b/usr.bin/mail/USD.doc/mail8.nr
new file mode 100644
index 0000000..e8e056b
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail8.nr
@@ -0,0 +1,75 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail8.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "Glossary"
+.pp
+This section contains the definitions of a few phrases
+peculiar to
+.i Mail .
+.ip "\fIalias\fP"
+An alternative name for a person or list of people.
+.ip "\fIflag\fP"
+An option, given on the command line of
+.i Mail ,
+prefaced with a \-. For example,
+.b \-f
+is a flag.
+.ip "\fIheader field\fP"
+At the beginning of a message, a line which contains information
+that is part of the structure of the message. Popular header fields
+include
+.i to ,
+.i cc ,
+and
+.i subject .
+.ip "\fImail\ \ \fP"
+A collection of messages. Often used in the phrase,
+.q "Have you read your mail?"
+.ip "\fImailbox\fP"
+The place where your mail is stored, typically in the directory
+/usr/spool/mail.
+.ip "\fImessage\fP"
+A single letter from someone, initially stored in your
+.i mailbox .
+.ip "\fImessage list\fP"
+A string used in
+.i Mail
+command mode to describe a sequence of messages.
+.ip "\fIoption\fP"
+A piece of special purpose information used to tailor
+.i Mail
+to your taste.
+Options are specified with the
+.b set
+command.
diff --git a/usr.bin/mail/USD.doc/mail9.nr b/usr.bin/mail/USD.doc/mail9.nr
new file mode 100644
index 0000000..99f7518
--- /dev/null
+++ b/usr.bin/mail/USD.doc/mail9.nr
@@ -0,0 +1,203 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail9.nr 8.1 (Berkeley) 6/8/93
+.\"
+.bp
+.sh 1 "Summary of commands, options, and escapes"
+.pp
+This section gives a quick summary of the
+.i Mail
+commands, binary and valued options, and tilde escapes.
+.pp
+The following table describes the commands:
+.TS
+center ;
+c ci
+lb l.
+Command Description
+_
++ Same as \fBnext\fP
+- Back up to previous message
+? Print brief summary of \fIMail\fP commands
+! Single command escape to shell
+Print Type message with ignored fields
+Reply Reply to author of message only
+Respond Same as \fBReply\fP
+Type Type message with ignored fields
+alias Define an alias as a set of user names
+alternates List other names you are known by
+chdir Change working directory, home by default
+copy Copy a message to a file or folder
+delete Delete a list of messages
+dp Same as \fBdt\fP
+dt Delete current message, type next message
+edit Edit a list of messages
+else Start of else part of conditional; see \fBif\fP
+endif End of conditional statement; see \fBif\fP
+exit Leave mail without changing anything
+file Interrogate/change current mail file
+folder Same as \fBfile\fP
+folders List the folders in your folder directory
+from List headers of a list of messages
+headers List current window of messages
+help Same as \fB?\fP
+hold Same as \fBpreserve\fP
+if Conditional execution of \fIMail\fP commands
+ignore Set/examine list of ignored header fields
+list List valid \fIMail\fP commands
+local List other names for the local host
+mail Send mail to specified names
+mbox Arrange to save a list of messages in \fImbox\fP
+next Go to next message and type it
+preserve Arrange to leave list of messages in system mailbox
+print Print messages
+quit Leave \fIMail\fP; update system mailbox, \fImbox\fP as appropriate
+reply Compose a reply to a message
+respond Same as \fBreply\fP
+retain Supersedes \fBignore\fP
+save Append messages, headers included, on a file
+set Set binary or valued options
+shell Invoke an interactive shell
+size Prints out size of message list
+source Read \fImail\fP commands from a file
+top Print first so many (5 by default) lines of list of messages
+type Same as \fBprint\fP
+undelete Undelete list of messages
+unread Marks list of messages as not been read
+unset Undo the operation of a \fBset\fP
+visual Invoke visual editor on a list of messages
+write Append messages to a file, don't include headers
+xit Same as \fBexit\fP
+z Scroll to next/previous screenful of headers
+.TE
+.bp
+.(b
+.pp
+The following table describes the options. Each option is
+shown as being either a binary or valued option.
+.TS
+center;
+c ci ci
+l ci l.
+Option Type Description
+_
+EDITOR valued Pathname of editor for ~e and \fBedit\fP
+PAGER valued Pathname of paginator for \fBPrint\fP, \fBprint\fP, \fBType\fP and \fBtype\fP
+SHELL valued Pathname of shell for \fBshell\fP, ~! and \fB!\fP
+VISUAL valued Pathname of screen editor for ~v, \fBvisual\fP
+append binary Always append messages to end of \fImbox\fP
+ask binary Prompt user for Subject: field when sending
+askcc binary Prompt user for additional Cc's at end of message
+autoprint binary Print next message after \fBdelete\fP
+crt valued Minimum number of lines before using \fBPAGER\fP
+debug binary Print out debugging information
+dot binary Accept . alone on line to terminate message input
+escape valued Escape character to be used instead of\ \ ~
+folder valued Directory to store folders in
+hold binary Hold messages in system mailbox by default
+ignore binary Ignore \s-2RUBOUT\s0 while sending mail
+ignoreeof binary Don't terminate letters/command input with \fB\(uaD\fP
+keep binary Don't unlink system mailbox when empty
+keepsave binary Don't delete \fBsave\fPd messages by default
+metoo binary Include sending user in aliases
+noheader binary Suppress initial printing of version and headers
+nosave binary Don't save partial letter in \fIdead.letter\fP
+quiet binary Suppress printing of \fIMail\fP version and message numbers
+record valued File to save all outgoing mail in
+screen valued Size of window of message headers for \fBz\fP, etc.
+sendmail valued Choose alternate mail delivery system
+toplines valued Number of lines to print in \fBtop\fP
+verbose binary Invoke sendmail with the \fB\-v\fP flag
+.TE
+.)b
+.(b
+.pp
+The following table summarizes the tilde escapes available
+while sending mail.
+.TS
+center;
+c ci ci
+l li l.
+Escape Arguments Description
+_
+~! command Execute shell command
+~b name ... Add names to "blind" Cc: list
+~c name ... Add names to Cc: field
+~d Read \fIdead.letter\fP into message
+~e Invoke text editor on partial message
+~f messages Read named messages
+~h Edit the header fields
+~m messages Read named messages, right shift by tab
+~p Print message entered so far
+~q Abort entry of letter; like \s-2RUBOUT\s0
+~r filename Read file into message
+~s string Set Subject: field to \fIstring\fP
+~t name ... Add names to To: field
+~v Invoke screen editor on message
+~w filename Write message on file
+~| command Pipe message through \fIcommand\fP
+~: Mail command Execute a \fIMail\fP command
+~~ string Quote a ~ in front of \fIstring\fP
+.TE
+.)b
+.(b
+.pp
+The following table shows the command line flags that
+.i Mail
+accepts:
+.TS
+center;
+c c
+l a.
+Flag Description
+_
+\-N Suppress the initial printing of headers
+\-T \fIfile\fP Article-id's of read/deleted messages to \fIfile\fP
+\-d Turn on debugging
+\-f \fIfile\fP Show messages in \fIfile\fP or \fI~/mbox\fP
+\-h \fInumber\fP Pass on hop count for mail forwarding
+\-i Ignore tty interrupt signals
+\-n Inhibit reading of /usr/lib/Mail.rc
+\-r \fIname\fP Pass on \fIname\fP for mail forwarding
+\-s \fIstring\fP Use \fIstring\fP as subject in outgoing mail
+\-u \fIname\fP Read \fIname's\fP mail instead of your own
+\-v Invoke sendmail with the \fB\-v\fP flag
+.TE
+.)b
+.lp
+Notes:
+.b \-T ,
+.b \-d ,
+.b \-h ,
+and
+.b \-r
+are not for human use.
diff --git a/usr.bin/mail/USD.doc/maila.nr b/usr.bin/mail/USD.doc/maila.nr
new file mode 100644
index 0000000..84b01fe
--- /dev/null
+++ b/usr.bin/mail/USD.doc/maila.nr
@@ -0,0 +1,33 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)maila.nr 8.1 (Berkeley) 6/8/93
+.\"
diff --git a/usr.bin/mail/aux.c b/usr.bin/mail/aux.c
new file mode 100644
index 0000000..31038c8
--- /dev/null
+++ b/usr.bin/mail/aux.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)aux.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Auxiliary functions.
+ */
+
+/*
+ * Return a pointer to a dynamic copy of the argument.
+ */
+char *
+savestr(str)
+ char *str;
+{
+ char *new;
+ int size = strlen(str) + 1;
+
+ if ((new = salloc(size)) != NOSTR)
+ bcopy(str, new, size);
+ return new;
+}
+
+/*
+ * Make a copy of new argument incorporating old one.
+ */
+char *
+save2str(str, old)
+ char *str, *old;
+{
+ char *new;
+ int newsize = strlen(str) + 1;
+ int oldsize = old ? strlen(old) + 1 : 0;
+
+ if ((new = salloc(newsize + oldsize)) != NOSTR) {
+ if (oldsize) {
+ bcopy(old, new, oldsize);
+ new[oldsize - 1] = ' ';
+ }
+ bcopy(str, new + oldsize, newsize);
+ }
+ return new;
+}
+
+/*
+ * Announce a fatal error and die.
+ */
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+panic(const char *fmt, ...)
+#else
+panic(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "panic: ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ fflush(stderr);
+ abort();
+}
+
+/*
+ * Touch the named message by setting its MTOUCH flag.
+ * Touched messages have the effect of not being sent
+ * back to the system mailbox on exit.
+ */
+void
+touch(mp)
+ register struct message *mp;
+{
+
+ mp->m_flag |= MTOUCH;
+ if ((mp->m_flag & MREAD) == 0)
+ mp->m_flag |= MREAD|MSTATUS;
+}
+
+/*
+ * Test to see if the passed file name is a directory.
+ * Return true if it is.
+ */
+int
+isdir(name)
+ char name[];
+{
+ struct stat sbuf;
+
+ if (stat(name, &sbuf) < 0)
+ return(0);
+ return((sbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+
+/*
+ * Count the number of arguments in the given string raw list.
+ */
+int
+argcount(argv)
+ char **argv;
+{
+ register char **ap;
+
+ for (ap = argv; *ap++ != NOSTR;)
+ ;
+ return ap - argv - 1;
+}
+
+/*
+ * Return the desired header line from the passed message
+ * pointer (or NOSTR if the desired header field is not available).
+ */
+char *
+hfield(field, mp)
+ char field[];
+ struct message *mp;
+{
+ register FILE *ibuf;
+ char linebuf[LINESIZE];
+ register int lc;
+ register char *hfield;
+ char *colon, *oldhfield = NOSTR;
+
+ ibuf = setinput(mp);
+ if ((lc = mp->m_lines - 1) < 0)
+ return NOSTR;
+ if (readline(ibuf, linebuf, LINESIZE) < 0)
+ return NOSTR;
+ while (lc > 0) {
+ if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
+ return oldhfield;
+ if (hfield = ishfield(linebuf, colon, field))
+ oldhfield = save2str(hfield, oldhfield);
+ }
+ return oldhfield;
+}
+
+/*
+ * Return the next header field found in the given message.
+ * Return >= 0 if something found, < 0 elsewise.
+ * "colon" is set to point to the colon in the header.
+ * Must deal with \ continuations & other such fraud.
+ */
+int
+gethfield(f, linebuf, rem, colon)
+ register FILE *f;
+ char linebuf[];
+ register int rem;
+ char **colon;
+{
+ char line2[LINESIZE];
+ register char *cp, *cp2;
+ register int c;
+
+ for (;;) {
+ if (--rem < 0)
+ return -1;
+ if ((c = readline(f, linebuf, LINESIZE)) <= 0)
+ return -1;
+ for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
+ cp++)
+ ;
+ if (*cp != ':' || cp == linebuf)
+ continue;
+ /*
+ * I guess we got a headline.
+ * Handle wraparounding
+ */
+ *colon = cp;
+ cp = linebuf + c;
+ for (;;) {
+ while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
+ ;
+ cp++;
+ if (rem <= 0)
+ break;
+ ungetc(c = getc(f), f);
+ if (c != ' ' && c != '\t')
+ break;
+ if ((c = readline(f, line2, LINESIZE)) < 0)
+ break;
+ rem--;
+ for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
+ ;
+ c -= cp2 - line2;
+ if (cp + c >= linebuf + LINESIZE - 2)
+ break;
+ *cp++ = ' ';
+ bcopy(cp2, cp, c);
+ cp += c;
+ }
+ *cp = 0;
+ return rem;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Check whether the passed line is a header line of
+ * the desired breed. Return the field body, or 0.
+ */
+
+char*
+ishfield(linebuf, colon, field)
+ char linebuf[], field[];
+ char *colon;
+{
+ register char *cp = colon;
+
+ *cp = 0;
+ if (strcasecmp(linebuf, field) != 0) {
+ *cp = ':';
+ return 0;
+ }
+ *cp = ':';
+ for (cp++; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ return cp;
+}
+
+/*
+ * Copy a string, lowercasing it as we go.
+ */
+void
+istrcpy(dest, src)
+ register char *dest, *src;
+{
+
+ do {
+ if (isupper(*src))
+ *dest++ = tolower(*src);
+ else
+ *dest++ = *src;
+ } while (*src++ != 0);
+}
+
+/*
+ * The following code deals with input stacking to do source
+ * commands. All but the current file pointer are saved on
+ * the stack.
+ */
+
+static int ssp; /* Top of file stack */
+struct sstack {
+ FILE *s_file; /* File we were in. */
+ int s_cond; /* Saved state of conditionals */
+ int s_loading; /* Loading .mailrc, etc. */
+} sstack[NOFILE];
+
+/*
+ * Pushdown current input file and switch to a new one.
+ * Set the global flag "sourcing" so that others will realize
+ * that they are no longer reading from a tty (in all probability).
+ */
+int
+source(arglist)
+ char **arglist;
+{
+ FILE *fi;
+ char *cp;
+
+ if ((cp = expand(*arglist)) == NOSTR)
+ return(1);
+ if ((fi = Fopen(cp, "r")) == NULL) {
+ perror(cp);
+ return(1);
+ }
+ if (ssp >= NOFILE - 1) {
+ printf("Too much \"sourcing\" going on.\n");
+ Fclose(fi);
+ return(1);
+ }
+ sstack[ssp].s_file = input;
+ sstack[ssp].s_cond = cond;
+ sstack[ssp].s_loading = loading;
+ ssp++;
+ loading = 0;
+ cond = CANY;
+ input = fi;
+ sourcing++;
+ return(0);
+}
+
+/*
+ * Pop the current input back to the previous level.
+ * Update the "sourcing" flag as appropriate.
+ */
+int
+unstack()
+{
+ if (ssp <= 0) {
+ printf("\"Source\" stack over-pop.\n");
+ sourcing = 0;
+ return(1);
+ }
+ Fclose(input);
+ if (cond != CANY)
+ printf("Unmatched \"if\"\n");
+ ssp--;
+ cond = sstack[ssp].s_cond;
+ loading = sstack[ssp].s_loading;
+ input = sstack[ssp].s_file;
+ if (ssp == 0)
+ sourcing = loading;
+ return(0);
+}
+
+/*
+ * Touch the indicated file.
+ * This is nifty for the shell.
+ */
+void
+alter(name)
+ char *name;
+{
+ struct stat sb;
+ struct timeval tv[2];
+ time_t time();
+
+ if (stat(name, &sb))
+ return;
+ tv[0].tv_sec = time((time_t *)0) + 1;
+ tv[1].tv_sec = sb.st_mtime;
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+ (void)utimes(name, tv);
+}
+
+/*
+ * Examine the passed line buffer and
+ * return true if it is all blanks and tabs.
+ */
+int
+blankline(linebuf)
+ char linebuf[];
+{
+ register char *cp;
+
+ for (cp = linebuf; *cp; cp++)
+ if (*cp != ' ' && *cp != '\t')
+ return(0);
+ return(1);
+}
+
+/*
+ * Get sender's name from this message. If the message has
+ * a bunch of arpanet stuff in it, we may have to skin the name
+ * before returning it.
+ */
+char *
+nameof(mp, reptype)
+ register struct message *mp;
+ int reptype;
+{
+ register char *cp, *cp2;
+
+ cp = skin(name1(mp, reptype));
+ if (reptype != 0 || charcount(cp, '!') < 2)
+ return(cp);
+ cp2 = rindex(cp, '!');
+ cp2--;
+ while (cp2 > cp && *cp2 != '!')
+ cp2--;
+ if (*cp2 == '!')
+ return(cp2 + 1);
+ return(cp);
+}
+
+/*
+ * Start of a "comment".
+ * Ignore it.
+ */
+char *
+skip_comment(cp)
+ register char *cp;
+{
+ register nesting = 1;
+
+ for (; nesting > 0 && *cp; cp++) {
+ switch (*cp) {
+ case '\\':
+ if (cp[1])
+ cp++;
+ break;
+ case '(':
+ nesting++;
+ break;
+ case ')':
+ nesting--;
+ break;
+ }
+ }
+ return cp;
+}
+
+/*
+ * Skin an arpa net address according to the RFC 822 interpretation
+ * of "host-phrase."
+ */
+char *
+skin(name)
+ char *name;
+{
+ register int c;
+ register char *cp, *cp2;
+ char *bufend;
+ int gotlt, lastsp;
+ char nbuf[BUFSIZ];
+
+ if (name == NOSTR)
+ return(NOSTR);
+ if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
+ && index(name, ' ') == NOSTR)
+ return(name);
+ gotlt = 0;
+ lastsp = 0;
+ bufend = nbuf;
+ for (cp = name, cp2 = bufend; c = *cp++; ) {
+ switch (c) {
+ case '(':
+ cp = skip_comment(cp);
+ lastsp = 0;
+ break;
+
+ case '"':
+ /*
+ * Start of a "quoted-string".
+ * Copy it in its entirety.
+ */
+ while (c = *cp) {
+ cp++;
+ if (c == '"')
+ break;
+ if (c != '\\')
+ *cp2++ = c;
+ else if (c = *cp) {
+ *cp2++ = c;
+ cp++;
+ }
+ }
+ lastsp = 0;
+ break;
+
+ case ' ':
+ if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
+ cp += 3, *cp2++ = '@';
+ else
+ if (cp[0] == '@' && cp[1] == ' ')
+ cp += 2, *cp2++ = '@';
+ else
+ lastsp = 1;
+ break;
+
+ case '<':
+ cp2 = bufend;
+ gotlt++;
+ lastsp = 0;
+ break;
+
+ case '>':
+ if (gotlt) {
+ gotlt = 0;
+ while ((c = *cp) && c != ',') {
+ cp++;
+ if (c == '(')
+ cp = skip_comment(cp);
+ else if (c == '"')
+ while (c = *cp) {
+ cp++;
+ if (c == '"')
+ break;
+ if (c == '\\' && *cp)
+ cp++;
+ }
+ }
+ lastsp = 0;
+ break;
+ }
+ /* Fall into . . . */
+
+ default:
+ if (lastsp) {
+ lastsp = 0;
+ *cp2++ = ' ';
+ }
+ *cp2++ = c;
+ if (c == ',' && !gotlt) {
+ *cp2++ = ' ';
+ for (; *cp == ' '; cp++)
+ ;
+ lastsp = 0;
+ bufend = cp2;
+ }
+ }
+ }
+ *cp2 = 0;
+
+ return(savestr(nbuf));
+}
+
+/*
+ * Fetch the sender's name from the passed message.
+ * Reptype can be
+ * 0 -- get sender's name for display purposes
+ * 1 -- get sender's name for reply
+ * 2 -- get sender's name for Reply
+ */
+char *
+name1(mp, reptype)
+ register struct message *mp;
+ int reptype;
+{
+ char namebuf[LINESIZE];
+ char linebuf[LINESIZE];
+ register char *cp, *cp2;
+ register FILE *ibuf;
+ int first = 1;
+
+ if ((cp = hfield("from", mp)) != NOSTR)
+ return cp;
+ if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
+ return cp;
+ ibuf = setinput(mp);
+ namebuf[0] = 0;
+ if (readline(ibuf, linebuf, LINESIZE) < 0)
+ return(savestr(namebuf));
+newname:
+ for (cp = linebuf; *cp && *cp != ' '; cp++)
+ ;
+ for (; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ for (cp2 = &namebuf[strlen(namebuf)];
+ *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+ if (readline(ibuf, linebuf, LINESIZE) < 0)
+ return(savestr(namebuf));
+ if ((cp = index(linebuf, 'F')) == NULL)
+ return(savestr(namebuf));
+ if (strncmp(cp, "From", 4) != 0)
+ return(savestr(namebuf));
+ while ((cp = index(cp, 'r')) != NULL) {
+ if (strncmp(cp, "remote", 6) == 0) {
+ if ((cp = index(cp, 'f')) == NULL)
+ break;
+ if (strncmp(cp, "from", 4) != 0)
+ break;
+ if ((cp = index(cp, ' ')) == NULL)
+ break;
+ cp++;
+ if (first) {
+ strcpy(namebuf, cp);
+ first = 0;
+ } else
+ strcpy(rindex(namebuf, '!')+1, cp);
+ strcat(namebuf, "!");
+ goto newname;
+ }
+ cp++;
+ }
+ return(savestr(namebuf));
+}
+
+/*
+ * Count the occurances of c in str
+ */
+int
+charcount(str, c)
+ char *str;
+ int c;
+{
+ register char *cp;
+ register int i;
+
+ for (i = 0, cp = str; *cp; cp++)
+ if (*cp == c)
+ i++;
+ return(i);
+}
+
+/*
+ * Are any of the characters in the two strings the same?
+ */
+int
+anyof(s1, s2)
+ register char *s1, *s2;
+{
+
+ while (*s1)
+ if (index(s2, *s1++))
+ return 1;
+ return 0;
+}
+
+/*
+ * Convert c to upper case
+ */
+int
+raise(c)
+ register int c;
+{
+
+ if (islower(c))
+ return toupper(c);
+ return c;
+}
+
+/*
+ * Copy s1 to s2, return pointer to null in s2.
+ */
+char *
+copy(s1, s2)
+ register char *s1, *s2;
+{
+
+ while (*s2++ = *s1++)
+ ;
+ return s2 - 1;
+}
+
+/*
+ * See if the given header field is supposed to be ignored.
+ */
+int
+isign(field, ignore)
+ char *field;
+ struct ignoretab ignore[2];
+{
+ char realfld[BUFSIZ];
+
+ if (ignore == ignoreall)
+ return 1;
+ /*
+ * Lower-case the string, so that "Status" and "status"
+ * will hash to the same place.
+ */
+ istrcpy(realfld, field);
+ if (ignore[1].i_count > 0)
+ return (!member(realfld, ignore + 1));
+ else
+ return (member(realfld, ignore));
+}
+
+int
+member(realfield, table)
+ register char *realfield;
+ struct ignoretab *table;
+{
+ register struct ignore *igp;
+
+ for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
+ if (*igp->i_field == *realfield &&
+ equal(igp->i_field, realfield))
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/mail/cmd1.c b/usr.bin/mail/cmd1.c
new file mode 100644
index 0000000..e83a036
--- /dev/null
+++ b/usr.bin/mail/cmd1.c
@@ -0,0 +1,451 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd1.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * User commands.
+ */
+
+/*
+ * Print the current active headings.
+ * Don't change dot if invoker didn't give an argument.
+ */
+
+static int screen;
+
+int
+headers(msgvec)
+ int *msgvec;
+{
+ register int n, mesg, flag;
+ register struct message *mp;
+ int size;
+
+ size = screensize();
+ n = msgvec[0];
+ if (n != 0)
+ screen = (n-1)/size;
+ if (screen < 0)
+ screen = 0;
+ mp = &message[screen * size];
+ if (mp >= &message[msgCount])
+ mp = &message[msgCount - size];
+ if (mp < &message[0])
+ mp = &message[0];
+ flag = 0;
+ mesg = mp - &message[0];
+ if (dot != &message[n-1])
+ dot = mp;
+ for (; mp < &message[msgCount]; mp++) {
+ mesg++;
+ if (mp->m_flag & MDELETED)
+ continue;
+ if (flag++ >= size)
+ break;
+ printhead(mesg);
+ }
+ if (flag == 0) {
+ printf("No more mail.\n");
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Scroll to the next/previous screen
+ */
+int
+scroll(arg)
+ char arg[];
+{
+ register int s, size;
+ int cur[1];
+
+ cur[0] = 0;
+ size = screensize();
+ s = screen;
+ switch (*arg) {
+ case 0:
+ case '+':
+ s++;
+ if (s * size > msgCount) {
+ printf("On last screenful of messages\n");
+ return(0);
+ }
+ screen = s;
+ break;
+
+ case '-':
+ if (--s < 0) {
+ printf("On first screenful of messages\n");
+ return(0);
+ }
+ screen = s;
+ break;
+
+ default:
+ printf("Unrecognized scrolling command \"%s\"\n", arg);
+ return(1);
+ }
+ return(headers(cur));
+}
+
+/*
+ * Compute screen size.
+ */
+int
+screensize()
+{
+ int s;
+ char *cp;
+
+ if ((cp = value("screen")) != NOSTR && (s = atoi(cp)) > 0)
+ return s;
+ return screenheight - 4;
+}
+
+/*
+ * Print out the headlines for each message
+ * in the passed message list.
+ */
+int
+from(msgvec)
+ int *msgvec;
+{
+ register int *ip;
+
+ for (ip = msgvec; *ip != NULL; ip++)
+ printhead(*ip);
+ if (--ip >= msgvec)
+ dot = &message[*ip - 1];
+ return(0);
+}
+
+/*
+ * Print out the header of a specific message.
+ * This is a slight improvement to the standard one.
+ */
+void
+printhead(mesg)
+ int mesg;
+{
+ struct message *mp;
+ char headline[LINESIZE], wcount[LINESIZE], *subjline, dispc, curind;
+ char pbuf[BUFSIZ];
+ struct headline hl;
+ int subjlen;
+ char *name;
+
+ mp = &message[mesg-1];
+ (void) readline(setinput(mp), headline, LINESIZE);
+ if ((subjline = hfield("subject", mp)) == NOSTR)
+ subjline = hfield("subj", mp);
+ /*
+ * Bletch!
+ */
+ curind = dot == mp ? '>' : ' ';
+ dispc = ' ';
+ if (mp->m_flag & MSAVED)
+ dispc = '*';
+ if (mp->m_flag & MPRESERVE)
+ dispc = 'P';
+ if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
+ dispc = 'N';
+ if ((mp->m_flag & (MREAD|MNEW)) == 0)
+ dispc = 'U';
+ if (mp->m_flag & MBOX)
+ dispc = 'M';
+ parse(headline, &hl, pbuf);
+ sprintf(wcount, "%3d/%-5ld", mp->m_lines, mp->m_size);
+ subjlen = screenwidth - 50 - strlen(wcount);
+ name = value("show-rcpt") != NOSTR ?
+ skin(hfield("to", mp)) : nameof(mp, 0);
+ if (subjline == NOSTR || subjlen < 0) /* pretty pathetic */
+ printf("%c%c%3d %-20.20s %16.16s %s\n",
+ curind, dispc, mesg, name, hl.l_date, wcount);
+ else
+ printf("%c%c%3d %-20.20s %16.16s %s \"%.*s\"\n",
+ curind, dispc, mesg, name, hl.l_date, wcount,
+ subjlen, subjline);
+}
+
+/*
+ * Print out the value of dot.
+ */
+int
+pdot()
+{
+ printf("%d\n", dot - &message[0] + 1);
+ return(0);
+}
+
+/*
+ * Print out all the possible commands.
+ */
+int
+pcmdlist()
+{
+ register struct cmd *cp;
+ register int cc;
+ extern struct cmd cmdtab[];
+
+ printf("Commands are:\n");
+ for (cc = 0, cp = cmdtab; cp->c_name != NULL; cp++) {
+ cc += strlen(cp->c_name) + 2;
+ if (cc > 72) {
+ printf("\n");
+ cc = strlen(cp->c_name) + 2;
+ }
+ if ((cp+1)->c_name != NOSTR)
+ printf("%s, ", cp->c_name);
+ else
+ printf("%s\n", cp->c_name);
+ }
+ return(0);
+}
+
+/*
+ * Paginate messages, honor ignored fields.
+ */
+int
+more(msgvec)
+ int *msgvec;
+{
+ return (type1(msgvec, 1, 1));
+}
+
+/*
+ * Paginate messages, even printing ignored fields.
+ */
+int
+More(msgvec)
+ int *msgvec;
+{
+
+ return (type1(msgvec, 0, 1));
+}
+
+/*
+ * Type out messages, honor ignored fields.
+ */
+int
+type(msgvec)
+ int *msgvec;
+{
+
+ return(type1(msgvec, 1, 0));
+}
+
+/*
+ * Type out messages, even printing ignored fields.
+ */
+int
+Type(msgvec)
+ int *msgvec;
+{
+
+ return(type1(msgvec, 0, 0));
+}
+
+/*
+ * Type out the messages requested.
+ */
+jmp_buf pipestop;
+int
+type1(msgvec, doign, page)
+ int *msgvec;
+ int doign, page;
+{
+ register *ip;
+ register struct message *mp;
+ register char *cp;
+ int nlines;
+ FILE *obuf;
+
+ obuf = stdout;
+ if (setjmp(pipestop))
+ goto close_pipe;
+ if (value("interactive") != NOSTR &&
+ (page || (cp = value("crt")) != NOSTR)) {
+ nlines = 0;
+ if (!page) {
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
+ nlines += message[*ip - 1].m_lines;
+ }
+ if (page || nlines > (*cp ? atoi(cp) : realscreenheight)) {
+ cp = value("PAGER");
+ if (cp == NULL || *cp == '\0')
+ cp = _PATH_MORE;
+ obuf = Popen(cp, "w");
+ if (obuf == NULL) {
+ perror(cp);
+ obuf = stdout;
+ } else
+ signal(SIGPIPE, brokpipe);
+ }
+ }
+ for (ip = msgvec; *ip && ip - msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ if (value("quiet") == NOSTR)
+ fprintf(obuf, "Message %d:\n", *ip);
+ (void) send(mp, obuf, doign ? ignore : 0, NOSTR);
+ }
+close_pipe:
+ if (obuf != stdout) {
+ /*
+ * Ignore SIGPIPE so it can't cause a duplicate close.
+ */
+ signal(SIGPIPE, SIG_IGN);
+ Pclose(obuf);
+ signal(SIGPIPE, SIG_DFL);
+ }
+ return(0);
+}
+
+/*
+ * Respond to a broken pipe signal --
+ * probably caused by quitting more.
+ */
+void
+brokpipe(signo)
+ int signo;
+{
+ longjmp(pipestop, 1);
+}
+
+/*
+ * Print the top so many lines of each desired message.
+ * The number of lines is taken from the variable "toplines"
+ * and defaults to 5.
+ */
+int
+top(msgvec)
+ int *msgvec;
+{
+ register int *ip;
+ register struct message *mp;
+ int c, topl, lines, lineb;
+ char *valtop, linebuf[LINESIZE];
+ FILE *ibuf;
+
+ topl = 5;
+ valtop = value("toplines");
+ if (valtop != NOSTR) {
+ topl = atoi(valtop);
+ if (topl < 0 || topl > 10000)
+ topl = 5;
+ }
+ lineb = 1;
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ if (value("quiet") == NOSTR)
+ printf("Message %d:\n", *ip);
+ ibuf = setinput(mp);
+ c = mp->m_lines;
+ if (!lineb)
+ printf("\n");
+ for (lines = 0; lines < c && lines <= topl; lines++) {
+ if (readline(ibuf, linebuf, LINESIZE) < 0)
+ break;
+ puts(linebuf);
+ lineb = blankline(linebuf);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Touch all the given messages so that they will
+ * get mboxed.
+ */
+int
+stouch(msgvec)
+ int msgvec[];
+{
+ register int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag |= MTOUCH;
+ dot->m_flag &= ~MPRESERVE;
+ }
+ return(0);
+}
+
+/*
+ * Make sure all passed messages get mboxed.
+ */
+int
+mboxit(msgvec)
+ int msgvec[];
+{
+ register int *ip;
+
+ for (ip = msgvec; *ip != 0; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag |= MTOUCH|MBOX;
+ dot->m_flag &= ~MPRESERVE;
+ }
+ return(0);
+}
+
+/*
+ * List the folders the user currently has.
+ */
+int
+folders()
+{
+ char dirname[BUFSIZ];
+ char *cmd;
+
+ if (getfold(dirname) < 0) {
+ printf("No value set for \"folder\"\n");
+ return 1;
+ }
+ if ((cmd = value("LISTER")) == NOSTR)
+ cmd = "ls";
+ (void) run_command(cmd, 0, -1, -1, dirname, NOSTR, NOSTR);
+ return 0;
+}
diff --git a/usr.bin/mail/cmd2.c b/usr.bin/mail/cmd2.c
new file mode 100644
index 0000000..976f390
--- /dev/null
+++ b/usr.bin/mail/cmd2.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/wait.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * More user commands.
+ */
+
+/*
+ * If any arguments were given, go to the next applicable argument
+ * following dot, otherwise, go to the next applicable message.
+ * If given as first command with no arguments, print first message.
+ */
+int
+next(msgvec)
+ int *msgvec;
+{
+ register struct message *mp;
+ register int *ip, *ip2;
+ int list[2], mdot;
+
+ if (*msgvec != NULL) {
+
+ /*
+ * If some messages were supplied, find the
+ * first applicable one following dot using
+ * wrap around.
+ */
+
+ mdot = dot - &message[0] + 1;
+
+ /*
+ * Find the first message in the supplied
+ * message list which follows dot.
+ */
+
+ for (ip = msgvec; *ip != NULL; ip++)
+ if (*ip > mdot)
+ break;
+ if (*ip == NULL)
+ ip = msgvec;
+ ip2 = ip;
+ do {
+ mp = &message[*ip2 - 1];
+ if ((mp->m_flag & MDELETED) == 0) {
+ dot = mp;
+ goto hitit;
+ }
+ if (*ip2 != NULL)
+ ip2++;
+ if (*ip2 == NULL)
+ ip2 = msgvec;
+ } while (ip2 != ip);
+ printf("No messages applicable\n");
+ return(1);
+ }
+
+ /*
+ * If this is the first command, select message 1.
+ * Note that this must exist for us to get here at all.
+ */
+
+ if (!sawcom)
+ goto hitit;
+
+ /*
+ * Just find the next good message after dot, no
+ * wraparound.
+ */
+
+ for (mp = dot+1; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
+ break;
+ if (mp >= &message[msgCount]) {
+ printf("At EOF\n");
+ return(0);
+ }
+ dot = mp;
+hitit:
+ /*
+ * Print dot.
+ */
+
+ list[0] = dot - &message[0] + 1;
+ list[1] = NULL;
+ return(type(list));
+}
+
+/*
+ * Save a message in a file. Mark the message as saved
+ * so we can discard when the user quits.
+ */
+int
+save(str)
+ char str[];
+{
+
+ return save1(str, 1, "save", saveignore);
+}
+
+/*
+ * Copy a message to a file without affected its saved-ness
+ */
+int
+copycmd(str)
+ char str[];
+{
+
+ return save1(str, 0, "copy", saveignore);
+}
+
+/*
+ * Save/copy the indicated messages at the end of the passed file name.
+ * If mark is true, mark the message "saved."
+ */
+int
+save1(str, mark, cmd, ignore)
+ char str[];
+ int mark;
+ char *cmd;
+ struct ignoretab *ignore;
+{
+ register int *ip;
+ register struct message *mp;
+ char *file, *disp;
+ int f, *msgvec;
+ FILE *obuf;
+
+ msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
+ if ((file = snarf(str, &f)) == NOSTR)
+ return(1);
+ if (!f) {
+ *msgvec = first(0, MMNORM);
+ if (*msgvec == NULL) {
+ printf("No messages to %s.\n", cmd);
+ return(1);
+ }
+ msgvec[1] = NULL;
+ }
+ if (f && getmsglist(str, msgvec, 0) < 0)
+ return(1);
+ if ((file = expand(file)) == NOSTR)
+ return(1);
+ printf("\"%s\" ", file);
+ fflush(stdout);
+ if (access(file, 0) >= 0)
+ disp = "[Appended]";
+ else
+ disp = "[New file]";
+ if ((obuf = Fopen(file, "a")) == NULL) {
+ perror(NOSTR);
+ return(1);
+ }
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ if (send(mp, obuf, ignore, NOSTR) < 0) {
+ perror(file);
+ Fclose(obuf);
+ return(1);
+ }
+ if (mark)
+ mp->m_flag |= MSAVED;
+ }
+ fflush(obuf);
+ if (ferror(obuf))
+ perror(file);
+ Fclose(obuf);
+ printf("%s\n", disp);
+ return(0);
+}
+
+/*
+ * Write the indicated messages at the end of the passed
+ * file name, minus header and trailing blank line.
+ */
+int
+swrite(str)
+ char str[];
+{
+
+ return save1(str, 1, "write", ignoreall);
+}
+
+/*
+ * Snarf the file from the end of the command line and
+ * return a pointer to it. If there is no file attached,
+ * just return NOSTR. Put a null in front of the file
+ * name so that the message list processing won't see it,
+ * unless the file name is the only thing on the line, in
+ * which case, return 0 in the reference flag variable.
+ */
+
+char *
+snarf(linebuf, flag)
+ char linebuf[];
+ int *flag;
+{
+ register char *cp;
+
+ *flag = 1;
+ cp = strlen(linebuf) + linebuf - 1;
+
+ /*
+ * Strip away trailing blanks.
+ */
+
+ while (cp > linebuf && isspace(*cp))
+ cp--;
+ *++cp = 0;
+
+ /*
+ * Now search for the beginning of the file name.
+ */
+
+ while (cp > linebuf && !isspace(*cp))
+ cp--;
+ if (*cp == '\0') {
+ printf("No file specified.\n");
+ return(NOSTR);
+ }
+ if (isspace(*cp))
+ *cp++ = 0;
+ else
+ *flag = 0;
+ return(cp);
+}
+
+/*
+ * Delete messages.
+ */
+int
+delete(msgvec)
+ int msgvec[];
+{
+ delm(msgvec);
+ return 0;
+}
+
+/*
+ * Delete messages, then type the new dot.
+ */
+int
+deltype(msgvec)
+ int msgvec[];
+{
+ int list[2];
+ int lastdot;
+
+ lastdot = dot - &message[0] + 1;
+ if (delm(msgvec) >= 0) {
+ list[0] = dot - &message[0] + 1;
+ if (list[0] > lastdot) {
+ touch(dot);
+ list[1] = NULL;
+ return(type(list));
+ }
+ printf("At EOF\n");
+ } else
+ printf("No more messages\n");
+ return(0);
+}
+
+/*
+ * Delete the indicated messages.
+ * Set dot to some nice place afterwards.
+ * Internal interface.
+ */
+int
+delm(msgvec)
+ int *msgvec;
+{
+ register struct message *mp;
+ register *ip;
+ int last;
+
+ last = NULL;
+ for (ip = msgvec; *ip != NULL; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ mp->m_flag |= MDELETED|MTOUCH;
+ mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
+ last = *ip;
+ }
+ if (last != NULL) {
+ dot = &message[last-1];
+ last = first(0, MDELETED);
+ if (last != NULL) {
+ dot = &message[last-1];
+ return(0);
+ }
+ else {
+ dot = &message[0];
+ return(-1);
+ }
+ }
+
+ /*
+ * Following can't happen -- it keeps lint happy
+ */
+
+ return(-1);
+}
+
+/*
+ * Undelete the indicated messages.
+ */
+int
+undelete(msgvec)
+ int *msgvec;
+{
+ register struct message *mp;
+ register *ip;
+
+ for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
+ mp = &message[*ip - 1];
+ touch(mp);
+ dot = mp;
+ mp->m_flag &= ~MDELETED;
+ }
+ return 0;
+}
+
+/*
+ * Interactively dump core on "core"
+ */
+int
+core()
+{
+ int pid;
+ extern union wait wait_status;
+
+ switch (pid = vfork()) {
+ case -1:
+ perror("fork");
+ return(1);
+ case 0:
+ abort();
+ _exit(1);
+ }
+ printf("Okie dokie");
+ fflush(stdout);
+ wait_child(pid);
+ if (wait_status.w_coredump)
+ printf(" -- Core dumped.\n");
+ else
+ printf(" -- Can't dump core.\n");
+ return 0;
+}
+
+/*
+ * Clobber as many bytes of stack as the user requests.
+ */
+int
+clobber(argv)
+ char **argv;
+{
+ register int times;
+
+ if (argv[0] == 0)
+ times = 1;
+ else
+ times = (atoi(argv[0]) + 511) / 512;
+ clob1(times);
+ return 0;
+}
+
+/*
+ * Clobber the stack.
+ */
+void
+clob1(n)
+ int n;
+{
+ char buf[512];
+ register char *cp;
+
+ if (n <= 0)
+ return;
+ for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
+ ;
+ clob1(n - 1);
+}
+
+/*
+ * Add the given header fields to the retained list.
+ * If no arguments, print the current list of retained fields.
+ */
+int
+retfield(list)
+ char *list[];
+{
+
+ return ignore1(list, ignore + 1, "retained");
+}
+
+/*
+ * Add the given header fields to the ignored list.
+ * If no arguments, print the current list of ignored fields.
+ */
+int
+igfield(list)
+ char *list[];
+{
+
+ return ignore1(list, ignore, "ignored");
+}
+
+int
+saveretfield(list)
+ char *list[];
+{
+
+ return ignore1(list, saveignore + 1, "retained");
+}
+
+int
+saveigfield(list)
+ char *list[];
+{
+
+ return ignore1(list, saveignore, "ignored");
+}
+
+int
+ignore1(list, tab, which)
+ char *list[];
+ struct ignoretab *tab;
+ char *which;
+{
+ char field[BUFSIZ];
+ register int h;
+ register struct ignore *igp;
+ char **ap;
+
+ if (*list == NOSTR)
+ return igshow(tab, which);
+ for (ap = list; *ap != 0; ap++) {
+ istrcpy(field, *ap);
+ if (member(field, tab))
+ continue;
+ h = hash(field);
+ igp = (struct ignore *) calloc(1, sizeof (struct ignore));
+ igp->i_field = calloc((unsigned) strlen(field) + 1,
+ sizeof (char));
+ strcpy(igp->i_field, field);
+ igp->i_link = tab->i_head[h];
+ tab->i_head[h] = igp;
+ tab->i_count++;
+ }
+ return 0;
+}
+
+/*
+ * Print out all currently retained fields.
+ */
+int
+igshow(tab, which)
+ struct ignoretab *tab;
+ char *which;
+{
+ register int h;
+ struct ignore *igp;
+ char **ap, **ring;
+ int igcomp();
+
+ if (tab->i_count == 0) {
+ printf("No fields currently being %s.\n", which);
+ return 0;
+ }
+ ring = (char **) salloc((tab->i_count + 1) * sizeof (char *));
+ ap = ring;
+ for (h = 0; h < HSHSIZE; h++)
+ for (igp = tab->i_head[h]; igp != 0; igp = igp->i_link)
+ *ap++ = igp->i_field;
+ *ap = 0;
+ qsort(ring, tab->i_count, sizeof (char *), igcomp);
+ for (ap = ring; *ap != 0; ap++)
+ printf("%s\n", *ap);
+ return 0;
+}
+
+/*
+ * Compare two names for sorting ignored field list.
+ */
+int
+igcomp(l, r)
+ const void *l, *r;
+{
+ return (strcmp(*(char **)l, *(char **)r));
+}
diff --git a/usr.bin/mail/cmd3.c b/usr.bin/mail/cmd3.c
new file mode 100644
index 0000000..54a5d5e
--- /dev/null
+++ b/usr.bin/mail/cmd3.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd3.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Still more user commands.
+ */
+
+/*
+ * Process a shell escape by saving signals, ignoring signals,
+ * and forking a sh -c
+ */
+int
+shell(str)
+ char *str;
+{
+ sig_t sigint = signal(SIGINT, SIG_IGN);
+ char *shell;
+ char cmd[BUFSIZ];
+
+ (void) strcpy(cmd, str);
+ if (bangexp(cmd) < 0)
+ return 1;
+ if ((shell = value("SHELL")) == NOSTR)
+ shell = _PATH_CSHELL;
+ (void) run_command(shell, 0, -1, -1, "-c", cmd, NOSTR);
+ (void) signal(SIGINT, sigint);
+ printf("!\n");
+ return 0;
+}
+
+/*
+ * Fork an interactive shell.
+ */
+/*ARGSUSED*/
+int
+dosh(str)
+ char *str;
+{
+ sig_t sigint = signal(SIGINT, SIG_IGN);
+ char *shell;
+
+ if ((shell = value("SHELL")) == NOSTR)
+ shell = _PATH_CSHELL;
+ (void) run_command(shell, 0, -1, -1, NOSTR, NOSTR, NOSTR);
+ (void) signal(SIGINT, sigint);
+ putchar('\n');
+ return 0;
+}
+
+/*
+ * Expand the shell escape by expanding unescaped !'s into the
+ * last issued command where possible.
+ */
+
+char lastbang[128];
+
+int
+bangexp(str)
+ char *str;
+{
+ char bangbuf[BUFSIZ];
+ register char *cp, *cp2;
+ register int n;
+ int changed = 0;
+
+ cp = str;
+ cp2 = bangbuf;
+ n = BUFSIZ;
+ while (*cp) {
+ if (*cp == '!') {
+ if (n < strlen(lastbang)) {
+overf:
+ printf("Command buffer overflow\n");
+ return(-1);
+ }
+ changed++;
+ strcpy(cp2, lastbang);
+ cp2 += strlen(lastbang);
+ n -= strlen(lastbang);
+ cp++;
+ continue;
+ }
+ if (*cp == '\\' && cp[1] == '!') {
+ if (--n <= 1)
+ goto overf;
+ *cp2++ = '!';
+ cp += 2;
+ changed++;
+ }
+ if (--n <= 1)
+ goto overf;
+ *cp2++ = *cp++;
+ }
+ *cp2 = 0;
+ if (changed) {
+ printf("!%s\n", bangbuf);
+ fflush(stdout);
+ }
+ strcpy(str, bangbuf);
+ strncpy(lastbang, bangbuf, 128);
+ lastbang[127] = 0;
+ return(0);
+}
+
+/*
+ * Print out a nice help message from some file or another.
+ */
+
+int
+help()
+{
+ register c;
+ register FILE *f;
+
+ if ((f = Fopen(_PATH_HELP, "r")) == NULL) {
+ perror(_PATH_HELP);
+ return(1);
+ }
+ while ((c = getc(f)) != EOF)
+ putchar(c);
+ Fclose(f);
+ return(0);
+}
+
+/*
+ * Change user's working directory.
+ */
+int
+schdir(arglist)
+ char **arglist;
+{
+ char *cp;
+
+ if (*arglist == NOSTR)
+ cp = homedir;
+ else
+ if ((cp = expand(*arglist)) == NOSTR)
+ return(1);
+ if (chdir(cp) < 0) {
+ perror(cp);
+ return(1);
+ }
+ return 0;
+}
+
+int
+respond(msgvec)
+ int *msgvec;
+{
+ if (value("Replyall") == NOSTR)
+ return (_respond(msgvec));
+ else
+ return (_Respond(msgvec));
+}
+
+/*
+ * Reply to a list of messages. Extract each name from the
+ * message header and send them off to mail1()
+ */
+int
+_respond(msgvec)
+ int *msgvec;
+{
+ struct message *mp;
+ char *cp, *rcv, *replyto;
+ char **ap;
+ struct name *np;
+ struct header head;
+
+ if (msgvec[1] != 0) {
+ printf("Sorry, can't reply to multiple messages at once\n");
+ return(1);
+ }
+ mp = &message[msgvec[0] - 1];
+ touch(mp);
+ dot = mp;
+ if ((rcv = skin(hfield("from", mp))) == NOSTR)
+ rcv = skin(nameof(mp, 1));
+ if ((replyto = skin(hfield("reply-to", mp))) != NOSTR)
+ np = extract(replyto, GTO);
+ else if ((cp = skin(hfield("to", mp))) != NOSTR)
+ np = extract(cp, GTO);
+ else
+ np = NIL;
+ np = elide(np);
+ /*
+ * Delete my name from the reply list,
+ * and with it, all my alternate names.
+ */
+ np = delname(np, myname);
+ if (altnames)
+ for (ap = altnames; *ap; ap++)
+ np = delname(np, *ap);
+ if (np != NIL && replyto == NOSTR)
+ np = cat(np, extract(rcv, GTO));
+ else if (np == NIL) {
+ if (replyto != NOSTR)
+ printf("Empty reply-to field -- replying to author\n");
+ np = extract(rcv, GTO);
+ }
+ head.h_to = np;
+ if ((head.h_subject = hfield("subject", mp)) == NOSTR)
+ head.h_subject = hfield("subj", mp);
+ head.h_subject = reedit(head.h_subject);
+ if (replyto == NOSTR && (cp = skin(hfield("cc", mp))) != NOSTR) {
+ np = elide(extract(cp, GCC));
+ np = delname(np, myname);
+ if (altnames != 0)
+ for (ap = altnames; *ap; ap++)
+ np = delname(np, *ap);
+ head.h_cc = np;
+ } else
+ head.h_cc = NIL;
+ head.h_bcc = NIL;
+ head.h_smopts = NIL;
+ mail1(&head, 1);
+ return(0);
+}
+
+/*
+ * Modify the subject we are replying to to begin with Re: if
+ * it does not already.
+ */
+char *
+reedit(subj)
+ register char *subj;
+{
+ char *newsubj;
+
+ if (subj == NOSTR)
+ return NOSTR;
+ if ((subj[0] == 'r' || subj[0] == 'R') &&
+ (subj[1] == 'e' || subj[1] == 'E') &&
+ subj[2] == ':')
+ return subj;
+ newsubj = salloc(strlen(subj) + 5);
+ strcpy(newsubj, "Re: ");
+ strcpy(newsubj + 4, subj);
+ return newsubj;
+}
+
+/*
+ * Preserve the named messages, so that they will be sent
+ * back to the system mailbox.
+ */
+int
+preserve(msgvec)
+ int *msgvec;
+{
+ register struct message *mp;
+ register int *ip, mesg;
+
+ if (edit) {
+ printf("Cannot \"preserve\" in edit mode\n");
+ return(1);
+ }
+ for (ip = msgvec; *ip != NULL; ip++) {
+ mesg = *ip;
+ mp = &message[mesg-1];
+ mp->m_flag |= MPRESERVE;
+ mp->m_flag &= ~MBOX;
+ dot = mp;
+ }
+ return(0);
+}
+
+/*
+ * Mark all given messages as unread.
+ */
+int
+unread(msgvec)
+ int msgvec[];
+{
+ register int *ip;
+
+ for (ip = msgvec; *ip != NULL; ip++) {
+ dot = &message[*ip-1];
+ dot->m_flag &= ~(MREAD|MTOUCH);
+ dot->m_flag |= MSTATUS;
+ }
+ return(0);
+}
+
+/*
+ * Print the size of each message.
+ */
+int
+messize(msgvec)
+ int *msgvec;
+{
+ register struct message *mp;
+ register int *ip, mesg;
+
+ for (ip = msgvec; *ip != NULL; ip++) {
+ mesg = *ip;
+ mp = &message[mesg-1];
+ printf("%d: %d/%ld\n", mesg, mp->m_lines, mp->m_size);
+ }
+ return(0);
+}
+
+/*
+ * Quit quickly. If we are sourcing, just pop the input level
+ * by returning an error.
+ */
+int
+rexit(e)
+ int e;
+{
+ if (sourcing)
+ return(1);
+ exit(e);
+ /*NOTREACHED*/
+}
+
+/*
+ * Set or display a variable value. Syntax is similar to that
+ * of csh.
+ */
+int
+set(arglist)
+ char **arglist;
+{
+ register struct var *vp;
+ register char *cp, *cp2;
+ char varbuf[BUFSIZ], **ap, **p;
+ int errs, h, s;
+
+ if (*arglist == NOSTR) {
+ for (h = 0, s = 1; h < HSHSIZE; h++)
+ for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
+ s++;
+ ap = (char **) salloc(s * sizeof *ap);
+ for (h = 0, p = ap; h < HSHSIZE; h++)
+ for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
+ *p++ = vp->v_name;
+ *p = NOSTR;
+ sort(ap);
+ for (p = ap; *p != NOSTR; p++)
+ printf("%s\t%s\n", *p, value(*p));
+ return(0);
+ }
+ errs = 0;
+ for (ap = arglist; *ap != NOSTR; ap++) {
+ cp = *ap;
+ cp2 = varbuf;
+ while (*cp != '=' && *cp != '\0')
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+ if (*cp == '\0')
+ cp = "";
+ else
+ cp++;
+ if (equal(varbuf, "")) {
+ printf("Non-null variable name required\n");
+ errs++;
+ continue;
+ }
+ assign(varbuf, cp);
+ }
+ return(errs);
+}
+
+/*
+ * Unset a bunch of variable values.
+ */
+int
+unset(arglist)
+ char **arglist;
+{
+ register struct var *vp, *vp2;
+ int errs, h;
+ char **ap;
+
+ errs = 0;
+ for (ap = arglist; *ap != NOSTR; ap++) {
+ if ((vp2 = lookup(*ap)) == NOVAR) {
+ if (!sourcing) {
+ printf("\"%s\": undefined variable\n", *ap);
+ errs++;
+ }
+ continue;
+ }
+ h = hash(*ap);
+ if (vp2 == variables[h]) {
+ variables[h] = variables[h]->v_link;
+ vfree(vp2->v_name);
+ vfree(vp2->v_value);
+ free((char *)vp2);
+ continue;
+ }
+ for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
+ ;
+ vp->v_link = vp2->v_link;
+ vfree(vp2->v_name);
+ vfree(vp2->v_value);
+ free((char *) vp2);
+ }
+ return(errs);
+}
+
+/*
+ * Put add users to a group.
+ */
+int
+group(argv)
+ char **argv;
+{
+ register struct grouphead *gh;
+ register struct group *gp;
+ register int h;
+ int s;
+ char **ap, *gname, **p;
+
+ if (*argv == NOSTR) {
+ for (h = 0, s = 1; h < HSHSIZE; h++)
+ for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
+ s++;
+ ap = (char **) salloc(s * sizeof *ap);
+ for (h = 0, p = ap; h < HSHSIZE; h++)
+ for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
+ *p++ = gh->g_name;
+ *p = NOSTR;
+ sort(ap);
+ for (p = ap; *p != NOSTR; p++)
+ printgroup(*p);
+ return(0);
+ }
+ if (argv[1] == NOSTR) {
+ printgroup(*argv);
+ return(0);
+ }
+ gname = *argv;
+ h = hash(gname);
+ if ((gh = findgroup(gname)) == NOGRP) {
+ gh = (struct grouphead *) calloc(sizeof *gh, 1);
+ gh->g_name = vcopy(gname);
+ gh->g_list = NOGE;
+ gh->g_link = groups[h];
+ groups[h] = gh;
+ }
+
+ /*
+ * Insert names from the command list into the group.
+ * Who cares if there are duplicates? They get tossed
+ * later anyway.
+ */
+
+ for (ap = argv+1; *ap != NOSTR; ap++) {
+ gp = (struct group *) calloc(sizeof *gp, 1);
+ gp->ge_name = vcopy(*ap);
+ gp->ge_link = gh->g_list;
+ gh->g_list = gp;
+ }
+ return(0);
+}
+
+/*
+ * Sort the passed string vecotor into ascending dictionary
+ * order.
+ */
+void
+sort(list)
+ char **list;
+{
+ register char **ap;
+ int diction();
+
+ for (ap = list; *ap != NOSTR; ap++)
+ ;
+ if (ap-list < 2)
+ return;
+ qsort(list, ap-list, sizeof(*list), diction);
+}
+
+/*
+ * Do a dictionary order comparison of the arguments from
+ * qsort.
+ */
+int
+diction(a, b)
+ const void *a, *b;
+{
+ return(strcmp(*(char **)a, *(char **)b));
+}
+
+/*
+ * The do nothing command for comments.
+ */
+
+/*ARGSUSED*/
+int
+null(e)
+ int e;
+{
+ return 0;
+}
+
+/*
+ * Change to another file. With no argument, print information about
+ * the current file.
+ */
+int
+file(argv)
+ register char **argv;
+{
+
+ if (argv[0] == NOSTR) {
+ newfileinfo();
+ return 0;
+ }
+ if (setfile(*argv) < 0)
+ return 1;
+ announce();
+ return 0;
+}
+
+/*
+ * Expand file names like echo
+ */
+int
+echo(argv)
+ char **argv;
+{
+ register char **ap;
+ register char *cp;
+
+ for (ap = argv; *ap != NOSTR; ap++) {
+ cp = *ap;
+ if ((cp = expand(cp)) != NOSTR) {
+ if (ap != argv)
+ putchar(' ');
+ printf("%s", cp);
+ }
+ }
+ putchar('\n');
+ return 0;
+}
+
+int
+Respond(msgvec)
+ int *msgvec;
+{
+ if (value("Replyall") == NOSTR)
+ return (_Respond(msgvec));
+ else
+ return (_respond(msgvec));
+}
+
+/*
+ * Reply to a series of messages by simply mailing to the senders
+ * and not messing around with the To: and Cc: lists as in normal
+ * reply.
+ */
+int
+_Respond(msgvec)
+ int msgvec[];
+{
+ struct header head;
+ struct message *mp;
+ register int *ap;
+ register char *cp;
+
+ head.h_to = NIL;
+ for (ap = msgvec; *ap != 0; ap++) {
+ mp = &message[*ap - 1];
+ touch(mp);
+ dot = mp;
+ if ((cp = skin(hfield("from", mp))) == NOSTR)
+ cp = skin(nameof(mp, 2));
+ head.h_to = cat(head.h_to, extract(cp, GTO));
+ }
+ if (head.h_to == NIL)
+ return 0;
+ mp = &message[msgvec[0] - 1];
+ if ((head.h_subject = hfield("subject", mp)) == NOSTR)
+ head.h_subject = hfield("subj", mp);
+ head.h_subject = reedit(head.h_subject);
+ head.h_cc = NIL;
+ head.h_bcc = NIL;
+ head.h_smopts = NIL;
+ mail1(&head, 1);
+ return 0;
+}
+
+/*
+ * Conditional commands. These allow one to parameterize one's
+ * .mailrc and do some things if sending, others if receiving.
+ */
+int
+ifcmd(argv)
+ char **argv;
+{
+ register char *cp;
+
+ if (cond != CANY) {
+ printf("Illegal nested \"if\"\n");
+ return(1);
+ }
+ cond = CANY;
+ cp = argv[0];
+ switch (*cp) {
+ case 'r': case 'R':
+ cond = CRCV;
+ break;
+
+ case 's': case 'S':
+ cond = CSEND;
+ break;
+
+ default:
+ printf("Unrecognized if-keyword: \"%s\"\n", cp);
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Implement 'else'. This is pretty simple -- we just
+ * flip over the conditional flag.
+ */
+int
+elsecmd()
+{
+
+ switch (cond) {
+ case CANY:
+ printf("\"Else\" without matching \"if\"\n");
+ return(1);
+
+ case CSEND:
+ cond = CRCV;
+ break;
+
+ case CRCV:
+ cond = CSEND;
+ break;
+
+ default:
+ printf("Mail's idea of conditions is screwed up\n");
+ cond = CANY;
+ break;
+ }
+ return(0);
+}
+
+/*
+ * End of if statement. Just set cond back to anything.
+ */
+int
+endifcmd()
+{
+
+ if (cond == CANY) {
+ printf("\"Endif\" without matching \"if\"\n");
+ return(1);
+ }
+ cond = CANY;
+ return(0);
+}
+
+/*
+ * Set the list of alternate names.
+ */
+int
+alternates(namelist)
+ char **namelist;
+{
+ register int c;
+ register char **ap, **ap2, *cp;
+
+ c = argcount(namelist) + 1;
+ if (c == 1) {
+ if (altnames == 0)
+ return(0);
+ for (ap = altnames; *ap; ap++)
+ printf("%s ", *ap);
+ printf("\n");
+ return(0);
+ }
+ if (altnames != 0)
+ free((char *) altnames);
+ altnames = (char **) calloc((unsigned) c, sizeof (char *));
+ for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
+ cp = (char *) calloc((unsigned) strlen(*ap) + 1, sizeof (char));
+ strcpy(cp, *ap);
+ *ap2 = cp;
+ }
+ *ap2 = 0;
+ return(0);
+}
diff --git a/usr.bin/mail/cmdtab.c b/usr.bin/mail/cmdtab.c
new file mode 100644
index 0000000..94e33a0
--- /dev/null
+++ b/usr.bin/mail/cmdtab.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "def.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Define all of the command names and bindings.
+ */
+
+struct cmd cmdtab[] = {
+ "next", next, NDMLIST, 0, MMNDEL,
+ "alias", group, M|RAWLIST, 0, 1000,
+ "print", type, MSGLIST, 0, MMNDEL,
+ "type", type, MSGLIST, 0, MMNDEL,
+ "Type", Type, MSGLIST, 0, MMNDEL,
+ "Print", Type, MSGLIST, 0, MMNDEL,
+ "visual", visual, I|MSGLIST, 0, MMNORM,
+ "top", top, MSGLIST, 0, MMNDEL,
+ "touch", stouch, W|MSGLIST, 0, MMNDEL,
+ "preserve", preserve, W|MSGLIST, 0, MMNDEL,
+ "delete", delete, W|P|MSGLIST, 0, MMNDEL,
+ "dp", deltype, W|MSGLIST, 0, MMNDEL,
+ "dt", deltype, W|MSGLIST, 0, MMNDEL,
+ "undelete", undelete, P|MSGLIST, MDELETED,MMNDEL,
+ "unset", unset, M|RAWLIST, 1, 1000,
+ "mail", sendmail, R|M|I|STRLIST, 0, 0,
+ "mbox", mboxit, W|MSGLIST, 0, 0,
+ "more", more, MSGLIST, 0, MMNDEL,
+ "page", more, MSGLIST, 0, MMNDEL,
+ "More", More, MSGLIST, 0, MMNDEL,
+ "Page", More, MSGLIST, 0, MMNDEL,
+ "unread", unread, MSGLIST, 0, MMNDEL,
+ "!", shell, I|STRLIST, 0, 0,
+ "copy", copycmd, M|STRLIST, 0, 0,
+ "chdir", schdir, M|RAWLIST, 0, 1,
+ "cd", schdir, M|RAWLIST, 0, 1,
+ "save", save, STRLIST, 0, 0,
+ "source", source, M|RAWLIST, 1, 1,
+ "set", set, M|RAWLIST, 0, 1000,
+ "shell", dosh, I|NOLIST, 0, 0,
+ "version", pversion, M|NOLIST, 0, 0,
+ "group", group, M|RAWLIST, 0, 1000,
+ "write", swrite, STRLIST, 0, 0,
+ "from", from, MSGLIST, 0, MMNORM,
+ "file", file, T|M|RAWLIST, 0, 1,
+ "folder", file, T|M|RAWLIST, 0, 1,
+ "folders", folders, T|M|NOLIST, 0, 0,
+ "?", help, M|NOLIST, 0, 0,
+ "z", scroll, M|STRLIST, 0, 0,
+ "headers", headers, MSGLIST, 0, MMNDEL,
+ "help", help, M|NOLIST, 0, 0,
+ "=", pdot, NOLIST, 0, 0,
+ "Reply", Respond, R|I|MSGLIST, 0, MMNDEL,
+ "Respond", Respond, R|I|MSGLIST, 0, MMNDEL,
+ "reply", respond, R|I|MSGLIST, 0, MMNDEL,
+ "respond", respond, R|I|MSGLIST, 0, MMNDEL,
+ "edit", editor, I|MSGLIST, 0, MMNORM,
+ "echo", echo, M|RAWLIST, 0, 1000,
+ "quit", quitcmd, NOLIST, 0, 0,
+ "list", pcmdlist, M|NOLIST, 0, 0,
+ "xit", rexit, M|NOLIST, 0, 0,
+ "exit", rexit, M|NOLIST, 0, 0,
+ "size", messize, MSGLIST, 0, MMNDEL,
+ "hold", preserve, W|MSGLIST, 0, MMNDEL,
+ "if", ifcmd, F|M|RAWLIST, 1, 1,
+ "else", elsecmd, F|M|RAWLIST, 0, 0,
+ "endif", endifcmd, F|M|RAWLIST, 0, 0,
+ "alternates", alternates, M|RAWLIST, 0, 1000,
+ "ignore", igfield, M|RAWLIST, 0, 1000,
+ "discard", igfield, M|RAWLIST, 0, 1000,
+ "retain", retfield, M|RAWLIST, 0, 1000,
+ "saveignore", saveigfield, M|RAWLIST, 0, 1000,
+ "savediscard", saveigfield, M|RAWLIST, 0, 1000,
+ "saveretain", saveretfield, M|RAWLIST, 0, 1000,
+/* "Header", Header, STRLIST, 0, 1000, */
+ "core", core, M|NOLIST, 0, 0,
+ "#", null, M|NOLIST, 0, 0,
+ "clobber", clobber, M|RAWLIST, 0, 1,
+ 0, 0, 0, 0, 0
+};
diff --git a/usr.bin/mail/collect.c b/usr.bin/mail/collect.c
new file mode 100644
index 0000000..9d54fd3
--- /dev/null
+++ b/usr.bin/mail/collect.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Collect input from standard input, handling
+ * ~ escapes.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Read a message from standard output and return a read file to it
+ * or NULL on error.
+ */
+
+/*
+ * The following hokiness with global variables is so that on
+ * receipt of an interrupt signal, the partial message can be salted
+ * away on dead.letter.
+ */
+
+static sig_t saveint; /* Previous SIGINT value */
+static sig_t savehup; /* Previous SIGHUP value */
+static sig_t savetstp; /* Previous SIGTSTP value */
+static sig_t savettou; /* Previous SIGTTOU value */
+static sig_t savettin; /* Previous SIGTTIN value */
+static FILE *collf; /* File for saving away */
+static int hadintr; /* Have seen one SIGINT so far */
+
+static jmp_buf colljmp; /* To get back to work */
+static int colljmp_p; /* whether to long jump */
+static jmp_buf collabort; /* To end collection with error */
+
+FILE *
+collect(hp, printheaders)
+ struct header *hp;
+ int printheaders;
+{
+ FILE *fbuf;
+ int lc, cc, escape, eofcount;
+ register int c, t;
+ char linebuf[LINESIZE], *cp;
+ extern char tempMail[];
+ char getsub;
+ int omask;
+ void collint(), collhup(), collstop();
+
+ collf = NULL;
+ /*
+ * Start catching signals from here, but we're still die on interrupts
+ * until we're in the main loop.
+ */
+ omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
+ if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+ signal(SIGINT, collint);
+ if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
+ signal(SIGHUP, collhup);
+ savetstp = signal(SIGTSTP, collstop);
+ savettou = signal(SIGTTOU, collstop);
+ savettin = signal(SIGTTIN, collstop);
+ if (setjmp(collabort) || setjmp(colljmp)) {
+ rm(tempMail);
+ goto err;
+ }
+ sigsetmask(omask & ~(sigmask(SIGINT) | sigmask(SIGHUP)));
+
+ noreset++;
+ if ((collf = Fopen(tempMail, "w+")) == NULL) {
+ perror(tempMail);
+ goto err;
+ }
+ unlink(tempMail);
+
+ /*
+ * If we are going to prompt for a subject,
+ * refrain from printing a newline after
+ * the headers (since some people mind).
+ */
+ t = GTO|GSUBJECT|GCC|GNL;
+ getsub = 0;
+ if (hp->h_subject == NOSTR && value("interactive") != NOSTR &&
+ (value("ask") != NOSTR || value("asksub") != NOSTR))
+ t &= ~GNL, getsub++;
+ if (printheaders) {
+ puthead(hp, stdout, t);
+ fflush(stdout);
+ }
+ if ((cp = value("escape")) != NOSTR)
+ escape = *cp;
+ else
+ escape = ESCAPE;
+ eofcount = 0;
+ hadintr = 0;
+
+ if (!setjmp(colljmp)) {
+ if (getsub)
+ grabh(hp, GSUBJECT);
+ } else {
+ /*
+ * Come here for printing the after-signal message.
+ * Duplicate messages won't be printed because
+ * the write is aborted if we get a SIGTTOU.
+ */
+cont:
+ if (hadintr) {
+ fflush(stdout);
+ fprintf(stderr,
+ "\n(Interrupt -- one more to kill letter)\n");
+ } else {
+ printf("(continue)\n");
+ fflush(stdout);
+ }
+ }
+ for (;;) {
+ colljmp_p = 1;
+ c = readline(stdin, linebuf, LINESIZE);
+ colljmp_p = 0;
+ if (c < 0) {
+ if (value("interactive") != NOSTR &&
+ value("ignoreeof") != NOSTR && ++eofcount < 25) {
+ printf("Use \".\" to terminate letter\n");
+ continue;
+ }
+ break;
+ }
+ eofcount = 0;
+ hadintr = 0;
+ if (linebuf[0] == '.' && linebuf[1] == '\0' &&
+ value("interactive") != NOSTR &&
+ (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
+ break;
+ if (linebuf[0] != escape || value("interactive") == NOSTR) {
+ if (putline(collf, linebuf) < 0)
+ goto err;
+ continue;
+ }
+ c = linebuf[1];
+ switch (c) {
+ default:
+ /*
+ * On double escape, just send the single one.
+ * Otherwise, it's an error.
+ */
+ if (c == escape) {
+ if (putline(collf, &linebuf[1]) < 0)
+ goto err;
+ else
+ break;
+ }
+ printf("Unknown tilde escape.\n");
+ break;
+ case 'C':
+ /*
+ * Dump core.
+ */
+ core();
+ break;
+ case '!':
+ /*
+ * Shell escape, send the balance of the
+ * line to sh -c.
+ */
+ shell(&linebuf[2]);
+ break;
+ case ':':
+ /*
+ * Escape to command mode, but be nice!
+ */
+ execute(&linebuf[2], 1);
+ goto cont;
+ case '.':
+ /*
+ * Simulate end of file on input.
+ */
+ goto out;
+ case 'q':
+ /*
+ * Force a quit of sending mail.
+ * Act like an interrupt happened.
+ */
+ hadintr++;
+ collint(SIGINT);
+ exit(1);
+ case 'h':
+ /*
+ * Grab a bunch of headers.
+ */
+ grabh(hp, GTO|GSUBJECT|GCC|GBCC);
+ goto cont;
+ case 't':
+ /*
+ * Add to the To list.
+ */
+ hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO));
+ break;
+ case 's':
+ /*
+ * Set the Subject list.
+ */
+ cp = &linebuf[2];
+ while (isspace(*cp))
+ cp++;
+ hp->h_subject = savestr(cp);
+ break;
+ case 'c':
+ /*
+ * Add to the CC list.
+ */
+ hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC));
+ break;
+ case 'b':
+ /*
+ * Add stuff to blind carbon copies list.
+ */
+ hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC));
+ break;
+ case 'd':
+ strcpy(linebuf + 2, getdeadletter());
+ /* fall into . . . */
+ case 'r':
+ /*
+ * Invoke a file:
+ * Search for the file name,
+ * then open it and copy the contents to collf.
+ */
+ cp = &linebuf[2];
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0') {
+ printf("Interpolate what file?\n");
+ break;
+ }
+ cp = expand(cp);
+ if (cp == NOSTR)
+ break;
+ if (isdir(cp)) {
+ printf("%s: Directory\n", cp);
+ break;
+ }
+ if ((fbuf = Fopen(cp, "r")) == NULL) {
+ perror(cp);
+ break;
+ }
+ printf("\"%s\" ", cp);
+ fflush(stdout);
+ lc = 0;
+ cc = 0;
+ while (readline(fbuf, linebuf, LINESIZE) >= 0) {
+ lc++;
+ if ((t = putline(collf, linebuf)) < 0) {
+ Fclose(fbuf);
+ goto err;
+ }
+ cc += t;
+ }
+ Fclose(fbuf);
+ printf("%d/%d\n", lc, cc);
+ break;
+ case 'w':
+ /*
+ * Write the message on a file.
+ */
+ cp = &linebuf[2];
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ fprintf(stderr, "Write what file!?\n");
+ break;
+ }
+ if ((cp = expand(cp)) == NOSTR)
+ break;
+ rewind(collf);
+ exwrite(cp, collf, 1);
+ break;
+ case 'm':
+ case 'M':
+ case 'f':
+ case 'F':
+ /*
+ * Interpolate the named messages, if we
+ * are in receiving mail mode. Does the
+ * standard list processing garbage.
+ * If ~f is given, we don't shift over.
+ */
+ if (forward(linebuf + 2, collf, c) < 0)
+ goto err;
+ goto cont;
+ case '?':
+ if ((fbuf = Fopen(_PATH_TILDE, "r")) == NULL) {
+ perror(_PATH_TILDE);
+ break;
+ }
+ while ((t = getc(fbuf)) != EOF)
+ (void) putchar(t);
+ Fclose(fbuf);
+ break;
+ case 'p':
+ /*
+ * Print out the current state of the
+ * message without altering anything.
+ */
+ rewind(collf);
+ printf("-------\nMessage contains:\n");
+ puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
+ while ((t = getc(collf)) != EOF)
+ (void) putchar(t);
+ goto cont;
+ case '|':
+ /*
+ * Pipe message through command.
+ * Collect output as new message.
+ */
+ rewind(collf);
+ mespipe(collf, &linebuf[2]);
+ goto cont;
+ case 'v':
+ case 'e':
+ /*
+ * Edit the current message.
+ * 'e' means to use EDITOR
+ * 'v' means to use VISUAL
+ */
+ rewind(collf);
+ mesedit(collf, c);
+ goto cont;
+ }
+ }
+ goto out;
+err:
+ if (collf != NULL) {
+ Fclose(collf);
+ collf = NULL;
+ }
+out:
+ if (collf != NULL)
+ rewind(collf);
+ noreset--;
+ sigblock(sigmask(SIGINT) | sigmask(SIGHUP));
+ signal(SIGINT, saveint);
+ signal(SIGHUP, savehup);
+ signal(SIGTSTP, savetstp);
+ signal(SIGTTOU, savettou);
+ signal(SIGTTIN, savettin);
+ sigsetmask(omask);
+ return collf;
+}
+
+/*
+ * Write a file, ex-like if f set.
+ */
+int
+exwrite(name, fp, f)
+ char name[];
+ FILE *fp;
+ int f;
+{
+ register FILE *of;
+ register int c;
+ long cc;
+ int lc;
+ struct stat junk;
+
+ if (f) {
+ printf("\"%s\" ", name);
+ fflush(stdout);
+ }
+ if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
+ if (!f)
+ fprintf(stderr, "%s: ", name);
+ fprintf(stderr, "File exists\n");
+ return(-1);
+ }
+ if ((of = Fopen(name, "w")) == NULL) {
+ perror(NOSTR);
+ return(-1);
+ }
+ lc = 0;
+ cc = 0;
+ while ((c = getc(fp)) != EOF) {
+ cc++;
+ if (c == '\n')
+ lc++;
+ (void) putc(c, of);
+ if (ferror(of)) {
+ perror(name);
+ Fclose(of);
+ return(-1);
+ }
+ }
+ Fclose(of);
+ printf("%d/%ld\n", lc, cc);
+ fflush(stdout);
+ return(0);
+}
+
+/*
+ * Edit the message being collected on fp.
+ * On return, make the edit file the new temp file.
+ */
+void
+mesedit(fp, c)
+ FILE *fp;
+ int c;
+{
+ sig_t sigint = signal(SIGINT, SIG_IGN);
+ FILE *nf = run_editor(fp, (off_t)-1, c, 0);
+
+ if (nf != NULL) {
+ fseek(nf, 0L, 2);
+ collf = nf;
+ Fclose(fp);
+ }
+ (void) signal(SIGINT, sigint);
+}
+
+/*
+ * Pipe the message through the command.
+ * Old message is on stdin of command;
+ * New message collected from stdout.
+ * Sh -c must return 0 to accept the new message.
+ */
+void
+mespipe(fp, cmd)
+ FILE *fp;
+ char cmd[];
+{
+ FILE *nf;
+ sig_t sigint = signal(SIGINT, SIG_IGN);
+ extern char tempEdit[];
+ char *shell;
+
+ if ((nf = Fopen(tempEdit, "w+")) == NULL) {
+ perror(tempEdit);
+ goto out;
+ }
+ (void) unlink(tempEdit);
+ /*
+ * stdin = current message.
+ * stdout = new message.
+ */
+ if ((shell = value("SHELL")) == NOSTR)
+ shell = _PATH_CSHELL;
+ if (run_command(shell,
+ 0, fileno(fp), fileno(nf), "-c", cmd, NOSTR) < 0) {
+ (void) Fclose(nf);
+ goto out;
+ }
+ if (fsize(nf) == 0) {
+ fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
+ (void) Fclose(nf);
+ goto out;
+ }
+ /*
+ * Take new files.
+ */
+ (void) fseek(nf, 0L, 2);
+ collf = nf;
+ (void) Fclose(fp);
+out:
+ (void) signal(SIGINT, sigint);
+}
+
+/*
+ * Interpolate the named messages into the current
+ * message, preceding each line with a tab.
+ * Return a count of the number of characters now in
+ * the message, or -1 if an error is encountered writing
+ * the message temporary. The flag argument is 'm' if we
+ * should shift over and 'f' if not.
+ */
+int
+forward(ms, fp, f)
+ char ms[];
+ FILE *fp;
+ int f;
+{
+ register int *msgvec;
+ extern char tempMail[];
+ struct ignoretab *ig;
+ char *tabst;
+
+ msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
+ if (msgvec == (int *) NOSTR)
+ return(0);
+ if (getmsglist(ms, msgvec, 0) < 0)
+ return(0);
+ if (*msgvec == 0) {
+ *msgvec = first(0, MMNORM);
+ if (*msgvec == NULL) {
+ printf("No appropriate messages\n");
+ return(0);
+ }
+ msgvec[1] = NULL;
+ }
+ if (f == 'f' || f == 'F')
+ tabst = NOSTR;
+ else if ((tabst = value("indentprefix")) == NOSTR)
+ tabst = "\t";
+ ig = isupper(f) ? NULL : ignore;
+ printf("Interpolating:");
+ for (; *msgvec != 0; msgvec++) {
+ struct message *mp = message + *msgvec - 1;
+
+ touch(mp);
+ printf(" %d", *msgvec);
+ if (send(mp, fp, ig, tabst) < 0) {
+ perror(tempMail);
+ return(-1);
+ }
+ }
+ printf("\n");
+ return(0);
+}
+
+/*
+ * Print (continue) when continued after ^Z.
+ */
+/*ARGSUSED*/
+void
+collstop(s)
+ int s;
+{
+ sig_t old_action = signal(s, SIG_DFL);
+
+ sigsetmask(sigblock(0) & ~sigmask(s));
+ kill(0, s);
+ sigblock(sigmask(s));
+ signal(s, old_action);
+ if (colljmp_p) {
+ colljmp_p = 0;
+ hadintr = 0;
+ longjmp(colljmp, 1);
+ }
+}
+
+/*
+ * On interrupt, come here to save the partial message in ~/dead.letter.
+ * Then jump out of the collection loop.
+ */
+/*ARGSUSED*/
+void
+collint(s)
+ int s;
+{
+ /*
+ * the control flow is subtle, because we can be called from ~q.
+ */
+ if (!hadintr) {
+ if (value("ignore") != NOSTR) {
+ puts("@");
+ fflush(stdout);
+ clearerr(stdin);
+ return;
+ }
+ hadintr = 1;
+ longjmp(colljmp, 1);
+ }
+ rewind(collf);
+ if (value("nosave") == NOSTR)
+ savedeadletter(collf);
+ longjmp(collabort, 1);
+}
+
+/*ARGSUSED*/
+void
+collhup(s)
+ int s;
+{
+ rewind(collf);
+ savedeadletter(collf);
+ /*
+ * Let's pretend nobody else wants to clean up,
+ * a true statement at this time.
+ */
+ exit(1);
+}
+
+void
+savedeadletter(fp)
+ register FILE *fp;
+{
+ register FILE *dbuf;
+ register int c;
+ char *cp;
+
+ if (fsize(fp) == 0)
+ return;
+ cp = getdeadletter();
+ c = umask(077);
+ dbuf = Fopen(cp, "a");
+ (void) umask(c);
+ if (dbuf == NULL)
+ return;
+ while ((c = getc(fp)) != EOF)
+ (void) putc(c, dbuf);
+ Fclose(dbuf);
+ rewind(fp);
+}
diff --git a/usr.bin/mail/def.h b/usr.bin/mail/def.h
new file mode 100644
index 0000000..1faca76
--- /dev/null
+++ b/usr.bin/mail/def.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)def.h 8.2 (Berkeley) 3/21/94
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * Author: Kurt Shoens (UCB) March 25, 1978
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <signal.h>
+#include <sgtty.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "pathnames.h"
+
+#define APPEND /* New mail goes to end of mailbox */
+
+#define ESCAPE '~' /* Default escape for sending */
+#define NMLSIZE 1024 /* max names in a message list */
+#define PATHSIZE MAXPATHLEN /* Size of pathnames throughout */
+#define HSHSIZE 59 /* Hash size for aliases and vars */
+#define LINESIZE BUFSIZ /* max readable line width */
+#define STRINGSIZE ((unsigned) 128)/* Dynamic allocation units */
+#define MAXARGC 1024 /* Maximum list of raw strings */
+#define NOSTR ((char *) 0) /* Null string pointer */
+#define MAXEXP 25 /* Maximum expansion of aliases */
+
+#define equal(a, b) (strcmp(a,b)==0)/* A nice function to string compare */
+
+struct message {
+ short m_flag; /* flags, see below */
+ short m_offset; /* offset in block of message */
+ long m_block; /* block number of this message */
+ long m_size; /* Bytes in the message */
+ long m_lines; /* Lines in the message */
+};
+
+/*
+ * flag bits.
+ */
+
+#define MUSED (1<<0) /* entry is used, but this bit isn't */
+#define MDELETED (1<<1) /* entry has been deleted */
+#define MSAVED (1<<2) /* entry has been saved */
+#define MTOUCH (1<<3) /* entry has been noticed */
+#define MPRESERVE (1<<4) /* keep entry in sys mailbox */
+#define MMARK (1<<5) /* message is marked! */
+#define MODIFY (1<<6) /* message has been modified */
+#define MNEW (1<<7) /* message has never been seen */
+#define MREAD (1<<8) /* message has been read sometime. */
+#define MSTATUS (1<<9) /* message status has changed */
+#define MBOX (1<<10) /* Send this to mbox, regardless */
+
+/*
+ * Given a file address, determine the block number it represents.
+ */
+#define blockof(off) ((int) ((off) / 4096))
+#define offsetof(off) ((int) ((off) % 4096))
+#define positionof(block, offset) ((off_t)(block) * 4096 + (offset))
+
+/*
+ * Format of the command description table.
+ * The actual table is declared and initialized
+ * in lex.c
+ */
+struct cmd {
+ char *c_name; /* Name of command */
+ int (*c_func)(); /* Implementor of the command */
+ short c_argtype; /* Type of arglist (see below) */
+ short c_msgflag; /* Required flags of messages */
+ short c_msgmask; /* Relevant flags of messages */
+};
+
+/* Yechh, can't initialize unions */
+
+#define c_minargs c_msgflag /* Minimum argcount for RAWLIST */
+#define c_maxargs c_msgmask /* Max argcount for RAWLIST */
+
+/*
+ * Argument types.
+ */
+
+#define MSGLIST 0 /* Message list type */
+#define STRLIST 1 /* A pure string */
+#define RAWLIST 2 /* Shell string list */
+#define NOLIST 3 /* Just plain 0 */
+#define NDMLIST 4 /* Message list, no defaults */
+
+#define P 040 /* Autoprint dot after command */
+#define I 0100 /* Interactive command bit */
+#define M 0200 /* Legal from send mode bit */
+#define W 0400 /* Illegal when read only bit */
+#define F 01000 /* Is a conditional command */
+#define T 02000 /* Is a transparent command */
+#define R 04000 /* Cannot be called from collect */
+
+/*
+ * Oft-used mask values
+ */
+
+#define MMNORM (MDELETED|MSAVED)/* Look at both save and delete bits */
+#define MMNDEL MDELETED /* Look only at deleted bit */
+
+/*
+ * Structure used to return a break down of a head
+ * line (hats off to Bill Joy!)
+ */
+
+struct headline {
+ char *l_from; /* The name of the sender */
+ char *l_tty; /* His tty string (if any) */
+ char *l_date; /* The entire date string */
+};
+
+#define GTO 1 /* Grab To: line */
+#define GSUBJECT 2 /* Likewise, Subject: line */
+#define GCC 4 /* And the Cc: line */
+#define GBCC 8 /* And also the Bcc: line */
+#define GMASK (GTO|GSUBJECT|GCC|GBCC)
+ /* Mask of places from whence */
+
+#define GNL 16 /* Print blank line after */
+#define GDEL 32 /* Entity removed from list */
+#define GCOMMA 64 /* detract puts in commas */
+
+/*
+ * Structure used to pass about the current
+ * state of the user-typed message header.
+ */
+
+struct header {
+ struct name *h_to; /* Dynamic "To:" string */
+ char *h_subject; /* Subject string */
+ struct name *h_cc; /* Carbon copies string */
+ struct name *h_bcc; /* Blind carbon copies */
+ struct name *h_smopts; /* Sendmail options */
+};
+
+/*
+ * Structure of namelist nodes used in processing
+ * the recipients of mail and aliases and all that
+ * kind of stuff.
+ */
+
+struct name {
+ struct name *n_flink; /* Forward link in list. */
+ struct name *n_blink; /* Backward list link */
+ short n_type; /* From which list it came */
+ char *n_name; /* This fella's name */
+};
+
+/*
+ * Structure of a variable node. All variables are
+ * kept on a singly-linked list of these, rooted by
+ * "variables"
+ */
+
+struct var {
+ struct var *v_link; /* Forward link to next variable */
+ char *v_name; /* The variable's name */
+ char *v_value; /* And it's current value */
+};
+
+struct group {
+ struct group *ge_link; /* Next person in this group */
+ char *ge_name; /* This person's user name */
+};
+
+struct grouphead {
+ struct grouphead *g_link; /* Next grouphead in list */
+ char *g_name; /* Name of this group */
+ struct group *g_list; /* Users in group. */
+};
+
+#define NIL ((struct name *) 0) /* The nil pointer for namelists */
+#define NONE ((struct cmd *) 0) /* The nil pointer to command tab */
+#define NOVAR ((struct var *) 0) /* The nil pointer to variables */
+#define NOGRP ((struct grouphead *) 0)/* The nil grouphead pointer */
+#define NOGE ((struct group *) 0) /* The nil group pointer */
+
+/*
+ * Structure of the hash table of ignored header fields
+ */
+struct ignoretab {
+ int i_count; /* Number of entries */
+ struct ignore {
+ struct ignore *i_link; /* Next ignored field in bucket */
+ char *i_field; /* This ignored field */
+ } *i_head[HSHSIZE];
+};
+
+/*
+ * Token values returned by the scanner used for argument lists.
+ * Also, sizes of scanner-related things.
+ */
+
+#define TEOL 0 /* End of the command line */
+#define TNUMBER 1 /* A message number */
+#define TDASH 2 /* A simple dash */
+#define TSTRING 3 /* A string (possibly containing -) */
+#define TDOT 4 /* A "." */
+#define TUP 5 /* An "^" */
+#define TDOLLAR 6 /* A "$" */
+#define TSTAR 7 /* A "*" */
+#define TOPEN 8 /* An '(' */
+#define TCLOSE 9 /* A ')' */
+#define TPLUS 10 /* A '+' */
+#define TERROR 11 /* A lexical error */
+
+#define REGDEP 2 /* Maximum regret depth. */
+#define STRINGLEN 1024 /* Maximum length of string token */
+
+/*
+ * Constants for conditional commands. These describe whether
+ * we should be executing stuff or not.
+ */
+
+#define CANY 0 /* Execute in send or receive mode */
+#define CRCV 1 /* Execute in receive mode only */
+#define CSEND 2 /* Execute in send mode only */
+
+/*
+ * Kludges to handle the change from setexit / reset to setjmp / longjmp
+ */
+
+#define setexit() setjmp(srbuf)
+#define reset(x) longjmp(srbuf, x)
+
+/*
+ * Truncate a file to the last character written. This is
+ * useful just before closing an old file that was opened
+ * for read/write.
+ */
+#define trunc(stream) { \
+ (void)fflush(stream); \
+ (void)ftruncate(fileno(stream), (long)ftell(stream)); \
+}
diff --git a/usr.bin/mail/edit.c b/usr.bin/mail/edit.c
new file mode 100644
index 0000000..97d3bd4
--- /dev/null
+++ b/usr.bin/mail/edit.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)edit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Perform message editing functions.
+ */
+
+/*
+ * Edit a message list.
+ */
+int
+editor(msgvec)
+ int *msgvec;
+{
+
+ return edit1(msgvec, 'e');
+}
+
+/*
+ * Invoke the visual editor on a message list.
+ */
+int
+visual(msgvec)
+ int *msgvec;
+{
+
+ return edit1(msgvec, 'v');
+}
+
+/*
+ * Edit a message by writing the message into a funnily-named file
+ * (which should not exist) and forking an editor on it.
+ * We get the editor from the stuff above.
+ */
+int
+edit1(msgvec, type)
+ int *msgvec;
+ int type;
+{
+ register int c;
+ int i;
+ FILE *fp;
+ register struct message *mp;
+ off_t size;
+
+ /*
+ * Deal with each message to be edited . . .
+ */
+ for (i = 0; msgvec[i] && i < msgCount; i++) {
+ sig_t sigint;
+
+ if (i > 0) {
+ char buf[100];
+ char *p;
+
+ printf("Edit message %d [ynq]? ", msgvec[i]);
+ if (fgets(buf, sizeof buf, stdin) == 0)
+ break;
+ for (p = buf; *p == ' ' || *p == '\t'; p++)
+ ;
+ if (*p == 'q')
+ break;
+ if (*p == 'n')
+ continue;
+ }
+ dot = mp = &message[msgvec[i] - 1];
+ touch(mp);
+ sigint = signal(SIGINT, SIG_IGN);
+ fp = run_editor(setinput(mp), mp->m_size, type, readonly);
+ if (fp != NULL) {
+ (void) fseek(otf, 0L, 2);
+ size = ftell(otf);
+ mp->m_block = blockof(size);
+ mp->m_offset = offsetof(size);
+ mp->m_size = fsize(fp);
+ mp->m_lines = 0;
+ mp->m_flag |= MODIFY;
+ rewind(fp);
+ while ((c = getc(fp)) != EOF) {
+ if (c == '\n')
+ mp->m_lines++;
+ if (putc(c, otf) == EOF)
+ break;
+ }
+ if (ferror(otf))
+ perror("/tmp");
+ (void) Fclose(fp);
+ }
+ (void) signal(SIGINT, sigint);
+ }
+ return 0;
+}
+
+/*
+ * Run an editor on the file at "fpp" of "size" bytes,
+ * and return a new file pointer.
+ * Signals must be handled by the caller.
+ * "Type" is 'e' for _PATH_EX, 'v' for _PATH_VI.
+ */
+FILE *
+run_editor(fp, size, type, readonly)
+ register FILE *fp;
+ off_t size;
+ int type, readonly;
+{
+ register FILE *nf = NULL;
+ register int t;
+ time_t modtime;
+ char *edit;
+ struct stat statb;
+ extern char tempEdit[];
+
+ if ((t = creat(tempEdit, readonly ? 0400 : 0600)) < 0) {
+ perror(tempEdit);
+ goto out;
+ }
+ if ((nf = Fdopen(t, "w")) == NULL) {
+ perror(tempEdit);
+ (void) unlink(tempEdit);
+ goto out;
+ }
+ if (size >= 0)
+ while (--size >= 0 && (t = getc(fp)) != EOF)
+ (void) putc(t, nf);
+ else
+ while ((t = getc(fp)) != EOF)
+ (void) putc(t, nf);
+ (void) fflush(nf);
+ if (fstat(fileno(nf), &statb) < 0)
+ modtime = 0;
+ else
+ modtime = statb.st_mtime;
+ if (ferror(nf)) {
+ (void) Fclose(nf);
+ perror(tempEdit);
+ (void) unlink(tempEdit);
+ nf = NULL;
+ goto out;
+ }
+ if (Fclose(nf) < 0) {
+ perror(tempEdit);
+ (void) unlink(tempEdit);
+ nf = NULL;
+ goto out;
+ }
+ nf = NULL;
+ if ((edit = value(type == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
+ edit = type == 'e' ? _PATH_EX : _PATH_VI;
+ if (run_command(edit, 0, -1, -1, tempEdit, NOSTR, NOSTR) < 0) {
+ (void) unlink(tempEdit);
+ goto out;
+ }
+ /*
+ * If in read only mode or file unchanged, just remove the editor
+ * temporary and return.
+ */
+ if (readonly) {
+ (void) unlink(tempEdit);
+ goto out;
+ }
+ if (stat(tempEdit, &statb) < 0) {
+ perror(tempEdit);
+ goto out;
+ }
+ if (modtime == statb.st_mtime) {
+ (void) unlink(tempEdit);
+ goto out;
+ }
+ /*
+ * Now switch to new file.
+ */
+ if ((nf = Fopen(tempEdit, "a+")) == NULL) {
+ perror(tempEdit);
+ (void) unlink(tempEdit);
+ goto out;
+ }
+ (void) unlink(tempEdit);
+out:
+ return nf;
+}
diff --git a/usr.bin/mail/extern.h b/usr.bin/mail/extern.h
new file mode 100644
index 0000000..8b1babf
--- /dev/null
+++ b/usr.bin/mail/extern.h
@@ -0,0 +1,253 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct name *cat __P((struct name *, struct name *));
+struct name *delname __P((struct name *, char []));
+struct name *elide __P((struct name *));
+struct name *extract __P((char [], int));
+struct name *gexpand __P((struct name *, struct grouphead *, int, int));
+struct name *nalloc __P((char [], int));
+struct name *outof __P((struct name *, FILE *, struct header *));
+struct name *put __P((struct name *, struct name *));
+struct name *tailof __P((struct name *));
+struct name *usermap __P((struct name *));
+FILE *Fdopen __P((int, char *));
+FILE *Fopen __P((char *, char *));
+FILE *Popen __P((char *, char *));
+FILE *collect __P((struct header *, int));
+char *copy __P((char *, char *));
+char *copyin __P((char *, char **));
+char *detract __P((struct name *, int));
+char *expand __P((char *));
+char *getdeadletter __P((void));
+char *getname __P((int));
+char *hfield __P((char [], struct message *));
+FILE *infix __P((struct header *, FILE *));
+char *ishfield __P((char [], char[], char *));
+char *name1 __P((struct message *, int));
+char *nameof __P((struct message *, int));
+char *nextword __P((char *, char *));
+char *readtty __P((char [], char []));
+char *reedit __P((char *));
+FILE *run_editor __P((FILE *, off_t, int, int));
+char *salloc __P((int));
+char *savestr __P((char *));
+FILE *setinput __P((struct message *));
+char *skin __P((char *));
+char *skip_comment __P((char *));
+char *snarf __P((char [], int *));
+char *username __P((void));
+char *value __P((char []));
+char *vcopy __P((char []));
+char *yankword __P((char *, char []));
+int Fclose __P((FILE *));
+int More __P((int *));
+int Pclose __P((FILE *));
+int Respond __P((int *));
+int Type __P((int *));
+int _Respond __P((int []));
+int _respond __P((int *));
+void alter __P((char *));
+int alternates __P((char **));
+void announce __P((void));
+int anyof __P((char *, char *));
+int append __P((struct message *, FILE *));
+int argcount __P((char **));
+void assign __P((char [], char []));
+int bangexp __P((char *));
+int blankline __P((char []));
+void brokpipe __P((int));
+int charcount __P((char *, int));
+int check __P((int, int));
+void clob1 __P((int));
+int clobber __P((char **));
+void close_all_files __P((void));
+int cmatch __P((char *, char *));
+void collhup __P((int));
+void collint __P((int));
+void collstop __P((int));
+void commands __P((void));
+int copycmd __P((char []));
+int core __P((void));
+int count __P((struct name *));
+int delete __P((int []));
+int delm __P((int []));
+int deltype __P((int []));
+void demail __P((void));
+int diction __P((const void *, const void *));
+int dosh __P((char *));
+int echo __P((char **));
+int edit1 __P((int *, int));
+int editor __P((int *));
+void edstop __P((void));
+int elsecmd __P((void));
+int endifcmd __P((void));
+int evalcol __P((int));
+int execute __P((char [], int));
+int exwrite __P((char [], FILE *, int));
+void fail __P((char [], char []));
+int file __P((char **));
+struct grouphead *
+ findgroup __P((char []));
+void findmail __P((char *, char *));
+int first __P((int, int));
+void fixhead __P((struct header *, struct name *));
+void fmt __P((char *, struct name *, FILE *, int));
+int folders __P((void));
+int forward __P((char [], FILE *, int));
+void free_child __P((int));
+int from __P((int *));
+off_t fsize __P((FILE *));
+int getfold __P((char *));
+int gethfield __P((FILE *, char [], int, char **));
+int getmsglist __P((char *, int *, int));
+int getrawlist __P((char [], char **, int));
+int getuserid __P((char []));
+int grabh __P((struct header *, int));
+int group __P((char **));
+void hangup __P((int));
+int hash __P((char *));
+void hdrstop __P((int));
+int headers __P((int *));
+int help __P((void));
+void holdsigs __P((void));
+int ifcmd __P((char **));
+int igcomp __P((const void *, const void *));
+int igfield __P((char *[]));
+int ignore1 __P((char *[], struct ignoretab *, char *));
+int igshow __P((struct ignoretab *, char *));
+void intr __P((int));
+int isdate __P((char []));
+int isdir __P((char []));
+int isfileaddr __P((char *));
+int ishead __P((char []));
+int isign __P((char *, struct ignoretab []));
+int isprefix __P((char *, char *));
+void istrcpy __P((char *, char *));
+struct cmd *
+ lex __P((char []));
+void load __P((char *));
+struct var *
+ lookup __P((char []));
+int mail __P((struct name *,
+ struct name *, struct name *, struct name *, char *));
+void mail1 __P((struct header *, int));
+void makemessage __P((FILE *));
+void mark __P((int));
+int markall __P((char [], int));
+int matchsender __P((char *, int));
+int matchsubj __P((char *, int));
+int mboxit __P((int []));
+int member __P((char *, struct ignoretab *));
+void mesedit __P((FILE *, int));
+void mespipe __P((FILE *, char []));
+int messize __P((int *));
+int metamess __P((int, int));
+int more __P((int *));
+int newfileinfo __P((void));
+int next __P((int *));
+int null __P((int));
+void panic __P((const char *, ...));
+void parse __P((char [], struct headline *, char []));
+int pcmdlist __P((void));
+int pdot __P((void));
+void prepare_child __P((int, int, int));
+int preserve __P((int *));
+void prettyprint __P((struct name *));
+void printgroup __P((char []));
+void printhead __P((int));
+int puthead __P((struct header *, FILE *, int));
+int putline __P((FILE *, char *));
+int pversion __P((int));
+void quit __P((void));
+int quitcmd __P((void));
+int raise __P((int));
+int readline __P((FILE *, char *, int));
+void register_file __P((FILE *, int, int));
+void regret __P((int));
+void relsesigs __P((void));
+int respond __P((int *));
+int retfield __P((char *[]));
+int rexit __P((int));
+int rm __P((char *));
+int run_command __P((char *, int, int, int, char *, char *, char *));
+int save __P((char []));
+int save1 __P((char [], int, char *, struct ignoretab *));
+void savedeadletter __P((FILE *));
+int saveigfield __P((char *[]));
+int savemail __P((char [], FILE *));
+int saveretfield __P((char *[]));
+int scan __P((char **));
+void scaninit __P((void));
+int schdir __P((char **));
+int screensize __P((void));
+int scroll __P((char []));
+int send __P((struct message *, FILE *, struct ignoretab *, char *));
+int sendmail __P((char *));
+int set __P((char **));
+int setfile __P((char *));
+void setmsize __P((int));
+void setptr __P((FILE *));
+void setscreensize __P((void));
+int shell __P((char *));
+void sigchild __P((int));
+void sort __P((char **));
+int source __P((char **));
+void spreserve __P((void));
+void sreset __P((void));
+int start_command __P((char *, int, int, int, char *, char *, char *));
+void statusput __P((struct message *, FILE *, char *));
+void stop __P((int));
+int stouch __P((int []));
+int swrite __P((char []));
+void tinit __P((void));
+int top __P((int *));
+void touch __P((struct message *));
+void ttyint __P((int));
+void ttystop __P((int));
+int type __P((int *));
+int type1 __P((int *, int, int));
+int undelete __P((int *));
+void unmark __P((int));
+char **unpack __P((struct name *));
+int unread __P((int []));
+void unregister_file __P((FILE *));
+int unset __P((char **));
+int unstack __P((void));
+void vfree __P((char *));
+int visual __P((int *));
+int wait_child __P((int));
+int wait_command __P((int));
+int writeback __P((FILE *));
diff --git a/usr.bin/mail/fio.c b/usr.bin/mail/fio.c
new file mode 100644
index 0000000..4f2a8a8
--- /dev/null
+++ b/usr.bin/mail/fio.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)fio.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include <unistd.h>
+#include <paths.h>
+#include <errno.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * File I/O.
+ */
+
+/*
+ * Set up the input pointers while copying the mail file into /tmp.
+ */
+void
+setptr(ibuf)
+ register FILE *ibuf;
+{
+ extern char *tmpdir;
+ register int c, count;
+ register char *cp, *cp2;
+ struct message this;
+ FILE *mestmp;
+ off_t offset;
+ int maybe, inhead;
+ char linebuf[LINESIZE];
+
+ /* Get temporary file. */
+ (void)sprintf(linebuf, "%s/mail.XXXXXX", tmpdir);
+ if ((c = mkstemp(linebuf)) == -1 ||
+ (mestmp = Fdopen(c, "r+")) == NULL) {
+ (void)fprintf(stderr, "mail: can't open %s\n", linebuf);
+ exit(1);
+ }
+ (void)unlink(linebuf);
+
+ msgCount = 0;
+ maybe = 1;
+ inhead = 0;
+ offset = 0;
+ this.m_flag = MUSED|MNEW;
+ this.m_size = 0;
+ this.m_lines = 0;
+ this.m_block = 0;
+ this.m_offset = 0;
+ for (;;) {
+ if (fgets(linebuf, LINESIZE, ibuf) == NULL) {
+ if (append(&this, mestmp)) {
+ perror("temporary file");
+ exit(1);
+ }
+ makemessage(mestmp);
+ return;
+ }
+ count = strlen(linebuf);
+ (void) fwrite(linebuf, sizeof *linebuf, count, otf);
+ if (ferror(otf)) {
+ perror("/tmp");
+ exit(1);
+ }
+ linebuf[count - 1] = 0;
+ if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
+ msgCount++;
+ if (append(&this, mestmp)) {
+ perror("temporary file");
+ exit(1);
+ }
+ this.m_flag = MUSED|MNEW;
+ this.m_size = 0;
+ this.m_lines = 0;
+ this.m_block = blockof(offset);
+ this.m_offset = offsetof(offset);
+ inhead = 1;
+ } else if (linebuf[0] == 0) {
+ inhead = 0;
+ } else if (inhead) {
+ for (cp = linebuf, cp2 = "status";; cp++) {
+ if ((c = *cp2++) == 0) {
+ while (isspace(*cp++))
+ ;
+ if (cp[-1] != ':')
+ break;
+ while (c = *cp++)
+ if (c == 'R')
+ this.m_flag |= MREAD;
+ else if (c == 'O')
+ this.m_flag &= ~MNEW;
+ inhead = 0;
+ break;
+ }
+ if (*cp != c && *cp != toupper(c))
+ break;
+ }
+ }
+ offset += count;
+ this.m_size += count;
+ this.m_lines++;
+ maybe = linebuf[0] == 0;
+ }
+}
+
+/*
+ * Drop the passed line onto the passed output buffer.
+ * If a write error occurs, return -1, else the count of
+ * characters written, including the newline.
+ */
+int
+putline(obuf, linebuf)
+ FILE *obuf;
+ char *linebuf;
+{
+ register int c;
+
+ c = strlen(linebuf);
+ (void) fwrite(linebuf, sizeof *linebuf, c, obuf);
+ (void) putc('\n', obuf);
+ if (ferror(obuf))
+ return (-1);
+ return (c + 1);
+}
+
+/*
+ * Read up a line from the specified input into the line
+ * buffer. Return the number of characters read. Do not
+ * include the newline at the end.
+ */
+int
+readline(ibuf, linebuf, linesize)
+ FILE *ibuf;
+ char *linebuf;
+ int linesize;
+{
+ register int n;
+
+ clearerr(ibuf);
+ if (fgets(linebuf, linesize, ibuf) == NULL)
+ return -1;
+ n = strlen(linebuf);
+ if (n > 0 && linebuf[n - 1] == '\n')
+ linebuf[--n] = '\0';
+ return n;
+}
+
+/*
+ * Return a file buffer all ready to read up the
+ * passed message pointer.
+ */
+FILE *
+setinput(mp)
+ register struct message *mp;
+{
+
+ fflush(otf);
+ if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) {
+ perror("fseek");
+ panic("temporary file seek");
+ }
+ return (itf);
+}
+
+/*
+ * Take the data out of the passed ghost file and toss it into
+ * a dynamically allocated message structure.
+ */
+void
+makemessage(f)
+ FILE *f;
+{
+ register size = (msgCount + 1) * sizeof (struct message);
+
+ if (message != 0)
+ free((char *) message);
+ if ((message = (struct message *) malloc((unsigned) size)) == 0)
+ panic("Insufficient memory for %d messages", msgCount);
+ dot = message;
+ size -= sizeof (struct message);
+ fflush(f);
+ (void) lseek(fileno(f), (off_t)sizeof *message, 0);
+ if (read(fileno(f), (char *) message, size) != size)
+ panic("Message temporary file corrupted");
+ message[msgCount].m_size = 0;
+ message[msgCount].m_lines = 0;
+ Fclose(f);
+}
+
+/*
+ * Append the passed message descriptor onto the temp file.
+ * If the write fails, return 1, else 0
+ */
+int
+append(mp, f)
+ struct message *mp;
+ FILE *f;
+{
+ return fwrite((char *) mp, sizeof *mp, 1, f) != 1;
+}
+
+/*
+ * Delete a file, but only if the file is a plain file.
+ */
+int
+rm(name)
+ char *name;
+{
+ struct stat sb;
+
+ if (stat(name, &sb) < 0)
+ return(-1);
+ if (!S_ISREG(sb.st_mode)) {
+ errno = EISDIR;
+ return(-1);
+ }
+ return(unlink(name));
+}
+
+static int sigdepth; /* depth of holdsigs() */
+static int omask;
+/*
+ * Hold signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+void
+holdsigs()
+{
+
+ if (sigdepth++ == 0)
+ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
+}
+
+/*
+ * Release signals SIGHUP, SIGINT, and SIGQUIT.
+ */
+void
+relsesigs()
+{
+
+ if (--sigdepth == 0)
+ sigsetmask(omask);
+}
+
+/*
+ * Determine the size of the file possessed by
+ * the passed buffer.
+ */
+off_t
+fsize(iob)
+ FILE *iob;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(iob), &sbuf) < 0)
+ return 0;
+ return sbuf.st_size;
+}
+
+/*
+ * Evaluate the string given as a new mailbox name.
+ * Supported meta characters:
+ * % for my system mail box
+ * %user for user's system mail box
+ * # for previous file
+ * & invoker's mbox file
+ * +file file in folder directory
+ * any shell meta character
+ * Return the file name as a dynamic string.
+ */
+char *
+expand(name)
+ register char *name;
+{
+ char xname[PATHSIZE];
+ char cmdbuf[PATHSIZE]; /* also used for file names */
+ register int pid, l;
+ register char *cp, *shell;
+ int pivec[2];
+ struct stat sbuf;
+ extern union wait wait_status;
+
+ /*
+ * The order of evaluation is "%" and "#" expand into constants.
+ * "&" can expand into "+". "+" can expand into shell meta characters.
+ * Shell meta characters expand into constants.
+ * This way, we make no recursive expansion.
+ */
+ switch (*name) {
+ case '%':
+ findmail(name[1] ? name + 1 : myname, xname);
+ return savestr(xname);
+ case '#':
+ if (name[1] != 0)
+ break;
+ if (prevfile[0] == 0) {
+ printf("No previous file\n");
+ return NOSTR;
+ }
+ return savestr(prevfile);
+ case '&':
+ if (name[1] == 0 && (name = value("MBOX")) == NOSTR)
+ name = "~/mbox";
+ /* fall through */
+ }
+ if (name[0] == '+' && getfold(cmdbuf) >= 0) {
+ sprintf(xname, "%s/%s", cmdbuf, name + 1);
+ name = savestr(xname);
+ }
+ /* catch the most common shell meta character */
+ if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) {
+ sprintf(xname, "%s%s", homedir, name + 1);
+ name = savestr(xname);
+ }
+ if (!anyof(name, "~{[*?$`'\"\\"))
+ return name;
+ if (pipe(pivec) < 0) {
+ perror("pipe");
+ return name;
+ }
+ sprintf(cmdbuf, "echo %s", name);
+ if ((shell = value("SHELL")) == NOSTR)
+ shell = _PATH_CSHELL;
+ pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NOSTR);
+ if (pid < 0) {
+ close(pivec[0]);
+ close(pivec[1]);
+ return NOSTR;
+ }
+ close(pivec[1]);
+ l = read(pivec[0], xname, BUFSIZ);
+ close(pivec[0]);
+ if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) {
+ fprintf(stderr, "\"%s\": Expansion failed.\n", name);
+ return NOSTR;
+ }
+ if (l < 0) {
+ perror("read");
+ return NOSTR;
+ }
+ if (l == 0) {
+ fprintf(stderr, "\"%s\": No match.\n", name);
+ return NOSTR;
+ }
+ if (l == BUFSIZ) {
+ fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
+ return NOSTR;
+ }
+ xname[l] = 0;
+ for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
+ ;
+ cp[1] = '\0';
+ if (index(xname, ' ') && stat(xname, &sbuf) < 0) {
+ fprintf(stderr, "\"%s\": Ambiguous.\n", name);
+ return NOSTR;
+ }
+ return savestr(xname);
+}
+
+/*
+ * Determine the current folder directory name.
+ */
+int
+getfold(name)
+ char *name;
+{
+ char *folder;
+
+ if ((folder = value("folder")) == NOSTR)
+ return (-1);
+ if (*folder == '/')
+ strcpy(name, folder);
+ else
+ sprintf(name, "%s/%s", homedir, folder);
+ return (0);
+}
+
+/*
+ * Return the name of the dead.letter file.
+ */
+char *
+getdeadletter()
+{
+ register char *cp;
+
+ if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR)
+ cp = expand("~/dead.letter");
+ else if (*cp != '/') {
+ char buf[PATHSIZE];
+
+ (void) sprintf(buf, "~/%s", cp);
+ cp = expand(buf);
+ }
+ return cp;
+}
diff --git a/usr.bin/mail/getname.c b/usr.bin/mail/getname.c
new file mode 100644
index 0000000..02a4131
--- /dev/null
+++ b/usr.bin/mail/getname.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getname.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <pwd.h>
+#include "extern.h"
+
+/* Getname / getuserid for those with hashed passwd data base). */
+
+/*
+ * Search the passwd file for a uid. Return name through ref parameter
+ * if found, indicating success with 0 return. Return -1 on error.
+ */
+char *
+getname(uid)
+ int uid;
+{
+ struct passwd *pw;
+
+ if ((pw = getpwuid(uid)) == NULL)
+ return NOSTR;
+ return pw->pw_name;
+}
+
+/*
+ * Convert the passed name to a user id and return it. Return -1
+ * on error.
+ */
+int
+getuserid(name)
+ char name[];
+{
+ struct passwd *pw;
+
+ if ((pw = getpwnam(name)) == NULL)
+ return -1;
+ return pw->pw_uid;
+}
diff --git a/usr.bin/mail/glob.h b/usr.bin/mail/glob.h
new file mode 100644
index 0000000..a159aca
--- /dev/null
+++ b/usr.bin/mail/glob.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)glob.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * A bunch of global variable declarations lie herein.
+ * def.h must be included first.
+ */
+
+int msgCount; /* Count of messages read in */
+int rcvmode; /* True if receiving mail */
+int sawcom; /* Set after first command */
+char *Tflag; /* -T temp file for netnews */
+int senderr; /* An error while checking */
+int edit; /* Indicates editing a file */
+int readonly; /* Will be unable to rewrite file */
+int noreset; /* String resets suspended */
+int sourcing; /* Currently reading variant file */
+int loading; /* Loading user definitions */
+int cond; /* Current state of conditional exc. */
+FILE *itf; /* Input temp file buffer */
+FILE *otf; /* Output temp file buffer */
+int image; /* File descriptor for image of msg */
+FILE *input; /* Current command input file */
+char mailname[PATHSIZE]; /* Name of current file */
+char prevfile[PATHSIZE]; /* Name of previous file */
+char *homedir; /* Path name of home directory */
+char *myname; /* My login name */
+off_t mailsize; /* Size of system mailbox */
+int lexnumber; /* Number of TNUMBER from scan() */
+char lexstring[STRINGLEN]; /* String from TSTRING, scan() */
+int regretp; /* Pointer to TOS of regret tokens */
+int regretstack[REGDEP]; /* Stack of regretted tokens */
+char *string_stack[REGDEP]; /* Stack of regretted strings */
+int numberstack[REGDEP]; /* Stack of regretted numbers */
+struct message *dot; /* Pointer to current message */
+struct message *message; /* The actual message structure */
+struct var *variables[HSHSIZE]; /* Pointer to active var list */
+struct grouphead *groups[HSHSIZE];/* Pointer to active groups */
+struct ignoretab ignore[2]; /* ignored and retained fields
+ 0 is ignore, 1 is retain */
+struct ignoretab saveignore[2]; /* ignored and retained fields
+ on save to folder */
+struct ignoretab ignoreall[2]; /* special, ignore all headers */
+char **altnames; /* List of alternate names for user */
+int debug; /* Debug flag set */
+int screenwidth; /* Screen width, or best guess */
+int screenheight; /* Screen height, or best guess,
+ for "header" command */
+int realscreenheight; /* the real screen height */
+
+#include <setjmp.h>
+
+jmp_buf srbuf;
+
+
+/*
+ * The pointers for the string allocation routines,
+ * there are NSPACE independent areas.
+ * The first holds STRINGSIZE bytes, the next
+ * twice as much, and so on.
+ */
+
+#define NSPACE 25 /* Total number of string spaces */
+struct strings {
+ char *s_topFree; /* Beginning of this area */
+ char *s_nextFree; /* Next alloctable place here */
+ unsigned s_nleft; /* Number of bytes left here */
+} stringdope[NSPACE];
diff --git a/usr.bin/mail/head.c b/usr.bin/mail/head.c
new file mode 100644
index 0000000..ca31ae1
--- /dev/null
+++ b/usr.bin/mail/head.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)head.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Routines for processing and detecting headlines.
+ */
+
+/*
+ * See if the passed line buffer is a mail header.
+ * Return true if yes. Note the extreme pains to
+ * accomodate all funny formats.
+ */
+int
+ishead(linebuf)
+ char linebuf[];
+{
+ register char *cp;
+ struct headline hl;
+ char parbuf[BUFSIZ];
+
+ cp = linebuf;
+ if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
+ *cp++ != ' ')
+ return (0);
+ parse(linebuf, &hl, parbuf);
+ if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
+ fail(linebuf, "No from or date field");
+ return (0);
+ }
+ if (!isdate(hl.l_date)) {
+ fail(linebuf, "Date field not legal date");
+ return (0);
+ }
+ /*
+ * I guess we got it!
+ */
+ return (1);
+}
+
+/*ARGSUSED*/
+void
+fail(linebuf, reason)
+ char linebuf[], reason[];
+{
+
+ /*
+ if (value("debug") == NOSTR)
+ return;
+ fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
+ */
+}
+
+/*
+ * Split a headline into its useful components.
+ * Copy the line into dynamic string space, then set
+ * pointers into the copied line in the passed headline
+ * structure. Actually, it scans.
+ */
+void
+parse(line, hl, pbuf)
+ char line[], pbuf[];
+ register struct headline *hl;
+{
+ register char *cp;
+ char *sp;
+ char word[LINESIZE];
+
+ hl->l_from = NOSTR;
+ hl->l_tty = NOSTR;
+ hl->l_date = NOSTR;
+ cp = line;
+ sp = pbuf;
+ /*
+ * Skip over "From" first.
+ */
+ cp = nextword(cp, word);
+ cp = nextword(cp, word);
+ if (*word)
+ hl->l_from = copyin(word, &sp);
+ if (cp != NOSTR && cp[0] == 't' && cp[1] == 't' && cp[2] == 'y') {
+ cp = nextword(cp, word);
+ hl->l_tty = copyin(word, &sp);
+ }
+ if (cp != NOSTR)
+ hl->l_date = copyin(cp, &sp);
+}
+
+/*
+ * Copy the string on the left into the string on the right
+ * and bump the right (reference) string pointer by the length.
+ * Thus, dynamically allocate space in the right string, copying
+ * the left string into it.
+ */
+char *
+copyin(src, space)
+ register char *src;
+ char **space;
+{
+ register char *cp;
+ char *top;
+
+ top = cp = *space;
+ while (*cp++ = *src++)
+ ;
+ *space = cp;
+ return (top);
+}
+
+/*
+ * Test to see if the passed string is a ctime(3) generated
+ * date string as documented in the manual. The template
+ * below is used as the criterion of correctness.
+ * Also, we check for a possible trailing time zone using
+ * the tmztype template.
+ */
+
+/*
+ * 'A' An upper case char
+ * 'a' A lower case char
+ * ' ' A space
+ * '0' A digit
+ * 'O' An optional digit or space
+ * ':' A colon
+ * 'N' A new line
+ */
+char ctype[] = "Aaa Aaa O0 00:00:00 0000";
+char tmztype[] = "Aaa Aaa O0 00:00:00 AAA 0000";
+
+int
+isdate(date)
+ char date[];
+{
+
+ return cmatch(date, ctype) || cmatch(date, tmztype);
+}
+
+/*
+ * Match the given string (cp) against the given template (tp).
+ * Return 1 if they match, 0 if they don't
+ */
+int
+cmatch(cp, tp)
+ register char *cp, *tp;
+{
+
+ while (*cp && *tp)
+ switch (*tp++) {
+ case 'a':
+ if (!islower(*cp++))
+ return 0;
+ break;
+ case 'A':
+ if (!isupper(*cp++))
+ return 0;
+ break;
+ case ' ':
+ if (*cp++ != ' ')
+ return 0;
+ break;
+ case '0':
+ if (!isdigit(*cp++))
+ return 0;
+ break;
+ case 'O':
+ if (*cp != ' ' && !isdigit(*cp))
+ return 0;
+ cp++;
+ break;
+ case ':':
+ if (*cp++ != ':')
+ return 0;
+ break;
+ case 'N':
+ if (*cp++ != '\n')
+ return 0;
+ break;
+ }
+ if (*cp || *tp)
+ return 0;
+ return (1);
+}
+
+/*
+ * Collect a liberal (space, tab delimited) word into the word buffer
+ * passed. Also, return a pointer to the next word following that,
+ * or NOSTR if none follow.
+ */
+char *
+nextword(wp, wbuf)
+ register char *wp, *wbuf;
+{
+ register c;
+
+ if (wp == NOSTR) {
+ *wbuf = 0;
+ return (NOSTR);
+ }
+ while ((c = *wp++) && c != ' ' && c != '\t') {
+ *wbuf++ = c;
+ if (c == '"') {
+ while ((c = *wp++) && c != '"')
+ *wbuf++ = c;
+ if (c == '"')
+ *wbuf++ = c;
+ else
+ wp--;
+ }
+ }
+ *wbuf = '\0';
+ for (; c == ' ' || c == '\t'; c = *wp++)
+ ;
+ if (c == 0)
+ return (NOSTR);
+ return (wp - 1);
+}
diff --git a/usr.bin/mail/lex.c b/usr.bin/mail/lex.c
new file mode 100644
index 0000000..2c7cb3e
--- /dev/null
+++ b/usr.bin/mail/lex.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lex.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <errno.h>
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Lexical processing of commands.
+ */
+
+char *prompt = "& ";
+
+/*
+ * Set up editing on the given file name.
+ * If the first character of name is %, we are considered to be
+ * editing the file, otherwise we are reading our mail which has
+ * signficance for mbox and so forth.
+ */
+int
+setfile(name)
+ char *name;
+{
+ FILE *ibuf;
+ int i;
+ struct stat stb;
+ char isedit = *name != '%';
+ char *who = name[1] ? name + 1 : myname;
+ static int shudclob;
+ extern char tempMesg[];
+ extern int errno;
+
+ if ((name = expand(name)) == NOSTR)
+ return -1;
+
+ if ((ibuf = Fopen(name, "r")) == NULL) {
+ if (!isedit && errno == ENOENT)
+ goto nomail;
+ perror(name);
+ return(-1);
+ }
+
+ if (fstat(fileno(ibuf), &stb) < 0) {
+ perror("fstat");
+ Fclose(ibuf);
+ return (-1);
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ Fclose(ibuf);
+ errno = EISDIR;
+ perror(name);
+ return (-1);
+
+ case S_IFREG:
+ break;
+
+ default:
+ Fclose(ibuf);
+ errno = EINVAL;
+ perror(name);
+ return (-1);
+ }
+
+ /*
+ * Looks like all will be well. We must now relinquish our
+ * hold on the current set of stuff. Must hold signals
+ * while we are reading the new file, else we will ruin
+ * the message[] data structure.
+ */
+
+ holdsigs();
+ if (shudclob)
+ quit();
+
+ /*
+ * Copy the messages into /tmp
+ * and set pointers.
+ */
+
+ readonly = 0;
+ if ((i = open(name, 1)) < 0)
+ readonly++;
+ else
+ close(i);
+ if (shudclob) {
+ fclose(itf);
+ fclose(otf);
+ }
+ shudclob = 1;
+ edit = isedit;
+ strcpy(prevfile, mailname);
+ if (name != mailname)
+ strcpy(mailname, name);
+ mailsize = fsize(ibuf);
+ if ((otf = fopen(tempMesg, "w")) == NULL) {
+ perror(tempMesg);
+ exit(1);
+ }
+ (void) fcntl(fileno(otf), F_SETFD, 1);
+ if ((itf = fopen(tempMesg, "r")) == NULL) {
+ perror(tempMesg);
+ exit(1);
+ }
+ (void) fcntl(fileno(itf), F_SETFD, 1);
+ rm(tempMesg);
+ setptr(ibuf);
+ setmsize(msgCount);
+ Fclose(ibuf);
+ relsesigs();
+ sawcom = 0;
+ if (!edit && msgCount == 0) {
+nomail:
+ fprintf(stderr, "No mail for %s\n", who);
+ return -1;
+ }
+ return(0);
+}
+
+int *msgvec;
+int reset_on_stop; /* do a reset() if stopped */
+
+/*
+ * Interpret user commands one by one. If standard input is not a tty,
+ * print no prompt.
+ */
+void
+commands()
+{
+ int eofloop = 0;
+ register int n;
+ char linebuf[LINESIZE];
+ void intr(), stop(), hangup();
+
+ if (!sourcing) {
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, intr);
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, hangup);
+ signal(SIGTSTP, stop);
+ signal(SIGTTOU, stop);
+ signal(SIGTTIN, stop);
+ }
+ setexit();
+ for (;;) {
+ /*
+ * Print the prompt, if needed. Clear out
+ * string space, and flush the output.
+ */
+ if (!sourcing && value("interactive") != NOSTR) {
+ reset_on_stop = 1;
+ printf(prompt);
+ }
+ fflush(stdout);
+ sreset();
+ /*
+ * Read a line of commands from the current input
+ * and handle end of file specially.
+ */
+ n = 0;
+ for (;;) {
+ if (readline(input, &linebuf[n], LINESIZE - n) < 0) {
+ if (n == 0)
+ n = -1;
+ break;
+ }
+ if ((n = strlen(linebuf)) == 0)
+ break;
+ n--;
+ if (linebuf[n] != '\\')
+ break;
+ linebuf[n++] = ' ';
+ }
+ reset_on_stop = 0;
+ if (n < 0) {
+ /* eof */
+ if (loading)
+ break;
+ if (sourcing) {
+ unstack();
+ continue;
+ }
+ if (value("interactive") != NOSTR &&
+ value("ignoreeof") != NOSTR &&
+ ++eofloop < 25) {
+ printf("Use \"quit\" to quit.\n");
+ continue;
+ }
+ break;
+ }
+ eofloop = 0;
+ if (execute(linebuf, 0))
+ break;
+ }
+}
+
+/*
+ * Execute a single command.
+ * Command functions return 0 for success, 1 for error, and -1
+ * for abort. A 1 or -1 aborts a load or source. A -1 aborts
+ * the interactive command loop.
+ * Contxt is non-zero if called while composing mail.
+ */
+int
+execute(linebuf, contxt)
+ char linebuf[];
+ int contxt;
+{
+ char word[LINESIZE];
+ char *arglist[MAXARGC];
+ struct cmd *com;
+ register char *cp, *cp2;
+ register int c;
+ int muvec[2];
+ int e = 1;
+
+ /*
+ * Strip the white space away from the beginning
+ * of the command, then scan out a word, which
+ * consists of anything except digits and white space.
+ *
+ * Handle ! escapes differently to get the correct
+ * lexical conventions.
+ */
+
+ for (cp = linebuf; isspace(*cp); cp++)
+ ;
+ if (*cp == '!') {
+ if (sourcing) {
+ printf("Can't \"!\" while sourcing\n");
+ goto out;
+ }
+ shell(cp+1);
+ return(0);
+ }
+ cp2 = word;
+ while (*cp && index(" \t0123456789$^.:/-+*'\"", *cp) == NOSTR)
+ *cp2++ = *cp++;
+ *cp2 = '\0';
+
+ /*
+ * Look up the command; if not found, bitch.
+ * Normally, a blank command would map to the
+ * first command in the table; while sourcing,
+ * however, we ignore blank lines to eliminate
+ * confusion.
+ */
+
+ if (sourcing && *word == '\0')
+ return(0);
+ com = lex(word);
+ if (com == NONE) {
+ printf("Unknown command: \"%s\"\n", word);
+ goto out;
+ }
+
+ /*
+ * See if we should execute the command -- if a conditional
+ * we always execute it, otherwise, check the state of cond.
+ */
+
+ if ((com->c_argtype & F) == 0)
+ if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
+ return(0);
+
+ /*
+ * Process the arguments to the command, depending
+ * on the type he expects. Default to an error.
+ * If we are sourcing an interactive command, it's
+ * an error.
+ */
+
+ if (!rcvmode && (com->c_argtype & M) == 0) {
+ printf("May not execute \"%s\" while sending\n",
+ com->c_name);
+ goto out;
+ }
+ if (sourcing && com->c_argtype & I) {
+ printf("May not execute \"%s\" while sourcing\n",
+ com->c_name);
+ goto out;
+ }
+ if (readonly && com->c_argtype & W) {
+ printf("May not execute \"%s\" -- message file is read only\n",
+ com->c_name);
+ goto out;
+ }
+ if (contxt && com->c_argtype & R) {
+ printf("Cannot recursively invoke \"%s\"\n", com->c_name);
+ goto out;
+ }
+ switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
+ case MSGLIST:
+ /*
+ * A message list defaulting to nearest forward
+ * legal message.
+ */
+ if (msgvec == 0) {
+ printf("Illegal use of \"message list\"\n");
+ break;
+ }
+ if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
+ break;
+ if (c == 0) {
+ *msgvec = first(com->c_msgflag,
+ com->c_msgmask);
+ msgvec[1] = NULL;
+ }
+ if (*msgvec == NULL) {
+ printf("No applicable messages\n");
+ break;
+ }
+ e = (*com->c_func)(msgvec);
+ break;
+
+ case NDMLIST:
+ /*
+ * A message list with no defaults, but no error
+ * if none exist.
+ */
+ if (msgvec == 0) {
+ printf("Illegal use of \"message list\"\n");
+ break;
+ }
+ if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
+ break;
+ e = (*com->c_func)(msgvec);
+ break;
+
+ case STRLIST:
+ /*
+ * Just the straight string, with
+ * leading blanks removed.
+ */
+ while (isspace(*cp))
+ cp++;
+ e = (*com->c_func)(cp);
+ break;
+
+ case RAWLIST:
+ /*
+ * A vector of strings, in shell style.
+ */
+ if ((c = getrawlist(cp, arglist,
+ sizeof arglist / sizeof *arglist)) < 0)
+ break;
+ if (c < com->c_minargs) {
+ printf("%s requires at least %d arg(s)\n",
+ com->c_name, com->c_minargs);
+ break;
+ }
+ if (c > com->c_maxargs) {
+ printf("%s takes no more than %d arg(s)\n",
+ com->c_name, com->c_maxargs);
+ break;
+ }
+ e = (*com->c_func)(arglist);
+ break;
+
+ case NOLIST:
+ /*
+ * Just the constant zero, for exiting,
+ * eg.
+ */
+ e = (*com->c_func)(0);
+ break;
+
+ default:
+ panic("Unknown argtype");
+ }
+
+out:
+ /*
+ * Exit the current source file on
+ * error.
+ */
+ if (e) {
+ if (e < 0)
+ return 1;
+ if (loading)
+ return 1;
+ if (sourcing)
+ unstack();
+ return 0;
+ }
+ if (value("autoprint") != NOSTR && com->c_argtype & P)
+ if ((dot->m_flag & MDELETED) == 0) {
+ muvec[0] = dot - &message[0] + 1;
+ muvec[1] = 0;
+ type(muvec);
+ }
+ if (!sourcing && (com->c_argtype & T) == 0)
+ sawcom = 1;
+ return(0);
+}
+
+/*
+ * Set the size of the message vector used to construct argument
+ * lists to message list functions.
+ */
+void
+setmsize(sz)
+ int sz;
+{
+
+ if (msgvec != 0)
+ free((char *) msgvec);
+ msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
+}
+
+/*
+ * Find the correct command in the command table corresponding
+ * to the passed command "word"
+ */
+
+struct cmd *
+lex(word)
+ char word[];
+{
+ register struct cmd *cp;
+ extern struct cmd cmdtab[];
+
+ /*
+ * ignore trailing chars after `#'
+ *
+ * lines with beginning `#' are comments
+ * spaces befor `#' are ignored in execute()
+ */
+
+ if (*word == '#')
+ *(word+1) = '\0';
+
+
+ for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
+ if (isprefix(word, cp->c_name))
+ return(cp);
+ return(NONE);
+}
+
+/*
+ * Determine if as1 is a valid prefix of as2.
+ * Return true if yep.
+ */
+int
+isprefix(as1, as2)
+ char *as1, *as2;
+{
+ register char *s1, *s2;
+
+ s1 = as1;
+ s2 = as2;
+ while (*s1++ == *s2)
+ if (*s2++ == '\0')
+ return(1);
+ return(*--s1 == '\0');
+}
+
+/*
+ * The following gets called on receipt of an interrupt. This is
+ * to abort printout of a command, mainly.
+ * Dispatching here when command() is inactive crashes rcv.
+ * Close all open files except 0, 1, 2, and the temporary.
+ * Also, unstack all source files.
+ */
+
+int inithdr; /* am printing startup headers */
+
+/*ARGSUSED*/
+void
+intr(s)
+ int s;
+{
+
+ noreset = 0;
+ if (!inithdr)
+ sawcom++;
+ inithdr = 0;
+ while (sourcing)
+ unstack();
+
+ close_all_files();
+
+ if (image >= 0) {
+ close(image);
+ image = -1;
+ }
+ fprintf(stderr, "Interrupt\n");
+ reset(0);
+}
+
+/*
+ * When we wake up after ^Z, reprint the prompt.
+ */
+void
+stop(s)
+ int s;
+{
+ sig_t old_action = signal(s, SIG_DFL);
+
+ sigsetmask(sigblock(0) & ~sigmask(s));
+ kill(0, s);
+ sigblock(sigmask(s));
+ signal(s, old_action);
+ if (reset_on_stop) {
+ reset_on_stop = 0;
+ reset(0);
+ }
+}
+
+/*
+ * Branch here on hangup signal and simulate "exit".
+ */
+/*ARGSUSED*/
+void
+hangup(s)
+ int s;
+{
+
+ /* nothing to do? */
+ exit(1);
+}
+
+/*
+ * Announce the presence of the current Mail version,
+ * give the message count, and print a header listing.
+ */
+void
+announce()
+{
+ int vec[2], mdot;
+
+ mdot = newfileinfo();
+ vec[0] = mdot;
+ vec[1] = 0;
+ dot = &message[mdot - 1];
+ if (msgCount > 0 && value("noheader") == NOSTR) {
+ inithdr++;
+ headers(vec);
+ inithdr = 0;
+ }
+}
+
+/*
+ * Announce information about the file we are editing.
+ * Return a likely place to set dot.
+ */
+int
+newfileinfo()
+{
+ register struct message *mp;
+ register int u, n, mdot, d, s;
+ char fname[BUFSIZ], zname[BUFSIZ], *ename;
+
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MNEW)
+ break;
+ if (mp >= &message[msgCount])
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & MREAD) == 0)
+ break;
+ if (mp < &message[msgCount])
+ mdot = mp - &message[0] + 1;
+ else
+ mdot = 1;
+ s = d = 0;
+ for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW)
+ n++;
+ if ((mp->m_flag & MREAD) == 0)
+ u++;
+ if (mp->m_flag & MDELETED)
+ d++;
+ if (mp->m_flag & MSAVED)
+ s++;
+ }
+ ename = mailname;
+ if (getfold(fname) >= 0) {
+ strcat(fname, "/");
+ if (strncmp(fname, mailname, strlen(fname)) == 0) {
+ sprintf(zname, "+%s", mailname + strlen(fname));
+ ename = zname;
+ }
+ }
+ printf("\"%s\": ", ename);
+ if (msgCount == 1)
+ printf("1 message");
+ else
+ printf("%d messages", msgCount);
+ if (n > 0)
+ printf(" %d new", n);
+ if (u-n > 0)
+ printf(" %d unread", u);
+ if (d > 0)
+ printf(" %d deleted", d);
+ if (s > 0)
+ printf(" %d saved", s);
+ if (readonly)
+ printf(" [Read only]");
+ printf("\n");
+ return(mdot);
+}
+
+/*
+ * Print the current version number.
+ */
+
+/*ARGSUSED*/
+int
+pversion(e)
+ int e;
+{
+ extern char *version;
+
+ printf("Version %s\n", version);
+ return(0);
+}
+
+/*
+ * Load a file of user definitions.
+ */
+void
+load(name)
+ char *name;
+{
+ register FILE *in, *oldin;
+
+ if ((in = Fopen(name, "r")) == NULL)
+ return;
+ oldin = input;
+ input = in;
+ loading = 1;
+ sourcing = 1;
+ commands();
+ loading = 0;
+ sourcing = 0;
+ input = oldin;
+ Fclose(in);
+}
diff --git a/usr.bin/mail/list.c b/usr.bin/mail/list.c
new file mode 100644
index 0000000..9759f2d
--- /dev/null
+++ b/usr.bin/mail/list.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)list.c 8.2 (Berkeley) 4/19/94";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <ctype.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Message list handling.
+ */
+
+/*
+ * Convert the user string of message numbers and
+ * store the numbers into vector.
+ *
+ * Returns the count of messages picked up or -1 on error.
+ */
+int
+getmsglist(buf, vector, flags)
+ char *buf;
+ int *vector, flags;
+{
+ register int *ip;
+ register struct message *mp;
+
+ if (msgCount == 0) {
+ *vector = 0;
+ return 0;
+ }
+ if (markall(buf, flags) < 0)
+ return(-1);
+ ip = vector;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MMARK)
+ *ip++ = mp - &message[0] + 1;
+ *ip = 0;
+ return(ip - vector);
+}
+
+/*
+ * Mark all messages that the user wanted from the command
+ * line in the message structure. Return 0 on success, -1
+ * on error.
+ */
+
+/*
+ * Bit values for colon modifiers.
+ */
+
+#define CMNEW 01 /* New messages */
+#define CMOLD 02 /* Old messages */
+#define CMUNREAD 04 /* Unread messages */
+#define CMDELETED 010 /* Deleted messages */
+#define CMREAD 020 /* Read messages */
+
+/*
+ * The following table describes the letters which can follow
+ * the colon and gives the corresponding modifier bit.
+ */
+
+struct coltab {
+ char co_char; /* What to find past : */
+ int co_bit; /* Associated modifier bit */
+ int co_mask; /* m_status bits to mask */
+ int co_equal; /* ... must equal this */
+} coltab[] = {
+ 'n', CMNEW, MNEW, MNEW,
+ 'o', CMOLD, MNEW, 0,
+ 'u', CMUNREAD, MREAD, 0,
+ 'd', CMDELETED, MDELETED, MDELETED,
+ 'r', CMREAD, MREAD, MREAD,
+ 0, 0, 0, 0
+};
+
+static int lastcolmod;
+
+int
+markall(buf, f)
+ char buf[];
+ int f;
+{
+ register char **np;
+ register int i;
+ register struct message *mp;
+ char *namelist[NMLSIZE], *bufp;
+ int tok, beg, mc, star, other, valdot, colmod, colresult;
+
+ valdot = dot - &message[0] + 1;
+ colmod = 0;
+ for (i = 1; i <= msgCount; i++)
+ unmark(i);
+ bufp = buf;
+ mc = 0;
+ np = &namelist[0];
+ scaninit();
+ tok = scan(&bufp);
+ star = 0;
+ other = 0;
+ beg = 0;
+ while (tok != TEOL) {
+ switch (tok) {
+ case TNUMBER:
+number:
+ if (star) {
+ printf("No numbers mixed with *\n");
+ return(-1);
+ }
+ mc++;
+ other++;
+ if (beg != 0) {
+ if (check(lexnumber, f))
+ return(-1);
+ for (i = beg; i <= lexnumber; i++)
+ if (f == MDELETED || (message[i - 1].m_flag & MDELETED) == 0)
+ mark(i);
+ beg = 0;
+ break;
+ }
+ beg = lexnumber;
+ if (check(beg, f))
+ return(-1);
+ tok = scan(&bufp);
+ regret(tok);
+ if (tok != TDASH) {
+ mark(beg);
+ beg = 0;
+ }
+ break;
+
+ case TPLUS:
+ if (beg != 0) {
+ printf("Non-numeric second argument\n");
+ return(-1);
+ }
+ i = valdot;
+ do {
+ i++;
+ if (i > msgCount) {
+ printf("Referencing beyond EOF\n");
+ return(-1);
+ }
+ } while ((message[i - 1].m_flag & MDELETED) != f);
+ mark(i);
+ break;
+
+ case TDASH:
+ if (beg == 0) {
+ i = valdot;
+ do {
+ i--;
+ if (i <= 0) {
+ printf("Referencing before 1\n");
+ return(-1);
+ }
+ } while ((message[i - 1].m_flag & MDELETED) != f);
+ mark(i);
+ }
+ break;
+
+ case TSTRING:
+ if (beg != 0) {
+ printf("Non-numeric second argument\n");
+ return(-1);
+ }
+ other++;
+ if (lexstring[0] == ':') {
+ colresult = evalcol(lexstring[1]);
+ if (colresult == 0) {
+ printf("Unknown colon modifier \"%s\"\n",
+ lexstring);
+ return(-1);
+ }
+ colmod |= colresult;
+ }
+ else
+ *np++ = savestr(lexstring);
+ break;
+
+ case TDOLLAR:
+ case TUP:
+ case TDOT:
+ lexnumber = metamess(lexstring[0], f);
+ if (lexnumber == -1)
+ return(-1);
+ goto number;
+
+ case TSTAR:
+ if (other) {
+ printf("Can't mix \"*\" with anything\n");
+ return(-1);
+ }
+ star++;
+ break;
+
+ case TERROR:
+ return -1;
+ }
+ tok = scan(&bufp);
+ }
+ lastcolmod = colmod;
+ *np = NOSTR;
+ mc = 0;
+ if (star) {
+ for (i = 0; i < msgCount; i++)
+ if ((message[i].m_flag & MDELETED) == f) {
+ mark(i+1);
+ mc++;
+ }
+ if (mc == 0) {
+ printf("No applicable messages.\n");
+ return(-1);
+ }
+ return(0);
+ }
+
+ /*
+ * If no numbers were given, mark all of the messages,
+ * so that we can unmark any whose sender was not selected
+ * if any user names were given.
+ */
+
+ if ((np > namelist || colmod != 0) && mc == 0)
+ for (i = 1; i <= msgCount; i++)
+ if ((message[i-1].m_flag & MDELETED) == f)
+ mark(i);
+
+ /*
+ * If any names were given, go through and eliminate any
+ * messages whose senders were not requested.
+ */
+
+ if (np > namelist) {
+ for (i = 1; i <= msgCount; i++) {
+ for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
+ if (**np == '/') {
+ if (matchsubj(*np, i)) {
+ mc++;
+ break;
+ }
+ }
+ else {
+ if (matchsender(*np, i)) {
+ mc++;
+ break;
+ }
+ }
+ if (mc == 0)
+ unmark(i);
+ }
+
+ /*
+ * Make sure we got some decent messages.
+ */
+
+ mc = 0;
+ for (i = 1; i <= msgCount; i++)
+ if (message[i-1].m_flag & MMARK) {
+ mc++;
+ break;
+ }
+ if (mc == 0) {
+ printf("No applicable messages from {%s",
+ namelist[0]);
+ for (np = &namelist[1]; *np != NOSTR; np++)
+ printf(", %s", *np);
+ printf("}\n");
+ return(-1);
+ }
+ }
+
+ /*
+ * If any colon modifiers were given, go through and
+ * unmark any messages which do not satisfy the modifiers.
+ */
+
+ if (colmod != 0) {
+ for (i = 1; i <= msgCount; i++) {
+ register struct coltab *colp;
+
+ mp = &message[i - 1];
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_bit & colmod)
+ if ((mp->m_flag & colp->co_mask)
+ != colp->co_equal)
+ unmark(i);
+
+ }
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MMARK)
+ break;
+ if (mp >= &message[msgCount]) {
+ register struct coltab *colp;
+
+ printf("No messages satisfy");
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_bit & colmod)
+ printf(" :%c", colp->co_char);
+ printf("\n");
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * Turn the character after a colon modifier into a bit
+ * value.
+ */
+int
+evalcol(col)
+ int col;
+{
+ register struct coltab *colp;
+
+ if (col == 0)
+ return(lastcolmod);
+ for (colp = &coltab[0]; colp->co_char; colp++)
+ if (colp->co_char == col)
+ return(colp->co_bit);
+ return(0);
+}
+
+/*
+ * Check the passed message number for legality and proper flags.
+ * If f is MDELETED, then either kind will do. Otherwise, the message
+ * has to be undeleted.
+ */
+int
+check(mesg, f)
+ int mesg, f;
+{
+ register struct message *mp;
+
+ if (mesg < 1 || mesg > msgCount) {
+ printf("%d: Invalid message number\n", mesg);
+ return(-1);
+ }
+ mp = &message[mesg-1];
+ if (f != MDELETED && (mp->m_flag & MDELETED) != 0) {
+ printf("%d: Inappropriate message\n", mesg);
+ return(-1);
+ }
+ return(0);
+}
+
+/*
+ * Scan out the list of string arguments, shell style
+ * for a RAWLIST.
+ */
+int
+getrawlist(line, argv, argc)
+ char line[];
+ char **argv;
+ int argc;
+{
+ register char c, *cp, *cp2, quotec;
+ int argn;
+ char linebuf[BUFSIZ];
+
+ argn = 0;
+ cp = line;
+ for (;;) {
+ for (; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ if (*cp == '\0')
+ break;
+ if (argn >= argc - 1) {
+ printf(
+ "Too many elements in the list; excess discarded.\n");
+ break;
+ }
+ cp2 = linebuf;
+ quotec = '\0';
+ while ((c = *cp) != '\0') {
+ cp++;
+ if (quotec != '\0') {
+ if (c == quotec)
+ quotec = '\0';
+ else if (c == '\\')
+ switch (c = *cp++) {
+ case '\0':
+ *cp2++ = '\\';
+ cp--;
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (*cp >= '0' && *cp <= '7')
+ c = c * 8 + *cp++ - '0';
+ if (*cp >= '0' && *cp <= '7')
+ c = c * 8 + *cp++ - '0';
+ *cp2++ = c;
+ break;
+ case 'b':
+ *cp2++ = '\b';
+ break;
+ case 'f':
+ *cp2++ = '\f';
+ break;
+ case 'n':
+ *cp2++ = '\n';
+ break;
+ case 'r':
+ *cp2++ = '\r';
+ break;
+ case 't':
+ *cp2++ = '\t';
+ break;
+ case 'v':
+ *cp2++ = '\v';
+ break;
+ default:
+ *cp2++ = c;
+ }
+ else if (c == '^') {
+ c = *cp++;
+ if (c == '?')
+ *cp2++ = '\177';
+ /* null doesn't show up anyway */
+ else if (c >= 'A' && c <= '_' ||
+ c >= 'a' && c <= 'z')
+ *cp2++ = c & 037;
+ else {
+ *cp2++ = '^';
+ cp--;
+ }
+ } else
+ *cp2++ = c;
+ } else if (c == '"' || c == '\'')
+ quotec = c;
+ else if (c == ' ' || c == '\t')
+ break;
+ else
+ *cp2++ = c;
+ }
+ *cp2 = '\0';
+ argv[argn++] = savestr(linebuf);
+ }
+ argv[argn] = NOSTR;
+ return argn;
+}
+
+/*
+ * scan out a single lexical item and return its token number,
+ * updating the string pointer passed **p. Also, store the value
+ * of the number or string scanned in lexnumber or lexstring as
+ * appropriate. In any event, store the scanned `thing' in lexstring.
+ */
+
+struct lex {
+ char l_char;
+ char l_token;
+} singles[] = {
+ '$', TDOLLAR,
+ '.', TDOT,
+ '^', TUP,
+ '*', TSTAR,
+ '-', TDASH,
+ '+', TPLUS,
+ '(', TOPEN,
+ ')', TCLOSE,
+ 0, 0
+};
+
+int
+scan(sp)
+ char **sp;
+{
+ register char *cp, *cp2;
+ register int c;
+ register struct lex *lp;
+ int quotec;
+
+ if (regretp >= 0) {
+ strcpy(lexstring, string_stack[regretp]);
+ lexnumber = numberstack[regretp];
+ return(regretstack[regretp--]);
+ }
+ cp = *sp;
+ cp2 = lexstring;
+ c = *cp++;
+
+ /*
+ * strip away leading white space.
+ */
+
+ while (c == ' ' || c == '\t')
+ c = *cp++;
+
+ /*
+ * If no characters remain, we are at end of line,
+ * so report that.
+ */
+
+ if (c == '\0') {
+ *sp = --cp;
+ return(TEOL);
+ }
+
+ /*
+ * If the leading character is a digit, scan
+ * the number and convert it on the fly.
+ * Return TNUMBER when done.
+ */
+
+ if (isdigit(c)) {
+ lexnumber = 0;
+ while (isdigit(c)) {
+ lexnumber = lexnumber*10 + c - '0';
+ *cp2++ = c;
+ c = *cp++;
+ }
+ *cp2 = '\0';
+ *sp = --cp;
+ return(TNUMBER);
+ }
+
+ /*
+ * Check for single character tokens; return such
+ * if found.
+ */
+
+ for (lp = &singles[0]; lp->l_char != 0; lp++)
+ if (c == lp->l_char) {
+ lexstring[0] = c;
+ lexstring[1] = '\0';
+ *sp = cp;
+ return(lp->l_token);
+ }
+
+ /*
+ * We've got a string! Copy all the characters
+ * of the string into lexstring, until we see
+ * a null, space, or tab.
+ * If the lead character is a " or ', save it
+ * and scan until you get another.
+ */
+
+ quotec = 0;
+ if (c == '\'' || c == '"') {
+ quotec = c;
+ c = *cp++;
+ }
+ while (c != '\0') {
+ if (c == quotec) {
+ cp++;
+ break;
+ }
+ if (quotec == 0 && (c == ' ' || c == '\t'))
+ break;
+ if (cp2 - lexstring < STRINGLEN-1)
+ *cp2++ = c;
+ c = *cp++;
+ }
+ if (quotec && c == 0) {
+ fprintf(stderr, "Missing %c\n", quotec);
+ return TERROR;
+ }
+ *sp = --cp;
+ *cp2 = '\0';
+ return(TSTRING);
+}
+
+/*
+ * Unscan the named token by pushing it onto the regret stack.
+ */
+void
+regret(token)
+ int token;
+{
+ if (++regretp >= REGDEP)
+ panic("Too many regrets");
+ regretstack[regretp] = token;
+ lexstring[STRINGLEN-1] = '\0';
+ string_stack[regretp] = savestr(lexstring);
+ numberstack[regretp] = lexnumber;
+}
+
+/*
+ * Reset all the scanner global variables.
+ */
+void
+scaninit()
+{
+ regretp = -1;
+}
+
+/*
+ * Find the first message whose flags & m == f and return
+ * its message number.
+ */
+int
+first(f, m)
+ int f, m;
+{
+ register struct message *mp;
+
+ if (msgCount == 0)
+ return 0;
+ f &= MDELETED;
+ m &= MDELETED;
+ for (mp = dot; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & m) == f)
+ return mp - message + 1;
+ for (mp = dot-1; mp >= &message[0]; mp--)
+ if ((mp->m_flag & m) == f)
+ return mp - message + 1;
+ return 0;
+}
+
+/*
+ * See if the passed name sent the passed message number. Return true
+ * if so.
+ */
+int
+matchsender(str, mesg)
+ char *str;
+ int mesg;
+{
+ register char *cp, *cp2, *backup;
+
+ if (!*str) /* null string matches nothing instead of everything */
+ return 0;
+ backup = cp2 = nameof(&message[mesg - 1], 0);
+ cp = str;
+ while (*cp2) {
+ if (*cp == 0)
+ return(1);
+ if (raise(*cp++) != raise(*cp2++)) {
+ cp2 = ++backup;
+ cp = str;
+ }
+ }
+ return(*cp == 0);
+}
+
+/*
+ * See if the given string matches inside the subject field of the
+ * given message. For the purpose of the scan, we ignore case differences.
+ * If it does, return true. The string search argument is assumed to
+ * have the form "/search-string." If it is of the form "/," we use the
+ * previous search string.
+ */
+
+char lastscan[128];
+int
+matchsubj(str, mesg)
+ char *str;
+ int mesg;
+{
+ register struct message *mp;
+ register char *cp, *cp2, *backup;
+
+ str++;
+ if (strlen(str) == 0)
+ str = lastscan;
+ else
+ strcpy(lastscan, str);
+ mp = &message[mesg-1];
+
+ /*
+ * Now look, ignoring case, for the word in the string.
+ */
+
+ if (value("searchheaders") && (cp = index(str, ':'))) {
+ *cp++ = '\0';
+ cp2 = hfield(str, mp);
+ cp[-1] = ':';
+ str = cp;
+ } else {
+ cp = str;
+ cp2 = hfield("subject", mp);
+ }
+ if (cp2 == NOSTR)
+ return(0);
+ backup = cp2;
+ while (*cp2) {
+ if (*cp == 0)
+ return(1);
+ if (raise(*cp++) != raise(*cp2++)) {
+ cp2 = ++backup;
+ cp = str;
+ }
+ }
+ return(*cp == 0);
+}
+
+/*
+ * Mark the named message by setting its mark bit.
+ */
+void
+mark(mesg)
+ int mesg;
+{
+ register int i;
+
+ i = mesg;
+ if (i < 1 || i > msgCount)
+ panic("Bad message number to mark");
+ message[i-1].m_flag |= MMARK;
+}
+
+/*
+ * Unmark the named message.
+ */
+void
+unmark(mesg)
+ int mesg;
+{
+ register int i;
+
+ i = mesg;
+ if (i < 1 || i > msgCount)
+ panic("Bad message number to unmark");
+ message[i-1].m_flag &= ~MMARK;
+}
+
+/*
+ * Return the message number corresponding to the passed meta character.
+ */
+int
+metamess(meta, f)
+ int meta, f;
+{
+ register int c, m;
+ register struct message *mp;
+
+ c = meta;
+ switch (c) {
+ case '^':
+ /*
+ * First 'good' message left.
+ */
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag & MDELETED) == f)
+ return(mp - &message[0] + 1);
+ printf("No applicable messages\n");
+ return(-1);
+
+ case '$':
+ /*
+ * Last 'good message left.
+ */
+ for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
+ if ((mp->m_flag & MDELETED) == f)
+ return(mp - &message[0] + 1);
+ printf("No applicable messages\n");
+ return(-1);
+
+ case '.':
+ /*
+ * Current message.
+ */
+ m = dot - &message[0] + 1;
+ if ((dot->m_flag & MDELETED) != f) {
+ printf("%d: Inappropriate message\n", m);
+ return(-1);
+ }
+ return(m);
+
+ default:
+ printf("Unknown metachar (%c)\n", c);
+ return(-1);
+ }
+}
diff --git a/usr.bin/mail/mail.1 b/usr.bin/mail/mail.1
new file mode 100644
index 0000000..376af05
--- /dev/null
+++ b/usr.bin/mail/mail.1
@@ -0,0 +1,1032 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mail.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt MAIL 1
+.Os BSD 4
+.Sh NAME
+.Nm mail
+.Nd send and receive mail
+.Sh SYNOPSIS
+.Nm mail
+.Op Fl iInv
+.Op Fl s Ar subject
+.Op Fl c Ar cc-addr
+.Op Fl b Ar bcc-addr
+.Ar to-addr...
+.Nm mail
+.Op Fl iInNv
+.Fl f
+.Op Ar name
+.Nm mail
+.Op Fl iInNv
+.Op Fl u Ar user
+.Sh INTRODUCTION
+.Nm Mail
+is an intelligent mail processing system, which has
+a command syntax reminiscent of
+.Xr \&ed 1
+with lines replaced by messages.
+.Pp
+.Bl -tag -width flag
+.It Fl v
+Verbose mode.
+The details of
+delivery are displayed on the user's terminal.
+.It Fl i
+Ignore tty interrupt signals.
+This is
+particularly useful when using
+.Nm mail
+on noisy phone lines.
+.It Fl I
+Forces mail to run in interactive mode even when
+input isn't a terminal.
+In particular, the
+.Sq Ic \&~
+special
+character when sending mail is only active in interactive mode.
+.It Fl n
+Inhibits reading
+.Pa /usr/share/misc/Mail.rc
+upon startup.
+.It Fl N
+Inhibits the initial display of message headers
+when reading mail or editing a mail folder.
+.It Fl s
+Specify subject on command line
+(only the first argument after the
+.Fl s
+flag is used as a subject; be careful to quote subjects
+containing spaces.)
+.It Fl c
+Send carbon copies to
+.Ar list
+of users.
+.It Fl b
+Send blind carbon copies to
+.Ar list .
+List should be a comma-separated list of names.
+.It Fl f
+Read in the contents of your
+.Ar mbox
+(or the specified file)
+for processing; when you
+.Ar quit ,
+.Nm mail
+writes undeleted messages back to this file.
+.It Fl u
+Is equivalent to:
+.Pp
+.Dl mail -f /var/spool/mail/user
+.El
+.Ss Sending mail
+To send a message to one or more people,
+.Nm mail
+can be invoked with arguments which are the names of people to
+whom the mail will be sent.
+You are then expected to type in
+your message, followed
+by an
+.Sq Li control\-D
+at the beginning of a line.
+The section below
+.Ar Replying to or originating mail ,
+describes some features of
+.Nm mail
+available to help you compose your letter.
+.Pp
+.Ss Reading mail
+In normal usage
+.Nm mail
+is given no arguments and checks your mail out of the
+post office, then
+prints out a one line header of each message found.
+The current message is initially the first message (numbered 1)
+and can be printed using the
+.Ic print
+command (which can be abbreviated
+.Ql Ic p ) .
+You can move among the messages much as you move between lines in
+.Xr \&ed 1 ,
+with the commands
+.Ql Ic \&+
+and
+.Ql Ic \&\-
+moving backwards and forwards, and
+simple numbers.
+.Pp
+.Ss Disposing of mail.
+After examining a message you can
+.Ic delete
+.Ql Ic d )
+the message or
+.Ic reply
+.Ql Ic r )
+to it.
+Deletion causes the
+.Nm mail
+program to forget about the message.
+This is not irreversible; the message can be
+.Ic undeleted
+.Ql Ic u )
+by giving its number, or the
+.Nm mail
+session can be aborted by giving the
+.Ic exit
+.Ql Ic x )
+command.
+Deleted messages will, however, usually disappear never to be seen again.
+.Pp
+.Ss Specifying messages
+Commands such as
+.Ic print
+and
+.Ic delete
+can be given a list of message numbers as arguments to apply
+to a number of messages at once.
+Thus
+.Dq Li delete 1 2
+deletes messages 1 and 2, while
+.Dq Li delete 1\-5
+deletes messages 1 through 5.
+The special name
+.Ql Li \&*
+addresses all messages, and
+.Ql Li \&$
+addresses
+the last message; thus the command
+.Ic top
+which prints the first few lines of a message could be used in
+.Dq Li top \&*
+to print the first few lines of all messages.
+.Pp
+.Ss Replying to or originating mail.
+You can use the
+.Ic reply
+command to
+set up a response to a message, sending it back to the
+person who it was from.
+Text you then type in, up to an end-of-file,
+defines the contents of the message.
+While you are composing a message,
+.Nm mail
+treats lines beginning with the character
+.Ql Ic \&~
+specially.
+For instance, typing
+.Ql Ic \&~m
+(alone on a line) will place a copy
+of the current message into the response right shifting it by a tabstop
+(see
+.Em indentprefix
+variable, below).
+Other escapes will set up subject fields, add and delete recipients
+to the message and allow you to escape to an editor to revise the
+message or to a shell to run some commands.
+(These options
+are given in the summary below.)
+.Pp
+.Ss Ending a mail processing session.
+You can end a
+.Nm mail
+session with the
+.Ic quit
+.Ql Ic q )
+command.
+Messages which have been examined go to your
+.Ar mbox
+file unless they have been deleted in which case they are discarded.
+Unexamined messages go back to the post office.
+(See the
+.Fl f
+option above).
+.Pp
+.Ss Personal and systemwide distribution lists.
+It is also possible to create a personal distribution lists so that,
+for instance, you can send mail to
+.Dq Li cohorts
+and have it go
+to a group of people.
+Such lists can be defined by placing a line like
+.Pp
+.Dl alias cohorts bill ozalp jkf mark kridle@ucbcory
+.Pp
+in the file
+.Pa \&.mailrc
+in your home directory.
+The current list of such aliases can be displayed with the
+.Ic alias
+command in
+.Nm mail .
+System wide distribution lists can be created by editing
+.Pa /etc/aliases ,
+see
+.Xr aliases 5
+and
+.Xr sendmail 8 ;
+these are kept in a different syntax.
+In mail you send, personal aliases will be expanded in mail sent
+to others so that they will be able to
+.Ic reply
+to the recipients.
+System wide
+.Ic aliases
+are not expanded when the mail is sent,
+but any reply returned to the machine will have the system wide
+alias expanded as all mail goes through
+.Xr sendmail .
+.Pp
+.Ss Network mail (ARPA, UUCP, Berknet)
+See
+.Xr mailaddr 7
+for a description of network addresses.
+.Pp
+.Nm Mail
+has a number of options which can be set in the
+.Pa .mailrc
+file to alter its behavior; thus
+.Dq Li set askcc
+enables the
+.Ar askcc
+feature.
+(These options are summarized below.)
+.Sh SUMMARY
+(Adapted from the `Mail Reference Manual')
+.Pp
+Each command is typed on a line by itself, and may take arguments
+following the command word.
+The command need not be typed in its
+entirety \- the first command which matches the typed prefix is used.
+For commands which take message lists as arguments, if no message
+list is given, then the next message forward which satisfies the
+command's requirements is used.
+If there are no messages forward of
+the current message, the search proceeds backwards, and if there are no
+good messages at all,
+.Nm mail
+types
+.Dq Li No applicable messages
+and
+aborts the command.
+.Bl -tag -width delete
+.It Ic \&\-
+Print out the preceding message.
+If given a numeric
+argument
+.Ar n ,
+goes to the
+.Ar n Ns 'th
+previous message and prints it.
+.It Ic \&#
+ignore the remainder of the line as a comment.
+.It Ic \&?
+Prints a brief summary of commands.
+.It Ic \&!
+Executes the shell
+(see
+.Xr sh 1
+and
+.Xr csh 1 )
+command which follows.
+.It Ic Print
+.Pq Ic P
+Like
+.Ic print
+but also prints out ignored header fields.
+See also
+.Ic print ,
+.Ic ignore
+and
+.Ic retain .
+.It Ic Reply
+.Pq Ic R
+Reply to originator.
+Does not reply to other
+recipients of the original message.
+.It Ic Type
+.Pq Ic T
+Identical to the
+.Ic Print
+command.
+.It Ic alias
+.Pq Ic a
+With no arguments, prints out all currently-defined aliases.
+With one
+argument, prints out that alias.
+With more than one argument, creates
+a new alias or changes an old one.
+.It Ic alternates
+.Pq Ic alt
+The
+.Ic alternates
+command is useful if you have accounts on several machines.
+It can be used to inform
+.Nm mail
+that the listed addresses are really you.
+When you
+.Ic reply
+to messages,
+.Nm mail
+will not send a copy of the message to any of the addresses
+listed on the
+.Ic alternates
+list.
+If the
+.Ic alternates
+command is given with no argument, the current set of alternate
+names is displayed.
+.It Ic chdir
+.Pq Ic c
+Changes the user's working directory to that specified, if given.
+If
+no directory is given, then changes to the user's login directory.
+.It Ic copy
+.Pq Ic co
+The
+.Ic copy
+command does the same thing that
+.Ic save
+does, except that it does not mark the messages it
+is used on for deletion when you quit.
+.It Ic delete
+.Pq Ic d
+Takes a list of messages as argument and marks them all as deleted.
+Deleted messages will not be saved in
+.Ar mbox ,
+nor will they be available for most other commands.
+.It Ic dp
+(also
+.Ic dt )
+Deletes the current message and prints the next message.
+If there is no next message,
+.Nm mail
+says
+.Dq Li "at EOF" .
+.It Ic edit
+.Pq Ic e
+Takes a list of messages and points the text editor at each one in
+turn.
+On return from the editor, the message is read back in.
+.It Ic exit
+.Pf ( Ic ex
+or
+.Ic x )
+Effects an immediate return to the Shell without
+modifying the user's system mailbox, his
+.Ar mbox
+file, or his edit file in
+.Fl f .
+.It Ic file
+.Pq Ic fi
+The same as
+.Ic folder .
+.It Ic folders
+List the names of the folders in your folder directory.
+.It Ic folder
+.Pq Ic fo
+The
+.Ic folder
+command switches to a new mail file or folder.
+With no
+arguments, it tells you which file you are currently reading.
+If you give it an argument, it will write out changes (such
+as deletions) you have made in the current file and read in
+the new file.
+Some special conventions are recognized for
+the name.
+# means the previous file, % means your system
+mailbox, %user means user's system mailbox, & means
+your
+.Ar mbox
+file, and
+\&+\&folder means a file in your folder
+directory.
+.It Ic from
+.Pq Ic f
+Takes a list of messages and prints their message headers.
+.It Ic headers
+.Pq Ic h
+Lists the current range of headers, which is an 18\-message group.
+If
+a
+.Ql \&+
+argument is given, then the next 18\-message group is printed, and if
+a
+.Ql \&\-
+argument is given, the previous 18\-message group is printed.
+.It Ic help
+A synonym for
+.Ic \&?
+.It Ic hold
+.Pf ( Ic ho ,
+also
+.Ic preserve )
+Takes a message list and marks each
+message therein to be saved in the
+user's system mailbox instead of in
+.Ar mbox .
+Does not override the
+.Ic delete
+command.
+.It Ic ignore
+Add the list of header fields named to the
+.Ar ignored list .
+Header fields in the ignore list are not printed
+on your terminal when you print a message.
+This
+command is very handy for suppression of certain machine-generated
+header fields.
+The
+.Ic Type
+and
+.Ic Print
+commands can be used to print a message in its entirety, including
+ignored fields.
+If
+.Ic ignore
+is executed with no arguments, it lists the current set of
+ignored fields.
+.It Ic mail
+.Pq Ic m
+Takes as argument login names and distribution group names and sends
+mail to those people.
+.It Ic mbox
+Indicate that a list of messages be sent to
+.Ic mbox
+in your home directory when you quit.
+This is the default
+action for messages if you do
+.Em not
+have the
+.Ic hold
+option set.
+.It Ic next
+.Pq Ic n
+like
+.Ic \&+
+or
+.Tn CR )
+Goes to the next message in sequence and types it.
+With an argument list, types the next matching message.
+.It Ic preserve
+.Pq Ic pre
+A synonym for
+.Ic hold .
+.It Ic print
+.Pq Ic p
+Takes a message list and types out each message on the user's terminal.
+.It Ic quit
+.Pq Ic q
+Terminates the session, saving all undeleted, unsaved messages in
+the user's
+.Ar mbox
+file in his login directory, preserving all messages marked with
+.Ic hold
+or
+.Ic preserve
+or never referenced
+in his system mailbox, and removing all other messages from his system
+mailbox.
+If new mail has arrived during the session, the message
+.Dq Li "You have new mail"
+is given.
+If given while editing a
+mailbox file with the
+.Fl f
+flag, then the edit file is rewritten.
+A return to the Shell is
+effected, unless the rewrite of edit file fails, in which case the user
+can escape with the
+.Ic exit
+command.
+.It Ic reply
+.Pq Ic r
+Takes a message list and sends mail to the sender and all
+recipients of the specified message.
+The default message must not be deleted.
+.It Ic respond
+A synonym for
+.Ic reply .
+.It Ic retain
+Add the list of header fields named to the
+.Ar retained list
+Only the header fields in the retain list
+are shown on your terminal when you print a message.
+All other header fields are suppressed.
+The
+.Ic Type
+and
+.Ic Print
+commands can be used to print a message in its entirety.
+If
+.Ic retain
+is executed with no arguments, it lists the current set of
+retained fields.
+.It Ic save
+.Pq Ic s
+Takes a message list and a filename and appends each message in
+turn to the end of the file.
+The filename in quotes, followed by the line
+count and character count is echoed on the user's terminal.
+.It Ic set
+.Pq Ic se
+With no arguments, prints all variable values.
+Otherwise, sets
+option.
+Arguments are of the form
+.Ar option=value
+(no space before or after =) or
+.Ar option .
+Quotation marks may be placed around any part of the assignment statement to
+quote blanks or tabs, i.e.
+.Dq Li "set indentprefix=\*q->\*q"
+.It Ic saveignore
+.Ic Saveignore
+is to
+.Ic save
+what
+.Ic ignore
+is to
+.Ic print
+and
+.Ic type .
+Header fields thus marked are filtered out when
+saving a message by
+.Ic save
+or when automatically saving to
+.Ar mbox .
+.It Ic saveretain
+.Ic Saveretain
+is to
+.Ic save
+what
+.Ic retain
+is to
+.Ic print
+and
+.Ic type .
+Header fields thus marked are the only ones saved
+with a message when saving by
+.Ic save
+or when automatically saving to
+.Ar mbox .
+.Ic Saveretain
+overrides
+.Ic saveignore .
+.It Ic shell
+.Pq Ic sh
+Invokes an interactive version of the shell.
+.It Ic size
+Takes a message list and prints out the size in characters of each
+message.
+.It Ic source
+The
+.Ic source
+command reads
+commands from a file.
+.It Ic top
+Takes a message list and prints the top few lines of each.
+The number of
+lines printed is controlled by the variable
+.Ic toplines
+and defaults to five.
+.It Ic type
+.Pq Ic t
+A synonym for
+.Ic print .
+.It Ic unalias
+Takes a list of names defined by
+.Ic alias
+commands and discards the remembered groups of users.
+The group names
+no longer have any significance.
+.It Ic undelete
+.Pq Ic u
+Takes a message list and marks each message as
+.Ic not
+being deleted.
+.It Ic unread
+.Pq Ic U
+Takes a message list and marks each message as
+.Ic not
+having been read.
+.It Ic unset
+Takes a list of option names and discards their remembered values;
+the inverse of
+.Ic set .
+.It Ic visual
+.Pq Ic v
+Takes a message list and invokes the display editor on each message.
+.It Ic write
+.Pq Ic w
+Similar to
+.Ic save ,
+except that
+.Ic only
+the message body
+.Pq Ar without
+the header) is saved.
+Extremely useful for such tasks as sending and receiving source
+program text over the message system.
+.It Ic xit
+.Pq Ic x
+A synonym for
+.Ic exit .
+.It Ic z
+.Nm Mail
+presents message headers in windowfuls as described under the
+.Ic headers
+command.
+You can move
+.Nm mail Ns 's
+attention forward to the next window with the
+.Ic \&z
+command.
+Also, you can move to the previous window by using
+.Ic \&z\&\- .
+.El
+.Ss Tilde/Escapes
+.Pp
+Here is a summary of the tilde escapes,
+which are used when composing messages to perform
+special functions.
+Tilde escapes are only recognized at the beginning
+of lines.
+The name
+.Dq Em tilde\ escape
+is somewhat of a misnomer since the actual escape character can be set
+by the option
+.Ic escape .
+.Bl -tag -width Ds
+.It Ic \&~! Ns Ar command
+Execute the indicated shell command, then return to the message.
+.It Ic \&~b Ns Ar name ...
+Add the given names to the list of carbon copy recipients but do not make
+the names visible in the Cc: line ("blind" carbon copy).
+.It Ic \&~c Ns Ar name ...
+Add the given names to the list of carbon copy recipients.
+.It Ic \&~d
+Read the file
+.Dq Pa dead.letter
+from your home directory into the message.
+.It Ic \&~e
+Invoke the text editor on the message collected so far.
+After the
+editing session is finished, you may continue appending text to the
+message.
+.It Ic \&~f Ns Ar messages
+Read the named messages into the message being sent.
+If no messages are specified, read in the current message.
+Message headers currently being ignored (by the
+.Ic ignore
+or
+.Ic retain
+command) are not included.
+.It Ic \&~F Ns Ar messages
+Identical to
+.Ic \&~f ,
+except all message headers are included.
+.It Ic \&~h
+Edit the message header fields by typing each one in turn and allowing
+the user to append text to the end or modify the field by using the
+current terminal erase and kill characters.
+.It Ic \&~m Ns Ar messages
+Read the named messages into the message being sent, indented by a
+tab or by the value of
+.Ar indentprefix .
+If no messages are specified,
+read the current message.
+Message headers currently being ignored (by the
+.Ic ignore
+or
+.Ic retain
+command) are not included.
+.It Ic \&~M Ns Ar messages
+Identical to
+.Ic \&~m ,
+except all message headers are included.
+.It Ic \&~p
+Print out the message collected so far, prefaced by the message header
+fields.
+.It Ic \&~q
+Abort the message being sent, copying the message to
+.Dq Pa dead.letter
+in your home directory if
+.Ic save
+is set.
+.It Ic \&~r Ns Ar filename
+Read the named file into the message.
+.It Ic \&~s Ns Ar string
+Cause the named string to become the current subject field.
+.It Ic \&~\&t Ns Ar name ...
+Add the given names to the direct recipient list.
+.It Ic \&~\&v
+Invoke an alternate editor (defined by the
+.Ev VISUAL
+option) on the
+message collected so far.
+Usually, the alternate editor will be a
+screen editor.
+After you quit the editor, you may resume appending
+text to the end of your message.
+.It Ic \&~w Ns Ar filename
+Write the message onto the named file.
+.It Ic \&~\&| Ns Ar command
+Pipe the message through the command as a filter.
+If the command gives
+no output or terminates abnormally, retain the original text of the
+message.
+The command
+.Xr fmt 1
+is often used as
+.Ic command
+to rejustify the message.
+.It Ic \&~: Ns Ar mail-command
+Execute the given mail command.
+Not all commands, however, are allowed.
+.It Ic \&~~ Ns Ar string
+Insert the string of text in the message prefaced by a single ~.
+If
+you have changed the escape character, then you should double
+that character in order to send it.
+.El
+.Ss Mail Options
+Options are controlled via
+.Ic set
+and
+.Ic unset
+commands.
+Options may be either binary, in which case it is only
+significant to see whether they are set or not; or string, in which
+case the actual value is of interest.
+The binary options include the following:
+.Bl -tag -width append
+.It Ar append
+Causes messages saved in
+.Ar mbox
+to be appended to the end rather than prepended.
+This should always be set (perhaps in
+.Pa /usr/share/misc/Mail.rc ) .
+.It Ar ask
+Causes
+.Nm mail
+to prompt you for the subject of each message you send.
+If
+you respond with simply a newline, no subject field will be sent.
+.It Ar askcc
+Causes you to be prompted for additional carbon copy recipients at the
+end of each message.
+Responding with a newline indicates your
+satisfaction with the current list.
+.It Ar autoprint
+Causes the
+.Ic delete
+command to behave like
+.Ic dp
+\- thus, after deleting a message, the next one will be typed
+automatically.
+.It Ar debug
+Setting the binary option
+.Ar debug
+is the same as specifying
+.Fl d
+on the command line and causes
+.Nm mail
+to output all sorts of information useful for debugging
+.Nm mail .
+.It Ar dot
+The binary option
+.Ar dot
+causes
+.Nm mail
+to interpret a period alone on a line as the terminator
+of a message you are sending.
+.It Ar hold
+This option is used to hold messages in the system mailbox
+by default.
+.It Ar ignore
+Causes interrupt signals from your terminal to be ignored and echoed as
+@'s.
+.It Ar ignoreeof
+An option related to
+.Ar dot
+is
+.Ar ignoreeof
+which makes
+.Nm mail
+refuse to accept a control-d as the end of a message.
+.Ar Ignoreeof
+also applies to
+.Nm mail
+command mode.
+.It Ar metoo
+Usually, when a group is expanded that contains the sender, the sender
+is removed from the expansion.
+Setting this option causes the sender
+to be included in the group.
+.It Ar noheader
+Setting the option
+.Ar noheader
+is the same as giving the
+.Fl N
+flag on the command line.
+.It Ar nosave
+Normally, when you abort a message with two
+.Tn RUBOUT
+(erase or delete)
+.Nm mail
+copies the partial letter to the file
+.Dq Pa dead.letter
+in your home directory.
+Setting the binary option
+.Ar nosave
+prevents this.
+.It Ar Replyall
+Reverses the sense of
+.Ic reply
+and
+.Ic Reply
+commands.
+.It Ar quiet
+Suppresses the printing of the version when first invoked.
+.It Ar searchheaders
+If this option is set, then a message-list specifier in the form ``/x:y''
+will expand to all messages containing the substring ``y'' in the header
+field ``x''. The string search is case insensitive.
+.It Ar verbose
+Setting the option
+.Ar verbose
+is the same as using the
+.Fl v
+flag on the command line.
+When mail runs in verbose mode,
+the actual delivery of messages is displayed on the user's
+terminal.
+.El
+.Ss Option String Values
+.Bl -tag -width Va
+.It Ev EDITOR
+Pathname of the text editor to use in the
+.Ic edit
+command and
+.Ic \&~e
+escape.
+If not defined, then a default editor is used.
+.It Ev LISTER
+Pathname of the directory lister to use in the
+.Ic folders
+command.
+Default is
+.Pa /bin/ls .
+.It Ev PAGER
+Pathname of the program to use in the
+.Ic more
+command or when
+.Ic crt
+variable is set.
+The default paginator
+.Xr more 1
+is used if this option is not defined.
+.It Ev SHELL
+Pathname of the shell to use in the
+.Ic \&!
+command and the
+.Ic \&~!
+escape.
+A default shell is used if this option is
+not defined.
+.It Ev VISUAL
+Pathname of the text editor to use in the
+.Ic visual
+command and
+.Ic \&~v
+escape.
+.It Va crt
+The valued option
+.Va crt
+is used as a threshold to determine how long a message must
+be before
+.Ev PAGER
+is used to read it.
+If
+.Va crt
+is set without a value,
+then the height of the terminal screen stored in the system
+is used to compute the threshold (see
+.Xr stty 1 ) .
+.It Ar escape
+If defined, the first character of this option gives the character to
+use in the place of ~ to denote escapes.
+.It Ar folder
+The name of the directory to use for storing folders of
+messages.
+If this name begins with a `/',
+.Nm mail
+considers it to be an absolute pathname; otherwise, the
+folder directory is found relative to your home directory.
+.It Ev MBOX
+The name of the
+.Ar mbox
+file.
+It can be the name of a folder.
+The default is
+.Dq Li mbox
+in the user's home directory.
+.It Ar record
+If defined, gives the pathname of the file used to record all outgoing
+mail.
+If not defined, then outgoing mail is not so saved.
+.It Ar indentprefix
+String used by the ``~m'' tilde escape for indenting messages, in place of
+the normal tab character (^I).
+Be sure to quote the value if it contains
+spaces or tabs.
+.It Ar toplines
+If defined, gives the number of lines of a message to be printed out
+with the
+.Ic top
+command; normally, the first five lines are printed.
+.El
+.Sh ENVIRONMENT
+.Nm Mail
+utilizes the
+.Ev HOME
+and
+.Ev USER
+environment variables.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/Mail.help* -compact
+.It Pa /var/spool/mail/*
+Post office.
+.It ~/mbox
+User's old mail.
+.It ~/.mailrc
+File giving initial mail commands.
+.It Pa /tmp/R*
+Temporary files.
+.It Pa /usr/share/misc/Mail.help*
+Help files.
+.It Pa /usr/share/misc/Mail.rc
+System initialization file.
+.El
+.Sh SEE ALSO
+.Xr fmt 1 ,
+.Xr newaliases 1 ,
+.Xr vacation 1 ,
+.Xr aliases 5 ,
+.Xr mailaddr 7 ,
+.Xr sendmail 8
+and
+.Rs
+.%T "The Mail Reference Manual" .
+.Re
+.Sh HISTORY
+A
+.Nm mail
+command
+appeared in
+.At v6 .
+This man page is derived from
+.%T "The Mail Reference Manual"
+originally written by Kurt Shoens.
+.Sh BUGS
+There are some flags that are not documented here.
+Most are
+not useful to the general user.
+.Pp
+Usually,
+.Nm mail
+is just a link to
+.Nm Mail ,
+which can be confusing.
diff --git a/usr.bin/mail/main.c b/usr.bin/mail/main.c
new file mode 100644
index 0000000..1e1579b
--- /dev/null
+++ b/usr.bin/mail/main.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Startup -- interface with user.
+ */
+
+jmp_buf hdrjmp;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ struct name *to, *cc, *bcc, *smopts;
+ char *subject;
+ char *ef;
+ char nosrc = 0;
+ void hdrstop();
+ sig_t prevint;
+ void sigchild();
+
+ /*
+ * Set up a reasonable environment.
+ * Figure out whether we are being run interactively,
+ * start the SIGCHLD catcher, and so forth.
+ */
+ (void) signal(SIGCHLD, sigchild);
+ if (isatty(0))
+ assign("interactive", "");
+ image = -1;
+ /*
+ * Now, determine how we are being used.
+ * We successively pick off - flags.
+ * If there is anything left, it is the base of the list
+ * of users to mail to. Argp will be set to point to the
+ * first of these users.
+ */
+ ef = NOSTR;
+ to = NIL;
+ cc = NIL;
+ bcc = NIL;
+ smopts = NIL;
+ subject = NOSTR;
+ while ((i = getopt(argc, argv, "INT:b:c:dfins:u:v")) != EOF) {
+ switch (i) {
+ case 'T':
+ /*
+ * Next argument is temp file to write which
+ * articles have been read/deleted for netnews.
+ */
+ Tflag = optarg;
+ if ((i = creat(Tflag, 0600)) < 0) {
+ perror(Tflag);
+ exit(1);
+ }
+ close(i);
+ break;
+ case 'u':
+ /*
+ * Next argument is person to pretend to be.
+ */
+ myname = optarg;
+ break;
+ case 'i':
+ /*
+ * User wants to ignore interrupts.
+ * Set the variable "ignore"
+ */
+ assign("ignore", "");
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 's':
+ /*
+ * Give a subject field for sending from
+ * non terminal
+ */
+ subject = optarg;
+ break;
+ case 'f':
+ /*
+ * User is specifying file to "edit" with Mail,
+ * as opposed to reading system mailbox.
+ * If no argument is given after -f, we read his
+ * mbox file.
+ *
+ * getopt() can't handle optional arguments, so here
+ * is an ugly hack to get around it.
+ */
+ if ((argv[optind]) && (argv[optind][0] != '-'))
+ ef = argv[optind++];
+ else
+ ef = "&";
+ break;
+ case 'n':
+ /*
+ * User doesn't want to source /usr/lib/Mail.rc
+ */
+ nosrc++;
+ break;
+ case 'N':
+ /*
+ * Avoid initial header printing.
+ */
+ assign("noheader", "");
+ break;
+ case 'v':
+ /*
+ * Send mailer verbose flag
+ */
+ assign("verbose", "");
+ break;
+ case 'I':
+ /*
+ * We're interactive
+ */
+ assign("interactive", "");
+ break;
+ case 'c':
+ /*
+ * Get Carbon Copy Recipient list
+ */
+ cc = cat(cc, nalloc(optarg, GCC));
+ break;
+ case 'b':
+ /*
+ * Get Blind Carbon Copy Recipient list
+ */
+ bcc = cat(bcc, nalloc(optarg, GBCC));
+ break;
+ case '?':
+ fputs("\
+Usage: mail [-iInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
+ [- sendmail-options ...]\n\
+ mail [-iInNv] -f [name]\n\
+ mail [-iInNv] [-u user]\n",
+ stderr);
+ exit(1);
+ }
+ }
+ for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
+ to = cat(to, nalloc(argv[i], GTO));
+ for (; argv[i]; i++)
+ smopts = cat(smopts, nalloc(argv[i], 0));
+ /*
+ * Check for inconsistent arguments.
+ */
+ if (to == NIL && (subject != NOSTR || cc != NIL || bcc != NIL)) {
+ fputs("You must specify direct recipients with -s, -c, or -b.\n", stderr);
+ exit(1);
+ }
+ if (ef != NOSTR && to != NIL) {
+ fprintf(stderr, "Cannot give -f and people to send to.\n");
+ exit(1);
+ }
+ tinit();
+ setscreensize();
+ input = stdin;
+ rcvmode = !to;
+ spreserve();
+ if (!nosrc)
+ load(_PATH_MASTER_RC);
+ /*
+ * Expand returns a savestr, but load only uses the file name
+ * for fopen, so it's safe to do this.
+ */
+ load(expand("~/.mailrc"));
+ if (!rcvmode) {
+ mail(to, cc, bcc, smopts, subject);
+ /*
+ * why wait?
+ */
+ exit(senderr);
+ }
+ /*
+ * Ok, we are reading mail.
+ * Decide whether we are editing a mailbox or reading
+ * the system mailbox, and open up the right stuff.
+ */
+ if (ef == NOSTR)
+ ef = "%";
+ if (setfile(ef) < 0)
+ exit(1); /* error already reported */
+ if (setjmp(hdrjmp) == 0) {
+ extern char *version;
+
+ if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+ signal(SIGINT, hdrstop);
+ if (value("quiet") == NOSTR)
+ printf("Mail version %s. Type ? for help.\n",
+ version);
+ announce();
+ fflush(stdout);
+ signal(SIGINT, prevint);
+ }
+ commands();
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ quit();
+ exit(0);
+}
+
+/*
+ * Interrupt printing of the headers.
+ */
+void
+hdrstop(signo)
+ int signo;
+{
+
+ fflush(stdout);
+ fprintf(stderr, "\nInterrupt\n");
+ longjmp(hdrjmp, 1);
+}
+
+/*
+ * Compute what the screen size for printing headers should be.
+ * We use the following algorithm for the height:
+ * If baud rate < 1200, use 9
+ * If baud rate = 1200, use 14
+ * If baud rate > 1200, use 24 or ws_row
+ * Width is either 80 or ws_col;
+ */
+void
+setscreensize()
+{
+ struct sgttyb tbuf;
+ struct winsize ws;
+
+ if (ioctl(1, TIOCGWINSZ, (char *) &ws) < 0)
+ ws.ws_col = ws.ws_row = 0;
+ if (ioctl(1, TIOCGETP, &tbuf) < 0)
+ tbuf.sg_ospeed = B9600;
+ if (tbuf.sg_ospeed < B1200)
+ screenheight = 9;
+ else if (tbuf.sg_ospeed == B1200)
+ screenheight = 14;
+ else if (ws.ws_row != 0)
+ screenheight = ws.ws_row;
+ else
+ screenheight = 24;
+ if ((realscreenheight = ws.ws_row) == 0)
+ realscreenheight = 24;
+ if ((screenwidth = ws.ws_col) == 0)
+ screenwidth = 80;
+}
diff --git a/usr.bin/mail/misc/mail.help b/usr.bin/mail/misc/mail.help
new file mode 100644
index 0000000..d5858c5
--- /dev/null
+++ b/usr.bin/mail/misc/mail.help
@@ -0,0 +1,23 @@
+ Mail Commands
+t <message list> type messages
+n goto and type next message
+e <message list> edit messages
+f <message list> give head lines of messages
+d <message list> delete messages
+s <message list> file append messages to file
+u <message list> undelete messages
+R <message list> reply to message senders
+r <message list> reply to message senders and all recipients
+pre <message list> make messages go back to /usr/spool/mail
+m <user list> mail to specific users
+q quit, saving unresolved messages in mbox
+x quit, do not remove system mailbox
+h print out active message headers
+! shell escape
+cd [directory] chdir to directory or home if none given
+
+A <message list> consists of integers, ranges of same, or user names separated
+by spaces. If omitted, Mail uses the last message typed.
+
+A <user list> consists of user names or aliases separated by spaces.
+Aliases are defined in .mailrc in your home directory.
diff --git a/usr.bin/mail/misc/mail.rc b/usr.bin/mail/misc/mail.rc
new file mode 100644
index 0000000..21922d7
--- /dev/null
+++ b/usr.bin/mail/misc/mail.rc
@@ -0,0 +1,2 @@
+set append dot save ask
+ignore Received Message-Id Resent-Message-Id Status Mail-From Return-Path Via
diff --git a/usr.bin/mail/misc/mail.tildehelp b/usr.bin/mail/misc/mail.tildehelp
new file mode 100644
index 0000000..0b1ab13
--- /dev/null
+++ b/usr.bin/mail/misc/mail.tildehelp
@@ -0,0 +1,22 @@
+-----------------------------------------------------------
+The following ~ escapes are defined:
+~~ Quote a single tilde
+~b users Add users to "blind" cc list
+~c users Add users to cc list
+~d Read in dead.letter
+~e Edit the message buffer
+~f messages Read in messages
+~F messages Same as ~f, but keep all header lines
+~h Prompt for to list, subject and cc list
+~r file Read a file into the message buffer
+~p Print the message buffer
+~m messages Read in messages, right shifted by a tab
+~M messages Same as ~m, but keep all header lines
+~s subject Set subject
+~t users Add users to to list
+~v Invoke display editor on message
+~w file Write message onto file.
+~? Print this message
+~!command Invoke the shell
+~|command Pipe the message through the command
+-----------------------------------------------------------
diff --git a/usr.bin/mail/names.c b/usr.bin/mail/names.c
new file mode 100644
index 0000000..e17e9f2
--- /dev/null
+++ b/usr.bin/mail/names.c
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Handle name lists.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Allocate a single element of a name list,
+ * initialize its name field to the passed
+ * name and return it.
+ */
+struct name *
+nalloc(str, ntype)
+ char str[];
+ int ntype;
+{
+ register struct name *np;
+
+ np = (struct name *) salloc(sizeof *np);
+ np->n_flink = NIL;
+ np->n_blink = NIL;
+ np->n_type = ntype;
+ np->n_name = savestr(str);
+ return(np);
+}
+
+/*
+ * Find the tail of a list and return it.
+ */
+struct name *
+tailof(name)
+ struct name *name;
+{
+ register struct name *np;
+
+ np = name;
+ if (np == NIL)
+ return(NIL);
+ while (np->n_flink != NIL)
+ np = np->n_flink;
+ return(np);
+}
+
+/*
+ * Extract a list of names from a line,
+ * and make a list of names from it.
+ * Return the list or NIL if none found.
+ */
+struct name *
+extract(line, ntype)
+ char line[];
+ int ntype;
+{
+ register char *cp;
+ register struct name *top, *np, *t;
+ char nbuf[BUFSIZ];
+
+ if (line == NOSTR || *line == '\0')
+ return NIL;
+ top = NIL;
+ np = NIL;
+ cp = line;
+ while ((cp = yankword(cp, nbuf)) != NOSTR) {
+ t = nalloc(nbuf, ntype);
+ if (top == NIL)
+ top = t;
+ else
+ np->n_flink = t;
+ t->n_blink = np;
+ np = t;
+ }
+ return top;
+}
+
+/*
+ * Turn a list of names into a string of the same names.
+ */
+char *
+detract(np, ntype)
+ register struct name *np;
+ int ntype;
+{
+ register int s;
+ register char *cp, *top;
+ register struct name *p;
+ register int comma;
+
+ comma = ntype & GCOMMA;
+ if (np == NIL)
+ return(NOSTR);
+ ntype &= ~GCOMMA;
+ s = 0;
+ if (debug && comma)
+ fprintf(stderr, "detract asked to insert commas\n");
+ for (p = np; p != NIL; p = p->n_flink) {
+ if (ntype && (p->n_type & GMASK) != ntype)
+ continue;
+ s += strlen(p->n_name) + 1;
+ if (comma)
+ s++;
+ }
+ if (s == 0)
+ return(NOSTR);
+ s += 2;
+ top = salloc(s);
+ cp = top;
+ for (p = np; p != NIL; p = p->n_flink) {
+ if (ntype && (p->n_type & GMASK) != ntype)
+ continue;
+ cp = copy(p->n_name, cp);
+ if (comma && p->n_flink != NIL)
+ *cp++ = ',';
+ *cp++ = ' ';
+ }
+ *--cp = 0;
+ if (comma && *--cp == ',')
+ *cp = 0;
+ return(top);
+}
+
+/*
+ * Grab a single word (liberal word)
+ * Throw away things between ()'s, and take anything between <>.
+ */
+char *
+yankword(ap, wbuf)
+ char *ap, wbuf[];
+{
+ register char *cp, *cp2;
+
+ cp = ap;
+ for (;;) {
+ if (*cp == '\0')
+ return NOSTR;
+ if (*cp == '(') {
+ register int nesting = 0;
+
+ while (*cp != '\0') {
+ switch (*cp++) {
+ case '(':
+ nesting++;
+ break;
+ case ')':
+ --nesting;
+ break;
+ }
+ if (nesting <= 0)
+ break;
+ }
+ } else if (*cp == ' ' || *cp == '\t' || *cp == ',')
+ cp++;
+ else
+ break;
+ }
+ if (*cp == '<')
+ for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
+ ;
+ else
+ for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
+ ;
+ *cp2 = '\0';
+ return cp;
+}
+
+/*
+ * For each recipient in the passed name list with a /
+ * in the name, append the message to the end of the named file
+ * and remove him from the recipient list.
+ *
+ * Recipients whose name begins with | are piped through the given
+ * program and removed.
+ */
+struct name *
+outof(names, fo, hp)
+ struct name *names;
+ FILE *fo;
+ struct header *hp;
+{
+ register int c;
+ register struct name *np, *top;
+ time_t now, time();
+ char *date, *fname, *ctime();
+ FILE *fout, *fin;
+ int ispipe;
+ extern char tempEdit[];
+
+ top = names;
+ np = names;
+ (void) time(&now);
+ date = ctime(&now);
+ while (np != NIL) {
+ if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
+ np = np->n_flink;
+ continue;
+ }
+ ispipe = np->n_name[0] == '|';
+ if (ispipe)
+ fname = np->n_name+1;
+ else
+ fname = expand(np->n_name);
+
+ /*
+ * See if we have copied the complete message out yet.
+ * If not, do so.
+ */
+
+ if (image < 0) {
+ if ((fout = Fopen(tempEdit, "a")) == NULL) {
+ perror(tempEdit);
+ senderr++;
+ goto cant;
+ }
+ image = open(tempEdit, 2);
+ (void) unlink(tempEdit);
+ if (image < 0) {
+ perror(tempEdit);
+ senderr++;
+ (void) Fclose(fout);
+ goto cant;
+ }
+ (void) fcntl(image, F_SETFD, 1);
+ fprintf(fout, "From %s %s", myname, date);
+ puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
+ while ((c = getc(fo)) != EOF)
+ (void) putc(c, fout);
+ rewind(fo);
+ (void) putc('\n', fout);
+ (void) fflush(fout);
+ if (ferror(fout))
+ perror(tempEdit);
+ (void) Fclose(fout);
+ }
+
+ /*
+ * Now either copy "image" to the desired file
+ * or give it as the standard input to the desired
+ * program as appropriate.
+ */
+
+ if (ispipe) {
+ int pid;
+ char *shell;
+
+ /*
+ * XXX
+ * We can't really reuse the same image file,
+ * because multiple piped recipients will
+ * share the same lseek location and trample
+ * on one another.
+ */
+ if ((shell = value("SHELL")) == NOSTR)
+ shell = _PATH_CSHELL;
+ pid = start_command(shell, sigmask(SIGHUP)|
+ sigmask(SIGINT)|sigmask(SIGQUIT),
+ image, -1, "-c", fname, NOSTR);
+ if (pid < 0) {
+ senderr++;
+ goto cant;
+ }
+ free_child(pid);
+ } else {
+ int f;
+ if ((fout = Fopen(fname, "a")) == NULL) {
+ perror(fname);
+ senderr++;
+ goto cant;
+ }
+ if ((f = dup(image)) < 0) {
+ perror("dup");
+ fin = NULL;
+ } else
+ fin = Fdopen(f, "r");
+ if (fin == NULL) {
+ fprintf(stderr, "Can't reopen image\n");
+ (void) Fclose(fout);
+ senderr++;
+ goto cant;
+ }
+ rewind(fin);
+ while ((c = getc(fin)) != EOF)
+ (void) putc(c, fout);
+ if (ferror(fout))
+ senderr++, perror(fname);
+ (void) Fclose(fout);
+ (void) Fclose(fin);
+ }
+cant:
+ /*
+ * In days of old we removed the entry from the
+ * the list; now for sake of header expansion
+ * we leave it in and mark it as deleted.
+ */
+ np->n_type |= GDEL;
+ np = np->n_flink;
+ }
+ if (image >= 0) {
+ (void) close(image);
+ image = -1;
+ }
+ return(top);
+}
+
+/*
+ * Determine if the passed address is a local "send to file" address.
+ * If any of the network metacharacters precedes any slashes, it can't
+ * be a filename. We cheat with .'s to allow path names like ./...
+ */
+int
+isfileaddr(name)
+ char *name;
+{
+ register char *cp;
+
+ if (*name == '+')
+ return 1;
+ for (cp = name; *cp; cp++) {
+ if (*cp == '!' || *cp == '%' || *cp == '@')
+ return 0;
+ if (*cp == '/')
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Map all of the aliased users in the invoker's mailrc
+ * file and insert them into the list.
+ * Changed after all these months of service to recursively
+ * expand names (2/14/80).
+ */
+
+struct name *
+usermap(names)
+ struct name *names;
+{
+ register struct name *new, *np, *cp;
+ struct grouphead *gh;
+ register int metoo;
+
+ new = NIL;
+ np = names;
+ metoo = (value("metoo") != NOSTR);
+ while (np != NIL) {
+ if (np->n_name[0] == '\\') {
+ cp = np->n_flink;
+ new = put(new, np);
+ np = cp;
+ continue;
+ }
+ gh = findgroup(np->n_name);
+ cp = np->n_flink;
+ if (gh != NOGRP)
+ new = gexpand(new, gh, metoo, np->n_type);
+ else
+ new = put(new, np);
+ np = cp;
+ }
+ return(new);
+}
+
+/*
+ * Recursively expand a group name. We limit the expansion to some
+ * fixed level to keep things from going haywire.
+ * Direct recursion is not expanded for convenience.
+ */
+
+struct name *
+gexpand(nlist, gh, metoo, ntype)
+ struct name *nlist;
+ struct grouphead *gh;
+ int metoo, ntype;
+{
+ struct group *gp;
+ struct grouphead *ngh;
+ struct name *np;
+ static int depth;
+ char *cp;
+
+ if (depth > MAXEXP) {
+ printf("Expanding alias to depth larger than %d\n", MAXEXP);
+ return(nlist);
+ }
+ depth++;
+ for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
+ cp = gp->ge_name;
+ if (*cp == '\\')
+ goto quote;
+ if (strcmp(cp, gh->g_name) == 0)
+ goto quote;
+ if ((ngh = findgroup(cp)) != NOGRP) {
+ nlist = gexpand(nlist, ngh, metoo, ntype);
+ continue;
+ }
+quote:
+ np = nalloc(cp, ntype);
+ /*
+ * At this point should allow to expand
+ * to self if only person in group
+ */
+ if (gp == gh->g_list && gp->ge_link == NOGE)
+ goto skip;
+ if (!metoo && strcmp(cp, myname) == 0)
+ np->n_type |= GDEL;
+skip:
+ nlist = put(nlist, np);
+ }
+ depth--;
+ return(nlist);
+}
+
+/*
+ * Concatenate the two passed name lists, return the result.
+ */
+struct name *
+cat(n1, n2)
+ struct name *n1, *n2;
+{
+ register struct name *tail;
+
+ if (n1 == NIL)
+ return(n2);
+ if (n2 == NIL)
+ return(n1);
+ tail = tailof(n1);
+ tail->n_flink = n2;
+ n2->n_blink = tail;
+ return(n1);
+}
+
+/*
+ * Unpack the name list onto a vector of strings.
+ * Return an error if the name list won't fit.
+ */
+char **
+unpack(np)
+ struct name *np;
+{
+ register char **ap, **top;
+ register struct name *n;
+ int t, extra, metoo, verbose;
+
+ n = np;
+ if ((t = count(n)) == 0)
+ panic("No names to unpack");
+ /*
+ * Compute the number of extra arguments we will need.
+ * We need at least two extra -- one for "mail" and one for
+ * the terminating 0 pointer. Additional spots may be needed
+ * to pass along -f to the host mailer.
+ */
+ extra = 2;
+ extra++;
+ metoo = value("metoo") != NOSTR;
+ if (metoo)
+ extra++;
+ verbose = value("verbose") != NOSTR;
+ if (verbose)
+ extra++;
+ top = (char **) salloc((t + extra) * sizeof *top);
+ ap = top;
+ *ap++ = "send-mail";
+ *ap++ = "-i";
+ if (metoo)
+ *ap++ = "-m";
+ if (verbose)
+ *ap++ = "-v";
+ for (; n != NIL; n = n->n_flink)
+ if ((n->n_type & GDEL) == 0)
+ *ap++ = n->n_name;
+ *ap = NOSTR;
+ return(top);
+}
+
+/*
+ * Remove all of the duplicates from the passed name list by
+ * insertion sorting them, then checking for dups.
+ * Return the head of the new list.
+ */
+struct name *
+elide(names)
+ struct name *names;
+{
+ register struct name *np, *t, *new;
+ struct name *x;
+
+ if (names == NIL)
+ return(NIL);
+ new = names;
+ np = names;
+ np = np->n_flink;
+ if (np != NIL)
+ np->n_blink = NIL;
+ new->n_flink = NIL;
+ while (np != NIL) {
+ t = new;
+ while (strcasecmp(t->n_name, np->n_name) < 0) {
+ if (t->n_flink == NIL)
+ break;
+ t = t->n_flink;
+ }
+
+ /*
+ * If we ran out of t's, put the new entry after
+ * the current value of t.
+ */
+
+ if (strcasecmp(t->n_name, np->n_name) < 0) {
+ t->n_flink = np;
+ np->n_blink = t;
+ t = np;
+ np = np->n_flink;
+ t->n_flink = NIL;
+ continue;
+ }
+
+ /*
+ * Otherwise, put the new entry in front of the
+ * current t. If at the front of the list,
+ * the new guy becomes the new head of the list.
+ */
+
+ if (t == new) {
+ t = np;
+ np = np->n_flink;
+ t->n_flink = new;
+ new->n_blink = t;
+ t->n_blink = NIL;
+ new = t;
+ continue;
+ }
+
+ /*
+ * The normal case -- we are inserting into the
+ * middle of the list.
+ */
+
+ x = np;
+ np = np->n_flink;
+ x->n_flink = t;
+ x->n_blink = t->n_blink;
+ t->n_blink->n_flink = x;
+ t->n_blink = x;
+ }
+
+ /*
+ * Now the list headed up by new is sorted.
+ * Go through it and remove duplicates.
+ */
+
+ np = new;
+ while (np != NIL) {
+ t = np;
+ while (t->n_flink != NIL &&
+ strcasecmp(np->n_name, t->n_flink->n_name) == 0)
+ t = t->n_flink;
+ if (t == np || t == NIL) {
+ np = np->n_flink;
+ continue;
+ }
+
+ /*
+ * Now t points to the last entry with the same name
+ * as np. Make np point beyond t.
+ */
+
+ np->n_flink = t->n_flink;
+ if (t->n_flink != NIL)
+ t->n_flink->n_blink = np;
+ np = np->n_flink;
+ }
+ return(new);
+}
+
+/*
+ * Put another node onto a list of names and return
+ * the list.
+ */
+struct name *
+put(list, node)
+ struct name *list, *node;
+{
+ node->n_flink = list;
+ node->n_blink = NIL;
+ if (list != NIL)
+ list->n_blink = node;
+ return(node);
+}
+
+/*
+ * Determine the number of undeleted elements in
+ * a name list and return it.
+ */
+int
+count(np)
+ register struct name *np;
+{
+ register int c;
+
+ for (c = 0; np != NIL; np = np->n_flink)
+ if ((np->n_type & GDEL) == 0)
+ c++;
+ return c;
+}
+
+/*
+ * Delete the given name from a namelist.
+ */
+struct name *
+delname(np, name)
+ register struct name *np;
+ char name[];
+{
+ register struct name *p;
+
+ for (p = np; p != NIL; p = p->n_flink)
+ if (strcasecmp(p->n_name, name) == 0) {
+ if (p->n_blink == NIL) {
+ if (p->n_flink != NIL)
+ p->n_flink->n_blink = NIL;
+ np = p->n_flink;
+ continue;
+ }
+ if (p->n_flink == NIL) {
+ if (p->n_blink != NIL)
+ p->n_blink->n_flink = NIL;
+ continue;
+ }
+ p->n_blink->n_flink = p->n_flink;
+ p->n_flink->n_blink = p->n_blink;
+ }
+ return np;
+}
+
+/*
+ * Pretty print a name list
+ * Uncomment it if you need it.
+ */
+
+/*
+void
+prettyprint(name)
+ struct name *name;
+{
+ register struct name *np;
+
+ np = name;
+ while (np != NIL) {
+ fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
+ np = np->n_flink;
+ }
+ fprintf(stderr, "\n");
+}
+*/
diff --git a/usr.bin/mail/pathnames.h b/usr.bin/mail/pathnames.h
new file mode 100644
index 0000000..13a7672
--- /dev/null
+++ b/usr.bin/mail/pathnames.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_EX "/usr/bin/ex"
+#define _PATH_HELP "/usr/share/misc/mail.help"
+#define _PATH_TILDE "/usr/share/misc/mail.tildehelp"
+#define _PATH_MASTER_RC "/etc/mail.rc"
+#define _PATH_MORE "/usr/bin/more"
diff --git a/usr.bin/mail/popen.c b/usr.bin/mail/popen.c
new file mode 100644
index 0000000..467d24a
--- /dev/null
+++ b/usr.bin/mail/popen.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)popen.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/wait.h>
+#include <fcntl.h>
+#include "extern.h"
+
+#define READ 0
+#define WRITE 1
+
+struct fp {
+ FILE *fp;
+ int pipe;
+ int pid;
+ struct fp *link;
+};
+static struct fp *fp_head;
+
+struct child {
+ int pid;
+ char done;
+ char free;
+ union wait status;
+ struct child *link;
+};
+static struct child *child;
+static struct child *findchild __P((int));
+static void delchild __P((struct child *));
+
+FILE *
+Fopen(file, mode)
+ char *file, *mode;
+{
+ FILE *fp;
+
+ if ((fp = fopen(file, mode)) != NULL) {
+ register_file(fp, 0, 0);
+ (void) fcntl(fileno(fp), F_SETFD, 1);
+ }
+ return fp;
+}
+
+FILE *
+Fdopen(fd, mode)
+ int fd;
+ char *mode;
+{
+ FILE *fp;
+
+ if ((fp = fdopen(fd, mode)) != NULL) {
+ register_file(fp, 0, 0);
+ (void) fcntl(fileno(fp), F_SETFD, 1);
+ }
+ return fp;
+}
+
+int
+Fclose(fp)
+ FILE *fp;
+{
+ unregister_file(fp);
+ return fclose(fp);
+}
+
+FILE *
+Popen(cmd, mode)
+ char *cmd;
+ char *mode;
+{
+ int p[2];
+ int myside, hisside, fd0, fd1;
+ int pid;
+ FILE *fp;
+
+ if (pipe(p) < 0)
+ return NULL;
+ (void) fcntl(p[READ], F_SETFD, 1);
+ (void) fcntl(p[WRITE], F_SETFD, 1);
+ if (*mode == 'r') {
+ myside = p[READ];
+ fd0 = -1;
+ hisside = fd1 = p[WRITE];
+ } else {
+ myside = p[WRITE];
+ hisside = fd0 = p[READ];
+ fd1 = -1;
+ }
+ if ((pid = start_command(cmd, 0, fd0, fd1, NOSTR, NOSTR, NOSTR)) < 0) {
+ close(p[READ]);
+ close(p[WRITE]);
+ return NULL;
+ }
+ (void) close(hisside);
+ if ((fp = fdopen(myside, mode)) != NULL)
+ register_file(fp, 1, pid);
+ return fp;
+}
+
+int
+Pclose(ptr)
+ FILE *ptr;
+{
+ int i;
+ int omask;
+
+ i = file_pid(ptr);
+ unregister_file(ptr);
+ (void) fclose(ptr);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
+ i = wait_child(i);
+ sigsetmask(omask);
+ return i;
+}
+
+void
+close_all_files()
+{
+
+ while (fp_head)
+ if (fp_head->pipe)
+ (void) Pclose(fp_head->fp);
+ else
+ (void) Fclose(fp_head->fp);
+}
+
+void
+register_file(fp, pipe, pid)
+ FILE *fp;
+ int pipe, pid;
+{
+ struct fp *fpp;
+
+ if ((fpp = (struct fp *) malloc(sizeof *fpp)) == NULL)
+ panic("Out of memory");
+ fpp->fp = fp;
+ fpp->pipe = pipe;
+ fpp->pid = pid;
+ fpp->link = fp_head;
+ fp_head = fpp;
+}
+
+void
+unregister_file(fp)
+ FILE *fp;
+{
+ struct fp **pp, *p;
+
+ for (pp = &fp_head; p = *pp; pp = &p->link)
+ if (p->fp == fp) {
+ *pp = p->link;
+ free((char *) p);
+ return;
+ }
+ panic("Invalid file pointer");
+}
+
+file_pid(fp)
+ FILE *fp;
+{
+ struct fp *p;
+
+ for (p = fp_head; p; p = p->link)
+ if (p->fp == fp)
+ return (p->pid);
+ panic("Invalid file pointer");
+ /*NOTREACHED*/
+}
+
+/*
+ * Run a command without a shell, with optional arguments and splicing
+ * of stdin and stdout. The command name can be a sequence of words.
+ * Signals must be handled by the caller.
+ * "Mask" contains the signals to ignore in the new process.
+ * SIGINT is enabled unless it's in the mask.
+ */
+/*VARARGS4*/
+int
+run_command(cmd, mask, infd, outfd, a0, a1, a2)
+ char *cmd;
+ int mask, infd, outfd;
+ char *a0, *a1, *a2;
+{
+ int pid;
+
+ if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
+ return -1;
+ return wait_command(pid);
+}
+
+/*VARARGS4*/
+int
+start_command(cmd, mask, infd, outfd, a0, a1, a2)
+ char *cmd;
+ int mask, infd, outfd;
+ char *a0, *a1, *a2;
+{
+ int pid;
+
+ if ((pid = vfork()) < 0) {
+ perror("fork");
+ return -1;
+ }
+ if (pid == 0) {
+ char *argv[100];
+ int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
+
+ if ((argv[i++] = a0) != NOSTR &&
+ (argv[i++] = a1) != NOSTR &&
+ (argv[i++] = a2) != NOSTR)
+ argv[i] = NOSTR;
+ prepare_child(mask, infd, outfd);
+ execvp(argv[0], argv);
+ perror(argv[0]);
+ _exit(1);
+ }
+ return pid;
+}
+
+void
+prepare_child(mask, infd, outfd)
+ int mask, infd, outfd;
+{
+ int i;
+
+ /*
+ * All file descriptors other than 0, 1, and 2 are supposed to be
+ * close-on-exec.
+ */
+ if (infd >= 0)
+ dup2(infd, 0);
+ if (outfd >= 0)
+ dup2(outfd, 1);
+ for (i = 1; i <= NSIG; i++)
+ if (mask & sigmask(i))
+ (void) signal(i, SIG_IGN);
+ if ((mask & sigmask(SIGINT)) == 0)
+ (void) signal(SIGINT, SIG_DFL);
+ (void) sigsetmask(0);
+}
+
+int
+wait_command(pid)
+ int pid;
+{
+
+ if (wait_child(pid) < 0) {
+ printf("Fatal error in process.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static struct child *
+findchild(pid)
+ int pid;
+{
+ register struct child **cpp;
+
+ for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
+ cpp = &(*cpp)->link)
+ ;
+ if (*cpp == NULL) {
+ *cpp = (struct child *) malloc(sizeof (struct child));
+ (*cpp)->pid = pid;
+ (*cpp)->done = (*cpp)->free = 0;
+ (*cpp)->link = NULL;
+ }
+ return *cpp;
+}
+
+static void
+delchild(cp)
+ register struct child *cp;
+{
+ register struct child **cpp;
+
+ for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
+ ;
+ *cpp = cp->link;
+ free((char *) cp);
+}
+
+void
+sigchild(signo)
+ int signo;
+{
+ int pid;
+ union wait status;
+ register struct child *cp;
+
+ while ((pid =
+ wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
+ cp = findchild(pid);
+ if (cp->free)
+ delchild(cp);
+ else {
+ cp->done = 1;
+ cp->status = status;
+ }
+ }
+}
+
+union wait wait_status;
+
+/*
+ * Wait for a specific child to die.
+ */
+int
+wait_child(pid)
+ int pid;
+{
+ int mask = sigblock(sigmask(SIGCHLD));
+ register struct child *cp = findchild(pid);
+
+ while (!cp->done)
+ sigpause(mask);
+ wait_status = cp->status;
+ delchild(cp);
+ sigsetmask(mask);
+ return wait_status.w_status ? -1 : 0;
+}
+
+/*
+ * Mark a child as don't care.
+ */
+void
+free_child(pid)
+ int pid;
+{
+ int mask = sigblock(sigmask(SIGCHLD));
+ register struct child *cp = findchild(pid);
+
+ if (cp->done)
+ delchild(cp);
+ else
+ cp->free = 1;
+ sigsetmask(mask);
+}
diff --git a/usr.bin/mail/quit.c b/usr.bin/mail/quit.c
new file mode 100644
index 0000000..05e708f
--- /dev/null
+++ b/usr.bin/mail/quit.c
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)quit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Rcv -- receive mail rationally.
+ *
+ * Termination processing.
+ */
+
+/*
+ * The "quit" command.
+ */
+int
+quitcmd()
+{
+ /*
+ * If we are sourcing, then return 1 so execute() can handle it.
+ * Otherwise, return -1 to abort command loop.
+ */
+ if (sourcing)
+ return 1;
+ return -1;
+}
+
+/*
+ * Save all of the undetermined messages at the top of "mbox"
+ * Save all untouched messages back in the system mailbox.
+ * Remove the system mailbox, if none saved there.
+ */
+void
+quit()
+{
+ int mcount, p, modify, autohold, anystat, holdbit, nohold;
+ FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
+ register struct message *mp;
+ register int c;
+ extern char tempQuit[], tempResid[];
+ struct stat minfo;
+ char *mbox;
+
+ /*
+ * If we are read only, we can't do anything,
+ * so just return quickly.
+ */
+ if (readonly)
+ return;
+ /*
+ * If editing (not reading system mail box), then do the work
+ * in edstop()
+ */
+ if (edit) {
+ edstop();
+ return;
+ }
+
+ /*
+ * See if there any messages to save in mbox. If no, we
+ * can save copying mbox to /tmp and back.
+ *
+ * Check also to see if any files need to be preserved.
+ * Delete all untouched messages to keep them out of mbox.
+ * If all the messages are to be preserved, just exit with
+ * a message.
+ */
+
+ fbuf = Fopen(mailname, "r");
+ if (fbuf == NULL)
+ goto newmail;
+ flock(fileno(fbuf), LOCK_EX);
+ rbuf = NULL;
+ if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
+ printf("New mail has arrived.\n");
+ rbuf = Fopen(tempResid, "w");
+ if (rbuf == NULL || fbuf == NULL)
+ goto newmail;
+#ifdef APPEND
+ fseek(fbuf, (long)mailsize, 0);
+ while ((c = getc(fbuf)) != EOF)
+ (void) putc(c, rbuf);
+#else
+ p = minfo.st_size - mailsize;
+ while (p-- > 0) {
+ c = getc(fbuf);
+ if (c == EOF)
+ goto newmail;
+ (void) putc(c, rbuf);
+ }
+#endif
+ Fclose(rbuf);
+ if ((rbuf = Fopen(tempResid, "r")) == NULL)
+ goto newmail;
+ rm(tempResid);
+ }
+
+ /*
+ * Adjust the message flags in each message.
+ */
+
+ anystat = 0;
+ autohold = value("hold") != NOSTR;
+ holdbit = autohold ? MPRESERVE : MBOX;
+ nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
+ if (value("keepsave") != NOSTR)
+ nohold &= ~MSAVED;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW) {
+ mp->m_flag &= ~MNEW;
+ mp->m_flag |= MSTATUS;
+ }
+ if (mp->m_flag & MSTATUS)
+ anystat++;
+ if ((mp->m_flag & MTOUCH) == 0)
+ mp->m_flag |= MPRESERVE;
+ if ((mp->m_flag & nohold) == 0)
+ mp->m_flag |= holdbit;
+ }
+ modify = 0;
+ if (Tflag != NOSTR) {
+ if ((readstat = Fopen(Tflag, "w")) == NULL)
+ Tflag = NOSTR;
+ }
+ for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MBOX)
+ c++;
+ if (mp->m_flag & MPRESERVE)
+ p++;
+ if (mp->m_flag & MODIFY)
+ modify++;
+ if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
+ char *id;
+
+ if ((id = hfield("article-id", mp)) != NOSTR)
+ fprintf(readstat, "%s\n", id);
+ }
+ }
+ if (Tflag != NOSTR)
+ Fclose(readstat);
+ if (p == msgCount && !modify && !anystat) {
+ printf("Held %d message%s in %s\n",
+ p, p == 1 ? "" : "s", mailname);
+ Fclose(fbuf);
+ return;
+ }
+ if (c == 0) {
+ if (p != 0) {
+ writeback(rbuf);
+ Fclose(fbuf);
+ return;
+ }
+ goto cream;
+ }
+
+ /*
+ * Create another temporary file and copy user's mbox file
+ * darin. If there is no mbox, copy nothing.
+ * If he has specified "append" don't copy his mailbox,
+ * just copy saveable entries at the end.
+ */
+
+ mbox = expand("&");
+ mcount = c;
+ if (value("append") == NOSTR) {
+ if ((obuf = Fopen(tempQuit, "w")) == NULL) {
+ perror(tempQuit);
+ Fclose(fbuf);
+ return;
+ }
+ if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
+ perror(tempQuit);
+ rm(tempQuit);
+ Fclose(obuf);
+ Fclose(fbuf);
+ return;
+ }
+ rm(tempQuit);
+ if ((abuf = Fopen(mbox, "r")) != NULL) {
+ while ((c = getc(abuf)) != EOF)
+ (void) putc(c, obuf);
+ Fclose(abuf);
+ }
+ if (ferror(obuf)) {
+ perror(tempQuit);
+ Fclose(ibuf);
+ Fclose(obuf);
+ Fclose(fbuf);
+ return;
+ }
+ Fclose(obuf);
+ close(creat(mbox, 0600));
+ if ((obuf = Fopen(mbox, "r+")) == NULL) {
+ perror(mbox);
+ Fclose(ibuf);
+ Fclose(fbuf);
+ return;
+ }
+ }
+ if (value("append") != NOSTR) {
+ if ((obuf = Fopen(mbox, "a")) == NULL) {
+ perror(mbox);
+ Fclose(fbuf);
+ return;
+ }
+ fchmod(fileno(obuf), 0600);
+ }
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if (mp->m_flag & MBOX)
+ if (send(mp, obuf, saveignore, NOSTR) < 0) {
+ perror(mbox);
+ Fclose(ibuf);
+ Fclose(obuf);
+ Fclose(fbuf);
+ return;
+ }
+
+ /*
+ * Copy the user's old mbox contents back
+ * to the end of the stuff we just saved.
+ * If we are appending, this is unnecessary.
+ */
+
+ if (value("append") == NOSTR) {
+ rewind(ibuf);
+ c = getc(ibuf);
+ while (c != EOF) {
+ (void) putc(c, obuf);
+ if (ferror(obuf))
+ break;
+ c = getc(ibuf);
+ }
+ Fclose(ibuf);
+ fflush(obuf);
+ }
+ trunc(obuf);
+ if (ferror(obuf)) {
+ perror(mbox);
+ Fclose(obuf);
+ Fclose(fbuf);
+ return;
+ }
+ Fclose(obuf);
+ if (mcount == 1)
+ printf("Saved 1 message in mbox\n");
+ else
+ printf("Saved %d messages in mbox\n", mcount);
+
+ /*
+ * Now we are ready to copy back preserved files to
+ * the system mailbox, if any were requested.
+ */
+
+ if (p != 0) {
+ writeback(rbuf);
+ Fclose(fbuf);
+ return;
+ }
+
+ /*
+ * Finally, remove his /usr/mail file.
+ * If new mail has arrived, copy it back.
+ */
+
+cream:
+ if (rbuf != NULL) {
+ abuf = Fopen(mailname, "r+");
+ if (abuf == NULL)
+ goto newmail;
+ while ((c = getc(rbuf)) != EOF)
+ (void) putc(c, abuf);
+ Fclose(rbuf);
+ trunc(abuf);
+ Fclose(abuf);
+ alter(mailname);
+ Fclose(fbuf);
+ return;
+ }
+ demail();
+ Fclose(fbuf);
+ return;
+
+newmail:
+ printf("Thou hast new mail.\n");
+ if (fbuf != NULL)
+ Fclose(fbuf);
+}
+
+/*
+ * Preserve all the appropriate messages back in the system
+ * mailbox, and print a nice message indicated how many were
+ * saved. On any error, just return -1. Else return 0.
+ * Incorporate the any new mail that we found.
+ */
+int
+writeback(res)
+ register FILE *res;
+{
+ register struct message *mp;
+ register int p, c;
+ FILE *obuf;
+
+ p = 0;
+ if ((obuf = Fopen(mailname, "r+")) == NULL) {
+ perror(mailname);
+ return(-1);
+ }
+#ifndef APPEND
+ if (res != NULL)
+ while ((c = getc(res)) != EOF)
+ (void) putc(c, obuf);
+#endif
+ for (mp = &message[0]; mp < &message[msgCount]; mp++)
+ if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
+ p++;
+ if (send(mp, obuf, (struct ignoretab *)0, NOSTR) < 0) {
+ perror(mailname);
+ Fclose(obuf);
+ return(-1);
+ }
+ }
+#ifdef APPEND
+ if (res != NULL)
+ while ((c = getc(res)) != EOF)
+ (void) putc(c, obuf);
+#endif
+ fflush(obuf);
+ trunc(obuf);
+ if (ferror(obuf)) {
+ perror(mailname);
+ Fclose(obuf);
+ return(-1);
+ }
+ if (res != NULL)
+ Fclose(res);
+ Fclose(obuf);
+ alter(mailname);
+ if (p == 1)
+ printf("Held 1 message in %s\n", mailname);
+ else
+ printf("Held %d messages in %s\n", p, mailname);
+ return(0);
+}
+
+/*
+ * Terminate an editing session by attempting to write out the user's
+ * file from the temporary. Save any new stuff appended to the file.
+ */
+void
+edstop()
+{
+ extern char *tmpdir;
+ register int gotcha, c;
+ register struct message *mp;
+ FILE *obuf, *ibuf, *readstat;
+ struct stat statb;
+ char tempname[30];
+ char *mktemp();
+
+ if (readonly)
+ return;
+ holdsigs();
+ if (Tflag != NOSTR) {
+ if ((readstat = Fopen(Tflag, "w")) == NULL)
+ Tflag = NOSTR;
+ }
+ for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
+ if (mp->m_flag & MNEW) {
+ mp->m_flag &= ~MNEW;
+ mp->m_flag |= MSTATUS;
+ }
+ if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
+ gotcha++;
+ if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
+ char *id;
+
+ if ((id = hfield("article-id", mp)) != NOSTR)
+ fprintf(readstat, "%s\n", id);
+ }
+ }
+ if (Tflag != NOSTR)
+ Fclose(readstat);
+ if (!gotcha || Tflag != NOSTR)
+ goto done;
+ ibuf = NULL;
+ if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
+ strcpy(tempname, tmpdir);
+ strcat(tempname, "mboxXXXXXX");
+ mktemp(tempname);
+ if ((obuf = Fopen(tempname, "w")) == NULL) {
+ perror(tempname);
+ relsesigs();
+ reset(0);
+ }
+ if ((ibuf = Fopen(mailname, "r")) == NULL) {
+ perror(mailname);
+ Fclose(obuf);
+ rm(tempname);
+ relsesigs();
+ reset(0);
+ }
+ fseek(ibuf, (long)mailsize, 0);
+ while ((c = getc(ibuf)) != EOF)
+ (void) putc(c, obuf);
+ Fclose(ibuf);
+ Fclose(obuf);
+ if ((ibuf = Fopen(tempname, "r")) == NULL) {
+ perror(tempname);
+ rm(tempname);
+ relsesigs();
+ reset(0);
+ }
+ rm(tempname);
+ }
+ printf("\"%s\" ", mailname);
+ fflush(stdout);
+ if ((obuf = Fopen(mailname, "r+")) == NULL) {
+ perror(mailname);
+ relsesigs();
+ reset(0);
+ }
+ trunc(obuf);
+ c = 0;
+ for (mp = &message[0]; mp < &message[msgCount]; mp++) {
+ if ((mp->m_flag & MDELETED) != 0)
+ continue;
+ c++;
+ if (send(mp, obuf, (struct ignoretab *) NULL, NOSTR) < 0) {
+ perror(mailname);
+ relsesigs();
+ reset(0);
+ }
+ }
+ gotcha = (c == 0 && ibuf == NULL);
+ if (ibuf != NULL) {
+ while ((c = getc(ibuf)) != EOF)
+ (void) putc(c, obuf);
+ Fclose(ibuf);
+ }
+ fflush(obuf);
+ if (ferror(obuf)) {
+ perror(mailname);
+ relsesigs();
+ reset(0);
+ }
+ Fclose(obuf);
+ if (gotcha) {
+ rm(mailname);
+ printf("removed\n");
+ } else
+ printf("complete\n");
+ fflush(stdout);
+
+done:
+ relsesigs();
+}
diff --git a/usr.bin/mail/rcv.h b/usr.bin/mail/rcv.h
new file mode 100644
index 0000000..6d78f36
--- /dev/null
+++ b/usr.bin/mail/rcv.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rcv.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Mail -- a mail program
+ *
+ * This file is included by normal files which want both
+ * globals and declarations.
+ */
+
+#include "def.h"
+#include "glob.h"
diff --git a/usr.bin/mail/send.c b/usr.bin/mail/send.c
new file mode 100644
index 0000000..063e6ad
--- /dev/null
+++ b/usr.bin/mail/send.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Mail to others.
+ */
+
+/*
+ * Send message described by the passed pointer to the
+ * passed output buffer. Return -1 on error.
+ * Adjust the status: field if need be.
+ * If doign is given, suppress ignored header fields.
+ * prefix is a string to prepend to each output line.
+ */
+int
+send(mp, obuf, doign, prefix)
+ register struct message *mp;
+ FILE *obuf;
+ struct ignoretab *doign;
+ char *prefix;
+{
+ long count;
+ register FILE *ibuf;
+ char line[LINESIZE];
+ int ishead, infld, ignoring, dostat, firstline;
+ register char *cp, *cp2;
+ register int c;
+ int length;
+ int prefixlen;
+
+ /*
+ * Compute the prefix string, without trailing whitespace
+ */
+ if (prefix != NOSTR) {
+ cp2 = 0;
+ for (cp = prefix; *cp; cp++)
+ if (*cp != ' ' && *cp != '\t')
+ cp2 = cp;
+ prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
+ }
+ ibuf = setinput(mp);
+ count = mp->m_size;
+ ishead = 1;
+ dostat = doign == 0 || !isign("status", doign);
+ infld = 0;
+ firstline = 1;
+ /*
+ * Process headers first
+ */
+ while (count > 0 && ishead) {
+ if (fgets(line, LINESIZE, ibuf) == NULL)
+ break;
+ count -= length = strlen(line);
+ if (firstline) {
+ /*
+ * First line is the From line, so no headers
+ * there to worry about
+ */
+ firstline = 0;
+ ignoring = doign == ignoreall;
+ } else if (line[0] == '\n') {
+ /*
+ * If line is blank, we've reached end of
+ * headers, so force out status: field
+ * and note that we are no longer in header
+ * fields
+ */
+ if (dostat) {
+ statusput(mp, obuf, prefix);
+ dostat = 0;
+ }
+ ishead = 0;
+ ignoring = doign == ignoreall;
+ } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
+ /*
+ * If this line is a continuation (via space or tab)
+ * of a previous header field, just echo it
+ * (unless the field should be ignored).
+ * In other words, nothing to do.
+ */
+ } else {
+ /*
+ * Pick up the header field if we have one.
+ */
+ for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
+ ;
+ cp2 = --cp;
+ while (isspace(*cp++))
+ ;
+ if (cp[-1] != ':') {
+ /*
+ * Not a header line, force out status:
+ * This happens in uucp style mail where
+ * there are no headers at all.
+ */
+ if (dostat) {
+ statusput(mp, obuf, prefix);
+ dostat = 0;
+ }
+ if (doign != ignoreall)
+ /* add blank line */
+ (void) putc('\n', obuf);
+ ishead = 0;
+ ignoring = 0;
+ } else {
+ /*
+ * If it is an ignored field and
+ * we care about such things, skip it.
+ */
+ *cp2 = 0; /* temporarily null terminate */
+ if (doign && isign(line, doign))
+ ignoring = 1;
+ else if ((line[0] == 's' || line[0] == 'S') &&
+ strcasecmp(line, "status") == 0) {
+ /*
+ * If the field is "status," go compute
+ * and print the real Status: field
+ */
+ if (dostat) {
+ statusput(mp, obuf, prefix);
+ dostat = 0;
+ }
+ ignoring = 1;
+ } else {
+ ignoring = 0;
+ *cp2 = c; /* restore */
+ }
+ infld = 1;
+ }
+ }
+ if (!ignoring) {
+ /*
+ * Strip trailing whitespace from prefix
+ * if line is blank.
+ */
+ if (prefix != NOSTR)
+ if (length > 1)
+ fputs(prefix, obuf);
+ else
+ (void) fwrite(prefix, sizeof *prefix,
+ prefixlen, obuf);
+ (void) fwrite(line, sizeof *line, length, obuf);
+ if (ferror(obuf))
+ return -1;
+ }
+ }
+ /*
+ * Copy out message body
+ */
+ if (doign == ignoreall)
+ count--; /* skip final blank line */
+ if (prefix != NOSTR)
+ while (count > 0) {
+ if (fgets(line, LINESIZE, ibuf) == NULL) {
+ c = 0;
+ break;
+ }
+ count -= c = strlen(line);
+ /*
+ * Strip trailing whitespace from prefix
+ * if line is blank.
+ */
+ if (c > 1)
+ fputs(prefix, obuf);
+ else
+ (void) fwrite(prefix, sizeof *prefix,
+ prefixlen, obuf);
+ (void) fwrite(line, sizeof *line, c, obuf);
+ if (ferror(obuf))
+ return -1;
+ }
+ else
+ while (count > 0) {
+ c = count < LINESIZE ? count : LINESIZE;
+ if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
+ break;
+ count -= c;
+ if (fwrite(line, sizeof *line, c, obuf) != c)
+ return -1;
+ }
+ if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
+ /* no final blank line */
+ if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
+ return -1;
+ return 0;
+}
+
+/*
+ * Output a reasonable looking status field.
+ */
+void
+statusput(mp, obuf, prefix)
+ register struct message *mp;
+ FILE *obuf;
+ char *prefix;
+{
+ char statout[3];
+ register char *cp = statout;
+
+ if (mp->m_flag & MREAD)
+ *cp++ = 'R';
+ if ((mp->m_flag & MNEW) == 0)
+ *cp++ = 'O';
+ *cp = 0;
+ if (statout[0])
+ fprintf(obuf, "%sStatus: %s\n",
+ prefix == NOSTR ? "" : prefix, statout);
+}
+
+/*
+ * Interface between the argument list and the mail1 routine
+ * which does all the dirty work.
+ */
+int
+mail(to, cc, bcc, smopts, subject)
+ struct name *to, *cc, *bcc, *smopts;
+ char *subject;
+{
+ struct header head;
+
+ head.h_to = to;
+ head.h_subject = subject;
+ head.h_cc = cc;
+ head.h_bcc = bcc;
+ head.h_smopts = smopts;
+ mail1(&head, 0);
+ return(0);
+}
+
+
+/*
+ * Send mail to a bunch of user names. The interface is through
+ * the mail routine below.
+ */
+int
+sendmail(str)
+ char *str;
+{
+ struct header head;
+
+ head.h_to = extract(str, GTO);
+ head.h_subject = NOSTR;
+ head.h_cc = NIL;
+ head.h_bcc = NIL;
+ head.h_smopts = NIL;
+ mail1(&head, 0);
+ return(0);
+}
+
+/*
+ * Mail a message on standard input to the people indicated
+ * in the passed header. (Internal interface).
+ */
+void
+mail1(hp, printheaders)
+ struct header *hp;
+ int printheaders;
+{
+ char *cp;
+ int pid;
+ char **namelist;
+ struct name *to;
+ FILE *mtf;
+
+ /*
+ * Collect user's mail from standard input.
+ * Get the result as mtf.
+ */
+ if ((mtf = collect(hp, printheaders)) == NULL)
+ return;
+ if (value("interactive") != NOSTR)
+ if (value("askcc") != NOSTR)
+ grabh(hp, GCC);
+ else {
+ printf("EOT\n");
+ (void) fflush(stdout);
+ }
+ if (fsize(mtf) == 0)
+ if (hp->h_subject == NOSTR)
+ printf("No message, no subject; hope that's ok\n");
+ else
+ printf("Null message body; hope that's ok\n");
+ /*
+ * Now, take the user names from the combined
+ * to and cc lists and do all the alias
+ * processing.
+ */
+ senderr = 0;
+ to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
+ if (to == NIL) {
+ printf("No recipients specified\n");
+ senderr++;
+ }
+ /*
+ * Look through the recipient list for names with /'s
+ * in them which we write to as files directly.
+ */
+ to = outof(to, mtf, hp);
+ if (senderr)
+ savedeadletter(mtf);
+ to = elide(to);
+ if (count(to) == 0)
+ goto out;
+ fixhead(hp, to);
+ if ((mtf = infix(hp, mtf)) == NULL) {
+ fprintf(stderr, ". . . message lost, sorry.\n");
+ return;
+ }
+ namelist = unpack(cat(hp->h_smopts, to));
+ if (debug) {
+ char **t;
+
+ printf("Sendmail arguments:");
+ for (t = namelist; *t != NOSTR; t++)
+ printf(" \"%s\"", *t);
+ printf("\n");
+ goto out;
+ }
+ if ((cp = value("record")) != NOSTR)
+ (void) savemail(expand(cp), mtf);
+ /*
+ * Fork, set up the temporary mail file as standard
+ * input for "mail", and exec with the user list we generated
+ * far above.
+ */
+ pid = fork();
+ if (pid == -1) {
+ perror("fork");
+ savedeadletter(mtf);
+ goto out;
+ }
+ if (pid == 0) {
+ prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
+ sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
+ fileno(mtf), -1);
+ if ((cp = value("sendmail")) != NOSTR)
+ cp = expand(cp);
+ else
+ cp = _PATH_SENDMAIL;
+ execv(cp, namelist);
+ perror(cp);
+ _exit(1);
+ }
+ if (value("verbose") != NOSTR)
+ (void) wait_child(pid);
+ else
+ free_child(pid);
+out:
+ (void) Fclose(mtf);
+}
+
+/*
+ * Fix the header by glopping all of the expanded names from
+ * the distribution list into the appropriate fields.
+ */
+void
+fixhead(hp, tolist)
+ struct header *hp;
+ struct name *tolist;
+{
+ register struct name *np;
+
+ hp->h_to = NIL;
+ hp->h_cc = NIL;
+ hp->h_bcc = NIL;
+ for (np = tolist; np != NIL; np = np->n_flink)
+ if ((np->n_type & GMASK) == GTO)
+ hp->h_to =
+ cat(hp->h_to, nalloc(np->n_name, np->n_type));
+ else if ((np->n_type & GMASK) == GCC)
+ hp->h_cc =
+ cat(hp->h_cc, nalloc(np->n_name, np->n_type));
+ else if ((np->n_type & GMASK) == GBCC)
+ hp->h_bcc =
+ cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
+}
+
+/*
+ * Prepend a header in front of the collected stuff
+ * and return the new file.
+ */
+FILE *
+infix(hp, fi)
+ struct header *hp;
+ FILE *fi;
+{
+ extern char tempMail[];
+ register FILE *nfo, *nfi;
+ register int c;
+
+ if ((nfo = Fopen(tempMail, "w")) == NULL) {
+ perror(tempMail);
+ return(fi);
+ }
+ if ((nfi = Fopen(tempMail, "r")) == NULL) {
+ perror(tempMail);
+ (void) Fclose(nfo);
+ return(fi);
+ }
+ (void) rm(tempMail);
+ (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
+ c = getc(fi);
+ while (c != EOF) {
+ (void) putc(c, nfo);
+ c = getc(fi);
+ }
+ if (ferror(fi)) {
+ perror("read");
+ rewind(fi);
+ return(fi);
+ }
+ (void) fflush(nfo);
+ if (ferror(nfo)) {
+ perror(tempMail);
+ (void) Fclose(nfo);
+ (void) Fclose(nfi);
+ rewind(fi);
+ return(fi);
+ }
+ (void) Fclose(nfo);
+ (void) Fclose(fi);
+ rewind(nfi);
+ return(nfi);
+}
+
+/*
+ * Dump the to, subject, cc header on the
+ * passed file buffer.
+ */
+int
+puthead(hp, fo, w)
+ struct header *hp;
+ FILE *fo;
+ int w;
+{
+ register int gotcha;
+
+ gotcha = 0;
+ if (hp->h_to != NIL && w & GTO)
+ fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
+ if (hp->h_subject != NOSTR && w & GSUBJECT)
+ fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
+ if (hp->h_cc != NIL && w & GCC)
+ fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
+ if (hp->h_bcc != NIL && w & GBCC)
+ fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
+ if (gotcha && w & GNL)
+ (void) putc('\n', fo);
+ return(0);
+}
+
+/*
+ * Format the given header line to not exceed 72 characters.
+ */
+void
+fmt(str, np, fo, comma)
+ char *str;
+ register struct name *np;
+ FILE *fo;
+ int comma;
+{
+ register col, len;
+
+ comma = comma ? 1 : 0;
+ col = strlen(str);
+ if (col)
+ fputs(str, fo);
+ for (; np != NIL; np = np->n_flink) {
+ if (np->n_flink == NIL)
+ comma = 0;
+ len = strlen(np->n_name);
+ col++; /* for the space */
+ if (col + len + comma > 72 && col > 4) {
+ fputs("\n ", fo);
+ col = 4;
+ } else
+ putc(' ', fo);
+ fputs(np->n_name, fo);
+ if (comma)
+ putc(',', fo);
+ col += len + comma;
+ }
+ putc('\n', fo);
+}
+
+/*
+ * Save the outgoing mail on the passed file.
+ */
+
+/*ARGSUSED*/
+int
+savemail(name, fi)
+ char name[];
+ register FILE *fi;
+{
+ register FILE *fo;
+ char buf[BUFSIZ];
+ register i;
+ time_t now, time();
+ char *ctime();
+
+ if ((fo = Fopen(name, "a")) == NULL) {
+ perror(name);
+ return (-1);
+ }
+ (void) time(&now);
+ fprintf(fo, "From %s %s", myname, ctime(&now));
+ while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
+ (void) fwrite(buf, 1, i, fo);
+ (void) putc('\n', fo);
+ (void) fflush(fo);
+ if (ferror(fo))
+ perror(name);
+ (void) Fclose(fo);
+ rewind(fi);
+ return (0);
+}
diff --git a/usr.bin/mail/strings.c b/usr.bin/mail/strings.c
new file mode 100644
index 0000000..f27b959
--- /dev/null
+++ b/usr.bin/mail/strings.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)strings.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * String allocation routines.
+ * Strings handed out here are reclaimed at the top of the command
+ * loop each time, so they need not be freed.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Allocate size more bytes of space and return the address of the
+ * first byte to the caller. An even number of bytes are always
+ * allocated so that the space will always be on a word boundary.
+ * The string spaces are of exponentially increasing size, to satisfy
+ * the occasional user with enormous string size requests.
+ */
+
+char *
+salloc(size)
+ int size;
+{
+ register char *t;
+ register int s;
+ register struct strings *sp;
+ int index;
+
+ s = size;
+ s += 3;
+ s &= ~03;
+ index = 0;
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+ if (sp->s_topFree == NOSTR && (STRINGSIZE << index) >= s)
+ break;
+ if (sp->s_nleft >= s)
+ break;
+ index++;
+ }
+ if (sp >= &stringdope[NSPACE])
+ panic("String too large");
+ if (sp->s_topFree == NOSTR) {
+ index = sp - &stringdope[0];
+ sp->s_topFree = malloc(STRINGSIZE << index);
+ if (sp->s_topFree == NOSTR) {
+ fprintf(stderr, "No room for space %d\n", index);
+ panic("Internal error");
+ }
+ sp->s_nextFree = sp->s_topFree;
+ sp->s_nleft = STRINGSIZE << index;
+ }
+ sp->s_nleft -= s;
+ t = sp->s_nextFree;
+ sp->s_nextFree += s;
+ return(t);
+}
+
+/*
+ * Reset the string area to be empty.
+ * Called to free all strings allocated
+ * since last reset.
+ */
+void
+sreset()
+{
+ register struct strings *sp;
+ register int index;
+
+ if (noreset)
+ return;
+ index = 0;
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
+ if (sp->s_topFree == NOSTR)
+ continue;
+ sp->s_nextFree = sp->s_topFree;
+ sp->s_nleft = STRINGSIZE << index;
+ index++;
+ }
+}
+
+/*
+ * Make the string area permanent.
+ * Meant to be called in main, after initialization.
+ */
+void
+spreserve()
+{
+ register struct strings *sp;
+
+ for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++)
+ sp->s_topFree = NOSTR;
+}
diff --git a/usr.bin/mail/temp.c b/usr.bin/mail/temp.c
new file mode 100644
index 0000000..0690027
--- /dev/null
+++ b/usr.bin/mail/temp.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)temp.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <errno.h>
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Give names to all the temporary files that we will need.
+ */
+
+char tempMail[24];
+char tempQuit[24];
+char tempEdit[24];
+char tempResid[24];
+char tempMesg[24];
+char *tmpdir;
+
+void
+tinit()
+{
+ register char *cp;
+ int len;
+
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = _PATH_TMP;
+ else {
+ len = strlen(tmpdir);
+ if ((cp = malloc(len + 2)) == NULL) {
+ (void)fprintf(stderr, "mail: %s\n", strerror(errno));
+ exit (1);
+ }
+ (void)strcpy(cp, tmpdir);
+ cp[len] = '/';
+ cp[len + 1] = '\0';
+ tmpdir = cp;
+ }
+
+ strcpy(tempMail, tmpdir);
+ mktemp(strcat(tempMail, "RsXXXXXX"));
+ strcpy(tempResid, tmpdir);
+ mktemp(strcat(tempResid, "RqXXXXXX"));
+ strcpy(tempQuit, tmpdir);
+ mktemp(strcat(tempQuit, "RmXXXXXX"));
+ strcpy(tempEdit, tmpdir);
+ mktemp(strcat(tempEdit, "ReXXXXXX"));
+ strcpy(tempMesg, tmpdir);
+ mktemp(strcat(tempMesg, "RxXXXXXX"));
+
+ /*
+ * It's okay to call savestr in here because main will
+ * do a spreserve() after us.
+ */
+ if (myname != NOSTR) {
+ if (getuserid(myname) < 0) {
+ printf("\"%s\" is not a user of this system\n",
+ myname);
+ exit(1);
+ }
+ } else {
+ if ((cp = username()) == NOSTR) {
+ myname = "ubluit";
+ if (rcvmode) {
+ printf("Who are you!?\n");
+ exit(1);
+ }
+ } else
+ myname = savestr(cp);
+ }
+ if ((cp = getenv("HOME")) == NOSTR)
+ cp = ".";
+ homedir = savestr(cp);
+ if (debug)
+ printf("user = %s, homedir = %s\n", myname, homedir);
+}
diff --git a/usr.bin/mail/tty.c b/usr.bin/mail/tty.c
new file mode 100644
index 0000000..b39eba5
--- /dev/null
+++ b/usr.bin/mail/tty.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Generally useful tty stuff.
+ */
+
+#include "rcv.h"
+#include "extern.h"
+
+static int c_erase; /* Current erase char */
+static int c_kill; /* Current kill char */
+static jmp_buf rewrite; /* Place to go when continued */
+static jmp_buf intjmp; /* Place to go when interrupted */
+#ifndef TIOCSTI
+static int ttyset; /* We must now do erase/kill */
+#endif
+
+/*
+ * Read all relevant header fields.
+ */
+
+int
+grabh(hp, gflags)
+ struct header *hp;
+ int gflags;
+{
+ struct sgttyb ttybuf;
+ sig_t saveint;
+#ifndef TIOCSTI
+ sig_t savequit;
+#endif
+ sig_t savetstp;
+ sig_t savettou;
+ sig_t savettin;
+ int errs;
+ void ttyint();
+
+ savetstp = signal(SIGTSTP, SIG_DFL);
+ savettou = signal(SIGTTOU, SIG_DFL);
+ savettin = signal(SIGTTIN, SIG_DFL);
+ errs = 0;
+#ifndef TIOCSTI
+ ttyset = 0;
+#endif
+ if (ioctl(fileno(stdin), TIOCGETP, &ttybuf) < 0) {
+ perror("gtty");
+ return(-1);
+ }
+ c_erase = ttybuf.sg_erase;
+ c_kill = ttybuf.sg_kill;
+#ifndef TIOCSTI
+ ttybuf.sg_erase = 0;
+ ttybuf.sg_kill = 0;
+ if ((saveint = signal(SIGINT, SIG_IGN)) == SIG_DFL)
+ signal(SIGINT, SIG_DFL);
+ if ((savequit = signal(SIGQUIT, SIG_IGN)) == SIG_DFL)
+ signal(SIGQUIT, SIG_DFL);
+#else
+ if (setjmp(intjmp))
+ goto out;
+ saveint = signal(SIGINT, ttyint);
+#endif
+ if (gflags & GTO) {
+#ifndef TIOCSTI
+ if (!ttyset && hp->h_to != NIL)
+ ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+ hp->h_to =
+ extract(readtty("To: ", detract(hp->h_to, 0)), GTO);
+ }
+ if (gflags & GSUBJECT) {
+#ifndef TIOCSTI
+ if (!ttyset && hp->h_subject != NOSTR)
+ ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+ hp->h_subject = readtty("Subject: ", hp->h_subject);
+ }
+ if (gflags & GCC) {
+#ifndef TIOCSTI
+ if (!ttyset && hp->h_cc != NIL)
+ ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+ hp->h_cc =
+ extract(readtty("Cc: ", detract(hp->h_cc, 0)), GCC);
+ }
+ if (gflags & GBCC) {
+#ifndef TIOCSTI
+ if (!ttyset && hp->h_bcc != NIL)
+ ttyset++, stty(fileno(stdin), &ttybuf);
+#endif
+ hp->h_bcc =
+ extract(readtty("Bcc: ", detract(hp->h_bcc, 0)), GBCC);
+ }
+out:
+ signal(SIGTSTP, savetstp);
+ signal(SIGTTOU, savettou);
+ signal(SIGTTIN, savettin);
+#ifndef TIOCSTI
+ ttybuf.sg_erase = c_erase;
+ ttybuf.sg_kill = c_kill;
+ if (ttyset)
+ stty(fileno(stdin), &ttybuf);
+ signal(SIGQUIT, savequit);
+#endif
+ signal(SIGINT, saveint);
+ return(errs);
+}
+
+/*
+ * Read up a header from standard input.
+ * The source string has the preliminary contents to
+ * be read.
+ *
+ */
+
+char *
+readtty(pr, src)
+ char pr[], src[];
+{
+ char ch, canonb[BUFSIZ];
+ int c;
+ register char *cp, *cp2;
+ void ttystop();
+
+ fputs(pr, stdout);
+ fflush(stdout);
+ if (src != NOSTR && strlen(src) > BUFSIZ - 2) {
+ printf("too long to edit\n");
+ return(src);
+ }
+#ifndef TIOCSTI
+ if (src != NOSTR)
+ cp = copy(src, canonb);
+ else
+ cp = copy("", canonb);
+ fputs(canonb, stdout);
+ fflush(stdout);
+#else
+ cp = src == NOSTR ? "" : src;
+ while (c = *cp++) {
+ if (c == c_erase || c == c_kill) {
+ ch = '\\';
+ ioctl(0, TIOCSTI, &ch);
+ }
+ ch = c;
+ ioctl(0, TIOCSTI, &ch);
+ }
+ cp = canonb;
+ *cp = 0;
+#endif
+ cp2 = cp;
+ while (cp2 < canonb + BUFSIZ)
+ *cp2++ = 0;
+ cp2 = cp;
+ if (setjmp(rewrite))
+ goto redo;
+ signal(SIGTSTP, ttystop);
+ signal(SIGTTOU, ttystop);
+ signal(SIGTTIN, ttystop);
+ clearerr(stdin);
+ while (cp2 < canonb + BUFSIZ) {
+ c = getc(stdin);
+ if (c == EOF || c == '\n')
+ break;
+ *cp2++ = c;
+ }
+ *cp2 = 0;
+ signal(SIGTSTP, SIG_DFL);
+ signal(SIGTTOU, SIG_DFL);
+ signal(SIGTTIN, SIG_DFL);
+ if (c == EOF && ferror(stdin)) {
+redo:
+ cp = strlen(canonb) > 0 ? canonb : NOSTR;
+ clearerr(stdin);
+ return(readtty(pr, cp));
+ }
+#ifndef TIOCSTI
+ if (cp == NOSTR || *cp == '\0')
+ return(src);
+ cp2 = cp;
+ if (!ttyset)
+ return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR);
+ while (*cp != '\0') {
+ c = *cp++;
+ if (c == c_erase) {
+ if (cp2 == canonb)
+ continue;
+ if (cp2[-1] == '\\') {
+ cp2[-1] = c;
+ continue;
+ }
+ cp2--;
+ continue;
+ }
+ if (c == c_kill) {
+ if (cp2 == canonb)
+ continue;
+ if (cp2[-1] == '\\') {
+ cp2[-1] = c;
+ continue;
+ }
+ cp2 = canonb;
+ continue;
+ }
+ *cp2++ = c;
+ }
+ *cp2 = '\0';
+#endif
+ if (equal("", canonb))
+ return(NOSTR);
+ return(savestr(canonb));
+}
+
+/*
+ * Receipt continuation.
+ */
+void
+ttystop(s)
+ int s;
+{
+ sig_t old_action = signal(s, SIG_DFL);
+
+ sigsetmask(sigblock(0) & ~sigmask(s));
+ kill(0, s);
+ sigblock(sigmask(s));
+ signal(s, old_action);
+ longjmp(rewrite, 1);
+}
+
+/*ARGSUSED*/
+void
+ttyint(s)
+ int s;
+{
+ longjmp(intjmp, 1);
+}
diff --git a/usr.bin/mail/v7.local.c b/usr.bin/mail/v7.local.c
new file mode 100644
index 0000000..5144c01
--- /dev/null
+++ b/usr.bin/mail/v7.local.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v7.local.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Mail -- a mail program
+ *
+ * Version 7
+ *
+ * Local routines that are installation dependent.
+ */
+
+#include "rcv.h"
+#include <fcntl.h>
+#include "extern.h"
+
+/*
+ * Locate the user's mailbox file (ie, the place where new, unread
+ * mail is queued).
+ */
+void
+findmail(user, buf)
+ char *user, *buf;
+{
+ (void)sprintf(buf, "%s/%s", _PATH_MAILDIR, user);
+}
+
+/*
+ * Get rid of the queued mail.
+ */
+void
+demail()
+{
+
+ if (value("keep") != NOSTR || rm(mailname) < 0)
+ close(creat(mailname, 0600));
+}
+
+/*
+ * Discover user login name.
+ */
+char *
+username()
+{
+ char *np;
+
+ if ((np = getenv("USER")) != NOSTR)
+ return np;
+ return getname(getuid());
+}
diff --git a/usr.bin/mail/vars.c b/usr.bin/mail/vars.c
new file mode 100644
index 0000000..2a90442
--- /dev/null
+++ b/usr.bin/mail/vars.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vars.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "rcv.h"
+#include "extern.h"
+
+/*
+ * Mail -- a mail program
+ *
+ * Variable handling stuff.
+ */
+
+/*
+ * Assign a value to a variable.
+ */
+void
+assign(name, value)
+ char name[], value[];
+{
+ register struct var *vp;
+ register int h;
+
+ h = hash(name);
+ vp = lookup(name);
+ if (vp == NOVAR) {
+ vp = (struct var *) calloc(sizeof *vp, 1);
+ vp->v_name = vcopy(name);
+ vp->v_link = variables[h];
+ variables[h] = vp;
+ }
+ else
+ vfree(vp->v_value);
+ vp->v_value = vcopy(value);
+}
+
+/*
+ * Free up a variable string. We do not bother to allocate
+ * strings whose value is "" since they are expected to be frequent.
+ * Thus, we cannot free same!
+ */
+void
+vfree(cp)
+ char *cp;
+{
+ if (*cp)
+ free(cp);
+}
+
+/*
+ * Copy a variable value into permanent (ie, not collected after each
+ * command) space. Do not bother to alloc space for ""
+ */
+
+char *
+vcopy(str)
+ char str[];
+{
+ char *new;
+ unsigned len;
+
+ if (*str == '\0')
+ return "";
+ len = strlen(str) + 1;
+ if ((new = malloc(len)) == NULL)
+ panic("Out of memory");
+ bcopy(str, new, (int) len);
+ return new;
+}
+
+/*
+ * Get the value of a variable and return it.
+ * Look in the environment if its not available locally.
+ */
+
+char *
+value(name)
+ char name[];
+{
+ register struct var *vp;
+
+ if ((vp = lookup(name)) == NOVAR)
+ return(getenv(name));
+ return(vp->v_value);
+}
+
+/*
+ * Locate a variable and return its variable
+ * node.
+ */
+
+struct var *
+lookup(name)
+ register char name[];
+{
+ register struct var *vp;
+
+ for (vp = variables[hash(name)]; vp != NOVAR; vp = vp->v_link)
+ if (*vp->v_name == *name && equal(vp->v_name, name))
+ return(vp);
+ return(NOVAR);
+}
+
+/*
+ * Locate a group name and return it.
+ */
+
+struct grouphead *
+findgroup(name)
+ register char name[];
+{
+ register struct grouphead *gh;
+
+ for (gh = groups[hash(name)]; gh != NOGRP; gh = gh->g_link)
+ if (*gh->g_name == *name && equal(gh->g_name, name))
+ return(gh);
+ return(NOGRP);
+}
+
+/*
+ * Print a group out on stdout
+ */
+void
+printgroup(name)
+ char name[];
+{
+ register struct grouphead *gh;
+ register struct group *gp;
+
+ if ((gh = findgroup(name)) == NOGRP) {
+ printf("\"%s\": not a group\n", name);
+ return;
+ }
+ printf("%s\t", gh->g_name);
+ for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link)
+ printf(" %s", gp->ge_name);
+ putchar('\n');
+}
+
+/*
+ * Hash the passed string and return an index into
+ * the variable or group hash table.
+ */
+int
+hash(name)
+ register char *name;
+{
+ register h = 0;
+
+ while (*name) {
+ h <<= 2;
+ h += *name++;
+ }
+ if (h < 0 && (h = -h) < 0)
+ h = 0;
+ return (h % HSHSIZE);
+}
diff --git a/usr.bin/mail/version.c b/usr.bin/mail/version.c
new file mode 100644
index 0000000..c7d39c3
--- /dev/null
+++ b/usr.bin/mail/version.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)version.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Just keep track of the date/sid of this version of Mail.
+ * Load this file first to get a "total" Mail version.
+ */
+char *version = "8.1 6/6/93";
diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile
new file mode 100644
index 0000000..09142d5
--- /dev/null
+++ b/usr.bin/make/Makefile
@@ -0,0 +1,15 @@
+# from: @(#)Makefile 5.2 (Berkeley) 12/28/90
+# $Id: Makefile,v 1.4 1995/06/16 22:46:38 ache Exp $
+
+PROG= make
+CFLAGS+= -I${.CURDIR} -DPOSIX -DSYSVINCLUDE
+SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \
+ make.c parse.c str.c suff.c targ.c var.c
+SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \
+ lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \
+ lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \
+ lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \
+ lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c
+.PATH: ${.CURDIR}/lst.lib
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/make/Makefile.dist b/usr.bin/make/Makefile.dist
new file mode 100644
index 0000000..c311d7e
--- /dev/null
+++ b/usr.bin/make/Makefile.dist
@@ -0,0 +1,7 @@
+# a very simple makefile...
+pmake:
+ @echo 'make started.'
+ cc -I. -c *.c
+ cd lst.lib; cc -I.. -c *.c
+ cc *.o lst.lib/*.o -o pmake
+ @echo 'make completed.'
diff --git a/usr.bin/make/PSD.doc/Makefile b/usr.bin/make/PSD.doc/Makefile
new file mode 100644
index 0000000..acac84f
--- /dev/null
+++ b/usr.bin/make/PSD.doc/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 8/14/93
+
+DIR= psd/12.make
+SRCS= tutorial.ms
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/make/PSD.doc/tutorial.ms b/usr.bin/make/PSD.doc/tutorial.ms
new file mode 100644
index 0000000..eca49d9
--- /dev/null
+++ b/usr.bin/make/PSD.doc/tutorial.ms
@@ -0,0 +1,3732 @@
+.\" Copyright (c) 1988, 1989 by Adam de Boor
+.\" Copyright (c) 1989 by Berkeley Softworks
+.\" Copyright (c) 1988, 1989, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Adam de Boor.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93
+.\"
+.EH 'PSD:12-%''PMake \*- A Tutorial'
+.OH 'PMake \*- A Tutorial''PSD:12-%'
+.\" xH is a macro to provide numbered headers that are automatically stuffed
+.\" into a table-of-contents, properly indented, etc. If the first argument
+.\" is numeric, it is taken as the depth for numbering (as for .NH), else
+.\" the default (1) is assumed.
+.\"
+.\" $Id: tutorial.ms,v 1.4 89/01/08 20:20:22 adam Exp Locker: adam $
+.\"
+.\" @P The initial paragraph distance.
+.\" @Q The piece of section number to increment (or 0 if none given)
+.\" @R Section header.
+.\" @S Indent for toc entry
+.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter)
+.de xH
+.NH \\$1
+\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.nr PD .1v
+.XS \\n%
+.ta 0.6i
+\\*(SN \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.XE
+.nr PD .3v
+..
+.\" CW is used to place a string in fixed-width or switch to a
+.\" fixed-width font.
+.\" C is a typewriter font for a laserwriter. Use something else if
+.\" you don't have one...
+.de CW
+.ie !\\n(.$ .ft C
+.el \&\\$3\fC\\$1\fP\\$2
+..
+.\" Anything I put in a display I want to be in fixed-width
+.am DS
+.CW
+..
+.\" The stuff in .No produces a little stop sign in the left margin
+.\" that says NOTE in it. Unfortunately, it does cause a break, but
+.\" hey. Can't have everything. In case you're wondering how I came
+.\" up with such weird commands, they came from running grn on a
+.\" gremlin file...
+.de No
+.br
+.ne 0.5i
+.po -0.5i
+.br
+.mk
+.nr g3 \\n(.f
+.nr g4 \\n(.s
+.sp -1
+.\" .st cf
+\D's -1u'\D't 5u'
+.sp -1
+\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u'
+.sp -1
+\D't 3u'
+.sp -1
+.sp 7u
+\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u'
+.sp -1
+.ft R
+.ps 6
+.nr g8 \\n(.d
+.ds g9 "NOTE
+.sp 74u
+\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9
+.sp |\\n(g8u
+.sp 166u
+\D't 3u'\D's -1u'
+.br
+.po
+.rt
+.ft \\n(g3
+.ps \\n(g4
+..
+.de Bp
+.ie !\\n(.$ .IP \(bu 2
+.el .IP "\&" 2
+..
+.po +.3i
+.TL
+PMake \*- A Tutorial
+.AU
+Adam de Boor
+.AI
+Berkeley Softworks
+2150 Shattuck Ave, Penthouse
+Berkeley, CA 94704
+adam@bsw.uu.net
+\&...!uunet!bsw!adam
+.FS
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies.
+The University of California, Berkeley Softworks, and Adam de Boor make no
+representations about the suitability of this software for any
+purpose. It is provided "as is" without express or implied warranty.
+.FE
+.PP
+.xH 1 Introduction
+.LP
+PMake is a program for creating other programs, or anything else you
+can think of for it to do. The basic idea behind PMake is that, for
+any given system, be it a program or a document or whatever, there
+will be some files that depend on the state of other files (on when
+they were last modified). PMake takes these dependencies, which you
+must specify, and uses them to build whatever it is you want it to
+build.
+.LP
+PMake is almost fully-compatible with Make, with which you may already
+be familiar. PMake's most important feature is its ability to run
+several different jobs at once, making the creation of systems
+considerably faster. It also has a great deal more functionality than
+Make. Throughout the text, whenever something is mentioned that is an
+important difference between PMake and Make (i.e. something that will
+cause a makefile to fail if you don't do something about it), or is
+simply important, it will be flagged with a little sign in the left
+margin, like this:
+.No
+.LP
+This tutorial is divided into three main sections corresponding to basic,
+intermediate and advanced PMake usage. If you already know Make well,
+you will only need to skim chapter 2 (there are some aspects of
+PMake that I consider basic to its use that didn't exist in Make).
+Things in chapter 3 make life much easier, while those in chapter 4
+are strictly for those who know what they are doing. Chapter 5 has
+definitions for the jargon I use and chapter 6 contains possible
+solutions to the problems presented throughout the tutorial.
+.xH 1 The Basics of PMake
+.LP
+PMake takes as input a file that tells a) which files depend on which
+other files to be complete and b) what to do about files that are
+``out-of-date.'' This file is known as a ``makefile'' and is usually
+.Ix 0 def makefile
+kept in the top-most directory of the system to be built. While you
+can call the makefile anything you want, PMake will look for
+.CW Makefile
+and
+.CW makefile
+(in that order) in the current directory if you don't tell it
+otherwise.
+.Ix 0 def makefile default
+To specify a different makefile, use the
+.B \-f
+flag (e.g.
+.CW "pmake -f program.mk" ''). ``
+.Ix 0 ref flags -f
+.Ix 0 ref makefile other
+.LP
+A makefile has four different types of lines in it:
+.RS
+.IP \(bu 2
+File dependency specifications
+.IP \(bu 2
+Creation commands
+.IP \(bu 2
+Variable assignments
+.IP \(bu 2
+Comments, include statements and conditional directives
+.RE
+.LP
+Any line may be continued over multiple lines by ending it with a
+backslash.
+.Ix 0 def "continuation line"
+The backslash, following newline and any initial whitespace
+on the following line are compressed into a single space before the
+input line is examined by PMake.
+.xH 2 Dependency Lines
+.LP
+As mentioned in the introduction, in any system, there are
+dependencies between the files that make up the system. For instance,
+in a program made up of several C source files and one header file,
+the C files will need to be re-compiled should the header file be
+changed. For a document of several chapters and one macro file, the
+chapters will need to be reprocessed if any of the macros changes.
+.Ix 0 def "dependency"
+These are dependencies and are specified by means of dependency lines in
+the makefile.
+.LP
+.Ix 0 def "dependency line"
+On a dependency line, there are targets and sources, separated by a
+one- or two-character operator.
+The targets ``depend'' on the sources and are usually created from
+them.
+.Ix 0 def target
+.Ix 0 def source
+.Ix 0 ref operator
+Any number of targets and sources may be specified on a dependency line.
+All the targets in the line are made to depend on all the sources.
+Targets and sources need not be actual files, but every source must be
+either an actual file or another target in the makefile.
+If you run out of room, use a backslash at the end of the line to continue onto
+the next one.
+.LP
+Any file may be a target and any file may be a source, but the
+relationship between the two (or however many) is determined by the
+``operator'' that separates them.
+.Ix 0 def operator
+Three types of operators exist: one specifies that the datedness of a
+target is determined by the state of its sources, while another
+specifies other files (the sources) that need to be dealt with before
+the target can be re-created. The third operator is very similar to
+the first, with the additional condition that the target is
+out-of-date if it has no sources. These operations are represented by
+the colon, the exclamation point and the double-colon, respectively, and are
+mutually exclusive. Their exact semantics are as follows:
+.IP ":"
+.Ix 0 def operator colon
+.Ix 0 def :
+If a colon is used, a target on the line is considered to be
+``out-of-date'' (and in need of creation) if
+.RS
+.IP \(bu 2
+any of the sources has been modified more recently than the target, or
+.IP \(bu 2
+the target doesn't exist.
+.RE
+.Ix 0 def out-of-date
+.IP "\&"
+Under this operation, steps will be taken to re-create the target only
+if it is found to be out-of-date by using these two rules.
+.IP "!"
+.Ix 0 def operator force
+.Ix 0 def !
+If an exclamation point is used, the target will always be re-created,
+but this will not happen until all of its sources have been examined
+and re-created, if necessary.
+.IP "::"
+.Ix 0 def operator double-colon
+.Ix 0 def ::
+If a double-colon is used, a target is out-of-date if:
+.RS
+.IP \(bu 2
+any of the sources has been modified more recently than the target, or
+.IP \(bu 2
+the target doesn't exist, or
+.IP \(bu 2
+the target has no sources.
+.RE
+.IP "\&"
+If the target is out-of-date according to these rules, it will be re-created.
+This operator also does something else to the targets, but I'll go
+into that in the next section (``Shell Commands'').
+.LP
+Enough words, now for an example. Take that C program I mentioned
+earlier. Say there are three C files
+.CW a.c , (
+.CW b.c
+and
+.CW c.c )
+each of which
+includes the file
+.CW defs.h .
+The dependencies between the files could then be expressed as follows:
+.DS
+program : a.o b.o c.o
+a.o b.o c.o : defs.h
+a.o : a.c
+b.o : b.c
+c.o : c.c
+.DE
+.LP
+You may be wondering at this point, where
+.CW a.o ,
+.CW b.o
+and
+.CW c.o
+came in and why
+.I they
+depend on
+.CW defs.h
+and the C files don't. The reason is quite simple:
+.CW program
+cannot be made by linking together .c files \*- it must be
+made from .o files. Likewise, if you change
+.CW defs.h ,
+it isn't the .c files that need to be re-created, it's the .o files.
+If you think of dependencies in these terms \*- which files (targets)
+need to be created from which files (sources) \*- you should have no problems.
+.LP
+An important thing to notice about the above example, is that all the
+\&.o files appear as targets on more than one line. This is perfectly
+all right: the target is made to depend on all the sources mentioned
+on all the dependency lines. E.g.
+.CW a.o
+depends on both
+.CW defs.h
+and
+.CW a.c .
+.Ix 0 ref dependency
+.No
+.LP
+The order of the dependency lines in the makefile is
+important: the first target on the first dependency line in the
+makefile will be the one that gets made if you don't say otherwise.
+That's why
+.CW program
+comes first in the example makefile, above.
+.LP
+Both targets and sources may contain the standard C-Shell wildcard
+characters
+.CW { , (
+.CW } ,
+.CW * ,
+.CW ? ,
+.CW [ ,
+and
+.CW ] ),
+but the non-curly-brace ones may only appear in the final component
+(the file portion) of the target or source. The characters mean the
+following things:
+.IP \fB{}\fP
+These enclose a comma-separated list of options and cause the pattern
+to be expanded once for each element of the list. Each expansion
+contains a different element. For example,
+.CW src/{whiffle,beep,fish}.c
+expands to the three words
+.CW src/whiffle.c ,
+.CW src/beep.c ,
+and
+.CW src/fish.c .
+These braces may be nested and, unlike the other wildcard characters,
+the resulting words need not be actual files. All other wildcard
+characters are expanded using the files that exist when PMake is
+started.
+.IP \fB*\fP
+This matches zero or more characters of any sort.
+.CW src/*.c
+will expand to the same three words as above as long as
+.CW src
+contains those three files (and no other files that end in
+.CW .c ).
+.IP \fB?\fP
+Matches any single character.
+.IP \fB[]\fP
+This is known as a character class and contains either a list of
+single characters, or a series of character ranges
+.CW a-z , (
+for example means all characters between a and z), or both. It matches
+any single character contained in the list. E.g.
+.CW [A-Za-z]
+will match all letters, while
+.CW [0123456789]
+will match all numbers.
+.xH 2 Shell Commands
+.LP
+``Isn't that nice,'' you say to yourself, ``but how are files
+actually `re-created,' as he likes to spell it?''
+The re-creation is accomplished by commands you place in the makefile.
+These commands are passed to the Bourne shell (better known as
+``/bin/sh'') to be executed and are
+.Ix 0 ref shell
+.Ix 0 ref re-creation
+.Ix 0 ref update
+expected to do what's necessary to update the target file (PMake
+doesn't actually check to see if the target was created. It just
+assumes it's there).
+.Ix 0 ref target
+.LP
+Shell commands in a makefile look a lot like shell commands you would
+type at a terminal, with one important exception: each command in a
+makefile
+.I must
+be preceded by at least one tab.
+.LP
+Each target has associated with it a shell script made up of
+one or more of these shell commands. The creation script for a target
+should immediately follow the dependency line for that target. While
+any given target may appear on more than one dependency line, only one
+of these dependency lines may be followed by a creation script, unless
+the `::' operator was used on the dependency line.
+.Ix 0 ref operator double-colon
+.Ix 0 ref ::
+.No
+.LP
+If the double-colon was used, each dependency line for the target
+may be followed by a shell script. That script will only be executed
+if the target on the associated dependency line is out-of-date with
+respect to the sources on that line, according to the rules I gave
+earlier.
+I'll give you a good example of this later on.
+.LP
+To expand on the earlier makefile, you might add commands as follows:
+.DS
+program : a.o b.o c.o
+ cc a.o b.o c.o \-o program
+a.o b.o c.o : defs.h
+a.o : a.c
+ cc \-c a.c
+b.o : b.c
+ cc \-c b.c
+c.o : c.c
+ cc \-c c.c
+.DE
+.LP
+Something you should remember when writing a makefile is, the
+commands will be executed if the
+.I target
+on the dependency line is out-of-date, not the sources.
+.Ix 0 ref target
+.Ix 0 ref source
+.Ix 0 ref out-of-date
+In this example, the command
+.CW "cc \-c a.c" '' ``
+will be executed if
+.CW a.o
+is out-of-date. Because of the `:' operator,
+.Ix 0 ref :
+.Ix 0 ref operator colon
+this means that should
+.CW a.c
+.I or
+.CW defs.h
+have been modified more recently than
+.CW a.o ,
+the command will be executed
+.CW a.o "\&" (
+will be considered out-of-date).
+.Ix 0 ref out-of-date
+.LP
+Remember how I said the only difference between a makefile shell
+command and a regular shell command was the leading tab? I lied. There
+is another way in which makefile commands differ from regular ones.
+The first two characters after the initial whitespace are treated
+specially.
+If they are any combination of `@' and `\-', they cause PMake to do
+different things.
+.LP
+In most cases, shell commands are printed before they're
+actually executed. This is to keep you informed of what's going on. If
+an `@' appears, however, this echoing is suppressed. In the case of an
+.CW echo
+command, say
+.CW "echo Linking index" ,'' ``
+it would be
+rather silly to see
+.DS
+echo Linking index
+Linking index
+.DE
+.LP
+so PMake allows you to place an `@' before the command
+.CW "@echo Linking index" '') (``
+to prevent the command from being printed.
+.LP
+The other special character is the `\-'. In case you didn't know,
+shell commands finish with a certain ``exit status.'' This status is
+made available by the operating system to whatever program invoked the
+command. Normally this status will be 0 if everything went ok and
+non-zero if something went wrong. For this reason, PMake will consider
+an error to have occurred if one of the shells it invokes returns a non-zero
+status. When it detects an error, PMake's usual action is to abort
+whatever it's doing and exit with a non-zero status itself (any other
+targets that were being created will continue being made, but nothing
+new will be started. PMake will exit after the last job finishes).
+This behavior can be altered, however, by placing a `\-' at the front
+of a command
+.CW "\-mv index index.old" ''), (``
+certain command-line arguments,
+or doing other things, to be detailed later. In such
+a case, the non-zero status is simply ignored and PMake keeps chugging
+along.
+.No
+.LP
+Because all the commands are given to a single shell to execute, such
+things as setting shell variables, changing directories, etc., last
+beyond the command in which they are found. This also allows shell
+compound commands (like
+.CW for
+loops) to be entered in a natural manner.
+Since this could cause problems for some makefiles that depend on
+each command being executed by a single shell, PMake has a
+.B \-B
+.Ix 0 ref compatibility
+.Ix 0 ref flags -B
+flag (it stands for backwards-compatible) that forces each command to
+be given to a separate shell. It also does several other things, all
+of which I discourage since they are now old-fashioned.\|.\|.\|.
+.No
+.LP
+A target's shell script is fed to the shell on its (the shell's) input stream.
+This means that any commands, such as
+.CW ci
+that need to get input from the terminal won't work right \*- they'll
+get the shell's input, something they probably won't find to their
+liking. A simple way around this is to give a command like this:
+.DS
+ci $(SRCS) < /dev/tty
+.DE
+This would force the program's input to come from the terminal. If you
+can't do this for some reason, your only other alternative is to use
+PMake in its fullest compatibility mode. See
+.B Compatibility
+in chapter 4.
+.Ix 0 ref compatibility
+.LP
+.xH 2 Variables
+.LP
+PMake, like Make before it, has the ability to save text in variables
+to be recalled later at your convenience. Variables in PMake are used
+much like variables in the shell and, by tradition, consist of
+all upper-case letters (you don't
+.I have
+to use all upper-case letters.
+In fact there's nothing to stop you from calling a variable
+.CW @^&$%$ .
+Just tradition). Variables are assigned-to using lines of the form
+.Ix 0 def variable assignment
+.DS
+VARIABLE = value
+.DE
+.Ix 0 def variable assignment
+appended-to by
+.DS
+VARIABLE += value
+.DE
+.Ix 0 def variable appending
+.Ix 0 def variable assignment appended
+.Ix 0 def +=
+conditionally assigned-to (if the variable isn't already defined) by
+.DS
+VARIABLE ?= value
+.DE
+.Ix 0 def variable assignment conditional
+.Ix 0 def ?=
+and assigned-to with expansion (i.e. the value is expanded (see below)
+before being assigned to the variable\*-useful for placing a value at
+the beginning of a variable, or other things) by
+.DS
+VARIABLE := value
+.DE
+.Ix 0 def variable assignment expanded
+.Ix 0 def :=
+.LP
+Any whitespace before
+.I value
+is stripped off. When appending, a space is placed between the old
+value and the stuff being appended.
+.LP
+The final way a variable may be assigned to is using
+.DS
+VARIABLE != shell-command
+.DE
+.Ix 0 def variable assignment shell-output
+.Ix 0 def !=
+In this case,
+.I shell-command
+has all its variables expanded (see below) and is passed off to a
+shell to execute. The output of the shell is then placed in the
+variable. Any newlines (other than the final one) are replaced by
+spaces before the assignment is made. This is typically used to find
+the current directory via a line like:
+.DS
+CWD != pwd
+.DE
+.LP
+.B Note:
+this is intended to be used to execute commands that produce small amounts
+of output (e.g. ``pwd''). The implementation is less than intelligent and will
+likely freeze if you execute something that produces thousands of
+bytes of output (8 Kb is the limit on many UNIX systems).
+.LP
+The value of a variable may be retrieved by enclosing the variable
+name in parentheses or curly braces and preceeding the whole thing
+with a dollar sign.
+.LP
+For example, to set the variable CFLAGS to the string
+.CW "\-I/sprite/src/lib/libc \-O" ,'' ``
+you would place a line
+.DS
+CFLAGS = \-I/sprite/src/lib/libc \-O
+.DE
+in the makefile and use the word
+.CW "$(CFLAGS)"
+wherever you would like the string
+.CW "\-I/sprite/src/lib/libc \-O"
+to appear. This is called variable expansion.
+.Ix 0 def variable expansion
+.No
+.LP
+Unlike Make, PMake will not expand a variable unless it knows
+the variable exists. E.g. if you have a
+.CW "${i}"
+in a shell command and you have not assigned a value to the variable
+.CW i
+(the empty string is considered a value, by the way), where Make would have
+substituted the empty string, PMake will leave the
+.CW "${i}"
+alone.
+To keep PMake from substituting for a variable it knows, precede the
+dollar sign with another dollar sign.
+(e.g. to pass
+.CW "${HOME}"
+to the shell, use
+.CW "$${HOME}" ).
+This causes PMake, in effect, to expand the
+.CW $
+macro, which expands to a single
+.CW $ .
+For compatibility, Make's style of variable expansion will be used
+if you invoke PMake with any of the compatibility flags (\c
+.B \-V ,
+.B \-B
+or
+.B \-M .
+The
+.B \-V
+flag alters just the variable expansion).
+.Ix 0 ref flags -V
+.Ix 0 ref flags -B
+.Ix 0 ref flags -M
+.Ix 0 ref compatibility
+.LP
+.Ix 0 ref variable expansion
+There are two different times at which variable expansion occurs:
+When parsing a dependency line, the expansion occurs immediately
+upon reading the line. If any variable used on a dependency line is
+undefined, PMake will print a message and exit.
+Variables in shell commands are expanded when the command is
+executed.
+Variables used inside another variable are expanded whenever the outer
+variable is expanded (the expansion of an inner variable has no effect
+on the outer variable. I.e. if the outer variable is used on a dependency
+line and in a shell command, and the inner variable changes value
+between when the dependency line is read and the shell command is
+executed, two different values will be substituted for the outer
+variable).
+.Ix 0 def variable types
+.LP
+Variables come in four flavors, though they are all expanded the same
+and all look about the same. They are (in order of expanding scope):
+.RS
+.IP \(bu 2
+Local variables.
+.Ix 0 ref variable local
+.IP \(bu 2
+Command-line variables.
+.Ix 0 ref variable command-line
+.IP \(bu 2
+Global variables.
+.Ix 0 ref variable global
+.IP \(bu 2
+Environment variables.
+.Ix 0 ref variable environment
+.RE
+.LP
+The classification of variables doesn't matter much, except that the
+classes are searched from the top (local) to the bottom (environment)
+when looking up a variable. The first one found wins.
+.xH 3 Local Variables
+.LP
+.Ix 0 def variable local
+Each target can have as many as seven local variables. These are
+variables that are only ``visible'' within that target's shell script
+and contain such things as the target's name, all of its sources (from
+all its dependency lines), those sources that were out-of-date, etc.
+Four local variables are defined for all targets. They are:
+.RS
+.IP ".TARGET"
+.Ix 0 def variable local .TARGET
+.Ix 0 def .TARGET
+The name of the target.
+.IP ".OODATE"
+.Ix 0 def variable local .OODATE
+.Ix 0 def .OODATE
+The list of the sources for the target that were considered out-of-date.
+The order in the list is not guaranteed to be the same as the order in
+which the dependencies were given.
+.IP ".ALLSRC"
+.Ix 0 def variable local .ALLSRC
+.Ix 0 def .ALLSRC
+The list of all sources for this target in the order in which they
+were given.
+.IP ".PREFIX"
+.Ix 0 def variable local .PREFIX
+.Ix 0 def .PREFIX
+The target without its suffix and without any leading path. E.g. for
+the target
+.CW ../../lib/compat/fsRead.c ,
+this variable would contain
+.CW fsRead .
+.RE
+.LP
+Three other local variables are set only for certain targets under
+special circumstances. These are the ``.IMPSRC,''
+.Ix 0 ref variable local .IMPSRC
+.Ix 0 ref .IMPSRC
+``.ARCHIVE,''
+.Ix 0 ref variable local .ARCHIVE
+.Ix 0 ref .ARCHIVE
+and ``.MEMBER''
+.Ix 0 ref variable local .MEMBER
+.Ix 0 ref .MEMBER
+variables. When they are set and how they are used is described later.
+.LP
+Four of these variables may be used in sources as well as in shell
+scripts.
+.Ix 0 def "dynamic source"
+.Ix 0 def source dynamic
+These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The
+variables in the sources are expanded once for each target on the
+dependency line, providing what is known as a ``dynamic source,''
+.Rd 0
+allowing you to specify several dependency lines at once. For example,
+.DS
+$(OBJS) : $(.PREFIX).c
+.DE
+will create a dependency between each object file and its
+corresponding C source file.
+.xH 3 Command-line Variables
+.LP
+.Ix 0 def variable command-line
+Command-line variables are set when PMake is first invoked by giving a
+variable assignment as one of the arguments. For example,
+.DS
+pmake "CFLAGS = -I/sprite/src/lib/libc -O"
+.DE
+would make
+.CW CFLAGS
+be a command-line variable with the given value. Any assignments to
+.CW CFLAGS
+in the makefile will have no effect, because once it
+is set, there is (almost) nothing you can do to change a command-line
+variable (the search order, you see). Command-line variables may be
+set using any of the four assignment operators, though only
+.CW =
+and
+.CW ?=
+behave as you would expect them to, mostly because assignments to
+command-line variables are performed before the makefile is read, thus
+the values set in the makefile are unavailable at the time.
+.CW +=
+.Ix 0 ref +=
+.Ix 0 ref variable assignment appended
+is the same as
+.CW = ,
+because the old value of the variable is sought only in the scope in
+which the assignment is taking place (for reasons of efficiency that I
+won't get into here).
+.CW :=
+and
+.CW ?=
+.Ix 0 ref :=
+.Ix 0 ref ?=
+.Ix 0 ref variable assignment expanded
+.Ix 0 ref variable assignment conditional
+will work if the only variables used are in the environment.
+.CW !=
+is sort of pointless to use from the command line, since the same
+effect can no doubt be accomplished using the shell's own command
+substitution mechanisms (backquotes and all that).
+.xH 3 Global Variables
+.LP
+.Ix 0 def variable global
+Global variables are those set or appended-to in the makefile.
+There are two classes of global variables: those you set and those PMake sets.
+As I said before, the ones you set can have any name you want them to have,
+except they may not contain a colon or an exclamation point.
+The variables PMake sets (almost) always begin with a
+period and always contain upper-case letters, only. The variables are
+as follows:
+.RS
+.IP .PMAKE
+.Ix 0 def variable global .PMAKE
+.Ix 0 def .PMAKE
+.Ix 0 def variable global MAKE
+.Ix 0 def MAKE
+The name by which PMake was invoked is stored in this variable. For
+compatibility, the name is also stored in the MAKE variable.
+.IP .MAKEFLAGS
+.Ix 0 def variable global .MAKEFLAGS
+.Ix 0 def .MAKEFLAGS variable
+.Ix 0 def variable global MFLAGS
+.Ix 0 def MFLAGS
+All the relevant flags with which PMake was invoked. This does not
+include such things as
+.B \-f
+or variable assignments. Again for compatibility, this value is stored
+in the MFLAGS variable as well.
+.RE
+.LP
+Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the
+section on special targets in chapter 3.
+.Ix 0 ref variable global .INCLUDES
+.Ix 0 ref variable global .LIBS
+.LP
+Global variables may be deleted using lines of the form:
+.Ix 0 def #undef
+.Ix 0 def variable deletion
+.DS
+#undef \fIvariable\fP
+.DE
+The
+.CW # ' `
+must be the first character on the line. Note that this may only be
+done on global variables.
+.xH 3 Environment Variables
+.LP
+.Ix 0 def variable environment
+Environment variables are passed by the shell that invoked PMake and
+are given by PMake to each shell it invokes. They are expanded like
+any other variable, but they cannot be altered in any way.
+.LP
+One special environment variable,
+.CW PMAKE ,
+.Ix 0 def variable environment PMAKE
+is examined by PMake for command-line flags, variable assignments,
+etc., it should always use. This variable is examined before the
+actual arguments to PMake are. In addition, all flags given to PMake,
+either through the
+.CW PMAKE
+variable or on the command line, are placed in this environment
+variable and exported to each shell PMake executes. Thus recursive
+invocations of PMake automatically receive the same flags as the
+top-most one.
+.LP
+Using all these variables, you can compress the sample makefile even more:
+.DS
+OBJS = a.o b.o c.o
+program : $(OBJS)
+ cc $(.ALLSRC) \-o $(.TARGET)
+$(OBJS) : defs.h
+a.o : a.c
+ cc \-c a.c
+b.o : b.c
+ cc \-c b.c
+c.o : c.c
+ cc \-c c.c
+.DE
+.Ix 0 ref variable local .ALLSRC
+.Ix 0 ref .ALLSRC
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref .TARGET
+.Rd 3
+.xH 2 Comments
+.LP
+.Ix 0 def comments
+Comments in a makefile start with a `#' character and extend to the
+end of the line. They may appear
+anywhere you want them, except in a shell command (though the shell
+will treat it as a comment, too). If, for some reason, you need to use the `#'
+in a variable or on a dependency line, put a backslash in front of it.
+PMake will compress the two into a single `#' (Note: this isn't true
+if PMake is operating in full-compatibility mode).
+.Ix 0 ref flags -M
+.Ix 0 ref compatibility
+.xH 2 Parallelism
+.No
+.LP
+PMake was specifically designed to re-create several targets at once,
+when possible. You do not have to do anything special to cause this to
+happen (unless PMake was configured to not act in parallel, in which
+case you will have to make use of the
+.B \-L
+and
+.B \-J
+flags (see below)),
+.Ix 0 ref flags -L
+.Ix 0 ref flags -J
+but you do have to be careful at times.
+.LP
+There are several problems you are likely to encounter. One is
+that some makefiles (and programs) are written in such a way that it is
+impossible for two targets to be made at once. The program
+.CW xstr ,
+for example,
+always modifies the files
+.CW strings
+and
+.CW x.c .
+There is no way to change it. Thus you cannot run two of them at once
+without something being trashed. Similarly, if you have commands
+in the makefile that always send output to the same file, you will not
+be able to make more than one target at once unless you change the
+file you use. You can, for instance, add a
+.CW $$$$
+to the end of the file name to tack on the process ID of the shell
+executing the command (each
+.CW $$
+expands to a single
+.CW $ ,
+thus giving you the shell variable
+.CW $$ ).
+Since only one shell is used for all the
+commands, you'll get the same file name for each command in the
+script.
+.LP
+The other problem comes from improperly-specified dependencies that
+worked in Make because of its sequential, depth-first way of examining
+them. While I don't want to go into depth on how PMake
+works (look in chapter 4 if you're interested), I will warn you that
+files in two different ``levels'' of the dependency tree may be
+examined in a different order in PMake than they were in Make. For
+example, given the makefile
+.DS
+a : b c
+b : d
+.DE
+PMake will examine the targets in the order
+.CW c ,
+.CW d ,
+.CW b ,
+.CW a .
+If the makefile's author expected PMake to abort before making
+.CW c
+if an error occurred while making
+.CW b ,
+or if
+.CW b
+needed to exist before
+.CW c
+was made,
+s/he will be sorely disappointed. The dependencies are
+incomplete, since in both these cases,
+.CW c
+would depend on
+.CW b .
+So watch out.
+.LP
+Another problem you may face is that, while PMake is set up to handle the
+output from multiple jobs in a graceful fashion, the same is not so for input.
+It has no way to regulate input to different jobs,
+so if you use the redirection from
+.CW /dev/tty
+I mentioned earlier, you must be careful not to run two of the jobs at once.
+.xH 2 Writing and Debugging a Makefile
+.LP
+Now you know most of what's in a makefile, what do you do next? There
+are two choices: (1) use one of the uncommonly-available makefile
+generators or (2) write your own makefile (I leave out the third choice of
+ignoring PMake and doing everything by hand as being beyond the bounds
+of common sense).
+.LP
+When faced with the writing of a makefile, it is usually best to start
+from first principles: just what
+.I are
+you trying to do? What do you want the makefile finally to produce?
+.LP
+To begin with a somewhat traditional example, let's say you need to
+write a makefile to create a program,
+.CW expr ,
+that takes standard infix expressions and converts them to prefix form (for
+no readily apparent reason). You've got three source files, in C, that
+make up the program:
+.CW main.c ,
+.CW parse.c ,
+and
+.CW output.c .
+Harking back to my pithy advice about dependency lines, you write the
+first line of the file:
+.DS
+expr : main.o parse.o output.o
+.DE
+because you remember
+.CW expr
+is made from
+.CW .o
+files, not
+.CW .c
+files. Similarly for the
+.CW .o
+files you produce the lines:
+.DS
+main.o : main.c
+parse.o : parse.c
+output.o : output.c
+main.o parse.o output.o : defs.h
+.DE
+.LP
+Great. You've now got the dependencies specified. What you need now is
+commands. These commands, remember, must produce the target on the
+dependency line, usually by using the sources you've listed.
+You remember about local variables? Good, so it should come
+to you as no surprise when you write
+.DS
+expr : main.o parse.o output.o
+ cc -o $(.TARGET) $(.ALLSRC)
+.DE
+Why use the variables? If your program grows to produce postfix
+expressions too (which, of course, requires a name change or two), it
+is one fewer place you have to change the file. You cannot do this for
+the object files, however, because they depend on their corresponding
+source files
+.I and
+.CW defs.h ,
+thus if you said
+.DS
+ cc -c $(.ALLSRC)
+.DE
+you'd get (for
+.CW main.o ):
+.DS
+ cc -c main.c defs.h
+.DE
+which is wrong. So you round out the makefile with these lines:
+.DS
+main.o : main.c
+ cc -c main.c
+parse.o : parse.c
+ cc -c parse.c
+output.o : output.c
+ cc -c output.c
+.DE
+.LP
+The makefile is now complete and will, in fact, create the program you
+want it to without unnecessary compilations or excessive typing on
+your part. There are two things wrong with it, however (aside from it
+being altogether too long, something I'll address in chapter 3):
+.IP 1)
+The string
+.CW "main.o parse.o output.o" '' ``
+is repeated twice, necessitating two changes when you add postfix
+(you were planning on that, weren't you?). This is in direct violation
+of de Boor's First Rule of writing makefiles:
+.QP
+.I
+Anything that needs to be written more than once
+should be placed in a variable.
+.IP "\&"
+I cannot emphasize this enough as being very important to the
+maintenance of a makefile and its program.
+.IP 2)
+There is no way to alter the way compilations are performed short of
+editing the makefile and making the change in all places. This is evil
+and violates de Boor's Second Rule, which follows directly from the
+first:
+.QP
+.I
+Any flags or programs used inside a makefile should be placed in a variable so
+they may be changed, temporarily or permanently, with the greatest ease.
+.LP
+The makefile should more properly read:
+.DS
+OBJS = main.o parse.o output.o
+expr : $(OBJS)
+ $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
+main.o : main.c
+ $(CC) $(CFLAGS) -c main.c
+parse.o : parse.c
+ $(CC) $(CFLAGS) -c parse.c
+output.o : output.c
+ $(CC) $(CFLAGS) -c output.c
+$(OBJS) : defs.h
+.DE
+Alternatively, if you like the idea of dynamic sources mentioned in
+section 2.3.1,
+.Rm 0 2.3.1
+.Rd 4
+.Ix 0 ref "dynamic source"
+.Ix 0 ref source dynamic
+you could write it like this:
+.DS
+OBJS = main.o parse.o output.o
+expr : $(OBJS)
+ $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
+$(OBJS) : $(.PREFIX).c defs.h
+ $(CC) $(CFLAGS) -c $(.PREFIX).c
+.DE
+These two rules and examples lead to de Boor's First Corollary:
+.QP
+.I
+Variables are your friends.
+.LP
+Once you've written the makefile comes the sometimes-difficult task of
+.Ix 0 ref debugging
+making sure the darn thing works. Your most helpful tool to make sure
+the makefile is at least syntactically correct is the
+.B \-n
+.Ix 0 ref flags -n
+flag, which allows you to see if PMake will choke on the makefile. The
+second thing the
+.B \-n
+flag lets you do is see what PMake would do without it actually doing
+it, thus you can make sure the right commands would be executed were
+you to give PMake its head.
+.LP
+When you find your makefile isn't behaving as you hoped, the first
+question that comes to mind (after ``What time is it, anyway?'') is
+``Why not?'' In answering this, two flags will serve you well:
+.CW "-d m" '' ``
+.Ix 0 ref flags -d
+and
+.CW "-p 2" .'' ``
+.Ix 0 ref flags -p
+The first causes PMake to tell you as it examines each target in the
+makefile and indicate why it is deciding whatever it is deciding. You
+can then use the information printed for other targets to see where
+you went wrong. The
+.CW "-p 2" '' ``
+flag makes PMake print out its internal state when it is done,
+allowing you to see that you forgot to make that one chapter depend on
+that file of macros you just got a new version of. The output from
+.CW "-p 2" '' ``
+is intended to resemble closely a real makefile, but with additional
+information provided and with variables expanded in those commands
+PMake actually printed or executed.
+.LP
+Something to be especially careful about is circular dependencies.
+.Ix 0 def dependency circular
+E.g.
+.DS
+a : b
+b : c d
+d : a
+.DE
+In this case, because of how PMake works,
+.CW c
+is the only thing PMake will examine, because
+.CW d
+and
+.CW a
+will effectively fall off the edge of the universe, making it
+impossible to examine
+.CW b
+(or them, for that matter).
+PMake will tell you (if run in its normal mode) all the targets
+involved in any cycle it looked at (i.e. if you have two cycles in the
+graph (naughty, naughty), but only try to make a target in one of
+them, PMake will only tell you about that one. You'll have to try to
+make the other to find the second cycle). When run as Make, it will
+only print the first target in the cycle.
+.xH 2 Invoking PMake
+.LP
+.Ix 0 ref flags
+.Ix 0 ref arguments
+.Ix 0 ref usage
+PMake comes with a wide variety of flags to choose from.
+They may appear in any order, interspersed with command-line variable
+assignments and targets to create.
+The flags are as follows:
+.IP "\fB\-d\fP \fIwhat\fP"
+.Ix 0 def flags -d
+.Ix 0 ref debugging
+This causes PMake to spew out debugging information that
+may prove useful to you. If you can't
+figure out why PMake is doing what it's doing, you might try using
+this flag. The
+.I what
+parameter is a string of single characters that tell PMake what
+aspects you are interested in. Most of what I describe will make
+little sense to you, unless you've dealt with Make before. Just
+remember where this table is and come back to it as you read on.
+The characters and the information they produce are as follows:
+.RS
+.IP a
+Archive searching and caching.
+.IP c
+Conditional evaluation.
+.IP d
+The searching and caching of directories.
+.IP j
+Various snippets of information related to the running of the multiple
+shells. Not particularly interesting.
+.IP m
+The making of each target: what target is being examined; when it was
+last modified; whether it is out-of-date; etc.
+.IP p
+Makefile parsing.
+.IP r
+Remote execution.
+.IP s
+The application of suffix-transformation rules. (See chapter 3)
+.IP t
+The maintenance of the list of targets.
+.IP v
+Variable assignment.
+.RE
+.IP "\&"
+Of these all, the
+.CW m
+and
+.CW s
+letters will be most useful to you.
+If the
+.B \-d
+is the final argument or the argument from which it would get these
+key letters (see below for a note about which argument would be used)
+begins with a
+.B \- ,
+all of these debugging flags will be set, resulting in massive amounts
+of output.
+.IP "\fB\-f\fP \fImakefile\fP"
+.Ix 0 def flags -f
+Specify a makefile to read different from the standard makefiles
+.CW Makefile "\&" (
+or
+.CW makefile ).
+.Ix 0 ref makefile default
+.Ix 0 ref makefile other
+If
+.I makefile
+is ``\-'', PMake uses the standard input. This is useful for making
+quick and dirty makefiles.\|.\|.
+.Ix 0 ref makefile "quick and dirty"
+.IP \fB\-h\fP
+.Ix 0 def flags -h
+Prints out a summary of the various flags PMake accepts. It can also
+be used to find out what level of concurrency was compiled into the
+version of PMake you are using (look at
+.B \-J
+and
+.B \-L )
+and various other information on how PMake was configured.
+.Ix 0 ref configuration
+.Ix 0 ref makefile system
+.IP \fB\-i\fP
+.Ix 0 def flags -i
+If you give this flag, PMake will ignore non-zero status returned
+by any of its shells. It's like placing a `\-' before all the commands
+in the makefile.
+.IP \fB\-k\fP
+.Ix 0 def flags -k
+This is similar to
+.B \-i
+in that it allows PMake to continue when it sees an error, but unlike
+.B \-i ,
+where PMake continues blithely as if nothing went wrong,
+.B \-k
+causes it to recognize the error and only continue work on those
+things that don't depend on the target, either directly or indirectly (through
+depending on something that depends on it), whose creation returned the error.
+The `k' is for ``keep going''.\|.\|.
+.Ix 0 ref target
+.IP \fB\-l\fP
+.Ix 0 def flags -l
+PMake has the ability to lock a directory against other
+people executing it in the same directory (by means of a file called
+``LOCK.make'' that it creates and checks for in the directory). This
+is a Good Thing because two people doing the same thing in the same place
+can be disastrous for the final product (too many cooks and all that).
+Whether this locking is the default is up to your system
+administrator. If locking is on,
+.B \-l
+will turn it off, and vice versa. Note that this locking will not
+prevent \fIyou\fP from invoking PMake twice in the same place \*- if
+you own the lock file, PMake will warn you about it but continue to execute.
+.IP \fB\-n\fP
+.Ix 0 def flags -n
+This flag tells PMake not to execute the commands needed to update the
+out-of-date targets in the makefile. Rather, PMake will simply print
+the commands it would have executed and exit. This is particularly
+useful for checking the correctness of a makefile. If PMake doesn't do
+what you expect it to, it's a good chance the makefile is wrong.
+.IP "\fB\-p\fP \fInumber\fP"
+.Ix 0 def flags -p
+.Ix 0 ref debugging
+This causes PMake to print its input in a reasonable form, though
+not necessarily one that would make immediate sense to anyone but me. The
+.I number
+is a bitwise-or of 1 and 2 where 1 means it should print the input
+before doing any processing and 2 says it should print it after
+everything has been re-created. Thus
+.CW "\-p 3"
+would print it twice\*-once before processing and once after (you
+might find the difference between the two interesting). This is mostly
+useful to me, but you may find it informative in some bizarre circumstances.
+.IP \fB\-q\fP
+.Ix 0 def flags -q
+If you give PMake this flag, it will not try to re-create anything. It
+will just see if anything is out-of-date and exit non-zero if so.
+.IP \fB\-r\fP
+.Ix 0 def flags -r
+When PMake starts up, it reads a default makefile that tells it what
+sort of system it's on and gives it some idea of what to do if you
+don't tell it anything. I'll tell you about it in chapter 3. If you
+give this flag, PMake won't read the default makefile.
+.IP \fB\-s\fP
+.Ix 0 def flags -s
+This causes PMake to not print commands before they're executed. It
+is the equivalent of putting an `@' before every command in the
+makefile.
+.IP \fB\-t\fP
+.Ix 0 def flags -t
+Rather than try to re-create a target, PMake will simply ``touch'' it
+so as to make it appear up-to-date. If the target didn't exist before,
+it will when PMake finishes, but if the target did exist, it will
+appear to have been updated.
+.IP \fB\-v\fP
+.Ix 0 def flags -v
+This is a mixed-compatibility flag intended to mimic the System V
+version of Make. It is the same as giving
+.B \-B ,
+and
+.B \-V
+as well as turning off directory locking. Targets can still be created
+in parallel, however. This is the mode PMake will enter if it is
+invoked either as
+.CW smake '' ``
+or
+.CW vmake ''. ``
+.IP \fB\-x\fP
+.Ix 0 def flags -x
+This tells PMake it's ok to export jobs to other machines, if they're
+available. It is used when running in Make mode, as exporting in this
+mode tends to make things run slower than if the commands were just
+executed locally.
+.IP \fB\-B\fP
+.Ix 0 ref compatibility
+.Ix 0 def flags -B
+Forces PMake to be as backwards-compatible with Make as possible while
+still being itself.
+This includes:
+.RS
+.IP \(bu 2
+Executing one shell per shell command
+.IP \(bu 2
+Expanding anything that looks even vaguely like a variable, with the
+empty string replacing any variable PMake doesn't know.
+.IP \(bu 2
+Refusing to allow you to escape a `#' with a backslash.
+.IP \(bu 2
+Permitting undefined variables on dependency lines and conditionals
+(see below). Normally this causes PMake to abort.
+.RE
+.IP \fB\-C\fP
+.Ix 0 def flags -C
+This nullifies any and all compatibility mode flags you may have given
+or implied up to the time the
+.B \-C
+is encountered. It is useful mostly in a makefile that you wrote for PMake
+to avoid bad things happening when someone runs PMake as
+.CW make '' ``
+or has things set in the environment that tell it to be compatible.
+.B \-C
+is
+.I not
+placed in the
+.CW PMAKE
+environment variable or the
+.CW .MAKEFLAGS
+or
+.CW MFLAGS
+global variables.
+.Ix 0 ref variable environment PMAKE
+.Ix 0 ref variable global .MAKEFLAGS
+.Ix 0 ref variable global MFLAGS
+.Ix 0 ref .MAKEFLAGS variable
+.Ix 0 ref MFLAGS
+.IP "\fB\-D\fP \fIvariable\fP"
+.Ix 0 def flags -D
+Allows you to define a variable to have
+.CW 1 '' ``
+as its value. The variable is a global variable, not a command-line
+variable. This is useful mostly for people who are used to the C
+compiler arguments and those using conditionals, which I'll get into
+in section 4.3
+.Rm 1 4.3
+.IP "\fB\-I\fP \fIdirectory\fP"
+.Ix 0 def flags -I
+Tells PMake another place to search for included makefiles. Yet
+another thing to be explained in chapter 3 (section 3.2, to be
+precise).
+.Rm 2 3.2
+.IP "\fB\-J\fP \fInumber\fP"
+.Ix 0 def flags -J
+Gives the absolute maximum number of targets to create at once on both
+local and remote machines.
+.IP "\fB\-L\fP \fInumber\fP"
+.Ix 0 def flags -L
+This specifies the maximum number of targets to create on the local
+machine at once. This may be 0, though you should be wary of doing
+this, as PMake may hang until a remote machine becomes available, if
+one is not available when it is started.
+.IP \fB\-M\fP
+.Ix 0 ref compatibility
+.Ix 0 def flags -M
+This is the flag that provides absolute, complete, full compatibility
+with Make. It still allows you to use all but a few of the features of
+PMake, but it is non-parallel. This is the mode PMake enters if you
+call it
+.CW make .'' ``
+.IP \fB\-P\fP
+.Ix 0 def flags -P
+.Ix 0 ref "output control"
+When creating targets in parallel, several shells are executing at
+once, each wanting to write its own two cent's-worth to the screen.
+This output must be captured by PMake in some way in order to prevent
+the screen from being filled with garbage even more indecipherable
+than you usually see. PMake has two ways of doing this, one of which
+provides for much cleaner output and a clear separation between the
+output of different jobs, the other of which provides a more immediate
+response so one can tell what is really happpening. The former is done
+by notifying you when the creation of a target starts, capturing the
+output and transferring it to the screen all at once when the job
+finishes. The latter is done by catching the output of the shell (and
+its children) and buffering it until an entire line is received, then
+printing that line preceded by an indication of which job produced
+the output. Since I prefer this second method, it is the one used by
+default. The first method will be used if you give the
+.B \-P
+flag to PMake.
+.IP \fB\-V\fP
+.Ix 0 def flags -V
+As mentioned before, the
+.B \-V
+flag tells PMake to use Make's style of expanding variables,
+substituting the empty string for any variable it doesn't know.
+.IP \fB\-W\fP
+.Ix 0 def flags -W
+There are several times when PMake will print a message at you that is
+only a warning, i.e. it can continue to work in spite of your having
+done something silly (such as forgotten a leading tab for a shell
+command). Sometimes you are well aware of silly things you have done
+and would like PMake to stop bothering you. This flag tells it to shut
+up about anything non-fatal.
+.IP \fB\-X\fP
+.Ix 0 def flags -X
+This flag causes PMake to not attempt to export any jobs to another
+machine.
+.LP
+Several flags may follow a single `\-'. Those flags that require
+arguments take them from successive parameters. E.g.
+.DS
+pmake -fDnI server.mk DEBUG /chip2/X/server/include
+.DE
+will cause PMake to read
+.CW server.mk
+as the input makefile, define the variable
+.CW DEBUG
+as a global variable and look for included makefiles in the directory
+.CW /chip2/X/server/include .
+.xH 2 Summary
+.LP
+A makefile is made of four types of lines:
+.RS
+.IP \(bu 2
+Dependency lines
+.IP \(bu 2
+Creation commands
+.IP \(bu 2
+Variable assignments
+.IP \(bu 2
+Comments, include statements and conditional directives
+.RE
+.LP
+A dependency line is a list of one or more targets, an operator
+.CW : ', (`
+.CW :: ', `
+or
+.CW ! '), `
+and a list of zero or more sources. Sources may contain wildcards and
+certain local variables.
+.LP
+A creation command is a regular shell command preceded by a tab. In
+addition, if the first two characters after the tab (and other
+whitespace) are a combination of
+.CW @ ' `
+or
+.CW - ', `
+PMake will cause the command to not be printed (if the character is
+.CW @ ') `
+or errors from it to be ignored (if
+.CW - '). `
+A blank line, dependency line or variable assignment terminates a
+creation script. There may be only one creation script for each target
+with a
+.CW : ' `
+or
+.CW ! ' `
+operator.
+.LP
+Variables are places to store text. They may be unconditionally
+assigned-to using the
+.CW = ' `
+.Ix 0 ref =
+.Ix 0 ref variable assignment
+operator, appended-to using the
+.CW += ' `
+.Ix 0 ref +=
+.Ix 0 ref variable assignment appended
+operator, conditionally (if the variable is undefined) assigned-to
+with the
+.CW ?= ' `
+.Ix 0 ref ?=
+.Ix 0 ref variable assignment conditional
+operator, and assigned-to with variable expansion with the
+.CW := ' `
+.Ix 0 ref :=
+.Ix 0 ref variable assignment expanded
+operator. The output of a shell command may be assigned to a variable
+using the
+.CW != ' `
+.Ix 0 ref !=
+.Ix 0 ref variable assignment shell-output
+operator. Variables may be expanded (their value inserted) by enclosing
+their name in parentheses or curly braces, prceeded by a dollar sign.
+A dollar sign may be escaped with another dollar sign. Variables are
+not expanded if PMake doesn't know about them. There are seven local
+variables:
+.CW .TARGET ,
+.CW .ALLSRC ,
+.CW .OODATE ,
+.CW .PREFIX ,
+.CW .IMPSRC ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER .
+Four of them
+.CW .TARGET , (
+.CW .PREFIX ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER )
+may be used to specify ``dynamic sources.''
+.Ix 0 ref "dynamic source"
+.Ix 0 ref source dynamic
+Variables are good. Know them. Love them. Live them.
+.LP
+Debugging of makefiles is best accomplished using the
+.B \-n ,
+.B "\-d m" ,
+and
+.B "\-p 2"
+flags.
+.xH 2 Exercises
+.ce
+\s+4\fBTBA\fP\s0
+.xH 1 Short-cuts and Other Nice Things
+.LP
+Based on what I've told you so far, you may have gotten the impression
+that PMake is just a way of storing away commands and making sure you
+don't forget to compile something. Good. That's just what it is.
+However, the ways I've described have been inelegant, at best, and
+painful, at worst.
+This chapter contains things that make the
+writing of makefiles easier and the makefiles themselves shorter and
+easier to modify (and, occasionally, simpler). In this chapter, I
+assume you are somewhat more
+familiar with Sprite (or UNIX, if that's what you're using) than I did
+in chapter 2, just so you're on your toes.
+So without further ado...
+.xH 2 Transformation Rules
+.LP
+As you know, a file's name consists of two parts: a base name, which
+gives some hint as to the contents of the file, and a suffix, which
+usually indicates the format of the file.
+Over the years, as
+.UX
+has developed,
+naming conventions, with regard to suffixes, have also developed that have
+become almost as incontrovertible as Law. E.g. a file ending in
+.CW .c
+is assumed to contain C source code; one with a
+.CW .o
+suffix is assumed to be a compiled, relocatable object file that may
+be linked into any program; a file with a
+.CW .ms
+suffix is usually a text file to be processed by Troff with the \-ms
+macro package, and so on.
+One of the best aspects of both Make and PMake comes from their
+understanding of how the suffix of a file pertains to its contents and
+their ability to do things with a file based soley on its suffix. This
+ability comes from something known as a transformation rule. A
+transformation rule specifies how to change a file with one suffix
+into a file with another suffix.
+.LP
+A transformation rule looks much like a dependency line, except the
+target is made of two known suffixes stuck together. Suffixes are made
+known to PMake by placing them as sources on a dependency line whose
+target is the special target
+.CW .SUFFIXES .
+E.g.
+.DS
+\&.SUFFIXES : .o .c
+\&.c.o :
+ $(CC) $(CFLAGS) -c $(.IMPSRC)
+.DE
+The creation script attached to the target is used to transform a file with
+the first suffix (in this case,
+.CW .c )
+into a file with the second suffix (here,
+.CW .o ).
+In addition, the target inherits whatever attributes have been applied
+to the transformation rule.
+The simple rule given above says that to transform a C source file
+into an object file, you compile it using
+.CW cc
+with the
+.CW \-c
+flag.
+This rule is taken straight from the system makefile. Many
+transformation rules (and suffixes) are defined there, and I refer you
+to it for more examples (type
+.CW "pmake -h" '' ``
+to find out where it is).
+.LP
+There are several things to note about the transformation rule given
+above:
+.RS
+.IP 1)
+The
+.CW .IMPSRC
+variable.
+.Ix 0 def variable local .IMPSRC
+.Ix 0 def .IMPSRC
+This variable is set to the ``implied source'' (the file from which
+the target is being created; the one with the first suffix), which, in this
+case, is the .c file.
+.IP 2)
+The
+.CW CFLAGS
+variable. Almost all of the transformation rules in the system
+makefile are set up using variables that you can alter in your
+makefile to tailor the rule to your needs. In this case, if you want
+all your C files to be compiled with the
+.B \-g
+flag, to provide information for
+.CW dbx ,
+you would set the
+.CW CFLAGS
+variable to contain
+.CW -g
+.CW "CFLAGS = -g" '') (``
+and PMake would take care of the rest.
+.RE
+.LP
+To give you a quick example, the makefile in 2.3.4
+.Rm 3 2.3.4
+could be changed to this:
+.DS
+OBJS = a.o b.o c.o
+program : $(OBJS)
+ $(CC) -o $(.TARGET) $(.ALLSRC)
+$(OBJS) : defs.h
+.DE
+The transformation rule I gave above takes the place of the 6 lines\**
+.FS
+This is also somewhat cleaner, I think, than the dynamic source
+solution presented in 2.6
+.FE
+.Rm 4 2.6
+.DS
+a.o : a.c
+ cc -c a.c
+b.o : b.c
+ cc -c b.c
+c.o : c.c
+ cc -c c.c
+.DE
+.LP
+Now you may be wondering about the dependency between the
+.CW .o
+and
+.CW .c
+files \*- it's not mentioned anywhere in the new makefile. This is
+because it isn't needed: one of the effects of applying a
+transformation rule is the target comes to depend on the implied
+source. That's why it's called the implied
+.I source .
+.LP
+For a more detailed example. Say you have a makefile like this:
+.DS
+a.out : a.o b.o
+ $(CC) $(.ALLSRC)
+.DE
+and a directory set up like this:
+.DS
+total 4
+-rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile
+-rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c
+-rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o
+-rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c
+.DE
+While just typing
+.CW pmake '' ``
+will do the right thing, it's much more informative to type
+.CW "pmake -d s" ''. ``
+This will show you what PMake is up to as it processes the files. In
+this case, PMake prints the following:
+.DS
+Suff_FindDeps (a.out)
+ using existing source a.o
+ applying .o -> .out to "a.o"
+Suff_FindDeps (a.o)
+ trying a.c...got it
+ applying .c -> .o to "a.c"
+Suff_FindDeps (b.o)
+ trying b.c...got it
+ applying .c -> .o to "b.c"
+Suff_FindDeps (a.c)
+ trying a.y...not there
+ trying a.l...not there
+ trying a.c,v...not there
+ trying a.y,v...not there
+ trying a.l,v...not there
+Suff_FindDeps (b.c)
+ trying b.y...not there
+ trying b.l...not there
+ trying b.c,v...not there
+ trying b.y,v...not there
+ trying b.l,v...not there
+--- a.o ---
+cc -c a.c
+--- b.o ---
+cc -c b.c
+--- a.out ---
+cc a.o b.o
+.DE
+.LP
+.CW Suff_FindDeps
+is the name of a function in PMake that is called to check for implied
+sources for a target using transformation rules.
+The transformations it tries are, naturally
+enough, limited to the ones that have been defined (a transformation
+may be defined multiple times, by the way, but only the most recent
+one will be used). You will notice, however, that there is a definite
+order to the suffixes that are tried. This order is set by the
+relative positions of the suffixes on the
+.CW .SUFFIXES
+line \*- the earlier a suffix appears, the earlier it is checked as
+the source of a transformation. Once a suffix has been defined, the
+only way to change its position in the pecking order is to remove all
+the suffixes (by having a
+.CW .SUFFIXES
+dependency line with no sources) and redefine them in the order you
+want. (Previously-defined transformation rules will be automatically
+redefined as the suffixes they involve are re-entered.)
+.LP
+Another way to affect the search order is to make the dependency
+explicit. In the above example,
+.CW a.out
+depends on
+.CW a.o
+and
+.CW b.o .
+Since a transformation exists from
+.CW .o
+to
+.CW .out ,
+PMake uses that, as indicated by the
+.CW "using existing source a.o" '' ``
+message.
+.LP
+The search for a transformation starts from the suffix of the target
+and continues through all the defined transformations, in the order
+dictated by the suffix ranking, until an existing file with the same
+base (the target name minus the suffix and any leading directories) is
+found. At that point, one or more transformation rules will have been
+found to change the one existing file into the target.
+.LP
+For example, ignoring what's in the system makefile for now, say you
+have a makefile like this:
+.DS
+\&.SUFFIXES : .out .o .c .y .l
+\&.l.c :
+ lex $(.IMPSRC)
+ mv lex.yy.c $(.TARGET)
+\&.y.c :
+ yacc $(.IMPSRC)
+ mv y.tab.c $(.TARGET)
+\&.c.o :
+ cc -c $(.IMPSRC)
+\&.o.out :
+ cc -o $(.TARGET) $(.IMPSRC)
+.DE
+and the single file
+.CW jive.l .
+If you were to type
+.CW "pmake -rd ms jive.out" ,'' ``
+you would get the following output for
+.CW jive.out :
+.DS
+Suff_FindDeps (jive.out)
+ trying jive.o...not there
+ trying jive.c...not there
+ trying jive.y...not there
+ trying jive.l...got it
+ applying .l -> .c to "jive.l"
+ applying .c -> .o to "jive.c"
+ applying .o -> .out to "jive.o"
+.DE
+and this is why: PMake starts with the target
+.CW jive.out ,
+figures out its suffix
+.CW .out ) (
+and looks for things it can transform to a
+.CW .out
+file. In this case, it only finds
+.CW .o ,
+so it looks for the file
+.CW jive.o .
+It fails to find it, so it looks for transformations into a
+.CW .o
+file. Again it has only one choice:
+.CW .c .
+So it looks for
+.CW jive.c
+and, as you know, fails to find it. At this point it has two choices:
+it can create the
+.CW .c
+file from either a
+.CW .y
+file or a
+.CW .l
+file. Since
+.CW .y
+came first on the
+.CW .SUFFIXES
+line, it checks for
+.CW jive.y
+first, but can't find it, so it looks for
+.CW jive.l
+and, lo and behold, there it is.
+At this point, it has defined a transformation path as follows:
+.CW .l
+\(->
+.CW .c
+\(->
+.CW .o
+\(->
+.CW .out
+and applies the transformation rules accordingly. For completeness,
+and to give you a better idea of what PMake actually did with this
+three-step transformation, this is what PMake printed for the rest of
+the process:
+.DS
+Suff_FindDeps (jive.o)
+ using existing source jive.c
+ applying .c -> .o to "jive.c"
+Suff_FindDeps (jive.c)
+ using existing source jive.l
+ applying .l -> .c to "jive.l"
+Suff_FindDeps (jive.l)
+Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
+Examining jive.c...non-existent...out-of-date
+--- jive.c ---
+lex jive.l
+\&.\|.\|. meaningless lex output deleted .\|.\|.
+mv lex.yy.c jive.c
+Examining jive.o...non-existent...out-of-date
+--- jive.o ---
+cc -c jive.c
+Examining jive.out...non-existent...out-of-date
+--- jive.out ---
+cc -o jive.out jive.o
+.DE
+.LP
+One final question remains: what does PMake do with targets that have
+no known suffix? PMake simply pretends it actually has a known suffix
+and searches for transformations accordingly.
+The suffix it chooses is the source for the
+.CW .NULL
+.Ix 0 ref .NULL
+target mentioned later. In the system makefile,
+.CW .out
+is chosen as the ``null suffix''
+.Ix 0 def suffix null
+.Ix 0 def "null suffix"
+because most people use PMake to create programs. You are, however,
+free and welcome to change it to a suffix of your own choosing.
+The null suffix is ignored, however, when PMake is in compatibility
+mode (see chapter 4).
+.xH 2 Including Other Makefiles
+.Ix 0 def makefile inclusion
+.Rd 2
+.LP
+Just as for programs, it is often useful to extract certain parts of a
+makefile into another file and just include it in other makefiles
+somehow. Many compilers allow you say something like
+.DS
+#include "defs.h"
+.DE
+to include the contents of
+.CW defs.h
+in the source file. PMake allows you to do the same thing for
+makefiles, with the added ability to use variables in the filenames.
+An include directive in a makefile looks either like this:
+.DS
+#include <file>
+.DE
+or this
+.DS
+#include "file"
+.DE
+The difference between the two is where PMake searches for the file:
+the first way, PMake will look for
+the file only in the system makefile directory (to find out what that
+directory is, give PMake the
+.B \-h
+flag).
+.Ix 0 ref flags -h
+For files in double-quotes, the search is more complex:
+.RS
+.IP 1)
+The directory of the makefile that's including the file.
+.IP 2)
+The current directory (the one in which you invoked PMake).
+.IP 3)
+The directories given by you using
+.B \-I
+flags, in the order in which you gave them.
+.IP 4)
+Directories given by
+.CW .PATH
+dependency lines (see chapter 4).
+.IP 5)
+The system makefile directory.
+.RE
+.LP
+in that order.
+.LP
+You are free to use PMake variables in the filename\*-PMake will
+expand them before searching for the file. You must specify the
+searching method with either angle brackets or double-quotes
+.I outside
+of a variable expansion. I.e. the following
+.DS
+SYSTEM = <command.mk>
+
+#include $(SYSTEM)
+.DE
+won't work.
+.xH 2 Saving Commands
+.LP
+.Ix 0 def ...
+There may come a time when you will want to save certain commands to
+be executed when everything else is done. For instance: you're
+making several different libraries at one time and you want to create the
+members in parallel. Problem is,
+.CW ranlib
+is another one of those programs that can't be run more than once in
+the same directory at the same time (each one creates a file called
+.CW __.SYMDEF
+into which it stuffs information for the linker to use. Two of them
+running at once will overwrite each other's file and the result will
+be garbage for both parties). You might want a way to save the ranlib
+commands til the end so they can be run one after the other, thus
+keeping them from trashing each other's file. PMake allows you to do
+this by inserting an ellipsis (``.\|.\|.'') as a command between
+commands to be run at once and those to be run later.
+.LP
+So for the
+.CW ranlib
+case above, you might do this:
+.Rd 5
+.DS
+lib1.a : $(LIB1OBJS)
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+
+lib2.a : $(LIB2OBJS)
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+This would save both
+.DS
+ranlib $(.TARGET)
+.DE
+commands until the end, when they would run one after the other
+(using the correct value for the
+.CW .TARGET
+variable, of course).
+.LP
+Commands saved in this manner are only executed if PMake manages to
+re-create everything without an error.
+.xH 2 Target Attributes
+.LP
+PMake allows you to give attributes to targets by means of special
+sources. Like everything else PMake uses, these sources begin with a
+period and are made up of all upper-case letters. There are various
+reasons for using them, and I will try to give examples for most of
+them. Others you'll have to find uses for yourself. Think of it as ``an
+exercise for the reader.'' By placing one (or more) of these as a source on a
+dependency line, you are ``marking the target(s) with that
+attribute.'' That's just the way I phrase it, so you know.
+.LP
+Any attributes given as sources for a transformation rule are applied
+to the target of the transformation rule when the rule is applied.
+.Ix 0 def attributes
+.Ix 0 ref source
+.Ix 0 ref target
+.nr pw 12
+.IP .DONTCARE \n(pw
+.Ix 0 def attributes .DONTCARE
+.Ix 0 def .DONTCARE
+If a target is marked with this attribute and PMake can't figure out
+how to create it, it will ignore this fact and assume the file isn't
+really needed or actually exists and PMake just can't find it. This may prove
+wrong, but the error will be noted later on, not when PMake tries to create
+the target so marked. This attribute also prevents PMake from
+attempting to touch the target if it is given the
+.B \-t
+flag.
+.Ix 0 ref flags -t
+.IP .EXEC \n(pw
+.Ix 0 def attributes .EXEC
+.Ix 0 def .EXEC
+This attribute causes its shell script to be executed while having no
+effect on targets that depend on it. This makes the target into a sort
+of subroutine. An example. Say you have some LISP files that need to
+be compiled and loaded into a LISP process. To do this, you echo LISP
+commands into a file and execute a LISP with this file as its input
+when everything's done. Say also that you have to load other files
+from another system before you can compile your files and further,
+that you don't want to go through the loading and dumping unless one
+of
+.I your
+files has changed. Your makefile might look a little bit
+like this (remember, this is an educational example, and don't worry
+about the
+.CW COMPILE
+rule, all will soon become clear, grasshopper):
+.DS
+system : init a.fasl b.fasl c.fasl
+ for i in $(.ALLSRC);
+ do
+ echo -n '(load "' >> input
+ echo -n ${i} >> input
+ echo '")' >> input
+ done
+ echo '(dump "$(.TARGET)")' >> input
+ lisp < input
+
+a.fasl : a.l init COMPILE
+b.fasl : b.l init COMPILE
+c.fasl : c.l init COMPILE
+COMPILE : .USE
+ echo '(compile "$(.ALLSRC)")' >> input
+init : .EXEC
+ echo '(load-system)' > input
+.DE
+.Ix 0 ref .USE
+.Ix 0 ref attributes .USE
+.Ix 0 ref variable local .ALLSRC
+.IP "\&"
+.CW .EXEC
+sources, don't appear in the local variables of targets that depend on
+them (nor are they touched if PMake is given the
+.B \-t
+flag).
+.Ix 0 ref flags -t
+Note that all the rules, not just that for
+.CW system ,
+include
+.CW init
+as a source. This is because none of the other targets can be made
+until
+.CW init
+has been made, thus they depend on it.
+.IP .EXPORT \n(pw
+.Ix 0 def attributes .EXPORT
+.Ix 0 def .EXPORT
+This is used to mark those targets whose creation should be sent to
+another machine if at all possible. This may be used by some
+exportation schemes if the exportation is expensive. You should ask
+your system administrator if it is necessary.
+.IP .EXPORTSAME \n(pw
+.Ix 0 def attributes .EXPORTSAME
+.Ix 0 def .EXPORTSAME
+Tells the export system that the job should be exported to a machine
+of the same architecture as the current one. Certain operations (e.g.
+running text through
+.CW nroff )
+can be performed the same on any architecture (CPU and
+operating system type), while others (e.g. compiling a program with
+.CW cc )
+must be performed on a machine with the same architecture. Not all
+export systems will support this attribute.
+.IP .IGNORE \n(pw
+.Ix 0 def attributes .IGNORE
+.Ix 0 def .IGNORE attribute
+Giving a target the
+.CW .IGNORE
+attribute causes PMake to ignore errors from any of the target's commands, as
+if they all had `\-' before them.
+.IP .INVISIBLE \n(pw
+.Ix 0 def attributes .INVISIBLE
+.Ix 0 def .INVISIBLE
+This allows you to specify one target as a source for another without
+the one affecting the other's local variables. Useful if, say, you
+have a makefile that creates two programs, one of which is used to
+create the other, so it must exist before the other is created. You
+could say
+.DS
+prog1 : $(PROG1OBJS) prog2 MAKEINSTALL
+prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL
+.DE
+where
+.CW MAKEINSTALL
+is some complex .USE rule (see below) that depends on the
+.Ix 0 ref .USE
+.CW .ALLSRC
+variable containing the right things. Without the
+.CW .INVISIBLE
+attribute for
+.CW prog2 ,
+the
+.CW MAKEINSTALL
+rule couldn't be applied. This is not as useful as it should be, and
+the semantics may change (or the whole thing go away) in the
+not-too-distant future.
+.IP .JOIN \n(pw
+.Ix 0 def attributes .JOIN
+.Ix 0 def .JOIN
+This is another way to avoid performing some operations in parallel
+while permitting everything else to be done so. Specifically it
+forces the target's shell script to be executed only if one or more of the
+sources was out-of-date. In addition, the target's name,
+in both its
+.CW .TARGET
+variable and all the local variables of any target that depends on it,
+is replaced by the value of its
+.CW .ALLSRC
+variable.
+As an example, suppose you have a program that has four libraries that
+compile in the same directory along with, and at the same time as, the
+program. You again have the problem with
+.CW ranlib
+that I mentioned earlier, only this time it's more severe: you
+can't just put the ranlib off to the end since the program
+will need those libraries before it can be re-created. You can do
+something like this:
+.DS
+program : $(OBJS) libraries
+ cc -o $(.TARGET) $(.ALLSRC)
+
+libraries : lib1.a lib2.a lib3.a lib4.a .JOIN
+ ranlib $(.OODATE)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+.Ix 0 ref variable local .OODATE
+.Ix 0 ref .TARGET
+.Ix 0 ref .ALLSRC
+.Ix 0 ref .OODATE
+In this case, PMake will re-create the
+.CW $(OBJS)
+as necessary, along with
+.CW lib1.a ,
+.CW lib2.a ,
+.CW lib3.a
+and
+.CW lib4.a .
+It will then execute
+.CW ranlib
+on any library that was changed and set
+.CW program 's
+.CW .ALLSRC
+variable to contain what's in
+.CW $(OBJS)
+followed by
+.CW "lib1.a lib2.a lib3.a lib4.a" .'' ``
+In case you're wondering, it's called
+.CW .JOIN
+because it joins together different threads of the ``input graph'' at
+the target marked with the attribute.
+Another aspect of the .JOIN attribute is it keeps the target from
+being created if the
+.B \-t
+flag was given.
+.Ix 0 ref flags -t
+.IP .MAKE \n(pw
+.Ix 0 def attributes .MAKE
+.Ix 0 def .MAKE
+The
+.CW .MAKE
+attribute marks its target as being a recursive invocation of PMake.
+This forces PMake to execute the script associated with the target (if
+it's out-of-date) even if you gave the
+.B \-n
+or
+.B \-t
+flag. By doing this, you can start at the top of a system and type
+.DS
+pmake -n
+.DE
+and have it descend the directory tree (if your makefiles are set up
+correctly), printing what it would have executed if you hadn't
+included the
+.B \-n
+flag.
+.IP .NOEXPORT \n(pw
+.Ix 0 def attributes .NOEXPORT
+.Ix 0 def .NOEXPORT attribute
+If possible, PMake will attempt to export the creation of all targets to
+another machine (this depends on how PMake was configured). Sometimes,
+the creation is so simple, it is pointless to send it to another
+machine. If you give the target the
+.CW .NOEXPORT
+attribute, it will be run locally, even if you've given PMake the
+.B "\-L 0"
+flag.
+.IP .NOTMAIN \n(pw
+.Ix 0 def attributes .NOTMAIN
+.Ix 0 def .NOTMAIN
+Normally, if you do not specify a target to make in any other way,
+PMake will take the first target on the first dependency line of a
+makefile as the target to create. That target is known as the ``Main
+Target'' and is labeled as such if you print the dependencies out
+using the
+.B \-p
+flag.
+.Ix 0 ref flags -p
+Giving a target this attribute tells PMake that the target is
+definitely
+.I not
+the Main Target.
+This allows you to place targets in an included makefile and
+have PMake create something else by default.
+.IP .PRECIOUS \n(pw
+.Ix 0 def attributes .PRECIOUS
+.Ix 0 def .PRECIOUS attribute
+When PMake is interrupted (you type control-C at the keyboard), it
+will attempt to clean up after itself by removing any half-made
+targets. If a target has the
+.CW .PRECIOUS
+attribute, however, PMake will leave it alone. An additional side
+effect of the `::' operator is to mark the targets as
+.CW .PRECIOUS .
+.Ix 0 ref operator double-colon
+.Ix 0 ref ::
+.IP .SILENT \n(pw
+.Ix 0 def attributes .SILENT
+.Ix 0 def .SILENT attribute
+Marking a target with this attribute keeps its commands from being
+printed when they're executed, just as if they had an `@' in front of them.
+.IP .USE \n(pw
+.Ix 0 def attributes .USE
+.Ix 0 def .USE
+By giving a target this attribute, you turn it into PMake's equivalent
+of a macro. When the target is used as a source for another target,
+the other target acquires the commands, sources and attributes (except
+.CW .USE )
+of the source.
+If the target already has commands, the
+.CW .USE
+target's commands are added to the end. If more than one .USE-marked
+source is given to a target, the rules are applied sequentially.
+.IP "\&" \n(pw
+The typical .USE rule (as I call them) will use the sources of the
+target to which it is applied (as stored in the
+.CW .ALLSRC
+variable for the target) as its ``arguments,'' if you will.
+For example, you probably noticed that the commands for creating
+.CW lib1.a
+and
+.CW lib2.a
+in the example in section 3.3
+.Rm 5 3.3
+were exactly the same. You can use the
+.CW .USE
+attribute to eliminate the repetition, like so:
+.DS
+lib1.a : $(LIB1OBJS) MAKELIB
+lib2.a : $(LIB2OBJS) MAKELIB
+
+MAKELIB : .USE
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+.IP "\&" \n(pw
+Several system makefiles (not to be confused with The System Makefile)
+make use of these .USE rules to make your
+life easier (they're in the default, system makefile directory...take a look).
+Note that the .USE rule source itself
+.CW MAKELIB ) (
+does not appear in any of the targets's local variables.
+There is no limit to the number of times I could use the
+.CW MAKELIB
+rule. If there were more libraries, I could continue with
+.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' ``
+and so on and so forth.
+.xH 2 Special Targets
+.LP
+As there were in Make, so there are certain targets that have special
+meaning to PMake. When you use one on a dependency line, it is the
+only target that may appear on the left-hand-side of the operator.
+.Ix 0 ref target
+.Ix 0 ref operator
+As for the attributes and variables, all the special targets
+begin with a period and consist of upper-case letters only.
+I won't describe them all in detail because some of them are rather
+complex and I'll describe them in more detail than you'll want in
+chapter 4.
+The targets are as follows:
+.nr pw 10
+.IP .BEGIN \n(pw
+.Ix 0 def .BEGIN
+Any commands attached to this target are executed before anything else
+is done. You can use it for any initialization that needs doing.
+.IP .DEFAULT \n(pw
+.Ix 0 def .DEFAULT
+This is sort of a .USE rule for any target (that was used only as a
+source) that PMake can't figure out any other way to create. It's only
+``sort of'' a .USE rule because only the shell script attached to the
+.CW .DEFAULT
+target is used. The
+.CW .IMPSRC
+variable of a target that inherits
+.CW .DEFAULT 's
+commands is set to the target's own name.
+.Ix 0 ref .IMPSRC
+.Ix 0 ref variable local .IMPSRC
+.IP .END \n(pw
+.Ix 0 def .END
+This serves a function similar to
+.CW .BEGIN ,
+in that commands attached to it are executed once everything has been
+re-created (so long as no errors occurred). It also serves the extra
+function of being a place on which PMake can hang commands you put off
+to the end. Thus the script for this target will be executed before
+any of the commands you save with the ``.\|.\|.''.
+.Ix 0 ref ...
+.IP .EXPORT \n(pw
+The sources for this target are passed to the exportation system compiled
+into PMake. Some systems will use these sources to configure
+themselves. You should ask your system administrator about this.
+.IP .IGNORE \n(pw
+.Ix 0 def .IGNORE target
+.Ix 0 ref .IGNORE attribute
+.Ix 0 ref attributes .IGNORE
+This target marks each of its sources with the
+.CW .IGNORE
+attribute. If you don't give it any sources, then it is like
+giving the
+.B \-i
+flag when you invoke PMake \*- errors are ignored for all commands.
+.Ix 0 ref flags -i
+.IP .INCLUDES \n(pw
+.Ix 0 def .INCLUDES target
+.Ix 0 def variable global .INCLUDES
+.Ix 0 def .INCLUDES variable
+The sources for this target are taken to be suffixes that indicate a
+file that can be included in a program source file.
+The suffix must have already been declared with
+.CW .SUFFIXES
+(see below).
+Any suffix so marked will have the directories on its search path
+(see
+.CW .PATH ,
+below) placed in the
+.CW .INCLUDES
+variable, each preceded by a
+.B \-I
+flag. This variable can then be used as an argument for the compiler
+in the normal fashion. The
+.CW .h
+suffix is already marked in this way in the system makefile.
+.Ix 0 ref makefile system
+E.g. if you have
+.DS
+\&.SUFFIXES : .bitmap
+\&.PATH.bitmap : /usr/local/X/lib/bitmaps
+\&.INCLUDES : .bitmap
+.DE
+PMake will place
+.CW "-I/usr/local/X/lib/bitmaps" '' ``
+in the
+.CW .INCLUDES
+variable and you can then say
+.DS
+cc $(.INCLUDES) -c xprogram.c
+.DE
+(Note: the
+.CW .INCLUDES
+variable is not actually filled in until the entire makefile has been read.)
+.IP .INTERRUPT \n(pw
+.Ix 0 def .INTERRUPT
+When PMake is interrupted,
+it will execute the commands in the script for this target, if it
+exists.
+.IP .LIBS \n(pw
+.Ix 0 def .LIBS target
+.Ix 0 def .LIBS variable
+.Ix 0 def variable global .LIBS
+This does for libraries what
+.CW .INCLUDES
+does for include files, except the flag used is
+.B \-L ,
+as required by those linkers that allow you to tell them where to find
+libraries. The variable used is
+.CW .LIBS .
+Be forewarned that PMake may not have been compiled to do this if the
+linker on your system doesn't accept the
+.B \-L
+flag, though the
+.CW .LIBS
+variable will always be defined once the makefile has been read.
+.IP .MAIN \n(pw
+.Ix 0 def .MAIN
+If you didn't give a target (or targets) to create when you invoked
+PMake, it will take the sources of this target as the targets to
+create.
+.IP .MAKEFLAGS \n(pw
+.Ix 0 def .MAKEFLAGS target
+This target provides a way for you to always specify flags for PMake
+when the makefile is used. The flags are just as they would be typed
+to the shell (except you can't use shell variables unless they're in
+the environment),
+though the
+.B \-f
+and
+.B \-r
+flags have no effect.
+.IP .NULL \n(pw
+.Ix 0 def .NULL
+.Ix 0 ref suffix null
+.Ix 0 ref "null suffix"
+This allows you to specify what suffix PMake should pretend a file has
+if, in fact, it has no known suffix. Only one suffix may be so
+designated. The last source on the dependency line is the suffix that
+is used (you should, however, only give one suffix.\|.\|.).
+.IP .PATH \n(pw
+.Ix 0 def .PATH
+If you give sources for this target, PMake will take them as
+directories in which to search for files it cannot find in the current
+directory. If you give no sources, it will clear out any directories
+added to the search path before. Since the effects of this all get
+very complex, I'll leave it til chapter four to give you a complete
+explanation.
+.IP .PATH\fIsuffix\fP \n(pw
+.Ix 0 ref .PATH
+This does a similar thing to
+.CW .PATH ,
+but it does it only for files with the given suffix. The suffix must
+have been defined already. Look at
+.B "Search Paths"
+(section 4.1)
+.Rm 6 4.1
+for more information.
+.IP .PRECIOUS \n(pw
+.Ix 0 def .PRECIOUS target
+.Ix 0 ref .PRECIOUS attribute
+.Ix 0 ref attributes .PRECIOUS
+Similar to
+.CW .IGNORE ,
+this gives the
+.CW .PRECIOUS
+attribute to each source on the dependency line, unless there are no
+sources, in which case the
+.CW .PRECIOUS
+attribute is given to every target in the file.
+.IP .RECURSIVE \n(pw
+.Ix 0 def .RECURSIVE
+.Ix 0 ref attributes .MAKE
+.Ix 0 ref .MAKE
+This target applies the
+.CW .MAKE
+attribute to all its sources. It does nothing if you don't give it any sources.
+.IP .SHELL \n(pw
+.Ix 0 def .SHELL
+PMake is not constrained to only using the Bourne shell to execute
+the commands you put in the makefile. You can tell it some other shell
+to use with this target. Check out
+.B "A Shell is a Shell is a Shell"
+(section 4.4)
+.Rm 7 4.4
+for more information.
+.IP .SILENT \n(pw
+.Ix 0 def .SILENT target
+.Ix 0 ref .SILENT attribute
+.Ix 0 ref attributes .SILENT
+When you use
+.CW .SILENT
+as a target, it applies the
+.CW .SILENT
+attribute to each of its sources. If there are no sources on the
+dependency line, then it is as if you gave PMake the
+.B \-s
+flag and no commands will be echoed.
+.IP .SUFFIXES \n(pw
+.Ix 0 def .SUFFIXES
+This is used to give new file suffixes for PMake to handle. Each
+source is a suffix PMake should recognize. If you give a
+.CW .SUFFIXES
+dependency line with no sources, PMake will forget about all the
+suffixes it knew (this also nukes the null suffix).
+For those targets that need to have suffixes defined, this is how you do it.
+.LP
+In addition to these targets, a line of the form
+.DS
+\fIattribute\fP : \fIsources\fP
+.DE
+applies the
+.I attribute
+to all the targets listed as
+.I sources .
+.xH 2 Modifying Variable Expansion
+.LP
+.Ix 0 def variable expansion modified
+.Ix 0 ref variable expansion
+.Ix 0 def variable modifiers
+Variables need not always be expanded verbatim. PMake defines several
+modifiers that may be applied to a variable's value before it is
+expanded. You apply a modifier by placing it after the variable name
+with a colon between the two, like so:
+.DS
+${\fIVARIABLE\fP:\fImodifier\fP}
+.DE
+Each modifier is a single character followed by something specific to
+the modifier itself.
+You may apply as many modifiers as you want \*- each one is applied to
+the result of the previous and is separated from the previous by
+another colon.
+.LP
+There are seven ways to modify a variable's expansion, most of which
+come from the C shell variable modification characters:
+.RS
+.IP "M\fIpattern\fP"
+.Ix 0 def :M
+.Ix 0 def modifier match
+This is used to select only those words (a word is a series of
+characters that are neither spaces nor tabs) that match the given
+.I pattern .
+The pattern is a wildcard pattern like that used by the shell, where
+.CW *
+means 0 or more characters of any sort;
+.CW ?
+is any single character;
+.CW [abcd]
+matches any single character that is either `a', `b', `c' or `d'
+(there may be any number of characters between the brackets);
+.CW [0-9]
+matches any single character that is between `0' and `9' (i.e. any
+digit. This form may be freely mixed with the other bracket form), and
+`\\' is used to escape any of the characters `*', `?', `[' or `:',
+leaving them as regular characters to match themselves in a word.
+For example, the system makefile
+.CW <makedepend.mk>
+uses
+.CW "$(CFLAGS:M-[ID]*)" '' ``
+to extract all the
+.CW \-I
+and
+.CW \-D
+flags that would be passed to the C compiler. This allows it to
+properly locate include files and generate the correct dependencies.
+.IP "N\fIpattern\fP"
+.Ix 0 def :N
+.Ix 0 def modifier nomatch
+This is identical to
+.CW :M
+except it substitutes all words that don't match the given pattern.
+.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]"
+.Ix 0 def :S
+.Ix 0 def modifier substitute
+Causes the first occurrence of
+.I search-string
+in the variable to be replaced by
+.I replacement-string ,
+unless the
+.CW g
+flag is given at the end, in which case all occurences of the string
+are replaced. The substitution is performed on each word in the
+variable in turn. If
+.I search-string
+begins with a
+.CW ^ ,
+the string must match starting at the beginning of the word. If
+.I search-string
+ends with a
+.CW $ ,
+the string must match to the end of the word (these two may be
+combined to force an exact match). If a backslash preceeds these two
+characters, however, they lose their special meaning. Variable
+expansion also occurs in the normal fashion inside both the
+.I search-string
+and the
+.I replacement-string ,
+.B except
+that a backslash is used to prevent the expansion of a
+.CW $ ,
+not another dollar sign, as is usual.
+Note that
+.I search-string
+is just a string, not a pattern, so none of the usual
+regular-expression/wildcard characters have any special meaning save
+.CW ^
+and
+.CW $ .
+In the replacement string,
+the
+.CW &
+character is replaced by the
+.I search-string
+unless it is preceded by a backslash.
+You are allowed to use any character except
+colon or exclamation point to separate the two strings. This so-called
+delimiter character may be placed in either string by preceeding it
+with a backslash.
+.IP T
+.Ix 0 def :T
+.Ix 0 def modifier tail
+Replaces each word in the variable expansion by its last
+component (its ``tail''). For example, given
+.DS
+OBJS = ../lib/a.o b /usr/lib/libm.a
+TAILS = $(OBJS:T)
+.DE
+the variable
+.CW TAILS
+would expand to
+.CW "a.o b libm.a" .'' ``
+.IP H
+.Ix 0 def :H
+.Ix 0 def modifier head
+This is similar to
+.CW :T ,
+except that every word is replaced by everything but the tail (the
+``head''). Using the same definition of
+.CW OBJS ,
+the string
+.CW "$(OBJS:H)" '' ``
+would expand to
+.CW "../lib /usr/lib" .'' ``
+Note that the final slash on the heads is removed and
+anything without a head is replaced by the empty string.
+.IP E
+.Ix 0 def :E
+.Ix 0 def modifier extension
+.Ix 0 def modifier suffix
+.Ix 0 ref suffix "variable modifier"
+.CW :E
+replaces each word by its suffix (``extension''). So
+.CW "$(OBJS:E)" '' ``
+would give you
+.CW ".o .a" .'' ``
+.IP R
+.Ix 0 def :R
+.Ix 0 def modifier root
+.Ix 0 def modifier base
+This replaces each word by everything but the suffix (the ``root'' of
+the word).
+.CW "$(OBJS:R)" '' ``
+expands to ``
+.CW "../lib/a b /usr/lib/libm" .''
+.RE
+.LP
+In addition, the System V style of substitution is also supported.
+This looks like:
+.DS
+$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP)
+.DE
+It must be the last modifier in the chain. The search is anchored at
+the end of each word, so only suffixes or whole words may be replaced.
+.xH 2 More on Debugging
+.xH 2 More Exercises
+.IP (3.1)
+You've got a set programs, each of which is created from its own
+assembly-language source file (suffix
+.CW .asm ).
+Each program can be assembled into two versions, one with error-checking
+code assembled in and one without. You could assemble them into files
+with different suffixes
+.CW .eobj \& (
+and
+.CW .obj ,
+for instance), but your linker only understands files that end in
+.CW .obj .
+To top it all off, the final executables
+.I must
+have the suffix
+.CW .exe .
+How can you still use transformation rules to make your life easier
+(Hint: assume the error-checking versions have
+.CW ec
+tacked onto their prefix)?
+.IP (3.2)
+Assume, for a moment or two, you want to perform a sort of
+``indirection'' by placing the name of a variable into another one,
+then you want to get the value of the first by expanding the second
+somehow. Unfortunately, PMake doesn't allow constructs like
+.DS I
+$($(FOO))
+.DE
+What do you do? Hint: no further variable expansion is performed after
+modifiers are applied, thus if you cause a $ to occur in the
+expansion, that's what will be in the result.
+.xH 1 PMake for Gods
+.LP
+This chapter is devoted to those facilities in PMake that allow you to
+do a great deal in a makefile with very little work, as well as do
+some things you couldn't do in Make without a great deal of work (and
+perhaps the use of other programs). The problem with these features,
+is they must be handled with care, or you will end up with a mess.
+.LP
+Once more, I assume a greater familiarity with
+.UX
+or Sprite than I did in the previous two chapters.
+.xH 2 Search Paths
+.Rd 6
+.LP
+PMake supports the dispersal of files into multiple directories by
+allowing you to specify places to look for sources with
+.CW .PATH
+targets in the makefile. The directories you give as sources for these
+targets make up a ``search path.'' Only those files used exclusively
+as sources are actually sought on a search path, the assumption being
+that anything listed as a target in the makefile can be created by the
+makefile and thus should be in the current directory.
+.LP
+There are two types of search paths
+in PMake: one is used for all types of files (including included
+makefiles) and is specified with a plain
+.CW .PATH
+target (e.g.
+.CW ".PATH : RCS" ''), ``
+while the other is specific to a certain type of file, as indicated by
+the file's suffix. A specific search path is indicated by immediately following
+the
+.CW .PATH
+with the suffix of the file. For instance
+.DS
+\&.PATH.h : /sprite/lib/include /sprite/att/lib/include
+.DE
+would tell PMake to look in the directories
+.CW /sprite/lib/include
+and
+.CW /sprite/att/lib/include
+for any files whose suffix is
+.CW .h .
+.LP
+The current directory is always consulted first to see if a file
+exists. Only if it cannot be found there are the directories in the
+specific search path, followed by those in the general search path,
+consulted.
+.LP
+A search path is also used when expanding wildcard characters. If the
+pattern has a recognizable suffix on it, the path for that suffix will
+be used for the expansion. Otherwise the default search path is employed.
+.LP
+When a file is found in some directory other than the current one, all
+local variables that would have contained the target's name
+.CW .ALLSRC , (
+and
+.CW .IMPSRC )
+will instead contain the path to the file, as found by PMake.
+Thus if you have a file
+.CW ../lib/mumble.c
+and a makefile
+.DS
+\&.PATH.c : ../lib
+mumble : mumble.c
+ $(CC) -o $(.TARGET) $(.ALLSRC)
+.DE
+the command executed to create
+.CW mumble
+would be
+.CW "cc -o mumble ../lib/mumble.c" .'' ``
+(As an aside, the command in this case isn't strictly necessary, since
+it will be found using transformation rules if it isn't given. This is because
+.CW .out
+is the null suffix by default and a transformation exists from
+.CW .c
+to
+.CW .out .
+Just thought I'd throw that in.)
+.LP
+If a file exists in two directories on the same search path, the file
+in the first directory on the path will be the one PMake uses. So if
+you have a large system spread over many directories, it would behoove
+you to follow a naming convention that avoids such conflicts.
+.LP
+Something you should know about the way search paths are implemented
+is that each directory is read, and its contents cached, exactly once
+\&\*- when it is first encountered \*- so any changes to the
+directories while PMake is running will not be noted when searching
+for implicit sources, nor will they be found when PMake attempts to
+discover when the file was last modified, unless the file was created in the
+current directory. While people have suggested that PMake should read
+the directories each time, my experience suggests that the caching seldom
+causes problems. In addition, not caching the directories slows things
+down enormously because of PMake's attempts to apply transformation
+rules through non-existent files \*- the number of extra file-system
+searches is truly staggering, especially if many files without
+suffixes are used and the null suffix isn't changed from
+.CW .out .
+.xH 2 Archives and Libraries
+.LP
+.UX
+and Sprite allow you to merge files into an archive using the
+.CW ar
+command. Further, if the files are relocatable object files, you can
+run
+.CW ranlib
+on the archive and get yourself a library that you can link into any
+program you want. The main problem with archives is they double the
+space you need to store the archived files, since there's one copy in
+the archive and one copy out by itself. The problem with libraries is
+you usually think of them as
+.CW -lm
+rather than
+.CW /usr/lib/libm.a
+and the linker thinks they're out-of-date if you so much as look at
+them.
+.LP
+PMake solves the problem with archives by allowing you to tell it to
+examine the files in the archives (so you can remove the individual
+files without having to regenerate them later). To handle the problem
+with libraries, PMake adds an additional way of deciding if a library
+is out-of-date:
+.IP \(bu 2
+If the table of contents is older than the library, or is missing, the
+library is out-of-date.
+.LP
+A library is any target that looks like
+.CW \-l name'' ``
+or that ends in a suffix that was marked as a library using the
+.CW .LIBS
+target.
+.CW .a
+is so marked in the system makefile.
+.LP
+Members of an archive are specified as
+``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''.
+Thus
+.CW libdix.a(window.o) '' ``'
+specifies the file
+.CW window.o
+in the archive
+.CW libdix.a .
+You may also use wildcards to specify the members of the archive. Just
+remember that most the wildcard characters will only find
+.I existing
+files.
+.LP
+A file that is a member of an archive is treated specially. If the
+file doesn't exist, but it is in the archive, the modification time
+recorded in the archive is used for the file when determining if the
+file is out-of-date. When figuring out how to make an archived member target
+(not the file itself, but the file in the archive \*- the
+\fIarchive\fP(\fImember\fP) target), special care is
+taken with the transformation rules, as follows:
+.IP \(bu 2
+\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP.
+.IP \(bu 2
+The transformation from the \fImember\fP's suffix to the
+\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target.
+.IP \(bu 2
+The \fIarchive\fP(\fImember\fP)'s
+.CW .TARGET
+variable is set to the name of the \fImember\fP if \fImember\fP is
+actually a target, or the path to the member file if \fImember\fP is
+only a source.
+.IP \(bu 2
+The
+.CW .ARCHIVE
+variable for the \fIarchive\fP(\fImember\fP) target is set to the name
+of the \fIarchive\fP.
+.Ix 0 def variable local .ARCHIVE
+.Ix 0 def .ARCHIVE
+.IP \(bu 2
+The
+.CW .MEMBER
+variable is set to the actual string inside the parentheses. In most
+cases, this will be the same as the
+.CW .TARGET
+variable.
+.Ix 0 def variable local .MEMBER
+.Ix 0 def .MEMBER
+.IP \(bu 2
+The \fIarchive\fP(\fImember\fP)'s place in the local variables of the
+targets that depend on it is taken by the value of its
+.CW .TARGET
+variable.
+.LP
+Thus, a program library could be created with the following makefile:
+.DS
+\&.o.a :
+ ...
+ rm -f $(.TARGET:T)
+OBJS = obj1.o obj2.o obj3.o
+libprog.a : libprog.a($(OBJS))
+ ar cru $(.TARGET) $(.OODATE)
+ ranlib $(.TARGET)
+.DE
+This will cause the three object files to be compiled (if the
+corresponding source files were modified after the object file or, if
+that doesn't exist, the archived object file), the out-of-date ones
+archived in
+.CW libprog.a ,
+a table of contents placed in the archive and the newly-archived
+object files to be removed.
+.LP
+All this is used in the
+.CW makelib.mk
+system makefile to create a single library with ease. This makefile
+looks like this:
+.DS
+.SM
+#
+# Rules for making libraries. The object files that make up the library are
+# removed once they are archived.
+#
+# To make several libararies in parallel, you should define the variable
+# "many_libraries". This will serialize the invocations of ranlib.
+#
+# To use, do something like this:
+#
+# OBJECTS = <files in the library>
+#
+# fish.a: fish.a($(OBJECTS)) MAKELIB
+#
+#
+
+#ifndef _MAKELIB_MK
+_MAKELIB_MK =
+
+#include <po.mk>
+
+\&.po.a .o.a :
+ ...
+ rm -f $(.MEMBER)
+
+ARFLAGS ?= crl
+
+#
+# Re-archive the out-of-date members and recreate the library's table of
+# contents using ranlib. If many_libraries is defined, put the ranlib off
+# til the end so many libraries can be made at once.
+#
+MAKELIB : .USE .PRECIOUS
+ ar $(ARFLAGS) $(.TARGET) $(.OODATE)
+#ifndef no_ranlib
+# ifdef many_libraries
+ ...
+# endif many_libraries
+ ranlib $(.TARGET)
+#endif no_ranlib
+
+#endif _MAKELIB_MK
+.DE
+.xH 2 On the Condition...
+.Rd 1
+.LP
+Like the C compiler before it, PMake allows you to configure the makefile,
+based on the current environment, using conditional statements. A
+conditional looks like this:
+.DS
+#if \fIboolean expression\fP
+\fIlines\fP
+#elif \fIanother boolean expression\fP
+\fImore lines\fP
+#else
+\fIstill more lines\fP
+#endif
+.DE
+They may be nested to a maximum depth of 30 and may occur anywhere
+(except in a comment, of course). The
+.CW # '' ``
+must the very first character on the line.
+.LP
+Each
+.I "boolean expression"
+is made up of terms that look like function calls, the standard C
+boolean operators
+.CW && ,
+.CW || ,
+and
+.CW ! ,
+and the standard relational operators
+.CW == ,
+.CW != ,
+.CW > ,
+.CW >= ,
+.CW < ,
+and
+.CW <= ,
+with
+.CW ==
+and
+.CW !=
+being overloaded to allow string comparisons as well.
+.CW &&
+represents logical AND;
+.CW ||
+is logical OR and
+.CW !
+is logical NOT. The arithmetic and string operators take precedence
+over all three of these operators, while NOT takes precedence over
+AND, which takes precedence over OR. This precedence may be
+overridden with parentheses, and an expression may be parenthesized to
+your heart's content. Each term looks like a call on one of four
+functions:
+.nr pw 9
+.Ix 0 def make
+.Ix 0 def conditional make
+.Ix 0 def if make
+.IP make \n(pw
+The syntax is
+.CW make( \fItarget\fP\c
+.CW )
+where
+.I target
+is a target in the makefile. This is true if the given target was
+specified on the command line, or as the source for a
+.CW .MAIN
+target (note that the sources for
+.CW .MAIN
+are only used if no targets were given on the command line).
+.IP defined \n(pw
+.Ix 0 def defined
+.Ix 0 def conditional defined
+.Ix 0 def if defined
+The syntax is
+.CW defined( \fIvariable\fP\c
+.CW )
+and is true if
+.I variable
+is defined. Certain variables are defined in the system makefile that
+identify the system on which PMake is being run.
+.IP exists \n(pw
+.Ix 0 def exists
+.Ix 0 def conditional exists
+.Ix 0 def if exists
+The syntax is
+.CW exists( \fIfile\fP\c
+.CW )
+and is true if the file can be found on the global search path (i.e.
+that defined by
+.CW .PATH
+targets, not by
+.CW .PATH \fIsuffix\fP
+targets).
+.IP empty \n(pw
+.Ix 0 def empty
+.Ix 0 def conditional empty
+.Ix 0 def if empty
+This syntax is much like the others, except the string inside the
+parentheses is of the same form as you would put between parentheses
+when expanding a variable, complete with modifiers and everything. The
+function returns true if the resulting string is empty (NOTE: an undefined
+variable in this context will cause at the very least a warning
+message about a malformed conditional, and at the worst will cause the
+process to stop once it has read the makefile. If you want to check
+for a variable being defined or empty, use the expression
+.CW !defined( \fIvar\fP\c ``
+.CW ") || empty(" \fIvar\fP\c
+.CW ) ''
+as the definition of
+.CW ||
+will prevent the
+.CW empty()
+from being evaluated and causing an error, if the variable is
+undefined). This can be used to see if a variable contains a given
+word, for example:
+.DS
+#if !empty(\fIvar\fP:M\fIword\fP)
+.DE
+.LP
+The arithmetic and string operators may only be used to test the value
+of a variable. The lefthand side must contain the variable expansion,
+while the righthand side contains either a string, enclosed in
+double-quotes, or a number. The standard C numeric conventions (except
+for specifying an octal number) apply to both sides. E.g.
+.DS
+#if $(OS) == 4.3
+
+#if $(MACHINE) == "sun3"
+
+#if $(LOAD_ADDR) < 0xc000
+.DE
+are all valid conditionals. In addition, the numeric value of a
+variable can be tested as a boolean as follows:
+.DS
+#if $(LOAD)
+.DE
+would see if
+.CW LOAD
+contains a non-zero value and
+.DS
+#if !$(LOAD)
+.DE
+would test if
+.CW LOAD
+contains a zero value.
+.LP
+In addition to the bare
+.CW #if ,'' ``
+there are other forms that apply one of the first two functions to each
+term. They are as follows:
+.DS
+ ifdef \fRdefined\fP
+ ifndef \fR!defined\fP
+ ifmake \fRmake\fP
+ ifnmake \fR!make\fP
+.DE
+There are also the ``else if'' forms:
+.CW elif ,
+.CW elifdef ,
+.CW elifndef ,
+.CW elifmake ,
+and
+.CW elifnmake .
+.LP
+For instance, if you wish to create two versions of a program, one of which
+is optimized (the production version) and the other of which is for debugging
+(has symbols for dbx), you have two choices: you can create two
+makefiles, one of which uses the
+.CW \-g
+flag for the compilation, while the other uses the
+.CW \-O
+flag, or you can use another target (call it
+.CW debug )
+to create the debug version. The construct below will take care of
+this for you. I have also made it so defining the variable
+.CW DEBUG
+(say with
+.CW "pmake -D DEBUG" )
+will also cause the debug version to be made.
+.DS
+#if defined(DEBUG) || make(debug)
+CFLAGS += -g
+#else
+CFLAGS += -O
+#endif
+.DE
+There are, of course, problems with this approach. The most glaring
+annoyance is that if you want to go from making a debug version to
+making a production version, you have to remove all the object files,
+or you will get some optimized and some debug versions in the same
+program. Another annoyance is you have to be careful not to make two
+targets that ``conflict'' because of some conditionals in the
+makefile. For instance
+.DS
+#if make(print)
+FORMATTER = ditroff -Plaser_printer
+#endif
+#if make(draft)
+FORMATTER = nroff -Pdot_matrix_printer
+#endif
+.DE
+would wreak havok if you tried
+.CW "pmake draft print" '' ``
+since you would use the same formatter for each target. As I said,
+this all gets somewhat complicated.
+.xH 2 A Shell is a Shell is a Shell
+.Rd 7
+.LP
+In normal operation, the Bourne Shell (better known as
+.CW sh '') ``
+is used to execute the commands to re-create targets. PMake also allows you
+to specify a different shell for it to use when executing these
+commands. There are several things PMake must know about the shell you
+wish to use. These things are specified as the sources for the
+.CW .SHELL
+.Ix 0 ref .SHELL
+.Ix 0 ref target .SHELL
+target by keyword, as follows:
+.IP "\fBpath=\fP\fIpath\fP"
+PMake needs to know where the shell actually resides, so it can
+execute it. If you specify this and nothing else, PMake will use the
+last component of the path and look in its table of the shells it
+knows and use the specification it finds, if any. Use this if you just
+want to use a different version of the Bourne or C Shell (yes, PMake knows
+how to use the C Shell too).
+.IP "\fBname=\fP\fIname\fP"
+This is the name by which the shell is to be known. It is a single
+word and, if no other keywords are specified (other than
+.B path ),
+it is the name by which PMake attempts to find a specification for
+it (as mentioned above). You can use this if you would just rather use
+the C Shell than the Bourne Shell
+.CW ".SHELL: name=csh" '' (``
+will do it).
+.IP "\fBquiet=\fP\fIecho-off command\fP"
+As mentioned before, PMake actually controls whether commands are
+printed by introducing commands into the shell's input stream. This
+keyword, and the next two, control what those commands are. The
+.B quiet
+keyword is the command used to turn echoing off. Once it is turned
+off, echoing is expected to remain off until the echo-on command is given.
+.IP "\fBecho=\fP\fIecho-on command\fP"
+The command PMake should give to turn echoing back on again.
+.IP "\fBfilter=\fP\fIprinted echo-off command\fP"
+Many shells will echo the echo-off command when it is given. This
+keyword tells PMake in what format the shell actually prints the
+echo-off command. Wherever PMake sees this string in the shell's
+output, it will delete it and any following whitespace, up to and
+including the next newline. See the example at the end of this section
+for more details.
+.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP"
+Unless a target has been marked
+.CW .SILENT ,
+PMake wants to start the shell running with echoing on. To do this, it
+passes this flag to the shell as one of its arguments. If either this
+or the next flag begins with a `\-', the flags will be passed to the
+shell as separate arguments. Otherwise, the two will be concatenated
+(if they are used at the same time, of course).
+.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP"
+Likewise, unless a target is marked
+.CW .IGNORE ,
+PMake wishes error-checking to be on from the very start. To this end,
+it will pass this flag to the shell as an argument. The same rules for
+an initial `\-' apply as for the
+.B echoFlag .
+.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP"
+Just as for echo-control, error-control is achieved by inserting
+commands into the shell's input stream. This is the command to make
+the shell check for errors. It also serves another purpose if the
+shell doesn't have error-control as commands, but I'll get into that
+in a minute. Again, once error checking has been turned on, it is
+expected to remain on until it is turned off again.
+.IP "\fBignore=\fP\fIcommand to turn error checking off\fP"
+This is the command PMake uses to turn error checking off. It has
+another use if the shell doesn't do error-control, but I'll tell you
+about that.\|.\|.\|now.
+.IP "\fBhasErrCtl=\fP\fIyes or no\fP"
+This takes a value that is either
+.B yes
+or
+.B no .
+Now you might think that the existence of the
+.B check
+and
+.B ignore
+keywords would be enough to tell PMake if the shell can do
+error-control, but you'd be wrong. If
+.B hasErrCtl
+is
+.B yes ,
+PMake uses the check and ignore commands in a straight-forward manner.
+If this is
+.B no ,
+however, their use is rather different. In this case, the check
+command is used as a template, in which the string
+.B %s
+is replaced by the command that's about to be executed, to produce a
+command for the shell that will echo the command to be executed. The
+ignore command is also used as a template, again with
+.B %s
+replaced by the command to be executed, to produce a command that will
+execute the command to be executed and ignore any error it returns.
+When these strings are used as templates, you must provide newline(s)
+.CW \en '') (``
+in the appropriate place(s).
+.LP
+The strings that follow these keywords may be enclosed in single or
+double quotes (the quotes will be stripped off) and may contain the
+usual C backslash-characters (\en is newline, \er is return, \eb is
+backspace, \e' escapes a single-quote inside single-quotes, \e"
+escapes a double-quote inside double-quotes). Now for an example.
+.LP
+This is actually the contents of the
+.CW <shx.mk>
+system makefile, and causes PMake to use the Bourne Shell in such a
+way that each command is printed as it is executed. That is, if more
+than one command is given on a line, each will be printed separately.
+Similarly, each time the body of a loop is executed, the commands
+within that loop will be printed, etc. The specification runs like
+this:
+.DS
+#
+# This is a shell specification to have the bourne shell echo
+# the commands just before executing them, rather than when it reads
+# them. Useful if you want to see how variables are being expanded, etc.
+#
+\&.SHELL : path=/bin/sh \e
+ quiet="set -" \e
+ echo="set -x" \e
+ filter="+ set - " \e
+ echoFlag=x \e
+ errFlag=e \e
+ hasErrCtl=yes \e
+ check="set -e" \e
+ ignore="set +e"
+.DE
+.LP
+It tells PMake the following:
+.Bp
+The shell is located in the file
+.CW /bin/sh .
+It need not tell PMake that the name of the shell is
+.CW sh
+as PMake can figure that out for itself (it's the last component of
+the path).
+.Bp
+The command to stop echoing is
+.CW "set -" .
+.Bp
+The command to start echoing is
+.CW "set -x" .
+.Bp
+When the echo off command is executed, the shell will print
+.CW "+ set - "
+(The `+' comes from using the
+.CW \-x
+flag (rather than the
+.CW \-v
+flag PMake usually uses)). PMake will remove all occurences of this
+string from the output, so you don't notice extra commands you didn't
+put there.
+.Bp
+The flag the Bourne Shell will take to start echoing in this way is
+the
+.CW \-x
+flag. The Bourne Shell will only take its flag arguments concatenated
+as its first argument, so neither this nor the
+.B errFlag
+specification begins with a \-.
+.Bp
+The flag to use to turn error-checking on from the start is
+.CW \-e .
+.Bp
+The shell can turn error-checking on and off, and the commands to do
+so are
+.CW "set +e"
+and
+.CW "set -e" ,
+respectively.
+.LP
+I should note that this specification is for Bourne Shells that are
+not part of Berkeley
+.UX ,
+as shells from Berkeley don't do error control. You can get a similar
+effect, however, by changing the last three lines to be:
+.DS
+ hasErrCtl=no \e
+ check="echo \e"+ %s\e"\en" \e
+ ignore="sh -c '%s || exit 0\en"
+.DE
+.LP
+This will cause PMake to execute the two commands
+.DS
+echo "+ \fIcmd\fP"
+sh -c '\fIcmd\fP || true'
+.DE
+for each command for which errors are to be ignored. (In case you are
+wondering, the thing for
+.CW ignore
+tells the shell to execute another shell without error checking on and
+always exit 0, since the
+.B ||
+causes the
+.CW "exit 0"
+to be executed only if the first command exited non-zero, and if the
+first command exited zero, the shell will also exit zero, since that's
+the last command it executed).
+.xH 2 Compatibility
+.Ix 0 ref compatibility
+.LP
+There are three (well, 3 \(12) levels of backwards-compatibility built
+into PMake. Most makefiles will need none at all. Some may need a
+little bit of work to operate correctly when run in parallel. Each
+level encompasses the previous levels (e.g.
+.B \-B
+(one shell per command) implies
+.B \-V )
+The three levels are described in the following three sections.
+.xH 3 DEFCON 3 \*- Variable Expansion
+.Ix 0 ref compatibility
+.LP
+As noted before, PMake will not expand a variable unless it knows of a
+value for it. This can cause problems for makefiles that expect to
+leave variables undefined except in special circumstances (e.g. if
+more flags need to be passed to the C compiler or the output from a
+text processor should be sent to a different printer). If the
+variables are enclosed in curly braces
+.CW ${PRINTER} ''), (``
+the shell will let them pass. If they are enclosed in parentheses,
+however, the shell will declare a syntax error and the make will come
+to a grinding halt.
+.LP
+You have two choices: change the makefile to define the variables
+(their values can be overridden on the command line, since that's
+where they would have been set if you used Make, anyway) or always give the
+.B \-V
+flag (this can be done with the
+.CW .MAKEFLAGS
+target, if you want).
+.xH 3 DEFCON 2 \*- The Number of the Beast
+.Ix 0 ref compatibility
+.LP
+Then there are the makefiles that expect certain commands, such as
+changing to a different directory, to not affect other commands in a
+target's creation script. You can solve this is either by going
+back to executing one shell per command (which is what the
+.B \-B
+flag forces PMake to do), which slows the process down a good bit and
+requires you to use semicolons and escaped newlines for shell constructs, or
+by changing the makefile to execute the offending command(s) in a subshell
+(by placing the line inside parentheses), like so:
+.DS
+install :: .MAKE
+ (cd src; $(.PMAKE) install)
+ (cd lib; $(.PMAKE) install)
+ (cd man; $(.PMAKE) install)
+.DE
+.Ix 0 ref operator double-colon
+.Ix 0 ref variable global .PMAKE
+.Ix 0 ref .PMAKE
+.Ix 0 ref .MAKE
+.Ix 0 ref attribute .MAKE
+This will always execute the three makes (even if the
+.B \-n
+flag was given) because of the combination of the ``::'' operator and
+the
+.CW .MAKE
+attribute. Each command will change to the proper directory to perform
+the install, leaving the main shell in the directory in which it started.
+.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery"
+.Ix 0 ref compatibility
+.LP
+The final category of makefile is the one where every command requires
+input, the dependencies are incompletely specified, or you simply
+cannot create more than one target at a time, as mentioned earlier. In
+addition, you may not have the time or desire to upgrade the makefile
+to run smoothly with PMake. If you are the conservative sort, this is
+the compatibility mode for you. It is entered either by giving PMake
+the
+.B \-M
+flag (for Make), or by executing PMake as
+.CW make .'' ``
+In either case, PMake performs things exactly like Make (while still
+supporting most of the nice new features PMake provides). This
+includes:
+.IP \(bu 2
+No parallel execution.
+.IP \(bu 2
+Targets are made in the exact order specified by the makefile. The
+sources for each target are made in strict left-to-right order, etc.
+.IP \(bu 2
+A single Bourne shell is used to execute each command, thus the
+shell's
+.CW $$
+variable is useless, changing directories doesn't work across command
+lines, etc.
+.IP \(bu 2
+If no special characters exist in a command line, PMake will break the
+command into words itself and execute the command directly, without
+executing a shell first. The characters that cause PMake to execute a
+shell are:
+.CW # ,
+.CW = ,
+.CW | ,
+.CW ^ ,
+.CW ( ,
+.CW ) ,
+.CW { ,
+.CW } ,
+.CW ; ,
+.CW & ,
+.CW < ,
+.CW > ,
+.CW * ,
+.CW ? ,
+.CW [ ,
+.CW ] ,
+.CW : ,
+.CW $ ,
+.CW ` ,
+and
+.CW \e .
+You should notice that these are all the characters that are given
+special meaning by the shell (except
+.CW '
+and
+.CW " ,
+which PMake deals with all by its lonesome).
+.IP \(bu 2
+The use of the null suffix is turned off.
+.Ix 0 ref "null suffix"
+.Ix 0 ref suffix null
+.xH 2 The Way Things Work
+.LP
+When PMake reads the makefile, it parses sources and targets into
+nodes in a graph. The graph is directed only in the sense that PMake
+knows which way is up. Each node contains not only links to all its
+parents and children (the nodes that depend on it and those on which
+it depends, respectively), but also a count of the number of its
+children that have already been processed.
+.LP
+The most important thing to know about how PMake uses this graph is
+that the traversal is breadth-first and occurs in two passes.
+.LP
+After PMake has parsed the makefile, it begins with the nodes the user
+has told it to make (either on the command line, or via a
+.CW .MAIN
+target, or by the target being the first in the file not labeled with
+the
+.CW .NOTMAIN
+attribute) placed in a queue. It continues to take the node off the
+front of the queue, mark it as something that needs to be made, pass
+the node to
+.CW Suff_FindDeps
+(mentioned earlier) to find any implicit sources for the node, and
+place all the node's children that have yet to be marked at the end of
+the queue. If any of the children is a
+.CW .USE
+rule, its attributes are applied to the parent, then its commands are
+appended to the parent's list of commands and its children are linked
+to its parent. The parent's unmade children counter is then decremented
+(since the
+.CW .USE
+node has been processed). You will note that this allows a
+.CW .USE
+node to have children that are
+.CW .USE
+nodes and the rules will be applied in sequence.
+If the node has no children, it is placed at the end of
+another queue to be examined in the second pass. This process
+continues until the first queue is empty.
+.LP
+At this point, all the leaves of the graph are in the examination
+queue. PMake removes the node at the head of the queue and sees if it
+is out-of-date. If it is, it is passed to a function that will execute
+the commands for the node asynchronously. When the commands have
+completed, all the node's parents have their unmade children counter
+decremented and, if the counter is then 0, they are placed on the
+examination queue. Likewise, if the node is up-to-date. Only those
+parents that were marked on the downward pass are processed in this
+way. Thus PMake traverses the graph back up to the nodes the user
+instructed it to create. When the examination queue is empty and no
+shells are running to create a target, PMake is finished.
+.LP
+Once all targets have been processed, PMake executes the commands
+attached to the
+.CW .END
+target, either explicitly or through the use of an ellipsis in a shell
+script. If there were no errors during the entire process but there
+are still some targets unmade (PMake keeps a running count of how many
+targets are left to be made), there is a cycle in the graph. PMake does
+a depth-first traversal of the graph to find all the targets that
+weren't made and prints them out one by one.
+.xH 1 Answers to Exercises
+.IP (3.1)
+This is something of a trick question, for which I apologize. The
+trick comes from the UNIX definition of a suffix, which PMake doesn't
+necessarily share. You will have noticed that all the suffixes used in
+this tutorial (and in UNIX in general) begin with a period
+.CW .ms , (
+.CW .c ,
+etc.). Now, PMake's idea of a suffix is more like English's: it's the
+characters at the end of a word. With this in mind, one possible
+.Ix 0 def suffix
+solution to this problem goes as follows:
+.DS I
+\&.SUFFIXES : ec.exe .exe ec.obj .obj .asm
+ec.objec.exe .obj.exe :
+ link -o $(.TARGET) $(.IMPSRC)
+\&.asmec.obj :
+ asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC)
+\&.asm.obj :
+ asm -o $(.TARGET) $(.IMPSRC)
+.DE
+.IP (3.2)
+The trick to this one lies in the ``:='' variable-assignment operator
+and the ``:S'' variable-expansion modifier.
+.Ix 0 ref variable assignment expanded
+.Ix 0 ref variable expansion modified
+.Ix 0 ref modifier substitute
+.Ix 0 ref :S
+.Ix 0 ref :=
+Basically what you want is to take the pointer variable, so to speak,
+and transform it into an invocation of the variable at which it
+points. You might try something like
+.DS I
+$(PTR:S/^/\e$(/:S/$/))
+.DE
+which places
+.CW $( '' ``
+at the front of the variable name and
+.CW ) '' ``
+at the end, thus transforming
+.CW VAR ,'' ``
+for example, into
+.CW $(VAR) ,'' ``
+which is just what we want. Unfortunately (as you know if you've tried
+it), since, as it says in the hint, PMake does no further substitution
+on the result of a modified expansion, that's \fIall\fP you get. The
+solution is to make use of ``:='' to place that string into yet
+another variable, then invoke the other variable directly:
+.DS I
+*PTR := $(PTR:S/^/\e$(/:S/$/)/)
+.DE
+You can then use
+.CW $(*PTR) '' ``
+to your heart's content.
+.de Gp
+.XP
+\&\fB\\$1:\fP
+..
+.xH 1 Glossary of Jargon
+.Gp "attribute"
+A property given to a target that causes PMake to treat it differently.
+.Gp "command script"
+The lines immediately following a dependency line that specify
+commands to execute to create each of the targets on the dependency
+line. Each line in the command script must begin with a tab.
+.Gp "command-line variable"
+A variable defined in an argument when PMake is first executed.
+Overrides all assignments to the same variable name in the makefile.
+.Gp "conditional"
+A construct much like that used in C that allows a makefile to be
+configured on the fly based on the local environment, or on what is being
+made by that invocation of PMake.
+.Gp "creation script"
+Commands used to create a target. See ``command script.''
+.Gp "dependency"
+The relationship between a source and a target. This comes in three
+flavors, as indicated by the operator between the target and the
+source. `:' gives a straight time-wise dependency (if the target is
+older than the source, the target is out-of-date), while `!' provides
+simply an ordering and always considers the target out-of-date. `::'
+is much like `:', save it creates multiple instances of a target each
+of which depends on its own list of sources.
+.Gp "dynamic source"
+This refers to a source that has a local variable invocation in it. It
+allows a single dependency line to specify a different source for each
+target on the line.
+.Gp "global variable"
+Any variable defined in a makefile. Takes precedence over variables
+defined in the environment, but not over command-line or local variables.
+.Gp "input graph"
+What PMake constructs from a makefile. Consists of nodes made of the
+targets in the makefile, and the links between them (the
+dependencies). The links are directed (from source to target) and
+there may not be any cycles (loops) in the graph.
+.Gp "local variable"
+A variable defined by PMake visible only in a target's shell script.
+There are seven local variables, not all of which are defined for
+every target:
+.CW .TARGET ,
+.CW .ALLSRC ,
+.CW .OODATE ,
+.CW .PREFIX ,
+.CW .IMPSRC ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER .
+.CW .TARGET ,
+.CW .PREFIX ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER
+may be used on dependency lines to create ``dynamic sources.''
+.Gp "makefile"
+A file that describes how a system is built. If you don't know what it
+is after reading this tutorial.\|.\|.\|.
+.Gp "modifier"
+A letter, following a colon, used to alter how a variable is expanded.
+It has no effect on the variable itself.
+.Gp "operator"
+What separates a source from a target (on a dependency line) and specifies
+the relationship between the two. There are three:
+.CW : ', `
+.CW :: ', `
+and
+.CW ! '. `
+.Gp "search path"
+A list of directories in which a file should be sought. PMake's view
+of the contents of directories in a search path does not change once
+the makefile has been read. A file is sought on a search path only if
+it is exclusively a source.
+.Gp "shell"
+A program to which commands are passed in order to create targets.
+.Gp "source"
+Anything to the right of an operator on a dependency line. Targets on
+the dependency line are usually created from the sources.
+.Gp "special target"
+A target that causes PMake to do special things when it's encountered.
+.Gp "suffix"
+The tail end of a file name. Usually begins with a period,
+.CW .c
+or
+.CW .ms ,
+e.g.
+.Gp "target"
+A word to the left of the operator on a dependency line. More
+generally, any file that PMake might create. A file may be (and often
+is) both a target and a source (what it is depends on how PMake is
+looking at it at the time \*- sort of like the wave/particle duality
+of light, you know).
+.Gp "transformation rule"
+A special construct in a makefile that specifies how to create a file
+of one type from a file of another, as indicated by their suffixes.
+.Gp "variable expansion"
+The process of substituting the value of a variable for a reference to
+it. Expansion may be altered by means of modifiers.
+.Gp "variable"
+A place in which to store text that may be retrieved later. Also used
+to define the local environment. Conditionals exist that test whether
+a variable is defined or not.
+.bp
+.\" Output table of contents last, with an entry for the index, making
+.\" sure to save and restore the last real page number for the index...
+.nr @n \n(PN+1
+.\" We are not generating an index
+.\" .XS \n(@n
+.\" Index
+.\" .XE
+.nr %% \n%
+.PX
+.nr % \n(%%
diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c
new file mode 100644
index 0000000..d5046e9
--- /dev/null
+++ b/usr.bin/make/arch.c
@@ -0,0 +1,1091 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+/*-
+ * arch.c --
+ * Functions to manipulate libraries, archives and their members.
+ *
+ * Once again, cacheing/hashing comes into play in the manipulation
+ * of archives. The first time an archive is referenced, all of its members'
+ * headers are read and hashed and the archive closed again. All hashed
+ * archives are kept on a list which is searched each time an archive member
+ * is referenced.
+ *
+ * The interface to this module is:
+ * Arch_ParseArchive Given an archive specification, return a list
+ * of GNode's, one for each member in the spec.
+ * FAILURE is returned if the specification is
+ * invalid for some reason.
+ *
+ * Arch_Touch Alter the modification time of the archive
+ * member described by the given node to be
+ * the current time.
+ *
+ * Arch_TouchLib Update the modification time of the library
+ * described by the given node. This is special
+ * because it also updates the modification time
+ * of the library's table of contents.
+ *
+ * Arch_MTime Find the modification time of a member of
+ * an archive *in the archive*. The time is also
+ * placed in the member's GNode. Returns the
+ * modification time.
+ *
+ * Arch_MemTime Find the modification time of a member of
+ * an archive. Called when the member doesn't
+ * already exist. Looks in the archive for the
+ * modification time. Returns the modification
+ * time.
+ *
+ * Arch_FindLib Search for a library along a path. The
+ * library name in the GNode should be in
+ * -l<name> format.
+ *
+ * Arch_LibOODate Special function to decide if a library node
+ * is out-of-date.
+ *
+ * Arch_Init Initialize this module.
+ *
+ * Arch_End Cleanup this module.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "config.h"
+
+static Lst archives; /* Lst of archives we've already examined */
+
+typedef struct Arch {
+ char *name; /* Name of archive */
+ Hash_Table members; /* All the members of the archive described
+ * by <name, struct ar_hdr *> key/value pairs */
+} Arch;
+
+static int ArchFindArchive __P((ClientData, ClientData));
+static void ArchFree __P((ClientData));
+static struct ar_hdr *ArchStatMember __P((char *, char *, Boolean));
+static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *));
+
+/*-
+ *-----------------------------------------------------------------------
+ * ArchFree --
+ * Free memory used by an archive
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+ArchFree(ap)
+ ClientData ap;
+{
+ Arch *a = (Arch *) ap;
+ Hash_Search search;
+ Hash_Entry *entry;
+
+ /* Free memory from hash entries */
+ for (entry = Hash_EnumFirst(&a->members, &search);
+ entry != (Hash_Entry *)NULL;
+ entry = Hash_EnumNext(&search))
+ free((Address) Hash_GetValue (entry));
+
+ free(a->name);
+ Hash_DeleteTable(&a->members);
+ free((Address) a);
+}
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_ParseArchive --
+ * Parse the archive specification in the given line and find/create
+ * the nodes for the specified archive members, placing their nodes
+ * on the given list.
+ *
+ * Results:
+ * SUCCESS if it was a valid specification. The linePtr is updated
+ * to point to the first non-space after the archive spec. The
+ * nodes for the members are placed on the given list.
+ *
+ * Side Effects:
+ * Some nodes may be created. The given list is extended.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Arch_ParseArchive (linePtr, nodeLst, ctxt)
+ char **linePtr; /* Pointer to start of specification */
+ Lst nodeLst; /* Lst on which to place the nodes */
+ GNode *ctxt; /* Context in which to expand variables */
+{
+ register char *cp; /* Pointer into line */
+ GNode *gn; /* New node */
+ char *libName; /* Library-part of specification */
+ char *memName; /* Member-part of specification */
+ char nameBuf[MAKE_BSIZE]; /* temporary place for node name */
+ char saveChar; /* Ending delimiter of member-name */
+ Boolean subLibName; /* TRUE if libName should have/had
+ * variable substitution performed on it */
+
+ libName = *linePtr;
+
+ subLibName = FALSE;
+
+ for (cp = libName; *cp != '(' && *cp != '\0'; cp++) {
+ if (*cp == '$') {
+ /*
+ * Variable spec, so call the Var module to parse the puppy
+ * so we can safely advance beyond it...
+ */
+ int length;
+ Boolean freeIt;
+ char *result;
+
+ result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt);
+ if (result == var_Error) {
+ return(FAILURE);
+ } else {
+ subLibName = TRUE;
+ }
+
+ if (freeIt) {
+ free(result);
+ }
+ cp += length-1;
+ }
+ }
+
+ *cp++ = '\0';
+ if (subLibName) {
+ libName = Var_Subst(NULL, libName, ctxt, TRUE);
+ }
+
+
+ for (;;) {
+ /*
+ * First skip to the start of the member's name, mark that
+ * place and skip to the end of it (either white-space or
+ * a close paren).
+ */
+ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
+
+ while (*cp != '\0' && *cp != ')' && isspace (*cp)) {
+ cp++;
+ }
+ memName = cp;
+ while (*cp != '\0' && *cp != ')' && !isspace (*cp)) {
+ if (*cp == '$') {
+ /*
+ * Variable spec, so call the Var module to parse the puppy
+ * so we can safely advance beyond it...
+ */
+ int length;
+ Boolean freeIt;
+ char *result;
+
+ result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt);
+ if (result == var_Error) {
+ return(FAILURE);
+ } else {
+ doSubst = TRUE;
+ }
+
+ if (freeIt) {
+ free(result);
+ }
+ cp += length;
+ } else {
+ cp++;
+ }
+ }
+
+ /*
+ * If the specification ends without a closing parenthesis,
+ * chances are there's something wrong (like a missing backslash),
+ * so it's better to return failure than allow such things to happen
+ */
+ if (*cp == '\0') {
+ printf("No closing parenthesis in archive specification\n");
+ return (FAILURE);
+ }
+
+ /*
+ * If we didn't move anywhere, we must be done
+ */
+ if (cp == memName) {
+ break;
+ }
+
+ saveChar = *cp;
+ *cp = '\0';
+
+ /*
+ * XXX: This should be taken care of intelligently by
+ * SuffExpandChildren, both for the archive and the member portions.
+ */
+ /*
+ * If member contains variables, try and substitute for them.
+ * This will slow down archive specs with dynamic sources, of course,
+ * since we'll be (non-)substituting them three times, but them's
+ * the breaks -- we need to do this since SuffExpandChildren calls
+ * us, otherwise we could assume the thing would be taken care of
+ * later.
+ */
+ if (doSubst) {
+ char *buf;
+ char *sacrifice;
+ char *oldMemName = memName;
+
+ memName = Var_Subst(NULL, memName, ctxt, TRUE);
+
+ /*
+ * Now form an archive spec and recurse to deal with nested
+ * variables and multi-word variable values.... The results
+ * are just placed at the end of the nodeLst we're returning.
+ */
+ buf = sacrifice = emalloc(strlen(memName)+strlen(libName)+3);
+
+ sprintf(buf, "%s(%s)", libName, memName);
+
+ if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
+ /*
+ * Must contain dynamic sources, so we can't deal with it now.
+ * Just create an ARCHV node for the thing and let
+ * SuffExpandChildren handle it...
+ */
+ gn = Targ_FindNode(buf, TARG_CREATE);
+
+ if (gn == NILGNODE) {
+ free(buf);
+ return(FAILURE);
+ } else {
+ gn->type |= OP_ARCHV;
+ (void)Lst_AtEnd(nodeLst, (ClientData)gn);
+ }
+ } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) {
+ /*
+ * Error in nested call -- free buffer and return FAILURE
+ * ourselves.
+ */
+ free(buf);
+ return(FAILURE);
+ }
+ /*
+ * Free buffer and continue with our work.
+ */
+ free(buf);
+ } else if (Dir_HasWildcards(memName)) {
+ Lst members = Lst_Init(FALSE);
+ char *member;
+
+ Dir_Expand(memName, dirSearchPath, members);
+ while (!Lst_IsEmpty(members)) {
+ member = (char *)Lst_DeQueue(members);
+
+ sprintf(nameBuf, "%s(%s)", libName, member);
+ free(member);
+ gn = Targ_FindNode (nameBuf, TARG_CREATE);
+ if (gn == NILGNODE) {
+ return (FAILURE);
+ } else {
+ /*
+ * We've found the node, but have to make sure the rest of
+ * the world knows it's an archive member, without having
+ * to constantly check for parentheses, so we type the
+ * thing with the OP_ARCHV bit before we place it on the
+ * end of the provided list.
+ */
+ gn->type |= OP_ARCHV;
+ (void) Lst_AtEnd (nodeLst, (ClientData)gn);
+ }
+ }
+ Lst_Destroy(members, NOFREE);
+ } else {
+ sprintf(nameBuf, "%s(%s)", libName, memName);
+ gn = Targ_FindNode (nameBuf, TARG_CREATE);
+ if (gn == NILGNODE) {
+ return (FAILURE);
+ } else {
+ /*
+ * We've found the node, but have to make sure the rest of the
+ * world knows it's an archive member, without having to
+ * constantly check for parentheses, so we type the thing with
+ * the OP_ARCHV bit before we place it on the end of the
+ * provided list.
+ */
+ gn->type |= OP_ARCHV;
+ (void) Lst_AtEnd (nodeLst, (ClientData)gn);
+ }
+ }
+ if (doSubst) {
+ free(memName);
+ }
+
+ *cp = saveChar;
+ }
+
+ /*
+ * If substituted libName, free it now, since we need it no longer.
+ */
+ if (subLibName) {
+ free(libName);
+ }
+
+ /*
+ * We promised the pointer would be set up at the next non-space, so
+ * we must advance cp there before setting *linePtr... (note that on
+ * entrance to the loop, cp is guaranteed to point at a ')')
+ */
+ do {
+ cp++;
+ } while (*cp != '\0' && isspace (*cp));
+
+ *linePtr = cp;
+ return (SUCCESS);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ArchFindArchive --
+ * See if the given archive is the one we are looking for. Called
+ * From ArchStatMember and ArchFindMember via Lst_Find.
+ *
+ * Results:
+ * 0 if it is, non-zero if it isn't.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ArchFindArchive (ar, archName)
+ ClientData ar; /* Current list element */
+ ClientData archName; /* Name we want */
+{
+ return (strcmp ((char *) archName, ((Arch *) ar)->name));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ArchStatMember --
+ * Locate a member of an archive, given the path of the archive and
+ * the path of the desired member.
+ *
+ * Results:
+ * A pointer to the current struct ar_hdr structure for the member. Note
+ * That no position is returned, so this is not useful for touching
+ * archive members. This is mostly because we have no assurances that
+ * The archive will remain constant after we read all the headers, so
+ * there's not much point in remembering the position...
+ *
+ * Side Effects:
+ *
+ *-----------------------------------------------------------------------
+ */
+static struct ar_hdr *
+ArchStatMember (archive, member, hash)
+ char *archive; /* Path to the archive */
+ char *member; /* Name of member. If it is a path, only the
+ * last component is used. */
+ Boolean hash; /* TRUE if archive should be hashed if not
+ * already so. */
+{
+#define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1)
+ FILE * arch; /* Stream to archive */
+ int size; /* Size of archive member */
+ char *cp; /* Useful character pointer */
+ char magic[SARMAG];
+ LstNode ln; /* Lst member containing archive descriptor */
+ Arch *ar; /* Archive descriptor */
+ Hash_Entry *he; /* Entry containing member's description */
+ struct ar_hdr arh; /* archive-member header for reading archive */
+ char memName[MAXPATHLEN+1];
+ /* Current member name while hashing. */
+
+ /*
+ * Because of space constraints and similar things, files are archived
+ * using their final path components, not the entire thing, so we need
+ * to point 'member' to the final component, if there is one, to make
+ * the comparisons easier...
+ */
+ cp = strrchr (member, '/');
+ if (cp != (char *) NULL) {
+ member = cp + 1;
+ }
+
+ ln = Lst_Find (archives, (ClientData) archive, ArchFindArchive);
+ if (ln != NILLNODE) {
+ ar = (Arch *) Lst_Datum (ln);
+
+ he = Hash_FindEntry (&ar->members, member);
+
+ if (he != (Hash_Entry *) NULL) {
+ return ((struct ar_hdr *) Hash_GetValue (he));
+ } else {
+ /* Try truncated name */
+ char copy[AR_MAX_NAME_LEN+1];
+ int len = strlen (member);
+
+ if (len > AR_MAX_NAME_LEN) {
+ len = AR_MAX_NAME_LEN;
+ strncpy(copy, member, AR_MAX_NAME_LEN);
+ copy[AR_MAX_NAME_LEN] = '\0';
+ }
+ if (he = Hash_FindEntry (&ar->members, copy))
+ return ((struct ar_hdr *) Hash_GetValue (he));
+ return ((struct ar_hdr *) NULL);
+ }
+ }
+
+ if (!hash) {
+ /*
+ * Caller doesn't want the thing hashed, just use ArchFindMember
+ * to read the header for the member out and close down the stream
+ * again. Since the archive is not to be hashed, we assume there's
+ * no need to allocate extra room for the header we're returning,
+ * so just declare it static.
+ */
+ static struct ar_hdr sarh;
+
+ arch = ArchFindMember(archive, member, &sarh, "r");
+
+ if (arch == (FILE *)NULL) {
+ return ((struct ar_hdr *)NULL);
+ } else {
+ fclose(arch);
+ return (&sarh);
+ }
+ }
+
+ /*
+ * We don't have this archive on the list yet, so we want to find out
+ * everything that's in it and cache it so we can get at it quickly.
+ */
+ arch = fopen (archive, "r");
+ if (arch == (FILE *) NULL) {
+ return ((struct ar_hdr *) NULL);
+ }
+
+ /*
+ * We use the ARMAG string to make sure this is an archive we
+ * can handle...
+ */
+ if ((fread (magic, SARMAG, 1, arch) != 1) ||
+ (strncmp (magic, ARMAG, SARMAG) != 0)) {
+ fclose (arch);
+ return ((struct ar_hdr *) NULL);
+ }
+
+ ar = (Arch *)emalloc (sizeof (Arch));
+ ar->name = strdup (archive);
+ Hash_InitTable (&ar->members, -1);
+ memName[AR_MAX_NAME_LEN] = '\0';
+
+ while (fread ((char *)&arh, sizeof (struct ar_hdr), 1, arch) == 1) {
+ if (strncmp ( arh.ar_fmag, ARFMAG, sizeof (arh.ar_fmag)) != 0) {
+ /*
+ * The header is bogus, so the archive is bad
+ * and there's no way we can recover...
+ */
+ fclose (arch);
+ Hash_DeleteTable (&ar->members);
+ free ((Address)ar);
+ return ((struct ar_hdr *) NULL);
+ } else {
+ (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name));
+ for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {
+ continue;
+ }
+ cp[1] = '\0';
+
+#ifdef AR_EFMT1
+ /*
+ * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+ * first <namelen> bytes of the file
+ */
+ if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
+ isdigit(memName[sizeof(AR_EFMT1) - 1])) {
+
+ unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
+
+ if (elen > MAXPATHLEN) {
+ fclose (arch);
+ Hash_DeleteTable (&ar->members);
+ free ((Address)ar);
+ return ((struct ar_hdr *) NULL);
+ }
+ if (fread (memName, elen, 1, arch) != 1) {
+ fclose (arch);
+ Hash_DeleteTable (&ar->members);
+ free ((Address)ar);
+ return ((struct ar_hdr *) NULL);
+ }
+ memName[elen] = '\0';
+ fseek (arch, -elen, 1);
+ if (DEBUG(ARCH) || DEBUG(MAKE)) {
+ printf("ArchStat: Extended format entry for %s\n", memName);
+ }
+ }
+#endif
+
+ he = Hash_CreateEntry (&ar->members, memName, (Boolean *)NULL);
+ Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr)));
+ memcpy ((Address)Hash_GetValue (he), (Address)&arh,
+ sizeof (struct ar_hdr));
+ }
+ /*
+ * We need to advance the stream's pointer to the start of the
+ * next header. Files are padded with newlines to an even-byte
+ * boundary, so we need to extract the size of the file from the
+ * 'size' field of the header and round it up during the seek.
+ */
+ arh.ar_size[sizeof(arh.ar_size)-1] = '\0';
+ size = (int) strtol(arh.ar_size, NULL, 10);
+ fseek (arch, (size + 1) & ~1, 1);
+ }
+
+ fclose (arch);
+
+ (void) Lst_AtEnd (archives, (ClientData) ar);
+
+ /*
+ * Now that the archive has been read and cached, we can look into
+ * the hash table to find the desired member's header.
+ */
+ he = Hash_FindEntry (&ar->members, member);
+
+ if (he != (Hash_Entry *) NULL) {
+ return ((struct ar_hdr *) Hash_GetValue (he));
+ } else {
+ return ((struct ar_hdr *) NULL);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ArchFindMember --
+ * Locate a member of an archive, given the path of the archive and
+ * the path of the desired member. If the archive is to be modified,
+ * the mode should be "r+", if not, it should be "r".
+ *
+ * Results:
+ * An FILE *, opened for reading and writing, positioned at the
+ * start of the member's struct ar_hdr, or NULL if the member was
+ * nonexistent. The current struct ar_hdr for member.
+ *
+ * Side Effects:
+ * The passed struct ar_hdr structure is filled in.
+ *
+ *-----------------------------------------------------------------------
+ */
+static FILE *
+ArchFindMember (archive, member, arhPtr, mode)
+ char *archive; /* Path to the archive */
+ char *member; /* Name of member. If it is a path, only the
+ * last component is used. */
+ struct ar_hdr *arhPtr; /* Pointer to header structure to be filled in */
+ char *mode; /* The mode for opening the stream */
+{
+ FILE * arch; /* Stream to archive */
+ int size; /* Size of archive member */
+ char *cp; /* Useful character pointer */
+ char magic[SARMAG];
+ int len, tlen;
+
+ arch = fopen (archive, mode);
+ if (arch == (FILE *) NULL) {
+ return ((FILE *) NULL);
+ }
+
+ /*
+ * We use the ARMAG string to make sure this is an archive we
+ * can handle...
+ */
+ if ((fread (magic, SARMAG, 1, arch) != 1) ||
+ (strncmp (magic, ARMAG, SARMAG) != 0)) {
+ fclose (arch);
+ return ((FILE *) NULL);
+ }
+
+ /*
+ * Because of space constraints and similar things, files are archived
+ * using their final path components, not the entire thing, so we need
+ * to point 'member' to the final component, if there is one, to make
+ * the comparisons easier...
+ */
+ cp = strrchr (member, '/');
+ if (cp != (char *) NULL) {
+ member = cp + 1;
+ }
+ len = tlen = strlen (member);
+ if (len > sizeof (arhPtr->ar_name)) {
+ tlen = sizeof (arhPtr->ar_name);
+ }
+
+ while (fread ((char *)arhPtr, sizeof (struct ar_hdr), 1, arch) == 1) {
+ if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof (arhPtr->ar_fmag) ) != 0) {
+ /*
+ * The header is bogus, so the archive is bad
+ * and there's no way we can recover...
+ */
+ fclose (arch);
+ return ((FILE *) NULL);
+ } else if (strncmp (member, arhPtr->ar_name, tlen) == 0) {
+ /*
+ * If the member's name doesn't take up the entire 'name' field,
+ * we have to be careful of matching prefixes. Names are space-
+ * padded to the right, so if the character in 'name' at the end
+ * of the matched string is anything but a space, this isn't the
+ * member we sought.
+ */
+ if (tlen != sizeof(arhPtr->ar_name) && arhPtr->ar_name[tlen] != ' '){
+ goto skip;
+ } else {
+ /*
+ * To make life easier, we reposition the file at the start
+ * of the header we just read before we return the stream.
+ * In a more general situation, it might be better to leave
+ * the file at the actual member, rather than its header, but
+ * not here...
+ */
+ fseek (arch, -sizeof(struct ar_hdr), 1);
+ return (arch);
+ }
+ } else
+#ifdef AR_EFMT1
+ /*
+ * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+ * first <namelen> bytes of the file
+ */
+ if (strncmp(arhPtr->ar_name, AR_EFMT1,
+ sizeof(AR_EFMT1) - 1) == 0 &&
+ isdigit(arhPtr->ar_name[sizeof(AR_EFMT1) - 1])) {
+
+ unsigned int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1)-1]);
+ char ename[MAXPATHLEN];
+
+ if (elen > MAXPATHLEN) {
+ fclose (arch);
+ return NULL;
+ }
+ if (fread (ename, elen, 1, arch) != 1) {
+ fclose (arch);
+ return NULL;
+ }
+ ename[elen] = '\0';
+ if (DEBUG(ARCH) || DEBUG(MAKE)) {
+ printf("ArchFind: Extended format entry for %s\n", ename);
+ }
+ if (strncmp(ename, member, len) == 0) {
+ /* Found as extended name */
+ fseek (arch, -sizeof(struct ar_hdr) - elen, 1);
+ return (arch);
+ }
+ fseek (arch, -elen, 1);
+ goto skip;
+ } else
+#endif
+ {
+skip:
+ /*
+ * This isn't the member we're after, so we need to advance the
+ * stream's pointer to the start of the next header. Files are
+ * padded with newlines to an even-byte boundary, so we need to
+ * extract the size of the file from the 'size' field of the
+ * header and round it up during the seek.
+ */
+ arhPtr->ar_size[sizeof(arhPtr->ar_size)-1] = '\0';
+ size = (int) strtol(arhPtr->ar_size, NULL, 10);
+ fseek (arch, (size + 1) & ~1, 1);
+ }
+ }
+
+ /*
+ * We've looked everywhere, but the member is not to be found. Close the
+ * archive and return NULL -- an error.
+ */
+ fclose (arch);
+ return ((FILE *) NULL);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_Touch --
+ * Touch a member of an archive.
+ *
+ * Results:
+ * The 'time' field of the member's header is updated.
+ *
+ * Side Effects:
+ * The modification time of the entire archive is also changed.
+ * For a library, this could necessitate the re-ranlib'ing of the
+ * whole thing.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Arch_Touch (gn)
+ GNode *gn; /* Node of member to touch */
+{
+ FILE * arch; /* Stream open to archive, positioned properly */
+ struct ar_hdr arh; /* Current header describing member */
+ char *p1, *p2;
+
+ arch = ArchFindMember(Var_Value (ARCHIVE, gn, &p1),
+ Var_Value (TARGET, gn, &p2),
+ &arh, "r+");
+ if (p1)
+ free(p1);
+ if (p2)
+ free(p2);
+ sprintf(arh.ar_date, "%-12ld", (long) now);
+
+ if (arch != (FILE *) NULL) {
+ (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch);
+ fclose (arch);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_TouchLib --
+ * Given a node which represents a library, touch the thing, making
+ * sure that the table of contents also is touched.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Both the modification time of the library and of the RANLIBMAG
+ * member are set to 'now'.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Arch_TouchLib (gn)
+ GNode *gn; /* The node of the library to touch */
+{
+ FILE * arch; /* Stream open to archive */
+ struct ar_hdr arh; /* Header describing table of contents */
+ struct timeval times[2]; /* Times for utimes() call */
+
+ arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+");
+ sprintf(arh.ar_date, "%-12ld", (long) now);
+
+ if (arch != (FILE *) NULL) {
+ (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch);
+ fclose (arch);
+
+ times[0].tv_sec = times[1].tv_sec = now;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(gn->path, times);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_MTime --
+ * Return the modification time of a member of an archive.
+ *
+ * Results:
+ * The modification time (seconds).
+ *
+ * Side Effects:
+ * The mtime field of the given node is filled in with the value
+ * returned by the function.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Arch_MTime (gn)
+ GNode *gn; /* Node describing archive member */
+{
+ struct ar_hdr *arhPtr; /* Header of desired member */
+ int modTime; /* Modification time as an integer */
+ char *p1, *p2;
+
+ arhPtr = ArchStatMember (Var_Value (ARCHIVE, gn, &p1),
+ Var_Value (TARGET, gn, &p2),
+ TRUE);
+ if (p1)
+ free(p1);
+ if (p2)
+ free(p2);
+
+ if (arhPtr != (struct ar_hdr *) NULL) {
+ modTime = (int) strtol(arhPtr->ar_date, NULL, 10);
+ } else {
+ modTime = 0;
+ }
+
+ gn->mtime = modTime;
+ return (modTime);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_MemMTime --
+ * Given a non-existent archive member's node, get its modification
+ * time from its archived form, if it exists.
+ *
+ * Results:
+ * The modification time.
+ *
+ * Side Effects:
+ * The mtime field is filled in.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Arch_MemMTime (gn)
+ GNode *gn;
+{
+ LstNode ln;
+ GNode *pgn;
+ char *nameStart,
+ *nameEnd;
+
+ if (Lst_Open (gn->parents) != SUCCESS) {
+ gn->mtime = 0;
+ return (0);
+ }
+ while ((ln = Lst_Next (gn->parents)) != NILLNODE) {
+ pgn = (GNode *) Lst_Datum (ln);
+
+ if (pgn->type & OP_ARCHV) {
+ /*
+ * If the parent is an archive specification and is being made
+ * and its member's name matches the name of the node we were
+ * given, record the modification time of the parent in the
+ * child. We keep searching its parents in case some other
+ * parent requires this child to exist...
+ */
+ nameStart = strchr (pgn->name, '(') + 1;
+ nameEnd = strchr (nameStart, ')');
+
+ if (pgn->make &&
+ strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) {
+ gn->mtime = Arch_MTime(pgn);
+ }
+ } else if (pgn->make) {
+ /*
+ * Something which isn't a library depends on the existence of
+ * this target, so it needs to exist.
+ */
+ gn->mtime = 0;
+ break;
+ }
+ }
+
+ Lst_Close (gn->parents);
+
+ return (gn->mtime);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_FindLib --
+ * Search for a library along the given search path.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The node's 'path' field is set to the found path (including the
+ * actual file name, not -l...). If the system can handle the -L
+ * flag when linking (or we cannot find the library), we assume that
+ * the user has placed the .LIBRARIES variable in the final linking
+ * command (or the linker will know where to find it) and set the
+ * TARGET variable for this node to be the node's name. Otherwise,
+ * we set the TARGET variable to be the full path of the library,
+ * as returned by Dir_FindFile.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Arch_FindLib (gn, path)
+ GNode *gn; /* Node of library to find */
+ Lst path; /* Search path */
+{
+ char *libName; /* file name for archive */
+
+ libName = (char *)emalloc (strlen (gn->name) + 6 - 2);
+ sprintf(libName, "lib%s.a", &gn->name[2]);
+
+ gn->path = Dir_FindFile (libName, path);
+
+ free (libName);
+
+#ifdef LIBRARIES
+ Var_Set (TARGET, gn->name, gn);
+#else
+ Var_Set (TARGET, gn->path == (char *) NULL ? gn->name : gn->path, gn);
+#endif LIBRARIES
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_LibOODate --
+ * Decide if a node with the OP_LIB attribute is out-of-date. Called
+ * from Make_OODate to make its life easier.
+ *
+ * There are several ways for a library to be out-of-date that are
+ * not available to ordinary files. In addition, there are ways
+ * that are open to regular files that are not available to
+ * libraries. A library that is only used as a source is never
+ * considered out-of-date by itself. This does not preclude the
+ * library's modification time from making its parent be out-of-date.
+ * A library will be considered out-of-date for any of these reasons,
+ * given that it is a target on a dependency line somewhere:
+ * Its modification time is less than that of one of its
+ * sources (gn->mtime < gn->cmtime).
+ * Its modification time is greater than the time at which the
+ * make began (i.e. it's been modified in the course
+ * of the make, probably by archiving).
+ * The modification time of one of its sources is greater than
+ * the one of its RANLIBMAG member (i.e. its table of contents
+ * is out-of-date). We don't compare of the archive time
+ * vs. TOC time because they can be too close. In my
+ * opinion we should not bother with the TOC at all since
+ * this is used by 'ar' rules that affect the data contents
+ * of the archive, not by ranlib rules, which affect the
+ * TOC.
+ *
+ * Results:
+ * TRUE if the library is out-of-date. FALSE otherwise.
+ *
+ * Side Effects:
+ * The library will be hashed if it hasn't been already.
+ *
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Arch_LibOODate (gn)
+ GNode *gn; /* The library's graph node */
+{
+ Boolean oodate;
+
+ if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) {
+ oodate = FALSE;
+ } else if ((gn->mtime > now) || (gn->mtime < gn->cmtime)) {
+ oodate = TRUE;
+ } else {
+ struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
+ int modTimeTOC; /* The table-of-contents's mod time */
+
+ arhPtr = ArchStatMember (gn->path, RANLIBMAG, FALSE);
+
+ if (arhPtr != (struct ar_hdr *)NULL) {
+ modTimeTOC = (int) strtol(arhPtr->ar_date, NULL, 10);
+
+ if (DEBUG(ARCH) || DEBUG(MAKE)) {
+ printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
+ }
+ oodate = (gn->cmtime > modTimeTOC);
+ } else {
+ /*
+ * A library w/o a table of contents is out-of-date
+ */
+ if (DEBUG(ARCH) || DEBUG(MAKE)) {
+ printf("No t.o.c....");
+ }
+ oodate = TRUE;
+ }
+ }
+ return (oodate);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_Init --
+ * Initialize things for this module.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The 'archives' list is initialized.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Arch_Init ()
+{
+ archives = Lst_Init (FALSE);
+}
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Arch_End --
+ * Cleanup things for this module.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The 'archives' list is freed
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Arch_End ()
+{
+ Lst_Destroy(archives, ArchFree);
+}
diff --git a/usr.bin/make/bit.h b/usr.bin/make/bit.h
new file mode 100644
index 0000000..1671326
--- /dev/null
+++ b/usr.bin/make/bit.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bit.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * bit.h --
+ *
+ * Definition of macros for setting and clearing bits in an array
+ * of integers.
+ *
+ * It is assumed that "int" is 32 bits wide.
+ */
+
+#ifndef _BIT
+#define _BIT
+
+#include "sprite.h"
+
+#define BIT_NUM_BITS_PER_INT 32
+#define BIT_NUM_BITS_PER_BYTE 8
+
+#define Bit_NumInts(numBits) \
+ (((numBits)+BIT_NUM_BITS_PER_INT -1)/BIT_NUM_BITS_PER_INT)
+
+#define Bit_NumBytes(numBits) \
+ (Bit_NumInts(numBits) * sizeof(int))
+
+#define Bit_Alloc(numBits, bitArrayPtr) \
+ bitArrayPtr = (int *)malloc((unsigned)Bit_NumBytes(numBits)); \
+ Bit_Zero((numBits), (bitArrayPtr))
+
+#define Bit_Free(bitArrayPtr) \
+ free((char *)bitArrayPtr)
+
+#define Bit_Set(numBits, bitArrayPtr) \
+ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] |= \
+ (1 << ((numBits) % BIT_NUM_BITS_PER_INT)))
+
+#define Bit_IsSet(numBits, bitArrayPtr) \
+ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] & \
+ (1 << ((numBits) % BIT_NUM_BITS_PER_INT)))
+
+#define Bit_Clear(numBits, bitArrayPtr) \
+ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] &= \
+ ~(1 << ((numBits) % BIT_NUM_BITS_PER_INT)))
+
+#define Bit_IsClear(numBits, bitArrayPtr) \
+ (!(Bit_IsSet((numBits), (bitArrayPtr))))
+
+#define Bit_Copy(numBits, srcArrayPtr, destArrayPtr) \
+ bcopy((char *)(srcArrayPtr), (char *)(destArrayPtr), \
+ Bit_NumBytes(numBits))
+
+#define Bit_Zero(numBits, bitArrayPtr) \
+ bzero((char *)(bitArrayPtr), Bit_NumBytes(numBits))
+
+extern int Bit_FindFirstSet();
+extern int Bit_FindFirstClear();
+extern Boolean Bit_Intersect();
+extern Boolean Bit_Union();
+extern Boolean Bit_AnySet();
+extern int *Bit_Expand();
+
+#endif /* _BIT */
diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c
new file mode 100644
index 0000000..a4ecaf6
--- /dev/null
+++ b/usr.bin/make/buf.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * buf.c --
+ * Functions for automatically-expanded buffers.
+ */
+
+#include "sprite.h"
+#include "make.h"
+#include "buf.h"
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/*
+ * BufExpand --
+ * Expand the given buffer to hold the given number of additional
+ * bytes.
+ * Makes sure there's room for an extra NULL byte at the end of the
+ * buffer in case it holds a string.
+ */
+#define BufExpand(bp,nb) \
+ if (bp->left < (nb)+1) {\
+ int newSize = (bp)->size + max((nb)+1,BUF_ADD_INC); \
+ Byte *newBuf = (Byte *) realloc((bp)->buffer, newSize); \
+ \
+ (bp)->inPtr = newBuf + ((bp)->inPtr - (bp)->buffer); \
+ (bp)->outPtr = newBuf + ((bp)->outPtr - (bp)->buffer);\
+ (bp)->buffer = newBuf;\
+ (bp)->size = newSize;\
+ (bp)->left = newSize - ((bp)->inPtr - (bp)->buffer);\
+ }
+
+#define BUF_DEF_SIZE 256 /* Default buffer size */
+#define BUF_ADD_INC 256 /* Expansion increment when Adding */
+#define BUF_UNGET_INC 16 /* Expansion increment when Ungetting */
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_OvAddByte --
+ * Add a single byte to the buffer. left is zero or negative.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The buffer may be expanded.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_OvAddByte (bp, byte)
+ register Buffer bp;
+ int byte;
+{
+ int nbytes = 1;
+ bp->left = 0;
+ BufExpand (bp, nbytes);
+
+ *bp->inPtr++ = byte;
+ bp->left--;
+
+ /*
+ * Null-terminate
+ */
+ *bp->inPtr = 0;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_AddBytes --
+ * Add a number of bytes to the buffer.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Guess what?
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_AddBytes (bp, numBytes, bytesPtr)
+ register Buffer bp;
+ int numBytes;
+ Byte *bytesPtr;
+{
+
+ BufExpand (bp, numBytes);
+
+ memcpy (bp->inPtr, bytesPtr, numBytes);
+ bp->inPtr += numBytes;
+ bp->left -= numBytes;
+
+ /*
+ * Null-terminate
+ */
+ *bp->inPtr = 0;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_UngetByte --
+ * Place the byte back at the beginning of the buffer.
+ *
+ * Results:
+ * SUCCESS if the byte was added ok. FAILURE if not.
+ *
+ * Side Effects:
+ * The byte is stuffed in the buffer and outPtr is decremented.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_UngetByte (bp, byte)
+ register Buffer bp;
+ int byte;
+{
+
+ if (bp->outPtr != bp->buffer) {
+ bp->outPtr--;
+ *bp->outPtr = byte;
+ } else if (bp->outPtr == bp->inPtr) {
+ *bp->inPtr = byte;
+ bp->inPtr++;
+ bp->left--;
+ *bp->inPtr = 0;
+ } else {
+ /*
+ * Yech. have to expand the buffer to stuff this thing in.
+ * We use a different expansion constant because people don't
+ * usually push back many bytes when they're doing it a byte at
+ * a time...
+ */
+ int numBytes = bp->inPtr - bp->outPtr;
+ Byte *newBuf;
+
+ newBuf = (Byte *)emalloc(bp->size + BUF_UNGET_INC);
+ memcpy ((char *)(newBuf+BUF_UNGET_INC), (char *)bp->outPtr, numBytes+1);
+ bp->outPtr = newBuf + BUF_UNGET_INC;
+ bp->inPtr = bp->outPtr + numBytes;
+ free ((char *)bp->buffer);
+ bp->buffer = newBuf;
+ bp->size += BUF_UNGET_INC;
+ bp->left = bp->size - (bp->inPtr - bp->buffer);
+ bp->outPtr -= 1;
+ *bp->outPtr = byte;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_UngetBytes --
+ * Push back a series of bytes at the beginning of the buffer.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * outPtr is decremented and the bytes copied into the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_UngetBytes (bp, numBytes, bytesPtr)
+ register Buffer bp;
+ int numBytes;
+ Byte *bytesPtr;
+{
+
+ if (bp->outPtr - bp->buffer >= numBytes) {
+ bp->outPtr -= numBytes;
+ memcpy (bp->outPtr, bytesPtr, numBytes);
+ } else if (bp->outPtr == bp->inPtr) {
+ Buf_AddBytes (bp, numBytes, bytesPtr);
+ } else {
+ int curNumBytes = bp->inPtr - bp->outPtr;
+ Byte *newBuf;
+ int newBytes = max(numBytes,BUF_UNGET_INC);
+
+ newBuf = (Byte *)emalloc (bp->size + newBytes);
+ memcpy((char *)(newBuf+newBytes), (char *)bp->outPtr, curNumBytes+1);
+ bp->outPtr = newBuf + newBytes;
+ bp->inPtr = bp->outPtr + curNumBytes;
+ free ((char *)bp->buffer);
+ bp->buffer = newBuf;
+ bp->size += newBytes;
+ bp->left = bp->size - (bp->inPtr - bp->buffer);
+ bp->outPtr -= numBytes;
+ memcpy ((char *)bp->outPtr, (char *)bytesPtr, numBytes);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_GetByte --
+ * Return the next byte from the buffer. Actually returns an integer.
+ *
+ * Results:
+ * Returns BUF_ERROR if there's no byte in the buffer, or the byte
+ * itself if there is one.
+ *
+ * Side Effects:
+ * outPtr is incremented and both outPtr and inPtr will be reset if
+ * the buffer is emptied.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Buf_GetByte (bp)
+ register Buffer bp;
+{
+ int res;
+
+ if (bp->inPtr == bp->outPtr) {
+ return (BUF_ERROR);
+ } else {
+ res = (int) *bp->outPtr;
+ bp->outPtr += 1;
+ if (bp->outPtr == bp->inPtr) {
+ bp->outPtr = bp->inPtr = bp->buffer;
+ bp->left = bp->size;
+ *bp->inPtr = 0;
+ }
+ return (res);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_GetBytes --
+ * Extract a number of bytes from the buffer.
+ *
+ * Results:
+ * The number of bytes gotten.
+ *
+ * Side Effects:
+ * The passed array is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Buf_GetBytes (bp, numBytes, bytesPtr)
+ register Buffer bp;
+ int numBytes;
+ Byte *bytesPtr;
+{
+
+ if (bp->inPtr - bp->outPtr < numBytes) {
+ numBytes = bp->inPtr - bp->outPtr;
+ }
+ memcpy (bytesPtr, bp->outPtr, numBytes);
+ bp->outPtr += numBytes;
+
+ if (bp->outPtr == bp->inPtr) {
+ bp->outPtr = bp->inPtr = bp->buffer;
+ bp->left = bp->size;
+ *bp->inPtr = 0;
+ }
+ return (numBytes);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_GetAll --
+ * Get all the available data at once.
+ *
+ * Results:
+ * A pointer to the data and the number of bytes available.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+Byte *
+Buf_GetAll (bp, numBytesPtr)
+ register Buffer bp;
+ int *numBytesPtr;
+{
+
+ if (numBytesPtr != (int *)NULL) {
+ *numBytesPtr = bp->inPtr - bp->outPtr;
+ }
+
+ return (bp->outPtr);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_Discard --
+ * Throw away bytes in a buffer.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The bytes are discarded.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_Discard (bp, numBytes)
+ register Buffer bp;
+ int numBytes;
+{
+
+ if (bp->inPtr - bp->outPtr <= numBytes) {
+ bp->inPtr = bp->outPtr = bp->buffer;
+ bp->left = bp->size;
+ *bp->inPtr = 0;
+ } else {
+ bp->outPtr += numBytes;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_Size --
+ * Returns the number of bytes in the given buffer. Doesn't include
+ * the null-terminating byte.
+ *
+ * Results:
+ * The number of bytes.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Buf_Size (buf)
+ Buffer buf;
+{
+ return (buf->inPtr - buf->outPtr);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_Init --
+ * Initialize a buffer. If no initial size is given, a reasonable
+ * default is used.
+ *
+ * Results:
+ * A buffer to be given to other functions in this library.
+ *
+ * Side Effects:
+ * The buffer is created, the space allocated and pointers
+ * initialized.
+ *
+ *-----------------------------------------------------------------------
+ */
+Buffer
+Buf_Init (size)
+ int size; /* Initial size for the buffer */
+{
+ Buffer bp; /* New Buffer */
+
+ bp = (Buffer)emalloc(sizeof(*bp));
+
+ if (size <= 0) {
+ size = BUF_DEF_SIZE;
+ }
+ bp->left = bp->size = size;
+ bp->buffer = (Byte *)emalloc(size);
+ bp->inPtr = bp->outPtr = bp->buffer;
+ *bp->inPtr = 0;
+
+ return (bp);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Buf_Destroy --
+ * Nuke a buffer and all its resources.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The buffer is freed.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Buf_Destroy (buf, freeData)
+ Buffer buf; /* Buffer to destroy */
+ Boolean freeData; /* TRUE if the data should be destroyed as well */
+{
+
+ if (freeData) {
+ free ((char *)buf->buffer);
+ }
+ free ((char *)buf);
+}
diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h
new file mode 100644
index 0000000..3eb9b68
--- /dev/null
+++ b/usr.bin/make/buf.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)buf.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*-
+ * buf.h --
+ * Header for users of the buf library.
+ */
+
+#ifndef _BUF_H
+#define _BUF_H
+
+#include "sprite.h"
+
+typedef char Byte;
+
+typedef struct Buffer {
+ int size; /* Current size of the buffer */
+ int left; /* Space left (== size - (inPtr - buffer)) */
+ Byte *buffer; /* The buffer itself */
+ Byte *inPtr; /* Place to write to */
+ Byte *outPtr; /* Place to read from */
+} *Buffer;
+
+/* Buf_AddByte adds a single byte to a buffer. */
+#define Buf_AddByte(bp, byte) \
+ (void) (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte), 1 : \
+ (*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0), 1)
+
+#define BUF_ERROR 256
+
+void Buf_OvAddByte __P((Buffer, int));
+void Buf_AddBytes __P((Buffer, int, Byte *));
+void Buf_UngetByte __P((Buffer, int));
+void Buf_UngetBytes __P((Buffer, int, Byte *));
+int Buf_GetByte __P((Buffer));
+int Buf_GetBytes __P((Buffer, int, Byte *));
+Byte *Buf_GetAll __P((Buffer, int *));
+void Buf_Discard __P((Buffer, int));
+int Buf_Size __P((Buffer));
+Buffer Buf_Init __P((int));
+void Buf_Destroy __P((Buffer, Boolean));
+
+#endif /* _BUF_H */
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c
new file mode 100644
index 0000000..a9e8970
--- /dev/null
+++ b/usr.bin/make/compat.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * compat.c --
+ * The routines in this file implement the full-compatibility
+ * mode of PMake. Most of the special functionality of PMake
+ * is available in this mode. Things not supported:
+ * - different shells.
+ * - friendly variable substitution.
+ *
+ * Interface:
+ * Compat_Run Initialize things for this module and recreate
+ * thems as need creatin'
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
+extern int errno;
+
+/*
+ * The following array is used to make a fast determination of which
+ * characters are interpreted specially by the shell. If a command
+ * contains any of these characters, it is executed by the shell, not
+ * directly by us.
+ */
+
+static char meta[256];
+
+static GNode *curTarg = NILGNODE;
+static GNode *ENDNode;
+static void CompatInterrupt __P((int));
+static int CompatRunCommand __P((ClientData, ClientData));
+static int CompatMake __P((ClientData, ClientData));
+
+/*-
+ *-----------------------------------------------------------------------
+ * CompatInterrupt --
+ * Interrupt the creation of the current target and remove it if
+ * it ain't precious.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The target is removed and the process exits. If .INTERRUPT exists,
+ * its commands are run first WITH INTERRUPTS IGNORED..
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+CompatInterrupt (signo)
+ int signo;
+{
+ GNode *gn;
+
+ if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) {
+ char *p1;
+ char *file = Var_Value (TARGET, curTarg, &p1);
+ struct stat st;
+
+ if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) &&
+ unlink(file) != -1) {
+ printf ("*** %s removed\n", file);
+ }
+ if (p1)
+ free(p1);
+
+ /*
+ * Run .INTERRUPT only if hit with interrupt signal
+ */
+ if (signo == SIGINT) {
+ gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
+ if (gn != NILGNODE) {
+ Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
+ }
+ }
+
+ }
+ exit (signo);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CompatRunCommand --
+ * Execute the next command for a target. If the command returns an
+ * error, the node's made field is set to ERROR and creation stops.
+ *
+ * Results:
+ * 0 if the command succeeded, 1 if an error occurred.
+ *
+ * Side Effects:
+ * The node's 'made' field may be set to ERROR.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+CompatRunCommand (cmdp, gnp)
+ ClientData cmdp; /* Command to execute */
+ ClientData gnp; /* Node from which the command came */
+{
+ char *cmdStart; /* Start of expanded command */
+ register char *cp;
+ Boolean silent, /* Don't print command */
+ errCheck; /* Check errors */
+ union wait reason; /* Reason for child's death */
+ int status; /* Description of child's death */
+ int cpid; /* Child actually found */
+ ReturnStatus stat; /* Status of fork */
+ LstNode cmdNode; /* Node where current command is located */
+ char **av; /* Argument vector for thing to exec */
+ int argc; /* Number of arguments in av or 0 if not
+ * dynamically allocated */
+ Boolean local; /* TRUE if command should be executed
+ * locally */
+ char *cmd = (char *) cmdp;
+ GNode *gn = (GNode *) gnp;
+
+ /*
+ * Avoid clobbered variable warnings by forcing the compiler
+ * to ``unregister'' variables
+ */
+#if __GNUC__
+ (void) &av;
+ (void) &errCheck;
+#endif
+ silent = gn->type & OP_SILENT;
+ errCheck = !(gn->type & OP_IGNORE);
+
+ cmdNode = Lst_Member (gn->commands, (ClientData)cmd);
+ cmdStart = Var_Subst (NULL, cmd, gn, FALSE);
+
+ /*
+ * brk_string will return an argv with a NULL in av[1], thus causing
+ * execvp to choke and die horribly. Besides, how can we execute a null
+ * command? In any case, we warn the user that the command expanded to
+ * nothing (is this the right thing to do?).
+ */
+
+ if (*cmdStart == '\0') {
+ free(cmdStart);
+ Error("%s expands to empty string", cmd);
+ return(0);
+ } else {
+ cmd = cmdStart;
+ }
+ Lst_Replace (cmdNode, (ClientData)cmdStart);
+
+ if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
+ (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart);
+ return(0);
+ } else if (strcmp(cmdStart, "...") == 0) {
+ gn->type |= OP_SAVE_CMDS;
+ return(0);
+ }
+
+ while ((*cmd == '@') || (*cmd == '-')) {
+ if (*cmd == '@') {
+ silent = TRUE;
+ } else {
+ errCheck = FALSE;
+ }
+ cmd++;
+ }
+
+ while (isspace((unsigned char)*cmd))
+ cmd++;
+
+ /*
+ * Search for meta characters in the command. If there are no meta
+ * characters, there's no need to execute a shell to execute the
+ * command.
+ */
+ for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {
+ continue;
+ }
+
+ /*
+ * Print the command before echoing if we're not supposed to be quiet for
+ * this one. We also print the command if -n given.
+ */
+ if (!silent || noExecute) {
+ printf ("%s\n", cmd);
+ fflush(stdout);
+ }
+
+ /*
+ * If we're not supposed to execute any commands, this is as far as
+ * we go...
+ */
+ if (noExecute) {
+ return (0);
+ }
+
+ if (*cp != '\0') {
+ /*
+ * If *cp isn't the null character, we hit a "meta" character and
+ * need to pass the command off to the shell. We give the shell the
+ * -e flag as well as -c if it's supposed to exit when it hits an
+ * error.
+ */
+ static char *shargv[4] = { "/bin/sh" };
+
+ shargv[1] = (errCheck ? "-ec" : "-c");
+ shargv[2] = cmd;
+ shargv[3] = (char *)NULL;
+ av = shargv;
+ argc = 0;
+ } else {
+ /*
+ * No meta-characters, so no need to exec a shell. Break the command
+ * into words to form an argument vector we can execute.
+ * brk_string sticks our name in av[0], so we have to
+ * skip over it...
+ */
+ av = brk_string(cmd, &argc, TRUE);
+ av += 1;
+ }
+
+ local = TRUE;
+
+ /*
+ * Fork and execute the single command. If the fork fails, we abort.
+ */
+ cpid = vfork();
+ if (cpid < 0) {
+ Fatal("Could not fork");
+ }
+ if (cpid == 0) {
+ if (local) {
+ execvp(av[0], av);
+ (void) write (2, av[0], strlen (av[0]));
+ (void) write (2, ": not found\n", sizeof(": not found"));
+ } else {
+ (void)execv(av[0], av);
+ }
+ exit(1);
+ }
+ free(cmdStart);
+ Lst_Replace (cmdNode, (ClientData) NULL);
+
+ /*
+ * The child is off and running. Now all we can do is wait...
+ */
+ while (1) {
+
+ while ((stat = wait((int *)&reason)) != cpid) {
+ if (stat == -1 && errno != EINTR) {
+ break;
+ }
+ }
+
+ if (stat > -1) {
+ if (WIFSTOPPED(reason)) {
+ status = reason.w_stopval; /* stopped */
+ } else if (WIFEXITED(reason)) {
+ status = reason.w_retcode; /* exited */
+ if (status != 0) {
+ printf ("*** Error code %d", status);
+ }
+ } else {
+ status = reason.w_termsig; /* signaled */
+ printf ("*** Signal %d", status);
+ }
+
+
+ if (!WIFEXITED(reason) || (status != 0)) {
+ if (errCheck) {
+ gn->made = ERROR;
+ if (keepgoing) {
+ /*
+ * Abort the current target, but let others
+ * continue.
+ */
+ printf (" (continuing)\n");
+ }
+ } else {
+ /*
+ * Continue executing commands for this target.
+ * If we return 0, this will happen...
+ */
+ printf (" (ignored)\n");
+ status = 0;
+ }
+ }
+ break;
+ } else {
+ Fatal ("error in wait: %d", stat);
+ /*NOTREACHED*/
+ }
+ }
+
+ return (status);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CompatMake --
+ * Make a target.
+ *
+ * Results:
+ * 0
+ *
+ * Side Effects:
+ * If an error is detected and not being ignored, the process exits.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+CompatMake (gnp, pgnp)
+ ClientData gnp; /* The node to make */
+ ClientData pgnp; /* Parent to abort if necessary */
+{
+ GNode *gn = (GNode *) gnp;
+ GNode *pgn = (GNode *) pgnp;
+ if (gn->type & OP_USE) {
+ Make_HandleUse(gn, pgn);
+ } else if (gn->made == UNMADE) {
+ /*
+ * First mark ourselves to be made, then apply whatever transformations
+ * the suffix module thinks are necessary. Once that's done, we can
+ * descend and make all our children. If any of them has an error
+ * but the -k flag was given, our 'make' field will be set FALSE again.
+ * This is our signal to not attempt to do anything but abort our
+ * parent as well.
+ */
+ gn->make = TRUE;
+ gn->made = BEINGMADE;
+ Suff_FindDeps (gn);
+ Lst_ForEach (gn->children, CompatMake, (ClientData)gn);
+ if (!gn->make) {
+ gn->made = ABORTED;
+ pgn->make = FALSE;
+ return (0);
+ }
+
+ if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
+ char *p1;
+ Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
+ if (p1)
+ free(p1);
+ }
+
+ /*
+ * All the children were made ok. Now cmtime contains the modification
+ * time of the newest child, we need to find out if we exist and when
+ * we were modified last. The criteria for datedness are defined by the
+ * Make_OODate function.
+ */
+ if (DEBUG(MAKE)) {
+ printf("Examining %s...", gn->name);
+ }
+ if (! Make_OODate(gn)) {
+ gn->made = UPTODATE;
+ if (DEBUG(MAKE)) {
+ printf("up-to-date.\n");
+ }
+ return (0);
+ } else if (DEBUG(MAKE)) {
+ printf("out-of-date.\n");
+ }
+
+ /*
+ * If the user is just seeing if something is out-of-date, exit now
+ * to tell him/her "yes".
+ */
+ if (queryFlag) {
+ exit (-1);
+ }
+
+ /*
+ * We need to be re-made. We also have to make sure we've got a $?
+ * variable. To be nice, we also define the $> variable using
+ * Make_DoAllVar().
+ */
+ Make_DoAllVar(gn);
+
+ /*
+ * Alter our type to tell if errors should be ignored or things
+ * should not be printed so CompatRunCommand knows what to do.
+ */
+ if (Targ_Ignore (gn)) {
+ gn->type |= OP_IGNORE;
+ }
+ if (Targ_Silent (gn)) {
+ gn->type |= OP_SILENT;
+ }
+
+ if (Job_CheckCommands (gn, Fatal)) {
+ /*
+ * Our commands are ok, but we still have to worry about the -t
+ * flag...
+ */
+ if (!touchFlag) {
+ curTarg = gn;
+ Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn);
+ curTarg = NILGNODE;
+ } else {
+ Job_Touch (gn, gn->type & OP_SILENT);
+ }
+ } else {
+ gn->made = ERROR;
+ }
+
+ if (gn->made != ERROR) {
+ /*
+ * If the node was made successfully, mark it so, update
+ * its modification time and timestamp all its parents. Note
+ * that for .ZEROTIME targets, the timestamping isn't done.
+ * This is to keep its state from affecting that of its parent.
+ */
+ gn->made = MADE;
+#ifndef RECHECK
+ /*
+ * We can't re-stat the thing, but we can at least take care of
+ * rules where a target depends on a source that actually creates
+ * the target, but only if it has changed, e.g.
+ *
+ * parse.h : parse.o
+ *
+ * parse.o : parse.y
+ * yacc -d parse.y
+ * cc -c y.tab.c
+ * mv y.tab.o parse.o
+ * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
+ *
+ * In this case, if the definitions produced by yacc haven't
+ * changed from before, parse.h won't have been updated and
+ * gn->mtime will reflect the current modification time for
+ * parse.h. This is something of a kludge, I admit, but it's a
+ * useful one..
+ *
+ * XXX: People like to use a rule like
+ *
+ * FRC:
+ *
+ * To force things that depend on FRC to be made, so we have to
+ * check for gn->children being empty as well...
+ */
+ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
+ gn->mtime = now;
+ }
+#else
+ /*
+ * This is what Make does and it's actually a good thing, as it
+ * allows rules like
+ *
+ * cmp -s y.tab.h parse.h || cp y.tab.h parse.h
+ *
+ * to function as intended. Unfortunately, thanks to the stateless
+ * nature of NFS (and the speed of this program), there are times
+ * when the modification time of a file created on a remote
+ * machine will not be modified before the stat() implied by
+ * the Dir_MTime occurs, thus leading us to believe that the file
+ * is unchanged, wreaking havoc with files that depend on this one.
+ *
+ * I have decided it is better to make too much than to make too
+ * little, so this stuff is commented out unless you're sure it's
+ * ok.
+ * -- ardeb 1/12/88
+ */
+ if (noExecute || Dir_MTime(gn) == 0) {
+ gn->mtime = now;
+ }
+ if (gn->cmtime > gn->mtime)
+ gn->mtime = gn->cmtime;
+ if (DEBUG(MAKE)) {
+ printf("update time: %s\n", Targ_FmtTime(gn->mtime));
+ }
+#endif
+ if (!(gn->type & OP_EXEC)) {
+ pgn->childMade = TRUE;
+ Make_TimeStamp(pgn, gn);
+ }
+ } else if (keepgoing) {
+ pgn->make = FALSE;
+ } else {
+ printf ("\n\nStop.\n");
+ exit (1);
+ }
+ } else if (gn->made == ERROR) {
+ /*
+ * Already had an error when making this beastie. Tell the parent
+ * to abort.
+ */
+ pgn->make = FALSE;
+ } else {
+ if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
+ char *p1;
+ Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
+ if (p1)
+ free(p1);
+ }
+ switch(gn->made) {
+ case BEINGMADE:
+ Error("Graph cycles through %s\n", gn->name);
+ gn->made = ERROR;
+ pgn->make = FALSE;
+ break;
+ case MADE:
+ if ((gn->type & OP_EXEC) == 0) {
+ pgn->childMade = TRUE;
+ Make_TimeStamp(pgn, gn);
+ }
+ break;
+ case UPTODATE:
+ if ((gn->type & OP_EXEC) == 0) {
+ Make_TimeStamp(pgn, gn);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Compat_Run --
+ * Initialize this mode and start making.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Guess what?
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Compat_Run(targs)
+ Lst targs; /* List of target nodes to re-create */
+{
+ char *cp; /* Pointer to string of shell meta-characters */
+ GNode *gn = NULL;/* Current root target */
+ int errors; /* Number of targets not remade due to errors */
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
+ signal(SIGINT, CompatInterrupt);
+ }
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+ signal(SIGTERM, CompatInterrupt);
+ }
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
+ signal(SIGHUP, CompatInterrupt);
+ }
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
+ signal(SIGQUIT, CompatInterrupt);
+ }
+
+ for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
+ meta[(unsigned char) *cp] = 1;
+ }
+ /*
+ * The null character serves as a sentinel in the string.
+ */
+ meta[0] = 1;
+
+ ENDNode = Targ_FindNode(".END", TARG_CREATE);
+ /*
+ * If the user has defined a .BEGIN target, execute the commands attached
+ * to it.
+ */
+ if (!queryFlag) {
+ gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
+ if (gn != NILGNODE) {
+ Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
+ }
+ }
+
+ /*
+ * For each entry in the list of targets to create, call CompatMake on
+ * it to create the thing. CompatMake will leave the 'made' field of gn
+ * in one of several states:
+ * UPTODATE gn was already up-to-date
+ * MADE gn was recreated successfully
+ * ERROR An error occurred while gn was being created
+ * ABORTED gn was not remade because one of its inferiors
+ * could not be made due to errors.
+ */
+ errors = 0;
+ while (!Lst_IsEmpty (targs)) {
+ gn = (GNode *) Lst_DeQueue (targs);
+ CompatMake (gn, gn);
+
+ if (gn->made == UPTODATE) {
+ printf ("`%s' is up to date.\n", gn->name);
+ } else if (gn->made == ABORTED) {
+ printf ("`%s' not remade because of errors.\n", gn->name);
+ errors += 1;
+ }
+ }
+
+ /*
+ * If the user has defined a .END target, run its commands.
+ */
+ if (errors == 0) {
+ Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn);
+ }
+}
diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c
new file mode 100644
index 0000000..4bf802b
--- /dev/null
+++ b/usr.bin/make/cond.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+/*-
+ * cond.c --
+ * Functions to handle conditionals in a makefile.
+ *
+ * Interface:
+ * Cond_Eval Evaluate the conditional in the passed line.
+ *
+ */
+
+#include <ctype.h>
+#include <math.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "buf.h"
+
+/*
+ * The parsing of conditional expressions is based on this grammar:
+ * E -> F || E
+ * E -> F
+ * F -> T && F
+ * F -> T
+ * T -> defined(variable)
+ * T -> make(target)
+ * T -> exists(file)
+ * T -> empty(varspec)
+ * T -> target(name)
+ * T -> symbol
+ * T -> $(varspec) op value
+ * T -> $(varspec) == "string"
+ * T -> $(varspec) != "string"
+ * T -> ( E )
+ * T -> ! T
+ * op -> == | != | > | < | >= | <=
+ *
+ * 'symbol' is some other symbol to which the default function (condDefProc)
+ * is applied.
+ *
+ * Tokens are scanned from the 'condExpr' string. The scanner (CondToken)
+ * will return And for '&' and '&&', Or for '|' and '||', Not for '!',
+ * LParen for '(', RParen for ')' and will evaluate the other terminal
+ * symbols, using either the default function or the function given in the
+ * terminal, and return the result as either True or False.
+ *
+ * All Non-Terminal functions (CondE, CondF and CondT) return Err on error.
+ */
+typedef enum {
+ And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err
+} Token;
+
+/*-
+ * Structures to handle elegantly the different forms of #if's. The
+ * last two fields are stored in condInvert and condDefProc, respectively.
+ */
+static int CondGetArg __P((char **, char **, char *, Boolean));
+static Boolean CondDoDefined __P((int, char *));
+static int CondStrMatch __P((ClientData, ClientData));
+static Boolean CondDoMake __P((int, char *));
+static Boolean CondDoExists __P((int, char *));
+static Boolean CondDoTarget __P((int, char *));
+static Boolean CondCvtArg __P((char *, double *));
+static Token CondToken __P((Boolean));
+static Token CondT __P((Boolean));
+static Token CondF __P((Boolean));
+static Token CondE __P((Boolean));
+
+static struct If {
+ char *form; /* Form of if */
+ int formlen; /* Length of form */
+ Boolean doNot; /* TRUE if default function should be negated */
+ Boolean (*defProc)(); /* Default function to apply */
+} ifs[] = {
+ { "ifdef", 5, FALSE, CondDoDefined },
+ { "ifndef", 6, TRUE, CondDoDefined },
+ { "ifmake", 6, FALSE, CondDoMake },
+ { "ifnmake", 7, TRUE, CondDoMake },
+ { "if", 2, FALSE, CondDoDefined },
+ { (char *)0, 0, FALSE, (Boolean (*)())0 }
+};
+
+static Boolean condInvert; /* Invert the default function */
+static Boolean (*condDefProc)(); /* Default function to apply */
+static char *condExpr; /* The expression to parse */
+static Token condPushBack=None; /* Single push-back token used in
+ * parsing */
+
+#define MAXIF 30 /* greatest depth of #if'ing */
+
+static Boolean condStack[MAXIF]; /* Stack of conditionals's values */
+static int condTop = MAXIF; /* Top-most conditional */
+static int skipIfLevel=0; /* Depth of skipped conditionals */
+static Boolean skipLine = FALSE; /* Whether the parse module is skipping
+ * lines */
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondPushBack --
+ * Push back the most recent token read. We only need one level of
+ * this, so the thing is just stored in 'condPushback'.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * condPushback is overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+CondPushBack (t)
+ Token t; /* Token to push back into the "stream" */
+{
+ condPushBack = t;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondGetArg --
+ * Find the argument of a built-in function.
+ *
+ * Results:
+ * The length of the argument and the address of the argument.
+ *
+ * Side Effects:
+ * The pointer is set to point to the closing parenthesis of the
+ * function call.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+CondGetArg (linePtr, argPtr, func, parens)
+ char **linePtr;
+ char **argPtr;
+ char *func;
+ Boolean parens; /* TRUE if arg should be bounded by parens */
+{
+ register char *cp;
+ int argLen;
+ register Buffer buf;
+
+ cp = *linePtr;
+ if (parens) {
+ while (*cp != '(' && *cp != '\0') {
+ cp++;
+ }
+ if (*cp == '(') {
+ cp++;
+ }
+ }
+
+ if (*cp == '\0') {
+ /*
+ * No arguments whatsoever. Because 'make' and 'defined' aren't really
+ * "reserved words", we don't print a message. I think this is better
+ * than hitting the user with a warning message every time s/he uses
+ * the word 'make' or 'defined' at the beginning of a symbol...
+ */
+ *argPtr = cp;
+ return (0);
+ }
+
+ while (*cp == ' ' || *cp == '\t') {
+ cp++;
+ }
+
+ /*
+ * Create a buffer for the argument and start it out at 16 characters
+ * long. Why 16? Why not?
+ */
+ buf = Buf_Init(16);
+
+ while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) {
+ if (*cp == '$') {
+ /*
+ * Parse the variable spec and install it as part of the argument
+ * if it's valid. We tell Var_Parse to complain on an undefined
+ * variable, so we don't do it too. Nor do we return an error,
+ * though perhaps we should...
+ */
+ char *cp2;
+ int len;
+ Boolean doFree;
+
+ cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree);
+
+ Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
+ if (doFree) {
+ free(cp2);
+ }
+ cp += len;
+ } else {
+ Buf_AddByte(buf, (Byte)*cp);
+ cp++;
+ }
+ }
+
+ Buf_AddByte(buf, (Byte)'\0');
+ *argPtr = (char *)Buf_GetAll(buf, &argLen);
+ Buf_Destroy(buf, FALSE);
+
+ while (*cp == ' ' || *cp == '\t') {
+ cp++;
+ }
+ if (parens && *cp != ')') {
+ Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()",
+ func);
+ return (0);
+ } else if (parens) {
+ /*
+ * Advance pointer past close parenthesis.
+ */
+ cp++;
+ }
+
+ *linePtr = cp;
+ return (argLen);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondDoDefined --
+ * Handle the 'defined' function for conditionals.
+ *
+ * Results:
+ * TRUE if the given variable is defined.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+CondDoDefined (argLen, arg)
+ int argLen;
+ char *arg;
+{
+ char savec = arg[argLen];
+ char *p1;
+ Boolean result;
+
+ arg[argLen] = '\0';
+ if (Var_Value (arg, VAR_CMD, &p1) != (char *)NULL) {
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+ if (p1)
+ free(p1);
+ arg[argLen] = savec;
+ return (result);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondStrMatch --
+ * Front-end for Str_Match so it returns 0 on match and non-zero
+ * on mismatch. Callback function for CondDoMake via Lst_Find
+ *
+ * Results:
+ * 0 if string matches pattern
+ *
+ * Side Effects:
+ * None
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+CondStrMatch(string, pattern)
+ ClientData string;
+ ClientData pattern;
+{
+ return(!Str_Match((char *) string,(char *) pattern));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondDoMake --
+ * Handle the 'make' function for conditionals.
+ *
+ * Results:
+ * TRUE if the given target is being made.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+CondDoMake (argLen, arg)
+ int argLen;
+ char *arg;
+{
+ char savec = arg[argLen];
+ Boolean result;
+
+ arg[argLen] = '\0';
+ if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) {
+ result = FALSE;
+ } else {
+ result = TRUE;
+ }
+ arg[argLen] = savec;
+ return (result);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondDoExists --
+ * See if the given file exists.
+ *
+ * Results:
+ * TRUE if the file exists and FALSE if it does not.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+CondDoExists (argLen, arg)
+ int argLen;
+ char *arg;
+{
+ char savec = arg[argLen];
+ Boolean result;
+ char *path;
+
+ arg[argLen] = '\0';
+ path = Dir_FindFile(arg, dirSearchPath);
+ if (path != (char *)NULL) {
+ result = TRUE;
+ free(path);
+ } else {
+ result = FALSE;
+ }
+ arg[argLen] = savec;
+ return (result);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondDoTarget --
+ * See if the given node exists and is an actual target.
+ *
+ * Results:
+ * TRUE if the node exists as a target and FALSE if it does not.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+CondDoTarget (argLen, arg)
+ int argLen;
+ char *arg;
+{
+ char savec = arg[argLen];
+ Boolean result;
+ GNode *gn;
+
+ arg[argLen] = '\0';
+ gn = Targ_FindNode(arg, TARG_NOCREATE);
+ if ((gn != NILGNODE) && !OP_NOP(gn->type)) {
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+ arg[argLen] = savec;
+ return (result);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondCvtArg --
+ * Convert the given number into a double. If the number begins
+ * with 0x, it is interpreted as a hexadecimal integer
+ * and converted to a double from there. All other strings just have
+ * strtod called on them.
+ *
+ * Results:
+ * Sets 'value' to double value of string.
+ * Returns true if the string was a valid number, false o.w.
+ *
+ * Side Effects:
+ * Can change 'value' even if string is not a valid number.
+ *
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+CondCvtArg(str, value)
+ register char *str;
+ double *value;
+{
+ if ((*str == '0') && (str[1] == 'x')) {
+ register long i;
+
+ for (str += 2, i = 0; *str; str++) {
+ int x;
+ if (isdigit((unsigned char) *str))
+ x = *str - '0';
+ else if (isxdigit((unsigned char) *str))
+ x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a';
+ else
+ return FALSE;
+ i = (i << 4) + x;
+ }
+ *value = (double) i;
+ return TRUE;
+ }
+ else {
+ char *eptr;
+ *value = strtod(str, &eptr);
+ return *eptr == '\0';
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondToken --
+ * Return the next token from the input.
+ *
+ * Results:
+ * A Token for the next lexical token in the stream.
+ *
+ * Side Effects:
+ * condPushback will be set back to None if it is used.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Token
+CondToken(doEval)
+ Boolean doEval;
+{
+ Token t;
+
+ if (condPushBack == None) {
+ while (*condExpr == ' ' || *condExpr == '\t') {
+ condExpr++;
+ }
+ switch (*condExpr) {
+ case '(':
+ t = LParen;
+ condExpr++;
+ break;
+ case ')':
+ t = RParen;
+ condExpr++;
+ break;
+ case '|':
+ if (condExpr[1] == '|') {
+ condExpr++;
+ }
+ condExpr++;
+ t = Or;
+ break;
+ case '&':
+ if (condExpr[1] == '&') {
+ condExpr++;
+ }
+ condExpr++;
+ t = And;
+ break;
+ case '!':
+ t = Not;
+ condExpr++;
+ break;
+ case '\n':
+ case '\0':
+ t = EndOfFile;
+ break;
+ case '$': {
+ char *lhs;
+ char *rhs;
+ char *op;
+ int varSpecLen;
+ Boolean doFree;
+
+ /*
+ * Parse the variable spec and skip over it, saving its
+ * value in lhs.
+ */
+ t = Err;
+ lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree);
+ if (lhs == var_Error) {
+ /*
+ * Even if !doEval, we still report syntax errors, which
+ * is what getting var_Error back with !doEval means.
+ */
+ return(Err);
+ }
+ condExpr += varSpecLen;
+
+ if (!isspace((unsigned char) *condExpr) &&
+ strchr("!=><", *condExpr) == NULL) {
+ Buffer buf;
+ char *cp;
+
+ buf = Buf_Init(0);
+
+ for (cp = lhs; *cp; cp++)
+ Buf_AddByte(buf, (Byte)*cp);
+
+ if (doFree)
+ free(lhs);
+
+ for (;*condExpr && !isspace((unsigned char) *condExpr);
+ condExpr++)
+ Buf_AddByte(buf, (Byte)*condExpr);
+
+ Buf_AddByte(buf, (Byte)'\0');
+ lhs = (char *)Buf_GetAll(buf, &varSpecLen);
+ Buf_Destroy(buf, FALSE);
+
+ doFree = TRUE;
+ }
+
+ /*
+ * Skip whitespace to get to the operator
+ */
+ while (isspace((unsigned char) *condExpr))
+ condExpr++;
+
+ /*
+ * Make sure the operator is a valid one. If it isn't a
+ * known relational operator, pretend we got a
+ * != 0 comparison.
+ */
+ op = condExpr;
+ switch (*condExpr) {
+ case '!':
+ case '=':
+ case '<':
+ case '>':
+ if (condExpr[1] == '=') {
+ condExpr += 2;
+ } else {
+ condExpr += 1;
+ }
+ break;
+ default:
+ op = "!=";
+ rhs = "0";
+
+ goto do_compare;
+ }
+ while (isspace((unsigned char) *condExpr)) {
+ condExpr++;
+ }
+ if (*condExpr == '\0') {
+ Parse_Error(PARSE_WARNING,
+ "Missing right-hand-side of operator");
+ goto error;
+ }
+ rhs = condExpr;
+do_compare:
+ if (*rhs == '"') {
+ /*
+ * Doing a string comparison. Only allow == and != for
+ * operators.
+ */
+ char *string;
+ char *cp, *cp2;
+ int qt;
+ Buffer buf;
+
+do_string_compare:
+ if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
+ Parse_Error(PARSE_WARNING,
+ "String comparison operator should be either == or !=");
+ goto error;
+ }
+
+ buf = Buf_Init(0);
+ qt = *rhs == '"' ? 1 : 0;
+
+ for (cp = &rhs[qt];
+ ((qt && (*cp != '"')) ||
+ (!qt && strchr(" \t)", *cp) == NULL)) &&
+ (*cp != '\0'); cp++) {
+ if ((*cp == '\\') && (cp[1] != '\0')) {
+ /*
+ * Backslash escapes things -- skip over next
+ * character, if it exists.
+ */
+ cp++;
+ Buf_AddByte(buf, (Byte)*cp);
+ } else if (*cp == '$') {
+ int len;
+ Boolean freeIt;
+
+ cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt);
+ if (cp2 != var_Error) {
+ Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
+ if (freeIt) {
+ free(cp2);
+ }
+ cp += len - 1;
+ } else {
+ Buf_AddByte(buf, (Byte)*cp);
+ }
+ } else {
+ Buf_AddByte(buf, (Byte)*cp);
+ }
+ }
+
+ Buf_AddByte(buf, (Byte)0);
+
+ string = (char *)Buf_GetAll(buf, (int *)0);
+ Buf_Destroy(buf, FALSE);
+
+ if (DEBUG(COND)) {
+ printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
+ lhs, string, op);
+ }
+ /*
+ * Null-terminate rhs and perform the comparison.
+ * t is set to the result.
+ */
+ if (*op == '=') {
+ t = strcmp(lhs, string) ? False : True;
+ } else {
+ t = strcmp(lhs, string) ? True : False;
+ }
+ free(string);
+ if (rhs == condExpr) {
+ if (!qt && *cp == ')')
+ condExpr = cp;
+ else
+ condExpr = cp + 1;
+ }
+ } else {
+ /*
+ * rhs is either a float or an integer. Convert both the
+ * lhs and the rhs to a double and compare the two.
+ */
+ double left, right;
+ char *string;
+
+ if (!CondCvtArg(lhs, &left))
+ goto do_string_compare;
+ if (*rhs == '$') {
+ int len;
+ Boolean freeIt;
+
+ string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt);
+ if (string == var_Error) {
+ right = 0.0;
+ } else {
+ if (!CondCvtArg(string, &right)) {
+ if (freeIt)
+ free(string);
+ goto do_string_compare;
+ }
+ if (freeIt)
+ free(string);
+ if (rhs == condExpr)
+ condExpr += len;
+ }
+ } else {
+ if (!CondCvtArg(rhs, &right))
+ goto do_string_compare;
+ if (rhs == condExpr) {
+ /*
+ * Skip over the right-hand side
+ */
+ while(!isspace((unsigned char) *condExpr) &&
+ (*condExpr != '\0')) {
+ condExpr++;
+ }
+ }
+ }
+
+ if (DEBUG(COND)) {
+ printf("left = %f, right = %f, op = %.2s\n", left,
+ right, op);
+ }
+ switch(op[0]) {
+ case '!':
+ if (op[1] != '=') {
+ Parse_Error(PARSE_WARNING,
+ "Unknown operator");
+ goto error;
+ }
+ t = (left != right ? True : False);
+ break;
+ case '=':
+ if (op[1] != '=') {
+ Parse_Error(PARSE_WARNING,
+ "Unknown operator");
+ goto error;
+ }
+ t = (left == right ? True : False);
+ break;
+ case '<':
+ if (op[1] == '=') {
+ t = (left <= right ? True : False);
+ } else {
+ t = (left < right ? True : False);
+ }
+ break;
+ case '>':
+ if (op[1] == '=') {
+ t = (left >= right ? True : False);
+ } else {
+ t = (left > right ? True : False);
+ }
+ break;
+ }
+ }
+error:
+ if (doFree)
+ free(lhs);
+ break;
+ }
+ default: {
+ Boolean (*evalProc)();
+ Boolean invert = FALSE;
+ char *arg;
+ int arglen;
+
+ if (strncmp (condExpr, "defined", 7) == 0) {
+ /*
+ * Use CondDoDefined to evaluate the argument and
+ * CondGetArg to extract the argument from the 'function
+ * call'.
+ */
+ evalProc = CondDoDefined;
+ condExpr += 7;
+ arglen = CondGetArg (&condExpr, &arg, "defined", TRUE);
+ if (arglen == 0) {
+ condExpr -= 7;
+ goto use_default;
+ }
+ } else if (strncmp (condExpr, "make", 4) == 0) {
+ /*
+ * Use CondDoMake to evaluate the argument and
+ * CondGetArg to extract the argument from the 'function
+ * call'.
+ */
+ evalProc = CondDoMake;
+ condExpr += 4;
+ arglen = CondGetArg (&condExpr, &arg, "make", TRUE);
+ if (arglen == 0) {
+ condExpr -= 4;
+ goto use_default;
+ }
+ } else if (strncmp (condExpr, "exists", 6) == 0) {
+ /*
+ * Use CondDoExists to evaluate the argument and
+ * CondGetArg to extract the argument from the
+ * 'function call'.
+ */
+ evalProc = CondDoExists;
+ condExpr += 6;
+ arglen = CondGetArg(&condExpr, &arg, "exists", TRUE);
+ if (arglen == 0) {
+ condExpr -= 6;
+ goto use_default;
+ }
+ } else if (strncmp(condExpr, "empty", 5) == 0) {
+ /*
+ * Use Var_Parse to parse the spec in parens and return
+ * True if the resulting string is empty.
+ */
+ int length;
+ Boolean doFree;
+ char *val;
+
+ condExpr += 5;
+
+ for (arglen = 0;
+ condExpr[arglen] != '(' && condExpr[arglen] != '\0';
+ arglen += 1)
+ continue;
+
+ if (condExpr[arglen] != '\0') {
+ val = Var_Parse(&condExpr[arglen - 1], VAR_CMD,
+ doEval, &length, &doFree);
+ if (val == var_Error) {
+ t = Err;
+ } else {
+ /*
+ * A variable is empty when it just contains
+ * spaces... 4/15/92, christos
+ */
+ char *p;
+ for (p = val; *p && isspace((unsigned char)*p); p++)
+ continue;
+ t = (*p == '\0') ? True : False;
+ }
+ if (doFree) {
+ free(val);
+ }
+ /*
+ * Advance condExpr to beyond the closing ). Note that
+ * we subtract one from arglen + length b/c length
+ * is calculated from condExpr[arglen - 1].
+ */
+ condExpr += arglen + length - 1;
+ } else {
+ condExpr -= 5;
+ goto use_default;
+ }
+ break;
+ } else if (strncmp (condExpr, "target", 6) == 0) {
+ /*
+ * Use CondDoTarget to evaluate the argument and
+ * CondGetArg to extract the argument from the
+ * 'function call'.
+ */
+ evalProc = CondDoTarget;
+ condExpr += 6;
+ arglen = CondGetArg(&condExpr, &arg, "target", TRUE);
+ if (arglen == 0) {
+ condExpr -= 6;
+ goto use_default;
+ }
+ } else {
+ /*
+ * The symbol is itself the argument to the default
+ * function. We advance condExpr to the end of the symbol
+ * by hand (the next whitespace, closing paren or
+ * binary operator) and set to invert the evaluation
+ * function if condInvert is TRUE.
+ */
+ use_default:
+ invert = condInvert;
+ evalProc = condDefProc;
+ arglen = CondGetArg(&condExpr, &arg, "", FALSE);
+ }
+
+ /*
+ * Evaluate the argument using the set function. If invert
+ * is TRUE, we invert the sense of the function.
+ */
+ t = (!doEval || (* evalProc) (arglen, arg) ?
+ (invert ? False : True) :
+ (invert ? True : False));
+ free(arg);
+ break;
+ }
+ }
+ } else {
+ t = condPushBack;
+ condPushBack = None;
+ }
+ return (t);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondT --
+ * Parse a single term in the expression. This consists of a terminal
+ * symbol or Not and a terminal symbol (not including the binary
+ * operators):
+ * T -> defined(variable) | make(target) | exists(file) | symbol
+ * T -> ! T | ( E )
+ *
+ * Results:
+ * True, False or Err.
+ *
+ * Side Effects:
+ * Tokens are consumed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Token
+CondT(doEval)
+ Boolean doEval;
+{
+ Token t;
+
+ t = CondToken(doEval);
+
+ if (t == EndOfFile) {
+ /*
+ * If we reached the end of the expression, the expression
+ * is malformed...
+ */
+ t = Err;
+ } else if (t == LParen) {
+ /*
+ * T -> ( E )
+ */
+ t = CondE(doEval);
+ if (t != Err) {
+ if (CondToken(doEval) != RParen) {
+ t = Err;
+ }
+ }
+ } else if (t == Not) {
+ t = CondT(doEval);
+ if (t == True) {
+ t = False;
+ } else if (t == False) {
+ t = True;
+ }
+ }
+ return (t);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondF --
+ * Parse a conjunctive factor (nice name, wot?)
+ * F -> T && F | T
+ *
+ * Results:
+ * True, False or Err
+ *
+ * Side Effects:
+ * Tokens are consumed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Token
+CondF(doEval)
+ Boolean doEval;
+{
+ Token l, o;
+
+ l = CondT(doEval);
+ if (l != Err) {
+ o = CondToken(doEval);
+
+ if (o == And) {
+ /*
+ * F -> T && F
+ *
+ * If T is False, the whole thing will be False, but we have to
+ * parse the r.h.s. anyway (to throw it away).
+ * If T is True, the result is the r.h.s., be it an Err or no.
+ */
+ if (l == True) {
+ l = CondF(doEval);
+ } else {
+ (void) CondF(FALSE);
+ }
+ } else {
+ /*
+ * F -> T
+ */
+ CondPushBack (o);
+ }
+ }
+ return (l);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * CondE --
+ * Main expression production.
+ * E -> F || E | F
+ *
+ * Results:
+ * True, False or Err.
+ *
+ * Side Effects:
+ * Tokens are, of course, consumed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Token
+CondE(doEval)
+ Boolean doEval;
+{
+ Token l, o;
+
+ l = CondF(doEval);
+ if (l != Err) {
+ o = CondToken(doEval);
+
+ if (o == Or) {
+ /*
+ * E -> F || E
+ *
+ * A similar thing occurs for ||, except that here we make sure
+ * the l.h.s. is False before we bother to evaluate the r.h.s.
+ * Once again, if l is False, the result is the r.h.s. and once
+ * again if l is True, we parse the r.h.s. to throw it away.
+ */
+ if (l == False) {
+ l = CondE(doEval);
+ } else {
+ (void) CondE(FALSE);
+ }
+ } else {
+ /*
+ * E -> F
+ */
+ CondPushBack (o);
+ }
+ }
+ return (l);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Cond_Eval --
+ * Evaluate the conditional in the passed line. The line
+ * looks like this:
+ * #<cond-type> <expr>
+ * where <cond-type> is any of if, ifmake, ifnmake, ifdef,
+ * ifndef, elif, elifmake, elifnmake, elifdef, elifndef
+ * and <expr> consists of &&, ||, !, make(target), defined(variable)
+ * and parenthetical groupings thereof.
+ *
+ * Results:
+ * COND_PARSE if should parse lines after the conditional
+ * COND_SKIP if should skip lines after the conditional
+ * COND_INVALID if not a valid conditional.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Cond_Eval (line)
+ char *line; /* Line to parse */
+{
+ struct If *ifp;
+ Boolean isElse;
+ Boolean value = FALSE;
+ int level; /* Level at which to report errors. */
+
+ level = PARSE_FATAL;
+
+ for (line++; *line == ' ' || *line == '\t'; line++) {
+ continue;
+ }
+
+ /*
+ * Find what type of if we're dealing with. The result is left
+ * in ifp and isElse is set TRUE if it's an elif line.
+ */
+ if (line[0] == 'e' && line[1] == 'l') {
+ line += 2;
+ isElse = TRUE;
+ } else if (strncmp (line, "endif", 5) == 0) {
+ /*
+ * End of a conditional section. If skipIfLevel is non-zero, that
+ * conditional was skipped, so lines following it should also be
+ * skipped. Hence, we return COND_SKIP. Otherwise, the conditional
+ * was read so succeeding lines should be parsed (think about it...)
+ * so we return COND_PARSE, unless this endif isn't paired with
+ * a decent if.
+ */
+ if (skipIfLevel != 0) {
+ skipIfLevel -= 1;
+ return (COND_SKIP);
+ } else {
+ if (condTop == MAXIF) {
+ Parse_Error (level, "if-less endif");
+ return (COND_INVALID);
+ } else {
+ skipLine = FALSE;
+ condTop += 1;
+ return (COND_PARSE);
+ }
+ }
+ } else {
+ isElse = FALSE;
+ }
+
+ /*
+ * Figure out what sort of conditional it is -- what its default
+ * function is, etc. -- by looking in the table of valid "ifs"
+ */
+ for (ifp = ifs; ifp->form != (char *)0; ifp++) {
+ if (strncmp (ifp->form, line, ifp->formlen) == 0) {
+ break;
+ }
+ }
+
+ if (ifp->form == (char *) 0) {
+ /*
+ * Nothing fit. If the first word on the line is actually
+ * "else", it's a valid conditional whose value is the inverse
+ * of the previous if we parsed.
+ */
+ if (isElse && (line[0] == 's') && (line[1] == 'e')) {
+ if (condTop == MAXIF) {
+ Parse_Error (level, "if-less else");
+ return (COND_INVALID);
+ } else if (skipIfLevel == 0) {
+ value = !condStack[condTop];
+ } else {
+ return (COND_SKIP);
+ }
+ } else {
+ /*
+ * Not a valid conditional type. No error...
+ */
+ return (COND_INVALID);
+ }
+ } else {
+ if (isElse) {
+ if (condTop == MAXIF) {
+ Parse_Error (level, "if-less elif");
+ return (COND_INVALID);
+ } else if (skipIfLevel != 0) {
+ /*
+ * If skipping this conditional, just ignore the whole thing.
+ * If we don't, the user might be employing a variable that's
+ * undefined, for which there's an enclosing ifdef that
+ * we're skipping...
+ */
+ return(COND_SKIP);
+ }
+ } else if (skipLine) {
+ /*
+ * Don't even try to evaluate a conditional that's not an else if
+ * we're skipping things...
+ */
+ skipIfLevel += 1;
+ return(COND_SKIP);
+ }
+
+ /*
+ * Initialize file-global variables for parsing
+ */
+ condDefProc = ifp->defProc;
+ condInvert = ifp->doNot;
+
+ line += ifp->formlen;
+
+ while (*line == ' ' || *line == '\t') {
+ line++;
+ }
+
+ condExpr = line;
+ condPushBack = None;
+
+ switch (CondE(TRUE)) {
+ case True:
+ if (CondToken(TRUE) == EndOfFile) {
+ value = TRUE;
+ break;
+ }
+ goto err;
+ /*FALLTHRU*/
+ case False:
+ if (CondToken(TRUE) == EndOfFile) {
+ value = FALSE;
+ break;
+ }
+ /*FALLTHRU*/
+ case Err:
+ err:
+ Parse_Error (level, "Malformed conditional (%s)",
+ line);
+ return (COND_INVALID);
+ default:
+ break;
+ }
+ }
+ if (!isElse) {
+ condTop -= 1;
+ } else if ((skipIfLevel != 0) || condStack[condTop]) {
+ /*
+ * If this is an else-type conditional, it should only take effect
+ * if its corresponding if was evaluated and FALSE. If its if was
+ * TRUE or skipped, we return COND_SKIP (and start skipping in case
+ * we weren't already), leaving the stack unmolested so later elif's
+ * don't screw up...
+ */
+ skipLine = TRUE;
+ return (COND_SKIP);
+ }
+
+ if (condTop < 0) {
+ /*
+ * This is the one case where we can definitely proclaim a fatal
+ * error. If we don't, we're hosed.
+ */
+ Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF);
+ return (COND_INVALID);
+ } else {
+ condStack[condTop] = value;
+ skipLine = !value;
+ return (value ? COND_PARSE : COND_SKIP);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Cond_End --
+ * Make sure everything's clean at the end of a makefile.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Parse_Error will be called if open conditionals are around.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Cond_End()
+{
+ if (condTop != MAXIF) {
+ Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop,
+ MAXIF-condTop == 1 ? "" : "s");
+ }
+ condTop = MAXIF;
+}
diff --git a/usr.bin/make/config.h b/usr.bin/make/config.h
new file mode 100644
index 0000000..18a9e68
--- /dev/null
+++ b/usr.bin/make/config.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define DEFSHELL 1 /* Bourne shell */
+
+/*
+ * DEFMAXJOBS
+ * DEFMAXLOCAL
+ * These control the default concurrency. On no occasion will more
+ * than DEFMAXJOBS targets be created at once (locally or remotely)
+ * DEFMAXLOCAL is the highest number of targets which will be
+ * created on the local machine at once. Note that if you set this
+ * to 0, nothing will ever happen...
+ */
+#define DEFMAXJOBS 4
+#define DEFMAXLOCAL 1
+
+/*
+ * INCLUDES
+ * LIBRARIES
+ * These control the handling of the .INCLUDES and .LIBS variables.
+ * If INCLUDES is defined, the .INCLUDES variable will be filled
+ * from the search paths of those suffixes which are marked by
+ * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS
+ * See suff.c for more details.
+ */
+#define INCLUDES
+#define LIBRARIES
+
+/*
+ * LIBSUFF
+ * Is the suffix used to denote libraries and is used by the Suff module
+ * to find the search path on which to seek any -l<xx> targets.
+ *
+ * RECHECK
+ * If defined, Make_Update will check a target for its current
+ * modification time after it has been re-made, setting it to the
+ * starting time of the make only if the target still doesn't exist.
+ * Unfortunately, under NFS the modification time often doesn't
+ * get updated in time, so a target will appear to not have been
+ * re-made, causing later targets to appear up-to-date. On systems
+ * that don't have this problem, you should defined this. Under
+ * NFS you probably should not, unless you aren't exporting jobs.
+ *
+ * POSIX
+ * If the POSIX standard for Make is to be followed. There are
+ * several areas that I dislike, hence this constant.
+ */
+#define LIBSUFF ".a"
+#define RECHECK
+
+#ifndef RANLIBMAG
+#define RANLIBMAG "__.SYMDEF"
+#endif
+/*#define POSIX*/
diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c
new file mode 100644
index 0000000..70f5038
--- /dev/null
+++ b/usr.bin/make/dir.c
@@ -0,0 +1,1274 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+/*-
+ * dir.c --
+ * Directory searching using wildcards and/or normal names...
+ * Used both for source wildcarding in the Makefile and for finding
+ * implicit sources.
+ *
+ * The interface for this module is:
+ * Dir_Init Initialize the module.
+ *
+ * Dir_End Cleanup the module.
+ *
+ * Dir_HasWildcards Returns TRUE if the name given it needs to
+ * be wildcard-expanded.
+ *
+ * Dir_Expand Given a pattern and a path, return a Lst of names
+ * which match the pattern on the search path.
+ *
+ * Dir_FindFile Searches for a file on a given search path.
+ * If it exists, the entire path is returned.
+ * Otherwise NULL is returned.
+ *
+ * Dir_MTime Return the modification time of a node. The file
+ * is searched for along the default search path.
+ * The path and mtime fields of the node are filled
+ * in.
+ *
+ * Dir_AddDir Add a directory to a search path.
+ *
+ * Dir_MakeFlags Given a search path and a command flag, create
+ * a string with each of the directories in the path
+ * preceded by the command flag and all of them
+ * separated by a space.
+ *
+ * Dir_Destroy Destroy an element of a search path. Frees up all
+ * things that can be freed for the element as long
+ * as the element is no longer referenced by any other
+ * search path.
+ * Dir_ClearPath Resets a search path to the empty list.
+ *
+ * For debugging:
+ * Dir_PrintDirectories Print stats about the directory cache.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+
+/*
+ * A search path consists of a Lst of Path structures. A Path structure
+ * has in it the name of the directory and a hash table of all the files
+ * in the directory. This is used to cut down on the number of system
+ * calls necessary to find implicit dependents and their like. Since
+ * these searches are made before any actions are taken, we need not
+ * worry about the directory changing due to creation commands. If this
+ * hampers the style of some makefiles, they must be changed.
+ *
+ * A list of all previously-read directories is kept in the
+ * openDirectories Lst. This list is checked first before a directory
+ * is opened.
+ *
+ * The need for the caching of whole directories is brought about by
+ * the multi-level transformation code in suff.c, which tends to search
+ * for far more files than regular make does. In the initial
+ * implementation, the amount of time spent performing "stat" calls was
+ * truly astronomical. The problem with hashing at the start is,
+ * of course, that pmake doesn't then detect changes to these directories
+ * during the course of the make. Three possibilities suggest themselves:
+ *
+ * 1) just use stat to test for a file's existence. As mentioned
+ * above, this is very inefficient due to the number of checks
+ * engendered by the multi-level transformation code.
+ * 2) use readdir() and company to search the directories, keeping
+ * them open between checks. I have tried this and while it
+ * didn't slow down the process too much, it could severely
+ * affect the amount of parallelism available as each directory
+ * open would take another file descriptor out of play for
+ * handling I/O for another job. Given that it is only recently
+ * that UNIX OS's have taken to allowing more than 20 or 32
+ * file descriptors for a process, this doesn't seem acceptable
+ * to me.
+ * 3) record the mtime of the directory in the Path structure and
+ * verify the directory hasn't changed since the contents were
+ * hashed. This will catch the creation or deletion of files,
+ * but not the updating of files. However, since it is the
+ * creation and deletion that is the problem, this could be
+ * a good thing to do. Unfortunately, if the directory (say ".")
+ * were fairly large and changed fairly frequently, the constant
+ * rehashing could seriously degrade performance. It might be
+ * good in such cases to keep track of the number of rehashes
+ * and if the number goes over a (small) limit, resort to using
+ * stat in its place.
+ *
+ * An additional thing to consider is that pmake is used primarily
+ * to create C programs and until recently pcc-based compilers refused
+ * to allow you to specify where the resulting object file should be
+ * placed. This forced all objects to be created in the current
+ * directory. This isn't meant as a full excuse, just an explanation of
+ * some of the reasons for the caching used here.
+ *
+ * One more note: the location of a target's file is only performed
+ * on the downward traversal of the graph and then only for terminal
+ * nodes in the graph. This could be construed as wrong in some cases,
+ * but prevents inadvertent modification of files when the "installed"
+ * directory for a file is provided in the search path.
+ *
+ * Another data structure maintained by this module is an mtime
+ * cache used when the searching of cached directories fails to find
+ * a file. In the past, Dir_FindFile would simply perform an access()
+ * call in such a case to determine if the file could be found using
+ * just the name given. When this hit, however, all that was gained
+ * was the knowledge that the file existed. Given that an access() is
+ * essentially a stat() without the copyout() call, and that the same
+ * filesystem overhead would have to be incurred in Dir_MTime, it made
+ * sense to replace the access() with a stat() and record the mtime
+ * in a cache for when Dir_MTime was actually called.
+ */
+
+Lst dirSearchPath; /* main search path */
+
+static Lst openDirectories; /* the list of all open directories */
+
+/*
+ * Variables for gathering statistics on the efficiency of the hashing
+ * mechanism.
+ */
+static int hits, /* Found in directory cache */
+ misses, /* Sad, but not evil misses */
+ nearmisses, /* Found under search path */
+ bigmisses; /* Sought by itself */
+
+static Path *dot; /* contents of current directory */
+static Hash_Table mtimes; /* Results of doing a last-resort stat in
+ * Dir_FindFile -- if we have to go to the
+ * system to find the file, we might as well
+ * have its mtime on record. XXX: If this is done
+ * way early, there's a chance other rules will
+ * have already updated the file, in which case
+ * we'll update it again. Generally, there won't
+ * be two rules to update a single file, so this
+ * should be ok, but... */
+
+
+static int DirFindName __P((ClientData, ClientData));
+static int DirMatchFiles __P((char *, Path *, Lst));
+static void DirExpandCurly __P((char *, char *, Lst, Lst));
+static void DirExpandInt __P((char *, Lst, Lst));
+static int DirPrintWord __P((ClientData, ClientData));
+static int DirPrintDir __P((ClientData, ClientData));
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_Init --
+ * initialize things for this module
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * some directories may be opened.
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_Init ()
+{
+ dirSearchPath = Lst_Init (FALSE);
+ openDirectories = Lst_Init (FALSE);
+ Hash_InitTable(&mtimes, 0);
+
+ /*
+ * Since the Path structure is placed on both openDirectories and
+ * the path we give Dir_AddDir (which in this case is openDirectories),
+ * we need to remove "." from openDirectories and what better time to
+ * do it than when we have to fetch the thing anyway?
+ */
+ Dir_AddDir (openDirectories, ".");
+ dot = (Path *) Lst_DeQueue (openDirectories);
+
+ /*
+ * We always need to have dot around, so we increment its reference count
+ * to make sure it's not destroyed.
+ */
+ dot->refCount += 1;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_End --
+ * cleanup things for this module
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * none
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_End()
+{
+ dot->refCount -= 1;
+ Dir_Destroy((ClientData) dot);
+ Dir_ClearPath(dirSearchPath);
+ Lst_Destroy(dirSearchPath, NOFREE);
+ Dir_ClearPath(openDirectories);
+ Lst_Destroy(openDirectories, NOFREE);
+ Hash_DeleteTable(&mtimes);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * DirFindName --
+ * See if the Path structure describes the same directory as the
+ * given one by comparing their names. Called from Dir_AddDir via
+ * Lst_Find when searching the list of open directories.
+ *
+ * Results:
+ * 0 if it is the same. Non-zero otherwise
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static int
+DirFindName (p, dname)
+ ClientData p; /* Current name */
+ ClientData dname; /* Desired name */
+{
+ return (strcmp (((Path *)p)->name, (char *) dname));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_HasWildcards --
+ * see if the given name has any wildcard characters in it
+ *
+ * Results:
+ * returns TRUE if the word should be expanded, FALSE otherwise
+ *
+ * Side Effects:
+ * none
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Dir_HasWildcards (name)
+ char *name; /* name to check */
+{
+ register char *cp;
+
+ for (cp = name; *cp; cp++) {
+ switch(*cp) {
+ case '{':
+ case '[':
+ case '?':
+ case '*':
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * DirMatchFiles --
+ * Given a pattern and a Path structure, see if any files
+ * match the pattern and add their names to the 'expansions' list if
+ * any do. This is incomplete -- it doesn't take care of patterns like
+ * src / *src / *.c properly (just *.c on any of the directories), but it
+ * will do for now.
+ *
+ * Results:
+ * Always returns 0
+ *
+ * Side Effects:
+ * File names are added to the expansions lst. The directory will be
+ * fully hashed when this is done.
+ *-----------------------------------------------------------------------
+ */
+static int
+DirMatchFiles (pattern, p, expansions)
+ char *pattern; /* Pattern to look for */
+ Path *p; /* Directory to search */
+ Lst expansions; /* Place to store the results */
+{
+ Hash_Search search; /* Index into the directory's table */
+ Hash_Entry *entry; /* Current entry in the table */
+ Boolean isDot; /* TRUE if the directory being searched is . */
+
+ isDot = (*p->name == '.' && p->name[1] == '\0');
+
+ for (entry = Hash_EnumFirst(&p->files, &search);
+ entry != (Hash_Entry *)NULL;
+ entry = Hash_EnumNext(&search))
+ {
+ /*
+ * See if the file matches the given pattern. Note we follow the UNIX
+ * convention that dot files will only be found if the pattern
+ * begins with a dot (note also that as a side effect of the hashing
+ * scheme, .* won't match . or .. since they aren't hashed).
+ */
+ if (Str_Match(entry->name, pattern) &&
+ ((entry->name[0] != '.') ||
+ (pattern[0] == '.')))
+ {
+ (void)Lst_AtEnd(expansions,
+ (isDot ? strdup(entry->name) :
+ str_concat(p->name, entry->name,
+ STR_ADDSLASH)));
+ }
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * DirExpandCurly --
+ * Expand curly braces like the C shell. Does this recursively.
+ * Note the special case: if after the piece of the curly brace is
+ * done there are no wildcard characters in the result, the result is
+ * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The given list is filled with the expansions...
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+DirExpandCurly(word, brace, path, expansions)
+ char *word; /* Entire word to expand */
+ char *brace; /* First curly brace in it */
+ Lst path; /* Search path to use */
+ Lst expansions; /* Place to store the expansions */
+{
+ char *end; /* Character after the closing brace */
+ char *cp; /* Current position in brace clause */
+ char *start; /* Start of current piece of brace clause */
+ int bracelevel; /* Number of braces we've seen. If we see a
+ * right brace when this is 0, we've hit the
+ * end of the clause. */
+ char *file; /* Current expansion */
+ int otherLen; /* The length of the other pieces of the
+ * expansion (chars before and after the
+ * clause in 'word') */
+ char *cp2; /* Pointer for checking for wildcards in
+ * expansion before calling Dir_Expand */
+
+ start = brace+1;
+
+ /*
+ * Find the end of the brace clause first, being wary of nested brace
+ * clauses.
+ */
+ for (end = start, bracelevel = 0; *end != '\0'; end++) {
+ if (*end == '{') {
+ bracelevel++;
+ } else if ((*end == '}') && (bracelevel-- == 0)) {
+ break;
+ }
+ }
+ if (*end == '\0') {
+ Error("Unterminated {} clause \"%s\"", start);
+ return;
+ } else {
+ end++;
+ }
+ otherLen = brace - word + strlen(end);
+
+ for (cp = start; cp < end; cp++) {
+ /*
+ * Find the end of this piece of the clause.
+ */
+ bracelevel = 0;
+ while (*cp != ',') {
+ if (*cp == '{') {
+ bracelevel++;
+ } else if ((*cp == '}') && (bracelevel-- <= 0)) {
+ break;
+ }
+ cp++;
+ }
+ /*
+ * Allocate room for the combination and install the three pieces.
+ */
+ file = emalloc(otherLen + cp - start + 1);
+ if (brace != word) {
+ strncpy(file, word, brace-word);
+ }
+ if (cp != start) {
+ strncpy(&file[brace-word], start, cp-start);
+ }
+ strcpy(&file[(brace-word)+(cp-start)], end);
+
+ /*
+ * See if the result has any wildcards in it. If we find one, call
+ * Dir_Expand right away, telling it to place the result on our list
+ * of expansions.
+ */
+ for (cp2 = file; *cp2 != '\0'; cp2++) {
+ switch(*cp2) {
+ case '*':
+ case '?':
+ case '{':
+ case '[':
+ Dir_Expand(file, path, expansions);
+ goto next;
+ }
+ }
+ if (*cp2 == '\0') {
+ /*
+ * Hit the end w/o finding any wildcards, so stick the expansion
+ * on the end of the list.
+ */
+ (void)Lst_AtEnd(expansions, file);
+ } else {
+ next:
+ free(file);
+ }
+ start = cp+1;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * DirExpandInt --
+ * Internal expand routine. Passes through the directories in the
+ * path one by one, calling DirMatchFiles for each. NOTE: This still
+ * doesn't handle patterns in directories...
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Things are added to the expansions list.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+DirExpandInt(word, path, expansions)
+ char *word; /* Word to expand */
+ Lst path; /* Path on which to look */
+ Lst expansions; /* Place to store the result */
+{
+ LstNode ln; /* Current node */
+ Path *p; /* Directory in the node */
+
+ if (Lst_Open(path) == SUCCESS) {
+ while ((ln = Lst_Next(path)) != NILLNODE) {
+ p = (Path *)Lst_Datum(ln);
+ DirMatchFiles(word, p, expansions);
+ }
+ Lst_Close(path);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * DirPrintWord --
+ * Print a word in the list of expansions. Callback for Dir_Expand
+ * when DEBUG(DIR), via Lst_ForEach.
+ *
+ * Results:
+ * === 0
+ *
+ * Side Effects:
+ * The passed word is printed, followed by a space.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+DirPrintWord(word, dummy)
+ ClientData word;
+ ClientData dummy;
+{
+ printf("%s ", (char *) word);
+
+ return(dummy ? 0 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_Expand --
+ * Expand the given word into a list of words by globbing it looking
+ * in the directories on the given search path.
+ *
+ * Results:
+ * A list of words consisting of the files which exist along the search
+ * path matching the given pattern.
+ *
+ * Side Effects:
+ * Directories may be opened. Who knows?
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_Expand (word, path, expansions)
+ char *word; /* the word to expand */
+ Lst path; /* the list of directories in which to find
+ * the resulting files */
+ Lst expansions; /* the list on which to place the results */
+{
+ char *cp;
+
+ if (DEBUG(DIR)) {
+ printf("expanding \"%s\"...", word);
+ }
+
+ cp = strchr(word, '{');
+ if (cp) {
+ DirExpandCurly(word, cp, path, expansions);
+ } else {
+ cp = strchr(word, '/');
+ if (cp) {
+ /*
+ * The thing has a directory component -- find the first wildcard
+ * in the string.
+ */
+ for (cp = word; *cp; cp++) {
+ if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') {
+ break;
+ }
+ }
+ if (*cp == '{') {
+ /*
+ * This one will be fun.
+ */
+ DirExpandCurly(word, cp, path, expansions);
+ return;
+ } else if (*cp != '\0') {
+ /*
+ * Back up to the start of the component
+ */
+ char *dirpath;
+
+ while (cp > word && *cp != '/') {
+ cp--;
+ }
+ if (cp != word) {
+ char sc;
+ /*
+ * If the glob isn't in the first component, try and find
+ * all the components up to the one with a wildcard.
+ */
+ sc = cp[1];
+ cp[1] = '\0';
+ dirpath = Dir_FindFile(word, path);
+ cp[1] = sc;
+ /*
+ * dirpath is null if can't find the leading component
+ * XXX: Dir_FindFile won't find internal components.
+ * i.e. if the path contains ../Etc/Object and we're
+ * looking for Etc, it won't be found. Ah well.
+ * Probably not important.
+ */
+ if (dirpath != (char *)NULL) {
+ char *dp = &dirpath[strlen(dirpath) - 1];
+ if (*dp == '/')
+ *dp = '\0';
+ path = Lst_Init(FALSE);
+ Dir_AddDir(path, dirpath);
+ DirExpandInt(cp+1, path, expansions);
+ Lst_Destroy(path, NOFREE);
+ }
+ } else {
+ /*
+ * Start the search from the local directory
+ */
+ DirExpandInt(word, path, expansions);
+ }
+ } else {
+ /*
+ * Return the file -- this should never happen.
+ */
+ DirExpandInt(word, path, expansions);
+ }
+ } else {
+ /*
+ * First the files in dot
+ */
+ DirMatchFiles(word, dot, expansions);
+
+ /*
+ * Then the files in every other directory on the path.
+ */
+ DirExpandInt(word, path, expansions);
+ }
+ }
+ if (DEBUG(DIR)) {
+ Lst_ForEach(expansions, DirPrintWord, (ClientData) 0);
+ fputc('\n', stdout);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_FindFile --
+ * Find the file with the given name along the given search path.
+ *
+ * Results:
+ * The path to the file or NULL. This path is guaranteed to be in a
+ * different part of memory than name and so may be safely free'd.
+ *
+ * Side Effects:
+ * If the file is found in a directory which is not on the path
+ * already (either 'name' is absolute or it is a relative path
+ * [ dir1/.../dirn/file ] which exists below one of the directories
+ * already on the search path), its directory is added to the end
+ * of the path on the assumption that there will be more files in
+ * that directory later on. Sometimes this is true. Sometimes not.
+ *-----------------------------------------------------------------------
+ */
+char *
+Dir_FindFile (name, path)
+ char *name; /* the file to find */
+ Lst path; /* the Lst of directories to search */
+{
+ register char *p1; /* pointer into p->name */
+ register char *p2; /* pointer into name */
+ LstNode ln; /* a list element */
+ register char *file; /* the current filename to check */
+ register Path *p; /* current path member */
+ register char *cp; /* index of first slash, if any */
+ Boolean hasSlash; /* true if 'name' contains a / */
+ struct stat stb; /* Buffer for stat, if necessary */
+ Hash_Entry *entry; /* Entry for mtimes table */
+
+ /*
+ * Find the final component of the name and note whether it has a
+ * slash in it (the name, I mean)
+ */
+ cp = strrchr (name, '/');
+ if (cp) {
+ hasSlash = TRUE;
+ cp += 1;
+ } else {
+ hasSlash = FALSE;
+ cp = name;
+ }
+
+ if (DEBUG(DIR)) {
+ printf("Searching for %s...", name);
+ }
+ /*
+ * No matter what, we always look for the file in the current directory
+ * before anywhere else and we *do not* add the ./ to it if it exists.
+ * This is so there are no conflicts between what the user specifies
+ * (fish.c) and what pmake finds (./fish.c).
+ */
+ if ((!hasSlash || (cp - name == 2 && *name == '.')) &&
+ (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL)) {
+ if (DEBUG(DIR)) {
+ printf("in '.'\n");
+ }
+ hits += 1;
+ dot->hits += 1;
+ return (strdup (name));
+ }
+
+ if (Lst_Open (path) == FAILURE) {
+ if (DEBUG(DIR)) {
+ printf("couldn't open path, file not found\n");
+ }
+ misses += 1;
+ return ((char *) NULL);
+ }
+
+ /*
+ * We look through all the directories on the path seeking one which
+ * contains the final component of the given name and whose final
+ * component(s) match the name's initial component(s). If such a beast
+ * is found, we concatenate the directory name and the final component
+ * and return the resulting string. If we don't find any such thing,
+ * we go on to phase two...
+ */
+ while ((ln = Lst_Next (path)) != NILLNODE) {
+ p = (Path *) Lst_Datum (ln);
+ if (DEBUG(DIR)) {
+ printf("%s...", p->name);
+ }
+ if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
+ if (DEBUG(DIR)) {
+ printf("here...");
+ }
+ if (hasSlash) {
+ /*
+ * If the name had a slash, its initial components and p's
+ * final components must match. This is false if a mismatch
+ * is encountered before all of the initial components
+ * have been checked (p2 > name at the end of the loop), or
+ * we matched only part of one of the components of p
+ * along with all the rest of them (*p1 != '/').
+ */
+ p1 = p->name + strlen (p->name) - 1;
+ p2 = cp - 2;
+ while (p2 >= name && p1 >= p->name && *p1 == *p2) {
+ p1 -= 1; p2 -= 1;
+ }
+ if (p2 >= name || (p1 >= p->name && *p1 != '/')) {
+ if (DEBUG(DIR)) {
+ printf("component mismatch -- continuing...");
+ }
+ continue;
+ }
+ }
+ file = str_concat (p->name, cp, STR_ADDSLASH);
+ if (DEBUG(DIR)) {
+ printf("returning %s\n", file);
+ }
+ Lst_Close (path);
+ p->hits += 1;
+ hits += 1;
+ return (file);
+ } else if (hasSlash) {
+ /*
+ * If the file has a leading path component and that component
+ * exactly matches the entire name of the current search
+ * directory, we assume the file doesn't exist and return NULL.
+ */
+ for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) {
+ continue;
+ }
+ if (*p1 == '\0' && p2 == cp - 1) {
+ if (DEBUG(DIR)) {
+ printf("must be here but isn't -- returing NULL\n");
+ }
+ Lst_Close (path);
+ return ((char *) NULL);
+ }
+ }
+ }
+
+ /*
+ * We didn't find the file on any existing members of the directory.
+ * If the name doesn't contain a slash, that means it doesn't exist.
+ * If it *does* contain a slash, however, there is still hope: it
+ * could be in a subdirectory of one of the members of the search
+ * path. (eg. /usr/include and sys/types.h. The above search would
+ * fail to turn up types.h in /usr/include, but it *is* in
+ * /usr/include/sys/types.h) If we find such a beast, we assume there
+ * will be more (what else can we assume?) and add all but the last
+ * component of the resulting name onto the search path (at the
+ * end). This phase is only performed if the file is *not* absolute.
+ */
+ if (!hasSlash) {
+ if (DEBUG(DIR)) {
+ printf("failed.\n");
+ }
+ misses += 1;
+ return ((char *) NULL);
+ }
+
+ if (*name != '/') {
+ Boolean checkedDot = FALSE;
+
+ if (DEBUG(DIR)) {
+ printf("failed. Trying subdirectories...");
+ }
+ (void) Lst_Open (path);
+ while ((ln = Lst_Next (path)) != NILLNODE) {
+ p = (Path *) Lst_Datum (ln);
+ if (p != dot) {
+ file = str_concat (p->name, name, STR_ADDSLASH);
+ } else {
+ /*
+ * Checking in dot -- DON'T put a leading ./ on the thing.
+ */
+ file = strdup(name);
+ checkedDot = TRUE;
+ }
+ if (DEBUG(DIR)) {
+ printf("checking %s...", file);
+ }
+
+
+ if (stat (file, &stb) == 0) {
+ if (DEBUG(DIR)) {
+ printf("got it.\n");
+ }
+
+ Lst_Close (path);
+
+ /*
+ * We've found another directory to search. We know there's
+ * a slash in 'file' because we put one there. We nuke it after
+ * finding it and call Dir_AddDir to add this new directory
+ * onto the existing search path. Once that's done, we restore
+ * the slash and triumphantly return the file name, knowing
+ * that should a file in this directory every be referenced
+ * again in such a manner, we will find it without having to do
+ * numerous numbers of access calls. Hurrah!
+ */
+ cp = strrchr (file, '/');
+ *cp = '\0';
+ Dir_AddDir (path, file);
+ *cp = '/';
+
+ /*
+ * Save the modification time so if it's needed, we don't have
+ * to fetch it again.
+ */
+ if (DEBUG(DIR)) {
+ printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
+ file);
+ }
+ entry = Hash_CreateEntry(&mtimes, (char *) file,
+ (Boolean *)NULL);
+ Hash_SetValue(entry, (long)stb.st_mtime);
+ nearmisses += 1;
+ return (file);
+ } else {
+ free (file);
+ }
+ }
+
+ if (DEBUG(DIR)) {
+ printf("failed. ");
+ }
+ Lst_Close (path);
+
+ if (checkedDot) {
+ /*
+ * Already checked by the given name, since . was in the path,
+ * so no point in proceeding...
+ */
+ if (DEBUG(DIR)) {
+ printf("Checked . already, returning NULL\n");
+ }
+ return(NULL);
+ }
+ }
+
+ /*
+ * Didn't find it that way, either. Sigh. Phase 3. Add its directory
+ * onto the search path in any case, just in case, then look for the
+ * thing in the hash table. If we find it, grand. We return a new
+ * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
+ * Note that if the directory holding the file doesn't exist, this will
+ * do an extra search of the final directory on the path. Unless something
+ * weird happens, this search won't succeed and life will be groovy.
+ *
+ * Sigh. We cannot add the directory onto the search path because
+ * of this amusing case:
+ * $(INSTALLDIR)/$(FILE): $(FILE)
+ *
+ * $(FILE) exists in $(INSTALLDIR) but not in the current one.
+ * When searching for $(FILE), we will find it in $(INSTALLDIR)
+ * b/c we added it here. This is not good...
+ */
+#ifdef notdef
+ cp[-1] = '\0';
+ Dir_AddDir (path, name);
+ cp[-1] = '/';
+
+ bigmisses += 1;
+ ln = Lst_Last (path);
+ if (ln == NILLNODE) {
+ return ((char *) NULL);
+ } else {
+ p = (Path *) Lst_Datum (ln);
+ }
+
+ if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) {
+ return (strdup (name));
+ } else {
+ return ((char *) NULL);
+ }
+#else /* !notdef */
+ if (DEBUG(DIR)) {
+ printf("Looking for \"%s\"...", name);
+ }
+
+ bigmisses += 1;
+ entry = Hash_FindEntry(&mtimes, name);
+ if (entry != (Hash_Entry *)NULL) {
+ if (DEBUG(DIR)) {
+ printf("got it (in mtime cache)\n");
+ }
+ return(strdup(name));
+ } else if (stat (name, &stb) == 0) {
+ entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL);
+ if (DEBUG(DIR)) {
+ printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
+ name);
+ }
+ Hash_SetValue(entry, (long)stb.st_mtime);
+ return (strdup (name));
+ } else {
+ if (DEBUG(DIR)) {
+ printf("failed. Returning NULL\n");
+ }
+ return ((char *)NULL);
+ }
+#endif /* notdef */
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_MTime --
+ * Find the modification time of the file described by gn along the
+ * search path dirSearchPath.
+ *
+ * Results:
+ * The modification time or 0 if it doesn't exist
+ *
+ * Side Effects:
+ * The modification time is placed in the node's mtime slot.
+ * If the node didn't have a path entry before, and Dir_FindFile
+ * found one for it, the full name is placed in the path slot.
+ *-----------------------------------------------------------------------
+ */
+int
+Dir_MTime (gn)
+ GNode *gn; /* the file whose modification time is
+ * desired */
+{
+ char *fullName; /* the full pathname of name */
+ struct stat stb; /* buffer for finding the mod time */
+ Hash_Entry *entry;
+
+ if (gn->type & OP_ARCHV) {
+ return Arch_MTime (gn);
+ } else if (gn->path == (char *)NULL) {
+ fullName = Dir_FindFile (gn->name, dirSearchPath);
+ } else {
+ fullName = gn->path;
+ }
+
+ if (fullName == (char *)NULL) {
+ fullName = strdup(gn->name);
+ }
+
+ entry = Hash_FindEntry(&mtimes, fullName);
+ if (entry != (Hash_Entry *)NULL) {
+ /*
+ * Only do this once -- the second time folks are checking to
+ * see if the file was actually updated, so we need to actually go
+ * to the file system.
+ */
+ if (DEBUG(DIR)) {
+ printf("Using cached time %s for %s\n",
+ Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName);
+ }
+ stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
+ Hash_DeleteEntry(&mtimes, entry);
+ } else if (stat (fullName, &stb) < 0) {
+ if (gn->type & OP_MEMBER) {
+ if (fullName != gn->path)
+ free(fullName);
+ return Arch_MemMTime (gn);
+ } else {
+ stb.st_mtime = 0;
+ }
+ }
+ if (fullName && gn->path == (char *)NULL) {
+ gn->path = fullName;
+ }
+
+ gn->mtime = stb.st_mtime;
+ return (gn->mtime);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_AddDir --
+ * Add the given name to the end of the given path. The order of
+ * the arguments is backwards so ParseDoDependency can do a
+ * Lst_ForEach of its list of paths...
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * A structure is added to the list and the directory is
+ * read and hashed.
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_AddDir (path, name)
+ Lst path; /* the path to which the directory should be
+ * added */
+ char *name; /* the name of the directory to add */
+{
+ LstNode ln; /* node in case Path structure is found */
+ register Path *p; /* pointer to new Path structure */
+ DIR *d; /* for reading directory */
+ register struct dirent *dp; /* entry in directory */
+
+ ln = Lst_Find (openDirectories, (ClientData)name, DirFindName);
+ if (ln != NILLNODE) {
+ p = (Path *)Lst_Datum (ln);
+ if (Lst_Member(path, (ClientData)p) == NILLNODE) {
+ p->refCount += 1;
+ (void)Lst_AtEnd (path, (ClientData)p);
+ }
+ } else {
+ if (DEBUG(DIR)) {
+ printf("Caching %s...", name);
+ fflush(stdout);
+ }
+
+ if ((d = opendir (name)) != (DIR *) NULL) {
+ p = (Path *) emalloc (sizeof (Path));
+ p->name = strdup (name);
+ p->hits = 0;
+ p->refCount = 1;
+ Hash_InitTable (&p->files, -1);
+
+ /*
+ * Skip the first two entries -- these will *always* be . and ..
+ */
+ (void)readdir(d);
+ (void)readdir(d);
+
+ while ((dp = readdir (d)) != (struct dirent *) NULL) {
+#ifdef sun
+ /*
+ * The sun directory library doesn't check for a 0 inode
+ * (0-inode slots just take up space), so we have to do
+ * it ourselves.
+ */
+ if (dp->d_fileno == 0) {
+ continue;
+ }
+#endif /* sun */
+ (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL);
+ }
+ (void) closedir (d);
+ (void)Lst_AtEnd (openDirectories, (ClientData)p);
+ (void)Lst_AtEnd (path, (ClientData)p);
+ }
+ if (DEBUG(DIR)) {
+ printf("done\n");
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_CopyDir --
+ * Callback function for duplicating a search path via Lst_Duplicate.
+ * Ups the reference count for the directory.
+ *
+ * Results:
+ * Returns the Path it was given.
+ *
+ * Side Effects:
+ * The refCount of the path is incremented.
+ *
+ *-----------------------------------------------------------------------
+ */
+ClientData
+Dir_CopyDir(p)
+ ClientData p;
+{
+ ((Path *) p)->refCount += 1;
+
+ return ((ClientData)p);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_MakeFlags --
+ * Make a string by taking all the directories in the given search
+ * path and preceding them by the given flag. Used by the suffix
+ * module to create variables for compilers based on suffix search
+ * paths.
+ *
+ * Results:
+ * The string mentioned above. Note that there is no space between
+ * the given flag and each directory. The empty string is returned if
+ * Things don't go well.
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+char *
+Dir_MakeFlags (flag, path)
+ char *flag; /* flag which should precede each directory */
+ Lst path; /* list of directories */
+{
+ char *str; /* the string which will be returned */
+ char *tstr; /* the current directory preceded by 'flag' */
+ LstNode ln; /* the node of the current directory */
+ Path *p; /* the structure describing the current directory */
+
+ str = strdup ("");
+
+ if (Lst_Open (path) == SUCCESS) {
+ while ((ln = Lst_Next (path)) != NILLNODE) {
+ p = (Path *) Lst_Datum (ln);
+ tstr = str_concat (flag, p->name, 0);
+ str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE);
+ }
+ Lst_Close (path);
+ }
+
+ return (str);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_Destroy --
+ * Nuke a directory descriptor, if possible. Callback procedure
+ * for the suffixes module when destroying a search path.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If no other path references this directory (refCount == 0),
+ * the Path and all its data are freed.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_Destroy (pp)
+ ClientData pp; /* The directory descriptor to nuke */
+{
+ Path *p = (Path *) pp;
+ p->refCount -= 1;
+
+ if (p->refCount == 0) {
+ LstNode ln;
+
+ ln = Lst_Member (openDirectories, (ClientData)p);
+ (void) Lst_Remove (openDirectories, ln);
+
+ Hash_DeleteTable (&p->files);
+ free((Address)p->name);
+ free((Address)p);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_ClearPath --
+ * Clear out all elements of the given search path. This is different
+ * from destroying the list, notice.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The path is set to the empty list.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_ClearPath(path)
+ Lst path; /* Path to clear */
+{
+ Path *p;
+ while (!Lst_IsEmpty(path)) {
+ p = (Path *)Lst_DeQueue(path);
+ Dir_Destroy((ClientData) p);
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Dir_Concat --
+ * Concatenate two paths, adding the second to the end of the first.
+ * Makes sure to avoid duplicates.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Reference counts for added dirs are upped.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Dir_Concat(path1, path2)
+ Lst path1; /* Dest */
+ Lst path2; /* Source */
+{
+ LstNode ln;
+ Path *p;
+
+ for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) {
+ p = (Path *)Lst_Datum(ln);
+ if (Lst_Member(path1, (ClientData)p) == NILLNODE) {
+ p->refCount += 1;
+ (void)Lst_AtEnd(path1, (ClientData)p);
+ }
+ }
+}
+
+/********** DEBUG INFO **********/
+void
+Dir_PrintDirectories()
+{
+ LstNode ln;
+ Path *p;
+
+ printf ("#*** Directory Cache:\n");
+ printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
+ hits, misses, nearmisses, bigmisses,
+ (hits+bigmisses+nearmisses ?
+ hits * 100 / (hits + bigmisses + nearmisses) : 0));
+ printf ("# %-20s referenced\thits\n", "directory");
+ if (Lst_Open (openDirectories) == SUCCESS) {
+ while ((ln = Lst_Next (openDirectories)) != NILLNODE) {
+ p = (Path *) Lst_Datum (ln);
+ printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
+ }
+ Lst_Close (openDirectories);
+ }
+}
+
+static int DirPrintDir (p, dummy)
+ ClientData p;
+ ClientData dummy;
+{
+ printf ("%s ", ((Path *) p)->name);
+ return (dummy ? 0 : 0);
+}
+
+void
+Dir_PrintPath (path)
+ Lst path;
+{
+ Lst_ForEach (path, DirPrintDir, (ClientData)0);
+}
diff --git a/usr.bin/make/dir.h b/usr.bin/make/dir.h
new file mode 100644
index 0000000..dd0c2a7
--- /dev/null
+++ b/usr.bin/make/dir.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dir.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* dir.h --
+ */
+
+#ifndef _DIR
+#define _DIR
+
+typedef struct Path {
+ char *name; /* Name of directory */
+ int refCount; /* Number of paths with this directory */
+ int hits; /* the number of times a file in this
+ * directory has been found */
+ Hash_Table files; /* Hash table of files in directory */
+} Path;
+
+void Dir_Init __P((void));
+void Dir_End __P((void));
+Boolean Dir_HasWildcards __P((char *));
+void Dir_Expand __P((char *, Lst, Lst));
+char *Dir_FindFile __P((char *, Lst));
+int Dir_MTime __P((GNode *));
+void Dir_AddDir __P((Lst, char *));
+char *Dir_MakeFlags __P((char *, Lst));
+void Dir_ClearPath __P((Lst));
+void Dir_Concat __P((Lst, Lst));
+void Dir_PrintDirectories __P((void));
+void Dir_PrintPath __P((Lst));
+void Dir_Destroy __P((ClientData));
+ClientData Dir_CopyDir __P((ClientData));
+
+#endif /* _DIR */
diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c
new file mode 100644
index 0000000..c93f317
--- /dev/null
+++ b/usr.bin/make/for.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 1992, The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * for.c --
+ * Functions to handle loops in a makefile.
+ *
+ * Interface:
+ * For_Eval Evaluate the loop in the passed line.
+ * For_Run Run accumulated loop
+ *
+ */
+
+#include <ctype.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "buf.h"
+
+/*
+ * For statements are of the form:
+ *
+ * .for <variable> in <varlist>
+ * ...
+ * .endfor
+ *
+ * The trick is to look for the matching end inside for for loop
+ * To do that, we count the current nesting level of the for loops.
+ * and the .endfor statements, accumulating all the statements between
+ * the initial .for loop and the matching .endfor;
+ * then we evaluate the for loop for each variable in the varlist.
+ */
+
+static int forLevel = 0; /* Nesting level */
+static char *forVar; /* Iteration variable */
+static Buffer forBuf; /* Commands in loop */
+static Lst forLst; /* List of items */
+
+/*
+ * State of a for loop.
+ */
+typedef struct _For {
+ Buffer buf; /* Unexpanded buffer */
+ char* var; /* Index name */
+ Lst lst; /* List of variables */
+} For;
+
+static int ForExec __P((ClientData, ClientData));
+
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * For_Eval --
+ * Evaluate the for loop in the passed line. The line
+ * looks like this:
+ * .for <variable> in <varlist>
+ *
+ * Results:
+ * TRUE: We found a for loop, or we are inside a for loop
+ * FALSE: We did not find a for loop, or we found the end of the for
+ * for loop.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+For_Eval (line)
+ char *line; /* Line to parse */
+{
+ char *ptr = line, *sub, *wrd;
+ int level; /* Level at which to report errors. */
+
+ level = PARSE_FATAL;
+
+
+ if (forLevel == 0) {
+ Buffer buf;
+ int varlen;
+
+ for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
+ continue;
+ /*
+ * If we are not in a for loop quickly determine if the statement is
+ * a for.
+ */
+ if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
+ !isspace((unsigned char) ptr[3]))
+ return FALSE;
+ ptr += 3;
+
+ /*
+ * we found a for loop, and now we are going to parse it.
+ */
+ while (*ptr && isspace((unsigned char) *ptr))
+ ptr++;
+
+ /*
+ * Grab the variable
+ */
+ buf = Buf_Init(0);
+ for (wrd = ptr; *ptr && !isspace((unsigned char) *ptr); ptr++)
+ continue;
+ Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
+
+ forVar = (char *) Buf_GetAll(buf, &varlen);
+ if (varlen == 0) {
+ Parse_Error (level, "missing variable in for");
+ return 0;
+ }
+ Buf_Destroy(buf, FALSE);
+
+ while (*ptr && isspace((unsigned char) *ptr))
+ ptr++;
+
+ /*
+ * Grab the `in'
+ */
+ if (ptr[0] != 'i' || ptr[1] != 'n' ||
+ !isspace((unsigned char) ptr[2])) {
+ Parse_Error (level, "missing `in' in for");
+ printf("%s\n", ptr);
+ return 0;
+ }
+ ptr += 3;
+
+ while (*ptr && isspace((unsigned char) *ptr))
+ ptr++;
+
+ /*
+ * Make a list with the remaining words
+ */
+ forLst = Lst_Init(FALSE);
+ buf = Buf_Init(0);
+ sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
+
+#define ADDWORD() \
+ Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
+ Buf_AddByte(buf, (Byte) '\0'), \
+ Lst_AtEnd(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
+ Buf_Destroy(buf, FALSE)
+
+ for (ptr = sub; *ptr && isspace((unsigned char) *ptr); ptr++)
+ continue;
+
+ for (wrd = ptr; *ptr; ptr++)
+ if (isspace((unsigned char) *ptr)) {
+ ADDWORD();
+ buf = Buf_Init(0);
+ while (*ptr && isspace((unsigned char) *ptr))
+ ptr++;
+ wrd = ptr--;
+ }
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
+ if (ptr - wrd > 0)
+ ADDWORD();
+ else
+ Buf_Destroy(buf, TRUE);
+ free((Address) sub);
+
+ forBuf = Buf_Init(0);
+ forLevel++;
+ return 1;
+ }
+ else if (*ptr == '.') {
+
+ for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++)
+ continue;
+
+ if (strncmp(ptr, "endfor", 6) == 0 &&
+ (isspace((unsigned char) ptr[6]) || !ptr[6])) {
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: end for %d\n", forLevel);
+ if (--forLevel < 0) {
+ Parse_Error (level, "for-less endfor");
+ return 0;
+ }
+ }
+ else if (strncmp(ptr, "for", 3) == 0 &&
+ isspace((unsigned char) ptr[3])) {
+ forLevel++;
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: new loop %d\n", forLevel);
+ }
+ }
+
+ if (forLevel != 0) {
+ Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
+ Buf_AddByte(forBuf, (Byte) '\n');
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ForExec --
+ * Expand the for loop for this index and push it in the Makefile
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ForExec(namep, argp)
+ ClientData namep;
+ ClientData argp;
+{
+ char *name = (char *) namep;
+ For *arg = (For *) argp;
+ int len;
+ Var_Set(arg->var, name, VAR_GLOBAL);
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
+ Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
+ VAR_GLOBAL, FALSE));
+ Var_Delete(arg->var, VAR_GLOBAL);
+
+ return 0;
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * For_Run --
+ * Run the for loop, immitating the actions of an include file
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+For_Run()
+{
+ For arg;
+
+ if (forVar == NULL || forBuf == NULL || forLst == NULL)
+ return;
+ arg.var = forVar;
+ arg.buf = forBuf;
+ arg.lst = forLst;
+ forVar = NULL;
+ forBuf = NULL;
+ forLst = NULL;
+
+ Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
+
+ free((Address)arg.var);
+ Lst_Destroy(arg.lst, (void (*) __P((ClientData))) free);
+ Buf_Destroy(arg.buf, TRUE);
+}
diff --git a/usr.bin/make/hash.c b/usr.bin/make/hash.c
new file mode 100644
index 0000000..026d163
--- /dev/null
+++ b/usr.bin/make/hash.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* hash.c --
+ *
+ * This module contains routines to manipulate a hash table.
+ * See hash.h for a definition of the structure of the hash
+ * table. Hash tables grow automatically as the amount of
+ * information increases.
+ */
+#include "sprite.h"
+#include "make.h"
+#include "hash.h"
+
+/*
+ * Forward references to local procedures that are used before they're
+ * defined:
+ */
+
+static void RebuildTable __P((Hash_Table *));
+
+/*
+ * The following defines the ratio of # entries to # buckets
+ * at which we rebuild the table to make it larger.
+ */
+
+#define rebuildLimit 8
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_InitTable --
+ *
+ * This routine just sets up the hash table.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Memory is allocated for the initial bucket area.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_InitTable(t, numBuckets)
+ register Hash_Table *t; /* Structure to use to hold table. */
+ int numBuckets; /* How many buckets to create for starters.
+ * This number is rounded up to a power of
+ * two. If <= 0, a reasonable default is
+ * chosen. The table will grow in size later
+ * as needed. */
+{
+ register int i;
+ register struct Hash_Entry **hp;
+
+ /*
+ * Round up the size to a power of two.
+ */
+ if (numBuckets <= 0)
+ i = 16;
+ else {
+ for (i = 2; i < numBuckets; i <<= 1)
+ continue;
+ }
+ t->numEntries = 0;
+ t->size = i;
+ t->mask = i - 1;
+ t->bucketPtr = hp = (struct Hash_Entry **)emalloc(sizeof(*hp) * i);
+ while (--i >= 0)
+ *hp++ = NULL;
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_DeleteTable --
+ *
+ * This routine removes everything from a hash table
+ * and frees up the memory space it occupied (except for
+ * the space in the Hash_Table structure).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Lots of memory is freed up.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_DeleteTable(t)
+ Hash_Table *t;
+{
+ register struct Hash_Entry **hp, *h, *nexth = NULL;
+ register int i;
+
+ for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
+ for (h = *hp++; h != NULL; h = nexth) {
+ nexth = h->next;
+ free((char *)h);
+ }
+ }
+ free((char *)t->bucketPtr);
+
+ /*
+ * Set up the hash table to cause memory faults on any future access
+ * attempts until re-initialization.
+ */
+ t->bucketPtr = NULL;
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_FindEntry --
+ *
+ * Searches a hash table for an entry corresponding to key.
+ *
+ * Results:
+ * The return value is a pointer to the entry for key,
+ * if key was present in the table. If key was not
+ * present, NULL is returned.
+ *
+ * Side Effects:
+ * None.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_FindEntry(t, key)
+ Hash_Table *t; /* Hash table to search. */
+ char *key; /* A hash key. */
+{
+ register Hash_Entry *e;
+ register unsigned h;
+ register char *p;
+
+ for (h = 0, p = key; *p;)
+ h = (h << 5) - h + *p++;
+ p = key;
+ for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next)
+ if (e->namehash == h && strcmp(e->name, p) == 0)
+ return (e);
+ return (NULL);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_CreateEntry --
+ *
+ * Searches a hash table for an entry corresponding to
+ * key. If no entry is found, then one is created.
+ *
+ * Results:
+ * The return value is a pointer to the entry. If *newPtr
+ * isn't NULL, then *newPtr is filled in with TRUE if a
+ * new entry was created, and FALSE if an entry already existed
+ * with the given key.
+ *
+ * Side Effects:
+ * Memory may be allocated, and the hash buckets may be modified.
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_CreateEntry(t, key, newPtr)
+ register Hash_Table *t; /* Hash table to search. */
+ char *key; /* A hash key. */
+ Boolean *newPtr; /* Filled in with TRUE if new entry created,
+ * FALSE otherwise. */
+{
+ register Hash_Entry *e;
+ register unsigned h;
+ register char *p;
+ int keylen;
+ struct Hash_Entry **hp;
+
+ /*
+ * Hash the key. As a side effect, save the length (strlen) of the
+ * key in case we need to create the entry.
+ */
+ for (h = 0, p = key; *p;)
+ h = (h << 5) - h + *p++;
+ keylen = p - key;
+ p = key;
+ for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) {
+ if (e->namehash == h && strcmp(e->name, p) == 0) {
+ if (newPtr != NULL)
+ *newPtr = FALSE;
+ return (e);
+ }
+ }
+
+ /*
+ * The desired entry isn't there. Before allocating a new entry,
+ * expand the table if necessary (and this changes the resulting
+ * bucket chain).
+ */
+ if (t->numEntries >= rebuildLimit * t->size)
+ RebuildTable(t);
+ e = (Hash_Entry *) emalloc(sizeof(*e) + keylen);
+ hp = &t->bucketPtr[h & t->mask];
+ e->next = *hp;
+ *hp = e;
+ e->clientData = NULL;
+ e->namehash = h;
+ (void) strcpy(e->name, p);
+ t->numEntries++;
+
+ if (newPtr != NULL)
+ *newPtr = TRUE;
+ return (e);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_DeleteEntry --
+ *
+ * Delete the given hash table entry and free memory associated with
+ * it.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Hash chain that entry lives in is modified and memory is freed.
+ *
+ *---------------------------------------------------------
+ */
+
+void
+Hash_DeleteEntry(t, e)
+ Hash_Table *t;
+ Hash_Entry *e;
+{
+ register Hash_Entry **hp, *p;
+
+ if (e == NULL)
+ return;
+ for (hp = &t->bucketPtr[e->namehash & t->mask];
+ (p = *hp) != NULL; hp = &p->next) {
+ if (p == e) {
+ *hp = p->next;
+ free((char *)p);
+ t->numEntries--;
+ return;
+ }
+ }
+ (void) write(2, "bad call to Hash_DeleteEntry\n", 29);
+ abort();
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_EnumFirst --
+ * This procedure sets things up for a complete search
+ * of all entries recorded in the hash table.
+ *
+ * Results:
+ * The return value is the address of the first entry in
+ * the hash table, or NULL if the table is empty.
+ *
+ * Side Effects:
+ * The information in searchPtr is initialized so that successive
+ * calls to Hash_Next will return successive HashEntry's
+ * from the table.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_EnumFirst(t, searchPtr)
+ Hash_Table *t; /* Table to be searched. */
+ register Hash_Search *searchPtr;/* Area in which to keep state
+ * about search.*/
+{
+ searchPtr->tablePtr = t;
+ searchPtr->nextIndex = 0;
+ searchPtr->hashEntryPtr = NULL;
+ return Hash_EnumNext(searchPtr);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * Hash_EnumNext --
+ * This procedure returns successive entries in the hash table.
+ *
+ * Results:
+ * The return value is a pointer to the next HashEntry
+ * in the table, or NULL when the end of the table is
+ * reached.
+ *
+ * Side Effects:
+ * The information in searchPtr is modified to advance to the
+ * next entry.
+ *
+ *---------------------------------------------------------
+ */
+
+Hash_Entry *
+Hash_EnumNext(searchPtr)
+ register Hash_Search *searchPtr; /* Area used to keep state about
+ search. */
+{
+ register Hash_Entry *e;
+ Hash_Table *t = searchPtr->tablePtr;
+
+ /*
+ * The hashEntryPtr field points to the most recently returned
+ * entry, or is nil if we are starting up. If not nil, we have
+ * to start at the next one in the chain.
+ */
+ e = searchPtr->hashEntryPtr;
+ if (e != NULL)
+ e = e->next;
+ /*
+ * If the chain ran out, or if we are starting up, we need to
+ * find the next nonempty chain.
+ */
+ while (e == NULL) {
+ if (searchPtr->nextIndex >= t->size)
+ return (NULL);
+ e = t->bucketPtr[searchPtr->nextIndex++];
+ }
+ searchPtr->hashEntryPtr = e;
+ return (e);
+}
+
+/*
+ *---------------------------------------------------------
+ *
+ * RebuildTable --
+ * This local routine makes a new hash table that
+ * is larger than the old one.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The entire hash table is moved, so any bucket numbers
+ * from the old table are invalid.
+ *
+ *---------------------------------------------------------
+ */
+
+static void
+RebuildTable(t)
+ register Hash_Table *t;
+{
+ register Hash_Entry *e, *next = NULL, **hp, **xp;
+ register int i, mask;
+ register Hash_Entry **oldhp;
+ int oldsize;
+
+ oldhp = t->bucketPtr;
+ oldsize = i = t->size;
+ i <<= 1;
+ t->size = i;
+ t->mask = mask = i - 1;
+ t->bucketPtr = hp = (struct Hash_Entry **) emalloc(sizeof(*hp) * i);
+ while (--i >= 0)
+ *hp++ = NULL;
+ for (hp = oldhp, i = oldsize; --i >= 0;) {
+ for (e = *hp++; e != NULL; e = next) {
+ next = e->next;
+ xp = &t->bucketPtr[e->namehash & mask];
+ e->next = *xp;
+ *xp = e;
+ }
+ }
+ free((char *)oldhp);
+}
diff --git a/usr.bin/make/hash.h b/usr.bin/make/hash.h
new file mode 100644
index 0000000..ebc30e1
--- /dev/null
+++ b/usr.bin/make/hash.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hash.h 8.1 (Berkeley) 6/6/93
+ */
+
+/* hash.h --
+ *
+ * This file contains definitions used by the hash module,
+ * which maintains hash tables.
+ */
+
+#ifndef _HASH
+#define _HASH
+
+/*
+ * The following defines one entry in the hash table.
+ */
+
+typedef struct Hash_Entry {
+ struct Hash_Entry *next; /* Used to link together all the
+ * entries associated with the same
+ * bucket. */
+ ClientData clientData; /* Arbitrary piece of data associated
+ * with key. */
+ unsigned namehash; /* hash value of key */
+ char name[1]; /* key string */
+} Hash_Entry;
+
+typedef struct Hash_Table {
+ struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one
+ * for each bucket in the table. */
+ int size; /* Actual size of array. */
+ int numEntries; /* Number of entries in the table. */
+ int mask; /* Used to select bits for hashing. */
+} Hash_Table;
+
+/*
+ * The following structure is used by the searching routines
+ * to record where we are in the search.
+ */
+
+typedef struct Hash_Search {
+ Hash_Table *tablePtr; /* Table being searched. */
+ int nextIndex; /* Next bucket to check (after current). */
+ Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */
+} Hash_Search;
+
+/*
+ * Macros.
+ */
+
+/*
+ * ClientData Hash_GetValue(h)
+ * Hash_Entry *h;
+ */
+
+#define Hash_GetValue(h) ((h)->clientData)
+
+/*
+ * Hash_SetValue(h, val);
+ * Hash_Entry *h;
+ * char *val;
+ */
+
+#define Hash_SetValue(h, val) ((h)->clientData = (ClientData) (val))
+
+/*
+ * Hash_Size(n) returns the number of words in an object of n bytes
+ */
+
+#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int))
+
+void Hash_InitTable __P((Hash_Table *, int));
+void Hash_DeleteTable __P((Hash_Table *));
+Hash_Entry *Hash_FindEntry __P((Hash_Table *, char *));
+Hash_Entry *Hash_CreateEntry __P((Hash_Table *, char *, Boolean *));
+void Hash_DeleteEntry __P((Hash_Table *, Hash_Entry *));
+Hash_Entry *Hash_EnumFirst __P((Hash_Table *, Hash_Search *));
+Hash_Entry *Hash_EnumNext __P((Hash_Search *));
+
+#endif /* _HASH */
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
new file mode 100644
index 0000000..ed5f2fc
--- /dev/null
+++ b/usr.bin/make/job.c
@@ -0,0 +1,2668 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * job.c --
+ * handle the creation etc. of our child processes.
+ *
+ * Interface:
+ * Job_Make Start the creation of the given target.
+ *
+ * Job_CatchChildren Check for and handle the termination of any
+ * children. This must be called reasonably
+ * frequently to keep the whole make going at
+ * a decent clip, since job table entries aren't
+ * removed until their process is caught this way.
+ * Its single argument is TRUE if the function
+ * should block waiting for a child to terminate.
+ *
+ * Job_CatchOutput Print any output our children have produced.
+ * Should also be called fairly frequently to
+ * keep the user informed of what's going on.
+ * If no output is waiting, it will block for
+ * a time given by the SEL_* constants, below,
+ * or until output is ready.
+ *
+ * Job_Init Called to intialize this module. in addition,
+ * any commands attached to the .BEGIN target
+ * are executed before this function returns.
+ * Hence, the makefile must have been parsed
+ * before this function is called.
+ *
+ * Job_Full Return TRUE if the job table is filled.
+ *
+ * Job_Empty Return TRUE if the job table is completely
+ * empty.
+ *
+ * Job_ParseShell Given the line following a .SHELL target, parse
+ * the line as a shell specification. Returns
+ * FAILURE if the spec was incorrect.
+ *
+ * Job_End Perform any final processing which needs doing.
+ * This includes the execution of any commands
+ * which have been/were attached to the .END
+ * target. It should only be called when the
+ * job table is empty.
+ *
+ * Job_AbortAll Abort all currently running jobs. It doesn't
+ * handle output or do anything for the jobs,
+ * just kills them. It should only be called in
+ * an emergency, as it were.
+ *
+ * Job_CheckCommands Verify that the commands for a target are
+ * ok. Provide them if necessary and possible.
+ *
+ * Job_Touch Update a target without really updating it.
+ *
+ * Job_Wait Wait for all currently-running jobs to finish.
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
+#include "pathnames.h"
+
+extern int errno;
+
+/*
+ * error handling variables
+ */
+static int errors = 0; /* number of errors reported */
+static int aborting = 0; /* why is the make aborting? */
+#define ABORT_ERROR 1 /* Because of an error */
+#define ABORT_INTERRUPT 2 /* Because it was interrupted */
+#define ABORT_WAIT 3 /* Waiting for jobs to finish */
+
+
+/*
+ * post-make command processing. The node postCommands is really just the
+ * .END target but we keep it around to avoid having to search for it
+ * all the time.
+ */
+static GNode *postCommands; /* node containing commands to execute when
+ * everything else is done */
+static int numCommands; /* The number of commands actually printed
+ * for a target. Should this number be
+ * 0, no shell will be executed. */
+
+
+/*
+ * Return values from JobStart.
+ */
+#define JOB_RUNNING 0 /* Job is running */
+#define JOB_ERROR 1 /* Error in starting the job */
+#define JOB_FINISHED 2 /* The job is already finished */
+#define JOB_STOPPED 3 /* The job is stopped */
+
+/*
+ * tfile is the name of a file into which all shell commands are put. It is
+ * used over by removing it before the child shell is executed. The XXXXX in
+ * the string are replaced by the pid of the make process in a 5-character
+ * field with leading zeroes.
+ */
+static char tfile[] = TMPPAT;
+
+
+/*
+ * Descriptions for various shells.
+ */
+static Shell shells[] = {
+ /*
+ * CSH description. The csh can do echo control by playing
+ * with the setting of the 'echo' shell variable. Sadly,
+ * however, it is unable to do error control nicely.
+ */
+{
+ "csh",
+ TRUE, "unset verbose", "set verbose", "unset verbose", 10,
+ FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"",
+ "v", "e",
+},
+ /*
+ * SH description. Echo control is also possible and, under
+ * sun UNIX anyway, one can even control error checking.
+ */
+{
+ "sh",
+ TRUE, "set -", "set -v", "set -", 5,
+ FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n",
+ "v", "e",
+},
+ /*
+ * UNKNOWN.
+ */
+{
+ (char *)0,
+ FALSE, (char *)0, (char *)0, (char *)0, 0,
+ FALSE, (char *)0, (char *)0,
+ (char *)0, (char *)0,
+}
+};
+static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to
+ * which we pass all
+ * commands in the Makefile.
+ * It is set by the
+ * Job_ParseShell function */
+static char *shellPath = (char *) NULL, /* full pathname of
+ * executable image */
+ *shellName; /* last component of shell */
+
+
+static int maxJobs; /* The most children we can run at once */
+static int maxLocal; /* The most local ones we can have */
+int nJobs; /* The number of children currently running */
+int nLocal; /* The number of local children */
+Lst jobs; /* The structures that describe them */
+Boolean jobFull; /* Flag to tell when the job table is full. It
+ * is set TRUE when (1) the total number of
+ * running jobs equals the maximum allowed or
+ * (2) a job can only be run locally, but
+ * nLocal equals maxLocal */
+#ifndef RMT_WILL_WATCH
+static fd_set outputs; /* Set of descriptors of pipes connected to
+ * the output channels of children */
+#endif
+
+GNode *lastNode; /* The node for which output was most recently
+ * produced. */
+char *targFmt; /* Format string to use to head output from a
+ * job when it's not the most-recent job heard
+ * from */
+#define TARG_FMT "--- %s ---\n" /* Default format */
+
+/*
+ * When JobStart attempts to run a job remotely but can't, and isn't allowed
+ * to run the job locally, or when Job_CatchChildren detects a job that has
+ * been migrated home, the job is placed on the stoppedJobs queue to be run
+ * when the next job finishes.
+ */
+Lst stoppedJobs; /* Lst of Job structures describing
+ * jobs that were stopped due to concurrency
+ * limits or migration home */
+
+
+#if defined(USE_PGRP) && defined(SYSV)
+#define KILL(pid,sig) killpg (-(pid),(sig))
+#else
+# if defined(USE_PGRP)
+#define KILL(pid,sig) killpg ((pid),(sig))
+# else
+#define KILL(pid,sig) kill ((pid),(sig))
+# endif
+#endif
+
+static int JobCondPassSig __P((ClientData, ClientData));
+static void JobPassSig __P((int));
+static int JobCmpPid __P((ClientData, ClientData));
+static int JobPrintCommand __P((ClientData, ClientData));
+static int JobSaveCommand __P((ClientData, ClientData));
+static void JobFinish __P((Job *, union wait));
+static void JobExec __P((Job *, char **));
+static void JobMakeArgv __P((Job *, char **));
+static void JobRestart __P((Job *));
+static int JobStart __P((GNode *, int, Job *));
+static void JobDoOutput __P((Job *, Boolean));
+static Shell *JobMatchShell __P((char *));
+static void JobInterrupt __P((int));
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobCondPassSig --
+ * Pass a signal to a job if the job is remote or if USE_PGRP
+ * is defined.
+ *
+ * Results:
+ * === 0
+ *
+ * Side Effects:
+ * None, except the job may bite it.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+JobCondPassSig(jobp, signop)
+ ClientData jobp; /* Job to biff */
+ ClientData signop; /* Signal to send it */
+{
+ Job *job = (Job *) jobp;
+ int signo = *(int *) signop;
+#ifdef RMT_WANTS_SIGNALS
+ if (job->flags & JOB_REMOTE) {
+ (void)Rmt_Signal(job, signo);
+ } else {
+ KILL(job->pid, signo);
+ }
+#else
+ /*
+ * Assume that sending the signal to job->pid will signal any remote
+ * job as well.
+ */
+ KILL(job->pid, signo);
+#endif
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobPassSig --
+ * Pass a signal on to all remote jobs and to all local jobs if
+ * USE_PGRP is defined, then die ourselves.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * We die by the same signal.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobPassSig(signo)
+ int signo; /* The signal number we've received */
+{
+ int mask;
+
+ Lst_ForEach(jobs, JobCondPassSig, (ClientData)(long)signo);
+
+ /*
+ * Deal with proper cleanup based on the signal received. We only run
+ * the .INTERRUPT target if the signal was in fact an interrupt. The other
+ * three termination signals are more of a "get out *now*" command.
+ */
+ if (signo == SIGINT) {
+ JobInterrupt(TRUE);
+ } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) {
+ JobInterrupt(FALSE);
+ }
+
+ /*
+ * Leave gracefully if SIGQUIT, rather than core dumping.
+ */
+ if (signo == SIGQUIT) {
+ Finish(0);
+ }
+
+ /*
+ * Send ourselves the signal now we've given the message to everyone else.
+ * Note we block everything else possible while we're getting the signal.
+ * This ensures that all our jobs get continued when we wake up before
+ * we take any other signal.
+ */
+ mask = sigblock(0);
+ (void) sigsetmask(~0 & ~(1 << (signo-1)));
+ signal(signo, SIG_DFL);
+
+ kill(getpid(), signo);
+
+ signo = SIGCONT;
+ Lst_ForEach(jobs, JobCondPassSig, (ClientData) &signo);
+
+ sigsetmask(mask);
+ signal(signo, JobPassSig);
+
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobCmpPid --
+ * Compare the pid of the job with the given pid and return 0 if they
+ * are equal. This function is called from Job_CatchChildren via
+ * Lst_Find to find the job descriptor of the finished job.
+ *
+ * Results:
+ * 0 if the pid's match
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static int
+JobCmpPid (job, pid)
+ ClientData job; /* job to examine */
+ ClientData pid; /* process id desired */
+{
+ return ( *(int *) pid - ((Job *) job)->pid);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobPrintCommand --
+ * Put out another command for the given job. If the command starts
+ * with an @ or a - we process it specially. In the former case,
+ * so long as the -s and -n flags weren't given to make, we stick
+ * a shell-specific echoOff command in the script. In the latter,
+ * we ignore errors for the entire job, unless the shell has error
+ * control.
+ * If the command is just "..." we take all future commands for this
+ * job to be commands to be executed once the entire graph has been
+ * made and return non-zero to signal that the end of the commands
+ * was reached. These commands are later attached to the postCommands
+ * node and executed by Job_End when all things are done.
+ * This function is called from JobStart via Lst_ForEach.
+ *
+ * Results:
+ * Always 0, unless the command was "..."
+ *
+ * Side Effects:
+ * If the command begins with a '-' and the shell has no error control,
+ * the JOB_IGNERR flag is set in the job descriptor.
+ * If the command is "..." and we're not ignoring such things,
+ * tailCmds is set to the successor node of the cmd.
+ * numCommands is incremented if the command is actually printed.
+ *-----------------------------------------------------------------------
+ */
+static int
+JobPrintCommand (cmdp, jobp)
+ ClientData cmdp; /* command string to print */
+ ClientData jobp; /* job for which to print it */
+{
+ Boolean noSpecials; /* true if we shouldn't worry about
+ * inserting special commands into
+ * the input stream. */
+ Boolean shutUp = FALSE; /* true if we put a no echo command
+ * into the command file */
+ Boolean errOff = FALSE; /* true if we turned error checking
+ * off before printing the command
+ * and need to turn it back on */
+ char *cmdTemplate; /* Template to use when printing the
+ * command */
+ char *cmdStart; /* Start of expanded command */
+ LstNode cmdNode; /* Node for replacing the command */
+ char *cmd = (char *) cmdp;
+ Job *job = (Job *) jobp;
+
+ noSpecials = (noExecute && ! (job->node->type & OP_MAKE));
+
+ if (strcmp (cmd, "...") == 0) {
+ job->node->type |= OP_SAVE_CMDS;
+ if ((job->flags & JOB_IGNDOTS) == 0) {
+ job->tailCmds = Lst_Succ (Lst_Member (job->node->commands,
+ (ClientData)cmd));
+ return (1);
+ }
+ return (0);
+ }
+
+#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg)
+
+ numCommands += 1;
+
+ /*
+ * For debugging, we replace each command with the result of expanding
+ * the variables in the command.
+ */
+ cmdNode = Lst_Member (job->node->commands, (ClientData)cmd);
+ cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE);
+ Lst_Replace (cmdNode, (ClientData)cmdStart);
+
+ cmdTemplate = "%s\n";
+
+ /*
+ * Check for leading @' and -'s to control echoing and error checking.
+ */
+ while (*cmd == '@' || *cmd == '-') {
+ if (*cmd == '@') {
+ shutUp = TRUE;
+ } else {
+ errOff = TRUE;
+ }
+ cmd++;
+ }
+
+ while (isspace((unsigned char) *cmd))
+ cmd++;
+
+ if (shutUp) {
+ if (! (job->flags & JOB_SILENT) && !noSpecials &&
+ commandShell->hasEchoCtl) {
+ DBPRINTF ("%s\n", commandShell->echoOff);
+ } else {
+ shutUp = FALSE;
+ }
+ }
+
+ if (errOff) {
+ if ( ! (job->flags & JOB_IGNERR) && !noSpecials) {
+ if (commandShell->hasErrCtl) {
+ /*
+ * we don't want the error-control commands showing
+ * up either, so we turn off echoing while executing
+ * them. We could put another field in the shell
+ * structure to tell JobDoOutput to look for this
+ * string too, but why make it any more complex than
+ * it already is?
+ */
+ if (! (job->flags & JOB_SILENT) && !shutUp &&
+ commandShell->hasEchoCtl) {
+ DBPRINTF ("%s\n", commandShell->echoOff);
+ DBPRINTF ("%s\n", commandShell->ignErr);
+ DBPRINTF ("%s\n", commandShell->echoOn);
+ } else {
+ DBPRINTF ("%s\n", commandShell->ignErr);
+ }
+ } else if (commandShell->ignErr &&
+ (*commandShell->ignErr != '\0'))
+ {
+ /*
+ * The shell has no error control, so we need to be
+ * weird to get it to ignore any errors from the command.
+ * If echoing is turned on, we turn it off and use the
+ * errCheck template to echo the command. Leave echoing
+ * off so the user doesn't see the weirdness we go through
+ * to ignore errors. Set cmdTemplate to use the weirdness
+ * instead of the simple "%s\n" template.
+ */
+ if (! (job->flags & JOB_SILENT) && !shutUp &&
+ commandShell->hasEchoCtl) {
+ DBPRINTF ("%s\n", commandShell->echoOff);
+ DBPRINTF (commandShell->errCheck, cmd);
+ shutUp = TRUE;
+ }
+ cmdTemplate = commandShell->ignErr;
+ /*
+ * The error ignoration (hee hee) is already taken care
+ * of by the ignErr template, so pretend error checking
+ * is still on.
+ */
+ errOff = FALSE;
+ } else {
+ errOff = FALSE;
+ }
+ } else {
+ errOff = FALSE;
+ }
+ }
+
+ DBPRINTF (cmdTemplate, cmd);
+
+ if (errOff) {
+ /*
+ * If echoing is already off, there's no point in issuing the
+ * echoOff command. Otherwise we issue it and pretend it was on
+ * for the whole command...
+ */
+ if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){
+ DBPRINTF ("%s\n", commandShell->echoOff);
+ shutUp = TRUE;
+ }
+ DBPRINTF ("%s\n", commandShell->errCheck);
+ }
+ if (shutUp) {
+ DBPRINTF ("%s\n", commandShell->echoOn);
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobSaveCommand --
+ * Save a command to be executed when everything else is done.
+ * Callback function for JobFinish...
+ *
+ * Results:
+ * Always returns 0
+ *
+ * Side Effects:
+ * The command is tacked onto the end of postCommands's commands list.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+JobSaveCommand (cmd, gn)
+ ClientData cmd;
+ ClientData gn;
+{
+ cmd = (ClientData) Var_Subst (NULL, (char *) cmd, (GNode *) gn, FALSE);
+ (void)Lst_AtEnd (postCommands->commands, cmd);
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobFinish --
+ * Do final processing for the given job including updating
+ * parents and starting new jobs as available/necessary. Note
+ * that we pay no attention to the JOB_IGNERR flag here.
+ * This is because when we're called because of a noexecute flag
+ * or something, jstat.w_status is 0 and when called from
+ * Job_CatchChildren, the status is zeroed if it s/b ignored.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Some nodes may be put on the toBeMade queue.
+ * Final commands for the job are placed on postCommands.
+ *
+ * If we got an error and are aborting (aborting == ABORT_ERROR) and
+ * the job list is now empty, we are done for the day.
+ * If we recognized an error (errors !=0), we set the aborting flag
+ * to ABORT_ERROR so no more jobs will be started.
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+JobFinish (job, status)
+ Job *job; /* job to finish */
+ union wait status; /* sub-why job went away */
+{
+ Boolean done;
+
+ if ((WIFEXITED(status) &&
+ (((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) ||
+ (WIFSIGNALED(status) && (status.w_termsig != SIGCONT)))
+ {
+ /*
+ * If it exited non-zero and either we're doing things our
+ * way or we're not ignoring errors, the job is finished.
+ * Similarly, if the shell died because of a signal
+ * the job is also finished. In these
+ * cases, finish out the job's output before printing the exit
+ * status...
+ */
+ if (usePipes) {
+#ifdef RMT_WILL_WATCH
+ Rmt_Ignore(job->inPipe);
+#else
+ FD_CLR(job->inPipe, &outputs);
+#endif /* RMT_WILL_WATCH */
+ if (job->outPipe != job->inPipe) {
+ (void)close (job->outPipe);
+ }
+ JobDoOutput (job, TRUE);
+ (void)close (job->inPipe);
+ } else {
+ (void)close (job->outFd);
+ JobDoOutput (job, TRUE);
+ }
+
+ if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
+ fclose(job->cmdFILE);
+ }
+ done = TRUE;
+ } else if (WIFEXITED(status) && status.w_retcode != 0) {
+ /*
+ * Deal with ignored errors in -B mode. We need to print a message
+ * telling of the ignored error as well as setting status.w_status
+ * to 0 so the next command gets run. To do this, we set done to be
+ * TRUE if in -B mode and the job exited non-zero. Note we don't
+ * want to close down any of the streams until we know we're at the
+ * end.
+ */
+ done = TRUE;
+ } else {
+ /*
+ * No need to close things down or anything.
+ */
+ done = FALSE;
+ }
+
+ if (done ||
+ WIFSTOPPED(status) ||
+ (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) ||
+ DEBUG(JOB))
+ {
+ FILE *out;
+
+ if (!usePipes && (job->flags & JOB_IGNERR)) {
+ /*
+ * If output is going to a file and this job is ignoring
+ * errors, arrange to have the exit status sent to the
+ * output file as well.
+ */
+ out = fdopen (job->outFd, "w");
+ } else {
+ out = stdout;
+ }
+
+ if (WIFEXITED(status)) {
+ if (status.w_retcode != 0) {
+ if (usePipes && job->node != lastNode) {
+ fprintf (out, targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ fprintf (out, "*** Error code %d%s\n", status.w_retcode,
+ (job->flags & JOB_IGNERR) ? " (ignored)" : "");
+
+ if (job->flags & JOB_IGNERR) {
+ status.w_status = 0;
+ }
+ } else if (DEBUG(JOB)) {
+ if (usePipes && job->node != lastNode) {
+ fprintf (out, targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ fprintf (out, "*** Completed successfully\n");
+ }
+ } else if (WIFSTOPPED(status)) {
+ if (usePipes && job->node != lastNode) {
+ fprintf (out, targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ if (! (job->flags & JOB_REMIGRATE)) {
+ fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig);
+ }
+ job->flags |= JOB_RESUME;
+ (void)Lst_AtEnd(stoppedJobs, (ClientData)job);
+ fflush(out);
+ return;
+ } else if (status.w_termsig == SIGCONT) {
+ /*
+ * If the beastie has continued, shift the Job from the stopped
+ * list to the running one (or re-stop it if concurrency is
+ * exceeded) and go and get another child.
+ */
+ if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) {
+ if (usePipes && job->node != lastNode) {
+ fprintf (out, targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ fprintf (out, "*** Continued\n");
+ }
+ if (! (job->flags & JOB_CONTINUING)) {
+ JobRestart(job);
+ } else {
+ Lst_AtEnd(jobs, (ClientData)job);
+ nJobs += 1;
+ if (! (job->flags & JOB_REMOTE)) {
+ nLocal += 1;
+ }
+ if (nJobs == maxJobs) {
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Job queue is full.\n");
+ }
+ }
+ }
+ fflush(out);
+ return;
+ } else {
+ if (usePipes && job->node != lastNode) {
+ fprintf (out, targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ fprintf (out, "*** Signal %d\n", status.w_termsig);
+ }
+
+ fflush (out);
+ }
+
+ /*
+ * Now handle the -B-mode stuff. If the beast still isn't finished,
+ * try and restart the job on the next command. If JobStart says it's
+ * ok, it's ok. If there's an error, this puppy is done.
+ */
+ if ((status.w_status == 0) &&
+ !Lst_IsAtEnd (job->node->commands))
+ {
+ switch (JobStart (job->node,
+ job->flags & JOB_IGNDOTS,
+ job))
+ {
+ case JOB_RUNNING:
+ done = FALSE;
+ break;
+ case JOB_ERROR:
+ done = TRUE;
+ status.w_retcode = 1;
+ break;
+ case JOB_FINISHED:
+ /*
+ * If we got back a JOB_FINISHED code, JobStart has already
+ * called Make_Update and freed the job descriptor. We set
+ * done to false here to avoid fake cycles and double frees.
+ * JobStart needs to do the update so we can proceed up the
+ * graph when given the -n flag..
+ */
+ done = FALSE;
+ break;
+ }
+ } else {
+ done = TRUE;
+ }
+
+
+ if (done &&
+ (aborting != ABORT_ERROR) &&
+ (aborting != ABORT_INTERRUPT) &&
+ (status.w_status == 0))
+ {
+ /*
+ * As long as we aren't aborting and the job didn't return a non-zero
+ * status that we shouldn't ignore, we call Make_Update to update
+ * the parents. In addition, any saved commands for the node are placed
+ * on the .END target.
+ */
+ if (job->tailCmds != NILLNODE) {
+ Lst_ForEachFrom (job->node->commands, job->tailCmds,
+ JobSaveCommand,
+ (ClientData)job->node);
+ }
+ job->node->made = MADE;
+ Make_Update (job->node);
+ free((Address)job);
+ } else if (status.w_status) {
+ errors += 1;
+ free((Address)job);
+ }
+
+ while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) {
+ JobRestart((Job *)Lst_DeQueue(stoppedJobs));
+ }
+
+ /*
+ * Set aborting if any error.
+ */
+ if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) {
+ /*
+ * If we found any errors in this batch of children and the -k flag
+ * wasn't given, we set the aborting flag so no more jobs get
+ * started.
+ */
+ aborting = ABORT_ERROR;
+ }
+
+ if ((aborting == ABORT_ERROR) && Job_Empty()) {
+ /*
+ * If we are aborting and the job table is now empty, we finish.
+ */
+ (void) unlink (tfile);
+ Finish (errors);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Touch --
+ * Touch the given target. Called by JobStart when the -t flag was
+ * given
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The data modification of the file is changed. In addition, if the
+ * file did not exist, it is created.
+ *-----------------------------------------------------------------------
+ */
+void
+Job_Touch (gn, silent)
+ GNode *gn; /* the node of the file to touch */
+ Boolean silent; /* TRUE if should not print messages */
+{
+ int streamID; /* ID of stream opened to do the touch */
+ struct timeval times[2]; /* Times for utimes() call */
+
+ if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) {
+ /*
+ * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets
+ * and, as such, shouldn't really be created.
+ */
+ return;
+ }
+
+ if (!silent) {
+ printf ("touch %s\n", gn->name);
+ }
+
+ if (noExecute) {
+ return;
+ }
+
+ if (gn->type & OP_ARCHV) {
+ Arch_Touch (gn);
+ } else if (gn->type & OP_LIB) {
+ Arch_TouchLib (gn);
+ } else {
+ char *file = gn->path ? gn->path : gn->name;
+
+ times[0].tv_sec = times[1].tv_sec = now;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ if (utimes(file, times) < 0){
+ streamID = open (file, O_RDWR | O_CREAT, 0666);
+
+ if (streamID >= 0) {
+ char c;
+
+ /*
+ * Read and write a byte to the file to change the
+ * modification time, then close the file.
+ */
+ if (read(streamID, &c, 1) == 1) {
+ lseek(streamID, 0L, L_SET);
+ write(streamID, &c, 1);
+ }
+
+ (void)close (streamID);
+ } else
+ printf("*** couldn't touch %s: %s", file, strerror(errno));
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_CheckCommands --
+ * Make sure the given node has all the commands it needs.
+ *
+ * Results:
+ * TRUE if the commands list is/was ok.
+ *
+ * Side Effects:
+ * The node will have commands from the .DEFAULT rule added to it
+ * if it needs them.
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Job_CheckCommands (gn, abortProc)
+ GNode *gn; /* The target whose commands need
+ * verifying */
+ void (*abortProc) __P((char *, ...));
+ /* Function to abort with message */
+{
+ if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) &&
+ (gn->type & OP_LIB) == 0) {
+ /*
+ * No commands. Look for .DEFAULT rule from which we might infer
+ * commands
+ */
+ if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) {
+ char *p1;
+ /*
+ * Make only looks for a .DEFAULT if the node was never the
+ * target of an operator, so that's what we do too. If
+ * a .DEFAULT was given, we substitute its commands for gn's
+ * commands and set the IMPSRC variable to be the target's name
+ * The DEFAULT node acts like a transformation rule, in that
+ * gn also inherits any attributes or sources attached to
+ * .DEFAULT itself.
+ */
+ Make_HandleUse(DEFAULT, gn);
+ Var_Set (IMPSRC, Var_Value (TARGET, gn, &p1), gn);
+ if (p1)
+ free(p1);
+ } else if (Dir_MTime (gn) == 0) {
+ /*
+ * The node wasn't the target of an operator we have no .DEFAULT
+ * rule to go on and the target doesn't already exist. There's
+ * nothing more we can do for this branch. If the -k flag wasn't
+ * given, we stop in our tracks, otherwise we just don't update
+ * this node's parents so they never get examined.
+ */
+ if (gn->type & OP_OPTIONAL) {
+ printf ("make: don't know how to make %s (ignored)\n",
+ gn->name);
+ } else if (keepgoing) {
+ printf ("make: don't know how to make %s (continuing)\n",
+ gn->name);
+ return (FALSE);
+ } else {
+ (*abortProc) ("make: don't know how to make %s. Stop",
+ gn->name);
+ return(FALSE);
+ }
+ }
+ }
+ return (TRUE);
+}
+#ifdef RMT_WILL_WATCH
+/*-
+ *-----------------------------------------------------------------------
+ * JobLocalInput --
+ * Handle a pipe becoming readable. Callback function for Rmt_Watch
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * JobDoOutput is called.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static void
+JobLocalInput(stream, job)
+ int stream; /* Stream that's ready (ignored) */
+ Job *job; /* Job to which the stream belongs */
+{
+ JobDoOutput(job, FALSE);
+}
+#endif /* RMT_WILL_WATCH */
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobExec --
+ * Execute the shell for the given job. Called from JobStart and
+ * JobRestart.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * A shell is executed, outputs is altered and the Job structure added
+ * to the job table.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobExec(job, argv)
+ Job *job; /* Job to execute */
+ char **argv;
+{
+ int cpid; /* ID of new child */
+
+ if (DEBUG(JOB)) {
+ int i;
+
+ printf("Running %s %sly\n", job->node->name,
+ job->flags&JOB_REMOTE?"remote":"local");
+ printf("\tCommand: ");
+ for (i = 0; argv[i] != (char *)NULL; i++) {
+ printf("%s ", argv[i]);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Some jobs produce no output and it's disconcerting to have
+ * no feedback of their running (since they produce no output, the
+ * banner with their name in it never appears). This is an attempt to
+ * provide that feedback, even if nothing follows it.
+ */
+ if ((lastNode != job->node) && (job->flags & JOB_FIRST) &&
+ !(job->flags & JOB_SILENT))
+ {
+ printf(targFmt, job->node->name);
+ lastNode = job->node;
+ }
+
+#ifdef RMT_NO_EXEC
+ if (job->flags & JOB_REMOTE) {
+ goto jobExecFinish;
+ }
+#endif /* RMT_NO_EXEC */
+
+ if ((cpid = vfork()) == -1) {
+ Punt ("Cannot fork");
+ } else if (cpid == 0) {
+
+ /*
+ * Must duplicate the input stream down to the child's input and
+ * reset it to the beginning (again). Since the stream was marked
+ * close-on-exec, we must clear that bit in the new input.
+ */
+ (void) dup2(fileno(job->cmdFILE), 0);
+ fcntl(0, F_SETFD, 0);
+ lseek(0, 0, L_SET);
+
+ if (usePipes) {
+ /*
+ * Set up the child's output to be routed through the pipe
+ * we've created for it.
+ */
+ (void) dup2 (job->outPipe, 1);
+ } else {
+ /*
+ * We're capturing output in a file, so we duplicate the
+ * descriptor to the temporary file into the standard
+ * output.
+ */
+ (void) dup2 (job->outFd, 1);
+ }
+ /*
+ * The output channels are marked close on exec. This bit was
+ * duplicated by the dup2 (on some systems), so we have to clear
+ * it before routing the shell's error output to the same place as
+ * its standard output.
+ */
+ fcntl(1, F_SETFD, 0);
+ (void) dup2 (1, 2);
+
+#ifdef USE_PGRP
+ /*
+ * We want to switch the child into a different process family so
+ * we can kill it and all its descendants in one fell swoop,
+ * by killing its process family, but not commit suicide.
+ */
+
+ (void) setpgrp(0, getpid());
+#endif USE_PGRP
+
+ (void) execv (shellPath, argv);
+ (void) write (2, "Could not execute shell\n",
+ sizeof ("Could not execute shell"));
+ _exit (1);
+ } else {
+ job->pid = cpid;
+
+ if (usePipes && (job->flags & JOB_FIRST) ) {
+ /*
+ * The first time a job is run for a node, we set the current
+ * position in the buffer to the beginning and mark another
+ * stream to watch in the outputs mask
+ */
+ job->curPos = 0;
+
+#ifdef RMT_WILL_WATCH
+ Rmt_Watch(job->inPipe, JobLocalInput, job);
+#else
+ FD_SET(job->inPipe, &outputs);
+#endif /* RMT_WILL_WATCH */
+ }
+
+ if (job->flags & JOB_REMOTE) {
+ job->rmtID = 0;
+ } else {
+ nLocal += 1;
+ /*
+ * XXX: Used to not happen if CUSTOMS. Why?
+ */
+ if (job->cmdFILE != stdout) {
+ fclose(job->cmdFILE);
+ job->cmdFILE = NULL;
+ }
+ }
+ }
+
+#ifdef RMT_NO_EXEC
+jobExecFinish:
+#endif
+ /*
+ * Now the job is actually running, add it to the table.
+ */
+ nJobs += 1;
+ (void)Lst_AtEnd (jobs, (ClientData)job);
+ if (nJobs == maxJobs) {
+ jobFull = TRUE;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobMakeArgv --
+ * Create the argv needed to execute the shell for a given job.
+ *
+ *
+ * Results:
+ *
+ * Side Effects:
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobMakeArgv(job, argv)
+ Job *job;
+ char **argv;
+{
+ int argc;
+ static char args[10]; /* For merged arguments */
+
+ argv[0] = shellName;
+ argc = 1;
+
+ if ((commandShell->exit && (*commandShell->exit != '-')) ||
+ (commandShell->echo && (*commandShell->echo != '-')))
+ {
+ /*
+ * At least one of the flags doesn't have a minus before it, so
+ * merge them together. Have to do this because the *(&(@*#*&#$#
+ * Bourne shell thinks its second argument is a file to source.
+ * Grrrr. Note the ten-character limitation on the combined arguments.
+ */
+ (void)sprintf(args, "-%s%s",
+ ((job->flags & JOB_IGNERR) ? "" :
+ (commandShell->exit ? commandShell->exit : "")),
+ ((job->flags & JOB_SILENT) ? "" :
+ (commandShell->echo ? commandShell->echo : "")));
+
+ if (args[1]) {
+ argv[argc] = args;
+ argc++;
+ }
+ } else {
+ if (!(job->flags & JOB_IGNERR) && commandShell->exit) {
+ argv[argc] = commandShell->exit;
+ argc++;
+ }
+ if (!(job->flags & JOB_SILENT) && commandShell->echo) {
+ argv[argc] = commandShell->echo;
+ argc++;
+ }
+ }
+ argv[argc] = (char *)NULL;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobRestart --
+ * Restart a job that stopped for some reason.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * jobFull will be set if the job couldn't be run.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+JobRestart(job)
+ Job *job; /* Job to restart */
+{
+ if (job->flags & JOB_REMIGRATE) {
+ if (DEBUG(JOB)) {
+ printf("Remigrating %x\n", job->pid);
+ }
+ if (nLocal != maxLocal) {
+ /*
+ * Job cannot be remigrated, but there's room on the local
+ * machine, so resume the job and note that another
+ * local job has started.
+ */
+ if (DEBUG(JOB)) {
+ printf("resuming on local machine\n");
+ }
+ KILL(job->pid, SIGCONT);
+ nLocal +=1;
+ job->flags &= ~(JOB_REMIGRATE|JOB_RESUME);
+ } else {
+ /*
+ * Job cannot be restarted. Mark the table as full and
+ * place the job back on the list of stopped jobs.
+ */
+ if (DEBUG(JOB)) {
+ printf("holding\n");
+ }
+ (void)Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Job queue is full.\n");
+ }
+ return;
+ }
+
+ (void)Lst_AtEnd(jobs, (ClientData)job);
+ nJobs += 1;
+ if (nJobs == maxJobs) {
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Job queue is full.\n");
+ }
+ }
+ } else if (job->flags & JOB_RESTART) {
+ /*
+ * Set up the control arguments to the shell. This is based on the
+ * flags set earlier for this job. If the JOB_IGNERR flag is clear,
+ * the 'exit' flag of the commandShell is used to cause it to exit
+ * upon receiving an error. If the JOB_SILENT flag is clear, the
+ * 'echo' flag of the commandShell is used to get it to start echoing
+ * as soon as it starts processing commands.
+ */
+ char *argv[4];
+
+ JobMakeArgv(job, argv);
+
+ if (DEBUG(JOB)) {
+ printf("Restarting %s...", job->node->name);
+ }
+ if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) {
+ /*
+ * Can't be exported and not allowed to run locally -- put it
+ * back on the hold queue and mark the table full
+ */
+ if (DEBUG(JOB)) {
+ printf("holding\n");
+ }
+ (void)Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Job queue is full.\n");
+ }
+ return;
+ } else {
+ /*
+ * Job may be run locally.
+ */
+ if (DEBUG(JOB)) {
+ printf("running locally\n");
+ }
+ job->flags &= ~JOB_REMOTE;
+ }
+ JobExec(job, argv);
+ } else {
+ /*
+ * The job has stopped and needs to be restarted. Why it stopped,
+ * we don't know...
+ */
+ if (DEBUG(JOB)) {
+ printf("Resuming %s...", job->node->name);
+ }
+ if (((job->flags & JOB_REMOTE) ||
+ (nLocal < maxLocal) ||
+ (((job->flags & JOB_SPECIAL)) &&
+ (maxLocal == 0))) &&
+ (nJobs != maxJobs))
+ {
+ /*
+ * If the job is remote, it's ok to resume it as long as the
+ * maximum concurrency won't be exceeded. If it's local and
+ * we haven't reached the local concurrency limit already (or the
+ * job must be run locally and maxLocal is 0), it's also ok to
+ * resume it.
+ */
+ Boolean error;
+ extern int errno;
+ union wait status;
+
+#ifdef RMT_WANTS_SIGNALS
+ if (job->flags & JOB_REMOTE) {
+ error = !Rmt_Signal(job, SIGCONT);
+ } else
+#endif /* RMT_WANTS_SIGNALS */
+ error = (KILL(job->pid, SIGCONT) != 0);
+
+ if (!error) {
+ /*
+ * Make sure the user knows we've continued the beast and
+ * actually put the thing in the job table.
+ */
+ job->flags |= JOB_CONTINUING;
+ status.w_termsig = SIGCONT;
+ JobFinish(job, status);
+
+ job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
+ if (DEBUG(JOB)) {
+ printf("done\n");
+ }
+ } else {
+ Error("couldn't resume %s: %s",
+ job->node->name, strerror(errno));
+ status.w_status = 0;
+ status.w_retcode = 1;
+ JobFinish(job, status);
+ }
+ } else {
+ /*
+ * Job cannot be restarted. Mark the table as full and
+ * place the job back on the list of stopped jobs.
+ */
+ if (DEBUG(JOB)) {
+ printf("table full\n");
+ }
+ (void)Lst_AtFront(stoppedJobs, (ClientData)job);
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Job queue is full.\n");
+ }
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobStart --
+ * Start a target-creation process going for the target described
+ * by the graph node gn.
+ *
+ * Results:
+ * JOB_ERROR if there was an error in the commands, JOB_FINISHED
+ * if there isn't actually anything left to do for the job and
+ * JOB_RUNNING if the job has been started.
+ *
+ * Side Effects:
+ * A new Job node is created and added to the list of running
+ * jobs. PMake is forked and a child shell created.
+ *-----------------------------------------------------------------------
+ */
+static int
+JobStart (gn, flags, previous)
+ GNode *gn; /* target to create */
+ int flags; /* flags for the job to override normal ones.
+ * e.g. JOB_SPECIAL or JOB_IGNDOTS */
+ Job *previous; /* The previous Job structure for this node,
+ * if any. */
+{
+ register Job *job; /* new job descriptor */
+ char *argv[4]; /* Argument vector to shell */
+ static int jobno = 0; /* job number of catching output in a file */
+ Boolean cmdsOK; /* true if the nodes commands were all right */
+ Boolean local; /* Set true if the job was run locally */
+ Boolean noExec; /* Set true if we decide not to run the job */
+
+ if (previous != (Job *)NULL) {
+ previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE);
+ job = previous;
+ } else {
+ job = (Job *) emalloc (sizeof (Job));
+ if (job == (Job *)NULL) {
+ Punt("JobStart out of memory");
+ }
+ flags |= JOB_FIRST;
+ }
+
+ job->node = gn;
+ job->tailCmds = NILLNODE;
+
+ /*
+ * Set the initial value of the flags for this job based on the global
+ * ones and the node's attributes... Any flags supplied by the caller
+ * are also added to the field.
+ */
+ job->flags = 0;
+ if (Targ_Ignore (gn)) {
+ job->flags |= JOB_IGNERR;
+ }
+ if (Targ_Silent (gn)) {
+ job->flags |= JOB_SILENT;
+ }
+ job->flags |= flags;
+
+ /*
+ * Check the commands now so any attributes from .DEFAULT have a chance
+ * to migrate to the node
+ */
+ if (job->flags & JOB_FIRST) {
+ cmdsOK = Job_CheckCommands(gn, Error);
+ } else {
+ cmdsOK = TRUE;
+ }
+
+ /*
+ * If the -n flag wasn't given, we open up OUR (not the child's)
+ * temporary file to stuff commands in it. The thing is rd/wr so we don't
+ * need to reopen it to feed it to the shell. If the -n flag *was* given,
+ * we just set the file to be stdout. Cute, huh?
+ */
+ if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
+ /*
+ * We're serious here, but if the commands were bogus, we're
+ * also dead...
+ */
+ if (!cmdsOK) {
+ DieHorribly();
+ }
+
+ job->cmdFILE = fopen (tfile, "w+");
+ if (job->cmdFILE == (FILE *) NULL) {
+ Punt ("Could not open %s", tfile);
+ }
+ fcntl(fileno(job->cmdFILE), F_SETFD, 1);
+ /*
+ * Send the commands to the command file, flush all its buffers then
+ * rewind and remove the thing.
+ */
+ noExec = FALSE;
+
+ /*
+ * used to be backwards; replace when start doing multiple commands
+ * per shell.
+ */
+ if (compatMake) {
+ /*
+ * Be compatible: If this is the first time for this node,
+ * verify its commands are ok and open the commands list for
+ * sequential access by later invocations of JobStart.
+ * Once that is done, we take the next command off the list
+ * and print it to the command file. If the command was an
+ * ellipsis, note that there's nothing more to execute.
+ */
+ if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){
+ cmdsOK = FALSE;
+ } else {
+ LstNode ln = Lst_Next (gn->commands);
+
+ if ((ln == NILLNODE) ||
+ JobPrintCommand ((char *)Lst_Datum (ln), job))
+ {
+ noExec = TRUE;
+ Lst_Close (gn->commands);
+ }
+ if (noExec && !(job->flags & JOB_FIRST)) {
+ /*
+ * If we're not going to execute anything, the job
+ * is done and we need to close down the various
+ * file descriptors we've opened for output, then
+ * call JobDoOutput to catch the final characters or
+ * send the file to the screen... Note that the i/o streams
+ * are only open if this isn't the first job.
+ * Note also that this could not be done in
+ * Job_CatchChildren b/c it wasn't clear if there were
+ * more commands to execute or not...
+ */
+ if (usePipes) {
+#ifdef RMT_WILL_WATCH
+ Rmt_Ignore(job->inPipe);
+#else
+ FD_CLR(job->inPipe, &outputs);
+#endif
+ if (job->outPipe != job->inPipe) {
+ (void)close (job->outPipe);
+ }
+ JobDoOutput (job, TRUE);
+ (void)close (job->inPipe);
+ } else {
+ (void)close (job->outFd);
+ JobDoOutput (job, TRUE);
+ }
+ }
+ }
+ } else {
+ /*
+ * We can do all the commands at once. hooray for sanity
+ */
+ numCommands = 0;
+ Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job);
+
+ /*
+ * If we didn't print out any commands to the shell script,
+ * there's not much point in executing the shell, is there?
+ */
+ if (numCommands == 0) {
+ noExec = TRUE;
+ }
+ }
+ } else if (noExecute) {
+ /*
+ * Not executing anything -- just print all the commands to stdout
+ * in one fell swoop. This will still set up job->tailCmds correctly.
+ */
+ if (lastNode != gn) {
+ printf (targFmt, gn->name);
+ lastNode = gn;
+ }
+ job->cmdFILE = stdout;
+ /*
+ * Only print the commands if they're ok, but don't die if they're
+ * not -- just let the user know they're bad and keep going. It
+ * doesn't do any harm in this case and may do some good.
+ */
+ if (cmdsOK) {
+ Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job);
+ }
+ /*
+ * Don't execute the shell, thank you.
+ */
+ noExec = TRUE;
+ } else {
+ /*
+ * Just touch the target and note that no shell should be executed.
+ * Set cmdFILE to stdout to make life easier. Check the commands, too,
+ * but don't die if they're no good -- it does no harm to keep working
+ * up the graph.
+ */
+ job->cmdFILE = stdout;
+ Job_Touch (gn, job->flags&JOB_SILENT);
+ noExec = TRUE;
+ }
+
+ /*
+ * If we're not supposed to execute a shell, don't.
+ */
+ if (noExec) {
+ /*
+ * Unlink and close the command file if we opened one
+ */
+ if (job->cmdFILE != stdout) {
+ (void) unlink (tfile);
+ fclose(job->cmdFILE);
+ } else {
+ fflush (stdout);
+ }
+
+ /*
+ * We only want to work our way up the graph if we aren't here because
+ * the commands for the job were no good.
+ */
+ if (cmdsOK) {
+ if (aborting == 0) {
+ if (job->tailCmds != NILLNODE) {
+ Lst_ForEachFrom(job->node->commands, job->tailCmds,
+ JobSaveCommand,
+ (ClientData)job->node);
+ }
+ Make_Update(job->node);
+ }
+ free((Address)job);
+ return(JOB_FINISHED);
+ } else {
+ free((Address)job);
+ return(JOB_ERROR);
+ }
+ } else {
+ fflush (job->cmdFILE);
+ (void) unlink (tfile);
+ }
+
+ /*
+ * Set up the control arguments to the shell. This is based on the flags
+ * set earlier for this job.
+ */
+ JobMakeArgv(job, argv);
+
+ /*
+ * If we're using pipes to catch output, create the pipe by which we'll
+ * get the shell's output. If we're using files, print out that we're
+ * starting a job and then set up its temporary-file name. This is just
+ * tfile with two extra digits tacked on -- jobno.
+ */
+ if (job->flags & JOB_FIRST) {
+ if (usePipes) {
+ int fd[2];
+ (void) pipe(fd);
+ job->inPipe = fd[0];
+ job->outPipe = fd[1];
+ (void)fcntl (job->inPipe, F_SETFD, 1);
+ (void)fcntl (job->outPipe, F_SETFD, 1);
+ } else {
+ printf ("Remaking `%s'\n", gn->name);
+ fflush (stdout);
+ sprintf (job->outFile, "%s%02d", tfile, jobno);
+ jobno = (jobno + 1) % 100;
+ job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600);
+ (void)fcntl (job->outFd, F_SETFD, 1);
+ }
+ }
+
+ local = TRUE;
+
+ if (local && (((nLocal >= maxLocal) &&
+ !(job->flags & JOB_SPECIAL) &&
+ (maxLocal != 0))))
+ {
+ /*
+ * The job can only be run locally, but we've hit the limit of
+ * local concurrency, so put the job on hold until some other job
+ * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END)
+ * may be run locally even when the local limit has been reached
+ * (e.g. when maxLocal == 0), though they will be exported if at
+ * all possible.
+ */
+ jobFull = TRUE;
+
+ if (DEBUG(JOB)) {
+ printf("Can only run job locally.\n");
+ }
+ job->flags |= JOB_RESTART;
+ (void)Lst_AtEnd(stoppedJobs, (ClientData)job);
+ } else {
+ if ((nLocal >= maxLocal) && local) {
+ /*
+ * If we're running this job locally as a special case (see above),
+ * at least say the table is full.
+ */
+ jobFull = TRUE;
+ if (DEBUG(JOB)) {
+ printf("Local job queue is full.\n");
+ }
+ }
+ JobExec(job, argv);
+ }
+ return(JOB_RUNNING);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobDoOutput --
+ * This function is called at different times depending on
+ * whether the user has specified that output is to be collected
+ * via pipes or temporary files. In the former case, we are called
+ * whenever there is something to read on the pipe. We collect more
+ * output from the given job and store it in the job's outBuf. If
+ * this makes up a line, we print it tagged by the job's identifier,
+ * as necessary.
+ * If output has been collected in a temporary file, we open the
+ * file and read it line by line, transfering it to our own
+ * output channel until the file is empty. At which point we
+ * remove the temporary file.
+ * In both cases, however, we keep our figurative eye out for the
+ * 'noPrint' line for the shell from which the output came. If
+ * we recognize a line, we don't print it. If the command is not
+ * alone on the line (the character after it is not \0 or \n), we
+ * do print whatever follows it.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * curPos may be shifted as may the contents of outBuf.
+ *-----------------------------------------------------------------------
+ */
+static void
+JobDoOutput (job, finish)
+ register Job *job; /* the job whose output needs printing */
+ Boolean finish; /* TRUE if this is the last time we'll be
+ * called for this job */
+{
+ Boolean gotNL = FALSE; /* true if got a newline */
+ register int nr; /* number of bytes read */
+ register int i; /* auxiliary index into outBuf */
+ register int max; /* limit for i (end of current data) */
+ int nRead; /* (Temporary) number of bytes read */
+
+ FILE *oFILE; /* Stream pointer to shell's output file */
+ char inLine[132];
+
+
+ if (usePipes) {
+ /*
+ * Read as many bytes as will fit in the buffer.
+ */
+end_loop:
+
+ nRead = read (job->inPipe, &job->outBuf[job->curPos],
+ JOB_BUFSIZE - job->curPos);
+ if (nRead < 0) {
+ if (DEBUG(JOB)) {
+ perror("JobDoOutput(piperead)");
+ }
+ nr = 0;
+ } else {
+ nr = nRead;
+ }
+
+ /*
+ * If we hit the end-of-file (the job is dead), we must flush its
+ * remaining output, so pretend we read a newline if there's any
+ * output remaining in the buffer.
+ * Also clear the 'finish' flag so we stop looping.
+ */
+ if ((nr == 0) && (job->curPos != 0)) {
+ job->outBuf[job->curPos] = '\n';
+ nr = 1;
+ finish = FALSE;
+ } else if (nr == 0) {
+ finish = FALSE;
+ }
+
+ /*
+ * Look for the last newline in the bytes we just got. If there is
+ * one, break out of the loop with 'i' as its index and gotNL set
+ * TRUE.
+ */
+ max = job->curPos + nr;
+ for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
+ if (job->outBuf[i] == '\n') {
+ gotNL = TRUE;
+ break;
+ } else if (job->outBuf[i] == '\0') {
+ /*
+ * Why?
+ */
+ job->outBuf[i] = ' ';
+ }
+ }
+
+ if (!gotNL) {
+ job->curPos += nr;
+ if (job->curPos == JOB_BUFSIZE) {
+ /*
+ * If we've run out of buffer space, we have no choice
+ * but to print the stuff. sigh.
+ */
+ gotNL = TRUE;
+ i = job->curPos;
+ }
+ }
+ if (gotNL) {
+ /*
+ * Need to send the output to the screen. Null terminate it
+ * first, overwriting the newline character if there was one.
+ * So long as the line isn't one we should filter (according
+ * to the shell description), we print the line, preceeded
+ * by a target banner if this target isn't the same as the
+ * one for which we last printed something.
+ * The rest of the data in the buffer are then shifted down
+ * to the start of the buffer and curPos is set accordingly.
+ */
+ job->outBuf[i] = '\0';
+ if (i >= job->curPos) {
+ register char *cp, *ecp;
+
+ cp = job->outBuf;
+ if (commandShell->noPrint) {
+ ecp = Str_FindSubstring(job->outBuf,
+ commandShell->noPrint);
+ while (ecp != (char *)NULL) {
+ if (cp != ecp) {
+ *ecp = '\0';
+ if (job->node != lastNode) {
+ printf (targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ /*
+ * The only way there wouldn't be a newline after
+ * this line is if it were the last in the buffer.
+ * however, since the non-printable comes after it,
+ * there must be a newline, so we don't print one.
+ */
+ printf ("%s", cp);
+ }
+ cp = ecp + commandShell->noPLen;
+ if (cp != &job->outBuf[i]) {
+ /*
+ * Still more to print, look again after skipping
+ * the whitespace following the non-printable
+ * command....
+ */
+ cp++;
+ while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
+ cp++;
+ }
+ ecp = Str_FindSubstring (cp,
+ commandShell->noPrint);
+ } else {
+ break;
+ }
+ }
+ }
+
+ /*
+ * There's still more in that thar buffer. This time, though,
+ * we know there's no newline at the end, so we add one of
+ * our own free will.
+ */
+ if (*cp != '\0') {
+ if (job->node != lastNode) {
+ printf (targFmt, job->node->name);
+ lastNode = job->node;
+ }
+ printf ("%s\n", cp);
+ }
+
+ fflush (stdout);
+ }
+ if (i < max - 1) {
+ /* shift the remaining characters down */
+ memcpy ( job->outBuf, &job->outBuf[i + 1], max - (i + 1));
+ job->curPos = max - (i + 1);
+
+ } else {
+ /*
+ * We have written everything out, so we just start over
+ * from the start of the buffer. No copying. No nothing.
+ */
+ job->curPos = 0;
+ }
+ }
+ if (finish) {
+ /*
+ * If the finish flag is true, we must loop until we hit
+ * end-of-file on the pipe. This is guaranteed to happen eventually
+ * since the other end of the pipe is now closed (we closed it
+ * explicitly and the child has exited). When we do get an EOF,
+ * finish will be set FALSE and we'll fall through and out.
+ */
+ goto end_loop;
+ }
+ } else {
+ /*
+ * We've been called to retrieve the output of the job from the
+ * temporary file where it's been squirreled away. This consists of
+ * opening the file, reading the output line by line, being sure not
+ * to print the noPrint line for the shell we used, then close and
+ * remove the temporary file. Very simple.
+ *
+ * Change to read in blocks and do FindSubString type things as for
+ * pipes? That would allow for "@echo -n..."
+ */
+ oFILE = fopen (job->outFile, "r");
+ if (oFILE != (FILE *) NULL) {
+ printf ("Results of making %s:\n", job->node->name);
+ while (fgets (inLine, sizeof(inLine), oFILE) != NULL) {
+ register char *cp, *ecp, *endp;
+
+ cp = inLine;
+ endp = inLine + strlen(inLine);
+ if (endp[-1] == '\n') {
+ *--endp = '\0';
+ }
+ if (commandShell->noPrint) {
+ ecp = Str_FindSubstring(cp, commandShell->noPrint);
+ while (ecp != (char *)NULL) {
+ if (cp != ecp) {
+ *ecp = '\0';
+ /*
+ * The only way there wouldn't be a newline after
+ * this line is if it were the last in the buffer.
+ * however, since the non-printable comes after it,
+ * there must be a newline, so we don't print one.
+ */
+ printf ("%s", cp);
+ }
+ cp = ecp + commandShell->noPLen;
+ if (cp != endp) {
+ /*
+ * Still more to print, look again after skipping
+ * the whitespace following the non-printable
+ * command....
+ */
+ cp++;
+ while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
+ cp++;
+ }
+ ecp = Str_FindSubstring(cp, commandShell->noPrint);
+ } else {
+ break;
+ }
+ }
+ }
+
+ /*
+ * There's still more in that thar buffer. This time, though,
+ * we know there's no newline at the end, so we add one of
+ * our own free will.
+ */
+ if (*cp != '\0') {
+ printf ("%s\n", cp);
+ }
+ }
+ fclose (oFILE);
+ (void) unlink (job->outFile);
+ }
+ }
+ fflush(stdout);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_CatchChildren --
+ * Handle the exit of a child. Called from Make_Make.
+ *
+ * Results:
+ * none.
+ *
+ * Side Effects:
+ * The job descriptor is removed from the list of children.
+ *
+ * Notes:
+ * We do waits, blocking or not, according to the wisdom of our
+ * caller, until there are no more children to report. For each
+ * job, call JobFinish to finish things off. This will take care of
+ * putting jobs on the stoppedJobs queue.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Job_CatchChildren (block)
+ Boolean block; /* TRUE if should block on the wait. */
+{
+ int pid; /* pid of dead child */
+ register Job *job; /* job descriptor for dead child */
+ LstNode jnode; /* list element for finding job */
+ union wait status; /* Exit/termination status */
+
+ /*
+ * Don't even bother if we know there's no one around.
+ */
+ if (nLocal == 0) {
+ return;
+ }
+
+ while ((pid = wait3((int *)&status, (block?0:WNOHANG)|WUNTRACED,
+ (struct rusage *)0)) > 0)
+ {
+ if (DEBUG(JOB))
+ printf("Process %d exited or stopped.\n", pid);
+
+
+ jnode = Lst_Find (jobs, (ClientData)&pid, JobCmpPid);
+
+ if (jnode == NILLNODE) {
+ if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) {
+ jnode = Lst_Find(stoppedJobs, (ClientData) &pid, JobCmpPid);
+ if (jnode == NILLNODE) {
+ Error("Resumed child (%d) not in table", pid);
+ continue;
+ }
+ job = (Job *)Lst_Datum(jnode);
+ (void)Lst_Remove(stoppedJobs, jnode);
+ } else {
+ Error ("Child (%d) not in table?", pid);
+ continue;
+ }
+ } else {
+ job = (Job *) Lst_Datum (jnode);
+ (void)Lst_Remove (jobs, jnode);
+ nJobs -= 1;
+ if (jobFull && DEBUG(JOB)) {
+ printf("Job queue is no longer full.\n");
+ }
+ jobFull = FALSE;
+ nLocal -= 1;
+ }
+
+ JobFinish (job, status);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_CatchOutput --
+ * Catch the output from our children, if we're using
+ * pipes do so. Otherwise just block time until we get a
+ * signal (most likely a SIGCHLD) since there's no point in
+ * just spinning when there's nothing to do and the reaping
+ * of a child can wait for a while.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Output is read from pipes if we're piping.
+ * -----------------------------------------------------------------------
+ */
+void
+Job_CatchOutput ()
+{
+ int nfds;
+ struct timeval timeout;
+ fd_set readfds;
+ register LstNode ln;
+ register Job *job;
+#ifdef RMT_WILL_WATCH
+ int pnJobs; /* Previous nJobs */
+#endif
+
+ fflush(stdout);
+#ifdef RMT_WILL_WATCH
+ pnJobs = nJobs;
+
+ /*
+ * It is possible for us to be called with nJobs equal to 0. This happens
+ * if all the jobs finish and a job that is stopped cannot be run
+ * locally (eg if maxLocal is 0) and cannot be exported. The job will
+ * be placed back on the stoppedJobs queue, Job_Empty() will return false,
+ * Make_Run will call us again when there's nothing for which to wait.
+ * nJobs never changes, so we loop forever. Hence the check. It could
+ * be argued that we should sleep for a bit so as not to swamp the
+ * exportation system with requests. Perhaps we should.
+ *
+ * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren
+ * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT.
+ * It may use the variable nLocal to determine if it needs to call
+ * Job_CatchChildren (if nLocal is 0, there's nothing for which to
+ * wait...)
+ */
+ while (nJobs != 0 && pnJobs == nJobs) {
+ Rmt_Wait();
+ }
+#else
+ if (usePipes) {
+ readfds = outputs;
+ timeout.tv_sec = SEL_SEC;
+ timeout.tv_usec = SEL_USEC;
+
+ if ((nfds = select (FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout)) < 0)
+ {
+ return;
+ } else {
+ if (Lst_Open (jobs) == FAILURE) {
+ Punt ("Cannot open job table");
+ }
+ while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) {
+ job = (Job *) Lst_Datum (ln);
+ if (FD_ISSET(job->inPipe, &readfds)) {
+ JobDoOutput (job, FALSE);
+ nfds -= 1;
+ }
+ }
+ Lst_Close (jobs);
+ }
+ }
+#endif /* RMT_WILL_WATCH */
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Make --
+ * Start the creation of a target. Basically a front-end for
+ * JobStart used by the Make module.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Another job is started.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Job_Make (gn)
+ GNode *gn;
+{
+ (void)JobStart (gn, 0, (Job *)NULL);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Init --
+ * Initialize the process module
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * lists and counters are initialized
+ *-----------------------------------------------------------------------
+ */
+void
+Job_Init (maxproc, maxlocal)
+ int maxproc; /* the greatest number of jobs which may be
+ * running at one time */
+ int maxlocal; /* the greatest number of local jobs which may
+ * be running at once. */
+{
+ GNode *begin; /* node for commands to do at the very start */
+
+ sprintf (tfile, "/tmp/make%05d", getpid());
+
+ jobs = Lst_Init (FALSE);
+ stoppedJobs = Lst_Init(FALSE);
+ maxJobs = maxproc;
+ maxLocal = maxlocal;
+ nJobs = 0;
+ nLocal = 0;
+ jobFull = FALSE;
+
+ aborting = 0;
+ errors = 0;
+
+ lastNode = NILGNODE;
+
+ if (maxJobs == 1) {
+ /*
+ * If only one job can run at a time, there's no need for a banner,
+ * no is there?
+ */
+ targFmt = "";
+ } else {
+ targFmt = TARG_FMT;
+ }
+
+ if (shellPath == (char *) NULL) {
+ /*
+ * The user didn't specify a shell to use, so we are using the
+ * default one... Both the absolute path and the last component
+ * must be set. The last component is taken from the 'name' field
+ * of the default shell description pointed-to by commandShell.
+ * All default shells are located in _PATH_DEFSHELLDIR.
+ */
+ shellName = commandShell->name;
+ shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH);
+ }
+
+ if (commandShell->exit == (char *)NULL) {
+ commandShell->exit = "";
+ }
+ if (commandShell->echo == (char *)NULL) {
+ commandShell->echo = "";
+ }
+
+ /*
+ * Catch the four signals that POSIX specifies if they aren't ignored.
+ * JobPassSig will take care of calling JobInterrupt if appropriate.
+ */
+ if (signal (SIGINT, SIG_IGN) != SIG_IGN) {
+ signal (SIGINT, JobPassSig);
+ }
+ if (signal (SIGHUP, SIG_IGN) != SIG_IGN) {
+ signal (SIGHUP, JobPassSig);
+ }
+ if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) {
+ signal (SIGQUIT, JobPassSig);
+ }
+ if (signal (SIGTERM, SIG_IGN) != SIG_IGN) {
+ signal (SIGTERM, JobPassSig);
+ }
+ /*
+ * There are additional signals that need to be caught and passed if
+ * either the export system wants to be told directly of signals or if
+ * we're giving each job its own process group (since then it won't get
+ * signals from the terminal driver as we own the terminal)
+ */
+#if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP)
+ if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) {
+ signal (SIGTSTP, JobPassSig);
+ }
+ if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) {
+ signal (SIGTTOU, JobPassSig);
+ }
+ if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) {
+ signal (SIGTTIN, JobPassSig);
+ }
+ if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) {
+ signal (SIGWINCH, JobPassSig);
+ }
+#endif
+
+ begin = Targ_FindNode (".BEGIN", TARG_NOCREATE);
+
+ if (begin != NILGNODE) {
+ JobStart (begin, JOB_SPECIAL, (Job *)0);
+ while (nJobs) {
+ Job_CatchOutput();
+#ifndef RMT_WILL_WATCH
+ Job_CatchChildren (!usePipes);
+#endif /* RMT_WILL_WATCH */
+ }
+ }
+ postCommands = Targ_FindNode (".END", TARG_CREATE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Full --
+ * See if the job table is full. It is considered full if it is OR
+ * if we are in the process of aborting OR if we have
+ * reached/exceeded our local quota. This prevents any more jobs
+ * from starting up.
+ *
+ * Results:
+ * TRUE if the job table is full, FALSE otherwise
+ * Side Effects:
+ * None.
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Job_Full ()
+{
+ return (aborting || jobFull);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Empty --
+ * See if the job table is empty. Because the local concurrency may
+ * be set to 0, it is possible for the job table to become empty,
+ * while the list of stoppedJobs remains non-empty. In such a case,
+ * we want to restart as many jobs as we can.
+ *
+ * Results:
+ * TRUE if it is. FALSE if it ain't.
+ *
+ * Side Effects:
+ * None.
+ *
+ * -----------------------------------------------------------------------
+ */
+Boolean
+Job_Empty ()
+{
+ if (nJobs == 0) {
+ if (!Lst_IsEmpty(stoppedJobs) && !aborting) {
+ /*
+ * The job table is obviously not full if it has no jobs in
+ * it...Try and restart the stopped jobs.
+ */
+ jobFull = FALSE;
+ while (!jobFull && !Lst_IsEmpty(stoppedJobs)) {
+ JobRestart((Job *)Lst_DeQueue(stoppedJobs));
+ }
+ return(FALSE);
+ } else {
+ return(TRUE);
+ }
+ } else {
+ return(FALSE);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobMatchShell --
+ * Find a matching shell in 'shells' given its final component.
+ *
+ * Results:
+ * A pointer to the Shell structure.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Shell *
+JobMatchShell (name)
+ char *name; /* Final component of shell path */
+{
+ register Shell *sh; /* Pointer into shells table */
+ Shell *match; /* Longest-matching shell */
+ register char *cp1,
+ *cp2;
+ char *eoname;
+
+ eoname = name + strlen (name);
+
+ match = (Shell *) NULL;
+
+ for (sh = shells; sh->name != NULL; sh++) {
+ for (cp1 = eoname - strlen (sh->name), cp2 = sh->name;
+ *cp1 != '\0' && *cp1 == *cp2;
+ cp1++, cp2++) {
+ continue;
+ }
+ if (*cp1 != *cp2) {
+ continue;
+ } else if (match == (Shell *) NULL ||
+ strlen (match->name) < strlen (sh->name)) {
+ match = sh;
+ }
+ }
+ return (match == (Shell *) NULL ? sh : match);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_ParseShell --
+ * Parse a shell specification and set up commandShell, shellPath
+ * and shellName appropriately.
+ *
+ * Results:
+ * FAILURE if the specification was incorrect.
+ *
+ * Side Effects:
+ * commandShell points to a Shell structure (either predefined or
+ * created from the shell spec), shellPath is the full path of the
+ * shell described by commandShell, while shellName is just the
+ * final component of shellPath.
+ *
+ * Notes:
+ * A shell specification consists of a .SHELL target, with dependency
+ * operator, followed by a series of blank-separated words. Double
+ * quotes can be used to use blanks in words. A backslash escapes
+ * anything (most notably a double-quote and a space) and
+ * provides the functionality it does in C. Each word consists of
+ * keyword and value separated by an equal sign. There should be no
+ * unnecessary spaces in the word. The keywords are as follows:
+ * name Name of shell.
+ * path Location of shell. Overrides "name" if given
+ * quiet Command to turn off echoing.
+ * echo Command to turn echoing on
+ * filter Result of turning off echoing that shouldn't be
+ * printed.
+ * echoFlag Flag to turn echoing on at the start
+ * errFlag Flag to turn error checking on at the start
+ * hasErrCtl True if shell has error checking control
+ * check Command to turn on error checking if hasErrCtl
+ * is TRUE or template of command to echo a command
+ * for which error checking is off if hasErrCtl is
+ * FALSE.
+ * ignore Command to turn off error checking if hasErrCtl
+ * is TRUE or template of command to execute a
+ * command so as to ignore any errors it returns if
+ * hasErrCtl is FALSE.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Job_ParseShell (line)
+ char *line; /* The shell spec */
+{
+ char **words;
+ int wordCount;
+ register char **argv;
+ register int argc;
+ char *path;
+ Shell newShell;
+ Boolean fullSpec = FALSE;
+
+ while (isspace (*line)) {
+ line++;
+ }
+ words = brk_string (line, &wordCount, TRUE);
+
+ memset ((Address)&newShell, 0, sizeof(newShell));
+
+ /*
+ * Parse the specification by keyword
+ */
+ for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1;
+ argc != 0;
+ argc--, argv++) {
+ if (strncmp (*argv, "path=", 5) == 0) {
+ path = &argv[0][5];
+ } else if (strncmp (*argv, "name=", 5) == 0) {
+ newShell.name = &argv[0][5];
+ } else {
+ if (strncmp (*argv, "quiet=", 6) == 0) {
+ newShell.echoOff = &argv[0][6];
+ } else if (strncmp (*argv, "echo=", 5) == 0) {
+ newShell.echoOn = &argv[0][5];
+ } else if (strncmp (*argv, "filter=", 7) == 0) {
+ newShell.noPrint = &argv[0][7];
+ newShell.noPLen = strlen(newShell.noPrint);
+ } else if (strncmp (*argv, "echoFlag=", 9) == 0) {
+ newShell.echo = &argv[0][9];
+ } else if (strncmp (*argv, "errFlag=", 8) == 0) {
+ newShell.exit = &argv[0][8];
+ } else if (strncmp (*argv, "hasErrCtl=", 10) == 0) {
+ char c = argv[0][10];
+ newShell.hasErrCtl = !((c != 'Y') && (c != 'y') &&
+ (c != 'T') && (c != 't'));
+ } else if (strncmp (*argv, "check=", 6) == 0) {
+ newShell.errCheck = &argv[0][6];
+ } else if (strncmp (*argv, "ignore=", 7) == 0) {
+ newShell.ignErr = &argv[0][7];
+ } else {
+ Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"",
+ *argv);
+ return (FAILURE);
+ }
+ fullSpec = TRUE;
+ }
+ }
+
+ if (path == (char *)NULL) {
+ /*
+ * If no path was given, the user wants one of the pre-defined shells,
+ * yes? So we find the one s/he wants with the help of JobMatchShell
+ * and set things up the right way. shellPath will be set up by
+ * Job_Init.
+ */
+ if (newShell.name == (char *)NULL) {
+ Parse_Error (PARSE_FATAL, "Neither path nor name specified");
+ return (FAILURE);
+ } else {
+ commandShell = JobMatchShell (newShell.name);
+ shellName = newShell.name;
+ }
+ } else {
+ /*
+ * The user provided a path. If s/he gave nothing else (fullSpec is
+ * FALSE), try and find a matching shell in the ones we know of.
+ * Else we just take the specification at its word and copy it
+ * to a new location. In either case, we need to record the
+ * path the user gave for the shell.
+ */
+ shellPath = path;
+ path = strrchr (path, '/');
+ if (path == (char *)NULL) {
+ path = shellPath;
+ } else {
+ path += 1;
+ }
+ if (newShell.name != (char *)NULL) {
+ shellName = newShell.name;
+ } else {
+ shellName = path;
+ }
+ if (!fullSpec) {
+ commandShell = JobMatchShell (shellName);
+ } else {
+ commandShell = (Shell *) emalloc(sizeof(Shell));
+ *commandShell = newShell;
+ }
+ }
+
+ if (commandShell->echoOn && commandShell->echoOff) {
+ commandShell->hasEchoCtl = TRUE;
+ }
+
+ if (!commandShell->hasErrCtl) {
+ if (commandShell->errCheck == (char *)NULL) {
+ commandShell->errCheck = "";
+ }
+ if (commandShell->ignErr == (char *)NULL) {
+ commandShell->ignErr = "%s\n";
+ }
+ }
+
+ /*
+ * Do not free up the words themselves, since they might be in use by the
+ * shell specification...
+ */
+ free (words);
+ return SUCCESS;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * JobInterrupt --
+ * Handle the receipt of an interrupt.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * All children are killed. Another job will be started if the
+ * .INTERRUPT target was given.
+ *-----------------------------------------------------------------------
+ */
+static void
+JobInterrupt (runINTERRUPT)
+ int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT
+ * target should be executed */
+{
+ LstNode ln; /* element in job table */
+ Job *job; /* job descriptor in that element */
+ GNode *interrupt; /* the node describing the .INTERRUPT target */
+
+ aborting = ABORT_INTERRUPT;
+
+ (void)Lst_Open (jobs);
+ while ((ln = Lst_Next (jobs)) != NILLNODE) {
+ job = (Job *) Lst_Datum (ln);
+
+ if (!Targ_Precious (job->node)) {
+ char *file = (job->node->path == (char *)NULL ?
+ job->node->name :
+ job->node->path);
+ struct stat st;
+ if (!noExecute && lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) &&
+ unlink(file) != -1) {
+ Error ("*** %s removed", file);
+ }
+ }
+#ifdef RMT_WANTS_SIGNALS
+ if (job->flags & JOB_REMOTE) {
+ /*
+ * If job is remote, let the Rmt module do the killing.
+ */
+ if (!Rmt_Signal(job, SIGINT)) {
+ /*
+ * If couldn't kill the thing, finish it out now with an
+ * error code, since no exit report will come in likely.
+ */
+ union wait status;
+
+ status.w_status = 0;
+ status.w_retcode = 1;
+ JobFinish(job, status);
+ }
+ } else if (job->pid) {
+ KILL(job->pid, SIGINT);
+ }
+#else
+ if (job->pid) {
+ KILL(job->pid, SIGINT);
+ }
+#endif /* RMT_WANTS_SIGNALS */
+ }
+ Lst_Close (jobs);
+
+ if (runINTERRUPT && !touchFlag) {
+ interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE);
+ if (interrupt != NILGNODE) {
+ ignoreErrors = FALSE;
+
+ JobStart (interrupt, JOB_IGNDOTS, (Job *)0);
+ while (nJobs) {
+ Job_CatchOutput();
+#ifndef RMT_WILL_WATCH
+ Job_CatchChildren (!usePipes);
+#endif /* RMT_WILL_WATCH */
+ }
+ }
+ }
+ (void) unlink (tfile);
+ exit (0);
+}
+
+/*
+ *-----------------------------------------------------------------------
+ * Job_End --
+ * Do final processing such as the running of the commands
+ * attached to the .END target.
+ *
+ * Results:
+ * Number of errors reported.
+ *
+ * Side Effects:
+ * The process' temporary file (tfile) is removed if it still
+ * existed.
+ *-----------------------------------------------------------------------
+ */
+int
+Job_End ()
+{
+ if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) {
+ if (errors) {
+ Error ("Errors reported so .END ignored");
+ } else {
+ JobStart (postCommands, JOB_SPECIAL | JOB_IGNDOTS,
+ (Job *)0);
+
+ while (nJobs) {
+ Job_CatchOutput();
+#ifndef RMT_WILL_WATCH
+ Job_CatchChildren (!usePipes);
+#endif /* RMT_WILL_WATCH */
+ }
+ }
+ }
+ (void) unlink (tfile);
+ return(errors);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_Wait --
+ * Waits for all running jobs to finish and returns. Sets 'aborting'
+ * to ABORT_WAIT to prevent other jobs from starting.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Currently running jobs finish.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Job_Wait()
+{
+ aborting = ABORT_WAIT;
+ while (nJobs != 0) {
+ Job_CatchOutput();
+#ifndef RMT_WILL_WATCH
+ Job_CatchChildren(!usePipes);
+#endif /* RMT_WILL_WATCH */
+ }
+ aborting = 0;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Job_AbortAll --
+ * Abort all currently running jobs without handling output or anything.
+ * This function is to be called only in the event of a major
+ * error. Most definitely NOT to be called from JobInterrupt.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * All children are killed, not just the firstborn
+ *-----------------------------------------------------------------------
+ */
+void
+Job_AbortAll ()
+{
+ LstNode ln; /* element in job table */
+ Job *job; /* the job descriptor in that element */
+ int foo;
+
+ aborting = ABORT_ERROR;
+
+ if (nJobs) {
+
+ (void)Lst_Open (jobs);
+ while ((ln = Lst_Next (jobs)) != NILLNODE) {
+ job = (Job *) Lst_Datum (ln);
+
+ /*
+ * kill the child process with increasingly drastic signals to make
+ * darn sure it's dead.
+ */
+#ifdef RMT_WANTS_SIGNALS
+ if (job->flags & JOB_REMOTE) {
+ Rmt_Signal(job, SIGINT);
+ Rmt_Signal(job, SIGKILL);
+ } else {
+ KILL(job->pid, SIGINT);
+ KILL(job->pid, SIGKILL);
+ }
+#else
+ KILL(job->pid, SIGINT);
+ KILL(job->pid, SIGKILL);
+#endif /* RMT_WANTS_SIGNALS */
+ }
+ }
+
+ /*
+ * Catch as many children as want to report in at first, then give up
+ */
+ while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0)
+ continue;
+ (void) unlink (tfile);
+}
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
new file mode 100644
index 0000000..d16986b
--- /dev/null
+++ b/usr.bin/make/job.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)job.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*-
+ * job.h --
+ * Definitions pertaining to the running of jobs in parallel mode.
+ * Exported from job.c for the use of remote-execution modules.
+ */
+#ifndef _JOB_H_
+#define _JOB_H_
+
+#define TMPPAT "/tmp/makeXXXXX"
+
+/*
+ * The SEL_ constants determine the maximum amount of time spent in select
+ * before coming out to see if a child has finished. SEL_SEC is the number of
+ * seconds and SEL_USEC is the number of micro-seconds
+ */
+#define SEL_SEC 0
+#define SEL_USEC 500000
+
+
+/*-
+ * Job Table definitions.
+ *
+ * Each job has several things associated with it:
+ * 1) The process id of the child shell
+ * 2) The graph node describing the target being made by this job
+ * 3) A LstNode for the first command to be saved after the job
+ * completes. This is NILLNODE if there was no "..." in the job's
+ * commands.
+ * 4) An FILE* for writing out the commands. This is only
+ * used before the job is actually started.
+ * 5) A union of things used for handling the shell's output. Different
+ * parts of the union are used based on the value of the usePipes
+ * flag. If it is true, the output is being caught via a pipe and
+ * the descriptors of our pipe, an array in which output is line
+ * buffered and the current position in that buffer are all
+ * maintained for each job. If, on the other hand, usePipes is false,
+ * the output is routed to a temporary file and all that is kept
+ * is the name of the file and the descriptor open to the file.
+ * 6) An identifier provided by and for the exclusive use of the
+ * Rmt module.
+ * 7) A word of flags which determine how the module handles errors,
+ * echoing, etc. for the job
+ *
+ * The job "table" is kept as a linked Lst in 'jobs', with the number of
+ * active jobs maintained in the 'nJobs' variable. At no time will this
+ * exceed the value of 'maxJobs', initialized by the Job_Init function.
+ *
+ * When a job is finished, the Make_Update function is called on each of the
+ * parents of the node which was just remade. This takes care of the upward
+ * traversal of the dependency graph.
+ */
+#define JOB_BUFSIZE 1024
+typedef struct Job {
+ int pid; /* The child's process ID */
+ GNode *node; /* The target the child is making */
+ LstNode tailCmds; /* The node of the first command to be
+ * saved when the job has been run */
+ FILE *cmdFILE; /* When creating the shell script, this is
+ * where the commands go */
+ int rmtID; /* ID returned from Rmt module */
+ short flags; /* Flags to control treatment of job */
+#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
+#define JOB_SILENT 0x002 /* no output */
+#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally
+ * if we can't export it and maxLocal is 0 */
+#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
+ * commands */
+#define JOB_REMOTE 0x010 /* Job is running remotely */
+#define JOB_FIRST 0x020 /* Job is first job for the node */
+#define JOB_REMIGRATE 0x040 /* Job needs to be remigrated */
+#define JOB_RESTART 0x080 /* Job needs to be completely restarted */
+#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped,
+ * for some reason */
+#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job.
+ * Used to avoid infinite recursion between
+ * JobFinish and JobRestart */
+ union {
+ struct {
+ int op_inPipe; /* Input side of pipe associated
+ * with job's output channel */
+ int op_outPipe; /* Output side of pipe associated with
+ * job's output channel */
+ char op_outBuf[JOB_BUFSIZE + 1];
+ /* Buffer for storing the output of the
+ * job, line by line */
+ int op_curPos; /* Current position in op_outBuf */
+ } o_pipe; /* data used when catching the output via
+ * a pipe */
+ struct {
+ char of_outFile[sizeof(TMPPAT)+2];
+ /* Name of file to which shell output
+ * was rerouted */
+ int of_outFd; /* Stream open to the output
+ * file. Used to funnel all
+ * from a single job to one file
+ * while still allowing
+ * multiple shell invocations */
+ } o_file; /* Data used when catching the output in
+ * a temporary file */
+ } output; /* Data for tracking a shell's output */
+} Job;
+
+#define outPipe output.o_pipe.op_outPipe
+#define inPipe output.o_pipe.op_inPipe
+#define outBuf output.o_pipe.op_outBuf
+#define curPos output.o_pipe.op_curPos
+#define outFile output.o_file.of_outFile
+#define outFd output.o_file.of_outFd
+
+
+/*-
+ * Shell Specifications:
+ * Each shell type has associated with it the following information:
+ * 1) The string which must match the last character of the shell name
+ * for the shell to be considered of this type. The longest match
+ * wins.
+ * 2) A command to issue to turn off echoing of command lines
+ * 3) A command to issue to turn echoing back on again
+ * 4) What the shell prints, and its length, when given the echo-off
+ * command. This line will not be printed when received from the shell
+ * 5) A boolean to tell if the shell has the ability to control
+ * error checking for individual commands.
+ * 6) The string to turn this checking on.
+ * 7) The string to turn it off.
+ * 8) The command-flag to give to cause the shell to start echoing
+ * commands right away.
+ * 9) The command-flag to cause the shell to Lib_Exit when an error is
+ * detected in one of the commands.
+ *
+ * Some special stuff goes on if a shell doesn't have error control. In such
+ * a case, errCheck becomes a printf template for echoing the command,
+ * should echoing be on and ignErr becomes another printf template for
+ * executing the command while ignoring the return status. If either of these
+ * strings is empty when hasErrCtl is FALSE, the command will be executed
+ * anyway as is and if it causes an error, so be it.
+ */
+typedef struct Shell {
+ char *name; /* the name of the shell. For Bourne and C
+ * shells, this is used only to find the
+ * shell description when used as the single
+ * source of a .SHELL target. For user-defined
+ * shells, this is the full path of the shell.
+ */
+ Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */
+ char *echoOff; /* command to turn off echo */
+ char *echoOn; /* command to turn it back on again */
+ char *noPrint; /* command to skip when printing output from
+ * shell. This is usually the command which
+ * was executed to turn off echoing */
+ int noPLen; /* length of noPrint command */
+ Boolean hasErrCtl; /* set if can control error checking for
+ * individual commands */
+ char *errCheck; /* string to turn error checking on */
+ char *ignErr; /* string to turn off error checking */
+ /*
+ * command-line flags
+ */
+ char *echo; /* echo commands */
+ char *exit; /* exit on error */
+} Shell;
+
+
+extern char *targFmt; /* Format string for banner that separates
+ * output from multiple jobs. Contains a
+ * single %s where the name of the node being
+ * made should be put. */
+extern GNode *lastNode; /* Last node for which a banner was printed.
+ * If Rmt module finds it necessary to print
+ * a banner, it should set this to the node
+ * for which the banner was printed */
+extern int nJobs; /* Number of jobs running (local and remote) */
+extern int nLocal; /* Number of jobs running locally */
+extern Lst jobs; /* List of active job descriptors */
+extern Lst stoppedJobs; /* List of jobs that are stopped or didn't
+ * quite get started */
+extern Boolean jobFull; /* Non-zero if no more jobs should/will start*/
+
+
+void Job_Touch __P((GNode *, Boolean));
+Boolean Job_CheckCommands __P((GNode *, void (*abortProc )(char *, ...)));
+void Job_CatchChildren __P((Boolean));
+void Job_CatchOutput __P((void));
+void Job_Make __P((GNode *));
+void Job_Init __P((int, int));
+Boolean Job_Full __P((void));
+Boolean Job_Empty __P((void));
+ReturnStatus Job_ParseShell __P((char *));
+int Job_End __P((void));
+void Job_Wait __P((void));
+void Job_AbortAll __P((void));
+void JobFlagForMigration __P((int));
+
+#endif /* _JOB_H_ */
diff --git a/usr.bin/make/list.h b/usr.bin/make/list.h
new file mode 100644
index 0000000..1b21bfe
--- /dev/null
+++ b/usr.bin/make/list.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)list.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * list.h --
+ *
+ * Structures, macros, and routines exported by the List module.
+ */
+
+#ifndef _LIST
+#define _LIST
+
+#ifndef _SPRITE
+#include "sprite.h"
+#endif _SPRITE
+
+/*
+ * This module defines the list abstraction, which enables one to link
+ * together arbitrary data structures. Lists are doubly-linked and
+ * circular. A list contains a header followed by its real members, if
+ * any. (An empty list therefore consists of a single element, the
+ * header, whose nextPtr and prevPtr fields point to itself). To refer
+ * to a list as a whole, the user keeps a pointer to the header; that
+ * header is initialized by a call to List_Init(), which creates an empty
+ * list given a pointer to a List_Links structure (described below).
+ *
+ * The links are contained in a two-element structure called List_Links.
+ * A list joins List_Links records (that is, each List_Links structure
+ * points to other List_Links structures), but if the List_Links is the
+ * first field within a larger structure, then the larger structures are
+ * effectively linked together as follows:
+ *
+ * header
+ * (List_Links) first elt. second elt.
+ * ----------------- ----------------- -----------------
+ * ..-> | nextPtr | ----> | List_Links | ----> | List_Links |----..
+ * | - - - - - - - | | | | |
+ * ..-- | prevPtr | <---- | | <---- | |<---..
+ * ----------------- - --- --- --- - - --- --- --- -
+ * | rest of | | rest of |
+ * | structure | | structure |
+ * | | | |
+ * | ... | | ... |
+ * ----------------- -----------------
+ *
+ * It is possible to link structures through List_Links fields that are
+ * not at the beginning of the larger structure, but it is then necessary
+ * to perform pointer arithmetic to find the beginning of the larger
+ * structure, given a pointer to some point within it.
+ *
+ * A typical structure might be something like:
+ *
+ * typedef struct {
+ * List_Links links;
+ * char ch;
+ * integer flags;
+ * } EditChar;
+ *
+ * Before an element is inserted in a list for the first time, it must
+ * be initialized by calling the macro List_InitElement().
+ */
+
+
+/*
+ * data structure for lists
+ */
+
+typedef struct List_Links {
+ struct List_Links *prevPtr;
+ struct List_Links *nextPtr;
+} List_Links;
+
+/*
+ * procedures
+ */
+
+void List_Init(); /* initialize a header to a list */
+void List_Insert(); /* insert an element into a list */
+void List_Remove(); /* remove an element from a list */
+void List_Move(); /* move an element elsewhere in a list */
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_InitElement --
+ *
+ * Initialize a list element. Must be called before an element is first
+ * inserted into a list.
+ *
+ * ----------------------------------------------------------------------------
+ */
+#define List_InitElement(elementPtr) \
+ (elementPtr)->prevPtr = (List_Links *) NIL; \
+ (elementPtr)->nextPtr = (List_Links *) NIL;
+
+/*
+ * Macros for stepping through or selecting parts of lists
+ */
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * LIST_FORALL --
+ *
+ * Macro to loop through a list and perform an operation on each member.
+ *
+ * Usage: LIST_FORALL(headerPtr, itemPtr) {
+ * / *
+ * * operation on itemPtr, which points to successive members
+ * * of the list
+ * *
+ * * It may be appropriate to first assign
+ * * foobarPtr = (Foobar *) itemPtr;
+ * * to refer to the entire Foobar structure.
+ * * /
+ * }
+ *
+ * Note: itemPtr must be a List_Links pointer variable, and headerPtr
+ * must evaluate to a pointer to a List_Links structure.
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define LIST_FORALL(headerPtr, itemPtr) \
+ for (itemPtr = List_First(headerPtr); \
+ !List_IsAtEnd((headerPtr),itemPtr); \
+ itemPtr = List_Next(itemPtr))
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_IsEmpty --
+ *
+ * Macro: Boolean value, TRUE if the given list does not contain any
+ * members.
+ *
+ * Usage: if (List_IsEmpty(headerPtr)) ...
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define List_IsEmpty(headerPtr) \
+ ((headerPtr) == (headerPtr)->nextPtr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_IsAtEnd --
+ *
+ * Macro: Boolean value, TRUE if itemPtr is after the end of headerPtr
+ * (i.e., itemPtr is the header of the list).
+ *
+ * Usage: if (List_IsAtEnd(headerPtr, itemPtr)) ...
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+
+#define List_IsAtEnd(headerPtr, itemPtr) \
+ ((itemPtr) == (headerPtr))
+
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_First --
+ *
+ * Macro to return the first member in a list, which is the header if
+ * the list is empty.
+ *
+ * Usage: firstPtr = List_First(headerPtr);
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define List_First(headerPtr) ((headerPtr)->nextPtr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_Last --
+ *
+ * Macro to return the last member in a list, which is the header if
+ * the list is empty.
+ *
+ * Usage: lastPtr = List_Last(headerPtr);
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define List_Last(headerPtr) ((headerPtr)->prevPtr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_Prev --
+ *
+ * Macro to return the member preceding the given member in its list.
+ * If the given list member is the first element in the list, List_Prev
+ * returns the list header.
+ *
+ * Usage: prevPtr = List_Prev(itemPtr);
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define List_Prev(itemPtr) ((itemPtr)->prevPtr)
+
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * List_Next --
+ *
+ * Macro to return the member following the given member in its list.
+ * If the given list member is the last element in the list, List_Next
+ * returns the list header.
+ *
+ * Usage: nextPtr = List_Next(itemPtr);
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+#define List_Next(itemPtr) ((itemPtr)->nextPtr)
+
+
+/*
+ * ----------------------------------------------------------------------------
+ * The List_Insert procedure takes two arguments. The first argument
+ * is a pointer to the structure to be inserted into a list, and
+ * the second argument is a pointer to the list member after which
+ * the new element is to be inserted. Macros are used to determine
+ * which existing member will precede the new one.
+ *
+ * The List_Move procedure takes a destination argument with the same
+ * semantics as List_Insert.
+ *
+ * The following macros define where to insert the new element
+ * in the list:
+ *
+ * LIST_AFTER(itemPtr) -- insert after itemPtr
+ * LIST_BEFORE(itemPtr) -- insert before itemPtr
+ * LIST_ATFRONT(headerPtr) -- insert at front of list
+ * LIST_ATREAR(headerPtr) -- insert at end of list
+ *
+ * For example,
+ *
+ * List_Insert(itemPtr, LIST_AFTER(otherPtr));
+ *
+ * will insert itemPtr following otherPtr in the list containing otherPtr.
+ * ----------------------------------------------------------------------------
+ */
+
+#define LIST_AFTER(itemPtr) ((List_Links *) itemPtr)
+
+#define LIST_BEFORE(itemPtr) (((List_Links *) itemPtr)->prevPtr)
+
+#define LIST_ATFRONT(headerPtr) ((List_Links *) headerPtr)
+
+#define LIST_ATREAR(headerPtr) (((List_Links *) headerPtr)->prevPtr)
+
+#endif /* _LIST */
diff --git a/usr.bin/make/lst.h b/usr.bin/make/lst.h
new file mode 100644
index 0000000..6131ae3
--- /dev/null
+++ b/usr.bin/make/lst.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lst.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*-
+ * lst.h --
+ * Header for using the list library
+ */
+#ifndef _LST_H_
+#define _LST_H_
+
+#include <sprite.h>
+#include <sys/cdefs.h>
+#if __STDC__
+#include <stdlib.h>
+#endif
+
+/*
+ * basic typedef. This is what the Lst_ functions handle
+ */
+
+typedef struct Lst *Lst;
+typedef struct LstNode *LstNode;
+
+#define NILLST ((Lst) NIL)
+#define NILLNODE ((LstNode) NIL)
+
+/*
+ * NOFREE can be used as the freeProc to Lst_Destroy when the elements are
+ * not to be freed.
+ * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate.
+ */
+#define NOFREE ((void (*) __P((ClientData))) 0)
+#define NOCOPY ((ClientData (*) __P((ClientData))) 0)
+
+#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */
+#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */
+
+/*
+ * Creation/destruction functions
+ */
+/* Create a new list */
+Lst Lst_Init __P((Boolean));
+/* Duplicate an existing list */
+Lst Lst_Duplicate __P((Lst, ClientData (*)(ClientData)));
+/* Destroy an old one */
+void Lst_Destroy __P((Lst, void (*)(ClientData)));
+/* True if list is empty */
+Boolean Lst_IsEmpty __P((Lst));
+
+/*
+ * Functions to modify a list
+ */
+/* Insert an element before another */
+ReturnStatus Lst_Insert __P((Lst, LstNode, ClientData));
+/* Insert an element after another */
+ReturnStatus Lst_Append __P((Lst, LstNode, ClientData));
+/* Place an element at the front of a lst. */
+ReturnStatus Lst_AtFront __P((Lst, ClientData));
+/* Place an element at the end of a lst. */
+ReturnStatus Lst_AtEnd __P((Lst, ClientData));
+/* Remove an element */
+ReturnStatus Lst_Remove __P((Lst, LstNode));
+/* Replace a node with a new value */
+ReturnStatus Lst_Replace __P((LstNode, ClientData));
+/* Concatenate two lists */
+ReturnStatus Lst_Concat __P((Lst, Lst, int));
+
+/*
+ * Node-specific functions
+ */
+/* Return first element in list */
+LstNode Lst_First __P((Lst));
+/* Return last element in list */
+LstNode Lst_Last __P((Lst));
+/* Return successor to given element */
+LstNode Lst_Succ __P((LstNode));
+/* Get datum from LstNode */
+ClientData Lst_Datum __P((LstNode));
+
+/*
+ * Functions for entire lists
+ */
+/* Find an element in a list */
+LstNode Lst_Find __P((Lst, ClientData,
+ int (*)(ClientData, ClientData)));
+/* Find an element starting from somewhere */
+LstNode Lst_FindFrom __P((Lst, LstNode, ClientData,
+ int (*cProc)(ClientData, ClientData)));
+/*
+ * See if the given datum is on the list. Returns the LstNode containing
+ * the datum
+ */
+LstNode Lst_Member __P((Lst, ClientData));
+/* Apply a function to all elements of a lst */
+void Lst_ForEach __P((Lst, int (*)(ClientData, ClientData),
+ ClientData));
+/*
+ * Apply a function to all elements of a lst starting from a certain point.
+ * If the list is circular, the application will wrap around to the
+ * beginning of the list again.
+ */
+void Lst_ForEachFrom __P((Lst, LstNode,
+ int (*)(ClientData, ClientData),
+ ClientData));
+/*
+ * these functions are for dealing with a list as a table, of sorts.
+ * An idea of the "current element" is kept and used by all the functions
+ * between Lst_Open() and Lst_Close().
+ */
+/* Open the list */
+ReturnStatus Lst_Open __P((Lst));
+/* Next element please */
+LstNode Lst_Next __P((Lst));
+/* Done yet? */
+Boolean Lst_IsAtEnd __P((Lst));
+/* Finish table access */
+void Lst_Close __P((Lst));
+
+/*
+ * for using the list as a queue
+ */
+/* Place an element at tail of queue */
+ReturnStatus Lst_EnQueue __P((Lst, ClientData));
+/* Remove an element from head of queue */
+ClientData Lst_DeQueue __P((Lst));
+
+#endif /* _LST_H_ */
diff --git a/usr.bin/make/lst.lib/lstAppend.c b/usr.bin/make/lst.lib/lstAppend.c
new file mode 100644
index 0000000..16f9d87
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAppend.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstAppend.c --
+ * Add a new node with a new datum after an existing node
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Append --
+ * Create a new node and add it to the given list after the given node.
+ *
+ * Results:
+ * SUCCESS if all went well.
+ *
+ * Side Effects:
+ * A new ListNode is created and linked in to the List. The lastPtr
+ * field of the List will be altered if ln is the last node in the
+ * list. lastPtr and firstPtr will alter if the list was empty and
+ * ln was NILLNODE.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Append (l, ln, d)
+ Lst l; /* affected list */
+ LstNode ln; /* node after which to append the datum */
+ ClientData d; /* said datum */
+{
+ register List list;
+ register ListNode lNode;
+ register ListNode nLNode;
+
+ if (LstValid (l) && (ln == NILLNODE && LstIsEmpty (l))) {
+ goto ok;
+ }
+
+ if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) {
+ return (FAILURE);
+ }
+ ok:
+
+ list = (List)l;
+ lNode = (ListNode)ln;
+
+ PAlloc (nLNode, ListNode);
+ nLNode->datum = d;
+ nLNode->useCount = nLNode->flags = 0;
+
+ if (lNode == NilListNode) {
+ if (list->isCirc) {
+ nLNode->nextPtr = nLNode->prevPtr = nLNode;
+ } else {
+ nLNode->nextPtr = nLNode->prevPtr = NilListNode;
+ }
+ list->firstPtr = list->lastPtr = nLNode;
+ } else {
+ nLNode->prevPtr = lNode;
+ nLNode->nextPtr = lNode->nextPtr;
+
+ lNode->nextPtr = nLNode;
+ if (nLNode->nextPtr != NilListNode) {
+ nLNode->nextPtr->prevPtr = nLNode;
+ }
+
+ if (lNode == list->lastPtr) {
+ list->lastPtr = nLNode;
+ }
+ }
+
+ return (SUCCESS);
+}
+
diff --git a/usr.bin/make/lst.lib/lstAtEnd.c b/usr.bin/make/lst.lib/lstAtEnd.c
new file mode 100644
index 0000000..5513453
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAtEnd.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstAtEnd.c --
+ * Add a node at the end of the list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_AtEnd --
+ * Add a node to the end of the given list
+ *
+ * Results:
+ * SUCCESS if life is good.
+ *
+ * Side Effects:
+ * A new ListNode is created and added to the list.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_AtEnd (l, d)
+ Lst l; /* List to which to add the datum */
+ ClientData d; /* Datum to add */
+{
+ register LstNode end;
+
+ end = Lst_Last (l);
+ return (Lst_Append (l, end, d));
+}
diff --git a/usr.bin/make/lst.lib/lstAtFront.c b/usr.bin/make/lst.lib/lstAtFront.c
new file mode 100644
index 0000000..490cb9b
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstAtFront.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstAtFront.c --
+ * Add a node at the front of the list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_AtFront --
+ * Place a piece of data at the front of a list
+ *
+ * Results:
+ * SUCCESS or FAILURE
+ *
+ * Side Effects:
+ * A new ListNode is created and stuck at the front of the list.
+ * hence, firstPtr (and possible lastPtr) in the list are altered.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_AtFront (l, d)
+ Lst l;
+ ClientData d;
+{
+ register LstNode front;
+
+ front = Lst_First (l);
+ return (Lst_Insert (l, front, d));
+}
diff --git a/usr.bin/make/lst.lib/lstClose.c b/usr.bin/make/lst.lib/lstClose.c
new file mode 100644
index 0000000..19a8d2e
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstClose.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstClose.c --
+ * Close a list for sequential access.
+ * The sequential functions access the list in a slightly different way.
+ * CurPtr points to their idea of the current node in the list and they
+ * access the list based on it. Because the list is circular, Lst_Next
+ * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
+ * used to determine when to stop.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Close --
+ * Close a list which was opened for sequential access.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The list is closed.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Lst_Close (l)
+ Lst l; /* The list to close */
+{
+ register List list = (List) l;
+
+ if (LstValid(l) == TRUE) {
+ list->isOpen = FALSE;
+ list->atEnd = Unknown;
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstConcat.c b/usr.bin/make/lst.lib/lstConcat.c
new file mode 100644
index 0000000..3c90872
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstConcat.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * listConcat.c --
+ * Function to concatentate two lists.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Concat --
+ * Concatenate two lists. New elements are created to hold the data
+ * elements, if specified, but the elements themselves are not copied.
+ * If the elements should be duplicated to avoid confusion with another
+ * list, the Lst_Duplicate function should be called first.
+ * If LST_CONCLINK is specified, the second list is destroyed since
+ * its pointers have been corrupted and the list is no longer useable.
+ *
+ * Results:
+ * SUCCESS if all went well. FAILURE otherwise.
+ *
+ * Side Effects:
+ * New elements are created and appended the the first list.
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Concat (l1, l2, flags)
+ Lst l1; /* The list to which l2 is to be appended */
+ Lst l2; /* The list to append to l1 */
+ int flags; /* LST_CONCNEW if LstNode's should be duplicated
+ * LST_CONCLINK if should just be relinked */
+{
+ register ListNode ln; /* original LstNode */
+ register ListNode nln; /* new LstNode */
+ register ListNode last; /* the last element in the list. Keeps
+ * bookkeeping until the end */
+ register List list1 = (List)l1;
+ register List list2 = (List)l2;
+
+ if (!LstValid (l1) || !LstValid (l2)) {
+ return (FAILURE);
+ }
+
+ if (flags == LST_CONCLINK) {
+ if (list2->firstPtr != NilListNode) {
+ /*
+ * We set the nextPtr of the
+ * last element of list two to be NIL to make the loop easier and
+ * so we don't need an extra case should the first list turn
+ * out to be non-circular -- the final element will already point
+ * to NIL space and the first element will be untouched if it
+ * existed before and will also point to NIL space if it didn't.
+ */
+ list2->lastPtr->nextPtr = NilListNode;
+ /*
+ * So long as the second list isn't empty, we just link the
+ * first element of the second list to the last element of the
+ * first list. If the first list isn't empty, we then link the
+ * last element of the list to the first element of the second list
+ * The last element of the second list, if it exists, then becomes
+ * the last element of the first list.
+ */
+ list2->firstPtr->prevPtr = list1->lastPtr;
+ if (list1->lastPtr != NilListNode) {
+ list1->lastPtr->nextPtr = list2->firstPtr;
+ } else {
+ list1->firstPtr = list2->firstPtr;
+ }
+ list1->lastPtr = list2->lastPtr;
+ }
+ if (list1->isCirc && list1->firstPtr != NilListNode) {
+ /*
+ * If the first list is supposed to be circular and it is (now)
+ * non-empty, we must make sure it's circular by linking the
+ * first element to the last and vice versa
+ */
+ list1->firstPtr->prevPtr = list1->lastPtr;
+ list1->lastPtr->nextPtr = list1->firstPtr;
+ }
+ free ((Address)l2);
+ } else if (list2->firstPtr != NilListNode) {
+ /*
+ * We set the nextPtr of the last element of list 2 to be nil to make
+ * the loop less difficult. The loop simply goes through the entire
+ * second list creating new LstNodes and filling in the nextPtr, and
+ * prevPtr to fit into l1 and its datum field from the
+ * datum field of the corresponding element in l2. The 'last' node
+ * follows the last of the new nodes along until the entire l2 has
+ * been appended. Only then does the bookkeeping catch up with the
+ * changes. During the first iteration of the loop, if 'last' is nil,
+ * the first list must have been empty so the newly-created node is
+ * made the first node of the list.
+ */
+ list2->lastPtr->nextPtr = NilListNode;
+ for (last = list1->lastPtr, ln = list2->firstPtr;
+ ln != NilListNode;
+ ln = ln->nextPtr)
+ {
+ PAlloc (nln, ListNode);
+ nln->datum = ln->datum;
+ if (last != NilListNode) {
+ last->nextPtr = nln;
+ } else {
+ list1->firstPtr = nln;
+ }
+ nln->prevPtr = last;
+ nln->flags = nln->useCount = 0;
+ last = nln;
+ }
+
+ /*
+ * Finish bookkeeping. The last new element becomes the last element
+ * of list one.
+ */
+ list1->lastPtr = last;
+
+ /*
+ * The circularity of both list one and list two must be corrected
+ * for -- list one because of the new nodes added to it; list two
+ * because of the alteration of list2->lastPtr's nextPtr to ease the
+ * above for loop.
+ */
+ if (list1->isCirc) {
+ list1->lastPtr->nextPtr = list1->firstPtr;
+ list1->firstPtr->prevPtr = list1->lastPtr;
+ } else {
+ last->nextPtr = NilListNode;
+ }
+
+ if (list2->isCirc) {
+ list2->lastPtr->nextPtr = list2->firstPtr;
+ }
+ }
+
+ return (SUCCESS);
+}
+
diff --git a/usr.bin/make/lst.lib/lstDatum.c b/usr.bin/make/lst.lib/lstDatum.c
new file mode 100644
index 0000000..542898b
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDatum.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstDatum.c --
+ * Return the datum associated with a list node.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Datum --
+ * Return the datum stored in the given node.
+ *
+ * Results:
+ * The datum or (ick!) NIL if the node is invalid.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+ClientData
+Lst_Datum (ln)
+ LstNode ln;
+{
+ if (ln != NILLNODE) {
+ return (((ListNode)ln)->datum);
+ } else {
+ return ((ClientData) NIL);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstDeQueue.c b/usr.bin/make/lst.lib/lstDeQueue.c
new file mode 100644
index 0000000..dbb4da6
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDeQueue.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstDeQueue.c --
+ * Remove the node and return its datum from the head of the list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_DeQueue --
+ * Remove and return the datum at the head of the given list.
+ *
+ * Results:
+ * The datum in the node at the head or (ick) NIL if the list
+ * is empty.
+ *
+ * Side Effects:
+ * The head node is removed from the list.
+ *
+ *-----------------------------------------------------------------------
+ */
+ClientData
+Lst_DeQueue (l)
+ Lst l;
+{
+ ClientData rd;
+ register ListNode tln;
+
+ tln = (ListNode) Lst_First (l);
+ if (tln == NilListNode) {
+ return ((ClientData) NIL);
+ }
+
+ rd = tln->datum;
+ if (Lst_Remove (l, (LstNode)tln) == FAILURE) {
+ return ((ClientData) NIL);
+ } else {
+ return (rd);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstDestroy.c b/usr.bin/make/lst.lib/lstDestroy.c
new file mode 100644
index 0000000..7891e74
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDestroy.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstDestroy.c --
+ * Nuke a list and all its resources
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Destroy --
+ * Destroy a list and free all its resources. If the freeProc is
+ * given, it is called with the datum from each node in turn before
+ * the node is freed.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The given list is freed in its entirety.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Lst_Destroy (l, freeProc)
+ Lst l;
+ register void (*freeProc) __P((ClientData));
+{
+ register ListNode ln;
+ register ListNode tln = NilListNode;
+ register List list = (List)l;
+
+ if (l == NILLST || ! l) {
+ /*
+ * Note the check for l == (Lst)0 to catch uninitialized static Lst's.
+ * Gross, but useful.
+ */
+ return;
+ }
+
+ /* To ease scanning */
+ if (list->lastPtr != NilListNode)
+ list->lastPtr->nextPtr = NilListNode;
+ else {
+ free ((Address)l);
+ return;
+ }
+
+ if (freeProc) {
+ for (ln = list->firstPtr; ln != NilListNode; ln = tln) {
+ tln = ln->nextPtr;
+ (*freeProc) (ln->datum);
+ free ((Address)ln);
+ }
+ } else {
+ for (ln = list->firstPtr; ln != NilListNode; ln = tln) {
+ tln = ln->nextPtr;
+ free ((Address)ln);
+ }
+ }
+
+ free ((Address)l);
+}
diff --git a/usr.bin/make/lst.lib/lstDupl.c b/usr.bin/make/lst.lib/lstDupl.c
new file mode 100644
index 0000000..30124c1
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstDupl.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * listDupl.c --
+ * Duplicate a list. This includes duplicating the individual
+ * elements.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Duplicate --
+ * Duplicate an entire list. If a function to copy a ClientData is
+ * given, the individual client elements will be duplicated as well.
+ *
+ * Results:
+ * The new Lst structure or NILLST if failure.
+ *
+ * Side Effects:
+ * A new list is created.
+ *-----------------------------------------------------------------------
+ */
+Lst
+Lst_Duplicate (l, copyProc)
+ Lst l; /* the list to duplicate */
+ /* A function to duplicate each ClientData */
+ ClientData (*copyProc) __P((ClientData));
+{
+ register Lst nl;
+ register ListNode ln;
+ register List list = (List)l;
+
+ if (!LstValid (l)) {
+ return (NILLST);
+ }
+
+ nl = Lst_Init (list->isCirc);
+ if (nl == NILLST) {
+ return (NILLST);
+ }
+
+ ln = list->firstPtr;
+ while (ln != NilListNode) {
+ if (copyProc != NOCOPY) {
+ if (Lst_AtEnd (nl, (*copyProc) (ln->datum)) == FAILURE) {
+ return (NILLST);
+ }
+ } else if (Lst_AtEnd (nl, ln->datum) == FAILURE) {
+ return (NILLST);
+ }
+
+ if (list->isCirc && ln == list->lastPtr) {
+ ln = NilListNode;
+ } else {
+ ln = ln->nextPtr;
+ }
+ }
+
+ return (nl);
+}
diff --git a/usr.bin/make/lst.lib/lstEnQueue.c b/usr.bin/make/lst.lib/lstEnQueue.c
new file mode 100644
index 0000000..8987adc
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstEnQueue.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstEnQueue.c--
+ * Treat the list as a queue and place a datum at its end
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_EnQueue --
+ * Add the datum to the tail of the given list.
+ *
+ * Results:
+ * SUCCESS or FAILURE as returned by Lst_Append.
+ *
+ * Side Effects:
+ * the lastPtr field is altered all the time and the firstPtr field
+ * will be altered if the list used to be empty.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_EnQueue (l, d)
+ Lst l;
+ ClientData d;
+{
+ if (LstValid (l) == FALSE) {
+ return (FAILURE);
+ }
+
+ return (Lst_Append (l, Lst_Last(l), d));
+}
+
diff --git a/usr.bin/make/lst.lib/lstFind.c b/usr.bin/make/lst.lib/lstFind.c
new file mode 100644
index 0000000..7d8516a
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFind.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstFind.c --
+ * Find a node on a list.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Find --
+ * Find a node on the given list using the given comparison function
+ * and the given datum.
+ *
+ * Results:
+ * The found node or NILLNODE if none matches.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_Find (l, d, cProc)
+ Lst l;
+ ClientData d;
+ int (*cProc) __P((ClientData, ClientData));
+{
+ return (Lst_FindFrom (l, Lst_First(l), d, cProc));
+}
+
diff --git a/usr.bin/make/lst.lib/lstFindFrom.c b/usr.bin/make/lst.lib/lstFindFrom.c
new file mode 100644
index 0000000..530f307
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFindFrom.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstFindFrom.c --
+ * Find a node on a list from a given starting point. Used by Lst_Find.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_FindFrom --
+ * Search for a node starting and ending with the given one on the
+ * given list using the passed datum and comparison function to
+ * determine when it has been found.
+ *
+ * Results:
+ * The found node or NILLNODE
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_FindFrom (l, ln, d, cProc)
+ Lst l;
+ register LstNode ln;
+ register ClientData d;
+ register int (*cProc) __P((ClientData, ClientData));
+{
+ register ListNode tln;
+ Boolean found = FALSE;
+
+ if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
+ return (NILLNODE);
+ }
+
+ tln = (ListNode)ln;
+
+ do {
+ if ((*cProc) (tln->datum, d) == 0) {
+ found = TRUE;
+ break;
+ } else {
+ tln = tln->nextPtr;
+ }
+ } while (tln != (ListNode)ln && tln != NilListNode);
+
+ if (found) {
+ return ((LstNode)tln);
+ } else {
+ return (NILLNODE);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstFirst.c b/usr.bin/make/lst.lib/lstFirst.c
new file mode 100644
index 0000000..95e0893
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstFirst.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstFirst.c --
+ * Return the first node of a list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_First --
+ * Return the first node on the given list.
+ *
+ * Results:
+ * The first node or NILLNODE if the list is empty.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_First (l)
+ Lst l;
+{
+ if (!LstValid (l) || LstIsEmpty (l)) {
+ return (NILLNODE);
+ } else {
+ return ((LstNode)((List)l)->firstPtr);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstForEach.c b/usr.bin/make/lst.lib/lstForEach.c
new file mode 100644
index 0000000..f69b5e2
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstForEach.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstForeach.c --
+ * Perform a given function on all elements of a list.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_ForEach --
+ * Apply the given function to each element of the given list. The
+ * function should return 0 if Lst_ForEach should continue and non-
+ * zero if it should abort.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Only those created by the passed-in function.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*VARARGS2*/
+void
+Lst_ForEach (l, proc, d)
+ Lst l;
+ register int (*proc) __P((ClientData, ClientData));
+ register ClientData d;
+{
+ Lst_ForEachFrom(l, Lst_First(l), proc, d);
+}
+
diff --git a/usr.bin/make/lst.lib/lstForEachFrom.c b/usr.bin/make/lst.lib/lstForEachFrom.c
new file mode 100644
index 0000000..6ae43ef
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstForEachFrom.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * lstForEachFrom.c --
+ * Perform a given function on all elements of a list starting from
+ * a given point.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_ForEachFrom --
+ * Apply the given function to each element of the given list. The
+ * function should return 0 if traversal should continue and non-
+ * zero if it should abort.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Only those created by the passed-in function.
+ *
+ *-----------------------------------------------------------------------
+ */
+/*VARARGS2*/
+void
+Lst_ForEachFrom (l, ln, proc, d)
+ Lst l;
+ LstNode ln;
+ register int (*proc)();
+ register ClientData d;
+{
+ register ListNode tln = (ListNode)ln;
+ register List list = (List)l;
+ register ListNode next;
+ Boolean done;
+ int result;
+
+ if (!LstValid (list) || LstIsEmpty (list)) {
+ return;
+ }
+
+ do {
+ /*
+ * Take care of having the current element deleted out from under
+ * us.
+ */
+
+ next = tln->nextPtr;
+
+ (void) tln->useCount++;
+ result = (*proc) (tln->datum, d);
+ (void) tln->useCount--;
+
+ /*
+ * We're done with the traversal if
+ * - nothing's been added after the current node and
+ * - the next node to examine is the first in the queue or
+ * doesn't exist.
+ */
+ done = (next == tln->nextPtr &&
+ (next == NilListNode || next == list->firstPtr));
+
+ next = tln->nextPtr;
+
+ if (tln->flags & LN_DELETED) {
+ free((char *)tln);
+ }
+ tln = next;
+ } while (!result && !LstIsEmpty(list) && !done);
+
+}
diff --git a/usr.bin/make/lst.lib/lstInit.c b/usr.bin/make/lst.lib/lstInit.c
new file mode 100644
index 0000000..8e696cd
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInit.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * init.c --
+ * Initialize a new linked list.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Init --
+ * Create and initialize a new list.
+ *
+ * Results:
+ * The created list.
+ *
+ * Side Effects:
+ * A list is created, what else?
+ *
+ *-----------------------------------------------------------------------
+ */
+Lst
+Lst_Init(circ)
+ Boolean circ; /* TRUE if the list should be made circular */
+{
+ register List nList;
+
+ PAlloc (nList, List);
+
+ nList->firstPtr = NilListNode;
+ nList->lastPtr = NilListNode;
+ nList->isOpen = FALSE;
+ nList->isCirc = circ;
+ nList->atEnd = Unknown;
+
+ return ((Lst)nList);
+}
diff --git a/usr.bin/make/lst.lib/lstInsert.c b/usr.bin/make/lst.lib/lstInsert.c
new file mode 100644
index 0000000..e07df21
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInsert.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstInsert.c --
+ * Insert a new datum before an old one
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Insert --
+ * Insert a new node with the given piece of data before the given
+ * node in the given list.
+ *
+ * Results:
+ * SUCCESS or FAILURE.
+ *
+ * Side Effects:
+ * the firstPtr field will be changed if ln is the first node in the
+ * list.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Insert (l, ln, d)
+ Lst l; /* list to manipulate */
+ LstNode ln; /* node before which to insert d */
+ ClientData d; /* datum to be inserted */
+{
+ register ListNode nLNode; /* new lnode for d */
+ register ListNode lNode = (ListNode)ln;
+ register List list = (List)l;
+
+
+ /*
+ * check validity of arguments
+ */
+ if (LstValid (l) && (LstIsEmpty (l) && ln == NILLNODE))
+ goto ok;
+
+ if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) {
+ return (FAILURE);
+ }
+
+ ok:
+ PAlloc (nLNode, ListNode);
+
+ nLNode->datum = d;
+ nLNode->useCount = nLNode->flags = 0;
+
+ if (ln == NILLNODE) {
+ if (list->isCirc) {
+ nLNode->prevPtr = nLNode->nextPtr = nLNode;
+ } else {
+ nLNode->prevPtr = nLNode->nextPtr = NilListNode;
+ }
+ list->firstPtr = list->lastPtr = nLNode;
+ } else {
+ nLNode->prevPtr = lNode->prevPtr;
+ nLNode->nextPtr = lNode;
+
+ if (nLNode->prevPtr != NilListNode) {
+ nLNode->prevPtr->nextPtr = nLNode;
+ }
+ lNode->prevPtr = nLNode;
+
+ if (lNode == list->firstPtr) {
+ list->firstPtr = nLNode;
+ }
+ }
+
+ return (SUCCESS);
+}
+
diff --git a/usr.bin/make/lst.lib/lstInt.h b/usr.bin/make/lst.lib/lstInt.h
new file mode 100644
index 0000000..0296558
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstInt.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lstInt.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*-
+ * lstInt.h --
+ * Internals for the list library
+ */
+#ifndef _LSTINT_H_
+#define _LSTINT_H_
+
+#include "lst.h"
+
+typedef struct ListNode {
+ struct ListNode *prevPtr; /* previous element in list */
+ struct ListNode *nextPtr; /* next in list */
+ short useCount:8, /* Count of functions using the node.
+ * node may not be deleted until count
+ * goes to 0 */
+ flags:8; /* Node status flags */
+ ClientData datum; /* datum associated with this element */
+} *ListNode;
+/*
+ * Flags required for synchronization
+ */
+#define LN_DELETED 0x0001 /* List node should be removed when done */
+
+#define NilListNode ((ListNode)-1)
+
+typedef enum {
+ Head, Middle, Tail, Unknown
+} Where;
+
+typedef struct {
+ ListNode firstPtr; /* first node in list */
+ ListNode lastPtr; /* last node in list */
+ Boolean isCirc; /* true if the list should be considered
+ * circular */
+/*
+ * fields for sequential access
+ */
+ Where atEnd; /* Where in the list the last access was */
+ Boolean isOpen; /* true if list has been Lst_Open'ed */
+ ListNode curPtr; /* current node, if open. NilListNode if
+ * *just* opened */
+ ListNode prevPtr; /* Previous node, if open. Used by
+ * Lst_Remove */
+} *List;
+
+#define NilList ((List)-1)
+
+/*
+ * PAlloc (var, ptype) --
+ * Allocate a pointer-typedef structure 'ptype' into the variable 'var'
+ */
+#define PAlloc(var,ptype) var = (ptype) malloc (sizeof (*var))
+
+/*
+ * LstValid (l) --
+ * Return TRUE if the list l is valid
+ */
+#define LstValid(l) (((Lst)l == NILLST) ? FALSE : TRUE)
+
+/*
+ * LstNodeValid (ln, l) --
+ * Return TRUE if the LstNode ln is valid with respect to l
+ */
+#define LstNodeValid(ln, l) ((((LstNode)ln) == NILLNODE) ? FALSE : TRUE)
+
+/*
+ * LstIsEmpty (l) --
+ * TRUE if the list l is empty.
+ */
+#define LstIsEmpty(l) (((List)l)->firstPtr == NilListNode)
+
+#endif _LSTINT_H_
diff --git a/usr.bin/make/lst.lib/lstIsAtEnd.c b/usr.bin/make/lst.lib/lstIsAtEnd.c
new file mode 100644
index 0000000..2a8fc2f
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstIsAtEnd.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstIsAtEnd.c --
+ * Tell if the current node is at the end of the list.
+ * The sequential functions access the list in a slightly different way.
+ * CurPtr points to their idea of the current node in the list and they
+ * access the list based on it. Because the list is circular, Lst_Next
+ * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
+ * used to determine when to stop.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_IsAtEnd --
+ * Return true if have reached the end of the given list.
+ *
+ * Results:
+ * TRUE if at the end of the list (this includes the list not being
+ * open or being invalid) or FALSE if not. We return TRUE if the list
+ * is invalid or unopend so as to cause the caller to exit its loop
+ * asap, the assumption being that the loop is of the form
+ * while (!Lst_IsAtEnd (l)) {
+ * ...
+ * }
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Lst_IsAtEnd (l)
+ Lst l;
+{
+ register List list = (List) l;
+
+ return (!LstValid (l) || !list->isOpen ||
+ (list->atEnd == Head) || (list->atEnd == Tail));
+}
+
diff --git a/usr.bin/make/lst.lib/lstIsEmpty.c b/usr.bin/make/lst.lib/lstIsEmpty.c
new file mode 100644
index 0000000..c2a6509
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstIsEmpty.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstIsEmpty.c --
+ * A single function to decide if a list is empty
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_IsEmpty --
+ * Return TRUE if the given list is empty.
+ *
+ * Results:
+ * TRUE if the list is empty, FALSE otherwise.
+ *
+ * Side Effects:
+ * None.
+ *
+ * A list is considered empty if its firstPtr == NilListNode (or if
+ * the list itself is NILLIST).
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Lst_IsEmpty (l)
+ Lst l;
+{
+ return ( ! LstValid (l) || LstIsEmpty(l));
+}
+
diff --git a/usr.bin/make/lst.lib/lstLast.c b/usr.bin/make/lst.lib/lstLast.c
new file mode 100644
index 0000000..176aafb
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstLast.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstLast.c --
+ * Return the last element of a list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Last --
+ * Return the last node on the list l.
+ *
+ * Results:
+ * The requested node or NILLNODE if the list is empty.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_Last (l)
+ Lst l;
+{
+ if (!LstValid(l) || LstIsEmpty (l)) {
+ return (NILLNODE);
+ } else {
+ return ((LstNode)((List)l)->lastPtr);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstMember.c b/usr.bin/make/lst.lib/lstMember.c
new file mode 100644
index 0000000..9c1aac7
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstMember.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * lstMember.c --
+ * See if a given datum is on a given list.
+ */
+
+#include "lstInt.h"
+
+LstNode
+Lst_Member (l, d)
+ Lst l;
+ ClientData d;
+{
+ List list = (List) l;
+ register ListNode lNode;
+
+ lNode = list->firstPtr;
+ if (lNode == NilListNode) {
+ return NILLNODE;
+ }
+
+ do {
+ if (lNode->datum == d) {
+ return (LstNode)lNode;
+ }
+ lNode = lNode->nextPtr;
+ } while (lNode != NilListNode && lNode != list->firstPtr);
+
+ return NILLNODE;
+}
diff --git a/usr.bin/make/lst.lib/lstNext.c b/usr.bin/make/lst.lib/lstNext.c
new file mode 100644
index 0000000..b371eca
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstNext.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstNext.c --
+ * Return the next node for a list.
+ * The sequential functions access the list in a slightly different way.
+ * CurPtr points to their idea of the current node in the list and they
+ * access the list based on it. Because the list is circular, Lst_Next
+ * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be
+ * used to determine when to stop.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Next --
+ * Return the next node for the given list.
+ *
+ * Results:
+ * The next node or NILLNODE if the list has yet to be opened. Also
+ * if the list is non-circular and the end has been reached, NILLNODE
+ * is returned.
+ *
+ * Side Effects:
+ * the curPtr field is updated.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_Next (l)
+ Lst l;
+{
+ register ListNode tln;
+ register List list = (List)l;
+
+ if ((LstValid (l) == FALSE) ||
+ (list->isOpen == FALSE)) {
+ return (NILLNODE);
+ }
+
+ list->prevPtr = list->curPtr;
+
+ if (list->curPtr == NilListNode) {
+ if (list->atEnd == Unknown) {
+ /*
+ * If we're just starting out, atEnd will be Unknown.
+ * Then we want to start this thing off in the right
+ * direction -- at the start with atEnd being Middle.
+ */
+ list->curPtr = tln = list->firstPtr;
+ list->atEnd = Middle;
+ } else {
+ tln = NilListNode;
+ list->atEnd = Tail;
+ }
+ } else {
+ tln = list->curPtr->nextPtr;
+ list->curPtr = tln;
+
+ if (tln == list->firstPtr || tln == NilListNode) {
+ /*
+ * If back at the front, then we've hit the end...
+ */
+ list->atEnd = Tail;
+ } else {
+ /*
+ * Reset to Middle if gone past first.
+ */
+ list->atEnd = Middle;
+ }
+ }
+
+ return ((LstNode)tln);
+}
+
diff --git a/usr.bin/make/lst.lib/lstOpen.c b/usr.bin/make/lst.lib/lstOpen.c
new file mode 100644
index 0000000..f1decd7
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstOpen.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstOpen.c --
+ * Open a list for sequential access. The sequential functions access the
+ * list in a slightly different way. CurPtr points to their idea of the
+ * current node in the list and they access the list based on it.
+ * If the list is circular, Lst_Next and Lst_Prev will go around
+ * the list forever. Lst_IsAtEnd must be used to determine when to stop.
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Open --
+ * Open a list for sequential access. A list can still be searched,
+ * etc., without confusing these functions.
+ *
+ * Results:
+ * SUCCESS or FAILURE.
+ *
+ * Side Effects:
+ * isOpen is set TRUE and curPtr is set to NilListNode so the
+ * other sequential functions no it was just opened and can choose
+ * the first element accessed based on this.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Open (l)
+ register Lst l;
+{
+ if (LstValid (l) == FALSE) {
+ return (FAILURE);
+ }
+ ((List) l)->isOpen = TRUE;
+ ((List) l)->atEnd = LstIsEmpty (l) ? Head : Unknown;
+ ((List) l)->curPtr = NilListNode;
+
+ return (SUCCESS);
+}
+
diff --git a/usr.bin/make/lst.lib/lstRemove.c b/usr.bin/make/lst.lib/lstRemove.c
new file mode 100644
index 0000000..c43e3da
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstRemove.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstRemove.c --
+ * Remove an element from a list
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Remove --
+ * Remove the given node from the given list.
+ *
+ * Results:
+ * SUCCESS or FAILURE.
+ *
+ * Side Effects:
+ * The list's firstPtr will be set to NilListNode if ln is the last
+ * node on the list. firsPtr and lastPtr will be altered if ln is
+ * either the first or last node, respectively, on the list.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Remove (l, ln)
+ Lst l;
+ LstNode ln;
+{
+ register List list = (List) l;
+ register ListNode lNode = (ListNode) ln;
+
+ if (!LstValid (l) ||
+ !LstNodeValid (ln, l)) {
+ return (FAILURE);
+ }
+
+ /*
+ * unlink it from the list
+ */
+ if (lNode->nextPtr != NilListNode) {
+ lNode->nextPtr->prevPtr = lNode->prevPtr;
+ }
+ if (lNode->prevPtr != NilListNode) {
+ lNode->prevPtr->nextPtr = lNode->nextPtr;
+ }
+
+ /*
+ * if either the firstPtr or lastPtr of the list point to this node,
+ * adjust them accordingly
+ */
+ if (list->firstPtr == lNode) {
+ list->firstPtr = lNode->nextPtr;
+ }
+ if (list->lastPtr == lNode) {
+ list->lastPtr = lNode->prevPtr;
+ }
+
+ /*
+ * Sequential access stuff. If the node we're removing is the current
+ * node in the list, reset the current node to the previous one. If the
+ * previous one was non-existent (prevPtr == NilListNode), we set the
+ * end to be Unknown, since it is.
+ */
+ if (list->isOpen && (list->curPtr == lNode)) {
+ list->curPtr = list->prevPtr;
+ if (list->curPtr == NilListNode) {
+ list->atEnd = Unknown;
+ }
+ }
+
+ /*
+ * the only way firstPtr can still point to ln is if ln is the last
+ * node on the list (the list is circular, so lNode->nextptr == lNode in
+ * this case). The list is, therefore, empty and is marked as such
+ */
+ if (list->firstPtr == lNode) {
+ list->firstPtr = NilListNode;
+ }
+
+ /*
+ * note that the datum is unmolested. The caller must free it as
+ * necessary and as expected.
+ */
+ if (lNode->useCount == 0) {
+ free ((Address)ln);
+ } else {
+ lNode->flags |= LN_DELETED;
+ }
+
+ return (SUCCESS);
+}
+
diff --git a/usr.bin/make/lst.lib/lstReplace.c b/usr.bin/make/lst.lib/lstReplace.c
new file mode 100644
index 0000000..344631a
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstReplace.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstReplace.c --
+ * Replace the datum in a node with a new datum
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Replace --
+ * Replace the datum in the given node with the new datum
+ *
+ * Results:
+ * SUCCESS or FAILURE.
+ *
+ * Side Effects:
+ * The datum field fo the node is altered.
+ *
+ *-----------------------------------------------------------------------
+ */
+ReturnStatus
+Lst_Replace (ln, d)
+ register LstNode ln;
+ ClientData d;
+{
+ if (ln == NILLNODE) {
+ return (FAILURE);
+ } else {
+ ((ListNode) ln)->datum = d;
+ return (SUCCESS);
+ }
+}
+
diff --git a/usr.bin/make/lst.lib/lstSucc.c b/usr.bin/make/lst.lib/lstSucc.c
new file mode 100644
index 0000000..06b354d
--- /dev/null
+++ b/usr.bin/make/lst.lib/lstSucc.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * LstSucc.c --
+ * return the successor to a given node
+ */
+
+#include "lstInt.h"
+
+/*-
+ *-----------------------------------------------------------------------
+ * Lst_Succ --
+ * Return the sucessor to the given node on its list.
+ *
+ * Results:
+ * The successor of the node, if it exists (note that on a circular
+ * list, if the node is the only one in the list, it is its own
+ * successor).
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+LstNode
+Lst_Succ (ln)
+ LstNode ln;
+{
+ if (ln == NILLNODE) {
+ return (NILLNODE);
+ } else {
+ return ((LstNode) ((ListNode) ln)->nextPtr);
+ }
+}
+
diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c
new file mode 100644
index 0000000..1698000
--- /dev/null
+++ b/usr.bin/make/main.c
@@ -0,0 +1,954 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1989, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * main.c --
+ * The main file for this entire program. Exit routines etc
+ * reside here.
+ *
+ * Utility functions defined in this file:
+ * Main_ParseArgLine Takes a line of arguments, breaks them and
+ * treats them as if they were given when first
+ * invoked. Used by the parse module to implement
+ * the .MFLAGS target.
+ *
+ * Error Print a tagged error message. The global
+ * MAKE variable must have been defined. This
+ * takes a format string and two optional
+ * arguments for it.
+ *
+ * Fatal Print an error message and exit. Also takes
+ * a format string and two arguments.
+ *
+ * Punt Aborts all jobs and exits with a message. Also
+ * takes a format string and two arguments.
+ *
+ * Finish Finish things up by printing the number of
+ * errors which occured, as passed to it, and
+ * exiting.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
+#include "pathnames.h"
+
+#ifndef DEFMAXLOCAL
+#define DEFMAXLOCAL DEFMAXJOBS
+#endif DEFMAXLOCAL
+
+#define MAKEFLAGS ".MAKEFLAGS"
+
+Lst create; /* Targets to be made */
+time_t now; /* Time at start of make */
+GNode *DEFAULT; /* .DEFAULT node */
+Boolean allPrecious; /* .PRECIOUS given on line by itself */
+
+static Boolean noBuiltins; /* -r flag */
+static Lst makefiles; /* ordered list of makefiles to read */
+int maxJobs; /* -J argument */
+static int maxLocal; /* -L argument */
+Boolean compatMake; /* -B argument */
+Boolean debug; /* -d flag */
+Boolean noExecute; /* -n flag */
+Boolean keepgoing; /* -k flag */
+Boolean queryFlag; /* -q flag */
+Boolean touchFlag; /* -t flag */
+Boolean usePipes; /* !-P flag */
+Boolean ignoreErrors; /* -i flag */
+Boolean beSilent; /* -s flag */
+Boolean oldVars; /* variable substitution style */
+Boolean checkEnvFirst; /* -e flag */
+static Boolean jobsRunning; /* TRUE if the jobs might be running */
+
+static Boolean ReadMakefile();
+static void usage();
+
+static char *curdir; /* startup directory */
+static char *objdir; /* where we chdir'ed to */
+
+/*-
+ * MainParseArgs --
+ * Parse a given argument vector. Called from main() and from
+ * Main_ParseArgLine() when the .MAKEFLAGS target is used.
+ *
+ * XXX: Deal with command line overriding .MAKEFLAGS in makefile
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Various global and local flags will be set depending on the flags
+ * given
+ */
+static void
+MainParseArgs(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ int c;
+
+ optind = 1; /* since we're called more than once */
+#ifdef notyet
+# define OPTFLAGS "BD:I:L:PSd:ef:ij:knqrst"
+#else
+# define OPTFLAGS "D:I:d:ef:ij:knqrst"
+#endif
+rearg: while((c = getopt(argc, argv, OPTFLAGS)) != EOF) {
+ switch(c) {
+ case 'D':
+ Var_Set(optarg, "1", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+ case 'I':
+ Parse_AddIncludeDir(optarg);
+ Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+#ifdef notyet
+ case 'B':
+ compatMake = TRUE;
+ break;
+ case 'L':
+ maxLocal = atoi(optarg);
+ Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+ case 'P':
+ usePipes = FALSE;
+ Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL);
+ break;
+ case 'S':
+ keepgoing = FALSE;
+ Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
+ break;
+#endif
+ case 'd': {
+ char *modules = optarg;
+
+ for (; *modules; ++modules)
+ switch (*modules) {
+ case 'A':
+ debug = ~0;
+ break;
+ case 'a':
+ debug |= DEBUG_ARCH;
+ break;
+ case 'c':
+ debug |= DEBUG_COND;
+ break;
+ case 'd':
+ debug |= DEBUG_DIR;
+ break;
+ case 'f':
+ debug |= DEBUG_FOR;
+ break;
+ case 'g':
+ if (modules[1] == '1') {
+ debug |= DEBUG_GRAPH1;
+ ++modules;
+ }
+ else if (modules[1] == '2') {
+ debug |= DEBUG_GRAPH2;
+ ++modules;
+ }
+ break;
+ case 'j':
+ debug |= DEBUG_JOB;
+ break;
+ case 'm':
+ debug |= DEBUG_MAKE;
+ break;
+ case 's':
+ debug |= DEBUG_SUFF;
+ break;
+ case 't':
+ debug |= DEBUG_TARG;
+ break;
+ case 'v':
+ debug |= DEBUG_VAR;
+ break;
+ default:
+ (void)fprintf(stderr,
+ "make: illegal argument to d option -- %c\n",
+ *modules);
+ usage();
+ }
+ Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+ }
+ case 'e':
+ checkEnvFirst = TRUE;
+ Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
+ break;
+ case 'f':
+ (void)Lst_AtEnd(makefiles, (ClientData)optarg);
+ break;
+ case 'i':
+ ignoreErrors = TRUE;
+ Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL);
+ break;
+ case 'j':
+ maxJobs = atoi(optarg);
+ Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
+ Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
+ break;
+ case 'k':
+ keepgoing = TRUE;
+ Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL);
+ break;
+ case 'n':
+ noExecute = TRUE;
+ Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL);
+ break;
+ case 'q':
+ queryFlag = TRUE;
+ /* Kind of nonsensical, wot? */
+ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL);
+ break;
+ case 'r':
+ noBuiltins = TRUE;
+ Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL);
+ break;
+ case 's':
+ beSilent = TRUE;
+ Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL);
+ break;
+ case 't':
+ touchFlag = TRUE;
+ Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL);
+ break;
+ default:
+ case '?':
+ usage();
+ }
+ }
+
+ oldVars = TRUE;
+
+ /*
+ * See if the rest of the arguments are variable assignments and
+ * perform them if so. Else take them to be targets and stuff them
+ * on the end of the "create" list.
+ */
+ for (argv += optind, argc -= optind; *argv; ++argv, --argc)
+ if (Parse_IsVar(*argv))
+ Parse_DoVar(*argv, VAR_CMD);
+ else {
+ if (!**argv)
+ Punt("illegal (null) argument.");
+ if (**argv == '-') {
+ if ((*argv)[1])
+ optind = 0; /* -flag... */
+ else
+ optind = 1; /* - */
+ goto rearg;
+ }
+ (void)Lst_AtEnd(create, (ClientData)strdup(*argv));
+ }
+}
+
+/*-
+ * Main_ParseArgLine --
+ * Used by the parse module when a .MFLAGS or .MAKEFLAGS target
+ * is encountered and by main() when reading the .MAKEFLAGS envariable.
+ * Takes a line of arguments and breaks it into its
+ * component words and passes those words and the number of them to the
+ * MainParseArgs function.
+ * The line should have all its leading whitespace removed.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Only those that come from the various arguments.
+ */
+void
+Main_ParseArgLine(line)
+ char *line; /* Line to fracture */
+{
+ char **argv; /* Manufactured argument vector */
+ int argc; /* Number of arguments in argv */
+
+ if (line == NULL)
+ return;
+ for (; *line == ' '; ++line)
+ continue;
+ if (!*line)
+ return;
+
+ argv = brk_string(line, &argc, TRUE);
+ MainParseArgs(argc, argv);
+}
+
+/*-
+ * main --
+ * The main function, for obvious reasons. Initializes variables
+ * and a few modules, then parses the arguments give it in the
+ * environment and on the command line. Reads the system makefile
+ * followed by either Makefile, makefile or the file given by the
+ * -f argument. Sets the .MAKEFLAGS PMake variable based on all the
+ * flags it has received by then uses either the Make or the Compat
+ * module to create the initial list of targets.
+ *
+ * Results:
+ * If -q was given, exits -1 if anything was out-of-date. Else it exits
+ * 0.
+ *
+ * Side Effects:
+ * The program exits when done. Targets are created. etc. etc. etc.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ Lst targs; /* target nodes to create -- passed to Make_Init */
+ Boolean outOfDate = TRUE; /* FALSE if all targets up to date */
+ struct stat sb, sa;
+ char *p, *p1, *path, *pwd, *getenv(), *getwd();
+ char mdpath[MAXPATHLEN + 1];
+ char obpath[MAXPATHLEN + 1];
+ char cdpath[MAXPATHLEN + 1];
+ struct utsname utsname;
+ char *machine = getenv("MACHINE");
+
+ /*
+ * Find where we are and take care of PWD for the automounter...
+ * All this code is so that we know where we are when we start up
+ * on a different machine with pmake.
+ */
+ curdir = cdpath;
+ if (getcwd(curdir, MAXPATHLEN) == NULL) {
+ (void)fprintf(stderr, "make: %s.\n", strerror(errno));
+ exit(2);
+ }
+
+ if (stat(curdir, &sa) == -1) {
+ (void)fprintf(stderr, "make: %s: %s.\n",
+ curdir, strerror(errno));
+ exit(2);
+ }
+
+ if ((pwd = getenv("PWD")) != NULL) {
+ if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino &&
+ sa.st_dev == sb.st_dev)
+ (void) strcpy(curdir, pwd);
+ }
+
+ /*
+ * Get the name of this type of MACHINE from utsname
+ * so we can share an executable for similar machines.
+ * (i.e. m68k: amiga hp300, mac68k, sun3, ...)
+ *
+ * Note that while MACHINE is decided at run-time,
+ * MACHINE_ARCH is always known at compile time.
+ */
+ if (!machine) {
+ if (uname(&utsname)) {
+ perror("make: uname");
+ exit(2);
+ }
+ machine = utsname.machine;
+ }
+
+ /*
+ * if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory
+ * exists, change into it and build there. Once things are
+ * initted, have to add the original directory to the search path,
+ * and modify the paths for the Makefiles apropriately. The
+ * current directory is also placed as a variable for make scripts.
+ */
+ if (!(path = getenv("MAKEOBJDIR"))) {
+ path = _PATH_OBJDIR;
+ (void) sprintf(mdpath, "%s.%s", path, machine);
+ }
+ else
+ (void) strncpy(mdpath, path, MAXPATHLEN + 1);
+
+ if (stat(mdpath, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+
+ if (chdir(mdpath)) {
+ (void)fprintf(stderr, "make warning: %s: %s.\n",
+ mdpath, strerror(errno));
+ objdir = curdir;
+ }
+ else {
+ if (mdpath[0] != '/') {
+ (void) sprintf(obpath, "%s/%s", curdir, mdpath);
+ objdir = obpath;
+ }
+ else
+ objdir = mdpath;
+ }
+ }
+ else {
+ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+
+ if (chdir(path)) {
+ (void)fprintf(stderr, "make warning: %s: %s.\n",
+ path, strerror(errno));
+ objdir = curdir;
+ }
+ else {
+ if (path[0] != '/') {
+ (void) sprintf(obpath, "%s/%s", curdir,
+ path);
+ objdir = obpath;
+ }
+ else
+ objdir = obpath;
+ }
+ }
+ else
+ objdir = curdir;
+ }
+
+ setenv("PWD", objdir, 1);
+
+ create = Lst_Init(FALSE);
+ makefiles = Lst_Init(FALSE);
+ beSilent = FALSE; /* Print commands as executed */
+ ignoreErrors = FALSE; /* Pay attention to non-zero returns */
+ noExecute = FALSE; /* Execute all commands */
+ keepgoing = FALSE; /* Stop on error */
+ allPrecious = FALSE; /* Remove targets when interrupted */
+ queryFlag = FALSE; /* This is not just a check-run */
+ noBuiltins = FALSE; /* Read the built-in rules */
+ touchFlag = FALSE; /* Actually update targets */
+ usePipes = TRUE; /* Catch child output in pipes */
+ debug = 0; /* No debug verbosity, please. */
+ jobsRunning = FALSE;
+
+ maxJobs = DEFMAXJOBS; /* Set default max concurrency */
+ maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */
+#ifdef notyet
+ compatMake = FALSE; /* No compat mode */
+#else
+ compatMake = TRUE; /* No compat mode */
+#endif
+
+
+ /*
+ * Initialize the parsing, directory and variable modules to prepare
+ * for the reading of inclusion paths and variable settings on the
+ * command line
+ */
+ Dir_Init(); /* Initialize directory structures so -I flags
+ * can be processed correctly */
+ Parse_Init(); /* Need to initialize the paths of #include
+ * directories */
+ Var_Init(); /* As well as the lists of variables for
+ * parsing arguments */
+ str_init();
+ if (objdir != curdir)
+ Dir_AddDir(dirSearchPath, curdir);
+ Var_Set(".CURDIR", curdir, VAR_GLOBAL);
+ Var_Set(".OBJDIR", objdir, VAR_GLOBAL);
+
+ /*
+ * Initialize various variables.
+ * MAKE also gets this name, for compatibility
+ * .MAKEFLAGS gets set to the empty string just in case.
+ * MFLAGS also gets initialized empty, for compatibility.
+ */
+ Var_Set("MAKE", argv[0], VAR_GLOBAL);
+ Var_Set(MAKEFLAGS, "", VAR_GLOBAL);
+ Var_Set("MFLAGS", "", VAR_GLOBAL);
+ Var_Set("MACHINE", machine, VAR_GLOBAL);
+#ifdef MACHINE_ARCH
+ Var_Set("MACHINE_ARCH", MACHINE_ARCH, VAR_GLOBAL);
+#endif
+
+ /*
+ * First snag any flags out of the MAKE environment variable.
+ * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's
+ * in a different format).
+ */
+#ifdef POSIX
+ Main_ParseArgLine(getenv("MAKEFLAGS"));
+#else
+ Main_ParseArgLine(getenv("MAKE"));
+#endif
+
+ MainParseArgs(argc, argv);
+
+ /*
+ * Initialize archive, target and suffix modules in preparation for
+ * parsing the makefile(s)
+ */
+ Arch_Init();
+ Targ_Init();
+ Suff_Init();
+
+ DEFAULT = NILGNODE;
+ (void)time(&now);
+
+ /*
+ * Set up the .TARGETS variable to contain the list of targets to be
+ * created. If none specified, make the variable empty -- the parser
+ * will fill the thing in with the default or .MAIN target.
+ */
+ if (!Lst_IsEmpty(create)) {
+ LstNode ln;
+
+ for (ln = Lst_First(create); ln != NILLNODE;
+ ln = Lst_Succ(ln)) {
+ char *name = (char *)Lst_Datum(ln);
+
+ Var_Append(".TARGETS", name, VAR_GLOBAL);
+ }
+ } else
+ Var_Set(".TARGETS", "", VAR_GLOBAL);
+
+ /*
+ * Read in the built-in rules first, followed by the specified makefile,
+ * if it was (makefile != (char *) NULL), or the default Makefile and
+ * makefile, in that order, if it wasn't.
+ */
+ if (!noBuiltins && !ReadMakefile(_PATH_DEFSYSMK))
+ Fatal("make: no system rules (%s).", _PATH_DEFSYSMK);
+
+ if (!Lst_IsEmpty(makefiles)) {
+ LstNode ln;
+
+ ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile);
+ if (ln != NILLNODE)
+ Fatal("make: cannot open %s.", (char *)Lst_Datum(ln));
+ } else if (!ReadMakefile("makefile"))
+ (void)ReadMakefile("Makefile");
+
+ (void)ReadMakefile(".depend");
+
+ Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL);
+ if (p1)
+ free(p1);
+
+ /* Install all the flags into the MAKE envariable. */
+ if (((p = Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1)) != NULL) && *p)
+#ifdef POSIX
+ setenv("MAKEFLAGS", p, 1);
+#else
+ setenv("MAKE", p, 1);
+#endif
+ if (p1)
+ free(p1);
+
+ /*
+ * For compatibility, look at the directories in the VPATH variable
+ * and add them to the search path, if the variable is defined. The
+ * variable's value is in the same format as the PATH envariable, i.e.
+ * <directory>:<directory>:<directory>...
+ */
+ if (Var_Exists("VPATH", VAR_CMD)) {
+ char *vpath, *path, *cp, savec;
+ /*
+ * GCC stores string constants in read-only memory, but
+ * Var_Subst will want to write this thing, so store it
+ * in an array
+ */
+ static char VPATH[] = "${VPATH}";
+
+ vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE);
+ path = vpath;
+ do {
+ /* skip to end of directory */
+ for (cp = path; *cp != ':' && *cp != '\0'; cp++)
+ continue;
+ /* Save terminator character so know when to stop */
+ savec = *cp;
+ *cp = '\0';
+ /* Add directory to search path */
+ Dir_AddDir(dirSearchPath, path);
+ *cp = savec;
+ path = cp + 1;
+ } while (savec == ':');
+ (void)free((Address)vpath);
+ }
+
+ /*
+ * Now that all search paths have been read for suffixes et al, it's
+ * time to add the default search path to their lists...
+ */
+ Suff_DoPaths();
+
+ /* print the initial graph, if the user requested it */
+ if (DEBUG(GRAPH1))
+ Targ_PrintGraph(1);
+
+ /*
+ * Have now read the entire graph and need to make a list of targets
+ * to create. If none was given on the command line, we consult the
+ * parsing module to find the main target(s) to create.
+ */
+ if (Lst_IsEmpty(create))
+ targs = Parse_MainName();
+ else
+ targs = Targ_FindList(create, TARG_CREATE);
+
+/*
+ * this was original amMake -- want to allow parallelism, so put this
+ * back in, eventually.
+ */
+ if (!compatMake) {
+ /*
+ * Initialize job module before traversing the graph, now that
+ * any .BEGIN and .END targets have been read. This is done
+ * only if the -q flag wasn't given (to prevent the .BEGIN from
+ * being executed should it exist).
+ */
+ if (!queryFlag) {
+ if (maxLocal == -1)
+ maxLocal = maxJobs;
+ Job_Init(maxJobs, maxLocal);
+ jobsRunning = TRUE;
+ }
+
+ /* Traverse the graph, checking on all the targets */
+ outOfDate = Make_Run(targs);
+ } else
+ /*
+ * Compat_Init will take care of creating all the targets as
+ * well as initializing the module.
+ */
+ Compat_Run(targs);
+
+ Lst_Destroy(targs, NOFREE);
+ Lst_Destroy(makefiles, NOFREE);
+ Lst_Destroy(create, (void (*) __P((ClientData))) free);
+
+ /* print the graph now it's been processed if the user requested it */
+ if (DEBUG(GRAPH2))
+ Targ_PrintGraph(2);
+
+ Suff_End();
+ Targ_End();
+ Arch_End();
+ str_end();
+ Var_End();
+ Parse_End();
+ Dir_End();
+
+ if (queryFlag && outOfDate)
+ return(1);
+ else
+ return(0);
+}
+
+/*-
+ * ReadMakefile --
+ * Open and parse the given makefile.
+ *
+ * Results:
+ * TRUE if ok. FALSE if couldn't open file.
+ *
+ * Side Effects:
+ * lots
+ */
+static Boolean
+ReadMakefile(fname)
+ char *fname; /* makefile to read */
+{
+ extern Lst parseIncPath, sysIncPath;
+ FILE *stream;
+ char *name, path[MAXPATHLEN + 1];
+
+ if (!strcmp(fname, "-")) {
+ Parse_File("(stdin)", stdin);
+ Var_Set("MAKEFILE", "", VAR_GLOBAL);
+ } else {
+ if ((stream = fopen(fname, "r")) != NULL)
+ goto found;
+ /* if we've chdir'd, rebuild the path name */
+ if (curdir != objdir && *fname != '/') {
+ (void)sprintf(path, "%s/%s", curdir, fname);
+ if ((stream = fopen(path, "r")) != NULL) {
+ fname = path;
+ goto found;
+ }
+ }
+ /* look in -I and system include directories. */
+ name = Dir_FindFile(fname, parseIncPath);
+ if (!name)
+ name = Dir_FindFile(fname, sysIncPath);
+ if (!name || !(stream = fopen(name, "r")))
+ return(FALSE);
+ fname = name;
+ /*
+ * set the MAKEFILE variable desired by System V fans -- the
+ * placement of the setting here means it gets set to the last
+ * makefile specified, as it is set by SysV make.
+ */
+found: Var_Set("MAKEFILE", fname, VAR_GLOBAL);
+ Parse_File(fname, stream);
+ (void)fclose(stream);
+ }
+ return(TRUE);
+}
+
+/*-
+ * Error --
+ * Print an error message given its format.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The message is printed.
+ */
+/* VARARGS */
+void
+#if __STDC__
+Error(char *fmt, ...)
+#else
+Error(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ char *fmt;
+
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+}
+
+/*-
+ * Fatal --
+ * Produce a Fatal error message. If jobs are running, waits for them
+ * to finish.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The program exits
+ */
+/* VARARGS */
+void
+#if __STDC__
+Fatal(char *fmt, ...)
+#else
+Fatal(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ char *fmt;
+
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+ if (jobsRunning)
+ Job_Wait();
+
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+
+ if (DEBUG(GRAPH2))
+ Targ_PrintGraph(2);
+ exit(2); /* Not 1 so -q can distinguish error */
+}
+
+/*
+ * Punt --
+ * Major exception once jobs are being created. Kills all jobs, prints
+ * a message and exits.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * All children are killed indiscriminately and the program Lib_Exits
+ */
+/* VARARGS */
+void
+#if __STDC__
+Punt(char *fmt, ...)
+#else
+Punt(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ char *fmt;
+
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+
+ (void)fprintf(stderr, "make: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+
+ DieHorribly();
+}
+
+/*-
+ * DieHorribly --
+ * Exit without giving a message.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A big one...
+ */
+void
+DieHorribly()
+{
+ if (jobsRunning)
+ Job_AbortAll();
+ if (DEBUG(GRAPH2))
+ Targ_PrintGraph(2);
+ exit(2); /* Not 1, so -q can distinguish error */
+}
+
+/*
+ * Finish --
+ * Called when aborting due to errors in child shell to signal
+ * abnormal exit.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The program exits
+ */
+void
+Finish(errors)
+ int errors; /* number of errors encountered in Make_Make */
+{
+ Fatal("%d error%s", errors, errors == 1 ? "" : "s");
+}
+
+/*
+ * emalloc --
+ * malloc, but die on error.
+ */
+char *
+emalloc(len)
+ size_t len;
+{
+ char *p;
+
+ if ((p = (char *) malloc(len)) == NULL)
+ enomem();
+ return(p);
+}
+
+/*
+ * enomem --
+ * die when out of memory.
+ */
+void
+enomem()
+{
+ (void)fprintf(stderr, "make: %s.\n", strerror(errno));
+ exit(2);
+}
+
+/*
+ * usage --
+ * exit with usage message
+ */
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: make [-eiknqrst] [-D variable] [-d flags] [-f makefile ]\n\
+ [-I directory] [-j max_jobs] [variable=value]\n");
+ exit(2);
+}
+
+
+int
+PrintAddr(a, b)
+ ClientData a;
+ ClientData b;
+{
+ printf("%lx ", (unsigned long) a);
+ return b ? 0 : 0;
+}
diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1
new file mode 100644
index 0000000..9887965
--- /dev/null
+++ b/usr.bin/make/make.1
@@ -0,0 +1,883 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)make.1 8.4 (Berkeley) 3/19/94
+.\"
+.Dd March 19, 1994
+.Dt MAKE 1
+.Os
+.Sh NAME
+.Nm make
+.Nd maintain program dependencies
+.Sh SYNOPSIS
+.Nm make
+.Op Fl eiknqrstv
+.Op Fl D Ar variable
+.Op Fl d Ar flags
+.Op Fl f Ar makefile
+.Op Fl I Ar directory
+.Bk -words
+.Op Fl j Ar max_jobs
+.Ek
+.Op Ar variable=value
+.Op Ar target ...
+.Sh DESCRIPTION
+.Nm Make
+is a program designed to simplify the maintenance of other programs.
+Its input is a list of specifications as to the files upon which programs
+and other files depend.
+If the file
+.Ql Pa makefile
+exists, it is read for this list of specifications.
+If it does not exist, the file
+.Ql Pa Makefile
+is read.
+If the file
+.Ql Pa .depend
+exists, it is read (see
+.Xr mkdep 1) .
+.Pp
+This manual page is intended as a reference document only.
+For a more thorough description of
+.Nm make
+and makefiles, please refer to
+.%T "Make \- A Tutorial" .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D Ar variable
+Define Ar variable
+to be 1, in the global context.
+.It Fl d Ar flags
+Turn on debugging, and specify which portions of
+.Nm make
+are to print debugging information.
+.Ar Flags
+is one or more of the following:
+.Bl -tag -width Ds
+.It Ar A
+Print all possible debugging information;
+equivalent to specifying all of the debugging flags.
+.It Ar a
+Print debugging information about archive searching and caching.
+.It Ar c
+Print debugging information about conditional evaluation.
+.It Ar d
+Print debugging information about directory searching and caching.
+.It Ar "g1"
+Print the input graph before making anything.
+.It Ar "g2"
+Print the input graph after making everything, or before exiting
+on error.
+.It Ar j
+Print debugging information about running multiple shells.
+.It Ar m
+Print debugging information about making targets, including modification
+dates.
+.It Ar s
+Print debugging information about suffix-transformation rules.
+.It Ar t
+Print debugging information about target list maintenance.
+.It Ar v
+Print debugging information about variable assignment.
+.El
+.It Fl e
+Specify that environmental variables override macro assignments within
+makefiles.
+.It Fl f Ar makefile
+Specify a makefile to read instead of the default
+.Ql Pa makefile
+and
+.Ql Pa Makefile .
+If
+.Ar makefile
+is
+.Ql Fl ,
+standard input is read.
+Multiple makefile's may be specified, and are read in the order specified.
+.It Fl I Ar directory
+Specify a directory in which to search for makefiles and included makefiles.
+The system makefile directory is automatically included as part of this
+list.
+.It Fl i
+Ignore non-zero exit of shell commands in the makefile.
+Equivalent to specifying
+.Ql Fl
+before each command line in the makefile.
+.It Fl j Ar max_jobs
+Specify the maximum number of jobs that
+.Nm make
+may have running at any one time.
+.It Fl k
+Continue processing after errors are encountered, but only on those targets
+that do not depend on the target whose creation caused the error.
+.It Fl n
+Display the commands that would have been executed, but do not actually
+execute them.
+.It Fl q
+Do not execute any commands, but exit 0 if the specified targets are
+up-to-date and 1, otherwise.
+.It Fl r
+Do not use the built-in rules specified in the system makefile.
+.It Fl s
+Do not echo any commands as they are executed.
+Equivalent to specifying
+.Ql Ic @
+before each command line in the makefile.
+.It Fl t
+Rather than re-building a target as specified in the makefile, create it
+or update its modification time to make it appear up-to-date.
+.It Ar variable=value
+Set the value of the variable
+.Ar variable
+to
+.Ar value .
+.El
+.Pp
+There are seven different types of lines in a makefile: file dependency
+specifications, shell commands, variable assignments, include statements,
+conditional directives, for loops, and comments.
+.Pp
+In general, lines may be continued from one line to the next by ending
+them with a backslash
+.Pq Ql \e .
+The trailing newline character and initial whitespace on the following
+line are compressed into a single space.
+.Sh FILE DEPENDENCY SPECIFICATIONS
+Dependency lines consist of one or more targets, an operator, and zero
+or more sources.
+This creates a relationship where the targets ``depend'' on the sources
+and are usually created from them.
+The exact relationship between the target and the source is determined
+by the operator that separates them.
+The three operators are as follows:
+.Bl -tag -width flag
+.It Ic \&:
+A target is considered out-of-date if its modification time is less than
+those of any of its sources.
+Sources for a target accumulate over dependency lines when this operator
+is used.
+The target is removed if
+.Nm make
+is interrupted.
+.It Ic \&!
+Targets are always re-created, but not until all sources have been
+examined and re-created as necessary.
+Sources for a target accumulate over dependency lines when this operator
+is used.
+The target is removed if
+.Nm make
+is interrupted.
+.It Ic \&::
+If no sources are specified, the target is always re-created.
+Otherwise, a target is considered out-of-date if any of its sources has
+been modified more recently than the target.
+Sources for a target do not accumulate over dependency lines when this
+operator is used.
+The target will not be removed if
+.Nm make
+is interrupted.
+.El
+.Pp
+Targets and sources may contain the shell wildcard values
+.Ql ? ,
+.Ql * ,
+.Ql []
+and
+.Ql {} .
+The values
+.Ql ? ,
+.Ql *
+and
+.Ql []
+may only be used as part of the final
+component of the target or source, and must be used to describe existing
+files.
+The value
+.Ql {}
+need not necessarily be used to describe existing files.
+Expansion is in directory order, not alphabetically as done in the shell.
+.Sh SHELL COMMANDS
+Each target may have associated with it a series of shell commands, normally
+used to create the target.
+Each of the commands in this script
+.Em must
+be preceded by a tab.
+While any target may appear on a dependency line, only one of these
+dependencies may be followed by a creation script, unless the
+.Ql Ic ::
+operator is used.
+.Pp
+If the first or first two characters of the command line are
+.Ql Ic @
+and/or
+.Ql Ic \- ,
+the command is treated specially.
+A
+.Ql Ic @
+causes the command not to be echoed before it is executed.
+A
+.Ql Ic \-
+causes any non-zero exit status of the command line to be ignored.
+.Sh VARIABLE ASSIGNMENTS
+Variables in make are much like variables in the shell, and, by tradition,
+consist of all upper-case letters.
+The five operators that can be used to assign values to variables are as
+follows:
+.Bl -tag -width Ds
+.It Ic \&=
+Assign the value to the variable.
+Any previous value is overridden.
+.It Ic \&+=
+Append the value to the current value of the variable.
+.It Ic \&?=
+Assign the value to the variable if it is not already defined.
+.It Ic \&:=
+Assign with expansion, i.e. expand the value before assigning it
+to the variable.
+Normally, expansion is not done until the variable is referenced.
+.It Ic \&!=
+Expand the value and pass it to the shell for execution and assign
+the result to the variable.
+Any newlines in the result are replaced with spaces.
+.El
+.Pp
+Any white-space before the assigned
+.Ar value
+is removed; if the value is being appended, a single space is inserted
+between the previous contents of the variable and the appended value.
+.Pp
+Variables are expanded by surrounding the variable name with either
+curly braces
+.Pq Ql {}
+or parenthesis
+.Pq Ql ()
+and preceding it with
+a dollar sign
+.Pq Ql \&$ .
+If the variable name contains only a single letter, the surrounding
+braces or parenthesis are not required.
+This shorter form is not recommended.
+.Pp
+Variable substitution occurs at two distinct times, depending on where
+the variable is being used.
+Variables in dependency lines are expanded as the line is read.
+Variables in shell commands are expanded when the shell command is
+executed.
+.Pp
+The four different classes of variables (in order of increasing precedence)
+are:
+.Bl -tag -width Ds
+.It Environment variables
+Variables defined as part of
+.Nm make Ns 's
+environment.
+.It Global variables
+Variables defined in the makefile or in included makefiles.
+.It Command line variables
+Variables defined as part of the command line.
+.It Local variables
+Variables that are defined specific to a certain target.
+The seven local variables are as follows:
+.Bl -tag -width ".ARCHIVE"
+.It Va .ALLSRC
+The list of all sources for this target; also known as
+.Ql Va \&> .
+.It Va .ARCHIVE
+The name of the archive file.
+.It Va .IMPSRC
+The name/path of the source from which the target is to be transformed
+(the ``implied'' source); also known as
+.Ql Va \&< .
+.It Va .MEMBER
+The name of the archive member.
+.It Va .OODATE
+The list of sources for this target that were deemed out-of-date; also
+known as
+.Ql Va \&? .
+.It Va .PREFIX
+The file prefix of the file, containing only the file portion, no suffix
+or preceding directory components; also known as
+.Ql Va * .
+.It Va .TARGET
+The name of the target; also known as
+.Ql Va @ .
+.El
+.Pp
+The shorter forms
+.Ql Va @ ,
+.Ql Va ? ,
+.Ql Va \&>
+and
+.Ql Va *
+are permitted for backward
+compatibility with historical makefiles and are not recommended.
+The six variables
+.Ql Va "@F" ,
+.Ql Va "@D" ,
+.Ql Va "<F" ,
+.Ql Va "<D" ,
+.Ql Va "*F"
+and
+.Ql Va "*D"
+are
+permitted for compatibility with
+.At V
+makefiles and are not recommended.
+.Pp
+Four of the local variables may be used in sources on dependency lines
+because they expand to the proper value for each target on the line.
+These variables are
+.Ql Va .TARGET ,
+.Ql Va .PREFIX ,
+.Ql Va .ARCHIVE ,
+and
+.Ql Va .MEMBER .
+.Pp
+In addition,
+.Nm make
+sets or knows about the following variables:
+.Bl -tag -width MAKEFLAGS
+.It Va \&$
+A single dollar sign
+.Ql \&$ ,
+i.e.
+.Ql \&$$
+expands to a single dollar
+sign.
+.It Va .MAKE
+The name that
+.Nm make
+was executed with
+.Pq Va argv Op 0
+.It Va .CURDIR
+A path to the directory where
+.Nm make
+was executed.
+.It Va .OBJDIR
+A path to the directory where the targets are built.
+.It Ev MAKEFLAGS
+The environment variable
+.Ql Ev MAKEFLAGS
+may contain anything that
+may be specified on
+.Nm make Ns 's
+command line.
+Anything specified on
+.Nm make Ns 's
+command line is appended to the
+.Ql Ev MAKEFLAGS
+variable which is then
+entered into the environment for all programs which
+.Nm make
+executes.
+.El
+.Pp
+Variable expansion may be modified to select or modify each word of the
+variable (where a ``word'' is white-space delimited sequence of characters).
+The general format of a variable expansion is as follows:
+.Pp
+.Dl {variable[:modifier[:...]]}
+.Pp
+Each modifier begins with a colon and one of the following
+special characters.
+The colon may be escaped with a backslash
+.Pq Ql \e .
+.Bl -tag -width Cm E\&
+.It Cm E
+Replaces each word in the variable with its suffix.
+.It Cm H
+Replaces each word in the variable with everything but the last component.
+.It Cm M Ns Ar pattern
+Select only those words that match the rest of the modifier.
+The standard shell wildcard characters
+.Pf ( Ql * ,
+.Ql ? ,
+and
+.Ql Op )
+may
+be used.
+The wildcard characters may be escaped with a backslash
+.Pq Ql \e .
+.It Cm N Ns Ar pattern
+This is identical to
+.Ql Cm M ,
+but selects all words which do not match
+the rest of the modifier.
+.It Cm R
+Replaces each word in the variable with everything but its suffix.
+.Sm off
+.It Cm S No \&/ Ar old_pattern Xo
+.No \&/ Ar new_pattern
+.No \&/ Op Cm g
+.Xc
+.Sm on
+Modify the first occurrence of
+.Ar old_pattern
+in each word to be replaced with
+.Ar new_pattern .
+If a
+.Ql g
+is appended to the last slash of the pattern, all occurrences
+in each word are replaced.
+If
+.Ar old_pattern
+begins with a carat
+.Pq Ql ^ ,
+.Ar old_pattern
+is anchored at the beginning of each word.
+If
+.Ar old_pattern
+ends with a dollar sign
+.Pq Ql \&$ ,
+it is anchored at the end of each word.
+Inside
+.Ar new_string ,
+an ampersand
+.Pq Ql &
+is replaced by
+.Ar old_pattern .
+Any character may be used as a delimiter for the parts of the modifier
+string.
+The anchoring, ampersand and delimiter characters may be escaped with a
+backslash
+.Pq Ql \e .
+.Pp
+Variable expansion occurs in the normal fashion inside both
+.Ar old_string
+and
+.Ar new_string
+with the single exception that a backslash is used to prevent the expansion
+of a dollar sign
+.Pq Ql \&$
+not a preceding dollar sign as is usual.
+.It Cm T
+Replaces each word in the variable with its last component.
+.It Ar old_string=new_string
+This is the
+.At V
+style variable substitution.
+It must be the last modifier specified.
+If
+.Ar old_string
+or
+.Ar new_string
+do not contain the pattern matching character
+.Ar %
+then it is assumed that they are
+anchored at the end of each word, so only suffixes or entire
+words may be replaced. Otherwise
+.Ar %
+is the substring of
+.Ar old_string
+to be replaced in
+.Ar new_string
+.El
+.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
+Makefile inclusion, conditional structures and for loops reminiscent
+of the C programming language are provided in
+.Nm make .
+All such structures are identified by a line beginning with a single
+dot
+.Pq Ql \&.
+character.
+Files are included with either
+.Ql .include <file>
+or
+.Ql .include \*qfile\*q .
+Variables between the angle brackets or double quotes are expanded
+to form the file name.
+If angle brackets are used, the included makefile is expected to be in
+the system makefile directory.
+If double quotes are used, the including makefile's directory and any
+directories specified using the
+.Fl I
+option are searched before the system
+makefile directory.
+.Pp
+Conditional expressions are also preceded by a single dot as the first
+character of a line.
+The possible conditionals are as follows:
+.Bl -tag -width Ds
+.It Ic .undef Ar variable
+Un-define the specified global variable.
+Only global variables may be un-defined.
+.It Xo
+.Ic \&.if
+.Oo \&! Oc Ns Ar expression
+.Op Ar operator expression ...
+.Xc
+Test the value of an expression.
+.It Xo
+.Ic .ifdef
+.Oo \&! Oc Ns Ar variable
+.Op Ar operator variable ...
+.Xc
+Test the value of a variable.
+.It Xo
+.Ic .ifndef
+.Oo \&! Oc Ns Ar variable
+.Op Ar operator variable ...
+.Xc
+Test the value of a variable.
+.It Xo
+.Ic .ifmake
+.Oo \&! Oc Ns Ar target
+.Op Ar operator target ...
+.Xc
+Test the the target being built.
+.It Xo
+.Ic .ifnmake
+.Oo \&! Oc Ar target
+.Op Ar operator target ...
+.Xc
+Test the target being built.
+.It Ic .else
+Reverse the sense of the last conditional.
+.It Xo
+.Ic .elif
+.Oo \&! Oc Ar expression
+.Op Ar operator expression ...
+.Xc
+A combination of
+.Ql Ic .else
+followed by
+.Ql Ic .if .
+.It Xo
+.Ic .elifdef
+.Oo \&! Oc Ns Ar variable
+.Op Ar operator variable ...
+.Xc
+A combination of
+.Ql Ic .else
+followed by
+.Ql Ic .ifdef .
+.It Xo
+.Ic .elifndef
+.Oo \&! Oc Ns Ar variable
+.Op Ar operator variable ...
+.Xc
+A combination of
+.Ql Ic .else
+followed by
+.Ql Ic .ifndef .
+.It Xo
+.Ic .elifmake
+.Oo \&! Oc Ns Ar target
+.Op Ar operator target ...
+.Xc
+A combination of
+.Ql Ic .else
+followed by
+.Ql Ic .ifmake .
+.It Xo
+.Ic .elifnmake
+.Oo \&! Oc Ns Ar target
+.Op Ar operator target ...
+.Xc
+A combination of
+.Ql Ic .else
+followed by
+.Ql Ic .ifnmake .
+.It Ic .endif
+End the body of the conditional.
+.El
+.Pp
+The
+.Ar operator
+may be any one of the following:
+.Bl -tag -width "Cm XX"
+.It Cm \&|\&|
+logical OR
+.It Cm \&&&
+Logical
+.Tn AND ;
+of higher precedence than
+.Dq .
+.El
+.Pp
+As in C,
+.Nm make
+will only evaluate a conditional as far as is necessary to determine
+its value.
+Parentheses may be used to change the order of evaluation.
+The boolean operator
+.Ql Ic \&!
+may be used to logically negate an entire
+conditional.
+It is of higher precedence than
+.Ql Ic \&&& .
+.Pp
+The value of
+.Ar expression
+may be any of the following:
+.Bl -tag -width Ic defined
+.It Ic defined
+Takes a variable name as an argument and evaluates to true if the variable
+has been defined.
+.It Ic make
+Takes a target name as an argument and evaluates to true if the target
+was specified as part of
+.Nm make Ns 's
+command line or was declared the default target (either implicitly or
+explicitly, see
+.Va .MAIN )
+before the line containing the conditional.
+.It Ic empty
+Takes a variable, with possible modifiers, and evaluates to true if
+the expansion of the variable would result in an empty string.
+.It Ic exists
+Takes a file name as an argument and evaluates to true if the file exists.
+The file is searched for on the system search path (see
+.Va .PATH ) .
+.It Ic target
+Takes a target name as an argument and evaluates to true if the target
+has been defined.
+.El
+.Pp
+.Ar Expression
+may also be an arithmetic or string comparison. Variable expansion is
+performed on both sides of the comparison, after which the integral
+values are compared. A value is interpreted as hexadecimal if it is
+preceded by 0x, otherwise it is decimal; octal numbers are not supported.
+The standard C relational operators are all supported. If after
+variable expansion, either the left or right hand side of a
+.Ql Ic ==
+or
+.Ql Ic "!="
+operator is not an integral value, then
+string comparison is performed between the expanded
+variables.
+If no relational operator is given, it is assumed that the expanded
+variable is being compared against 0.
+.Pp
+When
+.Nm make
+is evaluating one of these conditional expression, and it encounters
+a word it doesn't recognize, either the ``make'' or ``defined''
+expression is applied to it, depending on the form of the conditional.
+If the form is
+.Ql Ic .ifdef
+or
+.Ql Ic .ifndef ,
+the ``defined'' expression
+is applied.
+Similarly, if the form is
+.Ql Ic .ifmake
+or
+.Ql Ic .ifnmake , the ``make''
+expression is applied.
+.Pp
+If the conditional evaluates to true the parsing of the makefile continues
+as before.
+If it evaluates to false, the following lines are skipped.
+In both cases this continues until a
+.Ql Ic .else
+or
+.Ql Ic .endif
+is found.
+.Pp
+For loops are typically used to apply a set of rules to a list of files.
+The syntax of a for loop is:
+.Bl -tag -width Ds
+.It Xo
+.Ic \&.for
+.Ar variable
+.Ic in
+.Ar expression
+.Xc
+.It Xo
+<make-rules>
+.Xc
+.It Xo
+.Ic \&.endfor
+.Xc
+.El
+After the for
+.Ic expression
+is evaluated, it is split into words. The
+iteration
+.Ic variable
+is successively set to each word, and substituted in the
+.Ic make-rules
+inside the body of the for loop.
+.Sh COMMENTS
+Comments begin with a hash
+.Pq Ql \&#
+character, anywhere but in a shell
+command line, and continue to the end of the line.
+.Sh SPECIAL SOURCES
+.Bl -tag -width Ic .IGNORE
+.It Ic .IGNORE
+Ignore any errors from the commands associated with this target, exactly
+as if they all were preceded by a dash
+.Pq Ql \- .
+.It Ic .MAKE
+Execute the commands associated with this target even if the
+.Fl n
+or
+.Fl t
+options were specified.
+Normally used to mark recursive
+.Nm make Ns 's .
+.It Ic .NOTMAIN
+Normally
+.Nm make
+selects the first target it encounters as the default target to be built
+if no target was specified.
+This source prevents this target from being selected.
+.It Ic .OPTIONAL
+If a target is marked with this attribute and
+.Nm make
+can't figure out how to create it, it will ignore this fact and assume
+the file isn't needed or already exists.
+.It Ic .PRECIOUS
+When
+.Nm make
+is interrupted, it removes any partially made targets.
+This source prevents the target from being removed.
+.It Ic .SILENT
+Do not echo any of the commands associated with this target, exactly
+as if they all were preceded by an at sign
+.Pq Ql @ .
+.It Ic .USE
+Turn the target into
+.Nm make Ns 's .
+version of a macro.
+When the target is used as a source for another target, the other target
+acquires the commands, sources, and attributes (except for
+.Ic .USE )
+of the
+source.
+If the target already has commands, the
+.Ic .USE
+target's commands are appended
+to them.
+.El
+.Sh "SPECIAL TARGETS"
+Special targets may not be included with other targets, i.e. they must be
+the only target specified.
+.Bl -tag -width Ic .BEGIN
+.It Ic .BEGIN
+Any command lines attached to this target are executed before anything
+else is done.
+.It Ic .DEFAULT
+This is sort of a
+.Ic .USE
+rule for any target (that was used only as a
+source) that
+.Nm make
+can't figure out any other way to create.
+Only the shell script is used.
+The
+.Ic .IMPSRC
+variable of a target that inherits
+.Ic .DEFAULT Ns 's
+commands is set
+to the target's own name.
+.It Ic .END
+Any command lines attached to this target are executed after everything
+else is done.
+.It Ic .IGNORE
+Mark each of the sources with the
+.Ic .IGNORE
+attribute.
+If no sources are specified, this is the equivalent of specifying the
+.Fl i
+option.
+.It Ic .INTERRUPT
+If
+.Nm make
+is interrupted, the commands for this target will be executed.
+.It Ic .MAIN
+If no target is specified when
+.Nm make
+is invoked, this target will be built.
+.It Ic .MAKEFLAGS
+This target provides a way to specify flags for
+.Nm make
+when the makefile is used.
+The flags are as if typed to the shell, though the
+.Fl f
+option will have
+no effect.
+.It Ic .PATH
+The sources are directories which are to be searched for files not
+found in the current directory.
+If no sources are specified, any previously specified directories are
+deleted.
+.It Ic .PRECIOUS
+Apply the
+.Ic .PRECIOUS
+attribute to any specified sources.
+If no sources are specified, the
+.Ic .PRECIOUS
+attribute is applied to every
+target in the file.
+.It Ic .SILENT
+Apply the
+.Ic .SILENT
+attribute to any specified sources.
+If no sources are specified, the
+.Ic .SILENT
+attribute is applied to every
+command in the file.
+.It Ic .SUFFIXES
+Each source specifies a suffix to
+.Nm make .
+If no sources are specified, any previous specified suffices are deleted.
+.Sh ENVIRONMENT
+.Nm Make
+utilizes the following environment variables, if they exist:
+.Ev MAKE ,
+.Ev MAKEFLAGS
+and
+.Ev MAKEOBJDIR .
+.Sh FILES
+.Bl -tag -width /usr/share/mk -compact
+.It .depend
+list of dependencies
+.It Makefile
+list of dependencies
+.It makefile
+list of dependencies
+.It sys.mk
+system makefile
+.It /usr/share/mk
+system makefile directory
+.El
+.Sh SEE ALSO
+.Xr mkdep 1
+.Sh HISTORY
+A
+.Nm Make
+command appeared in
+.At v7 .
diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c
new file mode 100644
index 0000000..d6959a8
--- /dev/null
+++ b/usr.bin/make/make.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*-
+ * make.c --
+ * The functions which perform the examination of targets and
+ * their suitability for creation
+ *
+ * Interface:
+ * Make_Run Initialize things for the module and recreate
+ * whatever needs recreating. Returns TRUE if
+ * work was (or would have been) done and FALSE
+ * otherwise.
+ *
+ * Make_Update Update all parents of a given child. Performs
+ * various bookkeeping chores like the updating
+ * of the cmtime field of the parent, filling
+ * of the IMPSRC context variable, etc. It will
+ * place the parent on the toBeMade queue if it
+ * should be.
+ *
+ * Make_TimeStamp Function to set the parent's cmtime field
+ * based on a child's modification time.
+ *
+ * Make_DoAllVar Set up the various local variables for a
+ * target, including the .ALLSRC variable, making
+ * sure that any variable that needs to exist
+ * at the very least has the empty value.
+ *
+ * Make_OODate Determine if a target is out-of-date.
+ *
+ * Make_HandleUse See if a child is a .USE node for a parent
+ * and perform the .USE actions if so.
+ */
+
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
+
+static Lst toBeMade; /* The current fringe of the graph. These
+ * are nodes which await examination by
+ * MakeOODate. It is added to by
+ * Make_Update and subtracted from by
+ * MakeStartJobs */
+static int numNodes; /* Number of nodes to be processed. If this
+ * is non-zero when Job_Empty() returns
+ * TRUE, there's a cycle in the graph */
+
+static int MakeAddChild __P((ClientData, ClientData));
+static int MakeAddAllSrc __P((ClientData, ClientData));
+static int MakeTimeStamp __P((ClientData, ClientData));
+static int MakeHandleUse __P((ClientData, ClientData));
+static Boolean MakeStartJobs __P((void));
+static int MakePrintStatus __P((ClientData, ClientData));
+/*-
+ *-----------------------------------------------------------------------
+ * Make_TimeStamp --
+ * Set the cmtime field of a parent node based on the mtime stamp in its
+ * child. Called from MakeOODate via Lst_ForEach.
+ *
+ * Results:
+ * Always returns 0.
+ *
+ * Side Effects:
+ * The cmtime of the parent node will be changed if the mtime
+ * field of the child is greater than it.
+ *-----------------------------------------------------------------------
+ */
+int
+Make_TimeStamp (pgn, cgn)
+ GNode *pgn; /* the current parent */
+ GNode *cgn; /* the child we've just examined */
+{
+ if (cgn->mtime > pgn->cmtime) {
+ pgn->cmtime = cgn->mtime;
+ }
+ return (0);
+}
+
+static int
+MakeTimeStamp (pgn, cgn)
+ ClientData pgn; /* the current parent */
+ ClientData cgn; /* the child we've just examined */
+{
+ return Make_TimeStamp((GNode *) pgn, (GNode *) cgn);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Make_OODate --
+ * See if a given node is out of date with respect to its sources.
+ * Used by Make_Run when deciding which nodes to place on the
+ * toBeMade queue initially and by Make_Update to screen out USE and
+ * EXEC nodes. In the latter case, however, any other sort of node
+ * must be considered out-of-date since at least one of its children
+ * will have been recreated.
+ *
+ * Results:
+ * TRUE if the node is out of date. FALSE otherwise.
+ *
+ * Side Effects:
+ * The mtime field of the node and the cmtime field of its parents
+ * will/may be changed.
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Make_OODate (gn)
+ register GNode *gn; /* the node to check */
+{
+ Boolean oodate;
+
+ /*
+ * Certain types of targets needn't even be sought as their datedness
+ * doesn't depend on their modification time...
+ */
+ if ((gn->type & (OP_JOIN|OP_USE|OP_EXEC)) == 0) {
+ (void) Dir_MTime (gn);
+ if (DEBUG(MAKE)) {
+ if (gn->mtime != 0) {
+ printf ("modified %s...", Targ_FmtTime(gn->mtime));
+ } else {
+ printf ("non-existent...");
+ }
+ }
+ }
+
+ /*
+ * A target is remade in one of the following circumstances:
+ * its modification time is smaller than that of its youngest child
+ * and it would actually be run (has commands or type OP_NOP)
+ * it's the object of a force operator
+ * it has no children, was on the lhs of an operator and doesn't exist
+ * already.
+ *
+ * Libraries are only considered out-of-date if the archive module says
+ * they are.
+ *
+ * These weird rules are brought to you by Backward-Compatability and
+ * the strange people who wrote 'Make'.
+ */
+ if (gn->type & OP_USE) {
+ /*
+ * If the node is a USE node it is *never* out of date
+ * no matter *what*.
+ */
+ if (DEBUG(MAKE)) {
+ printf(".USE node...");
+ }
+ oodate = FALSE;
+ } else if (gn->type & OP_LIB) {
+ if (DEBUG(MAKE)) {
+ printf("library...");
+ }
+
+ /*
+ * always out of date if no children and :: target
+ */
+
+ oodate = Arch_LibOODate (gn) ||
+ ((gn->cmtime == 0) && (gn->type & OP_DOUBLEDEP));
+ } else if (gn->type & OP_JOIN) {
+ /*
+ * A target with the .JOIN attribute is only considered
+ * out-of-date if any of its children was out-of-date.
+ */
+ if (DEBUG(MAKE)) {
+ printf(".JOIN node...");
+ }
+ oodate = gn->childMade;
+ } else if (gn->type & (OP_FORCE|OP_EXEC)) {
+ /*
+ * A node which is the object of the force (!) operator or which has
+ * the .EXEC attribute is always considered out-of-date.
+ */
+ if (DEBUG(MAKE)) {
+ if (gn->type & OP_FORCE) {
+ printf("! operator...");
+ } else {
+ printf(".EXEC node...");
+ }
+ }
+ oodate = TRUE;
+ } else if ((gn->mtime < gn->cmtime) ||
+ ((gn->cmtime == 0) &&
+ ((gn->mtime==0) || (gn->type & OP_DOUBLEDEP))))
+ {
+ /*
+ * A node whose modification time is less than that of its
+ * youngest child or that has no children (cmtime == 0) and
+ * either doesn't exist (mtime == 0) or was the object of a
+ * :: operator is out-of-date. Why? Because that's the way Make does
+ * it.
+ */
+ if (DEBUG(MAKE)) {
+ if (gn->mtime < gn->cmtime) {
+ printf("modified before source...");
+ } else if (gn->mtime == 0) {
+ printf("non-existent and no sources...");
+ } else {
+ printf(":: operator and no sources...");
+ }
+ }
+ oodate = TRUE;
+ } else {
+#if 0
+ /* WHY? */
+ if (DEBUG(MAKE)) {
+ printf("source %smade...", gn->childMade ? "" : "not ");
+ }
+ oodate = gn->childMade;
+#else
+ oodate = FALSE;
+#endif /* 0 */
+ }
+
+ /*
+ * If the target isn't out-of-date, the parents need to know its
+ * modification time. Note that targets that appear to be out-of-date
+ * but aren't, because they have no commands and aren't of type OP_NOP,
+ * have their mtime stay below their children's mtime to keep parents from
+ * thinking they're out-of-date.
+ */
+ if (!oodate) {
+ Lst_ForEach (gn->parents, MakeTimeStamp, (ClientData)gn);
+ }
+
+ return (oodate);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * MakeAddChild --
+ * Function used by Make_Run to add a child to the list l.
+ * It will only add the child if its make field is FALSE.
+ *
+ * Results:
+ * Always returns 0
+ *
+ * Side Effects:
+ * The given list is extended
+ *-----------------------------------------------------------------------
+ */
+static int
+MakeAddChild (gnp, lp)
+ ClientData gnp; /* the node to add */
+ ClientData lp; /* the list to which to add it */
+{
+ GNode *gn = (GNode *) gnp;
+ Lst l = (Lst) lp;
+ if (!gn->make && !(gn->type & OP_USE)) {
+ (void)Lst_EnQueue (l, (ClientData)gn);
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Make_HandleUse --
+ * Function called by Make_Run and SuffApplyTransform on the downward
+ * pass to handle .USE and transformation nodes. A callback function
+ * for Lst_ForEach, it implements the .USE and transformation
+ * functionality by copying the node's commands, type flags
+ * and children to the parent node. Should be called before the
+ * children are enqueued to be looked at by MakeAddChild.
+ *
+ * A .USE node is much like an explicit transformation rule, except
+ * its commands are always added to the target node, even if the
+ * target already has commands.
+ *
+ * Results:
+ * returns 0.
+ *
+ * Side Effects:
+ * Children and commands may be added to the parent and the parent's
+ * type may be changed.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Make_HandleUse (cgn, pgn)
+ register GNode *cgn; /* The .USE node */
+ register GNode *pgn; /* The target of the .USE node */
+{
+ register GNode *gn; /* A child of the .USE node */
+ register LstNode ln; /* An element in the children list */
+
+ if (cgn->type & (OP_USE|OP_TRANSFORM)) {
+ if ((cgn->type & OP_USE) || Lst_IsEmpty(pgn->commands)) {
+ /*
+ * .USE or transformation and target has no commands -- append
+ * the child's commands to the parent.
+ */
+ (void) Lst_Concat (pgn->commands, cgn->commands, LST_CONCNEW);
+ }
+
+ if (Lst_Open (cgn->children) == SUCCESS) {
+ while ((ln = Lst_Next (cgn->children)) != NILLNODE) {
+ gn = (GNode *)Lst_Datum (ln);
+
+ if (Lst_Member (pgn->children, gn) == NILLNODE) {
+ (void) Lst_AtEnd (pgn->children, gn);
+ (void) Lst_AtEnd (gn->parents, pgn);
+ pgn->unmade += 1;
+ }
+ }
+ Lst_Close (cgn->children);
+ }
+
+ pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM);
+
+ /*
+ * This child node is now "made", so we decrement the count of
+ * unmade children in the parent... We also remove the child
+ * from the parent's list to accurately reflect the number of decent
+ * children the parent has. This is used by Make_Run to decide
+ * whether to queue the parent or examine its children...
+ */
+ if (cgn->type & OP_USE) {
+ pgn->unmade -= 1;
+ }
+ }
+ return (0);
+}
+static int
+MakeHandleUse (pgn, cgn)
+ ClientData pgn; /* the current parent */
+ ClientData cgn; /* the child we've just examined */
+{
+ return Make_HandleUse((GNode *) pgn, (GNode *) cgn);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Make_Update --
+ * Perform update on the parents of a node. Used by JobFinish once
+ * a node has been dealt with and by MakeStartJobs if it finds an
+ * up-to-date node.
+ *
+ * Results:
+ * Always returns 0
+ *
+ * Side Effects:
+ * The unmade field of pgn is decremented and pgn may be placed on
+ * the toBeMade queue if this field becomes 0.
+ *
+ * If the child was made, the parent's childMade field will be set true
+ * and its cmtime set to now.
+ *
+ * If the child wasn't made, the cmtime field of the parent will be
+ * altered if the child's mtime is big enough.
+ *
+ * Finally, if the child is the implied source for the parent, the
+ * parent's IMPSRC variable is set appropriately.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Make_Update (cgn)
+ register GNode *cgn; /* the child node */
+{
+ register GNode *pgn; /* the parent node */
+ register char *cname; /* the child's name */
+ register LstNode ln; /* Element in parents and iParents lists */
+ char *p1;
+
+ cname = Var_Value (TARGET, cgn, &p1);
+ if (p1)
+ free(p1);
+
+ /*
+ * If the child was actually made, see what its modification time is
+ * now -- some rules won't actually update the file. If the file still
+ * doesn't exist, make its mtime now.
+ */
+ if (cgn->made != UPTODATE) {
+#ifndef RECHECK
+ /*
+ * We can't re-stat the thing, but we can at least take care of rules
+ * where a target depends on a source that actually creates the
+ * target, but only if it has changed, e.g.
+ *
+ * parse.h : parse.o
+ *
+ * parse.o : parse.y
+ * yacc -d parse.y
+ * cc -c y.tab.c
+ * mv y.tab.o parse.o
+ * cmp -s y.tab.h parse.h || mv y.tab.h parse.h
+ *
+ * In this case, if the definitions produced by yacc haven't changed
+ * from before, parse.h won't have been updated and cgn->mtime will
+ * reflect the current modification time for parse.h. This is
+ * something of a kludge, I admit, but it's a useful one..
+ * XXX: People like to use a rule like
+ *
+ * FRC:
+ *
+ * To force things that depend on FRC to be made, so we have to
+ * check for gn->children being empty as well...
+ */
+ if (!Lst_IsEmpty(cgn->commands) || Lst_IsEmpty(cgn->children)) {
+ cgn->mtime = now;
+ }
+#else
+ /*
+ * This is what Make does and it's actually a good thing, as it
+ * allows rules like
+ *
+ * cmp -s y.tab.h parse.h || cp y.tab.h parse.h
+ *
+ * to function as intended. Unfortunately, thanks to the stateless
+ * nature of NFS (by which I mean the loose coupling of two clients
+ * using the same file from a common server), there are times
+ * when the modification time of a file created on a remote
+ * machine will not be modified before the local stat() implied by
+ * the Dir_MTime occurs, thus leading us to believe that the file
+ * is unchanged, wreaking havoc with files that depend on this one.
+ *
+ * I have decided it is better to make too much than to make too
+ * little, so this stuff is commented out unless you're sure it's ok.
+ * -- ardeb 1/12/88
+ */
+ /*
+ * Christos, 4/9/92: If we are saving commands pretend that
+ * the target is made now. Otherwise archives with ... rules
+ * don't work!
+ */
+ if (noExecute || (cgn->type & OP_SAVE_CMDS) || Dir_MTime(cgn) == 0) {
+ cgn->mtime = now;
+ }
+ if (DEBUG(MAKE)) {
+ printf("update time: %s\n", Targ_FmtTime(cgn->mtime));
+ }
+#endif
+ }
+
+ if (Lst_Open (cgn->parents) == SUCCESS) {
+ while ((ln = Lst_Next (cgn->parents)) != NILLNODE) {
+ pgn = (GNode *)Lst_Datum (ln);
+ if (pgn->make) {
+ pgn->unmade -= 1;
+
+ if ( ! (cgn->type & (OP_EXEC|OP_USE))) {
+ if (cgn->made == MADE) {
+ pgn->childMade = TRUE;
+ if (pgn->cmtime < cgn->mtime) {
+ pgn->cmtime = cgn->mtime;
+ }
+ } else {
+ (void)Make_TimeStamp (pgn, cgn);
+ }
+ }
+ if (pgn->unmade == 0) {
+ /*
+ * Queue the node up -- any unmade predecessors will
+ * be dealt with in MakeStartJobs.
+ */
+ (void)Lst_EnQueue (toBeMade, (ClientData)pgn);
+ } else if (pgn->unmade < 0) {
+ Error ("Graph cycles through %s", pgn->name);
+ }
+ }
+ }
+ Lst_Close (cgn->parents);
+ }
+ /*
+ * Deal with successor nodes. If any is marked for making and has an unmade
+ * count of 0, has not been made and isn't in the examination queue,
+ * it means we need to place it in the queue as it restrained itself
+ * before.
+ */
+ for (ln = Lst_First(cgn->successors); ln != NILLNODE; ln = Lst_Succ(ln)) {
+ GNode *succ = (GNode *)Lst_Datum(ln);
+
+ if (succ->make && succ->unmade == 0 && succ->made == UNMADE &&
+ Lst_Member(toBeMade, (ClientData)succ) == NILLNODE)
+ {
+ (void)Lst_EnQueue(toBeMade, (ClientData)succ);
+ }
+ }
+
+ /*
+ * Set the .PREFIX and .IMPSRC variables for all the implied parents
+ * of this node.
+ */
+ if (Lst_Open (cgn->iParents) == SUCCESS) {
+ char *p1;
+ char *cpref = Var_Value(PREFIX, cgn, &p1);
+
+ while ((ln = Lst_Next (cgn->iParents)) != NILLNODE) {
+ pgn = (GNode *)Lst_Datum (ln);
+ if (pgn->make) {
+ Var_Set (IMPSRC, cname, pgn);
+ Var_Set (PREFIX, cpref, pgn);
+ }
+ }
+ if (p1)
+ free(p1);
+ Lst_Close (cgn->iParents);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * MakeAddAllSrc --
+ * Add a child's name to the ALLSRC and OODATE variables of the given
+ * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only
+ * if it has not been given the .EXEC, .USE or .INVISIBLE attributes.
+ * .EXEC and .USE children are very rarely going to be files, so...
+ * A child is added to the OODATE variable if its modification time is
+ * later than that of its parent, as defined by Make, except if the
+ * parent is a .JOIN node. In that case, it is only added to the OODATE
+ * variable if it was actually made (since .JOIN nodes don't have
+ * modification times, the comparison is rather unfair...)..
+ *
+ * Results:
+ * Always returns 0
+ *
+ * Side Effects:
+ * The ALLSRC variable for the given node is extended.
+ *-----------------------------------------------------------------------
+ */
+static int
+MakeAddAllSrc (cgnp, pgnp)
+ ClientData cgnp; /* The child to add */
+ ClientData pgnp; /* The parent to whose ALLSRC variable it should be */
+ /* added */
+{
+ GNode *cgn = (GNode *) cgnp;
+ GNode *pgn = (GNode *) pgnp;
+ if ((cgn->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) == 0) {
+ char *child;
+ char *p1;
+
+ child = Var_Value(TARGET, cgn, &p1);
+ Var_Append (ALLSRC, child, pgn);
+ if (pgn->type & OP_JOIN) {
+ if (cgn->made == MADE) {
+ Var_Append(OODATE, child, pgn);
+ }
+ } else if ((pgn->mtime < cgn->mtime) ||
+ (cgn->mtime >= now && cgn->made == MADE))
+ {
+ /*
+ * It goes in the OODATE variable if the parent is younger than the
+ * child or if the child has been modified more recently than
+ * the start of the make. This is to keep pmake from getting
+ * confused if something else updates the parent after the
+ * make starts (shouldn't happen, I know, but sometimes it
+ * does). In such a case, if we've updated the kid, the parent
+ * is likely to have a modification time later than that of
+ * the kid and anything that relies on the OODATE variable will
+ * be hosed.
+ *
+ * XXX: This will cause all made children to go in the OODATE
+ * variable, even if they're not touched, if RECHECK isn't defined,
+ * since cgn->mtime is set to now in Make_Update. According to
+ * some people, this is good...
+ */
+ Var_Append(OODATE, child, pgn);
+ }
+ if (p1)
+ free(p1);
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Make_DoAllVar --
+ * Set up the ALLSRC and OODATE variables. Sad to say, it must be
+ * done separately, rather than while traversing the graph. This is
+ * because Make defined OODATE to contain all sources whose modification
+ * times were later than that of the target, *not* those sources that
+ * were out-of-date. Since in both compatibility and native modes,
+ * the modification time of the parent isn't found until the child
+ * has been dealt with, we have to wait until now to fill in the
+ * variable. As for ALLSRC, the ordering is important and not
+ * guaranteed when in native mode, so it must be set here, too.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The ALLSRC and OODATE variables of the given node is filled in.
+ * If the node is a .JOIN node, its TARGET variable will be set to
+ * match its ALLSRC variable.
+ *-----------------------------------------------------------------------
+ */
+void
+Make_DoAllVar (gn)
+ GNode *gn;
+{
+ Lst_ForEach (gn->children, MakeAddAllSrc, (ClientData) gn);
+
+ if (!Var_Exists (OODATE, gn)) {
+ Var_Set (OODATE, "", gn);
+ }
+ if (!Var_Exists (ALLSRC, gn)) {
+ Var_Set (ALLSRC, "", gn);
+ }
+
+ if (gn->type & OP_JOIN) {
+ char *p1;
+ Var_Set (TARGET, Var_Value (ALLSRC, gn, &p1), gn);
+ if (p1)
+ free(p1);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * MakeStartJobs --
+ * Start as many jobs as possible.
+ *
+ * Results:
+ * If the query flag was given to pmake, no job will be started,
+ * but as soon as an out-of-date target is found, this function
+ * returns TRUE. At all other times, this function returns FALSE.
+ *
+ * Side Effects:
+ * Nodes are removed from the toBeMade queue and job table slots
+ * are filled.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+MakeStartJobs ()
+{
+ register GNode *gn;
+
+ while (!Job_Full() && !Lst_IsEmpty (toBeMade)) {
+ gn = (GNode *) Lst_DeQueue (toBeMade);
+ if (DEBUG(MAKE)) {
+ printf ("Examining %s...", gn->name);
+ }
+ /*
+ * Make sure any and all predecessors that are going to be made,
+ * have been.
+ */
+ if (!Lst_IsEmpty(gn->preds)) {
+ LstNode ln;
+
+ for (ln = Lst_First(gn->preds); ln != NILLNODE; ln = Lst_Succ(ln)){
+ GNode *pgn = (GNode *)Lst_Datum(ln);
+
+ if (pgn->make && pgn->made == UNMADE) {
+ if (DEBUG(MAKE)) {
+ printf("predecessor %s not made yet.\n", pgn->name);
+ }
+ break;
+ }
+ }
+ /*
+ * If ln isn't nil, there's a predecessor as yet unmade, so we
+ * just drop this node on the floor. When the node in question
+ * has been made, it will notice this node as being ready to
+ * make but as yet unmade and will place the node on the queue.
+ */
+ if (ln != NILLNODE) {
+ continue;
+ }
+ }
+
+ numNodes--;
+ if (Make_OODate (gn)) {
+ if (DEBUG(MAKE)) {
+ printf ("out-of-date\n");
+ }
+ if (queryFlag) {
+ return (TRUE);
+ }
+ Make_DoAllVar (gn);
+ Job_Make (gn);
+ } else {
+ if (DEBUG(MAKE)) {
+ printf ("up-to-date\n");
+ }
+ gn->made = UPTODATE;
+ if (gn->type & OP_JOIN) {
+ /*
+ * Even for an up-to-date .JOIN node, we need it to have its
+ * context variables so references to it get the correct
+ * value for .TARGET when building up the context variables
+ * of its parent(s)...
+ */
+ Make_DoAllVar (gn);
+ }
+
+ Make_Update (gn);
+ }
+ }
+ return (FALSE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * MakePrintStatus --
+ * Print the status of a top-level node, viz. it being up-to-date
+ * already or not created due to an error in a lower level.
+ * Callback function for Make_Run via Lst_ForEach.
+ *
+ * Results:
+ * Always returns 0.
+ *
+ * Side Effects:
+ * A message may be printed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+MakePrintStatus(gnp, cyclep)
+ ClientData gnp; /* Node to examine */
+ ClientData cyclep; /* True if gn->unmade being non-zero implies
+ * a cycle in the graph, not an error in an
+ * inferior */
+{
+ GNode *gn = (GNode *) gnp;
+ Boolean cycle = *(Boolean *) cyclep;
+ if (gn->made == UPTODATE) {
+ printf ("`%s' is up to date.\n", gn->name);
+ } else if (gn->unmade != 0) {
+ if (cycle) {
+ Boolean t = TRUE;
+ /*
+ * If printing cycles and came to one that has unmade children,
+ * print out the cycle by recursing on its children. Note a
+ * cycle like:
+ * a : b
+ * b : c
+ * c : b
+ * will cause this to erroneously complain about a being in
+ * the cycle, but this is a good approximation.
+ */
+ if (gn->made == CYCLE) {
+ Error("Graph cycles through `%s'", gn->name);
+ gn->made = ENDCYCLE;
+ Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t);
+ gn->made = UNMADE;
+ } else if (gn->made != ENDCYCLE) {
+ gn->made = CYCLE;
+ Lst_ForEach(gn->children, MakePrintStatus, (ClientData) &t);
+ }
+ } else {
+ printf ("`%s' not remade because of errors.\n", gn->name);
+ }
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Make_Run --
+ * Initialize the nodes to remake and the list of nodes which are
+ * ready to be made by doing a breadth-first traversal of the graph
+ * starting from the nodes in the given list. Once this traversal
+ * is finished, all the 'leaves' of the graph are in the toBeMade
+ * queue.
+ * Using this queue and the Job module, work back up the graph,
+ * calling on MakeStartJobs to keep the job table as full as
+ * possible.
+ *
+ * Results:
+ * TRUE if work was done. FALSE otherwise.
+ *
+ * Side Effects:
+ * The make field of all nodes involved in the creation of the given
+ * targets is set to 1. The toBeMade list is set to contain all the
+ * 'leaves' of these subgraphs.
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Make_Run (targs)
+ Lst targs; /* the initial list of targets */
+{
+ register GNode *gn; /* a temporary pointer */
+ register Lst examine; /* List of targets to examine */
+ int errors; /* Number of errors the Job module reports */
+
+ toBeMade = Lst_Init (FALSE);
+
+ examine = Lst_Duplicate(targs, NOCOPY);
+ numNodes = 0;
+
+ /*
+ * Make an initial downward pass over the graph, marking nodes to be made
+ * as we go down. We call Suff_FindDeps to find where a node is and
+ * to get some children for it if it has none and also has no commands.
+ * If the node is a leaf, we stick it on the toBeMade queue to
+ * be looked at in a minute, otherwise we add its children to our queue
+ * and go on about our business.
+ */
+ while (!Lst_IsEmpty (examine)) {
+ gn = (GNode *) Lst_DeQueue (examine);
+
+ if (!gn->make) {
+ gn->make = TRUE;
+ numNodes++;
+
+ /*
+ * Apply any .USE rules before looking for implicit dependencies
+ * to make sure everything has commands that should...
+ */
+ Lst_ForEach (gn->children, MakeHandleUse, (ClientData)gn);
+ Suff_FindDeps (gn);
+
+ if (gn->unmade != 0) {
+ Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine);
+ } else {
+ (void)Lst_EnQueue (toBeMade, (ClientData)gn);
+ }
+ }
+ }
+
+ Lst_Destroy (examine, NOFREE);
+
+ if (queryFlag) {
+ /*
+ * We wouldn't do any work unless we could start some jobs in the
+ * next loop... (we won't actually start any, of course, this is just
+ * to see if any of the targets was out of date)
+ */
+ return (MakeStartJobs());
+ } else {
+ /*
+ * Initialization. At the moment, no jobs are running and until some
+ * get started, nothing will happen since the remaining upward
+ * traversal of the graph is performed by the routines in job.c upon
+ * the finishing of a job. So we fill the Job table as much as we can
+ * before going into our loop.
+ */
+ (void) MakeStartJobs();
+ }
+
+ /*
+ * Main Loop: The idea here is that the ending of jobs will take
+ * care of the maintenance of data structures and the waiting for output
+ * will cause us to be idle most of the time while our children run as
+ * much as possible. Because the job table is kept as full as possible,
+ * the only time when it will be empty is when all the jobs which need
+ * running have been run, so that is the end condition of this loop.
+ * Note that the Job module will exit if there were any errors unless the
+ * keepgoing flag was given.
+ */
+ while (!Job_Empty ()) {
+ Job_CatchOutput ();
+ Job_CatchChildren (!usePipes);
+ (void)MakeStartJobs();
+ }
+
+ errors = Job_End();
+
+ /*
+ * Print the final status of each target. E.g. if it wasn't made
+ * because some inferior reported an error.
+ */
+ errors = ((errors == 0) && (numNodes != 0));
+ Lst_ForEach(targs, MakePrintStatus, (ClientData) &errors);
+
+ return (TRUE);
+}
diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h
new file mode 100644
index 0000000..cdc7950
--- /dev/null
+++ b/usr.bin/make/make.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)make.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*-
+ * make.h --
+ * The global definitions for pmake
+ */
+
+#ifndef _MAKE_H_
+#define _MAKE_H_
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#ifndef MAKE_BOOTSTRAP
+#include <sys/cdefs.h>
+#else
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#else
+#define __P(protos) () /* traditional C preprocessor */
+#endif
+#endif
+#if __STDC__
+#include <stdlib.h>
+#include <unistd.h>
+#endif
+#include "sprite.h"
+#include "lst.h"
+#include "config.h"
+#include "buf.h"
+
+/*-
+ * The structure for an individual graph node. Each node has several
+ * pieces of data associated with it.
+ * 1) the name of the target it describes
+ * 2) the location of the target file in the file system.
+ * 3) the type of operator used to define its sources (qv. parse.c)
+ * 4) whether it is involved in this invocation of make
+ * 5) whether the target has been remade
+ * 6) whether any of its children has been remade
+ * 7) the number of its children that are, as yet, unmade
+ * 8) its modification time
+ * 9) the modification time of its youngest child (qv. make.c)
+ * 10) a list of nodes for which this is a source
+ * 11) a list of nodes on which this depends
+ * 12) a list of nodes that depend on this, as gleaned from the
+ * transformation rules.
+ * 13) a list of nodes of the same name created by the :: operator
+ * 14) a list of nodes that must be made (if they're made) before
+ * this node can be, but that do no enter into the datedness of
+ * this node.
+ * 15) a list of nodes that must be made (if they're made) after
+ * this node is, but that do not depend on this node, in the
+ * normal sense.
+ * 16) a Lst of ``local'' variables that are specific to this target
+ * and this target only (qv. var.c [$@ $< $?, etc.])
+ * 17) a Lst of strings that are commands to be given to a shell
+ * to create this target.
+ */
+typedef struct GNode {
+ char *name; /* The target's name */
+ char *path; /* The full pathname of the file */
+ int type; /* Its type (see the OP flags, below) */
+
+ Boolean make; /* TRUE if this target needs to be remade */
+ enum {
+ UNMADE, BEINGMADE, MADE, UPTODATE, ERROR, ABORTED,
+ CYCLE, ENDCYCLE,
+ } made; /* Set to reflect the state of processing
+ * on this node:
+ * UNMADE - Not examined yet
+ * BEINGMADE - Target is already being made.
+ * Indicates a cycle in the graph. (compat
+ * mode only)
+ * MADE - Was out-of-date and has been made
+ * UPTODATE - Was already up-to-date
+ * ERROR - An error occured while it was being
+ * made (used only in compat mode)
+ * ABORTED - The target was aborted due to
+ * an error making an inferior (compat).
+ * CYCLE - Marked as potentially being part of
+ * a graph cycle. If we come back to a
+ * node marked this way, it is printed
+ * and 'made' is changed to ENDCYCLE.
+ * ENDCYCLE - the cycle has been completely
+ * printed. Go back and unmark all its
+ * members.
+ */
+ Boolean childMade; /* TRUE if one of this target's children was
+ * made */
+ int unmade; /* The number of unmade children */
+
+ int mtime; /* Its modification time */
+ int cmtime; /* The modification time of its youngest
+ * child */
+
+ Lst iParents; /* Links to parents for which this is an
+ * implied source, if any */
+ Lst cohorts; /* Other nodes for the :: operator */
+ Lst parents; /* Nodes that depend on this one */
+ Lst children; /* Nodes on which this one depends */
+ Lst successors; /* Nodes that must be made after this one */
+ Lst preds; /* Nodes that must be made before this one */
+
+ Lst context; /* The local variables */
+ Lst commands; /* Creation commands */
+
+ struct _Suff *suffix; /* Suffix for the node (determined by
+ * Suff_FindDeps and opaque to everyone
+ * but the Suff module) */
+} GNode;
+
+/*
+ * Manifest constants
+ */
+#define NILGNODE ((GNode *) NIL)
+
+/*
+ * The OP_ constants are used when parsing a dependency line as a way of
+ * communicating to other parts of the program the way in which a target
+ * should be made. These constants are bitwise-OR'ed together and
+ * placed in the 'type' field of each node. Any node that has
+ * a 'type' field which satisfies the OP_NOP function was never never on
+ * the lefthand side of an operator, though it may have been on the
+ * righthand side...
+ */
+#define OP_DEPENDS 0x00000001 /* Execution of commands depends on
+ * kids (:) */
+#define OP_FORCE 0x00000002 /* Always execute commands (!) */
+#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids
+ * per line (::) */
+#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP)
+
+#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't
+ * exist and can't be created */
+#define OP_USE 0x00000010 /* Use associated commands for parents */
+#define OP_EXEC 0x00000020 /* Target is never out of date, but always
+ * execute commands anyway. Its time
+ * doesn't matter, so it has none...sort
+ * of */
+#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */
+#define OP_PRECIOUS 0x00000080 /* Don't remove the target when
+ * interrupted */
+#define OP_SILENT 0x00000100 /* Don't echo commands when executed */
+#define OP_MAKE 0x00000200 /* Target is a recurrsive make so its
+ * commands should always be executed when
+ * it is out of date, regardless of the
+ * state of the -n or -t flags */
+#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its
+ * children was out-of-date */
+#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents.
+ * I.e. it doesn't show up in the parents's
+ * local variables. */
+#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main
+ * target' processing in parse.c */
+/* Attributes applied by PMake */
+#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */
+#define OP_MEMBER 0x40000000 /* Target is a member of an archive */
+#define OP_LIB 0x20000000 /* Target is a library */
+#define OP_ARCHV 0x10000000 /* Target is an archive construct */
+#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should.
+ * Used when parsing to catch multiple
+ * commands for a target */
+#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */
+#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */
+
+/*
+ * OP_NOP will return TRUE if the node with the given type was not the
+ * object of a dependency operator
+ */
+#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000)
+
+/*
+ * The TARG_ constants are used when calling the Targ_FindNode and
+ * Targ_FindList functions in targ.c. They simply tell the functions what to
+ * do if the desired node(s) is (are) not found. If the TARG_CREATE constant
+ * is given, a new, empty node will be created for the target, placed in the
+ * table of all targets and its address returned. If TARG_NOCREATE is given,
+ * a NIL pointer will be returned.
+ */
+#define TARG_CREATE 0x01 /* create node if not found */
+#define TARG_NOCREATE 0x00 /* don't create it */
+
+/*
+ * There are several places where expandable buffers are used (parse.c and
+ * var.c). This constant is merely the starting point for those buffers. If
+ * lines tend to be much shorter than this, it would be best to reduce BSIZE.
+ * If longer, it should be increased. Reducing it will cause more copying to
+ * be done for longer lines, but will save space for shorter ones. In any
+ * case, it ought to be a power of two simply because most storage allocation
+ * schemes allocate in powers of two.
+ */
+#define MAKE_BSIZE 256 /* starting size for expandable buffers */
+
+/*
+ * These constants are all used by the Str_Concat function to decide how the
+ * final string should look. If STR_ADDSPACE is given, a space will be
+ * placed between the two strings. If STR_ADDSLASH is given, a '/' will
+ * be used instead of a space. If neither is given, no intervening characters
+ * will be placed between the two strings in the final output. If the
+ * STR_DOFREE bit is set, the two input strings will be freed before
+ * Str_Concat returns.
+ */
+#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */
+#define STR_DOFREE 0x02 /* free source strings after concatenation */
+#define STR_ADDSLASH 0x04 /* add a slash when Str_Concat'ing */
+
+/*
+ * Error levels for parsing. PARSE_FATAL means the process cannot continue
+ * once the makefile has been parsed. PARSE_WARNING means it can. Passed
+ * as the first argument to Parse_Error.
+ */
+#define PARSE_WARNING 2
+#define PARSE_FATAL 1
+
+/*
+ * Values returned by Cond_Eval.
+ */
+#define COND_PARSE 0 /* Parse the next lines */
+#define COND_SKIP 1 /* Skip the next lines */
+#define COND_INVALID 2 /* Not a conditional statement */
+
+/*
+ * Definitions for the "local" variables. Used only for clarity.
+ */
+#define TARGET "@" /* Target of dependency */
+#define OODATE "?" /* All out-of-date sources */
+#define ALLSRC ">" /* All sources */
+#define IMPSRC "<" /* Source implied by transformation */
+#define PREFIX "*" /* Common prefix */
+#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
+#define MEMBER "%" /* Member in "archive(member)" syntax */
+
+#define FTARGET "@F" /* file part of TARGET */
+#define DTARGET "@D" /* directory part of TARGET */
+#define FIMPSRC "<F" /* file part of IMPSRC */
+#define DIMPSRC "<D" /* directory part of IMPSRC */
+#define FPREFIX "*F" /* file part of PREFIX */
+#define DPREFIX "*D" /* directory part of PREFIX */
+
+/*
+ * Global Variables
+ */
+extern Lst create; /* The list of target names specified on the
+ * command line. used to resolve #if
+ * make(...) statements */
+extern Lst dirSearchPath; /* The list of directories to search when
+ * looking for targets */
+
+extern Boolean compatMake; /* True if we are make compatible */
+extern Boolean ignoreErrors; /* True if should ignore all errors */
+extern Boolean beSilent; /* True if should print no commands */
+extern Boolean noExecute; /* True if should execute nothing */
+extern Boolean allPrecious; /* True if every target is precious */
+extern Boolean keepgoing; /* True if should continue on unaffected
+ * portions of the graph when have an error
+ * in one portion */
+extern Boolean touchFlag; /* TRUE if targets should just be 'touched'
+ * if out of date. Set by the -t flag */
+extern Boolean usePipes; /* TRUE if should capture the output of
+ * subshells by means of pipes. Otherwise it
+ * is routed to temporary files from which it
+ * is retrieved when the shell exits */
+extern Boolean queryFlag; /* TRUE if we aren't supposed to really make
+ * anything, just see if the targets are out-
+ * of-date */
+
+extern Boolean checkEnvFirst; /* TRUE if environment should be searched for
+ * variables before the global context */
+
+extern GNode *DEFAULT; /* .DEFAULT rule */
+
+extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
+ * in the Makefile itself */
+extern GNode *VAR_CMD; /* Variables defined on the command line */
+extern char var_Error[]; /* Value returned by Var_Parse when an error
+ * is encountered. It actually points to
+ * an empty string, so naive callers needn't
+ * worry about it. */
+
+extern time_t now; /* The time at the start of this whole
+ * process */
+
+extern Boolean oldVars; /* Do old-style variable substitution */
+
+/*
+ * debug control:
+ * There is one bit per module. It is up to the module what debug
+ * information to print.
+ */
+extern int debug;
+#define DEBUG_ARCH 0x0001
+#define DEBUG_COND 0x0002
+#define DEBUG_DIR 0x0004
+#define DEBUG_GRAPH1 0x0008
+#define DEBUG_GRAPH2 0x0010
+#define DEBUG_JOB 0x0020
+#define DEBUG_MAKE 0x0040
+#define DEBUG_SUFF 0x0080
+#define DEBUG_TARG 0x0100
+#define DEBUG_VAR 0x0200
+#define DEBUG_FOR 0x0400
+
+#ifdef __STDC__
+#define CONCAT(a,b) a##b
+#else
+#define I(a) a
+#define CONCAT(a,b) I(a)b
+#endif /* __STDC__ */
+
+#define DEBUG(module) (debug & CONCAT(DEBUG_,module))
+
+/*
+ * Since there are so many, all functions that return non-integer values are
+ * extracted by means of a sed script or two and stuck in the file "nonints.h"
+ */
+#include "nonints.h"
+
+int Make_TimeStamp __P((GNode *, GNode *));
+Boolean Make_OODate __P((GNode *));
+int Make_HandleUse __P((GNode *, GNode *));
+void Make_Update __P((GNode *));
+void Make_DoAllVar __P((GNode *));
+Boolean Make_Run __P((Lst));
+
+#endif /* _MAKE_H_ */
diff --git a/usr.bin/make/nonints.h b/usr.bin/make/nonints.h
new file mode 100644
index 0000000..b2efed6
--- /dev/null
+++ b/usr.bin/make/nonints.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nonints.h 8.3 (Berkeley) 3/19/94
+ */
+
+/* arch.c */
+ReturnStatus Arch_ParseArchive __P((char **, Lst, GNode *));
+void Arch_Touch __P((GNode *));
+void Arch_TouchLib __P((GNode *));
+int Arch_MTime __P((GNode *));
+int Arch_MemMTime __P((GNode *));
+void Arch_FindLib __P((GNode *, Lst));
+Boolean Arch_LibOODate __P((GNode *));
+void Arch_Init __P((void));
+void Arch_End __P((void));
+
+/* compat.c */
+void Compat_Run __P((Lst));
+
+/* cond.c */
+int Cond_Eval __P((char *));
+void Cond_End __P((void));
+
+/* for.c */
+int For_Eval __P((char *));
+void For_Run __P((void));
+
+/* main.c */
+void Main_ParseArgLine __P((char *));
+int main __P((int, char **));
+void Error __P((char *, ...));
+void Fatal __P((char *, ...));
+void Punt __P((char *, ...));
+void DieHorribly __P((void));
+int PrintAddr __P((ClientData, ClientData));
+void Finish __P((int));
+char *emalloc __P((size_t));
+void enomem __P((void));
+
+/* parse.c */
+void Parse_Error __P((int, char *, ...));
+Boolean Parse_AnyExport __P((void));
+Boolean Parse_IsVar __P((char *));
+void Parse_DoVar __P((char *, GNode *));
+void Parse_AddIncludeDir __P((char *));
+void Parse_File __P((char *, FILE *));
+void Parse_Init __P((void));
+void Parse_End __P((void));
+void Parse_FromString __P((char *));
+Lst Parse_MainName __P((void));
+
+/* str.c */
+void str_init __P((void));
+void str_end __P((void));
+char *str_concat __P((char *, char *, int));
+char **brk_string __P((char *, int *, Boolean));
+char *Str_FindSubstring __P((char *, char *));
+int Str_Match __P((char *, char *));
+char *Str_SYSVMatch __P((char *, char *, int *len));
+void Str_SYSVSubst __P((Buffer, char *, char *, int));
+
+/* suff.c */
+void Suff_ClearSuffixes __P((void));
+Boolean Suff_IsTransform __P((char *));
+GNode *Suff_AddTransform __P((char *));
+int Suff_EndTransform __P((ClientData, ClientData));
+void Suff_AddSuffix __P((char *));
+Lst Suff_GetPath __P((char *));
+void Suff_DoPaths __P((void));
+void Suff_AddInclude __P((char *));
+void Suff_AddLib __P((char *));
+void Suff_FindDeps __P((GNode *));
+void Suff_SetNull __P((char *));
+void Suff_Init __P((void));
+void Suff_End __P((void));
+void Suff_PrintAll __P((void));
+
+/* targ.c */
+void Targ_Init __P((void));
+void Targ_End __P((void));
+GNode *Targ_NewGN __P((char *));
+GNode *Targ_FindNode __P((char *, int));
+Lst Targ_FindList __P((Lst, int));
+Boolean Targ_Ignore __P((GNode *));
+Boolean Targ_Silent __P((GNode *));
+Boolean Targ_Precious __P((GNode *));
+void Targ_SetMain __P((GNode *));
+int Targ_PrintCmd __P((ClientData, ClientData));
+char *Targ_FmtTime __P((time_t));
+void Targ_PrintType __P((int));
+void Targ_PrintGraph __P((int));
+
+/* var.c */
+void Var_Delete __P((char *, GNode *));
+void Var_Set __P((char *, char *, GNode *));
+void Var_Append __P((char *, char *, GNode *));
+Boolean Var_Exists __P((char *, GNode *));
+char *Var_Value __P((char *, GNode *, char **));
+char *Var_Parse __P((char *, GNode *, Boolean, int *, Boolean *));
+char *Var_Subst __P((char *, char *, GNode *, Boolean));
+char *Var_GetTail __P((char *));
+char *Var_GetHead __P((char *));
+void Var_Init __P((void));
+void Var_End __P((void));
+void Var_Dump __P((GNode *));
diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c
new file mode 100644
index 0000000..a56e398
--- /dev/null
+++ b/usr.bin/make/parse.c
@@ -0,0 +1,2618 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * parse.c --
+ * Functions to parse a makefile.
+ *
+ * One function, Parse_Init, must be called before any functions
+ * in this module are used. After that, the function Parse_File is the
+ * main entry point and controls most of the other functions in this
+ * module.
+ *
+ * Most important structures are kept in Lsts. Directories for
+ * the #include "..." function are kept in the 'parseIncPath' Lst, while
+ * those for the #include <...> are kept in the 'sysIncPath' Lst. The
+ * targets currently being defined are kept in the 'targets' Lst.
+ *
+ * The variables 'fname' and 'lineno' are used to track the name
+ * of the current file and the line number in that file so that error
+ * messages can be more meaningful.
+ *
+ * Interface:
+ * Parse_Init Initialization function which must be
+ * called before anything else in this module
+ * is used.
+ *
+ * Parse_End Cleanup the module
+ *
+ * Parse_File Function used to parse a makefile. It must
+ * be given the name of the file, which should
+ * already have been opened, and a function
+ * to call to read a character from the file.
+ *
+ * Parse_IsVar Returns TRUE if the given line is a
+ * variable assignment. Used by MainParseArgs
+ * to determine if an argument is a target
+ * or a variable assignment. Used internally
+ * for pretty much the same thing...
+ *
+ * Parse_Error Function called when an error occurs in
+ * parsing. Used by the variable and
+ * conditional modules.
+ * Parse_MainName Returns a Lst of the main target to create.
+ */
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
+#include "buf.h"
+#include "pathnames.h"
+
+/*
+ * These values are returned by ParseEOF to tell Parse_File whether to
+ * CONTINUE parsing, i.e. it had only reached the end of an include file,
+ * or if it's DONE.
+ */
+#define CONTINUE 1
+#define DONE 0
+static Lst targets; /* targets we're working on */
+static Lst targCmds; /* command lines for targets */
+static Boolean inLine; /* true if currently in a dependency
+ * line or its commands */
+typedef struct {
+ char *str;
+ char *ptr;
+} PTR;
+
+static char *fname; /* name of current file (for errors) */
+static int lineno; /* line number in current file */
+static FILE *curFILE = NULL; /* current makefile */
+
+static PTR *curPTR = NULL; /* current makefile */
+
+static int fatals = 0;
+
+static GNode *mainNode; /* The main target to create. This is the
+ * first target on the first dependency
+ * line in the first makefile */
+/*
+ * Definitions for handling #include specifications
+ */
+typedef struct IFile {
+ char *fname; /* name of previous file */
+ int lineno; /* saved line number */
+ FILE * F; /* the open stream */
+ PTR * p; /* the char pointer */
+} IFile;
+
+static Lst includes; /* stack of IFiles generated by
+ * #includes */
+Lst parseIncPath; /* list of directories for "..." includes */
+Lst sysIncPath; /* list of directories for <...> includes */
+
+/*-
+ * specType contains the SPECial TYPE of the current target. It is
+ * Not if the target is unspecial. If it *is* special, however, the children
+ * are linked as children of the parent but not vice versa. This variable is
+ * set in ParseDoDependency
+ */
+typedef enum {
+ Begin, /* .BEGIN */
+ Default, /* .DEFAULT */
+ End, /* .END */
+ Ignore, /* .IGNORE */
+ Includes, /* .INCLUDES */
+ Interrupt, /* .INTERRUPT */
+ Libs, /* .LIBS */
+ MFlags, /* .MFLAGS or .MAKEFLAGS */
+ Main, /* .MAIN and we don't have anything user-specified to
+ * make */
+ NoExport, /* .NOEXPORT */
+ Not, /* Not special */
+ NotParallel, /* .NOTPARALELL */
+ Null, /* .NULL */
+ Order, /* .ORDER */
+ ExPath, /* .PATH */
+ Precious, /* .PRECIOUS */
+ ExShell, /* .SHELL */
+ Silent, /* .SILENT */
+ SingleShell, /* .SINGLESHELL */
+ Suffixes, /* .SUFFIXES */
+ Attribute /* Generic attribute */
+} ParseSpecial;
+
+static ParseSpecial specType;
+
+/*
+ * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER
+ * seen, then set to each successive source on the line.
+ */
+static GNode *predecessor;
+
+/*
+ * The parseKeywords table is searched using binary search when deciding
+ * if a target or source is special. The 'spec' field is the ParseSpecial
+ * type of the keyword ("Not" if the keyword isn't special as a target) while
+ * the 'op' field is the operator to apply to the list of targets if the
+ * keyword is used as a source ("0" if the keyword isn't special as a source)
+ */
+static struct {
+ char *name; /* Name of keyword */
+ ParseSpecial spec; /* Type when used as a target */
+ int op; /* Operator when used as a source */
+} parseKeywords[] = {
+{ ".BEGIN", Begin, 0 },
+{ ".DEFAULT", Default, 0 },
+{ ".END", End, 0 },
+{ ".EXEC", Attribute, OP_EXEC },
+{ ".IGNORE", Ignore, OP_IGNORE },
+{ ".INCLUDES", Includes, 0 },
+{ ".INTERRUPT", Interrupt, 0 },
+{ ".INVISIBLE", Attribute, OP_INVISIBLE },
+{ ".JOIN", Attribute, OP_JOIN },
+{ ".LIBS", Libs, 0 },
+{ ".MAIN", Main, 0 },
+{ ".MAKE", Attribute, OP_MAKE },
+{ ".MAKEFLAGS", MFlags, 0 },
+{ ".MFLAGS", MFlags, 0 },
+{ ".NOTMAIN", Attribute, OP_NOTMAIN },
+{ ".NOTPARALLEL", NotParallel, 0 },
+{ ".NULL", Null, 0 },
+{ ".OPTIONAL", Attribute, OP_OPTIONAL },
+{ ".ORDER", Order, 0 },
+{ ".PATH", ExPath, 0 },
+{ ".PRECIOUS", Precious, OP_PRECIOUS },
+{ ".RECURSIVE", Attribute, OP_MAKE },
+{ ".SHELL", ExShell, 0 },
+{ ".SILENT", Silent, OP_SILENT },
+{ ".SINGLESHELL", SingleShell, 0 },
+{ ".SUFFIXES", Suffixes, 0 },
+{ ".USE", Attribute, OP_USE },
+};
+
+static int ParseFindKeyword __P((char *));
+static int ParseLinkSrc __P((ClientData, ClientData));
+static int ParseDoOp __P((ClientData, ClientData));
+static void ParseDoSrc __P((int, char *));
+static int ParseFindMain __P((ClientData, ClientData));
+static int ParseAddDir __P((ClientData, ClientData));
+static int ParseClearPath __P((ClientData, ClientData));
+static void ParseDoDependency __P((char *));
+static int ParseAddCmd __P((ClientData, ClientData));
+static int ParseReadc __P((void));
+static void ParseUnreadc __P((int));
+static void ParseHasCommands __P((ClientData));
+static void ParseDoInclude __P((char *));
+#ifdef SYSVINCLUDE
+static void ParseTraditionalInclude __P((char *));
+#endif
+static int ParseEOF __P((int));
+static char *ParseReadLine __P((void));
+static char *ParseSkipLine __P((int));
+static void ParseFinishLine __P((void));
+
+/*-
+ *----------------------------------------------------------------------
+ * ParseFindKeyword --
+ * Look in the table of keywords for one matching the given string.
+ *
+ * Results:
+ * The index of the keyword, or -1 if it isn't there.
+ *
+ * Side Effects:
+ * None
+ *----------------------------------------------------------------------
+ */
+static int
+ParseFindKeyword (str)
+ char *str; /* String to find */
+{
+ register int start,
+ end,
+ cur;
+ register int diff;
+
+ start = 0;
+ end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1;
+
+ do {
+ cur = start + ((end - start) / 2);
+ diff = strcmp (str, parseKeywords[cur].name);
+
+ if (diff == 0) {
+ return (cur);
+ } else if (diff < 0) {
+ end = cur - 1;
+ } else {
+ start = cur + 1;
+ }
+ } while (start <= end);
+ return (-1);
+}
+
+/*-
+ * Parse_Error --
+ * Error message abort function for parsing. Prints out the context
+ * of the error (line number and file) as well as the message with
+ * two optional arguments.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * "fatals" is incremented if the level is PARSE_FATAL.
+ */
+/* VARARGS */
+void
+#if __STDC__
+Parse_Error(int type, char *fmt, ...)
+#else
+Parse_Error(va_alist)
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */
+ char *fmt;
+
+ va_start(ap);
+ type = va_arg(ap, int);
+ fmt = va_arg(ap, char *);
+#endif
+
+ (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno);
+ if (type == PARSE_WARNING)
+ (void)fprintf(stderr, "warning: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+ if (type == PARSE_FATAL)
+ fatals += 1;
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseLinkSrc --
+ * Link the parent node to its new child. Used in a Lst_ForEach by
+ * ParseDoDependency. If the specType isn't 'Not', the parent
+ * isn't linked as a parent of the child.
+ *
+ * Results:
+ * Always = 0
+ *
+ * Side Effects:
+ * New elements are added to the parents list of cgn and the
+ * children list of cgn. the unmade field of pgn is updated
+ * to reflect the additional child.
+ *---------------------------------------------------------------------
+ */
+static int
+ParseLinkSrc (pgnp, cgnp)
+ ClientData pgnp; /* The parent node */
+ ClientData cgnp; /* The child node */
+{
+ GNode *pgn = (GNode *) pgnp;
+ GNode *cgn = (GNode *) cgnp;
+ if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) {
+ (void)Lst_AtEnd (pgn->children, (ClientData)cgn);
+ if (specType == Not) {
+ (void)Lst_AtEnd (cgn->parents, (ClientData)pgn);
+ }
+ pgn->unmade += 1;
+ }
+ return (0);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseDoOp --
+ * Apply the parsed operator to the given target node. Used in a
+ * Lst_ForEach call by ParseDoDependency once all targets have
+ * been found and their operator parsed. If the previous and new
+ * operators are incompatible, a major error is taken.
+ *
+ * Results:
+ * Always 0
+ *
+ * Side Effects:
+ * The type field of the node is altered to reflect any new bits in
+ * the op.
+ *---------------------------------------------------------------------
+ */
+static int
+ParseDoOp (gnp, opp)
+ ClientData gnp; /* The node to which the operator is to be
+ * applied */
+ ClientData opp; /* The operator to apply */
+{
+ GNode *gn = (GNode *) gnp;
+ int op = *(int *) opp;
+ /*
+ * If the dependency mask of the operator and the node don't match and
+ * the node has actually had an operator applied to it before, and
+ * the operator actually has some dependency information in it, complain.
+ */
+ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) &&
+ !OP_NOP(gn->type) && !OP_NOP(op))
+ {
+ Parse_Error (PARSE_FATAL, "Inconsistent operator for %s", gn->name);
+ return (1);
+ }
+
+ if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) {
+ /*
+ * If the node was the object of a :: operator, we need to create a
+ * new instance of it for the children and commands on this dependency
+ * line. The new instance is placed on the 'cohorts' list of the
+ * initial one (note the initial one is not on its own cohorts list)
+ * and the new instance is linked to all parents of the initial
+ * instance.
+ */
+ register GNode *cohort;
+ LstNode ln;
+
+ cohort = Targ_NewGN(gn->name);
+ /*
+ * Duplicate links to parents so graph traversal is simple. Perhaps
+ * some type bits should be duplicated?
+ *
+ * Make the cohort invisible as well to avoid duplicating it into
+ * other variables. True, parents of this target won't tend to do
+ * anything with their local variables, but better safe than
+ * sorry.
+ */
+ Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort);
+ cohort->type = OP_DOUBLEDEP|OP_INVISIBLE;
+ (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort);
+
+ /*
+ * Replace the node in the targets list with the new copy
+ */
+ ln = Lst_Member(targets, (ClientData)gn);
+ Lst_Replace(ln, (ClientData)cohort);
+ gn = cohort;
+ }
+ /*
+ * We don't want to nuke any previous flags (whatever they were) so we
+ * just OR the new operator into the old
+ */
+ gn->type |= op;
+
+ return (0);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseDoSrc --
+ * Given the name of a source, figure out if it is an attribute
+ * and apply it to the targets if it is. Else decide if there is
+ * some attribute which should be applied *to* the source because
+ * of some special target and apply it if so. Otherwise, make the
+ * source be a child of the targets in the list 'targets'
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Operator bits may be added to the list of targets or to the source.
+ * The targets may have a new source added to their lists of children.
+ *---------------------------------------------------------------------
+ */
+static void
+ParseDoSrc (tOp, src)
+ int tOp; /* operator (if any) from special targets */
+ char *src; /* name of the source to handle */
+{
+ int op; /* operator (if any) from special source */
+ GNode *gn;
+
+ op = 0;
+ if (*src == '.' && isupper (src[1])) {
+ int keywd = ParseFindKeyword(src);
+ if (keywd != -1) {
+ op = parseKeywords[keywd].op;
+ }
+ }
+ if (op != 0) {
+ Lst_ForEach (targets, ParseDoOp, (ClientData)&op);
+ } else if (specType == Main) {
+ /*
+ * If we have noted the existence of a .MAIN, it means we need
+ * to add the sources of said target to the list of things
+ * to create. The string 'src' is likely to be free, so we
+ * must make a new copy of it. Note that this will only be
+ * invoked if the user didn't specify a target on the command
+ * line. This is to allow #ifmake's to succeed, or something...
+ */
+ (void) Lst_AtEnd (create, (ClientData)strdup(src));
+ /*
+ * Add the name to the .TARGETS variable as well, so the user cna
+ * employ that, if desired.
+ */
+ Var_Append(".TARGETS", src, VAR_GLOBAL);
+ } else if (specType == Order) {
+ /*
+ * Create proper predecessor/successor links between the previous
+ * source and the current one.
+ */
+ gn = Targ_FindNode(src, TARG_CREATE);
+ if (predecessor != NILGNODE) {
+ (void)Lst_AtEnd(predecessor->successors, (ClientData)gn);
+ (void)Lst_AtEnd(gn->preds, (ClientData)predecessor);
+ }
+ /*
+ * The current source now becomes the predecessor for the next one.
+ */
+ predecessor = gn;
+ } else {
+ /*
+ * If the source is not an attribute, we need to find/create
+ * a node for it. After that we can apply any operator to it
+ * from a special target or link it to its parents, as
+ * appropriate.
+ *
+ * In the case of a source that was the object of a :: operator,
+ * the attribute is applied to all of its instances (as kept in
+ * the 'cohorts' list of the node) or all the cohorts are linked
+ * to all the targets.
+ */
+ gn = Targ_FindNode (src, TARG_CREATE);
+ if (tOp) {
+ gn->type |= tOp;
+ } else {
+ Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn);
+ }
+ if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
+ register GNode *cohort;
+ register LstNode ln;
+
+ for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){
+ cohort = (GNode *)Lst_Datum(ln);
+ if (tOp) {
+ cohort->type |= tOp;
+ } else {
+ Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort);
+ }
+ }
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ParseFindMain --
+ * Find a real target in the list and set it to be the main one.
+ * Called by ParseDoDependency when a main target hasn't been found
+ * yet.
+ *
+ * Results:
+ * 0 if main not found yet, 1 if it is.
+ *
+ * Side Effects:
+ * mainNode is changed and Targ_SetMain is called.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ParseFindMain(gnp, dummy)
+ ClientData gnp; /* Node to examine */
+ ClientData dummy;
+{
+ GNode *gn = (GNode *) gnp;
+ if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) {
+ mainNode = gn;
+ Targ_SetMain(gn);
+ return (dummy ? 1 : 1);
+ } else {
+ return (dummy ? 0 : 0);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ParseAddDir --
+ * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going
+ *
+ * Results:
+ * === 0
+ *
+ * Side Effects:
+ * See Dir_AddDir.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ParseAddDir(path, name)
+ ClientData path;
+ ClientData name;
+{
+ Dir_AddDir((Lst) path, (char *) name);
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ParseClearPath --
+ * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going
+ *
+ * Results:
+ * === 0
+ *
+ * Side Effects:
+ * See Dir_ClearPath
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ParseClearPath(path, dummy)
+ ClientData path;
+ ClientData dummy;
+{
+ Dir_ClearPath((Lst) path);
+ return(dummy ? 0 : 0);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseDoDependency --
+ * Parse the dependency line in line.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The nodes of the sources are linked as children to the nodes of the
+ * targets. Some nodes may be created.
+ *
+ * We parse a dependency line by first extracting words from the line and
+ * finding nodes in the list of all targets with that name. This is done
+ * until a character is encountered which is an operator character. Currently
+ * these are only ! and :. At this point the operator is parsed and the
+ * pointer into the line advanced until the first source is encountered.
+ * The parsed operator is applied to each node in the 'targets' list,
+ * which is where the nodes found for the targets are kept, by means of
+ * the ParseDoOp function.
+ * The sources are read in much the same way as the targets were except
+ * that now they are expanded using the wildcarding scheme of the C-Shell
+ * and all instances of the resulting words in the list of all targets
+ * are found. Each of the resulting nodes is then linked to each of the
+ * targets as one of its children.
+ * Certain targets are handled specially. These are the ones detailed
+ * by the specType variable.
+ * The storing of transformation rules is also taken care of here.
+ * A target is recognized as a transformation rule by calling
+ * Suff_IsTransform. If it is a transformation rule, its node is gotten
+ * from the suffix module via Suff_AddTransform rather than the standard
+ * Targ_FindNode in the target module.
+ *---------------------------------------------------------------------
+ */
+static void
+ParseDoDependency (line)
+ char *line; /* the line to parse */
+{
+ char *cp; /* our current position */
+ GNode *gn; /* a general purpose temporary node */
+ int op; /* the operator on the line */
+ char savec; /* a place to save a character */
+ Lst paths; /* List of search paths to alter when parsing
+ * a list of .PATH targets */
+ int tOp; /* operator from special target */
+ Lst sources; /* list of source names after expansion */
+ Lst curTargs; /* list of target names to be found and added
+ * to the targets list */
+
+ tOp = 0;
+
+ specType = Not;
+ paths = (Lst)NULL;
+
+ curTargs = Lst_Init(FALSE);
+
+ do {
+ for (cp = line;
+ *cp && !isspace (*cp) &&
+ (*cp != '!') && (*cp != ':') && (*cp != '(');
+ cp++)
+ {
+ if (*cp == '$') {
+ /*
+ * Must be a dynamic source (would have been expanded
+ * otherwise), so call the Var module to parse the puppy
+ * so we can safely advance beyond it...There should be
+ * no errors in this, as they would have been discovered
+ * in the initial Var_Subst and we wouldn't be here.
+ */
+ int length;
+ Boolean freeIt;
+ char *result;
+
+ result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt);
+
+ if (freeIt) {
+ free(result);
+ }
+ cp += length-1;
+ }
+ continue;
+ }
+ if (*cp == '(') {
+ /*
+ * Archives must be handled specially to make sure the OP_ARCHV
+ * flag is set in their 'type' field, for one thing, and because
+ * things like "archive(file1.o file2.o file3.o)" are permissible.
+ * Arch_ParseArchive will set 'line' to be the first non-blank
+ * after the archive-spec. It creates/finds nodes for the members
+ * and places them on the given list, returning SUCCESS if all
+ * went well and FAILURE if there was an error in the
+ * specification. On error, line should remain untouched.
+ */
+ if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) {
+ Parse_Error (PARSE_FATAL,
+ "Error in archive specification: \"%s\"", line);
+ return;
+ } else {
+ continue;
+ }
+ }
+ savec = *cp;
+
+ if (!*cp) {
+ /*
+ * Ending a dependency line without an operator is a Bozo
+ * no-no
+ */
+ Parse_Error (PARSE_FATAL, "Need an operator");
+ return;
+ }
+ *cp = '\0';
+ /*
+ * Have a word in line. See if it's a special target and set
+ * specType to match it.
+ */
+ if (*line == '.' && isupper (line[1])) {
+ /*
+ * See if the target is a special target that must have it
+ * or its sources handled specially.
+ */
+ int keywd = ParseFindKeyword(line);
+ if (keywd != -1) {
+ if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
+ Parse_Error(PARSE_FATAL, "Mismatched special targets");
+ return;
+ }
+
+ specType = parseKeywords[keywd].spec;
+ tOp = parseKeywords[keywd].op;
+
+ /*
+ * Certain special targets have special semantics:
+ * .PATH Have to set the dirSearchPath
+ * variable too
+ * .MAIN Its sources are only used if
+ * nothing has been specified to
+ * create.
+ * .DEFAULT Need to create a node to hang
+ * commands on, but we don't want
+ * it in the graph, nor do we want
+ * it to be the Main Target, so we
+ * create it, set OP_NOTMAIN and
+ * add it to the list, setting
+ * DEFAULT to the new node for
+ * later use. We claim the node is
+ * A transformation rule to make
+ * life easier later, when we'll
+ * use Make_HandleUse to actually
+ * apply the .DEFAULT commands.
+ * .BEGIN
+ * .END
+ * .INTERRUPT Are not to be considered the
+ * main target.
+ * .NOTPARALLEL Make only one target at a time.
+ * .SINGLESHELL Create a shell for each command.
+ * .ORDER Must set initial predecessor to NIL
+ */
+ switch (specType) {
+ case ExPath:
+ if (paths == NULL) {
+ paths = Lst_Init(FALSE);
+ }
+ (void)Lst_AtEnd(paths, (ClientData)dirSearchPath);
+ break;
+ case Main:
+ if (!Lst_IsEmpty(create)) {
+ specType = Not;
+ }
+ break;
+ case Begin:
+ case End:
+ case Interrupt:
+ gn = Targ_FindNode(line, TARG_CREATE);
+ gn->type |= OP_NOTMAIN;
+ (void)Lst_AtEnd(targets, (ClientData)gn);
+ break;
+ case Default:
+ gn = Targ_NewGN(".DEFAULT");
+ gn->type |= (OP_NOTMAIN|OP_TRANSFORM);
+ (void)Lst_AtEnd(targets, (ClientData)gn);
+ DEFAULT = gn;
+ break;
+ case NotParallel:
+ {
+ extern int maxJobs;
+
+ maxJobs = 1;
+ break;
+ }
+ case SingleShell:
+ compatMake = 1;
+ break;
+ case Order:
+ predecessor = NILGNODE;
+ break;
+ default:
+ break;
+ }
+ } else if (strncmp (line, ".PATH", 5) == 0) {
+ /*
+ * .PATH<suffix> has to be handled specially.
+ * Call on the suffix module to give us a path to
+ * modify.
+ */
+ Lst path;
+
+ specType = ExPath;
+ path = Suff_GetPath (&line[5]);
+ if (path == NILLST) {
+ Parse_Error (PARSE_FATAL,
+ "Suffix '%s' not defined (yet)",
+ &line[5]);
+ return;
+ } else {
+ if (paths == (Lst)NULL) {
+ paths = Lst_Init(FALSE);
+ }
+ (void)Lst_AtEnd(paths, (ClientData)path);
+ }
+ }
+ }
+
+ /*
+ * Have word in line. Get or create its node and stick it at
+ * the end of the targets list
+ */
+ if ((specType == Not) && (*line != '\0')) {
+ if (Dir_HasWildcards(line)) {
+ /*
+ * Targets are to be sought only in the current directory,
+ * so create an empty path for the thing. Note we need to
+ * use Dir_Destroy in the destruction of the path as the
+ * Dir module could have added a directory to the path...
+ */
+ Lst emptyPath = Lst_Init(FALSE);
+
+ Dir_Expand(line, emptyPath, curTargs);
+
+ Lst_Destroy(emptyPath, Dir_Destroy);
+ } else {
+ /*
+ * No wildcards, but we want to avoid code duplication,
+ * so create a list with the word on it.
+ */
+ (void)Lst_AtEnd(curTargs, (ClientData)line);
+ }
+
+ while(!Lst_IsEmpty(curTargs)) {
+ char *targName = (char *)Lst_DeQueue(curTargs);
+
+ if (!Suff_IsTransform (targName)) {
+ gn = Targ_FindNode (targName, TARG_CREATE);
+ } else {
+ gn = Suff_AddTransform (targName);
+ }
+
+ (void)Lst_AtEnd (targets, (ClientData)gn);
+ }
+ } else if (specType == ExPath && *line != '.' && *line != '\0') {
+ Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
+ }
+
+ *cp = savec;
+ /*
+ * If it is a special type and not .PATH, it's the only target we
+ * allow on this line...
+ */
+ if (specType != Not && specType != ExPath) {
+ Boolean warn = FALSE;
+
+ while ((*cp != '!') && (*cp != ':') && *cp) {
+ if (*cp != ' ' && *cp != '\t') {
+ warn = TRUE;
+ }
+ cp++;
+ }
+ if (warn) {
+ Parse_Error(PARSE_WARNING, "Extra target ignored");
+ }
+ } else {
+ while (*cp && isspace (*cp)) {
+ cp++;
+ }
+ }
+ line = cp;
+ } while ((*line != '!') && (*line != ':') && *line);
+
+ /*
+ * Don't need the list of target names anymore...
+ */
+ Lst_Destroy(curTargs, NOFREE);
+
+ if (!Lst_IsEmpty(targets)) {
+ switch(specType) {
+ default:
+ Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
+ break;
+ case Default:
+ case Begin:
+ case End:
+ case Interrupt:
+ /*
+ * These four create nodes on which to hang commands, so
+ * targets shouldn't be empty...
+ */
+ case Not:
+ /*
+ * Nothing special here -- targets can be empty if it wants.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Have now parsed all the target names. Must parse the operator next. The
+ * result is left in op .
+ */
+ if (*cp == '!') {
+ op = OP_FORCE;
+ } else if (*cp == ':') {
+ if (cp[1] == ':') {
+ op = OP_DOUBLEDEP;
+ cp++;
+ } else {
+ op = OP_DEPENDS;
+ }
+ } else {
+ Parse_Error (PARSE_FATAL, "Missing dependency operator");
+ return;
+ }
+
+ cp++; /* Advance beyond operator */
+
+ Lst_ForEach (targets, ParseDoOp, (ClientData)&op);
+
+ /*
+ * Get to the first source
+ */
+ while (*cp && isspace (*cp)) {
+ cp++;
+ }
+ line = cp;
+
+ /*
+ * Several special targets take different actions if present with no
+ * sources:
+ * a .SUFFIXES line with no sources clears out all old suffixes
+ * a .PRECIOUS line makes all targets precious
+ * a .IGNORE line ignores errors for all targets
+ * a .SILENT line creates silence when making all targets
+ * a .PATH removes all directories from the search path(s).
+ */
+ if (!*line) {
+ switch (specType) {
+ case Suffixes:
+ Suff_ClearSuffixes ();
+ break;
+ case Precious:
+ allPrecious = TRUE;
+ break;
+ case Ignore:
+ ignoreErrors = TRUE;
+ break;
+ case Silent:
+ beSilent = TRUE;
+ break;
+ case ExPath:
+ Lst_ForEach(paths, ParseClearPath, (ClientData)NULL);
+ break;
+ default:
+ break;
+ }
+ } else if (specType == MFlags) {
+ /*
+ * Call on functions in main.c to deal with these arguments and
+ * set the initial character to a null-character so the loop to
+ * get sources won't get anything
+ */
+ Main_ParseArgLine (line);
+ *line = '\0';
+ } else if (specType == ExShell) {
+ if (Job_ParseShell (line) != SUCCESS) {
+ Parse_Error (PARSE_FATAL, "improper shell specification");
+ return;
+ }
+ *line = '\0';
+ } else if ((specType == NotParallel) || (specType == SingleShell)) {
+ *line = '\0';
+ }
+
+ /*
+ * NOW GO FOR THE SOURCES
+ */
+ if ((specType == Suffixes) || (specType == ExPath) ||
+ (specType == Includes) || (specType == Libs) ||
+ (specType == Null))
+ {
+ while (*line) {
+ /*
+ * If the target was one that doesn't take files as its sources
+ * but takes something like suffixes, we take each
+ * space-separated word on the line as a something and deal
+ * with it accordingly.
+ *
+ * If the target was .SUFFIXES, we take each source as a
+ * suffix and add it to the list of suffixes maintained by the
+ * Suff module.
+ *
+ * If the target was a .PATH, we add the source as a directory
+ * to search on the search path.
+ *
+ * If it was .INCLUDES, the source is taken to be the suffix of
+ * files which will be #included and whose search path should
+ * be present in the .INCLUDES variable.
+ *
+ * If it was .LIBS, the source is taken to be the suffix of
+ * files which are considered libraries and whose search path
+ * should be present in the .LIBS variable.
+ *
+ * If it was .NULL, the source is the suffix to use when a file
+ * has no valid suffix.
+ */
+ char savec;
+ while (*cp && !isspace (*cp)) {
+ cp++;
+ }
+ savec = *cp;
+ *cp = '\0';
+ switch (specType) {
+ case Suffixes:
+ Suff_AddSuffix (line);
+ break;
+ case ExPath:
+ Lst_ForEach(paths, ParseAddDir, (ClientData)line);
+ break;
+ case Includes:
+ Suff_AddInclude (line);
+ break;
+ case Libs:
+ Suff_AddLib (line);
+ break;
+ case Null:
+ Suff_SetNull (line);
+ break;
+ default:
+ break;
+ }
+ *cp = savec;
+ if (savec != '\0') {
+ cp++;
+ }
+ while (*cp && isspace (*cp)) {
+ cp++;
+ }
+ line = cp;
+ }
+ if (paths) {
+ Lst_Destroy(paths, NOFREE);
+ }
+ } else {
+ while (*line) {
+ /*
+ * The targets take real sources, so we must beware of archive
+ * specifications (i.e. things with left parentheses in them)
+ * and handle them accordingly.
+ */
+ while (*cp && !isspace (*cp)) {
+ if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) {
+ /*
+ * Only stop for a left parenthesis if it isn't at the
+ * start of a word (that'll be for variable changes
+ * later) and isn't preceded by a dollar sign (a dynamic
+ * source).
+ */
+ break;
+ } else {
+ cp++;
+ }
+ }
+
+ if (*cp == '(') {
+ GNode *gn;
+
+ sources = Lst_Init (FALSE);
+ if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) {
+ Parse_Error (PARSE_FATAL,
+ "Error in source archive spec \"%s\"", line);
+ return;
+ }
+
+ while (!Lst_IsEmpty (sources)) {
+ gn = (GNode *) Lst_DeQueue (sources);
+ ParseDoSrc (tOp, gn->name);
+ }
+ Lst_Destroy (sources, NOFREE);
+ cp = line;
+ } else {
+ if (*cp) {
+ *cp = '\0';
+ cp += 1;
+ }
+
+ ParseDoSrc (tOp, line);
+ }
+ while (*cp && isspace (*cp)) {
+ cp++;
+ }
+ line = cp;
+ }
+ }
+
+ if (mainNode == NILGNODE) {
+ /*
+ * If we have yet to decide on a main target to make, in the
+ * absence of any user input, we want the first target on
+ * the first dependency line that is actually a real target
+ * (i.e. isn't a .USE or .EXEC rule) to be made.
+ */
+ Lst_ForEach (targets, ParseFindMain, (ClientData)0);
+ }
+
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_IsVar --
+ * Return TRUE if the passed line is a variable assignment. A variable
+ * assignment consists of a single word followed by optional whitespace
+ * followed by either a += or an = operator.
+ * This function is used both by the Parse_File function and main when
+ * parsing the command-line arguments.
+ *
+ * Results:
+ * TRUE if it is. FALSE if it ain't
+ *
+ * Side Effects:
+ * none
+ *---------------------------------------------------------------------
+ */
+Boolean
+Parse_IsVar (line)
+ register char *line; /* the line to check */
+{
+ register Boolean wasSpace = FALSE; /* set TRUE if found a space */
+ register Boolean haveName = FALSE; /* Set TRUE if have a variable name */
+
+ /*
+ * Skip to variable name
+ */
+ while ((*line == ' ') || (*line == '\t')) {
+ line++;
+ }
+
+ while (*line != '=') {
+ if (*line == '\0') {
+ /*
+ * end-of-line -- can't be a variable assignment.
+ */
+ return (FALSE);
+ } else if ((*line == ' ') || (*line == '\t')) {
+ /*
+ * there can be as much white space as desired so long as there is
+ * only one word before the operator
+ */
+ wasSpace = TRUE;
+ } else if (wasSpace && haveName) {
+ /*
+ * Stop when an = operator is found.
+ */
+ if ((*line == '+') || (*line == ':') || (*line == '?') ||
+ (*line == '!')) {
+ break;
+ }
+
+ /*
+ * This is the start of another word, so not assignment.
+ */
+ return (FALSE);
+ } else {
+ haveName = TRUE;
+ wasSpace = FALSE;
+ }
+ line++;
+ }
+
+ /*
+ * A final check: if we stopped on a +, ?, ! or :, the next character must
+ * be an = or it ain't a valid assignment
+ */
+ if (((*line == '+') ||
+ (*line == '?') ||
+ (*line == ':') ||
+ (*line == '!')) &&
+ (line[1] != '='))
+ {
+ return (FALSE);
+ } else {
+ return (haveName);
+ }
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_DoVar --
+ * Take the variable assignment in the passed line and do it in the
+ * global context.
+ *
+ * Note: There is a lexical ambiguity with assignment modifier characters
+ * in variable names. This routine interprets the character before the =
+ * as a modifier. Therefore, an assignment like
+ * C++=/usr/bin/CC
+ * is interpreted as "C+ +=" instead of "C++ =".
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * the variable structure of the given variable name is altered in the
+ * global context.
+ *---------------------------------------------------------------------
+ */
+void
+Parse_DoVar (line, ctxt)
+ char *line; /* a line guaranteed to be a variable
+ * assignment. This reduces error checks */
+ GNode *ctxt; /* Context in which to do the assignment */
+{
+ char *cp; /* pointer into line */
+ enum {
+ VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
+ } type; /* Type of assignment */
+ char *opc; /* ptr to operator character to
+ * null-terminate the variable name */
+ /*
+ * Avoid clobbered variable warnings by forcing the compiler
+ * to ``unregister'' variables
+ */
+#if __GNUC__
+ (void) &cp;
+ (void) &line;
+#endif
+
+ /*
+ * Skip to variable name
+ */
+ while ((*line == ' ') || (*line == '\t')) {
+ line++;
+ }
+
+ /*
+ * Skip to operator character, nulling out whitespace as we go
+ */
+ for (cp = line + 1; *cp != '='; cp++) {
+ if (isspace (*cp)) {
+ *cp = '\0';
+ }
+ }
+ opc = cp-1; /* operator is the previous character */
+ *cp++ = '\0'; /* nuke the = */
+
+ /*
+ * Check operator type
+ */
+ switch (*opc) {
+ case '+':
+ type = VAR_APPEND;
+ *opc = '\0';
+ break;
+
+ case '?':
+ /*
+ * If the variable already has a value, we don't do anything.
+ */
+ *opc = '\0';
+ if (Var_Exists(line, ctxt)) {
+ return;
+ } else {
+ type = VAR_NORMAL;
+ }
+ break;
+
+ case ':':
+ type = VAR_SUBST;
+ *opc = '\0';
+ break;
+
+ case '!':
+ type = VAR_SHELL;
+ *opc = '\0';
+ break;
+
+ default:
+ type = VAR_NORMAL;
+ break;
+ }
+
+ while (isspace (*cp)) {
+ cp++;
+ }
+
+ if (type == VAR_APPEND) {
+ Var_Append (line, cp, ctxt);
+ } else if (type == VAR_SUBST) {
+ /*
+ * Allow variables in the old value to be undefined, but leave their
+ * invocation alone -- this is done by forcing oldVars to be false.
+ * XXX: This can cause recursive variables, but that's not hard to do,
+ * and this allows someone to do something like
+ *
+ * CFLAGS = $(.INCLUDES)
+ * CFLAGS := -I.. $(CFLAGS)
+ *
+ * And not get an error.
+ */
+ Boolean oldOldVars = oldVars;
+
+ oldVars = FALSE;
+ cp = Var_Subst(NULL, cp, ctxt, FALSE);
+ oldVars = oldOldVars;
+
+ Var_Set(line, cp, ctxt);
+ free(cp);
+ } else if (type == VAR_SHELL) {
+ char *args[4]; /* Args for invoking the shell */
+ int fds[2]; /* Pipe streams */
+ int cpid; /* Child PID */
+ int pid; /* PID from wait() */
+ Boolean freeCmd; /* TRUE if the command needs to be freed, i.e.
+ * if any variable expansion was performed */
+
+ /*
+ * Avoid clobbered variable warnings by forcing the compiler
+ * to ``unregister'' variables
+ */
+#if __GNUC__
+ (void) &freeCmd;
+#endif
+
+ /*
+ * Set up arguments for shell
+ */
+ args[0] = "sh";
+ args[1] = "-c";
+ if (strchr(cp, '$') != (char *)NULL) {
+ /*
+ * There's a dollar sign in the command, so perform variable
+ * expansion on the whole thing. The resulting string will need
+ * freeing when we're done, so set freeCmd to TRUE.
+ */
+ args[2] = Var_Subst(NULL, cp, VAR_CMD, TRUE);
+ freeCmd = TRUE;
+ } else {
+ args[2] = cp;
+ freeCmd = FALSE;
+ }
+ args[3] = (char *)NULL;
+
+ /*
+ * Open a pipe for fetching its output
+ */
+ pipe(fds);
+
+ /*
+ * Fork
+ */
+ cpid = vfork();
+ if (cpid == 0) {
+ /*
+ * Close input side of pipe
+ */
+ close(fds[0]);
+
+ /*
+ * Duplicate the output stream to the shell's output, then
+ * shut the extra thing down. Note we don't fetch the error
+ * stream...why not? Why?
+ */
+ dup2(fds[1], 1);
+ close(fds[1]);
+
+ execv("/bin/sh", args);
+ _exit(1);
+ } else if (cpid < 0) {
+ /*
+ * Couldn't fork -- tell the user and make the variable null
+ */
+ Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp);
+ Var_Set(line, "", ctxt);
+ } else {
+ int status;
+ int cc;
+ Buffer buf;
+ char *res;
+
+ /*
+ * No need for the writing half
+ */
+ close(fds[1]);
+
+ buf = Buf_Init (MAKE_BSIZE);
+
+ do {
+ char result[BUFSIZ];
+ cc = read(fds[0], result, sizeof(result));
+ if (cc > 0)
+ Buf_AddBytes(buf, cc, (Byte *) result);
+ }
+ while (cc > 0 || (cc == -1 && errno == EINTR));
+
+ /*
+ * Close the input side of the pipe.
+ */
+ close(fds[0]);
+
+ /*
+ * Wait for the process to exit.
+ */
+ while(((pid = wait(&status)) != cpid) && (pid >= 0))
+ continue;
+
+ res = (char *)Buf_GetAll (buf, &cc);
+ Buf_Destroy (buf, FALSE);
+
+ if (cc == 0) {
+ /*
+ * Couldn't read the child's output -- tell the user and
+ * set the variable to null
+ */
+ Parse_Error(PARSE_WARNING, "Couldn't read shell's output");
+ }
+
+ if (status) {
+ /*
+ * Child returned an error -- tell the user but still use
+ * the result.
+ */
+ Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp);
+ }
+
+ /*
+ * Null-terminate the result, convert newlines to spaces and
+ * install it in the variable.
+ */
+ res[cc] = '\0';
+ cp = &res[cc] - 1;
+
+ if (*cp == '\n') {
+ /*
+ * A final newline is just stripped
+ */
+ *cp-- = '\0';
+ }
+ while (cp >= res) {
+ if (*cp == '\n') {
+ *cp = ' ';
+ }
+ cp--;
+ }
+ Var_Set(line, res, ctxt);
+ free(res);
+
+ }
+ if (freeCmd) {
+ free(args[2]);
+ }
+ } else {
+ /*
+ * Normal assignment -- just do it.
+ */
+ Var_Set (line, cp, ctxt);
+ }
+}
+
+/*-
+ * ParseAddCmd --
+ * Lst_ForEach function to add a command line to all targets
+ *
+ * Results:
+ * Always 0
+ *
+ * Side Effects:
+ * A new element is added to the commands list of the node.
+ */
+static int
+ParseAddCmd(gnp, cmd)
+ ClientData gnp; /* the node to which the command is to be added */
+ ClientData cmd; /* the command to add */
+{
+ GNode *gn = (GNode *) gnp;
+ /* if target already supplied, ignore commands */
+ if (!(gn->type & OP_HAS_COMMANDS))
+ (void)Lst_AtEnd(gn->commands, cmd);
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ParseHasCommands --
+ * Callback procedure for Parse_File when destroying the list of
+ * targets on the last dependency line. Marks a target as already
+ * having commands if it does, to keep from having shell commands
+ * on multiple dependency lines.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * OP_HAS_COMMANDS may be set for the target.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+ParseHasCommands(gnp)
+ ClientData gnp; /* Node to examine */
+{
+ GNode *gn = (GNode *) gnp;
+ if (!Lst_IsEmpty(gn->commands)) {
+ gn->type |= OP_HAS_COMMANDS;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Parse_AddIncludeDir --
+ * Add a directory to the path searched for included makefiles
+ * bracketed by double-quotes. Used by functions in main.c
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The directory is appended to the list.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Parse_AddIncludeDir (dir)
+ char *dir; /* The name of the directory to add */
+{
+ Dir_AddDir (parseIncPath, dir);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseDoInclude --
+ * Push to another file.
+ *
+ * The input is the line minus the #include. A file spec is a string
+ * enclosed in <> or "". The former is looked for only in sysIncPath.
+ * The latter in . and the directories specified by -I command line
+ * options
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A structure is added to the includes Lst and readProc, lineno,
+ * fname and curFILE are altered for the new file
+ *---------------------------------------------------------------------
+ */
+static void
+ParseDoInclude (file)
+ char *file; /* file specification */
+{
+ char *fullname; /* full pathname of file */
+ IFile *oldFile; /* state associated with current file */
+ char endc; /* the character which ends the file spec */
+ char *cp; /* current position in file spec */
+ Boolean isSystem; /* TRUE if makefile is a system makefile */
+
+ /*
+ * Skip to delimiter character so we know where to look
+ */
+ while ((*file == ' ') || (*file == '\t')) {
+ file++;
+ }
+
+ if ((*file != '"') && (*file != '<')) {
+ Parse_Error (PARSE_FATAL,
+ ".include filename must be delimited by '\"' or '<'");
+ return;
+ }
+
+ /*
+ * Set the search path on which to find the include file based on the
+ * characters which bracket its name. Angle-brackets imply it's
+ * a system Makefile while double-quotes imply it's a user makefile
+ */
+ if (*file == '<') {
+ isSystem = TRUE;
+ endc = '>';
+ } else {
+ isSystem = FALSE;
+ endc = '"';
+ }
+
+ /*
+ * Skip to matching delimiter
+ */
+ for (cp = ++file; *cp && *cp != endc; cp++) {
+ continue;
+ }
+
+ if (*cp != endc) {
+ Parse_Error (PARSE_FATAL,
+ "Unclosed %cinclude filename. '%c' expected",
+ '.', endc);
+ return;
+ }
+ *cp = '\0';
+
+ /*
+ * Substitute for any variables in the file name before trying to
+ * find the thing.
+ */
+ file = Var_Subst (NULL, file, VAR_CMD, FALSE);
+
+ /*
+ * Now we know the file's name and its search path, we attempt to
+ * find the durn thing. A return of NULL indicates the file don't
+ * exist.
+ */
+ if (!isSystem) {
+ /*
+ * Include files contained in double-quotes are first searched for
+ * relative to the including file's location. We don't want to
+ * cd there, of course, so we just tack on the old file's
+ * leading path components and call Dir_FindFile to see if
+ * we can locate the beast.
+ */
+ char *prefEnd;
+
+ prefEnd = strrchr (fname, '/');
+ if (prefEnd != (char *)NULL) {
+ char *newName;
+
+ *prefEnd = '\0';
+ if (file[0] == '/')
+ newName = strdup(file);
+ else
+ newName = str_concat (fname, file, STR_ADDSLASH);
+ fullname = Dir_FindFile (newName, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(newName, dirSearchPath);
+ }
+ free (newName);
+ *prefEnd = '/';
+ } else {
+ fullname = (char *)NULL;
+ }
+ } else {
+ fullname = (char *)NULL;
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * System makefile or makefile wasn't found in same directory as
+ * included makefile. Search for it first on the -I search path,
+ * then on the .PATH search path, if not found in a -I directory.
+ * XXX: Suffix specific?
+ */
+ fullname = Dir_FindFile (file, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(file, dirSearchPath);
+ }
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * Still haven't found the makefile. Look for it on the system
+ * path as a last resort.
+ */
+ fullname = Dir_FindFile(file, sysIncPath);
+ }
+
+ if (fullname == (char *) NULL) {
+ *cp = endc;
+ Parse_Error (PARSE_FATAL, "Could not find %s", file);
+ return;
+ }
+
+ free(file);
+
+ /*
+ * Once we find the absolute path to the file, we get to save all the
+ * state from the current file before we can start reading this
+ * include file. The state is stored in an IFile structure which
+ * is placed on a list with other IFile structures. The list makes
+ * a very nice stack to track how we got here...
+ */
+ oldFile = (IFile *) emalloc (sizeof (IFile));
+ oldFile->fname = fname;
+
+ oldFile->F = curFILE;
+ oldFile->p = curPTR;
+ oldFile->lineno = lineno;
+
+ (void) Lst_AtFront (includes, (ClientData)oldFile);
+
+ /*
+ * Once the previous state has been saved, we can get down to reading
+ * the new file. We set up the name of the file to be the absolute
+ * name of the include file so error messages refer to the right
+ * place. Naturally enough, we start reading at line number 0.
+ */
+ fname = fullname;
+ lineno = 0;
+
+ curFILE = fopen (fullname, "r");
+ curPTR = NULL;
+ if (curFILE == (FILE * ) NULL) {
+ Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
+ /*
+ * Pop to previous file
+ */
+ (void) ParseEOF(0);
+ }
+}
+
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_FromString --
+ * Start Parsing from the given string
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A structure is added to the includes Lst and readProc, lineno,
+ * fname and curFILE are altered for the new file
+ *---------------------------------------------------------------------
+ */
+void
+Parse_FromString(str)
+ char *str;
+{
+ IFile *oldFile; /* state associated with this file */
+
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "%s\n----\n", str);
+
+ oldFile = (IFile *) emalloc (sizeof (IFile));
+ oldFile->lineno = lineno;
+ oldFile->fname = fname;
+ oldFile->F = curFILE;
+ oldFile->p = curPTR;
+
+ (void) Lst_AtFront (includes, (ClientData)oldFile);
+
+ curFILE = NULL;
+ curPTR = (PTR *) emalloc (sizeof (PTR));
+ curPTR->str = curPTR->ptr = str;
+ lineno = 0;
+ fname = strdup(fname);
+}
+
+
+#ifdef SYSVINCLUDE
+/*-
+ *---------------------------------------------------------------------
+ * ParseTraditionalInclude --
+ * Push to another file.
+ *
+ * The input is the line minus the "include". The file name is
+ * the string following the "include".
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A structure is added to the includes Lst and readProc, lineno,
+ * fname and curFILE are altered for the new file
+ *---------------------------------------------------------------------
+ */
+static void
+ParseTraditionalInclude (file)
+ char *file; /* file specification */
+{
+ char *fullname; /* full pathname of file */
+ IFile *oldFile; /* state associated with current file */
+ char *cp; /* current position in file spec */
+ char *prefEnd;
+
+ /*
+ * Skip over whitespace
+ */
+ while ((*file == ' ') || (*file == '\t')) {
+ file++;
+ }
+
+ if (*file == '\0') {
+ Parse_Error (PARSE_FATAL,
+ "Filename missing from \"include\"");
+ return;
+ }
+
+ /*
+ * Skip to end of line or next whitespace
+ */
+ for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) {
+ continue;
+ }
+
+ *cp = '\0';
+
+ /*
+ * Substitute for any variables in the file name before trying to
+ * find the thing.
+ */
+ file = Var_Subst (NULL, file, VAR_CMD, FALSE);
+
+ /*
+ * Now we know the file's name, we attempt to find the durn thing.
+ * A return of NULL indicates the file don't exist.
+ *
+ * Include files are first searched for relative to the including
+ * file's location. We don't want to cd there, of course, so we
+ * just tack on the old file's leading path components and call
+ * Dir_FindFile to see if we can locate the beast.
+ * XXX - this *does* search in the current directory, right?
+ */
+
+ prefEnd = strrchr (fname, '/');
+ if (prefEnd != (char *)NULL) {
+ char *newName;
+
+ *prefEnd = '\0';
+ newName = str_concat (fname, file, STR_ADDSLASH);
+ fullname = Dir_FindFile (newName, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(newName, dirSearchPath);
+ }
+ free (newName);
+ *prefEnd = '/';
+ } else {
+ fullname = (char *)NULL;
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * System makefile or makefile wasn't found in same directory as
+ * included makefile. Search for it first on the -I search path,
+ * then on the .PATH search path, if not found in a -I directory.
+ * XXX: Suffix specific?
+ */
+ fullname = Dir_FindFile (file, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(file, dirSearchPath);
+ }
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * Still haven't found the makefile. Look for it on the system
+ * path as a last resort.
+ */
+ fullname = Dir_FindFile(file, sysIncPath);
+ }
+
+ if (fullname == (char *) NULL) {
+ Parse_Error (PARSE_FATAL, "Could not find %s", file);
+ return;
+ }
+
+ /*
+ * Once we find the absolute path to the file, we get to save all the
+ * state from the current file before we can start reading this
+ * include file. The state is stored in an IFile structure which
+ * is placed on a list with other IFile structures. The list makes
+ * a very nice stack to track how we got here...
+ */
+ oldFile = (IFile *) emalloc (sizeof (IFile));
+ oldFile->fname = fname;
+
+ oldFile->F = curFILE;
+ oldFile->p = curPTR;
+ oldFile->lineno = lineno;
+
+ (void) Lst_AtFront (includes, (ClientData)oldFile);
+
+ /*
+ * Once the previous state has been saved, we can get down to reading
+ * the new file. We set up the name of the file to be the absolute
+ * name of the include file so error messages refer to the right
+ * place. Naturally enough, we start reading at line number 0.
+ */
+ fname = fullname;
+ lineno = 0;
+
+ curFILE = fopen (fullname, "r");
+ curPTR = NULL;
+ if (curFILE == (FILE * ) NULL) {
+ Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
+ /*
+ * Pop to previous file
+ */
+ (void) ParseEOF(1);
+ }
+}
+#endif
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseEOF --
+ * Called when EOF is reached in the current file. If we were reading
+ * an include file, the includes stack is popped and things set up
+ * to go back to reading the previous file at the previous location.
+ *
+ * Results:
+ * CONTINUE if there's more to do. DONE if not.
+ *
+ * Side Effects:
+ * The old curFILE, is closed. The includes list is shortened.
+ * lineno, curFILE, and fname are changed if CONTINUE is returned.
+ *---------------------------------------------------------------------
+ */
+static int
+ParseEOF (opened)
+ int opened;
+{
+ IFile *ifile; /* the state on the top of the includes stack */
+
+ if (Lst_IsEmpty (includes)) {
+ return (DONE);
+ }
+
+ ifile = (IFile *) Lst_DeQueue (includes);
+ free ((Address) fname);
+ fname = ifile->fname;
+ lineno = ifile->lineno;
+ if (opened && curFILE)
+ (void) fclose (curFILE);
+ if (curPTR) {
+ free((Address) curPTR->str);
+ free((Address) curPTR);
+ }
+ curFILE = ifile->F;
+ curPTR = ifile->p;
+ free ((Address)ifile);
+ return (CONTINUE);
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseReadc --
+ * Read a character from the current file
+ *
+ * Results:
+ * The character that was read
+ *
+ * Side Effects:
+ *---------------------------------------------------------------------
+ */
+static int
+ParseReadc()
+{
+ if (curFILE)
+ return fgetc(curFILE);
+
+ if (curPTR && *curPTR->ptr)
+ return *curPTR->ptr++;
+ return EOF;
+}
+
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseUnreadc --
+ * Put back a character to the current file
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ *---------------------------------------------------------------------
+ */
+static void
+ParseUnreadc(c)
+ int c;
+{
+ if (curFILE) {
+ ungetc(c, curFILE);
+ return;
+ }
+ if (curPTR) {
+ *--(curPTR->ptr) = c;
+ return;
+ }
+}
+
+
+/* ParseSkipLine():
+ * Grab the next line
+ */
+static char *
+ParseSkipLine(skip)
+ int skip; /* Skip lines that don't start with . */
+{
+ char *line;
+ int c, lastc = '\0', lineLength;
+ Buffer buf;
+
+ c = ParseReadc();
+
+ if (skip) {
+ /*
+ * Skip lines until get to one that begins with a
+ * special char.
+ */
+ while ((c != '.') && (c != EOF)) {
+ while (((c != '\n') || (lastc == '\\')) && (c != EOF))
+ {
+ /*
+ * Advance to next unescaped newline
+ */
+ if ((lastc = c) == '\n') {
+ lineno++;
+ }
+ c = ParseReadc();
+ }
+ lineno++;
+
+ lastc = c;
+ c = ParseReadc ();
+ }
+ }
+
+ if (c == EOF) {
+ Parse_Error (PARSE_FATAL, "Unclosed conditional/for loop");
+ return ((char *)NULL);
+ }
+
+ /*
+ * Read the entire line into buf
+ */
+ buf = Buf_Init (MAKE_BSIZE);
+ if (c != '\n') {
+ do {
+ Buf_AddByte (buf, (Byte)c);
+ c = ParseReadc();
+ } while ((c != '\n') && (c != EOF));
+ }
+ lineno++;
+
+ Buf_AddByte (buf, (Byte)'\0');
+ line = (char *)Buf_GetAll (buf, &lineLength);
+ Buf_Destroy (buf, FALSE);
+ return line;
+}
+
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseReadLine --
+ * Read an entire line from the input file. Called only by Parse_File.
+ * To facilitate escaped newlines and what have you, a character is
+ * buffered in 'lastc', which is '\0' when no characters have been
+ * read. When we break out of the loop, c holds the terminating
+ * character and lastc holds a character that should be added to
+ * the line (unless we don't read anything but a terminator).
+ *
+ * Results:
+ * A line w/o its newline
+ *
+ * Side Effects:
+ * Only those associated with reading a character
+ *---------------------------------------------------------------------
+ */
+static char *
+ParseReadLine ()
+{
+ Buffer buf; /* Buffer for current line */
+ register int c; /* the current character */
+ register int lastc; /* The most-recent character */
+ Boolean semiNL; /* treat semi-colons as newlines */
+ Boolean ignDepOp; /* TRUE if should ignore dependency operators
+ * for the purposes of setting semiNL */
+ Boolean ignComment; /* TRUE if should ignore comments (in a
+ * shell command */
+ char *line; /* Result */
+ char *ep; /* to strip trailing blanks */
+ int lineLength; /* Length of result */
+
+ semiNL = FALSE;
+ ignDepOp = FALSE;
+ ignComment = FALSE;
+
+ /*
+ * Handle special-characters at the beginning of the line. Either a
+ * leading tab (shell command) or pound-sign (possible conditional)
+ * forces us to ignore comments and dependency operators and treat
+ * semi-colons as semi-colons (by leaving semiNL FALSE). This also
+ * discards completely blank lines.
+ */
+ for (;;) {
+ c = ParseReadc();
+
+ if (c == '\t') {
+ ignComment = ignDepOp = TRUE;
+ break;
+ } else if (c == '\n') {
+ lineno++;
+ } else if (c == '#') {
+ ParseUnreadc(c);
+ break;
+ } else {
+ /*
+ * Anything else breaks out without doing anything
+ */
+ break;
+ }
+ }
+
+ if (c != EOF) {
+ lastc = c;
+ buf = Buf_Init(MAKE_BSIZE);
+
+ while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) &&
+ (c != EOF))
+ {
+test_char:
+ switch(c) {
+ case '\n':
+ /*
+ * Escaped newline: read characters until a non-space or an
+ * unescaped newline and replace them all by a single space.
+ * This is done by storing the space over the backslash and
+ * dropping through with the next nonspace. If it is a
+ * semi-colon and semiNL is TRUE, it will be recognized as a
+ * newline in the code below this...
+ */
+ lineno++;
+ lastc = ' ';
+ while ((c = ParseReadc ()) == ' ' || c == '\t') {
+ continue;
+ }
+ if (c == EOF || c == '\n') {
+ goto line_read;
+ } else {
+ /*
+ * Check for comments, semiNL's, etc. -- easier than
+ * ParseUnreadc(c); continue;
+ */
+ goto test_char;
+ }
+ /*NOTREACHED*/
+ break;
+
+ case ';':
+ /*
+ * Semi-colon: Need to see if it should be interpreted as a
+ * newline
+ */
+ if (semiNL) {
+ /*
+ * To make sure the command that may be following this
+ * semi-colon begins with a tab, we push one back into the
+ * input stream. This will overwrite the semi-colon in the
+ * buffer. If there is no command following, this does no
+ * harm, since the newline remains in the buffer and the
+ * whole line is ignored.
+ */
+ ParseUnreadc('\t');
+ goto line_read;
+ }
+ break;
+ case '=':
+ if (!semiNL) {
+ /*
+ * Haven't seen a dependency operator before this, so this
+ * must be a variable assignment -- don't pay attention to
+ * dependency operators after this.
+ */
+ ignDepOp = TRUE;
+ } else if (lastc == ':' || lastc == '!') {
+ /*
+ * Well, we've seen a dependency operator already, but it
+ * was the previous character, so this is really just an
+ * expanded variable assignment. Revert semi-colons to
+ * being just semi-colons again and ignore any more
+ * dependency operators.
+ *
+ * XXX: Note that a line like "foo : a:=b" will blow up,
+ * but who'd write a line like that anyway?
+ */
+ ignDepOp = TRUE; semiNL = FALSE;
+ }
+ break;
+ case '#':
+ if (!ignComment) {
+ if (compatMake && (lastc != '\\')) {
+ /*
+ * If the character is a hash mark and it isn't escaped
+ * (or we're being compatible), the thing is a comment.
+ * Skip to the end of the line.
+ */
+ do {
+ c = ParseReadc();
+ } while ((c != '\n') && (c != EOF));
+ goto line_read;
+ } else {
+ /*
+ * Don't add the backslash. Just let the # get copied
+ * over.
+ */
+ lastc = c;
+ continue;
+ }
+ }
+ break;
+ case ':':
+ case '!':
+ if (!ignDepOp && (c == ':' || c == '!')) {
+ /*
+ * A semi-colon is recognized as a newline only on
+ * dependency lines. Dependency lines are lines with a
+ * colon or an exclamation point. Ergo...
+ */
+ semiNL = TRUE;
+ }
+ break;
+ }
+ /*
+ * Copy in the previous character and save this one in lastc.
+ */
+ Buf_AddByte (buf, (Byte)lastc);
+ lastc = c;
+
+ }
+ line_read:
+ lineno++;
+
+ if (lastc != '\0') {
+ Buf_AddByte (buf, (Byte)lastc);
+ }
+ Buf_AddByte (buf, (Byte)'\0');
+ line = (char *)Buf_GetAll (buf, &lineLength);
+ Buf_Destroy (buf, FALSE);
+
+ /*
+ * Strip trailing blanks and tabs from the line.
+ * Do not strip a blank or tab that is preceeded by
+ * a '\'
+ */
+ ep = line;
+ while (*ep)
+ ++ep;
+ while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) {
+ if (ep[-2] == '\\')
+ break;
+ --ep;
+ }
+ *ep = 0;
+
+ if (line[0] == '.') {
+ /*
+ * The line might be a conditional. Ask the conditional module
+ * about it and act accordingly
+ */
+ switch (Cond_Eval (line)) {
+ case COND_SKIP:
+ /*
+ * Skip to next conditional that evaluates to COND_PARSE.
+ */
+ do {
+ free (line);
+ line = ParseSkipLine(1);
+ } while (line && Cond_Eval(line) != COND_PARSE);
+ if (line == NULL)
+ break;
+ /*FALLTHRU*/
+ case COND_PARSE:
+ free ((Address) line);
+ line = ParseReadLine();
+ break;
+ case COND_INVALID:
+ if (For_Eval(line)) {
+ int ok;
+ free(line);
+ do {
+ /*
+ * Skip after the matching end
+ */
+ line = ParseSkipLine(0);
+ if (line == NULL) {
+ Parse_Error (PARSE_FATAL,
+ "Unexpected end of file in for loop.\n");
+ break;
+ }
+ ok = For_Eval(line);
+ free(line);
+ }
+ while (ok);
+ if (line != NULL)
+ For_Run();
+ line = ParseReadLine();
+ }
+ break;
+ }
+ }
+ return (line);
+
+ } else {
+ /*
+ * Hit end-of-file, so return a NULL line to indicate this.
+ */
+ return((char *)NULL);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ParseFinishLine --
+ * Handle the end of a dependency group.
+ *
+ * Results:
+ * Nothing.
+ *
+ * Side Effects:
+ * inLine set FALSE. 'targets' list destroyed.
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+ParseFinishLine()
+{
+ if (inLine) {
+ Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL);
+ Lst_Destroy (targets, ParseHasCommands);
+ targets = NULL;
+ inLine = FALSE;
+ }
+}
+
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_File --
+ * Parse a file into its component parts, incorporating it into the
+ * current dependency graph. This is the main function and controls
+ * almost every other function in this module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Loads. Nodes are added to the list of all targets, nodes and links
+ * are added to the dependency graph. etc. etc. etc.
+ *---------------------------------------------------------------------
+ */
+void
+Parse_File(name, stream)
+ char *name; /* the name of the file being read */
+ FILE * stream; /* Stream open to makefile to parse */
+{
+ register char *cp, /* pointer into the line */
+ *line; /* the line we're working on */
+
+ inLine = FALSE;
+ fname = name;
+ curFILE = stream;
+ lineno = 0;
+ fatals = 0;
+
+ do {
+ while ((line = ParseReadLine ()) != NULL) {
+ if (*line == '.') {
+ /*
+ * Lines that begin with the special character are either
+ * include or undef directives.
+ */
+ for (cp = line + 1; isspace (*cp); cp++) {
+ continue;
+ }
+ if (strncmp (cp, "include", 7) == 0) {
+ ParseDoInclude (cp + 7);
+ goto nextLine;
+ } else if (strncmp(cp, "undef", 5) == 0) {
+ char *cp2;
+ for (cp += 5; isspace((unsigned char) *cp); cp++) {
+ continue;
+ }
+
+ for (cp2 = cp; !isspace((unsigned char) *cp2) &&
+ (*cp2 != '\0'); cp2++) {
+ continue;
+ }
+
+ *cp2 = '\0';
+
+ Var_Delete(cp, VAR_GLOBAL);
+ goto nextLine;
+ }
+ }
+ if (*line == '#') {
+ /* If we're this far, the line must be a comment. */
+ goto nextLine;
+ }
+
+ if (*line == '\t') {
+ /*
+ * If a line starts with a tab, it can only hope to be
+ * a creation command.
+ */
+#ifndef POSIX
+ shellCommand:
+#endif
+ for (cp = line + 1; isspace (*cp); cp++) {
+ continue;
+ }
+ if (*cp) {
+ if (inLine) {
+ /*
+ * So long as it's not a blank line and we're actually
+ * in a dependency spec, add the command to the list of
+ * commands of all targets in the dependency spec
+ */
+ Lst_ForEach (targets, ParseAddCmd, cp);
+ Lst_AtEnd(targCmds, (ClientData) line);
+ continue;
+ } else {
+ Parse_Error (PARSE_FATAL,
+ "Unassociated shell command \"%.20s\"",
+ cp);
+ }
+ }
+#ifdef SYSVINCLUDE
+ } else if (strncmp (line, "include", 7) == 0 &&
+ isspace(line[7]) &&
+ strchr(line, ':') == NULL) {
+ /*
+ * It's an S3/S5-style "include".
+ */
+ ParseTraditionalInclude (line + 7);
+ goto nextLine;
+#endif
+ } else if (Parse_IsVar (line)) {
+ ParseFinishLine();
+ Parse_DoVar (line, VAR_GLOBAL);
+ } else {
+ /*
+ * We now know it's a dependency line so it needs to have all
+ * variables expanded before being parsed. Tell the variable
+ * module to complain if some variable is undefined...
+ * To make life easier on novices, if the line is indented we
+ * first make sure the line has a dependency operator in it.
+ * If it doesn't have an operator and we're in a dependency
+ * line's script, we assume it's actually a shell command
+ * and add it to the current list of targets.
+ */
+#ifndef POSIX
+ Boolean nonSpace = FALSE;
+#endif
+
+ cp = line;
+ if (isspace((unsigned char) line[0])) {
+ while ((*cp != '\0') && isspace((unsigned char) *cp)) {
+ cp++;
+ }
+ if (*cp == '\0') {
+ goto nextLine;
+ }
+#ifndef POSIX
+ while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) {
+ nonSpace = TRUE;
+ cp++;
+ }
+#endif
+ }
+
+#ifndef POSIX
+ if (*cp == '\0') {
+ if (inLine) {
+ Parse_Error (PARSE_WARNING,
+ "Shell command needs a leading tab");
+ goto shellCommand;
+ } else if (nonSpace) {
+ Parse_Error (PARSE_FATAL, "Missing operator");
+ }
+ } else {
+#endif
+ ParseFinishLine();
+
+ cp = Var_Subst (NULL, line, VAR_CMD, TRUE);
+ free (line);
+ line = cp;
+
+ /*
+ * Need a non-circular list for the target nodes
+ */
+ if (targets)
+ Lst_Destroy(targets, NOFREE);
+
+ targets = Lst_Init (FALSE);
+ inLine = TRUE;
+
+ ParseDoDependency (line);
+#ifndef POSIX
+ }
+#endif
+ }
+
+ nextLine:
+
+ free (line);
+ }
+ /*
+ * Reached EOF, but it may be just EOF of an include file...
+ */
+ } while (ParseEOF(1) == CONTINUE);
+
+ /*
+ * Make sure conditionals are clean
+ */
+ Cond_End();
+
+ if (fatals) {
+ fprintf (stderr, "Fatal errors encountered -- cannot continue\n");
+ exit (1);
+ }
+}
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_Init --
+ * initialize the parsing module
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * the parseIncPath list is initialized...
+ *---------------------------------------------------------------------
+ */
+void
+Parse_Init ()
+{
+ char *cp = NULL, *start;
+ /* avoid faults on read-only strings */
+ static char syspath[] = _PATH_DEFSYSPATH;
+
+ mainNode = NILGNODE;
+ parseIncPath = Lst_Init (FALSE);
+ sysIncPath = Lst_Init (FALSE);
+ includes = Lst_Init (FALSE);
+ targCmds = Lst_Init (FALSE);
+
+ /*
+ * Add the directories from the DEFSYSPATH (more than one may be given
+ * as dir1:...:dirn) to the system include path.
+ */
+ for (start = syspath; *start != '\0'; start = cp) {
+ for (cp = start; *cp != '\0' && *cp != ':'; cp++)
+ continue;
+ if (*cp == '\0') {
+ Dir_AddDir(sysIncPath, start);
+ } else {
+ *cp++ = '\0';
+ Dir_AddDir(sysIncPath, start);
+ }
+ }
+}
+
+void
+Parse_End()
+{
+ Lst_Destroy(targCmds, (void (*) __P((ClientData))) free);
+ if (targets)
+ Lst_Destroy(targets, NOFREE);
+ Lst_Destroy(sysIncPath, Dir_Destroy);
+ Lst_Destroy(parseIncPath, Dir_Destroy);
+ Lst_Destroy(includes, NOFREE); /* Should be empty now */
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Parse_MainName --
+ * Return a Lst of the main target to create for main()'s sake. If
+ * no such target exists, we Punt with an obnoxious error message.
+ *
+ * Results:
+ * A Lst of the single node to create.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+Lst
+Parse_MainName()
+{
+ Lst main; /* result list */
+
+ main = Lst_Init (FALSE);
+
+ if (mainNode == NILGNODE) {
+ Punt ("make: no target to make.\n");
+ /*NOTREACHED*/
+ } else if (mainNode->type & OP_DOUBLEDEP) {
+ (void) Lst_AtEnd (main, (ClientData)mainNode);
+ Lst_Concat(main, mainNode->cohorts, LST_CONCNEW);
+ }
+ else
+ (void) Lst_AtEnd (main, (ClientData)mainNode);
+ return (main);
+}
diff --git a/usr.bin/make/pathnames.h b/usr.bin/make/pathnames.h
new file mode 100644
index 0000000..2c25ead
--- /dev/null
+++ b/usr.bin/make/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ * $Id: pathnames.h,v 1.2 1993/08/01 18:11:58 mycroft Exp $
+ */
+
+#define _PATH_OBJDIR "obj"
+#define _PATH_DEFSHELLDIR "/bin"
+#define _PATH_DEFSYSMK "/usr/share/mk/sys.mk"
+#define _PATH_DEFSYSPATH "/usr/share/mk"
diff --git a/usr.bin/make/sprite.h b/usr.bin/make/sprite.h
new file mode 100644
index 0000000..d5462d5
--- /dev/null
+++ b/usr.bin/make/sprite.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sprite.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * sprite.h --
+ *
+ * Common constants and type declarations for Sprite.
+ */
+
+#ifndef _SPRITE
+#define _SPRITE
+
+
+/*
+ * A boolean type is defined as an integer, not an enum. This allows a
+ * boolean argument to be an expression that isn't strictly 0 or 1 valued.
+ */
+
+typedef int Boolean;
+#ifndef TRUE
+#define TRUE 1
+#endif TRUE
+#ifndef FALSE
+#define FALSE 0
+#endif FALSE
+
+/*
+ * Functions that must return a status can return a ReturnStatus to
+ * indicate success or type of failure.
+ */
+
+typedef int ReturnStatus;
+
+/*
+ * The following statuses overlap with the first 2 generic statuses
+ * defined in status.h:
+ *
+ * SUCCESS There was no error.
+ * FAILURE There was a general error.
+ */
+
+#define SUCCESS 0x00000000
+#define FAILURE 0x00000001
+
+
+/*
+ * A nil pointer must be something that will cause an exception if
+ * referenced. There are two nils: the kernels nil and the nil used
+ * by user processes.
+ */
+
+#define NIL ~0
+#define USER_NIL 0
+#ifndef NULL
+#define NULL 0
+#endif NULL
+
+/*
+ * An address is just a pointer in C. It is defined as a character pointer
+ * so that address arithmetic will work properly, a byte at a time.
+ */
+
+typedef char *Address;
+
+/*
+ * ClientData is an uninterpreted word. It is defined as an int so that
+ * kdbx will not interpret client data as a string. Unlike an "Address",
+ * client data will generally not be used in arithmetic.
+ * But we don't have kdbx anymore so we define it as void (christos)
+ */
+
+typedef void *ClientData;
+
+#endif /* _SPRITE */
diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c
new file mode 100644
index 0000000..2dea338
--- /dev/null
+++ b/usr.bin/make/str.c
@@ -0,0 +1,492 @@
+/*-
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+/* from: static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: str.c,v 1.4 1995/05/30 06:32:05 rgrimes Exp $";
+#endif /* not lint */
+
+#include "make.h"
+
+static char **argv, *buffer;
+static int argmax, curlen;
+
+/*
+ * str_init --
+ * Initialize the strings package
+ *
+ */
+void
+str_init()
+{
+ char *p1;
+ argv = (char **)emalloc((argmax = 50) * sizeof(char *));
+ argv[0] = Var_Value(".MAKE", VAR_GLOBAL, &p1);
+}
+
+
+/*
+ * str_end --
+ * Cleanup the strings package
+ *
+ */
+void
+str_end()
+{
+ if (argv) {
+ if (argv[0])
+ free(argv[0]);
+ free((Address) argv);
+ }
+ if (buffer)
+ free(buffer);
+}
+
+/*-
+ * str_concat --
+ * concatenate the two strings, inserting a space or slash between them,
+ * freeing them if requested.
+ *
+ * returns --
+ * the resulting string in allocated space.
+ */
+char *
+str_concat(s1, s2, flags)
+ char *s1, *s2;
+ int flags;
+{
+ register int len1, len2;
+ register char *result;
+
+ /* get the length of both strings */
+ len1 = strlen(s1);
+ len2 = strlen(s2);
+
+ /* allocate length plus separator plus EOS */
+ result = emalloc((u_int)(len1 + len2 + 2));
+
+ /* copy first string into place */
+ memcpy(result, s1, len1);
+
+ /* add separator character */
+ if (flags & STR_ADDSPACE) {
+ result[len1] = ' ';
+ ++len1;
+ } else if (flags & STR_ADDSLASH) {
+ result[len1] = '/';
+ ++len1;
+ }
+
+ /* copy second string plus EOS into place */
+ memcpy(result + len1, s2, len2 + 1);
+
+ /* free original strings */
+ if (flags & STR_DOFREE) {
+ (void)free(s1);
+ (void)free(s2);
+ }
+ return(result);
+}
+
+/*-
+ * brk_string --
+ * Fracture a string into an array of words (as delineated by tabs or
+ * spaces) taking quotation marks into account. Leading tabs/spaces
+ * are ignored.
+ *
+ * returns --
+ * Pointer to the array of pointers to the words. To make life easier,
+ * the first word is always the value of the .MAKE variable.
+ */
+char **
+brk_string(str, store_argc, expand)
+ register char *str;
+ int *store_argc;
+ Boolean expand;
+{
+ register int argc, ch;
+ register char inquote, *p, *start, *t;
+ int len;
+
+ /* skip leading space chars. */
+ for (; *str == ' ' || *str == '\t'; ++str)
+ continue;
+
+ /* allocate room for a copy of the string */
+ if ((len = strlen(str) + 1) > curlen) {
+ if (buffer)
+ free(buffer);
+ buffer = emalloc(curlen = len);
+ }
+
+ /*
+ * copy the string; at the same time, parse backslashes,
+ * quotes and build the argument list.
+ */
+ argc = 1;
+ inquote = '\0';
+ for (p = str, start = t = buffer;; ++p) {
+ switch(ch = *p) {
+ case '"':
+ case '\'':
+ if (inquote)
+ if (inquote == ch)
+ inquote = '\0';
+ else
+ break;
+ else {
+ inquote = (char) ch;
+ /* Don't miss "" or '' */
+ if (start == NULL && p[1] == inquote) {
+ start = t + 1;
+ break;
+ }
+ }
+ if (!expand) {
+ if (!start)
+ start = t;
+ *t++ = ch;
+ }
+ continue;
+ case ' ':
+ case '\t':
+ case '\n':
+ if (inquote)
+ break;
+ if (!start)
+ continue;
+ /* FALLTHROUGH */
+ case '\0':
+ /*
+ * end of a token -- make sure there's enough argv
+ * space and save off a pointer.
+ */
+ if (!start)
+ goto done;
+
+ *t++ = '\0';
+ if (argc == argmax) {
+ argmax *= 2; /* ramp up fast */
+ if (!(argv = (char **)realloc(argv,
+ argmax * sizeof(char *))))
+ enomem();
+ }
+ argv[argc++] = start;
+ start = (char *)NULL;
+ if (ch == '\n' || ch == '\0')
+ goto done;
+ continue;
+ case '\\':
+ if (!expand) {
+ if (!start)
+ start = t;
+ *t++ = '\\';
+ ch = *++p;
+ break;
+ }
+
+ switch (ch = *++p) {
+ case '\0':
+ case '\n':
+ /* hmmm; fix it up as best we can */
+ ch = '\\';
+ --p;
+ break;
+ case 'b':
+ ch = '\b';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ case 'n':
+ ch = '\n';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ }
+ break;
+ }
+ if (!start)
+ start = t;
+ *t++ = (char) ch;
+ }
+done: argv[argc] = (char *)NULL;
+ *store_argc = argc;
+ return(argv);
+}
+
+/*
+ * Str_FindSubstring -- See if a string contains a particular substring.
+ *
+ * Results: If string contains substring, the return value is the location of
+ * the first matching instance of substring in string. If string doesn't
+ * contain substring, the return value is NULL. Matching is done on an exact
+ * character-for-character basis with no wildcards or special characters.
+ *
+ * Side effects: None.
+ */
+char *
+Str_FindSubstring(string, substring)
+ register char *string; /* String to search. */
+ char *substring; /* Substring to find in string */
+{
+ register char *a, *b;
+
+ /*
+ * First scan quickly through the two strings looking for a single-
+ * character match. When it's found, then compare the rest of the
+ * substring.
+ */
+
+ for (b = substring; *string != 0; string += 1) {
+ if (*string != *b)
+ continue;
+ a = string;
+ for (;;) {
+ if (*b == 0)
+ return(string);
+ if (*a++ != *b++)
+ break;
+ }
+ b = substring;
+ }
+ return((char *) NULL);
+}
+
+/*
+ * Str_Match --
+ *
+ * See if a particular string matches a particular pattern.
+ *
+ * Results: Non-zero is returned if string matches pattern, 0 otherwise. The
+ * matching operation permits the following special characters in the
+ * pattern: *?\[] (see the man page for details on what these mean).
+ *
+ * Side effects: None.
+ */
+int
+Str_Match(string, pattern)
+ register char *string; /* String */
+ register char *pattern; /* Pattern */
+{
+ char c2;
+
+ for (;;) {
+ /*
+ * See if we're at the end of both the pattern and the
+ * string. If, we succeeded. If we're at the end of the
+ * pattern but not at the end of the string, we failed.
+ */
+ if (*pattern == 0)
+ return(!*string);
+ if (*string == 0 && *pattern != '*')
+ return(0);
+ /*
+ * Check for a "*" as the next pattern character. It matches
+ * any substring. We handle this by calling ourselves
+ * recursively for each postfix of string, until either we
+ * match or we reach the end of the string.
+ */
+ if (*pattern == '*') {
+ pattern += 1;
+ if (*pattern == 0)
+ return(1);
+ while (*string != 0) {
+ if (Str_Match(string, pattern))
+ return(1);
+ ++string;
+ }
+ return(0);
+ }
+ /*
+ * Check for a "?" as the next pattern character. It matches
+ * any single character.
+ */
+ if (*pattern == '?')
+ goto thisCharOK;
+ /*
+ * Check for a "[" as the next pattern character. It is
+ * followed by a list of characters that are acceptable, or
+ * by a range (two characters separated by "-").
+ */
+ if (*pattern == '[') {
+ ++pattern;
+ for (;;) {
+ if ((*pattern == ']') || (*pattern == 0))
+ return(0);
+ if (*pattern == *string)
+ break;
+ if (pattern[1] == '-') {
+ c2 = pattern[2];
+ if (c2 == 0)
+ return(0);
+ if ((*pattern <= *string) &&
+ (c2 >= *string))
+ break;
+ if ((*pattern >= *string) &&
+ (c2 <= *string))
+ break;
+ pattern += 2;
+ }
+ ++pattern;
+ }
+ while ((*pattern != ']') && (*pattern != 0))
+ ++pattern;
+ goto thisCharOK;
+ }
+ /*
+ * If the next pattern character is '/', just strip off the
+ * '/' so we do exact matching on the character that follows.
+ */
+ if (*pattern == '\\') {
+ ++pattern;
+ if (*pattern == 0)
+ return(0);
+ }
+ /*
+ * There's no special character. Just make sure that the
+ * next characters of each string match.
+ */
+ if (*pattern != *string)
+ return(0);
+thisCharOK: ++pattern;
+ ++string;
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Str_SYSVMatch --
+ * Check word against pattern for a match (% is wild),
+ *
+ * Results:
+ * Returns the beginning position of a match or null. The number
+ * of characters matched is returned in len.
+ *
+ * Side Effects:
+ * None
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Str_SYSVMatch(word, pattern, len)
+ char *word; /* Word to examine */
+ char *pattern; /* Pattern to examine against */
+ int *len; /* Number of characters to substitute */
+{
+ char *p = pattern;
+ char *w = word;
+ char *m;
+
+ if (*p == '\0') {
+ /* Null pattern is the whole string */
+ *len = strlen(w);
+ return w;
+ }
+
+ if ((m = strchr(p, '%')) != NULL) {
+ /* check that the prefix matches */
+ for (; p != m && *w && *w == *p; w++, p++)
+ continue;
+
+ if (p != m)
+ return NULL; /* No match */
+
+ if (*++p == '\0') {
+ /* No more pattern, return the rest of the string */
+ *len = strlen(w);
+ return w;
+ }
+ }
+
+ m = w;
+
+ /* Find a matching tail */
+ do
+ if (strcmp(p, w) == 0) {
+ *len = w - m;
+ return m;
+ }
+ while (*w++ != '\0');
+
+ return NULL;
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Str_SYSVSubst --
+ * Substitute '%' on the pattern with len characters from src.
+ * If the pattern does not contain a '%' prepend len characters
+ * from src.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Places result on buf
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Str_SYSVSubst(buf, pat, src, len)
+ Buffer buf;
+ char *pat;
+ char *src;
+ int len;
+{
+ char *m;
+
+ if ((m = strchr(pat, '%')) != NULL) {
+ /* Copy the prefix */
+ Buf_AddBytes(buf, m - pat, (Byte *) pat);
+ /* skip the % */
+ pat = m + 1;
+ }
+
+ /* Copy the pattern */
+ Buf_AddBytes(buf, len, (Byte *) src);
+
+ /* append the rest */
+ Buf_AddBytes(buf, strlen(pat), (Byte *) pat);
+}
diff --git a/usr.bin/make/suff.c b/usr.bin/make/suff.c
new file mode 100644
index 0000000..7340383
--- /dev/null
+++ b/usr.bin/make/suff.c
@@ -0,0 +1,2435 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94";
+#endif /* not lint */
+
+/*-
+ * suff.c --
+ * Functions to maintain suffix lists and find implicit dependents
+ * using suffix transformation rules
+ *
+ * Interface:
+ * Suff_Init Initialize all things to do with suffixes.
+ *
+ * Suff_End Cleanup the module
+ *
+ * Suff_DoPaths This function is used to make life easier
+ * when searching for a file according to its
+ * suffix. It takes the global search path,
+ * as defined using the .PATH: target, and appends
+ * its directories to the path of each of the
+ * defined suffixes, as specified using
+ * .PATH<suffix>: targets. In addition, all
+ * directories given for suffixes labeled as
+ * include files or libraries, using the .INCLUDES
+ * or .LIBS targets, are played with using
+ * Dir_MakeFlags to create the .INCLUDES and
+ * .LIBS global variables.
+ *
+ * Suff_ClearSuffixes Clear out all the suffixes and defined
+ * transformations.
+ *
+ * Suff_IsTransform Return TRUE if the passed string is the lhs
+ * of a transformation rule.
+ *
+ * Suff_AddSuffix Add the passed string as another known suffix.
+ *
+ * Suff_GetPath Return the search path for the given suffix.
+ *
+ * Suff_AddInclude Mark the given suffix as denoting an include
+ * file.
+ *
+ * Suff_AddLib Mark the given suffix as denoting a library.
+ *
+ * Suff_AddTransform Add another transformation to the suffix
+ * graph. Returns GNode suitable for framing, I
+ * mean, tacking commands, attributes, etc. on.
+ *
+ * Suff_SetNull Define the suffix to consider the suffix of
+ * any file that doesn't have a known one.
+ *
+ * Suff_FindDeps Find implicit sources for and the location of
+ * a target based on its suffix. Returns the
+ * bottom-most node added to the graph or NILGNODE
+ * if the target had no implicit sources.
+ */
+
+#include <stdio.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "bit.h"
+
+static Lst sufflist; /* Lst of suffixes */
+static Lst suffClean; /* Lst of suffixes to be cleaned */
+static Lst srclist; /* Lst of sources */
+static Lst transforms; /* Lst of transformation rules */
+
+static int sNum = 0; /* Counter for assigning suffix numbers */
+
+/*
+ * Structure describing an individual suffix.
+ */
+typedef struct _Suff {
+ char *name; /* The suffix itself */
+ int nameLen; /* Length of the suffix */
+ short flags; /* Type of suffix */
+#define SUFF_INCLUDE 0x01 /* One which is #include'd */
+#define SUFF_LIBRARY 0x02 /* One which contains a library */
+#define SUFF_NULL 0x04 /* The empty suffix */
+ Lst searchPath; /* The path along which files of this suffix
+ * may be found */
+ int sNum; /* The suffix number */
+ int refCount; /* Reference count of list membership */
+ Lst parents; /* Suffixes we have a transformation to */
+ Lst children; /* Suffixes we have a transformation from */
+ Lst ref; /* List of lists this suffix is referenced */
+} Suff;
+
+/*
+ * Structure used in the search for implied sources.
+ */
+typedef struct _Src {
+ char *file; /* The file to look for */
+ char *pref; /* Prefix from which file was formed */
+ Suff *suff; /* The suffix on the file */
+ struct _Src *parent; /* The Src for which this is a source */
+ GNode *node; /* The node describing the file */
+ int children; /* Count of existing children (so we don't free
+ * this thing too early or never nuke it) */
+#ifdef DEBUG_SRC
+ Lst cp; /* Debug; children list */
+#endif
+} Src;
+
+/*
+ * A structure for passing more than one argument to the Lst-library-invoked
+ * function...
+ */
+typedef struct {
+ Lst l;
+ Src *s;
+} LstSrc;
+
+static Suff *suffNull; /* The NULL suffix for this run */
+static Suff *emptySuff; /* The empty suffix required for POSIX
+ * single-suffix transformation rules */
+
+
+static char *SuffStrIsPrefix __P((char *, char *));
+static char *SuffSuffIsSuffix __P((Suff *, char *));
+static int SuffSuffIsSuffixP __P((ClientData, ClientData));
+static int SuffSuffHasNameP __P((ClientData, ClientData));
+static int SuffSuffIsPrefix __P((ClientData, ClientData));
+static int SuffGNHasNameP __P((ClientData, ClientData));
+static void SuffFree __P((ClientData));
+static void SuffInsert __P((Lst, Suff *));
+static void SuffRemove __P((Lst, Suff *));
+static Boolean SuffParseTransform __P((char *, Suff **, Suff **));
+static int SuffRebuildGraph __P((ClientData, ClientData));
+static int SuffAddSrc __P((ClientData, ClientData));
+static int SuffRemoveSrc __P((Lst));
+static void SuffAddLevel __P((Lst, Src *));
+static Src *SuffFindThem __P((Lst, Lst));
+static Src *SuffFindCmds __P((Src *, Lst));
+static int SuffExpandChildren __P((ClientData, ClientData));
+static Boolean SuffApplyTransform __P((GNode *, GNode *, Suff *, Suff *));
+static void SuffFindDeps __P((GNode *, Lst));
+static void SuffFindArchiveDeps __P((GNode *, Lst));
+static void SuffFindNormalDeps __P((GNode *, Lst));
+static int SuffPrintName __P((ClientData, ClientData));
+static int SuffPrintSuff __P((ClientData, ClientData));
+static int SuffPrintTrans __P((ClientData, ClientData));
+
+ /*************** Lst Predicates ****************/
+/*-
+ *-----------------------------------------------------------------------
+ * SuffStrIsPrefix --
+ * See if pref is a prefix of str.
+ *
+ * Results:
+ * NULL if it ain't, pointer to character in str after prefix if so
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static char *
+SuffStrIsPrefix (pref, str)
+ register char *pref; /* possible prefix */
+ register char *str; /* string to check */
+{
+ while (*str && *pref == *str) {
+ pref++;
+ str++;
+ }
+
+ return (*pref ? NULL : str);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffSuffIsSuffix --
+ * See if suff is a suffix of str. Str should point to THE END of the
+ * string to check. (THE END == the null byte)
+ *
+ * Results:
+ * NULL if it ain't, pointer to character in str before suffix if
+ * it is.
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static char *
+SuffSuffIsSuffix (s, str)
+ register Suff *s; /* possible suffix */
+ char *str; /* string to examine */
+{
+ register char *p1; /* Pointer into suffix name */
+ register char *p2; /* Pointer into string being examined */
+
+ p1 = s->name + s->nameLen;
+ p2 = str;
+
+ while (p1 >= s->name && *p1 == *p2) {
+ p1--;
+ p2--;
+ }
+
+ return (p1 == s->name - 1 ? p2 : NULL);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffSuffIsSuffixP --
+ * Predicate form of SuffSuffIsSuffix. Passed as the callback function
+ * to Lst_Find.
+ *
+ * Results:
+ * 0 if the suffix is the one desired, non-zero if not.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffSuffIsSuffixP(s, str)
+ ClientData s;
+ ClientData str;
+{
+ return(!SuffSuffIsSuffix((Suff *) s, (char *) str));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffSuffHasNameP --
+ * Callback procedure for finding a suffix based on its name. Used by
+ * Suff_GetPath.
+ *
+ * Results:
+ * 0 if the suffix is of the given name. non-zero otherwise.
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffSuffHasNameP (s, sname)
+ ClientData s; /* Suffix to check */
+ ClientData sname; /* Desired name */
+{
+ return (strcmp ((char *) sname, ((Suff *) s)->name));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffSuffIsPrefix --
+ * See if the suffix described by s is a prefix of the string. Care
+ * must be taken when using this to search for transformations and
+ * what-not, since there could well be two suffixes, one of which
+ * is a prefix of the other...
+ *
+ * Results:
+ * 0 if s is a prefix of str. non-zero otherwise
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffSuffIsPrefix (s, str)
+ ClientData s; /* suffix to compare */
+ ClientData str; /* string to examine */
+{
+ return (SuffStrIsPrefix (((Suff *) s)->name, (char *) str) == NULL ? 1 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffGNHasNameP --
+ * See if the graph node has the desired name
+ *
+ * Results:
+ * 0 if it does. non-zero if it doesn't
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffGNHasNameP (gn, name)
+ ClientData gn; /* current node we're looking at */
+ ClientData name; /* name we're looking for */
+{
+ return (strcmp ((char *) name, ((GNode *) gn)->name));
+}
+
+ /*********** Maintenance Functions ************/
+
+static void
+SuffUnRef(lp, sp)
+ ClientData lp;
+ ClientData sp;
+{
+ Lst l = (Lst) lp;
+
+ LstNode ln = Lst_Member(l, sp);
+ if (ln != NILLNODE) {
+ Lst_Remove(l, ln);
+ ((Suff *) sp)->refCount--;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffFree --
+ * Free up all memory associated with the given suffix structure.
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * the suffix entry is detroyed
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffFree (sp)
+ ClientData sp;
+{
+ Suff *s = (Suff *) sp;
+
+ if (s == suffNull)
+ suffNull = NULL;
+
+ if (s == emptySuff)
+ emptySuff = NULL;
+
+ Lst_Destroy (s->ref, NOFREE);
+ Lst_Destroy (s->children, NOFREE);
+ Lst_Destroy (s->parents, NOFREE);
+ Lst_Destroy (s->searchPath, Dir_Destroy);
+
+ free ((Address)s->name);
+ free ((Address)s);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffRemove --
+ * Remove the suffix into the list
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The reference count for the suffix is decremented and the
+ * suffix is possibly freed
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffRemove(l, s)
+ Lst l;
+ Suff *s;
+{
+ SuffUnRef((ClientData) l, (ClientData) s);
+ if (s->refCount == 0)
+ SuffFree((ClientData) s);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffInsert --
+ * Insert the suffix into the list keeping the list ordered by suffix
+ * numbers.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The reference count of the suffix is incremented
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffInsert (l, s)
+ Lst l; /* the list where in s should be inserted */
+ Suff *s; /* the suffix to insert */
+{
+ LstNode ln; /* current element in l we're examining */
+ Suff *s2 = NULL; /* the suffix descriptor in this element */
+
+ if (Lst_Open (l) == FAILURE) {
+ return;
+ }
+ while ((ln = Lst_Next (l)) != NILLNODE) {
+ s2 = (Suff *) Lst_Datum (ln);
+ if (s2->sNum >= s->sNum) {
+ break;
+ }
+ }
+
+ Lst_Close (l);
+ if (DEBUG(SUFF)) {
+ printf("inserting %s(%d)...", s->name, s->sNum);
+ }
+ if (ln == NILLNODE) {
+ if (DEBUG(SUFF)) {
+ printf("at end of list\n");
+ }
+ (void)Lst_AtEnd (l, (ClientData)s);
+ s->refCount++;
+ (void)Lst_AtEnd(s->ref, (ClientData) l);
+ } else if (s2->sNum != s->sNum) {
+ if (DEBUG(SUFF)) {
+ printf("before %s(%d)\n", s2->name, s2->sNum);
+ }
+ (void)Lst_Insert (l, ln, (ClientData)s);
+ s->refCount++;
+ (void)Lst_AtEnd(s->ref, (ClientData) l);
+ } else if (DEBUG(SUFF)) {
+ printf("already there\n");
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_ClearSuffixes --
+ * This is gross. Nuke the list of suffixes but keep all transformation
+ * rules around. The transformation graph is destroyed in this process,
+ * but we leave the list of rules so when a new graph is formed the rules
+ * will remain.
+ * This function is called from the parse module when a
+ * .SUFFIXES:\n line is encountered.
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * the sufflist and its graph nodes are destroyed
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_ClearSuffixes ()
+{
+ Lst_Concat (suffClean, sufflist, LST_CONCLINK);
+ sufflist = Lst_Init(FALSE);
+ sNum = 0;
+ suffNull = emptySuff;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffParseTransform --
+ * Parse a transformation string to find its two component suffixes.
+ *
+ * Results:
+ * TRUE if the string is a valid transformation and FALSE otherwise.
+ *
+ * Side Effects:
+ * The passed pointers are overwritten.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+SuffParseTransform(str, srcPtr, targPtr)
+ char *str; /* String being parsed */
+ Suff **srcPtr; /* Place to store source of trans. */
+ Suff **targPtr; /* Place to store target of trans. */
+{
+ register LstNode srcLn; /* element in suffix list of trans source*/
+ register Suff *src; /* Source of transformation */
+ register LstNode targLn; /* element in suffix list of trans target*/
+ register char *str2; /* Extra pointer (maybe target suffix) */
+ LstNode singleLn; /* element in suffix list of any suffix
+ * that exactly matches str */
+ Suff *single = NULL;/* Source of possible transformation to
+ * null suffix */
+
+ srcLn = NILLNODE;
+ singleLn = NILLNODE;
+
+ /*
+ * Loop looking first for a suffix that matches the start of the
+ * string and then for one that exactly matches the rest of it. If
+ * we can find two that meet these criteria, we've successfully
+ * parsed the string.
+ */
+ for (;;) {
+ if (srcLn == NILLNODE) {
+ srcLn = Lst_Find(sufflist, (ClientData)str, SuffSuffIsPrefix);
+ } else {
+ srcLn = Lst_FindFrom (sufflist, Lst_Succ(srcLn), (ClientData)str,
+ SuffSuffIsPrefix);
+ }
+ if (srcLn == NILLNODE) {
+ /*
+ * Ran out of source suffixes -- no such rule
+ */
+ if (singleLn != NILLNODE) {
+ /*
+ * Not so fast Mr. Smith! There was a suffix that encompassed
+ * the entire string, so we assume it was a transformation
+ * to the null suffix (thank you POSIX). We still prefer to
+ * find a double rule over a singleton, hence we leave this
+ * check until the end.
+ *
+ * XXX: Use emptySuff over suffNull?
+ */
+ *srcPtr = single;
+ *targPtr = suffNull;
+ return(TRUE);
+ }
+ return (FALSE);
+ }
+ src = (Suff *) Lst_Datum (srcLn);
+ str2 = str + src->nameLen;
+ if (*str2 == '\0') {
+ single = src;
+ singleLn = srcLn;
+ } else {
+ targLn = Lst_Find(sufflist, (ClientData)str2, SuffSuffHasNameP);
+ if (targLn != NILLNODE) {
+ *srcPtr = src;
+ *targPtr = (Suff *)Lst_Datum(targLn);
+ return (TRUE);
+ }
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_IsTransform --
+ * Return TRUE if the given string is a transformation rule
+ *
+ *
+ * Results:
+ * TRUE if the string is a concatenation of two known suffixes.
+ * FALSE otherwise
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Suff_IsTransform (str)
+ char *str; /* string to check */
+{
+ Suff *src, *targ;
+
+ return (SuffParseTransform(str, &src, &targ));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_AddTransform --
+ * Add the transformation rule described by the line to the
+ * list of rules and place the transformation itself in the graph
+ *
+ * Results:
+ * The node created for the transformation in the transforms list
+ *
+ * Side Effects:
+ * The node is placed on the end of the transforms Lst and links are
+ * made between the two suffixes mentioned in the target name
+ *-----------------------------------------------------------------------
+ */
+GNode *
+Suff_AddTransform (line)
+ char *line; /* name of transformation to add */
+{
+ GNode *gn; /* GNode of transformation rule */
+ Suff *s, /* source suffix */
+ *t; /* target suffix */
+ LstNode ln; /* Node for existing transformation */
+
+ ln = Lst_Find (transforms, (ClientData)line, SuffGNHasNameP);
+ if (ln == NILLNODE) {
+ /*
+ * Make a new graph node for the transformation. It will be filled in
+ * by the Parse module.
+ */
+ gn = Targ_NewGN (line);
+ (void)Lst_AtEnd (transforms, (ClientData)gn);
+ } else {
+ /*
+ * New specification for transformation rule. Just nuke the old list
+ * of commands so they can be filled in again... We don't actually
+ * free the commands themselves, because a given command can be
+ * attached to several different transformations.
+ */
+ gn = (GNode *) Lst_Datum (ln);
+ Lst_Destroy (gn->commands, NOFREE);
+ Lst_Destroy (gn->children, NOFREE);
+ gn->commands = Lst_Init (FALSE);
+ gn->children = Lst_Init (FALSE);
+ }
+
+ gn->type = OP_TRANSFORM;
+
+ (void)SuffParseTransform(line, &s, &t);
+
+ /*
+ * link the two together in the proper relationship and order
+ */
+ if (DEBUG(SUFF)) {
+ printf("defining transformation from `%s' to `%s'\n",
+ s->name, t->name);
+ }
+ SuffInsert (t->children, s);
+ SuffInsert (s->parents, t);
+
+ return (gn);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_EndTransform --
+ * Handle the finish of a transformation definition, removing the
+ * transformation from the graph if it has neither commands nor
+ * sources. This is a callback procedure for the Parse module via
+ * Lst_ForEach
+ *
+ * Results:
+ * === 0
+ *
+ * Side Effects:
+ * If the node has no commands or children, the children and parents
+ * lists of the affected suffices are altered.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+Suff_EndTransform(gnp, dummy)
+ ClientData gnp; /* Node for transformation */
+ ClientData dummy; /* Node for transformation */
+{
+ GNode *gn = (GNode *) gnp;
+
+ if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) &&
+ Lst_IsEmpty(gn->children))
+ {
+ Suff *s, *t;
+
+ (void)SuffParseTransform(gn->name, &s, &t);
+
+ if (DEBUG(SUFF)) {
+ printf("deleting transformation from %s to %s\n",
+ s->name, t->name);
+ }
+
+ /*
+ * Remove the source from the target's children list. We check for a
+ * nil return to handle a beanhead saying something like
+ * .c.o .c.o:
+ *
+ * We'll be called twice when the next target is seen, but .c and .o
+ * are only linked once...
+ */
+ SuffRemove(t->children, s);
+
+ /*
+ * Remove the target from the source's parents list
+ */
+ SuffRemove(s->parents, t);
+ } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) {
+ printf("transformation %s complete\n", gn->name);
+ }
+
+ return(dummy ? 0 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffRebuildGraph --
+ * Called from Suff_AddSuffix via Lst_ForEach to search through the
+ * list of existing transformation rules and rebuild the transformation
+ * graph when it has been destroyed by Suff_ClearSuffixes. If the
+ * given rule is a transformation involving this suffix and another,
+ * existing suffix, the proper relationship is established between
+ * the two.
+ *
+ * Results:
+ * Always 0.
+ *
+ * Side Effects:
+ * The appropriate links will be made between this suffix and
+ * others if transformation rules exist for it.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffRebuildGraph(transformp, sp)
+ ClientData transformp; /* Transformation to test */
+ ClientData sp; /* Suffix to rebuild */
+{
+ GNode *transform = (GNode *) transformp;
+ Suff *s = (Suff *) sp;
+ char *cp;
+ LstNode ln;
+ Suff *s2;
+
+ /*
+ * First see if it is a transformation from this suffix.
+ */
+ cp = SuffStrIsPrefix(s->name, transform->name);
+ if (cp != (char *)NULL) {
+ ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffHasNameP);
+ if (ln != NILLNODE) {
+ /*
+ * Found target. Link in and return, since it can't be anything
+ * else.
+ */
+ s2 = (Suff *)Lst_Datum(ln);
+ SuffInsert(s2->children, s);
+ SuffInsert(s->parents, s2);
+ return(0);
+ }
+ }
+
+ /*
+ * Not from, maybe to?
+ */
+ cp = SuffSuffIsSuffix(s, transform->name + strlen(transform->name));
+ if (cp != (char *)NULL) {
+ /*
+ * Null-terminate the source suffix in order to find it.
+ */
+ cp[1] = '\0';
+ ln = Lst_Find(sufflist, (ClientData)transform->name, SuffSuffHasNameP);
+ /*
+ * Replace the start of the target suffix
+ */
+ cp[1] = s->name[0];
+ if (ln != NILLNODE) {
+ /*
+ * Found it -- establish the proper relationship
+ */
+ s2 = (Suff *)Lst_Datum(ln);
+ SuffInsert(s->children, s2);
+ SuffInsert(s2->parents, s);
+ }
+ }
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_AddSuffix --
+ * Add the suffix in string to the end of the list of known suffixes.
+ * Should we restructure the suffix graph? Make doesn't...
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A GNode is created for the suffix and a Suff structure is created and
+ * added to the suffixes list unless the suffix was already known.
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_AddSuffix (str)
+ char *str; /* the name of the suffix to add */
+{
+ Suff *s; /* new suffix descriptor */
+ LstNode ln;
+
+ ln = Lst_Find (sufflist, (ClientData)str, SuffSuffHasNameP);
+ if (ln == NILLNODE) {
+ s = (Suff *) emalloc (sizeof (Suff));
+
+ s->name = strdup (str);
+ s->nameLen = strlen (s->name);
+ s->searchPath = Lst_Init (FALSE);
+ s->children = Lst_Init (FALSE);
+ s->parents = Lst_Init (FALSE);
+ s->ref = Lst_Init (FALSE);
+ s->sNum = sNum++;
+ s->flags = 0;
+ s->refCount = 0;
+
+ (void)Lst_AtEnd (sufflist, (ClientData)s);
+ /*
+ * Look for any existing transformations from or to this suffix.
+ * XXX: Only do this after a Suff_ClearSuffixes?
+ */
+ Lst_ForEach (transforms, SuffRebuildGraph, (ClientData)s);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_GetPath --
+ * Return the search path for the given suffix, if it's defined.
+ *
+ * Results:
+ * The searchPath for the desired suffix or NILLST if the suffix isn't
+ * defined.
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+Lst
+Suff_GetPath (sname)
+ char *sname;
+{
+ LstNode ln;
+ Suff *s;
+
+ ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP);
+ if (ln == NILLNODE) {
+ return (NILLST);
+ } else {
+ s = (Suff *) Lst_Datum (ln);
+ return (s->searchPath);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_DoPaths --
+ * Extend the search paths for all suffixes to include the default
+ * search path.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The searchPath field of all the suffixes is extended by the
+ * directories in dirSearchPath. If paths were specified for the
+ * ".h" suffix, the directories are stuffed into a global variable
+ * called ".INCLUDES" with each directory preceeded by a -I. The same
+ * is done for the ".a" suffix, except the variable is called
+ * ".LIBS" and the flag is -L.
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_DoPaths()
+{
+ register Suff *s;
+ register LstNode ln;
+ char *ptr;
+ Lst inIncludes; /* Cumulative .INCLUDES path */
+ Lst inLibs; /* Cumulative .LIBS path */
+
+ if (Lst_Open (sufflist) == FAILURE) {
+ return;
+ }
+
+ inIncludes = Lst_Init(FALSE);
+ inLibs = Lst_Init(FALSE);
+
+ while ((ln = Lst_Next (sufflist)) != NILLNODE) {
+ s = (Suff *) Lst_Datum (ln);
+ if (!Lst_IsEmpty (s->searchPath)) {
+#ifdef INCLUDES
+ if (s->flags & SUFF_INCLUDE) {
+ Dir_Concat(inIncludes, s->searchPath);
+ }
+#endif /* INCLUDES */
+#ifdef LIBRARIES
+ if (s->flags & SUFF_LIBRARY) {
+ Dir_Concat(inLibs, s->searchPath);
+ }
+#endif /* LIBRARIES */
+ Dir_Concat(s->searchPath, dirSearchPath);
+ } else {
+ Lst_Destroy (s->searchPath, Dir_Destroy);
+ s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir);
+ }
+ }
+
+ Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL);
+ free(ptr);
+ Var_Set(".LIBS", ptr = Dir_MakeFlags("-L", inLibs), VAR_GLOBAL);
+ free(ptr);
+
+ Lst_Destroy(inIncludes, Dir_Destroy);
+ Lst_Destroy(inLibs, Dir_Destroy);
+
+ Lst_Close (sufflist);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_AddInclude --
+ * Add the given suffix as a type of file which gets included.
+ * Called from the parse module when a .INCLUDES line is parsed.
+ * The suffix must have already been defined.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The SUFF_INCLUDE bit is set in the suffix's flags field
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_AddInclude (sname)
+ char *sname; /* Name of suffix to mark */
+{
+ LstNode ln;
+ Suff *s;
+
+ ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP);
+ if (ln != NILLNODE) {
+ s = (Suff *) Lst_Datum (ln);
+ s->flags |= SUFF_INCLUDE;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_AddLib --
+ * Add the given suffix as a type of file which is a library.
+ * Called from the parse module when parsing a .LIBS line. The
+ * suffix must have been defined via .SUFFIXES before this is
+ * called.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The SUFF_LIBRARY bit is set in the suffix's flags field
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_AddLib (sname)
+ char *sname; /* Name of suffix to mark */
+{
+ LstNode ln;
+ Suff *s;
+
+ ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP);
+ if (ln != NILLNODE) {
+ s = (Suff *) Lst_Datum (ln);
+ s->flags |= SUFF_LIBRARY;
+ }
+}
+
+ /********** Implicit Source Search Functions *********/
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffAddSrc --
+ * Add a suffix as a Src structure to the given list with its parent
+ * being the given Src structure. If the suffix is the null suffix,
+ * the prefix is used unaltered as the file name in the Src structure.
+ *
+ * Results:
+ * always returns 0
+ *
+ * Side Effects:
+ * A Src structure is created and tacked onto the end of the list
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffAddSrc (sp, lsp)
+ ClientData sp; /* suffix for which to create a Src structure */
+ ClientData lsp; /* list and parent for the new Src */
+{
+ Suff *s = (Suff *) sp;
+ LstSrc *ls = (LstSrc *) lsp;
+ Src *s2; /* new Src structure */
+ Src *targ; /* Target structure */
+
+ targ = ls->s;
+
+ if ((s->flags & SUFF_NULL) && (*s->name != '\0')) {
+ /*
+ * If the suffix has been marked as the NULL suffix, also create a Src
+ * structure for a file with no suffix attached. Two birds, and all
+ * that...
+ */
+ s2 = (Src *) emalloc (sizeof (Src));
+ s2->file = strdup(targ->pref);
+ s2->pref = targ->pref;
+ s2->parent = targ;
+ s2->node = NILGNODE;
+ s2->suff = s;
+ s->refCount++;
+ s2->children = 0;
+ targ->children += 1;
+ (void)Lst_AtEnd (ls->l, (ClientData)s2);
+#ifdef DEBUG_SRC
+ s2->cp = Lst_Init(FALSE);
+ Lst_AtEnd(targ->cp, (ClientData) s2);
+ printf("1 add %x %x to %x:", targ, s2, ls->l);
+ Lst_ForEach(ls->l, PrintAddr, (ClientData) 0);
+ printf("\n");
+#endif
+ }
+ s2 = (Src *) emalloc (sizeof (Src));
+ s2->file = str_concat (targ->pref, s->name, 0);
+ s2->pref = targ->pref;
+ s2->parent = targ;
+ s2->node = NILGNODE;
+ s2->suff = s;
+ s->refCount++;
+ s2->children = 0;
+ targ->children += 1;
+ (void)Lst_AtEnd (ls->l, (ClientData)s2);
+#ifdef DEBUG_SRC
+ s2->cp = Lst_Init(FALSE);
+ Lst_AtEnd(targ->cp, (ClientData) s2);
+ printf("2 add %x %x to %x:", targ, s2, ls->l);
+ Lst_ForEach(ls->l, PrintAddr, (ClientData) 0);
+ printf("\n");
+#endif
+
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffAddLevel --
+ * Add all the children of targ as Src structures to the given list
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Lots of structures are created and added to the list
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffAddLevel (l, targ)
+ Lst l; /* list to which to add the new level */
+ Src *targ; /* Src structure to use as the parent */
+{
+ LstSrc ls;
+
+ ls.s = targ;
+ ls.l = l;
+
+ Lst_ForEach (targ->suff->children, SuffAddSrc, (ClientData)&ls);
+}
+
+/*-
+ *----------------------------------------------------------------------
+ * SuffRemoveSrc --
+ * Free all src structures in list that don't have a reference count
+ *
+ * Results:
+ * Ture if an src was removed
+ *
+ * Side Effects:
+ * The memory is free'd.
+ *----------------------------------------------------------------------
+ */
+static int
+SuffRemoveSrc (l)
+ Lst l;
+{
+ LstNode ln;
+ Src *s;
+ int t = 0;
+
+ if (Lst_Open (l) == FAILURE) {
+ return 0;
+ }
+#ifdef DEBUG_SRC
+ printf("cleaning %lx: ", (unsigned long) l);
+ Lst_ForEach(l, PrintAddr, (ClientData) 0);
+ printf("\n");
+#endif
+
+
+ while ((ln = Lst_Next (l)) != NILLNODE) {
+ s = (Src *) Lst_Datum (ln);
+ if (s->children == 0) {
+ free ((Address)s->file);
+ if (!s->parent)
+ free((Address)s->pref);
+ else {
+#ifdef DEBUG_SRC
+ LstNode ln = Lst_Member(s->parent->cp, (ClientData)s);
+ if (ln != NILLNODE)
+ Lst_Remove(s->parent->cp, ln);
+#endif
+ --s->parent->children;
+ }
+#ifdef DEBUG_SRC
+ printf("free: [l=%x] p=%x %d\n", l, s, s->children);
+ Lst_Destroy(s->cp, NOFREE);
+#endif
+ Lst_Remove(l, ln);
+ free ((Address)s);
+ t |= 1;
+ Lst_Close(l);
+ return TRUE;
+ }
+#ifdef DEBUG_SRC
+ else {
+ printf("keep: [l=%x] p=%x %d: ", l, s, s->children);
+ Lst_ForEach(s->cp, PrintAddr, (ClientData) 0);
+ printf("\n");
+ }
+#endif
+ }
+
+ Lst_Close(l);
+
+ return t;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffFindThem --
+ * Find the first existing file/target in the list srcs
+ *
+ * Results:
+ * The lowest structure in the chain of transformations
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static Src *
+SuffFindThem (srcs, slst)
+ Lst srcs; /* list of Src structures to search through */
+ Lst slst;
+{
+ Src *s; /* current Src */
+ Src *rs; /* returned Src */
+ char *ptr;
+
+ rs = (Src *) NULL;
+
+ while (!Lst_IsEmpty (srcs)) {
+ s = (Src *) Lst_DeQueue (srcs);
+
+ if (DEBUG(SUFF)) {
+ printf ("\ttrying %s...", s->file);
+ }
+
+ /*
+ * A file is considered to exist if either a node exists in the
+ * graph for it or the file actually exists.
+ */
+ if (Targ_FindNode(s->file, TARG_NOCREATE) != NILGNODE) {
+#ifdef DEBUG_SRC
+ printf("remove %x from %x\n", s, srcs);
+#endif
+ rs = s;
+ break;
+ }
+
+ if ((ptr = Dir_FindFile (s->file, s->suff->searchPath)) != NULL) {
+ rs = s;
+#ifdef DEBUG_SRC
+ printf("remove %x from %x\n", s, srcs);
+#endif
+ free(ptr);
+ break;
+ }
+
+ if (DEBUG(SUFF)) {
+ printf ("not there\n");
+ }
+
+ SuffAddLevel (srcs, s);
+ Lst_AtEnd(slst, (ClientData) s);
+ }
+
+ if (DEBUG(SUFF) && rs) {
+ printf ("got it\n");
+ }
+ return (rs);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffFindCmds --
+ * See if any of the children of the target in the Src structure is
+ * one from which the target can be transformed. If there is one,
+ * a Src structure is put together for it and returned.
+ *
+ * Results:
+ * The Src structure of the "winning" child, or NIL if no such beast.
+ *
+ * Side Effects:
+ * A Src structure may be allocated.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Src *
+SuffFindCmds (targ, slst)
+ Src *targ; /* Src structure to play with */
+ Lst slst;
+{
+ LstNode ln; /* General-purpose list node */
+ register GNode *t, /* Target GNode */
+ *s; /* Source GNode */
+ int prefLen;/* The length of the defined prefix */
+ Suff *suff; /* Suffix on matching beastie */
+ Src *ret; /* Return value */
+ char *cp;
+
+ t = targ->node;
+ (void) Lst_Open (t->children);
+ prefLen = strlen (targ->pref);
+
+ while ((ln = Lst_Next (t->children)) != NILLNODE) {
+ s = (GNode *)Lst_Datum (ln);
+
+ cp = strrchr (s->name, '/');
+ if (cp == (char *)NULL) {
+ cp = s->name;
+ } else {
+ cp++;
+ }
+ if (strncmp (cp, targ->pref, prefLen) == 0) {
+ /*
+ * The node matches the prefix ok, see if it has a known
+ * suffix.
+ */
+ ln = Lst_Find (sufflist, (ClientData)&cp[prefLen],
+ SuffSuffHasNameP);
+ if (ln != NILLNODE) {
+ /*
+ * It even has a known suffix, see if there's a transformation
+ * defined between the node's suffix and the target's suffix.
+ *
+ * XXX: Handle multi-stage transformations here, too.
+ */
+ suff = (Suff *)Lst_Datum (ln);
+
+ if (Lst_Member (suff->parents,
+ (ClientData)targ->suff) != NILLNODE)
+ {
+ /*
+ * Hot Damn! Create a new Src structure to describe
+ * this transformation (making sure to duplicate the
+ * source node's name so Suff_FindDeps can free it
+ * again (ick)), and return the new structure.
+ */
+ ret = (Src *)emalloc (sizeof (Src));
+ ret->file = strdup(s->name);
+ ret->pref = targ->pref;
+ ret->suff = suff;
+ suff->refCount++;
+ ret->parent = targ;
+ ret->node = s;
+ ret->children = 0;
+ targ->children += 1;
+#ifdef DEBUG_SRC
+ ret->cp = Lst_Init(FALSE);
+ printf("3 add %x %x\n", targ, ret);
+ Lst_AtEnd(targ->cp, (ClientData) ret);
+#endif
+ Lst_AtEnd(slst, (ClientData) ret);
+ if (DEBUG(SUFF)) {
+ printf ("\tusing existing source %s\n", s->name);
+ }
+ return (ret);
+ }
+ }
+ }
+ }
+ Lst_Close (t->children);
+ return ((Src *)NULL);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffExpandChildren --
+ * Expand the names of any children of a given node that contain
+ * variable invocations or file wildcards into actual targets.
+ *
+ * Results:
+ * === 0 (continue)
+ *
+ * Side Effects:
+ * The expanded node is removed from the parent's list of children,
+ * and the parent's unmade counter is decremented, but other nodes
+ * may be added.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+SuffExpandChildren(cgnp, pgnp)
+ ClientData cgnp; /* Child to examine */
+ ClientData pgnp; /* Parent node being processed */
+{
+ GNode *cgn = (GNode *) cgnp;
+ GNode *pgn = (GNode *) pgnp;
+ GNode *gn; /* New source 8) */
+ LstNode prevLN; /* Node after which new source should be put */
+ LstNode ln; /* List element for old source */
+ char *cp; /* Expanded value */
+
+ /*
+ * New nodes effectively take the place of the child, so place them
+ * after the child
+ */
+ prevLN = Lst_Member(pgn->children, (ClientData)cgn);
+
+ /*
+ * First do variable expansion -- this takes precedence over
+ * wildcard expansion. If the result contains wildcards, they'll be gotten
+ * to later since the resulting words are tacked on to the end of
+ * the children list.
+ */
+ if (strchr(cgn->name, '$') != (char *)NULL) {
+ if (DEBUG(SUFF)) {
+ printf("Expanding \"%s\"...", cgn->name);
+ }
+ cp = Var_Subst(NULL, cgn->name, pgn, TRUE);
+
+ if (cp != (char *)NULL) {
+ Lst members = Lst_Init(FALSE);
+
+ if (cgn->type & OP_ARCHV) {
+ /*
+ * Node was an archive(member) target, so we want to call
+ * on the Arch module to find the nodes for us, expanding
+ * variables in the parent's context.
+ */
+ char *sacrifice = cp;
+
+ (void)Arch_ParseArchive(&sacrifice, members, pgn);
+ } else {
+ /*
+ * Break the result into a vector of strings whose nodes
+ * we can find, then add those nodes to the members list.
+ * Unfortunately, we can't use brk_string b/c it
+ * doesn't understand about variable specifications with
+ * spaces in them...
+ */
+ char *start;
+ char *initcp = cp; /* For freeing... */
+
+ for (start = cp; *start == ' ' || *start == '\t'; start++)
+ continue;
+ for (cp = start; *cp != '\0'; cp++) {
+ if (*cp == ' ' || *cp == '\t') {
+ /*
+ * White-space -- terminate element, find the node,
+ * add it, skip any further spaces.
+ */
+ *cp++ = '\0';
+ gn = Targ_FindNode(start, TARG_CREATE);
+ (void)Lst_AtEnd(members, (ClientData)gn);
+ while (*cp == ' ' || *cp == '\t') {
+ cp++;
+ }
+ /*
+ * Adjust cp for increment at start of loop, but
+ * set start to first non-space.
+ */
+ start = cp--;
+ } else if (*cp == '$') {
+ /*
+ * Start of a variable spec -- contact variable module
+ * to find the end so we can skip over it.
+ */
+ char *junk;
+ int len;
+ Boolean doFree;
+
+ junk = Var_Parse(cp, pgn, TRUE, &len, &doFree);
+ if (junk != var_Error) {
+ cp += len - 1;
+ }
+
+ if (doFree) {
+ free(junk);
+ }
+ } else if (*cp == '\\' && *cp != '\0') {
+ /*
+ * Escaped something -- skip over it
+ */
+ cp++;
+ }
+ }
+
+ if (cp != start) {
+ /*
+ * Stuff left over -- add it to the list too
+ */
+ gn = Targ_FindNode(start, TARG_CREATE);
+ (void)Lst_AtEnd(members, (ClientData)gn);
+ }
+ /*
+ * Point cp back at the beginning again so the variable value
+ * can be freed.
+ */
+ cp = initcp;
+ }
+ /*
+ * Add all elements of the members list to the parent node.
+ */
+ while(!Lst_IsEmpty(members)) {
+ gn = (GNode *)Lst_DeQueue(members);
+
+ if (DEBUG(SUFF)) {
+ printf("%s...", gn->name);
+ }
+ if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) {
+ (void)Lst_Append(pgn->children, prevLN, (ClientData)gn);
+ prevLN = Lst_Succ(prevLN);
+ (void)Lst_AtEnd(gn->parents, (ClientData)pgn);
+ pgn->unmade++;
+ }
+ }
+ Lst_Destroy(members, NOFREE);
+ /*
+ * Free the result
+ */
+ free((char *)cp);
+ }
+ /*
+ * Now the source is expanded, remove it from the list of children to
+ * keep it from being processed.
+ */
+ ln = Lst_Member(pgn->children, (ClientData)cgn);
+ pgn->unmade--;
+ Lst_Remove(pgn->children, ln);
+ if (DEBUG(SUFF)) {
+ printf("\n");
+ }
+ } else if (Dir_HasWildcards(cgn->name)) {
+ Lst exp; /* List of expansions */
+ Lst path; /* Search path along which to expand */
+
+ /*
+ * Find a path along which to expand the word.
+ *
+ * If the word has a known suffix, use that path.
+ * If it has no known suffix and we're allowed to use the null
+ * suffix, use its path.
+ * Else use the default system search path.
+ */
+ cp = cgn->name + strlen(cgn->name);
+ ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffIsSuffixP);
+
+ if (DEBUG(SUFF)) {
+ printf("Wildcard expanding \"%s\"...", cgn->name);
+ }
+
+ if (ln != NILLNODE) {
+ Suff *s = (Suff *)Lst_Datum(ln);
+
+ if (DEBUG(SUFF)) {
+ printf("suffix is \"%s\"...", s->name);
+ }
+ path = s->searchPath;
+ } else {
+ /*
+ * Use default search path
+ */
+ path = dirSearchPath;
+ }
+
+ /*
+ * Expand the word along the chosen path
+ */
+ exp = Lst_Init(FALSE);
+ Dir_Expand(cgn->name, path, exp);
+
+ while (!Lst_IsEmpty(exp)) {
+ /*
+ * Fetch next expansion off the list and find its GNode
+ */
+ cp = (char *)Lst_DeQueue(exp);
+
+ if (DEBUG(SUFF)) {
+ printf("%s...", cp);
+ }
+ gn = Targ_FindNode(cp, TARG_CREATE);
+
+ /*
+ * If gn isn't already a child of the parent, make it so and
+ * up the parent's count of unmade children.
+ */
+ if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) {
+ (void)Lst_Append(pgn->children, prevLN, (ClientData)gn);
+ prevLN = Lst_Succ(prevLN);
+ (void)Lst_AtEnd(gn->parents, (ClientData)pgn);
+ pgn->unmade++;
+ }
+ }
+
+ /*
+ * Nuke what's left of the list
+ */
+ Lst_Destroy(exp, NOFREE);
+
+ /*
+ * Now the source is expanded, remove it from the list of children to
+ * keep it from being processed.
+ */
+ ln = Lst_Member(pgn->children, (ClientData)cgn);
+ pgn->unmade--;
+ Lst_Remove(pgn->children, ln);
+ if (DEBUG(SUFF)) {
+ printf("\n");
+ }
+ }
+
+ return(0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffApplyTransform --
+ * Apply a transformation rule, given the source and target nodes
+ * and suffixes.
+ *
+ * Results:
+ * TRUE if successful, FALSE if not.
+ *
+ * Side Effects:
+ * The source and target are linked and the commands from the
+ * transformation are added to the target node's commands list.
+ * All attributes but OP_DEPMASK and OP_TRANSFORM are applied
+ * to the target. The target also inherits all the sources for
+ * the transformation rule.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+SuffApplyTransform(tGn, sGn, t, s)
+ GNode *tGn; /* Target node */
+ GNode *sGn; /* Source node */
+ Suff *t; /* Target suffix */
+ Suff *s; /* Source suffix */
+{
+ LstNode ln; /* General node */
+ char *tname; /* Name of transformation rule */
+ GNode *gn; /* Node for same */
+
+ if (Lst_Member(tGn->children, (ClientData)sGn) == NILLNODE) {
+ /*
+ * Not already linked, so form the proper links between the
+ * target and source.
+ */
+ (void)Lst_AtEnd(tGn->children, (ClientData)sGn);
+ (void)Lst_AtEnd(sGn->parents, (ClientData)tGn);
+ tGn->unmade += 1;
+ }
+
+ if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) {
+ /*
+ * When a :: node is used as the implied source of a node, we have
+ * to link all its cohorts in as sources as well. Only the initial
+ * sGn gets the target in its iParents list, however, as that
+ * will be sufficient to get the .IMPSRC variable set for tGn
+ */
+ for (ln=Lst_First(sGn->cohorts); ln != NILLNODE; ln=Lst_Succ(ln)) {
+ gn = (GNode *)Lst_Datum(ln);
+
+ if (Lst_Member(tGn->children, (ClientData)gn) == NILLNODE) {
+ /*
+ * Not already linked, so form the proper links between the
+ * target and source.
+ */
+ (void)Lst_AtEnd(tGn->children, (ClientData)gn);
+ (void)Lst_AtEnd(gn->parents, (ClientData)tGn);
+ tGn->unmade += 1;
+ }
+ }
+ }
+ /*
+ * Locate the transformation rule itself
+ */
+ tname = str_concat(s->name, t->name, 0);
+ ln = Lst_Find(transforms, (ClientData)tname, SuffGNHasNameP);
+ free(tname);
+
+ if (ln == NILLNODE) {
+ /*
+ * Not really such a transformation rule (can happen when we're
+ * called to link an OP_MEMBER and OP_ARCHV node), so return
+ * FALSE.
+ */
+ return(FALSE);
+ }
+
+ gn = (GNode *)Lst_Datum(ln);
+
+ if (DEBUG(SUFF)) {
+ printf("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name);
+ }
+
+ /*
+ * Record last child for expansion purposes
+ */
+ ln = Lst_Last(tGn->children);
+
+ /*
+ * Pass the buck to Make_HandleUse to apply the rule
+ */
+ (void)Make_HandleUse(gn, tGn);
+
+ /*
+ * Deal with wildcards and variables in any acquired sources
+ */
+ ln = Lst_Succ(ln);
+ if (ln != NILLNODE) {
+ Lst_ForEachFrom(tGn->children, ln,
+ SuffExpandChildren, (ClientData)tGn);
+ }
+
+ /*
+ * Keep track of another parent to which this beast is transformed so
+ * the .IMPSRC variable can be set correctly for the parent.
+ */
+ (void)Lst_AtEnd(sGn->iParents, (ClientData)tGn);
+
+ return(TRUE);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffFindArchiveDeps --
+ * Locate dependencies for an OP_ARCHV node.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Same as Suff_FindDeps
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffFindArchiveDeps(gn, slst)
+ GNode *gn; /* Node for which to locate dependencies */
+ Lst slst;
+{
+ char *eoarch; /* End of archive portion */
+ char *eoname; /* End of member portion */
+ GNode *mem; /* Node for member */
+ static char *copy[] = { /* Variables to be copied from the member node */
+ TARGET, /* Must be first */
+ PREFIX, /* Must be second */
+ };
+ int i; /* Index into copy and vals */
+ Suff *ms; /* Suffix descriptor for member */
+ char *name; /* Start of member's name */
+
+ /*
+ * The node is an archive(member) pair. so we must find a
+ * suffix for both of them.
+ */
+ eoarch = strchr (gn->name, '(');
+ eoname = strchr (eoarch, ')');
+
+ *eoname = '\0'; /* Nuke parentheses during suffix search */
+ *eoarch = '\0'; /* So a suffix can be found */
+
+ name = eoarch + 1;
+
+ /*
+ * To simplify things, call Suff_FindDeps recursively on the member now,
+ * so we can simply compare the member's .PREFIX and .TARGET variables
+ * to locate its suffix. This allows us to figure out the suffix to
+ * use for the archive without having to do a quadratic search over the
+ * suffix list, backtracking for each one...
+ */
+ mem = Targ_FindNode(name, TARG_CREATE);
+ SuffFindDeps(mem, slst);
+
+ /*
+ * Create the link between the two nodes right off
+ */
+ if (Lst_Member(gn->children, (ClientData)mem) == NILLNODE) {
+ (void)Lst_AtEnd(gn->children, (ClientData)mem);
+ (void)Lst_AtEnd(mem->parents, (ClientData)gn);
+ gn->unmade += 1;
+ }
+
+ /*
+ * Copy in the variables from the member node to this one.
+ */
+ for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) {
+ char *p1;
+ Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn);
+ if (p1)
+ free(p1);
+
+ }
+
+ ms = mem->suffix;
+ if (ms == NULL) {
+ /*
+ * Didn't know what it was -- use .NULL suffix if not in make mode
+ */
+ if (DEBUG(SUFF)) {
+ printf("using null suffix\n");
+ }
+ ms = suffNull;
+ }
+
+
+ /*
+ * Set the other two local variables required for this target.
+ */
+ Var_Set (MEMBER, name, gn);
+ Var_Set (ARCHIVE, gn->name, gn);
+
+ if (ms != NULL) {
+ /*
+ * Member has a known suffix, so look for a transformation rule from
+ * it to a possible suffix of the archive. Rather than searching
+ * through the entire list, we just look at suffixes to which the
+ * member's suffix may be transformed...
+ */
+ LstNode ln;
+
+ /*
+ * Use first matching suffix...
+ */
+ ln = Lst_Find(ms->parents, eoarch, SuffSuffIsSuffixP);
+
+ if (ln != NILLNODE) {
+ /*
+ * Got one -- apply it
+ */
+ if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) &&
+ DEBUG(SUFF))
+ {
+ printf("\tNo transformation from %s -> %s\n",
+ ms->name, ((Suff *)Lst_Datum(ln))->name);
+ }
+ }
+ }
+
+ /*
+ * Replace the opening and closing parens now we've no need of the separate
+ * pieces.
+ */
+ *eoarch = '('; *eoname = ')';
+
+ /*
+ * Pretend gn appeared to the left of a dependency operator so
+ * the user needn't provide a transformation from the member to the
+ * archive.
+ */
+ if (OP_NOP(gn->type)) {
+ gn->type |= OP_DEPENDS;
+ }
+
+ /*
+ * Flag the member as such so we remember to look in the archive for
+ * its modification time.
+ */
+ mem->type |= OP_MEMBER;
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * SuffFindNormalDeps --
+ * Locate implicit dependencies for regular targets.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Same as Suff_FindDeps...
+ *
+ *-----------------------------------------------------------------------
+ */
+static void
+SuffFindNormalDeps(gn, slst)
+ GNode *gn; /* Node for which to find sources */
+ Lst slst;
+{
+ char *eoname; /* End of name */
+ char *sopref; /* Start of prefix */
+ LstNode ln; /* Next suffix node to check */
+ Lst srcs; /* List of sources at which to look */
+ Lst targs; /* List of targets to which things can be
+ * transformed. They all have the same file,
+ * but different suff and pref fields */
+ Src *bottom; /* Start of found transformation path */
+ Src *src; /* General Src pointer */
+ char *pref; /* Prefix to use */
+ Src *targ; /* General Src target pointer */
+
+
+ eoname = gn->name + strlen(gn->name);
+
+ sopref = gn->name;
+
+ /*
+ * Begin at the beginning...
+ */
+ ln = Lst_First(sufflist);
+ srcs = Lst_Init(FALSE);
+ targs = Lst_Init(FALSE);
+
+ /*
+ * We're caught in a catch-22 here. On the one hand, we want to use any
+ * transformation implied by the target's sources, but we can't examine
+ * the sources until we've expanded any variables/wildcards they may hold,
+ * and we can't do that until we've set up the target's local variables
+ * and we can't do that until we know what the proper suffix for the
+ * target is (in case there are two suffixes one of which is a suffix of
+ * the other) and we can't know that until we've found its implied
+ * source, which we may not want to use if there's an existing source
+ * that implies a different transformation.
+ *
+ * In an attempt to get around this, which may not work all the time,
+ * but should work most of the time, we look for implied sources first,
+ * checking transformations to all possible suffixes of the target,
+ * use what we find to set the target's local variables, expand the
+ * children, then look for any overriding transformations they imply.
+ * Should we find one, we discard the one we found before.
+ */
+ while(ln != NILLNODE) {
+ /*
+ * Look for next possible suffix...
+ */
+ ln = Lst_FindFrom(sufflist, ln, eoname, SuffSuffIsSuffixP);
+
+ if (ln != NILLNODE) {
+ int prefLen; /* Length of the prefix */
+ Src *targ;
+
+ /*
+ * Allocate a Src structure to which things can be transformed
+ */
+ targ = (Src *)emalloc(sizeof (Src));
+ targ->file = strdup(gn->name);
+ targ->suff = (Suff *)Lst_Datum(ln);
+ targ->suff->refCount++;
+ targ->node = gn;
+ targ->parent = (Src *)NULL;
+ targ->children = 0;
+#ifdef DEBUG_SRC
+ targ->cp = Lst_Init(FALSE);
+#endif
+
+ /*
+ * Allocate room for the prefix, whose end is found by subtracting
+ * the length of the suffix from the end of the name.
+ */
+ prefLen = (eoname - targ->suff->nameLen) - sopref;
+ targ->pref = emalloc(prefLen + 1);
+ memcpy(targ->pref, sopref, prefLen);
+ targ->pref[prefLen] = '\0';
+
+ /*
+ * Add nodes from which the target can be made
+ */
+ SuffAddLevel(srcs, targ);
+
+ /*
+ * Record the target so we can nuke it
+ */
+ (void)Lst_AtEnd(targs, (ClientData)targ);
+
+ /*
+ * Search from this suffix's successor...
+ */
+ ln = Lst_Succ(ln);
+ }
+ }
+
+ /*
+ * Handle target of unknown suffix...
+ */
+ if (Lst_IsEmpty(targs) && suffNull != NULL) {
+ if (DEBUG(SUFF)) {
+ printf("\tNo known suffix on %s. Using .NULL suffix\n", gn->name);
+ }
+
+ targ = (Src *)emalloc(sizeof (Src));
+ targ->file = strdup(gn->name);
+ targ->suff = suffNull;
+ targ->suff->refCount++;
+ targ->node = gn;
+ targ->parent = (Src *)NULL;
+ targ->children = 0;
+ targ->pref = strdup(sopref);
+#ifdef DEBUG_SRC
+ targ->cp = Lst_Init(FALSE);
+#endif
+
+ /*
+ * Only use the default suffix rules if we don't have commands
+ * or dependencies defined for this gnode
+ */
+ if (Lst_IsEmpty(gn->commands) && Lst_IsEmpty(gn->children))
+ SuffAddLevel(srcs, targ);
+ else {
+ if (DEBUG(SUFF))
+ printf("not ");
+ }
+
+ if (DEBUG(SUFF))
+ printf("adding suffix rules\n");
+
+ (void)Lst_AtEnd(targs, (ClientData)targ);
+ }
+
+ /*
+ * Using the list of possible sources built up from the target suffix(es),
+ * try and find an existing file/target that matches.
+ */
+ bottom = SuffFindThem(srcs, slst);
+
+ if (bottom == (Src *)NULL) {
+ /*
+ * No known transformations -- use the first suffix found for setting
+ * the local variables.
+ */
+ if (!Lst_IsEmpty(targs)) {
+ targ = (Src *)Lst_Datum(Lst_First(targs));
+ } else {
+ targ = (Src *)NULL;
+ }
+ } else {
+ /*
+ * Work up the transformation path to find the suffix of the
+ * target to which the transformation was made.
+ */
+ for (targ = bottom; targ->parent != NULL; targ = targ->parent)
+ continue;
+ }
+
+ /*
+ * The .TARGET variable we always set to be the name at this point,
+ * since it's only set to the path if the thing is only a source and
+ * if it's only a source, it doesn't matter what we put here as far
+ * as expanding sources is concerned, since it has none...
+ */
+ Var_Set(TARGET, gn->name, gn);
+
+ pref = (targ != NULL) ? targ->pref : gn->name;
+ Var_Set(PREFIX, pref, gn);
+
+ /*
+ * Now we've got the important local variables set, expand any sources
+ * that still contain variables or wildcards in their names.
+ */
+ Lst_ForEach(gn->children, SuffExpandChildren, (ClientData)gn);
+
+ if (targ == NULL) {
+ if (DEBUG(SUFF)) {
+ printf("\tNo valid suffix on %s\n", gn->name);
+ }
+
+sfnd_abort:
+ /*
+ * Deal with finding the thing on the default search path if the
+ * node is only a source (not on the lhs of a dependency operator
+ * or [XXX] it has neither children or commands).
+ */
+ if (OP_NOP(gn->type) ||
+ (Lst_IsEmpty(gn->children) && Lst_IsEmpty(gn->commands)))
+ {
+ gn->path = Dir_FindFile(gn->name,
+ (targ == NULL ? dirSearchPath :
+ targ->suff->searchPath));
+ if (gn->path != NULL) {
+ Var_Set(TARGET, gn->path, gn);
+
+ if (targ != NULL) {
+ /*
+ * Suffix known for the thing -- trim the suffix off
+ * the path to form the proper .PREFIX variable.
+ */
+ int len = strlen(gn->path);
+ char savec;
+
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = targ->suff;
+ gn->suffix->refCount++;
+
+ savec = gn->path[len-targ->suff->nameLen];
+ gn->path[len-targ->suff->nameLen] = '\0';
+
+ Var_Set(PREFIX, gn->path, gn);
+
+ gn->path[len-targ->suff->nameLen] = savec;
+ } else {
+ /*
+ * The .PREFIX gets the full path if the target has
+ * no known suffix.
+ */
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = NULL;
+
+ Var_Set(PREFIX, gn->path, gn);
+ }
+ }
+ } else {
+ /*
+ * Not appropriate to search for the thing -- set the
+ * path to be the name so Dir_MTime won't go grovelling for
+ * it.
+ */
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = (targ == NULL) ? NULL : targ->suff;
+ if (gn->suffix)
+ gn->suffix->refCount++;
+ if (gn->path != NULL)
+ free(gn->path);
+ gn->path = strdup(gn->name);
+ }
+
+ goto sfnd_return;
+ }
+
+ /*
+ * If the suffix indicates that the target is a library, mark that in
+ * the node's type field.
+ */
+ if (targ->suff->flags & SUFF_LIBRARY) {
+ gn->type |= OP_LIB;
+ }
+
+ /*
+ * Check for overriding transformation rule implied by sources
+ */
+ if (!Lst_IsEmpty(gn->children)) {
+ src = SuffFindCmds(targ, slst);
+
+ if (src != (Src *)NULL) {
+ /*
+ * Free up all the Src structures in the transformation path
+ * up to, but not including, the parent node.
+ */
+ while (bottom && bottom->parent != NULL) {
+ if (Lst_Member(slst, (ClientData) bottom) == NILLNODE) {
+ Lst_AtEnd(slst, (ClientData) bottom);
+ }
+ bottom = bottom->parent;
+ }
+ bottom = src;
+ }
+ }
+
+ if (bottom == NULL) {
+ /*
+ * No idea from where it can come -- return now.
+ */
+ goto sfnd_abort;
+ }
+
+ /*
+ * We now have a list of Src structures headed by 'bottom' and linked via
+ * their 'parent' pointers. What we do next is create links between
+ * source and target nodes (which may or may not have been created)
+ * and set the necessary local variables in each target. The
+ * commands for each target are set from the commands of the
+ * transformation rule used to get from the src suffix to the targ
+ * suffix. Note that this causes the commands list of the original
+ * node, gn, to be replaced by the commands of the final
+ * transformation rule. Also, the unmade field of gn is incremented.
+ * Etc.
+ */
+ if (bottom->node == NILGNODE) {
+ bottom->node = Targ_FindNode(bottom->file, TARG_CREATE);
+ }
+
+ for (src = bottom; src->parent != (Src *)NULL; src = src->parent) {
+ targ = src->parent;
+
+ if (src->node->suffix)
+ src->node->suffix->refCount--;
+ src->node->suffix = src->suff;
+ src->node->suffix->refCount++;
+
+ if (targ->node == NILGNODE) {
+ targ->node = Targ_FindNode(targ->file, TARG_CREATE);
+ }
+
+ SuffApplyTransform(targ->node, src->node,
+ targ->suff, src->suff);
+
+ if (targ->node != gn) {
+ /*
+ * Finish off the dependency-search process for any nodes
+ * between bottom and gn (no point in questing around the
+ * filesystem for their implicit source when it's already
+ * known). Note that the node can't have any sources that
+ * need expanding, since SuffFindThem will stop on an existing
+ * node, so all we need to do is set the standard and System V
+ * variables.
+ */
+ targ->node->type |= OP_DEPS_FOUND;
+
+ Var_Set(PREFIX, targ->pref, targ->node);
+
+ Var_Set(TARGET, targ->node->name, targ->node);
+ }
+ }
+
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ gn->suffix = src->suff;
+ gn->suffix->refCount++;
+
+ /*
+ * So Dir_MTime doesn't go questing for it...
+ */
+ if (gn->path)
+ free(gn->path);
+ gn->path = strdup(gn->name);
+
+ /*
+ * Nuke the transformation path and the Src structures left over in the
+ * two lists.
+ */
+sfnd_return:
+ if (bottom)
+ if (Lst_Member(slst, (ClientData) bottom) == NILLNODE)
+ Lst_AtEnd(slst, (ClientData) bottom);
+
+ while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs))
+ continue;
+
+ Lst_Concat(slst, srcs, LST_CONCLINK);
+ Lst_Concat(slst, targs, LST_CONCLINK);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_FindDeps --
+ * Find implicit sources for the target described by the graph node
+ * gn
+ *
+ * Results:
+ * Nothing.
+ *
+ * Side Effects:
+ * Nodes are added to the graph below the passed-in node. The nodes
+ * are marked to have their IMPSRC variable filled in. The
+ * PREFIX variable is set for the given node and all its
+ * implied children.
+ *
+ * Notes:
+ * The path found by this target is the shortest path in the
+ * transformation graph, which may pass through non-existent targets,
+ * to an existing target. The search continues on all paths from the
+ * root suffix until a file is found. I.e. if there's a path
+ * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but
+ * the .c and .l files don't, the search will branch out in
+ * all directions from .o and again from all the nodes on the
+ * next level until the .l,v node is encountered.
+ *
+ *-----------------------------------------------------------------------
+ */
+
+void
+Suff_FindDeps(gn)
+ GNode *gn;
+{
+
+ SuffFindDeps(gn, srclist);
+ while (SuffRemoveSrc(srclist))
+ continue;
+}
+
+
+static void
+SuffFindDeps (gn, slst)
+ GNode *gn; /* node we're dealing with */
+ Lst slst;
+{
+ if (gn->type & OP_DEPS_FOUND) {
+ /*
+ * If dependencies already found, no need to do it again...
+ */
+ return;
+ } else {
+ gn->type |= OP_DEPS_FOUND;
+ }
+
+ if (DEBUG(SUFF)) {
+ printf ("SuffFindDeps (%s)\n", gn->name);
+ }
+
+ if (gn->type & OP_ARCHV) {
+ SuffFindArchiveDeps(gn, slst);
+ } else if (gn->type & OP_LIB) {
+ /*
+ * If the node is a library, it is the arch module's job to find it
+ * and set the TARGET variable accordingly. We merely provide the
+ * search path, assuming all libraries end in ".a" (if the suffix
+ * hasn't been defined, there's nothing we can do for it, so we just
+ * set the TARGET variable to the node's name in order to give it a
+ * value).
+ */
+ LstNode ln;
+ Suff *s;
+
+ ln = Lst_Find (sufflist, (ClientData)LIBSUFF, SuffSuffHasNameP);
+ if (gn->suffix)
+ gn->suffix->refCount--;
+ if (ln != NILLNODE) {
+ gn->suffix = s = (Suff *) Lst_Datum (ln);
+ gn->suffix->refCount++;
+ Arch_FindLib (gn, s->searchPath);
+ } else {
+ gn->suffix = NULL;
+ Var_Set (TARGET, gn->name, gn);
+ }
+ /*
+ * Because a library (-lfoo) target doesn't follow the standard
+ * filesystem conventions, we don't set the regular variables for
+ * the thing. .PREFIX is simply made empty...
+ */
+ Var_Set(PREFIX, "", gn);
+ } else {
+ SuffFindNormalDeps(gn, slst);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_SetNull --
+ * Define which suffix is the null suffix.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * 'suffNull' is altered.
+ *
+ * Notes:
+ * Need to handle the changing of the null suffix gracefully so the
+ * old transformation rules don't just go away.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_SetNull(name)
+ char *name; /* Name of null suffix */
+{
+ Suff *s;
+ LstNode ln;
+
+ ln = Lst_Find(sufflist, (ClientData)name, SuffSuffHasNameP);
+ if (ln != NILLNODE) {
+ s = (Suff *)Lst_Datum(ln);
+ if (suffNull != (Suff *)NULL) {
+ suffNull->flags &= ~SUFF_NULL;
+ }
+ s->flags |= SUFF_NULL;
+ /*
+ * XXX: Here's where the transformation mangling would take place
+ */
+ suffNull = s;
+ } else {
+ Parse_Error (PARSE_WARNING, "Desired null suffix %s not defined.",
+ name);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Suff_Init --
+ * Initialize suffixes module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Many
+ *-----------------------------------------------------------------------
+ */
+void
+Suff_Init ()
+{
+ sufflist = Lst_Init (FALSE);
+ suffClean = Lst_Init(FALSE);
+ srclist = Lst_Init (FALSE);
+ transforms = Lst_Init (FALSE);
+
+ sNum = 0;
+ /*
+ * Create null suffix for single-suffix rules (POSIX). The thing doesn't
+ * actually go on the suffix list or everyone will think that's its
+ * suffix.
+ */
+ emptySuff = suffNull = (Suff *) emalloc (sizeof (Suff));
+
+ suffNull->name = strdup ("");
+ suffNull->nameLen = 0;
+ suffNull->searchPath = Lst_Init (FALSE);
+ Dir_Concat(suffNull->searchPath, dirSearchPath);
+ suffNull->children = Lst_Init (FALSE);
+ suffNull->parents = Lst_Init (FALSE);
+ suffNull->ref = Lst_Init (FALSE);
+ suffNull->sNum = sNum++;
+ suffNull->flags = SUFF_NULL;
+ suffNull->refCount = 1;
+
+}
+
+
+/*-
+ *----------------------------------------------------------------------
+ * Suff_End --
+ * Cleanup the this module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The memory is free'd.
+ *----------------------------------------------------------------------
+ */
+
+void
+Suff_End()
+{
+ Lst_Destroy(sufflist, SuffFree);
+ Lst_Destroy(suffClean, SuffFree);
+ if (suffNull)
+ SuffFree(suffNull);
+ Lst_Destroy(srclist, NOFREE);
+ Lst_Destroy(transforms, NOFREE);
+}
+
+
+/********************* DEBUGGING FUNCTIONS **********************/
+
+static int SuffPrintName(s, dummy)
+ ClientData s;
+ ClientData dummy;
+{
+ printf ("%s ", ((Suff *) s)->name);
+ return (dummy ? 0 : 0);
+}
+
+static int
+SuffPrintSuff (sp, dummy)
+ ClientData sp;
+ ClientData dummy;
+{
+ Suff *s = (Suff *) sp;
+ int flags;
+ int flag;
+
+ printf ("# `%s' [%d] ", s->name, s->refCount);
+
+ flags = s->flags;
+ if (flags) {
+ fputs (" (", stdout);
+ while (flags) {
+ flag = 1 << (ffs(flags) - 1);
+ flags &= ~flag;
+ switch (flag) {
+ case SUFF_NULL:
+ printf ("NULL");
+ break;
+ case SUFF_INCLUDE:
+ printf ("INCLUDE");
+ break;
+ case SUFF_LIBRARY:
+ printf ("LIBRARY");
+ break;
+ }
+ fputc(flags ? '|' : ')', stdout);
+ }
+ }
+ fputc ('\n', stdout);
+ printf ("#\tTo: ");
+ Lst_ForEach (s->parents, SuffPrintName, (ClientData)0);
+ fputc ('\n', stdout);
+ printf ("#\tFrom: ");
+ Lst_ForEach (s->children, SuffPrintName, (ClientData)0);
+ fputc ('\n', stdout);
+ printf ("#\tSearch Path: ");
+ Dir_PrintPath (s->searchPath);
+ fputc ('\n', stdout);
+ return (dummy ? 0 : 0);
+}
+
+static int
+SuffPrintTrans (tp, dummy)
+ ClientData tp;
+ ClientData dummy;
+{
+ GNode *t = (GNode *) tp;
+
+ printf ("%-16s: ", t->name);
+ Targ_PrintType (t->type);
+ fputc ('\n', stdout);
+ Lst_ForEach (t->commands, Targ_PrintCmd, (ClientData)0);
+ fputc ('\n', stdout);
+ return(dummy ? 0 : 0);
+}
+
+void
+Suff_PrintAll()
+{
+ printf ("#*** Suffixes:\n");
+ Lst_ForEach (sufflist, SuffPrintSuff, (ClientData)0);
+
+ printf ("#*** Transformations:\n");
+ Lst_ForEach (transforms, SuffPrintTrans, (ClientData)0);
+}
diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c
new file mode 100644
index 0000000..bd2dba8
--- /dev/null
+++ b/usr.bin/make/targ.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * targ.c --
+ * Functions for maintaining the Lst allTargets. Target nodes are
+ * kept in two structures: a Lst, maintained by the list library, and a
+ * hash table, maintained by the hash library.
+ *
+ * Interface:
+ * Targ_Init Initialization procedure.
+ *
+ * Targ_End Cleanup the module
+ *
+ * Targ_NewGN Create a new GNode for the passed target
+ * (string). The node is *not* placed in the
+ * hash table, though all its fields are
+ * initialized.
+ *
+ * Targ_FindNode Find the node for a given target, creating
+ * and storing it if it doesn't exist and the
+ * flags are right (TARG_CREATE)
+ *
+ * Targ_FindList Given a list of names, find nodes for all
+ * of them. If a name doesn't exist and the
+ * TARG_NOCREATE flag was given, an error message
+ * is printed. Else, if a name doesn't exist,
+ * its node is created.
+ *
+ * Targ_Ignore Return TRUE if errors should be ignored when
+ * creating the given target.
+ *
+ * Targ_Silent Return TRUE if we should be silent when
+ * creating the given target.
+ *
+ * Targ_Precious Return TRUE if the target is precious and
+ * should not be removed if we are interrupted.
+ *
+ * Debugging:
+ * Targ_PrintGraph Print out the entire graphm all variables
+ * and statistics for the directory cache. Should
+ * print something for suffixes, too, but...
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+
+static Lst allTargets; /* the list of all targets found so far */
+static Lst allGNs; /* List of all the GNodes */
+static Hash_Table targets; /* a hash table of same */
+
+#define HTSIZE 191 /* initial size of hash table */
+
+static int TargPrintOnlySrc __P((ClientData, ClientData));
+static int TargPrintName __P((ClientData, ClientData));
+static int TargPrintNode __P((ClientData, ClientData));
+static void TargFreeGN __P((ClientData));
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_Init --
+ * Initialize this module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The allTargets list and the targets hash table are initialized
+ *-----------------------------------------------------------------------
+ */
+void
+Targ_Init ()
+{
+ allTargets = Lst_Init (FALSE);
+ Hash_InitTable (&targets, HTSIZE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_End --
+ * Finalize this module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * All lists and gnodes are cleared
+ *-----------------------------------------------------------------------
+ */
+void
+Targ_End ()
+{
+ Lst_Destroy(allTargets, NOFREE);
+ if (allGNs)
+ Lst_Destroy(allGNs, TargFreeGN);
+ Hash_DeleteTable(&targets);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_NewGN --
+ * Create and initialize a new graph node
+ *
+ * Results:
+ * An initialized graph node with the name field filled with a copy
+ * of the passed name
+ *
+ * Side Effects:
+ * The gnode is added to the list of all gnodes.
+ *-----------------------------------------------------------------------
+ */
+GNode *
+Targ_NewGN (name)
+ char *name; /* the name to stick in the new node */
+{
+ register GNode *gn;
+
+ gn = (GNode *) emalloc (sizeof (GNode));
+ gn->name = strdup (name);
+ gn->path = (char *) 0;
+ if (name[0] == '-' && name[1] == 'l') {
+ gn->type = OP_LIB;
+ } else {
+ gn->type = 0;
+ }
+ gn->unmade = 0;
+ gn->make = FALSE;
+ gn->made = UNMADE;
+ gn->childMade = FALSE;
+ gn->mtime = gn->cmtime = 0;
+ gn->iParents = Lst_Init (FALSE);
+ gn->cohorts = Lst_Init (FALSE);
+ gn->parents = Lst_Init (FALSE);
+ gn->children = Lst_Init (FALSE);
+ gn->successors = Lst_Init (FALSE);
+ gn->preds = Lst_Init (FALSE);
+ gn->context = Lst_Init (FALSE);
+ gn->commands = Lst_Init (FALSE);
+ gn->suffix = NULL;
+
+ if (allGNs == NULL)
+ allGNs = Lst_Init(FALSE);
+ Lst_AtEnd(allGNs, (ClientData) gn);
+
+ return (gn);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * TargFreeGN --
+ * Destroy a GNode
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *-----------------------------------------------------------------------
+ */
+static void
+TargFreeGN (gnp)
+ ClientData gnp;
+{
+ GNode *gn = (GNode *) gnp;
+
+
+ free(gn->name);
+ if (gn->path)
+ free(gn->path);
+
+ Lst_Destroy(gn->iParents, NOFREE);
+ Lst_Destroy(gn->cohorts, NOFREE);
+ Lst_Destroy(gn->parents, NOFREE);
+ Lst_Destroy(gn->children, NOFREE);
+ Lst_Destroy(gn->successors, NOFREE);
+ Lst_Destroy(gn->preds, NOFREE);
+ Lst_Destroy(gn->context, NOFREE);
+ Lst_Destroy(gn->commands, NOFREE);
+ free((Address)gn);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_FindNode --
+ * Find a node in the list using the given name for matching
+ *
+ * Results:
+ * The node in the list if it was. If it wasn't, return NILGNODE of
+ * flags was TARG_NOCREATE or the newly created and initialized node
+ * if it was TARG_CREATE
+ *
+ * Side Effects:
+ * Sometimes a node is created and added to the list
+ *-----------------------------------------------------------------------
+ */
+GNode *
+Targ_FindNode (name, flags)
+ char *name; /* the name to find */
+ int flags; /* flags governing events when target not
+ * found */
+{
+ GNode *gn; /* node in that element */
+ Hash_Entry *he; /* New or used hash entry for node */
+ Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */
+ /* an entry for the node */
+
+
+ if (flags & TARG_CREATE) {
+ he = Hash_CreateEntry (&targets, name, &isNew);
+ if (isNew) {
+ gn = Targ_NewGN (name);
+ Hash_SetValue (he, gn);
+ (void) Lst_AtEnd (allTargets, (ClientData)gn);
+ }
+ } else {
+ he = Hash_FindEntry (&targets, name);
+ }
+
+ if (he == (Hash_Entry *) NULL) {
+ return (NILGNODE);
+ } else {
+ return ((GNode *) Hash_GetValue (he));
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_FindList --
+ * Make a complete list of GNodes from the given list of names
+ *
+ * Results:
+ * A complete list of graph nodes corresponding to all instances of all
+ * the names in names.
+ *
+ * Side Effects:
+ * If flags is TARG_CREATE, nodes will be created for all names in
+ * names which do not yet have graph nodes. If flags is TARG_NOCREATE,
+ * an error message will be printed for each name which can't be found.
+ * -----------------------------------------------------------------------
+ */
+Lst
+Targ_FindList (names, flags)
+ Lst names; /* list of names to find */
+ int flags; /* flags used if no node is found for a given
+ * name */
+{
+ Lst nodes; /* result list */
+ register LstNode ln; /* name list element */
+ register GNode *gn; /* node in tLn */
+ char *name;
+
+ nodes = Lst_Init (FALSE);
+
+ if (Lst_Open (names) == FAILURE) {
+ return (nodes);
+ }
+ while ((ln = Lst_Next (names)) != NILLNODE) {
+ name = (char *)Lst_Datum(ln);
+ gn = Targ_FindNode (name, flags);
+ if (gn != NILGNODE) {
+ /*
+ * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
+ * are added to the list in the order in which they were
+ * encountered in the makefile.
+ */
+ (void) Lst_AtEnd (nodes, (ClientData)gn);
+ if (gn->type & OP_DOUBLEDEP) {
+ (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW);
+ }
+ } else if (flags == TARG_NOCREATE) {
+ Error ("\"%s\" -- target unknown.", name);
+ }
+ }
+ Lst_Close (names);
+ return (nodes);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_Ignore --
+ * Return true if should ignore errors when creating gn
+ *
+ * Results:
+ * TRUE if should ignore errors
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Targ_Ignore (gn)
+ GNode *gn; /* node to check for */
+{
+ if (ignoreErrors || gn->type & OP_IGNORE) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_Silent --
+ * Return true if be silent when creating gn
+ *
+ * Results:
+ * TRUE if should be silent
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Targ_Silent (gn)
+ GNode *gn; /* node to check for */
+{
+ if (beSilent || gn->type & OP_SILENT) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_Precious --
+ * See if the given target is precious
+ *
+ * Results:
+ * TRUE if it is precious. FALSE otherwise
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Targ_Precious (gn)
+ GNode *gn; /* the node to check */
+{
+ if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+/******************* DEBUG INFO PRINTING ****************/
+
+static GNode *mainTarg; /* the main target, as set by Targ_SetMain */
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_SetMain --
+ * Set our idea of the main target we'll be creating. Used for
+ * debugging output.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * "mainTarg" is set to the main target's node.
+ *-----------------------------------------------------------------------
+ */
+void
+Targ_SetMain (gn)
+ GNode *gn; /* The main target we'll create */
+{
+ mainTarg = gn;
+}
+
+static int
+TargPrintName (gnp, ppath)
+ ClientData gnp;
+ ClientData ppath;
+{
+ GNode *gn = (GNode *) gnp;
+ printf ("%s ", gn->name);
+#ifdef notdef
+ if (ppath) {
+ if (gn->path) {
+ printf ("[%s] ", gn->path);
+ }
+ if (gn == mainTarg) {
+ printf ("(MAIN NAME) ");
+ }
+ }
+#endif /* notdef */
+ return (ppath ? 0 : 0);
+}
+
+
+int
+Targ_PrintCmd (cmd, dummy)
+ ClientData cmd;
+ ClientData dummy;
+{
+ printf ("\t%s\n", (char *) cmd);
+ return (dummy ? 0 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_FmtTime --
+ * Format a modification time in some reasonable way and return it.
+ *
+ * Results:
+ * The time reformatted.
+ *
+ * Side Effects:
+ * The time is placed in a static area, so it is overwritten
+ * with each call.
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Targ_FmtTime (time)
+ time_t time;
+{
+ struct tm *parts;
+ static char buf[40];
+ static char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ parts = localtime(&time);
+
+ sprintf (buf, "%d:%02d:%02d %s %d, 19%d",
+ parts->tm_hour, parts->tm_min, parts->tm_sec,
+ months[parts->tm_mon], parts->tm_mday, parts->tm_year);
+ return(buf);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_PrintType --
+ * Print out a type field giving only those attributes the user can
+ * set.
+ *
+ * Results:
+ *
+ * Side Effects:
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Targ_PrintType (type)
+ register int type;
+{
+ register int tbit;
+
+#ifdef __STDC__
+#define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break
+#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
+#else
+#define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break
+#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
+#endif /* __STDC__ */
+
+ type &= ~OP_OPMASK;
+
+ while (type) {
+ tbit = 1 << (ffs(type) - 1);
+ type &= ~tbit;
+
+ switch(tbit) {
+ PRINTBIT(OPTIONAL);
+ PRINTBIT(USE);
+ PRINTBIT(EXEC);
+ PRINTBIT(IGNORE);
+ PRINTBIT(PRECIOUS);
+ PRINTBIT(SILENT);
+ PRINTBIT(MAKE);
+ PRINTBIT(JOIN);
+ PRINTBIT(INVISIBLE);
+ PRINTBIT(NOTMAIN);
+ PRINTDBIT(LIB);
+ /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
+ case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
+ PRINTDBIT(ARCHV);
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * TargPrintNode --
+ * print the contents of a node
+ *-----------------------------------------------------------------------
+ */
+static int
+TargPrintNode (gnp, passp)
+ ClientData gnp;
+ ClientData passp;
+{
+ GNode *gn = (GNode *) gnp;
+ int pass = *(int *) passp;
+ if (!OP_NOP(gn->type)) {
+ printf("#\n");
+ if (gn == mainTarg) {
+ printf("# *** MAIN TARGET ***\n");
+ }
+ if (pass == 2) {
+ if (gn->unmade) {
+ printf("# %d unmade children\n", gn->unmade);
+ } else {
+ printf("# No unmade children\n");
+ }
+ if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) {
+ if (gn->mtime != 0) {
+ printf("# last modified %s: %s\n",
+ Targ_FmtTime(gn->mtime),
+ (gn->made == UNMADE ? "unmade" :
+ (gn->made == MADE ? "made" :
+ (gn->made == UPTODATE ? "up-to-date" :
+ "error when made"))));
+ } else if (gn->made != UNMADE) {
+ printf("# non-existent (maybe): %s\n",
+ (gn->made == MADE ? "made" :
+ (gn->made == UPTODATE ? "up-to-date" :
+ (gn->made == ERROR ? "error when made" :
+ "aborted"))));
+ } else {
+ printf("# unmade\n");
+ }
+ }
+ if (!Lst_IsEmpty (gn->iParents)) {
+ printf("# implicit parents: ");
+ Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
+ fputc ('\n', stdout);
+ }
+ }
+ if (!Lst_IsEmpty (gn->parents)) {
+ printf("# parents: ");
+ Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
+ fputc ('\n', stdout);
+ }
+
+ printf("%-16s", gn->name);
+ switch (gn->type & OP_OPMASK) {
+ case OP_DEPENDS:
+ printf(": "); break;
+ case OP_FORCE:
+ printf("! "); break;
+ case OP_DOUBLEDEP:
+ printf(":: "); break;
+ }
+ Targ_PrintType (gn->type);
+ Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
+ fputc ('\n', stdout);
+ Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
+ printf("\n\n");
+ if (gn->type & OP_DOUBLEDEP) {
+ Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
+ }
+ }
+ return (0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * TargPrintOnlySrc --
+ * Print only those targets that are just a source.
+ *
+ * Results:
+ * 0.
+ *
+ * Side Effects:
+ * The name of each file is printed preceeded by #\t
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+TargPrintOnlySrc(gnp, dummy)
+ ClientData gnp;
+ ClientData dummy;
+{
+ GNode *gn = (GNode *) gnp;
+ if (OP_NOP(gn->type))
+ printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
+
+ return (dummy ? 0 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Targ_PrintGraph --
+ * print the entire graph. heh heh
+ *
+ * Results:
+ * none
+ *
+ * Side Effects:
+ * lots o' output
+ *-----------------------------------------------------------------------
+ */
+void
+Targ_PrintGraph (pass)
+ int pass; /* Which pass this is. 1 => no processing
+ * 2 => processing done */
+{
+ printf("#*** Input graph:\n");
+ Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
+ printf("\n\n");
+ printf("#\n# Files that are only sources:\n");
+ Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
+ printf("#*** Global Variables:\n");
+ Var_Dump (VAR_GLOBAL);
+ printf("#*** Command-line Variables:\n");
+ Var_Dump (VAR_CMD);
+ printf("\n");
+ Dir_PrintDirectories();
+ printf("\n");
+ Suff_PrintAll();
+}
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c
new file mode 100644
index 0000000..851a80c
--- /dev/null
+++ b/usr.bin/make/var.c
@@ -0,0 +1,2033 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+/*-
+ * var.c --
+ * Variable-handling functions
+ *
+ * Interface:
+ * Var_Set Set the value of a variable in the given
+ * context. The variable is created if it doesn't
+ * yet exist. The value and variable name need not
+ * be preserved.
+ *
+ * Var_Append Append more characters to an existing variable
+ * in the given context. The variable needn't
+ * exist already -- it will be created if it doesn't.
+ * A space is placed between the old value and the
+ * new one.
+ *
+ * Var_Exists See if a variable exists.
+ *
+ * Var_Value Return the value of a variable in a context or
+ * NULL if the variable is undefined.
+ *
+ * Var_Subst Substitute named variable, or all variables if
+ * NULL in a string using
+ * the given context as the top-most one. If the
+ * third argument is non-zero, Parse_Error is
+ * called if any variables are undefined.
+ *
+ * Var_Parse Parse a variable expansion from a string and
+ * return the result and the number of characters
+ * consumed.
+ *
+ * Var_Delete Delete a variable in a context.
+ *
+ * Var_Init Initialize this module.
+ *
+ * Debugging:
+ * Var_Dump Print out all variables defined in the given
+ * context.
+ *
+ * XXX: There's a lot of duplication in these functions.
+ */
+
+#include <ctype.h>
+#include "make.h"
+#include "buf.h"
+
+/*
+ * This is a harmless return value for Var_Parse that can be used by Var_Subst
+ * to determine if there was an error in parsing -- easier than returning
+ * a flag, as things outside this module don't give a hoot.
+ */
+char var_Error[] = "";
+
+/*
+ * Similar to var_Error, but returned when the 'err' flag for Var_Parse is
+ * set false. Why not just use a constant? Well, gcc likes to condense
+ * identical string instances...
+ */
+static char varNoError[] = "";
+
+/*
+ * Internally, variables are contained in four different contexts.
+ * 1) the environment. They may not be changed. If an environment
+ * variable is appended-to, the result is placed in the global
+ * context.
+ * 2) the global context. Variables set in the Makefile are located in
+ * the global context. It is the penultimate context searched when
+ * substituting.
+ * 3) the command-line context. All variables set on the command line
+ * are placed in this context. They are UNALTERABLE once placed here.
+ * 4) the local context. Each target has associated with it a context
+ * list. On this list are located the structures describing such
+ * local variables as $(@) and $(*)
+ * The four contexts are searched in the reverse order from which they are
+ * listed.
+ */
+GNode *VAR_GLOBAL; /* variables from the makefile */
+GNode *VAR_CMD; /* variables defined on the command-line */
+
+static Lst allVars; /* List of all variables */
+
+#define FIND_CMD 0x1 /* look in VAR_CMD when searching */
+#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */
+#define FIND_ENV 0x4 /* look in the environment also */
+
+typedef struct Var {
+ char *name; /* the variable's name */
+ Buffer val; /* its value */
+ int flags; /* miscellaneous status flags */
+#define VAR_IN_USE 1 /* Variable's value currently being used.
+ * Used to avoid recursion */
+#define VAR_FROM_ENV 2 /* Variable comes from the environment */
+#define VAR_JUNK 4 /* Variable is a junk variable that
+ * should be destroyed when done with
+ * it. Used by Var_Parse for undefined,
+ * modified variables */
+} Var;
+
+typedef struct {
+ char *lhs; /* String to match */
+ int leftLen; /* Length of string */
+ char *rhs; /* Replacement string (w/ &'s removed) */
+ int rightLen; /* Length of replacement */
+ int flags;
+#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */
+#define VAR_MATCH_START 2 /* Match at start of word */
+#define VAR_MATCH_END 4 /* Match at end of word */
+#define VAR_NO_SUB 8 /* Substitution is non-global and already done */
+} VarPattern;
+
+static int VarCmp __P((ClientData, ClientData));
+static Var *VarFind __P((char *, GNode *, int));
+static void VarAdd __P((char *, char *, GNode *));
+static void VarDelete __P((ClientData));
+static Boolean VarHead __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarTail __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarSuffix __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarRoot __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarMatch __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData));
+static Boolean VarSubstitute __P((char *, Boolean, Buffer, ClientData));
+static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer,
+ ClientData),
+ ClientData));
+static int VarPrintVar __P((ClientData, ClientData));
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarCmp --
+ * See if the given variable matches the named one. Called from
+ * Lst_Find when searching for a variable of a given name.
+ *
+ * Results:
+ * 0 if they match. non-zero otherwise.
+ *
+ * Side Effects:
+ * none
+ *-----------------------------------------------------------------------
+ */
+static int
+VarCmp (v, name)
+ ClientData v; /* VAR structure to compare */
+ ClientData name; /* name to look for */
+{
+ return (strcmp ((char *) name, ((Var *) v)->name));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarFind --
+ * Find the given variable in the given context and any other contexts
+ * indicated.
+ *
+ * Results:
+ * A pointer to the structure describing the desired variable or
+ * NIL if the variable does not exist.
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static Var *
+VarFind (name, ctxt, flags)
+ char *name; /* name to find */
+ GNode *ctxt; /* context in which to find it */
+ int flags; /* FIND_GLOBAL set means to look in the
+ * VAR_GLOBAL context as well.
+ * FIND_CMD set means to look in the VAR_CMD
+ * context also.
+ * FIND_ENV set means to look in the
+ * environment */
+{
+ LstNode var;
+ Var *v;
+
+ /*
+ * If the variable name begins with a '.', it could very well be one of
+ * the local ones. We check the name against all the local variables
+ * and substitute the short version in for 'name' if it matches one of
+ * them.
+ */
+ if (*name == '.' && isupper((unsigned char) name[1]))
+ switch (name[1]) {
+ case 'A':
+ if (!strcmp(name, ".ALLSRC"))
+ name = ALLSRC;
+ if (!strcmp(name, ".ARCHIVE"))
+ name = ARCHIVE;
+ break;
+ case 'I':
+ if (!strcmp(name, ".IMPSRC"))
+ name = IMPSRC;
+ break;
+ case 'M':
+ if (!strcmp(name, ".MEMBER"))
+ name = MEMBER;
+ break;
+ case 'O':
+ if (!strcmp(name, ".OODATE"))
+ name = OODATE;
+ break;
+ case 'P':
+ if (!strcmp(name, ".PREFIX"))
+ name = PREFIX;
+ break;
+ case 'T':
+ if (!strcmp(name, ".TARGET"))
+ name = TARGET;
+ break;
+ }
+ /*
+ * First look for the variable in the given context. If it's not there,
+ * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
+ * depending on the FIND_* flags in 'flags'
+ */
+ var = Lst_Find (ctxt->context, (ClientData)name, VarCmp);
+
+ if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) {
+ var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp);
+ }
+ if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) &&
+ (ctxt != VAR_GLOBAL))
+ {
+ var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
+ }
+ if ((var == NILLNODE) && (flags & FIND_ENV)) {
+ char *env;
+
+ if ((env = getenv (name)) != NULL) {
+ int len;
+
+ v = (Var *) emalloc(sizeof(Var));
+ v->name = strdup(name);
+
+ len = strlen(env);
+
+ v->val = Buf_Init(len);
+ Buf_AddBytes(v->val, len, (Byte *)env);
+
+ v->flags = VAR_FROM_ENV;
+ return (v);
+ } else if (checkEnvFirst && (flags & FIND_GLOBAL) &&
+ (ctxt != VAR_GLOBAL))
+ {
+ var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp);
+ if (var == NILLNODE) {
+ return ((Var *) NIL);
+ } else {
+ return ((Var *)Lst_Datum(var));
+ }
+ } else {
+ return((Var *)NIL);
+ }
+ } else if (var == NILLNODE) {
+ return ((Var *) NIL);
+ } else {
+ return ((Var *) Lst_Datum (var));
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarAdd --
+ * Add a new variable of name name and value val to the given context
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The new variable is placed at the front of the given context
+ * The name and val arguments are duplicated so they may
+ * safely be freed.
+ *-----------------------------------------------------------------------
+ */
+static void
+VarAdd (name, val, ctxt)
+ char *name; /* name of variable to add */
+ char *val; /* value to set it to */
+ GNode *ctxt; /* context in which to set it */
+{
+ register Var *v;
+ int len;
+
+ v = (Var *) emalloc (sizeof (Var));
+
+ v->name = strdup (name);
+
+ len = val ? strlen(val) : 0;
+ v->val = Buf_Init(len+1);
+ Buf_AddBytes(v->val, len, (Byte *)val);
+
+ v->flags = 0;
+
+ (void) Lst_AtFront (ctxt->context, (ClientData)v);
+ (void) Lst_AtEnd (allVars, (ClientData) v);
+ if (DEBUG(VAR)) {
+ printf("%s:%s = %s\n", ctxt->name, name, val);
+ }
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarDelete --
+ * Delete a variable and all the space associated with it.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+static void
+VarDelete(vp)
+ ClientData vp;
+{
+ Var *v = (Var *) vp;
+ free(v->name);
+ Buf_Destroy(v->val, TRUE);
+ free((Address) v);
+}
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Delete --
+ * Remove a variable from a context.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * The Var structure is removed and freed.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Var_Delete(name, ctxt)
+ char *name;
+ GNode *ctxt;
+{
+ LstNode ln;
+
+ if (DEBUG(VAR)) {
+ printf("%s:delete %s\n", ctxt->name, name);
+ }
+ ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp);
+ if (ln != NILLNODE) {
+ register Var *v;
+
+ v = (Var *)Lst_Datum(ln);
+ Lst_Remove(ctxt->context, ln);
+ ln = Lst_Member(allVars, v);
+ Lst_Remove(allVars, ln);
+ VarDelete((ClientData) v);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Set --
+ * Set the variable name to the value val in the given context.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * If the variable doesn't yet exist, a new record is created for it.
+ * Else the old value is freed and the new one stuck in its place
+ *
+ * Notes:
+ * The variable is searched for only in its context before being
+ * created in that context. I.e. if the context is VAR_GLOBAL,
+ * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only
+ * VAR_CMD->context is searched. This is done to avoid the literally
+ * thousands of unnecessary strcmp's that used to be done to
+ * set, say, $(@) or $(<).
+ *-----------------------------------------------------------------------
+ */
+void
+Var_Set (name, val, ctxt)
+ char *name; /* name of variable to set */
+ char *val; /* value to give to the variable */
+ GNode *ctxt; /* context in which to set it */
+{
+ register Var *v;
+
+ /*
+ * We only look for a variable in the given context since anything set
+ * here will override anything in a lower context, so there's not much
+ * point in searching them all just to save a bit of memory...
+ */
+ v = VarFind (name, ctxt, 0);
+ if (v == (Var *) NIL) {
+ VarAdd (name, val, ctxt);
+ } else {
+ Buf_Discard(v->val, Buf_Size(v->val));
+ Buf_AddBytes(v->val, strlen(val), (Byte *)val);
+
+ if (DEBUG(VAR)) {
+ printf("%s:%s = %s\n", ctxt->name, name, val);
+ }
+ }
+ /*
+ * Any variables given on the command line are automatically exported
+ * to the environment (as per POSIX standard)
+ */
+ if (ctxt == VAR_CMD) {
+ setenv(name, val, 1);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Append --
+ * The variable of the given name has the given value appended to it in
+ * the given context.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * If the variable doesn't exist, it is created. Else the strings
+ * are concatenated (with a space in between).
+ *
+ * Notes:
+ * Only if the variable is being sought in the global context is the
+ * environment searched.
+ * XXX: Knows its calling circumstances in that if called with ctxt
+ * an actual target, it will only search that context since only
+ * a local variable could be being appended to. This is actually
+ * a big win and must be tolerated.
+ *-----------------------------------------------------------------------
+ */
+void
+Var_Append (name, val, ctxt)
+ char *name; /* Name of variable to modify */
+ char *val; /* String to append to it */
+ GNode *ctxt; /* Context in which this should occur */
+{
+ register Var *v;
+
+ v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
+
+ if (v == (Var *) NIL) {
+ VarAdd (name, val, ctxt);
+ } else {
+ Buf_AddByte(v->val, (Byte)' ');
+ Buf_AddBytes(v->val, strlen(val), (Byte *)val);
+
+ if (DEBUG(VAR)) {
+ printf("%s:%s = %s\n", ctxt->name, name,
+ (char *) Buf_GetAll(v->val, (int *)NULL));
+ }
+
+ if (v->flags & VAR_FROM_ENV) {
+ /*
+ * If the original variable came from the environment, we
+ * have to install it in the global context (we could place
+ * it in the environment, but then we should provide a way to
+ * export other variables...)
+ */
+ v->flags &= ~VAR_FROM_ENV;
+ Lst_AtFront(ctxt->context, (ClientData)v);
+ }
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Exists --
+ * See if the given variable exists.
+ *
+ * Results:
+ * TRUE if it does, FALSE if it doesn't
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+Boolean
+Var_Exists(name, ctxt)
+ char *name; /* Variable to find */
+ GNode *ctxt; /* Context in which to start search */
+{
+ Var *v;
+
+ v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV);
+
+ if (v == (Var *)NIL) {
+ return(FALSE);
+ } else if (v->flags & VAR_FROM_ENV) {
+ free(v->name);
+ Buf_Destroy(v->val, TRUE);
+ free((char *)v);
+ }
+ return(TRUE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Value --
+ * Return the value of the named variable in the given context
+ *
+ * Results:
+ * The value if the variable exists, NULL if it doesn't
+ *
+ * Side Effects:
+ * None
+ *-----------------------------------------------------------------------
+ */
+char *
+Var_Value (name, ctxt, frp)
+ char *name; /* name to find */
+ GNode *ctxt; /* context in which to search for it */
+ char **frp;
+{
+ Var *v;
+
+ v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
+ *frp = NULL;
+ if (v != (Var *) NIL) {
+ char *p = ((char *)Buf_GetAll(v->val, (int *)NULL));
+ if (v->flags & VAR_FROM_ENV) {
+ Buf_Destroy(v->val, FALSE);
+ free((Address) v);
+ *frp = p;
+ }
+ return p;
+ } else {
+ return ((char *) NULL);
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarHead --
+ * Remove the tail of the given word and place the result in the given
+ * buffer.
+ *
+ * Results:
+ * TRUE if characters were added to the buffer (a space needs to be
+ * added to the buffer before the next word).
+ *
+ * Side Effects:
+ * The trimmed word is added to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarHead (word, addSpace, buf, dummy)
+ char *word; /* Word to trim */
+ Boolean addSpace; /* True if need to add a space to the buffer
+ * before sticking in the head */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData dummy;
+{
+ register char *slash;
+
+ slash = strrchr (word, '/');
+ if (slash != (char *)NULL) {
+ if (addSpace) {
+ Buf_AddByte (buf, (Byte)' ');
+ }
+ *slash = '\0';
+ Buf_AddBytes (buf, strlen (word), (Byte *)word);
+ *slash = '/';
+ return (TRUE);
+ } else {
+ /*
+ * If no directory part, give . (q.v. the POSIX standard)
+ */
+ if (addSpace) {
+ Buf_AddBytes(buf, 2, (Byte *)" .");
+ } else {
+ Buf_AddByte(buf, (Byte)'.');
+ }
+ }
+ return(dummy ? TRUE : TRUE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarTail --
+ * Remove the head of the given word and place the result in the given
+ * buffer.
+ *
+ * Results:
+ * TRUE if characters were added to the buffer (a space needs to be
+ * added to the buffer before the next word).
+ *
+ * Side Effects:
+ * The trimmed word is added to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarTail (word, addSpace, buf, dummy)
+ char *word; /* Word to trim */
+ Boolean addSpace; /* TRUE if need to stick a space in the
+ * buffer before adding the tail */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData dummy;
+{
+ register char *slash;
+
+ if (addSpace) {
+ Buf_AddByte (buf, (Byte)' ');
+ }
+
+ slash = strrchr (word, '/');
+ if (slash != (char *)NULL) {
+ *slash++ = '\0';
+ Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
+ slash[-1] = '/';
+ } else {
+ Buf_AddBytes (buf, strlen(word), (Byte *)word);
+ }
+ return (dummy ? TRUE : TRUE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarSuffix --
+ * Place the suffix of the given word in the given buffer.
+ *
+ * Results:
+ * TRUE if characters were added to the buffer (a space needs to be
+ * added to the buffer before the next word).
+ *
+ * Side Effects:
+ * The suffix from the word is placed in the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarSuffix (word, addSpace, buf, dummy)
+ char *word; /* Word to trim */
+ Boolean addSpace; /* TRUE if need to add a space before placing
+ * the suffix in the buffer */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData dummy;
+{
+ register char *dot;
+
+ dot = strrchr (word, '.');
+ if (dot != (char *)NULL) {
+ if (addSpace) {
+ Buf_AddByte (buf, (Byte)' ');
+ }
+ *dot++ = '\0';
+ Buf_AddBytes (buf, strlen (dot), (Byte *)dot);
+ dot[-1] = '.';
+ addSpace = TRUE;
+ }
+ return (dummy ? addSpace : addSpace);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarRoot --
+ * Remove the suffix of the given word and place the result in the
+ * buffer.
+ *
+ * Results:
+ * TRUE if characters were added to the buffer (a space needs to be
+ * added to the buffer before the next word).
+ *
+ * Side Effects:
+ * The trimmed word is added to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarRoot (word, addSpace, buf, dummy)
+ char *word; /* Word to trim */
+ Boolean addSpace; /* TRUE if need to add a space to the buffer
+ * before placing the root in it */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData dummy;
+{
+ register char *dot;
+
+ if (addSpace) {
+ Buf_AddByte (buf, (Byte)' ');
+ }
+
+ dot = strrchr (word, '.');
+ if (dot != (char *)NULL) {
+ *dot = '\0';
+ Buf_AddBytes (buf, strlen (word), (Byte *)word);
+ *dot = '.';
+ } else {
+ Buf_AddBytes (buf, strlen(word), (Byte *)word);
+ }
+ return (dummy ? TRUE : TRUE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarMatch --
+ * Place the word in the buffer if it matches the given pattern.
+ * Callback function for VarModify to implement the :M modifier.
+ *
+ * Results:
+ * TRUE if a space should be placed in the buffer before the next
+ * word.
+ *
+ * Side Effects:
+ * The word may be copied to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarMatch (word, addSpace, buf, pattern)
+ char *word; /* Word to examine */
+ Boolean addSpace; /* TRUE if need to add a space to the
+ * buffer before adding the word, if it
+ * matches */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData pattern; /* Pattern the word must match */
+{
+ if (Str_Match(word, (char *) pattern)) {
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ addSpace = TRUE;
+ Buf_AddBytes(buf, strlen(word), (Byte *)word);
+ }
+ return(addSpace);
+}
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarSYSVMatch --
+ * Place the word in the buffer if it matches the given pattern.
+ * Callback function for VarModify to implement the System V %
+ * modifiers.
+ *
+ * Results:
+ * TRUE if a space should be placed in the buffer before the next
+ * word.
+ *
+ * Side Effects:
+ * The word may be copied to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarSYSVMatch (word, addSpace, buf, patp)
+ char *word; /* Word to examine */
+ Boolean addSpace; /* TRUE if need to add a space to the
+ * buffer before adding the word, if it
+ * matches */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData patp; /* Pattern the word must match */
+{
+ int len;
+ char *ptr;
+ VarPattern *pat = (VarPattern *) patp;
+
+ if (addSpace)
+ Buf_AddByte(buf, (Byte)' ');
+
+ addSpace = TRUE;
+
+ if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
+ Str_SYSVSubst(buf, pat->rhs, ptr, len);
+ else
+ Buf_AddBytes(buf, strlen(word), (Byte *) word);
+
+ return(addSpace);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarNoMatch --
+ * Place the word in the buffer if it doesn't match the given pattern.
+ * Callback function for VarModify to implement the :N modifier.
+ *
+ * Results:
+ * TRUE if a space should be placed in the buffer before the next
+ * word.
+ *
+ * Side Effects:
+ * The word may be copied to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarNoMatch (word, addSpace, buf, pattern)
+ char *word; /* Word to examine */
+ Boolean addSpace; /* TRUE if need to add a space to the
+ * buffer before adding the word, if it
+ * matches */
+ Buffer buf; /* Buffer in which to store it */
+ ClientData pattern; /* Pattern the word must match */
+{
+ if (!Str_Match(word, (char *) pattern)) {
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ addSpace = TRUE;
+ Buf_AddBytes(buf, strlen(word), (Byte *)word);
+ }
+ return(addSpace);
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarSubstitute --
+ * Perform a string-substitution on the given word, placing the
+ * result in the passed buffer.
+ *
+ * Results:
+ * TRUE if a space is needed before more characters are added.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarSubstitute (word, addSpace, buf, patternp)
+ char *word; /* Word to modify */
+ Boolean addSpace; /* True if space should be added before
+ * other characters */
+ Buffer buf; /* Buffer for result */
+ ClientData patternp; /* Pattern for substitution */
+{
+ register int wordLen; /* Length of word */
+ register char *cp; /* General pointer */
+ VarPattern *pattern = (VarPattern *) patternp;
+
+ wordLen = strlen(word);
+ if ((pattern->flags & VAR_NO_SUB) == 0) {
+ /*
+ * Still substituting -- break it down into simple anchored cases
+ * and if none of them fits, perform the general substitution case.
+ */
+ if ((pattern->flags & VAR_MATCH_START) &&
+ (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
+ /*
+ * Anchored at start and beginning of word matches pattern
+ */
+ if ((pattern->flags & VAR_MATCH_END) &&
+ (wordLen == pattern->leftLen)) {
+ /*
+ * Also anchored at end and matches to the end (word
+ * is same length as pattern) add space and rhs only
+ * if rhs is non-null.
+ */
+ if (pattern->rightLen != 0) {
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ addSpace = TRUE;
+ Buf_AddBytes(buf, pattern->rightLen,
+ (Byte *)pattern->rhs);
+ }
+ } else if (pattern->flags & VAR_MATCH_END) {
+ /*
+ * Doesn't match to end -- copy word wholesale
+ */
+ goto nosub;
+ } else {
+ /*
+ * Matches at start but need to copy in trailing characters
+ */
+ if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ addSpace = TRUE;
+ }
+ Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
+ Buf_AddBytes(buf, wordLen - pattern->leftLen,
+ (Byte *)(word + pattern->leftLen));
+ }
+ } else if (pattern->flags & VAR_MATCH_START) {
+ /*
+ * Had to match at start of word and didn't -- copy whole word.
+ */
+ goto nosub;
+ } else if (pattern->flags & VAR_MATCH_END) {
+ /*
+ * Anchored at end, Find only place match could occur (leftLen
+ * characters from the end of the word) and see if it does. Note
+ * that because the $ will be left at the end of the lhs, we have
+ * to use strncmp.
+ */
+ cp = word + (wordLen - pattern->leftLen);
+ if ((cp >= word) &&
+ (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
+ /*
+ * Match found. If we will place characters in the buffer,
+ * add a space before hand as indicated by addSpace, then
+ * stuff in the initial, unmatched part of the word followed
+ * by the right-hand-side.
+ */
+ if (((cp - word) + pattern->rightLen) != 0) {
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ addSpace = TRUE;
+ }
+ Buf_AddBytes(buf, cp - word, (Byte *)word);
+ Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
+ } else {
+ /*
+ * Had to match at end and didn't. Copy entire word.
+ */
+ goto nosub;
+ }
+ } else {
+ /*
+ * Pattern is unanchored: search for the pattern in the word using
+ * String_FindSubstring, copying unmatched portions and the
+ * right-hand-side for each match found, handling non-global
+ * subsititutions correctly, etc. When the loop is done, any
+ * remaining part of the word (word and wordLen are adjusted
+ * accordingly through the loop) is copied straight into the
+ * buffer.
+ * addSpace is set FALSE as soon as a space is added to the
+ * buffer.
+ */
+ register Boolean done;
+ int origSize;
+
+ done = FALSE;
+ origSize = Buf_Size(buf);
+ while (!done) {
+ cp = Str_FindSubstring(word, pattern->lhs);
+ if (cp != (char *)NULL) {
+ if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
+ Buf_AddByte(buf, (Byte)' ');
+ addSpace = FALSE;
+ }
+ Buf_AddBytes(buf, cp-word, (Byte *)word);
+ Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs);
+ wordLen -= (cp - word) + pattern->leftLen;
+ word = cp + pattern->leftLen;
+ if (wordLen == 0) {
+ done = TRUE;
+ }
+ if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
+ done = TRUE;
+ pattern->flags |= VAR_NO_SUB;
+ }
+ } else {
+ done = TRUE;
+ }
+ }
+ if (wordLen != 0) {
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ Buf_AddBytes(buf, wordLen, (Byte *)word);
+ }
+ /*
+ * If added characters to the buffer, need to add a space
+ * before we add any more. If we didn't add any, just return
+ * the previous value of addSpace.
+ */
+ return ((Buf_Size(buf) != origSize) || addSpace);
+ }
+ /*
+ * Common code for anchored substitutions: if performed a substitution
+ * and it's not supposed to be global, mark the pattern as requiring
+ * no more substitutions. addSpace was set TRUE if characters were
+ * added to the buffer.
+ */
+ if ((pattern->flags & VAR_SUB_GLOBAL) == 0) {
+ pattern->flags |= VAR_NO_SUB;
+ }
+ return (addSpace);
+ }
+ nosub:
+ if (addSpace) {
+ Buf_AddByte(buf, (Byte)' ');
+ }
+ Buf_AddBytes(buf, wordLen, (Byte *)word);
+ return(TRUE);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarModify --
+ * Modify each of the words of the passed string using the given
+ * function. Used to implement all modifiers.
+ *
+ * Results:
+ * A string of all the words modified appropriately.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static char *
+VarModify (str, modProc, datum)
+ char *str; /* String whose words should be trimmed */
+ /* Function to use to modify them */
+ Boolean (*modProc) __P((char *, Boolean, Buffer, ClientData));
+ ClientData datum; /* Datum to pass it */
+{
+ Buffer buf; /* Buffer for the new string */
+ Boolean addSpace; /* TRUE if need to add a space to the
+ * buffer before adding the trimmed
+ * word */
+ char **av; /* word list [first word does not count] */
+ int ac, i;
+
+ buf = Buf_Init (0);
+ addSpace = FALSE;
+
+ av = brk_string(str, &ac, FALSE);
+
+ for (i = 1; i < ac; i++)
+ addSpace = (*modProc)(av[i], addSpace, buf, datum);
+
+ Buf_AddByte (buf, '\0');
+ str = (char *)Buf_GetAll (buf, (int *)NULL);
+ Buf_Destroy (buf, FALSE);
+ return (str);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Parse --
+ * Given the start of a variable invocation, extract the variable
+ * name and find its value, then modify it according to the
+ * specification.
+ *
+ * Results:
+ * The (possibly-modified) value of the variable or var_Error if the
+ * specification is invalid. The length of the specification is
+ * placed in *lengthPtr (for invalid specifications, this is just
+ * 2...?).
+ * A Boolean in *freePtr telling whether the returned string should
+ * be freed by the caller.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Var_Parse (str, ctxt, err, lengthPtr, freePtr)
+ char *str; /* The string to parse */
+ GNode *ctxt; /* The context for the variable */
+ Boolean err; /* TRUE if undefined variables are an error */
+ int *lengthPtr; /* OUT: The length of the specification */
+ Boolean *freePtr; /* OUT: TRUE if caller should free result */
+{
+ register char *tstr; /* Pointer into str */
+ Var *v; /* Variable in invocation */
+ register char *cp; /* Secondary pointer into str (place marker
+ * for tstr) */
+ Boolean haveModifier;/* TRUE if have modifiers for the variable */
+ register char endc; /* Ending character when variable in parens
+ * or braces */
+ register char startc; /* Starting character when variable in parens
+ * or braces */
+ int cnt; /* Used to count brace pairs when variable in
+ * in parens or braces */
+ char *start;
+ Boolean dynamic; /* TRUE if the variable is local and we're
+ * expanding it in a non-local context. This
+ * is done to support dynamic sources. The
+ * result is just the invocation, unaltered */
+
+ *freePtr = FALSE;
+ dynamic = FALSE;
+ start = str;
+
+ if (str[1] != '(' && str[1] != '{') {
+ /*
+ * If it's not bounded by braces of some sort, life is much simpler.
+ * We just need to check for the first character and return the
+ * value if it exists.
+ */
+ char name[2];
+
+ name[0] = str[1];
+ name[1] = '\0';
+
+ v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
+ if (v == (Var *)NIL) {
+ *lengthPtr = 2;
+
+ if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) {
+ /*
+ * If substituting a local variable in a non-local context,
+ * assume it's for dynamic source stuff. We have to handle
+ * this specially and return the longhand for the variable
+ * with the dollar sign escaped so it makes it back to the
+ * caller. Only four of the local variables are treated
+ * specially as they are the only four that will be set
+ * when dynamic sources are expanded.
+ */
+ switch (str[1]) {
+ case '@':
+ return("$(.TARGET)");
+ case '%':
+ return("$(.ARCHIVE)");
+ case '*':
+ return("$(.PREFIX)");
+ case '!':
+ return("$(.MEMBER)");
+ }
+ }
+ /*
+ * Error
+ */
+ return (err ? var_Error : varNoError);
+ } else {
+ haveModifier = FALSE;
+ tstr = &str[1];
+ endc = str[1];
+ }
+ } else {
+ startc = str[1];
+ endc = startc == '(' ? ')' : '}';
+
+ /*
+ * Skip to the end character or a colon, whichever comes first.
+ */
+ for (tstr = str + 2;
+ *tstr != '\0' && *tstr != endc && *tstr != ':';
+ tstr++)
+ {
+ continue;
+ }
+ if (*tstr == ':') {
+ haveModifier = TRUE;
+ } else if (*tstr != '\0') {
+ haveModifier = FALSE;
+ } else {
+ /*
+ * If we never did find the end character, return NULL
+ * right now, setting the length to be the distance to
+ * the end of the string, since that's what make does.
+ */
+ *lengthPtr = tstr - str;
+ return (var_Error);
+ }
+ *tstr = '\0';
+
+ v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
+ if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
+ ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D'))
+ {
+ /*
+ * Check for bogus D and F forms of local variables since we're
+ * in a local context and the name is the right length.
+ */
+ switch(str[2]) {
+ case '@':
+ case '%':
+ case '*':
+ case '!':
+ case '>':
+ case '<':
+ {
+ char vname[2];
+ char *val;
+
+ /*
+ * Well, it's local -- go look for it.
+ */
+ vname[0] = str[2];
+ vname[1] = '\0';
+ v = VarFind(vname, ctxt, 0);
+
+ if (v != (Var *)NIL) {
+ /*
+ * No need for nested expansion or anything, as we're
+ * the only one who sets these things and we sure don't
+ * but nested invocations in them...
+ */
+ val = (char *)Buf_GetAll(v->val, (int *)NULL);
+
+ if (str[3] == 'D') {
+ val = VarModify(val, VarHead, (ClientData)0);
+ } else {
+ val = VarModify(val, VarTail, (ClientData)0);
+ }
+ /*
+ * Resulting string is dynamically allocated, so
+ * tell caller to free it.
+ */
+ *freePtr = TRUE;
+ *lengthPtr = tstr-start+1;
+ *tstr = endc;
+ return(val);
+ }
+ break;
+ }
+ }
+ }
+
+ if (v == (Var *)NIL) {
+ if ((((tstr-str) == 3) ||
+ ((((tstr-str) == 4) && (str[3] == 'F' ||
+ str[3] == 'D')))) &&
+ ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
+ {
+ /*
+ * If substituting a local variable in a non-local context,
+ * assume it's for dynamic source stuff. We have to handle
+ * this specially and return the longhand for the variable
+ * with the dollar sign escaped so it makes it back to the
+ * caller. Only four of the local variables are treated
+ * specially as they are the only four that will be set
+ * when dynamic sources are expanded.
+ */
+ switch (str[2]) {
+ case '@':
+ case '%':
+ case '*':
+ case '!':
+ dynamic = TRUE;
+ break;
+ }
+ } else if (((tstr-str) > 4) && (str[2] == '.') &&
+ isupper((unsigned char) str[3]) &&
+ ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
+ {
+ int len;
+
+ len = (tstr-str) - 3;
+ if ((strncmp(str+2, ".TARGET", len) == 0) ||
+ (strncmp(str+2, ".ARCHIVE", len) == 0) ||
+ (strncmp(str+2, ".PREFIX", len) == 0) ||
+ (strncmp(str+2, ".MEMBER", len) == 0))
+ {
+ dynamic = TRUE;
+ }
+ }
+
+ if (!haveModifier) {
+ /*
+ * No modifiers -- have specification length so we can return
+ * now.
+ */
+ *lengthPtr = tstr - start + 1;
+ *tstr = endc;
+ if (dynamic) {
+ str = emalloc(*lengthPtr + 1);
+ strncpy(str, start, *lengthPtr);
+ str[*lengthPtr] = '\0';
+ *freePtr = TRUE;
+ return(str);
+ } else {
+ return (err ? var_Error : varNoError);
+ }
+ } else {
+ /*
+ * Still need to get to the end of the variable specification,
+ * so kludge up a Var structure for the modifications
+ */
+ v = (Var *) emalloc(sizeof(Var));
+ v->name = &str[1];
+ v->val = Buf_Init(1);
+ v->flags = VAR_JUNK;
+ }
+ }
+ }
+
+ if (v->flags & VAR_IN_USE) {
+ Fatal("Variable %s is recursive.", v->name);
+ /*NOTREACHED*/
+ } else {
+ v->flags |= VAR_IN_USE;
+ }
+ /*
+ * Before doing any modification, we have to make sure the value
+ * has been fully expanded. If it looks like recursion might be
+ * necessary (there's a dollar sign somewhere in the variable's value)
+ * we just call Var_Subst to do any other substitutions that are
+ * necessary. Note that the value returned by Var_Subst will have
+ * been dynamically-allocated, so it will need freeing when we
+ * return.
+ */
+ str = (char *)Buf_GetAll(v->val, (int *)NULL);
+ if (strchr (str, '$') != (char *)NULL) {
+ str = Var_Subst(NULL, str, ctxt, err);
+ *freePtr = TRUE;
+ }
+
+ v->flags &= ~VAR_IN_USE;
+
+ /*
+ * Now we need to apply any modifiers the user wants applied.
+ * These are:
+ * :M<pattern> words which match the given <pattern>.
+ * <pattern> is of the standard file
+ * wildcarding form.
+ * :S<d><pat1><d><pat2><d>[g]
+ * Substitute <pat2> for <pat1> in the value
+ * :H Substitute the head of each word
+ * :T Substitute the tail of each word
+ * :E Substitute the extension (minus '.') of
+ * each word
+ * :R Substitute the root of each word
+ * (pathname minus the suffix).
+ * :lhs=rhs Like :S, but the rhs goes to the end of
+ * the invocation.
+ */
+ if ((str != (char *)NULL) && haveModifier) {
+ /*
+ * Skip initial colon while putting it back.
+ */
+ *tstr++ = ':';
+ while (*tstr != endc) {
+ char *newStr; /* New value to return */
+ char termc; /* Character which terminated scan */
+
+ if (DEBUG(VAR)) {
+ printf("Applying :%c to \"%s\"\n", *tstr, str);
+ }
+ switch (*tstr) {
+ case 'N':
+ case 'M':
+ {
+ char *pattern;
+ char *cp2;
+ Boolean copy;
+
+ copy = FALSE;
+ for (cp = tstr + 1;
+ *cp != '\0' && *cp != ':' && *cp != endc;
+ cp++)
+ {
+ if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
+ copy = TRUE;
+ cp++;
+ }
+ }
+ termc = *cp;
+ *cp = '\0';
+ if (copy) {
+ /*
+ * Need to compress the \:'s out of the pattern, so
+ * allocate enough room to hold the uncompressed
+ * pattern (note that cp started at tstr+1, so
+ * cp - tstr takes the null byte into account) and
+ * compress the pattern into the space.
+ */
+ pattern = emalloc(cp - tstr);
+ for (cp2 = pattern, cp = tstr + 1;
+ *cp != '\0';
+ cp++, cp2++)
+ {
+ if ((*cp == '\\') &&
+ (cp[1] == ':' || cp[1] == endc)) {
+ cp++;
+ }
+ *cp2 = *cp;
+ }
+ *cp2 = '\0';
+ } else {
+ pattern = &tstr[1];
+ }
+ if (*tstr == 'M' || *tstr == 'm') {
+ newStr = VarModify(str, VarMatch, (ClientData)pattern);
+ } else {
+ newStr = VarModify(str, VarNoMatch,
+ (ClientData)pattern);
+ }
+ if (copy) {
+ free(pattern);
+ }
+ break;
+ }
+ case 'S':
+ {
+ VarPattern pattern;
+ register char delim;
+ Buffer buf; /* Buffer for patterns */
+
+ pattern.flags = 0;
+ delim = tstr[1];
+ tstr += 2;
+ /*
+ * If pattern begins with '^', it is anchored to the
+ * start of the word -- skip over it and flag pattern.
+ */
+ if (*tstr == '^') {
+ pattern.flags |= VAR_MATCH_START;
+ tstr += 1;
+ }
+
+ buf = Buf_Init(0);
+
+ /*
+ * Pass through the lhs looking for 1) escaped delimiters,
+ * '$'s and backslashes (place the escaped character in
+ * uninterpreted) and 2) unescaped $'s that aren't before
+ * the delimiter (expand the variable substitution).
+ * The result is left in the Buffer buf.
+ */
+ for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
+ if ((*cp == '\\') &&
+ ((cp[1] == delim) ||
+ (cp[1] == '$') ||
+ (cp[1] == '\\')))
+ {
+ Buf_AddByte(buf, (Byte)cp[1]);
+ cp++;
+ } else if (*cp == '$') {
+ if (cp[1] != delim) {
+ /*
+ * If unescaped dollar sign not before the
+ * delimiter, assume it's a variable
+ * substitution and recurse.
+ */
+ char *cp2;
+ int len;
+ Boolean freeIt;
+
+ cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
+ Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
+ if (freeIt) {
+ free(cp2);
+ }
+ cp += len - 1;
+ } else {
+ /*
+ * Unescaped $ at end of pattern => anchor
+ * pattern at end.
+ */
+ pattern.flags |= VAR_MATCH_END;
+ }
+ } else {
+ Buf_AddByte(buf, (Byte)*cp);
+ }
+ }
+
+ Buf_AddByte(buf, (Byte)'\0');
+
+ /*
+ * If lhs didn't end with the delimiter, complain and
+ * return NULL
+ */
+ if (*cp != delim) {
+ *lengthPtr = cp - start + 1;
+ if (*freePtr) {
+ free(str);
+ }
+ Buf_Destroy(buf, TRUE);
+ Error("Unclosed substitution for %s (%c missing)",
+ v->name, delim);
+ return (var_Error);
+ }
+
+ /*
+ * Fetch pattern and destroy buffer, but preserve the data
+ * in it, since that's our lhs. Note that Buf_GetAll
+ * will return the actual number of bytes, which includes
+ * the null byte, so we have to decrement the length by
+ * one.
+ */
+ pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen);
+ pattern.leftLen--;
+ Buf_Destroy(buf, FALSE);
+
+ /*
+ * Now comes the replacement string. Three things need to
+ * be done here: 1) need to compress escaped delimiters and
+ * ampersands and 2) need to replace unescaped ampersands
+ * with the l.h.s. (since this isn't regexp, we can do
+ * it right here) and 3) expand any variable substitutions.
+ */
+ buf = Buf_Init(0);
+
+ tstr = cp + 1;
+ for (cp = tstr; *cp != '\0' && *cp != delim; cp++) {
+ if ((*cp == '\\') &&
+ ((cp[1] == delim) ||
+ (cp[1] == '&') ||
+ (cp[1] == '\\') ||
+ (cp[1] == '$')))
+ {
+ Buf_AddByte(buf, (Byte)cp[1]);
+ cp++;
+ } else if ((*cp == '$') && (cp[1] != delim)) {
+ char *cp2;
+ int len;
+ Boolean freeIt;
+
+ cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
+ Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2);
+ cp += len - 1;
+ if (freeIt) {
+ free(cp2);
+ }
+ } else if (*cp == '&') {
+ Buf_AddBytes(buf, pattern.leftLen,
+ (Byte *)pattern.lhs);
+ } else {
+ Buf_AddByte(buf, (Byte)*cp);
+ }
+ }
+
+ Buf_AddByte(buf, (Byte)'\0');
+
+ /*
+ * If didn't end in delimiter character, complain
+ */
+ if (*cp != delim) {
+ *lengthPtr = cp - start + 1;
+ if (*freePtr) {
+ free(str);
+ }
+ Buf_Destroy(buf, TRUE);
+ Error("Unclosed substitution for %s (%c missing)",
+ v->name, delim);
+ return (var_Error);
+ }
+
+ pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen);
+ pattern.rightLen--;
+ Buf_Destroy(buf, FALSE);
+
+ /*
+ * Check for global substitution. If 'g' after the final
+ * delimiter, substitution is global and is marked that
+ * way.
+ */
+ cp++;
+ if (*cp == 'g') {
+ pattern.flags |= VAR_SUB_GLOBAL;
+ cp++;
+ }
+
+ termc = *cp;
+ newStr = VarModify(str, VarSubstitute,
+ (ClientData)&pattern);
+ /*
+ * Free the two strings.
+ */
+ free(pattern.lhs);
+ free(pattern.rhs);
+ break;
+ }
+ case 'T':
+ if (tstr[1] == endc || tstr[1] == ':') {
+ newStr = VarModify (str, VarTail, (ClientData)0);
+ cp = tstr + 1;
+ termc = *cp;
+ break;
+ }
+ /*FALLTHRU*/
+ case 'H':
+ if (tstr[1] == endc || tstr[1] == ':') {
+ newStr = VarModify (str, VarHead, (ClientData)0);
+ cp = tstr + 1;
+ termc = *cp;
+ break;
+ }
+ /*FALLTHRU*/
+ case 'E':
+ if (tstr[1] == endc || tstr[1] == ':') {
+ newStr = VarModify (str, VarSuffix, (ClientData)0);
+ cp = tstr + 1;
+ termc = *cp;
+ break;
+ }
+ /*FALLTHRU*/
+ case 'R':
+ if (tstr[1] == endc || tstr[1] == ':') {
+ newStr = VarModify (str, VarRoot, (ClientData)0);
+ cp = tstr + 1;
+ termc = *cp;
+ break;
+ }
+ /*FALLTHRU*/
+ default: {
+ /*
+ * This can either be a bogus modifier or a System-V
+ * substitution command.
+ */
+ VarPattern pattern;
+ Boolean eqFound;
+
+ pattern.flags = 0;
+ eqFound = FALSE;
+ /*
+ * First we make a pass through the string trying
+ * to verify it is a SYSV-make-style translation:
+ * it must be: <string1>=<string2>)
+ */
+ cp = tstr;
+ cnt = 1;
+ while (*cp != '\0' && cnt) {
+ if (*cp == '=') {
+ eqFound = TRUE;
+ /* continue looking for endc */
+ }
+ else if (*cp == endc)
+ cnt--;
+ else if (*cp == startc)
+ cnt++;
+ if (cnt)
+ cp++;
+ }
+ if (*cp == endc && eqFound) {
+
+ /*
+ * Now we break this sucker into the lhs and
+ * rhs. We must null terminate them of course.
+ */
+ for (cp = tstr; *cp != '='; cp++)
+ continue;
+ pattern.lhs = tstr;
+ pattern.leftLen = cp - tstr;
+ *cp++ = '\0';
+
+ pattern.rhs = cp;
+ cnt = 1;
+ while (cnt) {
+ if (*cp == endc)
+ cnt--;
+ else if (*cp == startc)
+ cnt++;
+ if (cnt)
+ cp++;
+ }
+ pattern.rightLen = cp - pattern.rhs;
+ *cp = '\0';
+
+ /*
+ * SYSV modifications happen through the whole
+ * string. Note the pattern is anchored at the end.
+ */
+ newStr = VarModify(str, VarSYSVMatch,
+ (ClientData)&pattern);
+
+ /*
+ * Restore the nulled characters
+ */
+ pattern.lhs[pattern.leftLen] = '=';
+ pattern.rhs[pattern.rightLen] = endc;
+ termc = endc;
+ } else {
+ Error ("Unknown modifier '%c'\n", *tstr);
+ for (cp = tstr+1;
+ *cp != ':' && *cp != endc && *cp != '\0';
+ cp++)
+ continue;
+ termc = *cp;
+ newStr = var_Error;
+ }
+ }
+ }
+ if (DEBUG(VAR)) {
+ printf("Result is \"%s\"\n", newStr);
+ }
+
+ if (*freePtr) {
+ free (str);
+ }
+ str = newStr;
+ if (str != var_Error) {
+ *freePtr = TRUE;
+ } else {
+ *freePtr = FALSE;
+ }
+ if (termc == '\0') {
+ Error("Unclosed variable specification for %s", v->name);
+ } else if (termc == ':') {
+ *cp++ = termc;
+ } else {
+ *cp = termc;
+ }
+ tstr = cp;
+ }
+ *lengthPtr = tstr - start + 1;
+ } else {
+ *lengthPtr = tstr - start + 1;
+ *tstr = endc;
+ }
+
+ if (v->flags & VAR_FROM_ENV) {
+ Boolean destroy = FALSE;
+
+ if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) {
+ destroy = TRUE;
+ } else {
+ /*
+ * Returning the value unmodified, so tell the caller to free
+ * the thing.
+ */
+ *freePtr = TRUE;
+ }
+ Buf_Destroy(v->val, destroy);
+ free((Address)v);
+ } else if (v->flags & VAR_JUNK) {
+ /*
+ * Perform any free'ing needed and set *freePtr to FALSE so the caller
+ * doesn't try to free a static pointer.
+ */
+ if (*freePtr) {
+ free(str);
+ }
+ *freePtr = FALSE;
+ Buf_Destroy(v->val, TRUE);
+ free((Address)v);
+ if (dynamic) {
+ str = emalloc(*lengthPtr + 1);
+ strncpy(str, start, *lengthPtr);
+ str[*lengthPtr] = '\0';
+ *freePtr = TRUE;
+ } else {
+ str = var_Error;
+ }
+ }
+ return (str);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Subst --
+ * Substitute for all variables in the given string in the given context
+ * If undefErr is TRUE, Parse_Error will be called when an undefined
+ * variable is encountered.
+ *
+ * Results:
+ * The resulting string.
+ *
+ * Side Effects:
+ * None. The old string must be freed by the caller
+ *-----------------------------------------------------------------------
+ */
+char *
+Var_Subst (var, str, ctxt, undefErr)
+ char *var; /* Named variable || NULL for all */
+ char *str; /* the string in which to substitute */
+ GNode *ctxt; /* the context wherein to find variables */
+ Boolean undefErr; /* TRUE if undefineds are an error */
+{
+ Buffer buf; /* Buffer for forming things */
+ char *val; /* Value to substitute for a variable */
+ int length; /* Length of the variable invocation */
+ Boolean doFree; /* Set true if val should be freed */
+ static Boolean errorReported; /* Set true if an error has already
+ * been reported to prevent a plethora
+ * of messages when recursing */
+
+ buf = Buf_Init (MAKE_BSIZE);
+ errorReported = FALSE;
+
+ while (*str) {
+ if (var == NULL && (*str == '$') && (str[1] == '$')) {
+ /*
+ * A dollar sign may be escaped either with another dollar sign.
+ * In such a case, we skip over the escape character and store the
+ * dollar sign into the buffer directly.
+ */
+ str++;
+ Buf_AddByte(buf, (Byte)*str);
+ str++;
+ } else if (*str != '$') {
+ /*
+ * Skip as many characters as possible -- either to the end of
+ * the string or to the next dollar sign (variable invocation).
+ */
+ char *cp;
+
+ for (cp = str++; *str != '$' && *str != '\0'; str++)
+ continue;
+ Buf_AddBytes(buf, str - cp, (Byte *)cp);
+ } else {
+ if (var != NULL) {
+ int expand;
+ for (;;) {
+ if (str[1] != '(' && str[1] != '{') {
+ if (str[1] != *var) {
+ Buf_AddBytes(buf, 2, (Byte *) str);
+ str += 2;
+ expand = FALSE;
+ }
+ else
+ expand = TRUE;
+ break;
+ }
+ else {
+ char *p;
+
+ /*
+ * Scan up to the end of the variable name.
+ */
+ for (p = &str[2]; *p &&
+ *p != ':' && *p != ')' && *p != '}'; p++)
+ if (*p == '$')
+ break;
+ /*
+ * A variable inside the variable. We cannot expand
+ * the external variable yet, so we try again with
+ * the nested one
+ */
+ if (*p == '$') {
+ Buf_AddBytes(buf, p - str, (Byte *) str);
+ str = p;
+ continue;
+ }
+
+ if (strncmp(var, str + 2, p - str - 2) != 0 ||
+ var[p - str - 2] != '\0') {
+ /*
+ * Not the variable we want to expand, scan
+ * until the next variable
+ */
+ for (;*p != '$' && *p != '\0'; p++)
+ continue;
+ Buf_AddBytes(buf, p - str, (Byte *) str);
+ str = p;
+ expand = FALSE;
+ }
+ else
+ expand = TRUE;
+ break;
+ }
+ }
+ if (!expand)
+ continue;
+ }
+
+ val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
+
+ /*
+ * When we come down here, val should either point to the
+ * value of this variable, suitably modified, or be NULL.
+ * Length should be the total length of the potential
+ * variable invocation (from $ to end character...)
+ */
+ if (val == var_Error || val == varNoError) {
+ /*
+ * If performing old-time variable substitution, skip over
+ * the variable and continue with the substitution. Otherwise,
+ * store the dollar sign and advance str so we continue with
+ * the string...
+ */
+ if (oldVars) {
+ str += length;
+ } else if (undefErr) {
+ /*
+ * If variable is undefined, complain and skip the
+ * variable. The complaint will stop us from doing anything
+ * when the file is parsed.
+ */
+ if (!errorReported) {
+ Parse_Error (PARSE_FATAL,
+ "Undefined variable \"%.*s\"",length,str);
+ }
+ str += length;
+ errorReported = TRUE;
+ } else {
+ Buf_AddByte (buf, (Byte)*str);
+ str += 1;
+ }
+ } else {
+ /*
+ * We've now got a variable structure to store in. But first,
+ * advance the string pointer.
+ */
+ str += length;
+
+ /*
+ * Copy all the characters from the variable value straight
+ * into the new string.
+ */
+ Buf_AddBytes (buf, strlen (val), (Byte *)val);
+ if (doFree) {
+ free ((Address)val);
+ }
+ }
+ }
+ }
+
+ Buf_AddByte (buf, '\0');
+ str = (char *)Buf_GetAll (buf, (int *)NULL);
+ Buf_Destroy (buf, FALSE);
+ return (str);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_GetTail --
+ * Return the tail from each of a list of words. Used to set the
+ * System V local variables.
+ *
+ * Results:
+ * The resulting string.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Var_GetTail(file)
+ char *file; /* Filename to modify */
+{
+ return(VarModify(file, VarTail, (ClientData)0));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_GetHead --
+ * Find the leading components of a (list of) filename(s).
+ * XXX: VarHead does not replace foo by ., as (sun) System V make
+ * does.
+ *
+ * Results:
+ * The leading components.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Var_GetHead(file)
+ char *file; /* Filename to manipulate */
+{
+ return(VarModify(file, VarHead, (ClientData)0));
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Init --
+ * Initialize the module
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * The VAR_CMD and VAR_GLOBAL contexts are created
+ *-----------------------------------------------------------------------
+ */
+void
+Var_Init ()
+{
+ VAR_GLOBAL = Targ_NewGN ("Global");
+ VAR_CMD = Targ_NewGN ("Command");
+ allVars = Lst_Init(FALSE);
+
+}
+
+
+void
+Var_End ()
+{
+ Lst_Destroy(allVars, VarDelete);
+}
+
+
+/****************** PRINT DEBUGGING INFO *****************/
+static int
+VarPrintVar (vp, dummy)
+ ClientData vp;
+ ClientData dummy;
+{
+ Var *v = (Var *) vp;
+ printf ("%-16s = %s\n", v->name, (char *) Buf_GetAll(v->val, (int *)NULL));
+ return (dummy ? 0 : 0);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * Var_Dump --
+ * print all variables in a context
+ *-----------------------------------------------------------------------
+ */
+void
+Var_Dump (ctxt)
+ GNode *ctxt;
+{
+ Lst_ForEach (ctxt->context, VarPrintVar, (ClientData) 0);
+}
diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile
new file mode 100644
index 0000000..728d038
--- /dev/null
+++ b/usr.bin/man/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= man
+SRCS= config.c man.c
+MAN1= man.0
+MAN5= man.conf.0
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/man/config.c b/usr.bin/man/config.c
new file mode 100644
index 0000000..1d875b5
--- /dev/null
+++ b/usr.bin/man/config.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)config.c 8.7 (Berkeley) 1/3/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "pathnames.h"
+
+struct _head head;
+
+/*
+ * config --
+ *
+ * Read the configuration file and build a doubly linked
+ * list that looks like:
+ *
+ * tag1 <-> record <-> record <-> record
+ * |
+ * tag2 <-> record <-> record <-> record
+ */
+void
+config(fname)
+ char *fname;
+{
+ TAG *tp;
+ ENTRY *ep;
+ FILE *cfp;
+ size_t len;
+ int lcnt;
+ char *p, *t;
+
+ if (fname == NULL)
+ fname = _PATH_MANCONF;
+ if ((cfp = fopen(fname, "r")) == NULL)
+ err(1, "%s", fname);
+ TAILQ_INIT(&head);
+ for (lcnt = 1; (p = fgetln(cfp, &len)) != NULL; ++lcnt) {
+ if (len == 1) /* Skip empty lines. */
+ continue;
+ if (p[len - 1] != '\n') { /* Skip corrupted lines. */
+ warnx("%s: line %d corrupted", fname, lcnt);
+ continue;
+ }
+ p[len - 1] = '\0'; /* Terminate the line. */
+
+ /* Skip leading space. */
+ for (; *p != '\0' && isspace(*p); ++p);
+ /* Skip empty/comment lines. */
+ if (*p == '\0' || *p == '#')
+ continue;
+ /* Find first token. */
+ for (t = p; *t && !isspace(*t); ++t);
+ if (*t == '\0') /* Need more than one token.*/
+ continue;
+ *t = '\0';
+
+ for (tp = head.tqh_first; /* Find any matching tag. */
+ tp != NULL && strcmp(p, tp->s); tp = tp->q.tqe_next);
+
+ if (tp == NULL) /* Create a new tag. */
+ tp = addlist(p);
+
+ /*
+ * Attach new records. The keyword _build takes the rest of
+ * the line as a single entity, everything else is white
+ * space separated. The reason we're not just using strtok(3)
+ * for all of the parsing is so we don't get caught if a line
+ * has only a single token on it.
+ */
+ if (!strcmp(p, "_build")) {
+ while (*++t && isspace(*t));
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(t)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&tp->list, ep, q);
+ } else for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) {
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(p)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&tp->list, ep, q);
+ }
+ }
+}
+
+/*
+ * addlist --
+ * Add a tag to the list.
+ */
+TAG *
+addlist(name)
+ char *name;
+{
+ TAG *tp;
+
+ if ((tp = calloc(1, sizeof(TAG))) == NULL ||
+ (tp->s = strdup(name)) == NULL)
+ err(1, NULL);
+ TAILQ_INIT(&tp->list);
+ TAILQ_INSERT_TAIL(&head, tp, q);
+ return (tp);
+}
+
+/*
+ * getlist --
+ * Return the linked list of entries for a tag if it exists.
+ */
+TAG *
+getlist(name)
+ char *name;
+{
+ TAG *tp;
+
+ for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next)
+ if (!strcmp(name, tp->s))
+ return (tp);
+ return (NULL);
+}
+
+void
+debug(l)
+ char *l;
+{
+ TAG *tp;
+ ENTRY *ep;
+
+ (void)printf("%s ===============\n", l);
+ for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next) {
+ printf("%s\n", tp->s);
+ for (ep = tp->list.tqh_first; ep != NULL; ep = ep->q.tqe_next)
+ printf("\t%s\n", ep->s);
+ }
+}
diff --git a/usr.bin/man/config.h b/usr.bin/man/config.h
new file mode 100644
index 0000000..52ea969
--- /dev/null
+++ b/usr.bin/man/config.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.h 8.4 (Berkeley) 12/18/93
+ */
+
+typedef struct _tag {
+ TAILQ_ENTRY(_tag) q; /* Queue of tags. */
+
+ TAILQ_HEAD(tqh, _entry) list; /* Queue of entries. */
+ char *s; /* Associated string. */
+ size_t len; /* Length of 's'. */
+} TAG;
+typedef struct _entry {
+ TAILQ_ENTRY(_entry) q; /* Queue of entries. */
+
+ char *s; /* Associated string. */
+ size_t len; /* Length of 's'. */
+} ENTRY;
+
+TAILQ_HEAD(_head, _tag);
+extern struct _head head;
+
+TAG *addlist __P((char *));
+void config __P((char *));
+void debug __P((char *));
+TAG *getlist __P((char *));
diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1
new file mode 100644
index 0000000..081e204
--- /dev/null
+++ b/usr.bin/man/man.1
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)man.1 8.2 (Berkeley) 1/2/94
+.\"
+.Dd January 2, 1994
+.Dt MAN 1
+.Os BSD 4
+.Sh NAME
+.Nm man
+.Nd display the on-line manual pages
+.Sh SYNOPSIS
+.Nm man
+.Op Fl achw
+.Op Fl C Ar file
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Op Ar section
+.Ar name Ar ...
+.Sh DESCRIPTION
+The
+.Nm man
+utility
+displays the
+.Bx
+manual pages entitled
+.Ar name .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Display all of the manual pages for a specified
+.Ar section
+and
+.Ar name
+combination.
+(Normally, only the first manual page found is displayed.)
+.It Fl C
+Use the specified
+.Ar file
+instead of the default configuration file.
+This permits users to configure their own manual environment.
+See
+.Xr man.conf 5
+for a description of the contents of this file.
+.It Fl c
+Copy the manual page to the standard output instead of using
+.Xr more 1
+to paginate it.
+This is done by default if the standard output is not a terminal device.
+.It Fl h
+Display only the
+.Dq Tn SYNOPSIS
+lines of the requested manual pages.
+.It Fl M
+Override the list of standard directories which
+.Nm man
+searches for manual pages.
+The supplied
+.Ar path
+must be a colon (``:'') separated list of directories.
+This search path may also be set using the environment variable
+.Ev MANPATH .
+The subdirectories to be searched, and their search order,
+is specified by the ``_subdir'' line in the
+.Nm man
+configuration file.
+.It Fl m
+Augment the list of standard directories which
+.Nm man
+searches for manual pages.
+The supplied
+.Ar path
+must be a colon (``:'') separated list of directories.
+These directories will be searched before the standard directories or
+the directories specified using the
+.Fl M
+option or the
+.Ev MANPATH
+environment variable.
+The subdirectories to be searched, and their search order,
+is specified by the ``_subdir'' line in the
+.Nm man
+configuration file.
+.It Fl w
+List the pathnames of the manual pages which
+.Nm man
+would display for the specified
+.Ar section
+and
+.Ar name
+combination.
+.El
+.Pp
+The optional
+.Ar section
+argument restricts the directories that
+.Nm man
+will search.
+The
+.Nm man
+configuration file (see
+.Xr man.conf 5 )
+specifies the possible
+.Ar section
+values that are currently available.
+If only a single argument is specified or if the first argument is
+not a valid section,
+.Nm man
+assumes that the argument is the name of a manual page to be displayed.
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATHX
+.It Ev MACHINE
+As some manual pages are intended only for specific architectures,
+.Nm man
+searches any subdirectories,
+with the same name as the current architecture,
+in every directory which it searches.
+Machine specific areas are checked before general areas.
+The current machine type may be overridden by setting the environment
+variable
+.Ev MACHINE
+to the name of a specific architecture.
+.It Ev MANPATH
+The standard search path used by
+.Nm man
+may be overridden by specifying a path in the
+.Ev MANPATH
+environment
+variable.
+The format of the path is a colon (``:'') separated list of directories.
+The subdirectories to be searched as well as their search order
+is specified by the ``_subdir'' line in the
+.Nm man
+configuration file.
+.It Ev PAGER
+Any value of the environment variable
+.Ev PAGER
+will be used instead of the standard pagination program,
+.Xr more 1 .
+.El
+.Sh FILES
+.Bl -tag -width /etc/man.conf -compact
+.It Pa /etc/man.conf
+default man configuration file.
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1 ,
+.Xr man.conf 5
+.Sh BUGS
+The on-line manual pages are, by necessity, forgiving toward stupid
+display devices, causing a few manual pages to not as nicely formatted
+as their typeset counterparts.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/man/man.c b/usr.bin/man/man.c
new file mode 100644
index 0000000..a320e31
--- /dev/null
+++ b/usr.bin/man/man.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)man.c 8.16 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <glob.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "pathnames.h"
+
+int f_all, f_where;
+
+static void build_page __P((char *, char **));
+static void cat __P((char *));
+static char *check_pager __P((char *));
+static int cleanup __P((void));
+static void how __P((char *));
+static void jump __P((char **, char *, char *));
+static int manual __P((char *, TAG *, glob_t *));
+static void onsig __P((int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ TAG *defp, *defnewp, *section, *sectnewp, *subp;
+ ENTRY *e_defp, *e_sectp, *e_subp, *ep;
+ glob_t pg;
+ size_t len;
+ int ch, f_cat, f_how, found;
+ char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *slashp;
+ char *conffile, buf[MAXPATHLEN * 2];
+
+ f_cat = f_how = 0;
+ conffile = p_add = p_path = NULL;
+ while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:w")) != EOF)
+ switch (ch) {
+ case 'a':
+ f_all = 1;
+ break;
+ case 'C':
+ conffile = optarg;
+ break;
+ case 'c':
+ case '-': /* Deprecated. */
+ f_cat = 1;
+ break;
+ case 'h':
+ f_how = 1;
+ break;
+ case 'm':
+ p_add = optarg;
+ break;
+ case 'M':
+ case 'P': /* Backward compatibility. */
+ p_path = optarg;
+ break;
+ /*
+ * The -f and -k options are backward compatible,
+ * undocumented ways of calling whatis(1) and apropos(1).
+ */
+ case 'f':
+ jump(argv, "-f", "whatis");
+ /* NOTREACHED */
+ case 'k':
+ jump(argv, "-k", "apropos");
+ /* NOTREACHED */
+ case 'w':
+ f_all = f_where = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ if (!f_cat && !f_how && !f_where)
+ if (!isatty(1))
+ f_cat = 1;
+ else if ((pager = getenv("PAGER")) != NULL)
+ pager = check_pager(pager);
+ else
+ pager = _PATH_PAGER;
+
+ /* Read the configuration file. */
+ config(conffile);
+
+ /* Get the machine type. */
+ if ((machine = getenv("MACHINE")) == NULL)
+ machine = MACHINE;
+
+ /* If there's no _default list, create an empty one. */
+ if ((defp = getlist("_default")) == NULL)
+ defp = addlist("_default");
+
+ /*
+ * 1: If the user specified a MANPATH variable, or set the -M
+ * option, we replace the _default list with the user's list,
+ * appending the entries in the _subdir list and the machine.
+ */
+ if (p_path == NULL)
+ p_path = getenv("MANPATH");
+ if (p_path != NULL) {
+ while ((e_defp = defp->list.tqh_first) != NULL) {
+ free(e_defp->s);
+ TAILQ_REMOVE(&defp->list, e_defp, q);
+ }
+ for (p = strtok(p_path, ":");
+ p != NULL; p = strtok(NULL, ":")) {
+ slashp = p[strlen(p) - 1] == '/' ? "" : "/";
+ e_subp = (subp = getlist("_subdir")) == NULL ?
+ NULL : subp->list.tqh_first;
+ for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ p, slashp, e_subp->s, machine);
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(buf)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&defp->list, ep, q);
+ }
+ }
+ }
+
+ /*
+ * 2: If the user did not specify MANPATH, -M or a section, rewrite
+ * the _default list to include the _subdir list and the machine.
+ */
+ if (argv[1] == NULL)
+ section = NULL;
+ else if ((section = getlist(*argv)) != NULL)
+ ++argv;
+ if (p_path == NULL && section == NULL) {
+ defnewp = addlist("_default_new");
+ e_defp =
+ defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first;
+ for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) {
+ slashp =
+ e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/";
+ e_subp = (subp = getlist("_subdir")) == NULL ?
+ NULL : subp->list.tqh_first;
+ for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ e_defp->s, slashp, e_subp->s, machine);
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(buf)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&defnewp->list, ep, q);
+ }
+ }
+ defp = getlist("_default");
+ while ((e_defp = defp->list.tqh_first) != NULL) {
+ free(e_defp->s);
+ TAILQ_REMOVE(&defp->list, e_defp, q);
+ }
+ free(defp->s);
+ TAILQ_REMOVE(&head, defp, q);
+ defnewp = getlist("_default_new");
+ free(defnewp->s);
+ defnewp->s = "_default";
+ defp = defnewp;
+ }
+
+ /*
+ * 3: If the user set the -m option, insert the user's list before
+ * whatever list we have, again appending the _subdir list and
+ * the machine.
+ */
+ if (p_add != NULL)
+ for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) {
+ slashp = p[strlen(p) - 1] == '/' ? "" : "/";
+ e_subp = (subp = getlist("_subdir")) == NULL ?
+ NULL : subp->list.tqh_first;
+ for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ p, slashp, e_subp->s, machine);
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(buf)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_HEAD(&defp->list, ep, q);
+ }
+ }
+
+ /*
+ * 4: If none of MANPATH, -M, or -m were specified, and a section was,
+ * rewrite the section's paths (if they have a trailing slash) to
+ * append the _subdir list and the machine. This then becomes the
+ * _default list.
+ */
+ if (p_path == NULL && p_add == NULL && section != NULL) {
+ sectnewp = addlist("_section_new");
+ for (e_sectp = section->list.tqh_first;
+ e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) {
+ if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') {
+ (void)snprintf(buf, sizeof(buf),
+ "%s{/%s,}", e_sectp->s, machine);
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(buf)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&sectnewp->list, ep, q);
+ continue;
+ }
+ e_subp = (subp = getlist("_subdir")) == NULL ?
+ NULL : subp->list.tqh_first;
+ for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}",
+ e_sectp->s, e_subp->s, machine);
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(buf)) == NULL)
+ err(1, NULL);
+ TAILQ_INSERT_TAIL(&sectnewp->list, ep, q);
+ }
+ }
+ sectnewp->s = section->s;
+ defp = sectnewp;
+ TAILQ_REMOVE(&head, section, q);
+ }
+
+ /*
+ * 5: Search for the files. Set up an interrupt handler, so the
+ * temporary files go away.
+ */
+ (void)signal(SIGINT, onsig);
+ (void)signal(SIGHUP, onsig);
+
+ memset(&pg, 0, sizeof(pg));
+ for (found = 0; *argv; ++argv)
+ if (manual(*argv, defp, &pg))
+ found = 1;
+
+ /* 6: If nothing found, we're done. */
+ if (!found) {
+ (void)cleanup();
+ exit (1);
+ }
+
+ /* 7: If it's simple, display it fast. */
+ if (f_cat) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ cat(*ap);
+ }
+ exit (cleanup());
+ }
+ if (f_how) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ how(*ap);
+ }
+ exit(cleanup());
+ }
+ if (f_where) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ (void)printf("%s\n", *ap);
+ }
+ exit(cleanup());
+ }
+
+ /*
+ * 8: We display things in a single command; build a list of things
+ * to display.
+ */
+ for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ len += strlen(*ap) + 1;
+ }
+ if ((cmd = malloc(len)) == NULL) {
+ warn(NULL);
+ (void)cleanup();
+ exit(1);
+ }
+ p = cmd;
+ len = strlen(pager);
+ memmove(p, pager, len);
+ p += len;
+ *p++ = ' ';
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ len = strlen(*ap);
+ memmove(p, *ap, len);
+ p += len;
+ *p++ = ' ';
+ }
+ *p = '\0';
+
+ /* Use system(3) in case someone's pager is "pager arg1 arg2". */
+ (void)system(cmd);
+
+ exit(cleanup());
+}
+
+/*
+ * manual --
+ * Search the manuals for the pages.
+ */
+static int
+manual(page, tag, pg)
+ char *page;
+ TAG *tag;
+ glob_t *pg;
+{
+ ENTRY *ep, *e_sufp, *e_tag;
+ TAG *missp, *sufp;
+ int anyfound, cnt, found;
+ char *p, buf[128];
+
+ anyfound = 0;
+ buf[0] = '*';
+
+ /* For each element in the list... */
+ e_tag = tag == NULL ? NULL : tag->list.tqh_first;
+ for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page);
+ if (glob(buf,
+ GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE,
+ NULL, pg)) {
+ warn("globbing");
+ (void)cleanup();
+ exit(1);
+ }
+ if (pg->gl_matchc == 0)
+ continue;
+
+ /* Find out if it's really a man page. */
+ for (cnt = pg->gl_pathc - pg->gl_matchc;
+ cnt < pg->gl_pathc; ++cnt) {
+
+ /*
+ * Try the _suffix key words first.
+ *
+ * XXX
+ * Older versions of man.conf didn't have the suffix
+ * key words, it was assumed that everything was a .0.
+ * We just test for .0 first, it's fast and probably
+ * going to hit.
+ */
+ (void)snprintf(buf, sizeof(buf), "*/%s.0", page);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
+ goto next;
+
+ e_sufp = (sufp = getlist("_suffix")) == NULL ?
+ NULL : sufp->list.tqh_first;
+ for (found = 0;
+ e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) {
+ (void)snprintf(buf,
+ sizeof(buf), "*/%s%s", page, e_sufp->s);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ goto next;
+
+ /* Try the _build key words next. */
+ e_sufp = (sufp = getlist("_build")) == NULL ?
+ NULL : sufp->list.tqh_first;
+ for (found = 0;
+ e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) {
+ for (p = e_sufp->s;
+ *p != '\0' && !isspace(*p); ++p);
+ if (*p == '\0')
+ continue;
+ *p = '\0';
+ (void)snprintf(buf,
+ sizeof(buf), "*/%s%s", page, e_sufp->s);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
+ if (!f_where)
+ build_page(p + 1,
+ &pg->gl_pathv[cnt]);
+ *p = ' ';
+ found = 1;
+ break;
+ }
+ *p = ' ';
+ }
+ if (found) {
+next: anyfound = 1;
+ if (!f_all) {
+ /* Delete any other matches. */
+ while (++cnt< pg->gl_pathc)
+ pg->gl_pathv[cnt] = "";
+ break;
+ }
+ continue;
+ }
+
+ /* It's not a man page, forget about it. */
+ pg->gl_pathv[cnt] = "";
+ }
+
+ if (anyfound && !f_all)
+ break;
+ }
+
+ /* If not found, enter onto the missing list. */
+ if (!anyfound) {
+ if ((missp = getlist("_missing")) == NULL)
+ missp = addlist("_missing");
+ if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+ (ep->s = strdup(page)) == NULL) {
+ warn(NULL);
+ (void)cleanup();
+ exit(1);
+ }
+ TAILQ_INSERT_TAIL(&missp->list, ep, q);
+ }
+ return (anyfound);
+}
+
+/*
+ * build_page --
+ * Build a man page for display.
+ */
+static void
+build_page(fmt, pathp)
+ char *fmt, **pathp;
+{
+ static int warned;
+ ENTRY *ep;
+ TAG *intmpp;
+ int fd;
+ char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)];
+
+ /* Let the user know this may take awhile. */
+ if (!warned) {
+ warned = 1;
+ warnx("Formatting manual page...");
+ }
+
+ /* Add a remove-when-done list. */
+ if ((intmpp = getlist("_intmp")) == NULL)
+ intmpp = addlist("_intmp");
+
+ /* Move to the printf(3) format string. */
+ for (; *fmt && isspace(*fmt); ++fmt);
+
+ /*
+ * Get a temporary file and build a version of the file
+ * to display. Replace the old file name with the new one.
+ */
+ (void)strcpy(tpath, _PATH_TMP);
+ if ((fd = mkstemp(tpath)) == -1) {
+ warn("%s", tpath);
+ (void)cleanup();
+ exit(1);
+ }
+ (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
+ (void)snprintf(cmd, sizeof(cmd), buf, *pathp);
+ (void)system(cmd);
+ (void)close(fd);
+ if ((*pathp = strdup(tpath)) == NULL) {
+ warn(NULL);
+ (void)cleanup();
+ exit(1);
+ }
+
+ /* Link the built file into the remove-when-done list. */
+ if ((ep = malloc(sizeof(ENTRY))) == NULL) {
+ warn(NULL);
+ (void)cleanup();
+ exit(1);
+ }
+ ep->s = *pathp;
+ TAILQ_INSERT_TAIL(&intmpp->list, ep, q);
+}
+
+/*
+ * how --
+ * display how information
+ */
+static void
+how(fname)
+ char *fname;
+{
+ FILE *fp;
+
+ int lcnt, print;
+ char *p, buf[256];
+
+ if (!(fp = fopen(fname, "r"))) {
+ warn("%s", fname);
+ (void)cleanup();
+ exit (1);
+ }
+#define S1 "SYNOPSIS"
+#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
+#define D1 "DESCRIPTION"
+#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
+ for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
+ if (!strncmp(buf, S1, sizeof(S1) - 1) ||
+ !strncmp(buf, S2, sizeof(S2) - 1)) {
+ print = 1;
+ continue;
+ } else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
+ !strncmp(buf, D2, sizeof(D2) - 1))
+ return;
+ if (!print)
+ continue;
+ if (*buf == '\n')
+ ++lcnt;
+ else {
+ for(; lcnt; --lcnt)
+ (void)putchar('\n');
+ for (p = buf; isspace(*p); ++p);
+ (void)fputs(p, stdout);
+ }
+ }
+ (void)fclose(fp);
+}
+
+/*
+ * cat --
+ * cat out the file
+ */
+static void
+cat(fname)
+ char *fname;
+{
+ int fd, n;
+ char buf[2048];
+
+ if ((fd = open(fname, O_RDONLY, 0)) < 0) {
+ warn("%s", fname);
+ (void)cleanup();
+ exit(1);
+ }
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ if (write(STDOUT_FILENO, buf, n) != n) {
+ warn("write");
+ (void)cleanup();
+ exit (1);
+ }
+ if (n == -1) {
+ warn("read");
+ (void)cleanup();
+ exit(1);
+ }
+ (void)close(fd);
+}
+
+/*
+ * check_pager --
+ * check the user supplied page information
+ */
+static char *
+check_pager(name)
+ char *name;
+{
+ char *p, *save;
+
+ /*
+ * if the user uses "more", we make it "more -s"; watch out for
+ * PAGER = "mypager /usr/ucb/more"
+ */
+ for (p = name; *p && !isspace(*p); ++p);
+ for (; p > name && *p != '/'; --p);
+ if (p != name)
+ ++p;
+
+ /* make sure it's "more", not "morex" */
+ if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
+ save = name;
+ /* allocate space to add the "-s" */
+ if (!(name =
+ malloc((u_int)(strlen(save) + sizeof("-s") + 1))))
+ err(1, NULL);
+ (void)sprintf(name, "%s %s", save, "-s");
+ }
+ return(name);
+}
+
+/*
+ * jump --
+ * strip out flag argument and jump
+ */
+static void
+jump(argv, flag, name)
+ char **argv, *flag, *name;
+{
+ char **arg;
+
+ argv[0] = name;
+ for (arg = argv + 1; *arg; ++arg)
+ if (!strcmp(*arg, flag))
+ break;
+ for (; *arg; ++arg)
+ arg[0] = arg[1];
+ execvp(name, argv);
+ (void)fprintf(stderr, "%s: Command not found.\n", name);
+ exit(1);
+}
+
+/*
+ * onsig --
+ * If signaled, delete the temporary files.
+ */
+static void
+onsig(signo)
+ int signo;
+{
+ (void)cleanup();
+
+ (void)signal(signo, SIG_DFL);
+ (void)kill(getpid(), signo);
+
+ /* NOTREACHED */
+ exit (1);
+}
+
+/*
+ * cleanup --
+ * Clean up temporary files, show any error messages.
+ */
+static int
+cleanup()
+{
+ TAG *intmpp, *missp;
+ ENTRY *ep;
+ int rval;
+
+ rval = 0;
+ ep = (missp = getlist("_missing")) == NULL ?
+ NULL : missp->list.tqh_first;
+ if (ep != NULL)
+ for (; ep != NULL; ep = ep->q.tqe_next) {
+ warnx("no entry for %s in the manual.", ep->s);
+ rval = 1;
+ }
+
+ ep = (intmpp = getlist("_intmp")) == NULL ?
+ NULL : intmpp->list.tqh_first;
+ for (; ep != NULL; ep = ep->q.tqe_next)
+ (void)unlink(ep->s);
+ return (rval);
+}
+
+/*
+ * usage --
+ * print usage message and die
+ */
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n");
+ exit(1);
+}
diff --git a/usr.bin/man/man.conf b/usr.bin/man/man.conf
new file mode 100644
index 0000000..9faad52
--- /dev/null
+++ b/usr.bin/man/man.conf
@@ -0,0 +1,46 @@
+# Sheer, raging paranoia...
+_version BSD.2
+
+# The whatis/apropos database.
+_whatdb /usr/share/man/whatis.db
+
+# Subdirectories for paths ending in '/', IN SEARCH ORDER.
+_subdir cat{1,8,6,2,3,4,5,7,3f}
+
+# Files typed by suffix and their commands.
+# Note the order, .Z must come after .[1-9].Z, or it will match first.
+_suffix .0
+_build .[1-9] /usr/bin/nroff -man %s
+_build .[1-9].Z /usr/bin/zcat %s | /usr/bin/nroff -man
+_build .Z /usr/bin/zcat %s
+_build .0.Z /usr/bin/zcat %s
+_build .gz /usr/contrib/bin/gunzip %s
+_build .z /usr/contrib/bin/gunzip %s
+_build .nr /usr/bin/nroff -man %s
+
+# Sections and their directories.
+# All paths ending in '/' are the equivalent of entries specifying that
+# directory with all of the subdirectories listed for the keyword _subdir.
+
+# default
+_default /usr/{share,X11,contrib,local}/{man,man/old}/
+
+# Other sections that represent complete man subdirectories.
+X11 /usr/X11R4/man/
+X11R4 /usr/X11R4/man/
+contrib /usr/contrib/man/
+local /usr/local/man/
+new /usr/contrib/man/
+old /usr/share/man/old/
+
+# Specific section/directory combinations.
+1 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat1
+2 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat2
+3 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat3
+3F /usr/share/man/cat3f
+3f /usr/share/man/cat3f
+4 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat4
+5 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat5
+6 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat6
+7 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat7
+8 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat8
diff --git a/usr.bin/man/man.conf.5 b/usr.bin/man/man.conf.5
new file mode 100644
index 0000000..353a302
--- /dev/null
+++ b/usr.bin/man/man.conf.5
@@ -0,0 +1,195 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)man.conf.5 8.5 (Berkeley) 1/2/94
+.\"
+.Dd January 2, 1994
+.Dt MAN.CONF 5
+.Os
+.Sh NAME
+.Nm man.conf
+.Nd configuration file for
+.Xr man 1
+.Sh DESCRIPTION
+The
+.Xr man 1 ,
+.Xr apropos 1 ,
+and
+.Xr whatis 1
+commands
+search for manual pages or their database files as specified by the
+.Nm man.conf
+file.
+Manual pages are normally expected to be preformatted (see
+.Xr nroff 1 )
+and named with a trailing ``.0''.
+.Pp
+The
+.Nm man.conf
+file contains two types of lines.
+.Pp
+The first type of line is a ``section'' line, which contains a
+section name followed by one or more directory paths.
+The directory paths may contain the normal shell globbing characters,
+including curly braces (``{}''); to escape a shell globbing character,
+precede it with a backslash (``\e'').
+Lines in this format specify that manual pages for the section
+may be found in the following directories.
+.Pp
+Directories named with a trailing slash character (``/'') are expected
+to contain subdirectories of manual pages, (see the keyword ``_subdir''
+below) instead of manual pages.
+These subdirectories are searched instead of the directory.
+.Pp
+Before searching any directory for a manual page, the
+.Xr man 1
+command always searches the subdirectory with the same name
+as the current machine type, if it exists.
+No specification of these subdirectories is necessary in the
+.Nm man.conf
+file.
+.Pp
+Section names are unrestricted except for the reserved words specified
+below; in general, you should avoid anything with a leading underscore
+(``_'') to avoid future incompatibilities.
+.Pp
+The section named ``_default'' is the list of directories that will
+be searched if no section is specified by the user.
+.Pp
+The second type of line is preceded with a ``keyword''.
+The possible keywords and their meanings are as follows:
+.Pp
+.Bl -tag -width "_version"
+.It _build
+Man file names, regardless of their format, are expected to end in
+a ``.*'' pattern, i.e. a ``.'' followed by some suffix.
+The first field of a _build line lists a suffix which indicates
+files which need to be reformated or manipulated in some way before
+being displayed to the user.
+The suffix may contain the normal shell globbing characters (NOT
+including curly braces (``{}'')).
+The rest of the line must be a shell command line, the standard
+output of which is the manual page in a format which may be directly
+displayed to the user.
+Any occurrences of the string ``%s'' in the shell command line will
+be replaced by the name of the file which is being reformatted.
+.It _subdir
+The list (in search order) of subdirectories which will be searched in
+any directory named with a trailing slash (``/'') character.
+This list is also used when a path is specified to the
+.Xr man 1
+utility by the user, using the
+.Ev MANPATH
+environment variable or the
+.Fl M
+and
+.Fl m
+options.
+.It _suffix
+Man file names, regardless of their format are expected to end in
+a ``.*'' pattern, i.e. a ``.'' followed by some suffix.
+Each field of a _suffix line is a suffix which indicates
+files which do not need to be reformatted or manipulated
+in any way, but which may be directly displayed to the user.
+Each suffix may contain the normal shell globbing characters (NOT
+including curly braces (``{}'')).
+.It _version
+The version of the configuration file.
+.It _whatdb
+The full pathname (not just a directory path) for a database to be used
+by the
+.Xr apropos 1
+and
+.Xr whatis 1
+commands.
+.El
+.Pp
+Multiple specifications for all types of lines are cumulative and the
+entries are used in the order listed in the file; multiple entries may
+be listed per line, as well.
+.Pp
+Empty lines or lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Sh EXAMPLES
+Given the following
+.Nm man.conf
+file:
+.Bd -literal -offset indent
+_version BSD.2
+_subdir cat[123]
+_suffix .0
+_build .[1-9] nroff -man %s
+_build .tbl tbl %s | nroff -man
+_default /usr/share/man/
+sect3 /usr/share/man/{old/,}cat3
+.Ed
+.Pp
+By default, the command
+.Dq Li man mktemp
+will search for
+``mktemp.<any_digit>'' and ``mktemp.tbl''
+in the directories
+.Dq Pa /usr/share/man/cat1 ,
+.Dq Pa /usr/share/man/cat2 ,
+and
+.Dq Pa /usr/share/man/cat3 .
+If on a machine of type ``vax'', the subdirectory ``vax'' in each
+directory would be searched as well, before the directory was
+searched.
+.Pp
+If ``mktemp.tbl'' was found first, the command
+.Dq Li tbl <manual page> | nroff -man
+would be run to build a man page for display to the user.
+.Pp
+The command
+.Dq Li man sect3 mktemp
+would search the directories
+.Dq Pa /usr/share/man/old/cat3
+and
+.Dq Pa /usr/share/man/cat3 ,
+in that order, for
+the mktemp manual page.
+If a subdirectory with the same name as the current machine type
+existed in any of them, it would be searched as well, before each
+of them were searched.
+.Sh FILES
+.Bl -tag -width /etc/man.conf -compact
+.It Pa /etc/man.conf
+Standard manual directory search path.
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr machine 1 ,
+.Xr man 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1 ,
+.Xr fnmatch 3 ,
+.Xr glob 3
diff --git a/usr.bin/man/pathnames.h b/usr.bin/man/pathnames.h
new file mode 100644
index 0000000..17284fe
--- /dev/null
+++ b/usr.bin/man/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.3 (Berkeley) 1/2/94
+ */
+
+#define _PATH_MANCONF "/etc/man.conf"
+#define _PATH_PAGER "/usr/bin/more -s"
+#define _PATH_TMP "/tmp/man.XXXXXX"
+#define _PATH_WHATIS "whatis.db"
diff --git a/usr.bin/mesg/Makefile b/usr.bin/mesg/Makefile
new file mode 100644
index 0000000..8c54356
--- /dev/null
+++ b/usr.bin/mesg/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mesg
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mesg/mesg.1 b/usr.bin/mesg/mesg.1
new file mode 100644
index 0000000..70e29a9
--- /dev/null
+++ b/usr.bin/mesg/mesg.1
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mesg.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MESG 1
+.Os
+.Sh NAME
+.Nm mesg
+.Nd display (do not display) messages from other users
+.Sh SYNOPSIS
+.Nm mesg
+.Op Cm n | Cm y
+.Sh DESCRIPTION
+The
+.Nm mesg
+utility is invoked by a users to control write access others
+have to the terminal device associated with the standard error
+output.
+Write access is allowed by default, and programs such as
+.Xr talk 1
+and
+.Xr write 1
+may display messages on the terminal.
+.Pp
+Options available:
+.Bl -tag -width flag
+.It Cm n
+Disallows messages.
+.It Cm y
+Permits messages to be displayed.
+.El
+.Pp
+If no arguments are given,
+.Nm mesg
+displays the present message status to the standard error output.
+.Pp
+The
+.Nm mesg
+utility exits with one of the following values:
+.Bl -tag -width flag -compact -offset indent
+.Pp
+.It Li "\ 0"
+Messages are allowed.
+.It Li "\ 1"
+Messages are not allowed.
+.It Li "\>1"
+An error has occurred.
+.El
+.Sh FILES
+.Bl -tag -width /dev/[pt]ty[pq]? -compact
+.It Pa /dev/[pt]ty[pq]?
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr talk 1 ,
+.Xr write 1
+.Sh HISTORY
+A
+.Nm mesg
+command appeared in
+.At v6 .
diff --git a/usr.bin/mesg/mesg.c b/usr.bin/mesg/mesg.c
new file mode 100644
index 0000000..ad666f7
--- /dev/null
+++ b/usr.bin/mesg/mesg.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mesg.c 8.2 (Berkeley) 1/21/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ char *tty;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch (ch) {
+ case '?':
+ default:
+ goto usage;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((tty = ttyname(STDERR_FILENO)) == NULL)
+ err(1, "ttyname");
+ if (stat(tty, &sb) < 0)
+ err(1, "%s", tty);
+
+ if (*argv == NULL) {
+ if (sb.st_mode & S_IWGRP) {
+ (void)fprintf(stderr, "is y\n");
+ exit(0);
+ }
+ (void)fprintf(stderr, "is n\n");
+ exit(1);
+ }
+
+ switch (*argv[0]) {
+ case 'y':
+ if (chmod(tty, sb.st_mode | S_IWGRP) < 0)
+ err(1, "%s", tty);
+ exit(0);
+ case 'n':
+ if (chmod(tty, sb.st_mode & ~S_IWGRP) < 0)
+ err(1, "%s", tty);
+ exit(1);
+ }
+
+usage: (void)fprintf(stderr, "usage: mesg [y | n]\n");
+ exit(2);
+}
diff --git a/usr.bin/mk_cmds/Makefile b/usr.bin/mk_cmds/Makefile
new file mode 100644
index 0000000..a240c05
--- /dev/null
+++ b/usr.bin/mk_cmds/Makefile
@@ -0,0 +1,16 @@
+# $Id$
+
+PROG= mk_cmds
+#
+# NB: ct.c must come before cmd_tbl.c so that y.tab.h will be generated.
+#
+SRCS= mk_cmds.c options.c utils.c ct.c cmd_tbl.c
+CFLAGS+= -I. -I${.CURDIR}/../../lib/libss -DIN_MK_CMDS
+LFLAGS= -l
+CLEANFILES+= y.tab.c y.tab.h lex.yy.c cmd_tbl.c ct.c
+NOMAN= # XXX
+
+LDADD+= -ll
+DPADD+= ${LIBL}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mk_cmds/cmd_tbl.l b/usr.bin/mk_cmds/cmd_tbl.l
new file mode 100644
index 0000000..15b31c6
--- /dev/null
+++ b/usr.bin/mk_cmds/cmd_tbl.l
@@ -0,0 +1,79 @@
+%{
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board.
+ *
+ * For copyright info, see copyright.h.
+ */
+
+#include <string.h>
+#include "y.tab.h"
+#include "copyright.h"
+
+extern char *last_token, *ds();
+
+static l_command_table()
+{
+ last_token = "command_table";
+ return COMMAND_TABLE;
+}
+
+static l_request()
+{
+ last_token = "request";
+ return REQUEST;
+}
+
+static l_unimplemented()
+{
+ last_token = "unimplemented";
+ return UNIMPLEMENTED;
+}
+
+static l_end()
+{
+ last_token = "end";
+ return END;
+}
+
+static l_quoted_string()
+{
+ register char *p;
+ yylval.dynstr = ds(yytext+1);
+ if (p=rindex(yylval.dynstr, '"'))
+ *p='\0';
+ last_token = ds(yylval.dynstr);
+ return STRING;
+}
+
+static l_string()
+{
+ yylval.dynstr = ds(yytext);
+ last_token = ds(yylval.dynstr);
+ return STRING;
+}
+
+
+%}
+
+N [0-9]
+PC [^\"]
+AN [A-Z_a-z0-9]
+%%
+
+command_table return l_command_table();
+request return l_request();
+unimplemented return l_unimplemented();
+end return l_end();
+
+[\t\n ] ;
+
+\"{PC}*\" return l_quoted_string();
+
+{AN}* return l_string();
+
+#.*\n ;
+
+. return (*yytext);
+
+%%
+
diff --git a/usr.bin/mk_cmds/copyright.h b/usr.bin/mk_cmds/copyright.h
new file mode 100644
index 0000000..e0d1572
--- /dev/null
+++ b/usr.bin/mk_cmds/copyright.h
@@ -0,0 +1,19 @@
+/*
+
+Copyright 1987, 1989 by the Student Information Processing Board
+ of the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+*/
+
diff --git a/usr.bin/mk_cmds/ct.y b/usr.bin/mk_cmds/ct.y
new file mode 100644
index 0000000..2c62d4a
--- /dev/null
+++ b/usr.bin/mk_cmds/ct.y
@@ -0,0 +1,77 @@
+%{
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright info, see copyright.h.
+ */
+#include <stdio.h>
+#include "copyright.h"
+
+char *str_concat3(), *ds(), *generate_rqte(), *malloc(), *realloc(), *quote();
+long flag_value();
+char *last_token = (char *)NULL;
+FILE *output_file;
+long gensym_n = 0;
+
+%}
+%union {
+ char *dynstr;
+ long flags;
+}
+
+%token COMMAND_TABLE REQUEST UNKNOWN UNIMPLEMENTED END
+%token <dynstr> STRING
+%token <dynstr> FLAGNAME
+%type <dynstr> namelist header request_list
+%type <dynstr> request_entry
+%type <flags> flag_list options
+%left OPTIONS
+%{
+#include "ss.h"
+%}
+%start command_table
+%%
+command_table : header request_list END ';'
+ { write_ct($1, $2); }
+ ;
+
+header : COMMAND_TABLE STRING ';'
+ { $$ = $2; }
+ ;
+
+request_list : request_list request_entry
+ { $$ = str_concat3($1, $2, ""); }
+ |
+ { $$ = ""; }
+ ;
+
+request_entry : REQUEST STRING ',' STRING ',' namelist ',' options ';'
+ { $$ = generate_rqte($2, quote($4), $6, $8); }
+ | REQUEST STRING ',' STRING ',' namelist ';'
+ { $$ = generate_rqte($2, quote($4), $6, 0); }
+ | UNKNOWN namelist ';'
+ { $$ = generate_rqte("ss_unknown_request",
+ (char *)NULL, $2, 0); }
+ | UNIMPLEMENTED STRING ',' STRING ',' namelist ';'
+ { $$ = generate_rqte("ss_unimplemented", quote($4), $6, 3); }
+ ;
+
+options : '(' flag_list ')'
+ { $$ = $2; }
+ | '(' ')'
+ { $$ = 0; }
+ ;
+
+flag_list : flag_list ',' STRING
+ { $$ = $1 | flag_val($3); }
+ | STRING
+ { $$ = flag_val($1); }
+ ;
+
+namelist: STRING
+ { $$ = quote(ds($1)); }
+ | namelist ',' STRING
+ { $$ = str_concat3($1, quote($3), ",\n "); }
+ ;
+
+%%
diff --git a/usr.bin/mk_cmds/mk_cmds.c b/usr.bin/mk_cmds/mk_cmds.c
new file mode 100644
index 0000000..7b39957
--- /dev/null
+++ b/usr.bin/mk_cmds/mk_cmds.c
@@ -0,0 +1,94 @@
+/*
+ * make_commands.c
+ *
+ * Header: mk_cmds.c,v 1.6 89/01/25 07:47:26 raeburn Exp
+ * $Locker: $
+ *
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include "copyright.h"
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <strings.h>
+#include "ss_internal.h"
+
+static const char copyright[] =
+ "Copyright 1987 by MIT Student Information Processing Board";
+
+extern pointer malloc PROTOTYPE((unsigned));
+extern char *last_token;
+extern FILE *output_file;
+
+extern FILE *yyin, *yyout;
+extern int yylineno;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char c_file[MAXPATHLEN];
+ int result;
+ char *path, *p;
+
+ if (argc != 2) {
+ fputs("Usage: ", stderr);
+ fputs(argv[0], stderr);
+ fputs("cmdtbl.ct\n", stderr);
+ exit(1);
+ }
+
+ path = malloc(strlen(argv[1])+4); /* extra space to add ".ct" */
+ strcpy(path, argv[1]);
+ p = rindex(path, '/');
+ if (p == (char *)NULL)
+ p = path;
+ else
+ p++;
+ p = rindex(p, '.');
+ if (p == (char *)NULL || strcmp(p, ".ct"))
+ strcat(path, ".ct");
+ yyin = fopen(path, "r");
+ if (!yyin) {
+ perror(path);
+ exit(1);
+ }
+
+ p = rindex(path, '.');
+ *p = '\0';
+ strcpy(c_file, path);
+ strcat(c_file, ".c");
+ *p = '.';
+
+ output_file = fopen(c_file, "w+");
+ if (!output_file) {
+ perror(c_file);
+ exit(1);
+ }
+
+ fputs("/* ", output_file);
+ fputs(c_file, output_file);
+ fputs(" - automatically generated from ", output_file);
+ fputs(path, output_file);
+ fputs(" */\n", output_file);
+ fputs("#include <ss/ss.h>\n\n", output_file);
+ fputs("#ifndef __STDC__\n#define const\n#endif\n\n", output_file);
+ /* parse it */
+ result = yyparse();
+ /* put file descriptors back where they belong */
+ fclose(yyin); /* bye bye input file */
+ fclose(output_file); /* bye bye output file */
+
+ return result;
+}
+
+yyerror(s)
+ char *s;
+{
+ fputs(s, stderr);
+ fprintf(stderr, "\nLine %d; last token was '%s'\n",
+ yylineno, last_token);
+}
diff --git a/usr.bin/mk_cmds/options.c b/usr.bin/mk_cmds/options.c
new file mode 100644
index 0000000..dd648b0
--- /dev/null
+++ b/usr.bin/mk_cmds/options.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+#include "copyright.h"
+#include <stdio.h>
+#include "ss.h"
+
+struct option {
+ char *text;
+ long value;
+};
+
+static struct option options[] = {
+ { "dont_list", SS_OPT_DONT_LIST },
+ { "^list", SS_OPT_DONT_LIST },
+ { "dont_summarize", SS_OPT_DONT_SUMMARIZE },
+ { "^summarize", SS_OPT_DONT_SUMMARIZE },
+ { (char *)NULL, 0 }
+};
+
+long
+flag_val(string)
+ register char *string;
+{
+ register struct option *opt;
+ for (opt = options; opt->text; opt++)
+ if (!strcmp(opt->text, string))
+ return(opt->value);
+ return(0);
+}
diff --git a/usr.bin/mk_cmds/utils.c b/usr.bin/mk_cmds/utils.c
new file mode 100644
index 0000000..f1685e8
--- /dev/null
+++ b/usr.bin/mk_cmds/utils.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 1987, 1988 by MIT Student Information Processing Board
+ *
+ * For copyright information, see copyright.h.
+ */
+
+#include <string.h>
+#include "copyright.h"
+#include "ss_internal.h" /* includes stdio and string */
+
+extern FILE *output_file;
+
+extern int exit();
+char *gensym(), *str_concat3(), *quote(), *ds();
+extern long gensym_n;
+
+void write_ct(hdr, rql)
+ char const *hdr, *rql;
+{
+ char *sym;
+ sym = gensym("ssu");
+ fputs("static ss_request_entry ", output_file);
+ fputs(sym, output_file);
+ fputs("[] = {\n", output_file);
+ fputs(rql, output_file);
+ fputs(" { 0, 0, 0, 0 }\n", output_file);
+ fputs("};\n\nss_request_table ", output_file);
+ fputs(hdr, output_file);
+ fprintf(output_file, " = { %d, ", SS_RQT_TBL_V2);
+ fputs(sym, output_file);
+ fputs(" };\n", output_file);
+}
+
+char * generate_cmds_string(cmds)
+ char const *cmds;
+{
+ char * var_name = gensym("ssu");
+ fputs("static char const * const ", output_file);
+ fputs(var_name, output_file);
+ fputs("[] = {\n", output_file);
+ fputs(cmds, output_file);
+ fputs(",\n (char const *)0\n};\n", output_file);
+ return(var_name);
+}
+
+void generate_function_definition(func)
+ char const *func;
+{
+ fputs("extern void ", output_file);
+ fputs(func, output_file);
+ fputs(" __SS_PROTO;\n", output_file);
+}
+
+char * generate_rqte(func_name, info_string, cmds, options)
+ char const *func_name;
+ char const *info_string;
+ char const *cmds;
+ int options;
+{
+ int size;
+ char *string, *var_name, numbuf[16];
+ var_name = generate_cmds_string(cmds);
+ generate_function_definition(func_name);
+ size = 6; /* " { " */
+ size += strlen(var_name)+7; /* "quux, " */
+ size += strlen(func_name)+7; /* "foo, " */
+ size += strlen(info_string)+9; /* "\"Info!\", " */
+ sprintf(numbuf, "%d", options);
+ size += strlen(numbuf);
+ size += 4; /* " }," + NL */
+ string = malloc(size * sizeof(char *));
+ strcpy(string, " { ");
+ strcat(string, var_name);
+ strcat(string, ",\n ");
+ strcat(string, func_name);
+ strcat(string, ",\n ");
+ strcat(string, info_string);
+ strcat(string, ",\n ");
+ strcat(string, numbuf);
+ strcat(string, " },\n");
+ return(string);
+}
+
+char *
+gensym(name)
+ char *name;
+{
+ char *symbol;
+
+ symbol = malloc((strlen(name)+6) * sizeof(char));
+ gensym_n++;
+ sprintf(symbol, "%s%05ld", name, gensym_n);
+ return(symbol);
+}
+
+/* concatenate three strings and return the result */
+char *str_concat3(a, b, c)
+ register char *a, *b, *c;
+{
+ char *result;
+ int size_a = strlen(a);
+ int size_b = strlen(b);
+ int size_c = strlen(c);
+
+ result = malloc((size_a + size_b + size_c + 2)*sizeof(char));
+ strcpy(result, a);
+ strcpy(&result[size_a], c);
+ strcpy(&result[size_a+size_c], b);
+ return(result);
+}
+
+/* return copy of string enclosed in double-quotes */
+char *quote(string)
+ register char *string;
+{
+ register char *result;
+ int len;
+ len = strlen(string)+1;
+ result = malloc(len+2);
+ result[0] = '"';
+ bcopy(string, &result[1], len-1);
+ result[len] = '"';
+ result[len+1] = '\0';
+ return(result);
+}
+
+/* make duplicate of string and return pointer */
+char *ds(s)
+ register char *s;
+{
+ register int len = strlen(s) + 1;
+ register char *new;
+ new = malloc(len);
+ bcopy(s, new, len);
+ return(new);
+}
diff --git a/usr.bin/mkdep/Makefile b/usr.bin/mkdep/Makefile
new file mode 100644
index 0000000..4329dc0
--- /dev/null
+++ b/usr.bin/mkdep/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+MAN1= mkdep.1
+
+.if (${MACHINE} == "hp300" || ${MACHINE} == "i386" || \
+ ${MACHINE} == "mips" || ${MACHINE} == "sparc")
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/mkdep.gcc.sh ${DESTDIR}/usr/bin/mkdep
+.else
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/mkdep.sh ${DESTDIR}/usr/bin/mkdep
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mkdep/mkdep.1 b/usr.bin/mkdep/mkdep.1
new file mode 100644
index 0000000..5c62a6b
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.1
@@ -0,0 +1,103 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mkdep.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MKDEP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm mkdep
+.Nd construct Makefile dependency list
+.Sh SYNOPSIS
+.Nm mkdep
+.Op Fl ap
+.Op Fl f Ar file
+.Op Ar flags
+.Ar file ...
+.Sh DESCRIPTION
+.Nm Mkdep
+takes a set of flags for the C compiler and a list
+of C source files as arguments and constructs a set of include
+file dependencies which are written into the file ``.depend''.
+An example of its use in a Makefile might be:
+.Bd -literal -offset indent
+CFLAGS= -O -I../include
+SRCS= file1.c file2.c
+
+depend:
+ mkdep ${CFLAGS} ${SRCS}
+.Ed
+.Pp
+where the macro SRCS is the list of C source files and the macro
+CFLAGS is the list of flags for the C compiler.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Append to the output file,
+so that multiple
+.Nm mkdep Ns 's
+may be run from a single Makefile.
+.It Fl f
+Write the include file dependencies to
+.Ar file ,
+instead of the default ``.depend''.
+.It Fl p
+Cause
+.Nm mkdep
+to produce dependencies of the form:
+.Bd -literal -offset indent
+program: program.c
+.Ed
+.Pp
+so that subsequent makes will produce
+.Ar program
+directly from its C module rather than using an intermediate
+.Pa \&.o
+module.
+This is useful for programs whose source is contained in a single
+module.
+.El
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr cpp 1 ,
+.Xr make 1
+.Sh FILES
+.Bl -tag -width .depend -compact
+.It Pa .depend
+File containing list of dependencies.
+.El
+.Sh HISTORY
+The
+.Nm mkdep
+command appeared in
+.Bx 4.3 Tahoe .
diff --git a/usr.bin/mkdep/mkdep.append b/usr.bin/mkdep/mkdep.append
new file mode 100644
index 0000000..1c84dd1
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.append
@@ -0,0 +1,123 @@
+#!/bin/sh -
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)mkdep.append 8.1 (Berkeley) 6/6/93
+#
+
+PATH=/bin:/usr/bin:/usr/ucb
+export PATH
+
+MAKE=Makefile # default makefile name is "Makefile"
+
+while :
+ do case "$1" in
+ # -f allows you to select a makefile name
+ -f)
+ MAKE=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ SED='s;\.o;;'
+ shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if [ $# = 0 ] ; then
+ echo 'usage: mkdep [-p] [-f makefile] [flags] file ...'
+ exit 1
+fi
+
+if [ ! -w $MAKE ]; then
+ echo "mkdep: no writeable file \"$MAKE\""
+ exit 1
+fi
+
+TMP=/tmp/mkdep$$
+
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+
+cp $MAKE ${MAKE}.bak
+
+sed -e '/DO NOT DELETE THIS LINE/,$d' < $MAKE > $TMP
+
+cat << _EOF_ >> $TMP
+# DO NOT DELETE THIS LINE -- mkdep uses it.
+# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
+
+_EOF_
+
+# If your compiler doesn't have -M, add it. If you can't, the next two
+# lines will try and replace the "cc -M". The real problem is that this
+# hack can't deal with anything that requires a search path, and doesn't
+# even try for anything using bracket (<>) syntax.
+#
+# egrep '^#include[ ]*".*"' /dev/null $* |
+# sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' -e 's/\.c/.o/' |
+
+cc -M $* |
+sed "
+ s; \./; ;g
+ $SED" |
+awk '{
+ if ($1 != prev) {
+ if (rec != "")
+ print rec;
+ rec = $0;
+ prev = $1;
+ }
+ else {
+ if (length(rec $2) > 78) {
+ print rec;
+ rec = $0;
+ }
+ else
+ rec = rec " " $2
+ }
+}
+END {
+ print rec
+}' >> $TMP
+
+cat << _EOF_ >> $TMP
+
+# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
+_EOF_
+
+# copy to preserve permissions
+cp $TMP $MAKE
+rm -f ${MAKE}.bak $TMP
+exit 0
diff --git a/usr.bin/mkdep/mkdep.gcc.sh b/usr.bin/mkdep/mkdep.gcc.sh
new file mode 100644
index 0000000..8cba090
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.gcc.sh
@@ -0,0 +1,98 @@
+#!/bin/sh -
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)mkdep.gcc.sh 8.1 (Berkeley) 6/6/93
+#
+
+PATH=/bin:/usr/bin:/usr/ucb
+export PATH
+
+D=.depend # default dependency file is .depend
+append=0
+pflag=
+
+while :
+ do case "$1" in
+ # -a appends to the depend file
+ -a)
+ append=1
+ shift ;;
+
+ # -f allows you to select a makefile name
+ -f)
+ D=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ pflag=p
+ shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if [ $# = 0 ] ; then
+ echo 'usage: mkdep [-p] [-f depend_file] [cc_flags] file ...'
+ exit 1
+fi
+
+TMP=/tmp/mkdep$$
+
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+
+# For C sources, mkdep must use exactly the same cpp and predefined flags
+# as the compiler would. This is easily arranged by letting the compiler
+# pick the cpp. mkdep must be told the cpp to use for exceptional cases.
+MKDEP_CPP=${MKDEP_CPP-"cc -E"}
+
+if [ x$pflag = x ]; then
+ $MKDEP_CPP -M $* | sed -e 's; \./; ;g' > $TMP
+else
+ $MKDEP_CPP -M $* | sed -e 's;\.o:;:;' -e 's; \./; ;g' > $TMP
+fi
+
+if [ $? != 0 ]; then
+ echo 'mkdep: compile failed.'
+ rm -f $TMP
+ exit 1
+fi
+
+if [ $append = 1 ]; then
+ cat $TMP >> $D
+ rm -f $TMP
+else
+ mv $TMP $D
+fi
+exit 0
diff --git a/usr.bin/mkdep/mkdep.old.compiler b/usr.bin/mkdep/mkdep.old.compiler
new file mode 100644
index 0000000..5c77773
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.old.compiler
@@ -0,0 +1,143 @@
+#!/bin/sh -
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)mkdep.old.compiler 8.1 (Berkeley) 6/6/93
+#
+
+# This is a version of mkdep that works pretty well
+# with compilers that don't have -M.
+MAKE=Makefile # default makefile name is "Makefile"
+
+PATH=/bin:/usr/bin:/usr/ucb:/lib:/usr/lib
+
+INCL=
+
+while :
+ do case "$1" in
+ # -f allows you to select a makefile name
+ -f)
+ MAKE=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ SED='s;\.o;;'
+ shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if [ $# = 0 ] ; then
+ echo 'usage: mkdep [-p] [-f makefile] [flags] file ...'
+ exit 1
+fi
+
+if [ ! -w $MAKE ]; then
+ echo "mkdep: no writeable file \"$MAKE\""
+ exit 1
+fi
+
+TMP=/tmp/mkdep$$
+
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+
+cp $MAKE ${MAKE}.bak
+sed -e '/DO NOT DELETE THIS LINE/,$d' < $MAKE > $TMP
+
+cat << _EOF_ >> $TMP
+# DO NOT DELETE THIS LINE -- mkdep uses it.
+# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
+
+_EOF_
+
+
+for i do
+ case "$i" in
+ -c|-M|-O)
+ ;;
+ -I*)
+ INCL="$INCL $i";;
+ -D*|-U*)
+ FLAGS="$FLAGS $i";;
+ *)
+ # assume source file
+ # put '$dep' in front of dependencies
+ dep=`echo "$i" | sed -e 's,/,\\\\/,g' -e 's/\.c$/.o/'`
+
+ # Find includes, remove leading numerics, remove ./,
+ # remove double quotes, and remove trailing numerics.
+ # Sort that, discarding duplicates, and add '$dep'.
+ cpp $INCL $FLAGS "$i" | sed -e '
+ /^#/!d
+ s/# [0-9]* //
+ s,"./,",
+ s/"\(.*\)"/\1/
+ s/ [ 0-9]*$//' |
+ sort -u | sed -e "s/^/$dep: /";;
+ esac
+done |
+sed "
+ s; \./; ;g
+ /\.c:$/d
+ $SED" |
+awk '{
+ if ($1 != prev) {
+ if (rec != "")
+ print rec;
+ rec = $0;
+ prev = $1;
+ }
+ else {
+ if (length(rec $2) > 78) {
+ print rec;
+ rec = $0;
+ }
+ else
+ rec = rec " " $2
+ }
+}
+END {
+ print rec
+}' >> $TMP
+
+cat << _EOF_ >> $TMP
+
+# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
+_EOF_
+
+# copy to preserve permissions
+cp $TMP $MAKE
+rm -f $TMP
+exit 0
diff --git a/usr.bin/mkdep/mkdep.sh b/usr.bin/mkdep/mkdep.sh
new file mode 100644
index 0000000..5344108
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.sh
@@ -0,0 +1,111 @@
+#!/bin/sh -
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)mkdep.sh 8.1 (Berkeley) 6/6/93
+#
+
+PATH=/bin:/usr/bin:/usr/ucb:/usr/old/bin
+export PATH
+
+D=.depend # default dependency file is .depend
+append=0
+
+while :
+ do case "$1" in
+ # -a appends to the depend file
+ -a)
+ append=1
+ shift ;;
+
+ # -f allows you to select a makefile name
+ -f)
+ D=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ SED='s;\.o ; ;'
+ shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if [ $# = 0 ] ; then
+ echo 'usage: mkdep [-p] [-f depend_file] [cc_flags] file ...'
+ exit 1
+fi
+
+TMP=/tmp/mkdep$$
+
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+
+cc -M $* |
+sed "
+ s; \./; ;g
+ /\.c:$/d
+ $SED" |
+awk '{
+ if ($1 != prev) {
+ if (rec != "")
+ print rec;
+ rec = $0;
+ prev = $1;
+ }
+ else {
+ if (length(rec $2) > 78) {
+ print rec;
+ rec = $0;
+ }
+ else
+ rec = rec " " $2
+ }
+}
+END {
+ print rec
+}' > $TMP
+
+if [ $? != 0 ]; then
+ echo 'mkdep: compile failed.'
+ rm -f $TMP
+ exit 1
+fi
+
+if [ $append = 1 ]; then
+ cat $TMP >> $D
+ rm -f $TMP
+else
+ mv $TMP $D
+fi
+exit 0
diff --git a/usr.bin/mkdep/mkdep.ultrix b/usr.bin/mkdep/mkdep.ultrix
new file mode 100644
index 0000000..8c48667
--- /dev/null
+++ b/usr.bin/mkdep/mkdep.ultrix
@@ -0,0 +1,124 @@
+#!/bin/sh -
+#
+# Copyright (c) 1991, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)mkdep.ultrix 8.1 (Berkeley) 6/6/93
+#
+
+PATH=/bin:/usr/bin:/usr/ucb
+export PATH
+
+MAKE=Makefile # default makefile name is "Makefile"
+
+while :
+ do case "$1" in
+ # -f allows you to select a makefile name
+ -f)
+ MAKE=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ SED='s;\.o;;'
+ shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if [ $# = 0 ] ; then
+ echo 'usage: mkdep [-p] [-f makefile] [flags] file ...'
+ exit 1
+fi
+
+if [ ! -w $MAKE ]; then
+ echo "mkdep: no writeable file \"$MAKE\""
+ exit 1
+fi
+
+TMP=/tmp/mkdep$$
+
+trap 'rm -f $TMP ; exit 1' 1 2 3 13 15
+
+cp $MAKE ${MAKE}.bak
+
+sed -e '/DO NOT DELETE THIS LINE/,$d' < $MAKE > $TMP
+
+cat << _EOF_ >> $TMP
+# DO NOT DELETE THIS LINE -- mkdep uses it.
+# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
+
+_EOF_
+
+# If your compiler doesn't have -M, add it. If you can't, the next two
+# lines will try and replace the "cc -M". The real problem is that this
+# hack can't deal with anything that requires a search path, and doesn't
+# even try for anything using bracket (<>) syntax.
+#
+# egrep '^#include[ ]*".*"' /dev/null $* |
+# sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' -e 's/\.c/.o/' |
+
+# Ultrix has already used -M for something else.
+cc -Em $* |
+sed "
+ s; \./; ;g
+ $SED" |
+awk '{
+ if ($1 != prev) {
+ if (rec != "")
+ print rec;
+ rec = $0;
+ prev = $1;
+ }
+ else {
+ if (length(rec $2) > 78) {
+ print rec;
+ rec = $0;
+ }
+ else
+ rec = rec " " $2
+ }
+}
+END {
+ print rec
+}' >> $TMP
+
+cat << _EOF_ >> $TMP
+
+# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
+_EOF_
+
+# copy to preserve permissions
+cp $TMP $MAKE
+rm -f ${MAKE}.bak $TMP
+exit 0
diff --git a/usr.bin/mkfifo/Makefile b/usr.bin/mkfifo/Makefile
new file mode 100644
index 0000000..fff2939
--- /dev/null
+++ b/usr.bin/mkfifo/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkfifo
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mkfifo/mkfifo.1 b/usr.bin/mkfifo/mkfifo.1
new file mode 100644
index 0000000..f0607a7
--- /dev/null
+++ b/usr.bin/mkfifo/mkfifo.1
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mkfifo.1 8.2 (Berkeley) 1/5/94
+.\"
+.Dd January 5, 1994
+.Dt MKFIFO 1
+.Os BSD 4.4
+.Sh NAME
+.Nm mkfifo
+.Nd make fifos
+.Sh SYNOPSIS
+.Nm mkfifo
+.Ar fifo_name ...
+.Sh DESCRIPTION
+.Nm Mkfifo
+creates the fifos requested, in the order specified,
+using mode
+.Li \&0777 .
+.Pp
+.Nm Mkfifo
+requires write permission in the parent directory.
+.Pp
+.Nm Mkfifo
+exits 0 if successful, and >0 if an error occurred.
+.Sh STANDARDS
+The
+.Nm mkfifo
+utility is expected to be
+.St -p1003.2
+compliant.
+.Sh SEE ALSO
+.Xr mkdir 1 ,
+.Xr mknod 1 ,
+.Xr rm 1 ,
+.Xr mkfifo 2
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/mkfifo/mkfifo.c b/usr.bin/mkfifo/mkfifo.c
new file mode 100644
index 0000000..53b7435
--- /dev/null
+++ b/usr.bin/mkfifo/mkfifo.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkfifo.c 8.2 (Berkeley) 1/5/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int ch, exitval;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argv[0] == NULL)
+ usage();
+
+ for (exitval = 0; *argv != NULL; ++argv)
+ if (mkfifo(*argv, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ warn("%s", *argv);
+ exitval = 1;
+ }
+ exit(exitval);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "usage: mkfifo fifoname ...\n");
+ exit(1);
+}
diff --git a/usr.bin/mklocale/Japanese b/usr.bin/mklocale/Japanese
new file mode 100644
index 0000000..55eb155
--- /dev/null
+++ b/usr.bin/mklocale/Japanese
@@ -0,0 +1,158 @@
+# @(#)Japanese 8.1 (Berkeley) 6/6/93
+
+/*
+ * Japanese LOCALE_CTYPE definitions using EUC of JIS character sets
+ */
+
+ENCODING "EUC"
+
+/* JIS JIS JIS */
+/* X201 X208 X201 */
+/* 00-7f 84-fe */
+
+VARIABLE 1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080
+
+/*
+ * Code Set 1
+ */
+ALPHA 'A' - 'Z' 'a' - 'z'
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e
+LOWER 'a' - 'z'
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z'
+XDIGIT 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e
+
+MAPLOWER < 'A' - 'Z' : 'a' >
+MAPLOWER < 'a' - 'z' : 'a' >
+MAPUPPER < 'A' - 'Z' : 'A' >
+MAPUPPER < 'a' - 'z' : 'A' >
+TODIGIT < '0' - '9' : 0 >
+TODIGIT < 'A' - 'F' : 10 >
+TODIGIT < 'a' - 'f' : 10 >
+
+/*
+ * Code Set 2
+ */
+
+SPACE 0xa1a1
+PHONOGRAM 0xa1bc
+SPECIAL 0xa1a2 - 0xa1fe
+PUNCT 0xa1a2 - 0xa1f8 /* A few too many in here... */
+
+SPECIAL 0xa2a1 - 0xa2ae 0xa2ba - 0xa2c1 0xa2ca - 0xa2d0 0xa2dc - 0xa2ea
+SPECIAL 0xa2f2 - 0xa2f9 0xa2fe
+
+DIGIT 0xa3b0 - 0xa3b9
+UPPER 0xa3c1 - 0xa3da /* Romaji */
+LOWER 0xa3e1 - 0xa3fa /* Romaji */
+MAPLOWER < 0xa3c1 - 0xa3da : 0xa3e1 > /* English */
+MAPLOWER < 0xa3e1 - 0xa3fa : 0xa3e1 > /* English */
+MAPUPPER < 0xa3c1 - 0xa3da : 0xa3c1 >
+MAPUPPER < 0xa3e1 - 0xa3fa : 0xa3c1 >
+
+XDIGIT 0xa3c1 - 0xa3c6 0xa3e1 - 0xa3e6
+
+TODIGIT < 0xa3b0 - 0xa3b9 : 0 >
+TODIGIT < 0xa3c1 - 0xa3c6 : 10 >
+TODIGIT < 0xa3e1 - 0xa3e6 : 10 >
+
+PHONOGRAM 0xa4a1 - 0xa4f3
+PHONOGRAM 0xa5a1 - 0xa5f6
+
+UPPER 0xa6a1 - 0xa6b8 /* Greek */
+LOWER 0xa6c1 - 0xa6d8 /* Greek */
+MAPLOWER < 0xa6a1 - 0xa6b8 : 0xa6c1 >
+MAPLOWER < 0xa6c1 - 0xa6d8 : 0xa6c1 >
+MAPUPPER < 0xa6a1 - 0xa6b8 : 0xa6a1 >
+MAPUPPER < 0xa6c1 - 0xa6d8 : 0xa6a1 >
+
+UPPER 0xa7a1 - 0xa7c1 /* Cyrillic */
+LOWER 0xa7d1 - 0xa7f1 /* Cyrillic */
+MAPLOWER < 0xa7a1 - 0xa7c1 : 0xa7d1 >
+MAPLOWER < 0xa7d1 - 0xa7f1 : 0xa7d1 >
+MAPUPPER < 0xa7a1 - 0xa7c1 : 0xa7a1 >
+MAPUPPER < 0xa7d1 - 0xa7f1 : 0xa7a1 >
+
+SPECIAL 0xa8a1 - 0xa8c0
+
+IDEOGRAM 0xb0a1 - 0xb0fe
+IDEOGRAM 0xb1a1 - 0xb1fe
+IDEOGRAM 0xb2a1 - 0xb2fe
+IDEOGRAM 0xb3a1 - 0xb3fe
+IDEOGRAM 0xb4a1 - 0xb4fe
+IDEOGRAM 0xb5a1 - 0xb5fe
+IDEOGRAM 0xb6a1 - 0xb6fe
+IDEOGRAM 0xb7a1 - 0xb7fe
+IDEOGRAM 0xb8a1 - 0xb8fe
+IDEOGRAM 0xb9a1 - 0xb9fe
+IDEOGRAM 0xbaa1 - 0xbafe
+IDEOGRAM 0xbba1 - 0xbbfe
+IDEOGRAM 0xbca1 - 0xbcfe
+IDEOGRAM 0xbda1 - 0xbdfe
+IDEOGRAM 0xbea1 - 0xbefe
+IDEOGRAM 0xbfa1 - 0xbffe
+IDEOGRAM 0xc0a1 - 0xc0fe
+IDEOGRAM 0xc1a1 - 0xc1fe
+IDEOGRAM 0xc2a1 - 0xc2fe
+IDEOGRAM 0xc3a1 - 0xc3fe
+IDEOGRAM 0xc4a1 - 0xc4fe
+IDEOGRAM 0xc5a1 - 0xc5fe
+IDEOGRAM 0xc6a1 - 0xc6fe
+IDEOGRAM 0xc7a1 - 0xc7fe
+IDEOGRAM 0xc8a1 - 0xc8fe
+IDEOGRAM 0xc9a1 - 0xc9fe
+IDEOGRAM 0xcaa1 - 0xcafe
+IDEOGRAM 0xcba1 - 0xcbfe
+IDEOGRAM 0xcca1 - 0xccfe
+IDEOGRAM 0xcda1 - 0xcdfe
+IDEOGRAM 0xcea1 - 0xcefe
+IDEOGRAM 0xcfa1 - 0xcfd3
+IDEOGRAM 0xd0a1 - 0xd0fe
+IDEOGRAM 0xd1a1 - 0xd1fe
+IDEOGRAM 0xd2a1 - 0xd2fe
+IDEOGRAM 0xd3a1 - 0xd3fe
+IDEOGRAM 0xd4a1 - 0xd4fe
+IDEOGRAM 0xd5a1 - 0xd5fe
+IDEOGRAM 0xd6a1 - 0xd6fe
+IDEOGRAM 0xd7a1 - 0xd7fe
+IDEOGRAM 0xd8a1 - 0xd8fe
+IDEOGRAM 0xd9a1 - 0xd9fe
+IDEOGRAM 0xdaa1 - 0xdafe
+IDEOGRAM 0xdba1 - 0xdbfe
+IDEOGRAM 0xdca1 - 0xdcfe
+IDEOGRAM 0xdda1 - 0xddfe
+IDEOGRAM 0xdea1 - 0xdefe
+IDEOGRAM 0xdfa1 - 0xdffe
+IDEOGRAM 0xe0a1 - 0xe0fe
+IDEOGRAM 0xe1a1 - 0xe1fe
+IDEOGRAM 0xe2a1 - 0xe2fe
+IDEOGRAM 0xe3a1 - 0xe3fe
+IDEOGRAM 0xe4a1 - 0xe4fe
+IDEOGRAM 0xe5a1 - 0xe5fe
+IDEOGRAM 0xe6a1 - 0xe6fe
+IDEOGRAM 0xe7a1 - 0xe7fe
+IDEOGRAM 0xe8a1 - 0xe8fe
+IDEOGRAM 0xe9a1 - 0xe9fe
+IDEOGRAM 0xeaa1 - 0xeafe
+IDEOGRAM 0xeba1 - 0xebfe
+IDEOGRAM 0xeca1 - 0xecfe
+IDEOGRAM 0xeda1 - 0xedfe
+IDEOGRAM 0xeea1 - 0xeefe
+IDEOGRAM 0xefa1 - 0xeffe
+IDEOGRAM 0xf0a1 - 0xf0fe
+IDEOGRAM 0xf1a1 - 0xf1fe
+IDEOGRAM 0xf2a1 - 0xf2fe
+IDEOGRAM 0xf3a1 - 0xf3fe
+IDEOGRAM 0xf4a1 - 0xf4a4
+
+/*
+ * This is for Code Set 3, half-width kana
+ */
+SPECIAL 0xa1 - 0xdf
+PHONOGRAM 0xa1 - 0xdf
+CONTROL 0x84 - 0x97 0x9b - 0x9f 0xe0 - 0xfe
diff --git a/usr.bin/mklocale/Makefile b/usr.bin/mklocale/Makefile
new file mode 100644
index 0000000..9c29622
--- /dev/null
+++ b/usr.bin/mklocale/Makefile
@@ -0,0 +1,27 @@
+# @(#)Makefile 8.1 (Berkeley) 6/7/93
+
+PROG= mklocale
+SRCS= yacc.c lex.c
+CFLAGS+=-I.
+CLEANFILES+=y.tab.h yacc.c lex.c
+MAN1= mklocale.1
+LOCALES= ja_JP.EUC ru_SU.KOI8-R lt_LN.ISO_8859-1 ru_SU.CP866
+LOCALEDIR= ${DESTDIR}/usr/share/locale
+
+LATIN1LINKS = \
+ da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES fi_FI \
+ fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT nl_BE nl_NL no_NO \
+ pt_PT sv_SE
+
+afterinstall:
+ for locale in ${LOCALES}; do \
+ mklocale -o ${LOCALEDIR}/$$locale/LC_CTYPE ${.CURDIR}/data/$$locale; \
+ chown ${BINOWN}.${BINGRP} ${LOCALEDIR}/$$locale/LC_CTYPE; \
+ chmod 644 ${LOCALEDIR}/$$locale/LC_CTYPE; \
+ done
+ for l in ${LATIN1LINKS}; do \
+ ln -fs ../lt_LN.ISO_8859-1/LC_CTYPE \
+ ${LOCALEDIR}/$$l.ISO_8859-1/LC_CTYPE; \
+ done
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mklocale/POSIX b/usr.bin/mklocale/POSIX
new file mode 100644
index 0000000..4f1ae60
--- /dev/null
+++ b/usr.bin/mklocale/POSIX
@@ -0,0 +1,33 @@
+# @(#)POSIX 8.1 (Berkeley) 6/6/93
+
+/*
+ * Standard LOCALE_CTYPE for the C Locale
+ */
+ENCODING "NONE"
+VARIABLE A comment line or data line. Only 1 allowed. Copied verbatim.
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z'
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e
+LOWER 'a' - 'z'
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z'
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/README.locale_name b/usr.bin/mklocale/README.locale_name
new file mode 100644
index 0000000..f34cf67
--- /dev/null
+++ b/usr.bin/mklocale/README.locale_name
@@ -0,0 +1,7 @@
+Locale name string format must be compliant with XPG3 and
+using following format.
+ <locale name> ::= <language>_<territory>.<encoding>
+ <language> ::= based on ISO 639
+ <territory> ::= based on ISO 3166 (country code)
+ <encoding> ::= "EUC", "KOI8-R", ...
+
diff --git a/usr.bin/mklocale/data/ja_JP.EUC b/usr.bin/mklocale/data/ja_JP.EUC
new file mode 100644
index 0000000..88fdc91
--- /dev/null
+++ b/usr.bin/mklocale/data/ja_JP.EUC
@@ -0,0 +1,158 @@
+# @(#)Japanese 8.1 (Berkeley) 6/6/93
+
+/*
+ * Japanese LOCALE_CTYPE definitions using EUC of JIS character sets
+ */
+
+ENCODING "EUC"
+
+/* JIS JIS JIS */
+/* X201 X208 X201 */
+/* 00-7f 84-fe */
+
+VARIABLE 1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080
+
+/*
+ * Code Set 1
+ */
+ALPHA 'A' - 'Z' 'a' - 'z'
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e
+LOWER 'a' - 'z'
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z'
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e
+
+MAPLOWER < 'A' - 'Z' : 'a' >
+MAPLOWER < 'a' - 'z' : 'a' >
+MAPUPPER < 'A' - 'Z' : 'A' >
+MAPUPPER < 'a' - 'z' : 'A' >
+TODIGIT < '0' - '9' : 0 >
+TODIGIT < 'A' - 'F' : 10 >
+TODIGIT < 'a' - 'f' : 10 >
+
+/*
+ * Code Set 2
+ */
+
+SPACE 0xa1a1
+PHONOGRAM 0xa1bc
+SPECIAL 0xa1a2 - 0xa1fe
+PUNCT 0xa1a2 - 0xa1f8 /* A few too many in here... */
+
+SPECIAL 0xa2a1 - 0xa2ae 0xa2ba - 0xa2c1 0xa2ca - 0xa2d0 0xa2dc - 0xa2ea
+SPECIAL 0xa2f2 - 0xa2f9 0xa2fe
+
+DIGIT 0xa3b0 - 0xa3b9
+UPPER 0xa3c1 - 0xa3da /* Romaji */
+LOWER 0xa3e1 - 0xa3fa /* Romaji */
+MAPLOWER < 0xa3c1 - 0xa3da : 0xa3e1 > /* English */
+MAPLOWER < 0xa3e1 - 0xa3fa : 0xa3e1 > /* English */
+MAPUPPER < 0xa3c1 - 0xa3da : 0xa3c1 >
+MAPUPPER < 0xa3e1 - 0xa3fa : 0xa3c1 >
+
+XDIGIT 0xa3c1 - 0xa3c6 0xa3e1 - 0xa3e6
+
+TODIGIT < 0xa3b0 - 0xa3b9 : 0 >
+TODIGIT < 0xa3c1 - 0xa3c6 : 10 >
+TODIGIT < 0xa3e1 - 0xa3e6 : 10 >
+
+PHONOGRAM 0xa4a1 - 0xa4f3
+PHONOGRAM 0xa5a1 - 0xa5f6
+
+UPPER 0xa6a1 - 0xa6b8 /* Greek */
+LOWER 0xa6c1 - 0xa6d8 /* Greek */
+MAPLOWER < 0xa6a1 - 0xa6b8 : 0xa6c1 >
+MAPLOWER < 0xa6c1 - 0xa6d8 : 0xa6c1 >
+MAPUPPER < 0xa6a1 - 0xa6b8 : 0xa6a1 >
+MAPUPPER < 0xa6c1 - 0xa6d8 : 0xa6a1 >
+
+UPPER 0xa7a1 - 0xa7c1 /* Cyrillic */
+LOWER 0xa7d1 - 0xa7f1 /* Cyrillic */
+MAPLOWER < 0xa7a1 - 0xa7c1 : 0xa7d1 >
+MAPLOWER < 0xa7d1 - 0xa7f1 : 0xa7d1 >
+MAPUPPER < 0xa7a1 - 0xa7c1 : 0xa7a1 >
+MAPUPPER < 0xa7d1 - 0xa7f1 : 0xa7a1 >
+
+SPECIAL 0xa8a1 - 0xa8c0
+
+IDEOGRAM 0xb0a1 - 0xb0fe
+IDEOGRAM 0xb1a1 - 0xb1fe
+IDEOGRAM 0xb2a1 - 0xb2fe
+IDEOGRAM 0xb3a1 - 0xb3fe
+IDEOGRAM 0xb4a1 - 0xb4fe
+IDEOGRAM 0xb5a1 - 0xb5fe
+IDEOGRAM 0xb6a1 - 0xb6fe
+IDEOGRAM 0xb7a1 - 0xb7fe
+IDEOGRAM 0xb8a1 - 0xb8fe
+IDEOGRAM 0xb9a1 - 0xb9fe
+IDEOGRAM 0xbaa1 - 0xbafe
+IDEOGRAM 0xbba1 - 0xbbfe
+IDEOGRAM 0xbca1 - 0xbcfe
+IDEOGRAM 0xbda1 - 0xbdfe
+IDEOGRAM 0xbea1 - 0xbefe
+IDEOGRAM 0xbfa1 - 0xbffe
+IDEOGRAM 0xc0a1 - 0xc0fe
+IDEOGRAM 0xc1a1 - 0xc1fe
+IDEOGRAM 0xc2a1 - 0xc2fe
+IDEOGRAM 0xc3a1 - 0xc3fe
+IDEOGRAM 0xc4a1 - 0xc4fe
+IDEOGRAM 0xc5a1 - 0xc5fe
+IDEOGRAM 0xc6a1 - 0xc6fe
+IDEOGRAM 0xc7a1 - 0xc7fe
+IDEOGRAM 0xc8a1 - 0xc8fe
+IDEOGRAM 0xc9a1 - 0xc9fe
+IDEOGRAM 0xcaa1 - 0xcafe
+IDEOGRAM 0xcba1 - 0xcbfe
+IDEOGRAM 0xcca1 - 0xccfe
+IDEOGRAM 0xcda1 - 0xcdfe
+IDEOGRAM 0xcea1 - 0xcefe
+IDEOGRAM 0xcfa1 - 0xcfd3
+IDEOGRAM 0xd0a1 - 0xd0fe
+IDEOGRAM 0xd1a1 - 0xd1fe
+IDEOGRAM 0xd2a1 - 0xd2fe
+IDEOGRAM 0xd3a1 - 0xd3fe
+IDEOGRAM 0xd4a1 - 0xd4fe
+IDEOGRAM 0xd5a1 - 0xd5fe
+IDEOGRAM 0xd6a1 - 0xd6fe
+IDEOGRAM 0xd7a1 - 0xd7fe
+IDEOGRAM 0xd8a1 - 0xd8fe
+IDEOGRAM 0xd9a1 - 0xd9fe
+IDEOGRAM 0xdaa1 - 0xdafe
+IDEOGRAM 0xdba1 - 0xdbfe
+IDEOGRAM 0xdca1 - 0xdcfe
+IDEOGRAM 0xdda1 - 0xddfe
+IDEOGRAM 0xdea1 - 0xdefe
+IDEOGRAM 0xdfa1 - 0xdffe
+IDEOGRAM 0xe0a1 - 0xe0fe
+IDEOGRAM 0xe1a1 - 0xe1fe
+IDEOGRAM 0xe2a1 - 0xe2fe
+IDEOGRAM 0xe3a1 - 0xe3fe
+IDEOGRAM 0xe4a1 - 0xe4fe
+IDEOGRAM 0xe5a1 - 0xe5fe
+IDEOGRAM 0xe6a1 - 0xe6fe
+IDEOGRAM 0xe7a1 - 0xe7fe
+IDEOGRAM 0xe8a1 - 0xe8fe
+IDEOGRAM 0xe9a1 - 0xe9fe
+IDEOGRAM 0xeaa1 - 0xeafe
+IDEOGRAM 0xeba1 - 0xebfe
+IDEOGRAM 0xeca1 - 0xecfe
+IDEOGRAM 0xeda1 - 0xedfe
+IDEOGRAM 0xeea1 - 0xeefe
+IDEOGRAM 0xefa1 - 0xeffe
+IDEOGRAM 0xf0a1 - 0xf0fe
+IDEOGRAM 0xf1a1 - 0xf1fe
+IDEOGRAM 0xf2a1 - 0xf2fe
+IDEOGRAM 0xf3a1 - 0xf3fe
+IDEOGRAM 0xf4a1 - 0xf4a4
+
+/*
+ * This is for Code Set 3, half-width kana
+ */
+SPECIAL 0xa1 - 0xdf
+PHONOGRAM 0xa1 - 0xdf
+CONTROL 0x84 - 0x97 0x9b - 0x9f 0xe0 - 0xfe
diff --git a/usr.bin/mklocale/data/lt_LN.ISO8859-1 b/usr.bin/mklocale/data/lt_LN.ISO8859-1
new file mode 100644
index 0000000..5232ae3
--- /dev/null
+++ b/usr.bin/mklocale/data/lt_LN.ISO8859-1
@@ -0,0 +1,35 @@
+/*
+ * Standard LOCALE_CTYPE for the iso_8859_1 Locale
+ */
+ENCODING "NONE"
+VARIABLE ISO 8859-1 Latin-1 character set
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0xc0 - 0xff
+CONTROL 0x00 - 0x1f 0x7f - 0x9f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0xa0 - 0xff
+LOWER 'a' - 'z' 0xe0 - 0xff
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e 0xa1 - 0xbf
+SPACE 0x09 - 0x0d 0x20 0xa0
+UPPER 'A' - 'Z' 0xc0 - 0xde
+XDIGIT 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t' 0xa0
+PRINT 0x20 - 0x7e 0xa0 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0xc0 - 0xdd : 0xe0>
+MAPLOWER <0xe0 - 0xff : 0xe0>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xc0 - 0xdd : 0xc0>
+MAPUPPER <0xe0 - 0xff : 0xc0>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/data/lt_LN.ISO_8859-1 b/usr.bin/mklocale/data/lt_LN.ISO_8859-1
new file mode 100644
index 0000000..5c74641
--- /dev/null
+++ b/usr.bin/mklocale/data/lt_LN.ISO_8859-1
@@ -0,0 +1,39 @@
+/*
+ * Standard LOCALE_CTYPE for the iso_8859_1 Locale
+ */
+ENCODING "NONE"
+VARIABLE ISO 8859-1 Latin-1 character set
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0xc0 - 0xd6 0xd8 - 0xf6 0xf8 - 0xff
+CONTROL 0x00 - 0x1f 0x7f - 0x9f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0xa0 - 0xff
+LOWER 'a' - 'z' 0xdf - 0xf6 0xf8 - 0xff
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e 0xa1 - 0xbf 0xd7 0xf7
+SPACE 0x09 - 0x0d ' ' 0xa0
+UPPER 'A' - 'Z' 0xc0 - 0xd6 0xd8 - 0xde
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e 0xa0 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0xc0 - 0xd6 : 0xe0>
+MAPLOWER <0xd8 - 0xde : 0xf8>
+MAPLOWER <0xdf - 0xf6 : 0xdf>
+MAPLOWER <0xf8 - 0xff : 0xf8>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xc0 - 0xd6 : 0xc0>
+MAPUPPER <0xd8 - 0xde : 0xd8>
+MAPUPPER <0xe0 - 0xf6 : 0xc0>
+MAPUPPER <0xf8 - 0xfe : 0xd8>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/data/ru_SU.CP866 b/usr.bin/mklocale/data/ru_SU.CP866
new file mode 100644
index 0000000..659482b
--- /dev/null
+++ b/usr.bin/mklocale/data/ru_SU.CP866
@@ -0,0 +1,42 @@
+/*
+ * LOCALE_CTYPE for Russian Alternative character set (CP866)
+ */
+ENCODING "NONE"
+VARIABLE Russian Alternative charset (CP866) by ache@astral.msk.su
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0x80 - 0xaf 0xe0 - 0xf1
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0x80 - 0xff
+LOWER 'a' - 'z' 0xa0 - 0xaf 0xe0 - 0xef 0xf1
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z' 0x80 - 0x9f 0xf0
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e 0x80 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0x80 - 0x8f : 0xa0>
+MAPLOWER <0x90 - 0x9f : 0xe0>
+MAPLOWER <0xf0 0xf1>
+MAPLOWER <0xa0 - 0xaf : 0xa0>
+MAPLOWER <0xe0 - 0xef : 0xe0>
+MAPLOWER <0xf1 0xf1>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0x80 - 0x9f : 0x80>
+MAPUPPER <0xf0 0xf0>
+MAPUPPER <0xa0 - 0xaf : 0x80>
+MAPUPPER <0xe0 - 0xef : 0x90>
+MAPUPPER <0xf1 0xf0>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/data/ru_SU.KOI8-R b/usr.bin/mklocale/data/ru_SU.KOI8-R
new file mode 100644
index 0000000..6d9f539
--- /dev/null
+++ b/usr.bin/mklocale/data/ru_SU.KOI8-R
@@ -0,0 +1,39 @@
+/*
+ * LOCALE_CTYPE for Russian koi8-r character set (RFC1489)
+ */
+ENCODING "NONE"
+VARIABLE Russian koi8-r character set by ache@astral.msk.su
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0xa3 0xb3 0xc0 - 0xff
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0x80 - 0xff
+LOWER 'a' - 'z' 0xa3 0xc0 - 0xdf
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z' 0xb3 0xe0 - 0xff
+XDIGIT '0' - '9' 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e 0x80 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0xb3 0xa3>
+MAPLOWER <0xa3 0xa3>
+MAPLOWER <0xe0 - 0xff : 0xc0>
+MAPLOWER <0xc0 - 0xdf : 0xc0>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xb3 0xb3>
+MAPUPPER <0xa3 0xb3>
+MAPUPPER <0xe0 - 0xff : 0xe0>
+MAPUPPER <0xc0 - 0xdf : 0xe0>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/iso_8859_1 b/usr.bin/mklocale/iso_8859_1
new file mode 100644
index 0000000..5232ae3
--- /dev/null
+++ b/usr.bin/mklocale/iso_8859_1
@@ -0,0 +1,35 @@
+/*
+ * Standard LOCALE_CTYPE for the iso_8859_1 Locale
+ */
+ENCODING "NONE"
+VARIABLE ISO 8859-1 Latin-1 character set
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0xc0 - 0xff
+CONTROL 0x00 - 0x1f 0x7f - 0x9f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0xa0 - 0xff
+LOWER 'a' - 'z' 0xe0 - 0xff
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e 0xa1 - 0xbf
+SPACE 0x09 - 0x0d 0x20 0xa0
+UPPER 'A' - 'Z' 0xc0 - 0xde
+XDIGIT 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t' 0xa0
+PRINT 0x20 - 0x7e 0xa0 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0xc0 - 0xdd : 0xe0>
+MAPLOWER <0xe0 - 0xff : 0xe0>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xc0 - 0xdd : 0xc0>
+MAPUPPER <0xe0 - 0xff : 0xc0>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/ja_JP.EUC b/usr.bin/mklocale/ja_JP.EUC
new file mode 100644
index 0000000..55eb155
--- /dev/null
+++ b/usr.bin/mklocale/ja_JP.EUC
@@ -0,0 +1,158 @@
+# @(#)Japanese 8.1 (Berkeley) 6/6/93
+
+/*
+ * Japanese LOCALE_CTYPE definitions using EUC of JIS character sets
+ */
+
+ENCODING "EUC"
+
+/* JIS JIS JIS */
+/* X201 X208 X201 */
+/* 00-7f 84-fe */
+
+VARIABLE 1 0x0000 2 0x8080 2 0x0080 3 0x8000 0x8080
+
+/*
+ * Code Set 1
+ */
+ALPHA 'A' - 'Z' 'a' - 'z'
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e
+LOWER 'a' - 'z'
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z'
+XDIGIT 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e
+
+MAPLOWER < 'A' - 'Z' : 'a' >
+MAPLOWER < 'a' - 'z' : 'a' >
+MAPUPPER < 'A' - 'Z' : 'A' >
+MAPUPPER < 'a' - 'z' : 'A' >
+TODIGIT < '0' - '9' : 0 >
+TODIGIT < 'A' - 'F' : 10 >
+TODIGIT < 'a' - 'f' : 10 >
+
+/*
+ * Code Set 2
+ */
+
+SPACE 0xa1a1
+PHONOGRAM 0xa1bc
+SPECIAL 0xa1a2 - 0xa1fe
+PUNCT 0xa1a2 - 0xa1f8 /* A few too many in here... */
+
+SPECIAL 0xa2a1 - 0xa2ae 0xa2ba - 0xa2c1 0xa2ca - 0xa2d0 0xa2dc - 0xa2ea
+SPECIAL 0xa2f2 - 0xa2f9 0xa2fe
+
+DIGIT 0xa3b0 - 0xa3b9
+UPPER 0xa3c1 - 0xa3da /* Romaji */
+LOWER 0xa3e1 - 0xa3fa /* Romaji */
+MAPLOWER < 0xa3c1 - 0xa3da : 0xa3e1 > /* English */
+MAPLOWER < 0xa3e1 - 0xa3fa : 0xa3e1 > /* English */
+MAPUPPER < 0xa3c1 - 0xa3da : 0xa3c1 >
+MAPUPPER < 0xa3e1 - 0xa3fa : 0xa3c1 >
+
+XDIGIT 0xa3c1 - 0xa3c6 0xa3e1 - 0xa3e6
+
+TODIGIT < 0xa3b0 - 0xa3b9 : 0 >
+TODIGIT < 0xa3c1 - 0xa3c6 : 10 >
+TODIGIT < 0xa3e1 - 0xa3e6 : 10 >
+
+PHONOGRAM 0xa4a1 - 0xa4f3
+PHONOGRAM 0xa5a1 - 0xa5f6
+
+UPPER 0xa6a1 - 0xa6b8 /* Greek */
+LOWER 0xa6c1 - 0xa6d8 /* Greek */
+MAPLOWER < 0xa6a1 - 0xa6b8 : 0xa6c1 >
+MAPLOWER < 0xa6c1 - 0xa6d8 : 0xa6c1 >
+MAPUPPER < 0xa6a1 - 0xa6b8 : 0xa6a1 >
+MAPUPPER < 0xa6c1 - 0xa6d8 : 0xa6a1 >
+
+UPPER 0xa7a1 - 0xa7c1 /* Cyrillic */
+LOWER 0xa7d1 - 0xa7f1 /* Cyrillic */
+MAPLOWER < 0xa7a1 - 0xa7c1 : 0xa7d1 >
+MAPLOWER < 0xa7d1 - 0xa7f1 : 0xa7d1 >
+MAPUPPER < 0xa7a1 - 0xa7c1 : 0xa7a1 >
+MAPUPPER < 0xa7d1 - 0xa7f1 : 0xa7a1 >
+
+SPECIAL 0xa8a1 - 0xa8c0
+
+IDEOGRAM 0xb0a1 - 0xb0fe
+IDEOGRAM 0xb1a1 - 0xb1fe
+IDEOGRAM 0xb2a1 - 0xb2fe
+IDEOGRAM 0xb3a1 - 0xb3fe
+IDEOGRAM 0xb4a1 - 0xb4fe
+IDEOGRAM 0xb5a1 - 0xb5fe
+IDEOGRAM 0xb6a1 - 0xb6fe
+IDEOGRAM 0xb7a1 - 0xb7fe
+IDEOGRAM 0xb8a1 - 0xb8fe
+IDEOGRAM 0xb9a1 - 0xb9fe
+IDEOGRAM 0xbaa1 - 0xbafe
+IDEOGRAM 0xbba1 - 0xbbfe
+IDEOGRAM 0xbca1 - 0xbcfe
+IDEOGRAM 0xbda1 - 0xbdfe
+IDEOGRAM 0xbea1 - 0xbefe
+IDEOGRAM 0xbfa1 - 0xbffe
+IDEOGRAM 0xc0a1 - 0xc0fe
+IDEOGRAM 0xc1a1 - 0xc1fe
+IDEOGRAM 0xc2a1 - 0xc2fe
+IDEOGRAM 0xc3a1 - 0xc3fe
+IDEOGRAM 0xc4a1 - 0xc4fe
+IDEOGRAM 0xc5a1 - 0xc5fe
+IDEOGRAM 0xc6a1 - 0xc6fe
+IDEOGRAM 0xc7a1 - 0xc7fe
+IDEOGRAM 0xc8a1 - 0xc8fe
+IDEOGRAM 0xc9a1 - 0xc9fe
+IDEOGRAM 0xcaa1 - 0xcafe
+IDEOGRAM 0xcba1 - 0xcbfe
+IDEOGRAM 0xcca1 - 0xccfe
+IDEOGRAM 0xcda1 - 0xcdfe
+IDEOGRAM 0xcea1 - 0xcefe
+IDEOGRAM 0xcfa1 - 0xcfd3
+IDEOGRAM 0xd0a1 - 0xd0fe
+IDEOGRAM 0xd1a1 - 0xd1fe
+IDEOGRAM 0xd2a1 - 0xd2fe
+IDEOGRAM 0xd3a1 - 0xd3fe
+IDEOGRAM 0xd4a1 - 0xd4fe
+IDEOGRAM 0xd5a1 - 0xd5fe
+IDEOGRAM 0xd6a1 - 0xd6fe
+IDEOGRAM 0xd7a1 - 0xd7fe
+IDEOGRAM 0xd8a1 - 0xd8fe
+IDEOGRAM 0xd9a1 - 0xd9fe
+IDEOGRAM 0xdaa1 - 0xdafe
+IDEOGRAM 0xdba1 - 0xdbfe
+IDEOGRAM 0xdca1 - 0xdcfe
+IDEOGRAM 0xdda1 - 0xddfe
+IDEOGRAM 0xdea1 - 0xdefe
+IDEOGRAM 0xdfa1 - 0xdffe
+IDEOGRAM 0xe0a1 - 0xe0fe
+IDEOGRAM 0xe1a1 - 0xe1fe
+IDEOGRAM 0xe2a1 - 0xe2fe
+IDEOGRAM 0xe3a1 - 0xe3fe
+IDEOGRAM 0xe4a1 - 0xe4fe
+IDEOGRAM 0xe5a1 - 0xe5fe
+IDEOGRAM 0xe6a1 - 0xe6fe
+IDEOGRAM 0xe7a1 - 0xe7fe
+IDEOGRAM 0xe8a1 - 0xe8fe
+IDEOGRAM 0xe9a1 - 0xe9fe
+IDEOGRAM 0xeaa1 - 0xeafe
+IDEOGRAM 0xeba1 - 0xebfe
+IDEOGRAM 0xeca1 - 0xecfe
+IDEOGRAM 0xeda1 - 0xedfe
+IDEOGRAM 0xeea1 - 0xeefe
+IDEOGRAM 0xefa1 - 0xeffe
+IDEOGRAM 0xf0a1 - 0xf0fe
+IDEOGRAM 0xf1a1 - 0xf1fe
+IDEOGRAM 0xf2a1 - 0xf2fe
+IDEOGRAM 0xf3a1 - 0xf3fe
+IDEOGRAM 0xf4a1 - 0xf4a4
+
+/*
+ * This is for Code Set 3, half-width kana
+ */
+SPECIAL 0xa1 - 0xdf
+PHONOGRAM 0xa1 - 0xdf
+CONTROL 0x84 - 0x97 0x9b - 0x9f 0xe0 - 0xfe
diff --git a/usr.bin/mklocale/ldef.h b/usr.bin/mklocale/ldef.h
new file mode 100644
index 0000000..532787c
--- /dev/null
+++ b/usr.bin/mklocale/ldef.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Borman at Krystal Technologies.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ldef.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This should look a LOT like a _RuneEntry
+ */
+typedef struct rune_list {
+ rune_t min;
+ rune_t max;
+ rune_t map;
+ unsigned long *types;
+ struct rune_list *next;
+} rune_list;
+
+typedef struct rune_map {
+ unsigned long map[_CACHED_RUNES];
+ rune_list *root;
+} rune_map;
diff --git a/usr.bin/mklocale/lex.l b/usr.bin/mklocale/lex.l
new file mode 100644
index 0000000..0e2f1a9
--- /dev/null
+++ b/usr.bin/mklocale/lex.l
@@ -0,0 +1,152 @@
+%{
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Borman at Krystal Technologies.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lex.l 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ldef.h"
+#include "y.tab.h"
+%}
+
+ODIGIT [0-7]
+DIGIT [0-9]
+XDIGIT [0-9a-fA-F]
+W [\t\n\r ]
+
+%%
+\'.\' { yylval.rune = yytext[1];
+ return(RUNE); }
+
+'\\a' { yylval.rune = '\a';
+ return(RUNE); }
+'\\b' { yylval.rune = '\b';
+ return(RUNE); }
+'\\f' { yylval.rune = '\f';
+ return(RUNE); }
+'\\n' { yylval.rune = '\n';
+ return(RUNE); }
+'\\r' { yylval.rune = '\r';
+ return(RUNE); }
+'\\t' { yylval.rune = '\t';
+ return(RUNE); }
+'\\v' { yylval.rune = '\v';
+ return(RUNE); }
+
+0x{XDIGIT}+ { yylval.rune = strtol(yytext, 0, 16);
+ return(RUNE); }
+0{ODIGIT}+ { yylval.rune = strtol(yytext, 0, 8);
+ return(RUNE); }
+{DIGIT}+ { yylval.rune = strtol(yytext, 0, 10);
+ return(RUNE); }
+
+
+MAPLOWER { return(MAPLOWER); }
+MAPUPPER { return(MAPUPPER); }
+TODIGIT { return(DIGITMAP); }
+INVALID { return(INVALID); }
+
+ALPHA { yylval.i = _A|_R|_G; return(LIST); }
+CONTROL { yylval.i = _C; return(LIST); }
+DIGIT { yylval.i = _D|_R|_G; return(LIST); }
+GRAPH { yylval.i = _G|_R; return(LIST); }
+LOWER { yylval.i = _L|_R|_G; return(LIST); }
+PUNCT { yylval.i = _P|_R|_G; return(LIST); }
+SPACE { yylval.i = _S; return(LIST); }
+UPPER { yylval.i = _U|_R|_G; return(LIST); }
+XDIGIT { yylval.i = _X|_R|_G; return(LIST); }
+BLANK { yylval.i = _B; return(LIST); }
+PRINT { yylval.i = _R; return(LIST); }
+IDEOGRAM { yylval.i = _I|_R|_G; return(LIST); }
+SPECIAL { yylval.i = _T|_R|_G; return(LIST); }
+PHONOGRAM { yylval.i = _Q|_R|_G; return(LIST); }
+
+VARIABLE[\t ] { static char vbuf[1024];
+ char *v = vbuf;
+ while ((*v = input()) && *v != '\n')
+ ++v;
+ if (*v) {
+ unput(*v);
+ *v = 0;
+ }
+ yylval.str = vbuf;
+ return(VARIABLE);
+ }
+
+ENCODING { return(ENCODING); }
+
+\".*\" { char *e = yytext + 1;
+ yylval.str = e;
+ while (*e && *e != '"')
+ ++e;
+ *e = 0;
+ return(STRING); }
+
+\<|\(|\[ { return(LBRK); }
+
+\>|\)|\] { return(RBRK); }
+
+\- { return(THRU); }
+\.\.\. { return(THRU); }
+
+\: { return(':'); }
+
+{W}+ ;
+
+^\#.*\n ;
+\/\* { char lc = 0;
+ do {
+ while ((lc) != '*')
+ if ((lc = input()) == 0)
+ break;
+ } while((lc = input()) != '/');
+ }
+
+\\$ ;
+. { printf("Lex is skipping '%s'\n", yytext); }
+%%
+
+#if !defined(yywrap)
+yywrap()
+{
+ return(1);
+}
+#endif
diff --git a/usr.bin/mklocale/mklocale.1 b/usr.bin/mklocale/mklocale.1
new file mode 100644
index 0000000..b0b0c62
--- /dev/null
+++ b/usr.bin/mklocale/mklocale.1
@@ -0,0 +1,257 @@
+.\" Copyright (c) 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Paul Borman at Krystal Technologies.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mklocale.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd "April 18, 1994"
+.Dt MKLOCALE 1
+.Os
+.Sh NAME
+.Nm mklocale
+.Nd make LC_CTYPE locale files
+.Sh SYNOPSIS
+.Nm mklocale
+.Ar "< src-file"
+.Ar "> language/LC_CTYPE"
+.Sh DESCRIPTION
+The
+.Nm mklocale
+utility reads a
+.Dv LC_CTYPE
+source file from standard input and produces a
+.Dv LC_CTYPE
+binary file on standard output suitable for placement in
+.Dv /usr/share/locale/\fIlanguage\fP/LC_CTYPE.
+.Pp
+The format of
+.Ar src-file
+is quite simple.
+It consists of a series of lines which start with a keyword and have
+associated data following. C style comments are used
+to place comments in the file.
+.Pp
+Besides the keywords which will be listed below,
+the following are valid tokens in
+.Ar src-file :
+.Bl -tag -width literal
+.It Dv RUNE
+A
+.Dv RUNE
+may be any of the following:
+.Bl -tag -width 0x[0-9a-z]*
+.It Ar 'x'
+The ascii character
+.Ar x .
+.It Ar '\ex'
+The ANSI C character
+.Ar \ex
+where
+.Ar \ex
+is one of
+.Dv \ea ,
+.Dv \eb ,
+.Dv \ef ,
+.Dv \en ,
+.Dv \er ,
+.Dv \et ,
+or
+.Dv \ev .
+.It Ar 0x[0-9a-z]*
+A hexadecimal number representing a rune code.
+.It Ar 0[0-7]*
+An octal number representing a rune code.
+.It Ar [1-9][0-9]*
+A decimal number representing a rune code.
+.El
+.It Dv STRING
+A string enclosed in double quotes (").
+.It Dv THRU
+Either
+.Dv ...
+or
+.Dv - .
+Used to indicate ranges.
+.It Ar literal
+The follow characters are taken literally:
+.Bl -tag -width "<\|\|(\|\|["
+.It Dv "<\|(\|["
+Used to start a mapping. All are equivalent.
+.It Dv ">\|\^)\|]"
+Used to end a mapping. All are equivalent.
+.It Dv :
+Used as a delimiter in mappings.
+.El
+.El
+.sp
+Key words which should only appear once are:
+.Bl -tag -width PHONOGRAM
+.It Dv ENCODING
+Followed by a
+.Dv STRING
+which indicates the encoding mechanism to be used for this locale.
+The current encodings are:
+.Bl -tag -width NONE
+.It Dv NONE
+No translation and the default.
+.It Dv UTF2
+.Dv "Universal character set Transformation Format"
+adopted from
+.Nm "Plan 9 from Bell Labs" .
+This is the preferred encoding.
+.It Dv EUC
+.Dv EUC
+encoding as used by several
+vendors of
+.Ux
+systems.
+.El
+.It Dv VARIABLE
+This keyword must be followed by a single tab or space character,
+after which encoding specific data is placed.
+Currently only the
+.Dv "EUC"
+encoding requires variable data.
+See
+.Xr euc 4
+for further details.
+.It Dv INVALID
+A single
+.Dv RUNE
+follows and is used as the invalid rune for this locale.
+.El
+.sp
+The following keywords may appear multiple times and have the following
+format for data:
+.in +.5i
+.Bl -tag -width "<RUNE1 THRU RUNEn : RUNE2>"
+.It Dv <RUNE1 RUNE2>
+.Dv RUNE1
+is mapped to
+.Dv RUNE2 .
+.It Dv <RUNE1 THRU RUNEn : RUNE2>
+Runes
+.Dv RUNE1
+through
+.Dv RUNEn
+are mapped to
+.Dv RUNE2
+through
+.Dv RUNE2
++ n-1.
+.El
+.in -.5i
+.Bl -tag -width PHONOGRAM
+.It Dv MAPLOWER
+Defines the tolower mappings.
+.Dv RUNE2
+is the lower case representation of
+.Dv RUNE1.
+.It Dv MAPUPPER
+Defines the toupper mappings.
+.Dv RUNE2
+is the upper case representation of
+.Dv RUNE1.
+.It Dv TODIGIT
+Defines a map from runes to their digit value.
+.Dv RUNE2
+is the integer value represented by
+.Dv RUNE1 .
+For example, the ascii character
+.Nm '0'
+would map to the decimal value
+.Nm 0 .
+Only values up to
+.Nm 255
+are allowed.
+.El
+.sp
+The following keywords may appear multiple times and have the following
+format for data:
+.in +.5i
+.Bl -tag -width "RUNE1 THRU RUNEn"
+.It Dv RUNE
+This rune has the property defined by the keyword.
+.It Dv "RUNE1 THRU RUNEn"
+All the runes between and including
+.Dv RUNE1
+and
+.Dv RUNEn
+have the property defined by the keyword.
+.El
+.in -.5i
+.Bl -tag -width PHONOGRAM
+.It Dv ALPHA
+Defines runes which are alphabetic, printable and graphic.
+.It Dv CONTROL
+Defines runes which are control characters.
+.It Dv DIGIT
+Defines runes which are decimal digits, printable and graphic.
+.It Dv GRAPH
+Defines runes which are graphic and printable.
+.It Dv LOWER
+Defines runes which are lower case, printable and graphic.
+.It Dv PUNCT
+Defines runes which are punctuation, printable and graphic.
+.It Dv SPACE
+Defines runes which are spaces.
+.It Dv UPPER
+Defines runes which are upper case, printable and graphic.
+.It Dv XDIGIT
+Defines runes which are hexadecimal digits, printable and graphic.
+.It Dv BLANK
+Defines runes which are blank.
+.It Dv PRINT
+Defines runes which are printable.
+.It Dv IDEOGRAM
+Defines runes which are ideograms, printable and graphic.
+.It Dv SPECIAL
+Defines runes which are special characters, printable and graphic.
+.It Dv PHONOGRAM
+Defines runes which are phonograms, printable and graphic.
+.El
+.Sh SEE ALSO
+.Xr mbrune 3 ,
+.Xr rune 3 ,
+.Xr setlocale 3 ,
+.Xr euc 4 ,
+.Xr utf2 4
+.Sh BUGS
+The
+.Nm mklocale
+utility is overly simplistic.
+.Sh HISTORY
+The
+.Nm mklocale
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.bin/mklocale/ru_SU.KOI8-R b/usr.bin/mklocale/ru_SU.KOI8-R
new file mode 100644
index 0000000..d2164c4
--- /dev/null
+++ b/usr.bin/mklocale/ru_SU.KOI8-R
@@ -0,0 +1,39 @@
+/*
+ * LOCALE_CTYPE for Russian koi8-r character set (RFC1489)
+ */
+ENCODING "NONE"
+VARIABLE Russian koi8-r character set by ache@astral.msk.su
+
+#
+# This is a comment
+#
+ALPHA 'A' - 'Z' 'a' - 'z' 0xa3 0xb3 0xc0 - 0xff
+CONTROL 0x00 - 0x1f 0x7f
+DIGIT '0' - '9'
+GRAPH 0x21 - 0x7e 0x80 - 0xff
+LOWER 'a' - 'z' 0xa3 0xc0 - 0xdf
+PUNCT 0x21 - 0x2f 0x3a - 0x40 0x5b - 0x60 0x7b - 0x7e
+SPACE 0x09 - 0x0d 0x20
+UPPER 'A' - 'Z' 0xb3 0xe0 - 0xff
+XDIGIT 'a' - 'f' 'A' - 'F'
+BLANK ' ' '\t'
+PRINT 0x20 - 0x7e 0x80 - 0xff
+# IDEOGRAM
+# SPECIAL
+# PHONEGRAM
+
+MAPLOWER <'A' - 'Z' : 'a'>
+MAPLOWER <'a' - 'z' : 'a'>
+MAPLOWER <0xb3 0xa3>
+MAPLOWER <0xa3 0xa3>
+MAPLOWER <0xe0 - 0xff : 0xc0>
+MAPLOWER <0xc0 - 0xdf : 0xc0>
+MAPUPPER <'A' - 'Z' : 'A'>
+MAPUPPER <'a' - 'z' : 'A'>
+MAPUPPER <0xb3 0xb3>
+MAPUPPER <0xa3 0xb3>
+MAPUPPER <0xe0 - 0xff : 0xe0>
+MAPUPPER <0xc0 - 0xdf : 0xe0>
+TODIGIT <'0' - '9' : 0>
+TODIGIT <'A' - 'F' : 10>
+TODIGIT <'a' - 'f' : 10>
diff --git a/usr.bin/mklocale/yacc.y b/usr.bin/mklocale/yacc.y
new file mode 100644
index 0000000..8437dc3
--- /dev/null
+++ b/usr.bin/mklocale/yacc.y
@@ -0,0 +1,823 @@
+%{
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Paul Borman at Krystal Technologies.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)yacc.y 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <rune.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ldef.h"
+
+char *locale_file = "<stdout>";
+
+rune_map maplower = { 0, };
+rune_map mapupper = { 0, };
+rune_map types = { 0, };
+
+_RuneLocale new_locale = { 0, };
+
+void set_map __P((rune_map *, rune_list *, unsigned long));
+void set_digitmap __P((rune_map *, rune_list *));
+void add_map __P((rune_map *, rune_list *, unsigned long));
+%}
+
+%union {
+ rune_t rune;
+ int i;
+ char *str;
+
+ rune_list *list;
+}
+
+%token <rune> RUNE
+%token LBRK
+%token RBRK
+%token THRU
+%token MAPLOWER
+%token MAPUPPER
+%token DIGITMAP
+%token <i> LIST
+%token <str> VARIABLE
+%token ENCODING
+%token INVALID
+%token <str> STRING
+
+%type <list> list
+%type <list> map
+
+
+%%
+
+locale : /* empty */
+ | table
+ { dump_tables(); }
+ ;
+
+table : entry
+ | table entry
+ ;
+
+entry : ENCODING STRING
+ { strncpy(new_locale.encoding, $2, sizeof(new_locale.encoding)); }
+ | VARIABLE
+ { new_locale.variable_len = strlen($1) + 1;
+ new_locale.variable = malloc(new_locale.variable_len);
+ strcpy((char *)new_locale.variable, $1);
+ }
+ | INVALID RUNE
+ { new_locale.invalid_rune = $2; }
+ | LIST list
+ { set_map(&types, $2, $1); }
+ | MAPLOWER map
+ { set_map(&maplower, $2, 0); }
+ | MAPUPPER map
+ { set_map(&mapupper, $2, 0); }
+ | DIGITMAP map
+ { set_digitmap(&types, $2); }
+ ;
+
+list : RUNE
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $1;
+ $$->max = $1;
+ $$->next = 0;
+ }
+ | RUNE THRU RUNE
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $1;
+ $$->max = $3;
+ $$->next = 0;
+ }
+ | list RUNE
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $2;
+ $$->max = $2;
+ $$->next = $1;
+ }
+ | list RUNE THRU RUNE
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $2;
+ $$->max = $4;
+ $$->next = $1;
+ }
+ ;
+
+map : LBRK RUNE RUNE RBRK
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $2;
+ $$->max = $2;
+ $$->map = $3;
+ $$->next = 0;
+ }
+ | map LBRK RUNE RUNE RBRK
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $3;
+ $$->max = $3;
+ $$->map = $4;
+ $$->next = $1;
+ }
+ | LBRK RUNE THRU RUNE ':' RUNE RBRK
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $2;
+ $$->max = $4;
+ $$->map = $6;
+ $$->next = 0;
+ }
+ | map LBRK RUNE THRU RUNE ':' RUNE RBRK
+ {
+ $$ = (rune_list *)malloc(sizeof(rune_list));
+ $$->min = $3;
+ $$->max = $5;
+ $$->map = $7;
+ $$->next = $1;
+ }
+ ;
+%%
+
+int debug = 0;
+FILE *fp = stdout;
+
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ int x;
+
+ extern char *optarg;
+ extern int optind;
+
+ while ((x = getopt(ac, av, "do:")) != EOF) {
+ switch(x) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'o':
+ locale_file = optarg;
+ if ((fp = fopen(locale_file, "w")) == 0) {
+ perror(locale_file);
+ exit(1);
+ }
+ break;
+ default:
+ usage:
+ fprintf(stderr, "Usage: mklocale [-d] [-o output] [source]\n");
+ exit(1);
+ }
+ }
+
+ switch (ac - optind) {
+ case 0:
+ break;
+ case 1:
+ if (freopen(av[optind], "r", stdin) == 0) {
+ perror(av[optind]);
+ exit(1);
+ }
+ break;
+ default:
+ goto usage;
+ }
+ for (x = 0; x < _CACHED_RUNES; ++x) {
+ mapupper.map[x] = x;
+ maplower.map[x] = x;
+ }
+ new_locale.invalid_rune = _INVALID_RUNE;
+ memcpy(new_locale.magic, _RUNE_MAGIC_1, sizeof(new_locale.magic));
+
+ yyparse();
+}
+
+yyerror(s)
+ char *s;
+{
+ fprintf(stderr, "%s\n", s);
+}
+
+void *
+xmalloc(sz)
+ unsigned int sz;
+{
+ void *r = malloc(sz);
+ if (!r) {
+ perror("xmalloc");
+ abort();
+ }
+ return(r);
+}
+
+unsigned long *
+xlalloc(sz)
+ unsigned int sz;
+{
+ unsigned long *r = (unsigned long *)malloc(sz * sizeof(unsigned long));
+ if (!r) {
+ perror("xlalloc");
+ abort();
+ }
+ return(r);
+}
+
+unsigned long *
+xrelalloc(old, sz)
+ unsigned long *old;
+ unsigned int sz;
+{
+ unsigned long *r = (unsigned long *)realloc((char *)old,
+ sz * sizeof(unsigned long));
+ if (!r) {
+ perror("xrelalloc");
+ abort();
+ }
+ return(r);
+}
+
+void
+set_map(map, list, flag)
+ rune_map *map;
+ rune_list *list;
+ unsigned long flag;
+{
+ while (list) {
+ rune_list *nlist = list->next;
+ add_map(map, list, flag);
+ list = nlist;
+ }
+}
+
+void
+set_digitmap(map, list)
+ rune_map *map;
+ rune_list *list;
+{
+ rune_t i;
+
+ while (list) {
+ rune_list *nlist = list->next;
+ for (i = list->min; i <= list->max; ++i) {
+ if (list->map + (i - list->min)) {
+ rune_list *tmp = (rune_list *)xmalloc(sizeof(rune_list));
+ tmp->min = i;
+ tmp->max = i;
+ add_map(map, tmp, list->map + (i - list->min));
+ }
+ }
+ free(list);
+ list = nlist;
+ }
+}
+
+void
+add_map(map, list, flag)
+ rune_map *map;
+ rune_list *list;
+ unsigned long flag;
+{
+ rune_t i;
+ rune_list *lr = 0;
+ rune_list *r;
+ rune_t run;
+
+ while (list->min < _CACHED_RUNES && list->min <= list->max) {
+ if (flag)
+ map->map[list->min++] |= flag;
+ else
+ map->map[list->min++] = list->map++;
+ }
+
+ if (list->min > list->max) {
+ free(list);
+ return;
+ }
+
+ run = list->max - list->min + 1;
+
+ if (!(r = map->root) || (list->max < r->min - 1)
+ || (!flag && list->max == r->min - 1)) {
+ if (flag) {
+ list->types = xlalloc(run);
+ for (i = 0; i < run; ++i)
+ list->types[i] = flag;
+ }
+ list->next = map->root;
+ map->root = list;
+ return;
+ }
+
+ for (r = map->root; r && r->max + 1 < list->min; r = r->next)
+ lr = r;
+
+ if (!r) {
+ /*
+ * We are off the end.
+ */
+ if (flag) {
+ list->types = xlalloc(run);
+ for (i = 0; i < run; ++i)
+ list->types[i] = flag;
+ }
+ list->next = 0;
+ lr->next = list;
+ return;
+ }
+
+ if (list->max < r->min - 1) {
+ /*
+ * We come before this range and we do not intersect it.
+ * We are not before the root node, it was checked before the loop
+ */
+ if (flag) {
+ list->types = xlalloc(run);
+ for (i = 0; i < run; ++i)
+ list->types[i] = flag;
+ }
+ list->next = lr->next;
+ lr->next = list;
+ return;
+ }
+
+ /*
+ * At this point we have found that we at least intersect with
+ * the range pointed to by `r', we might intersect with one or
+ * more ranges beyond `r' as well.
+ */
+
+ if (!flag && list->map - list->min != r->map - r->min) {
+ /*
+ * There are only two cases when we are doing case maps and
+ * our maps needn't have the same offset. When we are adjoining
+ * but not intersecting.
+ */
+ if (list->max + 1 == r->min) {
+ lr->next = list;
+ list->next = r;
+ return;
+ }
+ if (list->min - 1 == r->max) {
+ list->next = r->next;
+ r->next = list;
+ return;
+ }
+ fprintf(stderr, "Error: conflicting map entries\n");
+ exit(1);
+ }
+
+ if (list->min >= r->min && list->max <= r->max) {
+ /*
+ * Subset case.
+ */
+
+ if (flag) {
+ for (i = list->min; i <= list->max; ++i)
+ r->types[i - r->min] |= flag;
+ }
+ free(list);
+ return;
+ }
+ if (list->min <= r->min && list->max >= r->max) {
+ /*
+ * Superset case. Make him big enough to hold us.
+ * We might need to merge with the guy after him.
+ */
+ if (flag) {
+ list->types = xlalloc(list->max - list->min + 1);
+
+ for (i = list->min; i <= list->max; ++i)
+ list->types[i - list->min] = flag;
+
+ for (i = r->min; i <= r->max; ++i)
+ list->types[i - list->min] |= r->types[i - r->min];
+
+ free(r->types);
+ r->types = list->types;
+ } else {
+ r->map = list->map;
+ }
+ r->min = list->min;
+ r->max = list->max;
+ free(list);
+ } else if (list->min < r->min) {
+ /*
+ * Our tail intersects his head.
+ */
+ if (flag) {
+ list->types = xlalloc(r->max - list->min + 1);
+
+ for (i = r->min; i <= r->max; ++i)
+ list->types[i - list->min] = r->types[i - r->min];
+
+ for (i = list->min; i < r->min; ++i)
+ list->types[i - list->min] = flag;
+
+ for (i = r->min; i <= list->max; ++i)
+ list->types[i - list->min] |= flag;
+
+ free(r->types);
+ r->types = list->types;
+ } else {
+ r->map = list->map;
+ }
+ r->min = list->min;
+ free(list);
+ return;
+ } else {
+ /*
+ * Our head intersects his tail.
+ * We might need to merge with the guy after him.
+ */
+ if (flag) {
+ r->types = xrelalloc(r->types, list->max - r->min + 1);
+
+ for (i = list->min; i <= r->max; ++i)
+ r->types[i - r->min] |= flag;
+
+ for (i = r->max+1; i <= list->max; ++i)
+ r->types[i - r->min] = flag;
+ }
+ r->max = r->max;
+ free(list);
+ }
+
+ /*
+ * Okay, check to see if we grew into the next guy(s)
+ */
+ while ((lr = r->next) && r->max >= lr->min) {
+ if (flag) {
+ if (r->max >= lr->max) {
+ /*
+ * Good, we consumed all of him.
+ */
+ for (i = lr->min; i <= lr->max; ++i)
+ r->types[i - r->min] |= lr->types[i - lr->min];
+ } else {
+ /*
+ * "append" him on to the end of us.
+ */
+ r->types = xrelalloc(r->types, lr->max - r->min + 1);
+
+ for (i = lr->min; i <= r->max; ++i)
+ r->types[i - r->min] |= lr->types[i - lr->min];
+
+ for (i = r->max+1; i <= lr->max; ++i)
+ r->types[i - r->min] = lr->types[i - lr->min];
+
+ r->max = lr->max;
+ }
+ } else {
+ if (lr->max > r->max)
+ r->max = lr->max;
+ }
+
+ r->next = lr->next;
+
+ if (flag)
+ free(lr->types);
+ free(lr);
+ }
+}
+
+void
+dump_tables()
+{
+ int x;
+ rune_list *list;
+
+ /*
+ * See if we can compress some of the istype arrays
+ */
+ for(list = types.root; list; list = list->next) {
+ list->map = list->types[0];
+ for (x = 1; x < list->max - list->min + 1; ++x) {
+ if (list->types[x] != list->map) {
+ list->map = 0;
+ break;
+ }
+ }
+ }
+
+ new_locale.invalid_rune = htonl(new_locale.invalid_rune);
+
+ /*
+ * Fill in our tables. Do this in network order so that
+ * diverse machines have a chance of sharing data.
+ * (Machines like Crays cannot share with little machines due to
+ * word size. Sigh. We tried.)
+ */
+ for (x = 0; x < _CACHED_RUNES; ++x) {
+ new_locale.runetype[x] = htonl(types.map[x]);
+ new_locale.maplower[x] = htonl(maplower.map[x]);
+ new_locale.mapupper[x] = htonl(mapupper.map[x]);
+ }
+
+ /*
+ * Count up how many ranges we will need for each of the extents.
+ */
+ list = types.root;
+
+ while (list) {
+ new_locale.runetype_ext.nranges++;
+ list = list->next;
+ }
+ new_locale.runetype_ext.nranges = htonl(new_locale.runetype_ext.nranges);
+
+ list = maplower.root;
+
+ while (list) {
+ new_locale.maplower_ext.nranges++;
+ list = list->next;
+ }
+ new_locale.maplower_ext.nranges = htonl(new_locale.maplower_ext.nranges);
+
+ list = mapupper.root;
+
+ while (list) {
+ new_locale.mapupper_ext.nranges++;
+ list = list->next;
+ }
+ new_locale.mapupper_ext.nranges = htonl(new_locale.mapupper_ext.nranges);
+
+ new_locale.variable_len = htonl(new_locale.variable_len);
+
+ /*
+ * Okay, we are now ready to write the new locale file.
+ */
+
+ /*
+ * PART 1: The _RuneLocale structure
+ */
+ if (fwrite((char *)&new_locale, sizeof(new_locale), 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+ /*
+ * PART 2: The runetype_ext structures (not the actual tables)
+ */
+ list = types.root;
+
+ while (list) {
+ _RuneEntry re;
+
+ re.min = htonl(list->min);
+ re.max = htonl(list->max);
+ re.map = htonl(list->map);
+
+ if (fwrite((char *)&re, sizeof(re), 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+
+ list = list->next;
+ }
+ /*
+ * PART 3: The maplower_ext structures
+ */
+ list = maplower.root;
+
+ while (list) {
+ _RuneEntry re;
+
+ re.min = htonl(list->min);
+ re.max = htonl(list->max);
+ re.map = htonl(list->map);
+
+ if (fwrite((char *)&re, sizeof(re), 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+
+ list = list->next;
+ }
+ /*
+ * PART 4: The mapupper_ext structures
+ */
+ list = mapupper.root;
+
+ while (list) {
+ _RuneEntry re;
+
+ re.min = htonl(list->min);
+ re.max = htonl(list->max);
+ re.map = htonl(list->map);
+
+ if (fwrite((char *)&re, sizeof(re), 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+
+ list = list->next;
+ }
+ /*
+ * PART 5: The runetype_ext tables
+ */
+ list = types.root;
+
+ while (list) {
+ for (x = 0; x < list->max - list->min + 1; ++x)
+ list->types[x] = htonl(list->types[x]);
+
+ if (!list->map) {
+ if (fwrite((char *)&list->types,
+ (list->max - list->min + 1) * sizeof(unsigned long),
+ 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+ }
+ list = list->next;
+ }
+ /*
+ * PART 5: And finally the variable data
+ */
+ if (fwrite((char *)new_locale.variable,
+ ntohl(new_locale.variable_len), 1, fp) != 1) {
+ perror(locale_file);
+ exit(1);
+ }
+ fclose(fp);
+
+ if (!debug)
+ return;
+
+ if (new_locale.encoding[0])
+ fprintf(stderr, "ENCODING %s\n", new_locale.encoding);
+ if (new_locale.variable)
+ fprintf(stderr, "VARIABLE %s\n", new_locale.variable);
+
+ fprintf(stderr, "\nMAPLOWER:\n\n");
+
+ for (x = 0; x < _CACHED_RUNES; ++x) {
+ if (isprint(maplower.map[x]))
+ fprintf(stderr, " '%c'", maplower.map[x]);
+ else if (maplower.map[x])
+ fprintf(stderr, "%04x", maplower.map[x]);
+ else
+ fprintf(stderr, "%4x", 0);
+ if ((x & 0xf) == 0xf)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n");
+
+ for (list = maplower.root; list; list = list->next)
+ fprintf(stderr, "\t%04x - %04x : %04x\n", list->min, list->max, list->map);
+
+ fprintf(stderr, "\nMAPUPPER:\n\n");
+
+ for (x = 0; x < _CACHED_RUNES; ++x) {
+ if (isprint(mapupper.map[x]))
+ fprintf(stderr, " '%c'", mapupper.map[x]);
+ else if (mapupper.map[x])
+ fprintf(stderr, "%04x", mapupper.map[x]);
+ else
+ fprintf(stderr, "%4x", 0);
+ if ((x & 0xf) == 0xf)
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "\n");
+
+ for (list = mapupper.root; list; list = list->next)
+ fprintf(stderr, "\t%04x - %04x : %04x\n", list->min, list->max, list->map);
+
+
+ fprintf(stderr, "\nTYPES:\n\n");
+
+ for (x = 0; x < _CACHED_RUNES; ++x) {
+ unsigned long r = types.map[x];
+
+ if (r) {
+ if (isprint(x))
+ fprintf(stderr, " '%c': %2d", x, r & 0xff);
+ else
+ fprintf(stderr, "%04x: %2d", x, r & 0xff);
+
+ fprintf(stderr, " %4s", (r & _A) ? "alph" : "");
+ fprintf(stderr, " %4s", (r & _C) ? "ctrl" : "");
+ fprintf(stderr, " %4s", (r & _D) ? "dig" : "");
+ fprintf(stderr, " %4s", (r & _G) ? "graf" : "");
+ fprintf(stderr, " %4s", (r & _L) ? "low" : "");
+ fprintf(stderr, " %4s", (r & _P) ? "punc" : "");
+ fprintf(stderr, " %4s", (r & _S) ? "spac" : "");
+ fprintf(stderr, " %4s", (r & _U) ? "upp" : "");
+ fprintf(stderr, " %4s", (r & _X) ? "xdig" : "");
+ fprintf(stderr, " %4s", (r & _B) ? "blnk" : "");
+ fprintf(stderr, " %4s", (r & _R) ? "prnt" : "");
+ fprintf(stderr, " %4s", (r & _I) ? "ideo" : "");
+ fprintf(stderr, " %4s", (r & _T) ? "spec" : "");
+ fprintf(stderr, " %4s", (r & _Q) ? "phon" : "");
+ fprintf(stderr, "\n");
+ }
+ }
+
+ for (list = types.root; list; list = list->next) {
+ if (list->map && list->min + 3 < list->max) {
+ unsigned long r = list->map;
+
+ fprintf(stderr, "%04x: %2d", list->min, r & 0xff);
+
+ fprintf(stderr, " %4s", (r & _A) ? "alph" : "");
+ fprintf(stderr, " %4s", (r & _C) ? "ctrl" : "");
+ fprintf(stderr, " %4s", (r & _D) ? "dig" : "");
+ fprintf(stderr, " %4s", (r & _G) ? "graf" : "");
+ fprintf(stderr, " %4s", (r & _L) ? "low" : "");
+ fprintf(stderr, " %4s", (r & _P) ? "punc" : "");
+ fprintf(stderr, " %4s", (r & _S) ? "spac" : "");
+ fprintf(stderr, " %4s", (r & _U) ? "upp" : "");
+ fprintf(stderr, " %4s", (r & _X) ? "xdig" : "");
+ fprintf(stderr, " %4s", (r & _B) ? "blnk" : "");
+ fprintf(stderr, " %4s", (r & _R) ? "prnt" : "");
+ fprintf(stderr, " %4s", (r & _I) ? "ideo" : "");
+ fprintf(stderr, " %4s", (r & _T) ? "spec" : "");
+ fprintf(stderr, " %4s", (r & _Q) ? "phon" : "");
+ fprintf(stderr, "\n...\n");
+
+ fprintf(stderr, "%04x: %2d", list->max, r & 0xff);
+
+ fprintf(stderr, " %4s", (r & _A) ? "alph" : "");
+ fprintf(stderr, " %4s", (r & _C) ? "ctrl" : "");
+ fprintf(stderr, " %4s", (r & _D) ? "dig" : "");
+ fprintf(stderr, " %4s", (r & _G) ? "graf" : "");
+ fprintf(stderr, " %4s", (r & _L) ? "low" : "");
+ fprintf(stderr, " %4s", (r & _P) ? "punc" : "");
+ fprintf(stderr, " %4s", (r & _S) ? "spac" : "");
+ fprintf(stderr, " %4s", (r & _U) ? "upp" : "");
+ fprintf(stderr, " %4s", (r & _X) ? "xdig" : "");
+ fprintf(stderr, " %4s", (r & _B) ? "blnk" : "");
+ fprintf(stderr, " %4s", (r & _R) ? "prnt" : "");
+ fprintf(stderr, " %4s", (r & _I) ? "ideo" : "");
+ fprintf(stderr, " %4s", (r & _T) ? "spec" : "");
+ fprintf(stderr, " %4s", (r & _Q) ? "phon" : "");
+ fprintf(stderr, "\n");
+ } else
+ for (x = list->min; x <= list->max; ++x) {
+ unsigned long r = ntohl(list->types[x - list->min]);
+
+ if (r) {
+ fprintf(stderr, "%04x: %2d", x, r & 0xff);
+
+ fprintf(stderr, " %4s", (r & _A) ? "alph" : "");
+ fprintf(stderr, " %4s", (r & _C) ? "ctrl" : "");
+ fprintf(stderr, " %4s", (r & _D) ? "dig" : "");
+ fprintf(stderr, " %4s", (r & _G) ? "graf" : "");
+ fprintf(stderr, " %4s", (r & _L) ? "low" : "");
+ fprintf(stderr, " %4s", (r & _P) ? "punc" : "");
+ fprintf(stderr, " %4s", (r & _S) ? "spac" : "");
+ fprintf(stderr, " %4s", (r & _U) ? "upp" : "");
+ fprintf(stderr, " %4s", (r & _X) ? "xdig" : "");
+ fprintf(stderr, " %4s", (r & _B) ? "blnk" : "");
+ fprintf(stderr, " %4s", (r & _R) ? "prnt" : "");
+ fprintf(stderr, " %4s", (r & _I) ? "ideo" : "");
+ fprintf(stderr, " %4s", (r & _T) ? "spec" : "");
+ fprintf(stderr, " %4s", (r & _Q) ? "phon" : "");
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+}
diff --git a/usr.bin/mkstr/Makefile b/usr.bin/mkstr/Makefile
new file mode 100644
index 0000000..c8af4d8
--- /dev/null
+++ b/usr.bin/mkstr/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkstr
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mkstr/mkstr.1 b/usr.bin/mkstr/mkstr.1
new file mode 100644
index 0000000..f0493c1
--- /dev/null
+++ b/usr.bin/mkstr/mkstr.1
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mkstr.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MKSTR 1
+.Os
+.Sh NAME
+.Nm mkstr
+.Nd create an error message file by massaging C source
+.Sh SYNOPSIS
+.Nm mkstr
+.Op Fl
+.Ar messagefile
+.Ar prefix file ...
+.Sh DESCRIPTION
+.Nm Mkstr
+creates files containing error messages extracted from C source,
+and restructures the same C source, to utilize the created error message
+file.
+The intent of
+.Nm mkstr
+was to reduce the size of large programs and
+reduce swapping (see
+.Sx BUGS
+section below).
+.Pp
+.Nm Mkstr
+processes each of the specified
+.Ar files ,
+placing a restructured version of the input in a file whose name
+consists of the specified
+.Ar prefix
+and the original name.
+A typical usage of
+.Nm mkstr
+is
+.Bd -literal -offset indent
+mkstr pistrings xx *.c
+.Ed
+.Pp
+This command causes all the error messages from the C source
+files in the current directory to be placed in the file
+.Ar pistrings
+and restructured copies of the sources to be placed in
+files whose names are prefixed with
+.Ar \&xx .
+.Pp
+Options:
+.Bl -tag -width indent
+.It Fl
+Error messages are placed at the end of the specified
+message file for recompiling part of a large
+.Nm mkstr
+ed
+program.
+.El
+.Pp
+.Nm mkstr
+finds error messages in the source by
+searching for the string
+.Li \&`error("'
+in the input stream.
+Each time it occurs, the C string starting at the
+.Sq \&"\&
+is stored
+in the message file followed by a null character and a new-line character;
+The new source is restructured with
+.Xr lseek 2
+pointers into the error message file for retrieval.
+.Bd -literal -offset indent
+char efilname = "/usr/lib/pi_strings";
+int efil = -1;
+
+error(a1, a2, a3, a4)
+\&{
+ char buf[256];
+
+ if (efil < 0) {
+ efil = open(efilname, 0);
+ if (efil < 0) {
+oops:
+ perror(efilname);
+ exit 1 ;
+ }
+ }
+ if (lseek(efil, (long) a1, 0) \ read(efil, buf, 256) <= 0)
+ goto oops;
+ printf(buf, a2, a3, a4);
+}
+.Ed
+.Sh SEE ALSO
+.Xr lseek 2 ,
+.Xr xstr 1
+.Sh HISTORY
+.Nm Mkstr
+appeared in
+.Bx 3.0 .
+.Sh BUGS
+.Nm mkstr
+was intended for the limited architecture of the PDP 11 family.
+Very few programs actually use it. The pascal interpreter,
+.Xr \&pi 1
+and the editor,
+.Xr \&ex 1
+are two programs that are built this way.
+It is not an efficient method, the error messages
+should be stored in the program text.
diff --git a/usr.bin/mkstr/mkstr.c b/usr.bin/mkstr/mkstr.c
new file mode 100644
index 0000000..c08857a
--- /dev/null
+++ b/usr.bin/mkstr/mkstr.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkstr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#define ungetchar(c) ungetc(c, stdin)
+
+long ftell();
+char *calloc();
+/*
+ * mkstr - create a string error message file by massaging C source
+ *
+ * Bill Joy UCB August 1977
+ *
+ * Modified March 1978 to hash old messages to be able to recompile
+ * without addding messages to the message file (usually)
+ *
+ * Based on an earlier program conceived by Bill Joy and Chuck Haley
+ *
+ * Program to create a string error message file
+ * from a group of C programs. Arguments are the name
+ * of the file where the strings are to be placed, the
+ * prefix of the new files where the processed source text
+ * is to be placed, and the files to be processed.
+ *
+ * The program looks for 'error("' in the source stream.
+ * Whenever it finds this, the following characters from the '"'
+ * to a '"' are replaced by 'seekpt' where seekpt is a
+ * pointer into the error message file.
+ * If the '(' is not immediately followed by a '"' no change occurs.
+ *
+ * The optional '-' causes strings to be added at the end of the
+ * existing error message file for recompilation of single routines.
+ */
+
+
+FILE *mesgread, *mesgwrite;
+char *progname;
+char usagestr[] = "usage: %s [ - ] mesgfile prefix file ...\n";
+char name[100], *np;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char addon = 0;
+
+ argc--, progname = *argv++;
+ if (argc > 1 && argv[0][0] == '-')
+ addon++, argc--, argv++;
+ if (argc < 3)
+ fprintf(stderr, usagestr, progname), exit(1);
+ mesgwrite = fopen(argv[0], addon ? "a" : "w");
+ if (mesgwrite == NULL)
+ perror(argv[0]), exit(1);
+ mesgread = fopen(argv[0], "r");
+ if (mesgread == NULL)
+ perror(argv[0]), exit(1);
+ inithash();
+ argc--, argv++;
+ strcpy(name, argv[0]);
+ np = name + strlen(name);
+ argc--, argv++;
+ do {
+ strcpy(np, argv[0]);
+ if (freopen(name, "w", stdout) == NULL)
+ perror(name), exit(1);
+ if (freopen(argv[0], "r", stdin) == NULL)
+ perror(argv[0]), exit(1);
+ process();
+ argc--, argv++;
+ } while (argc > 0);
+ exit(0);
+}
+
+process()
+{
+ register char *cp;
+ register c;
+
+ for (;;) {
+ c = getchar();
+ if (c == EOF)
+ return;
+ if (c != 'e') {
+ putchar(c);
+ continue;
+ }
+ if (match("error(")) {
+ printf("error(");
+ c = getchar();
+ if (c != '"')
+ putchar(c);
+ else
+ copystr();
+ }
+ }
+}
+
+match(ocp)
+ char *ocp;
+{
+ register char *cp;
+ register c;
+
+ for (cp = ocp + 1; *cp; cp++) {
+ c = getchar();
+ if (c != *cp) {
+ while (ocp < cp)
+ putchar(*ocp++);
+ ungetchar(c);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+copystr()
+{
+ register c, ch;
+ char buf[512];
+ register char *cp = buf;
+
+ for (;;) {
+ c = getchar();
+ if (c == EOF)
+ break;
+ switch (c) {
+
+ case '"':
+ *cp++ = 0;
+ goto out;
+ case '\\':
+ c = getchar();
+ switch (c) {
+
+ case 'b':
+ c = '\b';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case '\n':
+ continue;
+ case 'f':
+ c = '\f';
+ break;
+ case '0':
+ c = 0;
+ break;
+ case '\\':
+ break;
+ default:
+ if (!octdigit(c))
+ break;
+ c -= '0';
+ ch = getchar();
+ if (!octdigit(ch))
+ break;
+ c <<= 7, c += ch - '0';
+ ch = getchar();
+ if (!octdigit(ch))
+ break;
+ c <<= 3, c+= ch - '0', ch = -1;
+ break;
+ }
+ }
+ *cp++ = c;
+ }
+out:
+ *cp = 0;
+ printf("%d", hashit(buf, 1, NULL));
+}
+
+octdigit(c)
+ char c;
+{
+
+ return (c >= '0' && c <= '7');
+}
+
+inithash()
+{
+ char buf[512];
+ int mesgpt = 0;
+
+ rewind(mesgread);
+ while (fgetNUL(buf, sizeof buf, mesgread) != NULL) {
+ hashit(buf, 0, mesgpt);
+ mesgpt += strlen(buf) + 2;
+ }
+}
+
+#define NBUCKETS 511
+
+struct hash {
+ long hval;
+ unsigned hpt;
+ struct hash *hnext;
+} *bucket[NBUCKETS];
+
+hashit(str, really, fakept)
+ char *str;
+ char really;
+ unsigned fakept;
+{
+ int i;
+ register struct hash *hp;
+ char buf[512];
+ long hashval = 0;
+ register char *cp;
+
+ if (really)
+ fflush(mesgwrite);
+ for (cp = str; *cp;)
+ hashval = (hashval << 1) + *cp++;
+ i = hashval % NBUCKETS;
+ if (i < 0)
+ i += NBUCKETS;
+ if (really != 0)
+ for (hp = bucket[i]; hp != 0; hp = hp->hnext)
+ if (hp->hval == hashval) {
+ fseek(mesgread, (long) hp->hpt, 0);
+ fgetNUL(buf, sizeof buf, mesgread);
+/*
+ fprintf(stderr, "Got (from %d) %s\n", hp->hpt, buf);
+*/
+ if (strcmp(buf, str) == 0)
+ break;
+ }
+ if (!really || hp == 0) {
+ hp = (struct hash *) calloc(1, sizeof *hp);
+ hp->hnext = bucket[i];
+ hp->hval = hashval;
+ hp->hpt = really ? ftell(mesgwrite) : fakept;
+ if (really) {
+ fwrite(str, sizeof (char), strlen(str) + 1, mesgwrite);
+ fwrite("\n", sizeof (char), 1, mesgwrite);
+ }
+ bucket[i] = hp;
+ }
+/*
+ fprintf(stderr, "%s hashed to %ld at %d\n", str, hp->hval, hp->hpt);
+*/
+ return (hp->hpt);
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+fgetNUL(obuf, rmdr, file)
+ char *obuf;
+ register int rmdr;
+ FILE *file;
+{
+ register c;
+ register char *buf = obuf;
+
+ while (--rmdr > 0 && (c = getc(file)) != 0 && c != EOF)
+ *buf++ = c;
+ *buf++ = 0;
+ getc(file);
+ return ((feof(file) || ferror(file)) ? NULL : 1);
+}
diff --git a/usr.bin/modstat/Makefile b/usr.bin/modstat/Makefile
new file mode 100644
index 0000000..a602bb9
--- /dev/null
+++ b/usr.bin/modstat/Makefile
@@ -0,0 +1,44 @@
+#
+# Makefile for modstat
+#
+# 25 May 93 Terry Lambert Original
+#
+# Copyright (c) 1993 Terrence R. Lambert.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Terrence R. Lambert.
+# 4. The name Terrence R. Lambert may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: Makefile,v 1.4 1994/02/09 15:02:19 deraadt Exp $
+#
+
+PROG= modstat
+MAN8= modstat.8
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/modstat/modstat.8 b/usr.bin/modstat/modstat.8
new file mode 100644
index 0000000..99667e2
--- /dev/null
+++ b/usr.bin/modstat/modstat.8
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1993 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: modstat.8,v 1.1 1994/08/19 12:14:04 davidg Exp $
+.\"
+.Dd June 7, 1993
+.Dt MODSTAT 8
+.Os
+.Sh NAME
+.Nm modstat
+.Nd display status of loaded kernel modules
+.Sh SYNOPSIS
+.Nm modstat
+.Op Fl i Ar id
+.Op Fl n Ar name
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the status of any loadable kernel modules
+present in the kernel.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i Ar id
+Display the status of only the module with this ID.
+.It Fl n Ar name
+Display the status of only the module with this name.
+.El
+.Sh DIAGNOSTICS
+The
+.Nm
+utility exits with a status of 0 on success
+and with a nonzero status if an error occurs.
+.Sh SEE ALSO
+.Xr lkm 4 ,
+.Xr modunload 8 ,
+.Xr modstat 8
+.Sh HISTORY
+The
+.Nm
+command was designed to be similar in functionality
+to the corresponding command in
+.Tn "SunOS 4.1.3" .
+.Sh AUTHOR
+.Bl -tag
+Terrence R. Lambert, terry@cs.weber.edu
+.El
diff --git a/usr.bin/modstat/modstat.c b/usr.bin/modstat/modstat.c
new file mode 100644
index 0000000..956acf4
--- /dev/null
+++ b/usr.bin/modstat/modstat.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1993 Terrence R. Lambert.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Terrence R. Lambert.
+ * 4. The name Terrence R. Lambert may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TERRENCE R. LAMBERT ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: modstat.c,v 1.2 1995/04/18 02:19:17 wpaul Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <a.out.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/lkm.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include "pathnames.h"
+
+void
+usage()
+{
+
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "modstat [-i <module id>] [-n <module name>]\n");
+ exit(1);
+}
+
+static char *type_names[] = {
+ "SYSCALL",
+ "VFS",
+ "DEV",
+ "STRMOD",
+ "EXEC",
+ "MISC"
+};
+
+int
+dostat(devfd, modnum, modname)
+ int devfd;
+ int modnum;
+ char *modname;
+{
+ struct lmc_stat sbuf;
+
+ if (modname != NULL)
+ strcpy(sbuf.name, modname);
+
+ sbuf.id = modnum;
+
+ if (ioctl(devfd, LMSTAT, &sbuf) == -1) {
+ switch (errno) {
+ case EINVAL: /* out of range */
+ return 2;
+ case ENOENT: /* no such entry */
+ return 1;
+ default: /* other error (EFAULT, etc) */
+ warn("LMSTAT");
+ return 4;
+ }
+ }
+
+ /*
+ * Decode this stat buffer...
+ */
+ printf("%-7s %3d %3d %08x %04x %8x %3d %s\n",
+ type_names[sbuf.type],
+ sbuf.id, /* module id */
+ sbuf.offset, /* offset into modtype struct */
+ sbuf.area, /* address module loaded at */
+ sbuf.size, /* size in pages(K) */
+ sbuf.private, /* kernel address of private area */
+ sbuf.ver, /* Version; always 1 for now */
+ sbuf.name /* name from private area */
+ );
+
+ /*
+ * Done (success).
+ */
+ return 0;
+}
+
+int devfd;
+
+void
+cleanup()
+{
+
+ close(devfd);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+ int modnum = -1;
+ char *modname = NULL;
+
+ while ((c = getopt(argc, argv, "i:n:")) != EOF) {
+ switch (c) {
+ case 'i':
+ modnum = atoi(optarg);
+ break; /* number */
+ case 'n':
+ modname = optarg;
+ break; /* name */
+ case '?':
+ usage();
+ default:
+ printf("default!\n");
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ /*
+ * Open the virtual device device driver for exclusive use (needed
+ * to ioctl() to retrive the loaded module(s) status).
+ */
+ if ((devfd = open(_PATH_LKM, O_RDONLY, 0)) == -1)
+ err(2, _PATH_LKM);
+
+ atexit(cleanup);
+
+ printf("Type Id Off Loadaddr Size Info Rev Module Name\n");
+
+ /*
+ * Oneshot?
+ */
+ if (modnum != -1 || modname != NULL) {
+ if (dostat(devfd, modnum, modname))
+ exit(3);
+ exit(0);
+ }
+
+ /*
+ * Start at 0 and work up until "EINVAL".
+ */
+ for (modnum = 0; dostat(devfd, modnum, NULL) < 2; modnum++)
+ ;
+
+ exit(0);
+}
diff --git a/usr.bin/modstat/pathnames.h b/usr.bin/modstat/pathnames.h
new file mode 100644
index 0000000..81f70f2
--- /dev/null
+++ b/usr.bin/modstat/pathnames.h
@@ -0,0 +1,3 @@
+#include <paths.h>
+
+#define _PATH_LKM "/dev/lkm"
diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile
new file mode 100644
index 0000000..aaccab0
--- /dev/null
+++ b/usr.bin/more/Makefile
@@ -0,0 +1,16 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.3 1995/02/21 03:46:45 wollman Exp $
+
+PROG= more
+CFLAGS+=-I${.CURDIR} -DTERMIOS
+SRCS= ch.c command.c decode.c help.c input.c line.c linenum.c main.c \
+ option.c os.c output.c position.c prim.c screen.c signal.c tags.c \
+ ttyin.c
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/more.help \
+ ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/more/ch.c b/usr.bin/more/ch.c
new file mode 100644
index 0000000..52e80d0
--- /dev/null
+++ b/usr.bin/more/ch.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Low level character input from the input file.
+ * We use these special purpose routines which optimize moving
+ * both forward and backward from the current read pointer.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <less.h>
+
+int file = -1; /* File descriptor of the input file */
+
+/*
+ * Pool of buffers holding the most recently used blocks of the input file.
+ */
+struct buf {
+ struct buf *next, *prev;
+ long block;
+ int datasize;
+ char data[BUFSIZ];
+};
+int nbufs;
+
+/*
+ * The buffer pool is kept as a doubly-linked circular list, in order from
+ * most- to least-recently used. The circular list is anchored by buf_anchor.
+ */
+#define END_OF_CHAIN ((struct buf *)&buf_anchor)
+#define buf_head buf_anchor.next
+#define buf_tail buf_anchor.prev
+
+static struct {
+ struct buf *next, *prev;
+} buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
+
+extern int ispipe, cbufs, sigs;
+
+/*
+ * Current position in file.
+ * Stored as a block number and an offset into the block.
+ */
+static long ch_block;
+static int ch_offset;
+
+/* Length of file, needed if input is a pipe. */
+static off_t ch_fsize;
+
+/* Number of bytes read, if input is standard input (a pipe). */
+static off_t last_piped_pos;
+
+/*
+ * Get the character pointed to by the read pointer. ch_get() is a macro
+ * which is more efficient to call than fch_get (the function), in the usual
+ * case that the block desired is at the head of the chain.
+ */
+#define ch_get() \
+ ((buf_head->block == ch_block && \
+ ch_offset < buf_head->datasize) ? \
+ (unsigned char)buf_head->data[ch_offset] : fch_get())
+
+static
+fch_get()
+{
+ extern int bs_mode;
+ register struct buf *bp;
+ register int n, ch;
+ register char *p, *t;
+ off_t pos, lseek();
+
+ /* look for a buffer holding the desired block. */
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == ch_block) {
+ if (ch_offset >= bp->datasize)
+ /*
+ * Need more data in this buffer.
+ */
+ goto read_more;
+ /*
+ * On a pipe, we don't sort the buffers LRU
+ * because this can cause gaps in the buffers.
+ * For example, suppose we've got 12 1K buffers,
+ * and a 15K input stream. If we read the first 12K
+ * sequentially, then jump to line 1, then jump to
+ * the end, the buffers have blocks 0,4,5,6,..,14.
+ * If we then jump to line 1 again and try to
+ * read sequentially, we're out of luck when we
+ * get to block 1 (we'd get the "pipe error" below).
+ * To avoid this, we only sort buffers on a pipe
+ * when we actually READ the data, not when we
+ * find it already buffered.
+ */
+ if (ispipe)
+ return((unsigned char)bp->data[ch_offset]);
+ goto found;
+ }
+ /*
+ * Block is not in a buffer. Take the least recently used buffer
+ * and read the desired block into it. If the LRU buffer has data
+ * in it, and input is a pipe, then try to allocate a new buffer first.
+ */
+ if (ispipe && buf_tail->block != (long)(-1))
+ (void)ch_addbuf(1);
+ bp = buf_tail;
+ bp->block = ch_block;
+ bp->datasize = 0;
+
+read_more:
+ pos = (ch_block * BUFSIZ) + bp->datasize;
+ if (ispipe) {
+ /*
+ * The data requested should be immediately after
+ * the last data read from the pipe.
+ */
+ if (pos != last_piped_pos) {
+ error("pipe error");
+ quit();
+ }
+ } else
+ (void)lseek(file, pos, L_SET);
+
+ /*
+ * Read the block.
+ * If we read less than a full block, we just return the
+ * partial block and pick up the rest next time.
+ */
+ n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
+ if (n == READ_INTR)
+ return (EOI);
+ if (n < 0) {
+ error("read error");
+ quit();
+ }
+ if (ispipe)
+ last_piped_pos += n;
+
+ p = &bp->data[bp->datasize];
+ bp->datasize += n;
+
+ /*
+ * Set an EOI marker in the buffered data itself. Then ensure the
+ * data is "clean": there are no extra EOI chars in the data and
+ * that the "meta" bit (the 0200 bit) is reset in each char;
+ * also translate \r\n sequences to \n if -u flag not set.
+ */
+ if (n == 0) {
+ ch_fsize = pos;
+ bp->data[bp->datasize++] = EOI;
+ }
+
+ if (bs_mode) {
+ for (p = &bp->data[bp->datasize]; --n >= 0;) {
+ *--p;
+ if (*p == EOI)
+ *p = 0200;
+ }
+ }
+ else {
+ for (t = p; --n >= 0; ++p) {
+ ch = *p;
+ if (ch == '\r' && n && p[1] == '\n') {
+ ++p;
+ *t++ = '\n';
+ }
+ else
+ *t++ = (ch == EOI) ? 0200 : ch;
+ }
+ if (p != t) {
+ bp->datasize -= p - t;
+ if (ispipe)
+ last_piped_pos -= p - t;
+ }
+ }
+
+found:
+ if (buf_head != bp) {
+ /*
+ * Move the buffer to the head of the buffer chain.
+ * This orders the buffer chain, most- to least-recently used.
+ */
+ bp->next->prev = bp->prev;
+ bp->prev->next = bp->next;
+
+ bp->next = buf_head;
+ bp->prev = END_OF_CHAIN;
+ buf_head->prev = bp;
+ buf_head = bp;
+ }
+
+ if (ch_offset >= bp->datasize)
+ /*
+ * After all that, we still don't have enough data.
+ * Go back and try again.
+ */
+ goto read_more;
+
+ return((unsigned char)bp->data[ch_offset]);
+}
+
+/*
+ * Determine if a specific block is currently in one of the buffers.
+ */
+static
+buffered(block)
+ long block;
+{
+ register struct buf *bp;
+
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ if (bp->block == block)
+ return(1);
+ return(0);
+}
+
+/*
+ * Seek to a specified position in the file.
+ * Return 0 if successful, non-zero if can't seek there.
+ */
+ch_seek(pos)
+ register off_t pos;
+{
+ long new_block;
+
+ new_block = pos / BUFSIZ;
+ if (!ispipe || pos == last_piped_pos || buffered(new_block)) {
+ /*
+ * Set read pointer.
+ */
+ ch_block = new_block;
+ ch_offset = pos % BUFSIZ;
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * Seek to the end of the file.
+ */
+ch_end_seek()
+{
+ off_t ch_length();
+
+ if (!ispipe)
+ return(ch_seek(ch_length()));
+
+ /*
+ * Do it the slow way: read till end of data.
+ */
+ while (ch_forw_get() != EOI)
+ if (sigs)
+ return(1);
+ return(0);
+}
+
+/*
+ * Seek to the beginning of the file, or as close to it as we can get.
+ * We may not be able to seek there if input is a pipe and the
+ * beginning of the pipe is no longer buffered.
+ */
+ch_beg_seek()
+{
+ register struct buf *bp, *firstbp;
+
+ /*
+ * Try a plain ch_seek first.
+ */
+ if (ch_seek((off_t)0) == 0)
+ return(0);
+
+ /*
+ * Can't get to position 0.
+ * Look thru the buffers for the one closest to position 0.
+ */
+ firstbp = bp = buf_head;
+ if (bp == END_OF_CHAIN)
+ return(1);
+ while ((bp = bp->next) != END_OF_CHAIN)
+ if (bp->block < firstbp->block)
+ firstbp = bp;
+ ch_block = firstbp->block;
+ ch_offset = 0;
+ return(0);
+}
+
+/*
+ * Return the length of the file, if known.
+ */
+off_t
+ch_length()
+{
+ off_t lseek();
+
+ if (ispipe)
+ return(ch_fsize);
+ return((off_t)(lseek(file, (off_t)0, L_XTND)));
+}
+
+/*
+ * Return the current position in the file.
+ */
+off_t
+ch_tell()
+{
+ return(ch_block * BUFSIZ + ch_offset);
+}
+
+/*
+ * Get the current char and post-increment the read pointer.
+ */
+ch_forw_get()
+{
+ register int c;
+
+ c = ch_get();
+ if (c != EOI && ++ch_offset >= BUFSIZ) {
+ ch_offset = 0;
+ ++ch_block;
+ }
+ return(c);
+}
+
+/*
+ * Pre-decrement the read pointer and get the new current char.
+ */
+ch_back_get()
+{
+ if (--ch_offset < 0) {
+ if (ch_block <= 0 || (ispipe && !buffered(ch_block-1))) {
+ ch_offset = 0;
+ return(EOI);
+ }
+ ch_offset = BUFSIZ - 1;
+ ch_block--;
+ }
+ return(ch_get());
+}
+
+/*
+ * Allocate buffers.
+ * Caller wants us to have a total of at least want_nbufs buffers.
+ * keep==1 means keep the data in the current buffers;
+ * otherwise discard the old data.
+ */
+ch_init(want_nbufs, keep)
+ int want_nbufs;
+ int keep;
+{
+ register struct buf *bp;
+ char message[80];
+
+ cbufs = nbufs;
+ if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs)) {
+ /*
+ * Cannot allocate enough buffers.
+ * If we don't have ANY, then quit.
+ * Otherwise, just report the error and return.
+ */
+ (void)sprintf(message, "cannot allocate %d buffers",
+ want_nbufs - nbufs);
+ error(message);
+ if (nbufs == 0)
+ quit();
+ return;
+ }
+
+ if (keep)
+ return;
+
+ /*
+ * We don't want to keep the old data,
+ * so initialize all the buffers now.
+ */
+ for (bp = buf_head; bp != END_OF_CHAIN; bp = bp->next)
+ bp->block = (long)(-1);
+ last_piped_pos = (off_t)0;
+ ch_fsize = NULL_POSITION;
+ (void)ch_seek((off_t)0);
+}
+
+/*
+ * Allocate some new buffers.
+ * The buffers are added to the tail of the buffer chain.
+ */
+ch_addbuf(nnew)
+ int nnew;
+{
+ register struct buf *bp;
+ register struct buf *newbufs;
+ char *calloc();
+
+ /*
+ * We don't have enough buffers.
+ * Allocate some new ones.
+ */
+ newbufs = (struct buf *)calloc((u_int)nnew, sizeof(struct buf));
+ if (newbufs == NULL)
+ return(1);
+
+ /*
+ * Initialize the new buffers and link them together.
+ * Link them all onto the tail of the buffer list.
+ */
+ nbufs += nnew;
+ cbufs = nbufs;
+ for (bp = &newbufs[0]; bp < &newbufs[nnew]; bp++) {
+ bp->next = bp + 1;
+ bp->prev = bp - 1;
+ bp->block = (long)(-1);
+ }
+ newbufs[nnew-1].next = END_OF_CHAIN;
+ newbufs[0].prev = buf_tail;
+ buf_tail->next = &newbufs[0];
+ buf_tail = &newbufs[nnew-1];
+ return(0);
+}
diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c
new file mode 100644
index 0000000..f451604
--- /dev/null
+++ b/usr.bin/more/command.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)command.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <less.h>
+#include "pathnames.h"
+
+#define NO_MCA 0
+#define MCA_DONE 1
+#define MCA_MORE 2
+
+extern int erase_char, kill_char, werase_char;
+extern int ispipe;
+extern int sigs;
+extern int quit_at_eof;
+extern int hit_eof;
+extern int sc_width;
+extern int sc_height;
+extern int sc_window;
+extern int curr_ac;
+extern int ac;
+extern int quitting;
+extern int scroll;
+extern int screen_trashed; /* The screen has been overwritten */
+
+static char cmdbuf[120]; /* Buffer for holding a multi-char command */
+static char *cp; /* Pointer into cmdbuf */
+static int cmd_col; /* Current column of the multi-char command */
+static int longprompt; /* if stat command instead of prompt */
+static int mca; /* The multicharacter command (action) */
+static int last_mca; /* The previous mca */
+static int number; /* The number typed by the user */
+static int wsearch; /* Search for matches (1) or non-matches (0) */
+
+#define CMD_RESET cp = cmdbuf /* reset command buffer to empty */
+#define CMD_EXEC lower_left(); flush()
+
+/* backspace in command buffer. */
+static
+cmd_erase()
+{
+ int c;
+ /*
+ * backspace past beginning of the string: this usually means
+ * abort the command.
+ */
+ if (cp == cmdbuf)
+ return(1);
+
+ /* erase an extra character, for the carat. */
+ c = *--cp;
+ if (CONTROL_CHAR(c)) {
+ backspace();
+ --cmd_col;
+ }
+
+ backspace();
+ --cmd_col;
+ return(0);
+}
+
+/* set up the display to start a new multi-character command. */
+start_mca(action, prompt)
+ int action;
+ char *prompt;
+{
+ lower_left();
+ clear_eol();
+ putstr(prompt);
+ cmd_col = strlen(prompt);
+ mca = action;
+}
+
+/*
+ * process a single character of a multi-character command, such as
+ * a number, or the pattern of a search command.
+ */
+static
+cmd_char(c)
+ int c;
+{
+ if (c == erase_char)
+ return(cmd_erase());
+ /* in this order, in case werase == erase_char */
+ if (c == werase_char) {
+ if (cp > cmdbuf) {
+ while (isspace(cp[-1]) && !cmd_erase());
+ while (!isspace(cp[-1]) && !cmd_erase());
+ while (isspace(cp[-1]) && !cmd_erase());
+ }
+ return(cp == cmdbuf);
+ }
+ if (c == kill_char) {
+ while (!cmd_erase());
+ return(1);
+ }
+ /*
+ * No room in the command buffer, or no room on the screen;
+ * {{ Could get fancy here; maybe shift the displayed line
+ * and make room for more chars, like ksh. }}
+ */
+ if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3)
+ bell();
+ else {
+ *cp++ = c;
+ if (CONTROL_CHAR(c)) {
+ putchr('^');
+ cmd_col++;
+ c &= ~0200;
+ c = CARAT_CHAR(c);
+ }
+ putchr(c);
+ cmd_col++;
+ }
+ return(0);
+}
+
+prompt()
+{
+ extern int linenums, short_file;
+ extern char *current_name, *firstsearch, *next_name;
+ off_t len, pos, ch_length(), position(), forw_line();
+ char pbuf[40];
+
+ /*
+ * if nothing is displayed yet, display starting from line 1;
+ * if search string provided, go there instead.
+ */
+ if (position(TOP) == NULL_POSITION) {
+ if (forw_line((off_t)0) == NULL_POSITION)
+ return(0);
+ if (!firstsearch || !search(1, firstsearch, 1, 1))
+ jump_back(1);
+ }
+ else if (screen_trashed)
+ repaint();
+
+ /* if no -e flag and we've hit EOF on the last file, quit. */
+ if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac)
+ quit();
+
+ /* select the proper prompt and display it. */
+ lower_left();
+ clear_eol();
+ if (longprompt) {
+ so_enter();
+ putstr(current_name);
+ putstr(":");
+ if (!ispipe) {
+ (void)sprintf(pbuf, " file %d/%d", curr_ac + 1, ac);
+ putstr(pbuf);
+ }
+ if (linenums) {
+ (void)sprintf(pbuf, " line %d", currline(BOTTOM));
+ putstr(pbuf);
+ }
+ if ((pos = position(BOTTOM)) != NULL_POSITION) {
+ (void)sprintf(pbuf, " byte %qd", pos);
+ putstr(pbuf);
+ if (!ispipe && (len = ch_length())) {
+ (void)sprintf(pbuf, "/%qd pct %qd%%",
+ len, ((100 * pos) / len));
+ putstr(pbuf);
+ }
+ }
+ so_exit();
+ longprompt = 0;
+ }
+ else {
+ so_enter();
+ putstr(current_name);
+ if (hit_eof)
+ if (next_name) {
+ putstr(": END (next file: ");
+ putstr(next_name);
+ putstr(")");
+ }
+ else
+ putstr(": END");
+ else if (!ispipe &&
+ (pos = position(BOTTOM)) != NULL_POSITION &&
+ (len = ch_length())) {
+ (void)sprintf(pbuf, " (%qd%%)", ((100 * pos) / len));
+ putstr(pbuf);
+ }
+ so_exit();
+ }
+ return(1);
+}
+
+/* get command character. */
+static
+getcc()
+{
+ extern int cmdstack;
+ int ch;
+ off_t position();
+
+ /* left over from error() routine. */
+ if (cmdstack) {
+ ch = cmdstack;
+ cmdstack = NULL;
+ return(ch);
+ }
+ if (cp > cmdbuf && position(TOP) == NULL_POSITION) {
+ /*
+ * Command is incomplete, so try to complete it.
+ * There are only two cases:
+ * 1. We have "/string" but no newline. Add the \n.
+ * 2. We have a number but no command. Treat as #g.
+ * (This is all pretty hokey.)
+ */
+ if (mca != A_DIGIT)
+ /* Not a number; must be search string */
+ return('\n');
+ else
+ /* A number; append a 'g' */
+ return('g');
+ }
+ return(getchr());
+}
+
+/* execute a multicharacter command. */
+static
+exec_mca()
+{
+ extern int file;
+ extern char *tagfile;
+ register char *p;
+ char *glob();
+
+ *cp = '\0';
+ CMD_EXEC;
+ switch (mca) {
+ case A_F_SEARCH:
+ (void)search(1, cmdbuf, number, wsearch);
+ break;
+ case A_B_SEARCH:
+ (void)search(0, cmdbuf, number, wsearch);
+ break;
+ case A_EXAMINE:
+ for (p = cmdbuf; isspace(*p); ++p);
+ (void)edit(glob(p));
+ break;
+ case A_TAGFILE:
+ for (p = cmdbuf; isspace(*p); ++p);
+ findtag(p);
+ if (tagfile == NULL)
+ break;
+ if (edit(tagfile))
+ (void)tagsearch();
+ break;
+ }
+}
+
+/* add a character to a multi-character command. */
+static
+mca_char(c)
+ int c;
+{
+ switch (mca) {
+ case 0: /* not in a multicharacter command. */
+ case A_PREFIX: /* in the prefix of a command. */
+ return(NO_MCA);
+ case A_DIGIT:
+ /*
+ * Entering digits of a number.
+ * Terminated by a non-digit.
+ */
+ if (!isascii(c) || !isdigit(c) &&
+ c != erase_char && c != kill_char && c != werase_char) {
+ /*
+ * Not part of the number.
+ * Treat as a normal command character.
+ */
+ *cp = '\0';
+ number = atoi(cmdbuf);
+ CMD_RESET;
+ mca = 0;
+ return(NO_MCA);
+ }
+ break;
+ }
+
+ /*
+ * Any other multicharacter command
+ * is terminated by a newline.
+ */
+ if (c == '\n' || c == '\r') {
+ exec_mca();
+ return(MCA_DONE);
+ }
+
+ /* append the char to the command buffer. */
+ if (cmd_char(c))
+ return(MCA_DONE);
+
+ return(MCA_MORE);
+}
+
+/*
+ * Main command processor.
+ * Accept and execute commands until a quit command, then return.
+ */
+commands()
+{
+ register int c;
+ register int action;
+
+ last_mca = 0;
+ scroll = (sc_height + 1) / 2;
+
+ for (;;) {
+ mca = 0;
+ number = 0;
+
+ /*
+ * See if any signals need processing.
+ */
+ if (sigs) {
+ psignals();
+ if (quitting)
+ quit();
+ }
+ /*
+ * Display prompt and accept a character.
+ */
+ CMD_RESET;
+ if (!prompt()) {
+ next_file(1);
+ continue;
+ }
+ noprefix();
+ c = getcc();
+
+again: if (sigs)
+ continue;
+
+ /*
+ * If we are in a multicharacter command, call mca_char.
+ * Otherwise we call cmd_decode to determine the
+ * action to be performed.
+ */
+ if (mca)
+ switch (mca_char(c)) {
+ case MCA_MORE:
+ /*
+ * Need another character.
+ */
+ c = getcc();
+ goto again;
+ case MCA_DONE:
+ /*
+ * Command has been handled by mca_char.
+ * Start clean with a prompt.
+ */
+ continue;
+ case NO_MCA:
+ /*
+ * Not a multi-char command
+ * (at least, not anymore).
+ */
+ break;
+ }
+
+ /* decode the command character and decide what to do. */
+ switch (action = cmd_decode(c)) {
+ case A_DIGIT: /* first digit of a number */
+ start_mca(A_DIGIT, ":");
+ goto again;
+ case A_F_SCREEN: /* forward one screen */
+ CMD_EXEC;
+ if (number <= 0 && (number = sc_window) <= 0)
+ number = sc_height - 1;
+ forward(number, 1);
+ break;
+ case A_B_SCREEN: /* backward one screen */
+ CMD_EXEC;
+ if (number <= 0 && (number = sc_window) <= 0)
+ number = sc_height - 1;
+ backward(number, 1);
+ break;
+ case A_F_LINE: /* forward N (default 1) line */
+ CMD_EXEC;
+ forward(number <= 0 ? 1 : number, 0);
+ break;
+ case A_B_LINE: /* backward N (default 1) line */
+ CMD_EXEC;
+ backward(number <= 0 ? 1 : number, 0);
+ break;
+ case A_F_SCROLL: /* forward N lines */
+ CMD_EXEC;
+ if (number > 0)
+ scroll = number;
+ forward(scroll, 0);
+ break;
+ case A_B_SCROLL: /* backward N lines */
+ CMD_EXEC;
+ if (number > 0)
+ scroll = number;
+ backward(scroll, 0);
+ break;
+ case A_FREPAINT: /* flush buffers and repaint */
+ if (!ispipe) {
+ ch_init(0, 0);
+ clr_linenum();
+ }
+ /* FALLTHROUGH */
+ case A_REPAINT: /* repaint the screen */
+ CMD_EXEC;
+ repaint();
+ break;
+ case A_GOLINE: /* go to line N, default 1 */
+ CMD_EXEC;
+ if (number <= 0)
+ number = 1;
+ jump_back(number);
+ break;
+ case A_PERCENT: /* go to percent of file */
+ CMD_EXEC;
+ if (number < 0)
+ number = 0;
+ else if (number > 100)
+ number = 100;
+ jump_percent(number);
+ break;
+ case A_GOEND: /* go to line N, default end */
+ CMD_EXEC;
+ if (number <= 0)
+ jump_forw();
+ else
+ jump_back(number);
+ break;
+ case A_STAT: /* print file name, etc. */
+ longprompt = 1;
+ continue;
+ case A_QUIT: /* exit */
+ quit();
+ case A_F_SEARCH: /* search for a pattern */
+ case A_B_SEARCH:
+ if (number <= 0)
+ number = 1;
+ start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
+ last_mca = mca;
+ wsearch = 1;
+ c = getcc();
+ if (c == '!') {
+ /*
+ * Invert the sense of the search; set wsearch
+ * to 0 and get a new character for the start
+ * of the pattern.
+ */
+ start_mca(action,
+ (action == A_F_SEARCH) ? "!/" : "!?");
+ wsearch = 0;
+ c = getcc();
+ }
+ goto again;
+ case A_AGAIN_SEARCH: /* repeat previous search */
+ if (number <= 0)
+ number = 1;
+ if (wsearch)
+ start_mca(last_mca,
+ (last_mca == A_F_SEARCH) ? "/" : "?");
+ else
+ start_mca(last_mca,
+ (last_mca == A_F_SEARCH) ? "!/" : "!?");
+ CMD_EXEC;
+ (void)search(mca == A_F_SEARCH, (char *)NULL,
+ number, wsearch);
+ break;
+ case A_HELP: /* help */
+ lower_left();
+ clear_eol();
+ putstr("help");
+ CMD_EXEC;
+ help();
+ break;
+ case A_TAGFILE: /* tag a new file */
+ CMD_RESET;
+ start_mca(A_TAGFILE, "Tag: ");
+ c = getcc();
+ goto again;
+ case A_FILE_LIST: /* show list of file names */
+ CMD_EXEC;
+ showlist();
+ repaint();
+ break;
+ case A_EXAMINE: /* edit a new file */
+ CMD_RESET;
+ start_mca(A_EXAMINE, "Examine: ");
+ c = getcc();
+ goto again;
+ case A_VISUAL: /* invoke the editor */
+ if (ispipe) {
+ error("Cannot edit standard input");
+ break;
+ }
+ CMD_EXEC;
+ editfile();
+ ch_init(0, 0);
+ clr_linenum();
+ break;
+ case A_NEXT_FILE: /* examine next file */
+ if (number <= 0)
+ number = 1;
+ next_file(number);
+ break;
+ case A_PREV_FILE: /* examine previous file */
+ if (number <= 0)
+ number = 1;
+ prev_file(number);
+ break;
+ case A_SETMARK: /* set a mark */
+ lower_left();
+ clear_eol();
+ start_mca(A_SETMARK, "mark: ");
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ setmark(c);
+ break;
+ case A_GOMARK: /* go to mark */
+ lower_left();
+ clear_eol();
+ start_mca(A_GOMARK, "goto mark: ");
+ c = getcc();
+ if (c == erase_char || c == kill_char)
+ break;
+ gomark(c);
+ break;
+ case A_PREFIX:
+ /*
+ * The command is incomplete (more chars are needed).
+ * Display the current char so the user knows what's
+ * going on and get another character.
+ */
+ if (mca != A_PREFIX)
+ start_mca(A_PREFIX, "");
+ if (CONTROL_CHAR(c)) {
+ putchr('^');
+ c &= ~0200;
+ c = CARAT_CHAR(c);
+ }
+ putchr(c);
+ c = getcc();
+ goto again;
+ default:
+ bell();
+ break;
+ }
+ }
+}
+
+editfile()
+{
+ extern char *current_file;
+ static int dolinenumber;
+ static char *editor;
+ char *base;
+ int c;
+ char buf[MAXPATHLEN * 2 + 20], *getenv();
+
+ if (editor == NULL) {
+ editor = getenv("EDITOR");
+
+ /* default editor is vi */
+ if (editor == NULL || *editor == '\0')
+ editor = _PATH_VI;
+
+ /* check last component in case of full path */
+ base = strrchr(editor, '/');
+ if (!base)
+ base = editor;
+ else
+ base++;
+
+ /* emacs also accepts vi-style +nnnn */
+ if (strcmp(base, "vi") == 0 || strcmp(base, "emacs") == 0)
+ dolinenumber = 1;
+ else
+ dolinenumber = 0;
+ }
+ if (dolinenumber && (c = currline(MIDDLE)))
+ (void)sprintf(buf, "%s +%d %s", editor, c, current_file);
+ else
+ (void)sprintf(buf, "%s %s", editor, current_file);
+ lsystem(buf);
+}
+
+showlist()
+{
+ extern int sc_width;
+ extern char **av;
+ register int indx, width;
+ int len;
+ char *p;
+
+ if (ac <= 0) {
+ error("No files provided as arguments.");
+ return;
+ }
+ for (width = indx = 0; indx < ac;) {
+ p = strcmp(av[indx], "-") ? av[indx] : "stdin";
+ len = strlen(p) + 1;
+ if (curr_ac == indx)
+ len += 2;
+ if (width + len + 1 >= sc_width) {
+ if (!width) {
+ if (curr_ac == indx)
+ putchr('[');
+ putstr(p);
+ if (curr_ac == indx)
+ putchr(']');
+ ++indx;
+ }
+ width = 0;
+ putchr('\n');
+ continue;
+ }
+ if (width)
+ putchr(' ');
+ if (curr_ac == indx)
+ putchr('[');
+ putstr(p);
+ if (curr_ac == indx)
+ putchr(']');
+ width += len;
+ ++indx;
+ }
+ putchr('\n');
+ error((char *)NULL);
+}
diff --git a/usr.bin/more/decode.c b/usr.bin/more/decode.c
new file mode 100644
index 0000000..83fa624
--- /dev/null
+++ b/usr.bin/more/decode.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)decode.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines to decode user commands.
+ *
+ * This is all table driven.
+ * A command table is a sequence of command descriptors.
+ * Each command descriptor is a sequence of bytes with the following format:
+ * <c1><c2>...<cN><0><action>
+ * The characters c1,c2,...,cN are the command string; that is,
+ * the characters which the user must type.
+ * It is terminated by a null <0> byte.
+ * The byte after the null byte is the action code associated
+ * with the command string.
+ *
+ * The default commands are described by cmdtable.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <less.h>
+
+/*
+ * Command table is ordered roughly according to expected
+ * frequency of use, so the common commands are near the beginning.
+ */
+#define CONTROL(c) ((c)&037)
+
+static char cmdtable[] = {
+ '\r',0, A_F_LINE,
+ '\n',0, A_F_LINE,
+ 'j',0, A_F_LINE,
+ 'k',0, A_B_LINE,
+ 'd',0, A_F_SCROLL,
+ CONTROL('D'),0, A_F_SCROLL,
+ 'u',0, A_B_SCROLL,
+ CONTROL('U'),0, A_B_SCROLL,
+ ' ',0, A_F_SCREEN,
+ 'f',0, A_F_SCREEN,
+ CONTROL('F'),0, A_F_SCREEN,
+ 'b',0, A_B_SCREEN,
+ CONTROL('B'),0, A_B_SCREEN,
+ 'R',0, A_FREPAINT,
+ 'r',0, A_REPAINT,
+ CONTROL('L'),0, A_REPAINT,
+ 'g',0, A_GOLINE,
+ 'p',0, A_PERCENT,
+ '%',0, A_PERCENT,
+ 'G',0, A_GOEND,
+ '0',0, A_DIGIT,
+ '1',0, A_DIGIT,
+ '2',0, A_DIGIT,
+ '3',0, A_DIGIT,
+ '4',0, A_DIGIT,
+ '5',0, A_DIGIT,
+ '6',0, A_DIGIT,
+ '7',0, A_DIGIT,
+ '8',0, A_DIGIT,
+ '9',0, A_DIGIT,
+
+ '=',0, A_STAT,
+ CONTROL('G'),0, A_STAT,
+ '/',0, A_F_SEARCH,
+ '?',0, A_B_SEARCH,
+ 'n',0, A_AGAIN_SEARCH,
+ 'm',0, A_SETMARK,
+ '\'',0, A_GOMARK,
+ 'E',0, A_EXAMINE,
+ 'N',0, A_NEXT_FILE,
+ ':','n',0, A_NEXT_FILE,
+ 'P',0, A_PREV_FILE,
+ ':','p',0, A_PREV_FILE,
+ 'v',0, A_VISUAL,
+
+ 'h',0, A_HELP,
+ 'q',0, A_QUIT,
+ ':','q',0, A_QUIT,
+ ':','t',0, A_TAGFILE,
+ ':', 'a', 0, A_FILE_LIST,
+ 'Z','Z',0, A_QUIT,
+};
+
+char *cmdendtable = cmdtable + sizeof(cmdtable);
+
+#define MAX_CMDLEN 16
+
+static char kbuf[MAX_CMDLEN+1];
+static char *kp = kbuf;
+
+/*
+ * Indicate that we're not in a prefix command
+ * by resetting the command buffer pointer.
+ */
+noprefix()
+{
+ kp = kbuf;
+}
+
+/*
+ * Decode a command character and return the associated action.
+ */
+cmd_decode(c)
+ int c;
+{
+ register int action = A_INVALID;
+
+ /*
+ * Append the new command character to the command string in kbuf.
+ */
+ *kp++ = c;
+ *kp = '\0';
+
+ action = cmd_search(cmdtable, cmdendtable);
+
+ /* This is not a prefix character. */
+ if (action != A_PREFIX)
+ noprefix();
+ return(action);
+}
+
+/*
+ * Search a command table for the current command string (in kbuf).
+ */
+cmd_search(table, endtable)
+ char *table;
+ char *endtable;
+{
+ register char *p, *q;
+
+ for (p = table, q = kbuf; p < endtable; p++, q++) {
+ if (*p == *q) {
+ /*
+ * Current characters match.
+ * If we're at the end of the string, we've found it.
+ * Return the action code, which is the character
+ * after the null at the end of the string
+ * in the command table.
+ */
+ if (*p == '\0')
+ return(p[1]);
+ }
+ else if (*q == '\0') {
+ /*
+ * Hit the end of the user's command,
+ * but not the end of the string in the command table.
+ * The user's command is incomplete.
+ */
+ return(A_PREFIX);
+ } else {
+ /*
+ * Not a match.
+ * Skip ahead to the next command in the
+ * command table, and reset the pointer
+ * to the user's command.
+ */
+ while (*p++ != '\0');
+ q = kbuf-1;
+ }
+ }
+ /*
+ * No match found in the entire command table.
+ */
+ return(A_INVALID);
+}
diff --git a/usr.bin/more/help.c b/usr.bin/more/help.c
new file mode 100644
index 0000000..eeb3795
--- /dev/null
+++ b/usr.bin/more/help.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)help.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <less.h>
+#include "pathnames.h"
+
+help()
+{
+ char cmd[MAXPATHLEN + 20];
+
+ (void)sprintf(cmd, "-more %s", _PATH_HELPFILE);
+ lsystem(cmd);
+}
diff --git a/usr.bin/more/input.c b/usr.bin/more/input.c
new file mode 100644
index 0000000..bdf65bb
--- /dev/null
+++ b/usr.bin/more/input.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * High level routines dealing with getting lines of input
+ * from the file being viewed.
+ *
+ * When we speak of "lines" here, we mean PRINTABLE lines;
+ * lines processed with respect to the screen width.
+ * We use the term "raw line" to refer to lines simply
+ * delimited by newlines; not processed with respect to screen width.
+ */
+
+#include <sys/types.h>
+#include <less.h>
+
+extern int squeeze;
+extern int sigs;
+extern char *line;
+
+off_t ch_tell();
+
+/*
+ * Get the next line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the NEXT line. The line obtained is the line starting at curr_pos.
+ */
+off_t
+forw_line(curr_pos)
+ off_t curr_pos;
+{
+ off_t new_pos;
+ register int c;
+
+ if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
+ return (NULL_POSITION);
+
+ c = ch_forw_get();
+ if (c == EOI)
+ return (NULL_POSITION);
+
+ prewind();
+ for (;;)
+ {
+ if (sigs)
+ return (NULL_POSITION);
+ if (c == '\n' || c == EOI)
+ {
+ /*
+ * End of the line.
+ */
+ new_pos = ch_tell();
+ break;
+ }
+
+ /*
+ * Append the char to the line and get the next char.
+ */
+ if (pappend(c))
+ {
+ /*
+ * The char won't fit in the line; the line
+ * is too long to print in the screen width.
+ * End the line here.
+ */
+ new_pos = ch_tell() - 1;
+ break;
+ }
+ c = ch_forw_get();
+ }
+ (void) pappend('\0');
+
+ if (squeeze && *line == '\0')
+ {
+ /*
+ * This line is blank.
+ * Skip down to the last contiguous blank line
+ * and pretend it is the one which we are returning.
+ */
+ while ((c = ch_forw_get()) == '\n')
+ if (sigs)
+ return (NULL_POSITION);
+ if (c != EOI)
+ (void) ch_back_get();
+ new_pos = ch_tell();
+ }
+
+ return (new_pos);
+}
+
+/*
+ * Get the previous line.
+ * A "current" position is passed and a "new" position is returned.
+ * The current position is the position of the first character of
+ * a line. The new position is the position of the first character
+ * of the PREVIOUS line. The line obtained is the one starting at new_pos.
+ */
+off_t
+back_line(curr_pos)
+ off_t curr_pos;
+{
+ off_t new_pos, begin_new_pos;
+ int c;
+
+ if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
+ ch_seek(curr_pos-1))
+ return (NULL_POSITION);
+
+ if (squeeze)
+ {
+ /*
+ * Find out if the "current" line was blank.
+ */
+ (void) ch_forw_get(); /* Skip the newline */
+ c = ch_forw_get(); /* First char of "current" line */
+ (void) ch_back_get(); /* Restore our position */
+ (void) ch_back_get();
+
+ if (c == '\n')
+ {
+ /*
+ * The "current" line was blank.
+ * Skip over any preceeding blank lines,
+ * since we skipped them in forw_line().
+ */
+ while ((c = ch_back_get()) == '\n')
+ if (sigs)
+ return (NULL_POSITION);
+ if (c == EOI)
+ return (NULL_POSITION);
+ (void) ch_forw_get();
+ }
+ }
+
+ /*
+ * Scan backwards until we hit the beginning of the line.
+ */
+ for (;;)
+ {
+ if (sigs)
+ return (NULL_POSITION);
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = ch_tell();
+ break;
+ }
+ }
+
+ /*
+ * Now scan forwards from the beginning of this line.
+ * We keep discarding "printable lines" (based on screen width)
+ * until we reach the curr_pos.
+ *
+ * {{ This algorithm is pretty inefficient if the lines
+ * are much longer than the screen width,
+ * but I don't know of any better way. }}
+ */
+ if (ch_seek(new_pos))
+ return (NULL_POSITION);
+ loop:
+ begin_new_pos = new_pos;
+ prewind();
+
+ do
+ {
+ c = ch_forw_get();
+ if (c == EOI || sigs)
+ return (NULL_POSITION);
+ new_pos++;
+ if (c == '\n')
+ break;
+ if (pappend(c))
+ {
+ /*
+ * Got a full printable line, but we haven't
+ * reached our curr_pos yet. Discard the line
+ * and start a new one.
+ */
+ (void) pappend('\0');
+ (void) ch_back_get();
+ new_pos--;
+ goto loop;
+ }
+ } while (new_pos < curr_pos);
+
+ (void) pappend('\0');
+
+ return (begin_new_pos);
+}
diff --git a/usr.bin/more/less.h b/usr.bin/more/less.h
new file mode 100644
index 0000000..c4dd23f
--- /dev/null
+++ b/usr.bin/more/less.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)less.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define RECOMP
+
+#define NULL_POSITION ((off_t)(-1))
+
+#define EOI (0)
+#define READ_INTR (-2)
+
+/* Special chars used to tell put_line() to do something special */
+#define UL_CHAR '\201' /* Enter underline mode */
+#define UE_CHAR '\202' /* Exit underline mode */
+#define BO_CHAR '\203' /* Enter boldface mode */
+#define BE_CHAR '\204' /* Exit boldface mode */
+
+#define CONTROL_CHAR(c) (!isprint(c))
+#define CARAT_CHAR(c) ((c == '\177') ? '?' : (c | 0100))
+
+#define TOP (0)
+#define TOP_PLUS_ONE (1)
+#define BOTTOM (-1)
+#define BOTTOM_PLUS_ONE (-2)
+#define MIDDLE (-3)
+
+#define A_INVALID -1
+
+#define A_AGAIN_SEARCH 1
+#define A_B_LINE 2
+#define A_B_SCREEN 3
+#define A_B_SCROLL 4
+#define A_B_SEARCH 5
+#define A_DIGIT 6
+#define A_EXAMINE 7
+#define A_FREPAINT 8
+#define A_F_LINE 9
+#define A_F_SCREEN 10
+#define A_F_SCROLL 11
+#define A_F_SEARCH 12
+#define A_GOEND 13
+#define A_GOLINE 14
+#define A_GOMARK 15
+#define A_HELP 16
+#define A_NEXT_FILE 17
+#define A_PERCENT 18
+#define A_PREFIX 19
+#define A_PREV_FILE 20
+#define A_QUIT 21
+#define A_REPAINT 22
+#define A_SETMARK 23
+#define A_STAT 24
+#define A_VISUAL 25
+#define A_TAGFILE 26
+#define A_FILE_LIST 27
diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c
new file mode 100644
index 0000000..0f2b6c6
--- /dev/null
+++ b/usr.bin/more/line.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)line.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines to manipulate the "line buffer".
+ * The line buffer holds a line of output as it is being built
+ * in preparation for output to the screen.
+ * We keep track of the PRINTABLE length of the line as it is being built.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <less.h>
+
+static char linebuf[1024]; /* Buffer which holds the current output line */
+static char *curr; /* Pointer into linebuf */
+static int column; /* Printable length, accounting for
+ backspaces, etc. */
+/*
+ * A ridiculously complex state machine takes care of backspaces. The
+ * complexity arises from the attempt to deal with all cases, especially
+ * involving long lines with underlining, boldfacing or whatever. There
+ * are still some cases which will break it.
+ *
+ * There are four states:
+ * LN_NORMAL is the normal state (not in underline mode).
+ * LN_UNDERLINE means we are in underline mode. We expect to get
+ * either a sequence like "_\bX" or "X\b_" to continue
+ * underline mode, or anything else to end underline mode.
+ * LN_BOLDFACE means we are in boldface mode. We expect to get sequences
+ * like "X\bX\b...X\bX" to continue boldface mode, or anything
+ * else to end boldface mode.
+ * LN_UL_X means we are one character after LN_UNDERLINE
+ * (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
+ * LN_UL_XB means we are one character after LN_UL_X
+ * (we have gotten the backspace in "_\bX" or "X\b_";
+ * we expect one more ordinary character,
+ * which will put us back in state LN_UNDERLINE).
+ * LN_BO_X means we are one character after LN_BOLDFACE
+ * (we have gotten the 'X' in "X\bX").
+ * LN_BO_XB means we are one character after LN_BO_X
+ * (we have gotten the backspace in "X\bX";
+ * we expect one more 'X' which will put us back
+ * in LN_BOLDFACE).
+ */
+static int ln_state; /* Currently in normal/underline/bold/etc mode? */
+#define LN_NORMAL 0 /* Not in underline, boldface or whatever mode */
+#define LN_UNDERLINE 1 /* In underline, need next char */
+#define LN_UL_X 2 /* In underline, got char, need \b */
+#define LN_UL_XB 3 /* In underline, got char & \b, need one more */
+#define LN_BOLDFACE 4 /* In boldface, need next char */
+#define LN_BO_X 5 /* In boldface, got char, need \b */
+#define LN_BO_XB 6 /* In boldface, got char & \b, need same char */
+
+char *line; /* Pointer to the current line.
+ Usually points to linebuf. */
+
+extern int bs_mode;
+extern int tabstop;
+extern int bo_width, be_width;
+extern int ul_width, ue_width;
+extern int sc_width, sc_height;
+
+/*
+ * Rewind the line buffer.
+ */
+prewind()
+{
+ line = curr = linebuf;
+ ln_state = LN_NORMAL;
+ column = 0;
+}
+
+/*
+ * Append a character to the line buffer.
+ * Expand tabs into spaces, handle underlining, boldfacing, etc.
+ * Returns 0 if ok, 1 if couldn't fit in buffer.
+ */
+#define NEW_COLUMN(addon) \
+ if (column + addon + (ln_state ? ue_width : 0) > sc_width) \
+ return(1); \
+ else \
+ column += addon
+
+pappend(c)
+ int c;
+{
+ if (c == '\0') {
+ /*
+ * Terminate any special modes, if necessary.
+ * Append a '\0' to the end of the line.
+ */
+ switch (ln_state) {
+ case LN_UL_X:
+ curr[0] = curr[-1];
+ curr[-1] = UE_CHAR;
+ curr++;
+ break;
+ case LN_BO_X:
+ curr[0] = curr[-1];
+ curr[-1] = BE_CHAR;
+ curr++;
+ break;
+ case LN_UL_XB:
+ case LN_UNDERLINE:
+ *curr++ = UE_CHAR;
+ break;
+ case LN_BO_XB:
+ case LN_BOLDFACE:
+ *curr++ = BE_CHAR;
+ break;
+ }
+ ln_state = LN_NORMAL;
+ *curr = '\0';
+ return(0);
+ }
+
+ if (curr > linebuf + sizeof(linebuf) - 12)
+ /*
+ * Almost out of room in the line buffer.
+ * Don't take any chances.
+ * {{ Linebuf is supposed to be big enough that this
+ * will never happen, but may need to be made
+ * bigger for wide screens or lots of backspaces. }}
+ */
+ return(1);
+
+ if (!bs_mode) {
+ /*
+ * Advance the state machine.
+ */
+ switch (ln_state) {
+ case LN_NORMAL:
+ if (curr <= linebuf + 1
+ || curr[-1] != '\b')
+ break;
+ if (c == ((unsigned char)curr[-2]))
+ goto enter_boldface;
+ if (c == '_' || curr[-2] == '_')
+ goto enter_underline;
+ curr -= 2;
+ break;
+
+enter_boldface:
+ /*
+ * We have "X\bX" (including the current char).
+ * Switch into boldface mode.
+ */
+ column--;
+ if (column + bo_width + be_width >= sc_width)
+ /*
+ * Not enough room left on the screen to
+ * enter and exit boldface mode.
+ */
+ return (1);
+
+ if (bo_width > 0 && curr > linebuf + 2
+ && curr[-3] == ' ') {
+ /*
+ * Special case for magic cookie terminals:
+ * if the previous char was a space, replace
+ * it with the "enter boldface" sequence.
+ */
+ curr[-3] = BO_CHAR;
+ column += bo_width-1;
+ } else {
+ curr[-1] = curr[-2];
+ curr[-2] = BO_CHAR;
+ column += bo_width;
+ curr++;
+ }
+ goto ln_bo_xb_case;
+
+enter_underline:
+ /*
+ * We have either "_\bX" or "X\b_" (including
+ * the current char). Switch into underline mode.
+ */
+ column--;
+ if (column + ul_width + ue_width >= sc_width)
+ /*
+ * Not enough room left on the screen to
+ * enter and exit underline mode.
+ */
+ return (1);
+
+ if (ul_width > 0 &&
+ curr > linebuf + 2 && curr[-3] == ' ')
+ {
+ /*
+ * Special case for magic cookie terminals:
+ * if the previous char was a space, replace
+ * it with the "enter underline" sequence.
+ */
+ curr[-3] = UL_CHAR;
+ column += ul_width-1;
+ } else
+ {
+ curr[-1] = curr[-2];
+ curr[-2] = UL_CHAR;
+ column += ul_width;
+ curr++;
+ }
+ goto ln_ul_xb_case;
+ /*NOTREACHED*/
+ case LN_UL_XB:
+ /*
+ * Termination of a sequence "_\bX" or "X\b_".
+ */
+ if (c != '_' && curr[-2] != '_' && c == ((unsigned char)curr[-2]))
+ {
+ /*
+ * We seem to have run on from underlining
+ * into boldfacing - this is a nasty fix, but
+ * until this whole routine is rewritten as a
+ * real DFA, ... well ...
+ */
+ curr[0] = curr[-2];
+ curr[-2] = UE_CHAR;
+ curr[-1] = BO_CHAR;
+ curr += 2; /* char & non-existent backspace */
+ ln_state = LN_BO_XB;
+ goto ln_bo_xb_case;
+ }
+ln_ul_xb_case:
+ if (c == '_')
+ c = (unsigned char)curr[-2];
+ curr -= 2;
+ ln_state = LN_UNDERLINE;
+ break;
+ case LN_BO_XB:
+ /*
+ * Termination of a sequnce "X\bX".
+ */
+ if (c != ((unsigned char)curr[-2]) && (c == '_' || curr[-2] == '_'))
+ {
+ /*
+ * We seem to have run on from
+ * boldfacing into underlining.
+ */
+ curr[0] = curr[-2];
+ curr[-2] = BE_CHAR;
+ curr[-1] = UL_CHAR;
+ curr += 2; /* char & non-existent backspace */
+ ln_state = LN_UL_XB;
+ goto ln_ul_xb_case;
+ }
+ln_bo_xb_case:
+ curr -= 2;
+ ln_state = LN_BOLDFACE;
+ break;
+ case LN_UNDERLINE:
+ if (column + ue_width + bo_width + be_width >= sc_width)
+ /*
+ * We have just barely enough room to
+ * exit underline mode and handle a possible
+ * underline/boldface run on mixup.
+ */
+ return (1);
+ ln_state = LN_UL_X;
+ break;
+ case LN_BOLDFACE:
+ if (c == '\b')
+ {
+ ln_state = LN_BO_XB;
+ break;
+ }
+ if (column + be_width + ul_width + ue_width >= sc_width)
+ /*
+ * We have just barely enough room to
+ * exit underline mode and handle a possible
+ * underline/boldface run on mixup.
+ */
+ return (1);
+ ln_state = LN_BO_X;
+ break;
+ case LN_UL_X:
+ if (c == '\b')
+ ln_state = LN_UL_XB;
+ else
+ {
+ /*
+ * Exit underline mode.
+ * We have to shuffle the chars a bit
+ * to make this work.
+ */
+ curr[0] = curr[-1];
+ curr[-1] = UE_CHAR;
+ column += ue_width;
+ if (ue_width > 0 && curr[0] == ' ')
+ /*
+ * Another special case for magic
+ * cookie terminals: if the next
+ * char is a space, replace it
+ * with the "exit underline" sequence.
+ */
+ column--;
+ else
+ curr++;
+ ln_state = LN_NORMAL;
+ }
+ break;
+ case LN_BO_X:
+ if (c == '\b')
+ ln_state = LN_BO_XB;
+ else
+ {
+ /*
+ * Exit boldface mode.
+ * We have to shuffle the chars a bit
+ * to make this work.
+ */
+ curr[0] = curr[-1];
+ curr[-1] = BE_CHAR;
+ column += be_width;
+ if (be_width > 0 && curr[0] == ' ')
+ /*
+ * Another special case for magic
+ * cookie terminals: if the next
+ * char is a space, replace it
+ * with the "exit boldface" sequence.
+ */
+ column--;
+ else
+ curr++;
+ ln_state = LN_NORMAL;
+ }
+ break;
+ }
+ }
+
+ if (c == '\t') {
+ /*
+ * Expand a tab into spaces.
+ */
+ do {
+ NEW_COLUMN(1);
+ } while ((column % tabstop) != 0);
+ *curr++ = '\t';
+ return (0);
+ }
+
+ if (c == '\b') {
+ if (ln_state == LN_NORMAL)
+ NEW_COLUMN(0);
+ else
+ column--;
+ *curr++ = c;
+ return(0);
+ }
+
+ switch ((char)c) {
+ case UL_CHAR:
+ case UE_CHAR:
+ case BO_CHAR:
+ case BE_CHAR:
+ c &= ~0200;
+ /* fall through */
+ case '\200':
+ NEW_COLUMN(2);
+ break;
+ default:
+ if (CONTROL_CHAR(c))
+ NEW_COLUMN(2);
+ else
+ NEW_COLUMN(1);
+ break;
+ }
+
+ *curr++ = c;
+ return (0);
+}
+
+/*
+ * Analogous to forw_line(), but deals with "raw lines":
+ * lines which are not split for screen width.
+ * {{ This is supposed to be more efficient than forw_line(). }}
+ */
+off_t
+forw_raw_line(curr_pos)
+ off_t curr_pos;
+{
+ register char *p;
+ register int c;
+ off_t new_pos, ch_tell();
+
+ if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
+ (c = ch_forw_get()) == EOI)
+ return (NULL_POSITION);
+
+ p = linebuf;
+
+ for (;;)
+ {
+ if (c == '\n' || c == EOI)
+ {
+ new_pos = ch_tell();
+ break;
+ }
+ if (p >= &linebuf[sizeof(linebuf)-1])
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ * {{ The line buffer is supposed to be big
+ * enough that this never happens. }}
+ */
+ new_pos = ch_tell() - 1;
+ break;
+ }
+ *p++ = c;
+ c = ch_forw_get();
+ }
+ *p = '\0';
+ line = linebuf;
+ return (new_pos);
+}
+
+/*
+ * Analogous to back_line(), but deals with "raw lines".
+ * {{ This is supposed to be more efficient than back_line(). }}
+ */
+off_t
+back_raw_line(curr_pos)
+ off_t curr_pos;
+{
+ register char *p;
+ register int c;
+ off_t new_pos, ch_tell();
+
+ if (curr_pos == NULL_POSITION || curr_pos <= (off_t)0 ||
+ ch_seek(curr_pos-1))
+ return (NULL_POSITION);
+
+ p = &linebuf[sizeof(linebuf)];
+ *--p = '\0';
+
+ for (;;)
+ {
+ c = ch_back_get();
+ if (c == '\n')
+ {
+ /*
+ * This is the newline ending the previous line.
+ * We have hit the beginning of the line.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ if (c == EOI)
+ {
+ /*
+ * We have hit the beginning of the file.
+ * This must be the first line in the file.
+ * This must, of course, be the beginning of the line.
+ */
+ new_pos = (off_t)0;
+ break;
+ }
+ if (p <= linebuf)
+ {
+ /*
+ * Overflowed the input buffer.
+ * Pretend the line ended here.
+ */
+ new_pos = ch_tell() + 1;
+ break;
+ }
+ *--p = c;
+ }
+ line = p;
+ return (new_pos);
+}
diff --git a/usr.bin/more/linenum.c b/usr.bin/more/linenum.c
new file mode 100644
index 0000000..dd3d82d
--- /dev/null
+++ b/usr.bin/more/linenum.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)linenum.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Code to handle displaying line numbers.
+ *
+ * Finding the line number of a given file position is rather tricky.
+ * We don't want to just start at the beginning of the file and
+ * count newlines, because that is slow for large files (and also
+ * wouldn't work if we couldn't get to the start of the file; e.g.
+ * if input is a long pipe).
+ *
+ * So we use the function add_lnum to cache line numbers.
+ * We try to be very clever and keep only the more interesting
+ * line numbers when we run out of space in our table. A line
+ * number is more interesting than another when it is far from
+ * other line numbers. For example, we'd rather keep lines
+ * 100,200,300 than 100,101,300. 200 is more interesting than
+ * 101 because 101 can be derived very cheaply from 100, while
+ * 200 is more expensive to derive from 100.
+ *
+ * The function currline() returns the line number of a given
+ * position in the file. As a side effect, it calls add_lnum
+ * to cache the line number. Therefore currline is occasionally
+ * called to make sure we cache line numbers often enough.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <less.h>
+
+/*
+ * Structure to keep track of a line number and the associated file position.
+ * A doubly-linked circular list of line numbers is kept ordered by line number.
+ */
+struct linenum
+{
+ struct linenum *next; /* Link to next in the list */
+ struct linenum *prev; /* Line to previous in the list */
+ off_t pos; /* File position */
+ off_t gap; /* Gap between prev and next */
+ int line; /* Line number */
+};
+/*
+ * "gap" needs some explanation: the gap of any particular line number
+ * is the distance between the previous one and the next one in the list.
+ * ("Distance" means difference in file position.) In other words, the
+ * gap of a line number is the gap which would be introduced if this
+ * line number were deleted. It is used to decide which one to replace
+ * when we have a new one to insert and the table is full.
+ */
+
+#define NPOOL 50 /* Size of line number pool */
+
+#define LONGTIME (2) /* In seconds */
+
+int lnloop = 0; /* Are we in the line num loop? */
+
+static struct linenum anchor; /* Anchor of the list */
+static struct linenum *freelist; /* Anchor of the unused entries */
+static struct linenum pool[NPOOL]; /* The pool itself */
+static struct linenum *spare; /* We always keep one spare entry */
+
+extern int linenums;
+extern int sigs;
+
+/*
+ * Initialize the line number structures.
+ */
+clr_linenum()
+{
+ register struct linenum *p;
+
+ /*
+ * Put all the entries on the free list.
+ * Leave one for the "spare".
+ */
+ for (p = pool; p < &pool[NPOOL-2]; p++)
+ p->next = p+1;
+ pool[NPOOL-2].next = NULL;
+ freelist = pool;
+
+ spare = &pool[NPOOL-1];
+
+ /*
+ * Initialize the anchor.
+ */
+ anchor.next = anchor.prev = &anchor;
+ anchor.gap = 0;
+ anchor.pos = (off_t)0;
+ anchor.line = 1;
+}
+
+/*
+ * Calculate the gap for an entry.
+ */
+static
+calcgap(p)
+ register struct linenum *p;
+{
+ /*
+ * Don't bother to compute a gap for the anchor.
+ * Also don't compute a gap for the last one in the list.
+ * The gap for that last one should be considered infinite,
+ * but we never look at it anyway.
+ */
+ if (p == &anchor || p->next == &anchor)
+ return;
+ p->gap = p->next->pos - p->prev->pos;
+}
+
+/*
+ * Add a new line number to the cache.
+ * The specified position (pos) should be the file position of the
+ * FIRST character in the specified line.
+ */
+add_lnum(line, pos)
+ int line;
+ off_t pos;
+{
+ register struct linenum *p;
+ register struct linenum *new;
+ register struct linenum *nextp;
+ register struct linenum *prevp;
+ register off_t mingap;
+
+ /*
+ * Find the proper place in the list for the new one.
+ * The entries are sorted by position.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ if (p->line == line)
+ /* We already have this one. */
+ return;
+ nextp = p;
+ prevp = p->prev;
+
+ if (freelist != NULL)
+ {
+ /*
+ * We still have free (unused) entries.
+ * Use one of them.
+ */
+ new = freelist;
+ freelist = freelist->next;
+ } else
+ {
+ /*
+ * No free entries.
+ * Use the "spare" entry.
+ */
+ new = spare;
+ spare = NULL;
+ }
+
+ /*
+ * Fill in the fields of the new entry,
+ * and insert it into the proper place in the list.
+ */
+ new->next = nextp;
+ new->prev = prevp;
+ new->pos = pos;
+ new->line = line;
+
+ nextp->prev = new;
+ prevp->next = new;
+
+ /*
+ * Recalculate gaps for the new entry and the neighboring entries.
+ */
+ calcgap(new);
+ calcgap(nextp);
+ calcgap(prevp);
+
+ if (spare == NULL)
+ {
+ /*
+ * We have used the spare entry.
+ * Scan the list to find the one with the smallest
+ * gap, take it out and make it the spare.
+ * We should never remove the last one, so stop when
+ * we get to p->next == &anchor. This also avoids
+ * looking at the gap of the last one, which is
+ * not computed by calcgap.
+ */
+ mingap = anchor.next->gap;
+ for (p = anchor.next; p->next != &anchor; p = p->next)
+ {
+ if (p->gap <= mingap)
+ {
+ spare = p;
+ mingap = p->gap;
+ }
+ }
+ spare->next->prev = spare->prev;
+ spare->prev->next = spare->next;
+ }
+}
+
+/*
+ * If we get stuck in a long loop trying to figure out the
+ * line number, print a message to tell the user what we're doing.
+ */
+static
+longloopmessage()
+{
+ ierror("Calculating line numbers");
+ /*
+ * Set the lnloop flag here, so if the user interrupts while
+ * we are calculating line numbers, the signal handler will
+ * turn off line numbers (linenums=0).
+ */
+ lnloop = 1;
+}
+
+/*
+ * Find the line number associated with a given position.
+ * Return 0 if we can't figure it out.
+ */
+find_linenum(pos)
+ off_t pos;
+{
+ register struct linenum *p;
+ register int lno;
+ register int loopcount;
+ off_t cpos, back_raw_line(), forw_raw_line();
+ time_t startime, time();
+
+ if (!linenums)
+ /*
+ * We're not using line numbers.
+ */
+ return (0);
+ if (pos == NULL_POSITION)
+ /*
+ * Caller doesn't know what he's talking about.
+ */
+ return (0);
+ if (pos == (off_t)0)
+ /*
+ * Beginning of file is always line number 1.
+ */
+ return (1);
+
+ /*
+ * Find the entry nearest to the position we want.
+ */
+ for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
+ continue;
+ if (p->pos == pos)
+ /* Found it exactly. */
+ return (p->line);
+
+ /*
+ * This is the (possibly) time-consuming part.
+ * We start at the line we just found and start
+ * reading the file forward or backward till we
+ * get to the place we want.
+ *
+ * First decide whether we should go forward from the
+ * previous one or backwards from the next one.
+ * The decision is based on which way involves
+ * traversing fewer bytes in the file.
+ */
+ flush();
+ (void)time(&startime);
+ if (p == &anchor || pos - p->prev->pos < p->pos - pos)
+ {
+ /*
+ * Go forward.
+ */
+ p = p->prev;
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos < pos; lno++)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = forw_raw_line(cpos);
+ if (sigs || cpos == NULL_POSITION)
+ return (0);
+ if (loopcount >= 0 && ++loopcount > 100) {
+ loopcount = 0;
+ if (time((time_t *)NULL)
+ >= startime + LONGTIME) {
+ longloopmessage();
+ loopcount = -1;
+ }
+ }
+ }
+ lnloop = 0;
+ /*
+ * If the given position is not at the start of a line,
+ * make sure we return the correct line number.
+ */
+ if (cpos > pos)
+ lno--;
+ } else
+ {
+ /*
+ * Go backward.
+ */
+ if (ch_seek(p->pos))
+ return (0);
+ loopcount = 0;
+ for (lno = p->line, cpos = p->pos; cpos > pos; lno--)
+ {
+ /*
+ * Allow a signal to abort this loop.
+ */
+ cpos = back_raw_line(cpos);
+ if (sigs || cpos == NULL_POSITION)
+ return (0);
+ if (loopcount >= 0 && ++loopcount > 100) {
+ loopcount = 0;
+ if (time((time_t *)NULL)
+ >= startime + LONGTIME) {
+ longloopmessage();
+ loopcount = -1;
+ }
+ }
+ }
+ lnloop = 0;
+ }
+
+ /*
+ * We might as well cache it.
+ */
+ add_lnum(lno, cpos);
+ return (lno);
+}
+
+/*
+ * Return the line number of the "current" line.
+ * The argument "where" tells which line is to be considered
+ * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
+ */
+currline(where)
+ int where;
+{
+ off_t pos, ch_length(), position();
+
+ if ((pos = position(where)) == NULL_POSITION)
+ pos = ch_length();
+ return(find_linenum(pos));
+}
diff --git a/usr.bin/more/main.c b/usr.bin/more/main.c
new file mode 100644
index 0000000..f019bdc
--- /dev/null
+++ b/usr.bin/more/main.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988 Mark Nudleman.\n\
+@(#) Copyright (c) 1988, 1993
+ Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/7/93";
+#endif /* not lint */
+
+/*
+ * Entry point, initialization, miscellaneous routines.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <less.h>
+
+int ispipe;
+int new_file;
+int is_tty;
+char *current_file, *previous_file, *current_name, *next_name;
+off_t prev_pos;
+int any_display;
+int scroll;
+int ac;
+char **av;
+int curr_ac;
+int quitting;
+
+extern int file;
+extern int cbufs;
+extern int errmsgs;
+
+extern char *tagfile;
+extern int tagoption;
+
+/*
+ * Edit a new file.
+ * Filename "-" means standard input.
+ * No filename means the "current" file, from the command line.
+ */
+edit(filename)
+ register char *filename;
+{
+ extern int errno;
+ register int f;
+ register char *m;
+ off_t initial_pos, position();
+ static int didpipe;
+ char message[100], *p;
+ char *rindex(), *strerror(), *save(), *bad_file();
+
+ initial_pos = NULL_POSITION;
+ if (filename == NULL || *filename == '\0') {
+ if (curr_ac >= ac) {
+ error("No current file");
+ return(0);
+ }
+ filename = save(av[curr_ac]);
+ }
+ else if (strcmp(filename, "#") == 0) {
+ if (*previous_file == '\0') {
+ error("no previous file");
+ return(0);
+ }
+ filename = save(previous_file);
+ initial_pos = prev_pos;
+ } else
+ filename = save(filename);
+
+ /* use standard input. */
+ if (!strcmp(filename, "-")) {
+ if (didpipe) {
+ error("Can view standard input only once");
+ return(0);
+ }
+ f = 0;
+ }
+ else if ((m = bad_file(filename, message, sizeof(message))) != NULL) {
+ error(m);
+ free(filename);
+ return(0);
+ }
+ else if ((f = open(filename, O_RDONLY, 0)) < 0) {
+ (void)sprintf(message, "%s: %s", filename, strerror(errno));
+ error(message);
+ free(filename);
+ return(0);
+ }
+
+ if (isatty(f)) {
+ /*
+ * Not really necessary to call this an error,
+ * but if the control terminal (for commands)
+ * and the input file (for data) are the same,
+ * we get weird results at best.
+ */
+ error("Can't take input from a terminal");
+ if (f > 0)
+ (void)close(f);
+ (void)free(filename);
+ return(0);
+ }
+
+ /*
+ * We are now committed to using the new file.
+ * Close the current input file and set up to use the new one.
+ */
+ if (file > 0)
+ (void)close(file);
+ new_file = 1;
+ if (previous_file != NULL)
+ free(previous_file);
+ previous_file = current_file;
+ current_file = filename;
+ pos_clear();
+ prev_pos = position(TOP);
+ ispipe = (f == 0);
+ if (ispipe) {
+ didpipe = 1;
+ current_name = "stdin";
+ } else
+ current_name = (p = rindex(filename, '/')) ? p + 1 : filename;
+ if (curr_ac >= ac)
+ next_name = NULL;
+ else
+ next_name = av[curr_ac + 1];
+ file = f;
+ ch_init(cbufs, 0);
+ init_mark();
+
+ if (is_tty) {
+ int no_display = !any_display;
+ any_display = 1;
+ if (no_display && errmsgs > 0) {
+ /*
+ * We displayed some messages on error output
+ * (file descriptor 2; see error() function).
+ * Before erasing the screen contents,
+ * display the file name and wait for a keystroke.
+ */
+ error(filename);
+ }
+ /*
+ * Indicate there is nothing displayed yet.
+ */
+ if (initial_pos != NULL_POSITION)
+ jump_loc(initial_pos);
+ clr_linenum();
+ }
+ return(1);
+}
+
+/*
+ * Edit the next file in the command line list.
+ */
+next_file(n)
+ int n;
+{
+ extern int quit_at_eof;
+ off_t position();
+
+ if (curr_ac + n >= ac) {
+ if (quit_at_eof || position(TOP) == NULL_POSITION)
+ quit();
+ error("No (N-th) next file");
+ }
+ else
+ (void)edit(av[curr_ac += n]);
+}
+
+/*
+ * Edit the previous file in the command line list.
+ */
+prev_file(n)
+ int n;
+{
+ if (curr_ac - n < 0)
+ error("No (N-th) previous file");
+ else
+ (void)edit(av[curr_ac -= n]);
+}
+
+/*
+ * copy a file directly to standard output; used if stdout is not a tty.
+ * the only processing is to squeeze multiple blank input lines.
+ */
+static
+cat_file()
+{
+ extern int squeeze;
+ register int c, empty;
+
+ if (squeeze) {
+ empty = 0;
+ while ((c = ch_forw_get()) != EOI)
+ if (c != '\n') {
+ putchr(c);
+ empty = 0;
+ }
+ else if (empty < 2) {
+ putchr(c);
+ ++empty;
+ }
+ }
+ else while ((c = ch_forw_get()) != EOI)
+ putchr(c);
+ flush();
+}
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int envargc, argcnt;
+ char *envargv[2], *getenv();
+
+ /*
+ * Process command line arguments and MORE environment arguments.
+ * Command line arguments override environment arguments.
+ */
+ if (envargv[1] = getenv("MORE")) {
+ envargc = 2;
+ envargv[0] = "more";
+ envargv[2] = NULL;
+ (void)option(envargc, envargv);
+ }
+ argcnt = option(argc, argv);
+ argv += argcnt;
+ argc -= argcnt;
+
+ /*
+ * Set up list of files to be examined.
+ */
+ ac = argc;
+ av = argv;
+ curr_ac = 0;
+
+ /*
+ * Set up terminal, etc.
+ */
+ is_tty = isatty(1);
+ if (!is_tty) {
+ /*
+ * Output is not a tty.
+ * Just copy the input file(s) to output.
+ */
+ if (ac < 1) {
+ (void)edit("-");
+ cat_file();
+ } else {
+ do {
+ (void)edit((char *)NULL);
+ if (file >= 0)
+ cat_file();
+ } while (++curr_ac < ac);
+ }
+ exit(0);
+ }
+
+ raw_mode(1);
+ get_term();
+ open_getchr();
+ init();
+ init_signals(1);
+
+ /* select the first file to examine. */
+ if (tagoption) {
+ /*
+ * A -t option was given; edit the file selected by the
+ * "tags" search, and search for the proper line in the file.
+ */
+ if (!tagfile || !edit(tagfile) || tagsearch())
+ quit();
+ }
+ else if (ac < 1)
+ (void)edit("-"); /* Standard input */
+ else {
+ /*
+ * Try all the files named as command arguments.
+ * We are simply looking for one which can be
+ * opened without error.
+ */
+ do {
+ (void)edit((char *)NULL);
+ } while (file < 0 && ++curr_ac < ac);
+ }
+
+ if (file >= 0)
+ commands();
+ quit();
+ /*NOTREACHED*/
+}
+
+/*
+ * Copy a string to a "safe" place
+ * (that is, to a buffer allocated by malloc).
+ */
+char *
+save(s)
+ char *s;
+{
+ char *p, *strcpy(), *malloc();
+
+ p = malloc((u_int)strlen(s)+1);
+ if (p == NULL)
+ {
+ error("cannot allocate memory");
+ quit();
+ }
+ return(strcpy(p, s));
+}
+
+/*
+ * Exit the program.
+ */
+quit()
+{
+ /*
+ * Put cursor at bottom left corner, clear the line,
+ * reset the terminal modes, and exit.
+ */
+ quitting = 1;
+ lower_left();
+ clear_eol();
+ deinit();
+ flush();
+ raw_mode(0);
+ exit(0);
+}
diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1
new file mode 100644
index 0000000..d6c2a44
--- /dev/null
+++ b/usr.bin/more/more.1
@@ -0,0 +1,300 @@
+.\" Copyright (c) 1988, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)more.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt MORE 1
+.Os
+.Sh NAME
+.Nm more
+.Nd file perusal filter for crt viewing
+.Sh SYNOPSIS
+.Nm more
+.Op Fl ceinus
+.Op Fl t Ar tag
+.Op Fl x Ar tabs
+.Op Fl / Ar pattern
+.Op Fl #
+.Op Ar
+.Sh DESCRIPTION
+.Nm More
+is a filter for paging through text one screenful at a time. It
+uses
+.Xr termcap 3
+so it can run on a variety of terminals. There is even limited support
+for hardcopy terminals. (On a hardcopy terminal, lines which should be
+printed at the top of the screen are prefixed with an up-arrow.)
+.Ar File
+may be a single dash (``-''), implying stdin.
+.Sh OPTIONS
+Command line options are described below.
+Options are also taken from the environment variable
+.Ev MORE
+(make sure to precede them with a dash (``-'')) but command
+line options will override them.
+.Bl -tag -width flag
+.It Fl c
+Normally,
+.Nm more
+will repaint the screen by scrolling from the bottom of the screen.
+If the
+.Fl c
+option is set, when
+.Nm more
+needs to change the entire display, it will paint from the top line down.
+.It Fl e
+Normally, if displaying a single file,
+.Nm more
+exits as soon as it reaches end-of-file. The
+.Fl e
+option tells more to
+exit if it reaches end-of-file twice without an intervening operation.
+If the file is shorter than a single screen
+.Nm more
+will exit at end-of-file regardless.
+.It Fl i
+The
+.Fl i
+option causes searches to ignore case; that is,
+uppercase and lowercase are considered identical.
+.It Fl n
+The
+.Fl n
+flag suppresses line numbers.
+The default (to use line numbers) may cause
+.Nm more
+to run more slowly in some cases, especially with a very large input file.
+Suppressing line numbers with the
+.Fl n
+flag will avoid this problem.
+Using line numbers means: the line number will be displayed in the
+.Cm =
+command, and the
+.Cm v
+command will pass the current line number to the editor.
+.It Fl s
+The
+.Fl s
+option causes
+consecutive blank lines to be squeezed into a single blank line.
+.It Fl t
+The
+.Fl t
+option, followed immediately by a tag, will edit the file
+containing that tag. For more information, see the
+.Xr ctags 1
+command.
+.It Fl u
+By default,
+.Nm more
+treats backspaces and
+.Dv CR-LF
+sequences specially. Backspaces which appear
+adjacent to an underscore character are displayed as underlined text.
+Backspaces which appear between two identical characters are displayed
+as emboldened text.
+.Dv CR-LF
+sequences are compressed to a single linefeed
+character. The
+.Fl u
+option causes backspaces to always be displayed as
+control characters, i.e. as the two character sequence ``^H'', and
+.Dv CR-LF
+to be left alone.
+.It Fl x
+The
+.Fl x
+option sets tab stops every
+.Ar N
+positions. The default for
+.Ar N
+is 8.
+.It Fl /
+The
+.Fl /
+option specifies a string that will be searched for before
+each file is displayed.
+.Sh COMMANDS
+Interactive commands for
+.Nm more
+are based on
+.Xr vi 1 .
+Some commands may be preceded by a decimal number, called N in the
+descriptions below.
+In the following descriptions, ^X means control-X.
+.Pp
+.Bl -tag -width Ic
+.It Ic h
+Help: display a summary of these commands.
+If you forget all the other commands, remember this one.
+.It Xo
+.Ic SPACE
+.No or
+.Ic f
+.No or
+.Ic \&^F
+.Xc
+Scroll forward N lines, default one window.
+If N is more than the screen size, only the final screenful is displayed.
+.It Ic b No or Ic \&^B
+Scroll backward N lines, default one window (see option -z below).
+If N is more than the screen size, only the final screenful is displayed.
+.It Ic j No or Ic RETURN
+Scroll forward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.It Ic k
+Scroll backward N lines, default 1.
+The entire N lines are displayed, even if N is more than the screen size.
+.It Ic d No or Ic \&^D
+Scroll forward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.It Ic u No or Ic \&^U
+Scroll backward N lines, default one half of the screen size.
+If N is specified, it becomes the new default for
+subsequent d and u commands.
+.It Ic g
+Go to line N in the file, default 1 (beginning of file).
+.It Ic G
+Go to line N in the file, default the end of the file.
+.It Ic p No or Ic \&%
+Go to a position N percent into the file. N should be between 0
+and 100. (This works if standard input is being read, but only if
+.Nm more
+has already read to the end of the file. It is always fast, but
+not always useful.)
+.It Ic r No or Ic \&^L
+Repaint the screen.
+.It Ic R
+Repaint the screen, discarding any buffered input.
+Useful if the file is changing while it is being viewed.
+.It Ic m
+Followed by any lowercase letter,
+marks the current position with that letter.
+.It Ic \&'
+(Single quote.)
+Followed by any lowercase letter, returns to the position which
+was previously marked with that letter.
+Followed by another single quote, returns to the position at
+which the last "large" movement command was executed, or the
+beginning of the file if no such movements have occurred.
+All marks are lost when a new file is examined.
+.It Ic \&/ Ns Ar pattern
+Search forward in the file for the N-th line containing the pattern.
+N defaults to 1.
+The pattern is a POSIX.2
+.Dq extended format
+regular expression, as described in
+.Xr re_format 7 .
+The search starts at the second line displayed.
+.It Ic \&? Ns Ar pattern
+Search backward in the file for the N-th line containing the pattern.
+The search starts at the line immediately before the top line displayed.
+.It Ic \&/\&! Ns Ar pattern
+Like /, but the search is for the N-th line
+which does NOT contain the pattern.
+.It Ic \&?\&! Ns Ar pattern
+Like ?, but the search is for the N-th line
+which does NOT contain the pattern.
+.It Ic n
+Repeat previous search, for N-th line containing the last pattern
+(or
+.Tn NOT
+containing the last pattern, if the previous search
+was /! or ?!).
+.It Ic E Ns Op Ar filename
+Examine a new file.
+If the filename is missing, the "current" file (see the N and P commands
+below) from the list of files in the command line is re-examined.
+If the filename is a pound sign (#), the previously examined file is
+re-examined.
+.It Ic N No or Ic \&:n
+Examine the next file (from the list of files given in the command line).
+If a number N is specified (not to be confused with the command N),
+the N-th next file is examined.
+.It Ic P No or Ic \&:p
+Examine the previous file.
+If a number N is specified, the N-th previous file is examined.
+.It Ic \&:t
+Go to supplied tag.
+.It Ic v
+Invokes an editor to edit the current file being viewed.
+The editor is taken from the environment variable
+.Ev EDITOR ,
+or defaults to
+.Xr vi 1 .
+.It Ic \&= No or Ic \&^G
+These options print out the number of the file currently being displayed
+relative to the total number of files there are to display, the current
+line number, the current byte number and the total bytes to display, and
+what percentage of the file has been displayed. If
+.Nm more
+is reading from stdin, or the file is shorter than a single screen, some
+of these items may not be available. Note, all of these items reference
+the first byte of the last line displayed on the screen.
+.It Xo
+.Ic q
+.No or
+.Ic \&:q
+.No or
+.Ic ZZ
+.Xc
+Exits
+.Nm more .
+.El
+.Sh ENVIRONMENT
+.Nm More
+utilizes the following environment variables, if they exist:
+.Bl -tag -width Fl
+.It Ev MORE
+This variable may be set with favored options to
+.Nm more .
+.It Ev EDITOR
+Specify default editor.
+.It Ev SHELL
+Current shell in use (normally set by the shell at login time).
+.It Ev TERM
+Specifies terminal type, used by more to get the terminal
+characteristics necessary to manipulate the screen.
+.El
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr vi 1
+.Sh AUTHOR
+This software is derived from software contributed to Berkeley
+by Mark Nudleman.
+.Sh HISTORY
+The
+.Nm more
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/more/more.help b/usr.bin/more/more.help
new file mode 100644
index 0000000..fcef82f
--- /dev/null
+++ b/usr.bin/more/more.help
@@ -0,0 +1,39 @@
+ Commands flagged with an asterisk (``*'') may be preceeded by a number.
+ Commands of the form ``^X'' are control characters, i.e. control-X.
+
+ h Display this help.
+
+ f, ^F, SPACE * Forward N lines, default one screen.
+ b, ^B * Backward N lines, default one screen.
+ j, CR * Forward N lines, default 1 line.
+ k * Backward N lines, default 1 line.
+ d, ^D * Forward N lines, default half screen or last N to d/u.
+ u, ^U * Backward N lines, default half screen or last N to d/u.
+ g * Go to line N, default 1.
+ G * Go to line N, default the end of the file.
+ p, % * Position to N percent into the file.
+
+ r, ^L Repaint screen.
+ R Repaint screen, discarding buffered input.
+
+ m[a-z] Mark the current position with the supplied letter.
+ '[a-z] Return to the position previously marked by this letter.
+ '' Return to previous position.
+
+ /pattern * Search forward for N-th line containing the pattern.
+ /!pattern * Search forward for N-th line NOT containing the pattern.
+ ?pattern * Search backward for N-th line containing the pattern.
+ ?!pattern * Search backward for N-th line NOT containing the pattern.
+ n * Repeat previous search (for N-th occurence).
+
+ :a Display the list of files.
+ E [file] Examine a new file.
+ :n, N * Examine the next file.
+ :p, P * Examine the previous file.
+ :t [tag] Examine the tag.
+ v Run an editor on the current file.
+
+ =, ^G Print current file name and stats.
+
+ q, :q, or ZZ Exit.
+
diff --git a/usr.bin/more/option.c b/usr.bin/more/option.c
new file mode 100644
index 0000000..29349d0
--- /dev/null
+++ b/usr.bin/more/option.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)option.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <less.h>
+
+int top_scroll; /* Repaint screen from top */
+int bs_mode; /* How to process backspaces */
+int caseless; /* Do "caseless" searches */
+int cbufs = 10; /* Current number of buffers */
+int linenums = 1; /* Use line numbers */
+int quit_at_eof;
+int squeeze; /* Squeeze multiple blank lines into one */
+int tabstop = 8; /* Tab settings */
+int tagoption;
+
+char *firstsearch;
+extern int sc_height;
+
+option(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ static int sc_window_set = 0;
+ int ch;
+ char *p;
+
+ /* backward compatible processing for "+/search" */
+ char **a;
+ for (a = argv; *a; ++a)
+ if ((*a)[0] == '+' && (*a)[1] == '/')
+ (*a)[0] = '-';
+
+ optind = 1; /* called twice, re-init getopt. */
+ while ((ch = getopt(argc, argv, "0123456789/:ceinst:ux:f")) != EOF)
+ switch((char)ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * kludge: more was originally designed to take
+ * a number after a dash.
+ */
+ if (!sc_window_set) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ sc_height = atoi(++p);
+ else
+ sc_height = atoi(argv[optind] + 1);
+ sc_window_set = 1;
+ }
+ break;
+ case '/':
+ firstsearch = optarg;
+ break;
+ case 'c':
+ top_scroll = 1;
+ break;
+ case 'e':
+ quit_at_eof = 1;
+ break;
+ case 'i':
+ caseless = 1;
+ break;
+ case 'n':
+ linenums = 0;
+ break;
+ case 's':
+ squeeze = 1;
+ break;
+ case 't':
+ tagoption = 1;
+ findtag(optarg);
+ break;
+ case 'u':
+ bs_mode = 1;
+ break;
+ case 'x':
+ tabstop = atoi(optarg);
+ if (tabstop <= 0)
+ tabstop = 8;
+ break;
+ case 'f': /* ignore -f, compatability with old more */
+ break;
+ case '?':
+ default:
+ fprintf(stderr,
+ "usage: more [-ceinus] [-t tag] [-x tabs] [-/ pattern] [-#] [file ...]\n");
+ exit(1);
+ }
+ return(optind);
+}
diff --git a/usr.bin/more/os.c b/usr.bin/more/os.c
new file mode 100644
index 0000000..75b7a6e
--- /dev/null
+++ b/usr.bin/more/os.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)os.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Operating system dependent routines.
+ *
+ * Most of the stuff in here is based on Unix, but an attempt
+ * has been made to make things work on other operating systems.
+ * This will sometimes result in a loss of functionality, unless
+ * someone rewrites code specifically for the new operating system.
+ *
+ * The makefile provides defines to decide whether various
+ * Unix features are present.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <less.h>
+#include "pathnames.h"
+
+int reading;
+
+extern int screen_trashed;
+
+static jmp_buf read_label;
+
+/*
+ * Pass the specified command to a shell to be executed.
+ * Like plain "system()", but handles resetting terminal modes, etc.
+ */
+lsystem(cmd)
+ char *cmd;
+{
+ int inp;
+ char cmdbuf[256];
+ char *shell, *getenv();
+
+ /*
+ * Print the command which is to be executed,
+ * unless the command starts with a "-".
+ */
+ if (cmd[0] == '-')
+ cmd++;
+ else
+ {
+ lower_left();
+ clear_eol();
+ putstr("!");
+ putstr(cmd);
+ putstr("\n");
+ }
+
+ /*
+ * De-initialize the terminal and take out of raw mode.
+ */
+ deinit();
+ flush();
+ raw_mode(0);
+
+ /*
+ * Restore signals to their defaults.
+ */
+ init_signals(0);
+
+ /*
+ * Force standard input to be the terminal, "/dev/tty",
+ * even if less's standard input is coming from a pipe.
+ */
+ inp = dup(0);
+ (void)close(0);
+ if (open(_PATH_TTY, O_RDONLY, 0) < 0)
+ (void)dup(inp);
+
+ /*
+ * Pass the command to the system to be executed.
+ * If we have a SHELL environment variable, use
+ * <$SHELL -c "command"> instead of just <command>.
+ * If the command is empty, just invoke a shell.
+ */
+ if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
+ {
+ if (*cmd == '\0')
+ cmd = shell;
+ else
+ {
+ (void)sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
+ cmd = cmdbuf;
+ }
+ }
+ if (*cmd == '\0')
+ cmd = "sh";
+
+ (void)system(cmd);
+
+ /*
+ * Restore standard input, reset signals, raw mode, etc.
+ */
+ (void)close(0);
+ (void)dup(inp);
+ (void)close(inp);
+
+ init_signals(1);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+#if defined(SIGWINCH) || defined(SIGWIND)
+ /*
+ * Since we were ignoring window change signals while we executed
+ * the system command, we must assume the window changed.
+ */
+ winch();
+#endif
+}
+
+/*
+ * Like read() system call, but is deliberately interruptable.
+ * A call to intread() from a signal handler will interrupt
+ * any pending iread().
+ */
+iread(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ register int n;
+
+ if (setjmp(read_label))
+ /*
+ * We jumped here from intread.
+ */
+ return (READ_INTR);
+
+ flush();
+ reading = 1;
+ n = read(fd, buf, len);
+ reading = 0;
+ if (n < 0)
+ return (-1);
+ return (n);
+}
+
+intread()
+{
+ (void)sigsetmask(0L);
+ longjmp(read_label, 1);
+}
+
+/*
+ * Expand a filename, substituting any environment variables, etc.
+ * The implementation of this is necessarily very operating system
+ * dependent. This implementation is unabashedly only for Unix systems.
+ */
+FILE *popen();
+
+char *
+glob(filename)
+ char *filename;
+{
+ FILE *f;
+ char *p;
+ int ch;
+ char *cmd, *malloc(), *getenv();
+ static char buffer[MAXPATHLEN];
+
+ if (filename[0] == '#')
+ return (filename);
+
+ /*
+ * We get the shell to expand the filename for us by passing
+ * an "echo" command to the shell and reading its output.
+ */
+ p = getenv("SHELL");
+ if (p == NULL || *p == '\0')
+ {
+ /*
+ * Read the output of <echo filename>.
+ */
+ cmd = malloc((u_int)(strlen(filename)+8));
+ if (cmd == NULL)
+ return (filename);
+ (void)sprintf(cmd, "echo \"%s\"", filename);
+ } else
+ {
+ /*
+ * Read the output of <$SHELL -c "echo filename">.
+ */
+ cmd = malloc((u_int)(strlen(p)+12));
+ if (cmd == NULL)
+ return (filename);
+ (void)sprintf(cmd, "%s -c \"echo %s\"", p, filename);
+ }
+
+ if ((f = popen(cmd, "r")) == NULL)
+ return (filename);
+ free(cmd);
+
+ for (p = buffer; p < &buffer[sizeof(buffer)-1]; p++)
+ {
+ if ((ch = getc(f)) == '\n' || ch == EOF)
+ break;
+ *p = ch;
+ }
+ *p = '\0';
+ (void)pclose(f);
+ return(buffer);
+}
+
+char *
+bad_file(filename, message, len)
+ char *filename, *message;
+ u_int len;
+{
+ extern int errno;
+ struct stat statbuf;
+ char *strcat(), *strerror();
+
+ if (stat(filename, &statbuf) < 0) {
+ (void)sprintf(message, "%s: %s", filename, strerror(errno));
+ return(message);
+ }
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ static char is_dir[] = " is a directory";
+
+ strtcpy(message, filename, (int)(len-sizeof(is_dir)-1));
+ (void)strcat(message, is_dir);
+ return(message);
+ }
+ return((char *)NULL);
+}
+
+/*
+ * Copy a string, truncating to the specified length if necessary.
+ * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
+ */
+strtcpy(to, from, len)
+ char *to, *from;
+ int len;
+{
+ char *strncpy();
+
+ (void)strncpy(to, from, (int)len);
+ to[len-1] = '\0';
+}
+
diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c
new file mode 100644
index 0000000..a02e4d8
--- /dev/null
+++ b/usr.bin/more/output.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * High level routines dealing with the output to the screen.
+ */
+
+#include <stdio.h>
+#include <less.h>
+
+int errmsgs; /* Count of messages displayed by error() */
+
+extern int sigs;
+extern int sc_width, sc_height;
+extern int ul_width, ue_width;
+extern int so_width, se_width;
+extern int bo_width, be_width;
+extern int tabstop;
+extern int screen_trashed;
+extern int any_display;
+extern char *line;
+extern int mode_flags;
+static int last_pos_highlighted = 0;
+
+/* display the line which is in the line buffer. */
+put_line()
+{
+ register char *p;
+ register int c;
+ register int column;
+ extern int auto_wrap, ignaw;
+
+ if (sigs)
+ {
+ /*
+ * Don't output if a signal is pending.
+ */
+ screen_trashed = 1;
+ return;
+ }
+
+ if (line == NULL)
+ line = "";
+
+ if (last_pos_highlighted)
+ {
+ clear_eol();
+ last_pos_highlighted = 0;
+ }
+ column = 0;
+ for (p = line; *p != '\0'; p++)
+ {
+ switch ((char)(c = (unsigned char)*p))
+ {
+ case UL_CHAR:
+ ul_enter();
+ column += ul_width;
+ break;
+ case UE_CHAR:
+ ul_exit();
+ column += ue_width;
+ break;
+ case BO_CHAR:
+ bo_enter();
+ column += bo_width;
+ break;
+ case BE_CHAR:
+ bo_exit();
+ column += be_width;
+ break;
+ case '\t':
+ do
+ {
+ putchr(' ');
+ column++;
+ } while ((column % tabstop) != 0);
+ break;
+ case '\b':
+ putbs();
+ column--;
+ break;
+ default:
+ if (c == 0200 || CONTROL_CHAR(c))
+ {
+ c &= ~0200;
+ putchr('^');
+ putchr(CARAT_CHAR(c));
+ column += 2;
+ } else
+ {
+ putchr(c);
+ column++;
+ }
+ }
+ if (column == sc_width && mode_flags)
+ last_pos_highlighted = 1;
+ }
+ if (column < sc_width || !auto_wrap || ignaw)
+ putchr('\n');
+}
+
+static char obuf[1024];
+static char *ob = obuf;
+
+/*
+ * Flush buffered output.
+ */
+flush()
+{
+ register int n;
+
+ n = ob - obuf;
+ if (n == 0)
+ return;
+ if (write(1, obuf, n) != n)
+ screen_trashed = 1;
+ ob = obuf;
+}
+
+/*
+ * Purge any pending output.
+ */
+purge()
+{
+
+ ob = obuf;
+}
+
+/*
+ * Output a character.
+ */
+putchr(c)
+ int c;
+{
+ if (ob >= &obuf[sizeof(obuf)])
+ flush();
+ *ob++ = c;
+}
+
+/*
+ * Output a string.
+ */
+putstr(s)
+ register char *s;
+{
+ while (*s != '\0')
+ putchr(*s++);
+}
+
+int cmdstack;
+static char return_to_continue[] = "(press RETURN)";
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+error(s)
+ char *s;
+{
+ int ch;
+
+ ++errmsgs;
+ if (!any_display) {
+ /*
+ * Nothing has been displayed yet. Output this message on
+ * error output (file descriptor 2) and don't wait for a
+ * keystroke to continue.
+ *
+ * This has the desirable effect of producing all error
+ * messages on error output if standard output is directed
+ * to a file. It also does the same if we never produce
+ * any real output; for example, if the input file(s) cannot
+ * be opened. If we do eventually produce output, code in
+ * edit() makes sure these messages can be seen before they
+ * are overwritten or scrolled away.
+ */
+ (void)write(2, s, strlen(s));
+ (void)write(2, "\n", 1);
+ return;
+ }
+
+ lower_left();
+ clear_eol();
+ so_enter();
+ if (s) {
+ putstr(s);
+ putstr(" ");
+ }
+ putstr(return_to_continue);
+ so_exit();
+
+ if ((ch = getchr()) != '\n') {
+ if (ch == 'q')
+ quit();
+ cmdstack = ch;
+ }
+ lower_left();
+
+ if ((s==NULL)?0:(strlen(s)) + sizeof(return_to_continue) +
+ so_width + se_width + 1 > sc_width)
+ /*
+ * Printing the message has probably scrolled the screen.
+ * {{ Unless the terminal doesn't have auto margins,
+ * in which case we just hammered on the right margin. }}
+ */
+ repaint();
+ flush();
+}
+
+static char intr_to_abort[] = "... (interrupt to abort)";
+
+ierror(s)
+ char *s;
+{
+ lower_left();
+ clear_eol();
+ so_enter();
+ putstr(s);
+ putstr(intr_to_abort);
+ so_exit();
+ flush();
+}
diff --git a/usr.bin/more/pathnames.h b/usr.bin/more/pathnames.h
new file mode 100644
index 0000000..c564360
--- /dev/null
+++ b/usr.bin/more/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_HELPFILE "/usr/share/misc/more.help"
diff --git a/usr.bin/more/position.c b/usr.bin/more/position.c
new file mode 100644
index 0000000..57ffb32
--- /dev/null
+++ b/usr.bin/more/position.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)position.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with the "position" table.
+ * This is a table which tells the position (in the input file) of the
+ * first char on each currently displayed line.
+ *
+ * {{ The position table is scrolled by moving all the entries.
+ * Would be better to have a circular table
+ * and just change a couple of pointers. }}
+ */
+
+#include <sys/types.h>
+#include <less.h>
+
+static off_t *table; /* The position table */
+static int tablesize;
+
+extern int sc_height;
+
+/*
+ * Return the starting file position of a line displayed on the screen.
+ * The line may be specified as a line number relative to the top
+ * of the screen, but is usually one of these special cases:
+ * the top (first) line on the screen
+ * the second line on the screen
+ * the bottom line on the screen
+ * the line after the bottom line on the screen
+ */
+off_t
+position(where)
+ int where;
+{
+ switch (where)
+ {
+ case BOTTOM:
+ where = sc_height - 2;
+ break;
+ case BOTTOM_PLUS_ONE:
+ where = sc_height - 1;
+ break;
+ case MIDDLE:
+ where = sc_height / 2;
+ }
+ return (table[where]);
+}
+
+/*
+ * Add a new file position to the bottom of the position table.
+ */
+add_forw_pos(pos)
+ off_t pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table up.
+ */
+ for (i = 1; i < sc_height; i++)
+ table[i-1] = table[i];
+ table[sc_height - 1] = pos;
+}
+
+/*
+ * Add a new file position to the top of the position table.
+ */
+add_back_pos(pos)
+ off_t pos;
+{
+ register int i;
+
+ /*
+ * Scroll the position table down.
+ */
+ for (i = sc_height - 1; i > 0; i--)
+ table[i] = table[i-1];
+ table[0] = pos;
+}
+
+copytable()
+{
+ register int a, b;
+
+ for (a = 0; a < sc_height && table[a] == NULL_POSITION; a++);
+ for (b = 0; a < sc_height; a++, b++) {
+ table[b] = table[a];
+ table[a] = NULL_POSITION;
+ }
+}
+
+/*
+ * Initialize the position table, done whenever we clear the screen.
+ */
+pos_clear()
+{
+ register int i;
+ extern char *malloc(), *realloc();
+
+ if (table == 0) {
+ tablesize = sc_height > 25 ? sc_height : 25;
+ table = (off_t *)malloc(tablesize * sizeof *table);
+ } else if (sc_height >= tablesize) {
+ tablesize = sc_height;
+ table = (off_t *)realloc(table, tablesize * sizeof *table);
+ }
+
+ for (i = 0; i < sc_height; i++)
+ table[i] = NULL_POSITION;
+}
+
+/*
+ * See if the byte at a specified position is currently on the screen.
+ * Check the position table to see if the position falls within its range.
+ * Return the position table entry if found, -1 if not.
+ */
+onscreen(pos)
+ off_t pos;
+{
+ register int i;
+
+ if (pos < table[0])
+ return (-1);
+ for (i = 1; i < sc_height; i++)
+ if (pos < table[i])
+ return (i-1);
+ return (-1);
+}
diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c
new file mode 100644
index 0000000..adb17d1
--- /dev/null
+++ b/usr.bin/more/prim.c
@@ -0,0 +1,749 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)prim.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Primitives for displaying the file on the screen.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <regex.h>
+#include <limits.h>
+#include <less.h>
+
+int back_scroll = -1;
+int hit_eof; /* keeps track of how many times we hit end of file */
+int screen_trashed;
+
+static int squished;
+
+extern int sigs;
+extern int top_scroll;
+extern int sc_width, sc_height;
+extern int caseless;
+extern int linenums;
+extern int tagoption;
+extern char *line;
+extern int retain_below;
+
+off_t position(), forw_line(), back_line(), forw_raw_line(), back_raw_line();
+off_t ch_length(), ch_tell();
+
+/*
+ * Check to see if the end of file is currently "displayed".
+ */
+eof_check()
+{
+ off_t pos;
+
+ if (sigs)
+ return;
+ /*
+ * If the bottom line is empty, we are at EOF.
+ * If the bottom line ends at the file length,
+ * we must be just at EOF.
+ */
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION || pos == ch_length())
+ hit_eof++;
+}
+
+/*
+ * If the screen is "squished", repaint it.
+ * "Squished" means the first displayed line is not at the top
+ * of the screen; this can happen when we display a short file
+ * for the first time.
+ */
+squish_check()
+{
+ if (squished) {
+ squished = 0;
+ repaint();
+ }
+}
+
+/*
+ * Display n lines, scrolling forward, starting at position pos in the
+ * input file. "only_last" means display only the last screenful if
+ * n > screen size.
+ */
+forw(n, pos, only_last)
+ register int n;
+ off_t pos;
+ int only_last;
+{
+ extern int short_file;
+ static int first_time = 1;
+ int eof = 0, do_repaint;
+
+ squish_check();
+
+ /*
+ * do_repaint tells us not to display anything till the end,
+ * then just repaint the entire screen.
+ */
+ do_repaint = (only_last && n > sc_height-1);
+
+ if (!do_repaint) {
+ if (top_scroll && n >= sc_height - 1) {
+ /*
+ * Start a new screen.
+ * {{ This is not really desirable if we happen
+ * to hit eof in the middle of this screen,
+ * but we don't yet know if that will happen. }}
+ */
+ clear();
+ home();
+ } else {
+ lower_left();
+ clear_eol();
+ }
+
+ /*
+ * This is not contiguous with what is currently displayed.
+ * Clear the screen image (position table) and start a new
+ * screen.
+ */
+ if (pos != position(BOTTOM_PLUS_ONE)) {
+ pos_clear();
+ add_forw_pos(pos);
+ if (top_scroll) {
+ clear();
+ home();
+ } else if (!first_time)
+ putstr("...skipping...\n");
+ }
+ }
+
+ for (short_file = 0; --n >= 0;) {
+ /*
+ * Read the next line of input.
+ */
+ pos = forw_line(pos);
+ if (pos == NULL_POSITION) {
+ /*
+ * end of file; copy the table if the file was
+ * too small for an entire screen.
+ */
+ eof = 1;
+ if (position(TOP) == NULL_POSITION) {
+ copytable();
+ if (!position(TOP))
+ short_file = 1;
+ }
+ break;
+ }
+ /*
+ * Add the position of the next line to the position table.
+ * Display the current line on the screen.
+ */
+ add_forw_pos(pos);
+ if (do_repaint)
+ continue;
+ /*
+ * If this is the first screen displayed and we hit an early
+ * EOF (i.e. before the requested number of lines), we
+ * "squish" the display down at the bottom of the screen.
+ * But don't do this if a -t option was given; it can cause
+ * us to start the display after the beginning of the file,
+ * and it is not appropriate to squish in that case.
+ */
+ if (first_time && line == NULL && !top_scroll && !tagoption) {
+ squished = 1;
+ continue;
+ }
+ put_line();
+ }
+
+ if (eof && !sigs)
+ hit_eof++;
+ else
+ eof_check();
+ if (do_repaint)
+ repaint();
+ first_time = 0;
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n lines, scrolling backward.
+ */
+back(n, pos, only_last)
+ register int n;
+ off_t pos;
+ int only_last;
+{
+ int do_repaint;
+
+ squish_check();
+ do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
+ hit_eof = 0;
+ while (--n >= 0)
+ {
+ /*
+ * Get the previous line of input.
+ */
+ pos = back_line(pos);
+ if (pos == NULL_POSITION)
+ break;
+ /*
+ * Add the position of the previous line to the position table.
+ * Display the line on the screen.
+ */
+ add_back_pos(pos);
+ if (!do_repaint)
+ {
+ if (retain_below)
+ {
+ lower_left();
+ clear_eol();
+ }
+ home();
+ add_line();
+ put_line();
+ }
+ }
+
+ eof_check();
+ if (do_repaint)
+ repaint();
+ (void) currline(BOTTOM);
+}
+
+/*
+ * Display n more lines, forward.
+ * Start just after the line currently displayed at the bottom of the screen.
+ */
+forward(n, only_last)
+ int n;
+ int only_last;
+{
+ off_t pos;
+
+ if (hit_eof) {
+ /*
+ * If we're trying to go forward from end-of-file,
+ * go on to the next file.
+ */
+ next_file(1);
+ return;
+ }
+
+ pos = position(BOTTOM_PLUS_ONE);
+ if (pos == NULL_POSITION)
+ {
+ hit_eof++;
+ return;
+ }
+ forw(n, pos, only_last);
+}
+
+/*
+ * Display n more lines, backward.
+ * Start just before the line currently displayed at the top of the screen.
+ */
+backward(n, only_last)
+ int n;
+ int only_last;
+{
+ off_t pos;
+
+ pos = position(TOP);
+ /*
+ * This will almost never happen, because the top line is almost
+ * never empty.
+ */
+ if (pos == NULL_POSITION)
+ return;
+ back(n, pos, only_last);
+}
+
+/*
+ * Repaint the screen, starting from a specified position.
+ */
+prepaint(pos)
+ off_t pos;
+{
+ hit_eof = 0;
+ forw(sc_height-1, pos, 0);
+ screen_trashed = 0;
+}
+
+/*
+ * Repaint the screen.
+ */
+repaint()
+{
+ /*
+ * Start at the line currently at the top of the screen
+ * and redisplay the screen.
+ */
+ prepaint(position(TOP));
+}
+
+/*
+ * Jump to the end of the file.
+ * It is more convenient to paint the screen backward,
+ * from the end of the file toward the beginning.
+ */
+jump_forw()
+{
+ off_t pos;
+
+ if (ch_end_seek())
+ {
+ error("Cannot seek to end of file");
+ return;
+ }
+ lastmark();
+ pos = ch_tell();
+ clear();
+ pos_clear();
+ add_back_pos(pos);
+ back(sc_height - 1, pos, 0);
+}
+
+/*
+ * Jump to line n in the file.
+ */
+jump_back(n)
+ register int n;
+{
+ register int c, nlines;
+
+ /*
+ * This is done the slow way, by starting at the beginning
+ * of the file and counting newlines.
+ *
+ * {{ Now that we have line numbering (in linenum.c),
+ * we could improve on this by starting at the
+ * nearest known line rather than at the beginning. }}
+ */
+ if (ch_seek((off_t)0)) {
+ /*
+ * Probably a pipe with beginning of file no longer buffered.
+ * If he wants to go to line 1, we do the best we can,
+ * by going to the first line which is still buffered.
+ */
+ if (n <= 1 && ch_beg_seek() == 0)
+ jump_loc(ch_tell());
+ error("Cannot get to beginning of file");
+ return;
+ }
+
+ /*
+ * Start counting lines.
+ */
+ for (nlines = 1; nlines < n; nlines++)
+ while ((c = ch_forw_get()) != '\n')
+ if (c == EOI) {
+ char message[40];
+ (void)sprintf(message, "File has only %d lines",
+ nlines - 1);
+ error(message);
+ return;
+ }
+ jump_loc(ch_tell());
+}
+
+/*
+ * Jump to a specified percentage into the file.
+ * This is a poor compensation for not being able to
+ * quickly jump to a specific line number.
+ */
+jump_percent(percent)
+ int percent;
+{
+ off_t pos, len, ch_length();
+ register int c;
+
+ /*
+ * Determine the position in the file
+ * (the specified percentage of the file's length).
+ */
+ if ((len = ch_length()) == NULL_POSITION)
+ {
+ error("Don't know length of file");
+ return;
+ }
+ pos = (percent * len) / 100;
+
+ /*
+ * Back up to the beginning of the line.
+ */
+ if (ch_seek(pos) == 0)
+ {
+ while ((c = ch_back_get()) != '\n' && c != EOI)
+ ;
+ if (c == '\n')
+ (void) ch_forw_get();
+ pos = ch_tell();
+ }
+ jump_loc(pos);
+}
+
+/*
+ * Jump to a specified position in the file.
+ */
+jump_loc(pos)
+ off_t pos;
+{
+ register int nline;
+ off_t tpos;
+
+ if ((nline = onscreen(pos)) >= 0) {
+ /*
+ * The line is currently displayed.
+ * Just scroll there.
+ */
+ forw(nline, position(BOTTOM_PLUS_ONE), 0);
+ return;
+ }
+
+ /*
+ * Line is not on screen.
+ * Seek to the desired location.
+ */
+ if (ch_seek(pos)) {
+ error("Cannot seek to that position");
+ return;
+ }
+
+ /*
+ * See if the desired line is BEFORE the currently displayed screen.
+ * If so, then move forward far enough so the line we're on will be
+ * at the bottom of the screen, in order to be able to call back()
+ * to make the screen scroll backwards & put the line at the top of
+ * the screen.
+ * {{ This seems inefficient, but it's not so bad,
+ * since we can never move forward more than a
+ * screenful before we stop to redraw the screen. }}
+ */
+ tpos = position(TOP);
+ if (tpos != NULL_POSITION && pos < tpos) {
+ off_t npos = pos;
+ /*
+ * Note that we can't forw_line() past tpos here,
+ * so there should be no EOI at this stage.
+ */
+ for (nline = 0; npos < tpos && nline < sc_height - 1; nline++)
+ npos = forw_line(npos);
+
+ if (npos < tpos) {
+ /*
+ * More than a screenful back.
+ */
+ lastmark();
+ clear();
+ pos_clear();
+ add_back_pos(npos);
+ }
+
+ /*
+ * Note that back() will repaint() if nline > back_scroll.
+ */
+ back(nline, npos, 0);
+ return;
+ }
+ /*
+ * Remember where we were; clear and paint the screen.
+ */
+ lastmark();
+ prepaint(pos);
+}
+
+/*
+ * The table of marks.
+ * A mark is simply a position in the file.
+ */
+#define NMARKS (27) /* 26 for a-z plus one for quote */
+#define LASTMARK (NMARKS-1) /* For quote */
+static off_t marks[NMARKS];
+
+/*
+ * Initialize the mark table to show no marks are set.
+ */
+init_mark()
+{
+ int i;
+
+ for (i = 0; i < NMARKS; i++)
+ marks[i] = NULL_POSITION;
+}
+
+/*
+ * See if a mark letter is valid (between a and z).
+ */
+ static int
+badmark(c)
+ int c;
+{
+ if (c < 'a' || c > 'z')
+ {
+ error("Choose a letter between 'a' and 'z'");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Set a mark.
+ */
+setmark(c)
+ int c;
+{
+ if (badmark(c))
+ return;
+ marks[c-'a'] = position(TOP);
+}
+
+lastmark()
+{
+ marks[LASTMARK] = position(TOP);
+}
+
+/*
+ * Go to a previously set mark.
+ */
+gomark(c)
+ int c;
+{
+ off_t pos;
+
+ if (c == '\'') {
+ pos = marks[LASTMARK];
+ if (pos == NULL_POSITION)
+ pos = 0;
+ }
+ else {
+ if (badmark(c))
+ return;
+ pos = marks[c-'a'];
+ if (pos == NULL_POSITION) {
+ error("mark not set");
+ return;
+ }
+ }
+ jump_loc(pos);
+}
+
+/*
+ * Get the backwards scroll limit.
+ * Must call this function instead of just using the value of
+ * back_scroll, because the default case depends on sc_height and
+ * top_scroll, as well as back_scroll.
+ */
+get_back_scroll()
+{
+ if (back_scroll >= 0)
+ return (back_scroll);
+ if (top_scroll)
+ return (sc_height - 2);
+ return (sc_height - 1);
+}
+
+/*
+ * Search for the n-th occurence of a specified pattern,
+ * either forward or backward.
+ */
+search(search_forward, pattern, n, wantmatch)
+ register int search_forward;
+ register char *pattern;
+ register int n;
+ int wantmatch;
+{
+ off_t pos, linepos;
+ register char *p;
+ register char *q;
+ int linenum;
+ int linematch;
+ static regex_t rx;
+ static int oncethru;
+ int regerr;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ if (pattern && pattern[0]) {
+ if (oncethru) {
+ regfree(&rx);
+ }
+
+ regerr = regcomp(&rx, pattern, (REG_EXTENDED | REG_NOSUB
+ | (caseless ? REG_ICASE : 0)));
+
+ if (regerr) {
+ regerror(regerr, &rx, errbuf, sizeof errbuf);
+ error(errbuf);
+ oncethru = 0;
+ regfree(&rx);
+ return 0;
+ }
+ oncethru = 1;
+ } else if (!oncethru) {
+ error("No previous regular expression");
+ return 0;
+ }
+
+ /*
+ * Figure out where to start the search.
+ */
+
+ if (position(TOP) == NULL_POSITION) {
+ /*
+ * Nothing is currently displayed. Start at the beginning
+ * of the file. (This case is mainly for searches from the
+ * command line.
+ */
+ pos = (off_t)0;
+ } else if (!search_forward) {
+ /*
+ * Backward search: start just before the top line
+ * displayed on the screen.
+ */
+ pos = position(TOP);
+ } else {
+ /*
+ * Start at the second screen line displayed on the screen.
+ */
+ pos = position(TOP_PLUS_ONE);
+ }
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * Can't find anyplace to start searching from.
+ */
+ error("Nothing to search");
+ return(0);
+ }
+
+ linenum = find_linenum(pos);
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or
+ * until we hit end-of-file (or beginning-of-file
+ * if we're going backwards).
+ */
+ if (sigs)
+ /*
+ * A signal aborts the search.
+ */
+ return(0);
+
+ if (search_forward)
+ {
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos);
+ if (linenum != 0)
+ linenum++;
+ } else
+ {
+ /*
+ * Read the previous line and save the
+ * starting position of that line in linepos.
+ */
+ pos = back_raw_line(pos);
+ linepos = pos;
+ if (linenum != 0)
+ linenum--;
+ }
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * We hit EOF/BOF without a match.
+ */
+ error("Pattern not found");
+ return(0);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ */
+ if (linenums)
+ add_lnum(linenum, pos);
+
+ /*
+ * If this is a caseless search, convert uppercase in the
+ * input line to lowercase.
+ */
+ if (caseless)
+ for (p = q = line; *p; p++, q++)
+ *q = isupper(*p) ? tolower(*p) : *p;
+
+ /*
+ * Remove any backspaces along with the preceeding char.
+ * This allows us to match text which is underlined or
+ * overstruck.
+ */
+ for (p = q = line; *p; p++, q++)
+ if (q > line && *p == '\b')
+ /* Delete BS and preceeding char. */
+ q -= 2;
+ else
+ /* Otherwise, just copy. */
+ *q = *p;
+
+ /*
+ * Test the next line to see if we have a match.
+ */
+ linematch = !regexec(&rx, line, 0, 0, 0);
+
+ /*
+ * We are successful if wantmatch and linematch are
+ * both true (want a match and got it),
+ * or both false (want a non-match and got it).
+ */
+ if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
+ --n <= 0)
+ /*
+ * Found the line.
+ */
+ break;
+ }
+ jump_loc(linepos);
+ return(1);
+}
+
diff --git a/usr.bin/more/screen.c b/usr.bin/more/screen.c
new file mode 100644
index 0000000..bf12ad6
--- /dev/null
+++ b/usr.bin/more/screen.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)screen.c 8.2 (Berkeley) 4/20/94";
+#endif /* not lint */
+
+/*
+ * Routines which deal with the characteristics of the terminal.
+ * Uses termcap to be as terminal-independent as possible.
+ *
+ * {{ Someday this should be rewritten to use curses. }}
+ */
+
+#include <stdio.h>
+#include <less.h>
+
+#define TERMIOS 1
+
+#if TERMIO
+#include <termio.h>
+#else
+#if TERMIOS
+#include <termios.h>
+#define TAB3 0
+#include <sys/ioctl.h>
+#else
+#include <sgtty.h>
+#endif
+#endif
+
+#ifdef TIOCGWINSZ
+#include <sys/ioctl.h>
+#else
+/*
+ * For the Unix PC (ATT 7300 & 3B1):
+ * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
+ * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
+ */
+#include <sys/signal.h>
+#ifdef SIGPHONE
+#include <sys/window.h>
+#endif
+#endif
+
+/*
+ * Strings passed to tputs() to do various terminal functions.
+ */
+static char
+ *sc_pad, /* Pad string */
+ *sc_home, /* Cursor home */
+ *sc_addline, /* Add line, scroll down following lines */
+ *sc_lower_left, /* Cursor to last line, first column */
+ *sc_move, /* General cursor positioning */
+ *sc_clear, /* Clear screen */
+ *sc_eol_clear, /* Clear to end of line */
+ *sc_s_in, /* Enter standout (highlighted) mode */
+ *sc_s_out, /* Exit standout mode */
+ *sc_u_in, /* Enter underline mode */
+ *sc_u_out, /* Exit underline mode */
+ *sc_b_in, /* Enter bold mode */
+ *sc_b_out, /* Exit bold mode */
+ *sc_backspace, /* Backspace cursor */
+ *sc_init, /* Startup terminal initialization */
+ *sc_deinit; /* Exit terminal de-intialization */
+
+int auto_wrap; /* Terminal does \r\n when write past margin */
+int ignaw; /* Terminal ignores \n immediately after wrap */
+ /* The user's erase and line-kill chars */
+int retain_below; /* Terminal retains text below the screen */
+int erase_char, kill_char, werase_char;
+int sc_width, sc_height = -1; /* Height & width of screen */
+int sc_window = -1; /* window size for forward and backward */
+int bo_width, be_width; /* Printing width of boldface sequences */
+int ul_width, ue_width; /* Printing width of underline sequences */
+int so_width, se_width; /* Printing width of standout sequences */
+
+int mode_flags = 0;
+#define M_SO 1
+#define M_UL 2
+#define M_BO 4
+
+/*
+ * These two variables are sometimes defined in,
+ * and needed by, the termcap library.
+ * It may be necessary on some systems to declare them extern here.
+ */
+/*extern*/ short ospeed; /* Terminal output baud rate */
+/*extern*/ char PC; /* Pad character */
+
+extern int back_scroll;
+char *tgetstr();
+char *tgoto();
+
+/*
+ * Change terminal to "raw mode", or restore to "normal" mode.
+ * "Raw mode" means
+ * 1. An outstanding read will complete on receipt of a single keystroke.
+ * 2. Input is not echoed.
+ * 3. On output, \n is mapped to \r\n.
+ * 4. \t is NOT expanded into spaces.
+ * 5. Signal-causing characters such as ctrl-C (interrupt),
+ * etc. are NOT disabled.
+ * It doesn't matter whether an input \n is mapped to \r, or vice versa.
+ */
+raw_mode(on)
+ int on;
+{
+#if TERMIO || TERMIOS
+
+#if TERMIO
+ struct termio s;
+ static struct termio save_term;
+#else
+ struct termios s;
+ static struct termios save_term;
+#endif
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+#if TERMIO
+ (void)ioctl(2, TCGETA, &s);
+#else
+ tcgetattr(2, &s);
+#endif
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+#if TERMIO
+ ospeed = s.c_cflag & CBAUD;
+#else
+ /* more work needed here */
+#endif
+ erase_char = s.c_cc[VERASE];
+ kill_char = s.c_cc[VKILL];
+ werase_char = s.c_cc[VWERASE];
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
+ s.c_oflag |= (OPOST|ONLCR|TAB3);
+#if TERMIO
+ s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
+#endif
+ s.c_cc[VMIN] = 1;
+ s.c_cc[VTIME] = 0;
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+#if TERMIO
+ (void)ioctl(2, TCSETAW, &s);
+#else
+ tcsetattr(2, TCSADRAIN, &s);
+#endif
+#else
+ struct sgttyb s;
+ struct ltchars l;
+ static struct sgttyb save_term;
+
+ if (on)
+ {
+ /*
+ * Get terminal modes.
+ */
+ (void)ioctl(2, TIOCGETP, &s);
+ (void)ioctl(2, TIOCGLTC, &l);
+
+ /*
+ * Save modes and set certain variables dependent on modes.
+ */
+ save_term = s;
+ ospeed = s.sg_ospeed;
+ erase_char = s.sg_erase;
+ kill_char = s.sg_kill;
+ werase_char = l.t_werasc;
+
+ /*
+ * Set the modes to the way we want them.
+ */
+ s.sg_flags |= CBREAK;
+ s.sg_flags &= ~(ECHO|XTABS);
+ } else
+ {
+ /*
+ * Restore saved modes.
+ */
+ s = save_term;
+ }
+ (void)ioctl(2, TIOCSETN, &s);
+#endif
+}
+
+/*
+ * Get terminal capabilities via termcap.
+ */
+get_term()
+{
+ char termbuf[2048];
+ char *sp;
+ char *term;
+ int hard;
+#ifdef TIOCGWINSZ
+ struct winsize w;
+#else
+#ifdef WIOCGETD
+ struct uwdata w;
+#endif
+#endif
+ static char sbuf[1024];
+
+ char *getenv(), *strcpy();
+
+ /*
+ * Find out what kind of terminal this is.
+ */
+ if ((term = getenv("TERM")) == NULL)
+ term = "unknown";
+ if (tgetent(termbuf, term) <= 0)
+ (void)strcpy(termbuf, "dumb:co#80:hc:");
+
+ /*
+ * Get size of the screen.
+ */
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
+ sc_height = w.ws_row;
+#else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
+ sc_height = w.uw_height/w.uw_vs;
+#endif
+#endif
+ else
+ sc_height = tgetnum("li");
+ hard = (sc_height < 0 || tgetflag("hc"));
+ if (hard) {
+ /* Oh no, this is a hardcopy terminal. */
+ sc_height = 24;
+ }
+
+#ifdef TIOCGWINSZ
+ if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
+ sc_width = w.ws_col;
+ else
+#ifdef WIOCGETD
+ if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
+ sc_width = w.uw_width/w.uw_hs;
+ else
+#endif
+#endif
+ sc_width = tgetnum("co");
+ if (sc_width < 0)
+ sc_width = 80;
+
+ auto_wrap = tgetflag("am");
+ ignaw = tgetflag("xn");
+ retain_below = tgetflag("db");
+
+ /*
+ * Assumes termcap variable "sg" is the printing width of
+ * the standout sequence, the end standout sequence,
+ * the underline sequence, the end underline sequence,
+ * the boldface sequence, and the end boldface sequence.
+ */
+ if ((so_width = tgetnum("sg")) < 0)
+ so_width = 0;
+ be_width = bo_width = ue_width = ul_width = se_width = so_width;
+
+ /*
+ * Get various string-valued capabilities.
+ */
+ sp = sbuf;
+
+ sc_pad = tgetstr("pc", &sp);
+ if (sc_pad != NULL)
+ PC = *sc_pad;
+
+ sc_init = tgetstr("ti", &sp);
+ if (sc_init == NULL)
+ sc_init = "";
+
+ sc_deinit= tgetstr("te", &sp);
+ if (sc_deinit == NULL)
+ sc_deinit = "";
+
+ sc_eol_clear = tgetstr("ce", &sp);
+ if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
+ {
+ sc_eol_clear = "";
+ }
+
+ sc_clear = tgetstr("cl", &sp);
+ if (hard || sc_clear == NULL || *sc_clear == '\0')
+ {
+ sc_clear = "\n\n";
+ }
+
+ sc_move = tgetstr("cm", &sp);
+ if (hard || sc_move == NULL || *sc_move == '\0')
+ {
+ /*
+ * This is not an error here, because we don't
+ * always need sc_move.
+ * We need it only if we don't have home or lower-left.
+ */
+ sc_move = "";
+ }
+
+ sc_s_in = tgetstr("so", &sp);
+ if (hard || sc_s_in == NULL)
+ sc_s_in = "";
+
+ sc_s_out = tgetstr("se", &sp);
+ if (hard || sc_s_out == NULL)
+ sc_s_out = "";
+
+ sc_u_in = tgetstr("us", &sp);
+ if (hard || sc_u_in == NULL)
+ sc_u_in = sc_s_in;
+
+ sc_u_out = tgetstr("ue", &sp);
+ if (hard || sc_u_out == NULL)
+ sc_u_out = sc_s_out;
+
+ sc_b_in = tgetstr("md", &sp);
+ if (hard || sc_b_in == NULL)
+ {
+ sc_b_in = sc_s_in;
+ sc_b_out = sc_s_out;
+ } else
+ {
+ sc_b_out = tgetstr("me", &sp);
+ if (hard || sc_b_out == NULL)
+ sc_b_out = "";
+ }
+
+ sc_home = tgetstr("ho", &sp);
+ if (hard || sc_home == NULL || *sc_home == '\0')
+ {
+ if (*sc_move == '\0')
+ {
+ /*
+ * This last resort for sc_home is supposed to
+ * be an up-arrow suggesting moving to the
+ * top of the "virtual screen". (The one in
+ * your imagination as you try to use this on
+ * a hard copy terminal.)
+ */
+ sc_home = "|\b^";
+ } else
+ {
+ /*
+ * No "home" string,
+ * but we can use "move(0,0)".
+ */
+ (void)strcpy(sp, tgoto(sc_move, 0, 0));
+ sc_home = sp;
+ sp += strlen(sp) + 1;
+ }
+ }
+
+ sc_lower_left = tgetstr("ll", &sp);
+ if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
+ {
+ if (*sc_move == '\0')
+ {
+ sc_lower_left = "\r";
+ } else
+ {
+ /*
+ * No "lower-left" string,
+ * but we can use "move(0,last-line)".
+ */
+ (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1));
+ sc_lower_left = sp;
+ sp += strlen(sp) + 1;
+ }
+ }
+
+ /*
+ * To add a line at top of screen and scroll the display down,
+ * we use "al" (add line) or "sr" (scroll reverse).
+ */
+ if ((sc_addline = tgetstr("al", &sp)) == NULL ||
+ *sc_addline == '\0')
+ sc_addline = tgetstr("sr", &sp);
+
+ if (hard || sc_addline == NULL || *sc_addline == '\0')
+ {
+ sc_addline = "";
+ /* Force repaint on any backward movement */
+ back_scroll = 0;
+ }
+
+ if (tgetflag("bs"))
+ sc_backspace = "\b";
+ else
+ {
+ sc_backspace = tgetstr("bc", &sp);
+ if (sc_backspace == NULL || *sc_backspace == '\0')
+ sc_backspace = "\b";
+ }
+}
+
+
+/*
+ * Below are the functions which perform all the
+ * terminal-specific screen manipulation.
+ */
+
+int putchr();
+
+/*
+ * Initialize terminal
+ */
+init()
+{
+ tputs(sc_init, sc_height, putchr);
+}
+
+/*
+ * Deinitialize terminal
+ */
+deinit()
+{
+ tputs(sc_deinit, sc_height, putchr);
+}
+
+/*
+ * Home cursor (move to upper left corner of screen).
+ */
+home()
+{
+ tputs(sc_home, 1, putchr);
+}
+
+/*
+ * Add a blank line (called with cursor at home).
+ * Should scroll the display down.
+ */
+add_line()
+{
+ tputs(sc_addline, sc_height, putchr);
+}
+
+int short_file; /* if file less than a screen */
+lower_left()
+{
+ if (short_file) {
+ putchr('\r');
+ flush();
+ }
+ else
+ tputs(sc_lower_left, 1, putchr);
+}
+
+/*
+ * Ring the terminal bell.
+ */
+bell()
+{
+ putchr('\7');
+}
+
+/*
+ * Clear the screen.
+ */
+clear()
+{
+ if (mode_flags & M_SO)
+ so_exit();
+ if (mode_flags & M_UL)
+ ul_exit();
+ if (mode_flags & M_BO)
+ bo_exit();
+ tputs(sc_clear, sc_height, putchr);
+}
+
+/*
+ * Clear from the cursor to the end of the cursor's line.
+ * {{ This must not move the cursor. }}
+ */
+clear_eol()
+{
+ if (mode_flags & M_SO)
+ so_exit();
+ if (mode_flags & M_UL)
+ ul_exit();
+ if (mode_flags & M_BO)
+ bo_exit();
+ tputs(sc_eol_clear, 1, putchr);
+}
+
+/*
+ * Begin "standout" (bold, underline, or whatever).
+ */
+so_enter()
+{
+ tputs(sc_s_in, 1, putchr);
+ mode_flags |= M_SO;
+}
+
+/*
+ * End "standout".
+ */
+so_exit()
+{
+ tputs(sc_s_out, 1, putchr);
+ mode_flags &= ~M_SO;
+}
+
+/*
+ * Begin "underline" (hopefully real underlining,
+ * otherwise whatever the terminal provides).
+ */
+ul_enter()
+{
+ tputs(sc_u_in, 1, putchr);
+ mode_flags |= M_UL;
+}
+
+/*
+ * End "underline".
+ */
+ul_exit()
+{
+ tputs(sc_u_out, 1, putchr);
+ mode_flags &= ~M_UL;
+}
+
+/*
+ * Begin "bold"
+ */
+bo_enter()
+{
+ tputs(sc_b_in, 1, putchr);
+ mode_flags |= M_BO;
+}
+
+/*
+ * End "bold".
+ */
+bo_exit()
+{
+ tputs(sc_b_out, 1, putchr);
+ mode_flags &= ~M_BO;
+}
+
+/*
+ * Erase the character to the left of the cursor
+ * and move the cursor left.
+ */
+backspace()
+{
+ /*
+ * Try to erase the previous character by overstriking with a space.
+ */
+ tputs(sc_backspace, 1, putchr);
+ putchr(' ');
+ tputs(sc_backspace, 1, putchr);
+}
+
+/*
+ * Output a plain backspace, without erasing the previous char.
+ */
+putbs()
+{
+ tputs(sc_backspace, 1, putchr);
+}
diff --git a/usr.bin/more/signal.c b/usr.bin/more/signal.c
new file mode 100644
index 0000000..67d7e51
--- /dev/null
+++ b/usr.bin/more/signal.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)signal.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with signals.
+ *
+ * A signal usually merely causes a bit to be set in the "signals" word.
+ * At some convenient time, the mainline code checks to see if any
+ * signals need processing by calling psignal().
+ * If we happen to be reading from a file [in iread()] at the time
+ * the signal is received, we call intread to interrupt the iread.
+ */
+
+#include <less.h>
+#include <signal.h>
+
+/*
+ * "sigs" contains bits indicating signals which need to be processed.
+ */
+int sigs;
+
+#ifdef SIGTSTP
+#define S_STOP 02
+#endif
+#if defined(SIGWINCH) || defined(SIGWIND)
+#define S_WINCH 04
+#endif
+
+extern int sc_width, sc_height;
+extern int screen_trashed;
+extern int lnloop;
+extern int linenums;
+extern int scroll;
+extern int reading;
+
+#ifdef SIGTSTP
+/*
+ * "Stop" (^Z) signal handler.
+ */
+static void
+stop()
+{
+ (void)signal(SIGTSTP, stop);
+ sigs |= S_STOP;
+ if (reading)
+ intread();
+}
+#endif
+
+#ifdef SIGWINCH
+/*
+ * "Window" change handler
+ */
+void
+winch()
+{
+ (void)signal(SIGWINCH, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#else
+#ifdef SIGWIND
+/*
+ * "Window" change handler
+ */
+winch()
+{
+ (void)signal(SIGWIND, winch);
+ sigs |= S_WINCH;
+ if (reading)
+ intread();
+}
+#endif
+#endif
+
+static void
+purgeandquit()
+{
+
+ purge(); /* purge buffered output */
+ quit();
+}
+
+/*
+ * Set up the signal handlers.
+ */
+init_signals(on)
+ int on;
+{
+ if (on)
+ {
+ /*
+ * Set signal handlers.
+ */
+ (void)signal(SIGINT, purgeandquit);
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, stop);
+#endif
+#ifdef SIGWINCH
+ (void)signal(SIGWINCH, winch);
+#else
+#ifdef SIGWIND
+ (void)signal(SIGWIND, winch);
+#endif
+#endif
+ } else
+ {
+ /*
+ * Restore signals to defaults.
+ */
+ (void)signal(SIGINT, SIG_DFL);
+#ifdef SIGTSTP
+ (void)signal(SIGTSTP, SIG_DFL);
+#endif
+#ifdef SIGWINCH
+ (void)signal(SIGWINCH, SIG_IGN);
+#endif
+#ifdef SIGWIND
+ (void)signal(SIGWIND, SIG_IGN);
+#endif
+ }
+}
+
+/*
+ * Process any signals we have received.
+ * A received signal cause a bit to be set in "sigs".
+ */
+psignals()
+{
+ register int tsignals;
+
+ if ((tsignals = sigs) == 0)
+ return;
+ sigs = 0;
+
+#ifdef S_WINCH
+ if (tsignals & S_WINCH)
+ {
+ int old_width, old_height;
+ /*
+ * Re-execute get_term() to read the new window size.
+ */
+ old_width = sc_width;
+ old_height = sc_height;
+ get_term();
+ if (sc_width != old_width || sc_height != old_height)
+ {
+ scroll = (sc_height + 1) / 2;
+ screen_trashed = 1;
+ }
+ }
+#endif
+#ifdef SIGTSTP
+ if (tsignals & S_STOP)
+ {
+ /*
+ * Clean up the terminal.
+ */
+#ifdef SIGTTOU
+ (void)signal(SIGTTOU, SIG_IGN);
+#endif
+ lower_left();
+ clear_eol();
+ deinit();
+ (void)flush();
+ raw_mode(0);
+#ifdef SIGTTOU
+ (void)signal(SIGTTOU, SIG_DFL);
+#endif
+ (void)signal(SIGTSTP, SIG_DFL);
+ (void)kill(getpid(), SIGTSTP);
+ /*
+ * ... Bye bye. ...
+ * Hopefully we'll be back later and resume here...
+ * Reset the terminal and arrange to repaint the
+ * screen when we get back to the main command loop.
+ */
+ (void)signal(SIGTSTP, stop);
+ raw_mode(1);
+ init();
+ screen_trashed = 1;
+ }
+#endif
+}
diff --git a/usr.bin/more/tags.c b/usr.bin/more/tags.c
new file mode 100644
index 0000000..0d805fa
--- /dev/null
+++ b/usr.bin/more/tags.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tags.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <less.h>
+
+#define WHITESP(c) ((c)==' ' || (c)=='\t')
+
+char *tagfile;
+char *tagpattern;
+
+static char *tags = "tags";
+
+extern int linenums;
+extern int sigs;
+extern char *line;
+
+/*
+ * Find a tag in the "tags" file.
+ * Sets "tagfile" to the name of the file containing the tag,
+ * and "tagpattern" to the search pattern which should be used
+ * to find the tag.
+ */
+findtag(tag)
+ register char *tag;
+{
+ register char *p;
+ register FILE *f;
+ register int taglen;
+ int search_char;
+ static char tline[200];
+
+ if ((f = fopen(tags, "r")) == NULL)
+ {
+ error("No tags file");
+ tagfile = NULL;
+ return;
+ }
+
+ taglen = strlen(tag);
+
+ /*
+ * Search the tags file for the desired tag.
+ */
+ while (fgets(tline, sizeof(tline), f) != NULL)
+ {
+ if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
+ continue;
+
+ /*
+ * Found it.
+ * The line contains the tag, the filename and the
+ * pattern, separated by white space.
+ * The pattern is surrounded by a pair of identical
+ * search characters.
+ * Parse the line and extract these parts.
+ */
+ tagfile = tagpattern = NULL;
+
+ /*
+ * Skip over the whitespace after the tag name.
+ */
+ for (p = tline; !WHITESP(*p) && *p != '\0'; p++)
+ continue;
+ while (WHITESP(*p))
+ p++;
+ if (*p == '\0')
+ /* File name is missing! */
+ continue;
+
+ /*
+ * Save the file name.
+ * Skip over the whitespace after the file name.
+ */
+ tagfile = p;
+ while (!WHITESP(*p) && *p != '\0')
+ p++;
+ *p++ = '\0';
+ while (WHITESP(*p))
+ p++;
+ if (*p == '\0')
+ /* Pattern is missing! */
+ continue;
+
+ /*
+ * Save the pattern.
+ * Skip to the end of the pattern.
+ * Delete the initial "^" and the final "$" from the pattern.
+ */
+ search_char = *p++;
+ if (*p == '^')
+ p++;
+ tagpattern = p;
+ while (*p != search_char && *p != '\0')
+ p++;
+ if (p[-1] == '$')
+ p--;
+ *p = '\0';
+
+ (void)fclose(f);
+ return;
+ }
+ (void)fclose(f);
+ error("No such tag in tags file");
+ tagfile = NULL;
+}
+
+/*
+ * Search for a tag.
+ * This is a stripped-down version of search().
+ * We don't use search() for several reasons:
+ * - We don't want to blow away any search string we may have saved.
+ * - The various regular-expression functions (from different systems:
+ * regcmp vs. re_comp) behave differently in the presence of
+ * parentheses (which are almost always found in a tag).
+ */
+tagsearch()
+{
+ off_t pos, linepos, forw_raw_line();
+ int linenum;
+
+ pos = (off_t)0;
+ linenum = find_linenum(pos);
+
+ for (;;)
+ {
+ /*
+ * Get lines until we find a matching one or
+ * until we hit end-of-file.
+ */
+ if (sigs)
+ return (1);
+
+ /*
+ * Read the next line, and save the
+ * starting position of that line in linepos.
+ */
+ linepos = pos;
+ pos = forw_raw_line(pos);
+ if (linenum != 0)
+ linenum++;
+
+ if (pos == NULL_POSITION)
+ {
+ /*
+ * We hit EOF without a match.
+ */
+ error("Tag not found");
+ return (1);
+ }
+
+ /*
+ * If we're using line numbers, we might as well
+ * remember the information we have now (the position
+ * and line number of the current line).
+ */
+ if (linenums)
+ add_lnum(linenum, pos);
+
+ /*
+ * Test the line to see if we have a match.
+ */
+ if (strcmp(tagpattern, line) == 0)
+ break;
+ }
+
+ jump_loc(linepos);
+ return (0);
+}
diff --git a/usr.bin/more/ttyin.c b/usr.bin/more/ttyin.c
new file mode 100644
index 0000000..51376cd
--- /dev/null
+++ b/usr.bin/more/ttyin.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1988 Mark Nudleman
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttyin.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines dealing with getting input from the keyboard (i.e. from the user).
+ */
+
+#include <less.h>
+
+static int tty;
+
+/*
+ * Open keyboard for input.
+ * (Just use file descriptor 2.)
+ */
+open_getchr()
+{
+ tty = 2;
+}
+
+/*
+ * Get a character from the keyboard.
+ */
+getchr()
+{
+ char c;
+ int result;
+
+ do
+ {
+ result = iread(tty, &c, 1);
+ if (result == READ_INTR)
+ return (READ_INTR);
+ if (result < 0)
+ {
+ /*
+ * Don't call error() here,
+ * because error calls getchr!
+ */
+ quit();
+ }
+ } while (result != 1);
+ return ((unsigned char)c);
+}
diff --git a/usr.bin/msgs/Makefile b/usr.bin/msgs/Makefile
new file mode 100644
index 0000000..8613f79c
--- /dev/null
+++ b/usr.bin/msgs/Makefile
@@ -0,0 +1,8 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= msgs
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/msgs/msgs.1 b/usr.bin/msgs/msgs.1
new file mode 100644
index 0000000..63ce7ae
--- /dev/null
+++ b/usr.bin/msgs/msgs.1
@@ -0,0 +1,214 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)msgs.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MSGS 1
+.Os BSD 4
+.Sh NAME
+.Nm msgs
+.Nd system messages and junk mail program
+.Sh SYNOPSIS
+.Nm msgs
+.Op Fl fhlpq
+.Op Ar number
+.Op Ar \-number
+.Nm msgs
+.Op Fl s
+.Nm msgs
+.Op Fl c
+.Op \-days
+.Sh DESCRIPTION
+.Nm Msgs
+is used to read system messages.
+These messages are
+sent by mailing to the login `msgs' and should be short
+pieces of information which are suitable to be read once by most users
+of the system.
+.Pp
+.Nm Msgs
+is normally invoked each time you login, by placing it in the file
+.Pa .login
+(or
+.Pa .profile
+if you use
+.Xr sh 1 ) .
+It will then prompt you with the source and subject of each new message.
+If there is no subject line, the first few non-blank lines of the
+message will be displayed.
+If there is more to the message, you will be told how
+long it is and asked whether you wish to see the rest of the message.
+The possible responses are:
+.Bl -tag -width Fl
+.It Fl y
+Type the rest of the message.
+.It Ic RETURN
+Synonym for y.
+.It Fl n
+Skip this message
+and go on to the next message.
+.It Fl
+Redisplay the last message.
+.It Fl q
+Drop out of
+.Nm msgs ;
+the next time
+.Nm msgs
+will pick up where it last left off.
+.It Fl s
+Append the current message to the file ``Messages'' in the current directory;
+`s\-' will save the previously displayed message. A `s' or `s\-' may
+be followed by a space and a file name to receive the message replacing
+the default ``Messages''.
+.It Fl m
+A copy of the specified message is placed in a temporary
+mailbox and
+.Xr mail 1
+is invoked on that mailbox.
+Both `m' and `s' accept a numeric argument in place of the `\-'.
+.El
+.Pp
+.Nm Msgs
+keeps track of the next message you will see by a number in the file
+.Pa \&.msgsrc
+in your home directory.
+In the directory
+.Pa /var/msgs
+it keeps a set of files whose names are the (sequential) numbers
+of the messages they represent.
+The file
+.Pa /var/msgs/bounds
+shows the low and high number of the messages in the directory
+so that
+.Nm msgs
+can quickly determine if there are no messages for you.
+If the contents of
+.Pa bounds
+is incorrect it can be fixed by removing it;
+.Nm msgs
+will make a new
+.Pa bounds
+file the next time it is run.
+.Pp
+The
+.Fl s
+option is used for setting up the posting of messages. The line
+.Pp
+.Dl msgs: \&"\&| /usr/bin/msgs \-s\&"
+.Pp
+should be included in
+.Pa /etc/aliases
+(see
+.Xr newaliases 1 )
+to enable posting of messages.
+.Pp
+The
+.Fl c
+option is used for performing cleanup on
+.Pa /var/msgs.
+An entry with the
+.Fl c
+option should be placed in
+.Pa /etc/crontab
+to run every night. This will remove all messages over 21 days old.
+A different expiration may be specified on the command line to override
+the default.
+.Pp
+Options when reading messages include:
+.Bl -tag -width Fl
+.It Fl f
+Do not to say ``No new messages.''.
+This is useful in a
+.Pa .login
+file since this is often the case here.
+.It Fl q
+Queries whether there are messages, printing
+``There are new messages.'' if there are.
+The command ``msgs \-q'' is often used in login scripts.
+.It Fl h
+Print the first part of messages only.
+.It Fl l
+Option causes only locally originated messages to be reported.
+.It Ar num
+A message number can be given
+on the command line, causing
+.Nm msgs
+to start at the specified message rather than at the next message
+indicated by your
+.Pa \&.msgsrc
+file.
+Thus
+.Pp
+.Dl msgs \-h 1
+.Pp
+prints the first part of all messages.
+.It Ar \-number
+Start
+.Ar number
+messages back from the one indicated in the
+.Pa \&.msgsrc
+file, useful for reviews of recent messages.
+.It Fl p
+Pipe long messages through
+.Xr more 1 .
+.El
+.Pp
+Within
+.Nm msgs
+you can also go to any specific message by typing its number when
+.Nm msgs
+requests input as to what to do.
+.Sh ENVIRONMENT
+.Nm Msgs
+uses the
+.Ev HOME
+and
+.Ev TERM
+environment variables for the default home directory and
+terminal type.
+.Sh FILES
+.Bl -tag -width /var/msgs/* -compact
+.It Pa /var/msgs/*
+database
+.It ~/.msgsrc
+number of next message to be presented
+.El
+.Sh SEE ALSO
+.Xr aliases 5 ,
+.\".Xr crontab 5 ,
+.Xr mail 1 ,
+.Xr more 1
+.Sh HISTORY
+The
+.Nm msgs
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/msgs/msgs.c b/usr.bin/msgs/msgs.c
new file mode 100644
index 0000000..fd7d230
--- /dev/null
+++ b/usr.bin/msgs/msgs.c
@@ -0,0 +1,862 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * msgs - a user bulletin board program
+ *
+ * usage:
+ * msgs [fhlopq] [[-]number] to read messages
+ * msgs -s to place messages
+ * msgs -c [-days] to clean up the bulletin board
+ *
+ * prompt commands are:
+ * y print message
+ * n flush message, go to next message
+ * q flush message, quit
+ * p print message, turn on 'pipe thru more' mode
+ * P print message, turn off 'pipe thru more' mode
+ * - reprint last message
+ * s[-][<num>] [<filename>] save message
+ * m[-][<num>] mail with message in temp mbox
+ * x exit without flushing this message
+ * <num> print message number <num>
+ */
+
+#define V7 /* will look for TERM in the environment */
+#define OBJECT /* will object to messages without Subjects */
+/* #define REJECT */ /* will reject messages without Subjects
+ (OBJECT must be defined also) */
+/* #define UNBUFFERED *//* use unbuffered output */
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <termios.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#define CMODE 0666 /* bounds file creation mode */
+#define NO 0
+#define YES 1
+#define SUPERUSER 0 /* superuser uid */
+#define DAEMON 1 /* daemon uid */
+#define NLINES 24 /* default number of lines/crt screen */
+#define NDAYS 21 /* default keep time for messages */
+#define DAYS *24*60*60 /* seconds/day */
+#define MSGSRC ".msgsrc" /* user's rc file */
+#define BOUNDS "bounds" /* message bounds file */
+#define NEXT "Next message? [yq]"
+#define MORE "More? [ynq]"
+#define NOMORE "(No more) [q] ?"
+
+typedef char bool;
+
+FILE *msgsrc;
+FILE *newmsg;
+char *sep = "-";
+char inbuf[BUFSIZ];
+char fname[128];
+char cmdbuf[128];
+char subj[128];
+char from[128];
+char date[128];
+char *ptr;
+char *in;
+bool local;
+bool ruptible;
+bool totty;
+bool seenfrom;
+bool seensubj;
+bool blankline;
+bool printing = NO;
+bool mailing = NO;
+bool quitit = NO;
+bool sending = NO;
+bool intrpflg = NO;
+int uid;
+int msg;
+int prevmsg;
+int lct;
+int nlines;
+int Lpp = 0;
+time_t t;
+time_t keep;
+
+char *mktemp();
+char *nxtfld();
+void onintr();
+void onsusp();
+
+/* option initialization */
+bool hdrs = NO;
+bool qopt = NO;
+bool hush = NO;
+bool send_msg = NO;
+bool locomode = NO;
+bool use_pager = NO;
+bool clean = NO;
+bool lastcmd = NO;
+jmp_buf tstpbuf;
+
+main(argc, argv)
+int argc; char *argv[];
+{
+ bool newrc, already;
+ int rcfirst = 0; /* first message to print (from .rc) */
+ int rcback = 0; /* amount to back off of rcfirst */
+ int firstmsg, nextmsg, lastmsg = 0;
+ int blast = 0;
+ FILE *bounds;
+
+#ifdef UNBUFFERED
+ setbuf(stdout, NULL);
+#endif
+
+
+ time(&t);
+ setuid(uid = getuid());
+ ruptible = (signal(SIGINT, SIG_IGN) == SIG_DFL);
+ if (ruptible)
+ signal(SIGINT, SIG_DFL);
+
+ argc--, argv++;
+ while (argc > 0) {
+ if (isdigit(argv[0][0])) { /* starting message # */
+ rcfirst = atoi(argv[0]);
+ }
+ else if (isdigit(argv[0][1])) { /* backward offset */
+ rcback = atoi( &( argv[0][1] ) );
+ }
+ else {
+ ptr = *argv;
+ while (*ptr) switch (*ptr++) {
+
+ case '-':
+ break;
+
+ case 'c':
+ if (uid != SUPERUSER && uid != DAEMON) {
+ fprintf(stderr, "Sorry\n");
+ exit(1);
+ }
+ clean = YES;
+ break;
+
+ case 'f': /* silently */
+ hush = YES;
+ break;
+
+ case 'h': /* headers only */
+ hdrs = YES;
+ break;
+
+ case 'l': /* local msgs only */
+ locomode = YES;
+ break;
+
+ case 'o': /* option to save last message */
+ lastcmd = YES;
+ break;
+
+ case 'p': /* pipe thru 'more' during long msgs */
+ use_pager = YES;
+ break;
+
+ case 'q': /* query only */
+ qopt = YES;
+ break;
+
+ case 's': /* sending TO msgs */
+ send_msg = YES;
+ break;
+
+ default:
+ fprintf(stderr,
+ "usage: msgs [fhlopq] [[-]number]\n");
+ exit(1);
+ }
+ }
+ argc--, argv++;
+ }
+
+ /*
+ * determine current message bounds
+ */
+ sprintf(fname, "%s/%s", _PATH_MSGS, BOUNDS);
+ bounds = fopen(fname, "r");
+
+ if (bounds != NULL) {
+ fscanf(bounds, "%d %d\n", &firstmsg, &lastmsg);
+ fclose(bounds);
+ blast = lastmsg; /* save upper bound */
+ }
+
+ if (clean)
+ keep = t - (rcback? rcback : NDAYS) DAYS;
+
+ if (clean || bounds == NULL) { /* relocate message bounds */
+ struct direct *dp;
+ struct stat stbuf;
+ bool seenany = NO;
+ DIR *dirp;
+
+ dirp = opendir(_PATH_MSGS);
+ if (dirp == NULL) {
+ perror(_PATH_MSGS);
+ exit(errno);
+ }
+
+ firstmsg = 32767;
+ lastmsg = 0;
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)){
+ register char *cp = dp->d_name;
+ register int i = 0;
+
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_namlen == 0)
+ continue;
+
+ if (clean)
+ sprintf(inbuf, "%s/%s", _PATH_MSGS, cp);
+
+ while (isdigit(*cp))
+ i = i * 10 + *cp++ - '0';
+ if (*cp)
+ continue; /* not a message! */
+
+ if (clean) {
+ if (stat(inbuf, &stbuf) != 0)
+ continue;
+ if (stbuf.st_mtime < keep
+ && stbuf.st_mode&S_IWRITE) {
+ unlink(inbuf);
+ continue;
+ }
+ }
+
+ if (i > lastmsg)
+ lastmsg = i;
+ if (i < firstmsg)
+ firstmsg = i;
+ seenany = YES;
+ }
+ closedir(dirp);
+
+ if (!seenany) {
+ if (blast != 0) /* never lower the upper bound! */
+ lastmsg = blast;
+ firstmsg = lastmsg + 1;
+ }
+ else if (blast > lastmsg)
+ lastmsg = blast;
+
+ if (!send_msg) {
+ bounds = fopen(fname, "w");
+ if (bounds == NULL) {
+ perror(fname);
+ exit(errno);
+ }
+ chmod(fname, CMODE);
+ fprintf(bounds, "%d %d\n", firstmsg, lastmsg);
+ fclose(bounds);
+ }
+ }
+
+ if (send_msg) {
+ /*
+ * Send mode - place msgs in _PATH_MSGS
+ */
+ bounds = fopen(fname, "w");
+ if (bounds == NULL) {
+ perror(fname);
+ exit(errno);
+ }
+
+ nextmsg = lastmsg + 1;
+ sprintf(fname, "%s/%d", _PATH_MSGS, nextmsg);
+ newmsg = fopen(fname, "w");
+ if (newmsg == NULL) {
+ perror(fname);
+ exit(errno);
+ }
+ chmod(fname, 0644);
+
+ fprintf(bounds, "%d %d\n", firstmsg, nextmsg);
+ fclose(bounds);
+
+ sending = YES;
+ if (ruptible)
+ signal(SIGINT, onintr);
+
+ if (isatty(fileno(stdin))) {
+ ptr = getpwuid(uid)->pw_name;
+ printf("Message %d:\nFrom %s %sSubject: ",
+ nextmsg, ptr, ctime(&t));
+ fflush(stdout);
+ fgets(inbuf, sizeof inbuf, stdin);
+ putchar('\n');
+ fflush(stdout);
+ fprintf(newmsg, "From %s %sSubject: %s\n",
+ ptr, ctime(&t), inbuf);
+ blankline = seensubj = YES;
+ }
+ else
+ blankline = seensubj = NO;
+ for (;;) {
+ fgets(inbuf, sizeof inbuf, stdin);
+ if (feof(stdin) || ferror(stdin))
+ break;
+ blankline = (blankline || (inbuf[0] == '\n'));
+ seensubj = (seensubj || (!blankline && (strncmp(inbuf, "Subj", 4) == 0)));
+ fputs(inbuf, newmsg);
+ }
+#ifdef OBJECT
+ if (!seensubj) {
+ printf("NOTICE: Messages should have a Subject field!\n");
+#ifdef REJECT
+ unlink(fname);
+#endif
+ exit(1);
+ }
+#endif
+ exit(ferror(stdin));
+ }
+ if (clean)
+ exit(0);
+
+ /*
+ * prepare to display messages
+ */
+ totty = (isatty(fileno(stdout)) != 0);
+ use_pager = use_pager && totty;
+
+ sprintf(fname, "%s/%s", getenv("HOME"), MSGSRC);
+ msgsrc = fopen(fname, "r");
+ if (msgsrc) {
+ newrc = NO;
+ fscanf(msgsrc, "%d\n", &nextmsg);
+ fclose(msgsrc);
+ if (nextmsg > lastmsg+1) {
+ printf("Warning: bounds have been reset (%d, %d)\n",
+ firstmsg, lastmsg);
+ truncate(fname, (off_t)0);
+ newrc = YES;
+ }
+ else if (!rcfirst)
+ rcfirst = nextmsg - rcback;
+ }
+ else
+ newrc = YES;
+ msgsrc = fopen(fname, "r+");
+ if (msgsrc == NULL)
+ msgsrc = fopen(fname, "w");
+ if (msgsrc == NULL) {
+ perror(fname);
+ exit(errno);
+ }
+ if (rcfirst) {
+ if (rcfirst > lastmsg+1) {
+ printf("Warning: the last message is number %d.\n",
+ lastmsg);
+ rcfirst = nextmsg;
+ }
+ if (rcfirst > firstmsg)
+ firstmsg = rcfirst; /* don't set below first msg */
+ }
+ if (newrc) {
+ nextmsg = firstmsg;
+ fseek(msgsrc, 0L, 0);
+ fprintf(msgsrc, "%d\n", nextmsg);
+ fflush(msgsrc);
+ }
+
+#ifdef V7
+ if (totty) {
+ struct winsize win;
+ if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1)
+ Lpp = win.ws_row;
+ if (Lpp <= 0) {
+ if (tgetent(inbuf, getenv("TERM")) <= 0
+ || (Lpp = tgetnum("li")) <= 0) {
+ Lpp = NLINES;
+ }
+ }
+ }
+#endif
+ Lpp -= 6; /* for headers, etc. */
+
+ already = NO;
+ prevmsg = firstmsg;
+ printing = YES;
+ if (ruptible)
+ signal(SIGINT, onintr);
+
+ /*
+ * Main program loop
+ */
+ for (msg = firstmsg; msg <= lastmsg; msg++) {
+
+ sprintf(fname, "%s/%d", _PATH_MSGS, msg);
+ newmsg = fopen(fname, "r");
+ if (newmsg == NULL)
+ continue;
+
+ gfrsub(newmsg); /* get From and Subject fields */
+ if (locomode && !local) {
+ fclose(newmsg);
+ continue;
+ }
+
+ if (qopt) { /* This has to be located here */
+ printf("There are new messages.\n");
+ exit(0);
+ }
+
+ if (already && !hdrs)
+ putchar('\n');
+
+ /*
+ * Print header
+ */
+ if (totty)
+ signal(SIGTSTP, onsusp);
+ (void) setjmp(tstpbuf);
+ already = YES;
+ nlines = 2;
+ if (seenfrom) {
+ printf("Message %d:\nFrom %s %s", msg, from, date);
+ nlines++;
+ }
+ if (seensubj) {
+ printf("Subject: %s", subj);
+ nlines++;
+ }
+ else {
+ if (seenfrom) {
+ putchar('\n');
+ nlines++;
+ }
+ while (nlines < 6
+ && fgets(inbuf, sizeof inbuf, newmsg)
+ && inbuf[0] != '\n') {
+ fputs(inbuf, stdout);
+ nlines++;
+ }
+ }
+
+ lct = linecnt(newmsg);
+ if (lct)
+ printf("(%d%slines) ", lct, seensubj? " " : " more ");
+
+ if (hdrs) {
+ printf("\n-----\n");
+ fclose(newmsg);
+ continue;
+ }
+
+ /*
+ * Ask user for command
+ */
+ if (totty)
+ ask(lct? MORE : (msg==lastmsg? NOMORE : NEXT));
+ else
+ inbuf[0] = 'y';
+ if (totty)
+ signal(SIGTSTP, SIG_DFL);
+cmnd:
+ in = inbuf;
+ switch (*in) {
+ case 'x':
+ case 'X':
+ exit(0);
+
+ case 'q':
+ case 'Q':
+ quitit = YES;
+ printf("--Postponed--\n");
+ exit(0);
+ /* intentional fall-thru */
+ case 'n':
+ case 'N':
+ if (msg >= nextmsg) sep = "Flushed";
+ prevmsg = msg;
+ break;
+
+ case 'p':
+ case 'P':
+ use_pager = (*in++ == 'p');
+ /* intentional fallthru */
+ case '\n':
+ case 'y':
+ default:
+ if (*in == '-') {
+ msg = prevmsg-1;
+ sep = "replay";
+ break;
+ }
+ if (isdigit(*in)) {
+ msg = next(in);
+ sep = in;
+ break;
+ }
+
+ prmesg(nlines + lct + (seensubj? 1 : 0));
+ prevmsg = msg;
+
+ }
+
+ printf("--%s--\n", sep);
+ sep = "-";
+ if (msg >= nextmsg) {
+ nextmsg = msg + 1;
+ fseek(msgsrc, 0L, 0);
+ fprintf(msgsrc, "%d\n", nextmsg);
+ fflush(msgsrc);
+ }
+ if (newmsg)
+ fclose(newmsg);
+ if (quitit)
+ break;
+ }
+
+ /*
+ * Make sure .rc file gets updated
+ */
+ if (--msg >= nextmsg) {
+ nextmsg = msg + 1;
+ fseek(msgsrc, 0L, 0);
+ fprintf(msgsrc, "%d\n", nextmsg);
+ fflush(msgsrc);
+ }
+ if (already && !quitit && lastcmd && totty) {
+ /*
+ * save or reply to last message?
+ */
+ msg = prevmsg;
+ ask(NOMORE);
+ if (inbuf[0] == '-' || isdigit(inbuf[0]))
+ goto cmnd;
+ }
+ if (!(already || hush || qopt))
+ printf("No new messages.\n");
+ exit(0);
+}
+
+prmesg(length)
+int length;
+{
+ FILE *outf;
+
+ if (use_pager && length > Lpp) {
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ sprintf(cmdbuf, _PATH_PAGER, Lpp);
+ outf = popen(cmdbuf, "w");
+ if (!outf)
+ outf = stdout;
+ else
+ setbuf(outf, (char *)NULL);
+ }
+ else
+ outf = stdout;
+
+ if (seensubj)
+ putc('\n', outf);
+
+ while (fgets(inbuf, sizeof inbuf, newmsg)) {
+ fputs(inbuf, outf);
+ if (ferror(outf)) {
+ clearerr(outf);
+ break;
+ }
+ }
+
+ if (outf != stdout) {
+ pclose(outf);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ }
+ else {
+ fflush(stdout);
+ }
+
+ /* force wait on output */
+ tcdrain(fileno(stdout));
+}
+
+void
+onintr()
+{
+ signal(SIGINT, onintr);
+ if (mailing)
+ unlink(fname);
+ if (sending) {
+ unlink(fname);
+ puts("--Killed--");
+ exit(1);
+ }
+ if (printing) {
+ putchar('\n');
+ if (hdrs)
+ exit(0);
+ sep = "Interrupt";
+ if (newmsg)
+ fseek(newmsg, 0L, 2);
+ intrpflg = YES;
+ }
+}
+
+/*
+ * We have just gotten a susp. Suspend and prepare to resume.
+ */
+void
+onsusp()
+{
+
+ signal(SIGTSTP, SIG_DFL);
+ sigsetmask(0);
+ kill(0, SIGTSTP);
+ signal(SIGTSTP, onsusp);
+ if (!mailing)
+ longjmp(tstpbuf, 0);
+}
+
+linecnt(f)
+FILE *f;
+{
+ off_t oldpos = ftell(f);
+ int l = 0;
+ char lbuf[BUFSIZ];
+
+ while (fgets(lbuf, sizeof lbuf, f))
+ l++;
+ clearerr(f);
+ fseek(f, oldpos, 0);
+ return (l);
+}
+
+next(buf)
+char *buf;
+{
+ int i;
+ sscanf(buf, "%d", &i);
+ sprintf(buf, "Goto %d", i);
+ return(--i);
+}
+
+ask(prompt)
+char *prompt;
+{
+ char inch;
+ int n, cmsg;
+ off_t oldpos;
+ FILE *cpfrom, *cpto;
+
+ printf("%s ", prompt);
+ fflush(stdout);
+ intrpflg = NO;
+ (void) fgets(inbuf, sizeof inbuf, stdin);
+ if ((n = strlen(inbuf)) > 0 && inbuf[n - 1] == '\n')
+ inbuf[n - 1] = '\0';
+ if (intrpflg)
+ inbuf[0] = 'x';
+
+ /*
+ * Handle 'mail' and 'save' here.
+ */
+ if ((inch = inbuf[0]) == 's' || inch == 'm') {
+ if (inbuf[1] == '-')
+ cmsg = prevmsg;
+ else if (isdigit(inbuf[1]))
+ cmsg = atoi(&inbuf[1]);
+ else
+ cmsg = msg;
+ sprintf(fname, "%s/%d", _PATH_MSGS, cmsg);
+
+ oldpos = ftell(newmsg);
+
+ cpfrom = fopen(fname, "r");
+ if (!cpfrom) {
+ printf("Message %d not found\n", cmsg);
+ ask (prompt);
+ return;
+ }
+
+ if (inch == 's') {
+ in = nxtfld(inbuf);
+ if (*in) {
+ for (n=0; in[n] > ' '; n++) { /* sizeof fname? */
+ fname[n] = in[n];
+ }
+ fname[n] = NULL;
+ }
+ else
+ strcpy(fname, "Messages");
+ }
+ else {
+ strcpy(fname, _PATH_TMP);
+ mktemp(fname);
+ sprintf(cmdbuf, _PATH_MAIL, fname);
+ mailing = YES;
+ }
+ cpto = fopen(fname, "a");
+ if (!cpto) {
+ perror(fname);
+ mailing = NO;
+ fseek(newmsg, oldpos, 0);
+ ask(prompt);
+ return;
+ }
+
+ while (n = fread(inbuf, 1, sizeof inbuf, cpfrom))
+ fwrite(inbuf, 1, n, cpto);
+
+ fclose(cpfrom);
+ fclose(cpto);
+ fseek(newmsg, oldpos, 0); /* reposition current message */
+ if (inch == 's')
+ printf("Message %d saved in \"%s\"\n", cmsg, fname);
+ else {
+ system(cmdbuf);
+ unlink(fname);
+ mailing = NO;
+ }
+ ask(prompt);
+ }
+}
+
+gfrsub(infile)
+FILE *infile;
+{
+ off_t frompos;
+
+ seensubj = seenfrom = NO;
+ local = YES;
+ subj[0] = from[0] = date[0] = NULL;
+
+ /*
+ * Is this a normal message?
+ */
+ if (fgets(inbuf, sizeof inbuf, infile)) {
+ if (strncmp(inbuf, "From", 4)==0) {
+ /*
+ * expected form starts with From
+ */
+ seenfrom = YES;
+ frompos = ftell(infile);
+ ptr = from;
+ in = nxtfld(inbuf);
+ if (*in) while (*in && *in > ' ') {
+ if (*in == ':' || *in == '@' || *in == '!')
+ local = NO;
+ *ptr++ = *in++;
+ /* what about sizeof from ? */
+ }
+ *ptr = NULL;
+ if (*(in = nxtfld(in)))
+ strncpy(date, in, sizeof date);
+ else {
+ date[0] = '\n';
+ date[1] = NULL;
+ }
+ }
+ else {
+ /*
+ * not the expected form
+ */
+ fseek(infile, 0L, 0);
+ return;
+ }
+ }
+ else
+ /*
+ * empty file ?
+ */
+ return;
+
+ /*
+ * look for Subject line until EOF or a blank line
+ */
+ while (fgets(inbuf, sizeof inbuf, infile)
+ && !(blankline = (inbuf[0] == '\n'))) {
+ /*
+ * extract Subject line
+ */
+ if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
+ seensubj = YES;
+ frompos = ftell(infile);
+ strncpy(subj, nxtfld(inbuf), sizeof subj);
+ }
+ }
+ if (!blankline)
+ /*
+ * ran into EOF
+ */
+ fseek(infile, frompos, 0);
+
+ if (!seensubj)
+ /*
+ * for possible use with Mail
+ */
+ strncpy(subj, "(No Subject)\n", sizeof subj);
+}
+
+char *
+nxtfld(s)
+char *s;
+{
+ if (*s) while (*s && *s > ' ') s++; /* skip over this field */
+ if (*s) while (*s && *s <= ' ') s++; /* find start of next field */
+ return (s);
+}
diff --git a/usr.bin/msgs/pathnames.h b/usr.bin/msgs/pathnames.h
new file mode 100644
index 0000000..90f23f4
--- /dev/null
+++ b/usr.bin/msgs/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_MSGS "/var/msgs"
+#define _PATH_MAIL "/usr/bin/Mail -f %s"
+#define _PATH_PAGER "/usr/bin/more -%d"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/msgXXXXXX"
diff --git a/usr.bin/mt/Makefile b/usr.bin/mt/Makefile
new file mode 100644
index 0000000..253fa99
--- /dev/null
+++ b/usr.bin/mt/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mt
+CFLAGS+=-I/sys
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/mt/mt.1 b/usr.bin/mt/mt.1
new file mode 100644
index 0000000..d27c168
--- /dev/null
+++ b/usr.bin/mt/mt.1
@@ -0,0 +1,222 @@
+.\" Copyright (c) 1981, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mt.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MT 1
+.Os BSD 4
+.Sh NAME
+.Nm mt
+.Nd magnetic tape manipulating program
+.Sh SYNOPSIS
+.Nm mt
+.Op Fl f Ar tapename
+.Ar command
+.Op Ar count
+.Sh DESCRIPTION
+.Nm Mt
+is used to give commands to a magnetic tape drive.
+By default
+.Nm mt
+performs the requested operation once. Operations
+may be performed multiple times by specifying
+.Ar count .
+Note
+that
+.Ar tapename
+must reference a raw (not block) tape device.
+.Pp
+The available commands are listed below. Only as many
+characters as are required to uniquely identify a command
+need be specified.
+.Bl -tag -width "eof, weof"
+.It Cm weof
+Write
+.Ar count
+end-of-file marks at the current position on the tape.
+.It Cm fsf
+Forward space
+.Ar count
+files.
+.It Cm fsr
+Forward space
+.Ar count
+records.
+.It Cm bsf
+Back space
+.Ar count
+files.
+.It Cm bsr
+Back space
+.Ar count
+records.
+.It Cm rewind
+Rewind the tape
+(Count is ignored).
+.It Cm offline , rewoffl
+Rewind the tape and place the tape unit off-line
+(Count is ignored).
+.It Cm erase
+Erase the tape
+(Count is ignored).
+.It Cm status
+Print status information about the tape unit.
+.It Cm blocksize
+Set the block size for the tape unit. Zero means variable-length
+blocks.
+.It Cm density
+Set the density for the tape unit. For the density codes, see below.
+The density value could be given either numerically, or as a string,
+corresponding to the
+.Dq Reference
+field. If the string is abbreviated, it will be resolved in the order
+shown in the table, and the first matching entry will be used. If the
+given string and the resulting canonical density name do not match
+exactly, an informational message is printed about what the given
+string has been taken for.
+.It Cm eom
+Forward space to end of recorded medium
+(Count is ignored).
+.It Cm comp
+Set compression mode.
+(Not yet implemented.)
+.El
+.Pp
+If a tape name is not specified, and the environment variable
+.Ev TAPE
+does not exist;
+.Nm mt
+uses the device
+.Pa /dev/nrst0 .
+.Pp
+.Nm Mt
+returns a 0 exit status when the operation(s) were successful,
+1 if the command was unrecognized, and 2 if an operation failed.
+.Pp
+The different density codes are as follows:
+.Pp
+.Dl 0x0 default for device
+.Dl 0xE reserved for ECMA
+.Bd -literal -offset indent
+Value Tracks Density(bpi) Code Type Reference Note
+0x1 9 800 NRZI R X3.22-1983 2
+0x2 9 1600 PE R X3.39-1986 2
+0x3 9 6250 GCR R X3.54-1986 2
+0x5 4/9 8000 GCR C X3.136-1986 1
+0x6 9 3200 PE R X3.157-1987 2
+0x7 4 6400 IMFM C X3.116-1986 1
+0x8 4 8000 GCR CS X3.158-1986 1
+0x9 18 37871 GCR C X3B5/87-099 2
+0xA 22 6667 MFM C X3B5/86-199 1
+0xB 4 1600 PE C X3.56-1986 1
+0xC 24 12690 GCR C HI-TC1 1,5
+0xD 24 25380 GCR C HI-TC2 1,5
+0xF 15 10000 GCR C QIC-120 1,5
+0x10 18 10000 GCR C QIC-150 1,5
+0x11 26 16000 GCR C QIC-320(525?) 1,5
+0x12 30 51667 RLL C QIC-1350 1,5
+0x13 1 61000 DDS CS X3B5/88-185A 4
+0x14 1 43245 RLL CS X3.202-1991 4
+0x15 1 45434 RLL CS ECMA TC17 4
+0x16 48 10000 MFM C X3.193-1990 1
+0x17 48 42500 MFM C X3B5/91-174 1
+.Ed
+
+where Code means:
+.Bd -literal -offset indent
+NRZI Non Return to Zero, change on ones
+GCR Group Code Recording
+PE Phase Encoded
+IMFM Inverted Modified Frequency Modulation
+MFM Modified Frequency Modulation
+DDS Dat Data Storage
+RLL Run Length Encoding
+.Ed
+
+where Type means:
+.Bd -literal -offset indent
+R Reel-to-Reel
+C Cartridge
+CS cassette
+.Ed
+
+where Notes means:
+.Bd -literal -offset indent
+1 Serial Recorded
+2 Parallel Recorded
+3 Old format know as QIC-11
+4 Helical Scan
+5 Not ANSI standard, rather industry standard.
+.Ed
+
+.Sh ENVIRONMENT
+If the following environment variable exists, it is utilized by
+.Nm mt .
+.Bl -tag -width Fl
+.It Ev TAPE
+.Nm Mt
+checks the
+.Ev TAPE
+environment variable if the
+argument
+.Ar tapename
+is not given.
+.Sh FILES
+.Bl -tag -width /dev/rwt* -compact
+.It Pa /dev/rwt*
+Raw magnetic tape interface
+.It Pa /dev/*st[0-9]*
+SCSI magnetic tape interface
+.El
+.Sh SEE ALSO
+.\".Xr mtio 4 ,
+.Xr st 4 ,
+.\".Xr wt 4 ,
+.Xr dd 1 ,
+.Xr ioctl 2 ,
+.Xr environ 7
+.Sh HISTORY
+The
+.Nm mt
+command appeared in
+.Bx 4.3 .
+
+Extensions regarding the
+.Xr st 4
+driver appeared in 386BSD 0.1 as a separate
+.Xr st 1
+command, and have been merged into the
+.Nm
+command in FreeBSD 2.1.
+.\" mt.1: mtio(4) missing
+.\" mt.1: wt(4) missing
diff --git a/usr.bin/mt/mt.c b/usr.bin/mt/mt.c
new file mode 100644
index 0000000..81bdefb
--- /dev/null
+++ b/usr.bin/mt/mt.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * mt --
+ * magnetic tape manipulation program
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/* the appropriate sections of <sys/mtio.h> are also #ifdef'd for FreeBSD */
+#if defined(__FreeBSD__)
+/* c_flags */
+#define NEED_2ARGS 0x01
+#define ZERO_ALLOWED 0x02
+#define IS_DENSITY 0x04
+#define DISABLE_THIS 0x08
+#endif /* defined(__FreeBSD__) */
+
+struct commands {
+ char *c_name;
+ int c_code;
+ int c_ronly;
+#if defined(__FreeBSD__)
+ int c_flags;
+#endif /* defined(__FreeBSD__) */
+} com[] = {
+ { "bsf", MTBSF, 1 },
+ { "bsr", MTBSR, 1 },
+#if defined(__FreeBSD__)
+ /* XXX FreeBSD considered "eof" dangerous, since it's being
+ confused with "eom" (and is an alias for "weof" anyway) */
+ { "eof", MTWEOF, 0, DISABLE_THIS },
+#else
+ { "eof", MTWEOF, 0 },
+#endif
+ { "fsf", MTFSF, 1 },
+ { "fsr", MTFSR, 1 },
+ { "offline", MTOFFL, 1 },
+ { "rewind", MTREW, 1 },
+ { "rewoffl", MTOFFL, 1 },
+ { "status", MTNOP, 1 },
+ { "weof", MTWEOF, 0 },
+#if defined(__FreeBSD__)
+ { "erase", MTERASE, 0 },
+ { "blocksize", MTSETBSIZ, 0, NEED_2ARGS|ZERO_ALLOWED },
+ { "density", MTSETDNSTY, 0, NEED_2ARGS|ZERO_ALLOWED|IS_DENSITY },
+ { "eom", MTEOD, 1 },
+ { "comp", MTCOMP, 0, NEED_2ARGS|ZERO_ALLOWED },
+#endif /* defined(__FreeBSD__) */
+ { NULL }
+};
+
+void err __P((const char *, ...));
+void printreg __P((char *, u_int, char *));
+void status __P((struct mtget *));
+void usage __P((void));
+#if defined (__FreeBSD__)
+void st_status (struct mtget *);
+int stringtodens (const char *s);
+const char *denstostring (int d);
+void warn_eof __P((void));
+#endif /* defined (__FreeBSD__) */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct commands *comp;
+ struct mtget mt_status;
+ struct mtop mt_com;
+ int ch, len, mtfd;
+ char *p, *tape;
+
+ if ((tape = getenv("TAPE")) == NULL)
+ tape = DEFTAPE;
+
+ while ((ch = getopt(argc, argv, "f:t:")) != EOF)
+ switch(ch) {
+ case 'f':
+ case 't':
+ tape = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1 || argc > 2)
+ usage();
+
+ len = strlen(p = *argv++);
+ for (comp = com;; comp++) {
+ if (comp->c_name == NULL)
+ err("%s: unknown command", p);
+ if (strncmp(p, comp->c_name, len) == 0)
+ break;
+ }
+#if defined(__FreeBSD__)
+ if((comp->c_flags & NEED_2ARGS) && argc != 2)
+ usage();
+ if(comp->c_flags & DISABLE_THIS) {
+ warn_eof();
+ }
+#endif /* defined(__FreeBSD__) */
+ if ((mtfd = open(tape, comp->c_ronly ? O_RDONLY : O_RDWR)) < 0)
+ err("%s: %s", tape, strerror(errno));
+ if (comp->c_code != MTNOP) {
+ mt_com.mt_op = comp->c_code;
+ if (*argv) {
+#if defined (__FreeBSD__)
+ if (!isdigit(**argv) &&
+ comp->c_flags & IS_DENSITY) {
+ const char *dcanon;
+ mt_com.mt_count = stringtodens(*argv);
+ if (mt_com.mt_count == 0)
+ err("%s: unknown density", *argv);
+ dcanon = denstostring(mt_com.mt_count);
+ if (strcmp(dcanon, *argv) != 0)
+ printf(
+ "Using \"%s\" as an alias for %s\n",
+ *argv, dcanon);
+ p = "";
+ } else
+ /* allow for hex numbers; useful for density */
+ mt_com.mt_count = strtol(*argv, &p, 0);
+#else
+ mt_com.mt_count = strtol(*argv, &p, 10);
+#endif /* defined(__FreeBSD__) */
+ if (mt_com.mt_count <=
+#if defined (__FreeBSD__)
+ ((comp->c_flags & ZERO_ALLOWED)? -1: 0)
+#else
+ 0
+#endif /* defined (__FreeBSD__) */
+ || *p)
+ err("%s: illegal count", *argv);
+ }
+ else
+ mt_com.mt_count = 1;
+ if (ioctl(mtfd, MTIOCTOP, &mt_com) < 0)
+ err("%s: %s: %s", tape, comp->c_name, strerror(errno));
+ } else {
+ if (ioctl(mtfd, MTIOCGET, &mt_status) < 0)
+ err("%s", strerror(errno));
+ status(&mt_status);
+ }
+ exit (0);
+ /* NOTREACHED */
+}
+
+#ifdef vax
+#include <vax/mba/mtreg.h>
+#include <vax/mba/htreg.h>
+
+#include <vax/uba/utreg.h>
+#include <vax/uba/tmreg.h>
+#undef b_repcnt /* argh */
+#include <vax/uba/tsreg.h>
+#endif
+
+#ifdef sun
+#include <sundev/tmreg.h>
+#include <sundev/arreg.h>
+#endif
+
+#ifdef tahoe
+#include <tahoe/vba/cyreg.h>
+#endif
+
+struct tape_desc {
+ short t_type; /* type of magtape device */
+ char *t_name; /* printing name */
+ char *t_dsbits; /* "drive status" register */
+ char *t_erbits; /* "error" register */
+} tapes[] = {
+#ifdef vax
+ { MT_ISTS, "ts11", 0, TSXS0_BITS },
+ { MT_ISHT, "tm03", HTDS_BITS, HTER_BITS },
+ { MT_ISTM, "tm11", 0, TMER_BITS },
+ { MT_ISMT, "tu78", MTDS_BITS, 0 },
+ { MT_ISUT, "tu45", UTDS_BITS, UTER_BITS },
+#endif
+#ifdef sun
+ { MT_ISCPC, "TapeMaster", TMS_BITS, 0 },
+ { MT_ISAR, "Archive", ARCH_CTRL_BITS, ARCH_BITS },
+#endif
+#ifdef tahoe
+ { MT_ISCY, "cipher", CYS_BITS, CYCW_BITS },
+#endif
+#if defined (__FreeBSD__)
+ /*
+ * XXX This is terrific. The st driver reports the tape drive
+ * as 0x7 (MT_ISAR - Sun/Archive compatible); the wt driver
+ * either reports MT_ISVIPER1 for an Archive tape, or 0x11
+ * (MT_ISMFOUR) for other tapes.
+ * XXX for the wt driver, rely on it behaving like a "standard"
+ * magtape driver.
+ */
+ { MT_ISAR, "SCSI tape drive", 0, 0 },
+ { MT_ISVIPER1, "Archive Viper", 0, 0 },
+ { MT_ISMFOUR, "Wangtek", 0, 0 },
+#endif /* defined (__FreeBSD__) */
+ { 0 }
+};
+
+/*
+ * Interpret the status buffer returned
+ */
+void
+status(bp)
+ register struct mtget *bp;
+{
+ register struct tape_desc *mt;
+
+ for (mt = tapes;; mt++) {
+ if (mt->t_type == 0) {
+ (void)printf("%d: unknown tape drive type\n",
+ bp->mt_type);
+ return;
+ }
+ if (mt->t_type == bp->mt_type)
+ break;
+ }
+#if defined (__FreeBSD__)
+ if(mt->t_type == MT_ISAR)
+ st_status(bp);
+ else {
+#endif /* defined (__FreeBSD__) */
+ (void)printf("%s tape drive, residual=%d\n", mt->t_name, bp->mt_resid);
+ printreg("ds", bp->mt_dsreg, mt->t_dsbits);
+ printreg("\ner", bp->mt_erreg, mt->t_erbits);
+ (void)putchar('\n');
+#if defined (__FreeBSD__)
+ }
+#endif /* defined (__FreeBSD__) */
+}
+
+/*
+ * Print a register a la the %b format of the kernel's printf.
+ */
+void
+printreg(s, v, bits)
+ char *s;
+ register u_int v;
+ register char *bits;
+{
+ register int i, any = 0;
+ register char c;
+
+ if (bits && *bits == 8)
+ printf("%s=%o", s, v);
+ else
+ printf("%s=%x", s, v);
+ if (!bits)
+ return;
+ bits++;
+ if (v && bits) {
+ putchar('<');
+ while (i = *bits++) {
+ if (v & (1 << (i-1))) {
+ if (any)
+ putchar(',');
+ any = 1;
+ for (; (c = *bits) > 32; bits++)
+ putchar(c);
+ } else
+ for (; *bits > 32; bits++)
+ ;
+ }
+ putchar('>');
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: mt [-f device] command [ count ]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "mt: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+#if defined (__FreeBSD__)
+
+struct densities {
+ int dens;
+ const char *name;
+} dens [] = {
+ { 0x1, "X3.22-1983" },
+ { 0x2, "X3.39-1986" },
+ { 0x3, "X3.54-1986" },
+ { 0x5, "X3.136-1986" },
+ { 0x6, "X3.157-1987" },
+ { 0x7, "X3.116-1986" },
+ { 0x8, "X3.158-1986" },
+ { 0x9, "X3B5/87-099" },
+ { 0xA, "X3B5/86-199" },
+ { 0xB, "X3.56-1986" },
+ { 0xC, "HI-TC1" },
+ { 0xD, "HI-TC2" },
+ { 0xF, "QIC-120" },
+ { 0x10, "QIC-150" },
+ { 0x11, "QIC-320" },
+ { 0x12, "QIC-1350" },
+ { 0x13, "X3B5/88-185A" },
+ { 0x14, "X3.202-1991" },
+ { 0x15, "ECMA TC17" },
+ { 0x16, "X3.193-1990" },
+ { 0x17, "X3B5/91-174" },
+ { 0, 0 }
+};
+
+const char *
+denstostring(int d)
+{
+ static char buf[20];
+ struct densities *sd;
+
+ for (sd = dens; sd->dens; sd++)
+ if (sd->dens == d)
+ break;
+ if (sd->dens == 0) {
+ sprintf(buf, "0x%02x", d);
+ return buf;
+ } else
+ return sd->name;
+}
+
+int
+stringtodens(const char *s)
+{
+ struct densities *sd;
+ size_t l = strlen(s);
+
+ for (sd = dens; sd->dens; sd++)
+ if (strncasecmp(sd->name, s, l) == 0)
+ break;
+ return sd->dens;
+}
+
+
+const char *
+getblksiz(int bs)
+{
+ static char buf[25];
+ if (bs == 0)
+ return "variable";
+ else {
+ sprintf(buf, "= %d bytes", bs);
+ return buf;
+ }
+}
+
+
+void
+st_status(struct mtget *bp)
+{
+ printf("Present Mode: Density = %-12s Blocksize %s\n",
+ denstostring(bp->mt_density), getblksiz(bp->mt_blksiz));
+ printf("---------available modes---------\n");
+ printf("Mode 0: Density = %-12s Blocksize %s\n",
+ denstostring(bp->mt_density0), getblksiz(bp->mt_blksiz0));
+ printf("Mode 1: Density = %-12s Blocksize %s\n",
+ denstostring(bp->mt_density1), getblksiz(bp->mt_blksiz1));
+ printf("Mode 2: Density = %-12s Blocksize %s\n",
+ denstostring(bp->mt_density2), getblksiz(bp->mt_blksiz2));
+ printf("Mode 3: Density = %-12s Blocksize %s\n",
+ denstostring(bp->mt_density3), getblksiz(bp->mt_blksiz3));
+}
+
+void
+warn_eof(void)
+{
+ fprintf(stderr,
+ "The \"eof\" command has been disabled.\n"
+ "Use \"weof\" if you really want to write end-of-file marks,\n"
+ "or \"eom\" if you rather want to skip to the end of "
+ "recorded medium.\n");
+ exit(1);
+}
+
+#endif /* defined (__FreeBSD__) */
diff --git a/usr.bin/ncftp/.cvsimport.sh b/usr.bin/ncftp/.cvsimport.sh
new file mode 100644
index 0000000..b053415
--- /dev/null
+++ b/usr.bin/ncftp/.cvsimport.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+tar zxvf ncftp-1.9.4.tgz
+cd ncftp-1.9.4
+rm Makefile
+cvs import src/usr.bin/ncftp mgleason ncftp_1_9_4
diff --git a/usr.bin/ncftp/Blurb b/usr.bin/ncftp/Blurb
new file mode 100644
index 0000000..e9cd04c
--- /dev/null
+++ b/usr.bin/ncftp/Blurb
@@ -0,0 +1,66 @@
+Subject: NcFTP 1.6.0 - Alternative User Interface for FTP
+
+Archive-name: ncftp/part01
+Environment: UNIX, ANSI-C, !SVR4
+Supersedes: ncftp: Volume 39, Issue 53-57
+
+NcFTP - Alternative user interface for FTP
+Version 1.6.0 by Mike Gleason, NCEMRSoft.
+
+I used to list the features of ncftp in this blurb, but there are just
+too many to list. Even if you only ftp occasionally, it is worth your
+time to install ncftp (or atleast bug your sysadmin to). If you won't take
+my word for it, just ask around, or extract this archive and read the
+man page.
+
+1.6.0 is an "evolutionary" upgrade, which consolidates all the previous
+patches and adds a little. 1.6 and 1.5 are "interim" versions between
+2.0 which has been suspended indefinitely due to time constraints.
+
+Major changes since 1.5.6:
+
+* Built-in support for the "term" package, used by Linux, etc.
+
+* SCO Xenix, Besta, AIX 2, AIX 3, Dynix/PTX support
+
+* Better ISC Unix support.
+
+* Several bug fixes.
+
+
+Major changes since 1.0.2:
+
+* Supports the Getline (input-edit) and GNU Readline command-line
+ editing and history libraries.
+
+* Supports the Socks firewall library, and another firewall gateway
+ implementation.
+
+* Terrific new "recent-sites" file that automatically saves the
+ sites you call; when you open a site in the recent-sites file
+ (of course you can abbreviate the names), you start in the
+ same directory you were in last time.
+
+* Improved on-line help, and tips on how to use the program better
+ are printed each time you run the program.
+
+* Rewritten man page.
+
+* Faster ascii transfers.
+
+* Typing 'open' by itself lists all the sites the program knows
+ about (the ones in your .netrc and the recent-sites list) so
+ you can just pick one.
+
+* Enhanced colon-mode, that can dump a file to stdout (ftpcat mode)
+ or to a pager. (i.e. ncftp -c wu:/README >/dev/null).
+
+* You can choose whether an open is anonymous by default (like it
+ had always been) or a user login by default by setting a new
+ program variable.
+
+* Bugs fixed.
+
+Read the enclosed file, v2_Notes, which explains why I won't be
+able to work on the nearly finished, and much improved v2.0.
+
diff --git a/usr.bin/ncftp/Makefile b/usr.bin/ncftp/Makefile
new file mode 100644
index 0000000..5d13f04
--- /dev/null
+++ b/usr.bin/ncftp/Makefile
@@ -0,0 +1,17 @@
+PROG= ncftp
+
+SRCS= cmds.c cmdtab.c ftp.c ftprc.c getpass.c glob.c main.c open.c set.c \
+tips.c util.c
+
+DPADD= $(LIBREADLINE) $(LIBTERMCAP)
+LDADD= -lreadline -ltermcap
+
+CFLAGS+= -DGZCAT=\"/usr/bin/gzcat\" -DREADLINE -DCURSES -DNO_CURSES_H \
+ -DSYSLOG -DTRY_ABOR -DGATEWAY
+
+MK= $(CC) $(CFLAGS) $(LDADD)
+
+cmds.o:
+ $(CC) $(CFLAGS) -DMK='"$(MK)"' -c ${.CURDIR}/cmds.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ncftp/Makefile.ORIG b/usr.bin/ncftp/Makefile.ORIG
new file mode 100644
index 0000000..7fe52b8
--- /dev/null
+++ b/usr.bin/ncftp/Makefile.ORIG
@@ -0,0 +1,287 @@
+# Makefile for ncftp
+#
+# Major sections delimited by a dash lines. If several lines set the same
+# make variable, you can choose between the commented #samples, or just
+# type what you want manually.
+#--------------------------------------------------------------------------
+
+
+# System dependent definitions. See the README, part B.
+#--------------------------------------------------------------------------
+SDEFS =
+
+
+# Program definitions. See the README, part C.
+#--------------------------------------------------------------------------
+PDEFS =
+#PDEFS = -DGETLINE
+#PDEFS = -DREADLINE -DCURSES
+#PDEFS = -DSOCKS
+#PDEFS = -DPASSIVEMODE
+#PDEFS = -DDEBUG -DDB_ERRS
+
+
+# Choose your compiler and flags below. Make sure you use an ANSI compiler
+# that handles new style function declarations and prototypes (gcc should).
+#--------------------------------------------------------------------------
+CC = cc
+#CC = gcc
+
+#CFLAGS = $(TERM_INC) -O
+CFLAGS = $(TERM_INC) -O2
+#CFLAGS = $(TERM_INC) -g
+
+LFLAGS = -s
+#LFLAGS =
+
+
+# Additional libraries and/or object files.
+#
+# For each library, add -lLIBNAME to the LIBS line below, for a library
+# named libLIBNAME.a.
+#
+# For each object file, just add the pathname of the object file.
+#
+# Some may need any of -lsocket, -lnet, -linet, -lintl, or -lnsl.
+# You'll need -lcurses or -ltermcap if CURSES is defined.
+# You'll need -lreadline AND either -lcurses or -ltermcap if you
+# want to use the GNU Readline library.
+# You'll need -lgetline (compile it as a library) if you want to use
+# getline.
+# If your system is running Yellow Pages, you'll need to add the library
+# that has the YP/NIS version of getpwuid() in it (Important!)
+# You'll need to know where the Rconnect.o object file is if you want
+# to use Socks.
+#--------------------------------------------------------------------------
+LIBS =
+#LIBS = -ldbmalloc
+#LIBS = -lgetline
+#LIBS = -lreadline -lcurses
+#LIBS = ../lib/Rconnect.o
+#LIBS = -lnet -lnsl -lsocket -lcurses
+#LIBS = -lcurses -ltermcap
+
+# If the libraries are in a non-standard directory, or you if want to use
+# getline or readline and they aren't installed system-wide, add the
+# extra directories to look in here, using -L's.
+#--------------------------------------------------------------------------
+LIBDIRS =
+#LIBDIRS = -L../getline
+#LIBDIRS = -L../readline
+
+# To make term sources define this to your term directory
+TERM_INC =
+TERM_LIB =
+#TERM_INC = -include /usr/local/include/termnet.h
+#TERM_LIB = -ltermnet
+
+# Additional headers.
+#
+# If you defined READLINE or GETLINE, you have to tell where it's header
+# file can be found.
+#
+# For READLINE, provide a path which would find <readline/readline.h>,
+# so you would put the parent directory of the readline directory below.
+# If you had '/usr/local/readline/readline.h' you would use
+# -I/usr/local.
+#
+# For GETLINE, a little different. Just supply a path that would find
+# <getline.h>. If you had '/usr/local/getline/getline.h' you would use
+# -I/usr/local/getline.
+#--------------------------------------------------------------------------
+HDRDIRS =
+#HDRDIRS = -I../getline
+#HDRDIRS = -I..
+
+
+# If you want to 'make install,' edit these variables, otherwise don't
+# worry about it.
+# To install MAN style pages, set MANDIR to the proper location.
+# To install CATMAN style pages, set CATMANDIR, NROFF, and PACK to the proper
+# locations.
+# To inhibit the installation of either, unset MANDIR/CATMANDIR.
+#--------------------------------------------------------------------------
+#BINDIR = /usr/lbin
+BINDIR = /usr/local/bin
+MANDIR = /usr/man/man1
+#MANDIR =
+#CATMANDIR = /usr/catman/LOCAL/g1
+CATMANDIR =
+NROFF = /usr/ucb/nroff
+PACK = pack
+TEST = test
+RM = rm -f
+CP = cp
+CAT = cat
+
+
+#************************************************
+#*** SHOULD NOT NEED TO EDIT BELOW THIS POINT ***
+#************************************************
+
+DEFS = $(PDEFS) $(SDEFS)
+MK = $(CC) $(CFLAGS) $(DEFS) $(HDRDIRS) $(LFLAGS) $(LIBDIRS) $(LIBS)
+
+SRCS = cmds.c cmdtab.c ftp.c ftprc.c getpass.c glob.c main.c open.c set.c \
+tips.c util.c
+
+HEADERS = cmds.h copyright.h defaults.h ftp.h ftprc.h getpass.h glob.h \
+main.h open.h set.h sys.h util.h
+
+OBJS = cmds.o cmdtab.o ftp.o ftprc.o getpass.o glob.o main.o open.o set.o \
+tips.o util.o
+
+NAME = ncftp
+MAN = ncftp.1
+CATMAN = ncftp.z
+ALL = $(SRCS) $(HEADERS) patchlevel.h Blurb README Makefile $(MAN) \
+v2_Note
+
+C_COMPILE = $(CC) $(CFLAGS) $(DEFS) $(HDRDIRS)
+C_COMPILE2 = $(CC) $(CFLAGS) $(DEFS) -DMK='"$(MK)"' $(HDRDIRS)
+
+all: $(NAME) done
+
+$(NAME): $(OBJS)
+ $(CC) $(LFLAGS) $(LIBDIRS) $(OBJS) -o $(NAME) $(LIBS) $(TERM_LIB)
+
+install: $(NAME)
+ if $(TEST) -f $(BINDIR)/term ; then \
+ $(CP) $(BINDIR)/term $(BINDIR)/$(NAME) ; \
+ $(CAT) $(NAME) > $(BINDIR)/$(NAME) ; \
+ else \
+ $(CP) $(NAME) $(BINDIR)/$(NAME) ; \
+ fi
+ @if $(TEST) -n '$(MANDIR)'; then \
+ $(MAKE) install_man ; else true ; fi
+ @if $(TEST) -n '$(CATMANDIR)'; then \
+ $(MAKE) install_catman ; else true ; fi
+
+install_man: $(MAN)
+ $(CP) $(MAN) $(MANDIR)/$(MAN)
+
+
+install_catman: $(CATMAN)
+ $(CP) $(CATMAN) $(CATMANDIR)/$(CATMAN)
+
+uninstall:
+ $(RM) $(BINDIR)/$(NAME)
+ $(RM) $(MANDIR)/$(MAN)
+ $(RM) $(CATMANDIR)/$(CATMAN)
+
+$(CATMAN): $(MAN)
+ $(RM) tmp
+ $(NROFF) -man -Tlp $(MAN) > tmp
+ $(PACK) -f tmp
+ mv tmp.z $(CATMAN)
+
+cmds.o:
+ $(C_COMPILE2) cmds.c -c
+
+.c.o:
+ $(C_COMPILE) -c $<
+
+done: $(NAME)
+ -@ls -l $(NAME)
+ -@echo 'Done.'
+
+clean:
+ rm -f $(OBJS) $(NAME)
+
+# Dependencies:
+cmds.o: cmds.c
+cmds.o: sys.h
+cmds.o: util.h
+cmds.o: cmds.h
+cmds.o: main.h
+cmds.o: ftp.h
+cmds.o: ftprc.h
+cmds.o: getpass.h
+cmds.o: glob.h
+cmds.o: open.h
+cmds.o: set.h
+cmds.o: defaults.h
+cmds.o: copyright.h
+cmdtab.o: cmdtab.c
+cmdtab.o: sys.h
+cmdtab.o: util.h
+cmdtab.o: cmds.h
+cmdtab.o: main.h
+cmdtab.o: ftp.h
+cmdtab.o: ftprc.h
+cmdtab.o: glob.h
+cmdtab.o: open.h
+cmdtab.o: set.h
+cmdtab.o: copyright.h
+ftp.o: ftp.c
+ftp.o: sys.h
+ftp.o: util.h
+ftp.o: ftp.h
+ftp.o: cmds.h
+ftp.o: main.h
+ftp.o: ftprc.h
+ftp.o: getpass.h
+ftp.o: defaults.h
+ftp.o: copyright.h
+ftprc.o: ftprc.c
+ftprc.o: sys.h
+ftprc.o: util.h
+ftprc.o: ftprc.h
+ftprc.o: main.h
+ftprc.o: cmds.h
+ftprc.o: set.h
+ftprc.o: defaults.h
+ftprc.o: copyright.h
+getpass.o: getpass.c
+getpass.o: sys.h
+getpass.o: util.h
+getpass.o: cmds.h
+getpass.o: getpass.h
+getpass.o: copyright.h
+glob.o: glob.c
+glob.o: sys.h
+glob.o: util.h
+glob.o: glob.h
+glob.o: cmds.h
+glob.o: copyright.h
+main.o: main.c
+main.o: sys.h
+main.o: util.h
+main.o: cmds.h
+main.o: main.h
+main.o: ftp.h
+main.o: ftprc.h
+main.o: open.h
+main.o: set.h
+main.o: defaults.h
+main.o: copyright.h
+open.o: open.c
+open.o: sys.h
+open.o: util.h
+open.o: open.h
+open.o: cmds.h
+open.o: ftp.h
+open.o: ftprc.h
+open.o: main.h
+open.o: defaults.h
+open.o: copyright.h
+set.o: set.c
+set.o: sys.h
+set.o: util.h
+set.o: cmds.h
+set.o: main.h
+set.o: set.h
+set.o: defaults.h
+set.o: copyright.h
+tips.o: tips.c
+tips.o: sys.h
+tips.o: util.h
+util.o: util.c
+util.o: sys.h
+util.o: util.h
+util.o: cmds.h
+util.o: main.h
+util.o: ftp.h
+util.o: ftprc.h
+util.o: defaults.h
+util.o: copyright.h
diff --git a/usr.bin/ncftp/README b/usr.bin/ncftp/README
new file mode 100644
index 0000000..67a6610
--- /dev/null
+++ b/usr.bin/ncftp/README
@@ -0,0 +1,296 @@
+Note: This version is no longer being enhanced. As of this writing, version
+2.0 is the officially supported version. Only bug fixes and portability
+tweaks will be applied to the 1.9 series.
+
+If you are a novice user, and don't know how to compile things, try
+contacting your local guru first (get them to do it for you :-). Please
+understand that I don't have time to walk newbies through the whole
+installation procedure.
+
+1. READ this entire file. Part A, below, tells what to do if you want to
+ use NcFTP with a command-line editor. Part B tells you how to configure
+ the Makefile to compile the program for your system. Part C tells you
+ how to configure NcFTP's optional features. Part D tells you how to
+ contact me if you want to report a bug or submit new code to the
+ program.
+
+2. EDIT the Makefile, making any necessary changes described in parts
+ A, B, or C. Don't forget to read the directions in the Makefile,
+ so you don't forget any needed libraries, etcetera.
+
+3. You can also change the program's default behavior by editing defaults.h.
+ 99% of the time you don't need to do this, so you can skip this step.
+
+If you have problems, you can mail me, but please try your best to install
+it without my help. I'm quite tired of responding to lazy SunOS users
+because they didn't bother reading the directions so that they would have
+known that they needed to use GCC.
+
+I _do_ want to hear from you if you have comments or bug reports/fixes. I
+would also like to hear from you if you had a system that wasn't covered
+in sys.h, so I can add an entry for other users of your system.
+
+The latest version of ncftp is available in the directory:
+ ftp.cs.unl.edu:/pub/ncftp
+
+
+Part A. Installing with a command line editor:
+----------------------------------------------
+
+As of this release, GNU Readline and Chris Thewalt's Getline command-line
+editing and history facilities are supported. Neither are included with the
+ncftp sources. You can find Getline at:
+ ce.berkeley.edu:/pub/thewalt/getline.tar.Z (note: use 'ls', not 'dir!')
+and Readline is in the directory:
+ prep.ai.mit.edu:/pub/gnu
+
+To install Readline, you will need to know where libreadline.a and the
+header <readline/readline.h> are. You will need to link libreadline.a and
+libcurses.a (or libtermcap.a) with ncftp (see the Makefile). Good luck on
+trying to compile it. It is not an easy thing to do! In the Makefile, you
+will need to add -DREADLINE to PDEFS, add -lreadline -lcurses to LIBS, and
+edit the HDRDIRS and LIBDIRS lines. This stuff is already in the Makefile,
+so you can just uncomment it.
+
+To install Getline, you need to know where libgetline.a and it's header
+(getline.h) are. In the Makefile, you'll need to add -lgetline to LIBS and
+edit the HDRDIRS and LIBDIRS lines. This stuff is already in the Makefile,
+so you can just uncomment it.
+
+DO NOT bug me if you can't figure out how to compile Getline or Readline.
+Contact their respective authors instead. It is not essential that you use
+them.
+
+
+Part B. System Dependencies:
+----------------------------
+
+NcFTP may need work-arounds for some things due to the differences
+in implementations of unix. The following systems are taken care
+of automatically. For these systems, you should just be able to type
+'make' (but proceed to part C):
+
+ Silicon Graphics IRIX
+ IBM's AIX
+ SINIX
+ DEC's Ultrix (well, might need to use -lcursesX instead of -lcurses)
+ NeXT
+ Pyramid OSx
+ Berkley Software Design, Inc.'s BSDi
+
+Otherwise you will have to configure ncftp manually.
+
+Important for "Yellow Pages" users: Don't forget to link the library
+that includes the YP/NIS version of getpwuid(), etc. Otherwise the program
+won't be able to expand ~username/path/name type pathnames, and maybe even
+~/path/name types of pathnames. If you're wondering why the program isn't
+opening your rc file, this could be the cause.
+
+You will need to add these things to the SDEFS line in the Makefile
+as applicable. As an example, if I say 'add -DFoobar to SDEFS,' find
+the line in the Makefile that reads 'SDEFS=' (or 'SDEFS=-DFoo2') and
+change it to 'SDEFS=-DFoobar' (or 'SDEFS=-DFoo2 -DFoobar). If your
+system is listed below, follow the directions and then you ready to
+go to part C, below.
+
+ Sun Microsystems' SunOS/Solaris: Use an ANSI compiler such as
+ gcc (set CC=gcc in the Makefile), or acc (set CC=acc).
+ The regular 'cc' is not an ANSI compiler. You could also run
+ something like 'ansi2knr' on the sources and hope it works.
+ You will probably need to link both the curses and termcap
+ libraries if you use -DCURSES (see below). If you're running
+ Solaris (SunOS 5.x or greater) add -DSolaris to SDEFS.
+ I also needed to add -lnsl -lsocket to LIBS.
+
+ Hewlett-Packard HP-UX: If you have 7.0, you'll need to find
+ a copy of <ftp.h> from somewhere (8.0 has it though). Then
+ set CFLAGS= -Aa. You may also need to use gcc if your
+ compiler is non-ANSI. Note that for HP-UX, the default
+ terminal escape codes are for HP terminals, so you should
+ probably link termcap/curses in so it will get the ANSI
+ sequences if you're on a vt100, etc., terminal connected
+ to your HP-UX machine.
+
+ Linux: For 'term' support, from what I can tell just add
+ the path of 'client.a' to LIBS, and add -DTERM_FTP to SDEFS,
+ to turn on the term specific ftp code. May need to link
+ -lcurses and -ltermcap.
+
+ SCO Unix: Add -DSCO324 or -DSCO322 (as appropriate) to SDEFS,
+ and -lsocket to LIBS.
+
+ SCO Xenix 2.3.4: Add -DSCOXNX to SDEFS;
+ Try adding -DLINGER if puts don't work.
+ Add "-lsocket -ldir" to LIBS.
+
+ Bull DPX/2: Add -DBULL to SDEFS, add -linet to LIBS, and
+ use gcc.
+
+ Sequent's DYNIX: Use gcc and add -DDYNIX (if necessary) to SDEFS.
+ You may also be short several string functions which you will
+ have to get elsewhere, and perhaps mktime and strftime.
+ You can get all that stuff from the BSD sources (like ftp.uu.net).
+ Please bug Sequent to update their libc library!
+
+ Sequent's Dynix/PTX: Add -DDYNIXPTX to SDEFS.
+ Add -lsocket -linet -lnsl -lseq to LIBS.
+
+ DEC OSF1/1.3: Use gcc, Add -DGETCWDSIZET to SDEFS. cc might work,
+ though. Try cc if gcc chokes.
+
+If your system doesn't fit any of those, things will be trickier. Answer
+all these questions and add to the SDEFS line. You may want to try
+each option one at a time until everything works.
+
+* Is your system closer to System V or BSD? Your SDEFS line should have
+either -DBSD or -DSYSV. If you don't know, try leaving it blank first;
+some compilers automatically define it for you.
+
+* Add -DNO_CONST if your compiler chokes on the const directive. You
+will know if you need to add this if the compiler spits out errors saying
+it doesn't know what 'const' is.
+
+* As I said above, you will need to link special libraries if your system
+is running Yellow Pages.
+
+* Add -DSYSSELECTH if you need <sys/select.h> included for definitions
+of fd_set, etc.
+
+* Add -DNO_UNISTDH if you don't have <unistd.h>. If the compiler complains
+about not being able to open <unistd.h> add this.
+
+* Add -DNO_STDLIBH if you don't have <stdlib.h>. If the compiler complains
+about not being able to open <stdlib.h> add this.
+
+* Add -DNO_UTIMEH if you don't have <utime.h>. If the compiler complains
+about not being able to open <utime.h> add this.
+
+* Add -DNO_MKTIME if you don't have the mktime() system call, and don't
+feel like getting the source for it and compiling it in with the program.
+If you define this, the program will not set the file modification times
+to match the ones on the remote host (no big deal).
+
+* Add -DGETPASS if you would rather use the standard getpass() system
+call, instead of our version, Getpass(), which takes more than 8
+characters. You may want to define this if you are having problems
+compiling getpass.c.
+
+If you haven't given up on our Getpass(), you can try adding -DSGTTYB
+if you want to use a struct sgttyb instead of a struct termio. By default,
+BSD systems define SGTTYB automatically. You can also try adding -DTERMIOS
+to use a POSIX compliant struct termios instead. Don't pull your hair out
+trying to get the Getpass code to compile; if it gives you problems just
+define -DGETPASS and hope your system's getpass can handle passwords
+longer than 8 characters.
+
+* Add -DBAD_INETADDR if your inet_addr() function returns a struct in_addr
+instead of a u_long, as it should (in DG/UX 5.4.1).
+
+* Add -DBROKEN_MEMCPY if ncftp mysteriously dumps core when trying to open
+a remote host. I'm told that this happens because of some problem in System
+V's sockets don't like fprintf (and memcpy).
+
+* Add -DPTRTYPE=char if your pre-ANSI compiler complains about the
+way malloc() or free() are used, and in general does not like (void *)
+as a generic pointer type.
+
+* Add -DNO_STRFTIME if your system does not have strftime(). If you do,
+we won't try to use it. This means, however, you cannot use ``%'' values
+in your prompt.
+
+* Add -DNO_RENAME if your system does not have rename() (or the one it
+has is broken). If you do, we will use our own.
+
+* Add -DNO_STRSTR if your system does not have strstr(). If you do, we
+will use our own.
+
+* Add -DLINGER if puts to the remote system are incomplete.
+
+* Add -DNET_ERRNO_H if you need to include <net/errno.h> for definitions
+ of ECONNREFUSED, etcetera.
+
+* (Optional) Add -DGETCWDSIZET if your system's getcwd() takes a size_t
+as the second parameter instead of an int.
+
+* (Optional) Add -DHERROR if you know you have the herror() system
+call.
+
+* (Optional) Add -DU_WAIT if you know your wait system call takes
+a pointer to a 'union wait.' Defined automatically if you define
+BSD.
+
+* (Optional) Add -DHOSTNAME=\"machine.domain.nam\" if your system
+doesn't generate it's own hostname. To check this, compile ncftp
+then run it and type 'set.' Look at the variable anon-password.
+If the hostname is wrong, or if it is in the form of 'yourlogin' or
+'yourlogin@machine' instead of 'yourlogin@machine.xxx.yyy,'
+re-compile it with HOSTNAME set to your machine's address, in the
+form of 'machine.xxx.yyy.'
+
+* (Optional) Add -DHAS_DOMAINNAME if you have the getdomainname()
+ function.
+
+* (Optional) If you're having problems with your hostname not being
+full (i.e you have 'yourlogin@machine') all is not lost. First of all,
+define HAS_DOMAINNAME if you can. But sometimes getdomainname() doesn't
+work -- instead of giving you the domain name, it returns an empty
+string. So you can hardcode the domain name by defining DOMAIN_NAME to
+be the domain (i.e. add -DDOMAIN_NAME=\"domain.nam\"). That way, if
+getdomainname doesn't work, the program will have something to fall back
+on. This problem is common on SunOS/Solaris.
+
+* (Optional) Add -DSTRICT_PROTOS if your compiler wants function prototypes
+for all functions, not just non-int-returning ones. This is really just
+handy for debugging during development, so this is not recommended.
+
+
+Part C. Program Options:
+------------------------
+
+Add these as applicable to the PDEFS line in the Makefile.
+
+* -DGZCAT=\"path\": If you have the GNU gzip package installed on your system,
+ the program can try paging remote files compressed with gzip _and_
+ compress (instead of just compress). Add -DGZCAT=\"/full/path/to/zcat\"
+ with GZCAT set to the path name of GNU's zcat/gzcat.
+
+* -DCURSES: Uses curses library to display boldface, underline, etc.
+ By default ncftp uses hard-coded ANSI escapes (^[[1m etc.) to
+ save the 100k or so the curses library adds. You will also need
+ to edit the LIBS line in the Makefile to add -lcurses. You may
+ need to add -ltermcap instead, or both -lcurses and -ltermcap.
+ If you choose to use the termcap library, you may want to also add
+ -DNO_CURSES_H so it does not try to include <curses.h>.
+
+* -DSYSLOG: Define this to have ncftp log connections and transfers
+ to the syslog.
+
+* -DNO_TIPS: Define if you want to cut a little fat at the expense of
+ novice users.
+
+* -DGETLINE: If you want to use Chris Thewalt's getline input line editor
+ and history facility, add this (and see below).
+
+* -DREADLINE: If you want to use GNU's readline input line editor and
+ history facility, add this (and see the Makefile). If you do this, you
+ also need to add -DCURSES (see above).
+
+* -DSOCKS: NcFTP is now compatible with the Socks library by David Koblas,
+ at koblas@sgi.com. This lets you use NcFTP with a "firewall" gateway
+ for enhanced site security. You can get the latest version from...
+ ftp://ftp.netcom.com/pub/ko/koblas/
+
+ After you have compiled it, compile NcFTP
+ with -DSOCKS added to PDEFS, and the pathname of the Rconnect.o file
+ added to LIBS.
+
+* -DTRY_ABOR: Define if you want to try the 'ABOR' command from ncftp;
+ The aborting code has had some problems, so by default the program
+ 'aborts' by continuing to read input but not echoing output.
+
+* -DDB_ERRS: Define this if you want my Perror() function to be more
+ verbose. You may want to do this if you are a programmer examining this
+ code, and want to know where in the source the Perror's are coming
+ from.
+
+--mg (mgleason@cse.unl.edu)
diff --git a/usr.bin/ncftp/cmds.c b/usr.bin/ncftp/cmds.c
new file mode 100644
index 0000000..2b6972b
--- /dev/null
+++ b/usr.bin/ncftp/cmds.c
@@ -0,0 +1,2223 @@
+/* cmds.c */
+
+/* $RCSfile: cmds.c,v $
+ * $Revision: 1.1.1.1 $
+ * $Date: 1994/09/22 23:45:33 $
+ */
+
+#include "sys.h"
+
+#include <sys/wait.h>
+
+#include <sys/stat.h>
+#include <arpa/ftp.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#ifdef SYSLOG
+# include <syslog.h>
+#endif
+
+#include "util.h"
+#include "cmds.h"
+#include "main.h"
+#include "ftp.h"
+#include "ftprc.h"
+#include "getpass.h"
+#include "glob.h"
+#include "open.h"
+#include "set.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* cmds.c globals */
+int curtype; /* file transfer type */
+char *typeabbrs = "abiet";
+str32 curtypename; /* name of file transfer type */
+int verbose; /* verbosity level of output */
+int mprompt; /* interactively prompt on m* cmds */
+int passivemode; /* no reverse FTP connections */
+int debug; /* debugging level */
+int options; /* used during socket creation */
+int macnum; /* number of defined macros */
+int paging = 0;
+int creating = 0;
+struct macel macros[MAXMACROS];
+char *macbuf; /* holds ALL macros */
+int doingInitMacro = 0; /* TRUE if executing "init" macro. */
+static char pad1a[8] = "Pad 1a";
+jmp_buf jabort;
+static char pad1b[8] = "Pad 1b";
+char *mname; /* name of current m* command */
+int activemcmd; /* flag: if != 0, then active multi command */
+int warnNoLSFlagsWithWildcards = 0;
+ /* Tells whether the user has been
+ * warned about not being able to use
+ * flags with ls when using wildcards.
+ */
+longstring cwd; /* current remote directory */
+longstring lcwd; /* current local directory */
+Hostname lasthostname; /* name of last host w/ lookup(). */
+int logged_in = 0; /* TRUE if connected and user/pw OK. */
+int is_ls = 0; /* are we doing an ls? if so, then
+ read input into a line buffer
+ for re-use. */
+extern int buffer_only;
+struct lslist *lshead = NULL; /* hold last output from host */
+struct lslist *lstail = NULL;
+
+/* cmds.c externs */
+extern char *globerr, *home, *reply_string;
+extern int margc, connected, ansi_escapes;
+extern int code, connected;
+extern int toatty, fromatty;
+extern int data, progress_meter, remote_is_unix;
+extern int parsing_rc, keep_recent;
+extern char *altarg, *line, *margv[];
+extern char *globchars;
+extern Hostname hostname;
+extern RemoteSiteInfo gRmtInfo;
+extern string progname, pager, anon_password;
+extern string prompt, version, indataline;
+extern longstring logfname;
+extern long logsize;
+extern size_t xferbufsize;
+extern struct servent serv;
+extern struct cmd cmdtab[];
+extern struct userinfo uinfo;
+extern FILE *cin, *cout, *logf;
+extern int Optind;
+extern char *Optarg;
+extern int Optind;
+extern char *Optarg;
+
+#ifdef STRICT_PROTOS
+extern int gethostname(char *, int), getdomainname(char *, int);
+#endif
+
+
+struct types types[] = {
+ { "ascii", "A", TYPE_A, 0 },
+ { "binary", "I", TYPE_I, 0 },
+ { "image", "I", TYPE_I, 0 },
+ { "ebcdic", "E", TYPE_E, 0 },
+ { "tenex", "L", TYPE_L, "8" },
+ { 0 }
+};
+
+
+
+long GetDateSizeFromLSLine(char *fName, unsigned long *mod_time)
+{
+ char *cp, *np;
+ string lsline;
+ long size = SIZE_UNKNOWN;
+ int n, v;
+ struct lslist *savedh, *savedt;
+ static int depth = 0;
+
+ depth++; /* Try to prevent infinite recursion. */
+ *mod_time = MDTM_UNKNOWN;
+ v = verbose; verbose = V_QUIET;
+ is_ls = 1;
+ buffer_only = 1;
+ savedh = lshead;
+ savedt = lstail;
+ lshead = NULL;
+ (void) recvrequest("LIST", "-", fName, "w");
+ is_ls = 0;
+ buffer_only = 0;
+ verbose = v;
+ if (lshead == NULL) {
+ PurgeLineBuffer();
+ lshead = savedh;
+ lstail = savedt;
+ goto aa;
+ }
+ (void) Strncpy(lsline, lshead->string);
+ PurgeLineBuffer();
+ lshead = savedh;
+ lstail = savedt;
+
+ if (code >= 400 && code < 500)
+ goto aa;
+
+ /* See if this line looks like a unix-style ls line.
+ * If so, we can grab the date and size from it.
+ */
+ if (strpbrk(lsline, "-dlsbcp") == lsline) {
+ /* See if it looks like a typical '-rwxrwxrwx' line. */
+ cp = lsline + 1;
+ if (*cp != 'r' && *cp != '-')
+ goto aa;
+ ++cp;
+ if (*cp != 'w' && *cp != '-')
+ goto aa;
+ cp += 2;
+ if (*cp != 'r' && *cp != '-')
+ goto aa;
+
+ /* skip mode, links, owner (and possibly group) */
+ for (n = 0; n < 4; n++) {
+ np = cp;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ while (*cp != '\0' && isspace(*cp))
+ cp++;
+ }
+ if (!isdigit(*cp))
+ cp = np; /* back up (no group) */
+ (void) sscanf(cp, "%ld%n", &size, &n);
+
+ *mod_time = UnLSDate(cp + n + 1);
+
+ if (size < 100) {
+ /* May be the size of a link to the file, instead of the file. */
+ if ((cp = strstr(lsline, " -> ")) != NULL) {
+ /* Yes, it was a link. */
+ size = (depth>4) ? SIZE_UNKNOWN :
+ GetDateAndSize(cp + 4, mod_time);
+ /* Try the file. */
+ }
+ }
+ }
+aa:
+ --depth;
+ return (size);
+} /* GetDateSizeFromLSLine */
+
+
+
+
+/* The caller wanted to know the modification date and size of the remote
+ * file given to us. We try to get this information by using the SIZE
+ * and MDTM ftp commands, and if that didn't work we try sending the site
+ * a "ls -l <fName>" and try to get that information from the line it
+ * sends us back. It is possible that we won't be able to determine
+ * either of these, though.
+ */
+long GetDateAndSize(char *fName, unsigned long *mod_time)
+{
+ unsigned long mdtm, ls_mdtm;
+ long size, ls_size;
+ int have_mdtm, have_size;
+ string cmd;
+
+ size = SIZE_UNKNOWN;
+ mdtm = MDTM_UNKNOWN;
+ if (fName != NULL) {
+ have_mdtm = have_size = 0;
+ if (gRmtInfo.hasSIZE) {
+ (void) Strncpy(cmd, "SIZE ");
+ (void) Strncat(cmd, fName);
+ if (quiet_command(cmd) == 2) {
+ if (sscanf(reply_string, "%*d %ld", &size) == 1)
+ have_size = 1;
+ } else if (strncmp(reply_string, "550", (size_t)3) != 0)
+ gRmtInfo.hasSIZE = 0;
+ }
+
+#ifndef NO_MKTIME
+ /* We'll need mktime() to un-mangle this. */
+ if (gRmtInfo.hasMDTM) {
+ (void) Strncpy(cmd, "MDTM ");
+ (void) Strncat(cmd, fName);
+ if (quiet_command(cmd) == 2) {
+ /* Result should look like "213 19930602204445\n" */
+ mdtm = UnMDTMDate(reply_string);
+ if (mdtm != MDTM_UNKNOWN)
+ have_mdtm = 1;
+ } else if (strncmp(reply_string, "550", (size_t)3) != 0)
+ gRmtInfo.hasMDTM = 0;
+ }
+#endif /* NO_MKTIME */
+
+ if (!have_mdtm || !have_size)
+ ls_size = GetDateSizeFromLSLine(fName, &ls_mdtm);
+
+ /* Try to use the information from the real SIZE/MDTM commands if
+ * we could, since some maverick ftp server may be using a non-standard
+ * ls command, and we could parse it wrong.
+ */
+
+ if (!have_mdtm)
+ mdtm = ls_mdtm;
+ if (!have_size)
+ size = ls_size;
+
+ dbprintf("Used SIZE: %s; Used MDTM: %s\n",
+ have_size ? "yes" : "no",
+ have_mdtm ? "yes" : "no"
+ );
+
+ if (debug > 0) {
+ if (size != SIZE_UNKNOWN)
+ dbprintf("Size: %ld\n", size);
+ if (mdtm != MDTM_UNKNOWN)
+ dbprintf("Mdtm: %s\n", ctime((time_t *) &mdtm));
+ }
+ }
+ *mod_time = mdtm;
+ return size;
+} /* GetDateAndSize */
+
+
+
+
+
+int _settype(char *typename)
+{
+ register struct types *p;
+ int comret, c;
+ string cmd;
+ char *cp;
+
+ c = isupper(*typename) ? tolower(*typename) : (*typename);
+ if ((cp = index(typeabbrs, c)) != NULL)
+ p = &types[(int) (cp - typeabbrs)];
+ else {
+ (void) printf("%s: unknown type\n", typename);
+ return USAGE;
+ }
+ if (c == 't')
+ (void) strcpy(cmd, "TYPE L 8");
+ else
+ (void) sprintf(cmd, "TYPE %s", p->t_mode);
+ comret = command(cmd);
+ if (comret == COMPLETE) {
+ (void) Strncpy(curtypename, p->t_name);
+ curtype = p->t_type;
+ }
+ return NOERR;
+} /* _settype */
+
+
+
+
+int SetTypeByNumber(int i)
+{
+ char tstr[4], *tp = tstr, c;
+
+ tp[1] = c = 0;
+ switch (i) {
+ case TYPE_A: c = 'a'; break;
+ case TYPE_I: c = 'b'; break;
+ case TYPE_E: c = 'e'; break;
+ case TYPE_L: c = 't';
+ }
+ *tp = c;
+ return (c == 0 ? -1 : _settype(tp));
+} /* SetTypeByNumber */
+
+
+
+
+/*
+ * Set transfer type.
+ */
+int settype(int argc, char **argv)
+{
+ int result = NOERR;
+
+ if (argc > 2) {
+ result = USAGE;
+ } else {
+ if (argc < 2)
+ goto xx;
+ result = _settype(argv[1]);
+ if (IS_VVERBOSE)
+xx: (void) printf("Using %s mode to transfer files.\n", curtypename);
+ }
+ return result;
+} /* settype */
+
+
+
+
+/*ARGSUSED*/
+int setbinary(int argc, char **argv) { return (_settype("binary")); }
+/*ARGSUSED*/
+int setascii(int argc, char **argv) { return (_settype("ascii")); }
+
+
+
+/*
+ * Send a single file.
+ */
+int put(int argc, char **argv)
+{
+ char *cmd;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ }
+ if (argc < 2)
+ argv = re_makeargv("(local-file) ", &argc);
+ if (argc < 2) {
+usage:
+ return USAGE;
+ }
+ if (argc < 3)
+ argv = re_makeargv("(remote-file) ", &argc);
+ if (argc < 3)
+ goto usage;
+ cmd = (argv[0][0] == 'a') ? "APPE" : "STOR";
+ (void) sendrequest(cmd, argv[1], argv[2]);
+ return NOERR;
+} /* put */
+
+
+
+
+/*
+ * Send multiple files.
+ */
+int mput(int argc, char **argv)
+{
+ register int i;
+ Sig_t oldintr;
+ char *tp;
+
+ if (argc < 2)
+ argv = re_makeargv("(local-files) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ mname = argv[0];
+ activemcmd = 1;
+ oldintr = Signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ for (i = 1; i < argc; i++) {
+ register char **cpp, **gargs;
+ char *icopy;
+
+ /* Make a copy of the argument, because glob() will just copy
+ * the pointer you give it to the glob-arg vector, and blkfree()
+ * will want to free each element of the glob-arg vector
+ * later.
+ */
+ if ((icopy = NewString(argv[i])) == NULL)
+ break;
+ gargs = glob(icopy);
+ if (globerr != NULL) {
+ (void) printf("%s\n", globerr);
+ if (gargs) {
+ blkfree(gargs);
+ Free(gargs);
+ }
+ continue;
+ }
+ for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
+ if (activemcmd && confirm(argv[0], *cpp)) {
+ tp = *cpp;
+ (void) sendrequest("STOR", *cpp, tp);
+ if (!activemcmd && fromatty) {
+ if (confirm("Continue with","mput")) {
+ activemcmd++;
+ }
+ }
+ }
+ }
+ if (gargs != NULL) {
+ blkfree(gargs);
+ Free(gargs);
+ }
+ }
+ (void) Signal(SIGINT, oldintr);
+ activemcmd = 0;
+ return NOERR;
+} /* mput */
+
+
+
+
+int rem_glob_one(char *pattern)
+{
+ int oldverbose, result = 0;
+ char *cp;
+ string str, tname;
+ FILE *ftemp;
+
+ /* Check for wildcard characters. */
+ if (*pattern == '|' || strpbrk(pattern, globchars) == NULL)
+ return 0;
+
+ (void) tmp_name(tname);
+ oldverbose = verbose;
+ verbose = V_QUIET;
+ (void) recvrequest ("NLST", tname, pattern, "w");
+ verbose = oldverbose;
+ ftemp = fopen(tname, "r");
+ (void) chmod(tname, 0600);
+ if (ftemp == NULL || FGets(str, ftemp) == NULL) {
+ if (NOT_VQUIET)
+ (void) printf("%s: no match.\n", pattern);
+ result = -1;
+ goto done;
+ }
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+ (void) strcpy(pattern, str);
+ cp = FGets(str, ftemp);
+ /* It is an error if the pattern matched more than one file. */
+ if (cp != NULL) {
+ if (NOT_VQUIET)
+ (void) printf("?Ambiguous remote file name.\n");
+ result = -2;
+ }
+done:
+ if (ftemp != NULL)
+ (void) fclose(ftemp);
+ (void) unlink(tname);
+ return (result);
+} /* rem_glob_one */
+
+
+
+
+/*
+ * Receive (and maybe page) one file.
+ */
+int get(int argc, char **argv)
+{
+ string local_file;
+ char remote_file[256];
+ char *cp;
+ int oldtype = curtype, try_zcat;
+ size_t len;
+
+ /* paging mode is set if the command name is 'page' or 'more.' */
+ paging = (**argv != 'g');
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-file) ", &argc);
+
+ if (argc < 2) {
+ return USAGE;
+ }
+ cp = Strncpy(remote_file, argv[1]);
+ argv[1] = cp;
+ if (rem_glob_one(argv[1]) < 0)
+ return CMDERR;
+
+ if (paging) {
+ try_zcat = 0;
+ len = strlen(remote_file);
+
+ if (len > (size_t) 2) {
+ if (remote_file[len-2] == '.') {
+ /* Check for .Z files. */
+ if (remote_file[len-1] == 'Z')
+ try_zcat = 1;
+#ifdef GZCAT
+ /* Check for .z (gzip) files. */
+ if (remote_file[len-1] == 'z')
+ try_zcat = 1;
+#endif /* GZCAT */
+ }
+ }
+
+#ifdef GZCAT
+ if (len > (size_t) 3) {
+ /* Check for ".gz" (gzip) files. */
+ if (strcmp(remote_file + len - 3, ".gz") == 0)
+ try_zcat = 1;
+ }
+#endif /* GZCAT */
+
+ /* Run compressed remote files through zcat, then the pager.
+ * If GZCAT was defined, we also try paging gzipped files.
+ * Note that ZCAT is defined to be GZCAT if you defined
+ * GZCAT.
+ */
+
+ if (try_zcat) {
+ (void) _settype("b");
+ (void) sprintf(local_file, "|%s ", ZCAT);
+ argv[2] = Strncat(local_file, pager);
+ } else {
+ /* Try to use text mode for paging, so newlines get converted. */
+ (void) _settype("a");
+ argv[2] = pager;
+ }
+ } else {
+ /* normal get */
+ if (argc == 2) {
+ (void) Strncpy(local_file, argv[1]);
+ argv[2] = local_file;
+ } else {
+ if (argc < 3)
+ argv = re_makeargv("(local-file) ", &argc);
+ if (argc < 3)
+ return USAGE;
+ (void) LocalDotPath(argv[2]);
+ }
+ }
+ (void) recvrequest("RETR", argv[2], argv[1], "w");
+ if (paging) {
+ (void) SetTypeByNumber(oldtype); /* Restore it to what it was. */
+ paging = 0;
+ }
+ return NOERR;
+} /* get */
+
+
+
+/*ARGSUSED*/
+void mabort SIG_PARAMS
+{
+ (void) printf("\n");
+ (void) fflush(stdout);
+ if (activemcmd && fromatty) {
+ if (confirm("Continue with", mname)) {
+ longjmp(jabort,0);
+ }
+ }
+ activemcmd = 0;
+ longjmp(jabort,0);
+} /* mabort */
+
+
+
+
+/*
+ * Get multiple files.
+ */
+int mget(int argc, char **argv)
+{
+ char *cp;
+ longstring local;
+ Sig_t oldintr;
+ int errs;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-files) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ mname = argv[0];
+ activemcmd = 1;
+ oldintr = Signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv, &errs)) != NULL) {
+ if (*cp == '\0') {
+ activemcmd = 0;
+ continue;
+ }
+ if (activemcmd && confirm(argv[0], cp)) {
+ (void) Strncpy(local, cp);
+ (void) recvrequest("RETR", local, cp, "w");
+ if (!activemcmd && fromatty) {
+ if (confirm("Continue with","mget")) {
+ activemcmd++;
+ }
+ }
+ }
+ }
+ (void) Signal(SIGINT,oldintr);
+ activemcmd = 0;
+ if (!errs)
+ return NOERR;
+ else
+ return CMDERR;
+} /* mget */
+
+
+
+
+char *remglob(char *argv[], int *errs)
+{
+ static FILE *ftemp = NULL;
+ int oldverbose, i;
+ char *cp, *mode;
+ static string tmpname, str;
+ int result;
+
+ if (!activemcmd) {
+xx:
+ if (ftemp) {
+ (void) fclose(ftemp);
+ ftemp = NULL;
+ (void) unlink(tmpname);
+ }
+ return(NULL);
+ }
+ if (ftemp == NULL) {
+ (void) tmp_name(tmpname);
+ oldverbose = verbose, verbose = V_QUIET;
+ *errs = 0;
+ for (mode = "w", i=1; argv[i] != NULL; i++, mode = "a") {
+ result = recvrequest ("NLST", tmpname, argv[i], mode);
+ if (i == 1)
+ (void) chmod(tmpname, 0600);
+ if (result < 0) {
+ fprintf(stderr, "%s: %s.\n",
+ argv[i],
+ (strpbrk(argv[i], globchars) != NULL) ? "No match" :
+ "No such file"
+ );
+ ++(*errs);
+ }
+ }
+ verbose = oldverbose;
+ if (*errs == (i - 1)) {
+ /* Every pattern was in error, so we can't try anything. */
+ (void) unlink(tmpname); /* Shouldn't be there anyway. */
+ return NULL;
+ }
+ ftemp = fopen(tmpname, "r");
+ if (ftemp == NULL) {
+ PERROR("remglob", tmpname);
+ return (NULL);
+ }
+ }
+ if (FGets(str, ftemp) == NULL)
+ goto xx;
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+ return (str);
+} /* remglob */
+
+
+/*
+ * Turn on/off printing of server echo's, messages, and statistics.
+ */
+int setverbose(int argc, char **argv)
+{
+ if (argc > 1)
+ set_verbose(argv[1], 0);
+ else set_verbose(argv[1], -1);
+ return NOERR;
+} /* setverbose */
+
+
+
+/*
+ * Toggle interactive prompting
+ * during mget, mput, and mdelete.
+ */
+int setprompt(int argc, char **argv)
+{
+ if (argc > 1)
+ mprompt = StrToBool(argv[1]);
+ else mprompt = !mprompt;
+ if (IS_VVERBOSE)
+ (void) printf("Interactive prompting for m* commmands %s.\n", onoff(mprompt));
+ return NOERR;
+} /* setprompt */
+
+
+
+
+void fix_options(void)
+{
+ if (debug)
+ options |= SO_DEBUG;
+ else
+ options &= ~SO_DEBUG;
+} /* fix_options */
+
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+int setdebug(int argc, char **argv)
+{
+ int val;
+
+ if (argc > 1) {
+ val = StrToBool(argv[1]);
+ if (val < 0) {
+ (void) printf("%s: bad debugging value.\n", argv[1]);
+ return USAGE;
+ }
+ } else
+ val = !debug;
+ debug = val;
+ fix_options();
+ if (IS_VVERBOSE)
+ (void) printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+ return NOERR;
+} /* debug */
+
+
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+int cd(int argc, char **argv)
+{
+ if (argc < 2)
+ argv = re_makeargv("(remote-directory) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ (void) _cd(argv[1]);
+ return NOERR;
+} /* cd */
+
+
+
+
+int implicit_cd(char *dir)
+{
+ int i, j = 0;
+
+ if (connected) {
+ i = verbose;
+ /* Special verbosity level that ignores errors and prints other stuff,
+ * so you will get just the unknown command message and not an error
+ * message from cd.
+ */
+ verbose = V_IMPLICITCD;
+ j = _cd(dir);
+ verbose = i;
+ }
+ return j;
+} /* implicit_cd */
+
+
+
+
+int _cd(char *dir)
+{
+ register char *cp;
+ int result = 0;
+ string str;
+
+ if (dir == NULL)
+ goto getrwd;
+ /* Won't work because glob really is a ls, so 'cd pu*' will match
+ * pub/README, pub/file2, etc.
+ * if (result = rem_glob_one(dir) < 0)
+ * return result;
+ */
+ if (strncmp(dir, "CDUP", (size_t) 4) == 0)
+ (void) Strncpy(str, dir);
+ else
+ (void) sprintf(str, "CWD %s", dir);
+ if (command(str) != 5) {
+getrwd:
+ (void) quiet_command("PWD");
+ cp = rindex(reply_string, '\"');
+ if (cp != NULL) {
+ result = 1;
+ *cp = '\0';
+ cp = index(reply_string, '\"');
+ if (cp != NULL)
+ (void) Strncpy(cwd, ++cp);
+ }
+ }
+ dbprintf("Current remote directory is \"%s\"\n", cwd);
+ return (result);
+} /* _cd */
+
+
+
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+int lcd(int argc, char **argv)
+{
+ longstring ldir;
+
+ if (argc < 2)
+ argc++, argv[1] = home;
+ if (argc != 2) {
+ return USAGE;
+ }
+ (void) Strncpy(ldir, argv[1]);
+ if (chdir(LocalDotPath(ldir)) < 0) {
+ PERROR("lcd", ldir);
+ return CMDERR;
+ }
+ (void) get_cwd(lcwd, (int) sizeof(lcwd));
+ if (NOT_VQUIET)
+ (void) printf("Local directory now %s\n", lcwd);
+ return NOERR;
+} /* lcd */
+
+
+
+
+/*
+ * Delete a single file.
+ */
+int do_delete(int argc, char **argv)
+{
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote file to delete) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ if (rem_glob_one(argv[1]) == 0) {
+ (void) sprintf(str, "DELE %s", argv[1]);
+ (void) command(str);
+ }
+ return NOERR;
+} /* do_delete */
+
+
+
+
+/*
+ * Delete multiple files.
+ */
+int mdelete(int argc, char **argv)
+{
+ char *cp;
+ Sig_t oldintr;
+ string str;
+ int errs;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-files) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ mname = argv[0];
+ activemcmd = 1;
+ oldintr = Signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv, &errs)) != NULL) {
+ if (*cp == '\0') {
+ activemcmd = 0;
+ continue;
+ }
+ if (activemcmd && confirm(argv[0], cp)) {
+ (void) sprintf(str, "DELE %s", cp);
+ (void) command(str);
+ if (!activemcmd && fromatty) {
+ if (confirm("Continue with", "mdelete")) {
+ activemcmd++;
+ }
+ }
+ }
+ }
+ (void) Signal(SIGINT, oldintr);
+ activemcmd = 0;
+ if (errs > 0)
+ return CMDERR;
+ return NOERR;
+} /* mdelete */
+
+
+
+
+/*
+ * Rename a remote file.
+ */
+int renamefile(int argc, char **argv)
+{
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(from-name) ", &argc);
+ if (argc < 2) {
+usage:
+ return USAGE;
+ }
+ if (argc < 3)
+ argv = re_makeargv("(to-name) ", &argc);
+ if (argc < 3)
+ goto usage;
+ if (rem_glob_one(argv[1]) < 0)
+ return CMDERR;
+ (void) sprintf(str, "RNFR %s", argv[1]);
+ if (command(str) == CONTINUE) {
+ (void) sprintf(str, "RNTO %s", argv[2]);
+ (void) command(str);
+ }
+ return NOERR;
+} /* renamefile */
+
+
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+int ls(int argc, char **argv)
+{
+ char *whichcmd, *cp;
+ str32 lsflags;
+ string remote, local, str;
+ int listmode, pagemode, i;
+
+ PurgeLineBuffer();
+ pagemode = 0;
+ switch (**argv) {
+ case 'p': /* pls, pdir, pnlist */
+ pagemode = 1;
+ listmode = argv[0][1] == 'd';
+ break;
+ case 'd': /* dir */
+ listmode = 1;
+ break;
+ default: /* ls, nlist */
+ listmode = 0;
+ }
+ whichcmd = listmode ? "LIST" : "NLST";
+
+ (void) strncpy(local, (pagemode ? pager : "-"), sizeof(local));
+ remote[0] = lsflags[0] = 0;
+
+ /* Possible scenarios:
+ * 1. ls
+ * 2. ls -flags
+ * 3. ls directory
+ * 4. ls -flags >outfile
+ * 5. ls directory >outfile
+ * 6. ls -flags directory
+ * 7. ls -flags directory >outfile
+ *
+ * Note that using a wildcard will choke with flags. I.e., don't do
+ * "ls -CF *.tar," but instead do "ls *.tar."
+ */
+
+ for (i=1; i<argc; i++) {
+ switch (argv[i][0]) {
+ case '-':
+ /*
+ * If you give more than one set of flags, concat the each
+ * additional set to the first one (without the dash).
+ */
+ (void) strncat(lsflags, (argv[i] + (lsflags[0] == '-')), sizeof(lsflags));
+ break;
+ case '|':
+ (void) Strncpy(local, argv[i]);
+ LocalDotPath(local + 1);
+ break;
+ case '>':
+ /* We don't want the '>'. */
+ (void) Strncpy(local, argv[i] + 1);
+ LocalDotPath(local);
+ break;
+ default:
+ cp = argv[i];
+ /*
+ * In case you want to get a remote file called '--README--'
+ * or '>README,' you can use '\--README--' and '\>README.'
+ */
+ if ((cp[1] != 0) && (*cp == '\\'))
+ ++cp;
+ if (remote[0] != 0) {
+ (void) Strncat(remote, " ");
+ (void) Strncat(remote, cp);
+ } else {
+ (void) Strncpy(remote, cp);
+ }
+ } /* end switch */
+ } /* end loop */
+
+ /*
+ * If we are given an ls with some flags, make sure we use
+ * columnized output (-C) unless one column output (-1) is
+ * specified.
+ */
+ if (!listmode) {
+ if (lsflags[0] != 0) {
+ (void) Strncpy(str, lsflags);
+ for (cp = str + 1; *cp; cp++)
+ if (*cp == '1')
+ goto aa;
+ (void) sprintf(lsflags, "-FC%s", str + 1);
+ } else {
+ if (remote_is_unix)
+ (void) strcpy(lsflags, "-FC");
+ }
+ /* As noted above, we can't use -flags if the user gave a
+ * wildcard expr.
+ */
+ if (remote_is_unix && (strpbrk(remote, globchars) != NULL)) {
+ lsflags[0] = 0;
+ /* Warn the user what's going on. */
+ if ((warnNoLSFlagsWithWildcards == 0) && NOT_VQUIET) {
+ (void) fprintf(stderr, "Warning: ls flags disabled with wildcard expressions.\n");
+ warnNoLSFlagsWithWildcards++;
+ }
+ }
+ }
+
+aa:
+ is_ls = 1; /* tells getreply() to start saving input to a buffer. */
+ (void) Strncpy(str, remote);
+ if (lsflags[0] && remote[0])
+ (void) sprintf(remote, "%s%c%s", lsflags, LS_FLAGS_AND_FILE, str);
+ else
+ (void) strncpy(remote, lsflags[0] ? lsflags : str, sizeof(remote));
+ (void) recvrequest(whichcmd, local, (remote[0] == 0 ? NULL : remote), "w");
+ is_ls=0;
+ return NOERR;
+} /* ls */
+
+
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+int shell(int argc, char **argv)
+{
+ int pid;
+ Sig_t old1, old2;
+ char *theShell, *namep;
+#ifndef U_WAIT
+ int Status;
+#else
+ union wait Status;
+#endif
+ string str;
+
+ old1 = signal (SIGINT, SIG_IGN);
+ old2 = signal (SIGQUIT, SIG_IGN);
+ /* This will prevent <defunct> zombie processes. */
+ /* (void) signal(SIGCHLD, SIG_IGN); */
+
+ if ((pid = fork()) == 0) {
+ for (pid = 3; pid < 20; pid++)
+ (void) close(pid);
+ (void) Signal(SIGINT, SIG_DFL);
+ (void) Signal(SIGQUIT, SIG_DFL);
+ if ((theShell = getenv("SHELL")) == NULL)
+ theShell = uinfo.shell;
+ if (theShell == NULL)
+ theShell = "/bin/sh";
+ namep = rindex(theShell, '/');
+ if (namep == NULL)
+ namep = theShell;
+ (void) strcpy(str, "-");
+ (void) strcat(str, ++namep);
+ if (strcmp(namep, "sh") != 0)
+ str[0] = '+';
+ dbprintf ("%s\n", theShell);
+#if defined(BSD) || defined(_POSIX_SOURCE)
+ setreuid(-1,getuid());
+ setregid(-1,getgid());
+#endif
+ if (argc > 1)
+ (void) execl(theShell, str, "-c", altarg, (char *)0);
+ else
+ (void) execl(theShell, str, (char *)0);
+ PERROR("shell", theShell);
+ exit(1);
+ }
+ if (pid > 0)
+ while (wait((void *) &Status) != pid)
+ ;
+ (void) Signal(SIGINT, old1);
+ (void) Signal(SIGQUIT, old2);
+ if (pid == -1) {
+ PERROR("shell", "Try again later");
+ }
+ return NOERR;
+} /* shell */
+
+
+
+
+/*
+ * Send new user information (re-login)
+ */
+int do_user(int argc, char **argv)
+{
+ char acct[80];
+ int n, aflag = 0;
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(username) ", &argc);
+ if (argc > 4) {
+ return USAGE;
+ }
+ (void) sprintf(str, "USER %s", argv[1]);
+ n = command(str);
+ if (n == CONTINUE) {
+ if (argc < 3 )
+ argv[2] = Getpass("Password: "), argc++;
+ (void) sprintf(str, "PASS %s", argv[2]);
+ n = command(str);
+ }
+ if (n == CONTINUE) {
+ if (argc < 4) {
+ (void) printf("Account: "); (void) fflush(stdout);
+ (void) FGets(acct, stdin);
+ acct[strlen(acct) - 1] = '\0';
+ argv[3] = acct; argc++;
+ }
+ (void) sprintf(str, "ACCT %s", argv[3]);
+ n = command(str);
+ aflag++;
+ }
+ if (n != COMPLETE) {
+ (void) fprintf(stdout, "Login failed.\n");
+ logged_in = 0;
+ return (0);
+ }
+ if (!aflag && argc == 4) {
+ (void) sprintf(str, "ACCT %s", argv[3]);
+ (void) command(str);
+ }
+ logged_in = 1;
+ CheckRemoteSystemType(0);
+ return NOERR;
+} /* do_user */
+
+
+
+
+/*
+ * Print working directory.
+ */
+/*ARGSUSED*/
+int pwd(int argc, char **argv)
+{
+ (void) verbose_command("PWD");
+ return NOERR;
+} /* pwd */
+
+
+
+
+/*
+ * Make a directory.
+ */
+int makedir(int argc, char **argv)
+{
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(directory-name) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ (void) sprintf(str, "MKD %s", argv[1]);
+ (void) command(str);
+ return NOERR;
+} /* makedir */
+
+
+
+
+/*
+ * Remove a directory.
+ */
+int removedir(int argc, char **argv)
+{
+ string str;
+ if (argc < 2)
+ argv = re_makeargv("(directory-name) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ if (rem_glob_one(argv[1]) == 0) {
+ (void) sprintf(str, "RMD %s", argv[1]);
+ (void) command(str);
+ }
+ return NOERR;
+} /* removedir */
+
+
+
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+int quote(int argc, char **argv)
+{
+ int i, tmpverbose;
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(command line to send) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ str[0] = 0;
+ if (*argv[0] == 's') /* Command was 'site' instead of 'quote.' */
+ (void) Strncpy(str, "site ");
+ (void) Strncat(str, argv[1]);
+ for (i = 2; i < argc; i++) {
+ (void) Strncat(str, " ");
+ (void) Strncat(str, argv[i]);
+ }
+ tmpverbose = verbose;
+ verbose = V_VERBOSE;
+ if (command(str) == PRELIM) {
+ while (getreply(0) == PRELIM);
+ }
+ verbose = tmpverbose;
+ return NOERR;
+} /* quote */
+
+
+
+
+/*
+ * Ask the other side for help.
+ */
+int rmthelp(int argc, char **argv)
+{
+ string str;
+
+ if (argc == 1) (void) verbose_command("HELP");
+ else {
+ (void) sprintf(str, "HELP %s", argv[1]);
+ (void) verbose_command(str);
+ }
+ return NOERR;
+} /* rmthelp */
+
+
+
+
+/*
+ * Terminate session and exit.
+ */
+/*ARGSUSED*/
+int quit(int argc, char **argv)
+{
+ int rc;
+
+ /* slightly kludge. argc == -1 means failure from some other caller */
+ rc = close_up_shop() || argc == -1;
+ trim_log();
+ exit(rc);
+} /* quit */
+
+
+
+void close_streams(int wantShutDown)
+{
+ if (cout != NULL) {
+ if (wantShutDown)
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ if (cin != NULL) {
+ if (wantShutDown)
+ (void) shutdown(fileno(cin), 1+1);
+ (void) fclose(cin);
+ cin = NULL;
+ }
+} /* close_streams */
+
+
+
+
+/*
+ * Terminate session, but don't exit.
+ */
+/*ARGSUSED*/
+int disconnect(int argc, char **argv)
+{
+#ifdef SYSLOG
+ syslog (LOG_INFO, "%s disconnected from %s.", uinfo.username, hostname);
+#endif
+
+ (void) command("QUIT");
+ close_streams(0);
+ if (logged_in)
+ UpdateRecentSitesList(hostname, cwd);
+ hostname[0] = cwd[0] = 0;
+ logged_in = connected = 0;
+ data = -1;
+ macnum = 0;
+ return NOERR;
+} /* disconnect */
+
+
+
+int
+close_up_shop(void)
+{
+ static int only_once = 0;
+ int rcode = 0;
+
+ if (only_once++ > 0)
+ return (0);
+ if (connected)
+ (void) disconnect(0, NULL);
+ rcode = WriteRecentSitesFile();
+ if (logf != NULL) {
+ (void) fclose(logf);
+ logf = NULL;
+ }
+ return rcode;
+} /* close_up_shop */
+
+
+
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+int globulize(char **cpp)
+{
+ char **globbed;
+
+ (void) LocalPath(*cpp);
+ globbed = glob(*cpp);
+ if (globerr != NULL) {
+ (void) printf("%s: %s\n", *cpp, globerr);
+ if (globbed) {
+ blkfree(globbed);
+ Free(globbed);
+ }
+ return (0);
+ }
+ if (globbed) {
+ *cpp = *globbed++;
+ /* don't waste too much memory */
+ if (*globbed) {
+ blkfree(globbed);
+ Free(globbed);
+ }
+ }
+ return (1);
+} /* globulize */
+
+
+
+/* change directory to perent directory */
+/*ARGSUSED*/
+int cdup(int argc, char **argv)
+{
+ (void) _cd("CDUP");
+ return NOERR;
+} /* cdup */
+
+
+/* show remote system type */
+/*ARGSUSED*/
+int syst(int argc, char **argv)
+{
+ (void) verbose_command("SYST");
+ return NOERR;
+} /* syst */
+
+
+
+
+int make_macro(char *name, FILE *fp)
+{
+ char *tmp;
+ char *cp;
+ string str;
+ size_t len;
+ int i;
+
+ if (macnum == MAXMACROS) {
+ (void) fprintf(stderr, "Limit of %d macros have already been defined.\n", MAXMACROS);
+ return -1;
+ }
+
+ /* Make sure macros have unique names. If 'init' was attempted to be
+ * redefined, just return, since it was probably cmdOpen() in a redial
+ * mode which tried to define it again.
+ */
+ for (i = 0; i<macnum; i++) {
+ if (strncmp(name, macros[i].mac_name, (size_t)8) == 0) {
+ if (parsing_rc) {
+ /* Just shut up and read in the macro, but don't save it,
+ * because we already have it.
+ */
+ while ((cp = FGets(str, fp)) != NULL) {
+ /* See if we have a 'blank' line: just whitespace. */
+ while (*cp && isspace(*cp)) ++cp;
+ if (!*cp)
+ break;
+ }
+ } else
+ (void) fprintf(stderr,
+ "There is already a macro named '%s.'\n", name);
+ return -1;
+ }
+ }
+ (void) strncpy(macros[macnum].mac_name, name, (size_t)8);
+ if (macnum == 0)
+ macros[macnum].mac_start = macbuf;
+ else
+ macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+ tmp = macros[macnum].mac_start;
+ while (1) {
+ cp = FGets(str, fp);
+ if (cp == NULL) {
+ /*
+ * If we had started a macro, we will say it is
+ * okay to skip the blank line delimiter if we
+ * are at the EOF.
+ */
+ if (tmp > macros[macnum].mac_start)
+ goto endmac;
+ (void) fprintf(stderr, "No text supplied for macro \"%s.\"\n", name);
+ }
+ /* see if we have a 'blank' line: just whitespace. */
+ while (*cp && isspace(*cp)) ++cp;
+ if (*cp == '\0') {
+ /* Blank line; end this macro. */
+endmac:
+ macros[macnum++].mac_end = tmp;
+ return 0;
+ }
+ /* Add the text of this line to the macro. */
+ len = strlen(cp) + 1; /* we need the \0 too. */
+ if (tmp + len >= macbuf + MACBUFLEN) {
+ (void) fprintf(stderr, "Macro \"%s\" not defined -- %d byte buffer exceeded.\n", name, MACBUFLEN);
+ return -1;
+ }
+ (void) strcpy(tmp, cp);
+ tmp += len;
+ }
+} /* make_macro */
+
+
+
+
+int macdef(int argc, char **argv)
+{
+ if (argc < 2)
+ argv = re_makeargv("(macro name) ", &argc);
+ if (argc != 2) {
+ (void) domacro(0, NULL);
+ return USAGE;
+ }
+ (void) printf("Enter macro line by line, terminating it with a blank line\n");
+ (void) make_macro(argv[1], stdin);
+ return NOERR;
+} /* macdef */
+
+
+
+
+int domacro(int argc, char **argv)
+{
+ register int i, j;
+ register char *cp1, *cp2;
+ int count = 2, loopflg = 0;
+ string str;
+ struct cmd *c;
+
+ if (argc < 2) {
+ /* print macros. */
+ if (macnum == 0)
+ (void) printf("No macros defined.\n");
+ else {
+ (void) printf("Current macro definitions:\n");
+ for (i = 0; i < macnum; ++i) {
+ (void) printf("%s:\n", macros[i].mac_name);
+ cp1 = macros[i].mac_start;
+ cp2 = macros[i].mac_end;
+ while (cp1 < cp2) {
+ (void) printf(" > ");
+ while (cp1 < cp2 && *cp1)
+ putchar(*cp1++);
+ ++cp1;
+ }
+ }
+ }
+ if (argc == 0) return (NOERR); /* called from macdef(), above. */
+ argv = re_makeargv("(macro to run) ", &argc);
+ }
+ if (argc < 2) {
+ return USAGE;
+ }
+ for (i = 0; i < macnum; ++i) {
+ if (!strncmp(argv[1], macros[i].mac_name, (size_t) 9)) {
+ break;
+ }
+ }
+ if (i == macnum) {
+ (void) printf("'%s' macro not found.\n", argv[1]);
+ return USAGE;
+ }
+ doingInitMacro = (strcmp(macros[i].mac_name, "init") == 0);
+ (void) Strncpy(str, line);
+TOP:
+ cp1 = macros[i].mac_start;
+ while (cp1 != macros[i].mac_end) {
+ while (isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch(*cp1) {
+ case '\\':
+ *cp2++ = *++cp1;
+ break;
+ case '$':
+ if (isdigit(*(cp1+1))) {
+ j = 0;
+ while (isdigit(*++cp1)) {
+ j = 10*j + *cp1 - '0';
+ }
+ cp1--;
+ if (argc - 2 >= j) {
+ (void) strcpy(cp2, argv[j+1]);
+ cp2 += strlen(argv[j+1]);
+ }
+ break;
+ }
+ if (*(cp1+1) == 'i') {
+ loopflg = 1;
+ cp1++;
+ if (count < argc) {
+ (void) strcpy(cp2, argv[count]);
+ cp2 += strlen(argv[count]);
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp2++ = *cp1;
+ break;
+ }
+ if (*cp1 != '\0') {
+ cp1++;
+ }
+ }
+ *cp2 = '\0';
+ makeargv();
+ c = getcmd(margv[0]);
+ if ((c == (struct cmd *) -1) && !parsing_rc) {
+ (void) printf("?Ambiguous command\n");
+ } else if (c == NULL && !parsing_rc) {
+ (void) printf("?Invalid command\n");
+ } else if (c->c_conn && !connected) {
+ (void) printf("Not connected.\n");
+ } else {
+ if (IS_VVERBOSE)
+ (void) printf("%s\n",line);
+ if ((*c->c_handler)(margc, margv) == USAGE)
+ cmd_usage(c);
+ (void) strcpy(line, str);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (cp1 != macros[i].mac_end) {
+ cp1++;
+ }
+ }
+ if (loopflg && ++count < argc) {
+ goto TOP;
+ }
+ doingInitMacro = 0;
+ return NOERR;
+} /* domacro */
+
+
+
+/*
+ * get size of file on remote machine
+ */
+int sizecmd(int argc, char **argv)
+{
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-file) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ if (rem_glob_one(argv[1]) == 0) {
+ (void) sprintf(str, "SIZE %s", argv[1]);
+ (void) verbose_command(str);
+ }
+ return NOERR;
+} /* sizecmd */
+
+
+
+
+/*
+ * get last modification time of file on remote machine
+ */
+int modtime(int argc, char **argv)
+{
+ int overbose;
+ string str;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-file) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ if (rem_glob_one(argv[1]) == 0) {
+ overbose = verbose;
+ if (debug == 0)
+ verbose = V_QUIET;
+ (void) sprintf(str, "MDTM %s", argv[1]);
+ if (command(str) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ (void) sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d",
+ &yy, &mo, &day, &hour, &min, &sec);
+ /* might want to print this in local time */
+ (void) printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
+ mo, day, yy, hour, min, sec);
+ } else
+ (void) fputs(reply_string, stdout);
+ verbose = overbose;
+ }
+ return NOERR;
+} /* modtime */
+
+
+
+int lookup(int argc, char **argv)
+{
+ int i, j, by_name, result = NOERR;
+ struct hostent *host; /* structure returned by gethostbyaddr() */
+ extern int h_errno;
+#ifdef BAD_INETADDR
+ struct in_addr addr; /* address in host order */
+# define ADDR addr.s_addr
+#else
+ unsigned long addr; /* address in host order */
+# define ADDR addr
+#endif
+
+ if (argc < 2)
+ argv = re_makeargv("(sitename) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+
+ lasthostname[0] = 0;
+ for (i=1; i<argc; i++) {
+ /* does the argument look like an address? */
+ if (4 == sscanf (argv[i], "%d.%d.%d.%d", &j, &j, &j, &j)) {
+ /* ip */
+ addr = inet_addr (argv[i]);
+ if (ADDR == 0xffffffff) {
+ (void) fprintf(stderr, "## could not convert \"%s\" into a valid IP address.\n", argv[i]);
+ continue;
+ }
+ host = gethostbyaddr ((char *) &ADDR, 4, AF_INET);
+ by_name = 0;
+ } else {
+ /* name */
+ host = gethostbyname (argv[i]);
+ by_name = 1;
+ }
+ if (host == NULL) {
+ if (NOT_VQUIET) {
+ /* gethostxxx error */
+ if (h_errno == HOST_NOT_FOUND) {
+ (void) printf("%s: lookup error (%d).\n",
+ argv[i], h_errno);
+ result = h_errno;
+ } else {
+ (void) printf("%s \"%s\"\n",
+ (by_name==0 ? "unknown address" : "unknown host"),
+ argv[i]);
+ result =
+ h_errno != 0 ? h_errno :
+ -1;
+ }
+ }
+ } else {
+ if (*host->h_name)
+ (void) Strncpy(lasthostname, host->h_name);
+ for (j=0; host->h_aliases[j] != NULL; j++) {
+ if (strlen(host->h_aliases[j]) >
+ strlen(host->h_name) &&
+ strstr(host->h_aliases[j],host->h_name) != NULL)
+ (void) Strncpy(lasthostname,host->h_aliases[j]);
+ }
+ if (NOT_VQUIET) {
+ (void) printf("%-32s ", *host->h_name ? host->h_name : "???");
+ if (*host->h_addr_list) {
+ unsigned long horder;
+
+ horder = ntohl (*(unsigned long *) *(char **)host->h_addr_list);
+ (void) printf ("%lu.%lu.%lu.%lu\n",
+ (horder >> 24),
+ (horder >> 16) & 0xff,
+ (horder >> 8) & 0xff,
+ horder & 0xff);
+ }
+ else (void) printf("???\n");
+ }
+ }
+ } /* loop thru all sites */
+ return result;
+} /* lookup */
+
+
+
+
+int getlocalhostname(char *host, size_t size)
+{
+ int oldv, r;
+ char *argv[2];
+ char domain[64];
+
+#ifdef HOSTNAME
+ (void) strncpy(host, HOSTNAME, size);
+ return NOERR;
+#else
+ host[0] = '\0';
+ if ((r = gethostname(host, size)) == 0) {
+ if (host[0] == '\0') {
+ (void) fprintf(stderr,
+"Could not determine the hostname. Re-compile with HOSTNAME defined\n\
+to be the full name of your hostname.\n");
+ exit(1);
+ }
+ oldv = verbose;
+ verbose = V_QUIET;
+ argv[0] = "lookup";
+ (void) sprintf(line, "lookup %s", host);
+ (void) makeargv();
+ if (lookup(margc, margv) == 0 && lasthostname[0]) {
+ (void) _Strncpy(host, lasthostname, size);
+ domain[0] = '\0';
+#ifdef HAS_DOMAINNAME
+ /* getdomainname() returns just the domain name, without a
+ * preceding period. For example, on "cse.unl.edu", it would
+ * return "unl.edu".
+ *
+ * SunOS note: getdomainname will return an empty string if
+ * this machine isn't on NIS.
+ */
+ (void) getdomainname(domain, sizeof(domain) - 1);
+#endif
+#ifdef DOMAIN_NAME
+ (void) Strncpy(domain, DOMAIN_NAME);
+#endif
+ if (index(host, '.') == NULL) {
+ /* If the hostname has periods we'll assume that the
+ * it includes the domain name already. Some gethostname()s
+ * return the whole host name, others just the machine name.
+ * If we have just the machine name and we successfully
+ * found out the domain name (from above), we'll append
+ * the domain to the machine to get a full hostname.
+ */
+ if (domain[0]) {
+ if (domain[0] != '.')
+ (void) _Strncat(host, ".", size);
+ (void) _Strncat(host, domain, size);
+ } else {
+ fprintf(stderr,
+"WARNING: could not determine full host name (have: '%s').\n\
+The program should be re-compiled with DOMAIN_NAME defined to be the\n\
+domain name, i.e. -DDOMAIN_NAME=\\\"unl.edu\\\"\n\n",
+ host);
+ }
+ }
+ }
+ verbose = oldv;
+ }
+ return r;
+#endif
+} /* getlocalhostname */
+
+
+
+
+/*
+ * show status on remote machine
+ */
+int rmtstatus(int argc, char **argv)
+{
+ string str;
+
+ if (argc > 1) {
+ (void) sprintf(str, "STAT %s" , argv[1]);
+ (void) verbose_command(str);
+ } else (void) verbose_command("STAT");
+ return NOERR;
+} /* rmtstatus */
+
+
+
+
+/*
+ * create an empty file on remote machine.
+ */
+int create(int argc, char **argv)
+{
+ string str;
+ FILE *ftemp;
+
+ if (argc < 2)
+ argv = re_makeargv("(remote-file) ", &argc);
+ if (argc < 2) {
+ return USAGE;
+ }
+ (void) tmp_name(str);
+ ftemp = fopen(str, "w");
+ /* (void) fputc('x', ftemp); */
+ (void) fclose(ftemp);
+ creating = 1;
+ (void) sendrequest("STOR", str, argv[1]);
+ creating = 0;
+ (void) unlink(str);
+ return NOERR;
+} /* create */
+
+
+
+
+/* show version info */
+/*ARGSUSED*/
+int show_version(int argc, char **argv)
+{
+ char *DStrs[80];
+ int nDStrs = 0, i, j;
+
+ (void) printf("%-30s %s\n", "NcFTP Version:", version);
+ (void) printf("%-30s %s\n", "Author:",
+ "Mike Gleason, NCEMRSoft (mgleason@cse.unl.edu).");
+
+/* Now entering CPP hell... */
+#ifdef __DATE__
+ (void) printf("%-30s %s\n", "Compile Date:", __DATE__);
+#endif
+ (void) printf("%-30s %s (%s)\n", "Operating System:",
+#ifdef System
+ System,
+#else
+# ifdef unix
+ "UNIX",
+# else
+ "??",
+# endif
+#endif
+#ifdef SYSV
+ "SYSV");
+#else
+# ifdef BSD
+ "BSD");
+# else
+ "neither BSD nor SYSV?");
+# endif
+#endif
+
+ /* Show which CPP symbols were used in compilation. */
+#ifdef __GNUC__
+ DStrs[nDStrs++] = "__GNUC__";
+#endif
+#ifdef RINDEX
+ DStrs[nDStrs++] = "RINDEX";
+#endif
+#ifdef CURSES
+ DStrs[nDStrs++] = "CURSES";
+#endif
+#ifdef NO_CURSES_H
+ DStrs[nDStrs++] = "NO_CURSES_H";
+#endif
+#ifdef HERROR
+ DStrs[nDStrs++] = "HERROR";
+#endif
+#ifdef U_WAIT
+ DStrs[nDStrs++] = "U_WAIT";
+#endif
+#if defined(NO_CONST) || defined(const)
+ DStrs[nDStrs++] = "NO_CONST";
+#endif
+#ifdef NO_FORMATTING
+ DStrs[nDStrs++] = "NO_FORMATTING";
+#endif
+#ifdef DONT_TIMESTAMP
+ DStrs[nDStrs++] = "DONT_TIMESTAMP";
+#endif
+#ifdef GETPASS
+ DStrs[nDStrs++] = "GETPASS";
+#endif
+#ifdef HAS_GETCWD
+ DStrs[nDStrs++] = "HAS_GETCWD";
+#endif
+#ifdef GETCWDSIZET
+ DStrs[nDStrs++] = "GETCWDSIZET";
+#endif
+#ifdef HAS_DOMAINNAME
+ DStrs[nDStrs++] = "HAS_DOMAINNAME";
+#endif
+#ifdef DOMAIN_NAME
+ DStrs[nDStrs++] = "DOMAIN_NAME";
+#endif
+#ifdef Solaris
+ DStrs[nDStrs++] = "Solaris";
+#endif
+#ifdef USE_GETPWUID
+ DStrs[nDStrs++] = "USE_GETPWUID";
+#endif
+#ifdef HOSTNAME
+ DStrs[nDStrs++] = "HOSTNAME";
+#endif
+#ifdef SYSDIRH
+ DStrs[nDStrs++] = "SYSDIRH";
+#endif
+#ifdef SYSSELECTH
+ DStrs[nDStrs++] = "SYSSELECTH";
+#endif
+#ifdef TERMH
+ DStrs[nDStrs++] = "TERMH";
+#endif
+#ifdef NO_UNISTDH
+ DStrs[nDStrs++] = "NO_UNISTDH";
+#endif
+#ifdef NO_STDLIBH
+ DStrs[nDStrs++] = "NO_STDLIBH";
+#endif
+#ifdef SYSLOG
+ DStrs[nDStrs++] = "SYSLOG";
+#endif
+#ifdef BAD_INETADDR
+ DStrs[nDStrs++] = "BAD_INETADDR";
+#endif
+#ifdef SGTTYB
+ DStrs[nDStrs++] = "SGTTYB";
+#endif
+#ifdef TERMIOS
+ DStrs[nDStrs++] = "TERMIOS";
+#endif
+#ifdef STRICT_PROTOS
+ DStrs[nDStrs++] = "STRICT_PROTOS";
+#endif
+#ifdef dFTP_PORT
+ DStrs[nDStrs++] = "dFTP_PORT";
+#endif
+#ifdef BROKEN_MEMCPY
+ DStrs[nDStrs++] = "BROKEN_MEMCPY";
+#endif
+#ifdef READLINE
+ DStrs[nDStrs++] = "READLINE";
+#endif
+#ifdef GETLINE
+ DStrs[nDStrs++] = "GETLINE";
+#endif
+#ifdef _POSIX_SOURCE
+ DStrs[nDStrs++] = "_POSIX_SOURCE";
+#endif
+#ifdef _XOPEN_SOURCE
+ DStrs[nDStrs++] = "_XOPEN_SOURCE";
+#endif
+#ifdef NO_TIPS
+ DStrs[nDStrs++] = "NO_TIPS";
+#endif
+#ifdef GZCAT
+ DStrs[nDStrs++] = "GZCAT";
+#endif
+#ifdef LINGER
+ DStrs[nDStrs++] = "LINGER";
+#endif
+#ifdef TRY_NOREPLY
+ DStrs[nDStrs++] = "TRY_NOREPLY";
+#endif
+#ifdef NO_UTIMEH
+ DStrs[nDStrs++] = "NO_UTIMEH";
+#endif
+#ifdef DB_ERRS
+ DStrs[nDStrs++] = "DB_ERRS";
+#endif
+#ifdef NO_VARARGS
+ DStrs[nDStrs++] = "NO_VARARGS";
+#endif
+#ifdef NO_STDARGH
+ DStrs[nDStrs++] = "NO_STDARGH";
+#endif
+#ifdef NO_MKTIME
+ DStrs[nDStrs++] = "NO_MKTIME";
+#endif
+#ifdef NO_STRSTR
+ DStrs[nDStrs++] = "NO_STRSTR";
+#endif
+#ifdef NO_STRFTIME
+ DStrs[nDStrs++] = "NO_STRFTIME";
+#endif
+#ifdef NO_RENAME
+ DStrs[nDStrs++] = "NO_RENAME";
+#endif
+#ifdef TRY_ABOR
+ DStrs[nDStrs++] = "TRY_ABOR";
+#endif
+#ifdef GATEWAY
+ DStrs[nDStrs++] = "GATEWAY";
+#endif
+#ifdef SOCKS
+ DStrs[nDStrs++] = "SOCKS";
+#endif
+#ifdef NET_ERRNO_H
+ DStrs[nDStrs++] = "NET_ERRNO_H";
+#endif
+
+
+/* DONE with #ifdefs for now! */
+
+ (void) printf ("\nCompile Options:\n");
+ for (i=j=0; i<nDStrs; i++) {
+ if (j == 0)
+ (void) printf(" ");
+ (void) printf("%-15s", DStrs[i]);
+ if (++j == 4) {
+ j = 0;
+ (void) putchar('\n');
+ }
+ }
+ if (j != 0)
+ (void) putchar('\n');
+
+#ifdef MK
+ (void) printf("\nMK: %s\n", MK);
+#endif /* MK */
+
+ (void) printf("\nDefaults:\n");
+ (void) printf("\
+ Xfer Buf Size: %8d Debug: %d MPrompt: %d Verbosity: %d\n\
+ Prompt: %s Pager: %s ZCat: %s\n\
+ Logname: %s Logging: %d Type: %s Cmd Len: %d\n\
+ Recv Line Len: %d #Macros: %d Macbuf: %d Auto-Binary: %d\n\
+ Recent File: %s Recent On: %d nRecents: %d\n\
+ Redial Delay: %d Anon Open: %d New Mail Message: \"%s\"\n",
+ MAX_XFER_BUFSIZE, dDEBUG, dMPROMPT, dVERBOSE,
+ dPROMPT, dPAGER, ZCAT,
+ dLOGNAME, dLOGGING, dTYPESTR, CMDLINELEN,
+ RECEIVEDLINELEN, MAXMACROS, MACBUFLEN, dAUTOBINARY,
+ dRECENTF, dRECENT_ON, dMAXRECENTS,
+ dREDIALDELAY, dANONOPEN, NEWMAILMESSAGE
+ );
+#ifdef GATEWAY
+ (void) printf("\
+ Gateway Login: %s\n", dGATEWAY_LOGIN);
+#endif
+ return NOERR;
+} /* show_version */
+
+
+
+void PurgeLineBuffer(void)
+{
+ register struct lslist *a, *b;
+
+ for (a = lshead; a != NULL; ) {
+ b = a->next;
+ if (a->string)
+ free(a->string); /* free string */
+ Free(a); /* free node */
+ a = b;
+ }
+ lshead = lstail = NULL;
+} /* PurgeLineBuffer */
+
+
+
+
+/*ARGSUSED*/
+int ShowLineBuffer(int argc, char **argv)
+{
+ register struct lslist *a = lshead;
+ int pagemode;
+ FILE *fp;
+ Sig_t oldintp;
+
+ if (a == NULL)
+ return CMDERR;
+ pagemode= (**argv) == 'p' && pager[0] == '|';
+ if (pagemode) {
+ fp = popen(pager + 1, "w");
+ if (!fp) {
+ PERROR("ShowLineBuffer", pager + 1);
+ return CMDERR;
+ }
+ } else
+ fp = stdout;
+ oldintp = Signal(SIGPIPE, SIG_IGN);
+ while (a) {
+ if (a->string)
+ (void) fprintf(fp, "%s\n", a->string);
+ a = a->next;
+ }
+ if (pagemode)
+ (void) pclose(fp);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ return NOERR;
+} /* ShowLineBuffer */
+
+
+
+
+#if LIBMALLOC != LIBC_MALLOC
+/*ARGSUSED*/
+int MallocStatusCmd(int argc, char **argv)
+{
+#if (LIBMALLOC == FAST_MALLOC)
+ struct mallinfo mi;
+
+ mi = mallinfo();
+ printf("\
+total space in arena: %d\n\
+number of ordinary blocks: %d\n\
+number of small blocks: %d\n\
+number of holding blocks: %d\n\
+space in holding block headers: %d\n\
+space in small blocks in use: %d\n\
+space in free small blocks: %d\n\
+space in ordinary blocks in use: %d\n\
+space in free ordinary blocks: %d\n\
+cost of enabling keep option: %d\n",
+ mi.arena,
+ mi.ordblks,
+ mi.smblks,
+ mi.hblks,
+ mi.hblkhd,
+ mi.usmblks,
+ mi.fsmblks,
+ mi.uordblks,
+ mi.fordblks,
+ mi.keepcost
+ );
+#else
+#if (LIBMALLOC == DEBUG_MALLOC)
+ printf("malloc_chain_check: %d\n\n", malloc_chain_check(0));
+ if (argc > 1)
+ malloc_dump(1);
+ printf("malloc_inuse: %lu\n", malloc_inuse(NULL));
+#else
+ printf("Nothing to report.\n");
+#endif /* (LIBMALLOC == DEBUG_MALLOC) */
+#endif /* (LIBMALLOC == FAST_MALLOC) */
+
+ return (0);
+} /* MallocStatusCmd */
+#endif /* LIBMALLOC */
+
+
+
+
+/*ARGSUSED*/
+int unimpl(int argc, char **argv)
+{
+ if (!parsing_rc)
+ (void) printf("%s: command not supported. (and probably won't ever be).\n", argv[0]);
+ return (NOERR);
+} /* unimpl */
+
+int setpassive(int argc, char **argv)
+{
+ passivemode = !passivemode;
+ printf( "Passive mode %s.\n", (passivemode ? "ON" : "OFF") );
+ return NOERR;
+}
+
+
+/* eof cmds.c */
diff --git a/usr.bin/ncftp/cmds.h b/usr.bin/ncftp/cmds.h
new file mode 100644
index 0000000..0f8bce0
--- /dev/null
+++ b/usr.bin/ncftp/cmds.h
@@ -0,0 +1,132 @@
+/* cmds.h */
+
+#ifndef _cmd_h_
+#define _cmd_h_
+
+/* $RCSfile: cmds.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/07/09 10:58:19 $
+ */
+
+/* Verbosity levels. */
+#define V_QUIET -1
+#define V_ERRS 0
+#define V_TERSE 1
+#define V_VERBOSE 2
+#define V_IMPLICITCD 4
+#define IS_VQUIET (verbose <= V_QUIET)
+#define IS_VERRS (verbose == V_ERRS)
+#define IS_VTERSE (verbose == V_TERSE)
+#define IS_VVERBOSE (verbose == V_VERBOSE)
+#define NOT_VQUIET (verbose > V_QUIET)
+
+/* Open modes. */
+#define OPEN_A 1
+#define OPEN_U 0
+
+#define LS_FLAGS_AND_FILE '\1'
+
+/* Possible values returned by GetDateAndTime. */
+#define SIZE_UNKNOWN (-1L)
+#define MDTM_UNKNOWN (0L)
+
+/* Command result codes. */
+#define USAGE (88)
+#define NOERR (0)
+#define CMDERR (-1)
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+ char *c_name; /* name of command */
+ char c_conn; /* must be connected to use command */
+ char c_hidden; /* a hidden command or alias (won't show up in help) */
+ int (*c_handler)(int, char **); /* function to call */
+ char *c_help; /* help string */
+ char *c_usage; /* usage string or NULL, to ask the function itself. */
+};
+
+#define NCMDS ((int) ((sizeof (cmdtab) / sizeof (struct cmd)) - 1))
+
+struct macel {
+ char mac_name[9]; /* macro name */
+ char *mac_start; /* start of macro in macbuf */
+ char *mac_end; /* end of macro in macbuf */
+};
+
+struct types {
+ char *t_name;
+ char *t_mode;
+ int t_type;
+ char *t_arg;
+};
+
+struct lslist {
+ char *string;
+ struct lslist *next;
+};
+
+int settype(int argc, char **argv);
+int _settype(char *typename);
+int setbinary(int argc, char **argv);
+int setascii(int argc, char **argv);
+int put(int argc, char **argv);
+int mput(int argc, char **argv);
+int rem_glob_one(char *pattern);
+int get(int argc, char **argv);
+void mabort SIG_PARAMS;
+int mget(int argc, char **argv);
+char *remglob(char *argv[], int *);
+int setverbose(int argc, char **argv);
+int setprompt(int argc, char **argv);
+int setdebug(int argc, char **argv);
+void fix_options(void);
+int cd(int argc, char **argv);
+int implicit_cd(char *dir);
+int _cd(char *dir);
+int lcd(int argc, char **argv);
+int do_delete(int argc, char **argv);
+int mdelete(int argc, char **argv);
+int renamefile(int argc, char **argv);
+int ls(int argc, char **argv);
+int shell(int argc, char **argv);
+int do_user(int argc, char **argv);
+int pwd(int argc, char **argv);
+int makedir(int argc, char **argv);
+int removedir(int argc, char **argv);
+int quote(int argc, char **argv);
+int rmthelp(int argc, char **argv);
+int quit(int argc, char **argv);
+void close_streams(int wantShutDown);
+int disconnect(int argc, char **argv);
+int close_up_shop(void);
+int globulize(char **cpp);
+int cdup(int argc, char **argv);
+int syst(int argc, char **argv);
+int make_macro(char *name, FILE *fp);
+int macdef(int argc, char **argv);
+int domacro(int argc, char **argv);
+int sizecmd(int argc, char **argv);
+int modtime(int argc, char **argv);
+int lookup(int argc, char **argv);
+int rmtstatus(int argc, char **argv);
+int create(int argc, char **argv);
+int getlocalhostname(char *host, size_t size);
+int show_version(int argc, char **argv);
+void PurgeLineBuffer(void);
+int ShowLineBuffer(int argc, char **argv);
+int MallocStatusCmd(int argc, char **argv);
+int unimpl(int argc, char **argv);
+long GetDateSizeFromLSLine(char *fName, unsigned long *mod_time);
+long GetDateAndSize(char *fName, unsigned long *mod_time);
+int SetTypeByNumber(int i);
+int setpassive(int argc, char **argv);
+
+
+/* In util.c: */
+void cmd_help(struct cmd *c);
+void cmd_usage(struct cmd *c);
+struct cmd *getcmd(char *name);
+
+#endif /* _cmd_h_ */
diff --git a/usr.bin/ncftp/cmdtab.c b/usr.bin/ncftp/cmdtab.c
new file mode 100644
index 0000000..86d4a5f
--- /dev/null
+++ b/usr.bin/ncftp/cmdtab.c
@@ -0,0 +1,277 @@
+/* cmdtab.c */
+
+/* $RCSfile: cmdtab.c,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/07/09 11:04:56 $
+ */
+
+#include "sys.h"
+#include "util.h"
+#include "cmds.h"
+#include "main.h"
+#include "ftp.h"
+#include "ftprc.h"
+#include "glob.h"
+#include "open.h"
+#include "set.h"
+#include "copyright.h"
+
+#define REMOTEFILE " remote-file-name"
+#define REMOTEFILES " remote-file-names and/or UNIX-style-wildcards"
+#define LOCALFILE " local-file-name"
+#define LOCALFILES " local-file-names and/or UNIX-style-wildcards"
+#define LDIRNAME " local-directory-name"
+#define RMTDIRNAME " remote-directory-name"
+#define EMPTYSTR ""
+#define TOGGLE " [on | off] (no argument toggles the switch)"
+
+#define BINARYHELP "transfer files as binary files, without CR/LF translation"
+#define BINARYUSAGE EMPTYSTR
+
+#define CHDIRHELP "changes the current remote working directory"
+#define CHDIRUSAGE RMTDIRNAME
+
+#define CLOSEHELP "closes FTP connection to current remote host"
+#define CLOSEUSAGE EMPTYSTR
+
+#define DELETEHELP "deletes the specified file on the remote host"
+#define DELETEUSAGE REMOTEFILE
+
+#define DIRUSAGE " \
+[flags] [remote-items] [>outfile or \"|pipecmd [cmd-args]\"]\n\
+ Note that there must be no whitespace between > and outfile, or | and\n\
+ pipecmd, and if the pipe-command needs arguments, you must enclose the\n\
+ whole thing with double quotes.\n\
+Examples:\n\
+ dir -s\n\
+ dir remoteFile\n\
+ dir /pub/mac \"|head -20\"\n\
+ dir -rtR file1 file2 dir1 >contents.txt"
+
+#define GETUSAGE " remote-file-name [local-file-name or |pipecommand]\n\
+Examples:\n\
+ get myfile.txt\n\
+ get MYFILE.ZIP myfile.zip\n\
+ get myfile.txt |head\n\
+ get myfile.txt \"|head -20\"\n\
+ get ./help/newuser.txt (./newuser.txt will be local-file-name)\n\
+ get ./help/newuser.txt ./docs/newbie.help\n\
+ get my*.txt (pseudo-filename-completion if match is unique, i.e. myfile.txt)"
+
+#define HELPHELP "shows commands, and optionally tell you how to use a specific one"
+#define HELPUSAGE " [command-name | showall (shows hidden commands) | helpall"
+
+#define LSHELP "prints remote directory contents (short-mode)"
+#define LSUSAGE " \
+[flags] [remote-items] [>outfile or \"|pipecmd [cmd-args]\"]\n\
+ Note that there must be no whitespace between > and outfile, or | and\n\
+ pipecmd, and if the pipe-command needs arguments, you must enclose the\n\
+ whole thing with double quotes.\n\
+Examples:\n\
+ ls -s\n\
+ ls remoteFile\n\
+ ls /pub/mac \"|head -20\"\n\
+ ls -lrtR file1 file2 dir1 >contents.txt"
+
+#define OPENHELP "connects to a new remote host, and optionally fetches a file\n\
+ or sets the current remote working directory"
+#define OPENUSAGE " \
+[-a | -u] [-i] [-p N] [-r [-d N] [-g N]] hostname[:pathname]\n\
+ -a : Open anonymously (this is the default).\n\
+ -u : Open, specify user/password.\n\
+ -i : Ignore machine entry in your .netrc.\n\
+ -p N : Use port #N for connection.\n\
+ -r : \"Redial\" until connected.\n\
+ -d N : Redial, pausing N seconds between tries.\n\
+ -g N : Redial, giving up after N tries.\n\
+ :path : Open site, then retrieve file \"path.\" WWW-style paths are\n\
+ also acceptable, i.e. 'ftp://cse.unl.edu/mgleason/README.'"
+
+#define PAGEHELP "view a file on the remote host with your $PAGER"
+#define PAGEUSAGE REMOTEFILE
+
+#define PASSIVEHELP "enter passive transfer mode"
+
+#define PDIRUSAGE " [flags] [remote-files]"
+
+#define PUTHELP "sends a local file to the current remote host"
+#define PUTUSAGE " local-file-name [remote-file-name]"
+
+#define QUITHELP "quits the program"
+#define QUITUSAGE EMPTYSTR
+
+#define RHELPHELP "asks the remote-server for help"
+#define RHELPUSAGE " [help-topic (i.e. FTP command)]"
+
+#define UNIMPLHELP "this command is not supported"
+#define UNIMPLUSAGE (NULL)
+
+struct cmd cmdtab[] = {
+ /* name ; must-be-connected ; hidden ; help-string ; usage-string */
+ { "!", 0, 0, shell,
+ "spawns a shell for you to run other commands",
+ " [single-command-and-arguments]" },
+ { "$", 0, 0, domacro,
+ "runs a macro previously defined in your NETRC, or with the macdef cmd",
+ "macro-number" },
+ { "account", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "append", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "ascii", 1, 1, setascii,
+ "transfer files as text files, with proper CR/LF translation",
+ "" },
+ { "bell", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "binary", 1, 1, setbinary, BINARYHELP, BINARYUSAGE },
+ { "bye", 0, 1, quit, QUITHELP, QUITUSAGE },
+ { "case", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "cd", 1, 0, cd, CHDIRHELP, CHDIRUSAGE },
+ { "cdup", 1, 0, cdup,
+ "changes the current remote working directory to it's parent",
+ "" },
+ { "chdir", 1, 1, cd, CHDIRHELP, CHDIRUSAGE },
+ { "close", 1, 1, disconnect, CLOSEHELP, CLOSEUSAGE },
+ { "connect", 0, 1, cmdOpen, OPENHELP, OPENUSAGE },
+ { "cr", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "create", 1, 0, create,
+ "create an empty file on the remote host",
+ REMOTEFILE },
+ { "delete", 1, 0, do_delete, DELETEHELP, DELETEUSAGE },
+ { "debug", 0, 1, setdebug,
+ "to print debugging messages during execution of the program",
+ TOGGLE },
+ { "dir", 1, 0, ls,
+ "prints remote directory contents (long-mode)",
+ DIRUSAGE },
+ { "erase", 1, 1, do_delete, DELETEHELP, DELETEUSAGE },
+ { "exit", 0, 1, quit, QUITHELP, QUITUSAGE },
+ { "form", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "get", 1, 0, get,
+ "fetches a file from the current remote host", GETUSAGE },
+ { "glob", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "hash", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "help", 0, 0, help, HELPHELP, HELPUSAGE },
+ { "idle", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "image", 1, 1, setbinary, BINARYHELP, BINARYUSAGE },
+ { "lcd", 0, 0, lcd,
+ "changes the current local directory", LDIRNAME },
+ { "lookup", 0, 0, lookup,
+ "uses the name-server to tell you a host's IP number given it's\n\
+ name, or it's name given it's IP number",
+ " hostname | host-IP-number" },
+ { "ls", 1, 0, ls, LSHELP, LSUSAGE },
+ { "macdef", 0, 0, macdef,
+ "defines a macro which is expanded when you use the $ command",
+ " new-macro-name" },
+ { "mdelete", 1, 0, mdelete,
+ "deletes multiple files on the remote host", REMOTEFILES },
+ { "mdir", 1, 1, ls, LSHELP, LSUSAGE },
+#if LIBMALLOC != LIBC_MALLOC
+ { "memchk", 0, 0, MallocStatusCmd,
+ "show debugging information about memory usage.", EMPTYSTR },
+#endif
+ { "mget", 1, 0, mget,
+ "fetches multiple files from the remote host", REMOTEFILES },
+ { "mkdir", 1, 0, makedir,
+ "creates a new sub-directory on the current remote host",
+ RMTDIRNAME },
+ { "mls", 1, 0, ls, LSHELP, LSUSAGE },
+ { "mode", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "modtime", 1, 0, modtime,
+ "shows the last modification date for a remote file",
+ REMOTEFILE },
+ { "more", 1, 1, get, PAGEHELP, PAGEUSAGE },
+ { "mput", 1, 0, mput,
+ "sends multiple local files to the current remote host",
+ LOCALFILES },
+ { "newer", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "nlist", 1, 1, ls, LSHELP, LSUSAGE },
+ { "nmap", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "ntrans", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "open", 0, 0, cmdOpen, OPENHELP, OPENUSAGE },
+ { "p", 1, 1, get, PAGEHELP, PAGEUSAGE },
+ { "passive", 0, 0, setpassive, PASSIVEHELP, EMPTYSTR },
+ { "page", 1, 0, get, PAGEHELP, PAGEUSAGE },
+ { "pdir", 1, 0, ls,
+ "view a remote directory listing (long mode) with your $PAGER",
+ PDIRUSAGE },
+ { "pls", 1, 0, ls,
+ "view a remote directory listing (short mode) with your $PAGER",
+ PDIRUSAGE },
+ { "predir", 1, 0, ShowLineBuffer,
+ "view the last remote directory listing with your $PAGER",
+ EMPTYSTR },
+ { "prompt", 0, 1, setprompt,
+ "toggle interactive prompting on multiple commands",
+ TOGGLE },
+ { "proxy", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "put", 1, 0, put, PUTHELP, PUTUSAGE },
+ { "pwd", 1, 0, pwd,
+ "prints the name of the current remote directory",
+ EMPTYSTR },
+ { "quit", 0, 0, quit, QUITHELP, QUITUSAGE },
+ { "quote", 1, 0, quote,
+ "allows advanced users to directly enter FTP commands verbatim",
+ " FTP-commands" },
+ { "redir", 1, 0, ShowLineBuffer,
+ "re-prints the last directory listing",
+ EMPTYSTR },
+ { "reget", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "remotehelp", 1, 0, rmthelp, RHELPHELP, RHELPUSAGE },
+ { "reset", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "restart", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "rm", 1, 1, do_delete, DELETEHELP, DELETEUSAGE },
+ { "rstatus", 1, 0, rmtstatus,
+ "asks the remote-server for it's status",
+ EMPTYSTR },
+ { "rhelp", 1, 1, rmthelp, RHELPHELP, RHELPUSAGE },
+ { "rename", 1, 0, renamefile,
+ "changes the name of a file on the current remote host",
+ " old-name new-name" },
+ { "rmdir", 1, 0, removedir,
+ "deletes a directory on the current remote host",
+ RMTDIRNAME },
+ { "runique", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "send", 1, 1, put, PUTHELP, PUTUSAGE },
+ { "sendport", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "show", 0, 0, do_show,
+ "prints the value of some or all program variables",
+ " all | variable-names" },
+ { "set", 0, 0, set,
+ "changes the value of a program variable; for numeric/boolean\n\
+ variables sets them to 1/true",
+ " variable-name [= new-value]" },
+ { "site", 1, 0, quote,
+ "allows advanced users to send site-specific commands to the host",
+ " site-specific-commands\n\
+Example (to try on wuarchive.wustl.edu):\n\
+ site locate emacs" },
+ { "size", 1, 0, sizecmd,
+ "shows the size of a remote file",
+ REMOTEFILE },
+ { "struct", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "sunique", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "system", 1, 0, syst,
+ "tells you what type of machine the current remote host is",
+ EMPTYSTR },
+ { "tenex", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "umask", 0, 1, unimpl, UNIMPLHELP, UNIMPLUSAGE },
+ { "unset", 0, 0, set,
+ "resets the value of a program variable to it's default state, or for\n\
+ numeric/boolean variables, sets them to 0/false",
+ " variable-name" },
+ { "user", 1, 0, do_user,
+ "lets you login as a new user (with appropriate password)",
+ " new-user-name [new-password]" },
+ { "type", 1, 0, settype,
+ "changes the current file transfer method",
+ " ascii | binary | ebcdic | tenex" },
+ { "verbose", 0, 0, setverbose,
+ "controls how many message the program prints in response to commands",
+ " -1 (quiet) | 0 (errs) | 1 (terse) | 2 (verbose)" },
+ { "version", 0, 0, show_version,
+ "prints information about the program",
+ EMPTYSTR },
+ { "?", 0, 1, help, HELPHELP, HELPUSAGE },
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/* eof cmdtab.c */
diff --git a/usr.bin/ncftp/copyright.h b/usr.bin/ncftp/copyright.h
new file mode 100644
index 0000000..9c4290e
--- /dev/null
+++ b/usr.bin/ncftp/copyright.h
@@ -0,0 +1,25 @@
+/* Copyright.h */
+
+/*
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1992-1995 Mike Gleason, NCEMRSoft.
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions may not be sold for profit on physical
+ * media such as disks, tapes, and CD-ROMS, without expressed written
+ * permission.
+ */
+
+#ifdef _main_c_
+#ifndef lint
+static char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994, 1995 by NCEMRSoft and Copyright (c) 1985, 1989 Regents of the University of California.\n All rights reserved.\n";
+#endif /* not lint */
+#endif /* _main_c_ */
+
+/* eof copyright.h */
diff --git a/usr.bin/ncftp/defaults.h b/usr.bin/ncftp/defaults.h
new file mode 100644
index 0000000..18331ea
--- /dev/null
+++ b/usr.bin/ncftp/defaults.h
@@ -0,0 +1,138 @@
+/* Defaults.h: default values for ftp's common variables */
+
+/* These are all surrounded by #ifndef blocks so you can just use
+ * the -D flag with your compiler (i.e. -DZCAT=\"/usr/local/bin/zcat\").
+ */
+
+#ifndef _DEFAULTS_H_
+#define _DEFAULTS_H_
+
+/* $RCSfile: defaults.h,v $
+ * $Revision: 14020.13 $
+ * $Date: 93/07/09 10:58:27 $
+ */
+
+#ifndef NEWMAILMESSAGE /* For english speakers, "You have new mail." */
+#define NEWMAILMESSAGE "You have new mail."
+#endif
+
+#ifndef ZCAT /* Usually "zcat," but use the full pathname */
+ /* if possible. */
+# ifdef GZCAT /* If you said you had gnu's zcat, use it
+ * since it can do .Z files too.
+ */
+
+# define ZCAT GZCAT
+# else /* !GZCAT */
+# define ZCAT "zcat"
+# endif /* ifdef GZCAT */
+#endif /* ifndef ZCAT */
+
+#ifndef MAX_XFER_BUFSIZE
+#define MAX_XFER_BUFSIZE 32768
+#endif
+
+#ifndef dANONOPEN /* 1 or 0, usually 1 */
+#define dANONOPEN 1
+#endif
+
+#ifndef dDEBUG /* 1 or 0, usually 0 */
+#define dDEBUG 0
+#endif
+
+#ifndef dMPROMPT /* Usually 1, I prefer 0... */
+#define dMPROMPT 0
+#endif
+
+/* If passive FTP can be used, this specifies whether it is turned on
+ * by default. If not, we have passive mode available, but are using
+ * Port ftp by default.
+ */
+#ifndef dPASSIVE
+#define dPASSIVE 0 /* Use PORT for more portability... */
+#endif
+
+#ifndef dVERBOSE /* V_QUIET, V_ERRS, V_TERSE, V_VERBOSE */
+#define dVERBOSE V_TERSE
+#endif
+
+#ifndef dPROMPT /* short: "@Bftp@P>" */
+ /* long: "@B@E @UNcFTP@P @B@M@D@P ->" */
+#define dPROMPT "@B@c@Mncftp@P>" /* new two line prompt */
+#endif
+
+#ifndef dPAGER /* if set to empty string, act like 'cat' */
+#define dPAGER "more"
+#endif
+
+#ifndef dLOGNAME /* usu. put in the user's home directory. */
+#define dLOGNAME "~/.ftplog"
+#endif
+
+#ifndef dRECENTF /* usu. put in the user's home directory. */
+#define dRECENTF "~/.ncrecent"
+#endif
+
+#ifndef dMAXRECENTS /* limit to how many recent sites to save. */
+#define dMAXRECENTS 50
+#endif
+
+#ifndef dRECENT_ON /* Do you want the recent log on? */
+ /* usually 1. */
+#define dRECENT_ON 1
+#endif
+
+ /* Do you want logging on by default? */
+#ifndef dLOGGING /* usually 0 */
+#define dLOGGING 0
+#endif
+
+#ifndef dTYPE /* usually TYPE_A */
+#define dTYPE TYPE_A
+#endif
+
+#ifndef dTYPESTR /* usually "ascii" */
+#define dTYPESTR "ascii"
+#endif
+
+#ifndef dREDIALDELAY /* usu. 60 (seconds). */
+#define dREDIALDELAY 60
+#endif
+
+#ifndef CMDLINELEN
+#define CMDLINELEN 256
+#endif
+
+#ifndef RECEIVEDLINELEN
+#define RECEIVEDLINELEN 256
+#endif
+
+#ifndef MAXMACROS
+#define MAXMACROS 16
+#endif
+
+#ifndef MACBUFLEN /* usually 4096. */
+#define MACBUFLEN 4096
+#endif
+
+/* Do you want binary transfers by default? */
+#ifndef dAUTOBINARY /* usually 1 */
+#define dAUTOBINARY 1
+#endif
+
+#ifndef dPROGRESS
+#define dPROGRESS pr_philbar /* can be: pr_none, pr_percent, pr_philbar,
+ * or pr_kbytes
+ */
+#endif
+
+/* Default login name at gateway */
+#ifdef GATEWAY
+# ifndef dGATEWAY_LOGIN
+# define dGATEWAY_LOGIN "ftp"
+# endif
+#endif
+
+#endif /* _DEFAULTS_H_ */
+
+/* eof */
diff --git a/usr.bin/ncftp/ftp.c b/usr.bin/ncftp/ftp.c
new file mode 100644
index 0000000..2fdeffb
--- /dev/null
+++ b/usr.bin/ncftp/ftp.c
@@ -0,0 +1,1918 @@
+/* ftp.c */
+
+/* $RCSfile: ftp.c,v $
+ * $Revision: 14020.12 $
+ * $Date: 93/07/09 11:30:28 $
+ */
+
+#include "sys.h"
+
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#ifndef AIX /* AIX-2.2.1 declares utimbuf in unistd.h */
+#ifdef NO_UTIMEH
+struct utimbuf {time_t actime; time_t modtime;};
+#else
+# include <utime.h>
+#endif
+#endif /*AIX*/
+
+#ifdef SYSLOG
+# include <syslog.h>
+#endif
+
+/* You may need this for declarations of fd_set, etc. */
+#ifdef SYSSELECTH
+# include <sys/select.h>
+#else
+#ifdef STRICT_PROTOS
+#ifndef Select
+extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+#endif
+#endif
+#endif
+
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef NET_ERRNO_H
+# include <net/errno.h>
+#endif
+#include <netdb.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <ctype.h>
+#include "util.h"
+#include "ftp.h"
+#include "cmds.h"
+#include "main.h"
+#include "ftprc.h"
+#include "getpass.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* ftp.c globals */
+struct sockaddr_in hisctladdr;
+struct sockaddr_in data_addr;
+int data = -1;
+int abrtflag = 0;
+struct sockaddr_in myctladdr;
+FILE *cin = NULL, *cout = NULL;
+char *reply_string = NULL;
+static char pad3a[8] = "Pad 3a"; /* For SunOS :-( */
+jmp_buf sendabort;
+static char pad3b[8] = "Pad 3b";
+jmp_buf recvabort;
+static char pad3c[8] = "Pad 3c";
+int progress_meter = dPROGRESS;
+int cur_progress_meter;
+int sendport = -1; /* use PORT cmd for each data connection */
+int using_pasv;
+int code; /* return/reply code for ftp command */
+string indataline;
+int cpend; /* flag: if != 0, then pending server reply */
+char *xferbuf; /* buffer for local and remote I/O */
+size_t xferbufsize; /* size in bytes, of the transfer buffer. */
+long next_report;
+long bytes;
+long now_sec;
+long file_size;
+struct timeval start, stop;
+int buffer_only = 0; /* True if reading into redir line
+ * buffer only (not echoing to
+ * stdout).
+ */
+
+/* ftp.c externs */
+extern FILE *logf;
+extern string anon_password;
+extern longstring cwd, lcwd;
+extern Hostname hostname;
+extern int verbose, debug, macnum, margc;
+extern int curtype, creating, toatty;
+extern int options, activemcmd, paging;
+extern int ansi_escapes, logged_in, macnum;
+extern char *line, *margv[];
+extern char *tcap_normal, *tcap_boldface;
+extern char *tcap_underline, *tcap_reverse;
+extern struct userinfo uinfo;
+extern struct macel macros[];
+extern struct lslist *lshead, *lstail;
+extern int is_ls;
+extern int passivemode;
+
+#ifdef GATEWAY
+extern string gateway;
+extern string gate_login;
+#endif
+
+
+#ifdef _POSIX_SOURCE
+FILE *safeopen(int s, char *lmode){
+ FILE *file;
+
+ setreuid(geteuid(),getuid());
+ setregid(getegid(),getgid());
+ file=fdopen(s, lmode);
+ setreuid(geteuid(),getuid());
+ setregid(getegid(),getgid());
+ return(file);
+}
+#else
+#define safeopen fdopen
+#endif
+
+
+int hookup(char *host, unsigned int port)
+{
+ register struct hostent *hp = 0;
+ int s, len, hErr = -1;
+ string errstr;
+ char **curaddr = NULL;
+
+ bzero((char *)&hisctladdr, sizeof (hisctladdr));
+#ifdef BAD_INETADDR
+ hisctladdr.sin_addr = inet_addr(host);
+#else
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+#endif
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ hisctladdr.sin_family = AF_INET;
+ (void) Strncpy(hostname, host);
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+#ifdef HERROR
+ extern int h_errno;
+ if (h_errno == HOST_NOT_FOUND)
+ (void) printf("%s: unknown host\n", host);
+ else (void) fprintf(stderr, "%s: gethostbyname herror (%d): ",
+ host, h_errno);
+ herror(NULL);
+#else
+ (void) printf("%s: unknown host\n", host);
+#endif
+ goto done;
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+ curaddr = hp->h_addr_list;
+ bcopy(*curaddr, (caddr_t)&hisctladdr.sin_addr, hp->h_length);
+ (void) Strncpy(hostname, hp->h_name);
+ }
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ PERROR("hookup", "socket");
+ goto done;
+ }
+ hisctladdr.sin_port = port;
+#ifdef SOCKS
+ while (Rconnect(s, (struct sockaddr *) &hisctladdr, (int) sizeof (hisctladdr)) < 0) {
+#else
+ while (Connect(s, &hisctladdr, sizeof (hisctladdr)) < 0) {
+#endif
+ if (curaddr != NULL) {
+ curaddr++;
+ if (*curaddr != (char *)0) {
+ (void) sprintf(errstr, "connect error to address %s",
+ inet_ntoa(hisctladdr.sin_addr));
+ PERROR("hookup", errstr);
+ bcopy(*curaddr, (caddr_t)&hisctladdr.sin_addr, hp->h_length);
+ dbprintf("Trying %s...\n", inet_ntoa(hisctladdr.sin_addr));
+ (void) close(s);
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ PERROR("hookup", "socket");
+ goto done;
+ }
+ continue;
+ }
+ }
+ PERROR("hookup", host);
+ switch (errno) {
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ECONNABORTED:
+ case ETIMEDOUT:
+ case ECONNREFUSED:
+ case EHOSTDOWN:
+ hErr = -2; /* we can re-try later. */
+ }
+ goto bad;
+ }
+ len = sizeof (myctladdr);
+ if (Getsockname(s, (char *)&myctladdr, &len) < 0) {
+ PERROR("hookup", "getsockname");
+ goto bad;
+ }
+ cin = safeopen(s, "r");
+ cout = safeopen(dup(s), "w");
+ if (cin == NULL || cout == NULL) {
+ (void) fprintf(stderr, "ftp: safeopen failed.\n");
+ close_streams(0);
+ goto bad;
+ }
+ if (IS_VVERBOSE)
+ (void) printf("Connected to %s.\n", hostname);
+#ifdef IPTOS_LOWDELAY /* control is interactive */
+#ifdef IP_TOS
+ {
+ int nType = IPTOS_LOWDELAY;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS,
+ (char *) &nType, sizeof(nType)) < 0) {
+ PERROR("hookup", "setsockopt(IP_TOS)");
+ }
+ }
+#endif
+#endif
+ if (getreply(0) > 2) { /* read startup message from server */
+ close_streams(0);
+ if (code == 421)
+ hErr = -2; /* We can try again later. */
+ goto bad;
+ }
+#ifdef SO_OOBINLINE
+ {
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on))
+ < 0 && debug) {
+ PERROR("hookup", "setsockopt(SO_OOBINLINE)");
+ }
+ }
+#endif /* SO_OOBINLINE */
+
+ hErr = 0;
+ using_pasv = passivemode; /* Re-init for each new connection. */
+ goto done;
+
+bad:
+ (void) close(s);
+ if (cin != NULL)
+ (void) fclose(cin);
+ if (cout != NULL)
+ (void) fclose(cout);
+ cin = cout = NULL;
+done:
+ return (hErr);
+} /* hookup */
+
+
+/* This registers the user's username, password, and account with the remote
+ * host which validates it. If we get on, we also do some other things, like
+ * enter a log entry and execute the startup macro.
+ */
+int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit)
+{
+ string userName;
+ string str;
+ int n;
+ int sentAcct = 0;
+ int userWasPrompted = 0;
+ int result = CMDERR;
+ time_t now;
+
+ if (userNamePtr == NULL) {
+ /* Prompt for a username. */
+ (void) sprintf(str, "Login Name (%s): ", uinfo.username);
+ ++userWasPrompted;
+ if (Gets(str, userName, sizeof(userName)) == NULL)
+ goto done;
+ else if (userName[0]) {
+ /* User didn't just hit return. */
+ userNamePtr = userName;
+ } else {
+ /*
+ * User can hit return if he wants to enter his username
+ * automatically.
+ */
+ if (*uinfo.username != '\0')
+ userNamePtr = uinfo.username;
+ else
+ goto done;
+ }
+ }
+
+#ifdef GATEWAY
+ if (*gateway)
+ (void) sprintf(str, "USER %s@%s",
+ (*gate_login ? gate_login : dGATEWAY_LOGIN),
+ hostname);
+ else
+#endif
+ (void) sprintf(str, "USER %s", userNamePtr);
+
+ /* Send the user name. */
+ n = command(str);
+ if (n == CONTINUE) {
+ if (passWordPtr == NULL) {
+ if (((strcmp("anonymous", userName) == 0) ||
+ (strcmp("ftp", userName) == 0)) && (*anon_password != '\0'))
+ passWordPtr = anon_password;
+ else {
+ /* Prompt for a password. */
+ ++userWasPrompted;
+ passWordPtr = Getpass("Password:");
+ }
+ }
+
+ /* The remote site is requesting us to send the password now. */
+ (void) sprintf(str, "PASS %s", passWordPtr);
+ n = command(str);
+ if (n == CONTINUE) {
+ /* The remote site is requesting us to send the account now. */
+ if (accountPtr == NULL) {
+ /* Prompt for a username. */
+ (void) sprintf(str, "ACCT %s", Getpass("Account:"));
+ ++userWasPrompted;
+ } else {
+ (void) sprintf(str, "ACCT %s", accountPtr);
+ }
+ ++sentAcct; /* Keep track that we've sent the account already. */
+ n = command(str);
+ }
+ }
+
+ if (n != COMPLETE) {
+ (void) printf("Login failed.\n");
+ goto done;
+ }
+
+ /* If you specified an account, and the remote-host didn't request it
+ * (maybe it's optional), we will send the account information.
+ */
+ if (!sentAcct && accountPtr != NULL) {
+ (void) sprintf(str, "ACCT %s", accountPtr);
+ (void) command(str);
+ }
+
+ /* See if remote host dropped connection. Some sites will let you log
+ * in anonymously, only to tell you that they already have too many
+ * anon users, then drop you. We do a no-op here to see if they've
+ * ditched us.
+ */
+ n = quiet_command("NOOP");
+ if (n == TRANSIENT)
+ goto done;
+
+#ifdef SYSLOG
+ syslog(LOG_INFO, "%s connected to %s as %s.",
+ uinfo.username, hostname, userNamePtr);
+#endif
+
+ /* Save which sites we opened to the user's logfile. */
+ if (logf != NULL) {
+ (void) time(&now);
+ (void) fprintf(logf, "%s opened at %s",
+ hostname,
+ ctime(&now));
+ }
+
+ /* Let the user know we are logged in, unless he was prompted for some
+ * information already.
+ */
+ if (!userWasPrompted)
+ if (NOT_VQUIET)
+ (void) printf("Logged into %s.\n", hostname);
+
+ if ((doInit) && (macnum > 0)) {
+ /* Run the startup macro, if any. */
+ /* If macnum is non-zero, the init macro was defined from
+ * ruserpass. It would be the only macro defined at this
+ * point.
+ */
+ (void) strcpy(line, "$init");
+ makeargv();
+ (void) domacro(margc, margv);
+ }
+
+ _cd(NULL); /* Init cwd variable. */
+
+ result = NOERR;
+ logged_in = 1;
+
+done:
+ return (result);
+} /* Login */
+
+
+
+/*ARGSUSED*/
+void cmdabort SIG_PARAMS
+{
+ (void) printf("\n");
+ (void) fflush(stdout);
+ abrtflag++;
+} /* cmdabort */
+
+
+
+
+int CommandWithFlags(char *cmd, int flags)
+{
+ int r;
+ Sig_t oldintr;
+ string str;
+
+ if (cmd == NULL) {
+ /* Should never happen; bug if it does. */
+ PERROR("command", "NULL command");
+ return (-1);
+ }
+ abrtflag = 0;
+ if (debug) {
+ if (strncmp(cmd, "PASS", (size_t)4) == 0)
+ dbprintf("cmd: \"PASS ********\"\n");
+ else
+ dbprintf("cmd: \"%s\" (length %d)\n", cmd, (int) strlen(cmd));
+ }
+ if (cout == NULL) {
+ (void) sprintf(str, "%s: No control connection for command", cmd);
+ PERROR("command", str);
+ return (0);
+ }
+ oldintr = Signal(SIGINT, /* cmdabort */ SIG_IGN);
+
+ /* Used to have BROKEN_MEMCPY tested here. */
+ if (cout != NULL)
+ (void) fprintf(cout, "%s\r\n", cmd);
+
+ (void) fflush(cout);
+ cpend = 1;
+ r = (flags == WAIT_FOR_REPLY) ?
+ (getreply(strcmp(cmd, "QUIT") == 0)) : PRELIM;
+ if (abrtflag && oldintr != SIG_IGN && oldintr != NULL)
+ (*oldintr)(0);
+ (void) Signal(SIGINT, oldintr);
+ return(r);
+} /* CommandWithFlags */
+
+
+
+/* This stub runs 'CommandWithFlags' above, telling it to wait for
+ * reply after the command is sent.
+ */
+int command(char *cmd)
+{
+ return (CommandWithFlags(cmd, WAIT_FOR_REPLY));
+} /* command */
+
+/* This stub runs 'CommandWithFlags' above, telling it to NOT wait for
+ * reply after the command is sent.
+ */
+int command_noreply(char *cmd)
+{
+ return(CommandWithFlags(cmd, DONT_WAIT_FOR_REPLY));
+} /* command */
+
+
+
+int quiet_command(char *cmd)
+{
+ register int oldverbose, result;
+
+ oldverbose = verbose;
+ verbose = debug ? V_VERBOSE : V_QUIET;
+ result = command(cmd);
+ verbose = oldverbose;
+ return (result);
+} /* quiet_command */
+
+
+
+
+int verbose_command(char *cmd)
+{
+ register int oldverbose, result;
+
+ oldverbose = verbose;
+ verbose = V_VERBOSE;
+ result = command(cmd);
+ verbose = oldverbose;
+ return (result);
+} /* quiet_command */
+
+
+
+
+int getreply(int expecteof)
+{
+ register int c, n = 0;
+ int dig;
+ char *cp, *end, *dp;
+ int thiscode, originalcode = 0, continuation = 0;
+ Sig_t oldintr;
+
+ if (cin == NULL)
+ return (-1);
+ /* oldintr = Signal(SIGINT, SIG_IGN); */
+ oldintr = Signal(SIGINT, cmdabort);
+ end = reply_string + RECEIVEDLINELEN - 2;
+ for (;abrtflag==0;) {
+ dig = n = thiscode = code = 0;
+ cp = reply_string;
+ for (;abrtflag==0;) {
+ c = fgetc(cin);
+ if (c == IAC) { /* handle telnet commands */
+ switch (c = fgetc(cin)) {
+ case WILL:
+ case WONT:
+ c = fgetc(cin);
+ (void) fprintf(cout, "%c%c%c",IAC,DONT,c);
+ (void) fflush(cout);
+ break;
+ case DO:
+ case DONT:
+ c = fgetc(cin);
+ (void) fprintf(cout, "%c%c%c",IAC,WONT,c);
+ (void) fflush(cout);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ (void) Signal(SIGINT, oldintr);
+ code = 221;
+ return (0);
+ }
+ lostpeer(0);
+ if (NOT_VQUIET) {
+ (void) printf("421 Service not available, remote server has closed connection\n");
+ (void) fflush(stdout);
+ }
+ code = 421;
+ return(4);
+ }
+ if (cp < end && c != '\r')
+ *cp++ = c;
+
+ if (c == '\n')
+ break;
+ if (dig < 4 && isdigit(c))
+ code = thiscode = code * 10 + (c - '0');
+ else if (dig == 4 && c == '-') {
+ if (continuation)
+ code = 0;
+ continuation++;
+ }
+ if (n == 0)
+ n = c;
+ } /* end for(;;) #2 */
+
+ *cp = '\0';
+ dbprintf("rsp: %s", reply_string);
+
+ switch (verbose) {
+ case V_QUIET:
+ /* Don't print anything. */
+ break;
+ case V_ERRS:
+ if (n == '5') {
+ dp = reply_string;
+ goto stripCode;
+ }
+ break;
+ case V_IMPLICITCD:
+ case V_TERSE:
+ dp = NULL;
+ if (n == '5' && verbose == V_TERSE)
+ dp = reply_string;
+ else {
+ switch (thiscode) {
+ case 230:
+ case 214:
+ case 331:
+ case 332:
+ case 421: /* For ftp.apple.com, etc. */
+ dp = reply_string;
+ break;
+ case 220:
+ /*
+ * Skip the foo FTP server ready line.
+ */
+ if (strstr(reply_string, "ready.") == NULL)
+ dp = reply_string;
+ break;
+ case 250:
+ /*
+ * Print 250 lines if they aren't
+ * "250 CWD command successful."
+ */
+ if (strncmp(reply_string + 4, "CWD ", (size_t) 4))
+ dp = reply_string;
+ }
+ }
+ if (dp == NULL) break;
+stripCode:
+ /* Try to strip out the code numbers, etc. */
+ if (isdigit(*dp++) && isdigit(*dp++) && isdigit(*dp++)) {
+ if (*dp == ' ' || *dp == '-') {
+ dp++;
+ if (*dp == ' ') dp++;
+ } else dp = reply_string;
+ } else {
+ int spaces;
+ dp = reply_string;
+ for (spaces = 0; spaces < 4; ++spaces)
+ if (dp[spaces] != ' ')
+ break;
+ if (spaces == 4)
+ dp += spaces;
+ }
+ goto printLine;
+ case V_VERBOSE:
+ dp = reply_string;
+printLine: (void) fputs(dp, stdout);
+ } /* end switch */
+
+ if (continuation && code != originalcode) {
+ if (originalcode == 0)
+ originalcode = code;
+ continue;
+ }
+ if (n != '1')
+ cpend = 0;
+ (void) Signal(SIGINT,oldintr);
+ if (code == 421 || originalcode == 421)
+ lostpeer(0);
+ if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN && oldintr)
+ (*oldintr)(0);
+ break;
+ } /* end for(;;) #1 */
+ return (n - '0');
+} /* getreply */
+
+
+
+
+static int empty(struct fd_set *mask, int sec)
+{
+ struct timeval t;
+
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0;
+
+ return(Select(32, mask, NULL, NULL, &t));
+} /* empty */
+
+
+
+
+static void tvsub(struct timeval *tdiff, struct timeval *t1, struct timeval *t0)
+{
+ tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+ tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+ if (tdiff->tv_usec < 0)
+ tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+} /* tvsub */
+
+
+/* Variables private to progress_report code. */
+static int barlen;
+static long last_dot;
+static int dots;
+
+int start_progress(int sending, char *local)
+{
+ long s;
+ char spec[64];
+
+ cur_progress_meter = toatty ? progress_meter : 0;
+ if ((cur_progress_meter > pr_last) || (cur_progress_meter < 0))
+ cur_progress_meter = dPROGRESS;
+ if ((file_size <= 0) && ((cur_progress_meter == pr_percent) || (cur_progress_meter == pr_philbar) || (cur_progress_meter == pr_last)))
+ cur_progress_meter = pr_kbytes;
+ if (!ansi_escapes && (cur_progress_meter == pr_philbar))
+ cur_progress_meter = pr_dots;
+
+ (void) Gettimeofday(&start);
+ now_sec = start.tv_sec;
+
+ switch (cur_progress_meter) {
+ case pr_none:
+ break;
+ case pr_percent:
+ (void) printf("%s: ", local);
+ goto zz;
+ case pr_kbytes:
+ (void) printf("%s: ", local);
+ goto zz;
+ case pr_philbar:
+ (void) printf("%s%s file: %s %s\n",
+ tcap_boldface,
+ sending ? "Sending" : "Receiving",
+ local,
+ tcap_normal
+ );
+ barlen = 52;
+ for (s = file_size; s > 0; s /= 10L) barlen--;
+ (void) sprintf(spec, " 0 %%%ds %%ld bytes. ETA: --:--\r",
+ barlen);
+ (void) printf(spec, " ", file_size);
+ goto zz;
+ case pr_dots:
+ last_dot = (file_size / 10) + 1;
+ dots = 0;
+ (void) printf("%s: ", local);
+ zz:
+ (void) fflush(stdout);
+ Echo(stdin, 0);
+ } /* end switch */
+ return (cur_progress_meter);
+} /* start_progress */
+
+
+
+
+int progress_report(int finish_up)
+{
+ int size;
+ int perc;
+ float frac;
+ char spec[64];
+ float secsElap;
+ int secsLeft, minLeft;
+ struct timeval td;
+
+ next_report += xferbufsize;
+ (void) Gettimeofday(&stop);
+ if ((stop.tv_sec > now_sec) || (finish_up && file_size)) {
+ switch (cur_progress_meter) {
+ case pr_none:
+ break;
+ case pr_percent:
+ perc = (int) (100.0 * (float)bytes / (float)file_size);
+ if (perc > 100) perc = 100;
+ else if (perc < 0) perc = 0;
+ (void) printf("\b\b\b\b%3d%%", perc);
+ (void) fflush(stdout);
+ break;
+ case pr_philbar:
+ frac = (float)bytes / (float)file_size;
+ if (frac > 1.0)
+ frac = 1.0;
+ else if (frac < 0.0)
+ frac = 0.0;
+ size = (int) ((float)barlen * frac);
+ (void) sprintf(spec,
+ "%%3d%%%% 0 %%s%%%ds%%s%%%ds %%ld bytes. ETA:%%3d:%%02d\r",
+ size, barlen - size);
+ perc = (long) (100.0 * frac);
+ tvsub(&td, &stop, &start);
+ secsElap = td.tv_sec + (td.tv_usec / 1000000.0);
+ secsLeft = (int) ((float)file_size / ((float)bytes/secsElap) -
+ secsElap + 0.5);
+ minLeft = secsLeft / 60;
+ secsLeft = secsLeft - (minLeft * 60);
+ (void) printf(
+ spec,
+ perc,
+ tcap_reverse,
+ "",
+ tcap_normal,
+ "",
+ file_size,
+ minLeft,
+ secsLeft
+ );
+ (void) fflush(stdout);
+ break;
+ case pr_kbytes:
+ if ((bytes / 1024) > 0) {
+ (void) printf("\b\b\b\b\b\b%5ldK", bytes / 1024);
+ (void) fflush(stdout);
+ }
+ break;
+ case pr_dots:
+ if (bytes > last_dot) {
+ (void) fputc('.', stdout);
+ (void) fflush(stdout);
+ last_dot += (file_size / 10) + 1;
+ dots++;
+ }
+ } /* end switch */
+ now_sec = stop.tv_sec;
+ } /* end if we updated */
+ return (UserLoggedIn());
+} /* progress_report */
+
+
+
+
+
+void end_progress(char *direction, char *local, char *remote)
+{
+ struct timeval td;
+ float s, bs = 0.0;
+ str32 bsstr;
+ int doLastReport;
+ int receiving;
+ longstring fullRemote, fullLocal;
+
+ doLastReport = ((UserLoggedIn()) && (cur_progress_meter != pr_none) &&
+ (NOT_VQUIET) && (bytes > 0));
+
+ receiving = (direction[0] == 'r');
+
+ switch(FileType(local)) {
+ case IS_FILE:
+ (void) Strncpy(fullLocal, lcwd);
+ (void) Strncat(fullLocal, "/");
+ (void) Strncat(fullLocal, local);
+ break;
+ case IS_PIPE:
+ doLastReport = 0;
+ local = Strncpy(fullLocal, local);
+ break;
+ case IS_STREAM:
+ default:
+ doLastReport = 0;
+ local = Strncpy(fullLocal, receiving ? "stdout" : "stdin");
+ }
+
+ if (doLastReport)
+ (void) progress_report(1); /* tell progress proc to cleanup. */
+
+ tvsub(&td, &stop, &start);
+ s = td.tv_sec + (td.tv_usec / 1000000.0);
+
+ bsstr[0] = '\0';
+ if (s != 0.0) {
+ bs = (float)bytes / s;
+ if (bs > 1024.0)
+ sprintf(bsstr, "%.2f K/s", bs / 1024.0);
+ else
+ sprintf(bsstr, "%.2f Bytes/sec", bs);
+ }
+
+ if (doLastReport) switch(cur_progress_meter) {
+ case pr_none:
+ zz:
+ (void) printf("%s: %ld bytes %s in %.2f seconds, %s.\n",
+ local, bytes, direction, s, bsstr);
+ break;
+ case pr_kbytes:
+ case pr_percent:
+ (void) printf("%s%ld bytes %s in %.2f seconds, %s.\n",
+ cur_progress_meter == pr_kbytes ? "\b\b\b\b\b\b" : "\b\b\b\b",
+ bytes, direction, s, bsstr);
+ Echo(stdin, 1);
+ break;
+ case pr_philbar:
+ (void) printf("\n");
+ Echo(stdin, 1);
+ goto zz;
+ case pr_dots:
+ for (; dots < 10; dots++)
+ (void) fputc('.', stdout);
+ (void) fputc('\n', stdout);
+ Echo(stdin, 1);
+ goto zz;
+ }
+
+ /* Save transfers to the logfile. */
+ /* if a simple path is given, try to log the full path */
+ if (*remote != '/') {
+ (void) Strncpy(fullRemote, cwd);
+ (void) Strncat(fullRemote, "/");
+ (void) Strncat(fullRemote, remote);
+ } else
+ (void) Strncpy(fullRemote, remote);
+
+ if (logf != NULL) {
+ (void) fprintf(logf, "\t-> \"%s\" %s, %s\n",
+ fullRemote, direction, bsstr);
+ }
+#ifdef SYSLOG
+ {
+ longstring infoPart1;
+
+ /* Some syslog()'s can't take an unlimited number of arguments,
+ * so shorten our call to syslog to 5 arguments total.
+ */
+ Strncpy(infoPart1, uinfo.username);
+ if (receiving) {
+ Strncat(infoPart1, " received ");
+ Strncat(infoPart1, fullRemote);
+ Strncat(infoPart1, " as ");
+ Strncat(infoPart1, fullLocal);
+ Strncat(infoPart1, " from ");
+ } else {
+ Strncat(infoPart1, " sent ");
+ Strncat(infoPart1, fullLocal);
+ Strncat(infoPart1, " as ");
+ Strncat(infoPart1, fullRemote);
+ Strncat(infoPart1, " to ");
+ }
+ Strncat(infoPart1, hostname);
+ syslog (LOG_INFO, "%s (%ld bytes, %s).", infoPart1, bytes, bsstr);
+ }
+#endif /* SYSLOG */
+} /* end_progress */
+
+
+
+
+void close_file(FILE **fin, int filetype)
+{
+ if (*fin != NULL) {
+ if (filetype == IS_FILE) {
+ (void) fclose(*fin);
+ *fin = NULL;
+ } else if (filetype == IS_PIPE) {
+ (void) pclose(*fin);
+ *fin = NULL;
+ }
+ }
+} /* close_file */
+
+
+
+
+/*ARGSUSED*/
+void abortsend SIG_PARAMS
+{
+ activemcmd = 0;
+ abrtflag = 0;
+ (void) fprintf(stderr, "\nSend aborted.\n");
+ Echo(stdin, 1);
+ longjmp(sendabort, 1);
+} /* abortsend */
+
+
+
+int sendrequest(char *cmd, char *local, char *remote)
+{
+ FILE *fin, *dout = NULL;
+ Sig_t oldintr, oldintp;
+ string str;
+ register int c, d;
+ struct stat st;
+ int filetype, result = NOERR;
+ int do_reports = 0;
+ char *mode;
+ register char *bufp;
+
+ dbprintf("cmd: %s; rmt: %s; loc: %s.\n",
+ cmd,
+ remote == NULL ? "(null)" : remote,
+ local == NULL ? "(null)" : local
+ );
+
+ oldintr = NULL;
+ oldintp = NULL;
+ mode = "w";
+ bytes = file_size = 0L;
+ if (setjmp(sendabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ result = -1;
+ goto xx;
+ }
+ oldintr = Signal(SIGINT, abortsend);
+ file_size = -1;
+ if (strcmp(local, "-") == 0) {
+ fin = stdin;
+ filetype = IS_STREAM;
+ } else if (*local == '|') {
+ filetype = IS_PIPE;
+ oldintp = Signal(SIGPIPE,SIG_IGN);
+ fin = popen(local + 1, "r");
+ if (fin == NULL) {
+ PERROR("sendrequest", local + 1);
+ (void) Signal(SIGINT, oldintr);
+ (void) Signal(SIGPIPE, oldintp);
+ result = -1;
+ goto xx;
+ }
+ } else {
+ filetype = IS_FILE;
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ PERROR("sendrequest", local);
+ (void) Signal(SIGINT, oldintr);
+ result = -1;
+ goto xx;
+ }
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode&S_IFMT) != S_IFREG) {
+ (void) fprintf(stdout, "%s: not a plain file.\n", local);
+ (void) Signal(SIGINT, oldintr);
+ (void) fclose(fin);
+ result = -1;
+ goto xx;
+ }
+ file_size = st.st_size;
+ }
+ if (initconn()) {
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ result = -1;
+ close_file(&fin, filetype);
+ goto xx;
+ }
+ if (setjmp(sendabort))
+ goto Abort;
+
+#ifdef TRY_NOREPLY
+ if (remote) {
+ (void) sprintf(str, "%s %s", cmd, remote);
+ (void) command_noreply(str);
+ } else {
+ (void) command_noreply(cmd);
+ }
+
+ dout = dataconn(mode);
+ if (dout == NULL)
+ goto Abort;
+
+ if(getreply(0) != PRELIM) {
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ close_file(&fin, filetype);
+ return -1;
+ }
+#else
+ if (remote) {
+ (void) sprintf(str, "%s %s", cmd, remote);
+ if (command(str) != PRELIM) {
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ close_file(&fin, filetype);
+ goto xx;
+ }
+ } else {
+ if (command(cmd) != PRELIM) {
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ close_file(&fin, filetype);
+ goto xx;
+ }
+ }
+
+ dout = dataconn(mode);
+ if (dout == NULL)
+ goto Abort;
+#endif
+
+ (void) Gettimeofday(&start);
+ oldintp = Signal(SIGPIPE, SIG_IGN);
+ if ((do_reports = (filetype == IS_FILE && NOT_VQUIET)) != 0)
+ do_reports = start_progress(1, local);
+
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ errno = d = 0;
+ while ((c = read(fileno(fin), xferbuf, (int)xferbufsize)) > 0) {
+ bytes += c;
+ for (bufp = xferbuf; c > 0; c -= d, bufp += d)
+ if ((d = write(fileno(dout), bufp, c)) <= 0)
+ break;
+ /* Print progress indicator. */
+ if (do_reports)
+ do_reports = progress_report(0);
+ }
+ if (c < 0)
+ PERROR("sendrequest", local);
+ if (d <= 0) {
+ if (d == 0 && !creating)
+ (void) fprintf(stderr, "netout: write returned 0?\n");
+ else if (errno != EPIPE)
+ PERROR("sendrequest", "netout");
+ bytes = -1;
+ }
+ break;
+
+ case TYPE_A:
+ next_report = xferbufsize;
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ if (ferror(dout))
+ break;
+ (void) putc('\r', dout);
+ bytes++;
+ }
+ (void) putc(c, dout);
+ bytes++;
+
+ /* Print progress indicator. */
+ if (do_reports && bytes > next_report)
+ do_reports = progress_report(0);
+ }
+ if (ferror(fin))
+ PERROR("sendrequest", local);
+ if (ferror(dout)) {
+ if (errno != EPIPE)
+ PERROR("sendrequest", "netout");
+ bytes = -1;
+ }
+ break;
+ }
+Done:
+ close_file(&fin, filetype);
+ if (dout)
+ (void) fclose(dout);
+ (void) getreply(0);
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ end_progress("sent", local, remote);
+xx:
+ return (result);
+Abort:
+ result = -1;
+ if (!cpend)
+ goto xx;
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ goto Done;
+} /* sendrequest */
+
+
+
+
+/*ARGSUSED*/
+void abortrecv SIG_PARAMS
+{
+ activemcmd = 0;
+ abrtflag = 0;
+ (void) fprintf(stderr,
+#ifdef TRY_ABOR
+ "(abort)\n");
+#else
+ "\nAborting, please wait...");
+#endif
+ (void) fflush(stderr);
+ Echo(stdin, 1);
+ longjmp(recvabort, 1);
+} /* abortrecv */
+
+
+
+
+void GetLSRemoteDir(char *remote, char *remote_dir)
+{
+ char *cp;
+
+ /*
+ * The ls() function can specify a directory to list along with ls flags,
+ * if it sends the flags first followed by the directory name.
+ *
+ * So far, we don't care about the remote directory being listed. I put
+ * it now so I won't forget in case I need to do something with it later.
+ */
+ remote_dir[0] = 0;
+ if (remote != NULL) {
+ cp = index(remote, LS_FLAGS_AND_FILE);
+ if (cp == NULL)
+ (void) Strncpy(remote_dir, remote);
+ else {
+ *cp++ = ' ';
+ (void) Strncpy(remote_dir, cp);
+ }
+ }
+} /* GetLSRemoteDir */
+
+
+
+
+int AdjustLocalFileName(char *local)
+{
+ char *dir;
+
+ /* See if the file exists, and if we can overwrite it. */
+ if ((access(local, 0) == 0) && (access(local, 2) < 0))
+ goto noaccess;
+
+ /*
+ * Make sure we are writing to a valid local path.
+ * First check the local directory, and see if we can write to it.
+ */
+ if (access(local, 2) < 0) {
+ dir = rindex(local, '/');
+
+ if (errno != ENOENT && errno != EACCES) {
+ /* Report an error if it's one we can't handle. */
+ PERROR("AdjustLocalFileName", local);
+ return -1;
+ }
+ /* See if we have write permission on this directory. */
+ if (dir != NULL) {
+ /* Special case: /filename. */
+ if (dir != local)
+ *dir = 0;
+ if (access(dir == local ? "/" : local, 2) < 0) {
+ /*
+ * We have a big long pathname, like /a/b/c/d,
+ * but see if we can write into the current
+ * directory and call the file ./d.
+ */
+ if (access(".", 2) < 0) {
+ (void) strcpy(local, " and .");
+ goto noaccess;
+ }
+ (void) strcpy(local, dir + 1); /* use simple filename. */
+ } else
+ *dir = '/';
+ } else {
+ /* We have a simple path name (file name only). */
+ if (access(".", 2) < 0) {
+noaccess: PERROR("AdjustLocalFileName", local);
+ return -1;
+ }
+ }
+ }
+ return (NOERR);
+} /* AdjustLocalFileName */
+
+
+
+int SetToAsciiForLS(int is_retr, int currenttype)
+{
+ int oldt = -1, oldv;
+
+ if (!is_retr) {
+ if (currenttype != TYPE_A) {
+ oldt = currenttype;
+ oldv = verbose;
+ if (!debug)
+ verbose = V_QUIET;
+ (void) setascii(0, NULL);
+ verbose = oldv;
+ }
+ }
+ return oldt;
+} /* SetToAsciiForLS */
+
+
+
+int IssueCommand(char *ftpcmd, char *remote)
+{
+ string str;
+ int result = NOERR;
+
+ if (remote)
+ (void) sprintf(str, "%s %s", ftpcmd, remote);
+ else
+ (void) Strncpy(str, ftpcmd);
+
+#ifdef TRY_NOREPLY
+ if (command_noreply(str) != PRELIM)
+#else
+ if (command(str) != PRELIM)
+#endif
+ result = -1;
+ return (result);
+} /* IssueCommand */
+
+
+
+FILE *OpenOutputFile(int filetype, char *local, char *mode, Sig_t *oldintp)
+{
+ FILE *fout;
+
+ if (filetype == IS_STREAM) {
+ fout = stdout;
+ } else if (filetype == IS_PIPE) {
+ /* If it is a pipe, the pipecmd will have a | as the first char. */
+ ++local;
+ fout = popen(local, "w");
+ *oldintp = Signal(SIGPIPE, abortrecv);
+ } else {
+ fout = fopen(local, mode);
+ }
+ if (fout == NULL)
+ PERROR("OpenOutputFile", local);
+ return (fout);
+} /* OpenOutputFile */
+
+
+
+void ReceiveBinary(FILE *din, FILE *fout, int *do_reports, char *localfn)
+{
+ int c, d, do2;
+
+ errno = 0; /* Clear any old error left around. */
+ do2 = *do_reports; /* A slight optimization :-) */
+ bytes = 0; /* Init the byte-transfer counter. */
+
+ for (;;) {
+ /* Read a block from the input stream. */
+ c = read(fileno(din), xferbuf, (int)xferbufsize);
+
+ /* If c is zero, then we've read the whole file. */
+ if (c == 0)
+ break;
+
+ /* Check for errors that may have occurred while reading. */
+ if (c < 0) {
+ /* Error occurred while reading. */
+ if (errno != EPIPE)
+ PERROR("ReceiveBinary", "netin");
+ bytes = -1;
+ break;
+ }
+
+ /* Write out the same block we just read in. */
+ d = write(fileno(fout), xferbuf, c);
+
+ /* Check for write errors. */
+ if ((d < 0) || (ferror(fout))) {
+ /* Error occurred while writing. */
+ PERROR("ReceiveBinary", "outfile");
+ break;
+ }
+ if (d < c) {
+ (void) fprintf(stderr, "%s: short write\n", localfn);
+ break;
+ }
+
+ /* Update the byte counter. */
+ bytes += (long) c;
+
+ /* Print progress indicator. */
+ if (do2 != 0)
+ do2 = progress_report(0);
+ }
+
+ *do_reports = do2; /* Update the real do_reports variable. */
+} /* ReceiveBinary */
+
+
+
+void AddRedirLine(char *str2)
+{
+ register struct lslist *new;
+
+ (void) Strncpy(indataline, str2);
+ new = (struct lslist *) malloc((size_t) sizeof(struct lslist));
+ if (new != NULL) {
+ if ((new->string = NewString(str2)) != NULL) {
+ new->next = NULL;
+ if (lshead == NULL)
+ lshead = lstail = new;
+ else {
+ lstail->next = new;
+ lstail = new;
+ }
+ }
+ }
+} /* AddRedirLine */
+
+
+
+void ReceiveAscii(FILE *din, FILE *fout, int *do_reports, char *localfn, int
+lineMode)
+{
+ string str2;
+ int nchars = 0, c;
+ char *linePtr;
+ int do2 = *do_reports, stripped;
+
+ next_report = xferbufsize;
+ bytes = errno = 0;
+ if (lineMode) {
+ while ((linePtr = FGets(str2, din)) != NULL) {
+ bytes += (long) RemoveTrailingNewline(linePtr, &stripped);
+ if (is_ls || debug > 0)
+ AddRedirLine(linePtr);
+
+ /* Shutup while getting remote size and mod time. */
+ if (!buffer_only) {
+ c = fputs(linePtr, fout);
+
+ if (c != EOF) {
+ if (stripped > 0)
+ c = fputc('\n', fout);
+ }
+ if ((c == EOF) || (ferror(fout))) {
+ PERROR("ReceiveAscii", "outfile");
+ break;
+ }
+ }
+
+ /* Print progress indicator. */
+ if (do2 && bytes > next_report)
+ do2 = progress_report(0);
+ }
+ } else while ((c = getc(din)) != EOF) {
+ linePtr = str2;
+ while (c == '\r') {
+ bytes++;
+ if ((c = getc(din)) != '\n') {
+ if (ferror(fout))
+ goto break2;
+ /* Shutup while getting remote size and mod time. */
+ if (!buffer_only)
+ (void) putc('\r', fout);
+ if (c == '\0') {
+ bytes++;
+ goto contin2;
+ }
+ if (c == EOF)
+ goto contin2;
+ }
+ }
+ /* Shutup while getting remote size and mod time. */
+ if (!buffer_only)
+ (void) putc(c, fout);
+ bytes++;
+
+ /* Print progress indicator. */
+ if (do2 && bytes > next_report)
+ do2 = progress_report(0);
+
+ /* No seg violations, please */
+ if (nchars < sizeof(str2) - 1) {
+ *linePtr++ = c; /* build redir string */
+ nchars++;
+ }
+
+ contin2:
+ /* Save the input line in the buffer for recall later. */
+ if (c == '\n' && is_ls) {
+ *--linePtr = 0;
+ AddRedirLine(str2);
+ nchars = 0;
+ }
+
+ } /* while ((c = getc(din)) != EOF) */
+break2:
+ if (ferror(din)) {
+ if (errno != EPIPE)
+ PERROR("ReceiveAscii", "netin");
+ bytes = -1;
+ }
+ if (ferror(fout)) {
+ if (errno != EPIPE)
+ PERROR("ReceiveAscii", localfn);
+ }
+ *do_reports = do2;
+} /* ReceiveAscii */
+
+
+
+void CloseOutputFile(FILE *f, int filetype, char *name, time_t mt)
+{
+ struct utimbuf ut;
+
+ if (f != NULL) {
+ (void) fflush(f);
+ if (filetype == IS_FILE) {
+ (void) fclose(f);
+#ifndef DONT_TIMESTAMP
+ if (mt != (time_t)0) {
+ ut.actime = ut.modtime = mt;
+ (void) utime(name, &ut);
+ }
+#endif /* DONT_TIMESTAMP */
+ } else if (filetype == IS_PIPE) {
+ (void)pclose(f);
+ }
+ }
+} /* close_file */
+
+
+
+void ResetOldType(int oldtype)
+{
+ int oldv;
+
+ if (oldtype >= 0) {
+ oldv = verbose;
+ if (!debug)
+ verbose = V_QUIET;
+ (void) SetTypeByNumber(oldtype);
+ verbose = oldv;
+ }
+} /* ResetOldType */
+
+
+
+int FileType(char *fname)
+{
+ int ft = IS_FILE;
+
+ if (strcmp(fname, "-") == 0)
+ ft = IS_STREAM;
+ else if (*fname == '|')
+ ft = IS_PIPE;
+ return (ft);
+} /* FileType */
+
+
+
+
+void CloseData(void) {
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+} /* CloseData */
+
+
+
+
+int recvrequest(char *cmd, char *local, char *remote, char *mode)
+{
+ FILE *fout = NULL, *din = NULL;
+ Sig_t oldintr = NULL, oldintp = NULL;
+ int oldtype = -1, is_retr;
+ int nfnd;
+ char msg;
+ struct fd_set mask;
+ int filetype, do_reports = 0;
+ string remote_dir;
+ time_t remfTime = 0;
+ int result = -1;
+
+ dbprintf("---> cmd: %s; rmt: %s; loc: %s; mode: %s.\n",
+ cmd,
+ remote == NULL ? "(null)" : remote,
+ local == NULL ? "(null)" : local,
+ mode
+ );
+
+ is_retr = strcmp(cmd, "RETR") == 0;
+
+ GetLSRemoteDir(remote, remote_dir);
+ if ((filetype = FileType(local)) == IS_FILE) {
+ if (AdjustLocalFileName(local))
+ goto xx;
+ }
+
+ file_size = -1;
+ if (filetype == IS_FILE)
+ file_size = GetDateAndSize(remote, (unsigned long *) &remfTime);
+
+ if (initconn())
+ goto xx;
+
+ oldtype = SetToAsciiForLS(is_retr, curtype);
+
+ /* Issue the NLST command but don't wait for the reply. Some FTP
+ * servers make the data connection before issuing the
+ * "150 Opening ASCII mode data connection for /bin/ls" reply.
+ */
+ if (IssueCommand(cmd, remote))
+ goto xx;
+
+ if ((fout = OpenOutputFile(filetype, local, mode, &oldintp)) == NULL)
+ goto xx;
+
+ if ((din = dataconn("r")) == NULL)
+ goto Abort;
+
+#ifdef TRY_NOREPLY
+ /* Now get the reply we skipped above. */
+ (void) getreply(0);
+#endif
+
+ do_reports = NOT_VQUIET && is_retr && filetype == IS_FILE;
+ if (do_reports)
+ do_reports = start_progress(0, local);
+
+ if (setjmp(recvabort)) {
+#ifdef TRY_ABOR
+ goto Abort;
+#else
+ /* Just read the rest of the stream without doing anything with
+ * the results.
+ */
+ (void) Signal(SIGINT, SIG_IGN);
+ (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */
+ while (read(fileno(din), xferbuf, (int)xferbufsize) > 0)
+ ;
+ (void) fprintf(stderr, "\rAborted. \n");
+#endif
+ } else {
+ oldintr = Signal(SIGINT, abortrecv);
+
+ if (curtype == TYPE_A)
+ ReceiveAscii(din, fout, &do_reports, local, 1);
+ else
+ ReceiveBinary(din, fout, &do_reports, local);
+ result = NOERR;
+ /* Don't interrupt us now, since we finished successfully. */
+ (void) Signal(SIGPIPE, SIG_IGN);
+ (void) Signal(SIGINT, SIG_IGN);
+ }
+ CloseData();
+ (void) getreply(0);
+
+ goto xx;
+
+Abort:
+
+/* Abort using RFC959 recommended IP,SYNC sequence */
+
+ (void) Signal(SIGPIPE, SIG_IGN); /* Don't bug us while aborting. */
+ (void) Signal(SIGINT, SIG_IGN);
+ if (!cpend || !cout) goto xx;
+ (void) fprintf(cout,"%c%c",IAC,IP);
+ (void) fflush(cout);
+ msg = IAC;
+/* send IAC in urgent mode instead of DM because UNIX places oob mark */
+/* after urgent byte rather than before as now is protocol */
+ if (send(fileno(cout),&msg,1,MSG_OOB) != 1)
+ PERROR("recvrequest", "abort");
+ (void) fprintf(cout,"%cABOR\r\n",DM);
+ (void) fflush(cout);
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if (din)
+ FD_SET(fileno(din), &mask);
+ if ((nfnd = empty(&mask,10)) <= 0) {
+ if (nfnd < 0)
+ PERROR("recvrequest", "abort");
+ lostpeer(0);
+ }
+ if (din && FD_ISSET(fileno(din), &mask)) {
+ while ((read(fileno(din), xferbuf, xferbufsize)) > 0)
+ ;
+ }
+ if ((getreply(0)) == ERROR && code == 552) { /* needed for nic style abort */
+ CloseData();
+ (void) getreply(0);
+ }
+ (void) getreply(0);
+ result = -1;
+ CloseData();
+
+xx:
+ CloseOutputFile(fout, filetype, local, remfTime);
+ dbprintf("outfile closed.\n");
+ if (din)
+ (void) fclose(din);
+ if (is_retr)
+ end_progress("received", local, remote);
+ if (oldintr)
+ (void) Signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) Signal(SIGPIPE, oldintp);
+ dbprintf("recvrequest result = %d.\n", result);
+ if (oldtype >= 0)
+ ResetOldType(oldtype);
+ bytes = 0L;
+ return (result);
+} /* recvrequest */
+
+
+
+
+/*
+ * Need to start a listen on the data channel
+ * before we send the command, otherwise the
+ * server's connect may fail.
+ */
+
+
+int initconn(void)
+{
+ register char *p, *a;
+ int result, len, tmpno = 0;
+ int on = 1, rval;
+ string str;
+ Sig_t oldintr;
+ char *cp;
+ int a1, a2, a3, a4, p1, p2;
+ unsigned char n[6];
+
+ oldintr = Signal(SIGINT, SIG_IGN);
+
+ if (using_pasv) {
+ result = command("PASV");
+ if (result != COMPLETE) {
+ printf("Passive mode refused.\n");
+ using_pasv = 0;
+ goto TryPort;
+ }
+
+ /*
+ * What we've got here is a string of comma separated one-byte
+ * unsigned integer values. The first four are the IP address,
+ * the fifth is the MSB of the port address, and the sixth is the
+ * LSB of the port address. Extract this data and prepare a
+ * 'data_addr' (struct sockaddr_in).
+ */
+ for (cp = reply_string + 4; *cp != '\0'; cp++)
+ if (isdigit(*cp))
+ break;
+
+ if (sscanf(cp, "%d,%d,%d,%d,%d,%d",
+ &a1, &a2, &a3, &a4, &p1, &p2) != 6) {
+ printf("Cannot parse PASV response: %s\n", reply_string);
+ using_pasv = 0;
+ goto TryPort;
+ }
+
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ PERROR("initconn", "socket");
+ rval = 1;
+ goto Return;
+ }
+#ifdef LINGER /* If puts don't complete, you could try this. */
+ {
+ struct linger li;
+ li.l_onoff = 1;
+ li.l_linger = 900;
+
+ if (setsockopt(data, SOL_SOCKET, SO_LINGER,
+ (char *)&li, sizeof(struct linger)) < 0)
+ {
+ PERROR("initconn", "setsockopt(SO_LINGER)");
+ }
+ }
+#endif /* LINGER */
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)) < 0 ) {
+ PERROR("initconn", "setscokopt (ignored)");
+ }
+
+ n[0] = (unsigned char) a1;
+ n[1] = (unsigned char) a2;
+ n[2] = (unsigned char) a3;
+ n[3] = (unsigned char) a4;
+ n[4] = (unsigned char) p1;
+ n[5] = (unsigned char) p2;
+
+ data_addr.sin_family = AF_INET;
+ bcopy( (void *)&n[0], (void *)&data_addr.sin_addr, 4 );
+ bcopy( (void *)&n[4], (void *)&data_addr.sin_port, 2 );
+
+#ifdef SOCKS
+ if (Rconnect( data, (struct sockaddr *) &data_addr, (int) sizeof(data_addr) ) < 0 ) {
+#else
+ if (Connect( data, &data_addr, sizeof(data_addr) ) < 0 ) {
+#endif
+ if (errno == ECONNREFUSED) {
+ dbprintf("Could not connect to port specified by server;\n");
+ dbprintf("Falling back to PORT mode.\n");
+ close(data);
+ data = -1;
+ using_pasv = 0;
+ goto TryPort;
+ }
+ PERROR("initconn", "connect");
+ rval = 1;
+ goto Return;
+ }
+ rval = 0;
+ goto Return;
+ }
+
+TryPort:
+ rval = 0;
+
+noport:
+ data_addr = myctladdr;
+ if (sendport)
+ data_addr.sin_port = 0; /* let system pick one */
+ if (data != -1)
+ (void) close (data);
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ PERROR("initconn", "socket");
+ if (tmpno)
+ sendport = 1;
+ rval = 1; goto Return;
+ }
+
+ if (!sendport)
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
+ PERROR("initconn", "setsockopt (reuse address)");
+ goto bad;
+ }
+
+#ifdef SOCKS
+ if (Rbind(data, (struct sockaddr *)&data_addr, sizeof (data_addr), hisctladdr.sin_addr.s_addr) < 0) {
+#else
+ if (Bind(data, &data_addr, sizeof (data_addr)) < 0) {
+#endif
+ PERROR("initconn", "bind");
+ goto bad;
+ }
+#ifdef LINGER /* If puts don't complete, you could try this. */
+ {
+ struct linger li;
+ li.l_onoff = 1;
+ li.l_linger = 900;
+
+ if (setsockopt(data, SOL_SOCKET, SO_LINGER,
+ (char *)&li, sizeof(struct linger)) < 0)
+ {
+ PERROR("initconn", "setsockopt(SO_LINGER)");
+ }
+ }
+#endif /* LINGER */
+
+#ifdef IPTOS_THROUGHPUT /* transfers are background */
+#ifdef IP_TOS
+ {
+ int nType = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS,
+ (char *) &nType, sizeof(nType)) < 0) {
+ PERROR("initconn", "setsockopt(IP_TOS)");
+ }
+ }
+#endif
+#endif
+
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+ PERROR("initconn", "setsockopt (ignored)");
+ len = sizeof (data_addr);
+ if (Getsockname(data, (char *)&data_addr, &len) < 0) {
+ PERROR("initconn", "getsockname");
+ goto bad;
+ }
+
+#ifdef SOCKS
+ if (Rlisten(data, 1) < 0)
+#else
+ if (listen(data, 1) < 0)
+#endif
+ PERROR("initconn", "listen");
+ if (sendport) {
+ a = (char *)&data_addr.sin_addr;
+ p = (char *)&data_addr.sin_port;
+#define UC(x) (int) (((int) x) & 0xff)
+ (void) sprintf(str, "PORT %d,%d,%d,%d,%d,%d",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ result = command(str);
+ if (result == ERROR && sendport == -1) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ rval = (result != COMPLETE); goto Return;
+ }
+ if (tmpno)
+ sendport = 1;
+ rval = 0; goto Return;
+bad:
+ (void) close(data), data = -1;
+ if (tmpno)
+ sendport = 1;
+ rval = 1;
+Return:
+ (void) Signal(SIGINT, oldintr);
+ return (rval);
+} /* initconn */
+
+
+
+
+FILE *
+dataconn(char *mode)
+{
+ struct sockaddr_in from;
+ FILE *fp;
+ int s, fromlen = sizeof (from);
+
+ if (using_pasv)
+ return( fdopen( data, mode ));
+#ifdef SOCKS
+ s = Raccept(data, (struct sockaddr *) &from, &fromlen);
+#else
+ s = Accept(data, &from, &fromlen);
+#endif
+ if (s < 0) {
+ PERROR("dataconn", "accept");
+ (void) close(data), data = -1;
+ fp = NULL;
+ } else {
+ (void) close(data);
+ data = s;
+ fp = safeopen(data, mode);
+ }
+ return (fp);
+} /* dataconn */
+
+/* eof ftp.c */
diff --git a/usr.bin/ncftp/ftp.h b/usr.bin/ncftp/ftp.h
new file mode 100644
index 0000000..d531723
--- /dev/null
+++ b/usr.bin/ncftp/ftp.h
@@ -0,0 +1,67 @@
+/* ftp.h */
+
+#ifndef _ftp_h_
+#define _ftp_h_
+
+/* $RCSfile: ftp.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/07/09 11:04:12 $
+ */
+
+#define IS_FILE 1
+#define IS_STREAM 0
+#define IS_PIPE -1
+
+/* Progress-meter types. */
+#define pr_none 0
+#define pr_percent 1
+#define pr_philbar 2
+#define pr_kbytes 3
+#define pr_dots 4
+#define pr_last pr_dots
+
+/* Values sent to CommandWithFlags() to determine whether to read a reply
+ * from the remote host after sending the command.
+ */
+#define DONT_WAIT_FOR_REPLY 0
+#define WAIT_FOR_REPLY 1
+
+/* Expect EOF values for getreply() */
+#define DONT_EXPECT_EOF 0
+#define EXPECT_EOF 1
+
+int hookup(char *, unsigned int);
+int Login(char *userNamePtr, char *passWordPtr, char *accountPtr, int doInit);
+void cmdabort SIG_PARAMS;
+int CommandWithFlags(char *, int);
+int command(char *);
+int command_noreply(char *);
+int quiet_command(char *);
+int verbose_command(char *);
+int getreply(int);
+int start_progress(int, char *);
+int progress_report(int);
+void end_progress(char *, char *, char *);
+void close_file(FILE **, int);
+void abortsend SIG_PARAMS;
+int sendrequest(char *, char *, char *);
+void abortrecv SIG_PARAMS;
+void GetLSRemoteDir(char *, char *);
+int AdjustLocalFileName(char *);
+int SetToAsciiForLS(int, int);
+int IssueCommand(char *, char *);
+FILE *OpenOutputFile(int, char *, char *, Sig_t *);
+void ReceiveBinary(FILE *, FILE *, int *, char *);
+void AddRedirLine(char *);
+void ReceiveAscii(FILE *, FILE *, int *, char *, int);
+void CloseOutputFile(FILE *, int, char *, time_t);
+void ResetOldType(int);
+int FileType(char *);
+void CloseData(void);
+int recvrequest(char *, char *, char *, char *);
+int initconn(void);
+FILE *dataconn(char *);
+
+#endif /* _ftp_h_ */
+
+/* eof ftp.h */
diff --git a/usr.bin/ncftp/ftprc.c b/usr.bin/ncftp/ftprc.c
new file mode 100644
index 0000000..3fde1fd
--- /dev/null
+++ b/usr.bin/ncftp/ftprc.c
@@ -0,0 +1,615 @@
+/* ftprc.c */
+
+/* $RCSfile: ftprc.c,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/07/09 10:58:37 $
+ */
+
+#include "sys.h"
+
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <signal.h>
+
+#include "util.h"
+#include "ftprc.h"
+#include "main.h"
+#include "cmds.h"
+#include "set.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* ftprc.c global variables */
+siteptr firstsite = NULL, lastsite = NULL;
+recentsite recents[dMAXRECENTS];
+int nRecents = 0;
+int nSites = 0;
+int keep_recent = dRECENT_ON;
+longstring rcname;
+longstring recent_file;
+int parsing_rc = 0;
+
+extern char *line, *margv[];
+extern int margc, fromatty;
+extern string anon_password; /* most likely your email address */
+extern string pager;
+extern struct userinfo uinfo;
+
+int thrash_rc(void)
+{
+ struct stat st;
+ string word, str;
+ longstring cwd;
+ char *cp, *dp, *rc;
+ FILE *fp;
+ int i;
+
+ (void) get_cwd(cwd, sizeof(cwd));
+ if (cwd[strlen(cwd) - 1] != '/')
+ (void) Strncat(cwd, "/");
+
+ /* Because some versions of regular ftp complain about ncftp's
+ * #set commands, FTPRC takes precedence over NETRC.
+ */
+ cp = getenv("DOTDIR");
+ for (i=0; i<2; i++) {
+ rc = (i == 0) ? FTPRC : NETRC;
+
+ (void) sprintf(rcname, "%s%s", cwd, rc);
+ if (stat(rcname, &st) == 0)
+ goto foundrc;
+
+ (void) sprintf(rcname, "%s.%s", cwd, rc);
+ if (stat(rcname, &st) == 0)
+ goto foundrc;
+
+ if (cp != NULL) {
+ (void) sprintf(rcname, "%s/.%s", cp, rc);
+ if (stat(rcname, &st) == 0)
+ goto foundrc;
+ }
+
+ (void) sprintf(rcname, "%s/.%s", uinfo.homedir, rc);
+ if (stat(rcname, &st) == 0)
+ goto foundrc;
+ }
+
+ return (0); /* it's OK not to have an rc. */
+
+foundrc:
+ if ((st.st_mode & 077) != 0) /* rc must be unreadable by others. */
+ (void) chmod(rcname, 0600);
+
+ if ((fp = fopen(rcname, "r")) == NULL) {
+ PERROR("thrash_rc", rcname);
+ return -1;
+ }
+
+ parsing_rc = 1;
+ while ((cp = FGets(str, fp)) != 0) {
+ while (isspace(*cp)) ++cp; /* skip leading space. */
+ if (*cp == '#') {
+ if ((strncmp("set", ++cp, (size_t)3) == 0) || (strncmp("unset", cp, (size_t)5) == 0)) {
+ (void) strcpy(line, cp);
+ makeargv();
+ (void) set(margc, margv);
+ /* setting or unsetting a variable. */
+ } /* else a comment. */
+ } else {
+ if (strncmp(cp, "machine", (size_t) 7) == 0) {
+ /* We have a new machine record. */
+ cp += 7;
+ while (isspace(*cp)) ++cp; /* skip delimiting space. */
+ dp = word;
+ while (*cp && !isspace(*cp)) *dp++ = *cp++; /* copy the name. */
+ *dp = 0;
+ AddNewSitePtr(word);
+ }
+ }
+ }
+ (void) fclose(fp);
+ parsing_rc = 0;
+ return 1;
+} /* thrash_rc */
+
+
+
+
+void AddNewSitePtr(char *word)
+{
+ siteptr s;
+
+ if ((s = (siteptr) malloc(sizeof(site))) != 0) {
+ s->next = NULL;
+ if ((s->name = malloc(strlen(word) + 1)) != 0) {
+ (void) strcpy(s->name, word);
+ if (firstsite == NULL)
+ firstsite = lastsite = s;
+ else {
+ lastsite->next = s;
+ lastsite = s;
+ }
+ ++nSites;
+ } else {
+ Free(s);
+ }
+ }
+} /* AddNewSitePtr */
+
+
+
+
+static int RecentCmp(recentsite *a, recentsite *b)
+{
+ int i = 1;
+
+ if (a->lastcall > b->lastcall)
+ i = -1;
+ else if (a->lastcall == b->lastcall)
+ i = 0;
+ return i;
+} /* RecentCmp */
+
+
+
+
+static siteptr FindNetrcSite(char *host, int exact)
+{
+ register siteptr s, s2;
+ string str, host2;
+
+ (void) Strncpy(host2, host);
+ StrLCase(host2);
+
+ /* see if 'host' is in our list of favorite sites (in NETRC). */
+ for (s = firstsite; s != NULL; s2=s->next, s=s2) {
+ (void) Strncpy(str, s->name);
+ StrLCase(str);
+ if (exact) {
+ if (strcmp(str, host2) == 0)
+ return s;
+ } else {
+ if (strstr(str, host2) != NULL)
+ return s;
+ }
+ }
+ return NULL;
+} /* FindNetrcSite */
+
+
+
+
+static recentsite *FindRecentSite(char *host, int exact)
+{
+ register recentsite *r;
+ register int i;
+ string str, host2;
+
+ (void) Strncpy(host2, host);
+ StrLCase(host2);
+
+ /* see if 'host' is in our list of favorite sites (in recent-log). */
+ for (i=0; i<nRecents; i++) {
+ r = &recents[i];
+ (void) Strncpy(str, r->name);
+ StrLCase(str);
+ if (exact) {
+ if (strcmp(str, host2) == 0)
+ return r;
+ } else {
+ if (strstr(str, host2) != NULL)
+ return r;
+ }
+ }
+ return NULL;
+} /* FindRecentSite */
+
+
+
+
+void ReadRecentSitesFile(void)
+{
+ FILE *rfp;
+ recentsite *r;
+ char name[64];
+ int offset;
+ longstring str;
+
+ nRecents = 0;
+ if (recent_file[0] != 0 && keep_recent) {
+ rfp = fopen(recent_file, "r");
+ if (rfp != NULL) {
+ for (; nRecents < dMAXRECENTS; ) {
+ r = &recents[nRecents];
+ if (FGets(str, rfp) == NULL)
+ break;
+ (void) RemoveTrailingNewline(str, NULL);
+ name[0] = 0;
+ offset = 45;
+ if (sscanf(str, "%s %lu %n",
+ name,
+ (unsigned long *) &r->lastcall,
+ &offset) >= 2)
+ {
+ if ((r->name = NewString(name)) != NULL) {
+ r->dir = NewString(str + offset);
+ if (r->dir != NULL)
+ nRecents++;
+ else {
+ free(r->name);
+ r->name = r->dir = NULL;
+ }
+ }
+ }
+ }
+ (void) fclose(rfp);
+ }
+ }
+} /* ReadRecentSitesFile */
+
+
+
+static void SortRecentList(void)
+{
+ QSort(recents, nRecents, sizeof(recentsite), RecentCmp);
+} /* SortRecentList */
+
+
+
+
+int WriteRecentSitesFile(void)
+{
+ FILE *rfp;
+ recentsite *r;
+ int i;
+ int retcode = 0;
+
+ if ((recent_file[0] != 0) && (nRecents > 0) && (keep_recent)) {
+ dbprintf("Attempting to write %s...\n", recent_file);
+ rfp = fopen(recent_file, "w");
+ if (rfp != NULL) {
+ SortRecentList();
+ for (i=0; i<nRecents; i++) {
+ r = &recents[i];
+ (void) fprintf(rfp, "%-32s %11lu %s\n", r->name,
+ (unsigned long) r->lastcall, r->dir);
+ }
+ (void) fclose(rfp);
+ dbprintf("%s written successfully.\n", recent_file);
+ (void) chmod(recent_file, 0600);
+ } else {
+ perror(recent_file);
+ ++retcode;
+ }
+ }
+ return retcode;
+} /* WriteRecentSitesFile */
+
+
+
+
+void AddRecentSite(char *host, char *lastdir)
+{
+ char *nhost, *ndir;
+ recentsite *r;
+
+ if (keep_recent) {
+ nhost = NewString(host);
+ /* Use '/' to denote that the current directory wasn't known,
+ * because we won't try to cd to the root directory.
+ */
+ ndir = NewString(*lastdir ? lastdir : "/");
+
+ /* Don't bother if we don't have the memory, or if it is already
+ * in our NETRC.
+ */
+ if ((ndir != NULL) && (nhost != NULL) &&
+ (FindNetrcSite(host, 1) == NULL)) {
+ if (nRecents == dMAXRECENTS) {
+ SortRecentList();
+ r = &recents[dMAXRECENTS - 1];
+ if (r->name != NULL)
+ free(r->name);
+ if (r->dir != NULL)
+ free(r->dir);
+ } else {
+ r = &recents[nRecents];
+ nRecents++;
+ }
+ r->name = nhost;
+ r->dir = ndir;
+ (void) time(&r->lastcall);
+ SortRecentList();
+ }
+ }
+} /* AddRecentSite */
+
+
+
+
+/*
+ * After you are done with a site (by closing it or quitting), we
+ * need to update the list of recent sites called.
+ */
+void UpdateRecentSitesList(char *host, char *lastdir)
+{
+ recentsite *r;
+ char *ndir;
+
+ if (keep_recent) {
+ r = FindRecentSite(host, 1);
+ if (r == NULL)
+ AddRecentSite(host, lastdir);
+ else {
+ /* Update the last time connected, and the directory we left in. */
+ if ((ndir = NewString(*lastdir ? lastdir : "/")) != NULL) {
+ free(r->dir);
+ r->dir = ndir;
+ }
+ (void) time(&r->lastcall);
+ }
+ }
+} /* UpdateRecentSitesList */
+
+
+
+/*
+ * Prints out the number of sites we know about, so the user can figure out
+ * an abbreviation or type it's number to open (setpeer).
+ */
+void PrintSiteList(void)
+{
+ int i, j;
+ siteptr s, s2;
+ FILE *pagerfp;
+ Sig_t sigpipe;
+
+ if (fromatty) {
+ j = 0;
+ i = 1;
+ sigpipe = Signal(SIGPIPE, SIG_IGN);
+ if ((pagerfp = popen(pager + 1, "w")) == NULL)
+ pagerfp = stdout;
+ if (nRecents > 0) {
+ j++;
+ (void) fprintf(pagerfp, "\nRecently called sites:\n");
+ for (; i<=nRecents; i++) {
+ (void) fprintf(pagerfp, "%4d. %-32s", i, recents[i-1].name);
+ i++;
+ if (i <= nRecents) {
+ (void) fprintf(pagerfp, "%5d. %-32s", i, recents[i-1].name);
+ } else {
+ (void) fprintf(pagerfp, "\n");
+ break;
+ }
+ (void) fprintf(pagerfp, "\n");
+ }
+ }
+ if (nSites > 0) {
+ j++;
+ (void) fprintf(pagerfp, "Sites in your netrc (%s):\n", rcname);
+ for (s = firstsite; s != NULL; s2=s->next, s=s2, ++i) {
+ (void) fprintf(pagerfp, "%4d. %-32s", i, s->name);
+ s2=s->next;
+ s=s2;
+ i++;
+ if (s != NULL) {
+ (void) fprintf(pagerfp, "%5d. %-32s", i, s->name);
+ } else {
+ (void) fprintf(pagerfp, "\n");
+ break;
+ }
+ (void) fprintf(pagerfp, "\n");
+ }
+ }
+ if (j > 0) {
+ (void) fprintf(pagerfp, "\
+Note that you can specify an abbreviation of any name, or #x, where x is the\n\
+number of the site you want to connect to.\n\n");
+ }
+
+ if (pagerfp != stdout)
+ (void) pclose(pagerfp);
+ Signal(SIGPIPE, sigpipe);
+ }
+} /* PrintRecentSiteList */
+
+
+
+
+/*
+ * Given a sitename, check to see if the name was really an abbreviation
+ * of a site in the NETRC, or a site in our list of recently connected
+ * sites. Also check if the name was in the format #x, where x which
+ * would mean to use recents[x].name as the site; if x was greater than
+ * the number of sites in the recent list, then index into the netrc
+ * site list.
+ */
+#include <netdb.h>
+void GetFullSiteName(char *host, char *lastdir)
+{
+ register siteptr s, s2;
+ register recentsite *r;
+ char *ndir, *nhost, *cp;
+ int x, i, isAllDigits, exact;
+ struct hostent *hostentp;
+
+ ndir = nhost = NULL;
+ x = exact = 0;
+
+ /* First, see if the "abbreviation" is really just the name of
+ * a machine in the local domain, or if it was a full hostname
+ * already. That way we can avoid problems associated with
+ * short names, such as having "ce" as a machine in the local
+ * domain, but having "faces.unl.edu", where we would most likely
+ * want to use ce instead of faces if the user said "open ce".
+ * This will also prevent problems when you have a host named
+ * xx.yy.unl.edu, and another host named yy.unl.edu. If the user
+ * said "open yy.unl.edu" we should use yy.unl.edu, and not look
+ * for matches containing yy.unl.edu.
+ */
+ if ((hostentp = gethostbyname(host)) != NULL) {
+ strcpy(host, hostentp->h_name);
+ exact = 1;
+ }
+
+ /* Don't allow just numbers as abbreviations; "open 2" could be
+ * confused between site numbers in the open 'menu,' like
+ * "2. unlinfo.unl.edu" and IP numbers "128.93.2.1" or even numbers
+ * in the site name like "simtel20.army.mil."
+ */
+
+ for (isAllDigits = 1, cp = host; *cp != 0; cp++) {
+ if (!isdigit(*cp)) {
+ isAllDigits = 0;
+ break;
+ }
+ }
+
+ if (!isAllDigits) {
+ if (host[0] == '#')
+ (void) sscanf(host + 1, "%d", &x);
+ /* Try matching the abbreviation, since it isn't just a number. */
+ /* see if 'host' is in our list of favorite sites (in NETRC). */
+
+ if (x == 0) {
+ if ((s = FindNetrcSite(host, exact)) != NULL) {
+ nhost = s->name;
+ } else if ((r = FindRecentSite(host, exact)) != NULL) {
+ nhost = r->name;
+ ndir = r->dir;
+ }
+ }
+ } else if (sscanf(host, "%d", &x) != 1) {
+ x = 0;
+ }
+
+ if (--x >= 0) {
+ if (x < nRecents) {
+ nhost = recents[x].name;
+ ndir = recents[x].dir;
+ } else {
+ x -= nRecents;
+ if (x < nSites) {
+ for (i = 0, s = firstsite; i < x; s2=s->next, s=s2)
+ ++i;
+ nhost = s->name;
+ }
+ }
+ }
+
+ if (nhost != NULL) {
+ (void) strcpy(host, nhost);
+ if (lastdir != NULL) {
+ *lastdir = 0;
+ /* Don't cd if the dir is the root directory. */
+ if (ndir != NULL && (strcmp("/", ndir) != 0))
+ (void) strcpy(lastdir, ndir);
+ }
+ }
+} /* GetFullSiteName */
+
+
+
+
+int ruserpass2(char *host, char **username, char **pass, char **acct)
+{
+ FILE *fp;
+ char *cp, *dp, *dst, *ep;
+ str32 macname;
+ char *varname;
+ int site_found;
+ string str;
+ static string auser;
+ static str32 apass, aacct;
+
+ site_found = 0;
+
+ if ((fp = fopen(rcname, "r")) != NULL) {
+ parsing_rc = 1;
+ while (FGets(str, fp)) {
+ if ((cp = strstr(str, "machine")) != 0) {
+ /* Look for the machine token. */
+ cp += 7;
+ while (isspace(*cp))
+ cp++;
+ } else
+ continue;
+ if (strncmp(host, cp, strlen(host)) == 0) {
+ site_found = 1;
+ while (!isspace(*cp))
+ ++cp; /* skip the site name. */
+ do {
+ /* Skip any comments ahead of time. */
+ for (dp=cp; *dp; dp++) {
+ if (*dp == '#') {
+ *dp = 0;
+ break;
+ }
+ }
+
+ ep = cp;
+ while (1) {
+ varname = strtok(ep, RC_DELIM);
+ if (!varname) break;
+ dst = ep = NULL;
+ switch (*varname) {
+ case 'u': /* user */
+ *username = dst = auser;
+ break;
+ case 'l': /* login */
+ *username = dst = auser;
+ break;
+ case 'p': /* password */
+ *pass = dst = apass;
+ break;
+ case 'a': /* account */
+ *acct = dst = aacct;
+ break;
+ /* case 'd': /o default */
+ /* unused -- use 'set anon_password.' */
+ case 'm': /* macdef or machine */
+ if (strcmp(varname, "macdef"))
+ goto done; /* new machine record... */
+ dst = macname;
+ break;
+ default:
+ (void) fprintf(stderr, "Unknown .netrc keyword \"%s\"\n",
+ varname
+ );
+ }
+ if (dst) {
+ dp = strtok(ep, RC_DELIM);
+ if (dp)
+ (void) strcpy(dst, dp);
+ if (dst == macname) {
+ /*
+ * Read in the lines of the macro.
+ * The macro's end is denoted by
+ * a blank line.
+ */
+ (void) make_macro(macname, fp);
+ goto nextline;
+ }
+ }
+ }
+nextline: ;
+ } while ((cp = FGets(str, fp)) != 0);
+ break;
+ } /* end if we found the machine record. */
+ }
+done:
+ parsing_rc = 0;
+ (void) fclose(fp);
+ }
+
+ if (!site_found) {
+ /* didn't find it in the rc. */
+ return (0);
+ }
+
+ return (1); /* found */
+} /* ruserpass2 */
+
+/* eof ftprc.c */
diff --git a/usr.bin/ncftp/ftprc.h b/usr.bin/ncftp/ftprc.h
new file mode 100644
index 0000000..eed0217
--- /dev/null
+++ b/usr.bin/ncftp/ftprc.h
@@ -0,0 +1,39 @@
+/* ftprc.h */
+
+#ifndef _ftprc_h_
+#define _ftprc_h_
+
+/* $RCSfile: ftprc.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:45:31 $
+ */
+
+#define NETRC "netrc"
+#define FTPRC "ncftprc"
+
+#define RC_DELIM " \n\t,"
+
+typedef struct site *siteptr;
+typedef struct site {
+ char *name; /* name (or IP address) of site */
+ siteptr next;
+} site;
+
+typedef struct recentsite {
+ char *name; /* name (or IP address) of site */
+ char *dir; /* directory we were in last time we called. */
+ time_t lastcall; /* when this site was called last. */
+} recentsite;
+
+int thrash_rc(void);
+void AddNewSitePtr(char *word);
+int ruserpass2(char *host, char **user, char **pass, char **acct);
+void GetFullSiteName(char *host, char *lastdir);
+void ReadRecentSitesFile(void);
+int WriteRecentSitesFile(void);
+void AddRecentSite(char *host, char *lastdir);
+void UpdateRecentSitesList(char *host, char *lastdir);
+void PrintSiteList(void);
+
+#endif
+/* eof */
diff --git a/usr.bin/ncftp/getpass.c b/usr.bin/ncftp/getpass.c
new file mode 100644
index 0000000..01e203d
--- /dev/null
+++ b/usr.bin/ncftp/getpass.c
@@ -0,0 +1,160 @@
+/* Getpass.c */
+
+/* $RCSfile: getpass.c,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:44:36 $
+ */
+
+#include "sys.h"
+
+#include <signal.h>
+
+#include "util.h"
+#include "cmds.h"
+#include "getpass.h"
+#include "copyright.h"
+
+#ifndef GETPASS
+
+#ifndef sun /* ...both unnecessary, and conflicting with <termios.h> */
+#include <sys/ioctl.h>
+#endif
+
+#ifdef TERMIOS
+# include <termios.h>
+#else
+# ifdef SGTTYB
+# include <sgtty.h>
+# else
+# include <termio.h>
+# endif
+#endif /* !TERMIOS */
+
+#ifdef STRICT_PROTOS
+int ioctl(int, int, ...);
+#endif
+
+#endif /* GETPASS */
+
+
+
+
+void Echo(FILE *fp, int on)
+{
+#ifndef GETPASS /* Otherwise just do nothing which is ok. */
+
+#ifdef TERMIOS
+ static struct termios orig, noecho, *tp;
+#else
+# ifdef SGTTYB
+ static struct sgttyb orig, noecho, *tp;
+# else
+ static struct termio orig, noecho, *tp;
+# endif
+#endif
+ static int state = 0;
+ int fd = fileno(fp);
+
+ if (!isatty(fd))
+ return;
+
+ if (state == 0) {
+#ifdef TERMIOS
+ if (tcgetattr(fd, &orig) < 0)
+ PERROR("echo", "tcgetattr");
+ noecho = orig;
+ noecho.c_lflag &= ~ECHO;
+#else
+# ifdef SGTTYB
+ if (ioctl(fd, TIOCGETP, &orig) < 0)
+ PERROR("echo", "ioctl");
+ noecho = orig;
+ noecho.sg_flags &= ~ECHO;
+# else
+ if (ioctl(fd, TCGETA, &orig) < 0)
+ PERROR("echo", "ioctl");
+ noecho = orig;
+ noecho.c_lflag &= ~ECHO;
+# endif
+#endif
+ state = 1;
+ }
+ tp = NULL;
+ if (on && state == 2) {
+ /* Turn echo back on. */
+ tp = &orig;
+ state = 1;
+ } else if (!on && state == 1) {
+ /* Turn echo off. */
+ tp = &noecho;
+ state = 2;
+ }
+ if (tp != NULL) {
+#ifdef TERMIOS
+ if (tcsetattr(fd, TCSANOW, tp) < 0)
+ PERROR("echo", "tcsetattr");
+#else
+# ifdef SGTTYB
+ if (ioctl(fd, TIOCSETP, tp) < 0)
+ PERROR("echo", "ioctl");
+# else
+ if (ioctl(fd, TCSETA, tp) < 0)
+ PERROR("echo", "ioctl");
+# endif
+#endif /* !TERMIOS */
+ }
+
+#endif /* GETPASS */
+} /* Echo */
+
+
+
+#ifndef GETPASS
+
+char *Getpass(char *promptstr)
+{
+ register int ch;
+ register char *p;
+ FILE *fp, *outfp;
+ Sig_t oldintr;
+ static char buf[kMaxPassLen + 1];
+
+ /*
+ * read and write to /dev/tty if possible; else read from
+ * stdin and write to stderr.
+ */
+#if !defined(BOTCHED_FOPEN_RW)
+ if ((outfp = fp = fopen("/dev/tty", "w+")) == NULL) {
+ outfp = stderr;
+ fp = stdin;
+ }
+#else
+ /* SCO 32v2 botches "w+" open */
+ if ((fp = fopen("/dev/tty", "r")) == NULL)
+ fp = stdin;
+ if ((outfp = fopen("/dev/tty", "w")) == NULL)
+ outfp = stderr;
+#endif
+ oldintr = Signal(SIGINT, SIG_IGN);
+ Echo(fp, 0); /* Turn echoing off. */
+ (void) fputs(promptstr, outfp);
+ (void) rewind(outfp); /* implied flush */
+ for (p = buf; (ch = getc(fp)) != EOF && ch != '\n';)
+ if (p < buf + kMaxPassLen)
+ *p++ = ch;
+ *p = '\0';
+ (void)write(fileno(outfp), "\n", 1);
+ Echo(fp, 1);
+ (void) Signal(SIGINT, oldintr);
+ if (fp != stdin)
+ (void)fclose(fp);
+#if defined(BOTCHED_FOPEN_RW)
+ if (outfp != stderr)
+ (void)fclose(outfp);
+#endif
+ return(buf);
+} /* Getpass */
+
+#endif /* GETPASS */
+
+/* eof Getpass.c */
diff --git a/usr.bin/ncftp/getpass.h b/usr.bin/ncftp/getpass.h
new file mode 100644
index 0000000..c8d358c
--- /dev/null
+++ b/usr.bin/ncftp/getpass.h
@@ -0,0 +1,23 @@
+/* Getpass.h */
+
+#ifndef _getpass_h_
+#define _getpass_h_
+
+/* $RCSfile: getpass.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:45:36 $
+ */
+
+#define kMaxPassLen 127
+
+#ifdef GETPASS
+extern char *getpass(); /* Use the system supplied getpass. */
+#else
+char *Getpass(char *prompt);
+#endif
+
+void Echo(FILE *fp, int on);
+
+#endif /* _getpass_h_ */
+
+/* eof Getpass.h */
diff --git a/usr.bin/ncftp/glob.c b/usr.bin/ncftp/glob.c
new file mode 100644
index 0000000..b0f31d1
--- /dev/null
+++ b/usr.bin/ncftp/glob.c
@@ -0,0 +1,655 @@
+/* glob.c */
+
+/* $RCSfile: glob.c,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:44:32 $
+ */
+
+#include "sys.h"
+
+#include <sys/stat.h>
+
+/* Dir.h. Try <sys/dir.h> (add -DSYSDIRH) if <dirent.h> doesn't exist. */
+
+#ifndef SYSDIRH
+# include <dirent.h>
+#else
+# include <sys/dir.h>
+#endif
+
+#ifdef SCO324
+# define direct dirent
+#endif
+
+#include <errno.h>
+#include <pwd.h>
+#include "util.h"
+#include "glob.h"
+#include "cmds.h"
+#include "copyright.h"
+
+#ifndef NCARGS
+# define NCARGS 4096 /* # characters in exec arglist */
+#endif
+
+#define L_CURLY '{'
+#define R_CURLY '}'
+
+#define QUOTE 0200
+#define TRIM 0177
+#define eq(a,b) (strcmp(a, b)==0)
+#define GAVSIZ (NCARGS/6)
+#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
+
+static void ginit(char **agargv);
+static void collect(char *as);
+static void acollect(char *as);
+static void sort(void);
+static void expand(char *as);
+static void matchdir(char *pattern);
+static int execbrc(char *p, char *s);
+static match(char *s, char *p);
+static amatch(char *s, char *p);
+#if UNUSED
+static Gmatch(char *s, char *p);
+#endif
+static void Gcat(char *s1, char *s2);
+static void addpath(char c);
+static void rscan(char **t, int (*f )(char));
+static tglob(char c);
+static char *strspl(char *cp, char *dp);
+static char *strend(char *cp);
+
+static char **gargv; /* Pointer to the (stack) arglist */
+static int gargc; /* Number args in gargv */
+static int gnleft;
+static short gflag;
+char *globerr;
+char *home; /* you must initialize this elsewhere! */
+extern int errno;
+
+static int globcnt;
+
+char *globchars = "`{[*?";
+
+static char *gpath, *gpathp, *lastgpathp;
+static int globbed;
+static char *entp;
+static char **sortbas;
+
+char **
+glob(char *v)
+{
+ char agpath[BUFSIZ];
+ char *agargv[GAVSIZ];
+ char *vv[2];
+ vv[0] = v;
+ vv[1] = 0;
+ gflag = (short) 0;
+ rscan(vv, tglob);
+ if (gflag == (short) 0)
+ return (copyblk(vv));
+
+ globerr = 0;
+ gpath = agpath; gpathp = gpath; *gpathp = 0;
+ lastgpathp = &gpath[sizeof agpath - 2];
+ ginit(agargv); globcnt = 0;
+ collect(v);
+ if (globcnt == 0 && (gflag & (short)1)) {
+ blkfree(gargv), gargv = 0;
+ return (0);
+ } else
+ return (gargv = copyblk(gargv));
+}
+
+static
+void ginit(char **agargv)
+{
+ agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
+ gnleft = NCARGS - 4;
+}
+
+static
+void collect(char *as)
+{
+ if (eq(as, "{") || eq(as, "{}")) {
+ Gcat(as, "");
+ sort();
+ } else
+ acollect(as);
+}
+
+static
+void acollect(char *as)
+{
+ register int ogargc = gargc;
+
+ gpathp = gpath; *gpathp = 0; globbed = 0;
+ expand(as);
+ if (gargc != ogargc)
+ sort();
+}
+
+static
+void sort(void)
+{
+ register char **p1, **p2, *c;
+ char **Gvp = &gargv[gargc];
+
+ p1 = sortbas;
+ while (p1 < Gvp-1) {
+ p2 = p1;
+ while (++p2 < Gvp)
+ if (strcmp(*p1, *p2) > 0)
+ c = *p1, *p1 = *p2, *p2 = c;
+ p1++;
+ }
+ sortbas = Gvp;
+}
+
+static
+void expand(char *as)
+{
+ register char *cs;
+ register char *sgpathp, *oldcs;
+ struct stat stb;
+
+ sgpathp = gpathp;
+ cs = as;
+ if (*cs == '~' && gpathp == gpath) {
+ addpath('~');
+ for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
+ addpath(*cs++);
+ if (!*cs || *cs == '/') {
+ if (gpathp != gpath + 1) {
+ *gpathp = 0;
+ if (gethdir(gpath + 1))
+ globerr = "Unknown user name after ~";
+ (void) strcpy(gpath, gpath + 1);
+ } else
+ (void) strcpy(gpath, home);
+ gpathp = strend(gpath);
+ }
+ }
+ while (!any(*cs, globchars)) {
+ if (*cs == 0) {
+ if (!globbed)
+ Gcat(gpath, "");
+ else if (stat(gpath, &stb) >= 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ }
+ goto endit;
+ }
+ addpath(*cs++);
+ }
+ oldcs = cs;
+ while (cs > as && *cs != '/')
+ cs--, gpathp--;
+ if (*cs == '/')
+ cs++, gpathp++;
+ *gpathp = 0;
+ if (*oldcs == L_CURLY) {
+ (void) execbrc(cs, ((char *)0));
+ return;
+ }
+ matchdir(cs);
+endit:
+ gpathp = sgpathp;
+ *gpathp = 0;
+}
+
+static
+void matchdir(char *pattern)
+{
+ struct stat stb;
+#ifdef SYSDIRH
+ register struct direct *dp;
+#else
+ register struct dirent *dp;
+#endif
+ DIR *dirp;
+
+ dirp = opendir((*gpath ? gpath : "."));
+ if (dirp == NULL) {
+ if (globbed)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!isdir(stb)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (match(dp->d_name, pattern)) {
+ Gcat(gpath, dp->d_name);
+ globcnt++;
+ }
+ }
+ (void) closedir(dirp);
+ return;
+
+patherr1:
+ (void) closedir(dirp);
+patherr2:
+ globerr = "Bad directory components";
+}
+
+static
+int execbrc(char *p, char *s)
+{
+ char restbuf[BUFSIZ + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *sgpathp;
+
+ for (lm = restbuf; *p != L_CURLY; *lm++ = *p++)
+ continue;
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case L_CURLY:
+ brclev++;
+ continue;
+
+ case R_CURLY:
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ continue;
+ }
+pend:
+ brclev = 0;
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case L_CURLY:
+ brclev++;
+ continue;
+
+ case R_CURLY:
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ','|QUOTE:
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ (void) strcpy(lm, pl);
+ (void) strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ sgpathp = gpathp;
+ expand(restbuf);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ if (brclev)
+ return (0);
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ pm--;
+ continue;
+ }
+ if (brclev)
+ goto doit;
+ return (0);
+}
+
+static
+int match(char *s, char *p)
+{
+ register int c;
+ register char *sentp;
+ char sglobbed = globbed;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ globbed = sglobbed;
+ return (c);
+}
+
+static
+int amatch(char *s, char *p)
+{
+ register int scc;
+ int ok, lc;
+ char *sgpathp;
+ struct stat stb;
+ int c, cc;
+
+ globbed = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case L_CURLY:
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while ((cc = *p++) != '\0') {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (ok)
+ p--;
+ else
+ return 0;
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ s--;
+ do {
+ if (amatch(s, p))
+ return (1);
+ } while (*s++);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if (c != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ sgpathp = gpathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(gpath, &stb) == 0 && isdir(stb))
+ if (*p == 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ } else
+ expand(p);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ return (0);
+ }
+ }
+}
+
+#if UNUSED
+static
+Gmatch(char *s, char *p)
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (ok)
+ p--;
+ else
+ return 0;
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (Gmatch(s, p))
+ return (1);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+#endif
+
+static
+void Gcat(char *s1, char *s2)
+{
+ register int len = strlen(s1) + strlen(s2) + 1;
+
+ if (len >= gnleft || gargc >= GAVSIZ - 1)
+ globerr = "Arguments too long";
+ else {
+ gargc++;
+ gnleft -= len;
+ gargv[gargc] = 0;
+ gargv[gargc - 1] = strspl(s1, s2);
+ }
+}
+
+static
+void addpath(char c)
+{
+
+ if (gpathp >= lastgpathp)
+ globerr = "Pathname too long";
+ else {
+ *gpathp++ = c;
+ *gpathp = 0;
+ }
+}
+
+static
+void rscan(char **t, int (*f )(char))
+{
+ register char *p, c;
+
+ while ((p = *t++) != 0) {
+ if (f == tglob)
+ if (*p == '~')
+ gflag |= (short) 2;
+ else if (eq(p, "{") || eq(p, "{}"))
+ continue;
+ while ((c = *p++) != '\0')
+ (*f)(c);
+ }
+}
+/*
+static
+scan(t, f)
+ register char **t;
+ int (*f)(char);
+{
+ register char *p, c;
+
+ while (p = *t++)
+ while (c = *p)
+ *p++ = (*f)(c);
+} */
+
+static
+int tglob(char c)
+{
+
+ if (any(c, globchars))
+ gflag |= (c == L_CURLY ? (short)2 : (short)1);
+ return (c);
+}
+/*
+static
+trim(c)
+ char c;
+{
+
+ return (c & TRIM);
+} */
+
+
+int letter(char c)
+{
+ return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
+}
+
+int digit(char c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+int any(int c, char *s)
+{
+ while (*s)
+ if (*s++ == c)
+ return(1);
+ return(0);
+}
+
+int blklen(char **av)
+{
+ register int i = 0;
+
+ while (*av++)
+ i++;
+ return (i);
+}
+
+char **
+blkcpy(char **oav, char **bv)
+{
+ register char **av = oav;
+
+ while ((*av++ = *bv++) != 0)
+ continue;
+ return (oav);
+}
+
+void blkfree(char **av0)
+{
+ register char **av = av0;
+
+ while (*av)
+ free(*av++);
+}
+
+static
+char *
+strspl(char *cp, char *dp)
+{
+ register char *ep = (char *) malloc((size_t)(strlen(cp) + strlen(dp) + 1L));
+
+ if (ep == (char *)0)
+ fatal("Out of memory");
+ (void) strcpy(ep, cp);
+ (void) strcat(ep, dp);
+ return (ep);
+}
+
+char **
+copyblk(char **v)
+{
+ register char **nv = (char **)malloc((size_t)((blklen(v) + 1) *
+ sizeof(char **)));
+ if (nv == (char **)0)
+ fatal("Out of memory");
+
+ return (blkcpy(nv, v));
+}
+
+static
+char *
+strend(char *cp)
+{
+ while (*cp)
+ cp++;
+ return (cp);
+}
+
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+int gethdir(char *home_dir)
+{
+ register struct passwd *pp = getpwnam(home_dir);
+
+ if (pp == 0)
+ return (1);
+ (void) strcpy(home_dir, pp->pw_dir);
+ return (0);
+} /* gethdir */
+
+/* eof glob.c */
diff --git a/usr.bin/ncftp/glob.h b/usr.bin/ncftp/glob.h
new file mode 100644
index 0000000..4f35e7c
--- /dev/null
+++ b/usr.bin/ncftp/glob.h
@@ -0,0 +1,22 @@
+/* glob.h */
+
+#ifndef _glob_h_
+#define _glob_h_ 1
+
+/* $RCSfile: glob.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:45:32 $
+ */
+
+char **glob(char *v);
+int letter(char c);
+int digit(char c);
+int any(int c, char *s);
+int blklen(char **av);
+char **blkcpy(char **oav, char **bv);
+void blkfree(char **av0);
+char **copyblk(char **v);
+int gethdir(char *home_dir);
+
+#endif
+
diff --git a/usr.bin/ncftp/main.c b/usr.bin/ncftp/main.c
new file mode 100644
index 0000000..80b5015
--- /dev/null
+++ b/usr.bin/ncftp/main.c
@@ -0,0 +1,1160 @@
+/* main.c */
+
+#define _main_c_
+
+#define FTP_VERSION "1.9.4 (April 15, 1995)"
+
+/* #define BETA 1 */ /* If defined, it prints a little warning message. */
+
+#include "sys.h"
+
+#include <sys/stat.h>
+#include <arpa/ftp.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <pwd.h>
+
+#ifdef SYSLOG
+# include <syslog.h>
+#endif
+
+#if defined(CURSES) && !defined(NO_CURSES_H)
+# undef HZ /* Collides with HaZeltine ! */
+# include <curses.h>
+# ifdef TERMH
+# include <term.h>
+# endif
+#endif /* CURSES */
+
+#if defined(CURSES) && defined(SGTTYB)
+# include <sgtty.h>
+#endif
+
+#include "util.h"
+#include "cmds.h"
+#include "main.h"
+#include "ftp.h"
+#include "ftprc.h"
+#include "open.h"
+#include "set.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* main.c globals */
+int slrflag;
+int fromatty; /* input is from a terminal */
+int toatty; /* output is to a terminal */
+int doing_script; /* is a file being <redirected to me? */
+char *altarg; /* argv[1] with no shell-like preprocessing */
+struct servent serv; /* service spec for tcp/ftp */
+static char pad2a[8] = "Pad 2a"; /* SunOS overwrites jmp_bufs... */
+jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+static char pad2b[8] = "Pad 2b";
+char *line; /* input line buffer */
+char *stringbase; /* current scan point in line buffer */
+char *argbuf; /* argument storage buffer */
+char *argbase; /* current storage point in arg buffer */
+int margc; /* count of arguments on input line */
+char *margv[20]; /* args parsed from input line */
+struct userinfo uinfo; /* a copy of their pwent really */
+int ansi_escapes; /* for fancy graphics */
+int startup_msg = 1; /* TAR: display message on startup? */
+int ignore_rc; /* are we supposed to ignore the netrc */
+string progname; /* simple filename */
+string prompt, prompt2; /* shell prompt string */
+string anon_password; /* most likely your email address */
+string pager; /* program to browse text files */
+string version = FTP_VERSION;
+long eventnumber; /* number of commands we've done */
+FILE *logf = NULL; /* log user activity */
+longstring logfname; /* name of the logfile */
+long logsize = 4096L; /* max log size. 0 == no limit */
+int percent_flags; /* "%" in prompt string? */
+int at_flags; /* "@" in prompt string? */
+string mail_path; /* your mailbox */
+time_t mbox_time; /* last modified time of mbox */
+size_t epromptlen; /* length of the last line of the
+ * prompt as it will appear on screen,
+ * (i.e. no invis escape codes).
+ */
+
+#ifdef HPUX
+char *tcap_normal = "\033&d@"; /* Default ANSI escapes */
+char *tcap_boldface = "\033&dH"; /* Half Bright */
+char *tcap_underline = "\033&dD";
+char *tcap_reverse = "\033&dB";
+
+#else
+
+#ifdef NO_FORMATTING
+
+char *tcap_normal = "";
+char *tcap_boldface = "";
+char *tcap_underline = "";
+char *tcap_reverse = "";
+
+#else
+
+char *tcap_normal = "\033[0m"; /* Default ANSI escapes */
+char *tcap_boldface = "\033[1m";
+char *tcap_underline = "\033[4m";
+char *tcap_reverse = "\033[7m";
+
+#endif
+
+#endif
+
+size_t tcl_normal = 4, /* lengths of the above strings. */
+ tcl_bold = 4,
+ tcl_uline = 4,
+ tcl_rev = 4;
+
+#ifdef CURSES
+static char tcbuf[2048];
+#endif
+
+/* main.c externs */
+extern int debug, verbose, mprompt, passivemode;
+extern int options, cpend, data, connected, logged_in;
+extern int curtype, macnum, remote_is_unix;
+extern FILE *cout;
+extern struct cmd cmdtab[];
+extern str32 curtypename;
+extern char *macbuf;
+extern char *reply_string;
+extern char *short_verbose_msgs[4];
+extern string vstr;
+extern Hostname hostname;
+extern longstring cwd, lcwd, recent_file;
+extern int Optind;
+extern char *Optarg;
+#ifdef GATEWAY
+extern string gate_login;
+#endif
+
+void main(int argc, char **argv)
+{
+ register char *cp;
+ int top, opt, openopts = 0;
+ string tmp, oline;
+ struct servent *sptr;
+
+ if ((cp = rindex(argv[0], '/'))) cp++;
+ else cp = argv[0];
+ (void) Strncpy(progname, cp);
+
+ sptr = getservbyname("ftp", "tcp");
+ if (sptr == 0) fatal("ftp/tcp: unknown service");
+ serv = *sptr;
+
+ if (init_arrays()) /* Reserve large blocks of memory now */
+ fatal("could not reserve large amounts of memory.");
+
+#ifdef GZCAT
+ if ((GZCAT == (char *)1) || (GZCAT == (char *)0)) {
+ (void) fprintf(stderr,
+"You compiled the program with -DGZCAT, but you must specify the path with it!\n\
+Re-compile, this time with -DGZCAT=\\\"/path/to/gzcat\\\".\n");
+ exit(1);
+ }
+#endif
+#ifdef ZCAT
+ if ((ZCAT == (char *)1) || (ZCAT == (char *)0)) {
+ (void) fprintf(stderr,
+"You compiled the program with -DZCAT, but you must specify the path with it!\n\
+Re-compile, this time with -DZCAT=\\\"/path/to/zcat\\\".\n");
+ exit(1);
+ }
+#endif
+
+ /*
+ * Set up defaults for FTP.
+ */
+ mprompt = dMPROMPT;
+ debug = dDEBUG;
+ verbose = dVERBOSE;
+ passivemode = dPASSIVE;
+ (void) Strncpy(vstr, short_verbose_msgs[verbose+1]);
+
+ (void) Strncpy(curtypename, dTYPESTR);
+ curtype = dTYPE;
+ (void) Strncpy(prompt, dPROMPT);
+#ifdef GATEWAY
+ (void) Strncpy(gate_login, dGATEWAY_LOGIN);
+#endif
+
+#ifdef SOCKS
+ SOCKSinit("ncftp");
+#endif
+
+ /* Setup our pager variable, before we run through the rc,
+ which may change it. */
+ set_pager(getenv("PAGER"), 0);
+#ifdef CURSES
+ ansi_escapes = 1;
+ termcap_init();
+#else
+ ansi_escapes = 0;
+ if ((cp = getenv("TERM")) != NULL) {
+ if ((*cp == 'v' && cp[1] == 't') /* vt100, vt102, ... */
+ || (strcmp(cp, "xterm") == 0))
+ ansi_escapes = 1;
+ }
+#endif
+ (void) getuserinfo();
+
+ /* Init the mailbox checking code. */
+ (void) time(&mbox_time);
+
+ (void) Strncpy(anon_password, uinfo.username);
+ if (getlocalhostname(uinfo.hostname, sizeof(uinfo.hostname)) == 0) {
+ (void) Strncat(anon_password, "@");
+ (void) Strncat(anon_password, uinfo.hostname);
+ }
+#if dLOGGING
+ (void) Strncpy(logfname, dLOGNAME);
+ (void) LocalDotPath(logfname);
+#else
+ *logfname = 0;
+#endif
+ (void) Strncpy(recent_file, dRECENTF);
+ (void) LocalDotPath(recent_file);
+
+ (void) get_cwd(lcwd, (int) sizeof(lcwd));
+
+#ifdef SYSLOG
+# ifdef LOG_LOCAL3
+ openlog ("NcFTP", LOG_PID, LOG_LOCAL3);
+# else
+ openlog ("NcFTP", LOG_PID);
+# endif
+#endif /* SYSLOG */
+
+
+ ignore_rc = 0;
+ (void) strcpy(oline, "open ");
+ while ((opt = Getopt(argc, argv, "D:V:INPRHaicmup:rd:g:")) >= 0) {
+ switch(opt) {
+ case 'a':
+ case 'c':
+ case 'i':
+ case 'm':
+ case 'u':
+ case 'r':
+ (void) sprintf(tmp, "-%c ", opt);
+ goto cattmp;
+
+ case 'p':
+ case 'd':
+ case 'g':
+ (void) sprintf(tmp, "-%c %s ", opt, Optarg);
+ cattmp:
+ (void) strcat(oline, tmp);
+ openopts++;
+ break;
+
+ case 'D':
+ debug = atoi(Optarg);
+ break;
+
+ case 'V':
+ set_verbose(Optarg, 0);
+ break;
+
+ case 'I':
+ mprompt = !mprompt;
+ break;
+
+ case 'N':
+ ++ignore_rc;
+ break;
+
+ case 'P':
+ passivemode = !passivemode;
+ break;
+
+ case 'H':
+ (void) show_version(0, NULL);
+ exit (0);
+
+ default:
+ usage:
+ (void) fprintf(stderr, "Usage: %s [program options] [[open options] site.to.open[:path]]\n\
+Program Options:\n\
+ -D x : Set debugging level to x (a number).\n\
+ -H : Show version and compilation information.\n\
+ -I : Toggle interactive (mprompt) mode.\n\
+ -N : Toggle reading of the .netrc/.ncftprc.\n\
+ -P : Toggle passive mode ftp (for use behind firewalls).\n\
+ -V x : Set verbosity to level x (-1,0,1,2).\n\
+Open Options:\n\
+ -a : Open anonymously (this is the default).\n\
+ -u : Open, specify user/password.\n\
+ -i : Ignore machine entry in your .netrc.\n\
+ -p N : Use port #N for connection.\n\
+ -r : \"Redial\" until connected.\n\
+ -d N : Redial, pausing N seconds between tries.\n\
+ -g N : Redial, giving up after N tries.\n\
+ :path : ``Colon-mode:'' If \"path\" is a file, it opens site, retrieves\n\
+ file \"path,\" then exits; if \"path\" is a remote directory,\n\
+ it opens site then starts you in that directory..\n\
+ -c : If you're using colon-mode with a file path, this will cat the\n\
+ file to stdout instead of storing on disk.\n\
+ -m : Just like -c, only it pipes the file to your $PAGER.\n\
+Examples:\n\
+ ncftp ftp.unl.edu:/pub/README (just fetches README then quits)\n\
+ ncftp (just enters ncftp command shell)\n\
+ ncftp -V -u ftp.unl.edu\n\
+ ncftp -c ftp.unl.edu:/pub/README (cats README to stdout then quits)\n\
+ ncftp -D -r -d 120 -g 10 ftp.unl.edu\n", progname);
+ exit(1);
+ }
+ }
+
+ cp = argv[Optind]; /* the site to open. */
+ if (cp == NULL) {
+ if (openopts)
+ goto usage;
+ } else
+ (void) strcat(oline, cp);
+
+ if (ignore_rc <= 0)
+ (void) thrash_rc();
+ if (ignore_rc <= 1)
+ ReadRecentSitesFile();
+
+ (void) fix_options(); /* adjust "options" according to "debug" */
+
+ fromatty = doing_script = isatty(0);
+ toatty = isatty(1);
+ (void) UserLoggedIn(); /* Init parent-death detection. */
+ cpend = 0; /* no pending replies */
+
+ if (*logfname)
+ logf = fopen (logfname, "a");
+
+
+ /* The user specified a host, maybe in 'colon-mode', on the command
+ * line. Open it now...
+ */
+ if (argc > 1 && cp) {
+ if (setjmp(toplevel))
+ exit(0);
+ (void) Signal(SIGINT, intr);
+ (void) Signal(SIGPIPE, lostpeer);
+ (void) strcpy(line, oline);
+ makeargv();
+ /* setpeer uses this to tell if it was called from the cmd-line. */
+ eventnumber = 0L;
+ if (cmdOpen(margc, margv) != NOERR) {
+ exit(1);
+ }
+ }
+ eventnumber = 1L;
+
+ (void) init_prompt();
+
+ if (startup_msg) { /* TAR */
+ if (ansi_escapes) {
+#ifdef BETA
+# define BETA_MSG "\n\
+For testing purposes only. Do not re-distribute or subject to novice users."
+#else
+# define BETA_MSG ""
+#endif
+
+#ifndef CURSES
+ (void) printf("%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n",
+ tcap_boldface,
+ FTP_VERSION,
+ tcap_normal,
+ tcap_reverse,
+ BETA_MSG,
+ tcap_normal
+ );
+#else
+ char vis[256];
+ (void) sprintf(vis, "%sNcFTP %s by Mike Gleason, NCEMRSoft.%s%s%s%s\n",
+ tcap_boldface,
+ FTP_VERSION,
+ tcap_normal,
+ tcap_reverse,
+ BETA_MSG,
+ tcap_normal
+ );
+ tcap_put(vis);
+#endif /* !CURSES */
+ }
+ else
+ (void) printf("%s%s\n", FTP_VERSION, BETA_MSG);
+ } /* TAR */
+ if (NOT_VQUIET)
+ PrintTip();
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void) Signal(SIGINT, intr);
+ (void) Signal(SIGPIPE, lostpeer);
+ }
+ for (;;) {
+ if (cmdscanner(top) && !fromatty)
+ exit(1);
+ top = 1;
+ }
+} /* main */
+
+
+
+/*ARGSUSED*/
+void intr SIG_PARAMS
+{
+ dbprintf("intr()\n");
+ (void) Signal(SIGINT, intr);
+ (void) longjmp(toplevel, 1);
+} /* intr */
+
+
+
+int getuserinfo(void)
+{
+ register char *cp;
+ struct passwd *pw;
+ string str;
+ extern char *home; /* for glob.c */
+
+ home = uinfo.homedir; /* for glob.c */
+ pw = NULL;
+#ifdef USE_GETPWUID
+ /* Try to use getpwuid(), but if we have to, fall back to getpwnam(). */
+ pw = getpwuid(getuid());
+ if (pw == NULL) {
+ /* Oh well, try getpwnam() then. */
+ cp = getlogin();
+ if (cp == NULL) {
+ cp = getenv("LOGNAME");
+ if (cp == NULL)
+ cp = getenv("USER");
+ }
+ if (cp != NULL)
+ pw = getpwnam(cp);
+ }
+#else
+ /* Try to use getpwnam(), but if we have to, fall back to getpwuid(). */
+ cp = getlogin();
+ if (cp == NULL) {
+ cp = getenv("LOGNAME");
+ if (cp == NULL)
+ cp = getenv("USER");
+ }
+ if (cp != NULL)
+ pw = getpwnam(cp);
+ if (pw == NULL) {
+ /* Oh well, try getpwuid() then. */
+ pw = getpwuid(getuid());
+ }
+#endif
+ if (pw != NULL) {
+ uinfo.uid = pw->pw_uid;
+ (void) Strncpy(uinfo.username, pw->pw_name);
+ (void) Strncpy(uinfo.shell, pw->pw_shell);
+ if ((cp = getenv("HOME")) != NULL)
+ (void) Strncpy(uinfo.homedir, cp);
+ else
+ (void) Strncpy(uinfo.homedir, pw->pw_dir);
+ cp = getenv("MAIL");
+ if (cp != NULL) {
+ (void) Strncpy(str, cp);
+ } else {
+ /* Check a few typical mail directories.
+ * If we don't find it, too bad. Checking for new mail
+ * isn't very important anyway.
+ */
+ (void) sprintf(str, "/usr/spool/mail/%s", uinfo.username);
+ if (access(str, 0) < 0) {
+ (void) sprintf(str, "/var/mail/%s", uinfo.username);
+ }
+ }
+ cp = str;
+ if (access(cp, 0) < 0) {
+ mail_path[0] = 0;
+ } else {
+ /*
+ * mbox variable may be like MAIL=(28 /usr/mail/me /usr/mail/you),
+ * so try to find the first mail path.
+ */
+ while ((*cp != '/') && (*cp != 0))
+ cp++;
+ (void) Strncpy(mail_path, cp);
+ if ((cp = index(mail_path, ' ')) != NULL)
+ *cp = '\0';
+ }
+ return (0);
+ } else {
+ PERROR("getuserinfo", "Could not get your passwd entry!");
+ (void) Strncpy(uinfo.shell, "/bin/sh");
+ (void) Strncpy(uinfo.homedir, "."); /* current directory */
+ uinfo.uid = 999;
+ if ((cp = getenv("HOME")) != NULL)
+ (void) Strncpy(uinfo.homedir, cp);
+ mail_path[0] = 0;
+ return (-1);
+ }
+} /* getuserinfo */
+
+
+
+
+int init_arrays(void)
+{
+ if ((macbuf = (char *) malloc((size_t)(MACBUFLEN))) == NULL)
+ goto barf;
+ if ((line = (char *) malloc((size_t)(CMDLINELEN))) == NULL)
+ goto barf;
+ if ((argbuf = (char *) malloc((size_t)(CMDLINELEN))) == NULL)
+ goto barf;
+ if ((reply_string = (char *) malloc((size_t)(RECEIVEDLINELEN))) == NULL)
+ goto barf;
+
+ *macbuf = '\0';
+ init_transfer_buffer();
+ return (0);
+barf:
+ return (-1);
+} /* init_arrays */
+
+
+
+#ifndef BUFSIZ
+#define BUFSIZ 512
+#endif
+
+void init_transfer_buffer(void)
+{
+ extern char *xferbuf;
+ extern size_t xferbufsize;
+
+ /* Make sure we use a multiple of BUFSIZ for efficiency. */
+ xferbufsize = (MAX_XFER_BUFSIZE / BUFSIZ) * BUFSIZ;
+ while (1) {
+ xferbuf = (char *) malloc (xferbufsize);
+ if (xferbuf != NULL || xferbufsize < 1024)
+ break;
+ xferbufsize >>= 2;
+ }
+
+ if (xferbuf != NULL) return;
+ fatal("out of memory for transfer buffer.");
+} /* init_transfer_buffer */
+
+
+
+
+void init_prompt(void)
+{
+ register char *cp;
+
+ percent_flags = at_flags = 0;
+ for (cp = prompt; *cp; cp++) {
+ if (*cp == '%') percent_flags = 1;
+ else if (*cp == '@') at_flags = 1;
+ }
+} /* init_prompt */
+
+
+
+/*ARGSUSED*/
+void lostpeer SIG_PARAMS
+{
+ if (connected) {
+ close_streams(1);
+ if (data >= 0) {
+ (void) shutdown(data, 1+1);
+ (void) close(data);
+ data = -1;
+ }
+ connected = 0;
+ }
+ if (connected) {
+ close_streams(1);
+ connected = 0;
+ }
+ hostname[0] = cwd[0] = 0;
+ logged_in = macnum = 0;
+} /* lostpeer */
+
+
+/*
+ * Command parser.
+ */
+int cmdscanner(int top)
+{
+ register struct cmd *c;
+ int cmd_status, rcode = 0;
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ if (!doing_script && !UserLoggedIn())
+ (void) quit(0, NULL);
+ if (Gets(strprompt(), line, (size_t)CMDLINELEN) == NULL) {
+ (void) quit(0, NULL); /* control-d */
+ }
+ eventnumber++;
+ dbprintf("\"%s\"\n", line);
+ (void) makeargv();
+ if (margc == 0) {
+ continue; /* blank line... */
+ }
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *) -1) {
+ (void) printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ if (!implicit_cd(margv[0]))
+ (void) printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ (void) printf ("Not connected.\n");
+ continue;
+ }
+ cmd_status = (*c->c_handler)(margc, margv);
+ if (cmd_status == USAGE)
+ cmd_usage(c);
+ else if (cmd_status == CMDERR)
+ rcode = 1;
+ if (c->c_handler != help)
+ break;
+ }
+ (void) Signal(SIGINT, intr);
+ (void) Signal(SIGPIPE, lostpeer);
+ return rcode;
+} /* cmdscanner */
+
+
+
+
+char *strprompt(void)
+{
+ time_t tyme;
+ char eventstr[8];
+ char *dname, *lastlinestart;
+ register char *p, *q;
+ string str;
+ int flag;
+
+ if (at_flags == 0 && percent_flags == 0) {
+ epromptlen = strlen(prompt);
+ return (prompt); /* But don't overwrite it! */
+ }
+ epromptlen = 0;
+ lastlinestart = prompt2;
+ if (at_flags) {
+ for (p = prompt, q = prompt2, *q = 0; (*p); p++) {
+ if (*p == '@') switch (flag = *++p) {
+ case '\0':
+ --p;
+ break;
+ case 'M':
+ if (CheckNewMail() > 0)
+ q = Strpcpy(q, "(Mail) ");
+ break;
+ case 'N':
+ q = Strpcpy(q, "\n");
+ lastlinestart = q;
+ epromptlen = 0;
+ break;
+ case 'P': /* reset to no bold, no uline, no inverse, etc. */
+ if (ansi_escapes) {
+ q = Strpcpy(q, tcap_normal);
+ epromptlen += tcl_normal;
+ }
+ break;
+ case 'B': /* toggle boldface */
+ if (ansi_escapes) {
+ q = Strpcpy(q, tcap_boldface);
+ epromptlen += tcl_bold;
+ }
+ break;
+ case 'U': /* toggle underline */
+ if (ansi_escapes) {
+ q = Strpcpy(q, tcap_underline);
+ epromptlen += tcl_uline;
+ }
+ break;
+ case 'R':
+ case 'I': /* toggle inverse (reverse) video */
+ if (ansi_escapes) {
+ q = Strpcpy(q, tcap_reverse);
+ epromptlen += tcl_rev;
+ }
+ break;
+ case 'D': /* insert current directory */
+ case 'J':
+ if ((flag == 'J') && (remote_is_unix)) {
+ /* Not the whole path, just the dir name. */
+ dname = rindex(cwd, '/');
+ if (dname == NULL)
+ dname = cwd;
+ else if ((dname != cwd) && (dname[1]))
+ ++dname;
+ } else
+ dname = cwd;
+ if (dname[0]) {
+ q = Strpcpy(q, dname);
+ q = Strpcpy(q, " ");
+ }
+ break;
+ case 'H': /* insert name of connected host */
+ if (logged_in) {
+ (void) sprintf(str, "%s ", hostname);
+ q = Strpcpy(q, str);
+ }
+ break;
+ case 'C': /* Insert host:path (colon-mode format. */
+ if (logged_in) {
+ (void) sprintf(str, "%s:%s ", hostname, cwd);
+ q = Strpcpy(q, str);
+ } else
+ q = Strpcpy(q, "(not connected)");
+ break;
+ case 'c':
+ if (logged_in) {
+ (void) sprintf(str, "%s:%s\n", hostname, cwd);
+ q = Strpcpy(q, str);
+ lastlinestart = q; /* there is a \n at the end. */
+ epromptlen = 0;
+ }
+ break;
+ case '!':
+ case 'E': /* insert event number */
+ (void) sprintf(eventstr, "%ld", eventnumber);
+ q = Strpcpy(q, eventstr);
+ break;
+ default:
+ *q++ = *p; /* just copy it; unknown switch */
+ } else
+ *q++ = *p;
+ }
+ *q = '\0';
+ } else
+ (void) strcpy(prompt2, prompt);
+
+#ifndef NO_STRFTIME
+ if (percent_flags) {
+ /* only strftime if the user requested it (with a %something),
+ otherwise don't waste time doing nothing. */
+ (void) time(&tyme);
+ (void) Strncpy(str, prompt2);
+ (void) strftime(prompt2, sizeof(str), str, localtime(&tyme));
+ }
+#endif
+ epromptlen = (size_t) ((long) strlen(lastlinestart) - (long) epromptlen);
+ return (prompt2);
+} /* strprompt */
+
+
+/*
+ * Slice a string up into argc/argv.
+ */
+
+void makeargv(void)
+{
+ char **argp;
+
+ margc = 0;
+ argp = margv;
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+ while ((*argp++ = slurpstring()) != 0)
+ margc++;
+} /* makeargv */
+
+
+
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+char *slurpstring(void)
+{
+ int got_one = 0;
+ register char *sb = stringbase;
+ register char *ap = argbase;
+ char *tmp = argbase; /* will return this if token found */
+
+ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
+ switch (slrflag) { /* and $ as token for macro invoke */
+ case 0:
+ slrflag++;
+ stringbase++;
+ return ((*sb == '!') ? "!" : "$");
+ /* NOTREACHED */
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '=':
+ sb++; goto S0;
+
+ default:
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = sb;
+ break;
+ default:
+ break;
+ }
+ goto S1;
+ }
+
+S1:
+ switch (*sb) {
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '=':
+ case '\0':
+ goto OUT; /* end of token */
+
+ case '\\':
+ sb++; goto S2; /* slurp next character */
+
+ case '"':
+ sb++; goto S3; /* slurp quoted string */
+
+ default:
+ *ap++ = *sb++; /* add character to token */
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ sb++; goto S1;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S3;
+ }
+
+OUT:
+ if (got_one)
+ *ap++ = '\0';
+ argbase = ap; /* update storage pointer */
+ stringbase = sb; /* update scan pointer */
+ if (got_one) {
+ return(tmp);
+ }
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = (char *) 0;
+ break;
+ default:
+ break;
+ }
+ return((char *)0);
+} /* slurpstring */
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+int
+help(int argc, char **argv)
+{
+ register struct cmd *c;
+ int showall = 0, helpall = 0;
+ char *arg;
+ int i, j, k;
+ int nRows, nCols;
+ int nCmds2Print;
+ int screenColumns;
+ int len, widestName;
+ char *cp, **cmdnames, spec[16];
+
+ if (argc == 2) {
+ showall = (strcmp(argv[1], "showall") == 0);
+ helpall = (strcmp(argv[1], "helpall") == 0);
+ }
+ if (argc == 1 || showall) {
+ (void) printf("\
+Commands may be abbreviated. 'help showall' shows aliases, invisible and\n\
+unsupported commands. 'help <command>' gives a brief description of <command>.\n\n");
+
+ for (c = cmdtab, nCmds2Print=0; c->c_name != NULL; c++)
+ if (!c->c_hidden || showall)
+ nCmds2Print++;
+
+ if ((cmdnames = (char **) malloc(sizeof(char *) * nCmds2Print)) == NULL)
+ fatal("out of memory!");
+
+ for (c = cmdtab, i=0, widestName=0; c->c_name != NULL; c++) {
+ if (!c->c_hidden || showall) {
+ cmdnames[i++] = c->c_name;
+ len = (int) strlen(c->c_name);
+ if (len > widestName)
+ widestName = len;
+ }
+ }
+
+ if ((cp = getenv("COLUMNS")) == NULL)
+ screenColumns = 80;
+ else
+ screenColumns = atoi(cp);
+
+ widestName += 2; /* leave room for white-space in between cols. */
+ nCols = screenColumns / widestName;
+ /* if ((screenColumns % widestName) > 0) nCols++; */
+ nRows = nCmds2Print / nCols;
+ if ((nCmds2Print % nCols) > 0)
+ nRows++;
+
+ (void) sprintf(spec, "%%-%ds", widestName);
+ for (i=0; i<nRows; i++) {
+ for (j=0; j<nCols; j++) {
+ k = nRows*j + i;
+ if (k < nCmds2Print)
+ (void) printf(spec, cmdnames[k]);
+ }
+ (void) printf("\n");
+ }
+ Free(cmdnames);
+ } else if (helpall) {
+ /* Really intended to debug the help strings. */
+ for (c = cmdtab; c->c_name != NULL; c++) {
+ cmd_help(c);
+ cmd_usage(c);
+ }
+ } else while (--argc > 0) {
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ (void) printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ (void) printf("?Invalid help command %s\n", arg);
+ else {
+ cmd_help(c);
+ cmd_usage(c);
+ }
+ }
+ return NOERR;
+} /* help */
+
+
+/*
+ * If the user wants to, s/he can specify the maximum size of the log
+ * file, so it doesn't waste too much disk space. If the log is too
+ * fat, trim the older lines (at the top) until we're under the limit.
+ */
+void trim_log(void)
+{
+ FILE *new, *old;
+ struct stat st;
+ long fat;
+ string tmplogname, str;
+
+ if (logsize <= 0 || *logfname == 0 || stat(logfname, &st) ||
+ (old = fopen(logfname, "r")) == NULL)
+ return; /* never trim, or no log */
+ fat = st.st_size - logsize;
+ if (fat <= 0L) return; /* log too small yet */
+ while (fat > 0L) {
+ if (FGets(str, old) == NULL) return;
+ fat -= (long) strlen(str);
+ }
+ /* skip lines until a new site was opened */
+ while (1) {
+ if (FGets(str, old) == NULL) {
+ (void) fclose(old);
+ (void) unlink(logfname);
+ return; /* nothing left, start anew */
+ }
+ if (*str != '\t') break;
+ }
+
+ /* copy the remaining lines in "old" to "new" */
+ (void) Strncpy(tmplogname, logfname);
+ tmplogname[strlen(tmplogname) - 1] = 'T';
+ if ((new = fopen(tmplogname, "w")) == NULL) {
+ (void) PERROR("trim_log", tmplogname);
+ return;
+ }
+ (void) fputs(str, new);
+ while (FGets(str, old))
+ (void) fputs(str, new);
+ (void) fclose(old); (void) fclose(new);
+ if (unlink(logfname) < 0)
+ PERROR("trim_log", logfname);
+ if (rename(tmplogname, logfname) < 0)
+ PERROR("trim_log", tmplogname);
+} /* trim_log */
+
+
+
+
+int CheckNewMail(void)
+{
+ struct stat stbuf;
+
+ if (*mail_path == '\0') return 0;
+ if (stat(mail_path, &stbuf) < 0) { /* cant find mail_path so we'll */
+ *mail_path = '\0'; /* never check it again */
+ return 0;
+ }
+
+ /*
+ * Check if the size is non-zero and the access time is less than
+ * the modify time -- this indicates unread mail.
+ */
+ if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) {
+ if (stbuf.st_mtime > mbox_time) {
+ (void) printf("%s\n", NEWMAILMESSAGE);
+ mbox_time = stbuf.st_mtime;
+ }
+ return 1;
+ }
+
+ return 0;
+} /* CheckNewMail */
+
+
+
+#ifdef CURSES
+int termcap_get(char **dest, char *attr)
+{
+ static char area[1024];
+ static char *s = area;
+ char *buf, *cp;
+ int i, result = -1;
+ int len = 0;
+
+ *dest = NULL;
+ while (*attr != '\0') {
+ buf = tgetstr(attr, &s);
+ if (buf != NULL && buf[0] != '\0') {
+ for (i = 0; (buf[i] <= '9') && (buf[i] >= '0'); )
+ i++;
+ /* Get rid of the terminal delays, like "$<2>". */
+ if ((cp = strstr(&(buf[i]), "$<")) != NULL)
+ *cp = 0;
+ if (*dest == NULL)
+ *dest = (char *)malloc(strlen(&(buf[i])) + 1);
+ else
+ *dest = (char *)realloc(*dest, len + strlen(&(buf[i])) + 1);
+ if (*dest == NULL)
+ break;
+ (void) strcpy(*dest + len, &(buf[i]));
+ len += strlen (&(buf[i]));
+ }
+ attr += 2;
+ }
+ if (*dest == NULL)
+ *dest = "";
+ else
+ result = 0;
+ return (result);
+} /* termcap_get */
+
+
+
+void termcap_init(void)
+{
+ char *term;
+#ifdef SGTTYB
+ struct sgttyb ttyb;
+ extern short ospeed;
+#endif
+
+ if ((term = getenv("TERM")) == NULL) {
+ term = "dumb"; /* TAR */
+ ansi_escapes = 0;
+ }
+ if (tgetent(tcbuf,term) != 1) {
+ (void) fprintf(stderr,"Can't get termcap entry for terminal [%s]\n", term);
+ } else {
+ (void) termcap_get(&tcap_normal, "meuese");
+ if (termcap_get(&tcap_boldface, "md") < 0) {
+ /* Dim-mode is better than nothing... */
+ (void) termcap_get(&tcap_boldface, "mh");
+ }
+ (void) termcap_get(&tcap_underline, "us");
+ (void) termcap_get(&tcap_reverse, "so");
+ tcl_normal = strlen(tcap_normal);
+ tcl_bold = strlen(tcap_boldface);
+ tcl_uline = strlen(tcap_underline);
+ tcl_rev = strlen(tcap_reverse);
+#ifdef SGTTYB
+ if (ioctl(fileno(stdout), TIOCGETP, &ttyb) == 0)
+ ospeed = ttyb.sg_ospeed;
+#endif
+ }
+
+} /* termcap_init */
+
+
+
+static int c_output(int c)
+{
+ return (putchar(c));
+} /* c_output */
+
+
+
+
+void tcap_put(char *cap)
+{
+ tputs(cap, 0, c_output);
+} /* tcap_put */
+
+#endif /* CURSES */
+
+/* eof main.c */
+
diff --git a/usr.bin/ncftp/main.h b/usr.bin/ncftp/main.h
new file mode 100644
index 0000000..4e3bba6
--- /dev/null
+++ b/usr.bin/ncftp/main.h
@@ -0,0 +1,45 @@
+/* main.h */
+
+#ifndef _main_h_
+#define _main_h_
+
+struct userinfo {
+ str32 username;
+ string homedir;
+ string shell;
+ string hostname;
+ int uid;
+};
+
+void intr SIG_PARAMS;
+int getuserinfo(void);
+int init_arrays(void);
+void init_transfer_buffer(void);
+void init_prompt(void);
+void lostpeer SIG_PARAMS;
+int cmdscanner(int top);
+char *strprompt(void);
+void makeargv(void);
+char *slurpstring(void);
+int help(int argc, char **argv);
+void trim_log(void);
+int CheckNewMail(void);
+
+#ifdef CURSES
+ void tcap_put(char *cap);
+ void termcap_init(void);
+ int termcap_get(char **dest, char *attr);
+# ifndef TERMH /* <term.h> would take care of this. */
+# ifdef NO_CONST
+ extern char *tgetstr(char *, char **);
+# else
+ extern char *tgetstr(const char *, char **);
+# endif
+# endif /* TERMH */
+#endif /* CURSES */
+
+/* Should be in a 'tips.h,' but... */
+void PrintTip(void);
+
+#endif /* _main_h_ */
+
diff --git a/usr.bin/ncftp/ncftp.1 b/usr.bin/ncftp/ncftp.1
new file mode 100644
index 0000000..8cae4fb
--- /dev/null
+++ b/usr.bin/ncftp/ncftp.1
@@ -0,0 +1,1399 @@
+.\"-------
+.\" Man page portability notes
+.\"
+.\" These are some notes on conventions to maintain for greatest
+.\" portability of this man page to various other versions of
+.\" nroff.
+.\"
+.\" When you want a \ to appear in the output, use \e in the man page.
+.\" (NOTE this comes up in the rc grammar, where to print out '\n' the
+.\" man page must contain '\en'.)
+.\"
+.\" Evidently not all versions of nroff allow the omission of the
+.\" terminal " on a macro argument. Thus what could be written
+.\"
+.\" .Cr "exec >[2] err.out
+.\"
+.\" in true nroffs must be written
+.\"
+.\" .Cr "exec >[2] err.out"
+.\"
+.\" instead.
+.\"
+.\" Use symbolic font names (e.g. R, I, B) instead of the standard
+.\" font positions 1, 2, 3. Note that for Xf to work the standard
+.\" font names must be single characters.
+.\"
+.\" Note that sentences should end at the end of a line. nroff and
+.\" troff will supply the correct intersentence spacing, but only if
+.\" the sentences end at the end of a line. Explicit spaces, if given,
+.\" are apparently honored and the normal intersentence spacing is
+.\" supressed.
+.\"
+.\" DaviD W. Sanderson
+.\"-------
+.\" Dd distance to space vertically before a "display"
+.\" These are what n/troff use for interparagraph distance
+.\"-------
+.if t .nr Dd .4v
+.if n .nr Dd 1v
+.\"-------
+.\" Sp space down the interparagraph distance
+.\"-------
+.de Sp
+.sp \\n(Ddu
+..
+.\"-------
+.\" Ds begin a display, indented .5 inches from the surrounding text.
+.\"
+.\" Note that uses of Ds and De may NOT be nested.
+.\"-------
+.de Ds
+.Sp
+.in +0.5i
+.nf
+..
+.\"-------
+.\" De end a display (no trailing vertical spacing)
+.\"-------
+.de De
+.fi
+.in
+..
+.TH NcFTP 1 "1.9" NCEMRSoft
+.\"-------
+.SH "NAME"
+.\"-------
+NcFTP \(em Internet file transfer program
+.\"-------
+.SH "SYNOPSIS"
+.\"-------
+.B ncftp
+.RI [ "program options" ]
+.RI [[ "open options" ]
+.IR hostname [\c
+.B :\c
+.IR pathname ]]
+.\"-------
+.SH "DESCRIPTION"
+.\"-------
+.I NcFTP
+is a user interface to the Internet standard
+.IR "File Transfer Protocol" .
+This program allows a user to transfer files to and from a remote network
+site, and offers additional features that are not found in the standard
+interface,
+.IR ftp .
+.\"-------
+.SH "FEATURES"
+.\"-------
+Program options will be explained later in this document.
+Let's get down to business and go over the features
+that make this program worthwhile.
+.PP
+Here is the list of section headers; I have my $MANPAGER environment
+variable set to use
+.RB `` "less \-i" ''
+so that I can skip to the section I
+want (otherwise,
+.BI / regex
+commands to the pager won't match the section
+headers because of the formatting codes;
+the
+.RB `` \-i ''
+can search through the formatting codes)
+.Ds
+Establishing the remote connection
+Format of the RC file
+The Recent-sites file
+Redialing a busy remote site
+Supplying a sitename from your shell's command line
+Using Colon-mode
+Using FTP-cat and FTP-more mode
+Supplying a port number with the open command
+Displaying and changing program variables
+Program variables
+Listing a remote directory
+Viewing a remote directory with your pager
+Redisplaying the last directory listing
+Fetching files from the remote host
+Viewing a remote file with your pager
+Creating a message file on the remote host
+Looking up site names and addresses
+Checking the configuration of the program
+Using the command shell
+Customizing the prompt
+Keeping a log of your file transfers
+Program options
+A sample RC file
+.De
+.\"-------
+.SH "Establishing the remote connection"
+.\"-------
+Just opening a connection to a remote server was inconvenient enough in the
+stock
+.I ftp
+program to justify writing this program.
+Here at
+.IR NCEMRSoft ,
+we want to do our business as quickly and painlessly as possible.
+We'd
+rather save time and wear and tear on our metacarpals than bother typing
+entire site names, usernames, and email addresses masquerading as passwords,
+and setting binary mode.
+.PP
+We made all connections anonymous by default, and we automatically send our
+email address for the password on those connections.
+We allowed for site
+names to be abbreviated.
+.PP
+For each commonly accessed site, you can put an entry in your program
+preferences file (let's call it the ``ncftprc file'' or ``RC file'' for short).
+To open the site, from the command shell all you do is type:
+.Ds
+open wuarchive.wustl.edu
+.De
+.PP
+or
+.Ds
+o wuarchive.wustl.edu
+.De
+.PP
+As promised, you can abbreviate that further.
+Just use any abbreviation that
+would match only the site you had in mind.
+For the previous example, you
+could try:
+.Ds
+o wuarc
+o wustl
+o stl
+o wu
+.De
+.PP
+Any of those abbreviations would open wuarchive.wustl.edu anonymously,
+sending your anon-password (usually set to your email address) as the
+password.
+Keep in mind that the program tries opening the first site
+that matches the abbreviation you supplied.
+So:
+.Ds
+o w
+.De
+.PP
+might match a site named bowser.nintendo.co.jp if that site appeared before
+your entry for wuarchive.wustl.edu.
+.PP
+Most of the time we open remote sites anonymously, but
+there are times where you need to specifically open a site with an actual
+username and password.
+Let's say my partner, Phil Dietz, wants to FTP
+something out of my account.
+Perhaps he wants to fetch the latest version
+of the source code to
+.I NcFTP
+so he can optimize something or add a new feature behind my back.
+Since the
+program opens remote sites anonymously by default (actually, you can change
+this behavior; more on that later), he would have to specify a flag to the
+.I open
+command so he can supply my username and password.
+He would try:
+.Ds
+o \-u sphygmomanometer.unl.edu
+.De
+.PP
+or, more likely:
+.Ds
+o \-u sph
+.De
+.PP
+Then the program would prompt him for a username (login, whatever) and a
+password:
+.Ds
+Login Name (pdietz): mgleason
+Password: ********
+.De
+.PP
+If he got it right, he could raid my stuff.
+If not, he'd probably drop
+me an email asking me to quit changing my password so often.
+.PP
+There are even times where you want to FTP from your own account, like if
+you are debugging an FTP client you wrote.
+At this prompt:
+.Ds
+Login Name (mgleason):
+.De
+.PP
+I could just hit return to tell the program that I want ``mgleason'' as my
+username, then I would enter my password.
+.\"-------
+.SH "Format of the RC file"
+.\"-------
+This release of the program is somewhat compatible with the stock
+.I ftp
+program's
+.B ".netrc"
+file.
+However, I can promise you that in the near future the program will
+use a new format, so don't invest too much time in it.
+.PP
+The RC file can be named
+.RB `` ncftprc '',
+.RB `` netrc '',
+or
+.RB `` .ncftprc '',
+but it is usually named
+.RB `` .netrc ''
+so it can be used with the stock
+.I ftp
+program.
+.I NcFTP
+looks in the current working directory for any of those files, and then in
+your home directory, and after that it gives up (which is OK, because RC
+files aren't mandatory).
+.PP
+The file usually starts with
+.I #set
+and
+.I #unset
+commands that do things
+to the programs variables.
+The reason for the ``#'' is so the stock
+.I ftp
+program will think they are comments.
+You might have this appearing as
+the first few lines in your RC file (I'll explain later):
+.Ds
+#set debug 1
+#set pager "less \-EMi"
+#unset startup\-msg
+.De
+.PP
+After those, you put in machine entries for each of your favorite sites.
+Let's put in an entry for wuarchive.wustl.edu.
+First you would put:
+.Ds
+machine wuarchive.wustl.edu
+.De
+.PP
+Then you could put in your username, password, and account if you like:
+.Ds
+user anonymous
+password \-mgleason@ftp.cs.unl.edu
+account wuarc.does.not.use.accounts
+.De
+.PP
+Following that, you would add the startup macro that is run
+each time you connect to wuarchive.
+You must start it with this line:
+.Ds
+macdef init
+.De
+.PP
+Then put in the commands you want to do:
+.Ds
+cd /graphics/gif
+ls \-lt
+.De
+.PP
+After that, you end the macro with a blank line (important!).
+The finished machine entry would look like the following.
+To make the transition to the impending new format less painful,
+I recommend you adhere to this format:
+.ta 6m +6m
+.Ds
+machine wuarchive.wustl.edu
+ user anonymous
+ password \-mgleason@ftp.cs.unl.edu
+ account wuarc.does.not.use.accounts
+ macdef init
+ cd /graphics/gif
+ ls \-lt
+.RI \t( "mandatory blank line to end the macro" )
+.De
+.PP
+Of course, if all you want to do is open wuarchive anonymously, you
+needn't bother with the ``user'', ``password'', and ``account'' lines.
+You may want to put them in if you plan on using the stock
+.I ftp
+program, though.
+Try something like this:
+.ta 6m +6m
+.Ds
+machine wuarchive.wustl.edu
+ macdef init
+ cd /graphics/gif
+ ls \-lt
+.RI \t( "mandatory blank line to end the macro" )
+.De
+.PP
+You can tell the program to not run the startup macro if you supply
+.B "\-i"
+to the
+.I open
+command.
+.PP
+Really, you should only bother adding entries for sites that you want to
+run startup macros upon connection.
+The next section explains why.
+.\"-------
+.SH "The Recent-sites file"
+.\"-------
+Each time you open a site, the program saves the name of the site and the
+last directory you were in to the
+.I recent-sites file
+which is named
+.B ".ncrecent"
+and placed in your home directory.
+The program saves a
+predetermined number of these sites in the file, and when it reaches the
+limit, it discards the oldest entry so it can add a new one.
+.PP
+You can just go ahead and use the name of the site you want with the
+.I open
+command if you know it is in the
+.I recent\-file
+(and you can abbreviate the
+name, just like those in the RC file).
+But if you cannot remember what the
+name of the site you want, all you do is run the
+.I open
+command with
+no site parameter:
+.Ds
+open
+.De
+.PP
+This will pop up a list of the sites in the
+.IR "recent-file" ,
+and sites in your RC file.
+At the open prompt, just type the name (or an
+abbreviation of that name) or the number preceding the site name to open
+that site.
+After opening the site you wanted, the program sets the remote
+working directory to the same one you left in the last time you called.
+.PP
+If you don't like the idea of having the sites you called stored on disk,
+you can turn this feature off using an
+.I unset
+command, explained later.
+.\"-------
+.SH "Redialing a busy remote site"
+.\"-------
+Some remote sites limit the number of leeches, er, anonymous connections
+at a time to reduce the load on the host computer.
+You can use the
+.I open
+command's redial feature to keep attempting connections until you get on,
+although that is not a very polite thing to do.
+The simplest way to do
+this would be to just supply the
+.B \-r
+option:
+.Ds
+open \-r wuarc
+.De
+.PP
+There are also options you can use to tweak redial.
+The
+.B \-d
+flag sets
+the delay between dials, and the
+.B \-g
+flag sets a limit on how many dials
+should be attempting before giving up.
+If you don't supply
+.B \-g
+the program will dial a day and forever (which my Number Theory professor,
+Dr. Mientka, says is longer than forever and a day)
+until it connects successfully, or until you get sick of waiting and hit the
+interrupt key (usually ^C).
+.PP
+This example dials wuarchive every ten minutes, giving up after twenty
+attempts.
+Note that the redial delay is specified in seconds:
+.Ds
+open \-r \-d 600 \-g 20 wuarc
+.De
+.PP
+Please be considerate when you use redialing, so you won't tax the network.
+Site administrators can and do get angry when they get flooded with
+connections.
+.\"-------
+.SH "Supplying a sitename from your shell's command line"
+.\"-------
+When you run the program:
+.Ds
+ncftp
+.De
+.PP
+by itself does nothing and waits for you to type commands to the program's
+own shell.
+Just like the stock
+.I ftp
+program, you can supply a site name
+on the command line:
+.Ds
+ncftp wuarchive.wustl.edu
+.De
+.PP
+You can also use abbreviations as usual:
+.Ds
+ncftp wuarc
+.De
+.PP
+This is equivalent to running the program, then issuing an
+.I open
+command to open wuarchive.
+.\"-------
+.SH "Using Colon-mode"
+.\"-------
+The
+.I open
+command is not a one-trick pony.
+Another option is what I call
+.IR "colon-mode" .
+This feature is used (most of the time) from your shell's
+command line.
+.PP
+In ancient times, way back during the Disco era, you could use a program
+called
+.I tftp
+to fetch a file using the Internet standard
+.I Trivial File Transfer Protocol.
+You could use that program to do something like this
+from within its shell:
+.Ds
+get wuarchive.wustl.edu:/graphics/gif/README
+.De
+.PP
+and that would call wuarchive and fetch the
+.B README
+file.
+.PP
+You can use this program to do the same thing from your shell's command
+line:
+.Ds
+csh> ncftp wuarchive.wustl.edu:/graphics/gif/README
+csh> head README
+.De
+.PP
+This tells your shell, in this case the ``c-shell'' to run
+.IR NcFTP ,
+which
+would open wuarchive, fetch
+.B /graphics/gif/README
+and write the file
+.B ./README
+in the current working directory, and then exits.
+This is nice if you don't
+want to browse around the remote site, and you know exactly want you want.
+It would also come in handy in shell scripts, where you don't want to
+enter the command shell, and might not want the program to spew output.
+.PP
+You can use
+.I colon-mode
+to set the starting remote working directory also:
+.Ds
+csh> ncftp wuarchive.wustl.edu:/graphics/gif
+.De
+.PP
+This would run the program, open wuarchive, and
+.I cd
+to the gif directory, then run the program's command shell so you can
+browse.
+.PP
+.I Colon-mode
+is also available from within the program's command shell.
+At a prompt you can do stuff like this:
+.Ds
+ncftp> open wuarchive.wustl.edu:/graphics/gif/README
+ncftp> o wuarc:/graphics/gif
+.De
+.\"-------
+.SH "Using FTP-cat and FTP-more mode"
+.\"-------
+There are times where you might not want the program to write a
+.I colon-mode
+file in the current working directory, or perhaps you want to pipe the
+output of a remote file into something else.
+.I Colon-mode
+has options to
+do this.
+It was inspired by the guy who wrote the
+.I ftpcat
+perl script.
+The
+.B \-c
+option tells the program to write on the standard
+output stream.
+The
+.B \-m
+option pipes the file into your pager (like
+.IR more ")"
+Of course this won't work if the thing you give
+.I colon-mode
+is a directory! This example just dumps a remote file to stdout:
+.Ds
+csh> ncftp \-c wuarc:/graphics/gif/README
+\&...
+csh>
+.De
+.PP
+This example redirects a remote file into a different
+location:
+.Ds
+csh> ncftp \-c wu:/README > ~pdietz/thesis.tex
+.De
+.PP
+This one shows how to use a pipeline:
+.Ds
+csh> ncftp \-c wuarc:/README | tail | wc \-l
+10
+csh>
+.De
+.PP
+This shows how to page a remote file:
+.Ds
+csh> ncftp \-m wuarc:/graphics/gif/README
+\&...
+csh>
+.De
+.\"-------
+.SH "Supplying a port number with the open command"
+.\"-------
+This option just didn't fit anywhere else, so to finish out the open command,
+.B \-p
+lets you supply a port number if you have to
+.I ftp
+to a site using an nonstandard port number.
+Personally, I have yet to use this feature, but it is
+there for compatibility with the stock
+.I ftp
+program.
+.\"-------
+.SH "Displaying and changing program variables"
+.\"-------
+Now I'll explain the commands unique to
+.IR NcFTP .
+The others should perform the
+same as they would in the stock
+.I ftp
+program;
+consult the man page for it if you want those explained,
+or use the
+.I help
+command for a brief blurb.
+.PP
+The
+.I show
+command is used to display program variables and their values.
+.Ds
+show all
+.De
+.PP
+or
+.Ds
+show
+.De
+.PP
+would display all the variables with their values.
+.Ds
+.RI show " var1 var2 ... varN"
+.De
+.PP
+would display each specified variable and its value.
+.PP
+The
+.I set
+command changes the value of a program variable.
+Its syntax is:
+.Ds
+.RI set " varname value"
+.De
+.PP
+For Boolean or Integer variables,
+.Ds
+.RI set " varname"
+.De
+.PP
+would set the value of the variable
+.I varname
+to
+.B 1
+.RB ( true ).
+.PP
+The
+.I unset
+command can be used to set the variable to its default value,
+or for Boolean and Integer variables, set the value of the variable to
+.B 0
+.RB ( false ).
+For String variables, you can use this to set the value to an
+empty string.
+.PP
+You can use any of those three commands in both the command shell,
+or in the RC file with a ``#'' prepended.
+.\"-------
+.SH "Program variables"
+.\"-------
+Each variable can be one of the following types:
+.TP
+Boolean:
+Can be
+.RB `` on ''
+or
+.RB `` off ''
+(you can also use
+.RB `` 1 ''
+or
+.RB `` 0 '').
+.TP
+Integer:
+Can be any positive or negative number, or
+.BR 0 .
+.TP
+String:
+Is a string of characters.
+If the string needs to have a space
+in it, make sure you surround the whole string with double quotes in a
+.I set
+command.
+.PP
+Variables follow.
+Some variables are explained later in the relevant sections.
+.TP
+.IR anon\-open " (Boolean)"
+Tells whether the default login mode is anonymous if
+on, or if off, will prompt for a username/password.
+You can always override this by using either
+.B \-a
+or
+.B \-u
+with the
+.I open
+command.
+.TP
+.IR anon\-password " (String)"
+Sends this as the password when you login anonymously.
+By default this is your email address.
+.TP
+.IR ansi\-escapes " (Boolean)"
+If on, the program can use boldface, underline,
+and inverse text.
+.TP
+.IR auto\-binary " (Boolean)"
+If on, sets the transfer type to binary mode
+immediately after connection.
+.TP
+.IR debug " (Integer)"
+Sets the debugging level.
+.TP
+.IR gateway\-login " (String)"
+Tells which username to use when logging in to
+your firewall gateway host.
+.TP
+.IR gateway\-host " (String)"
+The site which is acting as your firewall gateway,
+or empty if you aren't using one.
+.TP
+.IR local\-dir " (String)"
+The current local working directory.
+I like to set this from my RC file,
+so all my files go into my download directory.
+.TP
+.IR logfile " (String)"
+The name of your personal transfer log, or empty
+if you aren't using a transfer log.
+.TP
+.IR logsize " (Integer)"
+The maximum ceiling of your log file, before the program
+removes old entries.
+.TP
+.IR mprompt " (Boolean)"
+If on, prompts for each remote file expanded from a
+wildcard globbing expression.
+.TP
+.IR netrc " (String, Read-only)"
+Tells you the name of the RC file in use.
+.TP
+.IR pager " (String)"
+The pathname and flags of the program used to display
+output one screenful at a time.
+The default is the value of your $PAGER
+environment variable.
+.TP
+.IR prompt " (String)"
+The prompt specification that expands into the prompt.
+.TP
+.IR progress\-reports " (Integer)"
+Which progress meter to use, or
+.B 0
+if you don't want progress reports during file transfers.
+Set it to
+.B 1
+for a simple percentage meter;
+.B 2
+for a fancy bar graph indicator;
+.B 3
+to print just the number of kilobytes transferred; or
+.B 4
+to print one dot for each 10% transferred, if you
+want to avoid the use of backspaces. Note that the program
+may use a different meter depending on how cooperative the
+remote host is, and what you have the
+.I ansi\-escapes
+variable set to.
+.TP
+.IR recent\-list " (Boolean)"
+If on, uses and updates the
+.I recent\-file.
+.TP
+.IR remote\-is\-unix " (Boolean)"
+Set automatically by the program upon connection,
+you may need to use this in a startup macro if the program guessed
+that a remote site was UNIX when it really is not.
+.TP
+.IR startup\-msg " (Boolean)"
+If on, prints the opening message and tip.
+.TP
+.IR tips " (Boolean)"
+If on, prints a tip on how to use the program better each
+time you run the program.
+.TP
+.IR type " (String)"
+The name of the file transfer mode in use,
+such as
+.RB `` binary ''
+or
+.RB `` ascii ''.
+.TP
+.IR verbose " (String/Integer)"
+Controls the amount of output spewed by the program.
+You can supply either the first character of the name of the
+verbosity level, or its number:
+.RS
+.TP
+.IR "Q" "uiet (\-1)"
+Won't print any output at all, even if an error occurs.
+.TP
+.IR "E" "rrors Only (0)"
+No output, except when errors occur.
+.TP
+.IR "T" "erse (1)"
+Prints errors, and useful output from the remote host.
+.TP
+.IR "V" "erbose (2)"
+Prints everything, even junk output from the remote end.
+.RE
+.\"-------
+.SH "Listing a remote directory"
+.\"-------
+The
+.I ls
+and
+.I dir
+commands perform in a similar manner to those of the
+stock
+.I ftp
+program.
+.PP
+The
+.I ls
+command sends the FTP command ``NLST'' for you.
+This command has been set so that it defaults
+to always listing files in columns (this is the
+.B \-C
+option given to the UNIX
+.I ls
+command) and appending
+metacharacters to each item name (this is the
+.B \-F
+option), so you can
+see which items are directories, files, links, etcetera.
+If you don't want
+your items columnized, you can try using the
+.B \-1
+option with
+.I ls
+to print one item per line.
+.PP
+The
+.I dir
+command sends the FTP command ``LIST'' for you, which instead
+of printing just item names, it prints item sizes, owners, dates, and
+permissions as well.
+This command is equivalent to
+.RB `` "ls \-l" ''
+on most remote systems.
+.PP
+The usage for both commands is the same.
+Here is the one for
+.IR ls :
+.PP
+.RS
+.B ls
+.RI [ \-flags ]
+.RI [ "directory and file names" ]
+.RI [ redirection ]
+.RE
+.PP
+Note that in this program, you can supply both flags and items to list in
+the same command.
+The stock version of
+.I ftp
+doesn't let you do this:
+.Ds
+ls \-lrt /info\-mac/help
+.De
+.PP
+Another thing that the program does which the others should have done is
+let you supply more than one item:
+.Ds
+ls \-lrt /info\-mac/help /pub /info\-mac/README
+.De
+.PP
+You can also redirect the output into a file, or pipe it into something.
+This example shows how to list the contents of the current remote directory,
+and save the output into a file in the current local directory:
+.Ds
+ls \-t >ls.out
+.De
+.PP
+Note that for this to work, there must be no whitespace between the ``>''
+and the filename, unlike your shell command line which allows for extra
+whitespace.
+This will be (actually, is) fixed in a future version of the
+program.
+.PP
+These examples show how to use a pipe:
+.Ds
+ls \-t |tail
+dir \-t "|less \-CM"
+ls \-t "|tail | wc"
+.De
+.PP
+Like the redirection example, there must be no whitespace between the first
+pipe character and the rest of the stuff.
+The trick is that it has to
+appear as one argument to the commands.
+The second and third examples
+illustrate the use of double quotes to squeeze extra parameters in.
+The second example can be done without all that typing.
+See the descriptions of the
+.I pdir
+and
+.I pls
+commands below.
+.\"-------
+.SH "Viewing a remote directory with your pager"
+.\"-------
+Didn't you hate it when you listed a remote directory, only to have most of
+the stuff scrolled off your terminal before you could read it?
+The
+.I pls
+and
+.I pdir
+commands take care of this for you.
+As you might have guessed,
+they perform exactly like their regular counterparts,
+only you view them with your pager.
+The pager to use is controlled by the
+.I pager
+program variable.
+.\"-------
+.SH "Redisplaying the last directory listing"
+.\"-------
+The program saves the listing into a local buffer,
+so if you need to see it again (probably forgot about
+.IR pdir )
+you can use the
+.I redir
+and
+.I predir
+commands for this.
+.\"-------
+.SH "Fetching files from the remote host"
+.\"-------
+The
+.I get
+and
+.I mget
+retrieve remote files for you.
+The usage for
+.I get
+is:
+.Ds
+get remote\-file [local\-file or redirection]
+.De
+.PP
+To fetch
+.B /pub/README
+and write it as a file named
+.BR ./junk/readme ,
+try:
+.Ds
+get /pub/README ./junk/readme
+.De
+.PP
+To fetch
+.B /pub/README
+and write it as
+.BR ./README ,
+just do:
+.Ds
+get /pub/README
+.De
+.PP
+This lets you fetch a file using its whole pathname, and write a copy of
+it in the current directory, without having to bother with typing a local
+filename.
+In the unlikely event that you have write permission to a
+directory called
+.B /pub
+on your local machine, it would write
+.RB `` README ''
+in that directory.
+.PP
+Most of the time the file you want will be in the current remote directory,
+so you can just do these:
+.Ds
+get README
+get README ./junk/readme
+.De
+.PP
+You can also use a redirection for
+.IR get ,
+just like you can with the
+.IR ls ", " dir ", and " redir
+commands.
+As described earlier, you have
+to conform to the format below for this release of the program:
+.Ds
+get README >/dev/null
+get README |head
+get README "|head \-8"
+get README "|less \-EMi"
+.De
+.PP
+The last example is facilitated by the
+.I page
+command described later.
+.PP
+The
+.I get
+command can also use a wildcard expression in an attempt to
+match exactly one remote file.
+I call it ``Poor Man's File Completion.''
+If you've done a remote listing, and you decide you want to download a
+file by the name of
+.RB `` obnoxiouslylongpackagename.tar.Z '',
+you can use
+``PMFC'' to save some keystrokes.
+Choose an expression that will only
+match that one file, then use it with
+.IR get :
+.Ds
+get obn*.Z a.tar.Z
+.De
+.PP
+If your pattern was unique,
+.I get
+will fetch that file only.
+If the pattern matched more than one file, the program will bitch and moan.
+.PP
+The
+.I mget
+command is used to fetch many files at a time.
+The difference between
+.I get
+and
+.I mget
+is that
+.I get
+lets you write only one file,
+but you can put it in a different directory, while
+.I mget
+fetches many files,
+always writing them in the current local directory.
+This example fetches several remote files at once:
+.Ds
+mget a.file.Z b.file.Z c.tar d.tar.Z
+.De
+.PP
+The
+.I mget
+command, and its ugly sisters,
+.I mput
+and
+.I mdelete
+let you use wildcard expressions.
+I could have done the previous example as:
+.Ds
+mget *.Z c.tar
+.De
+.PP
+instead.
+The ``m'' commands will verify each file,
+if you have the program variable
+.I mprompt
+set.
+.\"-------
+.SH "Viewing a remote file with your pager"
+.\"-------
+If you would like to read a file on the remote host without saving a copy
+of it on your machine, you can use the
+.I page
+(or
+.I more
+if you wish) command:
+.Ds
+page README
+page obn*README
+page README.Z
+.De
+.PP
+The second example show that you can use ``PMFC'' like you can for
+.IR get.
+The third example will work also, because if the program knows how to
+decompress the file, it will do so before feeding it to your pager.
+As stated earlier,
+you can change the program to use to page by setting the program variable
+.IR pager.
+.\"-------
+.SH "Creating a message file on the remote host"
+.\"-------
+Use the
+.I create
+an empty file on the remote site.
+Sometimes it is necessary to leave a note if you can't get in touch
+with the remote site's administrator.
+For example if a file is corrupted, you could try:
+.Ds
+create Foo.tar_is_corrupt
+.De
+.PP
+in hopes that the original uploader will replace it.
+.\"-------
+.SH "Looking up site names and addresses"
+.\"-------
+You can use the program's builtin
+.RI mini- nslookup
+facility.
+If you wanted to know the site's IP number, but only knew the name you
+could do:
+.Ds
+lookup ftp.cs.unl.edu
+.De
+.PP
+This would spit out IP number for that site, in this case ``129.93.1.12''.
+If you needed to know what a site's name was, but only knew the IP number,
+try:
+.Ds
+lookup 129.93.1.12
+.De
+.PP
+This would spit out the name for that site, in this case ``ftp.cs.unl.edu''.
+.\"-------
+.SH "Checking the configuration of the program"
+.\"-------
+Use the
+.I version
+command to print version and compilation information about the program.
+This will also tell you which optional features are
+compiled into the program, such as logging to the system log and which
+command line editor (if any) has been installed.
+.PP
+The author's email address is listed, and if you need to report something,
+send the output of this command along with your message.
+.\"-------
+.SH "Using the command shell"
+.\"-------
+Just like the stock
+.I ftp
+program, you type commands to it until you get
+bored and hit either ^D or type the
+.I quit
+command.
+.PP
+The program supports links to popular command line editing libraries.
+If the person who compiled it went to the effort, you will be able to
+edit the command line with arrow keys and other editing commands, and also
+scroll up and down in the command line history, usually with the up and
+down arrows.
+You can check the
+.I version
+command to see if either
+``GETLINE'' or ``READLINE'' are installed.
+.\"-------
+.SH "Customizing the prompt"
+.\"-------
+You can set the shell's prompt string to whatever you like.
+You can use several metacharacters that expand into something each prompt.
+The
+.RB `` % ''
+flags are passed to
+.IR strftime (3),
+so you can put the date or time in the prompt formatted as you like it:
+.Ds
+set prompt "%I:%M ncftp>"
+.De
+.PP
+That would insert the current time in the prompt.
+.PP
+The
+.RB `` @ ''
+flags are expanded by the program itself.
+Here's the list of them.
+.PP
+If you have an ANSI-compatible terminal, or you have the program variable
+.I ansi\-escapes
+set, you can use
+.BR @B ,
+.BR @I ,
+and
+.B @U
+to turn on boldface,
+inverse, and underline text respectively (otherwise they won't insert
+anything).
+You can also use
+.B @R
+to turn on inverse (reverse) text.
+.B @P
+sets the text back to plain text.
+.PP
+.B @D
+Inserts the full path of the current remote directory.
+The
+.B @J
+flag is similar except it inserts only the directory name.
+.PP
+.B @H
+Inserts the name of the remote host.
+.B @C
+inserts the host and current
+directory path in
+.I "colon-mode"
+format, such as
+``ftp.cs.unl.edu:/pub/mgleason'', or ``(not connected)''.
+The
+.B @c
+flag is similar, only it will insert ``ftp.cs.unl.edu:/pub/mgleason'' and a
+newline if connected, otherwise it prints nothing.
+The default prompt uses
+this flag to print a two line prompt when connected and a one line prompt
+when not connected.
+.PP
+.BR @E " or " @!
+inserts the event number (how many commands you've typed).
+.PP
+.B @M
+inserts ``(Mail)\0'' if mail has arrived since running the program.
+.PP
+.B @N
+inserts a newline character.
+.\"-------
+.SH "Keeping a log of your file transfers"
+.\"-------
+You can have the program keep a personal log file.
+I find it is useful so I can see where I got a certain file,
+or what the name of that site was I called two weeks ago.
+.PP
+To use a log, add:
+.Ds
+#set logfile ~/.ftplog
+.De
+.PP
+(or whatever you want to name the log) to your RC file.
+I don't want my log growing too large and using up all my disk space,
+so I also have:
+.Ds
+#set logsize 10240
+.De
+.PP
+in my RC file.
+If you set the limit on the maximum log size, the program will
+keep the log file at or below that size, discarding old entries.
+.PP
+Note that this is different from having SYSLOG appear in the
+.I version
+command's output.
+When this is on, your actions are recorded to the system
+log, so your system administrator can make sure you aren't doing anything
+``bad.''
+.\"-------
+.SH "Program options"
+.\"-------
+Remember that you can treat the command line like an
+.I open
+command,
+so all lowercase options are passed to the
+.I open
+command, and the
+uppercase options are handled by the main program.
+The uppercase options
+are described below; refer to the
+.I open
+command for descriptions of its options.
+.TP
+.BI \-D " x"
+sets the debugging level to
+.IR x .
+.TP
+.B \-H
+runs the
+.I version
+command and exits, so you can save the output of
+it to use when you need to mail me something.
+.TP
+.B \-I
+toggles the mprompt variable; this is provided for compatibility with
+.RB `` "ftp \-i" ''.
+.TP
+.B \-N
+disables reading of the RC file;
+this is provided for compatibility with
+.RB `` "ftp \-n" ''.
+.TP
+.B \-P
+toggle passive mode (defaults to off). Useful for work behind firewalls.
+.TP
+.BI \-V " x"
+sets verbosity to level
+.I x
+.RB ( \-1 ,
+.BR 0 ,
+.BR 1 ,
+.BR 2 )
+or
+.RB ( quiet ,
+.BR errs ,
+.BR terse ,
+.BR verbose ).
+See the description of the
+.I verbose
+program variable for more information.
+.PP
+Here are some example command lines.
+Again, see the description of the
+.I open
+command (especially
+.IR "colon-mode" " and " "FTP\-cat mode" ")"
+and all its functions for more information.
+.PP
+This just enters the
+.I NcFTP
+command shell:
+.Ds
+csh> ncftp
+.De
+.PP
+This fetches
+.B CONTENTS
+and then quits:
+.Ds
+csh> ncftp ftp.cs.unl.edu:/pub/mgleason/CONTENTS
+.De
+.PP
+Some others examples, with open options and main program options mixed in:
+.Ds
+csh> ncftp \-V quiet \-u ftp.unl.edu
+csh> ncftp \-c ftp.cs.unl.edu:/pub/mgleason/CONTENTS
+csh> ncftp \-D 2 \-r \-d 120 \-g 10 \-N ftp.unl.edu
+.De
+.\"-------
+.SH "A sample RC file"
+.\"-------
+Here is a sample RC file:
+.ta 6m +6m
+.Ds
+#set logfile ~/.ftplog
+#set progress\-reports 2
+#set local\-dir /usr/tmp/zz
+#set prompt "@B@E @UNcFTP@P @B@M@D@P \->"
+.sp
+machine sumex\-aim.stanford.edu
+ macdef init
+ cd /info\-mac
+ get ./help/recent\-files.txt "|grep \-v '.abs' > sumex"
+ !less sumex
+ pwd
+.sp
+# This site is in here just so I can use ``apple''
+# as an abbreviation.
+machine ftp.apple.com
+.sp
+# NcFTP will only ask for your password:
+machine ftp.cs.unl.edu
+ login mgleason
+.sp
+# You can supply a login and a password:
+machine fake.machine.unl.edu
+ login mgleason
+ password mypass
+ macdef init
+ cd ./foo/bar
+.sp
+# If an antiquated non-UNIX machine doesn't use
+# the "SYST" command, you may need to unset
+# remote\-is\-unix, if the remote host complains
+# about ``ls \-CF''.
+machine some.vms.unl.edu
+ macdef init
+ unset remote\-is\-unix
+.sp
+.De
+.\"-------
+.SH "AUTHORS"
+.\"-------
+.I NcFTP
+was written by Mike Gleason,
+.I NCEMRSoft
+(mgleason@cse.unl.edu), and based on code by the authors of the
+.I ftp
+from the BSD 4.3 distribution.
+.I NcFTP
+is copyrighted 1992, 1993 by NCEMRSoft
+and 1985, 1989 by the Regents of California.
+.PP
+Ideas and some code contributed by Phil Dietz,
+.IR NCEMRSoft "."
+Testing and debugging done by Phil and
+Kok Hon Yin
+.PP
+Extensive man page formatting work
+by DaviD W. Sanderson (dws@ssec.wisc.edu).
+.PP
+As of this writing, the most recent version is archived in
+/pub/ncftp, on
+.IR "ftp.cs.unl.edu" "."
+.\"-------
+.SH "BUGS"
+.\"-------
+Correct execution of many commands depends upon proper behavior
+by the remote server.
+.PP
+The remote server may drop the connection if you take a long time to
+page remote files.
+.PP
+Termcap padding is not correctly displayed.
+.PP
+There are no such sites named
+.I bowser.nintendo.co.jp
+or
+.IR sphygmomanometer.unl.edu .
+.\"-------
+.SH "SEE ALSO"
+.\"-------
+.IR strftime (3),
+.IR ftpd (8),
+.IR ftp (1),
+.IR nslookup (8),
+.IR compress (1),
+.IR gzip (1),
+.IR zcat (1),
+.IR fsp (1),
+.IR archie (1),
+.IR tftp (1).
diff --git a/usr.bin/ncftp/open.c b/usr.bin/ncftp/open.c
new file mode 100644
index 0000000..5836da6
--- /dev/null
+++ b/usr.bin/ncftp/open.c
@@ -0,0 +1,644 @@
+/* open.c */
+
+/* $RCSfile: open.c,v $
+ * $Revision: 1.1 $
+ * $Date: 93/07/09 11:27:07 $
+ */
+
+#include "sys.h"
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+
+#include <errno.h>
+
+#include "util.h"
+#include "open.h"
+#include "cmds.h"
+#include "ftp.h"
+#include "ftprc.h"
+#include "main.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* open.c globals */
+int remote_is_unix; /* TRUE if remote host is unix. */
+int auto_binary = dAUTOBINARY;
+int anon_open = dANONOPEN;
+ /* Anonymous logins by default? */
+int connected = 0; /* TRUE if connected to server */
+ /* If TRUE, set binary each connection. */
+int www = 0; /* TRUE if use URL */
+Hostname hostname; /* Name of current host */
+RemoteSiteInfo gRmtInfo;
+#ifdef GATEWAY
+string gateway; /* node name of firewall gateway */
+string gate_login; /* login at firewall gateway */
+#endif
+
+/* open.c externs */
+extern char *reply_string, *line, *Optarg, *margv[];
+extern int Optind, margc, verbose, macnum;
+extern long eventnumber;
+extern struct servent serv;
+extern FILE *cout;
+extern string anon_password;
+
+/* Given a pointer to an OpenOptions (structure containing all variables
+ * that can be set from the command line), this routine makes sure all
+ * the variables have valid values by setting them to their defaults.
+ */
+
+void InitOpenOptions(OpenOptions *openopt)
+{
+ /* How do you want to open a site if neither -a or -u are given?
+ * anon_open is true (default to anonymous login), unless
+ * defaults.h was edited to set dANONOPEN to 0 instead.
+ */
+ openopt->openmode = anon_open ? openImplicitAnon : openImplicitUser;
+
+ /* Normally you don't want to ignore the entry in your netrc. */
+ openopt->ignore_rc = 0;
+
+ /* Set the default delay if the user specifies redial mode without
+ * specifying the redial delay.
+ */
+ openopt->redial_delay = dREDIALDELAY;
+
+ /* Normally, you only want to try once. If you specify redial mode,
+ * this is changed.
+ */
+ openopt->max_dials = 1;
+
+ /* You don't want to cat the file to stdout by default. */
+ openopt->ftpcat = NO_FTPCAT;
+
+ /* Setup the port number to try. */
+#ifdef dFTP_PORT
+ /* If dFTP_PORT is defined, we use a different port number by default
+ * than the one supplied in the servent structure.
+ */
+ openopt->port = dFTP_PORT;
+ /* Make sure the correct byte order is supplied! */
+ openopt->port = htons(openopt->port);
+#else
+ /* Use the port number supplied by the operating system's servent
+ * structure.
+ */
+ openopt->port = serv.s_port;
+#endif
+
+ /* We are not in colon-mode (yet). */
+ openopt->colonmodepath[0] = 0;
+
+ /* Set the hostname to a null string, since there is no default host. */
+ openopt->hostname[0] = 0;
+
+ /* Set the opening directory path to a null string. */
+ openopt->cdpath[0] = 0;
+} /* InitOpenOptions */
+
+
+
+
+/* This is responsible for parsing the command line and setting variables
+ * in the OpenOptions structure according to the user's flags.
+ */
+
+int GetOpenOptions(int argc, char **argv, OpenOptions *openopt)
+{
+ int opt;
+ char *cp, *hostp, *cpath;
+
+ /* First setup the openopt variables. */
+ InitOpenOptions(openopt);
+
+ /* Tell Getopt() that we want to start over with a new command. */
+ Getopt_Reset();
+ while ((opt = Getopt(argc, argv, "aiup:rd:g:cm")) >= 0) {
+ switch (opt) {
+ case 'a':
+ /* User wants to open anonymously. */
+ openopt->openmode = openExplicitAnon;
+ break;
+
+ case 'u':
+ /* User wants to open with a login and password. */
+ openopt->openmode = openExplicitUser;
+ break;
+
+ case 'i':
+ /* User wants to ignore the entry in the netrc. */
+ openopt->ignore_rc = 1;
+ break;
+
+ case 'p':
+ /* User supplied a port number different from the default
+ * ftp port.
+ */
+ openopt->port = atoi(Optarg);
+ if (openopt->port <= 0) {
+ /* Probably never happen, but just in case. */
+ (void) printf("%s: bad port number (%s).\n", argv[0], Optarg);
+ goto usage;
+ }
+ /* Must ensure that the port is in the correct byte order! */
+ openopt->port = htons(openopt->port);
+ break;
+
+ case 'd':
+ /* User supplied a delay (in seconds) that differs from
+ * the default.
+ */
+ openopt->redial_delay = atoi(Optarg);
+ break;
+
+ case 'g':
+ /* User supplied an upper-bound on the number of redials
+ * to try.
+ */
+ openopt->max_dials = atoi(Optarg);
+ break;
+
+ case 'r':
+ openopt->max_dials = -1;
+ break;
+
+ case 'm':
+ /* ftpcat mode is only available from your shell command-line,
+ * not from the ncftp shell. Do that yourself with 'more zz'.
+ */
+ if (eventnumber == 0L) {
+ /* If eventnumber is zero, then we were called directly
+ * from main(), and before the ftp shell has started.
+ */
+ openopt->ftpcat = FTPMORE;
+ /* ftpcat mode is really ftpmore mode. */
+ break;
+ } else {
+ fprintf(stderr,
+"You can only use this form of colon-mode (-m) from your shell command line.\n\
+Try 'ncftp -m wuarchive.wustl.edu:/README'\n");
+ goto usage;
+ }
+ /* break; */
+
+ case 'c':
+ /* ftpcat mode is only available from your shell command-line,
+ * not from the ncftp shell. Do that yourself with 'get zz -'.
+ */
+ if (eventnumber == 0L) {
+ /* If eventnumber is zero, then we were called directly
+ * from main(), and before the ftp shell has started.
+ */
+ openopt->ftpcat = FTPCAT;
+ break;
+ } else {
+ fprintf(stderr,
+"You can only use ftpcat/colon-mode from your shell command line.\n\
+Try 'ncftp -c wuarchive.wustl.edu:/README > file.'\n");
+ goto usage;
+ }
+ /* break; */
+
+ default:
+ usage:
+ return USAGE;
+ }
+ }
+
+ if (argv[Optind] == NULL) {
+ /* No host was supplied. Print out the list of sites we know
+ * about and ask the user for one.
+ */
+ PrintSiteList();
+ (void) Gets("(site to open) ", openopt->hostname, sizeof(openopt->hostname));
+ /* Make sure the user just didn't hit return, in which case we
+ * just give up and go home.
+ */
+ if (openopt->hostname[0] == 0)
+ goto usage;
+ } else {
+ /* The user gave us a host to open.
+ *
+ * First, check to see if they gave us a colon-mode path
+ * along with the hostname. We also understand a WWW path,
+ * like "ftp://bang.nta.no/pub/fm2html.v.0.8.4.tar.Z".
+ */
+ hostp = argv[Optind];
+ cpath = NULL;
+ if ((cp = index(hostp, ':')) != NULL) {
+ *cp++ = '\0';
+ cpath = cp;
+ www = 0; /* Is 0 or 1, depending on the type of path. */
+ if ((*cp == '/') && (cp[1] == '/')) {
+ /* First make sure the path was intended to be used
+ * with ftp and not one of the other URLs.
+ */
+ if (strcmp(argv[Optind], "ftp")) {
+ fprintf(
+ stderr,
+ "Bad URL '%s' -- WWW paths must be prefixed by 'ftp://'.\n",
+ argv[Optind]
+ );
+ goto usage;
+ }
+
+ cp += 2;
+ hostp = cp;
+ cpath = NULL; /* It could have been ftp://hostname only. */
+
+ if ((cp = index(hostp, '/')) != NULL) {
+ *cp++ = '\0';
+ cpath = cp;
+ }
+ www = 1;
+ }
+ if (cpath != NULL) {
+ (void) Strncpy(openopt->colonmodepath, www ? "/" : "");
+ (void) Strncat(openopt->colonmodepath, cpath);
+ dbprintf("Colon-Mode Path = '%s'\n", openopt->colonmodepath);
+ }
+ }
+ (void) Strncpy(openopt->hostname, hostp);
+ dbprintf("Host = '%s'\n", hostp);
+ }
+ return NOERR;
+} /* GetOpenOptions */
+
+
+
+
+/* This examines the format of the string stored in the hostname
+ * field of the OpenOptions, and sees if has to strip out a colon-mode
+ * pathname (to store in the colonmodepath field). Since colon-mode
+ * is run quietly (without any output being generated), we init the
+ * login_verbosity variable here to quiet if we are running colon-mode.
+ */
+int CheckForColonMode(OpenOptions *openopt, int *login_verbosity)
+{
+ /* Usually the user doesn't supply hostname in colon-mode format,
+ * and wants to interactively browse the remote host, so set the
+ * login_verbosity to whatever it is set to now.
+ */
+ *login_verbosity = verbose;
+
+ if (openopt->colonmodepath[0] != 0) {
+ /* But if the user does use colon-mode, we want to do our business
+ * and leave, without all the login messages, etc., so set
+ * login_verbosity to quiet so we won't print anything until
+ * we finish. Colon-mode can be specified from the shell command
+ * line, so we would like to be able to execute ncftp as a one
+ * line command from the shell without spewing gobs of output.
+ */
+ *login_verbosity = V_QUIET;
+ } else if (openopt->ftpcat != 0) {
+ /* User specified ftpcat mode, but didn't supply the host:file. */
+ (void) fprintf(stderr, "You didn't use colon mode correctly.\n\
+If you use -c or -m, you need to do something like this:\n\
+ ncftp -c wuarchive.wustl.edu:/pub/README (to cat this file to stdout).\n");
+ return USAGE;
+ }
+ return NOERR;
+} /* CheckForColonMode */
+
+
+
+
+/* All this short routine does is to hookup a socket to either the
+ * remote host or the firewall gateway host.
+ */
+int HookupToRemote(OpenOptions *openopt)
+{
+ int hErr;
+
+#ifdef GATEWAY
+ /* Try connecting to the gateway host. */
+ if (*gateway) {
+ hErr = hookup(gateway, openopt->port);
+ (void) Strncpy(hostname, openopt->hostname);
+ } else
+#endif
+ hErr = hookup(openopt->hostname, openopt->port);
+
+ return hErr;
+} /* HookupToRemote */
+
+
+
+
+void CheckRemoteSystemType(int force_binary)
+{
+ int tmpverbose;
+ char *cp, c;
+
+ /* As of this writing, UNIX is pretty much standard. */
+ remote_is_unix = 1;
+
+ /* Do a SYSTem command quietly. */
+ tmpverbose = verbose;
+ verbose = V_QUIET;
+ if (command("SYST") == COMPLETE) {
+ if (tmpverbose == V_VERBOSE) {
+ /* Find the system type embedded in the reply_string,
+ * and separate it from the rest of the junk.
+ */
+ cp = index(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = index(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ (void) printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ remote_is_unix = !strncmp(reply_string + 4, "UNIX", (size_t) 4);
+ }
+
+ /* Set to binary mode if any of the following are true:
+ * (a) The user has auto-binary set;
+ * (b) The user is using colon-mode (force_binary);
+ * (c) The reply-string from SYST said it was UNIX with 8-bit chars.
+ */
+ if (auto_binary || force_binary
+ || !strncmp(reply_string, "215 UNIX Type: L8", (size_t) 17)) {
+ (void) _settype("binary");
+ if (tmpverbose > V_TERSE)
+ (void) printf("Using binary mode to transfer files.\n");
+ }
+
+ /* Print a warning for that (extremely) rare Tenex machine. */
+ if (tmpverbose >= V_ERRS &&
+ !strncmp(reply_string, "215 TOPS20", (size_t) 10)) {
+ (void) _settype("tenex");
+ (void) printf("Using tenex mode to transfer files.\n");
+ }
+ verbose = tmpverbose;
+} /* CheckRemoteSystemType */
+
+
+
+/* This is called if the user opened the host with a file appended to
+ * the host's name, like "wuarchive.wustl.edu:/pub/readme," or
+ * "wuarchive.wustl.edu:/pub." In the former case, we open wuarchive,
+ * and fetch "readme." In the latter case, we open wuarchive, then set
+ * the current remote directory to "/pub." If we are fetching a file,
+ * we can do some other tricks if "ftpcat mode" is enabled. This mode
+ * must be selected from your shell's command line, and this allows you
+ * to use the program as a one-liner to pipe a remote file into something,
+ * like "ncftp -c wu:/pub/README | wc." If the user uses ftpcat mode,
+ * the program immediately quits instead of going into it's own command
+ * shell.
+ */
+void ColonMode(OpenOptions *openopt)
+{
+ int tmpverbose;
+ int cmdstatus;
+
+ /* How do we tell if colonmodepath is a file or a directory?
+ * We first try cd'ing to the path first. If we can, then it
+ * was a directory. If we could not, we'll assume it was a file.
+ */
+
+ /* Shut up, so cd won't print 'foobar: Not a directory.' */
+ tmpverbose = verbose;
+ verbose = V_QUIET;
+
+ /* If we are using ftpcat|more mode, or we couldn't cd to the
+ * colon-mode path (then it must be a file to fetch), then
+ * we need to fetch a file.
+ */
+ if (openopt->ftpcat || ! _cd(openopt->colonmodepath)) {
+ /* We call the appropriate fetching routine, so we have to
+ * have the argc and argv set up correctly. To do this,
+ * we just make an entire command line, then let makeargv()
+ * convert it to argv/argc.
+ */
+ if (openopt->ftpcat == FTPCAT)
+ (void) sprintf(line, "get %s -", openopt->colonmodepath);
+ else if (openopt->ftpcat == FTPMORE)
+ (void) sprintf(line, "more %s", openopt->colonmodepath);
+ else {
+ /* Regular colon-mode, where we fetch the file, putting the
+ * copy in the current local directory.
+ */
+ (void) sprintf(line, "mget %s", openopt->colonmodepath);
+ }
+ makeargv();
+
+ /* Turn on messaging if we aren't catting. */
+ if (openopt->ftpcat == 0)
+ verbose = tmpverbose;
+
+ /* get() also handles 'more'. */
+ if (openopt->ftpcat)
+ cmdstatus = get(margc, margv);
+ else
+ cmdstatus = mget(margc, margv);
+
+ /* If we were invoked from the command line, quit
+ * after we got this file.
+ */
+ if (eventnumber == 0L) {
+ (void) quit(cmdstatus == CMDERR ? -1 : 0, NULL);
+ }
+ }
+ verbose = tmpverbose;
+} /* ColonMode */
+
+
+
+
+/* Given a properly set up OpenOptions, we try connecting to the site,
+ * redialing if necessary, and do some initialization steps so the user
+ * can send commands.
+ */
+int Open(OpenOptions *openopt)
+{
+ int hErr;
+ int dials;
+ char *ruser, *rpass, *racct;
+ int siteInRC;
+ char *user, *pass, *acct;
+ int login_verbosity, oldv;
+ int result = CMDERR;
+
+ macnum = 0; /* Reset macros. */
+
+ /* If the hostname supplied is in the form host.name.str:/path/file,
+ * then colon mode was used, and we need to fix the hostname to be
+ * just the hostname, copy the /path/file to colonmode path, and init
+ * the login_verbosity variable.
+ */
+ if (CheckForColonMode(openopt, &login_verbosity) == USAGE)
+ return USAGE;
+
+ /* If the hostname supplied was an abbreviation, such as just
+ * "wu" (wuarchive.wustl.edu), look through the list of sites
+ * we know about and get the whole name. We also would like
+ * the path we want to start out in, if it is available.
+ */
+ GetFullSiteName(openopt->hostname, openopt->cdpath);
+
+#ifdef GATEWAY
+ /* Make sure the gateway host name is a full name and not an
+ * abbreviation.
+ */
+ if (*gateway)
+ GetFullSiteName(gateway, NULL);
+#endif
+
+ ruser = rpass = racct = NULL;
+ /* This also loads the init macro. */
+ siteInRC = ruserpass2(openopt->hostname, &ruser, &rpass, &racct);
+ if (ISANONOPEN(openopt->openmode)) {
+ user = "anonymous";
+ pass = anon_password;
+ } else {
+ user = NULL;
+ pass = NULL;
+ }
+ acct = NULL;
+
+ if (siteInRC && !openopt->ignore_rc) {
+ acct = racct;
+ if (ruser != NULL) {
+ /* We were given a username. If we were given explicit
+ * instructions from the command line, follow those and
+ * ignore what the RC had. Otherwise if no -a or -u
+ * was specified, we use whatever was in the RC.
+ */
+ if (ISIMPLICITOPEN(openopt->openmode)) {
+ user = ruser;
+ pass = rpass;
+ }
+ }
+ }
+
+ for (
+ dials = 0;
+ openopt->max_dials < 0 || dials < openopt->max_dials;
+ dials++)
+ {
+ if (dials > 0) {
+ /* If this is the second dial or higher, sleep a bit. */
+ (void) sleep(openopt->redial_delay);
+ (void) fprintf(stderr, "Retry Number: %d\n", dials + 1);
+ }
+
+ if ((hErr = HookupToRemote(openopt)) == -2)
+ /* Recoverable, so we can try re-dialing. */
+ continue;
+ else if (hErr == NOERR) {
+ /* We were hookup'd successfully. */
+ connected = 1;
+
+ oldv = verbose; verbose = login_verbosity;
+
+#ifdef GATEWAY
+ if (*gateway) {
+ if ((Login(
+ user,
+ pass,
+ acct,
+ (!openopt->ignore_rc && !openopt->colonmodepath[0])
+ ) != NOERR) || cout == NULL)
+ goto nextdial; /* error! */
+ }
+#endif
+
+#ifdef GATEWAY
+ if (!*gateway) {
+#endif
+ /* We don't want to run the init macro for colon-mode. */
+ if ((Login(
+ user,
+ pass,
+ acct,
+ (!openopt->ignore_rc && !openopt->colonmodepath[0])
+ ) != NOERR) || cout == NULL)
+ {
+ goto nextdial; /* error! */
+ }
+#ifdef GATEWAY
+ }
+#endif
+
+ verbose = oldv;
+
+ /* We need to check for unix and see if we should set binary
+ * mode automatically.
+ */
+ CheckRemoteSystemType(openopt->colonmodepath[0] != (char)0);
+
+ if (openopt->colonmodepath[0]) {
+ ColonMode(openopt);
+ } else if (openopt->cdpath[0]) {
+ /* If we didn't have a colon-mode path, we try setting
+ * the current remote directory to cdpath. cdpath is
+ * usually the last directory we were in the previous
+ * time we called this site.
+ */
+ (void) _cd(openopt->cdpath);
+ } else {
+ /* Freshen 'cwd' variable for the prompt.
+ * We have to do atleast one 'cd' so our variable
+ * cwd (which is saved by _cd()) is set to something
+ * valid.
+ */
+ (void) _cd(NULL);
+ }
+ result = NOERR;
+ break; /* we are connected, so break the redial loop. */
+ /* end if we are connected */
+ } else {
+ /* Irrecoverable error, so don't bother redialing. */
+ /* The error message should have already been printed
+ * from Hookup().
+ */
+ break;
+ }
+nextdial:
+ disconnect(0, NULL);
+ continue; /* Try re-dialing. */
+ }
+ return (result);
+} /* Open */
+
+
+
+/* This stub is called by our command parser. */
+int cmdOpen(int argc, char **argv)
+{
+ OpenOptions openopt;
+ int result = NOERR;
+
+ /* If there is already a site open, close that one so we can
+ * open a new one.
+ */
+ if (connected && NOT_VQUIET && hostname[0]) {
+ (void) printf("Closing %s...\n", hostname);
+ (void) disconnect(0, NULL);
+ }
+
+ /* Reset the remote info structure for the new site we want to open.
+ * Assume we have these properties until we discover otherwise.
+ */
+ gRmtInfo.hasSIZE = 1;
+ gRmtInfo.hasMDTM = 1;
+
+ if ((GetOpenOptions(argc, argv, &openopt) == USAGE) ||
+ ((result = Open(&openopt)) == USAGE))
+ return USAGE;
+ /* Return an error if colon-mode/URL didn't work. */
+ return (openopt.colonmodepath[0] != '\0' ? result : NOERR);
+} /* cmdOpen */
+
+/* eof open.c */
diff --git a/usr.bin/ncftp/open.h b/usr.bin/ncftp/open.h
new file mode 100644
index 0000000..7be8ccb
--- /dev/null
+++ b/usr.bin/ncftp/open.h
@@ -0,0 +1,50 @@
+/* open.h */
+
+#ifndef _open_h_
+#define _open_h_ 1
+
+/* Variables for Open() that can be changed from the command line. */
+typedef struct OpenOptions {
+ int openmode;
+ int ignore_rc;
+ unsigned int port;
+ int redial_delay;
+ int max_dials;
+ int ftpcat;
+ Hostname hostname;
+ longstring cdpath;
+ longstring colonmodepath;
+} OpenOptions;
+
+typedef struct RemoteSiteInfo {
+ int hasSIZE;
+ int hasMDTM;
+} RemoteSiteInfo;
+
+/* Open modes. */
+#define openImplicitAnon 1
+#define openImplicitUser 4
+#define openExplicitAnon 3
+#define openExplicitUser 2
+
+#define ISUSEROPEN(a) ((a==openImplicitUser)||(a==openExplicitUser))
+#define ISANONOPEN(a) (!ISUSEROPEN(a))
+#define ISEXPLICITOPEN(a) ((a==openExplicitAnon)||(a==openExplicitUser))
+#define ISIMPLICITOPEN(a) (!ISEXPLICITOPEN(a))
+
+/* ftpcat modes. */
+#define NO_FTPCAT 0
+#define FTPCAT 1
+#define FTPMORE 2
+
+/* Protos: */
+void InitOpenOptions(OpenOptions *openopt);
+int GetOpenOptions(int argc, char **argv, OpenOptions *openopt);
+int CheckForColonMode(OpenOptions *openopt, int *login_verbosity);
+int HookupToRemote(OpenOptions *openopt);
+void CheckRemoteSystemType(int);
+void ColonMode(OpenOptions *openopt);
+int Open(OpenOptions *openopt);
+int cmdOpen(int argc, char **argv);
+
+#endif /* _open_h_ */
diff --git a/usr.bin/ncftp/patchlevel.h b/usr.bin/ncftp/patchlevel.h
new file mode 100644
index 0000000..d655b14
--- /dev/null
+++ b/usr.bin/ncftp/patchlevel.h
@@ -0,0 +1,156 @@
+v1.9.4 - April 15, 1995. Using PORT by default instead of PASV by default.
+ Method to get the mail pathname changed.
+
+v1.9.3 - March 5, 1995. Support for NetBSD and DELL added. Linger works
+ with passive mode now.
+
+v1.9.2 - January 20, 1995. Another passive mode fix with the SOCKS library.
+ Trying to avoid going into the interactive shell if colon-mode fails.
+
+v1.9.1 - January 1, 1995. Passive mode fix with the SOCKS library.
+
+v1.9.0 - December 22, 1994. The program won't exit from the interactive shell
+ if it's working from a tty. For example, it won't exit if you do an mget
+ on a pattern that won't match anything. Added padding around jmp_buf's
+ for SunOS. SunOS needs sigjmp_buf's, but plenty of OS's don't support
+ sigjmp_buf's yet. Fixed the tips to reflect the new archive site.
+
+v1.8.9 - December 20, 1994. Can now set "passive" user variable, or use
+ passive command to toggle PASV/PORT ftp. Debug mode now prints remote
+ responses. Can now get around buggy FTP servers like boombox.micro.umn.edu,
+ that give back invalid port numbers to PASV.
+
+v1.8.8 - December 19, 1994. Now falls back to port FTP if passive FTP fails.
+
+v1.8.7 - December 11, 1994. Tweaks for FreeBSD. Passive mode enabled and
+ turned on by default.
+
+v1.8.6 - October 30, 1994. Tweaks for Solaris in sys.h.
+
+v1.8.5 - September 20, 1994. Better(?) support for term.
+
+v1.8.4 - September 19, 1994. Bug in Makefile fixed. Bug involving getwd
+ fixed.
+
+v1.8.3 - August 27, 1994. Bug fixed where failed connection attempts when
+ using a numeric IP address would fail.
+
+v1.8.2 - August 4, 1994. Can compile with DONT_TIMESTAMP to turn off syncing
+ timestamps of fetched files with their remote counterparts. IP_TOS now
+ utilized.
+
+v1.8.1 - July 4, 1994. Forgot <signal.h> in ftprc.c.
+
+v1.8.0 - July 4, 1994. Tweak for DEC OSF/1. NO_FORMATTING added.
+ Support for QNX added. Reporting an error if the recent file could
+ not be written. Bumped up the max recents to 50; the open menu will
+ now be fed through your pager to avoid the problem of scrolling off
+ screen. Fixed problem with redialing and running out of descriptors.
+
+v1.7.8 - June 30, 1994. No longer defining TERMH for linux.
+
+v1.7.7 - June 21, 1994. Deleted a space in front of an " #endif".
+ No functionality change whatsoever...
+
+v1.7.6 - June 18, 1994. Added commands and code to support the
+ PASV command for passive negotiation of the data connection from
+ the host server to the client. This facilitates operation of the
+ client software from within a firewall. (J. B. Harrell)
+
+v1.7.5 - May 28, 1994. Fixed a rare problem with dimmed text. Fixed
+ compilation problem with Dynix. Defining the domain name now takes
+ precedence over the getdomainname() function.
+
+v1.7.4 - May 16, 1994. Tweaked hookup() a bit, to (try to) handle
+ hosts with multiple addresses better. Fixed error with GMT offsets.
+ Fixed 'addr_t' typo in SVR4 section. Moved SVR4 block down in sys.h.
+
+v1.7.3 - April 13, 1994. Fixed minor error in syslog reporting.
+ Trying both getpwnam() and getpwuid(), instead of just one of them,
+ increasing the probability the program can get your passwd entry.
+ Better compatibility with other types of password input devices.
+
+v1.7.2 - April 5, 1994. Bytes/sec is now correct. Fixed error when
+ NO_VARARGS was defined. Added support for <varargs.h>.
+
+v1.7.1 - March 27, 1994. Defining HAS_DOMAINNAME for NeXT. Term hack can
+ share sockets, plus some term stuff added to the Makefile. Trimmed
+ some old stuff from the patchlevel.h file, and putting new versions
+ first now. Smarter about determining abbreviations from local hostnames.
+ Fixed bug where progress meter would go beserk after trying to get
+ a non-existant file.
+
+v1.7.0 - March 14, 1994. More verbose when logging to the system log,
+ and making sure that syslog() itself is called with a total of 5
+ or less parameters. Official patch posted which incorporates all
+ the fixes to 1.6.0 (i.e. 1.6.1, 1.6.2, ... 1.6.9).
+
+v1.6.9 - March 11, 1994. Added DOMAIN_NAME and Solaris CPP symbols.
+ Better handling of getting the domain name, specifically with SunOS.
+ BSDi support added.
+
+v1.6.8 - March 4, 1994. Ensuring that tmp files are not public.
+ Trying harder to get the real hostname, and fixed problem with
+ disappearing progress meters (both due to T. Lindgren).
+
+v1.6.7 - February 20, 1994. Using getpwnam() instead of getpwuid().
+ Supporting WWW paths (i.e. ftp://host.name/path/name).
+
+v1.6.6 - February 15, 1994. Prevented scenario of fclosing a NULL FILE *.
+ Edited term ftp's hookup() a little. More defs for linux in sys.h.
+ Not updating a recent entry unless you were fully logged in.
+
+v1.6.5 - January 6, 1994. Fixed error with an #ifndef/#endif block having
+ whitespace before the #. No longer confirming "ls >file" actions.
+ Changed echo() to Echo(). AIX 3 uses TERMIOS.
+
+v1.6.4 - December 30, 1993. Fixed rare problem with GetDateAndTime.
+ confirm() will return true if you're running the init macro.
+
+v1.6.3 - December 28, 1993. Added a new diagnostic command, memchk,
+ to print stats from a special malloc library if you used one.
+ Using SIZE and MDTM when the remote site supports it. Using a new
+ set of routines for term (again).
+
+v1.6.2 - December 10, 1993.
+ Term hack no longer depends on the PASV command (!). The BROKEN_MEMCPY
+ problem worked-around. More wary of symbolic-link recursion.
+ Fixed local path expander. Fixed inadvertant flushing of the typeahead
+ buffer. Debug mode won't print your password. Progress meters
+ no longer goof up when the file is huge. Added time-remaining to the
+ Philbar.
+
+v1.6.1 - November 5, 1993.
+ Checking if we have permission to write over a file to fetch.
+ A few very minor changes. BSD no longer trying to use strchr :-)
+
+v1.6.0 - October 31, 1993.
+ Added "term" support for Linux users. Better SCO Xenix support. Added
+ -DLINGER, if you have a connection requiring it (so 'puts' to the remote
+ system complete). Added -DNET_ERRNO_H if you need to include
+ <net/errno.h>. Including more headers in sys.h. Fixed another globulize
+ bug. Fixed a bug in confirm() where prompt was overwriting a str32.
+ Added -DNO_CURSES_H if you do not want to try and include <curses.h>.
+ Added -DHAS_GETCWD (usually automatic) and HAS_DOMAINNAME. Logins as
+ "ftp" act like "anonymous." Fixed bug with "open #x". Making sure you
+ defined GZCAT as a string. Turning off termcap attributes one by one,
+ instead of using the turn-off-all-attributes. A few fixes for the man
+ page, including documentation of the progress-meter types. AIX 2.x,
+ AIX 3.x, ISC Unix, Dynix/PTX, and Besta support added to sys.h. Safer
+ use of getwd(). Colon-mode is quieter. Getuserinfo function tweaked.
+ Eliminated unnecessary GetHomeDir function in util.c. Reworked Gets(),
+ since it wasn't always stripping \n's. Recent file can now read dir
+ names with whitespace. Opening msg uses a larger buffer, because of
+ escape codes. Philbar now prints K/sec stats.
+
+v1.5.6 - September 20, 1993...
+v1.5.5 - September 16, 1993...
+v1.5.4 - September 14, 1993...
+v1.5.3 - September 2, 1993...
+v1.5.2 - August 30, 1993...
+v1.5.1 - August 29, 1993...
+v1.5.0 - August 22, 1993...
+
+v1.0.2 - Jan 17, 1993...
+v1.0.1 - December 8, 1992...
+v1.0.0 - December 6, 1992. Initial release.
diff --git a/usr.bin/ncftp/set.c b/usr.bin/ncftp/set.c
new file mode 100644
index 0000000..9ce8786
--- /dev/null
+++ b/usr.bin/ncftp/set.c
@@ -0,0 +1,368 @@
+/* Set.c */
+
+/* $RCSfile: set.c,v $
+ * $Revision: 14020.12 $
+ * $Date: 93/07/09 11:45:48 $
+ */
+
+#include "sys.h"
+
+#include <ctype.h>
+
+#include "util.h"
+#include "cmds.h"
+#include "main.h"
+#include "set.h"
+#include "defaults.h"
+#include "copyright.h"
+
+#ifdef TERM_FTP
+extern int compress_toggle;
+#endif
+
+/* Set.c globals: */
+char *verbose_msgs[4] = {
+ "Not printing anything.\n",
+ "Only printing necessary error messages.\n",
+ "Printing error messages and announcements from the remote host.\n",
+ "Printing all messages, errors, acknowledgments, and announcements.\n"
+};
+
+char *short_verbose_msgs[4] = {
+ "Quiet (-1)",
+ "Errors Only (0)",
+ "Terse (1)",
+ "Verbose (2)"
+};
+
+string vstr;
+
+/* Set.c externs: */
+extern int progress_meter, connected;
+extern int parsing_rc, keep_recent;
+extern string pager, anon_password, prompt;
+extern str32 curtypename;
+extern long logsize;
+extern FILE *logf;
+extern longstring rcname, logfname, lcwd;
+extern int auto_binary, ansi_escapes, debug;
+extern int mprompt, remote_is_unix, verbose;
+extern int startup_msg, anon_open, passivemode;
+#ifndef NO_TIPS
+extern int tips;
+#endif
+#ifdef GATEWAY
+extern string gateway, gate_login;
+#endif
+
+/* The variables must be sorted in alphabetical order, or else
+ * match_var() will choke.
+ */
+struct var vars[] = {
+ VARENTRY("anon-open", BOOL, 0, &anon_open, NULL),
+ VARENTRY("anon-password", STR, 0, anon_password, NULL),
+ VARENTRY("ansi-escapes", BOOL, 0, &ansi_escapes, NULL),
+ VARENTRY("auto-binary", BOOL, 0, &auto_binary, NULL),
+#ifdef TERM_FTP
+ VARENTRY("compress", INT, 0,
+ &compress_toggle,NULL),
+#endif
+ VARENTRY("debug", INT, 0, &debug, NULL),
+#ifdef GATEWAY
+ VARENTRY("gateway-login", STR, 0, gate_login, set_gatelogin),
+ VARENTRY("gateway-host", STR, 0, gateway, NULL),
+#endif
+ VARENTRY("local-dir", STR, 0, lcwd, set_ldir),
+ VARENTRY("logfile", STR, 0, logfname, set_log),
+ VARENTRY("logsize", LONG, 0, &logsize, NULL),
+ VARENTRY("mprompt", BOOL, 0, &mprompt, NULL),
+ VARENTRY("netrc", -STR, 0, rcname, NULL),
+ VARENTRY("passive", BOOL, 0, &passivemode, NULL),
+ VARENTRY("pager", STR, 0, pager + 1, set_pager),
+ VARENTRY("prompt", STR, 0, prompt, set_prompt),
+ VARENTRY("progress-reports",INT, 0, &progress_meter,NULL),
+ VARENTRY("recent-list", BOOL, 0, &keep_recent, NULL),
+ VARENTRY("remote-is-unix", BOOL, 1, &remote_is_unix,NULL),
+ VARENTRY("startup-msg", BOOL, 0, &startup_msg, NULL), /* TAR */
+#ifndef NO_TIPS
+ VARENTRY("tips", BOOL, 0, &tips, NULL),
+#endif
+ VARENTRY("type", STR, 1, curtypename, set_type),
+ VARENTRY("verbose", STR, 0, vstr, set_verbose),
+};
+
+
+void set_verbose(char *new, int unset)
+{
+ int i, c;
+
+ if (unset == -1) verbose = !verbose;
+ else if (unset || !new) verbose = V_ERRS;
+ else {
+ if (isalpha(*new)) {
+ c = islower(*new) ? toupper(*new) : *new;
+ for (i=0; i<(int)(sizeof(short_verbose_msgs)/sizeof(char *)); i++) {
+ if (short_verbose_msgs[i][0] == c)
+ verbose = i - 1;
+ }
+ } else {
+ i = atoi(new);
+ if (i < V_QUIET) i = V_QUIET;
+ else if (i > V_VERBOSE) i = V_VERBOSE;
+ verbose = i;
+ }
+ }
+ (void) Strncpy(vstr, short_verbose_msgs[verbose+1]);
+ if (!parsing_rc && NOT_VQUIET)
+ (void) fputs(verbose_msgs[verbose+1], stdout);
+} /* set_verbose */
+
+
+
+
+void set_prompt(char *new, int unset)
+{
+ (void) Strncpy(prompt, (unset || !new) ? dPROMPT : new);
+ init_prompt();
+} /* set_prompt */
+
+
+
+
+void set_log(char *fname, int unset)
+{
+ if (logf) {
+ (void) fclose(logf);
+ logf = NULL;
+ }
+ if (!unset && fname) {
+ (void) Strncpy(logfname, fname);
+ logf = fopen (LocalDotPath(logfname), "a");
+ }
+} /* set_log */
+
+
+
+
+void set_pager(char *new, int unset)
+{
+ if (unset)
+ (void) strcpy(pager, "-");
+ else {
+ if (!new)
+ new = dPAGER;
+ if (!new[0])
+ (void) Strncpy(pager, "-");
+ else {
+ (void) sprintf(pager, "|%s", (*new == '|' ? new + 1 : new));
+ (void) LocalPath(pager + 1);
+ }
+ }
+} /* set_pager */
+
+
+
+
+void set_type(char *newtype, int unset)
+{
+ int t = verbose;
+ verbose = V_QUIET;
+ if (!connected && t > V_QUIET)
+ (void) printf("Not connected.\n");
+ else if (newtype != NULL && !unset)
+ (void) _settype(newtype);
+ verbose = t;
+} /* set_type */
+
+
+
+
+void set_ldir(char *ldir, int unset)
+{
+ int t = verbose;
+ char *argv[2];
+
+ if (ldir && !unset) {
+ verbose = V_QUIET;
+ argv[1] = ldir;
+ (void) lcd(2, argv);
+ verbose = t;
+ }
+} /* set_ldir */
+
+
+
+
+#ifdef GATEWAY
+void set_gatelogin(char *glogin, int unset)
+{
+ if (unset || !glogin) {
+ gate_login[0] = gateway[0] = 0;
+ } else
+ (void) strcpy(gate_login, glogin);
+} /* set_gatelogin */
+#endif
+
+
+
+
+struct var *match_var(char *varname)
+{
+ int i, ambig;
+ struct var *v;
+ short c;
+
+ c = (short) strlen(varname);
+ for (i=0, v=vars; i<NVARS; i++, v++) {
+ if (strcmp(v->name, varname) == 0)
+ return v; /* exact match. */
+ if (c < v->nmlen) {
+ if (strncmp(v->name, varname, (size_t) c) == 0) {
+ /* Now make sure that it only matches one var name. */
+ if (c >= v[1].nmlen || (i == (NVARS - 1)))
+ ambig = 0;
+ else
+ ambig = !strncmp(v[1].name, varname, (size_t) c);
+ if (!ambig)
+ return v;
+ (void) fprintf(stderr, "%s: ambiguous variable name.\n", varname);
+ goto xx;
+ }
+ }
+ }
+ (void) fprintf(stderr, "%s: unknown variable.\n", varname);
+xx:
+ return ((struct var *)0);
+} /* match_var */
+
+
+
+
+void show_var(struct var *v)
+{
+ int c;
+
+ if (v != (struct var *)0) {
+ (void) printf("%-20s= ", v->name);
+ c = v->type;
+ if (c < 0) c = -c;
+ if (v->conn_required && !connected)
+ (void) printf("(not connected)\n");
+ else switch (c) {
+ case INT:
+ (void) printf("%d\n", *(int *)v->var); break;
+ case LONG:
+ (void) printf("%ld\n", *(long *)v->var); break;
+ case STR:
+ (void) printf("\"%s\"\n", (char *)v->var); break;
+ case BOOL:
+ (void) printf("%s\n", *(int *)v->var == 0 ? "no" : "yes");
+ }
+ }
+} /* show_var */
+
+
+
+
+void show(char *varname)
+{
+ int i;
+ struct var *v;
+
+ if ((varname == NULL) /* (Denotes show all vars) */
+ || (strcmp("all", varname) == 0))
+ {
+ for (i=0; i<NVARS; i++)
+ show_var(&vars[i]);
+ } else {
+ if ((v = match_var(varname)) != (struct var *)0)
+ show_var(v);
+ }
+} /* show */
+
+
+
+
+int do_show(int argc, char **argv)
+{
+ int i;
+
+ if (argc < 2)
+ show(NULL);
+ else
+ for (i=1; i<argc; i++)
+ show(argv[i]);
+ return NOERR;
+} /* do_show */
+
+
+
+
+int set(int argc, char **argv)
+{
+ int unset;
+ struct var *v;
+ char *var, *val = NULL;
+
+ if (argc < 2 || strncmp(argv[1], "all", (size_t)3) == 0) {
+ show(NULL); /* show all variables. */
+ } else {
+ unset = argv[0][0] == 'u';
+ var = argv[1];
+ if (argc > 2) {
+ /* could be '= value' or just 'value.' */
+ if (*argv[2] == '=') {
+ if (argc > 3)
+ val = argv[3];
+ else return USAGE; /* can't do 'set var =' */
+ } else
+ val = argv[2];
+ if (val[0] == 0)
+ val = NULL;
+ }
+ v = match_var(var);
+ if (v != NULL) {
+ if (v->conn_required && !connected)
+ (void) fprintf(stderr, "%s: must be connected.\n", var);
+ else if (v->type < 0)
+ (void) fprintf(stderr, "%s: read-only variable.\n", var);
+ else if (v->proc != (setvarproc) 0) {
+ (*v->proc)(val, unset); /* a custom set proc. */
+ } else if (unset) switch(v->type) {
+ case BOOL:
+ case INT:
+ *(int *) v->var = 0; break;
+ case LONG:
+ *(long *) v->var = 0; break;
+ case STR:
+ *(char *) v->var = 0; break;
+ } else {
+ if (val == NULL) switch(v->type) {
+ /* User just said "set varname" */
+ case BOOL:
+ case INT:
+ *(int *) v->var = 1; break;
+ case LONG:
+ *(long *) v->var = 1; break;
+ case STR:
+ *(char *) v->var = 0; break;
+ } else {
+ /* User said "set varname = value" */
+ switch (v->type) {
+ case BOOL:
+ *(int *)v->var = StrToBool(val); break;
+ case INT:
+ (void) sscanf(val, "%d", (int *) v->var); break;
+ case LONG:
+ (void) sscanf(val, "%ld", (long *) v->var); break;
+ case STR:
+ (void) strcpy(v->var, val); break;
+ }
+ }
+ }
+ }
+ }
+ return NOERR;
+} /* set */
+
+/* eof Set.c */
diff --git a/usr.bin/ncftp/set.h b/usr.bin/ncftp/set.h
new file mode 100644
index 0000000..92f7125
--- /dev/null
+++ b/usr.bin/ncftp/set.h
@@ -0,0 +1,46 @@
+/* Set.h */
+
+#ifndef _set_h_
+#define _set_h_
+
+/* $RCSfile: set.h,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/06/26 06:21:32 $
+ */
+
+/* Variable types. */
+#define INT 1
+#define LONG 2
+#define STR 3
+#define BOOL 4
+
+typedef void (*setvarproc)(char *, int);
+struct var {
+ char *name;
+ short nmlen;
+ short type;
+ short conn_required;
+ void *var;
+ setvarproc proc;
+};
+
+#define VARENTRY(n,t,c,v,p) { (n), (short)(sizeof(n) - 1), (t), (c), (v), (setvarproc)(p) }
+#define NVARS ((int) (sizeof(vars)/sizeof(struct var)))
+
+void set_prompt(char *new, int unset);
+void set_log(char *fname, int unset);
+void set_ldir(char *ldir, int unset);
+#ifdef GATEWAY
+void set_gateway(char *, int);
+void set_gatelogin(char *, int);
+#endif
+void set_pager(char *new, int unset);
+void set_verbose(char *new, int unset);
+void set_type(char *newtype, int unset);
+struct var *match_var(char *varname);
+void show_var(struct var *v);
+void show(char *varname);
+int do_show(int argc, char **argv);
+int set(int argc, char **argv);
+
+#endif /* _set_h_ */
diff --git a/usr.bin/ncftp/sys.h b/usr.bin/ncftp/sys.h
new file mode 100644
index 0000000..e357c4d
--- /dev/null
+++ b/usr.bin/ncftp/sys.h
@@ -0,0 +1,636 @@
+/* Sys.h
+ * See the README for details.
+ */
+
+/* $RCSfile: sys.h,v $
+ * $Revision: 14020.13 $
+ * $Date: 93/06/21 06:42:11 $
+ */
+
+
+#ifdef __sun
+# ifndef sun
+# define sun 1
+# endif
+#endif
+
+#ifdef sun
+# if !defined(__GNUC__) && !defined(__STDC__) && !defined(SunOverride)
+ /* If you choke here, but you know what you're doing, just
+ * define SunOverride.
+ */
+ ^^^ "You need to use an ANSI C compiler. Try using gcc or acc." ^^^
+# endif
+# ifdef Solaris /* not predefined. */
+# ifndef SYSV
+# define SYSV 1
+# endif
+# define System "Solaris"
+# else
+# define System "SunOS"
+# ifndef RINDEX
+# define RINDEX 1
+# endif
+# endif /* not Solaris */
+# ifndef TERMIOS
+# define TERMIOS 1
+# endif
+# ifndef HAS_DOMAINNAME
+# define HAS_DOMAINNAME 1
+# endif
+#endif /* sun */
+
+#ifdef __sgi
+# ifndef sgi
+# define sgi 1
+# endif
+#endif
+
+#ifdef sgi
+# define System "IRIX"
+# ifndef SYSV
+# define SYSV 1
+# endif
+# ifndef HERROR
+# define HERROR 1
+# endif
+# ifndef U_WAIT
+# define U_WAIT 1
+# endif
+# ifndef STRICT_PROTOS
+# define STRICT_PROTOS 1
+# endif
+# ifndef TERMIOS
+# define TERMIOS 1
+# endif
+#endif /* sgi */
+
+#ifdef AIX
+# define System "AIX 2.2.1"
+# define BSD_INCLUDES
+# define SYSV
+# define NO_STDLIB
+# define NO_UTIME_H
+# define NO_STRFTIME
+# define NO_STRSTR
+# define NO_MKTIME
+#endif /* AIX */
+
+#ifdef _AIX
+# define System "AIX 3.x"
+# define SYSSELECTH 1
+# define TERMIOS 1
+#endif /* _AIX */
+
+#ifdef __QNX__
+# define QNX
+# define System "QNX 4.21 (POSIX)"
+# define SYSSELECTH
+# define TERMIOS
+# define _POSIX_SOURCE
+# define GETCWDSIZET
+# define STRICT_PROTOS
+# define RINDEX
+# define NO_CURSES_H
+# define unlink remove
+# define bcopy(s,d,l) memcpy((d),(s),(l))
+# define bzero(cp,l) memset((cp),0,(l))
+# define NO_SYSPARAM
+# include <limits.h>
+# define NCARGS _POSIX_ARG_MAX
+#endif
+
+#ifdef SCOXNX
+# define System "SCO Xenix"
+# define LAI_TCP
+# define NO_UTIMEH
+# define NO_MKTIME
+# define NO_STRFTIME
+# define NO_STRSTR
+# define NO_RENAME
+# define LINGER /* else SCO bug causes incomplete transfers */
+# define SYSV 1
+#endif /* SCOXNX */
+
+#ifdef SCO322
+# define System "SCO Unix 3.2v2"
+# define BOTCHED_FOPEN_RW
+# define NO_RENAME /* it exists, but it corrupts filesystems */
+# define BROKEN_MEMCPY 1
+# define SYSV 1
+#endif /* SCO322 */
+
+#ifdef SCO324
+# define System "SCO Unix 3.2v4"
+# ifndef SYSV
+# define SYSV 1
+# endif
+# ifndef BROKEN_MEMCPY
+# define BROKEN_MEMCPY 1
+# endif
+#endif /* SCO324 */
+
+#ifdef linux
+# define System "Linux"
+# ifndef HAS_DOMAINNAME
+# define HAS_DOMAINNAME 1
+# endif
+# ifndef TERMIOS
+# define TERMIOS 1
+# endif
+# ifndef SYSV
+# define SYSV 1
+# endif
+#endif
+
+#ifdef ISC
+# define System "Interactive Unix"
+# ifndef SYSV
+# define SYSV 1
+# endif
+# ifndef BROKEN_MEMCPY
+# define BROKEN_MEMCPY 1
+# endif
+# ifndef NET_ERRNO_H
+# define NET_ERRNO_H 1
+# endif
+#endif /* ISC */
+
+#ifdef aux
+# define System "A/UX"
+# ifndef BROKEN_MEMCPY
+# define BROKEN_MEMCPY 1
+# endif
+# ifndef SYSV
+# define SYSV 1
+# endif
+#endif
+
+#ifdef NeXT
+# define System "NeXTStep"
+# ifndef RINDEX
+# define RINDEX 1
+# endif
+# ifndef BSD
+# define BSD 1
+# endif
+# ifndef NO_UNISTDH
+# define NO_UNISTDH 1
+# endif
+# ifndef NO_UTIMEH
+# define NO_UTIMEH
+# endif
+# ifndef HAS_DOMAINNAME
+# define HAS_DOMAINNAME 1
+# endif
+#endif
+
+#ifdef pyr
+# define System "OSx"
+# ifndef BSD
+# define BSD 1
+# endif
+# ifndef SGTTYB
+# define SGTTYB 1
+# endif
+# ifndef NO_STDLIBH
+# define NO_STDLIBH 1
+# endif
+extern int errno;
+#endif /* pyr */
+
+#ifdef _SEQUENT_
+# if !defined(DYNIXPTX) && !defined(DYNIX)
+# define DYNIXPTX 1
+# endif
+#endif
+
+#if DYNIXPTX
+# define System "Dynix/PTX"
+# ifndef SYSV
+# define SYSV 1
+# endif
+# ifndef TRY_NOREPLY
+# define TRY_NOREPLY 1
+# endif
+# define gettimeofday(a, b) get_process_stats(a, getpid(), 0, 0)
+#endif /* DYNIXPTX */
+
+#ifdef DYNIX
+# define System "Dynix"
+# ifndef BSD
+# define BSD 1
+# endif
+# ifndef SGTTYB
+# define SGTTYB 1
+# endif
+# ifndef NO_UTIMEH
+# define NO_UTIMEH 1
+# endif
+# ifndef NO_STDLIBH
+# define NO_STDLIBH 1
+# endif
+# ifndef NO_VARARGS
+# define NO_VARARGS 1
+# endif
+#endif /* DYNIX */
+
+#ifdef ultrix
+# define System "Ultrix"
+# ifndef BSD
+# define BSD 1
+# endif
+# ifndef USE_GETPWUID
+# define USE_GETPWUID 1
+# endif
+# ifndef __GNUC__
+# ifndef NO_CONST
+# define NO_CONST 1
+# endif
+# endif
+#endif /* ultrix */
+
+#ifdef __hpux
+# ifndef HPUX
+# define HPUX 1
+# endif
+# define Select(a,b,c,d,e) select((a), (int *)(b), (c), (d), (e))
+#endif
+
+#ifdef HPUX
+# define System "HP-UX"
+# ifndef _HPUX_SOURCE
+# define _HPUX_SOURCE 1
+# endif
+# ifndef GETCWDSIZET
+# define GETCWDSIZET 1
+# endif
+# define SYSV 1
+#endif /* HPUX */
+
+#ifdef SINIX
+# define System "SINIX"
+# ifndef SYSV
+# define SYSV 1
+# endif
+/* You may need to add -lresolv, -lport, -lcurses to MORELIBS in Makefile. */
+#endif
+
+#ifdef BULL /* added 23nov92 for Bull DPX/2 */
+# define _POSIX_SOURCE
+# define _XOPEN_SOURCE
+# define _BULL_SOURCE
+# ifndef SYSV
+# define SYSV 1
+# endif
+# define bull
+# define System "Bull DPX/2 BOS"
+# define SYSSELECTH
+#endif /* BULL */ /* added 23nov92 for Bull DPX/2 */
+
+#ifdef __dgux
+# ifndef DGUX
+# define DGUX 1
+# endif
+#endif
+
+#ifdef DGUX
+# ifndef _DGUX_SOURCE
+# define _DGUX_SOURCE
+# endif
+# define GETCWDSIZET 1
+# define BAD_INETADDR 1
+# define SYSV 1
+# define System "DG/UX"
+#endif /* DGUX */
+
+#ifdef apollo
+# ifndef BSD
+# define BSD 43
+# endif
+# define NO_UTIMEH 1
+# define System "Apollo"
+#endif
+
+#ifdef __Besta__
+# define SYSV 1
+# define SYSSELECTH 1
+# define NO_UNISTDH 1
+# define NO_STDLIBH 1
+# define NO_UTIMEH 1
+# ifndef BROKEN_MEMCPY
+# define BROKEN_MEMCPY 1
+# endif
+# include <sys/types.h>
+#endif
+
+#ifdef __osf__
+# ifdef __alpha /* DEC OSF/1 */
+# define GETCWDSIZET 1
+# endif
+# ifndef System
+# define System "DEC OSF/1"
+# endif
+#endif
+
+#ifdef DELL
+# ifndef System
+# define System "DELL SVR4 Issue 2.2"
+# endif
+# ifndef HAS_DOMAINNAME
+# define HAS_DOMAINNAME 1
+# endif
+# ifndef LINGER
+# define LINGER /* SVR4/386 Streams TCP/IP bug on close */
+# endif
+#endif /* DELL */
+
+/* -------------------------------------------------------------------- */
+
+#ifdef _SYSV
+# ifndef SYSV
+# define SYSV 1
+# endif
+#endif
+
+#ifdef USG
+# ifndef SYSV
+# define SYSV 1
+# endif
+#endif
+
+#ifdef _BSD
+# ifndef BSD
+# define BSD 1
+# endif
+#endif
+
+#ifdef SVR4
+# ifndef System
+# define System "System V.4"
+# endif
+# ifndef SYSV
+# define SYSV 1
+# endif
+# ifndef VOID
+# define VOID void
+# endif
+# ifndef HERROR
+# define HERROR 1
+# endif
+# ifdef TERMH
+# define TERMH 1
+# endif
+# ifndef Gettimeofday
+# define Gettimeofday gettimeofday
+# endif
+#endif /* SVR4 */
+
+#ifdef SYSV
+# ifndef RINDEX
+# define RINDEX 1
+# endif
+# define bcopy(s,d,l) memcpy((d),(s),(l))
+# define bzero(cp,l) memset((cp),0,(l))
+# ifndef HAS_GETCWD
+# define HAS_GETCWD 1
+# endif
+#endif
+
+#ifdef __bsdi__
+# define System "BSDi"
+# ifndef BSD
+# define BSD 1
+# endif
+# ifndef SYSSELECTH
+# define SYSSELECTH 1
+# endif
+# ifndef GETCWDSIZET
+# define GETCWDSIZET 1
+# endif
+# ifndef HERROR
+# define HERROR 1
+# endif
+#endif /* BSDi */
+
+#ifdef __FreeBSD__
+# define System "FreeBSD"
+# define GZCAT "/usr/bin/gzcat"
+# define HAS_DOMAINNAME 1
+# include <sys/types.h>
+# include <sys/param.h> /* this two for BSD definition */
+ /* to avoid redefinition of it to 1 */
+# define HERROR 1
+# define TERMIOS 1
+# define HAS_GETCWD 1
+# define U_WAIT 1
+#endif
+
+#ifdef __NetBSD__
+# define System "NetBSD"
+# define GZCAT "/usr/bin/zcat"
+# define HERROR 1
+# define TERMIOS 1
+# define HAS_GETCWD 1
+# define HAS_DOMAINNAME 1
+# define U_WAIT 1
+# define GETCWDSIZET 1
+# define NO_CONST 1 /* avoid prototype conflict */
+# include <sys/types.h>
+# include <sys/param.h>
+#endif
+
+#ifdef BSD
+# ifndef __FreeBSD__
+# ifndef SYSDIRH
+# define SYSDIRH 1
+# endif
+# endif
+# ifndef SGTTYB
+# define SGTTYB
+# endif
+#endif
+
+/*
+ * Generic pointer type, e.g. as returned by malloc().
+ */
+#ifndef PTRTYPE
+# define PTRTYPE void
+#endif
+
+#ifndef Free
+# define Free(a) free((PTRTYPE *)(a))
+#endif
+
+/*
+ * Some systems besides System V don't use rindex/index (like SunOS).
+ * Add -DRINDEX to your SDEFS line if you need to.
+ */
+#ifdef RINDEX
+ /* or #include <strings.h> if you have it. */
+# define rindex strrchr
+# define index strchr
+#endif /* RINDEX */
+
+#ifdef SOCKS
+#define Getsockname(d,a,l) Rgetsockname((d), (struct sockaddr *)(a), (l))
+#else
+#ifdef SYSV
+# define Getsockname(d,a,l) getsockname((d), (void *)(a), (l))
+#else
+# define Getsockname(d,a,l) getsockname((d), (struct sockaddr *)(a), (l))
+#endif
+#endif
+
+#ifndef Select
+# define Select(a,b,c,d,e) select((a), (b), (c), (d), (e))
+#endif
+
+#ifndef Connect
+#ifndef SVR4
+# define Connect(a,b,c) (connect((a), (struct sockaddr *)(b), (int)(c)))
+# define Bind(a,b,c) (bind((a), (struct sockaddr *)(b), (int)(c)))
+# define Accept(a,b,c) (accept((a), (struct sockaddr *)(b), (int *)(c)))
+#else /* SVR4 */
+# define Connect(a,b,c) (connect((a), (caddr_t)(b), (int)(c)))
+# define Bind(a,b,c) (bind((a), (caddr_t)(b), (int)(c)))
+# define Accept(a,b,c) (accept((a), (caddr_t)(b), (int *)(c)))
+#endif /* SVR4 */
+#endif /* Connect */
+
+#ifndef Gettimeofday
+# define Gettimeofday(a) gettimeofday(a, (struct timezone *)0)
+#endif /* Gettimeofday */
+
+#ifdef GETPASS
+# define Getpass getpass
+#endif
+
+/* Enable connections through firewall gateways */
+#ifndef GATEWAY
+# define GATEWAY 1
+#endif
+
+#ifdef _POSIX_SOURCE
+# define TERMIOS
+#endif
+
+/* Include frequently used headers: */
+
+#include <sys/types.h>
+
+#ifndef NO_SYSPARAM
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <sys/time.h>
+#include <time.h>
+
+#ifndef NO_STDLIBH
+# include <stdlib.h>
+#else
+extern PTRTYPE *malloc(size_t);
+extern PTRTYPE *calloc(size_t, size_t);
+extern PTRTYPE *malloc(size_t);
+extern void free(PTRTYPE *);
+extern PTRTYPE *realloc(PTRTYPE *, size_t);
+extern void exit(int);
+
+#ifdef NO_CONST
+extern char *getenv(char *);
+extern int atoi(char *);
+#else
+extern char *getenv(const char *);
+extern int atoi(const char *);
+#endif
+
+#endif /* NO_STDLIBH */
+
+#ifndef NO_UNISTDH
+# include <unistd.h>
+#else
+char *getlogin (void);
+# ifdef NO_CONST
+extern char *getenv(char *);
+# else
+extern char *getenv(const char *);
+# endif
+#endif /* NO_UNISTDH */
+
+#ifdef NO_STD_PROTOS
+extern int _filbuf(FILE *);
+extern int _flsbuf(int, FILE *);
+extern int fflush(FILE *);
+extern int fgetc(FILE *);
+extern int fprintf(FILE *, char *, ...);
+extern int fputc(int, FILE *);
+extern int fputs(char *, FILE *);
+extern int fclose(FILE *);
+extern int pclose(FILE *);
+extern void perror(char *);
+extern int printf(char *, ...);
+extern int rewind(FILE *);
+extern int sscanf(char *, char *, ...);
+extern int vfprintf(FILE *, char *, char *);
+
+extern char * mktemp(char *);
+extern int rename(char *, char *);
+
+extern int gettimeofday(struct timeval *, struct timezone *);
+extern time_t mktime(struct tm *);
+extern int strftime(char *, int, char *, struct tm *);
+extern time_t time(time_t *);
+
+extern int tolower(int);
+extern int toupper(int);
+
+#ifndef bcopy
+extern void bcopy(char *, char *, size_t);
+#endif
+#ifndef bzero
+extern void bzero(char *, size_t);
+#endif
+
+#ifdef SOCKS
+extern int Raccept(int, struct sockaddr *, int *);
+extern int Rbind(int, struct sockaddr *, int, unsigned long);
+extern int Rconnect(int, struct sockaddr *, int);
+extern int Rlisten(int, int);
+extern int Rgetsockname(int, struct sockaddr *, int *);
+#else
+extern int accept(int, struct sockaddr *, int *);
+extern int bind(int, struct sockaddr *, int);
+extern int connect(int, struct sockaddr *, int);
+extern int listen(int, int);
+extern int getsockname(int, struct sockaddr *, int *);
+#endif
+extern int gethostname(char *, int), getdomainname(char *, int);
+#ifndef Select
+extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+#endif
+extern int send(int, char *, int, int);
+extern int setsockopt(int, int, int, char *, int);
+extern int shutdown(int, int);
+extern int socket(int, int, int);
+#endif /* NO_STD_PROTOS */
+
+/* This malloc stuff is mostly for our own use. */
+#define LIBC_MALLOC 0
+#define FAST_MALLOC 1
+#define DEBUG_MALLOC 2
+
+#ifdef LIBMALLOC
+# if LIBMALLOC != LIBC_MALLOC
+ /* Make sure you use -I to use the malloc.h of choice. */
+# include <malloc.h>
+# endif
+#else
+# define LIBMALLOC LIBC_MALLOC
+#endif
+/* End of personal malloc junk. */
+
+/* eof sys.h */
diff --git a/usr.bin/ncftp/tips.c b/usr.bin/ncftp/tips.c
new file mode 100644
index 0000000..bdd0cd5
--- /dev/null
+++ b/usr.bin/ncftp/tips.c
@@ -0,0 +1,147 @@
+/* tips.c */
+
+/* $RCSfile: tips.c,v $
+ * $Revision: 14020.11 $
+ * $Date: 93/05/21 05:44:39 $
+ */
+
+#include "sys.h"
+
+#ifndef NO_TIPS
+
+#include "util.h"
+
+/* Make sure that the indentations are spaces, not tabs.
+ * Try newform -i-4 < tips.c > tips.c.new
+ *
+ * Always add new tips right above the last one.
+ */
+
+static char *tiplist[] = {
+ "Have you tried typing 'open' by itself lately?",
+
+ "If you don't want a .ncrecent file in your home directory, put the \n\
+ command '#unset recent-list' in your .ncftprc file.",
+
+ "pseudo-filename-completion is supported in some commands. To use it,\n\
+ use a wildcard expression that will match exactly one file. I.e., if you\n\
+ want to fetch obnoxiouslylongfilename.zip, try 'get obn*.zip.' Note that\n\
+ you can't use the cd command with this feature (yet).",
+
+ "You don't need to type the exact site name with open. If a site is in\n\
+ your .ncftprc or the recent-file (.ncrecent), just type a unique\n\
+ abbreviation (substring really). I.e. 'open wuar' if you have the site\n\
+ wuarchive.wustl.edu in your rc or recent-file.",
+
+ "You can put set commands in your .ncftprc, by adding lines such\n\
+ as '#set local-dir /usr/tmp' to the file, which will be run at startup.",
+
+ "Use the .ncftprc file to set variables at startup and to add sites that \n\
+ need init macros.\n\
+ Sample .ncftprc:\n\
+ #set pager \"less -M\"\n\
+ \n\
+ machine wuarchive.wustl.edu\n\
+ macdef init\n\
+ cd /pub\n\
+ get README\n\
+ dir\n\
+ (blank line to end macro)",
+
+ "If you want to keep your .netrc's for ftp and ncftp separate, name\n\
+ ncftp's rc to .ncftprc.",
+
+ "Type 'open' by itself to get a list of the sites in your recent-file and\n\
+ your .ncftprc. You can then supply '#5' at the prompt, or use 'open #5'\n\
+ later.",
+
+ "Colon-mode is a quick way to get a file from your shell. Try something\n\
+ like 'ncftp wuarchive.wustl.edu:/pub/README.'",
+
+ "The open command accepts several flags. Do a 'help open' for details.",
+
+ "Sometimes a directory listing is several screens long and you won't\n\
+ remember the thing you wanted. Use the 'predir' command to re-view the\n\
+ listing. The program keeps the copy locally, so you won't have to wait\n\
+ for the remote server to re-send it to you.",
+
+ "Use the 'page' (or 'more') command to view a remote file with your pager.",
+
+ "ncftp may be keeping detailed information on everything you transfer.\n\
+ Run the 'version' command and if you see SYSLOG, your actions are being\n\
+ recorded on the system log.",
+
+ "Try the 'redir' command to re-display the last directory listing (ls,\n\
+ dir, ls -lrt, etc). 'predir' does the same, only with your pager.",
+
+ "This program is pronounced Nik-F-T-P. NCEMRSoft is Nik'-mer-soft.",
+
+#ifdef GETLINE
+ "NcFTP was compiled with the Getline command-line/history editor! (by\n\
+ Chris Thewalt <thewalt@ce.berkeley.edu>). To activate it, use the up\n\
+ and down arrows to scroll through the history, and/or use EMACS-style\n\
+ commands to edit the line.",
+#endif
+
+#ifdef READLINE
+ "NcFTP was compiled with the GNU Readline command-line/history editor!\n\
+ To activate it, use the up & down arrows to scroll through the history,\n\
+ and/or use EMACS-style (or maybe VI-style) commands to edit the line.",
+#endif
+
+ "You can get the newest version of NcFTP from ftp.cs.unl.edu, in the\n\
+ /pub/ncftp directory, AFTER business hours.",
+
+ "The type of progress-meter that will be used depends if the remote host\n\
+ supports the SIZE command, and whether your terminal is capable of ANSI\n\
+ escape codes.",
+
+ "To report a bug, mail your message to mgleason@cse.unl.edu. Include the\n\
+ output of the 'version' command in your message. An easy way to do that\n\
+ is to compose your message, then do a 'ncftp -H >> msg.'",
+
+ "Don't put a site in your .ncftprc unless you want an 'init' macro. The \n\
+ recent-file saves sites with the last directory you were in, unlike \n\
+ the rc file, while still letting you use sitename abbreviations.",
+
+ "You can use World Wide Web style paths instead of colon-mode paths.\n\
+ For example, if the colon-mode path was 'ftp.cs.unl.edu:pub/ncftp',\n\
+ the WWW-style path would be 'ftp://ftp.cs.unl.edu/pub/ncftp'.",
+
+ "Sick and tired of these tips? Put '#unset tips' in your .ncftprc."
+};
+
+/* Not another dinky header, por favor. */
+#define NTIPS ((int) (sizeof(tiplist) / sizeof(char *)))
+void PrintTip(void);
+extern int fromatty, debug;
+
+int tips = 1;
+#endif /* NO_TIPS */
+
+void PrintTip(void)
+{
+#ifndef NO_TIPS
+ int cheap_rn, i, tn;
+ string str;
+
+ if (tips && fromatty) {
+ cheap_rn = (int) getpid() % NTIPS;
+ if (debug) {
+ (void) printf("pid: %d; ntips: %d\n", getpid(), NTIPS);
+ (void) Gets("*** Tip# (-1 == all): ", str, sizeof(str));
+ tn = atoi(str) - 1;
+ if (tn == -1)
+ tn = 0;
+ if (tn < -1)
+ for(i=0; i<NTIPS; i++)
+ (void) printf("Tip: %s\n", tiplist[i]);
+ else if (tn < NTIPS)
+ (void) printf("Tip: %s\n", tiplist[tn]);
+ } else
+ (void) printf("Tip: %s\n", tiplist[cheap_rn]);
+ }
+#endif /* NO_TIPS */
+} /* PrintTip */
+
+/* tips.c */
diff --git a/usr.bin/ncftp/util.c b/usr.bin/ncftp/util.c
new file mode 100644
index 0000000..f8dfd5d
--- /dev/null
+++ b/usr.bin/ncftp/util.c
@@ -0,0 +1,922 @@
+/* Util.c */
+
+/* $RCSfile: util.c,v $
+ * $Revision: 14020.13 $
+ * $Date: 93/05/23 09:38:13 $
+ */
+
+#include "sys.h"
+
+#include <errno.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#ifndef NO_VARARGS
+# ifdef NO_STDARGH
+# include <varargs.h>
+# else
+# include <stdarg.h>
+# endif
+#endif
+
+#ifdef READLINE
+# include <readline/readline.h>
+#endif /* READLINE */
+
+#ifdef GETLINE
+# include <getline.h>
+#endif
+
+#include "util.h"
+#include "cmds.h"
+#include "main.h"
+#include "ftp.h"
+#include "ftprc.h"
+#include "defaults.h"
+#include "copyright.h"
+
+/* Util.c globals */
+int Opterr = 1; /* if error message should be printed */
+int Optind = 1; /* index into parent argv vector */
+int Optopt; /* character checked for validity */
+char *Optarg; /* argument associated with option */
+char *Optplace = EMSG; /* saved position in an arg */
+
+/* Util.c externs */
+extern int toatty, fromatty;
+extern int verbose, doingInitMacro;
+extern string prompt2;
+extern char *line, *margv[];
+extern int margc;
+extern int debug, mprompt, activemcmd;
+extern string progname;
+extern struct cmd cmdtab[];
+extern struct userinfo uinfo;
+
+#ifndef NO_VARARGS
+/*VARARGS*/
+#ifdef NO_STDARGH
+void dbprintf(va_alist)
+ va_dcl
+#else
+void dbprintf(char *fmt0, ...)
+#endif
+{
+ va_list ap;
+ char *fmt;
+
+#ifdef NO_STDARGH
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#else
+ va_start(ap, fmt0);
+ fmt = fmt0;
+#endif
+
+ if (debug) {
+ (void) fprintf(DB_STREAM, "#DB# ");
+ (void) vfprintf(DB_STREAM, fmt, ap);
+ (void) fflush(DB_STREAM);
+ }
+ va_end(ap);
+} /* dbprintf */
+
+#endif /* have varargs */
+
+
+
+
+/*
+ * Concatenate src on the end of dst. The resulting string will have at most
+ * n-1 characters, not counting the NUL terminator which is always appended
+ * unlike strncat. The other big difference is that strncpy uses n as the
+ * max number of characters _appended_, while this routine uses n to limit
+ * the overall length of dst.
+ */
+char *_Strncat(char *dst, char *src, register size_t n)
+{
+ register size_t i;
+ register char *d, *s;
+
+ if (n != 0 && ((i = strlen(dst)) < (n - 1))) {
+ d = dst + i;
+ s = src;
+ /* If they specified a maximum of n characters, use n - 1 chars to
+ * hold the copy, and the last character in the array as a NUL.
+ * This is the difference between the regular strncpy routine.
+ * strncpy doesn't guarantee that your new string will have a
+ * NUL terminator, but this routine does.
+ */
+ for (++i; i<n; i++) {
+ if ((*d++ = *s++) == 0) {
+ /* Pad with zeros. */
+ for (; i<n; i++)
+ *d++ = 0;
+ return dst;
+ }
+ }
+ /* If we get here, then we have a full string, with n - 1 characters,
+ * so now we NUL terminate it and go home.
+ */
+ *d = 0;
+ }
+ return (dst);
+} /* _Strncat */
+
+
+/*
+ * Copy src to dst, truncating or null-padding to always copy n-1 bytes.
+ * Return dst.
+ */
+char *_Strncpy(char *dst, char *src, register size_t n)
+{
+ register char *d;
+ register char *s;
+ register size_t i;
+
+ d = dst;
+ *d = 0;
+ if (n != 0) {
+ s = src;
+ /* If they specified a maximum of n characters, use n - 1 chars to
+ * hold the copy, and the last character in the array as a NUL.
+ * This is the difference between the regular strncpy routine.
+ * strncpy doesn't guarantee that your new string will have a
+ * NUL terminator, but this routine does.
+ */
+ for (i=1; i<n; i++) {
+ if ((*d++ = *s++) == 0) {
+ /* Pad with zeros. */
+ for (; i<n; i++)
+ *d++ = 0;
+ return dst;
+ }
+ }
+ /* If we get here, then we have a full string, with n - 1 characters,
+ * so now we NUL terminate it and go home.
+ */
+ *d = 0;
+ }
+ return (dst);
+} /* _Strncpy */
+
+
+
+/* Converts any uppercase characters in the string to lowercase.
+ * Never would have guessed that, huh?
+ */
+void StrLCase(char *dst)
+{
+ register char *cp;
+
+ for (cp=dst; *cp != '\0'; cp++)
+ if (isupper((int) *cp))
+ *cp = (char) tolower(*cp);
+}
+
+
+
+
+char *Strpcpy(char *dst, char *src)
+{
+ while ((*dst++ = *src++) != '\0')
+ ;
+ return (--dst); /* return current value of dst, NOT original value! */
+} /* Strpcpy */
+
+
+
+/*
+ * malloc's a copy of oldstr.
+ */
+char *NewString(char *oldstr)
+{
+ size_t howLong;
+ char *newstr;
+
+ howLong = strlen(oldstr);
+ if ((newstr = malloc(howLong + 1)) != NULL)
+ (void) strcpy(newstr, oldstr);
+ return newstr;
+} /* NewString */
+
+
+
+
+
+void Getopt_Reset(void)
+{
+ Optind = 1;
+ Optplace = "";
+} /* Getopt_Reset */
+
+static char *NextOption(char *ostr)
+{
+ if ((Optopt = (int) *Optplace++) == (int) ':')
+ return 0;
+ return index(ostr, Optopt);
+}
+
+int Getopt(int nargc, char **nargv, char *ostr)
+{
+ register char *oli; /* Option letter list index */
+
+ if (!*Optplace) { /* update scanning pointer */
+ if (Optind >= nargc || *(Optplace = nargv[Optind]) != '-')
+ return (EOF);
+ if (Optplace[1] && *++Optplace == '-') { /* found "--" */
+ ++Optind;
+ return (EOF);
+ }
+ } /* Option letter okay? */
+ oli = NextOption(ostr);
+ if (oli == NULL) {
+ if (!*Optplace)
+ ++Optind;
+ if (Opterr) {
+ (void) fprintf(stderr, "%s%s%c\n", *nargv, ": illegal option -- ", Optopt);
+ return(BADCH);
+ }
+ }
+ if (*++oli != ':') { /* don't need argument */
+ Optarg = NULL;
+ if (!*Optplace)
+ ++Optind;
+ } else { /* need an argument */
+ if (*Optplace) /* no white space */
+ Optarg = Optplace;
+ else if (nargc <= ++Optind) { /* no arg */
+ Optplace = EMSG;
+ if (Opterr) {
+ (void) fprintf(stderr, "%s%s%c\n", *nargv, ": option requires an argument -- ", Optopt);
+ return(BADCH);
+ }
+ } else /* white space */
+ Optarg = nargv[Optind];
+ Optplace = EMSG;
+ ++Optind;
+ }
+ return (Optopt); /* dump back Option letter */
+} /* Getopt */
+
+
+
+
+
+/*
+ * Converts an ls date, in either the "Feb 4 1992" or "Jan 16 13:42"
+ * format to a time_t.
+ */
+unsigned long UnLSDate(char *dstr)
+{
+#ifdef NO_MKTIME
+ return (MDTM_UNKNOWN);
+#else
+ char *cp = dstr;
+ int mon, day, year, hr, min;
+ time_t now, mt;
+ unsigned long result = MDTM_UNKNOWN;
+ struct tm ut, *t;
+
+ switch (*cp++) {
+ case 'A':
+ mon = (*cp == 'u') ? 7 : 3;
+ break;
+ case 'D':
+ mon = 11;
+ break;
+ case 'F':
+ mon = 1;
+ break;
+ default: /* shut up un-init warning */
+ case 'J':
+ if (*cp++ == 'u')
+ mon = (*cp == 'l') ? 6 : 5;
+ else
+ mon = 0;
+ break;
+ case 'M':
+ mon = (*++cp == 'r') ? 2 : 4;
+ break;
+ case 'N':
+ mon = 10;
+ break;
+ case 'O':
+ mon = 9;
+ break;
+ case 'S':
+ mon = 8;
+ }
+ cp = dstr + 4;
+ day = 0;
+ if (*cp != ' ')
+ day = 10 * (*cp - '0');
+ cp++;
+ day += *cp++ - '0';
+ min = 0;
+
+ (void) time(&now);
+ t = localtime(&now);
+
+ if (*++cp != ' ') {
+ /* It's a time, XX:YY, not a year. */
+ cp[2] = ' ';
+ (void) sscanf(cp, "%d %d", &hr, &min);
+ cp[2] = ':';
+ year = t->tm_year;
+ if (mon > t->tm_mon)
+ --year;
+ } else {
+ hr = min = 0;
+ (void) sscanf(cp, "%d", &year);
+ year -= 1900;
+ }
+ /* Copy the whole structure of the 'tm' pointed to by t, so it will
+ * also set all fields we don't specify explicitly to be the same as
+ * they were in t. That way we copy non-standard fields such as
+ * tm_gmtoff, if it exists or not.
+ */
+ ut = *t;
+ ut.tm_sec = 1;
+ ut.tm_min = min;
+ ut.tm_hour = hr;
+ ut.tm_mday = day;
+ ut.tm_mon = mon;
+ ut.tm_year = year;
+ ut.tm_wday = ut.tm_yday = 0;
+ mt = mktime(&ut);
+ if (mt != (time_t) -1)
+ result = (unsigned long) mt;
+ return (result);
+#endif /* NO_MKTIME */
+} /* UnLSDate */
+
+
+
+/*
+ * Converts a MDTM date, like "213 19930602204445\n"
+ * format to a time_t.
+ */
+unsigned long UnMDTMDate(char *dstr)
+{
+#ifdef NO_MKTIME
+ return (MDTM_UNKNOWN);
+#else
+ struct tm ut;
+ time_t mt;
+ unsigned long result = MDTM_UNKNOWN;
+
+ /* Clear out the whole structure, along with any non-standard fields. */
+ bzero((char *)&ut, sizeof (struct tm));
+
+ if (sscanf(dstr, "%*s %04d%02d%02d%02d%02d%02d",
+ &ut.tm_year,
+ &ut.tm_mon,
+ &ut.tm_mday,
+ &ut.tm_hour,
+ &ut.tm_min,
+ &ut.tm_sec) == 6)
+ {
+ --ut.tm_mon;
+ ut.tm_year -= 1900;
+ mt = mktime(&ut);
+ if (mt != (time_t) -1)
+ result = (unsigned long) mt;
+ }
+ return result;
+#endif /* NO_MKTIME */
+} /* UnMDTMDate */
+
+
+
+void Perror(
+#ifdef DB_ERRS
+ char *fromProc
+ ,
+#ifdef __LINE__
+ int lineNum,
+#endif
+#endif
+ char *msg
+ )
+{
+ extern int errno;
+
+ if (NOT_VQUIET) {
+#ifdef sun
+ /*
+ * There is a problem in the SunOS headers when compiling with an ANSI
+ * compiler. The problem is that there are macros in the form of
+ * #define MAC(x) 'x', and this will always be the character x instead
+ * of whatever parameter was passed to MAC. If we get these errors, it
+ * usually means that you are trying to compile with gcc when you haven't
+ * run the 'fixincludes' script that fixes these macros. We will ignore
+ * the error, but it means that the echo() function won't work correctly,
+ * and you will see your password echo.
+ */
+ if (errno == ENOTTY)
+ return;
+#endif
+ (void) fprintf(stderr, "NcFTP");
+#ifdef DB_ERRS
+ if (fromProc != NULL)
+ (void) fprintf(stderr, "/%s", fromProc);
+#ifdef __LINE__
+ (void) fprintf(stderr, "/%d", lineNum);
+#endif
+#endif
+ (void) fprintf(stderr, ": ");
+ if (msg != NULL)
+ (void) fprintf(stderr, "%s (%d): ", msg, errno);
+ perror(NULL);
+ }
+} /* Perror */
+
+
+
+
+size_t RemoveTrailingNewline(char *cp, int *stripped)
+{
+ size_t len;
+ int nBytesStripped = 0;
+
+ if (cp != NULL) {
+ cp += (len = strlen(cp)) - 1;
+ if (*cp == '\n') {
+ *cp-- = 0; /* get rid of the newline. */
+ nBytesStripped++;
+ }
+ if (*cp == '\r') { /* no returns either, please. */
+ *cp = 0;
+ nBytesStripped++;
+ }
+ if (stripped != NULL)
+ *stripped = nBytesStripped;
+ return len;
+ }
+ return (size_t)0;
+} /* RemoveTrailingNewline */
+
+
+
+#ifdef GETLINE
+extern size_t epromptlen;
+
+/*
+ * The Getline library doesn't detect the ANSI escape sequences, so the
+ * library would think that a string is longer than actually appears on
+ * screen. This function lets Getline work properly. This function is
+ * intended to fix that problem for the main command prompt only. If any
+ * other prompts want to use ANSI escapes, a (costly) function would have
+ * to scan the prompt for all escape sequences.
+ */
+/*ARGSUSED*/
+static size_t MainPromptLen(char *pr)
+{
+ return (int)epromptlen;
+}
+#endif
+
+static char *StdioGets(char *promptstr, char *sline, size_t size)
+{
+ char *cp;
+
+ if (fromatty) {
+ /* It's okay to print a prompt if we are redirecting stdout,
+ * as long as stdin is still a tty. Otherwise, don't print
+ * a prompt at all if stdin is redirected.
+ */
+#ifdef CURSES
+ tcap_put(promptstr);
+#else
+ (void) fputs(promptstr, stdout);
+#endif
+ }
+ sline[0] = 0;
+ (void) fflush(stdout); /* for svr4 */
+ cp = fgets(sline, (int)(size - 2), stdin);
+ (void) RemoveTrailingNewline(sline, NULL);
+ return cp;
+} /* StdioGets */
+
+
+/* Given a prompt string, a destination string, and it's size, return feedback
+ * from the user in the destination string, with any trailing newlines
+ * stripped. Returns NULL if EOF encountered.
+ */
+char *Gets(char *promptstr, char *sline, size_t size)
+{
+ char *cp, ch;
+ string plines;
+#ifdef GETLINE
+ int ismainprompt = (promptstr == prompt2);
+#endif
+
+ if (!fromatty || !toatty) {
+ /* Don't worry about a cmdline/history editor if you redirected a
+ * file at me.
+ */
+ return (StdioGets(promptstr, sline, size));
+ }
+
+ sline[0] = 0; /* Clear it, in case of an error later. */
+
+ /*
+ * The prompt string may actually be several lines if the user put a
+ * newline in it with the @N option. In this case we only want to print
+ * the very last line, so the command-line editors won't screw up. So
+ * now we print all the lines except the last line.
+ */
+ cp = rindex(promptstr, '\n');
+ if (cp != NULL) {
+ ch = *++cp;
+ *cp = 0;
+ (void) Strncpy(plines, promptstr);
+ *cp = ch;
+ promptstr = cp;
+#ifdef CURSES
+ tcap_put(plines);
+#else
+ (void) fputs(plines, stdout);
+#endif
+ }
+
+#ifdef READLINE
+ if ((cp = readline(promptstr)) != NULL) {
+ (void) _Strncpy(sline, cp, size);
+ free(cp);
+ (void) RemoveTrailingNewline(cp = sline, NULL);
+ if (*cp != 0) /* Don't add blank lines to history buffer. */
+ add_history(cp);
+ }
+#else /* READLINE */
+
+#ifdef GETLINE
+ if (toatty) {
+ if (ismainprompt)
+ gl_strwidth(MainPromptLen);
+ if ((cp = getline(promptstr)) != NULL) {
+ if (*cp == '\0') /* You hit ^D. */
+ return NULL;
+ cp = _Strncpy(sline, cp, size);
+ (void) RemoveTrailingNewline(cp, NULL);
+ if (*cp != '\0') { /* Don't add blank lines to history buffer. */
+ gl_histadd(cp);
+ }
+ }
+ /* Hope your strlen is declared as returning a size_t. */
+ gl_strwidth(strlen);
+ } else {
+ cp = StdioGets(promptstr, sline, size);
+ }
+#else /* !GETLINE */
+ cp = StdioGets(promptstr, sline, size);
+#endif /* !GETLINE */
+#endif /* !READLINE */
+ return cp;
+} /* Gets */
+
+
+
+
+char **re_makeargv(char *promptstr, int *argc)
+{
+ size_t sz;
+
+ (void) strcat(line, " ");
+ sz = strlen(line);
+ (void) Gets(promptstr, &line[sz], (size_t) (CMDLINELEN - sz)) ;
+ (void) makeargv();
+ *argc = margc;
+ return (margv);
+} /* re_makeargv */
+
+
+
+#ifndef HAS_GETCWD
+extern char *getwd(char *);
+#endif
+
+char *get_cwd(char *buf, int size)
+{
+#ifdef HAS_GETCWD
+# ifdef NO_UNISTDH
+# ifdef GETCWDSIZET
+ extern char *getcwd(char *, size_t);
+# else
+ extern char *getcwd(char *, int);
+# endif
+# endif
+ return (getcwd(buf, size - 1));
+#else
+#ifndef MAXPATHLEN
+# define MAXPATHLEN (1024)
+#endif
+ static char *cwdbuf = NULL;
+
+ if (cwdbuf == NULL) {
+ cwdbuf = (char *)malloc((size_t) MAXPATHLEN);
+ if (cwdbuf == NULL)
+ fatal("out of memory for getwd buffer.");
+ }
+ getwd(cwdbuf);
+ return (_Strncpy(buf, cwdbuf, (size_t)size));
+#endif
+} /* get_cwd */
+
+
+
+int tmp_name(char *str)
+{
+ (void) strcpy(str, "/tmp/ncftpXXXXXX");
+ return (!mktemp(str));
+} /* tmp_name */
+
+
+
+
+char *onoff(int boolf)
+{
+ return (boolf ? "on" : "off");
+} /* onoff */
+
+
+
+
+int StrToBool(char *s)
+{
+ int c;
+ int result;
+
+ c = tolower(*s);
+ result = 0;
+ switch (c) {
+ case 'f': /* false */
+ case 'n': /* no */
+ break;
+ case 'o': /* test for "off" and "on" */
+ c = tolower(s[1]);
+ if (c == 'f')
+ break;
+ /* fall through */
+ case 't': /* true */
+ case 'y': /* yes */
+ result = 1;
+ break;
+ default: /* 1, 0, -1, other number? */
+ if (atoi(s) != 0)
+ result = 1;
+ }
+ return result;
+} /* StrToBool */
+
+
+
+
+int confirm(char *cmd, char *file)
+{
+ string str, pr;
+
+ if (!fromatty || (activemcmd && !mprompt) || (doingInitMacro))
+ return 1;
+ (void) sprintf(pr, "%s %s? ", cmd, file);
+ (void) Gets(pr, str, sizeof(str));
+ return (*str != 'n' && *str != 'N');
+} /* confirm */
+
+
+
+void fatal(char *msg)
+{
+ (void) fprintf(stderr, "%s: %s\n", progname, msg);
+ close_up_shop();
+ exit(1);
+} /* fatal */
+
+
+
+
+int UserLoggedIn(void)
+{
+ static int inited = 0;
+ static int parent_pid, stderr_was_tty;
+
+ if (!inited) {
+ stderr_was_tty = isatty(2);
+ parent_pid = getppid();
+ inited++;
+ }
+ if ((stderr_was_tty && !isatty(2)) || (getppid() != parent_pid))
+ return 0;
+ return 1;
+} /* UserLoggedIn */
+
+
+
+
+struct cmd *getcmd(char *name)
+{
+ struct cmd *c, *found;
+ int nmatches;
+ size_t len;
+ char *p;
+
+ found = (struct cmd *)0;
+ if (name != NULL) {
+ len = strlen(name);
+ nmatches = 0;
+ for (c = cmdtab; (p = c->c_name) != NULL; c++) {
+ if (strcmp(name, p) == 0) {
+ /* Exact match. */
+ found = c;
+ goto xx;
+ }
+ if (c->c_handler == unimpl)
+ continue;
+ if (strncmp(name, p, len) == 0) {
+ if (++nmatches > 1) {
+ found = ((struct cmd *) -1);
+ goto xx;
+ }
+ found = c;
+ } else if (found != NULL)
+ break;
+ }
+ }
+xx:
+ return (found);
+} /* getcmd */
+
+
+
+
+void cmd_help(struct cmd *c)
+{
+ (void) printf("%s: %s.\n",
+ c->c_name,
+ c->c_help
+ );
+} /* cmd_help */
+
+
+
+
+void cmd_usage(struct cmd *c)
+{
+ if (c->c_usage != NULL)
+ (void) printf("Usage: %s%s\n",
+ c->c_name,
+ c->c_usage
+ );
+} /* cmd_usage */
+
+
+
+
+/*
+ * A simple function that translates most pathnames with ~, ~user, or
+ * environment variables as the first item. It won't do paths with env vars
+ * or ~s in the middle of the path, but those are extremely rare.
+ */
+char *LocalPath(char *path)
+{
+ longstring orig;
+ struct passwd *pw;
+ char *firstent;
+ char *cp, *dp, *rest;
+
+ (void) Strncpy(orig, path);
+ firstent = orig;
+ if ((cp = index(orig, '/')) != NULL) {
+ if (cp == orig) {
+ /* If we got here, the path is actually a full path name,
+ * with the first character as a slash, so just leave it
+ * alone.
+ */
+ return (path);
+ }
+ /* Otherwise we can look at the first word of the path, and
+ * try to expand it, like $HOME/ or ~/, or it is a relative path,
+ * which is okay since we won't really do anything with it.
+ */
+ *cp = 0;
+ rest = cp + 1;
+ /* 'firstent' now contains the first 'word' in the path. */
+ } else {
+ /* Path was just a single word, or it is a full path, like:
+ * /usr/tmp/zz, so firstent is just the entire given "path."
+ */
+ rest = NULL;
+ }
+ if (orig[0] == '~') {
+ if (orig[1] == 0) {
+ firstent = uinfo.homedir;
+ } else {
+ pw = getpwnam(orig + 1);
+ if (pw != NULL)
+ firstent = pw->pw_dir;
+ }
+ } else if (orig[0] == '$') {
+ cp = orig + 1;
+ dp = orig + strlen(orig) - 1;
+ if ((*cp == '(' && *dp == ')') || (*cp == '{' && *dp == '}')) {
+ cp++;
+ *dp = 0;
+ }
+ firstent = getenv(cp);
+ if (firstent == NULL) {
+ (void) fprintf(stderr, "%s: no such environment variable.\n", cp);
+ firstent = "badEnvVar";
+ }
+ }
+ if (rest == NULL)
+ (void) strcpy(path, firstent);
+ else
+ (void) sprintf(path, "%s/%s", firstent, rest);
+ return (path);
+} /* LocalPath */
+
+
+
+/*
+ * A special case, where invisible dot-files that would normally appear in
+ * your home directory will appear instead as visible files in your $DOTDIR
+ * directory if you have one.
+ */
+
+#define LCMP(b) (strncmp(path, (b), (o = sizeof(b) - 1)) == 0)
+
+char *LocalDotPath(char *path)
+{
+ size_t o;
+ longstring s, s2;
+ char *cp = getenv("DOTDIR");
+
+ if (cp == NULL) {
+ goto aa;
+ } else {
+ if (*cp != '/' && *cp != '~') {
+ /* then maybe they mean relative to $HOME. */
+ (void) sprintf(s2, "%s/%s", uinfo.homedir, cp);
+ cp = s2;
+ }
+ if (LCMP("~/.") ||
+ LCMP("$HOME/.") ||
+ LCMP("$home/.") ||
+ LCMP("$(HOME)/.") ||
+ LCMP("${HOME}/.")
+ ) {
+ (void) Strncpy(s, path);
+ (void) sprintf(path, "%s/%s", cp, s + o);
+ cp = path;
+ } else {
+aa: cp = LocalPath(path);
+ }
+ }
+ return cp;
+} /* LocalDotPath */
+
+#ifdef NO_STRSTR
+
+/*
+ * The Elm Mail System - $Revision: 5.1 $ $State: Exp $
+ *
+ * Copyright (c) 1988-1992 USENET Community Trust
+ * Copyright (c) 1986,1987 Dave Taylor
+ */
+
+char *strstr(s1, s2)
+char *s1, *s2;
+{
+ int len;
+ char *ptr;
+ char *tmpptr;
+
+ ptr = NULL;
+ len = strlen(s2);
+
+ if ( len <= strlen(s1)) {
+ tmpptr = s1;
+ while ((ptr = index(tmpptr, (int)*s2)) != NULL) {
+ if (strncmp(ptr, s2, len) == 0) {
+ break;
+ }
+ tmpptr = ptr+1;
+ }
+ }
+ return (ptr);
+}
+
+#endif
+
+
+#ifdef NO_RENAME
+int rename(oldname, newname)
+const char *oldname, *newname;
+{
+ return (link(oldname, newname) == 0 ? unlink(oldname) : -1);
+}
+#endif /*NO_RENAME*/
+
+
+/* eof Util.c */
diff --git a/usr.bin/ncftp/util.h b/usr.bin/ncftp/util.h
new file mode 100644
index 0000000..64babbf
--- /dev/null
+++ b/usr.bin/ncftp/util.h
@@ -0,0 +1,104 @@
+/* Util.h */
+
+#ifndef _util_h_
+#define _util_h_
+
+/* $RCSfile: util.h,v $
+ * $Revision: 14020.12 $
+ * $Date: 93/07/09 11:32:49 $
+ */
+
+typedef char string[128], str32[32], longstring[512];
+typedef char Hostname[64];
+
+/* For Perror. */
+#ifdef DB_ERRS
+# ifdef __LINE__
+# define PERROR(p,e) Perror(p, __LINE__, e)
+ void Perror(char *, int, char *);
+# else
+# define PERROR(p,e) Perror(p, e)
+ void Perror(char *, char *);
+# endif
+#else
+# define PERROR(p,e) Perror(e)
+ void Perror(char *);
+#endif
+
+#ifdef NO_VARARGS
+ extern int debug;
+# define dbprintf if (debug) (void) printf
+#else
+# ifndef DB_STREAM
+# define DB_STREAM stdout
+# endif
+#ifndef NO_STDARGH
+void dbprintf(char *fmt0, ...);
+#else
+void dbprintf(int va_alist);
+#endif
+#endif
+
+/* For 'Getopt.' */
+#define BADCH ((int)'?')
+#define EMSG ""
+
+/* Handy macros. */
+#define Strncpy(d,s) _Strncpy((char *) (d), (char *) (s), (size_t) sizeof(d))
+#define Strncat(d,s) _Strncat((char *) (d), (char *) (s), (size_t) sizeof(d))
+#define FGets(a,b) fgets((a), (int) (sizeof(a) - 2), (b))
+
+#ifndef NO_CONST
+typedef int (*cmp_t)(const void *, const void *);
+#else
+typedef int (*cmp_t)(void *, void *);
+#endif
+
+#define QSort(base,n,sz,cmp) \
+ qsort(base, (size_t)(n), (size_t)(sz), (cmp_t)(cmp))
+
+#ifndef SIG_PARAMS
+#define SIG_PARAMS (int sig)
+#endif
+typedef void (*Sig_t) SIG_PARAMS;
+
+#define Signal(a,proc) signal((a), (Sig_t)(proc))
+
+/* Quiets warnings */
+#if defined(sun) /* ...actually, any UNIX system */
+# if defined(__GNUC__)
+# undef SIG_DFL
+# undef SIG_IGN
+# define SIG_DFL (Sig_t)0
+# define SIG_IGN (Sig_t)1
+# endif
+#endif
+
+/* Protos. */
+char *_Strncat(char *dst, char *src, register size_t n);
+char *_Strncpy(char *dst, char *src, register size_t n);
+void StrLCase(char *dst);
+char *NewString(char *oldstr);
+char **re_makeargv(char *promptstr, int *argc);
+char *onoff(int);
+int StrToBool(char *s);
+int confirm(char *cmd, char *file);
+void fatal(char *msg);
+char *get_cwd(char *buf, int size);
+int tmp_name(char *str);
+int Getopt(int argc, char **argv, char *opstring);
+void Getopt_Reset(void);
+char *Gets(char *promptstr, char *sline, size_t size);
+size_t RemoveTrailingNewline(char *cp, int *stripped);
+unsigned long UnLSDate(char *dstr);
+unsigned long UnMDTMDate(char *dstr);
+char *Strpcpy(char *dst, char *src);
+int UserLoggedIn(void);
+char *LocalPath(char *path);
+char *LocalDotPath(char *path);
+
+#ifdef NO_STRSTR
+char *strstr(char *s1, char *s2);
+#endif
+
+#endif /* _util_h_ */
diff --git a/usr.bin/ncftp/v2_Note b/usr.bin/ncftp/v2_Note
new file mode 100644
index 0000000..6900bf2
--- /dev/null
+++ b/usr.bin/ncftp/v2_Note
@@ -0,0 +1,35 @@
+Versions numbered between 1.5.0 and 1.9.9 are interim releases. No major
+features are planned for these; only tweaks and fixes.
+
+Version 2.0 is much cooler, but I haven't had time to work on it. If
+you send me email, I _will_ read it, but it may take me awhile to get to it,
+and I may not answer. Please don't be offended. I really regret having
+to release code that is such a mess. I should have wrote the code from
+scratch, rather than built it upon the original BSD code (which is a mess
+itself!).
+
+I apologize in advance for bugs I fixed in 2.0 but forgot to re-fix
+in these interim releases (I think I got'em all, though).
+
+Here is a list of things that are in the 2.0 code but not this version:
+
+* Easy-to-read, better organized, commented code.
+* Using my own "style guide," so source is coded uniformly.
+* Unlimited global macros. These are really nice! They take arguments,
+ so you can make mini-scripts. The macros can be typed as if they
+ were commands; no more $macroname crap.
+* No-longer using .netrc and it's format. This was necessary for the
+ global macros anyway, and it allowed me to add some other features,
+ like host aliases. Old .netrc's won't work. Sorry!
+* Improved command line parser, that lets any command use > and |, so
+ it behaves like a real shell command line (almost).
+* Improved 'redir' that works automatically, can reformat listings with
+ different ls flags, all without refetching it over the network. It
+ will also facilitate remote globbing... AND remote filename completion!
+* Many little things I can't begin to list.
+
+Keep that in mind if you want to make a feature enhancement. I'm telling
+you this now so you don't spend your time programming something that is
+already done for the 2.0 release. Also note that patches would
+have to be re-coded for 2.0. (The gist is that you should wait until
+2.0 is done before doing anything major).
diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile
new file mode 100644
index 0000000..51fc9fa
--- /dev/null
+++ b/usr.bin/netstat/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/12/93
+
+PROG= netstat
+SRCS= if.c inet.c iso.c main.c mbuf.c mroute.c ns.c route.c \
+ tp_astring.c unix.c
+CFLAGS+=-I/sys
+.PATH: ${.CURDIR}/../../sys/netiso
+BINGRP= kmem
+BINMODE=2555
+LDADD= -lkvm
+DPADD= ${LIBKVM}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c
new file mode 100644
index 0000000..adcc076
--- /dev/null
+++ b/usr.bin/netstat/if.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)if.c 8.2 (Berkeley) 2/21/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#include <netiso/iso.h>
+#include <netiso/iso_var.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "netstat.h"
+
+#define YES 1
+#define NO 0
+
+static void sidewaysintpr __P((u_int, u_long));
+static void catchalarm __P((int));
+
+/*
+ * Print a description of the network interfaces.
+ */
+void
+intpr(interval, ifnetaddr)
+ int interval;
+ u_long ifnetaddr;
+{
+ struct ifnet ifnet;
+ union {
+ struct ifaddr ifa;
+ struct in_ifaddr in;
+ struct ns_ifaddr ns;
+ struct iso_ifaddr iso;
+ } ifaddr;
+ u_long ifaddraddr;
+ struct sockaddr *sa;
+ char name[32], tname[16];
+
+ if (ifnetaddr == 0) {
+ printf("ifnet: symbol not defined\n");
+ return;
+ }
+ if (interval) {
+ sidewaysintpr((unsigned)interval, ifnetaddr);
+ return;
+ }
+ if (kread(ifnetaddr, (char *)&ifnetaddr, sizeof ifnetaddr))
+ return;
+ printf("%-5.5s %-5.5s %-11.11s %-15.15s %8.8s %5.5s",
+ "Name", "Mtu", "Network", "Address", "Ipkts", "Ierrs");
+ if (bflag)
+ printf(" %10.10s","Ibytes");
+ printf(" %8.8s %5.5s", "Opkts", "Oerrs");
+ if (bflag)
+ printf(" %10.10s","Obytes");
+ printf(" %5s", "Coll");
+ if (tflag)
+ printf(" %s", "Time");
+ if (dflag)
+ printf(" %s", "Drop");
+ putchar('\n');
+ ifaddraddr = 0;
+ while (ifnetaddr || ifaddraddr) {
+ struct sockaddr_in *sin;
+ register char *cp;
+ int n, m;
+
+ if (ifaddraddr == 0) {
+ if (kread(ifnetaddr, (char *)&ifnet, sizeof ifnet) ||
+ kread((u_long)ifnet.if_name, tname, 16))
+ return;
+ tname[15] = '\0';
+ ifnetaddr = (u_long)ifnet.if_next;
+ snprintf(name, 32, "%s%d", tname, ifnet.if_unit);
+ if (interface != 0 && (strcmp(name, interface) != 0))
+ continue;
+ cp = index(name, '\0');
+ if ((ifnet.if_flags&IFF_UP) == 0)
+ *cp++ = '*';
+ *cp = '\0';
+ ifaddraddr = (u_long)ifnet.if_addrlist;
+ }
+ printf("%-5.5s %-5d ", name, ifnet.if_mtu);
+ if (ifaddraddr == 0) {
+ printf("%-11.11s ", "none");
+ printf("%-15.15s ", "none");
+ } else {
+ if (kread(ifaddraddr, (char *)&ifaddr, sizeof ifaddr)) {
+ ifaddraddr = 0;
+ continue;
+ }
+#define CP(x) ((char *)(x))
+ cp = (CP(ifaddr.ifa.ifa_addr) - CP(ifaddraddr)) +
+ CP(&ifaddr); sa = (struct sockaddr *)cp;
+ switch (sa->sa_family) {
+ case AF_UNSPEC:
+ printf("%-11.11s ", "none");
+ printf("%-15.15s ", "none");
+ break;
+ case AF_INET:
+ sin = (struct sockaddr_in *)sa;
+#ifdef notdef
+ /* can't use inet_makeaddr because kernel
+ * keeps nets unshifted.
+ */
+ in = inet_makeaddr(ifaddr.in.ia_subnet,
+ INADDR_ANY);
+ printf("%-11.11s ", netname(in.s_addr,
+ ifaddr.in.ia_subnetmask));
+#else
+ printf("%-11.11s ",
+ netname(htonl(ifaddr.in.ia_subnet),
+ ifaddr.in.ia_subnetmask));
+#endif
+ printf("%-15.15s ",
+ routename(sin->sin_addr.s_addr));
+ break;
+ case AF_NS:
+ {
+ struct sockaddr_ns *sns =
+ (struct sockaddr_ns *)sa;
+ u_long net;
+ char netnum[8];
+
+ *(union ns_net *) &net = sns->sns_addr.x_net;
+ sprintf(netnum, "%lxH", ntohl(net));
+ upHex(netnum);
+ printf("ns:%-8s ", netnum);
+ printf("%-15s ",
+ ns_phost((struct sockaddr *)sns));
+ }
+ break;
+ case AF_LINK:
+ {
+ struct sockaddr_dl *sdl =
+ (struct sockaddr_dl *)sa;
+ cp = (char *)LLADDR(sdl);
+ n = sdl->sdl_alen;
+ }
+ m = printf("<Link>");
+ goto hexprint;
+ default:
+ m = printf("(%d)", sa->sa_family);
+ for (cp = sa->sa_len + (char *)sa;
+ --cp > sa->sa_data && (*cp == 0);) {}
+ n = cp - sa->sa_data + 1;
+ cp = sa->sa_data;
+ hexprint:
+ while (--n >= 0)
+ m += printf("%02x%c", *cp++ & 0xff,
+ n > 0 ? '.' : ' ');
+ m = 28 - m;
+ while (m-- > 0)
+ putchar(' ');
+ break;
+ }
+ ifaddraddr = (u_long)ifaddr.ifa.ifa_next;
+ }
+ printf("%8d %5d ",
+ ifnet.if_ipackets, ifnet.if_ierrors);
+ if (bflag)
+ printf("%10d ", ifnet.if_ibytes);
+ printf("%8d %5d ",
+ ifnet.if_opackets, ifnet.if_oerrors);
+ if (bflag)
+ printf("%10d ", ifnet.if_obytes);
+ printf("%5d", ifnet.if_collisions);
+ if (tflag)
+ printf(" %3d", ifnet.if_timer);
+ if (dflag)
+ printf(" %3d", ifnet.if_snd.ifq_drops);
+ putchar('\n');
+ }
+}
+
+#define MAXIF 10
+struct iftot {
+ char ift_name[16]; /* interface name */
+ u_int ift_ip; /* input packets */
+ u_int ift_ie; /* input errors */
+ u_int ift_op; /* output packets */
+ u_int ift_oe; /* output errors */
+ u_int ift_co; /* collisions */
+ u_int ift_dr; /* drops */
+ u_int ift_ib; /* input bytes */
+ u_int ift_ob; /* output bytes */
+} iftot[MAXIF];
+
+u_char signalled; /* set if alarm goes off "early" */
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+static void
+sidewaysintpr(interval, off)
+ unsigned interval;
+ u_long off;
+{
+ struct ifnet ifnet;
+ u_long firstifnet;
+ register struct iftot *ip, *total;
+ register int line;
+ struct iftot *lastif, *sum, *interesting;
+ int oldmask;
+
+ if (kread(off, (char *)&firstifnet, sizeof (u_long)))
+ return;
+ lastif = iftot;
+ sum = iftot + MAXIF - 1;
+ total = sum - 1;
+ interesting = iftot;
+ for (off = firstifnet, ip = iftot; off;) {
+ char *cp;
+ char name[16], tname[16];
+
+ if (kread(off, (char *)&ifnet, sizeof ifnet))
+ break;
+ if (kread((u_long)ifnet.if_name, tname, 16))
+ break;
+ tname[15] = '\0';
+ snprintf(name, 16, "%s%d", tname, ifnet.if_unit);
+ if (interface && strcmp(name, interface) == 0)
+ interesting = ip;
+ snprintf(ip->ift_name, 16, "(%s)", name);;
+ ip++;
+ if (ip >= iftot + MAXIF - 2)
+ break;
+ off = (u_long) ifnet.if_next;
+ }
+ lastif = ip;
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = NO;
+ (void)alarm(interval);
+banner:
+ printf(" input %s%-6.6s %soutput ", bflag ? " " : "",
+ interesting->ift_name, bflag ? " " : "");
+ if (lastif - iftot > 0) {
+ if (dflag)
+ printf(" ");
+ printf(" input %s(Total) %soutput", bflag ? " " : "",
+ bflag ? " " : "");
+ }
+ for (ip = iftot; ip < iftot + MAXIF; ip++) {
+ ip->ift_ip = 0;
+ ip->ift_ie = 0;
+ ip->ift_op = 0;
+ ip->ift_oe = 0;
+ ip->ift_co = 0;
+ ip->ift_dr = 0;
+ ip->ift_ib = 0;
+ ip->ift_ob = 0;
+ }
+ putchar('\n');
+ printf("%8.8s %5.5s ", "packets", "errs");
+ if (bflag)
+ printf("%10.10s ","bytes");
+ printf("%8.8s %5.5s ", "packets", "errs");
+ if (bflag)
+ printf("%10.10s ","bytes");
+ printf("%5.5s ", "colls");
+ if (dflag)
+ printf("%5.5s ", "drops");
+ if (lastif - iftot > 0) {
+ printf(" %8.8s %5.5s", "packets", "errs");
+ if (bflag)
+ printf(" %10.10s", "bytes");
+ printf(" %8.8s %5.5s", "packets", "errs");
+ if (bflag)
+ printf(" %10.10s", "bytes");
+ printf(" %5.5s", "colls");
+ if (dflag)
+ printf(" %5.5s", "drops");
+ }
+ putchar('\n');
+ fflush(stdout);
+ line = 0;
+loop:
+ sum->ift_ip = 0;
+ sum->ift_ie = 0;
+ sum->ift_op = 0;
+ sum->ift_oe = 0;
+ sum->ift_co = 0;
+ sum->ift_dr = 0;
+ sum->ift_ib = 0;
+ sum->ift_ob = 0;
+ for (off = firstifnet, ip = iftot; off && ip < lastif; ip++) {
+ if (kread(off, (char *)&ifnet, sizeof ifnet)) {
+ off = 0;
+ continue;
+ }
+ if (ip == interesting) {
+ printf("%8u %5u ",
+ ifnet.if_ipackets - ip->ift_ip,
+ ifnet.if_ierrors - ip->ift_ie);
+ if (bflag)
+ printf("%10u ", ifnet.if_ibytes - ip->ift_ib);
+ printf("%8u %5u ",
+ ifnet.if_opackets - ip->ift_op,
+ ifnet.if_oerrors - ip->ift_oe);
+ if (bflag)
+ printf("%10u ", ifnet.if_obytes - ip->ift_ob);
+ printf("%5u", ifnet.if_collisions - ip->ift_co);
+ if (dflag)
+ printf(" %5u",
+ ifnet.if_snd.ifq_drops - ip->ift_dr);
+ }
+ ip->ift_ip = ifnet.if_ipackets;
+ ip->ift_ie = ifnet.if_ierrors;
+ ip->ift_op = ifnet.if_opackets;
+ ip->ift_oe = ifnet.if_oerrors;
+ ip->ift_co = ifnet.if_collisions;
+ ip->ift_dr = ifnet.if_snd.ifq_drops;
+ ip->ift_ib = ifnet.if_ibytes;
+ ip->ift_ob = ifnet.if_obytes;
+ sum->ift_ip += ip->ift_ip;
+ sum->ift_ie += ip->ift_ie;
+ sum->ift_op += ip->ift_op;
+ sum->ift_oe += ip->ift_oe;
+ sum->ift_co += ip->ift_co;
+ sum->ift_dr += ip->ift_dr;
+ sum->ift_ib += ip->ift_ib;
+ sum->ift_ob += ip->ift_ob;
+ off = (u_long) ifnet.if_next;
+ }
+ if (lastif - iftot > 0) {
+ printf(" %8u %5u",
+ sum->ift_ip - total->ift_ip,
+ sum->ift_ie - total->ift_ie);
+ if (bflag)
+ printf(" %10u", sum->ift_ib - total->ift_ib);
+ printf(" %8u %5u",
+ sum->ift_op - total->ift_op,
+ sum->ift_oe - total->ift_oe);
+ if (bflag)
+ printf(" %10u", sum->ift_ob - total->ift_ob);
+ printf(" %5u", sum->ift_co - total->ift_co);
+ if (dflag)
+ printf(" %5u", sum->ift_dr - total->ift_dr);
+ }
+ *total = *sum;
+ putchar('\n');
+ fflush(stdout);
+ line++;
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (! signalled) {
+ sigpause(0);
+ }
+ sigsetmask(oldmask);
+ signalled = NO;
+ (void)alarm(interval);
+ if (line == 21)
+ goto banner;
+ goto loop;
+ /*NOTREACHED*/
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(signo)
+ int signo;
+{
+ signalled = YES;
+}
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
new file mode 100644
index 0000000..3be5f7c
--- /dev/null
+++ b/usr.bin/netstat/inet.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)inet.c 8.4 (Berkeley) 4/20/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/igmp_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_seq.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "netstat.h"
+
+struct inpcb inpcb;
+struct tcpcb tcpcb;
+struct socket sockb;
+
+char *inetname __P((struct in_addr *));
+void inetprint __P((struct in_addr *, int, char *));
+
+/*
+ * Print a summary of connections related to an Internet
+ * protocol. For TCP, also give state of connection.
+ * Listening processes (aflag) are suppressed unless the
+ * -a (all) flag is specified.
+ */
+void
+protopr(off, name)
+ u_long off;
+ char *name;
+{
+ struct inpcbhead head;
+ register struct inpcb *prev, *next;
+ int istcp;
+ static int first = 1;
+
+ if (off == 0)
+ return;
+
+ istcp = strcmp(name, "tcp") == 0;
+ kread(off, (char *)&head, sizeof (struct inpcbhead));
+ prev = (struct inpcb *)off;
+
+ for (next = head.lh_first; next != NULL; next = inpcb.inp_list.le_next) {
+ if (kread((u_long)next, (char *)&inpcb, sizeof (inpcb))) {
+ printf("???\n");
+ break;
+ }
+ if (!aflag &&
+ inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) {
+ prev = next;
+ continue;
+ }
+ if (kread((u_long)inpcb.inp_socket, (char *)&sockb, sizeof (sockb))) {
+ printf("???\n");
+ break;
+ };
+ if (istcp) {
+ if (kread((u_long)inpcb.inp_ppcb,
+ (char *)&tcpcb, sizeof (tcpcb))) {
+ printf("???\n");
+ break;
+ };
+ }
+ if (first) {
+ printf("Active Internet connections");
+ if (aflag)
+ printf(" (including servers)");
+ putchar('\n');
+ if (Aflag)
+ printf("%-8.8s ", "PCB");
+ printf(Aflag ?
+ "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %s\n" :
+ "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %s\n",
+ "Proto", "Recv-Q", "Send-Q",
+ "Local Address", "Foreign Address", "(state)");
+ first = 0;
+ }
+ if (Aflag)
+ if (istcp)
+ printf("%8x ", inpcb.inp_ppcb);
+ else
+ printf("%8x ", next);
+ printf("%-5.5s %6d %6d ", name, sockb.so_rcv.sb_cc,
+ sockb.so_snd.sb_cc);
+ inetprint(&inpcb.inp_laddr, (int)inpcb.inp_lport, name);
+ inetprint(&inpcb.inp_faddr, (int)inpcb.inp_fport, name);
+ if (istcp) {
+ if (tcpcb.t_state < 0 || tcpcb.t_state >= TCP_NSTATES)
+ printf(" %d", tcpcb.t_state);
+ else {
+ printf(" %s", tcpstates[tcpcb.t_state]);
+#if defined(TF_NEEDSYN) && defined(TF_NEEDSYN)
+ /* Show T/TCP `hidden state' */
+ if (tcpcb.t_flags & (TF_NEEDSYN|TF_NEEDFIN))
+ putchar('*');
+#endif /* defined(TF_NEEDSYN) && defined(TF_NEEDSYN) */
+ }
+ }
+ putchar('\n');
+ prev = next;
+ }
+}
+
+/*
+ * Dump TCP statistics structure.
+ */
+void
+tcp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct tcpstat tcpstat;
+
+ if (off == 0)
+ return;
+ printf ("%s:\n", name);
+ kread(off, (char *)&tcpstat, sizeof (tcpstat));
+
+#define p(f, m) if (tcpstat.f || sflag <= 1) \
+ printf(m, tcpstat.f, plural(tcpstat.f))
+#define p2(f1, f2, m) if (tcpstat.f1 || tcpstat.f2 || sflag <= 1) \
+ printf(m, tcpstat.f1, plural(tcpstat.f1), tcpstat.f2, plural(tcpstat.f2))
+#define p3(f, m) if (tcpstat.f || sflag <= 1) \
+ printf(m, tcpstat.f, plurales(tcpstat.f))
+
+ p(tcps_sndtotal, "\t%d packet%s sent\n");
+ p2(tcps_sndpack,tcps_sndbyte,
+ "\t\t%d data packet%s (%d byte%s)\n");
+ p2(tcps_sndrexmitpack, tcps_sndrexmitbyte,
+ "\t\t%d data packet%s (%d byte%s) retransmitted\n");
+ p(tcps_mturesent, "\t\t%d resend%s initiated by MTU discovery\n");
+ p2(tcps_sndacks, tcps_delack,
+ "\t\t%d ack-only packet%s (%d delayed)\n");
+ p(tcps_sndurg, "\t\t%d URG only packet%s\n");
+ p(tcps_sndprobe, "\t\t%d window probe packet%s\n");
+ p(tcps_sndwinup, "\t\t%d window update packet%s\n");
+ p(tcps_sndctrl, "\t\t%d control packet%s\n");
+ p(tcps_rcvtotal, "\t%d packet%s received\n");
+ p2(tcps_rcvackpack, tcps_rcvackbyte, "\t\t%d ack%s (for %d byte%s)\n");
+ p(tcps_rcvdupack, "\t\t%d duplicate ack%s\n");
+ p(tcps_rcvacktoomuch, "\t\t%d ack%s for unsent data\n");
+ p2(tcps_rcvpack, tcps_rcvbyte,
+ "\t\t%d packet%s (%d byte%s) received in-sequence\n");
+ p2(tcps_rcvduppack, tcps_rcvdupbyte,
+ "\t\t%d completely duplicate packet%s (%d byte%s)\n");
+ p(tcps_pawsdrop, "\t\t%d old duplicate packet%s\n");
+ p2(tcps_rcvpartduppack, tcps_rcvpartdupbyte,
+ "\t\t%d packet%s with some dup. data (%d byte%s duped)\n");
+ p2(tcps_rcvoopack, tcps_rcvoobyte,
+ "\t\t%d out-of-order packet%s (%d byte%s)\n");
+ p2(tcps_rcvpackafterwin, tcps_rcvbyteafterwin,
+ "\t\t%d packet%s (%d byte%s) of data after window\n");
+ p(tcps_rcvwinprobe, "\t\t%d window probe%s\n");
+ p(tcps_rcvwinupd, "\t\t%d window update packet%s\n");
+ p(tcps_rcvafterclose, "\t\t%d packet%s received after close\n");
+ p(tcps_rcvbadsum, "\t\t%d discarded for bad checksum%s\n");
+ p(tcps_rcvbadoff, "\t\t%d discarded for bad header offset field%s\n");
+ p(tcps_rcvshort, "\t\t%d discarded because packet too short\n");
+ p(tcps_connattempt, "\t%d connection request%s\n");
+ p(tcps_accepts, "\t%d connection accept%s\n");
+ p(tcps_connects, "\t%d connection%s established (including accepts)\n");
+ p2(tcps_closed, tcps_drops,
+ "\t%d connection%s closed (including %d drop%s)\n");
+ p(tcps_cachedrtt, "\t\t%d connection%s updated cached RTT on close\n");
+ p(tcps_cachedrttvar,
+ "\t\t%d connection%s updated cached RTT variance on close\n");
+ p(tcps_cachedssthresh,
+ "\t\t%d connection%s updated cached ssthresh on close\n");
+ p(tcps_conndrops, "\t%d embryonic connection%s dropped\n");
+ p2(tcps_rttupdated, tcps_segstimed,
+ "\t%d segment%s updated rtt (of %d attempt%s)\n");
+ p(tcps_rexmttimeo, "\t%d retransmit timeout%s\n");
+ p(tcps_timeoutdrop, "\t\t%d connection%s dropped by rexmit timeout\n");
+ p(tcps_persisttimeo, "\t%d persist timeout%s\n");
+ p(tcps_persistdrop, "\t\t%d connection%s dropped by persist timeout\n");
+ p(tcps_keeptimeo, "\t%d keepalive timeout%s\n");
+ p(tcps_keepprobe, "\t\t%d keepalive probe%s sent\n");
+ p(tcps_keepdrops, "\t\t%d connection%s dropped by keepalive\n");
+ p(tcps_predack, "\t%d correct ACK header prediction%s\n");
+ p(tcps_preddat, "\t%d correct data packet header prediction%s\n");
+#undef p
+#undef p2
+#undef p3
+}
+
+/*
+ * Dump UDP statistics structure.
+ */
+void
+udp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct udpstat udpstat;
+ u_long delivered;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&udpstat, sizeof (udpstat));
+ printf("%s:\n", name);
+#define p(f, m) if (udpstat.f || sflag <= 1) \
+ printf(m, udpstat.f, plural(udpstat.f))
+ p(udps_ipackets, "\t%u datagram%s received\n");
+ p(udps_hdrops, "\t%u with incomplete header\n");
+ p(udps_badlen, "\t%u with bad data length field\n");
+ p(udps_badsum, "\t%u with bad checksum\n");
+ p(udps_noport, "\t%u dropped due to no socket\n");
+ p(udps_noportbcast, "\t%u broadcast/multicast datagram%s dropped due to no socket\n");
+ p(udps_fullsock, "\t%u dropped due to full socket buffers\n");
+ delivered = udpstat.udps_ipackets -
+ udpstat.udps_hdrops -
+ udpstat.udps_badlen -
+ udpstat.udps_badsum -
+ udpstat.udps_noport -
+ udpstat.udps_noportbcast -
+ udpstat.udps_fullsock;
+ if (delivered || sflag <= 1)
+ printf("\t%u delivered\n", delivered);
+ p(udps_opackets, "\t%u datagram%s output\n");
+#undef p
+}
+
+/*
+ * Dump IP statistics structure.
+ */
+void
+ip_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct ipstat ipstat;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&ipstat, sizeof (ipstat));
+ printf("%s:\n", name);
+
+#define p(f, m) if (ipstat.f || sflag <= 1) \
+ printf(m, ipstat.f, plural(ipstat.f))
+
+ p(ips_total, "\t%u total packet%s received\n");
+ p(ips_badsum, "\t%u bad header checksum%s\n");
+ p(ips_toosmall, "\t%u with size smaller than minimum\n");
+ p(ips_tooshort, "\t%u with data size < data length\n");
+ p(ips_badhlen, "\t%u with header length < data size\n");
+ p(ips_badlen, "\t%u with data length < header length\n");
+ p(ips_badoptions, "\t%u with bad options\n");
+ p(ips_badvers, "\t%u with incorrect version number\n");
+ p(ips_fragments, "\t%u fragment%s received\n");
+ p(ips_fragdropped, "\t%u fragment%s dropped (dup or out of space)\n");
+ p(ips_fragtimeout, "\t%u fragment%s dropped after timeout\n");
+ p(ips_reassembled, "\t%u packet%s reassembled ok\n");
+ p(ips_delivered, "\t%u packet%s for this host\n");
+ p(ips_noproto, "\t%u packet%s for unknown/unsupported protocol\n");
+ p(ips_forward, "\t%u packet%s forwarded\n");
+ p(ips_cantforward, "\t%u packet%s not forwardable\n");
+ p(ips_redirectsent, "\t%u redirect%s sent\n");
+ p(ips_localout, "\t%u packet%s sent from this host\n");
+ p(ips_rawout, "\t%u packet%s sent with fabricated ip header\n");
+ p(ips_odropped, "\t%u output packet%s dropped due to no bufs, etc.\n");
+ p(ips_noroute, "\t%u output packet%s discarded due to no route\n");
+ p(ips_fragmented, "\t%u output datagram%s fragmented\n");
+ p(ips_ofragments, "\t%u fragment%s created\n");
+ p(ips_cantfrag, "\t%u datagram%s that can't be fragmented\n");
+#undef p
+}
+
+static char *icmpnames[] = {
+ "echo reply",
+ "#1",
+ "#2",
+ "destination unreachable",
+ "source quench",
+ "routing redirect",
+ "#6",
+ "#7",
+ "echo",
+ "router advertisement",
+ "router solicitation",
+ "time exceeded",
+ "parameter problem",
+ "time stamp",
+ "time stamp reply",
+ "information request",
+ "information request reply",
+ "address mask request",
+ "address mask reply",
+};
+
+/*
+ * Dump ICMP statistics.
+ */
+void
+icmp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct icmpstat icmpstat;
+ register int i, first;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&icmpstat, sizeof (icmpstat));
+ printf("%s:\n", name);
+
+#define p(f, m) if (icmpstat.f || sflag <= 1) \
+ printf(m, icmpstat.f, plural(icmpstat.f))
+
+ p(icps_error, "\t%u call%s to icmp_error\n");
+ p(icps_oldicmp,
+ "\t%u error%s not generated 'cuz old message was icmp\n");
+ for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
+ if (icmpstat.icps_outhist[i] != 0) {
+ if (first) {
+ printf("\tOutput histogram:\n");
+ first = 0;
+ }
+ printf("\t\t%s: %u\n", icmpnames[i],
+ icmpstat.icps_outhist[i]);
+ }
+ p(icps_badcode, "\t%u message%s with bad code fields\n");
+ p(icps_tooshort, "\t%u message%s < minimum length\n");
+ p(icps_checksum, "\t%u bad checksum%s\n");
+ p(icps_badlen, "\t%u message%s with bad length\n");
+ for (first = 1, i = 0; i < ICMP_MAXTYPE + 1; i++)
+ if (icmpstat.icps_inhist[i] != 0) {
+ if (first) {
+ printf("\tInput histogram:\n");
+ first = 0;
+ }
+ printf("\t\t%s: %u\n", icmpnames[i],
+ icmpstat.icps_inhist[i]);
+ }
+ p(icps_reflect, "\t%u message response%s generated\n");
+#undef p
+}
+
+/*
+ * Dump IGMP statistics structure.
+ */
+void
+igmp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct igmpstat igmpstat;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&igmpstat, sizeof (igmpstat));
+ printf("%s:\n", name);
+
+#define p(f, m) if (igmpstat.f || sflag <= 1) \
+ printf(m, igmpstat.f, plural(igmpstat.f))
+#define py(f, m) if (igmpstat.f || sflag <= 1) \
+ printf(m, igmpstat.f, igmpstat.f != 1 ? "ies" : "y")
+ p(igps_rcv_total, "\t%u message%s received\n");
+ p(igps_rcv_tooshort, "\t%u message%s received with too few bytes\n");
+ p(igps_rcv_badsum, "\t%u message%s received with bad checksum\n");
+ py(igps_rcv_queries, "\t%u membership quer%s received\n");
+ py(igps_rcv_badqueries, "\t%u membership quer%s received with invalid field(s)\n");
+ p(igps_rcv_reports, "\t%u membership report%s received\n");
+ p(igps_rcv_badreports, "\t%u membership report%s received with invalid field(s)\n");
+ p(igps_rcv_ourreports, "\t%u membership report%s received for groups to which we belong\n");
+ p(igps_snd_reports, "\t%u membership report%s sent\n");
+#undef p
+#undef py
+}
+
+/*
+ * Pretty print an Internet address (net address + port).
+ * If the nflag was specified, use numbers instead of names.
+ */
+void
+inetprint(in, port, proto)
+ register struct in_addr *in;
+ int port;
+ char *proto;
+{
+ struct servent *sp = 0;
+ char line[80], *cp;
+ int width;
+
+ sprintf(line, "%.*s.", (Aflag && !nflag) ? 12 : 16, inetname(in));
+ cp = index(line, '\0');
+ if (!nflag && port)
+ sp = getservbyport((int)port, proto);
+ if (sp || port == 0)
+ sprintf(cp, "%.8s", sp ? sp->s_name : "*");
+ else
+ sprintf(cp, "%d", ntohs((u_short)port));
+ width = Aflag ? 18 : 22;
+ printf(" %-*.*s", width, width, line);
+}
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+char *
+inetname(inp)
+ struct in_addr *inp;
+{
+ register char *cp;
+ static char line[50];
+ struct hostent *hp;
+ struct netent *np;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+
+ if (first && !nflag) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.')))
+ (void) strcpy(domain, cp + 1);
+ else
+ domain[0] = 0;
+ }
+ cp = 0;
+ if (!nflag && inp->s_addr != INADDR_ANY) {
+ int net = inet_netof(*inp);
+ int lna = inet_lnaof(*inp);
+
+ if (lna == INADDR_ANY) {
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ cp = np->n_name;
+ }
+ if (cp == 0) {
+ hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET);
+ if (hp) {
+ if ((cp = index(hp->h_name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ }
+ if (inp->s_addr == INADDR_ANY)
+ strcpy(line, "*");
+ else if (cp)
+ strcpy(line, cp);
+ else {
+ inp->s_addr = ntohl(inp->s_addr);
+#define C(x) ((x) & 0xff)
+ sprintf(line, "%u.%u.%u.%u", C(inp->s_addr >> 24),
+ C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr));
+ }
+ return (line);
+}
diff --git a/usr.bin/netstat/iso.c b/usr.bin/netstat/iso.c
new file mode 100644
index 0000000..ade4144
--- /dev/null
+++ b/usr.bin/netstat/iso.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)iso.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * $Header: /home/ncvs/src/usr.bin/netstat/iso.c,v 1.1.1.1 1994/05/27 12:32:25 rgrimes Exp $
+ * $Source: /home/ncvs/src/usr.bin/netstat/iso.c,v $
+ */
+/*******************************************************************************
+ Copyright IBM Corporation 1987
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+*******************************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netiso/iso.h>
+#include <netiso/iso_errno.h>
+#include <netiso/clnp.h>
+#include <netiso/esis.h>
+#include <netiso/clnp_stat.h>
+#include <netiso/argo_debug.h>
+#undef satosiso
+#include <netiso/tp_param.h>
+#include <netiso/tp_states.h>
+#include <netiso/tp_pcb.h>
+#include <netiso/tp_stat.h>
+#include <netiso/iso_pcb.h>
+#include <netiso/cltp_var.h>
+#include <netiso/cons.h>
+#ifdef IncStat
+#undef IncStat
+#endif
+#include <netiso/cons_pcb.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "netstat.h"
+
+static void tprintstat __P((struct tp_stat *, int));
+static void isonetprint __P((struct sockaddr_iso *, int));
+static void hexprint __P((int, char *, char *));
+extern void inetprint __P((struct in_addr *, int, char *));
+
+/*
+ * Dump esis stats
+ */
+void
+esis_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct esis_stat esis_stat;
+
+ if (off == 0 ||
+ kread(off, (char *)&esis_stat, sizeof (struct esis_stat)))
+ return;
+ printf("%s:\n", name);
+ printf("\t%d esh sent, %d esh received\n", esis_stat.es_eshsent,
+ esis_stat.es_eshrcvd);
+ printf("\t%d ish sent, %d ish received\n", esis_stat.es_ishsent,
+ esis_stat.es_ishrcvd);
+ printf("\t%d rd sent, %d rd received\n", esis_stat.es_rdsent,
+ esis_stat.es_rdrcvd);
+ printf("\t%d pdus not sent due to insufficient memory\n",
+ esis_stat.es_nomem);
+ printf("\t%d pdus received with bad checksum\n", esis_stat.es_badcsum);
+ printf("\t%d pdus received with bad version number\n",
+ esis_stat.es_badvers);
+ printf("\t%d pdus received with bad type field\n", esis_stat.es_badtype);
+ printf("\t%d short pdus received\n", esis_stat.es_toosmall);
+}
+
+/*
+ * Dump clnp statistics structure.
+ */
+void
+clnp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct clnp_stat clnp_stat;
+
+ if (off == 0 ||
+ kread(off, (char *)&clnp_stat, sizeof (clnp_stat)))
+ return;
+
+ printf("%s:\n\t%d total packets sent\n", name, clnp_stat.cns_sent);
+ printf("\t%d total fragments sent\n", clnp_stat.cns_fragments);
+ printf("\t%d total packets received\n", clnp_stat.cns_total);
+ printf("\t%d with fixed part of header too small\n",
+ clnp_stat.cns_toosmall);
+ printf("\t%d with header length not reasonable\n", clnp_stat.cns_badhlen);
+ printf("\t%d incorrect checksum%s\n",
+ clnp_stat.cns_badcsum, plural(clnp_stat.cns_badcsum));
+ printf("\t%d with unreasonable address lengths\n", clnp_stat.cns_badaddr);
+ printf("\t%d with forgotten segmentation information\n",
+ clnp_stat.cns_noseg);
+ printf("\t%d with an incorrect protocol identifier\n", clnp_stat.cns_noproto);
+ printf("\t%d with an incorrect version\n", clnp_stat.cns_badvers);
+ printf("\t%d dropped because the ttl has expired\n",
+ clnp_stat.cns_ttlexpired);
+ printf("\t%d clnp cache misses\n", clnp_stat.cns_cachemiss);
+ printf("\t%d clnp congestion experience bits set\n",
+ clnp_stat.cns_congest_set);
+ printf("\t%d clnp congestion experience bits received\n",
+ clnp_stat.cns_congest_rcvd);
+}
+/*
+ * Dump CLTP statistics structure.
+ */
+void
+cltp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct cltpstat cltpstat;
+
+ if (off == 0 ||
+ kread(off, (char *)&cltpstat, sizeof (cltpstat)))
+ return;
+ printf("%s:\n\t%u incomplete header%s\n", name,
+ cltpstat.cltps_hdrops, plural(cltpstat.cltps_hdrops));
+ printf("\t%u bad data length field%s\n",
+ cltpstat.cltps_badlen, plural(cltpstat.cltps_badlen));
+ printf("\t%u bad checksum%s\n",
+ cltpstat.cltps_badsum, plural(cltpstat.cltps_badsum));
+}
+
+struct tp_pcb tpcb;
+struct isopcb isopcb;
+struct socket sockb;
+union {
+ struct sockaddr_iso siso;
+ char data[128];
+} laddr, faddr;
+#define kget(o, p) \
+ (kread((u_long)(o), (char *)&p, sizeof (p)))
+
+static int first = 1;
+
+/*
+ * Print a summary of connections related to an Internet
+ * protocol. For TP, also give state of connection.
+ * Listening processes (aflag) are suppressed unless the
+ * -a (all) flag is specified.
+ */
+void
+iso_protopr(off, name)
+ u_long off;
+ char *name;
+{
+ struct isopcb cb;
+ register struct isopcb *prev, *next;
+
+ if (off == 0) {
+ printf("%s control block: symbol not in namelist\n", name);
+ return;
+ }
+ if (strcmp(name, "tp") == 0) {
+ tp_protopr(off, name);
+ return;
+ }
+ if (kread(off, (char *)&cb, sizeof(cb)))
+ return;
+ isopcb = cb;
+ prev = (struct isopcb *)off;
+ if (isopcb.isop_next == (struct isopcb *)off)
+ return;
+ while (isopcb.isop_next != (struct isopcb *)off) {
+ next = isopcb.isop_next;
+ kget(next, isopcb);
+ if (isopcb.isop_prev != prev) {
+ printf("prev 0x%x next 0x%x isop_prev 0x%x isop_next 0x%x???\n",
+ prev, next, isopcb.isop_prev, isopcb.isop_next);
+ break;
+ }
+ kget(isopcb.isop_socket, sockb);
+ iso_protopr1((u_long)next, 0);
+ putchar('\n');
+ prev = next;
+ }
+}
+
+void
+iso_protopr1(kern_addr, istp)
+ u_long kern_addr;
+ int istp;
+{
+ if (first) {
+ printf("Active ISO net connections");
+ if (aflag)
+ printf(" (including servers)");
+ putchar('\n');
+ if (Aflag)
+ printf("%-8.8s ", "PCB");
+ printf(Aflag ?
+ "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %s\n" :
+ "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %s\n",
+ "Proto", "Recv-Q", "Send-Q",
+ "Local Address", "Foreign Address", "(state)");
+ first = 0;
+ }
+ if (Aflag)
+ printf("%8x ",
+ (sockb.so_pcb ? (void *)sockb.so_pcb : (void *)kern_addr));
+ printf("%-5.5s %6d %6d ", "tp", sockb.so_rcv.sb_cc, sockb.so_snd.sb_cc);
+ if (istp && tpcb.tp_lsuffixlen) {
+ hexprint(tpcb.tp_lsuffixlen, tpcb.tp_lsuffix, "()");
+ printf("\t");
+ } else if (isopcb.isop_laddr == 0)
+ printf("*.*\t");
+ else {
+ if ((char *)isopcb.isop_laddr == ((char *)kern_addr) +
+ _offsetof(struct isopcb, isop_sladdr))
+ laddr.siso = isopcb.isop_sladdr;
+ else
+ kget(isopcb.isop_laddr, laddr);
+ isonetprint((struct sockaddr_iso *)&laddr, 1);
+ }
+ if (istp && tpcb.tp_fsuffixlen) {
+ hexprint(tpcb.tp_fsuffixlen, tpcb.tp_fsuffix, "()");
+ printf("\t");
+ } else if (isopcb.isop_faddr == 0)
+ printf("*.*\t");
+ else {
+ if ((char *)isopcb.isop_faddr == ((char *)kern_addr) +
+ _offsetof(struct isopcb, isop_sfaddr))
+ faddr.siso = isopcb.isop_sfaddr;
+ else
+ kget(isopcb.isop_faddr, faddr);
+ isonetprint((struct sockaddr_iso *)&faddr, 0);
+ }
+}
+
+void
+tp_protopr(off, name)
+ u_long off;
+ char *name;
+{
+ extern char *tp_sstring[];
+ struct tp_ref *tpr, *tpr_base;
+ struct tp_refinfo tpkerninfo;
+ int size;
+
+ kget(off, tpkerninfo);
+ size = tpkerninfo.tpr_size * sizeof (*tpr);
+ tpr_base = (struct tp_ref *)malloc(size);
+ if (tpr_base == 0)
+ return;
+ kread((u_long)(tpkerninfo.tpr_base), (char *)tpr_base, size);
+ for (tpr = tpr_base; tpr < tpr_base + tpkerninfo.tpr_size; tpr++) {
+ if (tpr->tpr_pcb == 0)
+ continue;
+ kget(tpr->tpr_pcb, tpcb);
+ if (tpcb.tp_state == ST_ERROR)
+ printf("undefined tpcb state: 0x%x\n", tpr->tpr_pcb);
+ if (!aflag &&
+ (tpcb.tp_state == TP_LISTENING ||
+ tpcb.tp_state == TP_CLOSED ||
+ tpcb.tp_state == TP_REFWAIT)) {
+ continue;
+ }
+ kget(tpcb.tp_sock, sockb);
+ if (tpcb.tp_npcb) switch(tpcb.tp_netservice) {
+ case IN_CLNS:
+ tp_inproto((u_long)tpkerninfo.tpr_base);
+ break;
+ default:
+ kget(tpcb.tp_npcb, isopcb);
+ iso_protopr1((u_long)tpcb.tp_npcb, 1);
+ break;
+ }
+ if (tpcb.tp_state >= tp_NSTATES)
+ printf(" %d", tpcb.tp_state);
+ else
+ printf(" %-12.12s", tp_sstring[tpcb.tp_state]);
+ putchar('\n');
+ }
+}
+
+void
+tp_inproto(pcb)
+ u_long pcb;
+{
+ struct inpcb inpcb;
+ kget(tpcb.tp_npcb, inpcb);
+ if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
+ return;
+ if (Aflag)
+ printf("%8x ", pcb);
+ printf("%-5.5s %6d %6d ", "tpip",
+ sockb.so_rcv.sb_cc, sockb.so_snd.sb_cc);
+ inetprint(&inpcb.inp_laddr, inpcb.inp_lport, "tp");
+ inetprint(&inpcb.inp_faddr, inpcb.inp_fport, "tp");
+}
+
+/*
+ * Pretty print an iso address (net address + port).
+ * If the nflag was specified, use numbers instead of names.
+ */
+
+#ifdef notdef
+char *
+isonetname(iso)
+ register struct iso_addr *iso;
+{
+ struct sockaddr_iso sa;
+ struct iso_hostent *ihe = 0;
+ struct iso_hostent *iso_gethostentrybyaddr();
+ struct iso_hostent *iso_getserventrybytsel();
+ struct iso_hostent Ihe;
+ static char line[80];
+
+ bzero(line, sizeof(line));
+ if( iso->isoa_afi ) {
+ sa.siso_family = AF_ISO;
+ sa.siso_addr = *iso;
+ sa.siso_tsuffix = 0;
+
+ if (!nflag )
+ ihe = iso_gethostentrybyaddr( &sa, 0, 0 );
+ if( ihe ) {
+ Ihe = *ihe;
+ ihe = &Ihe;
+ sprintf(line, "%s", ihe->isoh_hname);
+ } else {
+ sprintf(line, "%s", iso_ntoa(iso));
+ }
+ } else {
+ sprintf(line, "*");
+ }
+ return line;
+}
+
+static void
+isonetprint(iso, sufx, sufxlen, islocal)
+ register struct iso_addr *iso;
+ char *sufx;
+ u_short sufxlen;
+ int islocal;
+{
+ struct iso_hostent *iso_getserventrybytsel(), *ihe;
+ struct iso_hostent Ihe;
+ char *line, *cp;
+ int Alen = Aflag?18:22;
+
+ line = isonetname(iso);
+ cp = index(line, '\0');
+ ihe = (struct iso_hostent *)0;
+
+ if( islocal )
+ islocal = 20;
+ else
+ islocal = 22 + Alen;
+
+ if(Aflag)
+ islocal += 10 ;
+
+ if(!nflag) {
+ if( (cp -line)>10 ) {
+ cp = line+10;
+ bzero(cp, sizeof(line)-10);
+ }
+ }
+
+ *cp++ = '.';
+ if(sufxlen) {
+ if( !Aflag && !nflag && (ihe=iso_getserventrybytsel(sufx, sufxlen))) {
+ Ihe = *ihe;
+ ihe = &Ihe;
+ }
+ if( ihe && (strlen(ihe->isoh_aname)>0) ) {
+ sprintf(cp, "%s", ihe->isoh_aname);
+ } else {
+ iso_sprinttsel(cp, sufx, sufxlen);
+ }
+ } else
+ sprintf(cp, "*");
+ /*
+ fprintf(stdout, Aflag?" %-18.18s":" %-22.22s", line);
+ */
+
+ if( strlen(line) > Alen ) {
+ fprintf(stdout, " %s", line);
+ fprintf(stdout, "\n %*.s", islocal+Alen," ");
+ } else {
+ fprintf(stdout, " %-*.*s", Alen, Alen,line);
+ }
+}
+#endif
+
+#ifdef notdef
+static void
+x25_protopr(off, name)
+ u_long off;
+ char *name;
+{
+ static char *xpcb_states[] = {
+ "CLOSED",
+ "LISTENING",
+ "CLOSING",
+ "CONNECTING",
+ "ACKWAIT",
+ "OPEN",
+ };
+ register struct isopcb *prev, *next;
+ struct x25_pcb xpcb;
+
+ if (off == 0) {
+ printf("%s control block: symbol not in namelist\n", name);
+ return;
+ }
+ kread(off, &xpcb, sizeof (struct x25_pcb));
+ prev = (struct isopcb *)off;
+ if (xpcb.x_next == (struct isopcb *)off)
+ return;
+ while (xpcb.x_next != (struct isopcb *)off) {
+ next = isopcb.isop_next;
+ kread((u_long)next, &xpcb, sizeof (struct x25_pcb));
+ if (xpcb.x_prev != prev) {
+ printf("???\n");
+ break;
+ }
+ kread((u_long)xpcb.x_socket, &sockb, sizeof (sockb));
+
+ if (!aflag &&
+ xpcb.x_state == LISTENING ||
+ xpcb.x_state == TP_CLOSED ) {
+ prev = next;
+ continue;
+ }
+ if (first) {
+ printf("Active X25 net connections");
+ if (aflag)
+ printf(" (including servers)");
+ putchar('\n');
+ if (Aflag)
+ printf("%-8.8s ", "PCB");
+ printf(Aflag ?
+ "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %s\n" :
+ "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %s\n",
+ "Proto", "Recv-Q", "Send-Q",
+ "Local Address", "Foreign Address", "(state)");
+ first = 0;
+ }
+ printf("%-5.5s %6d %6d ", name, sockb.so_rcv.sb_cc,
+ sockb.so_snd.sb_cc);
+ isonetprint(&xpcb.x_laddr.siso_addr, &xpcb.x_lport,
+ sizeof(xpcb.x_lport), 1);
+ isonetprint(&xpcb.x_faddr.siso_addr, &xpcb.x_fport,
+ sizeof(xpcb.x_lport), 0);
+ if (xpcb.x_state < 0 || xpcb.x_state >= x25_NSTATES)
+ printf(" 0x0x0x0x0x0x0x0x0x%x", xpcb.x_state);
+ else
+ printf(" %-12.12s", xpcb_states[xpcb.x_state]);
+ putchar('\n');
+ prev = next;
+ }
+}
+#endif
+
+struct tp_stat tp_stat;
+
+void
+tp_stats(off, name)
+ caddr_t off, name;
+{
+ if (off == 0) {
+ printf("TP not configured\n\n");
+ return;
+ }
+ printf("%s:\n", name);
+ kget(off, tp_stat);
+ tprintstat(&tp_stat, 8);
+}
+
+#define OUT stdout
+
+static void
+tprintstat(s, indent)
+ register struct tp_stat *s;
+ int indent;
+{
+ fprintf(OUT,
+ "%*sReceiving:\n",indent," ");
+ fprintf(OUT,
+ "\t%*s%d variable parameter%s ignored\n", indent," ",
+ s->ts_param_ignored ,plural(s->ts_param_ignored));
+ fprintf(OUT,
+ "\t%*s%d invalid parameter code%s\n", indent, " ",
+ s->ts_inv_pcode ,plural(s->ts_inv_pcode));
+ fprintf(OUT,
+ "\t%*s%d invalid parameter value%s\n", indent, " ",
+ s->ts_inv_pval ,plural(s->ts_inv_pval));
+ fprintf(OUT,
+ "\t%*s%d invalid dutype%s\n", indent, " ",
+ s->ts_inv_dutype ,plural(s->ts_inv_dutype));
+ fprintf(OUT,
+ "\t%*s%d negotiation failure%s\n", indent, " ",
+ s->ts_negotfailed ,plural(s->ts_negotfailed));
+ fprintf(OUT,
+ "\t%*s%d invalid destination reference%s\n", indent, " ",
+ s->ts_inv_dref ,plural(s->ts_inv_dref));
+ fprintf(OUT,
+ "\t%*s%d invalid suffix parameter%s\n", indent, " ",
+ s->ts_inv_sufx ,plural(s->ts_inv_sufx));
+ fprintf(OUT,
+ "\t%*s%d invalid length\n",indent, " ", s->ts_inv_length);
+ fprintf(OUT,
+ "\t%*s%d invalid checksum%s\n", indent, " ",
+ s->ts_bad_csum ,plural(s->ts_bad_csum));
+ fprintf(OUT,
+ "\t%*s%d DT%s out of order\n", indent, " ",
+ s->ts_dt_ooo ,plural(s->ts_dt_ooo));
+ fprintf(OUT,
+ "\t%*s%d DT%s not in window\n", indent, " ",
+ s->ts_dt_niw ,plural(s->ts_dt_niw));
+ fprintf(OUT,
+ "\t%*s%d duplicate DT%s\n", indent, " ",
+ s->ts_dt_dup ,plural(s->ts_dt_dup));
+ fprintf(OUT,
+ "\t%*s%d XPD%s not in window\n", indent, " ",
+ s->ts_xpd_niw ,plural(s->ts_xpd_niw));
+ fprintf(OUT,
+ "\t%*s%d XPD%s w/o credit to stash\n", indent, " ",
+ s->ts_xpd_dup ,plural(s->ts_xpd_dup));
+ fprintf(OUT,
+ "\t%*s%d time%s local credit reneged\n", indent, " ",
+ s->ts_lcdt_reduced ,plural(s->ts_lcdt_reduced));
+ fprintf(OUT,
+ "\t%*s%d concatenated TPDU%s\n", indent, " ",
+ s->ts_concat_rcvd ,plural(s->ts_concat_rcvd));
+ fprintf(OUT,
+ "%*sSending:\n", indent, " ");
+ fprintf(OUT,
+ "\t%*s%d XPD mark%s discarded\n", indent, " ",
+ s->ts_xpdmark_del ,plural(s->ts_xpdmark_del));
+ fprintf(OUT,
+ "\t%*sXPD stopped data flow %d time%s\n", indent, " ",
+ s->ts_xpd_intheway ,plural(s->ts_xpd_intheway));
+ fprintf(OUT,
+ "\t%*s%d time%s foreign window closed\n", indent, " ",
+ s->ts_zfcdt ,plural(s->ts_zfcdt));
+ fprintf(OUT,
+ "%*sMiscellaneous:\n", indent, " ");
+ fprintf(OUT,
+ "\t%*s%d small mbuf%s\n", indent, " ",
+ s->ts_mb_small ,plural(s->ts_mb_small));
+ fprintf(OUT,
+ "\t%*s%d cluster%s\n", indent, " ",
+ s->ts_mb_cluster, plural(s->ts_mb_cluster));
+ fprintf(OUT,
+ "\t%*s%d source quench \n",indent, " ",
+ s->ts_quench);
+ fprintf(OUT,
+ "\t%*s%d dec bit%s\n", indent, " ",
+ s->ts_rcvdecbit, plural(s->ts_rcvdecbit));
+ fprintf(OUT,
+ "\t%*sM:L ( M mbuf chains of length L)\n", indent, " ");
+ {
+ register int j;
+
+ fprintf(OUT, "\t%*s%d: over 16\n", indent, " ",
+ s->ts_mb_len_distr[0]);
+ for( j=1; j<=8; j++) {
+ fprintf(OUT,
+ "\t%*s%d: %d\t\t%d: %d\n", indent, " ",
+ s->ts_mb_len_distr[j],j,
+ s->ts_mb_len_distr[j<<1],j<<1
+ );
+ }
+ }
+ fprintf(OUT,
+ "\t%*s%d EOT rcvd\n", indent, " ", s->ts_eot_input);
+ fprintf(OUT,
+ "\t%*s%d EOT sent\n", indent, " ", s->ts_EOT_sent);
+ fprintf(OUT,
+ "\t%*s%d EOT indication%s\n", indent, " ",
+ s->ts_eot_user ,plural(s->ts_eot_user));
+
+ fprintf(OUT,
+ "%*sConnections:\n", indent, " ");
+ fprintf(OUT,
+ "\t%*s%d connection%s used extended format\n", indent, " ",
+ s->ts_xtd_fmt ,plural(s->ts_xtd_fmt));
+ fprintf(OUT,
+ "\t%*s%d connection%s allowed transport expedited data\n", indent, " ",
+ s->ts_use_txpd ,plural(s->ts_use_txpd));
+ fprintf(OUT,
+ "\t%*s%d connection%s turned off checksumming\n", indent, " ",
+ s->ts_csum_off ,plural(s->ts_csum_off));
+ fprintf(OUT,
+ "\t%*s%d connection%s dropped due to retrans limit\n", indent, " ",
+ s->ts_conn_gaveup ,plural(s->ts_conn_gaveup));
+ fprintf(OUT,
+ "\t%*s%d tp 4 connection%s\n", indent, " ",
+ s->ts_tp4_conn ,plural(s->ts_tp4_conn));
+ fprintf(OUT,
+ "\t%*s%d tp 0 connection%s\n", indent, " ",
+ s->ts_tp0_conn ,plural(s->ts_tp0_conn));
+ {
+ register int j;
+ static char *name[]= {
+ "~LOCAL, PDN",
+ "~LOCAL,~PDN",
+ " LOCAL,~PDN",
+ " LOCAL, PDN"
+ };
+
+ fprintf(OUT,
+ "\n%*sRound trip times, listed in ticks:\n", indent, " ");
+ fprintf(OUT,
+ "\t%*s%11.11s %12.12s | %12.12s | %s\n", indent, " ",
+ "Category",
+ "Smoothed avg", "Deviation", "Deviation/Avg");
+ for (j = 0; j <= 3; j++) {
+ fprintf(OUT,
+ "\t%*s%11.11s: %-11d | %-11d | %-11d | %-11d\n", indent, " ",
+ name[j],
+ s->ts_rtt[j],
+ s->ts_rtt[j],
+ s->ts_rtv[j],
+ s->ts_rtv[j]);
+ }
+ }
+ fprintf(OUT,
+"\n%*sTpdus RECVD [%d valid, %3.6f %% of total (%d); %d dropped]\n",indent," ",
+ s->ts_tpdu_rcvd ,
+ ((s->ts_pkt_rcvd > 0) ?
+ ((100 * (float)s->ts_tpdu_rcvd)/(float)s->ts_pkt_rcvd)
+ : 0),
+ s->ts_pkt_rcvd,
+ s->ts_recv_drop );
+
+ fprintf(OUT,
+ "\t%*sDT %6d AK %6d DR %4d CR %4d \n", indent, " ",
+ s->ts_DT_rcvd, s->ts_AK_rcvd, s->ts_DR_rcvd, s->ts_CR_rcvd);
+ fprintf(OUT,
+ "\t%*sXPD %6d XAK %6d DC %4d CC %4d ER %4d\n", indent, " ",
+ s->ts_XPD_rcvd, s->ts_XAK_rcvd, s->ts_DC_rcvd, s->ts_CC_rcvd,
+ s->ts_ER_rcvd);
+ fprintf(OUT,
+ "\n%*sTpdus SENT [%d total, %d dropped]\n", indent, " ",
+ s->ts_tpdu_sent, s->ts_send_drop);
+
+ fprintf(OUT,
+ "\t%*sDT %6d AK %6d DR %4d CR %4d \n", indent, " ",
+ s->ts_DT_sent, s->ts_AK_sent, s->ts_DR_sent, s->ts_CR_sent);
+ fprintf(OUT,
+ "\t%*sXPD %6d XAK %6d DC %4d CC %4d ER %4d\n", indent, " ",
+ s->ts_XPD_sent, s->ts_XAK_sent, s->ts_DC_sent, s->ts_CC_sent,
+ s->ts_ER_sent);
+
+ fprintf(OUT,
+ "\n%*sRetransmissions:\n", indent, " ");
+#define PERCENT(X,Y) (((Y)>0)?((100 *(float)(X)) / (float) (Y)):0)
+
+ fprintf(OUT,
+ "\t%*sCR %6d CC %6d DR %6d \n", indent, " ",
+ s->ts_retrans_cr, s->ts_retrans_cc, s->ts_retrans_dr);
+ fprintf(OUT,
+ "\t%*sDT %6d (%5.2f%%)\n", indent, " ",
+ s->ts_retrans_dt,
+ PERCENT(s->ts_retrans_dt, s->ts_DT_sent));
+ fprintf(OUT,
+ "\t%*sXPD %6d (%5.2f%%)\n", indent, " ",
+ s->ts_retrans_xpd,
+ PERCENT(s->ts_retrans_xpd, s->ts_XPD_sent));
+
+
+ fprintf(OUT,
+ "\n%*sE Timers: [%6d ticks]\n", indent, " ", s->ts_Eticks);
+ fprintf(OUT,
+ "%*s%6d timer%s set \t%6d timer%s expired \t%6d timer%s cancelled\n",indent, " ",
+ s->ts_Eset ,plural(s->ts_Eset),
+ s->ts_Eexpired ,plural(s->ts_Eexpired),
+ s->ts_Ecan_act ,plural(s->ts_Ecan_act));
+
+ fprintf(OUT,
+ "\n%*sC Timers: [%6d ticks]\n", indent, " ",s->ts_Cticks);
+ fprintf(OUT,
+ "%*s%6d timer%s set \t%6d timer%s expired \t%6d timer%s cancelled\n",
+ indent, " ",
+ s->ts_Cset ,plural(s->ts_Cset),
+ s->ts_Cexpired ,plural(s->ts_Cexpired),
+ s->ts_Ccan_act ,plural(s->ts_Ccan_act));
+ fprintf(OUT,
+ "%*s%6d inactive timer%s cancelled\n", indent, " ",
+ s->ts_Ccan_inact ,plural(s->ts_Ccan_inact));
+
+ fprintf(OUT,
+ "\n%*sPathological debugging activity:\n", indent, " ");
+ fprintf(OUT,
+ "\t%*s%6d CC%s sent to zero dref\n", indent, " ",
+ s->ts_zdebug ,plural(s->ts_zdebug));
+ /* SAME LINE AS ABOVE */
+ fprintf(OUT,
+ "\t%*s%6d random DT%s dropped\n", indent, " ",
+ s->ts_ydebug ,plural(s->ts_ydebug));
+ fprintf(OUT,
+ "\t%*s%6d illegally large XPD TPDU%s\n", indent, " ",
+ s->ts_vdebug ,plural(s->ts_vdebug));
+ fprintf(OUT,
+ "\t%*s%6d faked reneging of cdt\n", indent, " ",
+ s->ts_ldebug );
+
+ fprintf(OUT,
+ "\n%*sACK reasons:\n", indent, " ");
+ fprintf(OUT, "\t%*s%6d not acked immediately\n", indent, " ",
+ s->ts_ackreason[_ACK_DONT_] );
+ fprintf(OUT, "\t%*s%6d strategy==each\n", indent, " ",
+ s->ts_ackreason[_ACK_STRAT_EACH_] );
+ fprintf(OUT, "\t%*s%6d strategy==fullwindow\n", indent, " ",
+ s->ts_ackreason[_ACK_STRAT_FULLWIN_] );
+ fprintf(OUT, "\t%*s%6d duplicate DT\n", indent, " ",
+ s->ts_ackreason[_ACK_DUP_] );
+ fprintf(OUT, "\t%*s%6d EOTSDU\n", indent, " ",
+ s->ts_ackreason[_ACK_EOT_] );
+ fprintf(OUT, "\t%*s%6d reordered DT\n", indent, " ",
+ s->ts_ackreason[_ACK_REORDER_] );
+ fprintf(OUT, "\t%*s%6d user rcvd\n", indent, " ",
+ s->ts_ackreason[_ACK_USRRCV_] );
+ fprintf(OUT, "\t%*s%6d fcc reqd\n", indent, " ",
+ s->ts_ackreason[_ACK_FCC_] );
+}
+#ifndef SSEL
+#define SSEL(s) ((s)->siso_tlen + TSEL(s))
+#define PSEL(s) ((s)->siso_slen + SSEL(s))
+#endif
+
+static void
+isonetprint(siso, islocal)
+ register struct sockaddr_iso *siso;
+ int islocal;
+{
+ hexprint(siso->siso_nlen, siso->siso_addr.isoa_genaddr, "{}");
+ if (siso->siso_tlen || siso->siso_slen || siso->siso_plen)
+ hexprint(siso->siso_tlen, TSEL(siso), "()");
+ if (siso->siso_slen || siso->siso_plen)
+ hexprint(siso->siso_slen, SSEL(siso), "[]");
+ if (siso->siso_plen)
+ hexprint(siso->siso_plen, PSEL(siso), "<>");
+ putchar(' ');
+}
+
+static char hexlist[] = "0123456789abcdef", obuf[128];
+
+static void
+hexprint(n, buf, delim)
+ int n;
+ char *buf, *delim;
+{
+ register u_char *in = (u_char *)buf, *top = in + n;
+ register char *out = obuf;
+ register int i;
+
+ if (n == 0)
+ return;
+ while (in < top) {
+ i = *in++;
+ *out++ = '.';
+ if (i > 0xf) {
+ out[1] = hexlist[i & 0xf];
+ i >>= 4;
+ out[0] = hexlist[i];
+ out += 2;
+ } else
+ *out++ = hexlist[i];
+ }
+ *obuf = *delim; *out++ = delim[1]; *out = 0;
+ printf("%s", obuf);
+}
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
new file mode 100644
index 0000000..c8f2a98
--- /dev/null
+++ b/usr.bin/netstat/main.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993\n\
+ Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 3/1/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include "netstat.h"
+
+struct nlist nl[] = {
+#define N_MBSTAT 0
+ { "_mbstat" },
+#define N_IPSTAT 1
+ { "_ipstat" },
+#define N_TCB 2
+ { "_tcb" },
+#define N_TCPSTAT 3
+ { "_tcpstat" },
+#define N_UDB 4
+ { "_udb" },
+#define N_UDPSTAT 5
+ { "_udpstat" },
+#define N_IFNET 6
+ { "_ifnet" },
+#define N_IMP 7
+ { "_imp_softc" },
+#define N_ICMPSTAT 8
+ { "_icmpstat" },
+#define N_RTSTAT 9
+ { "_rtstat" },
+#define N_UNIXSW 10
+ { "_unixsw" },
+#define N_IDP 11
+ { "_nspcb"},
+#define N_IDPSTAT 12
+ { "_idpstat"},
+#define N_SPPSTAT 13
+ { "_spp_istat"},
+#define N_NSERR 14
+ { "_ns_errstat"},
+#define N_CLNPSTAT 15
+ { "_clnp_stat"},
+#define IN_NOTUSED 16
+ { "_tp_inpcb" },
+#define ISO_TP 17
+ { "_tp_refinfo" },
+#define N_TPSTAT 18
+ { "_tp_stat" },
+#define N_ESISSTAT 19
+ { "_esis_stat"},
+#define N_NIMP 20
+ { "_nimp"},
+#define N_RTREE 21
+ { "_rt_tables"},
+#define N_CLTP 22
+ { "_cltb"},
+#define N_CLTPSTAT 23
+ { "_cltpstat"},
+#define N_NFILE 24
+ { "_nfile" },
+#define N_FILE 25
+ { "_file" },
+#define N_IGMPSTAT 26
+ { "_igmpstat" },
+#define N_MRTPROTO 27
+ { "_ip_mrtproto" },
+#define N_MRTSTAT 28
+ { "_mrtstat" },
+#define N_MFCTABLE 29
+ { "_mfctable" },
+#define N_VIFTABLE 30
+ { "_viftable" },
+ "",
+};
+
+struct protox {
+ u_char pr_index; /* index into nlist of cb head */
+ u_char pr_sindex; /* index into nlist of stat block */
+ u_char pr_wanted; /* 1 if wanted, 0 otherwise */
+ void (*pr_cblocks)(); /* control blocks printing routine */
+ void (*pr_stats)(); /* statistics printing routine */
+ char *pr_name; /* well-known name */
+} protox[] = {
+ { N_TCB, N_TCPSTAT, 1, protopr,
+ tcp_stats, "tcp" },
+ { N_UDB, N_UDPSTAT, 1, protopr,
+ udp_stats, "udp" },
+ { -1, N_IPSTAT, 1, 0,
+ ip_stats, "ip" },
+ { -1, N_ICMPSTAT, 1, 0,
+ icmp_stats, "icmp" },
+ { -1, N_IGMPSTAT, 1, 0,
+ igmp_stats, "igmp" },
+ { -1, -1, 0, 0,
+ 0, 0 }
+};
+
+struct protox nsprotox[] = {
+ { N_IDP, N_IDPSTAT, 1, nsprotopr,
+ idp_stats, "idp" },
+ { N_IDP, N_SPPSTAT, 1, nsprotopr,
+ spp_stats, "spp" },
+ { -1, N_NSERR, 1, 0,
+ nserr_stats, "ns_err" },
+ { -1, -1, 0, 0,
+ 0, 0 }
+};
+
+struct protox isoprotox[] = {
+ { ISO_TP, N_TPSTAT, 1, iso_protopr,
+ tp_stats, "tp" },
+ { N_CLTP, N_CLTPSTAT, 1, iso_protopr,
+ cltp_stats, "cltp" },
+ { -1, N_CLNPSTAT, 1, 0,
+ clnp_stats, "clnp"},
+ { -1, N_ESISSTAT, 1, 0,
+ esis_stats, "esis"},
+ { -1, -1, 0, 0,
+ 0, 0 }
+};
+
+struct protox *protoprotox[] = { protox, nsprotox, isoprotox, NULL };
+
+static void printproto __P((struct protox *, char *));
+static void usage __P((void));
+static struct protox *name2protox __P((char *));
+static struct protox *knownname __P((char *));
+
+kvm_t *kvmd;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ register struct protoent *p;
+ register struct protox *tp; /* for printing cblocks & stats */
+ register char *cp;
+ int ch;
+ char *nlistf = NULL, *memf = NULL;
+ char buf[_POSIX2_LINE_MAX];
+ char buf2[_POSIX2_LINE_MAX];
+
+ if (cp = rindex(argv[0], '/'))
+ prog = cp + 1;
+ else
+ prog = argv[0];
+ af = AF_UNSPEC;
+
+ while ((ch = getopt(argc, argv, "Aabdf:ghI:iM:mN:np:rstuw:")) != EOF)
+ switch(ch) {
+ case 'A':
+ Aflag = 1;
+ break;
+ case 'a':
+ aflag = 1;
+ break;
+ case 'b':
+ bflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ if (strcmp(optarg, "ns") == 0)
+ af = AF_NS;
+ else if (strcmp(optarg, "inet") == 0)
+ af = AF_INET;
+ else if (strcmp(optarg, "unix") == 0)
+ af = AF_UNIX;
+ else if (strcmp(optarg, "iso") == 0)
+ af = AF_ISO;
+ else {
+ errx(1, "%s: unknown address family", optarg);
+ }
+ break;
+ case 'g':
+ gflag = 1;
+ break;
+ case 'I': {
+ char *cp;
+
+ iflag = 1;
+ for (cp = interface = optarg; isalpha(*cp); cp++)
+ continue;
+ unit = atoi(cp);
+ break;
+ }
+ case 'i':
+ iflag = 1;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'm':
+ mflag = 1;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'p':
+ if ((tp = name2protox(optarg)) == NULL) {
+ errx(1,
+ "%s: unknown or uninstrumented protocol",
+ optarg);
+ }
+ pflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ ++sflag;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'u':
+ af = AF_UNIX;
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ iflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ if (isdigit(**argv)) {
+ interval = atoi(*argv);
+ if (interval <= 0)
+ usage();
+ ++argv;
+ iflag = 1;
+ }
+ if (*argv) {
+ nlistf = *argv;
+ if (*++argv)
+ memf = *argv;
+ }
+ }
+#endif
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ kvmd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
+ if (kvmd == NULL) {
+ errx(1, "kvm_open: %s", buf);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ if(nlistf)
+ errx(1, "%s: kvm_nlist: %s", nlistf, kvm_geterr(kvmd));
+ else
+ errx(1, "kvm_nlist: %s", kvm_geterr(kvmd));
+ }
+
+ if (nl[0].n_type == 0) {
+ if(nlistf)
+ errx(1, "%s: no namelist", nlistf);
+ else
+ errx(1, "no namelist");
+ }
+ if (mflag) {
+ mbpr(nl[N_MBSTAT].n_value);
+ exit(0);
+ }
+ if (pflag) {
+ if (tp->pr_stats)
+ (*tp->pr_stats)(nl[tp->pr_sindex].n_value,
+ tp->pr_name);
+ else
+ printf("%s: no stats routine\n", tp->pr_name);
+ exit(0);
+ }
+ /*
+ * Keep file descriptors open to avoid overhead
+ * of open/close on each call to get* routines.
+ */
+ sethostent(1);
+ setnetent(1);
+ if (iflag) {
+ intpr(interval, nl[N_IFNET].n_value);
+ exit(0);
+ }
+ if (rflag) {
+ if (sflag)
+ rt_stats(nl[N_RTSTAT].n_value);
+ else
+ routepr(nl[N_RTREE].n_value);
+ exit(0);
+ }
+ if (gflag) {
+ if (sflag)
+ mrt_stats(nl[N_MRTPROTO].n_value,
+ nl[N_MRTSTAT].n_value);
+ else
+ mroutepr(nl[N_MRTPROTO].n_value,
+ nl[N_MFCTABLE].n_value,
+ nl[N_VIFTABLE].n_value);
+ exit(0);
+ }
+ if (af == AF_INET || af == AF_UNSPEC) {
+ setprotoent(1);
+ setservent(1);
+ /* ugh, this is O(MN) ... why do we do this? */
+ while (p = getprotoent()) {
+ for (tp = protox; tp->pr_name; tp++)
+ if (strcmp(tp->pr_name, p->p_name) == 0)
+ break;
+ if (tp->pr_name == 0 || tp->pr_wanted == 0)
+ continue;
+ printproto(tp, p->p_name);
+ }
+ endprotoent();
+ }
+ if (af == AF_NS || af == AF_UNSPEC)
+ for (tp = nsprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+ if (af == AF_ISO || af == AF_UNSPEC)
+ for (tp = isoprotox; tp->pr_name; tp++)
+ printproto(tp, tp->pr_name);
+ if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
+ unixpr(nl[N_UNIXSW].n_value);
+ exit(0);
+}
+
+/*
+ * Print out protocol statistics or control blocks (per sflag).
+ * If the interface was not specifically requested, and the symbol
+ * is not in the namelist, ignore this one.
+ */
+static void
+printproto(tp, name)
+ register struct protox *tp;
+ char *name;
+{
+ void (*pr)();
+ u_long off;
+
+ if (sflag) {
+ pr = tp->pr_stats;
+ off = nl[tp->pr_sindex].n_value;
+ } else {
+ pr = tp->pr_cblocks;
+ off = nl[tp->pr_index].n_value;
+ }
+ if (pr != NULL && (off || af != AF_UNSPEC))
+ (*pr)(off, name);
+}
+
+/*
+ * Read kernel memory, return 0 on success.
+ */
+int
+kread(addr, buf, size)
+ u_long addr;
+ char *buf;
+ int size;
+{
+
+ if (kvm_read(kvmd, addr, buf, size) != size) {
+ warnx("%s", kvm_geterr(kvmd));
+ return (-1);
+ }
+ return (0);
+}
+
+char *
+plural(n)
+ int n;
+{
+ return (n != 1 ? "s" : "");
+}
+
+char *
+plurales(n)
+ int n;
+{
+ return (n != 1 ? "es" : "");
+}
+
+/*
+ * Find the protox for the given "well-known" name.
+ */
+static struct protox *
+knownname(name)
+ char *name;
+{
+ struct protox **tpp, *tp;
+
+ for (tpp = protoprotox; *tpp; tpp++)
+ for (tp = *tpp; tp->pr_name; tp++)
+ if (strcmp(tp->pr_name, name) == 0)
+ return (tp);
+ return (NULL);
+}
+
+/*
+ * Find the protox corresponding to name.
+ */
+static struct protox *
+name2protox(name)
+ char *name;
+{
+ struct protox *tp;
+ char **alias; /* alias from p->aliases */
+ struct protoent *p;
+
+ /*
+ * Try to find the name in the list of "well-known" names. If that
+ * fails, check if name is an alias for an Internet protocol.
+ */
+ if (tp = knownname(name))
+ return (tp);
+
+ setprotoent(1); /* make protocol lookup cheaper */
+ while (p = getprotoent()) {
+ /* assert: name not same as p->name */
+ for (alias = p->p_aliases; *alias; alias++)
+ if (strcmp(name, *alias) == 0) {
+ endprotoent();
+ return (knownname(p->p_name));
+ }
+ }
+ endprotoent();
+ return (NULL);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: %s [-Aan] [-f address_family] [-M core] [-N system]\n", prog);
+ (void)fprintf(stderr,
+" %s [-bdghimnrs] [-f address_family] [-M core] [-N system]\n", prog);
+ (void)fprintf(stderr,
+" %s [-bdn] [-I interface] [-M core] [-N system] [-w wait]\n", prog);
+ (void)fprintf(stderr,
+" %s [-M core] [-N system] [-p protocol]\n", prog);
+ exit(1);
+}
diff --git a/usr.bin/netstat/mbuf.c b/usr.bin/netstat/mbuf.c
new file mode 100644
index 0000000..0dfc943
--- /dev/null
+++ b/usr.bin/netstat/mbuf.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mbuf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+
+#include <stdio.h>
+#include "netstat.h"
+
+#define YES 1
+typedef int bool;
+
+struct mbstat mbstat;
+
+static struct mbtypes {
+ int mt_type;
+ char *mt_name;
+} mbtypes[] = {
+ { MT_DATA, "data" },
+ { MT_OOBDATA, "oob data" },
+ { MT_CONTROL, "ancillary data" },
+ { MT_HEADER, "packet headers" },
+ { MT_SOCKET, "socket structures" }, /* XXX */
+ { MT_PCB, "protocol control blocks" }, /* XXX */
+ { MT_RTABLE, "routing table entries" }, /* XXX */
+ { MT_HTABLE, "IMP host table entries" }, /* XXX */
+ { MT_ATABLE, "address resolution tables" },
+ { MT_FTABLE, "fragment reassembly queue headers" }, /* XXX */
+ { MT_SONAME, "socket names and addresses" },
+ { MT_SOOPTS, "socket options" },
+ { MT_RIGHTS, "access rights" },
+ { MT_IFADDR, "interface addresses" }, /* XXX */
+ { 0, 0 }
+};
+
+int nmbtypes = sizeof(mbstat.m_mtypes) / sizeof(short);
+bool seen[256]; /* "have we seen this type yet?" */
+
+/*
+ * Print mbuf statistics.
+ */
+void
+mbpr(mbaddr)
+ u_long mbaddr;
+{
+ register int totmem, totfree, totmbufs;
+ register int i;
+ register struct mbtypes *mp;
+
+ if (nmbtypes != 256) {
+ warnx("unexpected change to mbstat; check source");
+ return;
+ }
+ if (mbaddr == 0) {
+ warnx("mbstat: symbol not in namelist");
+ return;
+ }
+ if (kread(mbaddr, (char *)&mbstat, sizeof (mbstat)))
+ return;
+ totmbufs = 0;
+ for (mp = mbtypes; mp->mt_name; mp++)
+ totmbufs += mbstat.m_mtypes[mp->mt_type];
+ printf("%u mbufs in use:\n", totmbufs);
+ for (mp = mbtypes; mp->mt_name; mp++)
+ if (mbstat.m_mtypes[mp->mt_type]) {
+ seen[mp->mt_type] = YES;
+ printf("\t%u mbufs allocated to %s\n",
+ mbstat.m_mtypes[mp->mt_type], mp->mt_name);
+ }
+ seen[MT_FREE] = YES;
+ for (i = 0; i < nmbtypes; i++)
+ if (!seen[i] && mbstat.m_mtypes[i]) {
+ printf("\t%u mbufs allocated to <mbuf type %d>\n",
+ mbstat.m_mtypes[i], i);
+ }
+ printf("%u/%u mbuf clusters in use\n",
+ mbstat.m_clusters - mbstat.m_clfree, mbstat.m_clusters);
+ totmem = totmbufs * MSIZE + mbstat.m_clusters * MCLBYTES;
+ totfree = mbstat.m_clfree * MCLBYTES;
+ printf("%u Kbytes allocated to network (%d%% in use)\n",
+ totmem / 1024, (totmem - totfree) * 100 / totmem);
+ printf("%u requests for memory denied\n", mbstat.m_drops);
+ printf("%u requests for memory delayed\n", mbstat.m_wait);
+ printf("%u calls to protocol drain routines\n", mbstat.m_drain);
+}
diff --git a/usr.bin/netstat/mroute.c b/usr.bin/netstat/mroute.c
new file mode 100644
index 0000000..ea64cc8
--- /dev/null
+++ b/usr.bin/netstat/mroute.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1989 Stephen Deering
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Stephen Deering of Stanford University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mroute.c 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Print DVMRP multicast routing structures and statistics.
+ *
+ * MROUTING 1.0
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/igmp.h>
+#include <net/route.h>
+#define KERNEL 1 /* XXX bogus! */
+#include <netinet/ip_mroute.h>
+#undef KERNEL
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "netstat.h"
+
+void
+mroutepr(mrpaddr, mfcaddr, vifaddr)
+ u_long mrpaddr, mfcaddr, vifaddr;
+{
+ u_int mrtproto;
+ struct mbuf *mfctable[MFCTBLSIZ];
+ struct vif viftable[MAXVIFS];
+ struct mbuf mb, *m;
+ struct mfc smfc;
+ register struct vif *v;
+ register vifi_t vifi;
+ register struct in_addr *grp;
+ register int i, n;
+ register int banner_printed;
+ register int saved_nflag;
+ vifi_t maxvif;
+
+ if (mrpaddr == 0) {
+ printf("ip_mrtproto: symbol not in namelist\n");
+ return;
+ }
+
+ kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto));
+ switch (mrtproto) {
+
+ case 0:
+ printf("no multicast routing compiled into this system\n");
+ return;
+
+ case IGMP_DVMRP:
+ break;
+
+ default:
+ printf("multicast routing protocol %u, unknown\n", mrtproto);
+ return;
+ }
+
+ if (mfcaddr == 0) {
+ printf("mfctable: symbol not in namelist\n");
+ return;
+ }
+ if (vifaddr == 0) {
+ printf("viftable: symbol not in namelist\n");
+ return;
+ }
+
+ saved_nflag = nflag;
+ nflag = 1;
+
+ kread(vifaddr, (char *)&viftable, sizeof(viftable));
+ banner_printed = 0;
+ for (vifi = 0, v = viftable; vifi < MAXVIFS; ++vifi, ++v) {
+ if (v->v_lcl_addr.s_addr == 0)
+ continue;
+
+ maxvif = vifi;
+ if (!banner_printed) {
+ printf("\nVirtual Interface Table\n"
+ " Vif Thresh Rate Local-Address "
+ "Remote-Address Pkts-In Pkts-Out\n");
+ banner_printed = 1;
+ }
+
+ printf(" %2u %6u %4d %-15.15s",
+ vifi, v->v_threshold, v->v_rate_limit,
+ routename(v->v_lcl_addr.s_addr));
+ printf(" %-15.15s", (v->v_flags & VIFF_TUNNEL) ?
+ routename(v->v_rmt_addr.s_addr) : "");
+
+ printf(" %9lu %9lu\n", v->v_pkt_in, v->v_pkt_out);
+ }
+ if (!banner_printed)
+ printf("\nVirtual Interface Table is empty\n");
+
+ kread(mfcaddr, (char *)&mfctable, sizeof(mfctable));
+ banner_printed = 0;
+ for (i = 0; i < MFCTBLSIZ; ++i) {
+ m = mfctable[i];
+ while(m) {
+ kread((u_long)m, (char *)&mb, sizeof mb);
+ m = &mb;
+
+ if (!banner_printed) {
+ printf("\nMulticast Forwarding Cache\n"
+ " Origin Group "
+ " Packets In-Vif Out-Vifs:Ttls\n");
+ banner_printed = 1;
+ }
+
+ kread((u_long)mtod(m, char *),
+ (char *)&smfc, sizeof smfc);
+ printf(" %-15.15s", routename(smfc.mfc_origin.s_addr));
+ printf(" %-15.15s", routename(smfc.mfc_mcastgrp.s_addr));
+ printf(" %9lu", smfc.mfc_pkt_cnt);
+ printf(" %3d ", smfc.mfc_parent);
+ for (vifi = 0; vifi <= maxvif; vifi++) {
+ if (smfc.mfc_ttls[vifi] > 0)
+ printf(" %u:%u", vifi,
+ smfc.mfc_ttls[vifi]);
+ }
+ printf("\n");
+ m = m->m_act;
+ }
+ }
+ if (!banner_printed)
+ printf("\nMulticast Routing Table is empty\n");
+
+ printf("\n");
+ nflag = saved_nflag;
+}
+
+
+void
+mrt_stats(mrpaddr, mstaddr)
+ u_long mrpaddr, mstaddr;
+{
+ u_int mrtproto;
+ struct mrtstat mrtstat;
+
+ if(mrpaddr == 0) {
+ printf("ip_mrtproto: symbol not in namelist\n");
+ return;
+ }
+
+ kread(mrpaddr, (char *)&mrtproto, sizeof(mrtproto));
+ switch (mrtproto) {
+ case 0:
+ printf("no multicast routing compiled into this system\n");
+ return;
+
+ case IGMP_DVMRP:
+ break;
+
+ default:
+ printf("multicast routing protocol %u, unknown\n", mrtproto);
+ return;
+ }
+
+ if (mstaddr == 0) {
+ printf("mrtstat: symbol not in namelist\n");
+ return;
+ }
+
+ kread(mstaddr, (char *)&mrtstat, sizeof(mrtstat));
+ printf("multicast forwarding:\n");
+ printf(" %10u multicast forwarding cache lookup%s\n",
+ mrtstat.mrts_mfc_lookups, plural(mrtstat.mrts_mfc_lookups));
+ printf(" %10u multicast forwarding cache miss%s\n",
+ mrtstat.mrts_mfc_misses, plurales(mrtstat.mrts_mfc_misses));
+ printf(" %10u upcall%s to mrouted\n",
+ mrtstat.mrts_upcalls, plural(mrtstat.mrts_upcalls));
+ printf(" %10u upcall queue overflow%s\n",
+ mrtstat.mrts_upq_ovflw, plural(mrtstat.mrts_upq_ovflw));
+ printf(" %10u upcall%s dropped due to full socket buffer\n",
+ mrtstat.mrts_upq_sockfull, plural(mrtstat.mrts_upq_sockfull));
+ printf(" %10u cache cleanup%s\n",
+ mrtstat.mrts_cache_cleanups, plural(mrtstat.mrts_cache_cleanups));
+ printf(" %10u datagram%s with no route for origin\n",
+ mrtstat.mrts_no_route, plural(mrtstat.mrts_no_route));
+ printf(" %10u datagram%s arrived with bad tunneling\n",
+ mrtstat.mrts_bad_tunnel, plural(mrtstat.mrts_bad_tunnel));
+ printf(" %10u datagram%s could not be tunneled\n",
+ mrtstat.mrts_cant_tunnel, plural(mrtstat.mrts_cant_tunnel));
+ printf(" %10u datagram%s arrived on wrong interface\n",
+ mrtstat.mrts_wrong_if, plural(mrtstat.mrts_wrong_if));
+ printf(" %10u datagram%s selectively dropped\n",
+ mrtstat.mrts_drop_sel, plural(mrtstat.mrts_drop_sel));
+ printf(" %10u datagram%s dropped due to queue overflow\n",
+ mrtstat.mrts_q_overflow, plural(mrtstat.mrts_q_overflow));
+ printf(" %10u datagram%s dropped for being too large\n",
+ mrtstat.mrts_pkt2large, plural(mrtstat.mrts_pkt2large));
+}
diff --git a/usr.bin/netstat/netstat.1 b/usr.bin/netstat/netstat.1
new file mode 100644
index 0000000..da890c9
--- /dev/null
+++ b/usr.bin/netstat/netstat.1
@@ -0,0 +1,297 @@
+.\" Copyright (c) 1983, 1990, 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netstat.1 8.8 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt NETSTAT 1
+.Os BSD 4.2
+.Sh NAME
+.Nm netstat
+.Nd show network status
+.Sh SYNOPSIS
+.Nm netstat
+.Op Fl Aan
+.Op Fl f Ar address_family
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Nm netstat
+.Op Fl bdghimnrs
+.Op Fl f Ar address_family
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Nm netstat
+.Op Fl bdn
+.Op Fl I Ar interface
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl w Ar wait
+.Nm netstat
+.Op Fl p Ar protocol
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Sh DESCRIPTION
+The
+.Nm netstat
+command symbolically displays the contents of various network-related
+data structures.
+There are a number of output formats,
+depending on the options for the information presented.
+The first form of the command displays a list of active sockets for
+each protocol.
+The second form presents the contents of one of the other network
+data structures according to the option selected.
+Using the third form, with a
+.Ar wait
+interval specified,
+.Nm netstat
+will continuously display the information regarding packet
+traffic on the configured network interfaces.
+The fourth form displays statistics about the named protocol.
+.Pp
+The options have the following meaning:
+.Bl -tag -width flag
+.It Fl A
+With the default display,
+show the address of any protocol control blocks associated with sockets; used
+for debugging.
+.It Fl a
+With the default display,
+show the state of all sockets; normally sockets used by
+server processes are not shown.
+.It Fl b
+With either interface display (option
+.Fl i
+or an interval, as described below),
+show the number of bytes in and out.
+.It Fl d
+With either interface display (option
+.Fl i
+or an interval, as described below),
+show the number of dropped packets.
+.It Fl f Ar address_family
+Limit statistics or address control block reports to those
+of the specified
+.Ar address family .
+The following address families
+are recognized:
+.Ar inet ,
+for
+.Dv AF_INET ,
+.Ar ns ,
+for
+.Dv AF_NS ,
+.Ar iso ,
+for
+.Dv AF_ISO ,
+and
+.Ar unix ,
+for
+.Dv AF_UNIX .
+.It Fl g
+Show information related to multicast (group address) routing.
+By default, show the IP Multicast virtual-interface and routing tables.
+If the
+.Fl s
+option is also present, show multicast routing statistics.
+.It Fl h
+Show the state of the
+.Tn IMP
+host table (obsolete).
+.It Fl I Ar interface
+Show information about the specified interface;
+used with a
+.Ar wait
+interval as described below.
+.It Fl i
+Show the state of interfaces which have been auto-configured
+(interfaces statically configured into a system, but not
+located at boot time are not shown).
+If the
+.Fl a
+options is also present, multicast addresses currently in use are shown
+for each Ethernet interface and for each IP interface address.
+Multicast addresses are shown on separate lines following the interface
+address with which they are associated.
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.It Fl m
+Show statistics recorded by the memory management routines
+(the network manages a private pool of memory buffers).
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Pa /kernel .
+.It Fl n
+Show network addresses as numbers (normally
+.Nm netstat
+interprets addresses and attempts to display them
+symbolically).
+This option may be used with any of the display formats.
+.It Fl p Ar protocol
+Show statistics about
+.Ar protocol ,
+which is either a well-known name for a protocol or an alias for it. Some
+protocol names and aliases are listed in the file
+.Pa /etc/protocols .
+A null response typically means that there are no interesting numbers to
+report.
+The program will complain if
+.Ar protocol
+is unknown or if there is no statistics routine for it.
+.It Fl s
+Show per-protocol statistics.
+If this option is repeated, counters with a value of zero are suppressed.
+.It Fl r
+Show the routing tables.
+When
+.Fl s
+is also present, show routing statistics instead.
+.It Fl w Ar wait
+Show network interface statistics at intervals of
+.Ar wait
+seconds.
+.El
+.Pp
+The default display, for active sockets, shows the local
+and remote addresses, send and receive queue sizes (in bytes), protocol,
+and the internal state of the protocol.
+Address formats are of the form ``host.port'' or ``network.port''
+if a socket's address specifies a network but no specific host address.
+When known the host and network addresses are displayed symbolically
+according to the data bases
+.Pa /etc/hosts
+and
+.Pa /etc/networks ,
+respectively. If a symbolic name for an address is unknown, or if
+the
+.Fl n
+option is specified, the address is printed numerically, according
+to the address family.
+For more information regarding
+the Internet ``dot format,''
+refer to
+.Xr inet 3 ) .
+Unspecified,
+or ``wildcard'', addresses and ports appear as ``*''.
+.Pp
+The interface display provides a table of cumulative
+statistics regarding packets transferred, errors, and collisions.
+The network addresses of the interface
+and the maximum transmission unit (``mtu'') are also displayed.
+.Pp
+The routing table display indicates the available routes and
+their status. Each route consists of a destination host or network
+and a gateway to use in forwarding packets. The flags field shows
+a collection of information about the route stored as
+binary choices. The individual flags are discussed in more
+detail in the
+.Xr route 8
+and
+.Xr route 4
+manual pages.
+The mapping between letters and flags is:
+.Bl -column XXXX RTF_BLACKHOLE
+1 RTF_PROTO2 Protocol specific routing flag #1
+2 RTF_PROTO1 Protocol specific routing flag #2
+3 RTF_PROTO3 Protocol specific routing flag #3
+B RTF_BLACKHOLE Just discard pkts (during updates)
+C RTF_CLONING Generate new routes on use
+c RTF_PRCLONING Protocol-specified generate new routes on use
+D RTF_DYNAMIC Created dynamically (by redirect)
+G RTF_GATEWAY Destination requires forwarding by intermediary
+H RTF_HOST Host entry (net otherwise)
+L RTF_LLINFO Valid protocol to link address translation
+M RTF_MODIFIED Modified dynamically (by redirect)
+R RTF_REJECT Host or net unreachable
+S RTF_STATIC Manually added
+U RTF_UP Route usable
+W RTF_WASCLONED Route was generated as a result of cloning
+X RTF_XRESOLVE External daemon translates proto to link address
+.El
+.Pp
+Direct routes are created for each
+interface attached to the local host;
+the gateway field for such entries shows the address of the outgoing interface.
+The refcnt field gives the
+current number of active uses of the route. Connection oriented
+protocols normally hold on to a single route for the duration of
+a connection while connectionless protocols obtain a route while sending
+to the same destination.
+The use field provides a count of the number of packets
+sent using that route. The interface entry indicates the network
+interface utilized for the route.
+.Pp
+When
+.Nm netstat
+is invoked with the
+.Fl w
+option and a
+.Ar wait
+interval argument, it displays a running count of statistics related to
+network interfaces.
+An obsolescent version of this option used a numeric parameter
+with no option, and is currently supported for backward compatibility.
+This display consists of a column for the primary interface (the first
+interface found during autoconfiguration) and a column summarizing
+information for all interfaces.
+The primary interface may be replaced with another interface with the
+.Fl I
+option.
+The first line of each screen of information contains a summary since the
+system was last rebooted. Subsequent lines of output show values
+accumulated over the preceding interval.
+.Sh SEE ALSO
+.Xr iostat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr vmstat 1 ,
+.Xr hosts 5 ,
+.Xr networks 5 ,
+.Xr protocols 5 ,
+.Xr services 5 ,
+.Xr trpt 8 ,
+.Xr trsp 8
+.Sh HISTORY
+The
+.Nm netstat
+command appeared in
+.Bx 4.2 .
+.\" .Sh FILES
+.\" .Bl -tag -width /dev/kmem -compact
+.\" .It Pa /kernel
+.\" default kernel namelist
+.\" .It Pa /dev/kmem
+.\" default memory file
+.\" .El
+.Sh BUGS
+The notion of errors is ill-defined.
diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h
new file mode 100644
index 0000000..aa93f19
--- /dev/null
+++ b/usr.bin/netstat/netstat.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1992, 1993
+ * Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)netstat.h 8.2 (Berkeley) 1/4/94
+ */
+
+#include <sys/cdefs.h>
+
+int Aflag; /* show addresses of protocol control block */
+int aflag; /* show all sockets (including servers) */
+int bflag; /* show i/f total bytes in/out */
+int dflag; /* show i/f dropped packets */
+int gflag; /* show group (multicast) routing or stats */
+int iflag; /* show interfaces */
+int mflag; /* show memory stats */
+int nflag; /* show addresses numerically */
+int pflag; /* show given protocol */
+int rflag; /* show routing tables (or routing stats) */
+int sflag; /* show protocol statistics */
+int tflag; /* show i/f watchdog timers */
+
+int interval; /* repeat interval for i/f stats */
+
+char *interface; /* desired i/f for stats, or NULL for all i/fs */
+int unit; /* unit number for above */
+
+int af; /* address family */
+
+char *prog; /* program name */
+
+
+int kread __P((u_long addr, char *buf, int size));
+char *plural __P((int));
+char *plurales __P((int));
+
+void protopr __P((u_long, char *));
+void tcp_stats __P((u_long, char *));
+void udp_stats __P((u_long, char *));
+void ip_stats __P((u_long, char *));
+void icmp_stats __P((u_long, char *));
+void igmp_stats __P((u_long, char *));
+void protopr __P((u_long, char *));
+
+void mbpr(u_long);
+
+void hostpr __P((u_long, u_long));
+void impstats __P((u_long, u_long));
+
+void intpr __P((int, u_long));
+
+void pr_rthdr __P(());
+void pr_family __P((int));
+void rt_stats __P((u_long));
+char *ns_phost __P((struct sockaddr *));
+void upHex __P((char *));
+
+char *routename __P((u_long));
+char *netname __P((u_long, u_long));
+char *ns_print __P((struct sockaddr *));
+void routepr __P((u_long));
+
+void nsprotopr __P((u_long, char *));
+void spp_stats __P((u_long, char *));
+void idp_stats __P((u_long, char *));
+void nserr_stats __P((u_long, char *));
+
+void intpr __P((int, u_long));
+
+void unixpr __P((u_long));
+
+void esis_stats __P((u_long, char *));
+void clnp_stats __P((u_long, char *));
+void cltp_stats __P((u_long, char *));
+void iso_protopr __P((u_long, char *));
+void iso_protopr1 __P((u_long, int));
+void tp_protopr __P((u_long, char *));
+void tp_inproto __P((u_long));
+void tp_stats __P((caddr_t, caddr_t));
+
+void mroutepr __P((u_long, u_long, u_long));
+void mrt_stats __P((u_long, u_long));
diff --git a/usr.bin/netstat/ns.c b/usr.bin/netstat/ns.c
new file mode 100644
index 0000000..a7bee2f
--- /dev/null
+++ b/usr.bin/netstat/ns.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ns.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/tcp_fsm.h>
+
+#include <netns/ns.h>
+#include <netns/ns_pcb.h>
+#include <netns/idp.h>
+#include <netns/idp_var.h>
+#include <netns/ns_error.h>
+#include <netns/sp.h>
+#include <netns/spidp.h>
+#include <netns/spp_timer.h>
+#include <netns/spp_var.h>
+#define SANAMES
+#include <netns/spp_debug.h>
+
+#include <nlist.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "netstat.h"
+
+struct nspcb nspcb;
+struct sppcb sppcb;
+struct socket sockb;
+
+static char *ns_prpr __P((struct ns_addr *));
+static void ns_erputil __P((int, int));
+
+static int first = 1;
+
+/*
+ * Print a summary of connections related to a Network Systems
+ * protocol. For SPP, also give state of connection.
+ * Listening processes (aflag) are suppressed unless the
+ * -a (all) flag is specified.
+ */
+
+void
+nsprotopr(off, name)
+ u_long off;
+ char *name;
+{
+ struct nspcb cb;
+ register struct nspcb *prev, *next;
+ int isspp;
+
+ if (off == 0)
+ return;
+ isspp = strcmp(name, "spp") == 0;
+ kread(off, (char *)&cb, sizeof (struct nspcb));
+ nspcb = cb;
+ prev = (struct nspcb *)off;
+ if (nspcb.nsp_next == (struct nspcb *)off)
+ return;
+ for (;nspcb.nsp_next != (struct nspcb *)off; prev = next) {
+ u_long ppcb;
+
+ next = nspcb.nsp_next;
+ kread((u_long)next, (char *)&nspcb, sizeof (nspcb));
+ if (nspcb.nsp_prev != prev) {
+ printf("???\n");
+ break;
+ }
+ if (!aflag && ns_nullhost(nspcb.nsp_faddr) ) {
+ continue;
+ }
+ kread((u_long)nspcb.nsp_socket,
+ (char *)&sockb, sizeof (sockb));
+ ppcb = (u_long) nspcb.nsp_pcb;
+ if (ppcb) {
+ if (isspp) {
+ kread(ppcb, (char *)&sppcb, sizeof (sppcb));
+ } else continue;
+ } else
+ if (isspp) continue;
+ if (first) {
+ printf("Active NS connections");
+ if (aflag)
+ printf(" (including servers)");
+ putchar('\n');
+ if (Aflag)
+ printf("%-8.8s ", "PCB");
+ printf(Aflag ?
+ "%-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %s\n" :
+ "%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %s\n",
+ "Proto", "Recv-Q", "Send-Q",
+ "Local Address", "Foreign Address", "(state)");
+ first = 0;
+ }
+ if (Aflag)
+ printf("%8x ", ppcb);
+ printf("%-5.5s %6d %6d ", name, sockb.so_rcv.sb_cc,
+ sockb.so_snd.sb_cc);
+ printf(" %-22.22s", ns_prpr(&nspcb.nsp_laddr));
+ printf(" %-22.22s", ns_prpr(&nspcb.nsp_faddr));
+ if (isspp) {
+ extern char *tcpstates[];
+ if (sppcb.s_state >= TCP_NSTATES)
+ printf(" %d", sppcb.s_state);
+ else
+ printf(" %s", tcpstates[sppcb.s_state]);
+ }
+ putchar('\n');
+ prev = next;
+ }
+}
+#define ANY(x,y,z) \
+ ((x) ? printf("\t%d %s%s%s -- %s\n",x,y,plural(x),z,"x") : 0)
+
+/*
+ * Dump SPP statistics structure.
+ */
+void
+spp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct spp_istat spp_istat;
+#define sppstat spp_istat.newstats
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&spp_istat, sizeof (spp_istat));
+ printf("%s:\n", name);
+ ANY(spp_istat.nonucn, "connection", " dropped due to no new sockets ");
+ ANY(spp_istat.gonawy, "connection", " terminated due to our end dying");
+ ANY(spp_istat.nonucn, "connection",
+ " dropped due to inability to connect");
+ ANY(spp_istat.noconn, "connection",
+ " dropped due to inability to connect");
+ ANY(spp_istat.notme, "connection",
+ " incompleted due to mismatched id's");
+ ANY(spp_istat.wrncon, "connection", " dropped due to mismatched id's");
+ ANY(spp_istat.bdreas, "packet", " dropped out of sequence");
+ ANY(spp_istat.lstdup, "packet", " duplicating the highest packet");
+ ANY(spp_istat.notyet, "packet", " refused as exceeding allocation");
+ ANY(sppstat.spps_connattempt, "connection", " initiated");
+ ANY(sppstat.spps_accepts, "connection", " accepted");
+ ANY(sppstat.spps_connects, "connection", " established");
+ ANY(sppstat.spps_drops, "connection", " dropped");
+ ANY(sppstat.spps_conndrops, "embryonic connection", " dropped");
+ ANY(sppstat.spps_closed, "connection", " closed (includes drops)");
+ ANY(sppstat.spps_segstimed, "packet", " where we tried to get rtt");
+ ANY(sppstat.spps_rttupdated, "time", " we got rtt");
+ ANY(sppstat.spps_delack, "delayed ack", " sent");
+ ANY(sppstat.spps_timeoutdrop, "connection", " dropped in rxmt timeout");
+ ANY(sppstat.spps_rexmttimeo, "retransmit timeout", "");
+ ANY(sppstat.spps_persisttimeo, "persist timeout", "");
+ ANY(sppstat.spps_keeptimeo, "keepalive timeout", "");
+ ANY(sppstat.spps_keepprobe, "keepalive probe", " sent");
+ ANY(sppstat.spps_keepdrops, "connection", " dropped in keepalive");
+ ANY(sppstat.spps_sndtotal, "total packet", " sent");
+ ANY(sppstat.spps_sndpack, "data packet", " sent");
+ ANY(sppstat.spps_sndbyte, "data byte", " sent");
+ ANY(sppstat.spps_sndrexmitpack, "data packet", " retransmitted");
+ ANY(sppstat.spps_sndrexmitbyte, "data byte", " retransmitted");
+ ANY(sppstat.spps_sndacks, "ack-only packet", " sent");
+ ANY(sppstat.spps_sndprobe, "window probe", " sent");
+ ANY(sppstat.spps_sndurg, "packet", " sent with URG only");
+ ANY(sppstat.spps_sndwinup, "window update-only packet", " sent");
+ ANY(sppstat.spps_sndctrl, "control (SYN|FIN|RST) packet", " sent");
+ ANY(sppstat.spps_sndvoid, "request", " to send a non-existant packet");
+ ANY(sppstat.spps_rcvtotal, "total packet", " received");
+ ANY(sppstat.spps_rcvpack, "packet", " received in sequence");
+ ANY(sppstat.spps_rcvbyte, "byte", " received in sequence");
+ ANY(sppstat.spps_rcvbadsum, "packet", " received with ccksum errs");
+ ANY(sppstat.spps_rcvbadoff, "packet", " received with bad offset");
+ ANY(sppstat.spps_rcvshort, "packet", " received too short");
+ ANY(sppstat.spps_rcvduppack, "duplicate-only packet", " received");
+ ANY(sppstat.spps_rcvdupbyte, "duplicate-only byte", " received");
+ ANY(sppstat.spps_rcvpartduppack, "packet", " with some duplicate data");
+ ANY(sppstat.spps_rcvpartdupbyte, "dup. byte", " in part-dup. packet");
+ ANY(sppstat.spps_rcvoopack, "out-of-order packet", " received");
+ ANY(sppstat.spps_rcvoobyte, "out-of-order byte", " received");
+ ANY(sppstat.spps_rcvpackafterwin, "packet", " with data after window");
+ ANY(sppstat.spps_rcvbyteafterwin, "byte", " rcvd after window");
+ ANY(sppstat.spps_rcvafterclose, "packet", " rcvd after 'close'");
+ ANY(sppstat.spps_rcvwinprobe, "rcvd window probe packet", "");
+ ANY(sppstat.spps_rcvdupack, "rcvd duplicate ack", "");
+ ANY(sppstat.spps_rcvacktoomuch, "rcvd ack", " for unsent data");
+ ANY(sppstat.spps_rcvackpack, "rcvd ack packet", "");
+ ANY(sppstat.spps_rcvackbyte, "byte", " acked by rcvd acks");
+ ANY(sppstat.spps_rcvwinupd, "rcvd window update packet", "");
+}
+#undef ANY
+#define ANY(x,y,z) ((x) ? printf("\t%d %s%s%s\n",x,y,plural(x),z) : 0)
+
+/*
+ * Dump IDP statistics structure.
+ */
+void
+idp_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct idpstat idpstat;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&idpstat, sizeof (idpstat));
+ printf("%s:\n", name);
+ ANY(idpstat.idps_toosmall, "packet", " smaller than a header");
+ ANY(idpstat.idps_tooshort, "packet", " smaller than advertised");
+ ANY(idpstat.idps_badsum, "packet", " with bad checksums");
+}
+
+static struct {
+ u_short code;
+ char *name;
+ char *where;
+} ns_errnames[] = {
+ {0, "Unspecified Error", " at Destination"},
+ {1, "Bad Checksum", " at Destination"},
+ {2, "No Listener", " at Socket"},
+ {3, "Packet", " Refused due to lack of space at Destination"},
+ {01000, "Unspecified Error", " while gatewayed"},
+ {01001, "Bad Checksum", " while gatewayed"},
+ {01002, "Packet", " forwarded too many times"},
+ {01003, "Packet", " too large to be forwarded"},
+ {-1, 0, 0},
+};
+
+/*
+ * Dump NS Error statistics structure.
+ */
+/*ARGSUSED*/
+void
+nserr_stats(off, name)
+ u_long off;
+ char *name;
+{
+ struct ns_errstat ns_errstat;
+ register int j;
+ register int histoprint = 1;
+ int z;
+
+ if (off == 0)
+ return;
+ kread(off, (char *)&ns_errstat, sizeof (ns_errstat));
+ printf("NS error statistics:\n");
+ ANY(ns_errstat.ns_es_error, "call", " to ns_error");
+ ANY(ns_errstat.ns_es_oldshort, "error",
+ " ignored due to insufficient addressing");
+ ANY(ns_errstat.ns_es_oldns_err, "error request",
+ " in response to error packets");
+ ANY(ns_errstat.ns_es_tooshort, "error packet",
+ " received incomplete");
+ ANY(ns_errstat.ns_es_badcode, "error packet",
+ " received of unknown type");
+ for(j = 0; j < NS_ERR_MAX; j ++) {
+ z = ns_errstat.ns_es_outhist[j];
+ if (z && histoprint) {
+ printf("Output Error Histogram:\n");
+ histoprint = 0;
+ }
+ ns_erputil(z, ns_errstat.ns_es_codes[j]);
+
+ }
+ histoprint = 1;
+ for(j = 0; j < NS_ERR_MAX; j ++) {
+ z = ns_errstat.ns_es_inhist[j];
+ if (z && histoprint) {
+ printf("Input Error Histogram:\n");
+ histoprint = 0;
+ }
+ ns_erputil(z, ns_errstat.ns_es_codes[j]);
+ }
+}
+
+static void
+ns_erputil(z, c)
+ int z, c;
+{
+ int j;
+ char codebuf[30];
+ char *name, *where;
+
+ for(j = 0;; j ++) {
+ if ((name = ns_errnames[j].name) == 0)
+ break;
+ if (ns_errnames[j].code == c)
+ break;
+ }
+ if (name == 0) {
+ if (c > 01000)
+ where = "in transit";
+ else
+ where = "at destination";
+ sprintf(codebuf, "Unknown XNS error code 0%o", c);
+ name = codebuf;
+ } else
+ where = ns_errnames[j].where;
+ ANY(z, name, where);
+}
+
+static struct sockaddr_ns ssns = {AF_NS};
+
+static
+char *ns_prpr(x)
+ struct ns_addr *x;
+{
+ struct sockaddr_ns *sns = &ssns;
+
+ sns->sns_addr = *x;
+ return(ns_print((struct sockaddr *)sns));
+}
diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c
new file mode 100644
index 0000000..ce7695a
--- /dev/null
+++ b/usr.bin/netstat/route.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "From: @(#)route.c 8.3 (Berkeley) 3/9/94";
+#endif
+static const char rcsid[] =
+ "$Id: route.c,v 1.5 1995/05/30 06:32:53 rgrimes Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/mbuf.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#define KERNEL
+#include <net/route.h>
+#undef KERNEL
+#include <netinet/in.h>
+
+#include <netns/ns.h>
+
+#include <sys/sysctl.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <time.h>
+#include "netstat.h"
+
+#define kget(p, d) (kread((u_long)(p), (char *)&(d), sizeof (d)))
+
+/*
+ * Definitions for showing gateway flags.
+ */
+struct bits {
+ u_long b_mask;
+ char b_val;
+} bits[] = {
+ { RTF_UP, 'U' },
+ { RTF_GATEWAY, 'G' },
+ { RTF_HOST, 'H' },
+ { RTF_REJECT, 'R' },
+ { RTF_DYNAMIC, 'D' },
+ { RTF_MODIFIED, 'M' },
+ { RTF_DONE, 'd' }, /* Completed -- for routing messages only */
+ { RTF_MASK, 'm' }, /* Mask Present -- for routing messages only */
+ { RTF_CLONING, 'C' },
+ { RTF_XRESOLVE, 'X' },
+ { RTF_LLINFO, 'L' },
+ { RTF_STATIC, 'S' },
+ { RTF_PROTO1, '1' },
+ { RTF_PROTO2, '2' },
+ { RTF_WASCLONED,'W' },
+ { RTF_PRCLONING,'c' },
+ { RTF_PROTO3, '3' },
+ { 0 }
+};
+
+static union {
+ struct sockaddr u_sa;
+ u_short u_data[128];
+} pt_u;
+
+int do_rtent = 0;
+struct rtentry rtentry;
+struct radix_node rnode;
+struct radix_mask rmask;
+struct radix_node_head *rt_tables[AF_MAX+1];
+
+int NewTree = 0;
+
+static struct sockaddr *kgetsa __P((struct sockaddr *));
+static void p_tree __P((struct radix_node *));
+static void p_rtnode __P(());
+static void ntreestuff __P(());
+static void np_rtentry __P((struct rt_msghdr *));
+static void p_sockaddr __P((struct sockaddr *, int, int));
+static void p_flags __P((int, char *));
+static void p_rtentry __P((struct rtentry *));
+
+/*
+ * Print routing tables.
+ */
+void
+routepr(rtree)
+ u_long rtree;
+{
+ struct radix_node_head *rnh, head;
+ int i;
+
+ printf("Routing tables\n");
+
+ if (Aflag == 0 && NewTree)
+ ntreestuff();
+ else {
+ if (rtree == 0) {
+ printf("rt_tables: symbol not in namelist\n");
+ return;
+ }
+
+ kget(rtree, rt_tables);
+ for (i = 0; i <= AF_MAX; i++) {
+ if ((rnh = rt_tables[i]) == 0)
+ continue;
+ kget(rnh, head);
+ if (i == AF_UNSPEC) {
+ if (Aflag && af == 0) {
+ printf("Netmasks:\n");
+ p_tree(head.rnh_treetop);
+ }
+ } else if (af == AF_UNSPEC || af == i) {
+ pr_family(i);
+ do_rtent = 1;
+ pr_rthdr();
+ p_tree(head.rnh_treetop);
+ }
+ }
+ }
+}
+
+/*
+ * Print address family header before a section of the routing table.
+ */
+void
+pr_family(af)
+ int af;
+{
+ char *afname;
+
+ switch (af) {
+ case AF_INET:
+ afname = "Internet";
+ break;
+ case AF_NS:
+ afname = "XNS";
+ break;
+ case AF_ISO:
+ afname = "ISO";
+ break;
+ case AF_CCITT:
+ afname = "X.25";
+ break;
+ default:
+ afname = NULL;
+ break;
+ }
+ if (afname)
+ printf("\n%s:\n", afname);
+ else
+ printf("\nProtocol Family %d:\n", af);
+}
+
+/* column widths; each followed by one space */
+#define WID_DST 16 /* width of destination column */
+#define WID_GW 18 /* width of gateway column */
+
+/*
+ * Print header for routing table columns.
+ */
+void
+pr_rthdr()
+{
+
+ if (Aflag)
+ printf("%-8.8s ","Address");
+ printf("%-*.*s %-*.*s %-6.6s %6.6s%8.8s %8.8s %6s\n",
+ WID_DST, WID_DST, "Destination",
+ WID_GW, WID_GW, "Gateway",
+ "Flags", "Refs", "Use", "Netif", "Expire");
+}
+
+static struct sockaddr *
+kgetsa(dst)
+ register struct sockaddr *dst;
+{
+
+ kget(dst, pt_u.u_sa);
+ if (pt_u.u_sa.sa_len > sizeof (pt_u.u_sa))
+ kread((u_long)dst, (char *)pt_u.u_data, pt_u.u_sa.sa_len);
+ return (&pt_u.u_sa);
+}
+
+static void
+p_tree(rn)
+ struct radix_node *rn;
+{
+
+again:
+ kget(rn, rnode);
+ if (rnode.rn_b < 0) {
+ if (Aflag)
+ printf("%-8.8x ", rn);
+ if (rnode.rn_flags & RNF_ROOT) {
+ if (Aflag)
+ printf("(root node)%s",
+ rnode.rn_dupedkey ? " =>\n" : "\n");
+ } else if (do_rtent) {
+ kget(rn, rtentry);
+ p_rtentry(&rtentry);
+ if (Aflag)
+ p_rtnode();
+ } else {
+ p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_key),
+ 0, 44);
+ putchar('\n');
+ }
+ if (rn = rnode.rn_dupedkey)
+ goto again;
+ } else {
+ if (Aflag && do_rtent) {
+ printf("%-8.8x ", rn);
+ p_rtnode();
+ }
+ rn = rnode.rn_r;
+ p_tree(rnode.rn_l);
+ p_tree(rn);
+ }
+}
+
+char nbuf[20];
+
+static void
+p_rtnode()
+{
+ struct radix_mask *rm = rnode.rn_mklist;
+
+ if (rnode.rn_b < 0) {
+ if (rnode.rn_mask) {
+ printf("\t mask ");
+ p_sockaddr(kgetsa((struct sockaddr *)rnode.rn_mask),
+ 0, -1);
+ } else if (rm == 0)
+ return;
+ } else {
+ sprintf(nbuf, "(%d)", rnode.rn_b);
+ printf("%6.6s %8.8x : %8.8x", nbuf, rnode.rn_l, rnode.rn_r);
+ }
+ while (rm) {
+ kget(rm, rmask);
+ sprintf(nbuf, " %d refs, ", rmask.rm_refs);
+ printf(" mk = %8.8x {(%d),%s",
+ rm, -1 - rmask.rm_b, rmask.rm_refs ? nbuf : " ");
+ p_sockaddr(kgetsa((struct sockaddr *)rmask.rm_mask), 0, -1);
+ putchar('}');
+ if (rm = rmask.rm_mklist)
+ printf(" ->");
+ }
+ putchar('\n');
+}
+
+static void
+ntreestuff()
+{
+ size_t needed;
+ int mib[6];
+ char *buf, *next, *lim;
+ register struct rt_msghdr *rtm;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ err(1, "sysctl: net.route.0.0.dump estimate");
+ }
+
+ if ((buf = malloc(needed)) == 0) {
+ err(2, "malloc(%lu)", (unsigned long)needed);
+ }
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ err(1, "sysctl: net.route.0.0.dump");
+ }
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ np_rtentry(rtm);
+ }
+}
+
+static void
+np_rtentry(rtm)
+ register struct rt_msghdr *rtm;
+{
+ register struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
+#ifdef notdef
+ static int masks_done, banner_printed;
+#endif
+ static int old_af;
+ int af = 0, interesting = RTF_UP | RTF_GATEWAY | RTF_HOST;
+
+#ifdef notdef
+ /* for the moment, netmasks are skipped over */
+ if (!banner_printed) {
+ printf("Netmasks:\n");
+ banner_printed = 1;
+ }
+ if (masks_done == 0) {
+ if (rtm->rtm_addrs != RTA_DST ) {
+ masks_done = 1;
+ af = sa->sa_family;
+ }
+ } else
+#endif
+ af = sa->sa_family;
+ if (af != old_af) {
+ pr_family(af);
+ old_af = af;
+ }
+ if (rtm->rtm_addrs == RTA_DST)
+ p_sockaddr(sa, 0, 36);
+ else {
+ p_sockaddr(sa, rtm->rtm_flags, 16);
+ if (sa->sa_len == 0)
+ sa->sa_len = sizeof(long);
+ sa = (struct sockaddr *)(sa->sa_len + (char *)sa);
+ p_sockaddr(sa, 0, 18);
+ }
+ p_flags(rtm->rtm_flags & interesting, "%-6.6s ");
+ putchar('\n');
+}
+
+static void
+p_sockaddr(sa, flags, width)
+ struct sockaddr *sa;
+ int flags, width;
+{
+ char workbuf[128], *cplim;
+ register char *cp = workbuf;
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ {
+ register struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ cp = (sin->sin_addr.s_addr == 0) ? "default" :
+ ((flags & RTF_HOST) ?
+ routename(sin->sin_addr.s_addr) :
+ netname(sin->sin_addr.s_addr, 0L));
+ break;
+ }
+
+ case AF_NS:
+ cp = ns_print(sa);
+ break;
+
+ case AF_LINK:
+ {
+ register struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
+
+ if (sdl->sdl_nlen == 0 && sdl->sdl_alen == 0 &&
+ sdl->sdl_slen == 0)
+ (void) sprintf(workbuf, "link#%d", sdl->sdl_index);
+ else switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ {
+ register int i;
+ register u_char *lla = (u_char *)sdl->sdl_data +
+ sdl->sdl_nlen;
+
+ cplim = "";
+ for (i = 0; i < sdl->sdl_alen; i++, lla++) {
+ cp += sprintf(cp, "%s%x", cplim, *lla);
+ cplim = ":";
+ }
+ cp = workbuf;
+ break;
+ }
+ default:
+ cp = link_ntoa(sdl);
+ break;
+ }
+ break;
+ }
+
+ default:
+ {
+ register u_char *s = (u_char *)sa->sa_data, *slim;
+
+ slim = sa->sa_len + (u_char *) sa;
+ cplim = cp + sizeof(workbuf) - 6;
+ cp += sprintf(cp, "(%d)", sa->sa_family);
+ while (s < slim && cp < cplim) {
+ cp += sprintf(cp, " %02x", *s++);
+ if (s < slim)
+ cp += sprintf(cp, "%02x", *s++);
+ }
+ cp = workbuf;
+ }
+ }
+ if (width < 0 )
+ printf("%s ", cp);
+ else {
+ if (nflag)
+ printf("%-*s ", width, cp);
+ else
+ printf("%-*.*s ", width, width, cp);
+ }
+}
+
+static void
+p_flags(f, format)
+ register int f;
+ char *format;
+{
+ char name[33], *flags;
+ register struct bits *p = bits;
+
+ for (flags = name; p->b_mask; p++)
+ if (p->b_mask & f)
+ *flags++ = p->b_val;
+ *flags = '\0';
+ printf(format, name);
+}
+
+static void
+p_rtentry(rt)
+ register struct rtentry *rt;
+{
+ static struct ifnet ifnet, *lastif;
+ static char name[16];
+ static char prettyname[9];
+
+ /*
+ * Don't print protocol-cloned routes unless -a.
+ */
+ if(rt->rt_parent && !aflag)
+ return;
+
+ p_sockaddr(kgetsa(rt_key(rt)), rt->rt_flags, WID_DST);
+ p_sockaddr(kgetsa(rt->rt_gateway), RTF_HOST, WID_GW);
+ p_flags(rt->rt_flags, "%-6.6s ");
+ printf("%6d %8d ", rt->rt_refcnt, rt->rt_use);
+ if (rt->rt_ifp) {
+ if (rt->rt_ifp != lastif) {
+ kget(rt->rt_ifp, ifnet);
+ kread((u_long)ifnet.if_name, name, 16);
+ lastif = rt->rt_ifp;
+ snprintf(prettyname, sizeof prettyname,
+ "%.6s%d", name, ifnet.if_unit);
+ }
+ if(rt->rt_rmx.rmx_expire) {
+ time_t expire_time;
+
+ if ((expire_time
+ =rt->rt_rmx.rmx_expire - time((time_t *)0)) > 0)
+ printf(" %8.8s %6d%s", prettyname,
+ expire_time,
+ rt->rt_nodes[0].rn_dupedkey ? " =>" : "");
+ } else {
+ printf(" %8.8s%s", prettyname,
+ rt->rt_nodes[0].rn_dupedkey ? " =>" : "");
+ }
+
+ }
+ putchar('\n');
+}
+
+char *
+routename(in)
+ u_long in;
+{
+ register char *cp;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct hostent *hp;
+ static char domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+
+ if (first) {
+ first = 0;
+ if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+ (cp = index(domain, '.')))
+ (void) strcpy(domain, cp + 1);
+ else
+ domain[0] = 0;
+ }
+ cp = 0;
+ if (!nflag) {
+ hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+ AF_INET);
+ if (hp) {
+ if ((cp = index(hp->h_name, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = hp->h_name;
+ }
+ }
+ if (cp)
+ strncpy(line, cp, sizeof(line) - 1);
+ else {
+#define C(x) ((x) & 0xff)
+ in = ntohl(in);
+ sprintf(line, "%u.%u.%u.%u",
+ C(in >> 24), C(in >> 16), C(in >> 8), C(in));
+ }
+ return (line);
+}
+
+/*
+ * Return the name of the network whose address is given.
+ * The address is assumed to be that of a net or subnet, not a host.
+ */
+char *
+netname(in, mask)
+ u_long in, mask;
+{
+ char *cp = 0;
+ static char line[MAXHOSTNAMELEN + 1];
+ struct netent *np = 0;
+ u_long net;
+ register int i;
+ int subnetshift;
+
+ i = ntohl(in);
+ if (!nflag && i) {
+ if (mask == 0) {
+ if (IN_CLASSA(i)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(i)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ */
+ while (i &~ mask)
+ mask = (long)mask >> subnetshift;
+ }
+ net = i & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ cp = np->n_name;
+ }
+ if (cp)
+ strncpy(line, cp, sizeof(line) - 1);
+ else if ((i & 0xffffff) == 0)
+ sprintf(line, "%u", C(i >> 24));
+ else if ((i & 0xffff) == 0)
+ sprintf(line, "%u.%u", C(i >> 24) , C(i >> 16));
+ else if ((i & 0xff) == 0)
+ sprintf(line, "%u.%u.%u", C(i >> 24), C(i >> 16), C(i >> 8));
+ else
+ sprintf(line, "%u.%u.%u.%u", C(i >> 24),
+ C(i >> 16), C(i >> 8), C(i));
+ return (line);
+}
+
+/*
+ * Print routing statistics
+ */
+void
+rt_stats(off)
+ u_long off;
+{
+ struct rtstat rtstat;
+
+ if (off == 0) {
+ printf("rtstat: symbol not in namelist\n");
+ return;
+ }
+ kread(off, (char *)&rtstat, sizeof (rtstat));
+ printf("routing:\n");
+ printf("\t%u bad routing redirect%s\n",
+ rtstat.rts_badredirect, plural(rtstat.rts_badredirect));
+ printf("\t%u dynamically created route%s\n",
+ rtstat.rts_dynamic, plural(rtstat.rts_dynamic));
+ printf("\t%u new gateway%s due to redirects\n",
+ rtstat.rts_newgateway, plural(rtstat.rts_newgateway));
+ printf("\t%u destination%s found unreachable\n",
+ rtstat.rts_unreach, plural(rtstat.rts_unreach));
+ printf("\t%u use%s of a wildcard route\n",
+ rtstat.rts_wildcard, plural(rtstat.rts_wildcard));
+}
+short ns_nullh[] = {0,0,0};
+short ns_bh[] = {-1,-1,-1};
+
+char *
+ns_print(sa)
+ register struct sockaddr *sa;
+{
+ register struct sockaddr_ns *sns = (struct sockaddr_ns*)sa;
+ struct ns_addr work;
+ union { union ns_net net_e; u_long long_e; } net;
+ u_short port;
+ static char mybuf[50], cport[10], chost[25];
+ char *host = "";
+ register char *p; register u_char *q;
+
+ work = sns->sns_addr;
+ port = ntohs(work.x_port);
+ work.x_port = 0;
+ net.net_e = work.x_net;
+ if (ns_nullhost(work) && net.long_e == 0) {
+ if (port ) {
+ sprintf(mybuf, "*.%xH", port);
+ upHex(mybuf);
+ } else
+ sprintf(mybuf, "*.*");
+ return (mybuf);
+ }
+
+ if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) {
+ host = "any";
+ } else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) {
+ host = "*";
+ } else {
+ q = work.x_host.c_host;
+ sprintf(chost, "%02x%02x%02x%02x%02x%02xH",
+ q[0], q[1], q[2], q[3], q[4], q[5]);
+ for (p = chost; *p == '0' && p < chost + 12; p++)
+ continue;
+ host = p;
+ }
+ if (port)
+ sprintf(cport, ".%xH", htons(port));
+ else
+ *cport = 0;
+
+ sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport);
+ upHex(mybuf);
+ return(mybuf);
+}
+
+char *
+ns_phost(sa)
+ struct sockaddr *sa;
+{
+ register struct sockaddr_ns *sns = (struct sockaddr_ns *)sa;
+ struct sockaddr_ns work;
+ static union ns_net ns_zeronet;
+ char *p;
+
+ work = *sns;
+ work.sns_addr.x_port = 0;
+ work.sns_addr.x_net = ns_zeronet;
+
+ p = ns_print((struct sockaddr *)&work);
+ if (strncmp("0H.", p, 3) == 0) p += 3;
+ return(p);
+}
+
+void
+upHex(p0)
+ char *p0;
+{
+ register char *p = p0;
+ for (; *p; p++) switch (*p) {
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ *p += ('A' - 'a');
+ }
+}
diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c
new file mode 100644
index 0000000..0310b68
--- /dev/null
+++ b/usr.bin/netstat/unix.c
@@ -0,0 +1,134 @@
+/*-
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)unix.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Display protocol blocks in the unix domain.
+ */
+#include <kvm.h>
+#include <sys/param.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#define KERNEL
+struct uio;
+struct proc;
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "netstat.h"
+
+static void unixdomainpr __P((struct socket *, caddr_t));
+
+static struct file *file, *fileNFILE;
+static int nfiles;
+extern kvm_t *kvmd;
+
+void
+unixpr(off)
+ u_long off;
+{
+ register struct file *fp;
+ struct socket sock, *so = &sock;
+ char *filebuf;
+ struct protosw *unixsw = (struct protosw *)off;
+
+ filebuf = (char *)kvm_getfiles(kvmd, KERN_FILE, 0, &nfiles);
+ if (filebuf == 0) {
+ printf("Out of memory (file table).\n");
+ return;
+ }
+ file = (struct file *)(filebuf + sizeof(fp));
+ fileNFILE = file + nfiles;
+ for (fp = file; fp < fileNFILE; fp++) {
+ if (fp->f_count == 0 || fp->f_type != DTYPE_SOCKET)
+ continue;
+ if (kread((u_long)fp->f_data, (char *)so, sizeof (*so)))
+ continue;
+ /* kludge */
+ if (so->so_proto >= unixsw && so->so_proto <= unixsw + 2)
+ if (so->so_pcb)
+ unixdomainpr(so, fp->f_data);
+ }
+}
+
+static char *socktype[] =
+ { "#0", "stream", "dgram", "raw", "rdm", "seqpacket" };
+
+static void
+unixdomainpr(so, soaddr)
+ register struct socket *so;
+ caddr_t soaddr;
+{
+ struct unpcb unpcb, *unp = &unpcb;
+ struct mbuf mbuf, *m;
+ struct sockaddr_un *sa;
+ static int first = 1;
+
+ if (kread((u_long)so->so_pcb, (char *)unp, sizeof (*unp)))
+ return;
+ if (unp->unp_addr) {
+ m = &mbuf;
+ if (kread((u_long)unp->unp_addr, (char *)m, sizeof (*m)))
+ m = (struct mbuf *)0;
+ sa = (struct sockaddr_un *)(m->m_dat);
+ } else
+ m = (struct mbuf *)0;
+ if (first) {
+ printf("Active UNIX domain sockets\n");
+ printf(
+"%-8.8s %-6.6s %-6.6s %-6.6s %8.8s %8.8s %8.8s %8.8s Addr\n",
+ "Address", "Type", "Recv-Q", "Send-Q",
+ "Inode", "Conn", "Refs", "Nextref");
+ first = 0;
+ }
+ printf("%8x %-6.6s %6d %6d %8x %8x %8x %8x",
+ soaddr, socktype[so->so_type], so->so_rcv.sb_cc, so->so_snd.sb_cc,
+ unp->unp_vnode, unp->unp_conn,
+ unp->unp_refs, unp->unp_nextref);
+ if (m)
+ printf(" %.*s",
+ m->m_len - (int)(sizeof(*sa) - sizeof(sa->sun_path)),
+ sa->sun_path);
+ putchar('\n');
+}
diff --git a/usr.bin/nfsstat/Makefile b/usr.bin/nfsstat/Makefile
new file mode 100644
index 0000000..6d79ce7
--- /dev/null
+++ b/usr.bin/nfsstat/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= nfsstat
+CFLAGS+=-DNFS
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/nfsstat/nfsstat.1 b/usr.bin/nfsstat/nfsstat.1
new file mode 100644
index 0000000..9e778c7
--- /dev/null
+++ b/usr.bin/nfsstat/nfsstat.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)nfsstat.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
+.\"
+.Dd June 6, 1993
+.Dt NFSSTAT 1
+.Os BSD 4.4
+.Sh NAME
+.Nm nfsstat
+.Nd display
+.Tn NFS
+statistics
+.Sh SYNOPSIS
+.Nm nfsstat
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl w Ar wait
+.Sh DESCRIPTION
+.Nm Nfsstat
+displays statistics kept about
+.Tn NFS
+client and server activity.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Pa /kernel .
+.It Fl w
+Display a shorter summary of
+.Tn NFS
+activity for both the client and server at
+.Ar wait
+second intervals.
+.El
+.Sh FILES
+.Bl -tag -width /dev/kmem -compact
+.It Pa /kernel
+default kernel namelist
+.It Pa /dev/kmem
+default memory file
+.El
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr netstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr vmstat 1 ,
+.Xr sysctl 3 ,
+.Xr iostat 8 ,
+.Xr pstat 8 ,
+.Sh HISTORY
+The
+.Nm nfsstat
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/nfsstat/nfsstat.c b/usr.bin/nfsstat/nfsstat.c
new file mode 100644
index 0000000..0f54f12
--- /dev/null
+++ b/usr.bin/nfsstat/nfsstat.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "From: @(#)nfsstat.c 8.1 (Berkeley) 6/6/93";*/
+static const char rcsid[] =
+ "$Id: nfsstat.c,v 1.2 1994/10/23 23:25:43 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+#include <err.h>
+
+struct nlist nl[] = {
+#define N_NFSSTAT 0
+ { "_nfsstats" },
+ "",
+};
+kvm_t *kd;
+
+static int deadkernel = 0;
+
+void intpr __P((void));
+void printhdr __P((void));
+void sidewaysintpr __P((u_int));
+void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ u_int interval;
+ int ch;
+ char *memf, *nlistf;
+ char errbuf[80];
+
+ interval = 0;
+ memf = nlistf = NULL;
+ while ((ch = getopt(argc, argv, "M:N:w:")) != EOF)
+ switch(ch) {
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ interval = atoi(*argv);
+ if (*++argv) {
+ nlistf = *argv;
+ if (*++argv)
+ memf = *argv;
+ }
+ }
+#endif
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL) {
+ setgid(getgid());
+ deadkernel = 1;
+
+ if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY,
+ errbuf)) == 0) {
+ errx(1, "kvm_openfiles: %s", errbuf);
+ }
+ if (kvm_nlist(kd, nl) != 0) {
+ errx(1, "kvm_nlist: can't get names");
+ }
+ }
+
+ if (interval)
+ sidewaysintpr(interval);
+ else
+ intpr();
+ exit(0);
+}
+
+/*
+ * Read the nfs stats using sysctl(3) for live kernels, or kvm_read
+ * for dead ones.
+ */
+void
+readstats(stp)
+ struct nfsstats *stp;
+{
+ if(deadkernel) {
+ if(kvm_read(kd, (u_long)nl[N_NFSSTAT].n_value, stp,
+ sizeof *stp) < 0) {
+ err(1, "kvm_read");
+ }
+ } else {
+ int name[3];
+ size_t buflen = sizeof *stp;
+
+ name[0] = CTL_FS;
+ name[1] = MOUNT_NFS;
+ name[2] = NFS_NFSSTATS;
+
+ if(sysctl(name, 3, stp, &buflen, (void *)0, (size_t)0) < 0) {
+ err(1, "sysctl");
+ }
+ }
+}
+
+/*
+ * Print a description of the nfs stats.
+ */
+void
+intpr()
+{
+ struct nfsstats nfsstats;
+
+ readstats(&nfsstats);
+
+ printf("Client Info:\n");
+ printf("Rpc Counts:\n");
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Getattr", "Setattr", "Lookup", "Readlink", "Read",
+ "Write", "Create", "Remove");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.rpccnt[NFSPROC_GETATTR],
+ nfsstats.rpccnt[NFSPROC_SETATTR],
+ nfsstats.rpccnt[NFSPROC_LOOKUP],
+ nfsstats.rpccnt[NFSPROC_READLINK],
+ nfsstats.rpccnt[NFSPROC_READ],
+ nfsstats.rpccnt[NFSPROC_WRITE],
+ nfsstats.rpccnt[NFSPROC_CREATE],
+ nfsstats.rpccnt[NFSPROC_REMOVE]);
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Rename", "Link", "Symlink", "Mkdir", "Rmdir",
+ "Readdir", "RdirPlus", "Access");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.rpccnt[NFSPROC_RENAME],
+ nfsstats.rpccnt[NFSPROC_LINK],
+ nfsstats.rpccnt[NFSPROC_SYMLINK],
+ nfsstats.rpccnt[NFSPROC_MKDIR],
+ nfsstats.rpccnt[NFSPROC_RMDIR],
+ nfsstats.rpccnt[NFSPROC_READDIR],
+ nfsstats.rpccnt[NFSPROC_READDIRPLUS],
+ nfsstats.rpccnt[NFSPROC_ACCESS]);
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit",
+ "GLease", "Vacate", "Evict");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.rpccnt[NFSPROC_MKNOD],
+ nfsstats.rpccnt[NFSPROC_FSSTAT],
+ nfsstats.rpccnt[NFSPROC_FSINFO],
+ nfsstats.rpccnt[NFSPROC_PATHCONF],
+ nfsstats.rpccnt[NFSPROC_COMMIT],
+ nfsstats.rpccnt[NQNFSPROC_GETLEASE],
+ nfsstats.rpccnt[NQNFSPROC_VACATED],
+ nfsstats.rpccnt[NQNFSPROC_EVICTED]);
+ printf("Rpc Info:\n");
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "TimedOut", "Invalid", "X Replies", "Retries", "Requests");
+ printf("%9d %9d %9d %9d %9d\n",
+ nfsstats.rpctimeouts,
+ nfsstats.rpcinvalid,
+ nfsstats.rpcunexpected,
+ nfsstats.rpcretries,
+ nfsstats.rpcrequests);
+ printf("Cache Info:\n");
+ printf("%9.9s %9.9s %9.9s %9.9s",
+ "Attr Hits", "Misses", "Lkup Hits", "Misses");
+ printf(" %9.9s %9.9s %9.9s %9.9s\n",
+ "BioR Hits", "Misses", "BioW Hits", "Misses");
+ printf("%9d %9d %9d %9d",
+ nfsstats.attrcache_hits, nfsstats.attrcache_misses,
+ nfsstats.lookupcache_hits, nfsstats.lookupcache_misses);
+ printf(" %9d %9d %9d %9d\n",
+ nfsstats.biocache_reads-nfsstats.read_bios,
+ nfsstats.read_bios,
+ nfsstats.biocache_writes-nfsstats.write_bios,
+ nfsstats.write_bios);
+ printf("%9.9s %9.9s %9.9s %9.9s",
+ "BioRLHits", "Misses", "BioD Hits", "Misses");
+ printf(" %9.9s %9.9s\n", "DirE Hits", "Misses");
+ printf("%9d %9d %9d %9d",
+ nfsstats.biocache_readlinks-nfsstats.readlink_bios,
+ nfsstats.readlink_bios,
+ nfsstats.biocache_readdirs-nfsstats.readdir_bios,
+ nfsstats.readdir_bios);
+ printf(" %9d %9d\n",
+ nfsstats.direofcache_hits, nfsstats.direofcache_misses);
+ printf("\nServer Info:\n");
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Getattr", "Setattr", "Lookup", "Readlink", "Read",
+ "Write", "Create", "Remove");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.srvrpccnt[NFSPROC_GETATTR],
+ nfsstats.srvrpccnt[NFSPROC_SETATTR],
+ nfsstats.srvrpccnt[NFSPROC_LOOKUP],
+ nfsstats.srvrpccnt[NFSPROC_READLINK],
+ nfsstats.srvrpccnt[NFSPROC_READ],
+ nfsstats.srvrpccnt[NFSPROC_WRITE],
+ nfsstats.srvrpccnt[NFSPROC_CREATE],
+ nfsstats.srvrpccnt[NFSPROC_REMOVE]);
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Rename", "Link", "Symlink", "Mkdir", "Rmdir",
+ "Readdir", "RdirPlus", "Access");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.srvrpccnt[NFSPROC_RENAME],
+ nfsstats.srvrpccnt[NFSPROC_LINK],
+ nfsstats.srvrpccnt[NFSPROC_SYMLINK],
+ nfsstats.srvrpccnt[NFSPROC_MKDIR],
+ nfsstats.srvrpccnt[NFSPROC_RMDIR],
+ nfsstats.srvrpccnt[NFSPROC_READDIR],
+ nfsstats.srvrpccnt[NFSPROC_READDIRPLUS],
+ nfsstats.srvrpccnt[NFSPROC_ACCESS]);
+ printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
+ "Mknod", "Fsstat", "Fsinfo", "PathConf", "Commit",
+ "GLease", "Vacate", "Evict");
+ printf("%9d %9d %9d %9d %9d %9d %9d %9d\n",
+ nfsstats.srvrpccnt[NFSPROC_MKNOD],
+ nfsstats.srvrpccnt[NFSPROC_FSSTAT],
+ nfsstats.srvrpccnt[NFSPROC_FSINFO],
+ nfsstats.srvrpccnt[NFSPROC_PATHCONF],
+ nfsstats.srvrpccnt[NFSPROC_COMMIT],
+ nfsstats.srvrpccnt[NQNFSPROC_GETLEASE],
+ nfsstats.srvrpccnt[NQNFSPROC_VACATED],
+ nfsstats.srvrpccnt[NQNFSPROC_EVICTED]);
+ printf("Server Ret-Failed\n");
+ printf("%17d\n", nfsstats.srvrpc_errs);
+ printf("Server Faults\n");
+ printf("%13d\n", nfsstats.srv_errs);
+ printf("Server Cache Stats:\n");
+ printf("%9.9s %9.9s %9.9s %9.9s\n",
+ "Inprog", "Idem", "Non-idem", "Misses");
+ printf("%9d %9d %9d %9d\n",
+ nfsstats.srvcache_inproghits,
+ nfsstats.srvcache_idemdonehits,
+ nfsstats.srvcache_nonidemdonehits,
+ nfsstats.srvcache_misses);
+ printf("Server Lease Stats:\n");
+ printf("%9.9s %9.9s %9.9s\n",
+ "Leases", "PeakL", "GLeases");
+ printf("%9d %9d %9d\n",
+ nfsstats.srvnqnfs_leases,
+ nfsstats.srvnqnfs_maxleases,
+ nfsstats.srvnqnfs_getleases);
+ printf("Server Write Gathering:\n");
+ printf("%9.9s %9.9s %9.9s\n",
+ "WriteOps", "WriteRPC", "Opsaved");
+ printf("%9d %9d %9d\n",
+ nfsstats.srvvop_writes,
+ nfsstats.srvrpccnt[NFSPROC_WRITE],
+ nfsstats.srvrpccnt[NFSPROC_WRITE] - nfsstats.srvvop_writes);
+}
+
+u_char signalled; /* set if alarm goes off "early" */
+
+/*
+ * Print a running summary of nfs statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+void
+sidewaysintpr(interval)
+ u_int interval;
+{
+ struct nfsstats nfsstats, lastst;
+ int hdrcnt, oldmask;
+ void catchalarm();
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+ bzero((caddr_t)&lastst, sizeof(lastst));
+
+ for (hdrcnt = 1;;) {
+ if (!--hdrcnt) {
+ printhdr();
+ hdrcnt = 20;
+ }
+ readstats(&nfsstats);
+ printf("Client: %8d %8d %8d %8d %8d %8d %8d %8d\n",
+ nfsstats.rpccnt[NFSPROC_GETATTR]-lastst.rpccnt[NFSPROC_GETATTR],
+ nfsstats.rpccnt[NFSPROC_LOOKUP]-lastst.rpccnt[NFSPROC_LOOKUP],
+ nfsstats.rpccnt[NFSPROC_READLINK]-lastst.rpccnt[NFSPROC_READLINK],
+ nfsstats.rpccnt[NFSPROC_READ]-lastst.rpccnt[NFSPROC_READ],
+ nfsstats.rpccnt[NFSPROC_WRITE]-lastst.rpccnt[NFSPROC_WRITE],
+ nfsstats.rpccnt[NFSPROC_RENAME]-lastst.rpccnt[NFSPROC_RENAME],
+ nfsstats.rpccnt[NFSPROC_ACCESS]-lastst.rpccnt[NFSPROC_ACCESS],
+ (nfsstats.rpccnt[NFSPROC_READDIR]-lastst.rpccnt[NFSPROC_READDIR])
+ +(nfsstats.rpccnt[NFSPROC_READDIRPLUS]-lastst.rpccnt[NFSPROC_READDIRPLUS]));
+ printf("Server: %8d %8d %8d %8d %8d %8d %8d %8d\n",
+ nfsstats.srvrpccnt[NFSPROC_GETATTR]-lastst.srvrpccnt[NFSPROC_GETATTR],
+ nfsstats.srvrpccnt[NFSPROC_LOOKUP]-lastst.srvrpccnt[NFSPROC_LOOKUP],
+ nfsstats.srvrpccnt[NFSPROC_READLINK]-lastst.srvrpccnt[NFSPROC_READLINK],
+ nfsstats.srvrpccnt[NFSPROC_READ]-lastst.srvrpccnt[NFSPROC_READ],
+ nfsstats.srvrpccnt[NFSPROC_WRITE]-lastst.srvrpccnt[NFSPROC_WRITE],
+ nfsstats.srvrpccnt[NFSPROC_RENAME]-lastst.srvrpccnt[NFSPROC_RENAME],
+ nfsstats.srvrpccnt[NFSPROC_ACCESS]-lastst.srvrpccnt[NFSPROC_ACCESS],
+ (nfsstats.srvrpccnt[NFSPROC_READDIR]-lastst.srvrpccnt[NFSPROC_READDIR])
+ +(nfsstats.srvrpccnt[NFSPROC_READDIRPLUS]-lastst.srvrpccnt[NFSPROC_READDIRPLUS]));
+ lastst = nfsstats;
+ fflush(stdout);
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (!signalled)
+ sigpause(0);
+ sigsetmask(oldmask);
+ signalled = 0;
+ (void)alarm(interval);
+ }
+ /*NOTREACHED*/
+}
+
+void
+printhdr()
+{
+ printf(" %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s %8.8s\n",
+ "Getattr", "Lookup", "Readlink", "Read", "Write", "Rename",
+ "Access", "Readdir");
+ fflush(stdout);
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+void
+catchalarm()
+{
+ signalled = 1;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: nfsstat [-M core] [-N system] [-w interval]\n");
+ exit(1);
+}
diff --git a/usr.bin/nice/Makefile b/usr.bin/nice/Makefile
new file mode 100644
index 0000000..d36378e
--- /dev/null
+++ b/usr.bin/nice/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= nice
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/nice/nice.1 b/usr.bin/nice/nice.1
new file mode 100644
index 0000000..67b8471
--- /dev/null
+++ b/usr.bin/nice/nice.1
@@ -0,0 +1,97 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nice.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt NICE 1
+.Os BSD 4
+.Sh NAME
+.Nm nice
+.Nd execute a command at a low scheduling priority
+.Sh SYNOPSIS
+.Nm nice
+.Op Fl Ns Ar number
+.Ar command
+.Op Ar arguments
+.Sh DESCRIPTION
+.Nm Nice
+runs
+.Ar command
+at a low priority.
+(Think of low and slow).
+If
+.Fl Ns Ar number
+is specified, and if it is greater than or equal
+to 10 (the default),
+.Nm nice
+will execute
+.Ar command
+at that priority.
+The upper bound, or lowest priority that
+.Nm nice
+will run a command is 20.
+The lower bounds or
+higher priorities (integers less than 10)
+can only be requested by the super-user.
+Negative numbers are expressed as
+.Fl - Ns Ar number .
+.Pp
+The returned exit status is the exit value from the
+command executed by
+.Nm nice .
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr renice 8
+.Sh HISTORY
+A
+.Nm nice
+command appeared in
+.At v6 .
+.Sh BUGS
+.Nm Nice
+is particular to
+.Xr sh 1 .
+If you use
+.Xr csh 1 ,
+then commands executed with ``&'' are automatically immune to hangup
+signals while in the background.
+.Pp
+.Nm Nice
+is built into
+.Xr csh 1
+with a slightly different syntax than described here. The form
+.Ql nice +10
+nices to positive nice, and
+.Ql nice \-10
+can be used
+by the super-user to give a process more of the processor.
diff --git a/usr.bin/nice/nice.c b/usr.bin/nice/nice.c
new file mode 100644
index 0000000..6b905d9
--- /dev/null
+++ b/usr.bin/nice/nice.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)nice.c 8.2 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define DEFNICE 10
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int niceness = DEFNICE;
+
+ if (argv[1][0] == '-')
+ if (argv[1][1] == '-' || isdigit(argv[1][1])) {
+ niceness = atoi(argv[1] + 1);
+ ++argv;
+ } else
+ errx(1, "illegal option -- %s", argv[1]);
+
+ if (argv[1] == NULL)
+ usage();
+
+ errno = 0;
+ niceness += getpriority(PRIO_PROCESS, 0);
+ if (errno)
+ err(1, "getpriority");
+ if (setpriority(PRIO_PROCESS, 0, niceness))
+ err(1, "setpriority");
+ execvp(argv[1], &argv[1]);
+ err(1, "%s", argv[1]);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "nice [ -# ] command [ options ] [ operands ]\n");
+ exit(1);
+}
diff --git a/usr.bin/nm/Makefile b/usr.bin/nm/Makefile
new file mode 100644
index 0000000..5266202
--- /dev/null
+++ b/usr.bin/nm/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= nm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/nm/nm.1 b/usr.bin/nm/nm.1
new file mode 100644
index 0000000..ff7cdb2
--- /dev/null
+++ b/usr.bin/nm/nm.1
@@ -0,0 +1,117 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt NM 1
+.Os BSD 4
+.Sh NAME
+.Nm nm
+.Nd display name list (symbol table)
+.Sh SYNOPSIS
+.Nm nm
+.Op Fl agnopruw
+.Ar
+.Sh DESCRIPTION
+The symbol table (name list) of each object in
+.Ar file(s)
+is displayed.
+If a library (archive) is given,
+.Nm
+displays a list for each
+object archive member.
+If
+.Ar file
+is not present,
+.Nm
+searches for the file
+.Pa a.out
+and if present, displays the symbol
+table for
+.Pa a.out .
+.Bl -tag -width flag
+.It Fl a
+Display symbol table entries inserted for use by debuggers.
+.It Fl g
+Restrict display to external (global) symbols.
+.It Fl n
+Present results in numerical order.
+.It Fl o
+Display full path or library name of object on every line.
+.It Fl p
+Do not sort at all.
+.It Fl r
+Reverse order sort.
+.It Fl u
+Display undefined symbols only.
+.It Fl w
+Warn about non-object archive members.
+Normally, nm will silently ignore all archive members which are not
+object files.
+.El
+.Pp
+Each symbol name is preceded by its value (a blank field if the symbol
+is undefined) and one of the following letters:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Fl
+debugger symbol table entries (see the
+.Fl a
+option).
+.It Li A
+absolute
+.It Li B
+bss segment symbol
+.It Li C
+common symbol
+.It Li D
+data segment symbol
+.It Li f
+file name
+.It Li T
+text segment symbol
+.It Li U
+undefined
+.El
+.Pp
+If the symbol is local (non-external) the type letter is in lower case.
+The output is sorted alphabetically.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ar 5 ,
+.Xr a.out 5 ,
+.Xr stab 5
+.Sh HISTORY
+An
+.Nm nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/nm/nm.1aout b/usr.bin/nm/nm.1aout
new file mode 100644
index 0000000..ff7cdb2
--- /dev/null
+++ b/usr.bin/nm/nm.1aout
@@ -0,0 +1,117 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt NM 1
+.Os BSD 4
+.Sh NAME
+.Nm nm
+.Nd display name list (symbol table)
+.Sh SYNOPSIS
+.Nm nm
+.Op Fl agnopruw
+.Ar
+.Sh DESCRIPTION
+The symbol table (name list) of each object in
+.Ar file(s)
+is displayed.
+If a library (archive) is given,
+.Nm
+displays a list for each
+object archive member.
+If
+.Ar file
+is not present,
+.Nm
+searches for the file
+.Pa a.out
+and if present, displays the symbol
+table for
+.Pa a.out .
+.Bl -tag -width flag
+.It Fl a
+Display symbol table entries inserted for use by debuggers.
+.It Fl g
+Restrict display to external (global) symbols.
+.It Fl n
+Present results in numerical order.
+.It Fl o
+Display full path or library name of object on every line.
+.It Fl p
+Do not sort at all.
+.It Fl r
+Reverse order sort.
+.It Fl u
+Display undefined symbols only.
+.It Fl w
+Warn about non-object archive members.
+Normally, nm will silently ignore all archive members which are not
+object files.
+.El
+.Pp
+Each symbol name is preceded by its value (a blank field if the symbol
+is undefined) and one of the following letters:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Fl
+debugger symbol table entries (see the
+.Fl a
+option).
+.It Li A
+absolute
+.It Li B
+bss segment symbol
+.It Li C
+common symbol
+.It Li D
+data segment symbol
+.It Li f
+file name
+.It Li T
+text segment symbol
+.It Li U
+undefined
+.El
+.Pp
+If the symbol is local (non-external) the type letter is in lower case.
+The output is sorted alphabetically.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ar 5 ,
+.Xr a.out 5 ,
+.Xr stab 5
+.Sh HISTORY
+An
+.Nm nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/nm/nm.c b/usr.bin/nm/nm.c
new file mode 100644
index 0000000..931cbf0
--- /dev/null
+++ b/usr.bin/nm/nm.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hans Huebner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)nm.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <a.out.h>
+#include <stab.h>
+#include <ar.h>
+#include <dirent.h>
+#include <ranlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int ignore_bad_archive_entries = 1;
+int print_only_external_symbols;
+int print_only_undefined_symbols;
+int print_all_symbols;
+int print_file_each_line;
+int fcount;
+
+int rev;
+int fname(), rname(), value();
+int (*sfunc)() = fname;
+
+/* some macros for symbol type (nlist.n_type) handling */
+#define IS_DEBUGGER_SYMBOL(x) ((x) & N_STAB)
+#define IS_EXTERNAL(x) ((x) & N_EXT)
+#define SYMBOL_TYPE(x) ((x) & (N_TYPE | N_STAB))
+
+void *emalloc();
+
+/*
+ * main()
+ * parse command line, execute process_file() for each file
+ * specified on the command line.
+ */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch, errors;
+
+ while ((ch = getopt(argc, argv, "agnopruw")) != EOF) {
+ switch (ch) {
+ case 'a':
+ print_all_symbols = 1;
+ break;
+ case 'g':
+ print_only_external_symbols = 1;
+ break;
+ case 'n':
+ sfunc = value;
+ break;
+ case 'o':
+ print_file_each_line = 1;
+ break;
+ case 'p':
+ sfunc = NULL;
+ break;
+ case 'r':
+ rev = 1;
+ break;
+ case 'u':
+ print_only_undefined_symbols = 1;
+ break;
+ case 'w':
+ ignore_bad_archive_entries = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ fcount = argc - optind;
+ argv += optind;
+
+ if (rev && sfunc == fname)
+ sfunc = rname;
+
+ if (!fcount)
+ errors = process_file("a.out");
+ else {
+ errors = 0;
+ do {
+ errors |= process_file(*argv);
+ } while (*++argv);
+ }
+ exit(errors);
+}
+
+/*
+ * process_file()
+ * show symbols in the file given as an argument. Accepts archive and
+ * object files as input.
+ */
+process_file(fname)
+ char *fname;
+{
+ struct exec exec_head;
+ FILE *fp;
+ int retval;
+ char magic[SARMAG];
+
+ if (!(fp = fopen(fname, "r"))) {
+ (void)fprintf(stderr, "nm: cannot read %s.\n", fname);
+ return(1);
+ }
+
+ if (fcount > 1)
+ (void)printf("\n%s:\n", fname);
+
+ /*
+ * first check whether this is an object file - read a object
+ * header, and skip back to the beginning
+ */
+ if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) {
+ (void)fprintf(stderr, "nm: %s: bad format.\n", fname);
+ (void)fclose(fp);
+ return(1);
+ }
+ rewind(fp);
+
+ /* this could be an archive */
+ if (N_BADMAG(exec_head)) {
+ if (fread(magic, sizeof(magic), (size_t)1, fp) != 1 ||
+ strncmp(magic, ARMAG, SARMAG)) {
+ (void)fprintf(stderr,
+ "nm: %s: not object file or archive.\n", fname);
+ (void)fclose(fp);
+ return(1);
+ }
+ retval = show_archive(fname, fp);
+ } else
+ retval = show_objfile(fname, fp);
+ (void)fclose(fp);
+ return(retval);
+}
+
+/* scat: concatenate strings, returning new concatenation point
+ * and permitting overlap.
+ */
+static char *scat(char *dest, char *src)
+{
+ char *end;
+ int l1 = strlen(dest), l2 = strlen(src);
+
+ memmove(dest + l1, src, l2);
+ end = dest + l1 + l2;
+ *end = 0;
+
+ return end;
+}
+
+/*
+ * show_archive()
+ * show symbols in the given archive file
+ */
+show_archive(fname, fp)
+ char *fname;
+ FILE *fp;
+{
+ struct ar_hdr ar_head;
+ struct exec exec_head;
+ int i, rval;
+ long last_ar_off;
+ char *p, *name, *ar_name;
+ int extra = strlen(fname) + 3;
+
+ name = emalloc(MAXNAMLEN + extra);
+ ar_name = name + extra;
+
+ rval = 0;
+
+ /* while there are more entries in the archive */
+ while (fread((char *)&ar_head, sizeof(ar_head), (size_t)1, fp) == 1) {
+ /* bad archive entry - stop processing this archive */
+ if (strncmp(ar_head.ar_fmag, ARFMAG, sizeof(ar_head.ar_fmag))) {
+ (void)fprintf(stderr,
+ "nm: %s: bad format archive header", fname);
+ (void)free(name);
+ return(1);
+ }
+
+ /* remember start position of current archive object */
+ last_ar_off = ftell(fp);
+
+ /* skip ranlib entries */
+ if (!bcmp(ar_head.ar_name, RANLIBMAG, sizeof(RANLIBMAG) - 1))
+ goto skip;
+
+ /* handle long names. If one is present, read it in at the
+ * end of the "name" buffer.
+ */
+ if (!bcmp(ar_head.ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1))
+ {
+ size_t len = atoi(ar_head.ar_name + sizeof(AR_EFMT1) - 1);
+
+ if (len <= 0 || len > MAXNAMLEN)
+ {
+ fprintf(stderr, "nm: Illegal length for format 1 long name.\n");
+ goto skip;
+ }
+ if (fread(ar_name, 1, len, fp) != len)
+ {
+ (void)fprintf(stderr, "nm: EOF reading format 1 long name.\n");
+ (void)free(name);
+ return(1);
+ }
+ ar_name[len] = 0;
+ }
+ else
+ {
+ p = ar_head.ar_name;
+ for (i = 0; i < sizeof(ar_head.ar_name) && p[i] && p[i] != ' '; i++)
+ ar_name[i] = p[i];
+ ar_name[i] = 0;
+ }
+
+ /*
+ * construct a name of the form "archive.a:obj.o:" for the
+ * current archive entry if the object name is to be printed
+ * on each output line
+ */
+ p = name;
+ *p = 0;
+ if (print_file_each_line)
+ {
+ p = scat(p, fname);
+ p = scat(p, ":");
+ }
+ p = scat(p, ar_name);
+
+ /* get and check current object's header */
+ if (fread((char *)&exec_head, sizeof(exec_head), (size_t)1, fp) != 1) {
+ (void)fprintf(stderr, "nm: %s: premature EOF.\n", name);
+ (void)free(name);
+ return(1);
+ }
+
+ if (N_BADMAG(exec_head)) {
+ if (!ignore_bad_archive_entries) {
+ (void)fprintf(stderr,
+ "nm: %s: bad format.\n", name);
+ rval = 1;
+ }
+ } else {
+ (void)fseek(fp, (long)-sizeof(exec_head),
+ SEEK_CUR);
+ if (!print_file_each_line)
+ (void)printf("\n%s:\n", name);
+ rval |= show_objfile(name, fp);
+ }
+
+ /*
+ * skip to next archive object - it starts at the next
+ * even byte boundary
+ */
+#define even(x) (((x) + 1) & ~1)
+skip: if (fseek(fp, last_ar_off + even(atol(ar_head.ar_size)),
+ SEEK_SET)) {
+ (void)fprintf(stderr,
+ "nm: %s: %s\n", fname, strerror(errno));
+ (void)free(name);
+ return(1);
+ }
+ }
+ (void)free(name);
+ return(rval);
+}
+
+/*
+ * show_objfile()
+ * show symbols from the object file pointed to by fp. The current
+ * file pointer for fp is expected to be at the beginning of an a.out
+ * header.
+ */
+show_objfile(objname, fp)
+ char *objname;
+ FILE *fp;
+{
+ register struct nlist *names, *np;
+ register int i, nnames, nrawnames;
+ struct exec head;
+ long stabsize;
+ char *stab;
+
+ /* read a.out header */
+ if (fread((char *)&head, sizeof(head), (size_t)1, fp) != 1) {
+ (void)fprintf(stderr,
+ "nm: %s: cannot read header.\n", objname);
+ return(1);
+ }
+
+ /*
+ * skip back to the header - the N_-macros return values relative
+ * to the beginning of the a.out header
+ */
+ if (fseek(fp, (long)-sizeof(head), SEEK_CUR)) {
+ (void)fprintf(stderr,
+ "nm: %s: %s\n", objname, strerror(errno));
+ return(1);
+ }
+
+ /* stop if this is no valid object file */
+ if (N_BADMAG(head)) {
+ (void)fprintf(stderr,
+ "nm: %s: bad format.\n", objname);
+ return(1);
+ }
+
+ /* stop if the object file contains no symbol table */
+ if (!head.a_syms) {
+ (void)fprintf(stderr,
+ "nm: %s: no name list.\n", objname);
+ return(1);
+ }
+
+ if (fseek(fp, (long)N_SYMOFF(head), SEEK_CUR)) {
+ (void)fprintf(stderr,
+ "nm: %s: %s\n", objname, strerror(errno));
+ return(1);
+ }
+
+ /* get memory for the symbol table */
+ names = emalloc((size_t)head.a_syms);
+ nrawnames = head.a_syms / sizeof(*names);
+ if (fread((char *)names, (size_t)head.a_syms, (size_t)1, fp) != 1) {
+ (void)fprintf(stderr,
+ "nm: %s: cannot read symbol table.\n", objname);
+ (void)free((char *)names);
+ return(1);
+ }
+
+ /*
+ * Following the symbol table comes the string table. The first
+ * 4-byte-integer gives the total size of the string table
+ * _including_ the size specification itself.
+ */
+ if (fread((char *)&stabsize, sizeof(stabsize), (size_t)1, fp) != 1) {
+ (void)fprintf(stderr,
+ "nm: %s: cannot read stab size.\n", objname);
+ (void)free((char *)names);
+ return(1);
+ }
+ stab = emalloc((size_t)stabsize);
+
+ /*
+ * read the string table offset by 4 - all indices into the string
+ * table include the size specification.
+ */
+ stabsize -= 4; /* we already have the size */
+ if (fread(stab + 4, (size_t)stabsize, (size_t)1, fp) != 1) {
+ (void)fprintf(stderr,
+ "nm: %s: stab truncated..\n", objname);
+ (void)free((char *)names);
+ (void)free(stab);
+ return(1);
+ }
+
+ /*
+ * fix up the symbol table and filter out unwanted entries
+ *
+ * common symbols are characterized by a n_type of N_UNDF and a
+ * non-zero n_value -- change n_type to N_COMM for all such
+ * symbols to make life easier later.
+ *
+ * filter out all entries which we don't want to print anyway
+ */
+ for (np = names, i = nnames = 0; i < nrawnames; np++, i++) {
+ if (SYMBOL_TYPE(np->n_type) == N_UNDF && np->n_value)
+ np->n_type = N_COMM | (np->n_type & N_EXT);
+ if (!print_all_symbols && IS_DEBUGGER_SYMBOL(np->n_type))
+ continue;
+ if (print_only_external_symbols && !IS_EXTERNAL(np->n_type))
+ continue;
+ if (print_only_undefined_symbols &&
+ SYMBOL_TYPE(np->n_type) != N_UNDF)
+ continue;
+
+ /*
+ * make n_un.n_name a character pointer by adding the string
+ * table's base to n_un.n_strx
+ *
+ * don't mess with zero offsets
+ */
+ if (np->n_un.n_strx)
+ np->n_un.n_name = stab + np->n_un.n_strx;
+ else
+ np->n_un.n_name = "";
+ names[nnames++] = *np;
+ }
+
+ /* sort the symbol table if applicable */
+ if (sfunc)
+ qsort((char *)names, (size_t)nnames, sizeof(*names), sfunc);
+
+ /* print out symbols */
+ for (np = names, i = 0; i < nnames; np++, i++)
+ print_symbol(objname, np);
+
+ (void)free((char *)names);
+ (void)free(stab);
+ return(0);
+}
+
+/*
+ * print_symbol()
+ * show one symbol
+ */
+print_symbol(objname, sym)
+ char *objname;
+ register struct nlist *sym;
+{
+ char *typestring(), typeletter();
+
+ if (print_file_each_line)
+ (void)printf("%s:", objname);
+
+ /*
+ * handle undefined-only format seperately (no space is
+ * left for symbol values, no type field is printed)
+ */
+ if (print_only_undefined_symbols) {
+ (void)puts(sym->n_un.n_name);
+ return;
+ }
+
+ /* print symbol's value */
+ if (SYMBOL_TYPE(sym->n_type) == N_UNDF)
+ (void)printf(" ");
+ else
+ (void)printf("%08lx", sym->n_value);
+
+ /* print type information */
+ if (IS_DEBUGGER_SYMBOL(sym->n_type))
+ (void)printf(" - %02x %04x %5s ", sym->n_other,
+ sym->n_desc&0xffff, typestring(sym->n_type));
+ else
+ (void)printf(" %c ", typeletter(sym->n_type));
+
+ /* print the symbol's name */
+ (void)puts(sym->n_un.n_name);
+}
+
+/*
+ * typestring()
+ * return the a description string for an STAB entry
+ */
+char *
+typestring(type)
+ register u_char type;
+{
+ switch(type) {
+ case N_BCOMM:
+ return("BCOMM");
+ case N_ECOML:
+ return("ECOML");
+ case N_ECOMM:
+ return("ECOMM");
+ case N_ENTRY:
+ return("ENTRY");
+ case N_FNAME:
+ return("FNAME");
+ case N_FUN:
+ return("FUN");
+ case N_GSYM:
+ return("GSYM");
+ case N_LBRAC:
+ return("LBRAC");
+ case N_LCSYM:
+ return("LCSYM");
+ case N_LENG:
+ return("LENG");
+ case N_LSYM:
+ return("LSYM");
+ case N_PC:
+ return("PC");
+ case N_PSYM:
+ return("PSYM");
+ case N_RBRAC:
+ return("RBRAC");
+ case N_RSYM:
+ return("RSYM");
+ case N_SLINE:
+ return("SLINE");
+ case N_SO:
+ return("SO");
+ case N_SOL:
+ return("SOL");
+ case N_SSYM:
+ return("SSYM");
+ case N_STSYM:
+ return("STSYM");
+ }
+ return("???");
+}
+
+/*
+ * typeletter()
+ * return a description letter for the given basic type code of an
+ * symbol table entry. The return value will be upper case for
+ * external, lower case for internal symbols.
+ */
+char
+typeletter(type)
+ u_char type;
+{
+ switch(SYMBOL_TYPE(type)) {
+ case N_ABS:
+ return(IS_EXTERNAL(type) ? 'A' : 'a');
+ case N_BSS:
+ return(IS_EXTERNAL(type) ? 'B' : 'b');
+ case N_COMM:
+ return(IS_EXTERNAL(type) ? 'C' : 'c');
+ case N_DATA:
+ return(IS_EXTERNAL(type) ? 'D' : 'd');
+ case N_FN:
+ return(IS_EXTERNAL(type) ? 'F' : 'f');
+ case N_TEXT:
+ return(IS_EXTERNAL(type) ? 'T' : 't');
+ case N_UNDF:
+ return(IS_EXTERNAL(type) ? 'U' : 'u');
+ }
+ return('?');
+}
+
+fname(a0, b0)
+ void *a0, *b0;
+{
+ struct nlist *a = a0, *b = b0;
+
+ return(strcmp(a->n_un.n_name, b->n_un.n_name));
+}
+
+rname(a0, b0)
+ void *a0, *b0;
+{
+ struct nlist *a = a0, *b = b0;
+
+ return(strcmp(b->n_un.n_name, a->n_un.n_name));
+}
+
+value(a0, b0)
+ void *a0, *b0;
+{
+ register struct nlist *a = a0, *b = b0;
+
+ if (SYMBOL_TYPE(a->n_type) == N_UNDF)
+ if (SYMBOL_TYPE(b->n_type) == N_UNDF)
+ return(0);
+ else
+ return(-1);
+ else if (SYMBOL_TYPE(b->n_type) == N_UNDF)
+ return(1);
+ if (rev) {
+ if (a->n_value == b->n_value)
+ return(rname(a0, b0));
+ return(b->n_value > a->n_value ? 1 : -1);
+ } else {
+ if (a->n_value == b->n_value)
+ return(fname(a0, b0));
+ return(a->n_value > b->n_value ? 1 : -1);
+ }
+}
+
+void *
+emalloc(size)
+ size_t size;
+{
+ char *p;
+
+ /* NOSTRICT */
+ if (p = malloc(size))
+ return(p);
+ (void)fprintf(stderr, "nm: %s\n", strerror(errno));
+ exit(1);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "usage: nm [-agnopruw] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/nohup/Makefile b/usr.bin/nohup/Makefile
new file mode 100644
index 0000000..c072a27
--- /dev/null
+++ b/usr.bin/nohup/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= nohup
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/nohup/nohup.1 b/usr.bin/nohup/nohup.1
new file mode 100644
index 0000000..d04e486
--- /dev/null
+++ b/usr.bin/nohup/nohup.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nohup.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt NOHUP 1
+.Os
+.Sh NAME
+.Nm nohup
+.Nd invoke a command immune to hangups
+.Sh SYNOPSIS
+.Nm nohup
+.Ar command
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm nohup
+utility invokes
+.Ar command
+with
+its arguments
+and at this time sets the signal
+.Dv SIGHUP
+to be ignored. The signal
+.Dv SIGQUIT
+may also be set
+to be ignored.
+If the standard output is a terminal, the standard output is
+appended to the file
+.Pa nohup.out
+in the current directory.
+If standard error is a terminal, it is directed to the same place
+as the standard output.
+.Pp
+.Nm Nohup
+exits 1 if an error occurs, otherwise the exit status is that of
+.Ar command .
+.Sh ENVIRONMENT
+The following variable is utilized by
+.Nm nohup .
+.Bl -tag -width flag
+.It Ev HOME
+If the output file
+.Pa nohup.out
+cannot be created in the current directory, the
+.Nm nohup
+utility uses the directory named by
+.Ev HOME
+to create the file.
+.El
+.Sh SEE ALSO
+.Xr signal 3
+.Sh STANDARDS
+The
+.Nm nohup
+command is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/nohup/nohup.c b/usr.bin/nohup/nohup.c
new file mode 100644
index 0000000..d469a13
--- /dev/null
+++ b/usr.bin/nohup/nohup.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)nohup.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void dofile __P((void));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (argc < 2)
+ usage();
+
+ if (isatty(STDOUT_FILENO))
+ dofile();
+ if (isatty(STDERR_FILENO) && dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
+ /* may have just closed stderr */
+ (void)fprintf(stdin, "nohup: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+
+ execvp(argv[1], &argv[1]);
+ (void)fprintf(stderr,
+ "nohup: %s: %s\n", argv[1], strerror(errno));
+ exit(1);
+}
+
+void
+dofile()
+{
+ int fd;
+ char *p, path[MAXPATHLEN];
+
+#define FILENAME "nohup.out"
+ p = FILENAME;
+ if ((fd = open(p, O_RDWR|O_CREAT, S_IRUSR | S_IWUSR)) >= 0)
+ goto dupit;
+ if (p = getenv("HOME")) {
+ (void)strcpy(path, p);
+ (void)strcat(path, "/");
+ (void)strcat(path, FILENAME);
+ if ((fd = open(p = path,
+ O_RDWR|O_CREAT, S_IRUSR | S_IWUSR)) >= 0)
+ goto dupit;
+ }
+ (void)fprintf(stderr, "nohup: can't open a nohup.out file.\n");
+ exit(1);
+
+dupit: (void)lseek(fd, (off_t)0, SEEK_END);
+ if (dup2(fd, STDOUT_FILENO) == -1) {
+ (void)fprintf(stderr, "nohup: %s\n", strerror(errno));
+ exit(1);
+ }
+ (void)fprintf(stderr, "sending output to %s\n", p);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: nohup command\n");
+ exit(1);
+}
diff --git a/usr.bin/pagesize/Makefile b/usr.bin/pagesize/Makefile
new file mode 100644
index 0000000..0f34182
--- /dev/null
+++ b/usr.bin/pagesize/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 4/3/94
+
+MAN1= pagesize.1
+
+install:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/pagesize.sh ${DESTDIR}${BINDIR}/pagesize
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/pagesize/pagesize.1 b/usr.bin/pagesize/pagesize.1
new file mode 100644
index 0000000..4beec29
--- /dev/null
+++ b/usr.bin/pagesize/pagesize.1
@@ -0,0 +1,56 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pagesize.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PAGESIZE 1
+.Os BSD 4.2
+.Sh NAME
+.Nm pagesize
+.Nd print system page size
+.Sh SYNOPSIS
+.Nm pagesize
+.Sh DESCRIPTION
+.Nm Pagesize
+prints the size of a page of memory in bytes, as
+returned by
+.Xr getpagesize 2 .
+This program is useful in constructing portable
+shell scripts.
+.Sh SEE ALSO
+.Xr getpagesize 2
+.Sh HISTORY
+The
+.Nm pagesize
+command
+appeared in
+.Bx 4.2 .
diff --git a/usr.bin/pagesize/pagesize.sh b/usr.bin/pagesize/pagesize.sh
new file mode 100644
index 0000000..da22179
--- /dev/null
+++ b/usr.bin/pagesize/pagesize.sh
@@ -0,0 +1,40 @@
+#!/bin/sh -
+#
+# Copyright (c) 1994
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)pagesize.sh 8.1 (Berkeley) 4/3/94
+#
+
+PATH=/bin:/usr/bin:/usr/sbin
+export PATH
+
+sysctl -n hw.pagesize
diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile
new file mode 100644
index 0000000..b7866de
--- /dev/null
+++ b/usr.bin/passwd/Makefile
@@ -0,0 +1,42 @@
+# From: @(#)Makefile 8.3 (Berkeley) 4/2/94
+# $Id: Makefile,v 1.17 1995/09/03 11:40:37 markm Exp $
+
+PROG= passwd
+SRCS= local_passwd.c yp_passwd.c passwd.c pw_copy.c pw_util.c pw_yp.c
+DPADD= ${LIBCRYPT} ${LIBRPCSVC}
+LDADD= -lcrypt -lrpcsvc
+.PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \
+ ${.CURDIR}/../rlogin
+
+CFLAGS+=-DCRYPT -DYP -I${.CURDIR} -I${.CURDIR}/../../usr.sbin/vipw \
+ -I${.CURDIR}/../../usr.bin/chpass
+
+BINOWN= root
+BINMODE=4555
+MAN1=passwd.1
+LINKS=${BINDIR}/passwd ${BINDIR}/yppasswd
+MLINKS=passwd.1 yppasswd.1
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_EBONES))
+SRCS+= kpasswd.c
+.PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \
+ ${.CURDIR}/../../usr.bin/rlogin ${.CURDIR}/../../usr.bin/passwd \
+ ${.CURDIR}/../../eBones/usr.bin/passwd
+
+CFLAGS+= -DKERBEROS \
+ -I${.CURDIR}/../../eBones/include \
+ -I${.CURDIR}/../../eBones/lib/libkadm
+# XXX not defined: ${LIBKADM}, ${LIBCOM_ERR}
+DPADD= ${LIBKADM} ${LIBKRB} ${LIBDES} ${LIBCRYPT} ${LIBRPCSVC} ${LIBCOM_ERR}
+LDADD= -lkadm -lkrb -ldes -lcrypt -lrpcsvc -lcom_err
+DISTRIBUTION= krb
+.endif
+
+beforeinstall:
+ [ ! -e ${DESTDIR}${BINDIR}/passwd ] || \
+ chflags noschg ${DESTDIR}${BINDIR}/passwd
+
+afterinstall:
+ chflags schg ${DESTDIR}${BINDIR}/passwd
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/passwd/extern.h b/usr.bin/passwd/extern.h
new file mode 100644
index 0000000..7a4bd12
--- /dev/null
+++ b/usr.bin/passwd/extern.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From: @(#)extern.h 8.1 (Berkeley) 4/2/94
+ * $Id$
+ */
+
+int krb_passwd __P((char *, char *, char *, char *));
+int local_passwd __P((char *));
diff --git a/usr.bin/passwd/kpasswd_proto.h b/usr.bin/passwd/kpasswd_proto.h
new file mode 100644
index 0000000..465d4c7
--- /dev/null
+++ b/usr.bin/passwd/kpasswd_proto.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)kpasswd_proto.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * kpasswd_proto
+ *
+ * definitions for the kpasswd "protocol"
+ * (We hope this to be temporary until a real admin protocol is worked out.)
+ */
+
+struct kpasswd_data {
+ des_cblock random_key;
+ char secure_msg[_PASSWORD_LEN];
+};
+
+struct update_data {
+ char pw[_PASSWORD_LEN];
+ char secure_msg[_PASSWORD_LEN];
+};
+#define SERVICE "kpasswd"
+#define SECURE_STRING \
+ "Kerberos password update program -- 12/9/88 UC Berkeley"
diff --git a/usr.bin/passwd/krb_passwd.c b/usr.bin/passwd/krb_passwd.c
new file mode 100644
index 0000000..d4a0f15
--- /dev/null
+++ b/usr.bin/passwd/krb_passwd.c
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)krb_passwd.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#ifdef KERBEROS
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kpasswd_proto.h"
+
+#include "extern.h"
+
+#define PROTO "tcp"
+
+static void send_update __P((int, char *, char *));
+static void recv_ack __P((int));
+static void cleanup __P((void));
+static void finish __P((void));
+
+static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0 };
+static struct kpasswd_data proto_data;
+static des_cblock okey;
+static Key_schedule osched;
+static KTEXT_ST ticket;
+static Key_schedule random_schedule;
+static long authopts;
+static char realm[REALM_SZ], krbhst[MAX_HSTNM];
+static int sock;
+
+int
+krb_passwd()
+{
+ struct servent *se;
+ struct hostent *host;
+ struct sockaddr_in sin;
+ CREDENTIALS cred;
+ fd_set readfds;
+ int rval;
+ char pass[_PASSWORD_LEN], password[_PASSWORD_LEN];
+ static void finish();
+
+ static struct rlimit rl = { 0, 0 };
+
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGTSTP, SIG_IGN);
+
+ if (setrlimit(RLIMIT_CORE, &rl) < 0) {
+ warn("setrlimit");
+ return (1);
+ }
+
+ if ((se = getservbyname(SERVICE, PROTO)) == NULL) {
+ warnx("couldn't find entry for service %s/%s",
+ SERVICE, PROTO);
+ return (1);
+ }
+
+ if ((rval = krb_get_lrealm(realm,1)) != KSUCCESS) {
+ warnx("couldn't get local Kerberos realm: %s",
+ krb_err_txt[rval]);
+ return (1);
+ }
+
+ if ((rval = krb_get_krbhst(krbhst, realm, 1)) != KSUCCESS) {
+ warnx("couldn't get Kerberos host: %s",
+ krb_err_txt[rval]);
+ return (1);
+ }
+
+ if ((host = gethostbyname(krbhst)) == NULL) {
+ warnx("couldn't get host entry for krb host %s",
+ krbhst);
+ return (1);
+ }
+
+ sin.sin_family = host->h_addrtype;
+ memmove((char *) &sin.sin_addr, host->h_addr, host->h_length);
+ sin.sin_port = se->s_port;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ warn("socket");
+ return (1);
+ }
+
+ if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ warn("connect");
+ (void)close(sock);
+ return (1);
+ }
+
+ rval = krb_sendauth(
+ authopts, /* NOT mutual */
+ sock,
+ &ticket, /* (filled in) */
+ SERVICE,
+ krbhst, /* instance (krbhst) */
+ realm, /* dest realm */
+ (u_long) getpid(), /* checksum */
+ NULL, /* msg data */
+ NULL, /* credentials */
+ NULL, /* schedule */
+ NULL, /* local addr */
+ NULL, /* foreign addr */
+ "KPWDV0.1"
+ );
+
+ if (rval != KSUCCESS) {
+ warnx("Kerberos sendauth error: %s", krb_err_txt[rval]);
+ return (1);
+ }
+
+ krb_get_cred("krbtgt", realm, realm, &cred);
+
+ (void)printf("Changing Kerberos password for %s.%s@%s.\n",
+ cred.pname, cred.pinst, realm);
+
+ if (des_read_pw_string(pass,
+ sizeof(pass)-1, "Old Kerberos password:", 0)) {
+ warnx("error reading old Kerberos password");
+ return (1);
+ }
+
+ (void)des_string_to_key(pass, okey);
+ (void)des_key_sched(okey, osched);
+ (void)des_set_key(okey, osched);
+
+ /* wait on the verification string */
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+
+ rval =
+ select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout);
+
+ if ((rval < 1) || !FD_ISSET(sock, &readfds)) {
+ if(rval == 0) {
+ warnx("timed out (aborted)");
+ cleanup();
+ return (1);
+ }
+ warnx("select failed (aborted)");
+ cleanup();
+ return (1);
+ }
+
+ /* read verification string */
+
+ if (des_read(sock, &proto_data, sizeof(proto_data)) !=
+ sizeof(proto_data)) {
+ warnx("couldn't read verification string (aborted)");
+ cleanup();
+ return (1);
+ }
+
+ (void)signal(SIGHUP, finish);
+ (void)signal(SIGINT, finish);
+
+ if (strcmp(SECURE_STRING, proto_data.secure_msg) != 0) {
+ cleanup();
+ /* don't complain loud if user just hit return */
+ if (pass == NULL || (!*pass))
+ return (0);
+ (void)fprintf(stderr, "Sorry\n");
+ return (1);
+ }
+
+ (void)des_key_sched(proto_data.random_key, random_schedule);
+ (void)des_set_key(proto_data.random_key, random_schedule);
+ (void)memset(pass, 0, sizeof(pass));
+
+ if (des_read_pw_string(pass,
+ sizeof(pass)-1, "New Kerberos password:", 0)) {
+ warnx("error reading new Kerberos password (aborted)");
+ cleanup();
+ return (1);
+ }
+
+ if (des_read_pw_string(password,
+ sizeof(password)-1, "Retype new Kerberos password:", 0)) {
+ warnx("error reading new Kerberos password (aborted)");
+ cleanup();
+ return (1);
+ }
+
+ if (strcmp(password, pass) != 0) {
+ warnx("password mismatch (aborted)");
+ cleanup();
+ return (1);
+ }
+
+ if (strlen(pass) == 0)
+ (void)printf("using NULL password\n");
+
+ send_update(sock, password, SECURE_STRING);
+
+ /* wait for ACK */
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+
+ rval =
+ select(sock + 1, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout);
+ if ((rval < 1) || !FD_ISSET(sock, &readfds)) {
+ if(rval == 0) {
+ warnx("timed out reading ACK (aborted)");
+ cleanup();
+ exit(1);
+ }
+ warnx("select failed (aborted)");
+ cleanup();
+ exit(1);
+ }
+ recv_ack(sock);
+ cleanup();
+ return (0);
+}
+
+static void
+send_update(dest, pwd, str)
+ int dest;
+ char *pwd, *str;
+{
+ static struct update_data ud;
+
+ (void)strncpy(ud.secure_msg, str, _PASSWORD_LEN);
+ (void)strncpy(ud.pw, pwd, sizeof(ud.pw));
+ if (des_write(dest, &ud, sizeof(ud)) != sizeof(ud)) {
+ warnx("couldn't write pw update (abort)");
+ memset((char *)&ud, 0, sizeof(ud));
+ cleanup();
+ exit(1);
+ }
+}
+
+static void
+recv_ack(remote)
+ int remote;
+{
+ int cc;
+ char buf[BUFSIZ];
+
+ cc = des_read(remote, buf, sizeof(buf));
+ if (cc <= 0) {
+ warnx("error reading acknowledgement (aborted)");
+ cleanup();
+ exit(1);
+ }
+ (void)printf("%s", buf);
+}
+
+static void
+cleanup()
+{
+
+ (void)memset((char *)&proto_data, 0, sizeof(proto_data));
+ (void)memset((char *)okey, 0, sizeof(okey));
+ (void)memset((char *)osched, 0, sizeof(osched));
+ (void)memset((char *)random_schedule, 0, sizeof(random_schedule));
+}
+
+static void
+finish()
+{
+
+ (void)close(sock);
+ exit(1);
+}
+
+#endif /* KERBEROS */
diff --git a/usr.bin/passwd/local_passwd.c b/usr.bin/passwd/local_passwd.c
new file mode 100644
index 0000000..a8700ea
--- /dev/null
+++ b/usr.bin/passwd/local_passwd.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)local_passwd.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <pw_copy.h>
+#include <pw_util.h>
+#ifdef YP
+#include <pw_yp.h>
+#endif
+
+#include "extern.h"
+
+static uid_t uid;
+
+char *tempname;
+
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+void
+to64(s, v, n)
+ char *s;
+ long v;
+ int n;
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v&0x3f];
+ v >>= 6;
+ }
+}
+
+char *
+getnewpasswd(pw, nis)
+ struct passwd *pw;
+ int nis;
+{
+ int tries;
+ char *p, *t;
+ char buf[_PASSWORD_LEN+1], salt[9];
+ struct timeval tv;
+
+ if (!nis)
+ (void)printf("Changing local password for %s.\n", pw->pw_name);
+
+ if (uid && pw->pw_passwd[0] &&
+ strcmp(crypt(getpass("Old password:"), pw->pw_passwd),
+ pw->pw_passwd)) {
+ errno = EACCES;
+ pw_error(NULL, 1, 1);
+ }
+
+ for (buf[0] = '\0', tries = 0;;) {
+ p = getpass("New password:");
+ if (!*p) {
+ (void)printf("Password unchanged.\n");
+ pw_error(NULL, 0, 0);
+ }
+ if (strlen(p) <= 5 && (uid != 0 || ++tries < 2)) {
+ (void)printf("Please enter a longer password.\n");
+ continue;
+ }
+ for (t = p; *t && islower(*t); ++t);
+ if (!*t && (uid != 0 || ++tries < 2)) {
+ (void)printf("Please don't use an all-lower case password.\nUnusual capitalization, control characters or digits are suggested.\n");
+ continue;
+ }
+ (void)strcpy(buf, p);
+ if (!strcmp(buf, getpass("Retype new password:")))
+ break;
+ (void)printf("Mismatch; try again, EOF to quit.\n");
+ }
+ /* grab a random printable character that isn't a colon */
+ (void)srandom((int)time((time_t *)NULL));
+#ifdef NEWSALT
+ salt[0] = _PASSWORD_EFMT1;
+ to64(&salt[1], (long)(29 * 25), 4);
+ to64(&salt[5], random(), 4);
+#else
+ /* Make a good size salt for algoritms that can use it. */
+ to64(&salt[0], random(), 3);
+ gettimeofday(&tv,0);
+ to64(&salt[3], tv.tv_usec, 3);
+ to64(&salt[6], tv.tv_sec, 2);
+ salt[8] = '\0';
+#endif
+ return (crypt(buf, salt));
+}
+
+int
+local_passwd(uname)
+ char *uname;
+{
+ struct passwd *pw;
+ int pfd, tfd;
+
+ if (!(pw = getpwnam(uname)))
+ errx(1, "unknown user %s", uname);
+
+#ifdef YP
+ /* Use the right password information. */
+ pw = (struct passwd *)&local_password;
+#endif
+ uid = getuid();
+ if (uid && uid != pw->pw_uid)
+ errx(1, "%s", strerror(EACCES));
+
+ pw_init();
+ pfd = pw_lock();
+ tfd = pw_tmp();
+
+ /*
+ * Get the new password. Reset passwd change time to zero; when
+ * classes are implemented, go and get the "offset" value for this
+ * class and reset the timer.
+ */
+ pw->pw_passwd = getnewpasswd(pw, 0);
+ pw->pw_change = 0;
+ pw_copy(pfd, tfd, pw);
+
+ if (!pw_mkdb())
+ pw_error((char *)NULL, 0, 1);
+ return (0);
+}
diff --git a/usr.bin/passwd/passwd.1 b/usr.bin/passwd/passwd.1
new file mode 100644
index 0000000..c88216e
--- /dev/null
+++ b/usr.bin/passwd/passwd.1
@@ -0,0 +1,160 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)passwd.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PASSWD 1
+.Os BSD 4
+.Sh NAME
+.Nm passwd, yppasswd
+.Nd modify a user's password
+.Sh SYNOPSIS
+.Nm passwd
+.Op Fl l
+.Op Ar user
+.Sh DESCRIPTION
+.Nm Passwd
+changes the user's Kerberos password. First, the user is prompted for their
+current password.
+If the current password is correctly typed, a new password is
+requested.
+The new password must be entered twice to avoid typing errors.
+.Pp
+The new password should be at least six characters long and not
+purely alphabetic.
+Its total length must be less than
+.Dv _PASSWORD_LEN
+(currently 128 characters).
+Numbers, upper case letters and meta characters
+are encouraged.
+.Pp
+Once the password has been verified,
+.Nm passwd
+communicates the new password information to
+the Kerberos authenticating host.
+.Bl -tag -width flag
+.It Fl l
+This option causes the password to be updated only in the local
+password file, and not with the Kerberos database.
+When changing only the local password,
+.Xr pwd_mkdb 8
+is used to update the password databases.
+.El
+.Pp
+To change another user's Kerberos password, one must first
+run
+.Xr kinit 1
+followed by
+.Xr passwd 1 .
+The super-user is not required to provide a user's current password
+if only the local password is modified.
+.Sh NIS INTERACTION
+.Nm Passwd
+has built-in support for NIS. If a user exists in the NIS password
+database but does not exist locally,
+.Nm passwd
+automatically switches into ``yppasswd'' mode. If the specified
+user does not exist in either the local password database of the
+NIS password maps,
+.Nm passwd
+returns an error.
+The following additional options are supported for use with NIS:
+.Bl -tag -width flag
+.It Fl y
+The
+.Fl y
+flag overrides
+.Nm passwd 's
+checking heuristics and forces
+it into NIS mode.
+.It Fl l
+When NIS is enabled, the
+.Fl l
+flag can be used to force
+.Nm passwd
+into ``local only'' mode. This flag can be used to change the entry
+for a local user when an NIS user exists when the same login name.
+For example, you will sometimes find entries for system ``placeholder''
+users such as
+.Pa bin
+or
+.Pa daemon
+in both the NIS password maps and the local user database. By
+default,
+.Nm passwd
+will try to change the NIS password. The
+.Fl l
+flag can be used to change the local password instead.
+.El
+.Pp
+When changing an NIS password, the user is required to provide
+the old password for authentication (the
+.Xr yppasswdd 8
+daemon requires the original password before
+it will allow any changes to the NIS password maps).
+This restriction applies even to the
+super-user: the only way for an administrator to override a
+user's NIS password is by modifying the NIS password maps on
+the master NIS server.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/master.passwd
+The user database
+.It Pa /etc/passwd
+A Version 7 format password file
+.It Pa /etc/passwd.XXXXXX
+Temporary copy of the password file
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr kerberos 1 ,
+.Xr kinit 1 ,
+.Xr login 1 ,
+.Xr passwd 5 ,
+.Xr kpasswdd 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+.Rs
+.%A Robert Morris
+.%A Ken Thompson
+.%T "UNIX password security"
+.Re
+.Sh NOTES
+The
+.Xr yppasswd 1
+command is really only a link to
+.Nm passwd .
+.Sh HISTORY
+A
+.Nm passwd
+command appeared in
+.At v6 .
diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c
new file mode 100644
index 0000000..31e005d
--- /dev/null
+++ b/usr.bin/passwd/passwd.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "From: @(#)passwd.c 8.3 (Berkeley) 4/2/94";
+static const char rcsid[] =
+ "$Id: passwd.c,v 1.5 1995/08/13 16:07:35 wpaul Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef YP
+#include <pwd.h>
+#include <pw_yp.h>
+char *prog_name;
+int __use_yp = 0;
+#endif
+
+#ifdef KERBEROS
+#include "krb.h"
+#endif
+
+#include "extern.h"
+
+void usage __P((void));
+
+int use_local_passwd = 0;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ char *uname;
+ char *iflag = 0, *rflag = 0, *uflag = 0;
+
+#ifdef YP
+#ifdef KERBEROS
+ char realm[REALM_SZ];
+#define OPTIONS "lysfi:r:u:"
+#else
+#define OPTIONS "lysf"
+#endif
+#else
+#ifdef KERBEROS
+ char realm[REALM_SZ];
+#define OPTIONS "li:r:u:"
+#else
+#define OPTIONS "l"
+#endif
+#endif
+
+#ifdef YP
+ int res = 0;
+
+ if (strstr(argv[0], (prog_name = "yppasswd"))) __use_yp = 1;
+#endif
+
+ while ((ch = getopt(argc, argv, OPTIONS)) != EOF) {
+ switch (ch) {
+ case 'l': /* change local password file */
+ use_local_passwd = 1;
+ break;
+#ifdef KERBEROS
+ case 'i':
+ iflag = optarg;
+ break;
+ case 'r':
+ rflag = optarg;
+ break;
+ case 'u':
+ uflag = optarg;
+ break;
+#endif /* KERBEROS */
+#ifdef YP
+ case 'y': /* Change NIS password */
+ __use_yp = 1;
+ break;
+#endif
+ default:
+ case '?':
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ((uname = getlogin()) == NULL)
+ err(1, "getlogin");
+
+ switch(argc) {
+ case 0:
+ break;
+ case 1:
+ uname = argv[0];
+ break;
+ default:
+ usage();
+ }
+
+#ifdef YP
+ /*
+ * If NIS is turned on in the password database, use it, else punt.
+ */
+#ifdef KERBEROS
+ if (__use_yp || (iflag == NULL && rflag == NULL && uflag == NULL)) {
+#endif
+ res = use_yp(uname);
+ if (res == USER_YP_ONLY) {
+ if (!use_local_passwd) {
+ exit(yp_passwd(uname));
+ } else {
+ /*
+ * Reject -l flag if NIS is turned on and the user
+ * doesn't exist in the local password database.
+ */
+ errx(1, "unknown local user: %s.", uname);
+ }
+ } else if (res == USER_LOCAL_ONLY) {
+ /*
+ * Reject -y flag if user only exists locally.
+ */
+ if (__use_yp)
+ errx(1, "unknown NIS user: %s.", uname);
+ } else if (res == USER_YP_AND_LOCAL) {
+ if (!use_local_passwd)
+ exit(yp_passwd(uname));
+ }
+#ifdef KERBEROS
+ }
+#endif
+#endif
+
+ if (!use_local_passwd) {
+#ifdef KERBEROS
+ if(krb_get_lrealm(realm, 0) == KSUCCESS) {
+ fprintf(stderr, "realm %s\n", realm);
+ exit(krb_passwd(argv[0], iflag, rflag, uflag));
+ }
+#endif
+ }
+ exit(local_passwd(uname));
+}
+
+void
+usage()
+{
+
+#ifdef YP
+#ifdef KERBEROS
+ fprintf(stderr,
+ "usage: passwd [-l] [-i instance] [-r realm] [-u fullname]\n");
+ fprintf(stderr,
+ " [-l] [-y] [user]\n");
+#else
+ (void)fprintf(stderr, "usage: passwd [-l] [-y] [user] \n");
+#endif
+#else
+#ifdef KERBEROS
+ fprintf(stderr,
+ "usage: passwd [-l] [-i instance] [-r realm] [-u fullname] [user]\n");
+#else
+ (void)fprintf(stderr, "usage: passwd user\n");
+#endif
+#endif
+ exit(1);
+}
diff --git a/usr.bin/passwd/yp_passwd.c b/usr.bin/passwd/yp_passwd.c
new file mode 100644
index 0000000..a481f2d
--- /dev/null
+++ b/usr.bin/passwd/yp_passwd.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef YP
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yppasswd.h>
+#include <pw_yp.h>
+
+extern char *prog_name;
+uid_t uid;
+
+extern char *getnewpasswd __P(( struct passwd * , int ));
+
+char *
+getserver( void )
+{
+ char *domainname, *master;
+ int port, err;
+ int getrpcport();
+
+ if ((err = yp_get_default_domain(&domainname)) != 0) {
+ fprintf(stderr, "%s: can't get local yp domain: %s\n",
+ prog_name, yperr_string(err));
+ return NULL;
+ }
+
+ if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
+ fprintf(stderr, "%s: can't find the master ypserver: %s\n",
+ prog_name, yperr_string(err));
+ return NULL;
+ }
+ port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
+ if (port==0) {
+ fprintf (stderr, "%s: yppasswdd not running on NIS master host\n",
+ prog_name);
+ return NULL;
+ }
+ if (port >= IPPORT_RESERVED) {
+ fprintf (stderr, "%s: yppasswd daemon running on illegal port.\n",
+ prog_name);
+ return NULL;
+ }
+ return master;
+}
+
+int
+yp_passwd(char *user)
+{
+ struct timeval timeout;
+ struct yppasswd yppasswd;
+ struct passwd *pw;
+ CLIENT *clnt;
+ char *master;
+ int c, err, status;
+ char *s;
+
+ if ((master = getserver()) == NULL) {
+ exit(1);
+ }
+
+ /* Obtain the passwd struct for the user whose password is to be changed.
+ */
+ uid = getuid();
+ if (user == NULL) {
+ if ((pw = getpwuid(uid)) == NULL) {
+ fprintf ( stderr, "%s: unknown user (uid=%d).\n",
+ prog_name, (int)uid );
+ exit(1);
+ }
+ } else {
+ if ((pw = getpwnam(user)) == NULL) {
+ fprintf ( stderr, "%s: unknown user: %s.\n", prog_name, user );
+ exit(1);
+ }
+ if (pw->pw_uid != uid && uid != 0) {
+ fprintf ( stderr, "%s: Only root may change account information "
+ "for others\n", prog_name );
+ exit(1);
+ }
+ }
+
+ /* Use the correct password */
+ pw = (struct passwd *)&yp_password;
+
+ /* Initialize password information */
+ yppasswd.newpw.pw_passwd = pw->pw_passwd;
+ yppasswd.newpw.pw_name = pw->pw_name;
+ yppasswd.newpw.pw_uid = pw->pw_uid;
+ yppasswd.newpw.pw_gid = pw->pw_gid;
+ yppasswd.newpw.pw_gecos = pw->pw_gecos;
+ yppasswd.newpw.pw_dir = pw->pw_dir;
+ yppasswd.newpw.pw_shell = pw->pw_shell;
+ yppasswd.oldpass = NULL;
+
+ printf("Changing NIS password for %s on %s.\n", pw->pw_name, master);
+
+ /* Get old password */
+ if(pw->pw_passwd) {
+ char prompt[40];
+
+ s = getpass ("Old password: ");
+ if( strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd)) {
+ fprintf(stderr, "Sorry.\n");
+ exit (1);
+ }
+ yppasswd.oldpass = strdup(s);
+ }
+
+ if ((s = getnewpasswd(pw, 1)) == NULL)
+ exit (1);
+ yppasswd.newpw.pw_passwd = s;
+
+ /* The yppasswd.x file said `unix authentication required',
+ * so I added it. This is the only reason it is in here.
+ * My yppasswdd doesn't use it, but maybe some others out there
+ * do. --okir
+ */
+ clnt = clnt_create( master, YPPASSWDPROG, YPPASSWDVERS, "udp" );
+ clnt->cl_auth = authunix_create_default();
+ bzero( (char*)&status, sizeof(status) );
+ timeout.tv_sec = 25; timeout.tv_usec = 0;
+ err = clnt_call( clnt, YPPASSWDPROC_UPDATE,
+ xdr_yppasswd, (char*)&yppasswd,
+ xdr_int, (char*)&status,
+ &timeout );
+
+ if (err) {
+ clnt_perrno(err);
+ fprintf( stderr, "\n" );
+ } else if (status) {
+ fprintf( stderr, "Error while changing NIS password.\n");
+ }
+
+ printf("\nNIS password has%s been changed on %s.\n",
+ (err || status)? " not" : "", master);
+
+ auth_destroy( clnt->cl_auth );
+ clnt_destroy( clnt );
+ exit ((err || status) != 0);
+}
+#endif /* YP */
diff --git a/usr.bin/passwd/yppasswd.1 b/usr.bin/passwd/yppasswd.1
new file mode 100644
index 0000000..7004490
--- /dev/null
+++ b/usr.bin/passwd/yppasswd.1
@@ -0,0 +1,115 @@
+.\"
+.\" Manpage Copyright 1994 Olaf Kirch, <okir@monad.swb.de>
+.\"
+.Dd 18 December 1994
+.Dt YPPASSWD 1
+.Os FreeBSD 2.x
+.Sh NAME
+.Nm yppasswd, ypchpass, ypchfn, ypchsh
+.Nd modify a user's NIS password data
+.Sh SYNOPSIS
+.Nm yppasswd
+.Op Fl f
+.Op Fl s
+.Op Ar user
+.Nm ypchpass
+.Op Ar user
+.Nm ypchfn
+.Op Ar user
+.Nm ypchsh
+.Op Ar user
+.Sh DESCRIPTION
+When distributing your users' passwords over NIS (a.k.a. YP), the standard
+.Nm passwd ,
+.Nm chpass ,
+.Nm chfn , and
+.Nm chsh
+utilities cannot be
+used anymore to let a user change her password, because they only modify
+the password file on the local host. They are usually replaced by their
+YP counterparts, \fByppasswd\fP, \fBypchpass\fP, \fBypchfn\fP and \fBypchsh\fP.
+.Pp
+These commands are in fact the very same program, linked to different names.
+Using the command line switches, you can choose whether to update your
+password, your login shell (\fB-s\fP), or your GECOS field
+information (\fB-f\fP), or a combination of them. \fBchfn\fP and
+\fByppasswd\fP \fB-f\fP are equivalent, as are \fBchsh\fP and
+\fByppasswd\fP \fB-s\fP.
+.Pp
+When invoked without the \fIuser\fP argument, the account information for
+the invoking user will be updated, otherwise that of \fIuser\fP will be
+updated. This option is only available to the superuser.
+.Pp
+All tools will first prompt the user for the current NIS password needed
+for authentication with the \fByppasswdd\fP daemon. Subsequently, the
+program prompts for the updated information:
+.\"
+.\"
+.Pp "\fByppasswd\fP
+Change the user's NIS password. The user is prompted for the new password.
+While typing the password, echoing is turned off, so the password does not
+appear on the screen. An empty password is rejected, as are passwords shorter
+than six characters. The user will then be requested to retype the
+password to make sure it wasn't misspelled the first time.
+.\"
+.\"
+.Pp "\fBypchsh\fP or \fB-s\fP
+Change the user's login shell. The user is prompted for a new shell,
+offering the old one as default:
+.Pp
+.in +2n
+.ft B
+.nf
+Login shell [/bin/bash]: _
+.fi
+.ft
+.in
+.Pp
+To accept the default, simply press return. To clear the shell field in
+your \fBpasswd\fP file entry (so that the system's default shell is selected),
+enter the string \fInone\fP.
+.\"
+.\"
+.Pp "\fBypchfn\fP or \fB-f\fP
+Change the user's full name and related information. Traditionally, some
+applications expect the GECOS field (field 4) of the \fBpasswd\fP file to
+contain the user's real name (as opposed to the login name) plus some
+additional information like the office phone number. This information is
+displayed by \fBfinger(1)\fP and probably some other tools, too.
+.Pp
+When setting the full name, \fBypchfn\fP displays the following prompts,
+with the defaults in brackets:
+.Pp
+.in +2n
+.ft B
+.nf
+Name [Joe Doe]:
+Location [2nd floor, bldg 34]:
+Office Phone [12345]:
+Home Phone []:
+.fi
+.ft
+.in
+.Pp
+To accept a default, simply press return. To clear a field, enter the string
+\fInone\fP.
+.Sh LICENSE
+This program is a heavily beefed-up version of Theo de Raadt's \fByppasswd\fP
+client, which is covered by the BSD license. Therefore, the BSD license
+applies to this program as well.
+
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr passwd 5 ,
+.Xr passwd 1 ,
+.Xr yppasswdd 8 .
+.Sh AUTHOR
+Theo de Raadt <deraadt@fsa.ca> (original client)
+.br
+Olaf Kirch <okir@monad.swb.de> (heavy modifications and manpages)
+.br
+Bill Paul <wpaul@ctr.columbia.edu> (port to FreeBSD and merger with
+.Xr passwd 1
+.Sh NOTES
+All these commands are really only symbolic links to
+.Xr passwd 1
diff --git a/usr.bin/paste/Makefile b/usr.bin/paste/Makefile
new file mode 100644
index 0000000..aa237fb
--- /dev/null
+++ b/usr.bin/paste/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= paste
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/paste/paste.1 b/usr.bin/paste/paste.1
new file mode 100644
index 0000000..f62b994
--- /dev/null
+++ b/usr.bin/paste/paste.1
@@ -0,0 +1,119 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Adam S. Moskowitz and the Institute of Electrical and Electronics
+.\" Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)paste.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PASTE 1
+.Os
+.Sh NAME
+.Nm paste
+.Nd merge corresponding or subsequent lines of files
+.Sh SYNOPSIS
+.Nm paste
+.Op Fl s
+.Op Fl d Ar list
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm paste
+utility concatenates the corresponding lines of the given input files,
+replacing all but the last file's newline characters with a single tab
+character, and writes the resulting lines to standard output.
+If end-of-file is reached on an input file while other input files
+still contain data, the file is treated as if it were an endless source
+of empty lines.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d Ar list
+Use one or more of the provided characters to replace the newline
+characters instead of the default tab.
+The characters in
+.Ar list
+are used circularly, i.e., when
+.Ar list
+is exhausted the first character from
+.Ar list
+is reused.
+This continues until a line from the last input file (in default operation)
+or the last line in each file (using the -s option) is displayed, at which
+time
+.Nm paste
+begins selecting characters from the beginning of
+.Ar list
+again.
+.Pp
+The following special characters can also be used in list:
+.Pp
+.Bl -tag -width flag -compact
+.It Li \en
+newline character
+.It Li \et
+tab character
+.It Li \e\e
+backslash character
+.It Li \e0
+Empty string (not a null character).
+.El
+.Pp
+Any other character preceded by a backslash is equivalent to the
+character itself.
+.It Fl s
+Concatenate all of the lines of each separate input file in command line
+order.
+The newline character of every line except the last line in each input
+file is replaced with the tab character, unless otherwise specified by
+the -d option.
+.El
+.Pp
+If
+.Ql Fl
+is specified for one or more of the input files, the standard
+input is used; standard input is read one line at a time, circularly,
+for each instance of
+.Ql Fl .
+.Pp
+The
+.Nm paste
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr cut 1
+.Sh STANDARDS
+The
+.Nm paste
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/paste/paste.c b/usr.bin/paste/paste.c
new file mode 100644
index 0000000..7f17e1d
--- /dev/null
+++ b/usr.bin/paste/paste.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)paste.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+char *delim;
+int delimcnt;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ int ch, seq;
+
+ seq = 0;
+ while ((ch = getopt(argc, argv, "d:s")) != EOF)
+ switch(ch) {
+ case 'd':
+ delimcnt = tr(delim = optarg);
+ break;
+ case 's':
+ seq = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!delim) {
+ delimcnt = 1;
+ delim = "\t";
+ }
+
+ if (seq)
+ sequential(argv);
+ else
+ parallel(argv);
+ exit(0);
+}
+
+typedef struct _list {
+ struct _list *next;
+ FILE *fp;
+ int cnt;
+ char *name;
+} LIST;
+
+parallel(argv)
+ char **argv;
+{
+ register LIST *lp;
+ register int cnt;
+ register char ch, *p;
+ LIST *head, *tmp;
+ int opencnt, output;
+ char buf[_POSIX2_LINE_MAX + 1], *malloc();
+
+ for (cnt = 0, head = NULL; p = *argv; ++argv, ++cnt) {
+ if (!(lp = (LIST *)malloc((u_int)sizeof(LIST)))) {
+ (void)fprintf(stderr, "paste: %s.\n", strerror(ENOMEM));
+ exit(1);
+ }
+ if (p[0] == '-' && !p[1])
+ lp->fp = stdin;
+ else if (!(lp->fp = fopen(p, "r"))) {
+ (void)fprintf(stderr, "paste: %s: %s.\n", p,
+ strerror(errno));
+ exit(1);
+ }
+ lp->next = NULL;
+ lp->cnt = cnt;
+ lp->name = p;
+ if (!head)
+ head = tmp = lp;
+ else {
+ tmp->next = lp;
+ tmp = lp;
+ }
+ }
+
+ for (opencnt = cnt; opencnt;) {
+ for (output = 0, lp = head; lp; lp = lp->next) {
+ if (!lp->fp) {
+ if (output && lp->cnt &&
+ (ch = delim[(lp->cnt - 1) % delimcnt]))
+ putchar(ch);
+ continue;
+ }
+ if (!fgets(buf, sizeof(buf), lp->fp)) {
+ if (!--opencnt)
+ break;
+ lp->fp = NULL;
+ if (output && lp->cnt &&
+ (ch = delim[(lp->cnt - 1) % delimcnt]))
+ putchar(ch);
+ continue;
+ }
+ if (!(p = index(buf, '\n'))) {
+ (void)fprintf(stderr,
+ "paste: %s: input line too long.\n",
+ lp->name);
+ exit(1);
+ }
+ *p = '\0';
+ /*
+ * make sure that we don't print any delimiters
+ * unless there's a non-empty file.
+ */
+ if (!output) {
+ output = 1;
+ for (cnt = 0; cnt < lp->cnt; ++cnt)
+ if (ch = delim[cnt % delimcnt])
+ putchar(ch);
+ } else if (ch = delim[(lp->cnt - 1) % delimcnt])
+ putchar(ch);
+ (void)printf("%s", buf);
+ }
+ if (output)
+ putchar('\n');
+ }
+}
+
+sequential(argv)
+ char **argv;
+{
+ register FILE *fp;
+ register int cnt;
+ register char ch, *p, *dp;
+ char buf[_POSIX2_LINE_MAX + 1];
+
+ for (; p = *argv; ++argv) {
+ if (p[0] == '-' && !p[1])
+ fp = stdin;
+ else if (!(fp = fopen(p, "r"))) {
+ (void)fprintf(stderr, "paste: %s: %s.\n", p,
+ strerror(errno));
+ continue;
+ }
+ if (fgets(buf, sizeof(buf), fp)) {
+ for (cnt = 0, dp = delim;;) {
+ if (!(p = index(buf, '\n'))) {
+ (void)fprintf(stderr,
+ "paste: %s: input line too long.\n",
+ *argv);
+ exit(1);
+ }
+ *p = '\0';
+ (void)printf("%s", buf);
+ if (!fgets(buf, sizeof(buf), fp))
+ break;
+ if (ch = *dp++)
+ putchar(ch);
+ if (++cnt == delimcnt) {
+ dp = delim;
+ cnt = 0;
+ }
+ }
+ putchar('\n');
+ }
+ if (fp != stdin)
+ (void)fclose(fp);
+ }
+}
+
+tr(arg)
+ char *arg;
+{
+ register int cnt;
+ register char ch, *p;
+
+ for (p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
+ if (ch == '\\')
+ switch(ch = *p++) {
+ case 'n':
+ *arg = '\n';
+ break;
+ case 't':
+ *arg = '\t';
+ break;
+ case '0':
+ *arg = '\0';
+ break;
+ default:
+ *arg = ch;
+ break;
+ } else
+ *arg = ch;
+
+ if (!cnt) {
+ (void)fprintf(stderr, "paste: no delimiters specified.\n");
+ exit(1);
+ }
+ return(cnt);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "paste: [-s] [-d delimiters] file ...\n");
+ exit(1);
+}
diff --git a/usr.bin/patch/EXTERN.h b/usr.bin/patch/EXTERN.h
new file mode 100644
index 0000000..0271074
--- /dev/null
+++ b/usr.bin/patch/EXTERN.h
@@ -0,0 +1,15 @@
+/* $Header: EXTERN.h,v 2.0 86/09/17 15:35:37 lwall Exp $
+ *
+ * $Log: EXTERN.h,v $
+ * Revision 2.0 86/09/17 15:35:37 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#undef EXT
+#define EXT extern
+
+#undef INIT
+#define INIT(x)
+
+#undef DOINIT
diff --git a/usr.bin/patch/INTERN.h b/usr.bin/patch/INTERN.h
new file mode 100644
index 0000000..8bf16f5
--- /dev/null
+++ b/usr.bin/patch/INTERN.h
@@ -0,0 +1,15 @@
+/* $Header: INTERN.h,v 2.0 86/09/17 15:35:58 lwall Exp $
+ *
+ * $Log: INTERN.h,v $
+ * Revision 2.0 86/09/17 15:35:58 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#undef EXT
+#define EXT
+
+#undef INIT
+#define INIT(x) = x
+
+#define DOINIT
diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile
new file mode 100644
index 0000000..d6db9f7
--- /dev/null
+++ b/usr.bin/patch/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= patch
+SRCS= patch.c pch.c inp.c version.c util.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/patch/README b/usr.bin/patch/README
new file mode 100644
index 0000000..017b1a0
--- /dev/null
+++ b/usr.bin/patch/README
@@ -0,0 +1,79 @@
+
+The Makefile and config.h files in this directory work with the current
+BSD release. Don't run the Configure script, you'll get wrong results.
+
+Keith Bostic 1/10/88
+-----------------------------------------------------------------------------
+
+ Patch Kit, Version 2.0
+
+ Copyright (c) 1986, Larry Wall
+
+You may copy the patch kit in whole or in part as long as you don't try to
+make money off it, or pretend that you wrote it.
+--------------------------------------------------------------------------
+
+Please read all the directions below before you proceed any further, and
+then follow them carefully. Failure to do so may void your warranty. :-)
+
+After you have unpacked your kit, you should have all the files listed
+in MANIFEST.
+
+Installation
+
+1) Run Configure. This will figure out various things about your system.
+ Some things Configure will figure out for itself, other things it will
+ ask you about. It will then proceed to make config.h, config.sh, and
+ Makefile.
+
+ You might possibly have to trim # comments from the front of Configure
+ if your sh doesn't handle them, but all other # comments will be taken
+ care of.
+
+ If you don't have sh, you'll have to rip the prototype of config.h out
+ of Configure and generate the defines by hand.
+
+2) Glance through config.h to make sure system dependencies are correct.
+ Most of them should have been taken care of by running the Configure script.
+
+ If you have any additional changes to make to the C definitions, they
+ can be done in the Makefile, or in config.h. Bear in mind that they may
+ get undone next time you run Configure.
+
+3) make
+
+ This will attempt to make patch in the current directory.
+
+4) make install
+
+ This will put patch into a public directory (normally /usr/local/bin).
+ It will also try to put the man pages in a reasonable place. It will not
+ nroff the man page, however.
+
+5) Read the manual entry before running patch.
+
+6) IMPORTANT! Help save the world! Communicate any problems and
+ suggested patches to me, lwall@sdcrdcf.UUCP (Larry Wall), so we can
+ keep the world in sync. If you have a problem, there's someone else
+ out there who either has had or will have the same problem.
+
+ If possible, send in patches such that the patch program will apply them.
+ Context diffs are the best, then normal diffs. Don't send ed scripts--
+ I've probably changed my copy since the version you have.
+
+ Watch for patch patches in net.sources.bugs. Patches will generally be
+ in a form usable by the patch program. If you are just now bringing up
+ patch and aren't sure how many patches there are, write to me and I'll
+ send any you don't have. Your current patch level is shown in patchlevel.h.
+
+
+NEW FEATURES IN THIS RELEASE
+
+(Correct) support for 4.3bsd-style context diffs.
+Files can be created from scratch.
+You can specify a fuzz-factor for context matching.
+You can force patch to ask no questions.
+You can specify how much of the leading pathname to strip off filenames.
+Uses a Configure script for greater portability.
+You are now asked if you want to apply a reversed patch.
+No limit (apart from memory) on the size of hunks.
diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h
new file mode 100644
index 0000000..42d6883
--- /dev/null
+++ b/usr.bin/patch/common.h
@@ -0,0 +1,138 @@
+/* $Header: common.h,v 2.0 86/09/17 15:36:39 lwall Exp $
+ *
+ * $Log: common.h,v $
+ * Revision 2.0 86/09/17 15:36:39 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#define DEBUGGING
+
+#include "config.h"
+
+/* shut lint up about the following when return value ignored */
+
+#define Signal (void)signal
+#define Unlink (void)unlink
+#define Lseek (void)lseek
+#define Fseek (void)fseek
+#define Fstat (void)fstat
+#define Pclose (void)pclose
+#define Close (void)close
+#define Fclose (void)fclose
+#define Fflush (void)fflush
+#define Sprintf (void)sprintf
+#define Mktemp (void)mktemp
+#define Strcpy (void)strcpy
+#define Strcat (void)strcat
+
+#include <stdio.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <signal.h>
+
+/* constants */
+
+#define TRUE (1)
+#define FALSE (0)
+
+#define MAXHUNKSIZE 100000 /* is this enough lines? */
+#define INITHUNKMAX 125 /* initial dynamic allocation size */
+#define MAXLINELEN 1024
+#define BUFFERSIZE 1024
+#define ORIGEXT ".orig"
+#define SCCSPREFIX "s."
+#define GET "get -e %s"
+#define RCSSUFFIX ",v"
+#define CHECKOUT "co -l %s"
+
+/* handy definitions */
+
+#define Null(t) ((t)0)
+#define Nullch Null(char *)
+#define Nullfp Null(FILE *)
+#define Nulline Null(LINENUM)
+
+#define Ctl(ch) ((ch) & 037)
+
+#define strNE(s1,s2) (strcmp(s1, s2))
+#define strEQ(s1,s2) (!strcmp(s1, s2))
+#define strnNE(s1,s2,l) (strncmp(s1, s2, l))
+#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))
+
+/* typedefs */
+
+typedef char bool;
+typedef long LINENUM; /* must be signed */
+typedef unsigned MEM; /* what to feed malloc */
+
+/* globals */
+
+EXT int Argc; /* guess */
+EXT char **Argv;
+EXT int Argc_last; /* for restarting plan_b */
+EXT char **Argv_last;
+
+EXT struct stat filestat; /* file statistics area */
+EXT int filemode INIT(0644);
+
+EXT char buf[MAXLINELEN]; /* general purpose buffer */
+EXT FILE *ofp INIT(Nullfp); /* output file pointer */
+EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */
+
+EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */
+EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */
+
+#define MAXFILEC 2
+EXT int filec INIT(0); /* how many file arguments? */
+EXT char *filearg[MAXFILEC];
+EXT bool ok_to_create_file INIT(FALSE);
+EXT char *bestguess INIT(Nullch); /* guess at correct filename */
+
+EXT char *outname INIT(Nullch);
+EXT char rejname[128];
+
+EXT char *origext INIT(Nullch);
+
+EXT char TMPOUTNAME[] INIT("/tmp/patchoXXXXXX");
+EXT char TMPINNAME[] INIT("/tmp/patchiXXXXXX"); /* might want /usr/tmp here */
+EXT char TMPREJNAME[] INIT("/tmp/patchrXXXXXX");
+EXT char TMPPATNAME[] INIT("/tmp/patchpXXXXXX");
+EXT bool toutkeep INIT(FALSE);
+EXT bool trejkeep INIT(FALSE);
+
+EXT LINENUM last_offset INIT(0);
+#ifdef DEBUGGING
+EXT int debug INIT(0);
+#endif
+EXT LINENUM maxfuzz INIT(2);
+EXT bool force INIT(FALSE);
+EXT bool verbose INIT(TRUE);
+EXT bool reverse INIT(FALSE);
+EXT bool noreverse INIT(FALSE);
+EXT bool skip_rest_of_patch INIT(FALSE);
+EXT int strippath INIT(957);
+EXT bool canonicalize INIT(FALSE);
+
+#define CONTEXT_DIFF 1
+#define NORMAL_DIFF 2
+#define ED_DIFF 3
+#define NEW_CONTEXT_DIFF 4
+EXT int diff_type INIT(0);
+
+EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */
+EXT char if_defined[128]; /* #ifdef xyzzy */
+EXT char not_defined[128]; /* #ifndef xyzzy */
+EXT char else_defined[] INIT("#else\n");/* #else */
+EXT char end_defined[128]; /* #endif xyzzy */
+
+EXT char *revision INIT(Nullch); /* prerequisite revision, if any */
+
+char *malloc();
+char *realloc();
+char *strcpy();
+char *strcat();
+long atol();
+char *mktemp();
diff --git a/usr.bin/patch/config.h b/usr.bin/patch/config.h
new file mode 100644
index 0000000..9318fe7
--- /dev/null
+++ b/usr.bin/patch/config.h
@@ -0,0 +1,16 @@
+#define Reg1 register /**/
+#define Reg2 register /**/
+#define Reg3 register /**/
+#define Reg4 register /**/
+#define Reg5 register /**/
+#define Reg6 register /**/
+#define Reg7 register /**/
+#define Reg8 register /**/
+#define Reg9 register /**/
+#define Reg10 register /**/
+#define Reg11 /**/
+#define Reg12 /**/
+#define Reg13 /**/
+#define Reg14 /**/
+#define Reg15 /**/
+#define Reg16 /**/
diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c
new file mode 100644
index 0000000..a3eeb90
--- /dev/null
+++ b/usr.bin/patch/inp.c
@@ -0,0 +1,313 @@
+/* $Header: inp.c,v 2.0 86/09/17 15:37:02 lwall Exp $
+ *
+ * $Log: inp.c,v $
+ * Revision 2.0 86/09/17 15:37:02 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "pch.h"
+#include "INTERN.h"
+#include "inp.h"
+
+/* Input-file-with-indexable-lines abstract type */
+
+static long i_size; /* size of the input file */
+static char *i_womp; /* plan a buffer for entire file */
+static char **i_ptr; /* pointers to lines in i_womp */
+
+static int tifd = -1; /* plan b virtual string array */
+static char *tibuf[2]; /* plan b buffers */
+static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */
+static LINENUM lines_per_buf; /* how many lines per buffer */
+static int tireclen; /* length of records in tmp file */
+
+/* New patch--prepare to edit another file. */
+
+void
+re_input()
+{
+ if (using_plan_a) {
+ i_size = 0;
+#ifndef lint
+ if (i_ptr != Null(char**))
+ free((char *)i_ptr);
+#endif
+ if (i_womp != Nullch)
+ free(i_womp);
+ i_womp = Nullch;
+ i_ptr = Null(char **);
+ }
+ else {
+ using_plan_a = TRUE; /* maybe the next one is smaller */
+ Close(tifd);
+ tifd = -1;
+ free(tibuf[0]);
+ free(tibuf[1]);
+ tibuf[0] = tibuf[1] = Nullch;
+ tiline[0] = tiline[1] = -1;
+ tireclen = 0;
+ }
+}
+
+/* Constuct the line index, somehow or other. */
+
+void
+scan_input(filename)
+char *filename;
+{
+ if (!plan_a(filename))
+ plan_b(filename);
+ if (verbose) {
+ say3("Patching file %s using Plan %s...\n", filename,
+ (using_plan_a ? "A" : "B") );
+ }
+}
+
+/* Try keeping everything in memory. */
+
+bool
+plan_a(filename)
+char *filename;
+{
+ int ifd;
+ Reg1 char *s;
+ Reg2 LINENUM iline;
+
+ if (ok_to_create_file && stat(filename, &filestat) < 0) {
+ if (verbose)
+ say2("(Creating file %s...)\n",filename);
+ makedirs(filename, TRUE);
+ close(creat(filename, 0666));
+ }
+ if (stat(filename, &filestat) < 0) {
+ Sprintf(buf, "RCS/%s%s", filename, RCSSUFFIX);
+ if (stat(buf, &filestat) >= 0 || stat(buf+4, &filestat) >= 0) {
+ Sprintf(buf, CHECKOUT, filename);
+ if (verbose)
+ say2("Can't find %s--attempting to check it out from RCS.\n",
+ filename);
+ if (system(buf) || stat(filename, &filestat))
+ fatal2("Can't check out %s.\n", filename);
+ }
+ else {
+ Sprintf(buf, "SCCS/%s%s", SCCSPREFIX, filename);
+ if (stat(buf, &filestat) >= 0 || stat(buf+5, &filestat) >= 0) {
+ Sprintf(buf, GET, filename);
+ if (verbose)
+ say2("Can't find %s--attempting to get it from SCCS.\n",
+ filename);
+ if (system(buf) || stat(filename, &filestat))
+ fatal2("Can't get %s.\n", filename);
+ }
+ else
+ fatal2("Can't find %s.\n", filename);
+ }
+ }
+ filemode = filestat.st_mode;
+ if ((filemode & S_IFMT) & ~S_IFREG)
+ fatal2("%s is not a normal file--can't patch.\n", filename);
+ i_size = filestat.st_size;
+ if (out_of_mem) {
+ set_hunkmax(); /* make sure dynamic arrays are allocated */
+ out_of_mem = FALSE;
+ return FALSE; /* force plan b because plan a bombed */
+ }
+#ifdef lint
+ i_womp = Nullch;
+#else
+ i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */
+ /* i_size, but that's okay, I think. */
+#endif
+ if (i_womp == Nullch)
+ return FALSE;
+ if ((ifd = open(filename, 0)) < 0)
+ fatal2("Can't open file %s\n", filename);
+#ifndef lint
+ if (read(ifd, i_womp, (int)i_size) != i_size) {
+ Close(ifd); /* probably means i_size > 15 or 16 bits worth */
+ free(i_womp); /* at this point it doesn't matter if i_womp was */
+ return FALSE; /* undersized. */
+ }
+#endif
+ Close(ifd);
+ if (i_size && i_womp[i_size-1] != '\n')
+ i_womp[i_size++] = '\n';
+ i_womp[i_size] = '\0';
+
+ /* count the lines in the buffer so we know how many pointers we need */
+
+ iline = 0;
+ for (s=i_womp; *s; s++) {
+ if (*s == '\n')
+ iline++;
+ }
+#ifdef lint
+ i_ptr = Null(char**);
+#else
+ i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
+#endif
+ if (i_ptr == Null(char **)) { /* shucks, it was a near thing */
+ free((char *)i_womp);
+ return FALSE;
+ }
+
+ /* now scan the buffer and build pointer array */
+
+ iline = 1;
+ i_ptr[iline] = i_womp;
+ for (s=i_womp; *s; s++) {
+ if (*s == '\n')
+ i_ptr[++iline] = s+1; /* these are NOT null terminated */
+ }
+ input_lines = iline - 1;
+
+ /* now check for revision, if any */
+
+ if (revision != Nullch) {
+ if (!rev_in_string(i_womp)) {
+ if (force) {
+ if (verbose)
+ say2("\
+Warning: this file doesn't appear to be the %s version--patching anyway.\n",
+ revision);
+ }
+ else {
+ ask2("\
+This file doesn't appear to be the %s version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal1("Aborted.\n");
+ }
+ }
+ else if (verbose)
+ say2("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ return TRUE; /* plan a will work */
+}
+
+/* Keep (virtually) nothing in memory. */
+
+void
+plan_b(filename)
+char *filename;
+{
+ Reg3 FILE *ifp;
+ Reg1 int i = 0;
+ Reg2 int maxlen = 1;
+ Reg4 bool found_revision = (revision == Nullch);
+
+ using_plan_a = FALSE;
+ if ((ifp = fopen(filename, "r")) == Nullfp)
+ fatal2("Can't open file %s\n", filename);
+ if ((tifd = creat(TMPINNAME, 0666)) < 0)
+ fatal2("Can't open file %s\n", TMPINNAME);
+ while (fgets(buf, sizeof buf, ifp) != Nullch) {
+ if (revision != Nullch && !found_revision && rev_in_string(buf))
+ found_revision = TRUE;
+ if ((i = strlen(buf)) > maxlen)
+ maxlen = i; /* find longest line */
+ }
+ if (revision != Nullch) {
+ if (!found_revision) {
+ if (force) {
+ if (verbose)
+ say2("\
+Warning: this file doesn't appear to be the %s version--patching anyway.\n",
+ revision);
+ }
+ else {
+ ask2("\
+This file doesn't appear to be the %s version--patch anyway? [n] ",
+ revision);
+ if (*buf != 'y')
+ fatal1("Aborted.\n");
+ }
+ }
+ else if (verbose)
+ say2("Good. This file appears to be the %s version.\n",
+ revision);
+ }
+ Fseek(ifp, 0L, 0); /* rewind file */
+ lines_per_buf = BUFFERSIZE / maxlen;
+ tireclen = maxlen;
+ tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
+ tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
+ if (tibuf[1] == Nullch)
+ fatal1("Can't seem to get enough memory.\n");
+ for (i=1; ; i++) {
+ if (! (i % lines_per_buf)) /* new block */
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ fatal1("patch: can't write temp file.\n");
+ if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
+ == Nullch) {
+ input_lines = i - 1;
+ if (i % lines_per_buf)
+ if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
+ fatal1("patch: can't write temp file.\n");
+ break;
+ }
+ }
+ Fclose(ifp);
+ Close(tifd);
+ if ((tifd = open(TMPINNAME, 0)) < 0) {
+ fatal2("Can't reopen file %s\n", TMPINNAME);
+ }
+}
+
+/* Fetch a line from the input file, \n terminated, not necessarily \0. */
+
+char *
+ifetch(line,whichbuf)
+Reg1 LINENUM line;
+int whichbuf; /* ignored when file in memory */
+{
+ if (line < 1 || line > input_lines)
+ return "";
+ if (using_plan_a)
+ return i_ptr[line];
+ else {
+ LINENUM offline = line % lines_per_buf;
+ LINENUM baseline = line - offline;
+
+ if (tiline[0] == baseline)
+ whichbuf = 0;
+ else if (tiline[1] == baseline)
+ whichbuf = 1;
+ else {
+ tiline[whichbuf] = baseline;
+#ifndef lint /* complains of long accuracy */
+ Lseek(tifd, (off_t)baseline / lines_per_buf * BUFFERSIZE, 0);
+#endif
+ if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
+ fatal2("Error reading tmp file %s.\n", TMPINNAME);
+ }
+ return tibuf[whichbuf] + (tireclen*offline);
+ }
+}
+
+/* True if the string argument contains the revision number we want. */
+
+bool
+rev_in_string(string)
+char *string;
+{
+ Reg1 char *s;
+ Reg2 int patlen;
+
+ if (revision == Nullch)
+ return TRUE;
+ patlen = strlen(revision);
+ for (s = string; *s; s++) {
+ if (isspace(*s) && strnEQ(s+1, revision, patlen) &&
+ isspace(s[patlen+1] )) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
diff --git a/usr.bin/patch/inp.h b/usr.bin/patch/inp.h
new file mode 100644
index 0000000..c6d2a91
--- /dev/null
+++ b/usr.bin/patch/inp.h
@@ -0,0 +1,18 @@
+/* $Header: inp.h,v 2.0 86/09/17 15:37:25 lwall Exp $
+ *
+ * $Log: inp.h,v $
+ * Revision 2.0 86/09/17 15:37:25 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+EXT LINENUM input_lines INIT(0); /* how long is input file in lines */
+EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */
+ /* irretractibly output */
+
+bool rev_in_string();
+void scan_input();
+bool plan_a(); /* returns false if insufficient memory */
+void plan_b();
+char *ifetch();
+
diff --git a/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1
new file mode 100644
index 0000000..3e4a12e
--- /dev/null
+++ b/usr.bin/patch/patch.1
@@ -0,0 +1,446 @@
+''' $Header: patch.man,v 2.0 86/09/17 15:39:09 lwall Exp $
+'''
+''' $Log: patch.man,v $
+''' Revision 2.0 86/09/17 15:39:09 lwall
+''' Baseline for netwide release.
+'''
+''' Revision 1.4 86/08/01 19:23:22 lwall
+''' Documented -v, -p, -F.
+''' Added notes to patch senders.
+'''
+''' Revision 1.3 85/03/26 15:11:06 lwall
+''' Frozen.
+'''
+''' Revision 1.2.1.4 85/03/12 16:14:27 lwall
+''' Documented -p.
+'''
+''' Revision 1.2.1.3 85/03/12 16:09:41 lwall
+''' Documented -D.
+'''
+''' Revision 1.2.1.2 84/12/05 11:06:55 lwall
+''' Added -l switch, and noted bistability bug.
+'''
+''' Revision 1.2.1.1 84/12/04 17:23:39 lwall
+''' Branch for sdcrdcf changes.
+'''
+''' Revision 1.2 84/12/04 17:22:02 lwall
+''' Baseline version.
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Bell System Logo is used as a dummy character.
+'''
+.ie n \{\
+.tr \(bs-\*(Tr
+.ds -- \(bs-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH PATCH 1 "June 30, 1993"
+.SH NAME
+patch \- a program for applying a diff file to an original
+.SH SYNOPSIS
+.B patch
+[options] orig patchfile [+ [options] orig]
+.sp
+but usually just
+.sp
+.B patch
+<patchfile
+.SH DESCRIPTION
+.I Patch
+will take a patch file containing any of the three forms of difference
+listing produced by the
+.I diff
+program and apply those differences to an original file, producing a patched
+version.
+By default, the patched version is put in place of the original, with
+the original file backed up to the same name with the
+extension \*(L".orig\*(R", or as specified by the
+.B -b
+switch.
+You may also specify where you want the output to go with a
+.B -o
+switch.
+If
+.I patchfile
+is omitted, or is a hyphen, the patch will be read from standard input.
+.PP
+Upon startup, patch will attempt to determine the type of the diff listing,
+unless over-ruled by a
+.BR -c ,
+.BR -e ,
+or
+.B -n
+switch.
+Context diffs and normal diffs are applied by the
+.I patch
+program itself, while ed diffs are simply fed to the
+.I ed
+editor via a pipe.
+.PP
+.I Patch
+will try to skip any leading garbage, apply the diff,
+and then skip any trailing garbage.
+Thus you could feed an article or message containing a
+diff listing to
+.IR patch ,
+and it should work.
+If the entire diff is indented by a consistent amount,
+this will be taken into account.
+.PP
+With context diffs, and to a lesser extent with normal diffs,
+.I patch
+can detect when the line numbers mentioned in the patch are incorrect,
+and will attempt to find the correct place to apply each hunk of the patch.
+As a first guess, it takes the line number mentioned for the hunk, plus or
+minus any offset used in applying the previous hunk.
+If that is not the correct place,
+.I patch
+will scan both forwards and backwards for a set of lines matching the context
+given in the hunk.
+First
+.I patch
+looks for a place where all lines of the context match.
+If no such place is found, and it's a context diff, and the maximum fuzz factor
+is set to 1 or more, then another scan takes place ignoring the first and last
+line of context.
+If that fails, and the maximum fuzz factor is set to 2 or more,
+the first two and last two lines of context are ignored,
+and another scan is made.
+(The default maximum fuzz factor is 2.)
+If
+.I patch
+cannot find a place to install that hunk of the patch, it will put the
+hunk out to a reject file, which normally is the name of the output file
+plus \*(L".rej\*(R".
+(Note that the rejected hunk will come out in context diff form whether the
+input patch was a context diff or a normal diff.
+If the input was a normal diff, many of the contexts will simply be null.)
+The line numbers on the hunks in the reject file may be different than
+in the patch file: they reflect the approximate location patch thinks the
+failed hunks belong in the new file rather than the old one.
+.PP
+As each hunk is completed, you will be told whether the hunk succeeded or
+failed, and which line (in the new file)
+.I patch
+thought the hunk should go on.
+If this is different from the line number specified in the diff you will
+be told the offset.
+A single large offset MAY be an indication that a hunk was installed in the
+wrong place.
+You will also be told if a fuzz factor was used to make the match, in which
+case you should also be slightly suspicious.
+.PP
+If no original file is specified on the command line,
+.I patch
+will try to figure out from the leading garbage what the name of the file
+to edit is.
+In the header of a context diff, the filename is found from lines beginning
+with \*(L"***\*(R" or \*(L"---\*(R", with the shortest name of an existing
+file winning.
+Only context diffs have lines like that, but if there is an \*(L"Index:\*(R"
+line in the leading garbage,
+.I patch
+will try to use the filename from that line.
+The context diff header takes precedence over an Index line.
+If no filename can be intuited from the leading garbage, you will be asked
+for the name of the file to patch.
+.PP
+(If the original file cannot be found, but a suitable SCCS or RCS file is
+handy,
+.I patch
+will attempt to get or check out the file.)
+.PP
+Additionally, if the leading garbage contains a \*(L"Prereq: \*(R" line,
+.I patch
+will take the first word from the prerequisites line (normally a version
+number) and check the input file to see if that word can be found.
+If not,
+.I patch
+will ask for confirmation before proceeding.
+.PP
+The upshot of all this is that you should be able to say, while in a news
+interface, the following:
+.Sp
+ | patch -d /usr/src/local/blurfl
+.Sp
+and patch a file in the blurfl directory directly from the article containing
+the patch.
+.PP
+If the patch file contains more than one patch,
+.I patch
+will try to apply each of them as if they came from separate patch files.
+This means, among other things, that it is assumed that the name of the file
+to patch must be determined for each diff listing,
+and that the garbage before each diff listing will
+be examined for interesting things such as filenames and revision level, as
+mentioned previously.
+You can give switches (and another original file name) for the second and
+subsequent patches by separating the corresponding argument lists
+by a \*(L'+\*(R'.
+(The argument list for a second or subsequent patch may not specify a new
+patch file, however.)
+.PP
+.I Patch
+recognizes the following switches:
+.TP 5
+.B \-b
+causes the next argument to be interpreted as the backup extension, to be
+used in place of \*(L".orig\*(R".
+.TP 5
+.B \-c
+forces
+.I patch
+to interpret the patch file as a context diff.
+.TP 5
+.B \-d
+causes
+.I patch
+to interpret the next argument as a directory, and cd to it before doing
+anything else.
+.TP 5
+.B \-D
+causes
+.I patch
+to use the "#ifdef...#endif" construct to mark changes.
+The argument following will be used as the differentiating symbol.
+Note that, unlike the C compiler, there must be a space between the
+.B \-D
+and the argument.
+.TP 5
+.B \-e
+forces
+.I patch
+to interpret the patch file as an ed script.
+.TP 5
+.B \-f
+forces
+.I patch
+to assume that the user knows exactly what he or she is doing, and to not
+ask any questions.
+It does not suppress commentary, however.
+Use
+.B \-s
+for that.
+.TP 5
+.B \-F<number>
+sets the maximum fuzz factor.
+This switch only applied to context diffs, and causes
+.I patch
+to ignore up to that many lines in looking for places to install a hunk.
+Note that a larger fuzz factor increases the odds of a faulty patch.
+The default fuzz factor is 2, and it may not be set to more than
+the number of lines of context in the context diff, ordinarily 3.
+.TP 5
+.B \-l
+causes the pattern matching to be done loosely, in case the tabs and
+spaces have been munged in your input file.
+Any sequence of whitespace in the pattern line will match any sequence
+in the input file.
+Normal characters must still match exactly.
+Each line of the context must still match a line in the input file.
+.TP 5
+.B \-n
+forces
+.I patch
+to interpret the patch file as a normal diff.
+.TP 5
+.B \-N
+causes
+.I patch
+to ignore patches that it thinks are reversed or already applied.
+See also
+.B \-R .
+.TP 5
+.B \-o
+causes the next argument to be interpreted as the output file name.
+.TP 5
+.B \-p<number>
+sets the pathname strip count,
+which controls how pathnames found in the patch file are treated, in case
+the you keep your files in a different directory than the person who sent
+out the patch.
+The strip count specifies how many backslashes are to be stripped from
+the front of the pathname.
+(Any intervening directory names also go away.)
+For example, supposing the filename in the patch file was
+.sp
+ /u/howard/src/blurfl/blurfl.c
+.sp
+setting
+.B \-p
+or
+.B \-p0
+gives the entire pathname unmodified,
+.B \-p1
+gives
+.sp
+ u/howard/src/blurfl/blurfl.c
+.sp
+without the leading slash,
+.B \-p4
+gives
+.sp
+ blurfl/blurfl.c
+.sp
+and not specifying
+.B \-p
+at all just gives you "blurfl.c".
+Whatever you end up with is looked for either in the current directory,
+or the directory specified by the
+.B \-d
+switch.
+.TP 5
+.B \-r
+causes the next argument to be interpreted as the reject file name.
+.TP 5
+.B \-R
+tells
+.I patch
+that this patch was created with the old and new files swapped.
+(Yes, I'm afraid that does happen occasionally, human nature being what it
+is.)
+.I Patch
+will attempt to swap each hunk around before applying it.
+Rejects will come out in the swapped format.
+The
+.B \-R
+switch will not work with ed diff scripts because there is too little
+information to reconstruct the reverse operation.
+.Sp
+If the first hunk of a patch fails,
+.I patch
+will reverse the hunk to see if it can be applied that way.
+If it can, you will be asked if you want to have the
+.B \-R
+switch set.
+If it can't, the patch will continue to be applied normally.
+(Note: this method cannot detect a reversed patch if it is a normal diff
+and if the first command is an append (i.e. it should have been a delete)
+since appends always succeed, due to the fact that a null context will match
+anywhere.
+Luckily, most patches add or change lines rather than delete them, so most
+reversed normal diffs will begin with a delete, which will fail, triggering
+the heuristic.)
+.TP 5
+.B \-s
+makes
+.I patch
+do its work silently, unless an error occurs.
+.TP 5
+.B \-S
+causes
+.I patch
+to ignore this patch from the patch file, but continue on looking
+for the next patch in the file.
+Thus
+.sp
+ patch -S + -S + <patchfile
+.sp
+will ignore the first and second of three patches.
+.TP 5
+.B \-v
+causes
+.I patch
+to print out it's revision header and patch level.
+.TP 5
+.B \-x<number>
+sets internal debugging flags, and is of interest only to
+.I patch
+patchers.
+.SH ENVIRONMENT
+No environment variables are used by
+.IR patch .
+.SH FILES
+/tmp/patch*
+.SH SEE ALSO
+diff(1)
+.SH NOTES FOR PATCH SENDERS
+There are several things you should bear in mind if you are going to
+be sending out patches.
+First, you can save people a lot of grief by keeping a patchlevel.h file
+which is patched to increment the patch level as the first diff in the
+patch file you send out.
+If you put a Prereq: line in with the patch, it won't let them apply
+patches out of order without some warning.
+Second, make sure you've specified the filenames right, either in a
+context diff header, or with an Index: line.
+If you are patching something in a subdirectory, be sure to tell the patch
+user to specify a
+.B \-p
+switch as needed.
+Third, you can create a file by sending out a diff that compares a
+null file to the file you want to create.
+This will only work if the file you want to create doesn't exist already in
+the target directory.
+Fourth, take care not to send out reversed patches, since it makes people wonder
+whether they already applied the patch.
+Fifth, while you may be able to get away with putting 582 diff listings into
+one file, it is probably wiser to group related patches into separate files in
+case something goes haywire.
+.SH DIAGNOSTICS
+Too many to list here, but generally indicative that
+.I patch
+couldn't parse your patch file.
+.PP
+The message \*(L"Hmm...\*(R" indicates that there is unprocessed text in
+the patch file and that
+.I patch
+is attempting to intuit whether there is a patch in that text and, if so,
+what kind of patch it is.
+.SH CAVEATS
+.I Patch
+cannot tell if the line numbers are off in an ed script, and can only detect
+bad line numbers in a normal diff when it finds a \*(L"change\*(R" or
+a \*(L"delete\*(R" command.
+A context diff using fuzz factor 3 may have the same problem.
+Until a suitable interactive interface is added, you should probably do
+a context diff in these cases to see if the changes made sense.
+Of course, compiling without errors is a pretty good indication that the patch
+worked, but not always.
+.PP
+.I Patch
+usually produces the correct results, even when it has to do a lot of
+guessing.
+However, the results are guaranteed to be correct only when the patch is
+applied to exactly the same version of the file that the patch was
+generated from.
+.SH BUGS
+Could be smarter about partial matches, excessively \&deviant offsets and
+swapped code, but that would take an extra pass.
+.PP
+If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
+#endif),
+.I patch
+is incapable of patching both versions, and, if it works at all, will likely
+patch the wrong one, and tell you that it succeeded to boot.
+.PP
+If you apply a patch you've already applied,
+.I patch
+will think it is a reversed patch, and offer to un-apply the patch.
+This could be construed as a feature.
diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c
new file mode 100644
index 0000000..0f91c5c
--- /dev/null
+++ b/usr.bin/patch/patch.c
@@ -0,0 +1,800 @@
+#ifndef lint
+static char sccsid[] = "@(#)patch.c 8.1 (Berkeley) 6/6/93";
+#endif not lint
+
+char rcsid[] =
+ "$Header: patch.c,v 2.0.1.4 87/02/16 14:00:04 lwall Exp $";
+
+/* patch - a program to apply diffs to original files
+ *
+ * Copyright 1986, Larry Wall
+ *
+ * This program may be copied as long as you don't try to make any
+ * money off of it, or pretend that you wrote it.
+ *
+ * $Log: patch.c,v $
+ * Revision 2.0.1.4 87/02/16 14:00:04 lwall
+ * Short replacement caused spurious "Out of sync" message.
+ *
+ * Revision 2.0.1.3 87/01/30 22:45:50 lwall
+ * Improved diagnostic on sync error.
+ * Moved do_ed_script() to pch.c.
+ *
+ * Revision 2.0.1.2 86/11/21 09:39:15 lwall
+ * Fuzz factor caused offset of installed lines.
+ *
+ * Revision 2.0.1.1 86/10/29 13:10:22 lwall
+ * Backwards search could terminate prematurely.
+ *
+ * Revision 2.0 86/09/17 15:37:32 lwall
+ * Baseline for netwide release.
+ *
+ * Revision 1.5 86/08/01 20:53:24 lwall
+ * Changed some %d's to %ld's.
+ * Linted.
+ *
+ * Revision 1.4 86/08/01 19:17:29 lwall
+ * Fixes for machines that can't vararg.
+ * Added fuzz factor.
+ * Generalized -p.
+ * General cleanup.
+ *
+ * 85/08/15 van%ucbmonet@berkeley
+ * Changes for 4.3bsd diff -c.
+ *
+ * Revision 1.3 85/03/26 15:07:43 lwall
+ * Frozen.
+ *
+ * Revision 1.2.1.9 85/03/12 17:03:35 lwall
+ * Changed pfp->_file to fileno(pfp).
+ *
+ * Revision 1.2.1.8 85/03/12 16:30:43 lwall
+ * Check i_ptr and i_womp to make sure they aren't null before freeing.
+ * Also allow ed output to be suppressed.
+ *
+ * Revision 1.2.1.7 85/03/12 15:56:13 lwall
+ * Added -p option from jromine@uci-750a.
+ *
+ * Revision 1.2.1.6 85/03/12 12:12:51 lwall
+ * Now checks for normalness of file to patch.
+ *
+ * Revision 1.2.1.5 85/03/12 11:52:12 lwall
+ * Added -D (#ifdef) option from joe@fluke.
+ *
+ * Revision 1.2.1.4 84/12/06 11:14:15 lwall
+ * Made smarter about SCCS subdirectories.
+ *
+ * Revision 1.2.1.3 84/12/05 11:18:43 lwall
+ * Added -l switch to do loose string comparison.
+ *
+ * Revision 1.2.1.2 84/12/04 09:47:13 lwall
+ * Failed hunk count not reset on multiple patch file.
+ *
+ * Revision 1.2.1.1 84/12/04 09:42:37 lwall
+ * Branch for sdcrdcf changes.
+ *
+ * Revision 1.2 84/11/29 13:29:51 lwall
+ * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
+ * multiple calls to mktemp(). Will now work on machines that can only
+ * read 32767 chars. Added -R option for diffs with new and old swapped.
+ * Various cosmetic changes.
+ *
+ * Revision 1.1 84/11/09 17:03:58 lwall
+ * Initial revision
+ *
+ */
+
+#include "INTERN.h"
+#include "common.h"
+#include "EXTERN.h"
+#include "version.h"
+#include "util.h"
+#include "pch.h"
+#include "inp.h"
+
+/* procedures */
+
+void reinitialize_almost_everything();
+void get_some_switches();
+LINENUM locate_hunk();
+void abort_hunk();
+void apply_hunk();
+void init_output();
+void init_reject();
+void copy_till();
+void spew_output();
+void dump_line();
+bool patch_match();
+bool similar();
+void re_input();
+void my_exit();
+
+/* Apply a set of diffs as appropriate. */
+
+main(argc,argv)
+int argc;
+char **argv;
+{
+ LINENUM where;
+ LINENUM newwhere;
+ LINENUM fuzz;
+ LINENUM mymaxfuzz;
+ int hunk = 0;
+ int failed = 0;
+ int i;
+
+ setbuf(stderr, serrbuf);
+ for (i = 0; i<MAXFILEC; i++)
+ filearg[i] = Nullch;
+ Mktemp(TMPOUTNAME);
+ Mktemp(TMPINNAME);
+ Mktemp(TMPREJNAME);
+ Mktemp(TMPPATNAME);
+
+ /* parse switches */
+ Argc = argc;
+ Argv = argv;
+ get_some_switches();
+
+ /* make sure we clean up /tmp in case of disaster */
+ set_signals();
+
+ for (
+ open_patch_file(filearg[1]);
+ there_is_another_patch();
+ reinitialize_almost_everything()
+ ) { /* for each patch in patch file */
+
+ if (outname == Nullch)
+ outname = savestr(filearg[0]);
+
+ /* initialize the patched file */
+ if (!skip_rest_of_patch)
+ init_output(TMPOUTNAME);
+
+ /* for ed script just up and do it and exit */
+ if (diff_type == ED_DIFF) {
+ do_ed_script();
+ continue;
+ }
+
+ /* initialize reject file */
+ init_reject(TMPREJNAME);
+
+ /* find out where all the lines are */
+ if (!skip_rest_of_patch)
+ scan_input(filearg[0]);
+
+ /* from here on, open no standard i/o files, because malloc */
+ /* might misfire and we can't catch it easily */
+
+ /* apply each hunk of patch */
+ hunk = 0;
+ failed = 0;
+ out_of_mem = FALSE;
+ while (another_hunk()) {
+ hunk++;
+ fuzz = Nulline;
+ mymaxfuzz = pch_context();
+ if (maxfuzz < mymaxfuzz)
+ mymaxfuzz = maxfuzz;
+ if (!skip_rest_of_patch) {
+ do {
+ where = locate_hunk(fuzz);
+ if (hunk == 1 && where == Nulline && !force) {
+ /* dwim for reversed patch? */
+ if (!pch_swap()) {
+ if (fuzz == Nulline)
+ say1("\
+Not enough memory to try swapped hunk! Assuming unswapped.\n");
+ continue;
+ }
+ reverse = !reverse;
+ where = locate_hunk(fuzz); /* try again */
+ if (where == Nulline) { /* didn't find it swapped */
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ reverse = !reverse;
+ }
+ else if (noreverse) {
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ reverse = !reverse;
+ say1("\
+Ignoring previously applied (or reversed) patch.\n");
+ skip_rest_of_patch = TRUE;
+ }
+ else {
+ ask3("\
+%seversed (or previously applied) patch detected! %s -R? [y] ",
+ reverse ? "R" : "Unr",
+ reverse ? "Assume" : "Ignore");
+ if (*buf == 'n') {
+ ask1("Apply anyway? [n] ");
+ if (*buf != 'y')
+ skip_rest_of_patch = TRUE;
+ where = Nulline;
+ reverse = !reverse;
+ if (!pch_swap()) /* put it back to normal */
+ fatal1("Lost hunk on alloc error!\n");
+ }
+ }
+ }
+ } while (!skip_rest_of_patch && where == Nulline &&
+ ++fuzz <= mymaxfuzz);
+
+ if (skip_rest_of_patch) { /* just got decided */
+ Fclose(ofp);
+ ofp = Nullfp;
+ }
+ }
+
+ newwhere = pch_newfirst() + last_offset;
+ if (skip_rest_of_patch) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
+ }
+ else if (where == Nulline) {
+ abort_hunk();
+ failed++;
+ if (verbose)
+ say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
+ }
+ else {
+ apply_hunk(where);
+ if (verbose) {
+ say3("Hunk #%d succeeded at %ld", hunk, newwhere);
+ if (fuzz)
+ say2(" with fuzz %ld", fuzz);
+ if (last_offset)
+ say3(" (offset %ld line%s)",
+ last_offset, last_offset==1L?"":"s");
+ say1(".\n");
+ }
+ }
+ }
+
+ if (out_of_mem && using_plan_a) {
+ Argc = Argc_last;
+ Argv = Argv_last;
+ say1("\n\nRan out of memory using Plan A--trying again...\n\n");
+ continue;
+ }
+
+ assert(hunk);
+
+ /* finish spewing out the new file */
+ if (!skip_rest_of_patch)
+ spew_output();
+
+ /* and put the output where desired */
+ ignore_signals();
+ if (!skip_rest_of_patch) {
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = TRUE;
+ chmod(TMPOUTNAME, filemode);
+ }
+ else
+ chmod(outname, filemode);
+ }
+ Fclose(rejfp);
+ rejfp = Nullfp;
+ if (failed) {
+ if (!*rejname) {
+ Strcpy(rejname, outname);
+ Strcat(rejname, ".rej");
+ }
+ if (skip_rest_of_patch) {
+ say4("%d out of %d hunks ignored--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ else {
+ say4("%d out of %d hunks failed--saving rejects to %s\n",
+ failed, hunk, rejname);
+ }
+ if (move_file(TMPREJNAME, rejname) < 0)
+ trejkeep = TRUE;
+ }
+ set_signals();
+ }
+ my_exit(0);
+}
+
+/* Prepare to find the next patch to do in the patch file. */
+
+void
+reinitialize_almost_everything()
+{
+ re_patch();
+ re_input();
+
+ input_lines = 0;
+ last_frozen_line = 0;
+
+ filec = 0;
+ if (filearg[0] != Nullch && !out_of_mem) {
+ free(filearg[0]);
+ filearg[0] = Nullch;
+ }
+
+ if (outname != Nullch) {
+ free(outname);
+ outname = Nullch;
+ }
+
+ last_offset = 0;
+
+ diff_type = 0;
+
+ if (revision != Nullch) {
+ free(revision);
+ revision = Nullch;
+ }
+
+ reverse = FALSE;
+ skip_rest_of_patch = FALSE;
+
+ get_some_switches();
+
+ if (filec >= 2)
+ fatal1("You may not change to a different patch file.\n");
+}
+
+/* Process switches and filenames up to next '+' or end of list. */
+
+void
+get_some_switches()
+{
+ Reg1 char *s;
+
+ rejname[0] = '\0';
+ Argc_last = Argc;
+ Argv_last = Argv;
+ if (!Argc)
+ return;
+ for (Argc--,Argv++; Argc; Argc--,Argv++) {
+ s = Argv[0];
+ if (strEQ(s, "+")) {
+ return; /* + will be skipped by for loop */
+ }
+ if (*s != '-' || !s[1]) {
+ if (filec == MAXFILEC)
+ fatal1("Too many file arguments.\n");
+ filearg[filec++] = savestr(s);
+ }
+ else {
+ switch (*++s) {
+ case 'b':
+ origext = savestr(Argv[1]);
+ Argc--,Argv++;
+ break;
+ case 'c':
+ diff_type = CONTEXT_DIFF;
+ break;
+ case 'd':
+ if (!*++s) {
+ Argc--,Argv++;
+ s = Argv[0];
+ }
+ if (chdir(s) < 0)
+ fatal2("Can't cd to %s.\n", s);
+ break;
+ case 'D':
+ do_defines = TRUE;
+ if (!*++s) {
+ Argc--,Argv++;
+ s = Argv[0];
+ }
+ Sprintf(if_defined, "#ifdef %s\n", s);
+ Sprintf(not_defined, "#ifndef %s\n", s);
+ Sprintf(end_defined, "#endif /* %s */\n", s);
+ break;
+ case 'e':
+ diff_type = ED_DIFF;
+ break;
+ case 'f':
+ force = TRUE;
+ break;
+ case 'F':
+ if (*++s == '=')
+ s++;
+ maxfuzz = atoi(s);
+ break;
+ case 'l':
+ canonicalize = TRUE;
+ break;
+ case 'n':
+ diff_type = NORMAL_DIFF;
+ break;
+ case 'N':
+ noreverse = TRUE;
+ break;
+ case 'o':
+ outname = savestr(Argv[1]);
+ Argc--,Argv++;
+ break;
+ case 'p':
+ if (*++s == '=')
+ s++;
+ strippath = atoi(s);
+ break;
+ case 'r':
+ Strcpy(rejname, Argv[1]);
+ Argc--,Argv++;
+ break;
+ case 'R':
+ reverse = TRUE;
+ break;
+ case 's':
+ verbose = FALSE;
+ break;
+ case 'S':
+ skip_rest_of_patch = TRUE;
+ break;
+ case 'v':
+ version();
+ break;
+#ifdef DEBUGGING
+ case 'x':
+ debug = atoi(s+1);
+ break;
+#endif
+ default:
+ fatal2("Unrecognized switch: %s\n", Argv[0]);
+ }
+ }
+ }
+}
+
+/* Attempt to find the right place to apply this hunk of patch. */
+
+LINENUM
+locate_hunk(fuzz)
+LINENUM fuzz;
+{
+ Reg1 LINENUM first_guess = pch_first() + last_offset;
+ Reg2 LINENUM offset;
+ LINENUM pat_lines = pch_ptrn_lines();
+ Reg3 LINENUM max_pos_offset = input_lines - first_guess
+ - pat_lines + 1;
+ Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
+ + pch_context();
+
+ if (!pat_lines) /* null range matches always */
+ return first_guess;
+ if (max_neg_offset >= first_guess) /* do not try lines < 0 */
+ max_neg_offset = first_guess - 1;
+ if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
+ return first_guess;
+ for (offset = 1; ; offset++) {
+ Reg5 bool check_after = (offset <= max_pos_offset);
+ Reg6 bool check_before = (offset <= max_neg_offset);
+
+ if (check_after && patch_match(first_guess, offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say3("Offset changing from %ld to %ld\n", last_offset, offset);
+#endif
+ last_offset = offset;
+ return first_guess+offset;
+ }
+ else if (check_before && patch_match(first_guess, -offset, fuzz)) {
+#ifdef DEBUGGING
+ if (debug & 1)
+ say3("Offset changing from %ld to %ld\n", last_offset, -offset);
+#endif
+ last_offset = -offset;
+ return first_guess-offset;
+ }
+ else if (!check_before && !check_after)
+ return Nulline;
+ }
+}
+
+/* We did not find the pattern, dump out the hunk so they can handle it. */
+
+void
+abort_hunk()
+{
+ Reg1 LINENUM i;
+ Reg2 LINENUM pat_end = pch_end();
+ /* add in last_offset to guess the same as the previous successful hunk */
+ LINENUM oldfirst = pch_first() + last_offset;
+ LINENUM newfirst = pch_newfirst() + last_offset;
+ LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
+ LINENUM newlast = newfirst + pch_repl_lines() - 1;
+ char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : "");
+ char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----");
+
+ fprintf(rejfp, "***************\n");
+ for (i=0; i<=pat_end; i++) {
+ switch (pch_char(i)) {
+ case '*':
+ if (oldlast < oldfirst)
+ fprintf(rejfp, "*** 0%s\n", stars);
+ else if (oldlast == oldfirst)
+ fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
+ else
+ fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
+ break;
+ case '=':
+ if (newlast < newfirst)
+ fprintf(rejfp, "--- 0%s\n", minuses);
+ else if (newlast == newfirst)
+ fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
+ else
+ fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
+ break;
+ case '\n':
+ fprintf(rejfp, "%s", pfetch(i));
+ break;
+ case ' ': case '-': case '+': case '!':
+ fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
+ break;
+ default:
+ say1("Fatal internal error in abort_hunk().\n");
+ abort();
+ }
+ }
+}
+
+/* We found where to apply it (we hope), so do it. */
+
+void
+apply_hunk(where)
+LINENUM where;
+{
+ Reg1 LINENUM old = 1;
+ Reg2 LINENUM lastline = pch_ptrn_lines();
+ Reg3 LINENUM new = lastline+1;
+#define OUTSIDE 0
+#define IN_IFNDEF 1
+#define IN_IFDEF 2
+#define IN_ELSE 3
+ Reg4 int def_state = OUTSIDE;
+ Reg5 bool R_do_defines = do_defines;
+ Reg6 LINENUM pat_end = pch_end();
+
+ where--;
+ while (pch_char(new) == '=' || pch_char(new) == '\n')
+ new++;
+
+ while (old <= lastline) {
+ if (pch_char(old) == '-') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ }
+ else if (def_state == IN_IFDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ }
+ else if (new > pat_end)
+ break;
+ else if (pch_char(new) == '+') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ else if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ }
+ }
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ else {
+ if (pch_char(new) != pch_char(old)) {
+ say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
+ pch_hunk_beg() + old,
+ pch_hunk_beg() + new);
+#ifdef DEBUGGING
+ say3("oldchar = '%c', newchar = '%c'\n",
+ pch_char(old), pch_char(new));
+#endif
+ my_exit(1);
+ }
+ if (pch_char(new) == '!') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ fputs(not_defined, ofp);
+ def_state = IN_IFNDEF;
+ }
+ while (pch_char(old) == '!') {
+ if (R_do_defines) {
+ fputs(pfetch(old), ofp);
+ }
+ last_frozen_line++;
+ old++;
+ }
+ if (R_do_defines) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ while (pch_char(new) == '!') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ if (R_do_defines) {
+ fputs(end_defined, ofp);
+ def_state = OUTSIDE;
+ }
+ }
+ else {
+ assert(pch_char(new) == ' ');
+ old++;
+ new++;
+ }
+ }
+ }
+ if (new <= pat_end && pch_char(new) == '+') {
+ copy_till(where + old - 1);
+ if (R_do_defines) {
+ if (def_state == OUTSIDE) {
+ fputs(if_defined, ofp);
+ def_state = IN_IFDEF;
+ }
+ else if (def_state == IN_IFNDEF) {
+ fputs(else_defined, ofp);
+ def_state = IN_ELSE;
+ }
+ }
+ while (new <= pat_end && pch_char(new) == '+') {
+ fputs(pfetch(new), ofp);
+ new++;
+ }
+ }
+ if (R_do_defines && def_state != OUTSIDE) {
+ fputs(end_defined, ofp);
+ }
+}
+
+/* Open the new file. */
+
+void
+init_output(name)
+char *name;
+{
+ ofp = fopen(name, "w");
+ if (ofp == Nullfp)
+ fatal2("patch: can't create %s.\n", name);
+}
+
+/* Open a file to put hunks we can't locate. */
+
+void
+init_reject(name)
+char *name;
+{
+ rejfp = fopen(name, "w");
+ if (rejfp == Nullfp)
+ fatal2("patch: can't create %s.\n", name);
+}
+
+/* Copy input file to output, up to wherever hunk is to be applied. */
+
+void
+copy_till(lastline)
+Reg1 LINENUM lastline;
+{
+ Reg2 LINENUM R_last_frozen_line = last_frozen_line;
+
+ if (R_last_frozen_line > lastline)
+ say1("patch: misordered hunks! output will be garbled.\n");
+ while (R_last_frozen_line < lastline) {
+ dump_line(++R_last_frozen_line);
+ }
+ last_frozen_line = R_last_frozen_line;
+}
+
+/* Finish copying the input file to the output file. */
+
+void
+spew_output()
+{
+#ifdef DEBUGGING
+ if (debug & 256)
+ say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
+#endif
+ if (input_lines)
+ copy_till(input_lines); /* dump remainder of file */
+ Fclose(ofp);
+ ofp = Nullfp;
+}
+
+/* Copy one line from input to output. */
+
+void
+dump_line(line)
+LINENUM line;
+{
+ Reg1 char *s;
+ Reg2 char R_newline = '\n';
+
+ /* Note: string is not null terminated. */
+ for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
+}
+
+/* Does the patch pattern match at line base+offset? */
+
+bool
+patch_match(base, offset, fuzz)
+LINENUM base;
+LINENUM offset;
+LINENUM fuzz;
+{
+ Reg1 LINENUM pline = 1 + fuzz;
+ Reg2 LINENUM iline;
+ Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
+
+ for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
+ if (canonicalize) {
+ if (!similar(ifetch(iline, (offset >= 0)),
+ pfetch(pline),
+ pch_line_len(pline) ))
+ return FALSE;
+ }
+ else if (strnNE(ifetch(iline, (offset >= 0)),
+ pfetch(pline),
+ pch_line_len(pline) ))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Do two lines match with canonicalized white space? */
+
+bool
+similar(a,b,len)
+Reg1 char *a;
+Reg2 char *b;
+Reg3 int len;
+{
+ while (len) {
+ if (isspace(*b)) { /* whitespace (or \n) to match? */
+ if (!isspace(*a)) /* no corresponding whitespace? */
+ return FALSE;
+ while (len && isspace(*b) && *b != '\n')
+ b++,len--; /* skip pattern whitespace */
+ while (isspace(*a) && *a != '\n')
+ a++; /* skip target whitespace */
+ if (*a == '\n' || *b == '\n')
+ return (*a == *b); /* should end in sync */
+ }
+ else if (*a++ != *b++) /* match non-whitespace chars */
+ return FALSE;
+ else
+ len--; /* probably not necessary */
+ }
+ return TRUE; /* actually, this is not reached */
+ /* since there is always a \n */
+}
+
+/* Exit with cleanup. */
+
+void
+my_exit(status)
+int status;
+{
+ Unlink(TMPINNAME);
+ if (!toutkeep) {
+ Unlink(TMPOUTNAME);
+ }
+ if (!trejkeep) {
+ Unlink(TMPREJNAME);
+ }
+ Unlink(TMPPATNAME);
+ exit(status);
+}
diff --git a/usr.bin/patch/patchlevel.h b/usr.bin/patch/patchlevel.h
new file mode 100644
index 0000000..618bca4
--- /dev/null
+++ b/usr.bin/patch/patchlevel.h
@@ -0,0 +1 @@
+#define PATCHLEVEL 9
diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c
new file mode 100644
index 0000000..8837212
--- /dev/null
+++ b/usr.bin/patch/pch.c
@@ -0,0 +1,1108 @@
+/* $Header: pch.c,v 2.0.1.6 87/06/04 16:18:13 lwall Exp $
+ *
+ * $Log: pch.c,v $
+ * Revision 2.0.1.6 87/06/04 16:18:13 lwall
+ * pch_swap didn't swap p_bfake and p_efake.
+ *
+ * Revision 2.0.1.5 87/01/30 22:47:42 lwall
+ * Improved responses to mangled patches.
+ *
+ * Revision 2.0.1.4 87/01/05 16:59:53 lwall
+ * New-style context diffs caused double call to free().
+ *
+ * Revision 2.0.1.3 86/11/14 10:08:33 lwall
+ * Fixed problem where a long pattern wouldn't grow the hunk.
+ * Also restored p_input_line when backtracking so error messages are right.
+ *
+ * Revision 2.0.1.2 86/11/03 17:49:52 lwall
+ * New-style delete triggers spurious assertion error.
+ *
+ * Revision 2.0.1.1 86/10/29 15:52:08 lwall
+ * Could falsely report new-style context diff.
+ *
+ * Revision 2.0 86/09/17 15:39:37 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "INTERN.h"
+#include "pch.h"
+
+/* Patch (diff listing) abstract type. */
+
+static long p_filesize; /* size of the patch file */
+static LINENUM p_first; /* 1st line number */
+static LINENUM p_newfirst; /* 1st line number of replacement */
+static LINENUM p_ptrn_lines; /* # lines in pattern */
+static LINENUM p_repl_lines; /* # lines in replacement text */
+static LINENUM p_end = -1; /* last line in hunk */
+static LINENUM p_max; /* max allowed value of p_end */
+static LINENUM p_context = 3; /* # of context lines */
+static LINENUM p_input_line = 0; /* current line # from patch file */
+static char **p_line = Null(char**); /* the text of the hunk */
+static short *p_len = Null(short*); /* length of each line */
+static char *p_char = Nullch; /* +, -, and ! */
+static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */
+static int p_indent; /* indent to patch */
+static LINENUM p_base; /* where to intuit this time */
+static LINENUM p_bline; /* line # of p_base */
+static LINENUM p_start; /* where intuit found a patch */
+static LINENUM p_sline; /* and the line number for it */
+static LINENUM p_hunk_beg; /* line number of current hunk */
+static LINENUM p_efake = -1; /* end of faked up lines--don't free */
+static LINENUM p_bfake = -1; /* beg of faked up lines */
+
+/* Prepare to look for the next patch in the patch file. */
+
+void
+re_patch()
+{
+ p_first = Nulline;
+ p_newfirst = Nulline;
+ p_ptrn_lines = Nulline;
+ p_repl_lines = Nulline;
+ p_end = (LINENUM)-1;
+ p_max = Nulline;
+ p_indent = 0;
+}
+
+/* Open the patch file at the beginning of time. */
+
+void
+open_patch_file(filename)
+char *filename;
+{
+ if (filename == Nullch || !*filename || strEQ(filename, "-")) {
+ pfp = fopen(TMPPATNAME, "w");
+ if (pfp == Nullfp)
+ fatal2("patch: can't create %s.\n", TMPPATNAME);
+ while (fgets(buf, sizeof buf, stdin) != Nullch)
+ fputs(buf, pfp);
+ Fclose(pfp);
+ filename = TMPPATNAME;
+ }
+ pfp = fopen(filename, "r");
+ if (pfp == Nullfp)
+ fatal2("patch file %s not found\n", filename);
+ Fstat(fileno(pfp), &filestat);
+ p_filesize = filestat.st_size;
+ next_intuit_at(0L,1L); /* start at the beginning */
+ set_hunkmax();
+}
+
+/* Make sure our dynamically realloced tables are malloced to begin with. */
+
+void
+set_hunkmax()
+{
+#ifndef lint
+ if (p_line == Null(char**))
+ p_line = (char**) malloc((MEM)hunkmax * sizeof(char *));
+ if (p_len == Null(short*))
+ p_len = (short*) malloc((MEM)hunkmax * sizeof(short));
+#endif
+ if (p_char == Nullch)
+ p_char = (char*) malloc((MEM)hunkmax * sizeof(char));
+}
+
+/* Enlarge the arrays containing the current hunk of patch. */
+
+void
+grow_hunkmax()
+{
+ hunkmax *= 2;
+ /*
+ * Note that on most systems, only the p_line array ever gets fresh memory
+ * since p_len can move into p_line's old space, and p_char can move into
+ * p_len's old space. Not on PDP-11's however. But it doesn't matter.
+ */
+ assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch);
+#ifndef lint
+ p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *));
+ p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short));
+ p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char));
+#endif
+ if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch)
+ return;
+ if (!using_plan_a)
+ fatal1("patch: out of memory (grow_hunkmax)\n");
+ out_of_mem = TRUE; /* whatever is null will be allocated again */
+ /* from within plan_a(), of all places */
+}
+
+/* True if the remainder of the patch file contains a diff of some sort. */
+
+bool
+there_is_another_patch()
+{
+ if (p_base != 0L && p_base >= p_filesize) {
+ if (verbose)
+ say1("done\n");
+ return FALSE;
+ }
+ if (verbose)
+ say1("Hmm...");
+ diff_type = intuit_diff_type();
+ if (!diff_type) {
+ if (p_base != 0L) {
+ if (verbose)
+ say1(" Ignoring the trailing garbage.\ndone\n");
+ }
+ else
+ say1(" I can't seem to find a patch in there anywhere.\n");
+ return FALSE;
+ }
+ if (verbose)
+ say3(" %sooks like %s to me...\n",
+ (p_base == 0L ? "L" : "The next patch l"),
+ diff_type == CONTEXT_DIFF ? "a context diff" :
+ diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
+ diff_type == NORMAL_DIFF ? "a normal diff" :
+ "an ed script" );
+ if (p_indent && verbose)
+ say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s");
+ skip_to(p_start,p_sline);
+ while (filearg[0] == Nullch) {
+ if (force) {
+ say1("No file to patch. Skipping...\n");
+ filearg[0] = savestr(bestguess);
+ return TRUE;
+ }
+ ask1("File to patch: ");
+ if (*buf != '\n') {
+ if (bestguess)
+ free(bestguess);
+ bestguess = savestr(buf);
+ filearg[0] = fetchname(buf, 0, FALSE);
+ }
+ if (filearg[0] == Nullch) {
+ ask1("No file found--skip this patch? [n] ");
+ if (*buf != 'y') {
+ continue;
+ }
+ if (verbose)
+ say1("Skipping patch...\n");
+ filearg[0] = fetchname(bestguess, 0, TRUE);
+ skip_rest_of_patch = TRUE;
+ return TRUE;
+ }
+ }
+ return TRUE;
+}
+
+/* Determine what kind of diff is in the remaining part of the patch file. */
+
+int
+intuit_diff_type()
+{
+ Reg4 long this_line = 0;
+ Reg5 long previous_line;
+ Reg6 long first_command_line = -1;
+ long fcl_line;
+ Reg7 bool last_line_was_command = FALSE;
+ Reg8 bool this_is_a_command = FALSE;
+ Reg9 bool stars_last_line = FALSE;
+ Reg10 bool stars_this_line = FALSE;
+ Reg3 int indent;
+ Reg1 char *s;
+ Reg2 char *t;
+ char *indtmp = Nullch;
+ char *oldtmp = Nullch;
+ char *newtmp = Nullch;
+ char *indname = Nullch;
+ char *oldname = Nullch;
+ char *newname = Nullch;
+ Reg11 int retval;
+ bool no_filearg = (filearg[0] == Nullch);
+
+ ok_to_create_file = FALSE;
+ Fseek(pfp, p_base, 0);
+ p_input_line = p_bline - 1;
+ for (;;) {
+ previous_line = this_line;
+ last_line_was_command = this_is_a_command;
+ stars_last_line = stars_this_line;
+ this_line = ftell(pfp);
+ indent = 0;
+ p_input_line++;
+ if (fgets(buf, sizeof buf, pfp) == Nullch) {
+ if (first_command_line >= 0L) {
+ /* nothing but deletes!? */
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ }
+ else {
+ p_start = this_line;
+ p_sline = p_input_line;
+ retval = 0;
+ goto scan_exit;
+ }
+ }
+ for (s = buf; *s == ' ' || *s == '\t'; s++) {
+ if (*s == '\t')
+ indent += 8 - (indent % 8);
+ else
+ indent++;
+ }
+ for (t=s; isdigit(*t) || *t == ','; t++) ;
+ this_is_a_command = (isdigit(*s) &&
+ (*t == 'd' || *t == 'c' || *t == 'a') );
+ if (first_command_line < 0L && this_is_a_command) {
+ first_command_line = this_line;
+ fcl_line = p_input_line;
+ p_indent = indent; /* assume this for now */
+ }
+ if (!stars_last_line && strnEQ(s, "*** ", 4))
+ oldtmp = savestr(s+4);
+ else if (strnEQ(s, "--- ", 4))
+ newtmp = savestr(s+4);
+ else if (strnEQ(s, "Index:", 6))
+ indtmp = savestr(s+6);
+ else if (strnEQ(s, "Prereq:", 7)) {
+ for (t=s+7; isspace(*t); t++) ;
+ revision = savestr(t);
+ for (t=revision; *t && !isspace(*t); t++) ;
+ *t = '\0';
+ if (!*revision) {
+ free(revision);
+ revision = Nullch;
+ }
+ }
+ if ((!diff_type || diff_type == ED_DIFF) &&
+ first_command_line >= 0L &&
+ strEQ(s, ".\n") ) {
+ p_indent = indent;
+ p_start = first_command_line;
+ p_sline = fcl_line;
+ retval = ED_DIFF;
+ goto scan_exit;
+ }
+ stars_this_line = strnEQ(s, "********", 8);
+ if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
+ strnEQ(s, "*** ", 4)) {
+ if (!atol(s+4))
+ ok_to_create_file = TRUE;
+ /* if this is a new context diff the character just before */
+ /* the newline is a '*'. */
+ while (*s != '\n')
+ s++;
+ p_indent = indent;
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
+ goto scan_exit;
+ }
+ if ((!diff_type || diff_type == NORMAL_DIFF) &&
+ last_line_was_command &&
+ (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) {
+ p_start = previous_line;
+ p_sline = p_input_line - 1;
+ p_indent = indent;
+ retval = NORMAL_DIFF;
+ goto scan_exit;
+ }
+ }
+ scan_exit:
+ if (no_filearg) {
+ if (indtmp != Nullch)
+ indname = fetchname(indtmp, strippath, ok_to_create_file);
+ if (oldtmp != Nullch)
+ oldname = fetchname(oldtmp, strippath, ok_to_create_file);
+ if (newtmp != Nullch)
+ newname = fetchname(newtmp, strippath, ok_to_create_file);
+ if (oldname && newname) {
+ if (strlen(oldname) < strlen(newname))
+ filearg[0] = savestr(oldname);
+ else
+ filearg[0] = savestr(newname);
+ }
+ else if (oldname)
+ filearg[0] = savestr(oldname);
+ else if (newname)
+ filearg[0] = savestr(newname);
+ else if (indname)
+ filearg[0] = savestr(indname);
+ }
+ if (bestguess) {
+ free(bestguess);
+ bestguess = Nullch;
+ }
+ if (filearg[0] != Nullch)
+ bestguess = savestr(filearg[0]);
+ else if (indtmp != Nullch)
+ bestguess = fetchname(indtmp, strippath, TRUE);
+ else {
+ if (oldtmp != Nullch)
+ oldname = fetchname(oldtmp, strippath, TRUE);
+ if (newtmp != Nullch)
+ newname = fetchname(newtmp, strippath, TRUE);
+ if (oldname && newname) {
+ if (strlen(oldname) < strlen(newname))
+ bestguess = savestr(oldname);
+ else
+ bestguess = savestr(newname);
+ }
+ else if (oldname)
+ bestguess = savestr(oldname);
+ else if (newname)
+ bestguess = savestr(newname);
+ }
+ if (indtmp != Nullch)
+ free(indtmp);
+ if (oldtmp != Nullch)
+ free(oldtmp);
+ if (newtmp != Nullch)
+ free(newtmp);
+ if (indname != Nullch)
+ free(indname);
+ if (oldname != Nullch)
+ free(oldname);
+ if (newname != Nullch)
+ free(newname);
+ return retval;
+}
+
+/* Remember where this patch ends so we know where to start up again. */
+
+void
+next_intuit_at(file_pos,file_line)
+long file_pos;
+long file_line;
+{
+ p_base = file_pos;
+ p_bline = file_line;
+}
+
+/* Basically a verbose fseek() to the actual diff listing. */
+
+void
+skip_to(file_pos,file_line)
+long file_pos;
+long file_line;
+{
+ char *ret;
+
+ assert(p_base <= file_pos);
+ if (verbose && p_base < file_pos) {
+ Fseek(pfp, p_base, 0);
+ say1("The text leading up to this was:\n--------------------------\n");
+ while (ftell(pfp) < file_pos) {
+ ret = fgets(buf, sizeof buf, pfp);
+ assert(ret != Nullch);
+ say2("|%s", buf);
+ }
+ say1("--------------------------\n");
+ }
+ else
+ Fseek(pfp, file_pos, 0);
+ p_input_line = file_line - 1;
+}
+
+/* True if there is more of the current diff listing to process. */
+
+bool
+another_hunk()
+{
+ Reg1 char *s;
+ Reg8 char *ret;
+ Reg2 int context = 0;
+
+ while (p_end >= 0) {
+ if (p_end == p_efake)
+ p_end = p_bfake; /* don't free twice */
+ else
+ free(p_line[p_end]);
+ p_end--;
+ }
+ assert(p_end == -1);
+ p_efake = -1;
+
+ p_max = hunkmax; /* gets reduced when --- found */
+ if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
+ long line_beginning = ftell(pfp);
+ /* file pos of the current line */
+ LINENUM repl_beginning = 0; /* index of --- line */
+ Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */
+ Reg5 LINENUM fillsrc; /* index of first line to copy */
+ Reg6 LINENUM filldst; /* index of first missing line */
+ bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */
+ Reg9 bool repl_could_be_missing = TRUE;
+ /* no + or ! lines in this hunk */
+ bool repl_missing = FALSE; /* we are now backtracking */
+ long repl_backtrack_position = 0;
+ /* file pos of first repl line */
+ LINENUM repl_patch_line; /* input line number for same */
+ Reg7 LINENUM ptrn_copiable = 0;
+ /* # of copiable lines in ptrn */
+
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch || strnNE(buf, "********", 8)) {
+ next_intuit_at(line_beginning,p_input_line);
+ return FALSE;
+ }
+ p_context = 100;
+ p_hunk_beg = p_input_line + 1;
+ while (p_end < p_max) {
+ line_beginning = ftell(pfp);
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch) {
+ if (p_max - p_end < 4)
+ Strcpy(buf, " \n"); /* assume blank lines got chopped */
+ else {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal1("Unexpected end of file in patch.\n");
+ }
+ }
+ p_end++;
+ assert(p_end < hunkmax);
+ p_char[p_end] = *buf;
+ p_line[p_end] = Nullch;
+ switch (*buf) {
+ case '*':
+ if (strnEQ(buf, "********", 8)) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ else
+ fatal2("Unexpected end of hunk at line %ld.\n",
+ p_input_line);
+ }
+ if (p_end != 0) {
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal3("Unexpected *** at line %ld: %s", p_input_line, buf);
+ }
+ context = 0;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ for (s=buf; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ goto malformed;
+ p_first = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ for (; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ goto malformed;
+ p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
+ }
+ else if (p_first)
+ p_ptrn_lines = 1;
+ else {
+ p_ptrn_lines = 0;
+ p_first = 1;
+ }
+ p_max = p_ptrn_lines + 6; /* we need this much at least */
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ p_max = hunkmax;
+ break;
+ case '-':
+ if (buf[1] == '-') {
+ if (repl_beginning ||
+ (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n')))
+ {
+ if (p_end == 1) {
+ /* `old' lines were omitted - set up to fill */
+ /* them in from 'new' context lines. */
+ p_end = p_ptrn_lines + 1;
+ fillsrc = p_end + 1;
+ filldst = 1;
+ fillcnt = p_ptrn_lines;
+ }
+ else {
+ if (repl_beginning) {
+ if (repl_could_be_missing){
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ fatal3(
+"Duplicate \"---\" at line %ld--check line numbers at line %ld.\n",
+ p_input_line, p_hunk_beg + repl_beginning);
+ }
+ else {
+ fatal4(
+"%s \"---\" at line %ld--check line numbers at line %ld.\n",
+ (p_end <= p_ptrn_lines
+ ? "Premature"
+ : "Overdue" ),
+ p_input_line, p_hunk_beg);
+ }
+ }
+ }
+ repl_beginning = p_end;
+ repl_backtrack_position = ftell(pfp);
+ repl_patch_line = p_input_line;
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ p_char[p_end] = '=';
+ for (s=buf; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ goto malformed;
+ p_newfirst = (LINENUM) atol(s);
+ while (isdigit(*s)) s++;
+ if (*s == ',') {
+ for (; *s && !isdigit(*s); s++) ;
+ if (!*s)
+ goto malformed;
+ p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1;
+ }
+ else if (p_newfirst)
+ p_repl_lines = 1;
+ else {
+ p_repl_lines = 0;
+ p_newfirst = 1;
+ }
+ p_max = p_repl_lines + p_end;
+ if (p_max > MAXHUNKSIZE)
+ fatal4("Hunk too large (%ld lines) at line %ld: %s",
+ p_max, p_input_line, buf);
+ while (p_max >= hunkmax)
+ grow_hunkmax();
+ if (p_repl_lines != ptrn_copiable)
+ repl_could_be_missing = FALSE;
+ break;
+ }
+ goto change_line;
+ case '+': case '!':
+ repl_could_be_missing = FALSE;
+ change_line:
+ if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ if (context > 0) {
+ if (context < p_context)
+ p_context = context;
+ context = -1000;
+ }
+ p_line[p_end] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ break;
+ case '\t': case '\n': /* assume the 2 spaces got eaten */
+ if (repl_beginning && repl_could_be_missing &&
+ (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ p_line[p_end] = savestr(buf);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ if (p_end != p_ptrn_lines + 1) {
+ ptrn_spaces_eaten |= (repl_beginning != 0);
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_char[p_end] = ' ';
+ }
+ break;
+ case ' ':
+ if (!isspace(buf[1]) &&
+ repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ context++;
+ if (!repl_beginning)
+ ptrn_copiable++;
+ p_line[p_end] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end--;
+ return FALSE;
+ }
+ break;
+ default:
+ if (repl_beginning && repl_could_be_missing) {
+ repl_missing = TRUE;
+ goto hunk_done;
+ }
+ goto malformed;
+ }
+ /* set up p_len for strncmp() so we don't have to */
+ /* assume null termination */
+ if (p_line[p_end])
+ p_len[p_end] = strlen(p_line[p_end]);
+ else
+ p_len[p_end] = 0;
+ }
+
+ hunk_done:
+ if (p_end >=0 && !repl_beginning)
+ fatal2("No --- found in patch at line %ld\n", pch_hunk_beg());
+
+ if (repl_missing) {
+
+ /* reset state back to just after --- */
+ p_input_line = repl_patch_line;
+ for (p_end--; p_end > repl_beginning; p_end--)
+ free(p_line[p_end]);
+ Fseek(pfp, repl_backtrack_position, 0);
+
+ /* redundant 'new' context lines were omitted - set */
+ /* up to fill them in from the old file context */
+ fillsrc = 1;
+ filldst = repl_beginning+1;
+ fillcnt = p_repl_lines;
+ p_end = p_max;
+ }
+
+ if (diff_type == CONTEXT_DIFF &&
+ (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) {
+ if (verbose)
+ say1("\
+(Fascinating--this is really a new-style context diff but without the telltale\n\
+extra asterisks on the *** line that usually indicate the new style...)\n");
+ diff_type = NEW_CONTEXT_DIFF;
+ }
+
+ /* if there were omitted context lines, fill them in now */
+ if (fillcnt) {
+ p_bfake = filldst; /* remember where not to free() */
+ p_efake = filldst + fillcnt - 1;
+ while (fillcnt-- > 0) {
+ while (fillsrc <= p_end && p_char[fillsrc] != ' ')
+ fillsrc++;
+ if (fillsrc > p_end)
+ fatal2("Replacement text or line numbers mangled in hunk at line %ld\n",
+ p_hunk_beg);
+ p_line[filldst] = p_line[fillsrc];
+ p_char[filldst] = p_char[fillsrc];
+ p_len[filldst] = p_len[fillsrc];
+ fillsrc++; filldst++;
+ }
+ while (fillsrc <= p_end && fillsrc != repl_beginning &&
+ p_char[fillsrc] != ' ')
+ fillsrc++;
+#ifdef DEBUGGING
+ if (debug & 64)
+ printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
+ fillsrc,filldst,repl_beginning,p_end+1);
+#endif
+ assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
+ assert(filldst==p_end+1 || filldst==repl_beginning);
+ }
+ }
+ else { /* normal diff--fake it up */
+ char hunk_type;
+ Reg3 int i;
+ LINENUM min, max;
+ long line_beginning = ftell(pfp);
+
+ p_context = 0;
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch || !isdigit(*buf)) {
+ next_intuit_at(line_beginning,p_input_line);
+ return FALSE;
+ }
+ p_first = (LINENUM)atol(buf);
+ for (s=buf; isdigit(*s); s++) ;
+ if (*s == ',') {
+ p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
+ while (isdigit(*s)) s++;
+ }
+ else
+ p_ptrn_lines = (*s != 'a');
+ hunk_type = *s;
+ if (hunk_type == 'a')
+ p_first++; /* do append rather than insert */
+ min = (LINENUM)atol(++s);
+ for (; isdigit(*s); s++) ;
+ if (*s == ',')
+ max = (LINENUM)atol(++s);
+ else
+ max = min;
+ if (hunk_type == 'd')
+ min++;
+ p_end = p_ptrn_lines + 1 + max - min + 1;
+ if (p_end > MAXHUNKSIZE)
+ fatal4("Hunk too large (%ld lines) at line %ld: %s",
+ p_end, p_input_line, buf);
+ while (p_end >= hunkmax)
+ grow_hunkmax();
+ p_newfirst = min;
+ p_repl_lines = max - min + 1;
+ Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
+ p_line[0] = savestr(buf);
+ if (out_of_mem) {
+ p_end = -1;
+ return FALSE;
+ }
+ p_char[0] = '*';
+ for (i=1; i<=p_ptrn_lines; i++) {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("Unexpected end of file in patch at line %ld.\n",
+ p_input_line);
+ if (*buf != '<')
+ fatal2("< expected at line %ld of patch.\n", p_input_line);
+ p_line[i] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_char[i] = '-';
+ }
+ if (hunk_type == 'c') {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("Unexpected end of file in patch at line %ld.\n",
+ p_input_line);
+ if (*buf != '-')
+ fatal2("--- expected at line %ld of patch.\n", p_input_line);
+ }
+ Sprintf(buf, "--- %ld,%ld\n", min, max);
+ p_line[i] = savestr(buf);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_char[i] = '=';
+ for (i++; i<=p_end; i++) {
+ ret = pgets(buf, sizeof buf, pfp);
+ p_input_line++;
+ if (ret == Nullch)
+ fatal2("Unexpected end of file in patch at line %ld.\n",
+ p_input_line);
+ if (*buf != '>')
+ fatal2("> expected at line %ld of patch.\n", p_input_line);
+ p_line[i] = savestr(buf+2);
+ if (out_of_mem) {
+ p_end = i-1;
+ return FALSE;
+ }
+ p_len[i] = strlen(p_line[i]);
+ p_char[i] = '+';
+ }
+ }
+ if (reverse) /* backwards patch? */
+ if (!pch_swap())
+ say1("Not enough memory to swap next hunk!\n");
+#ifdef DEBUGGING
+ if (debug & 2) {
+ int i;
+ char special;
+
+ for (i=0; i <= p_end; i++) {
+ if (i == p_ptrn_lines)
+ special = '^';
+ else
+ special = ' ';
+ fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]);
+ Fflush(stderr);
+ }
+ }
+#endif
+ if (p_end+1 < hunkmax) /* paranoia reigns supreme... */
+ p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */
+ return TRUE;
+
+malformed:
+ fatal3("Malformed patch at line %ld: %s", p_input_line, buf);
+ /* about as informative as "Syntax error" in C */
+ return FALSE; /* for lint */
+}
+
+/* Input a line from the patch file, worrying about indentation. */
+
+char *
+pgets(bf,sz,fp)
+char *bf;
+int sz;
+FILE *fp;
+{
+ char *ret = fgets(bf, sz, fp);
+ Reg1 char *s;
+ Reg2 int indent = 0;
+
+ if (p_indent && ret != Nullch) {
+ for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
+ if (*s == '\t')
+ indent += 8 - (indent % 7);
+ else
+ indent++;
+ }
+ if (buf != s)
+ Strcpy(buf, s);
+ }
+ return ret;
+}
+
+/* Reverse the old and new portions of the current hunk. */
+
+bool
+pch_swap()
+{
+ char **tp_line; /* the text of the hunk */
+ short *tp_len; /* length of each line */
+ char *tp_char; /* +, -, and ! */
+ Reg1 LINENUM i;
+ Reg2 LINENUM n;
+ bool blankline = FALSE;
+ Reg3 char *s;
+
+ i = p_first;
+ p_first = p_newfirst;
+ p_newfirst = i;
+
+ /* make a scratch copy */
+
+ tp_line = p_line;
+ tp_len = p_len;
+ tp_char = p_char;
+ p_line = Null(char**); /* force set_hunkmax to allocate again */
+ p_len = Null(short*);
+ p_char = Nullch;
+ set_hunkmax();
+ if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) {
+#ifndef lint
+ if (p_line == Null(char**))
+ free((char*)p_line);
+ p_line = tp_line;
+ if (p_len == Null(short*))
+ free((char*)p_len);
+ p_len = tp_len;
+#endif
+ if (p_char == Nullch)
+ free((char*)p_char);
+ p_char = tp_char;
+ return FALSE; /* not enough memory to swap hunk! */
+ }
+
+ /* now turn the new into the old */
+
+ i = p_ptrn_lines + 1;
+ if (tp_char[i] == '\n') { /* account for possible blank line */
+ blankline = TRUE;
+ i++;
+ }
+ if (p_efake >= 0) { /* fix non-freeable ptr range */
+ n = p_end - i + 1;
+ if (p_efake > i)
+ n = -n;
+ p_efake += n;
+ p_bfake += n;
+ }
+ for (n=0; i <= p_end; i++,n++) {
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ if (p_char[n] == '+')
+ p_char[n] = '-';
+ p_len[n] = tp_len[i];
+ }
+ if (blankline) {
+ i = p_ptrn_lines + 1;
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ p_len[n] = tp_len[i];
+ n++;
+ }
+ assert(p_char[0] == '=');
+ p_char[0] = '*';
+ for (s=p_line[0]; *s; s++)
+ if (*s == '-')
+ *s = '*';
+
+ /* now turn the old into the new */
+
+ assert(tp_char[0] == '*');
+ tp_char[0] = '=';
+ for (s=tp_line[0]; *s; s++)
+ if (*s == '*')
+ *s = '-';
+ for (i=0; n <= p_end; i++,n++) {
+ p_line[n] = tp_line[i];
+ p_char[n] = tp_char[i];
+ if (p_char[n] == '-')
+ p_char[n] = '+';
+ p_len[n] = tp_len[i];
+ }
+ assert(i == p_ptrn_lines + 1);
+ i = p_ptrn_lines;
+ p_ptrn_lines = p_repl_lines;
+ p_repl_lines = i;
+#ifndef lint
+ if (tp_line == Null(char**))
+ free((char*)tp_line);
+ if (tp_len == Null(short*))
+ free((char*)tp_len);
+#endif
+ if (tp_char == Nullch)
+ free((char*)tp_char);
+ return TRUE;
+}
+
+/* Return the specified line position in the old file of the old context. */
+
+LINENUM
+pch_first()
+{
+ return p_first;
+}
+
+/* Return the number of lines of old context. */
+
+LINENUM
+pch_ptrn_lines()
+{
+ return p_ptrn_lines;
+}
+
+/* Return the probable line position in the new file of the first line. */
+
+LINENUM
+pch_newfirst()
+{
+ return p_newfirst;
+}
+
+/* Return the number of lines in the replacement text including context. */
+
+LINENUM
+pch_repl_lines()
+{
+ return p_repl_lines;
+}
+
+/* Return the number of lines in the whole hunk. */
+
+LINENUM
+pch_end()
+{
+ return p_end;
+}
+
+/* Return the number of context lines before the first changed line. */
+
+LINENUM
+pch_context()
+{
+ return p_context;
+}
+
+/* Return the length of a particular patch line. */
+
+short
+pch_line_len(line)
+LINENUM line;
+{
+ return p_len[line];
+}
+
+/* Return the control character (+, -, *, !, etc) for a patch line. */
+
+char
+pch_char(line)
+LINENUM line;
+{
+ return p_char[line];
+}
+
+/* Return a pointer to a particular patch line. */
+
+char *
+pfetch(line)
+LINENUM line;
+{
+ return p_line[line];
+}
+
+/* Return where in the patch file this hunk began, for error messages. */
+
+LINENUM
+pch_hunk_beg()
+{
+ return p_hunk_beg;
+}
+
+/* Apply an ed script by feeding ed itself. */
+
+void
+do_ed_script()
+{
+ Reg1 char *t;
+ Reg2 long beginning_of_this_line;
+ Reg3 bool this_line_is_command = FALSE;
+ Reg4 FILE *pipefp;
+ FILE *popen();
+
+ if (!skip_rest_of_patch) {
+ Unlink(TMPOUTNAME);
+ copy_file(filearg[0], TMPOUTNAME);
+ if (verbose)
+ Sprintf(buf, "/bin/ed %s", TMPOUTNAME);
+ else
+ Sprintf(buf, "/bin/ed - %s", TMPOUTNAME);
+ pipefp = popen(buf, "w");
+ }
+ for (;;) {
+ beginning_of_this_line = ftell(pfp);
+ if (pgets(buf, sizeof buf, pfp) == Nullch) {
+ next_intuit_at(beginning_of_this_line,p_input_line);
+ break;
+ }
+ p_input_line++;
+ for (t=buf; isdigit(*t) || *t == ','; t++) ;
+ this_line_is_command = (isdigit(*buf) &&
+ (*t == 'd' || *t == 'c' || *t == 'a') );
+ if (this_line_is_command) {
+ if (!skip_rest_of_patch)
+ fputs(buf, pipefp);
+ if (*t != 'd') {
+ while (pgets(buf, sizeof buf, pfp) != Nullch) {
+ p_input_line++;
+ if (!skip_rest_of_patch)
+ fputs(buf, pipefp);
+ if (strEQ(buf, ".\n"))
+ break;
+ }
+ }
+ }
+ else {
+ next_intuit_at(beginning_of_this_line,p_input_line);
+ break;
+ }
+ }
+ if (skip_rest_of_patch)
+ return;
+ fprintf(pipefp, "w\n");
+ fprintf(pipefp, "q\n");
+ Fflush(pipefp);
+ Pclose(pipefp);
+ ignore_signals();
+ if (move_file(TMPOUTNAME, outname) < 0) {
+ toutkeep = TRUE;
+ chmod(TMPOUTNAME, filemode);
+ }
+ else
+ chmod(outname, filemode);
+ set_signals();
+}
diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h
new file mode 100644
index 0000000..97a5b28
--- /dev/null
+++ b/usr.bin/patch/pch.h
@@ -0,0 +1,36 @@
+/* $Header: pch.h,v 2.0.1.1 87/01/30 22:47:16 lwall Exp $
+ *
+ * $Log: pch.h,v $
+ * Revision 2.0.1.1 87/01/30 22:47:16 lwall
+ * Added do_ed_script().
+ *
+ * Revision 2.0 86/09/17 15:39:57 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+EXT FILE *pfp INIT(Nullfp); /* patch file pointer */
+
+void re_patch();
+void open_patch_file();
+void set_hunkmax();
+void grow_hunkmax();
+bool there_is_another_patch();
+int intuit_diff_type();
+void next_intuit_at();
+void skip_to();
+bool another_hunk();
+bool pch_swap();
+char *pfetch();
+short pch_line_len();
+LINENUM pch_first();
+LINENUM pch_ptrn_lines();
+LINENUM pch_newfirst();
+LINENUM pch_repl_lines();
+LINENUM pch_end();
+LINENUM pch_context();
+LINENUM pch_hunk_beg();
+char pch_char();
+char *pfetch();
+char *pgets();
+void do_ed_script();
diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c
new file mode 100644
index 0000000..5582d18
--- /dev/null
+++ b/usr.bin/patch/util.c
@@ -0,0 +1,339 @@
+#include "EXTERN.h"
+#include "common.h"
+#include "INTERN.h"
+#include "util.h"
+
+/* Rename a file, copying it if necessary. */
+
+int
+move_file(from,to)
+char *from, *to;
+{
+ char bakname[512];
+ Reg1 char *s;
+ Reg2 int i;
+ Reg3 int fromfd;
+
+ /* to stdout? */
+
+ if (strEQ(to, "-")) {
+#ifdef DEBUGGING
+ if (debug & 4)
+ say2("Moving %s to stdout.\n", from);
+#endif
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ fatal2("patch: internal error, can't reopen %s\n", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(1, buf, i) != 1)
+ fatal1("patch: write failed\n");
+ Close(fromfd);
+ return 0;
+ }
+
+ Strcpy(bakname, to);
+ Strcat(bakname, origext?origext:ORIGEXT);
+ if (stat(to, &filestat) >= 0) { /* output file exists */
+ dev_t to_device = filestat.st_dev;
+ ino_t to_inode = filestat.st_ino;
+ char *simplename = bakname;
+
+ for (s=bakname; *s; s++) {
+ if (*s == '/')
+ simplename = s+1;
+ }
+ /* find a backup name that is not the same file */
+ while (stat(bakname, &filestat) >= 0 &&
+ to_device == filestat.st_dev && to_inode == filestat.st_ino) {
+ for (s=simplename; *s && !islower(*s); s++) ;
+ if (*s)
+ *s = toupper(*s);
+ else
+ Strcpy(simplename, simplename+1);
+ }
+ while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */
+#ifdef DEBUGGING
+ if (debug & 4)
+ say3("Moving %s to %s.\n", to, bakname);
+#endif
+ if (link(to, bakname) < 0) {
+ say3("patch: can't backup %s, output is in %s\n",
+ to, from);
+ return -1;
+ }
+ while (unlink(to) >= 0) ;
+ }
+#ifdef DEBUGGING
+ if (debug & 4)
+ say3("Moving %s to %s.\n", from, to);
+#endif
+ if (link(from, to) < 0) { /* different file system? */
+ Reg4 int tofd;
+
+ tofd = creat(to, 0666);
+ if (tofd < 0) {
+ say3("patch: can't create %s, output is in %s.\n",
+ to, from);
+ return -1;
+ }
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ fatal2("patch: internal error, can't reopen %s\n", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(tofd, buf, i) != i)
+ fatal1("patch: write failed\n");
+ Close(fromfd);
+ Close(tofd);
+ }
+ Unlink(from);
+ return 0;
+}
+
+/* Copy a file. */
+
+void
+copy_file(from,to)
+char *from, *to;
+{
+ Reg3 int tofd;
+ Reg2 int fromfd;
+ Reg1 int i;
+
+ tofd = creat(to, 0666);
+ if (tofd < 0)
+ fatal2("patch: can't create %s.\n", to);
+ fromfd = open(from, 0);
+ if (fromfd < 0)
+ fatal2("patch: internal error, can't reopen %s\n", from);
+ while ((i=read(fromfd, buf, sizeof buf)) > 0)
+ if (write(tofd, buf, i) != i)
+ fatal2("patch: write (%s) failed\n", to);
+ Close(fromfd);
+ Close(tofd);
+}
+
+/* Allocate a unique area for a string. */
+
+char *
+savestr(s)
+Reg1 char *s;
+{
+ Reg3 char *rv;
+ Reg2 char *t;
+
+ if (!s)
+ s = "Oops";
+ t = s;
+ while (*t++);
+ rv = malloc((MEM) (t - s));
+ if (rv == Nullch) {
+ if (using_plan_a)
+ out_of_mem = TRUE;
+ else
+ fatal1("patch: out of memory (savestr)\n");
+ }
+ else {
+ t = rv;
+ while (*t++ = *s++);
+ }
+ return rv;
+}
+
+#if defined(lint) && defined(CANVARARG)
+
+/*VARARGS ARGSUSED*/
+say(pat) char *pat; { ; }
+/*VARARGS ARGSUSED*/
+fatal(pat) char *pat; { ; }
+/*VARARGS ARGSUSED*/
+ask(pat) char *pat; { ; }
+
+#else
+
+/* Vanilla terminal output (buffered). */
+
+void
+say(pat,arg1,arg2,arg3)
+char *pat;
+int arg1,arg2,arg3;
+{
+ fprintf(stderr, pat, arg1, arg2, arg3);
+ Fflush(stderr);
+}
+
+/* Terminal output, pun intended. */
+
+void /* very void */
+fatal(pat,arg1,arg2,arg3)
+char *pat;
+int arg1,arg2,arg3;
+{
+ void my_exit();
+
+ say(pat, arg1, arg2, arg3);
+ my_exit(1);
+}
+
+/* Get a response from the user, somehow or other. */
+
+void
+ask(pat,arg1,arg2,arg3)
+char *pat;
+int arg1,arg2,arg3;
+{
+ int ttyfd;
+ int r;
+ bool tty2 = isatty(2);
+
+ Sprintf(buf, pat, arg1, arg2, arg3);
+ Fflush(stderr);
+ write(2, buf, strlen(buf));
+ if (tty2) { /* might be redirected to a file */
+ r = read(2, buf, sizeof buf);
+ }
+ else if (isatty(1)) { /* this may be new file output */
+ Fflush(stdout);
+ write(1, buf, strlen(buf));
+ r = read(1, buf, sizeof buf);
+ }
+ else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
+ /* might be deleted or unwriteable */
+ write(ttyfd, buf, strlen(buf));
+ r = read(ttyfd, buf, sizeof buf);
+ Close(ttyfd);
+ }
+ else if (isatty(0)) { /* this is probably patch input */
+ Fflush(stdin);
+ write(0, buf, strlen(buf));
+ r = read(0, buf, sizeof buf);
+ }
+ else { /* no terminal at all--default it */
+ buf[0] = '\n';
+ r = 1;
+ }
+ if (r <= 0)
+ buf[0] = 0;
+ else
+ buf[r] = '\0';
+ if (!tty2)
+ say1(buf);
+}
+#endif lint
+
+/* How to handle certain events when not in a critical region. */
+
+void
+set_signals()
+{
+ void my_exit();
+
+#ifndef lint
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ Signal(SIGHUP, my_exit);
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ Signal(SIGINT, my_exit);
+#endif
+}
+
+/* How to handle certain events when in a critical region. */
+
+void
+ignore_signals()
+{
+#ifndef lint
+ Signal(SIGHUP, SIG_IGN);
+ Signal(SIGINT, SIG_IGN);
+#endif
+}
+
+/* Make sure we'll have the directories to create a file. */
+
+void
+makedirs(filename,striplast)
+Reg1 char *filename;
+bool striplast;
+{
+ char tmpbuf[256];
+ Reg2 char *s = tmpbuf;
+ char *dirv[20];
+ Reg3 int i;
+ Reg4 int dirvp = 0;
+
+ while (*filename) {
+ if (*filename == '/') {
+ filename++;
+ dirv[dirvp++] = s;
+ *s++ = '\0';
+ }
+ else {
+ *s++ = *filename++;
+ }
+ }
+ *s = '\0';
+ dirv[dirvp] = s;
+ if (striplast)
+ dirvp--;
+ if (dirvp < 0)
+ return;
+ strcpy(buf, "mkdir");
+ s = buf;
+ for (i=0; i<=dirvp; i++) {
+ while (*s) s++;
+ *s++ = ' ';
+ strcpy(s, tmpbuf);
+ *dirv[i] = '/';
+ }
+ system(buf);
+}
+
+/* Make filenames more reasonable. */
+
+char *
+fetchname(at,strip_leading,assume_exists)
+char *at;
+int strip_leading;
+int assume_exists;
+{
+ char *s;
+ char *name;
+ Reg1 char *t;
+ char tmpbuf[200];
+
+ if (!at)
+ return Nullch;
+ s = savestr(at);
+ for (t=s; isspace(*t); t++) ;
+ name = t;
+#ifdef DEBUGGING
+ if (debug & 128)
+ say4("fetchname %s %d %d\n",name,strip_leading,assume_exists);
+#endif
+ if (strnEQ(name, "/dev/null", 9)) /* so files can be created by diffing */
+ return Nullch; /* against /dev/null. */
+ for (; *t && !isspace(*t); t++)
+ if (*t == '/')
+ if (--strip_leading >= 0)
+ name = t+1;
+ *t = '\0';
+ if (name != s && *s != '/') {
+ name[-1] = '\0';
+ if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) {
+ name[-1] = '/';
+ name=s;
+ }
+ }
+ name = savestr(name);
+ Sprintf(tmpbuf, "RCS/%s", name);
+ free(s);
+ if (stat(name, &filestat) < 0 && !assume_exists) {
+ Strcat(tmpbuf, RCSSUFFIX);
+ if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) {
+ Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name);
+ if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) {
+ free(name);
+ name = Nullch;
+ }
+ }
+ }
+ return name;
+}
diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h
new file mode 100644
index 0000000..9896c63
--- /dev/null
+++ b/usr.bin/patch/util.h
@@ -0,0 +1,74 @@
+/* $Header: util.h,v 2.0 86/09/17 15:40:06 lwall Exp $
+ *
+ * $Log: util.h,v $
+ * Revision 2.0 86/09/17 15:40:06 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+/* and for those machine that can't handle a variable argument list */
+
+#ifdef CANVARARG
+
+#define say1 say
+#define say2 say
+#define say3 say
+#define say4 say
+#define ask1 ask
+#define ask2 ask
+#define ask3 ask
+#define ask4 ask
+#define fatal1 fatal
+#define fatal2 fatal
+#define fatal3 fatal
+#define fatal4 fatal
+
+#else /* hope they allow multi-line macro actual arguments */
+
+#ifdef lint
+
+#define say1(a) say(a, 0, 0, 0)
+#define say2(a,b) say(a, (b)==(b), 0, 0)
+#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0)
+#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d))
+#define ask1(a) ask(a, 0, 0, 0)
+#define ask2(a,b) ask(a, (b)==(b), 0, 0)
+#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0)
+#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d))
+#define fatal1(a) fatal(a, 0, 0, 0)
+#define fatal2(a,b) fatal(a, (b)==(b), 0, 0)
+#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0)
+#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d))
+
+#else /* lint */
+ /* if this doesn't work, try defining CANVARARG above */
+#define say1(a) say(a, Nullch, Nullch, Nullch)
+#define say2(a,b) say(a, b, Nullch, Nullch)
+#define say3(a,b,c) say(a, b, c, Nullch)
+#define say4 say
+#define ask1(a) ask(a, Nullch, Nullch, Nullch)
+#define ask2(a,b) ask(a, b, Nullch, Nullch)
+#define ask3(a,b,c) ask(a, b, c, Nullch)
+#define ask4 ask
+#define fatal1(a) fatal(a, Nullch, Nullch, Nullch)
+#define fatal2(a,b) fatal(a, b, Nullch, Nullch)
+#define fatal3(a,b,c) fatal(a, b, c, Nullch)
+#define fatal4 fatal
+
+#endif /* lint */
+
+/* if neither of the above work, join all multi-line macro calls. */
+#endif
+
+EXT char serrbuf[BUFSIZ]; /* buffer for stderr */
+
+char *fetchname();
+int move_file();
+void copy_file();
+void say();
+void fatal();
+void ask();
+char *savestr();
+void set_signals();
+void ignore_signals();
+void makedirs();
diff --git a/usr.bin/patch/version.c b/usr.bin/patch/version.c
new file mode 100644
index 0000000..17dfb81
--- /dev/null
+++ b/usr.bin/patch/version.c
@@ -0,0 +1,28 @@
+/* $Header: version.c,v 2.0 86/09/17 15:40:11 lwall Exp $
+ *
+ * $Log: version.c,v $
+ * Revision 2.0 86/09/17 15:40:11 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+#include "EXTERN.h"
+#include "common.h"
+#include "util.h"
+#include "INTERN.h"
+#include "patchlevel.h"
+#include "version.h"
+
+/* Print out the version number and die. */
+
+void
+version()
+{
+ extern char rcsid[];
+
+#ifdef lint
+ rcsid[0] = rcsid[0];
+#else
+ fatal3("%s\nPatch level: %d\n", rcsid, PATCHLEVEL);
+#endif
+}
diff --git a/usr.bin/patch/version.h b/usr.bin/patch/version.h
new file mode 100644
index 0000000..08fe68d
--- /dev/null
+++ b/usr.bin/patch/version.h
@@ -0,0 +1,9 @@
+/* $Header: version.h,v 2.0 86/09/17 15:40:14 lwall Exp $
+ *
+ * $Log: version.h,v $
+ * Revision 2.0 86/09/17 15:40:14 lwall
+ * Baseline for netwide release.
+ *
+ */
+
+void version();
diff --git a/usr.bin/pr/Makefile b/usr.bin/pr/Makefile
new file mode 100644
index 0000000..70a921d
--- /dev/null
+++ b/usr.bin/pr/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pr
+SRCS= pr.c egetopt.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c
new file mode 100644
index 0000000..dd5bbbd
--- /dev/null
+++ b/usr.bin/pr/egetopt.c
@@ -0,0 +1,215 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)egetopt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+/*
+ * egetopt: get option letter from argument vector (an extended
+ * version of getopt).
+ *
+ * Non standard additions to the ostr specs are:
+ * 1) '?': immediate value following arg is optional (no white space
+ * between the arg and the value)
+ * 2) '#': +/- followed by a number (with an optional sign but
+ * no white space between the arg and the number). The - may be
+ * combined with other options, but the + cannot.
+ */
+
+int eopterr = 1; /* if error message should be printed */
+int eoptind = 1; /* index into parent argv vector */
+int eoptopt; /* character checked for validity */
+char *eoptarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define EMSG ""
+
+int
+egetopt(nargc, nargv, ostr)
+ int nargc;
+ char * const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ register char *oli; /* option letter list index */
+ static int delim; /* which option delimeter */
+ register char *p;
+ static char savec = '\0';
+
+ if (savec != '\0') {
+ *place = savec;
+ savec = '\0';
+ }
+
+ if (!*place) {
+ /*
+ * update scanning pointer
+ */
+ if ((eoptind >= nargc) ||
+ ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) {
+ place = EMSG;
+ return (EOF);
+ }
+
+ delim = (int)*place;
+ if (place[1] && *++place == '-' && !place[1]) {
+ /*
+ * found "--"
+ */
+ ++eoptind;
+ place = EMSG;
+ return (EOF);
+ }
+ }
+
+ /*
+ * check option letter
+ */
+ if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') ||
+ !(oli = strchr(ostr, eoptopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means EOF when by itself.
+ */
+ if ((eoptopt == (int)'-') && !*place)
+ return (EOF);
+ if (strchr(ostr, '#') && (isdigit(eoptopt) ||
+ (((eoptopt == (int)'-') || (eoptopt == (int)'+')) &&
+ isdigit(*place)))) {
+ /*
+ * # option: +/- with a number is ok
+ */
+ for (p = place; *p != '\0'; ++p) {
+ if (!isdigit(*p))
+ break;
+ }
+ eoptarg = place-1;
+
+ if (*p == '\0') {
+ place = EMSG;
+ ++eoptind;
+ } else {
+ place = p;
+ savec = *p;
+ *place = '\0';
+ }
+ return (delim);
+ }
+
+ if (!*place)
+ ++eoptind;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr, "%s: illegal option -- %c\n",
+ p, eoptopt);
+ }
+ return (BADCH);
+ }
+ if (delim == (int)'+') {
+ /*
+ * '+' is only allowed with numbers
+ */
+ if (!*place)
+ ++eoptind;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr,
+ "%s: illegal '+' delimiter with option -- %c\n",
+ p, eoptopt);
+ }
+ return (BADCH);
+ }
+ ++oli;
+ if ((*oli != ':') && (*oli != '?')) {
+ /*
+ * don't need argument
+ */
+ eoptarg = NULL;
+ if (!*place)
+ ++eoptind;
+ return (eoptopt);
+ }
+
+ if (*place) {
+ /*
+ * no white space
+ */
+ eoptarg = place;
+ } else if (*oli == '?') {
+ /*
+ * no arg, but NOT required
+ */
+ eoptarg = NULL;
+ } else if (nargc <= ++eoptind) {
+ /*
+ * no arg, but IS required
+ */
+ place = EMSG;
+ if (eopterr) {
+ if (!(p = strrchr(*nargv, '/')))
+ p = *nargv;
+ else
+ ++p;
+ (void)fprintf(stderr,
+ "%s: option requires an argument -- %c\n", p,
+ eoptopt);
+ }
+ return (BADCH);
+ } else {
+ /*
+ * arg has white space
+ */
+ eoptarg = nargv[eoptind];
+ }
+ place = EMSG;
+ ++eoptind;
+ return (eoptopt);
+}
diff --git a/usr.bin/pr/extern.h b/usr.bin/pr/extern.h
new file mode 100644
index 0000000..1456b55
--- /dev/null
+++ b/usr.bin/pr/extern.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+extern int eoptind;
+extern char *eoptarg;
+
+void addnum __P((char *, int, int));
+int egetopt __P((int, char * const *, const char *));
+void flsh_errs __P((void));
+int horzcol __P((int, char **));
+int inln __P((FILE *, char *, int, int *, int, int *));
+int inskip __P((FILE *, int, int));
+void mfail __P((void));
+int mulfile __P((int, char **));
+FILE *nxtfile __P((int, char **, char **, char *, int));
+int onecol __P((int, char **));
+int otln __P((char *, int, int *, int *, int));
+void pfail __P((void));
+int prhead __P((char *, char *, int));
+int prtail __P((int, int));
+int setup __P((int, char **));
+void terminate __P((int));
+void usage __P((void));
+int vertcol __P((int, char **));
diff --git a/usr.bin/pr/pr.1 b/usr.bin/pr/pr.1
new file mode 100644
index 0000000..d8d55be
--- /dev/null
+++ b/usr.bin/pr/pr.1
@@ -0,0 +1,347 @@
+.\" Copyright (c) 1991 Keith Muller.
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Keith Muller of the University of California, San Diego.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pr.1 8.3 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt PR 1
+.Os BSD 4.4
+.Sh NAME
+.Nm pr
+.Nd print files
+.Sh SYNOPSIS
+.Nm pr
+.Bk -words
+.Op Ar \&+page
+.Ek
+.Bk -words
+.Op Fl Ar column
+.Ek
+.Op Fl adFmrt
+.Bk -words
+.Oo
+.Op Fl e
+.Op Ar char
+.Op Ar gap
+.Oc
+.Ek
+.Bk -words
+.Op Fl h Ar header
+.Ek
+.Bk -words
+.Oo
+.Op Fl i
+.Op Ar char
+.Op Ar gap
+.Oc
+.Ek
+.Bk -words
+.Op Fl l Ar lines
+.Ek
+.Bk -words
+.Op Fl o Ar offset
+.Ek
+.Bk -words
+.Oo
+.Op Fl s
+.Op Ar char
+.Oc
+.Ek
+.Bk -words
+.Oo
+.Op Fl n
+.Op Ar char
+.Op Ar width
+.Oc
+.Ek
+.Bk -words
+.Op Fl w Ar width
+.Ek
+.Op -
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm pr
+utility is a printing and pagination filter for text files.
+When multiple input files are specified, each is read, formatted,
+and written to standard output.
+By default, the input is separated into 66-line pages, each with
+.sp
+.in +2
+.ti -2
+\(bu A 5-line header with the page number, date, time, and
+the pathname of the file.
+.sp
+.ti -2
+\(bu A 5-line trailer consisting of blank lines.
+.in -2
+.Pp
+If standard output is associated with a terminal,
+diagnostic messages are suppressed until the
+.Nm pr
+utility has completed processing.
+.Pp
+When multiple column output is specified,
+text columns are of equal width.
+By default text columns are separated by at least one
+.Em <blank>.
+Input lines that do not fit into a text column are truncated.
+Lines are not truncated under single column output.
+.Sh OPTIONS
+.Pp
+In the following option descriptions, column, lines, offset, page, and
+width are positive decimal integers and gap is a nonnegative decimal integer.
+.Bl -tag -width 4n
+.It Ar \&+page
+Begin output at page number
+.Ar page
+of the formatted input.
+.It Fl Ar column
+Produce output that is
+.Ar columns
+wide (default is 1) that is written vertically
+down each column in the order in which the text
+is received from the input file.
+The options
+.Fl e
+and
+.Fl i
+are assumed.
+This option should not be used with
+.Fl m .
+When used with
+.Fl t ,
+the minimum number of lines is used to display the output.
+.It Fl a
+Modify the effect of the
+.Fl column
+option so that the columns are filled across the page in a round-robin order
+(e.g., when column is 2, the first input line heads column
+1, the second heads column 2, the third is the second line
+in column 1, etc.).
+This option requires the use of the
+.Fl column
+option.
+.It Fl d
+Produce output that is double spaced. An extra
+.Em <newline>
+character is output following every <newline> found in the input.
+.It Fl e Ar \&[char\&]\&[gap\&]
+Expand each input <tab> to the next greater column
+position specified by the formula
+.Ar n*gap+1 ,
+where
+.Em n
+is an integer > 0.
+If
+.Ar gap
+is zero or is omitted the default is 8.
+All
+.Em <tab>
+characters in the input are expanded into the appropriate
+number of
+.Em <space>s .
+If any nondigit character,
+.Ar char ,
+is specified, it is used as the input tab character.
+.It Fl F
+Use a
+.Em <form-feed>
+character for new pages,
+instead of the default behavior that uses a
+sequence o
+.Em <newline>
+characters.
+.It Fl h Ar header
+.Ar header
+Use the string
+.Ar header
+to replace the
+.Ar file name
+in the header line.
+.It Fl i Ar \&[char\&]\&[gap\&]
+In output, replace multiple <space>s with <tab>s whenever two or more
+adjacent <space>s reach column positions
+.Ar gap+1 ,
+.Ar 2*gap+1 ,
+etc.
+If
+.Ar gap
+is zero or omitted, default
+.Em <tab>
+settings at every eighth column position
+is used.
+If any nondigit character,
+.Ar char ,
+is specified, it is used as the output
+.Em <tab>
+character.
+.It Fl l Ar lines
+Override the 66 line default and reset the page length to
+.Ar lines.
+If
+.Ar lines
+is not greater than the sum of both the header and trailer
+depths (in lines), the
+.Nm pr
+utility suppresses output of both the header and trailer, as if the
+.Fl t
+option were in effect.
+.It Fl m
+Merge the contents of multiple files.
+One line from each file specified by a file operand is
+written side by side into text columns of equal fixed widths, in
+terms of the number of column positions.
+The number of text columns depends on the number of
+file operands successfully opened.
+The maximum number of files merged depends on page width and the
+per process open file limit.
+The options
+.Fl e
+and
+.Fl i
+are assumed.
+.It Fl n Ar \&[char\&]\&[width\&]
+Provide
+.Ar width
+digit line numbering.
+The default for
+.Ar width ,
+if not specified, is 5.
+The number occupies the first
+.Ar width
+column positions of each text column or each line of
+.Fl m
+output.
+If
+.Ar char
+(any nondigit character) is given, it is appended to the line number to
+separate it from whatever follows. The default for
+.Ar char
+is a
+.Em <tab> .
+Line numbers longer than
+.Ar width
+columns are truncated.
+.It Fl o Ar offset
+Each line of output is preceded by
+.Ar offset
+.Em <spaces>s .
+If the
+.FL o
+option is not specified, the default is zero.
+The space taken is in addition to the output line width.
+.It Fl r
+Write no diagnostic reports on failure to open a file.
+.It Fl s Ar char
+Separate text columns by the single character
+.Ar char
+instead of by the appropriate number of
+.Em <space>s
+(default for
+.Ar char
+is the
+.Em <tab>
+character).
+.It Fl t
+Print neither the five-line identifying
+header nor the five-line trailer usually supplied for each page.
+Quit printing after the last line of each file without spacing to the
+end of the page.
+.It Fl w Ar width
+Set the width of the line to
+.Ar width
+column positions for multiple text-column output only.
+If the
+.Fl w
+option is not specified and the
+.Fl s
+option is not specified, the default width is 72.
+If the
+.Fl w
+option is not specified and the
+.Fl s
+option is specified, the default width is 512.
+.It Ar file
+A pathname of a file to be printed.
+If no
+.Ar file
+operands are specified, or if a
+.Ar file
+operand is
+.Sq Fl ,
+the standard input is used.
+The standard input is used only if no
+.Ar file
+operands are specified, or if a
+.Ar file
+operand is
+.Sq Fl .
+.El
+.Pp
+The
+.Fl s
+option does not allow the option letter to be separated from its
+argument, and the options
+.Fl e ,
+.Fl i ,
+and
+.Fl n
+require that both arguments, if present, not be separated from the option
+letter.
+.Sh ERRORS
+.Pp
+If
+.Nm pr
+receives an interrupt while printing to a terminal, it
+flushes all accumulated error messages to the screen before
+terminating.
+.Pp
+The
+.Nm pr
+utility exits 0 on success, and 1 if an error occurs.
+.Pp
+Error messages are written to standard error during the printing
+process (if output is redirected) or after all successful
+file printing is complete (when printing to a terminal).
+.Sh SEE ALSO
+.Xr cat 1 ,
+.Xr more 1
+.Sh STANDARDS
+The
+.Nm pr
+utility is
+.St -p1003.2
+compatible.
diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c
new file mode 100644
index 0000000..2d0eb45
--- /dev/null
+++ b/usr.bin/pr/pr.c
@@ -0,0 +1,1804 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)pr.c 8.2 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pr.h"
+#include "extern.h"
+
+/*
+ * pr: a printing and pagination filter. If multiple input files
+ * are specified, each is read, formatted, and written to standard
+ * output. By default, input is seperated into 66-line pages, each
+ * with a header that includes the page number, date, time and the
+ * files pathname.
+ *
+ * Complies with posix P1003.2/D11
+ */
+
+/*
+ * parameter variables
+ */
+int pgnm; /* starting page number */
+int clcnt; /* number of columns */
+int colwd; /* column data width - multiple columns */
+int across; /* mult col flag; write across page */
+int dspace; /* double space flag */
+char inchar; /* expand input char */
+int ingap; /* expand input gap */
+int formfeed; /* use formfeed as trailer */
+char *header; /* header name instead of file name */
+char ochar; /* contract output char */
+int ogap; /* contract output gap */
+int lines; /* number of lines per page */
+int merge; /* merge multiple files in output */
+char nmchar; /* line numbering append char */
+int nmwd; /* width of line number field */
+int offst; /* number of page offset spaces */
+int nodiag; /* do not report file open errors */
+char schar; /* text column separation character */
+int sflag; /* -s option for multiple columns */
+int nohead; /* do not write head and trailer */
+int pgwd; /* page width with multiple col output */
+char *timefrmt; /* time conversion string */
+
+/*
+ * misc globals
+ */
+FILE *err; /* error message file pointer */
+int addone; /* page length is odd with double space */
+int errcnt; /* error count on file processing */
+char digs[] = "0123456789"; /* page number translation map */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ret_val;
+
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, terminate);
+ ret_val = setup(argc, argv);
+ if (!ret_val) {
+ /*
+ * select the output format based on options
+ */
+ if (merge)
+ ret_val = mulfile(argc, argv);
+ else if (clcnt == 1)
+ ret_val = onecol(argc, argv);
+ else if (across)
+ ret_val = horzcol(argc, argv);
+ else
+ ret_val = vertcol(argc, argv);
+ } else
+ usage();
+ flsh_errs();
+ if (errcnt || ret_val)
+ exit(1);
+ return(0);
+}
+
+/*
+ * onecol: print files with only one column of output.
+ * Line length is unlimited.
+ */
+int
+onecol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int cnt = -1;
+ register int off;
+ register int lrgln;
+ register int linecnt;
+ register int num;
+ int lncnt;
+ int pagecnt;
+ int ips;
+ int ops;
+ int cps;
+ char *obuf;
+ char *lbuf;
+ char *nbuf;
+ char *hbuf;
+ char *ohbuf;
+ FILE *inf;
+ char *fname;
+ int mor;
+
+ if (nmwd)
+ num = nmwd + 1;
+ else
+ num = 0;
+ off = num + offst;
+
+ /*
+ * allocate line buffer
+ */
+ if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ /*
+ * allocate header buffer
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ ohbuf = hbuf + offst;
+ nbuf = obuf + offst;
+ lbuf = nbuf + num;
+ if (num)
+ nbuf[--num] = nmchar;
+ if (offst) {
+ (void)memset(obuf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ /*
+ * skip to specified page
+ */
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ linecnt = 0;
+ lrgln = 0;
+ ops = 0;
+ ips = 0;
+ cps = 0;
+
+ /*
+ * loop by line
+ */
+ while (linecnt < lines) {
+ /*
+ * input next line
+ */
+ if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
+ break;
+ if (!linecnt && !nohead &&
+ prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * start of new line.
+ */
+ if (!lrgln) {
+ if (num)
+ addnum(nbuf, num, ++lncnt);
+ if (otln(obuf,cnt+off, &ips, &ops, mor))
+ return(1);
+ } else if (otln(lbuf, cnt, &ips, &ops, mor))
+ return(1);
+
+ /*
+ * if line bigger than buffer, get more
+ */
+ if (mor) {
+ lrgln = 1;
+ continue;
+ }
+
+ /*
+ * whole line rcvd. reset tab proc. state
+ */
+ ++linecnt;
+ lrgln = 0;
+ ops = 0;
+ ips = 0;
+ }
+
+ /*
+ * fill to end of page
+ */
+ if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
+ return(1);
+
+ /*
+ * On EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * vertcol: print files with more than one column of output down a page
+ */
+int
+vertcol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *ptbf;
+ register char **lstdat;
+ register int i;
+ register int j;
+ register int cnt = -1;
+ register int pln;
+ register int *indy;
+ int cvc;
+ int *lindy;
+ int lncnt;
+ int stp;
+ int pagecnt;
+ int col = colwd + 1;
+ int mxlen = pgwd + offst + 1;
+ int mclcnt = clcnt - 1;
+ struct vcol *vc;
+ int mvc;
+ int tvc;
+ int cw = nmwd + 1;
+ int fullcol;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ char *fname;
+ FILE *inf;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ /*
+ * allocate page buffer
+ */
+ if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * allocate page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+ if (offst)
+ (void)memset(hbuf, (int)' ', offst);
+
+ /*
+ * col pointers when no headers
+ */
+ mvc = lines * clcnt;
+ if ((vc =
+ (struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * pointer into page where last data per line is located
+ */
+ if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){
+ mfail();
+ return(1);
+ }
+
+ /*
+ * fast index lookups to locate start of lines
+ */
+ if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
+ mfail();
+ return(1);
+ }
+ if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ if (nmwd)
+ fullcol = col + cw;
+ else
+ fullcol = col;
+
+ /*
+ * initialize buffer lookup indexes and offset area
+ */
+ for (j = 0; j < lines; ++j) {
+ lindy[j] = j * mxlen;
+ indy[j] = lindy[j] + offst;
+ if (offst) {
+ ptbf = buf + lindy[j];
+ (void)memset(ptbf, (int)' ', offst);
+ ptbf += offst;
+ } else
+ ptbf = buf + indy[j];
+ lstdat[j] = ptbf;
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ /*
+ * skip to requested page
+ */
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ /*
+ * loop by column
+ */
+ cvc = 0;
+ for (i = 0; i < clcnt; ++i) {
+ j = 0;
+ /*
+ * if last column, do not pad
+ */
+ if (i == mclcnt)
+ stp = 1;
+ else
+ stp = 0;
+ /*
+ * loop by line
+ */
+ for(;;) {
+ /*
+ * is this first column
+ */
+ if (!i) {
+ ptbf = buf + indy[j];
+ lstdat[j] = ptbf;
+ } else
+ ptbf = lstdat[j];
+ vc[cvc].pt = ptbf;
+
+ /*
+ * add number
+ */
+ if (nmwd) {
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+
+ /*
+ * input next line
+ */
+ cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
+ vc[cvc++].cnt = cnt;
+ if (cnt < 0)
+ break;
+ ptbf += cnt;
+
+ /*
+ * pad all but last column on page
+ */
+ if (!stp) {
+ /*
+ * pad to end of column
+ */
+ if (sflag)
+ *ptbf++ = schar;
+ else if ((pln = col-cnt) > 0) {
+ (void)memset(ptbf,
+ (int)' ',pln);
+ ptbf += pln;
+ }
+ }
+ /*
+ * remember last char in line
+ */
+ lstdat[j] = ptbf;
+ if (++j >= lines)
+ break;
+ }
+ if (cnt < 0)
+ break;
+ }
+
+ /*
+ * when -t (no header) is specified the spec requires
+ * the min number of lines. The last page may not have
+ * balanced length columns. To fix this we must reorder
+ * the columns. This is a very slow technique so it is
+ * only used under limited conditions. Without -t, the
+ * balancing of text columns is unspecified. To NOT
+ * balance the last page, add the global variable
+ * nohead to the if statement below e.g.
+ *
+ * if ((cnt < 0) && nohead && cvc ......
+ */
+ --cvc;
+
+ /*
+ * check to see if last page needs to be reordered
+ */
+ if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
+ pln = cvc/clcnt;
+ if (cvc % clcnt)
+ ++pln;
+
+ /*
+ * print header
+ */
+ if (!nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+ for (i = 0; i < pln; ++i) {
+ ips = 0;
+ ops = 0;
+ if (offst&& otln(buf,offst,&ips,&ops,1))
+ return(1);
+ tvc = i;
+
+ for (j = 0; j < clcnt; ++j) {
+ /*
+ * determine column length
+ */
+ if (j == mclcnt) {
+ /*
+ * last column
+ */
+ cnt = vc[tvc].cnt;
+ if (nmwd)
+ cnt += cw;
+ } else if (sflag) {
+ /*
+ * single ch between
+ */
+ cnt = vc[tvc].cnt + 1;
+ if (nmwd)
+ cnt += cw;
+ } else
+ cnt = fullcol;
+ if (otln(vc[tvc].pt, cnt, &ips,
+ &ops, 1))
+ return(1);
+ tvc += pln;
+ if (tvc >= cvc)
+ break;
+ }
+ /*
+ * terminate line
+ */
+ if (otln(buf, 0, &ips, &ops, 0))
+ return(1);
+ }
+ /*
+ * pad to end of page
+ */
+ if (prtail((lines - pln), 0))
+ return(1);
+ /*
+ * done with output, go to next file
+ */
+ break;
+ }
+
+ /*
+ * determine how many lines to output
+ */
+ if (i > 0)
+ pln = lines;
+ else
+ pln = j;
+
+ /*
+ * print header
+ */
+ if (pln && !nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * output each line
+ */
+ for (i = 0; i < pln; ++i) {
+ ptbf = buf + lindy[i];
+ if ((j = lstdat[i] - ptbf) <= offst)
+ break;
+ if (otln(ptbf, j, &ips, &ops, 0))
+ return(1);
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (pln && prtail((lines - pln), 0))
+ return(1);
+
+ /*
+ * if EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * horzcol: print files with more than one column of output across a page
+ */
+int
+horzcol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *ptbf;
+ register int pln;
+ register int cnt = -1;
+ register char *lstdat;
+ register int col = colwd + 1;
+ register int j;
+ register int i;
+ int lncnt;
+ int pagecnt;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ char *fname;
+ FILE *inf;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+ if (offst) {
+ (void)memset(buf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+
+ /*
+ * loop by file
+ */
+ while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
+ if (pgnm) {
+ if (inskip(inf, pgnm, lines))
+ continue;
+ pagecnt = pgnm;
+ } else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * loop by page
+ */
+ for(;;) {
+ /*
+ * loop by line
+ */
+ for (i = 0; i < lines; ++i) {
+ ptbf = buf + offst;
+ lstdat = ptbf;
+ j = 0;
+ /*
+ * loop by col
+ */
+ for(;;) {
+ if (nmwd) {
+ /*
+ * add number to column
+ */
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+ /*
+ * input line
+ */
+ if ((cnt = inln(inf,ptbf,colwd,&cps,1,
+ &mor)) < 0)
+ break;
+ ptbf += cnt;
+ lstdat = ptbf;
+
+ /*
+ * if last line skip padding
+ */
+ if (++j >= clcnt)
+ break;
+
+ /*
+ * pad to end of column
+ */
+ if (sflag)
+ *ptbf++ = schar;
+ else if ((pln = col - cnt) > 0) {
+ (void)memset(ptbf,(int)' ',pln);
+ ptbf += pln;
+ }
+ }
+
+ /*
+ * determine line length
+ */
+ if ((j = lstdat - buf) <= offst)
+ break;
+ if (!i && !nohead &&
+ prhead(hbuf, fname, pagecnt))
+ return(1);
+ /*
+ * output line
+ */
+ if (otln(buf, j, &ips, &ops, 0))
+ return(1);
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (i && prtail(lines-i, 0))
+ return(1);
+
+ /*
+ * if EOF go to next file
+ */
+ if (cnt < 0)
+ break;
+ ++pagecnt;
+ }
+ if (inf != stdin)
+ (void)fclose(inf);
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * mulfile: print files with more than one column of output and
+ * more than one file concurrently
+ */
+int
+mulfile(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *ptbf;
+ register int j;
+ register int pln;
+ register int cnt;
+ register char *lstdat;
+ register int i;
+ FILE **fbuf;
+ int actf;
+ int lncnt;
+ int col;
+ int pagecnt;
+ int fproc;
+ char *buf;
+ char *hbuf;
+ char *ohbuf;
+ char *fname;
+ int ips = 0;
+ int cps = 0;
+ int ops = 0;
+ int mor = 0;
+
+ /*
+ * array of FILE *, one for each operand
+ */
+ if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) {
+ mfail();
+ return(1);
+ }
+
+ /*
+ * page header
+ */
+ if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ ohbuf = hbuf + offst;
+
+ /*
+ * do not know how many columns yet. The number of operands provide an
+ * upper bound on the number of columns. We use the number of files
+ * we can open successfully to set the number of columns. The operation
+ * of the merge operation (-m) in relation to unsuccesful file opens
+ * is unspecified by posix.
+ */
+ j = 0;
+ while (j < clcnt) {
+ if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
+ break;
+ if (pgnm && (inskip(fbuf[j], pgnm, lines)))
+ fbuf[j] = NULL;
+ ++j;
+ }
+
+ /*
+ * if no files, exit
+ */
+ if (!j)
+ return(1);
+
+ /*
+ * calculate page boundries based on open file count
+ */
+ clcnt = j;
+ if (nmwd) {
+ colwd = (pgwd - clcnt - nmwd)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
+ } else {
+ colwd = (pgwd + 1 - clcnt)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - 1;
+ }
+ if (colwd < 1) {
+ (void)fprintf(err,
+ "pr: page width too small for %d columns\n", clcnt);
+ return(1);
+ }
+ actf = clcnt;
+ col = colwd + 1;
+
+ /*
+ * line buffer
+ */
+ if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
+ mfail();
+ return(1);
+ }
+ if (offst) {
+ (void)memset(buf, (int)' ', offst);
+ (void)memset(hbuf, (int)' ', offst);
+ }
+ if (pgnm)
+ pagecnt = pgnm;
+ else
+ pagecnt = 1;
+ lncnt = 0;
+
+ /*
+ * continue to loop while any file still has data
+ */
+ while (actf > 0) {
+ /*
+ * loop by line
+ */
+ for (i = 0; i < lines; ++i) {
+ ptbf = buf + offst;
+ lstdat = ptbf;
+ if (nmwd) {
+ /*
+ * add line number to line
+ */
+ addnum(ptbf, nmwd, ++lncnt);
+ ptbf += nmwd;
+ *ptbf++ = nmchar;
+ }
+ j = 0;
+ fproc = 0;
+
+ /*
+ * loop by column
+ */
+ for (j = 0; j < clcnt; ++j) {
+ if (fbuf[j] == NULL) {
+ /*
+ * empty column; EOF
+ */
+ cnt = 0;
+ } else if ((cnt = inln(fbuf[j], ptbf, colwd,
+ &cps, 1, &mor)) < 0) {
+ /*
+ * EOF hit; no data
+ */
+ if (fbuf[j] != stdin)
+ (void)fclose(fbuf[j]);
+ fbuf[j] = NULL;
+ --actf;
+ cnt = 0;
+ } else {
+ /*
+ * process file data
+ */
+ ptbf += cnt;
+ lstdat = ptbf;
+ fproc++;
+ }
+
+ /*
+ * if last ACTIVE column, done with line
+ */
+ if (fproc >= actf)
+ break;
+
+ /*
+ * pad to end of column
+ */
+ if (sflag) {
+ *ptbf++ = schar;
+ } else if ((pln = col - cnt) > 0) {
+ (void)memset(ptbf, (int)' ', pln);
+ ptbf += pln;
+ }
+ }
+
+ /*
+ * calculate data in line
+ */
+ if ((j = lstdat - buf) <= offst)
+ break;
+
+ if (!i && !nohead && prhead(hbuf, fname, pagecnt))
+ return(1);
+
+ /*
+ * output line
+ */
+ if (otln(buf, j, &ips, &ops, 0))
+ return(1);
+
+ /*
+ * if no more active files, done
+ */
+ if (actf <= 0) {
+ ++i;
+ break;
+ }
+ }
+
+ /*
+ * pad to end of page
+ */
+ if (i && prtail(lines-i, 0))
+ return(1);
+ ++pagecnt;
+ }
+ if (eoptind < argc)
+ return(1);
+ return(0);
+}
+
+/*
+ * inln(): input a line of data (unlimited length lines supported)
+ * Input is optionally expanded to spaces
+ *
+ * inf: file
+ * buf: buffer
+ * lim: buffer length
+ * cps: column positon 1st char in buffer (large line support)
+ * trnc: throw away data more than lim up to \n
+ * mor: set if more data in line (not truncated)
+ */
+int
+inln(inf, buf, lim, cps, trnc, mor)
+ FILE *inf;
+ char *buf;
+ register int lim;
+ int *cps;
+ int trnc;
+ int *mor;
+{
+ register int col;
+ register int gap = ingap;
+ register int ch = EOF;
+ register char *ptbuf;
+ register int chk = (int)inchar;
+
+ ptbuf = buf;
+
+ if (gap) {
+ /*
+ * expanding input option
+ */
+ while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
+ /*
+ * is this the input "tab" char
+ */
+ if (ch == chk) {
+ /*
+ * expand to number of spaces
+ */
+ col = (ptbuf - buf) + *cps;
+ col = gap - (col % gap);
+
+ /*
+ * if more than this line, push back
+ */
+ if ((col > lim) && (ungetc(ch, inf) == EOF))
+ return(1);
+
+ /*
+ * expand to spaces
+ */
+ while ((--col >= 0) && (--lim >= 0))
+ *ptbuf++ = ' ';
+ continue;
+ }
+ if (ch == '\n')
+ break;
+ *ptbuf++ = ch;
+ }
+ } else {
+ /*
+ * no expansion
+ */
+ while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
+ if (ch == '\n')
+ break;
+ *ptbuf++ = ch;
+ }
+ }
+ col = ptbuf - buf;
+ if (ch == EOF) {
+ *mor = 0;
+ *cps = 0;
+ if (!col)
+ return(-1);
+ return(col);
+ }
+ if (ch == '\n') {
+ /*
+ * entire line processed
+ */
+ *mor = 0;
+ *cps = 0;
+ return(col);
+ }
+
+ /*
+ * line was larger than limit
+ */
+ if (trnc) {
+ /*
+ * throw away rest of line
+ */
+ while ((ch = getc(inf)) != EOF) {
+ if (ch == '\n')
+ break;
+ }
+ *cps = 0;
+ *mor = 0;
+ } else {
+ /*
+ * save column offset if not truncated
+ */
+ *cps += col;
+ *mor = 1;
+ }
+
+ return(col);
+}
+
+/*
+ * otln(): output a line of data. (Supports unlimited length lines)
+ * output is optionally contracted to tabs
+ *
+ * buf: output buffer with data
+ * cnt: number of chars of valid data in buf
+ * svips: buffer input column position (for large lines)
+ * svops: buffer output column position (for large lines)
+ * mor: output line not complete in this buf; more data to come.
+ * 1 is more, 0 is complete, -1 is no \n's
+ */
+int
+otln(buf, cnt, svips, svops, mor)
+ register char *buf;
+ int cnt;
+ int *svops;
+ int *svips;
+ int mor;
+{
+ register int ops; /* last col output */
+ register int ips; /* last col in buf examined */
+ register int gap = ogap;
+ register int tbps;
+ register char *endbuf;
+
+ if (ogap) {
+ /*
+ * contracting on output
+ */
+ endbuf = buf + cnt;
+ ops = *svops;
+ ips = *svips;
+ while (buf < endbuf) {
+ /*
+ * count number of spaces and ochar in buffer
+ */
+ if (*buf == ' ') {
+ ++ips;
+ ++buf;
+ continue;
+ }
+
+ /*
+ * simulate ochar processing
+ */
+ if (*buf == ochar) {
+ ips += gap - (ips % gap);
+ ++buf;
+ continue;
+ }
+
+ /*
+ * got a non space char; contract out spaces
+ */
+ while (ops < ips) {
+ /*
+ * use as many ochar as will fit
+ */
+ if ((tbps = ops + gap - (ops % gap)) > ips)
+ break;
+ if (putchar(ochar) == EOF) {
+ pfail();
+ return(1);
+ }
+ ops = tbps;
+ }
+
+ while (ops < ips) {
+ /*
+ * finish off with spaces
+ */
+ if (putchar(' ') == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ops;
+ }
+
+ /*
+ * output non space char
+ */
+ if (putchar(*buf++) == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ips;
+ ++ops;
+ }
+
+ if (mor > 0) {
+ /*
+ * if incomplete line, save position counts
+ */
+ *svops = ops;
+ *svips = ips;
+ return(0);
+ }
+
+ if (mor < 0) {
+ while (ops < ips) {
+ /*
+ * use as many ochar as will fit
+ */
+ if ((tbps = ops + gap - (ops % gap)) > ips)
+ break;
+ if (putchar(ochar) == EOF) {
+ pfail();
+ return(1);
+ }
+ ops = tbps;
+ }
+ while (ops < ips) {
+ /*
+ * finish off with spaces
+ */
+ if (putchar(' ') == EOF) {
+ pfail();
+ return(1);
+ }
+ ++ops;
+ }
+ return(0);
+ }
+ } else {
+ /*
+ * output is not contracted
+ */
+ if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
+ pfail();
+ return(1);
+ }
+ if (mor != 0)
+ return(0);
+ }
+
+ /*
+ * process line end and double space as required
+ */
+ if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
+ pfail();
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * inskip(): skip over pgcnt pages with lncnt lines per page
+ * file is closed at EOF (if not stdin).
+ *
+ * inf FILE * to read from
+ * pgcnt number of pages to skip
+ * lncnt number of lines per page
+ */
+int
+inskip(inf, pgcnt, lncnt)
+ FILE *inf;
+ register int pgcnt;
+ register int lncnt;
+{
+ register int c;
+ register int cnt;
+
+ while(--pgcnt > 0) {
+ cnt = lncnt;
+ while ((c = getc(inf)) != EOF) {
+ if ((c == '\n') && (--cnt == 0))
+ break;
+ }
+ if (c == EOF) {
+ if (inf != stdin)
+ (void)fclose(inf);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * nxtfile: returns a FILE * to next file in arg list and sets the
+ * time field for this file (or current date).
+ *
+ * buf array to store proper date for the header.
+ * dt if set skips the date processing (used with -m)
+ */
+FILE *
+nxtfile(argc, argv, fname, buf, dt)
+ int argc;
+ char **argv;
+ char **fname;
+ char *buf;
+ int dt;
+{
+ FILE *inf = NULL;
+ struct timeval tv;
+ struct timezone tz;
+ struct tm *timeptr = NULL;
+ struct stat statbuf;
+ static int twice = -1;
+
+ ++twice;
+ if (eoptind >= argc) {
+ /*
+ * no file listed; default, use standard input
+ */
+ if (twice)
+ return(NULL);
+ clearerr(stdin);
+ inf = stdin;
+ if (header != NULL)
+ *fname = header;
+ else
+ *fname = FNAME;
+ if (nohead)
+ return(inf);
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err, "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ eoptind = argc - 1;
+ return(NULL);
+ }
+ timeptr = localtime(&(tv.tv_sec));
+ }
+ for (; eoptind < argc; ++eoptind) {
+ if (strcmp(argv[eoptind], "-") == 0) {
+ /*
+ * process a "-" for filename
+ */
+ clearerr(stdin);
+ inf = stdin;
+ if (header != NULL)
+ *fname = header;
+ else
+ *fname = FNAME;
+ ++eoptind;
+ if (nohead || (dt && twice))
+ return(inf);
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err,
+ "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ return(NULL);
+ }
+ timeptr = localtime(&(tv.tv_sec));
+ } else {
+ /*
+ * normal file processing
+ */
+ if ((inf = fopen(argv[eoptind], "r")) == NULL) {
+ ++errcnt;
+ if (nodiag)
+ continue;
+ (void)fprintf(err, "pr: Cannot open %s, %s\n",
+ argv[eoptind], strerror(errno));
+ continue;
+ }
+ if (header != NULL)
+ *fname = header;
+ else if (dt)
+ *fname = FNAME;
+ else
+ *fname = argv[eoptind];
+ ++eoptind;
+ if (nohead || (dt && twice))
+ return(inf);
+
+ if (dt) {
+ if (gettimeofday(&tv, &tz) < 0) {
+ ++errcnt;
+ (void)fprintf(err,
+ "pr: cannot get time of day, %s\n",
+ strerror(errno));
+ return(NULL);
+ }
+ timeptr = localtime(&(tv.tv_sec));
+ } else {
+ if (fstat(fileno(inf), &statbuf) < 0) {
+ ++errcnt;
+ (void)fclose(inf);
+ (void)fprintf(err,
+ "pr: Cannot stat %s, %s\n",
+ argv[eoptind], strerror(errno));
+ return(NULL);
+ }
+ timeptr = localtime(&(statbuf.st_mtime));
+ }
+ }
+ break;
+ }
+ if (inf == NULL)
+ return(NULL);
+
+ /*
+ * set up time field used in header
+ */
+ if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
+ ++errcnt;
+ if (inf != stdin)
+ (void)fclose(inf);
+ (void)fputs("pr: time conversion failed\n", err);
+ return(NULL);
+ }
+ return(inf);
+}
+
+/*
+ * addnum(): adds the line number to the column
+ * Truncates from the front or pads with spaces as required.
+ * Numbers are right justified.
+ *
+ * buf buffer to store the number
+ * wdth width of buffer to fill
+ * line line number
+ *
+ * NOTE: numbers occupy part of the column. The posix
+ * spec does not specify if -i processing should or should not
+ * occur on number padding. The spec does say it occupies
+ * part of the column. The usage of addnum currently treats
+ * numbers as part of the column so spaces may be replaced.
+ */
+void
+addnum(buf, wdth, line)
+ register char *buf;
+ register int wdth;
+ register int line;
+{
+ register char *pt = buf + wdth;
+
+ do {
+ *--pt = digs[line % 10];
+ line /= 10;
+ } while (line && (pt > buf));
+
+ /*
+ * pad with space as required
+ */
+ while (pt > buf)
+ *--pt = ' ';
+}
+
+/*
+ * prhead(): prints the top of page header
+ *
+ * buf buffer with time field (and offset)
+ * cnt number of chars in buf
+ * fname fname field for header
+ * pagcnt page number
+ */
+int
+prhead(buf, fname, pagcnt)
+ char *buf;
+ char *fname;
+ int pagcnt;
+{
+ int ips = 0;
+ int ops = 0;
+
+ if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
+ pfail();
+ return(1);
+ }
+ /*
+ * posix is not clear if the header is subject to line length
+ * restrictions. The specification for header line format
+ * in the spec clearly does not limit length. No pr currently
+ * restricts header length. However if we need to truncate in
+ * an reasonable way, adjust the length of the printf by
+ * changing HDFMT to allow a length max as an arguement printf.
+ * buf (which contains the offset spaces and time field could
+ * also be trimmed
+ *
+ * note only the offset (if any) is processed for tab expansion
+ */
+ if (offst && otln(buf, offst, &ips, &ops, -1))
+ return(1);
+ (void)printf(HDFMT,buf+offst, fname, pagcnt);
+ return(0);
+}
+
+/*
+ * prtail(): pad page with empty lines (if required) and print page trailer
+ * if requested
+ *
+ * cnt number of lines of padding needed
+ * incomp was a '\n' missing from last line output
+ */
+int
+prtail(cnt, incomp)
+ register int cnt;
+ int incomp;
+{
+ if (nohead) {
+ /*
+ * only pad with no headers when incomplete last line
+ */
+ if (!incomp)
+ return(0);
+ if ((dspace && (putchar('\n') == EOF)) ||
+ (putchar('\n') == EOF)) {
+ pfail();
+ return(1);
+ }
+ return(0);
+ }
+
+ /*
+ * if double space output two \n
+ */
+ if (dspace)
+ cnt *= 2;
+
+ /*
+ * if an odd number of lines per page, add an extra \n
+ */
+ if (addone)
+ ++cnt;
+
+ /*
+ * pad page
+ */
+ if (formfeed) {
+ if ((incomp && (putchar('\n') == EOF)) ||
+ (putchar('\f') == EOF)) {
+ pfail();
+ return(1);
+ }
+ return(0);
+ }
+ cnt += TAILLEN;
+ while (--cnt >= 0) {
+ if (putchar('\n') == EOF) {
+ pfail();
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/*
+ * terminate(): when a SIGINT is recvd
+ */
+void
+terminate(which_sig)
+ int which_sig;
+{
+ flsh_errs();
+ exit(1);
+}
+
+
+/*
+ * flsh_errs(): output saved up diagnostic messages after all normal
+ * processing has completed
+ */
+void
+flsh_errs()
+{
+ char buf[BUFSIZ];
+
+ (void)fflush(stdout);
+ (void)fflush(err);
+ if (err == stderr)
+ return;
+ rewind(err);
+ while (fgets(buf, BUFSIZ, err) != NULL)
+ (void)fputs(buf, stderr);
+}
+
+void
+mfail()
+{
+ (void)fputs("pr: memory allocation failed\n", err);
+}
+
+void
+pfail()
+{
+ (void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
+}
+
+void
+usage()
+{
+ (void)fputs(
+ "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n",err);
+ (void)fputs(
+ " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err);
+ (void)fputs(
+ " [-s[ch]] [-w width] [-] [file ...]\n", err);
+}
+
+/*
+ * setup: Validate command args, initialize and perform sanity
+ * checks on options
+ */
+int
+setup(argc, argv)
+ register int argc;
+ register char **argv;
+{
+ register int c;
+ int eflag = 0;
+ int iflag = 0;
+ int wflag = 0;
+ int cflag = 0;
+
+ if (isatty(fileno(stdout))) {
+ /*
+ * defer diagnostics until processing is done
+ */
+ if ((err = tmpfile()) == NULL) {
+ (void)fputs("Cannot defer diagnostic messages\n",stderr);
+ return(1);
+ }
+ } else
+ err = stderr;
+ while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?w:")) != EOF) {
+ switch (c) {
+ case '+':
+ if ((pgnm = atoi(eoptarg)) < 1) {
+ (void)fputs("pr: +page number must be 1 or more\n",
+ err);
+ return(1);
+ }
+ break;
+ case '-':
+ if ((clcnt = atoi(eoptarg)) < 1) {
+ (void)fputs("pr: -columns must be 1 or more\n",err);
+ return(1);
+ }
+ if (clcnt > 1)
+ ++cflag;
+ break;
+ case 'a':
+ ++across;
+ break;
+ case 'd':
+ ++dspace;
+ break;
+ case 'e':
+ ++eflag;
+ if ((eoptarg != NULL) && !isdigit(*eoptarg))
+ inchar = *eoptarg++;
+ else
+ inchar = INCHAR;
+ if ((eoptarg != NULL) && isdigit(*eoptarg)) {
+ if ((ingap = atoi(eoptarg)) < 0) {
+ (void)fputs(
+ "pr: -e gap must be 0 or more\n", err);
+ return(1);
+ }
+ if (ingap == 0)
+ ingap = INGAP;
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -e %s\n", eoptarg);
+ return(1);
+ } else
+ ingap = INGAP;
+ break;
+ case 'F':
+ ++formfeed;
+ break;
+ case 'h':
+ header = eoptarg;
+ break;
+ case 'i':
+ ++iflag;
+ if ((eoptarg != NULL) && !isdigit(*eoptarg))
+ ochar = *eoptarg++;
+ else
+ ochar = OCHAR;
+ if ((eoptarg != NULL) && isdigit(*eoptarg)) {
+ if ((ogap = atoi(eoptarg)) < 0) {
+ (void)fputs(
+ "pr: -i gap must be 0 or more\n", err);
+ return(1);
+ }
+ if (ogap == 0)
+ ogap = OGAP;
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -i %s\n", eoptarg);
+ return(1);
+ } else
+ ogap = OGAP;
+ break;
+ case 'l':
+ if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
+ (void)fputs(
+ "pr: Number of lines must be 1 or more\n",err);
+ return(1);
+ }
+ break;
+ case 'm':
+ ++merge;
+ break;
+ case 'n':
+ if ((eoptarg != NULL) && !isdigit(*eoptarg))
+ nmchar = *eoptarg++;
+ else
+ nmchar = NMCHAR;
+ if ((eoptarg != NULL) && isdigit(*eoptarg)) {
+ if ((nmwd = atoi(eoptarg)) < 1) {
+ (void)fputs(
+ "pr: -n width must be 1 or more\n",err);
+ return(1);
+ }
+ } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
+ (void)fprintf(err,
+ "pr: invalid value for -n %s\n", eoptarg);
+ return(1);
+ } else
+ nmwd = NMWD;
+ break;
+ case 'o':
+ if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){
+ (void)fputs("pr: -o offset must be 1 or more\n",
+ err);
+ return(1);
+ }
+ break;
+ case 'r':
+ ++nodiag;
+ break;
+ case 's':
+ ++sflag;
+ if (eoptarg == NULL)
+ schar = SCHAR;
+ else
+ schar = *eoptarg++;
+ if (*eoptarg != '\0') {
+ (void)fprintf(err,
+ "pr: invalid value for -s %s\n", eoptarg);
+ return(1);
+ }
+ break;
+ case 't':
+ ++nohead;
+ break;
+ case 'w':
+ ++wflag;
+ if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
+ (void)fputs(
+ "pr: -w width must be 1 or more \n",err);
+ return(1);
+ }
+ break;
+ case '?':
+ default:
+ return(1);
+ }
+ }
+
+ /*
+ * default and sanity checks
+ */
+ if (!clcnt) {
+ if (merge) {
+ if ((clcnt = argc - eoptind) <= 1) {
+ clcnt = CLCNT;
+ merge = 0;
+ }
+ } else
+ clcnt = CLCNT;
+ }
+ if (across) {
+ if (clcnt == 1) {
+ (void)fputs("pr: -a flag requires multiple columns\n",
+ err);
+ return(1);
+ }
+ if (merge) {
+ (void)fputs("pr: -m cannot be used with -a\n", err);
+ return(1);
+ }
+ }
+ if (!wflag) {
+ if (sflag)
+ pgwd = SPGWD;
+ else
+ pgwd = PGWD;
+ }
+ if (cflag || merge) {
+ if (!eflag) {
+ inchar = INCHAR;
+ ingap = INGAP;
+ }
+ if (!iflag) {
+ ochar = OCHAR;
+ ogap = OGAP;
+ }
+ }
+ if (cflag) {
+ if (merge) {
+ (void)fputs(
+ "pr: -m cannot be used with multiple columns\n", err);
+ return(1);
+ }
+ if (nmwd) {
+ colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
+ pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
+ } else {
+ colwd = (pgwd + 1 - clcnt)/clcnt;
+ pgwd = ((colwd + 1) * clcnt) - 1;
+ }
+ if (colwd < 1) {
+ (void)fprintf(err,
+ "pr: page width is too small for %d columns\n",clcnt);
+ return(1);
+ }
+ }
+ if (!lines)
+ lines = LINES;
+
+ /*
+ * make sure long enough for headers. if not disable
+ */
+ if (lines <= HEADLEN + TAILLEN)
+ ++nohead;
+ else if (!nohead)
+ lines -= HEADLEN + TAILLEN;
+
+ /*
+ * adjust for double space on odd length pages
+ */
+ if (dspace) {
+ if (lines == 1)
+ dspace = 0;
+ else {
+ if (lines & 1)
+ ++addone;
+ lines /= 2;
+ }
+ }
+
+ if ((timefrmt = getenv("LC_TIME")) == NULL)
+ timefrmt = TIMEFMT;
+ return(0);
+}
diff --git a/usr.bin/pr/pr.h b/usr.bin/pr/pr.h
new file mode 100644
index 0000000..d020e9f
--- /dev/null
+++ b/usr.bin/pr/pr.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1991 Keith Muller.
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pr.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * parameter defaults
+ */
+#define CLCNT 1
+#define INCHAR '\t'
+#define INGAP 8
+#define OCHAR '\t'
+#define OGAP 8
+#define LINES 66
+#define NMWD 5
+#define NMCHAR '\t'
+#define SCHAR '\t'
+#define PGWD 72
+#define SPGWD 512
+
+/*
+ * misc default values
+ */
+#define HDFMT "%s %s Page %d\n\n\n"
+#define HEADLEN 5
+#define TAILLEN 5
+#define TIMEFMT "%b %e %H:%M %Y"
+#define FNAME ""
+#define LBUF 8192
+#define HDBUF 512
+
+/*
+ * structure for vertical columns. Used to balance cols on last page
+ */
+struct vcol {
+ char *pt; /* ptr to col */
+ int cnt; /* char count */
+};
diff --git a/usr.bin/printenv/Makefile b/usr.bin/printenv/Makefile
new file mode 100644
index 0000000..94975bd
--- /dev/null
+++ b/usr.bin/printenv/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= printenv
+MLINKS= printenv.1 env.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/printenv/printenv.1 b/usr.bin/printenv/printenv.1
new file mode 100644
index 0000000..77d78f8
--- /dev/null
+++ b/usr.bin/printenv/printenv.1
@@ -0,0 +1,98 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)printenv.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PRINTENV 1
+.Os BSD 3
+.Sh NAME
+.Nm printenv , env
+.Nd print out the environment, set and print environment
+.Sh SYNOPSIS
+.Nm printenv
+.Op Ar name
+.Nm env
+.Op Fl
+.Op Ar name=value ...
+.Op Ar command
+.Sh DESCRIPTION
+.Nm Printenv
+prints out the names and values of the variables in the environment,
+with one name/value pair per line. If
+.Ar name
+is specified, only
+its value is printed.
+.Pp
+If a
+.Ar name
+is specified and it is not defined in the environment,
+.Nm printenv
+returns exit status 1, else it returns status 0.
+.Pp
+.Nm Env
+executes
+.Ar command
+after modifying the environment as
+specified on the command line. The option
+.Ar name=value
+specifies
+an environmental variable,
+.Ar name ,
+with a value of
+.Ar value .
+The option
+.Sq Fl
+causes
+.Nm env
+to completely ignore the environment
+it inherits.
+.Pp
+If no command is specified,
+.Nm env
+prints out the names and values
+of the variables in the environment, with one name/value pair per line.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr execvp 3 ,
+.Xr environ 7
+.Sh HISTORY
+The
+.Nm printenv
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+.Nm Env
+doesn't handle commands with equal (``='') signs in their
+names, for obvious reasons.
diff --git a/usr.bin/printenv/printenv.c b/usr.bin/printenv/printenv.c
new file mode 100644
index 0000000..121cd2f
--- /dev/null
+++ b/usr.bin/printenv/printenv.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)printenv.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void usage __P((void));
+
+/*
+ * printenv
+ *
+ * Bill Joy, UCB
+ * February, 1979
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char **environ;
+ register char *cp, **ep;
+ register size_t len;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ for (ep = environ; *ep; ep++)
+ (void)printf("%s\n", *ep);
+ exit(0);
+ }
+ len = strlen(*argv);
+ for (ep = environ; *ep; ep++)
+ if (!memcmp(*ep, *argv, len)) {
+ cp = *ep + len;
+ if (!*cp || *cp == '=') {
+ (void)printf("%s\n", *cp ? cp + 1 : cp);
+ exit(0);
+ }
+ }
+ exit(1);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: printenv [name]\n");
+ exit(1);
+}
diff --git a/usr.bin/printf/Makefile b/usr.bin/printf/Makefile
new file mode 100644
index 0000000..52b20f4
--- /dev/null
+++ b/usr.bin/printf/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= printf
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/printf/printf.1 b/usr.bin/printf/printf.1
new file mode 100644
index 0000000..4954158
--- /dev/null
+++ b/usr.bin/printf/printf.1
@@ -0,0 +1,272 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)printf.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PRINTF 1
+.Os
+.Sh NAME
+.Nm printf
+.Nd formatted output
+.Sh SYNOPSIS
+.Nm printf format
+.Op arguments ...
+.Sh DESCRIPTION
+.Nm Printf
+formats and prints its arguments, after the first, under control
+of the
+.Ar format .
+The
+.Ar format
+is a character string which contains three types of objects: plain characters,
+which are simply copied to standard output, character escape sequences which
+are converted and copied to the standard output, and format specifications,
+each of which causes printing of the next successive
+.Ar argument .
+.Pp
+The
+.Ar arguments
+after the first are treated as strings if the corresponding format is
+either
+.Cm c
+or
+.Cm s ;
+otherwise it is evaluated as a C constant, with the following extensions:
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+A leading plus or minus sign is allowed.
+.It
+If the leading character is a single or double quote, or not a digit,
+plus, or minus sign, the value is the ASCII code of the next character.
+.El
+.Pp
+The format string is reused as often as necessary to satisfy the
+.Ar arguments .
+Any extra format specifications are evaluated with zero or the null
+string.
+.Pp
+Character escape sequences are in backslash notation as defined in the
+draft proposed
+.Tn ANSI C
+Standard
+.Tn X3J11 .
+The characters and their meanings
+are as follows:
+.Bl -tag -width Ds -offset indent
+.It Cm \ea
+Write a <bell> character.
+.It Cm \eb
+Write a <backspace> character.
+.It Cm \ef
+Write a <form-feed> character.
+.It Cm \en
+Write a <new-line> character.
+.It Cm \er
+Write a <carriage return> character.
+.It Cm \et
+Write a <tab> character.
+.It Cm \ev
+Write a <vertical tab> character.
+.It Cm \e\'
+Write a <single quote> character.
+.It Cm \e\e
+Write a backslash character.
+.It Cm \e Ns Ar num
+Write an 8-bit character whose
+.Tn ASCII
+value is the 1-, 2-, or 3-digit
+octal number
+.Ar num .
+.El
+.Pp
+Each format specification is introduced by the percent character
+(``%'').
+The remainder of the format specification includes,
+in the following order:
+.Bl -tag -width Ds
+.It "Zero or more of the following flags:"
+.Bl -tag -width Ds
+.It Cm #
+A `#' character
+specifying that the value should be printed in an ``alternate form''.
+For
+.Cm c ,
+.Cm d ,
+and
+.Cm s ,
+formats, this option has no effect. For the
+.Cm o
+formats the precision of the number is increased to force the first
+character of the output string to a zero. For the
+.Cm x
+.Pq Cm X
+format, a non-zero result has the string
+.Li 0x
+.Pq Li 0X
+prepended to it. For
+.Cm e ,
+.Cm E ,
+.Cm f ,
+.Cm g ,
+and
+.Cm G ,
+formats, the result will always contain a decimal point, even if no
+digits follow the point (normally, a decimal point only appears in the
+results of those formats if a digit follows the decimal point). For
+.Cm g
+and
+.Cm G
+formats, trailing zeros are not removed from the result as they
+would otherwise be;
+.It Cm \&\-
+A minus sign `\-' which specifies
+.Em left adjustment
+of the output in the indicated field;
+.It Cm \&+
+A `+' character specifying that there should always be
+a sign placed before the number when using signed formats.
+.It Sq \&\ \&
+A space specifying that a blank should be left before a positive number
+for a signed format. A `+' overrides a space if both are used;
+.It Cm \&0
+A zero `0' character indicating that zero-padding should be used
+rather than blank-padding. A `\-' overrides a `0' if both are used;
+.El
+.It "Field Width:"
+An optional digit string specifying a
+.Em field width ;
+if the output string has fewer characters than the field width it will
+be blank-padded on the left (or right, if the left-adjustment indicator
+has been given) to make up the field width (note that a leading zero
+is a flag, but an embedded zero is part of a field width);
+.It Precision:
+An optional period,
+.Sq Cm \&.\& ,
+followed by an optional digit string giving a
+.Em precision
+which specifies the number of digits to appear after the decimal point,
+for
+.Cm e
+and
+.Cm f
+formats, or the maximum number of characters to be printed
+from a string; if the digit string is missing, the precision is treated
+as zero;
+.It Format:
+A character which indicates the type of format to use (one of
+.Cm diouxXfwEgGcs ) .
+.El
+.Pp
+A field width or precision may be
+.Sq Cm \&*
+instead of a digit string.
+In this case an
+.Ar argument
+supplies the field width or precision.
+.Pp
+The format characters and their meanings are:
+.Bl -tag -width Fl
+.It Cm diouXx
+The
+.Ar argument
+is printed as a signed decimal (d or i), unsigned decimal, unsigned octal,
+or unsigned hexadecimal (X or x), respectively.
+.It Cm f
+The
+.Ar argument
+is printed in the style `[\-]ddd.ddd' where the number of d's
+after the decimal point is equal to the precision specification for
+the argument.
+If the precision is missing, 6 digits are given; if the precision
+is explicitly 0, no digits and no decimal point are printed.
+.It Cm eE
+The
+.Ar argument
+is printed in the style
+.Cm e
+.`[-]d.ddd Ns \(+-dd\'
+where there
+is one digit before the decimal point and the number after is equal to
+the precision specification for the argument; when the precision is
+missing, 6 digits are produced.
+An upper-case E is used for an `E' format.
+.It Cm gG
+The
+.Ar argument
+is printed in style
+.Cm f
+or in style
+.Cm e
+.Pq Cm E
+whichever gives full precision in minimum space.
+.It Cm c
+The first character of
+.Ar argument
+is printed.
+.It Cm s
+Characters from the string
+.Ar argument
+are printed until the end is reached or until the number of characters
+indicated by the precision specification is reached; however if the
+precision is 0 or missing, all characters in the string are printed.
+.It Cm \&%
+Print a `%'; no argument is used.
+.El
+.Pp
+In no case does a non-existent or small field width cause truncation of
+a field; padding takes place only if the specified field width exceeds
+the actual width.
+.Sh RETURN VALUES
+.Nm Printf
+exits 0 on success, 1 on failure.
+.Sh SEE ALSO
+.Xr printf 3
+.Sh HISTORY
+The
+.Nm printf
+command appeared in
+.Bx 4.3 Reno .
+It is modeled
+after the standard library function,
+.Xr printf 3 .
+.Sh BUGS
+Since the floating point numbers are translated from
+.Tn ASCII
+to floating-point and
+then back again, floating-point precision may be lost.
+.Pp
+.Tn ANSI
+hexadecimal character constants were deliberately not provided.
diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c
new file mode 100644
index 0000000..9c8e9e0
--- /dev/null
+++ b/usr.bin/printf/printf.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(BUILTIN) && !defined(SHELL)
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+#endif
+
+#ifndef lint
+static char sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#ifdef SHELL
+#define EOF -1
+#else
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * XXX
+ * This *has* to go away. TK.
+ */
+#ifdef SHELL
+#define main printfcmd
+#define warnx(a, b, c) { \
+ char buf[64]; \
+ (void)sprintf(buf, sizeof(buf), a, b, c); \
+ error(buf); \
+}
+#include "../../bin/sh/bltin/bltin.h"
+#endif
+
+#define PF(f, func) { \
+ if (fieldwidth) \
+ if (precision) \
+ (void)printf(f, fieldwidth, precision, func); \
+ else \
+ (void)printf(f, fieldwidth, func); \
+ else if (precision) \
+ (void)printf(f, precision, func); \
+ else \
+ (void)printf(f, func); \
+}
+
+static int asciicode __P((void));
+static void escape __P((char *));
+static int getchr __P((void));
+static double getdouble __P((void));
+static int getint __P((int *));
+static int getlong __P((long *));
+static char *getstr __P((void));
+static char *mklong __P((char *, int));
+static void usage __P((void));
+
+static char **gargv;
+
+int
+#ifdef BUILTIN
+progprintf(argc, argv)
+#else
+main(argc, argv)
+#endif
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ static char *skip1, *skip2;
+ int ch, end, fieldwidth, precision;
+ char convch, nextch, *format, *fmt, *start;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ return (1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ return (1);
+ }
+
+ /*
+ * Basic algorithm is to scan the format string for conversion
+ * specifications -- once one is found, find out if the field
+ * width or precision is a '*'; if it is, gather up value. Note,
+ * format strings are reused as necessary to use up the provided
+ * arguments, arguments of zero/null string are provided to use
+ * up the format string.
+ */
+ skip1 = "#-+ 0";
+ skip2 = "0123456789";
+
+ escape(fmt = format = *argv); /* backslash interpretation */
+ gargv = ++argv;
+ for (;;) {
+ end = 0;
+ /* find next format specification */
+next: for (start = fmt;; ++fmt) {
+ if (!*fmt) {
+ /* avoid infinite loop */
+ if (end == 1) {
+ warnx("missing format character",
+ NULL, NULL);
+ return (1);
+ }
+ end = 1;
+ if (fmt > start)
+ (void)printf("%s", start);
+ if (!*gargv)
+ return (0);
+ fmt = format;
+ goto next;
+ }
+ /* %% prints a % */
+ if (*fmt == '%') {
+ if (*++fmt != '%')
+ break;
+ *fmt++ = '\0';
+ (void)printf("%s", start);
+ goto next;
+ }
+ }
+
+ /* skip to field width */
+ for (; strchr(skip1, *fmt); ++fmt);
+ if (*fmt == '*') {
+ if (getint(&fieldwidth))
+ return (1);
+ ++fmt;
+ } else {
+ fieldwidth = 0;
+
+ /* skip to possible '.', get following precision */
+ for (; strchr(skip2, *fmt); ++fmt);
+ }
+ if (*fmt == '.') {
+ /* precision present? */
+ ++fmt;
+ if (*fmt == '*') {
+ if (getint(&precision))
+ return (1);
+ ++fmt;
+ } else {
+ precision = 0;
+
+ /* skip to conversion char */
+ for (; strchr(skip2, *fmt); ++fmt);
+ }
+ } else
+ precision = 0;
+ if (!*fmt) {
+ warnx("missing format character", NULL, NULL);
+ return (1);
+ }
+
+ convch = *fmt;
+ nextch = *++fmt;
+ *fmt = '\0';
+ switch(convch) {
+ case 'c': {
+ char p;
+
+ p = getchr();
+ PF(start, p);
+ break;
+ }
+ case 's': {
+ char *p;
+
+ p = getstr();
+ PF(start, p);
+ break;
+ }
+ case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
+ long p;
+ char *f;
+
+ if ((f = mklong(start, convch)) == NULL)
+ return (1);
+ if (getlong(&p))
+ return (1);
+ PF(f, p);
+ break;
+ }
+ case 'e': case 'E': case 'f': case 'g': case 'G': {
+ double p;
+
+ p = getdouble();
+ PF(start, p);
+ break;
+ }
+ default:
+ warnx("illegal format character %c", convch, NULL);
+ return (1);
+ }
+ *fmt = nextch;
+ }
+ /* NOTREACHED */
+}
+
+static char *
+mklong(str, ch)
+ char *str;
+ int ch;
+{
+ static char copy[64];
+ int len;
+
+ len = strlen(str) + 2;
+ memmove(copy, str, len - 3);
+ copy[len - 3] = 'l';
+ copy[len - 2] = ch;
+ copy[len - 1] = '\0';
+ return (copy);
+}
+
+static void
+escape(fmt)
+ register char *fmt;
+{
+ register char *store;
+ register int value, c;
+
+ for (store = fmt; c = *fmt; ++fmt, ++store) {
+ if (c != '\\') {
+ *store = c;
+ continue;
+ }
+ switch (*++fmt) {
+ case '\0': /* EOS, user error */
+ *store = '\\';
+ *++store = '\0';
+ return;
+ case '\\': /* backslash */
+ case '\'': /* single quote */
+ *store = *fmt;
+ break;
+ case 'a': /* bell/alert */
+ *store = '\7';
+ break;
+ case 'b': /* backspace */
+ *store = '\b';
+ break;
+ case 'f': /* form-feed */
+ *store = '\f';
+ break;
+ case 'n': /* newline */
+ *store = '\n';
+ break;
+ case 'r': /* carriage-return */
+ *store = '\r';
+ break;
+ case 't': /* horizontal tab */
+ *store = '\t';
+ break;
+ case 'v': /* vertical tab */
+ *store = '\13';
+ break;
+ /* octal constant */
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ for (c = 3, value = 0;
+ c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
+ value <<= 3;
+ value += *fmt - '0';
+ }
+ --fmt;
+ *store = value;
+ break;
+ default:
+ *store = *fmt;
+ break;
+ }
+ }
+ *store = '\0';
+}
+
+static int
+getchr()
+{
+ if (!*gargv)
+ return ('\0');
+ return ((int)**gargv++);
+}
+
+static char *
+getstr()
+{
+ if (!*gargv)
+ return ("");
+ return (*gargv++);
+}
+
+static char *Number = "+-.0123456789";
+static int
+getint(ip)
+ int *ip;
+{
+ long val;
+
+ if (getlong(&val))
+ return (1);
+ if (val > INT_MAX) {
+ warnx("%s: %s", *gargv, strerror(ERANGE));
+ return (1);
+ }
+ *ip = val;
+ return (0);
+}
+
+static int
+getlong(lp)
+ long *lp;
+{
+ long val;
+ char *ep;
+
+ if (!*gargv) {
+ *lp = 0;
+ return (0);
+ }
+ if (strchr(Number, **gargv)) {
+ errno = 0;
+ val = strtol(*gargv, &ep, 0);
+ if (*ep != '\0') {
+ warnx("%s: illegal number", *gargv, NULL);
+ return (1);
+ }
+ if (errno == ERANGE)
+ if (val == LONG_MAX) {
+ warnx("%s: %s", *gargv, strerror(ERANGE));
+ return (1);
+ }
+ if (val == LONG_MIN) {
+ warnx("%s: %s", *gargv, strerror(ERANGE));
+ return (1);
+ }
+
+ *lp = val;
+ ++gargv;
+ return (0);
+ }
+ *lp = (long)asciicode();
+ return (0);
+}
+
+static double
+getdouble()
+{
+ if (!*gargv)
+ return ((double)0);
+ if (strchr(Number, **gargv))
+ return (atof(*gargv++));
+ return ((double)asciicode());
+}
+
+static int
+asciicode()
+{
+ register int ch;
+
+ ch = **gargv;
+ if (ch == '\'' || ch == '"')
+ ch = (*gargv)[1];
+ ++gargv;
+ return (ch);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: printf format [arg ...]\n");
+}
diff --git a/usr.bin/quota/Makefile b/usr.bin/quota/Makefile
new file mode 100644
index 0000000..2ee365e
--- /dev/null
+++ b/usr.bin/quota/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= quota
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/quota/quota.1 b/usr.bin/quota/quota.1
new file mode 100644
index 0000000..e87a3a4
--- /dev/null
+++ b/usr.bin/quota/quota.1
@@ -0,0 +1,131 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)quota.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt QUOTA 1
+.Os BSD 4.2
+.Sh NAME
+.Nm quota
+.Nd display disk usage and limits
+.Sh SYNOPSIS
+.Nm quota
+.Op Fl g
+.Op Fl u
+.Op Fl v | Fl q
+.Nm quota
+.Op Fl u
+.Op Fl v | Fl q
+.Ar user
+.Nm quota
+.Op Fl g
+.Op Fl v | Fl q
+.Ar group
+.Sh DESCRIPTION
+.Nm Quota
+displays users' disk usage and limits.
+By default only the user quotas are printed.
+.Pp
+Options:
+.Pp
+.Bl -tag -width Ds
+.It Fl g
+Print group quotas for the group
+of which the user is a member.
+The optional
+.Fl u
+flag is equivalent to the default.
+.It Fl v
+.Nm quota
+will display quotas on filesystems
+where no storage is allocated.
+.It Fl q
+Print a more terse message,
+containing only information
+on filesystems where usage is over quota.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+displays both the user quotas and the group quotas (for
+the user).
+.Pp
+Only the super-user may use the
+.Fl u
+flag and the optional
+.Ar user
+argument to view the limits of other users.
+Non-super-users can use the the
+.Fl g
+flag and optional
+.Ar group
+argument to view only the limits of groups of which they are members.
+.Pp
+The
+.Fl q
+flag takes precedence over the
+.Fl v
+flag.
+.Pp
+.Nm Quota
+reports the quotas of all the filesystems listed in
+.Pa /etc/fstab .
+If
+.Nm quota
+exits with a non-zero status, one or more filesystems
+are over quota.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+located at the filesystem root with user quotas
+.It Pa quota.group
+located at the filesystem root with group quotas
+.It Pa /etc/fstab
+to find filesystem names and locations
+.El
+.Sh HISTORY
+The
+.Nm quota
+command appeared in
+.Bx 4.2 .
+.Sh SEE ALSO
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c
new file mode 100644
index 0000000..8140ebb
--- /dev/null
+++ b/usr.bin/quota/quota.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)quota.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Disk quota reporting program.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <ufs/ufs/quota.h>
+#include <stdio.h>
+#include <fstab.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+struct quotause {
+ struct quotause *next;
+ long flags;
+ struct dqblk dqblk;
+ char fsname[MAXPATHLEN + 1];
+} *getprivs();
+#define FOUND 0x01
+
+int qflag;
+int vflag;
+
+main(argc, argv)
+ char *argv[];
+{
+ int ngroups, gidset[NGROUPS];
+ int i, gflag = 0, uflag = 0;
+ char ch;
+ extern char *optarg;
+ extern int optind, errno;
+
+ if (quotactl("/", 0, 0, (caddr_t)0) < 0 && errno == EOPNOTSUPP) {
+ fprintf(stderr, "There are no quotas on this system\n");
+ exit(0);
+ }
+ while ((ch = getopt(argc, argv, "ugvq")) != EOF) {
+ switch(ch) {
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'q':
+ qflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (!uflag && !gflag)
+ uflag++;
+ if (argc == 0) {
+ if (uflag)
+ showuid(getuid());
+ if (gflag) {
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ exit(1);
+ }
+ for (i = 1; i < ngroups; i++)
+ showgid(gidset[i]);
+ }
+ exit(0);
+ }
+ if (uflag && gflag)
+ usage();
+ if (uflag) {
+ for (; argc > 0; argc--, argv++) {
+ if (alldigits(*argv))
+ showuid(atoi(*argv));
+ else
+ showusrname(*argv);
+ }
+ exit(0);
+ }
+ if (gflag) {
+ for (; argc > 0; argc--, argv++) {
+ if (alldigits(*argv))
+ showgid(atoi(*argv));
+ else
+ showgrpname(*argv);
+ }
+ exit(0);
+ }
+}
+
+usage()
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "Usage: quota [-guqv]",
+ "\tquota [-qv] -u username ...",
+ "\tquota [-qv] -g groupname ...");
+ exit(1);
+}
+
+/*
+ * Print out quotas for a specified user identifier.
+ */
+showuid(uid)
+ u_long uid;
+{
+ struct passwd *pwd = getpwuid(uid);
+ u_long myuid;
+ char *name;
+
+ if (pwd == NULL)
+ name = "(no account)";
+ else
+ name = pwd->pw_name;
+ myuid = getuid();
+ if (uid != myuid && myuid != 0) {
+ printf("quota: %s (uid %lu): permission denied\n", name, uid);
+ return;
+ }
+ showquotas(USRQUOTA, uid, name);
+}
+
+/*
+ * Print out quotas for a specifed user name.
+ */
+showusrname(name)
+ char *name;
+{
+ struct passwd *pwd = getpwnam(name);
+ u_long myuid;
+
+ if (pwd == NULL) {
+ fprintf(stderr, "quota: %s: unknown user\n", name);
+ return;
+ }
+ myuid = getuid();
+ if (pwd->pw_uid != myuid && myuid != 0) {
+ fprintf(stderr, "quota: %s (uid %d): permission denied\n",
+ name, pwd->pw_uid);
+ return;
+ }
+ showquotas(USRQUOTA, pwd->pw_uid, name);
+}
+
+/*
+ * Print out quotas for a specified group identifier.
+ */
+showgid(gid)
+ u_long gid;
+{
+ struct group *grp = getgrgid(gid);
+ int ngroups, gidset[NGROUPS];
+ register int i;
+ char *name;
+
+ if (grp == NULL)
+ name = "(no entry)";
+ else
+ name = grp->gr_name;
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ return;
+ }
+ for (i = 1; i < ngroups; i++)
+ if (gid == gidset[i])
+ break;
+ if (i >= ngroups && getuid() != 0) {
+ fprintf(stderr, "quota: %s (gid %lu): permission denied\n",
+ name, gid);
+ return;
+ }
+ showquotas(GRPQUOTA, gid, name);
+}
+
+/*
+ * Print out quotas for a specifed group name.
+ */
+showgrpname(name)
+ char *name;
+{
+ struct group *grp = getgrnam(name);
+ int ngroups, gidset[NGROUPS];
+ register int i;
+
+ if (grp == NULL) {
+ fprintf(stderr, "quota: %s: unknown group\n", name);
+ return;
+ }
+ ngroups = getgroups(NGROUPS, gidset);
+ if (ngroups < 0) {
+ perror("quota: getgroups");
+ return;
+ }
+ for (i = 1; i < ngroups; i++)
+ if (grp->gr_gid == gidset[i])
+ break;
+ if (i >= ngroups && getuid() != 0) {
+ fprintf(stderr, "quota: %s (gid %d): permission denied\n",
+ name, grp->gr_gid);
+ return;
+ }
+ showquotas(GRPQUOTA, grp->gr_gid, name);
+}
+
+showquotas(type, id, name)
+ int type;
+ u_long id;
+ char *name;
+{
+ register struct quotause *qup;
+ struct quotause *quplist, *getprivs();
+ char *msgi, *msgb, *timeprt();
+ int myuid, fd, lines = 0;
+ static int first;
+ static time_t now;
+
+ if (now == 0)
+ time(&now);
+ quplist = getprivs(id, type);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (!vflag &&
+ qup->dqblk.dqb_isoftlimit == 0 &&
+ qup->dqblk.dqb_ihardlimit == 0 &&
+ qup->dqblk.dqb_bsoftlimit == 0 &&
+ qup->dqblk.dqb_bhardlimit == 0)
+ continue;
+ msgi = (char *)0;
+ if (qup->dqblk.dqb_ihardlimit &&
+ qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit)
+ msgi = "File limit reached on";
+ else if (qup->dqblk.dqb_isoftlimit &&
+ qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
+ if (qup->dqblk.dqb_itime > now)
+ msgi = "In file grace period on";
+ else
+ msgi = "Over file quota on";
+ msgb = (char *)0;
+ if (qup->dqblk.dqb_bhardlimit &&
+ qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit)
+ msgb = "Block limit reached on";
+ else if (qup->dqblk.dqb_bsoftlimit &&
+ qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
+ if (qup->dqblk.dqb_btime > now)
+ msgb = "In block grace period on";
+ else
+ msgb = "Over block quota on";
+ if (qflag) {
+ if ((msgi != (char *)0 || msgb != (char *)0) &&
+ lines++ == 0)
+ heading(type, id, name, "");
+ if (msgi != (char *)0)
+ printf("\t%s %s\n", msgi, qup->fsname);
+ if (msgb != (char *)0)
+ printf("\t%s %s\n", msgb, qup->fsname);
+ continue;
+ }
+ if (vflag ||
+ qup->dqblk.dqb_curblocks ||
+ qup->dqblk.dqb_curinodes) {
+ if (lines++ == 0)
+ heading(type, id, name, "");
+ printf("%15s%8lu%c%7lu%8lu%8s"
+ , qup->fsname
+ , (u_long)
+ (dbtob(qup->dqblk.dqb_curblocks) / 1024)
+ , (msgb == (char *)0) ? ' ' : '*'
+ , (u_long)
+ (dbtob(qup->dqblk.dqb_bsoftlimit) / 1024)
+ , (u_long)
+ (dbtob(qup->dqblk.dqb_bhardlimit) / 1024)
+ , (msgb == (char *)0) ? ""
+ : timeprt(qup->dqblk.dqb_btime));
+ printf("%8lu%c%7lu%8lu%8s\n"
+ , qup->dqblk.dqb_curinodes
+ , (msgi == (char *)0) ? ' ' : '*'
+ , qup->dqblk.dqb_isoftlimit
+ , qup->dqblk.dqb_ihardlimit
+ , (msgi == (char *)0) ? ""
+ : timeprt(qup->dqblk.dqb_itime)
+ );
+ continue;
+ }
+ }
+ if (!qflag && lines == 0)
+ heading(type, id, name, "none");
+}
+
+heading(type, id, name, tag)
+ int type;
+ u_long id;
+ char *name, *tag;
+{
+
+ printf("Disk quotas for %s %s (%cid %lu): %s\n", qfextension[type],
+ name, *qfextension[type], id, tag);
+ if (!qflag && tag[0] == '\0') {
+ printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n"
+ , "Filesystem"
+ , "blocks"
+ , "quota"
+ , "limit"
+ , "grace"
+ , "files"
+ , "quota"
+ , "limit"
+ , "grace"
+ );
+ }
+}
+
+/*
+ * Calculate the grace period and return a printable string for it.
+ */
+char *
+timeprt(seconds)
+ time_t seconds;
+{
+ time_t hours, minutes;
+ static char buf[20];
+ static time_t now;
+
+ if (now == 0)
+ time(&now);
+ if (now > seconds)
+ return ("none");
+ seconds -= now;
+ minutes = (seconds + 30) / 60;
+ hours = (minutes + 30) / 60;
+ if (hours >= 36) {
+ sprintf(buf, "%lddays", (hours + 12) / 24);
+ return (buf);
+ }
+ if (minutes >= 60) {
+ sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60);
+ return (buf);
+ }
+ sprintf(buf, "%2ld", minutes);
+ return (buf);
+}
+
+/*
+ * Collect the requested quota information.
+ */
+struct quotause *
+getprivs(id, quotatype)
+ register long id;
+ int quotatype;
+{
+ register struct fstab *fs;
+ register struct quotause *qup, *quptail;
+ struct quotause *quphead;
+ char *qfpathname;
+ int qcmd, fd;
+
+ setfsent();
+ quphead = (struct quotause *)0;
+ qcmd = QCMD(Q_GETQUOTA, quotatype);
+ while (fs = getfsent()) {
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (!hasquota(fs, quotatype, &qfpathname))
+ continue;
+ if ((qup = (struct quotause *)malloc(sizeof *qup)) == NULL) {
+ fprintf(stderr, "quota: out of memory\n");
+ exit(2);
+ }
+ if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ perror(qfpathname);
+ free(qup);
+ continue;
+ }
+ lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET);
+ switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
+ case 0: /* EOF */
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero((caddr_t)&qup->dqblk,
+ sizeof(struct dqblk));
+ break;
+
+ case sizeof(struct dqblk): /* OK */
+ break;
+
+ default: /* ERROR */
+ fprintf(stderr, "quota: read error");
+ perror(qfpathname);
+ close(fd);
+ free(qup);
+ continue;
+ }
+ close(fd);
+ }
+ strcpy(qup->fsname, fs->fs_file);
+ if (quphead == NULL)
+ quphead = qup;
+ else
+ quptail->next = qup;
+ quptail = qup;
+ qup->next = 0;
+ }
+ endfsent();
+ return (quphead);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp, *index(), *strtok();
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if (cp = index(opt, '='))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+alldigits(s)
+ register char *s;
+{
+ register c;
+
+ c = *s++;
+ do {
+ if (!isdigit(c))
+ return (0);
+ } while (c = *s++);
+ return (1);
+}
diff --git a/usr.bin/ranlib/Makefile b/usr.bin/ranlib/Makefile
new file mode 100644
index 0000000..a6382a6
--- /dev/null
+++ b/usr.bin/ranlib/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ranlib
+SRCS= archive.c build.c misc.c ranlib.c touch.c
+CFLAGS+=-I${.CURDIR} -I${.CURDIR}/../ar
+MAN1= ranlib.1
+MAN5= ranlib.5
+VPATH= ${.CURDIR}/../ar
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ranlib/build.c b/usr.bin/ranlib/build.c
new file mode 100644
index 0000000..ff745be
--- /dev/null
+++ b/usr.bin/ranlib/build.c
@@ -0,0 +1,288 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)build.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#include <a.out.h>
+#include <ar.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ranlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "archive.h"
+
+extern CHDR chdr; /* converted header */
+extern char *archive; /* archive name */
+extern char *tname; /* temporary file "name" */
+
+typedef struct _rlib {
+ struct _rlib *next; /* next structure */
+ off_t pos; /* offset of defining archive file */
+ char *sym; /* symbol */
+ int symlen; /* strlen(sym) */
+} RLIB;
+RLIB *rhead, **pnext;
+
+FILE *fp;
+
+long symcnt; /* symbol count */
+long tsymlen; /* total string length */
+
+static void rexec __P((int, int));
+static void symobj __P((void));
+
+build()
+{
+ CF cf;
+ int afd, tfd;
+ off_t size;
+
+ afd = open_archive(O_RDWR);
+ fp = fdopen(afd, "r+");
+ tfd = tmp();
+
+ SETCF(afd, archive, tfd, tname, RPAD|WPAD);
+
+ /* Read through the archive, creating list of symbols. */
+ pnext = &rhead;
+ symcnt = tsymlen = 0;
+ while(get_arobj(afd)) {
+ if (!strcmp(chdr.name, RANLIBMAG)) {
+ skip_arobj(afd);
+ continue;
+ }
+ rexec(afd, tfd);
+ put_arobj(&cf, (struct stat *)NULL);
+ }
+ *pnext = NULL;
+
+ /* Create the symbol table. */
+ symobj();
+
+ /* Copy the saved objects into the archive. */
+ size = lseek(tfd, (off_t)0, SEEK_CUR);
+ (void)lseek(tfd, (off_t)0, SEEK_SET);
+ SETCF(tfd, tname, afd, archive, RPAD|WPAD);
+ copy_ar(&cf, size);
+ (void)ftruncate(afd, lseek(afd, (off_t)0, SEEK_CUR));
+ (void)close(tfd);
+
+ /* Set the time. */
+ settime(afd);
+ close_archive(afd);
+ return(0);
+}
+
+/*
+ * rexec
+ * Read the exec structure; ignore any files that don't look
+ * exactly right.
+ */
+static void
+rexec(rfd, wfd)
+ register int rfd;
+ int wfd;
+{
+ register RLIB *rp;
+ register long nsyms;
+ register int nr, symlen;
+ register char *strtab, *sym;
+ struct exec ebuf;
+ struct nlist nl;
+ off_t r_off, w_off;
+ long strsize;
+ void *emalloc();
+
+ /* Get current offsets for original and tmp files. */
+ r_off = lseek(rfd, (off_t)0, SEEK_CUR);
+ w_off = lseek(wfd, (off_t)0, SEEK_CUR);
+
+ /* Read in exec structure. */
+ nr = read(rfd, &ebuf, sizeof(struct exec));
+ if (nr != sizeof(struct exec))
+ goto badread;
+
+ /* Check magic number and symbol count. */
+ if (N_BADMAG(ebuf) || ebuf.a_syms == 0)
+ goto bad1;
+
+ /* Seek to string table. */
+ if (lseek(rfd, r_off + N_STROFF(ebuf), SEEK_SET) == (off_t)-1)
+ error(archive);
+
+ /* Read in size of the string table. */
+ nr = read(rfd, &strsize, sizeof(strsize));
+ if (nr != sizeof(strsize))
+ goto badread;
+
+ /* Read in the string table. */
+ strsize -= sizeof(strsize);
+ strtab = emalloc(strsize);
+ nr = read(rfd, strtab, strsize);
+ if (nr != strsize) {
+badread: if (nr < 0)
+ error(archive);
+ goto bad2;
+ }
+
+ /* Seek to symbol table. */
+ if (fseek(fp, (long)r_off + N_SYMOFF(ebuf), SEEK_SET))
+ goto bad2;
+
+ /* For each symbol read the nlist entry and save it as necessary. */
+ nsyms = ebuf.a_syms / sizeof(struct nlist);
+ while (nsyms--) {
+ if (!fread(&nl, sizeof(struct nlist), 1, fp)) {
+ if (feof(fp))
+ badfmt();
+ error(archive);
+ }
+
+ /* Ignore if no name or local. */
+ if (!nl.n_un.n_strx || !(nl.n_type & N_EXT))
+ continue;
+
+ /*
+ * If the symbol is an undefined external and the n_value
+ * field is non-zero, keep it.
+ */
+ if ((nl.n_type & N_TYPE) == N_UNDF && !nl.n_value)
+ continue;
+
+ /* First four bytes are the table size. */
+ sym = strtab + nl.n_un.n_strx - sizeof(long);
+ symlen = strlen(sym) + 1;
+
+ rp = (RLIB *)emalloc(sizeof(RLIB));
+ rp->sym = (char *)emalloc(symlen);
+ bcopy(sym, rp->sym, symlen);
+ rp->symlen = symlen;
+ rp->pos = w_off;
+
+ /* Build in forward order for "ar -m" command. */
+ *pnext = rp;
+ pnext = &rp->next;
+
+ ++symcnt;
+ tsymlen += symlen;
+ }
+
+bad2: free(strtab);
+bad1: (void)lseek(rfd, r_off, SEEK_SET);
+}
+
+/*
+ * symobj --
+ * Write the symbol table into the archive, computing offsets as
+ * writing.
+ */
+static void
+symobj()
+{
+ register RLIB *rp, *rpnext;
+ struct ranlib rn;
+ off_t ransize;
+ long size, stroff;
+ char hb[sizeof(struct ar_hdr) + 1], pad;
+
+ /* Rewind the archive, leaving the magic number. */
+ if (fseek(fp, (long)SARMAG, SEEK_SET))
+ error(archive);
+
+ /* Size of the ranlib archive file, pad if necessary. */
+ ransize = sizeof(long) +
+ symcnt * sizeof(struct ranlib) + sizeof(long) + tsymlen;
+ if (ransize & 01) {
+ ++ransize;
+ pad = '\n';
+ } else
+ pad = '\0';
+
+ /* Put out the ranlib archive file header. */
+#define DEFMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+ (void)sprintf(hb, HDR2, RANLIBMAG, 0L, getuid(), getgid(),
+ DEFMODE & ~umask(0), ransize, ARFMAG);
+ if (!fwrite(hb, sizeof(struct ar_hdr), 1, fp))
+ error(tname);
+
+ /* First long is the size of the ranlib structure section. */
+ size = symcnt * sizeof(struct ranlib);
+ if (!fwrite(&size, sizeof(size), 1, fp))
+ error(tname);
+
+ /* Offset of the first archive file. */
+ size = SARMAG + sizeof(struct ar_hdr) + ransize;
+
+ /*
+ * Write out the ranlib structures. The offset into the string
+ * table is cumulative, the offset into the archive is the value
+ * set in rexec() plus the offset to the first archive file.
+ */
+ for (rp = rhead, stroff = 0; rp; rp = rp->next) {
+ rn.ran_un.ran_strx = stroff;
+ stroff += rp->symlen;
+ rn.ran_off = size + rp->pos;
+ if (!fwrite(&rn, sizeof(struct ranlib), 1, fp))
+ error(archive);
+ }
+
+ /* Second long is the size of the string table. */
+ if (!fwrite(&tsymlen, sizeof(tsymlen), 1, fp))
+ error(tname);
+
+ /* Write out the string table. */
+ for (rp = rhead; rp; rp = rpnext) {
+ if (!fwrite(rp->sym, rp->symlen, 1, fp))
+ error(tname);
+ rpnext = rp->next;
+ free(rp->sym);
+ free(rp);
+ }
+ rhead = NULL;
+
+ if (pad && !fwrite(&pad, sizeof(pad), 1, fp))
+ error(tname);
+
+ (void)fflush(fp);
+}
diff --git a/usr.bin/ranlib/misc.c b/usr.bin/ranlib/misc.c
new file mode 100644
index 0000000..5d1fe8f
--- /dev/null
+++ b/usr.bin/ranlib/misc.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
+
+extern char *archive; /* archive name */
+char *tname = "temporary file"; /* temporary file "name" */
+
+tmp()
+{
+ sigset_t set, oset;
+ int fd;
+ char *envtmp, path[MAXPATHLEN];
+
+ if ((envtmp = getenv("TMPDIR")) != NULL)
+ (void)sprintf(path, "%s%s", envtmp, strrchr(_PATH_RANTMP, '/'));
+ else
+ bcopy(_PATH_RANTMP, path, sizeof(_PATH_RANTMP));
+
+ sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((fd = mkstemp(path)) == -1)
+ error(path);
+ (void)unlink(path);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ return(fd);
+}
+
+void *
+emalloc(len)
+ int len;
+{
+ void *p;
+
+ if ((p = malloc((u_int)len)) == NULL)
+ error(archive);
+ return(p);
+}
+
+char *
+rname(path)
+ char *path;
+{
+ register char *ind;
+
+ return((ind = rindex(path, '/')) ? ind + 1 : path);
+}
+
+badfmt()
+{
+ errno = EFTYPE;
+ error(archive);
+}
+
+error(name)
+ char *name;
+{
+ (void)fprintf(stderr, "ranlib: %s: %s\n", name, strerror(errno));
+ exit(1);
+}
diff --git a/usr.bin/ranlib/pathnames.h b/usr.bin/ranlib/pathnames.h
new file mode 100644
index 0000000..c1df3f9
--- /dev/null
+++ b/usr.bin/ranlib/pathnames.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_RANTMP "/tmp/ranlib.XXXXXX"
diff --git a/usr.bin/ranlib/ranlib.1 b/usr.bin/ranlib/ranlib.1
new file mode 100644
index 0000000..c5bdf37
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.1
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ranlib.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RANLIB 1
+.Os
+.Sh NAME
+.Nm ranlib
+.Nd table-of-contents for archive libraries
+.Sh SYNOPSIS
+.Nm ranlib
+.Op Fl t
+.Ar file ...
+.Sh DESCRIPTION
+.Nm Ranlib
+creates a table of external references for archive libraries,
+normally used by the loader,
+.Xr ld 1 .
+This table is is named ``__.SYMDEF'' and is prepended to the archive.
+Files in the archive which are not executable and symbols which are
+uninteresting to the loader are ignored.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl t
+Set the modification time of the __.SYMDEF file.
+Some loaders (but not the FreeBSD one)
+compared this time with the modification time of the
+archive to verify that the table is up-to-date with respect to the
+archive.
+If the modification time has been changed without any change to the
+archive (for example, by a
+.Xr cp 1 ) ,
+the
+.Fl t
+option can be used to ``touch'' the modification time so that it
+appears that the table is up-to-date.
+This is also useful after using the
+.Fl t
+option of
+.Xr make 1 .
+.El
+.Sh FILES
+.Bl -tag -width /tmp/ranlib.XXXXXX -compact
+.It Pa /tmp/ranlib.XXXXXX
+Temporary file names.
+.El
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ld 1 ,
+.Xr lorder 1 ,
+.Xr nm 1 ,
+.Xr ranlib 5
+.Sh HISTORY
+A
+.Nm ranlib
+command appeared in
+.At v7 .
diff --git a/usr.bin/ranlib/ranlib.1aout b/usr.bin/ranlib/ranlib.1aout
new file mode 100644
index 0000000..c5bdf37
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.1aout
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ranlib.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RANLIB 1
+.Os
+.Sh NAME
+.Nm ranlib
+.Nd table-of-contents for archive libraries
+.Sh SYNOPSIS
+.Nm ranlib
+.Op Fl t
+.Ar file ...
+.Sh DESCRIPTION
+.Nm Ranlib
+creates a table of external references for archive libraries,
+normally used by the loader,
+.Xr ld 1 .
+This table is is named ``__.SYMDEF'' and is prepended to the archive.
+Files in the archive which are not executable and symbols which are
+uninteresting to the loader are ignored.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl t
+Set the modification time of the __.SYMDEF file.
+Some loaders (but not the FreeBSD one)
+compared this time with the modification time of the
+archive to verify that the table is up-to-date with respect to the
+archive.
+If the modification time has been changed without any change to the
+archive (for example, by a
+.Xr cp 1 ) ,
+the
+.Fl t
+option can be used to ``touch'' the modification time so that it
+appears that the table is up-to-date.
+This is also useful after using the
+.Fl t
+option of
+.Xr make 1 .
+.El
+.Sh FILES
+.Bl -tag -width /tmp/ranlib.XXXXXX -compact
+.It Pa /tmp/ranlib.XXXXXX
+Temporary file names.
+.El
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ld 1 ,
+.Xr lorder 1 ,
+.Xr nm 1 ,
+.Xr ranlib 5
+.Sh HISTORY
+A
+.Nm ranlib
+command appeared in
+.At v7 .
diff --git a/usr.bin/ranlib/ranlib.5 b/usr.bin/ranlib/ranlib.5
new file mode 100644
index 0000000..e953c51
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.5
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ranlib.5.5 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RANLIB 5
+.Os
+.Sh NAME
+.Nm ranlib
+.Nd archive (library) table-of-contents format
+.Sh SYNOPSIS
+.Fd #include <ranlib.h>
+.Sh DESCRIPTION
+The archive table-of-contents command
+.Nm ranlib
+creates a table of contents for archives, containing object files, to
+be used by the link-editor
+.Xr ld 1 .
+It operates on archives created with the utility
+.Xr ar 1 .
+.Pp
+The
+.Nm Ranlib
+function
+prepends a new file to the archive which has three separate parts.
+The first part is a standard archive header, which has a special name
+field, "__.SYMDEF".
+.Pp
+The second part is a ``long'' followed by a list of ranlib structures.
+The long is the size, in bytes, of the list of ranlib structures.
+Each of the ranlib structures consists of a zero based offset into the
+next section (a string table of symbols) and an offset from the beginning
+of the archive to the start of the archive file which defines the symbol.
+The actual number of ranlib structures is this number divided by the size
+of an individual ranlib structure.
+.Pp
+The third part is a ``long'' followed by a string table.
+The long is the size, in bytes of the string table.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ranlib 1
diff --git a/usr.bin/ranlib/ranlib.5.5 b/usr.bin/ranlib/ranlib.5.5
new file mode 100644
index 0000000..e953c51
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.5.5
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ranlib.5.5 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RANLIB 5
+.Os
+.Sh NAME
+.Nm ranlib
+.Nd archive (library) table-of-contents format
+.Sh SYNOPSIS
+.Fd #include <ranlib.h>
+.Sh DESCRIPTION
+The archive table-of-contents command
+.Nm ranlib
+creates a table of contents for archives, containing object files, to
+be used by the link-editor
+.Xr ld 1 .
+It operates on archives created with the utility
+.Xr ar 1 .
+.Pp
+The
+.Nm Ranlib
+function
+prepends a new file to the archive which has three separate parts.
+The first part is a standard archive header, which has a special name
+field, "__.SYMDEF".
+.Pp
+The second part is a ``long'' followed by a list of ranlib structures.
+The long is the size, in bytes, of the list of ranlib structures.
+Each of the ranlib structures consists of a zero based offset into the
+next section (a string table of symbols) and an offset from the beginning
+of the archive to the start of the archive file which defines the symbol.
+The actual number of ranlib structures is this number divided by the size
+of an individual ranlib structure.
+.Pp
+The third part is a ``long'' followed by a string table.
+The long is the size, in bytes of the string table.
+.Sh SEE ALSO
+.Xr ar 1 ,
+.Xr ranlib 1
diff --git a/usr.bin/ranlib/ranlib.c b/usr.bin/ranlib/ranlib.c
new file mode 100644
index 0000000..9d1d8a1
--- /dev/null
+++ b/usr.bin/ranlib/ranlib.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ranlib.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <archive.h>
+
+CHDR chdr;
+u_int options; /* UNUSED -- keep open_archive happy */
+char *archive;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch, eval, tflag;
+
+ tflag = 0;
+ while ((ch = getopt(argc, argv, "t")) != EOF)
+ switch(ch) {
+ case 't':
+ tflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ for (eval = 0; archive = *argv++;)
+ eval |= tflag ? touch() : build();
+ exit(eval);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "usage: ranlib [-t] archive ...\n");
+ exit(1);
+}
diff --git a/usr.bin/ranlib/touch.c b/usr.bin/ranlib/touch.c
new file mode 100644
index 0000000..0dd9123
--- /dev/null
+++ b/usr.bin/ranlib/touch.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Hugh Smith at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <ranlib.h>
+#include <ar.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <archive.h>
+
+extern CHDR chdr; /* converted header */
+extern char *archive; /* archive name */
+
+touch()
+{
+ int afd;
+
+ afd = open_archive(O_RDWR);
+
+ if (!get_arobj(afd) ||
+ strncmp(RANLIBMAG, chdr.name, sizeof(RANLIBMAG) - 1)) {
+ (void)fprintf(stderr,
+ "ranlib: %s: no symbol table.\n", archive);
+ return(1);
+ }
+ settime(afd);
+ close_archive(afd);
+ return(0);
+}
+
+settime(afd)
+ int afd;
+{
+ struct ar_hdr *hdr;
+ off_t size;
+ char buf[50];
+
+ size = SARMAG + sizeof(hdr->ar_name);
+ if (lseek(afd, size, SEEK_SET) == (off_t)-1)
+ error(archive);
+ (void)sprintf(buf, "%-12ld", time((time_t *)NULL) + RANLIBSKEW);
+ if (write(afd, buf, sizeof(hdr->ar_date)) != sizeof(hdr->ar_date))
+ error(archive);
+}
diff --git a/usr.bin/rdist/Makefile b/usr.bin/rdist/Makefile
new file mode 100644
index 0000000..853439c
--- /dev/null
+++ b/usr.bin/rdist/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.3 (Berkeley) 7/19/93
+
+PROG= rdist
+CFLAGS+=-I${.CURDIR}
+SRCS= docmd.c expand.c lookup.c main.c server.c
+OBJS+= gram.o
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+CLEANFILES=y.tab.h
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rdist/cron.entry b/usr.bin/rdist/cron.entry
new file mode 100644
index 0000000..90968cf
--- /dev/null
+++ b/usr.bin/rdist/cron.entry
@@ -0,0 +1 @@
+30 3 * * * /usr/bin/rdist -f /etc/Distfile >& /var/log/rdist/rdist.err 2>&1
diff --git a/usr.bin/rdist/defs.h b/usr.bin/rdist/defs.h
new file mode 100644
index 0000000..08bf8d6
--- /dev/null
+++ b/usr.bin/rdist/defs.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/9/93
+ */
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "pathnames.h"
+
+/*
+ * The version number should be changed whenever the protocol changes.
+ */
+#define VERSION 3
+
+ /* defines for yacc */
+#define EQUAL 1
+#define LP 2
+#define RP 3
+#define SM 4
+#define ARROW 5
+#define COLON 6
+#define DCOLON 7
+#define NAME 8
+#define STRING 9
+#define INSTALL 10
+#define NOTIFY 11
+#define EXCEPT 12
+#define PATTERN 13
+#define SPECIAL 14
+#define OPTION 15
+
+ /* lexical definitions */
+#define QUOTE 0200 /* used internally for quoted characters */
+#define TRIM 0177 /* Mask to strip quote bit */
+
+ /* table sizes */
+#define HASHSIZE 1021
+#define INMAX 3500
+
+ /* option flags */
+#define VERIFY 0x1
+#define WHOLE 0x2
+#define YOUNGER 0x4
+#define COMPARE 0x8
+#define REMOVE 0x10
+#define FOLLOW 0x20
+#define IGNLNKS 0x40
+
+ /* expand type definitions */
+#define E_VARS 0x1
+#define E_SHELL 0x2
+#define E_TILDE 0x4
+#define E_ALL 0x7
+
+ /* actions for lookup() */
+#define LOOKUP 0
+#define INSERT 1
+#define REPLACE 2
+
+#define ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+
+#define ALLOC(x) (struct x *) malloc(sizeof(struct x))
+
+struct namelist { /* for making lists of strings */
+ char *n_name;
+ struct namelist *n_next;
+};
+
+struct subcmd {
+ short sc_type; /* type - INSTALL,NOTIFY,EXCEPT,SPECIAL */
+ short sc_options;
+ char *sc_name;
+ struct namelist *sc_args;
+ struct subcmd *sc_next;
+};
+
+struct cmd {
+ int c_type; /* type - ARROW,DCOLON */
+ char *c_name; /* hostname or time stamp file name */
+ char *c_label; /* label for partial update */
+ struct namelist *c_files;
+ struct subcmd *c_cmds;
+ struct cmd *c_next;
+};
+
+struct linkbuf {
+ ino_t inum;
+ dev_t devnum;
+ int count;
+ char pathname[BUFSIZ];
+ char target[BUFSIZ];
+ struct linkbuf *nextp;
+};
+
+extern int debug; /* debugging flag */
+extern int nflag; /* NOP flag, don't execute commands */
+extern int qflag; /* Quiet. don't print messages */
+extern int options; /* global options */
+
+extern int nerrs; /* number of errors seen */
+extern int rem; /* remote file descriptor */
+extern int iamremote; /* acting as remote server */
+extern char tempfile[]; /* file name for logging changes */
+extern struct linkbuf *ihead; /* list of files with more than one link */
+extern struct passwd *pw; /* pointer to static area used by getpwent */
+extern struct group *gr; /* pointer to static area used by getgrent */
+extern char host[]; /* host name of master copy */
+extern char buf[]; /* general purpose buffer */
+
+int any __P((int, char *));
+char *colon __P((char *));
+void cleanup __P((int));
+void define __P((char *));
+void docmds __P((char **, int, char **));
+void error __P((const char *, ...));
+int except __P((char *));
+struct namelist *
+ expand __P((struct namelist *, int));
+char *exptilde __P((char [], char *));
+void fatal __P((const char *, ...));
+int inlist __P((struct namelist *, char *));
+void insert __P((char *,
+ struct namelist *, struct namelist *, struct subcmd *));
+void install __P((char *, char *, int, int));
+void log __P((FILE *, const char *, ...));
+struct namelist *
+ lookup __P((char *, int, struct namelist *));
+void lostconn __P((int));
+struct namelist *
+ makenl __P((char *));
+struct subcmd *
+ makesubcmd __P((int));
+void prnames __P((struct namelist *));
+void server __P((void));
+void yyerror __P((char *));
+int yyparse __P((void));
diff --git a/usr.bin/rdist/docmd.c b/usr.bin/rdist/docmd.c
new file mode 100644
index 0000000..8c4a1a2
--- /dev/null
+++ b/usr.bin/rdist/docmd.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+/*static char sccsid[] = "From: @(#)docmd.c 8.1 (Berkeley) 6/9/93";*/
+static const char rcsid[] =
+ "$Id: docmd.c,v 1.2 1995/02/21 04:32:54 wollman Exp $";
+#endif /* not lint */
+
+#include "defs.h"
+#include <setjmp.h>
+#include <netdb.h>
+#include <regex.h>
+
+FILE *lfp; /* log file for recording files updated */
+struct subcmd *subcmds; /* list of sub-commands for current cmd */
+jmp_buf env;
+
+static int makeconn __P((char *));
+static int okname __P((char *));
+static void closeconn __P((void));
+static void cmptime __P((char *));
+static void doarrow __P((char **,
+ struct namelist *, char *, struct subcmd *));
+static void dodcolon __P((char **,
+ struct namelist *, char *, struct subcmd *));
+static void notify __P((char *, char *, struct namelist *, time_t));
+static void rcmptime __P((struct stat *));
+
+/*
+ * Do the commands in cmds (initialized by yyparse).
+ */
+void
+docmds(dhosts, argc, argv)
+ char **dhosts;
+ int argc;
+ char **argv;
+{
+ register struct cmd *c;
+ register struct namelist *f;
+ register char **cpp;
+ extern struct cmd *cmds;
+
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+
+ for (c = cmds; c != NULL; c = c->c_next) {
+ if (dhosts != NULL && *dhosts != NULL) {
+ for (cpp = dhosts; *cpp; cpp++)
+ if (strcmp(c->c_name, *cpp) == 0)
+ goto fndhost;
+ continue;
+ }
+ fndhost:
+ if (argc) {
+ for (cpp = argv; *cpp; cpp++) {
+ if (c->c_label != NULL &&
+ strcmp(c->c_label, *cpp) == 0) {
+ cpp = NULL;
+ goto found;
+ }
+ for (f = c->c_files; f != NULL; f = f->n_next)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ }
+ continue;
+ } else
+ cpp = NULL;
+ found:
+ switch (c->c_type) {
+ case ARROW:
+ doarrow(cpp, c->c_files, c->c_name, c->c_cmds);
+ break;
+ case DCOLON:
+ dodcolon(cpp, c->c_files, c->c_name, c->c_cmds);
+ break;
+ default:
+ fatal("illegal command type %d\n", c->c_type);
+ }
+ }
+ closeconn();
+}
+
+/*
+ * Process commands for sending files to other machines.
+ */
+static void
+doarrow(filev, files, rhost, cmds)
+ char **filev;
+ struct namelist *files;
+ char *rhost;
+ struct subcmd *cmds;
+{
+ register struct namelist *f;
+ register struct subcmd *sc;
+ register char **cpp;
+ int n, ddir, opts = options;
+
+ if (debug)
+ printf("doarrow(%x, %s, %x)\n", files, rhost, cmds);
+
+ if (files == NULL) {
+ error("no files to be updated\n");
+ return;
+ }
+
+ subcmds = cmds;
+ ddir = files->n_next != NULL; /* destination is a directory */
+ if (nflag)
+ printf("updating host %s\n", rhost);
+ else {
+ if (setjmp(env))
+ goto done;
+ signal(SIGPIPE, lostconn);
+ if (!makeconn(rhost))
+ return;
+ if ((lfp = fopen(tempfile, "w")) == NULL) {
+ fatal("cannot open %s\n", tempfile);
+ exit(1);
+ }
+ }
+ for (f = files; f != NULL; f = f->n_next) {
+ if (filev) {
+ for (cpp = filev; *cpp; cpp++)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ if (!nflag)
+ (void) fclose(lfp);
+ continue;
+ }
+ found:
+ n = 0;
+ for (sc = cmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != INSTALL)
+ continue;
+ n++;
+ install(f->n_name, sc->sc_name,
+ sc->sc_name == NULL ? 0 : ddir, sc->sc_options);
+ opts = sc->sc_options;
+ }
+ if (n == 0)
+ install(f->n_name, NULL, 0, options);
+ }
+done:
+ if (!nflag) {
+ (void) signal(SIGPIPE, cleanup);
+ (void) fclose(lfp);
+ lfp = NULL;
+ }
+ for (sc = cmds; sc != NULL; sc = sc->sc_next)
+ if (sc->sc_type == NOTIFY)
+ notify(tempfile, rhost, sc->sc_args, 0);
+ if (!nflag) {
+ (void) unlink(tempfile);
+ for (; ihead != NULL; ihead = ihead->nextp) {
+ free(ihead);
+ if ((opts & IGNLNKS) || ihead->count == 0)
+ continue;
+ log(lfp, "%s: Warning: missing links\n",
+ ihead->pathname);
+ }
+ }
+}
+
+/*
+ * Create a connection to the rdist server on the machine rhost.
+ */
+static int
+makeconn(rhost)
+ char *rhost;
+{
+ register char *ruser, *cp;
+ static char *cur_host = NULL;
+ static int port = -1;
+ char tuser[20];
+ int n;
+ extern char user[];
+ extern int userid;
+
+ if (debug)
+ printf("makeconn(%s)\n", rhost);
+
+ if (cur_host != NULL && rem >= 0) {
+ if (strcmp(cur_host, rhost) == 0)
+ return(1);
+ closeconn();
+ }
+ cur_host = rhost;
+ cp = index(rhost, '@');
+ if (cp != NULL) {
+ char c = *cp;
+
+ *cp = '\0';
+ strncpy(tuser, rhost, sizeof(tuser)-1);
+ *cp = c;
+ rhost = cp + 1;
+ ruser = tuser;
+ if (*ruser == '\0')
+ ruser = user;
+ else if (!okname(ruser))
+ return(0);
+ } else
+ ruser = user;
+ if (!qflag)
+ printf("updating host %s\n", rhost);
+ (void) sprintf(buf, "%s -Server%s", _PATH_RDIST, qflag ? " -q" : "");
+ if (port < 0) {
+ struct servent *sp;
+
+ if ((sp = getservbyname("shell", "tcp")) == NULL)
+ fatal("shell/tcp: unknown service");
+ port = sp->s_port;
+ }
+
+ if (debug) {
+ printf("port = %d, luser = %s, ruser = %s\n", ntohs(port), user, ruser);
+ printf("buf = %s\n", buf);
+ }
+
+ fflush(stdout);
+ seteuid(0);
+ rem = rcmd(&rhost, port, user, ruser, buf, 0);
+ seteuid(userid);
+ if (rem < 0)
+ return(0);
+ cp = buf;
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ if (*cp == 'V') {
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+ *--cp = '\0';
+ cp = buf;
+ n = 0;
+ while (*cp >= '0' && *cp <= '9')
+ n = (n * 10) + (*cp++ - '0');
+ if (*cp == '\0' && n == VERSION)
+ return(1);
+ error("connection failed: version numbers don't match (local %d, remote %d)\n", VERSION, n);
+ } else {
+ error("connection failed: version numbers don't match\n");
+ error("got unexpected input:");
+ do {
+ error("%c", *cp);
+ } while (*cp != '\n' && read(rem, cp, 1) == 1);
+ }
+ closeconn();
+ return(0);
+}
+
+/*
+ * Signal end of previous connection.
+ */
+static void
+closeconn()
+{
+ if (debug)
+ printf("closeconn()\n");
+
+ if (rem >= 0) {
+ (void) write(rem, "\2\n", 2);
+ (void) close(rem);
+ rem = -1;
+ }
+}
+
+void
+lostconn(signo)
+ int signo;
+{
+ if (iamremote)
+ cleanup(0);
+ log(lfp, "rdist: lost connection\n");
+ longjmp(env, 1);
+}
+
+static int
+okname(name)
+ register char *name;
+{
+ register char *cp = name;
+ register int c;
+
+ do {
+ c = *cp;
+ if (c & 0200)
+ goto bad;
+ if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+ goto bad;
+ cp++;
+ } while (*cp);
+ return(1);
+bad:
+ error("invalid user name %s\n", name);
+ return(0);
+}
+
+time_t lastmod;
+FILE *tfp;
+extern char target[], *tp;
+
+/*
+ * Process commands for comparing files to time stamp files.
+ */
+static void
+dodcolon(filev, files, stamp, cmds)
+ char **filev;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *cmds;
+{
+ register struct subcmd *sc;
+ register struct namelist *f;
+ register char **cpp;
+ struct timeval tv[2];
+ struct timezone tz;
+ struct stat stb;
+
+ if (debug)
+ printf("dodcolon()\n");
+
+ if (files == NULL) {
+ error("no files to be updated\n");
+ return;
+ }
+ if (stat(stamp, &stb) < 0) {
+ error("%s: %s\n", stamp, strerror(errno));
+ return;
+ }
+ if (debug)
+ printf("%s: %ld\n", stamp, stb.st_mtime);
+
+ subcmds = cmds;
+ lastmod = stb.st_mtime;
+ if (nflag || (options & VERIFY))
+ tfp = NULL;
+ else {
+ if ((tfp = fopen(tempfile, "w")) == NULL) {
+ error("%s: %s\n", stamp, strerror(errno));
+ return;
+ }
+ (void) gettimeofday(&tv[0], &tz);
+ tv[1] = tv[0];
+ (void) utimes(stamp, tv);
+ }
+
+ for (f = files; f != NULL; f = f->n_next) {
+ if (filev) {
+ for (cpp = filev; *cpp; cpp++)
+ if (strcmp(f->n_name, *cpp) == 0)
+ goto found;
+ continue;
+ }
+ found:
+ tp = NULL;
+ cmptime(f->n_name);
+ }
+
+ if (tfp != NULL)
+ (void) fclose(tfp);
+ for (sc = cmds; sc != NULL; sc = sc->sc_next)
+ if (sc->sc_type == NOTIFY)
+ notify(tempfile, NULL, sc->sc_args, lastmod);
+ if (!nflag && !(options & VERIFY))
+ (void) unlink(tempfile);
+}
+
+/*
+ * Compare the mtime of file to the list of time stamps.
+ */
+static void
+cmptime(name)
+ char *name;
+{
+ struct stat stb;
+
+ if (debug)
+ printf("cmptime(%s)\n", name);
+
+ if (except(name))
+ return;
+
+ if (nflag) {
+ printf("comparing dates: %s\n", name);
+ return;
+ }
+
+ /*
+ * first time cmptime() is called?
+ */
+ if (tp == NULL) {
+ if (exptilde(target, name) == NULL)
+ return;
+ tp = name = target;
+ while (*tp)
+ tp++;
+ }
+ if (access(name, 4) < 0 || stat(name, &stb) < 0) {
+ error("%s: %s\n", name, strerror(errno));
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ break;
+
+ case S_IFDIR:
+ rcmptime(&stb);
+ return;
+
+ default:
+ error("%s: not a plain file\n", name);
+ return;
+ }
+
+ if (stb.st_mtime > lastmod)
+ log(tfp, "new: %s\n", name);
+}
+
+static void
+rcmptime(st)
+ struct stat *st;
+{
+ register DIR *d;
+ register struct direct *dp;
+ register char *cp;
+ char *otp;
+ int len;
+
+ if (debug)
+ printf("rcmptime(%x)\n", st);
+
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ error("%s/%s: Name too long\n", target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ cmptime(target);
+ }
+ closedir(d);
+ tp = otp;
+ *tp = '\0';
+}
+
+/*
+ * Notify the list of people the changes that were made.
+ * rhost == NULL if we are mailing a list of changes compared to at time
+ * stamp file.
+ */
+static void
+notify(file, rhost, to, lmod)
+ char *file, *rhost;
+ register struct namelist *to;
+ time_t lmod;
+{
+ register int fd, len;
+ struct stat stb;
+ FILE *pf;
+
+ if ((options & VERIFY) || to == NULL)
+ return;
+ if (!qflag) {
+ printf("notify ");
+ if (rhost)
+ printf("@%s ", rhost);
+ prnames(to);
+ }
+ if (nflag)
+ return;
+
+ if ((fd = open(file, 0)) < 0) {
+ error("%s: %s\n", file, strerror(errno));
+ return;
+ }
+ if (fstat(fd, &stb) < 0) {
+ error("%s: %s\n", file, strerror(errno));
+ (void) close(fd);
+ return;
+ }
+ if (stb.st_size == 0) {
+ (void) close(fd);
+ return;
+ }
+ /*
+ * Create a pipe to mailling program.
+ */
+ (void)sprintf(buf, "%s -oi -t", _PATH_SENDMAIL);
+ pf = popen(buf, "w");
+ if (pf == NULL) {
+ error("notify: \"%s\" failed\n", _PATH_SENDMAIL);
+ (void) close(fd);
+ return;
+ }
+ /*
+ * Output the proper header information.
+ */
+ fprintf(pf, "From: rdist (Remote distribution program)\n");
+ fprintf(pf, "To:");
+ if (!any('@', to->n_name) && rhost != NULL)
+ fprintf(pf, " %s@%s", to->n_name, rhost);
+ else
+ fprintf(pf, " %s", to->n_name);
+ to = to->n_next;
+ while (to != NULL) {
+ if (!any('@', to->n_name) && rhost != NULL)
+ fprintf(pf, ", %s@%s", to->n_name, rhost);
+ else
+ fprintf(pf, ", %s", to->n_name);
+ to = to->n_next;
+ }
+ putc('\n', pf);
+ if (rhost != NULL)
+ fprintf(pf, "Subject: files updated by rdist from %s to %s\n",
+ host, rhost);
+ else
+ fprintf(pf, "Subject: files updated after %s\n", ctime(&lmod));
+ putc('\n', pf);
+
+ while ((len = read(fd, buf, BUFSIZ)) > 0)
+ (void) fwrite(buf, 1, len, pf);
+ (void) close(fd);
+ (void) pclose(pf);
+}
+
+/*
+ * Return true if name is in the list.
+ */
+int
+inlist(list, file)
+ struct namelist *list;
+ char *file;
+{
+ register struct namelist *nl;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ if (!strcmp(file, nl->n_name))
+ return(1);
+ return(0);
+}
+
+/*
+ * Return TRUE if file is in the exception list.
+ */
+int
+except(file)
+ char *file;
+{
+ register struct subcmd *sc;
+ register struct namelist *nl;
+ regex_t rx;
+ int val;
+
+ if (debug)
+ printf("except(%s)\n", file);
+
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != EXCEPT && sc->sc_type != PATTERN)
+ continue;
+ for (nl = sc->sc_args; nl != NULL; nl = nl->n_next) {
+ if (sc->sc_type == EXCEPT) {
+ if (!strcmp(file, nl->n_name))
+ return(1);
+ continue;
+ }
+ val = regcomp(&rx, nl->n_name,
+ REG_EXTENDED | REG_NOSUB);
+ if (!regexec(&rx, file, 0, 0, 0)) {
+ regfree(&rx);
+ return 1;
+ }
+ regfree(&rx);
+ }
+ }
+ return(0);
+}
+
+char *
+colon(cp)
+ register char *cp;
+{
+
+ while (*cp) {
+ if (*cp == ':')
+ return(cp);
+ if (*cp == '/')
+ return(0);
+ cp++;
+ }
+ return(0);
+}
diff --git a/usr.bin/rdist/expand.c b/usr.bin/rdist/expand.c
new file mode 100644
index 0000000..6b5fd17
--- /dev/null
+++ b/usr.bin/rdist/expand.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)expand.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+#define GAVSIZ NCARGS / 6
+#define LC '{'
+#define RC '}'
+
+static char shchars[] = "${[*?";
+
+int which; /* bit mask of types to expand */
+int eargc; /* expanded arg count */
+char **eargv; /* expanded arg vectors */
+char *path;
+char *pathp;
+char *lastpathp;
+char *tilde; /* "~user" if not expanding tilde, else "" */
+char *tpathp;
+int nleft;
+
+int expany; /* any expansions done? */
+char *entp;
+char **sortbase;
+
+#define sort() qsort((char *)sortbase, &eargv[eargc] - sortbase, \
+ sizeof(*sortbase), argcmp), sortbase = &eargv[eargc]
+
+static void Cat __P((char *, char *));
+static void addpath __P((int));
+static int amatch __P((char *, char *));
+static int argcmp __P((const void *, const void *));
+static int execbrc __P((char *, char *));
+static void expsh __P((char *));
+static void expstr __P((char *));
+static int match __P((char *, char *));
+static void matchdir __P((char *));
+static int smatch __P((char *, char *));
+
+/*
+ * Take a list of names and expand any macros, etc.
+ * wh = E_VARS if expanding variables.
+ * wh = E_SHELL if expanding shell characters.
+ * wh = E_TILDE if expanding `~'.
+ * or any of these or'ed together.
+ *
+ * Major portions of this were snarfed from csh/sh.glob.c.
+ */
+struct namelist *
+expand(list, wh)
+ struct namelist *list;
+ int wh;
+{
+ register struct namelist *nl, *prev;
+ register int n;
+ char pathbuf[BUFSIZ];
+ char *argvbuf[GAVSIZ];
+
+ if (debug) {
+ printf("expand(%x, %d)\nlist = ", list, wh);
+ prnames(list);
+ }
+
+ if (wh == 0) {
+ register char *cp;
+
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ for (cp = nl->n_name; *cp; cp++)
+ *cp = *cp & TRIM;
+ return(list);
+ }
+
+ which = wh;
+ path = tpathp = pathp = pathbuf;
+ *pathp = '\0';
+ lastpathp = &path[sizeof pathbuf - 2];
+ tilde = "";
+ eargc = 0;
+ eargv = sortbase = argvbuf;
+ *eargv = 0;
+ nleft = NCARGS - 4;
+ /*
+ * Walk the name list and expand names into eargv[];
+ */
+ for (nl = list; nl != NULL; nl = nl->n_next)
+ expstr(nl->n_name);
+ /*
+ * Take expanded list of names from eargv[] and build a new list.
+ */
+ list = prev = NULL;
+ for (n = 0; n < eargc; n++) {
+ nl = makenl(NULL);
+ nl->n_name = eargv[n];
+ if (prev == NULL)
+ list = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+ if (debug) {
+ printf("expanded list = ");
+ prnames(list);
+ }
+ return(list);
+}
+
+static void
+expstr(s)
+ char *s;
+{
+ register char *cp, *cp1;
+ register struct namelist *tp;
+ char *tail;
+ char buf[BUFSIZ];
+ int savec, oeargc;
+ extern char homedir[];
+
+ if (s == NULL || *s == '\0')
+ return;
+
+ if ((which & E_VARS) && (cp = index(s, '$')) != NULL) {
+ *cp++ = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ if (*cp == LC) {
+ cp++;
+ if ((tail = index(cp, RC)) == NULL) {
+ yyerror("unmatched '{'");
+ return;
+ }
+ *tail++ = savec = '\0';
+ if (*cp == '\0') {
+ yyerror("no variable name after '$'");
+ return;
+ }
+ } else {
+ tail = cp + 1;
+ savec = *tail;
+ *tail = '\0';
+ }
+ tp = lookup(cp, NULL, 0);
+ if (savec != '\0')
+ *tail = savec;
+ if (tp != NULL) {
+ for (; tp != NULL; tp = tp->n_next) {
+ sprintf(buf, "%s%s%s", s, tp->n_name, tail);
+ expstr(buf);
+ }
+ return;
+ }
+ sprintf(buf, "%s%s", s, tail);
+ expstr(buf);
+ return;
+ }
+ if ((which & ~E_VARS) == 0 || !strcmp(s, "{") || !strcmp(s, "{}")) {
+ Cat(s, "");
+ sort();
+ return;
+ }
+ if (*s == '~') {
+ cp = ++s;
+ if (*cp == '\0' || *cp == '/') {
+ tilde = "~";
+ cp1 = homedir;
+ } else {
+ tilde = cp1 = buf;
+ *cp1++ = '~';
+ do
+ *cp1++ = *cp++;
+ while (*cp && *cp != '/');
+ *cp1 = '\0';
+ if (pw == NULL || strcmp(pw->pw_name, buf+1) != 0) {
+ if ((pw = getpwnam(buf+1)) == NULL) {
+ strcat(buf, ": unknown user name");
+ yyerror(buf+1);
+ return;
+ }
+ }
+ cp1 = pw->pw_dir;
+ s = cp;
+ }
+ for (cp = path; *cp++ = *cp1++; )
+ ;
+ tpathp = pathp = cp - 1;
+ } else {
+ tpathp = pathp = path;
+ tilde = "";
+ }
+ *pathp = '\0';
+ if (!(which & E_SHELL)) {
+ if (which & E_TILDE)
+ Cat(path, s);
+ else
+ Cat(tilde, s);
+ sort();
+ return;
+ }
+ oeargc = eargc;
+ expany = 0;
+ expsh(s);
+ if (eargc == oeargc)
+ Cat(s, ""); /* "nonomatch" is set */
+ sort();
+}
+
+static int
+argcmp(a1, a2)
+ const void *a1, *a2;
+{
+
+ return (strcmp(*(char **)a1, *(char **)a2));
+}
+
+/*
+ * If there are any Shell meta characters in the name,
+ * expand into a list, after searching directory
+ */
+static void
+expsh(s)
+ char *s;
+{
+ register char *cp;
+ register char *spathp, *oldcp;
+ struct stat stb;
+
+ spathp = pathp;
+ cp = s;
+ while (!any(*cp, shchars)) {
+ if (*cp == '\0') {
+ if (!expany || stat(path, &stb) >= 0) {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ }
+ goto endit;
+ }
+ addpath(*cp++);
+ }
+ oldcp = cp;
+ while (cp > s && *cp != '/')
+ cp--, pathp--;
+ if (*cp == '/')
+ cp++, pathp++;
+ *pathp = '\0';
+ if (*oldcp == '{') {
+ execbrc(cp, NULL);
+ return;
+ }
+ matchdir(cp);
+endit:
+ pathp = spathp;
+ *pathp = '\0';
+}
+
+static void
+matchdir(pattern)
+ char *pattern;
+{
+ struct stat stb;
+ register struct direct *dp;
+ DIR *dirp;
+
+ dirp = opendir(path);
+ if (dirp == NULL) {
+ if (expany)
+ return;
+ goto patherr2;
+ }
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!ISDIR(stb.st_mode)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+ while ((dp = readdir(dirp)) != NULL)
+ if (match(dp->d_name, pattern)) {
+ if (which & E_TILDE)
+ Cat(path, dp->d_name);
+ else {
+ strcpy(pathp, dp->d_name);
+ Cat(tilde, tpathp);
+ *pathp = '\0';
+ }
+ }
+ closedir(dirp);
+ return;
+
+patherr1:
+ closedir(dirp);
+patherr2:
+ strcat(path, ": ");
+ strcat(path, strerror(errno));
+ yyerror(path);
+}
+
+static int
+execbrc(p, s)
+ char *p, *s;
+{
+ char restbuf[BUFSIZ + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *spathp;
+
+ for (lm = restbuf; *p != '{'; *lm++ = *p++)
+ continue;
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ if (!*pe)
+ yyerror("Missing ']'");
+ continue;
+ }
+pend:
+ if (brclev || !*pe) {
+ yyerror("Missing '}'");
+ return (0);
+ }
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ strcpy(lm, pl);
+ strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ spathp = pathp;
+ expsh(restbuf);
+ pathp = spathp;
+ *pathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ yyerror("Missing ']'");
+ continue;
+ }
+ return (0);
+}
+
+static int
+match(s, p)
+ char *s, *p;
+{
+ register int c;
+ register char *sentp;
+ char sexpany = expany;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ expany = sexpany;
+ return (c);
+}
+
+static int
+amatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ char *spathp;
+ struct stat stb;
+ int c, cc;
+
+ expany = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ for (s--; *s; s++)
+ if (amatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == '\0')
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ spathp = pathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(path, &stb) == 0 && ISDIR(stb.st_mode))
+ if (*p == '\0') {
+ if (which & E_TILDE)
+ Cat(path, "");
+ else
+ Cat(tilde, tpathp);
+ } else
+ expsh(p);
+ pathp = spathp;
+ *pathp = '\0';
+ return (0);
+ }
+ }
+}
+
+static int
+smatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0) {
+ yyerror("Missing ']'");
+ return (0);
+ }
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (smatch(s, p))
+ return (1);
+ return (0);
+
+ case '\0':
+ return (scc == '\0');
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+
+static void
+Cat(s1, s2)
+ register char *s1, *s2;
+{
+ int len = strlen(s1) + strlen(s2) + 1;
+ register char *s;
+
+ nleft -= len;
+ if (nleft <= 0 || ++eargc >= GAVSIZ)
+ yyerror("Arguments too long");
+ eargv[eargc] = 0;
+ eargv[eargc - 1] = s = malloc(len);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ while (*s++ = *s1++ & TRIM)
+ ;
+ s--;
+ while (*s++ = *s2++ & TRIM)
+ ;
+}
+
+static void
+addpath(c)
+ int c;
+{
+
+ if (pathp >= lastpathp)
+ yyerror("Pathname too long");
+ else {
+ *pathp++ = c & TRIM;
+ *pathp = '\0';
+ }
+}
+
+/*
+ * Expand file names beginning with `~' into the
+ * user's home directory path name. Return a pointer in buf to the
+ * part corresponding to `file'.
+ */
+char *
+exptilde(buf, file)
+ char buf[];
+ register char *file;
+{
+ register char *s1, *s2, *s3;
+ extern char homedir[];
+
+ if (*file != '~') {
+ strcpy(buf, file);
+ return(buf);
+ }
+ if (*++file == '\0') {
+ s2 = homedir;
+ s3 = NULL;
+ } else if (*file == '/') {
+ s2 = homedir;
+ s3 = file;
+ } else {
+ s3 = file;
+ while (*s3 && *s3 != '/')
+ s3++;
+ if (*s3 == '/')
+ *s3 = '\0';
+ else
+ s3 = NULL;
+ if (pw == NULL || strcmp(pw->pw_name, file) != 0) {
+ if ((pw = getpwnam(file)) == NULL) {
+ error("%s: unknown user name\n", file);
+ if (s3 != NULL)
+ *s3 = '/';
+ return(NULL);
+ }
+ }
+ if (s3 != NULL)
+ *s3 = '/';
+ s2 = pw->pw_dir;
+ }
+ for (s1 = buf; *s1++ = *s2++; )
+ ;
+ s2 = --s1;
+ if (s3 != NULL) {
+ s2++;
+ while (*s1++ = *s3++)
+ ;
+ }
+ return(s2);
+}
diff --git a/usr.bin/rdist/gram.y b/usr.bin/rdist/gram.y
new file mode 100644
index 0000000..6e54911
--- /dev/null
+++ b/usr.bin/rdist/gram.y
@@ -0,0 +1,519 @@
+%{
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)gram.y 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include <sys/types.h>
+#include <regex.h>
+#include <limits.h>
+
+struct cmd *cmds = NULL;
+struct cmd *last_cmd;
+struct namelist *last_n;
+struct subcmd *last_sc;
+
+static char *makestr __P((char *));
+
+%}
+
+%term EQUAL 1
+%term LP 2
+%term RP 3
+%term SM 4
+%term ARROW 5
+%term COLON 6
+%term DCOLON 7
+%term NAME 8
+%term STRING 9
+%term INSTALL 10
+%term NOTIFY 11
+%term EXCEPT 12
+%term PATTERN 13
+%term SPECIAL 14
+%term OPTION 15
+
+%union {
+ int intval;
+ char *string;
+ struct subcmd *subcmd;
+ struct namelist *namel;
+}
+
+%type <intval> OPTION, options
+%type <string> NAME, STRING
+%type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
+%type <namel> namelist, names, opt_namelist
+
+%%
+
+file: /* VOID */
+ | file command
+ ;
+
+command: NAME EQUAL namelist = {
+ (void) lookup($1, INSERT, $3);
+ }
+ | namelist ARROW namelist cmdlist = {
+ insert(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist ARROW namelist cmdlist = {
+ insert($1, $3, $5, $6);
+ }
+ | namelist DCOLON NAME cmdlist = {
+ append(NULL, $1, $3, $4);
+ }
+ | NAME COLON namelist DCOLON NAME cmdlist = {
+ append($1, $3, $5, $6);
+ }
+ | error
+ ;
+
+namelist: NAME = {
+ $$ = makenl($1);
+ }
+ | LP names RP = {
+ $$ = $2;
+ }
+ ;
+
+names: /* VOID */ {
+ $$ = last_n = NULL;
+ }
+ | names NAME = {
+ if (last_n == NULL)
+ $$ = last_n = makenl($2);
+ else {
+ last_n->n_next = makenl($2);
+ last_n = last_n->n_next;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmdlist: /* VOID */ {
+ $$ = last_sc = NULL;
+ }
+ | cmdlist cmd = {
+ if (last_sc == NULL)
+ $$ = last_sc = $2;
+ else {
+ last_sc->sc_next = $2;
+ last_sc = $2;
+ $$ = $1;
+ }
+ }
+ ;
+
+cmd: INSTALL options opt_namelist SM = {
+ register struct namelist *nl;
+
+ $1->sc_options = $2 | options;
+ if ($3 != NULL) {
+ nl = expand($3, E_VARS);
+ if (nl) {
+ if (nl->n_next != NULL)
+ yyerror("only one name allowed\n");
+ $1->sc_name = nl->n_name;
+ free(nl);
+ } else
+ $1->sc_name = NULL;
+ }
+ $$ = $1;
+ }
+ | NOTIFY namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | EXCEPT namelist SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $$ = $1;
+ }
+ | PATTERN namelist SM = {
+ struct namelist *nl;
+ regex_t rx;
+ int val;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ for (nl = $2; nl != NULL; nl = nl->n_next) {
+ if (val = regcomp(&rx, nl->n_name,
+ REG_EXTENDED)) {
+ regerror(val, &rx, errbuf,
+ sizeof errbuf);
+ yyerror(errbuf);
+ }
+ regfree(&rx);
+ }
+ $1->sc_args = expand($2, E_VARS);
+ $$ = $1;
+ }
+ | SPECIAL opt_namelist STRING SM = {
+ if ($2 != NULL)
+ $1->sc_args = expand($2, E_ALL);
+ $1->sc_name = $3;
+ $$ = $1;
+ }
+ ;
+
+options: /* VOID */ = {
+ $$ = 0;
+ }
+ | options OPTION = {
+ $$ |= $2;
+ }
+ ;
+
+opt_namelist: /* VOID */ = {
+ $$ = NULL;
+ }
+ | namelist = {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+int yylineno = 1;
+extern FILE *fin;
+
+int
+yylex()
+{
+ static char yytext[INMAX];
+ register int c;
+ register char *cp1, *cp2;
+ static char quotechars[] = "[]{}*?$";
+
+again:
+ switch (c = getc(fin)) {
+ case EOF: /* end of file */
+ return(0);
+
+ case '#': /* start of comment */
+ while ((c = getc(fin)) != EOF && c != '\n')
+ ;
+ if (c == EOF)
+ return(0);
+ case '\n':
+ yylineno++;
+ case ' ':
+ case '\t': /* skip blanks */
+ goto again;
+
+ case '=': /* EQUAL */
+ return(EQUAL);
+
+ case '(': /* LP */
+ return(LP);
+
+ case ')': /* RP */
+ return(RP);
+
+ case ';': /* SM */
+ return(SM);
+
+ case '-': /* -> */
+ if ((c = getc(fin)) == '>')
+ return(ARROW);
+ ungetc(c, fin);
+ c = '-';
+ break;
+
+ case '"': /* STRING */
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("command string too long\n");
+ break;
+ }
+ c = getc(fin);
+ if (c == EOF || c == '"')
+ break;
+ if (c == '\\') {
+ if ((c = getc(fin)) == EOF) {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ if (c == '\n') {
+ yylineno++;
+ c = ' '; /* can't send '\n' */
+ }
+ *cp1++ = c;
+ }
+ if (c != '"')
+ yyerror("missing closing '\"'\n");
+ *cp1 = '\0';
+ yylval.string = makestr(yytext);
+ return(STRING);
+
+ case ':': /* : or :: */
+ if ((c = getc(fin)) == ':')
+ return(DCOLON);
+ ungetc(c, fin);
+ return(COLON);
+ }
+ cp1 = yytext;
+ cp2 = &yytext[INMAX - 1];
+ for (;;) {
+ if (cp1 >= cp2) {
+ yyerror("input line too long\n");
+ break;
+ }
+ if (c == '\\') {
+ if ((c = getc(fin)) != EOF) {
+ if (any(c, quotechars))
+ c |= QUOTE;
+ } else {
+ *cp1++ = '\\';
+ break;
+ }
+ }
+ *cp1++ = c;
+ c = getc(fin);
+ if (c == EOF || any(c, " \"'\t()=;:\n")) {
+ ungetc(c, fin);
+ break;
+ }
+ }
+ *cp1 = '\0';
+ if (yytext[0] == '-' && yytext[2] == '\0') {
+ switch (yytext[1]) {
+ case 'b':
+ yylval.intval = COMPARE;
+ return(OPTION);
+
+ case 'R':
+ yylval.intval = REMOVE;
+ return(OPTION);
+
+ case 'v':
+ yylval.intval = VERIFY;
+ return(OPTION);
+
+ case 'w':
+ yylval.intval = WHOLE;
+ return(OPTION);
+
+ case 'y':
+ yylval.intval = YOUNGER;
+ return(OPTION);
+
+ case 'h':
+ yylval.intval = FOLLOW;
+ return(OPTION);
+
+ case 'i':
+ yylval.intval = IGNLNKS;
+ return(OPTION);
+ }
+ }
+ if (!strcmp(yytext, "install"))
+ c = INSTALL;
+ else if (!strcmp(yytext, "notify"))
+ c = NOTIFY;
+ else if (!strcmp(yytext, "except"))
+ c = EXCEPT;
+ else if (!strcmp(yytext, "except_pat"))
+ c = PATTERN;
+ else if (!strcmp(yytext, "special"))
+ c = SPECIAL;
+ else {
+ yylval.string = makestr(yytext);
+ return(NAME);
+ }
+ yylval.subcmd = makesubcmd(c);
+ return(c);
+}
+
+int
+any(c, str)
+ register int c;
+ register char *str;
+{
+ while (*str)
+ if (c == *str++)
+ return(1);
+ return(0);
+}
+
+/*
+ * Insert or append ARROW command to list of hosts to be updated.
+ */
+void
+insert(label, files, hosts, subcmds)
+ char *label;
+ struct namelist *files, *hosts;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c, *prev, *nc;
+ register struct namelist *h;
+
+ files = expand(files, E_VARS|E_SHELL);
+ hosts = expand(hosts, E_ALL);
+ for (h = hosts; h != NULL; free(h), h = h->n_next) {
+ /*
+ * Search command list for an update to the same host.
+ */
+ for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
+ if (strcmp(c->c_name, h->n_name) == 0) {
+ do {
+ prev = c;
+ c = c->c_next;
+ } while (c != NULL &&
+ strcmp(c->c_name, h->n_name) == 0);
+ break;
+ }
+ }
+ /*
+ * Insert new command to update host.
+ */
+ nc = ALLOC(cmd);
+ if (nc == NULL)
+ fatal("ran out of memory\n");
+ nc->c_type = ARROW;
+ nc->c_name = h->n_name;
+ nc->c_label = label;
+ nc->c_files = files;
+ nc->c_cmds = subcmds;
+ nc->c_next = c;
+ if (prev == NULL)
+ cmds = nc;
+ else
+ prev->c_next = nc;
+ /* update last_cmd if appending nc to cmds */
+ if (c == NULL)
+ last_cmd = nc;
+ }
+}
+
+/*
+ * Append DCOLON command to the end of the command list since these are always
+ * executed in the order they appear in the distfile.
+ */
+void
+append(label, files, stamp, subcmds)
+ char *label;
+ struct namelist *files;
+ char *stamp;
+ struct subcmd *subcmds;
+{
+ register struct cmd *c;
+
+ c = ALLOC(cmd);
+ if (c == NULL)
+ fatal("ran out of memory\n");
+ c->c_type = DCOLON;
+ c->c_name = stamp;
+ c->c_label = label;
+ c->c_files = expand(files, E_ALL);
+ c->c_cmds = subcmds;
+ c->c_next = NULL;
+ if (cmds == NULL)
+ cmds = last_cmd = c;
+ else {
+ last_cmd->c_next = c;
+ last_cmd = c;
+ }
+}
+
+/*
+ * Error printing routine in parser.
+ */
+void
+yyerror(s)
+ char *s;
+{
+ ++nerrs;
+ fflush(stdout);
+ fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
+}
+
+/*
+ * Return a copy of the string.
+ */
+static char *
+makestr(str)
+ char *str;
+{
+ register char *cp, *s;
+
+ str = cp = malloc(strlen(s = str) + 1);
+ if (cp == NULL)
+ fatal("ran out of memory\n");
+ while (*cp++ = *s++)
+ ;
+ return(str);
+}
+
+/*
+ * Allocate a namelist structure.
+ */
+struct namelist *
+makenl(name)
+ char *name;
+{
+ register struct namelist *nl;
+
+ nl = ALLOC(namelist);
+ if (nl == NULL)
+ fatal("ran out of memory\n");
+ nl->n_name = name;
+ nl->n_next = NULL;
+ return(nl);
+}
+
+/*
+ * Make a sub command for lists of variables, commands, etc.
+ */
+struct subcmd *
+makesubcmd(type)
+ int type;
+{
+ register struct subcmd *sc;
+
+ sc = ALLOC(subcmd);
+ if (sc == NULL)
+ fatal("ran out of memory\n");
+ sc->sc_type = type;
+ sc->sc_args = NULL;
+ sc->sc_next = NULL;
+ sc->sc_name = NULL;
+ return(sc);
+}
diff --git a/usr.bin/rdist/lookup.c b/usr.bin/rdist/lookup.c
new file mode 100644
index 0000000..9819e68
--- /dev/null
+++ b/usr.bin/rdist/lookup.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lookup.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+ /* symbol types */
+#define VAR 1
+#define CONST 2
+
+struct syment {
+ int s_type;
+ char *s_name;
+ struct namelist *s_value;
+ struct syment *s_next;
+};
+
+static struct syment *hashtab[HASHSIZE];
+
+/*
+ * Define a variable from a command line argument.
+ */
+void
+define(name)
+ char *name;
+{
+ register char *cp, *s;
+ register struct namelist *nl;
+ struct namelist *value;
+
+ if (debug)
+ printf("define(%s)\n", name);
+
+ cp = index(name, '=');
+ if (cp == NULL)
+ value = NULL;
+ else if (cp[1] == '\0') {
+ *cp = '\0';
+ value = NULL;
+ } else if (cp[1] != '(') {
+ *cp++ = '\0';
+ value = makenl(cp);
+ } else {
+ nl = NULL;
+ *cp++ = '\0';
+ do
+ cp++;
+ while (*cp == ' ' || *cp == '\t');
+ for (s = cp; ; s++) {
+ switch (*s) {
+ case ')':
+ *s = '\0';
+ case '\0':
+ break;
+ case ' ':
+ case '\t':
+ *s++ = '\0';
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ')')
+ *s = '\0';
+ break;
+ default:
+ continue;
+ }
+ if (nl == NULL)
+ value = nl = makenl(cp);
+ else {
+ nl->n_next = makenl(cp);
+ nl = nl->n_next;
+ }
+ if (*s == '\0')
+ break;
+ cp = s;
+ }
+ }
+ (void) lookup(name, REPLACE, value);
+}
+
+/*
+ * Lookup name in the table and return a pointer to it.
+ * LOOKUP - just do lookup, return NULL if not found.
+ * INSERT - insert name with value, error if already defined.
+ * REPLACE - insert or replace name with value.
+ */
+
+struct namelist *
+lookup(name, action, value)
+ char *name;
+ int action;
+ struct namelist *value;
+{
+ register unsigned n;
+ register char *cp;
+ register struct syment *s;
+ char buf[256];
+
+ if (debug)
+ printf("lookup(%s, %d, %x)\n", name, action, value);
+
+ n = 0;
+ for (cp = name; *cp; )
+ n += *cp++;
+ n %= HASHSIZE;
+
+ for (s = hashtab[n]; s != NULL; s = s->s_next) {
+ if (strcmp(name, s->s_name))
+ continue;
+ if (action != LOOKUP) {
+ if (action != INSERT || s->s_type != CONST) {
+ (void)sprintf(buf, "%s redefined", name);
+ yyerror(buf);
+ }
+ }
+ return(s->s_value);
+ }
+
+ if (action == LOOKUP) {
+ (void)sprintf(buf, "%s undefined", name);
+ yyerror(buf);
+ return(NULL);
+ }
+
+ s = ALLOC(syment);
+ if (s == NULL)
+ fatal("ran out of memory\n");
+ s->s_next = hashtab[n];
+ hashtab[n] = s;
+ s->s_type = action == INSERT ? VAR : CONST;
+ s->s_name = name;
+ s->s_value = value;
+ return(value);
+}
diff --git a/usr.bin/rdist/main.c b/usr.bin/rdist/main.c
new file mode 100644
index 0000000..44b3279a
--- /dev/null
+++ b/usr.bin/rdist/main.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+#define NHOSTS 100
+
+/*
+ * Remote distribution program.
+ */
+
+char *distfile = NULL;
+#define _RDIST_TMP "/rdistXXXXXX"
+char tempfile[sizeof _PATH_TMP + sizeof _RDIST_TMP + 1];
+char *tempname;
+
+int debug; /* debugging flag */
+int nflag; /* NOP flag, just print commands without executing */
+int qflag; /* Quiet. Don't print messages */
+int options; /* global options */
+int iamremote; /* act as remote server for transfering files */
+
+FILE *fin = NULL; /* input file pointer */
+int rem = -1; /* file descriptor to remote source/sink process */
+char host[32]; /* host name */
+int nerrs; /* number of errors while sending/receiving */
+char user[10]; /* user's name */
+char homedir[128]; /* user's home directory */
+int userid; /* user's user ID */
+int groupid; /* user's group ID */
+
+struct passwd *pw; /* pointer to static area used by getpwent */
+struct group *gr; /* pointer to static area used by getgrent */
+
+static void usage __P((void));
+static void docmdargs __P((int, char *[]));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ int cmdargs = 0;
+ char *dhosts[NHOSTS], **hp = dhosts;
+
+ pw = getpwuid(userid = getuid());
+ if (pw == NULL) {
+ fprintf(stderr, "%s: Who are you?\n", argv[0]);
+ exit(1);
+ }
+ strcpy(user, pw->pw_name);
+ strcpy(homedir, pw->pw_dir);
+ groupid = pw->pw_gid;
+ gethostname(host, sizeof(host));
+ strcpy(tempfile, _PATH_TMP);
+ strcat(tempfile, _RDIST_TMP);
+ if ((tempname = rindex(tempfile, '/')) != 0)
+ tempname++;
+ else
+ tempname = tempfile;
+
+ while (--argc > 0) {
+ if ((arg = *++argv)[0] != '-')
+ break;
+ if (!strcmp(arg, "-Server"))
+ iamremote++;
+ else while (*++arg)
+ switch (*arg) {
+ case 'f':
+ if (--argc <= 0)
+ usage();
+ distfile = *++argv;
+ if (distfile[0] == '-' && distfile[1] == '\0')
+ fin = stdin;
+ break;
+
+ case 'm':
+ if (--argc <= 0)
+ usage();
+ if (hp >= &dhosts[NHOSTS-2]) {
+ fprintf(stderr, "rdist: too many destination hosts\n");
+ exit(1);
+ }
+ *hp++ = *++argv;
+ break;
+
+ case 'd':
+ if (--argc <= 0)
+ usage();
+ define(*++argv);
+ break;
+
+ case 'D':
+ debug++;
+ break;
+
+ case 'c':
+ cmdargs++;
+ break;
+
+ case 'n':
+ if (options & VERIFY) {
+ printf("rdist: -n overrides -v\n");
+ options &= ~VERIFY;
+ }
+ nflag++;
+ break;
+
+ case 'q':
+ qflag++;
+ break;
+
+ case 'b':
+ options |= COMPARE;
+ break;
+
+ case 'R':
+ options |= REMOVE;
+ break;
+
+ case 'v':
+ if (nflag) {
+ printf("rdist: -n overrides -v\n");
+ break;
+ }
+ options |= VERIFY;
+ break;
+
+ case 'w':
+ options |= WHOLE;
+ break;
+
+ case 'y':
+ options |= YOUNGER;
+ break;
+
+ case 'h':
+ options |= FOLLOW;
+ break;
+
+ case 'i':
+ options |= IGNLNKS;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ *hp = NULL;
+
+ seteuid(userid);
+ mktemp(tempfile);
+
+ if (iamremote) {
+ server();
+ exit(nerrs != 0);
+ }
+
+ if (cmdargs)
+ docmdargs(argc, argv);
+ else {
+ if (fin == NULL) {
+ if(distfile == NULL) {
+ if((fin = fopen("distfile","r")) == NULL)
+ fin = fopen("Distfile", "r");
+ } else
+ fin = fopen(distfile, "r");
+ if(fin == NULL) {
+ perror(distfile ? distfile : "distfile");
+ exit(1);
+ }
+ }
+ yyparse();
+ if (nerrs == 0)
+ docmds(dhosts, argc, argv);
+ }
+
+ exit(nerrs != 0);
+}
+
+static void
+usage()
+{
+ printf("Usage: rdist [-nqbhirvwyD] [-f distfile] [-d var=value] [-m host] [file ...]\n");
+ printf("or: rdist [-nqbhirvwyD] -c source [...] machine[:dest]\n");
+ exit(1);
+}
+
+/*
+ * rcp like interface for distributing files.
+ */
+static void
+docmdargs(nargs, args)
+ int nargs;
+ char *args[];
+{
+ register struct namelist *nl, *prev;
+ register char *cp;
+ struct namelist *files, *hosts;
+ struct subcmd *cmds;
+ char *dest;
+ static struct namelist tnl = { NULL, NULL };
+ int i;
+
+ if (nargs < 2)
+ usage();
+
+ prev = NULL;
+ for (i = 0; i < nargs - 1; i++) {
+ nl = makenl(args[i]);
+ if (prev == NULL)
+ files = prev = nl;
+ else {
+ prev->n_next = nl;
+ prev = nl;
+ }
+ }
+
+ cp = args[i];
+ if ((dest = index(cp, ':')) != NULL)
+ *dest++ = '\0';
+ tnl.n_name = cp;
+ hosts = expand(&tnl, E_ALL);
+ if (nerrs)
+ exit(1);
+
+ if (dest == NULL || *dest == '\0')
+ cmds = NULL;
+ else {
+ cmds = makesubcmd(INSTALL);
+ cmds->sc_options = options;
+ cmds->sc_name = dest;
+ }
+
+ if (debug) {
+ printf("docmdargs()\nfiles = ");
+ prnames(files);
+ printf("hosts = ");
+ prnames(hosts);
+ }
+ insert(NULL, files, hosts, cmds);
+ docmds(NULL, 0, NULL);
+}
+
+/*
+ * Print a list of NAME blocks (mostly for debugging).
+ */
+void
+prnames(nl)
+ register struct namelist *nl;
+{
+ printf("( ");
+ while (nl != NULL) {
+ printf("%s ", nl->n_name);
+ nl = nl->n_next;
+ }
+ printf(")\n");
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ extern int yylineno;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "rdist: line %d: Warning: ", yylineno);
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+ va_end(ap);
+}
diff --git a/usr.bin/rdist/pathnames.h b/usr.bin/rdist/pathnames.h
new file mode 100644
index 0000000..2e1b067
--- /dev/null
+++ b/usr.bin/rdist/pathnames.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ */
+
+#include <paths.h>
+
+#define _PATH_RDIST "rdist"
diff --git a/usr.bin/rdist/rdist.1 b/usr.bin/rdist/rdist.1
new file mode 100644
index 0000000..00a3339
--- /dev/null
+++ b/usr.bin/rdist/rdist.1
@@ -0,0 +1,413 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rdist.1 8.3 (Berkeley) 3/17/94
+.\"
+.Dd March 17, 1994
+.Dt RDIST 1
+.Os BSD 4.3
+.Sh NAME
+.Nm rdist
+.Nd remote file distribution program
+.Sh SYNOPSIS
+.Nm rdist
+.Op Fl nqbRhivwy
+.Op Fl f Ar distfile
+.Op Fl d Ar var=value
+.Op Fl m Ar host
+.Op Ar name ...
+.Nm rdist
+.Op Fl nqbRhivwy
+.Fl c
+.Ar name ...
+.Oo login@ Oc Ns Ar host Ns Op :dest
+.Sh DESCRIPTION
+.Nm Rdist
+is a program to maintain identical copies of files over multiple hosts.
+It preserves the owner, group, mode, and mtime of files if possible and
+can update programs that are executing.
+.Nm Rdist
+reads commands from
+.Ar distfile
+to direct the updating of files and/or directories.
+.Pp
+Options specific to the first SYNOPSIS form:
+.Pp
+.Bl -tag -width indent
+.It Fl
+If
+.Ar distfile
+is
+.Sq Fl ,
+the standard input is used.
+.It Fl f Ar distfile
+Use the specified
+.Ar distfile.
+.El
+.Pp
+If either the
+.Fl f
+or
+.Sq Fl
+option is not specified, the program looks first for
+.Dq Pa distfile ,
+then
+.Dq Pa Distfile
+to use as the input.
+If no names are specified on the command line,
+.Nm rdist
+will update all of the files and directories listed in
+.Ar distfile .
+Otherwise, the argument is taken to be the name of a file to be updated
+or the label of a command to execute. If label and file names conflict,
+it is assumed to be a label.
+These may be used together to update specific files
+using specific commands.
+.Pp
+Options specific to the second SYNOPSIS form:
+.Pp
+.Bl -tag -width Fl c
+.It Fl c
+Forces
+.Nm rdist
+to interpret the remaining arguments as a small
+.Ar distfile .
+.Pp
+The equivalent distfile is as follows.
+.Pp
+.Bd -filled -offset indent -compact
+.Pq Ar name ...
+.Li ->
+.Op Ar login@
+.Ar host
+.Bd -filled -offset indent -compact
+.Li install
+.Op Ar dest ;
+.Ed
+.Ed
+.El
+.Pp
+Options common to both forms:
+.Pp
+.Bl -tag -width Ic
+.It Fl b
+Binary comparison. Perform a binary comparison and update files if they differ
+rather than comparing dates and sizes.
+.It Fl d Ar var=value
+Define
+.Ar var
+to have
+.Ar value .
+The
+.Fl d
+option is used to define or override variable definitions in the
+.Ar distfile .
+.Ar Value
+can be the empty string, one name, or a list of names surrounded by
+parentheses and separated by tabs and/or spaces.
+.It Fl h
+Follow symbolic links. Copy the file that the link points to rather than the
+link itself.
+.It Fl i
+Ignore unresolved links.
+.Nm Rdist
+will normally try to maintain the link structure of files being transferred
+and warn the user if all the links cannot be found.
+.It Fl m Ar host
+Limit which machines are to be updated. Multiple
+.Fl m
+arguments can be given to limit updates to a subset of the hosts listed in the
+.Ar distfile .
+.It Fl n
+Print the commands without executing them. This option is
+useful for debugging
+.Ar distfile .
+.It Fl q
+Quiet mode. Files that are being modified are normally
+printed on standard output. The
+.Fl q
+option suppresses this.
+.It Fl R
+Remove extraneous files. If a directory is being updated, any files that exist
+on the remote host that do not exist in the master directory are removed.
+This is useful for maintaining truly identical copies of directories.
+.It Fl v
+Verify that the files are up to date on all the hosts. Any files
+that are out of date will be displayed but no files will be changed
+nor any mail sent.
+.It Fl w
+Whole mode. The whole file name is appended to the destination directory
+name. Normally, only the last component of a name is used when renaming files.
+This will preserve the directory structure of the files being
+copied instead of flattening the directory structure. For example,
+renaming a list of files such as ( dir1/f1 dir2/f2 ) to dir3 would create
+files dir3/dir1/f1 and dir3/dir2/f2 instead of dir3/f1 and dir3/f2.
+.It Fl y
+Younger mode. Files are normally updated if their
+.Ar mtime
+and
+.Ar size
+(see
+.Xr stat 2 )
+disagree. The
+.Fl y
+option causes
+.Nm rdist
+not to update files that are younger than the master copy.
+This can be used
+to prevent newer copies on other hosts from being replaced.
+A warning message is printed for files which are newer than the master copy.
+.El
+.Pp
+.Ar Distfile
+contains a sequence of entries that specify the files
+to be copied, the destination hosts, and what operations to perform
+to do the updating. Each entry has one of the following formats.
+.Pp
+.Bd -literal -offset indent -compact
+<variable name> `=' <name list>
+[label:]<source list> `\->' <destination list> <command list>
+[label:]<source list> `::' <time_stamp file> <command list>
+.Ed
+.Pp
+The first format is used for defining variables.
+The second format is used for distributing files to other hosts.
+The third format is used for making lists of files that have been changed
+since some given date.
+The
+.Ar source list
+specifies a
+list of files and/or directories on the local host which are to be used
+as the master copy for distribution.
+The
+.Ar destination list
+is the list of hosts to which these files are to be
+copied. Each file in the source list is added to a list of changes
+if the file is out of date on the host which is being updated (second format) or
+the file is newer than the time stamp file (third format).
+.Pp
+Labels are optional. They are used to identify a command for partial updates.
+.Pp
+Newlines, tabs, and blanks are only used as separators and are
+otherwise ignored. Comments begin with `#' and end with a newline.
+.Pp
+Variables to be expanded begin with `$' followed by one character or
+a name enclosed in curly braces (see the examples at the end).
+.Pp
+The source and destination lists have the following format:
+.Bd -literal -offset indent
+<name>
+.Ed
+or
+.Bd -literal -offset indent -compact
+`(' <zero or more names separated by white-space> `)'
+.Ed
+.Pp
+The shell meta-characters `[', `]', `{', `}', `*', and `?'
+are recognized and expanded (on the local host only) in the same way as
+.Xr csh 1 .
+They can be escaped with a backslash.
+The `~' character is also expanded in the same way as
+.Xr csh 1
+but is expanded separately on the local and destination hosts.
+When the
+.Fl w
+option is used with a file name that begins with `~', everything except the
+home directory is appended to the destination name.
+File names which do not begin with `/' or `~' use the destination user's
+home directory as the root directory for the rest of the file name.
+.Pp
+The command list consists of zero or more commands of the following
+format.
+.Bd -ragged -offset indent -compact
+.Bl -column except_patx pattern\ listx
+.It `install' <options> opt_dest_name `;'
+.It `notify' <name list> `;'
+.It `except' <name list> `;'
+.It `except_pat' <pattern list> `;'
+.It `special' <name list> string `;'
+.El
+.Ed
+.Pp
+The
+.Ic install
+command is used to copy out of date files and/or directories.
+Each source file is copied to each host in the destination list.
+Directories are recursively copied in the same way.
+.Ar Opt_dest_name
+is an optional parameter to rename files.
+If no
+.Ic install
+command appears in the command list or
+the destination name is not specified,
+the source file name is used.
+Directories in the path name will be created if they
+do not exist on the remote host.
+To help prevent disasters, a non-empty directory on a target host will
+never be replaced with a regular file or a symbolic link.
+However, under the `\-R' option a non-empty directory will be removed
+if the corresponding filename is completely absent on the master host.
+The
+.Ar options
+are `\-R', `\-h', `\-i', `\-v', `\-w', `\-y', and `\-b'
+and have the same semantics as
+options on the command line except they only apply to the files
+in the source list.
+The login name used on the destination host is the same as the local host
+unless the destination name is of the format ``login@host".
+.Pp
+The
+.Ic notify
+command is used to mail the list of files updated (and any errors
+that may have occurred) to the listed names.
+If no `@' appears in the name, the destination host is appended to
+the name
+(e.g., name1@host, name2@host, ...).
+.Pp
+The
+.Ic except
+command is used to update all of the files in the source list
+.Ic except
+for the files listed in
+.Ar name list .
+This is usually used to copy everything in a directory except certain files.
+.Pp
+The
+.Ic except_pat
+command is like the
+.Ic except
+command except that
+.Ar pattern list
+is a list of regular expressions
+(see
+.Xr re_format 7
+for details).
+If one of the patterns matches some string within a file name, that file will
+be ignored.
+Note that since `\e' is a quote character, it must be doubled to become
+part of the regular expression. Variables are expanded in
+.Ar pattern list
+but not shell file pattern matching characters. To include a `$', it
+must be escaped with `\e'.
+.Pp
+The
+.Ic special
+command is used to specify
+.Xr sh 1
+commands that are to be executed on the
+remote host after the file in
+.Ar name list
+is updated or installed.
+If the
+.Ar name list
+is omitted then the shell commands will be executed
+for every file updated or installed. The shell variable `FILE' is set
+to the current filename before executing the commands in
+.Ar string .
+.Ar String
+starts and ends with `"' and can cross multiple lines in
+.Ar distfile .
+Multiple commands to the shell should be separated by `;'.
+Commands are executed in the user's home directory on the host
+being updated.
+The
+.Ar special
+command can be used to rebuild private databases, etc.
+after a program has been updated.
+.Pp
+The following is a small example:
+.Bd -literal -offset indent
+HOSTS = ( matisse root@arpa )
+
+FILES = ( /bin /lib /usr/bin /usr/games
+\t/usr/include/{*.h,{stand,sys,vax*,pascal,machine}/*.h}
+\t/usr/lib /usr/man/man? /usr/ucb /usr/local/rdist )
+
+EXLIB = ( Mail.rc aliases aliases.dir aliases.pag crontab dshrc
+\tsendmail.cf sendmail.fc sendmail.hf sendmail.st uucp vfont )
+
+${FILES} -> ${HOSTS}
+\tinstall -R ;
+\texcept /usr/lib/${EXLIB} ;
+\texcept /usr/games/lib ;
+\tspecial /usr/lib/sendmail "/usr/lib/sendmail -bz" ;
+
+srcs:
+/usr/src/bin -> arpa
+\texcept_pat ( \e\e.o\e$ /SCCS\e$ ) ;
+
+IMAGEN = (ips dviimp catdvi)
+
+imagen:
+/usr/local/${IMAGEN} -> arpa
+\tinstall /usr/local/lib ;
+\tnotify ralph ;
+
+${FILES} :: stamp.cory
+\tnotify root@cory ;
+.Ed
+.Sh FILES
+.Bl -tag -width /tmp/rdist* -compact
+.It Pa distfile
+input command file
+.It Pa /tmp/rdist*
+temporary file for update lists
+.El
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr csh 1 ,
+.Xr stat 2 ,
+.Xr re_format 7
+.Sh HISTORY
+The
+.Nm rdist
+command appeared in
+.Bx 4.3 .
+.Sh DIAGNOSTICS
+A complaint about mismatch of rdist version numbers may really stem
+from some problem with starting your shell, e.g., you are in too many groups.
+.Sh BUGS
+Source files must reside on the local host where
+.Nm rdist
+is executed.
+.Pp
+There is no easy way to have a special command executed after all files
+in a directory have been updated.
+.Pp
+Variable expansion only works for name lists; there should be a general macro
+facility.
+.Pp
+.Nm Rdist
+aborts on files which have a negative mtime (before Jan 1, 1970).
+.Pp
+There should be a `force' option to allow replacement of non-empty directories
+by regular files or symlinks. A means of updating file modes and owners
+of otherwise identical files is also needed.
diff --git a/usr.bin/rdist/server.c b/usr.bin/rdist/server.c
new file mode 100644
index 0000000..0347052
--- /dev/null
+++ b/usr.bin/rdist/server.c
@@ -0,0 +1,1584 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)server.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/wait.h>
+#include "defs.h"
+
+#define ack() (void) write(rem, "\0\n", 2)
+#define err() (void) write(rem, "\1\n", 2)
+
+struct linkbuf *ihead; /* list of files with more than one link */
+char buf[BUFSIZ]; /* general purpose buffer */
+char target[BUFSIZ]; /* target/source directory name */
+char *tp; /* pointer to end of target name */
+char *Tdest; /* pointer to last T dest*/
+int catname; /* cat name to target name */
+char *stp[32]; /* stack of saved tp's for directories */
+int oumask; /* old umask for creating files */
+
+extern FILE *lfp; /* log file for mailing changes */
+
+static int chkparent __P((char *));
+static void clean __P((char *));
+static void comment __P((char *));
+static void dospecial __P((char *));
+static int fchog __P((int, char *, char *, char *, int));
+static void hardlink __P((char *));
+static void note __P((const char *, ...));
+static void query __P((char *));
+static void recvf __P((char *, int));
+static void removeit __P((struct stat *));
+static int response __P((void));
+static void rmchk __P((int));
+static struct linkbuf *
+ savelink __P((struct stat *));
+static void sendf __P((char *, int));
+static int update __P((char *, int, struct stat *));
+
+/*
+ * Server routine to read requests and process them.
+ * Commands are:
+ * Tname - Transmit file if out of date
+ * Vname - Verify if file out of date or not
+ * Qname - Query if file exists. Return mtime & size if it does.
+ */
+void
+server()
+{
+ char cmdbuf[BUFSIZ];
+ register char *cp;
+
+ signal(SIGHUP, cleanup);
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGPIPE, cleanup);
+
+ rem = 0;
+ oumask = umask(0);
+ (void) sprintf(buf, "V%d\n", VERSION);
+ (void) write(rem, buf, strlen(buf));
+
+ for (;;) {
+ cp = cmdbuf;
+ if (read(rem, cp, 1) <= 0)
+ return;
+ if (*cp++ == '\n') {
+ error("server: expected control record\n");
+ continue;
+ }
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup(0);
+ } while (*cp++ != '\n' && cp < &cmdbuf[BUFSIZ]);
+ *--cp = '\0';
+ cp = cmdbuf;
+ switch (*cp++) {
+ case 'T': /* init target file/directory name */
+ catname = 1; /* target should be directory */
+ goto dotarget;
+
+ case 't': /* init target file/directory name */
+ catname = 0;
+ dotarget:
+ if (exptilde(target, cp) == NULL)
+ continue;
+ tp = target;
+ while (*tp)
+ tp++;
+ ack();
+ continue;
+
+ case 'R': /* Transfer a regular file. */
+ recvf(cp, S_IFREG);
+ continue;
+
+ case 'D': /* Transfer a directory. */
+ recvf(cp, S_IFDIR);
+ continue;
+
+ case 'K': /* Transfer symbolic link. */
+ recvf(cp, S_IFLNK);
+ continue;
+
+ case 'k': /* Transfer hard link. */
+ hardlink(cp);
+ continue;
+
+ case 'E': /* End. (of directory) */
+ *tp = '\0';
+ if (catname <= 0) {
+ error("server: too many 'E's\n");
+ continue;
+ }
+ tp = stp[--catname];
+ *tp = '\0';
+ ack();
+ continue;
+
+ case 'C': /* Clean. Cleanup a directory */
+ clean(cp);
+ continue;
+
+ case 'Q': /* Query. Does the file/directory exist? */
+ query(cp);
+ continue;
+
+ case 'S': /* Special. Execute commands */
+ dospecial(cp);
+ continue;
+
+#ifdef notdef
+ /*
+ * These entries are reserved but not currently used.
+ * The intent is to allow remote hosts to have master copies.
+ * Currently, only the host rdist runs on can have masters.
+ */
+ case 'X': /* start a new list of files to exclude */
+ except = bp = NULL;
+ case 'x': /* add name to list of files to exclude */
+ if (*cp == '\0') {
+ ack();
+ continue;
+ }
+ if (*cp == '~') {
+ if (exptilde(buf, cp) == NULL)
+ continue;
+ cp = buf;
+ }
+ if (bp == NULL)
+ except = bp = expand(makeblock(NAME, cp), E_VARS);
+ else
+ bp->b_next = expand(makeblock(NAME, cp), E_VARS);
+ while (bp->b_next != NULL)
+ bp = bp->b_next;
+ ack();
+ continue;
+
+ case 'I': /* Install. Transfer file if out of date. */
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("server: options not delimited\n");
+ return;
+ }
+ install(cp, opts);
+ continue;
+
+ case 'L': /* Log. save message in log file */
+ log(lfp, cp);
+ continue;
+#endif
+
+ case '\1':
+ nerrs++;
+ continue;
+
+ case '\2':
+ return;
+
+ default:
+ error("server: unknown command '%s'\n", cp);
+ case '\0':
+ continue;
+ }
+ }
+}
+
+/*
+ * Update the file(s) if they are different.
+ * destdir = 1 if destination should be a directory
+ * (i.e., more than one source is being copied to the same destination).
+ */
+void
+install(src, dest, destdir, opts)
+ char *src, *dest;
+ int destdir, opts;
+{
+ char *rname;
+ char destcopy[BUFSIZ];
+
+ if (dest == NULL) {
+ opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
+ dest = src;
+ }
+
+ if (nflag || debug) {
+ printf("%s%s%s%s%s %s %s\n", opts & VERIFY ? "verify":"install",
+ opts & WHOLE ? " -w" : "",
+ opts & YOUNGER ? " -y" : "",
+ opts & COMPARE ? " -b" : "",
+ opts & REMOVE ? " -R" : "", src, dest);
+ if (nflag)
+ return;
+ }
+
+ rname = exptilde(target, src);
+ if (rname == NULL)
+ return;
+ tp = target;
+ while (*tp)
+ tp++;
+ /*
+ * If we are renaming a directory and we want to preserve
+ * the directory heirarchy (-w), we must strip off the leading
+ * directory name and preserve the rest.
+ */
+ if (opts & WHOLE) {
+ while (*rname == '/')
+ rname++;
+ destdir = 1;
+ } else {
+ rname = rindex(target, '/');
+ if (rname == NULL)
+ rname = target;
+ else
+ rname++;
+ }
+ if (debug)
+ printf("target = %s, rname = %s\n", target, rname);
+ /*
+ * Pass the destination file/directory name to remote.
+ */
+ (void) sprintf(buf, "%c%s\n", destdir ? 'T' : 't', dest);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+
+ if (destdir) {
+ strcpy(destcopy, dest);
+ Tdest = destcopy;
+ }
+ sendf(rname, opts);
+ Tdest = 0;
+}
+
+#define protoname() (pw ? pw->pw_name : user)
+#define protogroup() (gr ? gr->gr_name : group)
+/*
+ * Transfer the file or directory in target[].
+ * rname is the name of the file on the remote host.
+ */
+static void
+sendf(rname, opts)
+ char *rname;
+ int opts;
+{
+ register struct subcmd *sc;
+ struct stat stb;
+ int sizerr, f, u, len;
+ off_t i;
+ DIR *d;
+ struct direct *dp;
+ char *otp, *cp;
+ extern struct subcmd *subcmds;
+ static char user[15], group[15];
+
+ if (debug)
+ printf("sendf(%s, %x)\n", rname, opts);
+
+ if (except(target))
+ return;
+ if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ if ((u = update(rname, opts, &stb)) == 0) {
+ if ((stb.st_mode & S_IFMT) == S_IFREG && stb.st_nlink > 1)
+ (void) savelink(&stb);
+ return;
+ }
+
+ if (pw == NULL || pw->pw_uid != stb.st_uid)
+ if ((pw = getpwuid(stb.st_uid)) == NULL) {
+ log(lfp, "%s: no password entry for uid %d \n",
+ target, stb.st_uid);
+ pw = NULL;
+ (void)sprintf(user, ":%lu", stb.st_uid);
+ }
+ if (gr == NULL || gr->gr_gid != stb.st_gid)
+ if ((gr = getgrgid(stb.st_gid)) == NULL) {
+ log(lfp, "%s: no name for group %d\n",
+ target, stb.st_gid);
+ gr = NULL;
+ (void)sprintf(group, ":%lu", stb.st_gid);
+ }
+ if (u == 1) {
+ if (opts & VERIFY) {
+ log(lfp, "need to install: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "installing: %s\n", target);
+ opts &= ~(COMPARE|REMOVE);
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ if ((d = opendir(target)) == NULL) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
+ stb.st_mode & 07777, protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ closedir(d);
+ return;
+ }
+
+ if (opts & REMOVE)
+ rmchk(opts);
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") ||
+ !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ error("%s/%s: Name too long\n", target,
+ dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ sendf(dp->d_name, opts);
+ }
+ closedir(d);
+ (void) write(rem, "E\n", 2);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+ return;
+
+ case S_IFLNK:
+ if (u != 1)
+ opts |= COMPARE;
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ (void) sprintf(buf, "k%o %s %s\n", opts,
+ lp->pathname, rname);
+ else
+ (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+ lp->target, lp->pathname, rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ (void) response();
+ return;
+ }
+ }
+ (void) sprintf(buf, "K%o %o %qd %ld %s %s %s\n", opts,
+ stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+ sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
+ (void) write(rem, buf, stb.st_size);
+ if (debug)
+ printf("readlink = %.*s\n", (int)stb.st_size, buf);
+ goto done;
+
+ case S_IFREG:
+ break;
+
+ default:
+ error("%s: not a file or directory\n", target);
+ return;
+ }
+
+ if (u == 2) {
+ if (opts & VERIFY) {
+ log(lfp, "need to update: %s\n", target);
+ goto dospecial;
+ }
+ log(lfp, "updating: %s\n", target);
+ }
+
+ if (stb.st_nlink > 1) {
+ struct linkbuf *lp;
+
+ if ((lp = savelink(&stb)) != NULL) {
+ /* install link */
+ if (*lp->target == 0)
+ (void) sprintf(buf, "k%o %s %s\n", opts,
+ lp->pathname, rname);
+ else
+ (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+ lp->target, lp->pathname, rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ (void) response();
+ return;
+ }
+ }
+
+ if ((f = open(target, O_RDONLY, 0)) < 0) {
+ error("%s: %s\n", target, strerror(errno));
+ return;
+ }
+ (void) sprintf(buf, "R%o %o %qd %ld %s %s %s\n", opts,
+ stb.st_mode & 07777, stb.st_size, stb.st_mtime,
+ protoname(), protogroup(), rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ return;
+ }
+ sizerr = 0;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ (void) write(rem, buf, amt);
+ }
+ (void) close(f);
+done:
+ if (sizerr) {
+ error("%s: file changed size\n", target);
+ err();
+ } else
+ ack();
+ f = response();
+ if (f < 0 || f == 0 && (opts & COMPARE))
+ return;
+dospecial:
+ for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
+ if (sc->sc_type != SPECIAL)
+ continue;
+ if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
+ continue;
+ log(lfp, "special \"%s\"\n", sc->sc_name);
+ if (opts & VERIFY)
+ continue;
+ (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ while (response() > 0)
+ ;
+ }
+}
+
+static struct linkbuf *
+savelink(stp)
+ struct stat *stp;
+{
+ struct linkbuf *lp;
+
+ for (lp = ihead; lp != NULL; lp = lp->nextp)
+ if (lp->inum == stp->st_ino && lp->devnum == stp->st_dev) {
+ lp->count--;
+ return(lp);
+ }
+ lp = (struct linkbuf *) malloc(sizeof(*lp));
+ if (lp == NULL)
+ log(lfp, "out of memory, link information lost\n");
+ else {
+ lp->nextp = ihead;
+ ihead = lp;
+ lp->inum = stp->st_ino;
+ lp->devnum = stp->st_dev;
+ lp->count = stp->st_nlink - 1;
+ strcpy(lp->pathname, target);
+ if (Tdest)
+ strcpy(lp->target, Tdest);
+ else
+ *lp->target = 0;
+ }
+ return(NULL);
+}
+
+/*
+ * Check to see if file needs to be updated on the remote machine.
+ * Returns 0 if no update, 1 if remote doesn't exist, 2 if out of date
+ * and 3 if comparing binaries to determine if out of date.
+ */
+static int
+update(rname, opts, stp)
+ char *rname;
+ int opts;
+ struct stat *stp;
+{
+ register char *cp, *s;
+ register off_t size;
+ register time_t mtime;
+
+ if (debug)
+ printf("update(%s, %x, %x)\n", rname, opts, stp);
+
+ /*
+ * Check to see if the file exists on the remote machine.
+ */
+ (void) sprintf(buf, "Q%s\n", rname);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+again:
+ cp = s = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+ switch (*s++) {
+ case 'Y':
+ break;
+
+ case 'N': /* file doesn't exist so install it */
+ return(1);
+
+ case '\1':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ return(0);
+
+ case '\3':
+ *--cp = '\0';
+ if (lfp != NULL)
+ log(lfp, "update: note: %s\n", s);
+ goto again;
+
+ default:
+ *--cp = '\0';
+ error("update: unexpected response '%s'\n", s);
+ return(0);
+ }
+
+ if (*s == '\n')
+ return(2);
+
+ if (opts & COMPARE)
+ return(3);
+
+ size = 0;
+ while (isdigit(*s))
+ size = size * 10 + (*s++ - '0');
+ if (*s++ != ' ') {
+ error("update: size not delimited\n");
+ return(0);
+ }
+ mtime = 0;
+ while (isdigit(*s))
+ mtime = mtime * 10 + (*s++ - '0');
+ if (*s != '\n') {
+ error("update: mtime not delimited\n");
+ return(0);
+ }
+ /*
+ * File needs to be updated?
+ */
+ if (opts & YOUNGER) {
+ if (stp->st_mtime == mtime)
+ return(0);
+ if (stp->st_mtime < mtime) {
+ log(lfp, "Warning: %s: remote copy is newer\n", target);
+ return(0);
+ }
+ } else if (stp->st_mtime == mtime && stp->st_size == size)
+ return(0);
+ return(2);
+}
+
+/*
+ * Query. Check to see if file exists. Return one of the following:
+ * N\n - doesn't exist
+ * Ysize mtime\n - exists and its a regular file (size & mtime of file)
+ * Y\n - exists and its a directory or symbolic link
+ * ^Aerror message\n
+ */
+static void
+query(name)
+ char *name;
+{
+ struct stat stb;
+
+ if (catname)
+ (void) sprintf(tp, "/%s", name);
+
+ if (lstat(target, &stb) < 0) {
+ if (errno == ENOENT)
+ (void) write(rem, "N\n", 2);
+ else
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ *tp = '\0';
+ return;
+ }
+
+ switch (stb.st_mode & S_IFMT) {
+ case S_IFREG:
+ (void) sprintf(buf, "Y%qd %ld\n", stb.st_size, stb.st_mtime);
+ (void) write(rem, buf, strlen(buf));
+ break;
+
+ case S_IFLNK:
+ case S_IFDIR:
+ (void) write(rem, "Y\n", 2);
+ break;
+
+ default:
+ error("%s: not a file or directory\n", name);
+ break;
+ }
+ *tp = '\0';
+}
+
+static void
+recvf(cmd, type)
+ char *cmd;
+ int type;
+{
+ register char *cp;
+ int f, mode, opts, wrerr, olderrno;
+ off_t i, size;
+ time_t mtime;
+ struct stat stb;
+ struct timeval tvp[2];
+ char *owner, *group;
+ char new[BUFSIZ];
+ extern char *tempname;
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: options not delimited\n");
+ return;
+ }
+ mode = 0;
+ while (*cp >= '0' && *cp <= '7')
+ mode = (mode << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mode not delimited\n");
+ return;
+ }
+ size = 0;
+ while (isdigit(*cp))
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: size not delimited\n");
+ return;
+ }
+ mtime = 0;
+ while (isdigit(*cp))
+ mtime = mtime * 10 + (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("recvf: mtime not delimited\n");
+ return;
+ }
+ owner = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: owner name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+ group = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("recvf: group name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (type == S_IFDIR) {
+ if (catname >= sizeof(stp)) {
+ error("%s:%s: too many directory levels\n",
+ host, target);
+ return;
+ }
+ stp[catname] = tp;
+ if (catname++) {
+ *tp++ = '/';
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ }
+ if (opts & VERIFY) {
+ ack();
+ return;
+ }
+ if (lstat(target, &stb) == 0) {
+ if (ISDIR(stb.st_mode)) {
+ if ((stb.st_mode & 07777) == mode) {
+ ack();
+ return;
+ }
+ buf[0] = '\0';
+ (void) sprintf(buf + 1,
+ "%s: Warning: remote mode %o != local mode %o\n",
+ target, stb.st_mode & 07777, mode);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ return;
+ }
+ errno = ENOTDIR;
+ } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
+ chkparent(target) == 0 && mkdir(target, mode) == 0)) {
+ if (fchog(-1, target, owner, group, mode) == 0)
+ ack();
+ return;
+ }
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ tp = stp[--catname];
+ *tp = '\0';
+ return;
+ }
+
+ if (catname)
+ (void) sprintf(tp, "/%s", cp);
+ cp = rindex(target, '/');
+ if (cp == NULL)
+ strcpy(new, tempname);
+ else if (cp == target)
+ (void) sprintf(new, "/%s", tempname);
+ else {
+ *cp = '\0';
+ (void) sprintf(new, "%s/%s", target, tempname);
+ *cp = '/';
+ }
+
+ if (type == S_IFLNK) {
+ int j;
+
+ ack();
+ cp = buf;
+ for (i = 0; i < size; i += j) {
+ if ((j = read(rem, cp, size - i)) <= 0)
+ cleanup(0);
+ cp += j;
+ }
+ *cp = '\0';
+ if (response() < 0) {
+ err();
+ return;
+ }
+ if (symlink(buf, new) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ symlink(buf, new) < 0)
+ goto badnew1;
+ }
+ mode &= 0777;
+ if (opts & COMPARE) {
+ char tbuf[BUFSIZ];
+
+ if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
+ i == size && strncmp(buf, tbuf, size) == 0) {
+ (void) unlink(new);
+ ack();
+ return;
+ }
+ if (opts & VERIFY)
+ goto differ;
+ }
+ goto fixup;
+ }
+
+ if ((f = creat(new, mode)) < 0) {
+ if (errno != ENOENT || chkparent(new) < 0 ||
+ (f = creat(new, mode)) < 0)
+ goto badnew1;
+ }
+
+ ack();
+ wrerr = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ int amt = BUFSIZ;
+
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ int j = read(rem, cp, amt);
+
+ if (j <= 0) {
+ (void) close(f);
+ (void) unlink(new);
+ cleanup(0);
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (wrerr == 0 && write(f, buf, amt) != amt) {
+ olderrno = errno;
+ wrerr++;
+ }
+ }
+ if (response() < 0) {
+ err();
+ goto badnew2;
+ }
+ if (wrerr)
+ goto badnew1;
+ if (opts & COMPARE) {
+ FILE *f1, *f2;
+ int c;
+
+ if ((f1 = fopen(target, "r")) == NULL)
+ goto badtarget;
+ if ((f2 = fopen(new, "r")) == NULL) {
+badnew1: error("%s:%s: %s\n", host, new, strerror(errno));
+ goto badnew2;
+ }
+ while ((c = getc(f1)) == getc(f2))
+ if (c == EOF) {
+ (void) fclose(f1);
+ (void) fclose(f2);
+ ack();
+ goto badnew2;
+ }
+ (void) fclose(f1);
+ (void) fclose(f2);
+ if (opts & VERIFY) {
+differ: buf[0] = '\0';
+ (void) sprintf(buf + 1, "need to update: %s\n",target);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ goto badnew2;
+ }
+ }
+
+ /*
+ * Set last modified time
+ */
+ tvp[0].tv_sec = time(0);
+ tvp[0].tv_usec = 0;
+ tvp[1].tv_sec = mtime;
+ tvp[1].tv_usec = 0;
+ if (utimes(new, tvp) < 0)
+ note("%s: utimes failed %s: %s\n", host, new, strerror(errno));
+
+ if (fchog(f, new, owner, group, mode) < 0) {
+badnew2: (void) close(f);
+ (void) unlink(new);
+ return;
+ }
+ (void) close(f);
+
+fixup: if (rename(new, target) < 0) {
+badtarget: error("%s:%s: %s\n", host, target, strerror(errno));
+ (void) unlink(new);
+ return;
+ }
+
+ if (opts & COMPARE) {
+ buf[0] = '\0';
+ (void) sprintf(buf + 1, "updated %s\n", target);
+ (void) write(rem, buf, strlen(buf + 1) + 1);
+ } else
+ ack();
+}
+
+/*
+ * Creat a hard link to existing file.
+ */
+static void
+hardlink(cmd)
+ char *cmd;
+{
+ register char *cp;
+ struct stat stb;
+ char *oldname;
+ int opts, exists = 0;
+
+ cp = cmd;
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp++ != ' ') {
+ error("hardlink: options not delimited\n");
+ return;
+ }
+ oldname = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (*cp != ' ') {
+ error("hardlink: oldname name not delimited\n");
+ return;
+ }
+ *cp++ = '\0';
+
+ if (catname) {
+ (void) sprintf(tp, "/%s", cp);
+ }
+ if (lstat(target, &stb) == 0) {
+ int mode = stb.st_mode & S_IFMT;
+ if (mode != S_IFREG && mode != S_IFLNK) {
+ error("%s:%s: not a regular file\n", host, target);
+ return;
+ }
+ exists = 1;
+ }
+ if (chkparent(target) < 0 ) {
+ error("%s:%s: %s (no parent)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (exists && (unlink(target) < 0)) {
+ error("%s:%s: %s (unlink)\n",
+ host, target, strerror(errno));
+ return;
+ }
+ if (link(oldname, target) < 0) {
+ error("%s:can't link %s to %s\n",
+ host, target, oldname);
+ return;
+ }
+ ack();
+}
+
+/*
+ * Check to see if parent directory exists and create one if not.
+ */
+static int
+chkparent(name)
+ char *name;
+{
+ register char *cp;
+ struct stat stb;
+
+ cp = rindex(name, '/');
+ if (cp == NULL || cp == name)
+ return(0);
+ *cp = '\0';
+ if (lstat(name, &stb) < 0) {
+ if (errno == ENOENT && chkparent(name) >= 0 &&
+ mkdir(name, 0777 & ~oumask) >= 0) {
+ *cp = '/';
+ return(0);
+ }
+ } else if (ISDIR(stb.st_mode)) {
+ *cp = '/';
+ return(0);
+ }
+ *cp = '/';
+ return(-1);
+}
+
+/*
+ * Change owner, group and mode of file.
+ */
+static int
+fchog(fd, file, owner, group, mode)
+ int fd;
+ char *file, *owner, *group;
+ int mode;
+{
+ register int i;
+ int uid, gid;
+ extern char user[];
+ extern int userid;
+
+ uid = userid;
+ if (userid == 0) {
+ if (*owner == ':') {
+ uid = atoi(owner + 1);
+ } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
+ if ((pw = getpwnam(owner)) == NULL) {
+ if (mode & 04000) {
+ note("%s:%s: unknown login name, clearing setuid",
+ host, owner);
+ mode &= ~04000;
+ uid = 0;
+ }
+ } else
+ uid = pw->pw_uid;
+ } else
+ uid = pw->pw_uid;
+ if (*group == ':') {
+ gid = atoi(group + 1);
+ goto ok;
+ }
+ } else if ((mode & 04000) && strcmp(user, owner) != 0)
+ mode &= ~04000;
+ gid = -1;
+ if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
+ if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
+ || ((gr = getgrnam(group)) == NULL)) {
+ if (mode & 02000) {
+ note("%s:%s: unknown group", host, group);
+ mode &= ~02000;
+ }
+ } else
+ gid = gr->gr_gid;
+ } else
+ gid = gr->gr_gid;
+ if (userid && gid >= 0) {
+ if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
+ if (!(strcmp(user, gr->gr_mem[i])))
+ goto ok;
+ mode &= ~02000;
+ gid = -1;
+ }
+ok: if (fd != -1 && fchown(fd, uid, gid) < 0 || chown(file, uid, gid) < 0)
+ note("%s: %s chown: %s", host, file, strerror(errno));
+ else if (mode & 07000 &&
+ (fd != -1 && fchmod(fd, mode) < 0 || chmod(file, mode) < 0))
+ note("%s: %s chmod: %s", host, file, strerror(errno));
+ return(0);
+}
+
+/*
+ * Check for files on the machine being updated that are not on the master
+ * machine and remove them.
+ */
+static void
+rmchk(opts)
+ int opts;
+{
+ register char *cp, *s;
+ struct stat stb;
+
+ if (debug)
+ printf("rmchk()\n");
+
+ /*
+ * Tell the remote to clean the files from the last directory sent.
+ */
+ (void) sprintf(buf, "C%o\n", opts & VERIFY);
+ if (debug)
+ printf("buf = %s", buf);
+ (void) write(rem, buf, strlen(buf));
+ if (response() < 0)
+ return;
+ for (;;) {
+ cp = s = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+
+ switch (*s++) {
+ case 'Q': /* Query if file should be removed */
+ /*
+ * Return the following codes to remove query.
+ * N\n -- file exists - DON'T remove.
+ * Y\n -- file doesn't exist - REMOVE.
+ */
+ *--cp = '\0';
+ (void) sprintf(tp, "/%s", s);
+ if (debug)
+ printf("check %s\n", target);
+ if (except(target))
+ (void) write(rem, "N\n", 2);
+ else if (lstat(target, &stb) < 0)
+ (void) write(rem, "Y\n", 2);
+ else
+ (void) write(rem, "N\n", 2);
+ break;
+
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0')
+ log(lfp, "%s\n", s);
+ break;
+
+ case 'E':
+ *tp = '\0';
+ ack();
+ return;
+
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (buf[0] == '\2')
+ lostconn(0);
+ break;
+
+ default:
+ error("rmchk: unexpected response '%s'\n", buf);
+ err();
+ }
+ }
+}
+
+/*
+ * Check the current directory (initialized by the 'T' command to server())
+ * for extraneous files and remove them.
+ */
+static void
+clean(cp)
+ register char *cp;
+{
+ DIR *d;
+ register struct direct *dp;
+ struct stat stb;
+ char *otp;
+ int len, opts;
+
+ opts = 0;
+ while (*cp >= '0' && *cp <= '7')
+ opts = (opts << 3) | (*cp++ - '0');
+ if (*cp != '\0') {
+ error("clean: options not delimited\n");
+ return;
+ }
+ if ((d = opendir(target)) == NULL) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+ ack();
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ error("%s:%s/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ (void) sprintf(buf, "Q%s\n", dp->d_name);
+ (void) write(rem, buf, strlen(buf));
+ cp = buf;
+ do {
+ if (read(rem, cp, 1) != 1)
+ cleanup(0);
+ } while (*cp++ != '\n' && cp < &buf[BUFSIZ]);
+ *--cp = '\0';
+ cp = buf;
+ if (*cp != 'Y')
+ continue;
+ if (opts & VERIFY) {
+ cp = buf;
+ *cp++ = '\0';
+ (void) sprintf(cp, "need to remove: %s\n", target);
+ (void) write(rem, buf, strlen(cp) + 1);
+ } else
+ removeit(&stb);
+ }
+ closedir(d);
+ (void) write(rem, "E\n", 2);
+ (void) response();
+ tp = otp;
+ *tp = '\0';
+}
+
+/*
+ * Remove a file or directory (recursively) and send back an acknowledge
+ * or an error message.
+ */
+static void
+removeit(stp)
+ struct stat *stp;
+{
+ DIR *d;
+ struct direct *dp;
+ register char *cp;
+ struct stat stb;
+ char *otp;
+ int len;
+
+ switch (stp->st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFLNK:
+ if (unlink(target) < 0)
+ goto bad;
+ goto removed;
+
+ case S_IFDIR:
+ break;
+
+ default:
+ error("%s:%s: not a plain file\n", host, target);
+ return;
+ }
+
+ if ((d = opendir(target)) == NULL)
+ goto bad;
+
+ otp = tp;
+ len = tp - target;
+ while (dp = readdir(d)) {
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (len + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ error("%s:%s/%s: Name too long\n",
+ host, target, dp->d_name);
+ continue;
+ }
+ tp = otp;
+ *tp++ = '/';
+ cp = dp->d_name;;
+ while (*tp++ = *cp++)
+ ;
+ tp--;
+ if (lstat(target, &stb) < 0) {
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ continue;
+ }
+ removeit(&stb);
+ }
+ closedir(d);
+ tp = otp;
+ *tp = '\0';
+ if (rmdir(target) < 0) {
+bad:
+ error("%s:%s: %s\n", host, target, strerror(errno));
+ return;
+ }
+removed:
+ cp = buf;
+ *cp++ = '\0';
+ (void) sprintf(cp, "removed %s\n", target);
+ (void) write(rem, buf, strlen(cp) + 1);
+}
+
+/*
+ * Execute a shell command to handle special cases.
+ */
+static void
+dospecial(cmd)
+ char *cmd;
+{
+ int fd[2], status, pid, i;
+ register char *cp, *s;
+ char sbuf[BUFSIZ];
+ extern int userid, groupid;
+
+ if (pipe(fd) < 0) {
+ error("%s\n", strerror(errno));
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ /*
+ * Return everything the shell commands print.
+ */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open(_PATH_DEVNULL, O_RDONLY);
+ (void) dup(fd[1]);
+ (void) dup(fd[1]);
+ (void) close(fd[0]);
+ (void) close(fd[1]);
+ setgid(groupid);
+ setuid(userid);
+ execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
+ _exit(127);
+ }
+ (void) close(fd[1]);
+ s = sbuf;
+ *s++ = '\0';
+ while ((i = read(fd[0], buf, sizeof(buf))) > 0) {
+ cp = buf;
+ do {
+ *s++ = *cp++;
+ if (cp[-1] != '\n') {
+ if (s < &sbuf[sizeof(sbuf)-1])
+ continue;
+ *s++ = '\n';
+ }
+ /*
+ * Throw away blank lines.
+ */
+ if (s == &sbuf[2]) {
+ s--;
+ continue;
+ }
+ (void) write(rem, sbuf, s - sbuf);
+ s = &sbuf[1];
+ } while (--i);
+ }
+ if (s > &sbuf[1]) {
+ *s++ = '\n';
+ (void) write(rem, sbuf, s - sbuf);
+ }
+ while ((i = wait(&status)) != pid && i != -1)
+ ;
+ if (i == -1)
+ status = -1;
+ (void) close(fd[0]);
+ if (status)
+ error("shell returned %d\n", status);
+ else
+ ack();
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+log(FILE *fp, const char *fmt, ...)
+#else
+log(fp, fmt, va_alist)
+ FILE *fp;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ /* Print changes locally if not quiet mode */
+ if (!qflag)
+ (void)vprintf(fmt, ap);
+
+ /* Save changes (for mailing) if really updating files */
+ if (!(options & VERIFY) && fp != NULL)
+ (void)vfprintf(fp, fmt, ap);
+ va_end(ap);
+}
+
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static FILE *fp;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ ++nerrs;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void)fprintf(fp, "%crdist: ", 0x01);
+ (void)vfprintf(fp, fmt, ap);
+ fflush(fp);
+ }
+ else {
+ fflush(stdout);
+ (void)fprintf(stderr, "rdist: ");
+ (void)vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void)fprintf(lfp, "rdist: ");
+ (void)vfprintf(lfp, fmt, ap);
+ fflush(lfp);
+ }
+ va_end(ap);
+}
+
+void
+#if __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static FILE *fp;
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ ++nerrs;
+ if (!fp && !(fp = fdopen(rem, "w")))
+ return;
+ if (iamremote) {
+ (void)fprintf(fp, "%crdist: ", 0x02);
+ (void)vfprintf(fp, fmt, ap);
+ fflush(fp);
+ }
+ else {
+ fflush(stdout);
+ (void)fprintf(stderr, "rdist: ");
+ (void)vfprintf(stderr, fmt, ap);
+ fflush(stderr);
+ }
+ if (lfp != NULL) {
+ (void)fprintf(lfp, "rdist: ");
+ (void)vfprintf(lfp, fmt, ap);
+ fflush(lfp);
+ }
+ cleanup(0);
+}
+
+static int
+response()
+{
+ char *cp, *s;
+ char resp[BUFSIZ];
+
+ if (debug)
+ printf("response()\n");
+
+ cp = s = resp;
+ do {
+ if (read(rem, cp, 1) != 1)
+ lostconn(0);
+ } while (*cp++ != '\n' && cp < &resp[BUFSIZ]);
+
+ switch (*s++) {
+ case '\0':
+ *--cp = '\0';
+ if (*s != '\0') {
+ log(lfp, "%s\n", s);
+ return(1);
+ }
+ return(0);
+ case '\3':
+ *--cp = '\0';
+ log(lfp, "Note: %s\n",s);
+ return(response());
+
+ default:
+ s--;
+ /* fall into... */
+ case '\1':
+ case '\2':
+ nerrs++;
+ if (*s != '\n') {
+ if (!iamremote) {
+ fflush(stdout);
+ (void) write(2, s, cp - s);
+ }
+ if (lfp != NULL)
+ (void) fwrite(s, 1, cp - s, lfp);
+ }
+ if (resp[0] == '\2')
+ lostconn(0);
+ return(-1);
+ }
+}
+
+/*
+ * Remove temporary files and do any cleanup operations before exiting.
+ */
+void
+cleanup(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ exit(1);
+}
+
+static void
+#if __STDC__
+note(const char *fmt, ...)
+#else
+note(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ static char buf[BUFSIZ];
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ comment(buf);
+}
+
+static void
+comment(s)
+ char *s;
+{
+ char c;
+
+ c = '\3';
+ write(rem, &c, 1);
+ write(rem, s, strlen(s));
+ c = '\n';
+ write(rem, &c, 1);
+}
diff --git a/usr.bin/renice/Makefile b/usr.bin/renice/Makefile
new file mode 100644
index 0000000..1042921
--- /dev/null
+++ b/usr.bin/renice/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= renice
+MAN8= renice.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/renice/renice.8 b/usr.bin/renice/renice.8
new file mode 100644
index 0000000..5a260a8
--- /dev/null
+++ b/usr.bin/renice/renice.8
@@ -0,0 +1,131 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)renice.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt RENICE 8
+.Os BSD 4
+.Sh NAME
+.Nm renice
+.Nd alter priority of running processes
+.Sh SYNOPSIS
+.Nm renice
+.Ar priority
+.Oo
+.Op Fl p
+.Ar pid ...
+.Oc
+.Oo
+.Op Fl g
+.Ar pgrp ...
+.Oc
+.Oo
+.Op Fl u
+.Ar user ...
+.Oc
+.Sh DESCRIPTION
+.Nm Renice
+alters the
+scheduling priority of one or more running processes.
+The following
+.Ar who
+parameters are interpreted as process ID's, process group
+ID's, or user names.
+.Nm Renice Ns 'ing
+a process group causes all processes in the process group
+to have their scheduling priority altered.
+.Nm Renice Ns 'ing
+a user causes all processes owned by the user to have
+their scheduling priority altered.
+By default, the processes to be affected are specified by
+their process ID's.
+.Pp
+Options supported by
+.Nm renice :
+.Bl -tag -width Ds
+.It Fl g
+Force
+.Ar who
+parameters to be interpreted as process group ID's.
+.It Fl u
+Force the
+.Ar who
+parameters to be interpreted as user names.
+.It Fl p
+Resets the
+.Ar who
+interpretation to be (the default) process ID's.
+.El
+.Pp
+For example,
+.Bd -literal -offset
+renice +1 987 -u daemon root -p 32
+.Ed
+.Pp
+would change the priority of process ID's 987 and 32, and
+all processes owned by users daemon and root.
+.Pp
+Users other than the super-user may only alter the priority of
+processes they own,
+and can only monotonically increase their ``nice value''
+within the range 0 to
+.Dv PRIO_MAX
+(20).
+(This prevents overriding administrative fiats.)
+The super-user
+may alter the priority of any process
+and set the priority to any value in the range
+.Dv PRIO_MIN
+(\-20)
+to
+.Dv PRIO_MAX .
+Useful priorities are:
+20 (the affected processes will run only when nothing else
+in the system wants to),
+0 (the ``base'' scheduling priority),
+anything negative (to make things go very fast).
+.Sh FILES
+.Bl -tag -width /etc/passwd -compact
+.It Pa /etc/passwd
+to map user names to user ID's
+.El
+.Sh SEE ALSO
+.Xr getpriority 2 ,
+.Xr setpriority 2
+.Sh BUGS
+Non super-users can not increase scheduling priorities of their own processes,
+even if they were the ones that decreased the priorities in the first place.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/renice/renice.c b/usr.bin/renice/renice.c
new file mode 100644
index 0000000..37dd913
--- /dev/null
+++ b/usr.bin/renice/renice.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)renice.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <pwd.h>
+
+/*
+ * Change the priority (nice) of processes
+ * or groups of processes which are already
+ * running.
+ */
+main(argc, argv)
+ char **argv;
+{
+ int which = PRIO_PROCESS;
+ int who = 0, prio, errs = 0;
+
+ argc--, argv++;
+ if (argc < 2) {
+ fprintf(stderr, "usage: renice priority [ [ -p ] pids ] ");
+ fprintf(stderr, "[ [ -g ] pgrps ] [ [ -u ] users ]\n");
+ exit(1);
+ }
+ prio = atoi(*argv);
+ argc--, argv++;
+ if (prio > PRIO_MAX)
+ prio = PRIO_MAX;
+ if (prio < PRIO_MIN)
+ prio = PRIO_MIN;
+ for (; argc > 0; argc--, argv++) {
+ if (strcmp(*argv, "-g") == 0) {
+ which = PRIO_PGRP;
+ continue;
+ }
+ if (strcmp(*argv, "-u") == 0) {
+ which = PRIO_USER;
+ continue;
+ }
+ if (strcmp(*argv, "-p") == 0) {
+ which = PRIO_PROCESS;
+ continue;
+ }
+ if (which == PRIO_USER) {
+ register struct passwd *pwd = getpwnam(*argv);
+
+ if (pwd == NULL) {
+ fprintf(stderr, "renice: %s: unknown user\n",
+ *argv);
+ continue;
+ }
+ who = pwd->pw_uid;
+ } else {
+ who = atoi(*argv);
+ if (who < 0) {
+ fprintf(stderr, "renice: %s: bad value\n",
+ *argv);
+ continue;
+ }
+ }
+ errs += donice(which, who, prio);
+ }
+ exit(errs != 0);
+}
+
+donice(which, who, prio)
+ int which, who, prio;
+{
+ int oldprio;
+ extern int errno;
+
+ errno = 0, oldprio = getpriority(which, who);
+ if (oldprio == -1 && errno) {
+ fprintf(stderr, "renice: %d: ", who);
+ perror("getpriority");
+ return (1);
+ }
+ if (setpriority(which, who, prio) < 0) {
+ fprintf(stderr, "renice: %d: ", who);
+ perror("setpriority");
+ return (1);
+ }
+ printf("%d: old priority %d, new priority %d\n", who, oldprio, prio);
+ return (0);
+}
diff --git a/usr.bin/rev/Makefile b/usr.bin/rev/Makefile
new file mode 100644
index 0000000..1952ce9
--- /dev/null
+++ b/usr.bin/rev/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= rev
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rev/rev.1 b/usr.bin/rev/rev.1
new file mode 100644
index 0000000..7c50efc
--- /dev/null
+++ b/usr.bin/rev/rev.1
@@ -0,0 +1,48 @@
+.\" Copyright (c) 1985, 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rev.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt REV 1
+.Os
+.Sh NAME
+.Nm rev
+.Nd reverse lines of a file
+.Sh SYNOPSIS
+.Nm rev
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm rev
+utility copies the specified files to the standard output, reversing the
+order of characters in every line.
+If no files are specified, the standard input is read.
diff --git a/usr.bin/rev/rev.c b/usr.bin/rev/rev.c
new file mode 100644
index 0000000..fe95991
--- /dev/null
+++ b/usr.bin/rev/rev.c
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 1987, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rev.c 8.2 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *filename, *p, *t;
+ FILE *fp;
+ size_t len;
+ int ch, rval;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ fp = stdin;
+ filename = "stdin";
+ rval = 0;
+ do {
+ if (*argv) {
+ if ((fp = fopen(*argv, "r")) == NULL) {
+ warn("%s", *argv);
+ rval = 1;
+ ++argv;
+ continue;
+ }
+ filename = *argv++;
+ }
+ while ((p = fgetln(fp, &len)) != NULL) {
+ if (p[len - 1] == '\n')
+ --len;
+ t = p + len - 1;
+ for (t = p + len - 1; t >= p; --t)
+ putchar(*t);
+ putchar('\n');
+ }
+ if (ferror(fp)) {
+ warn("%s", filename);
+ rval = 1;
+ }
+ (void)fclose(fp);
+ } while(*argv);
+ exit(rval);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: rev [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/rlogin/Makefile b/usr.bin/rlogin/Makefile
new file mode 100644
index 0000000..38cf030
--- /dev/null
+++ b/usr.bin/rlogin/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= rlogin
+SRCS= rlogin.c
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+SRCS+= krcmd.c kcmd.c
+DPADD= ${LIBKRB} ${LIBDES}
+CFLAGS+=-DKERBEROS -DCRYPT
+LDADD= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rlogin/des_rw.c b/usr.bin/rlogin/des_rw.c
new file mode 100644
index 0000000..dbe47f0
--- /dev/null
+++ b/usr.bin/rlogin/des_rw.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)des_rw.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#ifdef CRYPT
+#ifdef KERBEROS
+#include <sys/param.h>
+
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static unsigned char des_inbuf[10240], storage[10240], *store_ptr;
+static bit_64 *key;
+static u_char *key_schedule;
+
+/* XXX these should be in a kerberos include file */
+int krb_net_read __P((int, char *, int));
+#ifdef notdef
+/* XXX too hard to make this work */
+int des_pcbc_encrypt __P((des_cblock *, des_cblock *, long,
+ des_key_schedule, des_cblock *, int));
+#endif
+
+/*
+ * NB: These routines will not function properly if NBIO
+ * is set
+ */
+
+/*
+ * des_set_key
+ *
+ * Set des encryption/decryption key for use by the des_read and
+ * des_write routines
+ *
+ * The inkey parameter is actually the DES initial vector,
+ * and the insched is the DES Key unwrapped for faster decryption
+ */
+
+void
+des_set_key(inkey, insched)
+ bit_64 *inkey;
+ u_char *insched;
+{
+ key = inkey;
+ key_schedule = insched;
+}
+
+void
+des_clear_key()
+{
+ bzero((char *) key, sizeof(C_Block));
+ bzero((char *) key_schedule, sizeof(Key_schedule));
+}
+
+
+int
+des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len, rd_len;
+ int nstored = 0;
+
+ if (nstored >= len) {
+ (void) bcopy(store_ptr, buf, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ (void) bcopy(store_ptr, buf, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+ if (krb_net_read(fd, (char *)&net_len, sizeof(net_len)) !=
+ sizeof(net_len)) {
+ /* XXX can't read enough, pipe
+ must have closed */
+ return(0);
+ }
+ net_len = ntohl(net_len);
+ if (net_len <= 0 || net_len > sizeof(des_inbuf)) {
+ /* preposterous length; assume out-of-sync; only
+ recourse is to close connection, so return 0 */
+ return(0);
+ }
+ /* the writer tells us how much real data we are getting, but
+ we need to read the pad bytes (8-byte boundary) */
+ rd_len = roundup(net_len, 8);
+ if (krb_net_read(fd, (char *)des_inbuf, rd_len) != rd_len) {
+ /* pipe must have closed, return 0 */
+ return(0);
+ }
+ (void) des_pcbc_encrypt(des_inbuf, /* inbuf */
+ storage, /* outbuf */
+ net_len, /* length */
+ key_schedule, /* DES key */
+ key, /* IV */
+ DECRYPT); /* direction */
+
+ if(net_len < 8)
+ store_ptr = storage + 8 - net_len;
+ else
+ store_ptr = storage;
+
+ nstored = net_len;
+ if (nstored > len) {
+ (void) bcopy(store_ptr, buf, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ (void) bcopy(store_ptr, buf, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+
+ return(nreturned);
+}
+
+static unsigned char des_outbuf[10240]; /* > longest write */
+
+int
+des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ static int seeded = 0;
+ static char garbage_buf[8];
+ long net_len, garbage;
+
+ if(len < 8) {
+ if(!seeded) {
+ seeded = 1;
+ srandom((int) time((long *)0));
+ }
+ garbage = random();
+ /* insert random garbage */
+ (void) bcopy(&garbage, garbage_buf, MIN(sizeof(long),8));
+ /* this "right-justifies" the data in the buffer */
+ (void) bcopy(buf, garbage_buf + 8 - len, len);
+ }
+ /* pcbc_encrypt outputs in 8-byte (64 bit) increments */
+
+ (void) des_pcbc_encrypt((len < 8) ? garbage_buf : buf,
+ des_outbuf,
+ (len < 8) ? 8 : len,
+ key_schedule, /* DES key */
+ key, /* IV */
+ ENCRYPT);
+
+ /* tell the other end the real amount, but send an 8-byte padded
+ packet */
+ net_len = htonl(len);
+ (void) write(fd, &net_len, sizeof(net_len));
+ (void) write(fd, des_outbuf, roundup(len,8));
+ return(len);
+}
+#endif /* KERBEROS */
+#endif /* CRYPT */
diff --git a/usr.bin/rlogin/kcmd.c b/usr.bin/rlogin/kcmd.c
new file mode 100644
index 0000000..14c0367
--- /dev/null
+++ b/usr.bin/rlogin/kcmd.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char Xsccsid[] = "derived from @(#)rcmd.c 5.17 (Berkeley) 6/27/88";
+static char sccsid[] = "@(#)kcmd.c 8.2 (Berkeley) 8/19/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#include <kerberosIV/kparse.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "krb.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define START_PORT 5120 /* arbitrary */
+
+int getport __P((int *));
+
+int
+kcmd(sock, ahost, rport, locuser, remuser, cmd, fd2p, ticket, service, realm,
+ cred, schedule, msg_data, laddr, faddr, authopts)
+ int *sock;
+ char **ahost;
+ u_short rport;
+ char *locuser, *remuser, *cmd;
+ int *fd2p;
+ KTEXT ticket;
+ char *service;
+ char *realm;
+ CREDENTIALS *cred;
+ Key_schedule schedule;
+ MSG_DAT *msg_data;
+ struct sockaddr_in *laddr, *faddr;
+ long authopts;
+{
+ int s, timo = 1, pid;
+ long oldmask;
+ struct sockaddr_in sin, from;
+ char c;
+#ifdef ATHENA_COMPAT
+ int lport = IPPORT_RESERVED - 1;
+#else
+ int lport = START_PORT;
+#endif
+ struct hostent *hp;
+ int rc;
+ char *host_save;
+ int status;
+
+ pid = getpid();
+ hp = gethostbyname(*ahost);
+ if (hp == NULL) {
+ /* fprintf(stderr, "%s: unknown host\n", *ahost); */
+ return (-1);
+ }
+
+ host_save = malloc(strlen(hp->h_name) + 1);
+ strcpy(host_save, hp->h_name);
+ *ahost = host_save;
+
+#ifdef KERBEROS
+ /* If realm is null, look up from table */
+ if (realm == NULL || realm[0] == '\0')
+ realm = krb_realmofhost(host_save);
+#endif /* KERBEROS */
+
+ oldmask = sigblock(sigmask(SIGURG));
+ for (;;) {
+ s = getport(&lport);
+ if (s < 0) {
+ if (errno == EAGAIN)
+ fprintf(stderr,
+ "kcmd(socket): All ports in use\n");
+ else
+ perror("kcmd: socket");
+ sigsetmask(oldmask);
+ return (-1);
+ }
+ fcntl(s, F_SETOWN, pid);
+ sin.sin_family = hp->h_addrtype;
+#if defined(ultrix) || defined(sun)
+ bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
+#else
+ bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length);
+#endif
+ sin.sin_port = rport;
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
+ break;
+ (void) close(s);
+ if (errno == EADDRINUSE) {
+ lport--;
+ continue;
+ }
+ /*
+ * don't wait very long for Kerberos rcmd.
+ */
+ if (errno == ECONNREFUSED && timo <= 4) {
+ /* sleep(timo); don't wait at all here */
+ timo *= 2;
+ continue;
+ }
+#if !(defined(ultrix) || defined(sun))
+ if (hp->h_addr_list[1] != NULL) {
+ int oerrno = errno;
+
+ fprintf(stderr,
+ "kcmd: connect to address %s: ",
+ inet_ntoa(sin.sin_addr));
+ errno = oerrno;
+ perror(NULL);
+ hp->h_addr_list++;
+ bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr,
+ hp->h_length);
+ fprintf(stderr, "Trying %s...\n",
+ inet_ntoa(sin.sin_addr));
+ continue;
+ }
+#endif /* !(defined(ultrix) || defined(sun)) */
+ if (errno != ECONNREFUSED)
+ perror(hp->h_name);
+ sigsetmask(oldmask);
+ return (-1);
+ }
+ lport--;
+ if (fd2p == 0) {
+ write(s, "", 1);
+ lport = 0;
+ } else {
+ char num[8];
+ int s2 = getport(&lport), s3;
+ int len = sizeof(from);
+
+ if (s2 < 0) {
+ status = -1;
+ goto bad;
+ }
+ listen(s2, 1);
+ (void) sprintf(num, "%d", lport);
+ if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
+ perror("kcmd(write): setting up stderr");
+ (void) close(s2);
+ status = -1;
+ goto bad;
+ }
+ s3 = accept(s2, (struct sockaddr *)&from, &len);
+ (void) close(s2);
+ if (s3 < 0) {
+ perror("kcmd:accept");
+ lport = 0;
+ status = -1;
+ goto bad;
+ }
+ *fd2p = s3;
+ from.sin_port = ntohs((u_short)from.sin_port);
+ if (from.sin_family != AF_INET ||
+ from.sin_port >= IPPORT_RESERVED) {
+ fprintf(stderr,
+ "kcmd(socket): protocol failure in circuit setup.\n");
+ status = -1;
+ goto bad2;
+ }
+ }
+ /*
+ * Kerberos-authenticated service. Don't have to send locuser,
+ * since its already in the ticket, and we'll extract it on
+ * the other side.
+ */
+ /* (void) write(s, locuser, strlen(locuser)+1); */
+
+ /* set up the needed stuff for mutual auth, but only if necessary */
+ if (authopts & KOPT_DO_MUTUAL) {
+ int sin_len;
+ *faddr = sin;
+
+ sin_len = sizeof(struct sockaddr_in);
+ if (getsockname(s, (struct sockaddr *)laddr, &sin_len) < 0) {
+ perror("kcmd(getsockname)");
+ status = -1;
+ goto bad2;
+ }
+ }
+#ifdef KERBEROS
+ if ((status = krb_sendauth(authopts, s, ticket, service, *ahost,
+ realm, (unsigned long) getpid(), msg_data,
+ cred, schedule,
+ laddr,
+ faddr,
+ "KCMDV0.1")) != KSUCCESS)
+ goto bad2;
+#endif /* KERBEROS */
+
+ (void) write(s, remuser, strlen(remuser)+1);
+ (void) write(s, cmd, strlen(cmd)+1);
+
+ if ((rc = read(s, &c, 1)) != 1) {
+ if (rc == -1)
+ perror(*ahost);
+ else
+ fprintf(stderr,"kcmd: bad connection with remote host\n");
+ status = -1;
+ goto bad2;
+ }
+ if (c != '\0') {
+ while (read(s, &c, 1) == 1) {
+ (void) write(2, &c, 1);
+ if (c == '\n')
+ break;
+ }
+ status = -1;
+ goto bad2;
+ }
+ sigsetmask(oldmask);
+ *sock = s;
+ return (KSUCCESS);
+bad2:
+ if (lport)
+ (void) close(*fd2p);
+bad:
+ (void) close(s);
+ sigsetmask(oldmask);
+ return (status);
+}
+
+int
+getport(alport)
+ int *alport;
+{
+ struct sockaddr_in sin;
+ int s, retval;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if ((retval = krb_get_local_addr(&sin)) != KSUCCESS) {
+ fprintf(stderr, "krb_get_local_addr: %s\n",krb_err_txt[retval]);
+ close(s);
+ return (-1);
+ }
+ if (s < 0)
+ return (-1);
+ for (;;) {
+ sin.sin_port = htons((u_short)*alport);
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
+ return (s);
+ if (errno != EADDRINUSE) {
+ (void) close(s);
+ return (-1);
+ }
+ (*alport)--;
+#ifdef ATHENA_COMPAT
+ if (*alport == IPPORT_RESERVED/2) {
+#else
+ if (*alport == IPPORT_RESERVED) {
+#endif
+ (void) close(s);
+ errno = EAGAIN; /* close */
+ return (-1);
+ }
+ }
+}
diff --git a/usr.bin/rlogin/krb.h b/usr.bin/rlogin/krb.h
new file mode 100644
index 0000000..07213f4
--- /dev/null
+++ b/usr.bin/rlogin/krb.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)krb.h 8.1 (Berkeley) 6/6/93
+ */
+
+int krcmd __P((char **, u_short, char *, char *, int *, char *));
+int krcmd_mutual __P((char **, u_short, char *, char *, int *,
+ char *, CREDENTIALS *, Key_schedule));
diff --git a/usr.bin/rlogin/krcmd.c b/usr.bin/rlogin/krcmd.c
new file mode 100644
index 0000000..c5eee4f
--- /dev/null
+++ b/usr.bin/rlogin/krcmd.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)krcmd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * $Source: /home/ncvs/src/usr.bin/rlogin/krcmd.c,v $
+ * $Header: /mit/kerberos/ucb/mit/kcmd/RCS/krcmd.c,v 5.1
+ * 89/07/25 15:38:44 kfall Exp Locker: kfall $
+ * static char *rcsid_kcmd_c =
+ * "$Header: /mit/kerberos/ucb/mit/kcmd/RCS/krcmd.c,v 5.1 89/07/25 15:38:44
+ * kfall Exp Locker: kfall $";
+ */
+
+#ifdef KERBEROS
+#include <sys/types.h>
+#ifdef CRYPT
+#include <sys/socket.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include <stdio.h>
+
+#define SERVICE_NAME "rcmd"
+
+int kcmd __P((int *, char **, u_short, char *, char *, char *, int *,
+ KTEXT, char *, char *, CREDENTIALS *, Key_schedule, MSG_DAT *,
+ struct sockaddr_in *, struct sockaddr_in *, long));
+
+/*
+ * krcmd: simplified version of Athena's "kcmd"
+ * returns a socket attached to the destination, -1 or krb error on error
+ * if fd2p is non-NULL, another socket is filled in for it
+ */
+
+int
+krcmd(ahost, rport, remuser, cmd, fd2p, realm)
+ char **ahost;
+ u_short rport;
+ char *remuser, *cmd;
+ int *fd2p;
+ char *realm;
+{
+ int sock = -1, err = 0;
+ KTEXT_ST ticket;
+ long authopts = 0L;
+
+ err = kcmd(
+ &sock,
+ ahost,
+ rport,
+ NULL, /* locuser not used */
+ remuser,
+ cmd,
+ fd2p,
+ &ticket,
+ SERVICE_NAME,
+ realm,
+ (CREDENTIALS *) NULL, /* credentials not used */
+ 0, /* key schedule not used */
+ (MSG_DAT *) NULL, /* MSG_DAT not used */
+ (struct sockaddr_in *) NULL, /* local addr not used */
+ (struct sockaddr_in *) NULL, /* foreign addr not used */
+ authopts
+ );
+
+ if (err > KSUCCESS && err < MAX_KRB_ERRORS) {
+ fprintf(stderr, "krcmd: %s\n", krb_err_txt[err]);
+ return(-1);
+ }
+ if (err < 0)
+ return(-1);
+ return(sock);
+}
+
+#ifdef CRYPT
+int
+krcmd_mutual(ahost, rport, remuser, cmd, fd2p, realm, cred, sched)
+ char **ahost;
+ u_short rport;
+ char *remuser, *cmd;
+ int *fd2p;
+ char *realm;
+ CREDENTIALS *cred;
+ Key_schedule sched;
+{
+ int sock, err;
+ KTEXT_ST ticket;
+ MSG_DAT msg_dat;
+ struct sockaddr_in laddr, faddr;
+ long authopts = KOPT_DO_MUTUAL;
+
+ err = kcmd(
+ &sock,
+ ahost,
+ rport,
+ NULL, /* locuser not used */
+ remuser,
+ cmd,
+ fd2p,
+ &ticket,
+ SERVICE_NAME,
+ realm,
+ cred, /* filled in */
+ sched, /* filled in */
+ &msg_dat, /* filled in */
+ &laddr, /* filled in */
+ &faddr, /* filled in */
+ authopts
+ );
+
+ if (err > KSUCCESS && err < MAX_KRB_ERRORS) {
+ fprintf(stderr, "krcmd_mutual: %s\n", krb_err_txt[err]);
+ return(-1);
+ }
+
+ if (err < 0)
+ return (-1);
+ return(sock);
+}
+#endif /* CRYPT */
+#endif /* KERBEROS */
diff --git a/usr.bin/rlogin/rlogin.1 b/usr.bin/rlogin/rlogin.1
new file mode 100644
index 0000000..8f4453a
--- /dev/null
+++ b/usr.bin/rlogin/rlogin.1
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rlogin.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RLOGIN 1
+.Os BSD 4.2
+.Sh NAME
+.Nm rlogin
+.Nd remote login
+.Sh SYNOPSIS
+.Ar rlogin
+.Op Fl 8DEKLdx
+.Op Fl e Ar char
+.Op Fl k Ar realm
+.Op Fl l Ar username
+.Ar host
+.Sh DESCRIPTION
+.Nm Rlogin
+starts a terminal session on a remote host
+.Ar host .
+.Pp
+.Nm Rlogin
+first attempts to use the Kerberos authorization mechanism, described below.
+If the remote host does not supporting Kerberos the standard Berkeley
+.Pa rhosts
+authorization mechanism is used.
+The options are as follows:
+.Bl -tag -width flag
+.It Fl 8
+The
+.Fl 8
+option allows an eight-bit input data path at all times; otherwise
+parity bits are stripped except when the remote side's stop and start
+characters are other than
+^S/^Q .
+.It Fl D
+The
+.Fl D
+option sets the TCP_NODELAY socket option which can improve interactive response
+at the expense of increased network load.
+.It Fl E
+The
+.Fl E
+option stops any character from being recognized as an escape character.
+When used with the
+.Fl 8
+option, this provides a completely transparent connection.
+.It Fl K
+The
+.Fl K
+option turns off all Kerberos authentication.
+.It Fl L
+The
+.Fl L
+option allows the rlogin session to be run in ``litout'' (see
+.Xr tty 4 )
+mode.
+.It Fl d
+The
+.Fl d
+option turns on socket debugging (see
+.Xr setsockopt 2 )
+on the TCP sockets used for communication with the remote host.
+.It Fl e
+The
+.Fl e
+option allows user specification of the escape character, which is
+``~'' by default.
+This specification may be as a literal character, or as an octal
+value in the form \ennn.
+.It Fl k
+The
+.FL k
+option requests rlogin to obtain tickets for the remote host
+in realm
+.Ar realm
+instead of the remote host's realm as determined by
+.Xr krb_realmofhost 3 .
+.It Fl x
+The
+.Fl x
+option turns on
+.Tn DES
+encryption for all data passed via the
+rlogin session.
+This may impact response time and
+.Tn CPU
+utilization, but provides
+increased security.
+.El
+.Pp
+A line of the form ``<escape char>.'' disconnects from the remote host.
+Similarly, the line ``<escape char>^Z'' will suspend the
+.Nm rlogin
+session, and ``<escape char><delayed-suspend char>'' suspends the
+send portion of the rlogin, but allows output from the remote system.
+By default, the tilde (``~'') character is the escape character, and
+normally control-Y (``^Y'') is the delayed-suspend character.
+.Pp
+All echoing takes place at the remote site, so that (except for delays)
+the
+.Nm rlogin
+is transparent.
+Flow control via ^S/^Q and flushing of input and output on interrupts
+are handled properly.
+.Sh KERBEROS AUTHENTICATION
+Each user may have a private authorization list in the file
+.Pa .klogin
+in their home directory.
+Each line in this file should contain a Kerberos principal name of the
+form
+.Ar principal.instance@realm .
+If the originating user is authenticated to one of the principals named
+in
+.Pa .klogin ,
+access is granted to the account.
+The principal
+.Ar accountname.@localrealm
+is granted access if
+there is no
+.Pa .klogin
+file.
+Otherwise a login and password will be prompted for on the remote machine
+as in
+.Xr login 1 .
+To avoid certain security problems, the
+.Pa .klogin
+file must be owned by
+the remote user.
+.Pp
+If Kerberos authentication fails, a warning message is printed and the
+standard Berkeley
+.Nm rlogin
+is used instead.
+.Sh ENVIRONMENT
+The following environment variable is utilized by
+.Nm rlogin :
+.Bl -tag -width TERM
+.It Ev TERM
+Determines the user's terminal type.
+.El
+.Sh SEE ALSO
+.Xr rsh 1 ,
+.Xr kerberos 3 ,
+.Xr krb_sendauth 3 ,
+.Xr krb_realmofhost 3
+.Sh HISTORY
+The
+.Nm rlogin
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+.Nm Rlogin
+will be replaced by
+.Xr telnet 1
+in the near future.
+.Pp
+More of the environment should be propagated.
diff --git a/usr.bin/rlogin/rlogin.c b/usr.bin/rlogin/rlogin.c
new file mode 100644
index 0000000..320844f
--- /dev/null
+++ b/usr.bin/rlogin/rlogin.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * rlogin - remote login
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+#include "krb.h"
+
+CREDENTIALS cred;
+Key_schedule schedule;
+int use_kerberos = 1, doencrypt;
+char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
+#endif
+
+#ifndef TIOCPKT_WINDOW
+#define TIOCPKT_WINDOW 0x80
+#endif
+
+/* concession to Sun */
+#ifndef SIGUSR1
+#define SIGUSR1 30
+#endif
+
+int eight, litout, rem;
+
+int noescape;
+u_char escapechar = '~';
+
+char *speeds[] = {
+ "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
+ "1800", "2400", "4800", "9600", "19200", "38400", "57600", "115200"
+};
+
+#ifdef OLDSUN
+struct winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+#else
+#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
+#endif
+struct winsize winsize;
+
+void catch_child __P((int));
+void copytochild __P((int));
+__dead void doit __P((long));
+__dead void done __P((int));
+void echo __P((char));
+u_int getescape __P((char *));
+void lostpeer __P((int));
+void mode __P((int));
+void msg __P((char *));
+void oob __P((int));
+int reader __P((int));
+void sendwindow __P((void));
+void setsignal __P((int));
+void sigwinch __P((int));
+void stop __P((char));
+__dead void usage __P((void));
+void writer __P((void));
+void writeroob __P((int));
+
+#ifdef KERBEROS
+void warning __P((const char *, ...));
+#endif
+#ifdef OLDSUN
+int get_window_size __P((int, struct winsize *));
+#endif
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ struct passwd *pw;
+ struct servent *sp;
+ struct sgttyb ttyb;
+ long omask;
+ int argoff, ch, dflag, Dflag, one, uid;
+ char *host, *p, *user, term[1024];
+
+ argoff = dflag = Dflag = 0;
+ one = 1;
+ host = user = NULL;
+
+ if (p = rindex(argv[0], '/'))
+ ++p;
+ else
+ p = argv[0];
+
+ if (strcmp(p, "rlogin"))
+ host = p;
+
+ /* handle "rlogin host flags" */
+ if (!host && argc > 2 && argv[1][0] != '-') {
+ host = argv[1];
+ argoff = 1;
+ }
+
+#ifdef KERBEROS
+#define OPTIONS "8DEKLde:k:l:x"
+#else
+#define OPTIONS "8DEKLde:l:"
+#endif
+ while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
+ switch(ch) {
+ case '8':
+ eight = 1;
+ break;
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'E':
+ noescape = 1;
+ break;
+ case 'K':
+#ifdef KERBEROS
+ use_kerberos = 0;
+#endif
+ break;
+ case 'L':
+ litout = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ noescape = 0;
+ escapechar = getescape(optarg);
+ break;
+#ifdef KERBEROS
+ case 'k':
+ dest_realm = dst_realm_buf;
+ (void)strncpy(dest_realm, optarg, REALM_SZ);
+ break;
+#endif
+ case 'l':
+ user = optarg;
+ break;
+#ifdef CRYPT
+#ifdef KERBEROS
+ case 'x':
+ doencrypt = 1;
+ break;
+#endif
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ optind += argoff;
+ argc -= optind;
+ argv += optind;
+
+ /* if haven't gotten a host yet, do so */
+ if (!host && !(host = *argv++))
+ usage();
+
+ if (*argv)
+ usage();
+
+ if (!(pw = getpwuid(uid = getuid()))) {
+ (void)fprintf(stderr, "rlogin: unknown user id.\n");
+ exit(1);
+ }
+ if (!user)
+ user = pw->pw_name;
+
+ sp = NULL;
+#ifdef KERBEROS
+ if (use_kerberos) {
+ sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp");
+ if (sp == NULL) {
+ use_kerberos = 0;
+ warning("can't get entry for %s/tcp service",
+ doencrypt ? "eklogin" : "klogin");
+ }
+ }
+#endif
+ if (sp == NULL)
+ sp = getservbyname("login", "tcp");
+ if (sp == NULL) {
+ (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
+ exit(1);
+ }
+
+ (void)strcpy(term, (p = getenv("TERM")) ? p : "network");
+ if (ioctl(0, TIOCGETP, &ttyb) == 0) {
+ (void)strcat(term, "/");
+ (void)strcat(term, speeds[(int)ttyb.sg_ospeed]);
+ }
+
+ (void)get_window_size(0, &winsize);
+
+ (void)signal(SIGPIPE, lostpeer);
+ /* will use SIGUSR1 for window size hack, so hold it off */
+ omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
+ /*
+ * We set SIGURG and SIGUSR1 below so that an
+ * incoming signal will be held pending rather than being
+ * discarded. Note that these routines will be ready to get
+ * a signal by the time that they are unblocked below.
+ */
+ (void)signal(SIGURG, copytochild);
+ (void)signal(SIGUSR1, writeroob);
+
+#ifdef KERBEROS
+try_connect:
+ if (use_kerberos) {
+ struct hostent *hp;
+
+ /* Fully qualify hostname (needed for krb_realmofhost). */
+ hp = gethostbyname(host);
+ if (hp != NULL && !(host = strdup(hp->h_name))) {
+ (void)fprintf(stderr, "rlogin: %s\n",
+ strerror(ENOMEM));
+ exit(1);
+ }
+
+ rem = KSUCCESS;
+ errno = 0;
+ if (dest_realm == NULL)
+ dest_realm = krb_realmofhost(host);
+
+#ifdef CRYPT
+ if (doencrypt) {
+ rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
+ dest_realm, &cred, schedule);
+ des_set_key(&cred.session, schedule);
+ } else
+#endif /* CRYPT */
+ rem = krcmd(&host, sp->s_port, user, term, 0,
+ dest_realm);
+ if (rem < 0) {
+ use_kerberos = 0;
+ sp = getservbyname("login", "tcp");
+ if (sp == NULL) {
+ (void)fprintf(stderr,
+ "rlogin: unknown service login/tcp.\n");
+ exit(1);
+ }
+ if (errno == ECONNREFUSED)
+ warning("remote host doesn't support Kerberos");
+ if (errno == ENOENT)
+ warning("can't provide Kerberos auth data");
+ goto try_connect;
+ }
+ } else {
+#ifdef CRYPT
+ if (doencrypt) {
+ (void)fprintf(stderr,
+ "rlogin: the -x flag requires Kerberos authentication.\n");
+ exit(1);
+ }
+#endif /* CRYPT */
+ rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
+ }
+#else
+ rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
+#endif /* KERBEROS */
+
+ if (rem < 0)
+ exit(1);
+
+ if (dflag &&
+ setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
+ (void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
+ strerror(errno));
+ if (Dflag &&
+ setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
+ perror("rlogin: setsockopt NODELAY (ignored)");
+
+ one = IPTOS_LOWDELAY;
+ if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
+ perror("rlogin: setsockopt TOS (ignored)");
+
+ (void)setuid(uid);
+ doit(omask);
+ /*NOTREACHED*/
+}
+
+int child, defflags, deflflags, tabflag;
+char deferase, defkill;
+struct tchars deftc;
+struct ltchars defltc;
+struct tchars notc = { -1, -1, -1, -1, -1, -1 };
+struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
+
+void
+doit(omask)
+ long omask;
+{
+ struct sgttyb sb;
+
+ (void)ioctl(0, TIOCGETP, (char *)&sb);
+ defflags = sb.sg_flags;
+ tabflag = defflags & TBDELAY;
+ defflags &= ECHO | CRMOD;
+ deferase = sb.sg_erase;
+ defkill = sb.sg_kill;
+ (void)ioctl(0, TIOCLGET, &deflflags);
+ (void)ioctl(0, TIOCGETC, &deftc);
+ notc.t_startc = deftc.t_startc;
+ notc.t_stopc = deftc.t_stopc;
+ (void)ioctl(0, TIOCGLTC, &defltc);
+ (void)signal(SIGINT, SIG_IGN);
+ setsignal(SIGHUP);
+ setsignal(SIGQUIT);
+ child = fork();
+ if (child == -1) {
+ (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
+ done(1);
+ }
+ if (child == 0) {
+ mode(1);
+ if (reader(omask) == 0) {
+ msg("connection closed.");
+ exit(0);
+ }
+ sleep(1);
+ msg("\007connection closed.");
+ exit(1);
+ }
+
+ /*
+ * We may still own the socket, and may have a pending SIGURG (or might
+ * receive one soon) that we really want to send to the reader. When
+ * one of these comes in, the trap copytochild simply copies such
+ * signals to the child. We can now unblock SIGURG and SIGUSR1
+ * that were set above.
+ */
+ (void)sigsetmask(omask);
+ (void)signal(SIGCHLD, catch_child);
+ writer();
+ msg("closed connection.");
+ done(0);
+}
+
+/* trap a signal, unless it is being ignored. */
+void
+setsignal(sig)
+ int sig;
+{
+ int omask = sigblock(sigmask(sig));
+
+ if (signal(sig, exit) == SIG_IGN)
+ (void)signal(sig, SIG_IGN);
+ (void)sigsetmask(omask);
+}
+
+__dead void
+done(status)
+ int status;
+{
+ int w, wstatus;
+
+ mode(0);
+ if (child > 0) {
+ /* make sure catch_child does not snap it up */
+ (void)signal(SIGCHLD, SIG_DFL);
+ if (kill(child, SIGKILL) >= 0)
+ while ((w = wait(&wstatus)) > 0 && w != child);
+ }
+ exit(status);
+}
+
+int dosigwinch;
+
+/*
+ * This is called when the reader process gets the out-of-band (urgent)
+ * request to turn on the window-changing protocol.
+ */
+void
+writeroob(signo)
+ int signo;
+{
+ if (dosigwinch == 0) {
+ sendwindow();
+ (void)signal(SIGWINCH, sigwinch);
+ }
+ dosigwinch = 1;
+}
+
+void
+catch_child(signo)
+ int signo;
+{
+ union wait status;
+ int pid;
+
+ for (;;) {
+ pid = wait3((int *)&status, WNOHANG|WUNTRACED, NULL);
+ if (pid == 0)
+ return;
+ /* if the child (reader) dies, just quit */
+ if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
+ done((int)(status.w_termsig | status.w_retcode));
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * writer: write to remote: 0 -> line.
+ * ~. terminate
+ * ~^Z suspend rlogin process.
+ * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
+ */
+void
+writer()
+{
+ register int bol, local, n;
+ char c;
+
+ bol = 1; /* beginning of line */
+ local = 0;
+ for (;;) {
+ n = read(STDIN_FILENO, &c, 1);
+ if (n <= 0) {
+ if (n < 0 && errno == EINTR)
+ continue;
+ break;
+ }
+ /*
+ * If we're at the beginning of the line and recognize a
+ * command character, then we echo locally. Otherwise,
+ * characters are echo'd remotely. If the command character
+ * is doubled, this acts as a force and local echo is
+ * suppressed.
+ */
+ if (bol) {
+ bol = 0;
+ if (!noescape && c == escapechar) {
+ local = 1;
+ continue;
+ }
+ } else if (local) {
+ local = 0;
+ if (c == '.' || c == deftc.t_eofc) {
+ echo(c);
+ break;
+ }
+ if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
+ bol = 1;
+ echo(c);
+ stop(c);
+ continue;
+ }
+ if (c != escapechar)
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ (void)des_write(rem,
+ (char *)&escapechar, 1);
+ else
+#endif
+#endif
+ (void)write(rem, &escapechar, 1);
+ }
+
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ if (des_write(rem, &c, 1) == 0) {
+ msg("line gone");
+ break;
+ }
+ } else
+#endif
+#endif
+ if (write(rem, &c, 1) == 0) {
+ msg("line gone");
+ break;
+ }
+ bol = c == defkill || c == deftc.t_eofc ||
+ c == deftc.t_intrc || c == defltc.t_suspc ||
+ c == '\r' || c == '\n';
+ }
+}
+
+void
+#if __STDC__
+echo(register char c)
+#else
+echo(c)
+ register char c;
+#endif
+{
+ register char *p;
+ char buf[8];
+
+ p = buf;
+ c &= 0177;
+ *p++ = escapechar;
+ if (c < ' ') {
+ *p++ = '^';
+ *p++ = c + '@';
+ } else if (c == 0177) {
+ *p++ = '^';
+ *p++ = '?';
+ } else
+ *p++ = c;
+ *p++ = '\r';
+ *p++ = '\n';
+ (void)write(STDOUT_FILENO, buf, p - buf);
+}
+
+void
+#if __STDC__
+stop(char cmdc)
+#else
+stop(cmdc)
+ char cmdc;
+#endif
+{
+ mode(0);
+ (void)signal(SIGCHLD, SIG_IGN);
+ (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
+ (void)signal(SIGCHLD, catch_child);
+ mode(1);
+ sigwinch(0); /* check for size changes */
+}
+
+void
+sigwinch(signo)
+ int signo;
+{
+ struct winsize ws;
+
+ if (dosigwinch && get_window_size(0, &ws) == 0 &&
+ bcmp(&ws, &winsize, sizeof(ws))) {
+ winsize = ws;
+ sendwindow();
+ }
+}
+
+/*
+ * Send the window size to the server via the magic escape
+ */
+void
+sendwindow()
+{
+ struct winsize *wp;
+ char obuf[4 + sizeof (struct winsize)];
+
+ wp = (struct winsize *)(obuf+4);
+ obuf[0] = 0377;
+ obuf[1] = 0377;
+ obuf[2] = 's';
+ obuf[3] = 's';
+ wp->ws_row = htons(winsize.ws_row);
+ wp->ws_col = htons(winsize.ws_col);
+ wp->ws_xpixel = htons(winsize.ws_xpixel);
+ wp->ws_ypixel = htons(winsize.ws_ypixel);
+
+#ifdef CRYPT
+#ifdef KERBEROS
+ if(doencrypt)
+ (void)des_write(rem, obuf, sizeof(obuf));
+ else
+#endif
+#endif
+ (void)write(rem, obuf, sizeof(obuf));
+}
+
+/*
+ * reader: read from remote: line -> 1
+ */
+#define READING 1
+#define WRITING 2
+
+jmp_buf rcvtop;
+int ppid, rcvcnt, rcvstate;
+char rcvbuf[8 * 1024];
+
+void
+oob(signo)
+ int signo;
+{
+ struct sgttyb sb;
+ int atmark, n, out, rcvd;
+ char waste[BUFSIZ], mark;
+
+ out = O_RDWR;
+ rcvd = 0;
+ while (recv(rem, &mark, 1, MSG_OOB) < 0) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ /*
+ * Urgent data not here yet. It may not be possible
+ * to send it yet if we are blocked for output and
+ * our input buffer is full.
+ */
+ if (rcvcnt < sizeof(rcvbuf)) {
+ n = read(rem, rcvbuf + rcvcnt,
+ sizeof(rcvbuf) - rcvcnt);
+ if (n <= 0)
+ return;
+ rcvd += n;
+ } else {
+ n = read(rem, waste, sizeof(waste));
+ if (n <= 0)
+ return;
+ }
+ continue;
+ default:
+ return;
+ }
+ }
+ if (mark & TIOCPKT_WINDOW) {
+ /* Let server know about window size changes */
+ (void)kill(ppid, SIGUSR1);
+ }
+ if (!eight && (mark & TIOCPKT_NOSTOP)) {
+ (void)ioctl(0, TIOCGETP, (char *)&sb);
+ sb.sg_flags &= ~CBREAK;
+ sb.sg_flags |= RAW;
+ (void)ioctl(0, TIOCSETN, (char *)&sb);
+ notc.t_stopc = -1;
+ notc.t_startc = -1;
+ (void)ioctl(0, TIOCSETC, (char *)&notc);
+ }
+ if (!eight && (mark & TIOCPKT_DOSTOP)) {
+ (void)ioctl(0, TIOCGETP, (char *)&sb);
+ sb.sg_flags &= ~RAW;
+ sb.sg_flags |= CBREAK;
+ (void)ioctl(0, TIOCSETN, (char *)&sb);
+ notc.t_stopc = deftc.t_stopc;
+ notc.t_startc = deftc.t_startc;
+ (void)ioctl(0, TIOCSETC, (char *)&notc);
+ }
+ if (mark & TIOCPKT_FLUSHWRITE) {
+ (void)ioctl(1, TIOCFLUSH, (char *)&out);
+ for (;;) {
+ if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
+ (void)fprintf(stderr, "rlogin: ioctl: %s.\n",
+ strerror(errno));
+ break;
+ }
+ if (atmark)
+ break;
+ n = read(rem, waste, sizeof (waste));
+ if (n <= 0)
+ break;
+ }
+ /*
+ * Don't want any pending data to be output, so clear the recv
+ * buffer. If we were hanging on a write when interrupted,
+ * don't want it to restart. If we were reading, restart
+ * anyway.
+ */
+ rcvcnt = 0;
+ longjmp(rcvtop, 1);
+ }
+
+ /* oob does not do FLUSHREAD (alas!) */
+
+ /*
+ * If we filled the receive buffer while a read was pending, longjmp
+ * to the top to restart appropriately. Don't abort a pending write,
+ * however, or we won't know how much was written.
+ */
+ if (rcvd && rcvstate == READING)
+ longjmp(rcvtop, 1);
+}
+
+/* reader: read from remote: line -> 1 */
+int
+reader(omask)
+ int omask;
+{
+ int pid, n, remaining;
+ char *bufp;
+
+#if BSD >= 43 || defined(SUNOS4)
+ pid = getpid(); /* modern systems use positives for pid */
+#else
+ pid = -getpid(); /* old broken systems use negatives */
+#endif
+ (void)signal(SIGTTOU, SIG_IGN);
+ (void)signal(SIGURG, oob);
+ ppid = getppid();
+ (void)fcntl(rem, F_SETOWN, pid);
+ (void)setjmp(rcvtop);
+ (void)sigsetmask(omask);
+ bufp = rcvbuf;
+ for (;;) {
+ while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
+ rcvstate = WRITING;
+ n = write(STDOUT_FILENO, bufp, remaining);
+ if (n < 0) {
+ if (errno != EINTR)
+ return (-1);
+ continue;
+ }
+ bufp += n;
+ }
+ bufp = rcvbuf;
+ rcvcnt = 0;
+ rcvstate = READING;
+
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt)
+ rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
+ else
+#endif
+#endif
+ rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
+ if (rcvcnt == 0)
+ return (0);
+ if (rcvcnt < 0) {
+ if (errno == EINTR)
+ continue;
+ (void)fprintf(stderr, "rlogin: read: %s.\n",
+ strerror(errno));
+ return (-1);
+ }
+ }
+}
+
+void
+mode(f)
+ int f;
+{
+ struct ltchars *ltc;
+ struct sgttyb sb;
+ struct tchars *tc;
+ int lflags;
+
+ (void)ioctl(0, TIOCGETP, (char *)&sb);
+ (void)ioctl(0, TIOCLGET, (char *)&lflags);
+ switch(f) {
+ case 0:
+ sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
+ sb.sg_flags |= defflags|tabflag;
+ tc = &deftc;
+ ltc = &defltc;
+ sb.sg_kill = defkill;
+ sb.sg_erase = deferase;
+ lflags = deflflags;
+ break;
+ case 1:
+ sb.sg_flags |= (eight ? RAW : CBREAK);
+ sb.sg_flags &= ~defflags;
+ /* preserve tab delays, but turn off XTABS */
+ if ((sb.sg_flags & TBDELAY) == XTABS)
+ sb.sg_flags &= ~TBDELAY;
+ tc = &notc;
+ ltc = &noltc;
+ sb.sg_kill = sb.sg_erase = -1;
+ if (litout)
+ lflags |= LLITOUT;
+ break;
+ default:
+ return;
+ }
+ (void)ioctl(0, TIOCSLTC, (char *)ltc);
+ (void)ioctl(0, TIOCSETC, (char *)tc);
+ (void)ioctl(0, TIOCSETN, (char *)&sb);
+ (void)ioctl(0, TIOCLSET, (char *)&lflags);
+}
+
+void
+lostpeer(signo)
+ int signo;
+{
+ (void)signal(SIGPIPE, SIG_IGN);
+ msg("\007connection closed.");
+ done(1);
+}
+
+/* copy SIGURGs to the child process. */
+void
+copytochild(signo)
+ int signo;
+{
+ (void)kill(child, SIGURG);
+}
+
+void
+msg(str)
+ char *str;
+{
+ (void)fprintf(stderr, "rlogin: %s\r\n", str);
+}
+
+#ifdef KERBEROS
+/* VARARGS */
+void
+#if __STDC__
+warning(const char *fmt, ...)
+#else
+warning(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, ".\n");
+}
+#endif
+
+__dead void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
+#ifdef KERBEROS
+#ifdef CRYPT
+ "8DEKLx", " [-k realm] ");
+#else
+ "8DEKL", " [-k realm] ");
+#endif
+#else
+ "8DEL", " ");
+#endif
+ exit(1);
+}
+
+/*
+ * The following routine provides compatibility (such as it is) between older
+ * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
+ */
+#ifdef OLDSUN
+int
+get_window_size(fd, wp)
+ int fd;
+ struct winsize *wp;
+{
+ struct ttysize ts;
+ int error;
+
+ if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
+ return (error);
+ wp->ws_row = ts.ts_lines;
+ wp->ws_col = ts.ts_cols;
+ wp->ws_xpixel = 0;
+ wp->ws_ypixel = 0;
+ return (0);
+}
+#endif
+
+u_int
+getescape(p)
+ register char *p;
+{
+ long val;
+ int len;
+
+ if ((len = strlen(p)) == 1) /* use any single char, including '\' */
+ return ((u_int)*p);
+ /* otherwise, \nnn */
+ if (*p == '\\' && len >= 2 && len <= 4) {
+ val = strtol(++p, NULL, 8);
+ for (;;) {
+ if (!*++p)
+ return ((u_int)val);
+ if (*p < '0' || *p > '8')
+ break;
+ }
+ }
+ msg("illegal option value -- e");
+ usage();
+ /* NOTREACHED */
+}
diff --git a/usr.bin/rpcgen/Makefile b/usr.bin/rpcgen/Makefile
new file mode 100644
index 0000000..3379b27
--- /dev/null
+++ b/usr.bin/rpcgen/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.1 1994/08/07 18:01:27 wollman Exp $
+
+PROG= rpcgen
+SRCS= rpc_clntout.c rpc_cout.c rpc_hout.c rpc_main.c rpc_parse.c rpc_scan.c \
+ rpc_svcout.c rpc_util.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rpcgen/rpc_clntout.c b/usr.bin/rpcgen/rpc_clntout.c
new file mode 100644
index 0000000..b410fa3
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_clntout.c
@@ -0,0 +1,130 @@
+/* @(#)rpc_clntout.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_clntout.c 1.2 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_clntout.c,v 1.1 1994/08/07 18:01:28 wollman Exp $";
+#endif
+
+/*
+ * rpc_clntout.c, Client-stub outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsytsems, Inc.
+ */
+#include <stdio.h>
+#include <strings.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+#define DEFAULT_TIMEOUT 25 /* in seconds */
+
+static int write_program(), printbody();
+
+
+void
+write_stubs()
+{
+ list *l;
+ definition *def;
+
+ f_print(fout,
+ "\n/* Default timeout can be changed using clnt_control() */\n");
+ f_print(fout, "static struct timeval TIMEOUT = { %d, 0 };\n",
+ DEFAULT_TIMEOUT);
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ write_program(def);
+ }
+ }
+}
+
+
+static
+write_program(def)
+ definition *def;
+{
+ version_list *vp;
+ proc_list *proc;
+
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\n");
+ ptype(proc->res_prefix, proc->res_type, 1);
+ f_print(fout, "*\n");
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "(argp, clnt)\n");
+ f_print(fout, "\t");
+ ptype(proc->arg_prefix, proc->arg_type, 1);
+ f_print(fout, "*argp;\n");
+ f_print(fout, "\tCLIENT *clnt;\n");
+ f_print(fout, "{\n");
+ printbody(proc);
+ f_print(fout, "}\n\n");
+ }
+ }
+}
+
+static char *
+ampr(type)
+ char *type;
+{
+ if (isvectordef(type, REL_ALIAS)) {
+ return ("");
+ } else {
+ return ("&");
+ }
+}
+
+static
+printbody(proc)
+ proc_list *proc;
+{
+ f_print(fout, "\tstatic ");
+ if (streq(proc->res_type, "void")) {
+ f_print(fout, "char ");
+ } else {
+ ptype(proc->res_prefix, proc->res_type, 0);
+ }
+ f_print(fout, "res;\n");
+ f_print(fout, "\n");
+ f_print(fout, "\tbzero((char *)%sres, sizeof(res));\n",
+ ampr(proc->res_type));
+ f_print(fout,
+ "\tif (clnt_call(clnt, %s, xdr_%s, argp, xdr_%s, %sres, TIMEOUT) != RPC_SUCCESS) {\n",
+ proc->proc_name, stringfix(proc->arg_type),
+ stringfix(proc->res_type), ampr(proc->res_type));
+ f_print(fout, "\t\treturn (NULL);\n");
+ f_print(fout, "\t}\n");
+ if (streq(proc->res_type, "void")) {
+ f_print(fout, "\treturn ((void *)%sres);\n",
+ ampr(proc->res_type));
+ } else {
+ f_print(fout, "\treturn (%sres);\n", ampr(proc->res_type));
+ }
+}
diff --git a/usr.bin/rpcgen/rpc_cout.c b/usr.bin/rpcgen/rpc_cout.c
new file mode 100644
index 0000000..734b2d2
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_cout.c
@@ -0,0 +1,355 @@
+/* @(#)rpc_cout.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_cout.c 1.8 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_cout.c,v 1.1 1994/08/07 18:01:29 wollman Exp $";
+#endif
+
+/*
+ * rpc_cout.c, XDR routine outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <strings.h>
+#include "rpc_util.h"
+#include "rpc_parse.h"
+
+static int print_header(), print_trailer(), space(), emit_enum(),
+ emit_union(), emit_struct(), emit_typedef(), print_stat();
+
+
+/*
+ * Emit the C-routine for the given definition
+ */
+void
+emit(def)
+ definition *def;
+{
+ if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) {
+ return;
+ }
+ print_header(def);
+ switch (def->def_kind) {
+ case DEF_UNION:
+ emit_union(def);
+ break;
+ case DEF_ENUM:
+ emit_enum(def);
+ break;
+ case DEF_STRUCT:
+ emit_struct(def);
+ break;
+ case DEF_TYPEDEF:
+ emit_typedef(def);
+ break;
+ }
+ print_trailer();
+}
+
+static
+findtype(def, type)
+ definition *def;
+ char *type;
+{
+ if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) {
+ return (0);
+ } else {
+ return (streq(def->def_name, type));
+ }
+}
+
+static
+undefined(type)
+ char *type;
+{
+ definition *def;
+
+ def = (definition *) FINDVAL(defined, type, findtype);
+ return (def == NULL);
+}
+
+
+static
+print_header(def)
+ definition *def;
+{
+ space();
+ f_print(fout, "bool_t\n");
+ f_print(fout, "xdr_%s(xdrs, objp)\n", def->def_name);
+ f_print(fout, "\tXDR *xdrs;\n");
+ f_print(fout, "\t%s ", def->def_name);
+ if (def->def_kind != DEF_TYPEDEF ||
+ !isvectordef(def->def.ty.old_type, def->def.ty.rel)) {
+ f_print(fout, "*");
+ }
+ f_print(fout, "objp;\n");
+ f_print(fout, "{\n");
+}
+
+static
+print_trailer()
+{
+ f_print(fout, "\treturn (TRUE);\n");
+ f_print(fout, "}\n");
+ space();
+}
+
+
+static
+print_ifopen(indent, name)
+ int indent;
+ char *name;
+{
+ tabify(fout, indent);
+ f_print(fout, "if (!xdr_%s(xdrs", name);
+}
+
+
+static
+print_ifarg(arg)
+ char *arg;
+{
+ f_print(fout, ", %s", arg);
+}
+
+
+static
+print_ifsizeof(prefix, type)
+ char *prefix;
+ char *type;
+{
+ if (streq(type, "bool")) {
+ f_print(fout, ", sizeof(bool_t), xdr_bool");
+ } else {
+ f_print(fout, ", sizeof(");
+ if (undefined(type) && prefix) {
+ f_print(fout, "%s ", prefix);
+ }
+ f_print(fout, "%s), xdr_%s", type, type);
+ }
+}
+
+static
+print_ifclose(indent)
+ int indent;
+{
+ f_print(fout, ")) {\n");
+ tabify(fout, indent);
+ f_print(fout, "\treturn (FALSE);\n");
+ tabify(fout, indent);
+ f_print(fout, "}\n");
+}
+
+static
+space()
+{
+ f_print(fout, "\n\n");
+}
+
+static
+print_ifstat(indent, prefix, type, rel, amax, objname, name)
+ int indent;
+ char *prefix;
+ char *type;
+ relation rel;
+ char *amax;
+ char *objname;
+ char *name;
+{
+ char *alt = NULL;
+
+ switch (rel) {
+ case REL_POINTER:
+ print_ifopen(indent, "pointer");
+ print_ifarg("(char **)");
+ f_print(fout, "%s", objname);
+ print_ifsizeof(prefix, type);
+ break;
+ case REL_VECTOR:
+ if (streq(type, "string")) {
+ alt = "string";
+ } else if (streq(type, "opaque")) {
+ alt = "opaque";
+ }
+ if (alt) {
+ print_ifopen(indent, alt);
+ print_ifarg(objname);
+ } else {
+ print_ifopen(indent, "vector");
+ print_ifarg("(char *)");
+ f_print(fout, "%s", objname);
+ }
+ print_ifarg(amax);
+ if (!alt) {
+ print_ifsizeof(prefix, type);
+ }
+ break;
+ case REL_ARRAY:
+ if (streq(type, "string")) {
+ alt = "string";
+ } else if (streq(type, "opaque")) {
+ alt = "bytes";
+ }
+ if (streq(type, "string")) {
+ print_ifopen(indent, alt);
+ print_ifarg(objname);
+ } else {
+ if (alt) {
+ print_ifopen(indent, alt);
+ } else {
+ print_ifopen(indent, "array");
+ }
+ print_ifarg("(char **)");
+ if (*objname == '&') {
+ f_print(fout, "%s.%s_val, (u_int *)%s.%s_len",
+ objname, name, objname, name);
+ } else {
+ f_print(fout, "&%s->%s_val, (u_int *)&%s->%s_len",
+ objname, name, objname, name);
+ }
+ }
+ print_ifarg(amax);
+ if (!alt) {
+ print_ifsizeof(prefix, type);
+ }
+ break;
+ case REL_ALIAS:
+ print_ifopen(indent, type);
+ print_ifarg(objname);
+ break;
+ }
+ print_ifclose(indent);
+}
+
+
+/* ARGSUSED */
+static
+emit_enum(def)
+ definition *def;
+{
+ print_ifopen(1, "enum");
+ print_ifarg("(enum_t *)objp");
+ print_ifclose(1);
+}
+
+
+static
+emit_union(def)
+ definition *def;
+{
+ declaration *dflt;
+ case_list *cl;
+ declaration *cs;
+ char *object;
+ char *format = "&objp->%s_u.%s";
+
+ print_stat(&def->def.un.enum_decl);
+ f_print(fout, "\tswitch (objp->%s) {\n", def->def.un.enum_decl.name);
+ for (cl = def->def.un.cases; cl != NULL; cl = cl->next) {
+ cs = &cl->case_decl;
+ f_print(fout, "\tcase %s:\n", cl->case_name);
+ if (!streq(cs->type, "void")) {
+ object = alloc(strlen(def->def_name) + strlen(format) +
+ strlen(cs->name) + 1);
+ s_print(object, format, def->def_name, cs->name);
+ print_ifstat(2, cs->prefix, cs->type, cs->rel, cs->array_max,
+ object, cs->name);
+ free(object);
+ }
+ f_print(fout, "\t\tbreak;\n");
+ }
+ dflt = def->def.un.default_decl;
+ if (dflt != NULL) {
+ if (!streq(dflt->type, "void")) {
+ f_print(fout, "\tdefault:\n");
+ object = alloc(strlen(def->def_name) + strlen(format) +
+ strlen(dflt->name) + 1);
+ s_print(object, format, def->def_name, dflt->name);
+ print_ifstat(2, dflt->prefix, dflt->type, dflt->rel,
+ dflt->array_max, object, dflt->name);
+ free(object);
+ f_print(fout, "\t\tbreak;\n");
+ }
+ } else {
+ f_print(fout, "\tdefault:\n");
+ f_print(fout, "\t\treturn (FALSE);\n");
+ }
+ f_print(fout, "\t}\n");
+}
+
+
+
+static
+emit_struct(def)
+ definition *def;
+{
+ decl_list *dl;
+
+ for (dl = def->def.st.decls; dl != NULL; dl = dl->next) {
+ print_stat(&dl->decl);
+ }
+}
+
+
+
+
+static
+emit_typedef(def)
+ definition *def;
+{
+ char *prefix = def->def.ty.old_prefix;
+ char *type = def->def.ty.old_type;
+ char *amax = def->def.ty.array_max;
+ relation rel = def->def.ty.rel;
+
+ print_ifstat(1, prefix, type, rel, amax, "objp", def->def_name);
+}
+
+
+
+
+
+static
+print_stat(dec)
+ declaration *dec;
+{
+ char *prefix = dec->prefix;
+ char *type = dec->type;
+ char *amax = dec->array_max;
+ relation rel = dec->rel;
+ char name[256];
+
+ if (isvectordef(type, rel)) {
+ s_print(name, "objp->%s", dec->name);
+ } else {
+ s_print(name, "&objp->%s", dec->name);
+ }
+ print_ifstat(1, prefix, type, rel, amax, name, dec->name);
+}
diff --git a/usr.bin/rpcgen/rpc_hout.c b/usr.bin/rpcgen/rpc_hout.c
new file mode 100644
index 0000000..9c2cd4e
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_hout.c
@@ -0,0 +1,374 @@
+/* @(#)rpc_hout.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_hout.c 1.6 87/07/28 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_hout.c,v 1.1 1994/08/07 18:01:30 wollman Exp $";
+#endif
+
+static int pconstdef(), pstructdef(), puniondef(), pdefine(), pprogramdef(),
+ penumdef(), ptypedef(), pdeclaration(), undefined2();
+
+/*
+ * rpc_hout.c, Header file outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include "rpc_util.h"
+#include "rpc_parse.h"
+
+
+/*
+ * Print the C-version of an xdr definition
+ */
+void
+print_datadef(def)
+ definition *def;
+{
+ if (def->def_kind != DEF_CONST) {
+ f_print(fout, "\n");
+ }
+ switch (def->def_kind) {
+ case DEF_STRUCT:
+ pstructdef(def);
+ break;
+ case DEF_UNION:
+ puniondef(def);
+ break;
+ case DEF_ENUM:
+ penumdef(def);
+ break;
+ case DEF_TYPEDEF:
+ ptypedef(def);
+ break;
+ case DEF_PROGRAM:
+ pprogramdef(def);
+ break;
+ case DEF_CONST:
+ pconstdef(def);
+ break;
+ }
+ if (def->def_kind != DEF_PROGRAM && def->def_kind != DEF_CONST) {
+ f_print(fout, "bool_t xdr_%s();\n", def->def_name);
+ }
+ if (def->def_kind != DEF_CONST) {
+ f_print(fout, "\n");
+ }
+}
+
+static
+pconstdef(def)
+ definition *def;
+{
+ pdefine(def->def_name, def->def.co);
+}
+
+static
+pstructdef(def)
+ definition *def;
+{
+ decl_list *l;
+ char *name = def->def_name;
+
+ f_print(fout, "struct %s {\n", name);
+ for (l = def->def.st.decls; l != NULL; l = l->next) {
+ pdeclaration(name, &l->decl, 1);
+ }
+ f_print(fout, "};\n");
+ f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+static
+puniondef(def)
+ definition *def;
+{
+ case_list *l;
+ char *name = def->def_name;
+ declaration *decl;
+
+ f_print(fout, "struct %s {\n", name);
+ decl = &def->def.un.enum_decl;
+ if (streq(decl->type, "bool")) {
+ f_print(fout, "\tbool_t %s;\n", decl->name);
+ } else {
+ f_print(fout, "\t%s %s;\n", decl->type, decl->name);
+ }
+ f_print(fout, "\tunion {\n");
+ for (l = def->def.un.cases; l != NULL; l = l->next) {
+ pdeclaration(name, &l->case_decl, 2);
+ }
+ decl = def->def.un.default_decl;
+ if (decl && !streq(decl->type, "void")) {
+ pdeclaration(name, decl, 2);
+ }
+ f_print(fout, "\t} %s_u;\n", name);
+ f_print(fout, "};\n");
+ f_print(fout, "typedef struct %s %s;\n", name, name);
+}
+
+
+
+static
+pdefine(name, num)
+ char *name;
+ char *num;
+{
+ f_print(fout, "#define %s %s\n", name, num);
+}
+
+static
+puldefine(name, num)
+ char *name;
+ char *num;
+{
+ f_print(fout, "#define %s ((u_long)%s)\n", name, num);
+}
+
+static
+define_printed(stop, start)
+ proc_list *stop;
+ version_list *start;
+{
+ version_list *vers;
+ proc_list *proc;
+
+ for (vers = start; vers != NULL; vers = vers->next) {
+ for (proc = vers->procs; proc != NULL; proc = proc->next) {
+ if (proc == stop) {
+ return (0);
+ } else if (streq(proc->proc_name, stop->proc_name)) {
+ return (1);
+ }
+ }
+ }
+ abort();
+ /* NOTREACHED */
+}
+
+
+static
+pprogramdef(def)
+ definition *def;
+{
+ version_list *vers;
+ proc_list *proc;
+
+ puldefine(def->def_name, def->def.pr.prog_num);
+ for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) {
+ puldefine(vers->vers_name, vers->vers_num);
+ for (proc = vers->procs; proc != NULL; proc = proc->next) {
+ if (!define_printed(proc, def->def.pr.versions)) {
+ puldefine(proc->proc_name, proc->proc_num);
+ }
+ pprocdef(proc, vers);
+ }
+ }
+}
+
+
+pprocdef(proc, vp)
+ proc_list *proc;
+ version_list *vp;
+{
+ f_print(fout, "extern ");
+ if (proc->res_prefix) {
+ if (streq(proc->res_prefix, "enum")) {
+ f_print(fout, "enum ");
+ } else {
+ f_print(fout, "struct ");
+ }
+ }
+ if (streq(proc->res_type, "bool")) {
+ f_print(fout, "bool_t *");
+ } else if (streq(proc->res_type, "string")) {
+ f_print(fout, "char **");
+ } else {
+ f_print(fout, "%s *", fixtype(proc->res_type));
+ }
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "();\n");
+}
+
+static
+penumdef(def)
+ definition *def;
+{
+ char *name = def->def_name;
+ enumval_list *l;
+ char *last = NULL;
+ int count = 0;
+
+ f_print(fout, "enum %s {\n", name);
+ for (l = def->def.en.vals; l != NULL; l = l->next) {
+ f_print(fout, "\t%s", l->name);
+ if (l->assignment) {
+ f_print(fout, " = %s", l->assignment);
+ last = l->assignment;
+ count = 1;
+ } else {
+ if (last == NULL) {
+ f_print(fout, " = %d", count++);
+ } else {
+ f_print(fout, " = %s + %d", last, count++);
+ }
+ }
+ f_print(fout, ",\n");
+ }
+ f_print(fout, "};\n");
+ f_print(fout, "typedef enum %s %s;\n", name, name);
+}
+
+static
+ptypedef(def)
+ definition *def;
+{
+ char *name = def->def_name;
+ char *old = def->def.ty.old_type;
+ char prefix[8]; /* enough to contain "struct ", including NUL */
+ relation rel = def->def.ty.rel;
+
+
+ if (!streq(name, old)) {
+ if (streq(old, "string")) {
+ old = "char";
+ rel = REL_POINTER;
+ } else if (streq(old, "opaque")) {
+ old = "char";
+ } else if (streq(old, "bool")) {
+ old = "bool_t";
+ }
+ if (undefined2(old, name) && def->def.ty.old_prefix) {
+ s_print(prefix, "%s ", def->def.ty.old_prefix);
+ } else {
+ prefix[0] = 0;
+ }
+ f_print(fout, "typedef ");
+ switch (rel) {
+ case REL_ARRAY:
+ f_print(fout, "struct {\n");
+ f_print(fout, "\tu_int %s_len;\n", name);
+ f_print(fout, "\t%s%s *%s_val;\n", prefix, old, name);
+ f_print(fout, "} %s", name);
+ break;
+ case REL_POINTER:
+ f_print(fout, "%s%s *%s", prefix, old, name);
+ break;
+ case REL_VECTOR:
+ f_print(fout, "%s%s %s[%s]", prefix, old, name,
+ def->def.ty.array_max);
+ break;
+ case REL_ALIAS:
+ f_print(fout, "%s%s %s", prefix, old, name);
+ break;
+ }
+ f_print(fout, ";\n");
+ }
+}
+
+
+static
+pdeclaration(name, dec, tab)
+ char *name;
+ declaration *dec;
+ int tab;
+{
+ char buf[8]; /* enough to hold "struct ", include NUL */
+ char *prefix;
+ char *type;
+
+ if (streq(dec->type, "void")) {
+ return;
+ }
+ tabify(fout, tab);
+ if (streq(dec->type, name) && !dec->prefix) {
+ f_print(fout, "struct ");
+ }
+ if (streq(dec->type, "string")) {
+ f_print(fout, "char *%s", dec->name);
+ } else {
+ prefix = "";
+ if (streq(dec->type, "bool")) {
+ type = "bool_t";
+ } else if (streq(dec->type, "opaque")) {
+ type = "char";
+ } else {
+ if (dec->prefix) {
+ s_print(buf, "%s ", dec->prefix);
+ prefix = buf;
+ }
+ type = dec->type;
+ }
+ switch (dec->rel) {
+ case REL_ALIAS:
+ f_print(fout, "%s%s %s", prefix, type, dec->name);
+ break;
+ case REL_VECTOR:
+ f_print(fout, "%s%s %s[%s]", prefix, type, dec->name,
+ dec->array_max);
+ break;
+ case REL_POINTER:
+ f_print(fout, "%s%s *%s", prefix, type, dec->name);
+ break;
+ case REL_ARRAY:
+ f_print(fout, "struct {\n");
+ tabify(fout, tab);
+ f_print(fout, "\tu_int %s_len;\n", dec->name);
+ tabify(fout, tab);
+ f_print(fout, "\t%s%s *%s_val;\n", prefix, type, dec->name);
+ tabify(fout, tab);
+ f_print(fout, "} %s", dec->name);
+ break;
+ }
+ }
+ f_print(fout, ";\n");
+}
+
+
+
+static
+undefined2(type, stop)
+ char *type;
+ char *stop;
+{
+ list *l;
+ definition *def;
+
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind != DEF_PROGRAM) {
+ if (streq(def->def_name, stop)) {
+ return (1);
+ } else if (streq(def->def_name, type)) {
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
diff --git a/usr.bin/rpcgen/rpc_main.c b/usr.bin/rpcgen/rpc_main.c
new file mode 100644
index 0000000..1344eb4
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_main.c
@@ -0,0 +1,444 @@
+/* @(#)rpc_main.c 2.2 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_main.c 1.7 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_main.c,v 1.2 1995/03/04 17:47:49 nate Exp $";
+#endif
+
+/*
+ * rpc_main.c, Top level of the RPC protocol compiler.
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/file.h>
+#include "rpc_util.h"
+#include "rpc_parse.h"
+#include "rpc_scan.h"
+
+#define EXTEND 1 /* alias for TRUE */
+
+struct commandline {
+ int cflag;
+ int hflag;
+ int lflag;
+ int sflag;
+ int mflag;
+ char *infile;
+ char *outfile;
+};
+
+static char *cmdname;
+static char CPP[] = "/usr/bin/cpp";
+static char CPPFLAGS[] = "-C";
+static char *allv[] = {
+ "rpcgen", "-s", "udp", "-s", "tcp",
+};
+static int allc = sizeof(allv)/sizeof(allv[0]);
+
+
+static int h_output(), c_output(), s_output(), l_output(), do_registers(),
+ parseargs();
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ struct commandline cmd;
+
+ if (!parseargs(argc, argv, &cmd)) {
+ f_print(stderr,
+ "usage: %s infile\n", cmdname);
+ f_print(stderr,
+ " %s [-c | -h | -l | -m] [-o outfile] [infile]\n",
+ cmdname);
+ f_print(stderr,
+ " %s [-s udp|tcp]* [-o outfile] [infile]\n",
+ cmdname);
+ exit(1);
+ }
+ if (cmd.cflag) {
+ c_output(cmd.infile, "-DRPC_XDR", !EXTEND, cmd.outfile);
+ } else if (cmd.hflag) {
+ h_output(cmd.infile, "-DRPC_HDR", !EXTEND, cmd.outfile);
+ } else if (cmd.lflag) {
+ l_output(cmd.infile, "-DRPC_CLNT", !EXTEND, cmd.outfile);
+ } else if (cmd.sflag || cmd.mflag) {
+ s_output(argc, argv, cmd.infile, "-DRPC_SVC", !EXTEND,
+ cmd.outfile, cmd.mflag);
+ } else {
+ c_output(cmd.infile, "-DRPC_XDR", EXTEND, "_xdr.c");
+ reinitialize();
+ h_output(cmd.infile, "-DRPC_HDR", EXTEND, ".h");
+ reinitialize();
+ l_output(cmd.infile, "-DRPC_CLNT", EXTEND, "_clnt.c");
+ reinitialize();
+ s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND,
+ "_svc.c", cmd.mflag);
+ }
+ exit(0);
+}
+
+/*
+ * strip path and add extension to filename
+ */
+static char *
+extendfile(path, ext)
+ char *path;
+ char *ext;
+{
+ char *file;
+ char *res;
+ char *p;
+
+ if ((file = rindex(path, '/')) == NULL)
+ file = path;
+ else
+ file++;
+
+ res = alloc(strlen(file) + strlen(ext) + 1);
+ if (res == NULL) {
+ abort();
+ }
+ p = rindex(file, '.');
+ if (p == NULL) {
+ p = file + strlen(file);
+ }
+ (void) strcpy(res, file);
+ (void) strcpy(res + (p - file), ext);
+ return (res);
+}
+
+/*
+ * Open output file with given extension
+ */
+static
+open_output(infile, outfile)
+ char *infile;
+ char *outfile;
+{
+ if (outfile == NULL) {
+ fout = stdout;
+ return;
+ }
+ if (infile != NULL && streq(outfile, infile)) {
+ f_print(stderr, "%s: output would overwrite %s\n", cmdname,
+ infile);
+ crash();
+ }
+ fout = fopen(outfile, "w");
+ if (fout == NULL) {
+ f_print(stderr, "%s: unable to open ", cmdname);
+ perror(outfile);
+ crash();
+ }
+ record_open(outfile);
+}
+
+/*
+ * Open input file with given define for C-preprocessor
+ */
+static
+open_input(infile, define)
+ char *infile;
+ char *define;
+{
+ int pd[2];
+
+ infilename = (infile == NULL) ? "<stdin>" : infile;
+ (void) pipe(pd);
+ switch (fork()) {
+ case 0:
+ (void) close(1);
+ (void) dup2(pd[1], 1);
+ (void) close(pd[0]);
+ execl(CPP, CPP, CPPFLAGS, define, infile, NULL);
+ perror("execl");
+ exit(1);
+ case -1:
+ perror("fork");
+ exit(1);
+ }
+ (void) close(pd[1]);
+ fin = fdopen(pd[0], "r");
+ if (fin == NULL) {
+ f_print(stderr, "%s: ", cmdname);
+ perror(infilename);
+ crash();
+ }
+}
+
+/*
+ * Compile into an XDR routine output file
+ */
+static
+c_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ char *include;
+ char *outfilename;
+ long tell;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ }
+ tell = ftell(fout);
+ while (def = get_definition()) {
+ emit(def);
+ }
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ }
+}
+
+/*
+ * Compile into an XDR header file
+ */
+static
+h_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ definition *def;
+ char *outfilename;
+ long tell;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ tell = ftell(fout);
+ while (def = get_definition()) {
+ print_datadef(def);
+ }
+ if (extend && tell == ftell(fout)) {
+ (void) unlink(outfilename);
+ }
+}
+
+/*
+ * Compile into an RPC service
+ */
+static
+s_output(argc, argv, infile, define, extend, outfile, nomain)
+ int argc;
+ char *argv[];
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+ int nomain;
+{
+ char *include;
+ definition *def;
+ int foundprogram;
+ char *outfilename;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ f_print(fout, "#include <stdio.h>\n");
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ }
+ foundprogram = 0;
+ while (def = get_definition()) {
+ foundprogram |= (def->def_kind == DEF_PROGRAM);
+ }
+ if (extend && !foundprogram) {
+ (void) unlink(outfilename);
+ return;
+ }
+ if (nomain) {
+ write_programs((char *)NULL);
+ } else {
+ write_most();
+ do_registers(argc, argv);
+ write_rest();
+ write_programs("static");
+ }
+}
+
+static
+l_output(infile, define, extend, outfile)
+ char *infile;
+ char *define;
+ int extend;
+ char *outfile;
+{
+ char *include;
+ definition *def;
+ int foundprogram;
+ char *outfilename;
+
+ open_input(infile, define);
+ outfilename = extend ? extendfile(infile, outfile) : outfile;
+ open_output(infile, outfilename);
+ f_print(fout, "#include <rpc/rpc.h>\n");
+ if (infile && (include = extendfile(infile, ".h"))) {
+ f_print(fout, "#include \"%s\"\n", include);
+ free(include);
+ }
+ foundprogram = 0;
+ while (def = get_definition()) {
+ foundprogram |= (def->def_kind == DEF_PROGRAM);
+ }
+ if (extend && !foundprogram) {
+ (void) unlink(outfilename);
+ return;
+ }
+ write_stubs();
+}
+
+/*
+ * Perform registrations for service output
+ */
+static
+do_registers(argc, argv)
+ int argc;
+ char *argv[];
+
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (streq(argv[i], "-s")) {
+ write_register(argv[i + 1]);
+ i++;
+ }
+ }
+}
+
+/*
+ * Parse command line arguments
+ */
+static
+parseargs(argc, argv, cmd)
+ int argc;
+ char *argv[];
+ struct commandline *cmd;
+
+{
+ int i;
+ int j;
+ char c;
+ char flag[(1 << 8 * sizeof(char))];
+ int nflags;
+
+ cmdname = argv[0];
+ cmd->infile = cmd->outfile = NULL;
+ if (argc < 2) {
+ return (0);
+ }
+ flag['c'] = 0;
+ flag['h'] = 0;
+ flag['s'] = 0;
+ flag['o'] = 0;
+ flag['l'] = 0;
+ flag['m'] = 0;
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ if (cmd->infile) {
+ return (0);
+ }
+ cmd->infile = argv[i];
+ } else {
+ for (j = 1; argv[i][j] != 0; j++) {
+ c = argv[i][j];
+ switch (c) {
+ case 'c':
+ case 'h':
+ case 'l':
+ case 'm':
+ if (flag[c]) {
+ return (0);
+ }
+ flag[c] = 1;
+ break;
+ case 'o':
+ case 's':
+ if (argv[i][j - 1] != '-' ||
+ argv[i][j + 1] != 0) {
+ return (0);
+ }
+ flag[c] = 1;
+ if (++i == argc) {
+ return (0);
+ }
+ if (c == 's') {
+ if (!streq(argv[i], "udp") &&
+ !streq(argv[i], "tcp")) {
+ return (0);
+ }
+ } else if (c == 'o') {
+ if (cmd->outfile) {
+ return (0);
+ }
+ cmd->outfile = argv[i];
+ }
+ goto nextarg;
+
+ default:
+ return (0);
+ }
+ }
+ nextarg:
+ ;
+ }
+ }
+ cmd->cflag = flag['c'];
+ cmd->hflag = flag['h'];
+ cmd->sflag = flag['s'];
+ cmd->lflag = flag['l'];
+ cmd->mflag = flag['m'];
+ nflags = cmd->cflag + cmd->hflag + cmd->sflag + cmd->lflag + cmd->mflag;
+ if (nflags == 0) {
+ if (cmd->outfile != NULL || cmd->infile == NULL) {
+ return (0);
+ }
+ } else if (nflags > 1) {
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.bin/rpcgen/rpc_parse.c b/usr.bin/rpcgen/rpc_parse.c
new file mode 100644
index 0000000..d042b58
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_parse.c
@@ -0,0 +1,424 @@
+/* @(#)rpc_parse.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_parse.c 1.4 87/04/28 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_parse.c,v 1.2 1995/05/15 00:03:32 ache Exp $";
+#endif
+
+/*
+ * rpc_parse.c, Parser for the RPC protocol compiler
+ * Copyright (C) 1987 Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include "rpc_util.h"
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+
+static int isdefined(), def_struct(), def_program(), def_enum(), def_const(),
+ def_union(), def_typedef(), get_declaration(), get_type(),
+ unsigned_dec();
+/*
+ * return the next definition you see
+ */
+definition *
+get_definition()
+{
+ definition *defp;
+ token tok;
+
+ defp = ALLOC(definition);
+ get_token(&tok);
+ switch (tok.kind) {
+ case TOK_STRUCT:
+ def_struct(defp);
+ break;
+ case TOK_UNION:
+ def_union(defp);
+ break;
+ case TOK_TYPEDEF:
+ def_typedef(defp);
+ break;
+ case TOK_ENUM:
+ def_enum(defp);
+ break;
+ case TOK_PROGRAM:
+ def_program(defp);
+ break;
+ case TOK_CONST:
+ def_const(defp);
+ break;
+ case TOK_EOF:
+ return (NULL);
+ break;
+ default:
+ error("definition keyword expected");
+ }
+ scan(TOK_SEMICOLON, &tok);
+ isdefined(defp);
+ return (defp);
+}
+
+static
+isdefined(defp)
+ definition *defp;
+{
+ STOREVAL(&defined, defp);
+}
+
+
+static
+def_struct(defp)
+ definition *defp;
+{
+ token tok;
+ declaration dec;
+ decl_list *decls;
+ decl_list **tailp;
+
+ defp->def_kind = DEF_STRUCT;
+
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_LBRACE, &tok);
+ tailp = &defp->def.st.decls;
+ do {
+ get_declaration(&dec, DEF_STRUCT);
+ decls = ALLOC(decl_list);
+ decls->decl = dec;
+ *tailp = decls;
+ tailp = &decls->next;
+ scan(TOK_SEMICOLON, &tok);
+ peek(&tok);
+ } while (tok.kind != TOK_RBRACE);
+ get_token(&tok);
+ *tailp = NULL;
+}
+
+static
+def_program(defp)
+ definition *defp;
+{
+ token tok;
+ version_list *vlist;
+ version_list **vtailp;
+ proc_list *plist;
+ proc_list **ptailp;
+
+ defp->def_kind = DEF_PROGRAM;
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_LBRACE, &tok);
+ vtailp = &defp->def.pr.versions;
+ scan(TOK_VERSION, &tok);
+ do {
+ scan(TOK_IDENT, &tok);
+ vlist = ALLOC(version_list);
+ vlist->vers_name = tok.str;
+ scan(TOK_LBRACE, &tok);
+ ptailp = &vlist->procs;
+ do {
+ plist = ALLOC(proc_list);
+ get_type(&plist->res_prefix, &plist->res_type, DEF_PROGRAM);
+ if (streq(plist->res_type, "opaque")) {
+ error("illegal result type");
+ }
+ scan(TOK_IDENT, &tok);
+ plist->proc_name = tok.str;
+ scan(TOK_LPAREN, &tok);
+ get_type(&plist->arg_prefix, &plist->arg_type, DEF_PROGRAM);
+ if (streq(plist->arg_type, "opaque")) {
+ error("illegal argument type");
+ }
+ scan(TOK_RPAREN, &tok);
+ scan(TOK_EQUAL, &tok);
+ scan_num(&tok);
+ scan(TOK_SEMICOLON, &tok);
+ plist->proc_num = tok.str;
+ *ptailp = plist;
+ ptailp = &plist->next;
+ peek(&tok);
+ } while (tok.kind != TOK_RBRACE);
+ *ptailp = NULL;
+ *vtailp = vlist;
+ vtailp = &vlist->next;
+ scan(TOK_RBRACE, &tok);
+ scan(TOK_EQUAL, &tok);
+ scan_num(&tok);
+ vlist->vers_num = tok.str;
+ scan(TOK_SEMICOLON, &tok);
+ scan2(TOK_VERSION, TOK_RBRACE, &tok);
+ } while (tok.kind == TOK_VERSION);
+ scan(TOK_EQUAL, &tok);
+ scan_num(&tok);
+ defp->def.pr.prog_num = tok.str;
+ *vtailp = NULL;
+}
+
+static
+def_enum(defp)
+ definition *defp;
+{
+ token tok;
+ enumval_list *elist;
+ enumval_list **tailp;
+
+ defp->def_kind = DEF_ENUM;
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_LBRACE, &tok);
+ tailp = &defp->def.en.vals;
+ do {
+ scan(TOK_IDENT, &tok);
+ elist = ALLOC(enumval_list);
+ elist->name = tok.str;
+ elist->assignment = NULL;
+ scan3(TOK_COMMA, TOK_RBRACE, TOK_EQUAL, &tok);
+ if (tok.kind == TOK_EQUAL) {
+ scan_num(&tok);
+ elist->assignment = tok.str;
+ scan2(TOK_COMMA, TOK_RBRACE, &tok);
+ }
+ *tailp = elist;
+ tailp = &elist->next;
+ } while (tok.kind != TOK_RBRACE);
+ *tailp = NULL;
+}
+
+static
+def_const(defp)
+ definition *defp;
+{
+ token tok;
+
+ defp->def_kind = DEF_CONST;
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_EQUAL, &tok);
+ scan2(TOK_IDENT, TOK_STRCONST, &tok);
+ defp->def.co = tok.str;
+}
+
+static
+def_union(defp)
+ definition *defp;
+{
+ token tok;
+ declaration dec;
+ case_list *cases;
+ case_list **tailp;
+
+ defp->def_kind = DEF_UNION;
+ scan(TOK_IDENT, &tok);
+ defp->def_name = tok.str;
+ scan(TOK_SWITCH, &tok);
+ scan(TOK_LPAREN, &tok);
+ get_declaration(&dec, DEF_UNION);
+ defp->def.un.enum_decl = dec;
+ tailp = &defp->def.un.cases;
+ scan(TOK_RPAREN, &tok);
+ scan(TOK_LBRACE, &tok);
+ scan(TOK_CASE, &tok);
+ while (tok.kind == TOK_CASE) {
+ scan(TOK_IDENT, &tok);
+ cases = ALLOC(case_list);
+ cases->case_name = tok.str;
+ scan(TOK_COLON, &tok);
+ get_declaration(&dec, DEF_UNION);
+ cases->case_decl = dec;
+ *tailp = cases;
+ tailp = &cases->next;
+ scan(TOK_SEMICOLON, &tok);
+ scan3(TOK_CASE, TOK_DEFAULT, TOK_RBRACE, &tok);
+ }
+ *tailp = NULL;
+ if (tok.kind == TOK_DEFAULT) {
+ scan(TOK_COLON, &tok);
+ get_declaration(&dec, DEF_UNION);
+ defp->def.un.default_decl = ALLOC(declaration);
+ *defp->def.un.default_decl = dec;
+ scan(TOK_SEMICOLON, &tok);
+ scan(TOK_RBRACE, &tok);
+ } else {
+ defp->def.un.default_decl = NULL;
+ }
+}
+
+
+static
+def_typedef(defp)
+ definition *defp;
+{
+ declaration dec;
+
+ defp->def_kind = DEF_TYPEDEF;
+ get_declaration(&dec, DEF_TYPEDEF);
+ defp->def_name = dec.name;
+ defp->def.ty.old_prefix = dec.prefix;
+ defp->def.ty.old_type = dec.type;
+ defp->def.ty.rel = dec.rel;
+ defp->def.ty.array_max = dec.array_max;
+}
+
+
+static
+get_declaration(dec, dkind)
+ declaration *dec;
+ defkind dkind;
+{
+ token tok;
+
+ get_type(&dec->prefix, &dec->type, dkind);
+ dec->rel = REL_ALIAS;
+ if (streq(dec->type, "void")) {
+ return;
+ }
+ scan2(TOK_STAR, TOK_IDENT, &tok);
+ if (tok.kind == TOK_STAR) {
+ dec->rel = REL_POINTER;
+ scan(TOK_IDENT, &tok);
+ }
+ dec->name = tok.str;
+ if (peekscan(TOK_LBRACKET, &tok)) {
+ if (dec->rel == REL_POINTER) {
+ error("no array-of-pointer declarations -- use typedef");
+ }
+ dec->rel = REL_VECTOR;
+ scan_num(&tok);
+ dec->array_max = tok.str;
+ scan(TOK_RBRACKET, &tok);
+ } else if (peekscan(TOK_LANGLE, &tok)) {
+ if (dec->rel == REL_POINTER) {
+ error("no array-of-pointer declarations -- use typedef");
+ }
+ dec->rel = REL_ARRAY;
+ if (peekscan(TOK_RANGLE, &tok)) {
+ dec->array_max = "~0"; /* unspecified size, use max */
+ } else {
+ scan_num(&tok);
+ dec->array_max = tok.str;
+ scan(TOK_RANGLE, &tok);
+ }
+ }
+ if (streq(dec->type, "opaque")) {
+ if (dec->rel != REL_ARRAY && dec->rel != REL_VECTOR) {
+ error("array declaration expected");
+ }
+ } else if (streq(dec->type, "string")) {
+ if (dec->rel != REL_ARRAY) {
+ error("variable-length array declaration expected");
+ }
+ }
+}
+
+
+static
+get_type(prefixp, typep, dkind)
+ char **prefixp;
+ char **typep;
+ defkind dkind;
+{
+ token tok;
+
+ *prefixp = NULL;
+ get_token(&tok);
+ switch (tok.kind) {
+ case TOK_IDENT:
+ *typep = tok.str;
+ break;
+ case TOK_STRUCT:
+ case TOK_ENUM:
+ case TOK_UNION:
+ *prefixp = tok.str;
+ scan(TOK_IDENT, &tok);
+ *typep = tok.str;
+ break;
+ case TOK_UNSIGNED:
+ unsigned_dec(typep);
+ break;
+ case TOK_SHORT:
+ *typep = "short";
+ (void) peekscan(TOK_INT, &tok);
+ break;
+ case TOK_LONG:
+ *typep = "long";
+ (void) peekscan(TOK_INT, &tok);
+ break;
+ case TOK_VOID:
+ if (dkind != DEF_UNION && dkind != DEF_PROGRAM) {
+ error("voids allowed only inside union and program definitions");
+ }
+ *typep = tok.str;
+ break;
+ case TOK_STRING:
+ case TOK_OPAQUE:
+ case TOK_CHAR:
+ case TOK_INT:
+ case TOK_FLOAT:
+ case TOK_DOUBLE:
+ case TOK_BOOL:
+ *typep = tok.str;
+ break;
+ default:
+ error("expected type specifier");
+ }
+}
+
+
+static
+unsigned_dec(typep)
+ char **typep;
+{
+ token tok;
+
+ peek(&tok);
+ switch (tok.kind) {
+ case TOK_CHAR:
+ get_token(&tok);
+ *typep = "u_char";
+ break;
+ case TOK_SHORT:
+ get_token(&tok);
+ *typep = "u_short";
+ (void) peekscan(TOK_INT, &tok);
+ break;
+ case TOK_LONG:
+ get_token(&tok);
+ *typep = "u_long";
+ (void) peekscan(TOK_INT, &tok);
+ break;
+ case TOK_INT:
+ get_token(&tok);
+ *typep = "u_int";
+ break;
+ default:
+ *typep = "u_int";
+ break;
+ }
+}
diff --git a/usr.bin/rpcgen/rpc_parse.h b/usr.bin/rpcgen/rpc_parse.h
new file mode 100644
index 0000000..38b8644
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_parse.h
@@ -0,0 +1,159 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ *
+ * from: @(#)rpc_parse.h 1.3 87/03/09 (C) 1987 SMI
+ * $Id: rpc_parse.h,v 1.1 1994/08/07 18:01:33 wollman Exp $
+ */
+
+/*
+ * rpc_parse.h, Definitions for the RPCL parser
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+enum defkind {
+ DEF_CONST,
+ DEF_STRUCT,
+ DEF_UNION,
+ DEF_ENUM,
+ DEF_TYPEDEF,
+ DEF_PROGRAM
+};
+typedef enum defkind defkind;
+
+typedef char *const_def;
+
+enum relation {
+ REL_VECTOR, /* fixed length array */
+ REL_ARRAY, /* variable length array */
+ REL_POINTER, /* pointer */
+ REL_ALIAS, /* simple */
+};
+typedef enum relation relation;
+
+struct typedef_def {
+ char *old_prefix;
+ char *old_type;
+ relation rel;
+ char *array_max;
+};
+typedef struct typedef_def typedef_def;
+
+
+struct enumval_list {
+ char *name;
+ char *assignment;
+ struct enumval_list *next;
+};
+typedef struct enumval_list enumval_list;
+
+struct enum_def {
+ enumval_list *vals;
+};
+typedef struct enum_def enum_def;
+
+
+struct declaration {
+ char *prefix;
+ char *type;
+ char *name;
+ relation rel;
+ char *array_max;
+};
+typedef struct declaration declaration;
+
+
+struct decl_list {
+ declaration decl;
+ struct decl_list *next;
+};
+typedef struct decl_list decl_list;
+
+struct struct_def {
+ decl_list *decls;
+};
+typedef struct struct_def struct_def;
+
+
+struct case_list {
+ char *case_name;
+ declaration case_decl;
+ struct case_list *next;
+};
+typedef struct case_list case_list;
+
+struct union_def {
+ declaration enum_decl;
+ case_list *cases;
+ declaration *default_decl;
+};
+typedef struct union_def union_def;
+
+
+
+struct proc_list {
+ char *proc_name;
+ char *proc_num;
+ char *arg_type;
+ char *arg_prefix;
+ char *res_type;
+ char *res_prefix;
+ struct proc_list *next;
+};
+typedef struct proc_list proc_list;
+
+
+struct version_list {
+ char *vers_name;
+ char *vers_num;
+ proc_list *procs;
+ struct version_list *next;
+};
+typedef struct version_list version_list;
+
+struct program_def {
+ char *prog_num;
+ version_list *versions;
+};
+typedef struct program_def program_def;
+
+struct definition {
+ char *def_name;
+ defkind def_kind;
+ union {
+ const_def co;
+ struct_def st;
+ union_def un;
+ enum_def en;
+ typedef_def ty;
+ program_def pr;
+ } def;
+};
+typedef struct definition definition;
+
+/* @(#)rpc_parse.h 2.1 88/08/01 4.0 RPCSRC */
+definition *get_definition();
diff --git a/usr.bin/rpcgen/rpc_scan.c b/usr.bin/rpcgen/rpc_scan.c
new file mode 100644
index 0000000..6450ab2
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_scan.c
@@ -0,0 +1,476 @@
+/* @(#)rpc_scan.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_scan.c 1.6 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_scan.c,v 1.1 1994/08/07 18:01:34 wollman Exp $";
+#endif
+
+/*
+ * rpc_scan.c, Scanner for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <strings.h>
+#include "rpc_scan.h"
+#include "rpc_util.h"
+
+#define startcomment(where) (where[0] == '/' && where[1] == '*')
+#define endcomment(where) (where[-1] == '*' && where[0] == '/')
+
+static int pushed = 0; /* is a token pushed */
+static token lasttok; /* last token, if pushed */
+
+static int unget_token(), findstrconst(), findconst(), findkind(), cppline(),
+ directive(), printdirective(), docppline();
+/*
+ * scan expecting 1 given token
+ */
+void
+scan(expect, tokp)
+ tok_kind expect;
+ token *tokp;
+{
+ get_token(tokp);
+ if (tokp->kind != expect) {
+ expected1(expect);
+ }
+}
+
+/*
+ * scan expecting 2 given tokens
+ */
+void
+scan2(expect1, expect2, tokp)
+ tok_kind expect1;
+ tok_kind expect2;
+ token *tokp;
+{
+ get_token(tokp);
+ if (tokp->kind != expect1 && tokp->kind != expect2) {
+ expected2(expect1, expect2);
+ }
+}
+
+/*
+ * scan expecting 3 given token
+ */
+void
+scan3(expect1, expect2, expect3, tokp)
+ tok_kind expect1;
+ tok_kind expect2;
+ tok_kind expect3;
+ token *tokp;
+{
+ get_token(tokp);
+ if (tokp->kind != expect1 && tokp->kind != expect2
+ && tokp->kind != expect3) {
+ expected3(expect1, expect2, expect3);
+ }
+}
+
+
+/*
+ * scan expecting a constant, possibly symbolic
+ */
+void
+scan_num(tokp)
+ token *tokp;
+{
+ get_token(tokp);
+ switch (tokp->kind) {
+ case TOK_IDENT:
+ break;
+ default:
+ error("constant or identifier expected");
+ }
+}
+
+
+/*
+ * Peek at the next token
+ */
+void
+peek(tokp)
+ token *tokp;
+{
+ get_token(tokp);
+ unget_token(tokp);
+}
+
+
+/*
+ * Peek at the next token and scan it if it matches what you expect
+ */
+int
+peekscan(expect, tokp)
+ tok_kind expect;
+ token *tokp;
+{
+ peek(tokp);
+ if (tokp->kind == expect) {
+ get_token(tokp);
+ return (1);
+ }
+ return (0);
+}
+
+
+
+/*
+ * Get the next token, printing out any directive that are encountered.
+ */
+void
+get_token(tokp)
+ token *tokp;
+{
+ int commenting;
+
+ if (pushed) {
+ pushed = 0;
+ *tokp = lasttok;
+ return;
+ }
+ commenting = 0;
+ for (;;) {
+ if (*where == 0) {
+ for (;;) {
+ if (!fgets(curline, MAXLINESIZE, fin)) {
+ tokp->kind = TOK_EOF;
+ *where = 0;
+ return;
+ }
+ linenum++;
+ if (commenting) {
+ break;
+ } else if (cppline(curline)) {
+ docppline(curline, &linenum,
+ &infilename);
+ } else if (directive(curline)) {
+ printdirective(curline);
+ } else {
+ break;
+ }
+ }
+ where = curline;
+ } else if (isspace(*where)) {
+ while (isspace(*where)) {
+ where++; /* eat */
+ }
+ } else if (commenting) {
+ where++;
+ if (endcomment(where)) {
+ where++;
+ commenting--;
+ }
+ } else if (startcomment(where)) {
+ where += 2;
+ commenting++;
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * 'where' is not whitespace, comment or directive Must be a token!
+ */
+ switch (*where) {
+ case ':':
+ tokp->kind = TOK_COLON;
+ where++;
+ break;
+ case ';':
+ tokp->kind = TOK_SEMICOLON;
+ where++;
+ break;
+ case ',':
+ tokp->kind = TOK_COMMA;
+ where++;
+ break;
+ case '=':
+ tokp->kind = TOK_EQUAL;
+ where++;
+ break;
+ case '*':
+ tokp->kind = TOK_STAR;
+ where++;
+ break;
+ case '[':
+ tokp->kind = TOK_LBRACKET;
+ where++;
+ break;
+ case ']':
+ tokp->kind = TOK_RBRACKET;
+ where++;
+ break;
+ case '{':
+ tokp->kind = TOK_LBRACE;
+ where++;
+ break;
+ case '}':
+ tokp->kind = TOK_RBRACE;
+ where++;
+ break;
+ case '(':
+ tokp->kind = TOK_LPAREN;
+ where++;
+ break;
+ case ')':
+ tokp->kind = TOK_RPAREN;
+ where++;
+ break;
+ case '<':
+ tokp->kind = TOK_LANGLE;
+ where++;
+ break;
+ case '>':
+ tokp->kind = TOK_RANGLE;
+ where++;
+ break;
+
+ case '"':
+ tokp->kind = TOK_STRCONST;
+ findstrconst(&where, &tokp->str);
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ tokp->kind = TOK_IDENT;
+ findconst(&where, &tokp->str);
+ break;
+
+
+ default:
+ if (!(isalpha(*where) || *where == '_')) {
+ char buf[100];
+ char *p;
+
+ s_print(buf, "illegal character in file: ");
+ p = buf + strlen(buf);
+ if (isprint(*where)) {
+ s_print(p, "%c", *where);
+ } else {
+ s_print(p, "%d", *where);
+ }
+ error(buf);
+ }
+ findkind(&where, tokp);
+ break;
+ }
+}
+
+
+
+static
+unget_token(tokp)
+ token *tokp;
+{
+ lasttok = *tokp;
+ pushed = 1;
+}
+
+
+static
+findstrconst(str, val)
+ char **str;
+ char **val;
+{
+ char *p;
+ int size;
+
+ p = *str;
+ do {
+ *p++;
+ } while (*p && *p != '"');
+ if (*p == 0) {
+ error("unterminated string constant");
+ }
+ p++;
+ size = p - *str;
+ *val = alloc(size + 1);
+ (void) strncpy(*val, *str, size);
+ (*val)[size] = 0;
+ *str = p;
+}
+
+static
+findconst(str, val)
+ char **str;
+ char **val;
+{
+ char *p;
+ int size;
+
+ p = *str;
+ if (*p == '0' && *(p + 1) == 'x') {
+ p++;
+ do {
+ p++;
+ } while (isxdigit(*p));
+ } else {
+ do {
+ p++;
+ } while (isdigit(*p));
+ }
+ size = p - *str;
+ *val = alloc(size + 1);
+ (void) strncpy(*val, *str, size);
+ (*val)[size] = 0;
+ *str = p;
+}
+
+
+
+static token symbols[] = {
+ {TOK_CONST, "const"},
+ {TOK_UNION, "union"},
+ {TOK_SWITCH, "switch"},
+ {TOK_CASE, "case"},
+ {TOK_DEFAULT, "default"},
+ {TOK_STRUCT, "struct"},
+ {TOK_TYPEDEF, "typedef"},
+ {TOK_ENUM, "enum"},
+ {TOK_OPAQUE, "opaque"},
+ {TOK_BOOL, "bool"},
+ {TOK_VOID, "void"},
+ {TOK_CHAR, "char"},
+ {TOK_INT, "int"},
+ {TOK_UNSIGNED, "unsigned"},
+ {TOK_SHORT, "short"},
+ {TOK_LONG, "long"},
+ {TOK_FLOAT, "float"},
+ {TOK_DOUBLE, "double"},
+ {TOK_STRING, "string"},
+ {TOK_PROGRAM, "program"},
+ {TOK_VERSION, "version"},
+ {TOK_EOF, "??????"},
+};
+
+
+static
+findkind(mark, tokp)
+ char **mark;
+ token *tokp;
+{
+
+ int len;
+ token *s;
+ char *str;
+
+ str = *mark;
+ for (s = symbols; s->kind != TOK_EOF; s++) {
+ len = strlen(s->str);
+ if (strncmp(str, s->str, len) == 0) {
+ if (!isalnum(str[len]) && str[len] != '_') {
+ tokp->kind = s->kind;
+ tokp->str = s->str;
+ *mark = str + len;
+ return;
+ }
+ }
+ }
+ tokp->kind = TOK_IDENT;
+ for (len = 0; isalnum(str[len]) || str[len] == '_'; len++);
+ tokp->str = alloc(len + 1);
+ (void) strncpy(tokp->str, str, len);
+ tokp->str[len] = 0;
+ *mark = str + len;
+}
+
+static
+cppline(line)
+ char *line;
+{
+ return (line == curline && *line == '#');
+}
+
+static
+directive(line)
+ char *line;
+{
+ return (line == curline && *line == '%');
+}
+
+static
+printdirective(line)
+ char *line;
+{
+ f_print(fout, "%s", line + 1);
+}
+
+static
+docppline(line, lineno, fname)
+ char *line;
+ int *lineno;
+ char **fname;
+{
+ char *file;
+ int num;
+ char *p;
+
+ line++;
+ while (isspace(*line)) {
+ line++;
+ }
+ num = atoi(line);
+ while (isdigit(*line)) {
+ line++;
+ }
+ while (isspace(*line)) {
+ line++;
+ }
+ if (*line != '"') {
+ error("preprocessor error");
+ }
+ line++;
+ p = file = alloc(strlen(line) + 1);
+ while (*line && *line != '"') {
+ *p++ = *line++;
+ }
+ if (*line == 0) {
+ error("preprocessor error");
+ }
+ *p = 0;
+ if (*file == 0) {
+ *fname = NULL;
+ } else {
+ *fname = file;
+ }
+ *lineno = num - 1;
+}
diff --git a/usr.bin/rpcgen/rpc_scan.h b/usr.bin/rpcgen/rpc_scan.h
new file mode 100644
index 0000000..579df0f
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_scan.h
@@ -0,0 +1,103 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ *
+ * from: @(#)rpc_scan.h 1.3 87/03/09 (C) 1987 SMI
+ * from: @(#)rpc_scan.h 2.1 88/08/01 4.0 RPCSRC
+ * $Id: rpc_scan.h,v 1.1 1994/08/07 18:01:35 wollman Exp $
+ */
+
+/*
+ * rpc_scan.h, Definitions for the RPCL scanner
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+
+/*
+ * kinds of tokens
+ */
+enum tok_kind {
+ TOK_IDENT,
+ TOK_STRCONST,
+ TOK_LPAREN,
+ TOK_RPAREN,
+ TOK_LBRACE,
+ TOK_RBRACE,
+ TOK_LBRACKET,
+ TOK_RBRACKET,
+ TOK_LANGLE,
+ TOK_RANGLE,
+ TOK_STAR,
+ TOK_COMMA,
+ TOK_EQUAL,
+ TOK_COLON,
+ TOK_SEMICOLON,
+ TOK_CONST,
+ TOK_STRUCT,
+ TOK_UNION,
+ TOK_SWITCH,
+ TOK_CASE,
+ TOK_DEFAULT,
+ TOK_ENUM,
+ TOK_TYPEDEF,
+ TOK_INT,
+ TOK_SHORT,
+ TOK_LONG,
+ TOK_UNSIGNED,
+ TOK_FLOAT,
+ TOK_DOUBLE,
+ TOK_OPAQUE,
+ TOK_CHAR,
+ TOK_STRING,
+ TOK_BOOL,
+ TOK_VOID,
+ TOK_PROGRAM,
+ TOK_VERSION,
+ TOK_EOF
+};
+typedef enum tok_kind tok_kind;
+
+/*
+ * a token
+ */
+struct token {
+ tok_kind kind;
+ char *str;
+};
+typedef struct token token;
+
+
+/*
+ * routine interface
+ */
+void scanprint();
+void scan();
+void scan2();
+void scan3();
+void scan_num();
+void peek();
+int peekscan();
+void get_token();
diff --git a/usr.bin/rpcgen/rpc_svcout.c b/usr.bin/rpcgen/rpc_svcout.c
new file mode 100644
index 0000000..c27fff0
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_svcout.c
@@ -0,0 +1,276 @@
+/* @(#)rpc_svcout.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_svcout.c 1.6 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_svcout.c,v 1.2 1995/03/04 17:47:50 nate Exp $";
+#endif
+
+/*
+ * rpc_svcout.c, Server-skeleton outputter for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsytsems, Inc.
+ */
+#include <stdio.h>
+#include <strings.h>
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+static char RQSTP[] = "rqstp";
+static char TRANSP[] = "transp";
+static char ARG[] = "argument";
+static char RESULT[] = "result";
+static char ROUTINE[] = "local";
+
+static int write_program(), printerr(), printif();
+/*
+ * write most of the service, that is, everything but the registrations.
+ */
+void
+write_most()
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout, "\nstatic void ");
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, "();");
+ }
+ }
+ }
+ f_print(fout, "\n\n");
+ f_print(fout, "main()\n");
+ f_print(fout, "{\n");
+ f_print(fout, "\tSVCXPRT *%s;\n", TRANSP);
+ f_print(fout, "\n");
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind != DEF_PROGRAM) {
+ continue;
+ }
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout, "\t(void)pmap_unset(%s, %s);\n", def->def_name, vp->vers_name);
+ }
+ }
+}
+
+
+/*
+ * write a registration for the given transport
+ */
+void
+write_register(transp)
+ char *transp;
+{
+ list *l;
+ definition *def;
+ version_list *vp;
+
+ f_print(fout, "\n");
+ f_print(fout, "\t%s = svc%s_create(RPC_ANYSOCK", TRANSP, transp);
+ if (streq(transp, "tcp")) {
+ f_print(fout, ", 0, 0");
+ }
+ f_print(fout, ");\n");
+ f_print(fout, "\tif (%s == NULL) {\n", TRANSP);
+ f_print(fout, "\t\t(void)fprintf(stderr, \"cannot create %s service.\\n\");\n", transp);
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind != DEF_PROGRAM) {
+ continue;
+ }
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout,
+ "\tif (!svc_register(%s, %s, %s, ",
+ TRANSP, def->def_name, vp->vers_name);
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, ", IPPROTO_%s)) {\n",
+ streq(transp, "udp") ? "UDP" : "TCP");
+ f_print(fout,
+ "\t\t(void)fprintf(stderr, \"unable to register (%s, %s, %s).\\n\");\n",
+ def->def_name, vp->vers_name, transp);
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+ }
+ }
+}
+
+
+/*
+ * write the rest of the service
+ */
+void
+write_rest()
+{
+ f_print(fout, "\tsvc_run();\n");
+ f_print(fout, "\t(void)fprintf(stderr, \"svc_run returned\\n\");\n");
+ f_print(fout, "\texit(1);\n");
+ f_print(fout, "}\n");
+}
+
+void
+write_programs(storage)
+ char *storage;
+{
+ list *l;
+ definition *def;
+
+ for (l = defined; l != NULL; l = l->next) {
+ def = (definition *) l->val;
+ if (def->def_kind == DEF_PROGRAM) {
+ write_program(def, storage);
+ }
+ }
+}
+
+
+static
+write_program(def, storage)
+ definition *def;
+ char *storage;
+{
+ version_list *vp;
+ proc_list *proc;
+ int filled;
+
+ for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) {
+ f_print(fout, "\n");
+ if (storage != NULL) {
+ f_print(fout, "%s ", storage);
+ }
+ f_print(fout, "void\n");
+ pvname(def->def_name, vp->vers_num);
+ f_print(fout, "(%s, %s)\n", RQSTP, TRANSP);
+ f_print(fout, " struct svc_req *%s;\n", RQSTP);
+ f_print(fout, " SVCXPRT *%s;\n", TRANSP);
+ f_print(fout, "{\n");
+
+ filled = 0;
+ f_print(fout, "\tunion {\n");
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ if (streq(proc->arg_type, "void")) {
+ continue;
+ }
+ filled = 1;
+ f_print(fout, "\t\t");
+ ptype(proc->arg_prefix, proc->arg_type, 0);
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, "_arg;\n");
+ }
+ if (!filled) {
+ f_print(fout, "\t\tint fill;\n");
+ }
+ f_print(fout, "\t} %s;\n", ARG);
+ f_print(fout, "\tchar *%s;\n", RESULT);
+ f_print(fout, "\tbool_t (*xdr_%s)(), (*xdr_%s)();\n", ARG, RESULT);
+ f_print(fout, "\tchar *(*%s)();\n", ROUTINE);
+ f_print(fout, "\n");
+ f_print(fout, "\tswitch (%s->rq_proc) {\n", RQSTP);
+
+ if (!nullproc(vp->procs)) {
+ f_print(fout, "\tcase NULLPROC:\n");
+ f_print(fout, "\t\t(void)svc_sendreply(%s, xdr_void, (char *)NULL);\n", TRANSP);
+ f_print(fout, "\t\treturn;\n\n");
+ }
+ for (proc = vp->procs; proc != NULL; proc = proc->next) {
+ f_print(fout, "\tcase %s:\n", proc->proc_name);
+ f_print(fout, "\t\txdr_%s = xdr_%s;\n", ARG,
+ stringfix(proc->arg_type));
+ f_print(fout, "\t\txdr_%s = xdr_%s;\n", RESULT,
+ stringfix(proc->res_type));
+ f_print(fout, "\t\t%s = (char *(*)()) ", ROUTINE);
+ pvname(proc->proc_name, vp->vers_num);
+ f_print(fout, ";\n");
+ f_print(fout, "\t\tbreak;\n\n");
+ }
+ f_print(fout, "\tdefault:\n");
+ printerr("noproc", TRANSP);
+ f_print(fout, "\t\treturn;\n");
+ f_print(fout, "\t}\n");
+
+ f_print(fout, "\tbzero((char *)&%s, sizeof(%s));\n", ARG, ARG);
+ printif("getargs", TRANSP, "(caddr_t)&", ARG);
+ printerr("decode", TRANSP);
+ f_print(fout, "\t\treturn;\n");
+ f_print(fout, "\t}\n");
+
+ f_print(fout, "\t%s = (*%s)(&%s, %s);\n", RESULT, ROUTINE, ARG,
+ RQSTP);
+ f_print(fout,
+ "\tif (%s != NULL && !svc_sendreply(%s, xdr_%s, %s)) {\n",
+ RESULT, TRANSP, RESULT, RESULT);
+ printerr("systemerr", TRANSP);
+ f_print(fout, "\t}\n");
+
+ printif("freeargs", TRANSP, "(caddr_t)&", ARG);
+ f_print(fout, "\t\t(void)fprintf(stderr, \"unable to free arguments\\n\");\n");
+ f_print(fout, "\t\texit(1);\n");
+ f_print(fout, "\t}\n");
+
+ f_print(fout, "}\n\n");
+ }
+}
+
+static
+printerr(err, transp)
+ char *err;
+ char *transp;
+{
+ f_print(fout, "\t\tsvcerr_%s(%s);\n", err, transp);
+}
+
+static
+printif(proc, transp, prefix, arg)
+ char *proc;
+ char *transp;
+ char *prefix;
+ char *arg;
+{
+ f_print(fout, "\tif (!svc_%s(%s, xdr_%s, %s%s)) {\n",
+ proc, transp, arg, prefix, arg);
+}
+
+
+nullproc(proc)
+ proc_list *proc;
+{
+ for (; proc != NULL; proc = proc->next) {
+ if (streq(proc->proc_num, "0")) {
+ return (1);
+ }
+ }
+ return (0);
+}
diff --git a/usr.bin/rpcgen/rpc_util.c b/usr.bin/rpcgen/rpc_util.c
new file mode 100644
index 0000000..fb3307d
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_util.c
@@ -0,0 +1,439 @@
+/* @(#)rpc_util.c 2.1 88/08/01 4.0 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpc_util.c 1.5 87/06/24 (C) 1987 SMI";*/
+static char rcsid[] = "$Id: rpc_util.c,v 1.1 1994/08/07 18:01:37 wollman Exp $";
+#endif
+
+/*
+ * rpc_util.c, Utility routines for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+#include <stdio.h>
+#include "rpc_scan.h"
+#include "rpc_parse.h"
+#include "rpc_util.h"
+
+char curline[MAXLINESIZE]; /* current read line */
+char *where = curline; /* current point in line */
+int linenum = 0; /* current line number */
+
+char *infilename; /* input filename */
+
+#define NFILES 4
+char *outfiles[NFILES]; /* output file names */
+int nfiles;
+
+FILE *fout; /* file pointer of current output */
+FILE *fin; /* file pointer of current input */
+
+list *defined; /* list of defined things */
+
+static int printwhere();
+
+/*
+ * Reinitialize the world
+ */
+reinitialize()
+{
+ bzero(curline, MAXLINESIZE);
+ where = curline;
+ linenum = 0;
+ defined = NULL;
+}
+
+/*
+ * string equality
+ */
+streq(a, b)
+ char *a;
+ char *b;
+{
+ return (strcmp(a, b) == 0);
+}
+
+/*
+ * find a value in a list
+ */
+char *
+findval(lst, val, cmp)
+ list *lst;
+ char *val;
+ int (*cmp) ();
+
+{
+ for (; lst != NULL; lst = lst->next) {
+ if ((*cmp) (lst->val, val)) {
+ return (lst->val);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * store a value in a list
+ */
+void
+storeval(lstp, val)
+ list **lstp;
+ char *val;
+{
+ list **l;
+ list *lst;
+
+ for (l = lstp; *l != NULL; l = (list **) & (*l)->next);
+ lst = ALLOC(list);
+ lst->val = val;
+ lst->next = NULL;
+ *l = lst;
+}
+
+
+static
+findit(def, type)
+ definition *def;
+ char *type;
+{
+ return (streq(def->def_name, type));
+}
+
+
+static char *
+fixit(type, orig)
+ char *type;
+ char *orig;
+{
+ definition *def;
+
+ def = (definition *) FINDVAL(defined, type, findit);
+ if (def == NULL || def->def_kind != DEF_TYPEDEF) {
+ return (orig);
+ }
+ switch (def->def.ty.rel) {
+ case REL_VECTOR:
+ return (def->def.ty.old_type);
+ case REL_ALIAS:
+ return (fixit(def->def.ty.old_type, orig));
+ default:
+ return (orig);
+ }
+}
+
+char *
+fixtype(type)
+ char *type;
+{
+ return (fixit(type, type));
+}
+
+char *
+stringfix(type)
+ char *type;
+{
+ if (streq(type, "string")) {
+ return ("wrapstring");
+ } else {
+ return (type);
+ }
+}
+
+void
+ptype(prefix, type, follow)
+ char *prefix;
+ char *type;
+ int follow;
+{
+ if (prefix != NULL) {
+ if (streq(prefix, "enum")) {
+ f_print(fout, "enum ");
+ } else {
+ f_print(fout, "struct ");
+ }
+ }
+ if (streq(type, "bool")) {
+ f_print(fout, "bool_t ");
+ } else if (streq(type, "string")) {
+ f_print(fout, "char *");
+ } else {
+ f_print(fout, "%s ", follow ? fixtype(type) : type);
+ }
+}
+
+
+static
+typedefed(def, type)
+ definition *def;
+ char *type;
+{
+ if (def->def_kind != DEF_TYPEDEF || def->def.ty.old_prefix != NULL) {
+ return (0);
+ } else {
+ return (streq(def->def_name, type));
+ }
+}
+
+isvectordef(type, rel)
+ char *type;
+ relation rel;
+{
+ definition *def;
+
+ for (;;) {
+ switch (rel) {
+ case REL_VECTOR:
+ return (!streq(type, "string"));
+ case REL_ARRAY:
+ return (0);
+ case REL_POINTER:
+ return (0);
+ case REL_ALIAS:
+ def = (definition *) FINDVAL(defined, type, typedefed);
+ if (def == NULL) {
+ return (0);
+ }
+ type = def->def.ty.old_type;
+ rel = def->def.ty.rel;
+ }
+ }
+}
+
+
+static char *
+locase(str)
+ char *str;
+{
+ char c;
+ static char buf[100];
+ char *p = buf;
+
+ while (c = *str++) {
+ *p++ = (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c;
+ }
+ *p = 0;
+ return (buf);
+}
+
+
+void
+pvname(pname, vnum)
+ char *pname;
+ char *vnum;
+{
+ f_print(fout, "%s_%s", locase(pname), vnum);
+}
+
+
+/*
+ * print a useful (?) error message, and then die
+ */
+void
+error(msg)
+ char *msg;
+{
+ printwhere();
+ f_print(stderr, "%s, line %d: ", infilename, linenum);
+ f_print(stderr, "%s\n", msg);
+ crash();
+}
+
+/*
+ * Something went wrong, unlink any files that we may have created and then
+ * die.
+ */
+crash()
+{
+ int i;
+
+ for (i = 0; i < nfiles; i++) {
+ (void) unlink(outfiles[i]);
+ }
+ exit(1);
+}
+
+
+void
+record_open(file)
+ char *file;
+{
+ if (nfiles < NFILES) {
+ outfiles[nfiles++] = file;
+ } else {
+ f_print(stderr, "too many files!\n");
+ crash();
+ }
+}
+
+static char expectbuf[100];
+static char *toktostr();
+
+/*
+ * error, token encountered was not the expected one
+ */
+void
+expected1(exp1)
+ tok_kind exp1;
+{
+ s_print(expectbuf, "expected '%s'",
+ toktostr(exp1));
+ error(expectbuf);
+}
+
+/*
+ * error, token encountered was not one of two expected ones
+ */
+void
+expected2(exp1, exp2)
+ tok_kind exp1, exp2;
+{
+ s_print(expectbuf, "expected '%s' or '%s'",
+ toktostr(exp1),
+ toktostr(exp2));
+ error(expectbuf);
+}
+
+/*
+ * error, token encountered was not one of 3 expected ones
+ */
+void
+expected3(exp1, exp2, exp3)
+ tok_kind exp1, exp2, exp3;
+{
+ s_print(expectbuf, "expected '%s', '%s' or '%s'",
+ toktostr(exp1),
+ toktostr(exp2),
+ toktostr(exp3));
+ error(expectbuf);
+}
+
+void
+tabify(f, tab)
+ FILE *f;
+ int tab;
+{
+ while (tab--) {
+ (void) fputc('\t', f);
+ }
+}
+
+
+
+static token tokstrings[] = {
+ {TOK_IDENT, "identifier"},
+ {TOK_CONST, "const"},
+ {TOK_RPAREN, ")"},
+ {TOK_LPAREN, "("},
+ {TOK_RBRACE, "}"},
+ {TOK_LBRACE, "{"},
+ {TOK_LBRACKET, "["},
+ {TOK_RBRACKET, "]"},
+ {TOK_STAR, "*"},
+ {TOK_COMMA, ","},
+ {TOK_EQUAL, "="},
+ {TOK_COLON, ":"},
+ {TOK_SEMICOLON, ";"},
+ {TOK_UNION, "union"},
+ {TOK_STRUCT, "struct"},
+ {TOK_SWITCH, "switch"},
+ {TOK_CASE, "case"},
+ {TOK_DEFAULT, "default"},
+ {TOK_ENUM, "enum"},
+ {TOK_TYPEDEF, "typedef"},
+ {TOK_INT, "int"},
+ {TOK_SHORT, "short"},
+ {TOK_LONG, "long"},
+ {TOK_UNSIGNED, "unsigned"},
+ {TOK_DOUBLE, "double"},
+ {TOK_FLOAT, "float"},
+ {TOK_CHAR, "char"},
+ {TOK_STRING, "string"},
+ {TOK_OPAQUE, "opaque"},
+ {TOK_BOOL, "bool"},
+ {TOK_VOID, "void"},
+ {TOK_PROGRAM, "program"},
+ {TOK_VERSION, "version"},
+ {TOK_EOF, "??????"}
+};
+
+static char *
+toktostr(kind)
+ tok_kind kind;
+{
+ token *sp;
+
+ for (sp = tokstrings; sp->kind != TOK_EOF && sp->kind != kind; sp++);
+ return (sp->str);
+}
+
+
+
+static
+printbuf()
+{
+ char c;
+ int i;
+ int cnt;
+
+# define TABSIZE 4
+
+ for (i = 0; c = curline[i]; i++) {
+ if (c == '\t') {
+ cnt = 8 - (i % TABSIZE);
+ c = ' ';
+ } else {
+ cnt = 1;
+ }
+ while (cnt--) {
+ (void) fputc(c, stderr);
+ }
+ }
+}
+
+
+static
+printwhere()
+{
+ int i;
+ char c;
+ int cnt;
+
+ printbuf();
+ for (i = 0; i < where - curline; i++) {
+ c = curline[i];
+ if (c == '\t') {
+ cnt = 8 - (i % TABSIZE);
+ } else {
+ cnt = 1;
+ }
+ while (cnt--) {
+ (void) fputc('^', stderr);
+ }
+ }
+ (void) fputc('\n', stderr);
+}
diff --git a/usr.bin/rpcgen/rpc_util.h b/usr.bin/rpcgen/rpc_util.h
new file mode 100644
index 0000000..8aeea35
--- /dev/null
+++ b/usr.bin/rpcgen/rpc_util.h
@@ -0,0 +1,113 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ *
+ * from: @(#)rpc_util.h 1.6 87/06/24 (C) 1987 SMI
+ * from: @(#)rpc_util.h 2.1 88/08/01 4.0 RPCSRC
+ * $Id: rpc_util.h,v 1.1 1994/08/07 18:01:38 wollman Exp $
+ */
+
+/*
+ * rpc_util.h, Useful definitions for the RPC protocol compiler
+ * Copyright (C) 1987, Sun Microsystems, Inc.
+ */
+extern char *malloc();
+
+#define alloc(size) malloc((unsigned)(size))
+#define ALLOC(object) (object *) malloc(sizeof(object))
+
+#define s_print (void) sprintf
+#define f_print (void) fprintf
+
+struct list {
+ char *val;
+ struct list *next;
+};
+typedef struct list list;
+
+/*
+ * Global variables
+ */
+#define MAXLINESIZE 1024
+extern char curline[MAXLINESIZE];
+extern char *where;
+extern int linenum;
+
+extern char *infilename;
+extern FILE *fout;
+extern FILE *fin;
+
+extern list *defined;
+
+/*
+ * rpc_util routines
+ */
+void storeval();
+
+#define STOREVAL(list,item) \
+ storeval(list,(char *)item)
+
+char *findval();
+
+#define FINDVAL(list,item,finder) \
+ findval(list, (char *) item, finder)
+
+char *fixtype();
+char *stringfix();
+void pvname();
+void ptype();
+int isvectordef();
+int streq();
+void error();
+void expected1();
+void expected2();
+void expected3();
+void tabify();
+void record_open();
+
+/*
+ * rpc_cout routines
+ */
+void cprint();
+void emit();
+
+/*
+ * rpc_hout routines
+ */
+void print_datadef();
+
+/*
+ * rpc_svcout routines
+ */
+void write_most();
+void write_register();
+void write_rest();
+
+/*
+ * rpc_clntout routines
+ */
+void write_stubs();
diff --git a/usr.bin/rpcgen/rpcgen.1 b/usr.bin/rpcgen/rpcgen.1
new file mode 100644
index 0000000..5fd9274
--- /dev/null
+++ b/usr.bin/rpcgen/rpcgen.1
@@ -0,0 +1,156 @@
+.\" from: @(#)rpcgen.1 2.2 88/08/02 4.0 RPCSRC
+.\" $Id: rpcgen.1,v 1.1 1993/09/13 23:20:21 jtc Exp $
+.\"
+.Dd January 18, 1988
+.Dt RPCGEN 1
+.Os
+.Sh NAME
+.Nm rpcgen
+.Nd RPC protocol compiler
+.Sh SYNOPSIS
+.Nm rpcgen
+.Ar infile
+.Nm rpcgen
+.Fl c Li |
+.Fl h Li |
+.Fl l Li |
+.Fl m
+.Op Fl o Ar outfile
+.Op Ar infile
+.Nm rpcgen
+.Fl s Ar transport
+.Op Fl o Ar outfile
+.Op Ar infile
+.Sh DESCRIPTION
+.Nm rpcgen
+is a tool that generates C code to implement an
+.Tn RPC
+protocol. The input to
+.Nm rpcgen
+is a language similar to C known as
+.Tn RPC
+Language (Remote Procedure Call Language). Information about the
+syntax of
+.Tn RPC
+Language is available in the
+.Rs
+.%T "rpcgen Programming Guide"
+.Re
+.Pp
+.Nm rpcgen
+is normally used as in the first synopsis where it takes an input file
+and generates four output files. If the
+.Ar infile
+is named
+.Pa proto.x ,
+then
+.Nm rpcgen
+will generate a header file in
+.Pa proto.h ,
+.Tn XDR
+routines in
+.Pa proto_xdr.c ,
+server-side stubs in
+.Pa proto_svc.c ,
+and client-side stubs in
+.Pa proto_clnt.c .
+.Pp
+The other synopses shown above are used when one does not want to
+generate all the output files, but only a particular one.
+.\" Their usage is described in the
+.\" .Sx USAGE
+.\" section below.
+.Pp
+The C-preprocessor,
+.Xr cpp 1 ,
+is run on all input files before they are actually
+interpreted by
+.Nm rpcgen ,
+so all the
+.Xr cpp
+directives are legal within an
+.Nm rpcgen
+input file. For each type of output file,
+.Nm rpcgen
+defines a special
+.Xr cpp
+symbol for use by the
+.Nm rpcgen
+programmer:
+.Pp
+.Bl -tag -width RPC_CLNT -compact
+.It Dv RPC_HDR
+defined when compiling into header files
+.It Dv RPC_XDR
+defined when compiling into
+.Tn XDR
+routines
+.It Dv RPC_SVC
+defined when compiling into server-side stubs
+.It Dv RPC_CLNT
+defined when compiling into client-side stubs
+.El
+.Pp
+In addition,
+.Nm rpcgen
+does a little preprocessing of its own. Any line beginning with
+.Sq %
+is passed directly into the output file, uninterpreted by
+.Nm rpcgen .
+.Pp
+You can customize some of your
+.Tn XDR
+routines by leaving those data types undefined. For every data type
+that is undefined,
+.Nm rpcgen
+will assume that there exists a routine with the name
+.Tn xdr_
+prepended to the name of the undefined type.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl c
+Compile into
+.Tn XDR
+routines.
+.It Fl h
+Compile into C data-definitions (a header file).
+.It Fl l
+Compile into client-side stubs.
+.It Fl m
+Compile into server-side stubs, but do not generate a
+.Fn main
+routine. This option is useful for doing callback-routines and for
+people who need to write their own
+.Fn main
+routine to do initialization.
+.It Fl o Ar outfile
+Specify the name of the output file. If none is specified, standard
+output is used (
+.Fl c ,
+.Fl h ,
+.Fl l
+and
+.Fl s
+modes only).
+.It Fl s Ar transport
+Compile into server-side stubs, using the the given transport. The
+supported transports are
+.Tn udp
+and
+.Tn tcp .
+This option may be invoked more than once so as to compile a server
+that serves multiple transports.
+.El
+.Sh SEE ALSO
+.Xr cpp 1
+.Rs
+.%T "rpcgen Programming Guide"
+.Re
+.Sh BUGS
+Nesting is not supported. As a work-around, structures can be
+declared at top-level, and their name used inside other structures in
+order to achieve the same effect.
+.Pp
+Name clashes can occur when using program definitions, since the
+apparent scoping does not really apply. Most of these can be avoided
+by giving unique names for programs, versions, procedures and types.
diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile
new file mode 100644
index 0000000..1605846
--- /dev/null
+++ b/usr.bin/rpcinfo/Makefile
@@ -0,0 +1,8 @@
+# from: @(#)Makefile 5.2 (Berkeley) 5/11/90
+# $Id: Makefile,v 1.2 1993/11/10 03:46:45 smace Exp $
+
+PROG= rpcinfo
+MAN8 = rpcinfo.8
+
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rpcinfo/rpcinfo.8 b/usr.bin/rpcinfo/rpcinfo.8
new file mode 100644
index 0000000..c2c556d
--- /dev/null
+++ b/usr.bin/rpcinfo/rpcinfo.8
@@ -0,0 +1,166 @@
+.\" from: @(#)rpcinfo.8c 2.2 88/08/03 4.0 RPCSRC; from 1.24 88/02/25 SMI
+.\" $Id: rpcinfo.8,v 1.1 1993/09/13 23:22:41 jtc Exp $
+.\"
+.Dd December 17, 1987
+.Dt RPCINFO 8
+.Os
+.Sh NAME
+.Nm rpcinfo
+.Nd report RPC information
+.Sh SYNOPSIS
+.Nm rpcinfo
+.Fl p
+.Op Ar host
+.Nm rpcinfo
+.Op Fl n Ar portnum
+.Fl u Ar host
+.Ar program
+.Op Ar version
+.Nm rpcinfo
+.Op Fl n Ar portnum
+.Fl t Ar host
+.Ar program
+.Op Ar version
+.Nm rpcinfo
+.Fl b
+.Ar program version
+.Nm rpcinfo
+.Fl d
+.Ar program version
+.Sh DESCRIPTION
+.Nm rpcinfo
+makes an
+.Tn RPC
+call to an
+.Tn RPC
+server and reports what it finds.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl p
+Probe the portmapper on
+.Ar host ,
+and print a list of all registered
+.Tn RPC
+programs. If
+.Ar host
+is not specified, it defaults to the value returned by
+.Xr hostname 1 .
+.It Fl u
+Make an
+.Tn RPC
+call to procedure 0 of
+.Ar program
+on the specified
+.Ar host
+using
+.Tn UDP ,
+and report whether a response was received.
+.It Fl t
+Make an
+.Tn RPC
+call to procedure 0 of
+.Ar program
+on the specified
+.Ar host
+using
+.Tn TCP ,
+and report whether a response was received.
+.It Fl n
+Use
+.Ar portnum
+as the port number for the
+.Fl t
+and
+.Fl u
+options instead of the port number given by the portmapper.
+.It Fl b
+Make an
+.Tn RPC
+broadcast to procedure 0 of the specified
+.Ar program
+and
+.Ar version
+using
+.Tn UDP
+and report all hosts that respond.
+.It Fl d
+Delete registration for the
+.Tn RPC
+service of the specified
+.Ar program
+and
+.Ar version .
+This option can be exercised only by the super-user.
+.El
+.Pp
+The
+.Ar program
+argument can be either a name or a number.
+.Pp
+If a
+.Ar version
+is specified,
+.Nm rpcinfo
+attempts to call that version of the specified
+.Ar program .
+Otherwise,
+.Nm rpcinfo
+attempts to find all the registered version
+numbers for the specified
+.Ar program
+by calling version 0 (which is presumed not
+to exist; if it does exist,
+.Ar rpcinfo
+attempts to obtain this information by calling
+an extremely high version
+number instead) and attempts to call each registered version.
+Note: the version number is required for
+.Fl b
+and
+.Fl d
+options.
+.Sh EXAMPLES
+To show all of the
+.Tn RPC
+services registered on the local machine use:
+.Pp
+.Dl example% rpcinfo -p
+.Pp
+To show all of the
+.Tn RPC
+services registered on the machine named
+.Ar klaxon
+use:
+.Pp
+.Dl example% rpcinfo -p klaxon
+.Pp
+To show all machines on the local net that are running the Yellow Pages
+service use:
+.Pp
+.Dl example% rpcinfo -b ypserv 'version' | uniq
+.Pp
+where 'version' is the current Yellow Pages version obtained from the
+results of the
+.Fl p
+switch above.
+.Pp
+To delete the registration for version 1 of the
+.Nm walld
+service use:
+.Pp
+.Dl example% rpcinfo -d walld 1
+.Sh SEE ALSO
+.Xr rpc 5 ,
+.Xr portmap 8
+.Rs
+.%T "RPC Programming Guide"
+.Re
+.Sh BUGS
+In releases prior to SunOS 3.0, the Network File System (NFS) did not
+register itself with the portmapper;
+.Nm rpcinfo
+cannot be used to make
+.Tn RPC
+calls to the
+.Tn NFS
+server on hosts running such releases.
diff --git a/usr.bin/rpcinfo/rpcinfo.c b/usr.bin/rpcinfo/rpcinfo.c
new file mode 100644
index 0000000..0258461
--- /dev/null
+++ b/usr.bin/rpcinfo/rpcinfo.c
@@ -0,0 +1,666 @@
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rpcinfo.c 1.22 87/08/12 SMI";*/
+/*static char sccsid[] = "from: @(#)rpcinfo.c 2.2 88/08/11 4.0 RPCSRC";*/
+static char rcsid[] = "$Id: rpcinfo.c,v 1.1 1994/08/07 18:23:25 wollman Exp $";
+#endif
+
+/*
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * rpcinfo: ping a particular rpc program
+ * or dump the portmapper
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <signal.h>
+#include <ctype.h>
+
+#define MAXHOSTLEN 256
+
+#define MIN_VERS ((u_long) 0)
+#define MAX_VERS ((u_long) 4294967295UL)
+
+static void udpping(/*u_short portflag, int argc, char **argv*/);
+static void tcpping(/*u_short portflag, int argc, char **argv*/);
+static int pstatus(/*CLIENT *client, u_long prognum, u_long vers*/);
+static void pmapdump(/*int argc, char **argv*/);
+static bool_t reply_proc(/*void *res, struct sockaddr_in *who*/);
+static void brdcst(/*int argc, char **argv*/);
+static void deletereg(/* int argc, char **argv */) ;
+static void usage(/*void*/);
+static u_long getprognum(/*char *arg*/);
+static u_long getvers(/*char *arg*/);
+static void get_inet_address(/*struct sockaddr_in *addr, char *host*/);
+extern u_long inet_addr(); /* in 4.2BSD, arpa/inet.h called that a in_addr */
+extern char *inet_ntoa();
+
+/*
+ * Functions to be performed.
+ */
+#define NONE 0 /* no function */
+#define PMAPDUMP 1 /* dump portmapper registrations */
+#define TCPPING 2 /* ping TCP service */
+#define UDPPING 3 /* ping UDP service */
+#define BRDCST 4 /* ping broadcast UDP service */
+#define DELETES 5 /* delete registration for the service */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int c;
+ extern char *optarg;
+ extern int optind;
+ int errflg;
+ int function;
+ u_short portnum;
+
+ function = NONE;
+ portnum = 0;
+ errflg = 0;
+ while ((c = getopt(argc, argv, "ptubdn:")) != EOF) {
+ switch (c) {
+
+ case 'p':
+ if (function != NONE)
+ errflg = 1;
+ else
+ function = PMAPDUMP;
+ break;
+
+ case 't':
+ if (function != NONE)
+ errflg = 1;
+ else
+ function = TCPPING;
+ break;
+
+ case 'u':
+ if (function != NONE)
+ errflg = 1;
+ else
+ function = UDPPING;
+ break;
+
+ case 'b':
+ if (function != NONE)
+ errflg = 1;
+ else
+ function = BRDCST;
+ break;
+
+ case 'n':
+ portnum = (u_short) atoi(optarg); /* hope we don't get bogus # */
+ break;
+
+ case 'd':
+ if (function != NONE)
+ errflg = 1;
+ else
+ function = DELETES;
+ break;
+
+ case '?':
+ errflg = 1;
+ }
+ }
+
+ if (errflg || function == NONE) {
+ usage();
+ return (1);
+ }
+
+ switch (function) {
+
+ case PMAPDUMP:
+ if (portnum != 0) {
+ usage();
+ return (1);
+ }
+ pmapdump(argc - optind, argv + optind);
+ break;
+
+ case UDPPING:
+ udpping(portnum, argc - optind, argv + optind);
+ break;
+
+ case TCPPING:
+ tcpping(portnum, argc - optind, argv + optind);
+ break;
+
+ case BRDCST:
+ if (portnum != 0) {
+ usage();
+ return (1);
+ }
+ brdcst(argc - optind, argv + optind);
+ break;
+
+ case DELETES:
+ deletereg(argc - optind, argv + optind);
+ break;
+ }
+
+ return (0);
+}
+
+static void
+udpping(portnum, argc, argv)
+ u_short portnum;
+ int argc;
+ char **argv;
+{
+ struct timeval to;
+ struct sockaddr_in addr;
+ enum clnt_stat rpc_stat;
+ CLIENT *client;
+ u_long prognum, vers, minvers, maxvers;
+ int sock = RPC_ANYSOCK;
+ struct rpc_err rpcerr;
+ int failure;
+
+ if (argc < 2 || argc > 3) {
+ usage();
+ exit(1);
+ }
+ prognum = getprognum(argv[1]);
+ get_inet_address(&addr, argv[0]);
+ /* Open the socket here so it will survive calls to clnt_destroy */
+ sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ perror("rpcinfo: socket");
+ exit(1);
+ }
+ failure = 0;
+ if (argc == 2) {
+ /*
+ * A call to version 0 should fail with a program/version
+ * mismatch, and give us the range of versions supported.
+ */
+ addr.sin_port = htons(portnum);
+ to.tv_sec = 5;
+ to.tv_usec = 0;
+ if ((client = clntudp_create(&addr, prognum, (u_long)0,
+ to, &sock)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu is not available\n",
+ prognum);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
+ xdr_void, (char *)NULL, to);
+ if (rpc_stat == RPC_PROGVERSMISMATCH) {
+ clnt_geterr(client, &rpcerr);
+ minvers = rpcerr.re_vers.low;
+ maxvers = rpcerr.re_vers.high;
+ } else if (rpc_stat == RPC_SUCCESS) {
+ /*
+ * Oh dear, it DOES support version 0.
+ * Let's try version MAX_VERS.
+ */
+ addr.sin_port = htons(portnum);
+ to.tv_sec = 5;
+ to.tv_usec = 0;
+ if ((client = clntudp_create(&addr, prognum, MAX_VERS,
+ to, &sock)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, MAX_VERS);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, NULLPROC, xdr_void,
+ (char *)NULL, xdr_void, (char *)NULL, to);
+ if (rpc_stat == RPC_PROGVERSMISMATCH) {
+ clnt_geterr(client, &rpcerr);
+ minvers = rpcerr.re_vers.low;
+ maxvers = rpcerr.re_vers.high;
+ } else if (rpc_stat == RPC_SUCCESS) {
+ /*
+ * It also supports version MAX_VERS.
+ * Looks like we have a wise guy.
+ * OK, we give them information on all
+ * 4 billion versions they support...
+ */
+ minvers = 0;
+ maxvers = MAX_VERS;
+ } else {
+ (void) pstatus(client, prognum, MAX_VERS);
+ exit(1);
+ }
+ } else {
+ (void) pstatus(client, prognum, (u_long)0);
+ exit(1);
+ }
+ clnt_destroy(client);
+ for (vers = minvers; vers <= maxvers; vers++) {
+ addr.sin_port = htons(portnum);
+ to.tv_sec = 5;
+ to.tv_usec = 0;
+ if ((client = clntudp_create(&addr, prognum, vers,
+ to, &sock)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, vers);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, NULLPROC, xdr_void,
+ (char *)NULL, xdr_void, (char *)NULL, to);
+ if (pstatus(client, prognum, vers) < 0)
+ failure = 1;
+ clnt_destroy(client);
+ }
+ }
+ else {
+ vers = getvers(argv[2]);
+ addr.sin_port = htons(portnum);
+ to.tv_sec = 5;
+ to.tv_usec = 0;
+ if ((client = clntudp_create(&addr, prognum, vers,
+ to, &sock)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, vers);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
+ xdr_void, (char *)NULL, to);
+ if (pstatus(client, prognum, vers) < 0)
+ failure = 1;
+ }
+ (void) close(sock); /* Close it up again */
+ if (failure)
+ exit(1);
+}
+
+static void
+tcpping(portnum, argc, argv)
+ u_short portnum;
+ int argc;
+ char **argv;
+{
+ struct timeval to;
+ struct sockaddr_in addr;
+ enum clnt_stat rpc_stat;
+ CLIENT *client;
+ u_long prognum, vers, minvers, maxvers;
+ int sock = RPC_ANYSOCK;
+ struct rpc_err rpcerr;
+ int failure;
+
+ if (argc < 2 || argc > 3) {
+ usage();
+ exit(1);
+ }
+ prognum = getprognum(argv[1]);
+ get_inet_address(&addr, argv[0]);
+ failure = 0;
+ if (argc == 2) {
+ /*
+ * A call to version 0 should fail with a program/version
+ * mismatch, and give us the range of versions supported.
+ */
+ addr.sin_port = htons(portnum);
+ if ((client = clnttcp_create(&addr, prognum, MIN_VERS,
+ &sock, 0, 0)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu is not available\n",
+ prognum);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, NULLPROC, xdr_void, (char *)NULL,
+ xdr_void, (char *)NULL, to);
+ if (rpc_stat == RPC_PROGVERSMISMATCH) {
+ clnt_geterr(client, &rpcerr);
+ minvers = rpcerr.re_vers.low;
+ maxvers = rpcerr.re_vers.high;
+ } else if (rpc_stat == RPC_SUCCESS) {
+ /*
+ * Oh dear, it DOES support version 0.
+ * Let's try version MAX_VERS.
+ */
+ addr.sin_port = htons(portnum);
+ if ((client = clnttcp_create(&addr, prognum, MAX_VERS,
+ &sock, 0, 0)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, MAX_VERS);
+ exit(1);
+ }
+ to.tv_sec = 10;
+ to.tv_usec = 0;
+ rpc_stat = clnt_call(client, NULLPROC, xdr_void,
+ (char *)NULL, xdr_void, (char *)NULL, to);
+ if (rpc_stat == RPC_PROGVERSMISMATCH) {
+ clnt_geterr(client, &rpcerr);
+ minvers = rpcerr.re_vers.low;
+ maxvers = rpcerr.re_vers.high;
+ } else if (rpc_stat == RPC_SUCCESS) {
+ /*
+ * It also supports version MAX_VERS.
+ * Looks like we have a wise guy.
+ * OK, we give them information on all
+ * 4 billion versions they support...
+ */
+ minvers = 0;
+ maxvers = MAX_VERS;
+ } else {
+ (void) pstatus(client, prognum, MAX_VERS);
+ exit(1);
+ }
+ } else {
+ (void) pstatus(client, prognum, MIN_VERS);
+ exit(1);
+ }
+ clnt_destroy(client);
+ (void) close(sock);
+ sock = RPC_ANYSOCK; /* Re-initialize it for later */
+ for (vers = minvers; vers <= maxvers; vers++) {
+ addr.sin_port = htons(portnum);
+ if ((client = clnttcp_create(&addr, prognum, vers,
+ &sock, 0, 0)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, vers);
+ exit(1);
+ }
+ to.tv_usec = 0;
+ to.tv_sec = 10;
+ rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
+ xdr_void, (char *)NULL, to);
+ if (pstatus(client, prognum, vers) < 0)
+ failure = 1;
+ clnt_destroy(client);
+ (void) close(sock);
+ sock = RPC_ANYSOCK;
+ }
+ }
+ else {
+ vers = getvers(argv[2]);
+ addr.sin_port = htons(portnum);
+ if ((client = clnttcp_create(&addr, prognum, vers, &sock,
+ 0, 0)) == NULL) {
+ clnt_pcreateerror("rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, vers);
+ exit(1);
+ }
+ to.tv_usec = 0;
+ to.tv_sec = 10;
+ rpc_stat = clnt_call(client, 0, xdr_void, (char *)NULL,
+ xdr_void, (char *)NULL, to);
+ if (pstatus(client, prognum, vers) < 0)
+ failure = 1;
+ }
+ if (failure)
+ exit(1);
+}
+
+/*
+ * This routine should take a pointer to an "rpc_err" structure, rather than
+ * a pointer to a CLIENT structure, but "clnt_perror" takes a pointer to
+ * a CLIENT structure rather than a pointer to an "rpc_err" structure.
+ * As such, we have to keep the CLIENT structure around in order to print
+ * a good error message.
+ */
+static int
+pstatus(client, prognum, vers)
+ register CLIENT *client;
+ u_long prognum;
+ u_long vers;
+{
+ struct rpc_err rpcerr;
+
+ clnt_geterr(client, &rpcerr);
+ if (rpcerr.re_status != RPC_SUCCESS) {
+ clnt_perror(client, "rpcinfo");
+ printf("program %lu version %lu is not available\n",
+ prognum, vers);
+ return (-1);
+ } else {
+ printf("program %lu version %lu ready and waiting\n",
+ prognum, vers);
+ return (0);
+ }
+}
+
+static void
+pmapdump(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct sockaddr_in server_addr;
+ register struct hostent *hp;
+ struct pmaplist *head = NULL;
+ int socket = RPC_ANYSOCK;
+ struct timeval minutetimeout;
+ register CLIENT *client;
+ struct rpcent *rpc;
+
+ if (argc > 1) {
+ usage();
+ exit(1);
+ }
+ if (argc == 1)
+ get_inet_address(&server_addr, argv[0]);
+ else {
+ bzero((char *)&server_addr, sizeof server_addr);
+ server_addr.sin_family = AF_INET;
+ if ((hp = gethostbyname("localhost")) != NULL)
+ bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr,
+ hp->h_length);
+ else
+ server_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
+ }
+ minutetimeout.tv_sec = 60;
+ minutetimeout.tv_usec = 0;
+ server_addr.sin_port = htons(PMAPPORT);
+ if ((client = clnttcp_create(&server_addr, PMAPPROG,
+ PMAPVERS, &socket, 50, 500)) == NULL) {
+ clnt_pcreateerror("rpcinfo: can't contact portmapper");
+ exit(1);
+ }
+ if (clnt_call(client, PMAPPROC_DUMP, xdr_void, NULL,
+ xdr_pmaplist, &head, minutetimeout) != RPC_SUCCESS) {
+ fprintf(stderr, "rpcinfo: can't contact portmapper: ");
+ clnt_perror(client, "rpcinfo");
+ exit(1);
+ }
+ if (head == NULL) {
+ printf("No remote programs registered.\n");
+ } else {
+ printf(" program vers proto port\n");
+ for (; head != NULL; head = head->pml_next) {
+ printf("%10ld%5ld",
+ head->pml_map.pm_prog,
+ head->pml_map.pm_vers);
+ if (head->pml_map.pm_prot == IPPROTO_UDP)
+ printf("%6s", "udp");
+ else if (head->pml_map.pm_prot == IPPROTO_TCP)
+ printf("%6s", "tcp");
+ else
+ printf("%6ld", head->pml_map.pm_prot);
+ printf("%7ld", head->pml_map.pm_port);
+ rpc = getrpcbynumber(head->pml_map.pm_prog);
+ if (rpc)
+ printf(" %s\n", rpc->r_name);
+ else
+ printf("\n");
+ }
+ }
+}
+
+/*
+ * reply_proc collects replies from the broadcast.
+ * to get a unique list of responses the output of rpcinfo should
+ * be piped through sort(1) and then uniq(1).
+ */
+
+/*ARGSUSED*/
+static bool_t
+reply_proc(res, who)
+ void *res; /* Nothing comes back */
+ struct sockaddr_in *who; /* Who sent us the reply */
+{
+ register struct hostent *hp;
+
+ hp = gethostbyaddr((char *) &who->sin_addr, sizeof who->sin_addr,
+ AF_INET);
+ printf("%s %s\n", inet_ntoa(who->sin_addr),
+ (hp == NULL) ? "(unknown)" : hp->h_name);
+ return(FALSE);
+}
+
+static void
+brdcst(argc, argv)
+ int argc;
+ char **argv;
+{
+ enum clnt_stat rpc_stat;
+ u_long prognum, vers;
+
+ if (argc != 2) {
+ usage();
+ exit(1);
+ }
+ prognum = getprognum(argv[0]);
+ vers = getvers(argv[1]);
+ rpc_stat = clnt_broadcast(prognum, vers, NULLPROC, xdr_void,
+ (char *)NULL, xdr_void, (char *)NULL, reply_proc);
+ if ((rpc_stat != RPC_SUCCESS) && (rpc_stat != RPC_TIMEDOUT)) {
+ fprintf(stderr, "rpcinfo: broadcast failed: %s\n",
+ clnt_sperrno(rpc_stat));
+ exit(1);
+ }
+ exit(0);
+}
+
+static void
+deletereg(argc, argv)
+ int argc;
+ char **argv;
+{ u_long prog_num, version_num ;
+
+ if (argc != 2) {
+ usage() ;
+ exit(1) ;
+ }
+ if (getuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not root\n") ;
+ exit(1) ;
+ }
+ prog_num = getprognum(argv[0]);
+ version_num = getvers(argv[1]);
+ if ((pmap_unset(prog_num, version_num)) == 0) {
+ fprintf(stderr, "rpcinfo: Could not delete registration for prog %s version %s\n",
+ argv[0], argv[1]) ;
+ exit(1) ;
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: rpcinfo [ -n portnum ] -u host prognum [ versnum ]\n");
+ fprintf(stderr, " rpcinfo [ -n portnum ] -t host prognum [ versnum ]\n");
+ fprintf(stderr, " rpcinfo -p [ host ]\n");
+ fprintf(stderr, " rpcinfo -b prognum versnum\n");
+ fprintf(stderr, " rpcinfo -d prognum versnum\n") ;
+}
+
+static u_long
+getprognum(arg)
+ char *arg;
+{
+ register struct rpcent *rpc;
+ register u_long prognum;
+
+ if (isalpha(*arg)) {
+ rpc = getrpcbyname(arg);
+ if (rpc == NULL) {
+ fprintf(stderr, "rpcinfo: %s is unknown service\n",
+ arg);
+ exit(1);
+ }
+ prognum = rpc->r_number;
+ } else {
+ prognum = (u_long) atoi(arg);
+ }
+
+ return (prognum);
+}
+
+static u_long
+getvers(arg)
+ char *arg;
+{
+ register u_long vers;
+
+ vers = (int) atoi(arg);
+ return (vers);
+}
+
+static void
+get_inet_address(addr, host)
+ struct sockaddr_in *addr;
+ char *host;
+{
+ register struct hostent *hp;
+
+ bzero((char *)addr, sizeof *addr);
+ addr->sin_addr.s_addr = (u_long) inet_addr(host);
+ if (addr->sin_addr.s_addr == -1 || addr->sin_addr.s_addr == 0) {
+ if ((hp = gethostbyname(host)) == NULL) {
+ fprintf(stderr, "rpcinfo: %s is unknown host\n", host);
+ exit(1);
+ }
+ bcopy(hp->h_addr, (char *)&addr->sin_addr, hp->h_length);
+ }
+ addr->sin_family = AF_INET;
+}
diff --git a/usr.bin/rs/Makefile b/usr.bin/rs/Makefile
new file mode 100644
index 0000000..21da34d
--- /dev/null
+++ b/usr.bin/rs/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rs
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rs/rs.1 b/usr.bin/rs/rs.1
new file mode 100644
index 0000000..07de48c
--- /dev/null
+++ b/usr.bin/rs/rs.1
@@ -0,0 +1,197 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rs.1 8.2 (Berkeley) 12/30/93
+.\"
+.TH RS 1 "December 30, 1993"
+.UC 4
+.SH NAME
+rs \- reshape a data array
+.SH SYNOPSIS
+\fBrs [ \-[csCS][\fRx\fB][kKgGw][\fRN\fB]tTeEnyjhHm ] [ \fRrows\fB [ \fRcols\fB ] ]\fR
+.SH DESCRIPTION
+.I Rs
+reads the standard input, interpreting each line as a row
+of blank-separated entries in an array,
+transforms the array according to the options,
+and writes it on the standard output.
+With no arguments it transforms stream input into a columnar
+format convenient for terminal viewing.
+.PP
+The shape of the input array is deduced from the number of lines
+and the number of columns on the first line.
+If that shape is inconvenient, a more useful one might be
+obtained by skipping some of the input with the \fB\-k\fP option.
+Other options control interpretation of the input columns.
+.PP
+The shape of the output array is influenced by the
+.I rows
+and
+.I cols
+specifications, which should be positive integers.
+If only one of them is a positive integer,
+.I rs
+computes a value for the other which will accommodate
+all of the data.
+When necessary, missing data are supplied in a manner
+specified by the options and surplus data are deleted.
+There are options to control presentation of the output columns,
+including transposition of the rows and columns.
+.PP
+The options are described below.
+.IP \fB\-c\fRx
+Input columns are delimited by the single character \fIx\fP.
+A missing \fIx\fP is taken to be `^I'.
+.IP \fB\-s\fRx
+Like \fB\-c\fR, but maximal strings of \fIx\fP are delimiters.
+.IP \fB\-C\fRx
+Output columns are delimited by the single character \fIx\fP.
+A missing \fIx\fP is taken to be `^I'.
+.IP \fB\-S\fRx
+Like \fB\-C\fR, but padded strings of \fIx\fP are delimiters.
+.IP \fB\-t\fR
+Fill in the rows of the output array using the columns of the
+input array, that is, transpose the input while honoring any
+.I rows
+and
+.I cols
+specifications.
+.IP \fB\-T\fR
+Print the pure transpose of the input, ignoring any
+.I rows
+or
+.I cols
+specification.
+.IP \fB\-k\fRN
+Ignore the first \fIN\fR lines of input.
+.IP \fB\-K\fRN
+Like \fB\-k\fR, but print the ignored lines.
+.IP \fB\-g\fRN
+The gutter width (inter-column space), normally 2, is taken to be \fIN\fR.
+.IP \fB\-G\fRN
+The gutter width has \fIN\fR percent of the maximum
+column width added to it.
+.IP \fB\-e\fR
+Consider each line of input as an array entry.
+.IP \fB\-n\fR
+On lines having fewer entries than the first line,
+use null entries to pad out the line.
+Normally, missing entries are taken from the next line of input.
+.IP \fB\-y\fR
+If there are too few entries to make up the output dimensions,
+pad the output by recycling the input from the beginning.
+Normally, the output is padded with blanks.
+.IP \fB\-h\fR
+Print the shape of the input array and do nothing else.
+The shape is just the number of lines and the number of
+entries on the first line.
+.IP \fB\-H\fR
+Like \fB\-h\fR, but also print the length of each line.
+.IP \fB\-j\fR
+Right adjust entries within columns.
+.IP \fB\-w\fRN
+The width of the display, normally 80, is taken to be the positive
+integer \fIN\fP.
+.IP \fB\-m\fR
+Do not trim excess delimiters from the ends of the output array.
+.PP
+With no arguments,
+.I rs
+transposes its input, and assumes one array entry per input line
+unless the first non-ignored line is longer than the display width.
+Option letters which take numerical arguments interpret a missing
+number as zero unless otherwise indicated.
+.SH EXAMPLES
+.de IC
+.IP
+.ss 36
+.ft B
+..
+.de NC
+.br
+.ss 12
+.PP
+..
+.I Rs
+can be used as a filter to convert the stream output
+of certain programs (e.g.,
+.IR spell ,
+.IR du ,
+.IR file ,
+.IR look ,
+.IR nm ,
+.IR who ,
+and
+.IR wc (1))
+into a convenient ``window'' format, as in
+.IC
+who | rs
+.NC
+This function has been incorporated into the
+.IR ls (1)
+program, though for most programs with similar output
+.I rs
+suffices.
+.PP
+To convert stream input into vector output and back again, use
+.IC
+rs 1 0 | rs 0 1
+.NC
+A 10 by 10 array of random numbers from 1 to 100 and
+its transpose can be generated with
+.IC
+jot \-r 100 | rs 10 10 | tee array | rs \-T > tarray
+.NC
+In the editor
+.IR vi (1),
+a file consisting of a multi-line vector with 9 elements per line
+can undergo insertions and deletions,
+and then be neatly reshaped into 9 columns with
+.IC
+:1,$!rs 0 9
+.NC
+Finally, to sort a database by the first line of each 4-line field, try
+.IC
+rs \-eC 0 4 | sort | rs \-c 0 1
+.NC
+.SH SEE ALSO
+jot(1), vi(1), sort(1), pr(1)
+.SH BUGS
+Handles only two dimensional arrays.
+
+The algorithm currently reads the whole file into memory,
+so files that do not fit in memory will not be reshaped.
+
+Fields cannot be defined yet on character positions.
+
+Re-ordering of columns is not yet possible.
+
+There are too many options.
diff --git a/usr.bin/rs/rs.c b/usr.bin/rs/rs.c
new file mode 100644
index 0000000..429dadc
--- /dev/null
+++ b/usr.bin/rs/rs.c
@@ -0,0 +1,546 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * rs - reshape a data array
+ * Author: John Kunze, Office of Comp. Affairs, UCB
+ * BEWARE: lots of unfinished edges
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+long flags;
+#define TRANSPOSE 000001
+#define MTRANSPOSE 000002
+#define ONEPERLINE 000004
+#define ONEISEPONLY 000010
+#define ONEOSEPONLY 000020
+#define NOTRIMENDCOL 000040
+#define SQUEEZE 000100
+#define SHAPEONLY 000200
+#define DETAILSHAPE 000400
+#define RIGHTADJUST 001000
+#define NULLPAD 002000
+#define RECYCLE 004000
+#define SKIPPRINT 010000
+#define ICOLBOUNDS 020000
+#define OCOLBOUNDS 040000
+#define ONEPERCHAR 0100000
+#define NOARGS 0200000
+
+short *colwidths;
+short *cord;
+short *icbd;
+short *ocbd;
+int nelem;
+char **elem;
+char **endelem;
+char *curline;
+int allocsize = BUFSIZ;
+int curlen;
+int irows, icols;
+int orows, ocols;
+int maxlen;
+int skip;
+int propgutter;
+char isep = ' ', osep = ' ';
+int owidth = 80, gutter = 2;
+
+void error __P((char *, char *));
+void getargs __P((int, char *[]));
+void getfile __P((void));
+int getline __P((void));
+char *getlist __P((short **, char *));
+char *getnum __P((int *, char *, int));
+char **getptrs __P((char **));
+void prepfile __P((void));
+void prints __P((char *, int));
+void putfile __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ getargs(argc, argv);
+ getfile();
+ if (flags & SHAPEONLY) {
+ printf("%d %d\n", irows, icols);
+ exit(0);
+ }
+ prepfile();
+ putfile();
+ exit(0);
+}
+
+void
+getfile()
+{
+ register char *p;
+ register char *endp;
+ register char **ep = 0;
+ int multisep = (flags & ONEISEPONLY ? 0 : 1);
+ int nullpad = flags & NULLPAD;
+ char **padto;
+
+ while (skip--) {
+ getline();
+ if (flags & SKIPPRINT)
+ puts(curline);
+ }
+ getline();
+ if (flags & NOARGS && curlen < owidth)
+ flags |= ONEPERLINE;
+ if (flags & ONEPERLINE)
+ icols = 1;
+ else /* count cols on first line */
+ for (p = curline, endp = curline + curlen; p < endp; p++) {
+ if (*p == isep && multisep)
+ continue;
+ icols++;
+ while (*p && *p != isep)
+ p++;
+ }
+ ep = getptrs(elem);
+ p = curline;
+ do {
+ if (flags & ONEPERLINE) {
+ *ep++ = curline;
+ if (maxlen < curlen)
+ maxlen = curlen;
+ irows++;
+ continue;
+ }
+ for (p = curline, endp = curline + curlen; p < endp; p++) {
+ if (*p == isep && multisep)
+ continue; /* eat up column separators */
+ if (*p == isep) /* must be an empty column */
+ *ep = "";
+ else /* store column entry */
+ *ep = p;
+ while (p < endp && *p != isep)
+ p++; /* find end of entry */
+ *p = '\0'; /* mark end of entry */
+ if (maxlen < p - *ep) /* update maxlen */
+ maxlen = p - *ep;
+ ep++; /* prepare for next entry */
+ }
+ irows++; /* update row count */
+ if (nullpad) { /* pad missing entries */
+ padto = elem + irows * icols;
+ while (ep < padto)
+ *ep++ = "";
+ }
+ if (ep > endelem) /* if low on pointers */
+ ep = getptrs(ep); /* get some more */
+ } while (getline() != EOF);
+ *ep = 0; /* mark end of pointers */
+ nelem = ep - elem;
+}
+
+void
+putfile()
+{
+ register char **ep;
+ register int i, j;
+
+ ep = elem;
+ if (flags & TRANSPOSE)
+ for (i = 0; i < orows; i++) {
+ for (j = i; j < nelem; j += orows)
+ prints(ep[j], (j - i) / orows);
+ putchar('\n');
+ }
+ else
+ for (i = 0; i < orows; i++) {
+ for (j = 0; j < ocols; j++)
+ prints(*ep++, j);
+ putchar('\n');
+ }
+}
+
+void
+prints(s, col)
+ char *s;
+ int col;
+{
+ register int n;
+ register char *p = s;
+
+ while (*p)
+ p++;
+ n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
+ if (flags & RIGHTADJUST)
+ while (n-- > 0)
+ putchar(osep);
+ for (p = s; *p; p++)
+ putchar(*p);
+ while (n-- > 0)
+ putchar(osep);
+}
+
+void
+error(msg, s)
+ char *msg, *s;
+{
+ fprintf(stderr, "rs: ");
+ fprintf(stderr, msg, s);
+ fprintf(stderr,
+"\nUsage: rs [ -[csCS][x][kKgGw][N]tTeEnyjhHm ] [ rows [ cols ] ]\n");
+ exit(1);
+}
+
+void
+prepfile()
+{
+ register char **ep;
+ register int i;
+ register int j;
+ char **lp;
+ int colw;
+ int max = 0;
+ int n;
+
+ if (!nelem)
+ exit(0);
+ gutter += maxlen * propgutter / 100.0;
+ colw = maxlen + gutter;
+ if (flags & MTRANSPOSE) {
+ orows = icols;
+ ocols = irows;
+ }
+ else if (orows == 0 && ocols == 0) { /* decide rows and cols */
+ ocols = owidth / colw;
+ if (ocols == 0)
+ fprintf(stderr, "Display width %d is less than column width %d\n", owidth, colw);
+ if (ocols > nelem)
+ ocols = nelem;
+ orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+ }
+ else if (orows == 0) /* decide on rows */
+ orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+ else if (ocols == 0) /* decide on cols */
+ ocols = nelem / orows + (nelem % orows ? 1 : 0);
+ lp = elem + orows * ocols;
+ while (lp > endelem) {
+ getptrs(elem + nelem);
+ lp = elem + orows * ocols;
+ }
+ if (flags & RECYCLE) {
+ for (ep = elem + nelem; ep < lp; ep++)
+ *ep = *(ep - nelem);
+ nelem = lp - elem;
+ }
+ if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
+ error("malloc: No gutter space", "");
+ if (flags & SQUEEZE) {
+ if (flags & TRANSPOSE)
+ for (ep = elem, i = 0; i < ocols; i++) {
+ for (j = 0; j < orows; j++)
+ if ((n = strlen(*ep++)) > max)
+ max = n;
+ colwidths[i] = max + gutter;
+ }
+ else
+ for (i = 0; i < ocols; i++) {
+ for (j = i; j < nelem; j += ocols)
+ if ((n = strlen(ep[j])) > max)
+ max = n;
+ colwidths[i] = max + gutter;
+ }
+ }
+ /* for (i = 0; i < orows; i++) {
+ for (j = i; j < nelem; j += orows)
+ prints(ep[j], (j - i) / orows);
+ putchar('\n');
+ }
+ else
+ for (i = 0; i < orows; i++) {
+ for (j = 0; j < ocols; j++)
+ prints(*ep++, j);
+ putchar('\n');
+ }*/
+ else
+ for (i = 0; i < ocols; i++)
+ colwidths[i] = colw;
+ if (!(flags & NOTRIMENDCOL)) {
+ if (flags & RIGHTADJUST)
+ colwidths[0] -= gutter;
+ else
+ colwidths[ocols - 1] = 0;
+ }
+ n = orows * ocols;
+ if (n > nelem && (flags & RECYCLE))
+ nelem = n;
+ /*for (i = 0; i < ocols; i++)
+ fprintf(stderr, "%d ",colwidths[i]);
+ fprintf(stderr, "is colwidths, nelem %d\n", nelem);*/
+}
+
+#define BSIZE 2048
+char ibuf[BSIZE]; /* two screenfuls should do */
+
+int
+getline() /* get line; maintain curline, curlen; manage storage */
+{
+ static int putlength;
+ static char *endblock = ibuf + BSIZE;
+ register char *p;
+ register int c, i;
+
+ if (!irows) {
+ curline = ibuf;
+ putlength = flags & DETAILSHAPE;
+ }
+ else if (skip <= 0) { /* don't waste storage */
+ curline += curlen + 1;
+ if (putlength) /* print length, recycle storage */
+ printf(" %d line %d\n", curlen, irows);
+ }
+ if (!putlength && endblock - curline < BUFSIZ) { /* need storage */
+ /*ww = endblock-curline; tt += ww;*/
+ /*printf("#wasted %d total %d\n",ww,tt);*/
+ if (!(curline = (char *) malloc(BSIZE)))
+ error("File too large", "");
+ endblock = curline + BSIZE;
+ /*printf("#endb %d curline %d\n",endblock,curline);*/
+ }
+ for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
+ if ((c = getchar()) == EOF || c == '\n')
+ break;
+ *p = '\0';
+ curlen = i - 1;
+ return(c);
+}
+
+char **
+getptrs(sp)
+ char **sp;
+{
+ register char **p, **ep;
+
+ for (;;) {
+ allocsize += allocsize;
+ if (!(p = (char **) malloc(allocsize * sizeof(char *)))) {
+ perror("rs");
+ exit(1);
+ }
+ if ((endelem = p + allocsize - icols) <= p) {
+ free(p);
+ continue;
+ }
+ if (elem != 0)
+ free(elem);
+ ep = elem;
+ elem = p;
+ while (ep < sp)
+ *p++ = *ep++;
+ return(p);
+ }
+}
+
+void
+getargs(ac, av)
+ int ac;
+ char *av[];
+{
+ register char *p;
+
+ if (ac == 1) {
+ flags |= NOARGS | TRANSPOSE;
+ }
+ while (--ac && **++av == '-')
+ for (p = *av+1; *p; p++)
+ switch (*p) {
+ case 'T':
+ flags |= MTRANSPOSE;
+ case 't':
+ flags |= TRANSPOSE;
+ break;
+ case 'c': /* input col. separator */
+ flags |= ONEISEPONLY;
+ case 's': /* one or more allowed */
+ if (p[1])
+ isep = *++p;
+ else
+ isep = '\t'; /* default is ^I */
+ break;
+ case 'C':
+ flags |= ONEOSEPONLY;
+ case 'S':
+ if (p[1])
+ osep = *++p;
+ else
+ osep = '\t'; /* default is ^I */
+ break;
+ case 'w': /* window width, default 80 */
+ p = getnum(&owidth, p, 0);
+ if (owidth <= 0)
+ error("Width must be a positive integer", "");
+ break;
+ case 'K': /* skip N lines */
+ flags |= SKIPPRINT;
+ case 'k': /* skip, do not print */
+ p = getnum(&skip, p, 0);
+ if (!skip)
+ skip = 1;
+ break;
+ case 'm':
+ flags |= NOTRIMENDCOL;
+ break;
+ case 'g': /* gutter space */
+ p = getnum(&gutter, p, 0);
+ break;
+ case 'G':
+ p = getnum(&propgutter, p, 0);
+ break;
+ case 'e': /* each line is an entry */
+ flags |= ONEPERLINE;
+ break;
+ case 'E':
+ flags |= ONEPERCHAR;
+ break;
+ case 'j': /* right adjust */
+ flags |= RIGHTADJUST;
+ break;
+ case 'n': /* null padding for missing values */
+ flags |= NULLPAD;
+ break;
+ case 'y':
+ flags |= RECYCLE;
+ break;
+ case 'H': /* print shape only */
+ flags |= DETAILSHAPE;
+ case 'h':
+ flags |= SHAPEONLY;
+ break;
+ case 'z': /* squeeze col width */
+ flags |= SQUEEZE;
+ break;
+ /*case 'p':
+ ipagespace = atoi(++p); (default is 1)
+ break;*/
+ case 'o': /* col order */
+ p = getlist(&cord, p);
+ break;
+ case 'b':
+ flags |= ICOLBOUNDS;
+ p = getlist(&icbd, p);
+ break;
+ case 'B':
+ flags |= OCOLBOUNDS;
+ p = getlist(&ocbd, p);
+ break;
+ default:
+ error("Bad flag: %.1s", p);
+ }
+ /*if (!osep)
+ osep = isep;*/
+ switch (ac) {
+ /*case 3:
+ opages = atoi(av[2]);*/
+ case 2:
+ ocols = atoi(av[1]);
+ case 1:
+ orows = atoi(av[0]);
+ case 0:
+ break;
+ default:
+ error("Too many arguments. What do you mean by `%s'?", av[3]);
+ }
+}
+
+char *
+getlist(list, p)
+ short **list;
+ char *p;
+{
+ register int count = 1;
+ register char *t;
+
+ for (t = p + 1; *t; t++) {
+ if (!isdigit(*t))
+ error("Option %.1s requires a list of unsigned numbers separated by commas", t);
+ count++;
+ while (*t && isdigit(*t))
+ t++;
+ if (*t != ',')
+ break;
+ }
+ if (!(*list = (short *) malloc(count * sizeof(short))))
+ error("No list space", "");
+ count = 0;
+ for (t = p + 1; *t; t++) {
+ (*list)[count++] = atoi(t);
+ printf("++ %d ", (*list)[count-1]);
+ fflush(stdout);
+ while (*t && isdigit(*t))
+ t++;
+ if (*t != ',')
+ break;
+ }
+ (*list)[count] = 0;
+ return(t - 1);
+}
+
+char *
+getnum(num, p, strict) /* num = number p points to; if (strict) complain */
+ int *num, strict; /* returns pointer to end of num */
+ char *p;
+{
+ register char *t = p;
+
+ if (!isdigit(*++t)) {
+ if (strict || *t == '-' || *t == '+')
+ error("Option %.1s requires an unsigned integer", p);
+ *num = 0;
+ return(p);
+ }
+ *num = atoi(t);
+ while (*++t)
+ if (!isdigit(*t))
+ break;
+ return(--t);
+}
diff --git a/usr.bin/rsh/Makefile b/usr.bin/rsh/Makefile
new file mode 100644
index 0000000..ca63790
--- /dev/null
+++ b/usr.bin/rsh/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= rsh
+SRCS= rsh.c
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS -DCRYPT
+SRCS+= krcmd.c kcmd.c
+DPADD= ${LIBKRB} ${LIBDES}
+LDADD= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+.PATH: ${.CURDIR}/../rlogin
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rsh/pathnames.h b/usr.bin/rsh/pathnames.h
new file mode 100644
index 0000000..16753c8
--- /dev/null
+++ b/usr.bin/rsh/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_RLOGIN "/usr/bin/rlogin"
diff --git a/usr.bin/rsh/rsh.1 b/usr.bin/rsh/rsh.1
new file mode 100644
index 0000000..908e7eb
--- /dev/null
+++ b/usr.bin/rsh/rsh.1
@@ -0,0 +1,181 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rsh.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RSH 1
+.Os BSD 4.2
+.Sh NAME
+.Nm rsh
+.Nd remote shell
+.Sh SYNOPSIS
+.Nm rsh
+.Op Fl Kdnx
+.Op Fl k Ar realm
+.Op Fl l Ar username
+.Ar host
+.Op command
+.Sh DESCRIPTION
+.Nm Rsh
+executes
+.Ar command
+on
+.Ar host .
+.Pp
+.Nm Rsh
+copies its standard input to the remote command, the standard
+output of the remote command to its standard output, and the
+standard error of the remote command to its standard error.
+Interrupt, quit and terminate signals are propagated to the remote
+command;
+.Nm rsh
+normally terminates when the remote command does.
+The options are as follows:
+.Bl -tag -width flag
+.It Fl K
+The
+.Fl K
+option turns off all Kerberos authentication.
+.It Fl d
+The
+.Fl d
+option turns on socket debugging (using
+.Xr setsockopt 2 )
+on the
+.Tn TCP
+sockets used for communication with the remote host.
+.It Fl k
+The
+.Fl k
+option causes
+.Nm rsh
+to obtain tickets for the remote host in
+.Ar realm
+instead of the remote host's realm as determined by
+.Xr krb_realmofhost 3 .
+.It Fl l
+By default, the remote username is the same as the local username.
+The
+.Fl l
+option allows the remote name to be specified.
+Kerberos authentication is used, and authorization is determined
+as in
+.Xr rlogin 1 .
+.It Fl n
+The
+.Fl n
+option redirects input from the special device
+.Pa /dev/null
+(see the
+.Sx BUGS
+section of this manual page).
+.It Fl x
+The
+.Fl x
+option turns on
+.Tn DES
+encryption for all data exchange.
+This may introduce a significant delay in response time.
+.El
+.Pp
+If no
+.Ar command
+is specified, you will be logged in on the remote host using
+.Xr rlogin 1 .
+.Pp
+Shell metacharacters which are not quoted are interpreted on local machine,
+while quoted metacharacters are interpreted on the remote machine.
+For example, the command
+.Pp
+.Dl rsh otherhost cat remotefile >> localfile
+.Pp
+appends the remote file
+.Ar remotefile
+to the local file
+.Ar localfile ,
+while
+.Pp
+.Dl rsh otherhost cat remotefile \&">>\&" other_remotefile
+.Pp
+appends
+.Ar remotefile
+to
+.Ar other_remotefile .
+.\" .Pp
+.\" Many sites specify a large number of host names as commands in the
+.\" directory /usr/hosts.
+.\" If this directory is included in your search path, you can use the
+.\" shorthand ``host command'' for the longer form ``rsh host command''.
+.Sh FILES
+.Bl -tag -width /etc/hosts -compact
+.It Pa /etc/hosts
+.El
+.Sh SEE ALSO
+.Xr rlogin 1 ,
+.Xr kerberos 3 ,
+.Xr krb_sendauth 3 ,
+.Xr krb_realmofhost 3
+.Sh HISTORY
+The
+.Nm rsh
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+If you are using
+.Xr csh 1
+and put a
+.Nm rsh
+in the background without redirecting its input away from the terminal,
+it will block even if no reads are posted by the remote command.
+If no input is desired you should redirect the input of
+.Nm rsh
+to
+.Pa /dev/null
+using the
+.Fl n
+option.
+.Pp
+You cannot run an interactive command
+(like
+.Xr rogue 6
+or
+.Xr vi 1 )
+using
+.Nm rsh ;
+use
+.Xr rlogin 1
+instead.
+.Pp
+Stop signals stop the local
+.Nm rsh
+process only; this is arguably wrong, but currently hard to fix for reasons
+too complicated to explain here.
diff --git a/usr.bin/rsh/rsh.c b/usr.bin/rsh/rsh.c
new file mode 100644
index 0000000..6b6c396
--- /dev/null
+++ b/usr.bin/rsh/rsh.c
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 1983, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "From: @(#)rsh.c 8.3 (Berkeley) 4/6/94";
+static char rcsid[] =
+ "$Id: rsh.c,v 1.4 1995/05/30 06:33:24 rgrimes Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <varargs.h>
+
+#include "pathnames.h"
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+
+CREDENTIALS cred;
+Key_schedule schedule;
+int use_kerberos = 1, doencrypt;
+char dst_realm_buf[REALM_SZ], *dest_realm;
+extern char *krb_realmofhost();
+#endif
+
+/*
+ * rsh - remote shell
+ */
+int rfd2;
+
+char *copyargs __P((char **));
+void sendsig __P((int));
+void talk __P((int, long, pid_t, int));
+void usage __P((void));
+void warning __P(());
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct passwd *pw;
+ struct servent *sp;
+ long omask;
+ int argoff, asrsh, ch, dflag, nflag, one, rem;
+ pid_t pid;
+ uid_t uid;
+ char *args, *host, *p, *user;
+
+ argoff = asrsh = dflag = nflag = 0;
+ one = 1;
+ host = user = NULL;
+
+ /* if called as something other than "rsh", use it as the host name */
+ if (p = strrchr(argv[0], '/'))
+ ++p;
+ else
+ p = argv[0];
+ if (strcmp(p, "rsh"))
+ host = p;
+ else
+ asrsh = 1;
+
+ /* handle "rsh host flags" */
+ if (!host && argc > 2 && argv[1][0] != '-') {
+ host = argv[1];
+ argoff = 1;
+ }
+
+#ifdef KERBEROS
+#ifdef CRYPT
+#define OPTIONS "8KLde:k:l:nwx"
+#else
+#define OPTIONS "8KLde:k:l:nw"
+#endif
+#else
+#define OPTIONS "8KLde:l:nw"
+#endif
+ while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
+ switch(ch) {
+ case 'K':
+#ifdef KERBEROS
+ use_kerberos = 0;
+#endif
+ break;
+ case 'L': /* -8Lew are ignored to allow rlogin aliases */
+ case 'e':
+ case 'w':
+ case '8':
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'l':
+ user = optarg;
+ break;
+#ifdef KERBEROS
+ case 'k':
+ dest_realm = dst_realm_buf;
+ strncpy(dest_realm, optarg, REALM_SZ);
+ break;
+#endif
+ case 'n':
+ nflag = 1;
+ break;
+#ifdef KERBEROS
+#ifdef CRYPT
+ case 'x':
+ doencrypt = 1;
+ break;
+#endif
+#endif
+ case '?':
+ default:
+ usage();
+ }
+ optind += argoff;
+
+ /* if haven't gotten a host yet, do so */
+ if (!host && !(host = argv[optind++]))
+ usage();
+
+ /* if no further arguments, must have been called as rlogin. */
+ if (!argv[optind]) {
+ if (asrsh)
+ *argv = "rlogin";
+ execv(_PATH_RLOGIN, argv);
+ err(1, "can't exec %s", _PATH_RLOGIN);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!(pw = getpwuid(uid = getuid())))
+ errx(1, "unknown user id");
+ if (!user)
+ user = pw->pw_name;
+
+#ifdef KERBEROS
+#ifdef CRYPT
+ /* -x turns off -n */
+ if (doencrypt)
+ nflag = 0;
+#endif
+#endif
+
+ args = copyargs(argv);
+
+ sp = NULL;
+#ifdef KERBEROS
+ if (use_kerberos) {
+ sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp");
+ if (sp == NULL) {
+ use_kerberos = 0;
+ warning("can't get entry for %s/tcp service",
+ doencrypt ? "ekshell" : "kshell");
+ }
+ }
+#endif
+ if (sp == NULL)
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL)
+ errx(1, "shell/tcp: unknown service");
+
+#ifdef KERBEROS
+try_connect:
+ if (use_kerberos) {
+ struct hostent *hp;
+
+ /* fully qualify hostname (needed for krb_realmofhost) */
+ hp = gethostbyname(host);
+ if (hp != NULL && !(host = strdup(hp->h_name)))
+ err(1, NULL);
+
+ rem = KSUCCESS;
+ errno = 0;
+ if (dest_realm == NULL)
+ dest_realm = krb_realmofhost(host);
+
+#ifdef CRYPT
+ if (doencrypt) {
+ rem = krcmd_mutual(&host, sp->s_port, user, args,
+ &rfd2, dest_realm, &cred, schedule);
+ des_set_key(&cred.session, schedule);
+ } else
+#endif
+ rem = krcmd(&host, sp->s_port, user, args, &rfd2,
+ dest_realm);
+ if (rem < 0) {
+ use_kerberos = 0;
+ sp = getservbyname("shell", "tcp");
+ if (sp == NULL)
+ errx(1, "shell/tcp: unknown service");
+ if (errno == ECONNREFUSED)
+ warning("remote host doesn't support Kerberos");
+ if (errno == ENOENT)
+ warning("can't provide Kerberos auth data");
+ goto try_connect;
+ }
+ } else {
+ if (doencrypt)
+ errx(1, "the -x flag requires Kerberos authentication");
+ rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
+ }
+#else
+ rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2);
+#endif
+
+ if (rem < 0)
+ exit(1);
+
+ if (rfd2 < 0)
+ errx(1, "can't establish stderr");
+ if (dflag) {
+ if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one,
+ sizeof(one)) < 0)
+ warn("setsockopt");
+ if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one,
+ sizeof(one)) < 0)
+ warn("setsockopt");
+ }
+
+ (void)setuid(uid);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGINT, sendsig);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGQUIT, sendsig);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ (void)signal(SIGTERM, sendsig);
+
+ if (!nflag) {
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ }
+
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (!doencrypt)
+#endif
+#endif
+ {
+ (void)ioctl(rfd2, FIONBIO, &one);
+ (void)ioctl(rem, FIONBIO, &one);
+ }
+
+ talk(nflag, omask, pid, rem);
+
+ if (!nflag)
+ (void)kill(pid, SIGKILL);
+ exit(0);
+}
+
+void
+talk(nflag, omask, pid, rem)
+ int nflag;
+ long omask;
+ pid_t pid;
+ int rem;
+{
+ int cc, wc;
+ fd_set readfrom, ready, rembits;
+ char *bp, buf[BUFSIZ];
+
+ if (!nflag && pid == 0) {
+ (void)close(rfd2);
+
+reread: errno = 0;
+ if ((cc = read(0, buf, sizeof buf)) <= 0)
+ goto done;
+ bp = buf;
+
+rewrite:
+ FD_ZERO(&rembits);
+ FD_SET(rem, &rembits);
+ if (select(16, 0, &rembits, 0, 0) < 0) {
+ if (errno != EINTR)
+ err(1, "select");
+ goto rewrite;
+ }
+ if (!FD_ISSET(rem, &rembits))
+ goto rewrite;
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (doencrypt)
+ wc = des_write(rem, bp, cc);
+ else
+#endif
+#endif
+ wc = write(rem, bp, cc);
+ if (wc < 0) {
+ if (errno == EWOULDBLOCK)
+ goto rewrite;
+ goto done;
+ }
+ bp += wc;
+ cc -= wc;
+ if (cc == 0)
+ goto reread;
+ goto rewrite;
+done:
+ (void)shutdown(rem, 1);
+ exit(0);
+ }
+
+ (void)sigsetmask(omask);
+ FD_ZERO(&readfrom);
+ FD_SET(rfd2, &readfrom);
+ FD_SET(rem, &readfrom);
+ do {
+ ready = readfrom;
+ if (select(16, &ready, 0, 0, 0) < 0) {
+ if (errno != EINTR)
+ err(1, "select");
+ continue;
+ }
+ if (FD_ISSET(rfd2, &ready)) {
+ errno = 0;
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (doencrypt)
+ cc = des_read(rfd2, buf, sizeof buf);
+ else
+#endif
+#endif
+ cc = read(rfd2, buf, sizeof buf);
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ FD_CLR(rfd2, &readfrom);
+ } else
+ (void)write(2, buf, cc);
+ }
+ if (FD_ISSET(rem, &ready)) {
+ errno = 0;
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (doencrypt)
+ cc = des_read(rem, buf, sizeof buf);
+ else
+#endif
+#endif
+ cc = read(rem, buf, sizeof buf);
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ FD_CLR(rem, &readfrom);
+ } else
+ (void)write(1, buf, cc);
+ }
+ } while (FD_ISSET(rfd2, &readfrom) || FD_ISSET(rem, &readfrom));
+}
+
+void
+sendsig(sig)
+ int sig;
+{
+ char signo;
+
+ signo = sig;
+#ifdef KERBEROS
+#ifdef CRYPT
+ if (doencrypt)
+ (void)des_write(rfd2, &signo, 1);
+ else
+#endif
+#endif
+ (void)write(rfd2, &signo, 1);
+}
+
+#ifdef KERBEROS
+/* VARARGS */
+void
+warning(va_alist)
+va_dcl
+{
+ va_list ap;
+ char *fmt;
+
+ (void)fprintf(stderr, "rsh: warning, using standard rsh: ");
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, ".\n");
+}
+#endif
+
+char *
+copyargs(argv)
+ char **argv;
+{
+ int cc;
+ char **ap, *args, *p;
+
+ cc = 0;
+ for (ap = argv; *ap; ++ap)
+ cc += strlen(*ap) + 1;
+ if (!(args = malloc((u_int)cc)))
+ err(1, NULL);
+ for (p = args, ap = argv; *ap; ++ap) {
+ (void)strcpy(p, *ap);
+ for (p = strcpy(p, *ap); *p; ++p);
+ if (ap[1])
+ *p++ = ' ';
+ }
+ return (args);
+}
+
+void
+usage()
+{
+
+ (void)fprintf(stderr,
+ "usage: rsh [-nd%s]%s[-l login] host [command]\n",
+#ifdef KERBEROS
+#ifdef CRYPT
+ "x", " [-k realm] ");
+#else
+ "", " [-k realm] ");
+#endif
+#else
+ "", " ");
+#endif
+ exit(1);
+}
+
diff --git a/usr.bin/rup/Makefile b/usr.bin/rup/Makefile
new file mode 100644
index 0000000..1d53109
--- /dev/null
+++ b/usr.bin/rup/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:47:06 smace Exp $
+
+PROG= rup
+MAN1= rup.1
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rup/rup.1 b/usr.bin/rup/rup.1
new file mode 100644
index 0000000..e9bc3d3
--- /dev/null
+++ b/usr.bin/rup/rup.1
@@ -0,0 +1,94 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1985, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rup.1,v 1.1.1.1 1994/08/28 15:01:31 csgr Exp $
+.\"
+.Dd June 7, 1993
+.Dt RUP 1
+.Os BSD 4.3
+.Sh NAME
+.Nm rup
+.Nd remote status display
+.Sh SYNOPSIS
+.Nm rup
+.Op Ar host ...
+.Sh DESCRIPTION
+.Nm rup
+displays a summary of the current system status of a particular
+.Em host
+or all hosts on the local network.
+The output shows the current time of day, how long the system has
+been up,
+and the load averages.
+The load average numbers give the number of jobs in the run queue
+averaged over 1, 5 and 15 minutes.
+.Pp
+The
+.Xr rpc.rstatd 8
+daemon must be running on the remote host for this command to
+work.
+.Nm rup
+uses an RPC protocol defined in /usr/include/rpcsvc/rstat.x.
+.Sh EXAMPLE
+.Bd -unfilled -offset indent -compact
+example% rup otherhost
+otherhost 7:36am up 6 days, 16:45, load average: 0.20, 0.23, 0.18
+example%
+.Ed
+.Sh DIAGNOSTICS
+.Bl -tag -width indent
+.It rup: RPC: Program not registered
+The
+.Xr rpc.rstatd 8
+daemon has not been started on the remote host.
+.It rup: RPC: Timed out
+A communication error occurred. Either the network is
+excessively congested, or the
+.Xr rpc.rstatd 8
+daemon has terminated on the remote host.
+.It rup: RPC: Port mapper failure - RPC: Timed out
+The remote host is not running the portmapper (see
+.Xr portmap 8 ),
+and cannot accomodate any RPC-based services. The host may be down.
+.El
+.Sh SEE ALSO
+.Xr portmap 8 ,
+.Xr rpc.rstatd 8
+.Sh HISTORY
+The
+.Nm rup
+command
+appeared in
+.Em Sun-OS .
+.Sh BUGS
+The sorting options are not implemented.
diff --git a/usr.bin/rup/rup.c b/usr.bin/rup/rup.c
new file mode 100644
index 0000000..e248be1
--- /dev/null
+++ b/usr.bin/rup/rup.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rup.c,v 1.1.1.1 1994/08/28 15:01:31 csgr Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <arpa/inet.h>
+
+#undef FSHIFT /* Use protocol's shift and scale values */
+#undef FSCALE
+#include <rpcsvc/rstat.h>
+
+#define HOST_WIDTH 15
+
+char *argv0;
+
+struct host_list {
+ struct host_list *next;
+ struct in_addr addr;
+} *hosts;
+
+int search_host(struct in_addr addr)
+{
+ struct host_list *hp;
+
+ if (!hosts)
+ return(0);
+
+ for (hp = hosts; hp != NULL; hp = hp->next) {
+ if (hp->addr.s_addr == addr.s_addr)
+ return(1);
+ }
+ return(0);
+}
+
+void remember_host(struct in_addr addr)
+{
+ struct host_list *hp;
+
+ if (!(hp = (struct host_list *)malloc(sizeof(struct host_list)))) {
+ fprintf(stderr, "%s: no memory.\n", argv0);
+ exit(1);
+ }
+ hp->addr.s_addr = addr.s_addr;
+ hp->next = hosts;
+ hosts = hp;
+}
+
+rstat_reply(char *replyp, struct sockaddr_in *raddrp)
+{
+ struct tm *tmp_time;
+ struct tm host_time;
+ struct tm host_uptime;
+ char days_buf[16];
+ char hours_buf[16];
+ struct hostent *hp;
+ char *host;
+ statstime *host_stat = (statstime *)replyp;
+
+ if (search_host(raddrp->sin_addr))
+ return(0);
+
+ hp = gethostbyaddr((char *)&raddrp->sin_addr.s_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp)
+ host = hp->h_name;
+ else
+ host = inet_ntoa(raddrp->sin_addr);
+
+ /* truncate hostname to fit nicely into field */
+ if (strlen(host) > HOST_WIDTH)
+ host[HOST_WIDTH] = '\0';
+
+ printf("%-*s\t", HOST_WIDTH, host);
+
+ tmp_time = localtime((time_t *)&host_stat->curtime.tv_sec);
+ host_time = *tmp_time;
+
+ host_stat->curtime.tv_sec -= host_stat->boottime.tv_sec;
+
+ tmp_time = gmtime((time_t *)&host_stat->curtime.tv_sec);
+ host_uptime = *tmp_time;
+
+ if (host_uptime.tm_yday != 0)
+ sprintf(days_buf, "%3d day%s, ", host_uptime.tm_yday,
+ (host_uptime.tm_yday > 1) ? "s" : "");
+ else
+ days_buf[0] = '\0';
+
+ if (host_uptime.tm_hour != 0)
+ sprintf(hours_buf, "%2d:%02d, ",
+ host_uptime.tm_hour, host_uptime.tm_min);
+ else
+ if (host_uptime.tm_min != 0)
+ sprintf(hours_buf, "%2d mins, ", host_uptime.tm_min);
+ else
+ hours_buf[0] = '\0';
+
+ printf(" %2d:%02d%cm up %9.9s%9.9s load average: %.2f %.2f %.2f\n",
+ host_time.tm_hour % 12,
+ host_time.tm_min,
+ (host_time.tm_hour >= 12) ? 'p' : 'a',
+ days_buf,
+ hours_buf,
+ (double)host_stat->avenrun[0]/FSCALE,
+ (double)host_stat->avenrun[1]/FSCALE,
+ (double)host_stat->avenrun[2]/FSCALE);
+
+ remember_host(raddrp->sin_addr);
+ return(0);
+}
+
+onehost(char *host)
+{
+ CLIENT *rstat_clnt;
+ statstime host_stat;
+ struct sockaddr_in addr;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ fprintf(stderr, "%s: unknown host \"%s\"\n",
+ argv0, host);
+ return(-1);
+ }
+
+ rstat_clnt = clnt_create(host, RSTATPROG, RSTATVERS_TIME, "udp");
+ if (rstat_clnt == NULL) {
+ fprintf(stderr, "%s: %s %s", argv0, host, clnt_spcreateerror(""));
+ return(-1);
+ }
+
+ bzero((char *)&host_stat, sizeof(host_stat));
+ if (clnt_call(rstat_clnt, RSTATPROC_STATS, xdr_void, NULL, xdr_statstime, &host_stat, NULL) != RPC_SUCCESS) {
+ fprintf(stderr, "%s: %s: %s\n", argv0, host, clnt_sperror(rstat_clnt, host));
+ return(-1);
+ }
+
+ addr.sin_addr.s_addr = *(int *)hp->h_addr;
+ rstat_reply((char *)&host_stat, &addr);
+}
+
+allhosts()
+{
+ statstime host_stat;
+ enum clnt_stat clnt_stat;
+
+ clnt_stat = clnt_broadcast(RSTATPROG, RSTATVERS_TIME, RSTATPROC_STATS,
+ xdr_void, NULL,
+ xdr_statstime, &host_stat, rstat_reply);
+ if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) {
+ fprintf(stderr, "%s: %s\n", argv0, clnt_sperrno(clnt_stat));
+ exit(1);
+ }
+}
+
+usage()
+{
+ fprintf(stderr, "Usage: %s [hosts ...]\n", argv0);
+ exit(1);
+}
+
+main(int argc, char *argv[])
+{
+ int ch;
+ extern int optind;
+
+ if (!(argv0 = rindex(argv[0], '/')))
+ argv0 = argv[0];
+ else
+ argv0++;
+
+ while ((ch = getopt(argc, argv, "?")) != -1)
+ switch (ch) {
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+
+ setlinebuf(stdout);
+ if (argc == optind)
+ allhosts();
+ else {
+ for (; optind < argc; optind++)
+ (void) onehost(argv[optind]);
+ }
+ exit(0);
+}
diff --git a/usr.bin/ruptime/Makefile b/usr.bin/ruptime/Makefile
new file mode 100644
index 0000000..57f1e15
--- /dev/null
+++ b/usr.bin/ruptime/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ruptime
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ruptime/ruptime.1 b/usr.bin/ruptime/ruptime.1
new file mode 100644
index 0000000..b3b0af8
--- /dev/null
+++ b/usr.bin/ruptime/ruptime.1
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1983, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ruptime.1 8.2 (Berkeley) 4/5/94
+.\"
+.Dd April 5, 1994
+.Dt RUPTIME 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ruptime
+.Nd show host status of local machines
+.Sh SYNOPSIS
+.Nm ruptime
+.Op Fl alrtu
+.Sh DESCRIPTION
+.Nm Ruptime
+gives a status line like
+.Ar uptime
+for each machine on the local network; these are formed from packets
+broadcast by each host on the network once a minute.
+.Pp
+Machines for which no status report has been received for 11
+minutes are shown as being down.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Users idle an hour or more are not counted unless the
+.Fl a
+flag is given.
+.It Fl l
+Sort by load average.
+.It Fl r
+Reverses the sort order.
+.It Fl t
+Sort by uptime.
+.It Fl u
+Sort by number of users.
+.El
+.Pp
+The default listing is sorted by host name.
+.Sh FILES
+.Bl -tag -width /var/rwho/whod.* -compact
+.It Pa /var/rwho/whod.*
+data files
+.El
+.Sh SEE ALSO
+.Xr rwho 1
+.Xr uptime 1
+.Sh HISTORY
+.Nm Ruptime
+appeared in
+.Bx 4.2 .
diff --git a/usr.bin/ruptime/ruptime.c b/usr.bin/ruptime/ruptime.c
new file mode 100644
index 0000000..ba02fdb
--- /dev/null
+++ b/usr.bin/ruptime/ruptime.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: ruptime.c,v 1.5 1995/08/07 19:17:40 wollman Exp $ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ruptime.c 8.2 (Berkeley) 4/5/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <protocols/rwhod.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+struct hs {
+ struct whod *hs_wd;
+ int hs_nusers;
+} *hs;
+struct whod awhod;
+
+#define ISDOWN(h) (now - (h)->hs_wd->wd_recvtime > 11 * 60)
+#define WHDRSIZE (sizeof (awhod) - sizeof (awhod.wd_we))
+
+size_t nhosts;
+time_t now;
+int rflg = 1;
+
+int hscmp __P((const void *, const void *));
+char *interval __P((time_t, char *));
+int lcmp __P((const void *, const void *));
+void morehosts __P((void));
+int tcmp __P((const void *, const void *));
+int ucmp __P((const void *, const void *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ struct dirent *dp;
+ struct hs *hsp;
+ struct whod *wd;
+ struct whoent *we;
+ DIR *dirp;
+ size_t hspace;
+ int aflg, cc, ch, fd, i, maxloadav;
+ char buf[sizeof(struct whod)];
+ int (*cmp) __P((const void *, const void *));
+
+ aflg = 0;
+ cmp = hscmp;
+ while ((ch = getopt(argc, argv, "alrut")) != EOF)
+ switch (ch) {
+ case 'a':
+ aflg = 1;
+ break;
+ case 'l':
+ cmp = lcmp;
+ break;
+ case 'r':
+ rflg = -1;
+ break;
+ case 't':
+ cmp = tcmp;
+ break;
+ case 'u':
+ cmp = ucmp;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL)
+ err(1, "%s", _PATH_RWHODIR);
+
+ maxloadav = -1;
+ for (nhosts = hspace = 0; (dp = readdir(dirp)) != NULL;) {
+ if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5))
+ continue;
+ if ((fd = open(dp->d_name, O_RDONLY, 0)) < 0) {
+ warn("%s", dp->d_name);
+ continue;
+ }
+ cc = read(fd, buf, sizeof(struct whod));
+ (void)close(fd);
+
+ if (cc < WHDRSIZE)
+ continue;
+ if (nhosts == hspace) {
+ if ((hs =
+ realloc(hs, (hspace += 40) * sizeof(*hs))) == NULL)
+ err(1, NULL);
+ hsp = hs + nhosts;
+ }
+
+ if ((hsp->hs_wd = malloc((size_t)WHDRSIZE)) == NULL)
+ err(1, NULL);
+ memmove(hsp->hs_wd, buf, (size_t)WHDRSIZE);
+
+ for (wd = (struct whod *)buf, i = 0; i < 2; ++i)
+ if (wd->wd_loadav[i] > maxloadav)
+ maxloadav = wd->wd_loadav[i];
+
+ for (hsp->hs_nusers = 0,
+ we = (struct whoent *)(buf + cc); --we >= wd->wd_we;)
+ if (aflg || we->we_idle < 3600)
+ ++hsp->hs_nusers;
+ ++hsp;
+ ++nhosts;
+ }
+ if (nhosts == 0)
+ errx(1, "no hosts in %s.", _PATH_RWHODIR);
+
+ (void)time(&now);
+ qsort(hs, nhosts, sizeof(hs[0]), cmp);
+ for (i = 0; i < nhosts; i++) {
+ hsp = &hs[i];
+ if (ISDOWN(hsp)) {
+ (void)printf("%-12.12s%s\n", hsp->hs_wd->wd_hostname,
+ interval(now - hsp->hs_wd->wd_recvtime, "down"));
+ continue;
+ }
+ (void)printf(
+ "%-12.12s%s, %4d user%s load %*.2f, %*.2f, %*.2f\n",
+ hsp->hs_wd->wd_hostname,
+ interval((time_t)hsp->hs_wd->wd_sendtime -
+ (time_t)hsp->hs_wd->wd_boottime, " up"),
+ hsp->hs_nusers,
+ hsp->hs_nusers == 1 ? ", " : "s,",
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[0] / 100.0,
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[1] / 100.0,
+ maxloadav >= 1000 ? 5 : 4,
+ hsp->hs_wd->wd_loadav[2] / 100.0);
+ }
+ exit(0);
+}
+
+char *
+interval(tval, updown)
+ time_t tval;
+ char *updown;
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (tval < 0) {
+ (void)snprintf(resbuf, sizeof(resbuf), " %s ??:??", updown);
+ return (resbuf);
+ }
+ /* round to minutes. */
+ minutes = (tval + (60 - 1)) / 60;
+ hours = minutes / 60;
+ minutes %= 60;
+ days = hours / 24;
+ hours %= 24;
+ if (days)
+ (void)snprintf(resbuf, sizeof(resbuf),
+ "%s %2d+%02d:%02d", updown, days, hours, minutes);
+ else
+ (void)snprintf(resbuf, sizeof(resbuf),
+ "%s %2d:%02d", updown, hours, minutes);
+ return (resbuf);
+}
+
+#define HS(a) ((struct hs *)(a))
+
+/* Alphabetical comparison. */
+int
+hscmp(a1, a2)
+ const void *a1, *a2;
+{
+ return (rflg *
+ strcmp(HS(a1)->hs_wd->wd_hostname, HS(a2)->hs_wd->wd_hostname));
+}
+
+/* Load average comparison. */
+int
+lcmp(a1, a2)
+ const void *a1, *a2;
+{
+ if (ISDOWN(HS(a1)))
+ if (ISDOWN(HS(a2)))
+ return (tcmp(a1, a2));
+ else
+ return (rflg);
+ else if (ISDOWN(HS(a2)))
+ return (-rflg);
+ else
+ return (rflg *
+ (HS(a2)->hs_wd->wd_loadav[0] - HS(a1)->hs_wd->wd_loadav[0]));
+}
+
+/* Number of users comparison. */
+int
+ucmp(a1, a2)
+ const void *a1, *a2;
+{
+ if (ISDOWN(HS(a1)))
+ if (ISDOWN(HS(a2)))
+ return (tcmp(a1, a2));
+ else
+ return (rflg);
+ else if (ISDOWN(HS(a2)))
+ return (-rflg);
+ else
+ return (rflg * (HS(a2)->hs_nusers - HS(a1)->hs_nusers));
+}
+
+/* Uptime comparison. */
+int
+tcmp(a1, a2)
+ const void *a1, *a2;
+{
+ return (rflg * (
+ (ISDOWN(HS(a2)) ? HS(a2)->hs_wd->wd_recvtime - now
+ : HS(a2)->hs_wd->wd_sendtime - HS(a2)->hs_wd->wd_boottime)
+ -
+ (ISDOWN(HS(a1)) ? HS(a1)->hs_wd->wd_recvtime - now
+ : HS(a1)->hs_wd->wd_sendtime - HS(a1)->hs_wd->wd_boottime)
+ ));
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: ruptime [-alrut]\n");
+ exit(1);
+}
diff --git a/usr.bin/rusers/Makefile b/usr.bin/rusers/Makefile
new file mode 100644
index 0000000..6e2fd6a
--- /dev/null
+++ b/usr.bin/rusers/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:47:23 smace Exp $
+
+PROG = rusers
+MAN1 = rusers.1
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rusers/rusers.1 b/usr.bin/rusers/rusers.1
new file mode 100644
index 0000000..ef7d123
--- /dev/null
+++ b/usr.bin/rusers/rusers.1
@@ -0,0 +1,93 @@
+.\" Copyright (c) 1983, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)rusers.1 6.7 (Berkeley) 4/23/91
+.\" $Id: rusers.1,v 1.1 1993/09/16 01:15:47 jtc Exp $
+.\"
+.Dd April 23, 1991
+.Dt RUSERS 1
+.Os BSD 4.2
+.Sh NAME
+.Nm rusers
+.Nd who is logged in to machines on local network
+.Sh SYNOPSIS
+.Nm rusers
+.Op Fl al
+.Op Ar host ...
+.Sh DESCRIPTION
+The
+.Nm rusers
+command produces output similar to
+.Xr who ,
+but for the list of hosts or all machines on the local
+network. For each host responding to the rusers query,
+the hostname with the names of the users currently logged
+on is printed on each line. The rusers command will wait for
+one minute to catch late responders.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Print all machines responding even if no one is currently logged in.
+.It Fl l
+Print a long format listing. This includes the user name, host name,
+tty that the user is logged in to, the date and time the user
+logged in, the amount of time since the user typed on the keyboard,
+and the remote host they logged in from (if applicable).
+.El
+.Sh DIAGNOSTICS
+.Bl -tag -width indent
+.It rusers: RPC: Program not registered
+The
+.Xr rpc.rusersd 8
+daemon has not been started on the remote host.
+.It rusers: RPC: Timed out
+A communication error occurred. Either the network is
+excessively congested, or the
+.Xr rpc.rusersd 8
+daemon has terminated on the remote host.
+.It rusers: RPC: Port mapper failure - RPC: Timed out
+The remote host is not running the portmapper (see
+.Xr portmap 8 ),
+and cannot accomodate any RPC-based services. The host may be down.
+.El
+.Sh SEE ALSO
+.Xr portmap 8 ,
+.Xr who 1 ,
+.Xr rpc.rusersd 8
+.Sh HISTORY
+The
+.Nm rusers
+command
+appeared in
+.Em Sun-OS .
+.Sh BUGS
+The sorting options are not implemented.
diff --git a/usr.bin/rusers/rusers.c b/usr.bin/rusers/rusers.c
new file mode 100644
index 0000000..9c72342
--- /dev/null
+++ b/usr.bin/rusers/rusers.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 1993, John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: rusers.c,v 1.1.1.1 1994/08/28 15:06:02 csgr Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <strings.h>
+#include <rpc/rpc.h>
+#include <arpa/inet.h>
+#include <rpcsvc/rnusers.h>
+
+#define MAX_INT 0x7fffffff
+#define HOST_WIDTH 20
+#define LINE_WIDTH 15
+char *argv0;
+
+int longopt;
+int allopt;
+
+struct host_list {
+ struct host_list *next;
+ struct in_addr addr;
+} *hosts;
+
+int search_host(struct in_addr addr)
+{
+ struct host_list *hp;
+
+ if (!hosts)
+ return(0);
+
+ for (hp = hosts; hp != NULL; hp = hp->next) {
+ if (hp->addr.s_addr == addr.s_addr)
+ return(1);
+ }
+ return(0);
+}
+
+void remember_host(struct in_addr addr)
+{
+ struct host_list *hp;
+
+ if (!(hp = (struct host_list *)malloc(sizeof(struct host_list)))) {
+ fprintf(stderr, "%s: no memory.\n", argv0);
+ exit(1);
+ }
+ hp->addr.s_addr = addr.s_addr;
+ hp->next = hosts;
+ hosts = hp;
+}
+
+rusers_reply(char *replyp, struct sockaddr_in *raddrp)
+{
+ int x, idle;
+ char date[32], idle_time[64], remote[64];
+ struct hostent *hp;
+ utmpidlearr *up = (utmpidlearr *)replyp;
+ char *host;
+ int days, hours, minutes, seconds;
+
+ if (search_host(raddrp->sin_addr))
+ return(0);
+
+ if (!allopt && !up->utmpidlearr_len)
+ return(0);
+
+ hp = gethostbyaddr((char *)&raddrp->sin_addr.s_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (hp)
+ host = hp->h_name;
+ else
+ host = inet_ntoa(raddrp->sin_addr);
+
+ if (!longopt)
+ printf("%-*s ", HOST_WIDTH, host);
+
+ for (x = 0; x < up->utmpidlearr_len; x++) {
+ strncpy(date,
+ &(ctime((time_t *)&(up->utmpidlearr_val[x].ui_utmp.ut_time))[4]),
+ sizeof(date)-1);
+
+ idle = up->utmpidlearr_val[x].ui_idle;
+ sprintf(idle_time, " :%02d", idle);
+ if (idle == MAX_INT)
+ strcpy(idle_time, "??");
+ else if (idle == 0)
+ strcpy(idle_time, "");
+ else {
+ seconds = idle;
+ days = seconds/(60*60*24);
+ seconds %= (60*60*24);
+ hours = seconds/(60*60);
+ seconds %= (60*60);
+ minutes = seconds/60;
+ seconds %= 60;
+ if (idle > 60)
+ sprintf(idle_time, "%d:%02d",
+ minutes, seconds);
+ if (idle >= (60*60))
+ sprintf(idle_time, "%d:%02d:%02d",
+ hours, minutes, seconds);
+ if (idle >= (24*60*60))
+ sprintf(idle_time, "%d days, %d:%02d:%02d",
+ days, hours, minutes, seconds);
+ }
+
+ strncpy(remote, up->utmpidlearr_val[x].ui_utmp.ut_host, sizeof(remote)-1);
+ if (strlen(remote) != 0)
+ sprintf(remote, "(%.16s)", up->utmpidlearr_val[x].ui_utmp.ut_host);
+
+ if (longopt)
+ printf("%-8.8s %*s:%-*.*s %-12.12s %6s %.18s\n",
+ up->utmpidlearr_val[x].ui_utmp.ut_name,
+ HOST_WIDTH, host,
+ LINE_WIDTH, LINE_WIDTH, up->utmpidlearr_val[x].ui_utmp.ut_line,
+ date,
+ idle_time,
+ remote
+ );
+ else
+ printf("%s ",
+ up->utmpidlearr_val[x].ui_utmp.ut_name);
+ }
+ if (!longopt)
+ putchar('\n');
+
+ remember_host(raddrp->sin_addr);
+ return(0);
+}
+
+onehost(char *host)
+{
+ utmpidlearr up;
+ CLIENT *rusers_clnt;
+ struct sockaddr_in addr;
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ fprintf(stderr, "%s: unknown host \"%s\"\n",
+ argv0, host);
+ exit(1);
+ }
+
+ rusers_clnt = clnt_create(host, RUSERSPROG, RUSERSVERS_IDLE, "udp");
+ if (rusers_clnt == NULL) {
+ clnt_pcreateerror(argv0);
+ exit(1);
+ }
+
+ bzero((char *)&up, sizeof(up));
+ if (clnt_call(rusers_clnt, RUSERSPROC_NAMES, xdr_void, NULL, xdr_utmpidlearr, &up, NULL) != RPC_SUCCESS) {
+ clnt_perror(rusers_clnt, argv0);
+ exit(1);
+ }
+ addr.sin_addr.s_addr = *(int *)hp->h_addr;
+ rusers_reply((char *)&up, &addr);
+}
+
+allhosts()
+{
+ utmpidlearr up;
+ enum clnt_stat clnt_stat;
+
+ bzero((char *)&up, sizeof(up));
+ clnt_stat = clnt_broadcast(RUSERSPROG, RUSERSVERS_IDLE, RUSERSPROC_NAMES,
+ xdr_void, NULL,
+ xdr_utmpidlearr, &up, rusers_reply);
+ if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_TIMEDOUT) {
+ fprintf(stderr, "%s: %s\n", argv0, clnt_sperrno(clnt_stat));
+ exit(1);
+ }
+}
+
+usage()
+{
+ fprintf(stderr, "Usage: %s [-la] [hosts ...]\n", argv0);
+ exit(1);
+}
+
+main(int argc, char *argv[])
+{
+ int ch;
+ extern int optind;
+
+ if (!(argv0 = rindex(argv[0], '/')))
+ argv0 = argv[0];
+ else
+ argv0++;
+
+
+ while ((ch = getopt(argc, argv, "al")) != -1)
+ switch (ch) {
+ case 'a':
+ allopt++;
+ break;
+ case 'l':
+ longopt++;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+
+ setlinebuf(stdout);
+ if (argc == optind)
+ allhosts();
+ else {
+ for (; optind < argc; optind++)
+ (void) onehost(argv[optind]);
+ }
+ exit(0);
+}
diff --git a/usr.bin/rwall/Makefile b/usr.bin/rwall/Makefile
new file mode 100644
index 0000000..07bb678
--- /dev/null
+++ b/usr.bin/rwall/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.2 1993/11/10 03:47:37 smace Exp $
+
+PROG = rwall
+MAN1 = rwall.1
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rwall/rwall.1 b/usr.bin/rwall/rwall.1
new file mode 100644
index 0000000..0bab417
--- /dev/null
+++ b/usr.bin/rwall/rwall.1
@@ -0,0 +1,79 @@
+.\" Copyright (c) 1983, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)rwall.1 6.7 (Berkeley) 4/23/91
+.\" $Id: rwall.1,v 1.1 1993/09/16 01:11:03 jtc Exp $
+.\"
+.Dd April 23, 1991
+.Dt RWALL 1
+.Os BSD 4.2
+.Sh NAME
+.Nm rwall
+.Nd send a message to users logged on a host
+.Sh SYNOPSIS
+.Nm rwall
+.Ar host
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm rwall
+command sends a message to the users logged into the specified host. The
+message to be sent can be typed in and terminated with EOF or it can
+be in a
+.Ar file .
+.Sh DIAGNOSTICS
+.Bl -tag -width indent
+.It rwall: RPC: Program not registered
+The
+.Xr rpc.rwalld 8
+daemon has not been started on the remote host.
+.It rwall: RPC: Timed out
+A communication error occurred. Either the network is
+excessively congested, or the
+.Xr rpc.rwalld 8
+daemon has terminated on the remote host.
+.It rwall: RPC: Port mapper failure - RPC: Timed out
+The remote host is not running the portmapper (see
+.Xr portmap 8 ),
+and cannot accomodate any RPC-based services. The host may be down.
+.El
+.Sh SEE ALSO
+.Xr portmap 8 ,
+.Xr who 1 ,
+.Xr rpc.rwalld 8
+.Sh HISTORY
+The
+.Nm rwall
+command
+appeared in
+.Em Sun-OS .
+.Sh BUGS
+The sorting options are not implemented.
diff --git a/usr.bin/rwall/rwall.c b/usr.bin/rwall/rwall.c
new file mode 100644
index 0000000..8a2e2b3
--- /dev/null
+++ b/usr.bin/rwall/rwall.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * Copyright (c) 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)wall.c 5.14 (Berkeley) 3/2/91";*/
+static char rcsid[] = "$Id: rwall.c,v 1.1.1.1 1994/08/28 15:11:02 csgr Exp $";
+#endif /* not lint */
+
+/*
+ * This program is not related to David Wall, whose Stanford Ph.D. thesis
+ * is entitled "Mechanisms for Broadcast and Selective Broadcast".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/rwall.h>
+
+int mbufsize;
+char *mbuf;
+
+/* ARGSUSED */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *wallhost, res;
+ CLIENT *cl;
+
+ if ((argc < 2) || (argc > 3)) {
+ fprintf(stderr, "usage: %s hostname [file]\n", argv[0]);
+ exit(1);
+ }
+
+ wallhost = argv[1];
+
+ makemsg(argv[2]);
+
+ /*
+ * Create client "handle" used for calling MESSAGEPROG on the
+ * server designated on the command line. We tell the rpc package
+ * to use the "tcp" protocol when contacting the server.
+ */
+ cl = clnt_create(wallhost, WALLPROG, WALLVERS, "udp");
+ if (cl == NULL) {
+ /*
+ * Couldn't establish connection with server.
+ * Print error message and die.
+ */
+ clnt_pcreateerror(wallhost);
+ exit(1);
+ }
+
+ if (clnt_call(cl, WALLPROC_WALL, xdr_wrapstring, &mbuf, xdr_void, &res, NULL) != RPC_SUCCESS) {
+ /*
+ * An error occurred while calling the server.
+ * Print error message and die.
+ */
+ clnt_perror(cl, wallhost);
+ exit(1);
+ }
+
+ exit(0);
+}
+
+makemsg(fname)
+ char *fname;
+{
+ register int ch, cnt;
+ struct tm *lt;
+ struct passwd *pw;
+ struct stat sbuf;
+ time_t now, time();
+ FILE *fp;
+ int fd;
+ char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[15];
+ char *getlogin(), *strcpy(), *ttyname();
+
+ (void)strcpy(tmpname, _PATH_TMP);
+ (void)strcat(tmpname, "/wall.XXXXXX");
+ if (!(fd = mkstemp(tmpname)) || !(fp = fdopen(fd, "r+"))) {
+ (void)fprintf(stderr, "wall: can't open temporary file.\n");
+ exit(1);
+ }
+ (void)unlink(tmpname);
+
+ if (!(whom = getlogin()))
+ whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
+ (void)gethostname(hostname, sizeof(hostname));
+ (void)time(&now);
+ lt = localtime(&now);
+
+ /*
+ * all this stuff is to blank out a square for the message;
+ * we wrap message lines at column 79, not 80, because some
+ * terminals wrap after 79, some do not, and we can't tell.
+ * Which means that we may leave a non-blank character
+ * in column 80, but that can't be helped.
+ */
+ (void)fprintf(fp, "Remote Broadcast Message from %s@%s\n",
+ whom, hostname);
+ (void)fprintf(fp, " (%s) at %d:%02d ...\n", ttyname(2),
+ lt->tm_hour, lt->tm_min);
+
+ putc('\n', fp);
+
+ if (fname && !(freopen(fname, "r", stdin))) {
+ (void)fprintf(stderr, "wall: can't read %s.\n", fname);
+ exit(1);
+ }
+ while (fgets(lbuf, sizeof(lbuf), stdin))
+ fputs(lbuf, fp);
+ rewind(fp);
+
+ if (fstat(fd, &sbuf)) {
+ (void)fprintf(stderr, "wall: can't stat temporary file.\n");
+ exit(1);
+ }
+ mbufsize = sbuf.st_size;
+ if (!(mbuf = malloc((u_int)mbufsize))) {
+ (void)fprintf(stderr, "wall: out of memory.\n");
+ exit(1);
+ }
+ if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) {
+ (void)fprintf(stderr, "wall: can't read temporary file.\n");
+ exit(1);
+ }
+ (void)close(fd);
+}
diff --git a/usr.bin/rwho/Makefile b/usr.bin/rwho/Makefile
new file mode 100644
index 0000000..328aa38
--- /dev/null
+++ b/usr.bin/rwho/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rwho
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/rwho/rwho.1 b/usr.bin/rwho/rwho.1
new file mode 100644
index 0000000..f039b01
--- /dev/null
+++ b/usr.bin/rwho/rwho.1
@@ -0,0 +1,80 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rwho.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt RWHO 1
+.Os BSD 4.2
+.Sh NAME
+.Nm rwho
+.Nd who is logged in on local machines
+.Sh SYNOPSIS
+.Nm rwho
+.Op Fl a
+.Sh DESCRIPTION
+The
+.Nm rwho
+command produces output similar to
+.Xr who ,
+but for all machines on the local network.
+If no report has been
+received from a machine for 5 minutes then
+.Nm rwho
+assumes the machine is down, and does not report users last known
+to be logged into that machine.
+.Pp
+If a users hasn't typed to the system for a minute or more, then
+.Nm rwho
+reports this idle time. If a user hasn't typed to the system for
+an hour or more, then
+the user will be omitted from the output of
+.Nm rwho
+unless the
+.Fl a
+flag is given.
+.Sh FILES
+.Bl -tag -width /var/rwho/rhowd.* -compact
+.It Pa /var/rwho/whod.*
+information about other machines
+.El
+.Sh SEE ALSO
+.Xr ruptime 1 ,
+.Xr rwhod 8
+.Sh HISTORY
+The
+.Nm rwho
+command
+appeared in
+.Bx 4.3 .
+.Sh BUGS
+This is unwieldy when the number of machines
+on the local net is large.
diff --git a/usr.bin/rwho/rwho.c b/usr.bin/rwho/rwho.c
new file mode 100644
index 0000000..c7f8398
--- /dev/null
+++ b/usr.bin/rwho/rwho.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rwho.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <protocols/rwhod.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+DIR *dirp;
+
+struct whod wd;
+int utmpcmp();
+#define NUSERS 1000
+struct myutmp {
+ char myhost[MAXHOSTNAMELEN];
+ int myidle;
+ struct outmp myutmp;
+} myutmp[NUSERS];
+int nusers;
+
+#define WHDRSIZE (sizeof (wd) - sizeof (wd.wd_we))
+/*
+ * this macro should be shared with ruptime.
+ */
+#define down(w,now) ((now) - (w)->wd_recvtime > 11 * 60)
+
+time_t now;
+int aflg;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ struct direct *dp;
+ int cc, width;
+ register struct whod *w = &wd;
+ register struct whoent *we;
+ register struct myutmp *mp;
+ int f, n, i;
+ time_t time();
+
+ while ((ch = getopt(argc, argv, "a")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ aflg = 1;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "usage: rwho [-a]\n");
+ exit(1);
+ }
+ if (chdir(_PATH_RWHODIR) || (dirp = opendir(".")) == NULL) {
+ perror(_PATH_RWHODIR);
+ exit(1);
+ }
+ mp = myutmp;
+ (void)time(&now);
+ while (dp = readdir(dirp)) {
+ if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5))
+ continue;
+ f = open(dp->d_name, O_RDONLY);
+ if (f < 0)
+ continue;
+ cc = read(f, (char *)&wd, sizeof (struct whod));
+ if (cc < WHDRSIZE) {
+ (void) close(f);
+ continue;
+ }
+ if (down(w,now)) {
+ (void) close(f);
+ continue;
+ }
+ cc -= WHDRSIZE;
+ we = w->wd_we;
+ for (n = cc / sizeof (struct whoent); n > 0; n--) {
+ if (aflg == 0 && we->we_idle >= 60*60) {
+ we++;
+ continue;
+ }
+ if (nusers >= NUSERS) {
+ printf("too many users\n");
+ exit(1);
+ }
+ mp->myutmp = we->we_utmp; mp->myidle = we->we_idle;
+ (void) strcpy(mp->myhost, w->wd_hostname);
+ nusers++; we++; mp++;
+ }
+ (void) close(f);
+ }
+ qsort((char *)myutmp, nusers, sizeof (struct myutmp), utmpcmp);
+ mp = myutmp;
+ width = 0;
+ for (i = 0; i < nusers; i++) {
+ /* append one for the blank and use 8 for the out_line */
+ int j = strlen(mp->myhost) + 1 + 8;
+ if (j > width)
+ width = j;
+ mp++;
+ }
+ mp = myutmp;
+ for (i = 0; i < nusers; i++) {
+ char buf[BUFSIZ], cbuf[80];
+ strftime(cbuf, sizeof(cbuf), "%c", localtime((time_t *)&mp->myutmp.out_time));
+ (void)sprintf(buf, "%s:%-.8s", mp->myhost, mp->myutmp.out_line);
+ printf("%-8.8s %-*s %.12s",
+ mp->myutmp.out_name,
+ width,
+ buf,
+ cbuf + 4);
+ mp->myidle /= 60;
+ if (mp->myidle) {
+ if (aflg) {
+ if (mp->myidle >= 100*60)
+ mp->myidle = 100*60 - 1;
+ if (mp->myidle >= 60)
+ printf(" %2d", mp->myidle / 60);
+ else
+ printf(" ");
+ } else
+ printf(" ");
+ printf(":%02d", mp->myidle % 60);
+ }
+ printf("\n");
+ mp++;
+ }
+ exit(0);
+}
+
+utmpcmp(u1, u2)
+ struct myutmp *u1, *u2;
+{
+ int rc;
+
+ rc = strncmp(u1->myutmp.out_name, u2->myutmp.out_name, 8);
+ if (rc)
+ return (rc);
+ rc = strncmp(u1->myhost, u2->myhost, 8);
+ if (rc)
+ return (rc);
+ return (strncmp(u1->myutmp.out_line, u2->myutmp.out_line, 8));
+}
diff --git a/usr.bin/sasc/INSTALL b/usr.bin/sasc/INSTALL
new file mode 100644
index 0000000..448a7a2
--- /dev/null
+++ b/usr.bin/sasc/INSTALL
@@ -0,0 +1,86 @@
+To install the device driver, please do the following steps:
+
+1. Install the files by copying them as listed in the following table:
+
+ asc.c /usr/sys/i386/isa/
+ ascreg.h /usr/sys/i386/isa/
+ asc_ioctl.h /usr/sys/i386/include/
+
+ Note that if you have copies of the system header directories
+ in /usr/include/ instead of symbolic links, you have to copy
+ `asc.h' to /usr/include/machine/ also. I recommend to replace
+ the copies by links though.
+
+[the following steps, 2..5, can be achieved by moving to
+ /sys/i386 and doing "patch < diffs.asc"]
+
+2. Make the driver source known to config(8) by editing the file
+ /usr/src/sys/i386/conf/files.i386. Just append the following line:
+
+ i386/isa/asc.c optional asc device-driver
+
+3. Include a driver access record in /usr/src/sys/i386/i386/conf.c
+ Append the following structure at the end of the array that
+ contains the *character* device drivers. Remember the major number
+ that will be used for the driver, i.e. the number following the
+ number af the preceeding record.
+
+ { ascopen, ascclose, ascread, nowrite, /*<major>*/
+ ascioctl, nostop, nullreset, nodevtotty, /* asc */
+ ascselect, nommap, NULL },
+
+4. Insert the definitions for the base port addresses of the device
+ into the file /usr/src/sys/i386/isa/isa.h:
+
+#define IO_ASC1 0x3EB /* AmiScan addr.grp. 1 */
+#define IO_ASC2 0x22B /* AmiScan addr.grp. 2 */
+#define IO_ASC3 0x26B /* AmiScan addr.grp. 3 */
+#define IO_ASC4 0x2AB /* AmiScan addr.grp. 4 */
+#define IO_ASC5 0x2EB /* AmiScan addr.grp. 5 */
+#define IO_ASC6 0x32B /* AmiScan addr.grp. 6 */
+#define IO_ASC7 0x36B /* AmiScan addr.grp. 7 */
+#define IO_ASC8 0x3AB /* AmiScan addr.grp. 8 */
+
+5. Patch /dev/MAKEDEV by adding the following lines in the switch to
+ create the device entries:
+
+ asc*)
+ rm -f asc0
+ mknod asc0 c 68 0
+ mknod asc0p c 68 8
+ chmod 666 asc0 asc0p
+ chown root.wheel asc0 asc0p
+ ;;
+
+
+
+6. Edit your kernel configuration file (in /usr/src/sys/i386/conf/)
+ by inserting the following line:
+
+ device asc0 at isa? port 0x2ab tty drq 3 irq 10 vector ascintr
+
+ This should usually work for you as it reflects the factory
+ settings of the AMI scanner. However, if this conflicts with
+ any other device on your system, you have the option to change
+ `drq 3' into `drq 1' or `drq 5' and the actual port value to
+ "IO_ASC1".. "IO_ASC8" as defined in isa.h
+
+7. Rebuild the kernel, don't forget to config(8) and `make depend' first.
+
+8. Make the following device nodes:
+
+ mknod /dev/asc0 c <major> 0
+ mknod /dev/asc0p c <major> 8
+
+ (or, cd /dev and do ./MAKEDEV asc0).
+
+ If you plan to modify the and debug the driver, add these (you
+ won't need these though, unless you know how to get this
+ information from elsewhere.
+
+ mknod /dev/asc0d c <major> 32
+ mknod /dev/asc0pd c <major> 40
+
+9. Install the new kernel and reboot. You can try the driver by
+ doing a simple "cat /dev/asc0p > myfile.pbm" and then trying
+ to display the PBM image with xv or some other tool.
diff --git a/usr.bin/sasc/Makefile b/usr.bin/sasc/Makefile
new file mode 100644
index 0000000..022cd27
--- /dev/null
+++ b/usr.bin/sasc/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sasc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sasc/README b/usr.bin/sasc/README
new file mode 100644
index 0000000..5436bd2
--- /dev/null
+++ b/usr.bin/sasc/README
@@ -0,0 +1,9 @@
+ASC - A device driver for a handy scanner
+
+This is a device driver for GI1904-based hand scanners, e.g. the Trust
+Amiscan Grey and possibly others. The driver is based on the "gsc"
+driver and, partly, on a Linux driver.
+
+The driver has a working select().
+
+-Luigi Rizzo (luigi@iet.unipi.it)
diff --git a/usr.bin/sasc/sasc.1 b/usr.bin/sasc/sasc.1
new file mode 100644
index 0000000..754ba2a
--- /dev/null
+++ b/usr.bin/sasc/sasc.1
@@ -0,0 +1,94 @@
+.\" sasc(1) - manual page for the `asc' scanner device driver utility
+.\"
+.\"
+.\" Copyright (c) 1995 Gunther Schadow. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Gunther Schadow.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id:$
+.\"
+.TH SASC 1 "January 6, 1995" FreeBSD "FreeBSD Reference Manual"
+.SH NAME
+\fBsasc\fP - set the options of the asc scanner device
+.SH SYNOPSIS
+.TP 5
+\fBsasc\fP
+[\fB-sq\fP]
+[\fB-b\fP\ \fIlen\fP]
+[\fB-f\fP\ \fIfile\fP]
+[\fB-h\fP\ \fIheight\fP]
+[\fB-r\fP\ \fIresolution\fP]
+[\fB-t\fP\ \fItimeout\fP]
+[\fB-w\fP\ \fIwidth\fP]
+.SH DESCRIPTION
+The \fBsasc\fP utility provides shell level access to the ioctl
+requests served by the handy scanner device driver asc. Please see
+asc(4) for the exact meaning of the requests. Generally they modify
+the output and behaviour of the asc scanner device. When \fBsasc\fP is
+called with no option only the current settings are reported.
+.SH OPTIONS
+.TP 3
+\fB-s\fP [ASC_SRESSW]
+Set the scanner with the values of the resolution selector switch.
+.TP
+\fB-q\fP
+Operate in quiet mode, i.e. do not report any of the current setings.
+Normally the parameters are shown after the modifications have been
+performed.
+.TP
+\fB-f\fP \fIfile\fP
+Operate on a different scanner device node given by filename. Note
+that even though there may exist more than one node of scanner device
+several of them will refer the same device unit. The modifications are
+performed od the unit regardless of the device node by which it is
+accessed.
+.TP
+\fB-r\fP \fIresolution\fP [ASC_SRES]
+Set the resolution in dpi.
+.TP
+\fB-w\fP \fIwidth\fP [ASC_SWIDHT]
+Set the width of the bitmap in pixels.
+.TP
+\fB-h\fP \fIheight\fP [ASC_SHEIGHT]
+Set the height of the bitmap in lines of pixels.
+.TP
+\fB-b\fP \fIlen\fP [ASC_SBLEN]
+Set the internal dma buffer to be \fIlen\fP lines in size.
+.TP
+\fB-t\fP \fItimeout\fP [ASC_SBTIME]
+Set the timeout time for reading one dma buffer.
+.SH FILES
+.TP 15
+.BI /dev/asc0
+device node for \fIraw\fP output.
+.TP
+.BI /dev/asc0p
+device node for output in \fIpbm\fP file format.
+.PB
+.SH SEE ALSO
+asc(4)
+.SH AUTHOR
+Gunther Schadow <gusw@fub46.zedat.fu-berlin.de>
diff --git a/usr.bin/sasc/sasc.c b/usr.bin/sasc/sasc.c
new file mode 100644
index 0000000..2e1c74f
--- /dev/null
+++ b/usr.bin/sasc/sasc.c
@@ -0,0 +1,196 @@
+/* sasc(1) - utility for the `asc' scanner device driver
+ *
+ *
+ * Copyright (c) 1995 Gunther Schadow. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gunther Schadow.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * $Id:$
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/asc_ioctl.h>
+
+#ifndef DEFAULT_FILE
+#define DEFAULT_FILE "/dev/asc0"
+#endif
+#ifdef FAIL
+#undef FAIL
+#endif
+#define FAIL -1
+
+usage(char *progn)
+{
+ fprintf(stderr, "usage: %s [-sq] [-f file] [-r dpi] "
+ "[-w width] [-h height] "
+ "[-b len] [-t time]\n", progn);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char c;
+ int fd;
+
+ char *file = DEFAULT_FILE;
+
+ int show_dpi = 0;
+ int show_width = 0;
+ int show_height = 0;
+ int show_blen = 0;
+ int show_btime = 0;
+ int show_all = 1;
+
+ int set_blen = 0;
+ int set_dpi = 0;
+ int set_width = 0;
+ int set_height = 0;
+ int set_btime = 0;
+ int set_switch = 0;
+
+ char *progn = *argv;
+
+ if (argc == 0) usage(progn);
+
+ while( (c = getopt(argc, argv, "sqf:b:r:w:h:t:")) != FAIL)
+ {
+ switch(c) {
+ case 'f': file = optarg; break;
+ case 'r': set_dpi = atoi(optarg); break;
+ case 'w': set_width = atoi(optarg); break;
+ case 'h': set_height = atoi(optarg); break;
+ case 'b': set_blen = atoi(optarg); break;
+ case 't': set_btime = atoi(optarg); break;
+ case 's': set_switch = 1; break;
+ case 'q': show_all = 0; break;
+ default: usage(progn);
+ }
+ }
+
+ fd = open(file, O_RDONLY);
+ if ( fd == FAIL )
+ {
+ perror(file);
+ exit(1);
+ }
+
+ if (set_switch != 0)
+ {
+ if(ioctl(fd, ASC_SRESSW) == FAIL)
+ {
+ perror("ASC_SRESSW");
+ exit(1);
+ }
+ }
+
+ if (set_dpi != 0)
+ {
+ if(ioctl(fd, ASC_SRES, &set_dpi) == FAIL)
+ {
+ perror("ASC_SRES");
+ exit(1);
+ }
+ }
+
+ if (set_width != 0)
+ {
+ if(ioctl(fd, ASC_SWIDTH, &set_width) == FAIL)
+ {
+ perror("ASC_SWIDTH");
+ exit(1);
+ }
+ }
+
+ if (set_height != 0)
+ {
+ if(ioctl(fd, ASC_SHEIGHT, &set_height) == FAIL)
+ {
+ perror("ASC_SHEIGHT");
+ exit(1);
+ }
+ }
+
+ if (set_blen != 0)
+ {
+ if(ioctl(fd, ASC_SBLEN, &set_blen) == FAIL)
+ {
+ perror("ASC_SBLEN");
+ exit(1);
+ }
+ }
+
+ if (set_btime != 0)
+ {
+ if(ioctl(fd, ASC_SBTIME, &set_btime) == FAIL)
+ {
+ perror("ASC_SBTIME");
+ exit(1);
+ }
+ }
+
+ if (show_all != 0)
+ {
+ if(ioctl(fd, ASC_GRES, &show_dpi) == FAIL)
+ {
+ perror("ASC_GRES");
+ exit(1);
+ }
+ if(ioctl(fd, ASC_GWIDTH, &show_width) == FAIL)
+ {
+ perror("ASC_GWIDTH");
+ exit(1);
+ }
+ if(ioctl(fd, ASC_GHEIGHT, &show_height) == FAIL)
+ {
+ perror("ASC_GHEIGHT");
+ exit(1);
+ }
+ if(ioctl(fd, ASC_GBLEN, &show_blen) == FAIL)
+ {
+ perror("ASC_GBLEN");
+ exit(1);
+ }
+ if(ioctl(fd, ASC_GBTIME, &show_btime) == FAIL)
+ {
+ perror("ASC_GBTIME");
+ exit(1);
+ }
+
+ printf("%s:\n", file);
+ printf("resolution\t %d dpi\n", show_dpi);
+ printf("width\t\t %d\n", show_width);
+ printf("height\t\t %d\n",show_height);
+ printf("buffer length\t %d\n", show_blen);
+ printf("buffer timeout\t %d\n", show_btime);
+ }
+
+ return 0;
+}
diff --git a/usr.bin/sccs/Makefile b/usr.bin/sccs/Makefile
new file mode 100644
index 0000000..0ee9d1b
--- /dev/null
+++ b/usr.bin/sccs/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sccs
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sccs/PSD.doc/Makefile b/usr.bin/sccs/PSD.doc/Makefile
new file mode 100644
index 0000000..4d2ba7f
--- /dev/null
+++ b/usr.bin/sccs/PSD.doc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= psd/14.sccs
+SRCS= sccs.me
+MACROS= -me
+
+paper.ps: ${SRCS}
+ ${ROFF} ${SRCS} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/sccs/PSD.doc/sccs.me b/usr.bin/sccs/PSD.doc/sccs.me
new file mode 100644
index 0000000..ab598c2
--- /dev/null
+++ b/usr.bin/sccs/PSD.doc/sccs.me
@@ -0,0 +1,1608 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sccs.me 8.1 (Berkeley) 6/8/93
+.\"
+.eh '\fRPSD:14-%\fP''\fRAn Introduction to the Source Code Control System\fP'
+.oh '\fRAn Introduction to the Source Code Control System\fP''\fRPSD:14-%\fP'
+.ds S \s-1SCCS\s0
+.ds I \s-1SID\s0
+.nr bi 8n
+.ev 1 \" only for keeps
+.ss 16
+.ev
+.\".he '\*S Introduction''%'
+.+c
+.(l C
+.sz 14
+.b
+An Introduction to the
+Source Code Control System
+.sz
+.r
+.sp
+Eric Allman
+.i "Project Ingres"
+.i "University of California at Berkeley"
+.)l
+.sp 3
+.pp
+.(f
+This is version 1.21 of this document.
+It was last modified on 12/5/80.
+.)f
+This document gives a quick introduction
+to using the Source Code Control System
+(\*S).
+The presentation is geared to programmers
+who are more concerned with
+what
+to do to get a task done
+rather than how it works;
+for this reason some of the examples
+are not well explained.
+For details of what the magic options do,
+see the section on
+.q "Further Information" .
+.(l F
+This is a working document.
+Please send any comments or suggestions
+to eric@Berkeley.Edu.
+.)l
+.sh 1 "Introduction"
+.pp
+\*S is a source management system.
+Such a system maintains a record of versions of a system;
+a record is kept with each set of changes
+of what the changes are,
+why they were made,
+and who made them and when.
+Old versions can be recovered,
+and different versions can be maintained simultaneously.
+In projects with more than one person,
+\*S will insure that two people are not
+editing the same file at the same time.
+.pp
+All versions of your program,
+plus the log and other information,
+is kept in a file called the
+.q "s-file" .
+There are three major operations
+that can be performed on the s-file:
+.np
+Get a file for compilation (not for editing).
+This operation retrieves a version of the file
+from the s-file.
+By default, the latest version is retrieved.
+This file is intended for compilation, printing, or whatever;
+it is specifically NOT intended to be edited
+or changed in any way;
+any changes made to a file retrieved
+in this way will probably be lost.
+.np
+Get a file for editing.
+This operation also retrieves a version of the file
+from the s-file,
+but this file is intended to be edited and then
+incorporated back into the s-file.
+Only one person may be editing a file at one time.
+.np
+Merge a file back into the s-file.
+This is the companion operation to (2).
+A new version number is assigned,
+and comments are saved explaining why this change was made.
+.sh 1 "Learning the Lingo"
+.pp
+There are a number of terms that are worth learning
+before we go any farther.
+.sh 2 "S-file"
+.pp
+The s-file
+is a single file that holds all the different versions
+of your file.
+The s-file is stored in
+differential format;
+.i i.e. ,
+only the differences between versions are stored,
+rather than the entire text of the new version.
+This saves disk space
+and allows selective changes to be removed later.
+Also included in the s-file
+is some header information for each version,
+including the comments given by the person who
+created the version explaining why the changes were made.
+.sh 2 "Deltas"
+.pp
+Each set of changes to the s-file
+(which is approximately [but not exactly!] equivalent
+to a version of the file)
+is called a
+.i delta .
+Although technically a delta only includes the
+.i changes
+made,
+in practice
+it is usual for
+each delta to be made with respect to
+all the deltas that have occurred before\**.
+.(f
+\**This matches normal usage, where the previous changes are not saved
+at all,
+so all changes are automatically based on all other changes
+that have happened through history.
+.)f
+However,
+it is possible to get a version of the file
+that has selected deltas removed out of the middle
+of the list of changes \*-
+equivalent to removing your changes later.
+.sh 2 "\*I's (or, version numbers)"
+.pp
+A \*I
+(\*S Id)
+is a number that represents a delta.
+This is normally a two-part number
+consisting of a
+.q release
+number and a
+.q level
+number.
+Normally the release number stays the same,
+however,
+it is possible to move into a new release
+if some major change is being made.
+.pp
+Since all past deltas are normally applied,
+the \*I of the final delta applied
+can be used to represent a version number of the file
+as a whole.
+.sh 2 "Id keywords"
+.pp
+When you get a version of a file
+with intent to compile and install it
+(\c
+.i i.e. ,
+something other than edit it),
+some special keywords are expanded inline
+by \*S.
+These
+.i "Id Keywords"
+can be used to include the current version number
+or other information into the file.
+All id keywords are of the form
+.b % \c
+.i x \c
+.b % ,
+where
+.i x
+is an upper case letter.
+For example,
+.b %\&I\&%
+is the \*I of the latest delta applied,
+.b %\&W\&%
+includes the module name,
+\*I,
+and a mark that makes it findable by a program,
+and
+.b %\&G\&%
+is the date of the latest delta applied.
+There are many others,
+most of which are of dubious usefulness.
+.pp
+When you get a file for editing,
+the id keywords are not expanded;
+this is so that after you put them back in to the s-file,
+they will be expanded automatically on each new version.
+But notice: if you were to get them
+expanded accidently,
+then your file would appear to be the same version
+forever more,
+which would of course defeat the purpose.
+Also,
+if you should install a version of the program
+without expanding the id keywords,
+it will be impossible to tell what version it is
+(since all it will have is
+.q %\&W\&%
+or whatever).
+.sh 1 "Creating \*S Files"
+.pp
+To put source files
+into
+\*S
+format, run the following shell script from csh:
+.(b
+mkdir SCCS save
+foreach i (*.[ch])
+ sccs admin \-i$i $i
+ mv $i save/$i
+end
+.)b
+This will put the named files
+into s-files
+in the subdirectory
+.q SCCS
+The files will be removed from the current directory
+and hidden away in the directory
+.q save ,
+so the next thing you will probably want to do
+is to get all the files
+(described below).
+When you are convinced that
+\*S has correctly created the s-files,
+you should remove the directory
+.q save .
+.pp
+If you want to have id keywords in the files,
+it is best to put them in before you create the s-files.
+If you do not,
+.i admin
+will print
+.q "No Id Keywords (cm7)" ,
+which is a warning message only.
+.sh 1 "Getting Files for Compilation"
+.pp
+To get a copy of the latest version
+of a file,
+run
+.(b
+sccs get prog.c
+.)b
+\*S will respond:
+.(b
+1.1
+87 lines
+.)b
+meaning that version 1.1 was retrieved\**
+.(f
+\**Actually,
+the \*I of the final delta applied was 1.1.
+.)f
+and that it has 87 lines.
+The file
+.i prog.c
+will be created
+in the current directory.
+The file will be read-only
+to remind you that you are not
+supposed to change it.
+.pp
+This copy of the file
+should not be changed,
+since \*S is unable
+to merge the changes
+back into the s-file.
+If you do make changes,
+they will be lost the next time
+someone does a
+.i get .
+.sh 1 "Changing Files (or, Creating Deltas)"
+.sh 2 "Getting a copy to edit"
+.pp
+To edit a source file,
+you must first get it,
+requesting permission to edit it\**:
+.(f
+\**The
+.q "edit"
+command is equivalent to using the \-e
+flag to
+.i "get" ,
+as:
+.(l
+sccs get \-e prog.c
+.)l
+Keep this in mind when reading other documentation.
+.)f
+.(b
+sccs edit prog.c
+.)b
+The response will be the same as with
+.i get
+except that it will also say:
+.(b
+New delta 1.2
+.)b
+You then edit it,
+using a standard text editor:
+.(b
+vi prog.c
+.)b
+.sh 2 "Merging the changes back into the s-file"
+.pp
+When the desired changes are made,
+you can put your changes into the
+\*S
+file using the
+.i delta
+command:
+.(b
+sccs delta prog.c
+.)b
+.pp
+Delta will prompt you for
+.q "comments?"
+before it merges the changes in.
+At this prompt you should type a one-line description
+of what the changes mean
+(more lines can be entered by ending each line
+except the last with a backslash\**).
+.(f
+\**Yes, this is a stupid default.
+.)f
+.i Delta
+will then type:
+.(b
+1.2
+5 inserted
+3 deleted
+84 unchanged
+.)b
+saying that delta 1.2 was created,
+and it inserted five lines,
+removed three lines,
+and left 84 lines unchanged\**.
+.(f
+\**Changes to a line are counted as a line deleted
+and a line inserted.
+.)f
+The
+.i prog.c
+file will be removed;
+it can be retrieved
+using
+.i get .
+.sh 2 "When to make deltas"
+.pp
+It is probably unwise to make a delta
+before every recompilation or test;
+otherwise,
+you tend to get a lot of deltas with comments like
+.q "fixed compilation problem in previous delta"
+or
+.q "fixed botch in 1.3" .
+However,
+it is very important to delta everything
+before installing a module for general use.
+A good technique is to edit the files you need,
+make all necessary changes and tests,
+compiling and editing as often as necessary
+without making deltas.
+When you are satisfied that you have a working version,
+delta everything being edited,
+re-get them,
+and recompile everything.
+.sh 2 "What's going on: the info command"
+.pp
+To find out what files where being edited,
+you can use:
+.(b
+sccs info
+.)b
+to print out all the files being edited
+and other information such as the name of the user
+who did the edit.
+Also,
+the command:
+.(b
+sccs check
+.)b
+is nearly equivalent to the
+.i info
+command,
+except that it is silent if nothing is being edited,
+and returns non-zero exit status if anything is being edited;
+it can be used in an
+.q install
+entry in a makefile
+to abort the install
+if anything has not been properly deltaed.
+.pp
+If you know that everything being edited should be deltaed,
+you can use:
+.(b
+sccs delta \`sccs tell\`
+.)b
+The
+.i tell
+command is similar to
+.i info
+except that only the names of files being edited
+are output,
+one per line.
+.pp
+All of these commands take a
+.b \-b
+flag
+to ignore
+.q branches
+(alternate versions, described later)
+and the
+.b \-u
+flag to only give files being edited by you.
+The
+.b \-u
+flag takes an optional
+.i user
+argument,
+giving only files being edited by that user.
+For example,
+.(b
+sccs info \-ujohn
+.)b
+gives a listing of files being edited by john.
+.sh 2 "ID keywords"
+.pp
+Id keywords can be inserted into your file
+that will be expanded automatically by
+.i get .
+For example,
+a line such as:
+.(b
+static char SccsId[] = "%\&W\&%\et%\&G\&%";
+.)b
+will be replaced with something like:
+.(b
+static char SccsId[] = "@\&(#)prog.c 1.2 08/29/80";
+.)b
+This tells you
+the name and version
+of the source file
+and the time the delta was created.
+The string
+.q "@\&(#)"
+is a special string
+which signals the beginning
+of an
+\*S
+Id keyword.
+.sh 3 "The what command"
+.pp
+To find out what version of a program
+is being run,
+use:
+.(b
+sccs what prog.c /usr/bin/prog
+.)b
+which will print all strings
+it finds that
+begin with
+.q "@\&(#)" .
+This works on all types of files,
+including binaries and libraries.
+For example, the above command will output something like:
+.(b
+prog.c:
+ prog.c 1.2 08/29/80
+/usr/bin/prog:
+ prog.c 1.1 02/05/79
+.)b
+From this I can see
+that the source that I have in prog.c
+will not compile into the same version
+as the binary in /usr/bin/prog.
+.sh 3 "Where to put id keywords"
+.pp
+ID keywords can be inserted anywhere,
+including in comments,
+but
+Id Keywords that are compiled into the object module
+are especially useful,
+since it lets you find out what version of
+the object is being run,
+as well as the source.
+However,
+there is a cost:
+data space is used up to store
+the keywords,
+and on small address space machines
+this may be prohibitive.
+.pp
+When you put id keywords into header files,
+it is important that you assign them to different variables.
+For example, you might use:
+.(b
+static char AccessSid[] = "%\&W\&% %\&G\&%";
+.)b
+in the file
+.i access.h
+and:
+.(b
+static char OpsysSid[] = "%\&W\&% %\&G\&%";
+.)b
+in the file
+.i opsys.h .
+Otherwise,
+you will get compilation errors because
+.q SccsId
+is redefined.
+The problem with this is that if the header file
+is included by many modules that are loaded together,
+the version number of that header file is included
+in the object module many times;
+you may find it more to your taste
+to put id keywords in header files
+in comments.
+.sh 2 "Keeping \*I's consistent across files"
+.pp
+With some care,
+it is possible to keep the \*I's consistent
+in multi-file systems.
+The trick here is to always
+.i edit
+all files
+at once.
+The changes can then be made
+to whatever files are necessary
+and then all files
+(even those not changed)
+are redeltaed.
+This can be done fairly easily
+by just specifying the name of the directory
+that the \*S files are in:
+.(b
+sccs edit SCCS
+.)b
+which will
+.i edit
+all files in that directory.
+To make the delta, use:
+.(b
+sccs delta SCCS
+.)b
+You will be prompted for comments only once.
+.sh 2 "Creating new releases"
+.pp
+When you want to create a new release
+of a program,
+you can specify the release number you want to create
+on the
+.i edit
+command.
+For example:
+.(b
+sccs edit \-r2 prog.c
+.)b
+will cause the next delta to be in release two
+(that is,
+it will be numbered 2.1).
+Future deltas will automatically be in release two.
+To change the release number
+of an entire system,
+use:
+.(b
+sccs edit \-r2 SCCS
+.)b
+.sh 1 "Restoring Old Versions"
+.sh 2 "Reverting to old versions"
+.pp
+Suppose that after delta 1.2
+was stable
+you made and released a delta 1.3.
+But this introduced a bug,
+so you made a delta 1.4 to correct it.
+But 1.4 was still buggy,
+and you decided you wanted to go back
+to the old version.
+You could
+revert to delta 1.2
+by choosing the \*I in a get:
+.(b
+sccs get \-r1.2 prog.c
+.)b
+This will produce a version of
+.i prog.c
+that is delta 1.2
+that can be reinstalled so that work can proceed.
+.pp
+In some cases you don't know
+what the \*I of the delta you want is.
+However,
+you can revert to the version of the program
+that was running as of a certain date
+by using the
+.b \-c
+(cutoff) flag.
+For example,
+.(b
+sccs get \-c800722120000 prog.c
+.)b
+will retrieve whatever version was current
+as of July 22, 1980
+at 12:00 noon.
+Trailing components can be stripped off
+(defaulting to their highest legal value),
+and punctuation can be inserted in the obvious
+places;
+for example,
+the above line could be equivalently stated:
+.(b
+sccs get \-c"80/07/22 12:00:00" prog.c
+.)b
+.sh 2 "Selectively deleting old deltas"
+.pp
+Suppose that you later decided
+that you liked the changes in delta 1.4,
+but that delta 1.3 should be removed.
+You could do this by
+.i excluding
+delta 1.3:
+.(b
+sccs edit \-x1.3 prog.c
+.)b
+When delta 1.5 is made,
+it will include the changes made
+in delta 1.4,
+but will exclude the changes made
+in delta 1.3.
+You can exclude a range of deltas
+using a dash.
+For example,
+if you want to get rid of 1.3 and 1.4
+you can use:
+.(b
+sccs edit \-x1.3\-1.4 prog.c
+.)b
+which will exclude all deltas from 1.3 to 1.4.
+Alternatively,
+.(b
+sccs edit \-x1.3\-1 prog.c
+.)b
+will exclude a range of deltas
+from 1.3 to the current highest delta in release 1.
+.pp
+In certain cases when using
+.b \-x
+(or
+.b \-i ;
+see below)
+there will be conflicts
+between versions;
+for example, it may be necessary
+to both include and delete
+a particular line.
+If this happens,
+\*S always prints out a message
+telling the range of lines effected;
+these lines should then be examined very carefully
+to see if the version \*S got
+is ok.
+.pp
+Since each delta
+(in the sense of
+.q "a set of changes" )
+can be excluded at will,
+that this makes it most useful
+to put each semantically distinct change
+into its own delta.
+.sh 1 "Auditing Changes"
+.sh 2 "The prt command"
+.pp
+When you created a delta,
+you presumably gave a reason for the delta
+to the
+.q "comments?"
+prompt.
+To print out these comments later,
+use:
+.(b
+sccs prt prog.c
+.)b
+This will produce
+a report
+for each delta
+of the \*I,
+time and date of creation,
+user who created the delta,
+number of lines inserted, deleted, and unchanged,
+and the comments associated with the delta.
+For example, the output of the above command might be:
+.(b
+D 1.2 80/08/29 12:35:31 bill 2 1 00005/00003/00084
+removed "-q" option
+.sp \n(psu
+D 1.1 79/02/05 00:19:31 eric 1 0 00087/00000/00000
+date and time created 80/06/10 00:19:31 by eric
+.)b
+.sh 2 "Finding why lines were inserted"
+.pp
+To find out
+why you inserted lines,
+you can get a copy of the file
+with each line
+preceded by the \*I that created it:
+.(b
+sccs get \-m prog.c
+.)b
+You can then find out
+what this delta did
+by printing the comments using
+.i prt .
+.pp
+To find out what lines are associated with a particular delta
+(\c
+.i e.g. ,
+1.3),
+use:
+.(b
+sccs get \-m \-p prog.c \(bv grep \'^1.3\'
+.)b
+The
+.b \-p
+flag causes \*S to output the generated source
+to the standard output rather than to a file.
+.sh 2 "Finding what changes you have made"
+.pp
+When you are editing a file,
+you can find out what changes you have made using:
+.(b
+sccs diffs prog.c
+.)b
+Most of the ``diff'' flags can be used.
+To pass the
+.b \-c
+flag,
+use
+.b \-C .
+.pp
+To compare two versions that are in deltas,
+use:
+.(b
+sccs sccsdiff -r1.3 -r1.6 prog.c
+.)b
+to see the differences between delta 1.3 and delta 1.6.
+.sh 1 "Shorthand Notations"
+.pp
+There are several sequences of commands that get
+executed frequently.
+.i Sccs
+tries to make it easy to do these.
+.sh 2 "Delget"
+.pp
+A frequent requirement is to make a delta of some file
+and then get that file.
+This can be done by using:
+.(b
+sccs delget prog.c
+.)b
+which is entirely equivalent to using:
+.(b
+sccs delta prog.c
+sccs get prog.c
+.)b
+The
+.q deledit
+command is equivalent to
+.q delget
+except that the
+.q edit
+command is used
+instead of the
+.q get
+command.
+.sh 2 "Fix"
+.pp
+Frequently, there are small bugs
+in deltas,
+e.g., compilation errors,
+for which there is no reason to maintain an audit trail.
+To
+.i replace
+a delta, use:
+.(b
+sccs fix \-r1.4 prog.c
+.)b
+This will get a copy of delta 1.4 of prog.c for you to edit
+and then delete delta 1.4 from the \*S file.
+When you do a delta of prog.c,
+it will be delta 1.4 again.
+The \-r flag must be specified,
+and the delta that is specified must be a leaf delta,
+i.e., no other deltas may have been made subsequent
+to the creation of that delta.
+.sh 2 "Unedit"
+.pp
+If you found you edited a file
+that you did not want to edit,
+you can back out by using:
+.(b
+sccs unedit prog.c
+.)b
+.sh 2 "The \-d flag"
+.pp
+If you are working on a project
+where the \*S code is in a directory somewhere,
+you may be able to simplify things
+by using a shell alias.
+For example,
+the alias:
+.(b
+alias syssccs sccs \-d/usr/src
+.)b
+will allow you to issue commands such as:
+.(b
+syssccs edit cmd/who.c
+.)b
+which will look for the file
+.q "/usr/src/cmd/SCCS/who.c" .
+The file
+.q who.c
+will always be created in your current directory
+regardless of the value of the \-d flag.
+.sh 1 "Using \*S on a Project"
+.pp
+Working on a project with several people
+has its own set of special problems.
+The main problem occurs when two people
+modify a file at the same time.
+\*S prevents this by locking an s-file
+while it is being edited.
+.pp
+As a result,
+files should not be reserved for editing
+unless they are actually being edited at the time,
+since this will prevent other people on the project
+from making necessary changes.
+For example,
+a good scenario for working might be:
+.(b
+sccs edit a.c g.c t.c
+vi a.c g.c t.c
+# do testing of the (experimental) version
+sccs delget a.c g.c t.c
+sccs info
+# should respond "Nothing being edited"
+make install
+.)b
+.pp
+As a general rule,
+all source files should be deltaed
+before installing the program for general use.
+This will insure that it is possible
+to restore any version in use at any time.
+.sh 1 "Saving Yourself"
+.sh 2 "Recovering a munged edit file"
+.pp
+Sometimes you may find
+that you have destroyed or trashed
+a file that you were trying to edit\**.
+.(f
+\**Or given up and decided to start over.
+.)f
+Unfortunately,
+you can't just remove it
+and re-\c
+.i edit
+it;
+\*S keeps track
+of the fact
+that someone is trying to edit it,
+so it won't let you do it again.
+Neither can you just get it using
+.i get ,
+since that would expand the Id keywords.
+Instead,
+you can say:
+.(b
+sccs get \-k prog.c
+.)b
+This will not expand the Id keywords,
+so it is safe to do a delta
+with it.
+.pp
+Alternately,
+you can
+.i unedit
+and
+.i edit
+the file.
+.sh 2 "Restoring the s-file"
+.pp
+In particularly bad circumstances,
+the \*S file itself
+may get munged.
+The most common way this happens
+is that it gets edited.
+Since \*S keeps a checksum,
+you will get errors every time you read the file.
+To fix this checksum, use:
+.(b
+sccs admin \-z prog.c
+.)b
+.sh 1 "Using the Admin Command"
+.pp
+There are a number of parameters that can be set
+using the
+.i admin
+command.
+The most interesting of these are flags.
+Flags can be added by using the
+.b \-f
+flag.
+For example:
+.(b
+sccs admin \-fd1 prog.c
+.)b
+sets the
+.q d
+flag to the value
+.q 1 .
+This flag can be deleted by using:
+.(b
+sccs admin \-dd prog.c
+.)b
+The most useful flags are:
+.nr ii 7n
+.ip "b"
+Allow branches to be made using the
+\-b
+flag to
+.i edit .
+.ip "d\fISID\fP"
+Default \*I to be used on a
+.i get
+or
+.i edit .
+If this is just a release number
+it constrains the
+version
+to a particular release only.
+.ip "i"
+Give a fatal error
+if there are no Id Keywords in a file.
+This is useful to guarantee that a version of the
+file does not get merged into the s-file
+that has the Id Keywords inserted as constants
+instead of internal forms.
+.ip "y"
+The
+.q type
+of the module.
+Actually,
+the value of this flag is unused by \*S
+except that it replaces the
+.b %\&Y\&%
+keyword.
+.pp
+The
+.b \-t\fIfile\fR
+flag can be used
+to store descriptive text
+from
+.i file .
+This descriptive text might be the documentation
+or a design and implementation document.
+Using the
+.b \-t
+flag insures that if the \*S file is sent,
+the documentation will be sent also.
+If
+.i file
+is omitted,
+the descriptive text is deleted.
+To see the descriptive text,
+use
+.q "prt \-t" .
+.pp
+The
+.i admin
+command can be used safely
+any number of times on files.
+A file need not be gotten
+for
+.i admin
+to work.
+.sh 1 "Maintaining Different Versions (Branches)"
+.pp
+Sometimes it is convenient
+to maintain an experimental version of a program
+for an extended period
+while normal maintenance continues
+on the version in production.
+This can be done using a
+.q branch.
+Normally deltas continue in a straight line,
+each depending on the delta before.
+Creating a branch
+.q "forks off"
+a version of the program.
+.pp
+The ability to create branches
+must be enabled in advance using:
+.(b
+sccs admin \-fb prog.c
+.)b
+The
+.b \-fb
+flag can be specified when the
+\*S file is first created.
+.sh 2 "Creating a branch"
+.pp
+To create a branch, use:
+.(b
+sccs edit \-b prog.c
+.)b
+This will create a branch
+with (for example) \*I 1.5.1.1.
+The deltas for this version
+will be numbered
+1.5.1.\c
+.i n .
+.sh 2 "Getting from a branch"
+.pp
+Deltas in a branch are normally not included
+when you do a get.
+To get these versions,
+you will have to say:
+.(b
+sccs get \-r1.5.1 prog.c
+.)b
+.sh 2 "Merging a branch back into the main trunk"
+.pp
+At some point you will have finished the experiment,
+and if it was successful
+you will want to incorporate it into the release version.
+But in the meantime
+someone may have created a delta 1.6
+that you don't want to lose.
+The commands:
+.(b
+sccs edit \-i1.5.1.1\-1.5.1 prog.c
+sccs delta prog.c
+.)b
+will merge all of your changes
+into the release system.
+If some of the changes conflict,
+get will print an error;
+the generated result
+should be carefully examined
+before the delta is made.
+.sh 2 "A more detailed example"
+.pp
+The following technique might be used
+to maintain a different version of a program.
+First,
+create a directory to contain the new version:
+.(b
+mkdir ../newxyz
+cd ../newxyz
+.)b
+Edit a copy of the program
+on a branch:
+.(b
+sccs \-d../xyz edit prog.c
+.)b
+When using the old version,
+be sure to use the
+.b \-b
+flag to info, check, tell, and clean
+to avoid confusion.
+For example, use:
+.(b
+sccs info \-b
+.)b
+when in the directory
+.q xyz .
+.pp
+If you want to save a copy of the program
+(still on the branch)
+back in the s-file,
+you can use:
+.(b
+sccs -d../xyz deledit prog.c
+.)b
+which will do a delta on the branch
+and reedit it for you.
+.pp
+When the experiment is complete, merge it back into the s-file
+using delta:
+.(b
+sccs -d../xyz delta prog.c
+.)b
+At this point you must decide whether this version
+should be merged back into the trunk
+(\c
+.i i.e.
+the default version),
+which may have undergone changes.
+If so, it can be merged using the
+.b \-i
+flag to
+.i edit
+as described above.
+.sh 2 "A warning"
+.pp
+Branches should be kept to a minimum.
+After the first branch from the trunk,
+\*I's are assigned rather haphazardly,
+and the structure gets complex fast.
+.sh 1 "Using \*S with Make"
+.pp
+\*S and make can be made to work together
+with a little care.
+A few sample makefiles
+for common applications are shown.
+.pp
+There are a few basic entries that every makefile
+ought to have.
+These are:
+.nr ii 1i
+.ip a.out
+(or whatever the makefile generates.)
+This entry regenerates whatever this makefile is
+supposed to regenerate.
+If the makefile regenerates many things,
+this should be called
+.q all
+and should in turn
+have dependencies on everything
+the makefile can generate.
+.ip install
+Moves the objects to the final
+resting place,
+doing any special
+.i chmod 's
+or
+.i ranlib 's
+as appropriate.
+.ip sources
+Creates all the source files from \*S files.
+.ip clean
+Removes all files from the current directory
+that can be regenerated from \*S files.
+.ip print
+Prints the contents of the directory.
+.lp
+The examples shown below are only partial examples,
+and may omit some of these entries
+when they are deemed to be obvious.
+.pp
+The
+.i clean
+entry should not remove files that can be
+regenerated from the \*S files.
+It is sufficiently important to have the
+source files around at all times
+that the only time they should be removed
+is when the directory is being mothballed.
+To do this, the command:
+.(b
+sccs clean
+.)b
+can be used.
+This will remove all files for which an s-file
+exists,
+but which is not being edited.
+.sh 2 "To maintain single programs"
+.pp
+Frequently there are directories with several
+largely unrelated programs
+(such as simple commands).
+These can be put into a single makefile:
+.(b
+LDFLAGS= \-i \-s
+.sp \n(psu
+prog: prog.o
+ $(CC) $(LDFLAGS) \-o prog prog.o
+prog.o: prog.c prog.h
+.sp \n(psu
+example: example.o
+ $(CC) $(LDFLAGS) \-o example example.o
+example.o: example.c
+.sp \n(psu
+\&.DEFAULT:
+ sccs get $<
+.)b
+The trick here
+is that the .DEFAULT rule
+is called every time
+something is needed
+that does not exist,
+and no other rule exists to make it.
+The explicit dependency of the
+.b \&.o
+file on the
+.b \&.c
+file is important.
+Another way of doing the same thing is:
+.(b
+SRCS= prog.c prog.h example.c
+.sp \n(psu
+LDFLAGS= \-i \-s
+.sp \n(psu
+prog: prog.o
+ $(CC) $(LDFLAGS) \-o prog prog.o
+prog.o: prog.h
+.sp \n(psu
+example: example.o
+ $(CC) $(LDFLAGS) \-o example example.o
+.sp \n(psu
+sources: $(SRCS)
+$(SRCS):
+ sccs get $@
+.)b
+There are a couple of advantages to this approach:
+(1) the explicit dependencies of the .o on the .c files are
+not needed,
+(2) there is an entry called "sources" so if you want to get
+all the sources you can just say
+.q "make sources" ,
+and
+(3) the makefile is less likely to do confusing things
+since it won't try to
+.i get
+things that do not exist.
+.sh 2 "To maintain a library"
+.pp
+Libraries that are largely static
+are best updated using explicit commands,
+since
+.i make
+doesn't know about updating them properly.
+However,
+libraries that are in the process of being developed
+can be handled quite adequately.
+The problem is that the .o files
+have to be kept out of the library
+as well as in the library.
+.(b
+# configuration information
+OBJS= a.o b.o c.o d.o
+SRCS= a.c b.c c.c d.s x.h y.h z.h
+TARG= /usr/lib
+.sp \n(psu
+# programs
+GET= sccs get
+REL=
+AR= \-ar
+RANLIB= ranlib
+.sp \n(psu
+lib.a: $(OBJS)
+ $(AR) rvu lib.a $(OBJS)
+ $(RANLIB) lib.a
+.sp \n(psu
+install: lib.a
+ sccs check
+ cp lib.a $(TARG)/lib.a
+ $(RANLIB) $(TARG)/lib.a
+.sp \n(psu
+sources: $(SRCS)
+$(SRCS):
+ $(GET) $(REL) $@
+.sp \n(psu
+print: sources
+ pr *.h *.[cs]
+clean:
+ rm \-f *.o
+ rm \-f core a.out $(LIB)
+.)b
+.pp
+The
+.q "$(REL)"
+in the get
+can be used to get old versions
+easily; for example:
+.(b
+make b.o REL=\-r1.3
+.)b
+.pp
+The
+.i install
+entry includes the line
+.q "sccs check"
+before anything else.
+This guarantees that all the s-files
+are up to date
+(\c
+.i i.e. ,
+nothing is being edited),
+and will abort the
+.i make
+if this condition is not met.
+.sh 2 "To maintain a large program"
+.(b
+OBJS= a.o b.o c.o d.o
+SRCS= a.c b.c c.y d.s x.h y.h z.h
+.sp \n(psu
+GET= sccs get
+REL=
+.sp \n(psu
+a.out: $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) $(LIBS)
+.sp \n(psu
+sources: $(SRCS)
+$(SRCS):
+ $(GET) $(REL) $@
+.)b
+(The
+.i print
+and
+.i clean
+entries are identical to the previous case.)
+This makefile requires copies of the source and object files
+to be kept during development.
+It is probably also wise to include lines of the form:
+.(b
+a.o: x.h y.h
+b.o: z.h
+c.o: x.h y.h z.h
+z.h: x.h
+.)b
+so that modules will be recompiled
+if header files change.
+.pp
+Since
+.i make
+does not do transitive closure on dependencies,
+you may find in some makefiles lines like:
+.(b
+z.h: x.h
+ touch z.h
+.)b
+This would be used in cases where file z.h
+has a line:
+.(b
+#include "x.h"
+.)b
+in order to bring the mod date of z.h in line
+with the mod date of x.h.
+When you have a makefile such as above,
+the
+.i touch
+command can be removed completely;
+the equivalent effect will be achieved
+by doing an automatic
+.i get
+on z.h.
+.sh 1 "Further Information"
+.pp
+The
+.i "SCCS/PWB User's Manual"
+gives a deeper description
+of how to use \*S.
+Of particular interest
+are the numbering of branches,
+the l-file,
+which gives a description of what deltas were used on a get,
+and certain other \*S commands.
+.pp
+The \*S manual pages
+are a good last resort.
+These should be read by software managers
+and by people who want to know
+everything about everything.
+.pp
+Both of these documents were written without the
+.i sccs
+front end in mind,
+so most of the examples are slightly different from those
+in this document.
+.bp
+.sz 12
+.ce
+.b "Quick Reference"
+.sz
+.sp 2
+.sh 1 Commands 1
+.pp
+The following commands should all be preceded with
+.q sccs .
+This list is not exhaustive;
+for more options see
+.i "Further Information" .
+.ip get 9n
+Gets files for compilation (not for editing).
+Id keywords are expanded.
+.ba 9n
+.nr ii 8n
+.ip \-r\fI\*I\fP
+Version to get.
+.ip \-p
+Send to standard output rather than to the actual file.
+.ip \-k
+Don't expand id keywords.
+.ip \-i\fIlist\fP
+List of deltas to include.
+.ip \-x\fIlist\fP
+List of deltas to exclude.
+.ip \-m
+Precede each line with \*I of creating delta.
+.ip \-c\fIdate\fP
+Don't apply any deltas created after
+.i date.
+.ba
+.ip edit 9n
+Gets files for editing.
+Id keywords are not expanded.
+Should be matched with a
+.i delta
+command.
+.ba 9n
+.nr ii 8n
+.ip \-r\fI\*I\fP
+Same as
+.i get .
+If
+.i \*I
+specifies a release that does not yet exist,
+the highest numbered delta is retrieved
+and the new delta is numbered with
+.i \*I .
+.ip \-b
+Create a branch.
+.ip \-i\fIlist\fP
+Same as
+.i get .
+.ip \-x\fIlist\fP
+Same as
+.i get .
+.ba
+.ip delta 9n
+Merge a file gotten using
+.i edit
+back into the s-file.
+Collect comments about why this delta was made.
+.ip unedit 9n
+Remove a file that has been edited previously
+without merging the changes into the s-file.
+.ip prt 9n
+Produce a report of changes.
+.ba 9n
+.nr ii 5n
+.ip \-t
+Print the descriptive text.
+.ip \-e
+Print (nearly) everything.
+.ba
+.ip info 9n
+Give a list of all files being edited.
+.ba 9n
+.nr ii 5n
+.ip \-b
+Ignore branches.
+.ip \-u[\fIuser\fP]
+Ignore files not being edited by
+.i user .
+.ba
+.ip check 9n
+Same as
+.i info ,
+except that nothing is printed if nothing is being edited
+and exit status is returned.
+.ip tell 9n
+Same as
+.i info ,
+except that one line is produced per file being edited containing
+only the file name.
+.ip clean 9n
+Remove all files that can be regenerated from the
+s-file.
+.ip what 9n
+Find and print id keywords.
+.ip admin 9n
+Create or set parameters on s-files.
+.ba 9n
+.nr ii 8n
+.ip \-i\fIfile\fP
+Create, using
+.i file
+as the initial contents.
+.ip \-z
+Rebuild the checksum in case
+the file has been trashed.
+.ip \-f\fIflag\fP
+Turn on the
+.i flag .
+.ip \-d\fIflag\fP
+Turn off (delete) the
+.i flag .
+.ip \-t\fIfile\fP
+Replace the descriptive text
+in the s-file with the contents of
+.i file .
+If
+.i file
+is omitted,
+the text is deleted.
+Useful for storing documentation
+or
+.q "design & implementation"
+documents to insure they get distributed with the
+s-file.
+.lp
+Useful flags are:
+.ip b
+Allow branches to be made using the \-b flag to
+.i edit.
+.ip d\fI\*I\fP
+Default \*I to be used
+on a
+.i get
+or
+.i edit .
+.ip i
+Cause
+.q "No Id Keywords"
+error message
+to be a fatal error rather than a warning.
+.ip t
+The module
+.q type ;
+the value of this flag replaces the
+.b %\&Y\&%
+keyword.
+.ba
+.ip fix 9n
+Remove a delta and reedit it.
+.ip delget 9n
+Do a
+.i delta
+followed by a
+.i get .
+.ip deledit 9n
+Do a
+.i delta
+followed by an
+.i edit .
+.sh 1 "Id Keywords"
+.nr ii 6n
+.ip "%\&Z\&%"
+Expands to
+.q @\&(#)
+for the
+.i what
+command to find.
+.ip "%\&M\&%"
+The current module name,
+.i e.g.,
+.q prog.c .
+.ip "%\&I\&%"
+The highest \*I applied.
+.ip "%\&W\&%"
+A shorthand for
+.q "%\&Z\&%%\&M\&% <tab> %\&I\&%" .
+.ip "%\&G\&%"
+The date of the delta
+corresponding to the
+.q "%\&I\&%"
+keyword.
+.ip "%\&R\&%"
+The current release number,
+.i i.e. ,
+the first component of the
+.q "%\&I\&%"
+keyword.
+.ip "%\&Y\&%"
+Replaced by the value of the
+.b t
+flag
+(set by
+.i admin ).
diff --git a/usr.bin/sccs/PSD.doc/spell.ok b/usr.bin/sccs/PSD.doc/spell.ok
new file mode 100644
index 0000000..fb2fe24
--- /dev/null
+++ b/usr.bin/sccs/PSD.doc/spell.ok
@@ -0,0 +1,77 @@
+AccessSid
+Admin
+Allman
+Berkeley.Edu
+Delget
+Ingres
+LDFLAGS
+LIB
+LIBS
+OBJS
+OpsysSid
+PS1:14
+PWB
+REL
+SCCS
+SID
+SRCS
+Sccs
+SccsId
+System''PS1:14
+TARG
+a.c
+a.o
+a.out
+access.h
+admin
+b.c
+b.o
+backslash
+bi
+c.c
+c.o
+c.y
+ch
+cm7
+cmd
+cs
+d.o
+d.s
+deledit
+delget
+eric
+example.c
+example.o
+fb
+fd1
+foreach
+g.c
+info
+inline
+john
+lib
+lib.a
+makefile
+makefiles
+mod
+mothballed
+newxyz
+ok
+opsys.h
+prog
+prog.c
+prog.h
+prog.o
+prt
+rvu
+sccs
+sccsdiff
+src
+syssccs
+t.c
+ujohn
+who.c
+x.h
+xyz
+y.h
+z.h
diff --git a/usr.bin/sccs/pathnames.h b/usr.bin/sccs/pathnames.h
new file mode 100644
index 0000000..4da6874
--- /dev/null
+++ b/usr.bin/sccs/pathnames.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_SCCSADMIN "/usr/local/bin/admin"
+#define _PATH_SCCSBDIFF "/usr/local/bin/bdiff"
+#define _PATH_SCCSCOMB "/usr/local/bin/comb"
+#define _PATH_SCCSDELTA "/usr/local/bin/delta"
+#define _PATH_SCCSDIFF "/usr/local/bin/sccsdiff"
+#define _PATH_SCCSGET "/usr/local/bin/get"
+#define _PATH_SCCSHELP "/usr/local/bin/help"
+#define _PATH_SCCSPRS "/usr/local/bin/prs"
+#define _PATH_SCCSPRT "/usr/local/bin/prt"
+#define _PATH_SCCSRMDEL "/usr/local/bin/rmdel"
+#define _PATH_SCCSVAL "/usr/local/bin/val"
+#define _PATH_SCCSWHAT "/usr/local/bin/what"
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/sccsXXXXX"
diff --git a/usr.bin/sccs/sccs.1 b/usr.bin/sccs/sccs.1
new file mode 100644
index 0000000..7f4990c
--- /dev/null
+++ b/usr.bin/sccs/sccs.1
@@ -0,0 +1,398 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sccs.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt SCCS 1
+.Os BSD 4.2
+.Sh NAME
+.Nm sccs
+.Nd front end for the
+.Li SCCS
+subsystem
+.Sh SYNOPSIS
+.Nm sccs
+.Op Fl r
+.Op Fl d Ar path
+.Op Fl p Ar path
+.Ar command
+.Op flags
+.Op Ar
+.Sh DESCRIPTION
+.Nm Sccs
+is a front end to the
+.Li SCCS
+programs
+that
+helps them mesh more cleanly
+with
+the rest of UNIX.
+It
+also includes the capability to run
+.Dq set user id
+to another user
+to
+provide additional protection.
+.Pp
+Basically,
+.Nm sccs
+runs the command with the specified
+.Ar flags
+and
+.Ar args .
+Each argument is normally modified to be prepended with
+.Dq Li SCCS/s. .
+.Pp
+Flags to be interpreted by the
+.Nm sccs
+program must be before the
+.Ar command
+argument.
+Flags to be passed to the actual
+.Li SCCS
+program must come after the
+.Ar command
+argument.
+These flags are specific to the command and
+are discussed in the documentation for that command.
+.Pp
+Besides the usual
+.Li SCCS
+commands,
+several
+.Dq pseudo-commands
+can be issued.
+These are:
+.Bl -tag -width deledit
+.It Cm edit
+Equivalent
+to
+.Dq Li get \-e .
+.It Cm delget
+Perform a delta on the named files and
+then get new versions.
+The new versions will have id keywords expanded, and
+will not be editable.
+The
+.Fl m ,
+.Fl p ,
+.Fl r ,
+.Fl s ,
+and
+.Fl y
+flags will be passed to
+.Nm delta ,
+and the
+.Fl b,
+.Fl c ,
+.Fl e ,
+.Fl i ,
+.Fl k ,
+.Fl l ,
+.Fl s ,
+.\" anybody who has a bad xterm which is almost anyone
+and
+.Fl x
+flags will be passed to get.
+.It Cm deledit
+Equivalent
+to
+.Nm delget
+except that the
+.Nm get
+phase includes the
+.Fl e
+flag.
+This
+option is useful for making a
+.Em checkpoint
+of your current editing phase. The same flags will be passed to delta
+as described above, and
+all the flags listed for
+.om get
+above except
+.Fl e
+and
+.Fl k
+are
+passed to
+.Nm edit .
+.It Cm create
+Creates
+an
+.Li SCCS
+file ,
+taking
+the initial contents from the file of the same name.
+Any
+flags to
+.Nm admin
+are accepted. If the creation is successful,
+the files are renamed with a comma on the front.
+These should be removed when you are convinced that the
+.Li SCCS
+files
+have been created successfully.
+.It Cm fix
+Must
+be followed by a
+.Fl r
+flag.
+This command essentially removes the named delta, but
+leaves you with a copy of the delta
+with the changes that were in it. It
+is useful for fixing small compiler bugs, etc.
+Since it doesn't leave audit trails, it should be used carefully.
+.It Cm clean
+This routine removes everything from the current directory
+that can be recreated from SCCS files.
+It will not remove any files being edited.
+If the
+.Fl b
+flag is given, branches are ignored in the determination of
+whether they are being edited; this
+is dangerous if you are keeping the branches in the
+same directory.
+.It Cm unedit
+This
+is the opposite of an
+.Nm edit
+or
+a
+.Dq Li get \-e .
+It should be used with extreme caution, since
+any changes you made since the get will be irretrievably lost.
+.It Cm info
+Gives a listing of all files being edited.
+If the
+.Fl b
+flag
+is given, branches (i.e.,
+.Li SID Ns \&\'s
+with two or fewer components)
+are ignored. If the
+.Fl u
+flag is given (with an optional argument) then
+only files being edited by you (or the named user) are listed.
+.It Cm check
+Like
+.Nm info
+except that nothing is printed if nothing is being edited, and
+a non-zero exit status is returned if anything is being edited.
+The intent is to have this included in an
+.Em install
+entry in a makefile to insure that everything is included into the
+.Li SCCS
+file before a version is installed.
+.It Cm tell
+Gives a newline-separated list of the files being edited
+on the standard output. Takes the
+.Fl b
+and
+.Fl u
+flags like
+.Nm info
+and
+.Nm check .
+.It Cm diffs
+Gives a
+.Nm diff
+listing between the current version of the
+program(s) you have out for editing and the versions in
+.Li SCCS
+format.
+The
+.Fl r ,
+.Fl c ,
+.Fl i ,
+.Fl x ,
+and
+.Fl t
+flags are passed to
+.if n \{\
+. br
+.\}
+.Nm get ;
+the
+.Fl l ,
+.Fl s ,
+.Fl e ,
+.Fl f ,
+.Fl h ,
+and
+.Fl b
+options are passed to
+.if n \{\
+. br
+.\}
+.Nm diff .
+The
+.Fl C
+flag is passed to
+.Nm diff
+as
+.Fl c .
+.It Cm print
+This command prints out verbose information
+about the named files.
+.Pp
+.It Fl r
+Runs
+.Nm sccs
+as the real user rather than as whatever effective user
+.Nm sccs
+is
+.Dq Li set user id
+to.
+.It Fl d
+Specifies a root directory for the
+.Li SCCS
+files.
+The default is the current directory.
+If environment variable
+.Ev PROJECT
+is set,
+it will be used to determine the
+.Fl d
+flag.
+.It Fl p
+Defines the pathname of the directory in which the
+.Li SCCS
+files will be found;
+.Dq Li SCCS
+is the default.
+The
+.Fl p
+flag
+differs from the
+.Fl d
+flag
+in that the
+.Fl d
+argument is prepended to the entire pathname and the
+.Fl p
+argument is inserted before the final component of the pathname.
+For example,
+.Dq Li sccs \-d/x \-py get a/b
+will convert to
+.Dq Li get /x/a/y/s.b .
+The intent here is to create aliases such as
+.Dq Li alias syssccs sccs -d/usr/src
+which
+will be used as
+.Dq Li syssccs get cmd/who.c .
+.Pp
+Certain
+commands (such as
+.Nm admin )
+cannot be run
+.Dq Li set user id
+by all users, since this would allow anyone to change the authorizations.
+These commands are always run as the real user.
+.Sh EXAMPLES
+To get a file for editing,
+edit it,
+and produce a new delta:
+.Pp
+.Dl sccs get \-e file.c
+.Dl ex file.c
+.Dl sccs delta file.c
+.Pp
+To get a file from another directory:
+.Pp
+.Dl sccs \-p/usr/src/sccs/s. get cc.c
+.Pp
+or
+.Pp
+.Dl sccs get /usr/src/sccs/s.cc.c
+.Pp
+To make a delta of a large number of files
+in the current directory:
+.Pp
+.Dl sccs delta *.c
+.Pp
+To get a list of files being edited that are not on branches:
+.Pp
+.Dl sccs info \-b
+.Pp
+To delta everything being edited by you:
+.Pp
+.Dl sccs delta \`sccs tell \-u\`
+.Pp
+In a makefile, to get source files
+from an
+.Li SCCS
+file if it does not already exist:
+.Pp
+.Dl SRCS = <list of source files>
+.Dl $(SRCS):
+.Dl \&\tsccs get $(REL) $@
+.Sh ENVIRONMENT
+.Bl -tag -width Ar
+.It Ev PROJECT
+The PROJECT environment variable is checked by the
+.Fl d
+flag. If
+it begins with a slash, it is taken directly; otherwise,
+the home directory of a user of that name is
+examined for a subdirectory
+.Dq Li src
+or
+.Dq Li source .
+If such a directory is found, it is used.
+.El
+.Sh SEE ALSO
+.Xr what 1
+.Xr admin SCCS ,
+.Xr chghist SCCS ,
+.Xr comb SCCS ,
+.Xr delta SCCS ,
+.Xr get SCCS ,
+.Xr help SCCS ,
+.Xr prt SCCS ,
+.Xr rmdel SCCS ,
+.Xr sccsdiff SCCS ,
+.Rs
+.%A Eric Allman
+.%T "An Introduction to the Source Code Control System"
+.Re
+.Sh HISTORY
+The
+.Nm sccs
+command
+appeared in
+.Bx 4.3 .
+.Sh BUGS
+It should be able to take directory arguments on pseudo-commands
+like the
+.Li SCCS
+commands do.
diff --git a/usr.bin/sccs/sccs.c b/usr.bin/sccs/sccs.c
new file mode 100644
index 0000000..2dfd76d
--- /dev/null
+++ b/usr.bin/sccs/sccs.c
@@ -0,0 +1,1621 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)sccs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include <signal.h>
+#include <sysexits.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include "pathnames.h"
+
+/*
+** SCCS.C -- human-oriented front end to the SCCS system.
+**
+** Without trying to add any functionality to speak of, this
+** program tries to make SCCS a little more accessible to human
+** types. The main thing it does is automatically put the
+** string "SCCS/s." on the front of names. Also, it has a
+** couple of things that are designed to shorten frequent
+** combinations, e.g., "delget" which expands to a "delta"
+** and a "get".
+**
+** This program can also function as a setuid front end.
+** To do this, you should copy the source, renaming it to
+** whatever you want, e.g., "syssccs". Change any defaults
+** in the program (e.g., syssccs might default -d to
+** "/usr/src/sys"). Then recompile and put the result
+** as setuid to whomever you want. In this mode, sccs
+** knows to not run setuid for certain programs in order
+** to preserve security, and so forth.
+**
+** Usage:
+** sccs [flags] command [args]
+**
+** Flags:
+** -d<dir> <dir> represents a directory to search
+** out of. It should be a full pathname
+** for general usage. E.g., if <dir> is
+** "/usr/src/sys", then a reference to the
+** file "dev/bio.c" becomes a reference to
+** "/usr/src/sys/dev/bio.c".
+** -p<path> prepends <path> to the final component
+** of the pathname. By default, this is
+** "SCCS". For example, in the -d example
+** above, the path then gets modified to
+** "/usr/src/sys/dev/SCCS/s.bio.c". In
+** more common usage (without the -d flag),
+** "prog.c" would get modified to
+** "SCCS/s.prog.c". In both cases, the
+** "s." gets automatically prepended.
+** -r run as the real user.
+**
+** Commands:
+** admin,
+** get,
+** delta,
+** rmdel,
+** cdc,
+** etc. Straight out of SCCS; only difference
+** is that pathnames get modified as
+** described above.
+** enter Front end doing "sccs admin -i<name> <name>"
+** create Macro for "enter" followed by "get".
+** edit Macro for "get -e".
+** unedit Removes a file being edited, knowing
+** about p-files, etc.
+** delget Macro for "delta" followed by "get".
+** deledit Macro for "delta" followed by "get -e".
+** branch Macro for "get -b -e", followed by "delta
+** -s -n", followd by "get -e -t -g".
+** diffs "diff" the specified version of files
+** and the checked-out version.
+** print Macro for "prs -e" followed by "get -p -m".
+** tell List what files are being edited.
+** info Print information about files being edited.
+** clean Remove all files that can be
+** regenerated from SCCS files.
+** check Like info, but return exit status, for
+** use in makefiles.
+** fix Remove a top delta & reedit, but save
+** the previous changes in that delta.
+**
+** Compilation Flags:
+** UIDUSER -- determine who the user is by looking at the
+** uid rather than the login name -- for machines
+** where SCCS gets the user in this way.
+** SCCSDIR -- if defined, forces the -d flag to take on
+** this value. This is so that the setuid
+** aspects of this program cannot be abused.
+** This flag also disables the -p flag.
+** SCCSPATH -- the default for the -p flag.
+** MYNAME -- the title this program should print when it
+** gives error messages.
+**
+** Compilation Instructions:
+** cc -O -n -s sccs.c
+** The flags listed above can be -D defined to simplify
+** recompilation for variant versions.
+**
+** Author:
+** Eric Allman, UCB/INGRES
+** Copyright 1980 Regents of the University of California
+*/
+
+
+/******************* Configuration Information ********************/
+
+# ifndef SCCSPATH
+# define SCCSPATH "SCCS" /* pathname in which to find s-files */
+# endif NOT SCCSPATH
+
+# ifndef MYNAME
+# define MYNAME "sccs" /* name used for printing errors */
+# endif NOT MYNAME
+
+/**************** End of Configuration Information ****************/
+
+typedef char bool;
+# define TRUE 1
+# define FALSE 0
+
+# define bitset(bit, word) ((bool) ((bit) & (word)))
+
+struct sccsprog
+{
+ char *sccsname; /* name of SCCS routine */
+ short sccsoper; /* opcode, see below */
+ short sccsflags; /* flags, see below */
+ char *sccspath; /* pathname of binary implementing */
+};
+
+/* values for sccsoper */
+# define PROG 0 /* call a program */
+# define CMACRO 1 /* command substitution macro */
+# define FIX 2 /* fix a delta */
+# define CLEAN 3 /* clean out recreatable files */
+# define UNEDIT 4 /* unedit a file */
+# define SHELL 5 /* call a shell file (like PROG) */
+# define DIFFS 6 /* diff between sccs & file out */
+# define DODIFF 7 /* internal call to diff program */
+# define ENTER 8 /* enter new files */
+
+/* bits for sccsflags */
+# define NO_SDOT 0001 /* no s. on front of args */
+# define REALUSER 0002 /* protected (e.g., admin) */
+
+/* modes for the "clean", "info", "check" ops */
+# define CLEANC 0 /* clean command */
+# define INFOC 1 /* info command */
+# define CHECKC 2 /* check command */
+# define TELLC 3 /* give list of files being edited */
+
+/*
+** Description of commands known to this program.
+** First argument puts the command into a class. Second arg is
+** info regarding treatment of this command. Third arg is a
+** list of flags this command accepts from macros, etc. Fourth
+** arg is the pathname of the implementing program, or the
+** macro definition, or the arg to a sub-algorithm.
+*/
+
+struct sccsprog SccsProg[] = {
+ "admin", PROG, REALUSER, _PATH_SCCSADMIN,
+ "cdc", PROG, 0, _PATH_SCCSRMDEL,
+ "comb", PROG, 0, _PATH_SCCSCOMB,
+ "delta", PROG, 0, _PATH_SCCSDELTA,
+ "get", PROG, 0, _PATH_SCCSGET,
+ "help", PROG, NO_SDOT, _PATH_SCCSHELP,
+ "prs", PROG, 0, _PATH_SCCSPRS,
+ "prt", PROG, 0, _PATH_SCCSPRT,
+ "rmdel", PROG, REALUSER, _PATH_SCCSRMDEL,
+ "val", PROG, 0, _PATH_SCCSVAL,
+ "what", PROG, NO_SDOT, _PATH_SCCSWHAT,
+ "sccsdiff", SHELL, REALUSER, _PATH_SCCSDIFF,
+ "edit", CMACRO, NO_SDOT, "get -e",
+ "delget", CMACRO, NO_SDOT, "delta:mysrp/get:ixbeskcl -t",
+ "deledit", CMACRO, NO_SDOT,
+ "delta:mysrp -n/get:ixbskcl -e -t -g",
+ "fix", FIX, NO_SDOT, NULL,
+ "clean", CLEAN, REALUSER|NO_SDOT,
+ (char *) CLEANC,
+ "info", CLEAN, REALUSER|NO_SDOT,
+ (char *) INFOC,
+ "check", CLEAN, REALUSER|NO_SDOT,
+ (char *) CHECKC,
+ "tell", CLEAN, REALUSER|NO_SDOT,
+ (char *) TELLC,
+ "unedit", UNEDIT, NO_SDOT, NULL,
+ "diffs", DIFFS, NO_SDOT|REALUSER,
+ NULL,
+ "-diff", DODIFF, NO_SDOT|REALUSER,
+ _PATH_SCCSBDIFF,
+ "print", CMACRO, 0, "prs -e/get -p -m -s",
+ "branch", CMACRO, NO_SDOT,
+ "get:ixrc -e -b/delta: -s -n -ybranch-place-holder/get:pl -e -t -g",
+ "enter", ENTER, NO_SDOT, NULL,
+ "create", CMACRO, NO_SDOT, "enter/get:ixbeskcl -t",
+ NULL, -1, 0, NULL
+};
+
+/* one line from a p-file */
+struct pfile
+{
+ char *p_osid; /* old SID */
+ char *p_nsid; /* new SID */
+ char *p_user; /* user who did edit */
+ char *p_date; /* date of get */
+ char *p_time; /* time of get */
+ char *p_aux; /* extra info at end */
+};
+
+char *SccsPath = SCCSPATH; /* pathname of SCCS files */
+# ifdef SCCSDIR
+char *SccsDir = SCCSDIR; /* directory to begin search from */
+# else
+char *SccsDir = "";
+# endif
+char MyName[] = MYNAME; /* name used in messages */
+int OutFile = -1; /* override output file for commands */
+bool RealUser; /* if set, running as real user */
+# ifdef DEBUG
+bool Debug; /* turn on tracing */
+# endif
+# ifndef V6
+extern char *getenv();
+# endif V6
+
+char *gstrcat(), *strcat();
+char *gstrncat(), *strncat();
+char *gstrcpy(), *strcpy();
+#define FBUFSIZ BUFSIZ
+#define PFILELG 120
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *p;
+ extern struct sccsprog *lookup();
+ register int i;
+# ifndef V6
+# ifndef SCCSDIR
+ register struct passwd *pw;
+ extern struct passwd *getpwnam();
+ char buf[FBUFSIZ];
+
+ /* pull "SccsDir" out of the environment (possibly) */
+ p = getenv("PROJECTDIR");
+ if (p != NULL && p[0] != '\0')
+ {
+ if (p[0] == '/')
+ SccsDir = p;
+ else
+ {
+ pw = getpwnam(p);
+ if (pw == NULL)
+ {
+ usrerr("user %s does not exist", p);
+ exit(EX_USAGE);
+ }
+ gstrcpy(buf, pw->pw_dir, sizeof(buf));
+ gstrcat(buf, "/src", sizeof(buf));
+ if (access(buf, 0) < 0)
+ {
+ gstrcpy(buf, pw->pw_dir, sizeof(buf));
+ gstrcat(buf, "/source", sizeof(buf));
+ if (access(buf, 0) < 0)
+ {
+ usrerr("project %s has no source!", p);
+ exit(EX_USAGE);
+ }
+ }
+ SccsDir = buf;
+ }
+ }
+# endif SCCSDIR
+# endif V6
+
+ /*
+ ** Detect and decode flags intended for this program.
+ */
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage: %s [flags] command [flags]\n", MyName);
+ exit(EX_USAGE);
+ }
+ argv[argc] = NULL;
+
+ if (lookup(argv[0]) == NULL)
+ {
+ while ((p = *++argv) != NULL)
+ {
+ if (*p != '-')
+ break;
+ switch (*++p)
+ {
+ case 'r': /* run as real user */
+ setuid(getuid());
+ RealUser++;
+ break;
+
+# ifndef SCCSDIR
+ case 'p': /* path of sccs files */
+ SccsPath = ++p;
+ if (SccsPath[0] == '\0' && argv[1] != NULL)
+ SccsPath = *++argv;
+ break;
+
+ case 'd': /* directory to search from */
+ SccsDir = ++p;
+ if (SccsDir[0] == '\0' && argv[1] != NULL)
+ SccsDir = *++argv;
+ break;
+# endif
+
+# ifdef DEBUG
+ case 'T': /* trace */
+ Debug++;
+ break;
+# endif
+
+ default:
+ usrerr("unknown option -%s", p);
+ break;
+ }
+ }
+ if (SccsPath[0] == '\0')
+ SccsPath = ".";
+ }
+
+ i = command(argv, FALSE, "");
+ exit(i);
+}
+
+/*
+** COMMAND -- look up and perform a command
+**
+** This routine is the guts of this program. Given an
+** argument vector, it looks up the "command" (argv[0])
+** in the configuration table and does the necessary stuff.
+**
+** Parameters:
+** argv -- an argument vector to process.
+** forkflag -- if set, fork before executing the command.
+** editflag -- if set, only include flags listed in the
+** sccsklets field of the command descriptor.
+** arg0 -- a space-seperated list of arguments to insert
+** before argv.
+**
+** Returns:
+** zero -- command executed ok.
+** else -- error status.
+**
+** Side Effects:
+** none.
+*/
+
+command(argv, forkflag, arg0)
+ char **argv;
+ bool forkflag;
+ char *arg0;
+{
+ register struct sccsprog *cmd;
+ register char *p;
+ char buf[FBUFSIZ];
+ extern struct sccsprog *lookup();
+ char *nav[1000];
+ char **np;
+ register char **ap;
+ register int i;
+ register char *q;
+ extern bool unedit();
+ int rval = 0;
+ extern char *index();
+ extern char *makefile();
+ char *editchs;
+ extern char *tail();
+
+# ifdef DEBUG
+ if (Debug)
+ {
+ printf("command:\n\t\"%s\"\n", arg0);
+ for (np = argv; *np != NULL; np++)
+ printf("\t\"%s\"\n", *np);
+ }
+# endif
+
+ /*
+ ** Copy arguments.
+ ** Copy from arg0 & if necessary at most one arg
+ ** from argv[0].
+ */
+
+ np = ap = &nav[1];
+ editchs = NULL;
+ for (p = arg0, q = buf; *p != '\0' && *p != '/'; )
+ {
+ *np++ = q;
+ while (*p == ' ')
+ p++;
+ while (*p != ' ' && *p != '\0' && *p != '/' && *p != ':')
+ *q++ = *p++;
+ *q++ = '\0';
+ if (*p == ':')
+ {
+ editchs = q;
+ while (*++p != '\0' && *p != '/' && *p != ' ')
+ *q++ = *p;
+ *q++ = '\0';
+ }
+ }
+ *np = NULL;
+ if (*ap == NULL)
+ *np++ = *argv++;
+
+ /*
+ ** Look up command.
+ ** At this point, *ap is the command name.
+ */
+
+ cmd = lookup(*ap);
+ if (cmd == NULL)
+ {
+ usrerr("Unknown command \"%s\"", *ap);
+ return (EX_USAGE);
+ }
+
+ /*
+ ** Copy remaining arguments doing editing as appropriate.
+ */
+
+ for (; *argv != NULL; argv++)
+ {
+ p = *argv;
+ if (*p == '-')
+ {
+ if (p[1] == '\0' || editchs == NULL || index(editchs, p[1]) != NULL)
+ *np++ = p;
+ }
+ else
+ {
+ if (!bitset(NO_SDOT, cmd->sccsflags))
+ p = makefile(p);
+ if (p != NULL)
+ *np++ = p;
+ }
+ }
+ *np = NULL;
+
+ /*
+ ** Interpret operation associated with this command.
+ */
+
+ switch (cmd->sccsoper)
+ {
+ case SHELL: /* call a shell file */
+ *ap = cmd->sccspath;
+ *--ap = "sh";
+ rval = callprog(_PATH_BSHELL, cmd->sccsflags, ap, forkflag);
+ break;
+
+ case PROG: /* call an sccs prog */
+ rval = callprog(cmd->sccspath, cmd->sccsflags, ap, forkflag);
+ break;
+
+ case CMACRO: /* command macro */
+ /* step through & execute each part of the macro */
+ for (p = cmd->sccspath; *p != '\0'; p++)
+ {
+ q = p;
+ while (*p != '\0' && *p != '/')
+ p++;
+ rval = command(&ap[1], *p != '\0', q);
+ if (rval != 0)
+ break;
+ }
+ break;
+
+ case FIX: /* fix a delta */
+ if (ap[1]==0 || strncmp(ap[1], "-r", 2)!=0)
+ {
+ usrerr("-r flag needed for fix command");
+ rval = EX_USAGE;
+ break;
+ }
+
+ /* get the version with all changes */
+ rval = command(&ap[1], TRUE, "get -k");
+
+ /* now remove that version from the s-file */
+ if (rval == 0)
+ rval = command(&ap[1], TRUE, "rmdel:r");
+
+ /* and edit the old version (but don't clobber new vers) */
+ if (rval == 0)
+ rval = command(&ap[2], FALSE, "get -e -g");
+ break;
+
+ case CLEAN:
+ rval = clean((int) cmd->sccspath, ap);
+ break;
+
+ case UNEDIT:
+ for (argv = np = &ap[1]; *argv != NULL; argv++)
+ {
+ if (unedit(*argv))
+ *np++ = *argv;
+ }
+ *np = NULL;
+
+ /* get all the files that we unedited successfully */
+ if (np > &ap[1])
+ rval = command(&ap[1], FALSE, "get");
+ break;
+
+ case DIFFS: /* diff between s-file & edit file */
+ /* find the end of the flag arguments */
+ for (np = &ap[1]; *np != NULL && **np == '-'; np++)
+ continue;
+ argv = np;
+
+ /* for each file, do the diff */
+ p = argv[1];
+ while (*np != NULL)
+ {
+ /* messy, but we need a null terminated argv */
+ *argv = *np++;
+ argv[1] = NULL;
+ i = dodiff(ap, tail(*argv));
+ if (rval == 0)
+ rval = i;
+ argv[1] = p;
+ }
+ break;
+
+ case DODIFF: /* internal diff call */
+ setuid(getuid());
+ for (np = ap; *np != NULL; np++)
+ {
+ if ((*np)[0] == '-' && (*np)[1] == 'C')
+ (*np)[1] = 'c';
+ }
+
+ /* insert "-" argument */
+ np[1] = NULL;
+ np[0] = np[-1];
+ np[-1] = "-";
+
+ /* execute the diff program of choice */
+# ifndef V6
+ execvp("diff", ap);
+# endif
+ execv(cmd->sccspath, argv);
+ syserr("cannot exec %s", cmd->sccspath);
+ exit(EX_OSERR);
+
+ case ENTER: /* enter new sccs files */
+ /* skip over flag arguments */
+ for (np = &ap[1]; *np != NULL && **np == '-'; np++)
+ continue;
+ argv = np;
+
+ /* do an admin for each file */
+ p = argv[1];
+ while (*np != NULL)
+ {
+ printf("\n%s:\n", *np);
+ strcpy(buf, "-i");
+ gstrcat(buf, *np, sizeof(buf));
+ ap[0] = buf;
+ argv[0] = tail(*np);
+ argv[1] = NULL;
+ rval = command(ap, TRUE, "admin");
+ argv[1] = p;
+ if (rval == 0)
+ {
+ strcpy(buf, ",");
+ gstrcat(buf, tail(*np), sizeof(buf));
+ if (link(*np, buf) >= 0)
+ unlink(*np);
+ }
+ np++;
+ }
+ break;
+
+ default:
+ syserr("oper %d", cmd->sccsoper);
+ exit(EX_SOFTWARE);
+ }
+# ifdef DEBUG
+ if (Debug)
+ printf("command: rval=%d\n", rval);
+# endif
+ return (rval);
+}
+
+/*
+** LOOKUP -- look up an SCCS command name.
+**
+** Parameters:
+** name -- the name of the command to look up.
+**
+** Returns:
+** ptr to command descriptor for this command.
+** NULL if no such entry.
+**
+** Side Effects:
+** none.
+*/
+
+struct sccsprog *
+lookup(name)
+ char *name;
+{
+ register struct sccsprog *cmd;
+
+ for (cmd = SccsProg; cmd->sccsname != NULL; cmd++)
+ {
+ if (strcmp(cmd->sccsname, name) == 0)
+ return (cmd);
+ }
+ return (NULL);
+}
+
+/*
+** CALLPROG -- call a program
+**
+** Used to call the SCCS programs.
+**
+** Parameters:
+** progpath -- pathname of the program to call.
+** flags -- status flags from the command descriptors.
+** argv -- an argument vector to pass to the program.
+** forkflag -- if true, fork before calling, else just
+** exec.
+**
+** Returns:
+** The exit status of the program.
+** Nothing if forkflag == FALSE.
+**
+** Side Effects:
+** Can exit if forkflag == FALSE.
+*/
+
+callprog(progpath, flags, argv, forkflag)
+ char *progpath;
+ short flags;
+ char **argv;
+ bool forkflag;
+{
+ register int i;
+ register int wpid;
+ auto int st;
+ register int sigcode;
+ register int coredumped;
+ register const char *sigmsg;
+ char sigmsgbuf[10+1]; /* "Signal 127" + terminating '\0' */
+
+# ifdef DEBUG
+ if (Debug)
+ {
+ printf("callprog:\n");
+ for (i = 0; argv[i] != NULL; i++)
+ printf("\t\"%s\"\n", argv[i]);
+ }
+# endif
+
+ if (*argv == NULL)
+ return (-1);
+
+ /*
+ ** Fork if appropriate.
+ */
+
+ if (forkflag)
+ {
+# ifdef DEBUG
+ if (Debug)
+ printf("Forking\n");
+# endif
+ i = fork();
+ if (i < 0)
+ {
+ syserr("cannot fork");
+ exit(EX_OSERR);
+ }
+ else if (i > 0)
+ {
+ while ((wpid = wait(&st)) != -1 && wpid != i)
+ ;
+ if ((sigcode = st & 0377) == 0)
+ st = (st >> 8) & 0377;
+ else
+ {
+ coredumped = sigcode & 0200;
+ sigcode &= 0177;
+ if (sigcode != SIGINT && sigcode != SIGPIPE)
+ {
+ if (sigcode < NSIG)
+ sigmsg = sys_siglist[sigcode];
+ else
+ {
+ sprintf(sigmsgbuf, "Signal %d",
+ sigcode);
+ sigmsg = sigmsgbuf;
+ }
+ fprintf(stderr, "sccs: %s: %s%s", argv[0],
+ sigmsg,
+ coredumped ? " - core dumped": "");
+ }
+ st = EX_SOFTWARE;
+ }
+ if (OutFile >= 0)
+ {
+ close(OutFile);
+ OutFile = -1;
+ }
+ return (st);
+ }
+ }
+ else if (OutFile >= 0)
+ {
+ syserr("callprog: setting stdout w/o forking");
+ exit(EX_SOFTWARE);
+ }
+
+ /* set protection as appropriate */
+ if (bitset(REALUSER, flags))
+ setuid(getuid());
+
+ /* change standard input & output if needed */
+ if (OutFile >= 0)
+ {
+ close(1);
+ dup(OutFile);
+ close(OutFile);
+ }
+
+ /* call real SCCS program */
+ execv(progpath, argv);
+ syserr("cannot execute %s", progpath);
+ exit(EX_UNAVAILABLE);
+ /*NOTREACHED*/
+}
+
+/*
+** MAKEFILE -- make filename of SCCS file
+**
+** If the name passed is already the name of an SCCS file,
+** just return it. Otherwise, munge the name into the name
+** of the actual SCCS file.
+**
+** There are cases when it is not clear what you want to
+** do. For example, if SccsPath is an absolute pathname
+** and the name given is also an absolute pathname, we go
+** for SccsPath (& only use the last component of the name
+** passed) -- this is important for security reasons (if
+** sccs is being used as a setuid front end), but not
+** particularly intuitive.
+**
+** Parameters:
+** name -- the file name to be munged.
+**
+** Returns:
+** The pathname of the sccs file.
+** NULL on error.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+makefile(name)
+ char *name;
+{
+ register char *p;
+ char buf[3*FBUFSIZ];
+ extern char *malloc();
+ extern char *rindex();
+ extern bool safepath();
+ extern bool isdir();
+ register char *q;
+
+ p = rindex(name, '/');
+ if (p == NULL)
+ p = name;
+ else
+ p++;
+
+ /*
+ ** Check to see that the path is "safe", i.e., that we
+ ** are not letting some nasty person use the setuid part
+ ** of this program to look at or munge some presumably
+ ** hidden files.
+ */
+
+ if (SccsDir[0] == '/' && !safepath(name))
+ return (NULL);
+
+ /*
+ ** Create the base pathname.
+ */
+
+ /* first the directory part */
+ if (SccsDir[0] != '\0' && name[0] != '/' && strncmp(name, "./", 2) != 0)
+ {
+ gstrcpy(buf, SccsDir, sizeof(buf));
+ gstrcat(buf, "/", sizeof(buf));
+ }
+ else
+ gstrcpy(buf, "", sizeof(buf));
+
+ /* then the head of the pathname */
+ gstrncat(buf, name, p - name, sizeof(buf));
+ q = &buf[strlen(buf)];
+
+ /* now copy the final part of the name, in case useful */
+ gstrcpy(q, p, sizeof(buf));
+
+ /* so is it useful? */
+ if (strncmp(p, "s.", 2) != 0 && !isdir(buf))
+ {
+ /* sorry, no; copy the SCCS pathname & the "s." */
+ gstrcpy(q, SccsPath, sizeof(buf));
+ gstrcat(buf, "/s.", sizeof(buf));
+
+ /* and now the end of the name */
+ gstrcat(buf, p, sizeof(buf));
+ }
+
+ /* if i haven't changed it, why did I do all this? */
+ if (strcmp(buf, name) == 0)
+ p = name;
+ else
+ {
+ /* but if I have, squirrel it away */
+ p = malloc(strlen(buf) + 1);
+ if (p == NULL)
+ {
+ perror("Sccs: no mem");
+ exit(EX_OSERR);
+ }
+ strcpy(p, buf);
+ }
+
+ return (p);
+}
+
+/*
+** ISDIR -- return true if the argument is a directory.
+**
+** Parameters:
+** name -- the pathname of the file to check.
+**
+** Returns:
+** TRUE if 'name' is a directory, FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+isdir(name)
+ char *name;
+{
+ struct stat stbuf;
+
+ return (stat(name, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) == S_IFDIR);
+}
+
+/*
+** SAFEPATH -- determine whether a pathname is "safe"
+**
+** "Safe" pathnames only allow you to get deeper into the
+** directory structure, i.e., full pathnames and ".." are
+** not allowed.
+**
+** Parameters:
+** p -- the name to check.
+**
+** Returns:
+** TRUE -- if the path is safe.
+** FALSE -- if the path is not safe.
+**
+** Side Effects:
+** Prints a message if the path is not safe.
+*/
+
+bool
+safepath(p)
+ register char *p;
+{
+ extern char *index();
+
+ if (*p != '/')
+ {
+ while (strncmp(p, "../", 3) != 0 && strcmp(p, "..") != 0)
+ {
+ p = index(p, '/');
+ if (p == NULL)
+ return (TRUE);
+ p++;
+ }
+ }
+
+ printf("You may not use full pathnames or \"..\"\n");
+ return (FALSE);
+}
+
+/*
+** CLEAN -- clean out recreatable files
+**
+** Any file for which an "s." file exists but no "p." file
+** exists in the current directory is purged.
+**
+** Parameters:
+** mode -- tells whether this came from a "clean", "info", or
+** "check" command.
+** argv -- the rest of the argument vector.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Removes files in the current directory.
+** Prints information regarding files being edited.
+** Exits if a "check" command.
+*/
+
+clean(mode, argv)
+ int mode;
+ char **argv;
+{
+ struct direct *dir;
+ char buf[FBUFSIZ];
+ char *bufend;
+ register DIR *dirp;
+ register char *basefile;
+ bool gotedit;
+ bool gotpfent;
+ FILE *pfp;
+ bool nobranch = FALSE;
+ extern struct pfile *getpfent();
+ register struct pfile *pf;
+ register char **ap;
+ extern char *username();
+ char *usernm = NULL;
+ char *subdir = NULL;
+ char *cmdname;
+
+ /*
+ ** Process the argv
+ */
+
+ cmdname = *argv;
+ for (ap = argv; *++ap != NULL; )
+ {
+ if (**ap == '-')
+ {
+ /* we have a flag */
+ switch ((*ap)[1])
+ {
+ case 'b':
+ nobranch = TRUE;
+ break;
+
+ case 'u':
+ if ((*ap)[2] != '\0')
+ usernm = &(*ap)[2];
+ else if (ap[1] != NULL && ap[1][0] != '-')
+ usernm = *++ap;
+ else
+ usernm = username();
+ break;
+ }
+ }
+ else
+ {
+ if (subdir != NULL)
+ usrerr("too many args");
+ else
+ subdir = *ap;
+ }
+ }
+
+ /*
+ ** Find and open the SCCS directory.
+ */
+
+ gstrcpy(buf, SccsDir, sizeof(buf));
+ if (buf[0] != '\0')
+ gstrcat(buf, "/", sizeof(buf));
+ if (subdir != NULL)
+ {
+ gstrcat(buf, subdir, sizeof(buf));
+ gstrcat(buf, "/", sizeof(buf));
+ }
+ gstrcat(buf, SccsPath, sizeof(buf));
+ bufend = &buf[strlen(buf)];
+
+ dirp = opendir(buf);
+ if (dirp == NULL)
+ {
+ usrerr("cannot open %s", buf);
+ return (EX_NOINPUT);
+ }
+
+ /*
+ ** Scan the SCCS directory looking for s. files.
+ ** gotedit tells whether we have tried to clean any
+ ** files that are being edited.
+ */
+
+ gotedit = FALSE;
+ while (dir = readdir(dirp)) {
+ if (strncmp(dir->d_name, "s.", 2) != 0)
+ continue;
+
+ /* got an s. file -- see if the p. file exists */
+ gstrcpy(bufend, "/p.", sizeof(buf));
+ basefile = bufend + 3;
+ gstrcpy(basefile, &dir->d_name[2], sizeof(buf));
+
+ /*
+ ** open and scan the p-file.
+ ** 'gotpfent' tells if we have found a valid p-file
+ ** entry.
+ */
+
+ pfp = fopen(buf, "r");
+ gotpfent = FALSE;
+ if (pfp != NULL)
+ {
+ /* the file exists -- report it's contents */
+ while ((pf = getpfent(pfp)) != NULL)
+ {
+ if (nobranch && isbranch(pf->p_nsid))
+ continue;
+ if (usernm != NULL && strcmp(usernm, pf->p_user) != 0 && mode != CLEANC)
+ continue;
+ gotedit = TRUE;
+ gotpfent = TRUE;
+ if (mode == TELLC)
+ {
+ printf("%s\n", basefile);
+ break;
+ }
+ printf("%12s: being edited: ", basefile);
+ putpfent(pf, stdout);
+ }
+ fclose(pfp);
+ }
+
+ /* the s. file exists and no p. file exists -- unlink the g-file */
+ if (mode == CLEANC && !gotpfent)
+ {
+ char unlinkbuf[FBUFSIZ];
+ gstrcpy(unlinkbuf, &dir->d_name[2], sizeof(unlinkbuf));
+ unlink(unlinkbuf);
+ }
+ }
+
+ /* cleanup & report results */
+ closedir(dirp);
+ if (!gotedit && mode == INFOC)
+ {
+ printf("Nothing being edited");
+ if (nobranch)
+ printf(" (on trunk)");
+ if (usernm == NULL)
+ printf("\n");
+ else
+ printf(" by %s\n", usernm);
+ }
+ if (mode == CHECKC)
+ exit(gotedit);
+ return (EX_OK);
+}
+
+/*
+** ISBRANCH -- is the SID a branch?
+**
+** Parameters:
+** sid -- the sid to check.
+**
+** Returns:
+** TRUE if the sid represents a branch.
+** FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+isbranch(sid)
+ char *sid;
+{
+ register char *p;
+ int dots;
+
+ dots = 0;
+ for (p = sid; *p != '\0'; p++)
+ {
+ if (*p == '.')
+ dots++;
+ if (dots > 1)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+** UNEDIT -- unedit a file
+**
+** Checks to see that the current user is actually editting
+** the file and arranges that s/he is not editting it.
+**
+** Parameters:
+** fn -- the name of the file to be unedited.
+**
+** Returns:
+** TRUE -- if the file was successfully unedited.
+** FALSE -- if the file was not unedited for some
+** reason.
+**
+** Side Effects:
+** fn is removed
+** entries are removed from pfile.
+*/
+
+bool
+unedit(fn)
+ char *fn;
+{
+ register FILE *pfp;
+ char *cp, *pfn;
+ static char tfn[] = _PATH_TMP;
+ FILE *tfp;
+ register char *q;
+ bool delete = FALSE;
+ bool others = FALSE;
+ char *myname;
+ extern char *username();
+ struct pfile *pent;
+ extern struct pfile *getpfent();
+ char buf[PFILELG];
+ extern char *makefile(), *rindex(), *tail();
+
+ /* make "s." filename & find the trailing component */
+ pfn = makefile(fn);
+ if (pfn == NULL)
+ return (FALSE);
+ q = rindex(pfn, '/');
+ if (q == NULL)
+ q = &pfn[-1];
+ if (q[1] != 's' || q[2] != '.')
+ {
+ usrerr("bad file name \"%s\"", fn);
+ return (FALSE);
+ }
+
+ /* turn "s." into "p." & try to open it */
+ *++q = 'p';
+
+ pfp = fopen(pfn, "r");
+ if (pfp == NULL)
+ {
+ printf("%12s: not being edited\n", fn);
+ return (FALSE);
+ }
+
+ /* create temp file for editing p-file */
+ mktemp(tfn);
+ tfp = fopen(tfn, "w");
+ if (tfp == NULL)
+ {
+ usrerr("cannot create \"%s\"", tfn);
+ exit(EX_OSERR);
+ }
+
+ /* figure out who I am */
+ myname = username();
+
+ /*
+ ** Copy p-file to temp file, doing deletions as needed.
+ */
+
+ while ((pent = getpfent(pfp)) != NULL)
+ {
+ if (strcmp(pent->p_user, myname) == 0)
+ {
+ /* a match */
+ delete++;
+ }
+ else
+ {
+ /* output it again */
+ putpfent(pent, tfp);
+ others++;
+ }
+ }
+
+ /*
+ * Before changing anything, make sure we can remove
+ * the file in question (assuming it exists).
+ */
+ if (delete) {
+ extern int errno;
+
+ cp = tail(fn);
+ errno = 0;
+ if (access(cp, 0) < 0 && errno != ENOENT)
+ goto bad;
+ if (errno == 0)
+ /*
+ * This is wrong, but the rest of the program
+ * has built in assumptions about "." as well,
+ * so why make unedit a special case?
+ */
+ if (access(".", 2) < 0) {
+ bad:
+ printf("%12s: can't remove\n", cp);
+ fclose(tfp);
+ fclose(pfp);
+ unlink(tfn);
+ return (FALSE);
+ }
+ }
+ /* do final cleanup */
+ if (others)
+ {
+ /* copy it back (perhaps it should be linked?) */
+ if (freopen(tfn, "r", tfp) == NULL)
+ {
+ syserr("cannot reopen \"%s\"", tfn);
+ exit(EX_OSERR);
+ }
+ if (freopen(pfn, "w", pfp) == NULL)
+ {
+ usrerr("cannot create \"%s\"", pfn);
+ return (FALSE);
+ }
+ while (fgets(buf, sizeof buf, tfp) != NULL)
+ fputs(buf, pfp);
+ }
+ else
+ {
+ /* it's empty -- remove it */
+ unlink(pfn);
+ }
+ fclose(tfp);
+ fclose(pfp);
+ unlink(tfn);
+
+ /* actually remove the g-file */
+ if (delete)
+ {
+ /*
+ * Since we've checked above, we can
+ * use the return from unlink to
+ * determine if the file existed or not.
+ */
+ if (unlink(cp) >= 0)
+ printf("%12s: removed\n", cp);
+ return (TRUE);
+ }
+ else
+ {
+ printf("%12s: not being edited by you\n", fn);
+ return (FALSE);
+ }
+}
+
+/*
+** DODIFF -- diff an s-file against a g-file
+**
+** Parameters:
+** getv -- argv for the 'get' command.
+** gfile -- name of the g-file to diff against.
+**
+** Returns:
+** Result of get.
+**
+** Side Effects:
+** none.
+*/
+
+dodiff(getv, gfile)
+ char **getv;
+ char *gfile;
+{
+ int pipev[2];
+ int rval;
+ register int i;
+ register int pid;
+ auto int st;
+ extern int errno;
+ sig_t osig;
+
+ printf("\n------- %s -------\n", gfile);
+ fflush(stdout);
+
+ /* create context for diff to run in */
+ if (pipe(pipev) < 0)
+ {
+ syserr("dodiff: pipe failed");
+ exit(EX_OSERR);
+ }
+ if ((pid = fork()) < 0)
+ {
+ syserr("dodiff: fork failed");
+ exit(EX_OSERR);
+ }
+ else if (pid > 0)
+ {
+ /* in parent; run get */
+ OutFile = pipev[1];
+ close(pipev[0]);
+ rval = command(&getv[1], TRUE, "get:rcixt -s -k -p");
+ osig = signal(SIGINT, SIG_IGN);
+ while (((i = wait(&st)) >= 0 && i != pid) || errno == EINTR)
+ errno = 0;
+ signal(SIGINT, osig);
+ /* ignore result of diff */
+ }
+ else
+ {
+ /* in child, run diff */
+ if (close(pipev[1]) < 0 || close(0) < 0 ||
+ dup(pipev[0]) != 0 || close(pipev[0]) < 0)
+ {
+ syserr("dodiff: magic failed");
+ exit(EX_OSERR);
+ }
+ command(&getv[1], FALSE, "-diff:elsfhbC");
+ }
+ return (rval);
+}
+
+/*
+** TAIL -- return tail of filename.
+**
+** Parameters:
+** fn -- the filename.
+**
+** Returns:
+** a pointer to the tail of the filename; e.g., given
+** "cmd/ls.c", "ls.c" is returned.
+**
+** Side Effects:
+** none.
+*/
+
+char *
+tail(fn)
+ register char *fn;
+{
+ register char *p;
+
+ for (p = fn; *p != 0; p++)
+ if (*p == '/' && p[1] != '\0' && p[1] != '/')
+ fn = &p[1];
+ return (fn);
+}
+
+/*
+** GETPFENT -- get an entry from the p-file
+**
+** Parameters:
+** pfp -- p-file file pointer
+**
+** Returns:
+** pointer to p-file struct for next entry
+** NULL on EOF or error
+**
+** Side Effects:
+** Each call wipes out results of previous call.
+*/
+
+struct pfile *
+getpfent(pfp)
+ FILE *pfp;
+{
+ static struct pfile ent;
+ static char buf[PFILELG];
+ register char *p;
+ extern char *nextfield();
+
+ if (fgets(buf, sizeof buf, pfp) == NULL)
+ return (NULL);
+
+ ent.p_osid = p = buf;
+ ent.p_nsid = p = nextfield(p);
+ ent.p_user = p = nextfield(p);
+ ent.p_date = p = nextfield(p);
+ ent.p_time = p = nextfield(p);
+ ent.p_aux = p = nextfield(p);
+
+ return (&ent);
+}
+
+
+char *
+nextfield(p)
+ register char *p;
+{
+ if (p == NULL || *p == '\0')
+ return (NULL);
+ while (*p != ' ' && *p != '\n' && *p != '\0')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ {
+ *p = '\0';
+ return (NULL);
+ }
+ *p++ = '\0';
+ return (p);
+}
+ /*
+** PUTPFENT -- output a p-file entry to a file
+**
+** Parameters:
+** pf -- the p-file entry
+** f -- the file to put it on.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** pf is written onto file f.
+*/
+
+putpfent(pf, f)
+ register struct pfile *pf;
+ register FILE *f;
+{
+ fprintf(f, "%s %s %s %s %s", pf->p_osid, pf->p_nsid,
+ pf->p_user, pf->p_date, pf->p_time);
+ if (pf->p_aux != NULL)
+ fprintf(f, " %s", pf->p_aux);
+ else
+ fprintf(f, "\n");
+}
+
+/*
+** USRERR -- issue user-level error
+**
+** Parameters:
+** f -- format string.
+** p1-p3 -- parameters to a printf.
+**
+** Returns:
+** -1
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+usrerr(f, p1, p2, p3)
+ char *f;
+{
+ fprintf(stderr, "\n%s: ", MyName);
+ fprintf(stderr, f, p1, p2, p3);
+ fprintf(stderr, "\n");
+
+ return (-1);
+}
+
+/*
+** SYSERR -- print system-generated error.
+**
+** Parameters:
+** f -- format string to a printf.
+** p1, p2, p3 -- parameters to f.
+**
+** Returns:
+** never.
+**
+** Side Effects:
+** none.
+*/
+
+/*VARARGS1*/
+syserr(f, p1, p2, p3)
+ char *f;
+{
+ extern int errno;
+
+ fprintf(stderr, "\n%s SYSERR: ", MyName);
+ fprintf(stderr, f, p1, p2, p3);
+ fprintf(stderr, "\n");
+ if (errno == 0)
+ exit(EX_SOFTWARE);
+ else
+ {
+ perror(NULL);
+ exit(EX_OSERR);
+ }
+}
+ /*
+** USERNAME -- return name of the current user
+**
+** Parameters:
+** none
+**
+** Returns:
+** name of current user
+**
+** Side Effects:
+** none
+*/
+
+char *
+username()
+{
+# ifdef UIDUSER
+ extern struct passwd *getpwuid();
+ register struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL)
+ {
+ syserr("who are you? (uid=%d)", getuid());
+ exit(EX_OSERR);
+ }
+ return (pw->pw_name);
+# else
+ extern char *getlogin();
+ register char *p;
+
+ p = getenv("USER");
+ if (p == NULL || p[0] == '\0')
+ p = getlogin();
+ return (p);
+# endif UIDUSER
+}
+
+/*
+** Guarded string manipulation routines; the last argument
+** is the length of the buffer into which the strcpy or strcat
+** is to be done.
+*/
+char *gstrcat(to, from, length)
+ char *to, *from;
+ int length;
+{
+ if (strlen(from) + strlen(to) >= length) {
+ gstrbotch(to, from);
+ }
+ return(strcat(to, from));
+}
+
+char *gstrncat(to, from, n, length)
+ char *to, *from;
+ int n;
+ int length;
+{
+ if (n + strlen(to) >= length) {
+ gstrbotch(to, from);
+ }
+ return(strncat(to, from, n));
+}
+
+char *gstrcpy(to, from, length)
+ char *to, *from;
+ int length;
+{
+ if (strlen(from) >= length) {
+ gstrbotch(from, (char *)0);
+ }
+ return(strcpy(to, from));
+}
+gstrbotch(str1, str2)
+ char *str1, *str2;
+{
+ usrerr("Filename(s) too long: %s %s", str1, str2);
+}
diff --git a/usr.bin/script/Makefile b/usr.bin/script/Makefile
new file mode 100644
index 0000000..78dbe68
--- /dev/null
+++ b/usr.bin/script/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= script
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/script/script.1 b/usr.bin/script/script.1
new file mode 100644
index 0000000..deda037
--- /dev/null
+++ b/usr.bin/script/script.1
@@ -0,0 +1,123 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)script.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt SCRIPT 1
+.Os BSD 4
+.Sh NAME
+.Nm script
+.Nd make typescript of terminal session
+.Sh SYNOPSIS
+.Nm script
+.Op Fl a
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Script
+makes a typescript of everything printed on your terminal.
+It is useful for students who need a hardcopy record of an interactive
+session as proof of an assignment, as the typescript file
+can be printed out later with
+.Xr lpr 1 .
+.Pp
+If the argument
+.Ar file
+is given,
+.Nm
+saves all dialogue in
+.Ar file .
+If no file name is given, the typescript is saved in the file
+.Pa typescript .
+.Pp
+Option:
+.Bl -tag -width Ds
+.It Fl a
+Append the output to
+.Ar file
+or
+.Pa typescript ,
+retaining the prior contents.
+.El
+.Pp
+The script ends when the forked shell exits (a
+.Em control-D
+to exit
+the Bourne shell
+.Pf ( Xr sh 1 ) ,
+and
+.Em exit ,
+.Em logout
+or
+.Em control-d
+(if
+.Em ignoreeof
+is not set) for the
+C-shell,
+.Xr csh 1 ) .
+.Pp
+Certain interactive commands, such as
+.Xr vi 1 ,
+create garbage in the typescript file.
+.Nm Script
+works best with commands that do not manipulate the
+screen, the results are meant to emulate a hardcopy
+terminal.
+.Sh ENVIRONMENT
+The following environment variable is utilized by
+.Nm script :
+.Bl -tag -width SHELL
+.It Ev SHELL
+If the variable
+.Ev SHELL
+exists, the shell forked by
+.Nm script
+will be that shell. If
+.Ev SHELL
+is not set, the Bourne shell
+is assumed. (Most shells set this variable automatically).
+.El
+.Sh SEE ALSO
+.Xr csh 1
+(for the
+.Em history
+mechanism).
+.Sh HISTORY
+The
+.Nm script
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+.Nm Script
+places
+.Sy everything
+in the log file, including linefeeds and backspaces.
+This is not what the naive user expects.
diff --git a/usr.bin/script/script.c b/usr.bin/script/script.c
new file mode 100644
index 0000000..25c5ad6
--- /dev/null
+++ b/usr.bin/script/script.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+FILE *fscript;
+int master, slave;
+int child, subchild;
+int outcc;
+char *fname;
+
+struct termios tt;
+
+__dead void done __P((void));
+ void dooutput __P((void));
+ void doshell __P((void));
+ void err __P((const char *, ...));
+ void fail __P((void));
+ void finish __P((int));
+ void scriptflush __P((int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int cc;
+ struct termios rtt;
+ struct winsize win;
+ int aflg, ch;
+ char ibuf[BUFSIZ];
+
+ aflg = 0;
+ while ((ch = getopt(argc, argv, "a")) != EOF)
+ switch(ch) {
+ case 'a':
+ aflg = 1;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr, "usage: script [-a] [file]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ fname = argv[0];
+ else
+ fname = "typescript";
+
+ if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
+ err("%s: %s", fname, strerror(errno));
+
+ (void)tcgetattr(STDIN_FILENO, &tt);
+ (void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
+ if (openpty(&master, &slave, NULL, &tt, &win) == -1)
+ err("openpty: %s", strerror(errno));
+
+ (void)printf("Script started, output file is %s\n", fname);
+ rtt = tt;
+ cfmakeraw(&rtt);
+ rtt.c_lflag &= ~ECHO;
+ (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
+
+ (void)signal(SIGCHLD, finish);
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail();
+ }
+ if (child == 0) {
+ subchild = child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail();
+ }
+ if (child)
+ dooutput();
+ else
+ doshell();
+ }
+
+ (void)fclose(fscript);
+ while ((cc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0)
+ (void)write(master, ibuf, cc);
+ done();
+}
+
+void
+finish(signo)
+ int signo;
+{
+ register int die, pid;
+ union wait status;
+
+ die = 0;
+ while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
+ if (pid == child)
+ die = 1;
+
+ if (die)
+ done();
+}
+
+void
+dooutput()
+{
+ struct itimerval value;
+ register int cc;
+ time_t tvec;
+ char obuf[BUFSIZ];
+
+ (void)close(STDIN_FILENO);
+ tvec = time(NULL);
+ (void)fprintf(fscript, "Script started on %s", ctime(&tvec));
+
+ (void)signal(SIGALRM, scriptflush);
+ value.it_interval.tv_sec = 60 / 2;
+ value.it_interval.tv_usec = 0;
+ value.it_value = value.it_interval;
+ (void)setitimer(ITIMER_REAL, &value, NULL);
+ for (;;) {
+ cc = read(master, obuf, sizeof (obuf));
+ if (cc <= 0)
+ break;
+ (void)write(1, obuf, cc);
+ (void)fwrite(obuf, 1, cc, fscript);
+ outcc += cc;
+ }
+ done();
+}
+
+void
+scriptflush(signo)
+ int signo;
+{
+ if (outcc) {
+ (void)fflush(fscript);
+ outcc = 0;
+ }
+}
+
+void
+doshell()
+{
+ char *shell;
+
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = _PATH_BSHELL;
+
+ (void)close(master);
+ (void)fclose(fscript);
+ login_tty(slave);
+ execl(shell, "sh", "-i", NULL);
+ perror(shell);
+ fail();
+}
+
+void
+fail()
+{
+
+ (void)kill(0, SIGTERM);
+ done();
+}
+
+void
+done()
+{
+ time_t tvec;
+
+ if (subchild) {
+ tvec = time(NULL);
+ (void)fprintf(fscript,"\nScript done on %s", ctime(&tvec));
+ (void)fclose(fscript);
+ (void)close(master);
+ } else {
+ (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt);
+ (void)printf("Script done, output file is %s\n", fname);
+ }
+ exit(0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "script: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/sed/Makefile b/usr.bin/sed/Makefile
new file mode 100644
index 0000000..99f860b
--- /dev/null
+++ b/usr.bin/sed/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sed
+SRCS= compile.c main.c misc.c process.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sed/POSIX b/usr.bin/sed/POSIX
new file mode 100644
index 0000000..41955af
--- /dev/null
+++ b/usr.bin/sed/POSIX
@@ -0,0 +1,198 @@
+# @(#)POSIX 8.1 (Berkeley) 6/6/93
+
+Comments on the IEEE P1003.2 Draft 12
+ Part 2: Shell and Utilities
+ Section 4.55: sed - Stream editor
+
+Diomidis Spinellis <dds@doc.ic.ac.uk>
+Keith Bostic <bostic@cs.berkeley.edu>
+
+In the following paragraphs, "wrong" usually means "inconsistent with
+historic practice", as most of the following comments refer to
+undocumented inconsistencies between the historical versions of sed and
+the POSIX 1003.2 standard. All the comments are notes taken while
+implementing a POSIX-compatible version of sed, and should not be
+interpreted as official opinions or criticism towards the POSIX committee.
+All uses of "POSIX" refer to section 4.55, Draft 12 of POSIX 1003.2.
+
+ 1. 32V and BSD derived implementations of sed strip the text
+ arguments of the a, c and i commands of their initial blanks,
+ i.e.
+
+ #!/bin/sed -f
+ a\
+ foo\
+ \ indent\
+ bar
+
+ produces:
+
+ foo
+ indent
+ bar
+
+ POSIX does not specify this behavior as the System V versions of
+ sed do not do this stripping. The argument against stripping is
+ that it is difficult to write sed scripts that have leading blanks
+ if they are stripped. The argument for stripping is that it is
+ difficult to write readable sed scripts unless indentation is allowed
+ and ignored, and leading whitespace is obtainable by entering a
+ backslash in front of it. This implementation follows the BSD
+ historic practice.
+
+ 2. Historical versions of sed required that the w flag be the last
+ flag to an s command as it takes an additional argument. This
+ is obvious, but not specified in POSIX.
+
+ 3. Historical versions of sed required that whitespace follow a w
+ flag to an s command. This is not specified in POSIX. This
+ implementation permits whitespace but does not require it.
+
+ 4. Historical versions of sed permitted any number of whitespace
+ characters to follow the w command. This is not specified in
+ POSIX. This implementation permits whitespace but does not
+ require it.
+
+ 5. The rule for the l command differs from historic practice. Table
+ 2-15 includes the various ANSI C escape sequences, including \\
+ for backslash. Some historical versions of sed displayed two
+ digit octal numbers, too, not three as specified by POSIX. POSIX
+ is a cleanup, and is followed by this implementation.
+
+ 6. The POSIX specification for ! does not specify that for a single
+ command the command must not contain an address specification
+ whereas the command list can contain address specifications. The
+ specification for ! implies that "3!/hello/p" works, and it never
+ has, historically. Note,
+
+ 3!{
+ /hello/p
+ }
+
+ does work.
+
+ 7. POSIX does not specify what happens with consecutive ! commands
+ (e.g. /foo/!!!p). Historic implementations allow any number of
+ !'s without changing the behaviour. (It seems logical that each
+ one might reverse the behaviour.) This implementation follows
+ historic practice.
+
+ 8. Historic versions of sed permitted commands to be separated
+ by semi-colons, e.g. 'sed -ne '1p;2p;3q' printed the first
+ three lines of a file. This is not specified by POSIX.
+ Note, the ; command separator is not allowed for the commands
+ a, c, i, w, r, :, b, t, # and at the end of a w flag in the s
+ command. This implementation follows historic practice and
+ implements the ; separator.
+
+ 9. Historic versions of sed terminated the script if EOF was reached
+ during the execution of the 'n' command, i.e.:
+
+ sed -e '
+ n
+ i\
+ hello
+ ' </dev/null
+
+ did not produce any output. POSIX does not specify this behavior.
+ This implementation follows historic practice.
+
+10. Deleted.
+
+11. Historical implementations do not output the change text of a c
+ command in the case of an address range whose first line number
+ is greater than the second (e.g. 3,1). POSIX requires that the
+ text be output. Since the historic behavior doesn't seem to have
+ any particular purpose, this implementation follows the POSIX
+ behavior.
+
+12. POSIX does not specify whether address ranges are checked and
+ reset if a command is not executed due to a jump. The following
+ program will behave in different ways depending on whether the
+ 'c' command is triggered at the third line, i.e. will the text
+ be output even though line 3 of the input will never logically
+ encounter that command.
+
+ 2,4b
+ 1,3c\
+ text
+
+ Historic implementations, and this implementation, do not output
+ the text in the above example. The general rule, therefore,
+ is that a range whose second address is never matched extends to
+ the end of the input.
+
+13. Historical implementations allow an output suppressing #n at the
+ beginning of -e arguments as well as in a script file. POSIX
+ does not specify this. This implementation follows historical
+ practice.
+
+14. POSIX does not explicitly specify how sed behaves if no script is
+ specified. Since the sed Synopsis permits this form of the command,
+ and the language in the Description section states that the input
+ is output, it seems reasonable that it behave like the cat(1)
+ command. Historic sed implementations behave differently for "ls |
+ sed", where they produce no output, and "ls | sed -e#", where they
+ behave like cat. This implementation behaves like cat in both cases.
+
+15. The POSIX requirement to open all w files at the beginning makes
+ sed behave nonintuitively when the w commands are preceded by
+ addresses or are within conditional blocks. This implementation
+ follows historic practice and POSIX, by default, and provides the
+ -a option which opens the files only when they are needed.
+
+16. POSIX does not specify how escape sequences other than \n and \D
+ (where D is the delimiter character) are to be treated. This is
+ reasonable, however, it also doesn't state that the backslash is
+ to be discarded from the output regardless. A strict reading of
+ POSIX would be that "echo xyz | sed s/./\a" would display "\ayz".
+ As historic sed implementations always discarded the backslash,
+ this implementation does as well.
+
+17. POSIX specifies that an address can be "empty". This implies
+ that constructs like ",d" or "1,d" and ",5d" are allowed. This
+ is not true for historic implementations or this implementation
+ of sed.
+
+18. The b t and : commands are documented in POSIX to ignore leading
+ white space, but no mention is made of trailing white space.
+ Historic implementations of sed assigned different locations to
+ the labels "x" and "x ". This is not useful, and leads to subtle
+ programming errors, but it is historic practice and changing it
+ could theoretically break working scripts. This implementation
+ follows historic practice.
+
+19. Although POSIX specifies that reading from files that do not exist
+ from within the script must not terminate the script, it does not
+ specify what happens if a write command fails. Historic practice
+ is to fail immediately if the file cannot be opened or written.
+ This implementation follows historic practice.
+
+20. Historic practice is that the \n construct can be used for either
+ string1 or string2 of the y command. This is not specified by
+ POSIX. This implementation follows historic practice.
+
+21. Deleted.
+
+22. Historic implementations of sed ignore the RE delimiter characters
+ within character classes. This is not specified in POSIX. This
+ implementation follows historic practice.
+
+23. Historic implementations handle empty RE's in a special way: the
+ empty RE is interpreted as if it were the last RE encountered,
+ whether in an address or elsewhere. POSIX does not document this
+ behavior. For example the command:
+
+ sed -e /abc/s//XXX/
+
+ substitutes XXX for the pattern abc. The semantics of "the last
+ RE" can be defined in two different ways:
+
+ 1. The last RE encountered when compiling (lexical/static scope).
+ 2. The last RE encountered while running (dynamic scope).
+
+ While many historical implementations fail on programs depending
+ on scope differences, the SunOS version exhibited dynamic scope
+ behaviour. This implementation does dynamic scoping, as this seems
+ the most useful and in order to remain consistent with historical
+ practice.
diff --git a/usr.bin/sed/TEST/hanoi.sed b/usr.bin/sed/TEST/hanoi.sed
new file mode 100644
index 0000000..d29c648
--- /dev/null
+++ b/usr.bin/sed/TEST/hanoi.sed
@@ -0,0 +1,102 @@
+# Towers of Hanoi in sed.
+#
+# @(#)hanoi.sed 8.1 (Berkeley) 6/6/93
+#
+#
+# Ex:
+# Run "sed -f hanoi.sed", and enter:
+#
+# :abcd: : :<CR><CR>
+#
+# note -- TWO carriage returns, a peculiarity of sed), this will output the
+# sequence of states involved in moving 4 rings, the largest called "a" and
+# the smallest called "d", from the first to the second of three towers, so
+# that the rings on any tower at any time are in descending order of size.
+# You can start with a different arrangement and a different number of rings,
+# say :ce:b:ax: and it will give the shortest procedure for moving them all
+# to the middle tower. The rules are: the names of the rings must all be
+# lower-case letters, they must be input within 3 fields (representing the
+# towers) and delimited by 4 colons, such that the letters within each field
+# are in alphabetical order (i.e. rings are in descending order of size).
+#
+# For the benefit of anyone who wants to figure out the script, an "internal"
+# line of the form
+# b:0abx:1a2b3 :2 :3x2
+# has the following meaning: the material after the three markers :1, :2,
+# and :3 represents the three towers; in this case the current set-up is
+# ":ab : :x :". The numbers after a, b and x in these fields indicate
+# that the next time it gets a chance, it will move a to tower 2, move b
+# to tower 3, and move x to tower 2. The string after :0 just keeps track
+# of the alphabetical order of the names of the rings. The b at the
+# beginning means that it is now dealing with ring b (either about to move
+# it, or re-evaluating where it should next be moved to).
+#
+# Although this version is "limited" to 26 rings because of the size of the
+# alphabet, one could write a script using the same idea in which the rings
+# were represented by arbitrary [strings][within][brackets], and in place of
+# the built-in line of the script giving the order of the letters of the
+# alphabet, it would accept from the user a line giving the ordering to be
+# assumed, e.g. [ucbvax][decvax][hplabs][foo][bar].
+#
+# George Bergman
+# Math, UC Berkeley 94720 USA
+
+# cleaning, diagnostics
+s/ *//g
+/^$/d
+/[^a-z:]/{a\
+Illegal characters: use only a-z and ":". Try again.
+d
+}
+/^:[a-z]*:[a-z]*:[a-z]*:$/!{a\
+Incorrect format: use\
+\ : string1 : string2 : string3 :<CR><CR>\
+Try again.
+d
+}
+/\([a-z]\).*\1/{a\
+Repeated letters not allowed. Try again.
+d
+}
+# initial formatting
+h
+s/[a-z]/ /g
+G
+s/^:\( *\):\( *\):\( *\):\n:\([a-z]*\):\([a-z]*\):\([a-z]*\):$/:1\4\2\3:2\5\1\3:3\6\1\2:0/
+s/[a-z]/&2/g
+s/^/abcdefghijklmnopqrstuvwxyz/
+:a
+s/^\(.\).*\1.*/&\1/
+s/.//
+/^[^:]/ba
+s/\([^0]*\)\(:0.*\)/\2\1:/
+s/^[^0]*0\(.\)/\1&/
+:b
+# outputting current state without markers
+h
+s/.*:1/:/
+s/[123]//gp
+g
+:c
+# establishing destinations
+/^\(.\).*\1:1/td
+/^\(.\).*:1[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:1[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+/^\(.\).*:1[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:2[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+/^\(.\).*:2[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:2[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:3[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/
+/^\(.\).*:3[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/
+/^\(.\).*:3[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/
+bc
+# iterate back to find smallest out-of-place ring
+:d
+s/^\(.\)\(:0[^:]*\([^:]\)\1.*:\([123]\)[^:]*\1\)\4/\3\2\4/
+td
+# move said ring (right, resp. left)
+s/^\(.\)\(.*\)\1\([23]\)\(.*:\3[^ ]*\) /\1\2 \4\1\3/
+s/^\(.\)\(.*:\([12]\)[^ ]*\) \(.*\)\1\3/\1\2\1\3\4 /
+tb
+s/.*/Done! Try another, or end with ^D./p
+d
diff --git a/usr.bin/sed/TEST/math.sed b/usr.bin/sed/TEST/math.sed
new file mode 100644
index 0000000..8e7bf51
--- /dev/null
+++ b/usr.bin/sed/TEST/math.sed
@@ -0,0 +1,163 @@
+#
+# @(#)math.sed 8.1 (Berkeley) 6/6/93
+#
+# Addition and multiplication in sed.
+# ++ for a limited time only do (expr) too!!!
+#
+# Kevin S Braunsdorf, PUCC UNIX Group, ksb@cc.purdue.edu.
+#
+# Ex:
+# echo "4+7*3" | sed -f %f
+
+# make sure the expression is well formed
+s/[ ]//g
+/[+*\/-]$/{
+ a\
+ poorly formed expression, operator on the end
+ q
+}
+/^[+*\/]/{
+ a\
+ poorly formed expression, leading operator
+ q
+}
+
+# fill hold space with done token
+x
+s/^.*/done/
+x
+
+# main loop, process operators (*, + and () )
+: loop
+/^\+/{
+ s///
+ b loop
+}
+/^\(.*\)(\([^)]*\))\(.*\)$/{
+ H
+ s//\2/
+ x
+ s/^\(.*\)\n\(.*\)(\([^()]*\))\(.*\)$/()\2@\4@\1/
+ x
+ b loop
+}
+/^[0-9]*\*/b mul
+/^\([0-9]*\)\+\([0-9+*]*\*[0-9]*\)$/{
+ s//\2+\1/
+ b loop
+}
+/^[0-9]*\+/{
+ s/$/=/
+ b add
+}
+x
+/^done$/{
+ x
+ p
+ d
+}
+/^()/{
+ s///
+ x
+ G
+ s/\(.*\)\n\([^@]*\)@\([^@]*\)@\(.*\)/\2\1\3/
+ x
+ s/[^@]*@[^@]*@\(.*\)/\1/
+ x
+ b loop
+}
+i\
+help, stack problem
+p
+x
+p
+q
+
+# turn mul into add until 1*x -> x
+: mul
+/^0*1\*/{
+ s///
+ b loop
+}
+/^\([0-9]*\)0\*/{
+ s/^\([0-9]*\)0\*\([0-9]*\)/\1*\20/
+ b mul
+}
+s/^\([0-9]*\)1\*/\10*/
+s/^\([0-9]*\)2\*/\11*/
+s/^\([0-9]*\)3\*/\12*/
+s/^\([0-9]*\)4\*/\13*/
+s/^\([0-9]*\)5\*/\14*/
+s/^\([0-9]*\)6\*/\15*/
+s/^\([0-9]*\)7\*/\16*/
+s/^\([0-9]*\)8\*/\17*/
+s/^\([0-9]*\)9\*/\18*/
+s/\*\([0-9*]*\)/*\1+\1/
+b mul
+
+# get rid of a plus term until 0+x -> x
+: add
+/^\+\([0-9+*]*\)=/{
+ s//\1/
+ b loop
+}
+/^\([0-9*]*\)\+=/{
+ s//\1/
+ b loop
+}
+/^\([0-9]*\)\+\([0-9*+]*\)\+=/{
+ s//\2+\1/
+ b loop
+}
+/^\([0-9]*\)0\+\([0-9]*\)\([0-9]\)=/{
+ s//\1+\2=\3/
+ b add
+}
+/^\([0-9]*\)\([0-9]\)\+\([0-9]*\)0=/{
+ s//\1+\3=\2/
+ b add
+}
+/^\([0-9]*\)0\+\([0-9*+]*\)\+\([0-9]*\)\([0-9]\)=/{
+ s//\1+\2+\3=\4/
+ b add
+}
+/^\([0-9]*\)\([0-9]\)\+\([0-9*+]*\)\+\([0-9]*\)0=/{
+ s//\1+\3+\4=\2/
+ b add
+}
+s/^\([0-9]*\)1\+/\10+/
+s/^\([0-9]*\)2\+/\11+/
+s/^\([0-9]*\)3\+/\12+/
+s/^\([0-9]*\)4\+/\13+/
+s/^\([0-9]*\)5\+/\14+/
+s/^\([0-9]*\)6\+/\15+/
+s/^\([0-9]*\)7\+/\16+/
+s/^\([0-9]*\)8\+/\17+/
+s/^\([0-9]*\)9\+/\18+/
+
+s/9=\([0-9]*\)$/_=\1/
+s/8=\([0-9]*\)$/9=\1/
+s/7=\([0-9]*\)$/8=\1/
+s/6=\([0-9]*\)$/7=\1/
+s/5=\([0-9]*\)$/6=\1/
+s/4=\([0-9]*\)$/5=\1/
+s/3=\([0-9]*\)$/4=\1/
+s/2=\([0-9]*\)$/3=\1/
+s/1=\([0-9]*\)$/2=\1/
+/_/{
+ s//_0/
+ : inc
+ s/9_/_0/
+ s/8_/9/
+ s/7_/8/
+ s/6_/7/
+ s/5_/6/
+ s/4_/5/
+ s/3_/4/
+ s/2_/3/
+ s/1_/2/
+ s/0_/1/
+ s/\+_/+1/
+ /_/b inc
+}
+b add
diff --git a/usr.bin/sed/TEST/sed.test b/usr.bin/sed/TEST/sed.test
new file mode 100644
index 0000000..e91036f
--- /dev/null
+++ b/usr.bin/sed/TEST/sed.test
@@ -0,0 +1,552 @@
+#!/bin/sh -
+#
+# Copyright (c) 1992 Diomidis Spinellis.
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)sed.test 8.1 (Berkeley) 6/6/93
+#
+
+# sed Regression Tests
+#
+# The following files are created:
+# lines[1-4], script1, script2
+# Two directories *.out contain the test results
+
+main()
+{
+ BASE=/usr/old/bin/sed
+ BASELOG=sed.out
+ TEST=../obj/sed
+ TESTLOG=nsed.out
+ DICT=/usr/share/dict/words
+
+ test_error | more
+
+ awk 'END { for (i = 1; i < 15; i++) print "l1_" i}' </dev/null >lines1
+ awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' </dev/null >lines2
+
+ exec 4>&1 5>&2
+
+ # Set these flags to get messages about known problems
+ BSD=1
+ GNU=0
+ SUN=0
+ tests $BASE $BASELOG
+
+ BSD=0
+ GNU=0
+ SUN=0
+ tests $TEST $TESTLOG
+ exec 1>&4 2>&5
+ diff -c $BASELOG $TESTLOG | more
+}
+
+tests()
+{
+ SED=$1
+ DIR=$2
+ rm -rf $DIR
+ mkdir $DIR
+ MARK=100
+
+ test_args
+ test_addr
+ echo Testing commands
+ test_group
+ test_acid
+ test_branch
+ test_pattern
+ test_print
+ test_subst
+}
+
+mark()
+{
+ MARK=`expr $MARK + 1`
+ exec 1>&4 2>&5
+ exec >"$DIR/${MARK}_$1"
+ echo "Test $1:$MARK"
+ # Uncomment this line to match tests with sed error messages
+ echo "Test $1:$MARK" >&5
+}
+
+test_args()
+{
+ mark '1.1'
+ echo Testing argument parsing
+ echo First type
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED 's/^/e1_/p' lines1
+ fi
+ mark '1.2' ; $SED -n 's/^/e1_/p' lines1
+ mark '1.3'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED 's/^/e1_/p' <lines1
+ fi
+ mark '1.4' ; $SED -n 's/^/e1_/p' <lines1
+ echo Second type
+ mark '1.4.1'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed fails this
+ fi
+ $SED -e '' <lines1
+ echo 's/^/s1_/p' >script1
+ echo 's/^/s2_/p' >script2
+ mark '1.5'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 lines1
+ fi
+ mark '1.6'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 <lines1
+ fi
+ mark '1.7'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' lines1
+ fi
+ mark '1.8'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' <lines1
+ fi
+ mark '1.9' ; $SED -n -f script1 lines1
+ mark '1.10' ; $SED -n -f script1 <lines1
+ mark '1.11' ; $SED -n -e 's/^/e1_/p' lines1
+ mark '1.12'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -n -e 's/^/e1_/p' <lines1
+ fi
+ mark '1.13'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' -e 's/^/e2_/p' lines1
+ fi
+ mark '1.14'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -f script1 -f script2 lines1
+ fi
+ mark '1.15'
+ if [ $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo GNU and SunOS sed fail this following older POSIX draft
+ else
+ $SED -e 's/^/e1_/p' -f script1 lines1
+ fi
+ mark '1.16'
+ if [ $SUN -eq 1 ] ; then
+ echo SunOS sed prints only with -n
+ else
+ $SED -e 's/^/e1_/p' lines1 lines1
+ fi
+ # POSIX D11.2:11251
+ mark '1.17' ; $SED p <lines1 lines1
+cat >script1 <<EOF
+#n
+# A comment
+
+p
+EOF
+ mark '1.18' ; $SED -f script1 <lines1 lines1
+}
+
+test_addr()
+{
+ echo Testing address ranges
+ mark '2.1' ; $SED -n -e '4p' lines1
+ mark '2.2' ; $SED -n -e '20p' lines1 lines2
+ mark '2.3' ; $SED -n -e '$p' lines1
+ mark '2.4' ; $SED -n -e '$p' lines1 lines2
+ mark '2.5' ; $SED -n -e '$a\
+hello' /dev/null
+ mark '2.6' ; $SED -n -e '$p' lines1 /dev/null lines2
+ # Should not print anything
+ mark '2.7' ; $SED -n -e '20p' lines1
+ mark '2.8' ; $SED -n -e '0p' lines1
+ mark '2.9' ; $SED -n '/l1_7/p' lines1
+ mark '2.10' ; $SED -n ' /l1_7/ p' lines1
+ mark '2.11'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '\_l1\_7_p' lines1
+ mark '2.12' ; $SED -n '1,4p' lines1
+ mark '2.13' ; $SED -n '1,$p' lines1 lines2
+ mark '2.14' ; $SED -n '1,/l2_9/p' lines1 lines2
+ mark '2.15' ; $SED -n '/4/,$p' lines1 lines2
+ mark '2.16' ; $SED -n '/4/,20p' lines1 lines2
+ mark '2.17' ; $SED -n '/4/,/10/p' lines1 lines2
+ mark '2.18' ; $SED -n '/l2_3/,/l1_8/p' lines1 lines2
+ mark '2.19'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '12,3p' lines1 lines2
+ mark '2.20'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n '/l1_7/,3p' lines1 lines2
+}
+
+test_group()
+{
+ echo Brace and other grouping
+ mark '3.1' ; $SED -e '
+4,12 {
+ s/^/^/
+ s/$/$/
+ s/_/T/
+}' lines1
+ mark '3.2' ; $SED -e '
+4,12 {
+ s/^/^/
+ /6/,/10/ {
+ s/$/$/
+ /8/ s/_/T/
+ }
+}' lines1
+ mark '3.3' ; $SED -e '
+4,12 !{
+ s/^/^/
+ /6/,/10/ !{
+ s/$/$/
+ /8/ !s/_/T/
+ }
+}' lines1
+ mark '3.4' ; $SED -e '4,12!s/^/^/' lines1
+}
+
+test_acid()
+{
+ echo Testing a c d and i commands
+ mark '4.1' ; $SED -n -e '
+s/^/before_i/p
+20i\
+inserted
+s/^/after_i/p
+' lines1 lines2
+ mark '4.2' ; $SED -n -e '
+5,12s/^/5-12/
+s/^/before_a/p
+/5-12/a\
+appended
+s/^/after_a/p
+' lines1 lines2
+ mark '4.3'
+ if [ $GNU -eq 1 ] ; then
+ echo GNU sed fails this
+ fi
+ $SED -n -e '
+s/^/^/p
+/l1_/a\
+appended
+8,10N
+s/$/$/p
+' lines1 lines2
+ mark '4.4' ; $SED -n -e '
+c\
+hello
+' lines1
+ mark '4.5' ; $SED -n -e '
+8c\
+hello
+' lines1
+ mark '4.6' ; $SED -n -e '
+3,14c\
+hello
+' lines1
+# SunOS and GNU sed behave differently. We follow POSIX
+# mark '4.7' ; $SED -n -e '
+#8,3c\
+#hello
+#' lines1
+ mark '4.8' ; $SED d <lines1
+}
+
+test_branch()
+{
+ echo Testing labels and branching
+ mark '5.1' ; $SED -n -e '
+b label4
+:label3
+s/^/label3_/p
+b end
+:label4
+2,12b label1
+b label2
+:label1
+s/^/label1_/p
+b
+:label2
+s/^/label2_/p
+b label3
+:end
+' lines1
+ mark '5.2'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ $SED -n -e '
+s/l1_/l2_/
+t ok
+b
+:ok
+s/^/tested /p
+' lines1 lines2
+# SunOS sed behaves differently here. Clarification needed.
+# mark '5.3' ; $SED -n -e '
+#5,8b inside
+#1,5 {
+# s/^/^/p
+# :inside
+# s/$/$/p
+#}
+#' lines1
+# Check that t clears the substitution done flag
+ mark '5.4' ; $SED -n -e '
+1,8s/^/^/
+t l1
+:l1
+t l2
+s/$/$/p
+b
+:l2
+s/^/ERROR/
+' lines1
+# Check that reading a line clears the substitution done flag
+ mark '5.5'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed fails this test
+ fi
+ $SED -n -e '
+t l2
+1,8s/^/^/p
+2,7N
+b
+:l2
+s/^/ERROR/p
+' lines1
+ mark '5.6' ; $SED 5q lines1
+ mark '5.7' ; $SED -e '
+5i\
+hello
+5q' lines1
+# Branch across block boundary
+ mark '5.8' ; $SED -e '
+{
+:b
+}
+s/l/m/
+tb' lines1
+}
+
+test_pattern()
+{
+echo Pattern space commands
+# Check that the pattern space is deleted
+ mark '6.1' ; $SED -n -e '
+c\
+changed
+p
+' lines1
+ mark '6.2' ; $SED -n -e '
+4d
+p
+' lines1
+# SunOS sed refused to print here
+# mark '6.3' ; $SED -e '
+#N
+#N
+#N
+#D
+#P
+#4p
+#' lines1
+ mark '6.4' ; $SED -e '
+2h
+3H
+4g
+5G
+6x
+6p
+6x
+6p
+' lines1
+ mark '6.5' ; $SED -e '4n' lines1
+ mark '6.6' ; $SED -n -e '4n' lines1
+}
+
+test_print()
+{
+ echo Testing print and file routines
+ awk 'END {for (i = 1; i < 256; i++) printf("%c", i);print "\n"}' \
+ </dev/null >lines3
+ # GNU and SunOS sed behave differently here
+ mark '7.1'
+ if [ $BSD -eq 1 ] ; then
+ echo 'BSD sed drops core on this one; TEST SKIPPED'
+ else
+ $SED -n l lines3
+ fi
+ mark '7.2' ; $SED -e '/l2_/=' lines1 lines2
+ rm -f lines4
+ mark '7.3' ; $SED -e '3,12w lines4' lines1
+ echo w results
+ cat lines4
+ mark '7.4' ; $SED -e '4r lines2' lines1
+ mark '7.5' ; $SED -e '5r /dev/dds' lines1
+ mark '7.6' ; $SED -e '6r /dev/null' lines1
+ mark '7.7'
+ if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo BSD, GNU and SunOS cannot pass this one
+ else
+ sed '200q' $DICT | sed 's$.*$s/^/&/w tmpdir/&$' >script1
+ rm -rf tmpdir
+ mkdir tmpdir
+ $SED -f script1 lines1
+ cat tmpdir/*
+ rm -rf tmpdir
+ fi
+ mark '7.8'
+ if [ $BSD -eq 1 ] ; then
+ echo BSD sed cannot pass 7.7
+ else
+ echo line1 > lines3
+ echo "" >> lines3
+ $SED -n -e '$p' lines3 /dev/null
+ fi
+
+}
+
+test_subst()
+{
+ echo Testing substitution commands
+ mark '8.1' ; $SED -e 's/./X/g' lines1
+ mark '8.2' ; $SED -e 's,.,X,g' lines1
+# GNU and SunOS sed thinks we are escaping . as wildcard, not as separator
+# mark '8.3' ; $SED -e 's.\..X.g' lines1
+# POSIX does not say that this should work
+# mark '8.4' ; $SED -e 's/[/]/Q/' lines1
+ mark '8.4' ; $SED -e 's/[\/]/Q/' lines1
+ mark '8.5' ; $SED -e 's_\__X_' lines1
+ mark '8.6' ; $SED -e 's/./(&)/g' lines1
+ mark '8.7' ; $SED -e 's/./(\&)/g' lines1
+ mark '8.8' ; $SED -e 's/\(.\)\(.\)\(.\)/x\3x\2x\1/g' lines1
+ mark '8.9' ; $SED -e 's/_/u0\
+u1\
+u2/g' lines1
+ mark '8.10'
+ if [ $BSD -eq 1 -o $GNU -eq 1 ] ; then
+ echo 'BSD/GNU sed do not understand digit flags on s commands'
+ fi
+ $SED -e 's/./X/4' lines1
+ rm -f lines4
+ mark '8.11' ; $SED -e 's/1/X/w lines4' lines1
+ echo s wfile results
+ cat lines4
+ mark '8.12' ; $SED -e 's/[123]/X/g' lines1
+ mark '8.13' ; $SED -e 'y/0123456789/9876543210/' lines1
+ mark '8.14' ;
+ if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then
+ echo BSD/GNU/SUN sed fail this test
+ else
+ $SED -e 'y10\123456789198765432\101' lines1
+ fi
+ mark '8.15' ; $SED -e '1N;2y/\n/X/' lines1
+ mark '8.16'
+ if [ $BSD -eq 1 ] ; then
+ echo 'BSD sed does not handle branch defined REs'
+ else
+ echo 'eeefff' | $SED -e 'p' -e 's/e/X/p' -e ':x' \
+ -e 's//Y/p' -e '/f/bx'
+ fi
+}
+
+test_error()
+{
+ exec 0>&3 4>&1 5>&2
+ exec 0</dev/null
+ exec 2>&1
+ set -x
+ $TEST -x && exit 1
+ $TEST -f && exit 1
+ $TEST -e && exit 1
+ $TEST -f /dev/dds && exit 1
+ $TEST p /dev/dds && exit 1
+ $TEST -f /bin/sh && exit 1
+ $TEST '{' && exit 1
+ $TEST '{' && exit 1
+ $TEST '/hello/' && exit 1
+ $TEST '1,/hello/' && exit 1
+ $TEST -e '-5p' && exit 1
+ $TEST '/jj' && exit 1
+ $TEST 'a hello' && exit 1
+ $TEST 'a \ hello' && exit 1
+ $TEST 'b foo' && exit 1
+ $TEST 'd hello' && exit 1
+ $TEST 's/aa' && exit 1
+ $TEST 's/aa/' && exit 1
+ $TEST 's/a/b' && exit 1
+ $TEST 's/a/b/c/d' && exit 1
+ $TEST 's/a/b/ 1 2' && exit 1
+ $TEST 's/a/b/ 1 g' && exit 1
+ $TEST 's/a/b/w' && exit 1
+ $TEST 'y/aa' && exit 1
+ $TEST 'y/aa/b/' && exit 1
+ $TEST 'y/aa/' && exit 1
+ $TEST 'y/a/b' && exit 1
+ $TEST 'y/a/b/c/d' && exit 1
+ $TEST '!' && exit 1
+ $TEST supercalifrangolisticexprialidociussupercalifrangolisticexcius
+ set +x
+ exec 0>&3 1>&4 2>&5
+}
+
+main
diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c
new file mode 100644
index 0000000..9f13fd7
--- /dev/null
+++ b/usr.bin/sed/compile.c
@@ -0,0 +1,810 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "defs.h"
+#include "extern.h"
+
+#define LHSZ 128
+#define LHMASK (LHSZ - 1)
+static struct labhash {
+ struct labhash *lh_next;
+ u_int lh_hash;
+ struct s_command *lh_cmd;
+ int lh_ref;
+} *labels[LHSZ];
+
+static char *compile_addr __P((char *, struct s_addr *));
+static char *compile_ccl __P((char **, char *));
+static char *compile_delimited __P((char *, char *));
+static char *compile_flags __P((char *, struct s_subst *));
+static char *compile_re __P((char *, regex_t **));
+static char *compile_subst __P((char *, struct s_subst *));
+static char *compile_text __P((void));
+static char *compile_tr __P((char *, char **));
+static struct s_command
+ **compile_stream __P((struct s_command **));
+static char *duptoeol __P((char *, char *));
+static void enterlabel __P((struct s_command *));
+static struct s_command
+ *findlabel __P((char *));
+static void fixuplabel __P((struct s_command *, struct s_command *));
+static void uselabel __P((void));
+
+/*
+ * Command specification. This is used to drive the command parser.
+ */
+struct s_format {
+ char code; /* Command code */
+ int naddr; /* Number of address args */
+ enum e_args args; /* Argument type */
+};
+
+static struct s_format cmd_fmts[] = {
+ {'{', 2, GROUP},
+ {'}', 0, ENDGROUP},
+ {'a', 1, TEXT},
+ {'b', 2, BRANCH},
+ {'c', 2, TEXT},
+ {'d', 2, EMPTY},
+ {'D', 2, EMPTY},
+ {'g', 2, EMPTY},
+ {'G', 2, EMPTY},
+ {'h', 2, EMPTY},
+ {'H', 2, EMPTY},
+ {'i', 1, TEXT},
+ {'l', 2, EMPTY},
+ {'n', 2, EMPTY},
+ {'N', 2, EMPTY},
+ {'p', 2, EMPTY},
+ {'P', 2, EMPTY},
+ {'q', 1, EMPTY},
+ {'r', 1, RFILE},
+ {'s', 2, SUBST},
+ {'t', 2, BRANCH},
+ {'w', 2, WFILE},
+ {'x', 2, EMPTY},
+ {'y', 2, TR},
+ {'!', 2, NONSEL},
+ {':', 0, LABEL},
+ {'#', 0, COMMENT},
+ {'=', 1, EMPTY},
+ {'\0', 0, COMMENT},
+};
+
+/* The compiled program. */
+struct s_command *prog;
+
+/*
+ * Compile the program into prog.
+ * Initialise appends.
+ */
+void
+compile()
+{
+ *compile_stream(&prog) = NULL;
+ fixuplabel(prog, NULL);
+ uselabel();
+ appends = xmalloc(sizeof(struct s_appends) * appendnum);
+ match = xmalloc((maxnsub + 1) * sizeof(regmatch_t));
+}
+
+#define EATSPACE() do { \
+ if (p) \
+ while (*p && isascii(*p) && isspace(*p)) \
+ p++; \
+ } while (0)
+
+static struct s_command **
+compile_stream(link)
+ struct s_command **link;
+{
+ register char *p;
+ static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */
+ struct s_command *cmd, *cmd2, *stack;
+ struct s_format *fp;
+ int naddr; /* Number of addresses */
+
+ stack = 0;
+ for (;;) {
+ if ((p = cu_fgets(lbuf, sizeof(lbuf))) == NULL) {
+ if (stack != 0)
+ err(COMPILE, "unexpected EOF (pending }'s)");
+ return (link);
+ }
+
+semicolon: EATSPACE();
+ if (p && (*p == '#' || *p == '\0'))
+ continue;
+ *link = cmd = xmalloc(sizeof(struct s_command));
+ link = &cmd->next;
+ cmd->nonsel = cmd->inrange = 0;
+ /* First parse the addresses */
+ naddr = 0;
+
+/* Valid characters to start an address */
+#define addrchar(c) (strchr("0123456789/\\$", (c)))
+ if (addrchar(*p)) {
+ naddr++;
+ cmd->a1 = xmalloc(sizeof(struct s_addr));
+ p = compile_addr(p, cmd->a1);
+ EATSPACE(); /* EXTENSION */
+ if (*p == ',') {
+ p++;
+ EATSPACE(); /* EXTENSION */
+ naddr++;
+ cmd->a2 = xmalloc(sizeof(struct s_addr));
+ p = compile_addr(p, cmd->a2);
+ EATSPACE();
+ } else
+ cmd->a2 = 0;
+ } else
+ cmd->a1 = cmd->a2 = 0;
+
+nonsel: /* Now parse the command */
+ if (!*p)
+ err(COMPILE, "command expected");
+ cmd->code = *p;
+ for (fp = cmd_fmts; fp->code; fp++)
+ if (fp->code == *p)
+ break;
+ if (!fp->code)
+ err(COMPILE, "invalid command code %c", *p);
+ if (naddr > fp->naddr)
+ err(COMPILE,
+"command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr);
+ switch (fp->args) {
+ case NONSEL: /* ! */
+ p++;
+ EATSPACE();
+ cmd->nonsel = ! cmd->nonsel;
+ goto nonsel;
+ case GROUP: /* { */
+ p++;
+ EATSPACE();
+ cmd->next = stack;
+ stack = cmd;
+ link = &cmd->u.c;
+ if (*p)
+ goto semicolon;
+ break;
+ case ENDGROUP:
+ /*
+ * Short-circuit command processing, since end of
+ * group is really just a noop.
+ */
+ cmd->nonsel = 1;
+ if (stack == 0)
+ err(COMPILE, "unexpected }");
+ cmd2 = stack;
+ stack = cmd2->next;
+ cmd2->next = cmd;
+ /*FALLTHROUGH*/
+ case EMPTY: /* d D g G h H l n N p P q x = \0 */
+ p++;
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ if (*p)
+ err(COMPILE,
+"extra characters at the end of %c command", cmd->code);
+ break;
+ case TEXT: /* a c i */
+ p++;
+ EATSPACE();
+ if (*p != '\\')
+ err(COMPILE,
+"command %c expects \\ followed by text", cmd->code);
+ p++;
+ EATSPACE();
+ if (*p)
+ err(COMPILE,
+"extra characters after \\ at the end of %c command", cmd->code);
+ cmd->t = compile_text();
+ break;
+ case COMMENT: /* \0 # */
+ break;
+ case WFILE: /* w */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ err(COMPILE, "filename expected");
+ cmd->t = duptoeol(p, "w command");
+ if (aflag)
+ cmd->u.fd = -1;
+ else if ((cmd->u.fd = open(p,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(FATAL, "%s: %s\n", p, strerror(errno));
+ break;
+ case RFILE: /* r */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ err(COMPILE, "filename expected");
+ else
+ cmd->t = duptoeol(p, "read command");
+ break;
+ case BRANCH: /* b t */
+ p++;
+ EATSPACE();
+ if (*p == '\0')
+ cmd->t = NULL;
+ else
+ cmd->t = duptoeol(p, "branch");
+ break;
+ case LABEL: /* : */
+ p++;
+ EATSPACE();
+ cmd->t = duptoeol(p, "label");
+ if (strlen(p) == 0)
+ err(COMPILE, "empty label");
+ enterlabel(cmd);
+ break;
+ case SUBST: /* s */
+ p++;
+ if (*p == '\0' || *p == '\\')
+ err(COMPILE,
+"substitute pattern can not be delimited by newline or backslash");
+ cmd->u.s = xmalloc(sizeof(struct s_subst));
+ p = compile_re(p, &cmd->u.s->re);
+ if (p == NULL)
+ err(COMPILE, "unterminated substitute pattern");
+ --p;
+ p = compile_subst(p, cmd->u.s);
+ p = compile_flags(p, cmd->u.s);
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ break;
+ case TR: /* y */
+ p++;
+ p = compile_tr(p, (char **)&cmd->u.y);
+ EATSPACE();
+ if (*p == ';') {
+ p++;
+ link = &cmd->next;
+ goto semicolon;
+ }
+ if (*p)
+ err(COMPILE,
+"extra text at the end of a transform command");
+ break;
+ }
+ }
+}
+
+/*
+ * Get a delimited string. P points to the delimeter of the string; d points
+ * to a buffer area. Newline and delimiter escapes are processed; other
+ * escapes are ignored.
+ *
+ * Returns a pointer to the first character after the final delimiter or NULL
+ * in the case of a non-terminated string. The character array d is filled
+ * with the processed string.
+ */
+static char *
+compile_delimited(p, d)
+ char *p, *d;
+{
+ char c;
+
+ c = *p++;
+ if (c == '\0')
+ return (NULL);
+ else if (c == '\\')
+ err(COMPILE, "\\ can not be used as a string delimiter");
+ else if (c == '\n')
+ err(COMPILE, "newline can not be used as a string delimiter");
+ while (*p) {
+ if (*p == '[') {
+ if ((d = compile_ccl(&p, d)) == NULL)
+ err(COMPILE, "unbalanced brackets ([])");
+ continue;
+ } else if (*p == '\\' && p[1] == '[') {
+ *d++ = *p++;
+ } else if (*p == '\\' && p[1] == c)
+ p++;
+ else if (*p == '\\' && p[1] == 'n') {
+ *d++ = '\n';
+ p += 2;
+ continue;
+ } else if (*p == '\\' && p[1] == '\\')
+ *d++ = *p++;
+ else if (*p == c) {
+ *d = '\0';
+ return (p + 1);
+ }
+ *d++ = *p++;
+ }
+ return (NULL);
+}
+
+
+/* compile_ccl: expand a POSIX character class */
+static char *
+compile_ccl(sp, t)
+ char **sp;
+ char *t;
+{
+ int c, d;
+ char *s = *sp;
+
+ *t++ = *s++;
+ if (*s == '^')
+ *t++ = *s++;
+ if (*s == ']')
+ *t++ = *s++;
+ for (; *s && (*t = *s) != ']'; s++, t++)
+ if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) {
+ *++t = *++s, t++, s++;
+ for (c = *s; (*t = *s) != ']' || c != d; s++, t++)
+ if ((c = *s) == '\0')
+ return NULL;
+ } else if (*s == '\\' && s[1] == 'n')
+ *t = '\n', s++;
+ return (*s == ']') ? *sp = ++s, ++t : NULL;
+}
+
+/*
+ * Get a regular expression. P points to the delimiter of the regular
+ * expression; repp points to the address of a regexp pointer. Newline
+ * and delimiter escapes are processed; other escapes are ignored.
+ * Returns a pointer to the first character after the final delimiter
+ * or NULL in the case of a non terminated regular expression. The regexp
+ * pointer is set to the compiled regular expression.
+ * Cflags are passed to regcomp.
+ */
+static char *
+compile_re(p, repp)
+ char *p;
+ regex_t **repp;
+{
+ int eval;
+ char re[_POSIX2_LINE_MAX + 1];
+
+ p = compile_delimited(p, re);
+ if (p && strlen(re) == 0) {
+ *repp = NULL;
+ return (p);
+ }
+ *repp = xmalloc(sizeof(regex_t));
+ if (p && (eval = regcomp(*repp, re, 0)) != 0)
+ err(COMPILE, "RE error: %s", strregerror(eval, *repp));
+ if (maxnsub < (*repp)->re_nsub)
+ maxnsub = (*repp)->re_nsub;
+ return (p);
+}
+
+/*
+ * Compile the substitution string of a regular expression and set res to
+ * point to a saved copy of it. Nsub is the number of parenthesized regular
+ * expressions.
+ */
+static char *
+compile_subst(p, s)
+ char *p;
+ struct s_subst *s;
+{
+ static char lbuf[_POSIX2_LINE_MAX + 1];
+ int asize, ref, size;
+ char c, *text, *op, *sp;
+
+ c = *p++; /* Terminator character */
+ if (c == '\0')
+ return (NULL);
+
+ s->maxbref = 0;
+ s->linenum = linenum;
+ asize = 2 * _POSIX2_LINE_MAX + 1;
+ text = xmalloc(asize);
+ size = 0;
+ do {
+ op = sp = text + size;
+ for (; *p; p++) {
+ if (*p == '\\') {
+ p++;
+ if (strchr("123456789", *p) != NULL) {
+ *sp++ = '\\';
+ ref = *p - '0';
+ if (s->re != NULL &&
+ ref > s->re->re_nsub)
+ err(COMPILE,
+"\\%c not defined in the RE", *p);
+ if (s->maxbref < ref)
+ s->maxbref = ref;
+ } else if (*p == '&' || *p == '\\')
+ *sp++ = '\\';
+ } else if (*p == c) {
+ p++;
+ *sp++ = '\0';
+ size += sp - op;
+ s->new = xrealloc(text, size);
+ return (p);
+ } else if (*p == '\n') {
+ err(COMPILE,
+"unescaped newline inside substitute pattern");
+ /* NOTREACHED */
+ }
+ *sp++ = *p;
+ }
+ size += sp - op;
+ if (asize - size < _POSIX2_LINE_MAX + 1) {
+ asize *= 2;
+ text = xmalloc(asize);
+ }
+ } while (cu_fgets(p = lbuf, sizeof(lbuf)));
+ err(COMPILE, "unterminated substitute in regular expression");
+ /* NOTREACHED */
+}
+
+/*
+ * Compile the flags of the s command
+ */
+static char *
+compile_flags(p, s)
+ char *p;
+ struct s_subst *s;
+{
+ int gn; /* True if we have seen g or n */
+ char wfile[_POSIX2_LINE_MAX + 1], *q;
+
+ s->n = 1; /* Default */
+ s->p = 0;
+ s->wfile = NULL;
+ s->wfd = -1;
+ for (gn = 0;;) {
+ EATSPACE(); /* EXTENSION */
+ switch (*p) {
+ case 'g':
+ if (gn)
+ err(COMPILE,
+"more than one number or 'g' in substitute flags");
+ gn = 1;
+ s->n = 0;
+ break;
+ case '\0':
+ case '\n':
+ case ';':
+ return (p);
+ case 'p':
+ s->p = 1;
+ break;
+ case '1': case '2': case '3':
+ case '4': case '5': case '6':
+ case '7': case '8': case '9':
+ if (gn)
+ err(COMPILE,
+"more than one number or 'g' in substitute flags");
+ gn = 1;
+ /* XXX Check for overflow */
+ s->n = (int)strtol(p, &p, 10);
+ break;
+ case 'w':
+ p++;
+#ifdef HISTORIC_PRACTICE
+ if (*p != ' ') {
+ err(WARNING, "space missing before w wfile");
+ return (p);
+ }
+#endif
+ EATSPACE();
+ q = wfile;
+ while (*p) {
+ if (*p == '\n')
+ break;
+ *q++ = *p++;
+ }
+ *q = '\0';
+ if (q == wfile)
+ err(COMPILE, "no wfile specified");
+ s->wfile = strdup(wfile);
+ if (!aflag && (s->wfd = open(wfile,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(FATAL, "%s: %s\n", wfile, strerror(errno));
+ return (p);
+ default:
+ err(COMPILE,
+ "bad flag in substitute command: '%c'", *p);
+ break;
+ }
+ p++;
+ }
+}
+
+/*
+ * Compile a translation set of strings into a lookup table.
+ */
+static char *
+compile_tr(p, transtab)
+ char *p;
+ char **transtab;
+{
+ int i;
+ char *lt, *op, *np;
+ char old[_POSIX2_LINE_MAX + 1];
+ char new[_POSIX2_LINE_MAX + 1];
+
+ if (*p == '\0' || *p == '\\')
+ err(COMPILE,
+"transform pattern can not be delimited by newline or backslash");
+ p = compile_delimited(p, old);
+ if (p == NULL) {
+ err(COMPILE, "unterminated transform source string");
+ return (NULL);
+ }
+ p = compile_delimited(--p, new);
+ if (p == NULL) {
+ err(COMPILE, "unterminated transform target string");
+ return (NULL);
+ }
+ EATSPACE();
+ if (strlen(new) != strlen(old)) {
+ err(COMPILE, "transform strings are not the same length");
+ return (NULL);
+ }
+ /* We assume characters are 8 bits */
+ lt = xmalloc(UCHAR_MAX);
+ for (i = 0; i <= UCHAR_MAX; i++)
+ lt[i] = (char)i;
+ for (op = old, np = new; *op; op++, np++)
+ lt[(u_char)*op] = *np;
+ *transtab = lt;
+ return (p);
+}
+
+/*
+ * Compile the text following an a or i command.
+ */
+static char *
+compile_text()
+{
+ int asize, size;
+ char *text, *p, *op, *s;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ asize = 2 * _POSIX2_LINE_MAX + 1;
+ text = xmalloc(asize);
+ size = 0;
+ while (cu_fgets(lbuf, sizeof(lbuf))) {
+ op = s = text + size;
+ p = lbuf;
+ EATSPACE();
+ for (; *p; p++) {
+ if (*p == '\\')
+ p++;
+ *s++ = *p;
+ }
+ size += s - op;
+ if (p[-2] != '\\') {
+ *s = '\0';
+ break;
+ }
+ if (asize - size < _POSIX2_LINE_MAX + 1) {
+ asize *= 2;
+ text = xmalloc(asize);
+ }
+ }
+ return (xrealloc(text, size + 1));
+}
+
+/*
+ * Get an address and return a pointer to the first character after
+ * it. Fill the structure pointed to according to the address.
+ */
+static char *
+compile_addr(p, a)
+ char *p;
+ struct s_addr *a;
+{
+ char *end;
+
+ switch (*p) {
+ case '\\': /* Context address */
+ ++p;
+ /* FALLTHROUGH */
+ case '/': /* Context address */
+ p = compile_re(p, &a->u.r);
+ if (p == NULL)
+ err(COMPILE, "unterminated regular expression");
+ a->type = AT_RE;
+ return (p);
+
+ case '$': /* Last line */
+ a->type = AT_LAST;
+ return (p + 1);
+ /* Line number */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ a->type = AT_LINE;
+ a->u.l = strtol(p, &end, 10);
+ return (end);
+ default:
+ err(COMPILE, "expected context address");
+ return (NULL);
+ }
+}
+
+/*
+ * duptoeol --
+ * Return a copy of all the characters up to \n or \0.
+ */
+static char *
+duptoeol(s, ctype)
+ register char *s;
+ char *ctype;
+{
+ size_t len;
+ int ws;
+ char *start;
+
+ ws = 0;
+ for (start = s; *s != '\0' && *s != '\n'; ++s)
+ ws = isspace(*s);
+ *s = '\0';
+ if (ws)
+ err(WARNING, "whitespace after %s", ctype);
+ len = s - start + 1;
+ return (memmove(xmalloc(len), start, len));
+}
+
+/*
+ * Convert goto label names to addresses, and count a and r commands, in
+ * the given subset of the script. Free the memory used by labels in b
+ * and t commands (but not by :).
+ *
+ * TODO: Remove } nodes
+ */
+static void
+fixuplabel(cp, end)
+ struct s_command *cp, *end;
+{
+
+ for (; cp != end; cp = cp->next)
+ switch (cp->code) {
+ case 'a':
+ case 'r':
+ appendnum++;
+ break;
+ case 'b':
+ case 't':
+ /* Resolve branch target. */
+ if (cp->t == NULL) {
+ cp->u.c = NULL;
+ break;
+ }
+ if ((cp->u.c = findlabel(cp->t)) == NULL)
+ err(COMPILE2, "undefined label '%s'", cp->t);
+ free(cp->t);
+ break;
+ case '{':
+ /* Do interior commands. */
+ fixuplabel(cp->u.c, cp->next);
+ break;
+ }
+}
+
+/*
+ * Associate the given command label for later lookup.
+ */
+static void
+enterlabel(cp)
+ struct s_command *cp;
+{
+ register struct labhash **lhp, *lh;
+ register u_char *p;
+ register u_int h, c;
+
+ for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++)
+ h = (h << 5) + h + c;
+ lhp = &labels[h & LHMASK];
+ for (lh = *lhp; lh != NULL; lh = lh->lh_next)
+ if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0)
+ err(COMPILE2, "duplicate label '%s'", cp->t);
+ lh = xmalloc(sizeof *lh);
+ lh->lh_next = *lhp;
+ lh->lh_hash = h;
+ lh->lh_cmd = cp;
+ lh->lh_ref = 0;
+ *lhp = lh;
+}
+
+/*
+ * Find the label contained in the command l in the command linked
+ * list cp. L is excluded from the search. Return NULL if not found.
+ */
+static struct s_command *
+findlabel(name)
+ char *name;
+{
+ register struct labhash *lh;
+ register u_char *p;
+ register u_int h, c;
+
+ for (h = 0, p = (u_char *)name; (c = *p) != 0; p++)
+ h = (h << 5) + h + c;
+ for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) {
+ if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) {
+ lh->lh_ref = 1;
+ return (lh->lh_cmd);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Warn about any unused labels. As a side effect, release the label hash
+ * table space.
+ */
+static void
+uselabel()
+{
+ register struct labhash *lh, *next;
+ register int i;
+
+ for (i = 0; i < LHSZ; i++) {
+ for (lh = labels[i]; lh != NULL; lh = next) {
+ next = lh->lh_next;
+ if (!lh->lh_ref)
+ err(WARNING, "unused label '%s'",
+ lh->lh_cmd->t);
+ free(lh);
+ }
+ }
+}
diff --git a/usr.bin/sed/defs.h b/usr.bin/sed/defs.h
new file mode 100644
index 0000000..6e78e91
--- /dev/null
+++ b/usr.bin/sed/defs.h
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Types of address specifications
+ */
+enum e_atype {
+ AT_RE, /* Line that match RE */
+ AT_LINE, /* Specific line */
+ AT_LAST, /* Last line */
+};
+
+/*
+ * Format of an address
+ */
+struct s_addr {
+ enum e_atype type; /* Address type */
+ union {
+ u_long l; /* Line number */
+ regex_t *r; /* Regular expression */
+ } u;
+};
+
+/*
+ * Substitution command
+ */
+struct s_subst {
+ int n; /* Occurrence to subst. */
+ int p; /* True if p flag */
+ char *wfile; /* NULL if no wfile */
+ int wfd; /* Cached file descriptor */
+ regex_t *re; /* Regular expression */
+ int maxbref; /* Largest backreference. */
+ u_long linenum; /* Line number. */
+ char *new; /* Replacement text */
+};
+
+
+/*
+ * An internally compiled command.
+ * Initialy, label references are stored in t, on a second pass they
+ * are updated to pointers.
+ */
+struct s_command {
+ struct s_command *next; /* Pointer to next command */
+ struct s_addr *a1, *a2; /* Start and end address */
+ char *t; /* Text for : a c i r w */
+ union {
+ struct s_command *c; /* Command(s) for b t { */
+ struct s_subst *s; /* Substitute command */
+ u_char *y; /* Replace command array */
+ int fd; /* File descriptor for w */
+ } u;
+ char code; /* Command code */
+ u_int nonsel:1; /* True if ! */
+ u_int inrange:1; /* True if in range */
+};
+
+/*
+ * Types of command arguments recognised by the parser
+ */
+enum e_args {
+ EMPTY, /* d D g G h H l n N p P q x = \0 */
+ TEXT, /* a c i */
+ NONSEL, /* ! */
+ GROUP, /* { */
+ ENDGROUP, /* } */
+ COMMENT, /* # */
+ BRANCH, /* b t */
+ LABEL, /* : */
+ RFILE, /* r */
+ WFILE, /* w */
+ SUBST, /* s */
+ TR /* y */
+};
+
+/*
+ * Structure containing things to append before a line is read
+ */
+struct s_appends {
+ enum {AP_STRING, AP_FILE} type;
+ char *s;
+ size_t len;
+};
+
+enum e_spflag {
+ APPEND, /* Append to the contents. */
+ REPLACE, /* Replace the contents. */
+};
+
+/*
+ * Structure for a space (process, hold, otherwise).
+ */
+typedef struct {
+ char *space; /* Current space pointer. */
+ size_t len; /* Current length. */
+ int deleted; /* If deleted. */
+ char *back; /* Backing memory. */
+ size_t blen; /* Backing memory length. */
+} SPACE;
+
+/*
+ * Error severity codes:
+ */
+#define FATAL 0 /* Exit immediately with 1 */
+#define ERROR 1 /* Continue, but change exit value */
+#define WARNING 2 /* Just print the warning */
+#define COMPILE 3 /* Print error, count and finish script */
+#define COMPILE2 3 /* Print error, count and finish script */
diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h
new file mode 100644
index 0000000..54f2fb5
--- /dev/null
+++ b/usr.bin/sed/extern.h
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+extern struct s_command *prog;
+extern struct s_appends *appends;
+extern regmatch_t *match;
+extern size_t maxnsub;
+extern u_long linenum;
+extern int appendnum;
+extern int lastline;
+extern int aflag, eflag, nflag;
+extern char *fname;
+
+void cfclose __P((struct s_command *, struct s_command *));
+void compile __P((void));
+void cspace __P((SPACE *, char *, size_t, enum e_spflag));
+char *cu_fgets __P((char *, int));
+void err __P((int, const char *, ...));
+int mf_fgets __P((SPACE *, enum e_spflag));
+void process __P((void));
+char *strregerror __P((int, regex_t *));
+void *xmalloc __P((u_int));
+void *xrealloc __P((void *, u_int));
diff --git a/usr.bin/sed/main.c b/usr.bin/sed/main.c
new file mode 100644
index 0000000..7a41bdb
--- /dev/null
+++ b/usr.bin/sed/main.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "defs.h"
+#include "extern.h"
+
+/*
+ * Linked list of units (strings and files) to be compiled
+ */
+struct s_compunit {
+ struct s_compunit *next;
+ enum e_cut {CU_FILE, CU_STRING} type;
+ char *s; /* Pointer to string or fname */
+};
+
+/*
+ * Linked list pointer to compilation units and pointer to current
+ * next pointer.
+ */
+static struct s_compunit *script, **cu_nextp = &script;
+
+/*
+ * Linked list of files to be processed
+ */
+struct s_flist {
+ char *fname;
+ struct s_flist *next;
+};
+
+/*
+ * Linked list pointer to files and pointer to current
+ * next pointer.
+ */
+static struct s_flist *files, **fl_nextp = &files;
+
+int aflag, eflag, nflag;
+
+/*
+ * Current file and line number; line numbers restart across compilation
+ * units, but span across input files.
+ */
+char *fname; /* File name. */
+u_long linenum;
+int lastline; /* TRUE on the last line of the last file */
+
+static void add_compunit __P((enum e_cut, char *));
+static void add_file __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, fflag;
+
+ fflag = 0;
+ while ((c = getopt(argc, argv, "ae:f:n")) != EOF)
+ switch (c) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ add_compunit(CU_STRING, optarg);
+ break;
+ case 'f':
+ fflag = 1;
+ add_compunit(CU_FILE, optarg);
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ default:
+ case '?':
+ (void)fprintf(stderr,
+"usage:\tsed script [-an] [file ...]\n\tsed [-an] [-e script] ... [-f script_file] ... [file ...]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* First usage case; script is the first arg */
+ if (!eflag && !fflag && *argv) {
+ add_compunit(CU_STRING, *argv);
+ argv++;
+ }
+
+ compile();
+
+ /* Continue with first and start second usage */
+ if (*argv)
+ for (; *argv; argv++)
+ add_file(*argv);
+ else
+ add_file(NULL);
+ process();
+ cfclose(prog, NULL);
+ if (fclose(stdout))
+ err(FATAL, "stdout: %s", strerror(errno));
+ exit (0);
+}
+
+/*
+ * Like fgets, but go through the chain of compilation units chaining them
+ * together. Empty strings and files are ignored.
+ */
+char *
+cu_fgets(buf, n)
+ char *buf;
+ int n;
+{
+ static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF;
+ static FILE *f; /* Current open file */
+ static char *s; /* Current pointer inside string */
+ static char string_ident[30];
+ char *p;
+
+again:
+ switch (state) {
+ case ST_EOF:
+ if (script == NULL)
+ return (NULL);
+ linenum = 0;
+ switch (script->type) {
+ case CU_FILE:
+ if ((f = fopen(script->s, "r")) == NULL)
+ err(FATAL,
+ "%s: %s", script->s, strerror(errno));
+ fname = script->s;
+ state = ST_FILE;
+ goto again;
+ case CU_STRING:
+ if ((snprintf(string_ident,
+ sizeof(string_ident), "\"%s\"", script->s)) >=
+ sizeof(string_ident) - 1)
+ (void)strcpy(string_ident +
+ sizeof(string_ident) - 6, " ...\"");
+ fname = string_ident;
+ s = script->s;
+ state = ST_STRING;
+ goto again;
+ }
+ case ST_FILE:
+ if ((p = fgets(buf, n, f)) != NULL) {
+ linenum++;
+ if (linenum == 1 && buf[0] == '#' && buf[1] == 'n')
+ nflag = 1;
+ return (p);
+ }
+ script = script->next;
+ (void)fclose(f);
+ state = ST_EOF;
+ goto again;
+ case ST_STRING:
+ if (linenum == 0 && s[0] == '#' && s[1] == 'n')
+ nflag = 1;
+ p = buf;
+ for (;;) {
+ if (n-- <= 1) {
+ *p = '\0';
+ linenum++;
+ return (buf);
+ }
+ switch (*s) {
+ case '\0':
+ state = ST_EOF;
+ if (s == script->s) {
+ script = script->next;
+ goto again;
+ } else {
+ script = script->next;
+ *p = '\0';
+ linenum++;
+ return (buf);
+ }
+ case '\n':
+ *p++ = '\n';
+ *p = '\0';
+ s++;
+ linenum++;
+ return (buf);
+ default:
+ *p++ = *s++;
+ }
+ }
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Like fgets, but go through the list of files chaining them together.
+ * Set len to the length of the line.
+ */
+int
+mf_fgets(sp, spflag)
+ SPACE *sp;
+ enum e_spflag spflag;
+{
+ static FILE *f; /* Current open file */
+ size_t len;
+ char c, *p;
+
+ if (f == NULL)
+ /* Advance to first non-empty file */
+ for (;;) {
+ if (files == NULL) {
+ lastline = 1;
+ return (0);
+ }
+ if (files->fname == NULL) {
+ f = stdin;
+ fname = "stdin";
+ } else {
+ fname = files->fname;
+ if ((f = fopen(fname, "r")) == NULL)
+ err(FATAL, "%s: %s",
+ fname, strerror(errno));
+ }
+ if ((c = getc(f)) != EOF) {
+ (void)ungetc(c, f);
+ break;
+ }
+ (void)fclose(f);
+ files = files->next;
+ }
+
+ if (lastline) {
+ sp->len = 0;
+ return (0);
+ }
+
+ /*
+ * Use fgetln so that we can handle essentially infinite input data.
+ * Can't use the pointer into the stdio buffer as the process space
+ * because the ungetc() can cause it to move.
+ */
+ p = fgetln(f, &len);
+ if (ferror(f))
+ err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
+ cspace(sp, p, len, spflag);
+
+ linenum++;
+ /* Advance to next non-empty file */
+ while ((c = getc(f)) == EOF) {
+ (void)fclose(f);
+ files = files->next;
+ if (files == NULL) {
+ lastline = 1;
+ return (1);
+ }
+ if (files->fname == NULL) {
+ f = stdin;
+ fname = "stdin";
+ } else {
+ fname = files->fname;
+ if ((f = fopen(fname, "r")) == NULL)
+ err(FATAL, "%s: %s", fname, strerror(errno));
+ }
+ }
+ (void)ungetc(c, f);
+ return (1);
+}
+
+/*
+ * Add a compilation unit to the linked list
+ */
+static void
+add_compunit(type, s)
+ enum e_cut type;
+ char *s;
+{
+ struct s_compunit *cu;
+
+ cu = xmalloc(sizeof(struct s_compunit));
+ cu->type = type;
+ cu->s = s;
+ cu->next = NULL;
+ *cu_nextp = cu;
+ cu_nextp = &cu->next;
+}
+
+/*
+ * Add a file to the linked list
+ */
+static void
+add_file(s)
+ char *s;
+{
+ struct s_flist *fp;
+
+ fp = xmalloc(sizeof(struct s_flist));
+ fp->next = NULL;
+ *fl_nextp = fp;
+ fp->fname = s;
+ fl_nextp = &fp->next;
+}
diff --git a/usr.bin/sed/misc.c b/usr.bin/sed/misc.c
new file mode 100644
index 0000000..444b7a8
--- /dev/null
+++ b/usr.bin/sed/misc.c
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "defs.h"
+#include "extern.h"
+
+/*
+ * malloc with result test
+ */
+void *
+xmalloc(size)
+ u_int size;
+{
+ void *p;
+
+ if ((p = malloc(size)) == NULL)
+ err(FATAL, "%s", strerror(errno));
+ return (p);
+}
+
+/*
+ * realloc with result test
+ */
+void *
+xrealloc(p, size)
+ void *p;
+ u_int size;
+{
+ if (p == NULL) /* Compatibility hack. */
+ return (xmalloc(size));
+
+ if ((p = realloc(p, size)) == NULL)
+ err(FATAL, "%s", strerror(errno));
+ return (p);
+}
+
+/*
+ * Return a string for a regular expression error passed. This is a overkill,
+ * because of the silly semantics of regerror (we can never know the size of
+ * the buffer).
+ */
+char *
+strregerror(errcode, preg)
+ int errcode;
+ regex_t *preg;
+{
+ static char *oe;
+ size_t s;
+
+ if (oe != NULL)
+ free(oe);
+ s = regerror(errcode, preg, "", 0);
+ oe = xmalloc(s);
+ (void)regerror(errcode, preg, oe, s);
+ return (oe);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+/*
+ * Error reporting function
+ */
+void
+#if __STDC__
+err(int severity, const char *fmt, ...)
+#else
+err(severity, fmt, va_alist)
+ int severity;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "sed: ");
+ switch (severity) {
+ case WARNING:
+ case COMPILE:
+ (void)fprintf(stderr, "%lu: %s: ", linenum, fname);
+ }
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (severity == WARNING)
+ return;
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/sed/process.c b/usr.bin/sed/process.c
new file mode 100644
index 0000000..c435e1e
--- /dev/null
+++ b/usr.bin/sed/process.c
@@ -0,0 +1,631 @@
+/*-
+ * Copyright (c) 1992 Diomidis Spinellis.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis of Imperial College, University of London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "defs.h"
+#include "extern.h"
+
+static SPACE HS, PS, SS;
+#define pd PS.deleted
+#define ps PS.space
+#define psl PS.len
+#define hs HS.space
+#define hsl HS.len
+
+static inline int applies __P((struct s_command *));
+static void flush_appends __P((void));
+static void lputs __P((char *));
+static inline int regexec_e __P((regex_t *, const char *, int, int, size_t));
+static void regsub __P((SPACE *, char *, char *));
+static int substitute __P((struct s_command *));
+
+struct s_appends *appends; /* Array of pointers to strings to append. */
+static int appendx; /* Index into appends array. */
+int appendnum; /* Size of appends array. */
+
+static int lastaddr; /* Set by applies if last address of a range. */
+static int sdone; /* If any substitutes since last line input. */
+ /* Iov structure for 'w' commands. */
+static regex_t *defpreg;
+size_t maxnsub;
+regmatch_t *match;
+
+#define OUT(s) { fwrite(s, sizeof(u_char), psl, stdout); }
+
+void
+process()
+{
+ struct s_command *cp;
+ SPACE tspace;
+ size_t len, oldpsl;
+ char *p;
+
+ for (linenum = 0; mf_fgets(&PS, REPLACE);) {
+ pd = 0;
+top:
+ cp = prog;
+redirect:
+ while (cp != NULL) {
+ if (!applies(cp)) {
+ cp = cp->next;
+ continue;
+ }
+ switch (cp->code) {
+ case '{':
+ cp = cp->u.c;
+ goto redirect;
+ case 'a':
+ if (appendx >= appendnum)
+ appends = xrealloc(appends,
+ sizeof(struct s_appends) *
+ (appendnum *= 2));
+ appends[appendx].type = AP_STRING;
+ appends[appendx].s = cp->t;
+ appends[appendx].len = strlen(cp->t);
+ appendx++;
+ break;
+ case 'b':
+ cp = cp->u.c;
+ goto redirect;
+ case 'c':
+ pd = 1;
+ psl = 0;
+ if (cp->a2 == NULL || lastaddr)
+ (void)printf("%s", cp->t);
+ break;
+ case 'd':
+ pd = 1;
+ goto new;
+ case 'D':
+ if (pd)
+ goto new;
+ if ((p = memchr(ps, '\n', psl - 1)) == NULL) {
+ pd = 1;
+ goto new;
+ } else {
+ psl -= (p + 1) - ps;
+ memmove(ps, p + 1, psl);
+ goto top;
+ }
+ case 'g':
+ cspace(&PS, hs, hsl, REPLACE);
+ break;
+ case 'G':
+ cspace(&PS, hs, hsl, 0);
+ break;
+ case 'h':
+ cspace(&HS, ps, psl, REPLACE);
+ break;
+ case 'H':
+ cspace(&HS, ps, psl, 0);
+ break;
+ case 'i':
+ (void)printf("%s", cp->t);
+ break;
+ case 'l':
+ lputs(ps);
+ break;
+ case 'n':
+ if (!nflag && !pd)
+ OUT(ps)
+ flush_appends();
+ if (!mf_fgets(&PS, REPLACE))
+ exit(0);
+ pd = 0;
+ break;
+ case 'N':
+ flush_appends();
+ if (!mf_fgets(&PS, 0)) {
+ if (!nflag && !pd)
+ OUT(ps)
+ exit(0);
+ }
+ break;
+ case 'p':
+ if (pd)
+ break;
+ OUT(ps)
+ break;
+ case 'P':
+ if (pd)
+ break;
+ if ((p = memchr(ps, '\n', psl - 1)) != NULL) {
+ oldpsl = psl;
+ psl = (p + 1) - ps;
+ }
+ OUT(ps)
+ if (p != NULL)
+ psl = oldpsl;
+ break;
+ case 'q':
+ if (!nflag && !pd)
+ OUT(ps)
+ flush_appends();
+ exit(0);
+ case 'r':
+ if (appendx >= appendnum)
+ appends = xrealloc(appends,
+ sizeof(struct s_appends) *
+ (appendnum *= 2));
+ appends[appendx].type = AP_FILE;
+ appends[appendx].s = cp->t;
+ appends[appendx].len = strlen(cp->t);
+ appendx++;
+ break;
+ case 's':
+ sdone |= substitute(cp);
+ break;
+ case 't':
+ if (sdone) {
+ sdone = 0;
+ cp = cp->u.c;
+ goto redirect;
+ }
+ break;
+ case 'w':
+ if (pd)
+ break;
+ if (cp->u.fd == -1 && (cp->u.fd = open(cp->t,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
+ DEFFILEMODE)) == -1)
+ err(FATAL, "%s: %s\n",
+ cp->t, strerror(errno));
+ if (write(cp->u.fd, ps, psl) != psl)
+ err(FATAL, "%s: %s\n",
+ cp->t, strerror(errno));
+ break;
+ case 'x':
+ if (hs == NULL)
+ cspace(&HS, "\n", 1, REPLACE);
+ tspace = PS;
+ PS = HS;
+ HS = tspace;
+ break;
+ case 'y':
+ if (pd)
+ break;
+ for (p = ps, len = psl; --len; ++p)
+ *p = cp->u.y[*p];
+ break;
+ case ':':
+ case '}':
+ break;
+ case '=':
+ (void)printf("%lu\n", linenum);
+ }
+ cp = cp->next;
+ } /* for all cp */
+
+new: if (!nflag && !pd)
+ OUT(ps)
+ flush_appends();
+ } /* for all lines */
+}
+
+/*
+ * TRUE if the address passed matches the current program state
+ * (lastline, linenumber, ps).
+ */
+#define MATCH(a) \
+ (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \
+ (a)->type == AT_LINE ? linenum == (a)->u.l : lastline
+
+/*
+ * Return TRUE if the command applies to the current line. Sets the inrange
+ * flag to process ranges. Interprets the non-select (``!'') flag.
+ */
+static inline int
+applies(cp)
+ struct s_command *cp;
+{
+ int r;
+
+ lastaddr = 0;
+ if (cp->a1 == NULL && cp->a2 == NULL)
+ r = 1;
+ else if (cp->a2)
+ if (cp->inrange) {
+ if (MATCH(cp->a2)) {
+ cp->inrange = 0;
+ lastaddr = 1;
+ }
+ r = 1;
+ } else if (MATCH(cp->a1)) {
+ /*
+ * If the second address is a number less than or
+ * equal to the line number first selected, only
+ * one line shall be selected.
+ * -- POSIX 1003.2
+ */
+ if (cp->a2->type == AT_LINE &&
+ linenum >= cp->a2->u.l)
+ lastaddr = 1;
+ else
+ cp->inrange = 1;
+ r = 1;
+ } else
+ r = 0;
+ else
+ r = MATCH(cp->a1);
+ return (cp->nonsel ? ! r : r);
+}
+
+/*
+ * substitute --
+ * Do substitutions in the pattern space. Currently, we build a
+ * copy of the new pattern space in the substitute space structure
+ * and then swap them.
+ */
+static int
+substitute(cp)
+ struct s_command *cp;
+{
+ SPACE tspace;
+ regex_t *re;
+ size_t re_off, slen;
+ int lastempty, n;
+ char *s;
+
+ s = ps;
+ re = cp->u.s->re;
+ if (re == NULL) {
+ if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) {
+ linenum = cp->u.s->linenum;
+ err(COMPILE, "\\%d not defined in the RE",
+ cp->u.s->maxbref);
+ }
+ }
+ if (!regexec_e(re, s, 0, 0, psl))
+ return (0);
+
+ SS.len = 0; /* Clean substitute space. */
+ slen = psl;
+ n = cp->u.s->n;
+ lastempty = 1;
+
+ switch (n) {
+ case 0: /* Global */
+ do {
+ if (lastempty || match[0].rm_so != match[0].rm_eo) {
+ /* Locate start of replaced string. */
+ re_off = match[0].rm_so;
+ /* Copy leading retained string. */
+ cspace(&SS, s, re_off, APPEND);
+ /* Add in regular expression. */
+ regsub(&SS, s, cp->u.s->new);
+ }
+
+ /* Move past this match. */
+ if (match[0].rm_so != match[0].rm_eo) {
+ s += match[0].rm_eo;
+ slen -= match[0].rm_eo;
+ lastempty = 0;
+ } else {
+ if (match[0].rm_so == 0)
+ cspace(&SS, s, match[0].rm_so + 1,
+ APPEND);
+ else
+ cspace(&SS, s + match[0].rm_so, 1,
+ APPEND);
+ s += match[0].rm_so + 1;
+ slen -= match[0].rm_so + 1;
+ lastempty = 1;
+ }
+ } while (slen > 0 && regexec_e(re, s, REG_NOTBOL, 0, slen));
+ /* Copy trailing retained string. */
+ if (slen > 0)
+ cspace(&SS, s, slen, APPEND);
+ break;
+ default: /* Nth occurrence */
+ while (--n) {
+ s += match[0].rm_eo;
+ slen -= match[0].rm_eo;
+ if (!regexec_e(re, s, REG_NOTBOL, 0, slen))
+ return (0);
+ }
+ /* FALLTHROUGH */
+ case 1: /* 1st occurrence */
+ /* Locate start of replaced string. */
+ re_off = match[0].rm_so + (s - ps);
+ /* Copy leading retained string. */
+ cspace(&SS, ps, re_off, APPEND);
+ /* Add in regular expression. */
+ regsub(&SS, s, cp->u.s->new);
+ /* Copy trailing retained string. */
+ s += match[0].rm_eo;
+ slen -= match[0].rm_eo;
+ cspace(&SS, s, slen, APPEND);
+ break;
+ }
+
+ /*
+ * Swap the substitute space and the pattern space, and make sure
+ * that any leftover pointers into stdio memory get lost.
+ */
+ tspace = PS;
+ PS = SS;
+ SS = tspace;
+ SS.space = SS.back;
+
+ /* Handle the 'p' flag. */
+ if (cp->u.s->p)
+ OUT(ps)
+
+ /* Handle the 'w' flag. */
+ if (cp->u.s->wfile && !pd) {
+ if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
+ O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
+ err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno));
+ if (write(cp->u.s->wfd, ps, psl) != psl)
+ err(FATAL, "%s: %s\n", cp->u.s->wfile, strerror(errno));
+ }
+ return (1);
+}
+
+/*
+ * Flush append requests. Always called before reading a line,
+ * therefore it also resets the substitution done (sdone) flag.
+ */
+static void
+flush_appends()
+{
+ FILE *f;
+ int count, i;
+ char buf[8 * 1024];
+
+ for (i = 0; i < appendx; i++)
+ switch (appends[i].type) {
+ case AP_STRING:
+ fwrite(appends[i].s, sizeof(char), appends[i].len,
+ stdout);
+ break;
+ case AP_FILE:
+ /*
+ * Read files probably shouldn't be cached. Since
+ * it's not an error to read a non-existent file,
+ * it's possible that another program is interacting
+ * with the sed script through the file system. It
+ * would be truly bizarre, but possible. It's probably
+ * not that big a performance win, anyhow.
+ */
+ if ((f = fopen(appends[i].s, "r")) == NULL)
+ break;
+ while (count = fread(buf, sizeof(char), sizeof(buf), f))
+ (void)fwrite(buf, sizeof(char), count, stdout);
+ (void)fclose(f);
+ break;
+ }
+ if (ferror(stdout))
+ err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
+ appendx = sdone = 0;
+}
+
+static void
+lputs(s)
+ register char *s;
+{
+ register int count;
+ register char *escapes, *p;
+ struct winsize win;
+ static int termwidth = -1;
+
+ if (termwidth == -1)
+ if (p = getenv("COLUMNS"))
+ termwidth = atoi(p);
+ else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 &&
+ win.ws_col > 0)
+ termwidth = win.ws_col;
+ else
+ termwidth = 60;
+
+ for (count = 0; *s; ++s) {
+ if (count >= termwidth) {
+ (void)printf("\\\n");
+ count = 0;
+ }
+ if (isascii(*s) && isprint(*s) && *s != '\\') {
+ (void)putchar(*s);
+ count++;
+ } else {
+ escapes = "\\\a\b\f\n\r\t\v";
+ (void)putchar('\\');
+ if (p = strchr(escapes, *s)) {
+ (void)putchar("\\abfnrtv"[p - escapes]);
+ count += 2;
+ } else {
+ (void)printf("%03o", *(u_char *)s);
+ count += 4;
+ }
+ }
+ }
+ (void)putchar('$');
+ (void)putchar('\n');
+ if (ferror(stdout))
+ err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
+}
+
+static inline int
+regexec_e(preg, string, eflags, nomatch, slen)
+ regex_t *preg;
+ const char *string;
+ int eflags, nomatch;
+ size_t slen;
+{
+ int eval;
+
+ if (preg == NULL) {
+ if (defpreg == NULL)
+ err(FATAL, "first RE may not be empty");
+ } else
+ defpreg = preg;
+
+ /* Set anchors, discounting trailing newline (if any). */
+ if (slen > 0 && string[slen - 1] == '\n')
+ slen--;
+ match[0].rm_so = 0;
+ match[0].rm_eo = slen;
+
+ eval = regexec(defpreg, string,
+ nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND);
+ switch(eval) {
+ case 0:
+ return (1);
+ case REG_NOMATCH:
+ return (0);
+ }
+ err(FATAL, "RE error: %s", strregerror(eval, defpreg));
+ /* NOTREACHED */
+}
+
+/*
+ * regsub - perform substitutions after a regexp match
+ * Based on a routine by Henry Spencer
+ */
+static void
+regsub(sp, string, src)
+ SPACE *sp;
+ char *string, *src;
+{
+ register int len, no;
+ register char c, *dst;
+
+#define NEEDSP(reqlen) \
+ if (sp->len >= sp->blen - (reqlen) - 1) { \
+ sp->blen += (reqlen) + 1024; \
+ sp->space = sp->back = xrealloc(sp->back, sp->blen); \
+ dst = sp->space + sp->len; \
+ }
+
+ dst = sp->space + sp->len;
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '\\' && isdigit(*src))
+ no = *src++ - '0';
+ else
+ no = -1;
+ if (no < 0) { /* Ordinary character. */
+ if (c == '\\' && (*src == '\\' || *src == '&'))
+ c = *src++;
+ NEEDSP(1);
+ *dst++ = c;
+ ++sp->len;
+ } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) {
+ len = match[no].rm_eo - match[no].rm_so;
+ NEEDSP(len);
+ memmove(dst, string + match[no].rm_so, len);
+ dst += len;
+ sp->len += len;
+ }
+ }
+ NEEDSP(1);
+ *dst = '\0';
+}
+
+/*
+ * aspace --
+ * Append the source space to the destination space, allocating new
+ * space as necessary.
+ */
+void
+cspace(sp, p, len, spflag)
+ SPACE *sp;
+ char *p;
+ size_t len;
+ enum e_spflag spflag;
+{
+ size_t tlen;
+
+ /* Make sure SPACE has enough memory and ramp up quickly. */
+ tlen = sp->len + len + 1;
+ if (tlen > sp->blen) {
+ sp->blen = tlen + 1024;
+ sp->space = sp->back = xrealloc(sp->back, sp->blen);
+ }
+
+ if (spflag == REPLACE)
+ sp->len = 0;
+
+ memmove(sp->space + sp->len, p, len);
+
+ sp->space[sp->len += len] = '\0';
+}
+
+/*
+ * Close all cached opened files and report any errors
+ */
+void
+cfclose(cp, end)
+ register struct s_command *cp, *end;
+{
+
+ for (; cp != end; cp = cp->next)
+ switch(cp->code) {
+ case 's':
+ if (cp->u.s->wfd != -1 && close(cp->u.s->wfd))
+ err(FATAL,
+ "%s: %s", cp->u.s->wfile, strerror(errno));
+ cp->u.s->wfd = -1;
+ break;
+ case 'w':
+ if (cp->u.fd != -1 && close(cp->u.fd))
+ err(FATAL, "%s: %s", cp->t, strerror(errno));
+ cp->u.fd = -1;
+ break;
+ case '{':
+ cfclose(cp->u.c, cp->next);
+ break;
+ }
+}
diff --git a/usr.bin/sed/sed.1 b/usr.bin/sed/sed.1
new file mode 100644
index 0000000..917b92d
--- /dev/null
+++ b/usr.bin/sed/sed.1
@@ -0,0 +1,514 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sed.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd "December 30, 1993"
+.Dt SED 1
+.Os
+.Sh NAME
+.Nm sed
+.Nd stream editor
+.Sh SYNOPSIS
+.Nm sed
+.Op Fl an
+.Ar command
+.Op Ar file ...
+.Nm sed
+.Op Fl an
+.Op Fl e Ar command
+.Op Fl f Ar command_file
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm sed
+utility reads the specified files, or the standard input if no files
+are specified, modifying the input as specified by a list of commands.
+The input is then written to the standard output.
+.Pp
+A single command may be specified as the first argument to
+.Nm sed .
+Multiple commands may be specified by using the
+.Fl e
+or
+.Fl f
+options.
+All commands are applied to the input in the order they are specified
+regardless of their origin.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+The files listed as parameters for the
+.Dq w
+functions are created (or truncated) before any processing begins,
+by default.
+The
+.Fl a
+option causes
+.Nm sed
+to delay opening each file until a command containing the related
+.Dq w
+function is applied to a line of input.
+.It Fl e Ar command
+Append the editing commands specified by the
+.Ar command
+argument
+to the list of commands.
+.It Fl f Ar command_file
+Append the editing commands found in the file
+.Ar command_file
+to the list of commands.
+The editing commands should each be listed on a separate line.
+.It Fl n
+By default, each line of input is echoed to the standard output after
+all of the commands have been applied to it.
+The
+.Fl n
+option suppresses this behavior.
+.El
+.Pp
+The form of a
+.Nm sed
+command is as follows:
+.sp
+.Dl [address[,address]]function[arguments]
+.sp
+Whitespace may be inserted before the first address and the function
+portions of the command.
+.Pp
+Normally,
+.Nm sed
+cyclically copies a line of input, not including its terminating newline
+character, into a
+.Em "pattern space" ,
+(unless there is something left after a
+.Dq D
+function),
+applies all of the commands with addresses that select that pattern space,
+copies the pattern space to the standard output, appending a newline, and
+deletes the pattern space.
+.Pp
+Some of the functions use a
+.Em "hold space"
+to save all or part of the pattern space for subsequent retrieval.
+.Sh "Sed Addresses"
+An address is not required, but if specified must be a number (that counts
+input lines
+cumulatively across input files), a dollar
+.Po
+.Dq $
+.Pc
+character that addresses the last line of input, or a context address
+(which consists of a regular expression preceded and followed by a
+delimiter).
+.Pp
+A command line with no addresses selects every pattern space.
+.Pp
+A command line with one address selects all of the pattern spaces
+that match the address.
+.Pp
+A command line with two addresses selects the inclusive range from
+the first pattern space that matches the first address through the next
+pattern space that matches the second.
+(If the second address is a number less than or equal to the line number
+first selected, only that line is selected.)
+Starting at the first line following the selected range,
+.Nm sed
+starts looking again for the first address.
+.Pp
+Editing commands can be applied to non-selected pattern spaces by use
+of the exclamation character
+.Po
+.Dq !
+.Pc
+function.
+.Sh "Sed Regular Expressions"
+The
+.Nm sed
+regular expressions are basic regular expressions (BRE's, see
+.Xr regex 3
+for more information).
+In addition,
+.Nm sed
+has the following two additions to BRE's:
+.sp
+.Bl -enum -compact
+.It
+In a context address, any character other than a backslash
+.Po
+.Dq \e
+.Pc
+or newline character may be used to delimit the regular expression.
+Also, putting a backslash character before the delimiting character
+causes the character to be treated literally.
+For example, in the context address \exabc\exdefx, the RE delimiter
+is an
+.Dq x
+and the second
+.Dq x
+stands for itself, so that the regular expression is
+.Dq abcxdef .
+.sp
+.It
+The escape sequence \en matches a newline character embedded in the
+pattern space.
+You can't, however, use a literal newline character in an address or
+in the substitute command.
+.El
+.Pp
+One special feature of
+.Nm sed
+regular expressions is that they can default to the last regular
+expression used.
+If a regular expression is empty, i.e. just the delimiter characters
+are specified, the last regular expression encountered is used instead.
+The last regular expression is defined as the last regular expression
+used as part of an address or substitute command, and at run-time, not
+compile-time.
+For example, the command
+.Dq /abc/s//XXX/
+will substitute
+.Dq XXX
+for the pattern
+.Dq abc .
+.Sh "Sed Functions"
+In the following list of commands, the maximum number of permissible
+addresses for each command is indicated by [0addr], [1addr], or [2addr],
+representing zero, one, or two addresses.
+.Pp
+The argument
+.Em text
+consists of one or more lines.
+To embed a newline in the text, precede it with a backslash.
+Other backslashes in text are deleted and the following character
+taken literally.
+.Pp
+The
+.Dq r
+and
+.Dq w
+functions take an optional file parameter, which should be separated
+from the function letter by white space.
+Each file given as an argument to
+.Nm sed
+is created (or its contents truncated) before any input processing begins.
+.Pp
+The
+.Dq b ,
+.Dq r ,
+.Dq s ,
+.Dq t ,
+.Dq w ,
+.Dq y ,
+.Dq ! ,
+and
+.Dq \&:
+functions all accept additional arguments.
+The following synopses indicate which arguments have to be separated from
+the function letters by white space characters.
+.Pp
+Two of the functions take a function-list.
+This is a list of
+.Nm sed
+functions separated by newlines, as follows:
+.Bd -literal -offset indent
+{ function
+ function
+ ...
+ function
+}
+.Ed
+.Pp
+The
+.Dq {
+can be preceded by white space and can be followed by white space.
+The function can be preceded by white space.
+The terminating
+.Dq }
+must be preceded by a newline or optional white space.
+.sp
+.Bl -tag -width "XXXXXX" -compact
+.It [2addr] function-list
+Execute function-list only when the pattern space is selected.
+.sp
+.It [1addr]a\e
+.It text
+.br
+Write
+.Em text
+to standard output immediately before each attempt to read a line of input,
+whether by executing the
+.Dq N
+function or by beginning a new cycle.
+.sp
+.It [2addr]b[label]
+Branch to the
+.Dq \&:
+function with the specified label.
+If the label is not specified, branch to the end of the script.
+.sp
+.It [2addr]c\e
+.It text
+.br
+Delete the pattern space.
+With 0 or 1 address or at the end of a 2-address range,
+.Em text
+is written to the standard output.
+.sp
+.It [2addr]d
+Delete the pattern space and start the next cycle.
+.sp
+.It [2addr]D
+Delete the initial segment of the pattern space through the first
+newline character and start the next cycle.
+.sp
+.It [2addr]g
+Replace the contents of the pattern space with the contents of the
+hold space.
+.sp
+.It [2addr]G
+Append a newline character followed by the contents of the hold space
+to the pattern space.
+.sp
+.It [2addr]h
+Replace the contents of the hold space with the contents of the
+pattern space.
+.sp
+.It [2addr]H
+Append a newline character followed by the contents of the pattern space
+to the hold space.
+.sp
+.It [1addr]i\e
+.It text
+.br
+Write
+.Em text
+to the standard output.
+.sp
+.It [2addr]l
+(The letter ell.)
+Write the pattern space to the standard output in a visually unambiguous
+form.
+This form is as follows:
+.sp
+.Bl -tag -width "carriage-returnXX" -offset indent -compact
+.It backslash
+\e
+.It alert
+\ea
+.It form-feed
+\ef
+.It newline
+\en
+.It carriage-return
+\er
+.It tab
+\et
+.It vertical tab
+\ev
+.El
+.Pp
+Nonprintable characters are written as three-digit octal numbers (with a
+preceding backslash) for each byte in the character (most significant byte
+first).
+Long lines are folded, with the point of folding indicated by displaying
+a backslash followed by a newline.
+The end of each line is marked with a
+.Dq $ .
+.sp
+.It [2addr]n
+Write the pattern space to the standard output if the default output has
+not been suppressed, and replace the pattern space with the next line of
+input.
+.sp
+.It [2addr]N
+Append the next line of input to the pattern space, using an embedded
+newline character to separate the appended material from the original
+contents.
+Note that the current line number changes.
+.sp
+.It [2addr]p
+Write the pattern space to standard output.
+.sp
+.It [2addr]P
+Write the pattern space, up to the first newline character to the
+standard output.
+.sp
+.It [1addr]q
+Branch to the end of the script and quit without starting a new cycle.
+.sp
+.It [1addr]r file
+Copy the contents of
+.Em file
+to the standard output immediately before the next attempt to read a
+line of input.
+If
+.Em file
+cannot be read for any reason, it is silently ignored and no error
+condition is set.
+.sp
+.It [2addr]s/regular expression/replacement/flags
+Substitute the replacement string for the first instance of the regular
+expression in the pattern space.
+Any character other than backslash or newline can be used instead of
+a slash to delimit the RE and the replacement.
+Within the RE and the replacement, the RE delimiter itself can be used as
+a literal character if it is preceded by a backslash.
+.Pp
+An ampersand
+.Po
+.Dq &
+.Pc
+appearing in the replacement is replaced by the string matching the RE.
+The special meaning of
+.Dq &
+in this context can be suppressed by preceding it by a backslash.
+The string
+.Dq \e# ,
+where
+.Dq #
+is a digit, is replaced by the text matched
+by the corresponding backreference expression (see
+.Xr re_format 7 ).
+.Pp
+A line can be split by substituting a newline character into it.
+To specify a newline character in the replacement string, precede it with
+a backslash.
+.Pp
+The value of
+.Em flags
+in the substitute function is zero or more of the following:
+.Bl -tag -width "XXXXXX" -offset indent
+.It "0 ... 9"
+Make the substitution only for the N'th occurrence of the regular
+expression in the pattern space.
+.It g
+Make the substitution for all non-overlapping matches of the
+regular expression, not just the first one.
+.It p
+Write the pattern space to standard output if a replacement was made.
+If the replacement string is identical to that which it replaces, it
+is still considered to have been a replacement.
+.It w Em file
+Append the pattern space to
+.Em file
+if a replacement was made.
+If the replacement string is identical to that which it replaces, it
+is still considered to have been a replacement.
+.El
+.sp
+.It [2addr]t [label]
+Branch to the
+.Dq :
+function bearing the label if any substitutions have been made since the
+most recent reading of an input line or execution of a
+.Dq t
+function.
+If no label is specified, branch to the end of the script.
+.sp
+.It [2addr]w Em file
+Append the pattern space to the
+.Em file .
+.sp
+.It [2addr]x
+Swap the contents of the pattern and hold spaces.
+.sp
+.It [2addr]y/string1/string2/
+Replace all occurrences of characters in
+.Em string1
+in the pattern space with the corresponding characters from
+.Em string2 .
+Any character other than a backslash or newline can be used instead of
+a slash to delimit the strings.
+Within
+.Em string1
+and
+.Em string2 ,
+a backslash followed by any character other than a newline is that literal
+character, and a backslash followed by an ``n'' is replaced by a newline
+character.
+.sp
+.It [2addr]!function
+.It [2addr]!function-list
+Apply the function or function-list only to the lines that are
+.Em not
+selected by the address(es).
+.sp
+.It [0addr]:label
+This function does nothing; it bears a label to which the
+.Dq b
+and
+.Dq t
+commands may branch.
+.sp
+.It [1addr]=
+Write the line number to the standard output followed by a newline
+character.
+.sp
+.It [0addr]
+Empty lines are ignored.
+.sp
+.It [0addr]#
+The
+.Dq #
+and the remainder of the line are ignored (treated as a comment), with
+the single exception that if the first two characters in the file are
+.Dq #n ,
+the default output is suppressed.
+This is the same as specifying the
+.Fl n
+option on the command line.
+.El
+.Pp
+The
+.Nm sed
+utility exits 0 on success and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr ed 1 ,
+.Xr grep 1 ,
+.Xr regex 3 ,
+.Xr re_format 7
+.Sh HISTORY
+A
+.Nm sed
+command appeared in
+.At v7 .
+.Sh STANDARDS
+The
+.Nm sed
+function is expected to be a superset of the
+.St -p1003.2
+specification.
diff --git a/usr.bin/sgmlfmt/Makefile b/usr.bin/sgmlfmt/Makefile
new file mode 100644
index 0000000..8d761b4
--- /dev/null
+++ b/usr.bin/sgmlfmt/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.1.1.1 1995/05/09 23:58:06 jfieber Exp $
+
+MAN1= sgmlfmt.1
+
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/sgmlfmt.pl ${DESTDIR}${BINDIR}/sgmlfmt
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/sgmlfmt/sgmlfmt.1 b/usr.bin/sgmlfmt/sgmlfmt.1
new file mode 100644
index 0000000..1b18008
--- /dev/null
+++ b/usr.bin/sgmlfmt/sgmlfmt.1
@@ -0,0 +1,185 @@
+.Dd October 7, 1995
+.Os FreeBSD 2.1
+.Dt SGMLFMT 1
+.Sh NAME
+.Nm sgmlfmt
+.Nd Formats SGML files tagged according to the linuxdoc DTD.
+.Sh SYNOPSIS
+.Nm
+.Fl f Ar format
+.Op Fl links
+.Op Fl ssi
+.Op Fl i Ar name ...
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+command reads SGML files tagged according to the linuxdoc DTD,
+validates them using the
+.Xr sgmls 1
+parser and then converts them to the specified output format.
+The input file must include the following document type
+declaration before any uncommented text:
+.Bd -literal -offset indent
+<!DOCTYPE linuxdoc PUBLIC "-//FreeBSD//DTD linuxdoc//EN">
+.Ed
+.Pp Options for
+.Nm
+include the following:
+.Bl -tag -width Ds
+.It Fl f Ar format
+Determines the output format which can be one of the following:
+.Bl -tag -width Ds
+.It Ar html
+Generates a set of linked HTML files suitable for use with an
+HTML browser. A top level file,
+.Pa file.html ,
+contains the title, author, date, abstract and brief table of
+contents for the document. A file
+.Pa file_toc.html
+contains a complete table of contents. A series of files named
+.Pa file1.html ,
+.Pa file2.html ...
+.Pa filen.html
+contain the actual text of the document.
+.It Ar latex
+Generates a single output file with the extension
+.Pa .tex
+suitable for processing with LaTeX. Note that the LaTeX style
+file
+.Pa /usr/share/sgml/FreeBSD/lib/linuxdoc.sty
+must be accessible to LaTeX for correct processing.
+.It Ar ascii
+Generates a single output file with the extension
+.Pa .ascii
+suitable for viewing on an ASCII terminal.
+.It Ar nroff
+Generates a single output file with the extension
+.Pa .nroff
+suitable processing with
+.Xr nroff 1
+or
+.Xr groff 1 .
+This is actually an intermediate conversion used by the
+.Fl f Ar ascii
+format option.
+.El
+.It Fl i Ar name
+Pretend that
+.Dl <!ENTITY % name "INCLUDE">
+occurs at the start of the document type declaration subset in
+the document entity. Since repeated definitions of an entity are
+ignored, this definition will take precedence over any other
+definitions of this entity in the document type declaration.
+Multiple
+.Fl i
+options are allowed. If the declaration replaces the reserved
+name INCLUDE then the new reserved name will be the replacement
+text of the entity. Typically the document type declaration will
+contain
+.Dl <!ENTITY % name "IGNORE">
+and will use
+.Li %name;
+in the status keyword specification of a
+marked section declaration. In this case the effect of the
+option will be to cause the marked section not to be ignored.
+.It Fl links
+When used with the
+.Fl f Ar html
+option, for each
+.Li <label id="foo">
+in the document source
+.Nm
+generates a symbolic link
+.Pa foo.html
+pointing to the numbered
+.Pa .html
+file containing the reference. Since the number of the file
+containing a particular section can change when a document
+is modified, this provides a convenient hook by which separate
+documents can provide links into another document without the
+links becoming invalid when the target document is modified.
+When creating a symbolic link, any occurence of a slash (/) in label
+is replaced with percent (%), while any occurence of a space is replaced
+with an underscore (_).
+.It Fl ssi
+When used with the
+.Fl f Ar html
+option, hooks will be placed to have an http server include a
+custom header and footer on the document using the server side
+include mechanism. This mechanism is supported by the Apache and
+NCSA http servers and possibly others. Included files are named
+.Pa file.hdr
+and
+.Pa file.ftr
+for the header and footer of the
+.Pa file.html .
+Other pages include file are named
+.Pa file.ftr1 ,
+.Pa file.ftr2 ...
+.Pa file.ftrn
+where the number corresponds to the depth of the file from
+.Pa file.html .
+.El
+.Pp
+If the input file name ends with
+.Pa .sgml ,
+the extension may be omitted on the command line.
+In all cases, the output files are created in the current working
+directory.
+.Sh FILES
+.Pa /usr/share/sgml/FreeBSD/dtd/linuxdoc
+- the linuxdoc DTD.
+.Pp
+.Pa /usr/share/sgml/FreeBSD/rep/
+- directory containing replacement files for
+.Xr sgmlsasp 1 .
+.Pp
+.Pa /usr/share/sgml/FreeBSD/lib/linuxdoc.sty
+- the LaTeX style used in documents produced with the
+.Fl latex
+format option.
+.Sh SEE ALSO
+.Xr sgmls 1 ,
+.Xr sgmlsasp 1 ,
+.Xr groff 1
+.Sh HISTORY
+The
+.Nm
+command appeared in Version 2.0.5 FreeBSD UNIX.
+.Sh AUTHORS
+The
+.Nm
+command was written by John Fieber
+.Aq jfieber@FreeBSD.org .
+The linuxdoc DTD was written by Matt Welsh
+.Aq mdw@cs.cornell.edu
+and based on the Qwertz DTD written by Tom Gordon
+.Aq thomas.gordon@gmd.de .
+.Sh BUGS
+The ASCII output has many, many bugs including no meaningful
+cross references, and numerous formatting problems.
+.Pp
+A line in the SGML source file beginning with a period (.) will
+confuse
+.Xr groff 1
+which is used to generate ASCII output.
+.Pp
+The divison of the sources file into separate HTML files is
+currently fixed.
+.Pp
+Although legal according to the DTD, it the ascii formatting gets botched if
+the
+.Li <heading>
+.No ...
+.Li </heading>
+tags are omitted following a
+sectioning tag such as
+.Li <sect> .
+.Pp
+Beginning and ending tags for the
+.Li <tag>
+element must occur on the same line for correct formatting with
+the
+.Fl ascii
+option.
diff --git a/usr.bin/sgmlfmt/sgmlfmt.pl b/usr.bin/sgmlfmt/sgmlfmt.pl
new file mode 100755
index 0000000..30db8a6
--- /dev/null
+++ b/usr.bin/sgmlfmt/sgmlfmt.pl
@@ -0,0 +1,722 @@
+#!/usr/bin/perl
+# $Id: sgmlfmt.pl,v 1.7 1995/09/22 18:24:32 jfieber Exp $
+
+# Format an sgml document tagged according to the linuxdoc DTD.
+# by John Fieber <jfieber@freebsd.org> for the FreeBSD documentation
+# project.
+#
+# Bugs:
+#
+# Text lines that start with a period (.) confuse the conversions that
+# use groff. The workaround is to make sure the SGML source doesn't
+# have any periods at the beginning of a line.
+#
+# Although legal by the DTD, it the ascii formatting gets botched if
+# the <heading></heading> tags are omitted following a <sect?>.
+#
+# Beginning and end tags for the <tag> element must occur on the same line.
+#
+# The whole approach of using sgmlsasp and passing a few things
+# through for processing by this script is doomed. This whole thing
+# needs to be re-thought and a better DTD should be used anyway.
+#
+#######################################################################
+
+# Look in a couple places for the SGML DTD and replacement files
+#
+
+require 'newgetopt.pl';
+
+if (-d "$ENV{'HOME'}/lib/sgml/FreeBSD") {
+ $sgmldir = "$ENV{'HOME'}/lib/sgml";
+}
+elsif (-d "$ENV{'HOME'}/sgml/FreeBSD") {
+ $sgmldir = "$ENV{'HOME'}/sgml";
+}
+elsif (-d "/usr/share/sgml/FreeBSD" ) {
+ $sgmldir = "/usr/share/sgml";
+}
+else {
+ die "Cannot locate sgml files!\n";
+}
+
+#
+# Locate the DTD, an SGML declaration, and the replacement files
+#
+
+$dtdbase = "$sgmldir/FreeBSD";
+$dtd = "$dtdbase/dtd/linuxdoc";
+if (-f "$dtd.dec") {
+ $decl = "$dtd.dec";
+}
+else {
+ $decl = "";
+}
+$replbase = "$dtdbase/rep";
+
+if (! $ENV{"SGML_PATH"}) {
+ $ENV{"SGML_PATH"} = "$sgmldir/%O/%C/%T";
+}
+
+sub usage {
+ print "Usage:\n";
+ print "sgmlfmt -f <format> [-i <namea> ...] [-links] [-ssi] file\n";
+ print "where <format> is one of: html, latex, ascii, nroff\n";
+}
+#
+# Look for the file specified on the command line
+#
+
+sub getfile {
+ local($filearg) = @_;
+ if (-f "$filearg.sgml") {
+ $file = "$filearg.sgml";
+ }
+ elsif (-f $filearg) {
+ $file = $filearg;
+ }
+ else {
+ return 0;
+ }
+ $fileroot = $file;
+ $fileroot =~ s/.*\///; # drop the path
+ $fileroot =~ s/\.sgml$//; # drop the .sgml
+ $filepath = $file;
+ $filepath =~ s/\/*[^\/]*$//;
+ if ($filepath eq "") {
+ $ENV{"SGML_PATH"} .= ":.";
+ }
+ else {
+ $ENV{"SGML_PATH"} .= ":$filepath/%S:.";
+ }
+ return 1;
+}
+
+#
+# A function to run sgmls and sgmlsasp on the input file.
+#
+# Arguments:
+# 1. A file handle for the output
+# 2. A replacement file (directory actually)
+#
+
+sub sgmlparse {
+ local($fhandle, $replacement) = @_;
+ $defines = join(" -i ", @opt_i);
+ if ($defines ne "") {
+ $defines = "-i $defines";
+ }
+ $ENV{'SGML_PATH'} = "$replbase/$replacement.%N:$ENV{'SGML_PATH'}";
+ open($fhandle, "sgmls $defines $decl $file | sgmlsasp $replbase/$replacement.mapping |");
+}
+
+#
+# Generate nroff output
+#
+
+sub gen_nroff {
+ open (outfile, ">$fileroot.nroff");
+ &sgmlparse(infile, "nroff");
+ $\ = "\n"; # automatically add newline on print
+ while (<infile>) {
+ chop;
+ # This is supposed to ensure that no text line starts
+ # with a dot (.), thus confusing groff, but it doesn't
+ # appear to work.
+ unless (/^\.DS/.../^\.DE/) {
+ s/^[ \t]{1,}(.*)/$1/g;
+ }
+ s/^\.[ \t].*/\\\&$&/g;
+ print outfile;
+ }
+ $\ = "";
+ close(infile);
+ close(outfile);
+}
+
+#
+# Generate ASCII output using groff
+#
+
+sub gen_ascii {
+ &sgmlparse(infile, "nroff");
+ open(outfile, "| groff -T ascii -t -ms | col -b > $fileroot.ascii");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+}
+
+#
+# Generate Postscript output using groff (this is suboptimal
+# for printed output!)
+#
+
+sub gen_ps {
+ &sgmlparse(infile, "grops");
+ open(outfile, "| groff -T ps -t -ms > $fileroot.ps");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+}
+
+#
+# Generate LaTeX output
+#
+
+sub gen_latex {
+ open(outfile, ">$fileroot.tex");
+ &sgmlparse(infile, "latex");
+ while (<infile>) {
+ print outfile;
+ }
+ close(infile);
+ close(outfile);
+}
+
+
+#
+# Generate HTML output.
+#
+# HTML is generated in two passes.
+#
+# The first pass takes the output from sgmlsasp and gathers information
+# about the structure of the document that is used in the sceond pass
+# for splitting the file into separate files. Targets for cross
+# references are also stored in this pass.
+#
+# Based on the information from the first pass, the second pass
+# generates a numbered series of HTML files, a "toplevel" file
+# containing the title, author, abstract and a brief table of
+# contents. A detailed table of contents is also generated. The
+# second pass generates links for cross references and URLs.
+
+#
+# Tunable parameters
+#
+$maxlevel = 3; # max section depth
+$num_depth = 4; # depth of numbering
+$m_depth = 2; # depth of menus
+
+
+$sc = 0; # section counter
+$filecount = 0; # file counter
+
+# Other variables:
+#
+# st_xxxx - Section Table. Arrays containing information about a
+# given section. To be accesssed via the section counter $sc.
+#
+# st_ol - The output level of the given section. I.E. how many
+# levels from the table of contents does it lie in terms
+# of HTML files which is distinct from <sect1>, <sect2> etc.
+# levels.
+#
+# st_sl - The absolute depth of a section. Contrast st_ol.
+#
+# st_num - The section number in the form X.Y.Z....
+#
+# st_file - The HTML file the section belongs to.
+#
+# st_header - The text of the section title.
+#
+# st_parent - The section number of the given sections parent.
+
+sub gen_html {
+ local($i, $sl);
+ $tmpfile = "/tmp/sgmlf.$$";
+
+ open(bar, ">$tmpfile");
+# print STDERR "(Pass 1...";
+ &sgmlparse(foo, "html");
+ while (<foo>) {
+ print bar;
+ # count up the number of files to be generated
+ # and gather assorted info about the document structure
+ if (/^<\@\@sect>/) {
+ $sl++; # current section level
+ $sc++; # current section number
+ $st_sl[$sc] = $sl;
+
+ # In case this section has subsections, set the parent
+ # pointer for this level to point at this section.
+ $parent_pointer[$sl] = $sc;
+
+ # Figure out who is the parent if this section.
+ $st_parent[$sc] = $parent_pointer[$sl - 1];
+
+ # Per level counters
+ $counter[$sl]++;
+ $counter[$sl + 1] = 0;
+
+ # calculate the section number in the form x.y.z.
+ if ($sl <= $num_depth) {
+ $st_num[$sc] = $st_num[$st_parent[$sc]] . "$counter[$sl].";
+ }
+
+ # calculate the file number and output level
+ if ($sl <= $maxlevel) {
+ $filecount++;
+ $st_ol[$sc] = $sl;
+ }
+ else {
+ $st_ol[$sc] = $maxlevel;
+ }
+
+ $st_file[$sc] = $filecount;
+
+ # Calculate the highest level node in which this
+ # node should appear as a menu item.
+ $st_pl[$sc] = $sl - $m_depth;
+ if ($st_pl[$sc] < 0) {
+ $st_pl[$sc] = 0;
+ }
+ if ($st_pl[$sc] > $maxlevel) {
+ $st_pl[$sc] = $maxlevel;
+ }
+ }
+ if (/^<\@\@endsect>/) {
+ $sl--;
+ }
+
+ # record section titles
+ if (/^<\@\@head>/) {
+ chop;
+ s/^<\@\@head>//;
+ $st_header[$sc] = $_;
+ }
+
+ # record the section number that a label occurs in
+ if (/^<\@\@label>/) {
+ chop;
+ s/^<\@\@label>//;
+ if ($references{$_} eq "") {
+ $references{$_} = "$filecount";
+ if ($opt_links) {
+ &extlink($_, "${fileroot}${filecount}.html");
+ }
+ }
+ else {
+ print STDERR "Warning: the label `$_' is multiply-defined.\n";
+ }
+ }
+ }
+ close(bar);
+
+ open(foofile, $tmpfile);
+ &html2html(foofile, "boo");
+
+ unlink($tmpfile);
+}
+
+#
+# HTML conversion, pass number 2
+#
+
+sub html2html {
+ local($infile, $outfile) = @_;
+ local($i);
+
+ $sc = 0;
+ push(@scs, $sc);
+
+ open(tocfile, ">${fileroot}_toc.html");
+ print tocfile "<HTML>\n";
+
+ while (<$infile>) {
+ # change `<' and `>' to `&lt;' and `&gt;' in <pre></pre>
+ if (/<pre>/.../<\/pre>/) {
+ s/</\&lt;/g;
+ s/\&lt;([\/]*)pre>/<\1pre>/g;
+ s/>/\&gt;/g;
+ s/<([\/]*)pre\&gt;/<\1pre>/g;
+ }
+
+ # remove extraneous empty paragraphs (it is arguable that this
+ # is really a bug with the DTD, but changing it would break
+ # almost every document written to this DTD.)
+ s/<p><\/p>//;
+
+ tagsw: {
+ # titles and headings
+ if (s/^<\@\@title>//) {
+ chop;
+ $st_header[0] = $_;
+ $st_parent[0] = -1;
+ print tocfile "<HEAD>\n<TITLE>$st_header[0]</TITLE>\n</HEAD>\n";
+ print tocfile "<H1>$st_header[0]</H1>\n";
+
+ $header[$st_ol[$sc]] =
+ "<HTML>\n<HEAD>\n<TITLE>$st_header[0]</TITLE>\n" .
+ "</HEAD>\n<BODY>\n";
+ if ($opt_ssi) { # Server Side Include hook
+ $header[$st_ol[$sc]] .=
+ "<!--#include virtual=\"./$fileroot.hdr\" -->";
+ }
+ $header[$st_ol[$sc]] .= "\n<H1>$st_header[0]</H1>\n";
+
+ $footer[$st_ol[$sc]] = "\n";
+ if ($opt_ssi) { # Server Side Include hook
+ $footer[$st_ol[$sc]] .=
+ "<!--#include virtual=\"./$fileroot.ftr\" -->";
+ }
+ $footer[$st_ol[$sc]] .= "\n</BODY>\n</HTML>\n";
+ last tagsw;
+ }
+
+ #
+ # HEADER begin
+ #
+ if (s/^<\@\@head>//) {
+ chop;
+
+ if ($part == 1) {
+ $text[0] .= "<H1>Part $partnum:<BR>$_";
+ last tagsw;
+ }
+
+ $href = "\"${fileroot}$st_file[$sc].html#$sc\"";
+
+ # set up headers and footers
+ if ($st_sl[$sc] > 0 && $st_sl[$sc] <= $maxlevel) {
+ $header[$st_ol[$sc]] =
+ "<HTML>\n<HEAD>\n<TITLE>$_</TITLE>\n</HEAD>\n<BODY>\n";
+ if ($opt_ssi) { # Server Side Include hook
+ $header[$st_ol[$sc]] .=
+ "<!--#include virtual=\"./$fileroot.hdr$st_ol[$sc]\" -->";
+ }
+ $header[$st_ol[$sc]] .= "\n$navbar[$st_ol[$sc]]\n<HR>\n";
+ $footer[$st_ol[$sc]] = "<HR>\n$navbar[$st_ol[$sc]]\n";
+ if ($opt_ssi) { # Server Side Include hook
+ $footer[$st_ol[$sc]] .=
+ "<!--#include virtual=\"./$fileroot.ftr$st_ol[$sc]\" -->";
+ }
+ $footer[$st_ol[$sc]] .= "\n</BODY>\n</HTML>";
+ }
+
+ # Add this to the master table of contents
+ print tocfile "<DD>$st_num[$sc] " .
+ "<A HREF=$href>$_";
+
+ # Calculate the <H?> level to use in the HTML file
+ $hlevel = $st_sl[$sc] - $st_ol[$sc] + 2;
+ $shlevel = $st_sl[$sc] - $st_ol[$sc] + 3;
+
+ $i = $st_ol[$sc];
+
+ # Add the section header
+ $text[$i] .= "<H$hlevel><A NAME=\"$sc\"></A>$st_num[$sc] $_";
+ $i--;
+
+ # And also to the parent
+ if ($st_sl[$sc] == $st_ol[$sc] && $i >= 0) {
+ $text[$i] .= "<H$shlevel>$st_num[$sc] " .
+ "<A HREF=$href>$_";
+ $i--;
+ }
+
+ # and to the grandparents
+ for (; $i >= $st_pl[$sc]; $i--) {
+ $text[$i] .= "<DD>$st_num[$sc] " .
+ "<A HREF=$href>$_";
+ }
+
+ last tagsw;
+ }
+
+ #
+ # HEADER end
+ #
+ if (s/^<\@\@endhead>//) {
+ if ($part == 1) {
+ $text[0] .= "</H1>\n";
+ $part = 0;
+ last tagsw;
+ }
+ print tocfile "</A></DD>\n";
+
+ $i = $st_ol[$sc];
+
+ # Close the section header
+ $text[$i] .= "</H$hlevel>\n";
+ $i--;
+
+ # in the parent...
+ if ($st_sl[$sc] == $st_ol[$sc] && $i >= 0) {
+ $text[$i] .= "</A></H$shlevel>\n";
+ $i--;
+ }
+
+ # in the grandparent...
+ for (; $i >= $st_pl[$sc]; $i--) {
+ $text[$i] .= "</A></DD>\n";
+ }
+ last tagsw;
+ }
+
+ # sectioning
+ if (s/^<\@\@part>//) {
+ $part = 1;
+ $partnum++;
+ last tagsw;
+ }
+
+ #
+ # BEGINNING of a section
+ #
+ if (s/^<\@\@sect>//) {
+ # Increment the section counter and save it on a stack
+ # for future reference.
+ $sc++;
+ push(@scs, $sc);
+
+ # Set up the navigation bar
+ if ($st_file[$sc] > $st_file[$sc - 1]) {
+ &navbar($st_file[$sc], $filecount, $sc);
+ }
+
+ # Prepare for menu entries in the table of contents and
+ # parent file(s).
+ if ($st_sl[$sc - 1] < $st_sl[$sc]) {
+ print tocfile "<DL>\n";
+ $i = $st_ol[$sc] - 1 - ($st_sl[$sc] == $st_ol[$sc]);
+ for (; $i >= $st_pl[$sc]; $i--) {
+ $text[$i] .= "<DL>\n";
+ }
+ }
+ last tagsw;
+ }
+
+ #
+ # END of a section
+ #
+ if (s/^<\@\@endsect>//) {
+
+ # Remember the section number! Subsections may have
+ # altered the global $sc variable.
+ local ($lsc) = pop(@scs);
+
+ # Close off subsection menus we may have created in
+ # parent file(s).
+ if ($st_sl[$lsc] > $st_sl[$sc + 1]) {
+ print tocfile "</DL>\n";
+ $i = $st_ol[$lsc] - 1 - ($st_sl[$lsc] == $st_ol[$lsc]);
+ for (; $i >= $st_pl[$lsc]; $i--) {
+ $text[$i] .= "</DL>\n";
+ }
+ }
+
+ # If this section is below $maxlevel, write it now.
+ if ($st_sl[$lsc] <= $maxlevel) {
+ open(SECOUT, ">${fileroot}$st_file[$lsc].html");
+ print SECOUT "$header[$st_ol[$lsc]] $text[$st_ol[$lsc]] " .
+ "$footer[$st_ol[$lsc]]";
+ $text[$st_ol[$lsc]] = "";
+ close(SECOUT);
+ }
+ last tagsw;
+ }
+
+ # cross references
+ if (s/^<\@\@label>//) {
+ chop;
+ $text[$st_ol[$sc]] .= "<A NAME=\"$_\"></A>";
+ last tagsw;
+ }
+ if (s/^<\@\@ref>//) {
+ chop;
+ $refname = $_;
+ if ($references{$_} eq "") {
+ print "Warning: Reference to $_ has no defined target\n";
+ }
+ else {
+ $text[$st_ol[$sc]] .=
+ "<A HREF=\"${fileroot}$references{$_}.html#$_\">";
+ }
+ last tagsw;
+ }
+ if (s/^<\@\@endref>//) {
+# $text[$st_ol[$sc]] .= "</A>";
+ last tagsw;
+ }
+ if (s/^<\@\@refnam>//) {
+ $text[$st_ol[$sc]] .= "$refname</A>";
+ last tagsw;
+ }
+ # URLs
+ if (s/^<\@\@url>//) {
+ chop;
+ $urlname = $_;
+ $text[$st_ol[$sc]] .= "<A HREF=\"$urlname\">";
+ last tagsw;
+ }
+ if (s/^<\@\@urlnam>//) {
+ $text[$st_ol[$sc]] .= "$urlname</A>";
+ last tagsw;
+ }
+ if (s/^<\@\@endurl>//) {
+# $text[$st_ol[$sc]] .= "</A>";
+ last tagsw;
+ }
+
+
+ # If nothing else did anything with this line, just print it.
+ $text[$st_ol[$sc]] .= "$_";
+ }
+ }
+
+ print tocfile "</HTML>";
+ open(SECOUT, ">$fileroot.html");
+ print SECOUT "$header[0] $text[0] $footer[0]";
+ close(SECOUT);
+ close tocfile;
+}
+
+# navbar
+#
+# Generate a navigation bar to go on the top and bottom of the page.
+
+sub navbar {
+ local ($fnum, $fmax, $sc) = @_;
+ local ($i, $itext, $prv, $nxt, $colon);
+
+ $colon = "<b>:</b>";
+
+ # Generate the section hierarchy
+
+ $navbar[$st_ol[$sc]] =
+ "<A HREF=\"${fileroot}.html\"><EM>$st_header[0]</EM></A>\n";
+ $i = $st_parent[$sc];
+ while ($i > 0) {
+ $itext = " $colon <A HREF=\"${fileroot}$st_file[$i].html\"><EM>$st_header[$i]</EM></A>\n$itext";
+ $i = $st_parent[$i];
+ }
+ $navbar[$st_ol[$sc]] .= "$itext $colon <EM>$st_header[$sc]</EM><BR>\n";
+
+ # Generate previous and next pointers
+
+ # Previous pointer must be in a different file AND must be at the
+ # same or higher section level. If the current node is the
+ # beginning of a chapter, then previous will go to the beginning
+ # of the previous chapter, not the end of the previous chapter.
+
+ $prv = $sc;
+ while ($prv >= 0 && $st_file[$prv] >= $st_file[$sc] - 1) {
+ $prv--;
+ }
+ $prv++;
+ $navbar[$st_ol[$sc]] .=
+ "<b>Previous:</b> <A HREF=\"${fileroot}$st_file[$prv].html\"><EM>$st_header[$prv]</EM></A><BR>\n";
+
+ # Then next pointer must be in a higher numbered file OR the home
+ # page of the document.
+
+ $nxt = $sc;
+ if ($st_file[$nxt] == $filecount) {
+ $nxt = 0;
+ }
+ else {
+ while ($st_file[$nxt] == $st_file[$sc]) {
+ $nxt++;
+ }
+ }
+
+ $navbar[$st_ol[$sc]] .=
+ "<b>Next:</b> <A HREF=\"${fileroot}$st_file[$nxt].html\"><EM>$st_header[$nxt]</EM></A>\n";
+
+ $navbar[$st_ol[$sc]] .= "\n";
+
+}
+
+
+# extlink
+#
+# creates a symbolic link from the name in a reference to the numbered
+# html file. Since the file number that any given section has is
+# subject to change as the document goes through revisions, this allows
+# for a fixed target that separate documents can hook into.
+#
+# Slashes (/) in the reference are converted to percents (%) while
+# spaces ( ) are converted to underscores (_);
+
+sub extlink {
+ local ($ref, $fn) = @_;
+
+ $ref =~ s/\//%/g;
+ $ref =~ s/ /_/g;
+
+ $file = "$ref.html";
+
+ if (-e $file) {
+ if (-l $file) {
+ unlink($file);
+ symlink($fn, $file);
+ }
+ else {
+ print "Warning: $file exists and is not a symbolic link\n";
+ }
+ }
+ else {
+ symlink($fn, $file);
+ }
+}
+
+# Now, read the command line and take appropriate action
+
+sub main {
+ # Check arguments
+ if (!&NGetOpt('f=s', 'links', 'ssi', 'i:s@')) {
+ &usage;
+ exit 1;
+ }
+ if (@ARGV == 0) {
+ print "An input file must be specified.\n";
+ &usage;
+ exit 1;
+ }
+ if (&getfile($ARGV[0]) == 0) {
+ print "Cannot locate specified file: $ARGV[0]\n";
+ &usage;
+ exit 1;
+ }
+
+ # Generate output
+ if ($opt_f eq 'html') {
+ print "generating $fileroot.html";
+ if ($opt_links == 1) {
+ print " with external links";
+ }
+ print "...\n"; &gen_html();
+ }
+ elsif ($opt_f eq 'tex' || $opt_f eq 'latex') {
+ print "generating $fileroot.tex...\n"; &gen_latex();
+ }
+ elsif ($opt_f eq 'nroff') {
+ print "generating $fileroot.nroff...\n"; &gen_nroff();
+ }
+ elsif ($opt_f eq 'ascii') {
+ print "generating $fileroot.ascii...\n"; &gen_ascii();
+ }
+ elsif ($opt_f eq 'ps') {
+ print "generating $fileroot.ps...\n"; &gen_ps();
+ }
+ else {
+ if ($opt_f eq "") {
+ print "An output format must be specified with the -f option.\n";
+ }
+ else {
+ print "\"$opt_f\" is an unknown output format.\n";
+ }
+ &usage;
+ exit 1;
+ }
+
+}
+
+&main;
+
+exit 0;
+
diff --git a/usr.bin/sgmls/LICENSE b/usr.bin/sgmls/LICENSE
new file mode 100644
index 0000000..576ca35
--- /dev/null
+++ b/usr.bin/sgmls/LICENSE
@@ -0,0 +1,43 @@
+ LICENSE AND DISCLAIMER OF WARRANTIES
+
+ Standard Generalized Markup Language Users' Group (SGMLUG)
+ SGML Parser Materials
+
+ 1. License
+
+SGMLUG hereby grants to any user: (1) an irrevocable royalty-free,
+worldwide, non-exclusive license to use, execute, reproduce, display,
+perform and distribute copies of, and to prepare derivative works
+based upon these materials; and (2) the right to authorize others to
+do any of the foregoing.
+
+ 2. Disclaimer of Warranties
+
+(a) The SGML Parser Materials are provided "as is" to any USER. USER
+assumes responsibility for determining the suitability of the SGML
+Parser Materials for its use and for results obtained. SGMLUG makes
+no warranty that any errors have been eliminated from the SGML Parser
+Materials or that they can be eliminated by USER. SGMLUG shall not
+provide any support maintenance or other aid to USER or its licensees
+with respect to SGML Parser Materials. SGMLUG shall not be
+responsible for losses of any kind resulting from use of the SGML
+Parser Materials including (without limitation) any liability for
+business expense, machine downtime, or damages caused to USER or third
+parties by any deficiency, defect, error, or malfunction.
+
+(b) SGMLUG DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, ARISING OUT
+OF OR RELATING TO THE SGML PARSER MATERIALS OR ANY USE THEREOF,
+INCLUDING (WITHOUT LIMITATION) ANY WARRANTY WHATSOEVER AS TO THE
+FITNESS FOR A PARTICULAR USE OR THE MERCHANTABILITY OF THE SGML PARSER
+MATERIALS.
+
+(c) In no event shall SGMLUG be liable to USER or third parties
+licensed by USER for any indirect, special, incidental, or
+consequential damages (including lost profits).
+(d) SGMLUG has no knowledge of any conditions that would impair its right
+to license the SGML Parser Materials. Notwithstanding the foregoing,
+SGMLUG does not make any warranties or representations that the
+SGML Parser Materials are free of claims by third parties of patent,
+copyright infringement or the like, nor does SGMLUG assume any
+liability in respect of any such infringement of rights of third
+parties due to USER's operation under this license.
diff --git a/usr.bin/sgmls/Makefile b/usr.bin/sgmls/Makefile
new file mode 100644
index 0000000..62c6cea
--- /dev/null
+++ b/usr.bin/sgmls/Makefile
@@ -0,0 +1,9 @@
+#
+# Bmake file for sgmls
+# $Id:$
+#
+
+SUBDIR= libsgmls sgmls sgmlsasp rast
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.bin/sgmls/Makefile.inc b/usr.bin/sgmls/Makefile.inc
new file mode 100644
index 0000000..1e4fc2b
--- /dev/null
+++ b/usr.bin/sgmls/Makefile.inc
@@ -0,0 +1,13 @@
+#
+# Bmakefile for rast
+#
+# $id$
+#
+
+.include "${.CURDIR}/../../Makefile.inc"
+
+.if exists(${.CURDIR}/../libsgmls/obj)
+LIBSGMLS= ${.CURDIR}/../libsgmls/obj/libsgmls.a
+.else
+LIBSGMLS= ${.CURDIR}/../libsgmls/libsgmls.a
+.endif \ No newline at end of file
diff --git a/usr.bin/sgmls/README b/usr.bin/sgmls/README
new file mode 100644
index 0000000..dd6e257
--- /dev/null
+++ b/usr.bin/sgmls/README
@@ -0,0 +1,138 @@
+$Id:$
+
+This the sgmls release 1.1 SGML parser written by James Clark
+jjc@jclark.com, repackaged for FreeBSD. The original source may be
+obtained from ftp://ftp.jclark.com/.
+
+Pieces removed include:
+ * Test documents: Compiled on FreeBSD, sgmls passes all tests.
+ * sgml-mode.el: The sole file covered by the GNU GPL. This is not
+ installed anyway and anyone wishing to do serious SGML editing
+ would be best to get the psgml package.
+ * Makefiles and config files for other operating systems (vms, dos,
+ cms).
+ * Formatted versions of the man pages.
+
+
+20-Apr-1995 John Fieber <jfieber@freebsd.org>
+
+
+The original README and TODO follow.
+----------------------------------------------------------------------
+This is sgmls, an SGML parser derived from the ARCSGML parser
+materials which were written by Charles F. Goldfarb. (These are
+available for anonymous ftp from ftp.ifi.uio.no [128.240.88.1] in the
+directory SIGhyper/SGMLUG/distrib.)
+
+The version number is given in the file version.c.
+
+The file INSTALL contains installation instructions.
+
+The file NEWS describes recent user-visible changes.
+
+The file sgmls.man contains a Unix manual page; sgmls.txt is the
+formatted version of this.
+
+The file sgml-mode.el contains a very simple SGML mode for GNU Emacs.
+
+The files sgmls.c and sgmls.h contain a small library for parsing the
+output of sgmls. This is used by sgmlsasp, which translates the
+output of sgmls using an ASP replacement file, and by rast, which
+translates the output of sgmls to the format of a RAST result. The
+files sgmlsasp.man and rast.man contain Unix manual pages for sgmlsasp
+and rast; sgmlsasp.txt and rast.txt are the formatted versions of
+these.
+
+The file LICENSE contains the license which applies to arcsgml and
+accordingly to those parts of sgmls derived from arcsgml. See also
+the copyright notice at the beginning of sgmlxtrn.c. The parts that
+were written by me are in the public domain (any files that were
+written entirely by me contain a comment to that effect.) The file
+sgml-mode.el is covered by the GNU GPL.
+
+Please report any bugs to me. When reporting bugs, please include the
+version number, details of your machine, OS and compiler, and a
+complete self-contained file that will allow me to reproduce the bug.
+
+James Clark
+jjc@jclark.com
+
+----------------------------------------------------------------------
+Warn about mixed content models where #PCDATA can't occur everywhere.
+
+Perhaps there should be a configuration option saying what a control
+character is for the purpose of SHUNCHAR CONTROLS.
+
+Should the current character that is printed in error messages be
+taken from be taken from the file entity or the current entity?
+
+Refine SYS_ action. If we distinguish DELNONCH in lexmark, lexgrp,
+lexsd, we can have separate action that ignores the following
+character as well.
+
+Should RSs in CDATA/SDATA entities be ignored as specified in 322:1-2?
+Similarily, do the rules on REs in 322:3-11 apply to CDATA/SDATA
+entities? (I don't think they count as being `in content'.)
+
+What should the entity manager do when it encounters code 13 in an
+input file? (Currently it treats it as an RE.)
+
+Document when invalid exclusions are detected.
+
+Option not to perform capacity checking.
+
+Give a warning if the recommendation of 422:1-3 is contravened.
+
+Should an empty CDATA/RCDATA marked section be allowed in the document
+type declaration subset?
+
+Include example of use of SGML_PATH in documentation.
+
+Try to detect the situation in 310:8-10 (but see 282:1-2).
+
+Resize hash tables if they become too full.
+
+Say something in the man page about message catalogues.
+
+Consider whether support for SHORTREF NONE requires further changes
+(other than disallowing short reference mapping declaration).
+
+Fake /dev/fd/N and /dev/stdin for systems that don't provide it.
+
+Improve the effficiency of the entity manager by not closing and
+reopening files. If we run out of FILEs choose the stream with the
+fewest bytes remaining to be read, and read the rest of it into
+memory. Each entity level will have its own read buffer.
+
+Support multi-line error messages: automatically indent after
+newline. (We could output to a temporary file first, then copy to
+stderr replacing newlines by newline+indent).
+
+Option that says to output out of context things.
+
+Divide up formal public identifier errors. Give these errors their
+own type code.
+
+Consider whether, when OMITTAG is NO, we need to change interpretation
+of an empty start-tag (7.4.1.1).
+
+Possibly turn errors 70 and 136 into warnings.
+
+Make things work with NORMSEP > 2. Would need to keep track of number
+of CDATA and SDATA entities in CDATA attributes.
+
+Handle `SCOPE INSTANCE'.
+
+In entgen.c, truncate filenames for OSs that don't do this themselves.
+
+Provide an option that specifies that maximum number of errors; when
+this limit is exceeded sgmls would exit.
+
+Document non-portable assumptions in the code.
+
+Option to write out SGML declaration. In this case make it write out
+APPINFO parameter.
+
+Allow there to be catalogs mapping public ids to filenames.
+Environment variable SGML_CATALOG containing list of filenames of
+catalogs.
diff --git a/usr.bin/sgmls/configure b/usr.bin/sgmls/configure
new file mode 100755
index 0000000..7fd1968
--- /dev/null
+++ b/usr.bin/sgmls/configure
@@ -0,0 +1,617 @@
+#!/bin/sh
+# Generate config.h from unix.cfg.
+
+trap 'rm -f doit doit.c doit.o doit.log config.out; exit 1' 1 2 3 15
+
+on=
+off=
+CC=${CC-cc}
+
+# Normally we use VARARGS if __STDC__ is not defined.
+# Test whether this assumption is wrong.
+
+cat >doit.c <<\EOF
+#ifdef __STDC__
+#include <stdarg.h>
+int foo(char *s,...)
+{
+ va_list ap;
+
+ va_start(ap, s);
+ va_end(ap);
+ return 0;
+}
+#else
+int foo = 0;
+#endif
+EOF
+
+$CC $CFLAGS -c doit.c >/dev/null 2>&1
+if test $? -ne 0
+then
+ on="$on VARARGS"
+fi
+
+cat >doit.c <<\EOF
+#include <stddef.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ off="$off STDDEF_H_MISSING"
+else
+ on="$on STDDEF_H_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <stdlib.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ off="$off STDLIB_H_MISSING"
+else
+ on="$on STDLIB_H_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <limits.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ off="$off LIMITS_H_MISSING"
+else
+ on="$on LIMITS_H_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <vfork.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ on="$on HAVE_VFORK_H"
+else
+ off="$off HAVE_VFORK_H"
+fi
+
+cat >doit.c <<\EOF
+#include <unistd.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ on="$on HAVE_UNISTD_H"
+else
+ off="$off HAVE_UNISTD_H"
+fi
+
+cat >doit.c <<\EOF
+#include <sys/types.h>
+#include <sys/stat.h>
+int foo = 0;
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ on="$on HAVE_SYS_STAT_H"
+else
+ off="$off HAVE_SYS_STAT_H"
+fi
+
+cat >doit.c <<\EOF
+/* Exit normally unless we need to use isascii. */
+
+#include <ctype.h>
+#include <signal.h>
+
+static int whoops()
+{
+ _exit(1);
+}
+
+main()
+{
+ int c;
+#ifdef isascii
+#ifdef SIGSEGV
+ signal(SIGSEGV, whoops);
+#endif
+#ifdef SIGBUS
+ signal(SIGBUS, whoops);
+#endif
+#ifdef SIGIOT
+ signal(SIGIOT, whoops);
+#endif
+
+ for (c = 128; c < 256; c++) {
+ if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5'
+ || c == '6' || c == '7' || c == '8' || c == '9') {
+ if (!isdigit(c) || isalpha(c) || iscntrl(c) || isspace(c) || ispunct(c))
+ exit(1);
+ }
+ else if (isdigit(c))
+ exit(1);
+ else if (isalpha(c)) {
+ if (iscntrl(c) || isspace(c) || ispunct(c)
+ || (islower(c) && toupper(c) != c && !isupper(toupper(c)))
+ || (isupper(c) && tolower(c) != c && !islower(tolower(c))))
+ exit(1);
+ }
+ else if (islower(c) || isupper(c))
+ exit(1);
+ else if (iscntrl(c)) {
+ if (ispunct(c))
+ exit(1);
+ }
+ }
+#endif /* isascii */
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ # This tries to find the symbol that looks like the array
+ # used by <ctype.h>, and sees if its length appears to be 128
+ # rather than 256.
+ if test 1 = `(nm -n doit 2>/dev/null) | awk '
+BEGIN {
+ weight["0"] = 0;
+ weight["1"] = 1;
+ weight["2"] = 2;
+ weight["3"] = 3;
+ weight["4"] = 4;
+ weight["5"] = 5;
+ weight["6"] = 6;
+ weight["7"] = 7;
+ weight["8"] = 8;
+ weight["9"] = 9;
+ weight["a"] = weight["A"] = 10;
+ weight["b"] = weight["B"] = 11;
+ weight["c"] = weight["C"] = 12;
+ weight["d"] = weight["D"] = 13;
+ weight["e"] = weight["E"] = 14;
+ weight["f"] = weight["F"] = 15;
+}
+
+/^[0-9a-zA-Z]* D .*ctype/ && ctype_nr == 0 {
+ ctype_nr = NR;
+ addr = 0;
+ len = length($1);
+ for (i = 1; i <= len; i++)
+ addr = addr*16 + weight[substr($1, i, 1)];
+}
+
+/^[0-9a-zA-Z]* D / && NR == ctype_nr + 1 {
+ next_addr = 0;
+ len = length($1);
+ for (i = 1; i <= len; i++)
+ next_addr = next_addr*16 + weight[substr($1, i, 1)];
+}
+
+END {
+ size = next_addr - addr;
+ if (size >= 128 && size < 256)
+ print "1";
+ else
+ print "0";
+}'`
+ then
+ on="$on USE_ISASCII"
+ else
+ if ((yes | man 3 ctype) 2>/dev/null) \
+ | sed -e 's/.//g' -e 's/ *$//' -e '/de-$/N' \
+ -e 's/-\n//g' -e '/defined$/N' -e '/only$/N' \
+ -e '/where$/N' -e '/isascii$/N' -e '/is$/N' \
+ -e 's/\n/ /g' -e 's/ */ /g' \
+ | grep "defined only where isascii is true" >/dev/null
+ then
+ on="$on USE_ISASCII"
+ else
+ off="$off USE_ISASCII"
+ fi
+ fi
+else
+ on="$on USE_ISASCII"
+fi
+
+cat >doit.c <<\EOF
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ remove("foo");
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ off="$off REMOVE_MISSING"
+else
+ on="$on REMOVE_MISSING"
+fi
+
+cat >doit.c <<\EOF
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ getopt(argc, argv, "v");
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_GETOPT"
+else
+ off="$off HAVE_GETOPT"
+fi
+
+cat >doit.c <<\EOF
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ access("foo", 4);
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_ACCESS"
+else
+ off="$off HAVE_ACCESS"
+fi
+
+cat >doit.c <<\EOF
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ vfork();
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_VFORK"
+else
+ off="$off HAVE_VFORK"
+fi
+
+cat >doit.c <<\EOF
+main(argc, argv)
+int argc;
+char **argv;
+{
+
+ if (argc == 0) {
+ int status;
+ waitpid(-1, &status, 0);
+ }
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_WAITPID"
+else
+ off="$off HAVE_WAITPID"
+fi
+
+cat >doit.c <<\EOF
+#include <string.h>
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ strerror(0);
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ off="$off STRERROR_MISSING"
+else
+ on="$on STRERROR_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <strings.h>
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ bcopy((char *)0, (char *)0, 0);
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ # Only use BSD_STRINGS if ANSI string functions don't work.
+ cat >doit.c <<\EOF
+#include <string.h>
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ memcpy((char *)0, (char *)0, 0);
+ exit(0);
+}
+EOF
+
+ if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+ then
+ off="$off BSD_STRINGS"
+ else
+ on="$on BSD_STRINGS"
+ fi
+else
+ off="$off BSD_STRINGS"
+fi
+
+cat >doit.c <<\EOF
+#include <signal.h>
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0)
+ raise(SIGINT);
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ off="$off RAISE_MISSING"
+else
+ on="$on RAISE_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <stdio.h>
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0) {
+ fpos_t pos;
+ fsetpos(stdin, &pos);
+ fgetpos(stdin, &pos);
+ }
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ off="$off FPOS_MISSING"
+else
+ on="$on FPOS_MISSING"
+fi
+
+cat >doit.c <<\EOF
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0) {
+ pid_t pid;
+ int status;
+ long n = sysconf(_SC_OPEN_MAX);
+ pid = waitpid(-1, &status, 0);
+ WIFSTOPPED(status);
+ WIFSIGNALED(status);
+ WIFEXITED(status);
+ WEXITSTATUS(status);
+ WTERMSIG(status);
+ WSTOPSIG(status);
+ }
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on POSIX"
+else
+ off="$off POSIX"
+fi
+
+cat >doit.c <<\EOF
+#include <stdio.h>
+#include <signal.h>
+
+static int whoops()
+{
+ _exit(1);
+}
+
+main()
+{
+ char buf[30];
+#ifdef SIGSEGV
+ signal(SIGSEGV, whoops);
+#endif
+#ifdef SIGBUS
+ signal(SIGBUS, whoops);
+#endif
+#ifdef SIGIOT
+ signal(SIGIOT, whoops);
+#endif
+ sprintf(buf, "%2$s%2$s%1$s%1$s", "bar", "foo");
+ exit(!!strcmp(buf, "foofoobarbar"));
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_EXTENDED_PRINTF"
+else
+ off="$off HAVE_EXTENDED_PRINTF"
+fi
+
+cat >doit.c <<\EOF
+#include <nl_types.h>
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ if (argc == 0) {
+ nl_catd d = catopen("foo", 0);
+ catgets(d, 1, 1, "default");
+ catclose(d);
+ }
+ exit(0);
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ on="$on HAVE_CAT"
+else
+ off="$off HAVE_CAT"
+fi
+
+cat >doit.c <<\EOF
+#include <limits.h>
+
+char c = UCHAR_MAX;
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+#if CHAR_MIN < 0
+ exit(!(c < 0));
+#else
+ exit(!(c > 0));
+#endif
+}
+EOF
+
+if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+then
+ char_signed=
+else
+ cat >doit.c <<\EOF
+main()
+{
+ int i;
+
+ for (i = 0; i < 512; i++) {
+ char c = (char)i;
+ if (c < 0)
+ exit(1);
+ }
+ exit(0);
+}
+EOF
+
+ if $CC $CFLAGS -o doit doit.c $LIBS >/dev/null 2>&1 && ./doit 2>/dev/null
+ then
+ char_signed=0
+ else
+ char_signed=1
+ fi
+fi
+
+cat >doit.c <<\EOF
+
+typedef void VOID;
+
+extern VOID bar();
+
+VOID foo()
+{
+}
+EOF
+
+if $CC $CFLAGS -c doit.c >/dev/null 2>&1
+then
+ void_ret=void
+else
+ void_ret=int
+fi
+
+
+cat >doit.c <<\EOF
+
+void *foo()
+{
+ static char *buf;
+ return buf;
+}
+EOF
+
+if $CC $CFLAGS -c doit.c >doit.log 2>&1
+then
+ if test -s doit.log
+ then
+ void_star="char \*"
+ else
+ void_star="void \*"
+ fi
+
+else
+ void_star="char \*"
+fi
+
+edit=
+
+rm -f doit.c doit doit.log doit.o
+
+for var in $on
+do
+ edit="$edit -e 's;^/\\* *\\(#define $var [^/]*\\) *\\*/;\\1;'"
+done
+for var in $off
+do
+ edit="$edit -e 's;^#define $var [^/]*;/* & */;'"
+done
+
+if test -n "$char_signed"
+then
+ edit="$edit -e 's;^/\\* *\\(#define CHAR_SIGNED $char_signed\\) *\\*/;\\1;'"
+fi
+
+edit="$edit -e 's/^typedef .*VOID;/typedef $void_ret VOID;/'"
+edit="$edit -e 's/^typedef .*UNIV;/typedef ${void_star}UNIV;/'"
+
+if test "X$(PREFIX)" != "X/usr/local"
+then
+ edit="$edit -e '/DEFAULT_PATH/s;/usr/local;$PREFIX;g'"
+fi
+
+eval sed $edit unix.cfg ">config.out"
+
+mv config.out config.h
+
+exit 0
diff --git a/usr.bin/sgmls/libsgmls/Makefile b/usr.bin/sgmls/libsgmls/Makefile
new file mode 100644
index 0000000..e94fcc4
--- /dev/null
+++ b/usr.bin/sgmls/libsgmls/Makefile
@@ -0,0 +1,17 @@
+#
+# Bmakefile for libsgmls
+#
+# $id$
+#
+
+LIB= sgmls
+SRCS= sgmls.c
+
+CFLAGS+= -I${.CURDIR}/../sgmls
+
+NOMAN= noman
+NOPROFILE= noprofile
+
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.bin/sgmls/libsgmls/sgmls.c b/usr.bin/sgmls/libsgmls/sgmls.c
new file mode 100644
index 0000000..cbb03f1
--- /dev/null
+++ b/usr.bin/sgmls/libsgmls/sgmls.c
@@ -0,0 +1,1036 @@
+/* sgmls.c:
+ Library for reading output of sgmls.
+
+ Written by James Clark (jjc@jclark.com). */
+
+#include "config.h"
+#include "std.h"
+#include "sgmls.h"
+#include "lineout.h"
+
+#ifdef __GNUC__
+#define NO_RETURN volatile
+#else
+#define NO_RETURN /* as nothing */
+#endif
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+#ifndef __STDC__
+#define const /* as nothing */
+#endif
+
+typedef struct sgmls_data data_s;
+typedef struct sgmls_notation notation_s;
+typedef struct sgmls_internal_entity internal_entity_s;
+typedef struct sgmls_external_entity external_entity_s;
+typedef struct sgmls_entity entity_s;
+typedef struct sgmls_attribute attribute_s;
+typedef struct sgmls_event event_s;
+
+/* lists are sorted in reverse order of level */
+struct list {
+ int subdoc_level; /* -1 if associated with finished subdoc */
+ struct list *next;
+ char *name;
+};
+
+struct entity_list {
+ int subdoc_level;
+ struct entity_list *next;
+ entity_s entity;
+};
+
+struct notation_list {
+ int subdoc_level;
+ struct notation_list *next;
+ notation_s notation;
+};
+
+struct sgmls {
+ FILE *fp;
+ char *buf;
+ unsigned buf_size;
+ struct entity_list *entities;
+ struct notation_list *notations;
+ attribute_s *attributes;
+ unsigned long lineno;
+ char *filename;
+ unsigned filename_size;
+ unsigned long input_lineno;
+ int subdoc_level;
+ char **files; /* from `f' commands */
+ int nfiles;
+ char *sysid; /* from `s' command */
+ char *pubid; /* from `p' command */
+};
+
+enum error_code {
+ E_ZERO, /* Not an error */
+ E_NOMEM, /* Out of memory */
+ E_BADESCAPE, /* Bad escape */
+ E_NULESCAPE, /* \000 other than in data */
+ E_NUL, /* A null input character */
+ E_BADENTITY, /* Reference to undefined entity */
+ E_INTERNALENTITY, /* Internal entity when external was needed */
+ E_SYSTEM, /* System input error */
+ E_COMMAND, /* Bad command letter */
+ E_MISSING, /* Missing arguments */
+ E_NUMBER, /* Not a number */
+ E_ATTR, /* Bad attribute type */
+ E_BADNOTATION, /* Reference to undefined notation */
+ E_BADINTERNAL, /* Bad internal entity type */
+ E_BADEXTERNAL, /* Bad external entity type */
+ E_EOF, /* EOF in middle of line */
+ E_SDATA, /* \| other than in data */
+ E_LINELENGTH /* line longer than UNSIGNED_MAX */
+};
+
+static char *errlist[] = {
+ 0,
+ "Out of memory",
+ "Bad escape",
+ "\\0 escape not in data",
+ "Nul character in input",
+ "Reference to undefined entity",
+ "Internal entity when external was needed",
+ "System input error",
+ "Bad command letter",
+ "Missing arguments",
+ "Not a number",
+ "Bad attribute type",
+ "Reference to undefined notation",
+ "Bad internal entity type",
+ "Bad external entity type",
+ "EOF in middle of line",
+ "\\| other than in data",
+ "Too many V commands",
+ "Input line too long"
+};
+
+static void NO_RETURN error P((enum error_code));
+static int parse_data P((char *, unsigned long *));
+static void parse_location P((char *, struct sgmls *));
+static void parse_notation P((char *, notation_s *));
+static void parse_internal_entity P((char *, internal_entity_s *));
+static void parse_external_entity
+ P((char *, struct sgmls *, external_entity_s *));
+static void parse_subdoc_entity P((char *, external_entity_s *));
+static attribute_s *parse_attribute P((struct sgmls *, char *));
+static void grow_datav P((void));
+static char *unescape P((char *));
+static char *unescape_file P((char *));
+static int unescape1 P((char *));
+static char *scan_token P((char **));
+static int count_args P((char *));
+static struct list *list_find P((struct list *, char *, int));
+static UNIV xmalloc P((unsigned));
+static UNIV xrealloc P((UNIV , unsigned));
+static char *strsave P((char *));
+static int read_line P((struct sgmls *));
+static notation_s *lookup_notation P((struct sgmls *, char *));
+static entity_s *lookup_entity P((struct sgmls *, char *));
+static external_entity_s *lookup_external_entity P((struct sgmls *, char *));
+static void define_external_entity P((struct sgmls *, external_entity_s *));
+static void define_internal_entity P((struct sgmls *, internal_entity_s *));
+static void define_notation P((struct sgmls *, notation_s *));
+static data_s *copy_data P((data_s *, int));
+static void list_finish_level P((struct list **, int));
+static void add_attribute P((attribute_s **, attribute_s *));
+static void default_errhandler P((int, char *, unsigned long));
+
+#define xfree(s) do { if (s) free(s); } while (0)
+
+static sgmls_errhandler *errhandler = default_errhandler;
+static unsigned long input_lineno = 0;
+
+static data_s *datav = 0;
+static int datav_size = 0;
+
+struct sgmls *sgmls_create(fp)
+ FILE *fp;
+{
+ struct sgmls *sp;
+
+ sp = (struct sgmls *)malloc(sizeof(struct sgmls));
+ if (!sp)
+ return 0;
+ sp->fp = fp;
+ sp->entities = 0;
+ sp->notations = 0;
+ sp->attributes = 0;
+ sp->lineno = 0;
+ sp->filename = 0;
+ sp->filename_size = 0;
+ sp->input_lineno = 0;
+ sp->buf_size = 0;
+ sp->buf = 0;
+ sp->subdoc_level = 0;
+ sp->files = 0;
+ sp->nfiles = 0;
+ sp->sysid = 0;
+ sp->pubid = 0;
+ return sp;
+}
+
+void sgmls_free(sp)
+ struct sgmls *sp;
+{
+ struct entity_list *ep;
+ struct notation_list *np;
+
+ if (!sp)
+ return;
+ xfree(sp->filename);
+ sgmls_free_attributes(sp->attributes);
+
+ for (ep = sp->entities; ep;) {
+ struct entity_list *tem = ep->next;
+ if (ep->entity.is_internal) {
+ xfree(ep->entity.u.internal.data.s);
+ free(ep->entity.u.internal.name);
+ }
+ else {
+ int i;
+ for (i = 0; i < ep->entity.u.external.nfilenames; i++)
+ xfree(ep->entity.u.external.filenames[i]);
+ xfree(ep->entity.u.external.filenames);
+ xfree(ep->entity.u.external.sysid);
+ xfree(ep->entity.u.external.pubid);
+ sgmls_free_attributes(ep->entity.u.external.attributes);
+ free(ep->entity.u.internal.name);
+ }
+ free(ep);
+ ep = tem;
+ }
+
+ for (np = sp->notations; np;) {
+ struct notation_list *tem = np->next;
+ xfree(np->notation.sysid);
+ xfree(np->notation.pubid);
+ free(np->notation.name);
+ free(np);
+ np = tem;
+ }
+
+ xfree(sp->buf);
+ xfree(sp->pubid);
+ xfree(sp->sysid);
+ if (sp->files) {
+ int i;
+ for (i = 0; i < sp->nfiles; i++)
+ free(sp->files[i]);
+ free(sp->files);
+ }
+ free(sp);
+
+ xfree(datav);
+ datav = 0;
+ datav_size = 0;
+}
+
+sgmls_errhandler *sgmls_set_errhandler(handler)
+ sgmls_errhandler *handler;
+{
+ sgmls_errhandler *old = errhandler;
+ if (handler)
+ errhandler = handler;
+ return old;
+}
+
+int sgmls_next(sp, e)
+ struct sgmls *sp;
+ event_s *e;
+{
+ while (read_line(sp)) {
+ char *buf = sp->buf;
+
+ e->filename = sp->filename;
+ e->lineno = sp->lineno;
+
+ switch (buf[0]) {
+ case DATA_CODE:
+ e->u.data.n = parse_data(buf + 1, &sp->lineno);
+ e->u.data.v = datav;
+ e->type = SGMLS_EVENT_DATA;
+ return 1;
+ case START_CODE:
+ {
+ char *p;
+ e->u.start.attributes = sp->attributes;
+ sp->attributes = 0;
+ e->type = SGMLS_EVENT_START;
+ p = buf + 1;
+ e->u.start.gi = scan_token(&p);
+ return 1;
+ }
+ case END_CODE:
+ {
+ char *p = buf + 1;
+ e->type = SGMLS_EVENT_END;
+ e->u.end.gi = scan_token(&p);
+ return 1;
+ }
+ case START_SUBDOC_CODE:
+ case END_SUBDOC_CODE:
+ {
+ char *p = buf + 1;
+ char *name = scan_token(&p);
+ if (buf[0] == START_SUBDOC_CODE) {
+ e->u.entity = lookup_external_entity(sp, name);
+ sp->subdoc_level++;
+ e->type = SGMLS_EVENT_SUBSTART;
+ }
+ else {
+ e->type = SGMLS_EVENT_SUBEND;
+ list_finish_level((struct list **)&sp->entities, sp->subdoc_level);
+ list_finish_level((struct list **)&sp->notations, sp->subdoc_level);
+ sp->subdoc_level--;
+ e->u.entity = lookup_external_entity(sp, name);
+ }
+ return 1;
+ }
+ case ATTRIBUTE_CODE:
+ add_attribute(&sp->attributes, parse_attribute(sp, buf + 1));
+ break;
+ case DATA_ATTRIBUTE_CODE:
+ {
+ char *p = buf + 1;
+ char *name;
+ attribute_s *a;
+ external_entity_s *ext;
+
+ name = scan_token(&p);
+ a = parse_attribute(sp, p);
+ ext = lookup_external_entity(sp, name);
+ add_attribute(&ext->attributes, a);
+ }
+ break;
+ case REFERENCE_ENTITY_CODE:
+ {
+ char *p = buf + 1;
+ char *name;
+ name = scan_token(&p);
+ e->u.entity = lookup_external_entity(sp, name);
+ e->type = SGMLS_EVENT_ENTITY;
+ return 1;
+ }
+ case DEFINE_NOTATION_CODE:
+ {
+ notation_s notation;
+
+ parse_notation(buf + 1, &notation);
+ define_notation(sp, &notation);
+ }
+ break;
+ case DEFINE_EXTERNAL_ENTITY_CODE:
+ {
+ external_entity_s external;
+
+ parse_external_entity(buf + 1, sp, &external);
+ define_external_entity(sp, &external);
+ }
+ break;
+ case DEFINE_SUBDOC_ENTITY_CODE:
+ {
+ external_entity_s external;
+
+ parse_subdoc_entity(buf + 1, &external);
+ define_external_entity(sp, &external);
+ }
+ break;
+ case DEFINE_INTERNAL_ENTITY_CODE:
+ {
+ internal_entity_s internal;
+
+ parse_internal_entity(buf + 1, &internal);
+ define_internal_entity(sp, &internal);
+ }
+ break;
+ case PI_CODE:
+ e->u.pi.len = unescape1(buf + 1);
+ e->u.pi.s = buf + 1;
+ e->type = SGMLS_EVENT_PI;
+ return 1;
+ case LOCATION_CODE:
+ parse_location(buf + 1, sp);
+ break;
+ case APPINFO_CODE:
+ e->u.appinfo = unescape(buf + 1);
+ e->type = SGMLS_EVENT_APPINFO;
+ return 1;
+ case SYSID_CODE:
+ sp->sysid = strsave(unescape(buf + 1));
+ break;
+ case PUBID_CODE:
+ sp->pubid = strsave(unescape(buf + 1));
+ break;
+ case FILE_CODE:
+ sp->files = xrealloc(sp->files, (sp->nfiles + 1)*sizeof(char *));
+ sp->files[sp->nfiles] = strsave(unescape_file(buf + 1));
+ sp->nfiles += 1;
+ break;
+ case CONFORMING_CODE:
+ e->type = SGMLS_EVENT_CONFORMING;
+ return 1;
+ default:
+ error(E_COMMAND);
+ }
+ }
+
+ return 0;
+}
+
+static
+int parse_data(p, linenop)
+ char *p;
+ unsigned long *linenop;
+{
+ int n = 0;
+ char *start = p;
+ char *q;
+ int is_sdata = 0;
+
+ /* No need to copy before first escape. */
+
+ for (; *p != '\\' && *p != '\0'; p++)
+ ;
+ q = p;
+ while (*p) {
+ if (*p == '\\') {
+ switch (*++p) {
+ case '\\':
+ *q++ = *p++;
+ break;
+ case 'n':
+ *q++ = RECHAR;
+ *linenop += 1;
+ p++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int val = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ val = val*8 + (*p++ - '0');
+ if (*p >= '0' && *p <= '7')
+ val = val*8 + (*p++ - '0');
+ }
+ *q++ = (char)val;
+ }
+ break;
+ case '|':
+ if (q > start || is_sdata) {
+ if (n >= datav_size)
+ grow_datav();
+ datav[n].s = start;
+ datav[n].len = q - start;
+ datav[n].is_sdata = is_sdata;
+ n++;
+ }
+ is_sdata = !is_sdata;
+ start = q;
+ p++;
+ break;
+ default:
+ error(E_BADESCAPE);
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+
+ if (q > start || is_sdata) {
+ if (n >= datav_size)
+ grow_datav();
+ datav[n].s = start;
+ datav[n].len = q - start;
+ datav[n].is_sdata = is_sdata;
+ n++;
+ }
+ return n;
+}
+
+static
+void grow_datav()
+{
+ unsigned size = datav_size ? 2*datav_size : 2;
+ datav = (data_s *)xrealloc((UNIV)datav, size*sizeof(data_s));
+ datav_size = size;
+}
+
+static
+void parse_location(s, sp)
+ char *s;
+ struct sgmls *sp;
+{
+ unsigned size;
+
+ if (*s < '0' || *s > '9' || sscanf(s, "%lu", &sp->lineno) != 1)
+ error(E_NUMBER);
+ do {
+ ++s;
+ } while (*s >= '0' && *s <= '9');
+
+ if (*s != ' ')
+ return;
+ s++;
+ s = unescape_file(s);
+ size = strlen(s) + 1;
+ if (size <= sp->filename_size)
+ strcpy(sp->filename, s);
+ else {
+ sp->filename = xrealloc(sp->filename, size);
+ strcpy(sp->filename, s);
+ sp->filename_size = size;
+ }
+}
+
+static
+void parse_notation(s, n)
+ char *s;
+ notation_s *n;
+{
+ n->name = strsave(scan_token(&s));
+}
+
+static
+void parse_internal_entity(s, e)
+ char *s;
+ internal_entity_s *e;
+{
+ char *type;
+
+ e->name = strsave(scan_token(&s));
+ type = scan_token(&s);
+ if (strcmp(type, "CDATA") == 0)
+ e->data.is_sdata = 0;
+ else if (strcmp(type, "SDATA") == 0)
+ e->data.is_sdata = 1;
+ else
+ error(E_BADINTERNAL);
+ e->data.len = unescape1(s);
+ if (e->data.len == 0)
+ e->data.s = 0;
+ else {
+ e->data.s = xmalloc(e->data.len);
+ memcpy(e->data.s, s, e->data.len);
+ }
+}
+
+static
+void parse_external_entity(s, sp, e)
+ char *s;
+ struct sgmls *sp;
+ external_entity_s *e;
+{
+ char *type;
+ char *notation;
+
+ e->name = strsave(scan_token(&s));
+ type = scan_token(&s);
+ if (strcmp(type, "CDATA") == 0)
+ e->type = SGMLS_ENTITY_CDATA;
+ else if (strcmp(type, "SDATA") == 0)
+ e->type = SGMLS_ENTITY_SDATA;
+ else if (strcmp(type, "NDATA") == 0)
+ e->type = SGMLS_ENTITY_NDATA;
+ else
+ error(E_BADEXTERNAL);
+ notation = scan_token(&s);
+ e->notation = lookup_notation(sp, notation);
+}
+
+static
+void parse_subdoc_entity(s, e)
+ char *s;
+ external_entity_s *e;
+{
+ e->name = strsave(scan_token(&s));
+ e->type = SGMLS_ENTITY_SUBDOC;
+}
+
+static
+attribute_s *parse_attribute(sp, s)
+ struct sgmls *sp;
+ char *s;
+{
+ attribute_s *a;
+ char *type;
+
+ a = (attribute_s *)xmalloc(sizeof(*a));
+ a->name = strsave(scan_token(&s));
+ type = scan_token(&s);
+ if (strcmp(type, "CDATA") == 0) {
+ unsigned long lineno = 0;
+ a->type = SGMLS_ATTR_CDATA;
+ a->value.data.n = parse_data(s, &lineno);
+ a->value.data.v = copy_data(datav, a->value.data.n);
+ }
+ else if (strcmp(type, "IMPLIED") == 0) {
+ a->type = SGMLS_ATTR_IMPLIED;
+ }
+ else if (strcmp(type, "NOTATION") == 0) {
+ a->type = SGMLS_ATTR_NOTATION;
+ a->value.notation = lookup_notation(sp, scan_token(&s));
+ }
+ else if (strcmp(type, "ENTITY") == 0) {
+ int n, i;
+ a->type = SGMLS_ATTR_ENTITY;
+ n = count_args(s);
+ if (n == 0)
+ error(E_MISSING);
+ a->value.entity.v = (entity_s **)xmalloc(n*sizeof(entity_s *));
+ a->value.entity.n = n;
+ for (i = 0; i < n; i++)
+ a->value.entity.v[i] = lookup_entity(sp, scan_token(&s));
+ }
+ else if (strcmp(type, "TOKEN") == 0) {
+ int n, i;
+ a->type = SGMLS_ATTR_TOKEN;
+ n = count_args(s);
+ if (n == 0)
+ error(E_MISSING);
+ a->value.token.v = (char **)xmalloc(n * sizeof(char *));
+ for (i = 0; i < n; i++)
+ a->value.token.v[i] = strsave(scan_token(&s));
+ a->value.token.n = n;
+ }
+ else
+ error(E_ATTR);
+ return a;
+}
+
+void sgmls_free_attributes(p)
+ attribute_s *p;
+{
+ while (p) {
+ attribute_s *nextp = p->next;
+ switch (p->type) {
+ case SGMLS_ATTR_CDATA:
+ if (p->value.data.v) {
+ free(p->value.data.v[0].s);
+ free(p->value.data.v);
+ }
+ break;
+ case SGMLS_ATTR_TOKEN:
+ {
+ int i;
+ for (i = 0; i < p->value.token.n; i++)
+ free(p->value.token.v[i]);
+ xfree(p->value.token.v);
+ }
+ break;
+ case SGMLS_ATTR_ENTITY:
+ xfree(p->value.entity.v);
+ break;
+ case SGMLS_ATTR_IMPLIED:
+ case SGMLS_ATTR_NOTATION:
+ break;
+ }
+ free(p->name);
+ free(p);
+ p = nextp;
+ }
+}
+
+static
+data_s *copy_data(v, n)
+ data_s *v;
+ int n;
+{
+ if (n == 0)
+ return 0;
+ else {
+ int i;
+ unsigned total;
+ char *p;
+ data_s *result;
+
+ result = (data_s *)xmalloc(n*sizeof(data_s));
+ total = 0;
+ for (i = 0; i < n; i++)
+ total += v[i].len;
+ if (!total)
+ total++;
+ p = xmalloc(total);
+ for (i = 0; i < n; i++) {
+ result[i].s = p;
+ memcpy(result[i].s, v[i].s, v[i].len);
+ result[i].len = v[i].len;
+ p += v[i].len;
+ result[i].is_sdata = v[i].is_sdata;
+ }
+ return result;
+ }
+}
+
+/* Unescape s, and return nul-terminated data. Give an error
+if the data contains 0. */
+
+static
+char *unescape(s)
+ char *s;
+{
+ int len = unescape1(s);
+ if (memchr(s, '\0', len))
+ error(E_NULESCAPE);
+ s[len] = '\0';
+ return s;
+}
+
+/* Like unescape(), but REs are represented by 012 not 015. */
+
+static
+char *unescape_file(s)
+ char *s;
+{
+ char *p;
+ p = s = unescape(s);
+ while ((p = strchr(p, RECHAR)) != 0)
+ *p++ = '\n';
+ return s;
+
+}
+
+/* Unescape s, and return length of data. The data may contain 0. */
+
+static
+int unescape1(s)
+ char *s;
+{
+ const char *p;
+ char *q;
+
+ q = strchr(s, '\\');
+ if (!q)
+ return strlen(s);
+ p = q;
+ while (*p) {
+ if (*p == '\\') {
+ switch (*++p) {
+ case '\\':
+ *q++ = *p++;
+ break;
+ case 'n':
+ *q++ = RECHAR;
+ p++;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int val = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ val = val*8 + (*p++ - '0');
+ if (*p >= '0' && *p <= '7')
+ val = val*8 + (*p++ - '0');
+ }
+ *q++ = (char)val;
+ }
+ break;
+ case '|':
+ error(E_SDATA);
+ default:
+ error(E_BADESCAPE);
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+ return q - s;
+}
+
+static
+char *scan_token(pp)
+ char **pp;
+{
+ char *start = *pp;
+ while (**pp != '\0') {
+ if (**pp == ' ') {
+ **pp = '\0';
+ *pp += 1;
+ break;
+ }
+ *pp += 1;
+ }
+ if (!*start)
+ error(E_MISSING);
+ return start;
+}
+
+static
+int count_args(p)
+ char *p;
+{
+ int n = 0;
+
+ while (*p != '\0') {
+ n++;
+ do {
+ ++p;
+ if (*p == ' ') {
+ p++;
+ break;
+ }
+ } while (*p != '\0');
+ }
+ return n;
+}
+
+static
+int read_line(sp)
+ struct sgmls *sp;
+{
+ unsigned i = 0;
+ FILE *fp = sp->fp;
+ int c;
+ char *buf = sp->buf;
+ unsigned buf_size = sp->buf_size;
+
+ c = getc(fp);
+ if (c == EOF) {
+ input_lineno = sp->input_lineno;
+ if (ferror(fp))
+ error(E_SYSTEM);
+ return 0;
+ }
+
+ sp->input_lineno++;
+ input_lineno = sp->input_lineno;
+ for (;;) {
+ if (i >= buf_size) {
+ if (buf_size == 0)
+ buf_size = 24;
+ else if (buf_size > (unsigned)UINT_MAX/2) {
+ if (buf_size == (unsigned)UINT_MAX)
+ error(E_LINELENGTH);
+ buf_size = (unsigned)UINT_MAX;
+ }
+ else
+ buf_size *= 2;
+ buf = xrealloc(buf, buf_size);
+ sp->buf = buf;
+ sp->buf_size = buf_size;
+ }
+ if (c == '\0')
+ error(E_NUL);
+ if (c == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ buf[i++] = c;
+ c = getc(fp);
+ if (c == EOF) {
+ if (ferror(fp))
+ error(E_SYSTEM);
+ else
+ error(E_EOF);
+ }
+ }
+ return 1;
+}
+
+static
+notation_s *lookup_notation(sp, name)
+struct sgmls *sp;
+char *name;
+{
+ struct notation_list *p
+ = (struct notation_list *)list_find((struct list *)sp->notations, name,
+ sp->subdoc_level);
+ if (!p)
+ error(E_BADNOTATION);
+ return &p->notation;
+}
+
+static
+entity_s *lookup_entity(sp, name)
+struct sgmls *sp;
+char *name;
+{
+ struct entity_list *p
+ = (struct entity_list *)list_find((struct list *)sp->entities, name,
+ sp->subdoc_level);
+ if (!p)
+ error(E_BADENTITY);
+ return &p->entity;
+}
+
+static
+external_entity_s *lookup_external_entity(sp, name)
+struct sgmls *sp;
+char *name;
+{
+ entity_s *p = lookup_entity(sp, name);
+ if (p->is_internal)
+ error(E_INTERNALENTITY);
+ return &p->u.external;
+}
+
+static
+void define_external_entity(sp, e)
+struct sgmls *sp;
+external_entity_s *e;
+{
+ struct entity_list *p;
+ e->attributes = 0;
+ e->filenames = sp->files;
+ e->nfilenames = sp->nfiles;
+ sp->files = 0;
+ sp->nfiles = 0;
+ e->pubid = sp->pubid;
+ sp->pubid = 0;
+ e->sysid = sp->sysid;
+ sp->sysid = 0;
+ p = (struct entity_list *)xmalloc(sizeof(struct entity_list));
+ memcpy((UNIV)&p->entity.u.external, (UNIV)e, sizeof(*e));
+ p->entity.is_internal = 0;
+ p->subdoc_level = sp->subdoc_level;
+ p->next = sp->entities;
+ sp->entities = p;
+}
+
+static
+void define_internal_entity(sp, e)
+struct sgmls *sp;
+internal_entity_s *e;
+{
+ struct entity_list *p;
+ p = (struct entity_list *)xmalloc(sizeof(struct entity_list));
+ memcpy((UNIV)&p->entity.u.internal, (UNIV)e, sizeof(*e));
+ p->entity.is_internal = 1;
+ p->subdoc_level = sp->subdoc_level;
+ p->next = sp->entities;
+ sp->entities = p;
+}
+
+static
+void define_notation(sp, np)
+struct sgmls *sp;
+notation_s *np;
+{
+ struct notation_list *p;
+ np->sysid = sp->sysid;
+ sp->sysid = 0;
+ np->pubid = sp->pubid;
+ sp->pubid = 0;
+ p = (struct notation_list *)xmalloc(sizeof(struct notation_list));
+ memcpy((UNIV)&p->notation, (UNIV)np, sizeof(*np));
+ p->subdoc_level = sp->subdoc_level;
+ p->next = sp->notations;
+ sp->notations = p;
+}
+
+static
+struct list *list_find(p, name, level)
+ struct list *p;
+ char *name;
+ int level;
+{
+ for (; p && p->subdoc_level == level; p = p->next)
+ if (strcmp(p->name, name) == 0)
+ return p;
+ return 0;
+}
+
+/* Move all the items in the list whose subdoc level is level to the
+end of the list and make their subdoc_level -1. */
+
+static
+void list_finish_level(listp, level)
+ struct list **listp;
+ int level;
+{
+ struct list **pp, *next_level, *old_level;
+ for (pp = listp; *pp && (*pp)->subdoc_level == level; pp = &(*pp)->next)
+ (*pp)->subdoc_level = -1;
+ next_level = *pp;
+ *pp = 0;
+ old_level = *listp;
+ *listp = next_level;
+ for (pp = listp; *pp; pp = &(*pp)->next)
+ ;
+ *pp = old_level;
+}
+
+static
+void add_attribute(pp, a)
+ attribute_s **pp, *a;
+{
+ for (; *pp && strcmp((*pp)->name, a->name) < 0; pp = &(*pp)->next)
+ ;
+ a->next = *pp;
+ *pp = a;
+}
+
+
+static
+char *strsave(s)
+char *s;
+{
+ if (!s)
+ return s;
+ else {
+ char *p = xmalloc(strlen(s) + 1);
+ strcpy(p, s);
+ return p;
+ }
+}
+
+static
+UNIV xmalloc(n)
+ unsigned n;
+{
+ UNIV p = malloc(n);
+ if (!p)
+ error(E_NOMEM);
+ return p;
+}
+
+/* ANSI C says first argument to realloc can be NULL, but not everybody
+ appears to support this. */
+
+static
+UNIV xrealloc(p, n)
+ UNIV p;
+ unsigned n;
+{
+ p = p ? realloc(p, n) : malloc(n);
+ if (!p)
+ error(E_NOMEM);
+ return p;
+}
+
+static NO_RETURN
+void error(num)
+ enum error_code num;
+{
+ (*errhandler)((int)num, errlist[num], input_lineno);
+ abort();
+}
+
+static
+void default_errhandler(num, msg, lineno)
+ int num;
+ char *msg;
+ unsigned long lineno;
+{
+ fprintf(stderr, "Line %lu: %s\n", lineno, msg);
+ exit(1);
+}
diff --git a/usr.bin/sgmls/libsgmls/sgmls.h b/usr.bin/sgmls/libsgmls/sgmls.h
new file mode 100644
index 0000000..c327f15
--- /dev/null
+++ b/usr.bin/sgmls/libsgmls/sgmls.h
@@ -0,0 +1,127 @@
+/* sgmls.h
+ Interface to a library for reading output of sgmls. */
+
+struct sgmls_data {
+ char *s;
+ unsigned len;
+ char is_sdata;
+};
+
+struct sgmls_notation {
+ char *name;
+ char *sysid;
+ char *pubid;
+};
+
+struct sgmls_internal_entity {
+ char *name;
+ struct sgmls_data data;
+};
+
+enum sgmls_external_entity_type {
+ SGMLS_ENTITY_CDATA,
+ SGMLS_ENTITY_SDATA,
+ SGMLS_ENTITY_NDATA,
+ SGMLS_ENTITY_SUBDOC
+ };
+
+struct sgmls_external_entity {
+ char *name;
+ enum sgmls_external_entity_type type;
+ char **filenames;
+ int nfilenames;
+ char *pubid;
+ char *sysid;
+ struct sgmls_attribute *attributes;
+ struct sgmls_notation *notation;
+};
+
+struct sgmls_entity {
+ union {
+ struct sgmls_internal_entity internal;
+ struct sgmls_external_entity external;
+ } u;
+ char is_internal;
+};
+
+enum sgmls_attribute_type {
+ SGMLS_ATTR_IMPLIED,
+ SGMLS_ATTR_CDATA,
+ SGMLS_ATTR_TOKEN,
+ SGMLS_ATTR_ENTITY,
+ SGMLS_ATTR_NOTATION
+};
+
+struct sgmls_attribute {
+ struct sgmls_attribute *next;
+ char *name;
+ enum sgmls_attribute_type type;
+ union {
+ struct {
+ struct sgmls_data *v;
+ int n;
+ } data;
+ struct {
+ struct sgmls_entity **v;
+ int n;
+ } entity;
+ struct {
+ char **v;
+ int n;
+ } token;
+ struct sgmls_notation *notation;
+ } value;
+};
+
+enum sgmls_event_type {
+ SGMLS_EVENT_DATA, /* data */
+ SGMLS_EVENT_ENTITY, /* external entity reference */
+ SGMLS_EVENT_PI, /* processing instruction */
+ SGMLS_EVENT_START, /* element start */
+ SGMLS_EVENT_END, /* element end */
+ SGMLS_EVENT_SUBSTART, /* subdocument start */
+ SGMLS_EVENT_SUBEND, /* subdocument end */
+ SGMLS_EVENT_APPINFO, /* appinfo */
+ SGMLS_EVENT_CONFORMING /* the document was conforming */
+ };
+
+struct sgmls_event {
+ enum sgmls_event_type type;
+ union {
+ struct {
+ struct sgmls_data *v;
+ int n;
+ } data;
+ struct sgmls_external_entity *entity;
+ struct {
+ char *s;
+ unsigned len;
+ } pi;
+ struct {
+ char *gi;
+ struct sgmls_attribute *attributes;
+ } start;
+ struct {
+ char *gi;
+ } end;
+ char *appinfo;
+ } u;
+ char *filename; /* SGML filename */
+ unsigned long lineno; /* SGML lineno */
+};
+
+#ifdef __STDC__
+void sgmls_free_attributes(struct sgmls_attribute *);
+struct sgmls *sgmls_create(FILE *);
+int sgmls_next(struct sgmls *, struct sgmls_event *);
+void sgmls_free(struct sgmls *);
+typedef void sgmls_errhandler(int, char *, unsigned long);
+sgmls_errhandler *sgmls_set_errhandler(sgmls_errhandler *);
+#else /* not __STDC__ */
+void sgmls_free_attributes();
+struct sgmls *sgmls_create();
+int sgmls_next();
+void sgmls_free();
+typedef void sgmls_errhandler();
+sgmls_errhandler *sgmls_set_errhandler();
+#endif /* not __STDC__ */
diff --git a/usr.bin/sgmls/rast/Makefile b/usr.bin/sgmls/rast/Makefile
new file mode 100644
index 0000000..4c8a7c2
--- /dev/null
+++ b/usr.bin/sgmls/rast/Makefile
@@ -0,0 +1,18 @@
+#
+# Bmakefile for rast
+#
+# $id$
+#
+
+PROG= rast
+
+SRCS+= rast.c
+
+CFLAGS+= -I${.CURDIR}/../libsgmls -I${.CURDIR}/../sgmls
+
+LDADD= ${LIBSGMLS}
+DPADD= ${LIBSGMLS}
+
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/sgmls/rast/rast.1 b/usr.bin/sgmls/rast/rast.1
new file mode 100644
index 0000000..2d167fc
--- /dev/null
+++ b/usr.bin/sgmls/rast/rast.1
@@ -0,0 +1,75 @@
+.\" -*- nroff -*-
+.tr \(ts"
+.TH RAST 1
+.SH NAME
+rast \- translate output of sgmls to RAST format
+.SH SYNOPSIS
+.B rast
+[
+.BI \-o output_file
+]
+[
+.I input_file
+]
+.SH DESCRIPTION
+.I Rast
+translates the output of sgmls to the format of a RAST result.
+RAST is the Reference Application for SGML Testing defined in the Proposed
+American National Standard on Conformance Testing for Standard Generalized
+Markup Language (SGML) Systems (X3.190-199X).
+.I Rast
+reads from
+.I input_file
+or from standard input if
+.I input_file
+is not specified.
+It writes to
+.I output_file
+or to standard output if
+.I output_file
+is not specified;
+use of the
+.B \-o
+option avoids the need for
+.I rast
+to use a temporary file.
+.LP
+Note that the
+.B -c
+option of
+.I sgmls
+can generate a capacity report in RACT format.
+.SH BUGS
+Production [9] in clause 14.5.5 of the draft standard is clearly wrong;
+.I rast
+corrects it by appending
+.RI `,\ LE '.
+An alternative way to correct it would be to delete the
+.RB `, \(tsEND-ENTITY\(ts '.
+.LP
+In production [18] in clause 14.5.9,
+.RI ` markup\ data +'
+should be
+.RI ` markup\ data *'
+since internal sdata entities need not contain any characters (14.5.11),
+and
+.I markup\ data
+cannot be empty (14.5.9, 14.5.12).
+.LP
+The RAST result for the example in Annex B.4 is incorrect.
+The line
+.B G03-A1=
+should be immediately followed by a line
+.BR !g03-e1! .
+(The problem with production [9] also applies to this example.)
+.LP
+.I Rast
+outputs a newline after
+.B #ERROR
+in order to avoid producing files with partial lines.
+.SH "SEE ALSO"
+.IR sgmls (1)
+.br
+.I
+Conformance Testing for Standard Generalized Markup Language (SGML) Systems,
+(X3.190-199X), Draft July 1991
diff --git a/usr.bin/sgmls/rast/rast.c b/usr.bin/sgmls/rast/rast.c
new file mode 100644
index 0000000..2634679
--- /dev/null
+++ b/usr.bin/sgmls/rast/rast.c
@@ -0,0 +1,534 @@
+/* rast.c
+ Translate sgmls output to RAST result format.
+
+ Written by James Clark (jjc@jclark.com). */
+
+#include "config.h"
+#include "std.h"
+#include "sgmls.h"
+#include "getopt.h"
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+#ifdef __GNUC__
+#define NO_RETURN volatile
+#else
+#define NO_RETURN /* as nothing */
+#endif
+
+#ifdef VARARGS
+#define VP(parms) ()
+#else
+#define VP(parms) P(parms)
+#endif
+
+#ifdef USE_ISASCII
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+NO_RETURN void error VP((char *,...));
+
+static void input_error P((int, char *, unsigned long));
+static int do_file P((FILE *));
+static void usage P((void));
+
+static void output_processing_instruction P((char *, unsigned));
+static void output_data P((struct sgmls_data *, int));
+static void output_data_lines P((char *, unsigned));
+static void output_internal_sdata P((char *, unsigned));
+static void output_external_entity P((struct sgmls_external_entity *));
+static void output_external_entity_info P((struct sgmls_external_entity *));
+static void output_element_start P((char *, struct sgmls_attribute *));
+static void output_element_end P((char *));
+static void output_attribute P((struct sgmls_attribute *));
+static void output_tokens P((char **, int));
+static void output_markup_chars P((char *, unsigned));
+static void output_markup_string P((char *));
+static void output_char P((int, int));
+static void output_flush P((int));
+static void output_external_id P((char *, char *));
+static void output_entity P((struct sgmls_entity *));
+static void output_external_entity_info P((struct sgmls_external_entity *));
+static void output_internal_entity P((struct sgmls_internal_entity *));
+
+#define output_flush_markup() output_flush('!')
+#define output_flush_data() output_flush('|')
+
+static FILE *outfp;
+static int char_count = 0;
+static char *program_name;
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int opt;
+ char *output_file = 0;
+
+ program_name = argv[0];
+
+ while ((opt = getopt(argc, argv, "o:")) != EOF)
+ switch (opt) {
+ case 'o':
+ output_file = optarg;
+ break;
+ case '?':
+ usage();
+ default:
+ abort();
+ }
+
+ if (output_file) {
+ errno = 0;
+ outfp = fopen(output_file, "w");
+ if (!outfp)
+ error("couldn't open `%s' for output: %s", strerror(errno));
+ }
+ else {
+ outfp = tmpfile();
+ if (!outfp)
+ error("couldn't create temporary file: %s", strerror(errno));
+ }
+
+ if (argc - optind > 1)
+ usage();
+
+ if (argc - optind == 1) {
+ if (!freopen(argv[optind], "r", stdin))
+ error("couldn't open `%s' for input: %s", argv[optind], strerror(errno));
+ }
+
+ (void)sgmls_set_errhandler(input_error);
+
+ if (!do_file(stdin)) {
+ fclose(outfp);
+ if (output_file) {
+ if (!freopen(output_file, "w", stdout))
+ error("couldn't reopen `%s' for output: %s", strerror(errno));
+ }
+ fputs("#ERROR\n", stdout);
+ exit(EXIT_FAILURE);
+ }
+
+ if (output_file) {
+ errno = 0;
+ if (fclose(outfp) == EOF)
+ error("error closing `%s': %s", output_file, strerror(errno));
+ }
+ else {
+ errno = 0;
+ if (fseek(outfp, 0L, SEEK_SET))
+ error("couldn't rewind temporary file: %s", strerror(errno));
+ while ((c = getc(outfp)) != EOF)
+ if (putchar(c) == EOF)
+ error("error writing standard output: %s", strerror(errno));
+ }
+ exit(EXIT_SUCCESS);
+}
+
+static
+void usage()
+{
+ fprintf(stderr, "usage: %s [-o output_file] [input_file]\n", program_name);
+ exit(EXIT_FAILURE);
+}
+
+static
+int do_file(fp)
+ FILE *fp;
+{
+ struct sgmls *sp;
+ struct sgmls_event e;
+ int conforming = 0;
+
+ sp = sgmls_create(fp);
+ while (sgmls_next(sp, &e))
+ switch (e.type) {
+ case SGMLS_EVENT_DATA:
+ output_data(e.u.data.v, e.u.data.n);
+ break;
+ case SGMLS_EVENT_ENTITY:
+ output_external_entity(e.u.entity);
+ break;
+ case SGMLS_EVENT_PI:
+ output_processing_instruction(e.u.pi.s, e.u.pi.len);
+ break;
+ case SGMLS_EVENT_START:
+ output_element_start(e.u.start.gi, e.u.start.attributes);
+ sgmls_free_attributes(e.u.start.attributes);
+ break;
+ case SGMLS_EVENT_END:
+ output_element_end(e.u.end.gi);
+ break;
+ case SGMLS_EVENT_SUBSTART:
+ {
+ int level = 1;
+ output_external_entity(e.u.entity);
+ while (level > 0) {
+ if (!sgmls_next(sp, &e))
+ return 0;
+ switch (e.type) {
+ case SGMLS_EVENT_SUBSTART:
+ level++;
+ break;
+ case SGMLS_EVENT_SUBEND:
+ level--;
+ break;
+ case SGMLS_EVENT_START:
+ sgmls_free_attributes(e.u.start.attributes);
+ break;
+ default:
+ /* prevent compiler warnings */
+ break;
+ }
+ }
+ }
+ break;
+ case SGMLS_EVENT_APPINFO:
+ break;
+ case SGMLS_EVENT_CONFORMING:
+ conforming = 1;
+ break;
+ default:
+ abort();
+ }
+ sgmls_free(sp);
+ return conforming;
+}
+
+static
+void output_processing_instruction(s, len)
+ char *s;
+ unsigned len;
+{
+ fputs("[?", outfp);
+ if (len > 0) {
+ putc('\n', outfp);
+ output_data_lines(s, len);
+ output_flush_data();
+ }
+ fputs("]\n", outfp);
+}
+
+static
+void output_data(v, n)
+ struct sgmls_data *v;
+ int n;
+{
+ int i;
+ for (i = 0; i < n; i++) {
+ if (v[i].is_sdata)
+ output_internal_sdata(v[i].s, v[i].len);
+ else if (v[i].len > 0)
+ output_data_lines(v[i].s, v[i].len);
+ }
+}
+
+static
+void output_data_lines(s, n)
+ char *s;
+ unsigned n;
+{
+ assert(n > 0);
+ for (; n > 0; --n)
+ output_char((unsigned char)*s++, '|');
+ output_flush_data();
+}
+
+static
+void output_internal_sdata(s, n)
+ char *s;
+ unsigned n;
+{
+ fputs("#SDATA-TEXT\n", outfp);
+ output_markup_chars(s, n);
+ output_flush_markup();
+ fputs("#END-SDATA\n", outfp);
+}
+
+static
+void output_external_entity(e)
+ struct sgmls_external_entity *e;
+{
+ fprintf(outfp, "[&%s\n", e->name);
+ output_external_entity_info(e);
+ fputs("]\n", outfp);
+}
+
+static
+void output_element_start(gi, att)
+ char *gi;
+ struct sgmls_attribute *att;
+{
+ fprintf(outfp, "[%s", gi);
+ if (att) {
+ struct sgmls_attribute *p;
+ putc('\n', outfp);
+ for (p = att; p; p = p->next)
+ output_attribute(p);
+ }
+ fputs("]\n", outfp);
+}
+
+static
+void output_element_end(gi)
+ char *gi;
+{
+ fprintf(outfp, "[/%s]\n", gi);
+}
+
+static
+void output_attribute(p)
+ struct sgmls_attribute *p;
+{
+ fprintf(outfp, "%s=\n", p->name);
+ switch (p->type) {
+ case SGMLS_ATTR_IMPLIED:
+ fputs("#IMPLIED\n", outfp);
+ break;
+ case SGMLS_ATTR_CDATA:
+ {
+ struct sgmls_data *v = p->value.data.v;
+ int n = p->value.data.n;
+ int i;
+ for (i = 0; i < n; i++)
+ if (v[i].is_sdata)
+ output_internal_sdata(v[i].s, v[i].len);
+ else {
+ output_markup_chars(v[i].s, v[i].len);
+ output_flush_markup();
+ }
+ }
+ break;
+ case SGMLS_ATTR_TOKEN:
+ output_tokens(p->value.token.v, p->value.token.n);
+ break;
+ case SGMLS_ATTR_ENTITY:
+ {
+ int i;
+ for (i = 0; i < p->value.entity.n; i++) {
+ struct sgmls_entity *e = p->value.entity.v[i];
+ char *name;
+
+ if (e->is_internal)
+ name = e->u.internal.name;
+ else
+ name = e->u.external.name;
+ if (i > 0)
+ output_markup_string(" ");
+ output_markup_string(name);
+ }
+ output_flush_markup();
+ for (i = 0; i < p->value.entity.n; i++)
+ output_entity(p->value.entity.v[i]);
+ }
+ break;
+ case SGMLS_ATTR_NOTATION:
+ output_tokens(&p->value.notation->name, 1);
+ output_external_id(p->value.notation->pubid, p->value.notation->sysid);
+ break;
+ }
+}
+
+static void output_tokens(v, n)
+ char **v;
+ int n;
+{
+ int i;
+ assert(n > 0);
+ output_markup_string(v[0]);
+ for (i = 1; i < n; i++) {
+ output_markup_string(" ");
+ output_markup_string(v[i]);
+ }
+ output_flush_markup();
+}
+
+static
+void output_markup_chars(s, n)
+ char *s;
+ unsigned n;
+{
+ for (; n > 0; --n)
+ output_char((unsigned char)*s++, '!');
+}
+
+static
+void output_markup_string(s)
+ char *s;
+{
+ while (*s)
+ output_char((unsigned char)*s++, '!');
+}
+
+static
+void output_char(c, delim)
+ int c;
+ int delim;
+{
+ if (ISASCII(c) && isprint(c)) {
+ if (char_count == 0)
+ putc(delim, outfp);
+ putc(c, outfp);
+ char_count++;
+ if (char_count == 60) {
+ putc(delim, outfp);
+ putc('\n', outfp);
+ char_count = 0;
+ }
+ }
+ else {
+ output_flush(delim);
+ switch (c) {
+ case RECHAR:
+ fputs("#RE\n", outfp);
+ break;
+ case RSCHAR:
+ fputs("#RS\n", outfp);
+ break;
+ case TABCHAR:
+ fputs("#TAB\n", outfp);
+ break;
+ default:
+ fprintf(outfp, "#%d\n", c);
+ }
+ }
+}
+
+static
+void output_flush(delim)
+ int delim;
+{
+ if (char_count > 0) {
+ putc(delim, outfp);
+ putc('\n', outfp);
+ char_count = 0;
+ }
+}
+
+static
+void output_external_id(pubid, sysid)
+ char *pubid;
+ char *sysid;
+{
+ if (!pubid && !sysid)
+ fputs("#SYSTEM\n#NONE\n", outfp);
+ else {
+ if (pubid) {
+ fputs("#PUBLIC\n", outfp);
+ if (*pubid) {
+ output_markup_string(pubid);
+ output_flush_markup();
+ }
+ else
+ fputs("#EMPTY\n", outfp);
+ }
+ if (sysid) {
+ fputs("#SYSTEM\n", outfp);
+ if (*sysid) {
+ output_markup_string(sysid);
+ output_flush_markup();
+ }
+ else
+ fputs("#EMPTY\n", outfp);
+ }
+ }
+}
+
+static
+void output_entity(e)
+ struct sgmls_entity *e;
+{
+ if (e->is_internal)
+ output_internal_entity(&e->u.internal);
+ else
+ output_external_entity_info(&e->u.external);
+ fputs("#END-ENTITY", outfp);
+#ifndef ASIS
+ putc('\n', outfp);
+#endif
+}
+
+static
+void output_external_entity_info(e)
+ struct sgmls_external_entity *e;
+{
+ switch (e->type) {
+ case SGMLS_ENTITY_CDATA:
+ fputs("#CDATA-EXTERNAL", outfp);
+ break;
+ case SGMLS_ENTITY_SDATA:
+ fputs("#SDATA-EXTERNAL", outfp);
+ break;
+ case SGMLS_ENTITY_NDATA:
+ fputs("#NDATA-EXTERNAL", outfp);
+ break;
+ case SGMLS_ENTITY_SUBDOC:
+ fputs("#SUBDOC", outfp);
+ break;
+ }
+ putc('\n', outfp);
+ output_external_id(e->pubid, e->sysid);
+ if (e->type != SGMLS_ENTITY_SUBDOC) {
+ struct sgmls_attribute *p;
+ fprintf(outfp, "#NOTATION=%s\n", e->notation->name);
+ output_external_id(e->notation->pubid, e->notation->sysid);
+ for (p = e->attributes; p; p = p->next)
+ output_attribute(p);
+ }
+}
+
+static
+void output_internal_entity(e)
+ struct sgmls_internal_entity *e;
+{
+ if (e->data.is_sdata)
+ fputs("#SDATA-INTERNAL", outfp);
+ else
+ fputs("#CDATA-INTERNAL", outfp);
+ putc('\n', outfp);
+ output_markup_chars(e->data.s, e->data.len);
+ output_flush_markup();
+}
+
+static
+void input_error(num, str, lineno)
+ int num;
+ char *str;
+ unsigned long lineno;
+{
+ error("Error at input line %lu: %s", lineno, str);
+}
+
+NO_RETURN
+#ifdef VARARGS
+void error(va_alist) va_dcl
+#else
+void error(char *message,...)
+#endif
+{
+#ifdef VARARGS
+ char *message;
+#endif
+ va_list ap;
+
+ fprintf(stderr, "%s: ", program_name);
+#ifdef VARARGS
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+ vfprintf(stderr, message, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+}
diff --git a/usr.bin/sgmls/sgmls.pl b/usr.bin/sgmls/sgmls.pl
new file mode 100755
index 0000000..edb9eb6
--- /dev/null
+++ b/usr.bin/sgmls/sgmls.pl
@@ -0,0 +1,247 @@
+#! /usr/bin/perl
+
+# This is a skeleton of a perl script for processing the output of
+# sgmls. You must change the parts marked with "XXX".
+
+# XXX This is for troff: in data, turn \ into \e (which prints as \).
+# Backslashes in SDATA entities are left as backslashes.
+
+$backslash_in_data = "\\e";
+
+$prog = $0;
+
+$prog =~ s|.*/||;
+
+$level = 0;
+
+while (<STDIN>) {
+ chop;
+ $command = substr($_, 0, 1);
+ substr($_, 0, 1) = "";
+ if ($command eq '(') {
+ &start_element($_);
+ $level++;
+ }
+ elsif ($command eq ')') {
+ $level--;
+ &end_element($_);
+ foreach $key (keys %attribute_value) {
+ @splitkey = split($;, $key);
+ if ($splitkey[0] == $level) {
+ delete $attribute_value{$key};
+ delete $attribute_type{$key};
+ }
+ }
+ }
+ elsif ($command eq '-') {
+ &unescape_data($_);
+ &data($_);
+ }
+ elsif ($command eq 'A') {
+ @field = split(/ /, $_, 3);
+ $attribute_type{$level,$field[0]} = $field[1];
+ &unescape_data($field[2]);
+ $attribute_value{$level,$field[0]} = $field[2];
+ }
+ elsif ($command eq '&') {
+ &entity($_);
+ }
+ elsif ($command eq 'D') {
+ @field = split(/ /, $_, 4);
+ $data_attribute_type{$field[0], $field[1]} = $field[2];
+ &unescape_data($field[3]);
+ $data_attribute_value{$field[0], $field[1]} = $field[3];
+ }
+ elsif ($command eq 'N') {
+ $notation{$_} = 1;
+ if (defined($sysid)) {
+ $notation_sysid{$_} = $sysid;
+ undef($sysid);
+ }
+ if (defined($pubid)) {
+ $notation_pubid{$_} = $pubid;
+ undef($pubid);
+ }
+ }
+ elsif ($command eq 'I') {
+ @field = split(/ /, $_, 3);
+ $entity_type{$field[0]} = $field[1];
+ &unescape($field[2]);
+ # You may want to substitute \e for \ if the type is CDATA.
+ $entity_text{$field[0]} = $field[2];
+ $entity_code{$field[0]} = 'I';
+ }
+ elsif ($command eq 'E') {
+ @field = split(/ /, $_);
+ $entity_code{$field[0]} = 'E';
+ $entity_type{$field[0]} = $field[1];
+ $entity_notation{$field[0]} = $field[2];
+ if (defined(@files)) {
+ foreach $i (0..$#files) {
+ $entity_filename{$field[0], $i} = $files[i];
+ }
+ undef(@files);
+ }
+ if (defined($sysid)) {
+ $entity_sysid{$field[0]} = $sysid;
+ undef($sysid);
+ }
+ if (defined($pubid)) {
+ $entity_pubid{$field[0]} = $pubid;
+ undef($pubid);
+ }
+ }
+ elsif ($command eq 'S') {
+ $entity_code{$_} = 'S';
+ if (defined(@files)) {
+ foreach $i (0..$#files) {
+ $entity_filename{$_, $i} = $files[i];
+ }
+ undef(@files);
+ }
+ if (defined($sysid)) {
+ $entity_sysid{$_} = $sysid;
+ undef($sysid);
+ }
+ if (defined($pubid)) {
+ $entity_pubid{$_} = $pubid;
+ undef($pubid);
+ }
+ }
+ elsif ($command eq '?') {
+ &unescape($_);
+ &pi($_);
+ }
+ elsif ($command eq 'L') {
+ @field = split(/ /, $_);
+ $lineno = $field[0];
+ if ($#field >= 1) {
+ &unescape($field[1]);
+ $filename = $field[1];
+ }
+ }
+ elsif ($command eq 'V') {
+ @field = split(/ /, $_, 2);
+ &unescape($field[1]);
+ $environment{$field[0]} = $field[1];
+ }
+ elsif ($command eq '{') {
+ &start_subdoc($_);
+ }
+ elsif ($command eq '}') {
+ &end_subdoc($_);
+ }
+ elsif ($command eq 'f') {
+ &unescape($_);
+ push(@files, $_);
+ }
+ elsif ($command eq 'p') {
+ &unescape($_);
+ $pubid = $_;
+ }
+ elsif ($command eq 's') {
+ &unescape($_);
+ $sysid = $_;
+ }
+ elsif ($command eq 'C') {
+ $conforming = 1;
+ }
+ else {
+ warn "$prog:$ARGV:$.: unrecognized command \`$command'\n";
+ }
+}
+
+sub unescape {
+ $_[0] =~ s/\\([0-7][0-7]?[0-7]?|.)/&esc($1)/eg;
+}
+
+sub esc {
+ local($_) = $_[0];
+ if ($_ eq '012' || $_ eq '12') {
+ ""; # ignore RS
+ }
+ elsif (/^[0-7]/) {
+ sprintf("%c", oct);
+ }
+ elsif ($_ eq 'n') {
+ "\n";
+ }
+ elsif ($_ eq '|') {
+ "";
+ }
+ elsif ($_ eq "\\") {
+ "\\";
+ }
+ else {
+ $_;
+ }
+}
+
+sub unescape_data {
+ local($sdata) = 0;
+ $_[0] =~ s/\\([0-7][0-7]?[0-7]?|.)/&esc_data($1)/eg;
+}
+
+sub esc_data {
+ local($_) = $_[0];
+ if ($_ eq '012' || $_ eq '12') {
+ ""; # ignore RS
+ }
+ elsif (/^[0-7]/) {
+ sprintf("%c", oct);
+ }
+ elsif ($_ eq 'n') {
+ "\n";
+ }
+ elsif ($_ eq '|') {
+ $sdata = !$sdata;
+ "";
+ }
+ elsif ($_ eq "\\") {
+ $sdata ? "\\" : $backslash_in_data;
+ }
+ else {
+ $_;
+ }
+}
+
+
+sub start_element {
+ local($gi) = $_[0];
+ # XXX
+}
+
+sub end_element {
+ local($gi) = $_[0];
+ # XXX
+}
+
+sub data {
+ local($data) = $_[0];
+ # XXX
+}
+
+# A processing instruction.
+
+sub pi {
+ local($data) = $_[0];
+ # XXX
+}
+
+# A reference to an external entity.
+
+sub entity {
+ local($name) = $_[0];
+ # XXX
+}
+
+sub start_subdoc {
+ local($name) = $_[0];
+ # XXX
+}
+
+sub end_subdoc {
+ local($name) = $_[0];
+ # XXX
+}
+
diff --git a/usr.bin/sgmls/sgmls/Makefile b/usr.bin/sgmls/sgmls/Makefile
new file mode 100644
index 0000000..3a0a0cf
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/Makefile
@@ -0,0 +1,18 @@
+#
+# Bmakefile for sgmls
+#
+# $id$
+#
+
+PROG= sgmls
+
+SRCS+= lexrf.c pcbrf.c synrf.c context.c md1.c md2.c pars1.c pars2.c serv.c
+SRCS+= sgml1.c sgml2.c sgmlmsg.c sgmlxtrn.c traceset.c entgen.c sgmlio.c
+SRCS+= xfprintf.c main.c unixproc.c sgmldecl.c version.c strerror.c getopt.c
+SRCS+= msgcat.c lineout.c ambig.c exclude.c lextaba.c
+
+CFLAGS+= -I${.CURDIR}/../libsgmls
+
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/sgmls/sgmls/action.h b/usr.bin/sgmls/sgmls/action.h
new file mode 100644
index 0000000..08475bf
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/action.h
@@ -0,0 +1,179 @@
+/* ACTION.H: Symbols for all PCB action codes. */
+/* CONACT.H: Symbols for content parse action names (end with '_').
+ There must be no conflict with PARSEACT.H, which
+ uses 0 through 19, or SGMLACT.H, which uses 20 through 32
+ (except that 31 - 32 can be defined here because they are
+ used only by PARSEPRO and do not conflict with SGML.C).
+*/
+#define CIR_ 31 /* Invalid character(s) ignored in MDS; restarting parse. */
+#define DTD_ 32 /* Process DOCTYPE declaration. */
+#define DTE_ 33 /* End of DOCTYPE declaration. */
+#define PEP_ 34 /* TEMP: Previous character ended prolog. */
+#define DAS_ 35 /* Current character begins data. */
+#define FCE_ 36 /* Process free character (SR12-18, 21-30). */
+#define DCE_ 37 /* Data character in element text; change PCB. */
+#define LAS_ 38 /* Start lookahead buffer with current character. */
+#define LAM_ 39 /* Move character to lookahead buffer. */
+#define LAF_ 40 /* Flush the lookahead buffer; REPEATCC. */
+#define NED_ 41 /* Process null end-tag delimiter. */
+#define NET_ 42 /* Process null end-tag. */
+#define NST_ 43 /* Process null start-tag. */
+#define NLF_ 44 /* Flush lookahead buffer except for trailing NET or SR. */
+#define ETC_ 45 /* End-tag in CDATA or RCDATA; treat as data if invalid. */
+#define SRMIN 46 /* Dummy for SHORT REFERENCES: srn = SRn - SRMIN. */
+#define SR1_ 47 /* TAB */
+#define SR2_ 48 /* RE */
+#define SR3_ 49 /* RS */
+#define SR4_ 50 /* Leading blanks */
+#define SR5_ 51 /* Null record */
+#define DAR_ 52 /* Flush data buffer after repeating current character. */
+#define SR7_ 53 /* Trailing blanks */
+#define SR8_ 54 /* Space */
+#define SR9_ 55 /* Two or more blanks */
+#define SR10 56 /* Quotation mark (first data character) */
+#define SR11 57 /* Number sign */
+#define SR12 58 /* FCE CHARACTERS start here */
+/* _ 59 */
+#define BSQ_ 60 /* Blank sequence begun; find its end. */
+/* 61 In use by PARSEACT.H */
+/* 62 In use by PARSEACT.H */
+/* 63 In use by PARSEACT.H */
+/* 64 In use by PARSEACT.H */
+#define SR19 65 /* Hyphen */
+#define SR20 66 /* Two hyphens */
+#define SR25 71 /* Left bracket */
+#define SR26 72 /* Right bracket */
+#define RBR_ 73 /* Two right brackets. */
+#define GTR_ 74 /* EOB with pending data character */
+#define MSP_ 75 /* Marked section start in prolog outside DTD */
+#define APP_ 76 /* APPINFO (other than NONE) */
+#define STE_ 77 /* Start tag ended prolog */
+
+/* GRPACT.H: Symbols for group tokenization action names (all alpha).
+ There must be no conflict with PARSEACT.H, which
+ uses 0 - 19.
+*/
+#define AND 20 /* AND connector found. */
+#define DTAG 21 /* Data tag token group occurred (treat as #CHARS). */
+#define GRPE 22 /* Group ended. */
+#define GRP_ 23 /* Group started. */
+#define NAS_ 24 /* Name started in content model or name group. */
+#define NMT_ 25 /* Name or name token started in name token group. */
+#define OPT 26 /* OPT occurrence indicator for previous token. */
+#define OR 27 /* OR connector found. */
+#define OREP 28 /* OREP occurrence indicator for previous token. */
+#define REP 29 /* REP occurrence indicator for previous token. */
+#define RNS_ 30 /* Reserved name started (#PCDATA). */
+#define SEQ 31 /* SEQ connector found. */
+/* LITACT.H: Symbols for content parse action names (end with '_').
+ There must be no conflict with PARSEACT.H, which
+ uses 0 through 19.
+*/
+#define MLA_ 20 /* Move character to look-aside data buffer. */
+#define LPR_ 21 /* Move previous character to data buffer. */
+#define RSM_ 22 /* Process record start and move it to data buffer. */
+#define FUN_ 23 /* Replace function character with a space. */
+#define LP2_ 24 /* Move previous two characters to data buffer. */
+#define MLE_ 25 /* Minimum literal error: invalid character ignored. */
+#define RPR_ 26 /* Remove previous character from data buffer; terminate. */
+#define TER_ 27 /* Terminate the parse. */
+/* MDACT.H: Symbols for markup declaration parse action names (all alpha).
+ There must be no conflict with PARSEACT.H, which
+ uses 0 - 19.
+*/
+#define CDR 20 /* CD[1] (MINUS) occurred previously. */
+#define EMD 21 /* End of markup declaration. */
+#define GRPS 22 /* Group started. */
+#define LIT 23 /* Literal started: character data. */
+#define LITE 24 /* Literal started: character data; LITA is delimiter. */
+#define MGRP 25 /* Minus exception group (MINUS,GRPO). */
+#define NAS 26 /* Name started. */
+#define NMT 27 /* Name token started. */
+#define NUM 28 /* Number or number token started. */
+#define PEN 29 /* Parameter entity name being defined (PERO found). */
+#define PGRP 30 /* Plus exception group (PLUS,GRPO). */
+#define RNS 31 /* Reserved name started. */
+#define MDS 32 /* Markup declaration subset start. */
+#define PENR 33 /* REPEATCC; PERO found. */
+/* PARSEACT.H: Symbols for common parse action names (end with '_').
+ There must be no conflict with other action name
+ files, which use numbers greater than 19.
+*/
+#define CRA_ 1 /* Character reference: alphabetic. */
+#define CRN_ 2 /* Character reference: numeric; non-char refs o.k.. */
+#define NON_ 3 /* Single byte of non-character data found. */
+#define EOF_ 4 /* Error: illegal entity end; resume old input; return. */
+#define ER_ 5 /* Entity reference; start new input source; continue. */
+#define GET_ 6 /* EOB, EOS, or EE: resume old input source; continue. */
+#define INV_ 7 /* Error: invalid char terminated markup; repeat char. */
+#define LEN_ 8 /* Error: length limit exceeded; end markup; repeat char. */
+#define NOP_ 9 /* No action necessary. */
+#define PCI_ 10 /* Previous character was invalid. */
+#define PER_ 11 /* Parameter reference; start new input source; continue. */
+#define RC2_ 12 /* Back up two characters. */
+#define RCC_ 13 /* Repeat current character. */
+#define RCR_ 14 /* Repeat current character and return to caller. */
+#define EE_ 15 /* EOS or EE: resume old input source; return to caller. */
+#define RS_ 16 /* Record start: ccnt=0; ++rcnt. */
+#define ERX_ 17 /* Entity reference; start new input source; return. */
+#define SYS_ 18 /* Error allowed: SYSCHAR in input stream; replace it. */
+#define EOD_ 19 /* End of document. */
+/* Number way out of order to avoid recompilation. */
+#define NSC_ 58 /* Handle DELNONCH/DELXNONCH when NON_ is allowed */
+#define PEX_ 61 /* Parameter entity ref; start new input source; return. */
+#define DEF_ 62 /* Data entity found. */
+#define PIE_ 63 /* PI entity found (needed in markup). */
+#define LNR_ 64 /* LEN_ error with extra REPEATCC. */
+/* SGMLACT.H: Symbols for content parse action names (end with '_')
+ that are returned to SGML.C for processing.
+ There must be no conflict with PARSEACT.H, which
+ uses 0 through 19, or CONACT.H, which uses 34 and above.
+ (Note: 31 is also used in CONACT.H, but no conflict
+ is created because they are tested only in PARSEPRO.C, which
+ completes before SGML.C starts to examine those codes.
+ Also, when EOD_ is returned from PARSECON, it is changed
+ to LOP_.)
+*/
+#define CON_ 20 /* Normal content action (one of the following). */
+#define DAF_ 21 /* Data found. */
+#define ETG_ 22 /* Process end-tag. */
+#define MD_ 23 /* Process markup declaration (NAMESTRT found). */
+#define MDC_ 24 /* Process markup declaration comment (CD found). */
+#define MSS_ 25 /* Process marked section start. */
+#define MSE_ 26 /* Process marked section end. */
+#define PIS_ 27 /* Processing instruction (string). */
+#define REF_ 28 /* Record end found. */
+#define STG_ 29 /* Process start-tag. */
+#define RSR_ 30 /* Return RS to effect SGML state transition. */
+#define LOP_ 31 /* Loop for new content without returning anything. */
+/* TAGACT.H: Symbols for tag parse action names (all alpha).
+ There must be no conflict with PARSEACT.H, which
+ uses 0 - 19.
+*/
+#define AVD 20 /* Delimited attribute value started: normal delimiter. */
+#define AVU 21 /* Undelimited value started. */
+#define ETIC 22 /* Tag closed with ETI. */
+#define NVS 23 /* Name of attribute or value started. */
+#define NASV 24 /* Saved NAS was actually an NTV. */
+#define NTV 25 /* Name token value started; get name and full value. */
+#define TAGC 26 /* Tag closed normally. */
+#define TAGO 27 /* Tag closed implicitly by TAGO character. */
+#define AVDA 28 /* Delimited attribute value started: alternative delim. */
+#define DSC 29 /* Closed by DSC character. */
+/* VALACT.H: Symbols for attribute value tokenization action names (all alpha).
+*/
+#define NOPA 0 /* No action necessary. */
+#define INVA 1 /* Invalid character; terminate parse. */
+#define LENA 2 /* Length limit of token exceeded; terminate parse. */
+#define NASA 3 /* Name started. */
+#define NMTA 4 /* Name token started. */
+#define NUMA 5 /* Number or number token started. */
+
+/* SGML declaration parsing actions. */
+
+#define ESGD 20 /* End of SGML declaration. */
+#define LIT1 21 /* Literal started. */
+#define LIT2 22 /* Literal started with LITA delimiter. */
+#define NUM1 23 /* Number started. */
+#define NAS1 24 /* Name started. */
+#define ISIG 25 /* Insignificant character occurred. */
diff --git a/usr.bin/sgmls/sgmls/adl.h b/usr.bin/sgmls/sgmls/adl.h
new file mode 100644
index 0000000..930e1e8
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/adl.h
@@ -0,0 +1,118 @@
+/* ADL.H: Definitions for attribute descriptor list processing.
+*/
+/* N/C/SDATA external entity types for nxetype member of ne structure. */
+#define ESNCDATA 1 /* External character data entity. */
+#define ESNNDATA 2 /* Non-SGML data entity. */
+#define ESNSDATA 3 /* External specific character data entity. */
+#define ESNSUB 4 /* SGML subdocument entity. */
+
+/* N/C/SDATA control block for AENTITY attributes and NDATA returns.*/
+struct ne { /* N/C/SDATA entity control block. */
+ UNIV neid; /* Files for NDATA entity. */
+ UNCH *nepubid; /* Public identifier if specified. */
+ UNCH *nesysid; /* System identifier if specified. */
+ PDCB nedcn; /* Data content notation control block. */
+ struct ad *neal; /* Data attribute list (NULL if none). */
+ UNCH *neename; /* Ptr to entity name (length and EOS). */
+ UNCH nextype; /* Entity type: NDATA SDATA CDATA SUBDOC. */
+};
+#define NESZ (sizeof(struct ne))
+typedef struct ne *PNE;
+/* NDATA entity control block fields. */
+#define NEID(p) (((PNE)p)->neid) /* File ID of NDATA entity. */
+#define NESYSID(p) (((PNE)p)->nesysid) /* System ID of NDATA entity. */
+#define NEPUBID(p) (((PNE)p)->nepubid) /* Public ID of NDATA entity. */
+#define NEDCN(p) (((PNE)p)->nedcn->ename) /* Data content notation name. */
+#define NEDCNSYSID(p) (((PNE)p)->nedcn->sysid) /* Notation system ID.*/
+#define NEDCNPUBID(p) (((PNE)p)->nedcn->pubid) /* Notation public ID.*/
+#define NEDCNDEFINED(p) (((PNE)p)->nedcn->defined) /* Notation defined? */
+#define NEDCNADL(p) (((PNE)p)->nedcn->adl) /* Data content notation attlist.*/
+#define NEENAME(p) (((PNE)p)->neename) /* Entity name pointer. */
+#define NEXTYPE(p) (((PNE)p)->nextype) /* External entity type. */
+#define NEAL(p) (((PNE)p)->neal) /* Data attributes (if any). */
+#define NEDCNMARK(p) DCNMARK(((PNE)p)->nedcn)
+
+/* Attribute descriptor list entry. */
+struct ad {
+ UNCH *adname; /* Attribute name with length and EOS. */
+ UNCH adflags; /* Attribute flags. */
+ UNCH adtype; /* Value type. */
+ UNS adnum; /* Group size or member pos in grp. */
+ UNS adlen; /* Length of default or value (for capacity). */
+ UNCH *addef; /* Default value (NULL if REQUIRED or IMPLIED). */
+ union {
+ PNE n; /* AENTITY: NDATA control block. */
+ PDCB x; /* ANOTEGRP: DCN control block. */
+ } addata; /* Special data associated with some attributes.*/
+};
+#define ADSZ (sizeof(struct ad)) /* Size of an ad structure. */
+
+/* Attribute flags for entire list adflags: ADLF. */
+#define ADLREQ 0x80 /* Attribute list: 1=REQUIRED att defined. */
+#define ADLNOTE 0x40 /* Attribute list: 1=NOTATION att defined. */
+#define ADLCONR 0x20 /* Attribute list: 1=CONREF att defined. */
+
+/* Attribute flags for list member adflags: ADFLAGS(n). */
+#define AREQ 0x80 /* Attribute: 0=null; 1=required. */
+#define ACURRENT 0x40 /* Attribute: 0=normal; 1=current. */
+#define AFIXED 0x20 /* Attribute: 0=normal; 1=must equal default. */
+#define AGROUP 0x10 /* Attribute: 0=single; 1=group of ad's. */
+#define ACONREF 0x08 /* Attribute: 0=normal; 1=att is CONREF. */
+#define AINVALID 0x04 /* Attribute: 1=value is invalid; 0=o.k. */
+#define AERROR 0x02 /* Attribute: 1=error was specified; 0=o.k. */
+#define ASPEC 0x01 /* Attribute: 1=value was specified; 0=default. */
+
+/* Attribute types for adtype. */
+#define ANMTGRP 0x00 /* Attribute: Name token group or member. */
+#define ANOTEGRP 0x01 /* Attribute: Notation (name group). */
+#define ACHARS 0x02 /* Attribute: Character string. */
+#define AENTITY 0x03 /* Attribute: Data entity (name). */
+#define AID 0x04 /* Attribute: ID value (name). */
+#define AIDREF 0x05 /* Attribute: ID reference value (name). */
+#define ANAME 0x06 /* Attribute: Name. */
+#define ANMTOKE 0x07 /* Attribute: Name token. */
+#define ANUMBER 0x08 /* Attribute: Number. */
+#define ANUTOKE 0x09 /* Attribute: Number token. */
+#define ATKNLIST 0x0A /* Attribute: >= means value is a token list. */
+#define AENTITYS 0x0A /* Attribute: Data entities (name list). */
+#define AIDREFS 0x0B /* Attribute: ID reference value (name list). */
+#define ANAMES 0x0C /* Attribute: Name list. */
+#define ANMTOKES 0x0D /* Attribute: Name token list. */
+#define ANUMBERS 0x0E /* Attribute: Number list. */
+#define ANUTOKES 0x0F /* Attribute: Number token list. */
+
+/* Field definitions for entries in an attribute list.
+ The first argument to all of these is the list address.
+*/
+/* Attribute list: flags. */
+#define ADLF(a) ((a)[0].adflags)
+/* Attribute list: number of list members. */
+#define ADN(a) ((a)[0].adtype)
+/* Attribute list: number of attributes. */
+#define AN(a) ((a)[0].adnum)
+/* Nth attribute in list: name. */
+#define ADNAME(a, n) (((a)[n].adname+1))
+/* Nth att in list: number of val)ues. */
+#define ADNUM(a, n) ((a)[n].adnum)
+/* Nth attribute in list: flags. */
+#define ADFLAGS(a, n) ((a)[n].adflags)
+/* Nth attribute in list: type. */
+#define ADTYPE(a, n) ((a)[n].adtype)
+/* Nth attribute in list: len of def or val.*/
+#define ADLEN(a, n) ((a)[n].adlen)
+/* Nth attribute in list: def or value. */
+#define ADVAL(a, n) ((a)[n].addef)
+/* Nth attribute in list: special data. */
+#define ADDATA(a, n) ((a)[n].addata)
+/* Nth att: token at Pth pos in value. */
+#define ADTOKEN(a, n, p)(((a)[n].addef+(p)))
+
+#define IDHASH 101 /* Size of ID hash table. Must be prime. */
+struct id { /* ID attribute control block. */
+ struct id *idnext; /* Next ID in chain. */
+ UNCH *idname; /* ID name with length prefix and EOS. */
+ UNCH iddefed; /* Non-zero if it has been defined. */
+ struct fwdref *idrl; /* Chain of forward references to this ID. */
+};
+#define IDSZ sizeof(struct id)
+typedef struct id *PID; /* Ptr to ID attribute control block. */
diff --git a/usr.bin/sgmls/sgmls/ambig.c b/usr.bin/sgmls/sgmls/ambig.c
new file mode 100644
index 0000000..942aa5d
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/ambig.c
@@ -0,0 +1,438 @@
+/* ambig.c -
+ Content model ambiguity checking.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+/*
+This uses the construction in pp8-9 of [1], extended to deal with AND
+groups.
+
+Note that it is not correct for the purposes of ambiguity analysis to
+handle AND groups by turning them into an OR group of SEQ groups
+(consider (a&b?)).
+
+We build an automaton for the entire content model by adding the
+following case for AND:
+
+nullable(v) := nullable(left child) and nullable(right child)
+if nullable(right child) then
+ for each x in last(left child) do
+ follow(v,x) = follow(left child,x) U first(right child);
+if nullable(left child) then
+ for each x in last(right child) do
+ follow(v,x) = follow(right child,x) U first(left child);
+first(v) := first(left child) U first(right child);
+last(v) := first(left child) U first(right child);
+
+We also build an automaton for each AND group by building automata for
+each of the members of the AND group using the above procedure and
+then combine the members using:
+
+for each x in last(left child) do
+ follow(v,x) = follow(left child,x) U first(right child);
+for each x in last(right child) do
+ follow(v,x) = follow(right child,x) U first(left child);
+first(v) := first(left child) U first(right child);
+
+The content model is ambiguous just in case one of these automata is
+non-deterministic. (Note that when checking determinism we need to
+check the `first' set as well as all the `follow' sets.)
+
+Why is this correct? Consider a primitive token in a member of an AND
+group. There are two worst cases for ambiguity: firstly, when none of
+the other members of AND group have been matched; secondly, when just
+the nullable members remain to be matched. The first case is not
+affected by context of the AND group (unless the first case is
+identical to the second case.)
+
+Note that inclusions are not relevant for the purposes of determining
+the ambiguity of content models. Otherwise the case in clause
+11.2.5.1:
+
+ An element that can satisfy an element in the content model is
+ considered to do so, even if the element is also an inclusion.
+
+could never arise.
+
+[1] Anne Brueggemann-Klein, Regular Expressions into Finite Automata,
+Universitaet Freiburg, Institut fur Informatik, 33 July 1991.
+*/
+
+#include "sgmlincl.h"
+
+/* Sets of states are represented by 0-terminated, ordered lists of
+indexes in gbuf. */
+
+#define MAXSTATES (GRPGTCNT+2)
+#define listcat(x, y) strcat((char *)(x), (char *)(y))
+#define listcpy(x, y) strcpy((char *)(x), (char *)(y))
+
+/* Information about a content token. */
+
+struct contoken {
+ UNCH size;
+ UNCH nullable;
+ UNCH *first;
+ UNCH *last;
+};
+
+static VOID contoken P((int, int, struct contoken *));
+static VOID andgroup P((int, int, struct contoken *));
+static VOID orgroup P((int, int, struct contoken *));
+static VOID seqgroup P((int, int, struct contoken *));
+static VOID andambig P((int));
+static int listambig P((UNCH *));
+static VOID listmerge P((UNCH *, UNCH *));
+static struct contoken *newcontoken P((void));
+static VOID freecontoken P((struct contoken *));
+
+
+/* Dynamically allocated vector of follow sets. */
+
+static UNCH **follow;
+static UNCH *mergebuf; /* for use by listmerge */
+
+/* Set to non-zero if the content model is ambiguous. */
+
+static int ambigsw;
+
+/* Check the current content model (in gbuf) for ambiguity. */
+
+VOID ambig()
+{
+ struct contoken *s;
+ int i;
+
+ if (!follow) {
+ /* We can't allocate everything in one chunk, because that would
+ overflow a 16-bit unsigned if GRPGTCNT was 253. */
+ UNCH *ptr;
+ follow = (UNCH **)rmalloc(MAXSTATES*sizeof(UNCH *));
+ follow[0] = 0;
+ ptr = (UNCH *)rmalloc((MAXSTATES - 1)*MAXSTATES);
+ for (i = 1; i < MAXSTATES; i++) {
+ follow[i] = ptr;
+ ptr += MAXSTATES;
+ }
+ mergebuf = (UNCH *)rmalloc(MAXSTATES);
+ }
+
+ for (i = 1; i < MAXSTATES; i++)
+ follow[i][0] = 0;
+
+ ambigsw = 0;
+
+ s = newcontoken();
+ contoken(1, 1, s);
+
+ ambigsw = ambigsw || listambig(s->first);
+
+ freecontoken(s);
+
+ for (i = 1; !ambigsw && i < MAXSTATES; i++)
+ if (listambig(follow[i]))
+ ambigsw = 1;
+
+ if (ambigsw)
+ mderr(137, (UNCH *)0, (UNCH *)0);
+}
+
+/* Free memory used for ambiguity checking. */
+
+VOID ambigfree()
+{
+ if (follow) {
+ frem((UNIV)follow[1]);
+ frem((UNIV)follow);
+ frem((UNIV)mergebuf);
+ follow = 0;
+ }
+}
+
+/* Determine whether a list of primitive content tokens (each
+represented by its index in gbuf) is ambiguous. */
+
+static
+int listambig(list)
+UNCH *list;
+{
+ UNCH *p;
+ int chars = 0;
+ int rc = 0;
+
+ for (p = list; *p; p++) {
+ if ((gbuf[*p].ttype & TTMASK) == TTETD) {
+ struct etd *e = gbuf[*p].tu.thetd;
+ if (e->mark) {
+ rc = 1;
+ break;
+ }
+ e->mark = 1;
+ }
+ else {
+ assert((gbuf[*p].ttype & TTMASK) == TTCHARS);
+ if (chars) {
+ rc = 1;
+ break;
+ }
+ chars = 1;
+ }
+ }
+
+ for (p = list; *p; p++)
+ if ((gbuf[*p].ttype & TTMASK) == TTETD)
+ gbuf[*p].tu.thetd->mark = 0;
+
+ return rc;
+}
+
+
+/* Analyze a content token. The `checkand' argument is needed to ensure
+that the algorithm is not exponential in the AND-group nesting depth.
+*/
+
+static
+VOID contoken(m, checkand, res)
+int m; /* Index of content token in gbuf */
+int checkand; /* Non-zero if AND groups should be checked */
+struct contoken *res; /* Result */
+{
+ UNCH flags = gbuf[m].ttype;
+ switch (flags & TTMASK) {
+ case TTCHARS:
+ case TTETD:
+ res->first[0] = m;
+ res->first[1] = 0;
+ res->last[0] = m;
+ res->last[1] = 0;
+ res->size = 1;
+ res->nullable = 0;
+ break;
+ case TTAND:
+ if (checkand)
+ andambig(m);
+ andgroup(m, checkand, res);
+ break;
+ case TTOR:
+ orgroup(m, checkand, res);
+ break;
+ case TTSEQ:
+ seqgroup(m, checkand, res);
+ break;
+ default:
+ abort();
+ }
+ if (flags & TREP) {
+ UNCH *p;
+ for (p = res->last; *p; p++)
+ listmerge(follow[*p], res->first);
+ }
+ if (flags & TOPT)
+ res->nullable = 1;
+}
+
+/* Check an AND group for ambiguity. */
+
+static
+VOID andambig(m)
+int m;
+{
+ int i, tnum;
+ int lim;
+ struct contoken *curr;
+ struct contoken *next;
+
+ tnum = gbuf[m].tu.tnum;
+ assert(tnum > 0);
+ curr = newcontoken();
+ next = newcontoken();
+ contoken(m + 1, 0, curr);
+ i = m + 1 + curr->size;
+ curr->size += 1;
+ for (--tnum; tnum > 0; --tnum) {
+ UNCH *p;
+ contoken(i, 0, next);
+ curr->size += next->size;
+ i += next->size;
+ for (p = curr->last; *p; p++)
+ listcat(follow[*p], next->first);
+ for (p = next->last; *p; p++)
+ listmerge(follow[*p], curr->first);
+ listcat(curr->first, next->first);
+ listcat(curr->last, next->last);
+ }
+ lim = m + curr->size;
+ for (i = m + 1; i < lim; i++) {
+ if (listambig(follow[i]))
+ ambigsw = 1;
+ follow[i][0] = 0;
+ }
+ freecontoken(curr);
+ freecontoken(next);
+}
+
+/* Handle an AND group. */
+
+static
+VOID andgroup(m, checkand, res)
+int m;
+int checkand;
+struct contoken *res;
+{
+ int i, tnum;
+ /* union of the first sets of nullable members of the group */
+ UNCH *nullablefirst;
+ struct contoken *next;
+
+ tnum = gbuf[m].tu.tnum;
+ assert(tnum > 0);
+ contoken(m + 1, checkand, res);
+ nullablefirst = (UNCH *)rmalloc(MAXSTATES);
+ if (res->nullable)
+ listcpy(nullablefirst, res->first);
+ else
+ nullablefirst[0] = 0;
+ i = m + 1 + res->size;
+ res->size += 1;
+ next = newcontoken();
+ for (--tnum; tnum > 0; --tnum) {
+ UNCH *p;
+ contoken(i, checkand, next);
+ res->size += next->size;
+ i += next->size;
+ if (next->nullable)
+ for (p = res->last; *p; p++)
+ listcat(follow[*p], next->first);
+ for (p = next->last; *p; p++)
+ listmerge(follow[*p], nullablefirst);
+ listcat(res->first, next->first);
+ if (next->nullable)
+ listcat(nullablefirst, next->first);
+ listcat(res->last, next->last);
+ res->nullable &= next->nullable;
+ }
+ frem((UNIV)nullablefirst);
+ freecontoken(next);
+}
+
+/* Handle a SEQ group. */
+
+static
+VOID seqgroup(m, checkand, res)
+int m;
+int checkand;
+struct contoken *res;
+{
+ int i, tnum;
+ struct contoken *next;
+
+ tnum = gbuf[m].tu.tnum;
+ assert(tnum > 0);
+ contoken(m + 1, checkand, res);
+ i = m + 1 + res->size;
+ res->size += 1;
+ next = newcontoken();
+ for (--tnum; tnum > 0; --tnum) {
+ UNCH *p;
+ contoken(i, checkand, next);
+ res->size += next->size;
+ i += next->size;
+ for (p = res->last; *p; p++)
+ listcat(follow[*p], next->first);
+ if (res->nullable)
+ listcat(res->first, next->first);
+ if (next->nullable)
+ listcat(res->last, next->last);
+ else
+ listcpy(res->last, next->last);
+ res->nullable &= next->nullable;
+ }
+ freecontoken(next);
+}
+
+/* Handle an OR group. */
+
+static
+VOID orgroup(m, checkand, res)
+int m;
+int checkand;
+struct contoken *res;
+{
+ int i, tnum;
+ struct contoken *next;
+
+ tnum = gbuf[m].tu.tnum;
+ assert(tnum > 0);
+ contoken(m + 1, checkand, res);
+ i = m + 1 + res->size;
+ res->size += 1;
+ next = newcontoken();
+ for (--tnum; tnum > 0; --tnum) {
+ contoken(i, checkand, next);
+ res->size += next->size;
+ i += next->size;
+ listcat(res->first, next->first);
+ listcat(res->last, next->last);
+ res->nullable |= next->nullable;
+ }
+ freecontoken(next);
+}
+
+
+/* Merge the second ordered list into the first. */
+
+static
+VOID listmerge(p, b)
+UNCH *p, *b;
+{
+ UNCH *a = mergebuf;
+
+ strcpy((char *)a, (char *)p);
+
+ for (;;) {
+ if (*a) {
+ if (*b) {
+ if (*a < *b)
+ *p++ = *a++;
+ else if (*a > *b)
+ *p++ = *b++;
+ else
+ a++;
+ }
+ else
+ *p++ = *a++;
+ }
+ else if (*b)
+ *p++ = *b++;
+ else
+ break;
+ }
+ *p = '\0';
+}
+
+static
+struct contoken *newcontoken()
+{
+ struct contoken *p = (struct contoken *)rmalloc(sizeof(struct contoken)
+ + MAXSTATES*2);
+ p->first = (UNCH *)(p + 1);
+ p->last = p->first + MAXSTATES;
+ return p;
+}
+
+static
+VOID freecontoken(p)
+struct contoken *p;
+{
+ frem((UNIV)p);
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/appl.h b/usr.bin/sgmls/sgmls/appl.h
new file mode 100644
index 0000000..404d749
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/appl.h
@@ -0,0 +1,33 @@
+/* appl.h */
+
+enum {
+ E_NOMEM = 1,
+ E_DOC,
+ E_EXEC,
+ E_FORK,
+ E_WAIT,
+ E_SIGNAL,
+ E_OPEN,
+ E_CAPBOTCH,
+ E_SUBDOC
+};
+
+VOID process_document P((int));
+VOID output_conforming P((void));
+
+UNIV xmalloc P((UNS));
+UNIV xrealloc P((UNIV, UNS));
+VOID appl_error VP((int, ...));
+
+#ifdef SUPPORT_SUBDOC
+int run_process P((char **));
+char **make_argv P((UNIV));
+VOID get_subcaps P((void));
+#endif
+
+#ifdef SUPPORT_SUBDOC
+extern int suberr;
+#endif
+
+extern int suppsw;
+extern int locsw;
diff --git a/usr.bin/sgmls/sgmls/config.h b/usr.bin/sgmls/sgmls/config.h
new file mode 100644
index 0000000..562cdcf
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/config.h
@@ -0,0 +1,147 @@
+/* unix.cfg: Configuration file for sgmls on Unix. */
+
+/* A list of filename templates to use for searching for external entities.
+The filenames are separated by the character specified in PATH_FILE_SEP.
+See sgmls.man for details. */
+#define DEFAULT_PATH "/usr/share/sgml/%O/%C/%T:%N.%X:%N.%D"
+/* The character that separates the filenames templates. */
+#define PATH_FILE_SEP ':'
+/* The character that separates filenames in a system identifier.
+Usually the same as PATH_FILE_SEP. */
+#define SYSID_FILE_SEP ':'
+/* The environment variable that contains the list of filename templates. */
+#define PATH_ENV_VAR "SGML_PATH"
+
+/* MIN_DAT_SUBS_FROM and MIN_DATS_SUBS_TO tell sgmls how to transform a name
+or system identifier into a legal filename. A character in
+MIN_DAT_SUBS_FROM will be transformed into the character in the
+corresponding position in MIN_DAT_SUBS_TO. If there is no such
+position, then the character is removed. */
+/* This says that spaces should be transformed to underscores, and
+slashes to percents. */
+#define MIN_DAT_SUBS_FROM " /"
+#define MIN_DAT_SUBS_TO "_%"
+
+/* Define this to allow tracing. */
+/* #define TRACE 1 */
+
+/* Define this you want support for subdocuments. This is implemented
+using features that are not part of Standard C, so you might not want
+to define it if you are porting to a new system. Otherwise I suggest
+you leave it defined. */
+#define SUPPORT_SUBDOC 1
+
+/* Define HAVE_EXTENDED_PRINTF if your *printf functions supports
+X/Open extensions; if they do, then, for example,
+
+ printf("%2$s%1$s", "bar", "foo")
+
+should print `foobar'. */
+
+/* #define HAVE_EXTENDED_PRINTF 1 */
+
+/* Define HAVE_CAT if your system provides the X/Open message
+catalogue functions catopen() and catgets(), and you want to use them.
+An implementations of these functions is included and will be used if
+you don't define this. On SunOS 4.1.1, if you do define this you
+should set CC=/usr/xpg2bin/cc in the makefile. */
+
+/* #define HAVE_CAT 1 */
+
+#ifdef __STDC__
+/* Define this if your compiler supports prototypes. */
+#define USE_PROTOTYPES 1
+#endif
+
+/* Can't use <stdarg.h> without prototypes. */
+#ifndef USE_PROTOTYPES
+#define VARARGS 1
+#endif
+
+/* If your compiler defines __STDC__ but doesn't provide <stdarg.h>,
+you must define VARARGS yourself here. */
+/* #define VARARGS 1 */
+
+/* Define this if you do not have strerror(). */
+/* #define STRERROR_MISSING 1 */
+
+/* Define this unless the character testing functions in ctype.h
+are defined for all values representable as an unsigned char. You do
+not need to define this if your system is ANSI C conformant. You
+should define for old Unix systems. */
+/* #define USE_ISASCII 1 */
+
+/* Define this if your system provides the BSD style string operations
+rather than ANSI C ones (eg bcopy() rather than memcpy(), and index()
+rather than strchr()). */
+/* #define BSD_STRINGS 1 */
+
+/* Define this if you have getopt(). */
+#define HAVE_GETOPT 1
+
+/* Define this if you have access(). */
+#define HAVE_ACCESS 1
+
+/* Define this if you have <unistd.h>. */
+#define HAVE_UNISTD_H 1
+
+/* Define this if you have <sys/stat.h>. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define this if you have waitpid(). */
+#define HAVE_WAITPID 1
+
+/* Define this if your system is POSIX.1 (ISO 9945-1:1990) compliant. */
+#define POSIX 1
+
+/* Define this if you have the vfork() system call. */
+#define HAVE_VFORK 1
+
+/* Define this if you have <vfork.h>. */
+/* #define HAVE_VFORK_H 1 */
+
+/* Define this if you don't have <stdlib.h> */
+/* #define STDLIB_H_MISSING 1 */
+
+/* Define this if you don't have <stddef.h> */
+/* #define STDDEF_H_MISSING 1 */
+
+/* Define this if you don't have <limits.h> */
+/* #define LIMITS_H_MISSING 1 */
+
+/* Define this if you don't have remove(); unlink() will be used instead. */
+/* #define REMOVE_MISSING 1 */
+
+/* Define this if you don't have raise(); kill() will be used instead. */
+/* #define RAISE_MISSING 1 */
+
+/* Define this if you don't have fsetpos() and fgetpos(). */
+/* #define FPOS_MISSING 1 */
+
+/* Universal pointer type. */
+/* If your compiler doesn't fully support void *, change `void' to `char'. */
+typedef void *UNIV;
+
+/* If your compiler doesn't support void as a function return type,
+change `void' to `int'. */
+typedef void VOID;
+
+/* If you don't have an ANSI C conformant <limits.h>, define
+CHAR_SIGNED as 1 or 0 according to whether the `char' type is signed.
+The <limits.h> on some versions of System Release V 3.2 is not ANSI C
+conformant: the value of CHAR_MIN is 0 even though the `char' type is
+signed. */
+
+/* #define CHAR_SIGNED 1 */
+/* #define CHAR_SIGNED 0 */
+#ifndef CHAR_SIGNED
+#include <limits.h>
+#if CHAR_MIN < 0
+#define CHAR_SIGNED 1
+#else
+#define CHAR_SIGNED 0
+#endif
+#endif /* not CHAR_SIGNED */
+
+/* Assume the system character set is ISO Latin-1. */
+#include "latin1.h"
diff --git a/usr.bin/sgmls/sgmls/context.c b/usr.bin/sgmls/sgmls/context.c
new file mode 100644
index 0000000..1eb5a5c
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/context.c
@@ -0,0 +1,444 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+#include "context.h"
+
+#define GI (tags[ts].tetd->etdgi+1) /* GI of current element. */
+#define NEWGI (newetd->etdgi+1) /* GI of new tag. */
+#define STATUS (*statuspt) /* Token status: RCHIT RCMISS RCEND RCREQ RCNREQ*/
+#define PEX (-1) /* GI is a plus exception and not a minus. */
+
+#define ANYHIT(h) (grplongs == 1 ? ((h)[0] != 0) : anyhit(h))
+#define HITSET(h, n) (h[(unsigned)(n-1)>>LONGPOW] \
+ |= (1L<<((n-1)&(LONGBITS-1))))
+#define HITON(h, n) (h[(unsigned)(n-1)>>LONGPOW] & (1L<<((n-1)&(LONGBITS-1))))
+
+#define HITOFF(h, n) (!(HITON(h, n)))
+
+#define TOKENHIT HITON(H,T)
+
+static
+VOID copypos(to, from)
+struct mpos *to, *from;
+{
+ int i;
+ for (i = 0; i <= (int)from[0].t; i++) {
+ to[i].g = from[i].g;
+ to[i].t = from[i].t;
+ memcpy(to[i].h, from[i].h, grplongs*sizeof(unsigned long));
+ }
+}
+
+/* CONTEXT: Determine whether a GI is valid in the present structural context.
+ Returns RCHIT if valid, RCEND if element has ended, RCREQ if a
+ different element is required, and RCMISS if it is totally invalid.
+ On entry, pos points to the model token to be tested against the GI.
+ TO DO: Save allowed GIs for an error message on an RCMISS.
+ Support a "query" mode (what is allowed now?) by working
+ with a copy of pos.
+*/
+int context(gi, mod, pos, statuspt, mexts)
+struct etd *gi; /* ETD of new GI. */
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+UNCH *statuspt; /* Token status: RCHIT RCMISS RCEND RCREQ RCNREQ*/
+int mexts; /* >0=stack level of minus grp; -1=plus; 0=none.*/
+{
+ UNCH toccsv, gtypesv; /* Save token's TOCC and GTYPE in case grp ends.*/
+
+ if (mexts == -1) {
+ if (STATUS == RCEND)
+ return RCPEX;
+ copypos(savedpos, pos);
+ }
+ Tstart = T; /* Save starting token for AND group testing. */
+ while (STATUS!=RCMISS && STATUS!=RCEND) {
+ TRACEGI("CONTEXT", gi, mod, pos, Tstart);
+ while (TTYPE==TTOR || TTYPE==TTSEQ || TTYPE==TTAND) {
+ pos[P+1].g = M++; pos[++P].t = 1; HITCLEAR(H);
+ Tstart = T; /* Save starting token for AND group testing. */
+ TRACEGI("OPENGRP", gi, mod, pos, Tstart);
+ }
+ STATUS = (UNCH)tokenreq(gi, mod, pos);
+ TRACEGI("STATUS", gi, mod, pos, Tstart);
+ if (gi==TOKEN.tu.thetd) { /* Hit in model. */
+ STATUS = (UNCH)RCHIT;
+ gtypesv = GTYPE; toccsv = TOCC;
+ newtoken(mod, pos, statuspt);
+ return(mexts<=0 ? RCHIT : (gtypesv==TTOR || BITON(toccsv, TOPT))
+ ? RCMEX : RCHITMEX);
+ }
+ if (STATUS==RCREQ) {
+ if (mexts == -1)
+ break;
+ STATUS = RCHIT;
+ nextetd = TOKEN.tu.thetd;
+ newtoken(mod, pos, statuspt);
+ return(RCREQ);
+ }
+ /* else if (STATUS==RCNREQ) */
+ if (mexts>0) return(RCMEX);
+ newtoken(mod, pos, statuspt);
+ }
+ if (mexts == -1) {
+ copypos(pos, savedpos);
+ return STATUS = RCPEX;
+ }
+ return((int)STATUS);
+}
+/* ECONTEXT: Determine whether the current element can be ended, or whether
+ non-optional tokens remain at the current level or higher.
+ Returns 1 if element can be ended, or 0 if tokens remain.
+ On entry, STATUS==RCEND if there are no tokens left; if not,
+ pos points to the next model token to be tested.
+ TO DO: Support a "query" mode (what is required now?) by working
+ with a copy of pos.
+*/
+int econtext(mod, pos, statuspt)
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+UNCH *statuspt; /* Token status: RCHIT RCMISS RCEND RCREQ RCNREQ*/
+{
+ unsigned next; /* Position in AND group of next testable token.*/
+
+ Tstart = T;
+ TRACEEND("ECONT", mod, pos, 0, 0, Tstart);
+ if (P<=1) {nextetd = 0; return(TOKENHIT || BITON(TOCC, TOPT));}
+ nextetd = TTYPE == TTETD ? TOKEN.tu.thetd : 0;
+ while (STATUS!=RCMISS && STATUS!=RCEND) {
+ STATUS = (UNCH)testend(mod, pos, 0, 0);
+ TRACEEND("ECONTEND", mod, pos, 0, 0, Tstart);
+ nextetd = P<=1 || TTYPE != TTETD ? 0 : TOKEN.tu.thetd;
+ if (STATUS==RCEND) return(1);
+ if (P<=1) return(TOKENHIT || BITON(TOCC, TOPT));
+ if (STATUS==RCMISS) {
+ if (BITON(TOCC, TOPT)) nextetd = 0;
+ return(0);
+ }
+ if (!tokenopt(mod, pos)) return(0);
+
+ STATUS = RCNREQ;
+ if (GTYPE!=TTAND) ++T; /* T!=GNUM or group would have ended. */
+ else T = (UNCH)(((next = (UNS)offbit(H, (int)T, GNUM))!=0) ?
+ next : offbit(H, 0, GNUM));
+
+ M = G + grpsz(&GHDR, (int)T-1) + 1;
+ TRACEEND("ECONTNEW", mod, pos, 0, 0, Tstart);
+ }
+ if (STATUS==RCMISS) {
+ if (BITON(TOCC, TOPT)) nextetd = 0;
+ return(0);
+ }
+ return(1); /* STATUS==RCEND */
+}
+/* NEWTOKEN: Find the next token to test. Set STATUS to indicate results:
+ RCEND if element has ended (no more tokens to test);
+ RCREQ if required new token was found;
+ RCNREQ if non-required new token was found;
+ RCHIT if a hit token was repeated (now non-required);
+ and RCMISS if a new token can't be found because current token
+ (which was not hit) was neither unconditionally required nor
+ optional.
+*/
+VOID newtoken(mod, pos, statuspt)
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+UNCH *statuspt; /* Token status: RCHIT RCMISS RCEND RCREQ RCNREQ*/
+{
+ unsigned nextand = 0; /* Position in AND group of next testable token.*/
+ int currhit = (STATUS==RCHIT); /* 1=current GI hit; 0=not. */
+
+ /* If the GI was a hit, turn on the hit bit and set the status to
+ assume that the token to be tested against the next GI will
+ be non-required. If the current token is repeatable, exit so
+ it will stand as the next token to test.
+ */
+ if (STATUS==RCHIT) {
+ HITSET(H, T);
+ STATUS = RCNREQ;
+ if (BITON(TOCC, TREP)) return;
+ }
+ /* At this point, we must determine the next token to test:
+ either against the next GI, if this one was a hit, or
+ against the same GI if conditions permit a retry.
+ To find the next token, we must first end the current group,
+ if possible, and any we can that contain it.
+ If the outermost group was a hit and is repeatable, or
+ if the element has ended, we exit now.
+ If it hasn't ended, or was optional and ended with a miss,
+ we can retry the GI against the next token.
+ */
+ if ((STATUS = (UNCH)testend(mod, pos, 1, 1))!=RCNREQ) return;
+
+ /* At this point, the "current token" is either the original one,
+ or the token for the highest level unhit group that it ended.
+ We will retry a missed GI, by testing it against the next
+ token, if the current token:
+ 1. Is optional;
+ 2. Was hit (i.e., because it is repeatable and was hit by a
+ previous GI or because it is a hit group that just ended);
+ 3. Is in an AND or OR group and is not the last testable token.
+
+ It will be the next sequential one (unhit one, in an AND group);
+ if there are none left, use the first unhit token in the group.
+ In either case, set M to correspond to the new T.
+ */
+ retest:
+ TRACEEND("RETEST", mod, pos, (int)nextand, 1, Tstart);
+ if (GTYPE==TTAND) {
+ nextand = offbit(H, (int)T, GNUM);
+ if (!nextand)
+ nextand = offbit(H, 0, GNUM);
+ }
+ if ( BITON(TOCC, TOPT)
+ || TOKENHIT
+ || GTYPE==TTOR /* T!=GNUM or group would have ended. */
+ || nextand ) {
+ if (GTYPE!=TTAND) ++T; /* T!=GNUM or group would have ended. */
+ else T = nextand;
+ M = G + grpsz(&GHDR, (int)T-1) + 1;
+ if (GTYPE==TTAND) {
+ /* If AND group wrapped, it can end if all non-optionals were
+ hit. */
+ if (T==Tstart && !currhit) {
+ UNCH Psave = P;
+ int rc = testend(mod, pos, 0, 1);
+ if (Psave!=P) {if ((STATUS = (UNCH)rc)==RCNREQ) goto retest;}
+ else STATUS = RCMISS;
+ }
+
+ /* We only test unhit tokens, so we must use an unhit token
+ as Tstart (which is used to detect when the AND group has
+ wrapped). */
+ else if (HITON(H,Tstart)) Tstart = T;
+ }
+ }
+ else STATUS = RCMISS;
+ TRACEEND("NEWTOKEN", mod, pos, (int)nextand, 1, Tstart);
+}
+/* TESTEND: End the current group, if possible, and any that it is nested in.
+ The current token will either be a group header, or some token
+ that could not end its group. Return 1 if the (possibly new)
+ current token is repeatable; 0 if it is not.
+*/
+int testend(mod, pos, andoptsw, newtknsw)
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+int andoptsw; /* 1=test optional AND members; 0=ignore. */
+int newtknsw; /* 1=new token test; 0=end element test. */
+{
+ int rc = 0; /* Return code: RCNREQ RCHIT RCMISS RCEND */
+
+ while (!rc) {
+ TRACEEND("TRACEEND", mod, pos, rc, andoptsw, Tstart);
+ /* TESTMISS:
+ If we've hit no tokens yet in the current group, and
+ the current token is the last unhit one in the group we can test,
+ we will end the group (it may never really have started!)
+ because we might be able to try the token that follows it.
+ In any group, a token is the last testable unhit token if it
+ is the last sequential one, as the GI was already tested against
+ the preceding unhit tokens. In addition,
+ in a SEQ group, it is the last testable unhit token if it isn't
+ optional, because we can't skip past it to the following ones.
+ If we end the group, before popping the level, set M to G, as this
+ level`s group header will be the next level's current token.
+ */
+ if (!ANYHIT(H) && (T==GNUM
+ || (GTYPE==TTSEQ && BITOFF(TOCC, TOPT)))) {
+ M = G; --P; Tstart = T;
+ if (P<=1) {
+ if (BITON(TOCC, TOPT) || TOKENHIT) rc = RCEND;
+ else rc = RCMISS;
+ }
+ continue;
+ }
+ /* TESTHIT:
+ See if we've hit all the non-optional tokens in the group.
+ If so, pop to the previous level and set the group's hit bit.
+ If we were called from NEWTOKEN we are trying to find the token
+ to test against the next start-tag, so if the group is repeatable,
+ process it again. (If not, we were called from ECONTEXT and
+ are testing whether the element can be ended.)
+ Otherwise, if we are at the first level, the element is over.
+ */
+ if ((GTYPE==TTOR && TOKENHIT)
+ || (GTYPE==TTSEQ && T==(UNCH)GNUM
+ && (TOKENHIT || BITON(TOCC, TOPT)))
+ || (GTYPE==TTAND && allhit(&GHDR, H, 0, andoptsw))) {
+ M = G;
+ --P;
+ HITSET(H, T);
+ Tstart = T;
+ if (newtknsw && BITON(TOCC, TREP)) rc = RCHIT;
+ else if (P<=1) rc = RCEND;
+ /* If we are looking for a new token to test against the next
+ start-tag, then we need to consider optional and members
+ in this group, even if we didn't need to consider them
+ in the group that we just ended because that group had
+ wrapped. */
+ else if (newtknsw) andoptsw = 1;
+ /* Else loop to test new outer group. */
+ }
+ else rc = RCNREQ; /* No group ended this time, so return. */
+ }
+ TRACEEND("ENDFOUND", mod, pos, rc, andoptsw, Tstart);
+ return(rc);
+}
+/* TOKENOPT: Return 1 if current token is contextually optional;
+ otherwise, return 0.
+*/
+int tokenopt(mod, pos)
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+{
+ TRACEEND("TOKENOPT", mod, pos, 0, 0, Tstart);
+ return (BITON(TOCC, TOPT) /* Inherently optional. */
+ || TOKENHIT /* Was hit (handles "plus" suffix case). */
+ || (!ANYHIT(H) && groupopt(mod, pos)));
+ /* In optional group with no hits. */
+}
+/* GROUPOPT: Temporarily makes the current group be the current token so that
+ TOKENOPT() can be applied to it. Returns the value returned
+ by TOKENOPT.
+*/
+int groupopt(mod, pos)
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+{
+ UNCH saveM; /* Save M when testing if group is not required.*/
+ int rc; /* 1=contextually optional; 0=not. */
+
+ if (P==1) return(BITON(GOCC, TOPT) || TOKENHIT);
+ saveM = M; M = G; --P;
+ rc = tokenopt(mod, pos);
+ ++P; G = M; M = saveM;
+ return(rc);
+}
+/* TOKENREQ: Returns RCREQ if the current token is "contextually required".
+ That is, it is not contextually optional and
+ 1) it is a member of a "seq" group that is either required
+ or has at least 1 hit token.
+ 2) it is a member of an "and" group in which all other
+ tokens were hit.
+ Optional tokens are not counted
+ if GI is ETDCDATA, as we are looking for an
+ omitted start-tag. Otherwise, they are counted,
+ as the GI might match one of them.
+ Returns RCNREQ if the current token is "not required".
+*/
+int tokenreq(gi, mod, pos)
+struct etd *gi; /* ETD of new GI. */
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+{
+ TRACEGI("TOKENREQ", gi, mod, pos, Tstart);
+ return( tokenopt(mod, pos) ? RCNREQ
+ : ( GTYPE==TTSEQ && (ANYHIT(H) || groupreq(gi, mod, pos)==RCREQ)
+#if 0
+ || (GTYPE==TTAND && allhit(&GHDR, H, T, \*gi!=ETDCDATA*\ 1))
+#endif
+ )
+ ? RCREQ : RCNREQ );
+}
+/* GROUPREQ: Temporarily makes the current group be the current token so that
+ TOKENREQ() can be applied to it. Returns the value returned
+ by TOKENREQ.
+*/
+int groupreq(gi, mod, pos)
+struct etd *gi; /* ETD of new GI. */
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+{
+ UNCH saveM; /* Save M when testing if group is not required.*/
+ int rc; /* Return code: RCREQ RCNREQ */
+
+ if (P==1) return(BITOFF(GOCC, TOPT) ? RCREQ : RCNREQ);
+ saveM = M; M = G; --P;
+ rc = tokenreq(gi, mod, pos);
+ ++P; G = M; M = saveM;
+ return(rc);
+}
+/* GRPSZ: Returns the number of tokens spanned by a group in the model (M),
+ from the group's start (G) to a specified index within the group (T).
+ M = 0, plus 1 for each token in the group, plus the size of
+ any subgroups (gotten by calling GRPSZ recursively). On entry,
+ M must be equal to G at the current level.
+*/
+int grpsz(g, t)
+struct thdr *g; /* mod[G]: Ptr to group in the model. */
+int t; /* T: Index of last token in the group. */
+{
+ struct thdr *p = g; /* Ptr to current token in the model. */
+ int m = 0; /* Size of group (including nested groups). */
+ int i = 0; /* Number of group members (loop counter). */
+ UNS type; /* Token type (without TOREP bits). */
+
+ while (++i<=t) {
+ ++p; ++m;
+ type = GET(p->ttype, TTMASK);
+ if (type==TTOR || type==TTSEQ || type==TTAND) {
+ m += grpsz(p, p->tu.tnum);
+ p = g+m;
+ }
+ }
+ return(m);
+}
+/* ALLHIT: Returns 1 if all hit bits for the specified group are turned on,
+ (other than those that correspond to optional tokens if "opt" is
+ 0) and the "but" bit (all bits if "but" bit is zero). Otherwise,
+ returns 0. GRPSZ is used to skip past subgroup tokens.
+*/
+int allhit(p, hits, but, opt)
+struct thdr *p; /* mod[G]: Ptr to group in the model. */
+unsigned long *hits; /* H: Hit bits to be tested. */
+int but; /* Index of bit to ignore; 0=test all. */
+int opt; /* 1=optional tokens must be hit; 0=ignore. */
+{
+ int b = 0; /* Index of bit being tested in hits. */
+ int e = p->tu.tnum; /* Ending index (number of bits to test). */
+ unsigned type; /* Token type (without TOREP bits). */
+
+ while (++p, ++b<=e) {
+ if (HITOFF(hits,b) && (opt || BITOFF(p->ttype,TOPT)) && b!=but)
+ return 0;
+ if ((type = GET(p->ttype,TTMASK))==TTOR || type==TTSEQ || type==TTAND)
+ p += grpsz(p, p->tu.tnum);
+ }
+ return 1;
+}
+/* OFFBIT: Returns the index of the first unset bit after (i.e., not including)
+ the caller's "first" bit. If all bits through the
+ specified last bit are on, it returns 0.
+*/
+int offbit(bits, first, last)
+unsigned long *bits; /* Bits to be tested. */
+int first; /* Index of first bit to be tested in bits. */
+int last; /* Index of last bit to be tested in bits. */
+{
+ while (++first <= last)
+ if (HITOFF(bits, first))
+ return first;
+ return 0;
+}
+
+/* ANYHIT: Return 1 if any bit is set. */
+
+int anyhit(bits)
+unsigned long *bits;
+{
+ int i;
+ for (i = 0; i < grplongs; i++)
+ if (bits[i] != 0)
+ return 1;
+ return 0;
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/context.h b/usr.bin/sgmls/sgmls/context.h
new file mode 100644
index 0000000..04350c7
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/context.h
@@ -0,0 +1,17 @@
+/* context.h */
+
+#define M pos[0].g /* Index of current token in model. */
+#ifdef P
+#undef P
+#endif
+#define P pos[0].t /* Index of current group in pos. */
+#define G pos[P].g /* Index of current group in model. */
+#define T pos[P].t /* Index of current token in its group. */
+#define H pos[P].h /* Pointer to hit bits for current group. */
+#define GHDR mod[G] /* Current group header. */
+#define TOKEN mod[M] /* Current token. */
+#define TTYPE (GET(TOKEN.ttype, TTMASK)) /* Token type of current token. */
+#define TOCC (GET(TOKEN.ttype, TOREP)) /* Occurrence for current token. */
+#define GTYPE (GET(GHDR.ttype, TTMASK)) /* Token type of current group. */
+#define GOCC (GET(GHDR.ttype, TOREP)) /* Occurrence for current group. */
+#define GNUM GHDR.tu.tnum /* Number of tokens in current grp. */
diff --git a/usr.bin/sgmls/sgmls/dosproc.c b/usr.bin/sgmls/sgmls/dosproc.c
new file mode 100644
index 0000000..99b526d
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/dosproc.c
@@ -0,0 +1,40 @@
+/* dosproc.c -
+
+ MS-DOS implementation of run_process().
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifdef SUPPORT_SUBDOC
+
+#include "std.h"
+#include "entity.h"
+#include "appl.h"
+
+#include <process.h>
+
+int run_process(argv)
+char **argv;
+{
+ int ret;
+ fflush(stdout);
+ fflush(stderr);
+ ret = spawnvp(P_WAIT, argv[0], argv);
+ if (ret < 0)
+ appl_error(E_EXEC, argv[0], strerror(errno));
+ return ret;
+}
+
+#endif /* SUPPORT_SUBDOC */
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/ebcdic.c b/usr.bin/sgmls/sgmls/ebcdic.c
new file mode 100644
index 0000000..b8188c7
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/ebcdic.c
@@ -0,0 +1,42 @@
+/* ASCII to EBCDIC (ISO 8859-1 to IBM CP 37v2) table. */
+/* Contributed by C. M. Sperberg-McQueen <u35395@uicvm.uic.edu>. */
+
+/* The mapping must be 1 to 1. The positions of *CHAR and *CH in the table
+must not be changed, although the values in ebcdic.h can be. */
+
+#include "ebcdic.h"
+
+unsigned char charset[] = {
+ 0, 1, 2, 3, 55, 45, 46, 47,
+ GENRECHAR, TABCHAR, RSCHAR, 11, 12, RECHAR, 14, 15,
+ 16, 17, 18, 19, 60, 61, 50, 38,
+ 24, 25, EOFCHAR, 39, EOBCHAR, DELCDATA, DELSDATA, DELNONCH,
+ SPCCHAR, 90, 127, 123, 91, 108, 80, 125,
+ 77, 93, 92, 78, 107, 96, 75, 97,
+240, 241, 242, 243, 244, 245, 246, 247,
+248, 249, 122, 94, 76, 126, 110, 111,
+124, 193, 194, 195, 196, 197, 198, 199,
+200, 201, 209, 210, 211, 212, 213, 214,
+215, 216, 217, 226, 227, 228, 229, 230,
+231, 232, 233, 173, 224, 189, 176, 109,
+121, 129, 130, 131, 132, 133, 134, 135,
+136, 137, 145, 146, 147, 148, 149, 150,
+151, 152, 153, 162, 163, 164, 165, 166,
+167, 168, 169, 192, 79, 208, 161, 7,
+ 4, 6, 8, 9, 10, 20, 21, 23,
+ 26, 27, 32, 33, 34, 35, 36, 40,
+ 41, 42, 43, 44, 48, 49, 51, 52,
+ 53, 54, 56, 57, 58, 59, 62, 255,
+ 65, 170, 74, 177, 159, 178, 106, 181,
+187, 180, 154, 138, 95, 202, 175, 188,
+144, 143, 234, 250, 190, 160, 182, 179,
+157, 218, 155, 139, 183, 184, 185, 171,
+100, 101, 98, 102, 99, 103, 158, 104,
+116, 113, 114, 115, 120, 117, 118, 119,
+172, 105, 237, 238, 235, 239, 236, 191,
+128, 253, 254, 251, 252, 186, 174, 89,
+ 68, 69, 66, 70, 67, 71, 156, 72,
+ 84, 81, 82, 83, 88, 85, 86, 87,
+140, 73, 205, 206, 203, 207, 204, 225,
+112, 221, 222, 219, 220, 141, 142, 223,
+};
diff --git a/usr.bin/sgmls/sgmls/ebcdic.h b/usr.bin/sgmls/sgmls/ebcdic.h
new file mode 100644
index 0000000..1c35bcb
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/ebcdic.h
@@ -0,0 +1,40 @@
+/* SGML Character Use: EBCDIC
+*/
+
+#define EOFCHAR '\077' /* FUNCTION: EE (entity end: files). */
+#define EOBCHAR '\034' /* NONCHAR: EOB (file entity: end of buffer. */
+#define RSCHAR '\045' /* FUNCTION: RS (record start). */
+#define RECHAR '\015' /* FUNCTION: RE (record end). */
+#define TABCHAR '\005' /* FUNCTION: TAB (horizontal tab). */
+#define SPCCHAR '\100' /* FUNCTION: SPACE (horizontal space). */
+#define GENRECHAR '\026' /* NONCHAR: Generated RE. */
+#define DELCDATA '\035' /* NONCHAR: Delimiter for CDATA entity in
+ attribute value. */
+#define DELSDATA '\036' /* NONCHAR: Delimiter for SDATA entity in
+ attribute value. */
+#define DELNONCH '\037' /* NONCHAR: non-SGML character prefix. */
+
+/* This should work for EBCDIC. See comment in latin1.h. */
+#define SHIFTNON(ch) ((UNCH)(ch) | 0200)
+#define UNSHIFTNON(ch) ((UNCH)(ch) & ~0200)
+
+/* See comment in latin1.h. */
+#define CANON_NONSGML 255
+
+/* See comment in latin1.h. */
+#define CANON_DATACHAR 254
+
+/* Components for a formal public identifier for the whole of the
+system character set. Protect with ifndef so that it can be overriden
+in config.h. */
+
+/* Use a private escape sequence. */
+#ifndef SYSTEM_CHARSET_DESIGNATING_SEQUENCE
+#define SYSTEM_CHARSET_DESIGNATING_SEQUENCE "ESC 2/5 2/15 3/0"
+#endif
+#ifndef SYSTEM_CHARSET_OWNER
+#define SYSTEM_CHARSET_OWNER "-//IBM"
+#endif
+#ifndef SYSTEM_CHARSET_DESCRIPTION
+#define SYSTEM_CHARSET_DESCRIPTION "Code Page 1047"
+#endif
diff --git a/usr.bin/sgmls/sgmls/entgen.c b/usr.bin/sgmls/sgmls/entgen.c
new file mode 100644
index 0000000..e08e9f0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/entgen.c
@@ -0,0 +1,405 @@
+/* entgen.c -
+
+ Implement entgen() which generates a list of filenames from a struct fpi.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifdef HAVE_ACCESS
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* For R_OK. */
+#endif /* HAVE_UNISTD_H */
+
+#ifndef R_OK
+#define R_OK 4
+#endif /* not R_OK */
+
+#endif /* HAVE_ACCESS */
+
+#include "sgmlaux.h"
+
+/* Environment variable that contains path. */
+#ifndef PATH_ENV_VAR
+#define PATH_ENV_VAR "SGML_PATH"
+#endif
+/* Default search path. See field() for interpretation of %*. */
+#ifndef DEFAULT_PATH
+#define DEFAULT_PATH "/usr/local/lib/sgml/%O/%C/%T:%N.%X:%N.%D"
+#endif
+
+#ifndef PATH_FILE_SEP
+#define PATH_FILE_SEP ':'
+#endif
+
+#ifndef SYSID_FILE_SEP
+#define SYSID_FILE_SEP ':'
+#endif
+
+/* This says: change space to underscore, slash to percent. */
+
+#ifndef MIN_DAT_SUBS_FROM
+#define MIN_DAT_SUBS_FROM " /"
+#endif
+#ifndef MIN_DAT_SUBS_TO
+#define MIN_DAT_SUBS_TO "_%"
+#endif
+
+static int field P((struct fpi *, int, char *));
+static int mindatcpy P((char *, char *, int, int));
+static int testopen P((char *));
+static UNIV sysidgen P((char *));
+
+static char *path = 0;
+
+/* Non-zero if searching should be performed when a system identifier
+is specified. */
+static int sysidsrch = 0;
+
+#define EMPTY_VERSION "default"
+
+static char *classes[] = {
+ "capacity",
+ "charset",
+ "notation",
+ "syntax",
+ "document",
+ "dtd",
+ "elements",
+ "entities",
+ "lpd",
+ "nonsgml",
+ "shortref",
+ "subdoc",
+ "text"
+ };
+
+/* This is mainly for compatibility with arcsgml. */
+
+static char *genext[] = {
+ "nsd", /* Non-SGML data entity. */
+ "gml", /* GML document or text entity. */
+ "spe", /* System parameter entity. */
+ "dtd", /* Document type definition. */
+ "lpd", /* Link process definition. */
+ "pns", /* Public non-SGML data entity. */
+ "pge", /* Public general entity. */
+ "ppe", /* Public parameter entity. */
+ "pdt", /* Public document type definition. */
+ "plp", /* Public link process definition. */
+ "vns", /* Display version non-SGML data entity. */
+ "vge", /* Display version general entity. */
+ "vpe", /* Display version parameter entity. */
+ "vdt", /* Display version document type definition.*/
+ "vlp", /* Display version link process definition.*/
+};
+
+static char *ext[] = {
+ "sgml", /* SGML subdocument */
+ "data", /* Data */
+ "text", /* General text */
+ "parm", /* Parameter entity */
+ "dtd", /* Document type definition */
+ "lpd", /* Link process definition */
+};
+
+/* Like memcpy, but substitute, fold to lower case (if fold is
+non-zero) and null terminate. This is used both for minimum data and
+for names. If p is NULL, do nothing. Return len. */
+
+static int mindatcpy(p, q, len, fold)
+char *p, *q;
+int len;
+int fold;
+{
+ static char subsfrom[] = MIN_DAT_SUBS_FROM;
+ static char substo[] = MIN_DAT_SUBS_TO;
+ int n;
+
+ if (!p)
+ return len;
+ for (n = len; --n >= 0; q++) {
+ char *r = strchr(subsfrom, *q);
+ if (!r) {
+ if (fold && ISASCII(*q) && isupper((UNCH)*q))
+ *p++ = tolower((UNCH)*q);
+ else
+ *p++ = *q;
+ }
+ else {
+ int i = r - subsfrom;
+ if (i < sizeof(substo) - 1)
+ *p++ = substo[i];
+ }
+ }
+ *p = '\0';
+ return len;
+}
+
+
+/* Return length of field. Copy into buf if non-NULL. */
+
+static int field(f, c, buf)
+struct fpi *f;
+int c;
+char *buf;
+{
+ int n;
+
+ switch (c) {
+ case '%':
+ if (buf) {
+ buf[0] = '%';
+ buf[1] = '\0';
+ }
+ return 1;
+ case 'N': /* the entity, document or dcn name */
+ return mindatcpy(buf, (char *)f->fpinm, ustrlen(f->fpinm),
+ (f->fpistore != 1 && f->fpistore != 2 && f->fpistore != 3
+ ? NAMECASE
+ : ENTCASE));
+ case 'D': /* dcn name */
+ if (f->fpistore != 1) /* not a external data entity */
+ return -1;
+ if (f->fpinedcn == 0) /* it's a SUBDOC */
+ return -1;
+ return mindatcpy(buf, (char *)f->fpinedcn, ustrlen(f->fpinedcn),
+ NAMECASE);
+ case 'X':
+ /* This is for compatibility with arcsgml */
+ if (f->fpistore < 1 || f->fpistore > 5)
+ return -1;
+ n = (f->fpipubis != 0)*(f->fpiversw > 0 ? 2 : 1)*5+f->fpistore - 1;
+ if (buf)
+ strcpy(buf, genext[n]);
+ return strlen(genext[n]);
+ case 'Y': /* tYpe */
+ n = f->fpistore;
+ if (n < 1 || n > 5)
+ return -1;
+ if (n == 1 && f->fpinedcn == 0) /* it's a SUBDOC */
+ n = 0;
+ if (buf)
+ strcpy(buf, ext[n]);
+ return strlen(ext[n]);
+ case 'P': /* public identifier */
+ if (!f->fpipubis)
+ return -1;
+ return mindatcpy(buf, (char *)f->fpipubis, ustrlen(f->fpipubis), 0);
+ case 'S': /* system identifier */
+ if (!f->fpisysis)
+ return -1;
+ else {
+ UNCH *p;
+ n = 0;
+ for (p = f->fpisysis; *p; p++)
+ if (*p != RSCHAR) {
+ if (buf)
+ buf[n] = *p == RECHAR ? '\n' : *p;
+ n++;
+ }
+ return n;
+ }
+ }
+ /* Other fields need a formal public identifier. */
+ /* return -1 if the formal public identifier was invalid or missing. */
+ if (f->fpiversw < 0 || !f->fpipubis)
+ return -1;
+
+ switch (c) {
+ case 'A': /* Is it available? */
+ return f->fpitt == '+' ? 0 : -1;
+ case 'I': /* Is it ISO? */
+ return f->fpiot == '!' ? 0 : -1;
+ case 'R': /* Is it registered? */
+ return f->fpiot == '+' ? 0 : -1;
+ case 'U': /* Is it unregistered? */
+ return f->fpiot == '-' ? 0 : -1;
+ case 'L': /* public text language */
+ if (f->fpic == FPICHARS)
+ return -1;
+ /* it's entered in all upper case letters */
+ return mindatcpy(buf, (char *)f->fpipubis + f->fpil, f->fpill, 1);
+ case 'O': /* owner identifier */
+ return mindatcpy(buf, (char *)f->fpipubis + f->fpio, f->fpiol, 0);
+ case 'C': /* public text class */
+ n = f->fpic - 1;
+ if (n < 0 || n >= sizeof(classes)/sizeof(classes[0]))
+ return -1;
+ if (buf)
+ strcpy(buf, classes[n]);
+ return strlen(classes[n]);
+ case 'T': /* text description */
+ return mindatcpy(buf, (char *)f->fpipubis + f->fpit, f->fpitl, 0);
+ case 'V':
+ if (f->fpic < FPICMINV) /* class doesn't have version */
+ return -1;
+ if (f->fpiversw > 0) /* no version */
+ return -1;
+ if (f->fpivl == 0) { /* empty version: */
+ /* use device-independent version*/
+ if (buf)
+ strcpy(buf, EMPTY_VERSION);
+ return strlen(EMPTY_VERSION);
+ }
+ return mindatcpy(buf, (char *)f->fpipubis + f->fpiv, f->fpivl, 0);
+ case 'E': /* public text designating (escape) sequence */
+ if (f->fpic != FPICHARS)
+ return -1;
+ return mindatcpy(buf, (char *)f->fpipubis + f->fpil, f->fpill, 0);
+ default:
+ break;
+ }
+ return -1;
+}
+
+static int testopen(pathname)
+char *pathname;
+{
+#ifdef HAVE_ACCESS
+ return access(pathname, R_OK) >= 0;
+#else /* not HAVE_ACCESS */
+ FILE *fp;
+ fp = fopen(pathname, "r");
+ if (!fp)
+ return 0;
+ fclose(fp);
+ return 1;
+#endif /* not HAVE_ACCESS */
+}
+
+/* Return a pointer to an dynamically-allocated buffer that contains
+ the names of the files containing this entity, with each filename
+ terminated by a '\0', and with the list of filenames terminated by
+ another '\0'. */
+
+UNIV entgen(f)
+struct fpi *f;
+{
+ char *file;
+
+ assert(f->fpistore != 6); /* Musn't call entgen for a notation. */
+ if (!path) {
+ char *p;
+ char c;
+ path = getenv(PATH_ENV_VAR);
+ if (!path)
+ path = DEFAULT_PATH;
+ p = path;
+
+ /* Only search for system identifiers if path uses %S. */
+ while ((c = *p++) != '\0')
+ if (c == '%') {
+ if (*p == 'S') {
+ sysidsrch = 1;
+ break;
+ }
+ if (*p != '\0' && *p != PATH_FILE_SEP)
+ p++;
+ }
+ }
+ if (f->fpisysis
+ && (!sysidsrch
+ || strchr((char *)f->fpisysis, SYSID_FILE_SEP)
+ || strcmp((char *)f->fpisysis, STDINNAME) == 0))
+ return sysidgen((char *)f->fpisysis);
+
+ file = path;
+
+ for (;;) {
+ char *p;
+ int len = 0;
+ char *fileend = strchr(file, PATH_FILE_SEP);
+ if (!fileend)
+ fileend = strchr(file, '\0');
+
+ /* Check that all substitutions are non-null, and calculate
+ the resulting total length of the filename. */
+ for (p = file; p < fileend; p++)
+ if (*p == '%') {
+ int n;
+ /* Set len to -1 if a substitution is invalid. */
+ if (++p >= fileend) {
+ len = -1;
+ break;
+ }
+ n = field(f, *p, (char *)0);
+ if (n < 0) {
+ len = -1;
+ break;
+ }
+ len += n;
+ }
+ else
+ len++;
+
+ if (len > 0) {
+ /* We've got a valid non-empty filename. */
+ char *s;
+ char *buf;
+
+ s = buf = (char *)rmalloc(len + 2);
+ for (p = file; p < fileend; p++)
+ if (*p == '%')
+ s += field(f, *++p, s);
+ else
+ *s++ = *p;
+ *s++ = '\0';
+ if (testopen(buf)) {
+ /* Terminate the array of filenames. */
+ *s++ = '\0';
+ return buf;
+ }
+ free((UNIV)buf);
+ }
+ if (*fileend == '\0')
+ break;
+ file = ++fileend;
+ }
+ return 0;
+}
+
+/* Handle a system identifier without searching. */
+
+static
+UNIV sysidgen(s)
+char *s;
+{
+ char *buf, *p;
+
+ buf = (char *)rmalloc(strlen(s) + 2);
+
+ for (p = buf; *s; s++) {
+ if (*s == SYSID_FILE_SEP) {
+ if (p > buf && p[-1] != '\0')
+ *p++ = '\0';
+ }
+ else if (*s == RECHAR)
+ *p++ = '\n';
+ else if (*s != RSCHAR)
+ *p++ = *s;
+ }
+ /* Terminate this filename. */
+ if (p > buf && p[-1] != '\0')
+ *p++ = '\0';
+ if (p == buf) {
+ /* No filenames. */
+ frem((UNIV)buf);
+ return 0;
+ }
+ /* Terminate the list. */
+ *p++ = '\0';
+ return buf;
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/entity.h b/usr.bin/sgmls/sgmls/entity.h
new file mode 100644
index 0000000..d7d3096
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/entity.h
@@ -0,0 +1,189 @@
+/* Struct dcncb: attribute list added to support data attributes. */
+#ifndef ENTITY_H /* Don't include this file more than once. */
+#define ENTITY_H
+/* ENTITY.H: Definitions and control block templates for entity management.
+*/
+#include "tools.h" /* Definitions for type declarations, etc. */
+#include "msgcat.h"
+
+#define STDINNAME "-" /* File name that refers to standard input. */
+
+#define EOS '\0' /* NONCHAR: EE (entity end: strings). */
+
+#define AVALCASE 2 /* 2=untranslated string of name characters. */
+
+#define REFNAMELEN 8 /* reference quantity set NAMELEN */
+#define REFLITLEN 240 /* reference quantity set LITLEN */
+
+/* Minimization status of returned tags.
+*/
+#define MINNONE 0 /* Minimization: tag not minimized. */
+#define MINNULL 1 /* Minimization: tag was null. */
+#define MINNET 2 /* Minimization: end-tag was NET delimiter. */
+#define MINDATA 3 /* Minimization: end-tag was data tag. */
+#define MINSTAG 4 /* Minimization: tag implied by start-tag. */
+#define MINETAG 5 /* Minimization: end-tag implied by end-tag. */
+
+/* Formal public identifier public text classes.
+*/
+#define FPICAP 1
+#define FPICHARS 2
+#define FPINOT 3
+#define FPISYN 4
+#define FPICMINV 5 /* Minimum fpic value for versionable text. */
+#define FPIDOC 5
+#define FPIDTD 6
+#define FPIELEM 7
+#define FPIENT 8
+#define FPILPD 9
+#define FPINON 10
+#define FPISHORT 11
+#define FPISUB 12
+#define FPITEXT 13
+struct fpi { /* Formal public identifier. */
+ UNCH fpiot; /* Owner type: + or - or ! (for ISO). */
+ UNS fpiol; /* Length of owner identifier. */
+ UNS fpio; /* Offset in pubis of owner identifier (no EOS).*/
+ int fpic; /* Public text class. */
+ UNCH fpitt; /* Text type: - or + (for available). */
+ UNS fpitl; /* Length of text identifier. */
+ UNS fpit; /* Offset in pubis of text identifier (no EOS). */
+ UNS fpill; /* Language/designating sequence length. */
+ UNS fpil; /* Offset in pubis of language. */
+ UNS fpivl; /* Length of display version . */
+ UNS fpiv; /* Offset in pubis of display version (no EOS). */
+ int fpiversw; /* 1=use best ver; 0=use stated ver; -1=error. */
+ UNCH *fpinm; /* Entity/DCN name (EOS, no length). */
+ UNCH fpistore; /* 1=NDATA 2=general 3=parm 4=DTD 5=LPD 6=DCN. */
+ /* Name of the entity's DCN. Valid only when fpistore == 1.
+ NULL if it's a SUBDOC. */
+ UNCH *fpinedcn;
+ UNCH *fpipubis; /* Public ID string (EOS). */
+ UNCH *fpisysis; /* System ID string (EOS). */
+};
+#define FPISZ sizeof(struct fpi)
+typedef struct fpi *PFPI; /* Ptr to FPI control block. */
+
+/* General control blocks.
+*/
+#define NONONCH 1 /* Character references to non-chars invalid. */
+#define OKNONCH 0 /* Character references to non-chars allowed. */
+struct parse { /* Parse control block. */
+ char *pname; /* Parse name; content, tag, etc. */
+ UNCH *plex; /* Lexical analysis table. */
+ UNCH **ptab; /* State and action table. */
+ UNS state; /* State. */
+ UNS input; /* Input. */
+ UNS action; /* Action. */
+ UNS newstate; /* Next state. */
+};
+struct restate {
+ UNS sstate; /* State. */
+ UNS sinput; /* Input. */
+ UNS saction; /* Action. */
+ UNS snext; /* Next state. */
+};
+struct map {
+ UNCH *mapnm; /* Name followed by EOS. */
+ int mapdata; /* Data associated with that name. */
+};
+struct hash { /* Dummy structure for function arguments. */
+ struct hash *enext; /* Next entry in chain. */
+ UNCH *ename; /* Entry name with size and EOS. */
+};
+typedef struct hash *PHASH; /* Ptr to hash table entry. */
+typedef struct hash **THASH; /* Ptr to hash table. */
+
+struct fwdref { /* A forward id reference. */
+ struct fwdref *next; /* Pt to next reference in chain. */
+ UNIV msg; /* Ptr to saved error messsage. */
+};
+#define FWDREFSZ sizeof(struct fwdref)
+
+struct dcncb { /* Data content notation control block. */
+ struct dcncb *enext; /* Next DCN in chain. */
+ UNCH *ename; /* Notation name followed by EOS. */
+ UNCH mark; /* For use by application. */
+ UNCH entsw; /* Entity defined with this notation? */
+ UNCH defined; /* Has this notation been defined. */
+ UNCH *sysid; /* System identifier of notation. */
+ UNCH *pubid; /* Public identifier of notation. */
+ struct ad *adl; /* Data attribute list (NULL if none). */
+};
+#define DCBSZ sizeof(struct dcncb)
+#define DCNMARK(p) ((p)->mark ? 1 : ((p)->mark = 1, 0))
+
+typedef struct dcncb *PDCB; /* Ptr to DCN control block. */
+
+/* Number of capacities in a capacity set. */
+
+#define NCAPACITY 17
+
+struct sgmlcap {
+ char **name;
+ UNCH *points;
+ long *number;
+ long *limit;
+};
+
+struct sgmlstat { /* Document statistics. */
+ UNS dcncnt; /* Number of data content notations defined. */
+ UNS pmexgcnt; /* Number of plus or minus exception groups. */
+ UNS etdcnt; /* Number of element types declared. */
+ UNS etdercnt; /* Number of element types defined by default. */
+ UNS pmexcnt; /* Number of plus/minus exception grp members. */
+ UNS modcnt; /* Number of content model tokens defined. */
+ UNS attcnt; /* Number of attributes defined. */
+ UNS attdef; /* Characters of attribute defaults defined. */
+ UNS attgcnt; /* Number of att value grp members (incl dcn). */
+ UNS idcnt; /* Number of ID attributes specified. */
+ UNS idrcnt; /* Number of ID references specified. */
+ UNS ecbcnt; /* Number of entities declared. */
+ UNS ecbtext; /* Characters of entity text defined. */
+ UNS srcnt; /* Number of short reference tables defined. */
+ UNS dcntext; /* Characters of notation identifiers defined. */
+};
+struct switches { /* Parser control switches (1=non-standard). */
+ int swdupent; /* 1=msg if duplicate ENTITY def attempted;0=no.*/
+ int swcommnt; /* 1=return comment declarations as data; 0=no. */
+ int swrefmsg; /* 1=msg if undeclared ref is defaulted; 0=no. */
+ UNS swbufsz; /* Size of source file buffer for READ(). */
+ int swenttr; /* 1=trace entity stack in error messages; 0=no.*/
+ int sweltr; /* 1=trace element stack in error messages; 0=no. */
+ int swambig; /* 1=check content model ambiguity */
+ int swundef; /* 1=warn about undefined elements and notations. */
+ char *prog; /* Program name for error messages. */
+#ifdef TRACE
+ char *trace; /* What to trace in the body. */
+ char *ptrace; /* What to trace in the prolog. */
+#endif /* TRACE */
+ nl_catd catd; /* Message catalog descriptor. */
+ long nopen; /* Number of open document entities */
+ int onlypro; /* Parse only the prolog. */
+ char **includes; /* List of parameter entities to be defined
+ as "INCLUDE"; NULL terminated.*/
+ VOID (*die) P((void)); /* Function to call on fatal error. */
+};
+struct markup { /* Delimiter strings for text processor. */
+ UNCH *cro; /* LEXCON markup string: CRO */
+ UNCH *dso; /* LEXCON markup string: DSO */
+ UNCH *ero; /* LEXCON markup string: ERO */
+ UNCH *etag; /* LEXMARK markup string: end-tag */
+ UNCH *lit; /* LEXMARK markup string: LIT */
+ UNCH *lita; /* LEXMARK markup string: LITA */
+ UNCH *mdc; /* LEXCON markup string: MDC */
+ UNCH *mdo; /* LEXCON markup string: MDO */
+ UNCH *mse; /* LEXCON markup string: mse */
+ UNCH *mss; /* LEXCON markup string: mss */
+ UNCH *mssc; /* LEXCON markup string: mss CDATA */
+ UNCH *mssr; /* LEXCON markup string: mss RCDATA */
+ UNCH *pic; /* LEXCON markup string: PIC */
+ UNCH *pio; /* LEXCON markup string: PIO */
+ UNCH *refc; /* LEXGRP markup string: REFC */
+ UNCH *stag; /* LEXMARK markup string: start-tag */
+ UNCH *tagc; /* LEXMARK markup string: TAGC */
+ UNCH *vi; /* LEXMARK markup string: VI */
+ int lennet; /* LEXMARK markup string length: null end-tag. */
+ int lennst; /* LEXMARK markup string length: null start-tag.*/
+};
+#endif /* ndef ENTITY_H */
diff --git a/usr.bin/sgmls/sgmls/error.h b/usr.bin/sgmls/sgmls/error.h
new file mode 100644
index 0000000..d37d493
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/error.h
@@ -0,0 +1,61 @@
+/* ERROR.H: Symbols for SGML error codes (start with 'E_').
+ Numbers 46 - 56 are generated by ERROR.C.
+ Later numbers are coded directly.
+*/
+/* SGMLERR.C: General errors and syntax errors.
+*/
+#define E_CONTEXT 1 /* W GI not allowed at this point in structure. */
+#define E_MDNAME 2 /* E Invalid markup declaration name. */
+/*efine E_LEN 3 E Syntax error: length exceeded. */
+#define E_SYS 4 /* W Illegal system character. */
+#define E_ETAG 5 /* E End-tag does not match any open start-tag. */
+#define E_STAGMAX 6 /* E Maximum number of open elements exceeded. */
+/* E_ALLNULL 7 W Start- and end-tag omitted with null content. */
+#define E_EOF 8 /* E/W Illegal entity end in markup or delimited text. */
+/* fine E_INV 9 E Markup error: invalid character. */
+#define E_CHARS 10 /* W Data found in content that doesn't allow it. */
+/* fine E_NOETDE 11 E End-tag GI not defined by element declaration. */
+#define E_BADNM 12 /* E Name is not syntactically valid. */
+#define E_BADATT 13 /* E Attribute was not defined by element declaration. */
+#define E_VALINV 14 /* W Att value/declaration conflict: invalid char. */
+#define E_VALLEN 15 /* W Att value/declaration conflict: token too long. */
+#define E_VALCNT 16 /* W Att value/declaration conflict: too many tokens. */
+#define E_VALTYPE 17 /* W Att value/declaration conflict: wrong token type.*/
+#define E_VALGRP 18 /* W Att value/declaration conflict: token not in grp.*/
+#define E_VALREQ 19 /* W Att value/declaration conflict: req unspecified. */
+/* E_EMIN 20 W End-tag implied by end-tag; not minimizable. */
+/* E_SMIN 21 W Omitted start-tag was not minimizable. */
+#define E_POSSATT 22 /* E Possible att found but not defined; used as data.*/
+/* Late additions numbered out of order to avoid recompilation. */
+/*efine E_ENTSYNC 37 E Entity and group nesting levels out of sync. */
+#define E_BADVAL 25 /* W Att value omitted (null); default used. */
+/* E_ECONTXT 30 W Element ended prematurely (some content omitted).*/
+/* E_EMINST 39 W End-tag implied by start-tag; not minimizable. */
+/* E_MEXTAG 40 W *** In Use *** */
+#define E_MEXERR 41 /* W Attempt to exclude contextually required element.*/
+#define E_DOCTYPE 42 /* W No document type defined; *DOCTYPE assumed. */
+/* E_NOETDS 43 E Start-tag GI not defined by element declaration. */
+#define E_RESTART 44 /* E Invalid chars ignored; trying to restart parse. */
+
+/* MDERROR.C: Errors in markup declarations.
+*/
+/*efine E_DUP 23 E Duplicate specification. */
+/*efine E_KEY 24 E Incorrect keyword for parameter. */
+/*efine E_MSE 26 E MSE occurred with no corresponding MS. */
+/*efine E_MSS 27 E MSS exceeded maximum nesting level. */
+/*efine E_NUM 28 E Incorrect number of parameters. */
+#define E_TYPE 29 /* E Incorrect parameter type. */
+/* Late additions numbered out of order to avoid recompilation. */
+/*efine E_VAL 38 W Incorrect parameter value. */
+
+/* RESERROR.C: Errors in resource routines.
+*/
+/* Unused I End of primary source entity. */
+/* fine E_FILBUF 31 E Could not read next buffer. */
+/* fine E_ERFILE 32 E Could not open file. */
+/* fine E_MALLOC 33 T Could not obtain required main storage. */
+/* fine E_ERMAX 34 E Maximum number of open entities exceeded. */
+/* fine E_ERNAME 35 E Referenced entity undeclared. */
+/* fine E_ERLOOP 36 E Entity referenced within itself: ref ignored. */
+/* Late additions numbered out of order to avoid recompilation. */
+/* E_ERDEF 45 E Referenced entity undeclared; SYSTEM assumed. */
diff --git a/usr.bin/sgmls/sgmls/etype.h b/usr.bin/sgmls/sgmls/etype.h
new file mode 100644
index 0000000..707f602
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/etype.h
@@ -0,0 +1,91 @@
+/* ETYPE.H: Definitions for element type and group processing.
+*/
+#define MCHARS 0x80 /* Model: contains #CHARS. */
+#define MGI 0x40 /* Model: contains GI names. */
+#define MPHRASE 0x20 /* Model: first token is #CHARS. */
+#define MKEYWORD 0x1F /* Model: defined with single keyword. */
+#define MNONE 0x10 /* Model: contains no GIs or #CHARS. */
+#define MANY 0x08 /* Model: contains any GIs or #CHARS. */
+#define MRCDATA 0x04 /* Model: contains RCDATA. */
+#define MCDATA 0x02 /* Model: contains CDATA. */
+
+#define TOREP (TOPT+TREP) /* 11000000 Optional and repeatable. */
+#define TOPT 0x80 /* Token: 1=optional; 0=required. */
+#define TREP 0x40 /* Token: 1=repeatable; 0=not. */
+#define TXOREP (TXOPT+TXREP) /* * explicitly specified */
+#define TXOPT 0x20 /* ? explicitly specified */
+#define TXREP 0x10 /* + explicitly specified */
+#define TTMASK 0x0F /* 00001111 Mask for testing token type. */
+#define TTETD 4 /* 00000100 Token is an ETD. */
+#define TTAND 3 /* 00000011 Token is an AND group. */
+#define TTSEQ 2 /* 00000010 Token is a sequence group. */
+#define TTOR 1 /* 00000001 Token is an OR group. */
+#define TTCHARS 0 /* 00000000 Token is #CHARS. */
+
+struct thdr { /* Token header or model header. */
+ UNCH ttype; /* Token type attributes or model content. */
+ union {
+ int tnum; /* Group token: tokens in group.
+ Model header: content tokens at any level. */
+ struct etd *thetd; /* GI token: ptr to etd. */
+ } tu;
+};
+#define THSZ (sizeof(struct thdr))
+
+#define ETDHASH 211 /* Size of element hash table. Must be prime. */
+#define SMO 0x40 /* ETDMIN: Start-tag O minimization. */
+#define EMO 0x04 /* ETDMIN: End-tag O minimization. */
+#define EMM 0x02 /* ETDMIN: End-tag minimization explicitly
+ specified to be minus */
+#define ETDDCL 0x80 /* ETDMIN: Element was declared. */
+#define ETDUSED 0x20 /* ETDMIN: Element used in another declaration. */
+#define ETDOCC 0x10 /* ETDMIN: Element occurred in document. */
+
+struct etd { /* Element type definition. */
+ struct etd *etdnext; /* Next element type definition in hash chain. */
+ UNCH *etdgi; /* GI preceded by its length, followed by EOS. */
+ UNCH etdmin; /* Flag bits: minimization. */
+ UNCH mark; /* Mark bit: for ambiguity checking */
+ struct thdr *etdmod; /* Content model. */
+ struct etd **etdmex; /* Minus exceptions. */
+ struct etd **etdpex; /* Plus exceptions. */
+ struct ad *adl; /* Attribute descriptor list. */
+ struct entity **etdsrm; /* Short reference map. */
+};
+#define ETDSZ (sizeof(struct etd))
+typedef struct etd *PETD;
+extern struct etd dumetd[];
+
+/* Number of bits in a long must be >= 1<<LONGPOW */
+#define LONGPOW 5
+
+#define LONGBITS (1<<LONGPOW)
+
+struct mpos { /* Position of current element in model. */
+ UNCH g; /* Index of this group in the model. */
+ UNCH t; /* Index of the current token in this group. */
+ unsigned long *h; /* Hit bits of this group's tokens. */
+};
+
+#define HITCLEAR(h) MEMZERO((UNIV)(h), grplongs*sizeof(unsigned long))
+
+#define TAGCONER 0x01 /* 00000001 (contersw) Tag was out of context. */
+#define TAGNET 0x02 /* 00000010 (etisw) Tag has NET enabled. */
+#define TAGPEX 0x04 /* 00000100 (pexsw) Tag was plus exception. */
+#define TAGREF 0x08 /* 00001000 (conrefsw) Tag had CONREF or EMPTY.*/
+struct tag { /* Tag control block. */
+ UNCH status; /* Status of context check. */
+ UNCH tflags; /* Flags: TAGCONER TAGNET TAGPEX TAGREF */
+ struct etd *tetd; /* Element type definition for tag. */
+ struct entity **tsrm; /* Current short reference map. */
+ struct mpos *tpos; /* Position of next tag in this model. */
+};
+
+#define RCEND 1 /* No more tokens: end element and retry GI. */
+#define RCREQ 2 /* Required GI must precede proposed GI. */
+#define RCMISS 3 /* GI invalid: not element end; no required GI. */
+#define RCHIT 4 /* GI is the one expected next. */
+#define RCMEX 5 /* GI invalid: minus exception. */
+#define RCHITMEX 6 /* RCMEX with invalid attempted minus exclusion.*/
+#define RCPEX 7 /* GI is valid solely because of plus exclusion.*/
+#define RCNREQ 8 /* Token is not required; can retry invalid GI. */
diff --git a/usr.bin/sgmls/sgmls/exclude.c b/usr.bin/sgmls/sgmls/exclude.c
new file mode 100644
index 0000000..7d72cc0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/exclude.c
@@ -0,0 +1,121 @@
+/* exclude.c -
+ Exclusion checking.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "sgmlincl.h"
+
+static int excktok P((struct thdr *, int, int *));
+static int exmark P((int));
+
+/* Check that the current exclusions are legal for the content model
+of the current element. */
+
+VOID exclude()
+{
+ struct thdr *mod = tags[ts].tetd->etdmod;
+
+ if ((mod->ttype & MKEYWORD) == 0 && exmark(1)) {
+ int excl;
+
+ excktok(mod + 1, 0, &excl);
+ exmark(0);
+ }
+}
+
+/* Set the mark field of all current exclusions to val. Return 1 if
+there are some current exclusions. */
+
+static
+int exmark(val)
+int val;
+{
+ int i;
+ int gotone = 0;
+
+ for (i = ts; i > 0; --i) {
+ struct etd **p = tags[i].tetd->etdmex;
+ if (p) {
+ for (; *p; p++)
+ (*p)->mark = val;
+ gotone = 1;
+ }
+ }
+ return gotone;
+}
+
+/* Check exclusions for this token. Return size of token. */
+
+static
+int excktok(t, orgrp, excl)
+struct thdr *t;
+int orgrp; /* 1 if token is member of or group */
+int *excl; /* Set to 1 if token is excluded. */
+{
+ int size;
+ struct thdr *tem;
+ int tnum;
+ int optional = 0;
+ int hadopt, hadreq;
+
+ *excl = 0;
+
+ switch (t->ttype & TTMASK) {
+ case TTETD:
+ if (t->tu.thetd->mark) {
+ if (orgrp || (t->ttype & TOPT))
+ *excl = 1;
+ else
+ sgmlerr(217, &pcbstag, t->tu.thetd->etdgi + 1,
+ tags[ts].tetd->etdgi + 1);
+ }
+ /* fall through */
+ case TTCHARS:
+ size = 1;
+ break;
+ case TTOR:
+ case TTAND:
+ case TTSEQ:
+ tem = t + 1;
+ hadopt = 0;
+ hadreq = 0;
+ for (tnum = t->tu.tnum; tnum > 0; --tnum) {
+ int ex;
+ int n = excktok(tem, (t->ttype & TTMASK) == TTOR, &ex);
+ if (!ex) {
+ if (tem->ttype & TOPT)
+ hadopt = 1;
+ else
+ hadreq = 1;
+ }
+ tem += n;
+ }
+ size = tem - t;
+ if ((t->ttype & TTMASK) == TTOR)
+ optional = hadreq ? hadopt : 1;
+ else
+ optional = !hadreq;
+ break;
+ default:
+ abort();
+ }
+
+ /* Was required, but exclusions have made it optional.
+ eg <!element foo - - (a | b) -(a, b)> */
+
+ if (optional && !(t->ttype & TOPT))
+ sgmlerr(216, &pcbstag, tags[ts].tetd->etdgi + 1, (UNCH *)0);
+
+ return size;
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/genlex.c b/usr.bin/sgmls/sgmls/genlex.c
new file mode 100644
index 0000000..2a0d3a6
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/genlex.c
@@ -0,0 +1,114 @@
+/* genlex: Generate lexical tables for non-ASCII charsets. */
+
+#include "config.h"
+#include "std.h"
+#include "tools.h"
+
+#define CANON_ASCII_NONSGML 255 /* Canonical non-SGML character in ASCII. */
+#define CANON_ASCII_DATACHAR 254 /* Canonical DATACHAR in ASCII. */
+
+extern unsigned char charset[];
+extern UNCH *lextabs[];
+extern UNCH lextran[];
+
+static char *lextabnames[] = {
+ "lexcnm", "lexcon", "lexgrp", "lexlms", "lexmark", "lexsd", "lextoke"
+};
+
+static VOID print_tab(s, t)
+ char *s;
+ UNCH *t;
+{
+ int i;
+ printf("UNCH %s[] = {\n", s);
+ for (i = 0; i < 256; i++)
+ printf("%2d,%c", t[i], (i + 1) % 16 == 0 ? '\n' : ' ');
+ fputs("};\n\n", stdout);
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i;
+ UNCH tab[256];
+ char special[256];
+ /* Shunned character numbers in the reference concrete syntax. */
+ static UNCH refshun[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 255
+ };
+ char shunned[256];
+ char *program_name;
+
+ program_name = strrchr(argv[0], '/');
+ if (program_name)
+ program_name++;
+ else
+ program_name = argv[0];
+
+ /* Check that the mapping is 1-1. */
+ for (i = 0; i < 256; i++)
+ tab[i] = 0;
+ for (i = 0; i < 256; i++)
+ tab[charset[i]] = 1;
+ for (i = 0; i < 256; i++)
+ if (!tab[i]) {
+ fprintf(stderr, "%s: bad mapping: no character mapped to %d\n",
+ program_name, i);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Compute special. */
+ for (i = 0; i < 256; i++)
+ special[i] = 0;
+ for (i = 0; lextabs[i]; i++) {
+ int j;
+ for (j = 0; j < 256; j++)
+ if (lextabs[i][j] != lextabs[i][CANON_ASCII_NONSGML]
+ && lextabs[i][j] != lextabs[i][CANON_ASCII_DATACHAR])
+ special[charset[j]] = 1;
+ }
+
+ /* Compute shunned. */
+ for (i = 0; i < 256; i++)
+ shunned[i] = 0;
+ for (i = 0; i < sizeof(refshun); i++)
+ shunned[refshun[i]] = 1;
+
+ printf("/* This file was automatically generated by %s. Do not edit. */\n\n",
+ program_name);
+ fputs("#include \"config.h\"\n#include \"entity.h\"\n#include \"sgmldecl.h\"\n\n",
+ stdout);
+
+ /* Generate each of the lexical tables. */
+ for (i = 0; lextabs[i]; i++) {
+ int j;
+ for (j = 0; j < 256; j++)
+ tab[charset[j]] = lextabs[i][j];
+
+ for (j = 0; j < 256; j++)
+ if (!special[j]) {
+ if (shunned[j])
+ tab[j] = lextabs[i][CANON_ASCII_NONSGML];
+ else
+ tab[j] = lextabs[i][CANON_ASCII_DATACHAR];
+ }
+ print_tab(lextabnames[i], tab);
+ }
+
+ /* Generate lextran. */
+ for (i = 0; i < 256; i++)
+ tab[charset[i]] = charset[lextran[i]];
+ print_tab("lextran", tab);
+
+ /* Generate asciicharset. */
+ fputs("int asciicharset[] = {\n", stdout);
+ for (i = 0; i < 128; i++)
+ printf("%3d,%c", charset[i], (i + 1) % 16 == 0 ? '\n' : ' ');
+ for (i = 128; i < 256; i++)
+ printf("UNUSED,%c", (i + 1) % 8 == 0 ? '\n' : ' ');
+ fputs("};\n", stdout);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/usr.bin/sgmls/sgmls/getopt.c b/usr.bin/sgmls/sgmls/getopt.c
new file mode 100644
index 0000000..bc8edeb
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/getopt.c
@@ -0,0 +1,166 @@
+/* getopt.c -
+ getopt() for those systems that don't have it.
+
+ Derived from comp.sources.unix/volume3/att_getopt.
+ Modified by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifndef HAVE_GETOPT
+
+#include "std.h"
+#include "getopt.h"
+
+#ifdef SWITCHAR
+#include <dos.h>
+#endif
+
+int opterr = 1;
+int optind = 1;
+int optopt;
+char *optarg;
+
+#ifndef OPTION_CHAR
+#define OPTION_CHAR '-'
+#endif
+
+int getopt(argc, argv, opts)
+int argc;
+char **argv;
+char *opts;
+{
+#ifdef SWITCHAR
+ union REGS regs;
+ static char switchar = '\0';
+#endif
+ static int sp = 1;
+ register int c;
+ register char *cp;
+ char *message;
+#ifdef SWITCHAR
+ if (switchar == '\0') {
+ regs.x.ax = 0x3700;
+ intdos(&regs, &regs);
+ if (!regs.x.cflag)
+ switchar = regs.h.dl;
+ else
+ switchar = '/';
+ }
+#endif
+ if (sp == 1) {
+ if (optind >= argc)
+ return EOF;
+ if ((
+#ifdef SWITCHAR
+ argv[optind][0] != switchar &&
+#endif
+ argv[optind][0] != OPTION_CHAR) || argv[optind][1] == '\0') {
+#ifdef REORDER_ARGS
+ int i;
+ for (i = optind; i < argc; i++)
+ if ((
+#ifdef SWITCHAR
+ argv[i][0] == switchar ||
+#endif
+ argv[i][0] == OPTION_CHAR) && argv[i][1] != '\0')
+ break;
+ if (i < argc) {
+ c = argv[i][1];
+#ifdef CASE_INSENSITIVE_OPTIONS
+ if (isupper(c))
+ c = tolower(c);
+#endif
+ if (c != ':' && c != OPTION_CHAR && (cp = strchr(opts, c)) != NULL
+ && cp[1] == ':' && argv[i][2] == 0 && i < argc - 1) {
+ int j;
+ char *temp1 = argv[i];
+ char *temp2 = argv[i+1];
+ for (j = i - 1; j >= optind; j--)
+ argv[j+2] = argv[j];
+ argv[optind] = temp1;
+ argv[optind+1] = temp2;
+ }
+ else {
+ int j;
+ char *temp = argv[i];
+ for (j = i - 1; j >= optind; j--)
+ argv[j+1] = argv[j];
+ argv[optind] = temp;
+ }
+ }
+ else
+#endif
+ return EOF;
+ }
+ if ((argv[optind][0] == OPTION_CHAR && argv[optind][1] == OPTION_CHAR
+ && argv[optind][2] == '\0')
+#ifdef SWITCHAR
+ || (argv[optind][0] == switchar && argv[optind][1] == switchar
+ && argv[optind][2] == '\0')
+#endif
+ ) {
+ optind++;
+ return(EOF);
+ }
+ }
+ optopt = c = argv[optind][sp];
+#ifdef CASE_INSENSITIVE_OPTIONS
+ if (
+#ifdef USE_ISASCII
+ isascii(c) &&
+#endif /* USE_ISASCII */
+ isupper((unsigned char)c))
+ optopt = c = tolower((unsigned char)c);
+#endif /* CASE_INSENSITIVE_OPTIONS */
+ if (c == ':' || (cp = strchr(opts, c)) == NULL) {
+ if (argv[optind][++sp] == '\0') {
+ optind++;
+ sp = 1;
+ }
+ message = ": illegal option -- ";
+ goto bad;
+ }
+ if (*++cp == ':') {
+ if (argv[optind][sp+1] != '\0')
+ optarg = &argv[optind++][sp+1];
+ else if (++optind >= argc) {
+ sp = 1;
+ message = ": option requires an argument -- ";
+ goto bad;
+ }
+ else
+ optarg = argv[optind++];
+ sp = 1;
+ }
+ else {
+ if (argv[optind][++sp] == '\0') {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return c;
+bad:
+ if (opterr) {
+ fputs(argv[0], stderr);
+ fputs(message, stderr);
+ fputc(optopt, stderr);
+ fputc('\n', stderr);
+ }
+ return '?';
+}
+
+#endif /* not HAVE_GETOPT */
+
+/*
+Local Variables:
+c-indent-level: 4
+c-continued-statement-offset: 4
+c-brace-offset: 4
+c-argdecl-indent: 4
+c-label-offset: -4
+tab-width: 4
+End:
+*/
+
diff --git a/usr.bin/sgmls/sgmls/getopt.h b/usr.bin/sgmls/sgmls/getopt.h
new file mode 100644
index 0000000..4856560
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/getopt.h
@@ -0,0 +1,11 @@
+/* Declare getopt() and associated variables. */
+
+/* Don't use prototypes in case some system header file has a
+conflicting definition. Systems differ on how they declare the second
+parameter. */
+
+extern int getopt();
+
+extern char *optarg;
+extern int optind;
+extern int opterr;
diff --git a/usr.bin/sgmls/sgmls/keyword.h b/usr.bin/sgmls/sgmls/keyword.h
new file mode 100644
index 0000000..6c092f0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/keyword.h
@@ -0,0 +1,22 @@
+/* KEYWORD.H: Definitions for markup declaration keyword processing.
+*/
+/* Default value types for attribute definition list declaration.
+*/
+#define DNULL 1 /* Default value: implied attribute. */
+#define DREQ 2 /* Default value: required attribute. */
+#define DCURR 3 /* Default value: current attribute. */
+#define DCONR 4 /* Default value: content reference attribute. */
+#define DFIXED 5 /* Default value: fixed attribute. */
+
+/* External identifier types for entity and notation declarations.
+*/
+#define EDSYSTEM 1 /* SYSTEM (but not PUBLIC) identifier specified.*/
+#define EDPUBLIC 2 /* PUBLIC (but not SYSTEM) identifier specified.*/
+#define EDBOTH 3 /* PUBLIC and also SYSTEM identifiers specified.*/
+
+/* Marked section keywords.
+*/
+#define MSTEMP 1
+#define MSRCDATA 2
+#define MSCDATA 3
+#define MSIGNORE 4
diff --git a/usr.bin/sgmls/sgmls/latin1.h b/usr.bin/sgmls/sgmls/latin1.h
new file mode 100644
index 0000000..44f43f3
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/latin1.h
@@ -0,0 +1,51 @@
+/* SGML Character Use: ISO Latin 1.
+*/
+#define EOFCHAR '\032' /* FUNCTION: EE (entity end: files). */
+#define EOBCHAR '\034' /* NONCHAR: EOB (file entity: end of buffer. */
+#define RSCHAR '\012' /* FUNCTION: RS (record start). */
+#define RECHAR '\015' /* FUNCTION: RE (record end). */
+#define TABCHAR '\011' /* FUNCTION: TAB (horizontal tab). */
+#define SPCCHAR '\040' /* FUNCTION: SPACE (horizontal space). */
+#define GENRECHAR '\010' /* NONCHAR: Generated RE. */
+#define DELCDATA '\035' /* NONCHAR: Delimiter for CDATA entity in
+ attribute value. */
+#define DELSDATA '\036' /* NONCHAR: Delimiter for SDATA entity in
+ attribute value. */
+#define DELNONCH '\037' /* NONCHAR: non-SGML character prefix. */
+
+/* These two macros are used to handle non-SGML characters. A non-SGML
+by character is represented by a DELNONCH character followed by
+SHIFTNON(original_character). SHIFTNON must transform any character
+in the set 0, EOFCHAR, EOBCHAR, GENRECHAR, DELCDATA, DELSDATA,
+DELNONCH into a character that is not one of the set 0, EOFCHAR,
+EOBCHAR. Furthermore UNSHIFTNON(SHIFTNON(c)) must be equal to c for
+every character c in the former set. */
+/* This is a simple definition that works for ASCII-like character sets. */
+#define SHIFTNON(ch) ((UNCH)(ch) | 0100)
+#define UNSHIFTNON(ch) ((UNCH)(ch) & ~0100)
+
+/* A canonical NONSGML character. The character number that is shunned
+in the reference concrete syntax and is not the number of a
+significant (in the reference concrete syntax) character nor one of
+the above characters nor 0. */
+#define CANON_NONSGML 255
+
+/* A canonical DATACHAR character. The character number that is not
+shunned in the reference concrete syntax and is not the number of a
+significant (in the reference concrete syntax) SGML character nor one
+of the above characters. */
+#define CANON_DATACHAR 254
+
+/* Components for a formal public identifier for the whole of the
+system character set. Protect with ifndef so that it can be overriden
+in config.h. */
+
+#ifndef SYSTEM_CHARSET_DESIGNATING_SEQUENCE
+#define SYSTEM_CHARSET_DESIGNATING_SEQUENCE "ESC 2/13 4/1"
+#endif
+#ifndef SYSTEM_CHARSET_OWNER
+#define SYSTEM_CHARSET_OWNER "ISO Registration Number 100"
+#endif
+#ifndef SYSTEM_CHARSET_DESCRIPTION
+#define SYSTEM_CHARSET_DESCRIPTION "ECMA-94 Right Part of Latin Alphabet Nr. 1"
+#endif
diff --git a/usr.bin/sgmls/sgmls/lexcode.h b/usr.bin/sgmls/sgmls/lexcode.h
new file mode 100644
index 0000000..e4047ba
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lexcode.h
@@ -0,0 +1,11 @@
+/* Definitions of lexical codes needed by both lextaba.c and lexrf.c. */
+
+#define FCE 27 /* FRE Free character in use as an entity reference */
+#define FRE 0 /* FREECHAR that is not in a CON delimiter-in-context. */
+#define LITC 21 /* LIT LITA PIC or EE in use as a literal terminator */
+#define MSC3 15 /* ] Also MSC[2]. */
+#define NET 17 /* / When enabled. */
+#define ETI 16 /* / Actually ETAGO[2] */
+#define SPCR 19 /* Space in use as SR8. */
+#define TGO2 25 /* < TAGO; also MDO[1], PIO[1] */
+#define CDE 11 /* NONSGML delcdata CDATA/SDATA delimiter */
diff --git a/usr.bin/sgmls/sgmls/lexrf.c b/usr.bin/sgmls/sgmls/lexrf.c
new file mode 100644
index 0000000..ec3db83
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lexrf.c
@@ -0,0 +1,124 @@
+/* LEXRF: Lexical tables for reference concrete syntax.
+*/
+
+#include "config.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "synxtrn.h" /* Declarations for concrete syntax constants. */
+#include "action.h" /* Action names for all parsing. */
+#include "lexcode.h"
+
+static UNCH SRTAB[] = { TABCHAR, '\0' };
+static UNCH SRRE[] = { RECHAR, '\0' };
+static UNCH SRRS[] = { RSCHAR, '\0' };
+static UNCH SRRSB[] = { RSCHAR, 'B', '\0' };
+static UNCH SRRSRE[] = { RSCHAR, RECHAR, '\0' };
+static UNCH SRRSBRE[] = { RSCHAR, 'B', RECHAR, '\0' };
+static UNCH SRBRE[] = { 'B', RECHAR, '\0' };
+
+struct lexical lex = { /* Delimiter set constants for parser use. */
+ { /* Markup strings for text processor use. */
+ (UNCH *)"\4&#", /* LEXCON markup string: CRO */
+ (UNCH *)"[", /* LEXCON markup string: DSO */
+ (UNCH *)"\3&", /* LEXCON markup string: ERO */
+ (UNCH *)"\4</", /* LEXMARK markup string: end-tag */
+ (UNCH *)"\3\"", /* LEXMARK markup string: LIT */
+ (UNCH *)"\3'", /* LEXMARK markup string: LITA */
+ (UNCH *)"\3>", /* LEXCON markup string: MDC */
+ (UNCH *)"\4<!", /* LEXCON markup string: MDO */
+ (UNCH *)"\5]]>", /* LEXCON markup string: mse */
+ (UNCH *)"\5<![", /* LEXCON markup string: mss */
+ (UNCH *)"\13<![CDATA[", /* LEXCON markup string: mss CDATA */
+ (UNCH *)"\14<![RCDATA[", /* LEXCON markup string: mss RCDATA */
+ (UNCH *)"\3>", /* LEXCON markup string: PIC */
+ (UNCH *)"\4<?", /* LEXCON markup string: PIO */
+ (UNCH *)"\3;", /* LEXGRP markup string: ref close. */
+ (UNCH *)"\3<", /* LEXMARK markup string: start-tag */
+ (UNCH *)"\3>", /* LEXMARK markup string: TAGC */
+ (UNCH *)"\3=", /* LEXMARK markup string: VI */
+ 3, /* LEXMARK: length of null end-tag. */
+ 2 /* LEXMARK: length of null start-tag. */
+ },
+ { /* Short reference delimiters. */
+ { /* Short reference delimiter table. */
+ {(UNCH *)"", SRCT}, /* Dummy entry to store SR count. */
+ {SRTAB, 1}, /* TAB */
+ {SRRE, 2}, /* RE */
+ {SRRS, 3}, /* RS */
+ {SRRSB, 4}, /* Leading blanks */
+ {SRRSRE, 5}, /* Null record */
+ {SRRSBRE, 6}, /* Blank record */
+ {SRBRE, 7}, /* Trailing blanks */
+ {(UNCH *)" ", 8}, /* Space */
+ {(UNCH *)"BB", 9}, /* Two or more blanks */
+ {(UNCH *)"\"", 10}, /* Quotation mark (first data character) */
+ {(UNCH *)"#", 11}, /* Number sign */
+ {(UNCH *)"%", 12}, /* FCE CHARACTERS start here */
+ {(UNCH *)"'", 13},
+ {(UNCH *)"(", 14},
+ {(UNCH *)")", 15},
+ {(UNCH *)"*", 16},
+ {(UNCH *)"+", 17},
+ {(UNCH *)",", 18},
+ {(UNCH *)"-", 19}, /* Hyphen */
+ {(UNCH *)"--", 20}, /* Two hyphens */
+ {(UNCH *)":", 21},
+ {(UNCH *)";", 22},
+ {(UNCH *)"=", 23},
+ {(UNCH *)"@", 24},
+ {(UNCH *)"[", 25},
+ {(UNCH *)"]", 26},
+ {(UNCH *)"^", 27},
+ {(UNCH *)"_", 28}, /* Low line */
+ {(UNCH *)"{", 29},
+ {(UNCH *)"|", 30},
+ {(UNCH *)"}", 31},
+ {(UNCH *)"~", 32},
+ {0, 0}
+ },
+ { /* Printable form of unprintable SR delims.*/
+ "", /* Dummy entry to balance s.dtb. */
+ "&#TAB;", /* TAB */
+ "&#RE;", /* RE */
+ "&#RS;", /* RS */
+ "&#RS;B", /* Leading blanks */
+ "&#RS;&#RE;", /* Null record */
+ "&#RS;B&#RE;", /* Blank record */
+ "B&#RE;", /* Trailing blanks */
+ "&#SPACE;" /* Space */
+ },
+ 12, /* LEXCNM: Index of first FCE in srdeltab. */
+ 20, /*LEXCNM:Index of "two hyphens" in srdeltab*/
+ 10, /* LEXCNM: Index of first SR with data char. */
+ 19, /* LEXCNM: Index of hyphen in srdeltab. */
+ SRNPRT+1, /* LEXCNM: Index of 1st printable SR. */
+ 8, /* LEXCNM: Index of space in srdeltab. */
+ 25, /* LEXCNM: Index of left bracket in srdeltab. */
+ 26, /* LEXCNM: Index of right bracket in srdeltab. */
+ }, /* End of short reference delimiters. */
+ { /* General delimiter characters. */
+ GENRECHAR, /*LEXCNM:(BS)Generated RE; can't be markup.*/
+ '"', /* LEXMARK: Char used as LIT delimiter.*/
+ '\'', /* LEXMARK: Char used as LITA delimiter.*/
+ '>', /* LEXLMS: Char used as MDC delimiter.*/
+ ']', /* LEXLMS: Char used as MSC when enabled.*/
+ '/', /* LEXCON: Char used as NET when enabled.*/
+ '%', /* LEXMARK: Char used as PERO delimiter. */
+ '>', /* LEXCON: Char used as PIC delimiter.*/
+ '<' /* LEXCON: Char used as TAGO when enabled.*/
+ },
+ { /* Lexical table code assignments. */
+ FCE, /* LEXCNM: FRE char as entity reference.*/
+ FRE, /* LEXLMS: Free character not an entity ref.*/
+ LITC, /* LEXLMS: Literal close delimiter enabled. */
+ MSC3, /* LEXLMS: Marked section close delim enabled. */
+ NET, /* LEXCON: Null end-tag delimiter enabled. */
+ ETI, /* LEXCON: NET disabled; still used as ETI. */
+ SPCR, /* LEXCNM: Space in use as SHORTREF delim. */
+ TGO2, /* LEXCON: Tag open delimiter enabled. */
+ CDE /* LEXLMS: CDATA/SDATA delimiters. */
+ }
+};
+
+UNCH *lextabs[] = {
+ lexcnm, lexcon, lexgrp, lexlms, lexmark, lexsd, lextoke, 0
+};
diff --git a/usr.bin/sgmls/sgmls/lextaba.c b/usr.bin/sgmls/sgmls/lextaba.c
new file mode 100644
index 0000000..38a2fd1
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lextaba.c
@@ -0,0 +1,559 @@
+/* lextaba.c: lexical tables for ASCII. */
+
+/* These tables are munged by setnonsgml(). */
+
+#include "config.h"
+#include "entity.h"
+#include "lexcode.h"
+#include "sgmldecl.h"
+
+/* LEXCNM: Lexical table for mixed content (PCBCONM) parse.
+*/
+/* Symbols for SGML character set divisions and function characters. */
+#define NU 1 /* NUMERAL Numerals */
+#define NMC 2 /* LC/UCNMCHAR . - Period and hyphen */
+#define NMS 3 /* LC/UCNMSTRT Lower and uppercase letters */
+#define SPC 4 /* SPACE 32 Space */
+#define NON 5 /* NONSGML 0-31 127 255 Unused, except for: */
+#define EE 6 /* NONSGML 00 26 Entity end (end of file) */
+#define EOB 7 /* NONSGML 28 End disk buffer */
+#define RS 8 /* Function 10 Line feed */
+#define RE 9 /* Function 13 Carrier return */
+#define SEP 10 /* SEPCHAR 09 TAB: horizontal tab */
+#define NSC 12 /* NONSGML delnonch Non-SGML character prefix */
+
+/* Symbols for SGML delimiter roles in CON and CXT.
+ ETI and NET must be the same in LEXCNM and LEXCON.
+ FRE characters are changed to FCE if an FCE entity is declared.
+ They are changed back to FRE when the entity is canceled.
+*/
+#define ERO 13 /* & Also CRO[1] */
+#define NMRE 14 /* 08 Generated non-markup RE */
+#define COM 15 /* - For MDO context; also SR19 and SR20. */
+#undef LIT1
+#define LIT1 18 /* " SR10 */
+#define MDO 20 /* ! Actually MDO[2] */
+#define MSC1 21 /* ] Both MSC[1] and MSC[2]; also SR26. */
+#define MSO 22 /* [ For MDO context; also SR25. */
+#define PIO 23 /* ? Actually PIO[2] */
+#define RNI 24 /* # For CRO[2]; also SR11. */
+#define TGC1 25 /* > For TAGO and MSC context; also MDC, PIC */
+#define TGO1 26 /* < TAGO; also MDO[1], PIO[1] */
+
+UNCH lexcnm[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE, NON, NON, NON, NON, NON, NON, NON, NMRE,SEP, RS, NON, NON, RE, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE, NON, EOB, NON, NON, NSC, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, MDO, LIT1,RNI, FRE, FRE ,ERO, FRE, FRE, FRE, FRE, FRE, FRE, COM, NMC, ETI, /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU , NU , NU , NU , NU , NU , NU , NU , NU , NU , FRE, FRE, TGO1,FRE, TGC1,PIO, /*
+@ A B C D E F G H I J K L M N O */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, MSO, FRE, MSC1,FRE, FRE, /*
+` a b c d e f g h i j k l m n o */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, FRE, FRE, FRE, FRE, NON,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, NON
+};
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti lit spcr mdo msc mso net pio rni tagc tago fce */
+#undef ERO
+#undef NMRE
+#undef COM
+#undef LIT1
+/* def SPCR*/
+#undef MDO
+#undef MSC1
+#undef MSO
+#undef PIO
+#undef RNI
+#undef TGC1
+/* def TGO1*/
+/* def FCE*/
+/* LEXCON: Lexical table for RCDATA and CDATA content (PCBCON?),
+ prolog (PCBPRO), and nested declaration set (PCBMDS) parses.
+ Note: NMC is same as FRE; kept for consistency with LEXCNM and LEXLMS.
+*/
+/* Symbols for SGML character set divisions and function characters. */
+/* Same as for LEXCNM. */
+
+/* Symbols for SGML delimiter roles in CON, CXT, and DS.
+ ETI and NET must be the same in LEXCNM and LEXCON.
+ FRE characters are changed to FCE if an FCE entity is declared.
+ They are changed back to FRE when the entity is canceled.
+*/
+#define ERO 13 /* & Also CRO[1] */
+#define NMRE 14 /* 08 Generated non-markup RE */
+#define COM 15 /* - For MDO context. */
+/*#define ETI 16 / Actually ETAGO[2] */
+/*#define NET 17 / When enabled. */
+#define MDO 18 /* ! Actually MDO[2] */
+#define MSC2 19 /* ] Both MSC[1] and MSC[2]. */
+#define MSO 20 /* [ For MDO context. */
+#define PERO 21 /* % For prolog */
+#define PIO 22 /* ? Actually PIO[2] */
+#define RNI 23 /* # For CRO[2]. */
+#define TGC2 24 /* > For TAGO and MSC context; also MDC, PIC */
+
+UNCH lexcon[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE, NON, NON, NON, NON, NON, NON, NON, NMRE,SEP, RS, NON, NON, RE, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE, NON, EOB, NON, NON, NSC, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, MDO, FRE, RNI, FRE, PERO,ERO, FRE, FRE, FRE, FRE, FRE, FRE, COM, NMC, ETI, /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU , NU , NU , NU , NU , NU , NU , NU , NU , NU , FRE, FRE, TGO2,FRE, TGC2,PIO, /*
+@ A B C D E F G H I J K L M N O */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, MSO, FRE, MSC2,FRE, FRE, /*
+` a b c d e f g h i j k l m n o */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, FRE, FRE, FRE, FRE, NON,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, NON
+};
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+#undef FRE
+#undef NU
+#undef NMC
+#undef NMS
+#undef SPC
+#undef NON
+#undef EE
+#undef EOB
+#undef RS
+#undef RE
+#undef SEP
+#undef NSC
+#undef ERO
+#undef NMRE
+#undef COM
+/* def ETI*/
+/* def NET*/
+#undef MDO
+#undef MSC2
+#undef MSO
+#undef PERO
+#undef PIO
+#undef RNI
+#undef TGC2
+/* LEXGRP: Lexical table for group parses, including PCBREF.
+*/
+/* Symbols for SGML character set divisions. */
+#define BIT 0 /* Bit combinations (not NONCHAR) not allowed in a group. */
+#define NMC 1 /* NAMECHAR . - Period, underscore, and numerals */
+#define NMS 2 /* NAMESTRT Lower and uppercase letters */
+#define RE 3 /* Function 13 Carrier return */
+#define SPC 4 /* SPACE 32 09 Space; includes TAB */
+#define NON 5 /* NONCHAR 0-31 127 255 Unused, except for: */
+#define EE 6 /* Function 26 00 EE: entity end (end of file) */
+#define EOB 7 /* NONCHAR 28 End disk buffer. */
+#define RS 8 /* Function 10 RS: record start (line feed) */
+
+/* Symbols for SGML delimiter roles in GRP. */
+#define AND1 9 /* & */
+#define GRPC 10 /* ) */
+#define GRPO 11 /* ( */
+#undef LIT2
+#define LIT2 12 /* " For datatags. */
+#define LITA 13 /* ' For datatags. */
+#define DTGC 14 /* ] For datatags. */
+#define DTGO 15 /* [ For datatags. */
+#define OPT1 16 /* ? */
+#define OR1 17 /* | */
+#define PERO 18 /* % */
+#define PLUS 19 /* + */
+#define REP1 20 /* * */
+#define RNI 21 /* # For #CHARS */
+#define SEQ1 22 /* , */
+#define REFC 23 /* ; For references */
+
+UNCH lexgrp[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE , NON, NON, NON, NON, NON, NON, NON, NON, SPC, RS, NON, NON, RE, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE , NON, EOB, NON, NON, NON, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, BIT, LIT2,RNI, BIT, PERO,AND1,LITA,GRPO,GRPC,REP1,PLUS,SEQ1,NMC, NMC, BIT, /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NMC, NMC, NMC, NMC, NMC, NMC, NMC, NMC, NMC, NMC, BIT, REFC,BIT, BIT, BIT, OPT1,/*
+@ A B C D E F G H I J K L M N O */
+BIT, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, DTGO,BIT, DTGC,BIT, BIT, /*
+` a b c d e f g h i j k l m n o */
+BIT, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, BIT, OR1, BIT, BIT, NON,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, NON
+};
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+#undef BIT
+#undef NMC
+#undef NMS
+#undef RE
+#undef SPC
+#undef NON
+#undef EE
+#undef EOB
+#undef RS
+#undef AND1
+#undef GRPC
+#undef GRPO
+#undef LIT2
+#undef LITA
+#undef DTGC
+#undef DTGO
+#undef OPT1
+#undef OR1
+#undef PERO
+#undef PLUS
+#undef REP1
+#undef RNI
+#undef SEQ1
+#undef REFC
+/* LEXLMS: Lexical table for literal parses and marked sections.
+*/
+/* Symbols for SGML character set divisions and function characters.
+*/
+#define FRE 0 /* Free char: not in a delimiter or minimum literal. */
+#define NU 1 /* Numeral Numerals */
+#undef MIN
+#define MIN 2 /* Minimum literal '()+,-./:?= */
+#define NMS 3 /* LC/UCNMSTRT Lower and uppercase letters */
+#define SPC 4 /* SPACE 32 Space */
+#define NON 5 /* NONSGML 0-31 127 255 Unused, except for: */
+#define EE 6 /* NONSGML 00 26 Entity end (end of file) */
+#define EOB 7 /* NONSGML 28 End disk buffer */
+#define RS 8 /* Function 10 Line feed */
+#define RE 9 /* Function 13 Carrier return */
+#define SEP 10 /* SEPCHAR 09 TAB: horizontal tab */
+/*#define CDE 11 NONSGML delcdata CDATA/SDATA delimiter */
+#define NSC 12 /* NONSGML delnonch Non-SGML character prefix */
+/* Symbols for SGML delimiter roles in LIT, PI, and marked sections.
+ Either LIT, LITA, PIC, or EE, is changed to LITC when a literal is begun.
+ It is changed back when the LITC occurs (i.e., when the literal ends).
+*/
+#define ERO 13 /* & */
+#define MDO 14 /* ! Actually MDO[2] */
+#define MSO 16 /* [ For MDO context. */
+#define PERO 17 /* % For prolog. */
+#define RNI 18 /* # For CRO[2] */
+#define TGC3 19 /* > Also MDC for MSC context. */
+#define TGO3 20 /* < TAGO; also MDO[1] */
+
+/* Room has been left in the parse tables in case re-parsing of text
+ is eventually supported (i.e., saved parsed text is used by the
+ application to create a new SGML document, but CDATA and SDATA
+ entities in literals, and non-SGML characters, are left in their
+ parsed state to avoid the overhead of reconstituting the original
+ markup). In such a case, the two non-SGML characters DELCDATA and
+ DELSDATA are changed to CDE.
+ NOTE: The idea is a bad one, because the generated document would
+ be non-conforming, as it would contain non-SGML characters.
+*/
+UNCH lexlms[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE, NON, NON, NON, NON, NON, NON, NON, NON ,SEP, RS, NON, NON, RE, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE, NON, EOB, NON, NON, NSC, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, MDO, FRE, RNI, FRE, PERO,ERO, MIN, MIN, MIN, FRE, MIN, MIN, MIN, MIN, MIN, /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU , NU , NU , NU , NU , NU , NU , NU , NU , NU , MIN, FRE, TGO3,MIN, TGC3,MIN, /*
+@ A B C D E F G H I J K L M N O */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, MSO, FRE, MSC3,FRE, FRE, /*
+` a b c d e f g h i j k l m n o */
+FRE, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, FRE, FRE, FRE, FRE, NON,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE,
+FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, FRE, NON
+};
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tago tagc litc */
+/* def FRE*/
+#undef NU
+#undef MIN
+#undef NMS
+#undef SPC
+#undef NON
+#undef EE
+#undef EOB
+#undef RS
+#undef RE
+#undef SEP
+/* def CDE*/
+/* def NSC*/
+#undef ERO
+#undef MDO
+/* def MSC3*/
+#undef MSO
+#undef PERO
+#undef RNI
+#undef TGC3
+#undef TGO3
+/* def LITC*/
+/* LEXMARK: Lexical scan table for markup: PCBMD? and PCB?TAG.
+*/
+/* Symbols for SGML character set divisions. */
+#define BIT 0 /* Bit combinations not allowed; includes ESC SO SI */
+#define NMC 1 /* NAMECHAR . _ Period and underscore */
+#define NU 2 /* NUMERAL Numerals */
+#define NMS 3 /* NAMESTRT Lower and uppercase letters */
+#define SPC 4 /* SPACE 32 13 09 Space; includes RE TAB */
+#define NON 5 /* NONCHAR 0-31 127 255 Unused, except for: */
+#define EE 6 /* Function 26 00 EE: entity end (end of file) */
+#define EOB 7 /* NONCHAR 28 End disk buffer. */
+#define RS 8 /* Function 10 RS: record start (line feed) */
+
+/* Symbols for SGML delimiter roles in MD and TAG. */
+#define COM1 9 /* - Actually COM[1]; also COM[2], MINUS. */
+#define ETIB 10 /* / ETI; actually ETAGO[2]. */
+#define GRPO 11 /* ( */
+#define LIT3 12 /* " */
+#define LITA 13 /* ' */
+#define DSO 14 /* [ */
+#define DSC1 15 /* ] For data attribute specifications */
+#define PERO 16 /* % */
+#define PLUS 17 /* + */
+#define REFC 18 /* ; For references */
+#define RNI 19 /* # Also CRO[2] */
+#define TGC4 20 /* > Also MDC, PIC */
+#define TGO4 21 /* < TAGO; also MDO[1] */
+#define VI 22 /* = */
+
+UNCH lexmark[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE , NON, NON, NON, NON, NON, NON, NON, NON, SPC, RS, NON, NON, SPC, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE , NON, EOB, NON, NON, NON, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, BIT, LIT3,RNI, BIT, PERO,BIT, LITA,GRPO,BIT, BIT, PLUS,BIT, COM1,NMC ,ETIB,/*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU, NU, NU, NU, NU, NU, NU, NU, NU, NU, BIT, REFC,TGO4,VI, TGC4,BIT, /*
+@ A B C D E F G H I J K L M N O */
+BIT, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, DSO, BIT, DSC1, BIT, BIT, /*
+` a b c d e f g h i j k l m n o */
+BIT, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, BIT, BIT, BIT, BIT, NON,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT,
+BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, BIT, NON
+};
+/* bit nmc nu nms spc non ee eob rs com eti grpo lit lita
+ dso pero plus refc rni tagc tago vi */
+#undef BIT
+#undef NMC
+#undef NU
+#undef NMS
+#undef SPC
+#undef NON
+#undef EE
+#undef EOB
+#undef RS
+#undef COM1
+#undef ETIB
+#undef GRPO
+#undef LIT3
+#undef LITA
+#undef DSO
+#undef DSC
+#undef PERO
+#undef PLUS
+#undef REFC
+#undef RNI
+#undef TGC4
+#undef TGO4
+#undef VI
+/* LEXSD: Lexical scan table for SGML declaration.
+*/
+
+/* Symbols for SGML character set divisions. */
+#define SIG 0 /* Significant SGML characters. */
+#define DAT 1 /* DATACHAR Not significant, and not non-sgml. */
+#define NU 2 /* NUMERAL Numerals */
+#define NMS 3 /* NAMESTRT Lower and uppercase letters */
+#define SPC 4 /* SPACE 32 13 09 Space; includes RE TAB */
+#define NON 5 /* NONCHAR NONSGML */
+#define EE 6 /* Function 26 00 EE: entity end (end of file) */
+#define EOB 7 /* NONCHAR 28 End disk buffer. */
+#define RS 8 /* Function 10 RS: record start (line feed) */
+/* Symbols for SGML delimiter roles in SGML declaration. */
+#define COM1 9 /* - Actually COM[1]; also COM[2]. */
+#define LIT3 10 /* " */
+#define LITA 11 /* ' */
+#define TGC4 12 /* > Also MDC, PIC */
+
+UNCH lexsd[256] = { /*
+000 001       bs tab lf home ff cr so si */
+EE , NON, NON, NON, NON, NON, NON, NON, NON, SPC, RS, NON, NON, SPC, NON, NON, /*
+          eof esc rt left up down */
+NON, NON, NON, NON, NON, NON, NON, NON, NON, NON, EE , NON, EOB, NON, NON, NON, /*
+032 ! " # $ % & ' ( ) * + , - . / */
+SPC, SIG, LIT3,SIG, DAT, SIG ,SIG, LITA,SIG, SIG, SIG, SIG, SIG, COM1,SIG ,SIG,/*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU, NU, NU, NU, NU, NU, NU, NU, NU, NU, SIG, SIG, SIG, SIG, TGC4,SIG, /*
+@ A B C D E F G H I J K L M N O */
+SIG, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, SIG, DAT, SIG, SIG, SIG, /*
+` a b c d e f g h i j k l m n o */
+DAT, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, SIG, SIG, SIG, SIG, NON,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT,
+DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, DAT, NON
+};
+
+#undef SIG
+#undef DAT
+#undef NON
+#undef NU
+#undef NMS
+#undef SPC
+#undef EE
+#undef EOB
+#undef RS
+#undef COM1
+#undef LIT3
+#undef LITA
+#undef TGC4
+
+/* LEXTRAN: Translation table for SGML names.
+*/
+UNCH lextran[256] = { /*
+000 001       bs tab lf home ff cr so si */
+0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , /*
+          eof esc rt left up down */
+16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , /*
+space! " # $ % & ' ( ) * + , - . / */
+32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 , /*
+@ A B C D E F G H I J K L M N O */
+64 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 93 , 94 , 95 , /*
+` a b c d e f g h i j k l m n o */
+96 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , /*
+p q r s t u v w x y z { | } ~ 127 */
+80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 123, 124, 125, 126, 127,
+128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+};
+/* LEXTOKE: Lexical class table for tokenization scan.
+*/
+#include "lextoke.h" /* Symbols for tokenization lexical classes. */
+UNCH lextoke[256] = { /*
+
+000 001       bs tab lf home ff cr   */
+INV, INV, INV, INV, INV, INV, INV, INV, INV, SEP, REC, INV, INV, REC, INV, INV, /*
+          eof esc rt left up down */
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, EOB, INV, INV, INV, /*
+space! " # $ % & ' ( ) * + , - . / */
+SP , INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, NMC, NMC, INV, /*
+0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
+NU , NU , NU , NU , NU , NU , NU , NU , NU , NU , INV, INV, INV, INV, INV, INV, /*
+@ A B C D E F G H I J K L M N O */
+INV, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+P Q R S T U V W X Y Z [ \ ] ^ _ */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, INV, INV, INV, INV, INV, /*
+` a b c d e f g h i j k l m n o */
+INV, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, /*
+p q r s t u v w x y z { | } ~ 127 */
+NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, NMS, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV,
+INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV, INV
+};
+
+/* This table maps ASCII to the system character set. */
+int asciicharset[] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+};
diff --git a/usr.bin/sgmls/sgmls/lextabe.c b/usr.bin/sgmls/sgmls/lextabe.c
new file mode 100644
index 0000000..f93af89
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lextabe.c
@@ -0,0 +1,184 @@
+/* This file was automatically generated by genlex. Do not edit. */
+
+#include "config.h"
+#include "entity.h"
+#include "sgmldecl.h"
+
+UNCH lexcnm[] = {
+ 6, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 9, 5, 5,
+ 5, 5, 5, 5, 5, 5, 14, 5, 5, 5, 5, 5, 7, 5, 5, 12,
+ 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 26, 0, 0, 0,
+13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0,
+15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 23,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 18,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 22, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5,
+};
+
+UNCH lexcon[] = {
+ 6, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 9, 5, 5,
+ 5, 5, 5, 5, 5, 5, 14, 5, 5, 5, 5, 5, 7, 5, 5, 12,
+ 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 25, 0, 0, 0,
+13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0,
+15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 24, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 20, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5,
+};
+
+UNCH lexgrp[] = {
+ 6, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 3, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 5, 5, 5,
+ 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 11, 19, 17,
+ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 10, 23, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 18, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 13, 0, 12,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 15, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5,
+};
+
+UNCH lexlms[] = {
+ 6, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 9, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 5, 5, 12,
+ 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 20, 2, 2, 0,
+13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 2, 0, 0,
+ 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 17, 0, 19, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 18, 0, 2, 2, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 16, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5,
+};
+
+UNCH lexmark[] = {
+ 6, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 5, 5, 5,
+ 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21, 11, 17, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0,
+ 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 20, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 13, 22, 12,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 14, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 5,
+};
+
+UNCH lexsd[] = {
+ 6, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 5, 5, 5,
+ 1, 1, 1, 1, 1, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6,
+ 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1,
+ 9, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 12, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 11, 0, 10,
+ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1,
+ 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 0, 1, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1,
+ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 5,
+};
+
+UNCH lextoke[] = {
+ 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0,
+};
+
+UNCH lextran[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+128, 193, 194, 195, 196, 197, 198, 199, 200, 201, 138, 139, 140, 141, 142, 143,
+144, 209, 210, 211, 212, 213, 214, 215, 216, 217, 154, 155, 156, 157, 158, 159,
+160, 161, 226, 227, 228, 229, 230, 231, 232, 233, 170, 171, 172, 173, 174, 175,
+176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+};
+
+int asciicharset[] = {
+ 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
+ 64, 90, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111,
+124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
+215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 173, 224, 189, 176, 109,
+121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
+151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 79, 208, 161, 7,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED,
+};
diff --git a/usr.bin/sgmls/sgmls/lextoke.h b/usr.bin/sgmls/sgmls/lextoke.h
new file mode 100644
index 0000000..d2bcfa0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lextoke.h
@@ -0,0 +1,10 @@
+/* LEXTOKE.H: Symbols for tokenization lexical classes.
+*/
+#define INV 0 /* Invalid Chars Not allowed in an SGML name. */
+#define REC 1 /* Record Boundary RS and RE. */
+#define SEP 2 /* Separator TAB. */
+#define SP 3 /* SPACE */
+#define NMC 4 /* NAMECHAR . _ Period, underscore (plus NMS, NUM). */
+#define NMS 5 /* NAMESTRT Lower and uppercase letters */
+#define NU 6 /* NUMERAL Numerals */
+#define EOB 7 /* NONCHAR 28 End disk buffer. */
diff --git a/usr.bin/sgmls/sgmls/lineout.c b/usr.bin/sgmls/sgmls/lineout.c
new file mode 100644
index 0000000..553c835
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lineout.c
@@ -0,0 +1,653 @@
+/* lineout.c -
+ Implements line-oriented output format.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+#include "std.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "adl.h" /* Definitions for attribute list processing. */
+#include "sgmlmain.h" /* Main interface to SGML services. */
+#include "lineout.h"
+#include "appl.h"
+
+static VOID flush_data P((void));
+static VOID define_external_entity P((PNE));
+static VOID define_entity P((UNCH *));
+static VOID handle_attributes P((UNCH *, struct ad *));
+static VOID handle_token_list P((UNCH *, struct ad *, int));
+static VOID handle_single_token P((UNCH *, struct ad *, int));
+static VOID output_notation P((UNCH *, UNCH *, UNCH *));
+static VOID output_internal_entity P((UNCH *, int, UNCH *));
+static VOID output_external_entity P((UNCH *, int, UNIV, UNCH *, UNCH *,
+ UNCH *));
+static VOID output_subdoc P((UNCH *, UNIV, UNCH *, UNCH *));
+#ifdef SUPPORT_SUBDOC
+static VOID process_subdoc P((UNCH *, UNIV));
+#endif /* SUPPORT_SUBDOC */
+static VOID output_record_end P((void));
+static VOID output_pcdata P((UNS, UNCH *));
+static VOID output_cdata P((UNS, UNCH *));
+static VOID output_sdata P((UNS, UNCH *));
+static VOID output_entity_reference P((UNCH *));
+static VOID output_start_tag P((UNCH *));
+static VOID output_end_tag P((UNCH *));
+static VOID output_processing_instruction P((UNS, UNCH *));
+static VOID output_implied_attribute P((UNCH *, UNCH *));
+static char *attribute_type_string P((int));
+static VOID output_begin_attribute P((UNCH *, UNCH *, int));
+static VOID output_attribute_token P((UNS, UNCH *));
+static VOID output_end_attribute P((void));
+static VOID print_data P((UNS, UNCH *, int));
+static VOID print_string P((UNS, UNCH *, int));
+static VOID print_id P((UNIV, UNCH *, UNCH *));
+static VOID print_filename P((char *));
+static VOID output_location P((void));
+static VOID output_appinfo P((UNS, UNCH *));
+
+static int have_data = 0;
+static char *current_filename = 0;
+static unsigned long current_lineno = 0;
+
+VOID process_document(subdocsw)
+int subdocsw;
+{
+ enum sgmlevent rc;
+ struct rcbtag rcbtag;
+ struct rcbdata rcbdaf;
+
+ while ((rc = sgmlnext(&rcbdaf, &rcbtag)) != SGMLEOD) {
+#ifdef SUPPORT_SUBDOC
+ if (rc == SGMLDAF && !CONTERSW(rcbdaf) && NDESW(rcbdaf)
+ && NEXTYPE(NEPTR(rcbdaf)) == ESNSUB) {
+ if (!suppsw && !sgmlment(NEENAME(NEPTR(rcbdaf))))
+ define_external_entity(NEPTR(rcbdaf));
+ process_subdoc(NEENAME(NEPTR(rcbdaf)) + 1,
+ NEID(NEPTR(rcbdaf)));
+ continue;
+ }
+#endif /* SUPPORT_SUBDOC */
+ if (!suppsw)
+ switch (rc) {
+ case SGMLDAF:
+ if (CONTERSW(rcbdaf))
+ break;
+ if (CDESW(rcbdaf))
+ output_cdata(CDATALEN(rcbdaf), CDATA(rcbdaf));
+ else if (SDESW(rcbdaf))
+ output_sdata(CDATALEN(rcbdaf), CDATA(rcbdaf));
+ else if (NDESW(rcbdaf)) {
+ assert(NEXTYPE(NEPTR(rcbdaf)) != ESNSUB);
+ if (!sgmlment(NEENAME(NEPTR(rcbdaf))))
+ define_external_entity(NEPTR(rcbdaf));
+ output_entity_reference(NEENAME(NEPTR(rcbdaf)) + 1);
+ }
+ else
+ output_pcdata(CDATALEN(rcbdaf), CDATA(rcbdaf));
+ break;
+ case SGMLSTG:
+ if (CONTERSW(rcbtag))
+ break;
+ if (ALPTR(rcbtag))
+ handle_attributes((UNCH *)NULL, ALPTR(rcbtag));
+ output_start_tag(CURGI(rcbtag));
+ break;
+ case SGMLETG:
+ if (CONTERSW(rcbtag))
+ break;
+ output_end_tag(CURGI(rcbtag));
+ break;
+ case SGMLPIS:
+ if (CONTERSW(rcbdaf))
+ break;
+ output_processing_instruction(PDATALEN(rcbdaf),
+ PDATA(rcbdaf));
+ break;
+ case SGMLREF:
+ if (CONTERSW(rcbdaf))
+ break;
+ output_record_end();
+ break;
+ case SGMLAPP:
+ if (CONTERSW(rcbdaf))
+ break;
+ if (!subdocsw)
+ output_appinfo(ADATALEN(rcbdaf), ADATA(rcbdaf));
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+/* Output an indication that the document was conforming. */
+
+VOID output_conforming()
+{
+ if (!suppsw)
+ printf("%c\n", CONFORMING_CODE);
+}
+
+static VOID define_external_entity(p)
+PNE p;
+{
+ if (NEXTYPE(p) == ESNSUB)
+ output_subdoc(NEENAME(p) + 1, NEID(p), NEPUBID(p), NESYSID(p));
+ else {
+ if (!NEDCNMARK(p))
+ output_notation(NEDCN(p) + 1, NEDCNPUBID(p), NEDCNSYSID(p));
+ output_external_entity(NEENAME(p) + 1, NEXTYPE(p), NEID(p),
+ NEPUBID(p), NESYSID(p), NEDCN(p) + 1);
+ if (NEAL(p))
+ handle_attributes(NEENAME(p) + 1, NEAL(p));
+ }
+}
+
+static VOID define_entity(ename)
+UNCH *ename;
+{
+ int rc;
+ PNE np;
+ UNCH *tp;
+
+ if (sgmlment(ename)) /* already defined it */
+ return;
+ rc = sgmlgent(ename, &np, &tp);
+ switch (rc) {
+ case 1:
+ define_external_entity(np);
+ break;
+ case 2:
+ case 3:
+ output_internal_entity(ename + 1, rc == 3, tp);
+ break;
+ }
+}
+
+/* ENT is the name of the entity with which these attributes are associated;
+if it's NULL, they're associated with the next start tag. */
+
+static VOID handle_attributes(ent, al)
+UNCH *ent;
+struct ad *al;
+{
+ int aln;
+
+ for (aln = 1; aln <= ADN(al); aln++) {
+ if (GET(ADFLAGS(al, aln), AERROR))
+ ;
+ else if (GET(ADFLAGS(al, aln), AINVALID))
+ ;
+ else if (ADVAL(al, aln) == NULL)
+ output_implied_attribute(ent, ADNAME(al, aln));
+ else if (ADTYPE(al, aln) >= ATKNLIST)
+ handle_token_list(ent, al, aln);
+ else
+ handle_single_token(ent, al, aln);
+ if (BITON(ADFLAGS(al, aln), AGROUP))
+ aln += ADNUM(al, aln);
+ }
+}
+
+static VOID handle_token_list(ent, al, aln)
+UNCH *ent;
+struct ad *al;
+int aln;
+{
+ UNCH *ptr;
+ int i;
+ if (ADTYPE(al, aln) == AENTITYS) {
+ ptr = ADVAL(al, aln);
+ for (i = 0; i < ADNUM(al, aln); i++) {
+ /* Temporarily make token look like normal
+ name with length and EOS. */
+ UNCH c = ptr[*ptr + 1];
+ ptr[*ptr + 1] = '\0';
+ *ptr += 2;
+ define_entity(ptr);
+ *ptr -= 2;
+ ptr += *ptr + 1;
+ *ptr = c;
+ }
+ }
+ output_begin_attribute(ent, ADNAME(al, aln), ADTYPE(al, aln));
+ ptr = ADVAL(al, aln);
+ for (i = 0; i < ADNUM(al, aln); i++) {
+ /* The first byte is a length NOT including the length
+ byte; the tokens are not EOS terminated. */
+ output_attribute_token(*ptr, ptr + 1);
+ ptr += *ptr + 1;
+ }
+ output_end_attribute();
+}
+
+static VOID handle_single_token(ent, al, aln)
+UNCH *ent;
+struct ad *al;
+int aln;
+{
+ if (ADTYPE(al, aln) == ANOTEGRP && !DCNMARK(ADDATA(al, aln).x))
+ output_notation(ADVAL(al, aln) + 1,
+ ADDATA(al, aln).x->pubid,
+ ADDATA(al, aln).x->sysid);
+ else if (ADTYPE(al, aln) == AENTITY)
+ define_entity(ADVAL(al, aln));
+ output_begin_attribute(ent, ADNAME(al, aln), ADTYPE(al, aln));
+ if (ADTYPE(al, aln) == ACHARS)
+ output_attribute_token(ustrlen(ADVAL(al, aln)), ADVAL(al, aln));
+ else
+ output_attribute_token(*ADVAL(al, aln) - 2, ADVAL(al, aln) + 1);
+ output_end_attribute();
+}
+
+static VOID output_notation(name, pubid, sysid)
+UNCH *name;
+UNCH *pubid, *sysid;
+{
+ flush_data();
+ print_id((UNIV)0, pubid, sysid);
+ printf("%c%s\n", DEFINE_NOTATION_CODE, name);
+}
+
+static VOID output_internal_entity(ename, is_sdata, text)
+UNCH *ename;
+int is_sdata;
+UNCH *text;
+{
+ flush_data();
+ printf("%c%s %s ", DEFINE_INTERNAL_ENTITY_CODE, ename,
+ is_sdata ? "SDATA" : "CDATA");
+ print_string(text ? ustrlen(text) : 0, text, 0);
+ putchar('\n');
+}
+
+static VOID output_subdoc(nm, id, pubid, sysid)
+UNCH *nm;
+UNIV id;
+UNCH *pubid, *sysid;
+{
+ flush_data();
+ print_id(id, pubid, sysid);
+ printf("%c%s\n", DEFINE_SUBDOC_ENTITY_CODE, nm);
+}
+
+#ifdef SUPPORT_SUBDOC
+
+static VOID process_subdoc(nm, id)
+UNCH *nm;
+UNIV id;
+{
+ if (!suppsw) {
+ flush_data();
+ output_location();
+ printf("%c%s\n", START_SUBDOC_CODE, nm);
+ fflush(stdout);
+ }
+ fflush(stderr);
+
+ if (id) {
+ char **argv;
+ int ret;
+
+ argv = make_argv(id);
+ ret = run_process(argv);
+ if (ret != 0)
+ suberr++;
+
+ current_filename = 0;
+ free(argv);
+ if (ret == 0)
+ get_subcaps();
+ }
+ else {
+ suberr++;
+ appl_error(E_SUBDOC, nm);
+ }
+
+ if (!suppsw)
+ printf("%c%s\n", END_SUBDOC_CODE, nm);
+}
+
+#endif /* SUPPORT_SUBDOC */
+
+static VOID output_external_entity(nm, xtype, id, pubid, sysid, dcn)
+UNCH *nm, *dcn;
+UNIV id;
+UNCH *pubid, *sysid;
+int xtype;
+{
+ char *type;
+
+ flush_data();
+
+ print_id(id, pubid, sysid);
+
+ switch (xtype) {
+ case ESNCDATA:
+ type = "CDATA";
+ break;
+ case ESNNDATA:
+ type = "NDATA";
+ break;
+ case ESNSDATA:
+ type = "SDATA";
+ break;
+ default:
+ return;
+ }
+ printf("%c%s %s %s\n", DEFINE_EXTERNAL_ENTITY_CODE, nm, type, dcn);
+}
+
+static VOID output_record_end()
+{
+ static UNCH re = RECHAR;
+ print_data(1, &re, 0);
+}
+
+static VOID output_pcdata(n, s)
+UNS n;
+UNCH *s;
+{
+ print_data(n, s, 0);
+}
+
+static VOID output_cdata(n, s)
+UNS n;
+UNCH *s;
+{
+ print_data(n, s, 0);
+}
+
+static VOID output_sdata(n, s)
+UNS n;
+UNCH *s;
+{
+ print_data(n, s, 1);
+}
+
+static VOID output_entity_reference(s)
+UNCH *s;
+{
+ flush_data();
+ output_location();
+ printf("%c%s\n", REFERENCE_ENTITY_CODE, s);
+}
+
+static VOID output_start_tag(s)
+UNCH *s;
+{
+ flush_data();
+ output_location();
+ printf("%c%s\n", START_CODE, s);
+}
+
+static VOID output_end_tag(s)
+UNCH *s;
+{
+ flush_data();
+ printf("%c%s\n", END_CODE, s);
+}
+
+static VOID output_processing_instruction(n, s)
+UNS n;
+UNCH *s;
+{
+ flush_data();
+ output_location();
+ putchar(PI_CODE);
+ print_string(n, s, 0);
+ putchar('\n');
+}
+
+static VOID output_appinfo(n, s)
+UNS n;
+UNCH *s;
+{
+ flush_data();
+ output_location();
+ putchar(APPINFO_CODE);
+ print_string(n, s, 0);
+ putchar('\n');
+}
+
+
+static VOID output_implied_attribute(ent, aname)
+UNCH *ent, *aname;
+{
+ flush_data();
+ if (ent)
+ printf("%c%s %s IMPLIED\n", DATA_ATTRIBUTE_CODE, ent, aname);
+ else
+ printf("%c%s IMPLIED\n", ATTRIBUTE_CODE, aname);
+}
+
+static char *attribute_type_string(type)
+int type;
+{
+ switch (type) {
+ case ANMTGRP:
+ case ANAME:
+ case ANMTOKE:
+ case ANUTOKE:
+ case ANUMBER:
+ case ANAMES:
+ case ANMTOKES:
+ case ANUTOKES:
+ case ANUMBERS:
+ case AID:
+ case AIDREF:
+ case AIDREFS:
+ return "TOKEN";
+ case ANOTEGRP:
+ return "NOTATION";
+ case ACHARS:
+ return "CDATA";
+ case AENTITY:
+ case AENTITYS:
+ return "ENTITY";
+ }
+#if 0
+ fatal("invalid attribute type %d", type);
+#endif
+ return "INVALID";
+}
+
+static VOID output_begin_attribute(ent, aname, type)
+UNCH *ent, *aname;
+int type;
+{
+ flush_data();
+ if (ent)
+ printf("%c%s %s %s", DATA_ATTRIBUTE_CODE, ent, aname,
+ attribute_type_string(type));
+ else
+ printf("%c%s %s", ATTRIBUTE_CODE, aname,
+ attribute_type_string(type));
+
+}
+
+static VOID output_attribute_token(vallen, val)
+UNS vallen;
+UNCH *val;
+{
+ putchar(' ');
+ print_string(vallen, val, 0);
+}
+
+static VOID output_end_attribute()
+{
+ putchar('\n');
+}
+
+static VOID print_data(n, s, is_sdata)
+UNS n;
+UNCH *s;
+int is_sdata;
+{
+ if (n > 0 || is_sdata) {
+ if (n == 1 && *s == RECHAR)
+ current_lineno++;
+ else
+ output_location();
+ if (!have_data)
+ putchar(DATA_CODE);
+ print_string(n, s, is_sdata);
+ have_data = 1;
+ }
+}
+
+static VOID flush_data()
+{
+ if (have_data) {
+ putchar('\n');
+ have_data = 0;
+ }
+}
+
+static VOID output_location()
+{
+ char *filename;
+ unsigned long lineno;
+ int filename_changed = 0;
+
+ if (!locsw)
+ return;
+ if (!sgmlloc(&lineno, &filename))
+ return;
+ if (!current_filename || strcmp(filename, current_filename) != 0)
+ filename_changed = 1;
+ else if (lineno == current_lineno)
+ return;
+ flush_data();
+ printf("%c%lu", LOCATION_CODE, lineno);
+ current_lineno = lineno;
+ if (filename_changed) {
+ putchar(' ');
+ print_filename(filename);
+ current_filename = filename;
+ }
+ putchar('\n');
+}
+
+static VOID print_string(slen, s, is_sdata)
+UNS slen;
+UNCH *s;
+int is_sdata;
+{
+ if (is_sdata)
+ fputs("\\|", stdout);
+ while (slen > 0) {
+ UNCH ch = *s++;
+ slen--;
+ if (ch == DELSDATA) {
+ if (is_sdata)
+ ; /* I don't think this should happen */
+ else
+ fputs("\\|", stdout);
+ ;
+ }
+ else if (ch == DELCDATA)
+ ;
+ else {
+ if (ch == DELNONCH) {
+ if (!slen)
+ break;
+ ch = UNSHIFTNON(*s);
+ s++;
+ slen--;
+ }
+ switch (ch) {
+ case RECHAR:
+ fputs("\\n", stdout);
+ break;
+ case '\\':
+ fputs("\\\\", stdout);
+ break;
+ default:
+ if (ISASCII(ch) && isprint(ch))
+ putchar(ch);
+ else
+ printf("\\%03o", ch);
+ break;
+ }
+ }
+ }
+ if (is_sdata)
+ fputs("\\|", stdout);
+}
+
+
+static VOID print_id(id, pubid, sysid)
+UNIV id;
+UNCH *pubid;
+UNCH *sysid;
+{
+
+ if (pubid) {
+ putchar(PUBID_CODE);
+ print_string(ustrlen(pubid), pubid, 0);
+ putchar('\n');
+ }
+
+ if (sysid) {
+ putchar(SYSID_CODE);
+ print_string(ustrlen(sysid), sysid, 0);
+ putchar('\n');
+ }
+
+ if (id) {
+ char *p;
+
+ for (p = id; *p != '\0'; p++) {
+ putchar(FILE_CODE);
+ do {
+ switch (*p) {
+ case '\\':
+ fputs("\\\\", stdout);
+ break;
+ case '\n':
+ fputs("\\n", stdout);
+ break;
+ default:
+ if (ISASCII(*p) && isprint((UNCH)*p))
+ putchar(*p);
+ else
+ printf("\\%03o", (UNCH)*p);
+ break;
+ }
+ } while (*++p);
+ putchar('\n');
+ }
+ }
+}
+
+static VOID print_filename(s)
+char *s;
+{
+ for (; *s; s++)
+ switch (*s) {
+ case '\\':
+ fputs("\\\\", stdout);
+ break;
+ case '\n':
+ fputs("\\n", stdout);
+ break;
+ default:
+ if (ISASCII(*s) && isprint((UNCH)*s))
+ putchar(*s);
+ else
+ printf("\\%03o", (UNCH)*s);
+ break;
+ }
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/lineout.h b/usr.bin/sgmls/sgmls/lineout.h
new file mode 100644
index 0000000..f3c4231
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/lineout.h
@@ -0,0 +1,23 @@
+/* lineout.h */
+
+/* Output codes used by sgmls. */
+
+#define DATA_CODE '-'
+#define START_CODE '('
+#define END_CODE ')'
+#define ATTRIBUTE_CODE 'A'
+#define DATA_ATTRIBUTE_CODE 'D'
+#define REFERENCE_ENTITY_CODE '&'
+#define DEFINE_NOTATION_CODE 'N'
+#define DEFINE_EXTERNAL_ENTITY_CODE 'E'
+#define DEFINE_INTERNAL_ENTITY_CODE 'I'
+#define PI_CODE '?'
+#define DEFINE_SUBDOC_ENTITY_CODE 'S'
+#define START_SUBDOC_CODE '{'
+#define END_SUBDOC_CODE '}'
+#define LOCATION_CODE 'L'
+#define APPINFO_CODE '#'
+#define PUBID_CODE 'p'
+#define SYSID_CODE 's'
+#define FILE_CODE 'f'
+#define CONFORMING_CODE 'C'
diff --git a/usr.bin/sgmls/sgmls/main.c b/usr.bin/sgmls/sgmls/main.c
new file mode 100644
index 0000000..4c8bbb3
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/main.c
@@ -0,0 +1,602 @@
+/* main.c -
+ Main program for sgmls.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+#include "std.h"
+#include "getopt.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "adl.h" /* Definitions for attribute list processing. */
+#include "sgmlmain.h" /* Main interface to SGML services. */
+#include "appl.h"
+
+#define READCNT 512
+
+/* Before using argv[0] in error messages, strip off everything up to and
+including the last character in prog that occurs in PROG_PREFIX. */
+
+#ifndef PROG_PREFIX
+#define PROG_PREFIX "/"
+#endif /* not PROG_PREFIX */
+
+/* Message catalogue name. */
+#define CAT_NAME "sgmls"
+/* Message set to use for application error messages. */
+#define APP_SET 4
+
+#ifdef HAVE_EXTENDED_PRINTF
+#define xvfprintf vfprintf
+#else
+extern int xvfprintf P((FILE *, char *, va_list));
+#endif
+
+static VOID usage P((void));
+static VOID fatal VP((int, ...));
+static VOID do_error P((int, va_list));
+static VOID swinit P((struct switches *));
+static VOID write_caps P((char *, struct sgmlcap *));
+
+static UNIV make_docent P((int, char **));
+static char *munge_program_name P((char *, char *));
+static VOID die P((void));
+#ifdef SUPPORT_SUBDOC
+static VOID build_subargv P((struct switches *));
+static VOID cleanup P((void));
+static char *create_subcap_file P((void));
+#endif /* SUPPORT_SUBDOC */
+
+static char *errlist[] = {
+ 0,
+ "Out of memory",
+ "Cannot open SGML document entity",
+ "Cannot exec `%s': %s",
+ "Cannot fork: %s",
+ "Error waiting for process: %s",
+ "Program %s got fatal signal %d",
+ "Cannot open `%s': %s",
+ "Subdocument capacity botch",
+ "Non-existent subdocument entity `%s' not processed",
+};
+
+int suppsw = 0; /* Non-zero means suppress output. */
+int locsw = 0; /* Non-zero means generate location info. */
+static char *prog; /* Program name (for error messages). */
+static nl_catd catd; /* Message catalogue descriptor. */
+static char *capfile = 0; /* File for capacity report. */
+extern char *version_string;
+
+char options[] = {
+ 'c', ':', 'd', 'e', 'g', 'i', ':', 'l', 'o', ':', 'p', 'r', 's', 'u', 'v',
+#ifdef CANT_REDIRECT_STDERR
+ 'f', ':',
+#endif /* CANT_REDIRECT_STDERR */
+#ifdef TRACE
+ 'x', ':', 'y', ':',
+#endif /* TRACE */
+ '\0'
+};
+
+#ifdef SUPPORT_SUBDOC
+int suberr = 0; /* Error in subdocument. */
+static char *subargv[sizeof(options)];
+static int subargc = 0;
+static char nopenbuf[sizeof(long)*3 + 1];
+static char sgmldecl_file[L_tmpnam];
+static char subcap_file[L_tmpnam];
+#endif
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ static char stderr_buf[BUFSIZ];
+ int opt;
+#ifdef CANT_REDIRECT_STDERR
+ char *errfile = 0;
+#endif
+ struct sgmlcap cap;
+ struct switches sw;
+ int nincludes = 0; /* number of -i options */
+ setbuf(stderr, stderr_buf);
+
+ /* Define MAIN_HOOK in config.h if some function needs to be called here. */
+#ifdef MAIN_HOOK
+ MAIN_HOOK(argc, argv);
+#endif
+#ifdef SUPPORT_SUBDOC
+ subargv[subargc++] = argv[0];
+#endif
+
+ prog = argv[0] = munge_program_name(argv[0], "sgmls");
+
+ catd = catopen(CAT_NAME, 0);
+ swinit(&sw);
+
+ while ((opt = getopt(argc, argv, options)) != EOF) {
+ switch (opt) {
+ case 'l': /* Generate location information. */
+ locsw = 1;
+ break;
+ case 'c': /* Print capacity usage. */
+ capfile = optarg;
+ break;
+ case 's': /* Suppress output. */
+ suppsw = 1;
+ break;
+ case 'd': /* Report duplicate entity declarations. */
+ sw.swdupent = 1;
+ break;
+ case 'e': /* Provide entity stack trace in error msg. */
+ sw.swenttr = 1;
+ break;
+#ifdef CANT_REDIRECT_STDERR
+ case 'f': /* Redirect errors. */
+ errfile = optarg;
+ break;
+#endif /* CANT_REDIRECT_STDERR */
+ case 'g': /* Provide GI stack trace in error messages. */
+ sw.sweltr = 1;
+ break;
+ case 'p': /* Parse only the prolog. */
+ sw.onlypro = 1;
+ suppsw = 1;
+ break;
+ case 'r': /* Give warning for defaulted references. */
+ sw.swrefmsg = 1;
+ break;
+ case 'u':
+ sw.swundef = 1;
+ break;
+#ifdef TRACE
+ case 'x': /* Trace options for the document body. */
+ sw.trace = optarg;
+ break;
+ case 'y': /* Trace options for the prolog. */
+ sw.ptrace = optarg;
+ break;
+#endif /* TRACE */
+ case 'v': /* Print the version number. */
+ fprintf(stderr, "sgmls version %s\n", version_string);
+ fflush(stderr);
+ break;
+ case 'o':
+ sw.nopen = atol(optarg);
+ if (sw.nopen <= 0)
+ usage();
+ break;
+ case 'i': /* Define parameter entity as "INCLUDE". */
+ sw.includes = (char **)xrealloc((UNIV)sw.includes,
+ (nincludes + 2)*sizeof(char *));
+ sw.includes[nincludes++] = optarg;
+ sw.includes[nincludes] = 0;
+ break;
+ case '?':
+ usage();
+ default:
+ abort();
+ }
+ }
+
+#ifdef CANT_REDIRECT_STDERR
+ if (errfile) {
+ FILE *fp;
+ errno = 0;
+ fp = fopen(errfile, "w");
+ if (!fp)
+ fatal(E_OPEN, errfile, strerror(errno));
+ fclose(fp);
+ errno = 0;
+ if (!freopen(errfile, "w", stderr)) {
+ /* Can't use fatal() since stderr is now closed */
+ printf("%s: ", prog);
+ printf(errlist[E_OPEN], errfile, strerror(errno));
+ putchar('\n');
+ exit(EXIT_FAILURE);
+ }
+ }
+#endif /* CANT_REDIRECT_STDERR */
+
+ (void)sgmlset(&sw);
+
+#ifdef SUPPORT_SUBDOC
+ build_subargv(&sw);
+#endif
+ if (sgmlsdoc(make_docent(argc - optind, argv + optind)))
+ fatal(E_DOC);
+
+ process_document(sw.nopen > 0);
+ sgmlend(&cap);
+ if (capfile)
+ write_caps(capfile, &cap);
+#ifdef SUPPORT_SUBDOC
+ cleanup();
+ if (suberr)
+ exit(EXIT_FAILURE);
+#endif /* SUPPORT_SUBDOC */
+ if (sgmlgcnterr() > 0)
+ exit(EXIT_FAILURE);
+ if (!sw.nopen)
+ output_conforming();
+ exit(EXIT_SUCCESS);
+}
+
+static char *munge_program_name(arg, dflt)
+char *arg, *dflt;
+{
+ char *p;
+#ifdef PROG_STRIP_EXTENSION
+ char *ext;
+#endif
+ if (!arg || !*arg)
+ return dflt;
+ p = strchr(arg, '\0');
+ for (;;) {
+ if (p == arg)
+ break;
+ --p;
+ if (strchr(PROG_PREFIX, *p)) {
+ p++;
+ break;
+ }
+ }
+ arg = p;
+#ifdef PROG_STRIP_EXTENSION
+ ext = strrchr(arg, '.');
+ if (ext) {
+ p = (char *)xmalloc(ext - arg + 1);
+ memcpy(p, arg, ext - arg);
+ p[ext - arg] = '\0';
+ arg = p;
+ }
+#endif /* PROG_STRIP_EXTENSION */
+#ifdef PROG_FOLD
+#ifdef PROG_STRIP_EXTENSION
+ if (!ext) {
+#endif
+ p = xmalloc(strlen(arg) + 1);
+ strcpy(p, arg);
+ arg = p;
+#ifdef PROG_STRIP_EXTENSION
+ }
+#endif
+ for (p = arg; *p; p++)
+ if (ISASCII((unsigned char)*p) && isupper((unsigned char)*p))
+ *p = tolower((unsigned char)*p);
+#endif /* PROG_FOLD */
+ return arg;
+}
+
+static UNIV make_docent(argc, argv)
+int argc;
+char **argv;
+{
+ UNS len = 1;
+ int i;
+ UNIV res;
+ char *ptr;
+ static char *stdinname = STDINNAME;
+
+ if (argc == 0) {
+ argv = &stdinname;
+ argc = 1;
+ }
+
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ res = xmalloc(len);
+ ptr = (char *)res;
+ for (i = 0; i < argc; i++) {
+ strcpy(ptr, argv[i]);
+ ptr = strchr(ptr, '\0') + 1;
+ }
+ *ptr = '\0';
+ return res;
+}
+
+
+static VOID usage()
+{
+ /* Don't mention -o since this are for internal use only. */
+ fprintf(stderr, "Usage: %s [-deglprsuv]%s [-c file] [-i entity]%s [filename ...]\n",
+ prog,
+#ifdef CANT_REDIRECT_STDERR
+ " [-f file]",
+#else /* not CANT_REDIRECT_STDERR */
+ "",
+#endif /* not CANT_REDIRECT_STDERR */
+#ifdef TRACE
+ " [-x flags] [-y flags]"
+#else /* not TRACE */
+ ""
+#endif /* not TRACE */
+ );
+ exit(EXIT_FAILURE);
+}
+
+static VOID die()
+{
+#ifdef SUPPORT_SUBDOC
+ cleanup();
+#endif /* SUPPORT_SUBDOC */
+ exit(EXIT_FAILURE);
+}
+
+static VOID swinit(swp)
+struct switches *swp;
+{
+ swp->swenttr = 0;
+ swp->sweltr = 0;
+ swp->swbufsz = READCNT+2;
+ swp->prog = prog;
+ swp->swdupent = 0;
+ swp->swrefmsg = 0;
+#ifdef TRACE
+ swp->trace = 0;
+ swp->ptrace = 0;
+#endif /* TRACE */
+ swp->catd = catd;
+ swp->swambig = 1; /* Always check for ambiguity. */
+ swp->swundef = 0;
+ swp->nopen = 0;
+ swp->onlypro = 0;
+ swp->includes = 0;
+ swp->die = die;
+}
+
+#ifdef SUPPORT_SUBDOC
+
+static VOID build_subargv(swp)
+struct switches *swp;
+{
+ if (suppsw)
+ subargv[subargc++] = "-s";
+ if (locsw)
+ subargv[subargc++] = "-l";
+ if (swp->swdupent)
+ subargv[subargc++] = "-d";
+ if (swp->swenttr)
+ subargv[subargc++] = "-e";
+ if (swp->sweltr)
+ subargv[subargc++] = "-g";
+ if (swp->swrefmsg)
+ subargv[subargc++] = "-r";
+#ifdef TRACE
+ if (swp->trace) {
+ subargv[subargc++] = "-x";
+ subargv[subargc++] = swp->trace;
+ }
+ if (swp->ptrace) {
+ subargv[subargc++] = "-y";
+ subargv[subargc++] = swp->ptrace;
+ }
+#endif /* TRACE */
+ subargv[subargc++] = "-o";
+ sprintf(nopenbuf, "%ld", swp->nopen + 1);
+ subargv[subargc++] = nopenbuf;
+}
+
+
+static
+VOID handler(sig)
+int sig;
+{
+ signal(sig, SIG_DFL);
+ cleanup();
+ raise(sig);
+}
+
+static
+VOID cleanup()
+{
+ if (sgmldecl_file[0]) {
+ (void)remove(sgmldecl_file);
+ sgmldecl_file[0] = '\0';
+ }
+ if (subcap_file[0]) {
+ (void)remove(subcap_file);
+ subcap_file[0] = '\0';
+ }
+}
+
+static
+char *store_sgmldecl()
+{
+ if (!sgmldecl_file[0]) {
+ FILE *fp;
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, handler);
+#ifdef SIGTERM
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, handler);
+#endif /* SIGTERM */
+#ifdef SIGPIPE
+ if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
+ signal(SIGPIPE, handler);
+#endif
+#ifdef SIGHUP
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, handler);
+#endif
+ tmpnam(sgmldecl_file);
+ errno = 0;
+ fp = fopen(sgmldecl_file, "w");
+ if (!fp)
+ fatal(E_OPEN, sgmldecl_file, strerror(errno));
+ sgmlwrsd(fp);
+ fclose(fp);
+ }
+ return sgmldecl_file;
+}
+
+static
+char *create_subcap_file()
+{
+ if (subcap_file[0] == '\0') {
+ FILE *fp;
+ tmpnam(subcap_file);
+ fp = fopen(subcap_file, "w");
+ if (!fp)
+ fatal(E_OPEN, subcap_file, strerror(errno));
+ fclose(fp);
+ }
+ return subcap_file;
+}
+
+char **make_argv(id)
+UNIV id;
+{
+ int nfiles;
+ char *p;
+ char **argv;
+ int i;
+
+ for (p = (char *)id, nfiles = 0; *p; p = strchr(p, '\0') + 1)
+ nfiles++;
+
+ argv = (char **)xmalloc((subargc + 2 + 1 + nfiles + 1)*sizeof(char *));
+ memcpy((UNIV)argv, (UNIV)subargv, subargc*sizeof(char *));
+
+ i = subargc;
+
+ argv[i++] = "-c";
+ argv[i++] = create_subcap_file();
+
+ argv[i++] = store_sgmldecl();
+
+ for (p = (char *)id; *p; p = strchr(p, '\0') + 1)
+ argv[i++] = p;
+ argv[i] = 0;
+ return argv;
+}
+
+VOID get_subcaps()
+{
+ long cap[NCAPACITY];
+ FILE *fp;
+ int i;
+
+ if (!subcap_file[0])
+ return;
+ errno = 0;
+ fp = fopen(subcap_file, "r");
+ if (!fp)
+ fatal(E_OPEN, subcap_file, strerror(errno));
+ for (i = 0; i < NCAPACITY; i++)
+ if (fscanf(fp, "%*s %ld", cap + i) != 1)
+ fatal(E_CAPBOTCH);
+ fclose(fp);
+ sgmlsubcap(cap);
+}
+
+
+#endif /* SUPPORT_SUBDOC */
+
+/* Print capacity statistics.*/
+
+static VOID write_caps(name, p)
+char *name;
+struct sgmlcap *p;
+{
+ FILE *fp;
+ int i;
+ fp = fopen(name, "w");
+ if (!fp)
+ fatal(E_OPEN, name, strerror(errno));
+ /* This is in RACT format. */
+ for (i = 0; i < NCAPACITY; i++)
+ fprintf(fp, "%s %ld\n", p->name[i], p->number[i]*p->points[i]);
+ fclose(fp);
+}
+
+UNIV xmalloc(n)
+UNS n;
+{
+ UNIV p = malloc(n);
+ if (!p)
+ fatal(E_NOMEM);
+ return p;
+}
+
+UNIV xrealloc(s, n)
+UNIV s;
+UNS n;
+{
+ s = s ? realloc(s, n) : malloc(n);
+ if (!s)
+ fatal(E_NOMEM);
+ return s;
+}
+
+static
+#ifdef VARARGS
+VOID fatal(va_alist) va_dcl
+#else
+VOID fatal(int errnum,...)
+#endif
+{
+#ifdef VARARGS
+ int errnum;
+#endif
+ va_list ap;
+
+#ifdef VARARGS
+ va_start(ap);
+ errnum = va_arg(ap, int);
+#else
+ va_start(ap, errnum);
+#endif
+ do_error(errnum, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+#ifdef VARARGS
+VOID appl_error(va_alist) va_dcl
+#else
+VOID appl_error(int errnum,...)
+#endif
+{
+#ifdef VARARGS
+ int errnum;
+#endif
+ va_list ap;
+
+#ifdef VARARGS
+ va_start(ap);
+ errnum = va_arg(ap, int);
+#else
+ va_start(ap, errnum);
+#endif
+ do_error(errnum, ap);
+ va_end(ap);
+}
+
+static
+VOID do_error(errnum, ap)
+int errnum;
+va_list ap;
+{
+ char *text;
+ fprintf(stderr, "%s: ", prog);
+ assert(errnum > 0);
+ assert(errnum < sizeof(errlist)/sizeof(errlist[0]));
+ text = catgets(catd, APP_SET, errnum, errlist[errnum]);
+ assert(text != 0);
+ xvfprintf(stderr, text, ap);
+ fputc('\n', stderr);
+ fflush(stderr);
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/md1.c b/usr.bin/sgmls/sgmls/md1.c
new file mode 100644
index 0000000..9a294e3
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/md1.c
@@ -0,0 +1,862 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+/* MDADL: Process ATTLIST declaration.
+*/
+VOID mdadl(tbuf)
+UNCH *tbuf; /* Work area for tokenization (tbuf). */
+{
+ int i; /* Loop counter; temporary variable. */
+ int adlim; /* Number of unused ad slots in al. */
+ struct ad *alperm = 0; /* Attribute definition list. */
+ int stored = 0;
+
+ mdname = key[KATTLIST]; /* Identify declaration for messages. */
+ subdcl = 0; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es level for entity nesting check. */
+ reqadn = noteadn = 0; /* No required attributes yet. */
+ idadn = conradn = 0; /* No special atts yet.*/
+ AN(al) = 0; /* Number of attributes defined. */
+ ADN(al) = 0; /* Number of ad's in al (atts + name vals).*/
+ /* PARAMETER 1: Element name or a group of them.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: element name or group");
+ switch (pcbmd.action) {
+ case NAS:
+ nmgrp[0] = etddef(tbuf);
+ nmgrp[1] = 0;
+ break;
+ case GRPS:
+ parsegrp(nmgrp, &pcbgrnm, tbuf);
+ break;
+ case RNS: /* Reserved name started. */
+ if (ustrcmp(tbuf+1, key[KNOTATION])) {
+ mderr(118, tbuf+1, key[KNOTATION]);
+ return;
+ }
+ mdnadl(tbuf);
+ return;
+ default:
+ mderr(121, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* Save first GI for error msgs. */
+ if (nmgrp[0])
+ subdcl = nmgrp[0]->etdgi+1;
+ /* PARAMETER 2: Attribute definition list.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: attribute list");
+ if (pcbmd.action!=NAS) {
+ mderr(120, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ while (pcbmd.action==NAS) {
+ al[ADN(al)+1].adname = savenm(tbuf);
+ if ((adlim = ATTCNT-((int)++ADN(al)))<0) {
+ mderr(111, (UNCH *)0, (UNCH *)0);
+ adlfree(al, 1);
+ return;
+ }
+ ++AN(al);
+ if (mdattdef(adlim, 0)) {
+ adlfree(al, 1);
+ return;
+ }
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ }
+ if (AN(al)>0) { /* Save list only if 1 or more good atts. */
+ if (reqadn) SET(ADLF(al), ADLREQ); /* Element must have start-tag. */
+ if (noteadn) SET(ADLF(al), ADLNOTE); /* Element cannot be EMPTY. */
+ if (conradn) SET(ADLF(al), ADLCONR); /* Element cannot be EMPTY. */
+ alperm = (struct ad *)rmalloc((1+ADN(al))*ADSZ);
+ memcpy((UNIV)alperm, (UNIV)al, (1+ADN(al))*ADSZ );
+ ds.attcnt += AN(al); /* Number of attributes defined. */
+ ds.attgcnt += ADN(al) - AN(al); /* Number of att grp members. */
+ TRACEADL(alperm);
+ }
+ /* Clear attribute list for next declaration. */
+ MEMZERO((UNIV)al, (1+ADN(al))*ADSZ);
+
+ /* PARAMETER 3: End of declaration.
+ */
+ /* Next pcb.action was set during attribute definition loop. */
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) {mderr(126, (UNCH *)0, (UNCH *)0); return;}
+ if (es!=mdessv) synerr(37, &pcbmd);
+
+ /* EXECUTE: Store the definition for each element name specified.
+ */
+ TRACEGRP(nmgrp);
+ for (i = 0; nmgrp[i]; i++) {
+ if (nmgrp[i]->adl) { /* Error if an ADL exists. */
+ mderr(112, (UNCH *)0, (UNCH *)0);
+ continue;
+ }
+ nmgrp[i]->adl = alperm; /* If virgin, store the adl ptr. */
+ stored = 1;
+ if (alperm && nmgrp[i]->etdmod)
+ etdadl(nmgrp[i]); /* Check for conflicts with ETD. */
+ }
+ if (!stored && alperm) {
+ adlfree(alperm, 1);
+ frem((UNIV)alperm);
+ }
+}
+/* ETDADL: Check compatibility between ETD and ADL.
+*/
+VOID etdadl(p)
+struct etd *p; /* Pointer to element type definition. */
+{
+ parmno = 0;
+ /* Minimizable element cannot have required attribute. */
+ if (GET(p->etdmin, SMO) && GET(p->adl[0].adflags, ADLREQ)) {
+ mderr(40, (UNCH *)0, (UNCH *)0);
+ RESET(p->etdmin, SMO);
+ }
+ /* Empty element cannot have NOTATION attribute.
+ Attribute is not removed (too much trouble), but we trap
+ attempts to specify it on the start-tag in adlval().
+ */
+ if (GET(p->etdmod->ttype, MNONE)) {
+ if (GET(p->adl[0].adflags, ADLNOTE))
+ mderr(83, (UNCH *)0, (UNCH *)0);
+
+ /* Empty element cannot have CONREF attribute.
+ Attribute is not removed because it just acts
+ like IMPLIED anyway.
+ */
+ if (GET(p->adl[0].adflags, ADLCONR))
+ mderr(85, (UNCH *)0, (UNCH *)0);
+ }
+ /* "-" should not be specified for the end-tag minimization if
+ the element has a content reference attribute. */
+ if (GET(p->adl[0].adflags, ADLCONR) && BITON(p->etdmin, EMM))
+ mderr(153, (UNCH *)0, (UNCH *)0);
+}
+/* MDNADL: Process ATTLIST declaration for notation.
+ TO DO: Pass deftab and dvtab as parameters so
+ that prohibited types can be handled by leaving
+ them out of the tables.
+*/
+VOID mdnadl(tbuf)
+UNCH *tbuf; /* Work area for tokenization (tbuf). */
+{
+ int i; /* Loop counter; temporary variable. */
+ int adlim; /* Number of unused ad slots in al. */
+ struct ad *alperm = 0; /* Attribute definition list. */
+ int stored = 0;
+
+ /* PARAMETER 1: Notation name or a group of them.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: notation name or group");
+ switch (pcbmd.action) {
+ case NAS:
+ nnmgrp[0] = dcndef(tbuf);
+ nnmgrp[1] = 0;
+ break;
+ case GRPS:
+ parsngrp(nnmgrp, &pcbgrnm, tbuf);
+ break;
+ default:
+ mderr(121, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ subdcl = nnmgrp[0]->ename+1; /* Save first name for error msgs. */
+ /* PARAMETER 2: Attribute definition list.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: attribute list");
+ if (pcbmd.action!=NAS) {
+ mderr(120, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ while (pcbmd.action==NAS) {
+ al[ADN(al)+1].adname = savenm(tbuf);
+ if ((adlim = ATTCNT-((int)ADN(al)++))<0) {
+ mderr(111, (UNCH *)0, (UNCH *)0);
+ adlfree(al, 1);
+ return;
+ }
+ ++AN(al);
+ if (mdattdef(adlim, 1)) {
+ adlfree(al, 1);
+ return;
+ }
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ }
+ if (AN(al)>0) { /* Save list only if 1 or more good atts. */
+ alperm = (struct ad *)rmalloc((1+ADN(al))*ADSZ);
+ memcpy((UNIV)alperm, (UNIV)al, (1+ADN(al))*ADSZ );
+ ds.attcnt += AN(al); /* Number of attributes defined. */
+ ds.attgcnt += ADN(al) - AN(al); /* Number of att grp members. */
+ TRACEADL(alperm);
+ }
+ /* Clear attribute list for next declaration. */
+ MEMZERO((UNIV)al, (1+ADN(al))*ADSZ);
+
+ /* PARAMETER 3: End of declaration.
+ */
+ /* Next pcb.action was set during attribute definition loop. */
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) {mderr(126, (UNCH *)0, (UNCH *)0); return;}
+ if (es!=mdessv) synerr(37, &pcbmd);
+
+ /* EXECUTE: Store the definition for each notation name specified.
+ */
+ TRACENGR(nnmgrp);
+ for (i = 0; nnmgrp[i]; i++) {
+ if (nnmgrp[i]->adl) { /* Error if an ADL exists. */
+ mderr(112, (UNCH *)0, (UNCH *)0);
+ continue;
+ }
+ nnmgrp[i]->adl = alperm; /* If virgin, store the adl ptr. */
+ if (nnmgrp[i]->entsw)
+ fixdatt(nnmgrp[i]);
+ stored = 1;
+ TRACEDCN(nnmgrp[i]);
+ }
+ if (!stored && alperm) {
+ adlfree(alperm, 1);
+ frem((UNIV)alperm);
+ }
+}
+
+/* Data attributes have been specified for notation p, but entities
+have already been declared with notation p. Fix up the definitions of
+all entities with notation p. Generate an error for any data
+attribute that was required. */
+
+VOID fixdatt(p)
+struct dcncb *p;
+{
+ int i;
+ for (i = 0; i < ENTHASH; i++) {
+ struct entity *ep;
+ for (ep = etab[i]; ep; ep = ep->enext)
+ if (ep->estore == ESN && ep->etx.n && ep->etx.n->nedcn == p) {
+ int adn;
+ initatt(p->adl);
+ /* Don't use adlval because if there were required
+ attributes the error message wouldn't say what
+ entity was involved. */
+ for (adn = 1; adn <= ADN(al); adn++) {
+ if (GET(ADFLAGS(al,adn), AREQ)) {
+ sgmlerr(218, &pcbstag, ADNAME(al,adn),
+ ep->ename + 1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ }
+ if (BITON(ADFLAGS(al, adn), AGROUP))
+ adn += ADNUM(al, adn);
+ }
+ storedatt(ep->etx.n);
+ }
+ }
+}
+
+/* MDATTDEF: Process an individual attribute definition.
+ The attribute name is parsed by the caller.
+ Duplicate attributes are parsed, but removed from list.
+ Returns 0 if successful, otherwise returns 1.
+*/
+int mdattdef(adlim, datt)
+int adlim; /* Remaining capacity of al (in tokens).*/
+int datt; /* Non-zero if a data attribute. */
+{
+ int deftype; /* Default value type: 0=not keyword. */
+ int errsw = 0; /* 1=semantic error; ignore att. */
+ int novalsw = 0; /* 1=semantic error; treat as IMPLIED. */
+ int attadn = (int)ADN(al); /* Save ad number of this attribute. */
+ struct parse *grppcb = NULL; /* PCB for name/token grp parse. */
+ int errcode; /* Error type returned by PARSEVAL, ANMTGRP. */
+ UNCH *advalsv; /* Save area for permanent value ptr. */
+
+ /* PARAMETER 1: Attribute name (parsed by caller).
+ */
+ TRACEMD("1: attribute name");
+ if (anmget((int)ADN(al)-1, al[attadn].adname)) {
+ errsw = 1;
+ mderr(99, ADNAME(al,attadn), (UNCH *)0);
+ }
+ ADNUM(al,attadn) = ADFLAGS(al,attadn) = ADLEN(al,attadn) = 0;
+ ADVAL(al,attadn) = 0; ADDATA(al,attadn).x = 0; ADTYPE(al,attadn) = ANMTGRP;
+ /* PARAMETER 2: Declared value.
+ */
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: declared value");
+ switch (pcbmd.action) {
+ case NAS: /* Keyword for value type. */
+ switch (ADTYPE(al,attadn) = (UNCH)mapsrch(dvtab, lbuf+1)) {
+ case 0:
+ mderr(100, ADNAME(al,attadn), lbuf+1);
+ return 1;
+ case ANOTEGRP:
+ if (datt) {
+ errsw = 1;
+ mderr(156, (UNCH *)0, (UNCH *)0);
+ }
+ else if (!noteadn) noteadn = ADN(al);
+ else {
+ errsw = 1;
+ mderr(101, ADNAME(al,attadn), (UNCH *)0);
+ }
+ grppcb = &pcbgrnm; /* NOTATION requires name grp. */
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);/* Get GRPO*/
+ break;
+ case AID:
+ if (datt) {
+ errsw = 1;
+ mderr(144, (UNCH *)0, (UNCH *)0);
+ }
+ else if (!idadn)
+ idadn = attadn;
+ else {
+ errsw = 1;
+ mderr(102, ADNAME(al,attadn), (UNCH *)0);
+ }
+ break;
+ case AIDREF:
+ case AIDREFS:
+ if (datt) {
+ errsw = 1;
+ mderr(155, (UNCH *)0, (UNCH *)0);
+ }
+ break;
+ case AENTITY:
+ case AENTITYS:
+ if (datt) {
+ errsw = 1;
+ mderr(154, (UNCH *)0, (UNCH *)0);
+ }
+ break;
+ }
+ break;
+ case GRPS:
+ grppcb = &pcbgrnt; /* Normal grp is name token grp. */
+ break;
+ case EMD:
+ mderr(103, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ default:
+ mderr(104, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ }
+ /* PARAMETER 2A: Name token group.
+ */
+ if (grppcb != NULL) {
+ TRACEMD("2A: name group");
+ switch (pcbmd.action) {
+ case GRPS: /* Name token list. */
+ SET(ADFLAGS(al,attadn), AGROUP);
+ /* Call routine to parse group, create ad entries in adl. */
+ errcode = anmtgrp(grppcb, al+attadn,
+ (GRPCNT<adlim ? GRPCNT+1 : adlim+1),
+ &al[attadn].adnum, ADN(al));
+ if (errcode<=0) {
+ if (adlim < GRPCNT)
+ mderr(111, (UNCH *)0, (UNCH *)0);
+ else
+ mderr(105, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ }
+ ADN(al) += ADNUM(al,attadn); /* Add grp size to total ad cnt.*/
+ break;
+ default:
+ mderr(106, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ }
+ }
+ /* PARAMETER 3: Default value keyword.
+ */
+ parsemd(lbuf, AVALCASE,
+ (ADTYPE(al,attadn)==ACHARS) ? &pcblitr : &pcblitt, LITLEN);
+ TRACEMD("3: default keyword");
+ switch (pcbmd.action) {
+ case RNS: /* Keyword. */
+ deftype = mapsrch(deftab, lbuf+1);
+ switch (deftype) {
+ case DFIXED: /* FIXED */
+ SET(ADFLAGS(al,attadn), AFIXED);
+ parsemd(lbuf, AVALCASE,
+ (ADTYPE(al,attadn)==ACHARS) ? &pcblitr : &pcblitt,
+ LITLEN); /* Real default. */
+ goto parm3x; /* Go process specified value. */
+ case DCURR: /* CURRENT: If ID, treat as IMPLIED. */
+ if (ADTYPE(al,attadn)==AID) {
+ mderr(80, ADNAME(al,attadn), (UNCH *)0);
+ break;
+ }
+ if (datt) {
+ mderr(157, (UNCH *)0, (UNCH *)0);
+ break;
+ }
+ SET(ADFLAGS(al,attadn), ACURRENT);
+ break;
+ case DREQ: /* REQUIRED */
+ SET(ADFLAGS(al,attadn), AREQ); ++reqadn;
+ break;
+ case DCONR: /* CONREF */
+ if (ADTYPE(al,attadn)==AID) {
+ mderr(107, ADNAME(al,attadn), (UNCH *)0);
+ break;
+ }
+ if (datt) {
+ mderr(158, (UNCH *)0, (UNCH *)0);
+ break;
+ }
+ SET(ADFLAGS(al,attadn), ACONREF); conradn = 1;
+ case DNULL: /* IMPLIED */
+ break;
+ default: /* Unknown keyword is an error. */
+ mderr(108, ADNAME(al,attadn), lbuf+1);
+ errsw = 1;
+ }
+ if (errsw) {
+ /* Ignore erroneous att. */
+ adlfree(al, attadn);
+ --AN(al);
+ ADN(al) = (UNCH)attadn-1;
+ }
+ return(0);
+ default:
+ break;
+ }
+ /* PARAMETER 3x: Default value (non-keyword).
+ */
+ parm3x:
+ TRACEMD("3x: default (non-keyword)");
+ if (ADTYPE(al,attadn)==AID) { /* If ID, treat as IMPLIED. */
+ mderr(81, ADNAME(al,attadn), (UNCH *)0);
+ novalsw = 1; /* Keep parsing to keep things straight. */
+ }
+ switch (pcbmd.action) {
+ case LIT: /* Literal. */
+ case LITE: /* Literal. */
+ /* Null string (except CDATA) is error: msg and treat as IMPLIED. */
+ if (*lbuf == '\0' && ADTYPE(al,attadn)!=ACHARS) {
+ mderr(82, ADNAME(al,attadn), (UNCH *)0);
+ novalsw = 1;
+ }
+ break;
+ case NAS: /* Name character string. */
+ case NMT: /* Name character string. */
+ case NUM: /* Number or number token string. */
+ /* The name won't have a length byte because AVALCASE was specified. */
+ break;
+ case CDR:
+ parsetkn(lbuf, NMC, LITLEN);
+ break;
+ case EMD:
+ mderr(109, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ default:
+ mderr(110, ADNAME(al,attadn), (UNCH *)0);
+ return 1;
+ }
+ if (errsw) {
+ /* Ignore erroneous att. */
+ adlfree(al, attadn);
+ --AN(al);
+ ADN(al) = (UNCH)attadn-1;
+ return(0);
+ }
+ if (novalsw) return(0);
+
+ /* PARAMETER 3y: Validate and store default value.
+ */
+ if (ADTYPE(al,attadn)==ACHARS) {
+ UNS len = vallen(ACHARS, 0, lbuf);
+ if (len > LITLEN) {
+ /* Treat as implied. */
+ sgmlerr(224, &pcbmd, ADNAME(al,attadn), (UNCH *)0);
+ return 0;
+ }
+ /* No more checking for CDATA value. */
+ ADNUM(al,attadn) = 0; /* CDATA is 0 tokens. */
+ ADVAL(al,attadn) = savestr(lbuf);/* Store default; save ptr. */
+ ADLEN(al,attadn) = len;
+ ds.attdef += len;
+ return 0;
+ }
+ /* Parse value and save token count (GROUP implies 1 token). */
+ advalsv = (UNCH *)rmalloc(ustrlen(lbuf)+2); /* Storage for tokenized value. */
+ errcode = parseval(lbuf, (UNS)ADTYPE(al,attadn), advalsv);
+ if (BITOFF(ADFLAGS(al,attadn), AGROUP)) ADNUM(al,attadn) = (UNCH)tokencnt;
+
+ /* If value was invalid, or was a group member that was not in the group,
+ issue an appropriate message and set the error switch. */
+ if (errcode)
+ {sgmlerr((UNS)errcode, &pcbmd, ADNAME(al,attadn), lbuf); errsw = 1;}
+ else if ( BITON(ADFLAGS(al,attadn), AGROUP)
+ && !amemget(&al[attadn], (int)ADNUM(al,attadn), advalsv) ) {
+ sgmlerr(79, &pcbmd, ADNAME(al,attadn), advalsv+1);
+ errsw = 1;
+ }
+ ADLEN(al,attadn) = vallen(ADTYPE(al,attadn), ADNUM(al,attadn), advalsv);
+ if (ADLEN(al,attadn) > LITLEN) {
+ sgmlerr(224, &pcbmd, ADNAME(al,attadn), (UNCH *)0);
+ ADLEN(al,attadn) = 0;
+ errsw = 1;
+ }
+ /* For valid tokenized value, save it and update statistics. */
+ if (!errsw) {
+ ADVAL(al,attadn) = advalsv;
+ ds.attdef += ADLEN(al,attadn);
+ return 0;
+ }
+ /* If value was bad, free the value's storage and treat as
+ IMPLIED or REQUIRED. */
+ frem((UNIV)advalsv); /* Release storage for value. */
+ ADVAL(al,attadn) = NULL; /* And make value NULL. */
+ return 0;
+}
+/* ANMTGRP: Parse a name or name token group, create attribute descriptors
+ for its members, and add them to the attribute descriptor list.
+ The parse either terminates or returns a good token, so no
+ switch is needed.
+*/
+int anmtgrp(pcb, nt, grplim, adn, adsz)
+struct parse *pcb; /* PCB for name or name token grp. */
+struct ad nt[]; /* Buffer for creating name token list. */
+int grplim; /* Maximum size of list (plus 1). */
+UNS *adn; /* Ptr to number of names or tokens in grp. */
+int adsz; /* Size of att def list. */
+{
+ UNCH adtype = (UNCH)(pcb==&pcbgrnt ? ANMTGRP:ANOTEGRP);/*Attribute type.*/
+ int essv = es; /* Entity stack level when grp started. */
+
+ *adn = 0; /* Group is empty to start. */
+ while (parse(pcb)!=GRPE && *adn<grplim) {
+ switch (pcb->action) {
+ case NAS_: /* Name or name token (depending on pcb). */
+ case NMT_:
+ parsenm(lbuf, NAMECASE);
+ nt[*adn+1].adname = savenm(lbuf);
+ if (antvget((int)(adsz+*adn), nt[*adn+1].adname, (UNCH **)0))
+ mderr(98, ntoa((int)*adn+1), nt[*adn+1].adname+1);
+ nt[++*adn].adtype = adtype;
+ nt[*adn].addef = NULL;
+ continue;
+
+ case EE_: /* Entity ended (correctly or incorrectly). */
+ if (es<essv) {synerr(37, pcb); essv = es;}
+ continue;
+
+ case PIE_: /* PI entity reference (invalid). */
+ entpisw = 0; /* Reset PI entity indicator. */
+ synerr(59, pcb);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ if (es!=essv) synerr(37, pcb);
+ if (*adn==grplim) return -1;
+ else return *adn; /* Return number of tokens. */
+}
+/* MDDTDS: Process start of DOCTYPE declaration (through MSO).
+*/
+VOID mddtds(tbuf)
+UNCH *tbuf; /* Work area for tokenization[LITLEN+2]. */
+{
+ struct fpi fpicb; /* Formal public identifier structure. */
+ union etext etx; /* Ptr to entity text. */
+ UNCH estore = ESD; /* Entity storage class. */
+ int emdsw = 0; /* 1=end of declaration found; 0=not yet. */
+
+ mdname = key[KDOCTYPE]; /* Identify declaration for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es for checking entity nesting. */
+ dtdrefsw = 0; /* No external DTD entity as yet. */
+ /* PARAMETER 1: Document type name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: doc type name");
+ if (pcbmd.action!=NAS) {mderr(120, (UNCH *)0, (UNCH *)0); return;}
+ dtype = savenm(tbuf);
+ subdcl = dtype+1; /* Subject of declaration for error msgs. */
+
+ /* PARAMETER 2: External identifier keyword or MDS.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: extid or MDS");
+ switch (pcbmd.action) {
+ case NAS:
+ if (mdextid(tbuf, &fpicb, dtype+1, &estore, (PNE)0)==0) return;
+ if ((etx.x = entgen(&fpicb))==0)
+ mderr(146, dtype+1, (UNCH *)0);
+ else
+ dtdrefsw = 1; /* Signal external DTD entity. */
+ break;
+ case MDS:
+ goto execute;
+ default:
+ mderr(128, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* PARAMETER 3: MDS or end of declaration.
+ */
+ TRACEMD("3: MDS or EMD");
+ switch (pcbmd.action) {
+ default: /* Treat as end of declaration. */
+ mderr(126, (UNCH *)0, (UNCH *)0);
+ case EMD:
+ emdsw = 1;
+ case MDS:
+ break;
+ }
+ /* EXECUTE: Store entity definition if an external ID was specified.
+ */
+ execute:
+ if (es!=mdessv) synerr(37, &pcbmd);
+ propcb = &pcbmds; /* Prepare to parse doc type definition (MDS). */
+ if (dtdrefsw) {
+ /* TO DO: If concurrent DTD's supported, free existing
+ etext for all but first DTD (or reuse it). */
+ entdef(indtdent, estore, &etx);
+ ++ds.ecbcnt; ds.ecbtext += entlen;
+ if (emdsw) {
+ REPEATCC; /* Push back the MDC. */
+ *FPOS = lex.d.msc; /* Simulate end of DTD subset. */
+ REPEATCC; /* Back up to read MSC next. */
+ delmscsw = 1; /* Insert MSC after referenced DTD. */
+ }
+ }
+ indtdsw = 1; /* Allow "DTD only" parameters. */
+ return;
+}
+/* MDDTDE: Process DOCTYPE declaration end.
+*/
+VOID mddtde(tbuf)
+UNCH *tbuf; /* Work area for tokenization. */
+{
+ mdessv = es; /* Save es for checking entity nesting. */
+ propcb = &pcbpro; /* Restore normal prolog parse. */
+ indtdsw = 0; /* Prohibit "DTD only" parameters. */
+
+ mdname = key[KDOCTYPE]; /* Identify declaration for messages. */
+ subdcl = dtype+1; /* Subject of declaration for error msgs. */
+ parmno = 0; /* No parameters as yet. */
+ /* PARAMETER 4: End of declaration.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ if (es!=mdessv) synerr(37, &pcbmd);
+}
+/* MDELEM: Process ELEMENT declaration.
+*/
+VOID mdelem(tbuf)
+UNCH *tbuf; /* Work area for tokenization (tbuf). */
+{
+ UNCH *ranksuff = lbuf; /* Rank suffix. */
+ UNS dctype = 0; /* Declared content type (from dctab). */
+ UNCH fmin = 0; /* Minimization bit flags. */
+ int i; /* Loop counter. */
+ UNS u; /* Temporary variable. */
+ struct etd **mexgrp, **pexgrp; /* Ptr to model exceptions array. */
+ struct thdr *cmod, *cmodsv; /* Ptr to content model. */
+ UNCH *etdgi; /* GI of current etd (when going through group).*/
+ int minomitted = 0; /* Tag minimization parameters omitted. */
+
+ mdname = key[KELEMENT]; /* Identify declaration for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es level for entity nesting check. */
+ ranksuff[0] = 0;
+ mexgrp = pexgrp = 0;
+
+ /* PARAMETER 1: Element name or a group of them.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: element name or grp");
+ switch (pcbmd.action) {
+ case NAS:
+ nmgrp[0] = etddef(tbuf);
+ nmgrp[1] = 0;
+ break;
+ case GRPS:
+ parsegrp(nmgrp, &pcbgrnm, tbuf);
+ break;
+ default:
+ mderr(121, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* Save first GI for trace and error messages. */
+ if (nmgrp[0])
+ subdcl = nmgrp[0]->etdgi+1;
+
+ /* PARAMETER 1A: Rank suffix (optional).
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1A: rank suffix");
+ switch (pcbmd.action) {
+ case NUM:
+ ustrcpy(ranksuff, tbuf);
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ default:
+ break;
+ }
+ /* PARAMETER 2A: Start-tag minimization.
+ */
+ TRACEMD("2A: start min");
+ switch (pcbmd.action) {
+ case CDR:
+ break;
+ case NAS:
+ if (!ustrcmp(tbuf+1, key[KO])) {
+ if (OMITTAG==YES) SET(fmin, SMO);
+ break;
+ }
+ /* fall through */
+ default:
+ if (OMITTAG==NO) {minomitted=1; break;}
+ mderr(129, tbuf+1, (UNCH *)0);
+ return;
+ }
+ /* Must omit omitted end-tag minimization, if omitted
+ start-tag minimization was omitted (because OMITTAG == NO). */
+ if (!minomitted) {
+ /* PARAMETER 2B: End-tag minimization.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2B: end min");
+ switch (pcbmd.action) {
+ case NAS:
+ if (ustrcmp(tbuf+1, key[KO])) {mderr(129, tbuf+1, (UNCH *)0); return;}
+ if (OMITTAG==YES) SET(fmin, EMO);
+ break;
+ case CDR:
+ SET(fmin, EMM);
+ break;
+ default:
+ mderr(129, tbuf+1, (UNCH *)0);
+ return;
+ }
+ /* PARAMETER 3: Declared content.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ }
+ TRACEMD("3: declared content");
+ switch (pcbmd.action) {
+ case NAS:
+ dctype = mapsrch(dctab, tbuf+1);
+ if (!dctype) {mderr(24, tbuf+1, (UNCH *)0); return;}
+ /* Eliminate incompatibilities among parameters. */
+ if (GET(fmin, SMO) && GET(dctype, MNONE+MCDATA+MRCDATA)) {
+ mderr(58, (UNCH *)0, (UNCH *)0);
+ RESET(fmin, SMO);
+ }
+ if (GET(dctype, MNONE) && BITON(fmin, EMM)) {
+ mderr(87, (UNCH *)0, (UNCH *)0);
+ SET(fmin, EMO);
+ }
+ /* If valid, process like a content model. */
+ case GRPS:
+ cmodsv = parsemod((int)(pcbmd.action==GRPS ? 0 : dctype));
+ if (cmodsv==0) return;
+ u = (dctype ? 1 : cmodsv->tu.tnum+2) * THSZ;
+ cmod = (struct thdr *)rmalloc(u);
+ memcpy((UNIV)cmod , (UNIV)cmodsv, u );
+ ds.modcnt += cmod->tu.tnum;
+ TRACEMOD(cmod);
+ break;
+ default:
+ mderr(130, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* PARAMETERS 3A, 3B: Exceptions or end.
+ */
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ if (BITOFF(cmod->ttype, MCDATA+MRCDATA+MNONE)) {
+ /* PARAMETER 3A: Minus exceptions.
+ */
+ TRACEMD("3A: -grp");
+ switch (pcbmd.action) {
+ case MGRP:
+ /* We cheat and use nnmgrp for this. */
+ mexgrp = copygrp((PETD *)nnmgrp,
+ u = parsegrp((PETD *)nnmgrp, &pcbgrnm, tbuf));
+ ++ds.pmexgcnt; ds.pmexcnt += u-1;
+ TRACEGRP(mexgrp);
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ default:
+ break;
+ }
+ /* PARAMETER 3B: Plus exceptions.
+ */
+ TRACEMD("3B: +grp");
+ switch (pcbmd.action) {
+ case PGRP:
+ pexgrp = copygrp((PETD *)nnmgrp,
+ u = parsegrp((PETD *)nnmgrp, &pcbgrnm, tbuf));
+ ++ds.pmexgcnt; ds.pmexcnt += u-1;
+ TRACEGRP(pexgrp);
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ default:
+ break;
+ }
+ }
+ /* PARAMETER 4: End of declaration.
+ */
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ if (es!=mdessv) synerr(37, &pcbmd);
+
+ /* EXECUTE: Store the definition for each element name specified.
+ */
+ TRACEGRP(nmgrp);
+ for (i = -1; nmgrp[++i];) {
+ etdgi = nmgrp[i]->etdgi;
+ if (*ranksuff) {
+ if ((tbuf[0] = *etdgi + ustrlen(ranksuff)) - 2 > NAMELEN) {
+ mderr(131, etdgi+1, ranksuff);
+ continue;
+ }
+ memcpy(tbuf+1, etdgi+1, *etdgi-1);
+ ustrcpy(tbuf+*etdgi-1, ranksuff);
+ etdcan(etdgi);
+ nmgrp[i] = etddef(tbuf);
+ }
+ if (nmgrp[i]->etdmod) {mderr(56, etdgi+1, (UNCH *)0); continue;}
+ etdset(nmgrp[i], fmin+ETDDCL, cmod, mexgrp, pexgrp, nmgrp[i]->etdsrm);
+ ++ds.etdcnt;
+ if (nmgrp[i]->adl) etdadl(nmgrp[i]); /* Check ETD conflicts. */
+ TRACEETD(nmgrp[i]);
+ }
+}
+
+VOID adlfree(al, aln)
+struct ad *al;
+int aln;
+{
+ for (; aln <= ADN(al); aln++) {
+ frem((UNIV)al[aln].adname);
+ if (ADVAL(al, aln))
+ frem((UNIV)ADVAL(al, aln));
+ if (BITON(ADFLAGS(al, aln), AGROUP)) {
+ int i;
+ for (i = 0; i < ADNUM(al, aln); i++)
+ frem((UNIV)al[aln + i + 1].adname);
+ aln += ADNUM(al, aln);
+ }
+ }
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/md2.c b/usr.bin/sgmls/sgmls/md2.c
new file mode 100644
index 0000000..94dc4d3
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/md2.c
@@ -0,0 +1,801 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+/* MDENTITY: Process ENTITY declaration.
+*/
+VOID mdentity(tbuf)
+UNCH *tbuf; /* Work area for tokenization[LITLEN+2]. */
+{
+ struct fpi fpicb; /* Formal public identifier structure. */
+ struct fpi *fpis = &fpicb; /* Ptr to current or #DEFAULT fpi. */
+ union etext etx; /* Ptr to entity text. */
+ UNCH estore = ESM; /* Entity storage class. */
+ struct entity *ecb; /* Ptr to entity control block. */
+ int parmsw = 0; /* 1=parameter entity declaration; 0 = not. */
+ int defltsw = 0; /* 1=#DEFAULT declaration; 0=not. */
+ PNE pne = 0; /* Ptr to N/C/SDATA entity control block. */
+
+ mdname = key[KENTITY]; /* Declaration name for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es for checking entity nesting. */
+ /* PARAMETER 1: Entity name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(nmbuf, ENTCASE, &pcblitp, NAMELEN);
+ TRACEMD("1: entity nm");
+ switch (pcbmd.action) {
+ case PEN:
+ parsemd(nmbuf + 1, ENTCASE, &pcblitp, NAMELEN);
+ if (pcbmd.action!=NAS) {mderr(120, (UNCH *)0, (UNCH *)0); return;}
+ if (nmbuf[1] == NAMELEN + 2) {
+ /* It was too long. */
+ nmbuf[0] = NAMELEN + 2;
+ nmbuf[NAMELEN + 1] = '\0';
+ mderr(65, (UNCH *)0, (UNCH *)0);
+ }
+ else
+ nmbuf[0] = nmbuf[1] + 1; /* Increment length for PERO. */
+ nmbuf[1] = lex.d.pero; /* Prefix PERO to name. */
+ parmsw = 1; /* Indicate parameter entity. */
+ case NAS:
+ break;
+ case RNS: /* Reserved name started. */
+ if (ustrcmp(nmbuf+1, key[KDEFAULT])) {
+ mderr(118, nmbuf+1, key[KDEFAULT]);
+ return;
+ }
+ memcpy(nmbuf, indefent, *indefent);/* Copy #DEFAULT to name buffer. */
+ fpis = &fpidf; /* Use #DEFAULT fpi if external. */
+ defltsw = 1; /* Indicate #DEFAULT is being defined.*/
+ break;
+ default:
+ mderr(122, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ subdcl = nmbuf+1; /* Subject name for error messages. */
+ /* PARAMETER 2: Entity text keyword (optional).
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ TRACEMD("2: keyword");
+ switch (pcbmd.action) {
+ case NAS:
+ if ((estore = (UNCH)mapsrch(enttab, tbuf+1))==0) {
+ estore = parmsw ? ESP : ESF;
+ pne = (PNE)rmalloc(NESZ);
+ if (mdextid(tbuf, fpis, nmbuf+1+parmsw, &estore, pne)==0)
+ return;
+ if (defltsw) etx.x = NULL;
+ else if ((etx.x = entgen(&fpicb))==0) {
+ if (parmsw)
+ mderr(148, nmbuf+2, (UNCH *)0);
+ else
+ mderr(147, nmbuf+1, (UNCH *)0);
+ }
+ goto parm4;
+ }
+ if (parmsw && (estore==ESX || estore==ESC)) {
+ mderr(38, tbuf+1, (UNCH *)0);
+ estore = ESM;
+ }
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ break;
+ default:
+ estore = ESM;
+ break;
+ }
+ /* PARAMETER 3: Parameter literal.
+ */
+ TRACEMD("3: literal");
+ switch (pcbmd.action) {
+ case LITE:
+ case LIT:
+ switch (estore) {
+ case ESM: /* LITERAL: parameter literal required. */
+ case ESC: /* CDATA: parameter literal required. */
+ case ESX: /* SDATA: parameter literal required. */
+ case ESI: /* PI: parameter literal required. */
+ etx.c = savestr(tbuf);
+ break;
+ case ESMD: /* MD: parameter literal required. */
+ etx.c = sandwich(tbuf, lex.m.mdo, lex.m.mdc);
+ goto bcheck;
+ case ESMS: /* MS: parameter literal required. */
+ etx.c = sandwich(tbuf, lex.m.mss, lex.m.mse);
+ goto bcheck;
+ case ESS: /* STARTTAG: parameter literal required. */
+ etx.c = sandwich(tbuf, lex.m.stag, lex.m.tagc);
+ goto bcheck;
+ case ESE: /* ENDTAG: parameter literal required. */
+ etx.c = sandwich(tbuf, lex.m.etag, lex.m.tagc);
+ bcheck:
+ if (etx.c == 0) {
+ mderr(225, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ break;
+ }
+ break;
+ default:
+ mderr(123, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* PARAMETER 4: End of declaration.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ parm4:
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ if (es!=mdessv) synerr(37, &pcbmd);
+
+ /* EXECUTE: If the entity already exists, ignore the new definition.
+ If it is a new entity, store the definition.
+ */
+ if ((ecb = entfind(nmbuf))!=0 && ecb->estore) {
+ if (ecb->dflt) {
+ mderr(228, nmbuf + 1, (UNCH *)0);
+ hout((THASH)etab, nmbuf, hash(nmbuf, ENTHASH));
+ if (ecb->estore == ESN) {
+ frem((UNIV)NEID(ecb->etx.n));
+ frem((UNIV)ecb->etx.n);
+ }
+ else if (ecb->estore >= ESFM)
+ frem((UNIV)ecb->etx.x);
+ frem((UNIV)ecb);
+ }
+ else {
+ /* Duplicate definition: not an error. */
+ if (sw.swdupent) mderr(68, nmbuf+1, (UNCH *)0);
+ if (estore<ESFM) frem((UNIV)etx.c);
+ return;
+ }
+ }
+ ++ds.ecbcnt; /* Do capacity before NOTATION. */
+ ds.ecbtext += estore<ESFM ? ustrlen(etx.c) : entlen;
+ ecb = entdef(nmbuf, estore, &etx); /* Define the entity. */
+ if (estore==ESN) { /* If entity is external: */
+ NEENAME(pne) = ecb->ename; /* Store entity name in ne. */
+ NEID(pne) = etx.x; /* Store system fileid in ne. */
+ NESYSID(pne) = fpis->fpisysis ? savestr(fpis->fpisysis) : 0;
+ NEPUBID(pne) = fpis->fpipubis ? savestr(fpis->fpipubis) : 0;
+ ecb->etx.n = pne; /* Store ne control block in etx. */
+ TRACEESN(pne);
+ }
+ else if (pne)
+ frem((UNIV)pne);
+ if (defltsw) {
+ ecbdeflt = ecb; /* If #DEFAULT save ecb. */
+ if (fpidf.fpipubis)
+ fpidf.fpipubis = savestr(fpidf.fpipubis);
+ if (fpidf.fpisysis)
+ fpidf.fpisysis = savestr(fpidf.fpisysis);
+ }
+}
+/* SANDWICH: Catenate a prefix and suffix to a string.
+ The result has an EOS but no length.
+ Return 0 if the result if longer than LITLEN.
+*/
+UNCH *sandwich(s, pref, suff)
+UNCH *s; /* String, with EOS. */
+UNCH *pref; /* Prefix, with length and EOS. */
+UNCH *suff; /* Suffix, with length and EOS. */
+{
+ UNCH *pt;
+ UNS slen, tlen;
+
+ slen = ustrlen(s);
+ tlen = slen + (*pref - 2) + (*suff - 2);
+ if (tlen > LITLEN)
+ return 0;
+ pt = (UNCH *)rmalloc(tlen + 1);
+ memcpy(pt, pref + 1, *pref - 2);
+ memcpy(pt + (*pref - 2), s, slen);
+ memcpy(pt + (*pref - 2) + slen, suff + 1, *suff - 1);
+ return pt;
+}
+/* MDEXTID: Process external identifier parameter of a markup declaration.
+ On entry, tbuf contains SYSTEM or PUBLIC if all is well.
+ NULL is returned if an error, otherwise fpis. If it is a
+ valid external data entity, the caller's estore is set to ESN
+ and its nxetype is set to the code for the external entity type.
+ The event that terminated the parse is preserved in pcb.action,
+ so the caller should process it before further parsing.
+*/
+struct fpi *mdextid(tbuf, fpis, ename, estore, pne)
+UNCH *tbuf; /* Work area for tokenization[2*(LITLEN+2)]. */
+struct fpi *fpis; /* FPI structure. */
+UNCH *ename; /* Entity or notation name, with EOS, no length.*/
+ /* NOTE: No PERO on parameter entity name. */
+UNCH *estore; /* DTD, general or parameter entity, DCN. */
+PNE pne; /* Caller's external entity ptr. */
+{
+ PDCB dcb; /* Ptr to DCN control block. */
+ int exidtype; /* External ID type: 0=none 1=system 2=public. */
+ int exetype; /* External entity type. */
+
+ MEMZERO((UNIV)fpis, (UNS)FPISZ); /* Initialize fpi structure. */
+ /* Move entity name into fpi (any PERO was stripped by caller). */
+ fpis->fpinm = ename;
+ entlen = 0; /* Initialize external ID length. */
+
+ /* PARAMETER 1: External identifier keyword or error.
+ */
+ TRACEMD("1: extid keyword");
+ if ((exidtype = mapsrch(exttab, tbuf+1))==0) {
+ mderr(29, (UNCH *)0, (UNCH *)0);
+ return (struct fpi *)0;
+ }
+ if (exidtype==EDSYSTEM) goto parm3;
+
+ /* PARAMETER 2: Public ID literal.
+ */
+ pcbmd.newstate = 0;
+ /* The length of a minimum literal cannot exceed the value of LITLEN
+ in the reference quantity set. */
+ parsemd(pubibuf, NAMECASE, &pcblitv, REFLITLEN);
+ TRACEMD("2: pub ID literal");
+ switch (pcbmd.action) {
+ case LITE: /* Use alternative literal delimiter. */
+ case LIT: /* Save literal as public ID string. */
+ entlen = ustrlen(pubibuf);
+ fpis->fpipubis = pubibuf;
+ break;
+ default:
+ mderr(117, (UNCH *)0, (UNCH *)0);
+ return (struct fpi *)0; /* Signal error to caller. */
+ }
+ /* PARAMETER 3: System ID literal.
+ */
+ parm3:
+ pcbmd.newstate = 0;
+ parsemd(sysibuf, NAMECASE, &pcblitc, LITLEN);
+ TRACEMD("3: sys ID literal");
+ if (pcbmd.action==LIT || pcbmd.action==LITE) {
+ entlen += ustrlen(sysibuf);
+ fpis->fpisysis = sysibuf;
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ }
+ else memcpy(tbuf, sysibuf, *sysibuf);
+ if (*estore!=ESF || pcbmd.action!=NAS) goto genfpi;
+
+ /* PARAMETER 4: Entity type keyword.
+ */
+ TRACEMD("4: Entity type");
+ if ((exetype = mapsrch(extettab, tbuf+1))==0) {
+ mderr(24, tbuf+1, (UNCH *)0);
+ return (struct fpi *)0;
+ }
+ if (exetype==ESNSUB && SUBDOC == NO) {
+ mderr(90, tbuf+1, (UNCH *)0);
+ return (struct fpi *)0;
+ }
+
+ NEXTYPE(pne) = (UNCH)exetype; /* Save entity type in caller's ne. */
+ *estore = ESN; /* Signal that entity is a data entity. */
+
+ if (exetype==ESNSUB) {
+ pne->nedcn = 0;
+ pcbmd.newstate = 0; /* Parse next token for caller. */
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+ goto genfpi;
+ }
+ /* PARAMETER 5: Notation name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("5: notation");
+ if (pcbmd.action!=NAS) {mderr(119, tbuf+1, (UNCH *)0); return (struct fpi *)0;}
+ /* Locate the data content notation. */
+ pne->nedcn = dcb = dcndef(lbuf);
+ /* Note that we have defined an entity with this notation.
+ If attributes are later defined for this notation, we'll
+ have to fix up this entity. */
+ dcb->entsw = 1;
+
+ /* PARAMETER 6: Data attribute specification.
+ */
+ pcbmd.newstate = 0;
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("6: [att list]");
+ if (pcbmd.action!=MDS) { /* No attributes specified. */
+ if (dcb->adl == 0)
+ NEAL(pne) = 0;
+ else {
+ initatt(dcb->adl);
+ adlval((int)ADN(al), (struct etd *)0);
+ storedatt(pne);
+ }
+ goto genfpi;
+ }
+ if (dcb->adl==0) { /* Atts specified, but none defined. */
+ mderr(22, (UNCH *)0, (UNCH *)0);
+ return (struct fpi *)0;
+ }
+ pcbstag.newstate = pcbstan; /* First separator is optional. */
+ if ((parseatt(dcb->adl, tbuf))==0)/* Empty list. */
+ mderr(91, (UNCH *)0, (UNCH *)0);
+ else {
+ adlval((int)ADN(al), (struct etd *)0);
+ storedatt(pne);
+ }
+ parse(&pcbeal); /* Parse the list ending. */
+ pcbmd.newstate = 0; /* Parse next token for caller. */
+ parsemd(tbuf, NAMECASE, &pcblitp, LITLEN);
+
+ /* GENFPI: Builds a formal public identifier structure, including the
+ entity name, offsets of the components of the public ID, and
+ other data a system might use to identify the actual file.
+ */
+ genfpi:
+ TRACEMD("7: generate fpi");
+ fpis->fpistore = *estore - ESFM + 1; /* External entity type: 1-6. */
+ if (*estore == ESN) {
+ if (NEXTYPE(pne) == ESNSUB)
+ fpis->fpinedcn = 0;
+ else
+ fpis->fpinedcn = NEDCN(pne) + 1;
+ }
+ /* Analyze public ID and make structure entries. */
+ if (exidtype==EDPUBLIC) {
+ if (FORMAL==NO)
+ fpis->fpiversw = -1;
+ else if (parsefpi(fpis)>0) {
+ mderr(88, fpis->fpipubis, (UNCH *)0);
+ fpis->fpiversw = -1; /* Signal bad formal public ID. */
+ }
+ }
+ return fpis;
+}
+
+/* Store a data attribute. */
+
+VOID storedatt(pne)
+PNE pne;
+{
+ int i;
+
+ NEAL(pne) = (struct ad *)rmalloc((1+ADN(al))*ADSZ);
+ memcpy((UNIV)NEAL(pne), (UNIV)al, (1+ADN(al))*ADSZ);
+ for (i = 1; i <= (int)ADN(al); i++) {
+ if (GET(ADFLAGS(al, i), ASPEC))
+ ds.attdef += ADLEN(al, i);
+ if (NEAL(pne)[i].addef != 0)
+ NEAL(pne)[i].addef = savestr(NEAL(pne)[i].addef);
+ }
+ ds.attcnt += AN(al); /* Number of attributes defined. */
+#if 0
+ /* I can't see any reason to increase AVGRPCNT here. */
+ ds.attgcnt += ADN(al) - AN(al); /* Number of att grp members. */
+#endif
+}
+
+/* PARSEFPI: Parses a formal public identifier and builds a control block.
+ PARSEFPI returns a positive error code (1-10), or 0 if no errors.
+ It set fpiversw if no version was specified in the ID and the
+ public text is in a class that permits display versions.
+ Note: An empty version ("//") can be specified (usually it is
+ the non-device-specific form, such as a definitional entity set).
+*/
+int parsefpi(f)
+PFPI f; /* Ptr to formal public identifier structure. */
+{
+ UNCH *l; /* Pointer to EOS of public identifier. */
+ UNCH *p, *q; /* Ptrs to current field in public identifier. */
+ UNS len; /* Field length */
+
+ p = f->fpipubis; /* Point to start of identifier. */
+ l = p + ustrlen(p); /* Point to EOS of identifier. */
+ if (*p=='+' || *p=='-') { /* If owner registered, unregistered. */
+ f->fpiot = *p; /* Save owner type. */
+ if ((p += 3)>=l) return 1; /* Get to owner ID field. */
+ }
+ else f->fpiot = '!'; /* Indicate ISO owner identifier. */
+ if ((q = pubfield(p, l, '/', &len))==0) /* Find end of owner ID field. */
+ return 2;
+ f->fpiol = len; /* Save owner ID length. */
+ f->fpio = p - f->fpipubis; /* Save offset in pubis to owner ID. */
+
+ if ((p = pubfield(q, l, ' ', &len))==0) /* Find end of text class field. */
+ return 3;
+ *(--p) = EOS; /* Temporarily make class a string. */
+ f->fpic = mapsrch(pubcltab, q); /* Check for valid uc class name.*/
+ *p++ = ' '; /* Restore the SPACE delimiter. */
+ if (f->fpic==0) return 4; /* Error if not valid uc class name.*/
+
+ /* The public text class in a notation identifier must be NOTATION. */
+ if (f->fpistore == ESK - ESFM + 1 && f->fpic != FPINOT) return 10;
+
+ if (*p=='-') { /* If text is unavailable public text.*/
+ f->fpitt = *p; /* Save text type. */
+ if ((p += 3)>=l) return 5; /* Get to text description field. */
+ }
+ else f->fpitt = '+'; /* Indicate available public text. */
+ if ((q = pubfield(p, l, '/', &len))==0) /* Find end of text description. */
+ return 6;
+ f->fpitl = len; /* Save text description length. */
+ f->fpit = p - f->fpipubis; /* Save ptr to description.*/
+
+ p = pubfield(q, l, '/', &len); /* Bound language field. */
+ if (f->fpic != FPICHARS) {
+ int i;
+ /* Language must be all upper-case letters. */
+ /* The standard only says that it *should* be two letters, so
+ don't enforce that. */
+ for (i = 0; i < len; i++) {
+ /* Don't assume ASCII. */
+ if (!strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", q[i]))
+ return 7;
+ }
+ }
+ f->fpill = len;
+ f->fpil = q - f->fpipubis;
+ if (p!=0) { /* If there is a version field: */
+ if (f->fpic<FPICMINV) /* Error if class prohibits versions. */
+ return 8;
+ if ((pubfield(p, l, '/', &len))!=0) /* Bound version field. */
+ return 9; /* Error if yet another field. */
+ f->fpivl = len; /* Save version length. */
+ f->fpiv = p - f->fpipubis; /* Save ptr (in pubis) to version. */
+ }
+ else if (f->fpic>=FPICMINV) f->fpiversw = 1;/* No version: get the best. */
+ return(0);
+}
+/* PUBFIELD: Returns ptr to next field, or NULL if ID has ended.
+*/
+#ifdef USE_PROTOTYPES
+UNCH *pubfield(UNCH *p, UNCH *l, UNCH d, UNS *lenp)
+#else
+UNCH *pubfield(p, l, d, lenp)
+UNCH *p; /* Public identifier field (no length or EOS). */
+UNCH *l; /* Pointer to EOS of public identifier. */
+UNCH d; /* Field delimiter: ' ' or '/'. */
+UNS *lenp; /* Gets field length */
+#endif
+{
+ UNCH *psv = p+1; /* Save starting value of p. */
+
+ while (p<l) {
+ if (*p++==d) { /* Test for delimiter character. */
+ *lenp = p - psv; /* Save field length (no len or EOS). */
+ if (d=='/' && *p++!=d) /* Solidus requires a second one. */
+ continue;
+ return(p); /* Return ptr to next field. */
+ }
+ }
+ *lenp = p - --psv; /* Save field length (no len or EOS). */
+ return NULL;
+}
+/* MDMS: Process marked section start.
+ If already in special parse, bump the level counters and return
+ without parsing the declaration.
+*/
+struct parse *mdms(tbuf, pcb)
+UNCH *tbuf; /* Work area for tokenization [NAMELEN+2]. */
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ int key; /* Index of keyword in mslist. */
+ int ptype; /* Parameter token type. */
+ int pcbcode = 0; /* Parse code: 0=same; 2-4 per defines. */
+
+ if (++mslevel>TAGLVL) {
+ --mslevel;
+ sgmlerr(27, (struct parse *)0, ntoa(TAGLVL), (UNCH *)0);
+ }
+
+ /* If already in IGNORE mode, return without parsing parameters. */
+ if (msplevel) {++msplevel; return(pcb);}
+
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es for checking entity nesting. */
+ pcbmd.newstate = pcbmdtk; /* First separator is optional. */
+
+ /* PARAMETERS: TEMP, RCDATA, CDATA, IGNORE, INCLUDE, or MDS. */
+ while ((ptype = parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN))==NAS){
+ if ((key = mapsrch(mstab, tbuf+1))==0) {
+ sgmlerr(64, (struct parse *)0, ntoa(parmno), tbuf+1);
+ continue;
+ }
+ if (key==MSTEMP) continue; /* TEMP: for documentation. */
+ msplevel = 1; /* Special parse required. */
+ if (key>pcbcode) pcbcode = key; /* Update if higher priority. */
+ }
+ if (ptype!=MDS) {
+ NEWCC; /* Syntax error did REPEATCC. */
+ sgmlerr(97, (struct parse *)0, lex.m.dso, (UNCH *)0);
+ REPEATCC; /* 1st char of marked section. */
+ }
+ if (es!=mdessv) synerr(37, pcb);
+ TRACEMS(1, pcbcode, mslevel, msplevel);
+ if (pcbcode==MSIGNORE) pcb = &pcbmsi;
+ else if (pcbcode) {
+ pcb = pcbcode==MSCDATA ? &pcbmsc : (rcessv = es, &pcbmsrc);
+ }
+ return(pcb); /* Tell caller whether to change the parse. */
+}
+/* MDMSE: Process marked section end.
+ Issue an error if no marked section had started.
+*/
+int mdmse()
+{
+ int retcode = 0; /* Return code: 0=same parse; 1=cancel special. */
+
+ if (mslevel) --mslevel;
+ else sgmlerr(26, (struct parse *)0, (UNCH *)0, (UNCH *)0);
+
+ if (msplevel) if (--msplevel==0) retcode = 1;
+ TRACEMS(0, retcode, mslevel, msplevel);
+ return retcode;
+}
+/* MDNOT: Process NOTATION declaration.
+*/
+VOID mdnot(tbuf)
+UNCH *tbuf; /* Work area for tokenization[LITLEN+2]. */
+{
+ struct fpi fpicb; /* Formal public identifier structure. */
+ PDCB dcb; /* Ptr to notation entity in dcntab. */
+ UNCH estore = ESK; /* Entity storage class. */
+
+ mdname = key[KNOTATION]; /* Identify declaration for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es for checking entity nesting. */
+
+ /* PARAMETER 1: Notation name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: name");
+ if (pcbmd.action!=NAS) {mderr(120, (UNCH *)0, (UNCH *)0); return;}
+ subdcl = lbuf+1; /* Save notation name for error msgs. */
+
+ /* PARAMETER 2: External identifier keyword.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: extid");
+ if (pcbmd.action!=NAS) {mderr(29, (UNCH *)0, (UNCH *)0); return;}
+ if (mdextid(tbuf, &fpicb, lbuf+1, &estore, (PNE)0)==0) return;
+
+ /* PARAMETER 3: End of declaration.
+ Token was parsed by MDEXTID.
+ */
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ if (es!=mdessv) synerr(37, &pcbmd);
+
+ /* EXECUTE: Store notation name.
+ */
+ if ((dcb = dcnfind(lbuf)) != 0 && dcb->defined) {
+ mderr(56, lbuf+1, (UNCH *)0);
+ return;
+ }
+ /* else */
+ dcb = dcndef(lbuf);
+ dcb->defined = 1;
+ dcb->sysid = fpicb.fpisysis ? savestr(fpicb.fpisysis) : 0;
+ dcb->pubid = fpicb.fpipubis ? savestr(fpicb.fpipubis) : 0;
+ ++ds.dcncnt;
+ ds.dcntext += entlen;
+ TRACEDCN(dcb);
+ return;
+}
+/* DCNDEF: Define a notation and return its DCNCB.
+ If caller does not care if it already exists,
+ he should specify NULL for the notation text
+ so we don't clobber the existing text (if any).
+*/
+struct dcncb *dcndef(nname)
+UNCH *nname; /* Notation name (with length and EOS). */
+{
+ return((PDCB)hin((THASH)dcntab, nname, 0, DCBSZ));
+}
+/* DCNFIND: If a notation was declared, return its DCNCB.
+ Return NULL if it is not defined.
+*/
+struct dcncb *dcnfind(nname)
+UNCH *nname; /* Notation name (with length and EOS). */
+{
+ return((PDCB)hfind((THASH)dcntab, nname, 0));
+}
+#define SRM(i) (srhptr->srhsrm[i]) /* Current entry in SHORTREF map. */
+/* MDSRMDEF: Process short reference mapping declaration.
+*/
+VOID mdsrmdef(tbuf)
+UNCH *tbuf; /* Work area for tokenization[LITLEN+2]. */
+{
+ struct entity *entcb; /* Ptr to defined entity. */
+ PSRH srhptr; /* Ptr to short reference map hdr (in srhtab).*/
+ int srn; /* Short reference delimiter number in srdeltab.*/
+ int mapused = 0; /* Has map already been used? */
+
+ mdname = key[KSHORTREF]; /* Identify declaration for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ if (!sd.shortref) {mderr(198, (UNCH *)0, (UNCH *)0); return;}
+ mdessv = es; /* Save es for checking entity nesting. */
+ /* PARAMETER 1: SHORTREF map name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: map nm");
+ if (pcbmd.action!=NAS) {mderr(120, (UNCH *)0, (UNCH *)0); return;}
+ if ((srhptr = srhfind(tbuf))!=0) {
+ mapused = 1;
+ /* Error if map was declared (not just used). */
+ if (SRM(0)) {mderr(56, tbuf+1, (UNCH *)0); return;}
+ }
+ else srhptr = srhdef(tbuf); /* Create map with SRs mapped to NULL.*/
+ SRM(0) = (PECB)srhptr; /* Indicate map was actually declared.*/
+ subdcl = srhptr->ename+1; /* Save map name for error msgs. */
+
+ while ( pcbmd.newstate = 0,
+ parsemd(tbuf, NAMECASE, &pcblitp, SRMAXLEN)==LIT
+ || pcbmd.action==LITE ) {
+ /* PARAMETER 2: Delimiter string.
+ */
+ TRACEMD("2: SR string");
+ if ((srn = mapsrch(lex.s.dtb, tbuf))==0) {
+ mderr(124, tbuf, (UNCH *)0);
+ goto cleanup;
+ }
+ /* PARAMETER 3: Entity name.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, ENTCASE, &pcblitp, NAMELEN);
+ TRACEMD("3: entity");
+ if (pcbmd.action!=NAS) {mderr(120, (UNCH *)0, (UNCH *)0); goto cleanup;}
+ if ((entcb = entfind(tbuf))==0) {
+ union etext etx;
+ etx.x = 0;
+ entcb = entdef(tbuf, '\0', &etx);
+ }
+ if (SRM(srn)) {
+ mderr(56, (srn<lex.s.prtmin ? (UNCH *)lex.s.pdtb[srn]
+ : lex.s.dtb[srn].mapnm), (UNCH *)0);
+ continue;
+ }
+ SRM(srn) = entcb;
+ if (srn>=lex.s.fce && srn!=lex.s.hyp && srn!=lex.s.hyp2
+ && srn!=lex.s.lbr && srn!=lex.s.rbr)
+ lexcnm[*lex.s.dtb[srn].mapnm] = lex.l.fce;
+ else if (srn==lex.s.spc) lexcnm[' '] = lex.l.spcr;
+ }
+ /* PARAMETER 4: End of declaration.
+ */
+ TRACEMD(emd);
+ if (parmno==2)
+ {mderr((UNS)(pcbmd.action==EMD ? 28:123), (UNCH *)0, (UNCH *)0); goto cleanup;}
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ if (es!=mdessv) synerr(37, &pcbmd);
+ ++ds.srcnt;
+ TRACESRM("SHORTREF", srhptr->srhsrm, (UNCH *)0);
+ return;
+
+ cleanup:
+ /* Don't free the map if the map was in use (because of a USEMAP
+ declaration) before this declaration. */
+ if (mapused)
+ MEMZERO((UNIV)srhptr->srhsrm, sizeof(PECB)*(lex.s.dtb[0].mapdata+1));
+ else {
+ frem((UNIV)srhptr->srhsrm);
+ hout((THASH)srhtab, srhptr->ename, 0);
+ frem((UNIV)srhptr);
+ }
+}
+/* MDSRMUSE: Activate a short reference map.
+*/
+VOID mdsrmuse(tbuf)
+UNCH *tbuf; /* Work area for tokenization[LITLEN+2]. */
+{
+ PSRH srhptr; /* Ptr to short reference map hdr (in srhtab).*/
+ TECB srmptr; /* Ptr to short reference map (in header). */
+ int i; /* Loop counter; temporary variable. */
+
+ mdname = key[KUSEMAP]; /* Identify declaration for messages. */
+ subdcl = NULL; /* No subject as yet. */
+ parmno = 0; /* No parameters as yet. */
+ mdessv = es; /* Save es for checking entity nesting. */
+ /* PARAMETER 1: SHORTREF map name or "#EMPTY".
+ */
+ pcbmd.newstate = 0;
+ parsemd(lbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("1: map nm");
+ subdcl = lbuf+1; /* Subject name for error messages. */
+ switch (pcbmd.action) {
+ case RNS: /* Empty SHORTREF map requested. */
+ if (ustrcmp(lbuf+1, key[KEMPTY])) {
+ mderr(118, lbuf+1, key[KEMPTY]);
+ return;
+ }
+ srmptr = SRMNULL;
+ break;
+ case NAS: /* Map name specified; save if undefined. */
+ if ((srhptr = srhfind(lbuf))==0) {
+ if (!indtdsw) {mderr(125, (UNCH *)0, (UNCH *)0); return;}
+ srmptr = NULL;
+ }
+ else
+ srmptr = srhptr->srhsrm;
+ break;
+ default:
+ mderr(120, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* PARAMETER 2: Element name or a group of them. (In DTD only.)
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD("2: GI or grp");
+ switch (pcbmd.action) {
+ case NAS:
+ if (!indtdsw) {mderr(142, (UNCH *)0, (UNCH *)0); return;}
+ nmgrp[0] = etddef(tbuf);
+ nmgrp[1] = (PETD)NULL;
+ break;
+ case GRPS:
+ if (!indtdsw) {mderr(142, (UNCH *)0, (UNCH *)0); return;}
+ parsegrp(nmgrp, &pcbgrnm, tbuf);
+ break;
+ case EMD:
+ if (indtdsw) {mderr(28, (UNCH *)0, (UNCH *)0); return;}
+ tags[ts].tsrm = srmptr;
+ TRACESRM("USEMAP", tags[ts].tsrm, tags[ts].tetd->etdgi+1);
+ goto realemd;
+ default:
+ mderr(indtdsw ? 121 : 126, (UNCH *)0, (UNCH *)0);
+ return;
+ }
+ /* PARAMETER 3: End of declaration.
+ */
+ pcbmd.newstate = 0;
+ parsemd(tbuf, NAMECASE, &pcblitp, NAMELEN);
+ TRACEMD(emd);
+ if (pcbmd.action!=EMD) mderr(126, (UNCH *)0, (UNCH *)0);
+ /* If map has not yet been defined, do it and get map pointer. */
+ if (!srmptr) srmptr = (srhdef(lbuf))->srhsrm;
+
+ /* Store the map pointer for each element name specified.
+ */
+ TRACEGRP(nmgrp);
+ for (i = -1; nmgrp[++i];) {
+ if (!nmgrp[i]->etdsrm) nmgrp[i]->etdsrm = srmptr;
+ else if (sw.swdupent) mderr(68, nmgrp[i]->etdgi+1, (UNCH *)0);
+ }
+ realemd:
+ if (es!=mdessv) synerr(37, &pcbmd);
+}
+/* SRHDEF: Define a SHORTREF map and return ptr to its header.
+ All entries in map are mapped to NULL.
+ Caller must determine whether it already exists.
+*/
+PSRH srhdef(sname)
+UNCH *sname; /* SHORTREF map name (with length and EOS). */
+{
+ PSRH srh; /* Ptr to SHORTREF map hdr in srhtab. */
+
+ (srh = (PSRH)hin((THASH)srhtab, sname, 0, SRHSZ))->srhsrm =
+ (TECB)rmalloc((UNS)(lex.s.dtb[0].mapdata+1)*sizeof(PECB));
+ return(srh);
+}
+/* SRHFIND: If a SHORTREF map was declared, return the ptr to its header.
+ Return NULL if it is not defined.
+*/
+PSRH srhfind(sname)
+UNCH *sname; /* SHORTREF map name (with length and EOS). */
+{
+ return((PSRH)hfind((THASH)srhtab, sname, 0));
+}
+#undef SRM
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/msg.h b/usr.bin/sgmls/sgmls/msg.h
new file mode 100644
index 0000000..fa97a4c
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/msg.h
@@ -0,0 +1,252 @@
+/*
+Severity codes:
+I information (not an SGML error at all)
+W warning (an SGML markup error but it knows what you mean)
+E error
+C critical (fatal)
+
+Type codes:
+R resource
+C ?context/content
+M minimization
+Q quantity
+S syntax
+D declaration
+U unsupported feature
+*/
+struct {
+ char *text;
+ char severity;
+ char type;
+} messages[] = {
+/* 0 */ {0},
+/* 1 */ {"%s element not allowed at this point in %s element", 'E', 'C'},
+/* 2 */ {"%s markup declaration not permitted here; declaration ended", 'E', 'D'},
+/* 3 */ {"Length of name, number, or token exceeded NAMELEN or LITLEN limit", 'E', 'Q'},
+/* 4 */ {"Non-SGML character ignored", 'E', 'S'},
+/* 5 */ {"%s end-tag ignored: doesn't end any open element (current is %s)", 'E', 'C'},
+/* 6 */ {"%s start-tag exceeds open element limit; possible lies from %s on", 'E', 'Q'},
+/* 7 */ {"Start-tag omitted from %s with empty content", 'E', 'M'},
+/* 8 */ {"Illegal entity end in markup or delimited text", 'E', 'S'},
+/* 9 */ {"Incorrect character in markup; markup terminated", 'E', 'S'},
+/* 10 */ {"Data not allowed at this point in %s element", 'E', 'C'},
+/* 11 */ {"No element declaration for %s end-tag GI; end-tag ignored", 'E', 'C'},
+/* 12 */ {"%s name ignored: not a syntactically valid SGML name", 'E', 'S'},
+/* 13 */ {"%s = \"%s\" attribute ignored: not defined for this element", 'E', 'C'},
+/* 14 */ {"%s = \"%s\" attribute value defaulted: invalid character", 'E', 'S'},
+/* 15 */ {"%s = \"%s\" attribute value defaulted: token too long", 'E', 'Q'},
+/* 16 */ {"%s = \"%s\" attribute value defaulted: too many tokens", 'E', 'C'},
+/* 17 */ {"%s = \"%s\" attribute value defaulted: wrong token type", 'E', 'C'},
+/* 18 */ {"%s = \"%s\" attribute value defaulted: token not in group", 'E', 'C'},
+/* 19 */ {"Required %s attribute was not specified; may affect processing", 'E', 'C'},
+/* 20 */ {"%s end-tag implied by %s end-tag; not minimizable", 'E', 'M'},
+/* 21 */ {"%s start-tag implied by %s start-tag; not minimizable", 'W', 'M'},
+/* 22 */ {"Possible attributes treated as data because none were defined", 'E', 'C'},
+/* 23 */ {"Duplicate specification occurred for \"%s\"; may affect processing", 'E', 'D'},
+/* 24 */ {"\"%s\" keyword invalid; declaration terminated", 'E', 'D'},
+/* 25 */ {"%s = \"%s\" attribute defaulted: empty string not allowed for token", 'E', 'C'},
+/* 26 */ {"Marked section end ignored; not in a marked section", 'E', 'S'},
+/* 27 */ {"Marked section start ignored; %s marked sections open already", 'E', 'Q'},
+/* 28 */ {"One or more parameters missing; declaration ignored", 'E', 'D'},
+/* 29 */ {"\"PUBLIC\" or \"SYSTEM\" required; declaration terminated", 'E', 'D'},
+/* 30 */ {"%s element ended prematurely; required %s omitted", 'E', 'C'},
+/* 31 */ {"Entity \"%s\" terminated: could not read file", 'E', 'R'},
+/* 32 */ {"Could not open file for entity \"%s\"; entity reference ignored", 'E', 'R'},
+/* 33 */ {"Insufficient main memory; unable to continue parsing", 'C', 'R'},
+/* 34 */ {"%s entity reference ignored; exceeded open entity limit (%s)", 'E', 'Q'},
+/* 35 */ {"No declaration for entity \"%s\"; reference ignored", 'E', 'C'},
+/* 36 */ {"%s entity reference occurred within own text; reference ignored", 'E', 'C'},
+/* 37 */ {"Entity nesting level out of sync", 'E', 'S'},
+/* 38 */ {"Parameter entity text cannot have %s keyword; keyword ignored", 'E', 'D'},
+/* 39 */ {"%s end-tag implied by %s start-tag; not minimizable", 'W', 'M'},
+/* 40 */ {"Start-tag minimization ignored; element has required attribute", 'E', 'D'},
+/* 41 */ {"Required %s element cannot be excluded from %s element", 'E', 'C'},
+/* 42 */ {"No DOCTYPE declaration; document type is unknown", 'E', 'C'},
+/* 43 */ {"Undefined %1$s start-tag GI was used in DTD; \"%1$s O O ANY\" assumed", 'E', 'C'},
+/* 44 */ {"Invalid character(s) ignored; attempting to resume DOCTYPE subset", 'E', 'S'},
+/* 45 */ {"No declaration for entity \"%s\"; default definition used", 'I', 'C'},
+/* 46 */ {"%s end-tag implied by NET delimiter; not minimizable", 'W', 'M'},
+/* 47 */ {"%s end-tag implied by data; not minimizable", 'W', 'M'},
+/* 48 */ {"%s end-tag implied by short start-tag (no GI); not minimizable", 'W', 'M'},
+/* 49 */ {"%s start-tag implied by data; not minimizable", 'W', 'M'},
+/* 50 */ {"%s start-tag implied by short start-tag (no GI); not minimizable", 'W', 'M'},
+/* 51 */ {"Short end-tag (no GI) ignored: no open elements", 'E', 'C'},
+/* 52 */ {"No definition for %1$s document type; \"%1$s O O ANY\" assumed", 'E', 'C'},
+/* 53 */ {"No definition for %1$s implied start-tag; \"%1$s O O ANY\" assumed", 'E', 'C'},
+/* 54 */ {"%s element ended prematurely; required subelement omitted", 'E', 'C'},
+/* 55 */ {"Content model token %s: connectors conflict; first was used", 'E', 'D'},
+/* 56 */ {"Duplicate specification occurred for \"%s\"; duplicate ignored", 'E', 'D'},
+/* 57 */ {"Bad end-tag in R/CDATA element; treated as short (no GI) end-tag", 'E', 'S'},
+/* 58 */ {"Start-tag minimization should be \"-\" for element with declared content", 'I', 'D'},
+/* 59 */ {"Reference to PI entity not permitted here; reference ignored", 'E', 'S'},
+/* 60 */ {"Non-SGML character found; should have been character reference", 'W', 'S'},
+/* 61 */ {"Numeric character reference exceeds 255; reference ignored", 'E', 'S'},
+/* 62 */ {"Invalid alphabetic character reference ignored", 'E', 'S'},
+/* 63 */ {"Invalid character in minimum literal; character ignored", 'E', 'S'},
+/* 64 */ {"Keyword %s ignored; \"%s\" is not a valid marked section keyword", 'E', 'D'},
+/* 65 */ {"Parameter entity name longer than (NAMELEN-1); truncated", 'E', 'Q'},
+/* 66 */ {"Start-tag length exceeds TAGLEN limit; parsed correctly", 'W', 'Q'},
+/* 67 */ {"%s attribute defaulted: FIXED attribute must equal default", 'W', 'C'},
+/* 68 */ {"Duplicate specification occurred for \"%s\"; duplicate ignored", 'I', 'D'},
+/* 69 */ {"%s = \"%s\" IDREF attribute ignored: referenced ID does not exist", 'E', 'C'},
+/* 70 */ {"%s = \"%s\" IDREF attribute ignored: number of IDs in list exceeds GRPCNT limit", 'E', 'Q'},
+/* 71 */ {"%s = \"%s\" ID attribute ignored: ID in use for another element", 'E', 'C'},
+/* 72 */ {"%s = \"%s\" ENTITY attribute not general entity; may affect processing", 'E', 'C'},
+/* 73 */ {"%s = \"%s\" attribute ignored: previously specified in same list", 'W', 'C'},
+/* 74 */ {"\"?\" = \"%s\" name token ignored: not in any group in this list", 'E', 'C'},
+/* 75 */ {"Normalized attribute specification length over ATTSPLEN limit", 'E', 'Q'},
+/* 76 */ {"%s = \"%s\" NOTATION ignored: element content is empty", 'E', 'C'},
+/* 77 */ {"%s = \"%s\" NOTATION undefined: may affect processing", 'E', 'C'},
+/* 78 */ {"Entity \"%2$s\" has undefined notation \"%1$s\"", 'E', 'C'},
+/* 79 */ {"%s = \"%s\" default attribute value not in group; #IMPLIED used", 'E', 'C'},
+/* 80 */ {"#CURRENT default value treated as #IMPLIED for %s ID attribute", 'E', 'D'},
+/* 81 */ {"ID attribute %s cannot have a default value; treated as #IMPLIED", 'E', 'D'},
+/* 82 */ {"%s attribute must be token, not empty string; treated as #IMPLIED", 'E', 'D'},
+/* 83 */ {"NOTATION attribute ignored for EMPTY element", 'E', 'D'},
+/* 84 */ {"%s = \"%s\" NOTATION ignored: content reference specified", 'E', 'C'},
+/* 85 */ {"#CONREF default value treated as #IMPLIED for EMPTY element", 'W', 'D'},
+/* 86 */ {"%s = \"%s\" entity not data entity; may affect processing", 'E', 'C'},
+/* 87 */ {"End-tag minimization should be \"O\" for EMPTY element", 'I', 'D'},
+/* 88 */ {"Formal public identifier \"%s\" invalid; treated as informal", 'E', 'S'},
+/* 89 */ {"Out-of-context %2$s start-tag ended %1$s document element (and parse)", 'E', 'C'},
+/* 90 */ {"\"%s\" keyword is for unsupported feature; declaration terminated", 'E', 'D'},
+/* 91 */ {"Attribute specification list in prolog cannot be empty", 'E', 'D'},
+/* 92 */ {"Document ended invalidly within a literal; parsing ended", 'C', 'S'},
+/* 93 */ {"Short ref in map \"%2$s\" to undeclared entity \"%1$s\" treated as data", 'E', 'C'},
+/* 94 */ {"Could not reopen file to continue entity \"%s\"; entity terminated", 'E', 'R'},
+/* 95 */ {"Out-of-context data ended %s document element (and parse)", 'E', 'C'},
+/* 96 */ {"Short start-tag (no GI) ended %s document element (and parse)", 'E', 'C'},
+/* 97 */ {"DSO delimiter (%s) omitted from marked section declaration", 'E', 'D'},
+/* 98 */ {"Group token %s: duplicate name or name token \"%s\" ignored", 'E', 'D'},
+/* 99 */ {"Attempt to redefine %s attribute ignored", 'E', 'D'},
+/* 100 */ {"%s definition ignored: %s is not a valid declared value keyword", 'E', 'D'},
+/* 101 */ {"%s definition ignored: NOTATION attribute already defined", 'E', 'D'},
+/* 102 */ {"%s definition ignored: ID attribute already defined", 'E', 'D'},
+/* 103 */ {"%s definition ignored: no declared value specified", 'E', 'D'},
+/* 104 */ {"%s definition ignored: invalid declared value specified", 'E', 'D'},
+/* 105 */ {"%s definition ignored: number of names or name tokens in group exceeded GRPCNT limit", 'E', 'D'},
+/* 106 */ {"%s definition ignored: name group omitted for NOTATION attribute", 'E', 'D'},
+/* 107 */ {"#CONREF default value treated as #IMPLIED for %s ID attribute", 'E', 'D'},
+/* 108 */ {"%s definition ignored: %s is not a valid default value keyword", 'E', 'D'},
+/* 109 */ {"%s definition ignored: no default value specified", 'E', 'D'},
+/* 110 */ {"%s definition ignored: invalid default value specified", 'E', 'D'},
+/* 111 */ {"More than ATTCNT attribute names and/or name (token) values; terminated", 'E', 'D'},
+/* 112 */ {"Attempted redefinition of attribute definition list ignored", 'E', 'D'},
+/* 113 */ {"Content model token %s: more than GRPCNT model group tokens; terminated", 'E', 'Q'},
+/* 114 */ {"Content model token %s: more than GRPGTCNT content model tokens; terminated", 'E', 'Q'},
+/* 115 */ {"Content model token %s: more than GRPLVL nested model groups; terminated", 'E', 'Q'},
+/* 116 */ {"Content model token %s: %s invalid; declaration terminated", 'E', 'D'},
+/* 117 */ {"\"PUBLIC\" specified without public ID; declaration terminated", 'E', 'D'},
+/* 118 */ {"\"%s\" keyword invalid (only %s permitted); declaration terminated", 'E', 'D'},
+/* 119 */ {"\"%s\" specified without notation name; declaration terminated", 'E', 'D'},
+/* 120 */ {"Parameter must be a name; declaration terminated", 'E', 'D'},
+/* 121 */ {"Parameter must be a GI or a group of them; declaration terminated", 'E', 'D'},
+/* 122 */ {"Parameter must be a name or PERO (%%); declaration terminated", 'E', 'D'},
+/* 123 */ {"Parameter must be a literal; declaration terminated", 'E', 'D'},
+/* 124 */ {"\"%s\" not valid short reference delimiter; declaration terminated", 'E', 'D'},
+/* 125 */ {"Map does not exist; declaration ignored", 'E', 'C'},
+/* 126 */ {"MDC delimiter (>) expected; following text may be misinterpreted", 'E', 'D'},
+/* 127 */ {"Document ended invalidly within prolog; parsing ended", 'C', 'S'},
+/* 128 */ {"\"PUBLIC\" or \"SYSTEM\" or DSO ([) required; declaration terminated", 'E', 'D'},
+/* 129 */ {"Minimization must be \"-\" or \"O\" (not \"%s\"); declaration terminated", 'E', 'D'},
+/* 130 */ {"Content model or keyword expected; declaration terminated", 'E', 'D'},
+/* 131 */ {"Rank stem \"%s\" + suffix \"%s\" more than NAMELEN characters; not defined", 'E', 'D'},
+/* 132 */ {"Undefined %s start-tag GI ignored; not used in DTD", 'E', 'C'},
+/* 133 */ {"Document ended invalidly within a markup declaration; parsing ended", 'C', 'S'},
+/* 134 */ {"Normalized length of literal exceeded %s; markup terminated", 'E', 'Q'},
+/* 135 */ {"R/CDATA marked section in declaration subset; prolog terminated", 'E', 'D'},
+/* 136 */ {"%s = \"%s\" ENTITIES attribute ignored: more than GRPCNT in list", 'E', 'Q'},
+/* 137 */ {"Content model is ambiguous", 'W', 'D'},
+/* 138 */ {"Invalid parameter entity name \"%s\"", 'E', 'S'},
+/* 139 */ {"Document ended invalidly within a marked section; parsing ended", 'C', 'S'},
+/* 140 */ {"Element \"%s\" used in DTD but not defined", 'I', 'D'},
+/* 141 */ {"Invalid NDATA or SUBDOC entity reference occurred; ignored", 'E', 'S'},
+/* 142 */ {"Associated element type not allowed in document instance", 'E', 'C'},
+/* 143 */ {"Illegal DSC character; in different entity from DSO", 'E', 'C'},
+/* 144 */ {"Declared value of data attribute cannot be ID", 'E', 'D' },
+/* 145 */ {"Invalid reference to external CDATA or SDATA entity; ignored", 'E', 'S'},
+/* 146 */ {"Could not find external document type \"%s\"", 'E', 'R'},
+/* 147 */ {"Could not find external general entity \"%s\"", 'I', 'R'},
+/* 148 */ {"Could not find external parameter entity \"%s\"", 'I', 'R'},
+/* 149 */ {"Reference to non-existent general entity \"%s\" ignored", 'E', 'R'},
+/* 150 */ {"Could not find entity \"%s\" using default declaration", 'E', 'R'},
+/* 151 */ {"Could not find entity \"%2$s\" in attribute %1$s using default declaration", 'E', 'R'},
+/* 152 */ {"Short reference map \"%s\" used in DTD but not defined", 'I', 'D'},
+/* 153 */ {"End-tag minimization should be \"O\" for element with CONREF attribute", 'I', 'D'},
+/* 154 */ {"Declared value of data attribute cannot be ENTITY or ENTITIES", 'E', 'D' },
+/* 155 */ {"Declared value of data attribute cannot be IDREF or IDREFS", 'E', 'D' },
+/* 156 */ {"Declared value of data attribute cannot be NOTATION", 'E', 'D' },
+/* 157 */ {"CURRENT cannot be specified for a data attribute", 'E', 'D' },
+/* 158 */ {"CONREF cannot be specified for a data attribute", 'E', 'D' },
+/* 159 */ {"Short reference map for element \"%s\" not defined; ignored", 'E', 'C'},
+/* 160 */ {"Cannot create temporary file", 'C', 'R'},
+/* 161 */ {"Document ended invalidly within SGML declaration", 'C', 'D'},
+/* 162 */ {"Capacity limit %s exceeded by %s points", 'W', 'Q'},
+/* 163 */ {"Amendment 1 requires \"ISO 8879:1986\" instead of \"ISO 8879-1986\"", 'W', 'D'},
+/* 164 */ {"Non-markup, non-minimum data character in SGML declaration", 'E', 'D'},
+/* 165 */ {"Parameter cannot be a literal", 'E', 'D'},
+/* 166 */ {"Invalid concrete syntax scope \"%s\"", 'E', 'D'},
+/* 167 */ {"Parameter must be a number", 'E', 'D'},
+/* 168 */ {"\"%s\" should have been \"%s\"", 'E', 'D'},
+/* 169 */ {"Character number %s is not supported as an additional name character", 'E', 'U'},
+/* 170 */ {"Parameter must be a literal or \"%s\"", 'E', 'D'},
+/* 171 */ {"Bad character description for character %s", 'E', 'D'},
+/* 172 */ {"Character number %s is described more than once", 'W', 'D'},
+/* 173 */ {"Character number plus number of characters exceeds 256", 'E', 'D'},
+/* 174 */ {"No description for upper half of character set: assuming \"128 128 UNUSED\"", 'W', 'D'},
+/* 175 */ {"Character number %s was not described; assuming UNUSED", 'E', 'D'},
+/* 176 */ {"Non-significant shunned character number %s not declared UNUSED", 'E', 'D'},
+/* 177 */ {"Significant character \"%s\" cannot be non-SGML", 'E', 'D'},
+/* 178 */ {"Unknown capacity set \"%s\"", 'E', 'U'},
+/* 179 */ {"No capacities specified." , 'E', 'D'},
+/* 180 */ {"Unknown concrete syntax \"%s\"", 'E', 'U'},
+/* 181 */ {"Character number exceeds 255", 'E', 'D'},
+/* 182 */ {"Concrete syntax SWITCHES not supported", 'E', 'U'},
+/* 183 */ {"\"INSTANCE\" scope not supported", 'E', 'U'},
+/* 184 */ {"Value of \"%s\" feature must be one or more", 'E', 'D'},
+/* 185 */ {"\"%s\" invalid; must be \"YES\" or \"NO\"", 'E', 'D'},
+/* 186 */ {"\"%s\" invalid; must be \"PUBLIC\" or \"SGMLREF\"", 'E', 'D'},
+/* 187 */ {"Feature \"%s\" is not supported", 'E', 'U'},
+/* 188 */ {"Too many open subdocument entities", 'E', 'Q'},
+/* 189 */ {"Invalid formal public identifier", 'I', 'D'},
+/* 190 */ {"Public text class should have been \"%s\"", 'I', 'D'},
+/* 191 */ {"Character number %s must be non-SGML", 'W', 'D'},
+/* 192 */ {"Notation \"%s\" not defined in DTD", 'W', 'D'},
+/* 193 */ {"Unclosed start or end tag requires \"SHORTTAG YES\"", 'W', 'M'},
+/* 194 */ {"Net-enabling start tag requires \"SHORTTAG YES\"", 'W', 'M'},
+/* 195 */ {"Attribute name omission requires \"SHORTTAG YES\"", 'W', 'M'},
+/* 196 */ {"Undelimited attribute value requires \"SHORTTAG YES\"", 'W', 'M'},
+/* 197 */ {"Attribute specification omitted for \"%s\": requires markup minimization", 'W', 'M'},
+/* 198 */ {"Concrete syntax does not have any short reference delimiters", 'E', 'D'},
+/* 199 */ {"Character number %s does not exist in the base character set", 'E', 'D'},
+/* 200 */ {"Character number %s is UNUSED in the syntax reference character set", 'E', 'D'},
+/* 201 */ {"Character number %s was not described in the syntax reference character set", 'E', 'D'},
+/* 202 */ {"Character number %s in the syntax reference character set has no corresponding character in the system character set", 'E', 'D'},
+/* 203 */ {"Character number %s was described using an unknown base set", 'E', 'D'},
+/* 204 */ {"Duplication specification for added funtion \"%s\"", 'E', 'D'},
+/* 205 */ {"Added function character cannot be \"%s\"", 'E', 'D'},
+/* 206 */ {"Only reference concrete syntax function characters supported", 'E', 'U'},
+/* 207 */ {"Only reference concrete syntax general delimiters supported", 'E', 'U'},
+/* 208 */ {"Only reference concrete syntax short reference delimiters supported", 'E', 'U'},
+/* 209 */ {"Unrecognized keyword \"%s\"", 'E', 'D'},
+/* 210 */ {"Unrecognized quantity name \"%s\"", 'E', 'D'},
+/* 211 */ {"Interpretation of \"%s\" is not a valid name in the declared concrete syntax", 'E', 'D'},
+/* 212 */ {"Replacement reserved name \"%s\" cannot be reference reserved name", 'E', 'D'},
+/* 213 */ {"Duplicate replacement reserved name \"%s\"", 'E', 'D'},
+/* 214 */ {"Quantity \"%s\" must not be less than %s", 'E', 'D'},
+/* 215 */ {"Only values up to %2$s are supported for quantity \"%1$s\"", 'E', 'U'},
+/* 216 */ {"Exclusions attempt to change required status of group in \"%s\"", 'E', 'C'},
+/* 217 */ {"Exclusion cannot apply to token \"%s\" in content model for \"%s\"", 'E', 'C'},
+/* 218 */ {"Required %s attribute was not specified for entity %s", 'E', 'C'},
+/* 219 */ {"UCNMSTRT must have the same number of characters as LCNMSTRT", 'E', 'D'},
+/* 220 */ {"UCNMCHAR must have the same number of characters as LCNMCHAR", 'E', 'D'},
+/* 221 */ {"Character number %s assigned to both LCNMSTRT or UCNMSTRT and LCNMCHAR or UCNMCHAR", 'E', 'D'},
+/* 222 */ {"Character number %s cannot be an additional name character", 'E', 'D'},
+/* 223 */ {"It is unsupported for \"-\" not to be assigned to UCNMCHAR or LCNMCHAR", 'E', 'U'},
+/* 224 */ {"Normalized length of value of attribute \"%s\" exceeded LITLEN", 'E', 'Q'},
+/* 225 */ {"Length of interpreted parameter literal exceeds LITLEN less the length of the bracketing delimiters", 'E', 'Q'},
+/* 226 */ {"Start tag of document element omitted; not minimizable", 'W', 'M'},
+/* 227 */ {"Unrecognized designating escape sequence \"%s\"", 'I', 'U'},
+/* 228 */ {"Earlier reference to entity \"%s\" used default entity", 'I', 'D'},
+/* 229 */ {"Reference to non-existent parameter entity \"%s\" ignored", 'E', 'R'},
+};
diff --git a/usr.bin/sgmls/sgmls/msgcat.c b/usr.bin/sgmls/sgmls/msgcat.c
new file mode 100644
index 0000000..ec6a8b5
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/msgcat.c
@@ -0,0 +1,833 @@
+/* msgcat.c -
+ X/Open message catalogue functions and gencat utility.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifndef HAVE_CAT
+
+/* In this implementation the message catalogue format is the same as the
+message text source file format (see pp 42-43 of the X/Open
+Portability Guide, Issue 3, Volume 3.) This means that you don't have
+to use the gencat utility, but it is still useful for checking and
+merging catalogues. */
+
+/* Compile this with -DGENCAT to get the gencat utility. */
+
+#include "std.h"
+#include "msgcat.h"
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+/* Default message set. */
+#define NL_SETD 1
+
+#ifndef PATH_FILE_SEP
+#define PATH_FILE_SEP ':'
+#endif
+
+#ifndef DEFAULT_NLSPATH
+#define DEFAULT_NLSPATH ""
+#endif
+
+#ifndef DEFAULT_LANG
+#define DEFAULT_LANG "default"
+#endif
+
+#define HASH_TAB_SIZE 251
+
+struct message {
+ struct message *next;
+ unsigned msgnum;
+ unsigned setnum;
+ char *text;
+};
+
+struct cat {
+ char *name;
+ int loaded;
+ int bad;
+ struct message *table[HASH_TAB_SIZE];
+};
+
+static char *read_buf = 0;
+static unsigned read_buf_len = 0;
+
+/* Errors that can be generated by read_catalog. */
+
+enum cat_err {
+ E_ZERO, /* not an error */
+ E_BADARG,
+ E_NOMEM,
+ E_NOSUCHCOMMAND,
+ E_INPUT,
+ E_EOF,
+ E_BADSEP,
+ E_BADLINE
+};
+
+#ifdef GENCAT
+/* These must match enum cat_err. */
+static char *cat_errlist[] = {
+ "Error 0",
+ "Invalid argument to command",
+ "Out of memory",
+ "Unrecognized command",
+ "Input error",
+ "Unexpected end of file",
+ "Space or tab expected after message number",
+ "Invalid line",
+};
+#endif /* GENCAT */
+
+#ifndef GENCAT
+/* The value of NLSPATH. */
+static char *nlspath = 0;
+/* The value of LANG. */
+static char *lang = 0;
+#endif /* not GENCAT */
+
+static int current_lineno = -1;
+static enum cat_err cat_errno = E_ZERO;
+
+#ifndef GENCAT
+static void load_catalog P((struct cat *));
+static FILE *find_catalog P((char *, char **));
+#endif
+static int read_catalog P((FILE *, struct message **));
+static void delete_set P((struct message **, unsigned));
+static void delete_message P((struct message **, unsigned, unsigned));
+static int hash P((unsigned setnum, unsigned msgnum));
+static char *parse_text P((FILE *, int));
+
+#ifndef GENCAT
+
+nl_catd catopen(name, oflag)
+char *name;
+int oflag;
+{
+ struct cat *catp;
+ int i;
+
+ if (!name)
+ return 0;
+
+ catp = (struct cat *)malloc(sizeof *catp);
+ if (!catp)
+ return 0;
+ for (i = 0; i < HASH_TAB_SIZE; i++)
+ catp->table[i] = 0;
+ catp->name = malloc(strlen(name) + 1);
+ catp->loaded = 0;
+ catp->bad = 0;
+ strcpy(catp->name, name);
+ return (nl_catd)catp;
+}
+
+int catclose(catd)
+nl_catd catd;
+{
+ int i;
+ struct cat *catp = (struct cat *)catd;
+
+ if (!catp)
+ return 0;
+
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ struct message *p, *nextp;
+ for (p = catp->table[i]; p; p = nextp) {
+ nextp = p->next;
+ free(p->text);
+ free((char *)p);
+ }
+ }
+ if (catp->name)
+ free(catp->name);
+ free((char *)catp);
+ return 0;
+}
+
+char *catgets(catd, setnum, msgnum, dflt)
+nl_catd catd;
+int setnum, msgnum;
+char *dflt;
+{
+ struct message *p;
+ struct cat *catp;
+
+ /* setnum and msgnum are required to be >= 1. */
+ if (!catd || setnum <= 0 || msgnum <= 0)
+ return dflt;
+ catp = (struct cat *)catd;
+ if (!catp->loaded)
+ load_catalog(catp);
+ if (catp->bad)
+ return dflt;
+ for (p = catp->table[hash(setnum, msgnum)]; p; p = p->next)
+ if (p->msgnum == msgnum && p->setnum == setnum)
+ break;
+ if (!p)
+ return dflt;
+ return p->text;
+}
+
+static
+VOID load_catalog(catp)
+struct cat *catp;
+{
+ FILE *fp;
+ char *path;
+
+ catp->loaded = 1;
+ fp = find_catalog(catp->name, &path);
+ if (!fp) {
+ catp->bad = 1;
+ return;
+ }
+ current_lineno = 0;
+ if (read_catalog(fp, catp->table) < 0)
+ catp->bad = 1;
+ fclose(fp);
+ if (read_buf) {
+ free(read_buf);
+ read_buf = 0;
+ }
+ read_buf_len = 0;
+ free(path);
+}
+
+static
+FILE *find_catalog(name, pathp)
+char *name;
+char **pathp;
+{
+ char *path;
+
+ if (!name)
+ return 0;
+ if (!nlspath) {
+ nlspath = getenv("NLSPATH");
+ if (!nlspath)
+ nlspath = DEFAULT_NLSPATH;
+ }
+ if (!lang) {
+ lang = getenv("LANG");
+ if (!lang)
+ lang = DEFAULT_LANG;
+ }
+ path = nlspath;
+ for (;;) {
+ char *p;
+ unsigned len = 0;
+
+ for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
+ if (*p == '%') {
+ if (p[1] == 'N') {
+ p++;
+ len += strlen(name);
+ }
+ else if (p[1] == 'L') {
+ p++;
+ len += strlen(lang);
+ }
+ else if (p[1] == '%') {
+ p++;
+ len++;
+ }
+ else
+ len++;
+
+ }
+ else
+ len++;
+ }
+ if (len > 0) {
+ char *s, *try;
+ FILE *fp;
+ s = try = malloc(len + 1);
+ if (!s)
+ return 0;
+ for (p = path; *p != '\0' && *p != PATH_FILE_SEP; p++) {
+ if (*p == '%') {
+ if (p[1] == 'N') {
+ p++;
+ strcpy(s, name);
+ s += strlen(name);
+ }
+ else if (p[1] == 'L') {
+ p++;
+ strcpy(s, lang);
+ s += strlen(lang);
+ }
+ else if (p[1] == '%') {
+ p++;
+ *s++ = '%';
+ }
+ else
+ *s++ = *p;
+ }
+ else
+ *s++ = *p;
+ }
+ *s++ = '\0';
+ fp = fopen(try, "r");
+ if (fp) {
+ *pathp = try;
+ return fp;
+ }
+ free(try);
+ }
+ if (*p == '\0')
+ break;
+ path = ++p;
+ }
+ return 0;
+}
+
+#endif /* not GENCAT */
+
+/* 0 success, -1 error */
+
+static
+int parse_message(c, fp, table, setnum, quote)
+int c;
+FILE *fp;
+struct message **table;
+unsigned setnum;
+int quote;
+{
+ unsigned msgnum;
+ struct message *msgp;
+ char *text;
+ int hc;
+
+ msgnum = c - '0';
+ for (;;) {
+ c = getc(fp);
+ if (!isdigit(c))
+ break;
+ msgnum = msgnum*10 + (c - '0');
+ }
+ if (c == '\n') {
+ delete_message(table, setnum, msgnum);
+ return 0;
+ }
+ if (c != ' ' && c != '\t') {
+ cat_errno = E_BADSEP;
+ return -1;
+ }
+ text = parse_text(fp, quote);
+ if (!text)
+ return -1;
+ hc = hash(setnum, msgnum);
+ for (msgp = table[hc]; msgp; msgp = msgp->next)
+ if (msgp->setnum == setnum && msgp->msgnum == msgnum)
+ break;
+ if (msgp)
+ free(msgp->text);
+ else {
+ msgp = (struct message *)malloc(sizeof *msgp);
+ if (!msgp) {
+ cat_errno = E_NOMEM;
+ return -1;
+ }
+ msgp->next = table[hc];
+ table[hc] = msgp;
+ msgp->msgnum = msgnum;
+ msgp->setnum = setnum;
+ }
+ msgp->text = text;
+ return 0;
+}
+
+static
+char *parse_text(fp, quote)
+FILE *fp;
+int quote;
+{
+ unsigned i = 0;
+ char *p;
+ int c;
+ int quoted;
+
+ c = getc(fp);
+ if (c == quote) {
+ quoted = 1;
+ c = getc(fp);
+ }
+ else
+ quoted = 0;
+ for (;; c = getc(fp)) {
+ if (c == EOF) {
+ if (ferror(fp)) {
+ cat_errno = E_INPUT;
+ return 0;
+ }
+ break;
+ }
+ if (c == '\n')
+ break;
+ /* XXX
+
+ Can quotes be used in quoted message text if protected by \ ?
+
+ Is it illegal to omit the closing quote if there's an opening
+ quote?
+
+ Is it illegal to have anything after a closing quote?
+
+ */
+
+ if (quoted && c == quote) {
+ /* Skip the rest of the line. */
+ while ((c = getc(fp)) != '\n')
+ if (c == EOF) {
+ if (ferror(fp)) {
+ cat_errno = E_INPUT;
+ return 0;
+ }
+ break;
+ }
+ break;
+ }
+ if (c == '\\') {
+ int d;
+
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ switch (c) {
+ case '\n':
+ current_lineno++;
+ continue;
+ case 'n':
+ c = '\n';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '\\':
+ c = '\\';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ c -= '0';
+ d = getc(fp);
+ if (d >= '0' && d <= '7') {
+ c = c*8 + d - '0';
+ d = getc(fp);
+ if (d >= '0' && d <= '7')
+ c = c*8 + d - '0';
+ else if (d != EOF)
+ ungetc(d,fp);
+ }
+ else if (d != EOF)
+ ungetc(d, fp);
+ if (c == '\0')
+ continue; /* XXX */
+ break;
+ default:
+ /* Ignore the quote. */
+ break;
+ }
+ }
+ if (i >= read_buf_len) {
+ if (!read_buf)
+ read_buf = malloc(read_buf_len = 40);
+ else
+ read_buf = realloc(read_buf, read_buf_len *= 2);
+ if (!read_buf) {
+ cat_errno = E_NOMEM;
+ return 0;
+ }
+ }
+ read_buf[i++] = c;
+ }
+ p = malloc(i + 1);
+ if (!p) {
+ cat_errno = E_NOMEM;
+ return 0;
+ }
+ memcpy(p, read_buf, i);
+ p[i] = '\0';
+ return p;
+}
+
+/* 0 success, -1 error */
+
+static
+int parse_command(fp, table, setnump, quotep)
+FILE *fp;
+struct message **table;
+unsigned *setnump;
+int *quotep;
+{
+ char buf[128];
+ if (fgets(buf, 128, fp) == NULL) {
+ cat_errno = ferror(fp) ? E_INPUT : E_EOF;
+ return -1;
+ }
+ if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n')
+ /* a comment */;
+ else if (strncmp(buf, "set", 3) == 0) {
+ if (sscanf(buf + 3, "%u", setnump) != 1) {
+ cat_errno = E_BADARG;
+ return -1;
+ }
+
+ }
+ else if (strncmp(buf, "delset", 6) == 0) {
+ unsigned num;
+ if (sscanf(buf + 6, "%u", &num) != 1) {
+ cat_errno = E_BADARG;
+ return -1;
+ }
+ delete_set(table, num);
+ *setnump = NL_SETD;
+ }
+ else if (strncmp(buf, "quote", 5) == 0) {
+ char *p = buf + 5;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ /* XXX should \ be allowed as the quote character? */
+ if (*p == '\0' || *p == '\n')
+ *quotep = -1;
+ else
+ *quotep = *p;
+ }
+ else {
+ cat_errno = E_NOSUCHCOMMAND;
+ return -1;
+ }
+ if (strchr(buf, '\n') == 0) {
+ int c;
+ while ((c = getc(fp)) != '\n' && c != EOF)
+ ;
+ }
+ return 0;
+}
+
+
+static
+VOID delete_set(table, setnum)
+struct message **table;
+unsigned setnum;
+{
+ int i;
+
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ struct message *p, *nextp;
+ for (p = table[i], table[i] = 0; p; p = nextp) {
+ nextp = p->next;
+ if (p->setnum == setnum)
+ free((char *)p);
+ else {
+ p->next = table[i];
+ table[i] = p;
+ }
+ }
+ }
+}
+
+static
+VOID delete_message(table, setnum, msgnum)
+struct message **table;
+unsigned setnum, msgnum;
+{
+ struct message **pp;
+
+ for (pp = &table[hash(setnum, msgnum)]; *pp; pp = &(*pp)->next)
+ if ((*pp)->setnum == setnum && (*pp)->msgnum == msgnum) {
+ struct message *p = *pp;
+ *pp = p->next;
+ free(p->text);
+ free((char *)p);
+ break;
+ }
+}
+
+/* 0 success, -1 error. On error cat_errno is set to the error number. */
+
+static
+int read_catalog(fp, table)
+FILE *fp;
+struct message **table;
+{
+ int c;
+ unsigned setnum = NL_SETD;
+ int quote_char = -1;
+
+ for (;;) {
+ /* start of line */
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ ++current_lineno;
+ if (isdigit(c)) {
+ if (parse_message(c, fp, table, setnum, quote_char) < 0)
+ return -1;
+ }
+ else if (c == '$') {
+ if (parse_command(fp, table, &setnum, &quote_char) < 0)
+ return -1;
+ }
+ else if (c != '\n') {
+ while ((c = getc(fp)) != '\n' && c != EOF)
+ if (c != ' ' && c != '\t') {
+ cat_errno = E_BADLINE;
+ return -1;
+ }
+ if (c == EOF)
+ break;
+ }
+ }
+ return 0;
+}
+
+static
+int hash(setnum, msgnum)
+unsigned setnum, msgnum;
+{
+ return ((setnum << 8) + msgnum) % HASH_TAB_SIZE;
+}
+
+#ifdef GENCAT
+
+static char *program_name;
+
+static int message_compare P((UNIV, UNIV));
+static void print_text P((char *, FILE *));
+static void usage P((void));
+
+#ifdef VARARGS
+static void fatal();
+#else
+static void fatal P((char *,...));
+#endif
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ FILE *fp;
+ int i, j, nmessages;
+ struct message **list;
+ unsigned setnum;
+ struct message *table[HASH_TAB_SIZE];
+
+ program_name = argv[0];
+
+ if (argc < 3)
+ usage();
+
+ for (i = 0; i < HASH_TAB_SIZE; i++)
+ table[i] = 0;
+ for (i = 1; i < argc; i++) {
+ errno = 0;
+ fp = fopen(argv[i], "r");
+ if (!fp) {
+ if (i > 1)
+ fatal("can't open `%s': %s", argv[i], strerror(errno));
+ }
+ else {
+ current_lineno = 0;
+ cat_errno = E_ZERO;
+ if (read_catalog(fp, table) < 0) {
+ assert(cat_errno != E_ZERO);
+ assert(cat_errno
+ < sizeof(cat_errlist)/sizeof(cat_errlist[0]));
+ fatal("%s:%d: %s", argv[i], current_lineno,
+ cat_errlist[cat_errno]);
+ }
+ fclose(fp);
+ }
+ }
+
+ errno = 0;
+ fp = fopen(argv[1], "w");
+ if (!fp)
+ fatal("can't open `%s' for output: %s", argv[1], strerror(errno));
+ nmessages = 0;
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ struct message *p;
+ for (p = table[i]; p; p = p->next)
+ nmessages++;
+ }
+ list = (struct message **)malloc(nmessages*sizeof(struct message *));
+ if (!list)
+ fatal("out of memory");
+ j = 0;
+ for (i = 0; i < HASH_TAB_SIZE; i++) {
+ struct message *p;
+ for (p = table[i]; p; p = p->next)
+ list[j++] = p;
+ }
+ assert(j == nmessages);
+
+ qsort((UNIV)list, nmessages, sizeof(struct message *), message_compare);
+
+ setnum = NL_SETD;
+ for (i = 0; i < nmessages; i++) {
+ struct message *p = list[i];
+ if (p->setnum != setnum) {
+ setnum = p->setnum;
+ fprintf(fp, "$set %u\n", setnum);
+ }
+ fprintf(fp, "%u ", p->msgnum);
+ print_text(p->text, fp);
+ putc('\n', fp);
+ }
+ if (fclose(fp) == EOF)
+ fatal("error closing `%s'", argv[1]);
+ return 0;
+}
+
+static
+VOID usage()
+{
+ fprintf(stderr, "usage: %s catfile msgfile...\n", program_name);
+ exit(1);
+}
+
+static
+#ifdef VARARGS
+VOID fatal(va_alist) va_dcl
+#else /* not VARARGS */
+VOID fatal(char *message,...)
+#endif /* not VARARGS */
+{
+ va_list ap;
+
+#ifdef VARARGS
+ char *message;
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else /* not VARARGS */
+ va_start(ap, message);
+#endif /* not VARARGS */
+
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, message, ap);
+ putc('\n', stderr);
+ va_end(ap);
+ exit(1);
+}
+
+static
+int message_compare(p1, p2)
+UNIV p1, UNIV p2;
+{
+ struct message *m1 = *(struct message **)p1;
+ struct message *m2 = *(struct message **)p2;
+
+ if (m1->setnum < m2->setnum)
+ return -1;
+ if (m1->setnum > m2->setnum)
+ return 1;
+ if (m1->msgnum < m2->msgnum)
+ return -1;
+ if (m1->msgnum > m2->msgnum)
+ return 1;
+ return 0;
+}
+
+static
+VOID print_text(s, fp)
+char *s;
+FILE *fp;
+{
+ for (; *s; s++) {
+ if (*s == '\\')
+ fputs("\\\\", fp);
+ else if (ISASCII(*s) && isprint((UNCH)*s))
+ putc(*s, fp);
+ else {
+ switch (*s) {
+ case '\n':
+ fputs("\\n", fp);
+ break;
+ case '\b':
+ fputs("\\b", fp);
+ break;
+ case '\f':
+ fputs("\\f", fp);
+ break;
+ case '\t':
+ fputs("\\t", fp);
+ break;
+ case '\v':
+ fputs("\\v", fp);
+ break;
+ case '\r':
+ fputs("\\r", fp);
+ break;
+ default:
+ fprintf(fp, "\\%03o", (unsigned char)*s);
+ break;
+ }
+ }
+ }
+}
+
+#endif /* GENCAT */
+
+#ifdef TEST
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ nl_catd catd;
+ int msgnum, setnum;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s catalogue\n", argv[0]);
+ exit(1);
+ }
+ catd = catopen(argv[1], 0);
+ fprintf(stderr, "Enter set number, message number pairs:\n");
+ fflush(stderr);
+ while (scanf("%d %d", &setnum, &msgnum) == 2) {
+ char *msg = catgets(catd, setnum, msgnum, "<default>");
+ fprintf(stderr, "Returned \"%s\"\n", msg);
+ fflush(stderr);
+ }
+ return 0;
+}
+
+#endif /* TEST */
+
+#endif /* not HAVE_CAT */
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/msgcat.h b/usr.bin/sgmls/sgmls/msgcat.h
new file mode 100644
index 0000000..83e998a
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/msgcat.h
@@ -0,0 +1,13 @@
+
+#ifdef HAVE_CAT
+#include <nl_types.h>
+#else
+typedef UNIV nl_catd;
+#endif
+
+/* Don't use prototypes here in case nl_types.h declares a conflicting
+prototype. */
+
+nl_catd catopen();
+int catclose();
+char *catgets();
diff --git a/usr.bin/sgmls/sgmls/pars1.c b/usr.bin/sgmls/sgmls/pars1.c
new file mode 100644
index 0000000..8616107
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pars1.c
@@ -0,0 +1,958 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+#define GI (tags[ts].tetd->etdgi+1) /* GI of current element. */
+#define NEWGI (newetd->etdgi+1) /* GI of new tag. */
+
+static VOID doincludes P((void));
+static int pentname P((char *));
+static struct mpos *newmpos P((void));
+static VOID commbufs P((void));
+static VOID checkdtd P((void));
+
+/* PARSECON: Parse content of an element.
+*/
+int parsecon(tbuf, pcb)
+UNCH *tbuf; /* Work area for tokenization. */
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ int srn; /* SHORTREF delimiter number (1-32). */
+ int refrc; /* Return code from sentref, stagetd, etc. */
+
+ TRACECON(etagimct, dostag, datarc, pcb, conrefsw, didreq);
+ if (eodsw) return(EOD_);
+ if (didreq && (conrefsw & TAGREF)) {didreq = 0; goto conr;}
+ if (etagimct>0) {etagimsw = --etagimct ? 1 : 0; destack(); return(ETG_);}
+ if (dostag) {
+ conrefsw = conrefsv;
+ etisw = etiswsv;
+ if (charmode) {dostag = 0; return datarc;}
+ return stag(datarc);
+ }
+ if (conrefsw) {
+ conr:
+ destack();
+ conrefsw = 0;
+ return ETG_;
+ }
+ else if (eofsw) return(EOD_);
+
+ datarc = 0;
+ while (1) {
+ parse(pcb);
+ srn = (int)pcb->action - SRMIN; /* Just in case it's a SHORTREF. */
+ switch (pcb->action) {
+ case DCE_: /* Data character in element content. */
+ /* The data character might be a non-SGML character so
+ reprocess it using pcbconm. */
+ REPEATCC;
+ pcb = conpcb = &pcbconm;
+ pcb->newstate = pcbcnet;
+ continue;
+ case DAS_: /* Current character begins data. */
+ data = FPOS;
+ continue;
+
+ case NLF_: /* NET or SR returns data in lookahead buffer. */
+ datalen = (UNS)(ptcon - data); REPEATCC;
+ goto rcc;
+
+ case LAF_: /* Return data in lookahead buffer: mixed. */
+ datalen = (UNS)(ptcon+1 - data);
+ goto rcc;
+
+ case NON_: /* Single nonchar in nonchbuf. */
+ datalen = 2; data = nonchbuf;
+ goto nrcc;
+
+ case DAR_: /* Return data except for last char. */
+ REPEATCC;
+ case DAF_: /* Return data in source entity buffer. */
+ datalen = (UNS)(FPOS - data);
+ rcc:
+ REPEATCC;
+ case DEF_: /* Return data in data entity. */
+ nrcc:
+ datarc = DAF_;
+ if (pcb==&pcbcone) {
+ pcbconm.newstate = pcbcnet;
+ conpcb = &pcbconm;
+ }
+ if (charmode) return(datarc);
+ stagmin = MINNONE; stagreal = newetd = ETDCDATA;
+ return(stag(datarc));
+
+ case LAS_: /* Start lookahead buffer with current char. */
+ *(ptcon = data = tbuf+1) = *FPOS;
+ continue;
+
+ case LAM_: /* Move character to lookahead buffer. */
+ *++ptcon = *FPOS;
+ continue;
+
+ case STG_: /* Process non-null start-tag. */
+ CTRSET(tagctr); /* Start counting tag length. */
+ parsenm(tbuf, NAMECASE); /* Get the GI. */
+ newetd = etdref(tbuf);
+ if (newetd && newetd->adl) {
+ parseatt(newetd->adl, tbuf);
+ adlval((int)ADN(al), newetd);
+ }
+ parsetag(&pcbstag); /* Parse the tag ending. */
+ if ((CTRGET(tagctr)-tagdelsw)>=TAGLEN)
+ sgmlerr(66, &pcbstag, (UNCH *)0, (UNCH *)0);
+ if (!newetd) {
+ sgmlerr(132, pcb, tbuf+1, (UNCH *)0);
+ continue;
+ }
+ return(stagetd(&pcbstag));
+
+ case NST_: /* Process null start-tag. */
+ return nstetd();
+
+ case ETC_: /* End-tag in CDATA or RCDATA. */
+ case ETG_: /* Process non-null end-tag. */
+ newetd = etdref(parsenm(tbuf, NAMECASE)); /* Get the GI. */
+ parsetag(&pcbetag); /* Parse tag end. */
+ if (!newetd) /* Error: undefined.*/
+ sgmlerr(11, &pcbetag, tbuf+1, (UNCH *)0);
+ else if (etagetd(&pcbetag)>=0) return ETG_;/* Open element. */
+ if (pcb->action!=ETC_) continue;
+ /* Tag is undefined or not for an open element and we are in
+ a CDATA or RCDATA element; issue message and treat as
+ null end-tag (</>).
+ */
+ sgmlerr(57, &pcbetag, (UNCH *)0, (UNCH *)0);
+ case NET_: /* Process null end-tag. */
+ if ((refrc = netetd(conpcb))!=0) return ETG_;
+ continue;
+
+ case NED_: /* Process null end-tag delimiter. */
+ etagmin = MINNET;
+ newetd = etagreal = ETDNET;
+ etagimct = etag();
+ etagimsw = etagimct ? 1 : 0; destack();
+ return ETG_;
+ case GTR_:
+ if (entget()!=-1) {
+ data = FPOS;
+ continue;
+ }
+ /* fall through */
+ case EOD_: /* End of primary file. */
+ if (ts<1) return(EOD_); /* Normal end: stack is empty. */
+ etagimct = ts-1; /* Treat as end-tag for top tag on stack. */
+ etagmin = MINETAG; etagreal = tags[0].tetd;
+ destack();
+ eofsw = 1; /* Return EOD_ after destacking all. */
+ return ETG_;
+
+ /* Short references ending with blanks:
+ If the blank sequence is followed by RE, go do SR7 or SR6.
+ If the entity is undefined and we are in mixed content,
+ the blanks must be returned as data. If not, they
+ can be ignored.
+ */
+ case SR9_: /* Process SR9 (two or more blanks). */
+ REPEATCC; /* Make first blank the CC. */
+ case SR4_: /* Process SR4 (RS, blanks). */
+ parseseq(tbuf, BSEQLEN); /* Squeeze out all blanks. */
+ if (*FPOS=='\r') {srn = (srn==9) ? 7 : 6; data = tbuf; goto sr6;}
+ else REPEATCC;
+ if ((refrc = shortref(srn, pcb))==DEF_) goto nrcc;
+ if (refrc>0) return refrc;
+ if (refrc==ENTUNDEF && pcb==&pcbconm)
+ {data = tbuf; goto nrcc;}
+ continue;
+
+ /* Short references ending with RE:
+ If the reference is defined, the RE is ignored.
+ For RE and RS RE,
+ no special action is needed if the reference is undefined,
+ as the RE will be processed immediately as the current character.
+ For B RE and RS B RE,
+ the input is primed with a special character that will
+ be treated as an RE that cannot be a short reference.
+ */
+ case SR7_: /* Process SR7 (blanks, RE). */
+ datalen = (UNS)(FPOS - data);
+ case SR2_: /* Process SR2 (RE). */
+ case SR5_: /* Process SR5 (RS, RE). */
+ sr6: /* Process SR6 (RS, blanks, RE). */
+ if ((refrc = shortref(srn, pcb))!=ENTUNDEF) {
+ if (refrc==DEF_) goto nrcc; /* Defined: data entity. */
+ if (refrc>0) return refrc; /* Defined: tag entity. */
+ continue; /* Defined: not tag. */
+ }
+ if (pcb!=&pcbconm) continue; /* Not mixed; ignore chars. */
+ if (srn>=6) /* Return blanks as data. */
+ {*FPOS = lex.d.genre; REPEATCC; goto nrcc;}
+ case REF_: /* Undefined SR with RE; return record end. */
+ datarc = REF_;
+ if (charmode) return(datarc);
+#if 0
+ /* The standard says this situation can force a tag.
+ See 323:3-6, 412:1-7. */
+ /* If RE would be ignored, don't treat it as start-tag
+ because it could force a required tag; but do change
+ state to show that an RE was ignored.
+ */
+ if (scbsgml[pss].snext==scbsgmst) {
+ scbsgml[pss].snext = scbsgmnr;
+ TRACEGML(scbsgml, pss, conactsw, conact);
+ continue;
+ }
+#endif
+ stagmin = MINNONE; stagreal = newetd = ETDCDATA;
+ return(stag(datarc));
+
+ case SR3_: /* Process SR3 (RS). */
+ REPEATCC;
+ if ((refrc = shortref(srn, pcb))==DEF_) goto nrcc;
+ if (refrc>0) return refrc;
+ continue;
+
+ case RBR_: /* Two right brackets */
+ srn = 26;
+ REPEATCC;
+ /* fall through */
+ case SR1_: /* Process SR1 (TAB). */
+ case SR8_: /* Process SR8 (space). */
+ case SR19: /* Process SR19 (-). */
+ case SR26: /* Process SR26 (]). */
+ REPEATCC;
+ goto srproc;
+
+ case FCE_: /* Process free character (SR11-18, SR21-32). */
+ fce[0] = *FPOS;
+ srn = mapsrch(&lex.s.dtb[lex.s.fce], fce);
+ case SR10: /* Process SR10 ("). */
+ case SR11: /* Process SR11 (#). */
+ case SR20: /* Process SR20 (-). */
+ case SR25: /* Process SR25 ([). */
+ srproc:
+ if ((refrc = shortref(srn, pcb))==DEF_) goto nrcc;
+ if (refrc>0) return refrc;
+ if (refrc==ENTUNDEF) { /* Treat the SR as data. */
+ data = FPOS - (srn==lex.s.hyp2);/* Two data chars if SR20.*/
+ if (pcb!=&pcbconm) { /* If not in mixed content: */
+ if (srn>=lex.s.data) { /* Change PCB. */
+ pcb = conpcb = &pcbconm;
+ pcb->newstate = pcbcnda;
+ }
+ }
+ else pcb->newstate = pcbcnda;/* Now in data found state. */
+ }
+ continue;
+
+ case ERX_: /* Entity ref in RCDATA: cancel ending delims.*/
+ lexcon[lex.d.tago] = lex.l.fre;
+ lexcon[lex.d.net] = lex.l.nonet;
+ lexlms[lex.d.msc] = lex.l.fre;
+ continue;
+
+ case EE_: /* Entity end in RCDATA: check nesting. */
+ if (es<rcessv) {synerr(37, pcb); rcessv = es;}
+ /* If back at top level, re-enable the ending delimiters. */
+ if (es==rcessv) {
+ lexcon[lex.d.tago] = lex.l.tago;
+ lexcon[lex.d.net] = etictr ? lex.l.net : lex.l.nonet;
+ lexlms[lex.d.msc] = lex.l.msc;
+ }
+ continue;
+
+ case PIE_: /* PI entity: same as PIS_. */
+ return PIS_;
+
+ case RSR_: /* Record start: ccnt=0; ++rcnt.*/
+ ++RCNT; CTRSET(RSCC);
+ default:
+ return (int)pcb->action; /* Default (MD_ MDC_ MSS_ MSE_ PIS_). */
+ }
+ }
+}
+/* STAGETD: Process start-tag etd.
+*/
+int stagetd(pcb)
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ if (!newetd->etdmod) {
+ sgmlerr(43, pcb, newetd->etdgi+1, (UNCH *)0);
+ ++ds.etdercnt;
+ etdset(newetd, (UNCH)SMO+EMO+ETDOCC, &undechdr,
+ (PETD *)0, (PETD *)0, (PECB *)0);
+ TRACEETD(newetd);
+ }
+ stagmin = MINNONE; stagreal = newetd;
+ return stag(0);
+}
+/* NSTETD: Process null start-tag etd.
+*/
+int nstetd()
+{
+ newetd = ts>0 ? tags[ts].tetd
+ : tags[0].tetd->etdmod[2].tu.thetd;
+ stagmin = MINNULL; stagreal = ETDNULL;
+ etisw = 0;
+ return stag(0);
+}
+/* ETAGETD: Process end-tag etd.
+*/
+int etagetd(pcb)
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ etagmin = MINNONE; etagreal = newetd;
+ if ((etagimct = etag())<0) {
+ sgmlerr(E_ETAG, pcb, NEWGI, tags[ts].tetd->etdgi+1);
+ return etagimct;
+ }
+ etagimsw = etagimct ? 1 : 0; destack();
+ return ETG_;
+}
+/* NETETD: Process null end-tag etd.
+*/
+int netetd(pcb)
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ if (ts<1) {
+ sgmlerr(51, pcb, (UNCH *)0, (UNCH *)0);
+ return 0;
+ }
+ etagmin = MINNULL; etagreal = ETDNULL;
+ etagimsw = 0; destack();
+ return ETG_;
+}
+/* SHORTREF: Process a short (alternative) reference to an entity.
+ Returns ENTUNDEF if entity is not defined, otherwise returns
+ the return code from stagetd or etagetd if the entity was
+ a tag, or zero if an error occurred somewhere.
+*/
+int shortref(srn, pcb)
+int srn; /* Short reference number. */
+struct parse *pcb; /* Parse control block for this parse. */
+{
+ int rc; /* Return code from entopen. */
+
+ if (tags[ts].tsrm==SRMNULL || !tags[ts].tsrm[srn]) return ENTUNDEF;
+ if (!tags[ts].tsrm[srn]->estore) {
+ sgmlerr(93, pcb, tags[ts].tsrm[srn]->ename+1,
+ tags[ts].tsrm[0]->ename+1);
+ return(ENTUNDEF);
+ }
+ rc = entopen(tags[ts].tsrm[srn]);
+ if (rc==ENTDATA) return DEF_;
+ if (rc==ENTPI) return PIS_;
+ return(0);
+}
+/* PARSEPRO: Parse prolog.
+ Note: ptpro cannot overrun tbuf (and therefore needn't be
+ tested), as long as the buffer exceeds the longest
+ lookahead sequence in the content parse tables.
+*/
+int parsepro()
+{
+ struct parse *oldpcb;
+
+ while (1) {
+ int rc; /* Return code: DAF MSS DCE */
+ switch (parse(propcb)) {
+
+ case LAS_: /* Start lookahead buffer with current char. */
+ *(ptpro = data = tbuf+1) = *FPOS;
+ continue;
+ case LAM_: /* Move character to lookahead buffer. */
+ *++ptpro = *FPOS;
+ continue;
+ case LAF_: /* Return data in lookahead buffer. */
+ datalen = (UNS)(ptpro+1 - data);
+ REPEATCC;
+ rc = DAF_;
+ break; /* Prolog ended; data pending. */
+
+ case DTD_: /* Process document type declaration. */
+ parsenm(tbuf, NAMECASE); /* Get declaration name. */
+ if (!ustrcmp(tbuf+1, sgmlkey)
+ && !dtdsw && !sgmlsw++) {
+#if 0
+ parse(&pcbmdi);
+#endif
+ /* If we got some appinfo, return. */
+ if (sgmldecl())
+ return APP_;
+ continue;
+ }
+ if (!ustrcmp(tbuf+1, key[KDOCTYPE]) && !dtdsw++) {
+ startdtd();
+ mddtds(tbuf);
+ continue;
+ }
+ sgmlerr(E_MDNAME, propcb, tbuf+1, (UNCH *)0);
+ continue;
+ case DTE_: /* DOCTYPE declaration (and prolog) ended. */
+ REPEATCC; /* Put back char that followed MSC. */
+ if (es != 0)
+ sgmlerr(143, propcb, (UNCH *)0, (UNCH *)0);
+ else if (dtdrefsw) {/* Process referenced DTD before real DTE. */
+ dtdrefsw = 0; /* Keep us from coming through here again. */
+ REPEATCC; /* Put back MSC so it follows referenced DTD. */
+ entref(indtdent);
+ }
+ else mddtde(tbuf);
+ continue;
+
+ case MD_:
+ /* Process markup declaration within DTD or LPD. */
+ parsenm(tbuf, NAMECASE); /* Get declaration name. */
+ if (!ustrcmp(tbuf+1, key[KENTITY]))
+ mdentity(tbuf);
+ else if (!ustrcmp(tbuf+1, key[KUSEMAP]))
+ mdsrmuse(tbuf);
+ else if (!ustrcmp(tbuf+1, key[KATTLIST]))
+ mdadl(tbuf);
+ else if (!ustrcmp(tbuf+1, key[KSHORTREF]))
+ mdsrmdef(tbuf);
+ else if (!ustrcmp(tbuf+1, key[KELEMENT]))
+ mdelem(tbuf);
+ else if (!ustrcmp(tbuf+1, key[KNOTATION]))
+ mdnot(tbuf);
+ else
+ sgmlerr(E_MDNAME, propcb, tbuf+1, (UNCH *)0);
+ continue;
+ case MDC_: /* Process markup declaration comment. */
+ sgmlsw++; /* SGML declaration not allowed after comment */
+ parsemd(tbuf, NAMECASE, (struct parse *)0, NAMELEN);
+ continue;
+
+ case MSS_: /* Process marked section start. */
+ oldpcb = propcb;
+ propcb = mdms(tbuf, propcb);
+ if (propcb==&pcbmsc || propcb==&pcbmsrc) {
+ if (oldpcb == &pcbmds)
+ sgmlerr(135, oldpcb, (UNCH *)0, (UNCH *)0);
+ conpcb = propcb;
+ rc = DCE_;
+ break;
+ }
+ continue;
+ case MSE_: /* Process marked section end. */
+ if (mdmse()) propcb = &pcbmds;
+ continue;
+ case MSP_: /* Marked section start in prolog outside DTD */
+ rc = MSS_;
+ break;
+ case PIE_: /* PI entity: same as PIS_. */
+ return(PIS_);
+
+ case EOD_: /* Return end of primary entity. */
+ if (!sw.onlypro || propcb != &pcbpro || !dtdsw)
+ sgmlerr(127, propcb, (UNCH *)0, (UNCH *)0);
+ else {
+ setdtype();
+ checkdtd();
+ }
+ return propcb->action;
+ case PIS_: /* Return processing instruction (string). */
+ sgmlsw++; /* SGML declaration not allowed after PI */
+ return((int)propcb->action); /* Prolog will continue later. */
+
+ case CIR_: /* Chars ignored; trying to resume parse. */
+ synerr(E_RESTART, propcb);
+ REPEATCC;
+ continue;
+ case STE_: /* Start tag ended prolog */
+ REPEATCC;
+ REPEATCC;
+ rc = STE_;
+ break;
+ case PEP_: /* Previous character ended prolog. */
+ REPEATCC;
+ case DCE_: /* Data character ended prolog. */
+ REPEATCC;
+ rc = DCE_;
+ break;
+ case EE_: /* Illegal entity end in ignored marked section. */
+ /* An error message has already been given. */
+ continue;
+ default:
+ abort();
+ } /* switch */
+ setdtype(); /* First pass only: set document type. */
+ checkdtd();
+ if (sw.onlypro)
+ return EOD_;
+ TRACESET(); /* Set trace switches. */
+ endprolog();
+ /* *DOC is first element; stack it at level 0. */
+ stack(newetd = nextetd = stagreal = etagreal = docetd);
+ return(rc);
+ } /* while */
+}
+
+/* Allocate buffers that are used in the DTD. */
+
+VOID startdtd()
+{
+ nmgrp = (struct etd **)rmalloc((GRPCNT+1)*sizeof(struct etd *));
+ nnmgrp = (PDCB *)rmalloc((GRPCNT+1)*sizeof(PDCB));
+ gbuf = (struct thdr *)rmalloc((GRPGTCNT+3)*sizeof(struct thdr));
+ /* The extra 1 is for parsing the name of a parameter entity in
+ mdentity(). */
+ nmbuf = (UNCH *)rmalloc(NAMELEN+3);
+ pubibuf = (UNCH *)rmalloc(LITLEN+1);
+ sysibuf = (UNCH *)rmalloc(LITLEN+1);
+ commbufs();
+ doincludes();
+}
+
+static
+VOID checkdtd()
+{
+ struct dcncb *np;
+
+ if (sw.swundef) {
+ int i;
+ struct etd *ep;
+ struct srh *sp;
+
+ for (i = 0; i < ETDHASH; i++)
+ for (ep = etdtab[i]; ep; ep = ep->etdnext)
+ if (!ep->etdmod)
+ sgmlerr(140, (struct parse *)0, ep->etdgi + 1,
+ (UNCH *)0);
+ for (sp = srhtab[0]; sp; sp = sp->enext)
+ if (sp->srhsrm[0] == 0)
+ sgmlerr(152, (struct parse *)0, sp->ename + 1,
+ (UNCH *)0);
+ }
+ for (np = dcntab[0]; np; np = np->enext)
+ if (!np->defined)
+ sgmlerr(192, (struct parse *)0, np->ename + 1, (UNCH *)0);
+}
+
+/* Return non-zero if s is a valid parameter entity name.
+If so put a transformed name in entbuf. */
+
+static
+int pentname(s)
+char *s;
+{
+ int i;
+ if (lextoke[(UNCH)*s] != NMS)
+ return 0;
+ entbuf[2] = ENTCASE ? lextran[(UNCH)*s] : (UNCH)*s;
+ for (i = 1; s[i]; i++) {
+ if (i > NAMELEN - 1)
+ return 0;
+ if (lextoke[(UNCH)s[i]] < NMC || s[i] == EOBCHAR)
+ return 0;
+ entbuf[i + 2] = ENTCASE ? lextran[(UNCH)s[i]] : (UNCH)s[i];
+ }
+ entbuf[1] = lex.d.pero;
+ entbuf[i + 2] = '\0';
+ entbuf[0] = (UNCH)(i + 3); /* length byte, PERO and '\0' */
+ return 1;
+}
+
+/* Handle sw.includes. */
+
+static
+VOID doincludes()
+{
+ char **p;
+ if (!sw.includes)
+ return;
+ for (p = sw.includes; *p; p++) {
+ if (pentname(*p)) {
+ if (!entfind(entbuf)) {
+ union etext etx;
+ etx.c = savestr(key[KINCLUDE]);
+ entdef(entbuf, ESM, &etx);
+ ++ds.ecbcnt;
+ ds.ecbtext += ustrlen(key[KINCLUDE]);
+ }
+ }
+ else
+ sgmlerr(138, (struct parse *)0, (UNCH *)*p, (UNCH *)0);
+ }
+}
+
+/* Allocate buffers that are use both in the DTD and the instance. */
+
+static
+VOID commbufs()
+{
+ al = (struct ad *)rmalloc((ATTCNT+2)*sizeof(struct ad));
+ lbuf = (UNCH *)rmalloc(LITLEN + 1);
+}
+
+static
+struct mpos *newmpos()
+{
+ int j;
+ unsigned long *h;
+ struct mpos *p = (struct mpos *)rmalloc((GRPLVL+2)*sizeof(struct mpos));
+
+ assert(grplongs > 0);
+ h = (unsigned long *)rmalloc((GRPLVL+2)*grplongs*sizeof(unsigned long));
+ for (j = 0; j < GRPLVL+2; j++) {
+ p[j].h = h;
+ h += grplongs;
+ }
+ return p;
+}
+
+/* Perform end of prolog buffer allocation. */
+
+VOID endprolog()
+{
+ int i;
+
+ ambigfree();
+ if (dtdsw) {
+ frem((UNIV)nmgrp);
+ frem((UNIV)nnmgrp);
+ frem((UNIV)gbuf);
+ frem((UNIV)nmbuf);
+ frem((UNIV)sysibuf);
+ frem((UNIV)pubibuf);
+ }
+ else {
+ commbufs();
+ doincludes();
+ }
+ scbsgml = (struct restate *)rmalloc((TAGLVL+1)*sizeof(struct restate));
+ tags = (struct tag *)rmalloc((TAGLVL+1)*sizeof(struct tag));
+ grplongs = (GRPCNT + LONGBITS - 1)/LONGBITS;
+ for (i = 0; i < TAGLVL+1; i++)
+ tags[i].tpos = newmpos();
+ savedpos = newmpos();
+}
+
+/* SETDTYPE: Establish specified or default document type.
+*/
+VOID setdtype()
+{
+ /* Initialize default model hdr for declared content. */
+ undechdr.ttype = MANY+MCHARS+MGI; /* Declared content is ANY. */
+ undechdr.tu.tnum = 0; /* No content model. */
+
+ /* Initialize content model and etd for *DOC. */
+ prcon[0].ttype = MGI; /* Model is an element model. */
+ prcon[0].tu.tnum = 2; /* A single group with a single GI in it. */
+ prcon[1].ttype = TTSEQ; /* Non-repeatable SEQ group. */
+ prcon[1].tu.tnum = 1; /* Only one token in group. */
+ prcon[2].ttype = TTETD; /* Token is an etd. */
+ docetd = etddef(indocetd); /* etd for document as a whole. */
+ etdset(docetd, ETDOCC, prcon, (PETD *)0, (PETD *)0, SRMNULL);
+
+ /* Put specified or default document type etd in *DOC model. */
+ if (!dtype) {
+ sgmlerr(E_DOCTYPE, propcb, (UNCH *)0, (UNCH *)0);
+ dtype = indefetd;
+ }
+ prcon[2].tu.thetd = etddef(dtype);
+ if (!prcon[2].tu.thetd->etdmod) {
+ if (dtype != indefetd)
+ sgmlerr(52, propcb, dtype+1, (UNCH *)0);
+ ++ds.etdercnt;
+ etdset(prcon[2].tu.thetd, (UNCH)SMO+EMO+ETDUSED+ETDOCC, &undechdr,
+ (PETD *)0, (PETD *)0, (PECB *)0);
+ }
+ TRACEETD(docetd);
+ TRACEMOD(prcon);
+ TRACEETD(prcon[2].tu.thetd);
+ return;
+}
+/* PARSETAG: Tag end parser for SGML documents.
+ For start-tags, it
+ sets etisw to TAGNET if tag ended with ETI; otherwise to 0.
+*/
+VOID parsetag(pcb)
+struct parse *pcb; /* Parse control block: pcbstag or pcbetag. */
+{
+ tagdelsw = 1; /* Assume tag had an ETI or TAGC. */
+ switch (parse(pcb)) {
+ case ETIC: /* Tag closed with ETI. */
+ if (!sd.shorttag) synerr(194, pcb);
+ etisw = TAGNET; /* Set switch for stack entry flag. */
+ return;
+ case DSC:
+ synerr(9, pcb);
+ REPEATCC;
+ etisw = 0;
+ return;
+ case NVS: /* Att name or value token found. */
+ case NTV: /* Name token value found. */
+ synerr(E_POSSATT, pcb);
+ pcb->newstate = 0; /* Reset parse state. */
+ REPEATCC; /* Put it back for next read. */
+ tagdelsw = 0; /* Tag had no closing delimiter. */
+ etisw = 0; /* Don't flag stack entry. */
+ return;
+ case TAGO: /* Tag closing implied by TAGO. */
+ if (!sd.shorttag) synerr(193, pcb);
+ REPEATCC; /* Put it back for next read. */
+ tagdelsw = 0; /* Tag had no closing delimiter. */
+ case TAGC: /* Normal close. */
+ default: /* Invalid character (msg was sent). */
+ etisw = 0; /* Don't flag stack entry. */
+ return;
+ }
+}
+/* STAG: Check whether a start-tag is valid at this point in the document
+ structure, or whether other tags must precede it.
+ Special case processing is done for the fake tag, #CDATA, as
+ it is never stacked.
+*/
+int stag(dataret)
+int dataret; /* Data pending: DAF_ REF_ 0=not #PCDATA. */
+{
+ int rc, realrc; /* Return code from context or other test. */
+ int mexts = 0; /* >0=stack level of minus grp; -1=plus; 0=none.*/
+
+ badresw = pexsw = 0;
+ /* If real element (i.e., not #PCDATA) set mexts and test if empty. */
+ if (dataret==0) {
+ mexts = pexmex(newetd);
+ /* If element is declared empty, it is same as a conref. */
+ if (GET(newetd->etdmod->ttype, MNONE)) conrefsw = TAGREF;
+ }
+ if (GET(tags[ts].tetd->etdmod->ttype, MANY))
+ rc = mexts>0 ? RCMEX : RCHIT;
+ else rc = context(newetd, tags[ts].tetd->etdmod, tags[ts].tpos,
+ &tags[ts].status, mexts);
+ TRACESTG(newetd, dataret, rc, nextetd, mexts);
+
+ switch (rc) {
+ case RCEND: /* End current element, then retry start-tag. */
+ if (ts<1) realrc = RCMISS;
+ else realrc = RCEND;
+ break;
+ case RCREQ: /* Stack compulsory GI, then retry start-tag. */
+ realrc = RCREQ;
+ break;
+ case RCMISS: /* Start-tag invalid (#PCDATA or real). */
+ if (ts>0 && GET(tags[ts].tetd->etdmod->ttype, MANY))
+ realrc = RCEND;
+ else realrc = RCMISS;
+ break;
+ case RCMEX: /* Start-tag invalid (minus exception). */
+ etagimct = ts - mexts;
+ realrc = RCEND;
+ break;
+ case RCHITMEX: /* Invalid minus exclusion for required element. */
+#if 0 /* This will have been detected by exclude.c. */
+ sgmlerr(E_MEXERR, &pcbstag, NEWGI, tags[mexts].tetd->etdgi+1);
+#endif
+ case RCHIT: /* Start-tag was valid. */
+ realrc = RCHIT;
+ break;
+ case RCPEX: /* Start-tag valid only because of plus exception. */
+ pexsw = TAGPEX;
+ realrc = RCHIT;
+ break;
+ default:
+ abort();
+ }
+
+ switch (realrc) {
+ case RCEND: /* End current element, then retry start-tag. */
+ if (didreq) sgmlerr(07, &pcbstag, nextetd->etdgi+1, (UNCH *)0);
+ didreq = 0; /* No required start-tag done. */
+ dostag = 1; etiswsv = etisw; /* Save real start-tag status. */
+ conrefsv = conrefsw; /* Save real start-tag conref. */
+ conrefsw = 0; /* Current element is not empty. */
+ etagmin = MINSTAG; destack(); /* Process omitted end-tag. */
+ return ETG_;
+ case RCREQ: /* Stack compulsory GI, then retry start-tag. */
+ if (!BADPTR(nextetd)) {
+#if 0 /* This will have been detected in exclude.c. */
+ if ((mexts = pexmex(nextetd))>0)
+ sgmlerr(E_MEXERR, &pcbstag, nextetd->etdgi+1,
+ tags[mexts].tetd->etdgi+1);
+#endif
+ if (!nextetd->etdmod) {
+ sgmlerr(53, &pcbstag, nextetd->etdgi+1, (UNCH *)0);
+ etdset(nextetd, (UNCH)SMO+EMO+ETDOCC, &undechdr,
+ (PETD *)0, (PETD *)0, (PECB *)0);
+ ++ds.etdercnt;
+ TRACEETD(nextetd);
+ }
+ }
+ if (BITOFF(nextetd->etdmin, SMO)) {
+ if (!BADPTR(stagreal))
+ sgmlerr(21, &pcbstag, nextetd->etdgi+1, stagreal->etdgi+1);
+ else if (stagreal==ETDCDATA)
+ sgmlerr(49, &pcbstag, nextetd->etdgi+1, (UNCH *)0);
+ else sgmlerr(50, &pcbstag, nextetd->etdgi+1, (UNCH *)0);
+ }
+ didreq = 1; /* Required start-tag done. */
+ dostag = 1; etiswsv = etisw; /* Save real start-tag status. */
+ etisw = 0; conrefsv = conrefsw; /* Save real start-tag conref. */
+ /* If element is declared empty, it is same as a conref. */
+ conrefsw = (GET(nextetd->etdmod->ttype, MNONE)) ? TAGREF : 0;
+ stack(nextetd); /* Process omitted start-tag. */
+ return STG_;
+ case RCMISS: /* Start-tag invalid (#PCDATA or actual). */
+ dostag = 0; contersw |= 1; didreq = 0;
+ if (dataret) {
+ if (dataret==REF_) badresw = 1;
+ else sgmlerr(E_CHARS, conpcb, tags[ts].tetd->etdgi+1, (UNCH *)0);
+ return dataret;
+ }
+ sgmlerr(E_CONTEXT, &pcbstag, NEWGI, tags[ts].tetd->etdgi+1);
+ if (stagmin!=MINNULL) stagmin = MINNONE; stack(newetd);
+ return STG_;
+ case RCHIT: /* Start-tag was valid. */
+ dostag = 0; didreq = 0;
+ if (dataret) return dataret;
+ stack(newetd);
+ return STG_;
+ }
+ return NOP_; /* To avoid Borland C++ warning */
+}
+/* PEXMEX: See if a GI is in a plus or minus exception group on the stack.
+ If in a minus, returns stack level of minus group; otherwise,
+ returns -1 if in a plus and not a minus, and zero if in neither.
+*/
+int pexmex(curetd)
+struct etd *curetd; /* The etd for this GI. */
+{
+ int tsl; /* Temporary stack level for looping. */
+ int pex = 0; /* 1=found in plus grp; 0=not. */
+
+ for (tsl = ts; tsl>0; --tsl) {
+ if (tags[tsl].tetd->etdmex && ingrp(tags[tsl].tetd->etdmex, curetd))
+ return(tsl);
+ if (tags[tsl].tetd->etdpex && ingrp(tags[tsl].tetd->etdpex, curetd))
+ pex = -1;
+ }
+ return(pex);
+}
+/* STACK: Add a new entry to the tag stack.
+ If there is no room, issue a message and reuse last position.
+*/
+VOID stack(curetd)
+struct etd *curetd; /* The etd for this entry. */
+{
+ /* Stack the new element type definition (error if no room). */
+ if (++ts>TAGLVL)
+ sgmlerr(E_STAGMAX, conpcb, curetd->etdgi+1, tags[--ts].tetd->etdgi+1);
+ tags[ts].tetd = curetd;
+
+ /* Set flags: plus exception + tag had ETI + context error + empty. */
+ tags[ts].tflags = (UNCH)pexsw + etisw + contersw + conrefsw; contersw = 0;
+
+ /* If tag had ETI, update ETI counter and enable NET if first ETI. */
+ if (etisw && ++etictr==1) lexcon[lex.d.net] = lexcnm[lex.d.net] = lex.l.net;
+
+ /* If etd has ALT table, use it; otherwise, use last element's ALT. */
+ if (curetd->etdsrm) {
+ if (curetd->etdsrm != SRMNULL && curetd->etdsrm[0] == NULL) {
+ /* Map hasn't been defined. Ignore it. */
+ sgmlerr(159, &pcbstag, curetd->etdgi + 1, (UNCH *)0);
+ curetd->etdsrm = 0;
+ tags[ts].tsrm = tags[ts-1].tsrm;
+ }
+ else
+ tags[ts].tsrm = curetd->etdsrm;
+ }
+ else
+ tags[ts].tsrm = tags[ts-1].tsrm;
+
+ /* Initialize rest of stack entry. */
+ tags[ts].status = 0;
+ tags[ts].tpos[0].g = 1; /* M: Index in model of next token to test.*/
+ tags[ts].tpos[0].t = 1; /* P: Index in tpos of current group. */
+ HITCLEAR(tags[ts].tpos[0].h);
+ tags[ts].tpos[1].g = 1; /* Index of group in model (dummy grp). */
+ tags[ts].tpos[1].t = 1; /* 1st token is next in grp to be tested. */
+ HITCLEAR(tags[ts].tpos[1].h); /* No hits yet as yet. */
+ TRACESTK(&tags[ts], ts, etictr);
+
+ exclude();
+ return;
+}
+/* ETAG: Check validity of an end-tag by seeing if it matches any tag
+ on the stack. If so, return the offset of the match from the
+ current entry (0=current). If there is no match, issue a message
+ and return an error code (-1).
+ If the newetd is ETDNET, a NET delimiter was found, so check for
+ a tag that ended with ETI instead of a matching GI.
+*/
+int etag()
+{
+ int tsl = ts+1; /* Temporary stack level for looping. */
+
+ /* See if end-tag is anywhere on stack, starting at current entry. */
+ while (--tsl) {
+ if (newetd!=ETDNET ? newetd==tags[tsl].tetd : tags[tsl].tflags) {
+ TRACEETG(&tags[ts], newetd, tsl, ts-tsl);
+ return(ts-tsl);
+ }
+ }
+ return (-1); /* End-tag didn't match any start-tag. */
+}
+/* DESTACK:
+ Call ECONTEXT to see if element can be ended at this point.
+ and issue message if there are required tags left.
+ Remove the current entry from the tag stack.
+ Issue an error if the destacked element was not minimizable
+ and its end-tag was omitted.
+*/
+VOID destack()
+{
+ register int ecode = 0; /* Error code (0=o.k.). */
+ UNCH *eparm2 = NULL; /* Second parameter of error message. */
+ register int minmsgsw; /* 1=message if tag omitted; 0=no message. */
+
+ /* If element has a content model (i.e., not a keyword) and there
+ are required tags left, and no CONREF attribute was specified,
+ issue an error message.
+ */
+ if (!GET(tags[ts].tetd->etdmod->ttype, MKEYWORD)
+ && !conrefsw
+ && !econtext(tags[ts].tetd->etdmod, tags[ts].tpos, &tags[ts].status)) {
+ if (BADPTR(nextetd))
+ sgmlerr(54, conpcb, tags[ts].tetd->etdgi+1, (UNCH *)0);
+ else
+ sgmlerr(30, conpcb, tags[ts].tetd->etdgi+1, nextetd->etdgi+1);
+ }
+ /* If the current tag ended with ETI, decrement the etictr.
+ If etictr is now zero, disable the NET delimiter.
+ */
+ if (GET(tags[ts--].tflags, TAGNET) && --etictr==0)
+ lexcon[lex.d.net] = lexcnm[lex.d.net] = lex.l.nonet;
+
+ minmsgsw = BITOFF(tags[ts+1].tetd->etdmin, EMO);
+ if (!conrefsw && minmsgsw && (etagimsw || etagmin==MINETAG)) {
+ /* Minimization caused by NET delimiter. */
+ if (BADPTR(etagreal)) ecode = 46;
+ /* Minimization caused by a containing end-tag. */
+ else {ecode = 20; eparm2 = etagreal->etdgi+1;}
+ }
+ else if (!conrefsw && etagmin==MINSTAG && (minmsgsw || ts<=0)) {
+ /* Minimization caused by out-of-context start-tag. */
+ if (!BADPTR(stagreal)) {
+ ecode = ts>0 ? 39 : 89;
+ eparm2 = stagreal->etdgi+1;
+ }
+ /* Minimization caused by out-of-context data. */
+ else if (stagreal==ETDCDATA) ecode = ts>0 ? 47 : 95;
+ /* Minimization caused by out-of-context short start-tag. */
+ else ecode = ts>0 ? 48 : 96;
+ if (ts<=0 && ecode) eodsw = 1;
+ }
+ if (ecode) sgmlerr((UNS)ecode, conpcb, tags[ts+1].tetd->etdgi+1, eparm2);
+ /* TEMP: See if parser bug caused stack to go below zero. */
+ else if (ts<0) {sgmlerr(64, conpcb, (UNCH *)0, (UNCH *)0); ts = 0;}
+ TRACEDSK(&tags[ts], &tags[ts+1], ts, etictr);
+}
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/pars2.c b/usr.bin/sgmls/sgmls/pars2.c
new file mode 100644
index 0000000..cc4c4ec
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pars2.c
@@ -0,0 +1,1308 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+/* PARSE: Parse a source input stream with specified lexical and state tables.
+ Return to caller with action code.
+*/
+int parse(pcb)
+struct parse *pcb; /* Current parse control block. */
+{
+ int rc; /* Return code from ENTREF. */
+
+ while (1) {
+ NEWCC;
+ pcb->input = pcb->plex[*FPOS];
+ pcb->state = pcb->newstate;
+ pcb->newstate = (*(pcb->ptab + pcb->state)) [pcb->input];
+ pcb->action = (*(pcb->ptab + pcb->state + 1)) [pcb->input];
+ TRACEPCB(pcb);
+ switch (pcb->action) {
+ case RC2_: /* Back up two characters. */
+ REPEATCC;
+ case RCC_: /* Repeat current character. */
+ REPEATCC;
+ case NOP_: /* No action necessary.*/
+ continue;
+
+ case RS_: /* Record start: ccnt=0; ++rcnt.*/
+ ++RCNT; CTRSET(RSCC);
+ continue;
+
+ case GET_: /* EOB or dull EOS or EE found: keep going.*/
+ if (entget()==-1) {pcb->action = EOD_; break;}/* Signal if EOD.*/
+ continue;
+
+ case EOF_: /* Illegal entity end; return EE_. */
+ synerr(E_EOF, pcb);
+ pcb->action = EE_;
+ case EE_: /* Important EOS or EE found: return to caller.*/
+ if (entget()==-1) pcb->action = EOD_; /* Signal if EOD. */
+ break;
+
+ case PER_: /* Parameter entity reference. */
+ REPEATCC; /* Use PERO as 1st char of entity name. */
+ parsenm(entbuf, ENTCASE);
+ parse(&pcbref); /* Handle REFC or other terminator. */
+ rc = entref(entbuf);
+ if (rc==ENTPI) {pcb->action = PIE_; break;}
+ continue;
+
+ case ER_: /* General entity reference; continue. */
+ parsenm(entbuf, ENTCASE);
+ parse(&pcbref); /* Handle REFC or other terminator. */
+ rc = entref(entbuf);
+ if (rc==ENTDATA) {pcb->action = DEF_; break;}
+ if (rc==ENTPI) {pcb->action = PIE_; break;}
+ continue;
+
+
+ case PEX_: /* Parameter entity reference; return. */
+ REPEATCC; /* Use PERO as 1st char of entity name. */
+ case ERX_: /* General entity reference; return. */
+ parsenm(entbuf, ENTCASE);
+ parse(&pcbref); /* Handle REFC or other terminator. */
+ rc = entref(entbuf);
+ if (rc == ENTDATA){
+ /* Reference to external data/subdoc entity in replaceable
+ character data. */
+ if (BITON(entdatsw, NDECONT)) {
+ switch (((PNE)data)->nextype) {
+ case ESNCDATA:
+ case ESNSDATA:
+ /* The standard says `non-SGML data entity'
+ but the amendment should have changed it
+ to `external data entity'. */
+ synerr(145, pcb);
+ break;
+ case ESNNDATA:
+ case ESNSUB:
+ /* This is definitely illegal. */
+ synerr(141, pcb);
+ break;
+ }
+ entdatsw = 0;
+ continue;
+ }
+ pcb->action = DEF_;
+ }
+ else if (rc == ENTPI) {
+ /* Reference to PI entity not allowed in replaceable
+ character data. */
+ synerr(59, pcb);
+ entpisw = 0;
+ continue;
+ }
+ else if (rc) pcb->action = EE_;
+ break;
+
+ case CRN_: /* Character reference: numeric. */
+ parsetkn(entbuf, NU, NAMELEN);
+ parse(&pcbref); /* Handle reference terminator. */
+ pcb->action = charrefn(entbuf, pcb);
+ if (pcb->action==CRN_) continue; /* Invalid reference */
+ break;
+
+ case CRA_: /* Character reference: alphabetic. */
+ parsenm(entbuf, NAMECASE);
+ parse(&pcbref); /* Handle reference terminator. */
+ charrefa(entbuf);
+ continue;
+
+ case SYS_: /* Invalid NONCHAR: send msg and ignore. */
+ synerr(E_SYS, pcb);
+ if (*FPOS == DELNONCH) NEWCC;
+ continue;
+
+ case NON_: /* Valid NONCHAR: prefix and shift encoding. */
+ synerr(60, pcb);
+ pcb->action = datachar(*FPOS, pcb);
+ break;
+ case NSC_:
+ synerr(60, pcb);
+ NEWCC;
+ nonchbuf[1] = *FPOS;
+ pcb->action = NON_;
+ break;
+ case PCI_: /* Previous character was invalid (INV_). */
+ REPEATCC;
+ case INV_: /* Markup ended by invalid char; repeat char. */
+ synerr(9, pcb);
+ REPEATCC;
+ break;
+
+ case LNR_: /* Previous char exceeded len; back up to it. */
+ REPEATCC;
+ case LEN_: /* Token too long; ignore excess character. */
+ synerr(3, pcb);
+ continue;
+
+ case RCR_: /* Repeat current char and return to caller. */
+ REPEATCC;
+ default: /* Actions for specific parse. */
+ break;
+ }
+ return (int)pcb->action;
+ }
+}
+/* CHARREFA: Resolve an alphabetical reference to a function character
+ and put the character in the read buffer.
+ If reference is bad, issue an error message.
+*/
+VOID charrefa(r)
+UNCH *r; /* Undelimited char ref (with length and EOS). */
+{
+ UNCH thechar;
+
+ thechar = mapsrch(funtab, r+1);
+ if (thechar == 0)
+ synerr(62, &pcbref);
+ else {
+ /* This isn't ideal, because the character position will still
+ be wrong for one line. */
+ if (thechar == RSCHAR) RCNT--;
+ setcurchar(thechar);
+ REPEATCC;
+ }
+}
+
+/* Make the current character ch. */
+
+VOID setcurchar(ch)
+int ch;
+{
+ /* If we're reading directly from an internal entity, we can't
+ change the entity, since the entity might be referenced again.
+ So in this case we copy the entity. This is inefficient, but
+ it will only happen in a case like this:
+
+ <!entity % amp "&">
+ <!entity e "x%amp;#SPACE;">
+
+ Usually character references will have been processed while the
+ entity was being defined. */
+ if (*FPOS != ch) {
+ if (!FILESW && !COPIEDSW) {
+ UNCH *s = savestr(FBUF + 1);
+ FPOS = s + (FPOS - FBUF - 1);
+ FBUF = s - 1;
+ COPIEDSW = 1;
+ }
+ *FPOS = ch;
+ }
+}
+
+/* CHARREFN: Resolve a numeric character reference.
+ If reference is bad, issue an error message.
+*/
+
+int charrefn(r, pcb)
+UNCH *r; /* Undelimited character reference. */
+struct parse *pcb; /* Current parse control block. */
+{
+ int thechar;
+
+ thechar = atoi((char *)r);
+ if (thechar<0 || thechar>255) {
+ synerr(61, &pcbref);
+ return((int)pcb->action);
+ }
+ return datachar(thechar, pcb);
+}
+
+/* Return ch as a datachar. If this a non-SGML character which might
+confuse the parser, shift it to a code that won't and place it in a
+special buffer which has DELNONCH in the preceding byte. Otherwise
+put it the read buffer. */
+
+int datachar(ch, pcb)
+int ch;
+struct parse *pcb;
+{
+ switch (ch) {
+ case EOS:
+ case EOFCHAR:
+ case EOBCHAR:
+ case GENRECHAR:
+ case DELCDATA:
+ case DELSDATA:
+ case DELNONCH:
+ /* A potentially confusing character which must be prefixed
+ with DELNONCH. */
+ nonchbuf[1] = SHIFTNON((UNCH)ch);
+ return NON_;
+ }
+ setcurchar(ch);
+ /* If in content, return DCE_ for element content, DAF_ for mixed. */
+ /* If not content, it must be a literal parse, so return MLA_. */
+ if (pcb == conpcb) {
+ if (pcb == &pcbcone)
+ return DCE_;
+ else {
+ data = FPOS;
+ /* Action for DAF_ will do REPEATCC. */
+ NEWCC;
+ return DAF_;
+ }
+ }
+ else
+ return MLA_;
+}
+/* INITATT: Initialize al with adl. */
+
+VOID initatt(adl)
+struct ad *adl;
+{
+ notadn = 0; /* No NOTATION attribute yet. */
+ conrefsw = 0; /* Assume no content reference att. */
+ /* Copy attribute definition list as a template. */
+ memcpy((UNIV)al, (UNIV)adl, (1+ADN(adl))*ADSZ);
+}
+
+/* PARSEATT: Parse attribute specification list.
+ Make a current copy of the attribute definition list
+ and update it with the user's specifications.
+ Indicate each attribute that was specified in the
+ list (as opposed to defaulted) by setting the ASPEC flag.
+ If no attributes were specified, return NULL. Otherwise,
+ if in the prolog, make a permanent copy of the list and
+ return its pointer. If not in the prolog, return al.
+*/
+struct ad *parseatt(adl, pt)
+struct ad *adl; /* Attribute definition list. */
+UNCH *pt; /* Tokenization area: tbuf[TAGLEN+ATTSPLEN]. */
+{
+ UNCH *antvptr;
+ UNCH *nm = 0; /* Pointer to saved name in tbuf (with length). */
+ int adn = -1; /* Position of attribute in list (-1=empty). */
+ UNCH *tbuflim = pt + ATTSPLEN;
+ mdessv = es; /* Save es for checking entity nesting. */
+ initatt(adl);
+ while (pt<=tbuflim) {
+ parse(&pcbstag);
+ switch (pcbstag.action) {
+ case NVS: /* Att name or value token found. */
+ parsenm(pt, NAMECASE); /* Case translation wanted on name. */
+ pt += *(nm = pt); /* Save name while pointing past it. */
+ continue;
+
+ case AVD: /* Delimited value found. */
+ case AVDA: /* Delimited value found (alternate delimiter). */
+ /* Find position (adn) of saved attribute name in list. */
+ adn = anmget((int)ADN(al), nm);
+ parselit(pt,
+ (adn == 0 || ADTYPE(al, adn) == ACHARS)
+ ? &pcblitr
+ : &pcblitt,
+ LITLEN,
+ (pcbstag.action==AVD) ? lex.d.lit : lex.d.lita);
+ if (adn == 0) {
+ /* Error: unrecognized attribute name. */
+ sgmlerr(13, &pcbstag, nm+1, pt);
+ continue;
+ }
+ /* Tokenize and validate value; let it default if an error. */
+ /* Put value in list and bump ptr by the normalized length
+ (which is always >= the actual length). */
+ if (!attval(1, pt, adn, adl)) pt += ADLEN(al,adn);
+ continue;
+ case AVU: /* Attribute value found: undelimited. */
+ if (!sd.shorttag) sgmlerr(196, &pcbstag, (UNCH *)0, (UNCH *)0);
+ parsetkn(pt, NMC, LITLEN);
+ /* Find position (adn) of saved attribute name in list. */
+ if ((adn = anmget((int)ADN(al), nm))==0) {
+ /* Error: unrecognized attribute name. */
+ sgmlerr(13, &pcbstag, nm+1, pt);
+ continue;
+ }
+ /* Tokenize and validate value; let it default if an error. */
+ /* Put value in list and bump ptr by the normalized length
+ (which is always >= the actual length). */
+ if (!attval(1, pt, adn, adl)) pt += ADLEN(al,adn);
+ continue;
+
+ case NASV: /* Saved NVS was really an NTV. */
+ REPEATCC; /* Put back next token starter. */
+ pt = nm; /* Back up to NVS. */
+ case NTV: /* Name token value found. */
+ if (!sd.shorttag) sgmlerr(195, &pcbstag, (UNCH *)0, (UNCH *)0);
+ if (pcbstag.action==NTV) parsenm(pt, NAMECASE);
+ if ((adn = antvget((int)ADN(al), pt, &antvptr))==0) {
+ /* Error: unrecognized name token value. */
+ sgmlerr(74, &pcbstag, pt+1, (UNCH *)0);
+ continue;
+ }
+ /* Validate value; let it default if an error. */
+ /* Put value in list and bump ptr by the normalized length
+ (which is always >= the actual length). */
+ if (!attval(0, antvptr+1, adn, adl)) pt += ADLEN(al,adn);
+ continue;
+
+ default: /* All attributes have been parsed. */
+ REPEATCC; /* Put next char back for tag close parse. */
+ break;
+ }
+ break;
+ }
+ if (pt>tbuflim) synerr(75, &pcbstag);
+ if (es!=mdessv) synerr(37, &pcbstag);
+ if (adn<0) return((struct ad *)0); /* List was empty. */
+ TRACEADL(al);
+ return al;
+}
+/* ATTVAL: Validate a specified attribute value. Issue a message if it is
+ the wrong type (or otherwise is not up to spec), and use the default.
+ Call PARSEVAL to tokenize the value, unless it is a CDATA string.
+ If the attribute is a group, the value is a string.
+ For other types, the token count is set by PARSEVAL if the value
+ is syntactically correct. If incorrect (or if CDATA) the token
+ count is zero (i.e., the value is a string).
+ The length of a token does not include the length byte, and
+ there is no EOS. A string length (as always) includes both
+ the length byte and the EOS.
+ If it is a CONREF attribute, set a switch for STAG().
+ If it is a CURRENT attribute, store the value as the new default.
+*/
+#define DEFVAL adl[adn].addef /* Default value of current attribute. */
+#define DEFNUM adl[adn].adnum /* Default group size of current attribute. */
+#define DEFLEN adl[adn].adlen /* Length of default value of current attribute.*/
+int attval(mtvsw, adval, adn, adl)
+int mtvsw; /* Must tokenize value: 1=yes; 0=no. */
+UNCH *adval; /* Untokenized attribute value. */
+int adn; /* Attribute's position in list. */
+struct ad *adl; /* Element's master att def list. */
+{
+ int errcode; /* Value/declaration conflict error code. */
+
+ if (GET(ADFLAGS(al,adn), ASPEC)) /* Can't respecify same attribute. */
+ {sgmlerr(73, &pcbstag, ADNAME(al,adn), adval); return(1);}
+ SET(ADFLAGS(al,adn), ASPEC); /* Indicate att was specified. */
+ if (GET(ADFLAGS(al,adn), ACONREF)) /* If attribute is content reference: */
+ conrefsw = TAGREF; /* Set switch for STAG(). */
+ if (mtvsw && ADTYPE(al,adn)!=ACHARS) {
+ /* If no syntax errors, check for proper group membership. */
+ if ( ((errcode = parseval(adval, ADTYPE(al,adn), lbuf))==0)
+ && GET(ADFLAGS(al,adn), AGROUP)
+ && !amemget(&al[adn], ADNUM(al,adn), lbuf) ) errcode = 18;
+ /* If syntax or group membership error, send message and exit. */
+ if (errcode) {
+ sgmlerr(errcode, &pcbstag, ADNAME(al,adn), adval);
+ SET(ADFLAGS(al,adn), AERROR);
+ return(1);
+ }
+ /* Replace specified value in adval with tokenized in lbuf. */
+ ustrcpy(adval, lbuf);
+ if (BITOFF(ADFLAGS(al,adn), AGROUP)) ADNUM(al,adn) = (UNCH)tokencnt;
+ }
+ if (!mtvsw)
+ adval--;
+ /* If attribute is FIXED, specified value must equal default. */
+ if (BITON(ADFLAGS(al,adn), AFIXED) && ustrcmp(adval, DEFVAL)) {
+ /* Since the value has been tokenized, don't use it in the
+ error message. */
+ sgmlerr(67, &pcbstag, ADNAME(al,adn), (UNCH *)0);
+ SET(ADFLAGS(al,adn), AERROR);
+ return(1);
+ }
+ ADLEN(al,adn) = vallen(ADTYPE(al,adn), ADNUM(al,adn), adval);
+ if (ADLEN(al,adn) > LITLEN) {
+ sgmlerr(224, &pcbstag, ADNAME(al,adn), (UNCH *)0);
+ SET(ADFLAGS(al,adn), AERROR);
+ return 1;
+ }
+ ADVAL(al,adn) = adval;
+ /* If attribute is CURRENT, value is new default.*/
+ if (GET(ADFLAGS(al,adn), ACURRENT)) {
+ if (ADLEN(al,adn)>DEFLEN) {
+ ds.attdef += (ADLEN(al,adn) - DEFLEN);
+ DEFLEN = ADLEN(al,adn);
+ }
+ DEFVAL = replace(DEFVAL, ADVAL(al,adn));
+ DEFNUM = ADNUM(al,adn);
+ }
+ return(0); /* Indicate value was valid. */
+}
+/* ADLVAL: Validate the completed attribute definition list (defaults plus
+ specified values). Issue a message if an
+ attribute is required or current and its value is NULL.
+*/
+VOID adlval(adsz, newetd)
+int adsz; /* Size of list. */
+struct etd *newetd; /* Element type definition for this element. */
+{
+ int adn = 1; /* Position in list. */
+ UNCH *npt, *pt; /* Ptr save areas. */
+ UNCH nptsv; /* Save area for ptr value (length?). */
+ struct dcncb *dpt; /* Save area for dcncb ptr. */
+
+ aentctr = 0; /* Number of AENTITY tokens in this att list. */
+ idrctr = 0; /* Number of IDREF tokens in this att list. */
+ do {
+ if (ADVAL(al,adn)==NULL) { /* NULL value */
+ if (GET(ADFLAGS(al,adn), AREQ+ACURRENT)) { /*Error if REQ, CURRENT*/
+ sgmlerr(19, &pcbstag, ADNAME(al,adn), (UNCH *)0);
+ SET(ADFLAGS(al,adn), AINVALID);
+ }
+ }
+ else switch (ADTYPE(al,adn)) {
+ case AENTITY: /* Return data ecb pointer if valid entity. */
+ aenttst(adn, ADVAL(al,adn));
+ break;
+ case AENTITYS: /* Return data ecb pointers if valid entities. */
+ pt = ADVAL(al,adn);
+ tokencnt = (int)ADNUM(al,adn);
+ while (tokencnt--) {
+ nptsv = *(npt = pt + *pt+1);
+ *pt += 2; *npt = EOS;
+ aenttst(adn, pt);
+ *pt -= 2; *(pt = npt) = nptsv;
+ }
+ break;
+ case AID:
+ /* Define ID; msg if it already exists. */
+ if (iddef(ADVAL(al,adn))) {
+ sgmlerr(71, &pcbstag, ADNAME(al,adn), ADVAL(al,adn)+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ continue;
+ }
+ ++ds.idcnt;
+ break;
+ case AIDREF:
+ idreftst(adn, ADVAL(al,adn));
+ break;
+ case AIDREFS:
+ pt = ADVAL(al,adn);
+ tokencnt = (int)ADNUM(al,adn);
+ while (tokencnt--) {
+ nptsv = *(npt = pt + *pt+1);
+ *pt += 2; *npt = EOS;
+ idreftst(adn, pt);
+ *pt -= 2; *(pt = npt) = nptsv;
+ }
+ break;
+ case ANOTEGRP: /* Return notation identifier. */
+ if (GET(ADFLAGS(al,adn), ASPEC)) notadn = adn;/*NOTATION specified*/
+ if ((dpt = dcnfind(ADVAL(al,adn)))==0) {
+ sgmlerr(77, &pcbstag, ADNAME(al,adn), ADVAL(al,adn)+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ }
+ else ADDATA(al,adn).x = dpt;
+ break;
+ }
+ if (!sd.shorttag && !sd.omittag && ADVAL(al,adn)!=NULL
+ && !GET(ADFLAGS(al,adn), ASPEC+AINVALID))
+ sgmlerr(197, &pcbstag, ADNAME(al,adn), (UNCH *)0);
+ } while ((adn+=BITON(ADFLAGS(al,adn),AGROUP) ? (int)ADNUM(al,adn)+1 : 1)<=adsz);
+
+ /* Error if NOTATION specified with CONREF attribute or EMPTY element. */
+ if (notadn && (conrefsw
+ || (newetd && GET(newetd->etdmod->ttype, MNONE)))) {
+ sgmlerr((UNS)(conrefsw ? 84 : 76), &pcbstag,
+ ADNAME(al,notadn), ADVAL(al,notadn)+1);
+ SET(ADFLAGS(al,notadn), AINVALID);
+ }
+}
+/* AENTTST: Validate an individual ENTITY token in AENTITY or AENTITYS value.
+*/
+VOID aenttst(adn, pt)
+int adn; /* Position in list. */
+UNCH *pt; /* Ptr to current ENTITY token in value. */
+{
+ struct entity *ept; /* Save area for ecb ptr. */
+
+ if (++aentctr>GRPCNT) {
+ sgmlerr(136, &pcbstag, ADNAME(al,adn), pt+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ return;
+ }
+ if ( (ept = entfind(pt))==0
+ && (ecbdeflt==0 || (ept = usedef(pt))==0) ) {
+ sgmlerr(ecbdeflt ? 151 : 72, &pcbstag, ADNAME(al,adn), pt+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ return;
+ }
+ if (ept->estore==ESX || ept->estore==ESC || ept->estore==ESN) {
+ /* Error if DCN has no notation identifier. */
+ if (ept->estore==ESN && NEXTYPE(ept->etx.n)!=ESNSUB
+ && !NEDCNDEFINED(ept->etx.n)) {
+ sgmlerr(78, &pcbstag, NEDCN(ept->etx.n)+1,
+ pt+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ }
+ }
+ else {
+ sgmlerr(86, &pcbstag, ADNAME(al,adn), pt+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ }
+}
+/* IDREFTST: Validate an individual IDREF token in an IDREF or IDREFS value.
+*/
+VOID idreftst(adn, pt)
+int adn; /* Position in list. */
+UNCH *pt; /* Ptr to current IDREF token in value. */
+{
+ struct fwdref *rp;
+ if (++idrctr>GRPCNT) {
+ sgmlerr(70, &pcbstag, ADNAME(al,adn), pt+1);
+ SET(ADFLAGS(al,adn), AINVALID);
+ return;
+ }
+ /* Note IDREF; indicate if ID exists. */
+ if ((rp = idref(pt)) != 0)
+ rp->msg = saverr(69, &pcbstag, ADNAME(al,adn), pt+1);
+ ++ds.idrcnt;
+}
+/* ANMGET: Locate an attribute name in an attribute definition list.
+*/
+int anmget(adsz, nm)
+int adsz; /* Size of list. */
+UNCH *nm; /* Value to be found (with length byte). */
+{
+ int adn = 0; /* Position in list. */
+
+ while (++adn <= adsz && ustrcmp(nm+1, ADNAME(al,adn))) {
+ if (BITON(ADFLAGS(al,adn), AGROUP)) adn += (int)ADNUM(al,adn);
+ }
+ return (adn > adsz) ? 0 : adn;
+}
+/* ANTVGET: Find the position of a name token value in an attribute list.
+ Return the position of the attribute definition, or zero
+ if none was found. Set pp to the value, if non-NULL.
+*/
+int antvget(adsz, nm, pp)
+int adsz; /* Size of list. */
+UNCH *nm; /* Value to be found (with length byte). */
+UNCH **pp; /* Store value here */
+{
+ int adn = 0; /* Position in list. */
+
+ while (++adn<=adsz) {
+ /* Test only name group members. */
+ if (BITON(ADFLAGS(al,adn), AGROUP)) {
+ int advn; /* Position of value in sub-list. */
+ if ((advn = amemget(&al[adn], (int)ADNUM(al,adn), nm))!=0) {
+ if (pp)
+ *pp = al[adn+advn].adname;
+ return adn;
+ }
+ adn += (int)ADNUM(al,adn);
+ }
+ }
+ return 0;
+}
+/* AMEMGET: Get the position of a member in an attribute name token group.
+ Returns the position, or zero if not found.
+ The length byte is ignored in the comparison so that final
+ form tokens from ATTVAL can be compared to group members.
+*/
+int amemget(anmtgrp, adsz, nm)
+struct ad anmtgrp[]; /* Name token group. */
+int adsz; /* Size of group. */
+UNCH *nm; /* Name to be found (with length byte). */
+{
+ int adn = 0; /* Position in group. */
+
+ while ( ++adn<=adsz && ustrncmp(nm+1, anmtgrp[adn].adname+1, (UNS)*nm-1)) ;
+ return (adn>adsz) ? 0 : adn;
+}
+/* VALLEN: Returns the length of an attribute value for capacity
+ calculations. Normally, the length is NORMSEP plus the number
+ of characters. For tokenized lists, it is NORMSEP,
+ plus the number of characters in the tokens, plus
+ NORMSEP for each token.
+ ACHARS and tokenized lists don't have a length byte.
+
+*/
+UNS vallen(type, num, def)
+int type; /* ADTYPE(al,adn) */
+int num; /* ADNUM(al,adn) */
+UNCH *def; /* ADVAL(al,adn) */
+{
+ if (type == ACHARS)
+ return ustrlen(def) + NORMSEP;
+ if (type < ATKNLIST)
+ return *def - 2 + NORMSEP;
+ return ustrlen(def) + num * (NORMSEP - 1) + NORMSEP;
+}
+/* PARSEGRP: Parse GI names, get their etds, and form an array of pointers
+ to them. The array is terminated by a NULL pointer.
+ The number of pointers (including the NULL) is returned.
+ The grp buffer must have room for GRPCNT+1 etds.
+*/
+UNS parsegrp(grp, pcb, tbuf)
+struct etd *grp[]; /* Buffer for building the group. */
+struct parse *pcb; /* Current parse control block. */
+UNCH *tbuf;
+{
+ int grpcnt = 0; /* Number of etds in the group. */
+ int i;
+ int essv = es; /* Entity stack level when grp started. */
+
+ while (parse(pcb)!=GRPE && grpcnt<GRPCNT) {
+ switch (pcb->action) {
+ case NAS_: /* GI name: get its etd for the group. */
+ grp[grpcnt] = etddef(parsenm(tbuf, NAMECASE));
+ for (i = 0; i < grpcnt; i++)
+ if (grp[i] == grp[grpcnt]) {
+ mderr(98, ntoa(grpcnt + 1), grp[grpcnt]->etdgi + 1);
+ break;
+ }
+ if (i == grpcnt)
+ grpcnt++;
+ continue;
+
+ case EE_: /* Entity ended (correctly or incorrectly). */
+ if (es<essv) {synerr(37, pcb); essv = es;}
+ continue;
+
+ case PIE_: /* PI entity reference (invalid). */
+ entpisw = 0; /* Reset PI entity indicator. */
+ synerr(59, pcb);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ grp[grpcnt++] = 0; /* NULL pointer indicates end of group. */
+ if (es!=essv) synerr(37, pcb);
+ return grpcnt; /* Return number of ptrs in group. */
+}
+/* PARSNGRP: Parse notation names, get their dcncbs, and form an array of
+ pointers to them. The array is terminated by a NULL pointer.
+ The number of pointers (including the NULL) is returned.
+ The grp buffer must have room for GRPCNT+1 members.
+*/
+UNS parsngrp(grp, pcb, tbuf)
+struct dcncb *grp[]; /* Buffer for building the group. */
+struct parse *pcb; /* Current parse control block. */
+UNCH *tbuf;
+{
+ int grpcnt = 0; /* Number of members in the group. */
+ int i;
+ int essv = es; /* Entity stack level when grp started. */
+
+ while (parse(pcb)!=GRPE && grpcnt<GRPCNT) {
+ switch (pcb->action) {
+ case NAS_: /* Member name: get its control block. */
+ grp[grpcnt] = dcndef(parsenm(tbuf, NAMECASE));
+ for (i = 0; i < grpcnt; i++)
+ if (grp[i] == grp[grpcnt]) {
+ mderr(98, ntoa(grpcnt + 1), grp[grpcnt]->ename + 1);
+ break;
+ }
+ if (i == grpcnt)
+ grpcnt++;
+ continue;
+
+ case EE_: /* Entity ended (correctly or incorrectly). */
+ if (es<essv) {synerr(37, pcb); essv = es;}
+ continue;
+
+ case PIE_: /* PI entity reference (invalid). */
+ entpisw = 0; /* Reset PI entity indicator. */
+ synerr(59, pcb);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ grp[grpcnt++] = 0; /* NULL pointer indicates end of group. */
+ if (es!=essv) synerr(37, pcb);
+ return grpcnt; /* Return number of ptrs in group. */
+}
+/* COPYGRP: Allocate storage for a group and copy the group into it.
+*/
+PETD *copygrp(pg, grpsz)
+PETD pg[]; /* Pointer to a group (array of etd ptrs). */
+UNS grpsz; /* Number of ptrs in grp, including final NULL. */
+{
+ UNS glen; /* Group length in characters. */
+ PETD *gnm; /* Ptr to permanent name group. */
+
+ if (pg==0) return (PETD *)0;
+ glen = grpsz * sizeof(struct etd *);
+ memcpy( (UNIV)(gnm = (struct etd **)rmalloc(glen)) , (UNIV)pg, glen );
+ return gnm;
+}
+/* INGRP: Locate an etd in a name group and return its index+1 (or zero
+ if not found).
+*/
+int ingrp(pg, ketd)
+PETD pg[]; /* Array of pointers to etds. */
+PETD ketd; /* Pointer to etd to be found in group. */
+{
+ int i = 0; /* Array index. */
+
+ while (pg[i]) if (pg[i++]==ketd) return i;
+ return 0;
+}
+/* PARSELIT: Parse a delimited string and collect it into a token.
+ Caller supplies buffer, which must be 1 longer than
+ maximum string allowed.
+ Caller also supplies character that delimits the string.
+ TODO: Return 1 if CDATA, SDATA or NONSGML occurred.
+*/
+#ifdef USE_PROTOTYPES
+VOID parselit(UNCH *tbuf, struct parse *pcb, UNS maxlen, UNCH del)
+#else
+VOID parselit(tbuf, pcb, maxlen, del)
+UNCH *tbuf; /* Work area for tokenization (parmlen+1). */
+struct parse *pcb; /* Current parse control block. */
+UNS maxlen; /* Maximum length of token. */
+UNCH del; /* Literal delimiter: LIT LITA PIC EOS */
+#endif
+{
+ UNCH *pt = tbuf; /* Current pointer into tbuf. */
+ UNCH lexsv = lexlms[del];/* Saved lexlms value of delimiter. */
+ int essv = es; /* Entity stack level when literal started. */
+ UNCH datadel; /* Delimiter for CDATA/SDATA entity. */
+ int parmlen = (int)maxlen; /* Working limit (to be decremented). */
+
+ lexlms[del] = lex.l.litc; /* Set delimiter to act as literal close. */
+ do {
+ switch (parse(pcb)) {
+ case LP2_: /* Move 2nd char back to buffer; redo prev.*/
+ REPEATCC;
+ case LPR_: /* Move previous char to buffer; REPEATCC; */
+ REPEATCC;
+ case MLA_: /* Move character to buffer. */
+ *pt++ = *FPOS; --parmlen;
+ continue;
+
+ case FUN_: /* Function char found; replace with space.*/
+ *pt++ = ' '; --parmlen;
+ continue;
+
+ case RSM_: /* Record start: ccnt=0; ++rcnt.*/
+ ++RCNT; CTRSET(RSCC); *pt++ = *FPOS; --parmlen;
+ continue;
+
+ case ERX_: /* Entity reference: cancel LITC delim. */
+ case PEX_: /* Parameter entity ref: cancel LITC delim.*/
+ lexlms[del] = lexsv;
+ continue;
+
+ case EE_:
+ if (es<essv) {
+ synerr(37, pcb);
+ essv = es;
+ }
+ /* If back at top level, re-enable the LITC delimiter. */
+ if (es==essv) lexlms[del] = lex.l.litc;
+ continue;
+
+ case MLE_: /* Char not allowed in minimum literal. */
+ synerr(63, pcb);
+ continue;
+
+ case DEF_: /* Data entity: add it to buffer. */
+ if (pcb == &pcblitt) {
+ int parmlensv = parmlen;
+ entdatsw = 0;
+ parmlen = tokdata(pt, parmlen);
+ if (parmlen < 0)
+ break;
+ pt += parmlensv - parmlen;
+ continue;
+ }
+ if ((parmlen -= (int)datalen+2)<0) {entdatsw = 0; break;}
+ *pt++ = datadel =
+ BITON(entdatsw, CDECONT) ? DELCDATA : DELSDATA;
+ entdatsw = 0;
+ memcpy( pt , data, datalen );
+ pt += datalen;
+ *pt++ = datadel;
+ continue;
+
+ case NON_: /* Non-SGML char (delimited and shifted). */
+ if ((parmlen -= 2)<0) break;
+ memcpy( pt , nonchbuf, 2 );
+ pt += 2;
+ continue;
+
+ case RPR_: /* Remove character from buffer. */
+ --pt; ++parmlen;
+ break;
+
+ case EOD_:
+ exiterr(92, pcb);
+
+ default:
+ break;
+ }
+ break;
+ } while (parmlen>=0 && pcb->action!=TER_);
+
+ if (parmlen<0) {--pt; sgmlerr(134, pcb, ntoa((int)maxlen),(UNCH *)0); REPEATCC;}
+ datalen = (UNS)(pt-tbuf);/* To return PI string to text processor. */
+ *pt++ = EOS;
+ lexlms[del] = lexsv; /* Restore normal delimiter handling. */
+ if (es!=essv) synerr(37, pcb);
+ return;
+}
+
+/* Handle a data entity in a tokenized attribute value literal.
+Parmlen is amount of space left. Return new parmlen. If there's not
+enough space return -1, and copy up to parmlen + 1 characters. */
+
+int tokdata(pt, parmlen)
+UNCH *pt;
+int parmlen;
+{
+ int skip = (pcblitt.newstate == 0);
+ int i;
+
+ for (i = 0; parmlen >= 0 && i < datalen; i++) {
+ switch (data[i]) {
+ case RSCHAR:
+ /* ignore it */
+ break;
+ case RECHAR:
+ case TABCHAR:
+ case SPCCHAR:
+ if (!skip) {
+ *pt++ = data[i];
+ parmlen--;
+ skip = 1;
+ }
+ break;
+ default:
+ if (data[i] == DELNONCH) {
+ assert(i + 1 < datalen);
+ if ((parmlen -= 2) < 0)
+ break;
+ *pt++ = DELNONCH;
+ *pt++ = data[++i];
+ skip = 0;
+ }
+ else {
+ *pt++ = data[i];
+ parmlen--;
+ skip = 0;
+ }
+ break;
+ }
+ }
+ pcblitt.newstate = skip ? 0 : pcblittda;
+ return parmlen;
+}
+
+
+/* PARSEMD: Parser for markup declarations.
+ It returns a token each time it is called.
+
+*/
+int parsemd(pt, namecase, lpcb, tokenlen)
+UNCH *pt; /* Token buffer: >=tokenlen+2. */
+int namecase; /* Case translation: ENTCASE NAMECASE AVALCASE. */
+struct parse *lpcb; /* Parse control block for literal parse. */
+UNS tokenlen; /* Max length of expected token: NAMELEN LITLEN */
+{
+ struct parse *pcb; /* Current parse control block. */
+
+ pcb = (lpcb) ? &pcbmd : &pcbmdc; /* If no literal pcb, dcl is comment. */
+
+ doparse: while (parse(pcb)==EE_)
+ if (es<mdessv) {synerr(37, pcb); mdessv = es;}
+ if (pcb->action==PIE_) { /* PI entity reference not allowed. */
+ entpisw = 0; /* Reset PI entity indicator. */
+ synerr(59, pcb);
+ goto doparse;
+ }
+ ++parmno; /* Increment parameter counter. */
+ switch (pcb->action) {
+ case CDR: /* COM[1] (MINUS) occurred previously. */
+ REPEATCC;
+ return (int)pcb->action;
+ case LIT: /* Literal: CDATA with LIT delimiter. */
+ parselit(pt, lpcb, tokenlen, lex.d.lit);
+ return (int)pcb->action;
+ case LITE: /* Literal: CDATA with LITA delimiter. */
+ parselit(pt, lpcb, tokenlen, lex.d.lita);
+ return((int)(pcb->action = LIT));
+ case RNS: /* Reserved name started (after RNI). */
+ parsenm(pt, NAMECASE);
+ return (int)pcb->action;
+ case NAS: /* Name started. */
+ if (namecase!=AVALCASE) {
+ parsenm(pt, namecase);
+ return (int)pcb->action;
+ }
+ /* Treat attribute value as name character string. */
+ case NMT: /* Name token string. */
+ parsetkn(pt, NMC, (int)tokenlen); /* Get undelimited value. */
+ return (int)pcb->action;
+ case NUM: /* Number or number token string. */
+ parsetkn(pt, (UNCH)((int)tokenlen<=NAMELEN ? NU:NMC), (int)tokenlen);
+ return (int)pcb->action;
+ case PENR:
+ REPEATCC;
+ return (pcb->action = PEN);
+ case EOD_:
+ exiterr(133, pcb);
+ /* EXIT */
+ default: /* End of declaration. */
+ return (int)pcb->action; /* EMD GRPS MGRP PEN PGRP */
+ }
+}
+/* PARSEMOD: If the declared content was a keyword, the token count is zero
+ and it is only necessary to save the type. Otherwise,
+ collect the outermost token count and model type bytes for a model.
+ The count includes tokens found in nested groups also.
+ After building the model, parse for its occurrence indicator.
+*/
+struct thdr *parsemod(dctype)
+int dctype; /* Content type (0=model). */
+{
+ gbuf[0].ttype = (UNCH)dctype; /* Initialize content flags byte. */
+ if (dctype) {gbuf[0].tu.tnum = 0; return gbuf;} /* Return if not model. */
+
+ gbuf[0].tu.tnum = 0; /* Don't count 1st group or model header. */
+ gbuf[1].ttype = 0; /* Initialize 1st group type ... */
+ gbuf[1].tu.tnum = 0; /* and count. */
+ grplvl = 1; /* Content model is 1st level group. */
+ pcbgrcm.newstate = 0; /* Go parse the model group. */
+ /* Empty group is trapped during syntax parse; other errors return NULL. */
+ if (!parsegcm(&pcbgrcm, &gbuf[1], &gbuf[0])) return (struct thdr *)0;
+ parse(&pcbgrcs); /* Get the model suffix, if there is one. */
+ switch(pcbgrcs.action) {
+ case OPT: /* OPT occurrence indicator for model. */
+ SET(gbuf[1].ttype, TOPT|TXOPT);
+ break;
+ case REP: /* REP occurrence indicator for model. */
+ SET(gbuf[1].ttype, TREP|TXREP);
+ break;
+ case OREP: /* OREP occurrence indicator for model. */
+ SET(gbuf[1].ttype, TOREP|TXOREP);
+ break;
+ default: /* RCR_: Repeat char and return. */
+ break;
+ }
+ if (sw.swambig) ambig(); /* Check content model for ambiguity. */
+ return gbuf;
+}
+/* PARSEGCM: Collect token headers (struct thdr) into a group (array).
+ An etd is defined for each GI (if none exists) and its pointer is
+ stored in the header. The function is called recursively.
+*/
+struct thdr *parsegcm(pcb, pgh, gbuf)
+struct parse *pcb; /* Current parse control block. */
+struct thdr *pgh; /* Current group header in group buffer. */
+struct thdr *gbuf; /* Header for outermost group (model). */
+{
+#define MCON gbuf->ttype /* Model type (content attributes). */
+ struct thdr *pg=pgh; /* Current group token. */
+ struct thdr *pgsv=pgh; /* Saved current token for occ indicator. */
+ int optcnt = 0; /* Count of optional tokens in group. */
+ int essv = es; /* Entity stack level when grp started. */
+
+ while (gbuf->tu.tnum<=GRPGTCNT && pgh->tu.tnum<=GRPCNT && parse(pcb)!=GRPE)
+ switch (pcb->action) {
+
+ case NAS_: /* GI name: get its etd and store it. */
+ ++gbuf->tu.tnum; ++pgh->tu.tnum;
+ (pgsv = ++pg)->ttype = TTETD;
+ pg->tu.thetd = etddef(parsenm(tbuf, NAMECASE));
+ SET(MCON, MGI);
+ continue;
+
+ case RNS_: /* Reserved name started (#PCDATA). */
+ parsenm(tbuf, NAMECASE);
+ if (ustrcmp(tbuf+1, key[KPCDATA])) {
+ mderr(116, ntoa(gbuf->tu.tnum), tbuf+1);
+ return (struct thdr *)0;
+ }
+ /* If #PCDATA is the first non-group token, model is a phrase. */
+ if (!MCON) SET(MCON, MPHRASE);
+ case DTAG: /* Data tag template ignored; treat as #PCDATA. */
+ if (pcb->action==DTAG) SET(pgh->ttype, TTSEQ); /* DTAG is SEQ grp. */
+ ++gbuf->tu.tnum; ++pgh->tu.tnum;
+ (++pg)->ttype = TTCHARS+TOREP;/* #PCDATA is OPT and REP. */
+ pg->tu.thetd = ETDCDATA;
+ ++optcnt; /* Ct opt tokens to see if grp is opt.*/
+ SET(MCON, MCHARS);
+ continue;
+
+ case GRP_: /* Group started. */
+ ++gbuf->tu.tnum; ++pgh->tu.tnum;
+ (pgsv = ++pg)->ttype = 0; /* Type will be set by connector. */
+ pg->tu.tnum = 0; /* Group has number instead of etd. */
+ if (++grplvl>GRPLVL) {
+ mderr(115, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ return (struct thdr *)0;
+ }
+ pg = parsegcm(pcb, pg, gbuf);
+ if (!pg) return (struct thdr *)0;
+ if (GET(pgsv->ttype, TOPT)) ++optcnt; /* Indicate nested opt grp. */
+ --grplvl;
+ continue;
+
+ case OREP: /* OREP occurrence indicator for current token.*/
+ SET(pgsv->ttype, TREP|TXREP);
+ /* Now treat like OPT. */
+ case OPT: /* OPT occurrence indicator for current token. */
+ SET(pgsv->ttype, TXOPT);
+ if (GET(pgsv->ttype, TOPT)) continue; /* Exit if nested opt grp. */
+ SET(pgsv->ttype, TOPT);
+ ++optcnt; /* Count opt tokens to see if grp is optional. */
+ continue;
+ case REP: /* REP occurrence indicator for current token. */
+ SET(pgsv->ttype, TREP|TXREP);
+ continue;
+
+ case OR: /* OR connector found. */
+ if BITOFF(pgh->ttype, TTAND) SET(pgh->ttype, TTOR);
+ else if (GET(pgh->ttype, TTAND)!=TTOR)
+ mderr(55, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ continue;
+ case AND: /* AND connector found. */
+ if BITOFF(pgh->ttype, TTAND) SET(pgh->ttype, TTAND);
+ else if (GET(pgh->ttype, TTAND)!=TTAND)
+ mderr(55, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ continue;
+ case SEQ: /* SEQ connector found. */
+ if BITOFF(pgh->ttype, TTAND) SET(pgh->ttype, TTSEQ);
+ else if (GET(pgh->ttype, TTAND)!=TTSEQ)
+ mderr(55, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ continue;
+
+ case EE_: /* Entity ended (correctly or incorrectly). */
+ if (es<essv) {synerr(37, pcb); essv = es;}
+ continue;
+
+ case PIE_: /* PI entity reference (not permitted). */
+ entpisw = 0; /* Reset PI entity indicator. */
+ synerr(59, pcb);
+ continue;
+
+ default: /* Syntax errors return in disgrace. */
+ synerr(37, pcb);
+ return (struct thdr *)0;
+ }
+ if (pgh->tu.tnum>GRPCNT) {
+ mderr(113, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ return (struct thdr *)0;
+ }
+ if (gbuf->tu.tnum>GRPGTCNT) {
+ mderr(114, ntoa(gbuf->tu.tnum), (UNCH *)0);
+ return (struct thdr *)0;
+ }
+ if (pgh->tu.tnum==1) SET(pgh->ttype, TTSEQ); /* Unit grp is SEQ. */
+ /* An optional token in an OR group makes the group optional. */
+ if (GET(pgh->ttype, TTMASK)==TTOR && optcnt) SET(pgh->ttype, TOPT);
+ /* If all tokens in any group are optional, so is the group. */
+ if (pgh->tu.tnum<=optcnt) SET(pgh->ttype, TOPT);
+
+ if (es!=essv) synerr(37, pcb);
+ return pg; /* Return pointer to GRPS token. */
+}
+/* PARSENM: Parser for SGML names, which can be translated with LEXTRAN.
+ The input is read from the entity stack. CC is 1st char of name.
+ Returns a pointer to the parsed name.
+*/
+UNCH *parsenm(tbuf, nc)
+UNCH *tbuf; /* Buffer for name: >=NAMELEN+2. */
+int nc; /* Namecase translation: 1=yes; 0=no. */
+{
+ UNCH len; /* Length of name (incl EOS & length byte). */
+
+ *(tbuf + (len = 1) ) = nc ? lextran[*FPOS] : *FPOS;
+ while ((NEWCC, (int)lextoke[*FPOS]>=NMC) && (len<NAMELEN)) {
+ TRACETKN(NMC, lextoke);
+ if (lextoke[*(tbuf + ++len) = (nc ? lextran[*FPOS] : *FPOS)]==EOB) {
+ --len;
+ entget();
+ }
+ }
+ REPEATCC; /* Put back the non-token character. */
+ *(tbuf + ++len) = EOS; /* Terminate name with standard EOS. */
+ *tbuf = ++len; /* Store length ahead of name. */
+ return tbuf;
+}
+/* PARSETKN: Parser for start-tag attribute value tokens.
+ First character of token is already in *FPOS.
+ Returns a pointer to the parsed token.
+ Parsed token has EOS but no length byte.
+*/
+#ifdef USE_PROTOTYPES
+UNCH *parsetkn(UNCH *tbuf, UNCH scope, int maxlen)
+#else
+UNCH *parsetkn(tbuf, scope, maxlen)
+UNCH *tbuf; /* Buffer for token: >=maxlen+1. */
+UNCH scope; /* Minimum lexical class allowed. */
+int maxlen; /* Maximum length of a token. */
+#endif
+{
+ int i = 1;
+ tbuf[0] = *FPOS;
+ while (i < maxlen) {
+ NEWCC;
+ if (lextoke[*FPOS] < scope) {
+ REPEATCC;
+ break;
+ }
+ TRACETKN(scope, lextoke);
+ if (*FPOS == EOBCHAR)
+ entget();
+ else
+ tbuf[i++] = *FPOS;
+ }
+ tbuf[i] = EOS;
+ return tbuf;
+}
+/* PARSESEQ: Parser for blank sequences (i.e., space and TAB characters ).
+ First character of sequence is already in *FPOS.
+*/
+VOID parseseq(tbuf, maxlen)
+UNCH *tbuf; /* Buffer for storing found sequence. */
+int maxlen; /* Maximum length of a blank sequence. */
+{
+ tbuf[0] = *FPOS;
+ datalen = 1;
+ for (;;) {
+ NEWCC;
+ if (*FPOS == EOBCHAR) {
+ entget();
+ continue;
+ }
+ if ((lextoke[*FPOS] != SEP && *FPOS != SPCCHAR)
+ || datalen >= maxlen)
+ break;
+ tbuf[datalen++] = *FPOS;
+ TRACETKN(SEP, lextoke);
+ }
+}
+/* S2VALNM: Parser for attribute values that are tokenized like names.
+ The input is read from a string (hence S ("string") 2 ("to") VALNM).
+ It stops at the first bad character.
+ Returns a pointer to the created name.
+*/
+#ifdef USE_PROTOTYPES
+UNCH *s2valnm(UNCH *nm, UNCH *s, UNCH scope, int translate)
+#else
+UNCH *s2valnm(nm, s, scope, translate)
+UNCH *nm; /* Name to be created. */
+UNCH *s; /* Source string to be parsed as name. */
+UNCH scope; /* Minimum lexical class allowed. */
+int translate; /* Namecase translation: 1=yes; 0=no. */
+#endif
+{
+ UNCH len = 0; /* Length of name (incl EOS and length). */
+
+ for (; (int)lextoke[*s] >= scope && len < NAMELEN; s++)
+ nm[++len] = translate ? lextran[*s] : *s;
+ nm[++len] = EOS; /* Terminate name with standard EOS. */
+ *nm = ++len; /* Store length ahead of name. */
+ return nm;
+}
+/* PARSEVAL: Parser for attribute values.
+ The input is read from a string and tokenized in a buffer.
+ The input is terminated by EOS.
+ Each token is preceded by its actual length; there is no EOS.
+ If an error occurs while parsing, or
+ if a token doesn't conform, set the token count to 0 to show that
+ value was not tokenized and return the error code.
+ After successful parse, return buffer length and 0 error code.
+ The number of tokens found is set in external variable tokencnt.
+*/
+int parseval(s, atype, tbuf)
+UNCH *s; /* Source string to be parsed as token list. */
+UNS atype; /* Type of token list expected. */
+UNCH *tbuf; /* Work area for tokenization. */
+{
+ int t;
+ UNCH *pt = tbuf;
+
+ pcbval.newstate = 0; tokencnt = 0;
+ while (1) {
+ for (;;) {
+ pcbval.input = lextoke[*s];
+ pcbval.state = pcbval.newstate;
+ pcbval.newstate = (*(pcbval.ptab + pcbval.state)) [pcbval.input];
+ pcbval.action = (*(pcbval.ptab + pcbval.state+1)) [pcbval.input];
+ TRACEVAL(&pcbval, atype, s, tokencnt);
+ if (pcbval.action != NOPA)
+ break;
+ s++;
+ }
+
+
+ switch (pcbval.action) {
+ case INVA: /* Invalid character; terminate parse. */
+ if (*s == '\0') goto alldone; /* Normal termination. */
+ tokencnt = 0; /* Value was not tokenized. */
+ return(14);
+ case LENA: /* Length limit of token exceeded; end parse. */
+ tokencnt = 0; /* Value was not tokenized. */
+ return(15);
+ default: /* Token begun: NUMA, NASA, or NMTA. */
+ break;
+ }
+
+ ++tokencnt; /* One token per iteration. */
+ switch (atype) {
+ case AENTITY:
+ if (tokencnt>1) {tokencnt = 0; return(16);}
+ case AENTITYS:
+ if (pcbval.action!=NASA) {tokencnt = 0; return(17);}
+ s2valnm(pt, s, NMC, ENTCASE);
+ break;
+
+ case AID:
+ case AIDREF:
+ case ANAME:
+ case ANOTEGRP:
+ if (tokencnt>1) {tokencnt = 0; return(16);}
+ case AIDREFS:
+ case ANAMES:
+ if (pcbval.action!=NASA) {tokencnt = 0; return(17);}
+ s2valnm(pt, s, NMC, NAMECASE);
+ break;
+
+ case ANMTGRP:
+ case ANMTOKE:
+ if (tokencnt>1) {tokencnt = 0; return(16);}
+ case ANMTOKES:
+ /* No test needed because NMTA, NUMA and NASA are all valid. */
+ s2valnm(pt, s, NMC, NAMECASE);
+ break;
+
+ case ANUMBER:
+ if (tokencnt>1) {tokencnt = 0; return(16);}
+ case ANUMBERS:
+ if (pcbval.action!=NUMA) {tokencnt = 0; return(17);}
+ s2valnm(pt, s, NU, NAMECASE);
+ t = lextoke[s[*pt - 2]];
+ if (t == NMS || t == NMC) {tokencnt = 0; return(17);}
+ break;
+
+ case ANUTOKE:
+ if (tokencnt>1) {tokencnt = 0; return(16);}
+ case ANUTOKES:
+ if (pcbval.action!=NUMA) {tokencnt = 0; return(17);}
+ s2valnm(pt, s, NMC, NAMECASE);
+ break;
+ }
+ *pt -= 2;
+ s += *pt;
+ pt += *pt + 1;
+ }
+ alldone:
+ *pt++ = EOS;
+ if (*tbuf == '\0')
+ return 25;
+ if (atype < ATKNLIST)
+ *tbuf += 2; /* include length and EOS */
+ return 0;
+}
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/pcbrf.c b/usr.bin/sgmls/sgmls/pcbrf.c
new file mode 100644
index 0000000..a18617e
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/pcbrf.c
@@ -0,0 +1,1344 @@
+/* PCBRF: Parse tables for reference concrete syntax.
+*/
+#include "config.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "action.h" /* Action names for all parsing. */
+#include "synxtrn.h" /* Declarations for concrete syntax constants. */
+#include "adl.h" /* Definitions for attribute list processing. */
+/* PCBCONM: State and action table for content parse of mixed content.
+ Initial state assumes a start-tag was just processed.
+*/
+/* Symbols for state names (end with a number). */
+#define ET0 0 /* Markup found or buffer flushed; no data. */
+#define DA0 2 /* Data in buffer. */
+#define DA1 4 /* Data and space in buffer. */
+#define ER0 6 /* ERO found; start lookahead buffer. */
+#define CR0 8 /* CRO found (ERO, RNI). */
+#define RS0 10 /* RS found; possible SR 3-6. */
+#define ME0 12 /* MSC found; possible SR26. */
+#define ME1 14 /* MSC, MSC found. */
+#define ES0 16 /* TAGO found; start lookahead buffer. */
+#define EE0 18 /* End-tag start (TAGO,ETI); move to lookahead buffer. */
+#define NE0 20 /* End-tag start (TAGO,NET); process NET if not end-tag. */
+#define MD0 22 /* MDO found (TAGO, MDO[2]). */
+#define MC0 24 /* MDO, COM found. */
+#define SC0 26 /* COM found; possible SR19-20. */
+#define SP0 28 /* Space found; data pending; possible SR7 or SR9. */
+#define SR0 30 /* SPCR found; possible SR7 or SR9. */
+#define TB0 32 /* TAB found; possible SR7 or SR9. */
+
+int pcbcnet = ET0; /* PCBCONM: markup found or data buffer flushed.*/
+int pcbcnda = DA0; /* PCBCONM: data in buffer. */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+et0 []={DA0 ,DA0 ,DA0 ,DA0 ,SP0 ,ET0 ,ET0 ,ET0 ,RS0 ,ET0 ,TB0 ,DA0 ,ET0 ,ER0 ,
+ ET0 ,SC0 ,DA0 ,ET0 ,ET0 ,SR0 ,DA0 ,ME0 ,ET0 ,DA0 ,ET0 ,DA0 ,ES0 ,ET0 },/*et0*/
+et0a[]={DAS_,DAS_,DAS_,DAS_,DAS_,NON_,GET_,GET_,RSR_,SR2_,DAS_,DAS_,NSC_,LAS_,
+ REF_,NOP_,DAS_,NED_,SR10,DAS_,DAS_,NOP_,SR25,DAS_,SR11,DAS_,LAS_,FCE_},
+
+da0 []={DA0 ,DA0 ,DA0 ,DA0 ,DA1 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 },/*da0*/
+da0a[]={NOP_,NOP_,NOP_,NOP_,NOP_,DAF_,DAF_,DAF_,DAF_,DAF_,DAF_,NOP_,DAF_,DAF_,
+ DAF_,DAF_,NOP_,DAF_,DAF_,DAF_,NOP_,DAF_,DAF_,NOP_,NOP_,NOP_,DAF_,DAF_},
+
+da1 []={DA0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 },/*da1*/
+da1a[]={NOP_,NOP_,NOP_,NOP_,DAR_,DAF_,DAF_,DAR_,DAF_,DAR_,DAR_,NOP_,DAF_,DAF_,
+ DAF_,DAF_,NOP_,DAF_,DAF_,DAR_,NOP_,DAF_,DAF_,NOP_,NOP_,NOP_,DAF_,DAF_},
+
+er0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ER0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,CR0 ,ET0 ,ET0 ,ET0 },/*er0*/
+er0a[]={LAF_,LAF_,LAF_,ER_ ,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAM_,LAF_,LAF_,LAF_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+cr0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,CR0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*cr0*/
+cr0a[]={NLF_,CRN_,NLF_,CRA_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_},
+
+rs0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,RS0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*rs0*/
+rs0a[]={SR3_,SR3_,SR3_,SR3_,SR4_,SR3_,SR3_,GET_,SR3_,SR5_,SR4_,SR3_,SR3_,SR3_,
+ SR3_,SR3_,SR3_,NED_,SR3_,SR4_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_},
+
+me0 []={ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ME0, ET0 ,ET0 ,ET0 ,ET0, ET0, ET0,
+ ET0 ,ET0 ,ET0 ,ET0, ET0, ET0, ET0, ME1 ,ET0, ET0, ET0 ,ET0, ET0, ET0 },/*me0*/
+me0a[]={SR26,SR26,SR26,SR26,SR26,SR26,SR26,GET_,SR26,SR26,SR26,SR26,SR26,SR26,
+ SR26,SR26,SR26,SR26,SR26,SR26,SR26,NOP_,SR26,SR26,SR26,SR26,SR26,SR26},
+
+me1 []={ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ME1, ET0 ,ET0 ,ET0 ,ET0, ET0, ET0,
+ ET0 ,ET0 ,ET0 ,ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ET0 ,ET0, ET0, ET0 },/*me1*/
+me1a[]={RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,GET_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,
+ RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,MSE_,RBR_,RBR_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+es0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ES0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,EE0 ,NE0 ,ET0 ,ET0 ,MD0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*es0*/
+es0a[]={LAF_,LAF_,LAF_,STG_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAM_,LAM_,LAF_,LAF_,LAM_,LAF_,LAF_,PIS_,LAF_,NST_,LAF_,LAF_},
+
+ee0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,EE0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*ee0*/
+ee0a[]={LAF_,LAF_,LAF_,ETG_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,NET_,LAF_,LAF_},
+
+ne0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,NE0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*ne0*/
+ne0a[]={NLF_,NLF_,NLF_,ETG_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NET_,NLF_,NLF_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+md0 []={ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, MD0, ET0 ,ET0 ,ET0 ,ET0, ET0, ET0,
+ ET0 ,MC0 ,ET0 ,ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ET0 ,ET0, ET0, ET0 },/*md0*/
+md0a[]={LAF_,LAF_,LAF_,MD_ ,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,MSS_,LAF_,LAF_,MDC_,LAF_,LAF_},
+
+mc0 []={ET0, ET0, ET0, ET0, ET0, ET0 ,ET0, MC0, ET0 ,ET0, ET0 ,ET0, ET0, ET0,
+ ET0 ,ET0 ,ET0 ,ET0, ET0, ET0, ET0, ET0 ,ET0 ,ET0 ,ET0 ,ET0, ET0, ET0 },/*mc0*/
+mc0a[]={NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,MDC_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_},
+
+sc0 []={ET0, ET0, ET0, ET0, ET0, ET0 ,ET0, SC0, ET0 ,ET0, ET0 ,ET0, ET0, ET0,
+ ET0 ,ET0 ,ET0 ,ET0, ET0, ET0, ET0, ET0 ,ET0 ,ET0 ,ET0 ,ET0, ET0, ET0 },/*sc0*/
+sc0a[]={SR19,SR19,SR19,SR19,SR19,SR19,SR19,GET_,SR19,SR19,SR19,SR19,SR19,SR19,
+ SR19,SR20,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+sp0 []={DA0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 ,ET0 ,SP0 ,ET0 ,ET0 ,ET0 ,DA0 ,DA0 ,ET0 ,
+ ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,ET0 ,DA0 ,ET0 ,ET0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 },/*sp0*/
+sp0a[]={NOP_,NOP_,NOP_,NOP_,SR9_,DAF_,DAF_,GTR_,DAF_,SR7_,SR9_,NOP_,NOP_,DAF_,
+ DAF_,DAF_,NOP_,DAF_,DAF_,SR9_,NOP_,DAF_,DAF_,NOP_,NOP_,NOP_,DAF_,DAF_},
+
+sr0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,SR0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*sr0*/
+sr0a[]={SR8_,SR8_,SR8_,SR8_,SR9_,SR8_,SR8_,GET_,SR8_,SR7_,SR9_,SR8_,SR8_,SR8_,
+ SR8_,SR8_,SR8_,SR8_,SR8_,SR9_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_},
+
+tb0 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,TB0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*tb0*/
+tb0a[]={SR1_,SR1_,SR1_,SR1_,SR9_,SR1_,SR1_,GET_,SR1_,SR7_,SR9_,SR1_,SR1_,SR1_,
+ SR1_,SR1_,SR1_,SR1_,SR1_,SR9_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+
+*conmtab[] = {et0, et0a, da0, da0a, da1, da1a, er0, er0a, cr0, cr0a, rs0, rs0a,
+ me0, me0a, me1, me1a, es0, es0a, ee0, ee0a, ne0, ne0a, md0, md0a,
+ mc0, mc0a, sc0, sc0a, sp0, sp0a, sr0, sr0a, tb0, tb0a };
+struct parse pcbconm = {"CONM", lexcnm, conmtab, 0, 0, 0, 0};
+#undef ET0
+#undef DA0
+#undef DA1
+#undef ER0
+#undef CR0
+#undef RS0
+#undef ME0
+#undef ME1
+#undef ES0
+#undef EE0
+#undef NE0
+#undef MD0
+#undef MC0
+#undef SC0
+#undef SP0
+#undef SR0
+#undef TB0
+/* PCBCONE: State and action table for content parse of element content.
+ Initial state assumes a start-tag was just processed.
+*/
+/* Symbols for state names (end with a number). */
+#define ET2 0 /* Markup found. */
+#define ER2 2 /* ERO found; start lookahead buffer. */
+#define CR2 4 /* CRO found (ERO, RNI). */
+#define RS2 6 /* RS found; possible SR 3-6 if they were declared. */
+#define ME2 8 /* MSC found. */
+#define ME3 10 /* MSC, MSC found. */
+#define ES2 12 /* TAGO found; start lookahead buffer. */
+#define EE2 14 /* End-tag start (TAGO,ETI); move to lookahead buffer. */
+#define NE2 16 /* End-tag start (TAGO,NET); process NET if not end-tag. */
+#define MD2 18 /* MDO found (TAGO, MDO[2]). */
+#define MC2 20 /* MDO, COM found. */
+#define SC2 22 /* COM found; possible SR19-20 if they were mapped. */
+#define SP2 24 /* Space found; possible SR7 or SR9. */
+#define SR2 26 /* SPCR found; possible SR7 or SR9. */
+#define TB2 28 /* TAB found; possible SR7 or SR9. */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+et2 []={ET2 ,ET2 ,ET2 ,ET2 ,SP2 ,ET2 ,ET2 ,ET2 ,RS2 ,ET2 ,TB2 ,ET2 ,ET2 ,ER2 ,
+ ET2 ,SC2 ,ET2 ,ET2 ,ET2 ,SR2 ,ET2 ,ME2 ,ET2 ,ET2 ,ET2 ,ET2 ,ES2 ,ET2 },/*et2*/
+et2a[]={DCE_,DCE_,DCE_,DCE_,NOP_,DCE_,GET_,GET_,RS_ ,SR2_,NOP_,DCE_,DCE_,LAS_,
+ NOP_,NOP_,DCE_,NED_,SR10,NOP_,DCE_,NOP_,DCE_,DCE_,SR11,DCE_,LAS_,DCE_},
+
+er2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ER2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,CR2 ,ET2 ,ET2 ,ET2 },/*er2*/
+er2a[]={LAF_,LAF_,LAF_,ER_ ,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAM_,LAF_,LAF_,LAF_},
+
+cr2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,CR2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*cr2*/
+cr2a[]={NLF_,CRN_,NLF_,CRA_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_},
+
+rs2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,RS2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*rs2*/
+rs2a[]={SR3_,SR3_,SR3_,SR3_,SR4_,SR3_,SR3_,GET_,SR3_,SR5_,SR4_,SR3_,SR3_,SR3_,
+ SR3_,SR3_,SR3_,NED_,SR3_,SR4_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_,SR3_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spcr mdo msc mso pio rni tagc tago fce */
+me2 []={ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ME2, ET2 ,ET2 ,ET2 ,ET2, ET2, ET2,
+ ET2 ,ET2, ET2 ,ET2, ET2, ET2, ET2, ME3 ,ET2, ET2, ET2 ,ET2, ET2, ET2 },/*me2*/
+me2a[]={SR26,SR26,SR26,SR26,SR26,SR26,SR26,GET_,SR26,SR26,SR26,SR26,SR26,SR26,
+ SR26,SR26,SR26,SR26,SR26,SR26,SR26,NOP_,SR26,SR26,SR26,SR26,SR26,SR26},
+
+me3 []={ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ME3, ET2 ,ET2 ,ET2 ,ET2, ET2, ET2,
+ ET2 ,ET2, ET2 ,ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ET2 ,ET2, ET2, ET2 },/*me3*/
+me3a[]={RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,GET_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,
+ RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,RBR_,MSE_,RBR_,RBR_},
+
+es2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ES2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,EE2 ,NE2 ,ET2 ,ET2 ,MD2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*es2*/
+es2a[]={LAF_,LAF_,LAF_,STG_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAM_,LAM_,LAF_,LAF_,LAM_,LAF_,LAF_,PIS_,LAF_,NST_,LAF_,LAF_},
+
+ee2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,EE2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*ee2*/
+ee2a[]={LAF_,LAF_,LAF_,ETG_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,NET_,LAF_,LAF_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spc mdo msc mso pio rni tagc tago fce */
+ne2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,NE2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*ne2*/
+ne2a[]={NLF_,NLF_,NLF_,ETG_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NET_,NLF_,NLF_},
+
+md2 []={ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, MD2, ET2 ,ET2 ,ET2 ,ET2, ET2, ET2,
+ ET2 ,MC2, ET2 ,ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ET2 ,ET2, ET2, ET2 },/*md2*/
+md2a[]={LAF_,LAF_,LAF_,MD_ ,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,MSS_,LAF_,LAF_,MDC_,LAF_,LAF_},
+
+mc2 []={ET2, ET2, ET2, ET2, ET2, ET2 ,ET2, MC2, ET2 ,ET2, ET2 ,ET2, ET2, ET2,
+ ET2 ,ET2, ET2 ,ET2, ET2, ET2, ET2, ET2 ,ET2 ,ET2 ,ET2 ,ET2, ET2, ET2 },/*mc2*/
+mc2a[]={NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,GET_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,
+ NLF_,MDC_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_,NLF_},
+
+sc2 []={ET2, ET2, ET2, ET2, ET2, ET2 ,ET2, SC2, ET2 ,ET2, ET2 ,ET2, ET2, ET2,
+ ET2 ,ET2 ,ET2 ,ET2, ET2, ET2, ET2, ET2 ,ET2 ,ET2 ,ET2 ,ET2, ET2, ET2 },/*sc2*/
+sc2a[]={SR19,SR19,SR19,SR19,SR19,SR19,SR19,GET_,SR19,SR19,SR19,SR19,SR19,SR19,
+ SR19,SR20,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19,SR19},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net lit spc mdo msc mso pio rni tagc tago fce */
+sp2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,SP2 ,RS2 ,ET2 ,ET2 ,ET2 ,ET2 ,ER2 ,
+ ET2 ,SC2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ME2 ,ET2 ,ET2 ,ET2 ,ET2 ,ES2 ,ET2 },/*sp2*/
+sp2a[]={DCE_,DCE_,DCE_,DCE_,SR9_,DCE_,GET_,GET_,RS_ ,SR7_,SR9_,DCE_,DCE_,LAS_,
+ NOP_,NOP_,DCE_,NED_,SR10,SR9_,DCE_,LAS_,DCE_,DCE_,SR11,DCE_,LAS_,DCE_},
+
+sr2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,SR2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*sr2*/
+sr2a[]={SR8_,SR8_,SR8_,SR8_,SR9_,SR8_,SR8_,GET_,SR8_,SR7_,SR9_,SR8_,SR8_,SR8_,
+ SR8_,SR8_,SR8_,SR8_,SR8_,SR9_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_,SR8_},
+
+tb2 []={ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,TB2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,
+ ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 },/*tb2*/
+tb2a[]={SR1_,SR1_,SR1_,SR1_,SR9_,SR1_,SR1_,GET_,SR1_,SR7_,SR9_,SR1_,SR1_,SR1_,
+ SR1_,SR1_,SR1_,SR1_,SR1_,SR9_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_,SR1_},
+
+*conetab[] = {et2, et2a, er2, er2a, cr2, cr2a, rs2, rs2a, me2, me2a, me3, me3a,
+ es2, es2a, ee2, ee2a, ne2, ne2a, md2, md2a, mc2, mc2a, sc2, sc2a,
+ sp2, sp2a, sr2, sr2a, tb2, tb2a };
+struct parse pcbcone = {"CONE", lexcnm, conetab, 0, 0, 0, 0};
+#undef ET2
+#undef ER2
+#undef CR2
+#undef RS2
+#undef ME2
+#undef ME3
+#undef ES2
+#undef EE2
+#undef NE2
+#undef MD2
+#undef MC2
+#undef SC2
+#undef SP2
+#undef SR2
+#undef TB2
+/* PCBCONR: State and action table for content parse of replaceable character
+ data. Initial state assumes a start-tag was just processed.
+ Only entity references and character references are recognized.
+*/
+/* Symbols for state names (end with a number). */
+#define ET4 0 /* Markup found or buffer flushed; no data. */
+#define DA4 2 /* Data in buffer. */
+#define ER4 4 /* ERO found; start lookahead buffer. */
+#define CR4 6 /* CRO found (ER2, RNI). */
+#define ES4 8 /* TAGO found; start lookahead buffer. */
+#define EE4 10 /* End-tag start (TAGO,ETI); move to lookahead buffer. */
+#define NE4 12 /* End-tag start (TAGO,NET); process NET if not end-tag. */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+et4 []={DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,DA4 ,DA4 ,ET4 ,ER4 ,
+ ET4 ,DA4 ,DA4 ,ET4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,ES4 },/*et4*/
+et4a[]={DAS_,DAS_,DAS_,DAS_,DAS_,NON_,EE_ ,GET_,RS_ ,REF_,DAS_,DAS_,NSC_,LAS_,
+ REF_,DAS_,DAS_,NED_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_,LAS_},
+
+da4 []={DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,DA4 ,DA4 ,ET4 ,ET4 ,
+ ET4 ,DA4 ,DA4 ,ET4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,DA4 ,ET4 },/*da4*/
+da4a[]={NOP_,NOP_,NOP_,NOP_,NOP_,DAF_,DAF_,DAF_,DAF_,DAF_,NOP_,NOP_,DAF_,DAF_,
+ DAF_,NOP_,NOP_,DAF_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,DAF_},
+
+er4 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ER4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,
+ ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,CR4 ,ET4 ,ET4 },/*er4*/
+er4a[]={LAF_,LAF_,LAF_,ERX_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAM_,LAF_,LAF_},
+
+cr4 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,CR4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,
+ ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 },/*cr4*/
+cr4a[]={LAF_,CRN_,LAF_,CRA_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+es4 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ES4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,
+ ET4 ,ET4 ,EE4 ,NE4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 },/*es4*/
+es4a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAM_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+ee4 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,EE4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,
+ ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 },/*ee4*/
+ee4a[]={LAF_,LAF_,LAF_,ETC_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,NET_,LAF_},
+
+ne4 []={EE4 ,EE4 ,EE4 ,ET4 ,EE4 ,EE4 ,EE4 ,NE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,
+ EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,EE4 ,ET4 ,EE4 },/*ne4*/
+ne4a[]={RC2_,RC2_,RC2_,ETC_,RC2_,RC2_,RC2_,GET_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,
+ RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,NET_,RC2_},
+
+*conrtab[] = {et4, et4a, da4, da4a, er4, er4a, cr4, cr4a,
+ es4, es4a, ee4, ee4a, ne4, ne4a};
+struct parse pcbconr = {"CONR", lexcon, conrtab, 0, 0, 0, 0};
+#undef ET4
+#undef DA4
+#undef ER4
+#undef CR4
+#undef ES4
+#undef EE4
+#undef NE4
+/* PCBCONC: State and action table for content parse of character data.
+ Initial state assumes a start-tag was just processed.
+*/
+/* Symbols for state names (end with a number). */
+#define ET6 0 /* Markup found or buffer flushed; no data. */
+#define DA6 2 /* Data in buffer. */
+#define ES6 4 /* TAGO found; start lookahead buffer. */
+#define EE6 6 /* End-tag start (TAGO,ETI); move to lookahead buffer. */
+#define NE6 8 /* End-tag start (TAGO,NET); process NET if not end-tag. */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+et6 []={DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,DA6 ,DA6 ,ET6 ,DA6 ,
+ ET6 ,DA6 ,DA6 ,ET6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,ES6 },/*et6*/
+et6a[]={DAS_,DAS_,DAS_,DAS_,DAS_,NON_,EOF_,GET_,RS_ ,REF_,DAS_,DAS_,NSC_,DAS_,
+ REF_,DAS_,DAS_,NED_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_,LAS_},
+
+da6 []={DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,DA6 ,DA6 ,ET6 ,ET6 ,
+ ET6 ,DA6 ,DA6 ,ET6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,DA6 ,ET6 },/*da6*/
+da6a[]={NOP_,NOP_,NOP_,NOP_,NOP_,DAF_,DAF_,DAF_,DAF_,DAF_,NOP_,NOP_,DAF_,DAF_,
+ DAF_,NOP_,NOP_,DAF_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,DAF_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+es6 []={ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ES6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,
+ ET6 ,ET6 ,EE6 ,NE6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 },/*es6*/
+es6a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAM_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+ee6 []={ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,EE6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,
+ ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 ,ET6 },/*ee6*/
+ee6a[]={LAF_,LAF_,LAF_,ETC_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,NET_,LAF_},
+
+ne6 []={EE6 ,EE6 ,EE6 ,ET6 ,EE6 ,EE6 ,EE6 ,NE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,
+ EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,EE6 ,ET6 ,EE6 },/*ne6*/
+ne6a[]={RC2_,RC2_,RC2_,ETC_,RC2_,RC2_,RC2_,GET_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,
+ RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,RC2_,NET_,RC2_},
+
+*conctab[] = {et6, et6a, da6, da6a, es6, es6a, ee6, ee6a, ne6, ne6a};
+struct parse pcbconc = {"CONC", lexcon, conctab, 0, 0, 0, 0};
+#undef ET6
+#undef DA6
+#undef ES6
+#undef EE6
+#undef NE6
+/* PCBPRO: State and action table for prolog parse.
+ Initial state assumes document just began.
+*/
+/* Symbols for state names (end with a number). */
+#define ET7 0 /* Markup found. */
+#define ES7 2 /* TAGO found; start lookahead buffer. */
+#define MD7 4 /* MDO found (TAGO, MDO[2]). */
+#define MC7 6 /* MDO, COM found. */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+et7 []={ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,
+ ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ES7 },/*et7*/
+et7a[]={DCE_,DCE_,DCE_,DCE_,NOP_,DCE_,EE_ ,GET_,RS_ ,NOP_,NOP_,DCE_,DCE_,DCE_,
+ DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,DCE_,LAS_},
+
+es7 []={ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ES7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,
+ ET7 ,ET7 ,ET7 ,ET7 ,MD7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 ,ET7 },/*es7*/
+es7a[]={PEP_,PEP_,PEP_,STE_,PEP_,PEP_,PEP_,GET_,PEP_,PEP_,PEP_,PEP_,PEP_,PEP_,
+ PEP_,PEP_,PEP_,PEP_,LAM_,PEP_,PEP_,PEP_,PIS_,PEP_,STE_,PEP_},
+
+md7 []={ET7, ET7, ET7, ET7, ET7 ,ET7, ET7, MD7, ET7 ,ET7 ,ET7 ,ET7, ET7, ET7,
+ ET7, MC7, ET7, ET7, ET7, ET7 ,ET7, ET7, ET7, ET7 ,ET7, ET7 },/*md7*/
+md7a[]={LAF_,LAF_,LAF_,DTD_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAM_,LAF_,LAF_,LAF_,LAF_,MSP_,LAF_,LAF_,LAF_,NOP_,LAF_},
+
+mc7 []={ET7, ET7, ET7, ET7, ET7, ET7 ,ET7, MC7, ET7 ,ET7, ET7 ,ET7, ET7, ET7,
+ ET7, ET7, ET7, ET7, ET7, ET7 ,ET7 ,ET7, ET7 ,ET7 ,ET7, ET7 },/*mc7*/
+mc7a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,MDC_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+*protab[] = {et7, et7a, es7, es7a, md7, md7a, mc7, mc7a};
+struct parse pcbpro = {"PRO", lexcon, protab, 0, 0, 0, 0};
+#undef ET7
+#undef ES7
+#undef MD7
+#undef MC7
+/* PCBMDS: State and action table for parse of markup declaration subset.
+ Initial state assumes subset just began (MSO found).
+*/
+/* Symbols for state names (end with a number). */
+#define ET8 0 /* Markup found. */
+#define ER8 2 /* PERO found; start lookahead buffer. */
+#define ME8 4 /* MSC found. */
+#define ME9 6 /* MSC, MSC found. */
+#define ES8 8 /* TAGO found; start lookahead buffer. */
+#define MD8 10 /* MDO found (TAGO, MDO[2]). */
+#define MC8 12 /* MDO, CD found. */
+#define DC8 14 /* Data characters found (erroneously). */
+
+static UNCH
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+et8 []={DC8 ,DC8 ,DC8 ,DC8 ,ET8 ,DC8 ,ET8 ,ET8 ,ET8 ,ET8 ,ET8 ,DC8 ,DC8 ,DC8 ,
+ DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,ME8 ,DC8 ,ER8 ,DC8 ,DC8 ,DC8 ,ES8 },/*et8*/
+et8a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,GET_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+er8 []={DC8 ,DC8 ,DC8 ,ET8 ,DC8 ,DC8 ,DC8 ,ER8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,
+ DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 },/*er8*/
+er8a[]={NOP_,NOP_,NOP_,PER_,NOP_,SYS_,NOP_,GET_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+me8 []={ET8, ET8, ET8, ET8, ET8 ,ET8, ET8, ME8, ET8 ,ET8 ,ET8 ,ET8, ET8, ET8,
+ ET8 ,ET8, ET8 ,ET8, ET8, ME9 ,ET8, ET8, ET8, ET8 ,ET8, ET8 },/*me8*/
+me8a[]={DTE_,DTE_,DTE_,DTE_,DTE_,DTE_,DTE_,GET_,DTE_,DTE_,DTE_,DTE_,DTE_,DTE_,
+ DTE_,DTE_,DTE_,DTE_,DTE_,NOP_,DTE_,DTE_,DTE_,DTE_,DTE_,DTE_},
+
+me9 []={DC8, DC8, DC8, DC8, DC8 ,DC8, DC8, ME9, DC8 ,DC8 ,DC8 ,DC8, DC8, DC8,
+ DC8 ,DC8, DC8 ,DC8, DC8, DC8 ,DC8, DC8, DC8, DC8 ,ET8, DC8 },/*me9*/
+me9a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,GET_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,MSE_,NOP_},
+
+/* free nu nmc nms spc non ee eob rs re sep cde nsc ero
+ nmre com eti net mdo msc mso pero pio rni tagc tago */
+es8 []={DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,ES8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,
+ DC8 ,DC8 ,DC8 ,DC8 ,MD8 ,DC8 ,DC8 ,DC8 ,ET8 ,DC8 ,DC8 ,DC8 },/*es8*/
+es8a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,GET_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,PIS_,NOP_,NOP_,NOP_},
+
+md8 []={DC8, DC8, DC8, ET8, DC8 ,DC8, DC8, MD8, DC8 ,DC8 ,DC8 ,DC8, DC8, DC8,
+ DC8 ,MC8, DC8 ,DC8, DC8, DC8 ,ET8, DC8, DC8, DC8 ,ET8, DC8 },/*md8*/
+md8a[]={NOP_,NOP_,NOP_,MD_ ,NOP_,SYS_,NOP_,GET_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,MSS_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+mc8 []={DC8, DC8, DC8, DC8, DC8, DC8 ,DC8, MC8, DC8 ,DC8, DC8 ,DC8, DC8, DC8,
+ DC8 ,ET8, DC8 ,DC8, DC8, DC8 ,DC8 ,DC8, DC8 ,DC8 ,DC8, DC8 },/*mc8*/
+mc8a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,GET_,NOP_,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,MDC_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+dc8 []={DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,
+ DC8 ,DC8 ,DC8 ,DC8 ,DC8 ,ET8 ,DC8 ,ET8 ,DC8 ,DC8 ,DC8 ,ET8 },/*dc8*/
+dc8a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,GET_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,CIR_,NOP_,CIR_,NOP_,NOP_,NOP_,CIR_},
+
+*mdstab[] = {et8, et8a, er8, er8a, me8, me8a, me9, me9a,
+ es8, es8a, md8, md8a, mc8, mc8a, dc8, dc8a};
+struct parse pcbmds = {"MDS", lexcon, mdstab, 0, 0, 0, 0};
+#undef ET8
+#undef ER8
+#undef ME8
+#undef ME9
+#undef ES8
+#undef MD8
+#undef MC8
+#undef DC8
+/* PCBGRCM: State and action table for content model group.
+ Groups can nest. Reserved names are allowed.
+ Data tag token groups are allowed.
+ A non-reserved name or model group can have a suffix.
+ Columns are based on LEXGRP.C.
+*/
+/* Symbols for state names (end with a number). */
+#define TK1 0 /* Token expected: name, #CHARS, data tag grp, model. */
+#define CO1 2 /* Connector between tokens expected. */
+#define ER1 4 /* PERO found when token was expected. */
+#define SP1 6 /* Name or model: suffix or connector expected. */
+#define RN1 8 /* RNI found; possible #PCDATA. */
+#define DG1 10 /* Data tag: group begun; name expected. */
+#define DN1 12 /* Data tag: name found; SEQ connector expected. */
+#define DT1 14 /* Data tag: ignore template and pattern; MSC expected. */
+#define DR1 16 /* PERO found when data tag name was expected. */
+#define LI1 18 /* Literal in data tag group; search for LIT. */
+#define LA1 20 /* Literal in data tag group; search for LITA. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+tk01 []={TK1 ,TK1 ,SP1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,DG1 ,TK1 ,TK1 ,ER1 ,TK1 ,TK1 ,RN1 ,TK1 ,TK1 },/*tk1*/
+tk01a[]={INV_,INV_,NAS_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,GRP_,INV_,INV_,
+ INV_,GRP_,INV_,INV_,NOP_,INV_,INV_,NOP_,INV_,INV_},
+
+co01 []={TK1 ,TK1 ,TK1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,TK1 ,SP1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*co1*/
+co01a[]={INV_,INV_,INV_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,AND ,GRPE,INV_,INV_,INV_,
+ INV_,INV_,INV_,OR ,INV_,INV_,INV_,INV_,SEQ ,INV_},
+
+er01 []={TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,ER1 ,TK1 ,ER1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*er1*/
+er01a[]={PCI_,PCI_,PER_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+sp01 []={TK1 ,TK1 ,TK1 ,CO1 ,CO1 ,SP1 ,CO1 ,SP1 ,CO1 ,TK1 ,SP1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,CO1 ,TK1 ,TK1 ,CO1 ,CO1 ,TK1 ,TK1 ,TK1 },/*sp1*/
+sp01a[]={INV_,LEN_,LEN_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,AND ,GRPE,INV_,INV_,INV_,
+ INV_,INV_,OPT ,OR ,INV_,REP ,OREP,INV_,SEQ ,LEN_},
+
+/* bit nmc nms spc spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+rn01 []={TK1 ,TK1 ,CO1 ,TK1 ,TK1 ,RN1 ,TK1 ,RN1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*rn1*/
+rn01a[]={PCI_,PCI_,RNS_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+dg01 []={TK1 ,TK1 ,DN1 ,DG1 ,DG1 ,DG1 ,DG1 ,DG1 ,DG1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,DR1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*dg1*/
+dg01a[]={INV_,INV_,NAS_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,NOP_,INV_,INV_,INV_,INV_,INV_},
+
+dn01 []={TK1 ,TK1 ,TK1 ,DN1 ,DN1 ,DN1 ,DN1 ,DN1 ,DN1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,DT1 ,TK1 },/*dn1*/
+dn01a[]={INV_,INV_,INV_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,INV_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,INV_,INV_,INV_,INV_,DTAG,INV_},
+
+dt01 []={TK1 ,TK1 ,TK1 ,DT1 ,DT1 ,DT1 ,DT1 ,DT1 ,DT1 ,TK1 ,DT1 ,DT1 ,LI1 ,LA1 ,
+ CO1 ,TK1 ,TK1 ,DT1 ,DT1 ,TK1 ,TK1 ,TK1 ,DT1 ,TK1 },/*dt1*/
+dt01a[]={INV_,INV_,INV_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,INV_,NOP_,NOP_,NOP_,NOP_,
+ GRPE,INV_,INV_,NOP_,NOP_,INV_,INV_,INV_,NOP_,INV_},
+
+/* bit nmc nms spc spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+dr01 []={TK1 ,TK1 ,DG1 ,TK1 ,TK1 ,DR1 ,TK1 ,DR1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*dr1*/
+dr01a[]={PCI_,PCI_,PER_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+li01 []={LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,DT1 ,LI1 ,
+ LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 },/*li1*/
+li01a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+la01 []={LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,DT1 ,
+ LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 },/*la1*/
+la01a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+*grcmtab[] = {tk01, tk01a, co01, co01a, er01, er01a, sp01, sp01a,
+ rn01, rn01a, dg01, dg01a, dn01, dn01a, dt01, dt01a,
+ dr01, dr01a, li01, li01a, la01, la01a};
+struct parse pcbgrcm = {"GRCM", lexgrp, grcmtab, 0, 0, 0, 0};
+#undef TK1
+#undef CO1
+#undef ER1
+#undef SP1
+#undef RN1
+#undef DG1
+#undef DN1
+#undef DT1
+#undef DR1
+#undef LI1
+#undef LA1
+/* PCBGRCS: State and action table for content model suffix.
+ If suffix occurs, process it. Otherwise, put character
+ back for the next parse.
+*/
+/* Symbols for state names (end with a number). */
+#define SP4 0 /* Suffix expected. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+sp04 []={SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,
+ SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 ,SP4 },/*sp4*/
+sp04a[]={RCR_,RCR_,RCR_,RCR_,RCR_,SYS_,EE_ ,GET_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,
+ RCR_,RCR_,OPT ,RCR_,RCR_,REP ,OREP,RCR_,RCR_,RCR_},
+
+*grcstab[] = {sp04, sp04a};
+struct parse pcbgrcs = {"GRCS", lexgrp, grcstab, 0, 0, 0, 0};
+#undef SP4
+/* PCBGRNT: State and action table for name token group parse.
+ Groups cannot nest. Reserved names are not allowed.
+ No suffixes or data tag pattern groups.
+*/
+/* Symbols for state names (end with a number). */
+#define TK1 0 /* Token expected: name, #CHARS, data tag grp, model. */
+#define CO1 2 /* Connector between tokens expected. */
+#define ER1 4 /* PERO found when token was expected. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+tk02 []={TK1 ,CO1 ,CO1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,ER1 ,TK1 ,TK1 ,TK1 ,TK1 ,CO1 },/*tk1*/
+tk02a[]={INV_,NMT_,NMT_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,NOP_,INV_,INV_,INV_,INV_,NMT_},
+
+co02 []={TK1 ,TK1 ,TK1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*co1*/
+co02a[]={INV_,INV_,INV_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,NOP_,GRPE,INV_,INV_,INV_,
+ INV_,INV_,INV_,NOP_,INV_,INV_,INV_,INV_,NOP_,INV_},
+
+er02 []={TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,ER1 ,TK1 ,ER1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*er1*/
+er02a[]={PCI_,PCI_,PER_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+*grnttab[] = {tk02, tk02a, co02, co02a, er02, er02a};
+struct parse pcbgrnt = {"GRNT", lexgrp, grnttab, 0, 0, 0, 0};
+#undef TK1
+#undef CO1
+#undef ER1
+/* PCBGRNM: State and action table for name group parse.
+ Groups cannot nest. Reserved names are not allowed.
+ No suffixes or data tag pattern groups.
+*/
+/* Symbols for state names (end with a number). */
+#define TK1 0 /* Token expected: name, #CHARS, data tag grp, model. */
+#define CO1 2 /* Connector between tokens expected. */
+#define ER1 4 /* PERO found when token was expected. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+tk03 []={TK1 ,TK1 ,CO1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,ER1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*tk1*/
+tk03a[]={INV_,INV_,NAS_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,NOP_,INV_,INV_,INV_,INV_,INV_},
+
+co03 []={TK1 ,TK1 ,TK1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,CO1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*co1*/
+co03a[]={INV_,INV_,INV_,NOP_,NOP_,SYS_,EE_ ,GET_,RS_ ,NOP_,GRPE,INV_,INV_,INV_,
+ INV_,INV_,INV_,NOP_,INV_,INV_,INV_,INV_,NOP_,INV_},
+
+er03 []={TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,ER1 ,TK1 ,ER1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*er1*/
+er03a[]={PCI_,PCI_,PER_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+*grnmtab[] = {tk03, tk03a, co03, co03a, er03, er03a};
+struct parse pcbgrnm = {"GRNM", lexgrp, grnmtab, 0, 0, 0, 0};
+#undef TK1
+#undef CO1
+#undef ER1
+/* PCBREF: State and action table to find the end of entity, parameter entity,
+ and character references. The opening delimiter and name
+ have already been found; the parse determines whether the
+ tokenization of the name ended normally and processes the REFC.
+*/
+/* Symbols for state names (end with a number). */
+#define ER5 0 /* Handle REFC or other entity reference termination. */
+#define ER6 2 /* Return to caller and reset state for next call. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+er05 []={ER5 ,ER6 ,ER6 ,ER6 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,
+ ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER6 },/*er5*/
+er05a[]={RCR_,LEN_,LEN_,NOP_,RCR_,SYS_,RCR_,GET_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,
+ RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,NOP_},
+
+er06 []={ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,
+ ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 ,ER5 },/*er6*/
+er06a[]={RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,
+ RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_,RCR_},
+
+*reftab[]={er05, er05a, er06, er06a};
+struct parse pcbref = {"ENTREF", lexgrp, reftab, 0, 0, 0, 0};
+#undef ER5
+#undef ER6
+/*
+Use (typical) Name Ending Chsw References RS RE SEP
+Parameter literal LITPC LIT/A OK Parm,Char RSM_ LAM_ LAM_
+ Data tag template NO
+System ID LITC LIT/A n/a none RSM_ LAM_ LAM_
+ Processing instruction PIC
+Attribute value LITRV LIT/A NO Gen,Char RS_ FUN_ FUN_
+Minimum literal LITV LIT/A n/a none RS_ FUN_ MLE_
+*/
+/* PCBLITP: Literal parse with parameter and character references;
+ no function character translation.
+*/
+/* Symbols for state names (end with a number). */
+#define DA0 0 /* Data in buffer. */
+#define ER0 2 /* ERO found. */
+#define CR0 4 /* CRO found (ER0, RNI). */
+#define PR0 6 /* PRO found (for PCBLITP). */
+
+static UNCH
+/* free num min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+da13 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ER0 ,
+ DA0 ,DA0 ,DA0 ,PR0 ,DA0 ,DA0 ,DA0 ,DA0 },/*da3*/
+da13a[]={MLA_,MLA_,MLA_,MLA_,MLA_,NON_,EE_ ,GET_,RSM_,MLA_,MLA_,MLA_,NSC_,NOP_,
+ MLA_,MLA_,MLA_,NOP_,MLA_,MLA_,MLA_,TER_},
+
+er13 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ER0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 },/*er3*/
+er13a[]={LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,GET_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,
+ LPR_,LPR_,LPR_,LPR_,NOP_,LPR_,LPR_,LPR_},
+
+cr13 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*cr3*/
+cr13a[]={LP2_,CRN_,LP2_,CRA_,LP2_,LP2_,LP2_,GET_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,
+ LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_},
+
+pr13 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,PR0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*pr3*/
+pr13a[]={LPR_,LPR_,LPR_,PEX_,LPR_,LPR_,LPR_,GET_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,
+ LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_},
+
+*litptab[] = {da13, da13a, er13, er13a, cr13, cr13a, pr13, pr13a};
+struct parse pcblitp = {"LITP", lexlms, litptab, 0, 0, 0, 0};
+#undef DA0
+#undef ER0
+#undef CR0
+#undef PR0
+/* PCBLITC: Literal parse; no references; no function char translation.
+ Used for character data (system data).
+*/
+/* Symbols for state names (end with a number). */
+#define DA0 0 /* Data in buffer. */
+
+static UNCH
+/* free num min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+da2 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*da2*/
+da2a[]={MLA_,MLA_,MLA_,MLA_,MLA_,SYS_,EOF_,GET_,RSM_,MLA_,MLA_,MLA_,SYS_,MLA_,
+ MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,TER_},
+
+*litctab[] = {da2, da2a};
+struct parse pcblitc = {"LITC", lexlms, litctab, 0, 0, 0, 0};
+#undef DA0
+/* PCBLITR: Attribute value parse; general and character references;
+ function chars are translated.
+*/
+/* Symbols for state names (end with a number). */
+#define DA0 0 /* Data in buffer. */
+#define ER0 2 /* ERO found. */
+#define CR0 4 /* CRO found (ER0, RNI). */
+
+static UNCH
+/* free num min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+da11 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ER0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*da1*/
+da11a[]={MLA_,MLA_,MLA_,MLA_,MLA_,NON_,EE_ ,GET_,RS_ ,FUN_,FUN_,MLA_,NSC_,NOP_,
+ MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,TER_},
+
+er11 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ER0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 },/*er1*/
+er11a[]={LPR_,LPR_,LPR_,ERX_,LPR_,LPR_,LPR_,GET_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,
+ LPR_,LPR_,LPR_,LPR_,NOP_,LPR_,LPR_,LPR_},
+
+cr11 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*cr1*/
+cr11a[]={LP2_,CRN_,LP2_,CRA_,LP2_,LP2_,LP2_,GET_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,
+ LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_},
+
+*litrtab[] = {da11, da11a, er11, er11a, cr11, cr11a};
+struct parse pcblitr = {"LITR", lexlms, litrtab, 0, 0, 0, 0};
+#undef DA0
+#undef ER0
+#undef CR0
+/* PCBLITV: Literal parse; no references; RS ignored; RE/SPACE sequences
+ become single SPACE. Only minimum data characters allowed.
+*/
+/* Symbols for state names (end with a number). */
+#define LS0 0 /* Leading SPACE or RE found. */
+#define VA0 2 /* Valid character found. */
+#define SP0 4 /* SPACE/RE sequence begun. */
+
+static UNCH
+/* free num min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+ls10 []={VA0 ,VA0 ,VA0 ,VA0 ,LS0 ,VA0 ,LS0 ,LS0 ,LS0 ,LS0 ,LS0 ,VA0 ,VA0 ,VA0 ,
+ VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,LS0 },/*ls0*/
+ls10a[]={MLE_,MLA_,MLA_,MLA_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,MLE_,SYS_,SYS_,MLE_,
+ MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,TER_},
+va10 []={VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,SP0 ,VA0 ,VA0 ,VA0 ,
+ VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,LS0 },/*va0*/
+da10a[]={MLE_,MLA_,MLA_,MLA_,MLA_,SYS_,EOF_,GET_,RS_ ,FUN_,MLE_,SYS_,SYS_,MLE_,
+ MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,TER_},
+sp10 []={VA0 ,VA0 ,VA0 ,VA0 ,SP0 ,VA0 ,VA0 ,SP0 ,SP0 ,SP0 ,SP0 ,VA0 ,VA0 ,VA0 ,
+ VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,VA0 ,LS0 },/*sp0*/
+sp10a[]={MLE_,MLA_,MLA_,MLA_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,MLE_,SYS_,SYS_,MLE_,
+ MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,MLE_,RPR_},
+
+*litvtab[] = {ls10, ls10a, va10, da10a, sp10, sp10a};
+struct parse pcblitv = {"LITV", lexlms, litvtab, 0, 0, 0, 0};
+#undef LS0
+#undef VA0
+#undef SP0
+/* PCBLITT: Tokenized attribute value parse.
+*/
+
+/* PCBLITT: Attribute value parse; general and character references;
+ function chars are translated.
+*/
+/* Symbols for state names (end with a number). */
+#define SP0 0 /* Ignore spaces */
+#define DA0 2 /* Data character */
+#define ER0 4 /* ERO found; ignore space */
+#define ER1 6 /* ERO found; don't ignore space */
+#define CR0 8 /* CRO found (ER0, RNI); ignore space */
+#define CR1 10 /* CR0 found; don't ignore space */
+
+int pcblittda = DA0;
+
+static UNCH
+/* free num min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+
+sp14 []={DA0 ,DA0 ,DA0 ,DA0 ,SP0 ,DA0 ,DA0 ,SP0 ,SP0 ,SP0 ,SP0 ,DA0 ,DA0 ,ER0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*sp0*/
+sp14a[]={MLA_,MLA_,MLA_,MLA_,NOP_,NON_,EE_ ,GET_,RS_ ,NOP_,NOP_,MLA_,NSC_,NOP_,
+ MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,TER_},
+
+da14 []={DA0 ,DA0 ,DA0 ,DA0 ,SP0 ,DA0 ,DA0 ,DA0 ,DA0 ,SP0 ,SP0 ,DA0 ,DA0 ,ER1 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,SP0 },/*da0*/
+da14a[]={MLA_,MLA_,MLA_,MLA_,MLA_,NON_,EE_ ,GET_,RS_ ,FUN_,FUN_,MLA_,NSC_,NOP_,
+ MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,MLA_,TER_},
+
+er14 []={DA0 ,DA0 ,DA0 ,SP0 ,DA0 ,DA0 ,DA0 ,ER0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 },/*er0*/
+er14a[]={LPR_,LPR_,LPR_,ERX_,LPR_,LPR_,LPR_,GET_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,
+ LPR_,LPR_,LPR_,LPR_,NOP_,LPR_,LPR_,LPR_},
+
+er15 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ER1 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,CR1 ,DA0 ,DA0 ,DA0 },/*er1*/
+er15a[]={LPR_,LPR_,LPR_,ERX_,LPR_,LPR_,LPR_,GET_,LPR_,LPR_,LPR_,LPR_,LPR_,LPR_,
+ LPR_,LPR_,LPR_,LPR_,NOP_,LPR_,LPR_,LPR_},
+
+cr14 []={DA0 ,DA0 ,DA0 ,SP0 ,DA0 ,DA0 ,DA0 ,CR0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*cr0*/
+cr14a[]={LP2_,CRN_,LP2_,CRA_,LP2_,LP2_,LP2_,GET_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,
+ LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_},
+
+cr15 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,CR1 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,
+ DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*cr1*/
+cr15a[]={LP2_,CRN_,LP2_,CRA_,LP2_,LP2_,LP2_,GET_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,
+ LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_,LP2_},
+
+*litttab[] = {sp14, sp14a, da14, da14a, er14, er14a, er15, er15a, cr14, cr14a,
+ cr15, cr15a};
+struct parse pcblitt = {"LITT", lexlms, litttab, 0, 0, 0, 0};
+#undef SP0
+#undef DA0
+#undef ER0
+#undef ER1
+#undef CR0
+#undef CR1
+/* PCBMD: State and action table for markup declaration tokenization.
+ Columns are based on LEXMARK.C.
+*/
+/* Symbols for state names (end with a number). */
+#define SP1 0 /* Separator before token expected. */
+#define TK1 2 /* Token expected. */
+#define CM0 4 /* COM[1] found when sep expected: possible comment, MGRP.*/
+#define CM1 6 /* COM[1] found: possible comment, MGRP, or minus.*/
+#define CM2 8 /* COM[2] found; in comment. */
+#define CM3 10 /* Ending COM[1] found; end comment or continue it. */
+#define PR1 12 /* PERO found when token was expected. */
+#define PX1 14 /* PLUS found: PGRP or error. */
+#define RN1 16 /* RNI found; possible reserved name start. */
+
+int pcbmdtk = TK1; /* PCBMD: token expected. */
+
+static UNCH
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+sp21 []={SP1 ,SP1 ,SP1 ,SP1 ,TK1 ,SP1 ,TK1 ,SP1 ,TK1 ,CM0 ,SP1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,SP1 ,PR1 ,PX1 ,SP1 ,RN1 ,SP1 ,SP1 ,SP1 },
+sp21a[]={INV_,LEN_,LEN_,LEN_,NOP_,SYS_,EE_ ,GET_,RS_ ,NOP_,INV_,GRPS,LIT ,LITE,
+ MDS ,INV_,NOP_,NOP_,INV_,NOP_,EMD ,INV_,INV_},
+
+tk21 []={SP1 ,SP1 ,SP1 ,SP1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,CM1 ,SP1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,SP1 ,PR1 ,PX1 ,SP1 ,RN1 ,SP1 ,SP1 ,SP1 },
+tk21a[]={INV_,NMT ,NUM ,NAS ,NOP_,SYS_,EE_ ,GET_,RS_ ,NOP_,INV_,GRPS,LIT ,LITE,
+ MDS ,INV_,NOP_,NOP_,INV_,NOP_,EMD ,INV_,INV_},
+
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+cm20 []={SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,CM0 ,SP1 ,CM0 ,SP1 ,CM2 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 },
+cm20a[]={LNR_,LNR_,LNR_,LNR_,LNR_,SYS_,LNR_,GET_,LNR_,NOP_,LNR_,LNR_,LNR_,LNR_,
+ LNR_,LNR_,LNR_,LNR_,LNR_,LNR_,LNR_,LNR_,LNR_},
+
+cm21 []={TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,CM1 ,TK1 ,CM1 ,TK1 ,CM2 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },
+cm21a[]={CDR ,CDR ,CDR ,CDR ,CDR ,SYS_,CDR ,GET_,CDR ,NOP_,CDR ,MGRP,CDR ,CDR ,
+ CDR ,CDR ,CDR ,CDR ,CDR ,CDR ,CDR ,CDR ,CDR },
+
+cm22 []={CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,TK1 ,CM2 ,CM2 ,CM3 ,CM2 ,CM2 ,CM2 ,CM2 ,
+ CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 },
+cm22a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+cm23 []={CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM3 ,TK1 ,CM3 ,CM2 ,TK1 ,CM2 ,CM2 ,CM2 ,CM2 ,
+ CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 },
+cm23a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+pr21 []={SP1 ,SP1 ,SP1 ,TK1 ,TK1 ,PR1 ,SP1 ,PR1 ,TK1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,TK1 ,SP1 ,SP1 ,SP1 },
+pr21a[]={PCI_,PCI_,PCI_,PER_,PEN ,SYS_,PENR,GET_,PEN ,PENR,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PENR,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+px21 []={SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,PX1 ,SP1 ,PX1 ,SP1 ,SP1 ,SP1 ,TK1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 },
+px21a[]={PCI_,PCI_,PCI_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PGRP,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+rn21 []={TK1 ,TK1 ,TK1 ,SP1 ,TK1 ,RN1 ,TK1 ,RN1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,
+ TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },
+rn21a[]={PCI_,PCI_,PCI_,RNS ,PCI_,SYS_,PCI_,GET_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+*mdtab[] = {sp21, sp21a, tk21, tk21a, cm20, cm20a, cm21, cm21a, cm22, cm22a,
+ cm23, cm23a, pr21, pr21a, px21, px21a, rn21, rn21a};
+struct parse pcbmd = {"MD", lexmark, mdtab, 0, 0, 0, 0};
+#undef SP1
+#undef TK1
+#undef CM0
+#undef CM1
+#undef CM2
+#undef CM3
+#undef PR1
+#undef PX1
+#undef RN1
+/* PCBMDC: State and action table for comment declaration.
+*/
+/* Symbols for state names (end with a number). */
+#define CD2 0 /* COM[2] found; in comment. */
+#define CD3 2 /* Ending COM[1] found; end comment or continue it. */
+#define EM1 4 /* Ending COM[2] found; start new comment or end. */
+#define CD1 6 /* COM[1] found; new comment or error. */
+
+static UNCH
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+cd22 []={CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD3 ,CD2 ,CD2 ,CD2 ,CD2 ,
+ CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 },
+cd22a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+cd23 []={CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD3 ,CD2 ,CD3 ,CD2 ,EM1 ,CD2 ,CD2 ,CD2 ,CD2 ,
+ CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 },
+cd23a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+em21 []={CD2 ,CD2 ,CD2 ,CD2 ,EM1 ,EM1 ,CD2 ,EM1 ,EM1 ,CD1 ,CD2 ,CD2 ,CD2 ,CD2 ,
+ CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 },
+em21a[]={INV_,INV_,INV_,INV_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,INV_,INV_,EMD ,INV_,INV_},
+
+cd21 []={CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD1 ,CD2 ,CD1 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,
+ CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 ,CD2 },
+cd21a[]={PCI_,PCI_,PCI_,PCI_,PCI_,SYS_,EOF_,GET_,PCI_,NOP_,PCI_,PCI_,PCI_,PCI_,
+ PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_,PCI_},
+
+*mdctab[] = {cd22, cd22a, cd23, cd23a, em21, em21a, cd21, cd21a};
+struct parse pcbmdc = {"MDC", lexmark, mdctab, 0, 0, 0, 0};
+#undef CD2
+#undef CD3
+#undef EM1
+#undef CD1
+/* PCBMDI: State and action table for ignoring markup declarations.
+ Literals are handled properly so a TAGC won't end the declaration.
+ An error is noted if the entity ends within a declaration that
+ is being ignored.
+ TO DO: Handle nested declaration sets.
+*/
+/* Symbols for state names (end with a number). */
+#define NC1 0 /* Not in a comment; TAGC ends declaration. */
+#define IC1 2 /* COM[1] found; possible comment. */
+#define IC2 4 /* COM[2] found; in comment. */
+#define IC3 6 /* Ending COM[1] found; end comment or continue it. */
+#define LI1 8 /* Literal parameter; search for LIT. */
+#define LA1 10 /* Literal parameter; search for LITA. */
+
+static UNCH
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+nc21 []={NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,IC1 ,NC1 ,NC1 ,LI1 ,LA1 ,
+ NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 },
+nc21a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,EMD ,NOP_,NOP_},
+
+ic21 []={NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,IC1 ,NC1 ,IC1 ,NC1 ,IC2 ,NC1 ,NC1 ,LI1 ,LA1 ,
+ NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 ,NC1 },
+ic21a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,EMD ,NOP_,NOP_},
+
+ic22 []={IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,NC1 ,IC2 ,IC2 ,IC3 ,IC2 ,IC2 ,IC2 ,IC2 ,
+ IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 },
+ic22a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+ic23 []={IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC3 ,NC1 ,IC3 ,IC2 ,NC1 ,IC2 ,IC2 ,IC2 ,IC2 ,
+ IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 ,IC2 },/*ic3*/
+ic23a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+li21 []={LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,NC1 ,LI1 ,
+ LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 ,LI1 },/*li1*/
+li21a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+la21 []={LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,NC1 ,
+ LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 ,LA1 },/*la1*/
+la21a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+*mditab[] = {nc21, nc21a, ic21, ic21a, ic22, ic22a,
+ ic23, ic23a, li21, li21a, la21, la21a};
+struct parse pcbmdi = {"MDI", lexmark, mditab, 0, 0, 0, 0};
+#undef NC1
+#undef IC1
+#undef IC2
+#undef IC3
+#undef LI1
+#undef LA1
+/* PCBMSRC: State and action table for marked section in RCDATA mode.
+ Nested marked sections are not recognized; the first MSE ends it.
+ Initial state assumes an MS declaration was processed.
+ Columns are based on LEXLMS.C but LITC column needn't exist.
+*/
+/* Symbols for state names (end with a number). */
+#define ET0 0 /* MSS processed or buffer flushed; no data. */
+#define DA0 2 /* Data in buffer. */
+#define ER0 4 /* ERO found; start lookahead buffer. */
+#define CR0 6 /* CRO found (ER0, RNI). */
+#define ME0 8 /* MSC found. */
+#define ME1 10 /* MSC, MSC found. */
+
+static UNCH
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+et30 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,DA0 ,DA0 ,ET0 ,ER0 ,
+ DA0 ,ME0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*et0*/
+et30a[]={DAS_,DAS_,DAS_,DAS_,DAS_,NON_,EE_ ,GET_,RS_ ,REF_,DAS_,DAS_,NSC_,LAS_,
+ DAS_,LAS_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_},
+
+da30 []={DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,DA0 ,DA0 ,ET0 ,ET0 ,
+ DA0 ,ET0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 ,DA0 },/*da0*/
+da30a[]={NOP_,NOP_,NOP_,NOP_,NOP_,DAF_,DAF_,DAF_,DAF_,DAF_,NOP_,NOP_,DAF_,DAF_,
+ NOP_,DAF_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+er30 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ER0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,CR0 ,ET0 ,ET0 ,ET0 },/*er0*/
+er30a[]={LAF_,LAF_,LAF_,ERX_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAM_,LAF_,LAF_,LAF_},
+
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+cr30 []={ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,CR0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,
+ ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 ,ET0 },/*cr0*/
+cr30a[]={LAF_,CRN_,LAF_,CRA_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+me30 []={ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ME0, ET0 ,ET0 ,ET0 ,ET0, ET0 ,ET0 ,
+ ET0, ME1, ET0 ,ET0, ET0 ,ET0, ET0 ,ET0 },/*me0*/
+me30a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+me31 []={ET0, ET0, ET0, ET0, ET0 ,ET0, ET0, ME1, ET0 ,ET0 ,ET0 ,ET0, ET0 ,ET0 ,
+ ET0, ET0, ET0 ,ET0, ET0 ,ET0, ET0 ,ET0,},/*me1*/
+me31a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,MSE_,LAF_,LAF_},
+
+*msrctab[]={et30, et30a, da30, da30a, er30, er30a, cr30, cr30a,
+ me30, me30a, me31, me31a};
+struct parse pcbmsrc = {"MSRCDATA", lexlms, msrctab, 0, 0, 0, 0};
+#undef ET0
+#undef DA0
+#undef ER0
+#undef CR0
+#undef ME0
+#undef ME1
+/* PCBMSC: State and action table for marked section in CDATA mode.
+ Nested marked sections are not recognized; the first MSE ends it.
+ Initial state assumes an MS declaration was processed.
+*/
+/* Symbols for state names (end with a number). */
+#define ET2 0 /* MSS processed or buffer flushed; no data. */
+#define DA2 2 /* Data in buffer. */
+#define ME2 4 /* MSC found. */
+#define ME3 6 /* MSC, MSC found. */
+
+static UNCH
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+et32 []={DA2 ,DA2 ,DA2 ,DA2 ,DA2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,DA2 ,DA2 ,ET2 ,DA2 ,
+ DA2 ,ME2 ,DA2 ,DA2 ,DA2 ,DA2 ,DA2 ,DA2 },/*et2*/
+et32a[]={DAS_,DAS_,DAS_,DAS_,DAS_,NON_,EOF_,GET_,RS_ ,REF_,DAS_,DAS_,NSC_,DAS_,
+ DAS_,LAS_,DAS_,DAS_,DAS_,DAS_,DAS_,DAS_},
+
+da32 []={DA2 ,DA2 ,DA2 ,DA2 ,DA2 ,ET2 ,ET2 ,ET2 ,ET2 ,ET2 ,DA2 ,DA2 ,ET2 ,DA2 ,
+ DA2 ,ET2 ,DA2 ,DA2 ,DA2 ,DA2 ,DA2 ,DA2 },/*da2*/
+da32a[]={NOP_,NOP_,NOP_,NOP_,NOP_,DAF_,DAF_,DAF_,DAF_,DAF_,NOP_,NOP_,DAF_,NOP_,
+ NOP_,DAF_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+me32 []={ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ME2, ET2 ,ET2 ,ET2 ,ET2, ET2 ,ET2 ,
+ ET2, ME3, ET2 ,ET2, ET2 ,ET2, ET2, ET2,},/*me2*/
+me32a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAM_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_},
+
+me33 []={ET2, ET2, ET2, ET2, ET2 ,ET2, ET2, ME3, ET2 ,ET2 ,ET2 ,ET2, ET2 ,ET2 ,
+ ET2, ET2, ET2 ,ET2, ET2 ,ET2, ET2, ET2,},/*me3*/
+me33a[]={LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,GET_,LAF_,LAF_,LAF_,LAF_,LAF_,LAF_,
+ LAF_,LAF_,LAF_,LAF_,LAF_,MSE_,LAF_,LAF_},
+
+*msctab[]={et32, et32a, da32, da32a, me32, me32a, me33, me33a};
+struct parse pcbmsc = {"MSCDATA", lexlms, msctab, 0, 0, 0, 0};
+#undef ET2
+#undef DA2
+#undef ME2
+#undef ME3
+/* PCBMSI: State and action table for marked section in IGNORE mode.
+ Nested marked sections are recognized; the matching MSE ends it.
+ Initial state assumes an MS declaration, MSS, or MSE was processed.
+*/
+/* Symbols for state names (end with a number). */
+#define ET4 0 /* Markup found or buffer flushed; no data. */
+#define ME4 2 /* MSC found. */
+#define ME5 4 /* MSC, MSC found. */
+#define ES4 6 /* TAGO found. */
+#define MD4 8 /* MDO found (TAGO, MDO[2]). */
+
+static UNCH
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc refc */
+et34 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,
+ ET4 ,ME4 ,ET4 ,ET4 ,ET4 ,ET4 ,ES4 ,ET4 ,ET4 },/*et4*/
+et34a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+me34 []={ET4, ET4, ET4, ET4, ET4 ,ET4, ET4, ME4, ET4 ,ET4 ,ET4 ,ET4, ET4, ET4 ,
+ ET4, ME5 ,ET4, ET4, ET4 ,ET4, ET4, ET4, ET4,},/*me4*/
+me34a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+me35 []={ET4, ET4, ET4, ET4, ET4 ,ET4, ET4, ME5, ET4 ,ET4 ,ET4 ,ET4, ET4, ET4 ,
+ ET4, ET4 ,ET4, ET4, ET4 ,ET4, ET4, ET4, ET4,},/*me5*/
+me35a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,MSE_,NOP_,NOP_,NOP_},
+
+/* free nu min nms spc non ee eob rs re sep cde nsc ero
+ mdo msc mso pero rni tagc tago litc */
+es34 []={ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ES4 ,ET4 ,ES4 ,ET4 ,ET4 ,ET4 ,ET4 ,ES4 ,ET4 ,
+ MD4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 ,ET4 },/*es4*/
+es34a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+md34 []={ET4, ET4, ET4, ET4, ET4 ,MD4, ET4, MD4, ET4 ,ET4 ,ET4 ,ET4, ET4, ET4 ,
+ ET4, ET4 ,ET4, ET4, ET4 ,ET4, ET4, ET4,},/*md4*/
+md34a[]={NOP_,NOP_,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,SYS_,NOP_,
+ NOP_,NOP_,MSS_,NOP_,NOP_,NOP_,NOP_,NOP_},
+
+*msitab[]={et34, et34a, me34, me34a, me35, me35a, es34, es34a, md34, md34a};
+struct parse pcbmsi = {"MSIGNORE", lexlms, msitab, 0, 0, 0, 0};
+#undef ET4
+#undef ME4
+#undef ME5
+#undef ES4
+#undef MD4
+#undef NS4
+/* PCBSTAG: State and action table for start-tag parse.
+ Columns are based on LEXMARK.C.
+*/
+/* Symbols for state names (end with a number). */
+#define SP1 0 /* Separator before name expected. */
+#define AN1 2 /* Attribute name expected. */
+#define SP2 4 /* Separator or value indicator expected. */
+#define VI1 6 /* Value indicator expected. */
+#define AV1 8 /* Attribute value expected. */
+
+int pcbstan = AN1; /* PCBSTAG: attribute name expected. */
+
+static UNCH
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+sp41 []={SP1 ,SP1 ,SP1 ,SP1 ,AN1 ,SP1 ,SP1 ,SP1 ,AN1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 },
+sp41a[]={INV_,LEN_,LEN_,LEN_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,ETIC,INV_,INV_,INV_,
+ INV_,DSC ,INV_,INV_,INV_,INV_,TAGC,TAGO,INV_},
+
+an41 []={SP1 ,SP1 ,SP1 ,SP2 ,AN1 ,AN1 ,AN1 ,AN1 ,AN1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 },
+an41a[]={INV_,NTV ,NTV ,NVS ,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,ETIC,INV_,INV_,INV_,
+ INV_,DSC ,INV_,INV_,INV_,INV_,TAGC,TAGO,INV_},
+
+sp42 []={SP1 ,SP1 ,SP1 ,SP1 ,VI1 ,SP2 ,SP2 ,SP2 ,VI1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,AV1 },
+sp42a[]={INV_,LEN_,LEN_,LEN_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,NASV,INV_,INV_,INV_,
+ INV_,NASV,INV_,INV_,INV_,INV_,NASV,NASV,NOP_},
+
+/* bit nmc num nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+vi41 []={SP1 ,AN1 ,AN1 ,AN1 ,VI1 ,VI1 ,VI1 ,VI1 ,VI1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,AV1 },
+vi41a[]={INV_,NASV,NASV,NASV,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,NASV,INV_,INV_,INV_,
+ INV_,NASV,INV_,INV_,INV_,INV_,NASV,NASV,NOP_},
+
+av41 []={SP1 ,SP1 ,SP1 ,SP1 ,AV1 ,AV1 ,AV1 ,AV1 ,AV1 ,SP1 ,SP1 ,SP1 ,AN1 ,AN1 ,
+ SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 ,SP1 },
+av41a[]={INV_,AVU ,AVU ,AVU ,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,INV_,AVD ,AVDA,
+ INV_,INV_,INV_,INV_,INV_,INV_,INV_,INV_,INV_},
+
+*stagtab[] = {sp41, sp41a, an41, an41a, sp42, sp42a, vi41, vi41a, av41, av41a};
+struct parse pcbstag = {"STAG", lexmark, stagtab, 0, 0, 0, 0};
+#undef SP1
+#undef AN1
+#undef SP2
+#undef VI1
+#undef AV1
+/* PCBETAG: State and action table for end-tag parse.
+*/
+#define TC1 0 /* Tag close expected (no attributes allowed). */
+
+static UNCH
+/* bit nmc nu nms spc non ee eob rs com eti grpo lit lita
+ dso dsc pero plus refc rni tagc tago vi */
+tc41 []={TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,
+ TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 ,TC1 },/*tc1*/
+tc41a[]={INV_,INV_,INV_,INV_,NOP_,SYS_,EOF_,GET_,RS_ ,INV_,INV_,INV_,INV_,INV_,
+ INV_,INV_,INV_,INV_,INV_,INV_,TAGC,TAGO,INV_},
+
+*etagtab[] = {tc41, tc41a};
+struct parse pcbetag = {"ETAG", lexmark, etagtab, 0, 0, 0, 0};
+#undef TC1
+/* PCBVAL: State and action table for tokenizing attribute values.
+ Columns are based on lextoke (but EOB cannot occur).
+*/
+/* Symbols for state names (end with a number). */
+#define TK1 0 /* Token expected. */
+#define SP1 2 /* Separator before token expected. */
+
+static UNCH
+/* inv rec sep sp nmc nms nu eob */
+tk51 []={TK1 ,TK1 ,TK1 ,TK1 ,SP1 ,SP1 ,SP1 },/*tk1*/
+tk51a[]={INVA,INVA,INVA,NOPA,NMTA,NASA,NUMA},
+
+sp51 []={TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 },/*sp1*/
+sp51a[]={INVA,INVA,INVA,NOPA,LENA,LENA,LENA},
+
+*valtab[] = {tk51, tk51a, sp51, sp51a};
+struct parse pcbval = {"VAL", lextoke, valtab, 0, 0, 0, 0};
+#undef TK1
+#undef SP1
+/* PCBEAL: State and action table for end of attribute specification list.
+ If delimiter occurs, process it. Otherwise, put invalid character
+ back for the next parse.
+*/
+/* Symbols for state names (end with a number). */
+#define AL0 0 /* Delimiter expected. */
+
+static UNCH
+/* bit nmc nms re spc non ee eob rs and grpc grpo lit lita
+ dtgc dtgo opt or pero plus rep rni seq refc */
+al00 []={AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,
+ AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 ,AL0 },/*al0*/
+al00a[]={INV_,INV_,INV_,INV_,INV_,SYS_,EE_ ,GET_,INV_,INV_,INV_,INV_,INV_,INV_,
+ GRPE,INV_,INV_,INV_,INV_,INV_,INV_,INV_,INV_,INV_},
+
+*ealtab[] = {al00, al00a};
+struct parse pcbeal = {"EAL", lexgrp, ealtab, 0, 0, 0, 0};
+#undef AL0
+
+/* PCBSD: State and action tables for SGML declaration parsing. */
+
+/* Symbols for state names. */
+
+#define SP1 0 /* Separator before token expected. */
+#define TK1 2 /* Token expected. */
+#define CM0 4 /* COM[1] found when sep expected: possible comment.*/
+#define CM1 6 /* COM[1] found: possible comment.*/
+#define CM2 8 /* COM[2] found; in comment. */
+#define CM3 10 /* Ending COM[1] found; end comment or continue it. */
+
+static UNCH
+/* sig dat num nms spc non ee eob rs com lit lita tagc */
+
+sp31 []={SP1 ,SP1 ,SP1 ,SP1 ,TK1 ,SP1 ,SP1 ,SP1 ,TK1 ,CM0 ,TK1 ,TK1 ,SP1 },
+sp31a[]={INV_,ISIG,LEN_,LEN_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,LIT1,LIT2,ESGD},
+
+tk31 []={TK1 ,TK1 ,SP1 ,SP1 ,TK1 ,TK1 ,TK1 ,TK1 ,TK1 ,CM1 ,TK1 ,TK1 ,SP1 },
+tk31a[]={INV_,ISIG,NUM1,NAS1,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,LIT1,LIT2,ESGD},
+
+cm30 []={SP1 ,CM0 ,SP1 ,SP1 ,SP1 ,CM0 ,SP1 ,CM0 ,SP1 ,CM2 ,SP1 ,SP1 ,SP1 },
+cm30a[]={PCI_,ISIG,PCI_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,NOP_,PCI_,PCI_,PCI_},
+
+cm31 []={TK1 ,CM1 ,TK1 ,TK1 ,TK1 ,CM1 ,TK1 ,CM1 ,TK1 ,CM2 ,TK1 ,TK1 ,TK1 },
+cm31a[]={PCI_,ISIG,PCI_,PCI_,PCI_,SYS_,PCI_,GET_,PCI_,NOP_,PCI_,PCI_,PCI_},
+
+cm32 []={CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,CM2 ,TK1 ,CM2 ,CM2 ,CM3 ,CM2 ,CM2 ,CM2 },
+cm32a[]={NOP_,ISIG,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_},
+
+cm33 []={CM2 ,CM3 ,CM2 ,CM2 ,CM2 ,CM3 ,TK1 ,CM3 ,CM2 ,TK1 ,CM2 ,CM2 ,CM2 },
+cm33a[]={NOP_,ISIG,NOP_,NOP_,NOP_,SYS_,EOF_,GET_,RS_ ,NOP_,NOP_,NOP_,NOP_},
+
+*sdtab[]={sp31, sp31a, tk31, tk31a, cm30, cm30a, cm31, cm31a, cm32, cm32a,
+ cm33, cm33a};
+
+struct parse pcbsd = {"SD", lexsd, sdtab, 0, 0, 0, 0};
+
+#undef SP1
+#undef TK1
+#undef CM0
+#undef CM1
+#undef CM2
+#undef CM3
diff --git a/usr.bin/sgmls/sgmls/portproc.c b/usr.bin/sgmls/sgmls/portproc.c
new file mode 100644
index 0000000..0bb2431
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/portproc.c
@@ -0,0 +1,104 @@
+/* portproc.c -
+
+ Semi-portable implementation of run_process().
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifdef SUPPORT_SUBDOC
+
+#include "std.h"
+#include "entity.h"
+#include "appl.h"
+
+/* This code shows how you might use system() to implement run_process().
+ANSI C says very little about the behaviour of system(), and so this
+is necessarily system dependent. */
+
+/* Characters that are significant to the shell and so need quoting. */
+#define SHELL_MAGIC "$\\\"';&()|<>^ \t\n"
+/* Character with which to quote shell arguments. */
+#define SHELL_QUOTE_CHAR '\''
+/* String that can be used to get SHELL_QUOTE_CHAR into a quoted argument. */
+#define SHELL_ESCAPE_QUOTE "'\\''"
+/* Character that can be used to separate arguments to the shell. */
+#define SHELL_ARG_SEP ' '
+
+static UNS shell_quote P((char *, char *));
+
+int run_process(argv)
+char **argv;
+{
+ char **p;
+ char *s, *command;
+ int ret;
+ UNS len = 0;
+
+ for (p = argv; *p; p++)
+ len += shell_quote(*p, (char *)0);
+ len += p - argv;
+ s = command = xmalloc(len);
+ for (p = argv; *p; ++p) {
+ if (s > command)
+ *s++ = SHELL_ARG_SEP;
+ s += shell_quote(*p, s);
+ }
+ *s++ = '\0';
+ errno = 0;
+ ret = system(command);
+ if (ret < 0)
+ appl_error(E_EXEC, argv[0], strerror(errno));
+ free(command);
+ return ret;
+}
+
+/* Quote a string so that it appears as a single argument to the
+shell (as used for system()). Store the quoted argument in result, if
+result is not NULL. Return the length. */
+
+static
+UNS shell_quote(s, result)
+char *s, *result;
+{
+ UNS len = 0;
+ int quoted = 0;
+
+ if (strpbrk(s, SHELL_MAGIC)) {
+ quoted = 1;
+ len++;
+ if (result)
+ result[0] = SHELL_QUOTE_CHAR;
+ }
+ for (; *s; s++) {
+ if (*s == SHELL_QUOTE_CHAR) {
+ if (result)
+ strcpy(result + len, SHELL_ESCAPE_QUOTE);
+ len += strlen(SHELL_ESCAPE_QUOTE);
+ }
+ else {
+ if (result)
+ result[len] = *s;
+ len++;
+ }
+ }
+ if (quoted) {
+ if (result)
+ result[len] = SHELL_QUOTE_CHAR;
+ len++;
+ }
+ return len;
+}
+
+#endif /* SUPPORT_SUBDOC */
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/serv.c b/usr.bin/sgmls/sgmls/serv.c
new file mode 100644
index 0000000..68b5fe1
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/serv.c
@@ -0,0 +1,299 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+/* ETDDEF: Define an element type definition.
+ Use an existing one if there is one; otherwise create one, which
+ rmalloc initializes to zero which shows it is a virgin etd.
+*/
+PETD etddef(ename)
+UNCH *ename; /* Element name (GI) with length byte. */
+{
+ PETD p; /* Pointer to an etd. */
+ int hnum; /* Hash number for ename. */
+
+ if ((p = (PETD)hfind((THASH)etdtab,ename,hnum = hash(ename, ETDHASH)))==0){
+ p = (PETD)hin((THASH)etdtab, ename, hnum, ETDSZ);
+ }
+ return p;
+}
+/* ETDSET: Store data in an element type definition.
+ The etd must be valid and virgin (except for adl and etdmin).
+ As an etd cannot be modified, there is no checking for existing
+ pointers and no freeing of their storage.
+*/
+#ifdef USE_PROTOTYPES
+PETD etdset(PETD p, UNCH fmin, struct thdr *cmod, PETD *mexgrp, PETD *pexgrp,
+ struct entity **srm)
+#else
+PETD etdset(p, fmin, cmod, mexgrp, pexgrp, srm)
+PETD p; /* Pointer to an etd. */
+UNCH fmin; /* Minimization bit flags. */
+struct thdr *cmod; /* Pointer to content model. */
+PETD *mexgrp; /* Pointers to minus and plus exception lists. */
+PETD *pexgrp; /* Pointers to minus and plus exception lists. */
+struct entity **srm; /* Short reference map. */
+#endif
+{
+ p->etdmin |= fmin;
+ p->etdmod = cmod;
+ p->etdmex = mexgrp;
+ p->etdpex = pexgrp;
+ p->etdsrm = srm;
+ return p;
+}
+/* ETDREF: Retrieve the pointer to an element type definition.
+*/
+PETD etdref(ename)
+UNCH *ename; /* Element name (GI) with length byte.. */
+{
+
+ return (PETD)hfind((THASH)etdtab, ename, hash(ename, ETDHASH));
+}
+/* ETDCAN: Cancel an element definition. The etd is freed and is removed
+ from the hash table, but its model and other pointers are not freed.
+*/
+VOID etdcan(ename)
+UNCH *ename; /* GI name (with length and EOS). */
+{
+ PETD p;
+
+ if ((p = (PETD)hout((THASH)etdtab, ename, hash(ename, ETDHASH)))!=0)
+ frem((UNIV)p);
+}
+/* SYMBOL TABLE FUNCTIONS: These functions manage hash tables that are used
+ for entities, element type definitions, IDs, and other purposes. The
+ interface will be expanded in the future to include multiple environments,
+ probably by creating arrays of the present hash tables with each table
+ in the array corresponding to an environment level.
+*/
+/* HASH: Form hash value for a string.
+ From the Dragon Book, p436.
+*/
+int hash(s, hashsize)
+UNCH *s; /* String to be hashed. */
+int hashsize; /* Size of hash table array. */
+{
+ unsigned long h = 0, g;
+
+ while (*s != 0) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ return (int)(h % hashsize);
+}
+/* HFIND: Look for a name in a hash table.
+*/
+struct hash *hfind(htab, s, h)
+struct hash *htab[]; /* Hash table. */
+UNCH *s; /* Entity name. */
+int h; /* Hash value for entity name. */
+{
+ struct hash *np;
+
+ for (np = htab[h]; np != 0; np = np->enext)
+ if (ustrcmp(s, np->ename) == 0) return np; /* Found it. */
+ return (struct hash *)0; /* Not found. */
+}
+/* HIN: Locates an entry in a hash table, or allocates a new one.
+ Returns a pointer to a structure containing a name
+ and a pointer to the next entry. Other data in the
+ structure must be maintained by the caller.
+*/
+struct hash *hin(htab, name, h, size)
+struct hash *htab[]; /* Hash table. */
+UNCH *name; /* Entity name. */
+int h; /* Hash value for entity name. */
+UNS size; /* Size of structures pointed to by table. */
+{
+ struct hash *np;
+
+ if ((np = hfind(htab, name, h))!=0) return np; /* Return if name found. */
+ /* Allocate space for structure and name. */
+ np = (struct hash *)rmalloc(size + name[0]);
+ np->ename = (UNCH *)np + size;
+ memcpy(np->ename, name, name[0]); /* Store name in it. */
+ np->enext = htab[h]; /* 1st entry is now 2nd.*/
+ htab[h] = np; /* New entry is now 1st.*/
+ return np; /* Return new entry ptr. */
+}
+/* HOUT: Remove an entry from a hash table and return its pointer.
+ The caller must free any pointers in the entry and then
+ free the entry itself if that is what is desired; this
+ routine does not free any storage.
+*/
+struct hash *hout(htab, s, h)
+struct hash *htab[]; /* Hash table. */
+UNCH *s; /* Search argument entry name. */
+int h; /* Hash value for search entry name. */
+{
+ struct hash **pp;
+
+ for (pp = &htab[h]; *pp != 0; pp = &(*pp)->enext)
+ if (ustrcmp(s, (*pp)->ename) == 0) { /* Found it. */
+ struct hash *tem = *pp;
+ *pp = (*pp)->enext;
+ return tem;
+ }
+ return 0; /* NULL if not found; else ptr. */
+}
+/* SAVESTR: Save a null-terminated string
+*/
+UNCH *savestr(s)
+UNCH *s;
+{
+ UNCH *rp;
+
+ rp = (UNCH *)rmalloc(ustrlen(s) + 1);
+ ustrcpy(rp, s);
+ return rp;
+}
+/* SAVENM: Save a name (with length and EOS)
+*/
+UNCH *savenm(s)
+UNCH *s;
+{
+ UNCH *p;
+ p = (UNCH *)rmalloc(*s);
+ memcpy(p, s, *s);
+ return p;
+}
+/* REPLACE: Free the storage for the old string (p) and store the new (s).
+ If the specified ptr is NULL, don't free it.
+*/
+UNCH *replace(p, s)
+UNCH *p;
+UNCH *s;
+{
+ if (p) frem((UNIV)p); /* Free old storage (if any). */
+ if (!s) return(s); /* Return NULL if new string is NULL. */
+ return savestr(s);
+}
+/* RMALLOC: Interface to memory allocation with error handling.
+ If storage is not available, fatal error message is issued.
+ Storage is initialized to zeros.
+*/
+UNIV rmalloc(size)
+unsigned size; /* Number of bytes of initialized storage. */
+{
+ UNIV p = (UNIV)calloc(size, 1);
+ if (!p) exiterr(33, (struct parse *)0);
+ return p;
+}
+UNIV rrealloc(p, n)
+UNIV p;
+UNS n;
+{
+ UNIV r = realloc(p, n);
+ if (!r)
+ exiterr(33, (struct parse *)0);
+ return r;
+}
+
+UNCH *pt;
+/* FREM: Free specified memory area gotten with rmalloc().
+*/
+VOID frem(ptr)
+UNIV ptr; /* Memory area to be freed. */
+{
+ free(ptr);
+}
+/* MAPSRCH: Find a string in a table and return its associated value.
+ The last entry must be a dummy consisting of a NULL pointer for
+ the string and whatever return code is desired if the
+ string is not found in the table.
+*/
+int mapsrch(maptab, name)
+struct map maptab[];
+UNCH *name;
+{
+ int i = 0;
+
+ do {
+ UNCH *mapnm, *nm;
+ for (mapnm = maptab[i].mapnm, nm=name; *nm==*mapnm; mapnm++) {
+ if (!*nm++) return maptab[i].mapdata;
+ }
+ } while (maptab[++i].mapnm);
+ return maptab[i].mapdata;
+}
+/* IDDEF: Define an ID control block; return -1 if it already exists.
+*/
+int iddef(iname)
+UNCH *iname; /* ID name (with length and EOS). */
+{
+ PID p;
+ struct fwdref *r;
+
+ p = (PID)hin((THASH)itab, iname, hash(iname, IDHASH), IDSZ);
+ if (p->iddefed) return(-1);
+ p->iddefed = 1;
+ TRACEID("IDDEF", p);
+ /* Delete any forward references. */
+ r = p->idrl;
+ p->idrl = 0;
+ while (r) {
+ struct fwdref *tem = r->next;
+ if (r->msg)
+ msgsfree(r->msg);
+ frem((UNIV)r);
+ r = tem;
+ }
+ return(0);
+}
+/* IDREF: Store a reference to an ID and define the ID if it doesn't yet exist.
+ Return 0 if already defined, otherwise pointer to a fwdref.
+*/
+struct fwdref *idref(iname)
+UNCH *iname; /* ID name (with length and EOS). */
+{
+ PID p;
+ int hnum;
+ struct fwdref *rp;
+
+ if ((p = (PID)hfind((THASH)itab, iname, (hnum = hash(iname, IDHASH))))==0)
+ p = (PID)hin((THASH)itab, iname, hnum, IDSZ);
+ if (p->iddefed)
+ return 0;
+ rp = (struct fwdref *)rmalloc(FWDREFSZ);
+ rp->next = p->idrl;
+ p->idrl = rp;
+ rp->msg = 0;
+ TRACEID("IDREF", p);
+ return rp;
+}
+/* IDRCK: Check idrefs.
+*/
+VOID idrck()
+{
+ int i;
+ PID p;
+ struct fwdref *r;
+
+ for (i = 0; i < IDHASH; i++)
+ for (p = itab[i]; p; p = p->idnext)
+ if (!p->iddefed)
+ for (r = p->idrl; r; r = r->next)
+ svderr(r->msg);
+}
+/* NTOA: Converts a positive integer to an ASCII string (abuf)
+ No leading zeros are generated.
+*/
+UNCH *ntoa(i)
+int i;
+{
+ static UNCH buf[1 + 3*sizeof(int) + 1];
+ sprintf((char *)buf, "%d", i);
+ return buf;
+}
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgml1.c b/usr.bin/sgmls/sgmls/sgml1.c
new file mode 100644
index 0000000..a2808f4
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgml1.c
@@ -0,0 +1,477 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+
+#define ETDCON (tags[ts].tetd->etdmod->ttype) /* ETD content flags. */
+
+/* SGML: Main SGML driver routine.
+*/
+enum sgmlevent sgmlnext(rcbdafp, rcbtagp)
+struct rcbdata *rcbdafp;
+struct rcbtag *rcbtagp;
+{
+ while (prologsw && !conactsw) {
+ int oconact;
+ conact = parsepro();
+ conactsw = 0; /* Assume sgmlact() will not be skipped. */
+ switch(conact) {
+
+ case PIS_:
+ case EOD_:
+ case APP_: /* APPINFO */
+ conactsw = 1; /* We can skip sgmlact() in opening state. */
+ break;
+
+ case DAF_:
+ newetd = stagreal = ETDCDATA;
+ conact = stag(datarc = DAF_);
+ conactsw = 1; /* We can skip sgmlact() in opening state. */
+ prologsw = 0; /* End the prolog. */
+ break;
+ case DCE_:
+ case MSS_:
+ /* prcon[2].tu.thetd holds the etd for the document element. */
+ newetd = stagreal = prcon[2].tu.thetd;
+ stagmin = MINSTAG; /* This tag was minimized. */
+ /* It's an error if the start tag of the document element
+ is not minimizable. */
+ if (BITOFF(newetd->etdmin, SMO))
+ sgmlerr(226, conpcb, (UNCH *)0, (UNCH *)0);
+ oconact = conact; /* Save conact. */
+ conact = stag(0); /* Start the document element. */
+ conactsw = 1; /* conact needs processing. */
+ prologsw = 0; /* The prolog is finished. */
+ if (oconact == MSS_) {
+ if (msplevel==0) conpcb = getpcb((int)ETDCON);
+ conpcb = mdms(tbuf, conpcb); /* Parse the marked section
+ start. */
+ }
+ break;
+ default: /* STE_: not defined in SGMLACT.H. */
+ if (msplevel==0) conpcb = getpcb((int)ETDCON);
+ prologsw = 0; /* End the prolog. */
+ break;
+ }
+ }
+ for (;;) {
+ unsigned swact; /* Switch action: saved conact, new, or sgmlact.*/
+
+ if (conactsw) {
+ conactsw = 0;
+ swact = conact;
+ contersw = contersv;
+ }
+ else {
+ conact = parsecon(tbuf, conpcb);
+ swact = sgmlact((UNCH)(conact != EOD_ ? conact : LOP_));
+ }
+
+ switch (swact) {
+
+ case MD_: /* Process markup declaration. */
+ parsenm(tbuf, NAMECASE); /* Get declaration name. */
+ if (!ustrcmp(tbuf+1, key[KUSEMAP])) mdsrmuse(tbuf);
+ else sgmlerr(E_MDNAME, conpcb, tbuf+1, (UNCH *)0);
+ continue;
+ case MDC_: /* Process markup declaration comment. */
+ if (*FPOS!=lex.d.mdc)
+ parsemd(tbuf, NAMECASE, (struct parse *)0, NAMELEN);
+ continue;
+
+ case MSS_: /* Process marked section start. */
+ conpcb = mdms(tbuf, conpcb);
+ continue;
+ case MSE_: /* Process marked section end (drop to LOP_). */
+ if (mdmse()) conpcb = getpcb((int)ETDCON);
+ continue;
+
+ case PIS_: /* Return processing instruction (string). */
+ if (entpisw) rcbdafp->data = data;
+ else {
+ parselit(tbuf, &pcblitc, PILEN, lex.d.pic);
+ rcbdafp->data = tbuf;
+ }
+ rcbdafp->datalen = datalen;
+ rcbdafp->contersw = entpisw;
+ entpisw = 0; /* Reset for next time.*/
+ scbset(); /* Update location in current scb. */
+ return SGMLPIS;
+
+ case APP_:
+ rcbdafp->data = tbuf;
+ rcbdafp->datalen = ustrlen(tbuf);
+ rcbdafp->contersw = 0;
+ scbset();
+ return SGMLAPP;
+ case ETG_: /* Return end-tag. */
+ charmode = 0; /* Not in char mode unless CDATA or RCDATA.*/
+ if (msplevel==0) conpcb = getpcb((int)ETDCON);
+ rcbtagp->contersw = tags[ts+1].tflags;
+ rcbtagp->tagmin = etagimsw ? MINETAG : etagmin;
+ rcbtagp->curgi = tags[ts+1].tetd->etdgi;
+ rcbtagp->ru.oldgi = tags[ts].tetd->etdgi;
+ if (etagmin==MINSTAG) rcbtagp->tagreal =
+ BADPTR(stagreal) ? stagreal : (PETD)stagreal->etdgi;
+ else rcbtagp->tagreal =
+ BADPTR(etagreal) ? etagreal : (PETD)etagreal->etdgi;
+ rcbtagp->etictr = etictr;
+ rcbtagp->srmnm = tags[ts].tsrm!=SRMNULL ? tags[ts].tsrm[0]->ename
+ : 0;
+ scbset(); /* Update location in current scb. */
+ return SGMLETG;
+
+ case STG_: /* Return start-tag. */
+ charmode = 0; /* Not in char mode unless CDATA or RCDATA.*/
+ if (!conrefsw && msplevel==0) conpcb = getpcb((int)ETDCON);
+ rcbtagp->contersw = tags[ts].tflags;
+ rcbtagp->tagmin = dostag ? MINSTAG : stagmin;
+ rcbtagp->curgi = tags[ts].tetd->etdgi;
+ /* Get attribute list if one was defined for this element. */
+ rcbtagp->ru.al = !tags[ts].tetd->adl ? 0 :
+ rcbtagp->tagmin==MINNONE ? al : tags[ts].tetd->adl;
+ rcbtagp->tagreal = BADPTR(stagreal)?stagreal:(PETD)stagreal->etdgi;
+ rcbtagp->etictr = etictr;
+ rcbtagp->srmnm = tags[ts].tsrm!=SRMNULL ? tags[ts].tsrm[0]->ename
+ : 0;
+ scbset(); /* Update location in current scb. */
+ return SGMLSTG;
+
+ case DAF_: /* Return data in source entity buffer. */
+ charmode = 1;
+ rcbdafp->datalen = datalen;
+ rcbdafp->data = data;
+ rcbdafp->contersw = contersw | entdatsw;
+ contersw = entdatsw = 0;/* Reset for next time.*/
+ scbset(); /* Update location in current scb. */
+ return SGMLDAF;
+
+ case CON_: /* Process conact after returning REF_. */
+ conactsw = 1;
+ contersv = contersw;
+ case REF_: /* Return RE found. */
+ if (badresw) {
+ badresw = 0;
+ sgmlerr(E_CHARS, &pcbconm, tags[ts].tetd->etdgi+1, (UNCH *)0);
+ continue;
+ }
+ charmode = 1;
+ rcbdafp->contersw = contersw;
+ contersw = 0; /* Reset for next time.*/
+ scbset(); /* Update location in current scb. */
+ return SGMLREF;
+
+ case EOD_: /* End of source document entity. */
+ if (mslevel != 0) sgmlerr(139, conpcb, (UNCH *)0, (UNCH *)0);
+ idrck(); /* Check idrefs. */
+ scbset(); /* Update location in current scb. */
+ return SGMLEOD;
+
+ default: /* LOP_: Loop again with no action. */
+ continue;
+ }
+ }
+}
+/* PCBSGML: State and action table for action codes returned to text processor
+ by SGML.C.
+ Columns are based on SGMLACT.H values minus DAF_, except that end
+ of document has input code LOP_, regardless of its action code.
+*/
+/* Symbols for state names (end with a number). */
+#define ST1 0 /* Just had a start tag. */
+#define NR1 2 /* Just had an RS or RE. */
+#define DA1 4 /* Just had some data. */
+#define NR2 6 /* Just had an RE; RE pending. */
+#define ST2 8 /* Had only markup since last RE/RS; RE pending. */
+
+static UNCH sgmltab[][11] = {
+/*daf_ etg_ md_ mdc_ mss_ mse_ pis_ ref_ stg_ rsr_ eod */
+ {DA1 ,DA1 ,ST1 ,ST1 ,ST1 ,ST1 ,ST1 ,NR1 ,ST1 ,NR1 ,ST1 },/*st1*/
+ {DAF_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,LOP_,STG_,LOP_,EOD_},
+
+ {DA1 ,DA1 ,ST1 ,ST1 ,ST1 ,ST1 ,ST1 ,NR2 ,ST1 ,NR1 ,ST1 },/*nr1*/
+ {DAF_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,LOP_,STG_,LOP_,EOD_},
+
+ {DA1 ,DA1 ,DA1 ,DA1 ,DA1 ,DA1 ,DA1 ,NR2 ,ST1 ,NR1 ,ST1 },/*da1*/
+ {DAF_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,LOP_,STG_,LOP_,EOD_},
+
+ {DA1 ,DA1 ,ST2 ,ST2 ,ST2 ,ST2 ,ST2 ,NR2 ,ST1 ,NR2 ,ST1 },/*nr2*/
+ {CON_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,REF_,CON_,LOP_,EOD_},
+
+ {DA1 ,DA1 ,ST2 ,ST2 ,ST2 ,ST2 ,ST2 ,NR1 ,ST1 ,NR2 ,ST1 },/*st2*/
+ {CON_,ETG_,MD_ ,MDC_,MSS_,MSE_,PIS_,REF_,CON_,LOP_,EOD_},
+};
+int scbsgmst = ST1; /* SCBSGML: trailing stag or markup; ignore RE. */
+int scbsgmnr = NR1; /* SCBSGML: new record; do not ignore RE. */
+/* SGMLACT: Determine action to be taken by SGML.C based on current state and
+ specified input.
+ For start or end of a plus exception element, push or pop the
+ pcbsgml stack.
+ Return to caller with action code.
+*/
+#ifdef USE_PROTOTYPES
+int sgmlact(UNCH conret)
+#else
+int sgmlact(conret)
+UNCH conret; /* Action returned to SGML.C by content parse. */
+#endif
+{
+ int action;
+
+ if (conret==STG_ && GET(tags[ts].tflags, TAGPEX))
+ {++pss; scbsgml[pss].snext = ST1;}
+ scbsgml[pss].sstate = scbsgml[pss].snext;
+ scbsgml[pss].snext = sgmltab[scbsgml[pss].sstate]
+ [scbsgml[pss].sinput = conret-DAF_];
+ scbsgml[pss].saction = sgmltab[scbsgml[pss].sstate+1][scbsgml[pss].sinput];
+ TRACEGML(scbsgml, pss, conactsw, conact);
+ action = scbsgml[pss].saction;
+ if (conret==ETG_ && GET(tags[ts+1].tflags, TAGPEX)) {
+ pss--;
+ /* An included subelement affects the enclosing state like a
+ processing instruction (or MDC_ or MD_),
+ that is to say NR1 is changed to ST1 and NR2 to ST2. */
+ scbsgml[pss].sstate = scbsgml[pss].snext;
+ scbsgml[pss].snext = sgmltab[scbsgml[pss].sstate][PIS_ - DAF_];
+ }
+ return action;
+}
+/* GETPCB: Choose pcb for new or resumed element.
+*/
+struct parse *getpcb(etdcon)
+int etdcon; /* Content type of new or resumed element. */
+{
+ if (BITON(etdcon, MGI)) {
+ return(BITON(etdcon, MCHARS) ? &pcbconm : &pcbcone);
+ }
+ if (BITON(etdcon, MCDATA) || BITON(etdcon, MRCDATA)) {
+ charmode = 1;
+ return(BITON(etdcon, MCDATA) ? &pcbconc : (rcessv = es, &pcbconr));
+ }
+ return(&pcbconm);
+}
+
+struct markup *sgmlset(swp)
+struct switches *swp;
+{
+ /* Initialize variables based on switches structure members. */
+ sw = *swp;
+ rbufs = (UNCH *)rmalloc((UNS)3+sw.swbufsz) + 3; /* DOS file read area. */
+ TRACEPRO(); /* Set trace switches for prolog. */
+ msginit(swp);
+ ioinit(swp);
+ sdinit();
+ return &lex.m;
+}
+
+/* Points for each capacity, indexed by *CAP in sgmldecl.h. We'll replace
+2 with the real NAMELEN at run time. */
+
+static UNCH cappoints[] = {
+ 1,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2
+};
+
+static long capnumber[NCAPACITY];
+static long maxsubcap[NCAPACITY];
+
+VOID sgmlend(p)
+struct sgmlcap *p;
+{
+ int i;
+ for (; es >= 0; --es)
+ if (FILESW)
+ fileclos();
+
+ capnumber[NOTCAP] = ds.dcncnt;
+ capnumber[EXGRPCAP] = ds.pmexgcnt;
+ capnumber[ELEMCAP] = ds.etdcnt+ds.etdercnt;
+ capnumber[EXNMCAP] = ds.pmexcnt;
+ capnumber[GRPCAP] = ds.modcnt;
+ capnumber[ATTCAP] = ds.attcnt;
+ capnumber[ATTCHCAP] = ds.attdef;
+ capnumber[AVGRPCAP] = ds.attgcnt;
+ capnumber[IDCAP] = ds.idcnt;
+ capnumber[IDREFCAP] = ds.idrcnt;
+ capnumber[ENTCAP] = ds.ecbcnt;
+ capnumber[ENTCHCAP] = ds.ecbtext;
+ capnumber[MAPCAP] = ds.srcnt + ds.srcnt*lex.s.dtb[0].mapdata;
+ capnumber[NOTCHCAP] = ds.dcntext;
+
+ capnumber[TOTALCAP] = 0;
+
+ for (i = 1; i < NCAPACITY; i++) {
+ if (cappoints[i] > 1)
+ cappoints[i] = NAMELEN;
+ capnumber[i] += maxsubcap[i]/cappoints[i];
+ capnumber[TOTALCAP] += (long)capnumber[i] * cappoints[i];
+ }
+ p->number = capnumber;
+ p->points = cappoints;
+ p->limit = sd.capacity;
+ p->name = captab;
+
+ for (i = 0; i < NCAPACITY; i++) {
+ long excess = capnumber[i]*cappoints[i] - sd.capacity[i];
+ if (excess > 0) {
+ char buf[sizeof(long)*3 + 1];
+ sprintf(buf, "%ld", excess);
+ sgmlerr(162, (struct parse *)0,
+ (UNCH *)captab[i], (UNCH *)buf);
+ }
+ }
+}
+
+VOID sgmlsubcap(v)
+long *v;
+{
+ int i;
+ for (i = 0; i < NCAPACITY; i++)
+ if (v[i] > maxsubcap[i])
+ maxsubcap[i] = v[i];
+}
+
+int sgmlsdoc(ptr)
+UNIV ptr;
+{
+ struct entity *e;
+ union etext etx;
+ etx.x = ptr;
+
+ e = entdef(indocent, ESF, &etx);
+ if (!e)
+ return -1;
+ return entopen(e);
+}
+
+/* SGMLGENT: Get a data entity.
+ Returns:
+ -1 if the entity does not exist
+ -2 if it is not a data entity
+ 1 if it is an external entity
+ 2 if it is an internal cdata entity
+ 3 if it is an internal sdata entity
+*/
+int sgmlgent(iname, np, tp)
+UNCH *iname;
+PNE *np;
+UNCH **tp;
+{
+ PECB ep; /* Pointer to an entity control block. */
+
+ ep = entfind(iname);
+ if (!ep)
+ return -1;
+ switch (ep->estore) {
+ case ESN:
+ if (np)
+ *np = ep->etx.n;
+ return 1;
+ case ESC:
+ if (tp)
+ *tp = ep->etx.c;
+ return 2;
+ case ESX:
+ if (tp)
+ *tp = ep->etx.c;
+ return 3;
+ }
+ return -2;
+}
+
+/* Mark an entity. */
+
+int sgmlment(iname)
+UNCH *iname;
+{
+ PECB ep;
+ int rc;
+
+ ep = entfind(iname);
+ if (!ep)
+ return -1;
+ rc = ep->mark;
+ ep->mark = 1;
+ return rc;
+}
+
+int sgmlgcnterr()
+{
+ return msgcnterr();
+}
+
+/* This is for error handling functions that want to print a gi backtrace. */
+
+UNCH *getgi(i)
+int i;
+{
+ return i >= 0 && i <= ts ? tags[i].tetd->etdgi + 1 : NULL;
+}
+
+/* Returns the value of prologsw for the use by error handling functions. */
+
+int inprolog()
+{
+ return prologsw;
+}
+
+/* Used by the error handling functions to access scbs. */
+
+int getlocation(level, locp)
+int level;
+struct location *locp;
+{
+ if (level < 0 || level > es)
+ return 0;
+ if (locp) {
+ int es = level;
+ /* source macros access a variable called `es' */
+
+ locp->filesw = FILESW;
+ locp->rcnt = RCNT;
+ locp->ccnt = CCNT;
+ locp->ename = ENTITY + 1;
+ locp->fcb = SCBFCB;
+ locp->curchar = CC;
+ locp->nextchar = NEXTC;
+ }
+ return 1;
+}
+
+int sgmlloc(linenop, filenamep)
+unsigned long *linenop;
+char **filenamep;
+{
+ int level = es;
+ int es;
+
+ for (es = level; es >= 0 && !FILESW; es--)
+ ;
+ if (es < 0)
+ return 0;
+ *linenop = RCNT;
+ *filenamep = ioflid(SCBFCB);
+ return 1;
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgml2.c b/usr.bin/sgmls/sgmls/sgml2.c
new file mode 100644
index 0000000..83bccbd
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgml2.c
@@ -0,0 +1,499 @@
+/* Added exiterr() for terminal errors to prevent SGML.MSG errors. */
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+static int iorc; /* Return code from io* functions */
+/* ENTDEF: Process an entity definition and return the pointer to it.
+ The entity text must be in permanent storage.
+ There is no checking to see if the entity already exists;
+ the caller must have done that.
+*/
+#ifdef USE_PROTOTYPES
+PECB entdef(UNCH *ename, UNCH estore, union etext *petx)
+#else
+PECB entdef(ename, estore, petx)
+UNCH *ename; /* Entity name (with length and EOS). */
+UNCH estore; /* Entity storage class. */
+union etext *petx; /* Ptr to entity text union. */
+#endif
+{
+ PECB p;
+
+ p = (PECB)hin((THASH)etab, ename, hash(ename, ENTHASH), ENTSZ);
+ memcpy((UNIV)&p->etx, (UNIV)petx, ETEXTSZ);
+ p->estore = estore;
+ TRACEECB("ENTDEF", p);
+ return(p);
+}
+/* ENTFIND: If an entity exists, return ptr to its ecb.
+ Return NULL if it is not defined.
+*/
+PECB entfind(ename)
+UNCH *ename; /* Entity name (with length and EOS). */
+{
+ PECB p;
+
+ p = (PECB)hfind((THASH)etab, ename, hash(ename, ENTHASH));
+ TRACEECB("ENTFIND", p);
+ return p;
+}
+/* ENTREF: Process a general or parameter entity reference.
+ If the entity is defined it returns the return code from ENTOPEN.
+ It returns ENTUNDEF for undefined parameter entity references
+ and for general entity references when defaulting is not allowed.
+ Otherwise, it uses the default entity text.
+*/
+int entref(ename)
+UNCH *ename; /* Entity name (with length and EOS). */
+{
+ PECB ecb; /* Entity control block. */
+
+ /* Get the entity control block, if the entity has been defined. */
+ if ((ecb = (PECB)hfind((THASH)etab, ename, hash(ename, ENTHASH)))==0
+ || ecb->estore == 0) {
+ if ( ename[1]==lex.d.pero
+ || ecbdeflt==0
+ || (ecb = usedef(ename))==0 ) {
+ sgmlerr(ename[1] == lex.d.pero || ecbdeflt == 0 ? 35 : 150,
+ (struct parse *)0, ename+1, (UNCH *)0);
+ return(ENTUNDEF);
+ }
+ }
+ return(entopen(ecb));
+}
+/* ENTOPEN: Open a newly referenced entity.
+ Increment the stack pointer (es) and initialize the new entry.
+ ENTDATA if entity is CDATA or SDATA, ENTPI if it is PI,
+ 0 if normal and all o.k.; <0 if not.
+*/
+int entopen(ecb)
+struct entity *ecb; /* Entity control block. */
+{
+ int i; /* Loop counter. */
+
+ /* See if we have exceeded the entity nesting level. */
+ if (es>=ENTLVL) {
+ sgmlerr(34, (struct parse *)0, ecb->ename+1, ntoa(ENTLVL));
+ return(ENTMAX);
+ }
+ /* If entity is an etd, pi, or data, return it without creating an scb. */
+ switch (ecb->estore) {
+ case ESN:
+ if (NEXTYPE(ecb->etx.n)!=ESNSUB) {
+ if (!NEDCNDEFINED(ecb->etx.n))
+ sgmlerr(78, (struct parse *)0, NEDCN(ecb->etx.n)+1,
+ ecb->ename+1);
+ }
+ else {
+#if 0
+ if (!NEID(ecb->etx.n)) {
+ sgmlerr(149, (struct parse *)0, ecb->ename + 1, (UNCH *)0);
+ return ENTFILE;
+ }
+#endif
+ if (sw.nopen >= sd.subdoc)
+ sgmlerr(188, (struct parse *)0,
+ (UNCH *)NULL, (UNCH *)NULL);
+ }
+ data = (UNCH *)ecb->etx.n;
+ entdatsw = NDECONT;
+ return(ENTDATA);
+ case ESC:
+ case ESX:
+ datalen = ustrlen(ecb->etx.c);
+ data = ecb->etx.c;
+ entdatsw = (ecb->estore==ESC) ? CDECONT : SDECONT;
+ return(ENTDATA);
+ case ESI:
+ datalen = ustrlen(ecb->etx.c);
+ data = ecb->etx.c;
+ entpisw = 4;
+ return(ENTPI);
+ }
+ /* If the same entity is already open, send msg and ignore it.
+ Level 0 needn't be tested, as its entity name is always *DOC.
+ */
+ for (i = 0; ++i<=es;) if (scbs[i].ecb.enext==ecb) {
+ sgmlerr(36, (struct parse *)0, ecb->ename+1, (UNCH *)0);
+ return(ENTLOOP);
+ }
+ /* Update SCB if entity trace is wanted in messages or entity is a file.
+ (Avoid this at start when es==-1 or memory will be corrupted.)
+ */
+ if (es >= 0 && (sw.swenttr || FILESW)) scbset();
+
+ /* Stack the new source control block (we know there is room). */
+ ++es; /* Increment scbs index. */
+ RCNT = CCO = RSCC = 0; /* No records or chars yet. */
+ COPIEDSW = 0;
+ memcpy((UNIV)&ECB, (UNIV)ecb, (UNS)ENTSZ); /* Copy the ecb into the scb. */
+ ECBPTR = ecb; /* Save the ecb pointer in scb.ecb.enext. */
+ TRACEECB("ENTOPEN", ECBPTR);
+
+ /* For memory entities, the read buffer is the entity text.
+ The text starts at FBUF, so FPOS should be FBUF-1
+ because it is bumped before each character is read.
+ */
+ if (ECB.estore<ESFM) {FPOS = (FBUF = ECB.etx.c)-1; return 0;}
+
+ /* For file entities, suspend any open file and do first read. */
+ if (ECB.etx.x == 0) {
+ --es;
+ switch (ecb->estore) {
+ case ESF:
+ sgmlerr(149, (struct parse *)0, ecb->ename + 1, (UNCH *)0);
+ break;
+ case ESP:
+ sgmlerr(229, (struct parse *)0, ecb->ename + 2, (UNCH *)0);
+ break;
+ default:
+ abort();
+ }
+ return ENTFILE;
+ }
+ fileopen(); /* Open new external file. */
+ if (iorc<0) { /* If open not successful: */
+ FPOS = FBUF-1; /* Clean CCNT for OPEN error msg.*/
+ filerr(32, ecb->ename+1);
+ --es; /* Pop the stack. */
+ return(ENTFILE);
+ }
+ filepend(es); /* Suspend any open file. */
+ fileread(); /* First read of file must be ok.*/
+ return 0;
+}
+/* ENTGET: Get next record of entity (if there is one).
+ Otherwise, close the file (if entity is a file) and
+ pop the entity stack. If nothing else is on the stack,
+ return -1 to advise the caller.
+*/
+int entget()
+{
+ RSCC += (CCO = FPOS-FBUF);
+ /* Characters-in-record (ignore EOB/EOF). */
+ tagctr += CCO; /* Update tag length counter. */
+ switch (*FPOS) {
+ case EOBCHAR: /* End of file buffer: refill it. */
+ rbufs[-2] = FPOS[-2];
+ rbufs[-1] = FPOS[-1];
+ fileread(); /* Read the file. */
+ if (iorc > 0) break;
+ readerr:
+ filerr(31, ENTITY+1); /* Treat error as EOF. */
+ case EOFCHAR: /* End of file: close it. */
+ fileclos(); /* Call SGMLIO to close file. */
+ conterr:
+ if (es==0) { /* Report if it is primary file. */
+ FPOS = FBUF-1; /* Preserve CCNT for omitted end-tags. */
+ return -1;
+ }
+ case EOS: /* End of memory entity: pop the stack. */
+ TRACEECB("ENTPOP", ECBPTR);
+ if (COPIEDSW) {
+ frem((UNIV)(FBUF + 1));
+ COPIEDSW = 0;
+ }
+ --es; /* Pop the SCB stack. */
+ if (FBUF) break; /* Not a PEND file. */
+ filecont(); /* Resume previous file. */
+ if (iorc<0) { /* If CONT not successful: */
+ filerr(94, ENTITY+1);
+ goto conterr;
+ }
+ fileread(); /* Read the file. */
+ if (iorc<=0) goto readerr; /* If READ not successful: */
+ rbufs[-1] = SCB.pushback;
+ FPOS += CCO;
+ CCO = 0;
+ if (delmscsw && es==0) { /* End of DTD. */
+ delmscsw = 0;
+ *rbufs = lex.d.msc;
+ }
+ break;
+ }
+ return 0;
+}
+/* USEDEF: Use the default value for an entity reference.
+ Returns the ECB for the defaulted entity.
+*/
+PECB usedef(ename)
+UNCH *ename; /* Entity name (with length and EOS). */
+{
+ union etext etx; /* Save return from entgen. */
+ PECB ecb; /* Entity control block. */
+ PNE pne = 0; /* Ptr to NDATA entity control block. */
+ UNCH estore; /* Default entity storage type. */
+
+ if ((estore = ecbdeflt->estore)<ESFM) /* Default is an internal string. */
+ etx.c = ecbdeflt->etx.c;
+ else {
+ /* Move entity name into fpi. */
+ fpidf.fpinm = ename + 1;
+ if ((etx.x = entgen(&fpidf))==0) return (PECB)0;
+ if (estore==ESN) {
+ memcpy((UNIV)(pne=(PNE)rmalloc((UNS)NESZ)),(UNIV)ecbdeflt->etx.n,(UNS)NESZ);
+ NEID(pne) = etx.x;
+ etx.n = pne;
+ }
+ }
+ if (sw.swrefmsg) sgmlerr(45, (struct parse *)0, ename+1, (UNCH *)0);
+ ++ds.ecbcnt;
+ ecb = entdef(ename, estore, &etx);
+ ecb->dflt = 1;
+ if (pne) NEENAME(pne) = ecb->ename;
+ return(ecb);
+}
+/* SCBSET: Set source control block to current location in the current entity.
+ This routine is called by SGML when it returns to the text
+ processor and by ERROR when it reports an error.
+*/
+VOID scbset()
+{
+ if (es >= 0 && FBUF) {
+ CC = *FPOS;
+ if (*FPOS == DELNONCH)
+ NEXTC = FPOS[1];
+ else
+ NEXTC = 0;
+ CCO = FPOS + 1 - FBUF;
+ }
+}
+/* FILEOPEN: Call IOOPEN to open an external entity (file).
+*/
+VOID fileopen() /* Open an external entity's file. */
+{
+ iorc = ioopen(ECB.etx.x, &SCBFCB);
+}
+/* FILEREAD: Call IOREAD to read an open external entity (file).
+*/
+VOID fileread() /* Read the current external entity's file. */
+{
+ int newfile;
+ iorc = ioread(SCBFCB, rbufs, &newfile);
+ FPOS = (FBUF = rbufs) - 1; /* Actual read buffer. */
+ if (newfile) RCNT = 0;
+}
+/* FILEPEND: Call IOPEND to close an open external entity (file) temporarily.
+*/
+VOID filepend(es) /* Close the current external entity's file. */
+int es; /* Local index to scbs. */
+{
+ while (--es>=0) { /* Find last external file on stack. */
+ int off;
+ if (!FILESW) continue; /* Not an external file. */
+ if (!FBUF) continue; /* Already suspended. */
+ off = CCO;
+ assert(off >= -1);
+ if (off < 0) off = 0;
+ else CCO = 0;
+ FPOS -= CCO;
+ SCB.pushback = FPOS[-1];
+ FBUF = 0; /* Indicate pending file. */
+ RSCC += off; /* Update characters-in-record counter. */
+ tagctr += off; /* Update tag length counter. */
+ iopend(SCBFCB, off, rbufs);
+ return;
+ }
+}
+/* FILECONT: Call IOCONT to reopen an external entity (file).
+*/
+VOID filecont() /* Open an external entity's file. */
+{
+ iorc = iocont(SCBFCB);
+}
+/* FILECLOS: Call IOCLOSE to close an open external entity (file).
+*/
+VOID fileclos() /* Close the current external entity's file. */
+{
+ if (!SCBFCB)
+ return;
+ ioclose(SCBFCB);
+ /* The fcb will have been freed by sgmlio.
+ Make sure we don't access it again. */
+ SCBFCB = NULL;
+}
+/* ERROR: Interface to text processor SGML I/O services for error handling.
+*/
+VOID error(e)
+struct error *e;
+{
+ scbset(); /* Update location in source control block. */
+ msgprint(e);
+}
+/* PTRSRCH: Find a pointer in a list and return its index.
+ Search key must be on list as there is no limit test.
+ This routine is internal only -- not for user data.
+*/
+UNIV mdnmtab[] = {
+ (UNIV)key[KATTLIST],
+ (UNIV)key[KDOCTYPE],
+ (UNIV)key[KELEMENT],
+ (UNIV)key[KENTITY],
+ (UNIV)key[KLINKTYPE],
+ (UNIV)key[KLINK],
+ (UNIV)key[KNOTATION],
+ (UNIV)sgmlkey,
+ (UNIV)key[KSHORTREF],
+ (UNIV)key[KUSELINK],
+ (UNIV)key[KUSEMAP]
+};
+UNIV pcbtab[] = {
+ (UNIV)&pcbconc,
+ (UNIV)&pcbcone,
+ (UNIV)&pcbconm,
+ (UNIV)&pcbconr,
+ (UNIV)&pcbetag,
+ (UNIV)&pcbgrcm,
+ (UNIV)&pcbgrcs,
+ (UNIV)&pcbgrnm,
+ (UNIV)&pcbgrnt,
+ (UNIV)&pcblitc,
+ (UNIV)&pcblitp,
+ (UNIV)&pcblitr,
+ (UNIV)&pcblitt,
+ (UNIV)&pcblitv,
+ (UNIV)&pcbmd,
+ (UNIV)&pcbmdc,
+ (UNIV)&pcbmdi,
+ (UNIV)&pcbmds,
+ (UNIV)&pcbmsc,
+ (UNIV)&pcbmsi,
+ (UNIV)&pcbmsrc,
+ (UNIV)&pcbpro,
+ (UNIV)&pcbref,
+ (UNIV)&pcbstag,
+ (UNIV)&pcbval,
+ (UNIV)&pcbeal,
+ (UNIV)&pcbsd,
+};
+UNS ptrsrch(ptrtab, ptr)
+UNIV ptrtab[];
+UNIV ptr;
+{
+ UNS i;
+
+ for (i = 0; ; ++i)
+ if (ptrtab[i] == ptr)
+ break;
+ return i;
+}
+/* MDERR: Process errors for markup declarations.
+ Prepare the special parameters that only exist for
+ markup declaration errors.
+*/
+VOID mderr(number, parm1, parm2)
+UNS number; /* Error number. */
+UNCH *parm1; /* Additional parameters (or NULL). */
+UNCH *parm2; /* Additional parameters (or NULL). */
+{
+ struct error err;
+ errorinit(&err, subdcl ? MDERR : MDERR2, number);
+ err.parmno = parmno;
+ err.subdcl = subdcl;
+ err.eparm[0] = (UNIV)parm1;
+ err.eparm[1] = (UNIV)parm2;
+ err.errsp = (sizeof(pcbtab)/sizeof(pcbtab[0])) + ptrsrch(mdnmtab,
+ (UNIV)mdname);
+ error(&err);
+}
+/* SGMLERR: Process errors for SGML parser.
+*/
+VOID sgmlerr(number, pcb, parm1, parm2)
+UNS number; /* Error number. */
+struct parse *pcb; /* Current parse control block. */
+UNCH *parm1; /* Error message parameters. */
+UNCH *parm2; /* Error message parameters. */
+{
+ struct error err;
+ errorinit(&err, DOCERR, number);
+ if (!pcb) pcb = prologsw ? propcb : conpcb;
+ err.errsp = ptrsrch(pcbtab, (UNIV)pcb);
+ err.eparm[0] = (UNIV)parm1;
+ err.eparm[1] = (UNIV)parm2;
+ error(&err);
+}
+/* SAVERR: Save an error for possible later use.
+*/
+UNIV saverr(number, pcb, parm1, parm2)
+UNS number; /* Error number. */
+struct parse *pcb; /* Current parse control block. */
+UNCH *parm1; /* Error message parameters. */
+UNCH *parm2; /* Error message parameters. */
+{
+ struct error err;
+ errorinit(&err, DOCERR, number);
+ if (!pcb) pcb = prologsw ? propcb : conpcb;
+ err.errsp = ptrsrch(pcbtab, (UNIV)pcb);
+ err.eparm[0] = (UNIV)parm1;
+ err.eparm[1] = (UNIV)parm2;
+ scbset();
+ return msgsave(&err);
+}
+/* SVDERR: Print a saved error.
+*/
+VOID svderr(p)
+UNIV p;
+{
+ msgsprint(p);
+}
+/* EXITERR: Process terminal errors for SGML parser.
+*/
+VOID exiterr(number, pcb)
+UNS number; /* Error number. */
+struct parse *pcb; /* Current parse control block. */
+{
+ struct error err;
+ errorinit(&err, EXITERR, number);
+ if (!pcb) pcb = prologsw ? propcb : conpcb;
+ err.errsp = ptrsrch(pcbtab, (UNIV)pcb);
+ error(&err);
+ /* The error handler should have exited. */
+ abort();
+}
+/* SYNERR: Process syntax errors for SGML parser.
+*/
+VOID synerr(number, pcb)
+UNS number; /* Error number. */
+struct parse *pcb; /* Current parse control block. */
+{
+ struct error err;
+ errorinit(&err, DOCERR, number);
+ err.errsp = ptrsrch(pcbtab, (UNIV)pcb);
+ error(&err);
+}
+/* FILERR: Process a file access error.
+*/
+VOID filerr(number, parm)
+UNS number;
+UNCH *parm;
+{
+ struct error err;
+ errorinit(&err, FILERR, number);
+ err.eparm[0] = (UNIV)parm;
+ err.sverrno = errno;
+ error(&err);
+}
+/* ERRORINIT: Constructor for struct error.
+*/
+VOID errorinit(e, type, number)
+struct error *e;
+UNS type;
+UNS number;
+{
+ int i;
+ e->errtype = type;
+ e->errnum = number;
+ e->errsp = 0;
+ for (i = 0; i < MAXARGS; i++)
+ e->eparm[i] = 0;
+ e->parmno = 0;
+ e->subdcl = 0;
+}
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgmlaux.h b/usr.bin/sgmls/sgmls/sgmlaux.h
new file mode 100644
index 0000000..f87ac8b
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlaux.h
@@ -0,0 +1,70 @@
+/* This file controls the interface between the parser core and the auxiliary
+functions in entgen.c, sgmlio.c, and sgmlmsg.c */
+
+#include "std.h"
+#include "entity.h"
+#include "sgmldecl.h"
+
+/* Error types (ERRTYPE) for calls to error-handling services
+ performed for SGML by the text processor (SGMLIO).
+ NOTE: Strings in these blocks have no lengths, but cannot exceed
+ NAMELEN (plus 1 more byte for the zero terminator).
+*/
+#define FILERR 0 /* Error: file access. */
+#define DOCERR 1 /* Error: in document markup. */
+#define MDERR 2 /* Error: in markup declaration with subdcl. */
+#define MDERR2 3 /* Error: in markup declaration with no subdcl. */
+#define EXITERR 4 /* Error: terminal error in document markup. */
+/* Quantities affecting error messages and their arguments.
+*/
+#define MAXARGS 2 /* Maximum number of arguments in a msg. */
+
+/* NOTE: Error handler must return, or next call to SGML must be RSET or END,
+ except for EXITERR errors which must not return.
+*/
+struct error { /* IPB for error messages. */
+ UNS errtype; /* Type of error: DOC, MD, MD2, FIL. */
+ UNS errnum; /* Error number. */
+ UNS errsp; /* Special parameter index in message file. */
+ int sverrno; /* Saved value of errno. */
+ int parmno; /* MDERROR: declaration parameter number. */
+ UNCH *subdcl; /* MDERROR: subject of declaration. */
+ UNIV eparm[MAXARGS]; /* Ptrs to arguments (no length, but EOS). */
+};
+
+struct location {
+ int filesw;
+ unsigned long rcnt;
+ int ccnt;
+ UNCH curchar;
+ UNCH nextchar;
+ UNCH *ename;
+ UNIV fcb;
+};
+
+int ioopen P((UNIV, UNIV*));
+VOID ioclose P((UNIV));
+int ioread P((UNIV, UNCH *, int *));
+VOID iopend P((UNIV, int, UNCH *));
+int iocont P((UNIV));
+VOID ioinit P((struct switches *));
+char *ioflid P((UNIV));
+
+UNIV entgen P((struct fpi *));
+
+VOID msgprint P((struct error *));
+VOID msginit P((struct switches *));
+UNIV msgsave P((struct error *));
+VOID msgsprint P((UNIV));
+VOID msgsfree P((UNIV));
+int msgcnterr P((void));
+
+
+int inprolog P((void));
+UNCH *getgi P((int));
+
+int getlocation P((int, struct location *));
+UNIV rmalloc P((unsigned int));
+UNIV rrealloc P((UNIV, UNS));
+VOID frem P((UNIV));
+VOID exiterr P((unsigned int,struct parse *));
diff --git a/usr.bin/sgmls/sgmls/sgmldecl.c b/usr.bin/sgmls/sgmls/sgmldecl.c
new file mode 100644
index 0000000..1ba7fe7
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmldecl.c
@@ -0,0 +1,1741 @@
+/* sgmldecl.c -
+ SGML declaration parsing.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "sgmlincl.h"
+
+/* Symbolic names for the error numbers that are be generated only by
+this module. */
+
+#define E_STANDARD 163
+#define E_SIGNIFICANT 164
+#define E_BADLIT 165
+#define E_SCOPE 166
+#define E_XNUM 167
+#define E_BADVERSION 168
+#define E_NMUNSUP 169
+#define E_XNMLIT 170
+#define E_CHARDESC 171
+#define E_CHARDUP 172
+#define E_CHARRANGE 173
+#define E_7BIT 174
+#define E_CHARMISSING 175
+#define E_SHUNNED 176
+#define E_NONSGML 177
+#define E_CAPSET 178
+#define E_CAPMISSING 179
+#define E_SYNTAX 180
+#define E_CHARNUM 181
+#define E_SWITCHES 182
+#define E_INSTANCE 183
+#define E_ZEROFEATURE 184
+#define E_YESNO 185
+#define E_CAPACITY 186
+#define E_NOTSUPPORTED 187
+#define E_FORMAL 189
+#define E_BADCLASS 190
+#define E_MUSTBENON 191
+#define E_BADBASECHAR 199
+#define E_SYNREFUNUSED 200
+#define E_SYNREFUNDESC 201
+#define E_SYNREFUNKNOWN 202
+#define E_SYNREFUNKNOWNSET 203
+#define E_FUNDUP 204
+#define E_BADFUN 205
+#define E_FUNCHAR 206
+#define E_GENDELIM 207
+#define E_SRDELIM 208
+#define E_BADKEY 209
+#define E_BADQUANTITY 210
+#define E_BADNAME 211
+#define E_REFNAME 212
+#define E_DUPNAME 213
+#define E_QUANTITY 214
+#define E_QTOOBIG 215
+#define E_NMSTRTCNT 219
+#define E_NMCHARCNT 220
+#define E_NMDUP 221
+#define E_NMBAD 222
+#define E_NMMINUS 223
+#define E_UNKNOWNSET 227
+
+#define CANON_NMC '.' /* Canonical name character. */
+#define CANON_NMS 'A' /* Canonical name start character. */
+#define CANON_MIN ':' /* Canonical minimum data character. */
+
+#define SUCCESS 1
+#define FAIL 0
+#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
+#define matches(tok, str) (ustrcmp((tok)+1, (str)) == 0)
+
+static UNCH standard[] = "ISO 8879:1986";
+
+#define REFERENCE_SYNTAX "ISO 8879:1986//SYNTAX Reference//EN"
+#define CORE_SYNTAX "ISO 8879:1986//SYNTAX Core//EN"
+
+static UNCH (*newkey)[REFNAMELEN+1] = 0;
+
+struct pmap {
+ char *name;
+ UNIV value;
+};
+
+/* The reference capacity set. */
+#define REFCAPSET \
+{ 35000L, 35000L, 35000L, 35000L, 35000L, 35000L, 35000L, 35000L, 35000L, \
+35000L, 35000L, 35000L, 35000L, 35000L, 35000L, 35000L, 35000L }
+
+long refcapset[NCAPACITY] = REFCAPSET;
+
+/* A pmap of known capacity sets. */
+
+static struct pmap capset_map[] = {
+ { "ISO 8879:1986//CAPACITY Reference//EN", (UNIV)refcapset },
+ { 0 },
+};
+
+/* Table of capacity names. Must match *CAP in sgmldecl.h. */
+
+char *captab[] = {
+ "TOTALCAP",
+ "ENTCAP",
+ "ENTCHCAP",
+ "ELEMCAP",
+ "GRPCAP",
+ "EXGRPCAP",
+ "EXNMCAP",
+ "ATTCAP",
+ "ATTCHCAP",
+ "AVGRPCAP",
+ "NOTCAP",
+ "NOTCHCAP",
+ "IDCAP",
+ "IDREFCAP",
+ "MAPCAP",
+ "LKSETCAP",
+ "LKNMCAP",
+};
+
+/* The default SGML declaration. */
+#define MAXNUMBER 99999999L
+
+/* Reference quantity set */
+
+#define REFATTCNT 40
+#define REFATTSPLEN 960
+#define REFBSEQLEN 960
+#define REFDTAGLEN 16
+#define REFDTEMPLEN 16
+#define REFENTLVL 16
+#define REFGRPCNT 32
+#define REFGRPGTCNT 96
+#define REFGRPLVL 16
+#define REFNORMSEP 2
+#define REFPILEN 240
+#define REFTAGLEN 960
+#define REFTAGLVL 24
+
+#define ALLOC_MAX 65534
+
+#define BIGINT 30000
+
+#define MAXATTCNT ((ALLOC_MAX/sizeof(struct ad)) - 2)
+#define MAXATTSPLEN BIGINT
+#define MAXBSEQLEN BIGINT
+#define MAXDTAGLEN 16
+#define MAXDTEMPLEN 16
+#define MAXENTLVL ((ALLOC_MAX/sizeof(struct source)) - 1)
+#define MAXGRPCNT MAXGRPGTCNT
+/* Must be between 96 and 253 */
+#define MAXGRPGTCNT 253
+#define MAXGRPLVL MAXGRPGTCNT
+#define MAXLITLEN BIGINT
+/* This guarantees that NAMELEN < LITLEN (ie there's always space for a name
+in a buffer intended for a literal.) */
+#define MAXNAMELEN (REFLITLEN - 1)
+#define MAXNORMSEP 2
+#define MAXPILEN BIGINT
+#define MAXTAGLEN BIGINT
+#define MAXTAGLVL ((ALLOC_MAX/sizeof(struct tag)) - 1)
+
+/* Table of quantity names. Must match Q* in sgmldecl.h. */
+
+static char *quantity_names[] = {
+ "ATTCNT",
+ "ATTSPLEN",
+ "BSEQLEN",
+ "DTAGLEN",
+ "DTEMPLEN",
+ "ENTLVL",
+ "GRPCNT",
+ "GRPGTCNT",
+ "GRPLVL",
+ "LITLEN",
+ "NAMELEN",
+ "NORMSEP",
+ "PILEN",
+ "TAGLEN",
+ "TAGLVL",
+};
+
+static int max_quantity[] = {
+ MAXATTCNT,
+ MAXATTSPLEN,
+ MAXBSEQLEN,
+ MAXDTAGLEN,
+ MAXDTEMPLEN,
+ MAXENTLVL,
+ MAXGRPCNT,
+ MAXGRPGTCNT,
+ MAXGRPLVL,
+ MAXLITLEN,
+ MAXNAMELEN,
+ MAXNORMSEP,
+ MAXPILEN,
+ MAXTAGLEN,
+ MAXTAGLVL,
+};
+
+static char *quantity_changed;
+
+/* Non-zero means the APPINFO parameter was not NONE. */
+static int appinfosw = 0;
+
+struct sgmldecl sd = {
+ REFCAPSET, /* capacity */
+#ifdef SUPPORT_SUBDOC
+ MAXNUMBER, /* subdoc */
+#else /* not SUPPORT_SUBDOC */
+ 0, /* subdoc */
+#endif /* not SUPPORT_SUBDOC */
+ 1, /* formal */
+ 1, /* omittag */
+ 1, /* shorttag */
+ 1, /* shortref */
+ { 1, 0 }, /* general/entity name case translation */
+ { /* reference quantity set */
+ REFATTCNT,
+ REFATTSPLEN,
+ REFBSEQLEN,
+ REFDTAGLEN,
+ REFDTEMPLEN,
+ REFENTLVL,
+ REFGRPCNT,
+ REFGRPGTCNT,
+ REFGRPLVL,
+ REFLITLEN,
+ REFNAMELEN,
+ REFNORMSEP,
+ REFPILEN,
+ REFTAGLEN,
+ REFTAGLVL,
+ },
+};
+
+static int systemcharset[] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+};
+
+static struct pmap charset_map[] = {
+ { "ESC 2/5 4/0", (UNIV)asciicharset }, /* ISO 646 IRV */
+ { "ESC 2/8 4/2", (UNIV)asciicharset }, /* ISO Registration Number 6, ASCII */
+ { SYSTEM_CHARSET_DESIGNATING_SEQUENCE, (UNIV)systemcharset },
+ /* system character set */
+ { 0 }
+};
+
+static int synrefcharset[256]; /* the syntax reference character set */
+
+#define CHAR_NONSGML 01
+#define CHAR_SIGNIFICANT 02
+#define CHAR_MAGIC 04
+#define CHAR_SHUNNED 010
+
+static UNCH char_flags[256];
+static int done_nonsgml = 0;
+static UNCH *nlextoke = 0; /* new lextoke */
+static UNCH *nlextran = 0; /* new lextran */
+
+
+static UNCH kcharset[] = "CHARSET";
+static UNCH kbaseset[] = "BASESET";
+static UNCH kdescset[] = "DESCSET";
+static UNCH kunused[] = "UNUSED";
+static UNCH kcapacity[] = "CAPACITY";
+static UNCH kpublic[] = "PUBLIC";
+static UNCH ksgmlref[] = "SGMLREF";
+static UNCH kscope[] = "SCOPE";
+static UNCH kdocument[] = "DOCUMENT";
+static UNCH kinstance[] = "INSTANCE";
+static UNCH ksyntax[] = "SYNTAX";
+static UNCH kswitches[] = "SWITCHES";
+static UNCH kfeatures[] = "FEATURES";
+static UNCH kminimize[] = "MINIMIZE";
+static UNCH kdatatag[] = "DATATAG";
+static UNCH komittag[] = "OMITTAG";
+static UNCH krank[] = "RANK";
+static UNCH kshorttag[] = "SHORTTAG";
+static UNCH klink[] = "LINK";
+static UNCH ksimple[] = "SIMPLE";
+static UNCH kimplicit[] = "IMPLICIT";
+static UNCH kexplicit[] = "EXPLICIT";
+static UNCH kother[] = "OTHER";
+static UNCH kconcur[] = "CONCUR";
+static UNCH ksubdoc[] = "SUBDOC";
+static UNCH kformal[] = "FORMAL";
+static UNCH kyes[] = "YES";
+static UNCH kno[] = "NO";
+static UNCH kappinfo[] = "APPINFO";
+static UNCH knone[] = "NONE";
+static UNCH kshunchar[] = "SHUNCHAR";
+static UNCH kcontrols[] = "CONTROLS";
+static UNCH kfunction[] = "FUNCTION";
+static UNCH krs[] = "RS";
+static UNCH kre[] = "RE";
+static UNCH kspace[] = "SPACE";
+static UNCH knaming[] = "NAMING";
+static UNCH klcnmstrt[] = "LCNMSTRT";
+static UNCH kucnmstrt[] = "UCNMSTRT";
+static UNCH klcnmchar[] = "LCNMCHAR";
+static UNCH kucnmchar[] = "UCNMCHAR";
+static UNCH knamecase[] = "NAMECASE";
+static UNCH kdelim[] = "DELIM";
+static UNCH kgeneral[] = "GENERAL";
+static UNCH kentity[] = "ENTITY";
+static UNCH kshortref[] = "SHORTREF";
+static UNCH knames[] = "NAMES";
+static UNCH kquantity[] = "QUANTITY";
+
+#define sderr mderr
+
+static UNIV pmaplookup P((struct pmap *, char *));
+static UNCH *ltous P((long));
+static VOID sdfixstandard P((UNCH *));
+static int sdparm P((UNCH *, struct parse *));
+static int sdname P((UNCH *, UNCH *));
+static int sdckname P((UNCH *, UNCH *));
+static int sdversion P((UNCH *));
+static int sdcharset P((UNCH *));
+static int sdcsdesc P((UNCH *, int *));
+static int sdpubcapacity P((UNCH *));
+static int sdcapacity P((UNCH *));
+static int sdscope P((UNCH *));
+static VOID setlexical P((void));
+static VOID noemptytag P((void));
+static int sdpubsyntax P((UNCH *));
+static int sdsyntax P((UNCH *));
+static int sdxsyntax P((UNCH *));
+static int sdtranscharnum P((UNCH *));
+static int sdtranschar P((int));
+static int sdshunchar P((UNCH *));
+static int sdsynref P((UNCH *));
+static int sdfunction P((UNCH *));
+static int sdnaming P((UNCH *));
+static int sddelim P((UNCH *));
+static int sdnames P((UNCH *));
+static int sdquantity P((UNCH *));
+static int sdfeatures P((UNCH *));
+static int sdappinfo P((UNCH *));
+
+static VOID bufsalloc P((void));
+static VOID bufsrealloc P((void));
+
+/* Parse the SGML declaration. Return non-zero if there was some appinfo. */
+
+int sgmldecl()
+{
+ int i;
+ int errsw = 0;
+ UNCH endbuf[REFNAMELEN+2]; /* buffer for parsing terminating > */
+ static int (*section[]) P((UNCH *)) = {
+ sdversion,
+ sdcharset,
+ sdcapacity,
+ sdscope,
+ sdsyntax,
+ sdfeatures,
+ sdappinfo,
+ };
+ /* These are needed if we use mderr. */
+ parmno = 0;
+ mdname = sgmlkey;
+ subdcl = NULL;
+ for (i = 0; i < SIZEOF(section); i++)
+ if ((*section[i])(tbuf) == FAIL) {
+ errsw = 1;
+ break;
+ }
+ if (!errsw)
+ setlexical();
+ bufsrealloc();
+ /* Parse the >. Don't overwrite the appinfo. */
+ if (!errsw)
+ sdparm(endbuf, 0);
+ /* We must exit if we hit end of document. */
+ if (pcbsd.action == EOD_)
+ exiterr(161, &pcbsd);
+ if (!errsw && pcbsd.action != ESGD)
+ sderr(126, (UNCH *)0, (UNCH *)0);
+ return appinfosw;
+}
+
+/* Parse the literal (which should contain the version of the
+standard) at the beginning of a SGML declaration. */
+
+static int sdversion(tbuf)
+UNCH *tbuf;
+{
+ if (sdparm(tbuf, &pcblitv) != LIT1) {
+ sderr(123, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ sdfixstandard(tbuf);
+ if (ustrcmp(tbuf, standard) != 0)
+ sderr(E_BADVERSION, tbuf, standard);
+ return SUCCESS;
+}
+
+/* Parse the CHARSET section. Use one token lookahead. */
+
+static int sdcharset(tbuf)
+UNCH *tbuf;
+{
+ int i;
+ int status[256];
+
+ if (sdname(tbuf, kcharset) == FAIL) return FAIL;
+ (void)sdparm(tbuf, 0);
+
+ if (sdcsdesc(tbuf, status) == FAIL)
+ return FAIL;
+
+ for (i = 128; i < 256; i++)
+ if (status[i] != UNDESC)
+ break;
+ if (i >= 256) {
+ /* Only a 7-bit character set was described. Fill it out to 8-bits. */
+ for (i = 128; i < 256; i++)
+ status[i] = UNUSED;
+#if 0
+ sderr(E_7BIT, (UNCH *)0, (UNCH *)0);
+#endif
+ }
+ /* Characters that are declared UNUSED in the document character set
+ are assigned to non-SGML. */
+ for (i = 0; i < 256; i++) {
+ if (status[i] == UNDESC) {
+ sderr(E_CHARMISSING, ltous((long)i), (UNCH *)0);
+ char_flags[i] |= CHAR_NONSGML;
+ }
+ else if (status[i] == UNUSED)
+ char_flags[i] |= CHAR_NONSGML;
+ }
+ done_nonsgml = 1;
+ return SUCCESS;
+}
+
+/* Parse a character set description. Uses one character lookahead. */
+
+static int sdcsdesc(tbuf, status)
+UNCH *tbuf;
+int *status;
+{
+ int i;
+ int nsets = 0;
+ struct fpi fpi;
+
+ for (i = 0; i < 256; i++)
+ status[i] = UNDESC;
+
+ for (;;) {
+ int nchars;
+ int *baseset = 0;
+
+ if (pcbsd.action != NAS1) {
+ if (nsets == 0) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ break;
+ }
+ if (!matches(tbuf, kbaseset)) {
+ if (nsets == 0) {
+ sderr(118, tbuf+1, kbaseset);
+ return FAIL;
+ }
+ break;
+ }
+ nsets++;
+ MEMZERO((UNIV)&fpi, FPISZ);
+ if (sdparm(tbuf, &pcblitv) != LIT1) {
+ sderr(123, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ fpi.fpipubis = tbuf;
+ /* Give a warning if it is not a CHARSET fpi. */
+ if (parsefpi(&fpi))
+ sderr(E_FORMAL, (UNCH *)0, (UNCH *)0);
+ else if (fpi.fpic != FPICHARS)
+ sderr(E_BADCLASS, kcharset, (UNCH *)0);
+ else {
+ fpi.fpipubis[fpi.fpil + fpi.fpill] = '\0';
+ baseset = (int *)pmaplookup(charset_map,
+ (char *)fpi.fpipubis + fpi.fpil);
+ if (!baseset)
+ sderr(E_UNKNOWNSET, fpi.fpipubis + fpi.fpil, (UNCH *)0);
+ }
+ if (sdname(tbuf, kdescset) == FAIL) return FAIL;
+ nchars = 0;
+ for (;;) {
+ long start, count;
+ long basenum;
+ if (sdparm(tbuf, 0) != NUM1)
+ break;
+ start = atol((char *)tbuf);
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ count = atol((char *)tbuf);
+ switch (sdparm(tbuf, &pcblitv)) {
+ case NUM1:
+ basenum = atol((char *)tbuf);
+ break;
+ case LIT1:
+ basenum = UNKNOWN;
+ break;
+ case NAS1:
+ if (matches(tbuf, kunused)) {
+ basenum = UNUSED;
+ break;
+ }
+ /* fall through */
+ default:
+ sderr(E_CHARDESC, ltous(start), (UNCH *)0);
+ return FAIL;
+ }
+ if (start + count > 256)
+ sderr(E_CHARRANGE, (UNCH *)0, (UNCH *)0);
+ else {
+ int i;
+ int lim = (int)start + count;
+ for (i = (int)start; i < lim; i++) {
+ if (status[i] != UNDESC)
+ sderr(E_CHARDUP, ltous((long)i), (UNCH *)0);
+ else if (basenum == UNUSED || basenum == UNKNOWN)
+ status[i] = (int)basenum;
+ else if (baseset == 0)
+ status[i] = UNKNOWN_SET;
+ else {
+ int n = basenum + (i - start);
+ if (n < 0 || n > 255)
+ sderr(E_CHARRANGE, (UNCH *)0, (UNCH *)0);
+ else if (baseset[n] == UNUSED)
+ sderr(E_BADBASECHAR, ltous((long)n), (UNCH *)0);
+ else
+ status[i] = baseset[n];
+ }
+ }
+ }
+ nchars++;
+ }
+ if (nchars == 0) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Parse the CAPACITY section. Uses one token lookahead. */
+
+static int sdcapacity(tbuf)
+UNCH *tbuf;
+{
+ int ncap;
+
+ if (sdckname(tbuf, kcapacity) == FAIL)
+ return FAIL;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kpublic))
+ return sdpubcapacity(tbuf);
+ if (!matches(tbuf, ksgmlref)) {
+ sderr(E_CAPACITY, tbuf+1, (UNCH *)0);
+ return FAIL;
+ }
+ memcpy((UNIV)sd.capacity, (UNIV)refcapset, sizeof(sd.capacity));
+ ncap = 0;
+ for (;;) {
+ int capno = -1;
+ int i;
+
+ if (sdparm(tbuf, 0) != NAS1)
+ break;
+ for (i = 0; i < SIZEOF(captab); i++)
+ if (matches(tbuf, captab[i])) {
+ capno = i;
+ break;
+ }
+ if (capno < 0)
+ break;
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ sd.capacity[capno] = atol((char *)tbuf);
+ ncap++;
+ }
+ if (ncap == 0) {
+ sderr(E_CAPMISSING, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+/* Parse a CAPACITY section that started with PUBLIC. Must do one
+token lookahead, since sdcapacity() also does. */
+
+static int sdpubcapacity(tbuf)
+UNCH *tbuf;
+{
+ UNIV ptr;
+ if (sdparm(tbuf, &pcblitv) != LIT1) {
+ sderr(123, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ sdfixstandard(tbuf);
+ ptr = pmaplookup(capset_map, (char *)tbuf);
+ if (!ptr)
+ sderr(E_CAPSET, tbuf, (UNCH *)0);
+ else
+ memcpy((UNIV)sd.capacity, (UNIV)ptr, sizeof(sd.capacity));
+ (void)sdparm(tbuf, 0);
+ return SUCCESS;
+}
+
+/* Parse the SCOPE section. Uses no lookahead. */
+
+static int sdscope(tbuf)
+UNCH *tbuf;
+{
+ if (sdckname(tbuf, kscope) == FAIL)
+ return FAIL;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kdocument))
+ ;
+ else if (matches(tbuf, kinstance))
+ sderr(E_INSTANCE, (UNCH *)0, (UNCH *)0);
+ else {
+ sderr(E_SCOPE, tbuf+1, (UNCH *)0);
+ return FAIL;
+ }
+ return SUCCESS;
+}
+
+/* Parse the SYNTAX section. Uses one token lookahead. */
+
+static int sdsyntax(tbuf)
+UNCH *tbuf;
+{
+ if (sdname(tbuf, ksyntax) == FAIL) return FAIL;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kpublic))
+ return sdpubsyntax(tbuf);
+ return sdxsyntax(tbuf);
+}
+
+/* Parse the SYNTAX section which starts with PUBLIC. Uses one token
+lookahead. */
+
+static int sdpubsyntax(tbuf)
+UNCH *tbuf;
+{
+ int nswitches;
+ if (sdparm(tbuf, &pcblitv) != LIT1)
+ return FAIL;
+ sdfixstandard(tbuf);
+ if (ustrcmp(tbuf, CORE_SYNTAX) == 0)
+ sd.shortref = 0;
+ else if (ustrcmp(tbuf, REFERENCE_SYNTAX) == 0)
+ sd.shortref = 1;
+ else
+ sderr(E_SYNTAX, tbuf, (UNCH *)0);
+ if (sdparm(tbuf, 0) != NAS1)
+ return SUCCESS;
+ if (!matches(tbuf, kswitches))
+ return SUCCESS;
+ nswitches = 0;
+ for (;;) {
+ int errsw = 0;
+
+ if (sdparm(tbuf, 0) != NUM1)
+ break;
+ if (atol((char *)tbuf) > 255) {
+ sderr(E_CHARNUM, (UNCH *)0, (UNCH *)0);
+ errsw = 1;
+ }
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (!errsw) {
+ if (atol((char *)tbuf) > 255)
+ sderr(E_CHARNUM, (UNCH *)0, (UNCH *)0);
+ }
+ nswitches++;
+ }
+ if (nswitches == 0) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ sderr(E_SWITCHES, (UNCH *)0, (UNCH *)0);
+ return SUCCESS;
+}
+
+/* Parse an explicit concrete syntax. Uses one token lookahead. */
+
+static
+int sdxsyntax(tbuf)
+UNCH *tbuf;
+{
+ static int (*section[]) P((UNCH *)) = {
+ sdshunchar,
+ sdsynref,
+ sdfunction,
+ sdnaming,
+ sddelim,
+ sdnames,
+ sdquantity,
+ };
+ int i;
+
+ for (i = 0; i < SIZEOF(section); i++)
+ if ((*section[i])(tbuf) == FAIL)
+ return FAIL;
+ return SUCCESS;
+}
+
+/* Parse the SHUNCHAR section. Uses one token lookahead. */
+
+static
+int sdshunchar(tbuf)
+UNCH *tbuf;
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ char_flags[i] &= ~CHAR_SHUNNED;
+
+ if (sdckname(tbuf, kshunchar) == FAIL)
+ return FAIL;
+
+ if (sdparm(tbuf, 0) == NAS1) {
+ if (matches(tbuf, knone)) {
+ (void)sdparm(tbuf, 0);
+ return SUCCESS;
+ }
+ if (matches(tbuf, kcontrols)) {
+ for (i = 0; i < 256; i++)
+ if (ISASCII(i) && iscntrl(i))
+ char_flags[i] |= CHAR_SHUNNED;
+ if (sdparm(tbuf, 0) != NUM1)
+ return SUCCESS;
+ }
+ }
+ if (pcbsd.action != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ do {
+ long n = atol((char *)tbuf);
+ if (n > 255)
+ sderr(E_CHARNUM, (UNCH *)0, (UNCH *)0);
+ else
+ char_flags[(int)n] |= CHAR_SHUNNED;
+ } while (sdparm(tbuf, 0) == NUM1);
+ return SUCCESS;
+}
+
+/* Parse the syntax reference character set. Uses one token lookahead. */
+
+static
+int sdsynref(tbuf)
+UNCH *tbuf;
+{
+ return sdcsdesc(tbuf, synrefcharset);
+}
+
+/* Translate a character number from the syntax reference character set
+to the system character set. If it can't be done, give an error message
+and return -1. */
+
+static
+int sdtranscharnum(tbuf)
+UNCH *tbuf;
+{
+ long n = atol((char *)tbuf);
+ if (n > 255) {
+ sderr(E_CHARNUM, (UNCH *)0, (UNCH *)0);
+ return -1;
+ }
+ return sdtranschar((int)n);
+}
+
+
+static
+int sdtranschar(n)
+int n;
+{
+ int ch = synrefcharset[n];
+ if (ch >= 0)
+ return ch;
+ switch (ch) {
+ case UNUSED:
+ sderr(E_SYNREFUNUSED, ltous((long)n), (UNCH *)0);
+ break;
+ case UNDESC:
+ sderr(E_SYNREFUNDESC, ltous((long)n), (UNCH *)0);
+ break;
+ case UNKNOWN:
+ sderr(E_SYNREFUNKNOWN, ltous((long)n), (UNCH *)0);
+ break;
+ case UNKNOWN_SET:
+ sderr(E_SYNREFUNKNOWNSET, ltous((long)n), (UNCH *)0);
+ break;
+ default:
+ abort();
+ }
+ return -1;
+}
+
+
+/* Parse the function section. Uses two tokens lookahead. "NAMING"
+could be a function name. */
+
+static
+int sdfunction(tbuf)
+UNCH *tbuf;
+{
+ static UNCH *fun[] = { kre, krs, kspace };
+ static int funval[] = { RECHAR, RSCHAR, ' ' };
+ int i;
+ int had_tab = 0;
+ int changed = 0; /* attempted to change reference syntax */
+
+ if (sdckname(tbuf, kfunction) == FAIL)
+ return FAIL;
+ for (i = 0; i < SIZEOF(fun); i++) {
+ int ch;
+ if (sdname(tbuf, fun[i]) == FAIL)
+ return FAIL;
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ ch = sdtranscharnum(tbuf);
+ if (ch >= 0 && ch != funval[i])
+ changed = 1;
+ }
+ for (;;) {
+ int tabsw = 0;
+ int namingsw = 0;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, (UNCH *)"TAB")) {
+ tabsw = 1;
+ if (had_tab)
+ sderr(E_FUNDUP, (UNCH *)0, (UNCH *)0);
+ }
+ else {
+ for (i = 0; i < SIZEOF(fun); i++)
+ if (matches(tbuf, fun[i]))
+ sderr(E_BADFUN, fun[i], (UNCH *)0);
+ if (matches(tbuf, knaming))
+ namingsw = 1;
+ else
+ changed = 1;
+ }
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (namingsw) {
+ if (matches(tbuf, klcnmstrt))
+ break;
+ changed = 1;
+ }
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (tabsw && !had_tab) {
+ int ch = sdtranscharnum(tbuf);
+ if (ch >= 0 && ch != TABCHAR)
+ changed = 1;
+ had_tab = 1;
+ }
+
+ }
+ if (!had_tab)
+ changed = 1;
+ if (changed)
+ sderr(E_FUNCHAR, (UNCH *)0, (UNCH *)0);
+ return SUCCESS;
+}
+
+/* Parse the NAMING section. Uses no lookahead. */
+
+static
+int sdnaming(tbuf)
+UNCH *tbuf;
+{
+ int i;
+ int bad = 0;
+ static UNCH *classes[] = { klcnmstrt, kucnmstrt, klcnmchar, kucnmchar };
+ static UNCH *types[] = { kgeneral, kentity };
+
+#define NCLASSES SIZEOF(classes)
+
+ int bufsize = 4; /* allocated size of buf */
+ UNCH *buf = (UNCH *)rmalloc(bufsize); /* holds characters
+ in naming classes */
+ int bufi = 0; /* next index into buf */
+ int start[NCLASSES]; /* index of first character for each class */
+ int count[NCLASSES]; /* number of characters for each class */
+
+ for (i = 0; i < NCLASSES; i++) {
+ UNCH *s;
+
+ if (sdckname(tbuf, classes[i]) == FAIL) {
+ frem((UNIV)buf);
+ return FAIL;
+ }
+ if (sdparm(tbuf, &pcblitp) != LIT1) {
+ sderr(123, (UNCH *)0, (UNCH *)0);
+ frem((UNIV)buf);
+ return FAIL;
+ }
+ start[i] = bufi;
+
+ for (s = tbuf; *s; s++) {
+ int c = *s;
+ if (c == DELNONCH) {
+ c = UNSHIFTNON(*s);
+ s++;
+ }
+ c = sdtranschar(c);
+ if (c < 0)
+ bad = 1;
+ else if ((char_flags[c] & (CHAR_SIGNIFICANT | CHAR_MAGIC))
+ && c != '.' && c != '-') {
+ int class = lextoke[c];
+ if (class == SEP || class == SP || class == NMC
+ || class == NMS || class == NU)
+ sderr(E_NMBAD, ltous((long)c), (UNCH *)0);
+ else
+ sderr(E_NMUNSUP, ltous((long)c), (UNCH *)0);
+ bad = 1;
+ }
+ if (bufi >= bufsize)
+ buf = (UNCH *)rrealloc((UNIV)buf, bufsize *= 2);
+ buf[bufi++] = c;
+ }
+
+ count[i] = bufi - start[i];
+ (void)sdparm(tbuf, 0);
+ }
+ if (!bad && count[0] != count[1]) {
+ sderr(E_NMSTRTCNT, (UNCH *)0, (UNCH *)0);
+ bad = 1;
+ }
+ if (!bad && count[2] != count[3]) {
+ sderr(E_NMCHARCNT, (UNCH *)0, (UNCH *)0);
+ bad = 1;
+ }
+ if (!bad) {
+ nlextoke = (UNCH *)rmalloc(256);
+ memcpy((UNIV)nlextoke, lextoke, 256);
+ nlextoke['.'] = nlextoke['-'] = INV;
+
+ nlextran = (UNCH *)rmalloc(256);
+ memcpy((UNIV)nlextran, lextran, 256);
+
+ for (i = 0; i < count[0]; i++) {
+ UNCH lc = buf[start[0] + i];
+ UNCH uc = buf[start[1] + i];
+ nlextoke[lc] = NMS;
+ nlextoke[uc] = NMS;
+ nlextran[lc] = uc;
+ }
+
+ for (i = 0; i < count[2]; i++) {
+ UNCH lc = buf[start[2] + i];
+ UNCH uc = buf[start[3] + i];
+ if (nlextoke[lc] == NMS) {
+ sderr(E_NMDUP, ltous((long)lc), (UNCH *)0);
+ bad = 1;
+ }
+ else if (nlextoke[uc] == NMS) {
+ sderr(E_NMDUP, ltous((long)uc), (UNCH *)0);
+ bad = 1;
+ }
+ else {
+ nlextoke[lc] = NMC;
+ nlextoke[uc] = NMC;
+ nlextran[lc] = uc;
+ }
+ }
+ if (nlextoke['-'] != NMC) {
+ sderr(E_NMMINUS, (UNCH *)0, (UNCH *)0);
+ bad = 1;
+ }
+ if (bad) {
+ if (nlextoke) {
+ frem((UNIV)nlextoke);
+ nlextoke = 0;
+ }
+ if (nlextran) {
+ frem((UNIV)nlextran);
+ nlextran = 0;
+ }
+ }
+ }
+
+ frem((UNIV)buf);
+
+ if (sdckname(tbuf, knamecase) == FAIL)
+ return FAIL;
+ for (i = 0; i < SIZEOF(types); ++i) {
+ if (sdname(tbuf, types[i]) == FAIL)
+ return FAIL;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kyes))
+ sd.namecase[i] = 1;
+ else if (matches(tbuf, kno))
+ sd.namecase[i] = 0;
+ else {
+ sderr(E_YESNO, tbuf+1, (UNCH *)0);
+ return FAIL;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Parse the DELIM section. Uses one token lookahead. */
+
+static
+int sddelim(tbuf)
+UNCH *tbuf;
+{
+ int changed = 0;
+ if (sdname(tbuf, kdelim) == FAIL
+ || sdname(tbuf, kgeneral) == FAIL
+ || sdname(tbuf, ksgmlref) == FAIL)
+ return FAIL;
+ for (;;) {
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kshortref))
+ break;
+ if (sdparm(tbuf, &pcblitp) != LIT1) {
+ sderr(123, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ changed = 1;
+ }
+ if (changed) {
+ sderr(E_GENDELIM, (UNCH *)0,(UNCH *)0);
+ changed = 0;
+ }
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, ksgmlref))
+ sd.shortref = 1;
+ else if (matches(tbuf, knone))
+ sd.shortref = 0;
+ else {
+ sderr(118, tbuf+1, ksgmlref); /* probably they forgot SGMLREF */
+ return FAIL;
+ }
+ while (sdparm(tbuf, &pcblitp) == LIT1)
+ changed = 1;
+ if (changed)
+ sderr(E_SRDELIM, (UNCH *)0, (UNCH *)0);
+ return SUCCESS;
+}
+
+/* Parse the NAMES section. Uses one token lookahead. */
+
+static
+int sdnames(tbuf)
+UNCH *tbuf;
+{
+ int i;
+ if (sdckname(tbuf, knames) == FAIL)
+ return FAIL;
+ if (sdname(tbuf, ksgmlref) == FAIL)
+ return FAIL;
+
+ while (sdparm(tbuf, 0) == NAS1) {
+ int j;
+ if (matches(tbuf, kquantity))
+ break;
+ for (i = 0; i < NKEYS; i++)
+ if (matches(tbuf, key[i]))
+ break;
+ if (i >= NKEYS) {
+ sderr(E_BADKEY, tbuf+1, (UNCH *)0);
+ return FAIL;
+ }
+ if (sdparm(tbuf, &pcblitp) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (!newkey) {
+ newkey = (UNCH (*)[REFNAMELEN+1])rmalloc((REFNAMELEN+1)*NKEYS);
+ MEMZERO((UNIV)newkey, (REFNAMELEN+1)*NKEYS);
+ }
+ for (j = 0; j < NKEYS; j++) {
+ if (matches(tbuf, key[j])) {
+ sderr(E_REFNAME, tbuf + 1, (UNCH *)0);
+ break;
+ }
+ if (matches(tbuf, newkey[j])) {
+ sderr(E_DUPNAME, tbuf + 1, (UNCH *)0);
+ break;
+ }
+ }
+ if (j >= NKEYS)
+ ustrcpy(newkey[i], tbuf + 1);
+ }
+ /* Now install the new keys. */
+ if (newkey) {
+ for (i = 0; i < NKEYS; i++)
+ if (newkey[i][0] != '\0') {
+ UNCH temp[REFNAMELEN + 1];
+
+ ustrcpy(temp, key[i]);
+ ustrcpy(key[i], newkey[i]);
+ ustrcpy(newkey[i], temp);
+ }
+ }
+ return SUCCESS;
+}
+
+/* Parse the QUANTITY section. Uses one token lookahead. */
+
+static int sdquantity(tbuf)
+UNCH *tbuf;
+{
+ int quantity[NQUANTITY];
+ int i;
+
+ for (i = 0; i < NQUANTITY; i++)
+ quantity[i] = -1;
+ if (sdckname(tbuf, kquantity) == FAIL)
+ return FAIL;
+ if (sdname(tbuf, ksgmlref) == FAIL)
+ return FAIL;
+ while (sdparm(tbuf, 0) == NAS1 && !matches(tbuf, kfeatures)) {
+ long n;
+ for (i = 0; i < SIZEOF(quantity_names); i++)
+ if (matches(tbuf, quantity_names[i]))
+ break;
+ if (i >= SIZEOF(quantity_names)) {
+ sderr(E_BADQUANTITY, tbuf + 1, (UNCH *)0);
+ return FAIL;
+ }
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ n = atol((char *)tbuf);
+ if (n < sd.quantity[i])
+ sderr(E_QUANTITY, (UNCH *)quantity_names[i],
+ ltous((long)sd.quantity[i]));
+ else if (n > max_quantity[i]) {
+ sderr(E_QTOOBIG, (UNCH *)quantity_names[i],
+ ltous((long)max_quantity[i]));
+ quantity[i] = max_quantity[i];
+ }
+ else
+ quantity[i] = (int)n;
+ }
+ for (i = 0; i < NQUANTITY; i++)
+ if (quantity[i] > 0) {
+ sd.quantity[i] = quantity[i];
+ if (!quantity_changed)
+ quantity_changed = (char *)rmalloc(NQUANTITY);
+ quantity_changed[i] = 1;
+ }
+ return SUCCESS;
+}
+
+/* Parse the FEATURES section. Uses no lookahead. */
+
+static int sdfeatures(tbuf)
+UNCH *tbuf;
+{
+ static struct {
+ UNCH *name;
+ UNCH argtype; /* 0 = no argument, 1 = boolean, 2 = numeric */
+ UNIV valp; /* UNCH * if boolean, long * if numeric. */
+ } features[] = {
+ { kminimize, 0, 0 },
+ { kdatatag, 1, 0 },
+ { komittag, 1, (UNIV)&sd.omittag },
+ { krank, 1, 0 },
+ { kshorttag, 1, (UNIV)&sd.shorttag },
+ { klink, 0, 0 },
+ { ksimple, 2, 0 },
+ { kimplicit, 1, 0 },
+ { kexplicit, 2, 0 },
+ { kother, 0, 0 },
+ { kconcur, 2, 0 },
+ { ksubdoc, 2, (UNIV)&sd.subdoc },
+ { kformal, 1, (UNIV)&sd.formal },
+ };
+
+ int i;
+
+ if (sdckname(tbuf, kfeatures) == FAIL)
+ return FAIL;
+ for (i = 0; i < SIZEOF(features); i++) {
+ if (sdname(tbuf, features[i].name) == FAIL) return FAIL;
+ if (features[i].argtype > 0) {
+ long n;
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (matches(tbuf, kyes)) {
+ if (features[i].argtype > 1) {
+ if (sdparm(tbuf, 0) != NUM1) {
+ sderr(E_XNUM, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ n = atol((char *)tbuf);
+ if (n == 0)
+ sderr(E_ZEROFEATURE, features[i].name, (UNCH *)0);
+ }
+ else
+ n = 1;
+ }
+ else if (matches(tbuf, kno))
+ n = 0;
+ else {
+ sderr(E_YESNO, tbuf+1, (UNCH *)0);
+ return FAIL;
+ }
+ if (features[i].valp == 0) {
+ if (n > 0)
+ sderr(E_NOTSUPPORTED, features[i].name,
+ (UNCH *)0);
+ }
+ else if (features[i].argtype > 1)
+ *(long *)features[i].valp = n;
+ else
+ *(UNCH *)features[i].valp = (UNCH)n;
+ }
+ }
+ if (!sd.shorttag)
+ noemptytag();
+ return SUCCESS;
+}
+
+/* Parse the APPINFO section. Uses no lookahead. */
+
+static int sdappinfo(tbuf)
+UNCH *tbuf;
+{
+ if (sdname(tbuf, kappinfo) == FAIL) return FAIL;
+ switch (sdparm(tbuf, &pcblitv)) {
+ case LIT1:
+ appinfosw = 1;
+ break;
+ case NAS1:
+ if (matches(tbuf, knone))
+ break;
+ sderr(118, tbuf+1, knone);
+ return FAIL;
+ default:
+ sderr(E_XNMLIT, knone, (UNCH *)0);
+ return FAIL;
+ }
+ return SUCCESS;
+}
+
+/* Change a prefix of ISO 8879-1986 to ISO 8879:1986. Amendment 1 to
+the standard requires the latter. */
+
+static VOID sdfixstandard(tbuf)
+UNCH *tbuf;
+{
+ if (strncmp((char *)tbuf, "ISO 8879-1986", 13) == 0) {
+ sderr(E_STANDARD, (UNCH *)0, (UNCH *)0);
+ tbuf[8] = ':';
+ }
+}
+
+static int sdname(tbuf, key)
+UNCH *tbuf;
+UNCH *key;
+{
+ if (sdparm(tbuf, 0) != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (!matches(tbuf, key)) {
+ sderr(118, tbuf+1, key);
+ return FAIL;
+ }
+ return SUCCESS;
+}
+
+static int sdckname(tbuf, key)
+UNCH *tbuf;
+UNCH *key;
+{
+ if (pcbsd.action != NAS1) {
+ sderr(120, (UNCH *)0, (UNCH *)0);
+ return FAIL;
+ }
+ if (!matches(tbuf, key)) {
+ sderr(118, tbuf+1, key);
+ return FAIL;
+ }
+ return SUCCESS;
+}
+
+/* Parse a SGML declaration parameter. If lpcb is NULL, pt must be
+REFNAMELEN+2 characters long, otherwise at least LITLEN+2 characters
+long. LPCB should be NULL if a literal is not allowed. */
+
+static int sdparm(pt, lpcb)
+UNCH *pt; /* Token buffer. */
+struct parse *lpcb; /* PCB for literal parse. */
+{
+ for (;;) {
+ parse(&pcbsd);
+ if (pcbsd.action != ISIG)
+ break;
+ sderr(E_SIGNIFICANT, (UNCH *)0, (UNCH *)0);
+ }
+ ++parmno;
+ switch (pcbsd.action) {
+ case LIT1:
+ if (!lpcb) {
+ sderr(E_BADLIT, (UNCH *)0, (UNCH *)0);
+ REPEATCC;
+ return pcbsd.action = INV_;
+ }
+ parselit(pt, lpcb, REFLITLEN, lex.d.lit);
+ return pcbsd.action;
+ case LIT2:
+ if (!lpcb) {
+ sderr(E_BADLIT, (UNCH *)0, (UNCH *)0);
+ REPEATCC;
+ return pcbsd.action = INV_;
+ }
+ parselit(pt, lpcb, REFLITLEN, lex.d.lita);
+ return pcbsd.action = LIT1;
+ case NAS1:
+ parsenm(pt, 1);
+ return pcbsd.action;
+ case NUM1:
+ parsetkn(pt, NU, REFNAMELEN);
+ return pcbsd.action;
+ }
+ return pcbsd.action;
+}
+
+VOID sdinit()
+{
+ int i;
+ /* Shunned character numbers in the reference concrete syntax. */
+ static UNCH refshun[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127, 255
+ };
+ UNCH **p;
+ /* A character is magic if it is a non-SGML character used for
+ some internal purpose in the parser. */
+ char_flags[EOS] |= CHAR_MAGIC;
+ char_flags[EOBCHAR] |= CHAR_MAGIC;
+ char_flags[EOFCHAR] |= CHAR_MAGIC;
+ char_flags[GENRECHAR] |= CHAR_MAGIC;
+ char_flags[DELNONCH] |= CHAR_MAGIC;
+ char_flags[DELCDATA] |= CHAR_MAGIC;
+ char_flags[DELSDATA] |= CHAR_MAGIC;
+
+ /* Figure out the significant SGML characters. */
+ for (p = lextabs; *p; p++) {
+ UNCH datclass = (*p)[CANON_DATACHAR];
+ UNCH nonclass = (*p)[CANON_NONSGML];
+ for (i = 0; i < 256; i++)
+ if (!(char_flags[i] & CHAR_MAGIC)
+ && (*p)[i] != datclass && (*p)[i] != nonclass)
+ char_flags[i] |= CHAR_SIGNIFICANT;
+ }
+ for (i = 0; i < SIZEOF(refshun); i++)
+ char_flags[refshun[i]] |= CHAR_SHUNNED;
+ for (i = 0; i < 256; i++)
+ if (ISASCII(i) && iscntrl(i))
+ char_flags[i] |= CHAR_SHUNNED;
+ bufsalloc();
+}
+
+
+static
+VOID bufsalloc()
+{
+ scbs = (struct source *)rmalloc((REFENTLVL+1)*sizeof(struct source));
+ tbuf = (UNCH *)rmalloc(REFATTSPLEN+REFLITLEN+1);
+ /* entbuf is used for parsing numeric character references */
+ entbuf = (UNCH *)rmalloc(REFNAMELEN + 2);
+}
+
+static
+VOID bufsrealloc()
+{
+ UNS size;
+
+ if (ENTLVL != REFENTLVL)
+ scbs = (struct source *)rrealloc((UNIV)scbs,
+ (ENTLVL+1)*sizeof(struct source));
+ /* Calculate the size for tbuf. */
+ size = LITLEN + ATTSPLEN;
+ if (PILEN > size)
+ size = PILEN;
+ if (BSEQLEN > size)
+ size = BSEQLEN;
+ if (size != REFATTSPLEN + REFLITLEN)
+ tbuf = (UNCH *)rrealloc((UNIV)tbuf, size + 1);
+ if (NAMELEN != REFNAMELEN)
+ entbuf = (UNCH *)rrealloc((UNIV)entbuf, NAMELEN + 2);
+}
+
+
+/* Check that the non-SGML characters are compatible with the concrete
+syntax and munge the lexical tables accordingly. If IMPLIED is
+non-zero, then the SGML declaration was implied; in this case, don't
+give error messages about shunned characters not being declared
+non-SGML. Also make any changes that are required by the NAMING section.
+*/
+
+static VOID setlexical()
+{
+ int i;
+ UNCH **p;
+
+ if (nlextoke) {
+ /* Handle characters that were made significant by the
+ NAMING section. */
+ for (i = 0; i < 256; i++)
+ if (nlextoke[i] == NMC || nlextoke[i] == NMS)
+ char_flags[i] |= CHAR_SIGNIFICANT;
+ }
+
+ for (i = 0; i < 256; i++)
+ if (char_flags[i] & CHAR_SIGNIFICANT) {
+ /* Significant SGML characters musn't be non-SGML. */
+ if (char_flags[i] & CHAR_NONSGML) {
+ UNCH buf[2];
+ buf[0] = i;
+ buf[1] = '\0';
+ sderr(E_NONSGML, buf, (UNCH *)0);
+ char_flags[i] &= ~CHAR_NONSGML;
+ }
+ }
+ else {
+ /* Shunned characters that are not significant SGML characters
+ must be non-SGML. */
+ if ((char_flags[i] & (CHAR_SHUNNED | CHAR_NONSGML))
+ == CHAR_SHUNNED) {
+ sderr(E_SHUNNED, ltous((long)i), (UNCH *)0);
+ char_flags[i] |= CHAR_NONSGML;
+ }
+ }
+
+
+ /* Now munge the lexical tables. */
+ for (p = lextabs; *p; p++) {
+ UNCH nonclass = (*p)[CANON_NONSGML];
+ UNCH datclass = (*p)[CANON_DATACHAR];
+ UNCH nmcclass = (*p)[CANON_NMC];
+ UNCH nmsclass = (*p)[CANON_NMS];
+ UNCH minclass = (*p)[CANON_MIN];
+ for (i = 0; i < 256; i++) {
+ if (char_flags[i] & CHAR_NONSGML) {
+ /* We already know that it's not significant. */
+ if (!(char_flags[i] & CHAR_MAGIC))
+ (*p)[i] = nonclass;
+ }
+ else {
+ if (char_flags[i] & CHAR_MAGIC) {
+ sderr(E_MUSTBENON, ltous((long)i), (UNCH *)0);
+ }
+ else if (!(char_flags[i] & CHAR_SIGNIFICANT))
+ (*p)[i] = datclass;
+ else if (nlextoke
+ /* This relies on the fact that lextoke
+ occurs last in lextabs. */
+ && lextoke[i] != nlextoke[i]) {
+ switch (nlextoke[i]) {
+ case NMC:
+ (*p)[i] = nmcclass;
+ break;
+ case NMS:
+ (*p)[i] = nmsclass;
+ break;
+ case INV:
+ /* This will happen if period is not a
+ name character. */
+ (*p)[i] = minclass;
+ break;
+ default:
+ abort();
+ }
+ }
+ }
+ }
+ }
+ if (nlextran) {
+ memcpy((UNIV)lextran, (UNIV)nlextran, 256);
+ frem((UNIV)nlextran);
+ }
+ if (nlextoke) {
+ frem((UNIV)nlextoke);
+ nlextoke = 0;
+ }
+
+}
+
+/* Munge parse tables so that empty start and end tags are not recognized. */
+
+static VOID noemptytag()
+{
+ static struct parse *pcbs[] = { &pcbconm, &pcbcone, &pcbconr, &pcbconc };
+ int i;
+
+ for (i = 0; i < SIZEOF(pcbs); i++) {
+ int maxclass, maxstate;
+ int j, k, act;
+ UNCH *plex = pcbs[i]->plex;
+ UNCH **ptab = pcbs[i]->ptab;
+
+ /* Figure out the maximum lexical class. */
+ maxclass = 0;
+ for (j = 0; j < 256; j++)
+ if (plex[j] > maxclass)
+ maxclass = plex[j];
+
+ /* Now figure out the maximum state number and at the same time
+ change actions. */
+
+ maxstate = 0;
+
+ for (j = 0; j <= maxstate; j += 2) {
+ for (k = 0; k <= maxclass; k++)
+ if (ptab[j][k] > maxstate)
+ maxstate = ptab[j][k];
+ /* If the '>' class has an empty start or end tag action,
+ change it to the action that the NMC class has. */
+ act = ptab[j + 1][plex['>']];
+ if (act == NET_ || act == NST_)
+ ptab[j + 1][plex['>']] = ptab[j + 1][plex['_']];
+ }
+ }
+}
+
+/* Lookup the value of the entry in pmap PTR whose key is KEY. */
+
+static UNIV pmaplookup(ptr, key)
+struct pmap *ptr;
+char *key;
+{
+ for (; ptr->name; ptr++)
+ if (strcmp(key, ptr->name) == 0)
+ return ptr->value;
+ return 0;
+}
+
+/* Return an ASCII representation of N. */
+
+static UNCH *ltous(n)
+long n;
+{
+ static char buf[sizeof(long)*3 + 2];
+ sprintf(buf, "%ld", n);
+ return (UNCH *)buf;
+}
+
+VOID sgmlwrsd(fp)
+FILE *fp;
+{
+ int i;
+ int changed;
+ char *p;
+ char uc[256]; /* upper case characters (with different lower
+ case characters) */
+ char lcletter[256]; /* LC letters: a-z */
+
+ fprintf(fp, "<!SGML \"%s\"\n", standard);
+ fprintf(fp, "CHARSET\nBASESET \"%s//CHARSET %s//%s\"\nDESCSET\n",
+ SYSTEM_CHARSET_OWNER,
+ SYSTEM_CHARSET_DESCRIPTION,
+ SYSTEM_CHARSET_DESIGNATING_SEQUENCE);
+
+ if (!done_nonsgml) {
+ done_nonsgml = 1;
+ for (i = 0; i < 256; i++)
+ if ((char_flags[i] & (CHAR_SIGNIFICANT | CHAR_SHUNNED))
+ == CHAR_SHUNNED)
+ char_flags[i] |= CHAR_NONSGML;
+ }
+ i = 0;
+ while (i < 256) {
+ int j;
+ for (j = i + 1; j < 256; j++)
+ if ((char_flags[j] & CHAR_NONSGML)
+ != (char_flags[i] & CHAR_NONSGML))
+ break;
+ if (char_flags[i] & CHAR_NONSGML)
+ fprintf(fp, "%d %d UNUSED\n", i, j - i);
+ else
+ fprintf(fp, "%d %d %d\n", i, j - i, i);
+ i = j;
+ }
+ fprintf(fp, "CAPACITY\n");
+ changed = 0;
+ for (i = 0; i < NCAPACITY; i++)
+ if (refcapset[i] != sd.capacity[i]) {
+ if (!changed) {
+ fprintf(fp, "SGMLREF\n");
+ changed = 1;
+ }
+ fprintf(fp, "%s %ld\n", captab[i], sd.capacity[i]);
+ }
+ if (!changed)
+ fprintf(fp, "PUBLIC \"%s\"\n", capset_map[0].name);
+ fprintf(fp, "SCOPE DOCUMENT\n");
+
+ fprintf(fp, "SYNTAX\nSHUNCHAR");
+ for (i = 0; i < 256; i++)
+ if (char_flags[i] & CHAR_SHUNNED)
+ fprintf(fp, " %d", i);
+ fprintf(fp, "\n");
+ fprintf(fp, "BASESET \"%s//CHARSET %s//%s\"\nDESCSET 0 256 0\n",
+ SYSTEM_CHARSET_OWNER,
+ SYSTEM_CHARSET_DESCRIPTION,
+ SYSTEM_CHARSET_DESIGNATING_SEQUENCE);
+
+ fprintf(fp, "FUNCTION\nRE 13\nRS 10\nSPACE 32\nTAB SEPCHAR 9\n");
+
+ MEMZERO((UNIV)uc, 256);
+ for (i = 0; i < 256; i++)
+ if (lextran[i] != i)
+ uc[lextran[i]] = 1;
+
+ MEMZERO((UNIV)lcletter, 256);
+ for (p = "abcdefghijklmnopqrstuvwxyz"; *p; p++)
+ lcletter[(unsigned char)*p]= 1;
+
+ fprintf(fp, "NAMING\n");
+ fputs("LCNMSTRT \"", fp);
+ for (i = 0; i < 256; i++)
+ if (lextoke[i] == NMS && !uc[i] && !lcletter[i])
+ fprintf(fp, "&#%d;", i);
+ fputs("\"\n", fp);
+ fputs("UCNMSTRT \"", fp);
+ for (i = 0; i < 256; i++)
+ if (lextoke[i] == NMS && !uc[i] && !lcletter[i])
+ fprintf(fp, "&#%d;", lextran[i]);
+ fputs("\"\n", fp);
+ fputs("LCNMCHAR \"", fp);
+ for (i = 0; i < 256; i++)
+ if (lextoke[i] == NMC && !uc[i])
+ fprintf(fp, "&#%d;", i);
+ fputs("\"\n", fp);
+ fputs("UCNMCHAR \"", fp);
+ for (i = 0; i < 256; i++)
+ if (lextoke[i] == NMC && !uc[i])
+ fprintf(fp, "&#%d;", lextran[i]);
+ fputs("\"\n", fp);
+
+ fprintf(fp, "NAMECASE\nGENERAL %s\nENTITY %s\n",
+ sd.namecase[0] ? "YES" : "NO",
+ sd.namecase[1] ? "YES" : "NO");
+ fprintf(fp, "DELIM\nGENERAL SGMLREF\nSHORTREF %s\n",
+ sd.shortref ? "SGMLREF" : "NONE");
+ fprintf(fp, "NAMES SGMLREF\n");
+ if (newkey) {
+ /* The reference key was saved in newkey. */
+ for (i = 0; i < NKEYS; i++)
+ if (newkey[i][0])
+ fprintf(fp, "%s %s\n", newkey[i], key[i]);
+ }
+ fprintf(fp, "QUANTITY SGMLREF\n");
+ if (quantity_changed)
+ for (i = 0; i < NQUANTITY; i++)
+ if (quantity_changed[i])
+ fprintf(fp, "%s %d\n", quantity_names[i], sd.quantity[i]);
+ fprintf(fp,
+ "FEATURES\nMINIMIZE\nDATATAG NO OMITTAG %s RANK NO SHORTTAG %s\n",
+ sd.omittag ? "YES" : "NO",
+ sd.shorttag ? "YES" : "NO");
+ fprintf(fp, "LINK SIMPLE NO IMPLICIT NO EXPLICIT NO\n");
+ fprintf(fp, "OTHER CONCUR NO ");
+ if (sd.subdoc > 0)
+ fprintf(fp, "SUBDOC YES %ld ", sd.subdoc);
+ else
+ fprintf(fp, "SUBDOC NO ");
+ fprintf(fp, "FORMAL %s\n", sd.formal ? "YES" : "NO");
+ fprintf(fp, "APPINFO NONE");
+ fprintf(fp, ">\n");
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgmldecl.h b/usr.bin/sgmls/sgmls/sgmldecl.h
new file mode 100644
index 0000000..296bdb8
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmldecl.h
@@ -0,0 +1,84 @@
+/* sgmldecl.h: SGML declaration parsing. */
+
+#define QATTCNT 0
+#define QATTSPLEN 1
+#define QBSEQLEN 2
+#define QDTAGLEN 3
+#define QDTEMPLEN 4
+#define QENTLVL 5
+#define QGRPCNT 6
+#define QGRPGTCNT 7
+#define QGRPLVL 8
+#define QLITLEN 9
+#define QNAMELEN 10
+#define QNORMSEP 11
+#define QPILEN 12
+#define QTAGLEN 13
+#define QTAGLVL 14
+
+#define NQUANTITY (QTAGLVL+1)
+
+#define TOTALCAP 0
+#define ENTCAP 1
+#define ENTCHCAP 2
+#define ELEMCAP 3
+#define GRPCAP 4
+#define EXGRPCAP 5
+#define EXNMCAP 6
+#define ATTCAP 7
+#define ATTCHCAP 8
+#define AVGRPCAP 9
+#define NOTCAP 10
+#define NOTCHCAP 11
+#define IDCAP 12
+#define IDREFCAP 13
+#define MAPCAP 14
+#define LKSETCAP 15
+#define LKNMCAP 16
+
+extern char *captab[];
+
+struct sgmldecl {
+ long capacity[NCAPACITY];
+ long subdoc;
+ UNCH formal;
+ UNCH omittag;
+ UNCH shorttag;
+ UNCH shortref;
+ UNCH namecase[2]; /* case translation of general/entity names */
+ int quantity[NQUANTITY];
+};
+
+extern struct sgmldecl sd;
+
+#define OMITTAG (sd.omittag)
+#define SUBDOC (sd.subdoc)
+#define SHORTTAG (sd.shorttag)
+#define FORMAL (sd.formal)
+
+#define ATTCNT (sd.quantity[QATTCNT])
+#define ATTSPLEN (sd.quantity[QATTSPLEN])
+#define BSEQLEN (sd.quantity[QBSEQLEN])
+#define ENTLVL (sd.quantity[QENTLVL])
+#define GRPGTCNT (sd.quantity[QGRPGTCNT])
+#define GRPCNT (sd.quantity[QGRPCNT])
+#define GRPLVL (sd.quantity[QGRPLVL])
+#define LITLEN (sd.quantity[QLITLEN])
+#define NAMELEN (sd.quantity[QNAMELEN])
+#define NORMSEP (sd.quantity[QNORMSEP])
+#define PILEN (sd.quantity[QPILEN])
+#define TAGLEN (sd.quantity[QTAGLEN])
+#define TAGLVL (sd.quantity[QTAGLVL])
+
+#define NAMECASE (sd.namecase[0])
+#define ENTCASE (sd.namecase[1])
+
+#define YES 1
+#define NO 0
+
+#define UNUSED -1
+#define UNKNOWN -2
+#define UNDESC -3
+#define UNKNOWN_SET -4
+
+extern int asciicharset[];
diff --git a/usr.bin/sgmls/sgmls/sgmlfnsm.h b/usr.bin/sgmls/sgmls/sgmlfnsm.h
new file mode 100644
index 0000000..0d617fb
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlfnsm.h
@@ -0,0 +1,129 @@
+/* SGMLFNSM.H: SGML function declarations (ANSI prototypes). */
+VOID adlfree P((struct ad *, int));
+VOID adlval P((int,struct etd *));
+VOID aenttst P((int, UNCH *));
+int allhit P((struct thdr *,unsigned long *,int,int));
+VOID ambig P((void));
+VOID ambigfree P((void));
+int amemget P((struct ad *,int,UNCH *));
+int anmget P((int,UNCH *));
+int anmtgrp P((struct parse *,struct ad *,int,UNS *,int));
+int antvget P((int,UNCH *,UNCH **));
+int anyhit P((unsigned long *));
+int attval P((int,UNCH *,int,struct ad *));
+VOID charrefa P((UNCH *));
+int charrefn P((UNCH *, struct parse *));
+int context P((struct etd *,struct thdr *,struct mpos *,UNCH *,int));
+struct etd **copygrp P((struct etd **,unsigned int));
+int datachar P((int, struct parse *));
+struct dcncb *dcnfind P((UNCH *));
+VOID destack P((void));
+int econtext P((struct thdr *,struct mpos *,UNCH *));
+VOID endprolog P((void));
+struct entity *entfind P((UNCH *));
+int entopen P((struct entity *));
+/* VOID eposset P((void)); NOT YET IN USE. */
+VOID error P((struct error *));
+VOID errorinit P((struct error *, unsigned, unsigned));
+int etag P((void));
+int etagetd P((struct parse *));
+VOID etdadl P((struct etd *));
+VOID etdcan P((UNCH *));
+struct etd *etddef P((UNCH *));
+struct etd *etdref P((UNCH *));
+VOID exclude P((void));
+VOID fileclos P((void));
+VOID filecont P((void));
+VOID fileopen P((void));
+VOID filepend P((int));
+VOID fileread P((void));
+VOID filerr P((unsigned, UNCH *));
+VOID fixdatt P((struct dcncb *));
+struct parse *getpcb P((int));
+int groupopt P((struct thdr *,struct mpos *));
+int groupreq P((struct etd *,struct thdr *,struct mpos *));
+int grpsz P((struct thdr *,int));
+int hash P((UNCH *,int));
+struct hash *hfind P((struct hash **,UNCH *,int));
+struct hash *hin P((struct hash **,UNCH *,int,unsigned int));
+int iddef P((UNCH *));
+VOID idrck P((void));
+struct fwdref *idref P((UNCH *));
+VOID idreftst P((int,UNCH *));
+int ingrp P((struct etd **,struct etd *));
+VOID initatt P((struct ad *));
+int mapsrch P((struct map *,UNCH *));
+VOID mdadl P((UNCH *));
+int mdattdef P((int, int));
+VOID mddtde P((UNCH *));
+VOID mddtds P((UNCH *));
+VOID mdelem P((UNCH *));
+VOID mdentity P((UNCH *));
+VOID mderr P((unsigned int,UNCH *,UNCH *));
+struct parse *mdms P((UNCH *,struct parse *));
+int mdmse P((void));
+VOID mdnadl P((UNCH *));
+VOID mdnot P((UNCH *));
+VOID mdsrmdef P((UNCH *));
+VOID mdsrmuse P((UNCH *));
+int netetd P((struct parse *));
+VOID newtoken P((struct thdr *,struct mpos *,UNCH *));
+int nstetd P((void));
+UNCH *ntoa P((int));
+int offbit P((unsigned long *,int,int));
+int parsecon P((UNCH *,struct parse *));
+int parsefpi P((struct fpi *));
+struct thdr *parsegcm P((struct parse *,struct thdr *,struct thdr *));
+VOID parselit P((UNCH *,struct parse *,unsigned int,UNCH));
+struct thdr *parsemod P((int));
+int parsepro P((void));
+VOID parseseq P((UNCH *,int));
+VOID parsetag P((struct parse *));
+int parseval P((UNCH *,unsigned int,UNCH *));
+int pexmex P((struct etd *));
+unsigned int ptrsrch P((UNIV *,UNIV));
+UNCH *pubfield P((UNCH *,UNCH *,UNCH,UNS *));
+UNCH *replace P((UNCH *,UNCH *));
+UNCH *sandwich P((UNCH *,UNCH *,UNCH *));
+UNIV saverr P((unsigned int,struct parse *,UNCH *,UNCH *));
+VOID scbset P((void));
+VOID sdinit P((void));
+VOID setcurchar P((int));
+VOID setdtype P((void));
+int sgmlact P((UNCH));
+int sgmldecl P((void));
+VOID sgmlerr P((unsigned int,struct parse *,UNCH *,UNCH *));
+int shortref P((int,struct parse *));
+struct srh *srhfind P((UNCH *));
+VOID stack P((struct etd *));
+int stag P((int));
+int stagetd P((struct parse *));
+VOID startdtd P((void));
+UNCH *savenm P((UNCH *));
+UNCH *savestr P((UNCH *));
+VOID storedatt P((PNE));
+VOID svderr P((UNIV));
+VOID synerr P((unsigned int,struct parse *));
+int testend P((struct thdr *,struct mpos *,int,int));
+int tokenopt P((struct thdr *,struct mpos *));
+int tokenreq P((struct etd *,struct thdr *,struct mpos *));
+UNS vallen P((int,int,UNCH *));
+struct dcncb *dcndef P((UNCH *));
+struct entity *entdef P((UNCH *,UNCH,union etext *));
+int entget P((void));
+int entref P((UNCH *));
+struct etd *etdset P((struct etd *,UNCH,struct thdr *,struct etd **,
+ struct etd **, struct entity **));
+struct hash *hout P((struct hash **,UNCH *,int));
+struct fpi *mdextid P((UNCH *,struct fpi *,UNCH *,UNCH *,struct ne *));
+int parse P((struct parse *));
+struct ad *parseatt P((struct ad *,UNCH *));
+unsigned int parsegrp P((struct etd **,struct parse *, UNCH *));
+unsigned int parsngrp P((struct dcncb **,struct parse *, UNCH *));
+int parsemd P((UNCH *,int,struct parse *,unsigned int));
+UNCH *parsenm P((UNCH *,int));
+UNCH *parsetkn P((UNCH *,UNCH,int));
+UNCH *s2valnm P((UNCH *,UNCH *,UNCH,int));
+struct srh *srhdef P((UNCH *));
+int tokdata P((UNCH *, int));
+struct entity *usedef P((UNCH *));
diff --git a/usr.bin/sgmls/sgmls/sgmlincl.h b/usr.bin/sgmls/sgmls/sgmlincl.h
new file mode 100644
index 0000000..c4eb5cc
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlincl.h
@@ -0,0 +1,20 @@
+/* SGMLINCL.H: Include file for parser core. */
+#ifndef SGMLINCL /* Don't include this file more than once. */
+#define SGMLINCL 1
+#include "config.h"
+#include "std.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "action.h" /* Action names for all parsing. */
+#include "adl.h" /* Definitions for attribute list processing. */
+#include "error.h" /* Symbols for error codes. */
+#include "etype.h" /* Definitions for element type processing. */
+#include "keyword.h" /* Definitions for keyword processing. */
+#include "lextoke.h" /* Symbols for tokenization lexical classes. */
+#include "source.h" /* Templates for source entity control blocks. */
+#include "synxtrn.h" /* Declarations for concrete syntax constants. */
+#include "sgmlxtrn.h" /* External variable declarations. */
+#include "trace.h" /* Declarations for internal trace functions. */
+#include "sgmlmain.h"
+#include "sgmlaux.h"
+#include "sgmlfnsm.h" /* ANSI C: Declarations for SGML functions. */
+#endif /* ndef SGMLINCL */
diff --git a/usr.bin/sgmls/sgmls/sgmlio.c b/usr.bin/sgmls/sgmls/sgmlio.c
new file mode 100644
index 0000000..3db1d0d
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlio.c
@@ -0,0 +1,384 @@
+/* sgmlio.c -
+ IO functions for core parser.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+/* SGML must see a file in which records start with RS and end with
+ RE, and EOFCHAR (Ctl-Z) is present at the end. This module must
+ supply these characters if they are not naturally present in the
+ file. SGML will open two files at a time: when an entity is
+ nested, the new file is opened before closing the old in order to
+ make sure the open is successful. If it is, the original open file
+ is closed temporarily (IOPEND); when the stack is popped, the new
+ file is closed and the original file is re-opened (IOCONT). SGML
+ will check error returns for the initial open of a file and all
+ reads, and for re-openings when the stack is popped, but not for
+ closes. Returning <0 indicates an error; 0 or more is a successful
+ operation, except for IOREAD where the return value is the number
+ of characters read, and must exceed 0 to be successful. The first
+ READ must always be successful, and normally consists of just
+ priming the buffer with EOBCHAR (or RS EOBCHAR). SGMLIO must
+ assure that there is an EOBCHAR at the end of each block read,
+ except for the last block of the entity, which must have an
+ EOFCHAR.
+
+ SGML views an entity as a contiguous whole, without regard to its
+ actual form of storage. SGMLIO supports entities that are
+ equivalent to a single file of one or more records, or to a
+ concatenation of files.
+*/
+
+/* Uses only stream I/O. This module should be portable to most ANSI
+ systems. */
+/* We try to ensure that if an IO operation fails, then errno will contain
+ a meaningful value (although it may be zero.) */
+
+#include "config.h"
+#ifdef HAVE_O_NOINHERIT
+#include <fcntl.h>
+#include <io.h>
+#endif /* HAVE_O_NOINHERIT */
+
+#include "sgmlaux.h" /* Include files for auxiliary functions.. */
+
+#ifdef HAVE_O_NOINHERIT
+#define FOPENR(file) nifopen(file)
+FILE *nifopen P((char *));
+#else /* not HAVE_O_NOINHERIT */
+#define FOPENR(file) fopen((file), "r")
+#endif /* not HAVE_O_NOINHERIT */
+
+struct iofcb { /* I/O file control block. */
+ FILE *fp; /* File handle. */
+ fpos_t off; /* Offset in file of current read block. */
+ char *next; /* Next file (NULL if no more). */
+ char *file; /* Current file (no length byte). */
+ int pendoff; /* Offset into line when file suspended. */
+ char bol; /* Non-zero if currently at beginning of line. */
+ char first; /* Non-zero if the first read. */
+ char wasbol; /* Non-zero if current block was at beginning of line. */
+ char canseek;
+ UNCH *pendbuf; /* Saved partial buffer for suspended file
+ that can't be closed and reopened. */
+};
+
+static char *lastfile; /* The name of the last file closed. */
+static int bufsize; /* Size of buffer passed to ioread(). */
+static char ismagic[256]; /* Table of magic chars that need to be prefixed
+ by DELNONCH. */
+static int stdinused = 0;
+
+static char *nextstr P((char *)); /* Iterate over list of strings. */
+static FILE *openfile P((char *, char *));
+static int closefile P((FILE *));
+static int isreg P((FILE *));
+
+VOID ioinit(swp)
+struct switches *swp;
+{
+ ismagic[EOBCHAR] = 1;
+ ismagic[EOFCHAR] = 1;
+ ismagic[EOS] = 1;
+ ismagic[(UNCH)DELNONCH] = 1;
+ ismagic[(UNCH)GENRECHAR] = 1;
+ bufsize = swp->swbufsz;
+}
+
+int ioopen(id, pp)
+UNIV id;
+UNIV *pp;
+{
+ struct iofcb *f;
+ char *s;
+ errno = 0;
+ if (!id)
+ return -1;
+ s = id;
+ if (!*s)
+ return -1;
+ f = (struct iofcb *)rmalloc((UNS)sizeof(struct iofcb));
+ f->file = s;
+ f->next = nextstr(s);
+ errno = 0;
+ f->fp = openfile(f->file, &f->canseek);
+ f->bol = 1;
+ f->first = 1;
+ f->pendbuf = 0;
+ *pp = (UNIV)f;
+ return f->fp ? 1 : -1;
+}
+
+VOID ioclose(p)
+UNIV p;
+{
+ struct iofcb *f = (struct iofcb *)p;
+ if (f->fp)
+ closefile(f->fp);
+ lastfile = f->file;
+ frem((UNIV)f);
+}
+
+VOID iopend(p, off, buf)
+UNIV p;
+int off;
+UNCH *buf;
+{
+ struct iofcb *f = (struct iofcb *)p;
+ if (!f->canseek) {
+ UNCH *s;
+ for (s = buf + off; *s != EOFCHAR && *s != EOBCHAR; s++)
+ ;
+ s++;
+ f->pendbuf = (UNCH *)rmalloc((UNS)(s - buf - off));
+ memcpy((UNIV)f->pendbuf, (UNIV)(buf + off), (UNS)(s - buf - off));
+ return;
+ }
+ f->bol = 0;
+ if (f->wasbol) {
+ if (off == 0)
+ f->bol = 1;
+ else
+ off--;
+ }
+ f->pendoff = off;
+ if (f->fp) {
+ fclose(f->fp);
+ f->fp = 0;
+ }
+}
+
+int iocont(p)
+UNIV p;
+{
+ struct iofcb *f = (struct iofcb *)p;
+ int c = EOF;
+ int off = f->pendoff;
+
+ if (!f->canseek)
+ return 0;
+
+ errno = 0;
+ f->fp = FOPENR(f->file);
+ if (!f->fp)
+ return -1;
+ if (fsetpos(f->fp, &f->off))
+ return -1;
+ while (--off >= 0) {
+ c = getc(f->fp);
+ if (c != EOF && ismagic[c])
+ off--;
+ }
+ if (c == '\n')
+ f->bol = 1;
+ if (ferror(f->fp))
+ return -1;
+ return 0;
+}
+
+/* Return -1 on error, otherwise the number of bytes read. The
+strategy is to concatenate the files, insert a RS at the beginning of
+each line, and change each '\n' into a RE. The returned data
+shouldn't cross a file boundary, otherwise error messages might be
+inaccurate. The first read must always succeed. */
+
+int ioread(p, buf, newfilep)
+UNIV p;
+UNCH *buf;
+int *newfilep;
+{
+ int i = 0;
+ struct iofcb *f = (struct iofcb *)p;
+ FILE *fp;
+ int c;
+
+ *newfilep = 0;
+ if (f->first) {
+ buf[i] = EOBCHAR;
+ f->first = 0;
+ return 1;
+ }
+ if (f->pendbuf) {
+ for (i = 0;
+ (buf[i] = f->pendbuf[i]) != EOBCHAR && buf[i] != EOFCHAR;
+ i++)
+ ;
+ frem((UNIV)f->pendbuf);
+ f->pendbuf = 0;
+ return i + 1;
+ }
+ fp = f->fp;
+ for (;;) {
+ errno = 0;
+ if (f->canseek && fgetpos(fp, &f->off))
+ f->canseek = 0;
+ errno = 0;
+ c = getc(fp);
+ if (c != EOF)
+ break;
+ if (ferror(fp))
+ return -1;
+ if (closefile(fp) == EOF)
+ return -1;
+ if (!f->next){
+ f->fp = 0;
+ buf[0] = EOFCHAR;
+ return 1;
+ }
+ f->file = f->next;
+ f->next = nextstr(f->next);
+ *newfilep = 1;
+ errno = 0;
+ fp = f->fp = openfile(f->file, &f->canseek);
+ if (!fp)
+ return -1;
+ f->bol = 1;
+ }
+ if (f->bol) {
+ f->bol = 0;
+ buf[i++] = RSCHAR;
+ f->wasbol = 1;
+ }
+ else
+ f->wasbol = 0;
+ errno = 0;
+ for (;;) {
+ if (c == '\n') {
+ f->bol = 1;
+ buf[i++] = RECHAR;
+ break;
+ }
+ if (ismagic[c]) {
+ buf[i++] = DELNONCH;
+ buf[i++] = SHIFTNON(c);
+ }
+ else
+ buf[i++] = c;
+ if (i >= bufsize - 2)
+ break;
+ c = getc(fp);
+ if (c == EOF) {
+ if (ferror(fp))
+ return -1;
+ /* This is in the middle of a line. */
+ break;
+ }
+ }
+ buf[i++] = EOBCHAR;
+ return i;
+}
+
+static char *nextstr(p)
+char *p;
+{
+ p = strchr(p, '\0');
+ return *++p ? p : 0;
+}
+
+/* Return the filename associated with p. If p is NULL, return the filename
+of the last file closed. */
+
+char *ioflid(p)
+UNIV p;
+{
+ if (!p)
+ return lastfile;
+ return ((struct iofcb *)p)->file;
+}
+
+static
+FILE *openfile(name, seekp)
+char *name;
+char *seekp;
+{
+ FILE *fp;
+ if (strcmp(name, STDINNAME) == 0) {
+ if (stdinused)
+ return 0;
+ stdinused = 1;
+ *seekp = 0;
+ return stdin;
+ }
+ fp = FOPENR(name);
+ if (fp)
+ *seekp = isreg(fp);
+ return fp;
+}
+
+/* Return -1 on error, 0 otherwise. */
+
+static
+int closefile(fp)
+FILE *fp;
+{
+ if (fp == stdin) {
+ stdinused = 0;
+ clearerr(fp);
+ return 0;
+ }
+ else
+ return fclose(fp);
+}
+
+#ifdef HAVE_O_NOINHERIT
+
+/* This is the same as fopen(name, "r") except that it tells DOS that
+the file descriptor should not be inherited by child processes. */
+
+FILE *nifopen(name)
+char *name;
+{
+ int fd = open(name, O_RDONLY|O_NOINHERIT|O_TEXT);
+ if (fd < 0)
+ return 0;
+ return fdopen(fd, "r");
+}
+
+#endif /* HAVE_O_NOINHERIT */
+
+#ifdef HAVE_SYS_STAT_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISREG
+#ifdef S_IFMT
+#ifdef S_IFREG
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif /* S_IFREG */
+#endif /* S_IFMT */
+#endif /* not S_ISREG */
+
+#endif /* HAVE_SYS_STAT_H */
+
+/* Return 1 if fp might be associated with a regular file. 0
+otherwise. We check this because on many Unix systems lseek() will
+succeed on a (pseudo-)terminal although terminals aren't seekable in
+the way we need. */
+
+static
+int isreg(fp)
+FILE *fp;
+{
+#ifdef S_ISREG
+ struct stat sb;
+
+ /* This assumes that a system that has S_ISREG will also have
+ fstat() and fileno(). */
+ if (fstat(fileno(fp), &sb) == 0)
+ return S_ISREG(sb.st_mode);
+#endif /* S_ISREG */
+ return 1;
+}
+
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+comment-column: 30
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgmlmain.h b/usr.bin/sgmls/sgmls/sgmlmain.h
new file mode 100644
index 0000000..3911f76
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlmain.h
@@ -0,0 +1,101 @@
+/* SGMLMAIN: Main interface to SGML services.
+
+Preprocessor variable names are the only supported interface
+to data maintained by SGML. They are defined in this file or in adl.h.
+*/
+/* Return control block types (RCBTYPE) from calls to parser (SGML):
+ Names and strings follow the convention for the IPBs.
+*/
+enum sgmlevent {
+ SGMLEOD, /* End of document. */
+ SGMLDAF, /* Data found. */
+ SGMLSTG, /* Start-tag found. */
+ SGMLETG, /* End-tag found. */
+ SGMLREF, /* Record end found. */
+ SGMLPIS, /* Processing instruction (string). */
+ SGMLAPP /* APPINFO (string) */
+};
+
+struct rcbdata { /* Return control block: DAF EOD REF PIS APP. */
+ UNS contersw; /* 1=context error; 2,4,8=data type; 0=not. */
+ UNS datalen; /* Length of data or PI (0=single nonchar). */
+ UNCH *data; /* Data, PI, single nonSGML, or NDATA ecb ptr. */
+};
+
+struct rcbtag { /* Return control block for STG and ETG. */
+ UNS contersw; /* 1=context error; 2=NET enabled; 0/0=not. */
+ UNS tagmin; /* Minim: NONE NULL NET DATA; implied by S/ETAG */
+ UNCH *curgi; /* Start-tag (or end-tag) GI. */
+ union {
+ struct ad *al; /* Start-tag: attribute list. */
+ UNCH *oldgi; /* End-tag: resumed GI. */
+ } ru;
+ struct ad *lal; /* Start-tag: link attribute list (UNUSED). */
+ UNS format; /* Format class for default processing. */
+ struct etd *tagreal; /* Dummy etd or ptr to GI that implied this tag.*/
+ int etictr; /* Number of elements on stack with NET enabled.*/
+ UNCH *srmnm; /* Current SHORTREF map name (NULL=#EMPTY). */
+};
+
+/* Accessors for rcbdata and rcbtag. */
+/* Datatype abbreviations: C=unsigned char S=string U=unsigned int L=4 bytes
+ A=array P=ptr to structure N=name (see sgmlcb.h)
+*/
+/* Data control block fields: processing instructions (SGMLPIS).
+*/
+#define PDATA(d) ((d).data) /*S PI string. */
+#define PDATALEN(d) ((d).datalen) /*U Length of PI string. */
+#define PIESW(d) (((d).contersw & 4)) /*U 1=PIDATA entity returned. */
+/* Data control block fields: other data types.
+*/
+#define CDATA(d) ((d).data) /*S CDATA content string. */
+#define CDATALEN(d) ((d).datalen) /*U Length of CDATA content string. */
+#define CONTERSW(d) (((d).contersw &1))/*U 1=CDATA or TAG out of context. */
+#define CDESW(d) (((d).contersw & 2)) /*U 1=CDATA entity returned. */
+#define SDESW(d) (((d).contersw & 4)) /*U 1=SDATA entity returned. */
+#define NDESW(d) (((d).contersw & 8)) /*U 1=NDATA entity returned. */
+#define NEPTR(d) ((PNE)(d).data) /*P Ptr to NDATA control block. */
+#define MARKUP(d) ((d).data) /*A Markup delimiter strings. */
+#define DTYPELEN(d) ((d).datalen) /*U Length of doc type name +len+EOS. */
+#define DOCTYPE(d) ((d).data) /*S Document type name (with len+EOS). */
+#define ADATA(d) ((d).data) /*S APPINFO */
+#define ADATALEN(d) ((d).datalen) /*U Length of APPINFO string. */
+/* Tag control block fields.
+*/
+#define ALPTR(t) ((t).ru.al) /*P Ptr to SGML attribute list. */
+#define CURGI(t) ((t).curgi+1) /*N GI of started or ended element. */
+#define OLDGI(t) ((t).ru.oldgi) /*S GI of resumed element. */
+#define TAGMIN(t) (t).tagmin /*U Minimization for current tag. */
+#define TAGREAL(t) ((t).tagreal) /*P Dummy etd that implied this tag. */
+#define TAGRLNM(t) ((UNCH *)(t).tagreal) /*P GI of tag that implied this tag.*/
+#define ETISW(t) (((t).contersw & 2)) /*U 1=NET delimiter enabled by ETI. */
+#define PEXSW(t) (((t).contersw & 4)) /*U 1=Element was plus exception. */
+#define MTYSW(t) (((t).contersw & 8)) /*U 1=Element is empty. */
+#define ETICTR(t) ((t).etictr) /*U Number of active NET delimiters. */
+#define SRMNM(t) ((t).srmnm) /*S Name of current SHORTREF map. */
+#define SRMCNT(t) ((t).contersw) /*U Number of SHORTREF maps defined. */
+#define FORMAT(t) ((t).format) /*U Format class.*/
+
+/* These function names are chosen so as to be distinct in the first 6
+letters. */
+
+/* Initialize. */
+struct markup *sgmlset P((struct switches *));
+/* Cleanup and return capacity usage statistics. */
+VOID sgmlend P((struct sgmlcap *));
+/* Set document entity. */
+int sgmlsdoc P((UNIV));
+/* Get entity. */
+int sgmlgent P((UNCH *, PNE *, UNCH **));
+/* Mark an entity. Return is non-zero if already marked.*/
+int sgmlment P((UNCH *));
+/* Get the next sgml event. */
+enum sgmlevent sgmlnext P((struct rcbdata *, struct rcbtag *));
+/* Get the error count. */
+int sgmlgcnterr P((void));
+/* Get the current location. */
+int sgmlloc P((unsigned long *, char **));
+/* Write out the SGML declaration. */
+VOID sgmlwrsd P((FILE *));
+/* Note subdocument capacity usage. */
+VOID sgmlsubcap P((long *));
diff --git a/usr.bin/sgmls/sgmls/sgmlmsg.c b/usr.bin/sgmls/sgmls/sgmlmsg.c
new file mode 100644
index 0000000..454bc3e
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlmsg.c
@@ -0,0 +1,514 @@
+/* sgmlmsg.c -
+ message handling for core parser
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+#include "sgmlaux.h"
+#include "msg.h"
+
+static nl_catd catd;
+
+#define TEXT_SET 1 /* message set number for text of messages */
+#define HEADER_SET 2 /* message set number for header strings */
+#define PARM_SET 3 /* message set number for special parameters */
+
+#ifdef HAVE_EXTENDED_PRINTF
+#define xfprintf fprintf
+#else
+extern int xfprintf VP((FILE *, char *,...));
+#endif
+
+#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
+
+static char *gettext P((int));
+static char *getheader P((int));
+static char *getparm P((int));
+static VOID elttrace P((FILE *, int));
+static int printit P((FILE *, struct error *));
+static char *transparm P((UNCH *, char *));
+static VOID spaces P((FILE *, int));
+
+#define PARMBUFSIZ 50
+static char parmbuf[PARMBUFSIZ*2];
+static char *parmbuf1 = parmbuf;
+static char *parmbuf2 = parmbuf + PARMBUFSIZ;
+
+static char *prog; /* program name */
+static int sweltr; /* non-zero means print an element trace */
+static int swenttr; /* non-zero means print an entity trace */
+static int cnterr = 0;
+static VOID (*die) P((void));
+
+static char *headers[] = {
+"In file included",
+"SGML error", /* parameters: type, severity, number */
+"Unsupported feature", /* type U errors */
+"Error", /* for type R errors */
+"Warning", /* severity type I */
+" at %s, %.0sline %lu", /* ignore entity name and ccnt */
+" at entity %s, line %lu",
+"%.0s%.0s in declaration parameter %d", /* ignore first two parameters */
+"%.0s in declaration parameter %d", /* ignore first parameter */
+"%.0s", /* parse mode */
+" at end of file",
+" at end of entity",
+" at record start",
+" at record end",
+" at \"%c\"",
+" at \"\\%03o\"",
+" accessing \"%s\"",
+"Element structure:"
+};
+
+/* Indexes into headers[] */
+
+#define HDRPFX 0
+#define HDRALL 1
+#define HDRUNSUP 2
+#define HDRSYS 3
+#define HDRWARN 4
+#define HDRLOC 5
+#define HDRELOC 6
+#define HDRMD 7
+#define HDRMD2 8
+#define HDRMODE 9
+#define HDREOF 10
+#define HDREE 11
+#define HDRRS 12
+#define HDRRE 13
+#define HDRPRT 14
+#define HDRCTL 15
+#define HDRFIL 16
+#define HDRELT 17
+
+/* Special parameters (error::errsp) */
+static char *parms[] = {
+"character data",
+"element content",
+"mixed content",
+"replaceable character data",
+"tag close",
+"content model group",
+"content model occurrence indicator",
+"name group",
+"name token group",
+"system data",
+"parameter literal",
+"attribute value literal",
+"tokenized attribute value literal",
+"minimum literal",
+"markup declaration",
+"markup declaration comment",
+"ignored markup declaration",
+"declaration subset",
+"CDATA marked section",
+"IGNORE marked section",
+"RCDATA marked section",
+"prolog",
+"reference",
+"attribute specification list",
+"tokenized attribute value",
+"attribute specification list close",
+"SGML declaration",
+"attribute definition list",
+"document type",
+"element",
+"entity",
+"link type",
+"link set",
+"notation",
+"SGML",
+"short reference mapping",
+"link set use",
+"short reference use",
+};
+
+static FILE *tfp; /* temporary file for saved messages */
+
+struct saved {
+ long start;
+ long end;
+ char exiterr;
+ char countit;
+};
+
+VOID msgprint(e)
+struct error *e;
+{
+ if (printit(stderr, e))
+ ++cnterr;
+ fflush(stderr);
+ if (e->errtype == EXITERR) {
+ if (die) {
+ (*die)();
+ abort();
+ }
+ else
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* Save an error message. */
+
+UNIV msgsave(e)
+struct error *e;
+{
+ struct saved *sv;
+
+ sv = (struct saved *)rmalloc(sizeof(struct saved));
+ if (!tfp) {
+ tfp = tmpfile();
+ if (!tfp)
+ exiterr(160, (struct parse *)0);
+ }
+ sv->start = ftell(tfp);
+ sv->countit = (char)printit(tfp, e);
+ sv->end = ftell(tfp);
+ sv->exiterr = (char)(e->errtype == EXITERR);
+ return (UNIV)sv;
+}
+
+/* Print a saved error message. */
+
+VOID msgsprint(p)
+UNIV p;
+{
+ struct saved *sv = (struct saved *)p;
+ long cnt;
+
+ assert(p != 0);
+ assert(tfp != 0);
+ if (fseek(tfp, sv->start, SEEK_SET) < 0)
+ return;
+ /* Temporary files are opened in binary mode, so this is portable. */
+ cnt = sv->end - sv->start;
+ while (--cnt >= 0) {
+ int c = getc(tfp);
+ if (c == EOF)
+ break;
+ putc(c, stderr);
+ }
+ fflush(stderr);
+ if (sv->countit)
+ ++cnterr;
+ if (sv->exiterr)
+ exit(EXIT_FAILURE);
+}
+
+/* Free a sved error message. */
+
+VOID msgsfree(p)
+UNIV p;
+{
+ frem(p);
+}
+
+/* Return 1 if it should be counted as an error. */
+
+static int printit(efp, e)
+FILE *efp;
+struct error *e;
+{
+ int indent;
+ int countit;
+ int hdrcode;
+ int filelevel = -1, prevfilelevel = -1, toplevel;
+ struct location loc;
+ char type[2], severity[2];
+
+ assert(e->errnum < SIZEOF(messages));
+ assert(messages[e->errnum].text != NULL);
+ if (prog) {
+ fprintf(efp, "%s: ", prog);
+ indent = strlen(prog) + 2; /* don't rely on return value of fprintf */
+ /* Don't want to waste too much space on indenting. */
+ if (indent > 10)
+ indent = 4;
+ }
+ else
+ indent = 4;
+
+ for (toplevel = 0; getlocation(toplevel, &loc); toplevel++)
+ if (loc.filesw) {
+ prevfilelevel = filelevel;
+ filelevel = toplevel;
+ }
+ toplevel--;
+
+ if (e->errtype == FILERR) {
+ toplevel--;
+ filelevel = prevfilelevel;
+ }
+ if (swenttr && filelevel > 0) {
+ int level = 0;
+ int middle = 0; /* in the middle of a line */
+ do {
+ (void)getlocation(level, &loc);
+ if (loc.filesw) {
+ if (middle) {
+ fputs(":\n", efp);
+ spaces(efp, indent);
+ }
+ else
+ middle = 1;
+ xfprintf(efp, getheader(HDRPFX));
+ xfprintf(efp, getheader(HDRLOC), ioflid(loc.fcb),
+ loc.ename, loc.rcnt, loc.ccnt);
+ }
+ else if (middle)
+ xfprintf(efp, getheader(HDRELOC),
+ loc.ename, loc.rcnt + 1, loc.ccnt);
+ }
+ while (++level != filelevel);
+ if (middle) {
+ fputs(":\n", efp);
+ spaces(efp, indent);
+ }
+ }
+
+ /* We use strings for the type and severity,
+ so that the format can use %.0s to ignore them. */
+
+ type[0] = messages[e->errnum].type;
+ type[1] = '\0';
+ severity[0] = messages[e->errnum].severity;
+ severity[1] = '\0';
+
+ countit = (severity[0] != 'I');
+ if (!countit)
+ hdrcode = HDRWARN;
+ else if (type[0] == 'R')
+ hdrcode = HDRSYS;
+ else if (type[0] == 'U')
+ hdrcode = HDRUNSUP;
+ else
+ hdrcode = HDRALL;
+
+ xfprintf(efp, getheader(hdrcode), type, severity, e->errnum);
+
+ if (filelevel >= 0) {
+ (void)getlocation(filelevel, &loc);
+ xfprintf(efp, getheader(HDRLOC),
+ ioflid(loc.fcb), loc.ename, loc.rcnt, loc.ccnt);
+ while (filelevel < toplevel) {
+ ++filelevel;
+ if (swenttr) {
+ (void)getlocation(filelevel, &loc);
+ xfprintf(efp, getheader(HDRELOC),
+ loc.ename, loc.rcnt + 1, loc.ccnt);
+ }
+ }
+ }
+
+ /* It is necessary to copy the result of getparm() because
+ the specification of catgets() says in can return a
+ pointer to a static buffer which may get overwritten
+ by the next call to catgets(). */
+
+ switch (e->errtype) {
+ case MDERR:
+ strncpy(parmbuf, getparm(e->errsp), PARMBUFSIZ*2 - 1);
+ xfprintf(efp, getheader(HDRMD), parmbuf,
+ (e->subdcl ? e->subdcl : (UNCH *)""), e->parmno);
+ break;
+ case MDERR2:
+ /* no subdcl parameter */
+ strncpy(parmbuf, getparm(e->errsp), PARMBUFSIZ*2 - 1);
+ xfprintf(efp, getheader(HDRMD2), parmbuf, e->parmno);
+ break;
+ case DOCERR:
+ case EXITERR:
+ if (toplevel < 0)
+ break;
+ strncpy(parmbuf, getparm(e->errsp), PARMBUFSIZ*2 - 1);
+ xfprintf(efp, getheader(HDRMODE), parmbuf);
+ switch (loc.curchar) {
+ case EOFCHAR:
+ xfprintf(efp, getheader(HDREOF));
+ break;
+ case RSCHAR:
+ xfprintf(efp, getheader(HDRRS));
+ break;
+ case RECHAR:
+ xfprintf(efp, getheader(HDRRE));
+ break;
+ case DELNONCH:
+ xfprintf(efp, getheader(HDRCTL), UNSHIFTNON(loc.nextchar));
+ break;
+ case EOS:
+ xfprintf(efp, getheader(HDREE));
+ break;
+ case EOBCHAR:
+ break;
+ default:
+ if (ISASCII(loc.curchar) && isprint(loc.curchar))
+ xfprintf(efp, getheader(HDRPRT), loc.curchar);
+ else
+ xfprintf(efp, getheader(HDRCTL), loc.curchar);
+ break;
+ }
+ break;
+ case FILERR:
+ if (getlocation(toplevel + 1, &loc))
+ xfprintf(efp, getheader(HDRFIL), ioflid(loc.fcb));
+ break;
+ }
+ fputs(":\n", efp);
+
+ if (e->errtype == FILERR && e->sverrno != 0) {
+ char *errstr = strerror(e->sverrno);
+ UNS len = strlen(errstr);
+ /* Strip a trailing newline if there is one. */
+ if (len > 0 && errstr[len - 1] == '\n')
+ len--;
+ spaces(efp, indent);
+ for (; len > 0; len--, errstr++)
+ putc(*errstr, efp);
+ fputs(":\n", efp);
+ }
+
+ spaces(efp, indent);
+
+ xfprintf(efp, gettext(e->errnum),
+ transparm((UNCH *)e->eparm[0], parmbuf1),
+ transparm((UNCH *)e->eparm[1], parmbuf2));
+ putc('\n', efp);
+
+ if (sweltr)
+ elttrace(efp, indent);
+ return countit;
+}
+
+/* Print an element trace. */
+static VOID elttrace(efp, indent)
+FILE *efp;
+int indent;
+{
+ int i = 1;
+ UNCH *gi;
+
+ gi = getgi(i);
+ if (!gi)
+ return;
+ spaces(efp, indent);
+ xfprintf(efp, getheader(HDRELT));
+ do {
+ fprintf(efp, " %s", gi);
+ gi = getgi(++i);
+ } while (gi);
+ putc('\n', efp);
+}
+
+static VOID spaces(efp, indent)
+FILE *efp;
+int indent;
+{
+ while (--indent >= 0)
+ putc(' ', efp);
+}
+
+VOID msginit(swp)
+struct switches *swp;
+{
+ catd = swp->catd;
+ prog = swp->prog;
+ sweltr = swp->sweltr;
+ swenttr = swp->swenttr;
+ die = swp->die;
+}
+
+/* Return the error count. */
+
+int msgcnterr()
+{
+ return cnterr;
+}
+
+/* Transform a parameter into a form suitable for printing. */
+
+static char *transparm(s, buf)
+UNCH *s;
+char *buf;
+{
+ char *ptr;
+ int cnt;
+
+ if (!s)
+ return 0;
+
+ ptr = buf;
+ cnt = PARMBUFSIZ - 4; /* space for `...\0' */
+
+ while (*s) {
+ UNCH ch = *s++;
+ if (ch == DELNONCH) {
+ if (*s == '\0')
+ break;
+ ch = UNSHIFTNON(*s);
+ s++;
+ }
+ if (ch == DELCDATA || ch == DELSDATA)
+ ;
+ else if (ch == '\\') {
+ if (cnt < 2)
+ break;
+ *ptr++ = '\\';
+ *ptr++ = '\\';
+ cnt -= 2;
+ }
+ else if (ISASCII(ch) && isprint(ch)) {
+ if (cnt < 1)
+ break;
+ *ptr++ = ch;
+ cnt--;
+ }
+ else {
+ if (cnt < 4)
+ break;
+ sprintf(ptr, "\\%03o", ch);
+ ptr += 4;
+ cnt -= 4;
+ }
+ }
+ if (!*s)
+ *ptr = '\0';
+ else
+ strcpy(ptr, "...");
+ return buf;
+}
+
+/* The message and set numbers in the catgets function must be > 0. */
+
+static char *gettext(n)
+int n;
+{
+ assert(n > 0 && n < SIZEOF(messages));
+ assert(messages[n].text != 0);
+ return catgets(catd, TEXT_SET, n, messages[n].text);
+}
+
+static char *getheader(n)
+int n;
+{
+ assert(n >= 0 && n < SIZEOF(headers));
+ return catgets(catd, HEADER_SET, n + 1, headers[n]);
+}
+
+static char *getparm(n)
+int n;
+{
+ assert(n >= 0 && n < SIZEOF(parms));
+ return catgets(catd, PARM_SET, n + 1, parms[n]);
+}
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/sgmls.1 b/usr.bin/sgmls/sgmls/sgmls.1
new file mode 100644
index 0000000..b9967a0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmls.1
@@ -0,0 +1,871 @@
+'\" t
+.\" Uncomment the next line to get a man page accurate for MS-DOS
+.\"nr Os 1
+.\" Uncomment the next line if tracing is enabled.
+.\"nr Tr 1
+.if \n(.g .if !r Os .nr Os 0
+.tr \(ts"
+.ds S \s-1SGML\s0
+.de TS
+.br
+.sp .5
+..
+.de TE
+.br
+.sp .5
+..
+.de TQ
+.br
+.ns
+.TP \\$1
+..
+.TH SGMLS 1
+.SH NAME
+sgmls \- a validating SGML parser
+.sp
+An \*S System Conforming to
+.if n .br
+International Standard ISO 8879 \(em
+.br
+Standard Generalized Markup Language
+.SH SYNOPSIS
+.B sgmls
+[
+.B \-deglprsuv
+]
+[
+.BI \-c file
+]
+.if \n(Os=1 \{\
+[
+.BI \-f file
+]
+.\}
+[
+.BI \-i name
+]
+.if \n(Tr \{\
+[
+.BI \-x flags
+]
+[
+.BI \-y flags
+]
+.\}
+[
+.I filename\|.\|.\|.
+]
+.SH DESCRIPTION
+.I Sgmls
+parses and validates
+the \*S document entity in
+.I filename\|.\|.\|.
+and prints on the standard output a simple \s-1ASCII\s0 representation of its
+Element Structure Information Set.
+(This is the information set which a structure-controlled
+conforming \*S application should act upon.)
+Note that the document entity may be spread amongst several files;
+for example, the SGML declaration, document type declaration and document
+instance set could each be in a separate file.
+If no filenames are specified, then
+.I sgmls
+will read the document entity from the standard input.
+A filename of
+.B \-
+can also be used to refer to the standard input.
+.LP
+The following options are available:
+.TP
+.BI \-c file
+Write a report of capacity usage to
+.IR file .
+The report is in the format of a RACT result.
+RACT is the Reference Application for Capacity Testing defined in the
+Proposed American National Standard
+.I
+Conformance Testing for Standard Generalized Markup Language (SGL) Systems
+(X3.190-199X),
+Draft July 1991.
+.TP
+.B \-d
+Warn about duplicate entity declarations.
+.TP
+.B \-e
+Describe open entities in error messages.
+Error messages always include the position of the most recently
+opened external entity.
+.if \n(Os=1 \{\
+.TP
+.BI \-f file
+Redirect errors to
+.IR file .
+.\}
+.TP
+.B \-g
+Show the \s-1GI\s0s of open elements in error messages.
+.TP
+.BI \-i name
+Pretend that
+.RS
+.IP
+.BI <!ENTITY\ %\ name\ \(tsINCLUDE\(ts>
+.LP
+occurs at the start of the document type declaration subset
+in the \*S document entity.
+Since repeated definitions of an entity are ignored,
+this definition will take precedence over any other definitions
+of this entity in the document type declaration.
+Multiple
+.B \-i
+options are allowed.
+If the \*S declaration replaces the reserved name
+.B INCLUDE
+then the new reserved name will be the replacement text of the entity.
+Typically the document type declaration will contain
+.IP
+.BI <!ENTITY\ %\ name\ \(tsIGNORE\(ts>
+.LP
+and will use
+.BI % name ;
+in the status keyword specification of a marked section declaration.
+In this case the effect of the option will be to cause the marked
+section not to be ignored.
+.RE
+.TP
+.B \-l
+Output
+.B L
+commands giving the current line number and filename.
+.TP
+.B \-p
+Parse only the prolog.
+.I Sgmls
+will exit after parsing the document type declaration.
+Implies
+.BR \-s .
+.TP
+.B \-r
+Warn about defaulted references.
+.TP
+.B \-s
+Suppress output.
+Error messages will still be printed.
+.TP
+.B \-u
+Warn about undefined elements: elements used in the DTD but not defined.
+Also warn about undefined short reference maps.
+.TP
+.B \-v
+Print the version number.
+.if \n(Tr \{\
+.TP
+.BI \-x flags
+.br
+.ns
+.TP
+.BI \-y flags
+Enable debugging output;
+.B \-x
+applies to the document body,
+.B \-y
+to the prolog.
+Each character in the
+.I flags
+argument enables tracing of a particular activity.
+.RS
+.TP
+.B t
+Trace state transitions.
+.TP
+.B a
+Trace attribute activity.
+.TP
+.B c
+Trace context checking.
+.TP
+.B d
+Trace declaration parsing.
+.TP
+.B e
+Trace entities.
+.TP
+.B g
+Trace groups.
+.TP
+.B i
+Trace \s-1ID\s0s.
+.TP
+.B m
+Trace marked sections.
+.TP
+.B n
+Trace notations.
+.RE
+.\}
+.SS "Entity Manager"
+An external entity resides in one or more files.
+The entity manager component of
+.I sgmls
+maps a sequence of files into an entity in three sequential stages:
+.IP 1.
+each carriage return character is turned into a non-SGML character;
+.IP 2.
+each newline character is turned into a record end character,
+and at the same time
+a record start character is inserted at the beginning of each line;
+.IP 3.
+the files are concatenated.
+.LP
+A system identifier is
+interpreted as a list of filenames separated by
+.if \n(Os=0 colons.
+.if \n(Os=1 semi-colons.
+A filename of
+.B \-
+can be used to refer to the standard input.
+If no system identifier is supplied, then the entity manager will
+attempt to generate a filename using the public identifier
+(if there is one) and other information available to it.
+Notation identifiers are not subject to this treatment.
+This process is controlled by the environment variable
+.BR \s-1SGML_PATH\s0 ;
+this contains a
+.if \n(Os=0 colon-separated
+.if \n(Os=1 semicolon-separated
+list of filename templates.
+A filename template is a filename that may contain
+substitution fields; a substitution field is a
+.B %
+character followed by a single letter that indicates the value
+of the substitution.
+If
+.B \s-1SGML_PATH\s0
+uses the
+.B %S
+field (the value of which is the system identifier),
+then the entity manager will also use
+.B \s-1SGML_PATH\s0
+to generate a filename
+when a system identifier that does not contain any
+.if \n(Os=0 colons
+.if \n(Os=1 semi-colons
+is supplied.
+The value of a substitution can either be a string
+or it can be
+.IR null .
+The entity manager transforms the list of
+filename templates into a list of filenames by substituting for each
+substitution field and discarding any template
+that contained a substitution field whose value was null.
+It then uses the first resulting filename that exists and is readable.
+Substitution values are transformed before being used for substitution:
+firstly, any names that were subject to upper case substitution
+are folded to lower case;
+secondly,
+.if \n(Os=0 \{\
+.\" Unix
+space characters are mapped to underscores
+and slashes are mapped to percents.
+.\}
+.if \n(Os=1 \{\
+.\" MS-DOS
+the characters
+.B +,./:=?
+and space characters are deleted.
+.\}
+The value of the
+.B %S
+field is not transformed.
+The values of substitution fields are as follows:
+.TP
+.B %%
+A single
+.BR % .
+.TP
+.B %D
+The entity's data content notation.
+This substitution will succeed only for external data entities.
+.TP
+.B %N
+The entity, notation or document type name.
+.TP
+.B %P
+The public identifier if there was a public identifier,
+otherwise null.
+.TP
+.B %S
+The system identifier if there was a system identifier
+otherwise null.
+.TP
+.B %X
+(This is provided mainly for compatibility with \s-1ARCSGML\s0.)
+A three-letter string chosen as follows:
+.LP
+.RS
+.ne 11
+.TS
+tab(&);
+c|c|c s
+c|c|c s
+c|c|c|c
+c|c|c|c
+l|lB|lB|lB.
+&&With public identifier
+&&_
+&No public&Device&Device
+&identifier&independent&dependent
+_
+Data or subdocument entity&nsd&pns&vns
+General SGML text entity&gml&pge&vge
+Parameter entity&spe&ppe&vpe
+Document type definition&dtd&pdt&vdt
+Link process definition&lpd&plp&vlp
+.TE
+.LP
+The device dependent version is selected if the public text class
+allows a public text display version but no public text display
+version was specified.
+.RE
+.TP
+.B %Y
+The type of thing for which the filename is being generated:
+.TS
+tab(&);
+l lB.
+SGML subdocument entity&sgml
+Data entity&data
+General text entity&text
+Parameter entity&parm
+Document type definition&dtd
+Link process definition&lpd
+.TE
+.LP
+The value of the following substitution fields will be null
+unless a valid formal public identifier was supplied.
+.TP
+.B %A
+Null if the text identifier in the
+formal public identifier contains an unavailable text indicator,
+otherwise the empty string.
+.TP
+.B %C
+The public text class, mapped to lower case.
+.TP
+.B %E
+The public text designating sequence (escape sequence)
+if the public text class is
+.BR \s-1CHARSET\s0 ,
+otherwise null.
+.TP
+.B %I
+The empty string if the owner identifier in the formal public identifier
+is an \s-1ISO\s0 owner identifier,
+otherwise null.
+.TP
+.B %L
+The public text language, mapped to lower case,
+unless the public text class is
+.BR \s-1CHARSET\s0 ,
+in which case null.
+.TP
+.B %O
+The owner identifier (with the
+.B +//
+or
+.B \-//
+prefix stripped.)
+.TP
+.B %R
+The empty string if the owner identifier in the formal public identifier
+is a registered owner identifier,
+otherwise null.
+.TP
+.B %T
+The public text description.
+.TP
+.B %U
+The empty string if the owner identifier in the formal public identifier
+is an unregistered owner identifier,
+otherwise null.
+.TP
+.B %V
+The public text display version.
+This substitution will be null if the public text class
+does not allow a display version or if no version was specified.
+If an empty version was specified, a value of
+.B default
+will be used.
+.br
+.ne 18
+.SS "System declaration"
+The system declaration for
+.I sgmls
+is as follows:
+.LP
+.TS
+tab(&);
+c1 s1 s1 s1 s1 s1 s1 s1 s
+c s s s s s s s s
+l l s s s s s s s
+l l s s s s s s s
+l l s s s s s s s
+l l l s s s s s s
+c s s s s s s s s
+l l l l l l l l l
+l l l l l l l l l
+l l l l l l l l l
+l l s s s s s s s
+l l l s s s s s s
+l l l s s s s s s
+c s s s s s s s s
+l l l l l l l l l.
+SYSTEM "ISO 8879:1986"
+CHARSET
+BASESET&"ISO 646-1983//CHARSET
+&\h'\w'"'u'International Reference Version (IRV)//ESC 2/5 4/0"
+DESCSET&0\0128\00
+CAPACITY&PUBLIC&"ISO 8879:1986//CAPACITY Reference//EN"
+FEATURES
+MINIMIZE&DATATAG&NO&OMITTAG&YES&RANK&NO&SHORTTAG&YES
+LINK&SIMPLE&NO&IMPLICIT&NO&EXPLICIT&NO
+OTHER&CONCUR&NO&SUBDOC&YES 1&FORMAL&YES
+SCOPE&DOCUMENT
+SYNTAX&PUBLIC&"ISO 8879:1986//SYNTAX Reference//EN"
+SYNTAX&PUBLIC&"ISO 8879:1986//SYNTAX Core//EN"
+VALIDATE
+&GENERAL&YES&MODEL&YES&EXCLUDE&YES&CAPACITY&YES
+&NONSGML&YES&SGML&YES&FORMAL&YES
+.T&
+c s s s s s s s s
+l l l l l l l l l.
+SDIF
+&PACK&NO&UNPACK&NO
+.TE
+.LP
+The memory usage of
+.I sgmls
+is not a function of the capacity points used by a document;
+however,
+.I sgmls
+can handle capacities significantly greater than the reference capacity set.
+.LP
+In some environments,
+higher values may be supported for the \s-1SUBDOC\s0 parameter.
+.LP
+Documents that do not use optional features are also supported.
+For example, if
+.B FORMAL\ NO
+is specified in the \*S declaration,
+public identifiers will not be required to be valid formal public identifiers.
+.LP
+Certain parts of the concrete syntax may be changed:
+.RS
+.LP
+The shunned character numbers can be changed.
+.LP
+Eight bit characters can be assigned to
+\s-1LCNMSTRT\s0, \s-1UCNMSTRT\s0, \s-1LCNMCHAR\s0 and \s-1UCNMCHAR\s0.
+Declaring this requires that the syntax reference character set be declared
+like this:
+.RS
+.ne 3
+.TS
+tab(&);
+l l.
+BASESET&"ISO Registration Number 100//CHARSET
+&\h'\w'"'u'ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/13 4/1"
+DESCSET&0\0256\00
+.TE
+.RE
+.LP
+Uppercase substitution can be performed or not performed
+both for entity names and for other names.
+.LP
+Either short reference delimiters assigned by the reference delimiter set
+or no short reference delimiters are supported.
+.LP
+The reserved names can be changed.
+.LP
+The quantity set can be increased within certain limits
+subject to there being sufficient memory available.
+The upper limit on \s-1\%NAMELEN\s0 is 239.
+The upper limits on
+\s-1\%ATTCNT\s0, \s-1\%ATTSPLEN\s0, \s-1\%BSEQLEN\s0, \s-1\%ENTLVL\s0,
+\s-1\%LITLEN\s0, \s-1\%PILEN\s0, \s-1\%TAGLEN\s0, and \s-1\%TAGLVL\s0
+are more than thirty times greater than the reference limits.
+The upper limit on
+\s-1\%GRPCNT\s0, \s-1\%GRPGTCNT\s0, and \s-1\%GRPLVL\s0 is 253.
+\s-1\%NORMSEP\s0
+cannot be changed.
+\s-1\%DTAGLEN\s0
+are
+\s-1\%DTEMPLEN\s0
+irrelevant since
+.I sgmls
+does not support the
+\s-1\%DATATAG\s0
+feature.
+.RE
+.SS "\*S declaration"
+The \*S declaration may be omitted,
+the following declaration will be implied:
+.TS
+tab(&);
+c1 s1 s1 s1 s1 s1 s1 s1 s
+c s s s s s s s s
+l l s s s s s s s.
+<!SGML "ISO 8879:1986"
+CHARSET
+BASESET&"ISO 646-1983//CHARSET
+&\h'\w'"'u'International Reference Version (IRV)//ESC 2/5 4/0"
+DESCSET&\0\00\0\09\0UNUSED
+&\0\09\0\02\0\09
+&\011\0\02\0UNUSED
+&\013\0\01\013
+&\014\018\0UNUSED
+&\032\095\032
+&127\0\01\0UNUSED
+.T&
+l l l s s s s s s
+l l s s s s s s s
+l l l s s s s s s
+c s s s s s s s s
+l l l l l l l l l.
+CAPACITY&PUBLIC&"ISO 8879:1986//CAPACITY Reference//EN"
+SCOPE&DOCUMENT
+SYNTAX&PUBLIC&"ISO 8879:1986//SYNTAX Reference//EN"
+FEATURES
+MINIMIZE&DATATAG&NO&OMITTAG&YES&RANK&NO&SHORTTAG&YES
+LINK&SIMPLE&NO&IMPLICIT&NO&EXPLICIT&NO
+OTHER&CONCUR&NO&SUBDOC&YES 99999999&FORMAL&YES
+.T&
+c s s s s s s s s.
+APPINFO NONE>
+.TE
+with the exception that characters 128 through 254 will be assigned to
+\s-1DATACHAR\s0.
+When exporting documents that use characters in this range,
+an accurate description of the upper half of the document character set
+should be added to this declaration.
+For ISO Latin-1, an appropriate description would be:
+.br
+.ne 5
+.TS
+tab(&);
+l l.
+BASESET&"ISO Registration Number 100//CHARSET
+&\h'\w'"'u'ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/13 4/1"
+DESCSET&128\032\0UNUSED
+&160\095\032
+&255\0\01\0UNUSED
+.TE
+.SS "Output format"
+The output is a series of lines.
+Lines can be arbitrarily long.
+Each line consists of an initial command character
+and one or more arguments.
+Arguments are separated by a single space,
+but when a command takes a fixed number of arguments
+the last argument can contain spaces.
+There is no space between the command character and the first argument.
+Arguments can contain the following escape sequences.
+.TP
+.B \e\e
+A
+.BR \e.
+.TP
+.B \en
+A record end character.
+.TP
+.B \e|
+Internal \s-1SDATA\s0 entities are bracketed by these.
+.TP
+.BI \e nnn
+The character whose code is
+.I nnn
+octal.
+.LP
+A record start character will be represented by
+.BR \e012 .
+Most applications will need to ignore
+.B \e012
+and translate
+.B \en
+into newline.
+.LP
+The possible command characters and arguments are as follows:
+.TP
+.BI ( gi
+The start of an element whose generic identifier is
+.IR gi .
+Any attributes for this element
+will have been specified with
+.B A
+commands.
+.TP
+.BI ) gi
+The end an element whose generic identifier is
+.IR gi .
+.TP
+.BI \- data
+Data.
+.TP
+.BI & name
+A reference to an external data entity
+.IR name ;
+.I name
+will have been defined using an
+.B E
+command.
+.TP
+.BI ? pi
+A processing instruction with data
+.IR pi .
+.TP
+.BI A name\ val
+The next element to start has an attribute
+.I name
+with value
+.I val
+which takes one of the following forms:
+.RS
+.TP
+.B IMPLIED
+The value of the attribute is implied.
+.TP
+.BI CDATA\ data
+The attribute is character data.
+This is used for attributes whose declared value is
+.BR \s-1CDATA\s0 .
+.TP
+.BI NOTATION\ nname
+The attribute is a notation name;
+.I nname
+will have been defined using a
+.B N
+command.
+This is used for attributes whose declared value is
+.BR \s-1NOTATION\s0 .
+.TP
+.BI ENTITY\ name\|.\|.\|.
+The attribute is a list of general entity names.
+Each entity name will have been defined using an
+.BR I ,
+.B E
+or
+.B S
+command.
+This is used for attributes whose declared value is
+.B \s-1ENTITY\s0
+or
+.BR \s-1ENTITIES\s0 .
+.TP
+.BI TOKEN\ token\|.\|.\|.
+The attribute is a list of tokens.
+This is used for attributes whose declared value is anything else.
+.RE
+.TP
+.BI D ename\ name\ val
+This is the same as the
+.B A
+command, except that it specifies a data attribute for an
+external entity named
+.IR ename .
+Any
+.B D
+commands will come after the
+.B E
+command that defines the entity to which they apply, but
+before any
+.B &
+or
+.B A
+commands that reference the entity.
+.TP
+.BI N nname
+.IR nname.
+Define a notation
+This command will be preceded by a
+.B p
+command if the notation was declared with a public identifier,
+and by a
+.B s
+command if the notation was declared with a system identifier.
+A notation will only be defined if it is to be referenced
+in an
+.B E
+command or in an
+.B A
+command for an attribute with a declared value of
+.BR \s-1NOTATION\s0 .
+.TP
+.BI E ename\ typ\ nname
+Define an external data entity named
+.I ename
+with type
+.I typ
+.RB ( \s-1CDATA\s0 ,
+.B \s-1NDATA\s0
+or
+.BR \s-1SDATA\s0 )
+and notation
+.IR not.
+This command will be preceded by one or more
+.B f
+commands giving the filenames generated by the entity manager from the system
+and public identifiers,
+by a
+.B p
+command if a public identifier was declared for the entity,
+and by a
+.B s
+command if a system identifier was declared for the entity.
+.I not
+will have been defined using a
+.B N
+command.
+Data attributes may be specified for the entity using
+.B D
+commands.
+An external data entity will only be defined if it is to be referenced in a
+.B &
+command or in an
+.B A
+command for an attribute whose declared value is
+.B \s-1ENTITY\s0
+or
+.BR \s-1ENTITIES\s0 .
+.TP
+.BI I ename\ typ\ text
+Define an internal data entity named
+.I ename
+with type
+.I typ
+.RB ( \s-1CDATA\s0
+or
+.BR \s-1SDATA\s0 )
+and entity text
+.IR text .
+An internal data entity will only be defined if it is referenced in an
+.B A
+command for an attribute whose declared value is
+.B \s-1ENTITY\s0
+or
+.BR \s-1ENTITIES\s0 .
+.TP
+.BI S ename
+Define a subdocument entity named
+.IR ename .
+This command will be preceded by one or more
+.B f
+commands giving the filenames generated by the entity manager from the system
+and public identifiers,
+by a
+.B p
+command if a public identifier was declared for the entity,
+and by a
+.B s
+command if a system identifier was declared for the entity.
+A subdocument entity will only be defined if it is referenced
+in a
+.B {
+command
+or in an
+.B A
+command for an attribute whose declared value is
+.B \s-1ENTITY\s0
+or
+.BR \s-1ENTITIES\s0 .
+.TP
+.BI s sysid
+This command applies to the next
+.BR E ,
+.B S
+or
+.B N
+command and specifies the associated system identifier.
+.TP
+.BI p pubid
+This command applies to the next
+.BR E ,
+.B S
+or
+.B N
+command and specifies the associated public identifier.
+.TP
+.BI f filename
+This command applies to the next
+.B E
+or
+.B S
+command and specifies an associated filename.
+There will be more than one
+.B f
+command for a single
+.B E
+or
+.B S
+command if the system identifier used a
+.if \n(Os=0 colon.
+.if \n(Os=1 semi-colon.
+.TP
+.BI { ename
+The start of the \*S subdocument entity
+.IR ename ;
+.I ename
+will have been defined using a
+.B S
+command.
+.TP
+.BI } ename
+The end of the \*S subdocument entity
+.IR ename .
+.TP
+.BI L lineno\ file
+.TQ
+.BI L lineno
+Set the current line number and filename.
+The
+.I filename
+argument will be omitted if only the line number has changed.
+This will be output only if the
+.B \-l
+option has been given.
+.TP
+.BI # text
+An \s-1APPINFO\s0 parameter of
+.I text
+was specified in the \*S declaration.
+This is not strictly part of the ESIS, but a structure-controlled
+application is permitted to act on it.
+No
+.B #
+command will be output if
+.B \s-1APPINFO\s0\ \s-1NONE\s0
+was specified.
+A
+.B #
+command will occur at most once,
+and may be preceded only by a single
+.B L
+command.
+.TP
+.B C
+This command indicates that the document was a conforming \*S document.
+If this command is output, it will be the last command.
+An \*S document is not conforming if it references a subdocument entity
+that is not conforming.
+.SH BUGS
+Some non-SGML characters in literals are counted as two characters for the
+purposes of quantity and capacity calculations.
+.SH "SEE ALSO"
+The \*S Handbook, Charles F. Goldfarb
+.br
+\s-1ISO\s0 8879 (Standard Generalized Markup Language),
+International Organization for Standardization
+.SH ORIGIN
+\s-1ARCSGML\s0 was written by Charles F. Goldfarb.
+.LP
+.I Sgmls
+was derived from \s-1ARCSGML\s0 by James Clark (jjc@jclark.com),
+to whom bugs should be reported.
diff --git a/usr.bin/sgmls/sgmls/sgmlxtrn.c b/usr.bin/sgmls/sgmls/sgmlxtrn.c
new file mode 100644
index 0000000..d27eb66
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlxtrn.c
@@ -0,0 +1,223 @@
+/* Standard Generalized Markup Language Users' Group (SGMLUG)
+ SGML Parser Materials (ARCSGML 1.0)
+
+(C) 1983-1988 Charles F. Goldfarb (assigned to IBM Corporation)
+(C) 1988-1991 IBM Corporation
+
+Licensed to the SGML Users' Group for distribution under the terms of
+the following license: */
+
+char license[] =
+"SGMLUG hereby grants to any user: (1) an irrevocable royalty-free,\n\
+worldwide, non-exclusive license to use, execute, reproduce, display,\n\
+perform and distribute copies of, and to prepare derivative works\n\
+based upon these materials; and (2) the right to authorize others to\n\
+do any of the foregoing.\n";
+
+#include "sgmlincl.h"
+
+/* SGMLXTRN: Storage allocation and initialization for all public variables.
+ Exceptions: Constants lex????? and del????? are defined in
+ LEX?????.C modules; constants pcb????? are defined in PCB?????.c.
+*/
+int badresw = 0; /* 1=REF_ out of context; 0=valid. */
+int charmode = 0; /* >0=in #CHARS; 0=not. */
+int conactsw = 0; /* 1=return saved content action 0=get new one.*/
+int conrefsw = 0; /* 1=content reference att specified; 0=no. */
+int contersv = 0; /* Save contersw while processing pending REF. */
+int contersw = 0; /* 1=element or #CHARS out of context; 0=valid. */
+int datarc = 0; /* Return code for data: DAF_ or REF_. */
+int delmscsw = 0; /* 1=DELMSC must be read on return to es==0. */
+int didreq = 0; /* 1=required implied tag processed; 0=no. */
+int dostag = 0; /* 1=retry newetd instead of parsing; 0=parse. */
+int dtdsw = 0; /* DOCTYPE declaration found: 1=yes; 0=no. */
+int entdatsw = 0; /* 2=CDATA entity; 4=SDATA; 8=NDATA; 0=none. */
+int entpisw = 0; /* 4=PI entity occurred; 0=not. */
+int eodsw = 0; /* 1=eod found in error; 0=not yet. */
+int eofsw = 0; /* 1=eof found in body of document; 0=not yet. */
+int es = -1; /* Index of current source in stack. */
+int etagimct = 0; /* Implicitly ended elements left on stack. */
+int etagimsw = 0; /* 1=end-tag implied by other end-tag; 0=not. */
+int etagmin = MINNONE; /* Minim: NONE NULL NET DATA; implied by S/ETAG*/
+int etictr = 0; /* Number of "NET enabled" tags on stack. */
+int etisw = 0; /* 1=tag ended with eti; 0=did not. */
+int indtdsw = 0; /* Are we in the DTD? 1=yes; 0=no. */
+int mslevel = 0; /* Nesting level of marked sections. */
+int msplevel = 0; /* Nested MS levels subject to special parse. */
+int prologsw = 1; /* 1=in prolog; 0=not. */
+int pss = 0; /* SGMLACT: scbsgml stack level. */
+int sgmlsw = 0; /* SGML declaration found: 1=yes; 0=no. */
+int stagmin = MINNONE; /* Minimization: NONE, NULL tag, implied by STAG*/
+int tagctr = 0; /* Tag source chars read. */
+int ts = -1; /* Index of current tag in stack. */
+struct parse *propcb = &pcbpro; /* Current PCB for prolog parse. */
+int aentctr = 0; /* Number of ENTITY tokens in this att list. */
+int conact = 0; /* Return code from content parse. */
+int conrefsv = 0; /* Save conrefsw when doing implied start-tag.*/
+int dtdrefsw = 0; /* External DTD? 1=yes; 0=no. */
+int etiswsv = 0; /* Save etisw when processing implied start-tag.*/
+int grplvl = 0; /* Current level of nested grps in model. */
+int idrctr = 0; /* Number of IDREF tokens in this att list. */
+int mdessv = 0; /* ES level at start of markup declaration. */
+int notadn = 0; /* Position of NOTATION attribute in list. */
+int parmno = 0; /* Current markup declaration parameter number. */
+int pexsw = 0; /* 1=tag valid solely because of plus exception.*/
+int rcessv = 0; /* ES level at start of RCDATA content. */
+int tagdelsw = 0; /* 1=tag ended with delimiter; 0=no delimiter. */
+int tokencnt = 0; /* Number of tokens found in attribute value. */
+struct entity *ecbdeflt = 0; /* #DEFAULT ecb (NULL if no default entity). */
+struct etd *docetd = 0; /* The etd for the document as a whole. */
+struct etd *etagreal = 0; /* Actual or dummy etd that implied this tag. */
+struct etd *newetd = 0; /* The etd for a start- or end-tag recognized. */
+struct etd *nextetd = 0; /* ETD that must come next (only one choice). */
+struct etd *stagreal = 0; /* Actual or dummy etd that implied this tag. */
+struct parse *conpcb = 0; /* Current PCB for content parse. */
+UNCH *data = 0; /* Pointer to returned data in buffer. */
+UNCH *mdname = 0; /* Name of current markup declaration. */
+UNCH *ptcon = 0; /* Current pointer into tbuf. */
+UNCH *ptpro = 0; /* Current pointer into tbuf. */
+UNCH *rbufs = 0; /* DOS file read area: start position for read. */
+UNCH *subdcl = 0; /* Subject of markup declaration (e.g., GI). */
+int Tstart = 0; /* Save starting token for AND group testing. */
+UNS conradn = 0; /* 1=CONREF attribute in list (0=no). */
+UNS datalen = 0; /* Length of returned data in buffer. */
+UNS entlen = 0; /* Length of TAG or EXTERNAL entity text. */
+UNS idadn = 0; /* Number of ID attribute (0 if none). */
+UNS noteadn = 0; /* Number of NOTATION attribute (0 if none). */
+UNS reqadn = 0; /* Num of atts with REQUIRED default (0=none). */
+int grplongs; /* Number of longs for GRPCNT bitvector. */
+
+/* Variable arrays and structures.
+*/
+struct ad *al = 0; /* Current attribute list work area. */
+struct dcncb *dcntab[1]; /* List of data content notation names. */
+struct entity *etab[ENTHASH]; /* Entity hash table. */
+struct etd *etdtab[ETDHASH]; /* Element type definition hash table. */
+struct fpi fpidf; /* Fpi for #DEFAULT entity. */
+struct id *itab[IDHASH]; /* Unique identifier hash table. */
+struct etd **nmgrp = 0; /* Element name group */
+PDCB *nnmgrp = 0; /* Notation name group */
+struct restate *scbsgml = 0; /* SGMLACT: return action state stack. */
+struct source *scbs = 0; /* Stack of open sources ("SCB stack"). */
+struct srh *srhtab[1]; /* List of SHORTREF table headers. */
+struct sgmlstat ds; /* Document statistics. */
+struct switches sw; /* Parser control switches set by text proc. */
+struct tag *tags = 0; /* Stack of open elements ("tag stack"). */
+struct thdr *gbuf = 0; /* Buffer for creating group. */
+struct thdr prcon[3]; /* 0-2: Model for *DOC content. */
+struct thdr undechdr; /* 0:Default model hdr for undeclared content.*/
+UNCH *dtype = 0; /* Document type name. */
+UNCH *entbuf = 0; /* Buffer for entity reference name. */
+UNCH fce[2]; /* String form of FCE char.
+ (fce[1] must be EOS).*/
+/* Buffer for non-SGML character reference.*/
+UNCH nonchbuf[2] = { DELNONCH };
+UNCH *tbuf; /* Work area for tokenization. */
+UNCH *lbuf = 0; /* In tbuf: Literal parse area.*/
+UNCH *sysibuf = 0; /* Buffer for system identifiers. */
+UNCH *pubibuf = 0; /* Buffer for public identifiers. */
+UNCH *nmbuf = 0; /* Name buffer used by mdentity. */
+struct mpos *savedpos;
+
+/* Constants.
+*/
+struct map dctab[] = { /* Keywords for declared content parameter.*/
+ { key[KRCDATA], MRCDATA+MPHRASE },
+ { key[KCDATA], MCDATA+MPHRASE },
+ { key[KANY], MANY+MCHARS+MGI },
+ { key[KEMPTY], MNONE+MPHRASE },
+ { NULL, 0 }
+};
+struct map deftab[] = { /* Default value keywords. */
+ { key[KIMPLIED], DNULL },
+ { key[KREQUIRED], DREQ },
+ { key[KCURRENT], DCURR },
+ { key[KCONREF], DCONR },
+ { key[KFIXED], DFIXED},
+ { NULL, 0}
+};
+struct map dvtab[] = { /* Declared value: keywords and type codes.*/
+/* TYPE NUMBER */
+/* grp ANMTGRP Case 1 0 Grp size */
+/* grp member ANMTGRP Case 0 Position */
+/* grp ANOTEGRP Case 1 1 Grp size */
+ { key[KNOTATION], ANOTEGRP}, /* Case 1 Position */
+ { key[KCDATA], ACHARS }, /* Case 2 Always 0 */
+ { key[KENTITY], AENTITY }, /* Case 3 Normal 1 */
+ { key[KID], AID }, /* Case 4 Normal 1 */
+ { key[KIDREF], AIDREF }, /* Case 5 Normal 1 */
+ { key[KNAME], ANAME }, /* Case 6 Normal 1 */
+ { key[KNMTOKEN], ANMTOKE }, /* Case 7 Normal 1 */
+ { key[KNUMBER], ANUMBER }, /* Case 8 Normal 1 */
+ { key[KNUTOKEN], ANUTOKE }, /* Case 9 Normal 1 */
+ { key[KENTITIES], AENTITYS}, /* Case A Normal 1 */
+ { key[KIDREFS], AIDREFS }, /* Case B # tokens */
+ { key[KNAMES], ANAMES }, /* Case C # tokens */
+ { key[KNMTOKENS], ANMTOKES}, /* Case D # tokens */
+ { key[KNUMBERS], ANUMBERS}, /* Case E # tokens */
+ { key[KNUTOKENS], ANUTOKES}, /* Case F # tokens */
+ { NULL, 0 } /* Case 0 ERROR */
+};
+struct map enttab[] = { /* Entity declaration second parameter. */
+ { key[KCDATA], ESC },
+ { key[KSDATA], ESX },
+ { key[KMS], ESMS},
+ { key[KPI], ESI },
+ { key[KSTARTTAG], ESS },
+ { key[KENDTAG], ESE },
+ { key[KMD], ESMD},
+ { NULL, 0 }
+};
+struct map exttab[] = { /* Keywords for external identifier. */
+ { key[KSYSTEM], EDSYSTEM },
+ { key[KPUBLIC], EDPUBLIC },
+ { NULL, 0 }
+};
+struct map extettab[] = { /* Keywords for external entity type. */
+ { key[KCDATA], ESNCDATA },
+ { key[KNDATA], ESNNDATA },
+ { key[KSDATA], ESNSDATA },
+ { key[KSUBDOC], ESNSUB },
+ { NULL, 0 }
+};
+struct map funtab[] = { /* Function character reference names. */
+ { key[KRE], RECHAR },
+ { key[KRS], RSCHAR },
+ { key[KSPACE], SPCCHAR },
+ /* We should use an extra table for added functions. */
+ { (UNCH *)"TAB", TABCHAR },
+ { NULL, 0 }
+};
+struct map mstab[] = { /* Marked section keywords. */
+ { key[KTEMP], MSTEMP },
+ { key[KINCLUDE], MSTEMP }, /* Treat INCLUDE like TEMP; both are NOPs.*/
+ { key[KRCDATA], MSRCDATA},
+ { key[KCDATA], MSCDATA },
+ { key[KIGNORE], MSIGNORE},
+ { NULL, 0 }
+};
+struct map pubcltab[] = { /* Names for public text class. */
+ { (UNCH *)"CAPACITY", FPICAP },
+ { (UNCH *)"CHARSET", FPICHARS},
+ { (UNCH *)"DOCUMENT", FPIDOC },
+ { (UNCH *)"DTD", FPIDTD },
+ { (UNCH *)"ELEMENTS", FPIELEM },
+ { (UNCH *)"ENTITIES", FPIENT },
+ { (UNCH *)"LPD", FPILPD },
+ { (UNCH *)"NONSGML", FPINON },
+ { (UNCH *)"NOTATION", FPINOT },
+ { (UNCH *)"SHORTREF", FPISHORT},
+ { (UNCH *)"SUBDOC", FPISUB },
+ { (UNCH *)"SYNTAX", FPISYN },
+ { (UNCH *)"TEXT", FPITEXT },
+ { NULL, 0 }
+};
+UNCH indefent[] = "\12#DEFAULT"; /* Internal name: default entity name. */
+UNCH indefetd[] = "\12*DOCTYPE"; /* Internal name: default document type. */
+UNCH indocent[] = "\12*SGMLDOC"; /* Internal name: SGML document entity. */
+UNCH indocetd[] = "\6*DOC"; /* Internal name: document level etd. */
+UNCH indtdent[] = "\11*DTDENT"; /* Internal name: external DTD entity. */
+
+struct etd dumetd[3];
+struct entity *dumpecb;
+UNCH sgmlkey[] = "SGML";
diff --git a/usr.bin/sgmls/sgmls/sgmlxtrn.h b/usr.bin/sgmls/sgmls/sgmlxtrn.h
new file mode 100644
index 0000000..f1b0b4b
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/sgmlxtrn.h
@@ -0,0 +1,121 @@
+/* SGMLXTRN.H: External declarations for SGML public variables.
+ Exceptions: Constants lex????? and del????? are defined in
+ LEX?????.C modules; constants pcb????? are defined in PCB?????.c.
+*/
+#ifndef SGMLXTRN /* Don't include this file more than once. */
+#define SGMLXTRN
+extern int badresw; /* 1=REF_ out of context; 0=valid. */
+extern int charmode; /* >0=in #CHARS; 0=not. */
+extern int conactsw; /* 1=return saved content action 0=get new one.*/
+extern int conrefsw; /* 1=content reference att specified; 0=no. */
+extern int contersv; /* Save contersw while processing pending REF. */
+extern int contersw; /* 1=element or #CHARS out of context; 0=valid. */
+extern int datarc; /* Return code for data: DAF_ or REF_. */
+extern int delmscsw; /* 1=DELMSC must be read on return to es==0. */
+extern int didreq; /* 1=required implied tag processed; 0=no. */
+extern int dostag; /* 1=retry newetd instead of parsing; 0=parse. */
+extern int dtdsw; /* DOCTYPE declaration found: 1=yes; 0=no. */
+extern int entdatsw; /* 2=CDATA entity; 4=SDATA; 8=NDATA; 0=none. */
+extern int entpisw; /* 4=PI entity occurred; 0=not. */
+extern int eodsw; /* 1=eod found in error; 0=not yet. */
+extern int eofsw; /* 1=eof found in body of document; 0=not yet. */
+extern int etagimct; /* Implicitly ended elements left on stack. */
+extern int etagimsw; /* 1=end-tag implied by other end-tag; 0=not. */
+extern int etagmin; /* Minim: NONE NULL NET DATA; implied by S/ETAG*/
+extern int etictr; /* Number of "NET enabled" tags on stack. */
+extern int etisw; /* 1=tag ended with eti; 0=did not. */
+extern int indtdsw; /* Are we in the DTD? 1=yes; 0=no. */
+extern int mslevel; /* Nesting level of marked sections. */
+extern int msplevel; /* Nested MS levels subject to special parse. */
+extern int prologsw; /* 1=in prolog; 0=not. */
+extern int pss; /* SGMLACT: scbsgml stack level. */
+extern int sgmlsw; /* SGML declaration found: 1=yes; 0=no. */
+extern int stagmin; /* Minimization: NONE, NULL tag, implied by STAG*/
+extern int tagctr; /* Tag source chars read. */
+extern int ts; /* Index of current tag in stack. */
+extern struct parse *propcb; /* Current PCB for prolog parse. */
+extern int aentctr; /* Number of ENTITY tokens in this att list. */
+extern int conact; /* Return code from content parse. */
+extern int conrefsv; /* Save conrefsw when doing implied start-tag.*/
+extern int dtdrefsw; /* External DTD? 1=yes; 0=no. */
+extern int etiswsv; /* Save etisw when processing implied start-tag.*/
+extern int grplvl; /* Current level of nested grps in model. */
+extern int idrctr; /* Number of IDREF tokens in this att list. */
+extern int mdessv; /* ES level at start of markup declaration. */
+extern int notadn; /* Position of NOTATION attribute in list. */
+extern int parmno; /* Current markup declaration parameter number. */
+extern int pexsw; /* 1=tag valid solely because of plus exception.*/
+extern int rcessv; /* ES level at start of RCDATA content. */
+extern int tagdelsw; /* 1=tag ended with delimiter; 0=no delimiter. */
+extern int tokencnt; /* Number of tokens found in attribute value. */
+extern struct entity *ecbdeflt; /* #DEFAULT ecb (NULL if no default entity). */
+extern struct etd *docetd; /* The etd for the document as a whole. */
+extern struct etd *etagreal; /* Actual or dummy etd that implied this tag. */
+extern struct etd *newetd; /* The etd for a start- or end-tag recognized. */
+extern struct etd *nextetd; /* ETD that must come next (only one choice). */
+extern struct etd *stagreal; /* Actual or dummy etd that implied this tag. */
+extern struct parse *conpcb; /* Current PCB for content parse. */
+extern UNCH *data; /* Pointer to returned data in buffer. */
+extern UNCH *mdname; /* Name of current markup declaration. */
+extern UNCH *ptcon; /* Current pointer into tbuf. */
+extern UNCH *ptpro; /* Current pointer into tbuf. */
+extern UNCH *rbufs; /* DOS file read area: start position for read. */
+extern UNCH *subdcl; /* Subject of markup declaration (e.g., GI). */
+extern int Tstart; /* Save starting token for AND group testing. */
+extern UNS conradn; /* 1=CONREF attribute in list (0=no). */
+extern UNS datalen; /* Length of returned data in buffer. */
+extern UNS entlen; /* Length of TAG or EXTERNAL entity text. */
+extern UNS idadn; /* Number of ID attribute (0 if none). */
+extern UNS noteadn; /* Number of NOTATION attribute (0 if none). */
+extern UNS reqadn; /* Num of atts with REQUIRED default (0=none). */
+extern int grplongs; /* Number of longs for GRPCNT bitvector. */
+/* Variable arrays and structures.
+*/
+extern struct ad *al; /* Current attribute list work area. */
+extern struct dcncb *dcntab[];/* List of data content notation names. */
+extern struct entity *etab[]; /* Entity hash table. */
+extern struct etd *etdtab[]; /* Element type definition hash table. */
+extern struct fpi fpidf; /* Fpi for #DEFAULT entity. */
+extern struct id *itab[]; /* Unique identifier hash table. */
+extern struct etd **nmgrp; /* Element name group */
+extern PDCB *nnmgrp; /* Notation name group */
+extern struct restate *scbsgml; /* SGMLACT: return action state stack. */
+extern struct srh *srhtab[]; /* List of SHORTREF table headers. */
+extern struct sgmlstat ds; /* Document statistics. */
+extern struct switches sw; /* Parser control switches set by text proc. */
+extern struct tag *tags; /* Stack of open elements ("tag stack"). */
+extern struct thdr *gbuf; /* Buffer for creating group. */
+extern struct thdr prcon[]; /* 0-2: Model for *DOC content. */
+extern struct thdr undechdr; /* 0: Default model hdr for undeclared content. */
+extern UNCH *dtype; /* Document type name. */
+extern UNCH *entbuf; /* Buffer for entity reference name. */
+extern UNCH fce[]; /* String form of FCE char (fce[1] must be EOS).*/
+extern UNCH nonchbuf[]; /* Buffer for valid nonchar character reference.*/
+extern UNCH *tbuf; /* Work area for tokenization. */
+extern UNCH *lbuf; /* In tbuf: Literal parse area; TAGLEN limit.*/
+extern struct entity *dumpecb; /* SRMNULL points to this. */
+extern UNCH *sysibuf;
+extern UNCH *pubibuf;
+extern UNCH *nmbuf; /* Name buffer used by mdentity. */
+extern struct mpos *savedpos;
+
+/* Constants.
+*/
+extern int scbsgmnr; /* SCBSGML: new record; do not ignore RE. */
+extern int scbsgmst; /* SCBSGML: trailing stag or markup; ignore RE. */
+extern struct map dctab[]; /* Keywords for declared content parameter. */
+extern struct map deftab[]; /* Default value keywords. */
+extern struct map dvtab[]; /* Declared value: keywords and type codes.*/
+extern struct map enttab[]; /* Entity declaration second parameter. */
+extern struct map exttab[]; /* Keywords for external identifier. */
+extern struct map extettab[]; /* Keywords for external entity type. */
+extern struct map funtab[]; /* Function character reference names. */
+extern struct map mstab[]; /* Marked section keywords. */
+extern struct map pubcltab[]; /* Keywords for public text class. */
+extern UNCH indefent[]; /* Internal name: default entity name. */
+extern UNCH indefetd[]; /* Internal name: default document type. */
+extern UNCH indocent[]; /* Internal name: SGML document entity. */
+extern UNCH indocetd[]; /* Internal name: etd for document as a whole. */
+extern UNCH indtdent[]; /* Internal name: external DTD entity. */
+extern char license[]; /* SGML Users' Group free license. */
+#endif /* ndef SGMLXTRN */
diff --git a/usr.bin/sgmls/sgmls/source.h b/usr.bin/sgmls/sgmls/source.h
new file mode 100644
index 0000000..32cc85a
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/source.h
@@ -0,0 +1,114 @@
+/* SOURCE.H: Entity and source control block structures and definitions.
+*/
+#define ENTHASH 503 /* Size of entity hash table. Must be prime. */
+/* Entity storage class values for estore member of entity structure. */
+#define EST 1 /* String: Tag (usually a fixed STARTGI). */
+ /* <MDENTITY sets these:> */
+#define ESMD 2 /* String: Markup declaration. */
+#define ESMS 3 /* String: Marked section. */
+#define ESM 4 /* String: ordinary text. */
+ /* <ENTOPEN treats these specially:> */
+#define ESS 5 /* ETD: Start-tag. */
+#define ESE 6 /* ETD: End-tag. */
+#define ESI 7 /* String: PI. */
+#define ESX 8 /* String: SDATA general entity. */
+#define ESC 9 /* String: CDATA general entity. */
+ /* </MDENTITY> <MDEXTID sets these:> */
+#define ESFM 10 /* LPU: minimum external (file) storage class. */
+#define ESN 10 /* XCB: N/C/SDATA or SUBDOC control block. */
+ /* </ENTOPEN> */
+#define ESF 11 /* LPU: General entity. */
+#define ESP 12 /* LPU: Parameter entity. */
+#define ESD 13 /* LPU: Document type definition. */
+#define ESL 14 /* LPU: Link process definition. */
+#define ESK 15 /* LPU: Data content notation. */
+ /* </MDEXTID> */
+
+union etext { /* Entity text. */
+ UNIV x; /* External ID generated by system. */
+ UNCH *c; /* Character string. */
+ struct ne *n; /* N/C/SDATA or SUBDOC entity control block. */
+};
+#define ETEXTSZ sizeof(union etext)
+struct entity { /* Entity control block. */
+ struct entity *enext; /* Next entity in chain. */
+ UNCH *ename; /* Entity name with length and EOS. */
+ UNCH estore; /* Storage class (see values above). */
+ UNCH dflt; /* Declared as default entity. */
+ UNCH mark; /* For use by for sgmlment. */
+ union etext etx; /* Entity text. */
+};
+#define ENTSZ sizeof(struct entity)
+typedef struct entity *PECB; /* Ptr to entity control block. */
+typedef struct entity **TECB; /* Table of entity control blocks. */
+
+struct source { /* Source control block. */
+ struct entity ecb; /* Entity control block. */
+ unsigned long rcnt; /* Source record number. */
+ int ccnt; /* Source record chars since last RS. */
+ int curoff; /* Offset of curchar (chars read in this block).*/
+ UNCH curchar; /* Current character. */
+ UNCH nextchar; /* If curchar was DELNONCH, next character. */
+ UNIV fcb; /* SGMLIO fcb ptr returned by OPEN. */
+ UNCH *fbuf; /* 1st char in buffer (0=PEND) or entity text. */
+ UNCH *fpos; /* Current char in buffer or entity text. */
+ UNCH pushback; /* Character before pend position */
+ char copied; /* Is this a copy of the internal entity? */
+};
+#define SCBSZ sizeof(struct source)
+typedef struct source *PSCB; /* Ptr to source control block. */
+
+extern int es; /* Index of current source in stack. */
+extern struct source *scbs; /* Stack of open sources ("SCB stack"). */
+
+/* Member definitions for source and entity control blocks.
+*/
+#define SCB (scbs[es]) /* Ptr to current source control block. */
+
+#define ECB SCB.ecb /* Pointer to current entity control block. */
+#define FBUF SCB.fbuf /* Pointer to start of entity buffer. */
+#define FPOS SCB.fpos /* Pointer to current char of current source. */
+#define RSCC SCB.ccnt /* CCNT at start of block (across EOB/EOS/EOF). */
+#define CCO SCB.curoff /* Offset in read buffer of current char. */
+#define CC SCB.curchar /* Current character of current source entity. */
+#define NEXTC SCB.nextchar /* Next character in current source entity. */
+#define CCNT (SCB.ccnt+CCO) /* Position of CC in current record (RS=0). */
+#define RCNT SCB.rcnt /* Position of record in entity (origin=1). */
+#define SCBFCB SCB.fcb /* Current file control block (if FILESW). */
+#define ECBPTR ((ECB.enext)) /* Pointer to this entity's ECB. */
+#define ENTITY ((ECB.ename)) /* Current entity name. */
+#define FILESW (ECB.estore>=ESFM) /* 1=Entity is external file; 0=internal. */
+#define NEWCC (++FPOS) /* Get next current character. */
+#define REPEATCC (--FPOS) /* Repeat previous current character. */
+#define COPIEDSW SCB.copied /* Non-zero means entity was copied. */
+
+struct srh { /* Short reference map header. */
+ struct srh *enext; /* Next short reference map in chain. */
+ UNCH *ename; /* Short reference map name. */
+ TECB srhsrm; /* Ptr to short reference map. */
+};
+#define SRHSZ (sizeof(struct srh))
+typedef struct srh *PSRH; /* Ptr to short reference map header. */
+#define SRMNULL (&dumpecb) /* Dummy ptr to empty short reference map. */
+
+/* Definitions for ENTOPEN/ENTREF return codes.
+*/
+#define ENTUNDEF -1 /* Callers of ENTOPEN: entity undefined. */
+#define ENTLOOP -2 /* ENTOPEN: endless loop entity. */
+#define ENTMAX -3 /* ENTOPEN: too many open entities. */
+#define ENTFILE -4 /* ENTOPEN: file I/O error. */
+#define ENTDATA -5 /* ENTOPEN: CDATA or SDATA entity. */
+#define ENTPI -6 /* ENTOPEN: PI entity. */
+
+/* Definitions for ENTDATA switches set in contersw.
+*/
+#define CDECONT 2 /* 0010 CDATA entity referenced. */
+#define SDECONT 4 /* 0100 SDATA entity referenced. */
+#define NDECONT 8 /* 1000 NDATA entity referenced. */
+
+/* Definitions for manipulating signed source character counters.
+*/
+#define CTRSET(CTR) (CTR = (int) -(FPOS+1-FBUF)) /* Init source char ctr. */
+#define CTRGET(CTR) (CTR + (int) (FPOS+1-FBUF)) /* Read source char ctr. */
+
+
diff --git a/usr.bin/sgmls/sgmls/std.h b/usr.bin/sgmls/sgmls/std.h
new file mode 100644
index 0000000..3a9ab4b
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/std.h
@@ -0,0 +1,116 @@
+/* std.h -
+ Include standard header files.
+*/
+
+#ifndef STD_H
+#define STD_H 1
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#ifdef SUPPORT_SUBDOC
+#include <signal.h>
+#endif /* SUPPORT_SUBDOC */
+
+#ifndef STDDEF_H_MISSING
+#include <stddef.h>
+#endif /* not STDDEF_H_MISSING */
+
+#ifndef LIMITS_H_MISSING
+#include <limits.h>
+#endif /* not LIMITS_H_MISSING */
+
+#ifndef UINT_MAX
+#define UINT_MAX (sizeof(unsigned int) == 2 ? 0x7fff : \
+ (sizeof(unsigned int) == 4 ? 0x7fffffff : cant_guess_UINT_MAX))
+#endif
+
+#ifdef VARARGS
+#include <varargs.h>
+#else
+#include <stdarg.h>
+#endif
+
+#ifdef BSD_STRINGS
+#include <strings.h>
+#define memcpy(to, from, n) bcopy(from, to, n)
+#define memcmp(p, q, n) bcmp(p, q, n)
+#define strchr(s, c) index(s, c)
+#define strrchr(s, c) rindex(s, c)
+#else /* not BSD_STRINGS */
+#include <string.h>
+#endif /* not BSD_STRINGS */
+
+#ifdef STRERROR_MISSING
+#ifdef USE_PROTOTYPES
+extern char *strerror(int);
+#else
+extern char *strerror();
+#endif
+#endif /* STRERROR_MISSING */
+
+#ifdef STDLIB_H_MISSING
+UNIV malloc();
+UNIV calloc();
+UNIV realloc();
+char *getenv();
+long atol();
+#else /* not STDLIB_H_MISSING */
+#include <stdlib.h>
+#endif /* not STDLIB_H_MISSING */
+
+#ifdef REMOVE_MISSING
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#define remove unlink
+#endif /* REMOVE_MISSING */
+
+#ifdef RAISE_MISSING
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#define raise(sig) kill(getpid(), sig)
+#endif /* RAISE_MISSING */
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+
+#ifdef FPOS_MISSING
+typedef long fpos_t;
+#define fsetpos(stream, pos) fseek(stream, *(pos), SEEK_SET)
+#define fgetpos(stream, pos) ((*(pos) = ftell(stream)) == -1L)
+#endif /* FPOS_MISSING */
+
+/* Old BSD systems lack L_tmpnam and tmpnam(). This is a partial
+emulation using mktemp(). It requires that the argument to tmpnam()
+be non-NULL. */
+
+#ifndef L_tmpnam
+#define tmpnam_template "/tmp/sgmlsXXXXXX"
+#define L_tmpnam (sizeof(tmpnam_template))
+#undef tmpnam
+#define tmpnam(buf) \
+ (mktemp(strcpy(buf, tmpnam_template)) == 0 || (buf)[0] == '\0' ? 0 : (buf))
+#endif /* not L_tmpnam */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#endif /* not STD_H */
diff --git a/usr.bin/sgmls/sgmls/stklen.c b/usr.bin/sgmls/sgmls/stklen.c
new file mode 100644
index 0000000..43af5dd
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/stklen.c
@@ -0,0 +1,2 @@
+/* This tells Borland C++ to allocate a 14k stack. */
+unsigned _stklen = 14*1024;
diff --git a/usr.bin/sgmls/sgmls/strerror.c b/usr.bin/sgmls/sgmls/strerror.c
new file mode 100644
index 0000000..f5679c0
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/strerror.c
@@ -0,0 +1,36 @@
+/* strerror.c -
+ ANSI C strerror() function.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifdef STRERROR_MISSING
+#include <stdio.h>
+
+char *strerror(n)
+int n;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char buf[sizeof("Error ") + 1 + 3*sizeof(int)];
+
+ if (n >= 0 && n < sys_nerr && sys_errlist[n] != 0)
+ return sys_errlist[n];
+ else {
+ sprintf(buf, "Error %d", n);
+ return buf;
+ }
+}
+
+#endif /* STRERROR_MISSING */
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/synrf.c b/usr.bin/sgmls/sgmls/synrf.c
new file mode 100644
index 0000000..2076107
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/synrf.c
@@ -0,0 +1,72 @@
+/* SYNRF: Reserved names and other constants for reference concrete syntax.
+*/
+#include "config.h"
+#include "entity.h" /* Templates for entity control blocks. */
+#include "synxtrn.h" /* Declarations for concrete syntax constants. */
+#include "adl.h" /* Definitions for attribute list processing. */
+UNCH key[NKEYS][REFNAMELEN+1] = {
+ "ANY",
+ "ATTLIST",
+ "CDATA",
+ "CONREF",
+ "CURRENT",
+ "DEFAULT",
+ "DOCTYPE",
+ "ELEMENT",
+ "EMPTY",
+ "ENDTAG",
+ "ENTITIES",
+ "ENTITY",
+ "FIXED",
+ "ID",
+ "IDLINK",
+ "IDREF",
+ "IDREFS",
+ "IGNORE",
+ "IMPLIED",
+ "INCLUDE",
+ "INITIAL",
+ "LINK",
+ "LINKTYPE",
+ "MD",
+ "MS",
+ "NAME",
+ "NAMES",
+ "NDATA",
+ "NMTOKEN",
+ "NMTOKENS",
+ "NOTATION",
+ "NUMBER",
+ "NUMBERS",
+ "NUTOKEN",
+ "NUTOKENS",
+ "O",
+ "PCDATA",
+ "PI",
+ "POSTLINK",
+ "PUBLIC",
+ "RCDATA",
+ "RE",
+ "REQUIRED",
+ "RESTORE",
+ "RS",
+ "SDATA",
+ "SHORTREF",
+ "SIMPLE",
+ "SPACE",
+ "STARTTAG",
+ "SUBDOC",
+ "SYSTEM",
+ "TEMP",
+ "USELINK",
+ "USEMAP"
+};
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/synxtrn.h b/usr.bin/sgmls/sgmls/synxtrn.h
new file mode 100644
index 0000000..75b6471
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/synxtrn.h
@@ -0,0 +1,152 @@
+/* SYNXTRN.H: External declarations for concrete syntax constants.
+*/
+/* Short References
+*/
+#define SRCT 32 /* Number of short reference delimiters. */
+#define SRMAXLEN 3 /* Maximum length of a SHORTREF delimiter. */
+#define SRNPRT 8 /* Number of non-printable SHORTREF delimiters. */
+struct srdel {
+ struct map dtb[SRCT+2]; /* LEXCNM: Short reference delimiters. */
+ char *pdtb[SRNPRT+1]; /* LEXCNM: Printable form of unprintable SRs. */
+ int fce; /* LEXCNM: Index of first FCE in srdeltab. */
+ int hyp2; /* LEXCNM: Index of "two hyphens" in srdeltab. */
+ int data; /* LEXCNM: Index of first SR with data char. */
+ int hyp; /* LEXCNM: Index of hyphen in srdeltab. */
+ int prtmin; /* LEXCNM: Index of 1st printable SR. */
+ int spc; /* LEXCNM: Index of space in srdeltab. */
+ int lbr; /* LEXCNM: Index of left bracket in srdeltab. */
+ int rbr; /* LEXCNM: Index of right bracket in srdeltab. */
+};
+struct delim {
+ UNCH genre; /* LEXCON: Generated RE; cannot be markup. */
+ UNCH lit; /* LEXMARK: Char used as LIT delimiter.*/
+ UNCH lita; /* LEXMARK: Char used as LITA delimiter.*/
+ UNCH mdc; /* LEXLMS: Char used as MDC delimiter.*/
+ UNCH msc; /* LEXCON: Char used as MSC delimiter. */
+ UNCH net; /* LEXCON: Char used as NET when enabled.*/
+ UNCH pero; /* LEXMARK: Char used as PERO delimiter. */
+ UNCH pic; /* LEXCON: Char used as PIC delimiter.*/
+ UNCH tago; /* LEXCON: Char used as TAGO when enabled.*/
+};
+struct lexcode {
+ UNCH fce; /* LEXCNM: FRE character as entity reference. */
+ UNCH fre; /* LEXCON: Free character not an entity ref. */
+ UNCH litc; /* LEXLMS: Literal close delimiter enabled. */
+ UNCH msc; /* LEXLMS: Marked section close delim enabled. */
+ UNCH net; /* LEXCON: Null end-tag delimiter enabled. */
+ UNCH nonet; /* LEXCON: NET disabled; still used as ETI. */
+ UNCH spcr; /* LEXCNM: Space in use as SHORTREF delimiter. */
+ UNCH tago; /* LEXCON: Tag open delimiter enabled. */
+ UNCH cde; /* LEXLMS: CDATA/SDATA delimiters. */
+};
+struct lexical {
+ struct markup m; /* Markup strings for text processor. */
+ struct srdel s; /* Short reference delimiters. */
+ struct delim d; /* General delimiter characters. */
+ struct lexcode l; /* Lexical table code assignments. */
+};
+extern struct lexical lex; /* Delimiter set constants. */
+extern UNCH lexcnm[]; /* Lexical table: mixed content. */
+extern UNCH lexcon[]; /* Lexical table for content (except mixed). */
+extern UNCH lexgrp[]; /* Lexical table for groups. */
+extern UNCH lexlms[]; /* Lexical table: literals and marked sections. */
+extern UNCH lexmark[]; /* Lexical table for markup. */
+extern UNCH lexsd[]; /* Lexical table for SGML declaration. */
+extern UNCH lextran[]; /* Case translation table for SGML names. */
+extern UNCH lextoke[]; /* Lexical table for tokenization. */
+extern UNCH *lextabs[]; /* List of all lexical tables. */
+extern struct parse pcbconc; /* PCB: character data. */
+extern struct parse pcbcone; /* PCB: element content (no data allowed). */
+extern struct parse pcbconm; /* PCB: mixed content (data allowed). */
+extern struct parse pcbconr; /* PCB: replaceable character data. */
+extern struct parse pcbetag; /* PCB: end-tags. */
+extern struct parse pcbgrcm; /* PCB: content model group. */
+extern struct parse pcbgrcs; /* PCB: content model suffix. */
+extern struct parse pcbgrnm; /* PCB: name group. */
+extern struct parse pcbgrnt; /* PCB: name token group. */
+extern struct parse pcblitc; /* PCB: literal with CDATA. */
+extern struct parse pcblitp; /* PCB: literal with CDATA, parm & char refs. */
+extern struct parse pcblitr; /* PCB: attribute value with general refs. */
+extern struct parse pcblitt; /* PCB: tokenized attribute value. */
+extern struct parse pcblitv; /* PCB: literal with CDATA, function char trans.*/
+extern struct parse pcbmd; /* PCB: markup declaration. */
+extern struct parse pcbmdc; /* PCB: comment declaration. */
+extern struct parse pcbmdi; /* PCB: markup declaration (ignored). */
+extern struct parse pcbmds; /* PCB: markup declaration subset. */
+extern struct parse pcbmsc; /* PCB: marked section in CDATA mode. */
+extern struct parse pcbmsi; /* PCB: marked section in IGNORE mode. */
+extern struct parse pcbmsrc; /* PCB: marked section in RCDATA mode. */
+extern struct parse pcbpro; /* PCB: prolog. */
+extern struct parse pcbref; /* PCB: reference. */
+extern struct parse pcbstag; /* PCB: start-tag. */
+extern struct parse pcbval; /* PCB: attribute value. */
+extern struct parse pcbeal; /* PCB: end of attribute list. */
+extern struct parse pcbsd; /* PCB: SGML declaration. */
+extern int pcbcnda; /* PCBCONM: data in buffer. */
+extern int pcbcnet; /* PCBCONM: markup found or data buffer flushed.*/
+extern int pcbmdtk; /* PCBMD: token expected. */
+extern int pcbstan; /* PCBSTAG: attribute name expected. */
+extern int pcblittda; /* PCBLITT: data character found */
+
+#define KANY 0
+#define KATTLIST 1
+#define KCDATA 2
+#define KCONREF 3
+#define KCURRENT 4
+#define KDEFAULT 5
+#define KDOCTYPE 6
+#define KELEMENT 7
+#define KEMPTY 8
+#define KENDTAG 9
+#define KENTITIES 10
+#define KENTITY 11
+#define KFIXED 12
+#define KID 13
+#define KIDLINK 14
+#define KIDREF 15
+#define KIDREFS 16
+#define KIGNORE 17
+#define KIMPLIED 18
+#define KINCLUDE 19
+#define KINITIAL 20
+#define KLINK 21
+#define KLINKTYPE 22
+#define KMD 23
+#define KMS 24
+#define KNAME 25
+#define KNAMES 26
+#define KNDATA 27
+#define KNMTOKEN 28
+#define KNMTOKENS 29
+#define KNOTATION 30
+#define KNUMBER 31
+#define KNUMBERS 32
+#define KNUTOKEN 33
+#define KNUTOKENS 34
+#define KO 35
+#define KPCDATA 36
+#define KPI 37
+#define KPOSTLINK 38
+#define KPUBLIC 39
+#define KRCDATA 40
+#define KRE 41
+#define KREQUIRED 42
+#define KRESTORE 43
+#define KRS 44
+#define KSDATA 45
+#define KSHORTREF 46
+#define KSIMPLE 47
+#define KSPACE 48
+#define KSTARTTAG 49
+#define KSUBDOC 50
+#define KSYSTEM 51
+#define KTEMP 52
+#define KUSELINK 53
+#define KUSEMAP 54
+
+#define NKEYS (KUSEMAP+1)
+
+extern UNCH key[NKEYS][REFNAMELEN+1];
+
+/* Holds the SGML keyword (not alterable by concrete syntax). */
+extern UNCH sgmlkey[];
diff --git a/usr.bin/sgmls/sgmls/tools.h b/usr.bin/sgmls/sgmls/tools.h
new file mode 100644
index 0000000..57ce45a
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/tools.h
@@ -0,0 +1,76 @@
+/* TOOLS.H: Definitions for type declarations, printing, bit handling, etc.
+*/
+
+#if CHAR_SIGNED
+typedef unsigned char UNCH;
+#else
+typedef char UNCH;
+#endif
+
+#if CHAR_SIGNED
+#define ustrcmp(s1, s2) strcmp((char *)(s1), (char *)(s2))
+#define ustrcpy(s1, s2) strcpy((char *)(s1), (char *)(s2))
+#define ustrchr(s, c) (UNCH *)strchr((char *)(s), c)
+#define ustrncmp(s1, s2, n) strncmp((char *)(s1), (char *)(s2), n)
+#define ustrncpy(s1, s2, n) strncpy((char *)(s1), (char *)(s2), n)
+#define ustrlen(s1) strlen((char *)(s1))
+#else
+#define ustrcmp strcmp
+#define ustrcpy strcpy
+#define ustrchr strchr
+#define ustrncmp strncmp
+#define ustrncpy strncpy
+#define ustrlen strlen
+#endif
+
+#if 0
+int ustrcmp(UNCH *, UNCH *);
+UNCH *ustrchr(UNCH *, int);
+int ustrncmp(UNCH *, UNCH *, UNS);
+int ustrncpy(UNCH *, UNCH *, UNS);
+int ustrlen(UNCH *);
+#endif
+
+typedef unsigned UNS;
+
+#ifdef USE_ISASCII
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+#ifdef BSD_STRINGS
+#define MEMZERO(s, n) bzero(s, n)
+#else /* not BSD_STRINGS */
+#define MEMZERO(s, n) memset(s, '\0', n)
+#endif /* not BSD_STRINGS */
+
+/* Macros for bit manipulation.
+*/
+#define SET(word, bits) ((word) |= (bits)) /* Turn bits on */
+#define RESET(word, bits) ((word) &= ~(bits)) /* Turn bits off */
+#define GET(word, bits) ((word) & (bits)) /* 1=any bit on */
+#define BITOFF(word, bits) (GET(word, bits)==0) /* 1=no bits on */
+#define BITON(word, bits) ((word) & (bits)) /* 1=any bit on */
+
+#define ETDCDATA (dumetd) /* Dummy etd pointer for #PCDATA. */
+#define ETDNULL (dumetd + 1) /* Dummy etd pointer for null tag. */
+#define ETDNET (dumetd + 2) /* Dummy etd pointer for NET delimiter. */
+#define BADPTR(p) \
+ ((p) == NULL || (p) == ETDCDATA || (p) == ETDNULL || (p) == ETDNET)
+#define PTRNUM(p) ((p) == NULL ? 0 : ((p) - dumetd) + 1)
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+/* VP is used for prototypes of varargs functions. You can't have a
+prototype if the function is defined using varargs.h rather than
+stdarg.h. */
+#ifdef VARARGS
+#define VP(parms) ()
+#else
+#define VP(parms) P(parms)
+#endif
diff --git a/usr.bin/sgmls/sgmls/trace.h b/usr.bin/sgmls/sgmls/trace.h
new file mode 100644
index 0000000..56362be
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/trace.h
@@ -0,0 +1,113 @@
+/* TRACE.H: Declarations for internal trace functions. */
+
+#ifdef TRACE
+
+/* Trace variables.
+*/
+extern int trace; /* Switch: 1=trace state transitions; 0=don't. */
+extern int atrace; /* Switch: 1=trace attribute activity; 0=don't. */
+extern int ctrace; /* Switch: 1=trace context checking; 0=don't. */
+extern int dtrace; /* Switch: 1=trace declaration parsing; 0=don't.*/
+extern int etrace; /* Switch: 1=trace entity activity; 0=don't.*/
+extern int gtrace; /* Switch: 1=trace group creations; 0=don't. */
+extern int itrace; /* Switch: 1=trace ID activity; 0=don't. */
+extern int mtrace; /* Switch: 1=trace MS activity; 0=don't. */
+extern int ntrace; /* Switch: 1=trace data notation activity. */
+extern char emd[]; /* For "EMD" parameter type in dtrace calls. */
+
+VOID traceadl P((struct ad *));
+VOID tracecon P((int,int,int,struct parse *,int,int));
+VOID tracedcn P((struct dcncb *));
+VOID tracedsk P((struct tag *,struct tag *,int,int));
+VOID traceecb P((char *,struct entity *));
+VOID traceend P((char *,struct thdr *,struct mpos *,int,int,int));
+VOID traceesn P((struct ne *));
+VOID traceetd P((struct etd *));
+VOID traceetg P((struct tag *,struct etd *,int,int));
+VOID tracegi P((char *,struct etd *,struct thdr *,struct mpos *,int));
+VOID tracegml P((struct restate *,int,int,int));
+VOID tracegrp P((struct etd **));
+VOID traceid P((char *,struct id *));
+VOID tracemd P((char *));
+VOID tracemod P((struct thdr *));
+VOID tracems P((int,int,int,int));
+VOID tracengr P((struct dcncb **));
+VOID tracepcb P((struct parse *));
+VOID tracepro P((void));
+VOID traceset P((void));
+VOID tracesrm P((char *,struct entity **,UNCH *));
+VOID tracestg P((struct etd *,int,int,struct etd *,int));
+VOID tracestk P((struct tag *,int,int));
+VOID tracetkn P((int,UNCH *));
+VOID traceval P((struct parse *,unsigned int,UNCH *,int));
+
+#define TRACEADL(al) ((void)(atrace && (traceadl(al), 1)))
+#define TRACECON(etagimct, dostag, datarc, pcb, conrefsw, didreq) \
+ ((void)(gtrace \
+ && (tracecon(etagimct, dostag, datarc, pcb, conrefsw, didreq), 1)))
+#define TRACEDCN(dcn) ((void)(ntrace && (tracedcn(dcn), 1)))
+#define TRACEDSK(pts, ptso, ts3, etictr) \
+ ((void)(gtrace && (tracedsk(pts, ptso, ts3, etictr), 1)))
+#define TRACEECB(action, p) \
+ ((void)(etrace && (traceecb(action, p), 1)))
+#define TRACEEND(stagenm, mod, pos, rc, opt, Tstart) \
+ ((void)(ctrace && (traceend(stagenm, mod, pos, rc, opt, Tstart), 1)))
+#define TRACEESN(p) \
+ ((void)((etrace || atrace || ntrace) && (traceesn(p), 1)))
+#define TRACEETD(p) ((void)(gtrace && (traceetd(p), 1)))
+#define TRACEETG(pts, curetd, tsl, etagimct) \
+ ((void)(gtrace && (traceetg(pts, curetd, tsl, etagimct), 1)))
+#define TRACEGI(stagenm, gi, mod, pos, Tstart) \
+ ((void)(ctrace && (tracegi(stagenm, gi, mod, pos, Tstart), 1)))
+#define TRACEGML(scb, pss, conactsw, conact) \
+ ((void)(trace && (tracegml(scb, pss, conactsw, conact), 1)))
+#define TRACEGRP(p) ((void)(gtrace && (tracegrp(p), 1)))
+#define TRACEID(action, p) ((void)(itrace && (traceid(action, p), 1)))
+#define TRACEMD(p) ((void)(dtrace && (tracemd(p), 1)))
+#define TRACEMOD(p) ((void)(gtrace && (tracemod(p), 1)))
+#define TRACEMS(action, code, mslevel, msplevel) \
+ ((void)(mtrace && (tracems(action, code, mslevel, msplevel), 1)))
+#define TRACENGR(p) ((void)(gtrace && (tracengr(p), 1)))
+#define TRACEPCB(p) ((void)(trace && (tracepcb(p), 1)))
+#define TRACEPRO() (tracepro())
+#define TRACESET() (traceset())
+#define TRACESRM(action, pg, gi) \
+ ((void)(etrace && (tracesrm(action, pg, gi), 1)))
+#define TRACESTG(curetd, dataret, rc, nextetd, mexts) \
+ ((void)(gtrace && (tracestg(curetd, dataret, rc, nextetd, mexts), 1)))
+#define TRACESTK(pts, ts2, etictr) \
+ ((void)(gtrace && (tracestk(pts, ts2, etictr), 1)))
+#define TRACETKN(scope, lextoke) \
+ ((void)(trace && (tracetkn(scope, lextoke), 1)))
+#define TRACEVAL(pcb, atype, aval, tokencnt) \
+ ((void)(atrace && (traceval(pcb, atype, aval, tokencnt), 1)))
+
+#else /* not TRACE */
+
+#define TRACEADL(al) /* empty */
+#define TRACECON(etagimct, dostag, datarc, pcb, conrefsw, didreq) /* empty */
+#define TRACEDCN(dcn) /* empty */
+#define TRACEDSK(pts, ptso, ts3, etictr) /* empty */
+#define TRACEECB(action, p) /* empty */
+#define TRACEEND(stagenm, mod, pos, rc, opt, Tstart) /* empty */
+#define TRACEESN(p) /* empty */
+#define TRACEETG(pts, curetd, tsl, etagimct) /* empty */
+#define TRACEETD(p) /* empty */
+#define TRACEGI(stagenm, gi, mod, pos, Tstart) /* empty */
+#define TRACEGML(scb, pss, conactsw, conact) /* empty */
+#define TRACEGRP(p) /* empty */
+#define TRACEID(action, p) /* empty */
+#define TRACEMD(p) /* empty */
+#define TRACEMOD(p) /* empty */
+#define TRACEMS(action, code, mslevel, msplevel) /* empty */
+#define TRACENGR(p) /* empty */
+#define TRACEPCB(p) /* empty */
+#define TRACEPRO() /* empty */
+#define TRACESET() /* empty */
+#define TRACESRM(action, pg, gi) /* empty */
+#define TRACESTG(curetd, dataret, rc, nextetd, mexts) /* empty */
+#define TRACESTK(pts, ts2, etictr) /* empty */
+#define TRACETKN(scope, lextoke) /* empty */
+#define TRACEVAL(pcb, atype, aval, tokencnt) /* empty */
+
+#endif /* not TRACE */
diff --git a/usr.bin/sgmls/sgmls/traceset.c b/usr.bin/sgmls/sgmls/traceset.c
new file mode 100644
index 0000000..64ebd48
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/traceset.c
@@ -0,0 +1,465 @@
+#include "sgmlincl.h" /* #INCLUDE statements for SGML parser. */
+
+#ifdef TRACE
+
+#include "context.h"
+
+/* Token status: RCHIT RCMISS RCEND RCREQ RCNREQ */
+#define STATUX tags[ts].status
+
+/* Trace variables.
+*/
+int trace = 0; /* Switch: 1=trace state transitions; 0=don't. */
+int atrace = 0; /* Switch: 1=trace attribute activity; 0=don't. */
+int ctrace = 0; /* Switch: 1=trace context checking; 0=don't. */
+int dtrace = 0; /* Switch: 1=trace declaration parsing; 0=don't.*/
+int etrace = 0; /* Switch: 1=trace entity activity; 0=don't.*/
+int gtrace = 0; /* Switch: 1=trace group creations; 0=don't. */
+int itrace = 0; /* Switch: 1=trace ID activity; 0=don't. */
+int mtrace = 0; /* Switch: 1=trace MS activity; 0=don't. */
+int ntrace = 0; /* Switch: 1=trace notation activity; 0=don't. */
+char emd[] = "EMD"; /* For "EMD" parameter type in dtrace calls. */
+
+/* Return a printable representation of c.
+*/
+static
+char *printable(c)
+int c;
+{
+ static char buf[5];
+ if (c >= 040 && c < 0177) {
+ buf[0] = c;
+ buf[1] = '\0';
+ }
+ else
+ sprintf(buf, "\\%03o", (UNCH)c);
+ return buf;
+}
+
+static
+VOID dotrace(s)
+char *s;
+{
+ trace = (s && strchr(s, 't') != 0);
+ atrace = (s && strchr(s, 'a') != 0);
+ ctrace = (s && strchr(s, 'c') != 0);
+ dtrace = (s && strchr(s, 'd') != 0);
+ etrace = (s && strchr(s, 'e') != 0);
+ gtrace = (s && strchr(s, 'g') != 0);
+ itrace = (s && strchr(s, 'i') != 0);
+ mtrace = (s && strchr(s, 'm') != 0);
+ ntrace = (s && strchr(s, 'n') != 0);
+}
+/* TRACESET: Set switches for tracing body of document.
+*/
+VOID traceset()
+{
+ dotrace(sw.trace);
+
+ if (trace||atrace||ctrace||dtrace||etrace||gtrace||itrace||mtrace||ntrace)
+ fprintf(stderr,
+"TRACESET: state=%d;att=%d;con=%d;dcl=%d;ent=%d;grp=%d;id=%d;ms=%d;dcn=%d.\n",
+ trace, atrace, ctrace, dtrace, etrace, gtrace, itrace,
+ mtrace, ntrace);
+}
+/* TRACEPRO: Set switches for tracing prolog.
+ */
+VOID tracepro()
+{
+ dotrace(sw.ptrace);
+
+ if (trace||atrace||dtrace||etrace||gtrace||mtrace||ntrace)
+ fprintf(stderr,
+ "TRACEPRO: state=%d; att=%d; dcl=%d; ent=%d; grp=%d; ms=%d; dcn=%d.\n",
+ trace, atrace, dtrace, etrace, gtrace, mtrace, ntrace);
+}
+/* TRACEPCB: Trace character just parsed and other pcb data.
+ */
+VOID tracepcb(pcb)
+struct parse *pcb;
+{
+ fprintf(stderr, "%-8s %2u-%2u-%2u-%2u from %s [%3d] in %s, %d:%d.\n",
+ pcb->pname, pcb->state, pcb->input, pcb->action,
+ pcb->newstate, printable(*FPOS), *FPOS, ENTITY+1, RCNT,
+ RSCC+FPOS+1-FBUF);
+}
+/* TRACETKN: Trace character just read during token parse.
+ */
+VOID tracetkn(scope, lextoke)
+int scope;
+UNCH lextoke[]; /* Lexical table for token and name parses. */
+{
+ fprintf(stderr, "TOKEN %2d-%2d from %s [%3d] in %s, %d:%d.\n",
+ scope, lextoke[*FPOS],
+ printable(*FPOS), *FPOS, ENTITY+1, RCNT,
+ RSCC+FPOS+1-FBUF);
+}
+/* TRACEGML: Trace state of main SGML driver routine.
+ */
+VOID tracegml(scb, pss, conactsw, conact)
+struct restate *scb;
+int pss, conactsw, conact;
+{
+ fprintf(stderr,
+ "SGML%02d %2d-%2d-%2d-%2d in main driver; conactsw=%d; conact=%d.\n",
+ pss, scb[pss].sstate, scb[pss].sinput, scb[pss].saction,
+ scb[pss].snext, conactsw, conact);
+}
+/* TRACEVAL: Trace parse of an attribute value that is a token list.
+ */
+VOID traceval(pcb, atype, aval, tokencnt)
+struct parse *pcb;
+UNS atype; /* Type of token list expected. */
+UNCH *aval; /* Value string to be parsed as token list. */
+int tokencnt; /* Number of tokens found in attribute value. */
+{
+ fprintf(stderr,
+ "%-8s %2d-%2d-%2d-%2d at %p, atype=%02x, tokencnt=%d: ",
+ pcb->pname, pcb->state, pcb->input, pcb->action,
+ pcb->newstate, (UNIV)aval, atype, tokencnt);
+ fprintf(stderr, "%s\n", aval);
+}
+/* TRACESTK: Trace entry just placed on tag stack.
+ */
+VOID tracestk(pts, ts2, etictr)
+struct tag *pts; /* Stack entry for this tag. */
+int ts2; /* Stack depth. */
+int etictr; /* Number of "netok" tags on stack. */
+{
+ fprintf(stderr,
+ "STACK %s begun; stack depth %d; tflag=%02x; etictr=%d",
+ pts->tetd->etdgi+1, ts2, pts->tflags, etictr);
+ fprintf(stderr, " srm=%s.\n",
+ pts->tsrm!=SRMNULL ? (char *)(pts->tsrm[0]->ename+1) : "#EMPTY");
+}
+/* TRACEDSK: Trace entry just removed from tag stack.
+ */
+VOID tracedsk(pts, ptso, ts3, etictr)
+struct tag *pts; /* Stack entry for new open tag. */
+struct tag *ptso; /* Stack entry for tag just ended. */
+int ts3; /* Stack depth. */
+int etictr; /* Number of "netok" tags on stack. */
+{
+ fprintf(stderr,
+ "DESTACK %s ended; otflag=%02x; %s resumed; depth=%d; tflag=%02x; etictr=%d",
+ ptso->tetd->etdgi+1, ptso->tflags,
+ pts->tetd->etdgi+1, ts3, pts->tflags, etictr);
+ fprintf(stderr, " srm=%s.\n",
+ pts->tsrm!=SRMNULL ? (char *)(pts->tsrm[0]->ename+1) : "#EMPTY");
+}
+/* TRACECON: Trace interactions between content parse and stag/context
+ processing.
+ */
+VOID tracecon(etagimct, dostag, datarc, pcb, conrefsw, didreq)
+int etagimct; /* Implicitly ended elements left on stack. */
+int dostag; /* 1=retry newetd instead of parsing; 0=parse. */
+int datarc; /* Return code for data: DAF_ or REF_ or zero. */
+struct parse *pcb; /* Parse control block for this parse. */
+int conrefsw; /* 1=content reference att specified; 0=no. */
+int didreq; /* 1=required implied empty tag processed; 0=no.*/
+{
+ fprintf(stderr,
+ "CONTENT etagimct=%d dostag=%d datarc=%d pname=%s action=%d \
+conrefsw=%d didreq=%d\n",
+ etagimct, dostag, datarc, pcb->pname, pcb->action,
+ conrefsw, didreq);
+}
+/* TRACESTG: Trace start-tag context validation input and results.
+ */
+VOID tracestg(curetd, dataret, rc, nextetd, mexts)
+struct etd *curetd; /* The etd for this tag. */
+int dataret; /* Data pending: DAF_ REF_ 0=not #PCDATA. */
+int rc; /* Return code from context or other test. */
+struct etd *nextetd; /* The etd for a forced start-tag (if rc==2). */
+int mexts; /* >0=stack level of minus grp; -1=plus; 0=none.*/
+{
+ fprintf(stderr,
+ "STARTTAG newetd=%p; dataret=%d; rc=%d; nextetd=%p; mexts=%d.\n",
+ (UNIV)curetd, dataret, rc, (UNIV)nextetd, mexts);
+}
+/* TRACEETG: Trace end-tag matching test on stack.
+ */
+VOID traceetg(pts, curetd, tsl, etagimct)
+struct tag *pts; /* Stack entry for this tag. */
+struct etd *curetd; /* The etd for this tag. */
+int tsl; /* Temporary stack level for looping. */
+int etagimct; /* Num of implicitly ended tags left on stack. */
+{
+ fprintf(stderr,
+ "ENDTAG tsl=%d; newetd=%p; stacketd=%p; tflags=%02x; etagimct=%d.\n",
+ tsl, (UNIV)curetd, (UNIV)pts->tetd, pts->tflags, etagimct);
+}
+/* TRACEECB: Trace entity control block activity.
+ */
+VOID traceecb(action, p)
+char *action;
+struct entity *p;
+{
+ static char estype1[] = " TMMMSEIXCNFPDLK";
+ static char estype2[] = " DS ";
+ if (!p)
+ return;
+ fprintf(stderr,
+ "%-8s (es=%d) type %c%c entity %s at %p containing ",
+ action, es, estype1[p->estore], estype2[p->estore], p->ename+1,
+ (UNIV)p);
+ if (p->estore==ESN && strcmp(action, "ENTDEF"))
+ traceesn(p->etx.n);
+ else if (p->etx.x==0)
+ fprintf(stderr, "[NOTHING]");
+ else
+ fprintf(stderr, "%s",
+ p->etx.c[0] ? (char *)p->etx.c : "[EMPTY]");
+ putc('\n', stderr);
+}
+/* TRACEDCN: Trace data content notation activity.
+ */
+VOID tracedcn(p)
+struct dcncb *p;
+{
+ fprintf(stderr,
+ "DCN dcn=%p; adl=%p; notation is %s\n",
+ (UNIV)p, (UNIV)p->adl, p->ename+1);
+ if (p->adl)
+ traceadl(p->adl);
+}
+/* TRACEESN: Print a data entity control block.
+ */
+VOID traceesn(p)
+PNE p;
+{
+ fprintf(stderr, "ESN Entity name is %s; entity type is %s.\n",
+ (NEENAME(p)!=0) ? ((char *)NEENAME(p))+1 : "[UNDEFINED]",
+ /* NEXTYPE(p)); */
+ (NEXTYPE(p)==1 ? "CDATA" : (NEXTYPE(p)==2 ? "NDATA" : "SDATA")));
+ fprintf(stderr, " System ID is %s\n",
+ (NEID(p)!=0) ? (char *)NEID(p) : "[UNDEFINED]");
+ if (p->nedcn!=0)
+ tracedcn(p->nedcn);
+}
+/* TRACESRM: Print the members of a short reference map.
+ */
+VOID tracesrm(action, pg, gi)
+char *action;
+TECB pg;
+UNCH *gi;
+{
+ int i = 0; /* Loop counter. */
+
+ if (pg==SRMNULL)
+ fprintf(stderr, "%-8s SHORTREF table empty for %s.\n", action, gi);
+ else {
+ fprintf(stderr, "%-8s %s at %p mapped for %s.\n",
+ action, pg[0]->ename+1, (UNIV)pg,
+ gi ? (char *)gi : "definition");
+ while (++i<=lex.s.dtb[0].mapdata)
+ if (pg[i])
+ fprintf(stderr, "%14s%02u %p %s\n",
+ "SR", i, (UNIV)pg[i], pg[i]->ename+1);
+ }
+}
+/* TRACEADL: Print an attribute definition list.
+ */
+VOID traceadl(al)
+struct ad al[];
+{
+ int i=0;
+
+ fprintf(stderr, "ADLIST %p %d membe%s; %d attribut%s\n",
+ (UNIV)al, ADN(al), ADN(al)==1 ? "r" : "rs", AN(al),
+ AN(al)==1 ? "e" : "es");
+ while (++i<=ADN(al)) {
+ fprintf(stderr,
+ (BITOFF(ADFLAGS(al,i), AGROUP) && ADTYPE(al,i)<=ANOTEGRP)
+ ? " %p %-8s %02x %02x %2d %2d %p %p\n"
+ : " %p %-8s %02x %02x %2d %2d %p %p\n",
+ &al[i], ADNAME(al,i), ADFLAGS(al,i), ADTYPE(al,i), ADNUM(al,i),
+ ADLEN(al,i), ADVAL(al,i), ADDATA(al,i).x);
+ if (ADVAL(al,i)) {
+ fprintf(stderr, "%s", ADVAL(al,i));
+ if (ADTYPE(al,i)==AENTITY && ADDATA(al,i).n!=0) {
+ fprintf(stderr, "=>");
+ traceesn(ADDATA(al,i).n);
+ }
+ else if (ADTYPE(al,i)==ANOTEGRP)
+ fprintf(stderr, "=>%s",
+ (ADDATA(al,i).x->dcnid!=0)
+ ? (char *)ADDATA(al,i).x->dcnid
+ : "[UNDEFINED]");
+ }
+ else
+ fprintf(stderr, "[%s]",
+ GET(ADFLAGS(al,i), AREQ)
+ ? "REQUIRED"
+ : (GET(ADFLAGS(al,i), ACURRENT) ? "CURRENT" : "NULL"));
+ }
+ fprintf(stderr, "\n");
+}
+/* TRACEMOD: Print the members of a model.
+ */
+VOID tracemod(pg)
+struct thdr pg[];
+{
+ fprintf(stderr, "MODEL %p %02x %d\n",
+ (UNIV)&pg[0], pg[0].ttype, pg[0].tu.tnum);
+ if ((pg[0].ttype & MKEYWORD) == 0) {
+ int i;
+
+ for (i = 1; i < pg[0].tu.tnum + 2; i++) {
+ if (GET(pg[i].ttype, TTMASK) == TTETD)
+ fprintf(stderr, " %p %02x %s\n",
+ (UNIV)&pg[i], pg[i].ttype, pg[i].tu.thetd->etdgi+1);
+ else if (GET(pg[i].ttype, TTMASK) == TTCHARS)
+ fprintf(stderr, " %p %02x %s\n",
+ (UNIV)&pg[i], pg[i].ttype, "#PCDATA");
+ else
+ fprintf(stderr, " %p %02x %d\n",
+ (UNIV)&pg[i], pg[i].ttype, pg[i].tu.tnum);
+ }
+ }
+ fprintf(stderr, "\n");
+}
+/* TRACEGRP: Print the members of a name (i.e., etd) group.
+ */
+VOID tracegrp(pg)
+struct etd *pg[];
+{
+ int i = -1; /* Loop counter. */
+
+ fprintf(stderr, "ETDGRP %p\n", (UNIV)pg);
+ while (pg[++i]!=0)
+ fprintf(stderr, " %p %s\n", (UNIV)pg[i], pg[i]->etdgi+1);
+}
+/* TRACENGR: Print the members of a notation (i.e., dcncb) group.
+ */
+VOID tracengr(pg)
+struct dcncb *pg[];
+{
+ int i = -1; /* Loop counter. */
+
+ fprintf(stderr, "DCNGRP %p\n", (UNIV)pg);
+ while (pg[++i]!=0)
+ fprintf(stderr, " %p %s\n", (UNIV)pg[i], pg[i]->ename+1);
+}
+/* TRACEETD: Print an element type definition.
+ */
+VOID traceetd(p)
+struct etd *p; /* Pointer to an etd. */
+{
+ fprintf(stderr,
+"ETD etd=%p %s min=%02x cmod=%p ttype=%02x mex=%p, pex=%p, ",
+ (UNIV)p, p->etdgi+1, p->etdmin, (UNIV)p->etdmod,
+ p->etdmod->ttype, (UNIV)p->etdmex, (UNIV)p->etdpex);
+ fprintf(stderr, "adl=%p, srm=%s.\n",
+ (UNIV)p->adl,
+ (p->etdsrm==SRMNULL)
+ ? "#EMPTY"
+ : (p->etdsrm) ? (char *)(p->etdsrm[0]->ename+1) : "#CURRENT");
+}
+/* TRACEID: Print an ID control block.
+ */
+VOID traceid(action, p)
+char *action;
+struct id *p; /* Pointer to an ID. */
+{
+ fprintf(stderr, "%-8s %s at %p is %s; ", action, p->idname+1, (UNIV)p,
+ p->iddefed ? "defined" : "undefined");
+ fprintf(stderr, "last ref=%p\n", (UNIV)p->idrl);
+}
+/* TRACEMD: Trace a markup declaration parameter.
+ */
+VOID tracemd(parmid)
+char *parmid; /* Parameter identifier. */
+{
+ fprintf(stderr, "MDPARM %-8s for %-8s, token %02d, type %02u, %s.\n",
+ mdname, subdcl ? (char *)subdcl : "[NONE]", parmno, pcbmd.action, parmid);
+}
+/* TRACEMS: Trace marked section activity.
+ */
+VOID tracems(action, code, mslevel, msplevel)
+int action; /* 1=began new level; 0=resumed previous. */
+int code;
+int mslevel; /* Nesting level of marked sections. */
+int msplevel; /* Nested MS levels subject to special parse. */
+{
+ fprintf(stderr,
+ "MS%c %2d %s nesting level %d (msp %d).\n",
+ (action ? ' ' : 'E'), code, (action ? "began" : "resumed"),
+ mslevel, msplevel);
+}
+
+static
+VOID tracehits(h)
+unsigned long *h;
+{
+ int i;
+ fprintf(stderr, " H=");
+ for (i = grplongs - 1; i >= 0; --i)
+ fprintf(stderr, "%0*lx", LONGBITS/4, h[i]);
+}
+
+/* TRACEGI: Trace GI testing stages in CONTEXT.C processing.
+ */
+VOID tracegi(stagenm, gi, mod, pos, Tstart)
+char *stagenm;
+struct etd *gi; /* ETD of new GI. */
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+int Tstart; /* Initial T for this group. */
+{
+ int i = 0; /* Loop counter. */
+
+ fprintf(stderr, "%-10s %d:", stagenm, P);
+ while (++i<=P)
+ fprintf(stderr, " %d-%d", pos[i].g, pos[i].t);
+ fprintf(stderr, " (%u) gocc=%02x gtype=%02x gnum=%d",
+ M, GOCC, GTYPE, GNUM);
+ tracehits(H);
+ fprintf(stderr, " status=%d Tstart=%d\n", STATUX, Tstart);
+ fprintf(stderr,
+ "=>%-8s tocc=%02x ttype=%02x thetd=%p (%s) gietd=%p (%s)\n",
+ tags[ts].tetd->etdgi+1, TOCC, TTYPE, (UNIV)TOKEN.tu.thetd,
+ (TTYPE
+ ? (TTYPE==TTETD ? (char *)(TOKEN.tu.thetd->etdgi+1) : "#GROUP")
+ : "#PCDATA"),
+ (UNIV)gi,
+ (gi==ETDCDATA ? "#PCDATA" : (char *)(gi->etdgi+1)));
+}
+/* TRACEEND: Trace testing for end of group in CONTEXT.C processing.
+ */
+VOID traceend(stagenm, mod, pos, rc, opt, Tstart)
+char *stagenm;
+struct thdr mod[]; /* Model of current open element. */
+struct mpos pos[]; /* Position in open element's model. */
+int rc; /* Return code: RCNREQ RCHIT RCMISS RCEND */
+int opt; /* ALLHIT parm: 1=test optionals; 0=ignore. */
+int Tstart; /* Initial T for this group. */
+{
+ int i = 0; /* Loop counter. */
+
+ fprintf(stderr, "%-10s %d:", stagenm, P);
+ while (++i<=P)
+ fprintf(stderr, " %d-%d", pos[i].g, pos[i].t);
+ fprintf(stderr, " (%u) gocc=%02x gtype=%02x gnum=%d",
+ M, GOCC, GTYPE, GNUM);
+ tracehits(H);
+ fprintf(stderr, " status=%d Tstart=%d\n", STATUX, Tstart);
+ fprintf(stderr, "=>%-8s tocc=%02x ttype=%02x thetd=%p (%s)",
+ tags[ts].tetd->etdgi+1, TOCC, TTYPE, (UNIV)TOKEN.tu.thetd,
+ (TTYPE
+ ? (TTYPE==TTETD ? (char *)(TOKEN.tu.thetd->etdgi+1) : "#GROUP")
+ : "#PCDATA"));
+ fprintf(stderr, " rc=%d offbitT=%d allhit=%d\n",
+ rc, offbit(H, (int)T, GNUM), allhit(&GHDR, H, 0, opt));
+}
+
+#endif /* TRACE */
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/unix.cfg b/usr.bin/sgmls/sgmls/unix.cfg
new file mode 100644
index 0000000..0bc8410
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/unix.cfg
@@ -0,0 +1,147 @@
+/* unix.cfg: Configuration file for sgmls on Unix. */
+
+/* A list of filename templates to use for searching for external entities.
+The filenames are separated by the character specified in PATH_FILE_SEP.
+See sgmls.man for details. */
+#define DEFAULT_PATH "/usr/local/lib/sgml/%O/%C/%T:%N.%X:%N.%D"
+/* The character that separates the filenames templates. */
+#define PATH_FILE_SEP ':'
+/* The character that separates filenames in a system identifier.
+Usually the same as PATH_FILE_SEP. */
+#define SYSID_FILE_SEP ':'
+/* The environment variable that contains the list of filename templates. */
+#define PATH_ENV_VAR "SGML_PATH"
+
+/* MIN_DAT_SUBS_FROM and MIN_DATS_SUBS_TO tell sgmls how to transform a name
+or system identifier into a legal filename. A character in
+MIN_DAT_SUBS_FROM will be transformed into the character in the
+corresponding position in MIN_DAT_SUBS_TO. If there is no such
+position, then the character is removed. */
+/* This says that spaces should be transformed to underscores, and
+slashes to percents. */
+#define MIN_DAT_SUBS_FROM " /"
+#define MIN_DAT_SUBS_TO "_%"
+
+/* Define this to allow tracing. */
+/* #define TRACE 1 */
+
+/* Define this you want support for subdocuments. This is implemented
+using features that are not part of Standard C, so you might not want
+to define it if you are porting to a new system. Otherwise I suggest
+you leave it defined. */
+#define SUPPORT_SUBDOC 1
+
+/* Define HAVE_EXTENDED_PRINTF if your *printf functions supports
+X/Open extensions; if they do, then, for example,
+
+ printf("%2$s%1$s", "bar", "foo")
+
+should print `foobar'. */
+
+/* #define HAVE_EXTENDED_PRINTF 1 */
+
+/* Define HAVE_CAT if your system provides the X/Open message
+catalogue functions catopen() and catgets(), and you want to use them.
+An implementations of these functions is included and will be used if
+you don't define this. On SunOS 4.1.1, if you do define this you
+should set CC=/usr/xpg2bin/cc in the makefile. */
+
+/* #define HAVE_CAT 1 */
+
+#ifdef __STDC__
+/* Define this if your compiler supports prototypes. */
+#define USE_PROTOTYPES 1
+#endif
+
+/* Can't use <stdarg.h> without prototypes. */
+#ifndef USE_PROTOTYPES
+#define VARARGS 1
+#endif
+
+/* If your compiler defines __STDC__ but doesn't provide <stdarg.h>,
+you must define VARARGS yourself here. */
+/* #define VARARGS 1 */
+
+/* Define this if you do not have strerror(). */
+#define STRERROR_MISSING 1
+
+/* Define this unless the character testing functions in ctype.h
+are defined for all values representable as an unsigned char. You do
+not need to define this if your system is ANSI C conformant. You
+should define for old Unix systems. */
+/* #define USE_ISASCII 1 */
+
+/* Define this if your system provides the BSD style string operations
+rather than ANSI C ones (eg bcopy() rather than memcpy(), and index()
+rather than strchr()). */
+/* #define BSD_STRINGS 1 */
+
+/* Define this if you have getopt(). */
+#define HAVE_GETOPT 1
+
+/* Define this if you have access(). */
+#define HAVE_ACCESS 1
+
+/* Define this if you have <unistd.h>. */
+#define HAVE_UNISTD_H 1
+
+/* Define this if you have <sys/stat.h>. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define this if you have waitpid(). */
+#define HAVE_WAITPID 1
+
+/* Define this if your system is POSIX.1 (ISO 9945-1:1990) compliant. */
+#define POSIX 1
+
+/* Define this if you have the vfork() system call. */
+#define HAVE_VFORK 1
+
+/* Define this if you have <vfork.h>. */
+#define HAVE_VFORK_H 1
+
+/* Define this if you don't have <stdlib.h> */
+/* #define STDLIB_H_MISSING 1 */
+
+/* Define this if you don't have <stddef.h> */
+/* #define STDDEF_H_MISSING 1 */
+
+/* Define this if you don't have <limits.h> */
+/* #define LIMITS_H_MISSING 1 */
+
+/* Define this if you don't have remove(); unlink() will be used instead. */
+#define REMOVE_MISSING 1
+
+/* Define this if you don't have raise(); kill() will be used instead. */
+#define RAISE_MISSING 1
+
+/* Define this if you don't have fsetpos() and fgetpos(). */
+#define FPOS_MISSING 1
+
+/* Universal pointer type. */
+/* If your compiler doesn't fully support void *, change `void' to `char'. */
+typedef void *UNIV;
+
+/* If your compiler doesn't support void as a function return type,
+change `void' to `int'. */
+typedef void VOID;
+
+/* If you don't have an ANSI C conformant <limits.h>, define
+CHAR_SIGNED as 1 or 0 according to whether the `char' type is signed.
+The <limits.h> on some versions of System Release V 3.2 is not ANSI C
+conformant: the value of CHAR_MIN is 0 even though the `char' type is
+signed. */
+
+/* #define CHAR_SIGNED 1 */
+/* #define CHAR_SIGNED 0 */
+#ifndef CHAR_SIGNED
+#include <limits.h>
+#if CHAR_MIN < 0
+#define CHAR_SIGNED 1
+#else
+#define CHAR_SIGNED 0
+#endif
+#endif /* not CHAR_SIGNED */
+
+/* Assume the system character set is ISO Latin-1. */
+#include "latin1.h"
diff --git a/usr.bin/sgmls/sgmls/unixproc.c b/usr.bin/sgmls/sgmls/unixproc.c
new file mode 100644
index 0000000..9e79d62
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/unixproc.c
@@ -0,0 +1,98 @@
+/* unixproc.c -
+
+ Unix implementation of run_process().
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+#include "config.h"
+
+#ifdef SUPPORT_SUBDOC
+
+#ifdef POSIX
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#endif /* POSIX */
+
+#include "std.h"
+#include "entity.h"
+#include "appl.h"
+
+#ifndef POSIX
+
+#define WIFSTOPPED(s) (((s) & 0377) == 0177)
+#define WIFSIGNALED(s) (((s) & 0377) != 0 && ((s) & 0377 != 0177))
+#define WIFEXITED(s) (((s) & 0377) == 0)
+#define WEXITSTATUS(s) (((s) >> 8) & 0377)
+#define WTERMSIG(s) ((s) & 0177)
+#define WSTOPSIG(s) (((s) >> 8) & 0377)
+#define _SC_OPEN_MAX 0
+#define sysconf(name) (20)
+typedef int pid_t;
+
+#endif /* not POSIX */
+
+#ifndef HAVE_VFORK
+#define vfork() fork()
+#endif /* not HAVE_VFORK */
+
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif /* HAVE_VFORK_H */
+
+int run_process(argv)
+char **argv;
+{
+ pid_t pid;
+ int status;
+ int ret;
+
+ /* Can't trust Unix implementations to support fflush(NULL). */
+ fflush(stderr);
+ fflush(stdout);
+
+ pid = vfork();
+ if (pid == 0) {
+ /* child */
+ int i;
+ int open_max = (int)sysconf(_SC_OPEN_MAX);
+
+ for (i = 3; i < open_max; i++)
+ (void)close(i);
+ execvp(argv[0], argv);
+ appl_error(E_EXEC, argv[0], strerror(errno));
+ fflush(stderr);
+ _exit(127);
+ }
+ if (pid < 0) {
+ appl_error(E_FORK, strerror(errno));
+ return -1;
+ }
+ /* parent */
+ while ((ret = wait(&status)) != pid)
+ if (ret < 0) {
+ appl_error(E_WAIT, strerror(errno));
+ return -1;
+ }
+ if (WIFSIGNALED(status)) {
+ appl_error(E_SIGNAL, argv[0], WTERMSIG(status));
+ return -1;
+ }
+ /* Must have exited normally. */
+ return WEXITSTATUS(status);
+}
+
+#endif /* SUPPORT_SUBDOC */
+
+/*
+Local Variables:
+c-indent-level: 5
+c-continued-statement-offset: 5
+c-brace-offset: -5
+c-argdecl-indent: 0
+c-label-offset: -5
+End:
+*/
diff --git a/usr.bin/sgmls/sgmls/version.c b/usr.bin/sgmls/sgmls/version.c
new file mode 100644
index 0000000..7144593
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/version.c
@@ -0,0 +1 @@
+char *version_string = "1.1";
diff --git a/usr.bin/sgmls/sgmls/xfprintf.c b/usr.bin/sgmls/sgmls/xfprintf.c
new file mode 100644
index 0000000..f544faa
--- /dev/null
+++ b/usr.bin/sgmls/sgmls/xfprintf.c
@@ -0,0 +1,568 @@
+/* xfprintf.c -
+ X/Open extended v?fprintf implemented in terms of v?fprintf.
+
+ Written by James Clark (jjc@jclark.com).
+*/
+
+/* Compile with:
+
+ -DVARARGS to use varargs.h instead of stdarg.h
+ -DLONG_DOUBLE_MISSING if your compiler doesn't like `long double'
+ -DFP_SUPPORT to include floating point stuff
+*/
+
+#include "config.h"
+
+#ifndef HAVE_EXTENDED_PRINTF
+
+#include "std.h"
+
+#ifdef lint
+/* avoid stupid lint warnings */
+#undef va_arg
+#define va_arg(ap, type) (ap, (type)0)
+#endif
+
+#ifdef FP_SUPPORT
+#ifdef LONG_DOUBLE_MISSING
+typedef double long_double;
+#else
+typedef long double long_double;
+#endif
+#endif /* FP_SUPPORT */
+
+#ifndef __STDC__
+#define const /* as nothing */
+#endif
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+#ifdef VARARGS
+typedef int (*printer)();
+#else
+typedef int (*printer)(UNIV, const char *, ...);
+#endif
+
+enum arg_type {
+ NONE,
+ INT,
+ UNSIGNED,
+ LONG,
+ UNSIGNED_LONG,
+#ifdef FP_SUPPORT
+ DOUBLE,
+ LONG_DOUBLE,
+#endif /* FP_SUPPORT */
+ PCHAR,
+ PINT,
+ PLONG,
+ PSHORT
+};
+
+union arg {
+ int i;
+ unsigned u;
+ long l;
+ unsigned long ul;
+#ifdef FP_SUPPORT
+ double d;
+ long_double ld;
+#endif /* FP_SUPPORT */
+ char *pc;
+ UNIV pv;
+ int *pi;
+ short *ps;
+ long *pl;
+};
+
+#define NEXT 0
+#define MISSING 10
+
+struct spec {
+ enum arg_type type;
+ char pos;
+ char field_width;
+ char precision;
+};
+
+#define FLAG_CHARS "-+ #0"
+
+static int parse_spec P((const char **, struct spec *));
+static int find_arg_types P((const char *, enum arg_type *));
+static void get_arg P((enum arg_type, va_list *, union arg *));
+static int do_arg P((UNIV, printer, const char *, enum arg_type, union arg *));
+static int xdoprt P((UNIV, printer, const char *, va_list));
+static int printit P((UNIV, printer, const char *, va_list, int, union arg *));
+static int maybe_positional P((const char *));
+
+/* Return 1 if sucessful, 0 otherwise. **pp points to character after % */
+
+static int parse_spec(pp, sp)
+const char **pp;
+struct spec *sp;
+{
+ char modifier = 0;
+ sp->pos = NEXT;
+ if (isdigit((unsigned char)(**pp)) && (*pp)[1] == '$') {
+ if (**pp == '0')
+ return 0;
+ sp->pos = **pp - '0';
+ *pp += 2;
+ }
+
+ while (**pp != '\0' && strchr(FLAG_CHARS, **pp))
+ *pp += 1;
+
+ /* handle the field width */
+
+ sp->field_width = MISSING;
+ if (**pp == '*') {
+ *pp += 1;
+ if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') {
+ if (**pp == '0')
+ return 0;
+ sp->field_width = **pp - '0';
+ *pp += 2;
+ }
+ else
+ sp->field_width = NEXT;
+ }
+ else {
+ while (isdigit((unsigned char)**pp))
+ *pp += 1;
+ }
+
+ /* handle the precision */
+ sp->precision = MISSING;
+ if (**pp == '.') {
+ *pp += 1;
+ if (**pp == '*') {
+ *pp += 1;
+ if (isdigit((unsigned char)**pp) && (*pp)[1] == '$') {
+ if (**pp == '0')
+ return 0;
+ sp->precision = **pp - '0';
+ *pp += 2;
+ }
+ else
+ sp->precision = NEXT;
+ }
+ else {
+ while (isdigit((unsigned char)**pp))
+ *pp += 1;
+ }
+ }
+ /* handle h l or L */
+
+ if (**pp == 'h' || **pp == 'l' || **pp == 'L') {
+ modifier = **pp;
+ *pp += 1;
+ }
+
+ switch (**pp) {
+ case 'd':
+ case 'i':
+ sp->type = modifier == 'l' ? LONG : INT;
+ break;
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ sp->type = modifier == 'l' ? UNSIGNED_LONG : UNSIGNED;
+ break;
+#ifdef FP_SUPPORT
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ sp->type = modifier == 'L' ? LONG_DOUBLE : DOUBLE;
+ break;
+#endif /* FP_SUPPORT */
+ case 'c':
+ sp->type = INT;
+ break;
+ case 's':
+ sp->type = PCHAR;
+ break;
+ case 'p':
+ /* a pointer to void has the same representation as a pointer to char */
+ sp->type = PCHAR;
+ break;
+ case 'n':
+ if (modifier == 'h')
+ sp->type = PSHORT;
+ else if (modifier == 'l')
+ sp->type = PLONG;
+ else
+ sp->type = PINT;
+ break;
+ case '%':
+ sp->type = NONE;
+ break;
+ default:
+ return 0;
+ }
+ *pp += 1;
+ return 1;
+}
+
+
+static int find_arg_types(format, arg_type)
+ const char *format;
+ enum arg_type *arg_type;
+{
+ int i, pos;
+ const char *p;
+ struct spec spec;
+
+ for (i = 0; i < 9; i++)
+ arg_type[i] = NONE;
+
+ pos = 0;
+
+ p = format;
+ while (*p)
+ if (*p == '%') {
+ p++;
+ if (!parse_spec(&p, &spec))
+ return 0;
+ if (spec.type != NONE) {
+ int n;
+ if (spec.pos == NEXT)
+ n = pos++;
+ else
+ n = spec.pos - 1;
+ if (n < 9) {
+ enum arg_type t = arg_type[n];
+ if (t != NONE && t != spec.type)
+ return 0;
+ arg_type[n] = spec.type;
+ }
+ }
+ if (spec.field_width != MISSING) {
+ int n;
+ if (spec.field_width == NEXT)
+ n = pos++;
+ else
+ n = spec.field_width - 1;
+ if (n < 9) {
+ enum arg_type t = arg_type[n];
+ if (t != NONE && t != INT)
+ return 0;
+ arg_type[n] = INT;
+ }
+ }
+ if (spec.precision != MISSING) {
+ int n;
+ if (spec.precision == NEXT)
+ n = pos++;
+ else
+ n = spec.precision - 1;
+ if (n < 9) {
+ enum arg_type t = arg_type[n];
+ if (t != NONE && t != INT)
+ return 0;
+ arg_type[n] = INT;
+ }
+ }
+ }
+ else
+ p++;
+ return 1;
+}
+
+static void get_arg(arg_type, app, argp)
+ enum arg_type arg_type;
+ va_list *app;
+ union arg *argp;
+{
+ switch (arg_type) {
+ case NONE:
+ break;
+ case INT:
+ argp->i = va_arg(*app, int);
+ break;
+ case UNSIGNED:
+ argp->u = va_arg(*app, unsigned);
+ break;
+ case LONG:
+ argp->l = va_arg(*app, long);
+ break;
+ case UNSIGNED_LONG:
+ argp->ul = va_arg(*app, unsigned long);
+ break;
+#ifdef FP_SUPPORT
+ case DOUBLE:
+ argp->d = va_arg(*app, double);
+ break;
+ case LONG_DOUBLE:
+ argp->ld = va_arg(*app, long_double);
+ break;
+#endif /* FP_SUPPORT */
+ case PCHAR:
+ argp->pc = va_arg(*app, char *);
+ break;
+ case PINT:
+ argp->pi = va_arg(*app, int *);
+ break;
+ case PSHORT:
+ argp->ps = va_arg(*app, short *);
+ break;
+ case PLONG:
+ argp->pl = va_arg(*app, long *);
+ break;
+ default:
+ abort();
+ }
+}
+
+static int do_arg(handle, func, buf, arg_type, argp)
+ UNIV handle;
+ printer func;
+ const char *buf;
+ enum arg_type arg_type;
+ union arg *argp;
+{
+ switch (arg_type) {
+ case NONE:
+ return (*func)(handle, buf);
+ case INT:
+ return (*func)(handle, buf, argp->i);
+ case UNSIGNED:
+ return (*func)(handle, buf, argp->u);
+ case LONG:
+ return (*func)(handle, buf, argp->l);
+ case UNSIGNED_LONG:
+ return (*func)(handle, buf, argp->ul);
+#ifdef FP_SUPPORT
+ case DOUBLE:
+ return (*func)(handle, buf, argp->d);
+ case LONG_DOUBLE:
+ return (*func)(handle, buf, argp->ld);
+#endif /* FP_SUPPORT */
+ case PCHAR:
+ return (*func)(handle, buf, argp->pc);
+ case PINT:
+ return (*func)(handle, buf, argp->pi);
+ case PSHORT:
+ return (*func)(handle, buf, argp->ps);
+ case PLONG:
+ return (*func)(handle, buf, argp->pl);
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+static int printit(handle, func, p, ap, nargs, arg)
+ UNIV handle;
+ printer func;
+ const char *p;
+ va_list ap;
+ int nargs;
+ union arg *arg;
+{
+ char buf[512]; /* enough for a spec */
+ int count = 0;
+ int pos = 0;
+
+ while (*p)
+ if (*p == '%') {
+ char *q;
+ struct spec spec;
+ const char *start;
+ int had_field_width;
+ union arg *argp;
+ union arg a;
+ int res;
+
+ start = ++p;
+ if (!parse_spec(&p, &spec))
+ abort(); /* should have caught it in find_arg_types */
+
+ buf[0] = '%';
+ q = buf + 1;
+
+ if (spec.pos != NEXT)
+ start += 2;
+
+ /* substitute in precision and field width if necessary */
+ had_field_width = 0;
+ while (start < p) {
+ if (*start == '*') {
+ char c;
+ int n, val;
+
+ start++;
+ if (!had_field_width && spec.field_width != MISSING) {
+ c = spec.field_width;
+ had_field_width = 1;
+ }
+ else
+ c = spec.precision;
+ if (c == NEXT)
+ n = pos++;
+ else {
+ start += 2;
+ n = c - 1;
+ }
+ if (n >= nargs)
+ val = va_arg(ap, int);
+ else
+ val = arg[n].i;
+
+ /* ignore negative precision */
+ if (val >= 0 || q[-1] != '.') {
+ (void)sprintf(q, "%d", val);
+ q = strchr(q, '\0');
+ }
+ }
+ else
+ *q++ = *start++;
+ }
+ *q++ = '\0';
+
+ argp = 0;
+ if (spec.type != NONE) {
+ int n = spec.pos == NEXT ? pos++ : spec.pos - 1;
+ if (n >= nargs) {
+ get_arg(spec.type, &ap, &a);
+ argp = &a;
+ }
+ else
+ argp = arg + n;
+ }
+
+ res = do_arg(handle, func, buf, spec.type, argp);
+ if (res < 0)
+ return -1;
+ count += res;
+ }
+ else {
+ if ((*func)(handle, "%c", *p++) < 0)
+ return -1;
+ count++;
+ }
+ return count;
+}
+
+/* Do a quick check to see if it may contains any positional thingies. */
+
+static int maybe_positional(format)
+ const char *format;
+{
+ const char *p;
+
+ p = format;
+ for (;;) {
+ p = strchr(p, '$');
+ if (!p)
+ return 0;
+ if (p - format >= 2
+ && isdigit((unsigned char)p[-1])
+ && (p[-2] == '%' || p[-2] == '*'))
+ break; /* might be a positional thingy */
+ }
+ return 1;
+}
+
+static int xdoprt(handle, func, format, ap)
+ UNIV handle;
+ printer func;
+ const char *format;
+ va_list ap;
+{
+ enum arg_type arg_type[9];
+ union arg arg[9];
+ int nargs, i;
+
+ if (!find_arg_types(format, arg_type))
+ return -1;
+
+ for (nargs = 0; nargs < 9; nargs++)
+ if (arg_type[nargs] == NONE)
+ break;
+
+ for (i = nargs; i < 9; i++)
+ if (arg_type[i] != NONE)
+ return -1;
+
+ for (i = 0; i < nargs; i++)
+ get_arg(arg_type[i], &ap, arg + i);
+
+ return printit(handle, func, format, ap, nargs, arg);
+}
+
+#ifdef VARARGS
+static int do_fprintf(va_alist) va_dcl
+#else
+static int do_fprintf(UNIV p, const char *format,...)
+#endif
+{
+#ifdef VARARGS
+ UNIV p;
+ const char *format;
+#endif
+ va_list ap;
+ int res;
+
+#ifdef VARARGS
+ va_start(ap);
+ p = va_arg(ap, UNIV);
+ format = va_arg(ap, char *);
+#else
+ va_start(ap, format);
+#endif
+
+ res = vfprintf((FILE *)p, format, ap);
+ va_end(ap);
+ return res;
+}
+
+#ifdef VARARGS
+int xfprintf(va_alist) va_dcl
+#else
+int xfprintf(FILE *fp, const char *format, ...)
+#endif
+{
+#ifdef VARARGS
+ FILE *fp;
+ char *format;
+#endif
+ va_list ap;
+ int res;
+
+#ifdef VARARGS
+ va_start(ap);
+ fp = va_arg(ap, FILE *);
+ format = va_arg(ap, char *);
+#else
+ va_start(ap, format);
+#endif
+ if (maybe_positional(format))
+ res = xdoprt((UNIV)fp, do_fprintf, format, ap);
+ else
+ res = vfprintf(fp, format, ap);
+ va_end(ap);
+ return res;
+}
+
+int xvfprintf(fp, format, ap)
+ FILE *fp;
+ const char *format;
+ va_list ap;
+{
+ int res;
+ if (maybe_positional(format))
+ res = xdoprt((UNIV)fp, do_fprintf, format, ap);
+ else
+ res = vfprintf(fp, format, ap);
+ return res;
+}
+
+#endif /* not HAVE_EXTENDED_PRINTF */
diff --git a/usr.bin/sgmls/sgmlsasp/Makefile b/usr.bin/sgmls/sgmlsasp/Makefile
new file mode 100644
index 0000000..69bfdab
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/Makefile
@@ -0,0 +1,18 @@
+#
+# Bmakefile for sgmlsasp
+#
+# $id$
+#
+
+PROG= sgmlsasp
+
+SRCS+= sgmlsasp.c replace.c
+
+CFLAGS+= -I${.CURDIR}/../libsgmls -I${.CURDIR}/../sgmls
+
+LDADD= ${LIBSGMLS}
+DPADD= ${LIBSGMLS}
+
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
+
diff --git a/usr.bin/sgmls/sgmlsasp/replace.c b/usr.bin/sgmls/sgmlsasp/replace.c
new file mode 100644
index 0000000..a37086b
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/replace.c
@@ -0,0 +1,468 @@
+/* replace.c
+ Parse ASP style replacement file.
+
+ Written by James Clark (jjc@jclark.com). */
+
+#include "sgmlsasp.h"
+#include "replace.h"
+
+#define TABLE_SIZE 251
+
+struct table_entry {
+ enum event_type type;
+ char *gi;
+ struct replacement replacement;
+ struct table_entry *next;
+};
+
+struct replacement_table {
+ struct table_entry *table[TABLE_SIZE];
+};
+
+struct buffer {
+ char *s;
+ unsigned len;
+ unsigned size;
+};
+
+/* Tokens returned by get_token(). */
+
+#define STRING 1
+#define STAGO 2
+#define ETAGO 3
+#define PLUS 4
+
+static int get P((void));
+static int peek P((void));
+static int get_token P((void));
+static void scan_name P((struct buffer *, int));
+static struct replacement *define_replacement
+ P((struct replacement_table *, enum event_type, char *));
+static struct replacement_item **parse_string
+ P((struct replacement_item **, int));
+static UNIV xmalloc P((unsigned));
+static UNIV xrealloc P((UNIV, unsigned));
+static struct replacement_item **add_replacement_data
+ P((struct replacement_item **, char *, unsigned));
+static struct replacement_item **add_replacement_attr
+ P((struct replacement_item **, char *));
+static int hash P((enum event_type, char *));
+static NO_RETURN void parse_error VP((char *,...));
+static VOID buffer_init P((struct buffer *));
+static VOID buffer_append P((struct buffer *, int));
+static char *buffer_extract P((struct buffer *));
+#if 0
+static VOID buffer_free P((struct buffer *));
+#endif
+
+#define buffer_length(buf) ((buf)->len)
+
+#define NEW(type) ((type *)xmalloc(sizeof(type)))
+
+static int current_lineno;
+static char *current_file;
+static FILE *fp;
+
+struct replacement_table *make_replacement_table()
+{
+ int i;
+ struct replacement_table *tablep;
+
+ tablep = NEW(struct replacement_table);
+ for (i = 0; i < TABLE_SIZE; i++)
+ tablep->table[i] = 0;
+ return tablep;
+}
+
+void load_replacement_file(tablep, file)
+ struct replacement_table *tablep;
+ char *file;
+{
+ int tok;
+ struct buffer name;
+
+ buffer_init(&name);
+ errno = 0;
+ fp = fopen(file, "r");
+ if (!fp) {
+ if (errno)
+ error("can't open `%s': %s", file, strerror(errno));
+ else
+ error("can't open `%s'", file);
+ }
+
+ current_lineno = 1;
+ current_file = file;
+ tok = get_token();
+ while (tok != EOF) {
+ struct replacement *p;
+ struct replacement_item **tail;
+ enum event_type type;
+
+ if (tok != STAGO && tok != ETAGO)
+ parse_error("syntax error");
+ type = tok == STAGO ? START_ELEMENT : END_ELEMENT;
+ scan_name(&name, '>');
+ p = define_replacement(tablep, type, buffer_extract(&name));
+ tok = get_token();
+ if (tok == PLUS) {
+ if (p)
+ p->flags |= NEWLINE_BEGIN;
+ tok = get_token();
+ }
+ tail = p ? &p->items : 0;
+ while (tok == STRING) {
+ tail = parse_string(tail, type == START_ELEMENT);
+ tok = get_token();
+ }
+ if (tok == PLUS) {
+ if (p)
+ p->flags |= NEWLINE_END;
+ tok = get_token();
+ }
+ }
+ fclose(fp);
+}
+
+static
+struct replacement_item **parse_string(tail, recog_attr)
+ struct replacement_item **tail;
+ int recog_attr;
+{
+ struct buffer buf;
+ unsigned len;
+
+ buffer_init(&buf);
+ for (;;) {
+ int c = get();
+ if (c == '\"')
+ break;
+ if (recog_attr && c == '[') {
+ if (buffer_length(&buf)) {
+ len = buffer_length(&buf);
+ tail = add_replacement_data(tail, buffer_extract(&buf), len);
+ }
+ scan_name(&buf, ']');
+ tail = add_replacement_attr(tail, buffer_extract(&buf));
+ }
+ else {
+ if (c == '\\') {
+ c = get();
+ switch (c) {
+ case EOF:
+ parse_error("unfinished string at end of file");
+ case 's':
+ buffer_append(&buf, ' ');
+ break;
+ case 'n':
+ buffer_append(&buf, '\n');
+ break;
+ case 't':
+ buffer_append(&buf, '\t');
+ break;
+ case 'r':
+ buffer_append(&buf, '\r');
+ break;
+ case 'f':
+ buffer_append(&buf, '\f');
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int val = c - '0';
+ c = peek();
+ if ('0' <= c && c <= '7') {
+ (void)get();
+ val = val*8 + (c - '0');
+ c = peek();
+ if ('0' <= c && c <= '7') {
+ (void)get();
+ val = val*8 + (c - '0');
+ }
+ }
+ buffer_append(&buf, val);
+ break;
+ }
+ default:
+ buffer_append(&buf, c);
+ break;
+ }
+ }
+ else
+ buffer_append(&buf, c);
+ }
+ }
+ len = buffer_length(&buf);
+ if (len > 0)
+ tail = add_replacement_data(tail, buffer_extract(&buf), len);
+ return tail;
+}
+
+static
+struct replacement_item **add_replacement_data(tail, buf, n)
+ struct replacement_item **tail;
+ char *buf;
+ unsigned n;
+{
+ if (!tail)
+ free(buf);
+ else {
+ *tail = NEW(struct replacement_item);
+ (*tail)->type = DATA_REPL;
+ (*tail)->u.data.n = n;
+ (*tail)->next = 0;
+ (*tail)->u.data.s = buf;
+ tail = &(*tail)->next;
+ }
+ return tail;
+}
+
+static
+struct replacement_item **add_replacement_attr(tail, name)
+ struct replacement_item **tail;
+ char *name;
+{
+ if (!tail)
+ free(name);
+ else {
+ *tail = NEW(struct replacement_item);
+ (*tail)->type = ATTR_REPL;
+ (*tail)->next = 0;
+ (*tail)->u.attr = name;
+ tail = &(*tail)->next;
+ }
+ return tail;
+}
+
+static
+int get_token()
+{
+ int c;
+
+ for (;;) {
+ c = get();
+ while (isspace(c))
+ c = get();
+ if (c != '%')
+ break;
+ do {
+ c = get();
+ if (c == EOF)
+ return EOF;
+ } while (c != '\n');
+ }
+ switch (c) {
+ case '+':
+ return PLUS;
+ case '<':
+ c = peek();
+ if (c == '/') {
+ (void)get();
+ return ETAGO;
+ }
+ return STAGO;
+ case '"':
+ return STRING;
+ case EOF:
+ return EOF;
+ default:
+ parse_error("bad input character `%c'", c);
+ }
+ return EOF;
+}
+
+static
+void scan_name(buf, term)
+ struct buffer *buf;
+ int term;
+{
+ int c;
+ for (;;) {
+ c = get();
+ if (c == term)
+ break;
+ if (c == '\n' || c == EOF)
+ parse_error("missing `%c'", term);
+ if (fold_general_names) {
+ if (islower((unsigned char)c))
+ c = toupper((unsigned char)c);
+ }
+ buffer_append(buf, c);
+ }
+ if (buffer_length(buf) == 0)
+ parse_error("empty name");
+ buffer_append(buf, '\0');
+}
+
+static
+int get()
+{
+ int c = getc(fp);
+ if (c == '\n')
+ current_lineno++;
+ return c;
+}
+
+static
+int peek()
+{
+ int c = getc(fp);
+ if (c != EOF)
+ ungetc(c, fp);
+ return c;
+}
+
+struct replacement *lookup_replacement(tablep, type, name)
+ struct replacement_table *tablep;
+ enum event_type type;
+ char *name;
+{
+ int h = hash(type, name);
+ struct table_entry *p;
+
+ for (p = tablep->table[h]; p; p = p->next)
+ if (strcmp(name, p->gi) == 0 && type == p->type)
+ return &p->replacement;
+ return 0;
+}
+
+/* Return 0 if already defined. */
+
+static
+struct replacement *define_replacement(tablep, type, name)
+ struct replacement_table *tablep;
+ enum event_type type;
+ char *name;
+{
+ int h = hash(type, name);
+ struct table_entry *p;
+
+ for (p = tablep->table[h]; p; p = p->next)
+ if (strcmp(name, p->gi) == 0 && type == p->type)
+ return 0;
+ p = NEW(struct table_entry);
+ p->next = tablep->table[h];
+ tablep->table[h] = p;
+ p->type = type;
+ p->gi = name;
+ p->replacement.flags = 0;
+ p->replacement.items = 0;
+ return &p->replacement;
+}
+
+static
+VOID buffer_init(buf)
+ struct buffer *buf;
+{
+ buf->size = buf->len = 0;
+ buf->s = 0;
+}
+
+static
+char *buffer_extract(buf)
+ struct buffer *buf;
+{
+ char *s = buf->s;
+ buf->s = 0;
+ buf->len = 0;
+ buf->size = 0;
+ return s;
+}
+
+#if 0
+static
+VOID buffer_free(buf)
+ struct buffer *buf;
+{
+ if (buf->s) {
+ free((UNIV)buf->s);
+ buf->s = 0;
+ buf->size = buf->size = 0;
+ }
+}
+#endif
+
+static
+VOID buffer_append(buf, c)
+ struct buffer *buf;
+ int c;
+{
+ if (buf->len >= buf->size) {
+ if (!buf->size)
+ buf->s = (char *)xmalloc(buf->size = 10);
+ else
+ buf->s = (char *)xrealloc((UNIV)buf->s, buf->size *= 2);
+ }
+ buf->s[buf->len] = c;
+ buf->len += 1;
+}
+
+static
+int hash(type, s)
+ enum event_type type;
+ char *s;
+{
+ unsigned long h = 0, g;
+
+ while (*s != 0) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ h ^= (int)type;
+ return (int)(h % TABLE_SIZE);
+}
+
+static
+UNIV xmalloc(n)
+ unsigned n;
+{
+ UNIV p = (UNIV)malloc(n);
+ if (!p)
+ parse_error("out of memory");
+ return p;
+}
+
+static
+UNIV xrealloc(p, size)
+ UNIV p;
+ unsigned size;
+{
+ p = (UNIV)realloc(p, size);
+ if (!p)
+ parse_error("out of memory");
+ return p;
+}
+
+static NO_RETURN
+#ifdef VARARGS
+void parse_error(va_alist) va_dcl
+#else
+void parse_error(char *message,...)
+#endif
+{
+ char buf[512];
+#ifdef VARARGS
+ char *message;
+#endif
+ va_list ap;
+
+#ifdef VARARGS
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+ vsprintf(buf, message, ap);
+ va_end(ap);
+ error("%s:%d: %s", current_file, current_lineno, buf);
+}
diff --git a/usr.bin/sgmls/sgmlsasp/replace.h b/usr.bin/sgmls/sgmlsasp/replace.h
new file mode 100644
index 0000000..be2bbcd
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/replace.h
@@ -0,0 +1,35 @@
+/* replace.h
+ Interface to replacement file parser. */
+
+enum replacement_type {
+ DATA_REPL,
+ ATTR_REPL
+ };
+
+struct replacement_item {
+ union {
+ char *attr;
+ struct {
+ char *s;
+ unsigned n;
+ } data;
+ } u;
+ enum replacement_type type;
+ struct replacement_item *next;
+};
+
+#define NEWLINE_BEGIN 01
+#define NEWLINE_END 02
+
+struct replacement {
+ unsigned flags;
+ struct replacement_item *items;
+};
+
+enum event_type { START_ELEMENT, END_ELEMENT };
+
+struct replacement_table *make_replacement_table P((void));
+void load_replacement_file P((struct replacement_table *, char *));
+
+struct replacement *
+lookup_replacement P((struct replacement_table *, enum event_type, char *));
diff --git a/usr.bin/sgmls/sgmlsasp/sgmlsasp.1 b/usr.bin/sgmls/sgmlsasp/sgmlsasp.1
new file mode 100644
index 0000000..ab03371
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/sgmlsasp.1
@@ -0,0 +1,30 @@
+.\" -*- nroff -*-
+.TH SGMLSASP 1
+.SH NAME
+sgmlsasp \- translate output of sgmls using ASP replacement files
+.SH SYNOPSIS
+.B sgmls
+.RB [ \-n ]
+.I replacement_file\|.\|.\|.
+.SH DESCRIPTION
+.I sgmlsasp
+translates the standard input using the specification in
+.I replacement_file\|.\|.\|.
+and writes the result to the standard output.
+The standard input must be in the format output by
+.IR sgmls .
+Each replacement file must be in the format of an
+Amsterdam SGML parser (ASP) replacement file;
+this format is described in the ASP documentation.
+Duplicate replacements are silently ignored.
+The
+.B \-n
+option disables upper-case substitution (folding) for names in
+replacement files; this option should be used with concrete syntaxes
+that do not specify upper-case substitution for general names (that
+is, names that are not entity names).
+.SH BUGS
+References to external data entities are ignored.
+(Support for external data entities is not implemented in ASP.)
+.SH "SEE ALSO"
+.IR sgmls (1)
diff --git a/usr.bin/sgmls/sgmlsasp/sgmlsasp.c b/usr.bin/sgmls/sgmlsasp/sgmlsasp.c
new file mode 100644
index 0000000..eacf1c1
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/sgmlsasp.c
@@ -0,0 +1,278 @@
+/* sgmlsasp.c
+ Translate sgmls output using ASP replacement file.
+
+ Written by James Clark (jjc@jclark.com). */
+
+#include "sgmlsasp.h"
+#include "sgmls.h"
+#include "replace.h"
+#include "getopt.h"
+
+/* Non-zero if general (non-entity) names should be folded to upper case. */
+int fold_general_names = 1;
+
+static char *program_name;
+static char last_char = '\n';
+
+static void output_begin_line P((void));
+static void output_data P((struct sgmls_data *, int));
+static void output_pi P((char *, unsigned));
+static void output_token P((char *));
+static void output_attribute P((struct sgmls_attribute *));
+static void output_data_char P((int));
+static void output_replacement
+ P((struct replacement *, struct sgmls_attribute *));
+static void do_file P((FILE *, struct replacement_table *));
+static void usage P((void));
+static void input_error P((int, char *, unsigned long));
+
+#define output_char(c) (last_char = (c), putchar(c))
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct replacement_table *tablep;
+ int i;
+ int opt;
+ program_name = argv[0];
+
+ while ((opt = getopt(argc, argv, "n")) != EOF)
+ switch (opt) {
+ case 'n':
+ fold_general_names = 0;
+ break;
+ case '?':
+ usage();
+ default:
+ assert(0);
+ }
+ if (argc - optind <= 0)
+ usage();
+ tablep = make_replacement_table();
+ for (i = optind; i < argc; i++)
+ load_replacement_file(tablep, argv[i]);
+ (void)sgmls_set_errhandler(input_error);
+ do_file(stdin, tablep);
+ exit(0);
+}
+
+static
+void usage()
+{
+ fprintf(stderr, "usage: %s [-n] replacement_file...\n", program_name);
+ exit(1);
+}
+
+static
+void input_error(num, str, lineno)
+ int num;
+ char *str;
+ unsigned long lineno;
+{
+ error("Error at input line %lu: %s", lineno, str);
+}
+
+static
+void do_file(fp, tablep)
+ FILE *fp;
+ struct replacement_table *tablep;
+{
+ struct sgmls *sp;
+ struct sgmls_event e;
+
+ sp = sgmls_create(fp);
+ while (sgmls_next(sp, &e))
+ switch (e.type) {
+ case SGMLS_EVENT_DATA:
+ output_data(e.u.data.v, e.u.data.n);
+ break;
+ case SGMLS_EVENT_ENTITY:
+ /* XXX what should we do here? */
+ break;
+ case SGMLS_EVENT_PI:
+ output_pi(e.u.pi.s, e.u.pi.len);
+ break;
+ case SGMLS_EVENT_START:
+ output_replacement(lookup_replacement(tablep,
+ START_ELEMENT, e.u.start.gi),
+ e.u.start.attributes);
+ sgmls_free_attributes(e.u.start.attributes);
+ break;
+ case SGMLS_EVENT_END:
+ output_replacement(lookup_replacement(tablep, END_ELEMENT, e.u.end.gi),
+ 0);
+ break;
+ case SGMLS_EVENT_SUBSTART:
+ break;
+ case SGMLS_EVENT_SUBEND:
+ break;
+ case SGMLS_EVENT_APPINFO:
+ break;
+ case SGMLS_EVENT_CONFORMING:
+ break;
+ default:
+ abort();
+ }
+ sgmls_free(sp);
+}
+
+static
+void output_data(v, n)
+struct sgmls_data *v;
+int n;
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ char *s = v[i].s;
+ int len = v[i].len;
+ for (; len > 0; len--, s++)
+ output_data_char(*s);
+ }
+}
+
+static
+void output_pi(s, len)
+ char *s;
+ unsigned len;
+{
+ for (; len > 0; len--, s++)
+ output_data_char(*s);
+}
+
+static
+void output_replacement(repl, attributes)
+struct replacement *repl;
+struct sgmls_attribute *attributes;
+{
+ struct replacement_item *p;
+ struct sgmls_attribute *a;
+ int i;
+
+ if (!repl)
+ return;
+ if (repl->flags & NEWLINE_BEGIN)
+ output_begin_line();
+
+ for (p = repl->items; p; p = p->next)
+ switch (p->type) {
+ case DATA_REPL:
+ for (i = 0; i < p->u.data.n; i++)
+ output_char(p->u.data.s[i]);
+ break;
+ case ATTR_REPL:
+ for (a = attributes; a; a = a->next)
+ if (strcmp(a->name, p->u.attr) == 0) {
+ output_attribute(a);
+ break;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ if (repl->flags & NEWLINE_END)
+ output_begin_line();
+}
+
+static
+void output_attribute(p)
+struct sgmls_attribute *p;
+{
+ switch (p->type) {
+ case SGMLS_ATTR_IMPLIED:
+ break;
+ case SGMLS_ATTR_CDATA:
+ output_data(p->value.data.v, p->value.data.n);
+ break;
+ case SGMLS_ATTR_TOKEN:
+ {
+ char **token = p->value.token.v;
+ int n = p->value.token.n;
+
+ if (n > 0) {
+ int i;
+ output_token(token[0]);
+ for (i = 1; i < n; i++) {
+ output_char(' ');
+ output_token(token[i]);
+ }
+ }
+ }
+ break;
+ case SGMLS_ATTR_ENTITY:
+ {
+ struct sgmls_entity **v = p->value.entity.v;
+ int n = p->value.entity.n;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (i > 0)
+ output_char(' ');
+ output_token(v[i]->is_internal
+ ? v[i]->u.internal.name
+ : v[i]->u.external.name);
+ }
+ }
+ break;
+ case SGMLS_ATTR_NOTATION:
+ if (p->value.notation)
+ output_token(p->value.notation->name);
+ break;
+ default:
+ abort();
+ }
+}
+
+static
+void output_token(s)
+ char *s;
+{
+ for (; *s; s++)
+ output_char(*s);
+}
+
+static
+void output_data_char(c)
+ int c;
+{
+ if (c != RSCHAR) {
+ if (c == RECHAR)
+ c = '\n';
+ output_char(c);
+ }
+}
+
+static
+void output_begin_line()
+{
+ if (last_char != '\n')
+ output_char('\n');
+}
+
+NO_RETURN
+#ifdef VARARGS
+void error(va_alist) va_dcl
+#else
+void error(char *message,...)
+#endif
+{
+#ifdef VARARGS
+ char *message;
+#endif
+ va_list ap;
+
+ fprintf(stderr, "%s: ", program_name);
+#ifdef VARARGS
+ va_start(ap);
+ message = va_arg(ap, char *);
+#else
+ va_start(ap, message);
+#endif
+ vfprintf(stderr, message, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+}
diff --git a/usr.bin/sgmls/sgmlsasp/sgmlsasp.h b/usr.bin/sgmls/sgmlsasp/sgmlsasp.h
new file mode 100644
index 0000000..b3ad402
--- /dev/null
+++ b/usr.bin/sgmls/sgmlsasp/sgmlsasp.h
@@ -0,0 +1,26 @@
+/* sgmlsasp.h */
+
+#include "config.h"
+#include "std.h"
+
+#ifdef USE_PROTOTYPES
+#define P(parms) parms
+#else
+#define P(parms) ()
+#endif
+
+#ifdef __GNUC__
+#define NO_RETURN volatile
+#else
+#define NO_RETURN /* as nothing */
+#endif
+
+#ifdef VARARGS
+#define VP(parms) ()
+#else
+#define VP(parms) P(parms)
+#endif
+
+NO_RETURN void error VP((char *,...));
+
+extern int fold_general_names;
diff --git a/usr.bin/sgmls/unix.cfg b/usr.bin/sgmls/unix.cfg
new file mode 100644
index 0000000..0bc8410
--- /dev/null
+++ b/usr.bin/sgmls/unix.cfg
@@ -0,0 +1,147 @@
+/* unix.cfg: Configuration file for sgmls on Unix. */
+
+/* A list of filename templates to use for searching for external entities.
+The filenames are separated by the character specified in PATH_FILE_SEP.
+See sgmls.man for details. */
+#define DEFAULT_PATH "/usr/local/lib/sgml/%O/%C/%T:%N.%X:%N.%D"
+/* The character that separates the filenames templates. */
+#define PATH_FILE_SEP ':'
+/* The character that separates filenames in a system identifier.
+Usually the same as PATH_FILE_SEP. */
+#define SYSID_FILE_SEP ':'
+/* The environment variable that contains the list of filename templates. */
+#define PATH_ENV_VAR "SGML_PATH"
+
+/* MIN_DAT_SUBS_FROM and MIN_DATS_SUBS_TO tell sgmls how to transform a name
+or system identifier into a legal filename. A character in
+MIN_DAT_SUBS_FROM will be transformed into the character in the
+corresponding position in MIN_DAT_SUBS_TO. If there is no such
+position, then the character is removed. */
+/* This says that spaces should be transformed to underscores, and
+slashes to percents. */
+#define MIN_DAT_SUBS_FROM " /"
+#define MIN_DAT_SUBS_TO "_%"
+
+/* Define this to allow tracing. */
+/* #define TRACE 1 */
+
+/* Define this you want support for subdocuments. This is implemented
+using features that are not part of Standard C, so you might not want
+to define it if you are porting to a new system. Otherwise I suggest
+you leave it defined. */
+#define SUPPORT_SUBDOC 1
+
+/* Define HAVE_EXTENDED_PRINTF if your *printf functions supports
+X/Open extensions; if they do, then, for example,
+
+ printf("%2$s%1$s", "bar", "foo")
+
+should print `foobar'. */
+
+/* #define HAVE_EXTENDED_PRINTF 1 */
+
+/* Define HAVE_CAT if your system provides the X/Open message
+catalogue functions catopen() and catgets(), and you want to use them.
+An implementations of these functions is included and will be used if
+you don't define this. On SunOS 4.1.1, if you do define this you
+should set CC=/usr/xpg2bin/cc in the makefile. */
+
+/* #define HAVE_CAT 1 */
+
+#ifdef __STDC__
+/* Define this if your compiler supports prototypes. */
+#define USE_PROTOTYPES 1
+#endif
+
+/* Can't use <stdarg.h> without prototypes. */
+#ifndef USE_PROTOTYPES
+#define VARARGS 1
+#endif
+
+/* If your compiler defines __STDC__ but doesn't provide <stdarg.h>,
+you must define VARARGS yourself here. */
+/* #define VARARGS 1 */
+
+/* Define this if you do not have strerror(). */
+#define STRERROR_MISSING 1
+
+/* Define this unless the character testing functions in ctype.h
+are defined for all values representable as an unsigned char. You do
+not need to define this if your system is ANSI C conformant. You
+should define for old Unix systems. */
+/* #define USE_ISASCII 1 */
+
+/* Define this if your system provides the BSD style string operations
+rather than ANSI C ones (eg bcopy() rather than memcpy(), and index()
+rather than strchr()). */
+/* #define BSD_STRINGS 1 */
+
+/* Define this if you have getopt(). */
+#define HAVE_GETOPT 1
+
+/* Define this if you have access(). */
+#define HAVE_ACCESS 1
+
+/* Define this if you have <unistd.h>. */
+#define HAVE_UNISTD_H 1
+
+/* Define this if you have <sys/stat.h>. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define this if you have waitpid(). */
+#define HAVE_WAITPID 1
+
+/* Define this if your system is POSIX.1 (ISO 9945-1:1990) compliant. */
+#define POSIX 1
+
+/* Define this if you have the vfork() system call. */
+#define HAVE_VFORK 1
+
+/* Define this if you have <vfork.h>. */
+#define HAVE_VFORK_H 1
+
+/* Define this if you don't have <stdlib.h> */
+/* #define STDLIB_H_MISSING 1 */
+
+/* Define this if you don't have <stddef.h> */
+/* #define STDDEF_H_MISSING 1 */
+
+/* Define this if you don't have <limits.h> */
+/* #define LIMITS_H_MISSING 1 */
+
+/* Define this if you don't have remove(); unlink() will be used instead. */
+#define REMOVE_MISSING 1
+
+/* Define this if you don't have raise(); kill() will be used instead. */
+#define RAISE_MISSING 1
+
+/* Define this if you don't have fsetpos() and fgetpos(). */
+#define FPOS_MISSING 1
+
+/* Universal pointer type. */
+/* If your compiler doesn't fully support void *, change `void' to `char'. */
+typedef void *UNIV;
+
+/* If your compiler doesn't support void as a function return type,
+change `void' to `int'. */
+typedef void VOID;
+
+/* If you don't have an ANSI C conformant <limits.h>, define
+CHAR_SIGNED as 1 or 0 according to whether the `char' type is signed.
+The <limits.h> on some versions of System Release V 3.2 is not ANSI C
+conformant: the value of CHAR_MIN is 0 even though the `char' type is
+signed. */
+
+/* #define CHAR_SIGNED 1 */
+/* #define CHAR_SIGNED 0 */
+#ifndef CHAR_SIGNED
+#include <limits.h>
+#if CHAR_MIN < 0
+#define CHAR_SIGNED 1
+#else
+#define CHAR_SIGNED 0
+#endif
+#endif /* not CHAR_SIGNED */
+
+/* Assume the system character set is ISO Latin-1. */
+#include "latin1.h"
diff --git a/usr.bin/shar/Makefile b/usr.bin/shar/Makefile
new file mode 100644
index 0000000..79895df
--- /dev/null
+++ b/usr.bin/shar/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+MAN1= shar.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/shar.sh ${DESTDIR}/usr/bin/shar
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/shar/shar.1 b/usr.bin/shar/shar.1
new file mode 100644
index 0000000..71202ee
--- /dev/null
+++ b/usr.bin/shar/shar.1
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)shar.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt SHAR 1
+.Os BSD 4.4
+.Sh NAME
+.Nm shar
+.Nd create a shell archive of files
+.Sh SYNOPSIS
+.Nm shar Ar
+.Sh DESCRIPTION
+.Nm Shar
+writes an
+.Xr sh 1
+shell script to the standard output which will recreate the file
+hierarchy specified by the command line operands.
+Directories will be recreated and must be specified before the
+files they contain (the
+.Xr find 1
+utility does this correctly).
+.Pp
+.Nm Shar
+is normally used for distributing files by
+.Xr ftp 1
+or
+.Xr mail 1 .
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr uuencode 1 ,
+.Xr tar 1
+.Sh BUGS
+.Nm Shar
+makes no provisions for special types of files or files containing
+magic characters.
+.Pp
+It is easy to insert trojan horses into
+.Nm shar
+files.
+It is strongly recommended that all shell archive files be examined
+before running them through
+.Xr sh 1 .
+Archives produced using this implementation of
+.Nm shar
+may be easily examined with the command:
+.Bd -literal -offset indent
+egrep -v '^[X#]' shar.file
+.Ed
+.Sh EXAMPLES
+To create a shell archive of the program
+.Xr ls 1
+and mail it to Rick:
+.Bd -literal -offset indent
+cd ls
+shar `find . -print` \&| mail -s "ls source" rick
+.Ed
+.Pp
+To recreate the program directory:
+.Bd -literal -offset indent
+mkdir ls
+cd ls
+...
+<delete header lines and examine mailed archive>
+...
+sh archive
+.Ed
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/shar/shar.sh b/usr.bin/shar/shar.sh
new file mode 100644
index 0000000..08db22c
--- /dev/null
+++ b/usr.bin/shar/shar.sh
@@ -0,0 +1,74 @@
+#!/bin/sh -
+#
+# Copyright (c) 1990, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)shar.sh 8.1 (Berkeley) 6/6/93
+#
+
+if [ $# -eq 0 ]; then
+ echo 'usage: shar file ...'
+ exit 1
+fi
+
+cat << EOF
+# This is a shell archive. Save it in a file, remove anything before
+# this line, and then unpack it by entering "sh file". Note, it may
+# create directories; files and directories will be owned by you and
+# have default permissions.
+#
+# This archive contains:
+#
+EOF
+
+for i
+do
+ echo "# $i"
+done
+
+echo "#"
+
+for i
+do
+ if [ -d $i ]; then
+ echo "echo c - $i"
+ echo "mkdir -p $i > /dev/null 2>&1"
+ else
+ echo "echo x - $i"
+ echo "sed 's/^X//' >$i << 'END-of-$i'"
+ sed 's/^/X/' $i
+ echo "END-of-$i"
+ fi
+done
+echo exit
+echo ""
+
+exit 0
diff --git a/usr.bin/showmount/Makefile b/usr.bin/showmount/Makefile
new file mode 100644
index 0000000..9bf4783
--- /dev/null
+++ b/usr.bin/showmount/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= showmount
+MAN8= showmount.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/showmount/showmount.8 b/usr.bin/showmount/showmount.8
new file mode 100644
index 0000000..05c89a9
--- /dev/null
+++ b/usr.bin/showmount/showmount.8
@@ -0,0 +1,93 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Rick Macklem at The University of Guelph.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)showmount.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt SHOWMOUNT 8
+.Os BSD 4
+.Sh NAME
+.Nm showmount
+.Nd show remote nfs mounts on host
+.Sh SYNOPSIS
+.Nm showmount
+.Op Fl ade3
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Showmount
+shows status information about the
+.Tn NFS
+server on
+.Ar host .
+By default it prints the names of all hosts that have
+.Tn NFS
+file systems mounted
+on the host. See
+.%T "NFS: Network File System Protocol Specification" ,
+RFC 1094,
+Appendix A,
+and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I,
+for a detailed description of the protocol.
+.Bl -tag -width Ds
+.It Fl a
+List all mount points in the form:
+.Bd -ragged -offset indent -compact
+.Ar host : Ns Ar dirpath
+.Ed
+.It Fl d
+List directory paths of mount points instead of hosts
+.It Fl e
+Show the
+.Ar host Ns 's
+exports list
+.It Fl 3
+Use mount protocol Version 3, compatible with NFS Version 3.
+.El
+.Sh SEE ALSO
+.Xr mount 1 ,
+.Xr mountd 8
+.Sh BUGS
+The mount daemon running on the server only has an idea of the actual mounts,
+since the
+.Tn NFS
+server is stateless.
+.Nm Showmount
+will only display the information
+as accurately as the mount daemon reports it.
+.Sh HISTORY
+The
+.Nm showmount
+utility first appeared in 4.4BSD.
diff --git a/usr.bin/showmount/showmount.c b/usr.bin/showmount/showmount.c
new file mode 100644
index 0000000..8cb342d
--- /dev/null
+++ b/usr.bin/showmount/showmount.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)showmount.c 8.1 (Berkeley) 6/6/93";
+#endif not lint
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <nfs/rpcv2.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Constant defs */
+#define ALL 1
+#define DIRS 2
+
+#define DODUMP 0x1
+#define DOEXPORTS 0x2
+
+struct mountlist {
+ struct mountlist *ml_left;
+ struct mountlist *ml_right;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct grouplist {
+ struct grouplist *gr_next;
+ char gr_name[RPCMNT_NAMELEN+1];
+};
+
+struct exportslist {
+ struct exportslist *ex_next;
+ struct grouplist *ex_groups;
+ char ex_dirp[RPCMNT_PATHLEN+1];
+};
+
+static struct mountlist *mntdump;
+static struct exportslist *exports;
+static int type = 0;
+int xdr_mntdump(), xdr_exports();
+
+/*
+ * This command queries the NFS mount daemon for it's mount list and/or
+ * it's exports list and prints them out.
+ * See "NFS: Network File System Protocol Specification, RFC1094, Appendix A"
+ * and the "Network File System Protocol XXX.."
+ * for detailed information on the protocol.
+ */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct mountlist *mntp;
+ register struct exportslist *exp;
+ register struct grouplist *grp;
+ extern char *optarg;
+ extern int optind;
+ register int rpcs = 0, mntvers = 1;
+ char ch;
+ char *host;
+ int estat;
+
+ while ((ch = getopt(argc, argv, "ade3")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ if (type == 0) {
+ type = ALL;
+ rpcs |= DODUMP;
+ } else
+ usage();
+ break;
+ case 'd':
+ if (type == 0) {
+ type = DIRS;
+ rpcs |= DODUMP;
+ } else
+ usage();
+ break;
+ case 'e':
+ rpcs |= DOEXPORTS;
+ break;
+ case '3':
+ mntvers = 3;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ host = *argv;
+ else
+ host = "localhost";
+
+ if (rpcs == 0)
+ rpcs = DODUMP;
+
+ if (rpcs & DODUMP)
+ if ((estat = callrpc(host, RPCPROG_MNT, mntvers,
+ RPCMNT_DUMP, xdr_void, (char *)0,
+ xdr_mntdump, (char *)&mntdump)) != 0) {
+ clnt_perrno(estat);
+ fprintf(stderr, "Can't do Mountdump rpc\n");
+ exit(1);
+ }
+ if (rpcs & DOEXPORTS)
+ if ((estat = callrpc(host, RPCPROG_MNT, mntvers,
+ RPCMNT_EXPORT, xdr_void, (char *)0,
+ xdr_exports, (char *)&exports)) != 0) {
+ clnt_perrno(estat);
+ fprintf(stderr, "Can't do Exports rpc\n");
+ exit(1);
+ }
+
+ /* Now just print out the results */
+ if (rpcs & DODUMP) {
+ switch (type) {
+ case ALL:
+ printf("All mount points on %s:\n", host);
+ break;
+ case DIRS:
+ printf("Directories on %s:\n", host);
+ break;
+ default:
+ printf("Hosts on %s:\n", host);
+ break;
+ };
+ print_dump(mntdump);
+ }
+ if (rpcs & DOEXPORTS) {
+ printf("Exports list on %s:\n", host);
+ exp = exports;
+ while (exp) {
+ printf("%-35s", exp->ex_dirp);
+ grp = exp->ex_groups;
+ if (grp == NULL) {
+ printf("Everyone\n");
+ } else {
+ while (grp) {
+ printf("%s ", grp->gr_name);
+ grp = grp->gr_next;
+ }
+ printf("\n");
+ }
+ exp = exp->ex_next;
+ }
+ }
+}
+
+/*
+ * Xdr routine for retrieving the mount dump list
+ */
+xdr_mntdump(xdrsp, mlp)
+ XDR *xdrsp;
+ struct mountlist **mlp;
+{
+ register struct mountlist *mp;
+ register struct mountlist *tp;
+ register struct mountlist **otp;
+ int val, val2;
+ int bool;
+ char *strp;
+
+ *mlp = (struct mountlist *)0;
+ if (!xdr_bool(xdrsp, &bool))
+ return (0);
+ while (bool) {
+ mp = (struct mountlist *)malloc(sizeof(struct mountlist));
+ if (mp == NULL)
+ return (0);
+ mp->ml_left = mp->ml_right = (struct mountlist *)0;
+ strp = mp->ml_host;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = mp->ml_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+
+ /*
+ * Build a binary tree on sorted order of either host or dirp.
+ * Drop any duplications.
+ */
+ if (*mlp == NULL) {
+ *mlp = mp;
+ } else {
+ tp = *mlp;
+ while (tp) {
+ val = strcmp(mp->ml_host, tp->ml_host);
+ val2 = strcmp(mp->ml_dirp, tp->ml_dirp);
+ switch (type) {
+ case ALL:
+ if (val == 0) {
+ if (val2 == 0) {
+ free((caddr_t)mp);
+ goto next;
+ }
+ val = val2;
+ }
+ break;
+ case DIRS:
+ if (val2 == 0) {
+ free((caddr_t)mp);
+ goto next;
+ }
+ val = val2;
+ break;
+ default:
+ if (val == 0) {
+ free((caddr_t)mp);
+ goto next;
+ }
+ break;
+ };
+ if (val < 0) {
+ otp = &tp->ml_left;
+ tp = tp->ml_left;
+ } else {
+ otp = &tp->ml_right;
+ tp = tp->ml_right;
+ }
+ }
+ *otp = mp;
+ }
+next:
+ if (!xdr_bool(xdrsp, &bool))
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Xdr routine to retrieve exports list
+ */
+xdr_exports(xdrsp, exp)
+ XDR *xdrsp;
+ struct exportslist **exp;
+{
+ register struct exportslist *ep;
+ register struct grouplist *gp;
+ int bool, grpbool;
+ char *strp;
+
+ *exp = (struct exportslist *)0;
+ if (!xdr_bool(xdrsp, &bool))
+ return (0);
+ while (bool) {
+ ep = (struct exportslist *)malloc(sizeof(struct exportslist));
+ if (ep == NULL)
+ return (0);
+ ep->ex_groups = (struct grouplist *)0;
+ strp = ep->ex_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ if (!xdr_bool(xdrsp, &grpbool))
+ return (0);
+ while (grpbool) {
+ gp = (struct grouplist *)malloc(sizeof(struct grouplist));
+ if (gp == NULL)
+ return (0);
+ strp = gp->gr_name;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ gp->gr_next = ep->ex_groups;
+ ep->ex_groups = gp;
+ if (!xdr_bool(xdrsp, &grpbool))
+ return (0);
+ }
+ ep->ex_next = *exp;
+ *exp = ep;
+ if (!xdr_bool(xdrsp, &bool))
+ return (0);
+ }
+ return (1);
+}
+
+usage()
+{
+ fprintf(stderr, "usage: showmount [-ade] host\n");
+ exit(1);
+}
+
+/*
+ * Print the binary tree in inorder so that output is sorted.
+ */
+print_dump(mp)
+ struct mountlist *mp;
+{
+
+ if (mp == NULL)
+ return;
+ if (mp->ml_left)
+ print_dump(mp->ml_left);
+ switch (type) {
+ case ALL:
+ printf("%s:%s\n", mp->ml_host, mp->ml_dirp);
+ break;
+ case DIRS:
+ printf("%s\n", mp->ml_dirp);
+ break;
+ default:
+ printf("%s\n", mp->ml_host);
+ break;
+ };
+ if (mp->ml_right)
+ print_dump(mp->ml_right);
+}
diff --git a/usr.bin/size/Makefile b/usr.bin/size/Makefile
new file mode 100644
index 0000000..3ad91be
--- /dev/null
+++ b/usr.bin/size/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= size
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/size/size.1 b/usr.bin/size/size.1
new file mode 100644
index 0000000..5f3b2fa
--- /dev/null
+++ b/usr.bin/size/size.1
@@ -0,0 +1,60 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)size.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt SIZE 1
+.Os
+.Sh NAME
+.Nm size
+.Nd display object file segment sizes (text, data and bss)
+.Sh SYNOPSIS
+.Nm size
+.Op Ar object_file ...
+.Sh DESCRIPTION
+.Nm Size
+displays the text, data and bss segment sizes of the specified
+.Ar object_file
+in bytes (in decimal), and the sum of the three segments (in
+decimal and hexadecimal).
+If no
+.Ar object_file
+is specified
+.Nm
+attempts to report on the file
+.Pa a.out .
+.Sh SEE ALSO
+.Xr a.out 5
+.Sh HISTORY
+A
+.Nm size
+command appeared in Version 6 AT&T Unix.
diff --git a/usr.bin/size/size.1aout b/usr.bin/size/size.1aout
new file mode 100644
index 0000000..5f3b2fa
--- /dev/null
+++ b/usr.bin/size/size.1aout
@@ -0,0 +1,60 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)size.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt SIZE 1
+.Os
+.Sh NAME
+.Nm size
+.Nd display object file segment sizes (text, data and bss)
+.Sh SYNOPSIS
+.Nm size
+.Op Ar object_file ...
+.Sh DESCRIPTION
+.Nm Size
+displays the text, data and bss segment sizes of the specified
+.Ar object_file
+in bytes (in decimal), and the sum of the three segments (in
+decimal and hexadecimal).
+If no
+.Ar object_file
+is specified
+.Nm
+attempts to report on the file
+.Pa a.out .
+.Sh SEE ALSO
+.Xr a.out 5
+.Sh HISTORY
+A
+.Nm size
+command appeared in Version 6 AT&T Unix.
diff --git a/usr.bin/size/size.c b/usr.bin/size/size.c
new file mode 100644
index 0000000..674c3c8
--- /dev/null
+++ b/usr.bin/size/size.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)size.c 8.2 (Berkeley) 12/9/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <a.out.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+void err __P((const char *, ...));
+int show __P((int, char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, eval;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ eval = 0;
+ if (*argv)
+ do {
+ eval |= show(argc, *argv);
+ } while (*++argv);
+ else
+ eval |= show(1, "a.out");
+ exit(eval);
+}
+
+int
+show(count, name)
+ int count;
+ char *name;
+{
+ static int first = 1;
+ struct exec head;
+ u_long total;
+ int fd;
+
+ if ((fd = open(name, O_RDONLY, 0)) < 0) {
+ err("%s: %s", name, strerror(errno));
+ return (1);
+ }
+ if (read(fd, &head, sizeof(head)) != sizeof(head) || N_BADMAG(head)) {
+ (void)close(fd);
+ err("%s: not in a.out format", name);
+ return (1);
+ }
+ (void)close(fd);
+
+ if (first) {
+ first = 0;
+ (void)printf("text\tdata\tbss\tdec\thex\n");
+ }
+ total = head.a_text + head.a_data + head.a_bss;
+ (void)printf("%lu\t%lu\t%lu\t%lu\t%lx", head.a_text, head.a_data,
+ head.a_bss, total, total);
+ if (count > 1)
+ (void)printf("\t%s", name);
+ (void)printf("\n");
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: size [file ...]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "size: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+}
diff --git a/usr.bin/soelim/Makefile b/usr.bin/soelim/Makefile
new file mode 100644
index 0000000..8f86a63
--- /dev/null
+++ b/usr.bin/soelim/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= soelim
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/soelim/soelim.1 b/usr.bin/soelim/soelim.1
new file mode 100644
index 0000000..68575aa
--- /dev/null
+++ b/usr.bin/soelim/soelim.1
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)soelim.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt SOELIM 1
+.Os BSD 3
+.Sh NAME
+.Nm soelim
+.Nd eliminate \&.so's from nroff input
+.Sh SYNOPSIS
+.Nm soelim
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Soelim
+reads the specified files or the standard input and performs the textual
+inclusion implied by the
+.Xr nroff 1
+directives of the form:
+.Pp
+.Dl \&.so somefile
+.Pp
+The directives need to appear at the beginning of input lines.
+This is useful since programs such as
+.Xr tbl 1
+do not normally do this; it allows the placement of individual tables
+in separate files to be run as a part of a large document.
+.Pp
+An argument consisting of a single minus
+.Ql Fl
+is taken to be
+a file name corresponding to the standard input.
+.Pp
+Note that inclusion can be suppressed by using
+.Ql \e'
+instead of
+.Ql \e. ,
+i.e.
+.Pp
+.Dl \'so /usr/lib/tmac.s
+.Pp
+A sample usage of
+.Nm soelim
+would be
+.Pp
+.Bd -literal -offset indent -compact
+soelim exum?.n \&| tbl \&| nroff \-ms \&| col \&| lpr
+.Ed
+.Sh SEE ALSO
+.Xr colcrt 1 ,
+.Xr more 1
+.Sh BUGS
+The format of the source commands must involve no strangeness \-
+exactly one blank must precede and no blanks follow the file name.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/soelim/soelim.c b/usr.bin/soelim/soelim.c
new file mode 100644
index 0000000..fab07c3
--- /dev/null
+++ b/usr.bin/soelim/soelim.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)soelim.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+/*
+ * soelim - a filter to process n/troff input eliminating .so's
+ *
+ * Author: Bill Joy UCB July 8, 1977
+ *
+ * This program eliminates .so's from a n/troff input stream.
+ * It can be used to prepare safe input for submission to the
+ * phototypesetter since the software supporting the operator
+ * doesn't let him do chdir.
+ *
+ * This is a kludge and the operator should be given the
+ * ability to do chdir.
+ *
+ * This program is more generally useful, it turns out, because
+ * the program tbl doesn't understand ".so" directives.
+ */
+#define STDIN_NAME "-"
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ argc--;
+ argv++;
+ if (argc == 0) {
+ (void)process(STDIN_NAME);
+ exit(0);
+ }
+ do {
+ (void)process(argv[0]);
+ argv++;
+ argc--;
+ } while (argc > 0);
+ exit(0);
+}
+
+int process(file)
+ char *file;
+{
+ register char *cp;
+ register int c;
+ char fname[BUFSIZ];
+ FILE *soee;
+ int isfile;
+
+ if (!strcmp(file, STDIN_NAME)) {
+ soee = stdin;
+ } else {
+ soee = fopen(file, "r");
+ if (soee == NULL) {
+ perror(file);
+ return(-1);
+ }
+ }
+ for (;;) {
+ c = getc(soee);
+ if (c == EOF)
+ break;
+ if (c != '.')
+ goto simple;
+ c = getc(soee);
+ if (c != 's') {
+ putchar('.');
+ goto simple;
+ }
+ c = getc(soee);
+ if (c != 'o') {
+ printf(".s");
+ goto simple;
+ }
+ do
+ c = getc(soee);
+ while (c == ' ' || c == '\t');
+ cp = fname;
+ isfile = 0;
+ for (;;) {
+ switch (c) {
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case EOF:
+ goto donename;
+
+ default:
+ *cp++ = c;
+ c = getc(soee);
+ isfile++;
+ continue;
+ }
+ }
+donename:
+ if (cp == fname) {
+ printf(".so");
+ goto simple;
+ }
+ *cp = 0;
+ if (process(fname) < 0)
+ if (isfile)
+ printf(".so %s\n", fname);
+ continue;
+simple:
+ if (c == EOF)
+ break;
+ putchar(c);
+ if (c != '\n') {
+ c = getc(soee);
+ goto simple;
+ }
+ }
+ if (soee != stdin) {
+ fclose(soee);
+ }
+ return(0);
+}
diff --git a/usr.bin/sort/sort.1 b/usr.bin/sort/sort.1
new file mode 100644
index 0000000..0ff0633
--- /dev/null
+++ b/usr.bin/sort/sort.1
@@ -0,0 +1,310 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sort.1 8.1 (Berkeley) 6/10/93
+.\"
+.Dd June 10, 1993
+.Dt SORT 1
+.Os
+.Sh NAME
+.Nm sort
+.Nd sort or merge text files
+.Sh SYNOPSIS
+.Nm sort
+.Op Fl mubdfinrtx
+.Oo
+.Cm \(pl Ns Ar pos1
+.Op Fl Ns Ar pos2
+.Oc
+.Ar ...
+.Op Fl o Ar output
+.Op Fl T Ar directory
+.Op Ar file
+.Ar ...
+.Sh DESCRIPTION
+The
+.Nm sort
+utility
+sorts text files by lines.
+Comparisons are based on one or more sort keys (or fields) extracted
+from each line of input, and are performed
+lexicographically. By default, if keys are not given,
+.Nm sort
+regards each input line as a single field.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Check that the single input file is sorted lexicographically.
+If the file is not sorted,
+.Nm sort
+sorts it and writes the sorted output to the standard output or the
+filename specified by the
+.Fl o
+option.
+.It Fl m
+Merge only; the input files are assumed to be pre-sorted.
+.It Fl o Ar output
+The argument given is the name of an
+.Ar output
+file to
+be used instead of the standard output.
+This file
+can be the same as one of the input files.
+.It Fl T Ar directory
+The argument
+.Ar directory
+is used for creating temporary files.
+.It Fl u
+Unique: suppress all but one in each set of lines
+having equal keys.
+If used with the
+.Fl c
+option,
+check that there are no lines with duplicate keys.
+.El
+.Pp
+The following options override the default ordering rules.
+When ordering options appear independent of key field
+specifications, the requested field ordering rules are
+applied globally to all sort keys.
+.\" When attached to a
+.\" specific key
+.\" (see
+.\" .Fl k ) ,
+.\" the specified ordering options override
+.\" all global ordering options for that key.
+.Bl -tag -width indent
+.It Fl d
+Only blank space and alphanumeric characters
+.\" according
+.\" to the current setting of LC_CTYPE
+are used
+in making comparisons.
+.It Fl f
+Considers all lowercase characters that have uppercase
+equivalents to be the same for purposes of
+comparison.
+.It Fl i
+Ignore all non-printable characters.
+.It Fl n
+An initial numeric string, consisting of optional
+blank space, optional minus sign, and zero or more
+digits (including decimal point)
+.\" with
+.\" optional radix character and thousands
+.\" separator
+.\" (as defined in the current locale),
+is sorted by arithmetic value.
+The
+.Fl n
+option implies
+the
+.Fl b
+option. (See below.)
+Note that the
+.Fl b
+option
+is only effective when key fields have been specified
+and that
+.Fl \&0
+is considered equal to zero.
+.optional It Fl r
+Reverse the sense of comparisons.
+.El
+.Pp
+The treatment of field separators can be altered using the
+options:
+.Bl -tag -width indent
+.It Fl b
+Leading blank spaces are ignored when determining the starting
+ending positions of a restricted sort key.
+If the
+.Fl b
+option is specified before the first
+.Cm \(pl Ns Ar pos1
+argument, it shall be applied to all
+.Cm \(pl Ns Ar pos1
+arguments.
+Otherwise, the
+.Fl b
+option can be
+attached independently to each
+.Cm \(pl Ns Ar pos1
+or
+.Fl Ar pos2
+argument (see below).
+.It Fl t Ar char
+.Ar Char
+is used as the field separator character;
+.Ar char
+is not considered to be part of a field (although it
+can be included in a sort key).
+Each occurrence of
+.Ar char
+is significant (for example,
+.Dq Ar charchar
+delimits an empty field).
+If
+.Fl t
+is not specified,
+blank space characters are used as default field
+separators.
+.It Cm \(pl Ns Ar pos1
+Designates the start position of a key field.
+.It Fl Ns Ar pos1
+Designates the end position of a key field.
+.El
+.Pp
+The following operands are available:
+.Bl -tag -width indent
+.Ar file
+The pathname of a file to be sorted, merged, or checked.
+If no file
+operands are specified, or if
+a file operand is
+.Fl ,
+the standard input is used.
+.Pp
+A field is
+defined as a minimal sequence of characters followed by a
+field separator or a newline character.
+By default, the first
+blank space of a sequence of blank spaces acts as the field separator.
+All blank spaces in a sequence of blank spaces are considered
+to be part of the next field; for example, all blank spaces at
+the beginning of a line are considered to be part of the
+first field.
+.Pp
+Fields are specified
+by the
+.Cm \(pl Ns Ar pos1
+and
+.Fl Ar pos2
+arguments. A missing
+.Cm \(pl Ns Ar pos1
+argument defaults to the beginning of a line.
+A missing
+.Fl Ar pos2
+argument defaults to the end of a line.
+.Pp
+The arguments
+.Cm \(pl Ns Ar pos1
+and
+.Fl Ar pos2
+have the form
+.Em m.n
+followed by one or more of the options
+.Fl b , d , f , i ,
+.Fl n , r .
+A
+.Cm \(pl Ns Ar pos1
+position specified by
+.Em m.n
+is interpreted to
+mean the
+.Em n Ns th
+character in the
+.Em m Ns \(pl1th
+field.
+A missing
+.Em \&.n
+means
+.Ql \&.0 ,
+indicating the first character of the
+.Em m Ns \(pl1th
+field.
+If the
+.Fl b
+option is in effect,
+.Em n
+is counted from the first
+non-blank character in the
+.Em m Ns \(pl1th
+field;
+.Em m Ns \&.0b
+refers to the first
+non-blank character in the
+.Em m Ns \(pl1th
+field.
+.Pp
+A
+.Fl Ar pos2
+position specified by
+.Em m.n
+is interpreted to mean
+the
+.Em n Ns th
+character (including separators) after the last
+character of the
+.Em m Ns th
+field.
+A missing
+.Em \&.n
+means
+.Ql \&.0 ,
+indicating
+the last character of the
+.Em m Ns th
+field.
+If the
+.Fl b
+option
+is in effect,
+.Em n
+is counted from the last leading blank character in
+the
+.Em m Ns \(pl1th
+field;
+.Em m Ns \&.1b
+refers to the first non-blank character in the
+.Em m Ns \(pl1th
+field.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/tmp/stm*, /tmp/*
+Default temporary directories (in order of search).
+.El
+.Sh SEE ALSO
+.Xr comm 1 ,
+.Xr uniq 1 ,
+.Xr join 1
+.Sh DIAGNOSTICS
+.Sh BUGS
+Lines which are longer than 4096 are discarded and processing continues.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/split/Makefile b/usr.bin/split/Makefile
new file mode 100644
index 0000000..93048f7
--- /dev/null
+++ b/usr.bin/split/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= split
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/split/split.1 b/usr.bin/split/split.1
new file mode 100644
index 0000000..34f3c07
--- /dev/null
+++ b/usr.bin/split/split.1
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)split.1 8.3 (Berkeley) 4/16/94
+.\"
+.Dd April 16, 1994
+.Dt SPLIT 1
+.Os
+.Sh NAME
+.Nm split
+.Nd split a file into pieces
+.Sh SYNOPSIS
+.Nm split
+.Op Fl b Ar byte_count[k|m]
+.Op Fl l Ar line_count
+.Op Ar file Op Ar name
+.Sh DESCRIPTION
+The
+.Nm split
+utility reads the given
+.Ar file
+(or standard input if no file is specified)
+and breaks it up into files of 1000 lines each.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Create smaller files
+.Ar byte_count
+bytes in length.
+If
+.Dq Li k
+is appended to the number, the file is split into
+.Ar byte_count
+kilobyte pieces.
+If
+.Dq Li m
+is appended to the number, the file is split into
+.Ar byte_count
+megabyte pieces.
+.It Fl l
+Create smaller files
+.Ar n
+lines in length.
+.El
+.Pp
+If additional arguments are specified, the first is used as the name
+of the input file which is to be split.
+If a second additional argument is specified, it is used as a prefix
+for the names of the files into which the file is split.
+In this case, each file into which the file is split is named by the
+prefix followed by a lexically ordered suffix in the range of
+.Dq Li aa-zz .
+.Pp
+If the
+.Ar name
+argument is not specified, the file is split into lexically ordered
+files named in the range of
+.Dq Li xaa-zzz .
+.Sh BUGS
+For historical reasons, if you specify
+.Ar name ,
+.Nm split
+can only create 676 separate
+files.
+The default naming convention allows 2028 separate files.
+.Sh HISTORY
+A
+.Nm split
+command appeared in
+.At v6 .
diff --git a/usr.bin/split/split.c b/usr.bin/split/split.c
new file mode 100644
index 0000000..efd2a21
--- /dev/null
+++ b/usr.bin/split/split.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)split.c 8.2 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DEFLINE 1000 /* Default num lines per file. */
+
+long bytecnt; /* Byte count to split on. */
+long numlines; /* Line count to split on. */
+int file_open; /* If a file open. */
+int ifd = -1, ofd = -1; /* Input/output file descriptors. */
+char bfr[MAXBSIZE]; /* I/O buffer. */
+char fname[MAXPATHLEN]; /* File name prefix. */
+
+void newfile __P((void));
+void split1 __P((void));
+void split2 __P((void));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *ep, *p;
+
+ while ((ch = getopt(argc, argv, "-0123456789b:l:")) != EOF)
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * Undocumented kludge: split was originally designed
+ * to take a number after a dash.
+ */
+ if (numlines == 0) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ numlines = strtol(++p, &ep, 10);
+ else
+ numlines =
+ strtol(argv[optind] + 1, &ep, 10);
+ if (numlines <= 0 || *ep)
+ errx(1,
+ "%s: illegal line count.", optarg);
+ }
+ break;
+ case '-': /* Undocumented: historic stdin flag. */
+ if (ifd != -1)
+ usage();
+ ifd = 0;
+ break;
+ case 'b': /* Byte count. */
+ if ((bytecnt = strtol(optarg, &ep, 10)) <= 0 ||
+ *ep != '\0' && *ep != 'k' && *ep != 'm')
+ errx(1, "%s: illegal byte count.", optarg);
+ if (*ep == 'k')
+ bytecnt *= 1024;
+ else if (*ep == 'm')
+ bytecnt *= 1048576;
+ break;
+ case 'l': /* Line count. */
+ if (numlines != 0)
+ usage();
+ if ((numlines = strtol(optarg, &ep, 10)) <= 0 || *ep)
+ errx(1, "%s: illegal line count.", optarg);
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (*argv != NULL)
+ if (ifd == -1) { /* Input file. */
+ if ((ifd = open(*argv, O_RDONLY, 0)) < 0)
+ err(1, "%s", *argv);
+ ++argv;
+ }
+ if (*argv != NULL) /* File name prefix. */
+ (void)strcpy(fname, *argv++);
+ if (*argv != NULL)
+ usage();
+
+ if (numlines == 0)
+ numlines = DEFLINE;
+ else if (bytecnt)
+ usage();
+
+ if (ifd == -1) /* Stdin by default. */
+ ifd = 0;
+
+ if (bytecnt) {
+ split1();
+ exit (0);
+ }
+ split2();
+ exit(0);
+}
+
+/*
+ * split1 --
+ * Split the input by bytes.
+ */
+void
+split1()
+{
+ long bcnt;
+ int dist, len;
+ char *C;
+
+ for (bcnt = 0;;)
+ switch (len = read(ifd, bfr, MAXBSIZE)) {
+ case 0:
+ exit(0);
+ case -1:
+ err(1, "read");
+ /* NOTREACHED */
+ default:
+ if (!file_open) {
+ newfile();
+ file_open = 1;
+ }
+ if (bcnt + len >= bytecnt) {
+ dist = bytecnt - bcnt;
+ if (write(ofd, bfr, dist) != dist)
+ err(1, "write");
+ len -= dist;
+ for (C = bfr + dist; len >= bytecnt;
+ len -= bytecnt, C += bytecnt) {
+ newfile();
+ if (write(ofd,
+ C, (int)bytecnt) != bytecnt)
+ err(1, "write");
+ }
+ if (len) {
+ newfile();
+ if (write(ofd, C, len) != len)
+ err(1, "write");
+ } else
+ file_open = 0;
+ bcnt = len;
+ } else {
+ bcnt += len;
+ if (write(ofd, bfr, len) != len)
+ err(1, "write");
+ }
+ }
+}
+
+/*
+ * split2 --
+ * Split the input by lines.
+ */
+void
+split2()
+{
+ long lcnt;
+ int len, bcnt;
+ char *Ce, *Cs;
+
+ for (lcnt = 0;;)
+ switch (len = read(ifd, bfr, MAXBSIZE)) {
+ case 0:
+ exit(0);
+ case -1:
+ err(1, "read");
+ /* NOTREACHED */
+ default:
+ if (!file_open) {
+ newfile();
+ file_open = 1;
+ }
+ for (Cs = Ce = bfr; len--; Ce++)
+ if (*Ce == '\n' && ++lcnt == numlines) {
+ bcnt = Ce - Cs + 1;
+ if (write(ofd, Cs, bcnt) != bcnt)
+ err(1, "write");
+ lcnt = 0;
+ Cs = Ce + 1;
+ if (len)
+ newfile();
+ else
+ file_open = 0;
+ }
+ if (Cs < Ce) {
+ bcnt = Ce - Cs;
+ if (write(ofd, Cs, bcnt) != bcnt)
+ err(1, "write");
+ }
+ }
+}
+
+/*
+ * newfile --
+ * Open a new output file.
+ */
+void
+newfile()
+{
+ static long fnum;
+ static int defname;
+ static char *fpnt;
+
+ if (ofd == -1) {
+ if (fname[0] == '\0') {
+ fname[0] = 'x';
+ fpnt = fname + 1;
+ defname = 1;
+ } else {
+ fpnt = fname + strlen(fname);
+ defname = 0;
+ }
+ ofd = fileno(stdout);
+ }
+ /*
+ * Hack to increase max files; original code wandered through
+ * magic characters. Maximum files is 3 * 26 * 26 == 2028
+ */
+#define MAXFILES 676
+ if (fnum == MAXFILES) {
+ if (!defname || fname[0] == 'z')
+ errx(1, "too many files.");
+ ++fname[0];
+ fnum = 0;
+ }
+ fpnt[0] = fnum / 26 + 'a';
+ fpnt[1] = fnum % 26 + 'a';
+ ++fnum;
+ if (!freopen(fname, "w", stdout))
+ err(1, "%s", fname);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: split [-b byte_count] [-l line_count] [file [prefix]]\n");
+ exit(1);
+}
diff --git a/usr.bin/strings/Makefile b/usr.bin/strings/Makefile
new file mode 100644
index 0000000..87f557d
--- /dev/null
+++ b/usr.bin/strings/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= strings
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/strings/strings.1 b/usr.bin/strings/strings.1
new file mode 100644
index 0000000..ceeae10
--- /dev/null
+++ b/usr.bin/strings/strings.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)strings.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt STRINGS 1
+.Os BSD 3
+.Sh NAME
+.Nm strings
+.Nd find printable strings in a file
+.Sh SYNOPSIS
+.Nm strings
+.Op Fl afo
+.Op Fl n Ar number
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Strings
+displays the sequences of printable characters in each of the specified
+files, or in the standard input, by default.
+By default, a sequence must be at least four characters in length
+before being displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+By default,
+.Nm strings
+only searches the text and data segments of object files.
+The
+.Fl a
+option causes
+.Nm strings
+to search the entire object file.
+.It Fl f
+Each string is preceded by the name of the file
+in which it was found.
+.It Fl n
+Specifies the minimum number of characters in a sequence to be
+.Ar number ,
+instead of four.
+.It Fl o
+Each string is preceded by its decimal offset in the
+file.
+.El
+.Pp
+.Nm Strings
+is useful for identifying random binaries, among other things.
+.Sh SEE ALSO
+.Xr hexdump 1
+.Sh BUGS
+The algorithm for identifying strings is extremely primitive.
+In particular, machine code instructions on certain architectures
+can resemble sequences of ASCII bytes, which
+will fool the algorithm.
+.Sh COMPATIBILITY
+Historic implementations of
+.Nm
+only search the initialized data portion of the object file.
+This was reasonable as strings were normally stored there.
+Given new compiler technology which installs strings in the
+text portion of the object file, the default behavior was
+changed.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/strings/strings.1aout b/usr.bin/strings/strings.1aout
new file mode 100644
index 0000000..ceeae10
--- /dev/null
+++ b/usr.bin/strings/strings.1aout
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)strings.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt STRINGS 1
+.Os BSD 3
+.Sh NAME
+.Nm strings
+.Nd find printable strings in a file
+.Sh SYNOPSIS
+.Nm strings
+.Op Fl afo
+.Op Fl n Ar number
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Strings
+displays the sequences of printable characters in each of the specified
+files, or in the standard input, by default.
+By default, a sequence must be at least four characters in length
+before being displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+By default,
+.Nm strings
+only searches the text and data segments of object files.
+The
+.Fl a
+option causes
+.Nm strings
+to search the entire object file.
+.It Fl f
+Each string is preceded by the name of the file
+in which it was found.
+.It Fl n
+Specifies the minimum number of characters in a sequence to be
+.Ar number ,
+instead of four.
+.It Fl o
+Each string is preceded by its decimal offset in the
+file.
+.El
+.Pp
+.Nm Strings
+is useful for identifying random binaries, among other things.
+.Sh SEE ALSO
+.Xr hexdump 1
+.Sh BUGS
+The algorithm for identifying strings is extremely primitive.
+In particular, machine code instructions on certain architectures
+can resemble sequences of ASCII bytes, which
+will fool the algorithm.
+.Sh COMPATIBILITY
+Historic implementations of
+.Nm
+only search the initialized data portion of the object file.
+This was reasonable as strings were normally stored there.
+Given new compiler technology which installs strings in the
+text portion of the object file, the default behavior was
+changed.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/strings/strings.c b/usr.bin/strings/strings.c
new file mode 100644
index 0000000..435e134
--- /dev/null
+++ b/usr.bin/strings/strings.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1980, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)strings.c 8.2 (Berkeley) 1/28/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <a.out.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define DEF_LEN 4 /* default minimum string length */
+#define ISSTR(ch) (isascii(ch) && (isprint(ch) || ch == '\t'))
+
+typedef struct exec EXEC; /* struct exec cast */
+
+static long foff; /* offset in the file */
+static int hcnt, /* head count */
+ head_len, /* length of header */
+ read_len; /* length to read */
+static u_char hbfr[sizeof(EXEC)]; /* buffer for struct exec */
+
+static void usage();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register int ch, cnt;
+ register u_char *C;
+ EXEC *head;
+ int exitcode, minlen;
+ short asdata, oflg, fflg;
+ u_char *bfr;
+ char *file, *p;
+
+ /*
+ * for backward compatibility, allow '-' to specify 'a' flag; no
+ * longer documented in the man page or usage string.
+ */
+ asdata = exitcode = fflg = oflg = 0;
+ minlen = -1;
+ while ((ch = getopt(argc, argv, "-0123456789an:of")) != EOF)
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ /*
+ * kludge: strings was originally designed to take
+ * a number after a dash.
+ */
+ if (minlen == -1) {
+ p = argv[optind - 1];
+ if (p[0] == '-' && p[1] == ch && !p[2])
+ minlen = atoi(++p);
+ else
+ minlen = atoi(argv[optind] + 1);
+ }
+ break;
+ case '-':
+ case 'a':
+ asdata = 1;
+ break;
+ case 'f':
+ fflg = 1;
+ break;
+ case 'n':
+ minlen = atoi(optarg);
+ break;
+ case 'o':
+ oflg = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (minlen == -1)
+ minlen = DEF_LEN;
+ else if (minlen < 1) {
+ (void)fprintf(stderr, "strings: length less than 1\n");
+ exit (1);
+ }
+
+ if (!(bfr = malloc((u_int)minlen))) {
+ (void)fprintf(stderr, "strings: %s\n", strerror(errno));
+ exit(1);
+ }
+ bfr[minlen] = '\0';
+ file = "stdin";
+ do {
+ if (*argv) {
+ file = *argv++;
+ if (!freopen(file, "r", stdin)) {
+ (void)fprintf(stderr,
+ "strings: %s: %s\n", file, strerror(errno));
+ exitcode = 1;
+ goto nextfile;
+ }
+ }
+ foff = 0;
+#define DO_EVERYTHING() {read_len = -1; head_len = 0; goto start;}
+ read_len = -1;
+ if (asdata)
+ DO_EVERYTHING()
+ else {
+ head = (EXEC *)hbfr;
+ if ((head_len =
+ read(fileno(stdin), head, sizeof(EXEC))) == -1)
+ DO_EVERYTHING()
+ if (head_len == sizeof(EXEC) && !N_BADMAG(*head)) {
+ foff = N_TXTOFF(*head);
+ if (fseek(stdin, foff, SEEK_SET) == -1)
+ DO_EVERYTHING()
+ read_len = head->a_text + head->a_data;
+ head_len = 0;
+ }
+ else
+ hcnt = 0;
+ }
+start:
+ for (cnt = 0; (ch = getch()) != EOF;) {
+ if (ISSTR(ch)) {
+ if (!cnt)
+ C = bfr;
+ *C++ = ch;
+ if (++cnt < minlen)
+ continue;
+ if (fflg)
+ printf("%s:", file);
+ if (oflg)
+ printf("%07ld %s",
+ foff - minlen, (char *)bfr);
+ else
+ printf("%s", bfr);
+ while ((ch = getch()) != EOF && ISSTR(ch))
+ putchar((char)ch);
+ putchar('\n');
+ }
+ cnt = 0;
+ }
+nextfile: ;
+ } while (*argv);
+ exit(exitcode);
+}
+
+/*
+ * getch --
+ * get next character from wherever
+ */
+getch()
+{
+ ++foff;
+ if (head_len) {
+ if (hcnt < head_len)
+ return((int)hbfr[hcnt++]);
+ head_len = 0;
+ }
+ if (read_len == -1 || read_len-- > 0)
+ return(getchar());
+ return(EOF);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: strings [-afo] [-n length] [file ... ]\n");
+ exit(1);
+}
diff --git a/usr.bin/strip/Makefile b/usr.bin/strip/Makefile
new file mode 100644
index 0000000..b9bfe09
--- /dev/null
+++ b/usr.bin/strip/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= strip
+MAN1= strip.1
+CLEANFILES += maybe_stripped
+
+all: maybe_stripped
+
+maybe_stripped: strip
+ cp -p strip maybe_stripped
+.if ${STRIP:M-s} != ""
+ ./strip maybe_stripped
+.endif
+
+install: maninstall
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ maybe_stripped ${DESTDIR}${BINDIR}/strip
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/strip/strip.1 b/usr.bin/strip/strip.1
new file mode 100644
index 0000000..f7af486
--- /dev/null
+++ b/usr.bin/strip/strip.1
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)strip.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt STRIP 1
+.Os
+.Sh NAME
+.Nm strip
+.Nd remove unnecessary information from executable files
+.Sh SYNOPSIS
+.Nm strip
+.Op Fl d
+.Op Fl x
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm strip
+utility
+deletes the relocation information and symbol table used by
+assemblers, loaders and debuggers.
+This significantly
+decreases the size of the installed binaries and saves disk space.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Delete only debugging and empty symbols.
+.It Fl x
+Delete only debugging, compiler identification, and local symbols.
+.El
+.Pp
+.Nm Strip
+exits 0 on success and 1 if an error occurred.
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr ld 1 ,
+.Xr stab 5
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/strip/strip.1aout b/usr.bin/strip/strip.1aout
new file mode 100644
index 0000000..f7af486
--- /dev/null
+++ b/usr.bin/strip/strip.1aout
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)strip.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt STRIP 1
+.Os
+.Sh NAME
+.Nm strip
+.Nd remove unnecessary information from executable files
+.Sh SYNOPSIS
+.Nm strip
+.Op Fl d
+.Op Fl x
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm strip
+utility
+deletes the relocation information and symbol table used by
+assemblers, loaders and debuggers.
+This significantly
+decreases the size of the installed binaries and saves disk space.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Delete only debugging and empty symbols.
+.It Fl x
+Delete only debugging, compiler identification, and local symbols.
+.El
+.Pp
+.Nm Strip
+exits 0 on success and 1 if an error occurred.
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr ld 1 ,
+.Xr stab 5
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/strip/strip.c b/usr.bin/strip/strip.c
new file mode 100644
index 0000000..42cc96e
--- /dev/null
+++ b/usr.bin/strip/strip.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "@(#)strip.c 8.1 (Berkeley) 6/6/93";*/
+static char RCSid[] = "$Id: strip.c,v 1.4 1994/12/18 01:18:17 ache Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <a.out.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct exec EXEC;
+typedef struct nlist NLIST;
+
+#define strx n_un.n_strx
+
+void err __P((int, const char *fmt, ...));
+void s_stab __P((const char *, int, EXEC *));
+void s_sym __P((const char *, int, EXEC *));
+void usage __P((void));
+
+int xflag = 0;
+int err_val = 0;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int fd, nb;
+ EXEC head;
+ void (*sfcn)__P((const char *, int, EXEC *));
+ int ch;
+ char *fn;
+
+ sfcn = s_sym;
+ while ((ch = getopt(argc, argv, "dx")) != EOF)
+ switch(ch) {
+ case 'x':
+ xflag = 1;
+ /*FALLTHROUGH*/
+ case 'd':
+ sfcn = s_stab;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ while ((fn = *argv++) != NULL) {
+ if ((fd = open(fn, O_RDWR)) < 0 ||
+ (nb = read(fd, &head, sizeof(EXEC))) == -1) {
+ err(0, "%s: %s", fn, strerror(errno));
+ if (fd >= 0 && close(fd))
+ err(0, "%s: %s", fn, strerror(errno));
+ continue;
+ }
+ if (nb != sizeof(EXEC) || N_BADMAG(head)) {
+ err(0, "%s: %s", fn, strerror(EFTYPE));
+ if (close(fd))
+ err(0, "%s: %s", fn, strerror(errno));
+ continue;
+ }
+ sfcn(fn, fd, &head);
+ if (close(fd))
+ err(0, "%s: %s", fn, strerror(errno));
+ }
+ exit(err_val);
+}
+
+void
+s_sym(fn, fd, ep)
+ const char *fn;
+ int fd;
+ register EXEC *ep;
+{
+ register off_t fsize;
+
+ /* If no symbols or data/text relocation info, quit. */
+ if (!ep->a_syms && !ep->a_trsize && !ep->a_drsize)
+ return;
+
+ /*
+ * New file size is the header plus text and data segments.
+ */
+ fsize = N_DATOFF(*ep) + ep->a_data;
+
+ /* Set symbol size and relocation info values to 0. */
+ ep->a_syms = ep->a_trsize = ep->a_drsize = 0;
+
+ /* Rewrite the header and truncate the file. */
+ if (lseek(fd, (off_t)0, SEEK_SET) == -1 ||
+ write(fd, ep, sizeof(EXEC)) != sizeof(EXEC) ||
+ ftruncate(fd, fsize))
+ err(0, "%s: %s", fn, strerror(errno));
+}
+
+void
+s_stab(fn, fd, ep)
+ const char *fn;
+ int fd;
+ EXEC *ep;
+{
+ register int cnt, len;
+ register char *nstr, *nstrbase, *p, *strbase;
+ register NLIST *sym, *nsym;
+ struct stat sb;
+ NLIST *symbase;
+
+ /* Quit if no symbols. */
+ if (ep->a_syms == 0)
+ return;
+
+ /* Stat the file. */
+ if (fstat(fd, &sb) < 0) {
+ err(0, "%s: %s", fn, strerror(errno));
+ return;
+ }
+
+ /* Check size. */
+ if (sb.st_size > SIZE_T_MAX) {
+ err(0, "%s: %s", fn, strerror(EFBIG));
+ return;
+ }
+
+ /* Map the file. */
+ if ((ep = (EXEC *)mmap(NULL, (size_t)sb.st_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0)) == (EXEC *)-1) {
+ err(0, "%s: %s", fn, strerror(errno));
+ return;
+ }
+
+ /*
+ * Initialize old and new symbol pointers. They both point to the
+ * beginning of the symbol table in memory, since we're deleting
+ * entries.
+ */
+ sym = nsym = symbase = (NLIST *)((char *)ep + N_SYMOFF(*ep));
+
+ /*
+ * Allocate space for the new string table, initialize old and
+ * new string pointers. Handle the extra long at the beginning
+ * of the string table.
+ */
+ strbase = (char *)ep + N_STROFF(*ep);
+ if ((nstrbase = malloc((size_t)*(u_long *)strbase)) == NULL) {
+ err(0, "%s", strerror(errno));
+ munmap((caddr_t)ep, sb.st_size);
+ return;
+ }
+ nstr = nstrbase + sizeof(u_long);
+
+ /*
+ * Read through the symbol table. For each non-debugging symbol,
+ * copy it and save its string in the new string table. Keep
+ * track of the number of symbols.
+ */
+ for (cnt = ep->a_syms / sizeof(NLIST); cnt--; ++sym)
+ if (!(sym->n_type & N_STAB) && sym->strx) {
+ *nsym = *sym;
+ nsym->strx = nstr - nstrbase;
+ p = strbase + sym->strx;
+ if (xflag &&
+ (!(sym->n_type & N_EXT) ||
+ (sym->n_type & ~N_EXT) == N_FN ||
+ strcmp(p, "gcc_compiled.") == 0 ||
+ strcmp(p, "gcc2_compiled.") == 0 ||
+ strcmp(p, "___gnu_compiled_c") == 0))
+ continue;
+ len = strlen(p) + 1;
+ bcopy(p, nstr, len);
+ nstr += len;
+ ++nsym;
+ }
+
+ /* Fill in new symbol table size. */
+ ep->a_syms = (nsym - symbase) * sizeof(NLIST);
+
+ /* Fill in the new size of the string table. */
+ *(u_long *)nstrbase = len = nstr - nstrbase;
+
+ /*
+ * Copy the new string table into place. Nsym should be pointing
+ * at the address past the last symbol entry.
+ */
+ bcopy(nstrbase, (void *)nsym, len);
+
+ /* Truncate to the current length. */
+ if (ftruncate(fd, (char *)nsym + len - (char *)ep))
+ err(0, "%s: %s", fn, strerror(errno));
+ munmap((caddr_t)ep, (size_t)sb.st_size);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: strip [-d] file ...\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "strip: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+ err_val = 1;
+}
diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile
new file mode 100644
index 0000000..0303a5e
--- /dev/null
+++ b/usr.bin/su/Makefile
@@ -0,0 +1,26 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= su
+SRCS= su.c
+
+COPTS+= -DSKEY
+.if defined(WHEELSU)
+COPTS+= -DWHEELSU
+.endif
+
+LDADD= -lskey -lmd -lcrypt
+DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/su/su.1 b/usr.bin/su/su.1
new file mode 100644
index 0000000..d4abfd0
--- /dev/null
+++ b/usr.bin/su/su.1
@@ -0,0 +1,199 @@
+.\" Copyright (c) 1988, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)su.1 8.2 (Berkeley) 4/18/94
+.\"
+.\" this is for hilit19's braindeadness: "
+.Dd April 18, 1994
+.Dt SU 1
+.Os
+.Sh NAME
+.Nm su
+.Nd substitute user identity
+.Sh SYNOPSIS
+.Nm su
+.Op Fl Kflm
+.Op Ar login Op Ar args
+.Sh DESCRIPTION
+.Nm Su
+requests the Kerberos password for
+.Ar login
+(or for
+.Dq Ar login Ns .root ,
+if no login is provided), and switches to
+that user and group ID after obtaining a Kerberos ticket granting ticket.
+A shell is then executed.
+.Nm Su
+will resort to the local password file to find the password for
+.Ar login
+if there is a Kerberos error.
+If
+.Nm su
+is executed by root, no password is requested and a shell
+with the appropriate user ID is executed; no additional Kerberos tickets
+are obtained.
+.Pp
+By default, the environment is unmodified with the exception of
+.Ev USER ,
+.Ev HOME ,
+and
+.Ev SHELL .
+.Ev HOME
+and
+.Ev SHELL
+are set to the target login's default values.
+.Ev USER
+is set to the target login, unless the target login has a user ID of 0,
+in which case it is unmodified.
+The invoked shell is the target login's.
+This is the traditional behavior of
+.Nm su .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl K
+Do not attempt to use Kerberos to authenticate the user.
+.It Fl f
+If the invoked shell is
+.Xr csh 1 ,
+this option prevents it from reading the
+.Dq Pa .cshrc
+file.
+.It Fl l
+Simulate a full login.
+The environment is discarded except for
+.Ev HOME ,
+.Ev SHELL ,
+.Ev PATH ,
+.Ev TERM ,
+and
+.Ev USER .
+.Ev HOME
+and
+.Ev SHELL
+are modified as above.
+.Ev USER
+is set to the target login.
+.Ev PATH
+is set to
+.Dq Pa /bin:/usr/bin .
+.Ev TERM
+is imported from your current environment.
+The invoked shell is the target login's, and
+.Nm su
+will change directory to the target login's home directory.
+.It Fl m
+Leave the environment unmodified.
+The invoked shell is your login shell, and no directory changes are made.
+As a security precaution, if the target user's shell is a non-standard
+shell (as defined by
+.Xr getusershell 3 )
+and the caller's real uid is
+non-zero,
+.Nm su
+will fail.
+.El
+.Pp
+The
+.Fl l
+and
+.Fl m
+options are mutually exclusive; the last one specified
+overrides any previous ones.
+.Pp
+If the optional
+.Ar args
+are provided on the command line, they are passed to the login shell of
+the target login. This allows it to pass arbitrary commands via
+the
+.Fl c
+option as understood by most shells. Note that
+.Fl c
+usually expects a single argument only; you have to quote it when
+passing multiple words.
+.Pp
+Only users in group 0 (normally
+.Dq wheel )
+can
+.Nm su
+to
+.Dq root .
+.Pp
+By default (unless the prompt is reset by a startup file) the super-user
+prompt is set to
+.Dq Sy \&#
+to remind one of its awesome power.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr login 1 ,
+.Xr sh 1 ,
+.Xr kinit 1 ,
+.Xr kerberos 1 ,
+.Xr passwd 5 ,
+.Xr group 5 ,
+.Xr environ 7
+.Sh ENVIRONMENT
+Environment variables used by
+.Nm su :
+.Bl -tag -width HOME
+.It Ev HOME
+Default home directory of real user ID unless modified as
+specified above.
+.It Ev PATH
+Default search path of real user ID unless modified as specified above.
+.It Ev TERM
+Provides terminal type which may be retained for the substituted
+user ID.
+.It Ev USER
+The user ID is always the effective ID (the target user ID) after an
+.Nm su
+unless the user ID is 0 (root).
+.El
+.Sh EXAMPLES
+.Bl -tag -width 5n -compact
+.It Li "su man -c catman"
+Runs the command
+.Li catman
+as user
+.Li man .
+You will be asked for man's password unless your real UID is 0.
+.It Li "su man -c 'catman /usr/share/man /usr/local/man /usr/X11R6/man'"
+Same as above, but the target command constitutes of more than a
+single word.
+.It Li "su -l foo"
+Pretend a login for user
+.Li foo .
+.El
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c
new file mode 100644
index 0000000..521d88a
--- /dev/null
+++ b/usr.bin/su/su.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef SKEY
+#include <skey.h>
+#endif
+
+#ifdef KERBEROS
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#include <netdb.h>
+
+#define ARGSTR "-Kflm"
+
+int use_kerberos = 1;
+#else
+#define ARGSTR "-flm"
+#endif
+
+char *ontty __P((void));
+int chshell __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char **environ;
+ struct passwd *pwd;
+#ifdef WHEELSU
+ char *targetpass;
+ int iswheelsu;
+#endif /* WHEELSU */
+ char *p, **g, *user, *shell, *username, *cleanenv[20], **nargv, **np;
+ struct group *gr;
+ uid_t ruid;
+ int asme, ch, asthem, fastlogin, prio, i;
+ enum { UNSET, YES, NO } iscsh = UNSET;
+ char shellbuf[MAXPATHLEN];
+
+#ifdef WHEELSU
+ iswheelsu =
+#endif /* WHEELSU */
+ asme = asthem = fastlogin = 0;
+ user = "root";
+ while(optind < argc)
+ if((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ switch((char)ch) {
+#ifdef KERBEROS
+ case 'K':
+ use_kerberos = 0;
+ break;
+#endif
+ case 'f':
+ fastlogin = 1;
+ break;
+ case '-':
+ case 'l':
+ asme = 0;
+ asthem = 1;
+ break;
+ case 'm':
+ asme = 1;
+ asthem = 0;
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr, "usage: su [%s] [login]\n",
+ ARGSTR);
+ exit(1);
+ }
+ else
+ {
+ user = argv[optind++];
+ break;
+ }
+
+ if((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) {
+ errx(1, "malloc failure");
+ }
+
+ nargv[argc + 3] = NULL;
+ for (i = argc; i >= optind; i--)
+ nargv[i + 3] = argv[i];
+ np = &nargv[i + 3];
+
+ argv += optind;
+
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (errno)
+ prio = 0;
+ (void)setpriority(PRIO_PROCESS, 0, -2);
+ openlog("su", LOG_CONS, 0);
+
+ /* get current login name and shell */
+ ruid = getuid();
+ username = getlogin();
+ if (username == NULL || (pwd = getpwnam(username)) == NULL ||
+ pwd->pw_uid != ruid)
+ pwd = getpwuid(ruid);
+ if (pwd == NULL)
+ errx(1, "who are you?");
+ username = strdup(pwd->pw_name);
+ if (username == NULL)
+ err(1, NULL);
+ if (asme)
+ if (pwd->pw_shell && *pwd->pw_shell)
+ shell = strcpy(shellbuf, pwd->pw_shell);
+ else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+
+ /* get target login information, default to root */
+ if ((pwd = getpwnam(user)) == NULL) {
+ errx(1, "unknown login: %s", user);
+ }
+
+#ifdef WHEELSU
+ targetpass = strdup(pwd->pw_passwd);
+#endif /* WHEELSU */
+
+ if (ruid) {
+#ifdef KERBEROS
+ if (!use_kerberos || kerberos(username, user, pwd->pw_uid))
+#endif
+ {
+ /* only allow those in group zero to su to root. */
+ if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
+ for (g = gr->gr_mem;; ++g) {
+ if (!*g)
+ errx(1,
+ "you are not in the correct group to su %s.",
+ user);
+ if (strcmp(username, *g) == 0) {
+#ifdef WHEELSU
+ iswheelsu = 1;
+#endif /* WHEELSU */
+ break;
+ }
+ }
+ /* if target requires a password, verify it */
+ if (*pwd->pw_passwd) {
+#ifdef SKEY
+#ifdef WHEELSU
+ if (iswheelsu) {
+ pwd = getpwnam(username);
+ }
+#endif /* WHEELSU */
+ p = skey_getpass("Password:", pwd, 1);
+ if (!(!strcmp(pwd->pw_passwd,
+ skey_crypt(p, pwd->pw_passwd, pwd, 1))
+#ifdef WHEELSU
+ || (iswheelsu && !strcmp(targetpass,
+ crypt(p,
+ targetpass)))
+#endif /* WHEELSU */
+ )) {
+#else
+ p = getpass("Password:");
+ if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
+#endif
+ fprintf(stderr, "Sorry\n");
+ syslog(LOG_AUTH|LOG_WARNING,
+ "BAD SU %s to %s%s", username,
+ user, ontty());
+ exit(1);
+ }
+#ifdef WHEELSU
+ if (iswheelsu) {
+ pwd = getpwnam(user);
+ }
+#endif /* WHEELSU */
+ }
+ if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
+ fprintf(stderr, "Sorry - account expired\n");
+ syslog(LOG_AUTH|LOG_WARNING,
+ "BAD SU %s to %s%s", username,
+ user, ontty());
+ exit(1);
+ }
+ }
+ }
+
+ if (asme) {
+ /* if asme and non-standard target shell, must be root */
+ if (!chshell(pwd->pw_shell) && ruid)
+ errx(1, "permission denied (shell).");
+ } else if (pwd->pw_shell && *pwd->pw_shell) {
+ shell = pwd->pw_shell;
+ iscsh = UNSET;
+ } else {
+ shell = _PATH_BSHELL;
+ iscsh = NO;
+ }
+
+ /* if we're forking a csh, we want to slightly muck the args */
+ if (iscsh == UNSET) {
+ if (p = strrchr(shell, '/'))
+ ++p;
+ else
+ p = shell;
+ if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO)
+ iscsh = strcmp(p, "tcsh") ? NO : YES;
+ }
+
+ /* set permissions */
+ if (setgid(pwd->pw_gid) < 0)
+ err(1, "setgid");
+ if (initgroups(user, pwd->pw_gid))
+ errx(1, "initgroups failed");
+ if (setuid(pwd->pw_uid) < 0)
+ err(1, "setuid");
+
+ if (!asme) {
+ if (asthem) {
+ p = getenv("TERM");
+ cleanenv[0] = NULL;
+ environ = cleanenv;
+ (void)setenv("PATH", _PATH_DEFPATH, 1);
+ (void)setenv("TERM", p, 1);
+ if (chdir(pwd->pw_dir) < 0)
+ errx(1, "no directory");
+ }
+ if (asthem || pwd->pw_uid)
+ (void)setenv("USER", pwd->pw_name, 1);
+ (void)setenv("HOME", pwd->pw_dir, 1);
+ (void)setenv("SHELL", shell, 1);
+ }
+
+ if (iscsh == YES) {
+ if (fastlogin)
+ *np-- = "-f";
+ if (asme)
+ *np-- = "-m";
+ }
+
+ /* csh strips the first character... */
+ *np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
+
+ if (ruid != 0)
+ syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
+ username, user, ontty());
+
+ (void)setpriority(PRIO_PROCESS, 0, prio);
+
+ execv(shell, np);
+ err(1, "%s", shell);
+}
+
+int
+chshell(sh)
+ char *sh;
+{
+ char *cp;
+
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, sh) == 0)
+ return (1);
+ return (0);
+}
+
+char *
+ontty()
+{
+ char *p;
+ static char buf[MAXPATHLEN + 4];
+
+ buf[0] = 0;
+ if (p = ttyname(STDERR_FILENO))
+ snprintf(buf, sizeof(buf), " on %s", p);
+ return (buf);
+}
+
+#ifdef KERBEROS
+kerberos(username, user, uid)
+ char *username, *user;
+ int uid;
+{
+ extern char *krb_err_txt[];
+ KTEXT_ST ticket;
+ AUTH_DAT authdata;
+ struct hostent *hp;
+ char *p;
+ int kerno;
+ u_long faddr;
+ struct sockaddr_in local_addr;
+ char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN];
+ char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN];
+ char *krb_get_phost();
+
+ if (krb_get_lrealm(lrealm, 1) != KSUCCESS)
+ return (1);
+ if (koktologin(username, lrealm, user) && !uid) {
+ warnx("kerberos: not in %s's ACL.", user);
+ return (1);
+ }
+ (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid());
+
+ (void)setenv("KRBTKFILE", krbtkfile, 1);
+ (void)krb_set_tkt_string(krbtkfile);
+ /*
+ * Set real as well as effective ID to 0 for the moment,
+ * to make the kerberos library do the right thing.
+ */
+ if (setuid(0) < 0) {
+ warn("setuid");
+ return (1);
+ }
+
+ /*
+ * Little trick here -- if we are su'ing to root,
+ * we need to get a ticket for "xxx.root", where xxx represents
+ * the name of the person su'ing. Otherwise (non-root case),
+ * we need to get a ticket for "yyy.", where yyy represents
+ * the name of the person being su'd to, and the instance is null
+ *
+ * We should have a way to set the ticket lifetime,
+ * with a system default for root.
+ */
+ kerno = krb_get_pw_in_tkt((uid == 0 ? username : user),
+ (uid == 0 ? "root" : ""), lrealm,
+ "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0);
+
+ if (kerno != KSUCCESS) {
+ if (kerno == KDC_PR_UNKNOWN) {
+ warnx("kerberos: principal unknown: %s.%s@%s",
+ (uid == 0 ? username : user),
+ (uid == 0 ? "root" : ""), lrealm);
+ return (1);
+ }
+ warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "BAD Kerberos SU: %s to %s%s: %s",
+ username, user, ontty(), krb_err_txt[kerno]);
+ return (1);
+ }
+
+ if (chown(krbtkfile, uid, -1) < 0) {
+ warn("chown");
+ (void)unlink(krbtkfile);
+ return (1);
+ }
+
+ (void)setpriority(PRIO_PROCESS, 0, -2);
+
+ if (gethostname(hostname, sizeof(hostname)) == -1) {
+ warn("gethostname");
+ dest_tkt();
+ return (1);
+ }
+
+ (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost));
+ savehost[sizeof(savehost) - 1] = '\0';
+
+ kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33);
+
+ if (kerno == KDC_PR_UNKNOWN) {
+ warnx("Warning: TGT not verified.");
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
+ username, user, ontty(), krb_err_txt[kerno],
+ "rcmd", savehost);
+ } else if (kerno != KSUCCESS) {
+ warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
+ syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
+ username, user, ontty(), krb_err_txt[kerno]);
+ dest_tkt();
+ return (1);
+ } else {
+ if ((kerno = krb_get_local_addr(&local_addr)) != KSUCCESS) {
+ warnx("Unable to get our local address: %s",
+ krb_err_txt[kerno]);
+ dest_tkt();
+ return (1);
+ }
+ faddr = local_addr.sin_addr.s_addr;
+ if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
+ &authdata, "")) != KSUCCESS) {
+ warnx("kerberos: unable to verify rcmd ticket: %s\n",
+ krb_err_txt[kerno]);
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "failed su: %s to %s%s: %s", username,
+ user, ontty(), krb_err_txt[kerno]);
+ dest_tkt();
+ return (1);
+ }
+ }
+ return (0);
+}
+
+koktologin(name, realm, toname)
+ char *name, *realm, *toname;
+{
+ AUTH_DAT *kdata;
+ AUTH_DAT kdata_st;
+
+ kdata = &kdata_st;
+ memset((char *)kdata, 0, sizeof(*kdata));
+ (void)strcpy(kdata->pname, name);
+ (void)strcpy(kdata->pinst,
+ ((strcmp(toname, "root") == 0) ? "root" : ""));
+ (void)strcpy(kdata->prealm, realm);
+ return (kuserok(kdata, toname));
+}
+#endif
diff --git a/usr.bin/symorder/Makefile b/usr.bin/symorder/Makefile
new file mode 100644
index 0000000..5fe1caf
--- /dev/null
+++ b/usr.bin/symorder/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 5.3 (Berkeley) 5/11/90
+
+PROG= symorder
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/symorder/symorder.1 b/usr.bin/symorder/symorder.1
new file mode 100644
index 0000000..2616ab3
--- /dev/null
+++ b/usr.bin/symorder/symorder.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)symorder.1 6.5 (Berkeley) 4/22/91
+.\"
+.Dd April 22, 1991
+.Dt SYMORDER 1
+.Os BSD 3
+.Sh NAME
+.Nm symorder
+.Nd rearrange name list
+.Sh SYNOPSIS
+.Nm symorder
+.Fl c
+.Fl m
+.Fl t
+.Fl x
+.Ar excludelist
+.Ar symlist file
+.Sh DESCRIPTION
+The file
+.Ar symlist
+contains a list of symbols to be found in
+.Ar file,
+one symbol per line.
+.Pp
+The symbol table of
+.Ar file
+is updated in place;
+symbols read from
+.Ar symlist
+are relocated to the beginning of the table and in the order given.
+.Bl -tag -width flag
+.It Fl c
+Makes all any symbols not in
+.Ar symlist
+local to this file.
+.It Fl t
+Restrict the symbol table to the symbols listed in
+.Ar symlist .
+.It Fl x Ar excludelist
+Exclude the symbols listed in
+.Ar excludelist
+from the symbol table.
+.It Fl m
+Exit with status zero, even if some symbols are missing.
+.El
+.Pp
+This program was specifically designed to cut down on the
+overhead of getting symbols from
+.Pa /kernel.
+.Sh DIAGNOSTICS
+The
+.Nm symorder
+utility exits 0 on success, 1 if a symbol
+listed in the
+.Ar symlist
+file was not found in the symbol
+table, and >1 if an error occurs.
+.Sh SEE ALSO
+.Xr nm 3 ,
+.Xr nlist 3 ,
+.Xr strip 3
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/symorder/symorder.c b/usr.bin/symorder/symorder.c
new file mode 100644
index 0000000..8ccb113
--- /dev/null
+++ b/usr.bin/symorder/symorder.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)symorder.c 5.8 (Berkeley) 4/1/91";
+#endif /* not lint */
+
+/*
+ * symorder - reorder symbol table
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <a.out.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SPACE 500
+
+#define OKEXIT 0
+#define NOTFOUNDEXIT 1
+#define ERREXIT 2
+
+char *exclude[SPACE];
+struct nlist order[SPACE];
+
+struct exec exec;
+struct stat stb;
+struct nlist *newtab, *symtab;
+off_t sa;
+int nexclude, nsym, strtabsize, symfound, symkept, small, missing, clean;
+char *kfile, *newstrings, *strings, asym[BUFSIZ];
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register struct nlist *p, *symp;
+ register FILE *f, *xfile;
+ register int i;
+ register char *start, *t, *xfilename;
+ int ch, n, o;
+
+ xfilename = NULL;
+ while ((ch = getopt(argc, argv, "cmtx:")) != EOF)
+ switch(ch) {
+ case 'c':
+ clean = 1;
+ break;
+ case 'm':
+ missing = 1;
+ break;
+ case 't':
+ small = 1;
+ break;
+ case 'x':
+ if (xfilename != NULL)
+ usage();
+ xfilename = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ if ((f = fopen(argv[0], "r")) == NULL)
+ error(argv[0]);
+
+ for (p = order; fgets(asym, sizeof(asym), f) != NULL;) {
+ for (t = asym; isspace(*t); ++t);
+ if (!*(start = t))
+ continue;
+ while (*++t);
+ if (*--t == '\n')
+ *t = '\0';
+ p->n_un.n_name = strdup(start);
+ ++p;
+ if (++nsym >= sizeof order / sizeof order[0])
+ break;
+ }
+ (void)fclose(f);
+
+ if (xfilename != NULL) {
+ if ((xfile = fopen(xfilename, "r")) == NULL)
+ error(xfilename);
+ for (; fgets(asym, sizeof(asym), xfile) != NULL;) {
+ for (t = asym; isspace(*t); ++t);
+ if (!*(start = t))
+ continue;
+ while (*++t);
+ if (*--t == '\n')
+ *t = '\0';
+ exclude[nexclude] = strdup(start);
+ if (++nexclude >= sizeof exclude / sizeof exclude[0])
+ break;
+ }
+ (void)fclose(xfile);
+ }
+
+ kfile = argv[1];
+ if ((f = fopen(kfile, "r")) == NULL)
+ error(kfile);
+ if ((o = open(kfile, O_WRONLY)) < 0)
+ error(kfile);
+
+ /* read exec header */
+ if ((fread(&exec, sizeof(exec), 1, f)) != 1)
+ badfmt("no exec header");
+ if (N_BADMAG(exec))
+ badfmt("bad magic number");
+ if (exec.a_syms == 0)
+ badfmt("stripped");
+ (void)fstat(fileno(f), &stb);
+ if (stb.st_size < N_STROFF(exec) + sizeof(off_t))
+ badfmt("no string table");
+
+ /* seek to and read the symbol table */
+ sa = N_SYMOFF(exec);
+ (void)fseek(f, sa, SEEK_SET);
+ n = exec.a_syms;
+ if (!(symtab = (struct nlist *)malloc(n)))
+ error(NULL);
+ if (fread((void *)symtab, 1, n, f) != n)
+ badfmt("corrupted symbol table");
+
+ /* read string table size and string table */
+ if (fread((void *)&strtabsize, sizeof(int), 1, f) != 1 ||
+ strtabsize <= 0)
+ badfmt("corrupted string table");
+ strings = malloc(strtabsize);
+ if (strings == NULL)
+ error(NULL);
+ /*
+ * Subtract four from strtabsize since strtabsize includes itself,
+ * and we've already read it.
+ */
+ if (fread(strings, 1, strtabsize - sizeof(int), f) !=
+ strtabsize - sizeof(int))
+ badfmt("corrupted string table");
+
+ newtab = (struct nlist *)malloc(n);
+ if (newtab == (struct nlist *)NULL)
+ error(NULL);
+ memset(newtab, 0, n);
+
+ i = n / sizeof(struct nlist);
+ reorder(symtab, newtab, i);
+ free((void *)symtab);
+ symtab = newtab;
+
+ newstrings = malloc(strtabsize);
+ if (newstrings == NULL)
+ error(NULL);
+ t = newstrings;
+ for (symp = symtab; --i >= 0; symp++) {
+ if (symp->n_un.n_strx == 0)
+ continue;
+ if (inlist(symp) < 0) {
+ if (small)
+ continue;
+ if (clean && !savesymb(symp))
+ symp->n_type &= ~N_EXT;
+ }
+ symp->n_un.n_strx -= sizeof(int);
+ (void)strcpy(t, &strings[symp->n_un.n_strx]);
+ symp->n_un.n_strx = (t - newstrings) + sizeof(int);
+ t += strlen(t) + 1;
+ }
+
+ /* update shrunk sizes */
+ strtabsize = t - newstrings + sizeof(int);
+ n = symkept * sizeof(struct nlist);
+
+ /* fix exec sym size */
+ (void)lseek(o, (off_t)0, SEEK_SET);
+ exec.a_syms = n;
+ if (write(o, (void *)&exec, sizeof(exec)) != sizeof(exec))
+ error(kfile);
+
+ (void)lseek(o, sa, SEEK_SET);
+ if (write(o, (void *)symtab, n) != n)
+ error(kfile);
+ if (write(o, (void *)&strtabsize, sizeof(int)) != sizeof(int))
+ error(kfile);
+ if (write(o, newstrings, strtabsize - sizeof(int)) !=
+ strtabsize - sizeof(int))
+ error(kfile);
+
+ ftruncate(o, lseek(o, (off_t)0, SEEK_CUR));
+
+ if ((i = nsym - symfound) > 0) {
+ (void)printf("symorder: %d symbol%s not found:\n",
+ i, i == 1 ? "" : "s");
+ for (i = 0; i < nsym; i++)
+ if (order[i].n_value == 0)
+ printf("%s\n", order[i].n_un.n_name);
+ if (!missing)
+ exit(NOTFOUNDEXIT);
+ }
+ exit(OKEXIT);
+}
+
+savesymb(s)
+ register struct nlist *s;
+{
+ if ((s->n_type & N_EXT) != N_EXT)
+ return 0;
+ switch (s->n_type & N_TYPE) {
+ case N_TEXT:
+ case N_DATA:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+reorder(st1, st2, entries)
+ register struct nlist *st1, *st2;
+ int entries;
+{
+ register struct nlist *p;
+ register int i, n;
+
+ for (p = st1, n = entries; --n >= 0; ++p)
+ if (inlist(p) != -1)
+ ++symfound;
+ for (p = st2 + symfound, n = entries; --n >= 0; ++st1) {
+ if (excluded(st1))
+ continue;
+ i = inlist(st1);
+ if (i == -1)
+ *p++ = *st1;
+ else
+ st2[i] = *st1;
+ ++symkept;
+ }
+}
+
+inlist(p)
+ register struct nlist *p;
+{
+ register char *nam;
+ register struct nlist *op;
+
+ if (p->n_type & N_STAB || p->n_un.n_strx == 0)
+ return (-1);
+ if (p->n_un.n_strx < sizeof(int) || p->n_un.n_strx >= strtabsize)
+ badfmt("corrupted symbol table");
+ nam = &strings[p->n_un.n_strx - sizeof(int)];
+ for (op = &order[nsym]; --op >= order; ) {
+ if (strcmp(op->n_un.n_name, nam) != 0)
+ continue;
+ op->n_value = 1;
+ return (op - order);
+ }
+ return (-1);
+}
+
+excluded(p)
+ register struct nlist *p;
+{
+ register char *nam;
+ register int x;
+
+ if (p->n_type & N_STAB || p->n_un.n_strx == 0)
+ return (0);
+ if (p->n_un.n_strx < sizeof(int) || p->n_un.n_strx >= strtabsize)
+ badfmt("corrupted symbol table");
+ nam = &strings[p->n_un.n_strx - sizeof(int)];
+ for (x = nexclude; --x >= 0; )
+ if (strcmp(nam, exclude[x]) == 0)
+ return (1);
+ return (0);
+}
+
+badfmt(why)
+ char *why;
+{
+ (void)fprintf(stderr,
+ "symorder: %s: %s: %s\n", kfile, why, strerror(EFTYPE));
+ exit(ERREXIT);
+}
+
+error(n)
+ char *n;
+{
+ int sverr;
+
+ sverr = errno;
+ (void)fprintf(stderr, "symorder: ");
+ if (n)
+ (void)fprintf(stderr, "%s: ", n);
+ (void)fprintf(stderr, "%s\n", strerror(sverr));
+ exit(ERREXIT);
+}
+
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: symorder [-m] [-t] [-x excludelist] symlist file\n");
+ exit(ERREXIT);
+}
diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile
new file mode 100644
index 0000000..ecf831e
--- /dev/null
+++ b/usr.bin/systat/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= systat
+CFLAGS+=-I${.CURDIR}/../../sys -I${.CURDIR}/../vmstat
+SRCS= cmds.c cmdtab.c disks.c fetch.c iostat.c keyboard.c vmstat.c main.c \
+ mbufs.c netcmds.c netstat.c pigs.c swap.c
+LDADD= -lcurses -ltermcap -lm -lkvm
+DPADD= ${LIBCURSES} ${LIBTERMCAP} ${LIBM}
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/systat/cmds.c b/usr.bin/systat/cmds.c
new file mode 100644
index 0000000..4987c2c
--- /dev/null
+++ b/usr.bin/systat/cmds.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+#include "systat.h"
+#include "extern.h"
+
+void
+command(cmd)
+ char *cmd;
+{
+ register struct cmdtab *p;
+ register char *cp;
+ int interval, omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+ for (cp = cmd; *cp && !isspace(*cp); cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+ if (*cmd == '\0')
+ return;
+ for (; *cp && isspace(*cp); cp++)
+ ;
+ if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "q") == 0)
+ die(0);
+ if (strcmp(cmd, "load") == 0) {
+ load();
+ goto done;
+ }
+ if (strcmp(cmd, "stop") == 0) {
+ alarm(0);
+ mvaddstr(CMDLINE, 0, "Refresh disabled.");
+ clrtoeol();
+ goto done;
+ }
+ if (strcmp(cmd, "help") == 0) {
+ int col, len;
+
+ move(CMDLINE, col = 0);
+ for (p = cmdtab; p->c_name; p++) {
+ len = strlen(p->c_name);
+ if (col + len > COLS)
+ break;
+ addstr(p->c_name); col += len;
+ if (col + 1 < COLS)
+ addch(' ');
+ }
+ clrtoeol();
+ goto done;
+ }
+ interval = atoi(cmd);
+ if (interval <= 0 &&
+ (strcmp(cmd, "start") == 0 || strcmp(cmd, "interval") == 0)) {
+ interval = *cp ? atoi(cp) : naptime;
+ if (interval <= 0) {
+ error("%d: bad interval.", interval);
+ goto done;
+ }
+ }
+ if (interval > 0) {
+ alarm(0);
+ naptime = interval;
+ display(0);
+ status();
+ goto done;
+ }
+ p = lookup(cmd);
+ if (p == (struct cmdtab *)-1) {
+ error("%s: Ambiguous command.", cmd);
+ goto done;
+ }
+ if (p) {
+ if (curcmd == p)
+ goto done;
+ alarm(0);
+ (*curcmd->c_close)(wnd);
+ wnd = (*p->c_open)();
+ if (wnd == 0) {
+ error("Couldn't open new display");
+ wnd = (*curcmd->c_open)();
+ if (wnd == 0) {
+ error("Couldn't change back to previous cmd");
+ exit(1);
+ }
+ p = curcmd;
+ }
+ if ((p->c_flags & CF_INIT) == 0) {
+ if ((*p->c_init)())
+ p->c_flags |= CF_INIT;
+ else
+ goto done;
+ }
+ curcmd = p;
+ labels();
+ display(0);
+ status();
+ goto done;
+ }
+ if (curcmd->c_cmd == 0 || !(*curcmd->c_cmd)(cmd, cp))
+ error("%s: Unknown command.", cmd);
+done:
+ sigsetmask(omask);
+}
+
+struct cmdtab *
+lookup(name)
+ register char *name;
+{
+ register char *p, *q;
+ register struct cmdtab *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = (struct cmdtab *) 0;
+ for (c = cmdtab; p = c->c_name; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmdtab *)-1);
+ return (found);
+}
+
+void
+status()
+{
+
+ error("Showing %s, refresh every %d seconds.",
+ curcmd->c_name, naptime);
+}
+
+int
+prefix(s1, s2)
+ register char *s1, *s2;
+{
+
+ while (*s1 == *s2) {
+ if (*s1 == '\0')
+ return (1);
+ s1++, s2++;
+ }
+ return (*s1 == '\0');
+}
diff --git a/usr.bin/systat/cmdtab.c b/usr.bin/systat/cmdtab.c
new file mode 100644
index 0000000..71eef34
--- /dev/null
+++ b/usr.bin/systat/cmdtab.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "systat.h"
+#include "extern.h"
+
+struct cmdtab cmdtab[] = {
+ { "pigs", showpigs, fetchpigs, labelpigs,
+ initpigs, openpigs, closepigs, 0,
+ CF_LOADAV },
+ { "swap", showswap, fetchswap, labelswap,
+ initswap, openswap, closeswap, 0,
+ CF_LOADAV },
+ { "mbufs", showmbufs, fetchmbufs, labelmbufs,
+ initmbufs, openmbufs, closembufs, 0,
+ CF_LOADAV },
+ { "iostat", showiostat, fetchiostat, labeliostat,
+ initiostat, openiostat, closeiostat, cmdiostat,
+ CF_LOADAV },
+ { "vmstat", showkre, fetchkre, labelkre,
+ initkre, openkre, closekre, cmdkre,
+ 0 },
+ { "netstat", shownetstat, fetchnetstat, labelnetstat,
+ initnetstat, opennetstat, closenetstat, cmdnetstat,
+ CF_LOADAV },
+ { 0 }
+};
+struct cmdtab *curcmd = &cmdtab[0];
diff --git a/usr.bin/systat/disks.c b/usr.bin/systat/disks.c
new file mode 100644
index 0000000..f1ec4ab
--- /dev/null
+++ b/usr.bin/systat/disks.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)disks.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/buf.h>
+#include <sys/dkstat.h>
+
+#include <nlist.h>
+#include <ctype.h>
+#include <paths.h>
+#include <string.h>
+#include <stdlib.h>
+#include "systat.h"
+#include "extern.h"
+
+static void dkselect __P((char *, int, int []));
+static int read_names __P((void));
+
+static struct nlist namelist[] = {
+#define X_DK_NDRIVE 0
+ { "_dk_ndrive" },
+#define X_DK_WPMS 1
+ { "_dk_wpms" },
+#if defined(hp300) || defined(luna68k)
+#define X_HPDINIT (X_DK_WPMS+1)
+ { "_hp_dinit" },
+#endif
+#if defined(i386)
+#define X_DK_NAMES (X_DK_WPMS+1)
+ { "_dk_names" },
+#endif
+#ifdef mips
+#define X_SCSI_DINIT (X_DK_WPMS+1)
+ { "_scsi_dinit" },
+#endif
+#ifdef sun
+#define X_MBDINIT (X_DK_WPMS+1)
+ { "_mbdinit" },
+#endif
+#ifdef tahoe
+#define X_VBDINIT (X_DK_WPMS+1)
+ { "_vbdinit" },
+#endif
+#ifdef vax
+#define X_MBDINIT (X_DK_WPMS+1)
+ { "_mbdinit" },
+#define X_UBDINIT (X_DK_WPMS+2)
+ { "_ubdinit" },
+#endif
+ { "" },
+};
+
+float *dk_mspw;
+int dk_ndrive, *dk_select;
+char **dr_name;
+
+#include "names.c" /* XXX */
+
+int
+dkinit()
+{
+ register int i;
+ register char *cp;
+ static int once = 0;
+ static char buf[1024];
+
+ if (once)
+ return(1);
+
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[X_DK_NDRIVE].n_value == 0) {
+ error("dk_ndrive undefined in kernel");
+ return(0);
+ }
+ NREAD(X_DK_NDRIVE, &dk_ndrive, LONG);
+ if (dk_ndrive <= 0) {
+ error("dk_ndrive=%d according to %s", dk_ndrive, getbootfile());
+ return(0);
+ }
+ dk_mspw = (float *)calloc(dk_ndrive, sizeof (float));
+ {
+ long *wpms = (long *)calloc(dk_ndrive, sizeof(long));
+ KREAD(NPTR(X_DK_WPMS), wpms, dk_ndrive * sizeof (long));
+ for (i = 0; i < dk_ndrive; i++)
+ *(dk_mspw + i) = (*(wpms + i) == 0)? 0.0:
+ (float) 1.0 / *(wpms + i);
+ free(wpms);
+ }
+ dr_name = (char **)calloc(dk_ndrive, sizeof (char *));
+ dk_select = (int *)calloc(dk_ndrive, sizeof (int));
+ for (cp = buf, i = 0; i < dk_ndrive; i++) {
+ dr_name[i] = cp;
+ sprintf(dr_name[i], "dk%d", i);
+ cp += strlen(dr_name[i]) + 1;
+ if (dk_mspw[i] != 0.0)
+ dk_select[i] = 1;
+ }
+ if (!read_names()) {
+ free(dr_name);
+ free(dk_select);
+ free(dk_mspw);
+ return(0);
+ }
+ once = 1;
+ return(1);
+}
+
+int
+dkcmd(cmd, args)
+ char *cmd, *args;
+{
+ if (prefix(cmd, "display") || prefix(cmd, "add")) {
+ dkselect(args, 1, dk_select);
+ return (1);
+ }
+ if (prefix(cmd, "ignore") || prefix(cmd, "delete")) {
+ dkselect(args, 0, dk_select);
+ return (1);
+ }
+ if (prefix(cmd, "drives")) {
+ register int i;
+
+ move(CMDLINE, 0); clrtoeol();
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_mspw[i] != 0.0)
+ printw("%s ", dr_name[i]);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+dkselect(args, truefalse, selections)
+ char *args;
+ int truefalse, selections[];
+{
+ register char *cp;
+ register int i;
+ char *index();
+
+ cp = index(args, '\n');
+ if (cp)
+ *cp = '\0';
+ for (;;) {
+ for (cp = args; *cp && isspace(*cp); cp++)
+ ;
+ args = cp;
+ for (; *cp && !isspace(*cp); cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+ if (cp - args == 0)
+ break;
+ for (i = 0; i < dk_ndrive; i++)
+ if (strcmp(args, dr_name[i]) == 0) {
+ if (dk_mspw[i] != 0.0)
+ selections[i] = truefalse;
+ else
+ error("%s: drive not configured",
+ dr_name[i]);
+ break;
+ }
+ if (i >= dk_ndrive)
+ error("%s: unknown drive", args);
+ args = cp;
+ }
+}
diff --git a/usr.bin/systat/extern.h b/usr.bin/systat/extern.h
new file mode 100644
index 0000000..2e264f0
--- /dev/null
+++ b/usr.bin/systat/extern.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+#include <fcntl.h>
+#include <kvm.h>
+
+extern struct cmdtab *curcmd;
+extern struct cmdtab cmdtab[];
+extern struct text *xtext;
+extern WINDOW *wnd;
+extern char **dr_name;
+extern char c, *namp, hostname[];
+extern double avenrun[3];
+extern float *dk_mspw;
+extern kvm_t *kd;
+extern long ntext, textp;
+extern int *dk_select;
+extern int CMDLINE;
+extern int dk_ndrive;
+extern int hz, stathz;
+extern double hertz; /* sampling frequency for cp_time and dk_time */
+extern int naptime, col;
+extern int nhosts;
+extern int nports;
+extern int protos;
+extern int verbose;
+
+struct inpcb;
+
+int checkhost __P((struct inpcb *));
+int checkport __P((struct inpcb *));
+void closeiostat __P((WINDOW *));
+void closekre __P((WINDOW *));
+void closembufs __P((WINDOW *));
+void closenetstat __P((WINDOW *));
+void closepigs __P((WINDOW *));
+void closeswap __P((WINDOW *));
+int cmdiostat __P((char *, char *));
+int cmdkre __P((char *, char *));
+int cmdnetstat __P((char *, char *));
+struct cmdtab *lookup __P((char *));
+void command __P((char *));
+void die __P((int));
+void display __P((int));
+int dkinit __P((void));
+int dkcmd __P((char *, char *));
+void error __P((const char *fmt, ...));
+void fetchiostat __P((void));
+void fetchkre __P((void));
+void fetchmbufs __P((void));
+void fetchnetstat __P((void));
+void fetchpigs __P((void));
+void fetchswap __P((void));
+int initiostat __P((void));
+int initkre __P((void));
+int initmbufs __P((void));
+int initnetstat __P((void));
+int initpigs __P((void));
+int initswap __P((void));
+int keyboard __P((void));
+int kvm_ckread __P((void *, void *, int));
+void labeliostat __P((void));
+void labelkre __P((void));
+void labelmbufs __P((void));
+void labelnetstat __P((void));
+void labelpigs __P((void));
+void labels __P((void));
+void labelswap __P((void));
+void load __P((void));
+int netcmd __P((char *, char *));
+void nlisterr __P((struct nlist []));
+WINDOW *openiostat __P((void));
+WINDOW *openkre __P((void));
+WINDOW *openmbufs __P((void));
+WINDOW *opennetstat __P((void));
+WINDOW *openpigs __P((void));
+WINDOW *openswap __P((void));
+int prefix __P((char *, char *));
+void showiostat __P((void));
+void showkre __P((void));
+void showmbufs __P((void));
+void shownetstat __P((void));
+void showpigs __P((void));
+void showswap __P((void));
+void status __P((void));
+void suspend __P((int));
diff --git a/usr.bin/systat/fetch.c b/usr.bin/systat/fetch.c
new file mode 100644
index 0000000..ff69ac5
--- /dev/null
+++ b/usr.bin/systat/fetch.c
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)fetch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include "systat.h"
+#include "extern.h"
+
+int
+kvm_ckread(a, b, l)
+ void *a, *b;
+ int l;
+{
+ if (kvm_read(kd, (u_long)a, b, l) != l) {
+ if (verbose)
+ error("error reading kmem at %x\n", a);
+ return (0);
+ }
+ else
+ return (1);
+}
diff --git a/usr.bin/systat/iostat.c b/usr.bin/systat/iostat.c
new file mode 100644
index 0000000..7c066f1
--- /dev/null
+++ b/usr.bin/systat/iostat.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)iostat.c 8.1 (Berkeley) 6/6/93";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <nlist.h>
+#include <paths.h>
+#include "systat.h"
+#include "extern.h"
+
+static struct nlist namelist[] = {
+#define X_DK_BUSY 0
+ { "_dk_busy" },
+#define X_DK_TIME 1
+ { "_dk_time" },
+#define X_DK_XFER 2
+ { "_dk_xfer" },
+#define X_DK_WDS 3
+ { "_dk_wds" },
+#define X_DK_SEEK 4
+ { "_dk_seek" },
+#define X_CP_TIME 5
+ { "_cp_time" },
+#ifdef vax
+#define X_MBDINIT (X_CP_TIME+1)
+ { "_mbdinit" },
+#define X_UBDINIT (X_CP_TIME+2)
+ { "_ubdinit" },
+#endif
+#ifdef tahoe
+#define X_VBDINIT (X_CP_TIME+1)
+ { "_vbdinit" },
+#endif
+ { "" },
+};
+
+static struct {
+ int dk_busy;
+ long cp_time[CPUSTATES];
+ long *dk_time;
+ long *dk_wds;
+ long *dk_seek;
+ long *dk_xfer;
+} s, s1;
+
+static int linesperregion;
+static double etime;
+static int numbers = 0; /* default display bar graphs */
+static int msps = 0; /* default ms/seek shown */
+
+static int barlabels __P((int));
+static void histogram __P((double, int, double));
+static int numlabels __P((int));
+static int stats __P((int, int, int));
+static void stat1 __P((int, int));
+
+
+WINDOW *
+openiostat()
+{
+ return (subwin(stdscr, LINES-1-5, 0, 5, 0));
+}
+
+void
+closeiostat(w)
+ WINDOW *w;
+{
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+int
+initiostat()
+{
+ if (namelist[X_DK_BUSY].n_type == 0) {
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[X_DK_BUSY].n_type == 0) {
+ error("Disk init information isn't in namelist");
+ return(0);
+ }
+ }
+ if (! dkinit())
+ return(0);
+ if (dk_ndrive) {
+#define allocate(e, t) \
+ s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
+ s1./**/e = (t *)calloc(dk_ndrive, sizeof (t));
+ allocate(dk_time, long);
+ allocate(dk_wds, long);
+ allocate(dk_seek, long);
+ allocate(dk_xfer, long);
+#undef allocate
+ }
+ return(1);
+}
+
+void
+fetchiostat()
+{
+ if (namelist[X_DK_BUSY].n_type == 0)
+ return;
+ NREAD(X_DK_BUSY, &s.dk_busy, LONG);
+ NREAD(X_DK_TIME, s.dk_time, dk_ndrive * LONG);
+ NREAD(X_DK_XFER, s.dk_xfer, dk_ndrive * LONG);
+ NREAD(X_DK_WDS, s.dk_wds, dk_ndrive * LONG);
+ NREAD(X_DK_SEEK, s.dk_seek, dk_ndrive * LONG);
+ NREAD(X_CP_TIME, s.cp_time, sizeof s.cp_time);
+}
+
+#define INSET 10
+
+void
+labeliostat()
+{
+ int row;
+
+ if (namelist[X_DK_BUSY].n_type == 0) {
+ error("No dk_busy defined.");
+ return;
+ }
+ row = 0;
+ wmove(wnd, row, 0); wclrtobot(wnd);
+ mvwaddstr(wnd, row++, INSET,
+ "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100");
+ mvwaddstr(wnd, row++, 0, "cpu user|");
+ mvwaddstr(wnd, row++, 0, " nice|");
+ mvwaddstr(wnd, row++, 0, " system|");
+ mvwaddstr(wnd, row++, 0, "interrupt|");
+ mvwaddstr(wnd, row++, 0, " idle|");
+ if (numbers)
+ row = numlabels(row + 1);
+ else
+ row = barlabels(row + 1);
+}
+
+static int
+numlabels(row)
+ int row;
+{
+ int i, col, regions, ndrives;
+
+#define COLWIDTH 14
+#define DRIVESPERLINE ((wnd->maxx - INSET) / COLWIDTH)
+ for (ndrives = 0, i = 0; i < dk_ndrive; i++)
+ if (dk_select[i])
+ ndrives++;
+ regions = howmany(ndrives, DRIVESPERLINE);
+ /*
+ * Deduct -regions for blank line after each scrolling region.
+ */
+ linesperregion = (wnd->maxy - row - regions) / regions;
+ /*
+ * Minimum region contains space for two
+ * label lines and one line of statistics.
+ */
+ if (linesperregion < 3)
+ linesperregion = 3;
+ col = 0;
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_select[i] && dk_mspw[i] != 0.0) {
+ if (col + COLWIDTH >= wnd->maxx - INSET) {
+ col = 0, row += linesperregion + 1;
+ if (row > wnd->maxy - (linesperregion + 1))
+ break;
+ }
+ mvwaddstr(wnd, row, col + 4, dr_name[i]);
+ mvwaddstr(wnd, row + 1, col, "bps tps msps");
+ col += COLWIDTH;
+ }
+ if (col)
+ row += linesperregion + 1;
+ return (row);
+}
+
+static int
+barlabels(row)
+ int row;
+{
+ int i;
+
+ mvwaddstr(wnd, row++, INSET,
+ "/0 /5 /10 /15 /20 /25 /30 /35 /40 /45 /50");
+ linesperregion = 2 + msps;
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_select[i] && dk_mspw[i] != 0.0) {
+ if (row > wnd->maxy - linesperregion)
+ break;
+ mvwprintw(wnd, row++, 0, "%3.3s bps|", dr_name[i]);
+ mvwaddstr(wnd, row++, 0, " tps|");
+ if (msps)
+ mvwaddstr(wnd, row++, 0, " msps|");
+ }
+ return (row);
+}
+
+
+void
+showiostat()
+{
+ register long t;
+ register int i, row, col;
+
+ if (namelist[X_DK_BUSY].n_type == 0)
+ return;
+ for (i = 0; i < dk_ndrive; i++) {
+#define X(fld) t = s.fld[i]; s.fld[i] -= s1.fld[i]; s1.fld[i] = t
+ X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time);
+ }
+ etime = 0;
+ for(i = 0; i < CPUSTATES; i++) {
+ X(cp_time);
+ etime += s.cp_time[i];
+ }
+ if (etime == 0.0)
+ etime = 1.0;
+ etime /= hertz;
+ row = 1;
+ for (i = 0; i < CPUSTATES; i++)
+ stat1(row++, i);
+ if (!numbers) {
+ row += 2;
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_select[i] && dk_mspw[i] != 0.0) {
+ if (row > wnd->maxy - linesperregion)
+ break;
+ row = stats(row, INSET, i);
+ }
+ return;
+ }
+ col = 0;
+ wmove(wnd, row + linesperregion, 0);
+ wdeleteln(wnd);
+ wmove(wnd, row + 3, 0);
+ winsertln(wnd);
+ for (i = 0; i < dk_ndrive; i++)
+ if (dk_select[i] && dk_mspw[i] != 0.0) {
+ if (col + COLWIDTH >= wnd->maxx) {
+ col = 0, row += linesperregion + 1;
+ if (row > wnd->maxy - (linesperregion + 1))
+ break;
+ wmove(wnd, row + linesperregion, 0);
+ wdeleteln(wnd);
+ wmove(wnd, row + 3, 0);
+ winsertln(wnd);
+ }
+ (void) stats(row + 3, col, i);
+ col += COLWIDTH;
+ }
+}
+
+static int
+stats(row, col, dn)
+ int row, col, dn;
+{
+ double atime, words, xtime, itime;
+
+ atime = s.dk_time[dn];
+ atime /= hertz;
+ words = s.dk_wds[dn]*32.0; /* number of words transferred */
+ xtime = dk_mspw[dn]*words; /* transfer time */
+ itime = atime - xtime; /* time not transferring */
+ if (xtime < 0)
+ itime += xtime, xtime = 0;
+ if (itime < 0)
+ xtime += itime, itime = 0;
+ if (numbers) {
+ mvwprintw(wnd, row, col, "%3.0f%4.0f%5.1f",
+ words / 512 / etime, s.dk_xfer[dn] / etime,
+ s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0.0);
+ return (row);
+ }
+ wmove(wnd, row++, col);
+ histogram(words / 512 / etime, 50, 1.0);
+ wmove(wnd, row++, col);
+ histogram(s.dk_xfer[dn] / etime, 50, 1.0);
+ if (msps) {
+ wmove(wnd, row++, col);
+ histogram(s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0,
+ 50, 1.0);
+ }
+ return (row);
+}
+
+static void
+stat1(row, o)
+ int row, o;
+{
+ register int i;
+ double time;
+
+ time = 0;
+ for (i = 0; i < CPUSTATES; i++)
+ time += s.cp_time[i];
+ if (time == 0.0)
+ time = 1.0;
+ wmove(wnd, row, INSET);
+#define CPUSCALE 0.5
+ histogram(100.0 * s.cp_time[o] / time, 50, CPUSCALE);
+}
+
+static void
+histogram(val, colwidth, scale)
+ double val;
+ int colwidth;
+ double scale;
+{
+ char buf[10];
+ register int k;
+ register int v = (int)(val * scale) + 0.5;
+
+ k = MIN(v, colwidth);
+ if (v > colwidth) {
+ sprintf(buf, "%4.1f", val);
+ k -= strlen(buf);
+ while (k--)
+ waddch(wnd, 'X');
+ waddstr(wnd, buf);
+ return;
+ }
+ while (k--)
+ waddch(wnd, 'X');
+ wclrtoeol(wnd);
+}
+
+int
+cmdiostat(cmd, args)
+ char *cmd, *args;
+{
+
+ if (prefix(cmd, "msps"))
+ msps = !msps;
+ else if (prefix(cmd, "numbers"))
+ numbers = 1;
+ else if (prefix(cmd, "bars"))
+ numbers = 0;
+ else if (!dkcmd(cmd, args))
+ return (0);
+ wclear(wnd);
+ labeliostat();
+ refresh();
+ return (1);
+}
diff --git a/usr.bin/systat/keyboard.c b/usr.bin/systat/keyboard.c
new file mode 100644
index 0000000..ae4feb5
--- /dev/null
+++ b/usr.bin/systat/keyboard.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)keyboard.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <signal.h>
+#include <termios.h>
+
+#include "systat.h"
+#include "extern.h"
+
+int
+keyboard()
+{
+ char ch, line[80];
+ int oldmask;
+
+ for (;;) {
+ col = 0;
+ move(CMDLINE, 0);
+ do {
+ refresh();
+ ch = getch() & 0177;
+ if (ch == 0177 && ferror(stdin)) {
+ clearerr(stdin);
+ continue;
+ }
+ if (ch >= 'A' && ch <= 'Z')
+ ch += 'a' - 'A';
+ if (col == 0) {
+#define mask(s) (1 << ((s) - 1))
+ if (ch == CTRL('l')) {
+ oldmask = sigblock(mask(SIGALRM));
+ wrefresh(curscr);
+ sigsetmask(oldmask);
+ continue;
+ }
+ if (ch == CTRL('g')) {
+ oldmask = sigblock(mask(SIGALRM));
+ status();
+ sigsetmask(oldmask);
+ continue;
+ }
+ if (ch != ':')
+ continue;
+ move(CMDLINE, 0);
+ clrtoeol();
+ }
+ if (ch == erasechar() && col > 0) {
+ if (col == 1 && line[0] == ':')
+ continue;
+ col--;
+ goto doerase;
+ }
+ if (ch == CTRL('w') && col > 0) {
+ while (--col >= 0 && isspace(line[col]))
+ ;
+ col++;
+ while (--col >= 0 && !isspace(line[col]))
+ if (col == 0 && line[0] == ':')
+ break;
+ col++;
+ goto doerase;
+ }
+ if (ch == killchar() && col > 0) {
+ col = 0;
+ if (line[0] == ':')
+ col++;
+ doerase:
+ move(CMDLINE, col);
+ clrtoeol();
+ continue;
+ }
+ if (isprint(ch) || ch == ' ') {
+ line[col] = ch;
+ mvaddch(CMDLINE, col, ch);
+ col++;
+ }
+ } while (col == 0 || (ch != '\r' && ch != '\n'));
+ line[col] = '\0';
+ oldmask = sigblock(mask(SIGALRM));
+ command(line + 1);
+ sigsetmask(oldmask);
+ }
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c
new file mode 100644
index 0000000..69523c9
--- /dev/null
+++ b/usr.bin/systat/main.c
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <nlist.h>
+#include <signal.h>
+#include <stdio.h>
+#include "systat.h"
+#include "extern.h"
+
+static struct nlist namelist[] = {
+#define X_FIRST 0
+#define X_HZ 0
+ { "_hz" },
+#define X_STATHZ 1
+ { "_stathz" },
+ { "" }
+};
+static int dellave;
+
+kvm_t *kd;
+sig_t sigtstpdfl;
+double avenrun[3];
+int col;
+int naptime = 5;
+int verbose = 1; /* to report kvm read errs */
+int hz, stathz;
+double hertz;
+char c;
+char *namp;
+char hostname[MAXHOSTNAMELEN];
+WINDOW *wnd;
+int CMDLINE;
+
+static WINDOW *wload; /* one line window for load average */
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char errbuf[80];
+
+ argc--, argv++;
+ while (argc > 0) {
+ if (argv[0][0] == '-') {
+ struct cmdtab *p;
+
+ p = lookup(&argv[0][1]);
+ if (p == (struct cmdtab *)-1) {
+ fprintf(stderr, "%s: ambiguous request\n",
+ &argv[0][1]);
+ exit(1);
+ }
+ if (p == (struct cmdtab *)0) {
+ fprintf(stderr, "%s: unknown request\n",
+ &argv[0][1]);
+ exit(1);
+ }
+ curcmd = p;
+ } else {
+ naptime = atoi(argv[0]);
+ if (naptime <= 0)
+ naptime = 5;
+ }
+ argc--, argv++;
+ }
+ kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+ if (kd == NULL) {
+ error("%s", errbuf);
+ exit(1);
+ }
+ signal(SIGINT, die);
+ signal(SIGQUIT, die);
+ signal(SIGTERM, die);
+
+ /*
+ * Initialize display. Load average appears in a one line
+ * window of its own. Current command's display appears in
+ * an overlapping sub-window of stdscr configured by the display
+ * routines to minimize update work by curses.
+ */
+ initscr();
+ CMDLINE = LINES - 1;
+ wnd = (*curcmd->c_open)();
+ if (wnd == NULL) {
+ fprintf(stderr, "Couldn't initialize display.\n");
+ die(0);
+ }
+ wload = newwin(1, 0, 3, 20);
+ if (wload == NULL) {
+ fprintf(stderr, "Couldn't set up load average window.\n");
+ die(0);
+ }
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ exit(1);
+ }
+ if (namelist[X_FIRST].n_type == 0) {
+ fprintf(stderr, "couldn't read namelist.\n");
+ exit(1);
+ }
+ gethostname(hostname, sizeof (hostname));
+ NREAD(X_HZ, &hz, LONG);
+ NREAD(X_STATHZ, &stathz, LONG);
+ hertz = stathz ? stathz : hz;
+ (*curcmd->c_init)();
+ curcmd->c_flags |= CF_INIT;
+ labels();
+
+ dellave = 0.0;
+
+ signal(SIGALRM, display);
+ display(0);
+ noecho();
+ crmode();
+ keyboard();
+ /*NOTREACHED*/
+}
+
+void
+labels()
+{
+ if (curcmd->c_flags & CF_LOADAV) {
+ mvaddstr(2, 20,
+ "/0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /10");
+ mvaddstr(3, 5, "Load Average");
+ }
+ (*curcmd->c_label)();
+#ifdef notdef
+ mvprintw(21, 25, "CPU usage on %s", hostname);
+#endif
+ refresh();
+}
+
+void
+display(signo)
+ int signo;
+{
+ register int i, j;
+
+ /* Get the load average over the last minute. */
+ (void) getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
+ (*curcmd->c_fetch)();
+ if (curcmd->c_flags & CF_LOADAV) {
+ j = 5.0*avenrun[0] + 0.5;
+ dellave -= avenrun[0];
+ if (dellave >= 0.0)
+ c = '<';
+ else {
+ c = '>';
+ dellave = -dellave;
+ }
+ if (dellave < 0.1)
+ c = '|';
+ dellave = avenrun[0];
+ wmove(wload, 0, 0); wclrtoeol(wload);
+ for (i = (j > 50) ? 50 : j; i > 0; i--)
+ waddch(wload, c);
+ if (j > 50)
+ wprintw(wload, " %4.1f", avenrun[0]);
+ }
+ (*curcmd->c_refresh)();
+ if (curcmd->c_flags & CF_LOADAV)
+ wrefresh(wload);
+ wrefresh(wnd);
+ move(CMDLINE, col);
+ refresh();
+ alarm(naptime);
+}
+
+void
+load()
+{
+
+ (void) getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
+ mvprintw(CMDLINE, 0, "%4.1f %4.1f %4.1f",
+ avenrun[0], avenrun[1], avenrun[2]);
+ clrtoeol();
+}
+
+void
+die(signo)
+ int signo;
+{
+ move(CMDLINE, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(0);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if __STDC__
+void
+error(const char *fmt, ...)
+#else
+void
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[255];
+ int oy, ox;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ if (wnd) {
+ getyx(stdscr, oy, ox);
+ (void) vsprintf(buf, fmt, ap);
+ clrtoeol();
+ standout();
+ mvaddstr(CMDLINE, 0, buf);
+ standend();
+ move(oy, ox);
+ refresh();
+ } else {
+ (void) vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+}
+
+void
+nlisterr(namelist)
+ struct nlist namelist[];
+{
+ int i, n;
+
+ n = 0;
+ clear();
+ mvprintw(2, 10, "systat: nlist: can't find following symbols:");
+ for (i = 0;
+ namelist[i].n_name != NULL && *namelist[i].n_name != '\0'; i++)
+ if (namelist[i].n_value == 0)
+ mvprintw(2 + ++n, 10, "%s", namelist[i].n_name);
+ move(CMDLINE, 0);
+ clrtoeol();
+ refresh();
+ endwin();
+ exit(1);
+}
diff --git a/usr.bin/systat/mbufs.c b/usr.bin/systat/mbufs.c
new file mode 100644
index 0000000..ac4f73a
--- /dev/null
+++ b/usr.bin/systat/mbufs.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mbufs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mbuf.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <nlist.h>
+#include <paths.h>
+#include "systat.h"
+#include "extern.h"
+
+static struct mbstat *mb;
+
+char *mtnames[] = {
+ "free",
+ "data",
+ "headers",
+ "sockets",
+ "pcbs",
+ "routes",
+ "hosts",
+ "arps",
+ "socknames",
+ "zombies",
+ "sockopts",
+ "frags",
+ "rights",
+ "ifaddrs",
+};
+
+#define NNAMES (sizeof (mtnames) / sizeof (mtnames[0]))
+
+WINDOW *
+openmbufs()
+{
+ return (subwin(stdscr, LINES-5-1, 0, 5, 0));
+}
+
+void
+closembufs(w)
+ WINDOW *w;
+{
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+void
+labelmbufs()
+{
+ wmove(wnd, 0, 0); wclrtoeol(wnd);
+ mvwaddstr(wnd, 0, 10,
+ "/0 /5 /10 /15 /20 /25 /30 /35 /40 /45 /50 /55 /60");
+}
+
+void
+showmbufs()
+{
+ register int i, j, max, index;
+ char buf[10];
+
+ if (mb == 0)
+ return;
+ for (j = 0; j < wnd->maxy; j++) {
+ max = 0, index = -1;
+ for (i = 0; i < wnd->maxy; i++)
+ if (mb->m_mtypes[i] > max) {
+ max = mb->m_mtypes[i];
+ index = i;
+ }
+ if (max == 0)
+ break;
+ if (j > NNAMES)
+ mvwprintw(wnd, 1+j, 0, "%10d", index);
+ else
+ mvwprintw(wnd, 1+j, 0, "%-10.10s", mtnames[index]);
+ wmove(wnd, 1 + j, 10);
+ if (max > 60) {
+ sprintf(buf, " %d", max);
+ max = 60;
+ while (max--)
+ waddch(wnd, 'X');
+ waddstr(wnd, buf);
+ } else {
+ while (max--)
+ waddch(wnd, 'X');
+ wclrtoeol(wnd);
+ }
+ mb->m_mtypes[index] = 0;
+ }
+ wmove(wnd, 1+j, 0); wclrtobot(wnd);
+}
+
+static struct nlist namelist[] = {
+#define X_MBSTAT 0
+ { "_mbstat" },
+ { "" }
+};
+
+int
+initmbufs()
+{
+ if (namelist[X_MBSTAT].n_type == 0) {
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[X_MBSTAT].n_type == 0) {
+ error("namelist on %s failed", getbootfile());
+ return(0);
+ }
+ }
+ if (mb == 0)
+ mb = (struct mbstat *)calloc(1, sizeof (*mb));
+ return(1);
+}
+
+void
+fetchmbufs()
+{
+ if (namelist[X_MBSTAT].n_type == 0)
+ return;
+ NREAD(X_MBSTAT, mb, sizeof (*mb));
+}
diff --git a/usr.bin/systat/netcmds.c b/usr.bin/systat/netcmds.c
new file mode 100644
index 0000000..584b780
--- /dev/null
+++ b/usr.bin/systat/netcmds.c
@@ -0,0 +1,309 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)netcmds.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Common network command support routines.
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "systat.h"
+#include "extern.h"
+
+#define streq(a,b) (strcmp(a,b)==0)
+
+static struct hitem {
+ struct in_addr addr;
+ int onoff;
+} *hosts;
+
+int nports, nhosts, protos;
+
+static void changeitems __P((char *, int));
+static int selectproto __P((char *));
+static void showprotos __P((void));
+static int selectport __P((long, int));
+static void showports __P((void));
+static int selecthost __P((struct in_addr *, int));
+static void showhosts __P((void));
+
+int
+netcmd(cmd, args)
+ char *cmd, *args;
+{
+
+ if (prefix(cmd, "tcp") || prefix(cmd, "udp")) {
+ selectproto(cmd);
+ return (1);
+ }
+ if (prefix(cmd, "ignore") || prefix(cmd, "display")) {
+ changeitems(args, prefix(cmd, "display"));
+ return (1);
+ }
+ if (prefix(cmd, "reset")) {
+ selectproto(0);
+ selecthost(0, 0);
+ selectport(-1, 0);
+ return (1);
+ }
+ if (prefix(cmd, "show")) {
+ move(CMDLINE, 0); clrtoeol();
+ if (*args == '\0') {
+ showprotos();
+ showhosts();
+ showports();
+ return (1);
+ }
+ if (prefix(args, "protos"))
+ showprotos();
+ else if (prefix(args, "hosts"))
+ showhosts();
+ else if (prefix(args, "ports"))
+ showports();
+ else
+ addstr("show what?");
+ return (1);
+ }
+ return (0);
+}
+
+
+static void
+changeitems(args, onoff)
+ char *args;
+ int onoff;
+{
+ register char *cp;
+ struct servent *sp;
+ struct hostent *hp;
+ struct in_addr in;
+ char *index();
+
+ cp = index(args, '\n');
+ if (cp)
+ *cp = '\0';
+ for (;;args = cp) {
+ for (cp = args; *cp && isspace(*cp); cp++)
+ ;
+ args = cp;
+ for (; *cp && !isspace(*cp); cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+ if (cp - args == 0)
+ break;
+ sp = getservbyname(args,
+ protos == TCP ? "tcp" : protos == UDP ? "udp" : 0);
+ if (sp) {
+ selectport(sp->s_port, onoff);
+ continue;
+ }
+ hp = gethostbyname(args);
+ if (hp == 0) {
+ in.s_addr = inet_addr(args);
+ if (in.s_addr == -1) {
+ error("%s: unknown host or port", args);
+ continue;
+ }
+ } else
+ in = *(struct in_addr *)hp->h_addr;
+ selecthost(&in, onoff);
+ }
+}
+
+static int
+selectproto(proto)
+ char *proto;
+{
+ int new = protos;
+
+ if (proto == 0 || streq(proto, "all"))
+ new = TCP|UDP;
+ else if (streq(proto, "tcp"))
+ new = TCP;
+ else if (streq(proto, "udp"))
+ new = UDP;
+ return (new != protos, protos = new);
+}
+
+static void
+showprotos()
+{
+
+ if ((protos&TCP) == 0)
+ addch('!');
+ addstr("tcp ");
+ if ((protos&UDP) == 0)
+ addch('!');
+ addstr("udp ");
+}
+
+static struct pitem {
+ long port;
+ int onoff;
+} *ports;
+
+static int
+selectport(port, onoff)
+ long port;
+ int onoff;
+{
+ register struct pitem *p;
+
+ if (port == -1) {
+ if (ports == 0)
+ return (0);
+ free((char *)ports), ports = 0;
+ nports = 0;
+ return (1);
+ }
+ for (p = ports; p < ports+nports; p++)
+ if (p->port == port) {
+ p->onoff = onoff;
+ return (0);
+ }
+ if (nports == 0)
+ ports = (struct pitem *)malloc(sizeof (*p));
+ else
+ ports = (struct pitem *)realloc(ports, (nports+1)*sizeof (*p));
+ p = &ports[nports++];
+ p->port = port;
+ p->onoff = onoff;
+ return (1);
+}
+
+int
+checkport(inp)
+ register struct inpcb *inp;
+{
+ register struct pitem *p;
+
+ if (ports)
+ for (p = ports; p < ports+nports; p++)
+ if (p->port == inp->inp_lport || p->port == inp->inp_fport)
+ return (p->onoff);
+ return (1);
+}
+
+static void
+showports()
+{
+ register struct pitem *p;
+ struct servent *sp;
+
+ for (p = ports; p < ports+nports; p++) {
+ sp = getservbyport(p->port,
+ protos == TCP|UDP ? 0 : protos == TCP ? "tcp" : "udp");
+ if (!p->onoff)
+ addch('!');
+ if (sp)
+ printw("%s ", sp->s_name);
+ else
+ printw("%d ", p->port);
+ }
+}
+
+static int
+selecthost(in, onoff)
+ struct in_addr *in;
+ int onoff;
+{
+ register struct hitem *p;
+
+ if (in == 0) {
+ if (hosts == 0)
+ return (0);
+ free((char *)hosts), hosts = 0;
+ nhosts = 0;
+ return (1);
+ }
+ for (p = hosts; p < hosts+nhosts; p++)
+ if (p->addr.s_addr == in->s_addr) {
+ p->onoff = onoff;
+ return (0);
+ }
+ if (nhosts == 0)
+ hosts = (struct hitem *)malloc(sizeof (*p));
+ else
+ hosts = (struct hitem *)realloc(hosts, (nhosts+1)*sizeof (*p));
+ p = &hosts[nhosts++];
+ p->addr = *in;
+ p->onoff = onoff;
+ return (1);
+}
+
+int
+checkhost(inp)
+ register struct inpcb *inp;
+{
+ register struct hitem *p;
+
+ if (hosts)
+ for (p = hosts; p < hosts+nhosts; p++)
+ if (p->addr.s_addr == inp->inp_laddr.s_addr ||
+ p->addr.s_addr == inp->inp_faddr.s_addr)
+ return (p->onoff);
+ return (1);
+}
+
+static void
+showhosts()
+{
+ register struct hitem *p;
+ struct hostent *hp;
+
+ for (p = hosts; p < hosts+nhosts; p++) {
+ hp = gethostbyaddr((char *)&p->addr, sizeof (p->addr), AF_INET);
+ if (!p->onoff)
+ addch('!');
+ printw("%s ", hp ? hp->h_name : (char *)inet_ntoa(p->addr));
+ }
+}
diff --git a/usr.bin/systat/netstat.c b/usr.bin/systat/netstat.c
new file mode 100644
index 0000000..3e8ec4d
--- /dev/null
+++ b/usr.bin/systat/netstat.c
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * netstat
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp_var.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include <netinet/tcpip.h>
+#include <netinet/tcp_seq.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcp_debug.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <nlist.h>
+#include <paths.h>
+#include "systat.h"
+#include "extern.h"
+
+static void enter __P((struct inpcb *, struct socket *, int, char *));
+static char *inetname __P((struct in_addr));
+static void inetprint __P((struct in_addr *, int, char *));
+
+#define streq(a,b) (strcmp(a,b)==0)
+#define YMAX(w) ((w)->maxy-1)
+
+WINDOW *
+opennetstat()
+{
+ sethostent(1);
+ setnetent(1);
+ return (subwin(stdscr, LINES-5-1, 0, 5, 0));
+}
+
+struct netinfo {
+ struct netinfo *ni_forw, *ni_prev;
+ short ni_line; /* line on screen */
+ short ni_seen; /* 0 when not present in list */
+ short ni_flags;
+#define NIF_LACHG 0x1 /* local address changed */
+#define NIF_FACHG 0x2 /* foreign address changed */
+ short ni_state; /* tcp state */
+ char *ni_proto; /* protocol */
+ struct in_addr ni_laddr; /* local address */
+ long ni_lport; /* local port */
+ struct in_addr ni_faddr; /* foreign address */
+ long ni_fport; /* foreign port */
+ long ni_rcvcc; /* rcv buffer character count */
+ long ni_sndcc; /* snd buffer character count */
+};
+
+static struct {
+ struct netinfo *ni_forw, *ni_prev;
+} netcb;
+
+static int aflag = 0;
+static int nflag = 0;
+static int lastrow = 1;
+static void enter(), inetprint();
+static char *inetname();
+
+void
+closenetstat(w)
+ WINDOW *w;
+{
+ register struct netinfo *p;
+
+ endhostent();
+ endnetent();
+ p = (struct netinfo *)netcb.ni_forw;
+ while (p != (struct netinfo *)&netcb) {
+ if (p->ni_line != -1)
+ lastrow--;
+ p->ni_line = -1;
+ p = p->ni_forw;
+ }
+ if (w != NULL) {
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+ }
+}
+
+static struct nlist namelist[] = {
+#define X_TCB 0
+ { "_tcb" },
+#define X_UDB 1
+ { "_udb" },
+ { "" },
+};
+
+int
+initnetstat()
+{
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[X_TCB].n_value == 0) {
+ error("No symbols in namelist");
+ return(0);
+ }
+ netcb.ni_forw = netcb.ni_prev = (struct netinfo *)&netcb;
+ protos = TCP|UDP;
+ return(1);
+}
+
+void
+fetchnetstat()
+{
+ register struct inpcb *prev, *next;
+ register struct netinfo *p;
+ struct inpcbhead head;
+ struct inpcb inpcb;
+ struct socket sockb;
+ struct tcpcb tcpcb;
+ void *off;
+ int istcp;
+
+ if (namelist[X_TCB].n_value == 0)
+ return;
+ for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw)
+ p->ni_seen = 0;
+ if (protos&TCP) {
+ off = NPTR(X_TCB);
+ istcp = 1;
+ }
+ else if (protos&UDP) {
+ off = NPTR(X_UDB);
+ istcp = 0;
+ }
+ else {
+ error("No protocols to display");
+ return;
+ }
+again:
+ KREAD(off, &head, sizeof (struct inpcbhead));
+ for (next = head.lh_first; next != NULL; next = inpcb.inp_list.le_next) {
+ KREAD(next, &inpcb, sizeof (inpcb));
+ if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY)
+ continue;
+ if (nhosts && !checkhost(&inpcb))
+ continue;
+ if (nports && !checkport(&inpcb))
+ continue;
+ KREAD(inpcb.inp_socket, &sockb, sizeof (sockb));
+ if (istcp) {
+ KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
+ enter(&inpcb, &sockb, tcpcb.t_state, "tcp");
+ } else
+ enter(&inpcb, &sockb, 0, "udp");
+ }
+ if (istcp && (protos&UDP)) {
+ istcp = 0;
+ off = NPTR(X_UDB);
+ goto again;
+ }
+}
+
+static void
+enter(inp, so, state, proto)
+ register struct inpcb *inp;
+ register struct socket *so;
+ int state;
+ char *proto;
+{
+ register struct netinfo *p;
+
+ /*
+ * Only take exact matches, any sockets with
+ * previously unbound addresses will be deleted
+ * below in the display routine because they
+ * will appear as ``not seen'' in the kernel
+ * data structures.
+ */
+ for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
+ if (!streq(proto, p->ni_proto))
+ continue;
+ if (p->ni_lport != inp->inp_lport ||
+ p->ni_laddr.s_addr != inp->inp_laddr.s_addr)
+ continue;
+ if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr &&
+ p->ni_fport == inp->inp_fport)
+ break;
+ }
+ if (p == (struct netinfo *)&netcb) {
+ if ((p = malloc(sizeof(*p))) == NULL) {
+ error("Out of memory");
+ return;
+ }
+ p->ni_prev = (struct netinfo *)&netcb;
+ p->ni_forw = netcb.ni_forw;
+ netcb.ni_forw->ni_prev = p;
+ netcb.ni_forw = p;
+ p->ni_line = -1;
+ p->ni_laddr = inp->inp_laddr;
+ p->ni_lport = inp->inp_lport;
+ p->ni_faddr = inp->inp_faddr;
+ p->ni_fport = inp->inp_fport;
+ p->ni_proto = proto;
+ p->ni_flags = NIF_LACHG|NIF_FACHG;
+ }
+ p->ni_rcvcc = so->so_rcv.sb_cc;
+ p->ni_sndcc = so->so_snd.sb_cc;
+ p->ni_state = state;
+ p->ni_seen = 1;
+}
+
+/* column locations */
+#define LADDR 0
+#define FADDR LADDR+23
+#define PROTO FADDR+23
+#define RCVCC PROTO+6
+#define SNDCC RCVCC+7
+#define STATE SNDCC+7
+
+
+void
+labelnetstat()
+{
+ if (namelist[X_TCB].n_type == 0)
+ return;
+ wmove(wnd, 0, 0); wclrtobot(wnd);
+ mvwaddstr(wnd, 0, LADDR, "Local Address");
+ mvwaddstr(wnd, 0, FADDR, "Foreign Address");
+ mvwaddstr(wnd, 0, PROTO, "Proto");
+ mvwaddstr(wnd, 0, RCVCC, "Recv-Q");
+ mvwaddstr(wnd, 0, SNDCC, "Send-Q");
+ mvwaddstr(wnd, 0, STATE, "(state)");
+}
+
+void
+shownetstat()
+{
+ register struct netinfo *p, *q;
+
+ /*
+ * First, delete any connections that have gone
+ * away and adjust the position of connections
+ * below to reflect the deleted line.
+ */
+ p = netcb.ni_forw;
+ while (p != (struct netinfo *)&netcb) {
+ if (p->ni_line == -1 || p->ni_seen) {
+ p = p->ni_forw;
+ continue;
+ }
+ wmove(wnd, p->ni_line, 0); wdeleteln(wnd);
+ q = netcb.ni_forw;
+ for (; q != (struct netinfo *)&netcb; q = q->ni_forw)
+ if (q != p && q->ni_line > p->ni_line) {
+ q->ni_line--;
+ /* this shouldn't be necessary */
+ q->ni_flags |= NIF_LACHG|NIF_FACHG;
+ }
+ lastrow--;
+ q = p->ni_forw;
+ p->ni_prev->ni_forw = p->ni_forw;
+ p->ni_forw->ni_prev = p->ni_prev;
+ free(p);
+ p = q;
+ }
+ /*
+ * Update existing connections and add new ones.
+ */
+ for (p = netcb.ni_forw; p != (struct netinfo *)&netcb; p = p->ni_forw) {
+ if (p->ni_line == -1) {
+ /*
+ * Add a new entry if possible.
+ */
+ if (lastrow > YMAX(wnd))
+ continue;
+ p->ni_line = lastrow++;
+ p->ni_flags |= NIF_LACHG|NIF_FACHG;
+ }
+ if (p->ni_flags & NIF_LACHG) {
+ wmove(wnd, p->ni_line, LADDR);
+ inetprint(&p->ni_laddr, p->ni_lport, p->ni_proto);
+ p->ni_flags &= ~NIF_LACHG;
+ }
+ if (p->ni_flags & NIF_FACHG) {
+ wmove(wnd, p->ni_line, FADDR);
+ inetprint(&p->ni_faddr, p->ni_fport, p->ni_proto);
+ p->ni_flags &= ~NIF_FACHG;
+ }
+ mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto);
+ mvwprintw(wnd, p->ni_line, RCVCC, "%6d", p->ni_rcvcc);
+ mvwprintw(wnd, p->ni_line, SNDCC, "%6d", p->ni_sndcc);
+ if (streq(p->ni_proto, "tcp"))
+ if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES)
+ mvwprintw(wnd, p->ni_line, STATE, "%d",
+ p->ni_state);
+ else
+ mvwaddstr(wnd, p->ni_line, STATE,
+ tcpstates[p->ni_state]);
+ wclrtoeol(wnd);
+ }
+ if (lastrow < YMAX(wnd)) {
+ wmove(wnd, lastrow, 0); wclrtobot(wnd);
+ wmove(wnd, YMAX(wnd), 0); wdeleteln(wnd); /* XXX */
+ }
+}
+
+/*
+ * Pretty print an Internet address (net address + port).
+ * If the nflag was specified, use numbers instead of names.
+ */
+static void
+inetprint(in, port, proto)
+ register struct in_addr *in;
+ int port;
+ char *proto;
+{
+ struct servent *sp = 0;
+ char line[80], *cp, *index();
+
+ sprintf(line, "%.*s.", 16, inetname(*in));
+ cp = index(line, '\0');
+ if (!nflag && port)
+ sp = getservbyport(port, proto);
+ if (sp || port == 0)
+ sprintf(cp, "%.8s", sp ? sp->s_name : "*");
+ else
+ sprintf(cp, "%d", ntohs((u_short)port));
+ /* pad to full column to clear any garbage */
+ cp = index(line, '\0');
+ while (cp - line < 22)
+ *cp++ = ' ';
+ *cp = '\0';
+ waddstr(wnd, line);
+}
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static char *
+inetname(in)
+ struct in_addr in;
+{
+ char *cp = 0;
+ static char line[50];
+ struct hostent *hp;
+ struct netent *np;
+
+ if (!nflag && in.s_addr != INADDR_ANY) {
+ int net = inet_netof(in);
+ int lna = inet_lnaof(in);
+
+ if (lna == INADDR_ANY) {
+ np = getnetbyaddr(net, AF_INET);
+ if (np)
+ cp = np->n_name;
+ }
+ if (cp == 0) {
+ hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
+ if (hp)
+ cp = hp->h_name;
+ }
+ }
+ if (in.s_addr == INADDR_ANY)
+ strcpy(line, "*");
+ else if (cp)
+ strcpy(line, cp);
+ else {
+ in.s_addr = ntohl(in.s_addr);
+#define C(x) ((x) & 0xff)
+ sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
+ C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
+ }
+ return (line);
+}
+
+int
+cmdnetstat(cmd, args)
+ char *cmd, *args;
+{
+ register struct netinfo *p;
+
+ if (prefix(cmd, "all")) {
+ aflag = !aflag;
+ goto fixup;
+ }
+ if (prefix(cmd, "numbers") || prefix(cmd, "names")) {
+ int new;
+
+ new = prefix(cmd, "numbers");
+ if (new == nflag)
+ return (1);
+ p = netcb.ni_forw;
+ for (; p != (struct netinfo *)&netcb; p = p->ni_forw) {
+ if (p->ni_line == -1)
+ continue;
+ p->ni_flags |= NIF_LACHG|NIF_FACHG;
+ }
+ nflag = new;
+ goto redisplay;
+ }
+ if (!netcmd(cmd, args))
+ return (0);
+fixup:
+ fetchnetstat();
+redisplay:
+ shownetstat();
+ refresh();
+ return (1);
+}
diff --git a/usr.bin/systat/pigs.c b/usr.bin/systat/pigs.c
new file mode 100644
index 0000000..a8b06f1
--- /dev/null
+++ b/usr.bin/systat/pigs.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pigs.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+/*
+ * Pigs display from Bill Reeves at Lucasfilm
+ */
+
+#include <sys/param.h>
+#include <sys/dkstat.h>
+#include <sys/dir.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <curses.h>
+#include <math.h>
+#include <nlist.h>
+#include <pwd.h>
+#include <stdlib.h>
+
+#include "extern.h"
+#include "systat.h"
+
+int compar __P((const void *, const void *));
+
+static int nproc;
+static struct p_times {
+ float pt_pctcpu;
+ struct kinfo_proc *pt_kp;
+} *pt;
+
+static long stime[CPUSTATES];
+static int fscale;
+static double lccpu;
+
+WINDOW *
+openpigs()
+{
+ return (subwin(stdscr, LINES-5-1, 0, 5, 0));
+}
+
+void
+closepigs(w)
+ WINDOW *w;
+{
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+
+void
+showpigs()
+{
+ register int i, j, y, k;
+ struct eproc *ep;
+ float total;
+ int factor;
+ char *uname, *pname, pidname[30];
+
+ if (pt == NULL)
+ return;
+ /* Accumulate the percent of cpu per user. */
+ total = 0.0;
+ for (i = 0; i <= nproc; i++) {
+ /* Accumulate the percentage. */
+ total += pt[i].pt_pctcpu;
+ }
+
+ if (total < 1.0)
+ total = 1.0;
+ factor = 50.0/total;
+
+ qsort(pt, nproc + 1, sizeof (struct p_times), compar);
+ y = 1;
+ i = nproc + 1;
+ if (i > wnd->maxy-1)
+ i = wnd->maxy-1;
+ for (k = 0; i > 0 && pt[k].pt_pctcpu > 0.01; i--, y++, k++) {
+ if (pt[k].pt_kp == NULL) {
+ uname = "";
+ pname = "<idle>";
+ }
+ else {
+ ep = &pt[k].pt_kp->kp_eproc;
+ uname = (char *)user_from_uid(ep->e_ucred.cr_uid, 0);
+ pname = pt[k].pt_kp->kp_proc.p_comm;
+ }
+ wmove(wnd, y, 0);
+ wclrtoeol(wnd);
+ mvwaddstr(wnd, y, 0, uname);
+ sprintf(pidname, "%10.10s", pname, 0);
+ mvwaddstr(wnd, y, 9, pidname);
+ wmove(wnd, y, 20);
+ for (j = pt[k].pt_pctcpu*factor + 0.5; j > 0; j--)
+ waddch(wnd, 'X');
+ }
+ wmove(wnd, y, 0); wclrtobot(wnd);
+}
+
+static struct nlist namelist[] = {
+#define X_FIRST 0
+#define X_CPTIME 0
+ { "_cp_time" },
+#define X_CCPU 1
+ { "_ccpu" },
+#define X_FSCALE 2
+ { "_fscale" },
+
+ { "" }
+};
+
+int
+initpigs()
+{
+ fixpt_t ccpu;
+
+ if (namelist[X_FIRST].n_type == 0) {
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[X_FIRST].n_type == 0) {
+ error("namelist failed");
+ return(0);
+ }
+ }
+ KREAD(NPTR(X_CPTIME), stime, sizeof (stime));
+ NREAD(X_CCPU, &ccpu, LONG);
+ NREAD(X_FSCALE, &fscale, LONG);
+ lccpu = log((double) ccpu / fscale);
+
+ return(1);
+}
+
+void
+fetchpigs()
+{
+ register int i;
+ register float time;
+ register struct proc *pp;
+ register float *pctp;
+ struct kinfo_proc *kpp;
+ long ctime[CPUSTATES];
+ double t;
+ static int lastnproc = 0;
+
+ if (namelist[X_FIRST].n_type == 0)
+ return;
+ if ((kpp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc)) == NULL) {
+ error("%s", kvm_geterr(kd));
+ if (pt)
+ free(pt);
+ return;
+ }
+ if (nproc > lastnproc) {
+ free(pt);
+ if ((pt =
+ malloc((nproc + 1) * sizeof(struct p_times))) == NULL) {
+ error("Out of memory");
+ die(0);
+ }
+ }
+ lastnproc = nproc;
+ /*
+ * calculate %cpu for each proc
+ */
+ for (i = 0; i < nproc; i++) {
+ pt[i].pt_kp = &kpp[i];
+ pp = &kpp[i].kp_proc;
+ pctp = &pt[i].pt_pctcpu;
+ time = pp->p_swtime;
+ if (time == 0 || (pp->p_flag & P_INMEM) == 0)
+ *pctp = 0;
+ else
+ *pctp = ((double) pp->p_pctcpu /
+ fscale) / (1.0 - exp(time * lccpu));
+ }
+ /*
+ * and for the imaginary "idle" process
+ */
+ KREAD(NPTR(X_CPTIME), ctime, sizeof (ctime));
+ t = 0;
+ for (i = 0; i < CPUSTATES; i++)
+ t += ctime[i] - stime[i];
+ if (t == 0.0)
+ t = 1.0;
+ pt[nproc].pt_kp = NULL;
+ pt[nproc].pt_pctcpu = (ctime[CP_IDLE] - stime[CP_IDLE]) / t;
+ for (i = 0; i < CPUSTATES; i++)
+ stime[i] = ctime[i];
+}
+
+void
+labelpigs()
+{
+ wmove(wnd, 0, 0);
+ wclrtoeol(wnd);
+ mvwaddstr(wnd, 0, 20,
+ "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100");
+}
+
+int
+compar(a, b)
+ const void *a, *b;
+{
+ return (((struct p_times *) a)->pt_pctcpu >
+ ((struct p_times *) b)->pt_pctcpu)? -1: 1;
+}
diff --git a/usr.bin/systat/swap.c b/usr.bin/systat/swap.c
new file mode 100644
index 0000000..68375cb
--- /dev/null
+++ b/usr.bin/systat/swap.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1980, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)swap.c 8.2 (Berkeley) 2/21/94";
+#endif /* not lint */
+
+/*
+ * swapinfo - based on a program of the same name by Kevin Lahey
+ */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/rlist.h>
+
+#include <kvm.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "systat.h"
+#include "extern.h"
+
+extern char *devname __P((int, int));
+extern char *getbsize __P((int *headerlenp, long *blocksizep));
+void showspace __P((char *header, int hlen, long blocksize));
+
+kvm_t *kd;
+
+struct nlist syms[] = {
+ { "_swapmap" }, /* list of free swap areas */
+#define VM_SWAPMAP 0
+ { "_nswapmap" },/* size of the swap map */
+#define VM_NSWAPMAP 1
+ { "_swdevt" }, /* list of swap devices and sizes */
+#define VM_SWDEVT 2
+ { "_nswap" }, /* size of largest swap device */
+#define VM_NSWAP 3
+ { "_nswdev" }, /* number of swap devices */
+#define VM_NSWDEV 4
+ { "_dmmax" }, /* maximum size of a swap block */
+#define VM_DMMAX 5
+ 0
+};
+
+static int nswap, nswdev, dmmax, nswapmap;
+static struct swdevt *sw;
+static long *perdev, blocksize;
+static struct map *swapmap, *kswapmap;
+static struct mapent *mp;
+static int nfree, hlen;
+
+#define SVAR(var) __STRING(var) /* to force expansion */
+#define KGET(idx, var) \
+ KGET1(idx, &var, sizeof(var), SVAR(var))
+#define KGET1(idx, p, s, msg) \
+ KGET2(syms[idx].n_value, p, s, msg)
+#define KGET2(addr, p, s, msg) \
+ if (kvm_read(kd, addr, p, s) != s) { \
+ error("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return (0); \
+ }
+
+WINDOW *
+openswap()
+{
+ return (subwin(stdscr, LINES-5-1, 0, 5, 0));
+}
+
+void
+closeswap(w)
+ WINDOW *w;
+{
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+initswap()
+{
+ int i;
+ char msgbuf[BUFSIZ];
+ static int once = 0;
+
+#if 0
+ if (once)
+ return (1);
+ if (kvm_nlist(kd, syms)) {
+ strcpy(msgbuf, "systat: swap: cannot find");
+ for (i = 0; syms[i].n_name != NULL; i++) {
+ if (syms[i].n_value == 0) {
+ strcat(msgbuf, " ");
+ strcat(msgbuf, syms[i].n_name);
+ }
+ }
+ error(msgbuf);
+ return (0);
+ }
+ KGET(VM_NSWAP, nswap);
+ KGET(VM_NSWDEV, nswdev);
+ KGET(VM_DMMAX, dmmax);
+ KGET(VM_NSWAPMAP, nswapmap);
+ KGET(VM_SWAPMAP, kswapmap); /* kernel `swapmap' is a pointer */
+ if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
+ (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
+ (mp = malloc(nswapmap * sizeof(*mp))) == NULL) {
+ error("swap malloc");
+ return (0);
+ }
+ KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
+ once = 1;
+ return (1);
+#else
+ return (0);
+#endif
+}
+
+void
+fetchswap()
+{
+ int s, e, i;
+
+#if 0
+ s = nswapmap * sizeof(*mp);
+ if (kvm_read(kd, (long)kswapmap, mp, s) != s)
+ error("cannot read swapmap: %s", kvm_geterr(kd));
+
+ /* first entry in map is `struct map'; rest are mapent's */
+ swapmap = (struct map *)mp;
+ if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
+ error("panic: swap: nswapmap goof");
+
+ /*
+ * Count up swap space.
+ */
+ nfree = 0;
+ bzero(perdev, nswdev * sizeof(*perdev));
+ for (mp++; mp->m_addr != 0; mp++) {
+ s = mp->m_addr; /* start of swap region */
+ e = mp->m_addr + mp->m_size; /* end of region */
+ nfree += mp->m_size;
+
+ /*
+ * Swap space is split up among the configured disks.
+ * The first dmmax blocks of swap space some from the
+ * first disk, the next dmmax blocks from the next,
+ * and so on. The list of free space joins adjacent
+ * free blocks, ignoring device boundries. If we want
+ * to keep track of this information per device, we'll
+ * just have to extract it ourselves.
+ */
+
+ /* calculate first device on which this falls */
+ i = (s / dmmax) % nswdev;
+ while (s < e) { /* XXX this is inefficient */
+ int bound = roundup(s + 1, dmmax);
+
+ if (bound > e)
+ bound = e;
+ perdev[i] += bound - s;
+ if (++i >= nswdev)
+ i = 0;
+ s = bound;
+ }
+ }
+#endif
+}
+
+void
+labelswap()
+{
+ char *header;
+ int row, i;
+
+ row = 0;
+ wmove(wnd, row, 0); wclrtobot(wnd);
+ header = getbsize(&hlen, &blocksize);
+ mvwprintw(wnd, row++, 0, "%-5s%*s%9s %55s",
+ "Disk", hlen, header, "Used",
+ "/0% /10% /20% /30% /40% /50% /60% /70% /80% /90% /100%");
+ for (i = 0; i < nswdev; i++) {
+ mvwprintw(wnd, i + 1, 0, "%-5s",
+ devname(sw[i].sw_dev, S_IFBLK));
+ }
+}
+
+void
+showswap()
+{
+ int col, row, div, i, j, avail, npfree, used, xsize, xfree;
+
+ div = blocksize / 512;
+ avail = npfree = 0;
+ for (i = 0; i < nswdev; i++) {
+ col = 5;
+ mvwprintw(wnd, i + 1, col, "%*d", hlen, sw[i].sw_nblks / div);
+ col += hlen;
+ /*
+ * Don't report statistics for partitions which have not
+ * yet been activated via swapon(8).
+ */
+ if (!sw[i].sw_freed) {
+ mvwprintw(wnd, i + 1, col + 8,
+ "0 *** not available for swapping ***");
+ continue;
+ }
+ xsize = sw[i].sw_nblks;
+ xfree = perdev[i];
+ used = xsize - xfree;
+ mvwprintw(wnd, i + 1, col, "%9d ", used / div);
+ for (j = (100 * used / xsize + 1) / 2; j > 0; j--)
+ waddch(wnd, 'X');
+ npfree++;
+ avail += xsize;
+ }
+ /*
+ * If only one partition has been set up via swapon(8), we don't
+ * need to bother with totals.
+ */
+ if (npfree > 1) {
+ used = avail - nfree;
+ mvwprintw(wnd, i + 1, 0, "%-5s%*d%9d ",
+ "Total", hlen, avail / div, used / div);
+ for (j = (100 * used / avail + 1) / 2; j > 0; j--)
+ waddch(wnd, 'X');
+ }
+}
diff --git a/usr.bin/systat/systat.1 b/usr.bin/systat/systat.1
new file mode 100644
index 0000000..a4d82e2
--- /dev/null
+++ b/usr.bin/systat/systat.1
@@ -0,0 +1,424 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)systat.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt SYSTAT 1
+.Os BSD 4.3
+.Sh NAME
+.Nm systat
+.Nd display system statistics on a crt
+.Sh SYNOPSIS
+.Nm systat
+.Op Fl display
+.Op Ar refresh-interval
+.Sh DESCRIPTION
+.Nm Systat
+displays various system statistics in a screen oriented fashion
+using the curses screen display library,
+.Xr curses 3 .
+.Pp
+While
+.Nm systat
+is running the screen is usually divided into two windows (an exception
+is the vmstat display which uses the entire screen). The
+upper window depicts the current system load average. The
+information displayed in the lower window may vary, depending on
+user commands. The last line on the screen is reserved for user
+input and error messages.
+.Pp
+By default
+.Nm systat
+displays the processes getting the largest percentage of the processor
+in the lower window. Other displays show swap space usage, disk
+.Tn I/O
+statistics (a la
+.Xr iostat 1 ) ,
+virtual memory statistics (a la
+.Xr vmstat 1 ) ,
+network ``mbuf'' utilization, and network connections (a la
+.Xr netstat 1 ) .
+.Pp
+Input is interpreted at two different levels.
+A ``global'' command interpreter processes all keyboard input.
+If this command interpreter fails to recognize a command, the
+input line is passed to a per-display command interpreter. This
+allows each display to have certain display-specific commands.
+.Pp
+Command line options:
+.Bl -tag -width "refresh_interval"
+.It Fl Ns Ar display
+The
+.Fl
+flag expects
+.Ar display
+to be one of:
+.Ic pigs ,
+.Ic iostat ,
+.Ic swap ,
+.Ic mbufs ,
+.Ic vmstat
+or
+.Ic netstat .
+These displays can also be requested interactively (without the
+.Dq Fl )
+and are described in
+full detail below.
+.It Ar refresh-interval
+The
+.Ar refresh-value
+specifies the screen refresh time interval in seconds.
+.El
+.Pp
+Certain characters cause immediate action by
+.Nm systat .
+These are
+.Bl -tag -width Fl
+.It Ic \&^L
+Refresh the screen.
+.It Ic \&^G
+Print the name of the current ``display'' being shown in
+the lower window and the refresh interval.
+.It Ic \&^Z
+Stop
+.Nm systat .
+.It Ic \&:
+Move the cursor to the command line and interpret the input
+line typed as a command. While entering a command the
+current character erase, word erase, and line kill characters
+may be used.
+.El
+.Pp
+The following commands are interpreted by the ``global''
+command interpreter.
+.Bl -tag -width Fl
+.It Ic help
+Print the names of the available displays on the command line.
+.It Ic load
+Print the load average over the past 1, 5, and 15 minutes
+on the command line.
+.It Ic stop
+Stop refreshing the screen.
+.It Xo
+.Op Ic start
+.Op Ar number
+.Xc
+Start (continue) refreshing the screen. If a second, numeric,
+argument is provided it is interpreted as a refresh interval
+(in seconds).
+Supplying only a number will set the refresh interval to this
+value.
+.It Ic quit
+Exit
+.Nm systat .
+(This may be abbreviated to
+.Ic q . )
+.El
+.Pp
+The available displays are:
+.Bl -tag -width Ic
+.It Ic pigs
+Display, in the lower window, those processes resident in main
+memory and getting the
+largest portion of the processor (the default display).
+When less than 100% of the
+processor is scheduled to user processes, the remaining time
+is accounted to the ``idle'' process.
+.It Ic iostat
+Display, in the lower window, statistics about processor use
+and disk throughput. Statistics on processor use appear as
+bar graphs of the amount of time executing in user mode (``user''),
+in user mode running low priority processes (``nice''), in
+system mode (``system''), in interrupt mode (``interrupt''),
+and idle (``idle''). Statistics
+on disk throughput show, for each drive, kilobytes of data transferred,
+number of disk transactions performed, and average seek time
+(in milliseconds). This information may be displayed as
+bar graphs or as rows of numbers which scroll downward. Bar
+graphs are shown by default;
+.Pp
+The following commands are specific to the
+.Ic iostat
+display; the minimum unambiguous prefix may be supplied.
+.Pp
+.Bl -tag -width Fl -compact
+.It Cm numbers
+Show the disk
+.Tn I/O
+statistics in numeric form. Values are
+displayed in numeric columns which scroll downward.
+.It Cm bars
+Show the disk
+.Tn I/O
+statistics in bar graph form (default).
+.It Cm msps
+Toggle the display of average seek time (the default is to
+not display seek times).
+.El
+.It Ic swap
+Show information about swap space usage on all the
+swap areas compiled into the kernel.
+The first column is the device name of the partition.
+The next column is the total space available in the partition.
+The
+.Ar Used
+column indicates the total blocks used so far;
+the graph shows the percentage of space in use on each partition.
+If there are more than one swap partition in use,
+a total line is also shown.
+Areas known to the kernel, but not in use are shown as not available.
+.It Ic mbufs
+Display, in the lower window, the number of mbufs allocated
+for particular uses, i.e. data, socket structures, etc.
+.It Ic vmstat
+Take over the entire display and show a (rather crowded) compendium
+of statistics related to virtual memory usage, process scheduling,
+device interrupts, system name translation cacheing, disk
+.Tn I/O
+etc.
+.Pp
+The upper left quadrant of the screen shows the number
+of users logged in and the load average over the last one, five,
+and fifteen minute intervals.
+Below this line are statistics on memory utilization.
+The first row of the table reports memory usage only among
+active processes, that is processes that have run in the previous
+twenty seconds.
+The second row reports on memory usage of all processes.
+The first column reports on the number of physical pages
+claimed by processes.
+The second column reports the number of physical pages that
+are devoted to read only text pages.
+The third and fourth columns report the same two figures for
+virtual pages, that is the number of pages that would be
+needed if all processes had all of their pages.
+Finally the last column shows the number of physical pages
+on the free list.
+.Pp
+Below the memory display is the disk usage display.
+It reports the number of seeks, transfers, and number
+of kilobyte blocks transferred per second averaged over the
+refresh period of the display (by default, five seconds).
+For some disks it also reports the average milliseconds per seek.
+Note that the system only keeps statistics on at most four disks.
+.Pp
+Below the disk display is a list of the
+average number of processes (over the last refresh interval)
+that are runnable (`r'), in page wait (`p'),
+in disk wait other than paging (`d'),
+sleeping (`s'), and swapped out but desiring to run (`w').
+Below the queue length listing is a numerical listing and
+a bar graph showing the amount of
+system (shown as `='), interrupt (shown as `+'), user (shown as `>'),
+nice (shown as `-'), and idle time (shown as ` ').
+.Pp
+At the bottom left are statistics on name translations.
+It lists the number of names translated in the previous interval,
+the number and percentage of the translations that were
+handled by the system wide name translation cache, and
+the number and percentage of the translations that were
+handled by the per process name translation cache.
+.Pp
+Under the date in the upper right hand quadrant are statistics
+on paging and swapping activity.
+The first two columns report the average number of pages
+brought in and out per second over the last refresh interval
+due to page faults and the paging daemon.
+The third and fourth columns report the average number of pages
+brought in and out per second over the last refresh interval
+due to swap requests initiated by the scheduler.
+The first row of the display shows the average
+number of disk transfers per second over the last refresh interval;
+the second row of the display shows the average
+number of pages transferred per second over the last refresh interval.
+.Pp
+Below the paging statistics is a line listing the average number of
+total reclaims ('Rec'),
+intransit blocking page faults (`It'),
+swap text pages found in free list (`F/S'),
+file system text pages found in free list (`F/F'),
+reclaims from free list
+pages freed by the clock daemon (`Fre'),
+and sequential process pages freed (`SFr')
+per second over the refresh interval.
+.Pp
+Below this line are statistics on the average number of
+zero filled pages (`zf') and demand filled text pages (`xf')
+per second over the refresh period.
+The first row indicates the number of requests that were
+resolved, the second row shows the number that were set up,
+and the last row shows the percentage of setup requests that were
+actually used.
+Note that this percentage is usually less than 100%,
+however it may exceed 100% if a large number of requests
+are actually used long after they were set up during a
+period when no new pages are being set up.
+Thus this figure is most interesting when observed over
+a long time period, such as from boot time
+(see below on getting such a display).
+.Pp
+Below the page fill statistics is a column that
+lists the average number of context switches (`Csw'),
+traps (`Trp'; includes page faults), system calls (`Sys'), interrupts (`Int'),
+characters output to DZ ports using
+.No pseudo Ns -DMA
+(`Pdm'),
+network software interrupts (`Sof'),
+page faults (`Flt'), pages scanned by the page daemon (`Scn'),
+and revolutions of the page daemon's hand (`Rev')
+per second over the refresh interval.
+.Pp
+Running down the right hand side of the display is a breakdown
+of the interrupts being handled by the system.
+At the top of the list is the total interrupts per second
+over the time interval.
+The rest of the column breaks down the total on a device
+by device basis.
+Only devices that have interrupted at least once since boot time are shown.
+.Pp
+The following commands are specific to the
+.Ic vmstat
+display; the minimum unambiguous prefix may be supplied.
+.Pp
+.Bl -tag -width Ar -compact
+.It Cm boot
+Display cumulative statistics since the system was booted.
+.It Cm run
+Display statistics as a running total from the point this
+command is given.
+.It Cm time
+Display statistics averaged over the refresh interval (the default).
+.It Cm zero
+Reset running statistics to zero.
+.El
+.It Ic netstat
+Display, in the lower window, network connections. By default,
+network servers awaiting requests are not displayed. Each address
+is displayed in the format ``host.port'', with each shown symbolically,
+when possible. It is possible to have addresses displayed numerically,
+limit the display to a set of ports, hosts, and/or protocols
+(the minimum unambiguous prefix may be supplied):
+.Pp
+.Bl -tag -width Ar -compact
+.It Cm all
+Toggle the displaying of server processes awaiting requests (this
+is the equivalent of the
+.Fl a
+flag to
+.Ar netstat 1 ) .
+.It Cm numbers
+Display network addresses numerically.
+.It Cm names
+Display network addresses symbolically.
+.It Ar protocol
+Display only network connections using the indicated protocol
+(currently either ``tcp'' or ``udp'').
+.It Cm ignore Op Ar items
+Do not display information about connections associated with
+the specified hosts or ports. Hosts and ports may be specified
+by name (``vangogh'', ``ftp''), or numerically. Host addresses
+use the Internet dot notation (``128.32.0.9''). Multiple items
+may be specified with a single command by separating them with
+spaces.
+.It Cm display Op Ar items
+Display information about the connections associated with the
+specified hosts or ports. As for
+.Ar ignore ,
+.Op Ar items
+may be names or numbers.
+.It Cm show Op Ar ports\&|hosts
+Show, on the command line, the currently selected protocols,
+hosts, and ports. Hosts and ports which are being ignored
+are prefixed with a `!'. If
+.Ar ports
+or
+.Ar hosts
+is supplied as an argument to
+.Cm show ,
+then only the requested information will be displayed.
+.It Cm reset
+Reset the port, host, and protocol matching mechanisms to the default
+(any protocol, port, or host).
+.El
+.El
+.Pp
+Commands to switch between displays may be abbreviated to the
+minimum unambiguous prefix; for example, ``io'' for ``iostat''.
+Certain information may be discarded when the screen size is
+insufficient for display. For example, on a machine with 10
+drives the
+.Ic iostat
+bar graph displays only 3 drives on a 24 line terminal. When
+a bar graph would overflow the allotted screen space it is
+truncated and the actual value is printed ``over top'' of the bar.
+.Pp
+The following commands are common to each display which shows
+information about disk drives. These commands are used to
+select a set of drives to report on, should your system have
+more drives configured than can normally be displayed on the
+screen.
+.Pp
+.Bl -tag -width Tx -compact
+.It Cm ignore Op Ar drives
+Do not display information about the drives indicated. Multiple
+drives may be specified, separated by spaces.
+.It Cm display Op Ar drives
+Display information about the drives indicated. Multiple drives
+may be specified, separated by spaces.
+.El
+.Sh FILES
+.Bl -tag -width /etc/networks -compact
+.It Pa /kernel
+For the namelist.
+.It Pa /dev/kmem
+For information in main memory.
+.It Pa /dev/drum
+For information about swapped out processes.
+.It Pa /etc/hosts
+For host names.
+.It Pa /etc/networks
+For network names.
+.It Pa /etc/services
+For port names.
+.El
+.Sh HISTORY
+The
+.Nm systat
+program appeared in
+.Bx 4.3 .
+.Sh BUGS
+Takes 2-10 percent of the cpu.
+Certain displays presume a minimum of 80 characters per line.
+The
+.Ic vmstat
+display looks out of place because it is (it was added in as
+a separate display rather than created as a new program).
diff --git a/usr.bin/systat/systat.h b/usr.bin/systat/systat.h
new file mode 100644
index 0000000..72f65ff
--- /dev/null
+++ b/usr.bin/systat/systat.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1980, 1989, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)systat.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <curses.h>
+
+struct cmdtab {
+ char *c_name; /* command name */
+ void (*c_refresh)(); /* display refresh */
+ void (*c_fetch)(); /* sets up data structures */
+ void (*c_label)(); /* label display */
+ int (*c_init)(); /* initialize namelist, etc. */
+ WINDOW *(*c_open)(); /* open display */
+ void (*c_close)(); /* close display */
+ int (*c_cmd)(); /* display command interpreter */
+ char c_flags; /* see below */
+};
+
+#define CF_INIT 0x1 /* been initialized */
+#define CF_LOADAV 0x2 /* display w/ load average */
+
+#define TCP 0x1
+#define UDP 0x2
+
+#define KREAD(addr, buf, len) kvm_ckread((addr), (buf), (len))
+#define NVAL(indx) namelist[(indx)].n_value
+#define NPTR(indx) (void *)NVAL((indx))
+#define NREAD(indx, buf, len) kvm_ckread(NPTR((indx)), (buf), (len))
+#define LONG (sizeof (long))
diff --git a/usr.bin/systat/vmstat.c b/usr.bin/systat/vmstat.c
new file mode 100644
index 0000000..f6a89a7
--- /dev/null
+++ b/usr.bin/systat/vmstat.c
@@ -0,0 +1,675 @@
+/*-
+ * Copyright (c) 1983, 1989, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 1/12/94";
+#endif /* not lint */
+
+/*
+ * Cursed vmstat -- from Robert Elz.
+ */
+
+#include <sys/param.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <vm/vm.h>
+
+#include <signal.h>
+#include <nlist.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <paths.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "systat.h"
+#include "extern.h"
+
+static struct Info {
+ long time[CPUSTATES];
+ struct vmmeter Cnt;
+ struct vmtotal Total;
+ long *dk_time;
+ long *dk_wds;
+ long *dk_seek;
+ long *dk_xfer;
+ int dk_busy;
+ struct nchstats nchstats;
+ long nchcount;
+ long *intrcnt;
+ int bufspace;
+} s, s1, s2, z;
+
+#define cnt s.Cnt
+#define oldcnt s1.Cnt
+#define total s.Total
+#define nchtotal s.nchstats
+#define oldnchtotal s1.nchstats
+
+static enum state { BOOT, TIME, RUN } state = TIME;
+
+static void allocinfo __P((struct Info *));
+static void copyinfo __P((struct Info *, struct Info *));
+static float cputime __P((int));
+static void dinfo __P((int, int));
+static void getinfo __P((struct Info *, enum state));
+static void putint __P((int, int, int, int));
+static void putfloat __P((double, int, int, int, int, int));
+static int ucount __P((void));
+
+static int ut;
+static char buf[26];
+static time_t t;
+static double etime;
+static int nintr;
+static long *intrloc;
+static char **intrname;
+static int nextintsrow;
+
+struct utmp utmp;
+
+
+WINDOW *
+openkre()
+{
+
+ ut = open(_PATH_UTMP, O_RDONLY);
+ if (ut < 0)
+ error("No utmp");
+ return (stdscr);
+}
+
+void
+closekre(w)
+ WINDOW *w;
+{
+
+ (void) close(ut);
+ if (w == NULL)
+ return;
+ wclear(w);
+ wrefresh(w);
+}
+
+
+static struct nlist namelist[] = {
+#define X_CPTIME 0
+ { "_cp_time" },
+#define X_CNT 1
+ { "_cnt" },
+#define X_BUFFERSPACE 2
+ { "_bufspace" },
+#define X_DK_BUSY 3
+ { "_dk_busy" },
+#define X_DK_TIME 4
+ { "_dk_time" },
+#define X_DK_XFER 5
+ { "_dk_xfer" },
+#define X_DK_WDS 6
+ { "_dk_wds" },
+#define X_DK_SEEK 7
+ { "_dk_seek" },
+#define X_NCHSTATS 8
+ { "_nchstats" },
+#define X_INTRNAMES 9
+ { "_intrnames" },
+#define X_EINTRNAMES 10
+ { "_eintrnames" },
+#define X_INTRCNT 11
+ { "_intrcnt" },
+#define X_EINTRCNT 12
+ { "_eintrcnt" },
+ { "" },
+};
+
+/*
+ * These constants define where the major pieces are laid out
+ */
+#define STATROW 0 /* uses 1 row and 68 cols */
+#define STATCOL 2
+#define MEMROW 2 /* uses 4 rows and 31 cols */
+#define MEMCOL 0
+#define PAGEROW 2 /* uses 4 rows and 26 cols */
+#define PAGECOL 36
+#define INTSROW 2 /* uses all rows to bottom and 17 cols */
+#define INTSCOL 61
+#define PROCSROW 7 /* uses 2 rows and 20 cols */
+#define PROCSCOL 0
+#define GENSTATROW 7 /* uses 2 rows and 30 cols */
+#define GENSTATCOL 20
+#define VMSTATROW 6 /* uses 17 rows and 12 cols */
+#define VMSTATCOL 48
+#define GRAPHROW 10 /* uses 3 rows and 51 cols */
+#define GRAPHCOL 0
+#define NAMEIROW 14 /* uses 3 rows and 38 cols */
+#define NAMEICOL 0
+#define DISKROW 18 /* uses 5 rows and 50 cols (for 9 drives) */
+#define DISKCOL 0
+
+#define DRIVESPACE 9 /* max # for space */
+
+#if DK_NDRIVE > DRIVESPACE
+#define MAXDRIVES DRIVESPACE /* max # to display */
+#else
+#define MAXDRIVES DK_NDRIVE /* max # to display */
+#endif
+
+int
+initkre()
+{
+ char *intrnamebuf, *cp;
+ int i;
+ static int once = 0;
+
+ if (namelist[0].n_type == 0) {
+ if (kvm_nlist(kd, namelist)) {
+ nlisterr(namelist);
+ return(0);
+ }
+ if (namelist[0].n_type == 0) {
+ error("No namelist");
+ return(0);
+ }
+ }
+ if (! dkinit())
+ return(0);
+ if (dk_ndrive && !once) {
+#define allocate(e, t) \
+ s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
+ s1./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
+ s2./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \
+ z./**/e = (t *)calloc(dk_ndrive, sizeof (t));
+ allocate(dk_time, long);
+ allocate(dk_wds, long);
+ allocate(dk_seek, long);
+ allocate(dk_xfer, long);
+ once = 1;
+#undef allocate
+ }
+ if (nintr == 0) {
+ nintr = (namelist[X_EINTRCNT].n_value -
+ namelist[X_INTRCNT].n_value) / sizeof (long);
+ intrloc = calloc(nintr, sizeof (long));
+ intrname = calloc(nintr, sizeof (long));
+ intrnamebuf = malloc(namelist[X_EINTRNAMES].n_value -
+ namelist[X_INTRNAMES].n_value);
+ if (intrnamebuf == 0 || intrname == 0 || intrloc == 0) {
+ error("Out of memory\n");
+ if (intrnamebuf)
+ free(intrnamebuf);
+ if (intrname)
+ free(intrname);
+ if (intrloc)
+ free(intrloc);
+ nintr = 0;
+ return(0);
+ }
+ NREAD(X_INTRNAMES, intrnamebuf, NVAL(X_EINTRNAMES) -
+ NVAL(X_INTRNAMES));
+ for (cp = intrnamebuf, i = 0; i < nintr; i++) {
+ intrname[i] = cp;
+ cp += strlen(cp) + 1;
+ }
+ nextintsrow = INTSROW + 2;
+ allocinfo(&s);
+ allocinfo(&s1);
+ allocinfo(&s2);
+ allocinfo(&z);
+ }
+ getinfo(&s2, RUN);
+ copyinfo(&s2, &s1);
+ return(1);
+}
+
+void
+fetchkre()
+{
+ time_t now;
+
+ time(&now);
+ strcpy(buf, ctime(&now));
+ buf[16] = '\0';
+ getinfo(&s, state);
+}
+
+void
+labelkre()
+{
+ register int i, j;
+
+ clear();
+ mvprintw(STATROW, STATCOL + 4, "users Load");
+ mvprintw(MEMROW, MEMCOL, "Mem:KB REAL VIRTUAL");
+ mvprintw(MEMROW + 1, MEMCOL, " Tot Share Tot Share");
+ mvprintw(MEMROW + 2, MEMCOL, "Act");
+ mvprintw(MEMROW + 3, MEMCOL, "All");
+
+ mvprintw(MEMROW + 1, MEMCOL + 31, "Free");
+
+ mvprintw(PAGEROW, PAGECOL, " VN PAGER SWAP PAGER ");
+ mvprintw(PAGEROW + 1, PAGECOL, " in out in out ");
+ mvprintw(PAGEROW + 2, PAGECOL, "count");
+ mvprintw(PAGEROW + 3, PAGECOL, "pages");
+
+ mvprintw(INTSROW, INTSCOL + 3, " Interrupts");
+ mvprintw(INTSROW + 1, INTSCOL + 9, "total");
+
+ mvprintw(VMSTATROW + 0, VMSTATCOL + 10, "cow");
+ mvprintw(VMSTATROW + 1, VMSTATCOL + 10, "zfod");
+ mvprintw(VMSTATROW + 2, VMSTATCOL + 10, "wire");
+ mvprintw(VMSTATROW + 3, VMSTATCOL + 10, "act");
+ mvprintw(VMSTATROW + 4, VMSTATCOL + 10, "inact");
+ mvprintw(VMSTATROW + 5, VMSTATCOL + 10, "cache");
+ mvprintw(VMSTATROW + 6, VMSTATCOL + 10, "free");
+ mvprintw(VMSTATROW + 7, VMSTATCOL + 10, "daefr");
+ mvprintw(VMSTATROW + 8, VMSTATCOL + 10, "prcfr");
+ mvprintw(VMSTATROW + 9, VMSTATCOL + 10, "react");
+ mvprintw(VMSTATROW + 10, VMSTATCOL + 10, "pdwake");
+ mvprintw(VMSTATROW + 11, VMSTATCOL + 10, "pdpgs");
+ mvprintw(VMSTATROW + 12, VMSTATCOL + 10, "intrn");
+ mvprintw(VMSTATROW + 13, VMSTATCOL + 10, "buf");
+
+ mvprintw(GENSTATROW, GENSTATCOL, " Csw Trp Sys Int Sof Flt");
+
+ mvprintw(GRAPHROW, GRAPHCOL,
+ " . %%Sys . %%Intr . %%User . %%Nice . %%Idle");
+ mvprintw(PROCSROW, PROCSCOL, "Proc:r p d s w");
+ mvprintw(GRAPHROW + 1, GRAPHCOL,
+ "| | | | | | | | | | |");
+
+ mvprintw(NAMEIROW, NAMEICOL, "Namei Name-cache Proc-cache");
+ mvprintw(NAMEIROW + 1, NAMEICOL,
+ " Calls hits %% hits %%");
+ mvprintw(DISKROW, DISKCOL, "Discs");
+ mvprintw(DISKROW + 1, DISKCOL, "seeks");
+ mvprintw(DISKROW + 2, DISKCOL, "xfers");
+ mvprintw(DISKROW + 3, DISKCOL, " blks");
+ mvprintw(DISKROW + 4, DISKCOL, " msps");
+ j = 0;
+ for (i = 0; i < dk_ndrive && j < MAXDRIVES; i++)
+ if (dk_select[i]) {
+ mvprintw(DISKROW, DISKCOL + 5 + 5 * j,
+ " %3.3s", dr_name[j]);
+ j++;
+ }
+ for (i = 0; i < nintr; i++) {
+ if (intrloc[i] == 0)
+ continue;
+ mvprintw(intrloc[i], INTSCOL + 9, "%-10.10s", intrname[i]);
+ }
+}
+
+#define X(fld) {t=s.fld[i]; s.fld[i]-=s1.fld[i]; if(state==TIME) s1.fld[i]=t;}
+#define Y(fld) {t = s.fld; s.fld -= s1.fld; if(state == TIME) s1.fld = t;}
+#define Z(fld) {t = s.nchstats.fld; s.nchstats.fld -= s1.nchstats.fld; \
+ if(state == TIME) s1.nchstats.fld = t;}
+#define PUTRATE(fld, l, c, w) \
+ Y(fld); \
+ putint((int)((float)s.fld/etime + 0.5), l, c, w)
+#define MAXFAIL 5
+
+static char cpuchar[CPUSTATES] = { '=' , '+', '>', '-', ' ' };
+static char cpuorder[CPUSTATES] = { CP_SYS, CP_INTR, CP_USER, CP_NICE,
+ CP_IDLE };
+
+void
+showkre()
+{
+ float f1, f2;
+ int psiz, inttotal;
+ int i, l, c;
+ static int failcnt = 0;
+
+ for (i = 0; i < dk_ndrive; i++) {
+ X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time);
+ }
+ etime = 0;
+ for(i = 0; i < CPUSTATES; i++) {
+ X(time);
+ etime += s.time[i];
+ }
+ if (etime < 5.0) { /* < 5 ticks - ignore this trash */
+ if (failcnt++ >= MAXFAIL) {
+ clear();
+ mvprintw(2, 10, "The alternate system clock has died!");
+ mvprintw(3, 10, "Reverting to ``pigs'' display.");
+ move(CMDLINE, 0);
+ refresh();
+ failcnt = 0;
+ sleep(5);
+ command("pigs");
+ }
+ return;
+ }
+ failcnt = 0;
+ etime /= hertz;
+ inttotal = 0;
+ for (i = 0; i < nintr; i++) {
+ if (s.intrcnt[i] == 0)
+ continue;
+ if (intrloc[i] == 0) {
+ if (nextintsrow == LINES)
+ continue;
+ intrloc[i] = nextintsrow++;
+ mvprintw(intrloc[i], INTSCOL + 9, "%-10.10s",
+ intrname[i]);
+ }
+ X(intrcnt);
+ l = (int)((float)s.intrcnt[i]/etime + 0.5);
+ inttotal += l;
+ putint(l, intrloc[i], INTSCOL + 2, 6);
+ }
+ putint(inttotal, INTSROW + 1, INTSCOL + 2, 6);
+ Z(ncs_goodhits); Z(ncs_badhits); Z(ncs_miss);
+ Z(ncs_long); Z(ncs_pass2); Z(ncs_2passes); Z(ncs_neghits);
+ s.nchcount = nchtotal.ncs_goodhits + nchtotal.ncs_badhits +
+ nchtotal.ncs_miss + nchtotal.ncs_long + nchtotal.ncs_neghits;
+ if (state == TIME)
+ s1.nchcount = s.nchcount;
+
+ psiz = 0;
+ f2 = 0.0;
+ for (c = 0; c < CPUSTATES; c++) {
+ i = cpuorder[c];
+ f1 = cputime(i);
+ f2 += f1;
+ l = (int) ((f2 + 1.0) / 2.0) - psiz;
+ if (f1 > 99.9)
+ f1 = 99.9; /* no room to display 100.0 */
+ putfloat(f1, GRAPHROW, GRAPHCOL + 10 * c, 4, 1, 0);
+ move(GRAPHROW + 2, psiz);
+ psiz += l;
+ while (l-- > 0)
+ addch(cpuchar[c]);
+ }
+
+ putint(ucount(), STATROW, STATCOL, 3);
+ putfloat(avenrun[0], STATROW, STATCOL + 17, 6, 2, 0);
+ putfloat(avenrun[1], STATROW, STATCOL + 23, 6, 2, 0);
+ putfloat(avenrun[2], STATROW, STATCOL + 29, 6, 2, 0);
+ mvaddstr(STATROW, STATCOL + 53, buf);
+#define pgtokb(pg) ((pg) * cnt.v_page_size / 1024)
+ putint(pgtokb(total.t_arm), MEMROW + 2, MEMCOL + 3, 6);
+ putint(pgtokb(total.t_armshr), MEMROW + 2, MEMCOL + 9, 6);
+ putint(pgtokb(total.t_avm), MEMROW + 2, MEMCOL + 15, 7);
+ putint(pgtokb(total.t_avmshr), MEMROW + 2, MEMCOL + 22, 7);
+ putint(pgtokb(total.t_rm), MEMROW + 3, MEMCOL + 3, 6);
+ putint(pgtokb(total.t_rmshr), MEMROW + 3, MEMCOL + 9, 6);
+ putint(pgtokb(total.t_vm), MEMROW + 3, MEMCOL + 15, 7);
+ putint(pgtokb(total.t_vmshr), MEMROW + 3, MEMCOL + 22, 7);
+ putint(pgtokb(total.t_free), MEMROW + 2, MEMCOL + 29, 6);
+ putint(total.t_rq - 1, PROCSROW + 1, PROCSCOL + 3, 3);
+ putint(total.t_pw, PROCSROW + 1, PROCSCOL + 6, 3);
+ putint(total.t_dw, PROCSROW + 1, PROCSCOL + 9, 3);
+ putint(total.t_sl, PROCSROW + 1, PROCSCOL + 12, 3);
+ putint(total.t_sw, PROCSROW + 1, PROCSCOL + 15, 3);
+ PUTRATE(Cnt.v_cow_faults, VMSTATROW + 0, VMSTATCOL + 3, 6);
+ PUTRATE(Cnt.v_zfod, VMSTATROW + 1, VMSTATCOL + 4, 5);
+ putint(pgtokb(cnt.v_wire_count), VMSTATROW + 2, VMSTATCOL, 9);
+ putint(pgtokb(cnt.v_active_count), VMSTATROW + 3, VMSTATCOL, 9);
+ putint(pgtokb(cnt.v_inactive_count), VMSTATROW + 4, VMSTATCOL, 9);
+ putint(pgtokb(cnt.v_cache_count), VMSTATROW + 5, VMSTATCOL, 9);
+ putint(pgtokb(cnt.v_free_count), VMSTATROW + 6, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_dfree, VMSTATROW + 7, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_pfree, VMSTATROW + 8, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_reactivated, VMSTATROW + 9, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_pdwakeups, VMSTATROW + 10, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_pdpages, VMSTATROW + 11, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_intrans, VMSTATROW + 12, VMSTATCOL, 9);
+ putint(s.bufspace/1024, VMSTATROW + 13, VMSTATCOL, 9);
+ PUTRATE(Cnt.v_vnodein, PAGEROW + 2, PAGECOL + 5, 5);
+ PUTRATE(Cnt.v_vnodeout, PAGEROW + 2, PAGECOL + 10, 5);
+ PUTRATE(Cnt.v_swapin, PAGEROW + 2, PAGECOL + 17, 5);
+ PUTRATE(Cnt.v_swapout, PAGEROW + 2, PAGECOL + 22, 5);
+ PUTRATE(Cnt.v_vnodepgsin, PAGEROW + 3, PAGECOL + 5, 5);
+ PUTRATE(Cnt.v_vnodepgsout, PAGEROW + 3, PAGECOL + 10, 5);
+ PUTRATE(Cnt.v_swappgsin, PAGEROW + 3, PAGECOL + 17, 5);
+ PUTRATE(Cnt.v_swappgsout, PAGEROW + 3, PAGECOL + 22, 5);
+ PUTRATE(Cnt.v_swtch, GENSTATROW + 1, GENSTATCOL, 5);
+ PUTRATE(Cnt.v_trap, GENSTATROW + 1, GENSTATCOL + 5, 5);
+ PUTRATE(Cnt.v_syscall, GENSTATROW + 1, GENSTATCOL + 10, 5);
+ PUTRATE(Cnt.v_intr, GENSTATROW + 1, GENSTATCOL + 15, 5);
+ PUTRATE(Cnt.v_soft, GENSTATROW + 1, GENSTATCOL + 20, 5);
+ PUTRATE(Cnt.v_vm_faults, GENSTATROW + 1, GENSTATCOL + 25, 5);
+ mvprintw(DISKROW, DISKCOL + 5, " ");
+ for (i = 0, c = 0; i < dk_ndrive && c < MAXDRIVES; i++)
+ if (dk_select[i]) {
+ mvprintw(DISKROW, DISKCOL + 5 + 5 * c,
+ " %3.3s", dr_name[i]);
+ dinfo(i, ++c);
+ }
+ putint(s.nchcount, NAMEIROW + 2, NAMEICOL, 9);
+ putint((nchtotal.ncs_goodhits + nchtotal.ncs_neghits),
+ NAMEIROW + 2, NAMEICOL + 9, 9);
+#define nz(x) ((x) ? (x) : 1)
+ putfloat((nchtotal.ncs_goodhits+nchtotal.ncs_neghits) *
+ 100.0 / nz(s.nchcount),
+ NAMEIROW + 2, NAMEICOL + 19, 4, 0, 1);
+ putint(nchtotal.ncs_pass2, NAMEIROW + 2, NAMEICOL + 23, 9);
+ putfloat(nchtotal.ncs_pass2 * 100.0 / nz(s.nchcount),
+ NAMEIROW + 2, NAMEICOL + 34, 4, 0, 1);
+#undef nz
+}
+
+int
+cmdkre(cmd, args)
+ char *cmd, *args;
+{
+
+ if (prefix(cmd, "run")) {
+ copyinfo(&s2, &s1);
+ state = RUN;
+ return (1);
+ }
+ if (prefix(cmd, "boot")) {
+ state = BOOT;
+ copyinfo(&z, &s1);
+ return (1);
+ }
+ if (prefix(cmd, "time")) {
+ state = TIME;
+ return (1);
+ }
+ if (prefix(cmd, "zero")) {
+ if (state == RUN)
+ getinfo(&s1, RUN);
+ return (1);
+ }
+ return (dkcmd(cmd, args));
+}
+
+/* calculate number of users on the system */
+static int
+ucount()
+{
+ register int nusers = 0;
+
+ if (ut < 0)
+ return (0);
+ while (read(ut, &utmp, sizeof(utmp)))
+ if (utmp.ut_name[0] != '\0')
+ nusers++;
+
+ lseek(ut, 0L, L_SET);
+ return (nusers);
+}
+
+static float
+cputime(indx)
+ int indx;
+{
+ double t;
+ register int i;
+
+ t = 0;
+ for (i = 0; i < CPUSTATES; i++)
+ t += s.time[i];
+ if (t == 0.0)
+ t = 1.0;
+ return (s.time[indx] * 100.0 / t);
+}
+
+static void
+putint(n, l, c, w)
+ int n, l, c, w;
+{
+ char b[128];
+
+ move(l, c);
+ if (n == 0) {
+ while (w-- > 0)
+ addch(' ');
+ return;
+ }
+ sprintf(b, "%*d", w, n);
+ if (strlen(b) > w) {
+ while (w-- > 0)
+ addch('*');
+ return;
+ }
+ addstr(b);
+}
+
+static void
+putfloat(f, l, c, w, d, nz)
+ double f;
+ int l, c, w, d, nz;
+{
+ char b[128];
+
+ move(l, c);
+ if (nz && f == 0.0) {
+ while (--w >= 0)
+ addch(' ');
+ return;
+ }
+ sprintf(b, "%*.*f", w, d, f);
+ if (strlen(b) > w) {
+ while (--w >= 0)
+ addch('*');
+ return;
+ }
+ addstr(b);
+}
+
+static void
+getinfo(s, st)
+ struct Info *s;
+ enum state st;
+{
+ int mib[2], size;
+ extern int errno;
+
+ NREAD(X_CPTIME, s->time, sizeof s->time);
+ NREAD(X_CNT, &s->Cnt, sizeof s->Cnt);
+ NREAD(X_BUFFERSPACE, &s->bufspace, LONG);
+ NREAD(X_DK_BUSY, &s->dk_busy, LONG);
+ NREAD(X_DK_TIME, s->dk_time, dk_ndrive * LONG);
+ NREAD(X_DK_XFER, s->dk_xfer, dk_ndrive * LONG);
+ NREAD(X_DK_WDS, s->dk_wds, dk_ndrive * LONG);
+ NREAD(X_DK_SEEK, s->dk_seek, dk_ndrive * LONG);
+ NREAD(X_NCHSTATS, &s->nchstats, sizeof s->nchstats);
+ NREAD(X_INTRCNT, s->intrcnt, nintr * LONG);
+ size = sizeof(s->Total);
+ mib[0] = CTL_VM;
+ mib[1] = VM_METER;
+ if (sysctl(mib, 2, &s->Total, &size, NULL, 0) < 0) {
+ error("Can't get kernel info: %s\n", strerror(errno));
+ bzero(&s->Total, sizeof(s->Total));
+ }
+}
+
+static void
+allocinfo(s)
+ struct Info *s;
+{
+
+ s->intrcnt = (long *) malloc(nintr * sizeof(long));
+ if (s->intrcnt == NULL) {
+ fprintf(stderr, "systat: out of memory\n");
+ exit(2);
+ }
+}
+
+static void
+copyinfo(from, to)
+ register struct Info *from, *to;
+{
+ long *time, *wds, *seek, *xfer;
+ long *intrcnt;
+
+ /*
+ * time, wds, seek, and xfer are malloc'd so we have to
+ * save the pointers before the structure copy and then
+ * copy by hand.
+ */
+ time = to->dk_time; wds = to->dk_wds; seek = to->dk_seek;
+ xfer = to->dk_xfer; intrcnt = to->intrcnt;
+ *to = *from;
+ bcopy(from->dk_time, to->dk_time = time, dk_ndrive * sizeof (long));
+ bcopy(from->dk_wds, to->dk_wds = wds, dk_ndrive * sizeof (long));
+ bcopy(from->dk_seek, to->dk_seek = seek, dk_ndrive * sizeof (long));
+ bcopy(from->dk_xfer, to->dk_xfer = xfer, dk_ndrive * sizeof (long));
+ bcopy(from->intrcnt, to->intrcnt = intrcnt, nintr * sizeof (int));
+}
+
+static void
+dinfo(dn, c)
+ int dn, c;
+{
+ double words, atime, itime, xtime;
+
+ c = DISKCOL + c * 5;
+ atime = s.dk_time[dn];
+ atime /= hertz;
+ words = s.dk_wds[dn]*32.0; /* number of words transferred */
+ xtime = dk_mspw[dn]*words; /* transfer time */
+ itime = atime - xtime; /* time not transferring */
+ if (xtime < 0)
+ itime += xtime, xtime = 0;
+ if (itime < 0)
+ xtime += itime, itime = 0;
+ putint((int)((float)s.dk_seek[dn]/etime+0.5), DISKROW + 1, c, 5);
+ putint((int)((float)s.dk_xfer[dn]/etime+0.5), DISKROW + 2, c, 5);
+ putint((int)(words/etime/512.0 + 0.5), DISKROW + 3, c, 5);
+ if (s.dk_seek[dn])
+ putfloat(itime*1000.0/s.dk_seek[dn], DISKROW + 4, c, 5, 1, 1);
+ else
+ putint(0, DISKROW + 4, c, 5);
+}
diff --git a/usr.bin/tail/Makefile b/usr.bin/tail/Makefile
new file mode 100644
index 0000000..de1c3c9
--- /dev/null
+++ b/usr.bin/tail/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tail
+SRCS= forward.c misc.c read.c reverse.c tail.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h
new file mode 100644
index 0000000..65efc14
--- /dev/null
+++ b/usr.bin/tail/extern.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define WR(p, size) \
+ if (write(STDOUT_FILENO, p, size) != size) \
+ oerr();
+
+enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
+
+void forward __P((FILE *, enum STYLE, long, struct stat *));
+void reverse __P((FILE *, enum STYLE, long, struct stat *));
+
+void bytes __P((FILE *, off_t));
+void lines __P((FILE *, off_t));
+
+void err __P((int fatal, const char *fmt, ...));
+void ierr __P((void));
+void oerr __P((void));
+
+extern int fflag, rflag, rval;
+extern char *fname;
diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c
new file mode 100644
index 0000000..8e32cc4
--- /dev/null
+++ b/usr.bin/tail/forward.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+static void rlines __P((FILE *, long, struct stat *));
+
+/*
+ * forward -- display the file, from an offset, forward.
+ *
+ * There are eight separate cases for this -- regular and non-regular
+ * files, by bytes or lines and from the beginning or end of the file.
+ *
+ * FBYTES byte offset from the beginning of the file
+ * REG seek
+ * NOREG read, counting bytes
+ *
+ * FLINES line offset from the beginning of the file
+ * REG read, counting lines
+ * NOREG read, counting lines
+ *
+ * RBYTES byte offset from the end of the file
+ * REG seek
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * RLINES
+ * REG mmap the file and step back until reach the correct offset.
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ */
+void
+forward(fp, style, off, sbp)
+ FILE *fp;
+ enum STYLE style;
+ long off;
+ struct stat *sbp;
+{
+ register int ch;
+ struct timeval second;
+ fd_set zero;
+
+ switch(style) {
+ case FBYTES:
+ if (off == 0)
+ break;
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size < off)
+ off = sbp->st_size;
+ if (fseek(fp, off, SEEK_SET) == -1) {
+ ierr();
+ return;
+ }
+ } else while (off--)
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ break;
+ case FLINES:
+ if (off == 0)
+ break;
+ for (;;) {
+ if ((ch = getc(fp)) == EOF) {
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ break;
+ }
+ if (ch == '\n' && !--off)
+ break;
+ }
+ break;
+ case RBYTES:
+ if (S_ISREG(sbp->st_mode)) {
+ if (sbp->st_size >= off &&
+ fseek(fp, -off, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else
+ bytes(fp, off);
+ break;
+ case RLINES:
+ if (S_ISREG(sbp->st_mode))
+ if (!off) {
+ if (fseek(fp, 0L, SEEK_END) == -1) {
+ ierr();
+ return;
+ }
+ } else
+ rlines(fp, off, sbp);
+ else if (off == 0) {
+ while (getc(fp) != EOF);
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ } else
+ lines(fp, off);
+ break;
+ }
+
+ /*
+ * We pause for one second after displaying any data that has
+ * accumulated since we read the file.
+ */
+ if (fflag) {
+ FD_ZERO(&zero);
+ second.tv_sec = 1;
+ second.tv_usec = 0;
+ }
+
+ for (;;) {
+ while ((ch = getc(fp)) != EOF)
+ if (putchar(ch) == EOF)
+ oerr();
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ (void)fflush(stdout);
+ if (!fflag)
+ break;
+ /* Sleep(3) is eight system calls. Do it fast. */
+ if (select(0, &zero, &zero, &zero, &second) == -1)
+ err(1, "select: %s", strerror(errno));
+ clearerr(fp);
+ }
+}
+
+/*
+ * rlines -- display the last offset lines of the file.
+ */
+static void
+rlines(fp, off, sbp)
+ FILE *fp;
+ long off;
+ struct stat *sbp;
+{
+ register off_t size;
+ register char *p;
+ char *start;
+
+ if (!(size = sbp->st_size))
+ return;
+
+ if (size > SIZE_T_MAX) {
+ err(0, "%s: %s", fname, strerror(EFBIG));
+ return;
+ }
+
+ if ((start = mmap(NULL, (size_t)size,
+ PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) {
+ err(0, "%s: %s", fname, strerror(EFBIG));
+ return;
+ }
+
+ /* Last char is special, ignore whether newline or not. */
+ for (p = start + size - 1; --size;)
+ if (*--p == '\n' && !--off) {
+ ++p;
+ break;
+ }
+
+ /* Set the file pointer to reflect the length displayed. */
+ size = sbp->st_size - size;
+ WR(p, size);
+ if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
+ ierr();
+ return;
+ }
+ if (munmap(start, (size_t)sbp->st_size)) {
+ err(0, "%s: %s", fname, strerror(errno));
+ return;
+ }
+}
diff --git a/usr.bin/tail/misc.c b/usr.bin/tail/misc.c
new file mode 100644
index 0000000..18fd63d
--- /dev/null
+++ b/usr.bin/tail/misc.c
@@ -0,0 +1,91 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+void
+ierr()
+{
+ err(0, "%s: %s", fname, strerror(errno));
+}
+
+void
+oerr()
+{
+ err(1, "stdout: %s", strerror(errno));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int fatal, const char *fmt, ...)
+#else
+err(fatal, fmt, va_alist)
+ int fatal;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "tail: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (fatal)
+ exit(1);
+ rval = 1;
+}
diff --git a/usr.bin/tail/read.c b/usr.bin/tail/read.c
new file mode 100644
index 0000000..ad29dbd
--- /dev/null
+++ b/usr.bin/tail/read.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+/*
+ * bytes -- read bytes to an offset from the end and display.
+ *
+ * This is the function that reads to a byte offset from the end of the input,
+ * storing the data in a wrap-around buffer which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the character closest to the beginning of the input to
+ * the end.
+ */
+void
+bytes(fp, off)
+ register FILE *fp;
+ off_t off;
+{
+ register int ch, len, tlen;
+ register char *ep, *p, *t;
+ int wrap;
+ char *sp;
+
+ if ((sp = p = malloc(off)) == NULL)
+ err(1, "%s", strerror(errno));
+
+ for (wrap = 0, ep = p + off; (ch = getc(fp)) != EOF;) {
+ *p = ch;
+ if (++p == ep) {
+ wrap = 1;
+ p = sp;
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+
+ if (rflag) {
+ for (t = p - 1, len = 0; t >= sp; --t, ++len)
+ if (*t == '\n' && len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (wrap) {
+ tlen = len;
+ for (t = ep - 1, len = 0; t >= p; --t, ++len)
+ if (*t == '\n') {
+ if (len) {
+ WR(t + 1, len);
+ len = 0;
+ }
+ if (tlen) {
+ WR(sp, tlen);
+ tlen = 0;
+ }
+ }
+ if (len)
+ WR(t + 1, len);
+ if (tlen)
+ WR(sp, tlen);
+ }
+ } else {
+ if (wrap && (len = ep - p))
+ WR(p, len);
+ if (len = p - sp)
+ WR(sp, len);
+ }
+}
+
+/*
+ * lines -- read lines to an offset from the end and display.
+ *
+ * This is the function that reads to a line offset from the end of the input,
+ * storing the data in an array of buffers which is then displayed. If the
+ * rflag is set, the data is displayed in lines in reverse order, and this
+ * routine has the usual nastiness of trying to find the newlines. Otherwise,
+ * it is displayed from the line closest to the beginning of the input to
+ * the end.
+ */
+void
+lines(fp, off)
+ register FILE *fp;
+ off_t off;
+{
+ struct {
+ u_int blen;
+ u_int len;
+ char *l;
+ } *lines;
+ register int ch;
+ register char *p;
+ int blen, cnt, recno, wrap;
+ char *sp;
+
+ if ((lines = malloc(off * sizeof(*lines))) == NULL)
+ err(1, "%s", strerror(errno));
+ bzero(lines, off * sizeof(*lines));
+ sp = NULL;
+ blen = cnt = recno = wrap = 0;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (++cnt > blen) {
+ if ((sp = realloc(sp, blen += 1024)) == NULL)
+ err(1, "%s", strerror(errno));
+ p = sp + cnt - 1;
+ }
+ *p++ = ch;
+ if (ch == '\n') {
+ if (lines[recno].blen < cnt) {
+ lines[recno].blen = cnt + 256;
+ if ((lines[recno].l = realloc(lines[recno].l,
+ lines[recno].blen)) == NULL)
+ err(1, "%s", strerror(errno));
+ }
+ bcopy(sp, lines[recno].l, lines[recno].len = cnt);
+ cnt = 0;
+ p = sp;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+ }
+ if (ferror(fp)) {
+ ierr();
+ return;
+ }
+ if (cnt) {
+ lines[recno].l = sp;
+ lines[recno].len = cnt;
+ if (++recno == off) {
+ wrap = 1;
+ recno = 0;
+ }
+ }
+
+ if (rflag) {
+ for (cnt = recno - 1; cnt >= 0; --cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ if (wrap)
+ for (cnt = off - 1; cnt >= recno; --cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ } else {
+ if (wrap)
+ for (cnt = recno; cnt < off; ++cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ for (cnt = 0; cnt < recno; ++cnt)
+ WR(lines[cnt].l, lines[cnt].len);
+ }
+}
diff --git a/usr.bin/tail/reverse.c b/usr.bin/tail/reverse.c
new file mode 100644
index 0000000..34cd980
--- /dev/null
+++ b/usr.bin/tail/reverse.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)reverse.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+static void r_buf __P((FILE *));
+static void r_reg __P((FILE *, enum STYLE, long, struct stat *));
+
+/*
+ * reverse -- display input in reverse order by line.
+ *
+ * There are six separate cases for this -- regular and non-regular
+ * files by bytes, lines or the whole file.
+ *
+ * BYTES display N bytes
+ * REG mmap the file and display the lines
+ * NOREG cyclically read characters into a wrap-around buffer
+ *
+ * LINES display N lines
+ * REG mmap the file and display the lines
+ * NOREG cyclically read lines into a wrap-around array of buffers
+ *
+ * FILE display the entire file
+ * REG mmap the file and display the lines
+ * NOREG cyclically read input into a linked list of buffers
+ */
+void
+reverse(fp, style, off, sbp)
+ FILE *fp;
+ enum STYLE style;
+ long off;
+ struct stat *sbp;
+{
+ if (style != REVERSE && off == 0)
+ return;
+
+ if (S_ISREG(sbp->st_mode))
+ r_reg(fp, style, off, sbp);
+ else
+ switch(style) {
+ case FBYTES:
+ case RBYTES:
+ bytes(fp, off);
+ break;
+ case FLINES:
+ case RLINES:
+ lines(fp, off);
+ break;
+ case REVERSE:
+ r_buf(fp);
+ break;
+ }
+}
+
+/*
+ * r_reg -- display a regular file in reverse order by line.
+ */
+static void
+r_reg(fp, style, off, sbp)
+ FILE *fp;
+ register enum STYLE style;
+ long off;
+ struct stat *sbp;
+{
+ register off_t size;
+ register int llen;
+ register char *p;
+ char *start;
+
+ if (!(size = sbp->st_size))
+ return;
+
+ if (size > SIZE_T_MAX) {
+ err(0, "%s: %s", fname, strerror(EFBIG));
+ return;
+ }
+
+ if ((start = mmap(NULL, (size_t)size,
+ PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) {
+ err(0, "%s: %s", fname, strerror(EFBIG));
+ return;
+ }
+ p = start + size - 1;
+
+ if (style == RBYTES && off < size)
+ size = off;
+
+ /* Last char is special, ignore whether newline or not. */
+ for (llen = 1; --size; ++llen)
+ if (*--p == '\n') {
+ WR(p + 1, llen);
+ llen = 0;
+ if (style == RLINES && !--off) {
+ ++p;
+ break;
+ }
+ }
+ if (llen)
+ WR(p, llen);
+ if (munmap(start, (size_t)sbp->st_size))
+ err(0, "%s: %s", fname, strerror(errno));
+}
+
+typedef struct bf {
+ struct bf *next;
+ struct bf *prev;
+ int len;
+ char *l;
+} BF;
+
+/*
+ * r_buf -- display a non-regular file in reverse order by line.
+ *
+ * This is the function that saves the entire input, storing the data in a
+ * doubly linked list of buffers and then displays them in reverse order.
+ * It has the usual nastiness of trying to find the newlines, as there's no
+ * guarantee that a newline occurs anywhere in the file, let alone in any
+ * particular buffer. If we run out of memory, input is discarded (and the
+ * user warned).
+ */
+static void
+r_buf(fp)
+ FILE *fp;
+{
+ register BF *mark, *tl, *tr;
+ register int ch, len, llen;
+ register char *p;
+ off_t enomem;
+
+#define BSZ (128 * 1024)
+ for (mark = NULL, enomem = 0;;) {
+ /*
+ * Allocate a new block and link it into place in a doubly
+ * linked list. If out of memory, toss the LRU block and
+ * keep going.
+ */
+ if (enomem || (tl = malloc(sizeof(BF))) == NULL ||
+ (tl->l = malloc(BSZ)) == NULL) {
+ if (!mark)
+ err(1, "%s", strerror(errno));
+ tl = enomem ? tl->next : mark;
+ enomem += tl->len;
+ } else if (mark) {
+ tl->next = mark;
+ tl->prev = mark->prev;
+ mark->prev->next = tl;
+ mark->prev = tl;
+ } else
+ mark->next = mark->prev = (mark = tl);
+
+ /* Fill the block with input data. */
+ for (p = tl->l, len = 0;
+ len < BSZ && (ch = getc(fp)) != EOF; ++len)
+ *p++ = ch;
+
+ /*
+ * If no input data for this block and we tossed some data,
+ * recover it.
+ */
+ if (!len) {
+ if (enomem)
+ enomem -= tl->len;
+ tl = tl->prev;
+ break;
+ }
+
+ tl->len = len;
+ if (ch == EOF)
+ break;
+ }
+
+ if (enomem) {
+ (void)fprintf(stderr,
+ "tail: warning: %ld bytes discarded\n", enomem);
+ rval = 1;
+ }
+
+ /*
+ * Step through the blocks in the reverse order read. The last char
+ * is special, ignore whether newline or not.
+ */
+ for (mark = tl;;) {
+ for (p = tl->l + (len = tl->len) - 1, llen = 0; len--;
+ --p, ++llen)
+ if (*p == '\n') {
+ if (llen) {
+ WR(p + 1, llen);
+ llen = 0;
+ }
+ if (tl == mark)
+ continue;
+ for (tr = tl->next; tr->len; tr = tr->next) {
+ WR(tr->l, tr->len);
+ tr->len = 0;
+ if (tr == mark)
+ break;
+ }
+ }
+ tl->len = llen;
+ if ((tl = tl->prev) == mark)
+ break;
+ }
+ tl = tl->next;
+ if (tl->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+ while ((tl = tl->next)->len) {
+ WR(tl->l, tl->len);
+ tl->len = 0;
+ }
+}
diff --git a/usr.bin/tail/tail.1 b/usr.bin/tail/tail.1
new file mode 100644
index 0000000..02bfba8
--- /dev/null
+++ b/usr.bin/tail/tail.1
@@ -0,0 +1,165 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tail.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TAIL 1
+.Os BSD 4
+.Sh NAME
+.Nm tail
+.Nd display the last part of a file
+.Sh SYNOPSIS
+.Nm tail
+.Op Fl f Li | Fl r
+.Oo
+.Fl b Ar number |
+.Fl c Ar number |
+.Fl n Ar number
+.Oc
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm tail
+utility displays the contents of
+.Ar file
+or, by default, its standard input, to the standard output.
+.Pp
+The display begins at a byte, line or 512-byte block location in the
+input.
+Numbers having a leading plus (``+'') sign are relative to the beginning
+of the input, for example,
+.Dq -c +2
+starts the display at the second
+byte of the input.
+Numbers having a leading minus (``-'') sign or no explicit sign are
+relative to the end of the input, for example,
+.Dq -n 2
+displays the last two lines of the input.
+The default starting location is
+.Dq -n 10 ,
+or the last 10 lines of the input.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar number
+The location is
+.Ar number
+512-byte blocks.
+.It Fl c Ar number
+The location is
+.Ar number
+bytes.
+.It Fl f
+The
+.Fl f
+option causes
+.Nm tail
+to not stop when end of file is reached, but rather to wait for additional
+data to be appended to the input.
+The
+.Fl f
+option is ignored if the standard input is a pipe, but not if it is a FIFO.
+.It Fl n Ar number
+The location is
+.Ar number
+lines.
+.It Fl r
+The
+.Fl r
+option causes the input to be displayed in reverse order, by line.
+Additionally, this option changes the meaning of the
+.Fl b ,
+.Fl c
+and
+.Fl n
+options.
+When the
+.Fl r
+option is specified, these options specify the number of bytes, lines
+or 512-byte blocks to display, instead of the bytes, lines or blocks
+from the beginning or end of the input from which to begin the display.
+The default for the
+.Fl r
+option is to display all of the input.
+.El
+.Pp
+If more than a single file is specified, each file is preceded by a
+header consisting of the string
+.Dq ==> XXX <==
+where
+.Dq XXX
+is the name of the file.
+.Pp
+The
+.Nm tail
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr cat 1 ,
+.Xr head 1 ,
+.Xr sed 1
+.Sh STANDARDS
+The
+.Nm tail
+utility is expected to be a superset of the POSIX 1003.2
+specification.
+In particular, the
+.Fl b
+and
+.Fl r
+options are extensions to that standard.
+.Pp
+The historic command line syntax of
+.Nm tail
+is supported by this implementation.
+The only difference between this implementation and historic versions
+of
+.Nm tail ,
+once the command line syntax translation has been done, is that the
+.Fl b ,
+.Fl c
+and
+.Fl n
+options modify the
+.Fl r
+option, i.e. ``-r -c 4'' displays the last 4 characters of the last line
+of the input, while the historic tail (using the historic syntax ``-4cr'')
+would ignore the
+.Fl c
+option and display the last 4 lines of the input.
+.Sh HISTORY
+A
+.Nm tail
+command appeared in
+.At v7 .
diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c
new file mode 100644
index 0000000..cc8ea14
--- /dev/null
+++ b/usr.bin/tail/tail.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Sze-Tyan Wang.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+int fflag, rflag, rval;
+char *fname;
+
+static void obsolete __P((char **));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ FILE *fp;
+ long off;
+ enum STYLE style;
+ int ch, first;
+ char *p;
+
+ /*
+ * Tail's options are weird. First, -n10 is the same as -n-10, not
+ * -n+10. Second, the number options are 1 based and not offsets,
+ * so -n+1 is the first line, and -c-1 is the last byte. Third, the
+ * number options for the -r option specify the number of things that
+ * get displayed, not the starting point in the file. The one major
+ * incompatibility in this version as compared to historical versions
+ * is that the 'r' option couldn't be modified by the -lbc options,
+ * i.e. it was always done in lines. This version treats -rc as a
+ * number of characters in reverse order. Finally, the default for
+ * -r is the entire file, not 10 lines.
+ */
+#define ARG(units, forward, backward) { \
+ if (style) \
+ usage(); \
+ off = strtol(optarg, &p, 10) * (units); \
+ if (*p) \
+ err(1, "illegal offset -- %s", optarg); \
+ switch(optarg[0]) { \
+ case '+': \
+ if (off) \
+ off -= (units); \
+ style = (forward); \
+ break; \
+ case '-': \
+ off = -off; \
+ /* FALLTHROUGH */ \
+ default: \
+ style = (backward); \
+ break; \
+ } \
+}
+
+ obsolete(argv);
+ style = NOTSET;
+ while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF)
+ switch(ch) {
+ case 'b':
+ ARG(512, FBYTES, RBYTES);
+ break;
+ case 'c':
+ ARG(1, FBYTES, RBYTES);
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'n':
+ ARG(1, FLINES, RLINES);
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (fflag && argc > 1)
+ err(1, "-f option only appropriate for a single file");
+
+ /*
+ * If displaying in reverse, don't permit follow option, and convert
+ * style values.
+ */
+ if (rflag) {
+ if (fflag)
+ usage();
+ if (style == FBYTES)
+ style = RBYTES;
+ else if (style == FLINES)
+ style = RLINES;
+ }
+
+ /*
+ * If style not specified, the default is the whole file for -r, and
+ * the last 10 lines if not -r.
+ */
+ if (style == NOTSET)
+ if (rflag) {
+ off = 0;
+ style = REVERSE;
+ } else {
+ off = 10;
+ style = RLINES;
+ }
+
+ if (*argv)
+ for (first = 1; fname = *argv++;) {
+ if ((fp = fopen(fname, "r")) == NULL ||
+ fstat(fileno(fp), &sb)) {
+ ierr();
+ continue;
+ }
+ if (argc > 1) {
+ (void)printf("%s==> %s <==\n",
+ first ? "" : "\n", fname);
+ first = 0;
+ (void)fflush(stdout);
+ }
+
+ if (rflag)
+ reverse(fp, style, off, &sb);
+ else
+ forward(fp, style, off, &sb);
+ (void)fclose(fp);
+ }
+ else {
+ fname = "stdin";
+
+ if (fstat(fileno(stdin), &sb)) {
+ ierr();
+ exit(1);
+ }
+
+ /*
+ * Determine if input is a pipe. 4.4BSD will set the SOCKET
+ * bit in the st_mode field for pipes. Fix this then.
+ */
+ if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
+ errno == ESPIPE) {
+ errno = 0;
+ fflag = 0; /* POSIX.2 requires this. */
+ }
+
+ if (rflag)
+ reverse(stdin, style, off, &sb);
+ else
+ forward(stdin, style, off, &sb);
+ }
+ exit(rval);
+}
+
+/*
+ * Convert the obsolete argument form into something that getopt can handle.
+ * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
+ * the option argument for a -b, -c or -n option gets converted.
+ */
+static void
+obsolete(argv)
+ char *argv[];
+{
+ register char *ap, *p, *t;
+ int len;
+ char *start;
+
+ while (ap = *++argv) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+
+ switch(*++ap) {
+ /* Old-style option. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+
+ /* Malloc space for dash, new option and argument. */
+ len = strlen(*argv);
+ if ((start = p = malloc(len + 3)) == NULL)
+ err(1, "%s", strerror(errno));
+ *p++ = '-';
+
+ /*
+ * Go to the end of the option argument. Save off any
+ * trailing options (-3lf) and translate any trailing
+ * output style characters.
+ */
+ t = *argv + len - 1;
+ if (*t == 'f' || *t == 'r') {
+ *p++ = *t;
+ *t-- = '\0';
+ }
+ switch(*t) {
+ case 'b':
+ *p++ = 'b';
+ *t = '\0';
+ break;
+ case 'c':
+ *p++ = 'c';
+ *t = '\0';
+ break;
+ case 'l':
+ *t = '\0';
+ /* FALLTHROUGH */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *p++ = 'n';
+ break;
+ default:
+ err(1, "illegal option -- %s", *argv);
+ }
+ *p++ = *argv[0];
+ (void)strcpy(p, ap);
+ *argv = start;
+ continue;
+
+ /*
+ * Options w/ arguments, skip the argument and continue
+ * with the next option.
+ */
+ case 'b':
+ case 'c':
+ case 'n':
+ if (!ap[1])
+ ++argv;
+ /* FALLTHROUGH */
+ /* Options w/o arguments, continue with the next option. */
+ case 'f':
+ case 'r':
+ continue;
+
+ /* Illegal option, return and let getopt handle it. */
+ default:
+ return;
+ }
+ }
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/talk/Makefile b/usr.bin/talk/Makefile
new file mode 100644
index 0000000..9643a37
--- /dev/null
+++ b/usr.bin/talk/Makefile
@@ -0,0 +1,10 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= talk
+DPADD= ${LIBCURSES} ${LIBTERMCAP}
+LDADD= -lcurses -ltermcap
+SRCS= ctl.c ctl_transact.c display.c get_addrs.c get_names.c \
+ init_disp.c invite.c io.c look_up.c msgs.c talk.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/talk/ctl.c b/usr.bin/talk/ctl.c
new file mode 100644
index 0000000..18b0c5c
--- /dev/null
+++ b/usr.bin/talk/ctl.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ctl.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This file handles haggling with the various talk daemons to
+ * get a socket to talk to. sockt is opened and connected in
+ * the progress
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <netinet/in.h>
+#include "talk.h"
+#include "talk_ctl.h"
+
+struct sockaddr_in daemon_addr = { sizeof(daemon_addr), AF_INET };
+struct sockaddr_in ctl_addr = { sizeof(ctl_addr), AF_INET };
+struct sockaddr_in my_addr = { sizeof(my_addr), AF_INET };
+
+ /* inet addresses of the two machines */
+struct in_addr my_machine_addr;
+struct in_addr his_machine_addr;
+
+u_short daemon_port; /* port number of the talk daemon */
+
+int ctl_sockt;
+int sockt;
+int invitation_waiting = 0;
+
+CTL_MSG msg;
+
+open_sockt()
+{
+ int length;
+
+ my_addr.sin_addr = my_machine_addr;
+ my_addr.sin_port = 0;
+ sockt = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockt <= 0)
+ p_error("Bad socket");
+ if (bind(sockt, (struct sockaddr *)&my_addr, sizeof(my_addr)) != 0)
+ p_error("Binding local socket");
+ length = sizeof(my_addr);
+ if (getsockname(sockt, (struct sockaddr *)&my_addr, &length) == -1)
+ p_error("Bad address for socket");
+}
+
+/* open the ctl socket */
+open_ctl()
+{
+ int length;
+
+ ctl_addr.sin_port = 0;
+ ctl_addr.sin_addr = my_machine_addr;
+ ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ctl_sockt <= 0)
+ p_error("Bad socket");
+ if (bind(ctl_sockt,
+ (struct sockaddr *)&ctl_addr, sizeof(ctl_addr)) != 0)
+ p_error("Couldn't bind to control socket");
+ length = sizeof(ctl_addr);
+ if (getsockname(ctl_sockt,
+ (struct sockaddr *)&ctl_addr, &length) == -1)
+ p_error("Bad address for ctl socket");
+}
+
+/* print_addr is a debug print routine */
+print_addr(addr)
+ struct sockaddr_in addr;
+{
+ int i;
+
+ printf("addr = %x, port = %o, family = %o zero = ",
+ addr.sin_addr, addr.sin_port, addr.sin_family);
+ for (i = 0; i<8;i++)
+ printf("%o ", (int)addr.sin_zero[i]);
+ putchar('\n');
+}
diff --git a/usr.bin/talk/ctl_transact.c b/usr.bin/talk/ctl_transact.c
new file mode 100644
index 0000000..512e040
--- /dev/null
+++ b/usr.bin/talk/ctl_transact.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ctl_transact.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <errno.h>
+#include "talk_ctl.h"
+
+#define CTL_WAIT 2 /* time to wait for a response, in seconds */
+
+/*
+ * SOCKDGRAM is unreliable, so we must repeat messages if we have
+ * not recieved an acknowledgement within a reasonable amount
+ * of time
+ */
+ctl_transact(target, msg, type, rp)
+ struct in_addr target;
+ CTL_MSG msg;
+ int type;
+ CTL_RESPONSE *rp;
+{
+ int read_mask, ctl_mask, nready, cc;
+ struct timeval wait;
+
+ msg.type = type;
+ daemon_addr.sin_addr = target;
+ daemon_addr.sin_port = daemon_port;
+ ctl_mask = 1 << ctl_sockt;
+
+ /*
+ * Keep sending the message until a response of
+ * the proper type is obtained.
+ */
+ do {
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ /* resend message until a response is obtained */
+ do {
+ cc = sendto(ctl_sockt, (char *)&msg, sizeof (msg), 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr));
+ if (cc != sizeof (msg)) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on write to talk daemon");
+ }
+ read_mask = ctl_mask;
+ nready = select(32, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error waiting for daemon response");
+ }
+ } while (nready == 0);
+ /*
+ * Keep reading while there are queued messages
+ * (this is not necessary, it just saves extra
+ * request/acknowledgements being sent)
+ */
+ do {
+ cc = recv(ctl_sockt, (char *)rp, sizeof (*rp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on read from talk daemon");
+ }
+ read_mask = ctl_mask;
+ /* an immediate poll */
+ timerclear(&wait);
+ nready = select(32, &read_mask, 0, 0, &wait);
+ } while (nready > 0 && (rp->vers != TALK_VERSION ||
+ rp->type != type));
+ } while (rp->vers != TALK_VERSION || rp->type != type);
+ rp->id_num = ntohl(rp->id_num);
+ rp->addr.sa_family = ntohs(rp->addr.sa_family);
+}
diff --git a/usr.bin/talk/display.c b/usr.bin/talk/display.c
new file mode 100644
index 0000000..076c6e1
--- /dev/null
+++ b/usr.bin/talk/display.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * The window 'manager', initializes curses and handles the actual
+ * displaying of text
+ */
+#include "talk.h"
+#include <ctype.h>
+
+xwin_t my_win;
+xwin_t his_win;
+WINDOW *line_win;
+
+int curses_initialized = 0;
+
+/*
+ * max HAS to be a function, it is called with
+ * a argument of the form --foo at least once.
+ */
+max(a,b)
+ int a, b;
+{
+
+ return (a > b ? a : b);
+}
+
+/*
+ * Display some text on somebody's window, processing some control
+ * characters while we are at it.
+ */
+display(win, text, size)
+ register xwin_t *win;
+ register unsigned char *text;
+ int size;
+{
+ register int i;
+ char cch;
+
+ for (i = 0; i < size; i++) {
+ if (*text == '\n' || *text == '\r') {
+ waddch(win->x_win, '\n');
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ /* erase character */
+ if ( *text == win->cerase
+ || *text == 010 /* BS */
+ || *text == 0177 /* DEL */
+ ) {
+ wmove(win->x_win, win->x_line, max(--win->x_col, 0));
+ getyx(win->x_win, win->x_line, win->x_col);
+ waddch(win->x_win, ' ');
+ wmove(win->x_win, win->x_line, win->x_col);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ /*
+ * On word erase search backwards until we find
+ * the beginning of a word or the beginning of
+ * the line.
+ */
+ if ( *text == win->werase
+ || *text == 027 /* ^W */
+ ) {
+ int endcol, xcol, i, c;
+
+ endcol = win->x_col;
+ xcol = endcol - 1;
+ while (xcol >= 0) {
+ c = readwin(win->x_win, win->x_line, xcol);
+ if (c != ' ')
+ break;
+ xcol--;
+ }
+ while (xcol >= 0) {
+ c = readwin(win->x_win, win->x_line, xcol);
+ if (c == ' ')
+ break;
+ xcol--;
+ }
+ wmove(win->x_win, win->x_line, xcol + 1);
+ for (i = xcol + 1; i < endcol; i++)
+ waddch(win->x_win, ' ');
+ wmove(win->x_win, win->x_line, xcol + 1);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ /* line kill */
+ if ( *text == win->kill
+ || *text == 025 /* ^U */
+ ) {
+ wmove(win->x_win, win->x_line, 0);
+ wclrtoeol(win->x_win);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ continue;
+ }
+ if (*text == '\f') {
+ if (win == &my_win)
+ wrefresh(curscr);
+ text++;
+ continue;
+ }
+ if (*text == '\7') {
+ _putchar(*text);
+ text++;
+ continue;
+ }
+ if (!isprint(*text) && *text != '\t') {
+ waddch(win->x_win, '^');
+ getyx(win->x_win, win->x_line, win->x_col);
+ cch = (*text & 63) + 64;
+ waddch(win->x_win, cch);
+ } else
+ waddch(win->x_win, *text);
+ getyx(win->x_win, win->x_line, win->x_col);
+ text++;
+ }
+ wrefresh(win->x_win);
+}
+
+/*
+ * Read the character at the indicated position in win
+ */
+readwin(win, line, col)
+ WINDOW *win;
+{
+ int oldline, oldcol;
+ register int c;
+
+ getyx(win, oldline, oldcol);
+ wmove(win, line, col);
+ c = winch(win);
+ wmove(win, oldline, oldcol);
+ return (c);
+}
diff --git a/usr.bin/talk/get_addrs.c b/usr.bin/talk/get_addrs.c
new file mode 100644
index 0000000..b9db4a6
--- /dev/null
+++ b/usr.bin/talk/get_addrs.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)get_addrs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <netdb.h>
+#include <stdio.h>
+#include "talk_ctl.h"
+
+get_addrs(my_machine_name, his_machine_name)
+ char *my_machine_name, *his_machine_name;
+{
+ struct hostent *hp;
+ struct servent *sp;
+
+ msg.pid = htonl(getpid());
+ /* look up the address of the local host */
+ hp = gethostbyname(my_machine_name);
+ if (hp == NULL) {
+ fprintf(stderr, "talk: %s: ", my_machine_name);
+ herror((char *)NULL);
+ exit(-1);
+ }
+ bcopy(hp->h_addr, (char *)&my_machine_addr, hp->h_length);
+ /*
+ * If the callee is on-machine, just copy the
+ * network address, otherwise do a lookup...
+ */
+ if (strcmp(his_machine_name, my_machine_name)) {
+ hp = gethostbyname(his_machine_name);
+ if (hp == NULL) {
+ fprintf(stderr, "talk: %s: ", his_machine_name);
+ herror((char *)NULL);
+ exit(-1);
+ }
+ bcopy(hp->h_addr, (char *) &his_machine_addr, hp->h_length);
+ } else
+ his_machine_addr = my_machine_addr;
+ /* find the server's port */
+ sp = getservbyname("ntalk", "udp");
+ if (sp == 0) {
+ fprintf(stderr, "talk: %s/%s: service is not registered.\n",
+ "ntalk", "udp");
+ exit(-1);
+ }
+ daemon_port = sp->s_port;
+}
diff --git a/usr.bin/talk/get_names.c b/usr.bin/talk/get_names.c
new file mode 100644
index 0000000..0d1fcf1
--- /dev/null
+++ b/usr.bin/talk/get_names.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)get_names.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <pwd.h>
+#include "talk.h"
+
+char *getlogin();
+char *ttyname();
+char *rindex();
+extern CTL_MSG msg;
+
+/*
+ * Determine the local and remote user, tty, and machines
+ */
+get_names(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char hostname[MAXHOSTNAMELEN];
+ char *his_name, *my_name;
+ char *my_machine_name, *his_machine_name;
+ char *my_tty, *his_tty;
+ register char *cp;
+
+ if (argc < 2 ) {
+ printf("Usage: talk user [ttyname]\n");
+ exit(-1);
+ }
+ if (!isatty(0)) {
+ printf("Standard input must be a tty, not a pipe or a file\n");
+ exit(-1);
+ }
+ if ((my_name = getlogin()) == NULL) {
+ struct passwd *pw;
+
+ if ((pw = getpwuid(getuid())) == NULL) {
+ printf("You don't exist. Go away.\n");
+ exit(-1);
+ }
+ my_name = pw->pw_name;
+ }
+ gethostname(hostname, sizeof (hostname));
+ my_machine_name = hostname;
+ /* check for, and strip out, the machine name of the target */
+ for (cp = argv[1]; *cp && !index("@:!", *cp); cp++)
+ ;
+ if (*cp == '\0') {
+ /* this is a local to local talk */
+ his_name = argv[1];
+ his_machine_name = my_machine_name;
+ } else {
+ if (*cp++ == '@') {
+ /* user@host */
+ his_name = argv[1];
+ his_machine_name = cp;
+ } else {
+ /* host!user or host:user */
+ his_name = cp;
+ his_machine_name = argv[1];
+ }
+ *--cp = '\0';
+ }
+ if (argc > 2)
+ his_tty = argv[2]; /* tty name is arg 2 */
+ else
+ his_tty = "";
+ get_addrs(my_machine_name, his_machine_name);
+ /*
+ * Initialize the message template.
+ */
+ msg.vers = TALK_VERSION;
+ msg.addr.sa_family = htons(AF_INET);
+ msg.ctl_addr.sa_family = htons(AF_INET);
+ msg.id_num = htonl(0);
+ strncpy(msg.l_name, my_name, NAME_SIZE);
+ msg.l_name[NAME_SIZE - 1] = '\0';
+ strncpy(msg.r_name, his_name, NAME_SIZE);
+ msg.r_name[NAME_SIZE - 1] = '\0';
+ strncpy(msg.r_tty, his_tty, TTY_SIZE);
+ msg.r_tty[TTY_SIZE - 1] = '\0';
+}
diff --git a/usr.bin/talk/init_disp.c b/usr.bin/talk/init_disp.c
new file mode 100644
index 0000000..e50b9e8
--- /dev/null
+++ b/usr.bin/talk/init_disp.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)init_disp.c 8.2 (Berkeley) 2/16/94";
+#endif /* not lint */
+
+/*
+ * Initialization code for the display package,
+ * as well as the signal handling routines.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <err.h>
+#include "talk.h"
+
+/*
+ * Make sure the callee can write to the screen
+ */
+void check_writeable()
+{
+ char *tty;
+ struct stat sb;
+
+ if ((tty = ttyname(STDERR_FILENO)) == NULL)
+ err(1, "ttyname");
+ if (stat(tty, &sb) < 0)
+ err(1, "%s", tty);
+ if (!(sb.st_mode & S_IWGRP))
+ errx(1, "The callee cannot write to this terminal, use \"mesg y\".");
+}
+
+/*
+ * Set up curses, catch the appropriate signals,
+ * and build the various windows.
+ */
+init_display()
+{
+ void sig_sent();
+ struct sigvec sigv;
+
+ if (initscr() == NULL)
+ errx(1, "Terminal type unset or lacking necessary features.");
+ (void) sigvec(SIGTSTP, (struct sigvec *)0, &sigv);
+ sigv.sv_mask |= sigmask(SIGALRM);
+ (void) sigvec(SIGTSTP, &sigv, (struct sigvec *)0);
+ curses_initialized = 1;
+ clear();
+ refresh();
+ noecho();
+ crmode();
+ signal(SIGINT, sig_sent);
+ signal(SIGPIPE, sig_sent);
+ /* curses takes care of ^Z */
+ my_win.x_nlines = LINES / 2;
+ my_win.x_ncols = COLS;
+ my_win.x_win = newwin(my_win.x_nlines, my_win.x_ncols, 0, 0);
+ scrollok(my_win.x_win, TRUE);
+ wclear(my_win.x_win);
+
+ his_win.x_nlines = LINES / 2 - 1;
+ his_win.x_ncols = COLS;
+ his_win.x_win = newwin(his_win.x_nlines, his_win.x_ncols,
+ my_win.x_nlines+1, 0);
+ scrollok(his_win.x_win, TRUE);
+ wclear(his_win.x_win);
+
+ line_win = newwin(1, COLS, my_win.x_nlines, 0);
+ box(line_win, '-', '-');
+ wrefresh(line_win);
+ /* let them know we are working on it */
+ current_state = "No connection yet";
+}
+
+/*
+ * Trade edit characters with the other talk. By agreement
+ * the first three characters each talk transmits after
+ * connection are the three edit characters.
+ */
+set_edit_chars()
+{
+ char buf[3];
+ int cc;
+ struct sgttyb tty;
+ struct ltchars ltc;
+
+ ioctl(0, TIOCGETP, &tty);
+ ioctl(0, TIOCGLTC, (struct sgttyb *)&ltc);
+ my_win.cerase = tty.sg_erase;
+ my_win.kill = tty.sg_kill;
+ if (ltc.t_werasc == (char) -1)
+ my_win.werase = '\027'; /* control W */
+ else
+ my_win.werase = ltc.t_werasc;
+ buf[0] = my_win.cerase;
+ buf[1] = my_win.kill;
+ buf[2] = my_win.werase;
+ cc = write(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ cc = read(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ his_win.cerase = buf[0];
+ his_win.kill = buf[1];
+ his_win.werase = buf[2];
+}
+
+void
+sig_sent()
+{
+
+ message("Connection closing. Exiting");
+ quit();
+}
+
+/*
+ * All done talking...hang up the phone and reset terminal thingy's
+ */
+quit()
+{
+
+ if (curses_initialized) {
+ wmove(his_win.x_win, his_win.x_nlines-1, 0);
+ wclrtoeol(his_win.x_win);
+ wrefresh(his_win.x_win);
+ endwin();
+ }
+ if (invitation_waiting)
+ send_delete();
+ exit(0);
+}
diff --git a/usr.bin/talk/invite.c b/usr.bin/talk/invite.c
new file mode 100644
index 0000000..d7c33f3
--- /dev/null
+++ b/usr.bin/talk/invite.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)invite.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <errno.h>
+#include <setjmp.h>
+#include "talk_ctl.h"
+#include "talk.h"
+
+/*
+ * There wasn't an invitation waiting, so send a request containing
+ * our sockt address to the remote talk daemon so it can invite
+ * him
+ */
+
+/*
+ * The msg.id's for the invitations
+ * on the local and remote machines.
+ * These are used to delete the
+ * invitations.
+ */
+int local_id, remote_id;
+void re_invite();
+jmp_buf invitebuf;
+
+invite_remote()
+{
+ int nfd, read_mask, template, new_sockt;
+ struct itimerval itimer;
+ CTL_RESPONSE response;
+
+ itimer.it_value.tv_sec = RING_WAIT;
+ itimer.it_value.tv_usec = 0;
+ itimer.it_interval = itimer.it_value;
+ if (listen(sockt, 5) != 0)
+ p_error("Error on attempt to listen for caller");
+#ifdef MSG_EOR
+ /* copy new style sockaddr to old, swap family (short in old) */
+ msg.addr = *(struct osockaddr *)&my_addr; /* XXX new to old style*/
+ msg.addr.sa_family = htons(my_addr.sin_family);
+#else
+ msg.addr = *(struct sockaddr *)&my_addr;
+#endif
+ msg.id_num = htonl(-1); /* an impossible id_num */
+ invitation_waiting = 1;
+ announce_invite();
+ /*
+ * Shut off the automatic messages for a while,
+ * so we can use the interupt timer to resend the invitation
+ */
+ end_msgs();
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+ message("Waiting for your party to respond");
+ signal(SIGALRM, re_invite);
+ (void) setjmp(invitebuf);
+ while ((new_sockt = accept(sockt, 0, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Unable to connect with your party");
+ }
+ close(sockt);
+ sockt = new_sockt;
+
+ /*
+ * Have the daemons delete the invitations now that we
+ * have connected.
+ */
+ current_state = "Waiting for your party to respond";
+ start_msgs();
+
+ msg.id_num = htonl(local_id);
+ ctl_transact(my_machine_addr, msg, DELETE, &response);
+ msg.id_num = htonl(remote_id);
+ ctl_transact(his_machine_addr, msg, DELETE, &response);
+ invitation_waiting = 0;
+}
+
+/*
+ * Routine called on interupt to re-invite the callee
+ */
+void
+re_invite()
+{
+
+ message("Ringing your party again");
+ waddch(my_win.x_win, '\n');
+ if (current_line < my_win.x_nlines - 1)
+ current_line++;
+ /* force a re-announce */
+ msg.id_num = htonl(remote_id + 1);
+ announce_invite();
+ longjmp(invitebuf, 1);
+}
+
+static char *answers[] = {
+ "answer #0", /* SUCCESS */
+ "Your party is not logged on", /* NOT_HERE */
+ "Target machine is too confused to talk to us", /* FAILED */
+ "Target machine does not recognize us", /* MACHINE_UNKNOWN */
+ "Your party is refusing messages", /* PERMISSION_REFUSED */
+ "Target machine can not handle remote talk", /* UNKNOWN_REQUEST */
+ "Target machine indicates protocol mismatch", /* BADVERSION */
+ "Target machine indicates protocol botch (addr)",/* BADADDR */
+ "Target machine indicates protocol botch (ctl_addr)",/* BADCTLADDR */
+};
+#define NANSWERS (sizeof (answers) / sizeof (answers[0]))
+
+/*
+ * Transmit the invitation and process the response
+ */
+announce_invite()
+{
+ CTL_RESPONSE response;
+
+ current_state = "Trying to connect to your party's talk daemon";
+ ctl_transact(his_machine_addr, msg, ANNOUNCE, &response);
+ remote_id = response.id_num;
+ if (response.answer != SUCCESS) {
+ if (response.answer < NANSWERS)
+ message(answers[response.answer]);
+ quit();
+ }
+ /* leave the actual invitation on my talk daemon */
+ ctl_transact(my_machine_addr, msg, LEAVE_INVITE, &response);
+ local_id = response.id_num;
+}
+
+/*
+ * Tell the daemon to remove your invitation
+ */
+send_delete()
+{
+
+ msg.type = DELETE;
+ /*
+ * This is just a extra clean up, so just send it
+ * and don't wait for an answer
+ */
+ msg.id_num = htonl(remote_id);
+ daemon_addr.sin_addr = his_machine_addr;
+ if (sendto(ctl_sockt, &msg, sizeof (msg), 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr)) != sizeof(msg))
+ perror("send_delete (remote)");
+ msg.id_num = htonl(local_id);
+ daemon_addr.sin_addr = my_machine_addr;
+ if (sendto(ctl_sockt, &msg, sizeof (msg), 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr)) != sizeof (msg))
+ perror("send_delete (local)");
+}
diff --git a/usr.bin/talk/io.c b/usr.bin/talk/io.c
new file mode 100644
index 0000000..f7f31bd
--- /dev/null
+++ b/usr.bin/talk/io.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)io.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This file contains the I/O handling and the exchange of
+ * edit characters. This connection itself is established in
+ * ctl.c
+ */
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "talk.h"
+
+#define A_LONG_TIME 10000000
+#define STDIN_MASK (1<<fileno(stdin)) /* the bit mask for standard
+ input */
+
+/*
+ * The routine to do the actual talking
+ */
+talk()
+{
+ register int read_template, sockt_mask;
+ int read_set, nb;
+ char buf[BUFSIZ];
+ struct timeval wait;
+
+ message("Connection established\007\007\007");
+ current_line = 0;
+ sockt_mask = (1<<sockt);
+
+ /*
+ * Wait on both the other process (sockt_mask) and
+ * standard input ( STDIN_MASK )
+ */
+ read_template = sockt_mask | STDIN_MASK;
+ for (;;) {
+ read_set = read_template;
+ wait.tv_sec = A_LONG_TIME;
+ wait.tv_usec = 0;
+ nb = select(32, &read_set, 0, 0, &wait);
+ if (nb <= 0) {
+ if (errno == EINTR) {
+ read_set = read_template;
+ continue;
+ }
+ /* panic, we don't know what happened */
+ p_error("Unexpected error from select");
+ quit();
+ }
+ if (read_set & sockt_mask) {
+ /* There is data on sockt */
+ nb = read(sockt, buf, sizeof buf);
+ if (nb <= 0) {
+ message("Connection closed. Exiting");
+ quit();
+ }
+ display(&his_win, buf, nb);
+ }
+ if (read_set & STDIN_MASK) {
+ /*
+ * We can't make the tty non_blocking, because
+ * curses's output routines would screw up
+ */
+ ioctl(0, FIONREAD, (struct sgttyb *) &nb);
+ nb = read(0, buf, nb);
+ display(&my_win, buf, nb);
+ /* might lose data here because sockt is non-blocking */
+ write(sockt, buf, nb);
+ }
+ }
+}
+
+extern int errno;
+extern int sys_nerr;
+
+/*
+ * p_error prints the system error message on the standard location
+ * on the screen and then exits. (i.e. a curses version of perror)
+ */
+p_error(string)
+ char *string;
+{
+ wmove(my_win.x_win, current_line, 0);
+ wprintw(my_win.x_win, "[%s : %s (%d)]\n",
+ string, strerror(errno), errno);
+ wrefresh(my_win.x_win);
+ move(LINES-1, 0);
+ refresh();
+ quit();
+}
+
+/*
+ * Display string in the standard location
+ */
+message(string)
+ char *string;
+{
+ wmove(my_win.x_win, current_line, 0);
+ wprintw(my_win.x_win, "[%s]\n", string);
+ if (current_line < my_win.x_nlines - 1)
+ current_line++;
+ wrefresh(my_win.x_win);
+}
diff --git a/usr.bin/talk/look_up.c b/usr.bin/talk/look_up.c
new file mode 100644
index 0000000..cc4b4c5
--- /dev/null
+++ b/usr.bin/talk/look_up.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)look_up.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <protocols/talkd.h>
+#include <errno.h>
+#include "talk_ctl.h"
+#include "talk.h"
+
+/*
+ * See if the local daemon has an invitation for us.
+ */
+check_local()
+{
+ CTL_RESPONSE response;
+ register CTL_RESPONSE *rp = &response;
+
+ /* the rest of msg was set up in get_names */
+#ifdef MSG_EOR
+ /* copy new style sockaddr to old, swap family (short in old) */
+ msg.ctl_addr = *(struct osockaddr *)&ctl_addr;
+ msg.ctl_addr.sa_family = htons(ctl_addr.sin_family);
+#else
+ msg.ctl_addr = *(struct sockaddr *)&ctl_addr;
+#endif
+ /* must be initiating a talk */
+ if (!look_for_invite(rp))
+ return (0);
+ /*
+ * There was an invitation waiting for us,
+ * so connect with the other (hopefully waiting) party
+ */
+ current_state = "Waiting to connect with caller";
+ do {
+ if (rp->addr.sa_family != AF_INET)
+ p_error("Response uses invalid network address");
+ errno = 0;
+ if (connect(sockt,
+ (struct sockaddr *)&rp->addr, sizeof (rp->addr)) != -1)
+ return (1);
+ } while (errno == EINTR);
+ if (errno == ECONNREFUSED) {
+ /*
+ * The caller gave up, but his invitation somehow
+ * was not cleared. Clear it and initiate an
+ * invitation. (We know there are no newer invitations,
+ * the talkd works LIFO.)
+ */
+ ctl_transact(his_machine_addr, msg, DELETE, rp);
+ close(sockt);
+ open_sockt();
+ return (0);
+ }
+ p_error("Unable to connect with initiator");
+ /*NOTREACHED*/
+}
+
+/*
+ * Look for an invitation on 'machine'
+ */
+look_for_invite(rp)
+ CTL_RESPONSE *rp;
+{
+ struct in_addr machine_addr;
+
+ current_state = "Checking for invitation on caller's machine";
+ ctl_transact(his_machine_addr, msg, LOOK_UP, rp);
+ /* the switch is for later options, such as multiple invitations */
+ switch (rp->answer) {
+
+ case SUCCESS:
+ msg.id_num = htonl(rp->id_num);
+ return (1);
+
+ default:
+ /* there wasn't an invitation waiting for us */
+ return (0);
+ }
+}
diff --git a/usr.bin/talk/msgs.c b/usr.bin/talk/msgs.c
new file mode 100644
index 0000000..6e8cc9b
--- /dev/null
+++ b/usr.bin/talk/msgs.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)msgs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * A package to display what is happening every MSG_INTERVAL seconds
+ * if we are slow connecting.
+ */
+
+#include <sys/time.h>
+#include <signal.h>
+#include <stdio.h>
+#include "talk.h"
+
+#define MSG_INTERVAL 4
+
+char *current_state;
+int current_line = 0;
+
+void
+disp_msg()
+{
+ message(current_state);
+}
+
+start_msgs()
+{
+ struct itimerval itimer;
+
+ message(current_state);
+ signal(SIGALRM, disp_msg);
+ itimer.it_value.tv_sec = itimer.it_interval.tv_sec = MSG_INTERVAL;
+ itimer.it_value.tv_usec = itimer.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+end_msgs()
+{
+ struct itimerval itimer;
+
+ timerclear(&itimer.it_value);
+ timerclear(&itimer.it_interval);
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+ signal(SIGALRM, SIG_DFL);
+}
diff --git a/usr.bin/talk/talk.1 b/usr.bin/talk/talk.1
new file mode 100644
index 0000000..2f40fa6
--- /dev/null
+++ b/usr.bin/talk/talk.1
@@ -0,0 +1,133 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)talk.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TALK 1
+.Os BSD 4.2
+.Sh NAME
+.Nm talk
+.Nd talk to another user
+.Sh SYNOPSIS
+.Nm talk
+.Ar person
+.Op Ar ttyname
+.Sh DESCRIPTION
+.Nm Talk
+is a visual communication program which copies lines from your
+terminal to that of another user.
+.Pp
+Options available:
+.Bl -tag -width ttyname
+.It Ar person
+If you wish to talk to someone on your own machine, then
+.Ar person
+is just the person's login name. If you wish to talk to a user on
+another host, then
+.Ar person
+is of the form
+.Ql user@host
+or
+.Ql host!user
+or
+.Ql host:user .
+.It Ar ttyname
+If you wish to talk to a user who is logged in more than once, the
+.Ar ttyname
+argument may be used to indicate the appropriate terminal
+name, where
+.Ar ttyname
+is of the form
+.Ql ttyXX .
+.El
+.Pp
+When first called,
+.Nm talk
+sends the message
+.Bd -literal -offset indent -compact
+Message from TalkDaemon@his_machine...
+talk: connection requested by your_name@your_machine.
+talk: respond with: talk your_name@your_machine
+.Ed
+.Pp
+to the user you wish to talk to. At this point, the recipient
+of the message should reply by typing
+.Pp
+.Dl talk \ your_name@your_machine
+.Pp
+It doesn't matter from which machine the recipient replies, as
+long as his login-name is the same. Once communication is established,
+the two parties may type simultaneously, with their output appearing
+in separate windows. Typing control-L
+.Ql ^L
+will cause the screen to
+be reprinted, while your erase, kill, and word kill characters will
+behave normally. To exit, just type your interrupt character;
+.Nm talk
+then moves the cursor to the bottom of the screen and restores the
+terminal to its previous state.
+.Pp
+Permission to talk may be denied or granted by use of the
+.Xr mesg 1
+command. At the outset talking is allowed. Certain commands, in
+particular
+.Xr nroff 1
+and
+.Xr pr 1 ,
+disallow messages in order to
+prevent messy output.
+.Pp
+.Sh FILES
+.Bl -tag -width /var/run/utmp -compact
+.It Pa /etc/hosts
+to find the recipient's machine
+.It Pa /var/run/utmp
+to find the recipient's tty
+.El
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr mesg 1 ,
+.Xr who 1 ,
+.Xr write 1
+.Sh BUGS
+The version of
+.Xr talk 1
+released with
+.Bx 4.3
+uses a protocol that
+is incompatible with the protocol used in the version released with
+.Bx 4.2 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.bin/talk/talk.c b/usr.bin/talk/talk.c
new file mode 100644
index 0000000..ca0dcb4
--- /dev/null
+++ b/usr.bin/talk/talk.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)talk.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "talk.h"
+
+/*
+ * talk: A visual form of write. Using sockets, a two way
+ * connection is set up between the two people talking.
+ * With the aid of curses, the screen is split into two
+ * windows, and each users text is added to the window,
+ * one character at a time...
+ *
+ * Written by Kipp Hickman
+ *
+ * Modified to run under 4.1a by Clem Cole and Peter Moore
+ * Modified to run between hosts by Peter Moore, 8/19/82
+ * Modified to run under 4.1c by Peter Moore 3/17/83
+ * Fixed to not run with unwriteable terminals MRVM 28/12/94
+ */
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ get_names(argc, argv);
+ check_writeable();
+ init_display();
+ open_ctl();
+ open_sockt();
+ start_msgs();
+ if (!check_local())
+ invite_remote();
+ end_msgs();
+ set_edit_chars();
+ talk();
+}
diff --git a/usr.bin/talk/talk.h b/usr.bin/talk/talk.h
new file mode 100644
index 0000000..98a7118
--- /dev/null
+++ b/usr.bin/talk/talk.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)talk.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <curses.h>
+
+extern int sockt;
+extern int curses_initialized;
+extern int invitation_waiting;
+
+extern char *current_state;
+extern int current_line;
+
+typedef struct xwin {
+ WINDOW *x_win;
+ int x_nlines;
+ int x_ncols;
+ int x_line;
+ int x_col;
+ char kill;
+ char cerase;
+ char werase;
+} xwin_t;
+
+extern xwin_t my_win;
+extern xwin_t his_win;
+extern WINDOW *line_win;
diff --git a/usr.bin/talk/talk_ctl.h b/usr.bin/talk/talk_ctl.h
new file mode 100644
index 0000000..91c75d0
--- /dev/null
+++ b/usr.bin/talk/talk_ctl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)talk_ctl.h 8.1 (Berkeley) 6/6/93
+ */
+
+extern struct sockaddr_in daemon_addr;
+extern struct sockaddr_in ctl_addr;
+extern struct sockaddr_in my_addr;
+extern struct in_addr my_machine_addr;
+extern struct in_addr his_machine_addr;
+extern u_short daemon_port;
+extern int ctl_sockt;
+extern CTL_MSG msg;
diff --git a/usr.bin/tconv/Makefile b/usr.bin/tconv/Makefile
new file mode 100644
index 0000000..11b45db
--- /dev/null
+++ b/usr.bin/tconv/Makefile
@@ -0,0 +1,12 @@
+# Makefile for tconv
+# $Id: Makefile,v 1.1.1.1 1994/10/09 17:40:30 ache Exp $
+
+PROG= tconv
+SRCS= tconv.c quit.c
+MLINKS= tconv.1 tic.1 tconv.1 captoinfo.1
+LINKS= ${BINDIR}/tconv ${BINDIR}/tic ${BINDIR}/tconv ${BINDIR}/captoinfo
+CFLAGS+= -I${.CURDIR}/../../lib/libmytinfo -Wall
+DPADD= $(LIBMYTINFO)
+LDADD= -lmytinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tconv/quit.c b/usr.bin/tconv/quit.c
new file mode 100644
index 0000000..f30fb50
--- /dev/null
+++ b/usr.bin/tconv/quit.c
@@ -0,0 +1,72 @@
+/*
+ * quit.c
+ *
+ * By Ross Ridge
+ * Public Domain
+ * 92/02/01 07:30:14
+ *
+ * quit with a diagnostic message printed on stderr
+ *
+ */
+
+#define NOTLIB
+#include "defs.h"
+
+#ifdef USE_SCCS_IDS
+static const char SCCSid[] = "@(#) mytinfo quit.c 3.2 92/02/01 public domain, By Ross Ridge";
+#endif
+
+char *prg_name;
+
+#if defined(USE_PROTOTYPES) && !defined(lint)
+void (*cleanup)(int);
+#else
+void (*cleanup)();
+#endif
+
+/* PRINTFLIKE2 */
+noreturn
+#ifdef USE_STDARG
+#ifdef USE_PROTOTYPES
+void
+quit(int e, char *fmt, ...)
+#else
+void quit(e, fmt)
+int e;
+char *fmt;
+#endif
+#else
+void quit(va_alist)
+va_dcl
+#endif
+{
+#ifndef USE_STDARG
+ int e;
+ char *fmt;
+#endif
+ va_list ap;
+
+#ifdef USE_STDARG
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+ e = va_arg(ap, int);
+ fmt = va_arg(ap, char *);
+#endif
+
+ (*cleanup)(e);
+
+ if (e != 0)
+ fprintf(stderr, "%s: ", prg_name);
+#ifdef USE_DOPRNT
+ _doprnt(fmt, ap, stderr);
+#else
+ vfprintf(stderr, fmt, ap);
+#endif
+ putc('\n', stderr);
+ if (e > 0 && e < sys_nerr) {
+ fprintf(stderr, "%d - %s\n", e, sys_errlist[e]);
+ }
+ fflush(stderr);
+ exit(e);
+}
diff --git a/usr.bin/tconv/tconv.1 b/usr.bin/tconv/tconv.1
new file mode 100644
index 0000000..b4490ef
--- /dev/null
+++ b/usr.bin/tconv/tconv.1
@@ -0,0 +1,180 @@
+.\" @(#) mytinfo tconv.1 3.2 92/02/01 public domain, By Ross Ridge
+.TH TCONV 1 "92/02/01" "mytinfo"
+.SH NAME
+tconv \- convert between termcap, terminfo source and terminfo binary
+.SH SYNOPSIS
+.B tconv
+[\fB\-b\fR]
+[\fB\-c\fR\ [\fB\-OUGd\fR]]
+[\fB\-i\fR]
+[\fB\-B\fR\ [\fB\-D\fR\ dir]]
+[\fB\-I\fR]
+[\fB\-k\fR]
+[\fB\-V\fR]
+[\fB\-t\fR\ term]
+[file]
+.br
+.B tic
+[file]
+.br
+.B captoinfo
+[\fB\-t\fR\ term]
+[\fB\-OUGdk\fR]]
+[file]
+.SH DESCRIPTION
+.I tconv
+converts between the three terminal descriptions,
+termcap, terminfo source, and terminfo binary,
+that the
+.I tinfo
+library uses.
+It performs the same functions of
+.IR captoinfo (1M)
+and
+.IR tic (1M)
+of System V.
+It also can be used to generate a terminfo source listing from a terminfo
+binary, one of the functions of System V's
+.IR infocmp (1M).
+.SS Translation Options
+.PD 0
+.TP
+.B \-c
+convert from termcap
+.TP
+.B \-i
+convert from terminfo source
+.TP
+.B \-b
+convert from terminfo binary
+.TP
+.B \-B
+convert to terminfo binary
+.TP
+.B \-I
+convert to terminfo source
+.PD
+.PP
+If a file is specified, one of
+.B \-c
+or
+.B \-i
+must specified and the whole file while be translated.
+If no file is specified then the input options will only restrict looking
+for the terminal to be translated in places likely have descriptions
+of the desired type
+(ie. with the
+.B -c
+option in the
+.B TERMCAP
+environment variable, and in
+.IR /usr/share/misc/termcap ,
+with the
+.B -i
+option in the
+.B TERMINFO
+environment variable, and in
+.IR /usr/lib/terminfo ),
+otherwise
+.I tconv
+will look in all available databases.
+If neither
+.B \-I
+or
+.B \-B
+are given the
+.B \-I
+option will be assumed.
+If the
+.B \-B
+option is used, the compiled output will be put in the the
+terminfo database, otherwise standard output is used.
+.PP
+You cannot translate from terminfo binary to terminfo binary.
+Translating from terminfo source to terminfo source is possible,
+but not of much use in most cases, as
+.B use=
+fields will be followed and incorporated into the output terminal
+description.
+.PP
+.I tconv
+should be able translate all standard termcap parameterized strings
+terminfo format, but complex strings using GNU's %a code may be
+too hard to translate.
+If
+.I tconv
+thinks a termcap string is already in terminfo format (if a %p
+code appears in the string), it won't try to translate it.
+String capabilities that don't take parameters won't be translated.
+.PP
+.B
+.SS Termcap options
+The following options are available when translating termcap entries
+(\fB\-c\fR options is used).
+.PP
+.PD 0
+.TP
+.B \-d
+don't supply any defaults for missing capabilities
+.TP
+.B \-O
+include obsolete termcap capabilities
+.TP
+.B \-G
+include GNU capabilities
+.TP
+.B \-U
+include UW capabilities
+.PD
+.SS Other Options
+.PD 0
+.TP
+.B \-k
+keep comments when translating a file
+.TP
+.B \-V
+print version information and exit
+.TP
+.BI \-D " " dir
+directory to put terminfo binaries in
+.TP
+.BI \-t " " term
+terminal name to translate
+.PD
+.PP
+If no terminal specified with the
+.B \-t
+option, then the terminal name to to translate will be taken from the
+environment variable
+.BR TERM .
+.SH FILES
+.PD 0
+.TP 2i
+.B /usr/lib/terminfo
+The default location to get and put terminfo binaries.
+.TP
+.B /usr/lib/terminfo/terminfo.src
+The default filename of the terminfo source file.
+.TP
+.B /etc/termcap
+The default filename of the termcap database.
+.PD
+.SH "SEE ALSO"
+captoinfo(1M),
+tic(1M),
+infocmp(1M),
+termcap(3),
+curses(3X),
+term(4),
+termcap(4),
+terminfo(4).
+.SH DIAGNOSTICS
+The line number of a warning message when translating a file
+may refer to the last line of an entry instead of the line in the entry
+that generated the warning.
+.SH BUGS
+More warning messages could be generated.
+.I tconv
+can't translate to termcap. Binaries generated will have cancelled
+capabilities marked as cancelled, which is incompatible with
+System V Release 2.0 terminfo.
diff --git a/usr.bin/tconv/tconv.c b/usr.bin/tconv/tconv.c
new file mode 100644
index 0000000..2c281994
--- /dev/null
+++ b/usr.bin/tconv/tconv.c
@@ -0,0 +1,1384 @@
+/*
+ * tconv.c
+ *
+ * Ross Ridge
+ * Public Domain
+ * 92/02/01 07:30:23
+ *
+ * tconv [-b] [-c [-OUGd]] [-i] [-B [-D dir]] [-I] [-k] [-V] [-t term] [file]
+ *
+ * -c convert from termcap
+ * -i convert from terminfo source
+ * -b convert from terminfo binary
+ * -B convert to terminfo binary
+ * -I convert to terminfo source
+ * -V print version info
+ *
+ * The following switches are available when converting from termcap:
+ * -d don't supply any defaults for missing capabilities
+ * -O include obsolete termcap capabilities
+ * -G include GNU capabilities
+ * -U include UW capabilities
+ *
+ * -k keep comments
+ * -D dir directory to put terminfo binaries in
+ *
+ * -t term name of terminal to translate
+ * file filename of termcap/terminfo database to use
+ *
+ * If a file is specifed and no terminal is given the entire file we be
+ * translated.
+ * If no terminal and no file is specified then the terminal name will be
+ * taken from the environment variable TERM.
+ * Unless compiling to a terminfo binary, output is to stdout.
+ *
+ */
+
+#define NOTLIB
+#include "defs.h"
+#define SINGLE
+#include <term.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef USE_STDDEF
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+#include <unistd.h>
+#endif
+
+#ifndef __FreeBSD__
+#include "strtok.c"
+#include "mkdir.c"
+#endif
+
+#ifndef lint
+static const char SCCSid[] = "@(#) mytinfo tconv.c 3.2 92/02/01 public domain, By Ross Ridge";
+#endif
+
+extern int errno;
+
+/* the right margin of the output */
+#define LINELEN 76
+
+struct term_path *path; /* returned from _buildpath */
+
+TERMINAL _term_buf;
+char buf[MAX_BUF+1]; /* buffer for the termcap entry */
+
+int noOT = 1; /* -O */
+int noGNU = 1; /* -G */
+int noUW = 1; /* -W */
+int dodefault = 1; /* -d */
+int keepcomments = 0; /* -k */
+int compile = 0; /* -B */
+int from_tcap = 0; /* -c */
+int from_tinfo = 0; /* -i */
+int from_tbin = 0; /* -b */
+char *directory = NULL; /* -D */
+
+int continued = 0;
+int termcap = 1;
+
+int lineno = 0; /* current line number */
+
+/* print the first part of a warning message */
+void
+warn() {
+ if (lineno == 0)
+ fprintf(stderr, "warning: ");
+ else
+ fprintf(stderr, "(%s)%d: warning: ",
+ _term_buf.name_long, lineno);
+}
+
+/* output a string indenting at the beginning of a line, and wraping
+ * at the right margin.
+ */
+void
+putstr(s)
+char *s; {
+ static pos = 0;
+ int l;
+
+ if (s == NULL) {
+ if (pos != 0) {
+ pos = 0;
+ putchar('\n');
+ }
+ return;
+ }
+
+ if (termcap && noOT && *s == 'O')
+ return;
+ if (termcap && noGNU && *s == 'G')
+ return;
+ if (termcap && noUW && *s == 'U')
+ return;
+
+ l = strlen(s) + 2;
+
+ if (l + pos > LINELEN && pos != 0) {
+ putchar('\n');
+ pos = 0;
+ }
+
+ if (pos == 0) {
+ putchar('\t');
+ pos = 8;
+ } else
+ putchar(' ');
+
+ printf("%s,", s);
+
+ pos += l;
+}
+
+#ifndef MAX_PUSHED
+/* maximum # of parameters that can be pushed onto the stack */
+#define MAX_PUSHED 16
+#endif
+
+int stack[MAX_PUSHED]; /* the stack */
+int stackptr; /* the next empty place on the stack */
+int onstack; /* the top of stack */
+int seenm; /* seen a %m */
+int seenn; /* seen a %n */
+int seenr; /* seen a %r */
+int param; /* current parameter */
+char *dp; /* pointer to the end of the converted string */
+
+/* push onstack on to the stack */
+void
+push() {
+ if (stackptr > MAX_PUSHED) {
+ warn();
+ fprintf(stderr, "string to complex to covert\n");
+ } else
+ stack[stackptr++] = onstack;
+}
+
+/* pop the top of the stack into onstack */
+void
+pop() {
+ if (stackptr == 0)
+ if (onstack == 0) {
+ warn();
+ fprintf(stderr, "I'm confused\n");
+ } else
+ onstack = 0;
+ else
+ onstack = stack[--stackptr];
+ param++;
+}
+
+/* convert a character to a terminfo push */
+static int
+cvtchar(sp)
+register char *sp; {
+ char c;
+ int l;
+
+ switch(*sp) {
+ case '\\':
+ switch(*++sp) {
+ case '\'':
+ case '$':
+ case '\\':
+ case '%':
+ c = *sp;
+ l = 2;
+ break;
+ case '\0':
+ c = '\\';
+ l = 1;
+ break;
+ case '0':
+ if (sp[1] == '0' && sp[2] == '0') {
+ c = '\0';
+ l = 4;
+ } else {
+ c = '\200'; /* '\0' ???? */
+ l = 2;
+ }
+ break;
+ default:
+ c = *sp;
+ l = 2;
+ break;
+ }
+ break;
+ default:
+ c = *sp;
+ l = 1;
+ }
+ c &= 0177;
+ if (isgraph(c) && c != ',' && c != '\'' && c != '\\' && c != ':') {
+ *dp++ = '%'; *dp++ = '\''; *dp++ = c; *dp++ = '\'';
+ } else {
+ *dp++ = '%'; *dp++ = '{';
+ if (c > 99)
+ *dp++ = c / 100 + '0';
+ if (c > 9)
+ *dp++ = (c / 10) % 10 + '0';
+ *dp++ = c % 10 + '0';
+ *dp++ = '}';
+ }
+ return l;
+}
+
+/* push n copies of param on the terminfo stack if not already there */
+void
+getparm(parm, n)
+int parm;
+int n; {
+ if (seenr) {
+ if (parm == 1)
+ parm = 2;
+ else if (parm == 2)
+ parm = 1;
+ }
+ if (onstack == parm) {
+ if (n > 1) {
+ warn();
+ fprintf(stderr, "string may not be optimal");
+ *dp++ = '%'; *dp++ = 'P'; *dp++ = 'a';
+ while(n--) {
+ *dp++ = '%'; *dp++ = 'g'; *dp++ = 'a';
+ }
+ }
+ return;
+ }
+ if (onstack != 0)
+ push();
+
+ onstack = parm;
+
+ while(n--) { /* %p0 */
+ *dp++ = '%'; *dp++ = 'p'; *dp++ = '0' + parm;
+ }
+
+ if (seenn && parm < 3) { /* %{96}%^ */
+ *dp++ = '%'; *dp++ = '{'; *dp++ = '9'; *dp++ = '6'; *dp++ = '}';
+ *dp++ = '%'; *dp++ = '^';
+ }
+
+ if (seenm && parm < 3) { /* %{127}%^ */
+ *dp++ = '%'; *dp++ = '{'; *dp++ = '1'; *dp++ = '2'; *dp++ = '7';
+ *dp++ = '}'; *dp++ = '%'; *dp++ = '^';
+ }
+}
+
+/* convert a string to terminfo format */
+char *
+convstr(s, i)
+register char *s;
+int i; {
+ static char line[MAX_LINE];
+ register char *cap;
+ int nocode = 0;
+
+ stackptr = 0;
+ onstack = 0;
+ seenm = 0;
+ seenn = 0;
+ seenr = 0;
+ param = 1;
+
+ dp = line;
+ cap = strnames[i];
+#if 0
+ if (cap[0] == 'k'
+ || ((cap[0] == 'i' || cap[0] == 'r') && cap[1] == 's'
+ && (cap[2] == '1' || cap[2] == '2' || cap[2] == '3')))
+ /* if (k.* || [ir]s[123]) */
+ nocode = 1;
+#else
+ if (_strflags[i] != 'G')
+ nocode = 1;
+#endif
+ if (!nocode) {
+ char *d = s;
+ while(*s != '\0') {
+ if (s[0] == '\\' && s[1] != '\0')
+ s++;
+ else if (s[0] == '%' && s[1] != '\0') {
+ if (s[1] == 'p') {
+ if (termcap) {
+ warn();
+ fprintf(stderr,
+"string '%s' already in terminfo format\n", strcodes[i]);
+ nocode = 1;
+ break;
+ } else
+ nocode = 1;
+ }
+ s++;
+ }
+ s++;
+ }
+ if (!nocode && !termcap) {
+ warn();
+ fprintf(stderr,
+"string '%s' not in terminfo format, converting...\n", cap);
+ }
+ s = d;
+ }
+ while(*s != '\0') {
+ switch(*s) {
+ case '%':
+ s++;
+ if (nocode) {
+ *dp++ = '%';
+ break;
+ }
+ switch(*s++) {
+ case '%': *dp++ = '%'; break;
+ case 'r':
+ if (seenr++ == 1) {
+ warn();
+ fprintf(stderr, "seen %%r twice\n");
+ }
+ break;
+ case 'm':
+ if (seenm++ == 1) {
+ warn();
+ fprintf(stderr, "seen %%m twice\n");
+ }
+ break;
+ case 'n':
+ if (seenn++ == 1) {
+ warn();
+ fprintf(stderr, "seen %%n twice\n");
+ }
+ break;
+ case 'i': *dp++ = '%'; *dp++ = 'i'; break;
+ case '6':
+ case 'B':
+ getparm(param, 2);
+ /* %{6}%*%+ */
+ *dp++ = '%'; *dp++ = '{'; *dp++ = '6';
+ *dp++ = '}'; *dp++ = '%'; *dp++ = '*';
+ *dp++ = '%'; *dp++ = '+';
+ break;
+ case '8':
+ case 'D':
+ getparm(param, 2);
+ /* %{2}%*%- */
+ *dp++ = '%'; *dp++ = '{'; *dp++ = '2';
+ *dp++ = '}'; *dp++ = '%'; *dp++ = '*';
+ *dp++ = '%'; *dp++ = '-';
+ break;
+ case '>':
+ getparm(param, 2);
+ /* %?%{x}%>%t%{y}%+%; */
+ *dp++ = '%'; *dp++ = '?';
+ s += cvtchar(s);
+ *dp++ = '%'; *dp++ = '>';
+ *dp++ = '%'; *dp++ = 't';
+ s += cvtchar(s);
+ *dp++ = '%'; *dp++ = '+';
+ *dp++ = '%'; *dp++ = ';';
+ break;
+ case 'a':
+ if ((*s == '=' || *s == '+' || *s == '-'
+ || *s == '*' || *s == '/')
+ && (s[1] == 'p' || s[1] == 'c')
+ && s[2] != '\0') {
+ int l;
+ l = 2;
+ if (*s != '=')
+ getparm(param, 1);
+ if (s[1] == 'p') {
+ getparm(param + s[2] - '@', 1);
+ if (param != onstack) {
+ pop();
+ param--;
+ }
+ l++;
+ } else
+ l += cvtchar(s + 2);
+ switch(*s) {
+ case '+':
+ *dp++ = '%'; *dp++ = '+';
+ break;
+ case '-':
+ *dp++ = '%'; *dp++ = '-';
+ break;
+ case '*':
+ *dp++ = '%'; *dp++ = '*';
+ break;
+ case '/':
+ *dp++ = '%'; *dp++ = '/';
+ break;
+ case '=':
+ if (seenr)
+ if (param == 1)
+ onstack = 2;
+ else if (param == 2)
+ onstack = 1;
+ else
+ onstack = param;
+ else
+ onstack = param;
+ break;
+ }
+ s += l;
+ break;
+ }
+ getparm(param, 1);
+ s += cvtchar(s);
+ *dp++ = '%'; *dp++ = '+';
+ break;
+ case '+':
+ getparm(param, 1);
+ s += cvtchar(s);
+ *dp++ = '%'; *dp++ = '+';
+ *dp++ = '%'; *dp++ = 'c';
+ pop();
+ break;
+ case 's':
+ s += cvtchar(s);
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = '-';
+ break;
+ case '-':
+ s += cvtchar(s);
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = '-';
+ *dp++ = '%'; *dp++ = 'c';
+ pop();
+ break;
+ case '.':
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = 'c';
+ pop();
+ break;
+ case '2':
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = '0';
+ *dp++ = '2'; *dp++ = 'd';
+ pop();
+ break;
+ case '3':
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = '0';
+ *dp++ = '3'; *dp++ = 'd';
+ pop();
+ break;
+ case 'd':
+ getparm(param, 1);
+ *dp++ = '%'; *dp++ = 'd';
+ pop();
+ break;
+ case 'f':
+ param++;
+ break;
+ case 'b':
+ param--;
+ break;
+ default:
+ warn();
+ *dp++ = '%';
+ s--;
+ fprintf(stderr, "'%s' unknown %% code %c",
+ strcodes[i], *s);
+ if (*s >= 0 && *s < 32)
+ fprintf(stderr, "^%c\n", *s + '@');
+ else if (*s < 0 || *s >= 127)
+ fprintf(stderr, "\\%03o\n", *s & 0377);
+ else
+ fprintf(stderr, "%c\n", *s);
+ break;
+ }
+ break;
+ case '\\':
+ if (!compile) {*dp++ = *s++; *dp++ = *s++; break;}
+ /* FALLTHROUGH */
+ case '\n':
+ if (!compile) {*dp++ = '\\'; *dp++ = 'n'; s++; break;}
+ /* FALLTHROUGH */
+ case '\t':
+ if (!compile) {*dp++ = '\\'; *dp++ = 't'; s++; break;}
+ /* FALLTHROUGH */
+ case '\r':
+ if (!compile) {*dp++ = '\\'; *dp++ = 'r'; s++; break;}
+ /* FALLTHROUGH */
+ case '\200':
+ if (!compile) {*dp++ = '\\'; *dp++ = '0'; s++; break;}
+ /* FALLTHROUGH */
+ case '\f':
+ if (!compile) {*dp++ = '\\'; *dp++ = 'f'; s++; break;}
+ /* FALLTHROUGH */
+ case '\b':
+ if (!compile) {*dp++ = '\\'; *dp++ = 'b'; s++; break;}
+ /* FALLTHROUGH */
+ case ' ':
+ if (!compile) {*dp++ = '\\'; *dp++ = 's'; s++; break;}
+ /* FALLTHROUGH */
+ case '^':
+ if (!compile) {*dp++ = '\\'; *dp++ = '^'; s++; break;}
+ /* FALLTHROUGH */
+ case ':':
+ if (!compile) {*dp++ = '\\'; *dp++ = ':'; s++; break;}
+ /* FALLTHROUGH */
+ case ',':
+ if (!compile) {*dp++ = '\\'; *dp++ = ','; s++; break;}
+ /* FALLTHROUGH */
+#if 0
+ case '\'':
+ if (!compile) {*dp++ = '\\'; *dp++ = '\''; s++; break;}
+ /* FALLTHROUGH */
+#endif
+ default:
+ if (compile)
+ *dp++ = *s++;
+ else if (*s > 0 && *s < 32) {
+ *dp++ = '^';
+ *dp++ = *s + '@';
+ s++;
+ } else if (*s <= 0 || *s >= 127) {
+ *dp++ = '\\';
+ *dp++ = ((*s & 0300) >> 6) + '0';
+ *dp++ = ((*s & 0070) >> 3) + '0';
+ *dp++ = (*s & 0007) + '0';
+ s++;
+ } else
+ *dp++ = *s++;
+ break;
+ }
+ }
+ *dp = '\0';
+ return line;
+}
+
+#define LSB(n) ((unsigned) (n) & 0377)
+#define MSB(n) (((unsigned) (n) >> 8) & 0377)
+
+void
+writebin(fd, name)
+int fd;
+char *name; {
+ static char bin[MAX_BUF + 1];
+ register char *s;
+ register char *d;
+ register int i;
+ register char *t;
+ register int n;
+ char *strtbl;
+ int sz_name, n_bools, n_nums, n_offs, sz_strs;
+ extern int _boolorder[], _numorder[], _strorder[];
+
+ strncpy(bin + 12, name, 127);
+ bin[12 + 128] = '\0';
+ sz_name = strlen(name) + 1;
+ if (sz_name > 128)
+ sz_name = 128;
+
+ s = bin + 12 + sz_name;
+ for(i = 0; _boolorder[i] != -1; i++) {
+ switch(_term_buf.bools[i]) {
+ case -1: *s++ = 0; break;
+ case 0: *s++ = 0377; break;
+ default: *s++ = 1; break;
+ }
+ }
+ n_bools = i;
+ if ((sz_name + n_bools) & 1)
+ n_bools++;
+
+ s = bin + 12 + sz_name + n_bools;
+ for(i = 0; _numorder[i] != -1; i++) {
+ n = _term_buf.nums[_numorder[i]];
+ switch(n) {
+ case -2: *s++ = 0377; *s++ = 0377; break;
+ case -1: *s++ = 0376; *s++ = 0377; break;
+ default:
+ *s++ = LSB(n);
+ *s++ = MSB(n);
+ }
+ }
+ n_nums = i;
+
+ s = bin + 12 + sz_name + n_bools + n_nums * 2;
+ for(i = 0; _strorder[i] != -1; i++) {
+ if (_term_buf.strs[_strorder[i]] == (char *) 0) {
+ *s++ = 0376; *s++ = 0377;
+ } else {
+ *s++ = 0377; *s++ = 0377;
+ }
+ }
+ n_offs = i;
+
+ s = bin + 12 + sz_name + n_bools + n_nums * 2;
+ strtbl = d = s + n_offs * 2;
+ for(i = 0; _strorder[i] != -1; i++) {
+ t = _term_buf.strs[_strorder[i]];
+ if (t == (char *) -1 || t == (char *) 0)
+ s += 2;
+ else {
+ n = d - strtbl;
+ *s++ = LSB(n);
+ *s++ = MSB(n);
+ t = convstr(t, _strorder[i]);
+ while(*t != '\0') {
+ *d++ = *t++;
+ if (d >= bin + MAX_BUF - 1) {
+ warn();
+ fprintf(stderr,
+ "compiled entry to big\n");
+ *d++ = '\0';
+ goto toobig;
+ }
+ }
+ *d++ = '\0';
+ }
+ }
+
+toobig:
+ sz_strs = d - strtbl;
+
+ bin[0] = 032;
+ bin[1] = 01;
+ bin[2] = LSB(sz_name);
+ bin[3] = MSB(sz_name);
+ bin[4] = LSB(n_bools);
+ bin[5] = MSB(n_bools);
+ bin[6] = LSB(n_nums);
+ bin[7] = MSB(n_nums);
+ bin[8] = LSB(n_offs);
+ bin[9] = MSB(n_offs);
+ bin[10] = LSB(sz_strs);
+ bin[11] = MSB(sz_strs);
+
+ if (write(fd, bin, d - bin) == -1)
+ quit(errno, "can't write binary file");
+
+ return;
+}
+
+void
+find_directory() {
+ struct term_path *p;
+ struct stat st;
+
+ if (directory != NULL)
+ return;
+
+ p = path;
+ while(p->type != -1 && p->file != NULL) {
+ if (stat(p->file, &st) == 0) {
+ if ((st.st_mode & 0170000) == 0040000) {
+ directory = p->file;
+ return;
+ }
+ }
+ p++;
+ }
+ quit(-1, "can't find a terminfo directory");
+}
+
+/* convert a terminal name to a binary filename */
+char *
+binfile(name)
+char *name; {
+ static char line[MAX_LINE+1];
+
+ sprintf(line, "%s/%c/%s", directory, *name, name);
+ return line;
+}
+
+char *
+bindir(name)
+char *name; {
+ static char line[MAX_LINE+1];
+
+ sprintf(line, "%s/%c", directory, *name);
+ return line;
+}
+
+int
+badname(name)
+char *name; {
+ while(*name) {
+ if (*name == '/' || !isgraph(*name))
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+/* output a terminfo binary */
+void
+outputbin(name)
+char *name; {
+ register char *s, *d, *last;
+ char tmp[MAX_LINE+1];
+ char line[MAX_LINE+1];
+ int fd;
+
+ find_directory();
+
+ s = name;
+ d = line;
+ while(*s != '\0' && d < line + MAX_LINE) {
+ *d++ = *s++;
+ }
+
+ while(d > line && d[-1] == '|') {
+ d--;
+ }
+
+ *d = '\0';
+
+ s = strtok(line, "|");
+ last = NULL;
+
+ while(s != NULL && last == NULL) {
+ if (*s == '\0') {
+ ;
+ } else if (badname(s)) {
+ if (lineno)
+ warn();
+ fprintf(stderr, "bad terminal name '%s', ingored.\n",
+ s);
+ } else {
+ if (access(bindir(s), 2) == -1) {
+ if (errno != ENOENT)
+ quit(errno,
+ "can't access directory '%s'",
+ bindir(s));
+ if (mkdir(bindir(s), 0777) == -1)
+ quit(errno, "can't make directory '%s'",
+ bindir(s));
+ }
+ fd = open(binfile(s), O_WRONLY | O_CREAT | O_EXCL,
+ 0666);
+ if (fd == -1) {
+ if (errno != EEXIST)
+ quit(errno, "can't open file '%s'",
+ binfile(s));
+ if (unlink(binfile(s)) == -1)
+ quit(errno, "can't unlink file '%s'",
+ binfile(s));
+ fd = open(binfile(s),
+ O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (fd == -1)
+ quit(errno, "can't create file '%s'",
+ binfile(s));
+ }
+ writebin(fd, name);
+ close(fd);
+ last = s;
+ }
+ s = strtok(NULL, "|");
+ }
+
+ if (last == NULL) {
+ if (lineno)
+ warn();
+ fprintf(stderr, "no terminal name, entry ignored.\n");
+ return;
+ }
+
+ while(s != NULL && s + strlen(s) != d) {
+ if (*s == '\0' || strcmp(s, last) == 0) {
+ ;
+ } else if (badname(s)) {
+ if (lineno)
+ warn();
+ fprintf(stderr, "bad terminal name '%s', ingored.\n",
+ s);
+ } else {
+ if (access(bindir(s), 2) == -1) {
+ if (errno != ENOENT)
+ quit(errno,
+ "can't access directory '%s'",
+ bindir(s));
+ if (mkdir(bindir(s), 0777) == -1)
+ quit(errno, "can't make directory '%s'",
+ bindir(s));
+ }
+ if (access(binfile(s), 0) == -1) {
+ if (errno != ENOENT)
+ quit(errno, "can't access file '%s'",
+ binfile(s));
+ } else if (unlink(binfile(s)) == -1) {
+ quit(errno, "can't unlink file '%s'",
+ binfile(s));
+ }
+ strcpy(tmp, binfile(last));
+ if (link(tmp, binfile(s)) == -1) {
+ quit(errno, "can't link '%s' to '%s'",
+ last, binfile(s));
+ }
+ }
+ s = strtok(NULL, "|");
+ }
+ return;
+}
+
+/* output an entry in terminfo source format */
+void
+outputinfo(name)
+char *name; {
+ int i;
+ char line[MAX_LINE];
+
+ printf("%s,\n", name);
+
+ for(i = 0; i < NUM_OF_BOOLS; i++)
+ if (_term_buf.bools[i] == 0) {
+ sprintf(line, "%s@", boolnames[i]);
+ putstr(line);
+ } else if (_term_buf.bools[i] != -1)
+ putstr(boolnames[i]);
+
+ for(i = 0; i < NUM_OF_NUMS; i++)
+ if (_term_buf.nums[i] == -1) {
+ sprintf(line, "%s@", numnames[i]);
+ putstr(line);
+ } else if (_term_buf.nums[i] != -2) {
+ sprintf(line, "%s#%d", numnames[i], _term_buf.nums[i]);
+ putstr(line);
+ }
+
+ for(i = 0; i < NUM_OF_STRS; i++)
+ if (_term_buf.strs[i] == NULL) {
+ sprintf(line, "%s@", strnames[i]);
+ putstr(line);
+ } else if (_term_buf.strs[i] != (char *) -1) {
+ sprintf(line, "%s=%s", strnames[i],
+ convstr(_term_buf.strs[i], i));
+ putstr(line);
+ }
+ putstr(NULL);
+}
+
+/* convert a terminfo entry to binary format */
+void
+convtinfo() {
+ int i, r;
+
+ termcap = 0;
+
+ for(i = 0; i < NUM_OF_BOOLS; i++)
+ _term_buf.bools[i] = -1;
+ for(i = 0; i < NUM_OF_NUMS; i++)
+ _term_buf.nums[i] = -2;
+ for(i = 0; i < NUM_OF_STRS; i++)
+ _term_buf.strs[i] = (char *) -1;
+
+ _term_buf.name_all = NULL;
+
+ r = _gettinfo(buf, &_term_buf, path);
+ if (r != 0) {
+ if (lineno == 0)
+ quit(-1, "problem reading entry");
+ else {
+ warn();
+ fprintf(stderr, "problem reading entry\n");
+ }
+ }
+
+ if (compile)
+ outputbin(_term_buf.name_all);
+ else
+ outputinfo(_term_buf.name_all);
+ return;
+}
+
+/* convert a terminfo binary to terminfo source */
+void
+convtbin() {
+ int i, r;
+
+ termcap = 0;
+
+ for(i = 0; i < NUM_OF_BOOLS; i++)
+ _term_buf.bools[i] = -1;
+ for(i = 0; i < NUM_OF_NUMS; i++)
+ _term_buf.nums[i] = -2;
+ for(i = 0; i < NUM_OF_STRS; i++)
+ _term_buf.strs[i] = (char *) -1;
+
+ _term_buf.name_all = NULL;
+
+ r = _gettbin(buf, &_term_buf);
+ if (r != 0) {
+ if (lineno == 0)
+ quit(-1, "problem reading entry");
+ else {
+ warn();
+ fprintf(stderr, "problem reading entry\n");
+ }
+ }
+
+ outputinfo(_term_buf.name_all);
+
+ return;
+}
+
+/* convert a termcap entry to terminfo format */
+void
+convtcap() {
+ int i, r;
+ char *name;
+
+ termcap = 1;
+
+ for(i = 0; i < NUM_OF_BOOLS; i++)
+ _term_buf.bools[i] = -1;
+ for(i = 0; i < NUM_OF_NUMS; i++)
+ _term_buf.nums[i] = -2;
+ for(i = 0; i < NUM_OF_STRS; i++)
+ _term_buf.strs[i] = (char *) -1;
+
+ _term_buf.name_all = NULL;
+
+
+#if DEBUG
+ printf("%s\n", buf);
+#endif
+ r = _gettcap(buf, &_term_buf, path);
+ if (r != 0) {
+ if (lineno == 0)
+ quit(-1, "problem reading entry");
+ else {
+ warn();
+ fprintf(stderr, "problem reading entry\n");
+ }
+ }
+
+ if (dodefault && !continued)
+ _tcapdefault();
+
+ _tcapconv();
+
+ name = _term_buf.name_all;
+#if DEBUG
+ printf("...%s\n", name);
+#endif
+ if (name[0] != '\0' && name[1] != '\0' && name[2] == '|')
+ name += 3; /* skip the 2 letter code */
+
+ if (compile)
+ outputbin(name);
+ else
+ outputinfo(name);
+}
+
+void
+convbinfile(file)
+char *file; {
+ register FILE *f;
+ int r;
+
+ f = fopen(file, "r");
+
+ if (f == NULL)
+ quit(errno, "can't open '%s'", file);
+
+ r = fread(buf, sizeof(char), MAX_BUF, f);
+ if (r < 12 || buf[0] != 032 || buf[1] != 01)
+ quit(-1, "file '%s' corrupted", file);
+
+ convtbin();
+}
+
+/* convert a termcap file to terminfo format */
+void
+convtcfile(file)
+char *file; {
+ int nocolon;
+ register int c;
+ register char *d;
+ register FILE *f;
+
+ f = fopen(file, "r");
+
+ if (f == NULL)
+ quit(errno, "can't open '%s'", file);
+
+ d = buf;
+ c = getc(f);
+ while(c != EOF) {
+ lineno++;
+ if (c == '#') {
+ if (keepcomments) {
+ do {
+ putchar(c);
+ c = getc(f);
+ } while(c != '\n' && c != EOF);
+ putchar('\n');
+ } else
+ do
+ c = getc(f);
+ while(c != '\n' && c != EOF);
+ if (c != EOF)
+ c = getc(f);
+ continue;
+ }
+ while(isspace(c) && c != '\n')
+ c = getc(f);
+ if (c == '\n' && buf == d) {
+ c = getc(f);
+ continue;
+ }
+
+ while(c != EOF) {
+ if (c == '\\') {
+ c = getc(f);
+ if (c == EOF)
+ break;
+ if (c == '\n') {
+ c = getc(f);
+ break;
+ }
+ *d++ = '\\';
+ *d++ = c;
+ } else if (c == '\n') {
+ *d = '\0';
+ if (*--d == ':') {
+ nocolon = 0;
+ *d-- = '\0';
+ } else {
+ nocolon = 1;
+ }
+ while(d > buf && *d != ':')
+ d--;
+ if (d[1] == 't' && d[2] == 'c' && d[3] == '=') {
+ continued = 1;
+ d[1] = '\0';
+ } else
+ continued = 0;
+ convtcap();
+ if (nocolon) {
+ warn();
+ fprintf(stderr,
+ "entry doesn't end with :\n");
+ }
+ _term_buf.strbuf = _endstr();
+ _del_strs(&_term_buf);
+ if (continued) {
+ printf("\tuse=%s,\n", d + 4);
+ }
+ d = buf;
+ c = getc(f);
+ break;
+ } else
+ *d++ = c;
+ c = getc(f);
+ }
+ }
+}
+
+static int
+getln(f, buf, len)
+FILE *f;
+register char *buf;
+int len; {
+ register int c, i = 0;
+
+ while((c = getc(f)) == '#') {
+ lineno++;
+ if (keepcomments) {
+ putchar('#');
+ while((c = getc(f)) != '\n') {
+ if (c == EOF)
+ return -1;
+ putchar(c);
+ }
+ putchar('\n');
+ } else {
+ while((c = getc(f)) != '\n')
+ if (c == EOF)
+ return -1;
+ }
+ }
+
+ lineno++;
+ while(c != '\n') {
+ if (c == EOF)
+ return -1;
+ if (i < len) {
+ i++;
+ *buf++ = c;
+ }
+ c = getc(f);
+ }
+
+ while(isspace(*(buf-1))) {
+ buf--;
+ i--;
+ }
+
+ *buf = '\0';
+ return i;
+}
+
+void
+convtifile(file)
+char *file; {
+ static char line[MAX_LINE+1];
+ int l;
+ int n;
+ register FILE *f;
+
+ f = fopen(file, "r");
+
+ if (f == NULL)
+ quit(errno, "can't open '%s'", file);
+
+ lineno = 0;
+
+ l = getln(f, line, MAX_LINE);
+ while(l != -1) {
+ if (line[l-1] == ':') {
+ strncpy(buf, line, MAX_BUF);
+ convtcap();
+ } else if (line[l-1] == '\\') {
+ n = MAX_BUF;
+ do {
+ line[--l] = '\0';
+ if (n > 0)
+ strncpy(buf + MAX_BUF - n, line, n);
+ n -= l;
+ l = getln(f, line, MAX_LINE);
+ } while(l != -1 && line[l-1] == '\\');
+ if (n > 0 && l != -1)
+ strncpy(buf + MAX_BUF - n, line, n);
+ convtcap();
+ } else if (line[l-1] == ',') {
+ n = MAX_BUF;
+ do {
+ if (n > 0)
+ strncpy(buf + MAX_BUF - n, line, n);
+ n -= l;
+ l = getln(f, line, MAX_LINE);
+ } while(l != -1 && isspace(line[0]));
+#if 0
+ printf("buf = '%s'\n", buf);
+#endif
+ convtinfo();
+ continue;
+ } else if (line[0] != '\0') {
+ warn();
+ fprintf(stderr, "malformed line\n");
+ if (keepcomments) {
+ printf("%s\n", line);
+ }
+ }
+ l = getln(f, line, MAX_LINE);
+ }
+ return;
+}
+
+/* dummy routine for quit */
+/* ARGSUSED */
+void
+do_cleanup(e)
+int e; {
+ return;
+}
+
+/* print out usage, called by quit */
+/* ARGSUSED */
+void
+usage(e)
+int e; {
+ fprintf(stderr,
+"usage: %s [-b] [-c [-OUGd]] [-i] [-B [-D dir]] [-I] [-k] [-V]\n\t[-t term] [file]\n",
+ prg_name);
+ return;
+}
+
+int
+main(argc, argv)
+int argc;
+char **argv; {
+ extern char *optarg;
+ extern int optind;
+ extern int opterr;
+ char *term = NULL;
+ char *file = NULL;
+ int r;
+ char c;
+ int pversion = 0;
+
+ prg_name = strrchr(argv[0], '/');
+ if (prg_name == NULL)
+ prg_name = argv[0];
+ else
+ prg_name++;
+
+ cleanup = usage;
+
+ opterr = 0;
+
+ if (strcmp(prg_name, "tic") == 0)
+ compile = 1;
+
+ while ((c = getopt(argc, argv, "bciBIOGUdkVD:t:")) != -1) {
+ switch(c) {
+ case 'O':
+ noOT = 0;
+ break;
+ case 'G':
+ noGNU = 0;
+ break;
+ case 'U':
+ noUW = 0;
+ break;
+ case 'D':
+ if (directory != NULL)
+ quit(-1, "more than one directory specified");
+ directory = optarg;
+ break;
+ case 't':
+ if (term != NULL)
+ quit(-1, "more than one terminal specified");
+ term = optarg;
+ break;
+ case 'd': dodefault = 0; break;
+ case 'k': keepcomments = 1; break;
+ case 'b': from_tbin = 1; break;
+ case 'c': from_tcap = 1; break;
+ case 'i': from_tinfo = 1; break;
+ case 'B': compile = 1; break;
+ case 'I': compile = 0; break;
+ case 'V': pversion = 1; break;
+ case '?':
+ default:
+ quit(-1, "bad or missing command line argument");
+ }
+ }
+
+ if (pversion) {
+ quit(0, "%s\n%s", _mytinfo_version, SCCSid);
+ }
+
+ if (optind == argc - 1)
+ file = argv[optind];
+ else if (optind != argc)
+ quit(-1, "wrong number of arguments");
+
+ if (from_tbin + from_tcap + from_tinfo > 1)
+ quit(-1, "more than one input file type specified");
+
+ if (!from_tcap && !from_tinfo && !from_tbin && file != NULL) {
+ if (strcmp(prg_name, "cap2info") == 0
+ || strcmp(prg_name, "captoinfo") == 0)
+ from_tcap = 1;
+ else if (strcmp(prg_name, "tic") == 0)
+ from_tinfo = 1;
+ else
+ quit(-1, "no input file type specified");
+ }
+
+ if (from_tbin && compile)
+ quit(-1, "can't convert from binary to binary");
+
+ if (file != NULL) {
+ if (from_tbin) {
+ cleanup = do_cleanup;
+ convbinfile(file);
+ exit(0);
+ }
+ if (!compile)
+ path = _buildpath(file, 0, NULL, -1);
+ else {
+ path = _buildpath(file, 0,
+ "$TERMINFO", 2,
+ "$MYTERMINFO", 2,
+#ifdef TERMINFODIR
+ TERMINFODIR, 0,
+#endif
+ NULL, -1);
+ }
+ if (path == NULL)
+ quit(-1, "can't build path");
+ if (term == NULL) {
+ cleanup = do_cleanup;
+ if (from_tcap && !compile)
+ convtcfile(file);
+ else
+ convtifile(file);
+ exit(0);
+ }
+ } else if (from_tcap && !compile)
+ path = _buildpath("$TERMCAP", 1,
+#ifdef TERMCAPFILE
+ TERMCAPFILE, 0,
+#endif
+ NULL, -1);
+ else if (from_tinfo || from_tbin)
+ path = _buildpath("$TERMINFO", 2,
+ "$MYTERMINFO", 2,
+#ifdef TERMINFODIR
+ TERMINFODIR, 0,
+#endif
+#ifdef TERMINFOSRC
+ TERMINFOSRC, 0,
+#endif
+ NULL, -1);
+ else if (from_tcap)
+ path = _buildpath("$TERMCAP", 1,
+#ifdef TERMCAPFILE
+ TERMCAPFILE, 0,
+#endif
+ "$TERMINFO", 2,
+ "$MYTERMINFO", 2,
+#ifdef TERMINFODIR
+ TERMINFODIR, 0,
+#endif
+ NULL, -1);
+ else
+ path = _buildpath(
+#ifdef USE_TERMINFO
+ "$MYTERMINFO", 2,
+ "$TERMINFO", 2,
+#ifdef TERMINFODIR
+ TERMINFODIR, 0,
+#endif
+#ifdef TERMINFOSRC
+ TERMINFOSRC, 0,
+#endif
+#endif
+#ifdef USE_TERMCAP
+ "$TERMCAP", 1,
+#ifdef TERMCAPFILE
+ TERMCAPFILE, 0,
+#endif
+#endif
+ NULL, -1);
+ if (term == NULL) {
+ term = getenv("TERM");
+ if (term == NULL)
+ quit(-1, "no terminal type given");
+ }
+
+ cleanup = do_cleanup;
+
+ r = _findterm(term, path, buf);
+ switch(r) {
+ case 1:
+ convtcap();
+ break;
+ case 2:
+ convtinfo();
+ break;
+ case 3:
+ if (compile)
+ quit(-1, "entry is already compiled");
+ convtbin();
+ break;
+ default:
+ quit(-1, "can't find a terminal entry for '%s'", term);
+ }
+
+ exit(0);
+ return 0;
+}
diff --git a/usr.bin/tcopy/Makefile b/usr.bin/tcopy/Makefile
new file mode 100644
index 0000000..5a42f6d
--- /dev/null
+++ b/usr.bin/tcopy/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tcopy
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tcopy/pathnames.h b/usr.bin/tcopy/pathnames.h
new file mode 100644
index 0000000..ddf4286
--- /dev/null
+++ b/usr.bin/tcopy/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_DEFTAPE "/dev/rmt0"
diff --git a/usr.bin/tcopy/tcopy.1 b/usr.bin/tcopy/tcopy.1
new file mode 100644
index 0000000..d5554cd
--- /dev/null
+++ b/usr.bin/tcopy/tcopy.1
@@ -0,0 +1,89 @@
+.\" Copyright (c) 1985, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tcopy.1 8.2 (Berkeley) 4/17/94
+.\"
+.Dd April 17, 1994
+.Dt TCOPY 1
+.Os BSD 4.3
+.Sh NAME
+.Nm tcopy
+.Nd copy and/or verify mag tapes
+.Sh SYNOPSIS
+.Nm tcopy
+.Op Fl cvx
+.Op Fl s Ar maxblk
+.Oo Ar src Op Ar dest
+.Oc
+.Sh DESCRIPTION
+.Nm Tcopy
+is designed to copy magnetic tapes. The only assumption made
+about the tape is that there are two tape marks at the end.
+.Nm Tcopy
+with only a source tape
+.Pf ( Ar rmt0
+by default) specified will print
+information about the sizes of records and tape files. If a destination
+is specified a copy will be made of the source tape. The blocking on the
+destination tape will be identical to that used on the source tape. Copying
+a tape will yield the same output as if just printing the sizes.
+.Pp
+Options:
+.Bl -tag -width s_maxblk
+.It Fl c
+Copy
+.Ar src
+to
+.Ar dest
+and then verify that the two tapes are identical.
+.It Fl s Ar maxblk
+Specify a maximum block size,
+.Ar maxblk .
+.It Fl v
+Given the two tapes,
+.ar src
+and
+.Ar dest
+verify that they are identical.
+.It Fl x
+Output all informational messages to the standard error.
+This option is useful when
+.Ar dest
+is
+.Pa /dev/stdout .
+.El
+.Sh SEE ALSO
+.Xr mtio 4
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c
new file mode 100644
index 0000000..e717499
--- /dev/null
+++ b/usr.bin/tcopy/tcopy.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 1985, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tcopy.c 8.2 (Berkeley) 4/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+#define MAXREC (64 * 1024)
+#define NOCOUNT (-2)
+
+int filen, guesslen, maxblk = MAXREC;
+long lastrec, record, size, tsize;
+FILE *msg = stdout;
+
+void *getspace __P((int));
+void intr __P((int));
+void usage __P((void));
+void verify __P((int, int, char *));
+void writeop __P((int, int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int lastnread, nread, nw, inp, outp;
+ enum {READ, VERIFY, COPY, COPYVERIFY} op = READ;
+ sig_t oldsig;
+ int ch, needeof;
+ char *buff, *inf;
+
+ guesslen = 1;
+ while ((ch = getopt(argc, argv, "cs:vx")) != EOF)
+ switch((char)ch) {
+ case 'c':
+ op = COPYVERIFY;
+ break;
+ case 's':
+ maxblk = atoi(optarg);
+ if (maxblk <= 0) {
+ fprintf(stderr, "tcopy: illegal block size\n");
+ usage();
+ }
+ guesslen = 0;
+ break;
+ case 'v':
+ op = VERIFY;
+ break;
+ case 'x':
+ msg = stderr;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (op != READ)
+ usage();
+ inf = _PATH_DEFTAPE;
+ break;
+ case 1:
+ if (op != READ)
+ usage();
+ inf = argv[0];
+ break;
+ case 2:
+ if (op == READ)
+ op = COPY;
+ inf = argv[0];
+ if ((outp = open(argv[1], op == VERIFY ? O_RDONLY :
+ op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) {
+ perror(argv[1]);
+ exit(3);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if ((inp = open(inf, O_RDONLY, 0)) < 0) {
+ perror(inf);
+ exit(1);
+ }
+
+ buff = getspace(maxblk);
+
+ if (op == VERIFY) {
+ verify(inp, outp, buff);
+ exit(0);
+ }
+
+ if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+ (void) signal(SIGINT, intr);
+
+ needeof = 0;
+ for (lastnread = NOCOUNT;;) {
+ if ((nread = read(inp, buff, maxblk)) == -1) {
+ while (errno == EINVAL && (maxblk -= 1024)) {
+ nread = read(inp, buff, maxblk);
+ if (nread >= 0)
+ goto r1;
+ }
+ fprintf(stderr, "read error, file %d, record %ld: ",
+ filen, record);
+ perror("");
+ exit(1);
+ } else if (nread != lastnread) {
+ if (lastnread != 0 && lastnread != NOCOUNT) {
+ if (lastrec == 0 && nread == 0)
+ fprintf(msg, "%ld records\n", record);
+ else if (record - lastrec > 1)
+ fprintf(msg, "records %ld to %ld\n",
+ lastrec, record);
+ else
+ fprintf(msg, "record %ld\n", lastrec);
+ }
+ if (nread != 0)
+ fprintf(msg, "file %d: block size %d: ",
+ filen, nread);
+ (void) fflush(stdout);
+ lastrec = record;
+ }
+r1: guesslen = 0;
+ if (nread > 0) {
+ if (op == COPY || op == COPYVERIFY) {
+ if (needeof) {
+ writeop(outp, MTWEOF);
+ needeof = 0;
+ }
+ nw = write(outp, buff, nread);
+ if (nw != nread) {
+ fprintf(stderr,
+ "write error, file %d, record %ld: ",
+ filen, record);
+ if (nw == -1)
+ perror("");
+ else
+ fprintf(stderr,
+ "write (%d) != read (%d)\n",
+ nw, nread);
+ fprintf(stderr, "copy aborted\n");
+ exit(5);
+ }
+ }
+ size += nread;
+ record++;
+ } else {
+ if (lastnread <= 0 && lastnread != NOCOUNT) {
+ fprintf(msg, "eot\n");
+ break;
+ }
+ fprintf(msg,
+ "file %d: eof after %ld records: %ld bytes\n",
+ filen, record, size);
+ needeof = 1;
+ filen++;
+ tsize += size;
+ size = record = lastrec = 0;
+ lastnread = 0;
+ }
+ lastnread = nread;
+ }
+ fprintf(msg, "total length: %ld bytes\n", tsize);
+ (void)signal(SIGINT, oldsig);
+ if (op == COPY || op == COPYVERIFY) {
+ writeop(outp, MTWEOF);
+ writeop(outp, MTWEOF);
+ if (op == COPYVERIFY) {
+ writeop(outp, MTREW);
+ writeop(inp, MTREW);
+ verify(inp, outp, buff);
+ }
+ }
+ exit(0);
+}
+
+void
+verify(inp, outp, outb)
+ register int inp, outp;
+ register char *outb;
+{
+ register int eot, inmaxblk, inn, outmaxblk, outn;
+ register char *inb;
+
+ inb = getspace(maxblk);
+ inmaxblk = outmaxblk = maxblk;
+ for (eot = 0;; guesslen = 0) {
+ if ((inn = read(inp, inb, inmaxblk)) == -1) {
+ if (guesslen)
+ while (errno == EINVAL && (inmaxblk -= 1024)) {
+ inn = read(inp, inb, inmaxblk);
+ if (inn >= 0)
+ goto r1;
+ }
+ perror("tcopy: read error");
+ break;
+ }
+r1: if ((outn = read(outp, outb, outmaxblk)) == -1) {
+ if (guesslen)
+ while (errno == EINVAL && (outmaxblk -= 1024)) {
+ outn = read(outp, outb, outmaxblk);
+ if (outn >= 0)
+ goto r2;
+ }
+ perror("tcopy: read error");
+ break;
+ }
+r2: if (inn != outn) {
+ fprintf(msg,
+ "%s: tapes have different block sizes; %d != %d.\n",
+ "tcopy", inn, outn);
+ break;
+ }
+ if (!inn) {
+ if (eot++) {
+ fprintf(msg, "tcopy: tapes are identical.\n");
+ return;
+ }
+ } else {
+ if (bcmp(inb, outb, inn)) {
+ fprintf(msg,
+ "tcopy: tapes have different data.\n");
+ break;
+ }
+ eot = 0;
+ }
+ }
+ exit(1);
+}
+
+void
+intr(signo)
+ int signo;
+{
+ if (record)
+ if (record - lastrec > 1)
+ fprintf(msg, "records %ld to %ld\n", lastrec, record);
+ else
+ fprintf(msg, "record %ld\n", lastrec);
+ fprintf(msg, "interrupt at file %d: record %ld\n", filen, record);
+ fprintf(msg, "total length: %ld bytes\n", tsize + size);
+ exit(1);
+}
+
+void *
+getspace(blk)
+ int blk;
+{
+ void *bp;
+
+ if ((bp = malloc((size_t)blk)) == NULL) {
+ fprintf(stderr, "tcopy: no memory\n");
+ exit(11);
+ }
+ return (bp);
+}
+
+void
+writeop(fd, type)
+ int fd, type;
+{
+ struct mtop op;
+
+ op.mt_op = type;
+ op.mt_count = (daddr_t)1;
+ if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) {
+ perror("tcopy: tape op");
+ exit(6);
+ }
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n");
+ exit(1);
+}
diff --git a/usr.bin/tee/Makefile b/usr.bin/tee/Makefile
new file mode 100644
index 0000000..a713132
--- /dev/null
+++ b/usr.bin/tee/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tee
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tee/tee.1 b/usr.bin/tee/tee.1
new file mode 100644
index 0000000..2e453cb
--- /dev/null
+++ b/usr.bin/tee/tee.1
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tee.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TEE 1
+.Os
+.Sh NAME
+.Nm tee
+.Nd pipe fitting
+.Sh SYNOPSIS
+.Nm tee
+.Op Fl ai
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm tee
+utility copies standard input to standard output,
+making a copy in zero or more files.
+The output is unbuffered.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Append the output to the files rather than
+overwriting them.
+.It Fl i
+Ignore the
+.Dv SIGINT
+signal.
+.El
+.Pp
+The following operands are available:
+.Bl -tag -width file
+.It file
+A pathname of an output
+.Ar file .
+.El
+.Pp
+The
+.Nm tee
+utility takes the default action for all signals,
+except in the event of the
+.Fl i
+option.
+.Pp
+The
+.Nm tee
+utility exits 0 on success, and >0 if an error occurs.
+.Sh STANDARDS
+The
+.Nm tee
+function is expected to be
+.Tn POSIX
+.St -p1003.2
+compatible.
diff --git a/usr.bin/tee/tee.c b/usr.bin/tee/tee.c
new file mode 100644
index 0000000..ad1110a
--- /dev/null
+++ b/usr.bin/tee/tee.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tee.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct _list {
+ struct _list *next;
+ int fd;
+ char *name;
+} LIST;
+LIST *head;
+
+void add __P((int, char *));
+void err __P((int, const char *, ...));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register LIST *p;
+ register int n, fd, rval, wval;
+ register char *bp;
+ int append, ch, exitval;
+ char *buf;
+#define BSIZE (8 * 1024)
+
+ append = 0;
+ while ((ch = getopt(argc, argv, "ai")) != EOF)
+ switch((char)ch) {
+ case 'a':
+ append = 1;
+ break;
+ case 'i':
+ (void)signal(SIGINT, SIG_IGN);
+ break;
+ case '?':
+ default:
+ (void)fprintf(stderr, "usage: tee [-ai] [file ...]\n");
+ exit(1);
+ }
+ argv += optind;
+ argc -= optind;
+
+ if ((buf = malloc((u_int)BSIZE)) == NULL)
+ err(1, "%s", strerror(errno));
+
+ add(STDOUT_FILENO, "stdout");
+
+ for (exitval = 0; *argv; ++argv)
+ if ((fd = open(*argv, append ? O_WRONLY|O_CREAT|O_APPEND :
+ O_WRONLY|O_CREAT|O_TRUNC, DEFFILEMODE)) < 0) {
+ err(0, "%s: %s", *argv, strerror(errno));
+ exitval = 1;
+ } else
+ add(fd, *argv);
+
+ while ((rval = read(STDIN_FILENO, buf, BSIZE)) > 0)
+ for (p = head; p; p = p->next) {
+ n = rval;
+ bp = buf;
+ do {
+ if ((wval = write(p->fd, bp, n)) == -1) {
+ err(0, "%s: %s",
+ p->name, strerror(errno));
+ exitval = 1;
+ break;
+ }
+ bp += wval;
+ } while (n -= wval);
+ }
+ if (rval < 0)
+ err(1, "read: %s", strerror(errno));
+ exit(exitval);
+}
+
+void
+add(fd, name)
+ int fd;
+ char *name;
+{
+ LIST *p;
+
+ if ((p = malloc((u_int)sizeof(LIST))) == NULL)
+ err(1, "%s", strerror(errno));
+ p->fd = fd;
+ p->name = name;
+ p->next = head;
+ head = p;
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(int doexit, const char *fmt, ...)
+#else
+err(doexit, fmt, va_alist)
+ int doexit;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "tee: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (doexit)
+ exit(1);
+}
diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile
new file mode 100644
index 0000000..fee9815
--- /dev/null
+++ b/usr.bin/telnet/Makefile
@@ -0,0 +1,59 @@
+#
+# Copyright (c) 1990 The Regents of the University of California.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+#
+
+PROG= telnet
+
+CFLAGS+=-DTERMCAP -DKLUDGELINEMODE -DUSE_TERMIO #-DAUTHENTICATION -DENCRYPTION
+CFLAGS+=-DENV_HACK
+CFLAGS+=-I${.CURDIR}/../../lib
+
+#CFLAGS+= -DKRB4
+
+LDADD= -ltermcap -ltelnet
+#LDADD+= -lkrb -ldes
+DPADD= ${LIBTERMCAP}
+
+SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c telnet.c \
+ terminal.c tn3270.c utilities.c
+
+# These are the sources that have encryption stuff in them.
+CRYPT_SRC= authenc.c commands.c externs.h main.c network.c
+CRYPT_SRC+= ring.c ring.h telnet.c terminal.c utilities.c Makefile
+NOCRYPT_DIR=${.CURDIR}/Nocrypt
+
+.include <bsd.prog.mk>
+
+nocrypt:
+ @echo "Encryption code already removed."
diff --git a/usr.bin/telnet/README b/usr.bin/telnet/README
new file mode 100644
index 0000000..086c88f
--- /dev/null
+++ b/usr.bin/telnet/README
@@ -0,0 +1,566 @@
+
+
+This is a distribution of both client and server telnet. These programs
+have been compiled on:
+ telnet telnetd
+ BSD 4.3 Reno X X
+ UNICOS 5.1 X X
+ UNICOS 6.0 X X
+ UNICOS 6.1 X X
+ UNICOS 7.0 X X
+ SunOs 3.5 X X (no linemode in server)
+ SunOs 4.1 X X (no linemode in server)
+ DYNIX V3.0.17.9 X X (no linemode in server)
+ Ultrix 3.1 X X (no linemode in server)
+ Ultrix 4.0 X X (no linemode in server)
+
+In addition, previous versions have been compiled on the following
+machines, but were not available for testing this version.
+ telnet telnetd
+ SunOs 4.0.3c X X (no linemode in server)
+ BSD 4.3 X X (no linemode in server)
+ DYNIX V3.0.12 X X (no linemode in server)
+
+Februrary 22, 1991:
+
+ Features:
+
+ This version of telnet/telnetd has support for both
+ the AUTHENTICATION and ENCRYPTION options. The
+ AUTHENTICATION option is fairly well defined, and
+ an option number has been assigned to it. The
+ ENCRYPTION option is still in a state of flux; an
+ option number has NOT been assigned to it yet.
+ The code is provided in this release for experimental
+ and testing purposes.
+
+ The telnet "send" command can now be used to send
+ do/dont/will/wont commands, with any telnet option
+ name. The rules for when do/dont/will/wont are sent
+ are still followed, so just because the user requests
+ that one of these be sent doesn't mean that it will
+ be sent...
+
+ The telnet "getstatus" command no longer requires
+ that option printing be enabled to see the response
+ to the "DO STATUS" command.
+
+ A -n flag has been added to telnetd to disable
+ keepalives.
+
+ A new telnet command, "auth" has been added (if
+ AUTHENTICATE is defined). It has four sub-commands,
+ "status", "debug", "disable", "enable" and "help".
+
+ A new telnet command, "encrypt" has been added (if
+ ENCRYPT is defined). It has many sub-commands:
+ "enable", "type", "start", "stop", "input",
+ "-input", "output", "-output", "status", "auto",
+ "verbose", "debug", and "help".
+
+ An "rlogin" interface has been added. If the program
+ is named "rlogin", or the "-r" flag is given, then
+ an rlogin type of interface will be used.
+ ~. Terminates the session
+ ~<susp> Suspend the session
+ ~^] Escape to telnet command mode
+ ~~ Pass through the ~.
+ BUG: If you type the rlogin escape character
+ in the middle of a line while in rlogin
+ mode, you cannot erase it or any characters
+ before it. Hopefully this can be fixed
+ in a future release...
+
+ General changes:
+
+ A "libtelnet.a" has now been created. This libraray
+ contains code that is common to both telnet and
+ telnetd. This is also where library routines that
+ are needed, but are not in the standard C library,
+ are placed.
+
+ The makefiles have been re-done. All of the site
+ specific configuration information has now been put
+ into a single "Config.generic" file, in the top level
+ directory. Changing this one file will take care of
+ all three subdirectories. Also, to add a new/local
+ definition, a "Config.local" file may be created
+ at the top level; if that file exists, the subdirectories
+ will use that file instead of "Config.generic".
+
+ Many 1-2 line functions in commands.c have been
+ removed, and just inserted in-line, or replaced
+ with a macro.
+
+ Bug Fixes:
+
+ The non-termio code in both telnet and telnetd was
+ setting/clearing CTLECH in the sg_flags word. This
+ was incorrect, and has been changed to set/clear the
+ LCTLECH bit in the local mode word.
+
+ The SRCRT #define has been removed. If IP_OPTIONS
+ and IPPROTO_IP are defined on the system, then the
+ source route code is automatically enabled.
+
+ The NO_GETTYTAB #define has been removed; there
+ is a compatability routine that can be built into
+ libtelnet to achive the same results.
+
+ The server, telnetd, has been switched to use getopt()
+ for parsing the argument list.
+
+ The code for getting the input/output speeds via
+ cfgetispeed()/cfgetospeed() was still not quite
+ right in telnet. Posix says if the ispeed is 0,
+ then it is really equal to the ospeed.
+
+ The suboption processing code in telnet now has
+ explicit checks to make sure that we received
+ the entire suboption (telnetd was already doing this).
+
+ The telnet code for processing the terminal type
+ could cause a core dump if an existing connection
+ was closed, and a new connection opened without
+ exiting telnet.
+
+ Telnetd was doing a TCSADRAIN when setting the new
+ terminal settings; This is not good, because it means
+ that the tcsetattr() will hang waiting for output to
+ drain, and telnetd is the only one that will drain
+ the output... The fix is to use TCSANOW which does
+ not wait.
+
+ Telnetd was improperly setting/clearing the ISTRIP
+ flag in the c_lflag field, it should be using the
+ c_iflag field.
+
+ When the child process of telnetd was opening the
+ slave side of the pty, it was re-setting the EXTPROC
+ bit too early, and some of the other initialization
+ code was wiping it out. This would cause telnetd
+ to go out of linemode and into single character mode.
+
+ One instance of leaving linemode in telnetd forgot
+ to send a WILL ECHO to the client, the net result
+ would be that the user would see double character
+ echo.
+
+ If the MODE was being changed several times very
+ quickly, telnetd could get out of sync with the
+ state changes and the returning acks; and wind up
+ being left in the wrong state.
+
+September 14, 1990:
+
+ Switch the client to use getopt() for parsing the
+ argument list. The 4.3Reno getopt.c is included for
+ systems that don't have getopt().
+
+ Use the posix _POSIX_VDISABLE value for what value
+ to use when disabling special characters. If this
+ is undefined, it defaults to 0x3ff.
+
+ For non-termio systems, TIOCSETP was being used to
+ change the state of the terminal. This causes the
+ input queue to be flushed, which we don't want. This
+ is now changed to TIOCSETN.
+
+ Take out the "#ifdef notdef" around the code in the
+ server that generates a "sync" when the pty oputput
+ is flushed. The potential problem is that some older
+ telnet clients may go into an infinate loop when they
+ receive a "sync", if so, the server can be compiled
+ with "NO_URGENT" defined.
+
+ Fix the client where it was setting/clearing the OPOST
+ bit in the c_lflag field, not the c_oflag field.
+
+ Fix the client where it was setting/clearing the ISTRIP
+ bit in the c_lflag field, not the c_iflag field. (On
+ 4.3Reno, this is the ECHOPRT bit in the c_lflag field.)
+ The client also had its interpretation of WILL BINARY
+ and DO BINARY reversed.
+
+ Fix a bug in client that would cause a core dump when
+ attempting to remove the last environment variable.
+
+ In the client, there were a few places were switch()
+ was being passed a character, and if it was a negative
+ value, it could get sign extended, and not match
+ the 8 bit case statements. The fix is to and the
+ switch value with 0xff.
+
+ Add a couple more printoption() calls in the client, I
+ don't think there are any more places were a telnet
+ command can be received and not printed out when
+ "options" is on.
+
+ A new flag has been added to the client, "-a". Currently,
+ this just causes the USER name to be sent across, in
+ the future this may be used to signify that automatic
+ authentication is requested.
+
+ The USER variable is now only sent by the client if
+ the "-a" or "-l user" options are explicity used, or
+ if the user explicitly asks for the "USER" environment
+ variable to be exported. In the server, if it receives
+ the "USER" environment variable, it won't print out the
+ banner message, so that only "Password:" will be printed.
+ This makes the symantics more like rlogin, and should be
+ more familiar to the user. (People are not used to
+ getting a banner message, and then getting just a
+ "Password:" prompt.)
+
+ Re-vamp the code for starting up the child login
+ process. The code was getting ugly, and it was
+ hard to tell what was really going on. What we
+ do now is after the fork(), in the child:
+ 1) make sure we have no controlling tty
+ 2) open and initialize the tty
+ 3) do a setsid()/setpgrp()
+ 4) makes the tty our controlling tty.
+ On some systems, #2 makes the tty our controlling
+ tty, and #4 is a no-op. The parent process does
+ a gets rid of any controlling tty after the child
+ is fork()ed.
+
+ Use the strdup() library routine in telnet, instead
+ of the local savestr() routine. If you don't have
+ strdup(), you need to define NO_STRDUP.
+
+ Add support for ^T (SIGINFO/VSTATUS), found in the
+ 4.3Reno distribution. This maps to the AYT character.
+ You need a 4-line bugfix in the kernel to get this
+ to work properly:
+
+ > *** tty_pty.c.ORG Tue Sep 11 09:41:53 1990
+ > --- tty_pty.c Tue Sep 11 17:48:03 1990
+ > ***************
+ > *** 609,613 ****
+ > if ((tp->t_lflag&NOFLSH) == 0)
+ > ttyflush(tp, FREAD|FWRITE);
+ > ! pgsignal(tp->t_pgrp, *(unsigned int *)data);
+ > return(0);
+ > }
+ > --- 609,616 ----
+ > if ((tp->t_lflag&NOFLSH) == 0)
+ > ttyflush(tp, FREAD|FWRITE);
+ > ! pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
+ > ! if ((*(unsigned int *)data == SIGINFO) &&
+ > ! ((tp->t_lflag&NOKERNINFO) == 0))
+ > ! ttyinfo(tp);
+ > return(0);
+ > }
+
+ The client is now smarter when setting the telnet escape
+ character; it only sets it to one of VEOL and VEOL2 if
+ one of them is undefined, and the other one is not already
+ defined to the telnet escape character.
+
+ Handle TERMIOS systems that have seperate input and output
+ line speed settings imbedded in the flags.
+
+ Many other minor bug fixes.
+
+June 20, 1990:
+ Re-organize makefiles and source tree. The telnet/Source
+ directory is now gone, and all the source that was in
+ telnet/Source is now just in the telnet directory.
+
+ Seperate makefile for each system are now gone. There
+ are two makefiles, Makefile and Makefile.generic.
+ The "Makefile" has the definitions for the various
+ system, and "Makefile.generic" does all the work.
+ There is a variable called "WHAT" that is used to
+ specify what to make. For example, in the telnet
+ directory, you might say:
+ make 4.4bsd WHAT=clean
+ to clean out the directory.
+
+ Add support for the ENVIRON and XDISPLOC options.
+ In order for the server to work, login has to have
+ the "-p" option to preserve environment variables.
+
+ Add the SOFT_TAB and LIT_ECHO modes in the LINEMODE support.
+
+ Add the "-l user" option to command line and open command
+ (This is passed through the ENVIRON option).
+
+ Add the "-e" command line option, for setting the escape
+ character.
+
+ Add the "-D", diagnostic, option to the server. This allows
+ the server to print out debug information, which is very
+ useful when trying to debug a telnet that doesn't have any
+ debugging ability.
+
+ Turn off the literal next character when not in LINEMODE.
+
+ Don't recognize ^Y locally, just pass it through.
+
+ Make minor modifications for Sun4.0 and Sun4.1
+
+ Add support for both FORW1 and FORW2 characters. The
+ telnet escpape character is set to whichever of the
+ two is not being used. If both are in use, the escape
+ character is not set, so when in linemode the user will
+ have to follow the escape character with a <CR> or <EOF)
+ to get it passed through.
+
+ Commands can now be put in single and double quotes, and
+ a backslash is now an escape character. This is needed
+ for allowing arbitrary strings to be assigned to environment
+ variables.
+
+ Switch telnetd to use macros like telnet for keeping
+ track of the state of all the options.
+
+ Fix telnetd's processing of options so that we always do
+ the right processing of the LINEMODE option, regardless
+ of who initiates the request to turn it on. Also, make
+ sure that if the other side went "WILL ECHO" in response
+ to our "DO ECHO", that we send a "DONT ECHO" to get the
+ option turned back off!
+
+ Fix the TERMIOS setting of the terminal speed to handle both
+ BSD's seperate fields, and the SYSV method of CBAUD bits.
+
+ Change how we deal with the other side refusing to enable
+ an option. The sequence used to be: send DO option; receive
+ WONT option; send DONT option. Now, the sequence is: send
+ DO option; receive WONT option. Both should be valid
+ according to the spec, but there has been at least one
+ client implementation of telnet identified that can get
+ really confused by this. (The exact sequence, from a trace
+ on the server side, is (numbers are number of responses that
+ we expect to get after that line...):
+
+ send WILL ECHO 1 (initial request)
+ send WONT ECHO 2 (server is changing state)
+ recv DO ECHO 1 (first reply, ok. expect DONT ECHO next)
+ send WILL ECHO 2 (server changes state again)
+ recv DONT ECHO 1 (second reply, ok. expect DO ECHO next)
+ recv DONT ECHO 0 (third reply, wrong answer. got DONT!!!)
+ *** send WONT ECHO (send WONT to acknowledge the DONT)
+ send WILL ECHO 1 (ask again to enable option)
+ recv DO ECHO 0
+
+ recv DONT ECHO 0
+ send WONT ECHO 1
+ recv DONT ECHO 0
+ recv DO ECHO 1
+ send WILL ECHO 0
+ (and the last 5 lines loop forever)
+
+ The line with the "***" is last of the WILL/DONT/WONT sequence.
+ The change to the server to not generate that makes this same
+ example become:
+
+ send will ECHO 1
+ send wont ECHO 2
+ recv do ECHO 1
+ send will ECHO 2
+ recv dont ECHO 1
+ recv dont ECHO 0
+ recv do ECHO 1
+ send will ECHO 0
+
+ There is other option negotiation going on, and not sending
+ the third part changes some of the timings, but this specific
+ example no longer gets stuck in a loop. The "telnet.state"
+ file has been modified to reflect this change to the algorithm.
+
+ A bunch of miscellaneous bug fixes and changes to make
+ lint happier.
+
+ This version of telnet also has some KERBEROS stuff in
+ it. This has not been tested, it uses an un-authorized
+ telnet option number, and uses an out-of-date version
+ of the (still being defined) AUTHENTICATION option.
+ There is no support for this code, do not enable it.
+
+
+March 1, 1990:
+CHANGES/BUGFIXES SINCE LAST RELEASE:
+ Some support for IP TOS has been added. Requires that the
+ kernel support the IP_TOS socket option (currently this
+ is only in UNICOS 6.0).
+
+ Both telnet and telnetd now use the cc_t typedef. typedefs are
+ included for systems that don't have it (in termios.h).
+
+ SLC_SUSP was not supported properly before. It is now.
+
+ IAC EOF was not translated properly in telnetd for SYSV_TERMIO
+ when not in linemode. It now saves a copy of the VEOF character,
+ so that when ICANON is turned off and we can't trust it anymore
+ (because it is now the VMIN character) we use the saved value.
+
+ There were two missing "break" commands in the linemode
+ processing code in telnetd.
+
+ Telnetd wasn't setting the kernel window size information
+ properly. It was using the rows for both rows and columns...
+
+Questions/comments go to
+ David Borman
+ Cray Research, Inc.
+ 655F Lone Oak Drive
+ Eagan, MN 55123
+ dab@cray.com.
+
+README: You are reading it.
+
+Config.generic:
+ This file contains all the OS specific definitions. It
+ has pre-definitions for many common system types, and is
+ in standard makefile fromat. See the comments at the top
+ of the file for more information.
+
+Config.local:
+ This is not part of the distribution, but if this file exists,
+ it is used instead of "Config.generic". This allows site
+ specific configuration without having to modify the distributed
+ "Config.generic" file.
+
+kern.diff:
+ This file contains the diffs for the changes needed for the
+ kernel to support LINEMODE is the server. These changes are
+ for a 4.3BSD system. You may need to make some changes for
+ your particular system.
+
+ There is a new bit in the terminal state word, TS_EXTPROC.
+ When this bit is set, several aspects of the terminal driver
+ are disabled. Input line editing, character echo, and
+ mapping of signals are all disabled. This allows the telnetd
+ to turn of these functions when in linemode, but still keep
+ track of what state the user wants the terminal to be in.
+
+ New ioctl()s:
+
+ TIOCEXT Turn on/off the TS_EXTPROC bit
+ TIOCGSTATE Get t_state of tty to look at TS_EXTPROC bit
+ TIOCSIG Generate a signal to processes in the
+ current process group of the pty.
+
+ There is a new mode for packet driver, the TIOCPKT_IOCTL bit.
+ When packet mode is turned on in the pty, and the TS_EXTPROC
+ bit is set, then whenever the state of the pty is changed, the
+ next read on the master side of the pty will have the TIOCPKT_IOCTL
+ bit set, and the data will contain the following:
+ struct xx {
+ struct sgttyb a;
+ struct tchars b;
+ struct ltchars c;
+ int t_state;
+ int t_flags;
+ }
+ This allows the process on the server side of the pty to know
+ when the state of the terminal has changed, and what the new
+ state is.
+
+ However, if you define USE_TERMIO or SYSV_TERMIO, the code will
+ expect that the structure returned in the TIOCPKT_IOCTL is
+ the termio/termios structure.
+
+stty.diff:
+ This file contains the changes needed for the stty(1) program
+ to report on the current status of the TS_EXTPROC bit. It also
+ allows the user to turn on/off the TS_EXTPROC bit. This is useful
+ because it allows the user to say "stty -extproc", and the
+ LINEMODE option will be automatically disabled, and saying "stty
+ extproc" will re-enable the LINEMODE option.
+
+telnet.state:
+ Both the client and server have code in them to deal
+ with option negotiation loops. The algorithm that is
+ used is described in this file.
+
+tmac.doc:
+ Macros for use in formatting the man pages on non-4.3Reno
+ systems.
+
+telnet:
+ This directory contains the client code. No kernel changes are
+ needed to use this code.
+
+telnetd:
+ This directory contains the server code. If LINEMODE or KLUDGELINEMODE
+ are defined, then the kernel modifications listed above are needed.
+
+libtelnet:
+ This directory contains code that is common to both the client
+ and the server.
+
+arpa:
+ This directory has a new <arpa/telnet.h>
+
+
+The following TELNET options are supported:
+
+ LINEMODE:
+ The LINEMODE option is supported as per RFC1116. The
+ FORWARDMASK option is not currently supported.
+
+ BINARY: The client has the ability to turn on/off the BINARY
+ option in each direction. Turning on BINARY from
+ server to client causes the LITOUT bit to get set in
+ the terminal driver on both ends, turning on BINARY
+ from the client to the server causes the PASS8 bit
+ to get set in the terminal driver on both ends.
+
+ TERMINAL-TYPE:
+ This is supported as per RFC1091. On the server side,
+ when a terminal type is received, termcap/terminfo
+ is consulted to determine if it is a known terminal
+ type. It keeps requesting terminal types until it
+ gets one that it recongnizes, or hits the end of the
+ list. The server side looks up the entry in the
+ termcap/terminfo data base, and generates a list of
+ names which it then passes one at a time to each
+ request for a terminal type, duplicating the last
+ entry in the list before cycling back to the beginning.
+
+ NAWS: The Negotiate about Window Size, as per RFC 1073.
+
+ TERMINAL-SPEED:
+ Implemented as per RFC 1079
+
+ TOGGLE-FLOW-CONTROL:
+ Implemented as per RFC 1080
+
+ TIMING-MARK:
+ As per RFC 860
+
+ SGA: As per RFC 858
+
+ ECHO: As per RFC 857
+
+ STATUS:
+ The server will send its current status upon
+ request. It does not ask for the clients status.
+ The client will request the servers current status
+ from the "send getstatus" command.
+
+ ENVIRON:
+ This option is currently being defined by the IETF
+ Telnet Working Group, and an RFC has not yet been
+ issued, but should be in the near future...
+
+ X-DISPLAY-LOCATION:
+ This functionality can be done through the ENVIRON
+ option, it is added here for completeness.
+
+ AUTHENTICATION:
+ This option is currently being defined by the IETF
+ Telnet Working Group, and an RFC has not yet been
+ issued. The basic framework is pretty much decided,
+ but the definitions for the specific authentication
+ schemes is still in a state of flux.
+
+ ENCRYPT:
+ This option is currently being defined by the IETF
+ Telnet Working Group, and an RFC has not yet been
+ issued. The draft RFC is still in a state of flux,
+ so this code may change in the future.
diff --git a/usr.bin/telnet/authenc.c b/usr.bin/telnet/authenc.c
new file mode 100644
index 0000000..941a202
--- /dev/null
+++ b/usr.bin/telnet/authenc.c
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#if defined(AUTHENTICATION)
+#include <sys/types.h>
+#include <arpa/telnet.h>
+#include <libtelnet/encrypt.h>
+#include <libtelnet/misc.h>
+
+#include "general.h"
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+
+ int
+net_write(str, len)
+ unsigned char *str;
+ int len;
+{
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, str, len);
+ if (str[0] == IAC && str[1] == SE)
+ printsub('>', &str[2], len-2);
+ return(len);
+ }
+ return(0);
+}
+
+ void
+net_encrypt()
+{
+}
+
+ int
+telnet_spin()
+{
+ return(-1);
+}
+
+ char *
+telnet_getenv(val)
+ char *val;
+{
+ return((char *)env_getvalue((unsigned char *)val));
+}
+
+ char *
+telnet_gets(prompt, result, length, echo)
+ char *prompt;
+ char *result;
+ int length;
+ int echo;
+{
+ extern char *getpass();
+ extern int globalmode;
+ int om = globalmode;
+ char *res;
+
+ TerminalNewMode(-1);
+ if (echo) {
+ printf("%s", prompt);
+ res = fgets(result, length, stdin);
+ } else if (res = getpass(prompt)) {
+ strncpy(result, res, length);
+ res = result;
+ }
+ TerminalNewMode(om);
+ return(res);
+}
+#endif /* defined(AUTHENTICATION) */
diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c
new file mode 100644
index 0000000..9a2a532
--- /dev/null
+++ b/usr.bin/telnet/commands.c
@@ -0,0 +1,2779 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)commands.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#if defined(unix)
+#include <sys/param.h>
+#if defined(CRAY) || defined(sysV88)
+#include <sys/types.h>
+#endif
+#include <sys/file.h>
+#else
+#include <sys/types.h>
+#endif /* defined(unix) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef CRAY
+#include <fcntl.h>
+#endif /* CRAY */
+
+#include <signal.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <varargs.h>
+#include <errno.h>
+
+#include <arpa/telnet.h>
+
+#include "general.h"
+
+#include "ring.h"
+
+#include "externs.h"
+#include "defines.h"
+#include "types.h"
+
+#if !defined(CRAY) && !defined(sysV88)
+#include <netinet/in_systm.h>
+# if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix)
+# include <machine/endian.h>
+# endif /* vax */
+#endif /* !defined(CRAY) && !defined(sysV88) */
+#include <netinet/ip.h>
+
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif MAXHOSTNAMELEN
+
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+int tos = -1;
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+
+char *hostname;
+static char _hostname[MAXHOSTNAMELEN];
+
+extern char *getenv();
+
+extern int isprefix();
+extern char **genget();
+extern int Ambiguous();
+
+static call();
+
+typedef struct {
+ char *name; /* command name */
+ char *help; /* help string (NULL for no help) */
+ int (*handler)(); /* routine which executes command */
+ int needconnect; /* Do we need to be connected to execute? */
+} Command;
+
+static char line[256];
+static char saveline[256];
+static int margc;
+static char *margv[20];
+
+ static void
+makeargv()
+{
+ register char *cp, *cp2, c;
+ register char **argp = margv;
+
+ margc = 0;
+ cp = line;
+ if (*cp == '!') { /* Special case shell escape */
+ strcpy(saveline, line); /* save for shell command */
+ *argp++ = "!"; /* No room in string to get this */
+ margc++;
+ cp++;
+ }
+ while (c = *cp) {
+ register int inquote = 0;
+ while (isspace(c))
+ c = *++cp;
+ if (c == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ for (cp2 = cp; c != '\0'; c = *++cp) {
+ if (inquote) {
+ if (c == inquote) {
+ inquote = 0;
+ continue;
+ }
+ } else {
+ if (c == '\\') {
+ if ((c = *++cp) == '\0')
+ break;
+ } else if (c == '"') {
+ inquote = '"';
+ continue;
+ } else if (c == '\'') {
+ inquote = '\'';
+ continue;
+ } else if (isspace(c))
+ break;
+ }
+ *cp2++ = c;
+ }
+ *cp2 = '\0';
+ if (c == '\0')
+ break;
+ cp++;
+ }
+ *argp++ = 0;
+}
+
+/*
+ * Make a character string into a number.
+ *
+ * Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
+ */
+
+ static
+special(s)
+ register char *s;
+{
+ register char c;
+ char b;
+
+ switch (*s) {
+ case '^':
+ b = *++s;
+ if (b == '?') {
+ c = b | 0x40; /* DEL */
+ } else {
+ c = b & 0x1f;
+ }
+ break;
+ default:
+ c = *s;
+ break;
+ }
+ return c;
+}
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+ static char *
+control(c)
+ register cc_t c;
+{
+ static char buf[5];
+ /*
+ * The only way I could get the Sun 3.5 compiler
+ * to shut up about
+ * if ((unsigned int)c >= 0x80)
+ * was to assign "c" to an unsigned int variable...
+ * Arggg....
+ */
+ register unsigned int uic = (unsigned int)c;
+
+ if (uic == 0x7f)
+ return ("^?");
+ if (c == (cc_t)_POSIX_VDISABLE) {
+ return "off";
+ }
+ if (uic >= 0x80) {
+ buf[0] = '\\';
+ buf[1] = ((c>>6)&07) + '0';
+ buf[2] = ((c>>3)&07) + '0';
+ buf[3] = (c&07) + '0';
+ buf[4] = 0;
+ } else if (uic >= 0x20) {
+ buf[0] = c;
+ buf[1] = 0;
+ } else {
+ buf[0] = '^';
+ buf[1] = '@'+c;
+ buf[2] = 0;
+ }
+ return (buf);
+}
+
+
+
+/*
+ * The following are data structures and routines for
+ * the "send" command.
+ *
+ */
+
+struct sendlist {
+ char *name; /* How user refers to it (case independent) */
+ char *help; /* Help information (0 ==> no help) */
+ int needconnect; /* Need to be connected */
+ int narg; /* Number of arguments */
+ int (*handler)(); /* Routine to perform (for special ops) */
+ int nbyte; /* Number of bytes to send this command */
+ int what; /* Character to be sent (<0 ==> special) */
+};
+
+
+static int
+ send_esc P((void)),
+ send_help P((void)),
+ send_docmd P((char *)),
+ send_dontcmd P((char *)),
+ send_willcmd P((char *)),
+ send_wontcmd P((char *));
+
+static struct sendlist Sendlist[] = {
+ { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO },
+ { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT },
+ { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK },
+ { "break", 0, 1, 0, 0, 2, BREAK },
+ { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC },
+ { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL },
+ { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 },
+ { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA },
+ { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP },
+ { "intp", 0, 1, 0, 0, 2, IP },
+ { "interrupt", 0, 1, 0, 0, 2, IP },
+ { "intr", 0, 1, 0, 0, 2, IP },
+ { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP },
+ { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR },
+ { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT },
+ { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP },
+ { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF },
+ { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 },
+ { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 },
+ { "?", "Display send options", 0, 0, send_help, 0, 0 },
+ { "help", 0, 0, 0, send_help, 0, 0 },
+ { "do", 0, 0, 1, send_docmd, 3, 0 },
+ { "dont", 0, 0, 1, send_dontcmd, 3, 0 },
+ { "will", 0, 0, 1, send_willcmd, 3, 0 },
+ { "wont", 0, 0, 1, send_wontcmd, 3, 0 },
+ { 0 }
+};
+
+#define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
+ sizeof(struct sendlist)))
+
+ static int
+sendcmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int count; /* how many bytes we are going to need to send */
+ int i;
+ int question = 0; /* was at least one argument a question */
+ struct sendlist *s; /* pointer to current command */
+ int success = 0;
+ int needconnect = 0;
+
+ if (argc < 2) {
+ printf("need at least one argument for 'send' command\n");
+ printf("'send ?' for help\n");
+ return 0;
+ }
+ /*
+ * First, validate all the send arguments.
+ * In addition, we see how much space we are going to need, and
+ * whether or not we will be doing a "SYNCH" operation (which
+ * flushes the network queue).
+ */
+ count = 0;
+ for (i = 1; i < argc; i++) {
+ s = GETSEND(argv[i]);
+ if (s == 0) {
+ printf("Unknown send argument '%s'\n'send ?' for help.\n",
+ argv[i]);
+ return 0;
+ } else if (Ambiguous(s)) {
+ printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
+ argv[i]);
+ return 0;
+ }
+ if (i + s->narg >= argc) {
+ fprintf(stderr,
+ "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n",
+ s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
+ return 0;
+ }
+ count += s->nbyte;
+ if (s->handler == send_help) {
+ send_help();
+ return 0;
+ }
+
+ i += s->narg;
+ needconnect += s->needconnect;
+ }
+ if (!connected && needconnect) {
+ printf("?Need to be connected first.\n");
+ printf("'send ?' for help\n");
+ return 0;
+ }
+ /* Now, do we have enough room? */
+ if (NETROOM() < count) {
+ printf("There is not enough room in the buffer TO the network\n");
+ printf("to process your request. Nothing will be done.\n");
+ printf("('send synch' will throw away most data in the network\n");
+ printf("buffer, if this might help.)\n");
+ return 0;
+ }
+ /* OK, they are all OK, now go through again and actually send */
+ count = 0;
+ for (i = 1; i < argc; i++) {
+ if ((s = GETSEND(argv[i])) == 0) {
+ fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
+ (void) quit();
+ /*NOTREACHED*/
+ }
+ if (s->handler) {
+ count++;
+ success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
+ (s->narg > 1) ? argv[i+2] : 0);
+ i += s->narg;
+ } else {
+ NET2ADD(IAC, s->what);
+ printoption("SENT", IAC, s->what);
+ }
+ }
+ return (count == success);
+}
+
+ static int
+send_esc()
+{
+ NETADD(escape);
+ return 1;
+}
+
+ static int
+send_docmd(name)
+ char *name;
+{
+ return(send_tncmd(send_do, "do", name));
+}
+
+ static int
+send_dontcmd(name)
+ char *name;
+{
+ return(send_tncmd(send_dont, "dont", name));
+}
+ static int
+send_willcmd(name)
+ char *name;
+{
+ return(send_tncmd(send_will, "will", name));
+}
+ static int
+send_wontcmd(name)
+ char *name;
+{
+ return(send_tncmd(send_wont, "wont", name));
+}
+
+ int
+send_tncmd(func, cmd, name)
+ void (*func)();
+ char *cmd, *name;
+{
+ char **cpp;
+ extern char *telopts[];
+ register int val = 0;
+
+ if (isprefix(name, "help") || isprefix(name, "?")) {
+ register int col, len;
+
+ printf("Usage: send %s <value|option>\n", cmd);
+ printf("\"value\" must be from 0 to 255\n");
+ printf("Valid options are:\n\t");
+
+ col = 8;
+ for (cpp = telopts; *cpp; cpp++) {
+ len = strlen(*cpp) + 3;
+ if (col + len > 65) {
+ printf("\n\t");
+ col = 8;
+ }
+ printf(" \"%s\"", *cpp);
+ col += len;
+ }
+ printf("\n");
+ return 0;
+ }
+ cpp = (char **)genget(name, telopts, sizeof(char *));
+ if (Ambiguous(cpp)) {
+ fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n",
+ name, cmd);
+ return 0;
+ }
+ if (cpp) {
+ val = cpp - telopts;
+ } else {
+ register char *cp = name;
+
+ while (*cp >= '0' && *cp <= '9') {
+ val *= 10;
+ val += *cp - '0';
+ cp++;
+ }
+ if (*cp != 0) {
+ fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n",
+ name, cmd);
+ return 0;
+ } else if (val < 0 || val > 255) {
+ fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n",
+ name, cmd);
+ return 0;
+ }
+ }
+ if (!connected) {
+ printf("?Need to be connected first.\n");
+ return 0;
+ }
+ (*func)(val, 1);
+ return 1;
+}
+
+ static int
+send_help()
+{
+ struct sendlist *s; /* pointer to current command */
+ for (s = Sendlist; s->name; s++) {
+ if (s->help)
+ printf("%-15s %s\n", s->name, s->help);
+ }
+ return(0);
+}
+
+/*
+ * The following are the routines and data structures referred
+ * to by the arguments to the "toggle" command.
+ */
+
+ static int
+lclchars()
+{
+ donelclchars = 1;
+ return 1;
+}
+
+ static int
+togdebug()
+{
+#ifndef NOT43
+ if (net > 0 &&
+ (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
+ perror("setsockopt (SO_DEBUG)");
+ }
+#else /* NOT43 */
+ if (debug) {
+ if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
+ perror("setsockopt (SO_DEBUG)");
+ } else
+ printf("Cannot turn off socket debugging\n");
+#endif /* NOT43 */
+ return 1;
+}
+
+
+ static int
+togcrlf()
+{
+ if (crlf) {
+ printf("Will send carriage returns as telnet <CR><LF>.\n");
+ } else {
+ printf("Will send carriage returns as telnet <CR><NUL>.\n");
+ }
+ return 1;
+}
+
+int binmode;
+
+ static int
+togbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val >= 0) {
+ binmode = val;
+ } else {
+ if (my_want_state_is_will(TELOPT_BINARY) &&
+ my_want_state_is_do(TELOPT_BINARY)) {
+ binmode = 1;
+ } else if (my_want_state_is_wont(TELOPT_BINARY) &&
+ my_want_state_is_dont(TELOPT_BINARY)) {
+ binmode = 0;
+ }
+ val = binmode ? 0 : 1;
+ }
+
+ if (val == 1) {
+ if (my_want_state_is_will(TELOPT_BINARY) &&
+ my_want_state_is_do(TELOPT_BINARY)) {
+ printf("Already operating in binary mode with remote host.\n");
+ } else {
+ printf("Negotiating binary mode with remote host.\n");
+ tel_enter_binary(3);
+ }
+ } else {
+ if (my_want_state_is_wont(TELOPT_BINARY) &&
+ my_want_state_is_dont(TELOPT_BINARY)) {
+ printf("Already in network ascii mode with remote host.\n");
+ } else {
+ printf("Negotiating network ascii mode with remote host.\n");
+ tel_leave_binary(3);
+ }
+ }
+ return 1;
+}
+
+ static int
+togrbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val == -1)
+ val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
+
+ if (val == 1) {
+ if (my_want_state_is_do(TELOPT_BINARY)) {
+ printf("Already receiving in binary mode.\n");
+ } else {
+ printf("Negotiating binary mode on input.\n");
+ tel_enter_binary(1);
+ }
+ } else {
+ if (my_want_state_is_dont(TELOPT_BINARY)) {
+ printf("Already receiving in network ascii mode.\n");
+ } else {
+ printf("Negotiating network ascii mode on input.\n");
+ tel_leave_binary(1);
+ }
+ }
+ return 1;
+}
+
+ static int
+togxbinary(val)
+ int val;
+{
+ donebinarytoggle = 1;
+
+ if (val == -1)
+ val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
+
+ if (val == 1) {
+ if (my_want_state_is_will(TELOPT_BINARY)) {
+ printf("Already transmitting in binary mode.\n");
+ } else {
+ printf("Negotiating binary mode on output.\n");
+ tel_enter_binary(2);
+ }
+ } else {
+ if (my_want_state_is_wont(TELOPT_BINARY)) {
+ printf("Already transmitting in network ascii mode.\n");
+ } else {
+ printf("Negotiating network ascii mode on output.\n");
+ tel_leave_binary(2);
+ }
+ }
+ return 1;
+}
+
+
+static int togglehelp P((void));
+#if defined(AUTHENTICATION)
+extern int auth_togdebug P((int));
+#endif
+
+struct togglelist {
+ char *name; /* name of toggle */
+ char *help; /* help message */
+ int (*handler)(); /* routine to do actual setting */
+ int *variable;
+ char *actionexplanation;
+};
+
+static struct togglelist Togglelist[] = {
+ { "autoflush",
+ "flushing of output when sending interrupt characters",
+ 0,
+ &autoflush,
+ "flush output when sending interrupt characters" },
+ { "autosynch",
+ "automatic sending of interrupt characters in urgent mode",
+ 0,
+ &autosynch,
+ "send interrupt characters in urgent mode" },
+#if defined(AUTHENTICATION)
+ { "autologin",
+ "automatic sending of login and/or authentication info",
+ 0,
+ &autologin,
+ "send login name and/or authentication information" },
+ { "authdebug",
+ "Toggle authentication debugging",
+ auth_togdebug,
+ 0,
+ "print authentication debugging information" },
+#endif
+ { "skiprc",
+ "don't read ~/.telnetrc file",
+ 0,
+ &skiprc,
+ "skip reading of ~/.telnetrc file" },
+ { "binary",
+ "sending and receiving of binary data",
+ togbinary,
+ 0,
+ 0 },
+ { "inbinary",
+ "receiving of binary data",
+ togrbinary,
+ 0,
+ 0 },
+ { "outbinary",
+ "sending of binary data",
+ togxbinary,
+ 0,
+ 0 },
+ { "crlf",
+ "sending carriage returns as telnet <CR><LF>",
+ togcrlf,
+ &crlf,
+ 0 },
+ { "crmod",
+ "mapping of received carriage returns",
+ 0,
+ &crmod,
+ "map carriage return on output" },
+ { "localchars",
+ "local recognition of certain control characters",
+ lclchars,
+ &localchars,
+ "recognize certain control characters" },
+ { " ", "", 0 }, /* empty line */
+#if defined(unix) && defined(TN3270)
+ { "apitrace",
+ "(debugging) toggle tracing of API transactions",
+ 0,
+ &apitrace,
+ "trace API transactions" },
+ { "cursesdata",
+ "(debugging) toggle printing of hexadecimal curses data",
+ 0,
+ &cursesdata,
+ "print hexadecimal representation of curses data" },
+#endif /* defined(unix) && defined(TN3270) */
+ { "debug",
+ "debugging",
+ togdebug,
+ &debug,
+ "turn on socket level debugging" },
+ { "netdata",
+ "printing of hexadecimal network data (debugging)",
+ 0,
+ &netdata,
+ "print hexadecimal representation of network traffic" },
+ { "prettydump",
+ "output of \"netdata\" to user readable format (debugging)",
+ 0,
+ &prettydump,
+ "print user readable output for \"netdata\"" },
+ { "options",
+ "viewing of options processing (debugging)",
+ 0,
+ &showoptions,
+ "show option processing" },
+#if defined(unix)
+ { "termdata",
+ "(debugging) toggle printing of hexadecimal terminal data",
+ 0,
+ &termdata,
+ "print hexadecimal representation of terminal traffic" },
+#endif /* defined(unix) */
+ { "?",
+ 0,
+ togglehelp },
+ { "help",
+ 0,
+ togglehelp },
+ { 0 }
+};
+
+ static int
+togglehelp()
+{
+ struct togglelist *c;
+
+ for (c = Togglelist; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ printf("%-15s toggle %s\n", c->name, c->help);
+ else
+ printf("\n");
+ }
+ }
+ printf("\n");
+ printf("%-15s %s\n", "?", "display help information");
+ return 0;
+}
+
+ static void
+settogglehelp(set)
+ int set;
+{
+ struct togglelist *c;
+
+ for (c = Togglelist; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
+ c->help);
+ else
+ printf("\n");
+ }
+ }
+}
+
+#define GETTOGGLE(name) (struct togglelist *) \
+ genget(name, (char **) Togglelist, sizeof(struct togglelist))
+
+ static int
+toggle(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int retval = 1;
+ char *name;
+ struct togglelist *c;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "Need an argument to 'toggle' command. 'toggle ?' for help.\n");
+ return 0;
+ }
+ argc--;
+ argv++;
+ while (argc--) {
+ name = *argv++;
+ c = GETTOGGLE(name);
+ if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
+ name);
+ return 0;
+ } else if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
+ name);
+ return 0;
+ } else {
+ if (c->variable) {
+ *c->variable = !*c->variable; /* invert it */
+ if (c->actionexplanation) {
+ printf("%s %s.\n", *c->variable? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler) {
+ retval &= (*c->handler)(-1);
+ }
+ }
+ }
+ return retval;
+}
+
+/*
+ * The following perform the "set" command.
+ */
+
+#ifdef USE_TERMIO
+struct termio new_tc = { 0 };
+#endif
+
+struct setlist {
+ char *name; /* name */
+ char *help; /* help information */
+ void (*handler)();
+ cc_t *charp; /* where it is located at */
+};
+
+static struct setlist Setlist[] = {
+#ifdef KLUDGELINEMODE
+ { "echo", "character to toggle local echoing on/off", 0, &echoc },
+#endif
+ { "escape", "character to escape back to telnet command mode", 0, &escape },
+ { "rlogin", "rlogin escape character", 0, &rlogin },
+ { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
+ { " ", "" },
+ { " ", "The following need 'localchars' to be toggled true", 0, 0 },
+ { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp },
+ { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp },
+ { "quit", "character to cause an Abort process", 0, termQuitCharp },
+ { "eof", "character to cause an EOF ", 0, termEofCharp },
+ { " ", "" },
+ { " ", "The following are for local editing in linemode", 0, 0 },
+ { "erase", "character to use to erase a character", 0, termEraseCharp },
+ { "kill", "character to use to erase a line", 0, termKillCharp },
+ { "lnext", "character to use for literal next", 0, termLiteralNextCharp },
+ { "susp", "character to cause a Suspend Process", 0, termSuspCharp },
+ { "reprint", "character to use for line reprint", 0, termRprntCharp },
+ { "worderase", "character to use to erase a word", 0, termWerasCharp },
+ { "start", "character to use for XON", 0, termStartCharp },
+ { "stop", "character to use for XOFF", 0, termStopCharp },
+ { "forw1", "alternate end of line character", 0, termForw1Charp },
+ { "forw2", "alternate end of line character", 0, termForw2Charp },
+ { "ayt", "alternate AYT character", 0, termAytCharp },
+ { 0 }
+};
+
+#if defined(CRAY) && !defined(__STDC__)
+/* Work around compiler bug in pcc 4.1.5 */
+ void
+_setlist_init()
+{
+#ifndef KLUDGELINEMODE
+#define N 5
+#else
+#define N 6
+#endif
+ Setlist[N+0].charp = &termFlushChar;
+ Setlist[N+1].charp = &termIntChar;
+ Setlist[N+2].charp = &termQuitChar;
+ Setlist[N+3].charp = &termEofChar;
+ Setlist[N+6].charp = &termEraseChar;
+ Setlist[N+7].charp = &termKillChar;
+ Setlist[N+8].charp = &termLiteralNextChar;
+ Setlist[N+9].charp = &termSuspChar;
+ Setlist[N+10].charp = &termRprntChar;
+ Setlist[N+11].charp = &termWerasChar;
+ Setlist[N+12].charp = &termStartChar;
+ Setlist[N+13].charp = &termStopChar;
+ Setlist[N+14].charp = &termForw1Char;
+ Setlist[N+15].charp = &termForw2Char;
+ Setlist[N+16].charp = &termAytChar;
+#undef N
+}
+#endif /* defined(CRAY) && !defined(__STDC__) */
+
+ static struct setlist *
+getset(name)
+ char *name;
+{
+ return (struct setlist *)
+ genget(name, (char **) Setlist, sizeof(struct setlist));
+}
+
+ void
+set_escape_char(s)
+ char *s;
+{
+ if (rlogin != _POSIX_VDISABLE) {
+ rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
+ printf("Telnet rlogin escape character is '%s'.\n",
+ control(rlogin));
+ } else {
+ escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
+ printf("Telnet escape character is '%s'.\n", control(escape));
+ }
+}
+
+ static int
+setcmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int value;
+ struct setlist *ct;
+ struct togglelist *c;
+
+ if (argc < 2 || argc > 3) {
+ printf("Format is 'set Name Value'\n'set ?' for help.\n");
+ return 0;
+ }
+ if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
+ for (ct = Setlist; ct->name; ct++)
+ printf("%-15s %s\n", ct->name, ct->help);
+ printf("\n");
+ settogglehelp(1);
+ printf("%-15s %s\n", "?", "display help information");
+ return 0;
+ }
+
+ ct = getset(argv[1]);
+ if (ct == 0) {
+ c = GETTOGGLE(argv[1]);
+ if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
+ argv[1]);
+ return 0;
+ } else if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (c->variable) {
+ if ((argc == 2) || (strcmp("on", argv[2]) == 0))
+ *c->variable = 1;
+ else if (strcmp("off", argv[2]) == 0)
+ *c->variable = 0;
+ else {
+ printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n");
+ return 0;
+ }
+ if (c->actionexplanation) {
+ printf("%s %s.\n", *c->variable? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler)
+ (*c->handler)(1);
+ } else if (argc != 3) {
+ printf("Format is 'set Name Value'\n'set ?' for help.\n");
+ return 0;
+ } else if (Ambiguous(ct)) {
+ fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
+ argv[1]);
+ return 0;
+ } else if (ct->handler) {
+ (*ct->handler)(argv[2]);
+ printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp);
+ } else {
+ if (strcmp("off", argv[2])) {
+ value = special(argv[2]);
+ } else {
+ value = _POSIX_VDISABLE;
+ }
+ *(ct->charp) = (cc_t)value;
+ printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+ }
+ slc_check();
+ return 1;
+}
+
+ static int
+unsetcmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct setlist *ct;
+ struct togglelist *c;
+ register char *name;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "Need an argument to 'unset' command. 'unset ?' for help.\n");
+ return 0;
+ }
+ if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
+ for (ct = Setlist; ct->name; ct++)
+ printf("%-15s %s\n", ct->name, ct->help);
+ printf("\n");
+ settogglehelp(0);
+ printf("%-15s %s\n", "?", "display help information");
+ return 0;
+ }
+
+ argc--;
+ argv++;
+ while (argc--) {
+ name = *argv++;
+ ct = getset(name);
+ if (ct == 0) {
+ c = GETTOGGLE(name);
+ if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n",
+ name);
+ return 0;
+ } else if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+ name);
+ return 0;
+ }
+ if (c->variable) {
+ *c->variable = 0;
+ if (c->actionexplanation) {
+ printf("%s %s.\n", *c->variable? "Will" : "Won't",
+ c->actionexplanation);
+ }
+ }
+ if (c->handler)
+ (*c->handler)(0);
+ } else if (Ambiguous(ct)) {
+ fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n",
+ name);
+ return 0;
+ } else if (ct->handler) {
+ (*ct->handler)(0);
+ printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp);
+ } else {
+ *(ct->charp) = _POSIX_VDISABLE;
+ printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
+ }
+ }
+ return 1;
+}
+
+/*
+ * The following are the data structures and routines for the
+ * 'mode' command.
+ */
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+
+ static int
+dokludgemode()
+{
+ kludgelinemode = 1;
+ send_wont(TELOPT_LINEMODE, 1);
+ send_dont(TELOPT_SGA, 1);
+ send_dont(TELOPT_ECHO, 1);
+}
+#endif
+
+ static int
+dolinemode()
+{
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ send_dont(TELOPT_SGA, 1);
+#endif
+ send_will(TELOPT_LINEMODE, 1);
+ send_dont(TELOPT_ECHO, 1);
+ return 1;
+}
+
+ static int
+docharmode()
+{
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ send_do(TELOPT_SGA, 1);
+ else
+#endif
+ send_wont(TELOPT_LINEMODE, 1);
+ send_do(TELOPT_ECHO, 1);
+ return 1;
+}
+
+ static int
+dolmmode(bit, on)
+ int bit, on;
+{
+ unsigned char c;
+ extern int linemode;
+
+ if (my_want_state_is_wont(TELOPT_LINEMODE)) {
+ printf("?Need to have LINEMODE option enabled first.\n");
+ printf("'mode ?' for help.\n");
+ return 0;
+ }
+
+ if (on)
+ c = (linemode | bit);
+ else
+ c = (linemode & ~bit);
+ lm_mode(&c, 1, 1);
+ return 1;
+}
+
+ int
+setmode(bit)
+{
+ return dolmmode(bit, 1);
+}
+
+ int
+clearmode(bit)
+{
+ return dolmmode(bit, 0);
+}
+
+struct modelist {
+ char *name; /* command name */
+ char *help; /* help string */
+ int (*handler)(); /* routine which executes command */
+ int needconnect; /* Do we need to be connected to execute? */
+ int arg1;
+};
+
+extern int modehelp();
+
+static struct modelist ModeList[] = {
+ { "character", "Disable LINEMODE option", docharmode, 1 },
+#ifdef KLUDGELINEMODE
+ { "", "(or disable obsolete line-by-line mode)", 0 },
+#endif
+ { "line", "Enable LINEMODE option", dolinemode, 1 },
+#ifdef KLUDGELINEMODE
+ { "", "(or enable obsolete line-by-line mode)", 0 },
+#endif
+ { "", "", 0 },
+ { "", "These require the LINEMODE option to be enabled", 0 },
+ { "isig", "Enable signal trapping", setmode, 1, MODE_TRAPSIG },
+ { "+isig", 0, setmode, 1, MODE_TRAPSIG },
+ { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG },
+ { "edit", "Enable character editing", setmode, 1, MODE_EDIT },
+ { "+edit", 0, setmode, 1, MODE_EDIT },
+ { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT },
+ { "softtabs", "Enable tab expansion", setmode, 1, MODE_SOFT_TAB },
+ { "+softtabs", 0, setmode, 1, MODE_SOFT_TAB },
+ { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB },
+ { "litecho", "Enable literal character echo", setmode, 1, MODE_LIT_ECHO },
+ { "+litecho", 0, setmode, 1, MODE_LIT_ECHO },
+ { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO },
+ { "help", 0, modehelp, 0 },
+#ifdef KLUDGELINEMODE
+ { "kludgeline", 0, dokludgemode, 1 },
+#endif
+ { "", "", 0 },
+ { "?", "Print help information", modehelp, 0 },
+ { 0 },
+};
+
+
+ int
+modehelp()
+{
+ struct modelist *mt;
+
+ printf("format is: 'mode Mode', where 'Mode' is one of:\n\n");
+ for (mt = ModeList; mt->name; mt++) {
+ if (mt->help) {
+ if (*mt->help)
+ printf("%-15s %s\n", mt->name, mt->help);
+ else
+ printf("\n");
+ }
+ }
+ return 0;
+}
+
+#define GETMODECMD(name) (struct modelist *) \
+ genget(name, (char **) ModeList, sizeof(struct modelist))
+
+ static int
+modecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct modelist *mt;
+
+ if (argc != 2) {
+ printf("'mode' command requires an argument\n");
+ printf("'mode ?' for help.\n");
+ } else if ((mt = GETMODECMD(argv[1])) == 0) {
+ fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
+ } else if (Ambiguous(mt)) {
+ fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
+ } else if (mt->needconnect && !connected) {
+ printf("?Need to be connected first.\n");
+ printf("'mode ?' for help.\n");
+ } else if (mt->handler) {
+ return (*mt->handler)(mt->arg1);
+ }
+ return 0;
+}
+
+/*
+ * The following data structures and routines implement the
+ * "display" command.
+ */
+
+ static int
+display(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct togglelist *tl;
+ struct setlist *sl;
+
+#define dotog(tl) if (tl->variable && tl->actionexplanation) { \
+ if (*tl->variable) { \
+ printf("will"); \
+ } else { \
+ printf("won't"); \
+ } \
+ printf(" %s.\n", tl->actionexplanation); \
+ }
+
+#define doset(sl) if (sl->name && *sl->name != ' ') { \
+ if (sl->handler == 0) \
+ printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \
+ else \
+ printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \
+ }
+
+ if (argc == 1) {
+ for (tl = Togglelist; tl->name; tl++) {
+ dotog(tl);
+ }
+ printf("\n");
+ for (sl = Setlist; sl->name; sl++) {
+ doset(sl);
+ }
+ } else {
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ sl = getset(argv[i]);
+ tl = GETTOGGLE(argv[i]);
+ if (Ambiguous(sl) || Ambiguous(tl)) {
+ printf("?Ambiguous argument '%s'.\n", argv[i]);
+ return 0;
+ } else if (!sl && !tl) {
+ printf("?Unknown argument '%s'.\n", argv[i]);
+ return 0;
+ } else {
+ if (tl) {
+ dotog(tl);
+ }
+ if (sl) {
+ doset(sl);
+ }
+ }
+ }
+ }
+/*@*/optionstatus();
+ return 1;
+#undef doset
+#undef dotog
+}
+
+/*
+ * The following are the data structures, and many of the routines,
+ * relating to command processing.
+ */
+
+/*
+ * Set the escape character.
+ */
+ static int
+setescape(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ char buf[50];
+
+ printf(
+ "Deprecated usage - please use 'set escape%s%s' in the future.\n",
+ (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
+ if (argc > 2)
+ arg = argv[1];
+ else {
+ printf("new escape character: ");
+ (void) fgets(buf, sizeof(buf), stdin);
+ arg = buf;
+ }
+ if (arg[0] != '\0')
+ escape = arg[0];
+ if (!In3270) {
+ printf("Escape character is '%s'.\n", control(escape));
+ }
+ (void) fflush(stdout);
+ return 1;
+}
+
+ /*VARARGS*/
+ static int
+togcrmod()
+{
+ crmod = !crmod;
+ printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
+ printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
+ (void) fflush(stdout);
+ return 1;
+}
+
+ /*VARARGS*/
+ int
+suspend()
+{
+#ifdef SIGTSTP
+ setcommandmode();
+ {
+ long oldrows, oldcols, newrows, newcols, err;
+
+ err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+ (void) kill(0, SIGTSTP);
+ /*
+ * If we didn't get the window size before the SUSPEND, but we
+ * can get them now (???), then send the NAWS to make sure that
+ * we are set up for the right window size.
+ */
+ if (TerminalWindowSize(&newrows, &newcols) && connected &&
+ (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+ sendnaws();
+ }
+ }
+ /* reget parameters in case they were changed */
+ TerminalSaveState();
+ setconnmode(0);
+#else
+ printf("Suspend is not supported. Try the '!' command instead\n");
+#endif
+ return 1;
+}
+
+#if !defined(TN3270)
+ /*ARGSUSED*/
+ int
+shell(argc, argv)
+ int argc;
+ char *argv[];
+{
+ long oldrows, oldcols, newrows, newcols, err;
+
+ setcommandmode();
+
+ err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
+ switch(vfork()) {
+ case -1:
+ perror("Fork failed\n");
+ break;
+
+ case 0:
+ {
+ /*
+ * Fire up the shell in the child.
+ */
+ register char *shellp, *shellname;
+ extern char *rindex();
+
+ shellp = getenv("SHELL");
+ if (shellp == NULL)
+ shellp = "/bin/sh";
+ if ((shellname = rindex(shellp, '/')) == 0)
+ shellname = shellp;
+ else
+ shellname++;
+ if (argc > 1)
+ execl(shellp, shellname, "-c", &saveline[1], 0);
+ else
+ execl(shellp, shellname, 0);
+ perror("Execl");
+ _exit(1);
+ }
+ default:
+ (void)wait((int *)0); /* Wait for the shell to complete */
+
+ if (TerminalWindowSize(&newrows, &newcols) && connected &&
+ (err || ((oldrows != newrows) || (oldcols != newcols)))) {
+ sendnaws();
+ }
+ break;
+ }
+ return 1;
+}
+#else /* !defined(TN3270) */
+extern int shell();
+#endif /* !defined(TN3270) */
+
+ /*VARARGS*/
+ static
+bye(argc, argv)
+ int argc; /* Number of arguments */
+ char *argv[]; /* arguments */
+{
+ extern int resettermname;
+
+ if (connected) {
+ (void) shutdown(net, 2);
+ printf("Connection closed.\n");
+ (void) NetClose(net);
+ connected = 0;
+ resettermname = 1;
+#if defined(AUTHENTICATION)
+ auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) */
+ /* reset options */
+ tninit();
+#if defined(TN3270)
+ SetIn3270(); /* Get out of 3270 mode */
+#endif /* defined(TN3270) */
+ }
+ if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
+ longjmp(toplevel, 1);
+ /* NOTREACHED */
+ }
+ return 1; /* Keep lint, etc., happy */
+}
+
+/*VARARGS*/
+quit()
+{
+ (void) call(bye, "bye", "fromquit", 0);
+ Exit(0);
+ /*NOTREACHED*/
+}
+
+/*VARARGS*/
+ int
+logout()
+{
+ send_do(TELOPT_LOGOUT, 1);
+ (void) netflush();
+ return 1;
+}
+
+
+/*
+ * The SLC command.
+ */
+
+struct slclist {
+ char *name;
+ char *help;
+ void (*handler)();
+ int arg;
+};
+
+static void slc_help();
+
+struct slclist SlcList[] = {
+ { "export", "Use local special character definitions",
+ slc_mode_export, 0 },
+ { "import", "Use remote special character definitions",
+ slc_mode_import, 1 },
+ { "check", "Verify remote special character definitions",
+ slc_mode_import, 0 },
+ { "help", 0, slc_help, 0 },
+ { "?", "Print help information", slc_help, 0 },
+ { 0 },
+};
+
+ static void
+slc_help()
+{
+ struct slclist *c;
+
+ for (c = SlcList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ printf("%-15s %s\n", c->name, c->help);
+ else
+ printf("\n");
+ }
+ }
+}
+
+ static struct slclist *
+getslc(name)
+ char *name;
+{
+ return (struct slclist *)
+ genget(name, (char **) SlcList, sizeof(struct slclist));
+}
+
+ static
+slccmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct slclist *c;
+
+ if (argc != 2) {
+ fprintf(stderr,
+ "Need an argument to 'slc' command. 'slc ?' for help.\n");
+ return 0;
+ }
+ c = getslc(argv[1]);
+ if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ (*c->handler)(c->arg);
+ slcstate();
+ return 1;
+}
+
+/*
+ * The ENVIRON command.
+ */
+
+struct envlist {
+ char *name;
+ char *help;
+ void (*handler)();
+ int narg;
+};
+
+extern struct env_lst *
+ env_define P((unsigned char *, unsigned char *));
+extern void
+ env_undefine P((unsigned char *)),
+ env_export P((unsigned char *)),
+ env_unexport P((unsigned char *)),
+ env_send P((unsigned char *)),
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+ env_varval P((unsigned char *)),
+#endif
+ env_list P((void));
+static void
+ env_help P((void));
+
+struct envlist EnvList[] = {
+ { "define", "Define an environment variable",
+ (void (*)())env_define, 2 },
+ { "undefine", "Undefine an environment variable",
+ env_undefine, 1 },
+ { "export", "Mark an environment variable for automatic export",
+ env_export, 1 },
+ { "unexport", "Don't mark an environment variable for automatic export",
+ env_unexport, 1 },
+ { "send", "Send an environment variable", env_send, 1 },
+ { "list", "List the current environment variables",
+ env_list, 0 },
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+ { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
+ env_varval, 1 },
+#endif
+ { "help", 0, env_help, 0 },
+ { "?", "Print help information", env_help, 0 },
+ { 0 },
+};
+
+ static void
+env_help()
+{
+ struct envlist *c;
+
+ for (c = EnvList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ printf("%-15s %s\n", c->name, c->help);
+ else
+ printf("\n");
+ }
+ }
+}
+
+ static struct envlist *
+getenvcmd(name)
+ char *name;
+{
+ return (struct envlist *)
+ genget(name, (char **) EnvList, sizeof(struct envlist));
+}
+
+env_cmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct envlist *c;
+
+ if (argc < 2) {
+ fprintf(stderr,
+ "Need an argument to 'environ' command. 'environ ?' for help.\n");
+ return 0;
+ }
+ c = getenvcmd(argv[1]);
+ if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (c->narg + 2 != argc) {
+ fprintf(stderr,
+ "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n",
+ c->narg < argc + 2 ? "only " : "",
+ c->narg, c->narg == 1 ? "" : "s", c->name);
+ return 0;
+ }
+ (*c->handler)(argv[2], argv[3]);
+ return 1;
+}
+
+struct env_lst {
+ struct env_lst *next; /* pointer to next structure */
+ struct env_lst *prev; /* pointer to previous structure */
+ unsigned char *var; /* pointer to variable name */
+ unsigned char *value; /* pointer to variable value */
+ int export; /* 1 -> export with default list of variables */
+ int welldefined; /* A well defined variable */
+};
+
+struct env_lst envlisthead;
+
+ struct env_lst *
+env_find(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ for (ep = envlisthead.next; ep; ep = ep->next) {
+ if (strcmp((char *)ep->var, (char *)var) == 0)
+ return(ep);
+ }
+ return(NULL);
+}
+
+ void
+env_init()
+{
+ extern char **environ;
+ register char **epp, *cp;
+ register struct env_lst *ep;
+ extern char *index();
+
+ for (epp = environ; *epp; epp++) {
+ if (cp = index(*epp, '=')) {
+ *cp = '\0';
+ ep = env_define((unsigned char *)*epp,
+ (unsigned char *)cp+1);
+ ep->export = 0;
+ *cp = '=';
+ }
+ }
+ /*
+ * Special case for DISPLAY variable. If it is ":0.0" or
+ * "unix:0.0", we have to get rid of "unix" and insert our
+ * hostname.
+ */
+ if ((ep = env_find("DISPLAY"))
+ && ((*ep->value == ':')
+ || (strncmp((char *)ep->value, "unix:", 5) == 0))) {
+ char hbuf[256+1];
+ char *cp2 = index((char *)ep->value, ':');
+
+ gethostname(hbuf, 256);
+ hbuf[256] = '\0';
+ cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1);
+ sprintf((char *)cp, "%s%s", hbuf, cp2);
+ free(ep->value);
+ ep->value = (unsigned char *)cp;
+ }
+ /*
+ * If USER is not defined, but LOGNAME is, then add
+ * USER with the value from LOGNAME. By default, we
+ * don't export the USER variable.
+ */
+ if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) {
+ env_define((unsigned char *)"USER", ep->value);
+ env_unexport((unsigned char *)"USER");
+ }
+ env_export((unsigned char *)"DISPLAY");
+ env_export((unsigned char *)"PRINTER");
+}
+
+ struct env_lst *
+env_define(var, value)
+ unsigned char *var, *value;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var)) {
+ if (ep->var)
+ free(ep->var);
+ if (ep->value)
+ free(ep->value);
+ } else {
+ ep = (struct env_lst *)malloc(sizeof(struct env_lst));
+ ep->next = envlisthead.next;
+ envlisthead.next = ep;
+ ep->prev = &envlisthead;
+ if (ep->next)
+ ep->next->prev = ep;
+ }
+ ep->welldefined = opt_welldefined(var);
+ ep->export = 1;
+ ep->var = (unsigned char *)strdup((char *)var);
+ ep->value = (unsigned char *)strdup((char *)value);
+ return(ep);
+}
+
+ void
+env_undefine(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var)) {
+ ep->prev->next = ep->next;
+ if (ep->next)
+ ep->next->prev = ep->prev;
+ if (ep->var)
+ free(ep->var);
+ if (ep->value)
+ free(ep->value);
+ free(ep);
+ }
+}
+
+ void
+env_export(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ ep->export = 1;
+}
+
+ void
+env_unexport(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ ep->export = 0;
+}
+
+ void
+env_send(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (my_state_is_wont(TELOPT_NEW_ENVIRON)
+#ifdef OLD_ENVIRON
+ && my_state_is_wont(TELOPT_OLD_ENVIRON)
+#endif
+ ) {
+ fprintf(stderr,
+ "Cannot send '%s': Telnet ENVIRON option not enabled\n",
+ var);
+ return;
+ }
+ ep = env_find(var);
+ if (ep == 0) {
+ fprintf(stderr, "Cannot send '%s': variable not defined\n",
+ var);
+ return;
+ }
+ env_opt_start_info();
+ env_opt_add(ep->var);
+ env_opt_end(0);
+}
+
+ void
+env_list()
+{
+ register struct env_lst *ep;
+
+ for (ep = envlisthead.next; ep; ep = ep->next) {
+ printf("%c %-20s %s\n", ep->export ? '*' : ' ',
+ ep->var, ep->value);
+ }
+}
+
+ unsigned char *
+env_default(init, welldefined)
+ int init;
+{
+ static struct env_lst *nep = NULL;
+
+ if (init) {
+ nep = &envlisthead;
+ return;
+ }
+ if (nep) {
+ while (nep = nep->next) {
+ if (nep->export && (nep->welldefined == welldefined))
+ return(nep->var);
+ }
+ }
+ return(NULL);
+}
+
+ unsigned char *
+env_getvalue(var)
+ unsigned char *var;
+{
+ register struct env_lst *ep;
+
+ if (ep = env_find(var))
+ return(ep->value);
+ return(NULL);
+}
+
+#if defined(OLD_ENVIRON) && defined(ENV_HACK)
+ void
+env_varval(what)
+ unsigned char *what;
+{
+ extern int old_env_var, old_env_value, env_auto;
+ int len = strlen((char *)what);
+
+ if (len == 0)
+ goto unknown;
+
+ if (strncasecmp((char *)what, "status", len) == 0) {
+ if (env_auto)
+ printf("%s%s", "VAR and VALUE are/will be ",
+ "determined automatically\n");
+ if (old_env_var == OLD_ENV_VAR)
+ printf("VAR and VALUE set to correct definitions\n");
+ else
+ printf("VAR and VALUE definitions are reversed\n");
+ } else if (strncasecmp((char *)what, "auto", len) == 0) {
+ env_auto = 1;
+ old_env_var = OLD_ENV_VALUE;
+ old_env_value = OLD_ENV_VAR;
+ } else if (strncasecmp((char *)what, "right", len) == 0) {
+ env_auto = 0;
+ old_env_var = OLD_ENV_VAR;
+ old_env_value = OLD_ENV_VALUE;
+ } else if (strncasecmp((char *)what, "wrong", len) == 0) {
+ env_auto = 0;
+ old_env_var = OLD_ENV_VALUE;
+ old_env_value = OLD_ENV_VAR;
+ } else {
+unknown:
+ printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n");
+ }
+}
+#endif
+
+#if defined(AUTHENTICATION)
+/*
+ * The AUTHENTICATE command.
+ */
+
+struct authlist {
+ char *name;
+ char *help;
+ int (*handler)();
+ int narg;
+};
+
+extern int
+ auth_enable P((int)),
+ auth_disable P((int)),
+ auth_status P((void));
+static int
+ auth_help P((void));
+
+struct authlist AuthList[] = {
+ { "status", "Display current status of authentication information",
+ auth_status, 0 },
+ { "disable", "Disable an authentication type ('auth disable ?' for more)",
+ auth_disable, 1 },
+ { "enable", "Enable an authentication type ('auth enable ?' for more)",
+ auth_enable, 1 },
+ { "help", 0, auth_help, 0 },
+ { "?", "Print help information", auth_help, 0 },
+ { 0 },
+};
+
+ static int
+auth_help()
+{
+ struct authlist *c;
+
+ for (c = AuthList; c->name; c++) {
+ if (c->help) {
+ if (*c->help)
+ printf("%-15s %s\n", c->name, c->help);
+ else
+ printf("\n");
+ }
+ }
+ return 0;
+}
+
+auth_cmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct authlist *c;
+
+ c = (struct authlist *)
+ genget(argv[1], (char **) AuthList, sizeof(struct authlist));
+ if (c == 0) {
+ fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (Ambiguous(c)) {
+ fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n",
+ argv[1]);
+ return 0;
+ }
+ if (c->narg + 2 != argc) {
+ fprintf(stderr,
+ "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n",
+ c->narg < argc + 2 ? "only " : "",
+ c->narg, c->narg == 1 ? "" : "s", c->name);
+ return 0;
+ }
+ return((*c->handler)(argv[2], argv[3]));
+}
+#endif
+
+
+#if defined(unix) && defined(TN3270)
+ static void
+filestuff(fd)
+ int fd;
+{
+ int res;
+
+#ifdef F_GETOWN
+ setconnmode(0);
+ res = fcntl(fd, F_GETOWN, 0);
+ setcommandmode();
+
+ if (res == -1) {
+ perror("fcntl");
+ return;
+ }
+ printf("\tOwner is %d.\n", res);
+#endif
+
+ setconnmode(0);
+ res = fcntl(fd, F_GETFL, 0);
+ setcommandmode();
+
+ if (res == -1) {
+ perror("fcntl");
+ return;
+ }
+#ifdef notdef
+ printf("\tFlags are 0x%x: %s\n", res, decodeflags(res));
+#endif
+}
+#endif /* defined(unix) && defined(TN3270) */
+
+/*
+ * Print status about the connection.
+ */
+ /*ARGSUSED*/
+ static
+status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (connected) {
+ printf("Connected to %s.\n", hostname);
+ if ((argc < 2) || strcmp(argv[1], "notmuch")) {
+ int mode = getconnmode();
+
+ if (my_want_state_is_will(TELOPT_LINEMODE)) {
+ printf("Operating with LINEMODE option\n");
+ printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No");
+ printf("%s catching of signals\n",
+ (mode&MODE_TRAPSIG) ? "Local" : "No");
+ slcstate();
+#ifdef KLUDGELINEMODE
+ } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
+ printf("Operating in obsolete linemode\n");
+#endif
+ } else {
+ printf("Operating in single character mode\n");
+ if (localchars)
+ printf("Catching signals locally\n");
+ }
+ printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote");
+ if (my_want_state_is_will(TELOPT_LFLOW))
+ printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No");
+ }
+ } else {
+ printf("No connection.\n");
+ }
+# if !defined(TN3270)
+ printf("Escape character is '%s'.\n", control(escape));
+ (void) fflush(stdout);
+# else /* !defined(TN3270) */
+ if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
+ printf("Escape character is '%s'.\n", control(escape));
+ }
+# if defined(unix)
+ if ((argc >= 2) && !strcmp(argv[1], "everything")) {
+ printf("SIGIO received %d time%s.\n",
+ sigiocount, (sigiocount == 1)? "":"s");
+ if (In3270) {
+ printf("Process ID %d, process group %d.\n",
+ getpid(), getpgrp(getpid()));
+ printf("Terminal input:\n");
+ filestuff(tin);
+ printf("Terminal output:\n");
+ filestuff(tout);
+ printf("Network socket:\n");
+ filestuff(net);
+ }
+ }
+ if (In3270 && transcom) {
+ printf("Transparent mode command is '%s'.\n", transcom);
+ }
+# endif /* defined(unix) */
+ (void) fflush(stdout);
+ if (In3270) {
+ return 0;
+ }
+# endif /* defined(TN3270) */
+ return 1;
+}
+
+#ifdef SIGINFO
+/*
+ * Function that gets called when SIGINFO is received.
+ */
+ayt_status()
+{
+ (void) call(status, "status", "notmuch", 0);
+}
+#endif
+
+unsigned long inet_addr();
+
+ int
+tn(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct hostent *host = 0;
+ struct sockaddr_in sin;
+ struct servent *sp = 0;
+ unsigned long temp;
+ extern char *inet_ntoa();
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+ char *srp = 0, *strrchr();
+ unsigned long sourceroute(), srlen;
+#endif
+ char *cmd, *hostp = 0, *portp = 0, *user = 0;
+
+ /* clear the socket address prior to use */
+ bzero((char *)&sin, sizeof(sin));
+
+ if (connected) {
+ printf("?Already connected to %s\n", hostname);
+ setuid(getuid());
+ return 0;
+ }
+ if (argc < 2) {
+ (void) strcpy(line, "open ");
+ printf("(to) ");
+ (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ cmd = *argv;
+ --argc; ++argv;
+ while (argc) {
+ if (isprefix(*argv, "help") || isprefix(*argv, "?"))
+ goto usage;
+ if (strcmp(*argv, "-l") == 0) {
+ --argc; ++argv;
+ if (argc == 0)
+ goto usage;
+ user = *argv++;
+ --argc;
+ continue;
+ }
+ if (strcmp(*argv, "-a") == 0) {
+ --argc; ++argv;
+ autologin = 1;
+ continue;
+ }
+ if (hostp == 0) {
+ hostp = *argv++;
+ --argc;
+ continue;
+ }
+ if (portp == 0) {
+ portp = *argv++;
+ --argc;
+ continue;
+ }
+ usage:
+ printf("usage: %s [-l user] [-a] host-name [port]\n", cmd);
+ setuid(getuid());
+ return 0;
+ }
+ if (hostp == 0)
+ goto usage;
+
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+ if (hostp[0] == '@' || hostp[0] == '!') {
+ if ((hostname = strrchr(hostp, ':')) == NULL)
+ hostname = strrchr(hostp, '@');
+ hostname++;
+ srp = 0;
+ temp = sourceroute(hostp, &srp, &srlen);
+ if (temp == 0) {
+ herror(srp);
+ setuid(getuid());
+ return 0;
+ } else if (temp == -1) {
+ printf("Bad source route option: %s\n", hostp);
+ setuid(getuid());
+ return 0;
+ } else {
+ sin.sin_addr.s_addr = temp;
+ sin.sin_family = AF_INET;
+ }
+ } else {
+#endif
+ temp = inet_addr(hostp);
+ if (temp != (unsigned long) -1) {
+ sin.sin_addr.s_addr = temp;
+ sin.sin_family = AF_INET;
+ (void) strcpy(_hostname, hostp);
+ hostname = _hostname;
+ } else {
+ host = gethostbyname(hostp);
+ if (host) {
+ sin.sin_family = host->h_addrtype;
+#if defined(h_addr) /* In 4.3, this is a #define */
+ memcpy((caddr_t)&sin.sin_addr,
+ host->h_addr_list[0], host->h_length);
+#else /* defined(h_addr) */
+ memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
+#endif /* defined(h_addr) */
+ strncpy(_hostname, host->h_name, sizeof(_hostname));
+ _hostname[sizeof(_hostname)-1] = '\0';
+ hostname = _hostname;
+ } else {
+ herror(hostp);
+ setuid(getuid());
+ return 0;
+ }
+ }
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+ }
+#endif
+ if (portp) {
+ if (*portp == '-') {
+ portp++;
+ telnetport = 1;
+ } else
+ telnetport = 0;
+ sin.sin_port = atoi(portp);
+ if (sin.sin_port == 0) {
+ sp = getservbyname(portp, "tcp");
+ if (sp)
+ sin.sin_port = sp->s_port;
+ else {
+ printf("%s: bad port number\n", portp);
+ setuid(getuid());
+ return 0;
+ }
+ } else {
+#if !defined(htons)
+ u_short htons P((unsigned short));
+#endif /* !defined(htons) */
+ sin.sin_port = htons(sin.sin_port);
+ }
+ } else {
+ if (sp == 0) {
+ sp = getservbyname("telnet", "tcp");
+ if (sp == 0) {
+ fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
+ setuid(getuid());
+ return 0;
+ }
+ sin.sin_port = sp->s_port;
+ }
+ telnetport = 1;
+ }
+ printf("Trying %s...\n", inet_ntoa(sin.sin_addr));
+ do {
+ net = socket(AF_INET, SOCK_STREAM, 0);
+ setuid(getuid());
+ if (net < 0) {
+ perror("telnet: socket");
+ return 0;
+ }
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+ if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0)
+ perror("setsockopt (IP_OPTIONS)");
+#endif
+#if defined(IPPROTO_IP) && defined(IP_TOS)
+ {
+# if defined(HAS_GETTOS)
+ struct tosent *tp;
+ if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
+ tos = tp->t_tos;
+# endif
+ if (tos < 0)
+ tos = IPTOS_LOWDELAY;
+ if (tos
+ && (setsockopt(net, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof(int)) < 0)
+ && (errno != ENOPROTOOPT))
+ perror("telnet: setsockopt (IP_TOS) (ignored)");
+ }
+#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */
+
+ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
+ perror("setsockopt (SO_DEBUG)");
+ }
+
+ if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+#if defined(h_addr) /* In 4.3, this is a #define */
+ if (host && host->h_addr_list[1]) {
+ int oerrno = errno;
+
+ fprintf(stderr, "telnet: connect to address %s: ",
+ inet_ntoa(sin.sin_addr));
+ errno = oerrno;
+ perror((char *)0);
+ host->h_addr_list++;
+ memcpy((caddr_t)&sin.sin_addr,
+ host->h_addr_list[0], host->h_length);
+ (void) NetClose(net);
+ continue;
+ }
+#endif /* defined(h_addr) */
+ perror("telnet: Unable to connect to remote host");
+ return 0;
+ }
+ connected++;
+#if defined(AUTHENTICATION)
+ auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) */
+ } while (connected == 0);
+ cmdrc(hostp, hostname);
+ if (autologin && user == NULL) {
+ struct passwd *pw;
+
+ user = getenv("USER");
+ if (user == NULL ||
+ (pw = getpwnam(user)) && pw->pw_uid != getuid()) {
+ if (pw = getpwuid(getuid()))
+ user = pw->pw_name;
+ else
+ user = NULL;
+ }
+ }
+ if (user) {
+ env_define((unsigned char *)"USER", (unsigned char *)user);
+ env_export((unsigned char *)"USER");
+ }
+ (void) call(status, "status", "notmuch", 0);
+ if (setjmp(peerdied) == 0)
+ telnet(user);
+ (void) NetClose(net);
+ ExitString("Connection closed by foreign host.\n",1);
+ /*NOTREACHED*/
+}
+
+#define HELPINDENT (sizeof ("connect"))
+
+static char
+ openhelp[] = "connect to a site",
+ closehelp[] = "close current connection",
+ logouthelp[] = "forcibly logout remote user and close the connection",
+ quithelp[] = "exit telnet",
+ statushelp[] = "print status information",
+ helphelp[] = "print help information",
+ sendhelp[] = "transmit special characters ('send ?' for more)",
+ sethelp[] = "set operating parameters ('set ?' for more)",
+ unsethelp[] = "unset operating parameters ('unset ?' for more)",
+ togglestring[] ="toggle operating parameters ('toggle ?' for more)",
+ slchelp[] = "change state of special charaters ('slc ?' for more)",
+ displayhelp[] = "display operating parameters",
+#if defined(TN3270) && defined(unix)
+ transcomhelp[] = "specify Unix command for transparent mode pipe",
+#endif /* defined(TN3270) && defined(unix) */
+#if defined(AUTHENTICATION)
+ authhelp[] = "turn on (off) authentication ('auth ?' for more)",
+#endif
+#if defined(unix)
+ zhelp[] = "suspend telnet",
+#endif /* defined(unix) */
+ shellhelp[] = "invoke a subshell",
+ envhelp[] = "change environment variables ('environ ?' for more)",
+ modestring[] = "try to enter line or character mode ('mode ?' for more)";
+
+static int help();
+
+static Command cmdtab[] = {
+ { "close", closehelp, bye, 1 },
+ { "logout", logouthelp, logout, 1 },
+ { "display", displayhelp, display, 0 },
+ { "mode", modestring, modecmd, 0 },
+ { "open", openhelp, tn, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "send", sendhelp, sendcmd, 0 },
+ { "set", sethelp, setcmd, 0 },
+ { "unset", unsethelp, unsetcmd, 0 },
+ { "status", statushelp, status, 0 },
+ { "toggle", togglestring, toggle, 0 },
+ { "slc", slchelp, slccmd, 0 },
+#if defined(TN3270) && defined(unix)
+ { "transcom", transcomhelp, settranscom, 0 },
+#endif /* defined(TN3270) && defined(unix) */
+#if defined(AUTHENTICATION)
+ { "auth", authhelp, auth_cmd, 0 },
+#endif
+#if defined(unix)
+ { "z", zhelp, suspend, 0 },
+#endif /* defined(unix) */
+#if defined(TN3270)
+ { "!", shellhelp, shell, 1 },
+#else
+ { "!", shellhelp, shell, 0 },
+#endif
+ { "environ", envhelp, env_cmd, 0 },
+ { "?", helphelp, help, 0 },
+ 0
+};
+
+static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead";
+static char escapehelp[] = "deprecated command -- use 'set escape' instead";
+
+static Command cmdtab2[] = {
+ { "help", 0, help, 0 },
+ { "escape", escapehelp, setescape, 0 },
+ { "crmod", crmodhelp, togcrmod, 0 },
+ 0
+};
+
+
+/*
+ * Call routine with argc, argv set from args (terminated by 0).
+ */
+
+ /*VARARGS1*/
+ static
+call(va_alist)
+ va_dcl
+{
+ va_list ap;
+ typedef int (*intrtn_t)();
+ intrtn_t routine;
+ char *args[100];
+ int argno = 0;
+
+ va_start(ap);
+ routine = (va_arg(ap, intrtn_t));
+ while ((args[argno++] = va_arg(ap, char *)) != 0) {
+ ;
+ }
+ va_end(ap);
+ return (*routine)(argno-1, args);
+}
+
+
+ static Command *
+getcmd(name)
+ char *name;
+{
+ Command *cm;
+
+ if (cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command)))
+ return cm;
+ return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
+}
+
+ void
+command(top, tbuf, cnt)
+ int top;
+ char *tbuf;
+ int cnt;
+{
+ register Command *c;
+
+ setcommandmode();
+ if (!top) {
+ putchar('\n');
+#if defined(unix)
+ } else {
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+#endif /* defined(unix) */
+ }
+ for (;;) {
+ if (rlogin == _POSIX_VDISABLE)
+ printf("%s> ", prompt);
+ if (tbuf) {
+ register char *cp;
+ cp = line;
+ while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
+ cnt--;
+ tbuf = 0;
+ if (cp == line || *--cp != '\n' || cp == line)
+ goto getline;
+ *cp = '\0';
+ if (rlogin == _POSIX_VDISABLE)
+ printf("%s\n", line);
+ } else {
+ getline:
+ if (rlogin != _POSIX_VDISABLE)
+ printf("%s> ", prompt);
+ if (fgets(line, sizeof(line), stdin) == NULL) {
+ if (feof(stdin) || ferror(stdin)) {
+ (void) quit();
+ /*NOTREACHED*/
+ }
+ break;
+ }
+ }
+ if (line[0] == 0)
+ break;
+ makeargv();
+ if (margv[0] == 0) {
+ break;
+ }
+ c = getcmd(margv[0]);
+ if (Ambiguous(c)) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->needconnect && !connected) {
+ printf("?Need to be connected first.\n");
+ continue;
+ }
+ if ((*c->handler)(margc, margv)) {
+ break;
+ }
+ }
+ if (!top) {
+ if (!connected) {
+ longjmp(toplevel, 1);
+ /*NOTREACHED*/
+ }
+#if defined(TN3270)
+ if (shell_active == 0) {
+ setconnmode(0);
+ }
+#else /* defined(TN3270) */
+ setconnmode(0);
+#endif /* defined(TN3270) */
+ }
+}
+
+/*
+ * Help command.
+ */
+ static
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register Command *c;
+
+ if (argc == 1) {
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->name; c++)
+ if (c->help) {
+ printf("%-*s\t%s\n", HELPINDENT, c->name,
+ c->help);
+ }
+ return 0;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (Ambiguous(c))
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (Command *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%s\n", c->help);
+ }
+ return 0;
+}
+
+static char *rcname = 0;
+static char rcbuf[128];
+
+cmdrc(m1, m2)
+ char *m1, *m2;
+{
+ register Command *c;
+ FILE *rcfile;
+ int gotmachine = 0;
+ int l1 = strlen(m1);
+ int l2 = strlen(m2);
+ char m1save[64];
+
+ if (skiprc)
+ return;
+
+ strcpy(m1save, m1);
+ m1 = m1save;
+
+ if (rcname == 0) {
+ rcname = getenv("HOME");
+ if (rcname)
+ strcpy(rcbuf, rcname);
+ else
+ rcbuf[0] = '\0';
+ strcat(rcbuf, "/.telnetrc");
+ rcname = rcbuf;
+ }
+
+ if ((rcfile = fopen(rcname, "r")) == 0) {
+ return;
+ }
+
+ for (;;) {
+ if (fgets(line, sizeof(line), rcfile) == NULL)
+ break;
+ if (line[0] == 0)
+ break;
+ if (line[0] == '#')
+ continue;
+ if (gotmachine) {
+ if (!isspace(line[0]))
+ gotmachine = 0;
+ }
+ if (gotmachine == 0) {
+ if (isspace(line[0]))
+ continue;
+ if (strncasecmp(line, m1, l1) == 0)
+ strncpy(line, &line[l1], sizeof(line) - l1);
+ else if (strncasecmp(line, m2, l2) == 0)
+ strncpy(line, &line[l2], sizeof(line) - l2);
+ else if (strncasecmp(line, "DEFAULT", 7) == 0)
+ strncpy(line, &line[7], sizeof(line) - 7);
+ else
+ continue;
+ if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
+ continue;
+ gotmachine = 1;
+ }
+ makeargv();
+ if (margv[0] == 0)
+ continue;
+ c = getcmd(margv[0]);
+ if (Ambiguous(c)) {
+ printf("?Ambiguous command: %s\n", margv[0]);
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command: %s\n", margv[0]);
+ continue;
+ }
+ /*
+ * This should never happen...
+ */
+ if (c->needconnect && !connected) {
+ printf("?Need to be connected first for %s.\n", margv[0]);
+ continue;
+ }
+ (*c->handler)(margc, margv);
+ }
+ fclose(rcfile);
+}
+
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+
+/*
+ * Source route is handed in as
+ * [!]@hop1@hop2...[@|:]dst
+ * If the leading ! is present, it is a
+ * strict source route, otherwise it is
+ * assmed to be a loose source route.
+ *
+ * We fill in the source route option as
+ * hop1,hop2,hop3...dest
+ * and return a pointer to hop1, which will
+ * be the address to connect() to.
+ *
+ * Arguments:
+ * arg: pointer to route list to decipher
+ *
+ * cpp: If *cpp is not equal to NULL, this is a
+ * pointer to a pointer to a character array
+ * that should be filled in with the option.
+ *
+ * lenp: pointer to an integer that contains the
+ * length of *cpp if *cpp != NULL.
+ *
+ * Return values:
+ *
+ * Returns the address of the host to connect to. If the
+ * return value is -1, there was a syntax error in the
+ * option, either unknown characters, or too many hosts.
+ * If the return value is 0, one of the hostnames in the
+ * path is unknown, and *cpp is set to point to the bad
+ * hostname.
+ *
+ * *cpp: If *cpp was equal to NULL, it will be filled
+ * in with a pointer to our static area that has
+ * the option filled in. This will be 32bit aligned.
+ *
+ * *lenp: This will be filled in with how long the option
+ * pointed to by *cpp is.
+ *
+ */
+ unsigned long
+sourceroute(arg, cpp, lenp)
+ char *arg;
+ char **cpp;
+ int *lenp;
+{
+ static char lsr[44];
+#ifdef sysV88
+ static IOPTN ipopt;
+#endif
+ char *cp, *cp2, *lsrp, *lsrep;
+ register int tmp;
+ struct in_addr sin_addr;
+ register struct hostent *host = 0;
+ register char c;
+
+ /*
+ * Verify the arguments, and make sure we have
+ * at least 7 bytes for the option.
+ */
+ if (cpp == NULL || lenp == NULL)
+ return((unsigned long)-1);
+ if (*cpp != NULL && *lenp < 7)
+ return((unsigned long)-1);
+ /*
+ * Decide whether we have a buffer passed to us,
+ * or if we need to use our own static buffer.
+ */
+ if (*cpp) {
+ lsrp = *cpp;
+ lsrep = lsrp + *lenp;
+ } else {
+ *cpp = lsrp = lsr;
+ lsrep = lsrp + 44;
+ }
+
+ cp = arg;
+
+ /*
+ * Next, decide whether we have a loose source
+ * route or a strict source route, and fill in
+ * the begining of the option.
+ */
+#ifndef sysV88
+ if (*cp == '!') {
+ cp++;
+ *lsrp++ = IPOPT_SSRR;
+ } else
+ *lsrp++ = IPOPT_LSRR;
+#else
+ if (*cp == '!') {
+ cp++;
+ ipopt.io_type = IPOPT_SSRR;
+ } else
+ ipopt.io_type = IPOPT_LSRR;
+#endif
+
+ if (*cp != '@')
+ return((unsigned long)-1);
+
+#ifndef sysV88
+ lsrp++; /* skip over length, we'll fill it in later */
+ *lsrp++ = 4;
+#endif
+
+ cp++;
+
+ sin_addr.s_addr = 0;
+
+ for (c = 0;;) {
+ if (c == ':')
+ cp2 = 0;
+ else for (cp2 = cp; c = *cp2; cp2++) {
+ if (c == ',') {
+ *cp2++ = '\0';
+ if (*cp2 == '@')
+ cp2++;
+ } else if (c == '@') {
+ *cp2++ = '\0';
+ } else if (c == ':') {
+ *cp2++ = '\0';
+ } else
+ continue;
+ break;
+ }
+ if (!c)
+ cp2 = 0;
+
+ if ((tmp = inet_addr(cp)) != -1) {
+ sin_addr.s_addr = tmp;
+ } else if (host = gethostbyname(cp)) {
+#if defined(h_addr)
+ memcpy((caddr_t)&sin_addr,
+ host->h_addr_list[0], host->h_length);
+#else
+ memcpy((caddr_t)&sin_addr, host->h_addr, host->h_length);
+#endif
+ } else {
+ *cpp = cp;
+ return(0);
+ }
+ memcpy(lsrp, (char *)&sin_addr, 4);
+ lsrp += 4;
+ if (cp2)
+ cp = cp2;
+ else
+ break;
+ /*
+ * Check to make sure there is space for next address
+ */
+ if (lsrp + 4 > lsrep)
+ return((unsigned long)-1);
+ }
+#ifndef sysV88
+ if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) {
+ *cpp = 0;
+ *lenp = 0;
+ return((unsigned long)-1);
+ }
+ *lsrp++ = IPOPT_NOP; /* 32 bit word align it */
+ *lenp = lsrp - *cpp;
+#else
+ ipopt.io_len = lsrp - *cpp;
+ if (ipopt.io_len <= 5) { /* Is 3 better ? */
+ *cpp = 0;
+ *lenp = 0;
+ return((unsigned long)-1);
+ }
+ *lenp = sizeof(ipopt);
+ *cpp = (char *) &ipopt;
+#endif
+ return(sin_addr.s_addr);
+}
+#endif
diff --git a/usr.bin/telnet/defines.h b/usr.bin/telnet/defines.h
new file mode 100644
index 0000000..0978173
--- /dev/null
+++ b/usr.bin/telnet/defines.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defines.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define settimer(x) clocks.x = clocks.system++
+
+#if !defined(TN3270)
+
+#define SetIn3270()
+
+#endif /* !defined(TN3270) */
+
+#define NETADD(c) { *netoring.supply = c; ring_supplied(&netoring, 1); }
+#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
+#define NETBYTES() (ring_full_count(&netoring))
+#define NETROOM() (ring_empty_count(&netoring))
+
+#define TTYADD(c) if (!(SYNCHing||flushout)) { \
+ *ttyoring.supply = c; \
+ ring_supplied(&ttyoring, 1); \
+ }
+#define TTYBYTES() (ring_full_count(&ttyoring))
+#define TTYROOM() (ring_empty_count(&ttyoring))
+
+/* Various modes */
+#define MODE_LOCAL_CHARS(m) ((m)&(MODE_EDIT|MODE_TRAPSIG))
+#define MODE_LOCAL_ECHO(m) ((m)&MODE_ECHO)
+#define MODE_COMMAND_LINE(m) ((m)==-1)
+
+#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h
new file mode 100644
index 0000000..476a444
--- /dev/null
+++ b/usr.bin/telnet/externs.h
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)externs.h 8.2 (Berkeley) 12/15/93
+ */
+
+#ifndef BSD
+# define BSD 43
+#endif
+
+/*
+ * ucb stdio.h defines BSD as something wierd
+ */
+#if defined(sun) && defined(__svr4__)
+#define BSD 43
+#endif
+
+#ifndef USE_TERMIO
+# if BSD > 43 || defined(SYSV_TERMIO)
+# define USE_TERMIO
+# endif
+#endif
+
+#include <stdio.h>
+#include <setjmp.h>
+#if defined(CRAY) && !defined(NO_BSD_SETJMP)
+#include <bsdsetjmp.h>
+#endif
+#ifndef FILIO_H
+#include <sys/ioctl.h>
+#else
+#include <sys/filio.h>
+#endif
+#ifdef CRAY
+# include <errno.h>
+#endif /* CRAY */
+#ifdef USE_TERMIO
+# ifndef VINTR
+# ifdef SYSV_TERMIO
+# include <sys/termio.h>
+# else
+# include <sys/termios.h>
+# define termio termios
+# endif
+# endif
+#endif
+#if defined(NO_CC_T) || !defined(USE_TERMIO)
+# if !defined(USE_TERMIO)
+typedef char cc_t;
+# else
+typedef unsigned char cc_t;
+# endif
+#endif
+
+#ifndef NO_STRING_H
+#include <string.h>
+#endif
+#include <strings.h>
+
+#ifndef _POSIX_VDISABLE
+# ifdef sun
+# include <sys/param.h> /* pick up VDISABLE definition, mayby */
+# endif
+# ifdef VDISABLE
+# define _POSIX_VDISABLE VDISABLE
+# else
+# define _POSIX_VDISABLE ((cc_t)'\377')
+# endif
+#endif
+
+#define SUBBUFSIZE 256
+
+#ifndef CRAY
+extern int errno; /* outside this world */
+#endif /* !CRAY */
+
+#if !defined(P)
+# ifdef __STDC__
+# define P(x) x
+# else
+# define P(x) ()
+# endif
+#endif
+
+extern int
+ autologin, /* Autologin enabled */
+ skiprc, /* Don't process the ~/.telnetrc file */
+ eight, /* use eight bit mode (binary in and/or out */
+ flushout, /* flush output */
+ connected, /* Are we connected to the other side? */
+ globalmode, /* Mode tty should be in */
+ In3270, /* Are we in 3270 mode? */
+ telnetport, /* Are we connected to the telnet port? */
+ localflow, /* Flow control handled locally */
+ restartany, /* If flow control, restart output on any character */
+ localchars, /* we recognize interrupt/quit */
+ donelclchars, /* the user has set "localchars" */
+ showoptions,
+ net, /* Network file descriptor */
+ tin, /* Terminal input file descriptor */
+ tout, /* Terminal output file descriptor */
+ crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+ autoflush, /* flush output when interrupting? */
+ autosynch, /* send interrupt characters with SYNCH? */
+ SYNCHing, /* Is the stream in telnet SYNCH mode? */
+ donebinarytoggle, /* the user has put us in binary */
+ dontlecho, /* do we suppress local echoing right now? */
+ crmod,
+ netdata, /* Print out network data flow */
+ prettydump, /* Print "netdata" output in user readable format */
+#if defined(unix)
+#if defined(TN3270)
+ cursesdata, /* Print out curses data flow */
+ apitrace, /* Trace API transactions */
+#endif /* defined(TN3270) */
+ termdata, /* Print out terminal data flow */
+#endif /* defined(unix) */
+ debug; /* Debug level */
+
+extern cc_t escape; /* Escape to command mode */
+extern cc_t rlogin; /* Rlogin mode escape character */
+#ifdef KLUDGELINEMODE
+extern cc_t echoc; /* Toggle local echoing */
+#endif
+
+extern char
+ *prompt; /* Prompt for command. */
+
+extern char
+ doopt[],
+ dont[],
+ will[],
+ wont[],
+ options[], /* All the little options */
+ *hostname; /* Who are we connected to? */
+
+/*
+ * We keep track of each side of the option negotiation.
+ */
+
+#define MY_STATE_WILL 0x01
+#define MY_WANT_STATE_WILL 0x02
+#define MY_STATE_DO 0x04
+#define MY_WANT_STATE_DO 0x08
+
+/*
+ * Macros to check the current state of things
+ */
+
+#define my_state_is_do(opt) (options[opt]&MY_STATE_DO)
+#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL)
+#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO)
+#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL)
+
+#define my_state_is_dont(opt) (!my_state_is_do(opt))
+#define my_state_is_wont(opt) (!my_state_is_will(opt))
+#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt))
+#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt))
+
+#define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;}
+#define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;}
+#define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;}
+#define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;}
+
+#define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;}
+#define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;}
+#define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;}
+#define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;}
+
+/*
+ * Make everything symetrical
+ */
+
+#define HIS_STATE_WILL MY_STATE_DO
+#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO
+#define HIS_STATE_DO MY_STATE_WILL
+#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL
+
+#define his_state_is_do my_state_is_will
+#define his_state_is_will my_state_is_do
+#define his_want_state_is_do my_want_state_is_will
+#define his_want_state_is_will my_want_state_is_do
+
+#define his_state_is_dont my_state_is_wont
+#define his_state_is_wont my_state_is_dont
+#define his_want_state_is_dont my_want_state_is_wont
+#define his_want_state_is_wont my_want_state_is_dont
+
+#define set_his_state_do set_my_state_will
+#define set_his_state_will set_my_state_do
+#define set_his_want_state_do set_my_want_state_will
+#define set_his_want_state_will set_my_want_state_do
+
+#define set_his_state_dont set_my_state_wont
+#define set_his_state_wont set_my_state_dont
+#define set_his_want_state_dont set_my_want_state_wont
+#define set_his_want_state_wont set_my_want_state_dont
+
+
+extern FILE
+ *NetTrace; /* Where debugging output goes */
+extern unsigned char
+ NetTraceFile[]; /* Name of file where debugging output goes */
+extern void
+ SetNetTrace P((char *)); /* Function to change where debugging goes */
+
+extern jmp_buf
+ peerdied,
+ toplevel; /* For error conditions. */
+
+extern void
+ command P((int, char *, int)),
+ Dump P((int, unsigned char *, int)),
+ init_3270 P((void)),
+ printoption P((char *, int, int)),
+ printsub P((int, unsigned char *, int)),
+ sendnaws P((void)),
+ setconnmode P((int)),
+ setcommandmode P((void)),
+ setneturg P((void)),
+ sys_telnet_init P((void)),
+ telnet P((char *)),
+ tel_enter_binary P((int)),
+ TerminalFlushOutput P((void)),
+ TerminalNewMode P((int)),
+ TerminalRestoreState P((void)),
+ TerminalSaveState P((void)),
+ tninit P((void)),
+ upcase P((char *)),
+ willoption P((int)),
+ wontoption P((int));
+
+extern void
+ send_do P((int, int)),
+ send_dont P((int, int)),
+ send_will P((int, int)),
+ send_wont P((int, int));
+
+extern void
+ lm_will P((unsigned char *, int)),
+ lm_wont P((unsigned char *, int)),
+ lm_do P((unsigned char *, int)),
+ lm_dont P((unsigned char *, int)),
+ lm_mode P((unsigned char *, int, int));
+
+extern void
+ slc_init P((void)),
+ slcstate P((void)),
+ slc_mode_export P((void)),
+ slc_mode_import P((int)),
+ slc_import P((int)),
+ slc_export P((void)),
+ slc P((unsigned char *, int)),
+ slc_check P((void)),
+ slc_start_reply P((void)),
+ slc_add_reply P((int, int, int)),
+ slc_end_reply P((void));
+extern int
+ slc_update P((void));
+
+extern void
+ env_opt P((unsigned char *, int)),
+ env_opt_start P((void)),
+ env_opt_start_info P((void)),
+ env_opt_add P((unsigned char *)),
+ env_opt_end P((int));
+
+extern unsigned char
+ *env_default P((int, int)),
+ *env_getvalue P((unsigned char *));
+
+extern int
+ get_status P((void)),
+ dosynch P((void));
+
+extern cc_t
+ *tcval P((int));
+
+#ifndef USE_TERMIO
+
+extern struct tchars ntc;
+extern struct ltchars nltc;
+extern struct sgttyb nttyb;
+
+# define termEofChar ntc.t_eofc
+# define termEraseChar nttyb.sg_erase
+# define termFlushChar nltc.t_flushc
+# define termIntChar ntc.t_intrc
+# define termKillChar nttyb.sg_kill
+# define termLiteralNextChar nltc.t_lnextc
+# define termQuitChar ntc.t_quitc
+# define termSuspChar nltc.t_suspc
+# define termRprntChar nltc.t_rprntc
+# define termWerasChar nltc.t_werasc
+# define termStartChar ntc.t_startc
+# define termStopChar ntc.t_stopc
+# define termForw1Char ntc.t_brkc
+extern cc_t termForw2Char;
+extern cc_t termAytChar;
+
+# define termEofCharp (cc_t *)&ntc.t_eofc
+# define termEraseCharp (cc_t *)&nttyb.sg_erase
+# define termFlushCharp (cc_t *)&nltc.t_flushc
+# define termIntCharp (cc_t *)&ntc.t_intrc
+# define termKillCharp (cc_t *)&nttyb.sg_kill
+# define termLiteralNextCharp (cc_t *)&nltc.t_lnextc
+# define termQuitCharp (cc_t *)&ntc.t_quitc
+# define termSuspCharp (cc_t *)&nltc.t_suspc
+# define termRprntCharp (cc_t *)&nltc.t_rprntc
+# define termWerasCharp (cc_t *)&nltc.t_werasc
+# define termStartCharp (cc_t *)&ntc.t_startc
+# define termStopCharp (cc_t *)&ntc.t_stopc
+# define termForw1Charp (cc_t *)&ntc.t_brkc
+# define termForw2Charp (cc_t *)&termForw2Char
+# define termAytCharp (cc_t *)&termAytChar
+
+# else
+
+extern struct termio new_tc;
+
+# define termEofChar new_tc.c_cc[VEOF]
+# define termEraseChar new_tc.c_cc[VERASE]
+# define termIntChar new_tc.c_cc[VINTR]
+# define termKillChar new_tc.c_cc[VKILL]
+# define termQuitChar new_tc.c_cc[VQUIT]
+
+# ifndef VSUSP
+extern cc_t termSuspChar;
+# else
+# define termSuspChar new_tc.c_cc[VSUSP]
+# endif
+# if defined(VFLUSHO) && !defined(VDISCARD)
+# define VDISCARD VFLUSHO
+# endif
+# ifndef VDISCARD
+extern cc_t termFlushChar;
+# else
+# define termFlushChar new_tc.c_cc[VDISCARD]
+# endif
+# ifndef VWERASE
+extern cc_t termWerasChar;
+# else
+# define termWerasChar new_tc.c_cc[VWERASE]
+# endif
+# ifndef VREPRINT
+extern cc_t termRprntChar;
+# else
+# define termRprntChar new_tc.c_cc[VREPRINT]
+# endif
+# ifndef VLNEXT
+extern cc_t termLiteralNextChar;
+# else
+# define termLiteralNextChar new_tc.c_cc[VLNEXT]
+# endif
+# ifndef VSTART
+extern cc_t termStartChar;
+# else
+# define termStartChar new_tc.c_cc[VSTART]
+# endif
+# ifndef VSTOP
+extern cc_t termStopChar;
+# else
+# define termStopChar new_tc.c_cc[VSTOP]
+# endif
+# ifndef VEOL
+extern cc_t termForw1Char;
+# else
+# define termForw1Char new_tc.c_cc[VEOL]
+# endif
+# ifndef VEOL2
+extern cc_t termForw2Char;
+# else
+# define termForw2Char new_tc.c_cc[VEOL]
+# endif
+# ifndef VSTATUS
+extern cc_t termAytChar;
+#else
+# define termAytChar new_tc.c_cc[VSTATUS]
+#endif
+
+# if !defined(CRAY) || defined(__STDC__)
+# define termEofCharp &termEofChar
+# define termEraseCharp &termEraseChar
+# define termIntCharp &termIntChar
+# define termKillCharp &termKillChar
+# define termQuitCharp &termQuitChar
+# define termSuspCharp &termSuspChar
+# define termFlushCharp &termFlushChar
+# define termWerasCharp &termWerasChar
+# define termRprntCharp &termRprntChar
+# define termLiteralNextCharp &termLiteralNextChar
+# define termStartCharp &termStartChar
+# define termStopCharp &termStopChar
+# define termForw1Charp &termForw1Char
+# define termForw2Charp &termForw2Char
+# define termAytCharp &termAytChar
+# else
+ /* Work around a compiler bug */
+# define termEofCharp 0
+# define termEraseCharp 0
+# define termIntCharp 0
+# define termKillCharp 0
+# define termQuitCharp 0
+# define termSuspCharp 0
+# define termFlushCharp 0
+# define termWerasCharp 0
+# define termRprntCharp 0
+# define termLiteralNextCharp 0
+# define termStartCharp 0
+# define termStopCharp 0
+# define termForw1Charp 0
+# define termForw2Charp 0
+# define termAytCharp 0
+# endif
+#endif
+
+
+/* Ring buffer structures which are shared */
+
+extern Ring
+ netoring,
+ netiring,
+ ttyoring,
+ ttyiring;
+
+/* Tn3270 section */
+#if defined(TN3270)
+
+extern int
+ HaveInput, /* Whether an asynchronous I/O indication came in */
+ noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */
+ noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */
+ sigiocount, /* Count of SIGIO receptions */
+ shell_active; /* Subshell is active */
+
+extern char
+ *Ibackp, /* Oldest byte of 3270 data */
+ Ibuf[], /* 3270 buffer */
+ *Ifrontp, /* Where next 3270 byte goes */
+ tline[],
+ *transcom; /* Transparent command */
+
+extern int
+ settranscom P((int, char**));
+
+extern void
+ inputAvailable P((int));
+#endif /* defined(TN3270) */
diff --git a/usr.bin/telnet/fdset.h b/usr.bin/telnet/fdset.h
new file mode 100644
index 0000000..045bb72
--- /dev/null
+++ b/usr.bin/telnet/fdset.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fdset.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * The following is defined just in case someone should want to run
+ * this telnet on a 4.2 system.
+ *
+ */
+
+#ifndef FD_SETSIZE
+
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+
+#endif
diff --git a/usr.bin/telnet/general.h b/usr.bin/telnet/general.h
new file mode 100644
index 0000000..4efa951
--- /dev/null
+++ b/usr.bin/telnet/general.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)general.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Some general definitions.
+ */
+
+
+#define numberof(x) (sizeof x/sizeof x[0])
+#define highestof(x) (numberof(x)-1)
+
+#define ClearElement(x) memset((char *)&x, 0, sizeof x)
+#define ClearArray(x) memset((char *)x, 0, sizeof x)
diff --git a/usr.bin/telnet/krb4-proto.h b/usr.bin/telnet/krb4-proto.h
new file mode 100644
index 0000000..75f6d41
--- /dev/null
+++ b/usr.bin/telnet/krb4-proto.h
@@ -0,0 +1,207 @@
+#ifdef __STDC__
+# define P(s) s
+#else
+# define P(s) ()
+#endif
+
+/* add_ticket.c */
+int add_ticket P((KTEXT , int , char *, int , char *, char *, char *, int , KTEXT ));
+
+/* cr_err_reply.c */
+void cr_err_reply P((KTEXT , char *, char *, char *, u_long , u_long , char *));
+
+/* create_auth_reply.c */
+KTEXT create_auth_reply P((char *, char *, char *, long , int , unsigned long , int , KTEXT ));
+
+/* create_ciph.c */
+int create_ciph P((KTEXT , C_Block , char *, char *, char *, unsigned long , int , KTEXT , unsigned long , C_Block ));
+
+/* create_death_packet.c */
+KTEXT krb_create_death_packet P((char *));
+
+/* create_ticket.c */
+int krb_create_ticket P((KTEXT , unsigned int , char *, char *, char *, long , char *, int , long , char *, char *, C_Block ));
+
+/* debug_decl.c */
+
+/* decomp_ticket.c */
+int decomp_ticket P((KTEXT , unsigned char *, char *, char *, char *, unsigned long *, C_Block , int *, unsigned long *, char *, char *, C_Block , Key_schedule ));
+
+/* dest_tkt.c */
+int dest_tkt P((void ));
+
+/* extract_ticket.c */
+int extract_ticket P((KTEXT , int , char *, int *, int *, char *, KTEXT ));
+
+/* fgetst.c */
+int fgetst P((FILE *, char *, int ));
+
+/* get_ad_tkt.c */
+int get_ad_tkt P((char *, char *, char *, int ));
+
+/* get_admhst.c */
+int krb_get_admhst P((char *, char *, int ));
+
+/* get_cred.c */
+int krb_get_cred P((char *, char *, char *, CREDENTIALS *));
+
+/* get_in_tkt.c */
+int krb_get_pw_in_tkt P((char *, char *, char *, char *, char *, int , char *));
+int placebo_read_password P((des_cblock *, char *, int ));
+int placebo_read_pw_string P((char *, int , char *, int ));
+
+/* get_krbhst.c */
+int krb_get_krbhst P((char *, char *, int ));
+
+/* get_krbrlm.c */
+int krb_get_lrealm P((char *, int ));
+
+/* get_phost.c */
+char *krb_get_phost P((char *));
+
+/* get_pw_tkt.c */
+int get_pw_tkt P((char *, char *, char *, char *));
+
+/* get_request.c */
+int get_request P((KTEXT , int , char **, char **));
+
+/* get_svc_in_tkt.c */
+int krb_get_svc_in_tkt P((char *, char *, char *, char *, char *, int , char *));
+
+/* get_tf_fullname.c */
+int krb_get_tf_fullname P((char *, char *, char *, char *));
+
+/* get_tf_realm.c */
+int krb_get_tf_realm P((char *, char *));
+
+/* getopt.c */
+int getopt P((int , char **, char *));
+
+/* getrealm.c */
+char *krb_realmofhost P((char *));
+
+/* getst.c */
+int getst P((int , char *, int ));
+
+/* in_tkt.c */
+int in_tkt P((char *, char *));
+
+/* k_gethostname.c */
+int k_gethostname P((char *, int ));
+
+/* klog.c */
+char *klog P((int , char *, int , int , int , int , int , int , int , int , int , int ));
+int kset_logfile P((char *));
+
+/* kname_parse.c */
+int kname_parse P((char *, char *, char *, char *));
+int k_isname P((char *));
+int k_isinst P((char *));
+int k_isrealm P((char *));
+
+/* kntoln.c */
+int krb_kntoln P((AUTH_DAT *, char *));
+
+/* krb_err_txt.c */
+
+/* krb_get_in_tkt.c */
+int krb_get_in_tkt P((char *, char *, char *, char *, char *, int , int (*key_proc )(), int (*decrypt_proc )(), char *));
+
+/* kuserok.c */
+int kuserok P((AUTH_DAT *, char *));
+
+/* log.c */
+void log P((char *, int , int , int , int , int , int , int , int , int , int ));
+int set_logfile P((char *));
+int new_log P((long , char *));
+
+/* mk_err.c */
+long krb_mk_err P((u_char *, long , char *));
+
+/* mk_priv.c */
+long krb_mk_priv P((u_char *, u_char *, u_long , Key_schedule , C_Block , struct sockaddr_in *, struct sockaddr_in *));
+
+/* mk_req.c */
+int krb_mk_req P((KTEXT , char *, char *, char *, long ));
+int krb_set_lifetime P((int ));
+
+/* mk_safe.c */
+long krb_mk_safe P((u_char *, u_char *, u_long , C_Block *, struct sockaddr_in *, struct sockaddr_in *));
+
+/* month_sname.c */
+char *month_sname P((int ));
+
+/* netread.c */
+int krb_net_read P((int , char *, int ));
+
+/* netwrite.c */
+int krb_net_write P((int , char *, int ));
+
+/* one.c */
+
+/* pkt_cipher.c */
+KTEXT pkt_cipher P((KTEXT ));
+
+/* pkt_clen.c */
+int pkt_clen P((KTEXT ));
+
+/* rd_err.c */
+int krb_rd_err P((u_char *, u_long , long *, MSG_DAT *));
+
+/* rd_priv.c */
+long krb_rd_priv P((u_char *, u_long , Key_schedule , C_Block , struct sockaddr_in *, struct sockaddr_in *, MSG_DAT *));
+
+/* rd_req.c */
+int krb_set_key P((char *, int ));
+int krb_rd_req P((KTEXT , char *, char *, long , AUTH_DAT *, char *));
+
+/* rd_safe.c */
+long krb_rd_safe P((u_char *, u_long , C_Block *, struct sockaddr_in *, struct sockaddr_in *, MSG_DAT *));
+
+/* read_service_key.c */
+int read_service_key P((char *, char *, char *, int , char *, char *));
+
+/* recvauth.c */
+int krb_recvauth P((long , int , KTEXT , char *, char *, struct sockaddr_in *, struct sockaddr_in *, AUTH_DAT *, char *, Key_schedule , char *));
+
+/* save_credentials.c */
+int save_credentials P((char *, char *, char *, C_Block , int , int , KTEXT , long ));
+
+/* send_to_kdc.c */
+int send_to_kdc P((KTEXT , KTEXT , char *));
+
+/* sendauth.c */
+int krb_sendauth P((long , int , KTEXT , char *, char *, char *, u_long , MSG_DAT *, CREDENTIALS *, Key_schedule , struct sockaddr_in *, struct sockaddr_in *, char *));
+int krb_sendsvc P((int , char *));
+
+/* setenv.c */
+int setenv P((char *, char *, int ));
+void unsetenv P((char *));
+char *getenv P((char *));
+char *_findenv P((char *, int *));
+
+/* stime.c */
+char *stime P((long *));
+
+/* tf_shm.c */
+int krb_shm_create P((char *));
+int krb_is_diskless P((void ));
+int krb_shm_dest P((char *));
+
+/* tf_util.c */
+int tf_init P((char *, int ));
+int tf_get_pname P((char *));
+int tf_get_pinst P((char *));
+int tf_get_cred P((CREDENTIALS *));
+int tf_close P((void ));
+int tf_save_cred P((char *, char *, char *, C_Block , int , int , KTEXT , long ));
+
+/* tkt_string.c */
+char *tkt_string P((void ));
+void krb_set_tkt_string P((char *));
+
+/* util.c */
+int ad_print P((AUTH_DAT *));
+int placebo_cblock_print P((des_cblock ));
+
+#undef P
diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c
new file mode 100644
index 0000000..896405c
--- /dev/null
+++ b/usr.bin/telnet/main.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include "ring.h"
+#include "externs.h"
+#include "defines.h"
+
+/* These values need to be the same as defined in libtelnet/kerberos5.c */
+/* Either define them in both places, or put in some common header file. */
+#define OPTS_FORWARD_CREDS 0x00000002
+#define OPTS_FORWARDABLE_CREDS 0x00000001
+
+#if 0
+#define FORWARD
+#endif
+
+/*
+ * Initialize variables.
+ */
+ void
+tninit()
+{
+ init_terminal();
+
+ init_network();
+
+ init_telnet();
+
+ init_sys();
+
+#if defined(TN3270)
+ init_3270();
+#endif
+}
+
+ void
+usage()
+{
+ fprintf(stderr, "Usage: %s %s%s%s%s\n",
+ prompt,
+#ifdef AUTHENTICATION
+ "[-8] [-E] [-K] [-L] [-S tos] [-X atype] [-a] [-c] [-d] [-e char]",
+ "\n\t[-k realm] [-l user] [-f/-F] [-n tracefile] ",
+#else
+ "[-8] [-E] [-L] [-S tos] [-a] [-c] [-d] [-e char] [-l user]",
+ "\n\t[-n tracefile]",
+#endif
+#if defined(TN3270) && defined(unix)
+# ifdef AUTHENTICATION
+ "[-noasynch] [-noasynctty]\n\t[-noasyncnet] [-r] [-t transcom] ",
+# else
+ "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t[-t transcom]",
+# endif
+#else
+ "[-r] ",
+#endif
+ "[host-name [port]]"
+ );
+ exit(1);
+}
+
+/*
+ * main. Parse arguments, invoke the protocol or command parser.
+ */
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ char *user, *strrchr();
+#ifdef FORWARD
+ extern int forward_flags;
+#endif /* FORWARD */
+
+ tninit(); /* Clear out things */
+#if defined(CRAY) && !defined(__STDC__)
+ _setlist_init(); /* Work around compiler bug */
+#endif
+
+ TerminalSaveState();
+
+ if (prompt = strrchr(argv[0], '/'))
+ ++prompt;
+ else
+ prompt = argv[0];
+
+ user = NULL;
+
+ rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE;
+ autologin = -1;
+
+ while ((ch = getopt(argc, argv, "8EKLS:X:acde:fFk:l:n:rt:x")) != EOF) {
+ switch(ch) {
+ case '8':
+ eight = 3; /* binary output and input */
+ break;
+ case 'E':
+ rlogin = escape = _POSIX_VDISABLE;
+ break;
+ case 'K':
+#ifdef AUTHENTICATION
+ autologin = 0;
+#endif
+ break;
+ case 'L':
+ eight |= 2; /* binary output only */
+ break;
+ case 'S':
+ {
+#ifdef HAS_GETTOS
+ extern int tos;
+
+ if ((tos = parsetos(optarg, "tcp")) < 0)
+ fprintf(stderr, "%s%s%s%s\n",
+ prompt, ": Bad TOS argument '",
+ optarg,
+ "; will try to use default TOS");
+#else
+ fprintf(stderr,
+ "%s: Warning: -S ignored, no parsetos() support.\n",
+ prompt);
+#endif
+ }
+ break;
+ case 'X':
+#ifdef AUTHENTICATION
+ auth_disable_name(optarg);
+#endif
+ break;
+ case 'a':
+ autologin = 1;
+ break;
+ case 'c':
+ skiprc = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'e':
+ set_escape_char(optarg);
+ break;
+ case 'f':
+#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD)
+ if (forward_flags & OPTS_FORWARD_CREDS) {
+ fprintf(stderr,
+ "%s: Only one of -f and -F allowed.\n",
+ prompt);
+ usage();
+ }
+ forward_flags |= OPTS_FORWARD_CREDS;
+#else
+ fprintf(stderr,
+ "%s: Warning: -f ignored, no Kerberos V5 support.\n",
+ prompt);
+#endif
+ break;
+ case 'F':
+#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD)
+ if (forward_flags & OPTS_FORWARD_CREDS) {
+ fprintf(stderr,
+ "%s: Only one of -f and -F allowed.\n",
+ prompt);
+ usage();
+ }
+ forward_flags |= OPTS_FORWARD_CREDS;
+ forward_flags |= OPTS_FORWARDABLE_CREDS;
+#else
+ fprintf(stderr,
+ "%s: Warning: -F ignored, no Kerberos V5 support.\n",
+ prompt);
+#endif
+ break;
+ case 'k':
+#if defined(AUTHENTICATION) && defined(KRB4)
+ {
+ extern char *dest_realm, dst_realm_buf[], dst_realm_sz;
+ dest_realm = dst_realm_buf;
+ (void)strncpy(dest_realm, optarg, dst_realm_sz);
+ }
+#else
+ fprintf(stderr,
+ "%s: Warning: -k ignored, no Kerberos V4 support.\n",
+ prompt);
+#endif
+ break;
+ case 'l':
+ autologin = 1;
+ user = optarg;
+ break;
+ case 'n':
+#if defined(TN3270) && defined(unix)
+ /* distinguish between "-n oasynch" and "-noasynch" */
+ if (argv[optind - 1][0] == '-' && argv[optind - 1][1]
+ == 'n' && argv[optind - 1][2] == 'o') {
+ if (!strcmp(optarg, "oasynch")) {
+ noasynchtty = 1;
+ noasynchnet = 1;
+ } else if (!strcmp(optarg, "oasynchtty"))
+ noasynchtty = 1;
+ else if (!strcmp(optarg, "oasynchnet"))
+ noasynchnet = 1;
+ } else
+#endif /* defined(TN3270) && defined(unix) */
+ SetNetTrace(optarg);
+ break;
+ case 'r':
+ rlogin = '~';
+ break;
+ case 't':
+#if defined(TN3270) && defined(unix)
+ transcom = tline;
+ (void)strcpy(transcom, optarg);
+#else
+ fprintf(stderr,
+ "%s: Warning: -t ignored, no TN3270 support.\n",
+ prompt);
+#endif
+ break;
+ case 'x':
+ fprintf(stderr,
+ "%s: Warning: -x ignored, no ENCRYPT support.\n",
+ prompt);
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (autologin == -1)
+ autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1;
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc) {
+ char *args[7], **argp = args;
+
+ if (argc > 2)
+ usage();
+ *argp++ = prompt;
+ if (user) {
+ *argp++ = "-l";
+ *argp++ = user;
+ }
+ *argp++ = argv[0]; /* host */
+ if (argc > 1)
+ *argp++ = argv[1]; /* port */
+ *argp = 0;
+
+ if (setjmp(toplevel) != 0)
+ Exit(0);
+ if (tn(argp - args, args) == 1)
+ return (0);
+ else
+ return (1);
+ }
+ (void)setjmp(toplevel);
+ for (;;) {
+#ifdef TN3270
+ if (shell_active)
+ shell_continue();
+ else
+#endif
+ command(1, 0, 0);
+ }
+}
diff --git a/usr.bin/telnet/network.c b/usr.bin/telnet/network.c
new file mode 100644
index 0000000..60e8591
--- /dev/null
+++ b/usr.bin/telnet/network.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)network.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include <arpa/telnet.h>
+
+#include "ring.h"
+
+#include "defines.h"
+#include "externs.h"
+#include "fdset.h"
+
+Ring netoring, netiring;
+unsigned char netobuf[2*BUFSIZ], netibuf[BUFSIZ];
+
+/*
+ * Initialize internal network data structures.
+ */
+
+ void
+init_network()
+{
+ if (ring_init(&netoring, netobuf, sizeof netobuf) != 1) {
+ exit(1);
+ }
+ if (ring_init(&netiring, netibuf, sizeof netibuf) != 1) {
+ exit(1);
+ }
+ NetTrace = stdout;
+}
+
+
+/*
+ * Check to see if any out-of-band data exists on a socket (for
+ * Telnet "synch" processing).
+ */
+
+ int
+stilloob()
+{
+ static struct timeval timeout = { 0 };
+ fd_set excepts;
+ int value;
+
+ do {
+ FD_ZERO(&excepts);
+ FD_SET(net, &excepts);
+ value = select(net+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
+ } while ((value == -1) && (errno == EINTR));
+
+ if (value < 0) {
+ perror("select");
+ (void) quit();
+ /* NOTREACHED */
+ }
+ if (FD_ISSET(net, &excepts)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * setneturg()
+ *
+ * Sets "neturg" to the current location.
+ */
+
+ void
+setneturg()
+{
+ ring_mark(&netoring);
+}
+
+
+/*
+ * netflush
+ * Send as much data as possible to the network,
+ * handling requests for urgent data.
+ *
+ * The return value indicates whether we did any
+ * useful work.
+ */
+
+
+ int
+netflush()
+{
+ register int n, n1;
+
+ if ((n1 = n = ring_full_consecutive(&netoring)) > 0) {
+ if (!ring_at_mark(&netoring)) {
+ n = send(net, (char *)netoring.consume, n, 0); /* normal write */
+ } else {
+ /*
+ * In 4.2 (and 4.3) systems, there is some question about
+ * what byte in a sendOOB operation is the "OOB" data.
+ * To make ourselves compatible, we only send ONE byte
+ * out of band, the one WE THINK should be OOB (though
+ * we really have more the TCP philosophy of urgent data
+ * rather than the Unix philosophy of OOB data).
+ */
+ n = send(net, (char *)netoring.consume, 1, MSG_OOB);/* URGENT data */
+ }
+ }
+ if (n < 0) {
+ if (errno != ENOBUFS && errno != EWOULDBLOCK) {
+ setcommandmode();
+ perror(hostname);
+ (void)NetClose(net);
+ ring_clear_mark(&netoring);
+ longjmp(peerdied, -1);
+ /*NOTREACHED*/
+ }
+ n = 0;
+ }
+ if (netdata && n) {
+ Dump('>', netoring.consume, n);
+ }
+ if (n) {
+ ring_consumed(&netoring, n);
+ /*
+ * If we sent all, and more to send, then recurse to pick
+ * up the other half.
+ */
+ if ((n1 == n) && ring_full_consecutive(&netoring)) {
+ (void) netflush();
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/usr.bin/telnet/ring.c b/usr.bin/telnet/ring.c
new file mode 100644
index 0000000..6de5a67
--- /dev/null
+++ b/usr.bin/telnet/ring.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ring.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ *(((
+ * full: [consume, supply)
+ * empty: [supply, consume)
+ *]]]
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef size_t
+#undef size_t
+#endif
+
+#include <sys/types.h>
+#ifndef FILIO_H
+#include <sys/ioctl.h>
+#endif
+#include <sys/socket.h>
+
+#include "ring.h"
+#include "general.h"
+
+/* Internal macros */
+
+#if !defined(MIN)
+#define MIN(a,b) (((a)<(b))? (a):(b))
+#endif /* !defined(MIN) */
+
+#define ring_subtract(d,a,b) (((a)-(b) >= 0)? \
+ (a)-(b): (((a)-(b))+(d)->size))
+
+#define ring_increment(d,a,c) (((a)+(c) < (d)->top)? \
+ (a)+(c) : (((a)+(c))-(d)->size))
+
+#define ring_decrement(d,a,c) (((a)-(c) >= (d)->bottom)? \
+ (a)-(c) : (((a)-(c))-(d)->size))
+
+
+/*
+ * The following is a clock, used to determine full, empty, etc.
+ *
+ * There is some trickiness here. Since the ring buffers are initialized
+ * to ZERO on allocation, we need to make sure, when interpreting the
+ * clock, that when the times are EQUAL, then the buffer is FULL.
+ */
+static u_long ring_clock = 0;
+
+
+#define ring_empty(d) (((d)->consume == (d)->supply) && \
+ ((d)->consumetime >= (d)->supplytime))
+#define ring_full(d) (((d)->supply == (d)->consume) && \
+ ((d)->supplytime > (d)->consumetime))
+
+
+
+
+
+/* Buffer state transition routines */
+
+ ring_init(ring, buffer, count)
+Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ memset((char *)ring, 0, sizeof *ring);
+
+ ring->size = count;
+
+ ring->supply = ring->consume = ring->bottom = buffer;
+
+ ring->top = ring->bottom+ring->size;
+
+
+ return 1;
+}
+
+/* Mark routines */
+
+/*
+ * Mark the most recently supplied byte.
+ */
+
+ void
+ring_mark(ring)
+ Ring *ring;
+{
+ ring->mark = ring_decrement(ring, ring->supply, 1);
+}
+
+/*
+ * Is the ring pointing to the mark?
+ */
+
+ int
+ring_at_mark(ring)
+ Ring *ring;
+{
+ if (ring->mark == ring->consume) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Clear any mark set on the ring.
+ */
+
+ void
+ring_clear_mark(ring)
+ Ring *ring;
+{
+ ring->mark = 0;
+}
+
+/*
+ * Add characters from current segment to ring buffer.
+ */
+ void
+ring_supplied(ring, count)
+ Ring *ring;
+ int count;
+{
+ ring->supply = ring_increment(ring, ring->supply, count);
+ ring->supplytime = ++ring_clock;
+}
+
+/*
+ * We have just consumed "c" bytes.
+ */
+ void
+ring_consumed(ring, count)
+ Ring *ring;
+ int count;
+{
+ if (count == 0) /* don't update anything */
+ return;
+
+ if (ring->mark &&
+ (ring_subtract(ring, ring->mark, ring->consume) < count)) {
+ ring->mark = 0;
+ }
+ ring->consume = ring_increment(ring, ring->consume, count);
+ ring->consumetime = ++ring_clock;
+ /*
+ * Try to encourage "ring_empty_consecutive()" to be large.
+ */
+ if (ring_empty(ring)) {
+ ring->consume = ring->supply = ring->bottom;
+ }
+}
+
+
+
+/* Buffer state query routines */
+
+
+/* Number of bytes that may be supplied */
+ int
+ring_empty_count(ring)
+ Ring *ring;
+{
+ if (ring_empty(ring)) { /* if empty */
+ return ring->size;
+ } else {
+ return ring_subtract(ring, ring->consume, ring->supply);
+ }
+}
+
+/* number of CONSECUTIVE bytes that may be supplied */
+ int
+ring_empty_consecutive(ring)
+ Ring *ring;
+{
+ if ((ring->consume < ring->supply) || ring_empty(ring)) {
+ /*
+ * if consume is "below" supply, or empty, then
+ * return distance to the top
+ */
+ return ring_subtract(ring, ring->top, ring->supply);
+ } else {
+ /*
+ * else, return what we may.
+ */
+ return ring_subtract(ring, ring->consume, ring->supply);
+ }
+}
+
+/* Return the number of bytes that are available for consuming
+ * (but don't give more than enough to get to cross over set mark)
+ */
+
+ int
+ring_full_count(ring)
+ Ring *ring;
+{
+ if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+ if (ring_full(ring)) {
+ return ring->size; /* nothing consumed, but full */
+ } else {
+ return ring_subtract(ring, ring->supply, ring->consume);
+ }
+ } else {
+ return ring_subtract(ring, ring->mark, ring->consume);
+ }
+}
+
+/*
+ * Return the number of CONSECUTIVE bytes available for consuming.
+ * However, don't return more than enough to cross over set mark.
+ */
+ int
+ring_full_consecutive(ring)
+ Ring *ring;
+{
+ if ((ring->mark == 0) || (ring->mark == ring->consume)) {
+ if ((ring->supply < ring->consume) || ring_full(ring)) {
+ return ring_subtract(ring, ring->top, ring->consume);
+ } else {
+ return ring_subtract(ring, ring->supply, ring->consume);
+ }
+ } else {
+ if (ring->mark < ring->consume) {
+ return ring_subtract(ring, ring->top, ring->consume);
+ } else { /* Else, distance to mark */
+ return ring_subtract(ring, ring->mark, ring->consume);
+ }
+ }
+}
+
+/*
+ * Move data into the "supply" portion of of the ring buffer.
+ */
+ void
+ring_supply_data(ring, buffer, count)
+ Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ int i;
+
+ while (count) {
+ i = MIN(count, ring_empty_consecutive(ring));
+ memcpy(ring->supply, buffer, i);
+ ring_supplied(ring, i);
+ count -= i;
+ buffer += i;
+ }
+}
+
+#ifdef notdef
+
+/*
+ * Move data from the "consume" portion of the ring buffer
+ */
+ void
+ring_consume_data(ring, buffer, count)
+ Ring *ring;
+ unsigned char *buffer;
+ int count;
+{
+ int i;
+
+ while (count) {
+ i = MIN(count, ring_full_consecutive(ring));
+ memcpy(buffer, ring->consume, i);
+ ring_consumed(ring, i);
+ count -= i;
+ buffer += i;
+ }
+}
+#endif
+
diff --git a/usr.bin/telnet/ring.h b/usr.bin/telnet/ring.h
new file mode 100644
index 0000000..cc2cb12
--- /dev/null
+++ b/usr.bin/telnet/ring.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ring.h 8.1 (Berkeley) 6/6/93
+ */
+
+#if defined(P)
+# undef P
+#endif
+
+#if defined(__STDC__) || defined(LINT_ARGS)
+# define P(x) x
+#else
+# define P(x) ()
+#endif
+
+/*
+ * This defines a structure for a ring buffer.
+ *
+ * The circular buffer has two parts:
+ *(((
+ * full: [consume, supply)
+ * empty: [supply, consume)
+ *]]]
+ *
+ */
+typedef struct {
+ unsigned char *consume, /* where data comes out of */
+ *supply, /* where data comes in to */
+ *bottom, /* lowest address in buffer */
+ *top, /* highest address+1 in buffer */
+ *mark; /* marker (user defined) */
+ int size; /* size in bytes of buffer */
+ u_long consumetime, /* help us keep straight full, empty, etc. */
+ supplytime;
+} Ring;
+
+/* Here are some functions and macros to deal with the ring buffer */
+
+/* Initialization routine */
+extern int
+ ring_init P((Ring *ring, unsigned char *buffer, int count));
+
+/* Data movement routines */
+extern void
+ ring_supply_data P((Ring *ring, unsigned char *buffer, int count));
+#ifdef notdef
+extern void
+ ring_consume_data P((Ring *ring, unsigned char *buffer, int count));
+#endif
+
+/* Buffer state transition routines */
+extern void
+ ring_supplied P((Ring *ring, int count)),
+ ring_consumed P((Ring *ring, int count));
+
+/* Buffer state query routines */
+extern int
+ ring_empty_count P((Ring *ring)),
+ ring_empty_consecutive P((Ring *ring)),
+ ring_full_count P((Ring *ring)),
+ ring_full_consecutive P((Ring *ring));
+
+
+extern void
+ ring_clear_mark(),
+ ring_mark();
diff --git a/usr.bin/telnet/sys_bsd.c b/usr.bin/telnet/sys_bsd.c
new file mode 100644
index 0000000..f08161c
--- /dev/null
+++ b/usr.bin/telnet/sys_bsd.c
@@ -0,0 +1,1183 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sys_bsd.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+/*
+ * The following routines try to encapsulate what is system dependent
+ * (at least between 4.x and dos) which is used in telnet.c.
+ */
+
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <errno.h>
+#include <arpa/telnet.h>
+
+#include "ring.h"
+
+#include "fdset.h"
+
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+
+#if defined(CRAY) || (defined(USE_TERMIO) && !defined(SYSV_TERMIO))
+#define SIG_FUNC_RET void
+#else
+#define SIG_FUNC_RET int
+#endif
+
+#ifdef SIGINFO
+extern SIG_FUNC_RET ayt_status();
+#endif
+
+int
+ tout, /* Output file descriptor */
+ tin, /* Input file descriptor */
+ net;
+
+#ifndef USE_TERMIO
+struct tchars otc = { 0 }, ntc = { 0 };
+struct ltchars oltc = { 0 }, nltc = { 0 };
+struct sgttyb ottyb = { 0 }, nttyb = { 0 };
+int olmode = 0;
+# define cfgetispeed(ptr) (ptr)->sg_ispeed
+# define cfgetospeed(ptr) (ptr)->sg_ospeed
+# define old_tc ottyb
+
+#else /* USE_TERMIO */
+struct termio old_tc = { 0 };
+extern struct termio new_tc;
+
+# ifndef TCSANOW
+# ifdef TCSETS
+# define TCSANOW TCSETS
+# define TCSADRAIN TCSETSW
+# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t)
+# else
+# ifdef TCSETA
+# define TCSANOW TCSETA
+# define TCSADRAIN TCSETAW
+# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t)
+# else
+# define TCSANOW TIOCSETA
+# define TCSADRAIN TIOCSETAW
+# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t)
+# endif
+# endif
+# define tcsetattr(f, a, t) ioctl(f, a, (char *)t)
+# define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD)
+# ifdef CIBAUD
+# define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT)
+# else
+# define cfgetispeed(ptr) cfgetospeed(ptr)
+# endif
+# endif /* TCSANOW */
+# ifdef sysV88
+# define TIOCFLUSH TC_PX_DRAIN
+# endif
+#endif /* USE_TERMIO */
+
+static fd_set ibits, obits, xbits;
+
+
+ void
+init_sys()
+{
+ tout = fileno(stdout);
+ tin = fileno(stdin);
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ FD_ZERO(&xbits);
+
+ errno = 0;
+}
+
+
+ int
+TerminalWrite(buf, n)
+ char *buf;
+ int n;
+{
+ return write(tout, buf, n);
+}
+
+ int
+TerminalRead(buf, n)
+ char *buf;
+ int n;
+{
+ return read(tin, buf, n);
+}
+
+/*
+ *
+ */
+
+ int
+TerminalAutoFlush()
+{
+#if defined(LNOFLSH)
+ int flush;
+
+ ioctl(0, TIOCLGET, (char *)&flush);
+ return !(flush&LNOFLSH); /* if LNOFLSH, no autoflush */
+#else /* LNOFLSH */
+ return 1;
+#endif /* LNOFLSH */
+}
+
+#ifdef KLUDGELINEMODE
+extern int kludgelinemode;
+#endif
+/*
+ * TerminalSpecialChars()
+ *
+ * Look at an input character to see if it is a special character
+ * and decide what to do.
+ *
+ * Output:
+ *
+ * 0 Don't add this character.
+ * 1 Do add this character
+ */
+
+extern void xmitAO(), xmitEL(), xmitEC(), intp(), sendbrk();
+
+ int
+TerminalSpecialChars(c)
+ int c;
+{
+ if (c == termIntChar) {
+ intp();
+ return 0;
+ } else if (c == termQuitChar) {
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ sendbrk();
+ else
+#endif
+ sendabort();
+ return 0;
+ } else if (c == termEofChar) {
+ if (my_want_state_is_will(TELOPT_LINEMODE)) {
+ sendeof();
+ return 0;
+ }
+ return 1;
+ } else if (c == termSuspChar) {
+ sendsusp();
+ return(0);
+ } else if (c == termFlushChar) {
+ xmitAO(); /* Transmit Abort Output */
+ return 0;
+ } else if (!MODE_LOCAL_CHARS(globalmode)) {
+ if (c == termKillChar) {
+ xmitEL();
+ return 0;
+ } else if (c == termEraseChar) {
+ xmitEC(); /* Transmit Erase Character */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * Flush output to the terminal
+ */
+
+ void
+TerminalFlushOutput()
+{
+#ifdef TIOCFLUSH
+ (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
+#else
+ (void) ioctl(fileno(stdout), TCFLSH, (char *) 0);
+#endif
+}
+
+ void
+TerminalSaveState()
+{
+#ifndef USE_TERMIO
+ ioctl(0, TIOCGETP, (char *)&ottyb);
+ ioctl(0, TIOCGETC, (char *)&otc);
+ ioctl(0, TIOCGLTC, (char *)&oltc);
+ ioctl(0, TIOCLGET, (char *)&olmode);
+
+ ntc = otc;
+ nltc = oltc;
+ nttyb = ottyb;
+
+#else /* USE_TERMIO */
+ tcgetattr(0, &old_tc);
+
+ new_tc = old_tc;
+
+#ifndef VDISCARD
+ termFlushChar = CONTROL('O');
+#endif
+#ifndef VWERASE
+ termWerasChar = CONTROL('W');
+#endif
+#ifndef VREPRINT
+ termRprntChar = CONTROL('R');
+#endif
+#ifndef VLNEXT
+ termLiteralNextChar = CONTROL('V');
+#endif
+#ifndef VSTART
+ termStartChar = CONTROL('Q');
+#endif
+#ifndef VSTOP
+ termStopChar = CONTROL('S');
+#endif
+#ifndef VSTATUS
+ termAytChar = CONTROL('T');
+#endif
+#endif /* USE_TERMIO */
+}
+
+ cc_t *
+tcval(func)
+ register int func;
+{
+ switch(func) {
+ case SLC_IP: return(&termIntChar);
+ case SLC_ABORT: return(&termQuitChar);
+ case SLC_EOF: return(&termEofChar);
+ case SLC_EC: return(&termEraseChar);
+ case SLC_EL: return(&termKillChar);
+ case SLC_XON: return(&termStartChar);
+ case SLC_XOFF: return(&termStopChar);
+ case SLC_FORW1: return(&termForw1Char);
+#ifdef USE_TERMIO
+ case SLC_FORW2: return(&termForw2Char);
+# ifdef VDISCARD
+ case SLC_AO: return(&termFlushChar);
+# endif
+# ifdef VSUSP
+ case SLC_SUSP: return(&termSuspChar);
+# endif
+# ifdef VWERASE
+ case SLC_EW: return(&termWerasChar);
+# endif
+# ifdef VREPRINT
+ case SLC_RP: return(&termRprntChar);
+# endif
+# ifdef VLNEXT
+ case SLC_LNEXT: return(&termLiteralNextChar);
+# endif
+# ifdef VSTATUS
+ case SLC_AYT: return(&termAytChar);
+# endif
+#endif
+
+ case SLC_SYNCH:
+ case SLC_BRK:
+ case SLC_EOR:
+ default:
+ return((cc_t *)0);
+ }
+}
+
+ void
+TerminalDefaultChars()
+{
+#ifndef USE_TERMIO
+ ntc = otc;
+ nltc = oltc;
+ nttyb.sg_kill = ottyb.sg_kill;
+ nttyb.sg_erase = ottyb.sg_erase;
+#else /* USE_TERMIO */
+ memcpy(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc));
+# ifndef VDISCARD
+ termFlushChar = CONTROL('O');
+# endif
+# ifndef VWERASE
+ termWerasChar = CONTROL('W');
+# endif
+# ifndef VREPRINT
+ termRprntChar = CONTROL('R');
+# endif
+# ifndef VLNEXT
+ termLiteralNextChar = CONTROL('V');
+# endif
+# ifndef VSTART
+ termStartChar = CONTROL('Q');
+# endif
+# ifndef VSTOP
+ termStopChar = CONTROL('S');
+# endif
+# ifndef VSTATUS
+ termAytChar = CONTROL('T');
+# endif
+#endif /* USE_TERMIO */
+}
+
+#ifdef notdef
+void
+TerminalRestoreState()
+{
+}
+#endif
+
+/*
+ * TerminalNewMode - set up terminal to a specific mode.
+ * MODE_ECHO: do local terminal echo
+ * MODE_FLOW: do local flow control
+ * MODE_TRAPSIG: do local mapping to TELNET IAC sequences
+ * MODE_EDIT: do local line editing
+ *
+ * Command mode:
+ * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG
+ * local echo
+ * local editing
+ * local xon/xoff
+ * local signal mapping
+ *
+ * Linemode:
+ * local/no editing
+ * Both Linemode and Single Character mode:
+ * local/remote echo
+ * local/no xon/xoff
+ * local/no signal mapping
+ */
+
+
+ void
+TerminalNewMode(f)
+ register int f;
+{
+ static int prevmode = 0;
+#ifndef USE_TERMIO
+ struct tchars tc;
+ struct ltchars ltc;
+ struct sgttyb sb;
+ int lmode;
+#else /* USE_TERMIO */
+ struct termio tmp_tc;
+#endif /* USE_TERMIO */
+ int onoff;
+ int old;
+ cc_t esc;
+
+ globalmode = f&~MODE_FORCE;
+ if (prevmode == f)
+ return;
+
+ /*
+ * Write any outstanding data before switching modes
+ * ttyflush() returns 0 only when there is no more data
+ * left to write out, it returns -1 if it couldn't do
+ * anything at all, otherwise it returns 1 + the number
+ * of characters left to write.
+#ifndef USE_TERMIO
+ * We would really like ask the kernel to wait for the output
+ * to drain, like we can do with the TCSADRAIN, but we don't have
+ * that option. The only ioctl that waits for the output to
+ * drain, TIOCSETP, also flushes the input queue, which is NOT
+ * what we want (TIOCSETP is like TCSADFLUSH).
+#endif
+ */
+ old = ttyflush(SYNCHing|flushout);
+ if (old < 0 || old > 1) {
+#ifdef USE_TERMIO
+ tcgetattr(tin, &tmp_tc);
+#endif /* USE_TERMIO */
+ do {
+ /*
+ * Wait for data to drain, then flush again.
+ */
+#ifdef USE_TERMIO
+ tcsetattr(tin, TCSADRAIN, &tmp_tc);
+#endif /* USE_TERMIO */
+ old = ttyflush(SYNCHing|flushout);
+ } while (old < 0 || old > 1);
+ }
+
+ old = prevmode;
+ prevmode = f&~MODE_FORCE;
+#ifndef USE_TERMIO
+ sb = nttyb;
+ tc = ntc;
+ ltc = nltc;
+ lmode = olmode;
+#else
+ tmp_tc = new_tc;
+#endif
+
+ if (f&MODE_ECHO) {
+#ifndef USE_TERMIO
+ sb.sg_flags |= ECHO;
+#else
+ tmp_tc.c_lflag |= ECHO;
+ tmp_tc.c_oflag |= ONLCR;
+ if (crlf)
+ tmp_tc.c_iflag |= ICRNL;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~ECHO;
+#else
+ tmp_tc.c_lflag &= ~ECHO;
+ tmp_tc.c_oflag &= ~ONLCR;
+# ifdef notdef
+ if (crlf)
+ tmp_tc.c_iflag &= ~ICRNL;
+# endif
+#endif
+ }
+
+ if ((f&MODE_FLOW) == 0) {
+#ifndef USE_TERMIO
+ tc.t_startc = _POSIX_VDISABLE;
+ tc.t_stopc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */
+ } else {
+ if (restartany < 0) {
+ tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */
+ } else if (restartany > 0) {
+ tmp_tc.c_iflag |= IXOFF|IXON|IXANY;
+ } else {
+ tmp_tc.c_iflag |= IXOFF|IXON;
+ tmp_tc.c_iflag &= ~IXANY;
+ }
+#endif
+ }
+
+ if ((f&MODE_TRAPSIG) == 0) {
+#ifndef USE_TERMIO
+ tc.t_intrc = _POSIX_VDISABLE;
+ tc.t_quitc = _POSIX_VDISABLE;
+ tc.t_eofc = _POSIX_VDISABLE;
+ ltc.t_suspc = _POSIX_VDISABLE;
+ ltc.t_dsuspc = _POSIX_VDISABLE;
+#else
+ tmp_tc.c_lflag &= ~ISIG;
+#endif
+ localchars = 0;
+ } else {
+#ifdef USE_TERMIO
+ tmp_tc.c_lflag |= ISIG;
+#endif
+ localchars = 1;
+ }
+
+ if (f&MODE_EDIT) {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~CBREAK;
+ sb.sg_flags |= CRMOD;
+#else
+ tmp_tc.c_lflag |= ICANON;
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags |= CBREAK;
+ if (f&MODE_ECHO)
+ sb.sg_flags |= CRMOD;
+ else
+ sb.sg_flags &= ~CRMOD;
+#else
+ tmp_tc.c_lflag &= ~ICANON;
+ tmp_tc.c_iflag &= ~ICRNL;
+ tmp_tc.c_cc[VMIN] = 1;
+ tmp_tc.c_cc[VTIME] = 0;
+#endif
+ }
+
+ if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) {
+#ifndef USE_TERMIO
+ ltc.t_lnextc = _POSIX_VDISABLE;
+#else
+# ifdef VLNEXT
+ tmp_tc.c_cc[VLNEXT] = (cc_t)(_POSIX_VDISABLE);
+# endif
+#endif
+ }
+
+ if (f&MODE_SOFT_TAB) {
+#ifndef USE_TERMIO
+ sb.sg_flags |= XTABS;
+#else
+# ifdef OXTABS
+ tmp_tc.c_oflag |= OXTABS;
+# endif
+# ifdef TABDLY
+ tmp_tc.c_oflag &= ~TABDLY;
+ tmp_tc.c_oflag |= TAB3;
+# endif
+#endif
+ } else {
+#ifndef USE_TERMIO
+ sb.sg_flags &= ~XTABS;
+#else
+# ifdef OXTABS
+ tmp_tc.c_oflag &= ~OXTABS;
+# endif
+# ifdef TABDLY
+ tmp_tc.c_oflag &= ~TABDLY;
+# endif
+#endif
+ }
+
+ if (f&MODE_LIT_ECHO) {
+#ifndef USE_TERMIO
+ lmode &= ~LCTLECH;
+#else
+# ifdef ECHOCTL
+ tmp_tc.c_lflag &= ~ECHOCTL;
+# endif
+#endif
+ } else {
+#ifndef USE_TERMIO
+ lmode |= LCTLECH;
+#else
+# ifdef ECHOCTL
+ tmp_tc.c_lflag |= ECHOCTL;
+# endif
+#endif
+ }
+
+ if (f == -1) {
+ onoff = 0;
+ } else {
+#ifndef USE_TERMIO
+ if (f & MODE_OUTBIN)
+ lmode |= LLITOUT;
+ else
+ lmode &= ~LLITOUT;
+
+ if (f & MODE_INBIN)
+ lmode |= LPASS8;
+ else
+ lmode &= ~LPASS8;
+#else
+ if (f & MODE_INBIN)
+ tmp_tc.c_iflag &= ~ISTRIP;
+ else
+ tmp_tc.c_iflag |= ISTRIP;
+ if (f & MODE_OUTBIN) {
+ tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+ tmp_tc.c_cflag |= CS8;
+ tmp_tc.c_oflag &= ~OPOST;
+ } else {
+ tmp_tc.c_cflag &= ~(CSIZE|PARENB);
+ tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB);
+ tmp_tc.c_oflag |= OPOST;
+ }
+#endif
+ onoff = 1;
+ }
+
+ if (f != -1) {
+#ifdef SIGTSTP
+ SIG_FUNC_RET susp();
+#endif /* SIGTSTP */
+#ifdef SIGINFO
+ SIG_FUNC_RET ayt();
+#endif
+
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, susp);
+#endif /* SIGTSTP */
+#ifdef SIGINFO
+ (void) signal(SIGINFO, ayt);
+#endif
+#if defined(USE_TERMIO) && defined(NOKERNINFO)
+ tmp_tc.c_lflag |= NOKERNINFO;
+#endif
+ /*
+ * We don't want to process ^Y here. It's just another
+ * character that we'll pass on to the back end. It has
+ * to process it because it will be processed when the
+ * user attempts to read it, not when we send it.
+ */
+#ifndef USE_TERMIO
+ ltc.t_dsuspc = _POSIX_VDISABLE;
+#else
+# ifdef VDSUSP
+ tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE);
+# endif
+#endif
+#ifdef USE_TERMIO
+ /*
+ * If the VEOL character is already set, then use VEOL2,
+ * otherwise use VEOL.
+ */
+ esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape;
+ if ((tmp_tc.c_cc[VEOL] != esc)
+# ifdef VEOL2
+ && (tmp_tc.c_cc[VEOL2] != esc)
+# endif
+ ) {
+ if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE))
+ tmp_tc.c_cc[VEOL] = esc;
+# ifdef VEOL2
+ else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE))
+ tmp_tc.c_cc[VEOL2] = esc;
+# endif
+ }
+#else
+ if (tc.t_brkc == (cc_t)(_POSIX_VDISABLE))
+ tc.t_brkc = esc;
+#endif
+ } else {
+#ifdef SIGINFO
+ SIG_FUNC_RET ayt_status();
+
+ (void) signal(SIGINFO, ayt_status);
+#endif
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
+#endif /* SIGTSTP */
+#ifndef USE_TERMIO
+ ltc = oltc;
+ tc = otc;
+ sb = ottyb;
+ lmode = olmode;
+#else
+ tmp_tc = old_tc;
+#endif
+ }
+#ifndef USE_TERMIO
+ ioctl(tin, TIOCLSET, (char *)&lmode);
+ ioctl(tin, TIOCSLTC, (char *)&ltc);
+ ioctl(tin, TIOCSETC, (char *)&tc);
+ ioctl(tin, TIOCSETN, (char *)&sb);
+#else
+ if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0)
+ tcsetattr(tin, TCSANOW, &tmp_tc);
+#endif
+
+#if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
+# if !defined(sysV88)
+ ioctl(tin, FIONBIO, (char *)&onoff);
+ ioctl(tout, FIONBIO, (char *)&onoff);
+# endif
+#endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
+#if defined(TN3270)
+ if (noasynchtty == 0) {
+ ioctl(tin, FIOASYNC, (char *)&onoff);
+ }
+#endif /* defined(TN3270) */
+
+}
+
+/*
+ * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD).
+ */
+#if B4800 != 4800
+#define DECODE_BAUD
+#endif
+
+#ifdef DECODE_BAUD
+#ifndef B19200
+# define B19200 B9600
+#endif
+
+#ifndef B38400
+# define B38400 B19200
+#endif
+
+/*
+ * This code assumes that the values B0, B50, B75...
+ * are in ascending order. They do not have to be
+ * contiguous.
+ */
+struct termspeeds {
+ long speed;
+ long value;
+} termspeeds[] = {
+ { 0, B0 }, { 50, B50 }, { 75, B75 },
+ { 110, B110 }, { 134, B134 }, { 150, B150 },
+ { 200, B200 }, { 300, B300 }, { 600, B600 },
+ { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 },
+ { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 },
+ { 38400, B38400 }, { -1, B38400 }
+};
+#endif /* DECODE_BAUD */
+
+ void
+TerminalSpeeds(ispeed, ospeed)
+ long *ispeed;
+ long *ospeed;
+{
+#ifdef DECODE_BAUD
+ register struct termspeeds *tp;
+#endif /* DECODE_BAUD */
+ register long in, out;
+
+ out = cfgetospeed(&old_tc);
+ in = cfgetispeed(&old_tc);
+ if (in == 0)
+ in = out;
+
+#ifdef DECODE_BAUD
+ tp = termspeeds;
+ while ((tp->speed != -1) && (tp->value < in))
+ tp++;
+ *ispeed = tp->speed;
+
+ tp = termspeeds;
+ while ((tp->speed != -1) && (tp->value < out))
+ tp++;
+ *ospeed = tp->speed;
+#else /* DECODE_BAUD */
+ *ispeed = in;
+ *ospeed = out;
+#endif /* DECODE_BAUD */
+}
+
+ int
+TerminalWindowSize(rows, cols)
+ long *rows, *cols;
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+
+ if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) {
+ *rows = ws.ws_row;
+ *cols = ws.ws_col;
+ return 1;
+ }
+#endif /* TIOCGWINSZ */
+ return 0;
+}
+
+ int
+NetClose(fd)
+ int fd;
+{
+ return close(fd);
+}
+
+
+ void
+NetNonblockingIO(fd, onoff)
+ int fd;
+ int onoff;
+{
+ ioctl(fd, FIONBIO, (char *)&onoff);
+}
+
+#if defined(TN3270)
+ void
+NetSigIO(fd, onoff)
+ int fd;
+ int onoff;
+{
+ ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */
+}
+
+ void
+NetSetPgrp(fd)
+ int fd;
+{
+ int myPid;
+
+ myPid = getpid();
+ fcntl(fd, F_SETOWN, myPid);
+}
+#endif /*defined(TN3270)*/
+
+/*
+ * Various signal handling routines.
+ */
+
+ /* ARGSUSED */
+ SIG_FUNC_RET
+deadpeer(sig)
+ int sig;
+{
+ setcommandmode();
+ longjmp(peerdied, -1);
+}
+
+ /* ARGSUSED */
+ SIG_FUNC_RET
+intr(sig)
+ int sig;
+{
+ if (localchars) {
+ intp();
+ return;
+ }
+ setcommandmode();
+ longjmp(toplevel, -1);
+}
+
+ /* ARGSUSED */
+ SIG_FUNC_RET
+intr2(sig)
+ int sig;
+{
+ if (localchars) {
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode)
+ sendbrk();
+ else
+#endif
+ sendabort();
+ return;
+ }
+}
+
+#ifdef SIGTSTP
+ /* ARGSUSED */
+ SIG_FUNC_RET
+susp(sig)
+ int sig;
+{
+ if ((rlogin != _POSIX_VDISABLE) && rlogin_susp())
+ return;
+ if (localchars)
+ sendsusp();
+}
+#endif
+
+#ifdef SIGWINCH
+ /* ARGSUSED */
+ SIG_FUNC_RET
+sendwin(sig)
+ int sig;
+{
+ if (connected) {
+ sendnaws();
+ }
+}
+#endif
+
+#ifdef SIGINFO
+ /* ARGSUSED */
+ SIG_FUNC_RET
+ayt(sig)
+ int sig;
+{
+ if (connected)
+ sendayt();
+ else
+ ayt_status();
+}
+#endif
+
+
+ void
+sys_telnet_init()
+{
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGQUIT, intr2);
+ (void) signal(SIGPIPE, deadpeer);
+#ifdef SIGWINCH
+ (void) signal(SIGWINCH, sendwin);
+#endif
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, susp);
+#endif
+#ifdef SIGINFO
+ (void) signal(SIGINFO, ayt);
+#endif
+
+ setconnmode(0);
+
+ NetNonblockingIO(net, 1);
+
+#if defined(TN3270)
+ if (noasynchnet == 0) { /* DBX can't handle! */
+ NetSigIO(net, 1);
+ NetSetPgrp(net);
+ }
+#endif /* defined(TN3270) */
+
+#if defined(SO_OOBINLINE)
+ if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) {
+ perror("SetSockOpt");
+ }
+#endif /* defined(SO_OOBINLINE) */
+}
+
+/*
+ * Process rings -
+ *
+ * This routine tries to fill up/empty our various rings.
+ *
+ * The parameter specifies whether this is a poll operation,
+ * or a block-until-something-happens operation.
+ *
+ * The return value is 1 if something happened, 0 if not.
+ */
+
+ int
+process_rings(netin, netout, netex, ttyin, ttyout, poll)
+ int poll; /* If 0, then block until something to do */
+{
+ register int c;
+ /* One wants to be a bit careful about setting returnValue
+ * to one, since a one implies we did some useful work,
+ * and therefore probably won't be called to block next
+ * time (TN3270 mode only).
+ */
+ int returnValue = 0;
+ static struct timeval TimeValue = { 0 };
+
+ if (netout) {
+ FD_SET(net, &obits);
+ }
+ if (ttyout) {
+ FD_SET(tout, &obits);
+ }
+#if defined(TN3270)
+ if (ttyin) {
+ FD_SET(tin, &ibits);
+ }
+#else /* defined(TN3270) */
+ if (ttyin) {
+ FD_SET(tin, &ibits);
+ }
+#endif /* defined(TN3270) */
+#if defined(TN3270)
+ if (netin) {
+ FD_SET(net, &ibits);
+ }
+# else /* !defined(TN3270) */
+ if (netin) {
+ FD_SET(net, &ibits);
+ }
+# endif /* !defined(TN3270) */
+ if (netex) {
+ FD_SET(net, &xbits);
+ }
+ if ((c = select(16, &ibits, &obits, &xbits,
+ (poll == 0)? (struct timeval *)0 : &TimeValue)) < 0) {
+ if (c == -1) {
+ /*
+ * we can get EINTR if we are in line mode,
+ * and the user does an escape (TSTP), or
+ * some other signal generator.
+ */
+ if (errno == EINTR) {
+ return 0;
+ }
+# if defined(TN3270)
+ /*
+ * we can get EBADF if we were in transparent
+ * mode, and the transcom process died.
+ */
+ if (errno == EBADF) {
+ /*
+ * zero the bits (even though kernel does it)
+ * to make sure we are selecting on the right
+ * ones.
+ */
+ FD_ZERO(&ibits);
+ FD_ZERO(&obits);
+ FD_ZERO(&xbits);
+ return 0;
+ }
+# endif /* defined(TN3270) */
+ /* I don't like this, does it ever happen? */
+ printf("sleep(5) from telnet, after select\r\n");
+ sleep(5);
+ }
+ return 0;
+ }
+
+ /*
+ * Any urgent data?
+ */
+ if (FD_ISSET(net, &xbits)) {
+ FD_CLR(net, &xbits);
+ SYNCHing = 1;
+ (void) ttyflush(1); /* flush already enqueued data */
+ }
+
+ /*
+ * Something to read from the network...
+ */
+ if (FD_ISSET(net, &ibits)) {
+ int canread;
+
+ FD_CLR(net, &ibits);
+ canread = ring_empty_consecutive(&netiring);
+#if !defined(SO_OOBINLINE)
+ /*
+ * In 4.2 (and some early 4.3) systems, the
+ * OOB indication and data handling in the kernel
+ * is such that if two separate TCP Urgent requests
+ * come in, one byte of TCP data will be overlaid.
+ * This is fatal for Telnet, but we try to live
+ * with it.
+ *
+ * In addition, in 4.2 (and...), a special protocol
+ * is needed to pick up the TCP Urgent data in
+ * the correct sequence.
+ *
+ * What we do is: if we think we are in urgent
+ * mode, we look to see if we are "at the mark".
+ * If we are, we do an OOB receive. If we run
+ * this twice, we will do the OOB receive twice,
+ * but the second will fail, since the second
+ * time we were "at the mark", but there wasn't
+ * any data there (the kernel doesn't reset
+ * "at the mark" until we do a normal read).
+ * Once we've read the OOB data, we go ahead
+ * and do normal reads.
+ *
+ * There is also another problem, which is that
+ * since the OOB byte we read doesn't put us
+ * out of OOB state, and since that byte is most
+ * likely the TELNET DM (data mark), we would
+ * stay in the TELNET SYNCH (SYNCHing) state.
+ * So, clocks to the rescue. If we've "just"
+ * received a DM, then we test for the
+ * presence of OOB data when the receive OOB
+ * fails (and AFTER we did the normal mode read
+ * to clear "at the mark").
+ */
+ if (SYNCHing) {
+ int atmark;
+ static int bogus_oob = 0, first = 1;
+
+ ioctl(net, SIOCATMARK, (char *)&atmark);
+ if (atmark) {
+ c = recv(net, netiring.supply, canread, MSG_OOB);
+ if ((c == -1) && (errno == EINVAL)) {
+ c = recv(net, netiring.supply, canread, 0);
+ if (clocks.didnetreceive < clocks.gotDM) {
+ SYNCHing = stilloob(net);
+ }
+ } else if (first && c > 0) {
+ /*
+ * Bogosity check. Systems based on 4.2BSD
+ * do not return an error if you do a second
+ * recv(MSG_OOB). So, we do one. If it
+ * succeeds and returns exactly the same
+ * data, then assume that we are running
+ * on a broken system and set the bogus_oob
+ * flag. (If the data was different, then
+ * we probably got some valid new data, so
+ * increment the count...)
+ */
+ int i;
+ i = recv(net, netiring.supply + c, canread - c, MSG_OOB);
+ if (i == c &&
+ bcmp(netiring.supply, netiring.supply + c, i) == 0) {
+ bogus_oob = 1;
+ first = 0;
+ } else if (i < 0) {
+ bogus_oob = 0;
+ first = 0;
+ } else
+ c += i;
+ }
+ if (bogus_oob && c > 0) {
+ int i;
+ /*
+ * Bogosity. We have to do the read
+ * to clear the atmark to get out of
+ * an infinate loop.
+ */
+ i = read(net, netiring.supply + c, canread - c);
+ if (i > 0)
+ c += i;
+ }
+ } else {
+ c = recv(net, netiring.supply, canread, 0);
+ }
+ } else {
+ c = recv(net, netiring.supply, canread, 0);
+ }
+ settimer(didnetreceive);
+#else /* !defined(SO_OOBINLINE) */
+ c = recv(net, (char *)netiring.supply, canread, 0);
+#endif /* !defined(SO_OOBINLINE) */
+ if (c < 0 && errno == EWOULDBLOCK) {
+ c = 0;
+ } else if (c <= 0) {
+ return -1;
+ }
+ if (netdata) {
+ Dump('<', netiring.supply, c);
+ }
+ if (c)
+ ring_supplied(&netiring, c);
+ returnValue = 1;
+ }
+
+ /*
+ * Something to read from the tty...
+ */
+ if (FD_ISSET(tin, &ibits)) {
+ FD_CLR(tin, &ibits);
+ c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring));
+ if (c < 0 && errno == EWOULDBLOCK) {
+ c = 0;
+ } else {
+ /* EOF detection for line mode!!!! */
+ if ((c == 0) && MODE_LOCAL_CHARS(globalmode) && isatty(tin)) {
+ /* must be an EOF... */
+ *ttyiring.supply = termEofChar;
+ c = 1;
+ }
+ if (c <= 0) {
+ return -1;
+ }
+ if (termdata) {
+ Dump('<', ttyiring.supply, c);
+ }
+ ring_supplied(&ttyiring, c);
+ }
+ returnValue = 1; /* did something useful */
+ }
+
+ if (FD_ISSET(net, &obits)) {
+ FD_CLR(net, &obits);
+ returnValue |= netflush();
+ }
+ if (FD_ISSET(tout, &obits)) {
+ FD_CLR(tout, &obits);
+ returnValue |= (ttyflush(SYNCHing|flushout) > 0);
+ }
+
+ return returnValue;
+}
diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1
new file mode 100644
index 0000000..27079d9
--- /dev/null
+++ b/usr.bin/telnet/telnet.1
@@ -0,0 +1,1360 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)telnet.1 8.5 (Berkeley) 3/1/94
+.\"
+.Dd March 1, 1994
+.Dt TELNET 1
+.Os BSD 4.2
+.Sh NAME
+.Nm telnet
+.Nd user interface to the
+.Tn TELNET
+protocol
+.Sh SYNOPSIS
+.Nm telnet
+.Op Fl 8EFKLacdfrx
+.Op Fl S Ar tos
+.Op Fl X Ar authtype
+.Op Fl e Ar escapechar
+.Op Fl k Ar realm
+.Op Fl l Ar user
+.Op Fl n Ar tracefile
+.Oo
+.Ar host
+.Op port
+.Oc
+.Sh DESCRIPTION
+The
+.Nm telnet
+command
+is used to communicate with another host using the
+.Tn TELNET
+protocol.
+If
+.Nm telnet
+is invoked without the
+.Ar host
+argument, it enters command mode,
+indicated by its prompt
+.Pq Nm telnet\&> .
+In this mode, it accepts and executes the commands listed below.
+If it is invoked with arguments, it performs an
+.Ic open
+command with those arguments.
+.Pp
+Options:
+.Bl -tag -width indent
+.It Fl 8
+Specifies an 8-bit data path. This causes an attempt to
+negotiate the
+.Dv TELNET BINARY
+option on both input and output.
+.It Fl E
+Stops any character from being recognized as an escape character.
+.It Fl F
+If Kerberos V5 authentication is being used, the
+.Fl F
+option allows the local credentials to be forwarded
+to the remote system, including any credentials that
+have already been forwarded into the local environment.
+.It Fl K
+Specifies no automatic login to the remote system.
+.It Fl L
+Specifies an 8-bit data path on output. This causes the
+BINARY option to be negotiated on output.
+.It Fl S Ar tos
+Sets the IP type-of-service (TOS) option for the telnet
+connection to the value
+.Ar tos,
+which can be a numeric TOS value
+or, on systems that support it, a symbolic
+TOS name found in the /etc/iptos file.
+.It Fl X Ar atype
+Disables the
+.Ar atype
+type of authentication.
+.It Fl a
+Attempt automatic login.
+Currently, this sends the user name via the
+.Ev USER
+variable
+of the
+.Ev ENVIRON
+option if supported by the remote system.
+The name used is that of the current user as returned by
+.Xr getlogin 2
+if it agrees with the current user ID,
+otherwise it is the name associated with the user ID.
+.It Fl c
+Disables the reading of the user's
+.Pa \&.telnetrc
+file. (See the
+.Ic toggle skiprc
+command on this man page.)
+.It Fl d
+Sets the initial value of the
+.Ic debug
+toggle to
+.Dv TRUE
+.It Fl e Ar escape char
+Sets the initial
+.Nm
+.Nm telnet
+escape character to
+.Ar escape char.
+If
+.Ar escape char
+is omitted, then
+there will be no escape character.
+.It Fl f
+If Kerberos V5 authentication is being used, the
+.Fl f
+option allows the local credentials to be forwarded to the remote system.
+.It Fl k Ar realm
+If Kerberos authentication is being used, the
+.Fl k
+option requests that telnet obtain tickets for the remote host in
+realm realm instead of the remote host's realm, as determined
+by
+.Xr krb_realmofhost 3 .
+.It Fl l Ar user
+When connecting to the remote system, if the remote system
+understands the
+.Ev ENVIRON
+option, then
+.Ar user
+will be sent to the remote system as the value for the variable USER.
+This option implies the
+.Fl a
+option.
+This option may also be used with the
+.Ic open
+command.
+.It Fl n Ar tracefile
+Opens
+.Ar tracefile
+for recording trace information.
+See the
+.Ic set tracefile
+command below.
+.It Fl r
+Specifies a user interface similar to
+.Xr rlogin 1 .
+In this
+mode, the escape character is set to the tilde (~) character,
+unless modified by the -e option.
+.It Fl x
+Turns on encryption of the data stream if possible. This
+option is not available outside of the United States and
+Canada.
+.It Ar host
+Indicates the official name, an alias, or the Internet address
+of a remote host.
+.It Ar port
+Indicates a port number (address of an application). If a number is
+not specified, the default
+.Nm telnet
+port is used.
+.El
+.Pp
+When in rlogin mode, a line of the form ~. disconnects from the
+remote host; ~ is the telnet escape character.
+Similarly, the line ~^Z suspends the telnet session.
+The line ~^] escapes to the normal telnet escape prompt.
+.Pp
+Once a connection has been opened,
+.Nm telnet
+will attempt to enable the
+.Dv TELNET LINEMODE
+option.
+If this fails, then
+.Nm telnet
+will revert to one of two input modes:
+either \*(Lqcharacter at a time\*(Rq
+or \*(Lqold line by line\*(Rq
+depending on what the remote system supports.
+.Pp
+When
+.Dv LINEMODE
+is enabled, character processing is done on the
+local system, under the control of the remote system. When input
+editing or character echoing is to be disabled, the remote system
+will relay that information. The remote system will also relay
+changes to any special characters that happen on the remote
+system, so that they can take effect on the local system.
+.Pp
+In \*(Lqcharacter at a time\*(Rq mode, most
+text typed is immediately sent to the remote host for processing.
+.Pp
+In \*(Lqold line by line\*(Rq mode, all text is echoed locally,
+and (normally) only completed lines are sent to the remote host.
+The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used
+to turn off and on the local echo
+(this would mostly be used to enter passwords
+without the password being echoed).
+.Pp
+If the
+.Dv LINEMODE
+option is enabled, or if the
+.Ic localchars
+toggle is
+.Dv TRUE
+(the default for \*(Lqold line by line\*(Lq; see below),
+the user's
+.Ic quit ,
+.Ic intr ,
+and
+.Ic flush
+characters are trapped locally, and sent as
+.Tn TELNET
+protocol sequences to the remote side.
+If
+.Dv LINEMODE
+has ever been enabled, then the user's
+.Ic susp
+and
+.Ic eof
+are also sent as
+.Tn TELNET
+protocol sequences,
+and
+.Ic quit
+is sent as a
+.Dv TELNET ABORT
+instead of
+.Dv BREAK
+There are options (see
+.Ic toggle
+.Ic autoflush
+and
+.Ic toggle
+.Ic autosynch
+below)
+which cause this action to flush subsequent output to the terminal
+(until the remote host acknowledges the
+.Tn TELNET
+sequence) and flush previous terminal input
+(in the case of
+.Ic quit
+and
+.Ic intr ) .
+.Pp
+While connected to a remote host,
+.Nm telnet
+command mode may be entered by typing the
+.Nm telnet
+\*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq).
+When in command mode, the normal terminal editing conventions are available.
+.Pp
+The following
+.Nm telnet
+commands are available.
+Only enough of each command to uniquely identify it need be typed
+(this is also true for arguments to the
+.Ic mode ,
+.Ic set ,
+.Ic toggle ,
+.Ic unset ,
+.Ic slc ,
+.Ic environ ,
+and
+.Ic display
+commands).
+.Pp
+.Bl -tag -width "mode type"
+.It Ic auth Ar argument ...
+The auth command manipulates the information sent through the
+.Dv TELNET AUTHENTICATE
+option. Valid arguments for the
+auth command are as follows:
+.Bl -tag -width "disable type"
+.It Ic disable Ar type
+Disables the specified type of authentication. To
+obtain a list of available types, use the
+.Ic auth disable \&?
+command.
+.It Ic enable Ar type
+Enables the specified type of authentication. To
+obtain a list of available types, use the
+.Ic auth enable \&?
+command.
+.It Ic status
+Lists the current status of the various types of
+authentication.
+.El
+.It Ic close
+Close a
+.Tn TELNET
+session and return to command mode.
+.It Ic display Ar argument ...
+Displays all, or some, of the
+.Ic set
+and
+.Ic toggle
+values (see below).
+.It Ic encrypt Ar argument ...
+The encrypt command manipulates the information sent through the
+.Dv TELNET ENCRYPT
+option.
+.Pp
+Note: Because of export controls, the
+.Dv TELNET ENCRYPT
+option is not supported outside of the United States and Canada.
+.Pp
+Valid arguments for the encrypt command are as follows:
+.Bl -tag -width Ar
+.It Ic disable Ar type Ic [input|output]
+Disables the specified type of encryption. If you
+omit the input and output, both input and output
+are disabled. To obtain a list of available
+types, use the
+.Ic encrypt disable \&?
+command.
+.It Ic enable Ar type Ic [input|output]
+Enables the specified type of encryption. If you
+omit input and output, both input and output are
+enabled. To obtain a list of available types, use the
+.Ic encrypt enable \&?
+command.
+.It Ic input
+This is the same as the
+.Ic encrypt start input
+command.
+.It Ic -input
+This is the same as the
+.Ic encrypt stop input
+command.
+.It Ic output
+This is the same as the
+.Ic encrypt start output
+command.
+.It Ic -output
+This is the same as the
+.Ic encrypt stop output
+command.
+.It Ic start Ic [input|output]
+Attempts to start encryption. If you omit
+.Ic input
+and
+.Ic output,
+both input and output are enabled. To
+obtain a list of available types, use the
+.Ic encrypt enable \&?
+command.
+.It Ic status
+Lists the current status of encryption.
+.It Ic stop Ic [input|output]
+Stops encryption. If you omit input and output,
+encryption is on both input and output.
+.It Ic type Ar type
+Sets the default type of encryption to be used
+with later
+.Ic encrypt start
+or
+.Ic encrypt stop
+commands.
+.El
+.It Ic environ Ar arguments...
+The
+.Ic environ
+command is used to manipulate the
+the variables that my be sent through the
+.Dv TELNET ENVIRON
+option.
+The initial set of variables is taken from the users
+environment, with only the
+.Ev DISPLAY
+and
+.Ev PRINTER
+variables being exported by default.
+The
+.Ev USER
+variable is also exported if the
+.Fl a
+or
+.Fl l
+options are used.
+.br
+Valid arguments for the
+.Ic environ
+command are:
+.Bl -tag -width Fl
+.It Ic define Ar variable value
+Define the variable
+.Ar variable
+to have a value of
+.Ar value.
+Any variables defined by this command are automatically exported.
+The
+.Ar value
+may be enclosed in single or double quotes so
+that tabs and spaces may be included.
+.It Ic undefine Ar variable
+Remove
+.Ar variable
+from the list of environment variables.
+.It Ic export Ar variable
+Mark the variable
+.Ar variable
+to be exported to the remote side.
+.It Ic unexport Ar variable
+Mark the variable
+.Ar variable
+to not be exported unless
+explicitly asked for by the remote side.
+.It Ic list
+List the current set of environment variables.
+Those marked with a
+.Cm *
+will be sent automatically,
+other variables will only be sent if explicitly requested.
+.It Ic \&?
+Prints out help information for the
+.Ic environ
+command.
+.El
+.It Ic logout
+Sends the
+.Dv TELNET LOGOUT
+option to the remote side.
+This command is similar to a
+.Ic close
+command; however, if the remote side does not support the
+.Dv LOGOUT
+option, nothing happens.
+If, however, the remote side does support the
+.Dv LOGOUT
+option, this command should cause the remote side to close the
+.Tn TELNET
+connection.
+If the remote side also supports the concept of
+suspending a user's session for later reattachment,
+the logout argument indicates that you
+should terminate the session immediately.
+.It Ic mode Ar type
+.Ar Type
+is one of several options, depending on the state of the
+.Tn TELNET
+session.
+The remote host is asked for permission to go into the requested mode.
+If the remote host is capable of entering that mode, the requested
+mode will be entered.
+.Bl -tag -width Ar
+.It Ic character
+Disable the
+.Dv TELNET LINEMODE
+option, or, if the remote side does not understand the
+.Dv LINEMODE
+option, then enter \*(Lqcharacter at a time\*(Lq mode.
+.It Ic line
+Enable the
+.Dv TELNET LINEMODE
+option, or, if the remote side does not understand the
+.Dv LINEMODE
+option, then attempt to enter \*(Lqold-line-by-line\*(Lq mode.
+.It Ic isig Pq Ic \-isig
+Attempt to enable (disable) the
+.Dv TRAPSIG
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic edit Pq Ic \-edit
+Attempt to enable (disable) the
+.Dv EDIT
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic softtabs Pq Ic \-softtabs
+Attempt to enable (disable) the
+.Dv SOFT_TAB
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic litecho Pq Ic \-litecho
+Attempt to enable (disable) the
+.Dv LIT_ECHO
+mode of the
+.Dv LINEMODE
+option.
+This requires that the
+.Dv LINEMODE
+option be enabled.
+.It Ic \&?
+Prints out help information for the
+.Ic mode
+command.
+.El
+.It Xo
+.Ic open Ar host
+.Oo Op Fl l
+.Ar user
+.Oc Ns Oo Fl
+.Ar port Oc
+.Xc
+Open a connection to the named host.
+If no port number
+is specified,
+.Nm telnet
+will attempt to contact a
+.Tn TELNET
+server at the default port.
+The host specification may be either a host name (see
+.Xr hosts 5 )
+or an Internet address specified in the \*(Lqdot notation\*(Rq (see
+.Xr inet 3 ) .
+The
+.Op Fl l
+option may be used to specify the user name
+to be passed to the remote system via the
+.Ev ENVIRON
+option.
+When connecting to a non-standard port,
+.Nm telnet
+omits any automatic initiation of
+.Tn TELNET
+options. When the port number is preceded by a minus sign,
+the initial option negotiation is done.
+After establishing a connection, the file
+.Pa \&.telnetrc
+in the
+users home directory is opened. Lines beginning with a # are
+comment lines. Blank lines are ignored. Lines that begin
+without white space are the start of a machine entry. The
+first thing on the line is the name of the machine that is
+being connected to. The rest of the line, and successive
+lines that begin with white space are assumed to be
+.Nm telnet
+commands and are processed as if they had been typed
+in manually to the
+.Nm telnet
+command prompt.
+.It Ic quit
+Close any open
+.Tn TELNET
+session and exit
+.Nm telnet .
+An end of file (in command mode) will also close a session and exit.
+.It Ic send Ar arguments
+Sends one or more special character sequences to the remote host.
+The following are the arguments which may be specified
+(more than one argument may be specified at a time):
+.Pp
+.Bl -tag -width escape
+.It Ic abort
+Sends the
+.Dv TELNET ABORT
+(Abort
+processes)
+sequence.
+.It Ic ao
+Sends the
+.Dv TELNET AO
+(Abort Output) sequence, which should cause the remote system to flush
+all output
+.Em from
+the remote system
+.Em to
+the user's terminal.
+.It Ic ayt
+Sends the
+.Dv TELNET AYT
+(Are You There)
+sequence, to which the remote system may or may not choose to respond.
+.It Ic brk
+Sends the
+.Dv TELNET BRK
+(Break) sequence, which may have significance to the remote
+system.
+.It Ic ec
+Sends the
+.Dv TELNET EC
+(Erase Character)
+sequence, which should cause the remote system to erase the last character
+entered.
+.It Ic el
+Sends the
+.Dv TELNET EL
+(Erase Line)
+sequence, which should cause the remote system to erase the line currently
+being entered.
+.It Ic eof
+Sends the
+.Dv TELNET EOF
+(End Of File)
+sequence.
+.It Ic eor
+Sends the
+.Dv TELNET EOR
+(End of Record)
+sequence.
+.It Ic escape
+Sends the current
+.Nm telnet
+escape character (initially \*(Lq^\*(Rq).
+.It Ic ga
+Sends the
+.Dv TELNET GA
+(Go Ahead)
+sequence, which likely has no significance to the remote system.
+.It Ic getstatus
+If the remote side supports the
+.Dv TELNET STATUS
+command,
+.Ic getstatus
+will send the subnegotiation to request that the server send
+its current option status.
+.It Ic ip
+Sends the
+.Dv TELNET IP
+(Interrupt Process) sequence, which should cause the remote
+system to abort the currently running process.
+.It Ic nop
+Sends the
+.Dv TELNET NOP
+(No OPeration)
+sequence.
+.It Ic susp
+Sends the
+.Dv TELNET SUSP
+(SUSPend process)
+sequence.
+.It Ic synch
+Sends the
+.Dv TELNET SYNCH
+sequence.
+This sequence causes the remote system to discard all previously typed
+(but not yet read) input.
+This sequence is sent as
+.Tn TCP
+urgent
+data (and may not work if the remote system is a
+.Bx 4.2
+system -- if
+it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal).
+.It Ic do Ar cmd
+.It Ic dont Ar cmd
+.It Ic will Ar cmd
+.It Ic wont Ar cmd
+Sends the
+.Dv TELNET DO
+.Ar cmd
+sequence.
+.Ar Cmd
+can be either a decimal number between 0 and 255,
+or a symbolic name for a specific
+.Dv TELNET
+command.
+.Ar Cmd
+can also be either
+.Ic help
+or
+.Ic \&?
+to print out help information, including
+a list of known symbolic names.
+.It Ic \&?
+Prints out help information for the
+.Ic send
+command.
+.El
+.It Ic set Ar argument value
+.It Ic unset Ar argument value
+The
+.Ic set
+command will set any one of a number of
+.Nm telnet
+variables to a specific value or to
+.Dv TRUE .
+The special value
+.Ic off
+turns off the function associated with
+the variable, this is equivalent to using the
+.Ic unset
+command.
+The
+.Ic unset
+command will disable or set to
+.Dv FALSE
+any of the specified functions.
+The values of variables may be interrogated with the
+.Ic display
+command.
+The variables which may be set or unset, but not toggled, are
+listed here. In addition, any of the variables for the
+.Ic toggle
+command may be explicitly set or unset using
+the
+.Ic set
+and
+.Ic unset
+commands.
+.Bl -tag -width escape
+.It Ic ayt
+If
+.Tn TELNET
+is in localchars mode, or
+.Dv LINEMODE
+is enabled, and the status character is typed, a
+.Dv TELNET AYT
+sequence (see
+.Ic send ayt
+preceding) is sent to the
+remote host. The initial value for the "Are You There"
+character is the terminal's status character.
+.It Ic echo
+This is the value (initially \*(Lq^E\*(Rq) which, when in
+\*(Lqline by line\*(Rq mode, toggles between doing local echoing
+of entered characters (for normal processing), and suppressing
+echoing of entered characters (for entering, say, a password).
+.It Ic eof
+If
+.Nm telnet
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Rq mode, entering this character
+as the first character on a line will cause this character to be
+sent to the remote system.
+The initial value of the eof character is taken to be the terminal's
+.Ic eof
+character.
+.It Ic erase
+If
+.Nm telnet
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below),
+.Sy and
+if
+.Nm telnet
+is operating in \*(Lqcharacter at a time\*(Rq mode, then when this
+character is typed, a
+.Dv TELNET EC
+sequence (see
+.Ic send
+.Ic ec
+above)
+is sent to the remote system.
+The initial value for the erase character is taken to be
+the terminal's
+.Ic erase
+character.
+.It Ic escape
+This is the
+.Nm telnet
+escape character (initially \*(Lq^[\*(Rq) which causes entry
+into
+.Nm telnet
+command mode (when connected to a remote system).
+.It Ic flushoutput
+If
+.Nm telnet
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic flushoutput
+character is typed, a
+.Dv TELNET AO
+sequence (see
+.Ic send
+.Ic ao
+above)
+is sent to the remote host.
+The initial value for the flush character is taken to be
+the terminal's
+.Ic flush
+character.
+.It Ic forw1
+.It Ic forw2
+If
+.Tn TELNET
+is operating in
+.Dv LINEMODE ,
+these are the
+characters that, when typed, cause partial lines to be
+forwarded to the remote system. The initial value for
+the forwarding characters are taken from the terminal's
+eol and eol2 characters.
+.It Ic interrupt
+If
+.Nm telnet
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic interrupt
+character is typed, a
+.Dv TELNET IP
+sequence (see
+.Ic send
+.Ic ip
+above)
+is sent to the remote host.
+The initial value for the interrupt character is taken to be
+the terminal's
+.Ic intr
+character.
+.It Ic kill
+If
+.Nm telnet
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below),
+.Ic and
+if
+.Nm telnet
+is operating in \*(Lqcharacter at a time\*(Rq mode, then when this
+character is typed, a
+.Dv TELNET EL
+sequence (see
+.Ic send
+.Ic el
+above)
+is sent to the remote system.
+The initial value for the kill character is taken to be
+the terminal's
+.Ic kill
+character.
+.It Ic lnext
+If
+.Nm telnet
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic lnext
+character.
+The initial value for the lnext character is taken to be
+the terminal's
+.Ic lnext
+character.
+.It Ic quit
+If
+.Nm telnet
+is in
+.Ic localchars
+mode (see
+.Ic toggle
+.Ic localchars
+below)
+and the
+.Ic quit
+character is typed, a
+.Dv TELNET BRK
+sequence (see
+.Ic send
+.Ic brk
+above)
+is sent to the remote host.
+The initial value for the quit character is taken to be
+the terminal's
+.Ic quit
+character.
+.It Ic reprint
+If
+.Nm telnet
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic reprint
+character.
+The initial value for the reprint character is taken to be
+the terminal's
+.Ic reprint
+character.
+.It Ic rlogin
+This is the rlogin escape character.
+If set, the normal
+.Tn TELNET
+escape character is ignored unless it is
+preceded by this character at the beginning of a line.
+This character, at the beginning of a line followed by
+a "." closes the connection; when followed by a ^Z it
+suspends the telnet command. The initial state is to
+disable the rlogin escape character.
+.It Ic start
+If the
+.Dv TELNET TOGGLE-FLOW-CONTROL
+option has been enabled,
+then this character is taken to
+be the terminal's
+.Ic start
+character.
+The initial value for the kill character is taken to be
+the terminal's
+.Ic start
+character.
+.It Ic stop
+If the
+.Dv TELNET TOGGLE-FLOW-CONTROL
+option has been enabled,
+then this character is taken to
+be the terminal's
+.Ic stop
+character.
+The initial value for the kill character is taken to be
+the terminal's
+.Ic stop
+character.
+.It Ic susp
+If
+.Nm telnet
+is in
+.Ic localchars
+mode, or
+.Dv LINEMODE
+is enabled, and the
+.Ic suspend
+character is typed, a
+.Dv TELNET SUSP
+sequence (see
+.Ic send
+.Ic susp
+above)
+is sent to the remote host.
+The initial value for the suspend character is taken to be
+the terminal's
+.Ic suspend
+character.
+.It Ic tracefile
+This is the file to which the output, caused by
+.Ic netdata
+or
+.Ic option
+tracing being
+.Dv TRUE ,
+will be written. If it is set to
+.Dq Fl ,
+then tracing information will be written to standard output (the default).
+.It Ic worderase
+If
+.Nm telnet
+is operating in
+.Dv LINEMODE
+or \*(Lqold line by line\*(Lq mode, then this character is taken to
+be the terminal's
+.Ic worderase
+character.
+The initial value for the worderase character is taken to be
+the terminal's
+.Ic worderase
+character.
+.It Ic \&?
+Displays the legal
+.Ic set
+.Pq Ic unset
+commands.
+.El
+.It Ic slc Ar state
+The
+.Ic slc
+command (Set Local Characters) is used to set
+or change the state of the the special
+characters when the
+.Dv TELNET LINEMODE
+option has
+been enabled. Special characters are characters that get
+mapped to
+.Tn TELNET
+commands sequences (like
+.Ic ip
+or
+.Ic quit )
+or line editing characters (like
+.Ic erase
+and
+.Ic kill ) .
+By default, the local special characters are exported.
+.Bl -tag -width Fl
+.It Ic check
+Verify the current settings for the current special characters.
+The remote side is requested to send all the current special
+character settings, and if there are any discrepancies with
+the local side, the local side will switch to the remote value.
+.It Ic export
+Switch to the local defaults for the special characters. The
+local default characters are those of the local terminal at
+the time when
+.Nm telnet
+was started.
+.It Ic import
+Switch to the remote defaults for the special characters.
+The remote default characters are those of the remote system
+at the time when the
+.Tn TELNET
+connection was established.
+.It Ic \&?
+Prints out help information for the
+.Ic slc
+command.
+.El
+.It Ic status
+Show the current status of
+.Nm telnet .
+This includes the peer one is connected to, as well
+as the current mode.
+.It Ic toggle Ar arguments ...
+Toggle (between
+.Dv TRUE
+and
+.Dv FALSE )
+various flags that control how
+.Nm telnet
+responds to events.
+These flags may be set explicitly to
+.Dv TRUE
+or
+.Dv FALSE
+using the
+.Ic set
+and
+.Ic unset
+commands listed above.
+More than one argument may be specified.
+The state of these flags may be interrogated with the
+.Ic display
+command.
+Valid arguments are:
+.Bl -tag -width Ar
+.It Ic authdebug
+Turns on debugging information for the authentication code.
+.It Ic autoflush
+If
+.Ic autoflush
+and
+.Ic localchars
+are both
+.Dv TRUE ,
+then when the
+.Ic ao ,
+or
+.Ic quit
+characters are recognized (and transformed into
+.Tn TELNET
+sequences; see
+.Ic set
+above for details),
+.Nm telnet
+refuses to display any data on the user's terminal
+until the remote system acknowledges (via a
+.Dv TELNET TIMING MARK
+option)
+that it has processed those
+.Tn TELNET
+sequences.
+The initial value for this toggle is
+.Dv TRUE
+if the terminal user had not
+done an "stty noflsh", otherwise
+.Dv FALSE
+(see
+.Xr stty 1 ) .
+.It Ic autodecrypt
+When the
+.Dv TELNET ENCRYPT
+option is negotiated, by
+default the actual encryption (decryption) of the data
+stream does not start automatically. The autoencrypt
+(autodecrypt) command states that encryption of the
+output (input) stream should be enabled as soon as
+possible.
+.Pp
+Note: Because of export controls, the
+.Dv TELNET ENCRYPT
+option is not supported outside the United States and Canada.
+.It Ic autologin
+If the remote side supports the
+.Dv TELNET AUTHENTICATION
+option
+.Tn TELNET
+attempts to use it to perform automatic authentication. If the
+.Dv AUTHENTICATION
+option is not supported, the user's login
+name are propagated through the
+.Dv TELNET ENVIRON
+option.
+This command is the same as specifying
+.Ar a
+option on the
+.Ic open
+command.
+.It Ic autosynch
+If
+.Ic autosynch
+and
+.Ic localchars
+are both
+.Dv TRUE ,
+then when either the
+.Ic intr
+or
+.Ic quit
+characters is typed (see
+.Ic set
+above for descriptions of the
+.Ic intr
+and
+.Ic quit
+characters), the resulting
+.Tn TELNET
+sequence sent is followed by the
+.Dv TELNET SYNCH
+sequence.
+This procedure
+.Ic should
+cause the remote system to begin throwing away all previously
+typed input until both of the
+.Tn TELNET
+sequences have been read and acted upon.
+The initial value of this toggle is
+.Dv FALSE .
+.It Ic binary
+Enable or disable the
+.Dv TELNET BINARY
+option on both input and output.
+.It Ic inbinary
+Enable or disable the
+.Dv TELNET BINARY
+option on input.
+.It Ic outbinary
+Enable or disable the
+.Dv TELNET BINARY
+option on output.
+.It Ic crlf
+If this is
+.Dv TRUE ,
+then carriage returns will be sent as
+.Li <CR><LF> .
+If this is
+.Dv FALSE ,
+then carriage returns will be send as
+.Li <CR><NUL> .
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic crmod
+Toggle carriage return mode.
+When this mode is enabled, most carriage return characters received from
+the remote host will be mapped into a carriage return followed by
+a line feed.
+This mode does not affect those characters typed by the user, only
+those received from the remote host.
+This mode is not very useful unless the remote host
+only sends carriage return, but never line feed.
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic debug
+Toggles socket level debugging (useful only to the
+.Ic super user ) .
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic encdebug
+Turns on debugging information for the encryption code.
+.It Ic localchars
+If this is
+.Dv TRUE ,
+then the
+.Ic flush ,
+.Ic interrupt ,
+.Ic quit ,
+.Ic erase ,
+and
+.Ic kill
+characters (see
+.Ic set
+above) are recognized locally, and transformed into (hopefully) appropriate
+.Tn TELNET
+control sequences
+(respectively
+.Ic ao ,
+.Ic ip ,
+.Ic brk ,
+.Ic ec ,
+and
+.Ic el ;
+see
+.Ic send
+above).
+The initial value for this toggle is
+.Dv TRUE
+in \*(Lqold line by line\*(Rq mode,
+and
+.Dv FALSE
+in \*(Lqcharacter at a time\*(Rq mode.
+When the
+.Dv LINEMODE
+option is enabled, the value of
+.Ic localchars
+is ignored, and assumed to always be
+.Dv TRUE .
+If
+.Dv LINEMODE
+has ever been enabled, then
+.Ic quit
+is sent as
+.Ic abort ,
+and
+.Ic eof and
+.B suspend
+are sent as
+.Ic eof and
+.Ic susp ,
+see
+.Ic send
+above).
+.It Ic netdata
+Toggles the display of all network data (in hexadecimal format).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic options
+Toggles the display of some internal
+.Nm telnet
+protocol processing (having to do with
+.Tn TELNET
+options).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic prettydump
+When the
+.Ic netdata
+toggle is enabled, if
+.Ic prettydump
+is enabled the output from the
+.Ic netdata
+command will be formatted in a more user readable format.
+Spaces are put between each character in the output, and the
+beginning of any
+.Tn TELNET
+escape sequence is preceded by a '*' to aid in locating them.
+.It Ic skiprc
+When the skiprc toggle is
+.Dv TRUE ,
+.Tn TELNET
+skips the reading of the
+.Pa \&.telnetrc
+file in the users home
+directory when connections are opened. The initial
+value for this toggle is
+.Dv FALSE.
+.It Ic termdata
+Toggles the display of all terminal data (in hexadecimal format).
+The initial value for this toggle is
+.Dv FALSE .
+.It Ic verbose_encrypt
+When the
+.Ic verbose_encrypt
+toggle is
+.Dv TRUE ,
+.Tn TELNET
+prints out a message each time encryption is enabled or
+disabled. The initial value for this toggle is
+.Dv FALSE.
+Note: Because of export controls, data encryption
+is not supported outside of the United States and Canada.
+.It Ic \&?
+Displays the legal
+.Ic toggle
+commands.
+.El
+.It Ic z
+Suspend
+.Nm telnet .
+This command only works when the user is using the
+.Xr csh 1 .
+.It Ic \&! Op Ar command
+Execute a single command in a subshell on the local
+system. If
+.Ic command
+is omitted, then an interactive
+subshell is invoked.
+.It Ic \&? Op Ar command
+Get help. With no arguments,
+.Nm telnet
+prints a help summary.
+If a command is specified,
+.Nm telnet
+will print the help information for just that command.
+.El
+.Sh ENVIRONMENT
+.Nm Telnet
+uses at least the
+.Ev HOME ,
+.Ev SHELL ,
+.Ev DISPLAY ,
+and
+.Ev TERM
+environment variables.
+Other environment variables may be propagated
+to the other side via the
+.Dv TELNET ENVIRON
+option.
+.Sh FILES
+.Bl -tag -width ~/.telnetrc -compact
+.It Pa ~/.telnetrc
+user customized telnet startup values
+.El
+.Sh HISTORY
+The
+.Nm Telnet
+command appeared in
+.Bx 4.2 .
+.Sh NOTES
+.Pp
+On some remote systems, echo has to be turned off manually when in
+\*(Lqold line by line\*(Rq mode.
+.Pp
+In \*(Lqold line by line\*(Rq mode or
+.Dv LINEMODE
+the terminal's
+.Ic eof
+character is only recognized (and sent to the remote system)
+when it is the first character on a line.
diff --git a/usr.bin/telnet/telnet.c b/usr.bin/telnet/telnet.c
new file mode 100644
index 0000000..ea0ac92
--- /dev/null
+++ b/usr.bin/telnet/telnet.c
@@ -0,0 +1,2559 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)telnet.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#if defined(unix)
+#include <signal.h>
+/* By the way, we need to include curses.h before telnet.h since,
+ * among other things, telnet.h #defines 'DO', which is a variable
+ * declared in curses.h.
+ */
+#endif /* defined(unix) */
+
+#include <arpa/telnet.h>
+
+#include <ctype.h>
+
+#include "ring.h"
+
+#include "defines.h"
+#include "externs.h"
+#include "types.h"
+#include "general.h"
+
+
+#define strip(x) ((x)&0x7f)
+
+static unsigned char subbuffer[SUBBUFSIZE],
+ *subpointer, *subend; /* buffer for sub-options */
+#define SB_CLEAR() subpointer = subbuffer;
+#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
+#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
+ *subpointer++ = (c); \
+ }
+
+#define SB_GET() ((*subpointer++)&0xff)
+#define SB_PEEK() ((*subpointer)&0xff)
+#define SB_EOF() (subpointer >= subend)
+#define SB_LEN() (subend - subpointer)
+
+char options[256]; /* The combined options */
+char do_dont_resp[256];
+char will_wont_resp[256];
+
+int
+ eight = 0,
+ autologin = 0, /* Autologin anyone? */
+ skiprc = 0,
+ connected,
+ showoptions,
+ In3270, /* Are we in 3270 mode? */
+ ISend, /* trying to send network data in */
+ debug = 0,
+ crmod,
+ netdata, /* Print out network data flow */
+ crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
+#if defined(TN3270)
+ noasynchtty = 0,/* User specified "-noasynch" on command line */
+ noasynchnet = 0,/* User specified "-noasynch" on command line */
+ askedSGA = 0, /* We have talked about suppress go ahead */
+#endif /* defined(TN3270) */
+ telnetport,
+ SYNCHing, /* we are in TELNET SYNCH mode */
+ flushout, /* flush output */
+ autoflush = 0, /* flush output when interrupting? */
+ autosynch, /* send interrupt characters with SYNCH? */
+ localflow, /* we handle flow control locally */
+ restartany, /* if flow control enabled, restart on any character */
+ localchars, /* we recognize interrupt/quit */
+ donelclchars, /* the user has set "localchars" */
+ donebinarytoggle, /* the user has put us in binary */
+ dontlecho, /* do we suppress local echoing right now? */
+ globalmode;
+
+char *prompt = 0;
+
+cc_t escape;
+cc_t rlogin;
+#ifdef KLUDGELINEMODE
+cc_t echoc;
+#endif
+
+/*
+ * Telnet receiver states for fsm
+ */
+#define TS_DATA 0
+#define TS_IAC 1
+#define TS_WILL 2
+#define TS_WONT 3
+#define TS_DO 4
+#define TS_DONT 5
+#define TS_CR 6
+#define TS_SB 7 /* sub-option collection */
+#define TS_SE 8 /* looking for sub-option end */
+
+static int telrcv_state;
+#ifdef OLD_ENVIRON
+unsigned char telopt_environ = TELOPT_NEW_ENVIRON;
+#else
+# define telopt_environ TELOPT_NEW_ENVIRON
+#endif
+
+jmp_buf toplevel = { 0 };
+jmp_buf peerdied;
+
+int flushline;
+int linemode;
+
+#ifdef KLUDGELINEMODE
+int kludgelinemode = 1;
+#endif
+
+/*
+ * The following are some clocks used to decide how to interpret
+ * the relationship between various variables.
+ */
+
+Clocks clocks;
+
+#ifdef notdef
+Modelist modelist[] = {
+ { "telnet command mode", COMMAND_LINE },
+ { "character-at-a-time mode", 0 },
+ { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
+ { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
+ { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
+ { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
+ { "3270 mode", 0 },
+};
+#endif
+
+
+/*
+ * Initialize telnet environment.
+ */
+
+ void
+init_telnet()
+{
+ env_init();
+
+ SB_CLEAR();
+ ClearArray(options);
+
+ connected = In3270 = ISend = localflow = donebinarytoggle = 0;
+#if defined(AUTHENTICATION)
+ auth_encrypt_connect(connected);
+#endif /* defined(AUTHENTICATION) */
+ restartany = -1;
+
+ SYNCHing = 0;
+
+ /* Don't change NetTrace */
+
+ escape = CONTROL(']');
+ rlogin = _POSIX_VDISABLE;
+#ifdef KLUDGELINEMODE
+ echoc = CONTROL('E');
+#endif
+
+ flushline = 1;
+ telrcv_state = TS_DATA;
+}
+
+
+#ifdef notdef
+#include <varargs.h>
+
+ /*VARARGS*/
+ static void
+printring(va_alist)
+ va_dcl
+{
+ va_list ap;
+ char buffer[100]; /* where things go */
+ char *ptr;
+ char *format;
+ char *string;
+ Ring *ring;
+ int i;
+
+ va_start(ap);
+
+ ring = va_arg(ap, Ring *);
+ format = va_arg(ap, char *);
+ ptr = buffer;
+
+ while ((i = *format++) != 0) {
+ if (i == '%') {
+ i = *format++;
+ switch (i) {
+ case 'c':
+ *ptr++ = va_arg(ap, int);
+ break;
+ case 's':
+ string = va_arg(ap, char *);
+ ring_supply_data(ring, buffer, ptr-buffer);
+ ring_supply_data(ring, string, strlen(string));
+ ptr = buffer;
+ break;
+ case 0:
+ ExitString("printring: trailing %%.\n", 1);
+ /*NOTREACHED*/
+ default:
+ ExitString("printring: unknown format character.\n", 1);
+ /*NOTREACHED*/
+ }
+ } else {
+ *ptr++ = i;
+ }
+ }
+ ring_supply_data(ring, buffer, ptr-buffer);
+}
+#endif
+
+/*
+ * These routines are in charge of sending option negotiations
+ * to the other side.
+ *
+ * The basic idea is that we send the negotiation if either side
+ * is in disagreement as to what the current state should be.
+ */
+
+ void
+send_do(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
+ my_want_state_is_do(c))
+ return;
+ set_my_want_state_do(c);
+ do_dont_resp[c]++;
+ }
+ NET2ADD(IAC, DO);
+ NETADD(c);
+ printoption("SENT", DO, c);
+}
+
+ void
+send_dont(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
+ my_want_state_is_dont(c))
+ return;
+ set_my_want_state_dont(c);
+ do_dont_resp[c]++;
+ }
+ NET2ADD(IAC, DONT);
+ NETADD(c);
+ printoption("SENT", DONT, c);
+}
+
+ void
+send_will(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
+ my_want_state_is_will(c))
+ return;
+ set_my_want_state_will(c);
+ will_wont_resp[c]++;
+ }
+ NET2ADD(IAC, WILL);
+ NETADD(c);
+ printoption("SENT", WILL, c);
+}
+
+ void
+send_wont(c, init)
+ register int c, init;
+{
+ if (init) {
+ if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
+ my_want_state_is_wont(c))
+ return;
+ set_my_want_state_wont(c);
+ will_wont_resp[c]++;
+ }
+ NET2ADD(IAC, WONT);
+ NETADD(c);
+ printoption("SENT", WONT, c);
+}
+
+
+ void
+willoption(option)
+ int option;
+{
+ int new_state_ok = 0;
+
+ if (do_dont_resp[option]) {
+ --do_dont_resp[option];
+ if (do_dont_resp[option] && my_state_is_do(option))
+ --do_dont_resp[option];
+ }
+
+ if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
+
+ switch (option) {
+
+ case TELOPT_ECHO:
+# if defined(TN3270)
+ /*
+ * The following is a pain in the rear-end.
+ * Various IBM servers (some versions of Wiscnet,
+ * possibly Fibronics/Spartacus, and who knows who
+ * else) will NOT allow us to send "DO SGA" too early
+ * in the setup proceedings. On the other hand,
+ * 4.2 servers (telnetd) won't set SGA correctly.
+ * So, we are stuck. Empirically (but, based on
+ * a VERY small sample), the IBM servers don't send
+ * out anything about ECHO, so we postpone our sending
+ * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
+ * DO send).
+ */
+ {
+ if (askedSGA == 0) {
+ askedSGA = 1;
+ if (my_want_state_is_dont(TELOPT_SGA))
+ send_do(TELOPT_SGA, 1);
+ }
+ }
+ /* Fall through */
+ case TELOPT_EOR:
+#endif /* defined(TN3270) */
+ case TELOPT_BINARY:
+ case TELOPT_SGA:
+ settimer(modenegotiated);
+ /* FALL THROUGH */
+ case TELOPT_STATUS:
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+#endif
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_TM:
+ if (flushout)
+ flushout = 0;
+ /*
+ * Special case for TM. If we get back a WILL,
+ * pretend we got back a WONT.
+ */
+ set_my_want_state_dont(option);
+ set_my_state_dont(option);
+ return; /* Never reply to TM will's/wont's */
+
+ case TELOPT_LINEMODE:
+ default:
+ break;
+ }
+
+ if (new_state_ok) {
+ set_my_want_state_do(option);
+ send_do(option, 0);
+ setconnmode(0); /* possibly set new tty mode */
+ } else {
+ do_dont_resp[option]++;
+ send_dont(option, 0);
+ }
+ }
+ set_my_state_do(option);
+}
+
+ void
+wontoption(option)
+ int option;
+{
+ if (do_dont_resp[option]) {
+ --do_dont_resp[option];
+ if (do_dont_resp[option] && my_state_is_dont(option))
+ --do_dont_resp[option];
+ }
+
+ if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
+
+ switch (option) {
+
+#ifdef KLUDGELINEMODE
+ case TELOPT_SGA:
+ if (!kludgelinemode)
+ break;
+ /* FALL THROUGH */
+#endif
+ case TELOPT_ECHO:
+ settimer(modenegotiated);
+ break;
+
+ case TELOPT_TM:
+ if (flushout)
+ flushout = 0;
+ set_my_want_state_dont(option);
+ set_my_state_dont(option);
+ return; /* Never reply to TM will's/wont's */
+
+ default:
+ break;
+ }
+ set_my_want_state_dont(option);
+ if (my_state_is_do(option))
+ send_dont(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ } else if (option == TELOPT_TM) {
+ /*
+ * Special case for TM.
+ */
+ if (flushout)
+ flushout = 0;
+ set_my_want_state_dont(option);
+ }
+ set_my_state_dont(option);
+}
+
+ static void
+dooption(option)
+ int option;
+{
+ int new_state_ok = 0;
+
+ if (will_wont_resp[option]) {
+ --will_wont_resp[option];
+ if (will_wont_resp[option] && my_state_is_will(option))
+ --will_wont_resp[option];
+ }
+
+ if (will_wont_resp[option] == 0) {
+ if (my_want_state_is_wont(option)) {
+
+ switch (option) {
+
+ case TELOPT_TM:
+ /*
+ * Special case for TM. We send a WILL, but pretend
+ * we sent WONT.
+ */
+ send_will(option, 0);
+ set_my_want_state_wont(TELOPT_TM);
+ set_my_state_wont(TELOPT_TM);
+ return;
+
+# if defined(TN3270)
+ case TELOPT_EOR: /* end of record */
+# endif /* defined(TN3270) */
+ case TELOPT_BINARY: /* binary mode */
+ case TELOPT_NAWS: /* window size */
+ case TELOPT_TSPEED: /* terminal speed */
+ case TELOPT_LFLOW: /* local flow control */
+ case TELOPT_TTYPE: /* terminal type option */
+ case TELOPT_SGA: /* no big deal */
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_NEW_ENVIRON: /* New environment variable option */
+#ifdef OLD_ENVIRON
+ if (my_state_is_will(TELOPT_OLD_ENVIRON))
+ send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */
+ goto env_common;
+ case TELOPT_OLD_ENVIRON: /* Old environment variable option */
+ if (my_state_is_will(TELOPT_NEW_ENVIRON))
+ break; /* Don't enable if new one is in use! */
+ env_common:
+ telopt_environ = option;
+#endif
+ new_state_ok = 1;
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ if (autologin)
+ new_state_ok = 1;
+ break;
+#endif
+
+ case TELOPT_XDISPLOC: /* X Display location */
+ if (env_getvalue((unsigned char *)"DISPLAY"))
+ new_state_ok = 1;
+ break;
+
+ case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+ kludgelinemode = 0;
+ send_do(TELOPT_SGA, 1);
+#endif
+ set_my_want_state_will(TELOPT_LINEMODE);
+ send_will(option, 0);
+ set_my_state_will(TELOPT_LINEMODE);
+ slc_init();
+ return;
+
+ case TELOPT_ECHO: /* We're never going to echo... */
+ default:
+ break;
+ }
+
+ if (new_state_ok) {
+ set_my_want_state_will(option);
+ send_will(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ } else {
+ will_wont_resp[option]++;
+ send_wont(option, 0);
+ }
+ } else {
+ /*
+ * Handle options that need more things done after the
+ * other side has acknowledged the option.
+ */
+ switch (option) {
+ case TELOPT_LINEMODE:
+#ifdef KLUDGELINEMODE
+ kludgelinemode = 0;
+ send_do(TELOPT_SGA, 1);
+#endif
+ set_my_state_will(option);
+ slc_init();
+ send_do(TELOPT_SGA, 0);
+ return;
+ }
+ }
+ }
+ set_my_state_will(option);
+}
+
+ static void
+dontoption(option)
+ int option;
+{
+
+ if (will_wont_resp[option]) {
+ --will_wont_resp[option];
+ if (will_wont_resp[option] && my_state_is_wont(option))
+ --will_wont_resp[option];
+ }
+
+ if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
+ switch (option) {
+ case TELOPT_LINEMODE:
+ linemode = 0; /* put us back to the default state */
+ break;
+#ifdef OLD_ENVIRON
+ case TELOPT_NEW_ENVIRON:
+ /*
+ * The new environ option wasn't recognized, try
+ * the old one.
+ */
+ send_will(TELOPT_OLD_ENVIRON, 1);
+ telopt_environ = TELOPT_OLD_ENVIRON;
+ break;
+#endif
+ }
+ /* we always accept a DONT */
+ set_my_want_state_wont(option);
+ if (my_state_is_will(option))
+ send_wont(option, 0);
+ setconnmode(0); /* Set new tty mode */
+ }
+ set_my_state_wont(option);
+}
+
+/*
+ * Given a buffer returned by tgetent(), this routine will turn
+ * the pipe seperated list of names in the buffer into an array
+ * of pointers to null terminated names. We toss out any bad,
+ * duplicate, or verbose names (names with spaces).
+ */
+
+static char *name_unknown = "UNKNOWN";
+static char *unknown[] = { 0, 0 };
+
+ char **
+mklist(buf, name)
+ char *buf, *name;
+{
+ register int n;
+ register char c, *cp, **argvp, *cp2, **argv, **avt;
+
+ if (name) {
+ if (strlen(name) > 40) {
+ name = 0;
+ unknown[0] = name_unknown;
+ } else {
+ unknown[0] = name;
+ upcase(name);
+ }
+ } else
+ unknown[0] = name_unknown;
+ /*
+ * Count up the number of names.
+ */
+ for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
+ if (*cp == '|')
+ n++;
+ }
+ /*
+ * Allocate an array to put the name pointers into
+ */
+ argv = (char **)malloc((n+3)*sizeof(char *));
+ if (argv == 0)
+ return(unknown);
+
+ /*
+ * Fill up the array of pointers to names.
+ */
+ *argv = 0;
+ argvp = argv+1;
+ n = 0;
+ for (cp = cp2 = buf; (c = *cp); cp++) {
+ if (c == '|' || c == ':') {
+ *cp++ = '\0';
+ /*
+ * Skip entries that have spaces or are over 40
+ * characters long. If this is our environment
+ * name, then put it up front. Otherwise, as
+ * long as this is not a duplicate name (case
+ * insensitive) add it to the list.
+ */
+ if (n || (cp - cp2 > 41))
+ ;
+ else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
+ *argv = cp2;
+ else if (is_unique(cp2, argv+1, argvp))
+ *argvp++ = cp2;
+ if (c == ':')
+ break;
+ /*
+ * Skip multiple delimiters. Reset cp2 to
+ * the beginning of the next name. Reset n,
+ * the flag for names with spaces.
+ */
+ while ((c = *cp) == '|')
+ cp++;
+ cp2 = cp;
+ n = 0;
+ }
+ /*
+ * Skip entries with spaces or non-ascii values.
+ * Convert lower case letters to upper case.
+ */
+ if ((c == ' ') || !isascii(c))
+ n = 1;
+ else if (islower(c))
+ *cp = toupper(c);
+ }
+
+ /*
+ * Check for an old V6 2 character name. If the second
+ * name points to the beginning of the buffer, and is
+ * only 2 characters long, move it to the end of the array.
+ */
+ if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
+ --argvp;
+ for (avt = &argv[1]; avt < argvp; avt++)
+ *avt = *(avt+1);
+ *argvp++ = buf;
+ }
+
+ /*
+ * Duplicate last name, for TTYPE option, and null
+ * terminate the array. If we didn't find a match on
+ * our terminal name, put that name at the beginning.
+ */
+ cp = *(argvp-1);
+ *argvp++ = cp;
+ *argvp = 0;
+
+ if (*argv == 0) {
+ if (name)
+ *argv = name;
+ else {
+ --argvp;
+ for (avt = argv; avt < argvp; avt++)
+ *avt = *(avt+1);
+ }
+ }
+ if (*argv)
+ return(argv);
+ else
+ return(unknown);
+}
+
+ int
+is_unique(name, as, ae)
+ register char *name, **as, **ae;
+{
+ register char **ap;
+ register int n;
+
+ n = strlen(name) + 1;
+ for (ap = as; ap < ae; ap++)
+ if (strncasecmp(*ap, name, n) == 0)
+ return(0);
+ return (1);
+}
+
+#ifdef TERMCAP
+char termbuf[1024];
+
+ /*ARGSUSED*/
+ int
+setupterm(tname, fd, errp)
+ char *tname;
+ int fd, *errp;
+{
+ if (tgetent(termbuf, tname) == 1) {
+ termbuf[1023] = '\0';
+ if (errp)
+ *errp = 1;
+ return(0);
+ }
+ if (errp)
+ *errp = 0;
+ return(-1);
+}
+#else
+#define termbuf ttytype
+extern char ttytype[];
+#endif
+
+int resettermname = 1;
+
+ char *
+gettermname()
+{
+ char *tname;
+ static char **tnamep = 0;
+ static char **next;
+ int err;
+
+ if (resettermname) {
+ resettermname = 0;
+ if (tnamep && tnamep != unknown)
+ free(tnamep);
+ if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
+ (setupterm(tname, 1, &err) == 0)) {
+ tnamep = mklist(termbuf, tname);
+ } else {
+ if (tname && (strlen(tname) <= 40)) {
+ unknown[0] = tname;
+ upcase(tname);
+ } else
+ unknown[0] = name_unknown;
+ tnamep = unknown;
+ }
+ next = tnamep;
+ }
+ if (*next == 0)
+ next = tnamep;
+ return(*next++);
+}
+/*
+ * suboption()
+ *
+ * Look at the sub-option buffer, and try to be helpful to the other
+ * side.
+ *
+ * Currently we recognize:
+ *
+ * Terminal type, send request.
+ * Terminal speed (send request).
+ * Local flow control (is request).
+ * Linemode
+ */
+
+ static void
+suboption()
+{
+ unsigned char subchar;
+
+ printsub('<', subbuffer, SB_LEN()+2);
+ switch (subchar = SB_GET()) {
+ case TELOPT_TTYPE:
+ if (my_want_state_is_wont(TELOPT_TTYPE))
+ return;
+ if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
+ return;
+ } else {
+ char *name;
+ unsigned char temp[50];
+ int len;
+
+#if defined(TN3270)
+ if (tn3270_ttype()) {
+ return;
+ }
+#endif /* defined(TN3270) */
+ name = gettermname();
+ len = strlen(name) + 4 + 2;
+ if (len < NETROOM()) {
+ sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
+ TELQUAL_IS, name, IAC, SE);
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', &temp[2], len-2);
+ } else {
+ ExitString("No room in buffer for terminal type.\n", 1);
+ /*NOTREACHED*/
+ }
+ }
+ break;
+ case TELOPT_TSPEED:
+ if (my_want_state_is_wont(TELOPT_TSPEED))
+ return;
+ if (SB_EOF())
+ return;
+ if (SB_GET() == TELQUAL_SEND) {
+ long ospeed, ispeed;
+ unsigned char temp[50];
+ int len;
+
+ TerminalSpeeds(&ispeed, &ospeed);
+
+ sprintf((char *)temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
+ TELQUAL_IS, ospeed, ispeed, IAC, SE);
+ len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */
+
+ if (len < NETROOM()) {
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', temp+2, len - 2);
+ }
+/*@*/ else printf("lm_will: not enough room in buffer\n");
+ }
+ break;
+ case TELOPT_LFLOW:
+ if (my_want_state_is_wont(TELOPT_LFLOW))
+ return;
+ if (SB_EOF())
+ return;
+ switch(SB_GET()) {
+ case LFLOW_RESTART_ANY:
+ restartany = 1;
+ break;
+ case LFLOW_RESTART_XON:
+ restartany = 0;
+ break;
+ case LFLOW_ON:
+ localflow = 1;
+ break;
+ case LFLOW_OFF:
+ localflow = 0;
+ break;
+ default:
+ return;
+ }
+ setcommandmode();
+ setconnmode(0);
+ break;
+
+ case TELOPT_LINEMODE:
+ if (my_want_state_is_wont(TELOPT_LINEMODE))
+ return;
+ if (SB_EOF())
+ return;
+ switch (SB_GET()) {
+ case WILL:
+ lm_will(subpointer, SB_LEN());
+ break;
+ case WONT:
+ lm_wont(subpointer, SB_LEN());
+ break;
+ case DO:
+ lm_do(subpointer, SB_LEN());
+ break;
+ case DONT:
+ lm_dont(subpointer, SB_LEN());
+ break;
+ case LM_SLC:
+ slc(subpointer, SB_LEN());
+ break;
+ case LM_MODE:
+ lm_mode(subpointer, SB_LEN(), 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+#ifdef OLD_ENVIRON
+ case TELOPT_OLD_ENVIRON:
+#endif
+ case TELOPT_NEW_ENVIRON:
+ if (SB_EOF())
+ return;
+ switch(SB_PEEK()) {
+ case TELQUAL_IS:
+ case TELQUAL_INFO:
+ if (my_want_state_is_dont(subchar))
+ return;
+ break;
+ case TELQUAL_SEND:
+ if (my_want_state_is_wont(subchar)) {
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ env_opt(subpointer, SB_LEN());
+ break;
+
+ case TELOPT_XDISPLOC:
+ if (my_want_state_is_wont(TELOPT_XDISPLOC))
+ return;
+ if (SB_EOF())
+ return;
+ if (SB_GET() == TELQUAL_SEND) {
+ unsigned char temp[50], *dp;
+ int len;
+
+ if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
+ /*
+ * Something happened, we no longer have a DISPLAY
+ * variable. So, turn off the option.
+ */
+ send_wont(TELOPT_XDISPLOC, 1);
+ break;
+ }
+ sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
+ TELQUAL_IS, dp, IAC, SE);
+ len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */
+
+ if (len < NETROOM()) {
+ ring_supply_data(&netoring, temp, len);
+ printsub('>', temp+2, len - 2);
+ }
+/*@*/ else printf("lm_will: not enough room in buffer\n");
+ }
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION: {
+ if (!autologin)
+ break;
+ if (SB_EOF())
+ return;
+ switch(SB_GET()) {
+ case TELQUAL_IS:
+ if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
+ return;
+ auth_is(subpointer, SB_LEN());
+ break;
+ case TELQUAL_SEND:
+ if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+ return;
+ auth_send(subpointer, SB_LEN());
+ break;
+ case TELQUAL_REPLY:
+ if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
+ return;
+ auth_reply(subpointer, SB_LEN());
+ break;
+ case TELQUAL_NAME:
+ if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
+ return;
+ auth_name(subpointer, SB_LEN());
+ break;
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
+
+ void
+lm_will(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+/*@*/ printf("lm_will: no command!!!\n"); /* Should not happen... */
+ return;
+ }
+ switch(cmd[0]) {
+ case LM_FORWARDMASK: /* We shouldn't ever get this... */
+ default:
+ str_lm[3] = DONT;
+ str_lm[4] = cmd[0];
+ if (NETROOM() > sizeof(str_lm)) {
+ ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+ printsub('>', &str_lm[2], sizeof(str_lm)-2);
+ }
+/*@*/ else printf("lm_will: not enough room in buffer\n");
+ break;
+ }
+}
+
+ void
+lm_wont(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+/*@*/ printf("lm_wont: no command!!!\n"); /* Should not happen... */
+ return;
+ }
+ switch(cmd[0]) {
+ case LM_FORWARDMASK: /* We shouldn't ever get this... */
+ default:
+ /* We are always DONT, so don't respond */
+ return;
+ }
+}
+
+ void
+lm_do(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+/*@*/ printf("lm_do: no command!!!\n"); /* Should not happen... */
+ return;
+ }
+ switch(cmd[0]) {
+ case LM_FORWARDMASK:
+ default:
+ str_lm[3] = WONT;
+ str_lm[4] = cmd[0];
+ if (NETROOM() > sizeof(str_lm)) {
+ ring_supply_data(&netoring, str_lm, sizeof(str_lm));
+ printsub('>', &str_lm[2], sizeof(str_lm)-2);
+ }
+/*@*/ else printf("lm_do: not enough room in buffer\n");
+ break;
+ }
+}
+
+ void
+lm_dont(cmd, len)
+ unsigned char *cmd;
+ int len;
+{
+ if (len < 1) {
+/*@*/ printf("lm_dont: no command!!!\n"); /* Should not happen... */
+ return;
+ }
+ switch(cmd[0]) {
+ case LM_FORWARDMASK:
+ default:
+ /* we are always WONT, so don't respond */
+ break;
+ }
+}
+
+static unsigned char str_lm_mode[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
+};
+
+ void
+lm_mode(cmd, len, init)
+ unsigned char *cmd;
+ int len, init;
+{
+ if (len != 1)
+ return;
+ if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
+ return;
+ if (*cmd&MODE_ACK)
+ return;
+ linemode = *cmd&(MODE_MASK&~MODE_ACK);
+ str_lm_mode[4] = linemode;
+ if (!init)
+ str_lm_mode[4] |= MODE_ACK;
+ if (NETROOM() > sizeof(str_lm_mode)) {
+ ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
+ printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
+ }
+/*@*/ else printf("lm_mode: not enough room in buffer\n");
+ setconnmode(0); /* set changed mode */
+}
+
+
+
+/*
+ * slc()
+ * Handle special character suboption of LINEMODE.
+ */
+
+struct spc {
+ cc_t val;
+ cc_t *valp;
+ char flags; /* Current flags & level */
+ char mylevel; /* Maximum level & flags */
+} spc_data[NSLC+1];
+
+#define SLC_IMPORT 0
+#define SLC_EXPORT 1
+#define SLC_RVALUE 2
+static int slc_mode = SLC_EXPORT;
+
+ void
+slc_init()
+{
+ register struct spc *spcp;
+
+ localchars = 1;
+ for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
+ spcp->val = 0;
+ spcp->valp = 0;
+ spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
+ }
+
+#define initfunc(func, flags) { \
+ spcp = &spc_data[func]; \
+ if (spcp->valp = tcval(func)) { \
+ spcp->val = *spcp->valp; \
+ spcp->mylevel = SLC_VARIABLE|flags; \
+ } else { \
+ spcp->val = 0; \
+ spcp->mylevel = SLC_DEFAULT; \
+ } \
+ }
+
+ initfunc(SLC_SYNCH, 0);
+ /* No BRK */
+ initfunc(SLC_AO, 0);
+ initfunc(SLC_AYT, 0);
+ /* No EOR */
+ initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
+ initfunc(SLC_EOF, 0);
+#ifndef SYSV_TERMIO
+ initfunc(SLC_SUSP, SLC_FLUSHIN);
+#endif
+ initfunc(SLC_EC, 0);
+ initfunc(SLC_EL, 0);
+#ifndef SYSV_TERMIO
+ initfunc(SLC_EW, 0);
+ initfunc(SLC_RP, 0);
+ initfunc(SLC_LNEXT, 0);
+#endif
+ initfunc(SLC_XON, 0);
+ initfunc(SLC_XOFF, 0);
+#ifdef SYSV_TERMIO
+ spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
+ spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
+#endif
+ initfunc(SLC_FORW1, 0);
+#ifdef USE_TERMIO
+ initfunc(SLC_FORW2, 0);
+ /* No FORW2 */
+#endif
+
+ initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
+#undef initfunc
+
+ if (slc_mode == SLC_EXPORT)
+ slc_export();
+ else
+ slc_import(1);
+
+}
+
+ void
+slcstate()
+{
+ printf("Special characters are %s values\n",
+ slc_mode == SLC_IMPORT ? "remote default" :
+ slc_mode == SLC_EXPORT ? "local" :
+ "remote");
+}
+
+ void
+slc_mode_export()
+{
+ slc_mode = SLC_EXPORT;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_export();
+}
+
+ void
+slc_mode_import(def)
+ int def;
+{
+ slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
+ if (my_state_is_will(TELOPT_LINEMODE))
+ slc_import(def);
+}
+
+unsigned char slc_import_val[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
+};
+unsigned char slc_import_def[] = {
+ IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
+};
+
+ void
+slc_import(def)
+ int def;
+{
+ if (NETROOM() > sizeof(slc_import_val)) {
+ if (def) {
+ ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
+ printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
+ } else {
+ ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
+ printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
+ }
+ }
+/*@*/ else printf("slc_import: not enough room\n");
+}
+
+ void
+slc_export()
+{
+ register struct spc *spcp;
+
+ TerminalDefaultChars();
+
+ slc_start_reply();
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->mylevel != SLC_NOSUPPORT) {
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ if (spcp->valp)
+ spcp->val = *spcp->valp;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ (void)slc_update();
+ setconnmode(1); /* Make sure the character values are set */
+}
+
+ void
+slc(cp, len)
+ register unsigned char *cp;
+ int len;
+{
+ register struct spc *spcp;
+ register int func,level;
+
+ slc_start_reply();
+
+ for (; len >= 3; len -=3, cp +=3) {
+
+ func = cp[SLC_FUNC];
+
+ if (func == 0) {
+ /*
+ * Client side: always ignore 0 function.
+ */
+ continue;
+ }
+ if (func > NSLC) {
+ if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
+ slc_add_reply(func, SLC_NOSUPPORT, 0);
+ continue;
+ }
+
+ spcp = &spc_data[func];
+
+ level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
+
+ if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
+ ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
+ continue;
+ }
+
+ if (level == (SLC_DEFAULT|SLC_ACK)) {
+ /*
+ * This is an error condition, the SLC_ACK
+ * bit should never be set for the SLC_DEFAULT
+ * level. Our best guess to recover is to
+ * ignore the SLC_ACK bit.
+ */
+ cp[SLC_FLAGS] &= ~SLC_ACK;
+ }
+
+ if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */
+ continue;
+ }
+
+ level &= ~SLC_ACK;
+
+ if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
+ spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
+ spcp->val = (cc_t)cp[SLC_VALUE];
+ }
+ if (level == SLC_DEFAULT) {
+ if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
+ spcp->flags = spcp->mylevel;
+ else
+ spcp->flags = SLC_NOSUPPORT;
+ }
+ slc_add_reply(func, spcp->flags, spcp->val);
+ }
+ slc_end_reply();
+ if (slc_update())
+ setconnmode(1); /* set the new character values */
+}
+
+ void
+slc_check()
+{
+ register struct spc *spcp;
+
+ slc_start_reply();
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (spcp->valp && spcp->val != *spcp->valp) {
+ spcp->val = *spcp->valp;
+ if (spcp->val == (cc_t)(_POSIX_VDISABLE))
+ spcp->flags = SLC_NOSUPPORT;
+ else
+ spcp->flags = spcp->mylevel;
+ slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
+ }
+ }
+ slc_end_reply();
+ setconnmode(1);
+}
+
+
+unsigned char slc_reply[128];
+unsigned char *slc_replyp;
+
+ void
+slc_start_reply()
+{
+ slc_replyp = slc_reply;
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SB;
+ *slc_replyp++ = TELOPT_LINEMODE;
+ *slc_replyp++ = LM_SLC;
+}
+
+ void
+slc_add_reply(func, flags, value)
+ unsigned char func;
+ unsigned char flags;
+ cc_t value;
+{
+ if ((*slc_replyp++ = func) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = flags) == IAC)
+ *slc_replyp++ = IAC;
+ if ((*slc_replyp++ = (unsigned char)value) == IAC)
+ *slc_replyp++ = IAC;
+}
+
+ void
+slc_end_reply()
+{
+ register int len;
+
+ *slc_replyp++ = IAC;
+ *slc_replyp++ = SE;
+ len = slc_replyp - slc_reply;
+ if (len <= 6)
+ return;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
+ printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
+ }
+/*@*/else printf("slc_end_reply: not enough room\n");
+}
+
+ int
+slc_update()
+{
+ register struct spc *spcp;
+ int need_update = 0;
+
+ for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
+ if (!(spcp->flags&SLC_ACK))
+ continue;
+ spcp->flags &= ~SLC_ACK;
+ if (spcp->valp && (*spcp->valp != spcp->val)) {
+ *spcp->valp = spcp->val;
+ need_update = 1;
+ }
+ }
+ return(need_update);
+}
+
+#ifdef OLD_ENVIRON
+# ifdef ENV_HACK
+/*
+ * Earlier version of telnet/telnetd from the BSD code had
+ * the definitions of VALUE and VAR reversed. To ensure
+ * maximum interoperability, we assume that the server is
+ * an older BSD server, until proven otherwise. The newer
+ * BSD servers should be able to handle either definition,
+ * so it is better to use the wrong values if we don't
+ * know what type of server it is.
+ */
+int env_auto = 1;
+int old_env_var = OLD_ENV_VAR;
+int old_env_value = OLD_ENV_VALUE;
+# else
+# define old_env_var OLD_ENV_VAR
+# define old_env_value OLD_ENV_VALUE
+# endif
+#endif
+
+ void
+env_opt(buf, len)
+ register unsigned char *buf;
+ register int len;
+{
+ register unsigned char *ep = 0, *epc = 0;
+ register int i;
+
+ switch(buf[0]&0xff) {
+ case TELQUAL_SEND:
+ env_opt_start();
+ if (len == 1) {
+ env_opt_add(NULL);
+ } else for (i = 1; i < len; i++) {
+ switch (buf[i]&0xff) {
+#ifdef OLD_ENVIRON
+ case OLD_ENV_VAR:
+# ifdef ENV_HACK
+ if (telopt_environ == TELOPT_OLD_ENVIRON
+ && env_auto) {
+ /* Server has the same definitions */
+ old_env_var = OLD_ENV_VAR;
+ old_env_value = OLD_ENV_VALUE;
+ }
+ /* FALL THROUGH */
+# endif
+ case OLD_ENV_VALUE:
+ /*
+ * Although OLD_ENV_VALUE is not legal, we will
+ * still recognize it, just in case it is an
+ * old server that has VAR & VALUE mixed up...
+ */
+ /* FALL THROUGH */
+#else
+ case NEW_ENV_VAR:
+#endif
+ case ENV_USERVAR:
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ ep = epc = &buf[i+1];
+ break;
+ case ENV_ESC:
+ i++;
+ /*FALL THROUGH*/
+ default:
+ if (epc)
+ *epc++ = buf[i];
+ break;
+ }
+ }
+ if (ep) {
+ *epc = 0;
+ env_opt_add(ep);
+ }
+ env_opt_end(1);
+ break;
+
+ case TELQUAL_IS:
+ case TELQUAL_INFO:
+ /* Ignore for now. We shouldn't get it anyway. */
+ break;
+
+ default:
+ break;
+ }
+}
+
+#define OPT_REPLY_SIZE 256
+unsigned char *opt_reply;
+unsigned char *opt_replyp;
+unsigned char *opt_replyend;
+
+ void
+env_opt_start()
+{
+ if (opt_reply)
+ opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE);
+ else
+ opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
+ if (opt_reply == NULL) {
+/*@*/ printf("env_opt_start: malloc()/realloc() failed!!!\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply;
+ opt_replyend = opt_reply + OPT_REPLY_SIZE;
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SB;
+ *opt_replyp++ = telopt_environ;
+ *opt_replyp++ = TELQUAL_IS;
+}
+
+ void
+env_opt_start_info()
+{
+ env_opt_start();
+ if (opt_replyp)
+ opt_replyp[-1] = TELQUAL_INFO;
+}
+
+ void
+env_opt_add(ep)
+ register unsigned char *ep;
+{
+ register unsigned char *vp, c;
+
+ if (opt_reply == NULL) /*XXX*/
+ return; /*XXX*/
+
+ if (ep == NULL || *ep == '\0') {
+ /* Send user defined variables first. */
+ env_default(1, 0);
+ while (ep = env_default(0, 0))
+ env_opt_add(ep);
+
+ /* Now add the list of well know variables. */
+ env_default(1, 1);
+ while (ep = env_default(0, 1))
+ env_opt_add(ep);
+ return;
+ }
+ vp = env_getvalue(ep);
+ if (opt_replyp + (vp ? strlen((char *)vp) : 0) +
+ strlen((char *)ep) + 6 > opt_replyend)
+ {
+ register int len;
+ opt_replyend += OPT_REPLY_SIZE;
+ len = opt_replyend - opt_reply;
+ opt_reply = (unsigned char *)realloc(opt_reply, len);
+ if (opt_reply == NULL) {
+/*@*/ printf("env_opt_add: realloc() failed!!!\n");
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ return;
+ }
+ opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
+ opt_replyend = opt_reply + len;
+ }
+ if (opt_welldefined(ep))
+#ifdef OLD_ENVIRON
+ if (telopt_environ == TELOPT_OLD_ENVIRON)
+ *opt_replyp++ = old_env_var;
+ else
+#endif
+ *opt_replyp++ = NEW_ENV_VAR;
+ else
+ *opt_replyp++ = ENV_USERVAR;
+ for (;;) {
+ while (c = *ep++) {
+ switch(c&0xff) {
+ case IAC:
+ *opt_replyp++ = IAC;
+ break;
+ case NEW_ENV_VAR:
+ case NEW_ENV_VALUE:
+ case ENV_ESC:
+ case ENV_USERVAR:
+ *opt_replyp++ = ENV_ESC;
+ break;
+ }
+ *opt_replyp++ = c;
+ }
+ if (ep = vp) {
+#ifdef OLD_ENVIRON
+ if (telopt_environ == TELOPT_OLD_ENVIRON)
+ *opt_replyp++ = old_env_value;
+ else
+#endif
+ *opt_replyp++ = NEW_ENV_VALUE;
+ vp = NULL;
+ } else
+ break;
+ }
+}
+
+ int
+opt_welldefined(ep)
+ char *ep;
+{
+ if ((strcmp(ep, "USER") == 0) ||
+ (strcmp(ep, "DISPLAY") == 0) ||
+ (strcmp(ep, "PRINTER") == 0) ||
+ (strcmp(ep, "SYSTEMTYPE") == 0) ||
+ (strcmp(ep, "JOB") == 0) ||
+ (strcmp(ep, "ACCT") == 0))
+ return(1);
+ return(0);
+}
+ void
+env_opt_end(emptyok)
+ register int emptyok;
+{
+ register int len;
+
+ len = opt_replyp - opt_reply + 2;
+ if (emptyok || len > 6) {
+ *opt_replyp++ = IAC;
+ *opt_replyp++ = SE;
+ if (NETROOM() > len) {
+ ring_supply_data(&netoring, opt_reply, len);
+ printsub('>', &opt_reply[2], len - 2);
+ }
+/*@*/ else printf("slc_end_reply: not enough room\n");
+ }
+ if (opt_reply) {
+ free(opt_reply);
+ opt_reply = opt_replyp = opt_replyend = NULL;
+ }
+}
+
+
+
+ int
+telrcv()
+{
+ register int c;
+ register int scc;
+ register unsigned char *sbp;
+ int count;
+ int returnValue = 0;
+
+ scc = 0;
+ count = 0;
+ while (TTYROOM() > 2) {
+ if (scc == 0) {
+ if (count) {
+ ring_consumed(&netiring, count);
+ returnValue = 1;
+ count = 0;
+ }
+ sbp = netiring.consume;
+ scc = ring_full_consecutive(&netiring);
+ if (scc == 0) {
+ /* No more data coming in */
+ break;
+ }
+ }
+
+ c = *sbp++ & 0xff, scc--; count++;
+
+ switch (telrcv_state) {
+
+ case TS_CR:
+ telrcv_state = TS_DATA;
+ if (c == '\0') {
+ break; /* Ignore \0 after CR */
+ }
+ else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
+ TTYADD(c);
+ break;
+ }
+ /* Else, fall through */
+
+ case TS_DATA:
+ if (c == IAC) {
+ telrcv_state = TS_IAC;
+ break;
+ }
+# if defined(TN3270)
+ if (In3270) {
+ *Ifrontp++ = c;
+ while (scc > 0) {
+ c = *sbp++ & 0377, scc--; count++;
+ if (c == IAC) {
+ telrcv_state = TS_IAC;
+ break;
+ }
+ *Ifrontp++ = c;
+ }
+ } else
+# endif /* defined(TN3270) */
+ /*
+ * The 'crmod' hack (see following) is needed
+ * since we can't * set CRMOD on output only.
+ * Machines like MULTICS like to send \r without
+ * \n; since we must turn off CRMOD to get proper
+ * input, the mapping is done here (sigh).
+ */
+ if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
+ if (scc > 0) {
+ c = *sbp&0xff;
+ if (c == 0) {
+ sbp++, scc--; count++;
+ /* a "true" CR */
+ TTYADD('\r');
+ } else if (my_want_state_is_dont(TELOPT_ECHO) &&
+ (c == '\n')) {
+ sbp++, scc--; count++;
+ TTYADD('\n');
+ } else {
+
+ TTYADD('\r');
+ if (crmod) {
+ TTYADD('\n');
+ }
+ }
+ } else {
+ telrcv_state = TS_CR;
+ TTYADD('\r');
+ if (crmod) {
+ TTYADD('\n');
+ }
+ }
+ } else {
+ TTYADD(c);
+ }
+ continue;
+
+ case TS_IAC:
+process_iac:
+ switch (c) {
+
+ case WILL:
+ telrcv_state = TS_WILL;
+ continue;
+
+ case WONT:
+ telrcv_state = TS_WONT;
+ continue;
+
+ case DO:
+ telrcv_state = TS_DO;
+ continue;
+
+ case DONT:
+ telrcv_state = TS_DONT;
+ continue;
+
+ case DM:
+ /*
+ * We may have missed an urgent notification,
+ * so make sure we flush whatever is in the
+ * buffer currently.
+ */
+ printoption("RCVD", IAC, DM);
+ SYNCHing = 1;
+ (void) ttyflush(1);
+ SYNCHing = stilloob();
+ settimer(gotDM);
+ break;
+
+ case SB:
+ SB_CLEAR();
+ telrcv_state = TS_SB;
+ continue;
+
+# if defined(TN3270)
+ case EOR:
+ if (In3270) {
+ if (Ibackp == Ifrontp) {
+ Ibackp = Ifrontp = Ibuf;
+ ISend = 0; /* should have been! */
+ } else {
+ Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
+ ISend = 1;
+ }
+ }
+ printoption("RCVD", IAC, EOR);
+ break;
+# endif /* defined(TN3270) */
+
+ case IAC:
+# if !defined(TN3270)
+ TTYADD(IAC);
+# else /* !defined(TN3270) */
+ if (In3270) {
+ *Ifrontp++ = IAC;
+ } else {
+ TTYADD(IAC);
+ }
+# endif /* !defined(TN3270) */
+ break;
+
+ case NOP:
+ case GA:
+ default:
+ printoption("RCVD", IAC, c);
+ break;
+ }
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_WILL:
+ printoption("RCVD", WILL, c);
+ willoption(c);
+ SetIn3270();
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_WONT:
+ printoption("RCVD", WONT, c);
+ wontoption(c);
+ SetIn3270();
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_DO:
+ printoption("RCVD", DO, c);
+ dooption(c);
+ SetIn3270();
+ if (c == TELOPT_NAWS) {
+ sendnaws();
+ } else if (c == TELOPT_LFLOW) {
+ localflow = 1;
+ setcommandmode();
+ setconnmode(0);
+ }
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_DONT:
+ printoption("RCVD", DONT, c);
+ dontoption(c);
+ flushline = 1;
+ setconnmode(0); /* set new tty mode (maybe) */
+ SetIn3270();
+ telrcv_state = TS_DATA;
+ continue;
+
+ case TS_SB:
+ if (c == IAC) {
+ telrcv_state = TS_SE;
+ } else {
+ SB_ACCUM(c);
+ }
+ continue;
+
+ case TS_SE:
+ if (c != SE) {
+ if (c != IAC) {
+ /*
+ * This is an error. We only expect to get
+ * "IAC IAC" or "IAC SE". Several things may
+ * have happend. An IAC was not doubled, the
+ * IAC SE was left off, or another option got
+ * inserted into the suboption are all possibilities.
+ * If we assume that the IAC was not doubled,
+ * and really the IAC SE was left off, we could
+ * get into an infinate loop here. So, instead,
+ * we terminate the suboption, and process the
+ * partial suboption if we can.
+ */
+ SB_ACCUM(IAC);
+ SB_ACCUM(c);
+ subpointer -= 2;
+ SB_TERM();
+
+ printoption("In SUBOPTION processing, RCVD", IAC, c);
+ suboption(); /* handle sub-option */
+ SetIn3270();
+ telrcv_state = TS_IAC;
+ goto process_iac;
+ }
+ SB_ACCUM(c);
+ telrcv_state = TS_SB;
+ } else {
+ SB_ACCUM(IAC);
+ SB_ACCUM(SE);
+ subpointer -= 2;
+ SB_TERM();
+ suboption(); /* handle sub-option */
+ SetIn3270();
+ telrcv_state = TS_DATA;
+ }
+ }
+ }
+ if (count)
+ ring_consumed(&netiring, count);
+ return returnValue||count;
+}
+
+static int bol = 1, local = 0;
+
+ int
+rlogin_susp()
+{
+ if (local) {
+ local = 0;
+ bol = 1;
+ command(0, "z\n", 2);
+ return(1);
+ }
+ return(0);
+}
+
+ static int
+telsnd()
+{
+ int tcc;
+ int count;
+ int returnValue = 0;
+ unsigned char *tbp;
+
+ tcc = 0;
+ count = 0;
+ while (NETROOM() > 2) {
+ register int sc;
+ register int c;
+
+ if (tcc == 0) {
+ if (count) {
+ ring_consumed(&ttyiring, count);
+ returnValue = 1;
+ count = 0;
+ }
+ tbp = ttyiring.consume;
+ tcc = ring_full_consecutive(&ttyiring);
+ if (tcc == 0) {
+ break;
+ }
+ }
+ c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
+ if (rlogin != _POSIX_VDISABLE) {
+ if (bol) {
+ bol = 0;
+ if (sc == rlogin) {
+ local = 1;
+ continue;
+ }
+ } else if (local) {
+ local = 0;
+ if (sc == '.' || c == termEofChar) {
+ bol = 1;
+ command(0, "close\n", 6);
+ continue;
+ }
+ if (sc == termSuspChar) {
+ bol = 1;
+ command(0, "z\n", 2);
+ continue;
+ }
+ if (sc == escape) {
+ command(0, (char *)tbp, tcc);
+ bol = 1;
+ count += tcc;
+ tcc = 0;
+ flushline = 1;
+ break;
+ }
+ if (sc != rlogin) {
+ ++tcc;
+ --tbp;
+ --count;
+ c = sc = rlogin;
+ }
+ }
+ if ((sc == '\n') || (sc == '\r'))
+ bol = 1;
+ } else if (sc == escape) {
+ /*
+ * Double escape is a pass through of a single escape character.
+ */
+ if (tcc && strip(*tbp) == escape) {
+ tbp++;
+ tcc--;
+ count++;
+ bol = 0;
+ } else {
+ command(0, (char *)tbp, tcc);
+ bol = 1;
+ count += tcc;
+ tcc = 0;
+ flushline = 1;
+ break;
+ }
+ } else
+ bol = 0;
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
+ if (tcc > 0 && strip(*tbp) == echoc) {
+ tcc--; tbp++; count++;
+ } else {
+ dontlecho = !dontlecho;
+ settimer(echotoggle);
+ setconnmode(0);
+ flushline = 1;
+ break;
+ }
+ }
+#endif
+ if (MODE_LOCAL_CHARS(globalmode)) {
+ if (TerminalSpecialChars(sc) == 0) {
+ bol = 1;
+ break;
+ }
+ }
+ if (my_want_state_is_wont(TELOPT_BINARY)) {
+ switch (c) {
+ case '\n':
+ /*
+ * If we are in CRMOD mode (\r ==> \n)
+ * on our local machine, then probably
+ * a newline (unix) is CRLF (TELNET).
+ */
+ if (MODE_LOCAL_CHARS(globalmode)) {
+ NETADD('\r');
+ }
+ NETADD('\n');
+ bol = flushline = 1;
+ break;
+ case '\r':
+ if (!crlf) {
+ NET2ADD('\r', '\0');
+ } else {
+ NET2ADD('\r', '\n');
+ }
+ bol = flushline = 1;
+ break;
+ case IAC:
+ NET2ADD(IAC, IAC);
+ break;
+ default:
+ NETADD(c);
+ break;
+ }
+ } else if (c == IAC) {
+ NET2ADD(IAC, IAC);
+ } else {
+ NETADD(c);
+ }
+ }
+ if (count)
+ ring_consumed(&ttyiring, count);
+ return returnValue||count; /* Non-zero if we did anything */
+}
+
+/*
+ * Scheduler()
+ *
+ * Try to do something.
+ *
+ * If we do something useful, return 1; else return 0.
+ *
+ */
+
+
+ int
+Scheduler(block)
+ int block; /* should we block in the select ? */
+{
+ /* One wants to be a bit careful about setting returnValue
+ * to one, since a one implies we did some useful work,
+ * and therefore probably won't be called to block next
+ * time (TN3270 mode only).
+ */
+ int returnValue;
+ int netin, netout, netex, ttyin, ttyout;
+
+ /* Decide which rings should be processed */
+
+ netout = ring_full_count(&netoring) &&
+ (flushline ||
+ (my_want_state_is_wont(TELOPT_LINEMODE)
+#ifdef KLUDGELINEMODE
+ && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
+#endif
+ ) ||
+ my_want_state_is_will(TELOPT_BINARY));
+ ttyout = ring_full_count(&ttyoring);
+
+#if defined(TN3270)
+ ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
+#else /* defined(TN3270) */
+ ttyin = ring_empty_count(&ttyiring);
+#endif /* defined(TN3270) */
+
+#if defined(TN3270)
+ netin = ring_empty_count(&netiring);
+# else /* !defined(TN3270) */
+ netin = !ISend && ring_empty_count(&netiring);
+# endif /* !defined(TN3270) */
+
+ netex = !SYNCHing;
+
+ /* If we have seen a signal recently, reset things */
+# if defined(TN3270) && defined(unix)
+ if (HaveInput) {
+ HaveInput = 0;
+ (void) signal(SIGIO, inputAvailable);
+ }
+#endif /* defined(TN3270) && defined(unix) */
+
+ /* Call to system code to process rings */
+
+ returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
+
+ /* Now, look at the input rings, looking for work to do. */
+
+ if (ring_full_count(&ttyiring)) {
+# if defined(TN3270)
+ if (In3270) {
+ int c;
+
+ c = DataFromTerminal(ttyiring.consume,
+ ring_full_consecutive(&ttyiring));
+ if (c) {
+ returnValue = 1;
+ ring_consumed(&ttyiring, c);
+ }
+ } else {
+# endif /* defined(TN3270) */
+ returnValue |= telsnd();
+# if defined(TN3270)
+ }
+# endif /* defined(TN3270) */
+ }
+
+ if (ring_full_count(&netiring)) {
+# if !defined(TN3270)
+ returnValue |= telrcv();
+# else /* !defined(TN3270) */
+ returnValue = Push3270();
+# endif /* !defined(TN3270) */
+ }
+ return returnValue;
+}
+
+/*
+ * Select from tty and network...
+ */
+ void
+telnet(user)
+ char *user;
+{
+ sys_telnet_init();
+
+#if defined(AUTHENTICATION)
+ {
+ static char local_host[256] = { 0 };
+
+ if (!local_host[0]) {
+ gethostname(local_host, sizeof(local_host));
+ local_host[sizeof(local_host)-1] = 0;
+ }
+ auth_encrypt_init(local_host, hostname, "TELNET", 0);
+ auth_encrypt_user(user);
+ }
+#endif /* defined(AUTHENTICATION) */
+# if !defined(TN3270)
+ if (telnetport) {
+#if defined(AUTHENTICATION)
+ if (autologin)
+ send_will(TELOPT_AUTHENTICATION, 1);
+#endif
+ send_do(TELOPT_SGA, 1);
+ send_will(TELOPT_TTYPE, 1);
+ send_will(TELOPT_NAWS, 1);
+ send_will(TELOPT_TSPEED, 1);
+ send_will(TELOPT_LFLOW, 1);
+ send_will(TELOPT_LINEMODE, 1);
+ send_will(TELOPT_NEW_ENVIRON, 1);
+ send_do(TELOPT_STATUS, 1);
+ if (env_getvalue((unsigned char *)"DISPLAY"))
+ send_will(TELOPT_XDISPLOC, 1);
+ if (eight)
+ tel_enter_binary(eight);
+ }
+# endif /* !defined(TN3270) */
+
+# if !defined(TN3270)
+ for (;;) {
+ int schedValue;
+
+ while ((schedValue = Scheduler(0)) != 0) {
+ if (schedValue == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+
+ if (Scheduler(1) == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+# else /* !defined(TN3270) */
+ for (;;) {
+ int schedValue;
+
+ while (!In3270 && !shell_active) {
+ if (Scheduler(1) == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+
+ while ((schedValue = Scheduler(0)) != 0) {
+ if (schedValue == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+ /* If there is data waiting to go out to terminal, don't
+ * schedule any more data for the terminal.
+ */
+ if (ring_full_count(&ttyoring)) {
+ schedValue = 1;
+ } else {
+ if (shell_active) {
+ if (shell_continue() == 0) {
+ ConnectScreen();
+ }
+ } else if (In3270) {
+ schedValue = DoTerminalOutput();
+ }
+ }
+ if (schedValue && (shell_active == 0)) {
+ if (Scheduler(1) == -1) {
+ setcommandmode();
+ return;
+ }
+ }
+ }
+# endif /* !defined(TN3270) */
+}
+
+#if 0 /* XXX - this not being in is a bug */
+/*
+ * nextitem()
+ *
+ * Return the address of the next "item" in the TELNET data
+ * stream. This will be the address of the next character if
+ * the current address is a user data character, or it will
+ * be the address of the character following the TELNET command
+ * if the current address is a TELNET IAC ("I Am a Command")
+ * character.
+ */
+
+ static char *
+nextitem(current)
+ char *current;
+{
+ if ((*current&0xff) != IAC) {
+ return current+1;
+ }
+ switch (*(current+1)&0xff) {
+ case DO:
+ case DONT:
+ case WILL:
+ case WONT:
+ return current+3;
+ case SB: /* loop forever looking for the SE */
+ {
+ register char *look = current+2;
+
+ for (;;) {
+ if ((*look++&0xff) == IAC) {
+ if ((*look++&0xff) == SE) {
+ return look;
+ }
+ }
+ }
+ }
+ default:
+ return current+2;
+ }
+}
+#endif /* 0 */
+
+/*
+ * netclear()
+ *
+ * We are about to do a TELNET SYNCH operation. Clear
+ * the path to the network.
+ *
+ * Things are a bit tricky since we may have sent the first
+ * byte or so of a previous TELNET command into the network.
+ * So, we have to scan the network buffer from the beginning
+ * until we are up to where we want to be.
+ *
+ * A side effect of what we do, just to keep things
+ * simple, is to clear the urgent data pointer. The principal
+ * caller should be setting the urgent data pointer AFTER calling
+ * us in any case.
+ */
+
+ static void
+netclear()
+{
+#if 0 /* XXX */
+ register char *thisitem, *next;
+ char *good;
+#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
+ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
+
+ thisitem = netobuf;
+
+ while ((next = nextitem(thisitem)) <= netobuf.send) {
+ thisitem = next;
+ }
+
+ /* Now, thisitem is first before/at boundary. */
+
+ good = netobuf; /* where the good bytes go */
+
+ while (netoring.add > thisitem) {
+ if (wewant(thisitem)) {
+ int length;
+
+ next = thisitem;
+ do {
+ next = nextitem(next);
+ } while (wewant(next) && (nfrontp > next));
+ length = next-thisitem;
+ memcpy(good, thisitem, length);
+ good += length;
+ thisitem = next;
+ } else {
+ thisitem = nextitem(thisitem);
+ }
+ }
+
+#endif /* 0 */
+}
+
+/*
+ * These routines add various telnet commands to the data stream.
+ */
+
+ static void
+doflush()
+{
+ NET2ADD(IAC, DO);
+ NETADD(TELOPT_TM);
+ flushline = 1;
+ flushout = 1;
+ (void) ttyflush(1); /* Flush/drop output */
+ /* do printoption AFTER flush, otherwise the output gets tossed... */
+ printoption("SENT", DO, TELOPT_TM);
+}
+
+ void
+xmitAO()
+{
+ NET2ADD(IAC, AO);
+ printoption("SENT", IAC, AO);
+ if (autoflush) {
+ doflush();
+ }
+}
+
+
+ void
+xmitEL()
+{
+ NET2ADD(IAC, EL);
+ printoption("SENT", IAC, EL);
+}
+
+ void
+xmitEC()
+{
+ NET2ADD(IAC, EC);
+ printoption("SENT", IAC, EC);
+}
+
+
+ int
+dosynch()
+{
+ netclear(); /* clear the path to the network */
+ NETADD(IAC);
+ setneturg();
+ NETADD(DM);
+ printoption("SENT", IAC, DM);
+ return 1;
+}
+
+int want_status_response = 0;
+
+ int
+get_status()
+{
+ unsigned char tmp[16];
+ register unsigned char *cp;
+
+ if (my_want_state_is_dont(TELOPT_STATUS)) {
+ printf("Remote side does not support STATUS option\n");
+ return 0;
+ }
+ cp = tmp;
+
+ *cp++ = IAC;
+ *cp++ = SB;
+ *cp++ = TELOPT_STATUS;
+ *cp++ = TELQUAL_SEND;
+ *cp++ = IAC;
+ *cp++ = SE;
+ if (NETROOM() >= cp - tmp) {
+ ring_supply_data(&netoring, tmp, cp-tmp);
+ printsub('>', tmp+2, cp - tmp - 2);
+ }
+ ++want_status_response;
+ return 1;
+}
+
+ void
+intp()
+{
+ NET2ADD(IAC, IP);
+ printoption("SENT", IAC, IP);
+ flushline = 1;
+ if (autoflush) {
+ doflush();
+ }
+ if (autosynch) {
+ dosynch();
+ }
+}
+
+ void
+sendbrk()
+{
+ NET2ADD(IAC, BREAK);
+ printoption("SENT", IAC, BREAK);
+ flushline = 1;
+ if (autoflush) {
+ doflush();
+ }
+ if (autosynch) {
+ dosynch();
+ }
+}
+
+ void
+sendabort()
+{
+ NET2ADD(IAC, ABORT);
+ printoption("SENT", IAC, ABORT);
+ flushline = 1;
+ if (autoflush) {
+ doflush();
+ }
+ if (autosynch) {
+ dosynch();
+ }
+}
+
+ void
+sendsusp()
+{
+ NET2ADD(IAC, SUSP);
+ printoption("SENT", IAC, SUSP);
+ flushline = 1;
+ if (autoflush) {
+ doflush();
+ }
+ if (autosynch) {
+ dosynch();
+ }
+}
+
+ void
+sendeof()
+{
+ NET2ADD(IAC, xEOF);
+ printoption("SENT", IAC, xEOF);
+}
+
+ void
+sendayt()
+{
+ NET2ADD(IAC, AYT);
+ printoption("SENT", IAC, AYT);
+}
+
+/*
+ * Send a window size update to the remote system.
+ */
+
+ void
+sendnaws()
+{
+ long rows, cols;
+ unsigned char tmp[16];
+ register unsigned char *cp;
+
+ if (my_state_is_wont(TELOPT_NAWS))
+ return;
+
+#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
+ if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
+
+ if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */
+ return;
+ }
+
+ cp = tmp;
+
+ *cp++ = IAC;
+ *cp++ = SB;
+ *cp++ = TELOPT_NAWS;
+ PUTSHORT(cp, cols);
+ PUTSHORT(cp, rows);
+ *cp++ = IAC;
+ *cp++ = SE;
+ if (NETROOM() >= cp - tmp) {
+ ring_supply_data(&netoring, tmp, cp-tmp);
+ printsub('>', tmp+2, cp - tmp - 2);
+ }
+}
+
+ void
+tel_enter_binary(rw)
+ int rw;
+{
+ if (rw&1)
+ send_do(TELOPT_BINARY, 1);
+ if (rw&2)
+ send_will(TELOPT_BINARY, 1);
+}
+
+ void
+tel_leave_binary(rw)
+ int rw;
+{
+ if (rw&1)
+ send_dont(TELOPT_BINARY, 1);
+ if (rw&2)
+ send_wont(TELOPT_BINARY, 1);
+}
diff --git a/usr.bin/telnet/terminal.c b/usr.bin/telnet/terminal.c
new file mode 100644
index 0000000..6e3cf69
--- /dev/null
+++ b/usr.bin/telnet/terminal.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)terminal.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <arpa/telnet.h>
+#include <sys/types.h>
+
+#include "ring.h"
+
+#include "externs.h"
+#include "types.h"
+
+Ring ttyoring, ttyiring;
+unsigned char ttyobuf[2*BUFSIZ], ttyibuf[BUFSIZ];
+
+int termdata; /* Debugging flag */
+
+#ifdef USE_TERMIO
+# ifndef VDISCARD
+cc_t termFlushChar;
+# endif
+# ifndef VLNEXT
+cc_t termLiteralNextChar;
+# endif
+# ifndef VSUSP
+cc_t termSuspChar;
+# endif
+# ifndef VWERASE
+cc_t termWerasChar;
+# endif
+# ifndef VREPRINT
+cc_t termRprntChar;
+# endif
+# ifndef VSTART
+cc_t termStartChar;
+# endif
+# ifndef VSTOP
+cc_t termStopChar;
+# endif
+# ifndef VEOL
+cc_t termForw1Char;
+# endif
+# ifndef VEOL2
+cc_t termForw2Char;
+# endif
+# ifndef VSTATUS
+cc_t termAytChar;
+# endif
+#else
+cc_t termForw2Char;
+cc_t termAytChar;
+#endif
+
+/*
+ * initialize the terminal data structures.
+ */
+
+ void
+init_terminal()
+{
+ if (ring_init(&ttyoring, ttyobuf, sizeof ttyobuf) != 1) {
+ exit(1);
+ }
+ if (ring_init(&ttyiring, ttyibuf, sizeof ttyibuf) != 1) {
+ exit(1);
+ }
+ autoflush = TerminalAutoFlush();
+}
+
+
+/*
+ * Send as much data as possible to the terminal.
+ *
+ * Return value:
+ * -1: No useful work done, data waiting to go out.
+ * 0: No data was waiting, so nothing was done.
+ * 1: All waiting data was written out.
+ * n: All data - n was written out.
+ */
+
+
+ int
+ttyflush(drop)
+ int drop;
+{
+ register int n, n0, n1;
+
+ n0 = ring_full_count(&ttyoring);
+ if ((n1 = n = ring_full_consecutive(&ttyoring)) > 0) {
+ if (drop) {
+ TerminalFlushOutput();
+ /* we leave 'n' alone! */
+ } else {
+ n = TerminalWrite(ttyoring.consume, n);
+ }
+ }
+ if (n > 0) {
+ if (termdata && n) {
+ Dump('>', ttyoring.consume, n);
+ }
+ /*
+ * If we wrote everything, and the full count is
+ * larger than what we wrote, then write the
+ * rest of the buffer.
+ */
+ if (n1 == n && n0 > n) {
+ n1 = n0 - n;
+ if (!drop)
+ n1 = TerminalWrite(ttyoring.bottom, n1);
+ n += n1;
+ }
+ ring_consumed(&ttyoring, n);
+ }
+ if (n < 0)
+ return -1;
+ if (n == n0) {
+ if (n0)
+ return -1;
+ return 0;
+ }
+ return n0 - n + 1;
+}
+
+
+/*
+ * These routines decides on what the mode should be (based on the values
+ * of various global variables).
+ */
+
+
+ int
+getconnmode()
+{
+ extern int linemode;
+ int mode = 0;
+#ifdef KLUDGELINEMODE
+ extern int kludgelinemode;
+#endif
+
+ if (In3270)
+ return(MODE_FLOW);
+
+ if (my_want_state_is_dont(TELOPT_ECHO))
+ mode |= MODE_ECHO;
+
+ if (localflow)
+ mode |= MODE_FLOW;
+
+ if (my_want_state_is_will(TELOPT_BINARY))
+ mode |= MODE_INBIN;
+
+ if (his_want_state_is_will(TELOPT_BINARY))
+ mode |= MODE_OUTBIN;
+
+#ifdef KLUDGELINEMODE
+ if (kludgelinemode) {
+ if (my_want_state_is_dont(TELOPT_SGA)) {
+ mode |= (MODE_TRAPSIG|MODE_EDIT);
+ if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
+ mode &= ~MODE_ECHO;
+ }
+ }
+ return(mode);
+ }
+#endif
+ if (my_want_state_is_will(TELOPT_LINEMODE))
+ mode |= linemode;
+ return(mode);
+}
+
+ void
+setconnmode(force)
+ int force;
+{
+ register int newmode;
+
+ newmode = getconnmode()|(force?MODE_FORCE:0);
+
+ TerminalNewMode(newmode);
+
+
+}
+
+
+ void
+setcommandmode()
+{
+ TerminalNewMode(-1);
+}
diff --git a/usr.bin/telnet/tn3270.c b/usr.bin/telnet/tn3270.c
new file mode 100644
index 0000000..1f285cf
--- /dev/null
+++ b/usr.bin/telnet/tn3270.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tn3270.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <arpa/telnet.h>
+
+#include "general.h"
+
+#include "defines.h"
+#include "ring.h"
+#include "externs.h"
+#include "fdset.h"
+
+#if defined(TN3270)
+
+#include "../ctlr/screen.h"
+#include "../general/globals.h"
+
+#include "../sys_curses/telextrn.h"
+#include "../ctlr/externs.h"
+
+#if defined(unix)
+int
+ HaveInput, /* There is input available to scan */
+ cursesdata, /* Do we dump curses data? */
+ sigiocount; /* Number of times we got a SIGIO */
+
+char tline[200];
+char *transcom = 0; /* transparent mode command (default: none) */
+#endif /* defined(unix) */
+
+char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
+
+static char sb_terminal[] = { IAC, SB,
+ TELOPT_TTYPE, TELQUAL_IS,
+ 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
+ IAC, SE };
+#define SBTERMMODEL 13
+
+static int
+ Sent3270TerminalType; /* Have we said we are a 3270? */
+
+#endif /* defined(TN3270) */
+
+
+ void
+init_3270()
+{
+#if defined(TN3270)
+#if defined(unix)
+ HaveInput = 0;
+ sigiocount = 0;
+#endif /* defined(unix) */
+ Sent3270TerminalType = 0;
+ Ifrontp = Ibackp = Ibuf;
+ init_ctlr(); /* Initialize some things */
+ init_keyboard();
+ init_screen();
+ init_system();
+#endif /* defined(TN3270) */
+}
+
+
+#if defined(TN3270)
+
+/*
+ * DataToNetwork - queue up some data to go to network. If "done" is set,
+ * then when last byte is queued, we add on an IAC EOR sequence (so,
+ * don't call us with "done" until you want that done...)
+ *
+ * We actually do send all the data to the network buffer, since our
+ * only client needs for us to do that.
+ */
+
+ int
+DataToNetwork(buffer, count, done)
+ register char *buffer; /* where the data is */
+ register int count; /* how much to send */
+ int done; /* is this the last of a logical block */
+{
+ register int loop, c;
+ int origCount;
+
+ origCount = count;
+
+ while (count) {
+ /* If not enough room for EORs, IACs, etc., wait */
+ if (NETROOM() < 6) {
+ fd_set o;
+
+ FD_ZERO(&o);
+ netflush();
+ while (NETROOM() < 6) {
+ FD_SET(net, &o);
+ (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
+ (struct timeval *) 0);
+ netflush();
+ }
+ }
+ c = ring_empty_count(&netoring);
+ if (c > count) {
+ c = count;
+ }
+ loop = c;
+ while (loop) {
+ if (((unsigned char)*buffer) == IAC) {
+ break;
+ }
+ buffer++;
+ loop--;
+ }
+ if ((c = c-loop)) {
+ ring_supply_data(&netoring, buffer-c, c);
+ count -= c;
+ }
+ if (loop) {
+ NET2ADD(IAC, IAC);
+ count--;
+ buffer++;
+ }
+ }
+
+ if (done) {
+ NET2ADD(IAC, EOR);
+ netflush(); /* try to move along as quickly as ... */
+ }
+ return(origCount - count);
+}
+
+
+#if defined(unix)
+ void
+inputAvailable(signo)
+ int signo;
+{
+ HaveInput = 1;
+ sigiocount++;
+}
+#endif /* defined(unix) */
+
+ void
+outputPurge()
+{
+ (void) ttyflush(1);
+}
+
+
+/*
+ * The following routines are places where the various tn3270
+ * routines make calls into telnet.c.
+ */
+
+/*
+ * DataToTerminal - queue up some data to go to terminal.
+ *
+ * Note: there are people who call us and depend on our processing
+ * *all* the data at one time (thus the select).
+ */
+
+ int
+DataToTerminal(buffer, count)
+ register char *buffer; /* where the data is */
+ register int count; /* how much to send */
+{
+ register int c;
+ int origCount;
+
+ origCount = count;
+
+ while (count) {
+ if (TTYROOM() == 0) {
+#if defined(unix)
+ fd_set o;
+
+ FD_ZERO(&o);
+#endif /* defined(unix) */
+ (void) ttyflush(0);
+ while (TTYROOM() == 0) {
+#if defined(unix)
+ FD_SET(tout, &o);
+ (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
+ (struct timeval *) 0);
+#endif /* defined(unix) */
+ (void) ttyflush(0);
+ }
+ }
+ c = TTYROOM();
+ if (c > count) {
+ c = count;
+ }
+ ring_supply_data(&ttyoring, buffer, c);
+ count -= c;
+ buffer += c;
+ }
+ return(origCount);
+}
+
+
+/*
+ * Push3270 - Try to send data along the 3270 output (to screen) direction.
+ */
+
+ int
+Push3270()
+{
+ int save = ring_full_count(&netiring);
+
+ if (save) {
+ if (Ifrontp+save > Ibuf+sizeof Ibuf) {
+ if (Ibackp != Ibuf) {
+ memcpy(Ibuf, Ibackp, Ifrontp-Ibackp);
+ Ifrontp -= (Ibackp-Ibuf);
+ Ibackp = Ibuf;
+ }
+ }
+ if (Ifrontp+save < Ibuf+sizeof Ibuf) {
+ (void)telrcv();
+ }
+ }
+ return save != ring_full_count(&netiring);
+}
+
+
+/*
+ * Finish3270 - get the last dregs of 3270 data out to the terminal
+ * before quitting.
+ */
+
+ void
+Finish3270()
+{
+ while (Push3270() || !DoTerminalOutput()) {
+#if defined(unix)
+ HaveInput = 0;
+#endif /* defined(unix) */
+ ;
+ }
+}
+
+
+/* StringToTerminal - output a null terminated string to the terminal */
+
+ void
+StringToTerminal(s)
+ char *s;
+{
+ int count;
+
+ count = strlen(s);
+ if (count) {
+ (void) DataToTerminal(s, count); /* we know it always goes... */
+ }
+}
+
+
+#if ((!defined(NOT43)) || defined(PUTCHAR))
+/* _putchar - output a single character to the terminal. This name is so that
+ * curses(3x) can call us to send out data.
+ */
+
+ void
+_putchar(c)
+ char c;
+{
+#if defined(sun) /* SunOS 4.0 bug */
+ c &= 0x7f;
+#endif /* defined(sun) */
+ if (cursesdata) {
+ Dump('>', &c, 1);
+ }
+ if (!TTYROOM()) {
+ (void) DataToTerminal(&c, 1);
+ } else {
+ TTYADD(c);
+ }
+}
+#endif /* ((!defined(NOT43)) || defined(PUTCHAR)) */
+
+ void
+SetIn3270()
+{
+ if (Sent3270TerminalType && my_want_state_is_will(TELOPT_BINARY)
+ && my_want_state_is_do(TELOPT_BINARY) && !donebinarytoggle) {
+ if (!In3270) {
+ In3270 = 1;
+ Init3270(); /* Initialize 3270 functions */
+ /* initialize terminal key mapping */
+ InitTerminal(); /* Start terminal going */
+ setconnmode(0);
+ }
+ } else {
+ if (In3270) {
+ StopScreen(1);
+ In3270 = 0;
+ Stop3270(); /* Tell 3270 we aren't here anymore */
+ setconnmode(0);
+ }
+ }
+}
+
+/*
+ * tn3270_ttype()
+ *
+ * Send a response to a terminal type negotiation.
+ *
+ * Return '0' if no more responses to send; '1' if a response sent.
+ */
+
+ int
+tn3270_ttype()
+{
+ /*
+ * Try to send a 3270 type terminal name. Decide which one based
+ * on the format of our screen, and (in the future) color
+ * capaiblities.
+ */
+ InitTerminal(); /* Sets MaxNumberColumns, MaxNumberLines */
+ if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
+ Sent3270TerminalType = 1;
+ if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
+ MaxNumberLines = 27;
+ MaxNumberColumns = 132;
+ sb_terminal[SBTERMMODEL] = '5';
+ } else if (MaxNumberLines >= 43) {
+ MaxNumberLines = 43;
+ MaxNumberColumns = 80;
+ sb_terminal[SBTERMMODEL] = '4';
+ } else if (MaxNumberLines >= 32) {
+ MaxNumberLines = 32;
+ MaxNumberColumns = 80;
+ sb_terminal[SBTERMMODEL] = '3';
+ } else {
+ MaxNumberLines = 24;
+ MaxNumberColumns = 80;
+ sb_terminal[SBTERMMODEL] = '2';
+ }
+ NumberLines = 24; /* before we start out... */
+ NumberColumns = 80;
+ ScreenSize = NumberLines*NumberColumns;
+ if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
+ ExitString("Programming error: MAXSCREENSIZE too small.\n",
+ 1);
+ /*NOTREACHED*/
+ }
+ printsub('>', sb_terminal+2, sizeof sb_terminal-2);
+ ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+#if defined(unix)
+ int
+settranscom(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (argc == 1 && transcom) {
+ transcom = 0;
+ }
+ if (argc == 1) {
+ return 1;
+ }
+ transcom = tline;
+ (void) strcpy(transcom, argv[1]);
+ for (i = 2; i < argc; ++i) {
+ (void) strcat(transcom, " ");
+ (void) strcat(transcom, argv[i]);
+ }
+ return 1;
+}
+#endif /* defined(unix) */
+
+#endif /* defined(TN3270) */
diff --git a/usr.bin/telnet/types.h b/usr.bin/telnet/types.h
new file mode 100644
index 0000000..191d311
--- /dev/null
+++ b/usr.bin/telnet/types.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)types.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef struct {
+ char *modedescriptions;
+ char modetype;
+} Modelist;
+
+extern Modelist modelist[];
+
+typedef struct {
+ int
+ system, /* what the current time is */
+ echotoggle, /* last time user entered echo character */
+ modenegotiated, /* last time operating mode negotiated */
+ didnetreceive, /* last time we read data from network */
+ gotDM; /* when did we last see a data mark */
+} Clocks;
+
+extern Clocks clocks;
diff --git a/usr.bin/telnet/utilities.c b/usr.bin/telnet/utilities.c
new file mode 100644
index 0000000..b645192
--- /dev/null
+++ b/usr.bin/telnet/utilities.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utilities.c 8.2 (Berkeley) 12/15/93";
+#endif /* not lint */
+
+#define TELOPTS
+#define TELCMDS
+#define SLC_NAMES
+#include <arpa/telnet.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+
+#include "general.h"
+
+#include "fdset.h"
+
+#include "ring.h"
+
+#include "defines.h"
+
+#include "externs.h"
+
+FILE *NetTrace = 0; /* Not in bss, since needs to stay */
+int prettydump;
+
+/*
+ * upcase()
+ *
+ * Upcase (in place) the argument.
+ */
+
+ void
+upcase(argument)
+ register char *argument;
+{
+ register int c;
+
+ while ((c = *argument) != 0) {
+ if (islower(c)) {
+ *argument = toupper(c);
+ }
+ argument++;
+ }
+}
+
+/*
+ * SetSockOpt()
+ *
+ * Compensate for differences in 4.2 and 4.3 systems.
+ */
+
+ int
+SetSockOpt(fd, level, option, yesno)
+ int fd, level, option, yesno;
+{
+#ifndef NOT43
+ return setsockopt(fd, level, option,
+ (char *)&yesno, sizeof yesno);
+#else /* NOT43 */
+ if (yesno == 0) { /* Can't do that in 4.2! */
+ fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
+ option);
+ return -1;
+ }
+ return setsockopt(fd, level, option, 0, 0);
+#endif /* NOT43 */
+}
+
+/*
+ * The following are routines used to print out debugging information.
+ */
+
+unsigned char NetTraceFile[256] = "(standard output)";
+
+ void
+SetNetTrace(file)
+ register char *file;
+{
+ if (NetTrace && NetTrace != stdout)
+ fclose(NetTrace);
+ if (file && (strcmp(file, "-") != 0)) {
+ NetTrace = fopen(file, "w");
+ if (NetTrace) {
+ strcpy((char *)NetTraceFile, file);
+ return;
+ }
+ fprintf(stderr, "Cannot open %s.\n", file);
+ }
+ NetTrace = stdout;
+ strcpy((char *)NetTraceFile, "(standard output)");
+}
+
+ void
+Dump(direction, buffer, length)
+ char direction;
+ unsigned char *buffer;
+ int length;
+{
+# define BYTES_PER_LINE 32
+# define min(x,y) ((x<y)? x:y)
+ unsigned char *pThis;
+ int offset;
+ extern pettydump;
+
+ offset = 0;
+
+ while (length) {
+ /* print one line */
+ fprintf(NetTrace, "%c 0x%x\t", direction, offset);
+ pThis = buffer;
+ if (prettydump) {
+ buffer = buffer + min(length, BYTES_PER_LINE/2);
+ while (pThis < buffer) {
+ fprintf(NetTrace, "%c%.2x",
+ (((*pThis)&0xff) == 0xff) ? '*' : ' ',
+ (*pThis)&0xff);
+ pThis++;
+ }
+ length -= BYTES_PER_LINE/2;
+ offset += BYTES_PER_LINE/2;
+ } else {
+ buffer = buffer + min(length, BYTES_PER_LINE);
+ while (pThis < buffer) {
+ fprintf(NetTrace, "%.2x", (*pThis)&0xff);
+ pThis++;
+ }
+ length -= BYTES_PER_LINE;
+ offset += BYTES_PER_LINE;
+ }
+ if (NetTrace == stdout) {
+ fprintf(NetTrace, "\r\n");
+ } else {
+ fprintf(NetTrace, "\n");
+ }
+ if (length < 0) {
+ fflush(NetTrace);
+ return;
+ }
+ /* find next unique line */
+ }
+ fflush(NetTrace);
+}
+
+
+ void
+printoption(direction, cmd, option)
+ char *direction;
+ int cmd, option;
+{
+ if (!showoptions)
+ return;
+ if (cmd == IAC) {
+ if (TELCMD_OK(option))
+ fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option));
+ else
+ fprintf(NetTrace, "%s IAC %d", direction, option);
+ } else {
+ register char *fmt;
+ fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
+ (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
+ if (fmt) {
+ fprintf(NetTrace, "%s %s ", direction, fmt);
+ if (TELOPT_OK(option))
+ fprintf(NetTrace, "%s", TELOPT(option));
+ else if (option == TELOPT_EXOPL)
+ fprintf(NetTrace, "EXOPL");
+ else
+ fprintf(NetTrace, "%d", option);
+ } else
+ fprintf(NetTrace, "%s %d %d", direction, cmd, option);
+ }
+ if (NetTrace == stdout) {
+ fprintf(NetTrace, "\r\n");
+ fflush(NetTrace);
+ } else {
+ fprintf(NetTrace, "\n");
+ }
+ return;
+}
+
+ void
+optionstatus()
+{
+ register int i;
+ extern char will_wont_resp[], do_dont_resp[];
+
+ for (i = 0; i < 256; i++) {
+ if (do_dont_resp[i]) {
+ if (TELOPT_OK(i))
+ printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]);
+ else if (TELCMD_OK(i))
+ printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]);
+ else
+ printf("resp DO_DONT %d: %d\n", i,
+ do_dont_resp[i]);
+ if (my_want_state_is_do(i)) {
+ if (TELOPT_OK(i))
+ printf("want DO %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf("want DO %s\n", TELCMD(i));
+ else
+ printf("want DO %d\n", i);
+ } else {
+ if (TELOPT_OK(i))
+ printf("want DONT %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf("want DONT %s\n", TELCMD(i));
+ else
+ printf("want DONT %d\n", i);
+ }
+ } else {
+ if (my_state_is_do(i)) {
+ if (TELOPT_OK(i))
+ printf(" DO %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf(" DO %s\n", TELCMD(i));
+ else
+ printf(" DO %d\n", i);
+ }
+ }
+ if (will_wont_resp[i]) {
+ if (TELOPT_OK(i))
+ printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]);
+ else if (TELCMD_OK(i))
+ printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]);
+ else
+ printf("resp WILL_WONT %d: %d\n",
+ i, will_wont_resp[i]);
+ if (my_want_state_is_will(i)) {
+ if (TELOPT_OK(i))
+ printf("want WILL %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf("want WILL %s\n", TELCMD(i));
+ else
+ printf("want WILL %d\n", i);
+ } else {
+ if (TELOPT_OK(i))
+ printf("want WONT %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf("want WONT %s\n", TELCMD(i));
+ else
+ printf("want WONT %d\n", i);
+ }
+ } else {
+ if (my_state_is_will(i)) {
+ if (TELOPT_OK(i))
+ printf(" WILL %s\n", TELOPT(i));
+ else if (TELCMD_OK(i))
+ printf(" WILL %s\n", TELCMD(i));
+ else
+ printf(" WILL %d\n", i);
+ }
+ }
+ }
+
+}
+
+ void
+printsub(direction, pointer, length)
+ char direction; /* '<' or '>' */
+ unsigned char *pointer; /* where suboption data sits */
+ int length; /* length of suboption data */
+{
+ register int i;
+ char buf[512];
+ extern int want_status_response;
+
+ if (showoptions || direction == 0 ||
+ (want_status_response && (pointer[0] == TELOPT_STATUS))) {
+ if (direction) {
+ fprintf(NetTrace, "%s IAC SB ",
+ (direction == '<')? "RCVD":"SENT");
+ if (length >= 3) {
+ register int j;
+
+ i = pointer[length-2];
+ j = pointer[length-1];
+
+ if (i != IAC || j != SE) {
+ fprintf(NetTrace, "(terminated by ");
+ if (TELOPT_OK(i))
+ fprintf(NetTrace, "%s ", TELOPT(i));
+ else if (TELCMD_OK(i))
+ fprintf(NetTrace, "%s ", TELCMD(i));
+ else
+ fprintf(NetTrace, "%d ", i);
+ if (TELOPT_OK(j))
+ fprintf(NetTrace, "%s", TELOPT(j));
+ else if (TELCMD_OK(j))
+ fprintf(NetTrace, "%s", TELCMD(j));
+ else
+ fprintf(NetTrace, "%d", j);
+ fprintf(NetTrace, ", not IAC SE!) ");
+ }
+ }
+ length -= 2;
+ }
+ if (length < 1) {
+ fprintf(NetTrace, "(Empty suboption??\?)");
+ if (NetTrace == stdout)
+ fflush(NetTrace);
+ return;
+ }
+ switch (pointer[0]) {
+ case TELOPT_TTYPE:
+ fprintf(NetTrace, "TERMINAL-TYPE ");
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ fprintf(NetTrace, "SEND");
+ break;
+ default:
+ fprintf(NetTrace,
+ "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ break;
+ case TELOPT_TSPEED:
+ fprintf(NetTrace, "TERMINAL-SPEED");
+ if (length < 2) {
+ fprintf(NetTrace, " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ fprintf(NetTrace, " IS ");
+ fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2);
+ break;
+ default:
+ if (pointer[1] == 1)
+ fprintf(NetTrace, " SEND");
+ else
+ fprintf(NetTrace, " %d (unknown)", pointer[1]);
+ for (i = 2; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+ }
+ break;
+
+ case TELOPT_LFLOW:
+ fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
+ if (length < 2) {
+ fprintf(NetTrace, " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case LFLOW_OFF:
+ fprintf(NetTrace, " OFF"); break;
+ case LFLOW_ON:
+ fprintf(NetTrace, " ON"); break;
+ case LFLOW_RESTART_ANY:
+ fprintf(NetTrace, " RESTART-ANY"); break;
+ case LFLOW_RESTART_XON:
+ fprintf(NetTrace, " RESTART-XON"); break;
+ default:
+ fprintf(NetTrace, " %d (unknown)", pointer[1]);
+ }
+ for (i = 2; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+
+ case TELOPT_NAWS:
+ fprintf(NetTrace, "NAWS");
+ if (length < 2) {
+ fprintf(NetTrace, " (empty suboption??\?)");
+ break;
+ }
+ if (length == 2) {
+ fprintf(NetTrace, " ?%d?", pointer[1]);
+ break;
+ }
+ fprintf(NetTrace, " %d %d (%d)",
+ pointer[1], pointer[2],
+ (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
+ if (length == 4) {
+ fprintf(NetTrace, " ?%d?", pointer[3]);
+ break;
+ }
+ fprintf(NetTrace, " %d %d (%d)",
+ pointer[3], pointer[4],
+ (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
+ for (i = 5; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+
+#if defined(AUTHENTICATION)
+ case TELOPT_AUTHENTICATION:
+ fprintf(NetTrace, "AUTHENTICATION");
+ if (length < 2) {
+ fprintf(NetTrace, " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case TELQUAL_REPLY:
+ case TELQUAL_IS:
+ fprintf(NetTrace, " %s ", (pointer[1] == TELQUAL_IS) ?
+ "IS" : "REPLY");
+ if (AUTHTYPE_NAME_OK(pointer[2]))
+ fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[2]));
+ else
+ fprintf(NetTrace, "%d ", pointer[2]);
+ if (length < 3) {
+ fprintf(NetTrace, "(partial suboption??\?)");
+ break;
+ }
+ fprintf(NetTrace, "%s|%s",
+ ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+ "CLIENT" : "SERVER",
+ ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+ "MUTUAL" : "ONE-WAY");
+
+ auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
+ fprintf(NetTrace, "%s", buf);
+ break;
+
+ case TELQUAL_SEND:
+ i = 2;
+ fprintf(NetTrace, " SEND ");
+ while (i < length) {
+ if (AUTHTYPE_NAME_OK(pointer[i]))
+ fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[i]));
+ else
+ fprintf(NetTrace, "%d ", pointer[i]);
+ if (++i >= length) {
+ fprintf(NetTrace, "(partial suboption??\?)");
+ break;
+ }
+ fprintf(NetTrace, "%s|%s ",
+ ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
+ "CLIENT" : "SERVER",
+ ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
+ "MUTUAL" : "ONE-WAY");
+ ++i;
+ }
+ break;
+
+ case TELQUAL_NAME:
+ i = 2;
+ fprintf(NetTrace, " NAME \"");
+ while (i < length)
+ putc(pointer[i++], NetTrace);
+ putc('"', NetTrace);
+ break;
+
+ default:
+ for (i = 2; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+ }
+ break;
+#endif
+
+
+ case TELOPT_LINEMODE:
+ fprintf(NetTrace, "LINEMODE ");
+ if (length < 2) {
+ fprintf(NetTrace, " (empty suboption??\?)");
+ break;
+ }
+ switch (pointer[1]) {
+ case WILL:
+ fprintf(NetTrace, "WILL ");
+ goto common;
+ case WONT:
+ fprintf(NetTrace, "WONT ");
+ goto common;
+ case DO:
+ fprintf(NetTrace, "DO ");
+ goto common;
+ case DONT:
+ fprintf(NetTrace, "DONT ");
+ common:
+ if (length < 3) {
+ fprintf(NetTrace, "(no option??\?)");
+ break;
+ }
+ switch (pointer[2]) {
+ case LM_FORWARDMASK:
+ fprintf(NetTrace, "Forward Mask");
+ for (i = 3; i < length; i++)
+ fprintf(NetTrace, " %x", pointer[i]);
+ break;
+ default:
+ fprintf(NetTrace, "%d (unknown)", pointer[2]);
+ for (i = 3; i < length; i++)
+ fprintf(NetTrace, " %d", pointer[i]);
+ break;
+ }
+ break;
+
+ case LM_SLC:
+ fprintf(NetTrace, "SLC");
+ for (i = 2; i < length - 2; i += 3) {
+ if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
+ fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
+ else
+ fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]);
+ switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
+ case SLC_NOSUPPORT:
+ fprintf(NetTrace, " NOSUPPORT"); break;
+ case SLC_CANTCHANGE:
+ fprintf(NetTrace, " CANTCHANGE"); break;
+ case SLC_VARIABLE:
+ fprintf(NetTrace, " VARIABLE"); break;
+ case SLC_DEFAULT:
+ fprintf(NetTrace, " DEFAULT"); break;
+ }
+ fprintf(NetTrace, "%s%s%s",
+ pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
+ pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
+ if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
+ SLC_FLUSHOUT| SLC_LEVELBITS))
+ fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]);
+ fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]);
+ if ((pointer[i+SLC_VALUE] == IAC) &&
+ (pointer[i+SLC_VALUE+1] == IAC))
+ i++;
+ }
+ for (; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+
+ case LM_MODE:
+ fprintf(NetTrace, "MODE ");
+ if (length < 3) {
+ fprintf(NetTrace, "(no mode??\?)");
+ break;
+ }
+ {
+ char tbuf[64];
+ sprintf(tbuf, "%s%s%s%s%s",
+ pointer[2]&MODE_EDIT ? "|EDIT" : "",
+ pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
+ pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
+ pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
+ pointer[2]&MODE_ACK ? "|ACK" : "");
+ fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0");
+ }
+ if (pointer[2]&~(MODE_MASK))
+ fprintf(NetTrace, " (0x%x)", pointer[2]);
+ for (i = 3; i < length; i++)
+ fprintf(NetTrace, " ?0x%x?", pointer[i]);
+ break;
+ default:
+ fprintf(NetTrace, "%d (unknown)", pointer[1]);
+ for (i = 2; i < length; i++)
+ fprintf(NetTrace, " %d", pointer[i]);
+ }
+ break;
+
+ case TELOPT_STATUS: {
+ register char *cp;
+ register int j, k;
+
+ fprintf(NetTrace, "STATUS");
+
+ switch (pointer[1]) {
+ default:
+ if (pointer[1] == TELQUAL_SEND)
+ fprintf(NetTrace, " SEND");
+ else
+ fprintf(NetTrace, " %d (unknown)", pointer[1]);
+ for (i = 2; i < length; i++)
+ fprintf(NetTrace, " ?%d?", pointer[i]);
+ break;
+ case TELQUAL_IS:
+ if (--want_status_response < 0)
+ want_status_response = 0;
+ if (NetTrace == stdout)
+ fprintf(NetTrace, " IS\r\n");
+ else
+ fprintf(NetTrace, " IS\n");
+
+ for (i = 2; i < length; i++) {
+ switch(pointer[i]) {
+ case DO: cp = "DO"; goto common2;
+ case DONT: cp = "DONT"; goto common2;
+ case WILL: cp = "WILL"; goto common2;
+ case WONT: cp = "WONT"; goto common2;
+ common2:
+ i++;
+ if (TELOPT_OK((int)pointer[i]))
+ fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i]));
+ else
+ fprintf(NetTrace, " %s %d", cp, pointer[i]);
+
+ if (NetTrace == stdout)
+ fprintf(NetTrace, "\r\n");
+ else
+ fprintf(NetTrace, "\n");
+ break;
+
+ case SB:
+ fprintf(NetTrace, " SB ");
+ i++;
+ j = k = i;
+ while (j < length) {
+ if (pointer[j] == SE) {
+ if (j+1 == length)
+ break;
+ if (pointer[j+1] == SE)
+ j++;
+ else
+ break;
+ }
+ pointer[k++] = pointer[j++];
+ }
+ printsub(0, &pointer[i], k - i);
+ if (i < length) {
+ fprintf(NetTrace, " SE");
+ i = j;
+ } else
+ i = j - 1;
+
+ if (NetTrace == stdout)
+ fprintf(NetTrace, "\r\n");
+ else
+ fprintf(NetTrace, "\n");
+
+ break;
+
+ default:
+ fprintf(NetTrace, " %d", pointer[i]);
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ case TELOPT_XDISPLOC:
+ fprintf(NetTrace, "X-DISPLAY-LOCATION ");
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
+ break;
+ case TELQUAL_SEND:
+ fprintf(NetTrace, "SEND");
+ break;
+ default:
+ fprintf(NetTrace, "- unknown qualifier %d (0x%x).",
+ pointer[1], pointer[1]);
+ }
+ break;
+
+ case TELOPT_NEW_ENVIRON:
+ fprintf(NetTrace, "NEW-ENVIRON ");
+#ifdef OLD_ENVIRON
+ goto env_common1;
+ case TELOPT_OLD_ENVIRON:
+ fprintf(NetTrace, "OLD-ENVIRON");
+ env_common1:
+#endif
+ switch (pointer[1]) {
+ case TELQUAL_IS:
+ fprintf(NetTrace, "IS ");
+ goto env_common;
+ case TELQUAL_SEND:
+ fprintf(NetTrace, "SEND ");
+ goto env_common;
+ case TELQUAL_INFO:
+ fprintf(NetTrace, "INFO ");
+ env_common:
+ {
+ register int noquote = 2;
+#if defined(ENV_HACK) && defined(OLD_ENVIRON)
+ extern int old_env_var, old_env_value;
+#endif
+ for (i = 2; i < length; i++ ) {
+ switch (pointer[i]) {
+ case NEW_ENV_VALUE:
+#ifdef OLD_ENVIRON
+ /* case NEW_ENV_OVAR: */
+ if (pointer[0] == TELOPT_OLD_ENVIRON) {
+# ifdef ENV_HACK
+ if (old_env_var == OLD_ENV_VALUE)
+ fprintf(NetTrace, "\" (VALUE) " + noquote);
+ else
+# endif
+ fprintf(NetTrace, "\" VAR " + noquote);
+ } else
+#endif /* OLD_ENVIRON */
+ fprintf(NetTrace, "\" VALUE " + noquote);
+ noquote = 2;
+ break;
+
+ case NEW_ENV_VAR:
+#ifdef OLD_ENVIRON
+ /* case OLD_ENV_VALUE: */
+ if (pointer[0] == TELOPT_OLD_ENVIRON) {
+# ifdef ENV_HACK
+ if (old_env_value == OLD_ENV_VAR)
+ fprintf(NetTrace, "\" (VAR) " + noquote);
+ else
+# endif
+ fprintf(NetTrace, "\" VALUE " + noquote);
+ } else
+#endif /* OLD_ENVIRON */
+ fprintf(NetTrace, "\" VAR " + noquote);
+ noquote = 2;
+ break;
+
+ case ENV_ESC:
+ fprintf(NetTrace, "\" ESC " + noquote);
+ noquote = 2;
+ break;
+
+ case ENV_USERVAR:
+ fprintf(NetTrace, "\" USERVAR " + noquote);
+ noquote = 2;
+ break;
+
+ default:
+ def_case:
+ if (isprint(pointer[i]) && pointer[i] != '"') {
+ if (noquote) {
+ putc('"', NetTrace);
+ noquote = 0;
+ }
+ putc(pointer[i], NetTrace);
+ } else {
+ fprintf(NetTrace, "\" %03o " + noquote,
+ pointer[i]);
+ noquote = 2;
+ }
+ break;
+ }
+ }
+ if (!noquote)
+ putc('"', NetTrace);
+ break;
+ }
+ }
+ break;
+
+ default:
+ if (TELOPT_OK(pointer[0]))
+ fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
+ else
+ fprintf(NetTrace, "%d (unknown)", pointer[0]);
+ for (i = 1; i < length; i++)
+ fprintf(NetTrace, " %d", pointer[i]);
+ break;
+ }
+ if (direction) {
+ if (NetTrace == stdout)
+ fprintf(NetTrace, "\r\n");
+ else
+ fprintf(NetTrace, "\n");
+ }
+ if (NetTrace == stdout)
+ fflush(NetTrace);
+ }
+}
+
+/* EmptyTerminal - called to make sure that the terminal buffer is empty.
+ * Note that we consider the buffer to run all the
+ * way to the kernel (thus the select).
+ */
+
+ void
+EmptyTerminal()
+{
+#if defined(unix)
+ fd_set o;
+
+ FD_ZERO(&o);
+#endif /* defined(unix) */
+
+ if (TTYBYTES() == 0) {
+#if defined(unix)
+ FD_SET(tout, &o);
+ (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
+ (struct timeval *) 0); /* wait for TTLOWAT */
+#endif /* defined(unix) */
+ } else {
+ while (TTYBYTES()) {
+ (void) ttyflush(0);
+#if defined(unix)
+ FD_SET(tout, &o);
+ (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
+ (struct timeval *) 0); /* wait for TTLOWAT */
+#endif /* defined(unix) */
+ }
+ }
+}
+
+ void
+SetForExit()
+{
+ setconnmode(0);
+#if defined(TN3270)
+ if (In3270) {
+ Finish3270();
+ }
+#else /* defined(TN3270) */
+ do {
+ (void)telrcv(); /* Process any incoming data */
+ EmptyTerminal();
+ } while (ring_full_count(&netiring)); /* While there is any */
+#endif /* defined(TN3270) */
+ setcommandmode();
+ fflush(stdout);
+ fflush(stderr);
+#if defined(TN3270)
+ if (In3270) {
+ StopScreen(1);
+ }
+#endif /* defined(TN3270) */
+ setconnmode(0);
+ EmptyTerminal(); /* Flush the path to the tty */
+ setcommandmode();
+}
+
+ void
+Exit(returnCode)
+ int returnCode;
+{
+ SetForExit();
+ exit(returnCode);
+}
+
+ void
+ExitString(string, returnCode)
+ char *string;
+ int returnCode;
+{
+ SetForExit();
+ fwrite(string, 1, strlen(string), stderr);
+ exit(returnCode);
+}
diff --git a/usr.bin/tftp/Makefile b/usr.bin/tftp/Makefile
new file mode 100644
index 0000000..d91ba61
--- /dev/null
+++ b/usr.bin/tftp/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tftp
+SRCS= main.c tftp.c tftpsubs.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tftp/extern.h b/usr.bin/tftp/extern.h
new file mode 100644
index 0000000..9174cb1
--- /dev/null
+++ b/usr.bin/tftp/extern.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+void recvfile __P((int, char *, char *));
+void sendfile __P((int, char *, char *));
diff --git a/usr.bin/tftp/main.c b/usr.bin/tftp/main.c
new file mode 100644
index 0000000..c69195d
--- /dev/null
+++ b/usr.bin/tftp/main.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
+
+/*
+ * TFTP User Program -- Command Interface.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define TIMEOUT 5 /* secs between rexmt's */
+
+struct sockaddr_in peeraddr;
+int f;
+short port;
+int trace;
+int verbose;
+int connected;
+char mode[32];
+char line[200];
+int margc;
+char *margv[20];
+char *prompt = "tftp";
+jmp_buf toplevel;
+void intr();
+struct servent *sp;
+
+void get __P((int, char **));
+void help __P((int, char **));
+void modecmd __P((int, char **));
+void put __P((int, char **));
+void quit __P((int, char **));
+void setascii __P((int, char **));
+void setbinary __P((int, char **));
+void setpeer __P((int, char **));
+void setrexmt __P((int, char **));
+void settimeout __P((int, char **));
+void settrace __P((int, char **));
+void setverbose __P((int, char **));
+void status __P((int, char **));
+
+static __dead void command __P((void));
+
+static void getusage __P((char *));
+static void makeargv __P((void));
+static void putusage __P((char *));
+static void settftpmode __P((char *));
+
+#define HELPINDENT (sizeof("connect"))
+
+struct cmd {
+ char *name;
+ char *help;
+ void (*handler) __P((int, char **));
+};
+
+char vhelp[] = "toggle verbose mode";
+char thelp[] = "toggle packet tracing";
+char chelp[] = "connect to remote tftp";
+char qhelp[] = "exit tftp";
+char hhelp[] = "print help information";
+char shelp[] = "send file";
+char rhelp[] = "receive file";
+char mhelp[] = "set file transfer mode";
+char sthelp[] = "show current status";
+char xhelp[] = "set per-packet retransmission timeout";
+char ihelp[] = "set total retransmission timeout";
+char ashelp[] = "set mode to netascii";
+char bnhelp[] = "set mode to octet";
+
+struct cmd cmdtab[] = {
+ { "connect", chelp, setpeer },
+ { "mode", mhelp, modecmd },
+ { "put", shelp, put },
+ { "get", rhelp, get },
+ { "quit", qhelp, quit },
+ { "verbose", vhelp, setverbose },
+ { "trace", thelp, settrace },
+ { "status", sthelp, status },
+ { "binary", bnhelp, setbinary },
+ { "ascii", ashelp, setascii },
+ { "rexmt", xhelp, setrexmt },
+ { "timeout", ihelp, settimeout },
+ { "?", hhelp, help },
+ { 0 }
+};
+
+struct cmd *getcmd();
+char *tail();
+char *index();
+char *rindex();
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sockaddr_in sin;
+
+ sp = getservbyname("tftp", "udp");
+ if (sp == 0) {
+ fprintf(stderr, "tftp: udp/tftp: unknown service\n");
+ exit(1);
+ }
+ f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f < 0) {
+ perror("tftp: socket");
+ exit(3);
+ }
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("tftp: bind");
+ exit(1);
+ }
+ strcpy(mode, "netascii");
+ signal(SIGINT, intr);
+ if (argc > 1) {
+ if (setjmp(toplevel) != 0)
+ exit(0);
+ setpeer(argc, argv);
+ }
+ if (setjmp(toplevel) != 0)
+ (void)putchar('\n');
+ command();
+}
+
+char hostname[100];
+
+void
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct hostent *host;
+
+ if (argc < 2) {
+ strcpy(line, "Connect ");
+ printf("(to) ");
+ gets(&line[strlen(line)]);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ return;
+ }
+ host = gethostbyname(argv[1]);
+ if (host) {
+ peeraddr.sin_family = host->h_addrtype;
+ bcopy(host->h_addr, &peeraddr.sin_addr, host->h_length);
+ strcpy(hostname, host->h_name);
+ } else {
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_addr.s_addr = inet_addr(argv[1]);
+ if (peeraddr.sin_addr.s_addr == -1) {
+ connected = 0;
+ printf("%s: unknown host\n", argv[1]);
+ return;
+ }
+ strcpy(hostname, argv[1]);
+ }
+ port = sp->s_port;
+ if (argc == 3) {
+ port = atoi(argv[2]);
+ if (port < 0) {
+ printf("%s: bad port number\n", argv[2]);
+ connected = 0;
+ return;
+ }
+ port = htons(port);
+ }
+ connected = 1;
+}
+
+struct modes {
+ char *m_name;
+ char *m_mode;
+} modes[] = {
+ { "ascii", "netascii" },
+ { "netascii", "netascii" },
+ { "binary", "octet" },
+ { "image", "octet" },
+ { "octet", "octet" },
+/* { "mail", "mail" }, */
+ { 0, 0 }
+};
+
+void
+modecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct modes *p;
+ char *sep;
+
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", mode);
+ return;
+ }
+ if (argc == 2) {
+ for (p = modes; p->m_name; p++)
+ if (strcmp(argv[1], p->m_name) == 0)
+ break;
+ if (p->m_name) {
+ settftpmode(p->m_mode);
+ return;
+ }
+ printf("%s: unknown mode\n", argv[1]);
+ /* drop through and print usage message */
+ }
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = modes; p->m_name; p++) {
+ printf("%s%s", sep, p->m_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ printf(" ]\n");
+ return;
+}
+
+void
+setbinary(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ settftpmode("octet");
+}
+
+void
+setascii(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ settftpmode("netascii");
+}
+
+static void
+settftpmode(newmode)
+ char *newmode;
+{
+ strcpy(mode, newmode);
+ if (verbose)
+ printf("mode set to %s\n", mode);
+}
+
+
+/*
+ * Send file(s).
+ */
+void
+put(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ register int n;
+ register char *cp, *targ;
+
+ if (argc < 2) {
+ strcpy(line, "send ");
+ printf("(file) ");
+ gets(&line[strlen(line)]);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ putusage(argv[0]);
+ return;
+ }
+ targ = argv[argc - 1];
+ if (index(argv[argc - 1], ':')) {
+ char *cp;
+ struct hostent *hp;
+
+ for (n = 1; n < argc - 1; n++)
+ if (index(argv[n], ':')) {
+ putusage(argv[0]);
+ return;
+ }
+ cp = argv[argc - 1];
+ targ = index(cp, ':');
+ *targ++ = 0;
+ hp = gethostbyname(cp);
+ if (hp == NULL) {
+ fprintf(stderr, "tftp: %s: ", cp);
+ herror((char *)NULL);
+ return;
+ }
+ bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr, hp->h_length);
+ peeraddr.sin_family = hp->h_addrtype;
+ connected = 1;
+ strcpy(hostname, hp->h_name);
+ }
+ if (!connected) {
+ printf("No target machine specified.\n");
+ return;
+ }
+ if (argc < 4) {
+ cp = argc == 2 ? tail(targ) : argv[1];
+ fd = open(cp, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ cp, hostname, targ, mode);
+ peeraddr.sin_port = port;
+ sendfile(fd, targ, mode);
+ return;
+ }
+ /* this assumes the target is a directory */
+ /* on a remote unix system. hmmmm. */
+ cp = index(targ, '\0');
+ *cp++ = '/';
+ for (n = 1; n < argc - 1; n++) {
+ strcpy(cp, tail(argv[n]));
+ fd = open(argv[n], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(argv[n]);
+ continue;
+ }
+ if (verbose)
+ printf("putting %s to %s:%s [%s]\n",
+ argv[n], hostname, targ, mode);
+ peeraddr.sin_port = port;
+ sendfile(fd, targ, mode);
+ }
+}
+
+static void
+putusage(s)
+ char *s;
+{
+ printf("usage: %s file ... host:target, or\n", s);
+ printf(" %s file ... target (when already connected)\n", s);
+}
+
+/*
+ * Receive file(s).
+ */
+void
+get(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd;
+ register int n;
+ register char *cp;
+ char *src;
+
+ if (argc < 2) {
+ strcpy(line, "get ");
+ printf("(files) ");
+ gets(&line[strlen(line)]);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc < 2) {
+ getusage(argv[0]);
+ return;
+ }
+ if (!connected) {
+ for (n = 1; n < argc ; n++)
+ if (index(argv[n], ':') == 0) {
+ getusage(argv[0]);
+ return;
+ }
+ }
+ for (n = 1; n < argc ; n++) {
+ src = index(argv[n], ':');
+ if (src == NULL)
+ src = argv[n];
+ else {
+ struct hostent *hp;
+
+ *src++ = 0;
+ hp = gethostbyname(argv[n]);
+ if (hp == NULL) {
+ fprintf(stderr, "tftp: %s: ", argv[n]);
+ herror((char *)NULL);
+ continue;
+ }
+ bcopy(hp->h_addr, (caddr_t)&peeraddr.sin_addr,
+ hp->h_length);
+ peeraddr.sin_family = hp->h_addrtype;
+ connected = 1;
+ strcpy(hostname, hp->h_name);
+ }
+ if (argc < 4) {
+ cp = argc == 3 ? argv[2] : tail(src);
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ return;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ peeraddr.sin_port = port;
+ recvfile(fd, src, mode);
+ break;
+ }
+ cp = tail(src); /* new .. jdg */
+ fd = creat(cp, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "tftp: "); perror(cp);
+ continue;
+ }
+ if (verbose)
+ printf("getting from %s:%s to %s [%s]\n",
+ hostname, src, cp, mode);
+ peeraddr.sin_port = port;
+ recvfile(fd, src, mode);
+ }
+}
+
+static void
+getusage(s)
+ char *s;
+{
+ printf("usage: %s host:file host:file ... file, or\n", s);
+ printf(" %s file file ... file if connected\n", s);
+}
+
+int rexmtval = TIMEOUT;
+
+void
+setrexmt(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int t;
+
+ if (argc < 2) {
+ strcpy(line, "Rexmt-timeout ");
+ printf("(value) ");
+ gets(&line[strlen(line)]);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%s: bad value\n", argv[1]);
+ else
+ rexmtval = t;
+}
+
+int maxtimeout = 5 * TIMEOUT;
+
+void
+settimeout(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int t;
+
+ if (argc < 2) {
+ strcpy(line, "Maximum-timeout ");
+ printf("(value) ");
+ gets(&line[strlen(line)]);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (argc != 2) {
+ printf("usage: %s value\n", argv[0]);
+ return;
+ }
+ t = atoi(argv[1]);
+ if (t < 0)
+ printf("%s: bad value\n", argv[1]);
+ else
+ maxtimeout = t;
+}
+
+void
+status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (connected)
+ printf("Connected to %s.\n", hostname);
+ else
+ printf("Not connected.\n");
+ printf("Mode: %s Verbose: %s Tracing: %s\n", mode,
+ verbose ? "on" : "off", trace ? "on" : "off");
+ printf("Rexmt-interval: %d seconds, Max-timeout: %d seconds\n",
+ rexmtval, maxtimeout);
+}
+
+void
+intr()
+{
+
+ signal(SIGALRM, SIG_IGN);
+ alarm(0);
+ longjmp(toplevel, -1);
+}
+
+char *
+tail(filename)
+ char *filename;
+{
+ register char *s;
+
+ while (*filename) {
+ s = rindex(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ *s = '\0';
+ }
+ return (filename);
+}
+
+/*
+ * Command parser.
+ */
+static __dead void
+command()
+{
+ register struct cmd *c;
+
+ for (;;) {
+ printf("%s> ", prompt);
+ if (gets(line) == 0) {
+ if (feof(stdin)) {
+ exit(0);
+ } else {
+ continue;
+ }
+ }
+ if (line[0] == 0)
+ continue;
+ makeargv();
+ if (margc == 0)
+ continue;
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ (*c->handler)(margc, margv);
+ }
+}
+
+struct cmd *
+getcmd(name)
+ register char *name;
+{
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->name) != NULL; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void
+makeargv()
+{
+ register char *cp;
+ register char **argp = margv;
+
+ margc = 0;
+ for (cp = line; *cp;) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+void
+quit(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ exit(0);
+}
+
+/*
+ * Help command.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->name; c++)
+ printf("%-*s\t%s\n", (int)HELPINDENT, c->name, c->help);
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%s\n", c->help);
+ }
+}
+
+void
+settrace(argc, argv)
+ int argc;
+ char **argv;
+{
+ trace = !trace;
+ printf("Packet tracing %s.\n", trace ? "on" : "off");
+}
+
+void
+setverbose(argc, argv)
+ int argc;
+ char **argv;
+{
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", verbose ? "on" : "off");
+}
diff --git a/usr.bin/tftp/tftp.1 b/usr.bin/tftp/tftp.1
new file mode 100644
index 0000000..b2c5a16
--- /dev/null
+++ b/usr.bin/tftp/tftp.1
@@ -0,0 +1,173 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tftp.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt TFTP 1
+.Os BSD 4.3
+.Sh NAME
+.Nm tftp
+.Nd trivial file transfer program
+.Sh SYNOPSIS
+.Nm tftp
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Tftp
+is the user interface to the Internet
+.Tn TFTP
+(Trivial File Transfer Protocol),
+which allows users to transfer files to and from a remote machine.
+The remote
+.Ar host
+may be specified on the command line, in which case
+.Nm tftp
+uses
+.Ar host
+as the default host for future transfers (see the
+.Cm connect
+command below).
+.Sh COMMANDS
+Once
+.Nm tftp
+is running, it issues the prompt
+.LI tftp>
+and recognizes the following commands:
+.Pp
+.Bl -tag -width verbose -compact
+.It Cm \&? Ar command-name ...
+Print help information.
+.Pp
+.It Cm ascii
+Shorthand for "mode ascii"
+.Pp
+.It Cm binary
+Shorthand for "mode binary"
+.Pp
+.It Cm connect Ar host-name Op Ar port
+Set the
+.Ar host
+(and optionally
+.Ar port )
+for transfers.
+Note that the
+.Tn TFTP
+protocol, unlike the
+.Tn FTP
+protocol,
+does not maintain connections between transfers; thus, the
+.Cm connect
+command does not actually create a connection,
+but merely remembers what host is to be used for transfers.
+You do not have to use the
+.Cm connect
+command; the remote host can be specified as part of the
+.Cm get
+or
+.Cm put
+commands.
+.Pp
+.It Cm get Ar filename
+.It Cm get Ar remotename localname
+.It Cm get Ar file1 file2 ... fileN
+Get a file or set of files from the specified
+.Ar sources .
+.Ar Source
+can be in one of two forms:
+a filename on the remote host, if the host has already been specified,
+or a string of the form
+.Ar hosts:filename
+to specify both a host and filename at the same time.
+If the latter form is used,
+the last hostname specified becomes the default for future transfers.
+.Pp
+.It Cm mode Ar transfer-mode
+Set the mode for transfers;
+.Ar transfer-mode
+may be one of
+.Em ascii
+or
+.Em binary .
+The default is
+.Em ascii .
+.Pp
+.It Cm put Ar file
+.It Cm put Ar localfile remotefile
+.It Cm put Ar file1 file2 ... fileN remote-directory
+Put a file or set of files to the specified
+remote file or directory.
+The destination
+can be in one of two forms:
+a filename on the remote host, if the host has already been specified,
+or a string of the form
+.Ar hosts:filename
+to specify both a host and filename at the same time.
+If the latter form is used,
+the hostname specified becomes the default for future transfers.
+If the remote-directory form is used, the remote host is
+assumed to be a
+.Tn UNIX
+machine.
+.Pp
+.It Cm quit
+Exit
+.Nm tftp .
+An end of file also exits.
+.Pp
+.It Cm rexmt Ar retransmission-timeout
+Set the per-packet retransmission timeout, in seconds.
+.Pp
+.It Cm status
+Show current status.
+.Pp
+.It Cm timeout Ar total-transmission-timeout
+Set the total transmission timeout, in seconds.
+.Pp
+.It Cm trace
+Toggle packet tracing.
+.Pp
+.It Cm verbose
+Toggle verbose mode.
+.El
+.Sh BUGS
+.Pp
+Because there is no user-login or validation within
+the
+.Tn TFTP
+protocol, the remote site will probably have some
+sort of file-access restrictions in place. The
+exact methods are specific to each site and therefore
+difficult to document here.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c
new file mode 100644
index 0000000..2ca4001
--- /dev/null
+++ b/usr.bin/tftp/tftp.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftp.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
+
+/*
+ * TFTP User Program -- Protocol Machines
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "tftpsubs.h"
+
+extern int errno;
+
+extern struct sockaddr_in peeraddr; /* filled in by main */
+extern int f; /* the opened socket */
+extern int trace;
+extern int verbose;
+extern int rexmtval;
+extern int maxtimeout;
+
+#define PKTSIZE SEGSIZE+4
+char ackbuf[PKTSIZE];
+int timeout;
+jmp_buf toplevel;
+jmp_buf timeoutbuf;
+
+static void nak __P((int));
+static int makerequest __P((int, const char *, struct tftphdr *, const char *));
+static void printstats __P((const char *, unsigned long));
+static void startclock __P((void));
+static void stopclock __P((void));
+static void timer __P((int));
+static void tpacket __P((const char *, struct tftphdr *, int));
+
+/*
+ * Send the requested file.
+ */
+void
+sendfile(fd, name, mode)
+ int fd;
+ char *name;
+ char *mode;
+{
+ register struct tftphdr *ap; /* data and ack packets */
+ struct tftphdr *r_init(), *dp;
+ register int n;
+ volatile int block, size, convert;
+ volatile unsigned long amount;
+ struct sockaddr_in from;
+ int fromlen;
+ FILE *file;
+
+ startclock(); /* start stat's clock */
+ dp = r_init(); /* reset fillbuf/read-ahead code */
+ ap = (struct tftphdr *)ackbuf;
+ file = fdopen(fd, "r");
+ convert = !strcmp(mode, "netascii");
+ block = 0;
+ amount = 0;
+
+ signal(SIGALRM, timer);
+ do {
+ if (block == 0)
+ size = makerequest(WRQ, name, dp, mode) - 4;
+ else {
+ /* size = read(fd, dp->th_data, SEGSIZE); */
+ size = readit(file, &dp, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ break;
+ }
+ dp->th_opcode = htons((u_short)DATA);
+ dp->th_block = htons((u_short)block);
+ }
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+send_data:
+ if (trace)
+ tpacket("sent", dp, size + 4);
+ n = sendto(f, dp, size + 4, 0,
+ (struct sockaddr *)&peeraddr, sizeof(peeraddr));
+ if (n != size + 4) {
+ perror("tftp: sendto");
+ goto abort;
+ }
+ read_ahead(file, convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof(from);
+ n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ peeraddr.sin_port = from.sin_port; /* added */
+ if (trace)
+ tpacket("received", ap, n);
+ /* should verify packet came from server */
+ ap->th_opcode = ntohs(ap->th_opcode);
+ ap->th_block = ntohs(ap->th_block);
+ if (ap->th_opcode == ERROR) {
+ printf("Error code %d: %s\n", ap->th_code,
+ ap->th_msg);
+ goto abort;
+ }
+ if (ap->th_opcode == ACK) {
+ int j;
+
+ if (ap->th_block == block) {
+ break;
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n",
+ j);
+ }
+ if (ap->th_block == (block-1)) {
+ goto send_data;
+ }
+ }
+ }
+ if (block > 0)
+ amount += size;
+ block++;
+ } while (size == SEGSIZE || block == 1);
+abort:
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Sent", amount);
+}
+
+/*
+ * Receive a file.
+ */
+void
+recvfile(fd, name, mode)
+ int fd;
+ char *name;
+ char *mode;
+{
+ register struct tftphdr *ap;
+ struct tftphdr *dp, *w_init();
+ register int n;
+ volatile int block, size, firsttrip;
+ volatile unsigned long amount;
+ struct sockaddr_in from;
+ int fromlen;
+ FILE *file;
+ volatile int convert; /* true if converting crlf -> lf */
+
+ startclock();
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+ file = fdopen(fd, "w");
+ convert = !strcmp(mode, "netascii");
+ block = 1;
+ firsttrip = 1;
+ amount = 0;
+
+ signal(SIGALRM, timer);
+ do {
+ if (firsttrip) {
+ size = makerequest(RRQ, name, ap, mode);
+ firsttrip = 0;
+ } else {
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons((u_short)(block));
+ size = 4;
+ block++;
+ }
+ timeout = 0;
+ (void) setjmp(timeoutbuf);
+send_ack:
+ if (trace)
+ tpacket("sent", ap, size);
+ if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
+ sizeof(peeraddr)) != size) {
+ alarm(0);
+ perror("tftp: sendto");
+ goto abort;
+ }
+ write_behind(file, convert);
+ for ( ; ; ) {
+ alarm(rexmtval);
+ do {
+ fromlen = sizeof(from);
+ n = recvfrom(f, dp, PKTSIZE, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (n <= 0);
+ alarm(0);
+ if (n < 0) {
+ perror("tftp: recvfrom");
+ goto abort;
+ }
+ peeraddr.sin_port = from.sin_port; /* added */
+ if (trace)
+ tpacket("received", dp, n);
+ /* should verify client address */
+ dp->th_opcode = ntohs(dp->th_opcode);
+ dp->th_block = ntohs(dp->th_block);
+ if (dp->th_opcode == ERROR) {
+ printf("Error code %d: %s\n", dp->th_code,
+ dp->th_msg);
+ goto abort;
+ }
+ if (dp->th_opcode == DATA) {
+ int j;
+
+ if (dp->th_block == block) {
+ break; /* have next packet */
+ }
+ /* On an error, try to synchronize
+ * both sides.
+ */
+ j = synchnet(f);
+ if (j && trace) {
+ printf("discarded %d packets\n", j);
+ }
+ if (dp->th_block == (block-1)) {
+ goto send_ack; /* resend ack */
+ }
+ }
+ }
+ /* size = write(fd, dp->th_data, n - 4); */
+ size = writeit(file, &dp, n - 4, convert);
+ if (size < 0) {
+ nak(errno + 100);
+ break;
+ }
+ amount += size;
+ } while (size == SEGSIZE);
+abort: /* ok to ack, since user */
+ ap->th_opcode = htons((u_short)ACK); /* has seen err msg */
+ ap->th_block = htons((u_short)block);
+ (void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
+ sizeof(peeraddr));
+ write_behind(file, convert); /* flush last buffer */
+ fclose(file);
+ stopclock();
+ if (amount > 0)
+ printstats("Received", amount);
+}
+
+static int
+makerequest(request, name, tp, mode)
+ int request;
+ const char *name;
+ struct tftphdr *tp;
+ const char *mode;
+{
+ register char *cp;
+
+ tp->th_opcode = htons((u_short)request);
+ cp = tp->th_stuff;
+ strcpy(cp, name);
+ cp += strlen(name);
+ *cp++ = '\0';
+ strcpy(cp, mode);
+ cp += strlen(mode);
+ *cp++ = '\0';
+ return (cp - (char *)tp);
+}
+
+struct errmsg {
+ int e_code;
+ char *e_msg;
+} errmsgs[] = {
+ { EUNDEF, "Undefined error code" },
+ { ENOTFOUND, "File not found" },
+ { EACCESS, "Access violation" },
+ { ENOSPACE, "Disk full or allocation exceeded" },
+ { EBADOP, "Illegal TFTP operation" },
+ { EBADID, "Unknown transfer ID" },
+ { EEXISTS, "File already exists" },
+ { ENOUSER, "No such user" },
+ { -1, 0 }
+};
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+static void
+nak(error)
+ int error;
+{
+ register struct errmsg *pe;
+ register struct tftphdr *tp;
+ int length;
+ char *strerror();
+
+ tp = (struct tftphdr *)ackbuf;
+ tp->th_opcode = htons((u_short)ERROR);
+ tp->th_code = htons((u_short)error);
+ for (pe = errmsgs; pe->e_code >= 0; pe++)
+ if (pe->e_code == error)
+ break;
+ if (pe->e_code < 0) {
+ pe->e_msg = strerror(error - 100);
+ tp->th_code = EUNDEF;
+ }
+ strcpy(tp->th_msg, pe->e_msg);
+ length = strlen(pe->e_msg) + 4;
+ if (trace)
+ tpacket("sent", tp, length);
+ if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
+ sizeof(peeraddr)) != length)
+ perror("nak");
+}
+
+static void
+tpacket(s, tp, n)
+ const char *s;
+ struct tftphdr *tp;
+ int n;
+{
+ static char *opcodes[] =
+ { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
+ register char *cp, *file;
+ u_short op = ntohs(tp->th_opcode);
+ char *index();
+
+ if (op < RRQ || op > ERROR)
+ printf("%s opcode=%x ", s, op);
+ else
+ printf("%s %s ", s, opcodes[op]);
+ switch (op) {
+
+ case RRQ:
+ case WRQ:
+ n -= 2;
+ file = cp = tp->th_stuff;
+ cp = index(cp, '\0');
+ printf("<file=%s, mode=%s>\n", file, cp + 1);
+ break;
+
+ case DATA:
+ printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
+ break;
+
+ case ACK:
+ printf("<block=%d>\n", ntohs(tp->th_block));
+ break;
+
+ case ERROR:
+ printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
+ break;
+ }
+}
+
+struct timeval tstart;
+struct timeval tstop;
+
+static void
+startclock()
+{
+
+ (void)gettimeofday(&tstart, NULL);
+}
+
+static void
+stopclock()
+{
+
+ (void)gettimeofday(&tstop, NULL);
+}
+
+static void
+printstats(direction, amount)
+ const char *direction;
+ unsigned long amount;
+{
+ double delta;
+ /* compute delta in 1/10's second units */
+ delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
+ ((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
+ delta = delta/10.; /* back to seconds */
+ printf("%s %d bytes in %.1f seconds", direction, amount, delta);
+ if (verbose)
+ printf(" [%.0f bits/sec]", (amount*8.)/delta);
+ putchar('\n');
+}
+
+static void
+timer(sig)
+ int sig;
+{
+
+ timeout += rexmtval;
+ if (timeout >= maxtimeout) {
+ printf("Transfer timed out.\n");
+ longjmp(toplevel, -1);
+ }
+ longjmp(timeoutbuf, 1);
+}
diff --git a/usr.bin/tftp/tftpsubs.c b/usr.bin/tftp/tftpsubs.c
new file mode 100644
index 0000000..a143602
--- /dev/null
+++ b/usr.bin/tftp/tftpsubs.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tftpsubs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* Simple minded read-ahead/write-behind subroutines for tftp user and
+ server. Written originally with multiple buffers in mind, but current
+ implementation has two buffer logic wired in.
+
+ Todo: add some sort of final error check so when the write-buffer
+ is finally flushed, the caller can detect if the disk filled up
+ (or had an i/o error) and return a nak to the other side.
+
+ Jim Guyton 10/85
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "tftpsubs.h"
+
+#define PKTSIZE SEGSIZE+4 /* should be moved to tftp.h */
+
+struct bf {
+ int counter; /* size of data in buffer, or flag */
+ char buf[PKTSIZE]; /* room for data packet */
+} bfs[2];
+
+ /* Values for bf.counter */
+#define BF_ALLOC -3 /* alloc'd but not yet filled */
+#define BF_FREE -2 /* free */
+/* [-1 .. SEGSIZE] = size of data in the data buffer */
+
+static int nextone; /* index of next buffer to use */
+static int current; /* index of buffer in use */
+
+ /* control flags for crlf conversions */
+int newline = 0; /* fillbuf: in middle of newline expansion */
+int prevchar = -1; /* putbuf: previous char (cr check) */
+
+static struct tftphdr *rw_init();
+
+struct tftphdr *w_init() { return rw_init(0); } /* write-behind */
+struct tftphdr *r_init() { return rw_init(1); } /* read-ahead */
+
+static struct tftphdr *
+rw_init(x) /* init for either read-ahead or write-behind */
+ int x; /* zero for write-behind, one for read-head */
+{
+ newline = 0; /* init crlf flag */
+ prevchar = -1;
+ bfs[0].counter = BF_ALLOC; /* pass out the first buffer */
+ current = 0;
+ bfs[1].counter = BF_FREE;
+ nextone = x; /* ahead or behind? */
+ return (struct tftphdr *)bfs[0].buf;
+}
+
+
+/* Have emptied current buffer by sending to net and getting ack.
+ Free it and return next buffer filled with data.
+ */
+int
+readit(file, dpp, convert)
+ FILE *file; /* file opened for read */
+ struct tftphdr **dpp;
+ int convert; /* if true, convert to ascii */
+{
+ struct bf *b;
+
+ bfs[current].counter = BF_FREE; /* free old one */
+ current = !current; /* "incr" current */
+
+ b = &bfs[current]; /* look at new buffer */
+ if (b->counter == BF_FREE) /* if it's empty */
+ read_ahead(file, convert); /* fill it */
+/* assert(b->counter != BF_FREE);*//* check */
+ *dpp = (struct tftphdr *)b->buf; /* set caller's ptr */
+ return b->counter;
+}
+
+/*
+ * fill the input buffer, doing ascii conversions if requested
+ * conversions are lf -> cr,lf and cr -> cr, nul
+ */
+void
+read_ahead(file, convert)
+ FILE *file; /* file opened for read */
+ int convert; /* if true, convert to ascii */
+{
+ register int i;
+ register char *p;
+ register int c;
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone]; /* look at "next" buffer */
+ if (b->counter != BF_FREE) /* nop if not free */
+ return;
+ nextone = !nextone; /* "incr" next buffer ptr */
+
+ dp = (struct tftphdr *)b->buf;
+
+ if (convert == 0) {
+ b->counter = read(fileno(file), dp->th_data, SEGSIZE);
+ return;
+ }
+
+ p = dp->th_data;
+ for (i = 0 ; i < SEGSIZE; i++) {
+ if (newline) {
+ if (prevchar == '\n')
+ c = '\n'; /* lf to cr,lf */
+ else c = '\0'; /* cr to cr,nul */
+ newline = 0;
+ }
+ else {
+ c = getc(file);
+ if (c == EOF) break;
+ if (c == '\n' || c == '\r') {
+ prevchar = c;
+ c = '\r';
+ newline = 1;
+ }
+ }
+ *p++ = c;
+ }
+ b->counter = (int)(p - dp->th_data);
+}
+
+/* Update count associated with the buffer, get new buffer
+ from the queue. Calls write_behind only if next buffer not
+ available.
+ */
+int
+writeit(file, dpp, ct, convert)
+ FILE *file;
+ struct tftphdr **dpp;
+ int ct, convert;
+{
+ bfs[current].counter = ct; /* set size of data to write */
+ current = !current; /* switch to other buffer */
+ if (bfs[current].counter != BF_FREE) /* if not free */
+ (void)write_behind(file, convert); /* flush it */
+ bfs[current].counter = BF_ALLOC; /* mark as alloc'd */
+ *dpp = (struct tftphdr *)bfs[current].buf;
+ return ct; /* this is a lie of course */
+}
+
+/*
+ * Output a buffer to a file, converting from netascii if requested.
+ * CR,NUL -> CR and CR,LF => LF.
+ * Note spec is undefined if we get CR as last byte of file or a
+ * CR followed by anything else. In this case we leave it alone.
+ */
+int
+write_behind(file, convert)
+ FILE *file;
+ int convert;
+{
+ char *buf;
+ int count;
+ register int ct;
+ register char *p;
+ register int c; /* current character */
+ struct bf *b;
+ struct tftphdr *dp;
+
+ b = &bfs[nextone];
+ if (b->counter < -1) /* anything to flush? */
+ return 0; /* just nop if nothing to do */
+
+ count = b->counter; /* remember byte count */
+ b->counter = BF_FREE; /* reset flag */
+ dp = (struct tftphdr *)b->buf;
+ nextone = !nextone; /* incr for next time */
+ buf = dp->th_data;
+
+ if (count <= 0) return -1; /* nak logic? */
+
+ if (convert == 0)
+ return write(fileno(file), buf, count);
+
+ p = buf;
+ ct = count;
+ while (ct--) { /* loop over the buffer */
+ c = *p++; /* pick up a character */
+ if (prevchar == '\r') { /* if prev char was cr */
+ if (c == '\n') /* if have cr,lf then just */
+ fseek(file, -1, 1); /* smash lf on top of the cr */
+ else
+ if (c == '\0') /* if have cr,nul then */
+ goto skipit; /* just skip over the putc */
+ /* else just fall through and allow it */
+ }
+ putc(c, file);
+skipit:
+ prevchar = c;
+ }
+ return count;
+}
+
+
+/* When an error has occurred, it is possible that the two sides
+ * are out of synch. Ie: that what I think is the other side's
+ * response to packet N is really their response to packet N-1.
+ *
+ * So, to try to prevent that, we flush all the input queued up
+ * for us on the network connection on our host.
+ *
+ * We return the number of packets we flushed (mostly for reporting
+ * when trace is active).
+ */
+
+int
+synchnet(f)
+ int f; /* socket to flush */
+{
+ int i, j = 0;
+ char rbuf[PKTSIZE];
+ struct sockaddr_in from;
+ int fromlen;
+
+ while (1) {
+ (void) ioctl(f, FIONREAD, &i);
+ if (i) {
+ j++;
+ fromlen = sizeof from;
+ (void) recvfrom(f, rbuf, sizeof (rbuf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ } else {
+ return(j);
+ }
+ }
+}
diff --git a/usr.bin/tftp/tftpsubs.h b/usr.bin/tftp/tftpsubs.h
new file mode 100644
index 0000000..1733bf1
--- /dev/null
+++ b/usr.bin/tftp/tftpsubs.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tftpsubs.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Prototypes for read-ahead/write-behind subroutines for tftp user and
+ * server.
+ */
+struct tftphdr *r_init __P((void));
+void read_ahead __P((FILE *, int));
+int readit __P((FILE *, struct tftphdr **, int));
+
+int synchnet __P((int));
+
+struct tftphdr *w_init __P((void));
+int write_behind __P((FILE *, int));
+int writeit __P((FILE *, struct tftphdr **, int, int));
diff --git a/usr.bin/time/Makefile b/usr.bin/time/Makefile
new file mode 100644
index 0000000..ae649c7
--- /dev/null
+++ b/usr.bin/time/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= time
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/time/time.1 b/usr.bin/time/time.1
new file mode 100644
index 0000000..81053ed
--- /dev/null
+++ b/usr.bin/time/time.1
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)time.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TIME 1
+.Os BSD 4
+.Sh NAME
+.Nm time
+.Nd time command execution
+.Sh SYNOPSIS
+.Nm time
+.Op Fl l
+.Ar command
+.Sh DESCRIPTION
+The
+.Nm time
+utility
+executes and
+times
+.Ar command
+by initiating a timer and passing the
+.Ar command
+to the
+shell.
+After the
+.Ar command
+finishes,
+.Nm time
+writes to the standard error stream,
+(in seconds):
+the total time elapsed,
+time consumed by system overhead,
+and the time used to execute the
+.Ar command
+process.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl l
+The contents of the
+.Em rusage
+structure are printed as well.
+.El
+.Pp
+The
+.Xr csh 1
+has its own and syntactically different builtin version of
+.Nm time.
+The command described here
+is available as
+.Pa /usr/bin/time
+to
+.Xr csh
+users.
+.Sh BUGS
+The granularity of seconds on micro processors is crude and
+can result in times being reported for CPU usage which are too large by
+a second.
+.Sh SEE ALSO
+.Xr csh 1
+.Sh FILES
+.Bl -tag -width /usr/include/sys/resource.h -compact
+.It Pa /usr/include/sys/resource.h
+.El
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/time/time.c b/usr.bin/time/time.c
new file mode 100644
index 0000000..72c3766
--- /dev/null
+++ b/usr.bin/time/time.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)time.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+#include <stdio.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ register int pid;
+ int ch, status, lflag;
+ struct timeval before, after;
+ struct rusage ru;
+
+ lflag = 0;
+ while ((ch = getopt(argc, argv, "l")) != EOF)
+ switch((char)ch) {
+ case 'l':
+ lflag = 1;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "usage: time [-l] command.\n");
+ exit(1);
+ }
+
+ if (!(argc -= optind))
+ exit(0);
+ argv += optind;
+
+ gettimeofday(&before, (struct timezone *)NULL);
+ switch(pid = vfork()) {
+ case -1: /* error */
+ perror("time");
+ exit(1);
+ /* NOTREACHED */
+ case 0: /* child */
+ execvp(*argv, argv);
+ perror(*argv);
+ _exit(1);
+ /* NOTREACHED */
+ }
+ /* parent */
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ while (wait3(&status, 0, &ru) != pid); /* XXX use waitpid */
+ gettimeofday(&after, (struct timezone *)NULL);
+ if (status&0377)
+ fprintf(stderr, "Command terminated abnormally.\n");
+ after.tv_sec -= before.tv_sec;
+ after.tv_usec -= before.tv_usec;
+ if (after.tv_usec < 0)
+ after.tv_sec--, after.tv_usec += 1000000;
+ fprintf(stderr, "%9ld.%02ld real ", after.tv_sec, after.tv_usec/10000);
+ fprintf(stderr, "%9ld.%02ld user ",
+ ru.ru_utime.tv_sec, ru.ru_utime.tv_usec/10000);
+ fprintf(stderr, "%9ld.%02ld sys\n",
+ ru.ru_stime.tv_sec, ru.ru_stime.tv_usec/10000);
+ if (lflag) {
+ int hz = 100; /* XXX */
+ long ticks;
+
+ ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) +
+ hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000;
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_maxrss, "maximum resident set size");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_ixrss / ticks, "average shared memory size");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_idrss / ticks, "average unshared data size");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_isrss / ticks, "average unshared stack size");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_minflt, "page reclaims");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_majflt, "page faults");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_nswap, "swaps");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_inblock, "block input operations");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_oublock, "block output operations");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_msgsnd, "messages sent");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_msgrcv, "messages received");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_nsignals, "signals received");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_nvcsw, "voluntary context switches");
+ fprintf(stderr, "%10ld %s\n",
+ ru.ru_nivcsw, "involuntary context switches");
+ }
+ exit (status>>8);
+}
diff --git a/usr.bin/timedef/Makefile b/usr.bin/timedef/Makefile
new file mode 100644
index 0000000..80fe8d9
--- /dev/null
+++ b/usr.bin/timedef/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.3 1995/06/19 22:15:35 asami Exp $
+
+LOCALES= ru_SU.KOI8-R
+LOCALEDIR= ${DESTDIR}/usr/share/locale
+
+afterinstall:
+ for l in ${LOCALES}; do \
+ grep -v '^#' < ${.CURDIR}/data/$$l > ${LOCALEDIR}/$$l/LC_TIME; \
+ chown ${BINOWN}.${BINGRP} ${LOCALEDIR}/$$l/LC_TIME; \
+ chmod 644 ${LOCALEDIR}/$$l/LC_TIME; \
+ done
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/timedef/data/ru_SU.KOI8-R b/usr.bin/timedef/data/ru_SU.KOI8-R
new file mode 100644
index 0000000..e7a869f
--- /dev/null
+++ b/usr.bin/timedef/data/ru_SU.KOI8-R
@@ -0,0 +1,76 @@
+# WARNING: spaces may be essential at the end of lines
+# WARNING: empty lines are essential too
+#
+# Short months names
+#
+ÑÎ×
+ÆÅ×
+ÍÁÒ
+ÁÐÒ
+ÍÁÑ
+ÉÀÎ
+ÉÀÌ
+Á×Ç
+ÓÅÎ
+ÏËÔ
+ÎÏÑ
+ÄÅË
+#
+# Long months names
+#
+ÑÎ×ÁÒÑ
+ÆÅ×ÒÁÌÑ
+ÍÁÒÔÁ
+ÁÐÒÅÌÑ
+ÍÁÑ
+ÉÀÎÑ
+ÉÀÌÑ
+Á×ÇÕÓÔÁ
+ÓÅÎÔÑÂÒÑ
+ÏËÔÑÂÒÑ
+ÎÏÑÂÒÑ
+ÄÅËÁÂÒÑ
+#
+# Short weekdays names
+#
+×ÏÓ
+ÐÏÎ
+×ÔÏ
+ÓÒÅ
+ÞÅÔ
+ÐÑÔ
+ÓÕÂ
+#
+# Long weekdays names
+#
+×ÏÓËÒÅÓÅÎØÅ
+ÐÏÎÅÄÅÌØÎÉË
+×ÔÏÒÎÉË
+ÓÒÅÄÁ
+ÞÅÔ×ÅÒÇ
+ÐÑÔÎÉÃÁ
+ÓÕÂÂÏÔÁ
+#
+# X_fmt
+#
+%H:%M:%S
+#
+# x_fmt
+#
+%d.%m.%y
+#
+# c_fmt
+#
+%x %X
+#
+# am
+#
+
+#
+# pm
+#
+ÐÐ
+#
+# date_fmt
+#
+%a %e %b %H:%M:%S %Z %Y
diff --git a/usr.bin/tip/Makefile b/usr.bin/tip/Makefile
new file mode 100644
index 0000000..721823a
--- /dev/null
+++ b/usr.bin/tip/Makefile
@@ -0,0 +1,3 @@
+SUBDIR=libacu tip
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/tip/Makefile.inc b/usr.bin/tip/Makefile.inc
new file mode 100644
index 0000000..4a9bb26
--- /dev/null
+++ b/usr.bin/tip/Makefile.inc
@@ -0,0 +1,5 @@
+# These are mostly just for tip, but they're more visible up here.
+BINDIR?= /usr/bin
+BINOWN?= uucp
+BINGRP?= dialer
+BINMODE?= 4510
diff --git a/usr.bin/tip/NEWS b/usr.bin/tip/NEWS
new file mode 100644
index 0000000..9e37d42
--- /dev/null
+++ b/usr.bin/tip/NEWS
@@ -0,0 +1,65 @@
+Sat Mar 25 16:06:31 PST 1995
+
+hw_flow_control (boolean) capability added to modem configuration
+data base (/etc/modems).
+
+Configurable unidialer driver compiled in by default. Builtin
+ACU drivers no longer compiled in by default.
+
+All configuration details isolated in tipconf.h (with exception of
+pathnames.h).
+
+Made corrections to and updated manual page for tip.
+
+Conditional compilation of cu interface for systems that provide
+a separate cu implementation (e.g. FreeBSD).
+
+Sat Mar 25 00:35:08 PST 1995
+
+Corrected entry in man file to identify the lockfile dir as /var/spool/locks
+instead of /var/spool/uucp.
+
+xfer and transfer in cmds.c use standard C runtime buffered output.
+Eliminates a bug: buffer overruns when FRAMESIZE > BUFSIZ.
+
+Added xfer routine for doing cu style take commands.
+
+Thu Mar 16 08:17:57 PST 1995
+
+Added support for termios.
+
+Renamed acunap.* to acucommon.*. Moved common acu routines
+to it. Modified acu drivers to use common routines.
+
+Sat Mar 11 20:17:58 PST 1995
+
+Default nap function uses select call in preference to
+usleep or old, handrolled code.
+
+tip makefile does not create a link to cu by default.
+
+Wed Mar 8 00:11:04 PST 1995
+
+Reduced sleep time in finish () in cmds.c from 5 seconds to two.
+
+Sat Mar 4 19:00:34 PST 1995
+
+Added table-driven modem driver (unidialer) that fetches modem
+characteristics from a modem database ("/etc/modems").
+
+Removed common "nap" code from individual ACU drivers into a
+separate module.
+
+Added login script capability to remote database.
+
+Sun Feb 26 23:07:56 PST 1995
+
+Use HoneyDanber style locks by default (ASCII PIDs written to lock files).
+
+Changed default uucp file lock directory to "/var/spool/locks"
+to be consistent with default build of Taylor uucp.
+
+Reorganized build environment for tip into a master directory
+and two subdirectories. The master Makefile builds a library
+of ACU "drivers" in the directory libacu. Tip only links in
+drivers that it will actually use.
diff --git a/usr.bin/tip/README b/usr.bin/tip/README
new file mode 100644
index 0000000..12d45b2
--- /dev/null
+++ b/usr.bin/tip/README
@@ -0,0 +1,63 @@
+[See NEWS file for much more up-to-date information]
+
+Tip can be configured in a number of ways:
+
+ACU's:
+-----
+
+ACU Define in makefile
+-------------------- ---------------
+BIZCOMP 1022, 1031 BIZ1022, BIZ1031
+DEC DF02-AC, DF03-AC DF02, DF03
+DEC DN-11/Able Quadracall DN11
+Ventel VENTEL
+Vadic 831 V831
+
+New ACU's may be added by editing the ACU description table
+in acutab.c and writing a ``driver''.
+
+ACU usage can be monitored by defining ACULOG in the makefile.
+If this is done and no phone numbers should appear in the
+log file, define PRISTINE in the makefile.
+
+Variables:
+---------
+
+Tip's internal workings revolve around a set of (possibly)
+user defined variables. These are statically initialized
+in vars.c, and from the remote file.
+
+Note that adding or deleting variables requires tip to be completedly
+recompiled, as indexes into the variable table are used to avoid
+expensive lookups. These defines are set in tip.h.
+
+Commands:
+--------
+
+The command dispatch table is defined in cmdtab.c. Commands
+may have attributes such as EXPerimental and PRIVileged (only
+root may execute).
+
+
+
+--------------------------------------------------------------------------
+
+Recent changes about Jan 82
+
+A new, improved version of tip is now available. The most important
+addition is the capacility to specify a phone number with tip. The
+default baud rate is 1200. To use it do:
+
+ tip phone-number
+or
+ tip -300 phone-number
+
+for 300 baud.
+
+A ~^Z command has been added to tip as well.
+
+A new cu program is available that interfaces to the tip program.
+It attempts to give the same user interface as cu but it is really
+the tip program so you have all the advantages of tip. This allows
+cu (actually tip) to search for a free ACU instead of having the
+user specify which one he wants.
diff --git a/usr.bin/tip/TODO b/usr.bin/tip/TODO
new file mode 100644
index 0000000..41e2d6e
--- /dev/null
+++ b/usr.bin/tip/TODO
@@ -0,0 +1,38 @@
+x Fix "hangup failed" in unidialer
+x Genericize tty code and/or support termios
+x Fixup uucplock conventions for FreeBSD (including reference in tip man pages).
+Sanity check for ttys
+x Rename pathnames.h
+x Qualify on USR
+Use select call to merge tipin / tipout
+
+x Finish table-driven modem driver
+x Move HAVE_USLEEP to pathhnames.h
+x Add variable for login and logout scripts (connect with Perl???)
+x redials / redial delay (see dial shell script)
+
+Add debug log capabilities for table-driven modem driver (command mode modem I/Owith timestamps).
+
+Use select in modem drivers.
+Consolidate consh()-derived code in cmds.c
+Screen-oriented command menu?
+Add external file-transfer protocol implementations to command menu (rz, sz, others?)
+
+1. Rethink protection glitches on REMOTE & PHONES
+ files (setuid/setgid??).
+
+2. Make clean fix for scripting being set in .tiprc
+
+3. change EOFREAD to recognize more general strings.
+
+4. add an option that returns an exit status based on
+ whether resources for the requested operation are available.
+
+5. write a program to list known systems (a quick shell script
+ should do it); people keep forgetting the names.
+
+6. change remote file descriptions so that acu attributes are
+ are attached to a device so that several different devices
+ can be used to get to the same system (perhaps hardwired
+ and phone line). got any ideas here? I'm looking at something
+ like dv=cua1,cul1,dn11;cua2,,df03.
diff --git a/usr.bin/tip/acu.c b/usr.bin/tip/acu.c
new file mode 100644
index 0000000..f7bde99
--- /dev/null
+++ b/usr.bin/tip/acu.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)acu.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+static acu_t *acu = NOACU;
+static int conflag;
+static void acuabort();
+static acu_t *acutype();
+static jmp_buf jmpbuf;
+/*
+ * Establish connection for tip
+ *
+ * If DU is true, we should dial an ACU whose type is AT.
+ * The phone numbers are in PN, and the call unit is in CU.
+ *
+ * If the PN is an '@', then we consult the PHONES file for
+ * the phone numbers. This file is /etc/phones, unless overriden
+ * by an exported shell variable.
+ *
+ * The data base files must be in the format:
+ * host-name[ \t]*phone-number
+ * with the possibility of multiple phone numbers
+ * for a single host acting as a rotary (in the order
+ * found in the file).
+ */
+char *
+connect()
+{
+ register char *cp = PN;
+ char *phnum, string[256];
+ FILE *fd;
+ int tried = 0;
+
+ if (!DU) { /* regular connect message */
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), "", DV, "call completed");
+ return (NOSTR);
+ }
+ /*
+ * @ =>'s use data base in PHONES environment variable
+ * otherwise, use /etc/phones
+ */
+ signal(SIGINT, acuabort);
+ signal(SIGQUIT, acuabort);
+ if (setjmp(jmpbuf)) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ printf("\ncall aborted\n");
+ logent(value(HOST), "", "", "call aborted");
+ if (acu != NOACU) {
+ boolean(value(VERBOSE)) = FALSE;
+ if (conflag)
+ disconnect(NOSTR);
+ else
+ (*acu->acu_abort)();
+ }
+ return ("interrupt");
+ }
+ if ((acu = acutype(AT)) == NOACU)
+ return ("unknown ACU type");
+ if (*cp != '@') {
+ while (*cp) {
+ for (phnum = cp; *cp && *cp != ','; cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+
+ if (conflag = (*acu->acu_dialer)(phnum, CU)) {
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), phnum, acu->acu_name,
+ "call completed");
+ return (NOSTR);
+ } else
+ logent(value(HOST), phnum, acu->acu_name,
+ "call failed");
+ tried++;
+ }
+ } else {
+ if ((fd = fopen(PH, "r")) == NOFILE) {
+ printf("%s: ", PH);
+ return ("can't open phone number file");
+ }
+ while (fgets(string, sizeof(string), fd) != NOSTR) {
+ for (cp = string; !any(*cp, " \t\n"); cp++)
+ ;
+ if (*cp == '\n') {
+ fclose(fd);
+ return ("unrecognizable host name");
+ }
+ *cp++ = '\0';
+ if (strcmp(string, value(HOST)))
+ continue;
+ while (any(*cp, " \t"))
+ cp++;
+ if (*cp == '\n') {
+ fclose(fd);
+ return ("missing phone number");
+ }
+ for (phnum = cp; *cp && *cp != ',' && *cp != '\n'; cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+
+ if (conflag = (*acu->acu_dialer)(phnum, CU)) {
+ fclose(fd);
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), phnum, acu->acu_name,
+ "call completed");
+ return (NOSTR);
+ } else
+ logent(value(HOST), phnum, acu->acu_name,
+ "call failed");
+ tried++;
+ }
+ fclose(fd);
+ }
+ if (!tried)
+ logent(value(HOST), "", acu->acu_name, "missing phone number");
+ else
+ (*acu->acu_abort)();
+ return (tried ? "call failed" : "missing phone number");
+}
+
+disconnect(reason)
+ char *reason;
+{
+ if (!conflag) {
+ logent(value(HOST), "", DV, "call terminated");
+ return;
+ }
+ if (reason == NOSTR) {
+ logent(value(HOST), "", acu->acu_name, "call terminated");
+ if (boolean(value(VERBOSE)))
+ printf("\r\ndisconnecting...");
+ } else
+ logent(value(HOST), "", acu->acu_name, reason);
+ (*acu->acu_disconnect)();
+}
+
+static void
+acuabort(s)
+{
+ signal(s, SIG_IGN);
+ longjmp(jmpbuf, 1);
+}
+
+static acu_t *
+acutype(s)
+ register char *s;
+{
+ register acu_t *p;
+ extern acu_t acutable[];
+
+ for (p = acutable; p->acu_name != '\0'; p++)
+ if (!strcmp(s, p->acu_name))
+ return (p);
+ return (NOACU);
+}
diff --git a/usr.bin/tip/aculib/biz22.c b/usr.bin/tip/aculib/biz22.c
new file mode 100644
index 0000000..93c5e53
--- /dev/null
+++ b/usr.bin/tip/aculib/biz22.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)biz22.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+#define DISCONNECT_CMD "\20\04" /* disconnection string */
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * Dial up on a BIZCOMP Model 1022 with either
+ * tone dialing (mod = "V")
+ * pulse dialing (mod = "W")
+ */
+static int
+biz_dialer(num, mod)
+ char *num, *mod;
+{
+ register int connected = 0;
+ char cbuf[40];
+ static int cmd(), detect();
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ /*
+ * Disable auto-answer and configure for tone/pulse
+ * dialing
+ */
+ if (cmd("\02K\r")) {
+ printf("can't initialize bizcomp...");
+ return (0);
+ }
+ strcpy(cbuf, "\02.\r");
+ cbuf[1] = *mod;
+ if (cmd(cbuf)) {
+ printf("can't set dialing mode...");
+ return (0);
+ }
+ strcpy(cbuf, "\02D");
+ strcat(cbuf, num);
+ strcat(cbuf, "\r");
+ write(FD, cbuf, strlen(cbuf));
+ if (!detect("7\r")) {
+ printf("can't get dial tone...");
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("ringing...");
+ /*
+ * The reply from the BIZCOMP should be:
+ * 2 \r or 7 \r failure
+ * 1 \r success
+ */
+ connected = detect("1\r");
+#ifdef ACULOG
+ if (timeout) {
+ char line[80];
+
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "biz1022", line);
+ }
+#endif
+ if (timeout)
+ biz22_disconnect(); /* insurance */
+ return (connected);
+}
+
+biz22w_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "W"));
+}
+
+biz22f_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "V"));
+}
+
+biz22_disconnect()
+{
+ int rw = 2;
+
+ write(FD, DISCONNECT_CMD, 4);
+ sleep(2);
+ ioctl(FD, TIOCFLUSH, &rw);
+}
+
+biz22_abort()
+{
+
+ write(FD, "\02", 1);
+}
+
+static void
+sigALRM()
+{
+
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+cmd(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ write(FD, s, strlen(s));
+ f = signal(SIGALRM, sigALRM);
+ if (setjmp(timeoutbuf)) {
+ biz22_abort();
+ signal(SIGALRM, f);
+ return (1);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ signal(SIGALRM, f);
+ c &= 0177;
+ return (c != '\r');
+}
+
+static int
+detect(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ while (*s) {
+ if (setjmp(timeoutbuf)) {
+ biz22_abort();
+ break;
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+ if (c != *s++)
+ return (0);
+ }
+ signal(SIGALRM, f);
+ return (timeout == 0);
+}
diff --git a/usr.bin/tip/aculib/biz31.c b/usr.bin/tip/aculib/biz31.c
new file mode 100644
index 0000000..412974d
--- /dev/null
+++ b/usr.bin/tip/aculib/biz31.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)biz31.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+#define MAXRETRY 3 /* sync up retry count */
+#define DISCONNECT_CMD "\21\25\11\24" /* disconnection string */
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * Dial up on a BIZCOMP Model 1031 with either
+ * tone dialing (mod = "f")
+ * pulse dialing (mod = "w")
+ */
+static int
+biz_dialer(num, mod)
+ char *num, *mod;
+{
+ register int connected = 0;
+
+ if (!bizsync(FD)) {
+ logent(value(HOST), "", "biz", "out of sync");
+ printf("bizcomp out of sync\n");
+ delock(uucplock);
+ exit(0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ echo("#\rk$\r$\n"); /* disable auto-answer */
+ echo("$>$.$ #\r"); /* tone/pulse dialing */
+ echo(mod);
+ echo("$\r$\n");
+ echo("$>$.$ #\re$ "); /* disconnection sequence */
+ echo(DISCONNECT_CMD);
+ echo("\r$\n$\r$\n");
+ echo("$>$.$ #\rr$ "); /* repeat dial */
+ echo(num);
+ echo("\r$\n");
+ if (boolean(value(VERBOSE)))
+ printf("ringing...");
+ /*
+ * The reply from the BIZCOMP should be:
+ * `^G NO CONNECTION\r\n^G\r\n' failure
+ * ` CONNECTION\r\n^G' success
+ */
+ connected = detect(" ");
+#ifdef ACULOG
+ if (timeout) {
+ char line[80];
+
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "biz", line);
+ }
+#endif
+ if (!connected)
+ flush(" NO CONNECTION\r\n\07\r\n");
+ else
+ flush("CONNECTION\r\n\07");
+ if (timeout)
+ biz31_disconnect(); /* insurance */
+ return (connected);
+}
+
+biz31w_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "w"));
+}
+
+biz31f_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "f"));
+}
+
+biz31_disconnect()
+{
+
+ write(FD, DISCONNECT_CMD, 4);
+ sleep(2);
+ ioctl(FD, TIOCFLUSH);
+}
+
+biz31_abort()
+{
+
+ write(FD, "\33", 1);
+}
+
+static int
+echo(s)
+ register char *s;
+{
+ char c;
+
+ while (c = *s++) switch (c) {
+
+ case '$':
+ read(FD, &c, 1);
+ s++;
+ break;
+
+ case '#':
+ c = *s++;
+ write(FD, &c, 1);
+ break;
+
+ default:
+ write(FD, &c, 1);
+ read(FD, &c, 1);
+ }
+}
+
+static void
+sigALRM()
+{
+
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+detect(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ while (*s) {
+ if (setjmp(timeoutbuf)) {
+ printf("\07timeout waiting for reply\n");
+ biz31_abort();
+ break;
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ if (c != *s++)
+ break;
+ }
+ signal(SIGALRM, f);
+ return (timeout == 0);
+}
+
+static int
+flush(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ while (*s++) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(10);
+ read(FD, &c, 1);
+ alarm(0);
+ }
+ signal(SIGALRM, f);
+ timeout = 0; /* guard against disconnection */
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the bizcomp in sync. If you don't have the capacity or nread
+ * call there are gory ways to simulate this.
+ */
+static int
+bizsync(fd)
+{
+#ifdef FIOCAPACITY
+ struct capacity b;
+# define chars(b) ((b).cp_nbytes)
+# define IOCTL FIOCAPACITY
+#endif
+#ifdef FIONREAD
+ long b;
+# define chars(b) (b)
+# define IOCTL FIONREAD
+#endif
+ register int already = 0;
+ char buf[10];
+
+retry:
+ if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0 && chars(b) > 0)
+ ioctl(fd, TIOCFLUSH);
+ write(fd, "\rp>\r", 4);
+ sleep(1);
+ if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0) {
+ if (chars(b) != 10) {
+ nono:
+ if (already > MAXRETRY)
+ return (0);
+ write(fd, DISCONNECT_CMD, 4);
+ sleep(2);
+ already++;
+ goto retry;
+ } else {
+ read(fd, buf, 10);
+ if (strncmp(buf, "p >\r\n\r\n>", 8))
+ goto nono;
+ }
+ }
+ return (1);
+}
diff --git a/usr.bin/tip/aculib/courier.c b/usr.bin/tip/aculib/courier.c
new file mode 100644
index 0000000..85f7b0d
--- /dev/null
+++ b/usr.bin/tip/aculib/courier.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)courier.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Courier modem.
+ * Derived from Hayes driver.
+ */
+#include "tip.h"
+#include <stdio.h>
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+static int coursync();
+
+cour_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+#ifdef ACULOG
+ char line[80];
+#endif
+ static int cour_connect(), cour_swallow();
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ ioctl(FD, TIOCHPCL, 0);
+ /*
+ * Get in synch.
+ */
+ if (!coursync()) {
+badsynch:
+ printf("can't synchronize with courier\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "courier", "can't synch up");
+#endif
+ return (0);
+ }
+ cour_write(FD, "AT E0\r", 6); /* turn off echoing */
+ sleep(1);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ cour_verbose_read();
+#endif
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21);
+ if (!cour_swallow("\r\nOK\r\n"))
+ goto badsynch;
+ fflush(stdout);
+ cour_write(FD, "AT D", 4);
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+ cour_write(FD, num, strlen(num));
+ cour_write(FD, "\r", 1);
+ connected = cour_connect();
+#ifdef ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "cour", line);
+ }
+#endif
+ if (timeout)
+ cour_disconnect();
+ return (connected);
+}
+
+cour_disconnect()
+{
+ /* first hang up the modem*/
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ coursync(); /* reset */
+ close(FD);
+}
+
+cour_abort()
+{
+ cour_write(FD, "\r", 1); /* send anything to abort the call */
+ cour_disconnect();
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+cour_swallow(match)
+ register char *match;
+ {
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ return (1);
+ }
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c == *match++);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush(stdout);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ return (0);
+}
+
+struct baud_msg {
+ char *msg;
+ int baud;
+} baud_msg[] = {
+ "", B300,
+ " 1200", B1200,
+ " 2400", B2400,
+ " 9600", B9600,
+ " 9600/ARQ", B9600,
+ 0, 0,
+};
+
+static int
+cour_connect()
+{
+ char c;
+ int nc, nl, n;
+ struct sgttyb sb;
+ char dialer_buf[64];
+ struct baud_msg *bm;
+ sig_t f;
+
+ if (cour_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (cour_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ for (bm = baud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg,
+ dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (ioctl(FD, TIOCGETP, &sb) < 0) {
+ perror("TIOCGETP");
+ goto error;
+ }
+ sb.sg_ispeed = sb.sg_ospeed = bm->baud;
+ if (ioctl(FD, TIOCSETP, &sb) < 0) {
+ perror("TIOCSETP");
+ goto error;
+ }
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the courier in sync.
+ */
+static int
+coursync()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ cour_write(FD, "\rAT Z\r", 6); /* reset modem */
+ bzero(buf, sizeof(buf));
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf[len] = '\0';
+ printf("coursync: (\"%s\")\n\r", buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ sleep(1);
+ cour_write(FD, "+++", 3);
+ sleep(1);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ cour_write(FD, "\rAT Z\r", 6);
+ return (0);
+}
+
+cour_write(fd, cp, n)
+int fd;
+char *cp;
+int n;
+{
+ struct sgttyb sb;
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ write(1, cp, n);
+#endif
+ ioctl(fd, TIOCGETP, &sb);
+ ioctl(fd, TIOCSETP, &sb);
+ cour_nap();
+ for ( ; n-- ; cp++) {
+ write(fd, cp, 1);
+ ioctl(fd, TIOCGETP, &sb);
+ ioctl(fd, TIOCSETP, &sb);
+ cour_nap();
+ }
+}
+
+#ifdef DEBUG
+cour_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+/*
+ * Code stolen from /usr/src/lib/libc/gen/sleep.c
+ */
+#define mask(s) (1<<((s)-1))
+#define setvec(vec, a) \
+ vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
+
+static napms = 50; /* Give the courier 50 milliseconds between characters */
+
+static int ringring;
+
+cour_nap()
+{
+
+ static void cour_napx();
+ int omask;
+ struct itimerval itv, oitv;
+ register struct itimerval *itp = &itv;
+ struct sigvec vec, ovec;
+
+ timerclear(&itp->it_interval);
+ timerclear(&itp->it_value);
+ if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
+ return;
+ setvec(ovec, SIG_DFL);
+ omask = sigblock(mask(SIGALRM));
+ itp->it_value.tv_sec = napms/1000;
+ itp->it_value.tv_usec = ((napms%1000)*1000);
+ setvec(vec, cour_napx);
+ ringring = 0;
+ (void) sigvec(SIGALRM, &vec, &ovec);
+ (void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
+ while (!ringring)
+ sigpause(omask &~ mask(SIGALRM));
+ (void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
+ (void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);
+ (void) sigsetmask(omask);
+}
+
+static void
+cour_napx()
+{
+ ringring = 1;
+}
diff --git a/usr.bin/tip/aculib/df.c b/usr.bin/tip/aculib/df.c
new file mode 100644
index 0000000..5f294f9
--- /dev/null
+++ b/usr.bin/tip/aculib/df.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)df.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Dial the DF02-AC or DF03-AC
+ */
+
+#include "tip.h"
+
+static jmp_buf Sjbuf;
+static void timeout();
+
+df02_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (df_dialer(num, acu, 0));
+}
+
+df03_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (df_dialer(num, acu, 1));
+}
+
+df_dialer(num, acu, df03)
+ char *num, *acu;
+ int df03;
+{
+ register int f = FD;
+ struct sgttyb buf;
+ int speed = 0, rw = 2;
+ char c = '\0';
+
+ ioctl(f, TIOCHPCL, 0); /* make sure it hangs up when done */
+ if (setjmp(Sjbuf)) {
+ printf("connection timed out\r\n");
+ df_disconnect();
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+#ifdef TIOCMSET
+ if (df03) {
+ int st = TIOCM_ST; /* secondary Transmit flag */
+
+ ioctl(f, TIOCGETP, &buf);
+ if (buf.sg_ospeed != B1200) { /* must dial at 1200 baud */
+ speed = buf.sg_ospeed;
+ buf.sg_ospeed = buf.sg_ispeed = B1200;
+ ioctl(f, TIOCSETP, &buf);
+ ioctl(f, TIOCMBIC, &st); /* clear ST for 300 baud */
+ } else
+ ioctl(f, TIOCMBIS, &st); /* set ST for 1200 baud */
+ }
+#endif
+ signal(SIGALRM, timeout);
+ alarm(5 * strlen(num) + 10);
+ ioctl(f, TIOCFLUSH, &rw);
+ write(f, "\001", 1);
+ sleep(1);
+ write(f, "\002", 1);
+ write(f, num, strlen(num));
+ read(f, &c, 1);
+#ifdef TIOCMSET
+ if (df03 && speed) {
+ buf.sg_ispeed = buf.sg_ospeed = speed;
+ ioctl(f, TIOCSETP, &buf);
+ }
+#endif
+ return (c == 'A');
+}
+
+df_disconnect()
+{
+ int rw = 2;
+
+ write(FD, "\001", 1);
+ sleep(1);
+ ioctl(FD, TIOCFLUSH, &rw);
+}
+
+
+df_abort()
+{
+
+ df_disconnect();
+}
+
+
+static void
+timeout()
+{
+
+ longjmp(Sjbuf, 1);
+}
diff --git a/usr.bin/tip/aculib/dn11.c b/usr.bin/tip/aculib/dn11.c
new file mode 100644
index 0000000..152b376
--- /dev/null
+++ b/usr.bin/tip/aculib/dn11.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dn11.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for dialing up on DN-11
+ */
+#include "tip.h"
+
+int dn_abort();
+void alarmtr();
+static jmp_buf jmpbuf;
+static int child = -1, dn;
+
+dn_dialer(num, acu)
+ char *num, *acu;
+{
+ extern errno;
+ char *p, *q, phone[40];
+ int lt, nw, connected = 1;
+ register int timelim;
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ if ((dn = open(acu, 1)) < 0) {
+ if (errno == EBUSY)
+ printf("line busy...");
+ else
+ printf("acu open error...");
+ return (0);
+ }
+ if (setjmp(jmpbuf)) {
+ kill(child, SIGKILL);
+ close(dn);
+ return (0);
+ }
+ signal(SIGALRM, alarmtr);
+ timelim = 5 * strlen(num);
+ alarm(timelim < 30 ? 30 : timelim);
+ if ((child = fork()) == 0) {
+ /*
+ * ignore this stuff for aborts
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ sleep(2);
+ nw = write(dn, num, lt = strlen(num));
+ exit(nw != lt);
+ }
+ /*
+ * open line - will return on carrier
+ */
+ if ((FD = open(DV, 2)) < 0) {
+ if (errno == EIO)
+ printf("lost carrier...");
+ else
+ printf("dialup line open failed...");
+ alarm(0);
+ kill(child, SIGKILL);
+ close(dn);
+ return (0);
+ }
+ alarm(0);
+ ioctl(dn, TIOCHPCL, 0);
+ signal(SIGALRM, SIG_DFL);
+ while ((nw = wait(&lt)) != child && nw != -1)
+ ;
+ fflush(stdout);
+ close(dn);
+ if (lt != 0) {
+ close(FD);
+ return (0);
+ }
+ return (1);
+}
+
+void
+alarmtr()
+{
+ alarm(0);
+ longjmp(jmpbuf, 1);
+}
+
+/*
+ * Insurance, for some reason we don't seem to be
+ * hanging up...
+ */
+dn_disconnect()
+{
+
+ sleep(2);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
+
+dn_abort()
+{
+
+ sleep(2);
+ if (child > 0)
+ kill(child, SIGKILL);
+ if (dn > 0)
+ close(dn);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
diff --git a/usr.bin/tip/aculib/hayes.c b/usr.bin/tip/aculib/hayes.c
new file mode 100644
index 0000000..a2196f4
--- /dev/null
+++ b/usr.bin/tip/aculib/hayes.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hayes.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Hayes Modem
+ * (based on the old VenTel driver).
+ * The modem is expected to be strapped for "echo".
+ * Also, the switches enabling the DTR and CD lines
+ * must be set correctly.
+ * NOTICE:
+ * The easy way to hang up a modem is always simply to
+ * clear the DTR signal. However, if the +++ sequence
+ * (which switches the modem back to local mode) is sent
+ * before modem is hung up, removal of the DTR signal
+ * has no effect (except that it prevents the modem from
+ * recognizing commands).
+ * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984)
+ */
+/*
+ * TODO:
+ * It is probably not a good idea to switch the modem
+ * state between 'verbose' and terse (status messages).
+ * This should be kicked out and we should use verbose
+ * mode only. This would make it consistent with normal
+ * interactive use thru the command 'tip dialer'.
+ */
+#include "tip.h"
+
+#define min(a,b) ((a < b) ? a : b)
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+static char gobble();
+#define DUMBUFLEN 40
+static char dumbuf[DUMBUFLEN];
+
+#define DIALING 1
+#define IDLE 2
+#define CONNECTED 3
+#define FAILED 4
+static int state = IDLE;
+
+hay_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+ register int connected = 0;
+ char dummy;
+#ifdef ACULOG
+ char line[80];
+#endif
+ if (hay_sync() == 0) /* make sure we can talk to the modem */
+ return(0);
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+ ioctl(FD, TIOCHPCL, 0);
+ ioctl(FD, TIOCFLUSH, 0); /* get rid of garbage */
+ write(FD, "ATv0\r", 5); /* tell modem to use short status codes */
+ gobble("\r");
+ gobble("\r");
+ write(FD, "ATTD", 4); /* send dial command */
+ write(FD, num, strlen(num));
+ state = DIALING;
+ write(FD, "\r", 1);
+ connected = 0;
+ if (gobble("\r")) {
+ if ((dummy = gobble("01234")) != '1')
+ error_rep(dummy);
+ else
+ connected = 1;
+ }
+ if (connected)
+ state = CONNECTED;
+ else {
+ state = FAILED;
+ return (connected); /* lets get out of here.. */
+ }
+ ioctl(FD, TIOCFLUSH, 0);
+#ifdef ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "hayes", line);
+ }
+#endif
+ if (timeout)
+ hay_disconnect(); /* insurance */
+ return (connected);
+}
+
+
+hay_disconnect()
+{
+ char c;
+ int len, rlen;
+
+ /* first hang up the modem*/
+#ifdef DEBUG
+ printf("\rdisconnecting modem....\n\r");
+#endif
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ goodbye();
+}
+
+hay_abort()
+{
+
+ char c;
+
+ write(FD, "\r", 1); /* send anything to abort the call */
+ hay_disconnect();
+}
+
+static void
+sigALRM()
+{
+
+ printf("\07timeout waiting for reply\n\r");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static char
+gobble(match)
+ register char *match;
+{
+ char c;
+ sig_t f;
+ int i, status = 0;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+#ifdef DEBUG
+ printf("\ngobble: waiting for %s\n", match);
+#endif
+ do {
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ printf("%c 0x%x ", c, c);
+#endif
+ for (i = 0; i < strlen(match); i++)
+ if (c == match[i])
+ status = c;
+ } while (status == 0);
+ signal(SIGALRM, SIG_DFL);
+#ifdef DEBUG
+ printf("\n");
+#endif
+ return (status);
+}
+
+error_rep(c)
+ register char c;
+{
+ printf("\n\r");
+ switch (c) {
+
+ case '0':
+ printf("OK");
+ break;
+
+ case '1':
+ printf("CONNECT");
+ break;
+
+ case '2':
+ printf("RING");
+ break;
+
+ case '3':
+ printf("NO CARRIER");
+ break;
+
+ case '4':
+ printf("ERROR in input");
+ break;
+
+ case '5':
+ printf("CONNECT 1200");
+ break;
+
+ default:
+ printf("Unknown Modem error: %c (0x%x)", c, c);
+ }
+ printf("\n\r");
+ return;
+}
+
+/*
+ * set modem back to normal verbose status codes.
+ */
+goodbye()
+{
+ int len, rlen;
+ char c;
+
+ ioctl(FD, TIOCFLUSH, &len); /* get rid of trash */
+ if (hay_sync()) {
+ sleep(1);
+#ifndef DEBUG
+ ioctl(FD, TIOCFLUSH, 0);
+#endif
+ write(FD, "ATH0\r", 5); /* insurance */
+#ifndef DEBUG
+ c = gobble("03");
+ if (c != '0' && c != '3') {
+ printf("cannot hang up modem\n\r");
+ printf("please use 'tip dialer' to make sure the line is hung up\n\r");
+ }
+#endif
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+#ifdef DEBUG
+ printf("goodbye1: len=%d -- ", len);
+ rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
+ dumbuf[rlen] = '\0';
+ printf("read (%d): %s\r\n", rlen, dumbuf);
+#endif
+ write(FD, "ATv1\r", 5);
+ sleep(1);
+#ifdef DEBUG
+ ioctl(FD, FIONREAD, &len);
+ printf("goodbye2: len=%d -- ", len);
+ rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
+ dumbuf[rlen] = '\0';
+ printf("read (%d): %s\r\n", rlen, dumbuf);
+#endif
+ }
+ ioctl(FD, TIOCFLUSH, 0); /* clear the input buffer */
+ ioctl(FD, TIOCCDTR, 0); /* clear DTR (insurance) */
+ close(FD);
+}
+
+#define MAXRETRY 5
+
+hay_sync()
+{
+ int len, retry = 0;
+
+ while (retry++ <= MAXRETRY) {
+ write(FD, "AT\r", 3);
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, dumbuf, min(len, DUMBUFLEN));
+ if (index(dumbuf, '0') ||
+ (index(dumbuf, 'O') && index(dumbuf, 'K')))
+ return(1);
+#ifdef DEBUG
+ dumbuf[len] = '\0';
+ printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry);
+#endif
+ }
+ ioctl(FD, TIOCCDTR, 0);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ printf("Cannot synchronize with hayes...\n\r");
+ return(0);
+}
diff --git a/usr.bin/tip/aculib/t3000.c b/usr.bin/tip/aculib/t3000.c
new file mode 100644
index 0000000..5e07359
--- /dev/null
+++ b/usr.bin/tip/aculib/t3000.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)t3000.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Telebit T3000 modem.
+ * Derived from Courier driver.
+ */
+#include "tip.h"
+#include <stdio.h>
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+static int t3000_sync();
+
+t3000_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+#ifdef ACULOG
+ char line[80];
+#endif
+ static int t3000_connect(), t3000_swallow();
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ ioctl(FD, TIOCHPCL, 0);
+ /*
+ * Get in synch.
+ */
+ if (!t3000_sync()) {
+badsynch:
+ printf("can't synchronize with t3000\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "t3000", "can't synch up");
+#endif
+ return (0);
+ }
+ t3000_write(FD, "AT E0\r", 6); /* turn off echoing */
+ sleep(1);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ t3000_verbose_read();
+#endif
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ t3000_write(FD, "AT E0 H0 Q0 X4 V1\r", 18);
+ if (!t3000_swallow("\r\nOK\r\n"))
+ goto badsynch;
+ fflush(stdout);
+ t3000_write(FD, "AT D", 4);
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+ t3000_write(FD, num, strlen(num));
+ t3000_write(FD, "\r", 1);
+ connected = t3000_connect();
+#ifdef ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "t3000", line);
+ }
+#endif
+ if (timeout)
+ t3000_disconnect();
+ return (connected);
+}
+
+t3000_disconnect()
+{
+ /* first hang up the modem*/
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ t3000_sync(); /* reset */
+ close(FD);
+}
+
+t3000_abort()
+{
+ t3000_write(FD, "\r", 1); /* send anything to abort the call */
+ t3000_disconnect();
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+t3000_swallow(match)
+ register char *match;
+ {
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ return (1);
+ }
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c == *match++);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush(stdout);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ return (0);
+}
+
+#ifndef B19200 /* XXX */
+#define B19200 EXTA
+#define B38400 EXTB
+#endif
+
+struct tbaud_msg {
+ char *msg;
+ int baud;
+ int baud2;
+} tbaud_msg[] = {
+ "", B300, 0,
+ " 1200", B1200, 0,
+ " 2400", B2400, 0,
+ " 4800", B4800, 0,
+ " 9600", B9600, 0,
+ " 14400", B19200, B9600,
+ " 19200", B19200, B9600,
+ " 38400", B38400, B9600,
+ " 57600", B38400, B9600,
+ " 7512", B9600, 0,
+ " 1275", B2400, 0,
+ " 7200", B9600, 0,
+ " 12000", B19200, B9600,
+ 0, 0, 0,
+};
+
+static int
+t3000_connect()
+{
+ char c;
+ int nc, nl, n;
+ struct sgttyb sb;
+ char dialer_buf[64];
+ struct tbaud_msg *bm;
+ sig_t f;
+
+ if (t3000_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (t3000_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ for (bm = tbaud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg,
+ dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (ioctl(FD, TIOCGETP, &sb) < 0) {
+ perror("TIOCGETP");
+ goto error;
+ }
+ sb.sg_ispeed = sb.sg_ospeed = bm->baud;
+ if (ioctl(FD, TIOCSETP, &sb) < 0) {
+ if (bm->baud2) {
+ sb.sg_ispeed =
+ sb.sg_ospeed =
+ bm->baud2;
+ if (ioctl(FD,
+ TIOCSETP,
+ &sb) >= 0)
+ goto isok;
+ }
+ perror("TIOCSETP");
+ goto error;
+ }
+isok:
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the t3000 in sync.
+ */
+static int
+t3000_sync()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ t3000_write(FD, "\rAT Z\r", 6); /* reset modem */
+ bzero(buf, sizeof(buf));
+ sleep(2);
+ ioctl(FD, FIONREAD, &len);
+#if 1
+if (len == 0) len = 1;
+#endif
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf[len] = '\0';
+ printf("t3000_sync: (\"%s\")\n\r", buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ sleep(1);
+ t3000_write(FD, "+++", 3);
+ sleep(1);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ t3000_write(FD, "\rAT Z\r", 6);
+ return (0);
+}
+
+t3000_write(fd, cp, n)
+int fd;
+char *cp;
+int n;
+{
+ struct sgttyb sb;
+
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ write(1, cp, n);
+#endif
+ ioctl(fd, TIOCGETP, &sb);
+ ioctl(fd, TIOCSETP, &sb);
+ t3000_nap();
+ for ( ; n-- ; cp++) {
+ write(fd, cp, 1);
+ ioctl(fd, TIOCGETP, &sb);
+ ioctl(fd, TIOCSETP, &sb);
+ t3000_nap();
+ }
+}
+
+#ifdef DEBUG
+t3000_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+/*
+ * Code stolen from /usr/src/lib/libc/gen/sleep.c
+ */
+#define mask(s) (1<<((s)-1))
+#define setvec(vec, a) \
+ vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
+
+static napms = 50; /* Give the t3000 50 milliseconds between characters */
+
+static int ringring;
+
+t3000_nap()
+{
+
+ static void t3000_napx();
+ int omask;
+ struct itimerval itv, oitv;
+ register struct itimerval *itp = &itv;
+ struct sigvec vec, ovec;
+
+ timerclear(&itp->it_interval);
+ timerclear(&itp->it_value);
+ if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
+ return;
+ setvec(ovec, SIG_DFL);
+ omask = sigblock(mask(SIGALRM));
+ itp->it_value.tv_sec = napms/1000;
+ itp->it_value.tv_usec = ((napms%1000)*1000);
+ setvec(vec, t3000_napx);
+ ringring = 0;
+ (void) sigvec(SIGALRM, &vec, &ovec);
+ (void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
+ while (!ringring)
+ sigpause(omask &~ mask(SIGALRM));
+ (void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
+ (void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);
+ (void) sigsetmask(omask);
+}
+
+static void
+t3000_napx()
+{
+ ringring = 1;
+}
diff --git a/usr.bin/tip/aculib/v3451.c b/usr.bin/tip/aculib/v3451.c
new file mode 100644
index 0000000..1623a58
--- /dev/null
+++ b/usr.bin/tip/aculib/v3451.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v3451.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Vadic 3451 Modem
+ */
+#include "tip.h"
+
+static jmp_buf Sjbuf;
+
+v3451_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ sig_t func;
+ int ok;
+ int slow = number(value(BAUDRATE)) < 1200, rw = 2;
+ char phone[50];
+#ifdef ACULOG
+ char line[80];
+#endif
+ static int expect();
+ static void vawrite();
+
+ /*
+ * Get in synch
+ */
+ vawrite("I\r", 1 + slow);
+ vawrite("I\r", 1 + slow);
+ vawrite("I\r", 1 + slow);
+ vawrite("\005\r", 2 + slow);
+ if (!expect("READY")) {
+ printf("can't synchronize with vadic 3451\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "vadic", "can't synch up");
+#endif
+ return (0);
+ }
+ ioctl(FD, TIOCHPCL, 0);
+ sleep(1);
+ vawrite("D\r", 2 + slow);
+ if (!expect("NUMBER?")) {
+ printf("Vadic will not accept dial command\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "vadic", "will not accept dial");
+#endif
+ return (0);
+ }
+ strcpy(phone, num);
+ strcat(phone, "\r");
+ vawrite(phone, 1 + slow);
+ if (!expect(phone)) {
+ printf("Vadic will not accept phone number\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "vadic", "will not accept number");
+#endif
+ return (0);
+ }
+ func = signal(SIGINT,SIG_IGN);
+ /*
+ * You cannot interrupt the Vadic when its dialing;
+ * even dropping DTR does not work (definitely a
+ * brain damaged design).
+ */
+ vawrite("\r", 1 + slow);
+ vawrite("\r", 1 + slow);
+ if (!expect("DIALING:")) {
+ printf("Vadic failed to dial\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "vadic", "failed to dial");
+#endif
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ ok = expect("ON LINE");
+ signal(SIGINT, func);
+ if (!ok) {
+ printf("call failed\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "vadic", "call failed");
+#endif
+ return (0);
+ }
+ ioctl(FD, TIOCFLUSH, &rw);
+ return (1);
+}
+
+v3451_disconnect()
+{
+
+ close(FD);
+}
+
+v3451_abort()
+{
+
+ close(FD);
+}
+
+static void
+vawrite(cp, delay)
+ register char *cp;
+ int delay;
+{
+
+ for (; *cp; sleep(delay), cp++)
+ write(FD, cp, 1);
+}
+
+static
+expect(cp)
+ register char *cp;
+{
+ char buf[300];
+ register char *rp = buf;
+ int timeout = 30, online = 0;
+ static int notin();
+ static void alarmtr();
+
+ if (strcmp(cp, "\"\"") == 0)
+ return (1);
+ *rp = 0;
+ /*
+ * If we are waiting for the Vadic to complete
+ * dialing and get a connection, allow more time
+ * Unfortunately, the Vadic times out 24 seconds after
+ * the last digit is dialed
+ */
+ online = strcmp(cp, "ON LINE") == 0;
+ if (online)
+ timeout = number(value(DIALTIMEOUT));
+ signal(SIGALRM, alarmtr);
+ if (setjmp(Sjbuf))
+ return (0);
+ alarm(timeout);
+ while (notin(cp, buf) && rp < buf + sizeof (buf) - 1) {
+ if (online && notin("FAILED CALL", buf) == 0)
+ return (0);
+ if (read(FD, rp, 1) < 0) {
+ alarm(0);
+ return (0);
+ }
+ if (*rp &= 0177)
+ rp++;
+ *rp = '\0';
+ }
+ alarm(0);
+ return (1);
+}
+
+static void
+alarmtr()
+{
+ longjmp(Sjbuf, 1);
+}
+
+static int
+notin(sh, lg)
+ char *sh, *lg;
+{
+ static int prefix();
+
+ for (; *lg; lg++)
+ if (prefix(sh, lg))
+ return (0);
+ return (1);
+}
+
+static
+prefix(s1, s2)
+ register char *s1, *s2;
+{
+ register char c;
+
+ while ((c = *s1++) == *s2++)
+ if (c == '\0')
+ return (1);
+ return (c == '\0');
+}
diff --git a/usr.bin/tip/aculib/v831.c b/usr.bin/tip/aculib/v831.c
new file mode 100644
index 0000000..38aa230
--- /dev/null
+++ b/usr.bin/tip/aculib/v831.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v831.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for dialing up on Vadic 831
+ */
+#include "tip.h"
+
+int v831_abort();
+static void alarmtr();
+extern int errno;
+
+static jmp_buf jmpbuf;
+static int child = -1;
+
+v831_dialer(num, acu)
+ char *num, *acu;
+{
+ int status, pid, connected = 1;
+ register int timelim;
+ static int dialit();
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+#ifdef DEBUG
+ printf ("(acu=%s)\n", acu);
+#endif
+ if ((AC = open(acu, O_RDWR)) < 0) {
+ if (errno == EBUSY)
+ printf("line busy...");
+ else
+ printf("acu open error...");
+ return (0);
+ }
+ if (setjmp(jmpbuf)) {
+ kill(child, SIGKILL);
+ close(AC);
+ return (0);
+ }
+ signal(SIGALRM, alarmtr);
+ timelim = 5 * strlen(num);
+ alarm(timelim < 30 ? 30 : timelim);
+ if ((child = fork()) == 0) {
+ /*
+ * ignore this stuff for aborts
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ sleep(2);
+ exit(dialit(num, acu) != 'A');
+ }
+ /*
+ * open line - will return on carrier
+ */
+ if ((FD = open(DV, O_RDWR)) < 0) {
+#ifdef DEBUG
+ printf("(after open, errno=%d)\n", errno);
+#endif
+ if (errno == EIO)
+ printf("lost carrier...");
+ else
+ printf("dialup line open failed...");
+ alarm(0);
+ kill(child, SIGKILL);
+ close(AC);
+ return (0);
+ }
+ alarm(0);
+#ifdef notdef
+ ioctl(AC, TIOCHPCL, 0);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ while ((pid = wait(&status)) != child && pid != -1)
+ ;
+ if (status) {
+ close(AC);
+ return (0);
+ }
+ return (1);
+}
+
+static void
+alarmtr()
+{
+ alarm(0);
+ longjmp(jmpbuf, 1);
+}
+
+/*
+ * Insurance, for some reason we don't seem to be
+ * hanging up...
+ */
+v831_disconnect()
+{
+ struct sgttyb cntrl;
+
+ sleep(2);
+#ifdef DEBUG
+ printf("[disconnect: FD=%d]\n", FD);
+#endif
+ if (FD > 0) {
+ ioctl(FD, TIOCCDTR, 0);
+ ioctl(FD, TIOCGETP, &cntrl);
+ cntrl.sg_ispeed = cntrl.sg_ospeed = 0;
+ ioctl(FD, TIOCSETP, &cntrl);
+ ioctl(FD, TIOCNXCL, (struct sgttyb *)NULL);
+ }
+ close(FD);
+}
+
+v831_abort()
+{
+
+#ifdef DEBUG
+ printf("[abort: AC=%d]\n", AC);
+#endif
+ sleep(2);
+ if (child > 0)
+ kill(child, SIGKILL);
+ if (AC > 0)
+ ioctl(FD, TIOCNXCL, (struct sgttyb *)NULL);
+ close(AC);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
+
+/*
+ * Sigh, this probably must be changed at each site.
+ */
+struct vaconfig {
+ char *vc_name;
+ char vc_rack;
+ char vc_modem;
+} vaconfig[] = {
+ { "/dev/cua0",'4','0' },
+ { "/dev/cua1",'4','1' },
+ { 0 }
+};
+
+#define pc(x) (c = x, write(AC,&c,1))
+#define ABORT 01
+#define SI 017
+#define STX 02
+#define ETX 03
+
+static int
+dialit(phonenum, acu)
+ register char *phonenum;
+ char *acu;
+{
+ register struct vaconfig *vp;
+ struct sgttyb cntrl;
+ char c;
+ int i, two = 2;
+ static char *sanitize();
+
+ phonenum = sanitize(phonenum);
+#ifdef DEBUG
+ printf ("(dial phonenum=%s)\n", phonenum);
+#endif
+ if (*phonenum == '<' && phonenum[1] == 0)
+ return ('Z');
+ for (vp = vaconfig; vp->vc_name; vp++)
+ if (strcmp(vp->vc_name, acu) == 0)
+ break;
+ if (vp->vc_name == 0) {
+ printf("Unable to locate dialer (%s)\n", acu);
+ return ('K');
+ }
+ ioctl(AC, TIOCGETP, &cntrl);
+ cntrl.sg_ispeed = cntrl.sg_ospeed = B2400;
+ cntrl.sg_flags = RAW | EVENP | ODDP;
+ ioctl(AC, TIOCSETP, &cntrl);
+ ioctl(AC, TIOCFLUSH, &two);
+ pc(STX);
+ pc(vp->vc_rack);
+ pc(vp->vc_modem);
+ while (*phonenum && *phonenum != '<')
+ pc(*phonenum++);
+ pc(SI);
+ pc(ETX);
+ sleep(1);
+ i = read(AC, &c, 1);
+#ifdef DEBUG
+ printf("read %d chars, char=%c, errno %d\n", i, c, errno);
+#endif
+ if (i != 1)
+ c = 'M';
+ if (c == 'B' || c == 'G') {
+ char cc, oc = c;
+
+ pc(ABORT);
+ read(AC, &cc, 1);
+#ifdef DEBUG
+ printf("abort response=%c\n", cc);
+#endif
+ c = oc;
+ v831_disconnect();
+ }
+ close(AC);
+#ifdef DEBUG
+ printf("dialit: returns %c\n", c);
+#endif
+ return (c);
+}
+
+static char *
+sanitize(s)
+ register char *s;
+{
+ static char buf[128];
+ register char *cp;
+
+ for (cp = buf; *s; s++) {
+ if (!isdigit(*s) && *s == '<' && *s != '_')
+ continue;
+ if (*s == '_')
+ *s = '=';
+ *cp++ = *s;
+ }
+ *cp++ = 0;
+ return (buf);
+}
diff --git a/usr.bin/tip/aculib/ventel.c b/usr.bin/tip/aculib/ventel.c
new file mode 100644
index 0000000..28b0d28
--- /dev/null
+++ b/usr.bin/tip/aculib/ventel.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ventel.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Ventel Modem
+ * The Ventel is expected to be strapped for local echo (just like uucp)
+ */
+#include "tip.h"
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * some sleep calls have been replaced by this macro
+ * because some ventel modems require two <cr>s in less than
+ * a second in order to 'wake up'... yes, it is dirty...
+ */
+#define delay(num,denom) busyloop(CPUSPEED*num/denom)
+#define CPUSPEED 1000000 /* VAX 780 is 1MIPS */
+#define DELAY(n) { register long N = (n); while (--N > 0); }
+busyloop(n) { DELAY(n); }
+
+ven_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+ register int connected = 0;
+ char *msg, *index(), line[80];
+ static int gobble(), vensync();
+ static void echo();
+
+ /*
+ * Get in synch with a couple of carriage returns
+ */
+ if (!vensync(FD)) {
+ printf("can't synchronize with ventel\n");
+#ifdef ACULOG
+ logent(value(HOST), num, "ventel", "can't synch up");
+#endif
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+ ioctl(FD, TIOCHPCL, 0);
+ echo("#k$\r$\n$D$I$A$L$:$ ");
+ for (cp = num; *cp; cp++) {
+ delay(1, 10);
+ write(FD, cp, 1);
+ }
+ delay(1, 10);
+ write(FD, "\r", 1);
+ gobble('\n', line);
+ if (gobble('\n', line))
+ connected = gobble('!', line);
+ ioctl(FD, TIOCFLUSH);
+#ifdef ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "ventel", line);
+ }
+#endif
+ if (timeout)
+ ven_disconnect(); /* insurance */
+ if (connected || timeout || !boolean(value(VERBOSE)))
+ return (connected);
+ /* call failed, parse response for user */
+ cp = index(line, '\r');
+ if (cp)
+ *cp = '\0';
+ for (cp = line; cp = index(cp, ' '); cp++)
+ if (cp[1] == ' ')
+ break;
+ if (cp) {
+ while (*cp == ' ')
+ cp++;
+ msg = cp;
+ while (*cp) {
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ cp++;
+ }
+ printf("%s...", msg);
+ }
+ return (connected);
+}
+
+ven_disconnect()
+{
+
+ close(FD);
+}
+
+ven_abort()
+{
+
+ write(FD, "\03", 1);
+ close(FD);
+}
+
+static void
+echo(s)
+ register char *s;
+{
+ char c;
+
+ while (c = *s++) switch (c) {
+
+ case '$':
+ read(FD, &c, 1);
+ s++;
+ break;
+
+ case '#':
+ c = *s++;
+ write(FD, &c, 1);
+ break;
+
+ default:
+ write(FD, &c, 1);
+ read(FD, &c, 1);
+ }
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+gobble(match, response)
+ register char match;
+ char response[];
+{
+ register char *cp = response;
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ *cp = '\0';
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, cp, 1);
+ alarm(0);
+ c = (*cp++ &= 0177);
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c != '\n' && c != match);
+ signal(SIGALRM, SIG_DFL);
+ *cp = '\0';
+ return (c == match);
+}
+
+#define min(a,b) ((a)>(b)?(b):(a))
+/*
+ * This convoluted piece of code attempts to get
+ * the ventel in sync. If you don't have FIONREAD
+ * there are gory ways to simulate this.
+ */
+static int
+vensync(fd)
+{
+ int already = 0, nread;
+ char buf[60];
+
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected, and insure a consistent state
+ * to start from.
+ *
+ * If you don't have the ioctl calls to diddle directly
+ * with DTR, you can always try setting the baud rate to 0.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ while (already < MAXRETRY) {
+ /*
+ * After reseting the modem, send it two \r's to
+ * autobaud on. Make sure to delay between them
+ * so the modem can frame the incoming characters.
+ */
+ write(fd, "\r", 1);
+ delay(1,10);
+ write(fd, "\r", 1);
+ sleep(2);
+ if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) {
+ perror("tip: ioctl");
+ continue;
+ }
+ while (nread > 0) {
+ read(fd, buf, min(nread, 60));
+ if ((buf[nread - 1] & 0177) == '$')
+ return (1);
+ nread -= min(nread, 60);
+ }
+ sleep(1);
+ already++;
+ }
+ return (0);
+}
+
diff --git a/usr.bin/tip/acutab.c b/usr.bin/tip/acutab.c
new file mode 100644
index 0000000..112b43e
--- /dev/null
+++ b/usr.bin/tip/acutab.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)acutab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+extern int df02_dialer(), df03_dialer(), df_disconnect(), df_abort(),
+ biz31f_dialer(), biz31_disconnect(), biz31_abort(),
+ biz31w_dialer(),
+ biz22f_dialer(), biz22_disconnect(), biz22_abort(),
+ biz22w_dialer(),
+ ven_dialer(), ven_disconnect(), ven_abort(),
+ hay_dialer(), hay_disconnect(), hay_abort(),
+ cour_dialer(), cour_disconnect(), cour_abort(),
+ t3000_dialer(), t3000_disconnect(), t3000_abort(),
+ v3451_dialer(), v3451_disconnect(), v3451_abort(),
+ v831_dialer(), v831_disconnect(), v831_abort(),
+ dn_dialer(), dn_disconnect(), dn_abort();
+
+acu_t acutable[] = {
+#if BIZ1031
+ "biz31f", biz31f_dialer, biz31_disconnect, biz31_abort,
+ "biz31w", biz31w_dialer, biz31_disconnect, biz31_abort,
+#endif
+#if BIZ1022
+ "biz22f", biz22f_dialer, biz22_disconnect, biz22_abort,
+ "biz22w", biz22w_dialer, biz22_disconnect, biz22_abort,
+#endif
+#if DF02
+ "df02", df02_dialer, df_disconnect, df_abort,
+#endif
+#if DF03
+ "df03", df03_dialer, df_disconnect, df_abort,
+#endif
+#if DN11
+ "dn11", dn_dialer, dn_disconnect, dn_abort,
+#endif
+#ifdef VENTEL
+ "ventel",ven_dialer, ven_disconnect, ven_abort,
+#endif
+#ifdef HAYES
+ "hayes",hay_dialer, hay_disconnect, hay_abort,
+#endif
+#ifdef COURIER
+ "courier",cour_dialer, cour_disconnect, cour_abort,
+#endif
+#ifdef T3000
+ "t3000",t3000_dialer, t3000_disconnect, t3000_abort,
+#endif
+#ifdef V3451
+#ifndef V831
+ "vadic",v3451_dialer, v3451_disconnect, v3451_abort,
+#endif
+ "v3451",v3451_dialer, v3451_disconnect, v3451_abort,
+#endif
+#ifdef V831
+#ifndef V3451
+ "vadic",v831_dialer, v831_disconnect, v831_abort,
+#endif
+ "v831",v831_dialer, v831_disconnect, v831_abort,
+#endif
+ 0, 0, 0, 0
+};
+
diff --git a/usr.bin/tip/cmds.c b/usr.bin/tip/cmds.c
new file mode 100644
index 0000000..63bfee2
--- /dev/null
+++ b/usr.bin/tip/cmds.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * tip
+ *
+ * miscellaneous commands
+ */
+
+int quant[] = { 60, 60, 24 };
+
+char null = '\0';
+char *sep[] = { "second", "minute", "hour" };
+static char *argv[10]; /* argument vector for take and put */
+
+void timeout(); /* timeout function called on alarm */
+void stopsnd(); /* SIGINT handler during file transfers */
+void intcopy(); /* interrupt routine for file transfers */
+
+/*
+ * FTP - remote ==> local
+ * get a file from the remote host
+ */
+getfl(c)
+ char c;
+{
+ char buf[256], *cp, *expand();
+
+ putchar(c);
+ /*
+ * get the UNIX receiving file's name
+ */
+ if (prompt("Local file name? ", copyname))
+ return;
+ cp = expand(copyname);
+ if ((sfd = creat(cp, 0666)) < 0) {
+ printf("\r\n%s: cannot creat\r\n", copyname);
+ return;
+ }
+
+ /*
+ * collect parameters
+ */
+ if (prompt("List command for remote system? ", buf)) {
+ unlink(copyname);
+ return;
+ }
+ transfer(buf, sfd, value(EOFREAD));
+}
+
+/*
+ * Cu-like take command
+ */
+cu_take(cc)
+ char cc;
+{
+ int fd, argc;
+ char line[BUFSIZ], *expand(), *cp;
+
+ if (prompt("[take] ", copyname))
+ return;
+ if ((argc = args(copyname, argv)) < 1 || argc > 2) {
+ printf("usage: <take> from [to]\r\n");
+ return;
+ }
+ if (argc == 1)
+ argv[1] = argv[0];
+ cp = expand(argv[1]);
+ if ((fd = creat(cp, 0666)) < 0) {
+ printf("\r\n%s: cannot create\r\n", argv[1]);
+ return;
+ }
+ sprintf(line, "cat %s;echo \01", argv[0]);
+ transfer(line, fd, "\01");
+}
+
+static jmp_buf intbuf;
+/*
+ * Bulk transfer routine --
+ * used by getfl(), cu_take(), and pipefile()
+ */
+transfer(buf, fd, eofchars)
+ char *buf, *eofchars;
+{
+ register int ct;
+ char c, buffer[BUFSIZ];
+ register char *p = buffer;
+ register int cnt, eof;
+ time_t start;
+ sig_t f;
+ char r;
+
+ pwrite(FD, buf, size(buf));
+ quit = 0;
+ kill(pid, SIGIOT);
+ read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */
+
+ /*
+ * finish command
+ */
+ r = '\r';
+ pwrite(FD, &r, 1);
+ do
+ read(FD, &c, 1);
+ while ((c&0177) != '\n');
+ ioctl(0, TIOCSETC, &defchars);
+
+ (void) setjmp(intbuf);
+ f = signal(SIGINT, intcopy);
+ start = time(0);
+ for (ct = 0; !quit;) {
+ eof = read(FD, &c, 1) <= 0;
+ c &= 0177;
+ if (quit)
+ continue;
+ if (eof || any(c, eofchars))
+ break;
+ if (c == 0)
+ continue; /* ignore nulls */
+ if (c == '\r')
+ continue;
+ *p++ = c;
+
+ if (c == '\n' && boolean(value(VERBOSE)))
+ printf("\r%d", ++ct);
+ if ((cnt = (p-buffer)) == number(value(FRAMESIZE))) {
+ if (write(fd, buffer, cnt) != cnt) {
+ printf("\r\nwrite error\r\n");
+ quit = 1;
+ }
+ p = buffer;
+ }
+ }
+ if (cnt = (p-buffer))
+ if (write(fd, buffer, cnt) != cnt)
+ printf("\r\nwrite error\r\n");
+
+ if (boolean(value(VERBOSE)))
+ prtime(" lines transferred in ", time(0)-start);
+ ioctl(0, TIOCSETC, &tchars);
+ write(fildes[1], (char *)&ccc, 1);
+ signal(SIGINT, f);
+ close(fd);
+}
+
+/*
+ * FTP - remote ==> local process
+ * send remote input to local process via pipe
+ */
+pipefile()
+{
+ int cpid, pdes[2];
+ char buf[256];
+ int status, p;
+ extern int errno;
+
+ if (prompt("Local command? ", buf))
+ return;
+
+ if (pipe(pdes)) {
+ printf("can't establish pipe\r\n");
+ return;
+ }
+
+ if ((cpid = fork()) < 0) {
+ printf("can't fork!\r\n");
+ return;
+ } else if (cpid) {
+ if (prompt("List command for remote system? ", buf)) {
+ close(pdes[0]), close(pdes[1]);
+ kill (cpid, SIGKILL);
+ } else {
+ close(pdes[0]);
+ signal(SIGPIPE, intcopy);
+ transfer(buf, pdes[1], value(EOFREAD));
+ signal(SIGPIPE, SIG_DFL);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ }
+ } else {
+ register int f;
+
+ dup2(pdes[0], 0);
+ close(pdes[0]);
+ for (f = 3; f < 20; f++)
+ close(f);
+ execute(buf);
+ printf("can't execl!\r\n");
+ exit(0);
+ }
+}
+
+/*
+ * Interrupt service routine for FTP
+ */
+void
+stopsnd()
+{
+
+ stop = 1;
+ signal(SIGINT, SIG_IGN);
+}
+
+/*
+ * FTP - local ==> remote
+ * send local file to remote host
+ * terminate transmission with pseudo EOF sequence
+ */
+sendfile(cc)
+ char cc;
+{
+ FILE *fd;
+ char *fnamex;
+ char *expand();
+
+ putchar(cc);
+ /*
+ * get file name
+ */
+ if (prompt("Local file name? ", fname))
+ return;
+
+ /*
+ * look up file
+ */
+ fnamex = expand(fname);
+ if ((fd = fopen(fnamex, "r")) == NULL) {
+ printf("%s: cannot open\r\n", fname);
+ return;
+ }
+ transmit(fd, value(EOFWRITE), NULL);
+ if (!boolean(value(ECHOCHECK))) {
+ struct sgttyb buf;
+
+ ioctl(FD, TIOCGETP, &buf); /* this does a */
+ ioctl(FD, TIOCSETP, &buf); /* wflushtty */
+ }
+}
+
+/*
+ * Bulk transfer routine to remote host --
+ * used by sendfile() and cu_put()
+ */
+transmit(fd, eofchars, command)
+ FILE *fd;
+ char *eofchars, *command;
+{
+ char *pc, lastc;
+ int c, ccount, lcount;
+ time_t start_t, stop_t;
+ sig_t f;
+
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ stop = 0;
+ f = signal(SIGINT, stopsnd);
+ ioctl(0, TIOCSETC, &defchars);
+ read(repdes[0], (char *)&ccc, 1);
+ if (command != NULL) {
+ for (pc = command; *pc; pc++)
+ send(*pc);
+ if (boolean(value(ECHOCHECK)))
+ read(FD, (char *)&c, 1); /* trailing \n */
+ else {
+ struct sgttyb buf;
+
+ ioctl(FD, TIOCGETP, &buf); /* this does a */
+ ioctl(FD, TIOCSETP, &buf); /* wflushtty */
+ sleep(5); /* wait for remote stty to take effect */
+ }
+ }
+ lcount = 0;
+ lastc = '\0';
+ start_t = time(0);
+ while (1) {
+ ccount = 0;
+ do {
+ c = getc(fd);
+ if (stop)
+ goto out;
+ if (c == EOF)
+ goto out;
+ if (c == 0177 && !boolean(value(RAWFTP)))
+ continue;
+ lastc = c;
+ if (c < 040) {
+ if (c == '\n') {
+ if (!boolean(value(RAWFTP)))
+ c = '\r';
+ }
+ else if (c == '\t') {
+ if (!boolean(value(RAWFTP))) {
+ if (boolean(value(TABEXPAND))) {
+ send(' ');
+ while ((++ccount % 8) != 0)
+ send(' ');
+ continue;
+ }
+ }
+ } else
+ if (!boolean(value(RAWFTP)))
+ continue;
+ }
+ send(c);
+ } while (c != '\r' && !boolean(value(RAWFTP)));
+ if (boolean(value(VERBOSE)))
+ printf("\r%d", ++lcount);
+ if (boolean(value(ECHOCHECK))) {
+ timedout = 0;
+ alarm((int)value(ETIMEOUT));
+ do { /* wait for prompt */
+ read(FD, (char *)&c, 1);
+ if (timedout || stop) {
+ if (timedout)
+ printf("\r\ntimed out at eol\r\n");
+ alarm(0);
+ goto out;
+ }
+ } while ((c&0177) != character(value(PROMPT)));
+ alarm(0);
+ }
+ }
+out:
+ if (lastc != '\n' && !boolean(value(RAWFTP)))
+ send('\r');
+ for (pc = eofchars; *pc; pc++)
+ send(*pc);
+ stop_t = time(0);
+ fclose(fd);
+ signal(SIGINT, f);
+ if (boolean(value(VERBOSE)))
+ if (boolean(value(RAWFTP)))
+ prtime(" chars transferred in ", stop_t-start_t);
+ else
+ prtime(" lines transferred in ", stop_t-start_t);
+ write(fildes[1], (char *)&ccc, 1);
+ ioctl(0, TIOCSETC, &tchars);
+}
+
+/*
+ * Cu-like put command
+ */
+cu_put(cc)
+ char cc;
+{
+ FILE *fd;
+ char line[BUFSIZ];
+ int argc;
+ char *expand();
+ char *copynamex;
+
+ if (prompt("[put] ", copyname))
+ return;
+ if ((argc = args(copyname, argv)) < 1 || argc > 2) {
+ printf("usage: <put> from [to]\r\n");
+ return;
+ }
+ if (argc == 1)
+ argv[1] = argv[0];
+ copynamex = expand(argv[0]);
+ if ((fd = fopen(copynamex, "r")) == NULL) {
+ printf("%s: cannot open\r\n", copynamex);
+ return;
+ }
+ if (boolean(value(ECHOCHECK)))
+ sprintf(line, "cat>%s\r", argv[1]);
+ else
+ sprintf(line, "stty -echo;cat>%s;stty echo\r", argv[1]);
+ transmit(fd, "\04", line);
+}
+
+/*
+ * FTP - send single character
+ * wait for echo & handle timeout
+ */
+send(c)
+ char c;
+{
+ char cc;
+ int retry = 0;
+
+ cc = c;
+ pwrite(FD, &cc, 1);
+#ifdef notdef
+ if (number(value(CDELAY)) > 0 && c != '\r')
+ nap(number(value(CDELAY)));
+#endif
+ if (!boolean(value(ECHOCHECK))) {
+#ifdef notdef
+ if (number(value(LDELAY)) > 0 && c == '\r')
+ nap(number(value(LDELAY)));
+#endif
+ return;
+ }
+tryagain:
+ timedout = 0;
+ alarm((int)value(ETIMEOUT));
+ read(FD, &cc, 1);
+ alarm(0);
+ if (timedout) {
+ printf("\r\ntimeout error (%s)\r\n", ctrl(c));
+ if (retry++ > 3)
+ return;
+ pwrite(FD, &null, 1); /* poke it */
+ goto tryagain;
+ }
+}
+
+void
+timeout()
+{
+ signal(SIGALRM, timeout);
+ timedout = 1;
+}
+
+/*
+ * Stolen from consh() -- puts a remote file on the output of a local command.
+ * Identical to consh() except for where stdout goes.
+ */
+pipeout(c)
+{
+ char buf[256];
+ int cpid, status, p;
+ time_t start;
+
+ putchar(c);
+ if (prompt("Local command? ", buf))
+ return;
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ ioctl(0, TIOCSETC, &defchars);
+ read(repdes[0], (char *)&ccc, 1);
+ /*
+ * Set up file descriptors in the child and
+ * let it go...
+ */
+ if ((cpid = fork()) < 0)
+ printf("can't fork!\r\n");
+ else if (cpid) {
+ start = time(0);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ } else {
+ register int i;
+
+ dup2(FD, 1);
+ for (i = 3; i < 20; i++)
+ close(i);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ execute(buf);
+ printf("can't find `%s'\r\n", buf);
+ exit(0);
+ }
+ if (boolean(value(VERBOSE)))
+ prtime("away for ", time(0)-start);
+ write(fildes[1], (char *)&ccc, 1);
+ ioctl(0, TIOCSETC, &tchars);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+}
+
+#ifdef CONNECT
+/*
+ * Fork a program with:
+ * 0 <-> remote tty in
+ * 1 <-> remote tty out
+ * 2 <-> local tty out
+ */
+consh(c)
+{
+ char buf[256];
+ int cpid, status, p;
+ time_t start;
+
+ putchar(c);
+ if (prompt("Local command? ", buf))
+ return;
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ ioctl(0, TIOCSETC, &defchars);
+ read(repdes[0], (char *)&ccc, 1);
+ /*
+ * Set up file descriptors in the child and
+ * let it go...
+ */
+ if ((cpid = fork()) < 0)
+ printf("can't fork!\r\n");
+ else if (cpid) {
+ start = time(0);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ } else {
+ register int i;
+
+ dup2(FD, 0);
+ dup2(3, 1);
+ for (i = 3; i < 20; i++)
+ close(i);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ execute(buf);
+ printf("can't find `%s'\r\n", buf);
+ exit(0);
+ }
+ if (boolean(value(VERBOSE)))
+ prtime("away for ", time(0)-start);
+ write(fildes[1], (char *)&ccc, 1);
+ ioctl(0, TIOCSETC, &tchars);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+}
+#endif
+
+/*
+ * Escape to local shell
+ */
+shell()
+{
+ int shpid, status;
+ extern char **environ;
+ char *cp;
+
+ printf("[sh]\r\n");
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ unraw();
+ if (shpid = fork()) {
+ while (shpid != wait(&status));
+ raw();
+ printf("\r\n!\r\n");
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ return;
+ } else {
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ if ((cp = rindex(value(SHELL), '/')) == NULL)
+ cp = value(SHELL);
+ else
+ cp++;
+ shell_uid();
+ execl(value(SHELL), cp, 0);
+ printf("\r\ncan't execl!\r\n");
+ exit(1);
+ }
+}
+
+/*
+ * TIPIN portion of scripting
+ * initiate the conversation with TIPOUT
+ */
+setscript()
+{
+ char c;
+ /*
+ * enable TIPOUT side for dialogue
+ */
+ kill(pid, SIGEMT);
+ if (boolean(value(SCRIPT)))
+ write(fildes[1], value(RECORD), size(value(RECORD)));
+ write(fildes[1], "\n", 1);
+ /*
+ * wait for TIPOUT to finish
+ */
+ read(repdes[0], &c, 1);
+ if (c == 'n')
+ printf("can't create %s\r\n", value(RECORD));
+}
+
+/*
+ * Change current working directory of
+ * local portion of tip
+ */
+chdirectory()
+{
+ char dirname[80];
+ register char *cp = dirname;
+
+ if (prompt("[cd] ", dirname)) {
+ if (stoprompt)
+ return;
+ cp = value(HOME);
+ }
+ if (chdir(cp) < 0)
+ printf("%s: bad directory\r\n", cp);
+ printf("!\r\n");
+}
+
+tipabort(msg)
+ char *msg;
+{
+
+ kill(pid, SIGTERM);
+ disconnect(msg);
+ if (msg != NOSTR)
+ printf("\r\n%s", msg);
+ printf("\r\n[EOT]\r\n");
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ unraw();
+ exit(0);
+}
+
+finish()
+{
+ char *dismsg;
+
+ if ((dismsg = value(DISCONNECT)) != NOSTR) {
+ write(FD, dismsg, strlen(dismsg));
+ sleep(5);
+ }
+ tipabort(NOSTR);
+}
+
+void
+intcopy()
+{
+ raw();
+ quit = 1;
+ longjmp(intbuf, 1);
+}
+
+execute(s)
+ char *s;
+{
+ register char *cp;
+
+ if ((cp = rindex(value(SHELL), '/')) == NULL)
+ cp = value(SHELL);
+ else
+ cp++;
+ shell_uid();
+ execl(value(SHELL), cp, "-c", s, 0);
+}
+
+args(buf, a)
+ char *buf, *a[];
+{
+ register char *p = buf, *start;
+ register char **parg = a;
+ register int n = 0;
+
+ do {
+ while (*p && (*p == ' ' || *p == '\t'))
+ p++;
+ start = p;
+ if (*p)
+ *parg = p;
+ while (*p && (*p != ' ' && *p != '\t'))
+ p++;
+ if (p != start)
+ parg++, n++;
+ if (*p)
+ *p++ = '\0';
+ } while (*p);
+
+ return(n);
+}
+
+prtime(s, a)
+ char *s;
+ time_t a;
+{
+ register i;
+ int nums[3];
+
+ for (i = 0; i < 3; i++) {
+ nums[i] = (int)(a % quant[i]);
+ a /= quant[i];
+ }
+ printf("%s", s);
+ while (--i >= 0)
+ if (nums[i] || i == 0 && nums[1] == 0 && nums[2] == 0)
+ printf("%d %s%c ", nums[i], sep[i],
+ nums[i] == 1 ? '\0' : 's');
+ printf("\r\n!\r\n");
+}
+
+variable()
+{
+ char buf[256];
+
+ if (prompt("[set] ", buf))
+ return;
+ vlex(buf);
+ if (vtable[BEAUTIFY].v_access&CHANGED) {
+ vtable[BEAUTIFY].v_access &= ~CHANGED;
+ kill(pid, SIGSYS);
+ }
+ if (vtable[SCRIPT].v_access&CHANGED) {
+ vtable[SCRIPT].v_access &= ~CHANGED;
+ setscript();
+ /*
+ * So that "set record=blah script" doesn't
+ * cause two transactions to occur.
+ */
+ if (vtable[RECORD].v_access&CHANGED)
+ vtable[RECORD].v_access &= ~CHANGED;
+ }
+ if (vtable[RECORD].v_access&CHANGED) {
+ vtable[RECORD].v_access &= ~CHANGED;
+ if (boolean(value(SCRIPT)))
+ setscript();
+ }
+ if (vtable[TAND].v_access&CHANGED) {
+ vtable[TAND].v_access &= ~CHANGED;
+ if (boolean(value(TAND)))
+ tandem("on");
+ else
+ tandem("off");
+ }
+ if (vtable[LECHO].v_access&CHANGED) {
+ vtable[LECHO].v_access &= ~CHANGED;
+ HD = boolean(value(LECHO));
+ }
+ if (vtable[PARITY].v_access&CHANGED) {
+ vtable[PARITY].v_access &= ~CHANGED;
+ setparity();
+ }
+}
+
+/*
+ * Turn tandem mode on or off for remote tty.
+ */
+tandem(option)
+ char *option;
+{
+ struct sgttyb rmtty;
+
+ ioctl(FD, TIOCGETP, &rmtty);
+ if (strcmp(option,"on") == 0) {
+ rmtty.sg_flags |= TANDEM;
+ arg.sg_flags |= TANDEM;
+ } else {
+ rmtty.sg_flags &= ~TANDEM;
+ arg.sg_flags &= ~TANDEM;
+ }
+ ioctl(FD, TIOCSETP, &rmtty);
+ ioctl(0, TIOCSETP, &arg);
+}
+
+/*
+ * Send a break.
+ */
+genbrk()
+{
+
+ ioctl(FD, TIOCSBRK, NULL);
+ sleep(1);
+ ioctl(FD, TIOCCBRK, NULL);
+}
+
+/*
+ * Suspend tip
+ */
+suspend(c)
+ char c;
+{
+
+ unraw();
+ kill(c == CTRL('y') ? getpid() : 0, SIGTSTP);
+ raw();
+}
+
+/*
+ * expand a file name if it includes shell meta characters
+ */
+
+char *
+expand(name)
+ char name[];
+{
+ static char xname[BUFSIZ];
+ char cmdbuf[BUFSIZ];
+ register int pid, l, rc;
+ register char *cp, *Shell;
+ int s, pivec[2], (*sigint)();
+
+ if (!anyof(name, "~{[*?$`'\"\\"))
+ return(name);
+ /* sigint = signal(SIGINT, SIG_IGN); */
+ if (pipe(pivec) < 0) {
+ perror("pipe");
+ /* signal(SIGINT, sigint) */
+ return(name);
+ }
+ sprintf(cmdbuf, "echo %s", name);
+ if ((pid = vfork()) == 0) {
+ Shell = value(SHELL);
+ if (Shell == NOSTR)
+ Shell = _PATH_BSHELL;
+ close(pivec[0]);
+ close(1);
+ dup(pivec[1]);
+ close(pivec[1]);
+ close(2);
+ shell_uid();
+ execl(Shell, Shell, "-c", cmdbuf, 0);
+ _exit(1);
+ }
+ if (pid == -1) {
+ perror("fork");
+ close(pivec[0]);
+ close(pivec[1]);
+ return(NOSTR);
+ }
+ close(pivec[1]);
+ l = read(pivec[0], xname, BUFSIZ);
+ close(pivec[0]);
+ while (wait(&s) != pid);
+ ;
+ s &= 0377;
+ if (s != 0 && s != SIGPIPE) {
+ fprintf(stderr, "\"Echo\" failed\n");
+ return(NOSTR);
+ }
+ if (l < 0) {
+ perror("read");
+ return(NOSTR);
+ }
+ if (l == 0) {
+ fprintf(stderr, "\"%s\": No match\n", name);
+ return(NOSTR);
+ }
+ if (l == BUFSIZ) {
+ fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
+ return(NOSTR);
+ }
+ xname[l] = 0;
+ for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
+ ;
+ *++cp = '\0';
+ return(xname);
+}
+
+/*
+ * Are any of the characters in the two strings the same?
+ */
+
+anyof(s1, s2)
+ register char *s1, *s2;
+{
+ register int c;
+
+ while (c = *s1++)
+ if (any(c, s2))
+ return(1);
+ return(0);
+}
diff --git a/usr.bin/tip/cmdtab.c b/usr.bin/tip/cmdtab.c
new file mode 100644
index 0000000..f6bcb60
--- /dev/null
+++ b/usr.bin/tip/cmdtab.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+extern int shell(), getfl(), sendfile(), chdirectory();
+extern int finish(), help(), pipefile(), pipeout(), consh(), variable();
+extern int cu_take(), cu_put(), dollar(), genbrk(), suspend();
+
+esctable_t etable[] = {
+ { '!', NORM, "shell", shell },
+ { '<', NORM, "receive file from remote host", getfl },
+ { '>', NORM, "send file to remote host", sendfile },
+ { 't', NORM, "take file from remote UNIX", cu_take },
+ { 'p', NORM, "put file to remote UNIX", cu_put },
+ { '|', NORM, "pipe remote file", pipefile },
+ { '$', NORM, "pipe local command to remote host", pipeout },
+#ifdef CONNECT
+ { 'C', NORM, "connect program to remote host",consh },
+#endif
+ { 'c', NORM, "change directory", chdirectory },
+ { '.', NORM, "exit from tip", finish },
+ {CTRL('d'),NORM,"exit from tip", finish },
+ {CTRL('y'),NORM,"suspend tip (local+remote)", suspend },
+ {CTRL('z'),NORM,"suspend tip (local only)", suspend },
+ { 's', NORM, "set variable", variable },
+ { '?', NORM, "get this summary", help },
+ { '#', NORM, "send break", genbrk },
+ { 0, 0, 0 }
+};
diff --git a/usr.bin/tip/cu.c b/usr.bin/tip/cu.c
new file mode 100644
index 0000000..fae2b3c
--- /dev/null
+++ b/usr.bin/tip/cu.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cu.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+void cleanup();
+
+/*
+ * Botch the interface to look like cu's
+ */
+cumain(argc, argv)
+ char *argv[];
+{
+ register int i;
+ static char sbuf[12];
+
+ if (argc < 2) {
+ printf("usage: cu telno [-t] [-s speed] [-a acu] [-l line] [-#]\n");
+ exit(8);
+ }
+ CU = DV = NOSTR;
+ BR = DEFBR;
+ for (; argc > 1; argv++, argc--) {
+ if (argv[1][0] != '-')
+ PN = argv[1];
+ else switch (argv[1][1]) {
+
+ case 't':
+ HW = 1, DU = -1;
+ --argc;
+ continue;
+
+ case 'a':
+ CU = argv[2]; ++argv; --argc;
+ break;
+
+ case 's':
+ if (argc < 3 || speed(atoi(argv[2])) == 0) {
+ fprintf(stderr, "cu: unsupported speed %s\n",
+ argv[2]);
+ exit(3);
+ }
+ BR = atoi(argv[2]); ++argv; --argc;
+ break;
+
+ case 'l':
+ DV = argv[2]; ++argv; --argc;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (CU)
+ CU[strlen(CU)-1] = argv[1][1];
+ if (DV)
+ DV[strlen(DV)-1] = argv[1][1];
+ break;
+
+ default:
+ printf("Bad flag %s", argv[1]);
+ break;
+ }
+ }
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGHUP, cleanup);
+ signal(SIGTERM, cleanup);
+
+ /*
+ * The "cu" host name is used to define the
+ * attributes of the generic dialer.
+ */
+ (void)sprintf(sbuf, "cu%d", BR);
+ if ((i = hunt(sbuf)) == 0) {
+ printf("all ports busy\n");
+ exit(3);
+ }
+ if (i == -1) {
+ printf("link down\n");
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+ setbuf(stdout, NULL);
+ loginit();
+ user_uid();
+ vinit();
+ setparity("none");
+ boolean(value(VERBOSE)) = 0;
+ if (HW)
+ ttysetup(speed(BR));
+ if (connect()) {
+ printf("Connect failed\n");
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ exit(1);
+ }
+ if (!HW)
+ ttysetup(speed(BR));
+}
diff --git a/usr.bin/tip/hunt.c b/usr.bin/tip/hunt.c
new file mode 100644
index 0000000..650ac26
--- /dev/null
+++ b/usr.bin/tip/hunt.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+extern char *getremote();
+extern char *rindex();
+
+static jmp_buf deadline;
+static int deadfl;
+
+void
+dead()
+{
+ deadfl = 1;
+ longjmp(deadline, 1);
+}
+
+hunt(name)
+ char *name;
+{
+ register char *cp;
+ sig_t f;
+
+ f = signal(SIGALRM, dead);
+ while (cp = getremote(name)) {
+ deadfl = 0;
+ uucplock = rindex(cp, '/')+1;
+ if (uu_lock(uucplock) < 0)
+ continue;
+ /*
+ * Straight through call units, such as the BIZCOMP,
+ * VADIC and the DF, must indicate they're hardwired in
+ * order to get an open file descriptor placed in FD.
+ * Otherwise, as for a DN-11, the open will have to
+ * be done in the "open" routine.
+ */
+ if (!HW)
+ break;
+ if (setjmp(deadline) == 0) {
+ alarm(10);
+ FD = open(cp, O_RDWR);
+ }
+ alarm(0);
+ if (FD < 0) {
+ perror(cp);
+ deadfl = 1;
+ }
+ if (!deadfl) {
+ ioctl(FD, TIOCEXCL, 0);
+ ioctl(FD, TIOCHPCL, 0);
+ signal(SIGALRM, SIG_DFL);
+ return ((int)cp);
+ }
+ (void)uu_unlock(uucplock);
+ }
+ signal(SIGALRM, f);
+ return (deadfl ? -1 : (int)cp);
+}
diff --git a/usr.bin/tip/libacu/Makefile b/usr.bin/tip/libacu/Makefile
new file mode 100644
index 0000000..77a95cd
--- /dev/null
+++ b/usr.bin/tip/libacu/Makefile
@@ -0,0 +1,12 @@
+LIB= acu
+CFLAGS+= -I${.CURDIR}/../tip
+SRCS= acucommon.c biz22.c courier.c df.c dn11.c hayes.c \
+ multitech.c t3000.c tod.c unidialer.c v3451.c v831.c ventel.c
+NOPROFILE= yes
+
+$(OBJS): ${.CURDIR}/../tip/tipconf.h
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/usr.bin/tip/libacu/acucommon.c b/usr.bin/tip/libacu/acucommon.c
new file mode 100644
index 0000000..479e9a3
--- /dev/null
+++ b/usr.bin/tip/libacu/acucommon.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)acucommon.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Courier modem.
+ * Derived from Hayes driver.
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+#if HAVE_SELECT
+#include <sys/types.h>
+#include <sys/times.h>
+#include <unistd.h>
+
+void acu_nap (unsigned int how_long)
+{
+ struct timeval t;
+ t.tv_usec = (how_long % 1000) * 1000;
+ t.tv_sec = how_long / 1000;
+ (void) select (0, NULL, NULL, NULL, &t);
+}
+
+#elif HAVE_USLEEP
+void acu_nap (unsigned int how_long)
+{
+ (void) usleep (how_long * 1000);
+}
+
+#else
+
+/*
+ * Code stolen from /usr/src/lib/libc/gen/sleep.c
+ */
+#define mask(s) (1<<((s)-1))
+#define setvec(vec, a) \
+ vec.sv_handler = a; vec.sv_mask = vec.sv_onstack = 0
+
+static int ringring;
+
+static void acunap_napx()
+{
+ ringring = 1;
+}
+
+void acu_nap (unsigned int how_long)
+{
+ int omask;
+ struct itimerval itv, oitv;
+ register struct itimerval *itp = &itv;
+ struct sigvec vec, ovec;
+
+ timerclear(&itp->it_interval);
+ timerclear(&itp->it_value);
+ if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
+ return;
+ setvec(ovec, SIG_DFL);
+ omask = sigblock(mask(SIGALRM));
+ itp->it_value.tv_sec = how_long / 1000;
+ itp->it_value.tv_usec = ((how_long % 1000) * 1000);
+ setvec(vec, acunap_napx);
+ ringring = 0;
+ (void) sigvec(SIGALRM, &vec, &ovec);
+ (void) setitimer(ITIMER_REAL, itp, (struct itimerval *)0);
+ while (!ringring)
+ sigpause(omask &~ mask(SIGALRM));
+ (void) sigvec(SIGALRM, &ovec, (struct sigvec *)0);
+ (void) setitimer(ITIMER_REAL, &oitv, (struct itimerval *)0);
+ (void) sigsetmask(omask);
+}
+
+#endif /* HAVE_USLEEP */
+
+void acu_hw_flow_control (hw_flow_control)
+{
+#if HAVE_TERMIOS
+ struct termios t;
+ if (tcgetattr (FD, &t) == 0) {
+ if (hw_flow_control)
+ t.c_cflag |= CRTSCTS;
+ else
+ t.c_cflag &= ~CRTSCTS;
+ tcsetattr (FD, TCSANOW, &t);
+ }
+#endif /* HAVE_TERMIOS */
+}
+
+int acu_flush ()
+{
+#ifdef TIOCFLUSH
+ int flags = 0;
+ return (ioctl (FD, TIOCFLUSH, &flags) == 0); /* flush any clutter */
+#elif !HAVE_TERMIOS
+ struct sgttyb buf;
+ return (ioctl (FD, TIOCGETP, &buf) == 0 && ioctl (FD, TIOCSETP, &buf) == 0);
+#endif
+}
+
+int acu_getspeed ()
+{
+#if HAVE_TERMIOS
+ struct termios term;
+ tcgetattr (FD, &term);
+ return (term.c_ospeed);
+#else /* HAVE_TERMIOS */
+ struct sgttyb buf;
+ ioctl (FD, TIOCGETP, &buf);
+ return (buf.sg_ospeed);
+#endif
+}
+
+int acu_setspeed (int speed)
+{
+ int rc = 0;
+#if HAVE_TERMIOS
+ struct termios term;
+ if (tcgetattr (FD, &term) == 0) {
+#ifndef _POSIX_SOURCE
+ cfsetspeed (&term, speed);
+#else
+ cfsetispeed (&term, speed);
+ cfsetospeed (&term, speed);
+#endif
+ if (tcsetattr (FD, TCSANOW, &term) == 0)
+ ++rc;
+ }
+#else /* HAVE TERMIOS */
+ struct sgttyb sb;
+ if (ioctl(FD, TIOCGETP, &sb) < 0) {
+ perror("TIOCGETP");
+ }
+ else {
+ sb.sg_ispeed = sb.sg_ospeed = speed;
+ if (ioctl(FD, TIOCSETP, &sb) < 0) {
+ perror("TIOCSETP");
+ }
+ else
+ ++rc;
+ }
+#endif /* HAVE TERMIOS */
+ return (rc);
+}
+
+void acu_hupcl ()
+{
+#if HAVE_TERMIOS
+ struct termios term;
+ tcgetattr (FD, &term);
+ term.c_cflag |= HUPCL;
+ tcsetattr (FD, TCSANOW, &term);
+#elif defined(TIOCHPCL)
+ ioctl(FD, TIOCHPCL, 0);
+#endif
+}
+
+/* end of acucommon.c */
diff --git a/usr.bin/tip/libacu/acucommon.h b/usr.bin/tip/libacu/acucommon.h
new file mode 100644
index 0000000..50d28cd
--- /dev/null
+++ b/usr.bin/tip/libacu/acucommon.h
@@ -0,0 +1,6 @@
+void acu_nap (unsigned int how_long);
+void acu_hw_flow_control (int hw_flow_control);
+int acu_flush ();
+void acu_hupcl ();
+int acu_setspeed (int speed);
+/* end of acucommon.h */
diff --git a/usr.bin/tip/libacu/biz22.c b/usr.bin/tip/libacu/biz22.c
new file mode 100644
index 0000000..63fb11c
--- /dev/null
+++ b/usr.bin/tip/libacu/biz22.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)biz22.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+#define DISCONNECT_CMD "\20\04" /* disconnection string */
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * Dial up on a BIZCOMP Model 1022 with either
+ * tone dialing (mod = "V")
+ * pulse dialing (mod = "W")
+ */
+static int
+biz_dialer(num, mod)
+ char *num, *mod;
+{
+ register int connected = 0;
+ char cbuf[40];
+ static int cmd(), detect();
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ /*
+ * Disable auto-answer and configure for tone/pulse
+ * dialing
+ */
+ if (cmd("\02K\r")) {
+ printf("can't initialize bizcomp...");
+ return (0);
+ }
+ strcpy(cbuf, "\02.\r");
+ cbuf[1] = *mod;
+ if (cmd(cbuf)) {
+ printf("can't set dialing mode...");
+ return (0);
+ }
+ strcpy(cbuf, "\02D");
+ strcat(cbuf, num);
+ strcat(cbuf, "\r");
+ write(FD, cbuf, strlen(cbuf));
+ if (!detect("7\r")) {
+ printf("can't get dial tone...");
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("ringing...");
+ /*
+ * The reply from the BIZCOMP should be:
+ * 2 \r or 7 \r failure
+ * 1 \r success
+ */
+ connected = detect("1\r");
+#if ACULOG
+ if (timeout) {
+ char line[80];
+
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "biz1022", line);
+ }
+#endif
+ if (timeout)
+ biz22_disconnect(); /* insurance */
+ return (connected);
+}
+
+biz22w_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "W"));
+}
+
+biz22f_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "V"));
+}
+
+biz22_disconnect()
+{
+ int rw = 2;
+
+ write(FD, DISCONNECT_CMD, 4);
+ sleep(2);
+ ioctl(FD, TIOCFLUSH, &rw);
+}
+
+biz22_abort()
+{
+
+ write(FD, "\02", 1);
+}
+
+static void
+sigALRM()
+{
+
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+cmd(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ write(FD, s, strlen(s));
+ f = signal(SIGALRM, sigALRM);
+ if (setjmp(timeoutbuf)) {
+ biz22_abort();
+ signal(SIGALRM, f);
+ return (1);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ signal(SIGALRM, f);
+ c &= 0177;
+ return (c != '\r');
+}
+
+static int
+detect(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ while (*s) {
+ if (setjmp(timeoutbuf)) {
+ biz22_abort();
+ break;
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+ if (c != *s++)
+ return (0);
+ }
+ signal(SIGALRM, f);
+ return (timeout == 0);
+}
diff --git a/usr.bin/tip/libacu/biz31.c b/usr.bin/tip/libacu/biz31.c
new file mode 100644
index 0000000..9553a6d
--- /dev/null
+++ b/usr.bin/tip/libacu/biz31.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)biz31.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+#define MAXRETRY 3 /* sync up retry count */
+#define DISCONNECT_CMD "\21\25\11\24" /* disconnection string */
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * Dial up on a BIZCOMP Model 1031 with either
+ * tone dialing (mod = "f")
+ * pulse dialing (mod = "w")
+ */
+static int
+biz_dialer(num, mod)
+ char *num, *mod;
+{
+ register int connected = 0;
+
+ if (!bizsync(FD)) {
+ logent(value(HOST), "", "biz", "out of sync");
+ printf("bizcomp out of sync\n");
+ delock(uucplock);
+ exit(0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ echo("#\rk$\r$\n"); /* disable auto-answer */
+ echo("$>$.$ #\r"); /* tone/pulse dialing */
+ echo(mod);
+ echo("$\r$\n");
+ echo("$>$.$ #\re$ "); /* disconnection sequence */
+ echo(DISCONNECT_CMD);
+ echo("\r$\n$\r$\n");
+ echo("$>$.$ #\rr$ "); /* repeat dial */
+ echo(num);
+ echo("\r$\n");
+ if (boolean(value(VERBOSE)))
+ printf("ringing...");
+ /*
+ * The reply from the BIZCOMP should be:
+ * `^G NO CONNECTION\r\n^G\r\n' failure
+ * ` CONNECTION\r\n^G' success
+ */
+ connected = detect(" ");
+#if ACULOG
+ if (timeout) {
+ char line[80];
+
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "biz", line);
+ }
+#endif
+ if (!connected)
+ flush(" NO CONNECTION\r\n\07\r\n");
+ else
+ flush("CONNECTION\r\n\07");
+ if (timeout)
+ biz31_disconnect(); /* insurance */
+ return (connected);
+}
+
+biz31w_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "w"));
+}
+
+biz31f_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (biz_dialer(num, "f"));
+}
+
+biz31_disconnect()
+{
+
+ write(FD, DISCONNECT_CMD, 4);
+ sleep(2);
+ ioctl(FD, TIOCFLUSH);
+}
+
+biz31_abort()
+{
+
+ write(FD, "\33", 1);
+}
+
+static int
+echo(s)
+ register char *s;
+{
+ char c;
+
+ while (c = *s++) switch (c) {
+
+ case '$':
+ read(FD, &c, 1);
+ s++;
+ break;
+
+ case '#':
+ c = *s++;
+ write(FD, &c, 1);
+ break;
+
+ default:
+ write(FD, &c, 1);
+ read(FD, &c, 1);
+ }
+}
+
+static void
+sigALRM()
+{
+
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+detect(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ while (*s) {
+ if (setjmp(timeoutbuf)) {
+ printf("\07timeout waiting for reply\n");
+ biz31_abort();
+ break;
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ if (c != *s++)
+ break;
+ }
+ signal(SIGALRM, f);
+ return (timeout == 0);
+}
+
+static int
+flush(s)
+ register char *s;
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ while (*s++) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(10);
+ read(FD, &c, 1);
+ alarm(0);
+ }
+ signal(SIGALRM, f);
+ timeout = 0; /* guard against disconnection */
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the bizcomp in sync. If you don't have the capacity or nread
+ * call there are gory ways to simulate this.
+ */
+static int
+bizsync(fd)
+{
+#ifdef FIOCAPACITY
+ struct capacity b;
+# define chars(b) ((b).cp_nbytes)
+# define IOCTL FIOCAPACITY
+#endif
+#ifdef FIONREAD
+ long b;
+# define chars(b) (b)
+# define IOCTL FIONREAD
+#endif
+ register int already = 0;
+ char buf[10];
+
+retry:
+ if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0 && chars(b) > 0)
+ ioctl(fd, TIOCFLUSH);
+ write(fd, "\rp>\r", 4);
+ sleep(1);
+ if (ioctl(fd, IOCTL, (caddr_t)&b) >= 0) {
+ if (chars(b) != 10) {
+ nono:
+ if (already > MAXRETRY)
+ return (0);
+ write(fd, DISCONNECT_CMD, 4);
+ sleep(2);
+ already++;
+ goto retry;
+ } else {
+ read(fd, buf, 10);
+ if (strncmp(buf, "p >\r\n\r\n>", 8))
+ goto nono;
+ }
+ }
+ return (1);
+}
diff --git a/usr.bin/tip/libacu/courier.c b/usr.bin/tip/libacu/courier.c
new file mode 100644
index 0000000..84c710f
--- /dev/null
+++ b/usr.bin/tip/libacu/courier.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)courier.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Courier modem.
+ * Derived from Hayes driver.
+ */
+#include "tipconf.h"
+#include "tip.h"
+#include "acucommon.h"
+#include <stdio.h>
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+static int coursync();
+
+cour_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+#if ACULOG
+ char line[80];
+#endif
+ static int cour_connect(), cour_swallow();
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ acu_hupcl ();
+
+ /*
+ * Get in synch.
+ */
+ if (!coursync()) {
+badsynch:
+ printf("can't synchronize with courier\n");
+#if ACULOG
+ logent(value(HOST), num, "courier", "can't synch up");
+#endif
+ return (0);
+ }
+ cour_write(FD, "AT E0\r", 6); /* turn off echoing */
+ sleep(1);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ cour_verbose_read();
+#endif
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ cour_write(FD, "AT C1 E0 H0 Q0 X6 V1\r", 21);
+ if (!cour_swallow("\r\nOK\r\n"))
+ goto badsynch;
+ fflush(stdout);
+ cour_write(FD, "AT D", 4);
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+ cour_write(FD, num, strlen(num));
+ cour_write(FD, "\r", 1);
+ connected = cour_connect();
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "cour", line);
+ }
+#endif
+ if (timeout)
+ cour_disconnect();
+ return (connected);
+}
+
+cour_disconnect()
+{
+ /* first hang up the modem*/
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ coursync(); /* reset */
+ close(FD);
+}
+
+cour_abort()
+{
+ cour_write(FD, "\r", 1); /* send anything to abort the call */
+ cour_disconnect();
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+cour_swallow(match)
+ register char *match;
+ {
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ return (1);
+ }
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c == *match++);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush(stdout);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ return (0);
+}
+
+struct baud_msg {
+ char *msg;
+ int baud;
+} baud_msg[] = {
+ "", B300,
+ " 1200", B1200,
+ " 2400", B2400,
+ " 9600", B9600,
+ " 9600/ARQ", B9600,
+ 0, 0,
+};
+
+static int
+cour_connect()
+{
+ char c;
+ int nc, nl, n;
+ char dialer_buf[64];
+ struct baud_msg *bm;
+ sig_t f;
+
+ if (cour_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (cour_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ for (bm = baud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg,
+ dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (!acu_setspeed(bm->baud))
+ goto error;
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the courier in sync.
+ */
+static int
+coursync()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ cour_write(FD, "\rAT Z\r", 6); /* reset modem */
+ bzero(buf, sizeof(buf));
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf[len] = '\0';
+ printf("coursync: (\"%s\")\n\r", buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ sleep(1);
+ cour_write(FD, "+++", 3);
+ sleep(1);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ cour_write(FD, "\rAT Z\r", 6);
+ return (0);
+}
+
+cour_write(fd, cp, n)
+int fd;
+char *cp;
+int n;
+{
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ write(1, cp, n);
+#endif
+ acu_flush ();
+ cour_nap();
+ for ( ; n-- ; cp++) {
+ write(fd, cp, 1);
+ acu_flush ();
+ cour_nap();
+ }
+}
+
+#ifdef DEBUG
+cour_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+cour_nap()
+{
+ acu_nap (50);
+}
+
+/* end of courier.c */
diff --git a/usr.bin/tip/libacu/df.c b/usr.bin/tip/libacu/df.c
new file mode 100644
index 0000000..fc60ad3
--- /dev/null
+++ b/usr.bin/tip/libacu/df.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)df.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Dial the DF02-AC or DF03-AC
+ */
+
+#include "tipconf.h"
+#include "tip.h"
+
+static jmp_buf Sjbuf;
+static void timeout();
+
+df02_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (df_dialer(num, acu, 0));
+}
+
+df03_dialer(num, acu)
+ char *num, *acu;
+{
+
+ return (df_dialer(num, acu, 1));
+}
+
+df_dialer(num, acu, df03)
+ char *num, *acu;
+ int df03;
+{
+ register int f = FD;
+ int speed = 0, rw = 2;
+ char c = '\0';
+
+ acu_hupcl ();
+
+ if (setjmp(Sjbuf)) {
+ printf("connection timed out\r\n");
+ df_disconnect();
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+#ifdef TIOCMSET
+ if (df03) {
+ int st = TIOCM_ST; /* secondary Transmit flag */
+
+ if ((speed = acu_getspeed ()) != B1200) { /* must dial at 1200 baud */
+ acu_setspeed (B1200);
+ ioctl(f, TIOCMBIC, &st); /* clear ST for 300 baud */
+ } else
+ ioctl(f, TIOCMBIS, &st); /* set ST for 1200 baud */
+ }
+#endif
+ signal(SIGALRM, timeout);
+ alarm(5 * strlen(num) + 10);
+ ioctl(f, TIOCFLUSH, &rw);
+ write(f, "\001", 1);
+ sleep(1);
+ write(f, "\002", 1);
+ write(f, num, strlen(num));
+ read(f, &c, 1);
+#ifdef TIOCMSET
+ if (df03 && speed) {
+ acu_setspeed (speed);
+ }
+#endif
+ return (c == 'A');
+}
+
+df_disconnect()
+{
+ int rw = 2;
+
+ write(FD, "\001", 1);
+ sleep(1);
+ ioctl(FD, TIOCFLUSH, &rw);
+}
+
+
+df_abort()
+{
+
+ df_disconnect();
+}
+
+
+static void
+timeout()
+{
+
+ longjmp(Sjbuf, 1);
+}
diff --git a/usr.bin/tip/libacu/dn11.c b/usr.bin/tip/libacu/dn11.c
new file mode 100644
index 0000000..52749da
--- /dev/null
+++ b/usr.bin/tip/libacu/dn11.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dn11.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for dialing up on DN-11
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+int dn_abort();
+void alarmtr();
+static jmp_buf jmpbuf;
+static int child = -1, dn;
+
+dn_dialer(num, acu)
+ char *num, *acu;
+{
+ extern errno;
+ char *p, *q, phone[40];
+ int lt, nw, connected = 1;
+ register int timelim;
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+ if ((dn = open(acu, 1)) < 0) {
+ if (errno == EBUSY)
+ printf("line busy...");
+ else
+ printf("acu open error...");
+ return (0);
+ }
+ if (setjmp(jmpbuf)) {
+ kill(child, SIGKILL);
+ close(dn);
+ return (0);
+ }
+ signal(SIGALRM, alarmtr);
+ timelim = 5 * strlen(num);
+ alarm(timelim < 30 ? 30 : timelim);
+ if ((child = fork()) == 0) {
+ /*
+ * ignore this stuff for aborts
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ sleep(2);
+ nw = write(dn, num, lt = strlen(num));
+ exit(nw != lt);
+ }
+ /*
+ * open line - will return on carrier
+ */
+ if ((FD = open(DV, 2)) < 0) {
+ if (errno == EIO)
+ printf("lost carrier...");
+ else
+ printf("dialup line open failed...");
+ alarm(0);
+ kill(child, SIGKILL);
+ close(dn);
+ return (0);
+ }
+ alarm(0);
+
+#if HAVE_TERMIOS
+ {
+ struct termios term;
+ tcgetattr (dn, &term);
+ term.c_cflag |= HUPCL;
+ tcsetattr (dn, TCSANOW, &term);
+ }
+#elif defined(TIOCHPCL)
+ ioctl(dn, TIOCHPCL, 0);
+#endif
+
+ signal(SIGALRM, SIG_DFL);
+ while ((nw = wait(&lt)) != child && nw != -1)
+ ;
+ fflush(stdout);
+ close(dn);
+ if (lt != 0) {
+ close(FD);
+ return (0);
+ }
+ return (1);
+}
+
+void
+alarmtr()
+{
+ alarm(0);
+ longjmp(jmpbuf, 1);
+}
+
+/*
+ * Insurance, for some reason we don't seem to be
+ * hanging up...
+ */
+dn_disconnect()
+{
+
+ sleep(2);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
+
+dn_abort()
+{
+
+ sleep(2);
+ if (child > 0)
+ kill(child, SIGKILL);
+ if (dn > 0)
+ close(dn);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
diff --git a/usr.bin/tip/libacu/hayes.c b/usr.bin/tip/libacu/hayes.c
new file mode 100644
index 0000000..dcce6a5
--- /dev/null
+++ b/usr.bin/tip/libacu/hayes.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hayes.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Hayes Modem
+ * (based on the old VenTel driver).
+ * The modem is expected to be strapped for "echo".
+ * Also, the switches enabling the DTR and CD lines
+ * must be set correctly.
+ * NOTICE:
+ * The easy way to hang up a modem is always simply to
+ * clear the DTR signal. However, if the +++ sequence
+ * (which switches the modem back to local mode) is sent
+ * before modem is hung up, removal of the DTR signal
+ * has no effect (except that it prevents the modem from
+ * recognizing commands).
+ * (by Helge Skrivervik, Calma Company, Sunnyvale, CA. 1984)
+ */
+/*
+ * TODO:
+ * It is probably not a good idea to switch the modem
+ * state between 'verbose' and terse (status messages).
+ * This should be kicked out and we should use verbose
+ * mode only. This would make it consistent with normal
+ * interactive use thru the command 'tip dialer'.
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+#define min(a,b) ((a < b) ? a : b)
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+static char gobble();
+#define DUMBUFLEN 40
+static char dumbuf[DUMBUFLEN];
+
+#define DIALING 1
+#define IDLE 2
+#define CONNECTED 3
+#define FAILED 4
+static int state = IDLE;
+
+hay_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+ register int connected = 0;
+ char dummy;
+#if ACULOG
+ char line[80];
+#endif
+ if (hay_sync() == 0) /* make sure we can talk to the modem */
+ return(0);
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+ acu_hupcl ();
+ acu_flush ();
+ write(FD, "ATv0\r", 5); /* tell modem to use short status codes */
+ gobble("\r");
+ gobble("\r");
+ write(FD, "ATTD", 4); /* send dial command */
+ write(FD, num, strlen(num));
+ state = DIALING;
+ write(FD, "\r", 1);
+ connected = 0;
+ if (gobble("\r")) {
+ if ((dummy = gobble("01234")) != '1')
+ error_rep(dummy);
+ else
+ connected = 1;
+ }
+ if (connected)
+ state = CONNECTED;
+ else {
+ state = FAILED;
+ return (connected); /* lets get out of here.. */
+ }
+ ioctl(FD, TIOCFLUSH, 0);
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "hayes", line);
+ }
+#endif
+ if (timeout)
+ hay_disconnect(); /* insurance */
+ return (connected);
+}
+
+
+hay_disconnect()
+{
+ char c;
+ int len, rlen;
+
+ /* first hang up the modem*/
+#ifdef DEBUG
+ printf("\rdisconnecting modem....\n\r");
+#endif
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ goodbye();
+}
+
+hay_abort()
+{
+
+ char c;
+
+ write(FD, "\r", 1); /* send anything to abort the call */
+ hay_disconnect();
+}
+
+static void
+sigALRM()
+{
+
+ printf("\07timeout waiting for reply\n\r");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static char
+gobble(match)
+ register char *match;
+{
+ char c;
+ sig_t f;
+ int i, status = 0;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+#ifdef DEBUG
+ printf("\ngobble: waiting for %s\n", match);
+#endif
+ do {
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ printf("%c 0x%x ", c, c);
+#endif
+ for (i = 0; i < strlen(match); i++)
+ if (c == match[i])
+ status = c;
+ } while (status == 0);
+ signal(SIGALRM, SIG_DFL);
+#ifdef DEBUG
+ printf("\n");
+#endif
+ return (status);
+}
+
+error_rep(c)
+ register char c;
+{
+ printf("\n\r");
+ switch (c) {
+
+ case '0':
+ printf("OK");
+ break;
+
+ case '1':
+ printf("CONNECT");
+ break;
+
+ case '2':
+ printf("RING");
+ break;
+
+ case '3':
+ printf("NO CARRIER");
+ break;
+
+ case '4':
+ printf("ERROR in input");
+ break;
+
+ case '5':
+ printf("CONNECT 1200");
+ break;
+
+ default:
+ printf("Unknown Modem error: %c (0x%x)", c, c);
+ }
+ printf("\n\r");
+ return;
+}
+
+/*
+ * set modem back to normal verbose status codes.
+ */
+goodbye()
+{
+ int len, rlen;
+ char c;
+
+ ioctl(FD, TIOCFLUSH, &len); /* get rid of trash */
+ if (hay_sync()) {
+ sleep(1);
+#ifndef DEBUG
+ ioctl(FD, TIOCFLUSH, 0);
+#endif
+ write(FD, "ATH0\r", 5); /* insurance */
+#ifndef DEBUG
+ c = gobble("03");
+ if (c != '0' && c != '3') {
+ printf("cannot hang up modem\n\r");
+ printf("please use 'tip dialer' to make sure the line is hung up\n\r");
+ }
+#endif
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+#ifdef DEBUG
+ printf("goodbye1: len=%d -- ", len);
+ rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
+ dumbuf[rlen] = '\0';
+ printf("read (%d): %s\r\n", rlen, dumbuf);
+#endif
+ write(FD, "ATv1\r", 5);
+ sleep(1);
+#ifdef DEBUG
+ ioctl(FD, FIONREAD, &len);
+ printf("goodbye2: len=%d -- ", len);
+ rlen = read(FD, dumbuf, min(len, DUMBUFLEN));
+ dumbuf[rlen] = '\0';
+ printf("read (%d): %s\r\n", rlen, dumbuf);
+#endif
+ }
+ ioctl(FD, TIOCFLUSH, 0); /* clear the input buffer */
+ ioctl(FD, TIOCCDTR, 0); /* clear DTR (insurance) */
+ close(FD);
+}
+
+#define MAXRETRY 5
+
+hay_sync()
+{
+ int len, retry = 0;
+
+ while (retry++ <= MAXRETRY) {
+ write(FD, "AT\r", 3);
+ sleep(1);
+ ioctl(FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, dumbuf, min(len, DUMBUFLEN));
+ if (index(dumbuf, '0') ||
+ (index(dumbuf, 'O') && index(dumbuf, 'K')))
+ return(1);
+#ifdef DEBUG
+ dumbuf[len] = '\0';
+ printf("hay_sync: (\"%s\") %d\n\r", dumbuf, retry);
+#endif
+ }
+ ioctl(FD, TIOCCDTR, 0);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ printf("Cannot synchronize with hayes...\n\r");
+ return(0);
+}
diff --git a/usr.bin/tip/libacu/multitech.c b/usr.bin/tip/libacu/multitech.c
new file mode 100644
index 0000000..41d42b3
--- /dev/null
+++ b/usr.bin/tip/libacu/multitech.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)multitech.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Courier modem.
+ * Derived from Hayes driver.
+ */
+#include "tipconf.h"
+#include "tip.h"
+#include "acucommon.h"
+
+#include <stdio.h>
+
+/* #define DEBUG */
+#define MAXRETRY 5
+/*
+ Configuration
+*/
+static CONST char *dial_command = "ATDT";
+static CONST char *hangup_command = "ATH\r";
+static CONST char *echo_off_command = "ATE0\r";
+static CONST char *reset_command = "\rATZ\r";
+static CONST char *init_string = "AT$BA0$SB38400&E1&E4&E13&E15Q0V1X4E0S0=0\r";
+static CONST char *escape_sequence = "+++"; /* return to command escape sequence */
+static CONST int lock_baud = 1;
+static CONST unsigned int intercharacter_delay = 20;
+static CONST unsigned int intercommand_delay = 250;
+static CONST unsigned int escape_guard_time = 250;
+static CONST unsigned int reset_delay = 2000;
+
+/*
+ Forward declarations
+*/
+void multitech_write (int fd, CONST char *cp, int n);
+void multitech_write_str (int fd, CONST char *cp);
+void multitech_disconnect ();
+void acu_nap (unsigned int how_long);
+static void sigALRM ();
+static int multitechsync ();
+static int multitech_swallow (register char *match);
+
+/*
+ Global vars
+*/
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+
+int multitech_dialer (register char *num, char *acu)
+{
+ register char *cp;
+#if ACULOG
+ char line [80];
+#endif
+ static int multitech_connect(), multitech_swallow();
+
+ if (lock_baud)
+ {
+ int i;
+ if ((i = speed(number(value(BAUDRATE)))) == NULL)
+ return 0;
+ ttysetup (i);
+ }
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ acu_hupcl ();
+
+ /*
+ * Get in synch.
+ */
+ if (!multitechsync()) {
+badsynch:
+ printf("can't synchronize with multitech\n");
+#if ACULOG
+ logent(value(HOST), num, "multitech", "can't synch up");
+#endif
+ return (0);
+ }
+ acu_nap (intercommand_delay);
+
+ multitech_write_str (FD, echo_off_command); /* turn off echoing */
+
+ sleep(1);
+
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ multitech_verbose_read();
+#endif
+
+ acu_flush ();
+
+ acu_nap (intercommand_delay);
+ multitech_write_str (FD, init_string);
+
+ if (!multitech_swallow ("\r\nOK\r\n"))
+ goto badsynch;
+
+ fflush (stdout);
+
+ acu_nap (intercommand_delay);
+ multitech_write_str (FD, dial_command);
+
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+
+ multitech_write_str (FD, num);
+
+ multitech_write_str (FD, "\r");
+
+ connected = multitech_connect();
+
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "multitech", line);
+ }
+#endif
+ if (timeout)
+ multitech_disconnect ();
+ return (connected);
+}
+
+void multitech_disconnect ()
+{
+ int okay, retries;
+ for (retries = okay = 0; retries < 3 && !okay; retries++)
+ {
+ /* first hang up the modem*/
+ ioctl (FD, TIOCCDTR, 0);
+ acu_nap (escape_guard_time);
+ ioctl (FD, TIOCSDTR, 0);
+ acu_nap (escape_guard_time);
+ /*
+ * If not strapped for DTR control, try to get command mode.
+ */
+ acu_nap (escape_guard_time);
+ multitech_write_str (FD, escape_sequence);
+ acu_nap (escape_guard_time);
+ multitech_write_str (FD, hangup_command);
+ okay = multitech_swallow ("\r\nOK\r\n");
+ }
+ if (!okay)
+ {
+ #if ACULOG
+ logent(value(HOST), "", "multitech", "can't hang up modem");
+ #endif
+ if (boolean(value(VERBOSE)))
+ printf("hang up failed\n");
+ }
+ close (FD);
+}
+
+void multitech_abort ()
+{
+ multitech_write_str (FD, "\r"); /* send anything to abort the call */
+ multitech_disconnect ();
+}
+
+static void sigALRM ()
+{
+ (void) printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int multitech_swallow (register char *match)
+ {
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ return (1);
+ }
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c == *match++);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush (stdout);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ return (0);
+}
+
+static struct baud_msg {
+ char *msg;
+ int baud;
+} baud_msg[] = {
+ "", B300,
+ " 1200", B1200,
+ " 2400", B2400,
+ " 9600", B9600,
+ " 9600/ARQ", B9600,
+ 0, 0,
+};
+
+static int multitech_connect ()
+{
+ char c;
+ int nc, nl, n;
+ char dialer_buf[64];
+ struct baud_msg *bm;
+ sig_t f;
+
+ if (multitech_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (multitech_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ if (lock_baud) {
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ for (bm = baud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (!acu_setspeed (bm->baud))
+ goto error;
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the multitech in sync.
+ */
+static int multitechsync ()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ acu_nap (intercommand_delay);
+ ioctl (FD, TIOCFLUSH, 0); /* flush any clutter */
+ multitech_write_str (FD, reset_command); /* reset modem */
+ bzero(buf, sizeof(buf));
+ acu_nap (reset_delay);
+ ioctl (FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf [len] = '\0';
+ printf("multitechsync: (\"%s\")\n\r", buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ acu_nap (escape_guard_time);
+ multitech_write_str (FD, escape_sequence);
+ acu_nap (escape_guard_time);
+ multitech_write_str (FD, hangup_command);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ acu_nap (escape_guard_time);
+ ioctl (FD, TIOCCDTR, 0);
+ acu_nap (escape_guard_time);
+ ioctl (FD, TIOCSDTR, 0);
+ }
+ acu_nap (intercommand_delay);
+ multitech_write_str (FD, reset_command);
+ return (0);
+}
+
+void multitech_write_str (int fd, const char *cp)
+{
+#ifdef DEBUG
+ printf ("multitech: sending %s\n", cp);
+#endif
+ multitech_write (fd, cp, strlen (cp));
+}
+
+void multitech_write (int fd, const char *cp, int n)
+{
+ acu_flush ();
+ acu_nap (intercharacter_delay);
+ for ( ; n-- ; cp++) {
+ write (fd, cp, 1);
+ acu_flush ();
+ acu_nap (intercharacter_delay);
+ }
+}
+
+#ifdef DEBUG
+multitech_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+/* end of multitech.c */
diff --git a/usr.bin/tip/libacu/t3000.c b/usr.bin/tip/libacu/t3000.c
new file mode 100644
index 0000000..1c9f472
--- /dev/null
+++ b/usr.bin/tip/libacu/t3000.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)t3000.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Telebit T3000 modem.
+ * Derived from Courier driver.
+ */
+#include "tipconf.h"
+#include "tip.h"
+#include "acucommon.h"
+#include <stdio.h>
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+static int t3000_sync();
+
+t3000_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+#if ACULOG
+ char line[80];
+#endif
+ static int t3000_connect(), t3000_swallow();
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ acu_hupcl ();
+ /*
+ * Get in synch.
+ */
+ if (!t3000_sync()) {
+badsynch:
+ printf("can't synchronize with t3000\n");
+#if ACULOG
+ logent(value(HOST), num, "t3000", "can't synch up");
+#endif
+ return (0);
+ }
+ t3000_write(FD, "AT E0\r", 6); /* turn off echoing */
+ sleep(1);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ t3000_verbose_read();
+#endif
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ t3000_write(FD, "AT E0 H0 Q0 X4 V1\r", 18);
+ if (!t3000_swallow("\r\nOK\r\n"))
+ goto badsynch;
+ fflush(stdout);
+ t3000_write(FD, "AT D", 4);
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+ t3000_write(FD, num, strlen(num));
+ t3000_write(FD, "\r", 1);
+ connected = t3000_connect();
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "t3000", line);
+ }
+#endif
+ if (timeout)
+ t3000_disconnect();
+ return (connected);
+}
+
+t3000_disconnect()
+{
+ /* first hang up the modem*/
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ t3000_sync(); /* reset */
+ close(FD);
+}
+
+t3000_abort()
+{
+ t3000_write(FD, "\r", 1); /* send anything to abort the call */
+ t3000_disconnect();
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+t3000_swallow(match)
+ register char *match;
+ {
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ return (1);
+ }
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, &c, 1);
+ alarm(0);
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c == *match++);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush(stdout);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ return (0);
+}
+
+#ifndef B19200 /* XXX */
+#define B19200 EXTA
+#define B38400 EXTB
+#endif
+
+struct tbaud_msg {
+ char *msg;
+ int baud;
+ int baud2;
+} tbaud_msg[] = {
+ "", B300, 0,
+ " 1200", B1200, 0,
+ " 2400", B2400, 0,
+ " 4800", B4800, 0,
+ " 9600", B9600, 0,
+ " 14400", B19200, B9600,
+ " 19200", B19200, B9600,
+ " 38400", B38400, B9600,
+ " 57600", B38400, B9600,
+ " 7512", B9600, 0,
+ " 1275", B2400, 0,
+ " 7200", B9600, 0,
+ " 12000", B19200, B9600,
+ 0, 0, 0,
+};
+
+static int
+t3000_connect()
+{
+ char c;
+ int nc, nl, n;
+ char dialer_buf[64];
+ struct tbaud_msg *bm;
+ sig_t f;
+
+ if (t3000_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (t3000_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ for (bm = tbaud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg,
+ dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (!(acu_setspeed (bm->baud) || (bm->baud2 && acu_setspeed (bm->baud2))))
+ goto error;
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the t3000 in sync.
+ */
+static int
+t3000_sync()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ ioctl(FD, TIOCFLUSH, 0); /* flush any clutter */
+ t3000_write(FD, "\rAT Z\r", 6); /* reset modem */
+ bzero(buf, sizeof(buf));
+ sleep(2);
+ ioctl(FD, FIONREAD, &len);
+#if 1
+if (len == 0) len = 1;
+#endif
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf[len] = '\0';
+ printf("t3000_sync: (\"%s\")\n\r", buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ sleep(1);
+ t3000_write(FD, "+++", 3);
+ sleep(1);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ }
+ t3000_write(FD, "\rAT Z\r", 6);
+ return (0);
+}
+
+t3000_write(fd, cp, n)
+int fd;
+char *cp;
+int n;
+{
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ write(1, cp, n);
+#endif
+ acu_flush ();
+ t3000_nap();
+ for ( ; n-- ; cp++) {
+ write(fd, cp, 1);
+ acu_flush ();
+ t3000_nap();
+ }
+}
+
+#ifdef DEBUG
+t3000_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+t3000_nap()
+{
+ acu_nap (50);
+}
+
+/* end of t3000.c */
diff --git a/usr.bin/tip/libacu/tod.c b/usr.bin/tip/libacu/tod.c
new file mode 100644
index 0000000..f585063
--- /dev/null
+++ b/usr.bin/tip/libacu/tod.c
@@ -0,0 +1,107 @@
+/*
+ * tod.c -- time of day pseudo-class implementation
+ *
+ * Copyright (c) 1995 John H. Poplett
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * John H. Poplett.
+ * 4. This work was done expressly for inclusion into FreeBSD. Other use
+ * is allowed if this notation is included.
+ * 5. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "tod.h"
+
+#define USP 1000000
+
+int tod_cmp (const struct timeval *a, const struct timeval *b)
+{
+ int rc;
+ assert (a->tv_usec <= USP);
+ assert (b->tv_usec <= USP);
+ rc = a->tv_sec - b->tv_sec;
+ if (rc == 0)
+ rc = a->tv_usec - b->tv_usec;
+ return rc;
+}
+
+/*
+ TOD < command
+*/
+int tod_lt (const struct timeval *a, const struct timeval *b)
+{
+ return tod_cmp (a, b) < 0;
+}
+
+int tod_gt (const struct timeval *a, const struct timeval *b)
+{
+ return tod_cmp (a, b) > 0;
+}
+
+int tod_lte (const struct timeval *a, const struct timeval *b)
+{
+ return tod_cmp (a, b) <= 0;
+}
+
+int tod_gte (const struct timeval *a, const struct timeval *b)
+{
+ return tod_cmp (a, b) >= 0;
+}
+
+int tod_eq (const struct timeval *a, const struct timeval *b)
+{
+ return tod_cmp (a, b) == 0;
+}
+
+/*
+ TOD += command
+*/
+void tod_addto (struct timeval *a, const struct timeval *b)
+{
+ a->tv_usec += b->tv_usec;
+ a->tv_sec += b->tv_sec + a->tv_usec / USP;
+ a->tv_usec %= USP;
+}
+
+/*
+ TOD -= command
+*/
+void tod_subfrom (struct timeval *a, struct timeval b)
+{
+ assert (a->tv_usec <= USP);
+ assert (b.tv_usec <= USP);
+ if (b.tv_usec > a->tv_usec)
+ {
+ a->tv_usec += USP;
+ a->tv_sec -= 1;
+ }
+ a->tv_usec -= b.tv_usec;
+ a->tv_sec -= b.tv_sec;
+}
+
+void tod_gettime (struct timeval *tp)
+{
+ gettimeofday (tp, NULL);
+ tp->tv_sec += tp->tv_usec / USP;
+ tp->tv_usec %= USP;
+}
+
+/* end of tod.c */
diff --git a/usr.bin/tip/libacu/tod.h b/usr.bin/tip/libacu/tod.h
new file mode 100644
index 0000000..d772230
--- /dev/null
+++ b/usr.bin/tip/libacu/tod.h
@@ -0,0 +1,9 @@
+int tod_cmp (const struct timeval *a, const struct timeval *b);
+int tod_lt (const struct timeval *a, const struct timeval *b) ;
+int tod_gt (const struct timeval *a, const struct timeval *b);
+int tod_lte (const struct timeval *a, const struct timeval *b);
+int tod_gte (const struct timeval *a, const struct timeval *b);
+int tod_eq (const struct timeval *a, const struct timeval *b);
+void tod_addto (struct timeval *a, const struct timeval *b);
+void tod_subfrom (struct timeval *a, struct timeval b);
+void tod_gettime (struct timeval *tp);
diff --git a/usr.bin/tip/libacu/unidialer.c b/usr.bin/tip/libacu/unidialer.c
new file mode 100644
index 0000000..3d2701d
--- /dev/null
+++ b/usr.bin/tip/libacu/unidialer.c
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)unidialer.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Generalized routines for calling up on a Hayes AT command set based modem.
+ * Control variables are pulled out of a modem caps-style database to
+ * configure the driver for a particular modem.
+ */
+#include "tipconf.h"
+#include "tip.h"
+#include "pathnames.h"
+
+#include <sys/times.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "acucommon.h"
+#include "tod.h"
+
+/* #define DEBUG */
+#define MAXRETRY 5
+
+typedef enum
+{
+ mpt_notype, mpt_string, mpt_number, mpt_boolean
+} modem_parm_type_t;
+
+typedef struct {
+ modem_parm_type_t modem_parm_type;
+ const char *name;
+ union {
+ char **string;
+ unsigned int *number;
+ } value;
+ union {
+ char *string;
+ unsigned int number;
+ } default_value;
+} modem_parm_t;
+
+/*
+ Configuration
+*/
+static char modem_name [80];
+static char *dial_command;
+static char *hangup_command;
+static char *echo_off_command;
+static char *reset_command;
+static char *init_string;
+static char *escape_sequence;
+static int hw_flow_control;
+static int lock_baud;
+static unsigned int intercharacter_delay;
+static unsigned int intercommand_delay;
+static unsigned int escape_guard_time;
+static unsigned int reset_delay;
+
+static int unidialer_dialer (register char *num, char *acu);
+static void unidialer_disconnect ();
+static void unidialer_abort ();
+
+static acu_t unidialer =
+{
+ modem_name,
+ unidialer_dialer,
+ unidialer_disconnect,
+ unidialer_abort
+};
+
+/*
+ Table of parameters kept in modem database
+*/
+modem_parm_t modem_parms [] = {
+ { mpt_string, "dial_command", &dial_command, "ATDT%s\r" },
+ { mpt_string, "hangup_command", &hangup_command, "ATH\r", },
+ { mpt_string, "echo_off_command", &echo_off_command, "ATE0\r" },
+ { mpt_string, "reset_command", &reset_command, "ATZ\r" },
+ { mpt_string, "init_string", &init_string, "AT&F\r", },
+ { mpt_string, "escape_sequence", &escape_sequence, "+++" },
+ { mpt_boolean, "hw_flow_control", (char **)&hw_flow_control, NULL },
+ { mpt_boolean, "lock_baud", (char **)&lock_baud, NULL },
+ { mpt_number, "intercharacter_delay", (char **)&intercharacter_delay, (char *)50 },
+ { mpt_number, "intercommand_delay", (char **)&intercommand_delay, (char *)300 },
+ { mpt_number, "escape_guard_time", (char **)&escape_guard_time, (char *)300 },
+ { mpt_number, "reset_delay", (char **)&reset_delay, (char *)3000 },
+ { mpt_notype, NULL, NULL, NULL }
+};
+
+/*
+ Forward declarations
+*/
+static void unidialer_verbose_read ();
+static void unidialer_modem_cmd (int fd, CONST char *cmd);
+static void unidialer_write (int fd, CONST char *cp, int n);
+static void unidialer_write_str (int fd, CONST char *cp);
+static void unidialer_disconnect ();
+static void sigALRM ();
+static int unidialersync ();
+static int unidialer_swallow (register char *match);
+
+/*
+ Global vars
+*/
+static int timeout = 0;
+static int connected = 0;
+static jmp_buf timeoutbuf, intbuf;
+
+#define cgetflag(f) (cgetcap(bp, f, ':') != NULL)
+
+#ifdef DEBUG
+
+#define print_str(x) printf (#x " = %s\n", x)
+#define print_num(x) printf (#x " = %d\n", x)
+
+void dumpmodemparms (char *modem)
+{
+ printf ("modem parms for %s\n", modem);
+ print_str (dial_command);
+ print_str (hangup_command);
+ print_str (echo_off_command);
+ print_str (reset_command);
+ print_str (init_string);
+ print_str (escape_sequence);
+ print_num (lock_baud);
+ print_num (intercharacter_delay);
+ print_num (intercommand_delay);
+ print_num (escape_guard_time);
+ print_num (reset_delay);
+ printf ("\n");
+}
+#endif
+
+static int getmodemparms (const char *modem)
+{
+ char *bp, *db_array [3], *modempath;
+ int ndx, stat;
+ modem_parm_t *mpp;
+
+ modempath = getenv ("MODEMS");
+
+ ndx = 0;
+
+ if (modempath != NULL)
+ db_array [ndx++] = modempath;
+
+ db_array [ndx++] = _PATH_MODEMS;
+ db_array [ndx] = NULL;
+
+ if ((stat = cgetent (&bp, db_array, (char *)modem)) < 0) {
+ switch (stat) {
+ case -1:
+ fprintf (stderr, "tip: unknown modem %s\n", modem);
+ break;
+ case -2:
+ fprintf (stderr, "tip: can't open modem description file\n");
+ break;
+ case -3:
+ fprintf (stderr, "tip: possible reference loop in modem description file\n");
+ break;
+ }
+ return 0;
+ }
+ for (mpp = modem_parms; mpp->name; mpp++)
+ {
+ switch (mpp->modem_parm_type)
+ {
+ case mpt_string:
+ if (cgetstr (bp, (char *)mpp->name, mpp->value.string) == -1)
+ *mpp->value.string = mpp->default_value.string;
+ break;
+
+ case mpt_number:
+ {
+ long l;
+ if (cgetnum (bp, (char *)mpp->name, &l) == -1)
+ *mpp->value.number = mpp->default_value.number;
+ else
+ *mpp->value.number = (unsigned int)l;
+ }
+ break;
+
+ case mpt_boolean:
+ *mpp->value.number = cgetflag ((char *)mpp->name);
+ break;
+ }
+ }
+ strncpy (modem_name, modem, sizeof (modem_name) - 1);
+ modem_name [sizeof (modem_name) - 1] = '\0';
+ return 1;
+}
+
+/*
+*/
+acu_t* unidialer_getmodem (const char *modem_name)
+{
+ acu_t* rc = NOACU;
+ if (getmodemparms (modem_name))
+ rc = &unidialer;
+ return rc;
+}
+
+static int unidialer_modem_ready ()
+{
+#ifdef TIOCMGET
+ int state;
+ ioctl (FD, TIOCMGET, &state);
+ return (state & TIOCM_DSR) ? 1 : 0;
+#else
+ return (1);
+#endif
+}
+
+static int unidialer_waitfor_modem_ready (int ms)
+{
+#ifdef TIOCMGET
+ int count;
+ for (count = 0; count < ms; count += 100)
+ {
+ if (unidialer_modem_ready ())
+ {
+#ifdef DEBUG
+ printf ("unidialer_waitfor_modem_ready: modem ready.\n");
+#endif
+ break;
+ }
+ acu_nap (100);
+ }
+ return (count < ms);
+#else
+ acu_nap (250);
+ return (1);
+#endif
+}
+
+int unidialer_tty_clocal (int flag)
+{
+#if HAVE_TERMIOS
+ struct termios t;
+ tcgetattr (FD, &t);
+ if (flag)
+ t.c_cflag |= CLOCAL;
+ else
+ t.c_cflag &= ~CLOCAL;
+ tcsetattr (FD, TCSANOW, &t);
+#elif defined(TIOCMSET)
+ int state;
+ /*
+ Don't have CLOCAL so raise CD in software to
+ get the same effect.
+ */
+ ioctl (FD, TIOCMGET, &state);
+ if (flag)
+ state |= TIOCM_CD;
+ else
+ state &= ~TIOCM_CD;
+ ioctl (FD, TIOCMSET, &state);
+#endif
+}
+
+int unidialer_get_modem_response (char *buf, int bufsz, int response_timeout)
+{
+ sig_t f;
+ char c, *p = buf, *lid = buf + bufsz - 1;
+ int state;
+
+ assert (bufsz > 0);
+
+ f = signal (SIGALRM, sigALRM);
+
+ timeout = 0;
+
+ if (setjmp (timeoutbuf)) {
+ signal (SIGALRM, f);
+ *p = '\0';
+#ifdef DEBUG
+ printf ("get_response: timeout buf=%s, state=%d\n", buf, state);
+#endif
+ return (0);
+ }
+
+ ualarm (response_timeout * 1000, 0);
+
+ state = 0;
+
+ while (1)
+ {
+ switch (state)
+ {
+ case 0:
+ if (read (FD, &c, 1) == 1)
+ {
+ if (c == '\r')
+ {
+ ++state;
+ }
+ else
+ {
+#ifdef DEBUG
+ printf ("get_response: unexpected char %s.\n", ctrl (c));
+#endif
+ }
+ }
+ break;
+
+ case 1:
+ if (read (FD, &c, 1) == 1)
+ {
+ if (c == '\n')
+ {
+#ifdef DEBUG
+ printf ("get_response: <CRLF> encountered.\n", buf);
+#endif
+ ++state;
+ }
+ else
+ {
+ state = 0;
+#ifdef DEBUG
+ printf ("get_response: unexpected char %s.\n", ctrl (c));
+#endif
+ }
+ }
+ break;
+
+ case 2:
+ if (read (FD, &c, 1) == 1)
+ {
+ if (c == '\r')
+ ++state;
+ else if (c >= ' ' && p < lid)
+ *p++ = c;
+ }
+ break;
+
+ case 3:
+ if (read (FD, &c, 1) == 1)
+ {
+ if (c == '\n')
+ {
+ signal (SIGALRM, f);
+ /* ualarm (0, 0); */
+ alarm (0);
+ *p = '\0';
+#ifdef DEBUG
+ printf ("get_response: %s\n", buf);
+#endif
+ return (1);
+ }
+ else
+ {
+ state = 0;
+ p = buf;
+ }
+ }
+ break;
+ }
+ }
+}
+
+int unidialer_get_okay (int ms)
+{
+ int okay;
+ char buf [BUFSIZ];
+ okay = unidialer_get_modem_response (buf, sizeof (buf), ms) &&
+ strcmp (buf, "OK") == 0;
+ return okay;
+}
+
+static int unidialer_dialer (register char *num, char *acu)
+{
+ register char *cp;
+ char dial_string [80];
+#if ACULOG
+ char line [80];
+#endif
+ static int unidialer_connect(), unidialer_swallow();
+
+ #ifdef DEBUG
+ dumpmodemparms (modem_name);
+ #endif
+
+ if (lock_baud) {
+ int i;
+ if ((i = speed(number(value(BAUDRATE)))) == NULL)
+ return 0;
+ ttysetup (i);
+ }
+
+ if (boolean(value(VERBOSE)))
+ printf("Using \"%s\"\n", acu);
+
+ acu_hupcl ();
+
+ /*
+ * Get in synch.
+ */
+ if (!unidialersync()) {
+badsynch:
+ printf("tip: can't synchronize with %s\n", modem_name);
+#if ACULOG
+ logent(value(HOST), num, modem_name, "can't synch up");
+#endif
+ return (0);
+ }
+
+ unidialer_modem_cmd (FD, echo_off_command); /* turn off echoing */
+
+ sleep(1);
+
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ unidialer_verbose_read();
+#endif
+
+ acu_flush (); /* flush any clutter */
+
+ unidialer_modem_cmd (FD, init_string);
+
+ if (!unidialer_get_okay (250))
+ goto badsynch;
+
+ fflush (stdout);
+
+ for (cp = num; *cp; cp++)
+ if (*cp == '=')
+ *cp = ',';
+
+ (void) sprintf (dial_string, dial_command, num);
+
+ unidialer_modem_cmd (FD, dial_string);
+
+ connected = unidialer_connect ();
+
+ if (connected && hw_flow_control) {
+ acu_hw_flow_control (hw_flow_control);
+ }
+
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, modem_name, line);
+ }
+#endif
+
+ if (timeout)
+ unidialer_disconnect ();
+
+ return (connected);
+}
+
+static void unidialer_disconnect ()
+{
+ int okay, retries;
+
+ acu_flush (); /* flush any clutter */
+
+ unidialer_tty_clocal (TRUE);
+
+ /* first hang up the modem*/
+ ioctl (FD, TIOCCDTR, 0);
+ acu_nap (250);
+ ioctl (FD, TIOCSDTR, 0);
+
+ /*
+ * If AT&D2, then dropping DTR *should* just hangup the modem. But
+ * some modems reset anyway; also, the modem may be programmed to reset
+ * anyway with AT&D3. Play it safe and wait for the full reset time before
+ * proceeding.
+ */
+ acu_nap (reset_delay);
+
+ if (!unidialer_waitfor_modem_ready (reset_delay))
+ {
+#ifdef DEBUG
+ printf ("unidialer_disconnect: warning CTS low.\r\n");
+#endif
+ }
+
+ /*
+ * If not strapped for DTR control, try to get command mode.
+ */
+ for (retries = okay = 0; retries < MAXRETRY && !okay; retries++)
+ {
+ int timeout_value;
+ /* flush any clutter */
+ if (!acu_flush ())
+ {
+#ifdef DEBUG
+ printf ("unidialer_disconnect: warning flush failed.\r\n");
+#endif
+ }
+ timeout_value = escape_guard_time;
+ timeout_value += (timeout_value * retries / MAXRETRY);
+ acu_nap (timeout_value);
+ acu_flush (); /* flush any clutter */
+ unidialer_modem_cmd (FD, escape_sequence);
+ acu_nap (timeout_value);
+ unidialer_modem_cmd (FD, hangup_command);
+ okay = unidialer_get_okay (250);
+ }
+ if (!okay)
+ {
+ #if ACULOG
+ logent(value(HOST), "", modem_name, "can't hang up modem");
+ #endif
+ if (boolean(value(VERBOSE)))
+ printf("hang up failed\n");
+ }
+ (void) acu_flush ();
+ close (FD);
+}
+
+static void unidialer_abort ()
+{
+ unidialer_write_str (FD, "\r"); /* send anything to abort the call */
+ unidialer_disconnect ();
+}
+
+static void sigALRM ()
+{
+ (void) printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int unidialer_swallow (register char *match)
+{
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+
+ timeout = 0;
+
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ return (0);
+ }
+
+ alarm(number(value(DIALTIMEOUT)));
+
+ do {
+ if (*match =='\0') {
+ signal(SIGALRM, f);
+ alarm (0);
+ return (1);
+ }
+ do {
+ read (FD, &c, 1);
+ } while (c == '\0');
+ c &= 0177;
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ {
+ /* putchar(c); */
+ printf (ctrl (c));
+ }
+#endif
+ } while (c == *match++);
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ fflush (stdout);
+#endif
+ return (0);
+}
+
+static struct baud_msg {
+ char *msg;
+ int baud;
+} baud_msg[] = {
+ "", B300,
+ " 1200", B1200,
+ " 2400", B2400,
+ " 9600", B9600,
+ " 9600/ARQ", B9600,
+ 0, 0,
+};
+
+static int unidialer_connect ()
+{
+ char c;
+ int nc, nl, n;
+ char dialer_buf[64];
+ struct baud_msg *bm;
+ sig_t f;
+
+ if (unidialer_swallow("\r\n") == 0)
+ return (0);
+ f = signal(SIGALRM, sigALRM);
+again:
+ nc = 0; nl = sizeof(dialer_buf)-1;
+ bzero(dialer_buf, sizeof(dialer_buf));
+ timeout = 0;
+ for (nc = 0, nl = sizeof(dialer_buf)-1 ; nl > 0 ; nc++, nl--) {
+ if (setjmp(timeoutbuf))
+ break;
+ alarm(number(value(DIALTIMEOUT)));
+ n = read(FD, &c, 1);
+ alarm(0);
+ if (n <= 0)
+ break;
+ c &= 0x7f;
+ if (c == '\r') {
+ if (unidialer_swallow("\n") == 0)
+ break;
+ if (!dialer_buf[0])
+ goto again;
+ if (strcmp(dialer_buf, "RINGING") == 0 &&
+ boolean(value(VERBOSE))) {
+#ifdef DEBUG
+ printf("%s\r\n", dialer_buf);
+#endif
+ goto again;
+ }
+ if (strncmp(dialer_buf, "CONNECT",
+ sizeof("CONNECT")-1) != 0)
+ break;
+ if (lock_baud) {
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ for (bm = baud_msg ; bm->msg ; bm++)
+ if (strcmp(bm->msg, dialer_buf+sizeof("CONNECT")-1) == 0) {
+ if (!acu_setspeed (bm->baud))
+ goto error;
+ signal(SIGALRM, f);
+#ifdef DEBUG
+ if (boolean(value(VERBOSE)))
+ printf("%s\r\n", dialer_buf);
+#endif
+ return (1);
+ }
+ break;
+ }
+ dialer_buf[nc] = c;
+ }
+error1:
+ printf("%s\r\n", dialer_buf);
+error:
+ signal(SIGALRM, f);
+ return (0);
+}
+
+/*
+ * This convoluted piece of code attempts to get
+ * the unidialer in sync.
+ */
+static int unidialersync ()
+{
+ int already = 0;
+ int len;
+ char buf[40];
+
+ while (already++ < MAXRETRY) {
+ acu_nap (intercommand_delay);
+ acu_flush (); /* flush any clutter */
+ unidialer_write_str (FD, reset_command); /* reset modem */
+ bzero(buf, sizeof(buf));
+ acu_nap (reset_delay);
+ ioctl (FD, FIONREAD, &len);
+ if (len) {
+ len = read(FD, buf, sizeof(buf));
+#ifdef DEBUG
+ buf [len] = '\0';
+ printf("unidialersync (%s): (\"%s\")\n\r", modem_name, buf);
+#endif
+ if (index(buf, '0') ||
+ (index(buf, 'O') && index(buf, 'K')))
+ return(1);
+ }
+ /*
+ * If not strapped for DTR control,
+ * try to get command mode.
+ */
+ acu_nap (escape_guard_time);
+ unidialer_write_str (FD, escape_sequence);
+ acu_nap (escape_guard_time);
+ unidialer_write_str (FD, hangup_command);
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected.
+ */
+ acu_nap (escape_guard_time);
+ ioctl (FD, TIOCCDTR, 0);
+ acu_nap (1000);
+ ioctl (FD, TIOCSDTR, 0);
+ }
+ acu_nap (intercommand_delay);
+ unidialer_write_str (FD, reset_command);
+ return (0);
+}
+
+/*
+ Send commands to modem; impose delay between commands.
+*/
+static void unidialer_modem_cmd (int fd, const char *cmd)
+{
+ static struct timeval oldt = { 0, 0 };
+ struct timeval newt;
+ tod_gettime (&newt);
+ if (tod_lt (&newt, &oldt))
+ {
+ unsigned int naptime;
+ tod_subfrom (&oldt, newt);
+ naptime = oldt.tv_sec * 1000 + oldt.tv_usec / 1000;
+ if (naptime > intercommand_delay)
+ {
+#ifdef DEBUG
+ printf ("unidialer_modem_cmd: suspicious naptime (%u ms)\r\n", naptime);
+#endif
+ naptime = intercommand_delay;
+ }
+#ifdef DEBUG
+ printf ("unidialer_modem_cmd: delaying %u ms\r\n", naptime);
+#endif
+ acu_nap (naptime);
+ }
+ unidialer_write_str (fd, cmd);
+ tod_gettime (&oldt);
+ newt.tv_sec = 0;
+ newt.tv_usec = intercommand_delay;
+ tod_addto (&oldt, &newt);
+}
+
+static void unidialer_write_str (int fd, const char *cp)
+{
+#ifdef DEBUG
+ printf ("unidialer (%s): sending %s\n", modem_name, cp);
+#endif
+ unidialer_write (fd, cp, strlen (cp));
+}
+
+static void unidialer_write (int fd, const char *cp, int n)
+{
+ acu_nap (intercharacter_delay);
+ for ( ; n-- ; cp++) {
+ write (fd, cp, 1);
+ acu_nap (intercharacter_delay);
+ }
+}
+
+#ifdef DEBUG
+static void unidialer_verbose_read()
+{
+ int n = 0;
+ char buf[BUFSIZ];
+
+ if (ioctl(FD, FIONREAD, &n) < 0)
+ return;
+ if (n <= 0)
+ return;
+ if (read(FD, buf, n) != n)
+ return;
+ write(1, buf, n);
+}
+#endif
+
+/* end of unidialer.c */
diff --git a/usr.bin/tip/libacu/v3451.c b/usr.bin/tip/libacu/v3451.c
new file mode 100644
index 0000000..a98c4d4
--- /dev/null
+++ b/usr.bin/tip/libacu/v3451.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v3451.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Vadic 3451 Modem
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+static jmp_buf Sjbuf;
+
+v3451_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ sig_t func;
+ int ok;
+ int slow = number(value(BAUDRATE)) < 1200, rw = 2;
+ char phone[50];
+#if ACULOG
+ char line[80];
+#endif
+ static int expect();
+ static void vawrite();
+
+ /*
+ * Get in synch
+ */
+ vawrite("I\r", 1 + slow);
+ vawrite("I\r", 1 + slow);
+ vawrite("I\r", 1 + slow);
+ vawrite("\005\r", 2 + slow);
+ if (!expect("READY")) {
+ printf("can't synchronize with vadic 3451\n");
+#if ACULOG
+ logent(value(HOST), num, "vadic", "can't synch up");
+#endif
+ return (0);
+ }
+ acu_hupcl ();
+ sleep(1);
+ vawrite("D\r", 2 + slow);
+ if (!expect("NUMBER?")) {
+ printf("Vadic will not accept dial command\n");
+#if ACULOG
+ logent(value(HOST), num, "vadic", "will not accept dial");
+#endif
+ return (0);
+ }
+ strcpy(phone, num);
+ strcat(phone, "\r");
+ vawrite(phone, 1 + slow);
+ if (!expect(phone)) {
+ printf("Vadic will not accept phone number\n");
+#if ACULOG
+ logent(value(HOST), num, "vadic", "will not accept number");
+#endif
+ return (0);
+ }
+ func = signal(SIGINT,SIG_IGN);
+ /*
+ * You cannot interrupt the Vadic when its dialing;
+ * even dropping DTR does not work (definitely a
+ * brain damaged design).
+ */
+ vawrite("\r", 1 + slow);
+ vawrite("\r", 1 + slow);
+ if (!expect("DIALING:")) {
+ printf("Vadic failed to dial\n");
+#if ACULOG
+ logent(value(HOST), num, "vadic", "failed to dial");
+#endif
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ ok = expect("ON LINE");
+ signal(SIGINT, func);
+ if (!ok) {
+ printf("call failed\n");
+#if ACULOG
+ logent(value(HOST), num, "vadic", "call failed");
+#endif
+ return (0);
+ }
+ ioctl(FD, TIOCFLUSH, &rw);
+ return (1);
+}
+
+v3451_disconnect()
+{
+
+ close(FD);
+}
+
+v3451_abort()
+{
+
+ close(FD);
+}
+
+static void
+vawrite(cp, delay)
+ register char *cp;
+ int delay;
+{
+
+ for (; *cp; sleep(delay), cp++)
+ write(FD, cp, 1);
+}
+
+static
+expect(cp)
+ register char *cp;
+{
+ char buf[300];
+ register char *rp = buf;
+ int timeout = 30, online = 0;
+ static int notin();
+ static void alarmtr();
+
+ if (strcmp(cp, "\"\"") == 0)
+ return (1);
+ *rp = 0;
+ /*
+ * If we are waiting for the Vadic to complete
+ * dialing and get a connection, allow more time
+ * Unfortunately, the Vadic times out 24 seconds after
+ * the last digit is dialed
+ */
+ online = strcmp(cp, "ON LINE") == 0;
+ if (online)
+ timeout = number(value(DIALTIMEOUT));
+ signal(SIGALRM, alarmtr);
+ if (setjmp(Sjbuf))
+ return (0);
+ alarm(timeout);
+ while (notin(cp, buf) && rp < buf + sizeof (buf) - 1) {
+ if (online && notin("FAILED CALL", buf) == 0)
+ return (0);
+ if (read(FD, rp, 1) < 0) {
+ alarm(0);
+ return (0);
+ }
+ if (*rp &= 0177)
+ rp++;
+ *rp = '\0';
+ }
+ alarm(0);
+ return (1);
+}
+
+static void
+alarmtr()
+{
+ longjmp(Sjbuf, 1);
+}
+
+static int
+notin(sh, lg)
+ char *sh, *lg;
+{
+ static int prefix();
+
+ for (; *lg; lg++)
+ if (prefix(sh, lg))
+ return (0);
+ return (1);
+}
+
+static
+prefix(s1, s2)
+ register char *s1, *s2;
+{
+ register char c;
+
+ while ((c = *s1++) == *s2++)
+ if (c == '\0')
+ return (1);
+ return (c == '\0');
+}
diff --git a/usr.bin/tip/libacu/v831.c b/usr.bin/tip/libacu/v831.c
new file mode 100644
index 0000000..b670c2a
--- /dev/null
+++ b/usr.bin/tip/libacu/v831.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v831.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for dialing up on Vadic 831
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+int v831_abort();
+static void alarmtr();
+extern int errno;
+
+static jmp_buf jmpbuf;
+static int child = -1;
+
+v831_dialer(num, acu)
+ char *num, *acu;
+{
+ int status, pid, connected = 1;
+ register int timelim;
+ static int dialit();
+
+ if (boolean(value(VERBOSE)))
+ printf("\nstarting call...");
+#ifdef DEBUG
+ printf ("(acu=%s)\n", acu);
+#endif
+ if ((AC = open(acu, O_RDWR)) < 0) {
+ if (errno == EBUSY)
+ printf("line busy...");
+ else
+ printf("acu open error...");
+ return (0);
+ }
+ if (setjmp(jmpbuf)) {
+ kill(child, SIGKILL);
+ close(AC);
+ return (0);
+ }
+ signal(SIGALRM, alarmtr);
+ timelim = 5 * strlen(num);
+ alarm(timelim < 30 ? 30 : timelim);
+ if ((child = fork()) == 0) {
+ /*
+ * ignore this stuff for aborts
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ sleep(2);
+ exit(dialit(num, acu) != 'A');
+ }
+ /*
+ * open line - will return on carrier
+ */
+ if ((FD = open(DV, O_RDWR)) < 0) {
+#ifdef DEBUG
+ printf("(after open, errno=%d)\n", errno);
+#endif
+ if (errno == EIO)
+ printf("lost carrier...");
+ else
+ printf("dialup line open failed...");
+ alarm(0);
+ kill(child, SIGKILL);
+ close(AC);
+ return (0);
+ }
+ alarm(0);
+#ifdef notdef
+ ioctl(AC, TIOCHPCL, 0);
+#endif
+ signal(SIGALRM, SIG_DFL);
+ while ((pid = wait(&status)) != child && pid != -1)
+ ;
+ if (status) {
+ close(AC);
+ return (0);
+ }
+ return (1);
+}
+
+static void
+alarmtr()
+{
+ alarm(0);
+ longjmp(jmpbuf, 1);
+}
+
+/*
+ * Insurance, for some reason we don't seem to be
+ * hanging up...
+ */
+v831_disconnect()
+{
+ sleep(2);
+#ifdef DEBUG
+ printf("[disconnect: FD=%d]\n", FD);
+#endif
+ if (FD > 0) {
+ ioctl(FD, TIOCCDTR, 0);
+ acu_setspeec (0);
+ ioctl(FD, TIOCNXCL, 0);
+ }
+ close(FD);
+}
+
+v831_abort()
+{
+
+#ifdef DEBUG
+ printf("[abort: AC=%d]\n", AC);
+#endif
+ sleep(2);
+ if (child > 0)
+ kill(child, SIGKILL);
+ if (AC > 0)
+ ioctl(FD, TIOCNXCL, 0);
+ close(AC);
+ if (FD > 0)
+ ioctl(FD, TIOCCDTR, 0);
+ close(FD);
+}
+
+/*
+ * Sigh, this probably must be changed at each site.
+ */
+struct vaconfig {
+ char *vc_name;
+ char vc_rack;
+ char vc_modem;
+} vaconfig[] = {
+ { "/dev/cua0",'4','0' },
+ { "/dev/cua1",'4','1' },
+ { 0 }
+};
+
+#define pc(x) (c = x, write(AC,&c,1))
+#define ABORT 01
+#define SI 017
+#define STX 02
+#define ETX 03
+
+static int
+dialit(phonenum, acu)
+ register char *phonenum;
+ char *acu;
+{
+ register struct vaconfig *vp;
+ char c;
+ int i, two = 2;
+ static char *sanitize();
+
+ phonenum = sanitize(phonenum);
+#ifdef DEBUG
+ printf ("(dial phonenum=%s)\n", phonenum);
+#endif
+ if (*phonenum == '<' && phonenum[1] == 0)
+ return ('Z');
+ for (vp = vaconfig; vp->vc_name; vp++)
+ if (strcmp(vp->vc_name, acu) == 0)
+ break;
+ if (vp->vc_name == 0) {
+ printf("Unable to locate dialer (%s)\n", acu);
+ return ('K');
+ }
+ {
+#if HAVE_TERMIOS
+ struct termios termios;
+ tcgetattr (AC, &termios);
+ termios.c_iflag = 0;
+#ifndef _POSIX_SOURCE
+ termios.c_lflag = (PENDIN|ECHOKE|ECHOE);
+#else
+ termios.c_lflag = (PENDIN|ECHOE);
+#endif
+ termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
+ termios.c_ispeed = termios.c_ospeed = B2400;
+ tcsetattr (AC, TCSANOW, &termios);
+#else /* HAVE_TERMIOS */
+ struct sgttyb cntrl;
+ ioctl(AC, TIOCGETP, &cntrl);
+ cntrl.sg_ispeed = cntrl.sg_ospeed = B2400;
+ cntrl.sg_flags = RAW | EVENP | ODDP;
+ ioctl(AC, TIOCSETP, &cntrl);
+ #endif
+ }
+ ioctl(AC, TIOCFLUSH, &two);
+ pc(STX);
+ pc(vp->vc_rack);
+ pc(vp->vc_modem);
+ while (*phonenum && *phonenum != '<')
+ pc(*phonenum++);
+ pc(SI);
+ pc(ETX);
+ sleep(1);
+ i = read(AC, &c, 1);
+#ifdef DEBUG
+ printf("read %d chars, char=%c, errno %d\n", i, c, errno);
+#endif
+ if (i != 1)
+ c = 'M';
+ if (c == 'B' || c == 'G') {
+ char cc, oc = c;
+
+ pc(ABORT);
+ read(AC, &cc, 1);
+#ifdef DEBUG
+ printf("abort response=%c\n", cc);
+#endif
+ c = oc;
+ v831_disconnect();
+ }
+ close(AC);
+#ifdef DEBUG
+ printf("dialit: returns %c\n", c);
+#endif
+ return (c);
+}
+
+static char *
+sanitize(s)
+ register char *s;
+{
+ static char buf[128];
+ register char *cp;
+
+ for (cp = buf; *s; s++) {
+ if (!isdigit(*s) && *s == '<' && *s != '_')
+ continue;
+ if (*s == '_')
+ *s = '=';
+ *cp++ = *s;
+ }
+ *cp++ = 0;
+ return (buf);
+}
diff --git a/usr.bin/tip/libacu/ventel.c b/usr.bin/tip/libacu/ventel.c
new file mode 100644
index 0000000..73733e5
--- /dev/null
+++ b/usr.bin/tip/libacu/ventel.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ventel.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines for calling up on a Ventel Modem
+ * The Ventel is expected to be strapped for local echo (just like uucp)
+ */
+#include "tipconf.h"
+#include "tip.h"
+
+#define MAXRETRY 5
+
+static void sigALRM();
+static int timeout = 0;
+static jmp_buf timeoutbuf;
+
+/*
+ * some sleep calls have been replaced by this macro
+ * because some ventel modems require two <cr>s in less than
+ * a second in order to 'wake up'... yes, it is dirty...
+ */
+#define delay(num,denom) busyloop(CPUSPEED*num/denom)
+#define CPUSPEED 1000000 /* VAX 780 is 1MIPS */
+#define DELAY(n) { register long N = (n); while (--N > 0); }
+busyloop(n) { DELAY(n); }
+
+ven_dialer(num, acu)
+ register char *num;
+ char *acu;
+{
+ register char *cp;
+ register int connected = 0;
+ char *msg, *index(), line[80];
+ static int gobble(), vensync();
+ static void echo();
+
+ /*
+ * Get in synch with a couple of carriage returns
+ */
+ if (!vensync(FD)) {
+ printf("can't synchronize with ventel\n");
+#if ACULOG
+ logent(value(HOST), num, "ventel", "can't synch up");
+#endif
+ return (0);
+ }
+ if (boolean(value(VERBOSE)))
+ printf("\ndialing...");
+ fflush(stdout);
+ acu_hupcl ();
+ echo("#k$\r$\n$D$I$A$L$:$ ");
+ for (cp = num; *cp; cp++) {
+ delay(1, 10);
+ write(FD, cp, 1);
+ }
+ delay(1, 10);
+ write(FD, "\r", 1);
+ gobble('\n', line);
+ if (gobble('\n', line))
+ connected = gobble('!', line);
+ acu_flush ();
+#if ACULOG
+ if (timeout) {
+ sprintf(line, "%d second dial timeout",
+ number(value(DIALTIMEOUT)));
+ logent(value(HOST), num, "ventel", line);
+ }
+#endif
+ if (timeout)
+ ven_disconnect(); /* insurance */
+ if (connected || timeout || !boolean(value(VERBOSE)))
+ return (connected);
+ /* call failed, parse response for user */
+ cp = index(line, '\r');
+ if (cp)
+ *cp = '\0';
+ for (cp = line; cp = index(cp, ' '); cp++)
+ if (cp[1] == ' ')
+ break;
+ if (cp) {
+ while (*cp == ' ')
+ cp++;
+ msg = cp;
+ while (*cp) {
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ cp++;
+ }
+ printf("%s...", msg);
+ }
+ return (connected);
+}
+
+ven_disconnect()
+{
+
+ close(FD);
+}
+
+ven_abort()
+{
+
+ write(FD, "\03", 1);
+ close(FD);
+}
+
+static void
+echo(s)
+ register char *s;
+{
+ char c;
+
+ while (c = *s++) switch (c) {
+
+ case '$':
+ read(FD, &c, 1);
+ s++;
+ break;
+
+ case '#':
+ c = *s++;
+ write(FD, &c, 1);
+ break;
+
+ default:
+ write(FD, &c, 1);
+ read(FD, &c, 1);
+ }
+}
+
+static void
+sigALRM()
+{
+ printf("\07timeout waiting for reply\n");
+ timeout = 1;
+ longjmp(timeoutbuf, 1);
+}
+
+static int
+gobble(match, response)
+ register char match;
+ char response[];
+{
+ register char *cp = response;
+ sig_t f;
+ char c;
+
+ f = signal(SIGALRM, sigALRM);
+ timeout = 0;
+ do {
+ if (setjmp(timeoutbuf)) {
+ signal(SIGALRM, f);
+ *cp = '\0';
+ return (0);
+ }
+ alarm(number(value(DIALTIMEOUT)));
+ read(FD, cp, 1);
+ alarm(0);
+ c = (*cp++ &= 0177);
+#ifdef notdef
+ if (boolean(value(VERBOSE)))
+ putchar(c);
+#endif
+ } while (c != '\n' && c != match);
+ signal(SIGALRM, SIG_DFL);
+ *cp = '\0';
+ return (c == match);
+}
+
+#define min(a,b) ((a)>(b)?(b):(a))
+/*
+ * This convoluted piece of code attempts to get
+ * the ventel in sync. If you don't have FIONREAD
+ * there are gory ways to simulate this.
+ */
+static int
+vensync(fd)
+{
+ int already = 0, nread;
+ char buf[60];
+
+ /*
+ * Toggle DTR to force anyone off that might have left
+ * the modem connected, and insure a consistent state
+ * to start from.
+ *
+ * If you don't have the ioctl calls to diddle directly
+ * with DTR, you can always try setting the baud rate to 0.
+ */
+ ioctl(FD, TIOCCDTR, 0);
+ sleep(1);
+ ioctl(FD, TIOCSDTR, 0);
+ while (already < MAXRETRY) {
+ /*
+ * After reseting the modem, send it two \r's to
+ * autobaud on. Make sure to delay between them
+ * so the modem can frame the incoming characters.
+ */
+ write(fd, "\r", 1);
+ delay(1,10);
+ write(fd, "\r", 1);
+ sleep(2);
+ if (ioctl(fd, FIONREAD, (caddr_t)&nread) < 0) {
+ perror("tip: ioctl");
+ continue;
+ }
+ while (nread > 0) {
+ read(fd, buf, min(nread, 60));
+ if ((buf[nread - 1] & 0177) == '$')
+ return (1);
+ nread -= min(nread, 60);
+ }
+ sleep(1);
+ already++;
+ }
+ return (0);
+}
+
diff --git a/usr.bin/tip/log.c b/usr.bin/tip/log.c
new file mode 100644
index 0000000..5da2c45
--- /dev/null
+++ b/usr.bin/tip/log.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)log.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+#ifdef ACULOG
+static FILE *flog = NULL;
+
+/*
+ * Log file maintenance routines
+ */
+
+logent(group, num, acu, message)
+ char *group, *num, *acu, *message;
+{
+ char *user, *timestamp;
+ struct passwd *pwd;
+ long t;
+
+ if (flog == NULL)
+ return;
+ if (flock(fileno(flog), LOCK_EX) < 0) {
+ perror("tip: flock");
+ return;
+ }
+ if ((user = getlogin()) == NOSTR)
+ if ((pwd = getpwuid(getuid())) == NOPWD)
+ user = "???";
+ else
+ user = pwd->pw_name;
+ t = time(0);
+ timestamp = ctime(&t);
+ timestamp[24] = '\0';
+ fprintf(flog, "%s (%s) <%s, %s, %s> %s\n",
+ user, timestamp, group,
+#ifdef PRISTINE
+ "",
+#else
+ num,
+#endif
+ acu, message);
+ (void) fflush(flog);
+ (void) flock(fileno(flog), LOCK_UN);
+}
+
+loginit()
+{
+ flog = fopen(value(LOG), "a");
+ if (flog == NULL)
+ fprintf(stderr, "can't open log file %s.\r\n", value(LOG));
+}
+#endif
diff --git a/usr.bin/tip/partab.c b/usr.bin/tip/partab.c
new file mode 100644
index 0000000..1da4e23
--- /dev/null
+++ b/usr.bin/tip/partab.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)partab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Even parity table for 0-0177
+ */
+char evenpartab[] = {
+ 0000,0201,0202,0003,0204,0005,0006,0207,
+ 0210,0011,0012,0213,0014,0215,0216,0017,
+ 0220,0021,0022,0223,0024,0225,0226,0027,
+ 0030,0231,0232,0033,0234,0035,0036,0237,
+ 0240,0041,0042,0243,0044,0245,0246,0047,
+ 0050,0251,0252,0053,0254,0055,0056,0257,
+ 0060,0261,0262,0063,0264,0065,0066,0267,
+ 0270,0071,0072,0273,0074,0275,0276,0077,
+ 0300,0101,0102,0303,0104,0305,0306,0107,
+ 0110,0311,0312,0113,0314,0115,0116,0317,
+ 0120,0321,0322,0123,0324,0125,0126,0327,
+ 0330,0131,0132,0333,0134,0335,0336,0137,
+ 0140,0341,0342,0143,0344,0145,0146,0347,
+ 0350,0151,0152,0353,0154,0355,0356,0157,
+ 0360,0161,0162,0363,0164,0365,0366,0167,
+ 0170,0371,0372,0173,0374,0175,0176,0377,
+};
diff --git a/usr.bin/tip/pathnames.h b/usr.bin/tip/pathnames.h
new file mode 100644
index 0000000..fae04cb
--- /dev/null
+++ b/usr.bin/tip/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_ACULOG "/var/log/aculog"
+#define _PATH_LOCKDIRNAME "/var/spool/lock/LCK..%s"
+#ifdef notdef
+#define _PATH_LOCKDIRNAME "/var/spool/uucp/LCK/LCK..%s"
+#endif
+#define _PATH_PHONES "/etc/phones"
+#define _PATH_REMOTE "/etc/remote"
diff --git a/usr.bin/tip/remcap.c b/usr.bin/tip/remcap.c
new file mode 100644
index 0000000..7a7e0c2
--- /dev/null
+++ b/usr.bin/tip/remcap.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)remcap.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent rgetent
+#define tnchktc rnchktc
+#define tnamatch rnamatch
+#define tgetnum rgetnum
+#define tgetflag rgetflag
+#define tgetstr rgetstr
+#define E_TERMCAP RM = _PATH_REMOTE
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+
+char *RM;
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+static char *tskip();
+char *tgetstr();
+static char *tdecode();
+static char *remotefile;
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+tgetent(bp, name)
+ char *bp, *name;
+{
+ char lbuf[BUFSIZ], *cp, *p;
+ int rc1, rc2;
+
+ remotefile = cp = getenv(V_TERMCAP);
+ if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) {
+ remotefile = cp = _PATH_REMOTE;
+ return (getent(bp, name, cp));
+ } else {
+ if ((rc1 = getent(bp, name, cp)) != 1)
+ *bp = '\0';
+ remotefile = cp = _PATH_REMOTE;
+ rc2 = getent(lbuf, name, cp);
+ if (rc1 != 1 && rc2 != 1)
+ return (rc2);
+ if (rc2 == 1) {
+ p = lbuf;
+ if (rc1 == 1)
+ while (*p++ != ':')
+ ;
+ if (strlen(bp) + strlen(p) > BUFSIZ) {
+ write(2, "Remcap entry too long\n", 23);
+ return (-1);
+ }
+ strcat(bp, p);
+ }
+ tbuf = bp;
+ return (1);
+ }
+}
+
+getent(bp, name, cp)
+ char *bp, *name, *cp;
+{
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[BUFSIZ], *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ if (*cp!='/') {
+ cp2 = getenv(V_TERM);
+ if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
+ strcpy(bp,cp);
+ return (tnchktc());
+ } else
+ tf = open(E_TERMCAP, O_RDONLY);
+ } else
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf == 0)
+ tf = open(E_TERMCAP, O_RDONLY);
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ write(2,"Remcap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+tnchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+ char *cp;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strcpy(tcname, p+3);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ if (strcmp(remotefile, _PATH_REMOTE) == 0)
+ return (0);
+ else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(2, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strcpy(p, q);
+ tbuf = holdtbuf;
+ return (1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+tnamatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return (0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ register char *bp;
+{
+
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+tgetnum(id)
+ char *id;
+{
+ register int i, base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+tgetflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.bin/tip/remote.c b/usr.bin/tip/remote.c
new file mode 100644
index 0000000..9b86066
--- /dev/null
+++ b/usr.bin/tip/remote.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)remote.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pathnames.h"
+#include "tip.h"
+
+/*
+ * Attributes to be gleened from remote host description
+ * data base.
+ */
+static char **caps[] = {
+ &AT, &DV, &CM, &CU, &EL, &IE, &OE, &PN, &PR, &DI,
+ &ES, &EX, &FO, &RC, &RE, &PA
+};
+
+static char *capstrings[] = {
+ "at", "dv", "cm", "cu", "el", "ie", "oe", "pn", "pr",
+ "di", "es", "ex", "fo", "rc", "re", "pa", 0
+};
+
+static char *db_array[3] = { _PATH_REMOTE, 0, 0 };
+
+#define cgetflag(f) (cgetcap(bp, f, ':') != NULL)
+
+static
+getremcap(host)
+ register char *host;
+{
+ register char **p, ***q;
+ char *bp;
+ char *rempath;
+ int stat;
+
+ rempath = getenv("REMOTE");
+ if (rempath != NULL)
+ if (*rempath != '/')
+ /* we have an entry */
+ cgetset(rempath);
+ else { /* we have a path */
+ db_array[1] = rempath;
+ db_array[2] = _PATH_REMOTE;
+ }
+
+ if ((stat = cgetent(&bp, db_array, host)) < 0) {
+ if (DV ||
+ host[0] == '/' && access(DV = host, R_OK | W_OK) == 0) {
+ CU = DV;
+ HO = host;
+ HW = 1;
+ DU = 0;
+ if (!BR)
+ BR = DEFBR;
+ FS = DEFFS;
+ return;
+ }
+ switch(stat) {
+ case -1:
+ fprintf(stderr, "tip: unknown host %s\n", host);
+ break;
+ case -2:
+ fprintf(stderr,
+ "tip: can't open host description file\n");
+ break;
+ case -3:
+ fprintf(stderr,
+ "tip: possible reference loop in host description file\n");
+ break;
+ }
+ exit(3);
+ }
+
+ for (p = capstrings, q = caps; *p != NULL; p++, q++)
+ if (**q == NULL)
+ cgetstr(bp, *p, *q);
+ if (!BR && (cgetnum(bp, "br", &BR) == -1))
+ BR = DEFBR;
+ if (cgetnum(bp, "fs", &FS) == -1)
+ FS = DEFFS;
+ if (DU < 0)
+ DU = 0;
+ else
+ DU = cgetflag("du");
+ if (DV == NOSTR) {
+ fprintf(stderr, "%s: missing device spec\n", host);
+ exit(3);
+ }
+ if (DU && CU == NOSTR)
+ CU = DV;
+ if (DU && PN == NOSTR) {
+ fprintf(stderr, "%s: missing phone number\n", host);
+ exit(3);
+ }
+
+ HD = cgetflag("hd");
+
+ /*
+ * This effectively eliminates the "hw" attribute
+ * from the description file
+ */
+ if (!HW)
+ HW = (CU == NOSTR) || (DU && equal(DV, CU));
+ HO = host;
+ /*
+ * see if uppercase mode should be turned on initially
+ */
+ if (cgetflag("ra"))
+ boolean(value(RAISE)) = 1;
+ if (cgetflag("ec"))
+ boolean(value(ECHOCHECK)) = 1;
+ if (cgetflag("be"))
+ boolean(value(BEAUTIFY)) = 1;
+ if (cgetflag("nb"))
+ boolean(value(BEAUTIFY)) = 0;
+ if (cgetflag("sc"))
+ boolean(value(SCRIPT)) = 1;
+ if (cgetflag("tb"))
+ boolean(value(TABEXPAND)) = 1;
+ if (cgetflag("vb"))
+ boolean(value(VERBOSE)) = 1;
+ if (cgetflag("nv"))
+ boolean(value(VERBOSE)) = 0;
+ if (cgetflag("ta"))
+ boolean(value(TAND)) = 1;
+ if (cgetflag("nt"))
+ boolean(value(TAND)) = 0;
+ if (cgetflag("rw"))
+ boolean(value(RAWFTP)) = 1;
+ if (cgetflag("hd"))
+ boolean(value(HALFDUPLEX)) = 1;
+ if (RE == NOSTR)
+ RE = (char *)"tip.record";
+ if (EX == NOSTR)
+ EX = (char *)"\t\n\b\f";
+ if (ES != NOSTR)
+ vstring("es", ES);
+ if (FO != NOSTR)
+ vstring("fo", FO);
+ if (PR != NOSTR)
+ vstring("pr", PR);
+ if (RC != NOSTR)
+ vstring("rc", RC);
+ if (cgetnum(bp, "dl", &DL) == -1)
+ DL = 0;
+ if (cgetnum(bp, "cl", &CL) == -1)
+ CL = 0;
+ if (cgetnum(bp, "et", &ET) == -1)
+ ET = 10;
+}
+
+char *
+getremote(host)
+ char *host;
+{
+ register char *cp;
+ static char *next;
+ static int lookedup = 0;
+
+ if (!lookedup) {
+ if (host == NOSTR && (host = getenv("HOST")) == NOSTR) {
+ fprintf(stderr, "tip: no host specified\n");
+ exit(3);
+ }
+ getremcap(host);
+ next = DV;
+ lookedup++;
+ }
+ /*
+ * We return a new device each time we're called (to allow
+ * a rotary action to be simulated)
+ */
+ if (next == NOSTR)
+ return (NOSTR);
+ if ((cp = index(next, ',')) == NULL) {
+ DV = next;
+ next = NOSTR;
+ } else {
+ *cp++ = '\0';
+ DV = next;
+ next = cp;
+ }
+ return (DV);
+}
diff --git a/usr.bin/tip/tip.1 b/usr.bin/tip/tip.1
new file mode 100644
index 0000000..10b8a3e
--- /dev/null
+++ b/usr.bin/tip/tip.1
@@ -0,0 +1,451 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tip.1 8.4 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt TIP 1
+.Os BSD 4
+.Sh NAME
+.Nm tip ,
+.Nm cu
+.Nd connect to a remote system
+.Sh SYNOPSIS
+.Nm tip
+.Op Fl v
+.Fl Ns Ns Ar speed
+.Ar system\-name
+.Nm tip
+.Op Fl v
+.Fl Ns Ns Ar speed
+.Ar phone\-number
+.Nm cu
+.Ar phone\-number
+.Op Fl t
+.Op Fl s Ar speed
+.Op Fl a Ar acu
+.Op Fl l Ar line
+.Op Fl #
+.Sh DESCRIPTION
+.Nm Tip
+and
+.Ar cu
+establish a full-duplex connection to another machine,
+giving the appearance of being logged in directly on the
+remote cpu. It goes without saying that you must have a login
+on the machine (or equivalent) to which you wish to connect.
+The preferred interface is
+.Nm tip .
+The
+.Ar cu
+interface is included for those people attached to the
+``call
+.Ux Ns ''
+command of version 7. This manual page
+describes only
+.Nm tip .
+.Pp
+Available Option:
+.Bl -tag -width indent
+.It Fl v
+Set verbose mode.
+.El
+.Pp
+Typed characters are normally transmitted directly to the remote
+machine (which does the echoing as well). A tilde (`~') appearing
+as the first character of a line is an escape signal; the following
+are recognized:
+.Bl -tag -width flag
+.It Ic \&~^D No or Ic \&~ .
+Drop the connection and exit
+(you may still be logged in on the
+remote machine).
+.It Ic \&~c Op Ar name
+Change directory to
+.Ar name
+(no argument
+implies change to your home directory).
+.It Ic \&~!
+Escape to a shell (exiting the shell will
+return you to tip).
+.It Ic \&~>
+Copy file from local to remote.
+.Nm Tip
+prompts for the name of a local file to transmit.
+.It Ic \&~<
+Copy file from remote to local.
+.Nm Tip
+prompts first for the name of the file to be sent, then for
+a command to be executed on the remote machine.
+.It Ic \&~p Ar from Op Ar to
+Send a file to a remote
+.Ux
+host. The put command causes the remote
+.Ux
+system to run the command string ``cat > 'to''', while
+.Nm tip
+sends it the ``from''
+file. If the ``to'' file isn't specified the ``from'' file name is used.
+This command is actually a
+.Ux
+specific version of the ``~>'' command.
+.It Ic \&~t Ar from Op Ar to
+Take a file from a remote
+.Ux
+host.
+As in the put command the ``to'' file
+defaults to the ``from'' file name if it isn't specified.
+The remote host
+executes the command string ``cat 'from';echo ^A'' to send the file to
+.Nm tip .
+.It Ic \&~|
+Pipe the output from a remote command to a local
+.Ux
+process.
+The command string sent to the local
+.Ux
+system is processed by the shell.
+.It Ic \&~$
+Pipe the output from a local
+.Ux
+process to the remote host.
+The command string sent to the local
+.Ux
+system is processed by the shell.
+.It Ic \&~C
+Fork a child process on the local system to perform special protocols
+such as \s-1XMODEM\s+1. The child program will be run with the following
+somewhat unusual arrangement of file descriptors:
+.nf
+.in +1i
+0 <-> local tty in
+1 <-> local tty out
+2 <-> local tty out
+3 <-> remote tty in
+4 <-> remote tty out
+.in -1i
+.fi
+.It Ic \&~#
+Send a
+.Dv BREAK
+to the remote system.
+For systems which don't support the
+necessary
+.Ar ioctl
+call the break is simulated by a sequence of line speed changes
+and
+.Dv DEL
+characters.
+.It Ic \&~s
+Set a variable (see the discussion below).
+.It Ic \&~^Z
+Stop
+.Nm tip
+(only available with job control).
+.It Ic \&~^Y
+Stop only the ``local side'' of
+.Nm tip
+(only available with job control);
+the ``remote side'' of
+.Nm tip ,
+the side that displays output from the remote host, is left running.
+.It Ic \&~?
+Get a summary of the tilde escapes
+.El
+.Pp
+.Nm Tip
+uses the file
+.Pa /etc/remote
+to find how to reach a particular
+system and to find out how it should operate while talking
+to the system;
+refer to
+.Xr remote 5
+for a full description.
+Each system has a default baud rate with which to
+establish a connection. If this value is not suitable, the baud rate
+to be used may be specified on the command line, e.g.
+.Ql "tip -300 mds" .
+.Pp
+When
+.Nm tip
+establishes a connection it sends out a
+connection message to the remote system; the default value, if any,
+is defined in
+.Pa /etc/remote
+(see
+.Xr remote 5 ) .
+.Pp
+When
+.Nm tip
+prompts for an argument (e.g. during setup of
+a file transfer) the line typed may be edited with the standard
+erase and kill characters. A null line in response to a prompt,
+or an interrupt, will abort the dialogue and return you to the
+remote machine.
+.Pp
+.Nm Tip
+guards against multiple users connecting to a remote system
+by opening modems and terminal lines with exclusive access,
+and by honoring the locking protocol used by
+.Xr uucico 8 .
+.Pp
+During file transfers
+.Nm tip
+provides a running count of the number of lines transferred.
+When using the ~> and ~< commands, the ``eofread'' and ``eofwrite''
+variables are used to recognize end-of-file when reading, and
+specify end-of-file when writing (see below). File transfers
+normally depend on tandem mode for flow control. If the remote
+system does not support tandem mode, ``echocheck'' may be set
+to indicate
+.Nm tip
+should synchronize with the remote system on the echo of each
+transmitted character.
+.Pp
+When
+.Nm tip
+must dial a phone number to connect to a system it will print
+various messages indicating its actions.
+.Nm Tip
+supports the
+.Tn DEC DN Ns-11
+and
+Racal-Vadic 831 auto-call-units;
+the
+.Tn DEC DF Ns \&02
+and
+.Tn DF Ns \&03 ,
+Ventel 212+, Racal-Vadic 3451, and
+Bizcomp 1031 and 1032 integral call unit/modems.
+.Ss VARIABLES
+.Nm Tip
+maintains a set of
+.Ar variables
+which control its operation.
+Some of these variables are read-only to normal users (root is allowed
+to change anything of interest). Variables may be displayed
+and set through the ``s'' escape. The syntax for variables is patterned
+after
+.Xr vi 1
+and
+.Xr Mail 1 .
+Supplying ``all''
+as an argument to the set command displays all variables readable by
+the user. Alternatively, the user may request display of a particular
+variable by attaching a `?' to the end. For example ``escape?''
+displays the current escape character.
+.Pp
+Variables are numeric, string, character, or boolean values. Boolean
+variables are set merely by specifying their name; they may be reset
+by prepending a `!' to the name. Other variable types are set by
+concatenating an `=' and the value. The entire assignment must not
+have any blanks in it. A single set command may be used to interrogate
+as well as set a number of variables.
+Variables may be initialized at run time by placing set commands
+(without the ``~s'' prefix in a file
+.Pa .tiprc
+in one's home directory). The
+.Fl v
+option causes
+.Nm tip
+to display the sets as they are made.
+Certain common variables have abbreviations.
+The following is a list of common variables,
+their abbreviations, and their default values.
+.Bl -tag -width Ar
+.It Ar beautify
+(bool) Discard unprintable characters when a session is being scripted;
+abbreviated
+.Ar be .
+.It Ar baudrate
+(num) The baud rate at which the connection was established;
+abbreviated
+.Ar ba .
+.It Ar dialtimeout
+(num) When dialing a phone number, the time (in seconds)
+to wait for a connection to be established; abbreviated
+.Ar dial .
+.It Ar echocheck
+(bool) Synchronize with the remote host during file transfer by
+waiting for the echo of the last character transmitted; default is
+.Ar off .
+.It Ar eofread
+(str) The set of characters which signify an end-of-transmission
+during a ~< file transfer command; abbreviated
+.Ar eofr .
+.It Ar eofwrite
+(str) The string sent to indicate end-of-transmission during
+a ~> file transfer command; abbreviated
+.Ar eofw .
+.It Ar eol
+(str) The set of characters which indicate an end-of-line.
+.Nm Tip
+will recognize escape characters only after an end-of-line.
+.It Ar escape
+(char) The command prefix (escape) character; abbreviated
+.Ar es ;
+default value is `~'.
+.It Ar exceptions
+(str) The set of characters which should not be discarded
+due to the beautification switch; abbreviated
+.Ar ex ;
+default value is ``\et\en\ef\eb''.
+.It Ar force
+(char) The character used to force literal data transmission;
+abbreviated
+.Ar fo ;
+default value is `^P'.
+.It Ar framesize
+(num) The amount of data (in bytes) to buffer between file system
+writes when receiving files; abbreviated
+.Ar fr .
+.It Ar host
+(str) The name of the host to which you are connected; abbreviated
+.Ar ho .
+.It Ar prompt
+(char) The character which indicates an end-of-line on the remote
+host; abbreviated
+.Ar pr ;
+default value is `\en'. This value is used to synchronize during
+data transfers. The count of lines transferred during a file transfer
+command is based on receipt of this character.
+.It Ar raise
+(bool) Upper case mapping mode; abbreviated
+.Ar ra ;
+default value is
+.Ar off .
+When this mode is enabled, all lower case letters will be mapped to
+upper case by
+.Nm tip
+for transmission to the remote machine.
+.It Ar raisechar
+(char) The input character used to toggle upper case mapping mode;
+abbreviated
+.Ar rc ;
+default value is `^A'.
+.It Ar record
+(str) The name of the file in which a session script is recorded;
+abbreviated
+.Ar rec ;
+default value is ``tip.record''.
+.It Ar script
+(bool) Session scripting mode; abbreviated
+.Ar sc ;
+default is
+.Ar off .
+When
+.Ar script
+is
+.Li true ,
+.Nm tip
+will record everything transmitted by the remote machine in
+the script record file specified in
+.Ar record .
+If the
+.Ar beautify
+switch is on, only printable
+.Tn ASCII
+characters will be included in
+the script file (those characters betwee 040 and 0177). The
+variable
+.Ar exceptions
+is used to indicate characters which are an exception to the normal
+beautification rules.
+.It Ar tabexpand
+(bool) Expand tabs to spaces during file transfers; abbreviated
+.Ar tab ;
+default value is
+.Ar false .
+Each tab is expanded to 8 spaces.
+.It Ar verbose
+(bool) Verbose mode; abbreviated
+.Ar verb ;
+default is
+.Ar true .
+When verbose mode is enabled,
+.Nm tip
+prints messages while dialing, shows the current number
+of lines transferred during a file transfer operations,
+and more.
+.El
+.Sh ENVIRONMENT
+.Nm Tip
+uses the following environment variables:
+.Bl -tag -width Fl
+.It Ev SHELL
+(str) The name of the shell to use for the ~! command; default
+value is ``/bin/sh'', or taken from the environment.
+.It Ev HOME
+(str) The home directory to use for the ~c command; default
+value is taken from the environment.
+.It Ev HOST
+Check for a default host if none specified.
+.El
+.Pp
+The variables
+.Ev ${REMOTE}
+and
+.Ev ${PHONES}
+are also exported.
+.Sh FILES
+.Bl -tag -width /var/spool/uucp/LCK..* -compact
+.It Pa /etc/remote
+Global system descriptions.
+.It Pa /etc/phones
+Global phone number data base.
+.It ${REMOTE}
+Private system descriptions.
+.It ${PHONES}
+Private phone numbers.
+.It ~/.tiprc
+Initialization file.
+.It Pa tip.record
+Record file.
+.It /var/log/aculog
+Line access log.
+.It Pa /var/spool/uucp/LCK..*
+Lock file to avoid conflicts with
+.Xr uucp .
+.El
+.Sh DIAGNOSTICS
+Diagnostics are, hopefully, self explanatory.
+.Sh SEE ALSO
+.Xr remote 5 ,
+.Xr phones 5
+.Sh HISTORY
+The
+.Nm tip
+appeared command in
+.Bx 4.2 .
+.Sh BUGS
+The full set of variables is undocumented and should, probably, be
+pared down.
diff --git a/usr.bin/tip/tip.c b/usr.bin/tip/tip.c
new file mode 100644
index 0000000..0d84e75
--- /dev/null
+++ b/usr.bin/tip/tip.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * tip - UNIX link to other systems
+ * tip [-v] [-speed] system-name
+ * or
+ * cu phone-number [-s speed] [-l line] [-a acu]
+ */
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * Baud rate mapping table
+ */
+int bauds[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600,
+ 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1
+};
+
+int disc = OTTYDISC; /* tip normally runs this way */
+void intprompt();
+void timeout();
+void cleanup();
+void tipdone();
+char *sname();
+char PNbuf[256]; /* This limits the size of a number */
+
+main(argc, argv)
+ char *argv[];
+{
+ char *system = NOSTR;
+ register int i;
+ register char *p;
+ char sbuf[12];
+
+ gid = getgid();
+ egid = getegid();
+ uid = getuid();
+ euid = geteuid();
+ if (equal(sname(argv[0]), "cu")) {
+ cumode = 1;
+ cumain(argc, argv);
+ goto cucommon;
+ }
+
+ if (argc > 4) {
+ fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
+ exit(1);
+ }
+ if (!isatty(0)) {
+ fprintf(stderr, "tip: must be interactive\n");
+ exit(1);
+ }
+
+ for (; argc > 1; argv++, argc--) {
+ if (argv[1][0] != '-')
+ system = argv[1];
+ else switch (argv[1][1]) {
+
+ case 'v':
+ vflag++;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ BR = atoi(&argv[1][1]);
+ break;
+
+ default:
+ fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
+ break;
+ }
+ }
+
+ if (system == NOSTR)
+ goto notnumber;
+ if (isalpha(*system))
+ goto notnumber;
+ /*
+ * System name is really a phone number...
+ * Copy the number then stomp on the original (in case the number
+ * is private, we don't want 'ps' or 'w' to find it).
+ */
+ if (strlen(system) > sizeof PNbuf - 1) {
+ fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
+ sizeof PNbuf - 1);
+ exit(1);
+ }
+ strncpy( PNbuf, system, sizeof PNbuf - 1 );
+ for (p = system; *p; p++)
+ *p = '\0';
+ PN = PNbuf;
+ (void)sprintf(sbuf, "tip%d", BR);
+ system = sbuf;
+
+notnumber:
+ (void)signal(SIGINT, cleanup);
+ (void)signal(SIGQUIT, cleanup);
+ (void)signal(SIGHUP, cleanup);
+ (void)signal(SIGTERM, cleanup);
+ (void)signal(SIGUSR1, tipdone);
+
+ if ((i = hunt(system)) == 0) {
+ printf("all ports busy\n");
+ exit(3);
+ }
+ if (i == -1) {
+ printf("link down\n");
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+ setbuf(stdout, NULL);
+ loginit();
+
+ /*
+ * Kludge, their's no easy way to get the initialization
+ * in the right order, so force it here
+ */
+ if ((PH = getenv("PHONES")) == NOSTR)
+ PH = _PATH_PHONES;
+ vinit(); /* init variables */
+ setparity("even"); /* set the parity table */
+ if ((i = speed(number(value(BAUDRATE)))) == NULL) {
+ printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+
+ /*
+ * Now that we have the logfile and the ACU open
+ * return to the real uid and gid. These things will
+ * be closed on exit. Swap real and effective uid's
+ * so we can get the original permissions back
+ * for removing the uucp lock.
+ */
+ user_uid();
+
+ /*
+ * Hardwired connections require the
+ * line speed set before they make any transmissions
+ * (this is particularly true of things like a DF03-AC)
+ */
+ if (HW)
+ ttysetup(i);
+ if (p = connect()) {
+ printf("\07%s\n[EOT]\n", p);
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ exit(1);
+ }
+ if (!HW)
+ ttysetup(i);
+cucommon:
+ /*
+ * From here down the code is shared with
+ * the "cu" version of tip.
+ */
+
+ ioctl(0, TIOCGETP, (char *)&defarg);
+ ioctl(0, TIOCGETC, (char *)&defchars);
+ ioctl(0, TIOCGLTC, (char *)&deflchars);
+ ioctl(0, TIOCGETD, (char *)&odisc);
+ arg = defarg;
+ arg.sg_flags = ANYP | CBREAK;
+ tchars = defchars;
+ tchars.t_intrc = tchars.t_quitc = -1;
+ ltchars = deflchars;
+ ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
+ = ltchars.t_lnextc = -1;
+ raw();
+
+ pipe(fildes); pipe(repdes);
+ (void)signal(SIGALRM, timeout);
+
+ /*
+ * Everything's set up now:
+ * connection established (hardwired or dialup)
+ * line conditioned (baud rate, mode, etc.)
+ * internal data structures (variables)
+ * so, fork one process for local side and one for remote.
+ */
+ printf(cumode ? "Connected\r\n" : "\07connected\r\n");
+ if (pid = fork())
+ tipin();
+ else
+ tipout();
+ /*NOTREACHED*/
+}
+
+void
+cleanup()
+{
+
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ if (odisc)
+ ioctl(0, TIOCSETD, (char *)&odisc);
+ exit(0);
+}
+
+void
+tipdone()
+{
+ tipabort("Hangup.");
+}
+/*
+ * Muck with user ID's. We are setuid to the owner of the lock
+ * directory when we start. user_uid() reverses real and effective
+ * ID's after startup, to run with the user's permissions.
+ * daemon_uid() switches back to the privileged uid for unlocking.
+ * Finally, to avoid running a shell with the wrong real uid,
+ * shell_uid() sets real and effective uid's to the user's real ID.
+ */
+static int uidswapped;
+
+user_uid()
+{
+ if (uidswapped == 0) {
+ seteuid(uid);
+ uidswapped = 1;
+ }
+}
+
+daemon_uid()
+{
+
+ if (uidswapped) {
+ seteuid(euid);
+ uidswapped = 0;
+ }
+}
+
+shell_uid()
+{
+
+ seteuid(uid);
+}
+
+/*
+ * put the controlling keyboard into raw mode
+ */
+raw()
+{
+
+ ioctl(0, TIOCSETP, &arg);
+ ioctl(0, TIOCSETC, &tchars);
+ ioctl(0, TIOCSLTC, &ltchars);
+ ioctl(0, TIOCSETD, (char *)&disc);
+}
+
+
+/*
+ * return keyboard to normal mode
+ */
+unraw()
+{
+
+ ioctl(0, TIOCSETD, (char *)&odisc);
+ ioctl(0, TIOCSETP, (char *)&defarg);
+ ioctl(0, TIOCSETC, (char *)&defchars);
+ ioctl(0, TIOCSLTC, (char *)&deflchars);
+}
+
+static jmp_buf promptbuf;
+
+/*
+ * Print string ``s'', then read a string
+ * in from the terminal. Handles signals & allows use of
+ * normal erase and kill characters.
+ */
+prompt(s, p)
+ char *s;
+ register char *p;
+{
+ register char *b = p;
+ sig_t oint, oquit;
+
+ stoprompt = 0;
+ oint = signal(SIGINT, intprompt);
+ oquit = signal(SIGQUIT, SIG_IGN);
+ unraw();
+ printf("%s", s);
+ if (setjmp(promptbuf) == 0)
+ while ((*p = getchar()) != EOF && *p != '\n')
+ p++;
+ *p = '\0';
+
+ raw();
+ (void)signal(SIGINT, oint);
+ (void)signal(SIGQUIT, oquit);
+ return (stoprompt || p == b);
+}
+
+/*
+ * Interrupt service routine during prompting
+ */
+void
+intprompt()
+{
+
+ (void)signal(SIGINT, SIG_IGN);
+ stoprompt = 1;
+ printf("\r\n");
+ longjmp(promptbuf, 1);
+}
+
+/*
+ * ****TIPIN TIPIN****
+ */
+tipin()
+{
+ char gch, bol = 1;
+
+ /*
+ * Kinda klugey here...
+ * check for scripting being turned on from the .tiprc file,
+ * but be careful about just using setscript(), as we may
+ * send a SIGEMT before tipout has a chance to set up catching
+ * it; so wait a second, then setscript()
+ */
+ if (boolean(value(SCRIPT))) {
+ sleep(1);
+ setscript();
+ }
+
+ while (1) {
+ gch = getchar()&0177;
+ if ((gch == character(value(ESCAPE))) && bol) {
+ if (!(gch = escape()))
+ continue;
+ } else if (!cumode && gch == character(value(RAISECHAR))) {
+ boolean(value(RAISE)) = !boolean(value(RAISE));
+ continue;
+ } else if (gch == '\r') {
+ bol = 1;
+ pwrite(FD, &gch, 1);
+ if (boolean(value(HALFDUPLEX)))
+ printf("\r\n");
+ continue;
+ } else if (!cumode && gch == character(value(FORCE)))
+ gch = getchar()&0177;
+ bol = any(gch, value(EOL));
+ if (boolean(value(RAISE)) && islower(gch))
+ gch = toupper(gch);
+ pwrite(FD, &gch, 1);
+ if (boolean(value(HALFDUPLEX)))
+ printf("%c", gch);
+ }
+}
+
+extern esctable_t etable[];
+
+/*
+ * Escape handler --
+ * called on recognition of ``escapec'' at the beginning of a line
+ */
+escape()
+{
+ register char gch;
+ register esctable_t *p;
+ char c = character(value(ESCAPE));
+
+ gch = (getchar()&0177);
+ for (p = etable; p->e_char; p++)
+ if (p->e_char == gch) {
+ if ((p->e_flags&PRIV) && uid)
+ continue;
+ printf("%s", ctrl(c));
+ (*p->e_func)(gch);
+ return (0);
+ }
+ /* ESCAPE ESCAPE forces ESCAPE */
+ if (c != gch)
+ pwrite(FD, &c, 1);
+ return (gch);
+}
+
+speed(n)
+ int n;
+{
+ register int *p;
+
+ for (p = bauds; *p != -1; p++)
+ if (*p == n)
+ return (p - bauds);
+ return (NULL);
+}
+
+any(c, p)
+ register char c, *p;
+{
+ while (p && *p)
+ if (*p++ == c)
+ return (1);
+ return (0);
+}
+
+size(s)
+ register char *s;
+{
+ register int i = 0;
+
+ while (s && *s++)
+ i++;
+ return (i);
+}
+
+char *
+interp(s)
+ register char *s;
+{
+ static char buf[256];
+ register char *p = buf, c, *q;
+
+ while (c = *s++) {
+ for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
+ if (*q++ == c) {
+ *p++ = '\\'; *p++ = *q;
+ goto next;
+ }
+ if (c < 040) {
+ *p++ = '^'; *p++ = c + 'A'-1;
+ } else if (c == 0177) {
+ *p++ = '^'; *p++ = '?';
+ } else
+ *p++ = c;
+ next:
+ ;
+ }
+ *p = '\0';
+ return (buf);
+}
+
+char *
+ctrl(c)
+ char c;
+{
+ static char s[3];
+
+ if (c < 040 || c == 0177) {
+ s[0] = '^';
+ s[1] = c == 0177 ? '?' : c+'A'-1;
+ s[2] = '\0';
+ } else {
+ s[0] = c;
+ s[1] = '\0';
+ }
+ return (s);
+}
+
+/*
+ * Help command
+ */
+help(c)
+ char c;
+{
+ register esctable_t *p;
+
+ printf("%c\r\n", c);
+ for (p = etable; p->e_char; p++) {
+ if ((p->e_flags&PRIV) && uid)
+ continue;
+ printf("%2s", ctrl(character(value(ESCAPE))));
+ printf("%-2s %c %s\r\n", ctrl(p->e_char),
+ p->e_flags&EXP ? '*': ' ', p->e_help);
+ }
+}
+
+/*
+ * Set up the "remote" tty's state
+ */
+ttysetup(speed)
+ int speed;
+{
+ unsigned bits = LDECCTQ;
+
+ arg.sg_ispeed = arg.sg_ospeed = speed;
+ arg.sg_flags = RAW;
+ if (boolean(value(TAND)))
+ arg.sg_flags |= TANDEM;
+ ioctl(FD, TIOCSETP, (char *)&arg);
+ ioctl(FD, TIOCLBIS, (char *)&bits);
+}
+
+/*
+ * Return "simple" name from a file name,
+ * strip leading directories.
+ */
+char *
+sname(s)
+ register char *s;
+{
+ register char *p = s;
+
+ while (*s)
+ if (*s++ == '/')
+ p = s;
+ return (p);
+}
+
+static char partab[0200];
+static int bits8;
+
+/*
+ * Do a write to the remote machine with the correct parity.
+ * We are doing 8 bit wide output, so we just generate a character
+ * with the right parity and output it.
+ */
+pwrite(fd, buf, n)
+ int fd;
+ char *buf;
+ register int n;
+{
+ register int i;
+ register char *bp;
+ extern int errno;
+
+ bp = buf;
+ if (bits8 == 0)
+ for (i = 0; i < n; i++) {
+ *bp = partab[(*bp) & 0177];
+ bp++;
+ }
+ if (write(fd, buf, n) < 0) {
+ if (errno == EIO)
+ tipabort("Lost carrier.");
+ if (errno == ENODEV)
+ tipabort("tty not available.");
+ /* this is questionable */
+ perror("write");
+ }
+}
+
+/*
+ * Build a parity table with appropriate high-order bit.
+ */
+setparity(defparity)
+ char *defparity;
+{
+ register int i, flip, clr, set;
+ char *parity;
+ extern char evenpartab[];
+
+ if (value(PARITY) == NOSTR)
+ value(PARITY) = defparity;
+ parity = value(PARITY);
+ if (equal(parity, "none")) {
+ bits8 = 1;
+ return;
+ }
+ bits8 = 0;
+ flip = 0;
+ clr = 0377;
+ set = 0;
+ if (equal(parity, "odd"))
+ flip = 0200; /* reverse bit 7 */
+ else if (equal(parity, "zero"))
+ clr = 0177; /* turn off bit 7 */
+ else if (equal(parity, "one"))
+ set = 0200; /* turn on bit 7 */
+ else if (!equal(parity, "even")) {
+ (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
+ (void) fflush(stderr);
+ }
+ for (i = 0; i < 0200; i++)
+ partab[i] = evenpartab[i] ^ flip | set & clr;
+}
diff --git a/usr.bin/tip/tip.h b/usr.bin/tip/tip.h
new file mode 100644
index 0000000..f1b833d
--- /dev/null
+++ b/usr.bin/tip/tip.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tip.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * tip - terminal interface program
+ */
+
+#include <sys/types.h>
+#include <machine/endian.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#include <sgtty.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * Remote host attributes
+ */
+char *DV; /* UNIX device(s) to open */
+char *EL; /* chars marking an EOL */
+char *CM; /* initial connection message */
+char *IE; /* EOT to expect on input */
+char *OE; /* EOT to send to complete FT */
+char *CU; /* call unit if making a phone call */
+char *AT; /* acu type */
+char *PN; /* phone number(s) */
+char *DI; /* disconnect string */
+char *PA; /* parity to be generated */
+
+char *PH; /* phone number file */
+char *RM; /* remote file name */
+char *HO; /* host name */
+
+long BR; /* line speed for conversation */
+long FS; /* frame size for transfers */
+
+char DU; /* this host is dialed up */
+char HW; /* this device is hardwired, see hunt.c */
+char *ES; /* escape character */
+char *EX; /* exceptions */
+char *FO; /* force (literal next) char*/
+char *RC; /* raise character */
+char *RE; /* script record file */
+char *PR; /* remote prompt */
+long DL; /* line delay for file transfers to remote */
+long CL; /* char delay for file transfers to remote */
+long ET; /* echocheck timeout */
+char HD; /* this host is half duplex - do local echo */
+
+/*
+ * String value table
+ */
+typedef
+ struct {
+ char *v_name; /* whose name is it */
+ char v_type; /* for interpreting set's */
+ char v_access; /* protection of touchy ones */
+ char *v_abrev; /* possible abreviation */
+ char *v_value; /* casted to a union later */
+ }
+ value_t;
+
+#define STRING 01 /* string valued */
+#define BOOL 02 /* true-false value */
+#define NUMBER 04 /* numeric value */
+#define CHAR 010 /* character value */
+
+#define WRITE 01 /* write access to variable */
+#define READ 02 /* read access */
+
+#define CHANGED 01 /* low bit is used to show modification */
+#define PUBLIC 1 /* public access rights */
+#define PRIVATE 03 /* private to definer */
+#define ROOT 05 /* root defined */
+
+#define TRUE 1
+#define FALSE 0
+
+#define ENVIRON 020 /* initialize out of the environment */
+#define IREMOTE 040 /* initialize out of remote structure */
+#define INIT 0100 /* static data space used for initialization */
+#define TMASK 017
+
+/*
+ * Definition of ACU line description
+ */
+typedef
+ struct {
+ char *acu_name;
+ int (*acu_dialer)();
+ int (*acu_disconnect)();
+ int (*acu_abort)();
+ }
+ acu_t;
+
+#define equal(a, b) (strcmp(a,b)==0)/* A nice function to string compare */
+
+/*
+ * variable manipulation stuff --
+ * if we defined the value entry in value_t, then we couldn't
+ * initialize it in vars.c, so we cast it as needed to keep lint
+ * happy.
+ */
+typedef
+ union {
+ int zz_number;
+ short zz_boolean[2];
+ char zz_character[4];
+ int *zz_address;
+ }
+ zzhack;
+
+#define value(v) vtable[v].v_value
+
+#define number(v) ((((zzhack *)(&(v))))->zz_number)
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define boolean(v) ((((zzhack *)(&(v))))->zz_boolean[0])
+#define character(v) ((((zzhack *)(&(v))))->zz_character[0])
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define boolean(v) ((((zzhack *)(&(v))))->zz_boolean[1])
+#define character(v) ((((zzhack *)(&(v))))->zz_character[3])
+#endif
+
+#define address(v) ((((zzhack *)(&(v))))->zz_address)
+
+/*
+ * Escape command table definitions --
+ * lookup in this table is performed when ``escapec'' is recognized
+ * at the begining of a line (as defined by the eolmarks variable).
+*/
+
+typedef
+ struct {
+ char e_char; /* char to match on */
+ char e_flags; /* experimental, priviledged */
+ char *e_help; /* help string */
+ int (*e_func)(); /* command */
+ }
+ esctable_t;
+
+#define NORM 00 /* normal protection, execute anyone */
+#define EXP 01 /* experimental, mark it with a `*' on help */
+#define PRIV 02 /* priviledged, root execute only */
+
+extern int vflag; /* verbose during reading of .tiprc file */
+extern value_t vtable[]; /* variable table */
+
+#ifndef ACULOG
+#define logent(a, b, c, d)
+#define loginit()
+#endif
+
+/*
+ * Definition of indices into variable table so
+ * value(DEFINE) turns into a static address.
+ */
+
+#define BEAUTIFY 0
+#define BAUDRATE 1
+#define DIALTIMEOUT 2
+#define EOFREAD 3
+#define EOFWRITE 4
+#define EOL 5
+#define ESCAPE 6
+#define EXCEPTIONS 7
+#define FORCE 8
+#define FRAMESIZE 9
+#define HOST 10
+#define LOG 11
+#define PHONES 12
+#define PROMPT 13
+#define RAISE 14
+#define RAISECHAR 15
+#define RECORD 16
+#define REMOTE 17
+#define SCRIPT 18
+#define TABEXPAND 19
+#define VERBOSE 20
+#define SHELL 21
+#define HOME 22
+#define ECHOCHECK 23
+#define DISCONNECT 24
+#define TAND 25
+#define LDELAY 26
+#define CDELAY 27
+#define ETIMEOUT 28
+#define RAWFTP 29
+#define HALFDUPLEX 30
+#define LECHO 31
+#define PARITY 32
+
+#define NOVAL ((value_t *)NULL)
+#define NOACU ((acu_t *)NULL)
+#define NOSTR ((char *)NULL)
+#define NOFILE ((FILE *)NULL)
+#define NOPWD ((struct passwd *)0)
+
+struct sgttyb arg; /* current mode of local terminal */
+struct sgttyb defarg; /* initial mode of local terminal */
+struct tchars tchars; /* current state of terminal */
+struct tchars defchars; /* initial state of terminal */
+struct ltchars ltchars; /* current local characters of terminal */
+struct ltchars deflchars; /* initial local characters of terminal */
+
+FILE *fscript; /* FILE for scripting */
+
+int fildes[2]; /* file transfer synchronization channel */
+int repdes[2]; /* read process sychronization channel */
+int FD; /* open file descriptor to remote host */
+int AC; /* open file descriptor to dialer (v831 only) */
+int vflag; /* print .tiprc initialization sequence */
+int sfd; /* for ~< operation */
+int pid; /* pid of tipout */
+uid_t uid, euid; /* real and effective user id's */
+gid_t gid, egid; /* real and effective group id's */
+int stop; /* stop transfer session flag */
+int quit; /* same; but on other end */
+int intflag; /* recognized interrupt */
+int stoprompt; /* for interrupting a prompt session */
+int timedout; /* ~> transfer timedout */
+int cumode; /* simulating the "cu" program */
+
+char fname[80]; /* file name buffer for ~< */
+char copyname[80]; /* file name buffer for ~> */
+char ccc; /* synchronization character */
+char ch; /* for tipout */
+char *uucplock; /* name of lock file for uucp's */
+
+int odisc; /* initial tty line discipline */
+extern int disc; /* current tty discpline */
+
+extern char *ctrl();
+extern char *vinterp();
+extern char *connect();
+
+int tipabort __P((char *));
diff --git a/usr.bin/tip/tip/Makefile b/usr.bin/tip/tip/Makefile
new file mode 100644
index 0000000..702b52b
--- /dev/null
+++ b/usr.bin/tip/tip/Makefile
@@ -0,0 +1,24 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+#
+# Files are:
+# /etc/remote remote host description file
+# /etc/phones phone number file, owned by ${OWNER} and
+# mode 6??
+# /var/log/aculog ACU accounting file, owned by ${OWNER} and
+# mode 6?? {if ACULOG defined}
+
+.if exists(${.CURDIR}/../libacu/obj)
+LIBACU=${.CURDIR}/../libacu/obj/libacu.a
+.else
+LIBACU=${.CURDIR}/../libacu/libacu.a
+.endif
+
+PROG= tip
+LDADD+= $(LIBACU)
+LINKS= ${BINDIR}/tip
+MAN1= tip.1
+MAN5= modems.5
+SRCS= acu.c acutab.c cmds.c cmdtab.c cu.c hunt.c log.c partab.c \
+ remote.c tip.c tipout.c uucplock.c value.c vars.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tip/tip/acu.c b/usr.bin/tip/tip/acu.c
new file mode 100644
index 0000000..d320249
--- /dev/null
+++ b/usr.bin/tip/tip/acu.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)acu.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+#if UNIDIALER
+acu_t* unidialer_getmodem (const char *modem_name);
+#endif
+
+static acu_t *acu = NOACU;
+static int conflag;
+static void acuabort();
+static acu_t *acutype();
+static jmp_buf jmpbuf;
+/*
+ * Establish connection for tip
+ *
+ * If DU is true, we should dial an ACU whose type is AT.
+ * The phone numbers are in PN, and the call unit is in CU.
+ *
+ * If the PN is an '@', then we consult the PHONES file for
+ * the phone numbers. This file is /etc/phones, unless overriden
+ * by an exported shell variable.
+ *
+ * The data base files must be in the format:
+ * host-name[ \t]*phone-number
+ * with the possibility of multiple phone numbers
+ * for a single host acting as a rotary (in the order
+ * found in the file).
+ */
+char *
+connect()
+{
+ register char *cp = PN;
+ char *phnum, string[256];
+ FILE *fd;
+ int tried = 0;
+
+ if (!DU) { /* regular connect message */
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), "", DV, "call completed");
+ return (NOSTR);
+ }
+ /*
+ * @ =>'s use data base in PHONES environment variable
+ * otherwise, use /etc/phones
+ */
+ signal(SIGINT, acuabort);
+ signal(SIGQUIT, acuabort);
+ if (setjmp(jmpbuf)) {
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ printf("\ncall aborted\n");
+ logent(value(HOST), "", "", "call aborted");
+ if (acu != NOACU) {
+ boolean(value(VERBOSE)) = FALSE;
+ if (conflag)
+ disconnect(NOSTR);
+ else
+ (*acu->acu_abort)();
+ }
+ return ("interrupt");
+ }
+ if ((acu = acutype(AT)) == NOACU)
+ return ("unknown ACU type");
+ if (*cp != '@') {
+ while (*cp) {
+ for (phnum = cp; *cp && *cp != ','; cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+
+ if (conflag = (*acu->acu_dialer)(phnum, CU)) {
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), phnum, acu->acu_name,
+ "call completed");
+ return (NOSTR);
+ } else
+ logent(value(HOST), phnum, acu->acu_name,
+ "call failed");
+ tried++;
+ }
+ } else {
+ if ((fd = fopen(PH, "r")) == NOFILE) {
+ printf("%s: ", PH);
+ return ("can't open phone number file");
+ }
+ while (fgets(string, sizeof(string), fd) != NOSTR) {
+ for (cp = string; !any(*cp, " \t\n"); cp++)
+ ;
+ if (*cp == '\n') {
+ fclose(fd);
+ return ("unrecognizable host name");
+ }
+ *cp++ = '\0';
+ if (strcmp(string, value(HOST)))
+ continue;
+ while (any(*cp, " \t"))
+ cp++;
+ if (*cp == '\n') {
+ fclose(fd);
+ return ("missing phone number");
+ }
+ for (phnum = cp; *cp && *cp != ',' && *cp != '\n'; cp++)
+ ;
+ if (*cp)
+ *cp++ = '\0';
+
+ if (conflag = (*acu->acu_dialer)(phnum, CU)) {
+ fclose(fd);
+ if (CM != NOSTR)
+ pwrite(FD, CM, size(CM));
+ logent(value(HOST), phnum, acu->acu_name,
+ "call completed");
+ return (NOSTR);
+ } else
+ logent(value(HOST), phnum, acu->acu_name,
+ "call failed");
+ tried++;
+ }
+ fclose(fd);
+ }
+ if (!tried)
+ logent(value(HOST), "", acu->acu_name, "missing phone number");
+ else
+ (*acu->acu_abort)();
+ return (tried ? "call failed" : "missing phone number");
+}
+
+disconnect(reason)
+ char *reason;
+{
+ if (!conflag) {
+ logent(value(HOST), "", DV, "call terminated");
+ return;
+ }
+ if (reason == NOSTR) {
+ logent(value(HOST), "", acu->acu_name, "call terminated");
+ if (boolean(value(VERBOSE)))
+ printf("\r\ndisconnecting...");
+ } else
+ logent(value(HOST), "", acu->acu_name, reason);
+ (*acu->acu_disconnect)();
+}
+
+static void
+acuabort(s)
+{
+ signal(s, SIG_IGN);
+ longjmp(jmpbuf, 1);
+}
+
+static acu_t *
+acutype(s)
+ register char *s;
+{
+ register acu_t *p;
+ extern acu_t acutable[];
+
+ for (p = acutable; p->acu_name != '\0'; p++)
+ if (!strcmp(s, p->acu_name))
+ return (p);
+
+ #if UNIDIALER
+ return unidialer_getmodem (s);
+ #else
+ return (NOACU);
+ #endif
+}
diff --git a/usr.bin/tip/tip/acutab.c b/usr.bin/tip/tip/acutab.c
new file mode 100644
index 0000000..c482493
--- /dev/null
+++ b/usr.bin/tip/tip/acutab.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)acutab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+extern int df02_dialer(), df03_dialer(),
+ biz31f_dialer(),
+ biz31w_dialer(),
+ biz22f_dialer(),
+ biz22w_dialer(),
+ ven_dialer(),
+ hay_dialer(),
+ cour_dialer(),
+ multitech_dialer(),
+ t3000_dialer(),
+ v3451_dialer(),
+ v831_dialer(),
+ dn_dialer();
+
+extern void df_disconnect(), df_abort(),
+ biz31_disconnect(), biz31_abort(),
+ biz22_disconnect(), biz22_abort(),
+ ven_disconnect(), ven_abort(),
+ hay_disconnect(), hay_abort(),
+ cour_disconnect(), cour_abort(),
+ multitech_disconnect(), multitech_abort(),
+ t3000_disconnect(), t3000_abort(),
+ v3451_disconnect(), v3451_abort(),
+ v831_disconnect(), v831_abort(),
+ dn_disconnect(), dn_abort();
+
+acu_t acutable[] = {
+#if BIZ1031
+ "biz31f", biz31f_dialer, biz31_disconnect, biz31_abort,
+ "biz31w", biz31w_dialer, biz31_disconnect, biz31_abort,
+#endif
+#if BIZ1022
+ "biz22f", biz22f_dialer, biz22_disconnect, biz22_abort,
+ "biz22w", biz22w_dialer, biz22_disconnect, biz22_abort,
+#endif
+#if DF02
+ "df02", df02_dialer, df_disconnect, df_abort,
+#endif
+#if DF03
+ "df03", df03_dialer, df_disconnect, df_abort,
+#endif
+#if DN11
+ "dn11", dn_dialer, dn_disconnect, dn_abort,
+#endif
+#if VENTEL
+ "ventel",ven_dialer, ven_disconnect, ven_abort,
+#endif
+#if HAYES
+ "hayes",hay_dialer, hay_disconnect, hay_abort,
+#endif
+#if COURIER
+ "courier",cour_dialer, cour_disconnect, cour_abort,
+#endif
+#if MULTITECH
+ "multitech",multitech_dialer, multitech_disconnect, multitech_abort,
+#endif
+#if T3000
+ "t3000",t3000_dialer, t3000_disconnect, t3000_abort,
+#endif
+#if V3451
+#if !V831
+ "vadic",v3451_dialer, v3451_disconnect, v3451_abort,
+#endif
+ "v3451",v3451_dialer, v3451_disconnect, v3451_abort,
+#endif
+#if V831
+#if !V3451
+ "vadic",v831_dialer, v831_disconnect, v831_abort,
+#endif
+ "v831",v831_dialer, v831_disconnect, v831_abort,
+#endif
+ 0, 0, 0, 0
+};
+
diff --git a/usr.bin/tip/tip/cmds.c b/usr.bin/tip/tip/cmds.c
new file mode 100644
index 0000000..9ce6bff
--- /dev/null
+++ b/usr.bin/tip/tip/cmds.c
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+#include "pathnames.h"
+
+#include <stdio.h>
+
+/*
+ * tip
+ *
+ * miscellaneous commands
+ */
+
+int quant[] = { 60, 60, 24 };
+
+char null = '\0';
+char *sep[] = { "second", "minute", "hour" };
+static char *argv[10]; /* argument vector for take and put */
+
+void timeout(); /* timeout function called on alarm */
+void stopsnd(); /* SIGINT handler during file transfers */
+void intcopy(); /* interrupt routine for file transfers */
+
+void
+usedefchars ()
+{
+#if HAVE_TERMIOS
+ int cnt;
+ struct termios ttermios;
+ ttermios = ctermios;
+ for (cnt = 0; cnt < NCCS; cnt++)
+ ttermios.c_cc [cnt] = otermios.c_cc [cnt];
+ tcsetattr (0, TCSANOW, &ttermios);
+#else
+ ioctl(0, TIOCSETC, &defchars);
+#endif
+}
+
+void
+usetchars ()
+{
+#if HAVE_TERMIOS
+ tcsetattr (0, TCSANOW, &ctermios);
+#else
+ ioctl(0, TIOCSETC, &tchars);
+#endif
+}
+
+void
+flush_remote ()
+{
+#ifdef TIOCFLUSH
+ int cmd = 0;
+ ioctl (FD, TIOCFLUSH, &cmd);
+#else
+ struct sgttyb buf;
+ ioctl (FD, TIOCGETP, &buf); /* this does a */
+ ioctl (FD, TIOCSETP, &buf); /* wflushtty */
+#endif
+}
+
+/*
+ * FTP - remote ==> local
+ * get a file from the remote host
+ */
+getfl(c)
+ char c;
+{
+ char buf[256], *cp, *expand();
+
+ putchar(c);
+ /*
+ * get the UNIX receiving file's name
+ */
+ if (prompt("Local file name? ", copyname))
+ return;
+ cp = expand(copyname);
+ if ((sfd = creat(cp, 0666)) < 0) {
+ printf("\r\n%s: cannot creat\r\n", copyname);
+ return;
+ }
+
+ /*
+ * collect parameters
+ */
+ if (prompt("List command for remote system? ", buf)) {
+ unlink(copyname);
+ return;
+ }
+ transfer(buf, sfd, value(EOFREAD));
+}
+
+/*
+ * Cu-like take command
+ */
+cu_take(cc)
+ char cc;
+{
+ int fd, argc;
+ char line[BUFSIZ], *expand(), *cp;
+
+ if (prompt("[take] ", copyname))
+ return;
+ if ((argc = args(copyname, argv)) < 1 || argc > 2) {
+ printf("usage: <take> from [to]\r\n");
+ return;
+ }
+ if (argc == 1)
+ argv[1] = argv[0];
+ cp = expand(argv[1]);
+ if ((fd = creat(cp, 0666)) < 0) {
+ printf("\r\n%s: cannot create\r\n", argv[1]);
+ return;
+ }
+ (void)sprintf(line, "cat %s ; echo \"\" ; echo ___tip_end_of_file_marker___", argv[0]);
+ xfer(line, fd, "\n___tip_end_of_file_marker___\n");
+}
+
+extern jmp_buf intbuf;
+
+xfer(buf, fd, eofchars)
+ char *buf, *eofchars;
+{
+ register int ct;
+ char c, *match;
+ register int cnt, eof, v;
+ time_t start;
+ sig_t f;
+ char r;
+ FILE *ff;
+
+ v = boolean(value(VERBOSE));
+
+ if ((ff = fdopen (fd, "w")) == NULL) {
+ perror("file open");
+ return;
+ }
+ if ((cnt = number(value(FRAMESIZE))) != BUFSIZ)
+ if (setvbuf(ff, NULL, _IOFBF, cnt) != 0) {
+ perror("file allocation");
+ (void)fclose(ff);
+ return;
+ }
+
+ pwrite(FD, buf, size(buf));
+ quit = 0;
+ kill(pid, SIGIOT);
+ read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */
+
+ /*
+ * finish command
+ */
+ r = '\r';
+ pwrite(FD, &r, 1);
+ do
+ read(FD, &c, 1);
+ while ((c&0177) != '\n');
+
+ usedefchars ();
+
+ (void) setjmp(intbuf);
+ f = signal(SIGINT, intcopy);
+ start = time(0);
+ match = eofchars;
+ for (ct = 0; !quit;) {
+ eof = read(FD, &c, 1) <= 0;
+ c &= 0177;
+ if (quit)
+ continue;
+ if (eof)
+ break;
+ if (c == 0)
+ continue; /* ignore nulls */
+ if (c == '\r')
+ continue;
+ if (c != *match && match > eofchars) {
+ register char *p = eofchars;
+ while (p < match) {
+ if (*p == '\n'&& v)
+ (void)printf("\r%d", ++ct);
+ fputc(*p++, ff);
+ }
+ match = eofchars;
+ }
+ if (c == *match) {
+ if (*++match == '\0')
+ break;
+ } else {
+ if (c == '\n' && v)
+ (void)printf("\r%d", ++ct);
+ fputc(c, ff);
+ }
+ }
+ if (v)
+ prtime(" lines transferred in ", time(0)-start);
+ usetchars ();
+ write(fildes[1], (char *)&ccc, 1);
+ signal(SIGINT, f);
+ (void)fclose(ff);
+}
+
+static jmp_buf intbuf;
+/*
+ * Bulk transfer routine --
+ * used by getfl(), cu_take(), and pipefile()
+ */
+transfer(buf, fd, eofchars)
+ char *buf, *eofchars;
+{
+ register int ct;
+ char c, buffer[BUFSIZ];
+ register char *p = buffer;
+ register int cnt, eof, v;
+ time_t start;
+ sig_t f;
+ char r;
+ FILE *ff;
+
+ v = boolean(value(VERBOSE));
+
+ if ((ff = fdopen (fd, "w")) == NULL) {
+ perror("file open");
+ return;
+ }
+ if ((cnt = number(value(FRAMESIZE))) != BUFSIZ)
+ if (setvbuf(ff, NULL, _IOFBF, cnt) != 0) {
+ perror("file allocation");
+ (void)fclose(ff);
+ return;
+ }
+
+ pwrite(FD, buf, size(buf));
+ quit = 0;
+ kill(pid, SIGIOT);
+ read(repdes[0], (char *)&ccc, 1); /* Wait until read process stops */
+
+ /*
+ * finish command
+ */
+ r = '\r';
+ pwrite(FD, &r, 1);
+ do
+ read(FD, &c, 1);
+ while ((c&0177) != '\n');
+ usedefchars ();
+ (void) setjmp(intbuf);
+ f = signal(SIGINT, intcopy);
+ start = time(0);
+ for (ct = 0; !quit;) {
+ eof = read(FD, &c, 1) <= 0;
+ c &= 0177;
+ if (quit)
+ continue;
+ if (eof || any(c, eofchars))
+ break;
+ if (c == 0)
+ continue; /* ignore nulls */
+ if (c == '\r')
+ continue;
+ if (c == '\n' && v)
+ printf("\r%d", ++ct);
+ fputc(c, ff);
+ }
+ if (v)
+ prtime(" lines transferred in ", time(0)-start);
+ usetchars ();
+ write(fildes[1], (char *)&ccc, 1);
+ signal(SIGINT, f);
+ (void)fclose(ff);
+}
+
+/*
+ * FTP - remote ==> local process
+ * send remote input to local process via pipe
+ */
+pipefile()
+{
+ int cpid, pdes[2];
+ char buf[256];
+ int status, p;
+ extern int errno;
+
+ if (prompt("Local command? ", buf))
+ return;
+
+ if (pipe(pdes)) {
+ printf("can't establish pipe\r\n");
+ return;
+ }
+
+ if ((cpid = fork()) < 0) {
+ printf("can't fork!\r\n");
+ return;
+ } else if (cpid) {
+ if (prompt("List command for remote system? ", buf)) {
+ close(pdes[0]), close(pdes[1]);
+ kill (cpid, SIGKILL);
+ } else {
+ close(pdes[0]);
+ signal(SIGPIPE, intcopy);
+ transfer(buf, pdes[1], value(EOFREAD));
+ signal(SIGPIPE, SIG_DFL);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ }
+ } else {
+ register int f;
+
+ dup2(pdes[0], 0);
+ close(pdes[0]);
+ for (f = 3; f < 20; f++)
+ close(f);
+ execute(buf);
+ printf("can't execl!\r\n");
+ exit(0);
+ }
+}
+
+/*
+ * Interrupt service routine for FTP
+ */
+void
+stopsnd()
+{
+
+ stop = 1;
+ signal(SIGINT, SIG_IGN);
+}
+
+/*
+ * FTP - local ==> remote
+ * send local file to remote host
+ * terminate transmission with pseudo EOF sequence
+ */
+sendfile(cc)
+ char cc;
+{
+ FILE *fd;
+ char *fnamex;
+ char *expand();
+
+ putchar(cc);
+ /*
+ * get file name
+ */
+ if (prompt("Local file name? ", fname))
+ return;
+
+ /*
+ * look up file
+ */
+ fnamex = expand(fname);
+ if ((fd = fopen(fnamex, "r")) == NULL) {
+ printf("%s: cannot open\r\n", fname);
+ return;
+ }
+ transmit(fd, value(EOFWRITE), NULL);
+ if (!boolean(value(ECHOCHECK))) {
+ flush_remote ();
+ }
+}
+
+/*
+ * Bulk transfer routine to remote host --
+ * used by sendfile() and cu_put()
+ */
+transmit(fd, eofchars, command)
+ FILE *fd;
+ char *eofchars, *command;
+{
+ char *pc, lastc;
+ int c, ccount, lcount;
+ time_t start_t, stop_t;
+ sig_t f;
+
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ stop = 0;
+ f = signal(SIGINT, stopsnd);
+ usedefchars ();
+ read(repdes[0], (char *)&ccc, 1);
+ if (command != NULL) {
+ for (pc = command; *pc; pc++)
+ send(*pc);
+ if (boolean(value(ECHOCHECK)))
+ read(FD, (char *)&c, 1); /* trailing \n */
+ else {
+ flush_remote ();
+ sleep(5); /* wait for remote stty to take effect */
+ }
+ }
+ lcount = 0;
+ lastc = '\0';
+ start_t = time(0);
+ while (1) {
+ ccount = 0;
+ do {
+ c = getc(fd);
+ if (stop)
+ goto out;
+ if (c == EOF)
+ goto out;
+ if (c == 0177 && !boolean(value(RAWFTP)))
+ continue;
+ lastc = c;
+ if (c < 040) {
+ if (c == '\n') {
+ if (!boolean(value(RAWFTP)))
+ c = '\r';
+ }
+ else if (c == '\t') {
+ if (!boolean(value(RAWFTP))) {
+ if (boolean(value(TABEXPAND))) {
+ send(' ');
+ while ((++ccount % 8) != 0)
+ send(' ');
+ continue;
+ }
+ }
+ } else
+ if (!boolean(value(RAWFTP)))
+ continue;
+ }
+ send(c);
+ } while (c != '\r' && !boolean(value(RAWFTP)));
+ if (boolean(value(VERBOSE)))
+ printf("\r%d", ++lcount);
+ if (boolean(value(ECHOCHECK))) {
+ timedout = 0;
+ alarm((int)value(ETIMEOUT));
+ do { /* wait for prompt */
+ read(FD, (char *)&c, 1);
+ if (timedout || stop) {
+ if (timedout)
+ printf("\r\ntimed out at eol\r\n");
+ alarm(0);
+ goto out;
+ }
+ } while ((c&0177) != character(value(PROMPT)));
+ alarm(0);
+ }
+ }
+out:
+ if (lastc != '\n' && !boolean(value(RAWFTP)))
+ send('\r');
+ for (pc = eofchars; *pc; pc++)
+ send(*pc);
+ stop_t = time(0);
+ fclose(fd);
+ signal(SIGINT, f);
+ if (boolean(value(VERBOSE)))
+ if (boolean(value(RAWFTP)))
+ prtime(" chars transferred in ", stop_t-start_t);
+ else
+ prtime(" lines transferred in ", stop_t-start_t);
+ write(fildes[1], (char *)&ccc, 1);
+ usetchars ();
+}
+
+/*
+ * Cu-like put command
+ */
+cu_put(cc)
+ char cc;
+{
+ FILE *fd;
+ char line[BUFSIZ];
+ int argc;
+ char *expand();
+ char *copynamex;
+
+ if (prompt("[put] ", copyname))
+ return;
+ if ((argc = args(copyname, argv)) < 1 || argc > 2) {
+ printf("usage: <put> from [to]\r\n");
+ return;
+ }
+ if (argc == 1)
+ argv[1] = argv[0];
+ copynamex = expand(argv[0]);
+ if ((fd = fopen(copynamex, "r")) == NULL) {
+ printf("%s: cannot open\r\n", copynamex);
+ return;
+ }
+ if (boolean(value(ECHOCHECK)))
+ sprintf(line, "cat>%s\r", argv[1]);
+ else
+ sprintf(line, "stty -echo;cat>%s;stty echo\r", argv[1]);
+ transmit(fd, "\04", line);
+}
+
+/*
+ * FTP - send single character
+ * wait for echo & handle timeout
+ */
+send(c)
+ char c;
+{
+ char cc;
+ int retry = 0;
+
+ cc = c;
+ pwrite(FD, &cc, 1);
+#ifdef notdef
+ if (number(value(CDELAY)) > 0 && c != '\r')
+ nap(number(value(CDELAY)));
+#endif
+ if (!boolean(value(ECHOCHECK))) {
+#ifdef notdef
+ if (number(value(LDELAY)) > 0 && c == '\r')
+ nap(number(value(LDELAY)));
+#endif
+ return;
+ }
+tryagain:
+ timedout = 0;
+ alarm((int)value(ETIMEOUT));
+ read(FD, &cc, 1);
+ alarm(0);
+ if (timedout) {
+ printf("\r\ntimeout error (%s)\r\n", ctrl(c));
+ if (retry++ > 3)
+ return;
+ pwrite(FD, &null, 1); /* poke it */
+ goto tryagain;
+ }
+}
+
+void
+timeout()
+{
+ signal(SIGALRM, timeout);
+ timedout = 1;
+}
+
+/*
+ * Stolen from consh() -- puts a remote file on the output of a local command.
+ * Identical to consh() except for where stdout goes.
+ */
+pipeout(c)
+{
+ char buf[256];
+ int cpid, status, p;
+ time_t start;
+
+ putchar(c);
+ if (prompt("Local command? ", buf))
+ return;
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ usedefchars ();
+ read(repdes[0], (char *)&ccc, 1);
+ /*
+ * Set up file descriptors in the child and
+ * let it go...
+ */
+ if ((cpid = fork()) < 0)
+ printf("can't fork!\r\n");
+ else if (cpid) {
+ start = time(0);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ } else {
+ register int i;
+
+ dup2(FD, 1);
+ for (i = 3; i < 20; i++)
+ close(i);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ execute(buf);
+ printf("can't find `%s'\r\n", buf);
+ exit(0);
+ }
+ if (boolean(value(VERBOSE)))
+ prtime("away for ", time(0)-start);
+ write(fildes[1], (char *)&ccc, 1);
+ usetchars ();
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+}
+
+#if CONNECT
+
+int
+tiplink (char *cmd, unsigned int flags)
+{
+ int cpid, status, p;
+ time_t start;
+
+ if (flags & TL_SIGNAL_TIPOUT) {
+ kill(pid, SIGIOT); /* put TIPOUT into a wait state */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ usedefchars ();
+ read(repdes[0], (char *)&ccc, 1);
+ }
+
+ /*
+ * Set up file descriptors in the child and
+ * let it go...
+ */
+ if ((cpid = fork()) < 0)
+ printf("can't fork!\r\n");
+ else if (cpid) {
+ start = time(0);
+ while ((p = wait(&status)) > 0 && p != cpid)
+ ;
+ } else {
+ register int fd;
+
+ dup2(FD, 0);
+ dup2(3, 1);
+ for (fd = 3; fd < 20; fd++)
+ close (fd);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ execute (cmd);
+ printf("can't find `%s'\r\n", cmd);
+ exit(0);
+ }
+
+ if (flags & TL_VERBOSE && boolean(value(VERBOSE)))
+ prtime("away for ", time(0)-start);
+
+ if (flags & TL_SIGNAL_TIPOUT) {
+ write(fildes[1], (char *)&ccc, 1);
+ usetchars ();
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ }
+
+ return 0;
+}
+
+/*
+ * Fork a program with:
+ * 0 <-> remote tty in
+ * 1 <-> remote tty out
+ * 2 <-> local tty out
+ */
+consh(c)
+{
+ char buf[256];
+ putchar(c);
+ if (prompt("Local command? ", buf))
+ return;
+ tiplink (buf, TL_SIGNAL_TIPOUT | TL_VERBOSE);
+}
+#endif
+
+/*
+ * Escape to local shell
+ */
+shell()
+{
+ int shpid, status;
+ extern char **environ;
+ char *cp;
+
+ printf("[sh]\r\n");
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ unraw();
+ if (shpid = fork()) {
+ while (shpid != wait(&status));
+ raw();
+ printf("\r\n!\r\n");
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ return;
+ } else {
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ if ((cp = rindex(value(SHELL), '/')) == NULL)
+ cp = value(SHELL);
+ else
+ cp++;
+ shell_uid();
+ execl(value(SHELL), cp, 0);
+ printf("\r\ncan't execl!\r\n");
+ exit(1);
+ }
+}
+
+/*
+ * TIPIN portion of scripting
+ * initiate the conversation with TIPOUT
+ */
+setscript()
+{
+ char c;
+ /*
+ * enable TIPOUT side for dialogue
+ */
+ kill(pid, SIGEMT);
+ if (boolean(value(SCRIPT)))
+ write(fildes[1], value(RECORD), size(value(RECORD)));
+ write(fildes[1], "\n", 1);
+ /*
+ * wait for TIPOUT to finish
+ */
+ read(repdes[0], &c, 1);
+ if (c == 'n')
+ printf("can't create %s\r\n", value(RECORD));
+}
+
+/*
+ * Change current working directory of
+ * local portion of tip
+ */
+chdirectory()
+{
+ char dirname[80];
+ register char *cp = dirname;
+
+ if (prompt("[cd] ", dirname)) {
+ if (stoprompt)
+ return;
+ cp = value(HOME);
+ }
+ if (chdir(cp) < 0)
+ printf("%s: bad directory\r\n", cp);
+ printf("!\r\n");
+}
+
+tipabort(msg)
+ char *msg;
+{
+
+ kill(pid, SIGTERM);
+ disconnect(msg);
+ if (msg != NOSTR)
+ printf("\r\n%s", msg);
+ printf("\r\n[EOT]\r\n");
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ unraw();
+ exit(0);
+}
+
+finish()
+{
+ char *abortmsg = NOSTR, *dismsg;
+
+ if (LO != NOSTR && tiplink (LO, TL_SIGNAL_TIPOUT) != 0) {
+ abortmsg = "logout failed";
+ }
+
+ if ((dismsg = value(DISCONNECT)) != NOSTR) {
+ write(FD, dismsg, strlen(dismsg));
+ sleep (2);
+ }
+ tipabort(abortmsg);
+}
+
+void
+intcopy()
+{
+ raw();
+ quit = 1;
+ longjmp(intbuf, 1);
+}
+
+execute(s)
+ char *s;
+{
+ register char *cp;
+
+ if ((cp = rindex(value(SHELL), '/')) == NULL)
+ cp = value(SHELL);
+ else
+ cp++;
+ shell_uid();
+ execl(value(SHELL), cp, "-c", s, 0);
+}
+
+args(buf, a)
+ char *buf, *a[];
+{
+ register char *p = buf, *start;
+ register char **parg = a;
+ register int n = 0;
+
+ do {
+ while (*p && (*p == ' ' || *p == '\t'))
+ p++;
+ start = p;
+ if (*p)
+ *parg = p;
+ while (*p && (*p != ' ' && *p != '\t'))
+ p++;
+ if (p != start)
+ parg++, n++;
+ if (*p)
+ *p++ = '\0';
+ } while (*p);
+
+ return(n);
+}
+
+prtime(s, a)
+ char *s;
+ time_t a;
+{
+ register i;
+ int nums[3];
+
+ for (i = 0; i < 3; i++) {
+ nums[i] = (int)(a % quant[i]);
+ a /= quant[i];
+ }
+ printf("%s", s);
+ while (--i >= 0)
+ if (nums[i] || i == 0 && nums[1] == 0 && nums[2] == 0)
+ printf("%d %s%c ", nums[i], sep[i],
+ nums[i] == 1 ? '\0' : 's');
+ printf("\r\n!\r\n");
+}
+
+variable()
+{
+ char buf[256];
+
+ if (prompt("[set] ", buf))
+ return;
+ vlex(buf);
+ if (vtable[BEAUTIFY].v_access&CHANGED) {
+ vtable[BEAUTIFY].v_access &= ~CHANGED;
+ kill(pid, SIGSYS);
+ }
+ if (vtable[SCRIPT].v_access&CHANGED) {
+ vtable[SCRIPT].v_access &= ~CHANGED;
+ setscript();
+ /*
+ * So that "set record=blah script" doesn't
+ * cause two transactions to occur.
+ */
+ if (vtable[RECORD].v_access&CHANGED)
+ vtable[RECORD].v_access &= ~CHANGED;
+ }
+ if (vtable[RECORD].v_access&CHANGED) {
+ vtable[RECORD].v_access &= ~CHANGED;
+ if (boolean(value(SCRIPT)))
+ setscript();
+ }
+ if (vtable[TAND].v_access&CHANGED) {
+ vtable[TAND].v_access &= ~CHANGED;
+ if (boolean(value(TAND)))
+ tandem("on");
+ else
+ tandem("off");
+ }
+ if (vtable[LECHO].v_access&CHANGED) {
+ vtable[LECHO].v_access &= ~CHANGED;
+ HD = boolean(value(LECHO));
+ }
+ if (vtable[PARITY].v_access&CHANGED) {
+ vtable[PARITY].v_access &= ~CHANGED;
+ setparity();
+ }
+}
+
+/*
+ * Turn tandem mode on or off for remote tty.
+ */
+tandem(option)
+ char *option;
+{
+#if HAVE_TERMIOS
+ struct termios ttermios;
+ tcgetattr (FD, &ttermios);
+ if (strcmp(option,"on") == 0) {
+ ttermios.c_iflag |= IXOFF;
+ ctermios.c_iflag |= IXOFF;
+ }
+ else {
+ ttermios.c_iflag &= ~IXOFF;
+ ctermios.c_iflag &= ~IXOFF;
+ }
+ tcsetattr (FD, TCSANOW, &ttermios);
+ tcsetattr (0, TCSANOW, &ctermios);
+#else /* HAVE_TERMIOS */
+ struct sgttyb rmtty;
+
+ ioctl(FD, TIOCGETP, &rmtty);
+ if (strcmp(option,"on") == 0) {
+ rmtty.sg_flags |= TANDEM;
+ arg.sg_flags |= TANDEM;
+ } else {
+ rmtty.sg_flags &= ~TANDEM;
+ arg.sg_flags &= ~TANDEM;
+ }
+ ioctl(FD, TIOCSETP, &rmtty);
+ ioctl(0, TIOCSETP, &arg);
+#endif /* HAVE_TERMIOS */
+}
+
+/*
+ * Send a break.
+ */
+genbrk()
+{
+
+ ioctl(FD, TIOCSBRK, NULL);
+ sleep(1);
+ ioctl(FD, TIOCCBRK, NULL);
+}
+
+/*
+ * Suspend tip
+ */
+suspend(c)
+ char c;
+{
+
+ unraw();
+ kill(c == CTRL('y') ? getpid() : 0, SIGTSTP);
+ raw();
+}
+
+/*
+ * expand a file name if it includes shell meta characters
+ */
+
+char *
+expand(name)
+ char name[];
+{
+ static char xname[BUFSIZ];
+ char cmdbuf[BUFSIZ];
+ register int pid, l, rc;
+ register char *cp, *Shell;
+ int s, pivec[2], (*sigint)();
+
+ if (!anyof(name, "~{[*?$`'\"\\"))
+ return(name);
+ /* sigint = signal(SIGINT, SIG_IGN); */
+ if (pipe(pivec) < 0) {
+ perror("pipe");
+ /* signal(SIGINT, sigint) */
+ return(name);
+ }
+ sprintf(cmdbuf, "echo %s", name);
+ if ((pid = vfork()) == 0) {
+ Shell = value(SHELL);
+ if (Shell == NOSTR)
+ Shell = _PATH_BSHELL;
+ close(pivec[0]);
+ close(1);
+ dup(pivec[1]);
+ close(pivec[1]);
+ close(2);
+ shell_uid();
+ execl(Shell, Shell, "-c", cmdbuf, 0);
+ _exit(1);
+ }
+ if (pid == -1) {
+ perror("fork");
+ close(pivec[0]);
+ close(pivec[1]);
+ return(NOSTR);
+ }
+ close(pivec[1]);
+ l = read(pivec[0], xname, BUFSIZ);
+ close(pivec[0]);
+ while (wait(&s) != pid);
+ ;
+ s &= 0377;
+ if (s != 0 && s != SIGPIPE) {
+ fprintf(stderr, "\"Echo\" failed\n");
+ return(NOSTR);
+ }
+ if (l < 0) {
+ perror("read");
+ return(NOSTR);
+ }
+ if (l == 0) {
+ fprintf(stderr, "\"%s\": No match\n", name);
+ return(NOSTR);
+ }
+ if (l == BUFSIZ) {
+ fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
+ return(NOSTR);
+ }
+ xname[l] = 0;
+ for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
+ ;
+ *++cp = '\0';
+ return(xname);
+}
+
+/*
+ * Are any of the characters in the two strings the same?
+ */
+
+anyof(s1, s2)
+ register char *s1, *s2;
+{
+ register int c;
+
+ while (c = *s1++)
+ if (any(c, s2))
+ return(1);
+ return(0);
+}
diff --git a/usr.bin/tip/tip/cmdtab.c b/usr.bin/tip/tip/cmdtab.c
new file mode 100644
index 0000000..12ecc07
--- /dev/null
+++ b/usr.bin/tip/tip/cmdtab.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+extern int shell(), getfl(), sendfile(), chdirectory();
+extern int finish(), help(), pipefile(), pipeout(), consh(), variable();
+extern int cu_take(), cu_put(), dollar(), genbrk(), suspend();
+
+esctable_t etable[] = {
+ { '!', NORM, "shell", shell },
+ { '<', NORM, "receive file from remote host", getfl },
+ { '>', NORM, "send file to remote host", sendfile },
+ { 't', NORM, "take file from remote UNIX", cu_take },
+ { 'p', NORM, "put file to remote UNIX", cu_put },
+ { '|', NORM, "pipe remote file", pipefile },
+ { '$', NORM, "pipe local command to remote host", pipeout },
+#if CONNECT
+ { 'C', NORM, "connect program to remote host",consh },
+#endif
+ { 'c', NORM, "change directory", chdirectory },
+ { '.', NORM, "exit from tip", finish },
+ {CTRL('d'),NORM,"exit from tip", finish },
+ {CTRL('y'),NORM,"suspend tip (local+remote)", suspend },
+ {CTRL('z'),NORM,"suspend tip (local only)", suspend },
+ { 's', NORM, "set variable", variable },
+ { '?', NORM, "get this summary", help },
+ { '#', NORM, "send break", genbrk },
+ { 0, 0, 0 }
+};
diff --git a/usr.bin/tip/tip/cu.c b/usr.bin/tip/tip/cu.c
new file mode 100644
index 0000000..bab8492
--- /dev/null
+++ b/usr.bin/tip/tip/cu.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cu.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+void cleanup();
+
+#if INCLUDE_CU_INTERFACE
+
+/*
+ * Botch the interface to look like cu's
+ */
+cumain(argc, argv)
+ char *argv[];
+{
+ register int i;
+ static char sbuf[12];
+
+ if (argc < 2) {
+ printf("usage: cu telno [-t] [-s speed] [-a acu] [-l line] [-#]\n");
+ exit(8);
+ }
+ CU = DV = NOSTR;
+ BR = DEFBR;
+ for (; argc > 1; argv++, argc--) {
+ if (argv[1][0] != '-')
+ PN = argv[1];
+ else switch (argv[1][1]) {
+
+ case 't':
+ HW = 1, DU = -1;
+ --argc;
+ continue;
+
+ case 'a':
+ CU = argv[2]; ++argv; --argc;
+ break;
+
+ case 's':
+ if (argc < 3 || speed(atoi(argv[2])) == 0) {
+ fprintf(stderr, "cu: unsupported speed %s\n",
+ argv[2]);
+ exit(3);
+ }
+ BR = atoi(argv[2]); ++argv; --argc;
+ break;
+
+ case 'l':
+ DV = argv[2]; ++argv; --argc;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (CU)
+ CU[strlen(CU)-1] = argv[1][1];
+ if (DV)
+ DV[strlen(DV)-1] = argv[1][1];
+ break;
+
+ default:
+ printf("Bad flag %s", argv[1]);
+ break;
+ }
+ }
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGHUP, cleanup);
+ signal(SIGTERM, cleanup);
+
+ /*
+ * The "cu" host name is used to define the
+ * attributes of the generic dialer.
+ */
+ (void)sprintf(sbuf, "cu%d", BR);
+ if ((i = hunt(sbuf)) == 0) {
+ printf("all ports busy\n");
+ exit(3);
+ }
+ if (i == -1) {
+ printf("link down\n");
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+ setbuf(stdout, NULL);
+ loginit();
+ user_uid();
+ vinit();
+ setparity("none");
+ boolean(value(VERBOSE)) = 0;
+ if (HW)
+ ttysetup(speed(BR));
+ if (connect()) {
+ printf("Connect failed\n");
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ exit(1);
+ }
+ if (!HW)
+ ttysetup(speed(BR));
+}
+#endif /* INCLUDE_CU_INTERFACE */
diff --git a/usr.bin/tip/tip/dial.sh b/usr.bin/tip/tip/dial.sh
new file mode 100755
index 0000000..ed30da5
--- /dev/null
+++ b/usr.bin/tip/tip/dial.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# @(#)dial.sh -- dialup remote using tip
+#
+
+#set -x
+
+if [ $# -lt 1 ] ; then
+ echo "$0: not enough arguments" 1>&2
+ exit 1
+fi
+
+x=0
+
+while ! tip $* && test $x -lt 3
+do
+ sleep 5
+ x=$(($x+1))
+done
+
+exit 0
diff --git a/usr.bin/tip/tip/hunt.c b/usr.bin/tip/tip/hunt.c
new file mode 100644
index 0000000..97e3a6a
--- /dev/null
+++ b/usr.bin/tip/tip/hunt.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hunt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+extern char *getremote();
+extern char *rindex();
+
+static jmp_buf deadline;
+static int deadfl;
+
+void
+dead()
+{
+ deadfl = 1;
+ longjmp(deadline, 1);
+}
+
+hunt(name)
+ char *name;
+{
+ register char *cp;
+ sig_t f;
+
+ f = signal(SIGALRM, dead);
+ while (cp = getremote(name)) {
+ deadfl = 0;
+ uucplock = rindex(cp, '/')+1;
+ if (uu_lock(uucplock) < 0)
+ continue;
+ /*
+ * Straight through call units, such as the BIZCOMP,
+ * VADIC and the DF, must indicate they're hardwired in
+ * order to get an open file descriptor placed in FD.
+ * Otherwise, as for a DN-11, the open will have to
+ * be done in the "open" routine.
+ */
+ if (!HW)
+ break;
+ if (setjmp(deadline) == 0) {
+ alarm(10);
+ FD = open(cp, O_RDWR);
+ }
+ alarm(0);
+ if (FD < 0) {
+ perror(cp);
+ deadfl = 1;
+ }
+ if (!deadfl) {
+ ioctl(FD, TIOCEXCL, 0);
+#if HAVE_TERMIOS
+ {
+ struct termios t;
+ if (tcgetattr(FD, &t) == 0) {
+ t.c_cflag |= HUPCL;
+ (void)tcsetattr(FD, TCSANOW, &t);
+ }
+ }
+#else /* HAVE_TERMIOS */
+#ifdef TIOCHPCL
+ ioctl(FD, TIOCHPCL, 0);
+#endif
+#endif /* HAVE_TERMIOS */
+ signal(SIGALRM, SIG_DFL);
+ return ((int)cp);
+ }
+ (void)uu_unlock(uucplock);
+ }
+ signal(SIGALRM, f);
+ return (deadfl ? -1 : (int)cp);
+}
diff --git a/usr.bin/tip/tip/log.c b/usr.bin/tip/tip/log.c
new file mode 100644
index 0000000..16e918a
--- /dev/null
+++ b/usr.bin/tip/tip/log.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)log.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+
+#if ACULOG
+static FILE *flog = NULL;
+
+/*
+ * Log file maintenance routines
+ */
+
+logent(group, num, acu, message)
+ char *group, *num, *acu, *message;
+{
+ char *user, *timestamp;
+ struct passwd *pwd;
+ long t;
+
+ if (flog == NULL)
+ return;
+ if (flock(fileno(flog), LOCK_EX) < 0) {
+ perror("tip: flock");
+ return;
+ }
+ if ((user = getlogin()) == NOSTR)
+ if ((pwd = getpwuid(getuid())) == NOPWD)
+ user = "???";
+ else
+ user = pwd->pw_name;
+ t = time(0);
+ timestamp = ctime(&t);
+ timestamp[24] = '\0';
+ fprintf(flog, "%s (%s) <%s, %s, %s> %s\n",
+ user, timestamp, group,
+#if PRISTINE
+ "",
+#else
+ num,
+#endif
+ acu, message);
+ (void) fflush(flog);
+ (void) flock(fileno(flog), LOCK_UN);
+}
+
+loginit()
+{
+ flog = fopen(value(LOG), "a");
+ if (flog == NULL)
+ fprintf(stderr, "can't open log file %s.\r\n", value(LOG));
+}
+#endif
diff --git a/usr.bin/tip/tip/modems.5 b/usr.bin/tip/tip/modems.5
new file mode 100644
index 0000000..f6abba3
--- /dev/null
+++ b/usr.bin/tip/tip/modems.5
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)modems.5 3/24/95
+.\"
+.Dd March 24, 1995
+.Dt MODEMS 5
+.Os BSD 4.4
+.Sh NAME
+.Nm modems
+.Nd modem configuration data base
+.Sh DESCRIPTION
+The modems known by
+.Xr tip 1
+and their attributes are stored in an
+.Tn ASCII
+file which
+is structured somewhat like the
+.Xr termcap 5
+file. Each line in the file provides a description for a single
+.Xr modem .
+Fields are separated by a colon (``:'').
+Lines ending in a \e character with an immediately following newline are
+continued on the next line.
+.Pp
+The first entry is the name(s) of the modem. If there is more
+than one name for a system, the names are separated by vertical bars.
+After the name of the system comes the fields of the description. A
+field name followed by an `=' sign indicates a string value follows. A field
+name followed by a `#' sign indicates a following numeric value.
+.Pp
+When
+.Xr tip
+is invoked, an entry for a remote system is looked up in the
+.Pa /etc/remote database.
+If the entry includes an "ACU" type capability (abbreviated at),
+.Xr tip
+looks up the specified modem in
+.Pa /etc/modems.
+If a modem entry is found,
+the corresponding capabilities determine how
+.Xr tip
+programs the modem when connecting to and disconnecting from the
+remote system.
+.Sh CAPABILITIES
+Capabilities are either strings (str), numbers (num), or boolean
+flags (bool). A string capability is specified by
+.Em capability Ns Ar = Ns Em value ;
+for example, ``reset_command=ATZ\\r''. A numeric capability is specified by
+.Em capability Ns Ar # Ns Em value ;
+for example, ``intercharacter_delay#50''. A boolean capability is specified
+by simply listing the capability.
+.Bl -tag -width intercharacter_delay indent
+.It Cm \&dial_command
+(str)
+AT command used to dial remote system (typically, "ATDT")
+.It Cm \&echo_off_command
+(str)
+AT command to turn off command echo.
+.It Cm \&escape_guard_time
+(num)
+The delay, expressed in milliseconds, used to frame return-to-command
+escape sequences.
+.It Cm \&escape_sequence
+(str)
+The return-to-command escape sequence.
+.It Cm \&hangup_command
+(str)
+AT command used to hangup modem.
+.It Cm \&hw_flow_control
+(bool)
+Enable hardware (RTS/CTS) flow control between computer and modem (DTE/DCE).
+.It Cm \&init_string
+(str)
+AT command used to initialize modem before dialing.
+.It Cm \&intercharacter_delay
+(num)
+Delay value, expressed in milliseconds, between characters when sending commands
+to the modem.
+.It Cm \&intercommand_delay
+(num)
+Minimum delay value, expressed in milliseconds, to impose between commands
+issued to the modem.
+.It Cm \&lock_baud
+(bool)
+Use a fixed bit rate between the computer and the modem (DTE / DCE). The
+bit rate is specified in
+.Pa /etc/remote.
+.It Cm \&reset_command
+(str)
+AT command to reset the modem.
+.It Cm \&reset_delay
+(num)
+The time, expressed in milliseconds, required by the modem to complete
+a reset and return to a ready condition.
+.Sh FILES
+.Bl -tag -width /etc/modems -compact
+.It Pa /etc/modems
+The
+.Nm modems
+configuration database file
+resides in
+.Pa /etc .
+.El
+.Sh SEE ALSO
+.Xr tip 1 ,
+.Xr remote 5
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.4 .
diff --git a/usr.bin/tip/tip/partab.c b/usr.bin/tip/tip/partab.c
new file mode 100644
index 0000000..1da4e23
--- /dev/null
+++ b/usr.bin/tip/tip/partab.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)partab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Even parity table for 0-0177
+ */
+char evenpartab[] = {
+ 0000,0201,0202,0003,0204,0005,0006,0207,
+ 0210,0011,0012,0213,0014,0215,0216,0017,
+ 0220,0021,0022,0223,0024,0225,0226,0027,
+ 0030,0231,0232,0033,0234,0035,0036,0237,
+ 0240,0041,0042,0243,0044,0245,0246,0047,
+ 0050,0251,0252,0053,0254,0055,0056,0257,
+ 0060,0261,0262,0063,0264,0065,0066,0267,
+ 0270,0071,0072,0273,0074,0275,0276,0077,
+ 0300,0101,0102,0303,0104,0305,0306,0107,
+ 0110,0311,0312,0113,0314,0115,0116,0317,
+ 0120,0321,0322,0123,0324,0125,0126,0327,
+ 0330,0131,0132,0333,0134,0335,0336,0137,
+ 0140,0341,0342,0143,0344,0145,0146,0347,
+ 0350,0151,0152,0353,0154,0355,0356,0157,
+ 0360,0161,0162,0363,0164,0365,0366,0167,
+ 0170,0371,0372,0173,0374,0175,0176,0377,
+};
diff --git a/usr.bin/tip/tip/pathnames.h b/usr.bin/tip/tip/pathnames.h
new file mode 100644
index 0000000..0f9a15b
--- /dev/null
+++ b/usr.bin/tip/tip/pathnames.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+/*
+ Specify path to ACU (modem) log
+*/
+#define _PATH_ACULOG "/var/log/aculog"
+
+/*
+ Specify path and format of lock files
+*/
+/* #define _PATH_LOCKDIRNAME "/usr/spool/uucp/LCK..%s" */
+/* #define _PATH_LOCKDIRNAME "/etc/locks/LCK..%s" */
+/* #define _PATH_LOCKDIRNAME "/usr/spool/locks/LCK..%s" */
+/* #define _PATH_LOCKDIRNAME "/usr/spool/uucp/LCK/LCK..%s" */
+#define _PATH_LOCKDIRNAME _PATH_UUCPLOCK "LCK..%s"
+
+/*
+ Specify location for system wide databases
+*/
+#define _PATH_MODEMS "/etc/modems"
+#define _PATH_PHONES "/etc/phones"
+#define _PATH_REMOTE "/etc/remote"
+
diff --git a/usr.bin/tip/tip/remcap.c b/usr.bin/tip/tip/remcap.c
new file mode 100644
index 0000000..7a7e0c2
--- /dev/null
+++ b/usr.bin/tip/tip/remcap.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)remcap.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent rgetent
+#define tnchktc rnchktc
+#define tnamatch rnamatch
+#define tgetnum rgetnum
+#define tgetflag rgetflag
+#define tgetstr rgetstr
+#define E_TERMCAP RM = _PATH_REMOTE
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+
+char *RM;
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+static char *tskip();
+char *tgetstr();
+static char *tdecode();
+static char *remotefile;
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+tgetent(bp, name)
+ char *bp, *name;
+{
+ char lbuf[BUFSIZ], *cp, *p;
+ int rc1, rc2;
+
+ remotefile = cp = getenv(V_TERMCAP);
+ if (cp == (char *)0 || strcmp(cp, _PATH_REMOTE) == 0) {
+ remotefile = cp = _PATH_REMOTE;
+ return (getent(bp, name, cp));
+ } else {
+ if ((rc1 = getent(bp, name, cp)) != 1)
+ *bp = '\0';
+ remotefile = cp = _PATH_REMOTE;
+ rc2 = getent(lbuf, name, cp);
+ if (rc1 != 1 && rc2 != 1)
+ return (rc2);
+ if (rc2 == 1) {
+ p = lbuf;
+ if (rc1 == 1)
+ while (*p++ != ':')
+ ;
+ if (strlen(bp) + strlen(p) > BUFSIZ) {
+ write(2, "Remcap entry too long\n", 23);
+ return (-1);
+ }
+ strcat(bp, p);
+ }
+ tbuf = bp;
+ return (1);
+ }
+}
+
+getent(bp, name, cp)
+ char *bp, *name, *cp;
+{
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[BUFSIZ], *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ if (*cp!='/') {
+ cp2 = getenv(V_TERM);
+ if (cp2 == (char *)0 || strcmp(name,cp2) == 0) {
+ strcpy(bp,cp);
+ return (tnchktc());
+ } else
+ tf = open(E_TERMCAP, O_RDONLY);
+ } else
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf == 0)
+ tf = open(E_TERMCAP, O_RDONLY);
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ write(2,"Remcap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+tnchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+ char *cp;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strcpy(tcname, p+3);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ if (strcmp(remotefile, _PATH_REMOTE) == 0)
+ return (0);
+ else if (getent(tcbuf, tcname, _PATH_REMOTE) != 1)
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(2, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strcpy(p, q);
+ tbuf = holdtbuf;
+ return (1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+tnamatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return (0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ register char *bp;
+{
+
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+tgetnum(id)
+ char *id;
+{
+ register int i, base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+tgetflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.bin/tip/tip/remote.c b/usr.bin/tip/tip/remote.c
new file mode 100644
index 0000000..1c6e2d4
--- /dev/null
+++ b/usr.bin/tip/tip/remote.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)remote.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/syslimits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tipconf.h"
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * Attributes to be gleened from remote host description
+ * data base.
+ */
+static char **caps[] = {
+ &AT, &DV, &CM, &CU, &EL, &IE, &OE, &PN, &PR, &DI,
+ &ES, &EX, &FO, &RC, &RE, &PA, &LI, &LO
+};
+
+static char *capstrings[] = {
+ "at", "dv", "cm", "cu", "el", "ie", "oe", "pn", "pr",
+ "di", "es", "ex", "fo", "rc", "re", "pa", "li", "lo", 0
+};
+
+static char *db_array[3] = { _PATH_REMOTE, 0, 0 };
+
+#define cgetflag(f) (cgetcap(bp, f, ':') != NULL)
+
+/*
+ Expand the start tilde sequence at the start of the
+ specified path. Optionally, free space allocated to
+ path before reinitializing it.
+*/
+static int
+expand_tilde (char **path, void (*free) (char *p))
+{
+ int rc = 0;
+ char buffer [PATH_MAX];
+ char *tailp;
+ if ((tailp = strchr (*path + 1, '/')) != NULL)
+ {
+ struct passwd *pwd;
+ *tailp++ = '\0';
+ if (*(*path + 1) == '\0')
+ strcpy (buffer, getlogin ());
+ else
+ strcpy (buffer, *path + 1);
+ if ((pwd = getpwnam (buffer)) != NULL)
+ {
+ strcpy (buffer, pwd->pw_dir);
+ strcat (buffer, "/");
+ strcat (buffer, tailp);
+ if (free)
+ free (*path);
+ *path = strdup (buffer);
+ rc++;
+ }
+ return rc;
+ }
+}
+
+static
+getremcap(host)
+ register char *host;
+{
+ register char **p, ***q;
+ char *bp;
+ char *rempath;
+ int stat;
+
+ rempath = getenv("REMOTE");
+ if (rempath != NULL)
+ if (*rempath != '/')
+ /* we have an entry */
+ cgetset(rempath);
+ else { /* we have a path */
+ db_array[1] = rempath;
+ db_array[2] = _PATH_REMOTE;
+ }
+
+ if ((stat = cgetent(&bp, db_array, host)) < 0) {
+ if (DV ||
+ host[0] == '/' && access(DV = host, R_OK | W_OK) == 0) {
+ CU = DV;
+ HO = host;
+ HW = 1;
+ DU = 0;
+ if (!BR)
+ BR = DEFBR;
+ FS = DEFFS;
+ return;
+ }
+ switch(stat) {
+ case -1:
+ fprintf(stderr, "tip: unknown host %s\n", host);
+ break;
+ case -2:
+ fprintf(stderr,
+ "tip: can't open host description file\n");
+ break;
+ case -3:
+ fprintf(stderr,
+ "tip: possible reference loop in host description file\n");
+ break;
+ }
+ exit(3);
+ }
+
+ for (p = capstrings, q = caps; *p != NULL; p++, q++)
+ if (**q == NULL)
+ cgetstr(bp, *p, *q);
+ if (!BR && (cgetnum(bp, "br", &BR) == -1))
+ BR = DEFBR;
+ if (cgetnum(bp, "fs", &FS) == -1)
+ FS = DEFFS;
+ if (DU < 0)
+ DU = 0;
+ else
+ DU = cgetflag("du");
+ if (DV == NOSTR) {
+ fprintf(stderr, "%s: missing device spec\n", host);
+ exit(3);
+ }
+ if (DU && CU == NOSTR)
+ CU = DV;
+ if (DU && PN == NOSTR) {
+ fprintf(stderr, "%s: missing phone number\n", host);
+ exit(3);
+ }
+
+ HD = cgetflag("hd");
+
+ /*
+ * This effectively eliminates the "hw" attribute
+ * from the description file
+ */
+ if (!HW)
+ HW = (CU == NOSTR) || (DU && equal(DV, CU));
+ HO = host;
+
+ /*
+ If login script, verify access
+ */
+ if (LI != NOSTR) {
+ if (*LI == '~')
+ (void) expand_tilde (&LI, NULL);
+ if (access (LI, F_OK | X_OK) != 0) {
+ printf("tip (warning): can't open login script \"%s\"\n", LI);
+ LI = NOSTR;
+ }
+ }
+
+ /*
+ If logout script, verify access
+ */
+ if (LO != NOSTR) {
+ if (*LO == '~')
+ (void) expand_tilde (&LO, NULL);
+ if (access (LO, F_OK | X_OK) != 0) {
+ printf("tip (warning): can't open logout script \"%s\"\n", LO);
+ LO = NOSTR;
+ }
+ }
+
+ /*
+ * see if uppercase mode should be turned on initially
+ */
+ if (cgetflag("ra"))
+ boolean(value(RAISE)) = 1;
+ if (cgetflag("ec"))
+ boolean(value(ECHOCHECK)) = 1;
+ if (cgetflag("be"))
+ boolean(value(BEAUTIFY)) = 1;
+ if (cgetflag("nb"))
+ boolean(value(BEAUTIFY)) = 0;
+ if (cgetflag("sc"))
+ boolean(value(SCRIPT)) = 1;
+ if (cgetflag("tb"))
+ boolean(value(TABEXPAND)) = 1;
+ if (cgetflag("vb"))
+ boolean(value(VERBOSE)) = 1;
+ if (cgetflag("nv"))
+ boolean(value(VERBOSE)) = 0;
+ if (cgetflag("ta"))
+ boolean(value(TAND)) = 1;
+ if (cgetflag("nt"))
+ boolean(value(TAND)) = 0;
+ if (cgetflag("rw"))
+ boolean(value(RAWFTP)) = 1;
+ if (cgetflag("hd"))
+ boolean(value(HALFDUPLEX)) = 1;
+ if (RE == NOSTR)
+ RE = (char *)"tip.record";
+ if (EX == NOSTR)
+ EX = (char *)"\t\n\b\f";
+ if (ES != NOSTR)
+ vstring("es", ES);
+ if (FO != NOSTR)
+ vstring("fo", FO);
+ if (PR != NOSTR)
+ vstring("pr", PR);
+ if (RC != NOSTR)
+ vstring("rc", RC);
+ if (cgetnum(bp, "dl", &DL) == -1)
+ DL = 0;
+ if (cgetnum(bp, "cl", &CL) == -1)
+ CL = 0;
+ if (cgetnum(bp, "et", &ET) == -1)
+ ET = 10;
+}
+
+char *
+getremote(host)
+ char *host;
+{
+ register char *cp;
+ static char *next;
+ static int lookedup = 0;
+
+ if (!lookedup) {
+ if (host == NOSTR && (host = getenv("HOST")) == NOSTR) {
+ fprintf(stderr, "tip: no host specified\n");
+ exit(3);
+ }
+ getremcap(host);
+ next = DV;
+ lookedup++;
+ }
+ /*
+ * We return a new device each time we're called (to allow
+ * a rotary action to be simulated)
+ */
+ if (next == NOSTR)
+ return (NOSTR);
+ if ((cp = index(next, ',')) == NULL) {
+ DV = next;
+ next = NOSTR;
+ } else {
+ *cp++ = '\0';
+ DV = next;
+ next = cp;
+ }
+ return (DV);
+}
diff --git a/usr.bin/tip/tip/tip.1 b/usr.bin/tip/tip/tip.1
new file mode 100644
index 0000000..caae520
--- /dev/null
+++ b/usr.bin/tip/tip/tip.1
@@ -0,0 +1,461 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tip.1 8.4 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt TIP 1
+.Os BSD 4
+.Sh NAME
+.Nm tip ,
+.Nm cu
+.Nd connect to a remote system
+.Sh SYNOPSIS
+.Nm tip
+.Op Fl v
+.Fl Ns Ns Ar speed
+.Ar system\-name
+.Nm tip
+.Op Fl v
+.Fl Ns Ns Ar speed
+.Ar phone\-number
+.Nm cu
+.Ar phone\-number
+.Op Fl t
+.Op Fl s Ar speed
+.Op Fl a Ar acu
+.Op Fl l Ar line
+.Op Fl #
+.Sh DESCRIPTION
+.Nm Tip
+and
+.Ar cu
+establish a full-duplex connection to another machine,
+giving the appearance of being logged in directly on the
+remote cpu. It goes without saying that you must have a login
+on the machine (or equivalent) to which you wish to connect.
+The preferred interface is
+.Nm tip .
+The
+.Ar cu
+interface is included for those people attached to the
+``call
+.Ux Ns ''
+command of version 7. This manual page
+describes only
+.Nm tip .
+.Pp
+Available Option:
+.Bl -tag -width indent
+.It Fl v
+Set verbose mode.
+.El
+.Pp
+Typed characters are normally transmitted directly to the remote
+machine (which does the echoing as well). A tilde (`~') appearing
+as the first character of a line is an escape signal; the following
+are recognized:
+.Bl -tag -width flag
+.It Ic \&~^D No or Ic \&~ .
+Drop the connection and exit
+(you may still be logged in on the
+remote machine).
+.It Ic \&~c Op Ar name
+Change directory to
+.Ar name
+(no argument
+implies change to your home directory).
+.It Ic \&~!
+Escape to a shell (exiting the shell will
+return you to tip).
+.It Ic \&~>
+Copy file from local to remote.
+.Nm Tip
+prompts for the name of a local file to transmit.
+.It Ic \&~<
+Copy file from remote to local.
+.Nm Tip
+prompts first for the name of the file to be sent, then for
+a command to be executed on the remote machine.
+.It Ic \&~p Ar from Op Ar to
+Send a file to a remote
+.Ux
+host. The put command causes the remote
+.Ux
+system to run the command string ``cat > 'to''', while
+.Nm tip
+sends it the ``from''
+file. If the ``to'' file isn't specified the ``from'' file name is used.
+This command is actually a
+.Ux
+specific version of the ``~>'' command.
+.It Ic \&~t Ar from Op Ar to
+Take a file from a remote
+.Ux
+host.
+As in the put command the ``to'' file
+defaults to the ``from'' file name if it isn't specified.
+The remote host
+executes the command string ``cat 'from';echo ^A'' to send the file to
+.Nm tip .
+.It Ic \&~|
+Pipe the output from a remote command to a local
+.Ux
+process.
+The command string sent to the local
+.Ux
+system is processed by the shell.
+.It Ic \&~$
+Pipe the output from a local
+.Ux
+process to the remote host.
+The command string sent to the local
+.Ux
+system is processed by the shell.
+.It Ic \&~C
+Fork a child process on the local system to perform special protocols
+such as \s-1XMODEM\s+1. The child program will be run with the following
+somewhat unusual arrangement of file descriptors:
+.nf
+.in +1i
+0 <-> local tty in
+1 <-> local tty out
+2 <-> local tty out
+3 <-> remote tty in
+4 <-> remote tty out
+.in -1i
+.fi
+.It Ic \&~#
+Send a
+.Dv BREAK
+to the remote system.
+For systems which don't support the
+necessary
+.Ar ioctl
+call the break is simulated by a sequence of line speed changes
+and
+.Dv DEL
+characters.
+.It Ic \&~s
+Set a variable (see the discussion below).
+.It Ic \&~^Z
+Stop
+.Nm tip
+(only available with job control).
+.It Ic \&~^Y
+Stop only the ``local side'' of
+.Nm tip
+(only available with job control);
+the ``remote side'' of
+.Nm tip ,
+the side that displays output from the remote host, is left running.
+.It Ic \&~?
+Get a summary of the tilde escapes
+.El
+.Pp
+.Nm Tip
+uses the file
+.Pa /etc/remote
+to find how to reach a particular
+system and to find out how it should operate while talking
+to the system;
+refer to
+.Xr remote 5
+for a full description.
+Each system has a default baud rate with which to
+establish a connection. If this value is not suitable, the baud rate
+to be used may be specified on the command line, e.g.
+.Ql "tip -300 mds" .
+.Pp
+When
+.Nm tip
+establishes a connection it sends out a
+connection message to the remote system; the default value, if any,
+is defined in
+.Pa /etc/remote
+(see
+.Xr remote 5 ) .
+.Pp
+When
+.Nm tip
+prompts for an argument (e.g. during setup of
+a file transfer) the line typed may be edited with the standard
+erase and kill characters. A null line in response to a prompt,
+or an interrupt, will abort the dialogue and return you to the
+remote machine.
+.Pp
+.Nm Tip
+guards against multiple users connecting to a remote system
+by opening modems and terminal lines with exclusive access,
+and by honoring the locking protocol used by
+.Xr uucico 8 .
+.Pp
+During file transfers
+.Nm tip
+provides a running count of the number of lines transferred.
+When using the ~> and ~< commands, the ``eofread'' and ``eofwrite''
+variables are used to recognize end-of-file when reading, and
+specify end-of-file when writing (see below). File transfers
+normally depend on tandem mode for flow control. If the remote
+system does not support tandem mode, ``echocheck'' may be set
+to indicate
+.Nm tip
+should synchronize with the remote system on the echo of each
+transmitted character.
+.Pp
+When
+.Nm tip
+must dial a phone number to connect to a system it will print
+various messages indicating its actions.
+.Nm Tip
+supports modems that use the AT command set.
+.Nm Tip
+uses the file
+.Pa /etc/modems
+to find out how to operate with a particular
+modem; refer to
+.Xr modems 5
+for a full description.
+.Ss VARIABLES
+.Nm Tip
+maintains a set of
+.Ar variables
+which control its operation.
+Some of these variables are read-only to normal users (root is allowed
+to change anything of interest). Variables may be displayed
+and set through the ``s'' escape. The syntax for variables is patterned
+after
+.Xr vi 1
+and
+.Xr Mail 1 .
+Supplying ``all''
+as an argument to the set command displays all variables readable by
+the user. Alternatively, the user may request display of a particular
+variable by attaching a `?' to the end. For example ``escape?''
+displays the current escape character.
+.Pp
+Variables are numeric, string, character, or boolean values. Boolean
+variables are set merely by specifying their name; they may be reset
+by prepending a `!' to the name. Other variable types are set by
+concatenating an `=' and the value. The entire assignment must not
+have any blanks in it. A single set command may be used to interrogate
+as well as set a number of variables.
+Variables may be initialized at run time by placing set commands
+(without the ``~s'' prefix in a file
+.Pa .tiprc
+in one's home directory). The
+.Fl v
+option causes
+.Nm tip
+to display the sets as they are made.
+Certain common variables have abbreviations.
+The following is a list of common variables,
+their abbreviations, and their default values.
+.Bl -tag -width Ar
+.It Ar beautify
+(bool) Discard unprintable characters when a session is being scripted;
+abbreviated
+.Ar be .
+.It Ar baudrate
+(num) The baud rate at which the connection was established;
+abbreviated
+.Ar ba .
+.It Ar dialtimeout
+(num) When dialing a phone number, the time (in seconds)
+to wait for a connection to be established; abbreviated
+.Ar dial .
+.It Ar echocheck
+(bool) Synchronize with the remote host during file transfer by
+waiting for the echo of the last character transmitted; default is
+.Ar off .
+.It Ar eofread
+(str) The set of characters which signify an end-of-transmission
+during a ~< file transfer command; abbreviated
+.Ar eofr .
+.It Ar eofwrite
+(str) The string sent to indicate end-of-transmission during
+a ~> file transfer command; abbreviated
+.Ar eofw .
+.It Ar eol
+(str) The set of characters which indicate an end-of-line.
+.Nm Tip
+will recognize escape characters only after an end-of-line.
+.It Ar escape
+(char) The command prefix (escape) character; abbreviated
+.Ar es ;
+default value is `~'.
+.It Ar exceptions
+(str) The set of characters which should not be discarded
+due to the beautification switch; abbreviated
+.Ar ex ;
+default value is ``\et\en\ef\eb''.
+.It Ar force
+(char) The character used to force literal data transmission;
+abbreviated
+.Ar fo ;
+default value is `^P'.
+.It Ar framesize
+(num) The amount of data (in bytes) to buffer between file system
+writes when receiving files; abbreviated
+.Ar fr .
+.It Ar host
+(str) The name of the host to which you are connected; abbreviated
+.Ar ho .
+.It Ar login
+(str) Pathname of a login shell script to run once connected; standard input
+and output are redirected to the remote host. Leading tildes in the pathname
+are expanded expansion; abbreviated
+.Ar li .
+.It Ar logout
+(str) Pathname of a shell script to run before disconnecting; standard input
+and output are redirected to the remote host. Leading tildes in the pathname
+are expanded expansion; abbreviated
+.Ar lo .
+.It Ar prompt
+(char) The character which indicates an end-of-line on the remote
+host; abbreviated
+.Ar pr ;
+default value is `\en'. This value is used to synchronize during
+data transfers. The count of lines transferred during a file transfer
+command is based on receipt of this character.
+.It Ar raise
+(bool) Upper case mapping mode; abbreviated
+.Ar ra ;
+default value is
+.Ar off .
+When this mode is enabled, all lower case letters will be mapped to
+upper case by
+.Nm tip
+for transmission to the remote machine.
+.It Ar raisechar
+(char) The input character used to toggle upper case mapping mode;
+abbreviated
+.Ar rc ;
+default value is `^A'.
+.It Ar record
+(str) The name of the file in which a session script is recorded;
+abbreviated
+.Ar rec ;
+default value is ``tip.record''.
+.It Ar script
+(bool) Session scripting mode; abbreviated
+.Ar sc ;
+default is
+.Ar off .
+When
+.Ar script
+is
+.Li true ,
+.Nm tip
+will record everything transmitted by the remote machine in
+the script record file specified in
+.Ar record .
+If the
+.Ar beautify
+switch is on, only printable
+.Tn ASCII
+characters will be included in
+the script file (those characters betwee 040 and 0177). The
+variable
+.Ar exceptions
+is used to indicate characters which are an exception to the normal
+beautification rules.
+.It Ar tabexpand
+(bool) Expand tabs to spaces during file transfers; abbreviated
+.Ar tab ;
+default value is
+.Ar false .
+Each tab is expanded to 8 spaces.
+.It Ar verbose
+(bool) Verbose mode; abbreviated
+.Ar verb ;
+default is
+.Ar true .
+When verbose mode is enabled,
+.Nm tip
+prints messages while dialing, shows the current number
+of lines transferred during a file transfer operations,
+and more.
+.El
+.Sh ENVIRONMENT
+.Nm Tip
+uses the following environment variables:
+.Bl -tag -width Fl
+.It Ev SHELL
+(str) The name of the shell to use for the ~! command; default
+value is ``/bin/sh'', or taken from the environment.
+.It Ev HOME
+(str) The home directory to use for the ~c command; default
+value is taken from the environment.
+.It Ev HOST
+Check for a default host if none specified.
+.El
+.Pp
+The variables
+.Ev ${REMOTE}
+and
+.Ev ${PHONES}
+are also exported.
+.Sh FILES
+.Bl -tag -width /var/spool/lock/LCK..* -compact
+.It Pa /etc/modems
+Global modem configuration data base.
+.It Pa /etc/remote
+Global system descriptions.
+.It Pa /etc/phones
+Global phone number data base.
+.It ${REMOTE}
+Private system descriptions.
+.It ${PHONES}
+Private phone numbers.
+.It ~/.tiprc
+Initialization file.
+.It Pa tip.record
+Record file.
+.It /var/log/aculog
+Line access log.
+.It Pa /var/spool/lock/LCK..*
+Lock file to avoid conflicts with
+.Xr uucp .
+.El
+.Sh DIAGNOSTICS
+Diagnostics are, hopefully, self explanatory.
+.Sh SEE ALSO
+.Xr remote 5 ,
+.Xr phones 5
+.Sh HISTORY
+The
+.Nm tip
+appeared command in
+.Bx 4.2 .
+.Sh BUGS
+The full set of variables is undocumented and should, probably, be
+pared down.
diff --git a/usr.bin/tip/tip/tip.c b/usr.bin/tip/tip/tip.c
new file mode 100644
index 0000000..3482d00
--- /dev/null
+++ b/usr.bin/tip/tip/tip.c
@@ -0,0 +1,677 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tip.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ Forward declarations
+*/
+void ttysetup (int speed);
+
+/*
+ * tip - UNIX link to other systems
+ * tip [-v] [-speed] system-name
+ * or
+ * cu phone-number [-s speed] [-l line] [-a acu]
+ */
+
+#include "tipconf.h"
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * Baud rate mapping table
+ */
+#if !HAVE_TERMIOS
+CONST int bauds[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600,
+ 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, -1
+};
+#endif
+
+#if !HAVE_TERMIOS
+int disc = OTTYDISC; /* tip normally runs this way */
+#endif
+
+void intprompt();
+void timeout();
+void cleanup();
+void tipdone();
+char *sname();
+char PNbuf[256]; /* This limits the size of a number */
+
+void
+main(argc, argv)
+ char *argv[];
+{
+ char *system = NOSTR;
+ register int i;
+ register char *p;
+ char sbuf[12];
+
+ gid = getgid();
+ egid = getegid();
+ uid = getuid();
+ euid = geteuid();
+
+#if INCLUDE_CU_INTERFACE
+ if (equal(sname(argv[0]), "cu")) {
+ cumode = 1;
+ cumain(argc, argv);
+ goto cucommon;
+ }
+#endif /* INCLUDE_CU_INTERFACE */
+
+ if (argc > 4) {
+ fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
+ exit(1);
+ }
+ if (!isatty(0)) {
+ fprintf(stderr, "tip: must be interactive\n");
+ exit(1);
+ }
+
+ for (; argc > 1; argv++, argc--) {
+ if (argv[1][0] != '-')
+ system = argv[1];
+ else switch (argv[1][1]) {
+
+ case 'v':
+ vflag++;
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ BR = atoi(&argv[1][1]);
+ break;
+
+ default:
+ fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
+ break;
+ }
+ }
+
+ if (system == NOSTR)
+ goto notnumber;
+ if (isalpha(*system))
+ goto notnumber;
+ /*
+ * System name is really a phone number...
+ * Copy the number then stomp on the original (in case the number
+ * is private, we don't want 'ps' or 'w' to find it).
+ */
+ if (strlen(system) > sizeof PNbuf - 1) {
+ fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
+ sizeof PNbuf - 1);
+ exit(1);
+ }
+ strncpy( PNbuf, system, sizeof PNbuf - 1 );
+ for (p = system; *p; p++)
+ *p = '\0';
+ PN = PNbuf;
+ (void)sprintf(sbuf, "tip%d", BR);
+ system = sbuf;
+
+notnumber:
+ (void)signal(SIGINT, cleanup);
+ (void)signal(SIGQUIT, cleanup);
+ (void)signal(SIGHUP, cleanup);
+ (void)signal(SIGTERM, cleanup);
+ (void)signal(SIGUSR1, tipdone);
+
+ if ((i = hunt(system)) == 0) {
+ printf("all ports busy\n");
+ exit(3);
+ }
+ if (i == -1) {
+ printf("link down\n");
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+ setbuf(stdout, NULL);
+ loginit();
+
+ /*
+ * Kludge, their's no easy way to get the initialization
+ * in the right order, so force it here
+ */
+ if ((PH = getenv("PHONES")) == NOSTR)
+ PH = _PATH_PHONES;
+ vinit(); /* init variables */
+ setparity("even"); /* set the parity table */
+ if ((i = speed(number(value(BAUDRATE)))) == NULL) {
+ printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
+ (void)uu_unlock(uucplock);
+ exit(3);
+ }
+
+ /*
+ * Now that we have the logfile and the ACU open
+ * return to the real uid and gid. These things will
+ * be closed on exit. Swap real and effective uid's
+ * so we can get the original permissions back
+ * for removing the uucp lock.
+ */
+ user_uid();
+
+ /*
+ * Hardwired connections require the
+ * line speed set before they make any transmissions
+ * (this is particularly true of things like a DF03-AC)
+ */
+ if (HW)
+ ttysetup(i);
+ if (p = connect()) {
+ printf("\07%s\n[EOT]\n", p);
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+ exit(1);
+ }
+ if (!HW)
+ ttysetup(i);
+cucommon:
+ /*
+ * From here down the code is shared with
+ * the "cu" version of tip.
+ */
+
+#if HAVE_TERMIOS
+ tcgetattr (0, &otermios);
+ ctermios = otermios;
+#ifndef _POSIX_SOURCE
+ ctermios.c_iflag = (IMAXBEL|IXANY|ISTRIP|IXON|BRKINT);
+ ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOCTL|ECHOE|ECHOKE);
+#else
+ ctermios.c_iflag = (ISTRIP|IXON|BRKINT);
+ ctermios.c_lflag = (PENDIN|IEXTEN|ISIG|ECHOE);
+#endif
+ ctermios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
+ ctermios.c_cc[VINTR] = ctermios.c_cc[VQUIT] = -1;
+ ctermios.c_cc[VSUSP] = ctermios.c_cc[VDSUSP] = ctermios.c_cc[VDISCARD] =
+ ctermios.c_cc[VLNEXT] = -1;
+#else /* HAVE_TERMIOS */
+ ioctl(0, TIOCGETP, (char *)&defarg);
+ ioctl(0, TIOCGETC, (char *)&defchars);
+ ioctl(0, TIOCGLTC, (char *)&deflchars);
+ ioctl(0, TIOCGETD, (char *)&odisc);
+ arg = defarg;
+ arg.sg_flags = ANYP | CBREAK;
+ tchars = defchars;
+ tchars.t_intrc = tchars.t_quitc = -1;
+ ltchars = deflchars;
+ ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
+ = ltchars.t_lnextc = -1;
+#endif /* HAVE_TERMIOS */
+ raw();
+
+ pipe(fildes); pipe(repdes);
+ (void)signal(SIGALRM, timeout);
+
+ /*
+ * Everything's set up now:
+ * connection established (hardwired or dialup)
+ * line conditioned (baud rate, mode, etc.)
+ * internal data structures (variables)
+ * so, fork one process for local side and one for remote.
+ */
+ printf(cumode ? "Connected\r\n" : "\07connected\r\n");
+
+ if (LI != NOSTR && tiplink (LI, 0) != 0) {
+ tipabort ("login failed");
+ }
+
+ if (pid = fork())
+ tipin();
+ else
+ tipout();
+ /*NOTREACHED*/
+}
+
+void
+cleanup()
+{
+
+ daemon_uid();
+ (void)uu_unlock(uucplock);
+#if !HAVE_TERMIOS
+ if (odisc)
+ ioctl(0, TIOCSETD, (char *)&odisc);
+#endif
+ exit(0);
+}
+
+void
+tipdone()
+{
+ tipabort("Hangup.");
+}
+/*
+ * Muck with user ID's. We are setuid to the owner of the lock
+ * directory when we start. user_uid() reverses real and effective
+ * ID's after startup, to run with the user's permissions.
+ * daemon_uid() switches back to the privileged uid for unlocking.
+ * Finally, to avoid running a shell with the wrong real uid,
+ * shell_uid() sets real and effective uid's to the user's real ID.
+ */
+static int uidswapped;
+
+user_uid()
+{
+ if (uidswapped == 0) {
+ seteuid(uid);
+ uidswapped = 1;
+ }
+}
+
+daemon_uid()
+{
+
+ if (uidswapped) {
+ seteuid(euid);
+ uidswapped = 0;
+ }
+}
+
+shell_uid()
+{
+
+ seteuid(uid);
+}
+
+/*
+ * put the controlling keyboard into raw mode
+ */
+void
+raw ()
+{
+#if HAVE_TERMIOS
+ tcsetattr (0, TCSANOW, &ctermios);
+#else /* HAVE_TERMIOS */
+
+ ioctl(0, TIOCSETP, &arg);
+ ioctl(0, TIOCSETC, &tchars);
+ ioctl(0, TIOCSLTC, &ltchars);
+ ioctl(0, TIOCSETD, (char *)&disc);
+#endif /* HAVE_TERMIOS */
+}
+
+
+/*
+ * return keyboard to normal mode
+ */
+unraw()
+{
+#if HAVE_TERMIOS
+ tcsetattr (0, TCSANOW, &otermios);
+#else /* HAVE_TERMIOS */
+
+ ioctl(0, TIOCSETD, (char *)&odisc);
+ ioctl(0, TIOCSETP, (char *)&defarg);
+ ioctl(0, TIOCSETC, (char *)&defchars);
+ ioctl(0, TIOCSLTC, (char *)&deflchars);
+#endif /* HAVE_TERMIOS */
+}
+
+static jmp_buf promptbuf;
+
+/*
+ * Print string ``s'', then read a string
+ * in from the terminal. Handles signals & allows use of
+ * normal erase and kill characters.
+ */
+prompt(s, p)
+ char *s;
+ register char *p;
+{
+ register char *b = p;
+ sig_t oint, oquit;
+
+ stoprompt = 0;
+ oint = signal(SIGINT, intprompt);
+ oquit = signal(SIGQUIT, SIG_IGN);
+ unraw();
+ printf("%s", s);
+ if (setjmp(promptbuf) == 0)
+ while ((*p = getchar()) != EOF && *p != '\n')
+ p++;
+ *p = '\0';
+
+ raw();
+ (void)signal(SIGINT, oint);
+ (void)signal(SIGQUIT, oquit);
+ return (stoprompt || p == b);
+}
+
+/*
+ * Interrupt service routine during prompting
+ */
+void
+intprompt()
+{
+
+ (void)signal(SIGINT, SIG_IGN);
+ stoprompt = 1;
+ printf("\r\n");
+ longjmp(promptbuf, 1);
+}
+
+/*
+ * ****TIPIN TIPIN****
+ */
+tipin()
+{
+ char gch, bol = 1;
+
+ /*
+ * Kinda klugey here...
+ * check for scripting being turned on from the .tiprc file,
+ * but be careful about just using setscript(), as we may
+ * send a SIGEMT before tipout has a chance to set up catching
+ * it; so wait a second, then setscript()
+ */
+ if (boolean(value(SCRIPT))) {
+ sleep(1);
+ setscript();
+ }
+
+ while (1) {
+ gch = getchar()&0177;
+ if ((gch == character(value(ESCAPE))) && bol) {
+ if (!(gch = escape()))
+ continue;
+ } else if (!cumode && gch == character(value(RAISECHAR))) {
+ boolean(value(RAISE)) = !boolean(value(RAISE));
+ continue;
+ } else if (gch == '\r') {
+ bol = 1;
+ pwrite(FD, &gch, 1);
+ if (boolean(value(HALFDUPLEX)))
+ printf("\r\n");
+ continue;
+ } else if (!cumode && gch == character(value(FORCE)))
+ gch = getchar()&0177;
+ bol = any(gch, value(EOL));
+ if (boolean(value(RAISE)) && islower(gch))
+ gch = toupper(gch);
+ pwrite(FD, &gch, 1);
+ if (boolean(value(HALFDUPLEX)))
+ printf("%c", gch);
+ }
+}
+
+extern esctable_t etable[];
+
+/*
+ * Escape handler --
+ * called on recognition of ``escapec'' at the beginning of a line
+ */
+escape()
+{
+ register char gch;
+ register esctable_t *p;
+ char c = character(value(ESCAPE));
+
+ gch = (getchar()&0177);
+ for (p = etable; p->e_char; p++)
+ if (p->e_char == gch) {
+ if ((p->e_flags&PRIV) && uid)
+ continue;
+ printf("%s", ctrl(c));
+ (*p->e_func)(gch);
+ return (0);
+ }
+ /* ESCAPE ESCAPE forces ESCAPE */
+ if (c != gch)
+ pwrite(FD, &c, 1);
+ return (gch);
+}
+
+speed(n)
+ int n;
+{
+#if HAVE_TERMIOS
+ return (n);
+#else
+ register CONST int *p;
+
+ for (p = bauds; *p != -1; p++)
+ if (*p == n)
+ return (p - bauds);
+ return (NULL);
+#endif
+}
+
+any(c, p)
+ register char c, *p;
+{
+ while (p && *p)
+ if (*p++ == c)
+ return (1);
+ return (0);
+}
+
+size(s)
+ register char *s;
+{
+ register int i = 0;
+
+ while (s && *s++)
+ i++;
+ return (i);
+}
+
+char *
+interp(s)
+ register char *s;
+{
+ static char buf[256];
+ register char *p = buf, c, *q;
+
+ while (c = *s++) {
+ for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
+ if (*q++ == c) {
+ *p++ = '\\'; *p++ = *q;
+ goto next;
+ }
+ if (c < 040) {
+ *p++ = '^'; *p++ = c + 'A'-1;
+ } else if (c == 0177) {
+ *p++ = '^'; *p++ = '?';
+ } else
+ *p++ = c;
+ next:
+ ;
+ }
+ *p = '\0';
+ return (buf);
+}
+
+char *
+ctrl(c)
+ char c;
+{
+ static char s[3];
+
+ if (c < 040 || c == 0177) {
+ s[0] = '^';
+ s[1] = c == 0177 ? '?' : c+'A'-1;
+ s[2] = '\0';
+ } else {
+ s[0] = c;
+ s[1] = '\0';
+ }
+ return (s);
+}
+
+/*
+ * Help command
+ */
+help(c)
+ char c;
+{
+ register esctable_t *p;
+
+ printf("%c\r\n", c);
+ for (p = etable; p->e_char; p++) {
+ if ((p->e_flags&PRIV) && uid)
+ continue;
+ printf("%2s", ctrl(character(value(ESCAPE))));
+ printf("%-2s %c %s\r\n", ctrl(p->e_char),
+ p->e_flags&EXP ? '*': ' ', p->e_help);
+ }
+}
+
+/*
+ * Set up the "remote" tty's state
+ */
+void
+ttysetup (int speed)
+{
+#if HAVE_TERMIOS
+ struct termios termios;
+ tcgetattr (FD, &termios);
+ if (boolean(value(TAND)))
+ termios.c_iflag = IXOFF;
+ else
+ termios.c_iflag = 0;
+#ifndef _POSIX_SOURCE
+ termios.c_lflag = (PENDIN|ECHOKE|ECHOE);
+#else
+ termios.c_lflag = (PENDIN|ECHOE);
+#endif
+ termios.c_cflag = (CLOCAL|HUPCL|CREAD|CS8);
+ termios.c_ispeed = termios.c_ospeed = speed;
+ tcsetattr (FD, TCSANOW, &termios);
+#else /* HAVE_TERMIOS */
+ unsigned bits = LDECCTQ;
+
+ arg.sg_ispeed = arg.sg_ospeed = speed;
+ arg.sg_flags = RAW;
+ if (boolean(value(TAND)))
+ arg.sg_flags |= TANDEM;
+ ioctl(FD, TIOCSETP, (char *)&arg);
+ ioctl(FD, TIOCLBIS, (char *)&bits);
+#endif /* HAVE_TERMIOS */
+}
+
+/*
+ * Return "simple" name from a file name,
+ * strip leading directories.
+ */
+char *
+sname(s)
+ register char *s;
+{
+ register char *p = s;
+
+ while (*s)
+ if (*s++ == '/')
+ p = s;
+ return (p);
+}
+
+static char partab[0200];
+static int bits8;
+
+/*
+ * Do a write to the remote machine with the correct parity.
+ * We are doing 8 bit wide output, so we just generate a character
+ * with the right parity and output it.
+ */
+pwrite(fd, buf, n)
+ int fd;
+ char *buf;
+ register int n;
+{
+ register int i;
+ register char *bp;
+ extern int errno;
+
+ bp = buf;
+ if (bits8 == 0)
+ for (i = 0; i < n; i++) {
+ *bp = partab[(*bp) & 0177];
+ bp++;
+ }
+ if (write(fd, buf, n) < 0) {
+ if (errno == EIO)
+ tipabort("Lost carrier.");
+ if (errno == ENODEV)
+ tipabort("tty not available.");
+ /* this is questionable */
+ perror("write");
+ }
+}
+
+/*
+ * Build a parity table with appropriate high-order bit.
+ */
+setparity(defparity)
+ char *defparity;
+{
+ register int i, flip, clr, set;
+ char *parity;
+ extern char evenpartab[];
+
+ if (value(PARITY) == NOSTR)
+ value(PARITY) = defparity;
+ parity = value(PARITY);
+ if (equal(parity, "none")) {
+ bits8 = 1;
+ return;
+ }
+ bits8 = 0;
+ flip = 0;
+ clr = 0377;
+ set = 0;
+ if (equal(parity, "odd"))
+ flip = 0200; /* reverse bit 7 */
+ else if (equal(parity, "zero"))
+ clr = 0177; /* turn off bit 7 */
+ else if (equal(parity, "one"))
+ set = 0200; /* turn on bit 7 */
+ else if (!equal(parity, "even")) {
+ (void) fprintf(stderr, "%s: unknown parity value\r\n", parity);
+ (void) fflush(stderr);
+ }
+ for (i = 0; i < 0200; i++)
+ partab[i] = evenpartab[i] ^ flip | set & clr;
+}
diff --git a/usr.bin/tip/tip/tip.h b/usr.bin/tip/tip/tip.h
new file mode 100644
index 0000000..fb45ce0
--- /dev/null
+++ b/usr.bin/tip/tip/tip.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tip.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * tip - terminal interface program
+ */
+
+#include <sys/types.h>
+#include <machine/endian.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#if HAVE_TERMIOS
+#include <sys/ioctl.h> /* for TIOCHPCL */
+#include <sys/filio.h> /* for FIONREAD */
+#include <sys/termios.h>
+#else
+#include <sgtty.h>
+#endif
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * Remote host attributes
+ */
+char *DV; /* UNIX device(s) to open */
+char *EL; /* chars marking an EOL */
+char *CM; /* initial connection message */
+char *IE; /* EOT to expect on input */
+char *OE; /* EOT to send to complete FT */
+char *CU; /* call unit if making a phone call */
+char *AT; /* acu type */
+char *PN; /* phone number(s) */
+char *DI; /* disconnect string */
+char *PA; /* parity to be generated */
+
+char *PH; /* phone number file */
+char *RM; /* remote file name */
+char *HO; /* host name */
+
+char *LI; /* login script */
+char *LO; /* logout script */
+
+long BR; /* line speed for conversation */
+long FS; /* frame size for transfers */
+
+char DU; /* this host is dialed up */
+char HW; /* this device is hardwired, see hunt.c */
+char *ES; /* escape character */
+char *EX; /* exceptions */
+char *FO; /* force (literal next) char*/
+char *RC; /* raise character */
+char *RE; /* script record file */
+char *PR; /* remote prompt */
+long DL; /* line delay for file transfers to remote */
+long CL; /* char delay for file transfers to remote */
+long ET; /* echocheck timeout */
+char HD; /* this host is half duplex - do local echo */
+
+/*
+ * String value table
+ */
+typedef
+ struct {
+ char *v_name; /* whose name is it */
+ char v_type; /* for interpreting set's */
+ char v_access; /* protection of touchy ones */
+ char *v_abrev; /* possible abreviation */
+ char *v_value; /* casted to a union later */
+ }
+ value_t;
+
+#define STRING 01 /* string valued */
+#define BOOL 02 /* true-false value */
+#define NUMBER 04 /* numeric value */
+#define CHAR 010 /* character value */
+
+#define WRITE 01 /* write access to variable */
+#define READ 02 /* read access */
+
+#define CHANGED 01 /* low bit is used to show modification */
+#define PUBLIC 1 /* public access rights */
+#define PRIVATE 03 /* private to definer */
+#define ROOT 05 /* root defined */
+
+#define TRUE 1
+#define FALSE 0
+
+#define ENVIRON 020 /* initialize out of the environment */
+#define IREMOTE 040 /* initialize out of remote structure */
+#define INIT 0100 /* static data space used for initialization */
+#define TMASK 017
+
+/*
+ * Definition of ACU line description
+ */
+typedef
+ struct {
+ char *acu_name;
+ int (*acu_dialer)();
+ void (*acu_disconnect)();
+ void (*acu_abort)();
+ }
+ acu_t;
+
+#define equal(a, b) (strcmp(a,b)==0)/* A nice function to string compare */
+
+/*
+ * variable manipulation stuff --
+ * if we defined the value entry in value_t, then we couldn't
+ * initialize it in vars.c, so we cast it as needed to keep lint
+ * happy.
+ */
+typedef
+ union {
+ int zz_number;
+ short zz_boolean[2];
+ char zz_character[4];
+ int *zz_address;
+ }
+ zzhack;
+
+#define value(v) vtable[v].v_value
+
+#define number(v) ((((zzhack *)(&(v))))->zz_number)
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define boolean(v) ((((zzhack *)(&(v))))->zz_boolean[0])
+#define character(v) ((((zzhack *)(&(v))))->zz_character[0])
+#endif
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define boolean(v) ((((zzhack *)(&(v))))->zz_boolean[1])
+#define character(v) ((((zzhack *)(&(v))))->zz_character[3])
+#endif
+
+#define address(v) ((((zzhack *)(&(v))))->zz_address)
+
+/*
+ * Escape command table definitions --
+ * lookup in this table is performed when ``escapec'' is recognized
+ * at the begining of a line (as defined by the eolmarks variable).
+*/
+
+typedef
+ struct {
+ char e_char; /* char to match on */
+ char e_flags; /* experimental, priviledged */
+ char *e_help; /* help string */
+ int (*e_func)(); /* command */
+ }
+ esctable_t;
+
+#define NORM 00 /* normal protection, execute anyone */
+#define EXP 01 /* experimental, mark it with a `*' on help */
+#define PRIV 02 /* priviledged, root execute only */
+
+extern int vflag; /* verbose during reading of .tiprc file */
+extern value_t vtable[]; /* variable table */
+
+#if !ACULOG
+#define logent(a, b, c, d)
+#define loginit()
+#endif
+
+/*
+ * Definition of indices into variable table so
+ * value(DEFINE) turns into a static address.
+ */
+
+/*
+'a,.!awk '{ printf("\%s \%s \%d\n", $1, $2, NR - 1); }'
+*/
+
+#define BEAUTIFY 0
+#define BAUDRATE 1
+#define DIALTIMEOUT 2
+#define EOFREAD 3
+#define EOFWRITE 4
+#define EOL 5
+#define ESCAPE 6
+#define EXCEPTIONS 7
+#define FORCE 8
+#define FRAMESIZE 9
+#define HOST 10
+#define LOG 11
+#define LOGIN 12
+#define LOGOUT 13
+#define PHONES 14
+#define PROMPT 15
+#define RAISE 16
+#define RAISECHAR 17
+#define RECORD 18
+#define REMOTE 19
+#define SCRIPT 20
+#define TABEXPAND 21
+#define VERBOSE 22
+#define SHELL 23
+#define HOME 24
+#define ECHOCHECK 25
+#define DISCONNECT 26
+#define TAND 27
+#define LDELAY 28
+#define CDELAY 29
+#define ETIMEOUT 30
+#define RAWFTP 31
+#define HALFDUPLEX 32
+#define LECHO 33
+#define PARITY 34
+#define NOVAL ((value_t *)NULL)
+#define NOACU ((acu_t *)NULL)
+#define NOSTR ((char *)NULL)
+#define NOFILE ((FILE *)NULL)
+#define NOPWD ((struct passwd *)0)
+
+#if HAVE_TERMIOS
+struct termios otermios;
+struct termios ctermios;
+#else /* HAVE_TERMIOS */
+struct sgttyb arg; /* current mode of local terminal */
+struct sgttyb defarg; /* initial mode of local terminal */
+struct tchars tchars; /* current state of terminal */
+struct tchars defchars; /* initial state of terminal */
+struct ltchars ltchars; /* current local characters of terminal */
+struct ltchars deflchars; /* initial local characters of terminal */
+#endif /* HAVE_TERMIOS */
+
+FILE *fscript; /* FILE for scripting */
+
+int fildes[2]; /* file transfer synchronization channel */
+int repdes[2]; /* read process sychronization channel */
+int FD; /* open file descriptor to remote host */
+int AC; /* open file descriptor to dialer (v831 only) */
+int vflag; /* print .tiprc initialization sequence */
+int sfd; /* for ~< operation */
+int pid; /* pid of tipout */
+uid_t uid, euid; /* real and effective user id's */
+gid_t gid, egid; /* real and effective group id's */
+int stop; /* stop transfer session flag */
+int quit; /* same; but on other end */
+int intflag; /* recognized interrupt */
+int stoprompt; /* for interrupting a prompt session */
+int timedout; /* ~> transfer timedout */
+int cumode; /* simulating the "cu" program */
+
+char fname[80]; /* file name buffer for ~< */
+char copyname[80]; /* file name buffer for ~> */
+char ccc; /* synchronization character */
+char ch; /* for tipout */
+char *uucplock; /* name of lock file for uucp's */
+
+int odisc; /* initial tty line discipline */
+extern int disc; /* current tty discpline */
+
+extern char *ctrl();
+extern char *vinterp();
+extern char *connect();
+
+int tipabort __P((char *));
+
+#define TL_VERBOSE 0x00000001
+#define TL_SIGNAL_TIPOUT 0x00000002
+
+int tiplink (char *cmd, unsigned int flags);
+void raw ();
+
+/* end of tip.h */
diff --git a/usr.bin/tip/tip/tipconf.h b/usr.bin/tip/tip/tipconf.h
new file mode 100644
index 0000000..f21be62
--- /dev/null
+++ b/usr.bin/tip/tip/tipconf.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tipconf.h 8.1 (Berkeley) 3/25/95
+ */
+
+#ifndef tipconf_h_included
+#define tipconf_h_included
+
+/*
+ Define constness
+*/
+#define CONST const
+
+/*
+ Specify default bit rate for connections
+*/
+#define DEFBR 1200
+
+/*
+ Default frame size for file transfer buffering of writes
+ on local side
+*/
+#ifndef BUFSIZ
+#define DEFFS 1024
+#else
+#define DEFFS BUFSIZ
+#endif
+
+/*
+ Enable logging of ACU use
+*/
+#define ACULOG 1
+
+/*
+ Strip phone #s from ACU log file
+*/
+#define PRISTINE 1
+
+/*
+ Enable command to "connect" remote with local process
+*/
+#define CONNECT 1
+
+/*
+ Specify style of UUCP lock files
+*/
+#define HAVE_V2_LOCKFILES 0
+#define HAVE_HDB_LOCKFILES 1
+
+/*
+ System has a millisecond based sleep function
+*/
+#define HAVE_USLEEP 0
+
+/*
+ System has select
+*/
+#define HAVE_SELECT 1
+
+/*
+ System has termios tty interface
+*/
+#define HAVE_TERMIOS 1
+
+/*
+ Include configurable modem driver
+*/
+#define UNIDIALER 1
+
+/*
+ Specify builtin modem drivers to include
+*/
+#define BIZ1031 0
+#define BIZ1022 0
+#define COURIER 0
+#define DF02 0
+#define DF03 0
+#define DN11 0
+#define HAYES 0
+#define MULTITECH 0
+#define T3000 0
+#define V3451 0
+#define V831 0
+#define VENTEL 0
+
+/*
+ Include cu interface so that, when tip is linked to cu and then
+ invoked as cu, it behaves like cu.
+*/
+#define INCLUDE_CU_INTERFACE 0
+
+#endif
+
+/* end of tipconf.h */
diff --git a/usr.bin/tip/tip/tipout.c b/usr.bin/tip/tip/tipout.c
new file mode 100644
index 0000000..3464dd5
--- /dev/null
+++ b/usr.bin/tip/tip/tipout.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tipout.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+/*
+ * tip
+ *
+ * lower fork of tip -- handles passive side
+ * reading from the remote host
+ */
+
+static jmp_buf sigbuf;
+
+/*
+ * TIPOUT wait state routine --
+ * sent by TIPIN when it wants to posses the remote host
+ */
+void
+intIOT()
+{
+
+ write(repdes[1],&ccc,1);
+ read(fildes[0], &ccc,1);
+ longjmp(sigbuf, 1);
+}
+
+/*
+ * Scripting command interpreter --
+ * accepts script file name over the pipe and acts accordingly
+ */
+void
+intEMT()
+{
+ char c, line[256];
+ register char *pline = line;
+ char reply;
+
+ read(fildes[0], &c, 1);
+ while (c != '\n') {
+ *pline++ = c;
+ read(fildes[0], &c, 1);
+ }
+ *pline = '\0';
+ if (boolean(value(SCRIPT)) && fscript != NULL)
+ fclose(fscript);
+ if (pline == line) {
+ boolean(value(SCRIPT)) = FALSE;
+ reply = 'y';
+ } else {
+ if ((fscript = fopen(line, "a")) == NULL)
+ reply = 'n';
+ else {
+ reply = 'y';
+ boolean(value(SCRIPT)) = TRUE;
+ }
+ }
+ write(repdes[1], &reply, 1);
+ longjmp(sigbuf, 1);
+}
+
+void
+intTERM()
+{
+
+ if (boolean(value(SCRIPT)) && fscript != NULL)
+ fclose(fscript);
+ exit(0);
+}
+
+void
+intSYS()
+{
+
+ boolean(value(BEAUTIFY)) = !boolean(value(BEAUTIFY));
+ longjmp(sigbuf, 1);
+}
+
+/*
+ * ****TIPOUT TIPOUT****
+ */
+tipout()
+{
+ char buf[BUFSIZ];
+ register char *cp;
+ register int cnt;
+ extern int errno;
+ int omask;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGEMT, intEMT); /* attention from TIPIN */
+ signal(SIGTERM, intTERM); /* time to go signal */
+ signal(SIGIOT, intIOT); /* scripting going on signal */
+ signal(SIGHUP, intTERM); /* for dial-ups */
+ signal(SIGSYS, intSYS); /* beautify toggle */
+ (void) setjmp(sigbuf);
+ for (omask = 0;; sigsetmask(omask)) {
+ cnt = read(FD, buf, BUFSIZ);
+ if (cnt <= 0) {
+ /* lost carrier */
+ if (cnt < 0 && errno == EIO) {
+ sigblock(sigmask(SIGTERM));
+ intTERM();
+ /*NOTREACHED*/
+ } else if (cnt == 0 && errno == ENOENT) {
+ if (getppid() != 1)
+ kill(getppid(),SIGUSR1);
+ sigblock(sigmask(SIGTERM));
+ intTERM();
+ /*NOTREACHED*/
+ } else if (cnt < 0) {
+ if (getppid() != 1)
+ kill(getppid(),SIGUSR1);
+ sigblock(sigmask(SIGTERM));
+ intTERM();
+ /*NOTREACHED*/
+ }
+ continue;
+ }
+#define ALLSIGS sigmask(SIGEMT)|sigmask(SIGTERM)|sigmask(SIGIOT)|sigmask(SIGSYS)
+ omask = sigblock(ALLSIGS);
+ for (cp = buf; cp < buf + cnt; cp++)
+ *cp &= 0177;
+ write(1, buf, cnt);
+ if (boolean(value(SCRIPT)) && fscript != NULL) {
+ if (!boolean(value(BEAUTIFY))) {
+ fwrite(buf, 1, cnt, fscript);
+ continue;
+ }
+ for (cp = buf; cp < buf + cnt; cp++)
+ if ((*cp >= ' ' && *cp <= '~') ||
+ any(*cp, value(EXCEPTIONS)))
+ putc(*cp, fscript);
+ }
+ }
+}
diff --git a/usr.bin/tip/tip/uucplock.c b/usr.bin/tip/tip/uucplock.c
new file mode 100644
index 0000000..5c78bd4
--- /dev/null
+++ b/usr.bin/tip/tip/uucplock.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)uucplock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/dir.h>
+#include <errno.h>
+
+#include "tipconf.h"
+#include "pathnames.h"
+
+/* Forward declarations */
+static int put_pid (int fd, int pid);
+static int get_pid (int fd);
+
+/*
+ * uucp style locking routines
+ * return: 0 - success
+ * -1 - failure
+ */
+
+uu_lock (char *ttyname)
+{
+ int fd, pid;
+ char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN];
+ off_t lseek();
+
+ (void)sprintf(tbuf, _PATH_LOCKDIRNAME, ttyname);
+ fd = open(tbuf, O_RDWR|O_CREAT|O_EXCL, 0660);
+ if (fd < 0) {
+ /*
+ * file is already locked
+ * check to see if the process holding the lock still exists
+ */
+ fd = open(tbuf, O_RDWR, 0);
+ if (fd < 0) {
+ perror("lock open");
+ return(-1);
+ }
+ if ((pid = get_pid (fd)) == -1) {
+ (void)close(fd);
+ perror("lock read");
+ return(-1);
+ }
+
+ if (kill(pid, 0) == 0 || errno != ESRCH) {
+ (void)close(fd); /* process is still running */
+ return(-1);
+ }
+ /*
+ * The process that locked the file isn't running, so
+ * we'll lock it ourselves
+ */
+ if (lseek(fd, 0L, L_SET) < 0) {
+ (void)close(fd);
+ perror("lock lseek");
+ return(-1);
+ }
+ /* fall out and finish the locking process */
+ }
+ pid = getpid();
+ if (!put_pid (fd, pid)) {
+ (void)close(fd);
+ (void)unlink(tbuf);
+ perror("lock write");
+ return(-1);
+ }
+ (void)close(fd);
+ return(0);
+}
+
+uu_unlock (char *ttyname)
+{
+ char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN];
+
+ (void)sprintf(tbuf, _PATH_LOCKDIRNAME, ttyname);
+ return(unlink(tbuf));
+}
+
+static int put_pid (int fd, int pid)
+{
+#if HAVE_V2_LOCKFILES
+ return write (fd, (char *)&pid, sizeof (pid)) == sizeof (pid);
+#else
+ char buf [32];
+ int len;
+ len = sprintf (buf, "%10ld\n", (long) pid);
+ return write (fd, buf, len) == len;
+#endif
+}
+
+static int get_pid (int fd)
+{
+ int bytes_read, pid;
+#if HAVE_V2_LOCKFILES
+ bytes_read = read (fd, &pid, sizeof (pid));
+ if (bytes_read != sizeof (pid))
+ pid = -1;
+#else
+ char buf [32];
+ bytes_read = read (fd, buf, sizeof (buf) - 1);
+ if (bytes_read > 0) {
+ buf [bytes_read] = '\0';
+ pid = strtol (buf, (char **) NULL, 10);
+ }
+ else
+ pid = -1;
+#endif
+ return pid;
+}
+
+/* end of uucplock.c */
diff --git a/usr.bin/tip/tip/value.c b/usr.bin/tip/tip/value.c
new file mode 100644
index 0000000..7823805
--- /dev/null
+++ b/usr.bin/tip/tip/value.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)value.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+#define MIDDLE 35
+
+static value_t *vlookup();
+static int col = 0;
+
+/*
+ * Variable manipulation
+ */
+vinit()
+{
+ register value_t *p;
+ register char *cp;
+ FILE *f;
+ char file[256];
+
+ for (p = vtable; p->v_name != NULL; p++) {
+ if (p->v_type&ENVIRON)
+ if (cp = getenv(p->v_name))
+ p->v_value = cp;
+ if (p->v_type&IREMOTE)
+ number(p->v_value) = *address(p->v_value);
+ }
+ /*
+ * Read the .tiprc file in the HOME directory
+ * for sets
+ */
+ strcpy(file, value(HOME));
+ strcat(file, "/.tiprc");
+ if ((f = fopen(file, "r")) != NULL) {
+ register char *tp;
+
+ while (fgets(file, sizeof(file)-1, f) != NULL) {
+ if (vflag)
+ printf("set %s", file);
+ if (tp = rindex(file, '\n'))
+ *tp = '\0';
+ vlex(file);
+ }
+ fclose(f);
+ }
+ /*
+ * To allow definition of exception prior to fork
+ */
+ vtable[EXCEPTIONS].v_access &= ~(WRITE<<PUBLIC);
+}
+
+static int vaccess();
+
+/*VARARGS1*/
+vassign(p, v)
+ register value_t *p;
+ char *v;
+{
+
+ if (!vaccess(p->v_access, WRITE)) {
+ printf("access denied\r\n");
+ return;
+ }
+ switch (p->v_type&TMASK) {
+
+ case STRING:
+ if (p->v_value && equal(p->v_value, v))
+ return;
+ if (!(p->v_type&(ENVIRON|INIT)))
+ free(p->v_value);
+ if ((p->v_value = malloc(size(v)+1)) == NOSTR) {
+ printf("out of core\r\n");
+ return;
+ }
+ p->v_type &= ~(ENVIRON|INIT);
+ strcpy(p->v_value, v);
+ break;
+
+ case NUMBER:
+ if (number(p->v_value) == number(v))
+ return;
+ number(p->v_value) = number(v);
+ break;
+
+ case BOOL:
+ if (boolean(p->v_value) == (*v != '!'))
+ return;
+ boolean(p->v_value) = (*v != '!');
+ break;
+
+ case CHAR:
+ if (character(p->v_value) == *v)
+ return;
+ character(p->v_value) = *v;
+ }
+ p->v_access |= CHANGED;
+}
+
+static void vprint();
+
+vlex(s)
+ register char *s;
+{
+ register value_t *p;
+ static void vtoken();
+
+ if (equal(s, "all")) {
+ for (p = vtable; p->v_name; p++)
+ if (vaccess(p->v_access, READ))
+ vprint(p);
+ } else {
+ register char *cp;
+
+ do {
+ if (cp = vinterp(s, ' '))
+ cp++;
+ vtoken(s);
+ s = cp;
+ } while (s);
+ }
+ if (col > 0) {
+ printf("\r\n");
+ col = 0;
+ }
+}
+
+static void
+vtoken(s)
+ register char *s;
+{
+ register value_t *p;
+ register char *cp;
+ char *expand();
+
+ if (cp = index(s, '=')) {
+ *cp = '\0';
+ if (p = vlookup(s)) {
+ cp++;
+ if (p->v_type&NUMBER)
+ vassign(p, atoi(cp));
+ else {
+ if (strcmp(s, "record") == 0)
+ cp = expand(cp);
+ vassign(p, cp);
+ }
+ return;
+ }
+ } else if (cp = index(s, '?')) {
+ *cp = '\0';
+ if ((p = vlookup(s)) && vaccess(p->v_access, READ)) {
+ vprint(p);
+ return;
+ }
+ } else {
+ if (*s != '!')
+ p = vlookup(s);
+ else
+ p = vlookup(s+1);
+ if (p != NOVAL) {
+ vassign(p, s);
+ return;
+ }
+ }
+ printf("%s: unknown variable\r\n", s);
+}
+
+static void
+vprint(p)
+ register value_t *p;
+{
+ register char *cp;
+ extern char *interp(), *ctrl();
+
+ if (col > 0 && col < MIDDLE)
+ while (col++ < MIDDLE)
+ putchar(' ');
+ col += size(p->v_name);
+ switch (p->v_type&TMASK) {
+
+ case BOOL:
+ if (boolean(p->v_value) == FALSE) {
+ col++;
+ putchar('!');
+ }
+ printf("%s", p->v_name);
+ break;
+
+ case STRING:
+ printf("%s=", p->v_name);
+ col++;
+ if (p->v_value) {
+ cp = interp(p->v_value, NULL);
+ col += size(cp);
+ printf("%s", cp);
+ }
+ break;
+
+ case NUMBER:
+ col += 6;
+ printf("%s=%-5d", p->v_name, number(p->v_value));
+ break;
+
+ case CHAR:
+ printf("%s=", p->v_name);
+ col++;
+ if (p->v_value) {
+ cp = ctrl(character(p->v_value));
+ col += size(cp);
+ printf("%s", cp);
+ }
+ break;
+ }
+ if (col >= MIDDLE) {
+ col = 0;
+ printf("\r\n");
+ return;
+ }
+}
+
+
+static int
+vaccess(mode, rw)
+ register unsigned mode, rw;
+{
+ if (mode & (rw<<PUBLIC))
+ return (1);
+ if (mode & (rw<<PRIVATE))
+ return (1);
+ return ((mode & (rw<<ROOT)) && getuid() == 0);
+}
+
+static value_t *
+vlookup(s)
+ register char *s;
+{
+ register value_t *p;
+
+ for (p = vtable; p->v_name; p++)
+ if (equal(p->v_name, s) || (p->v_abrev && equal(p->v_abrev, s)))
+ return (p);
+ return (NULL);
+}
+
+char *
+vinterp(s, stop)
+ register char *s;
+ char stop;
+{
+ register char *p = s, c;
+ int num;
+
+ while ((c = *s++) && c != stop)
+ switch (c) {
+
+ case '^':
+ if (*s)
+ *p++ = *s++ - 0100;
+ else
+ *p++ = c;
+ break;
+
+ case '\\':
+ num = 0;
+ c = *s++;
+ if (c >= '0' && c <= '7')
+ num = (num<<3)+(c-'0');
+ else {
+ register char *q = "n\nr\rt\tb\bf\f";
+
+ for (; *q; q++)
+ if (c == *q++) {
+ *p++ = *q;
+ goto cont;
+ }
+ *p++ = c;
+ cont:
+ break;
+ }
+ if ((c = *s++) >= '0' && c <= '7') {
+ num = (num<<3)+(c-'0');
+ if ((c = *s++) >= '0' && c <= '7')
+ num = (num<<3)+(c-'0');
+ else
+ s--;
+ } else
+ s--;
+ *p++ = num;
+ break;
+
+ default:
+ *p++ = c;
+ }
+ *p = '\0';
+ return (c == stop ? s-1 : NULL);
+}
+
+/*
+ * assign variable s with value v (for NUMBER or STRING or CHAR types)
+ */
+
+vstring(s,v)
+ register char *s;
+ register char *v;
+{
+ register value_t *p;
+ char *expand();
+
+ p = vlookup(s);
+ if (p == 0)
+ return (1);
+ if (p->v_type&NUMBER)
+ vassign(p, atoi(v));
+ else {
+ if (strcmp(s, "record") == 0)
+ v = expand(v);
+ vassign(p, v);
+ }
+ return (0);
+}
diff --git a/usr.bin/tip/tip/vars.c b/usr.bin/tip/tip/vars.c
new file mode 100644
index 0000000..5fad05a
--- /dev/null
+++ b/usr.bin/tip/tip/vars.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vars.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tipconf.h"
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * Definition of variables
+ */
+value_t vtable[] = {
+ { "beautify", BOOL, (READ|WRITE)<<PUBLIC,
+ "be", (char *)TRUE },
+ { "baudrate", NUMBER|IREMOTE|INIT, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "ba", (char *)&BR },
+ { "dialtimeout",NUMBER, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "dial", (char *)60 },
+ { "eofread", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofr", (char *)&IE },
+ { "eofwrite", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofw", (char *)&OE },
+ { "eol", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ NOSTR, (char *)&EL },
+ { "escape", CHAR, (READ|WRITE)<<PUBLIC,
+ "es", (char *)'~' },
+ { "exceptions", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "ex", (char *)&EX },
+ { "force", CHAR, (READ|WRITE)<<PUBLIC,
+ "fo", (char *)CTRL('p') },
+ { "framesize", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "fr", (char *)&FS },
+ { "host", STRING|IREMOTE|INIT, READ<<PUBLIC,
+ "ho", (char *)&HO },
+ { "log", STRING|INIT, (READ|WRITE)<<ROOT,
+ NOSTR, _PATH_ACULOG },
+ { "login", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "li", (char *)&LI },
+ { "logout", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "lo", (char *)&LO },
+ { "phones", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&PH },
+ { "prompt", CHAR, (READ|WRITE)<<PUBLIC,
+ "pr", (char *)'\n' },
+ { "raise", BOOL, (READ|WRITE)<<PUBLIC,
+ "ra", (char *)FALSE },
+ { "raisechar", CHAR, (READ|WRITE)<<PUBLIC,
+ "rc", (char *)CTRL('a') },
+ { "record", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "rec", (char *)&RE },
+ { "remote", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&RM },
+ { "script", BOOL, (READ|WRITE)<<PUBLIC,
+ "sc", (char *)FALSE },
+ { "tabexpand", BOOL, (READ|WRITE)<<PUBLIC,
+ "tab", (char *)FALSE },
+ { "verbose", BOOL, (READ|WRITE)<<PUBLIC,
+ "verb", (char *)TRUE },
+ { "SHELL", STRING|ENVIRON|INIT, (READ|WRITE)<<PUBLIC,
+ NULL, _PATH_BSHELL },
+ { "HOME", STRING|ENVIRON, (READ|WRITE)<<PUBLIC,
+ NOSTR, NOSTR },
+ { "echocheck", BOOL, (READ|WRITE)<<PUBLIC,
+ "ec", (char *)FALSE },
+ { "disconnect", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "di", (char *)&DI },
+ { "tandem", BOOL, (READ|WRITE)<<PUBLIC,
+ "ta", (char *)TRUE },
+ { "linedelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "ldelay", (char *)&DL },
+ { "chardelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "cdelay", (char *)&CL },
+ { "etimeout", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "et", (char *)&ET },
+ { "rawftp", BOOL, (READ|WRITE)<<PUBLIC,
+ "raw", (char *)FALSE },
+ { "halfduplex", BOOL, (READ|WRITE)<<PUBLIC,
+ "hdx", (char *)FALSE },
+ { "localecho", BOOL, (READ|WRITE)<<PUBLIC,
+ "le", (char *)FALSE },
+ { "parity", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "par", (char *)&PA },
+ { NOSTR, NULL, NULL, NOSTR, NOSTR }
+};
diff --git a/usr.bin/tip/tipout.c b/usr.bin/tip/tipout.c
new file mode 100644
index 0000000..246cfb7
--- /dev/null
+++ b/usr.bin/tip/tipout.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tipout.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+/*
+ * tip
+ *
+ * lower fork of tip -- handles passive side
+ * reading from the remote host
+ */
+
+static jmp_buf sigbuf;
+
+/*
+ * TIPOUT wait state routine --
+ * sent by TIPIN when it wants to posses the remote host
+ */
+void
+intIOT()
+{
+
+ write(repdes[1],&ccc,1);
+ read(fildes[0], &ccc,1);
+ longjmp(sigbuf, 1);
+}
+
+/*
+ * Scripting command interpreter --
+ * accepts script file name over the pipe and acts accordingly
+ */
+void
+intEMT()
+{
+ char c, line[256];
+ register char *pline = line;
+ char reply;
+
+ read(fildes[0], &c, 1);
+ while (c != '\n') {
+ *pline++ = c;
+ read(fildes[0], &c, 1);
+ }
+ *pline = '\0';
+ if (boolean(value(SCRIPT)) && fscript != NULL)
+ fclose(fscript);
+ if (pline == line) {
+ boolean(value(SCRIPT)) = FALSE;
+ reply = 'y';
+ } else {
+ if ((fscript = fopen(line, "a")) == NULL)
+ reply = 'n';
+ else {
+ reply = 'y';
+ boolean(value(SCRIPT)) = TRUE;
+ }
+ }
+ write(repdes[1], &reply, 1);
+ longjmp(sigbuf, 1);
+}
+
+void
+intTERM()
+{
+
+ if (boolean(value(SCRIPT)) && fscript != NULL)
+ fclose(fscript);
+ exit(0);
+}
+
+void
+intSYS()
+{
+
+ boolean(value(BEAUTIFY)) = !boolean(value(BEAUTIFY));
+ longjmp(sigbuf, 1);
+}
+
+/*
+ * ****TIPOUT TIPOUT****
+ */
+tipout()
+{
+ char buf[BUFSIZ];
+ register char *cp;
+ register int cnt;
+ extern int errno;
+ int omask;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGEMT, intEMT); /* attention from TIPIN */
+ signal(SIGTERM, intTERM); /* time to go signal */
+ signal(SIGIOT, intIOT); /* scripting going on signal */
+ signal(SIGHUP, intTERM); /* for dial-ups */
+ signal(SIGSYS, intSYS); /* beautify toggle */
+ (void) setjmp(sigbuf);
+ for (omask = 0;; sigsetmask(omask)) {
+ cnt = read(FD, buf, BUFSIZ);
+ if (cnt <= 0) {
+ /* lost carrier */
+ if (cnt < 0 && errno == EIO) {
+ sigblock(sigmask(SIGTERM));
+ intTERM();
+ /*NOTREACHED*/
+ } else if (cnt == 0 && errno == ENOENT) {
+ kill(getppid(),SIGUSR1);
+ sigblock(sigmask(SIGTERM));
+ intTERM();
+ /*NOTREACHED*/
+ } else {
+ printf("%d %d\r",cnt,errno);
+ fflush(stdout);
+ }
+ continue;
+ }
+#define ALLSIGS sigmask(SIGEMT)|sigmask(SIGTERM)|sigmask(SIGIOT)|sigmask(SIGSYS)
+ omask = sigblock(ALLSIGS);
+ for (cp = buf; cp < buf + cnt; cp++)
+ *cp &= 0177;
+ write(1, buf, cnt);
+ if (boolean(value(SCRIPT)) && fscript != NULL) {
+ if (!boolean(value(BEAUTIFY))) {
+ fwrite(buf, 1, cnt, fscript);
+ continue;
+ }
+ for (cp = buf; cp < buf + cnt; cp++)
+ if ((*cp >= ' ' && *cp <= '~') ||
+ any(*cp, value(EXCEPTIONS)))
+ putc(*cp, fscript);
+ }
+ }
+}
diff --git a/usr.bin/tip/uucplock.c b/usr.bin/tip/uucplock.c
new file mode 100644
index 0000000..917d812
--- /dev/null
+++ b/usr.bin/tip/uucplock.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)uucplock.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/dir.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "pathnames.h"
+
+/*
+ * uucp style locking routines
+ * return: 0 - success
+ * -1 - failure
+ */
+
+uu_lock(ttyname)
+ char *ttyname;
+{
+ int fd, pid;
+ char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN];
+ FILE *ff;
+
+ (void)sprintf(tbuf, _PATH_LOCKDIRNAME, ttyname);
+ fd = open(tbuf, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd >= 0)
+ ff = fdopen(fd, "w");
+ if (fd < 0 || ff == NULL) {
+ /*
+ * file is already locked
+ * check to see if the process holding the lock still exists
+ */
+ ff = fopen(tbuf, "r+");
+ if (ff == NULL) {
+ perror("lock open");
+ return(-1);
+ }
+ if (fscanf(ff, "%10d\n", &pid) != 1) {
+ perror("lock read");
+ (void)fclose(ff);
+ return(-1);
+ }
+
+ if (kill(pid, 0) == 0 || errno != ESRCH) {
+ (void)fclose(ff); /* process is still running */
+ return(-1);
+ }
+ /*
+ * The process that locked the file isn't running, so
+ * we'll lock it ourselves
+ */
+ rewind(ff);
+ /* fall out and finish the locking process */
+ }
+ (void)fprintf(ff, "%10d\n", getpid());
+ (void)fclose(ff);
+ return(0);
+}
+
+uu_unlock(ttyname)
+ char *ttyname;
+{
+ char tbuf[sizeof(_PATH_LOCKDIRNAME) + MAXNAMLEN];
+
+ (void)sprintf(tbuf, _PATH_LOCKDIRNAME, ttyname);
+ return(unlink(tbuf));
+}
diff --git a/usr.bin/tip/value.c b/usr.bin/tip/value.c
new file mode 100644
index 0000000..ce29a21
--- /dev/null
+++ b/usr.bin/tip/value.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)value.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+
+#define MIDDLE 35
+
+static value_t *vlookup();
+static int col = 0;
+
+/*
+ * Variable manipulation
+ */
+vinit()
+{
+ register value_t *p;
+ register char *cp;
+ FILE *f;
+ char file[256];
+
+ for (p = vtable; p->v_name != NULL; p++) {
+ if (p->v_type&ENVIRON)
+ if (cp = getenv(p->v_name))
+ p->v_value = cp;
+ if (p->v_type&IREMOTE)
+ number(p->v_value) = *address(p->v_value);
+ }
+ /*
+ * Read the .tiprc file in the HOME directory
+ * for sets
+ */
+ strcpy(file, value(HOME));
+ strcat(file, "/.tiprc");
+ if ((f = fopen(file, "r")) != NULL) {
+ register char *tp;
+
+ while (fgets(file, sizeof(file)-1, f) != NULL) {
+ if (vflag)
+ printf("set %s", file);
+ if (tp = rindex(file, '\n'))
+ *tp = '\0';
+ vlex(file);
+ }
+ fclose(f);
+ }
+ /*
+ * To allow definition of exception prior to fork
+ */
+ vtable[EXCEPTIONS].v_access &= ~(WRITE<<PUBLIC);
+}
+
+static int vaccess();
+
+/*VARARGS1*/
+vassign(p, v)
+ register value_t *p;
+ char *v;
+{
+
+ if (!vaccess(p->v_access, WRITE)) {
+ printf("access denied\r\n");
+ return;
+ }
+ switch (p->v_type&TMASK) {
+
+ case STRING:
+ if (p->v_value && equal(p->v_value, v))
+ return;
+ if (!(p->v_type&(ENVIRON|INIT)))
+ free(p->v_value);
+ if ((p->v_value = malloc(size(v)+1)) == NOSTR) {
+ printf("out of core\r\n");
+ return;
+ }
+ p->v_type &= ~(ENVIRON|INIT);
+ strcpy(p->v_value, v);
+ break;
+
+ case NUMBER:
+ if (number(p->v_value) == number(v))
+ return;
+ number(p->v_value) = number(v);
+ break;
+
+ case BOOL:
+ if (boolean(p->v_value) == (*v != '!'))
+ return;
+ boolean(p->v_value) = (*v != '!');
+ break;
+
+ case CHAR:
+ if (character(p->v_value) == *v)
+ return;
+ character(p->v_value) = *v;
+ }
+ p->v_access |= CHANGED;
+}
+
+static void vprint();
+
+vlex(s)
+ register char *s;
+{
+ register value_t *p;
+ static void vtoken();
+
+ if (equal(s, "all")) {
+ for (p = vtable; p->v_name; p++)
+ if (vaccess(p->v_access, READ))
+ vprint(p);
+ } else {
+ register char *cp;
+
+ do {
+ if (cp = vinterp(s, ' '))
+ cp++;
+ vtoken(s);
+ s = cp;
+ } while (s);
+ }
+ if (col > 0) {
+ printf("\r\n");
+ col = 0;
+ }
+}
+
+static void
+vtoken(s)
+ register char *s;
+{
+ register value_t *p;
+ register char *cp;
+ char *expand();
+
+ if (cp = index(s, '=')) {
+ *cp = '\0';
+ if (p = vlookup(s)) {
+ cp++;
+ if (p->v_type&NUMBER)
+ vassign(p, atoi(cp));
+ else {
+ if (strcmp(s, "record") == 0)
+ cp = expand(cp);
+ vassign(p, cp);
+ }
+ return;
+ }
+ } else if (cp = index(s, '?')) {
+ *cp = '\0';
+ if ((p = vlookup(s)) && vaccess(p->v_access, READ)) {
+ vprint(p);
+ return;
+ }
+ } else {
+ if (*s != '!')
+ p = vlookup(s);
+ else
+ p = vlookup(s+1);
+ if (p != NOVAL) {
+ vassign(p, s);
+ return;
+ }
+ }
+ printf("%s: unknown variable\r\n", s);
+}
+
+static void
+vprint(p)
+ register value_t *p;
+{
+ register char *cp;
+ extern char *interp(), *ctrl();
+
+ if (col > 0 && col < MIDDLE)
+ while (col++ < MIDDLE)
+ putchar(' ');
+ col += size(p->v_name);
+ switch (p->v_type&TMASK) {
+
+ case BOOL:
+ if (boolean(p->v_value) == FALSE) {
+ col++;
+ putchar('!');
+ }
+ printf("%s", p->v_name);
+ break;
+
+ case STRING:
+ printf("%s=", p->v_name);
+ col++;
+ if (p->v_value) {
+ cp = interp(p->v_value, NULL);
+ col += size(cp);
+ printf("%s", cp);
+ }
+ break;
+
+ case NUMBER:
+ col += 6;
+ printf("%s=%-5d", p->v_name, number(p->v_value));
+ break;
+
+ case CHAR:
+ printf("%s=", p->v_name);
+ col++;
+ if (p->v_value) {
+ cp = ctrl(character(p->v_value));
+ col += size(cp);
+ printf("%s", cp);
+ }
+ break;
+ }
+ if (col >= MIDDLE) {
+ col = 0;
+ printf("\r\n");
+ return;
+ }
+}
+
+
+static int
+vaccess(mode, rw)
+ register unsigned mode, rw;
+{
+ if (mode & (rw<<PUBLIC))
+ return (1);
+ if (mode & (rw<<PRIVATE))
+ return (1);
+ return ((mode & (rw<<ROOT)) && getuid() == 0);
+}
+
+static value_t *
+vlookup(s)
+ register char *s;
+{
+ register value_t *p;
+
+ for (p = vtable; p->v_name; p++)
+ if (equal(p->v_name, s) || (p->v_abrev && equal(p->v_abrev, s)))
+ return (p);
+ return (NULL);
+}
+
+char *
+vinterp(s, stop)
+ register char *s;
+ char stop;
+{
+ register char *p = s, c;
+ int num;
+
+ while ((c = *s++) && c != stop)
+ switch (c) {
+
+ case '^':
+ if (*s)
+ *p++ = *s++ - 0100;
+ else
+ *p++ = c;
+ break;
+
+ case '\\':
+ num = 0;
+ c = *s++;
+ if (c >= '0' && c <= '7')
+ num = (num<<3)+(c-'0');
+ else {
+ register char *q = "n\nr\rt\tb\bf\f";
+
+ for (; *q; q++)
+ if (c == *q++) {
+ *p++ = *q;
+ goto cont;
+ }
+ *p++ = c;
+ cont:
+ break;
+ }
+ if ((c = *s++) >= '0' && c <= '7') {
+ num = (num<<3)+(c-'0');
+ if ((c = *s++) >= '0' && c <= '7')
+ num = (num<<3)+(c-'0');
+ else
+ s--;
+ } else
+ s--;
+ *p++ = num;
+ break;
+
+ default:
+ *p++ = c;
+ }
+ *p = '\0';
+ return (c == stop ? s-1 : NULL);
+}
+
+/*
+ * assign variable s with value v (for NUMBER or STRING or CHAR types)
+ */
+
+vstring(s,v)
+ register char *s;
+ register char *v;
+{
+ register value_t *p;
+ char *expand();
+
+ p = vlookup(s);
+ if (p == 0)
+ return (1);
+ if (p->v_type&NUMBER)
+ vassign(p, atoi(v));
+ else {
+ if (strcmp(s, "record") == 0)
+ v = expand(v);
+ vassign(p, v);
+ }
+ return (0);
+}
diff --git a/usr.bin/tip/vars.c b/usr.bin/tip/vars.c
new file mode 100644
index 0000000..debe01b
--- /dev/null
+++ b/usr.bin/tip/vars.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vars.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tip.h"
+#include "pathnames.h"
+
+/*
+ * Definition of variables
+ */
+value_t vtable[] = {
+ { "beautify", BOOL, (READ|WRITE)<<PUBLIC,
+ "be", (char *)TRUE },
+ { "baudrate", NUMBER|IREMOTE|INIT, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "ba", (char *)&BR },
+ { "dialtimeout",NUMBER, (READ<<PUBLIC)|(WRITE<<ROOT),
+ "dial", (char *)60 },
+ { "eofread", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofr", (char *)&IE },
+ { "eofwrite", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "eofw", (char *)&OE },
+ { "eol", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ NOSTR, (char *)&EL },
+ { "escape", CHAR, (READ|WRITE)<<PUBLIC,
+ "es", (char *)'~' },
+ { "exceptions", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "ex", (char *)&EX },
+ { "force", CHAR, (READ|WRITE)<<PUBLIC,
+ "fo", (char *)CTRL('p') },
+ { "framesize", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "fr", (char *)&FS },
+ { "host", STRING|IREMOTE|INIT, READ<<PUBLIC,
+ "ho", (char *)&HO },
+ { "log", STRING|INIT, (READ|WRITE)<<ROOT,
+ NOSTR, _PATH_ACULOG },
+ { "phones", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&PH },
+ { "prompt", CHAR, (READ|WRITE)<<PUBLIC,
+ "pr", (char *)'\n' },
+ { "raise", BOOL, (READ|WRITE)<<PUBLIC,
+ "ra", (char *)FALSE },
+ { "raisechar", CHAR, (READ|WRITE)<<PUBLIC,
+ "rc", (char *)CTRL('a') },
+ { "record", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "rec", (char *)&RE },
+ { "remote", STRING|INIT|IREMOTE, READ<<PUBLIC,
+ NOSTR, (char *)&RM },
+ { "script", BOOL, (READ|WRITE)<<PUBLIC,
+ "sc", (char *)FALSE },
+ { "tabexpand", BOOL, (READ|WRITE)<<PUBLIC,
+ "tab", (char *)FALSE },
+ { "verbose", BOOL, (READ|WRITE)<<PUBLIC,
+ "verb", (char *)TRUE },
+ { "SHELL", STRING|ENVIRON|INIT, (READ|WRITE)<<PUBLIC,
+ NULL, _PATH_BSHELL },
+ { "HOME", STRING|ENVIRON, (READ|WRITE)<<PUBLIC,
+ NOSTR, NOSTR },
+ { "echocheck", BOOL, (READ|WRITE)<<PUBLIC,
+ "ec", (char *)FALSE },
+ { "disconnect", STRING|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "di", (char *)&DI },
+ { "tandem", BOOL, (READ|WRITE)<<PUBLIC,
+ "ta", (char *)TRUE },
+ { "linedelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "ldelay", (char *)&DL },
+ { "chardelay", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "cdelay", (char *)&CL },
+ { "etimeout", NUMBER|IREMOTE|INIT, (READ|WRITE)<<PUBLIC,
+ "et", (char *)&ET },
+ { "rawftp", BOOL, (READ|WRITE)<<PUBLIC,
+ "raw", (char *)FALSE },
+ { "halfduplex", BOOL, (READ|WRITE)<<PUBLIC,
+ "hdx", (char *)FALSE },
+ { "localecho", BOOL, (READ|WRITE)<<PUBLIC,
+ "le", (char *)FALSE },
+ { "parity", STRING|INIT|IREMOTE, (READ|WRITE)<<PUBLIC,
+ "par", (char *)&PA },
+ { NOSTR, NULL, NULL, NOSTR, NOSTR }
+};
diff --git a/usr.bin/tn3270/Makefile b/usr.bin/tn3270/Makefile
new file mode 100644
index 0000000..2997c41
--- /dev/null
+++ b/usr.bin/tn3270/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+.if !make(install) && !make(distribute)
+# Build tools first so that things don't get built using stale tools and
+# then built again after the tools are freshened.
+#
+# XXX this doesn't fix the problem if the tools are built by running make
+# in the tn3270 subdir, because the Makefile doesn't give the full
+# dependencies of the tools.
+#
+# XXX this doesn't fix the problem for `make depend' either.
+SUBDIR= tools
+.endif
+
+SUBDIR+=tn3270 mset
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/tn3270/Makefile.inc b/usr.bin/tn3270/Makefile.inc
new file mode 100644
index 0000000..bd2446e
--- /dev/null
+++ b/usr.bin/tn3270/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+
+KBD= unix.kbd
+CFLAGS+=-DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO -DTN3270
diff --git a/usr.bin/tn3270/api/api_bsd.c b/usr.bin/tn3270/api/api_bsd.c
new file mode 100644
index 0000000..46db372
--- /dev/null
+++ b/usr.bin/tn3270/api/api_bsd.c
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)api_bsd.c 8.2 (Berkeley) 1/7/94";
+#endif /* not lint */
+
+#if defined(unix)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#include "../ctlr/api.h"
+#include "api_exch.h"
+
+
+int
+api_close_api()
+{
+ if (api_exch_outcommand(EXCH_CMD_DISASSOCIATE) == -1) {
+ return -1;
+ } else if (api_exch_flush() == -1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+int
+api_open_api(string)
+char *string; /* if non-zero, where to connect to */
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+ struct storage_descriptor sd;
+ extern char *getenv();
+ char thehostname[100];
+ char keyname[100];
+ char inkey[100];
+ FILE *keyfile;
+ int sock;
+ unsigned int port;
+ int i;
+
+ if (string == 0) {
+ string = getenv("API3270"); /* Get API */
+ if (string == 0) {
+ fprintf(stderr,
+ "API3270 environmental variable not set - no API.\n");
+ return -1; /* Nothing */
+ }
+ }
+
+ if (sscanf(string, "%[^:]:%d:%s", thehostname,
+ (int *)&port, keyname) != 3) {
+ fprintf(stderr, "API3270 environmental variable has bad format.\n");
+ return -1;
+ }
+ /* Now, try to connect */
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("opening API socket");
+ return -1;
+ }
+ server.sin_family = AF_INET;
+ hp = gethostbyname(thehostname);
+ if (hp == 0) {
+ fprintf(stderr, "%s specifies bad host name.\n", string);
+ return -1;
+ }
+ bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
+ server.sin_port = htons(port);
+
+ if (connect(sock, (struct sockaddr *)&server, sizeof server) < 0) {
+ perror("connecting to API server");
+ return -1;
+ }
+ /* Now, try application level connection */
+ if (api_exch_init(sock, "client") == -1) {
+ return -1;
+ }
+ if (api_exch_outcommand(EXCH_CMD_ASSOCIATE) == -1) {
+ return -1;
+ }
+ keyfile = fopen(keyname, "r");
+ if (keyfile == 0) {
+ perror("fopen");
+ return -1;
+ }
+ if (fscanf(keyfile, "%s\n", inkey) != 1) {
+ perror("fscanf");
+ return -1;
+ }
+ sd.length = strlen(inkey)+1;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, sd.length, inkey) == -1) {
+ return -1;
+ }
+ while ((i = api_exch_nextcommand()) != EXCH_CMD_ASSOCIATED) {
+ int passwd_length;
+ char *passwd, *getpass();
+ char buffer[200];
+
+ switch (i) {
+ case EXCH_CMD_REJECTED:
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC,
+ sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+ fprintf(stderr, "%s\n", buffer);
+ if (api_exch_outcommand(EXCH_CMD_ASSOCIATE) == -1) {
+ return -1;
+ }
+ break;
+ case EXCH_CMD_SEND_AUTH:
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+ passwd = getpass(buffer); /* Go to terminal */
+ passwd_length = strlen(passwd);
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+ if (sd.length) {
+ char *ptr;
+
+ ptr = passwd;
+ i = 0;
+ while (*ptr) {
+ *ptr++ ^= buffer[i++];
+ if (i >= sd.length) {
+ i = 0;
+ }
+ }
+ }
+ sd.length = passwd_length;
+ if (api_exch_outcommand(EXCH_CMD_AUTH) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, passwd_length, passwd) == -1) {
+ return -1;
+ }
+ break;
+ case -1:
+ return -1;
+ default:
+ fprintf(stderr,
+ "Waiting for connection indicator, received 0x%x.\n", i);
+ break;
+ }
+ }
+ /* YEAH */
+ return 0; /* Happiness! */
+}
+
+
+api_exch_api(regs, sregs, parms, length)
+union REGS *regs;
+struct SREGS *sregs;
+char *parms;
+int length;
+{
+ struct storage_descriptor sd;
+ int i;
+
+ if (api_exch_outcommand(EXCH_CMD_REQUEST) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_REGS, sizeof *regs, (char *)regs) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof *sregs, (char *)sregs) == -1) {
+ return -1;
+ }
+ sd.length = length;
+ sd.location = (long) parms;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, length, parms) == -1) {
+ return -1;
+ }
+ while ((i = api_exch_nextcommand()) != EXCH_CMD_REPLY) {
+ switch (i) {
+ case EXCH_CMD_GIMME:
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ /*XXX validity check GIMME? */
+ if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, sd.length,
+ (char *)sd.location) == -1) {
+ return -1;
+ }
+ break;
+ case EXCH_CMD_HEREIS:
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ /* XXX Validty check HEREIS? */
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length,
+ (char *)sd.location) == -1) {
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Waiting for reply command, we got command %d.\n",
+ i);
+ return -1;
+ }
+ }
+ if (api_exch_intype(EXCH_TYPE_REGS, sizeof *regs, (char *)regs) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_SREGS, sizeof *sregs, (char *)sregs) == -1) {
+ return -1;
+ }
+ /* YEAH */
+ return 0; /* Happiness! */
+}
+
+#endif /* unix */
diff --git a/usr.bin/tn3270/api/api_exch.c b/usr.bin/tn3270/api/api_exch.c
new file mode 100644
index 0000000..a4626b9
--- /dev/null
+++ b/usr.bin/tn3270/api/api_exch.c
@@ -0,0 +1,429 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)api_exch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "../general/general.h"
+
+#include "api_exch.h"
+
+static int sock; /* Socket number */
+
+static char whoarewe[40] = "";
+#define WHO_ARE_WE() fprintf(stderr, "(API %s) ", whoarewe);
+
+static enum {CONTENTION, SEND, RECEIVE } conversation;
+
+static struct exch_exch exch_state;
+
+static unsigned int
+ my_sequence,
+ your_sequence;
+
+static char ibuffer[4000], *ibuf_next, *ibuf_last;
+#define IBUFADDED(i) ibuf_last += (i)
+#define IBUFAVAILABLE() (ibuf_last-ibuf_next)
+#define IBUFFER() ibuffer
+#define IBUFFREE() (ibuffer+sizeof ibuffer-ibuf_last-1)
+#define IBUFGETBYTES(w,l) { memcpy(w, ibuf_next, l); ibuf_next += l; }
+#define IBUFRESET() (ibuf_next = ibuf_last = ibuffer)
+
+char obuffer[4000], *obuf_next;
+#define OBUFADDBYTES(w,l) { memcpy(obuf_next, w, l); obuf_next += l; }
+#define OBUFAVAILABLE() (obuf_next - obuffer)
+#define OBUFFER() obuffer
+#define OBUFRESET() obuf_next = obuffer
+#define OBUFROOM() (obuffer+sizeof obuffer-obuf_next)
+
+
+static int
+outflush()
+{
+ int length = OBUFAVAILABLE();
+
+ if (length != 0) {
+ if (write(sock, OBUFFER(), length) != length) {
+ WHO_ARE_WE();
+ perror("write");
+ return -1;
+ }
+ OBUFRESET();
+ }
+ return 0; /* All OK */
+}
+
+
+static int
+iget(location, length)
+char *location;
+int length;
+{
+ int count;
+
+ if (OBUFAVAILABLE()) {
+ if (outflush() == -1) {
+ return -1;
+ }
+ }
+ if ((count = IBUFAVAILABLE()) != 0) {
+ if (count > length) {
+ count = length;
+ }
+ IBUFGETBYTES(location, count);
+ length -= count;
+ location += count;
+ }
+ while (length) {
+ if (ibuf_next == ibuf_last) {
+ IBUFRESET();
+ }
+ if ((count = read(sock, IBUFFER(), IBUFFREE())) < 0) {
+ WHO_ARE_WE();
+ perror("read");
+ return -1;
+ }
+ if (count == 0) {
+ /* Reading past end-of-file */
+ WHO_ARE_WE();
+ fprintf(stderr, "End of file read\r\n");
+ return -1;
+ }
+ IBUFADDED(count);
+ if (count > length) {
+ count = length;
+ }
+ IBUFGETBYTES(location, count);
+ length -= count;
+ location += count;
+ }
+ return 0;
+}
+
+static char *
+exch_to_ascii(exch)
+int exch; /* opcode to decode */
+{
+ switch (exch) {
+ case EXCH_EXCH_COMMAND:
+ return "Command";
+ case EXCH_EXCH_TYPE:
+ return "Type";
+ case EXCH_EXCH_TURNAROUND:
+ return "Turnaround";
+ case EXCH_EXCH_RTS:
+ return "Request to Send";
+ default:
+ {
+ static char unknown[40];
+
+ sprintf(unknown, "(Unknown exchange 0x%02x)", exch&0xff);
+ return unknown;
+ }
+ }
+}
+
+/*
+ * Send the exch structure, updating the sequnce number field.
+ */
+
+static int
+send_state()
+{
+ if (OBUFROOM() < sizeof exch_state) {
+ if (outflush() == -1) {
+ return -1;
+ }
+ }
+ my_sequence = (my_sequence+1)&0xff;
+ exch_state.my_sequence = my_sequence;
+ exch_state.your_sequence = your_sequence;
+ OBUFADDBYTES((char *)&exch_state, sizeof exch_state);
+ return 0;
+}
+
+/*
+ * Receive the exch structure from the other side, checking
+ * sequence numbering.
+ */
+
+static int
+receive_state()
+{
+ if (iget((char *)&exch_state, sizeof exch_state) == -1) {
+ return -1;
+ }
+ if (conversation != CONTENTION) {
+ if (exch_state.your_sequence != my_sequence) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Send sequence number mismatch.\n");
+ return -1;
+ }
+ if (exch_state.my_sequence != ((++your_sequence)&0xff)) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Receive sequence number mismatch.\n");
+ return -1;
+ }
+ }
+ your_sequence = exch_state.my_sequence;
+ return 0;
+}
+
+static int
+enter_receive()
+{
+ switch (conversation) {
+ case CONTENTION:
+ exch_state.opcode = EXCH_EXCH_TURNAROUND;
+ if (send_state() == -1) {
+ return -1;
+ }
+ if (receive_state() == -1) {
+ return -1;
+ }
+ if (exch_state.opcode != EXCH_EXCH_RTS) {
+ WHO_ARE_WE();
+ fprintf(stderr, "In CONTENTION state: ");
+ if (exch_state.opcode == EXCH_EXCH_TURNAROUND) {
+ fprintf(stderr,
+ "Both sides tried to enter RECEIVE state.\n");
+ } else {
+ fprintf(stderr,
+ "Protocol error trying to enter RECEIVE state.\n");
+ }
+ return -1;
+ }
+ break;
+ case SEND:
+ exch_state.opcode = EXCH_EXCH_TURNAROUND;
+ if (send_state() == -1) {
+ return -1;
+ }
+ break;
+ }
+ conversation = RECEIVE;
+ return 0;
+}
+
+static int
+enter_send()
+{
+ switch (conversation) {
+ case CONTENTION:
+ exch_state.opcode = EXCH_EXCH_RTS;
+ if (send_state() == -1) {
+ return -1;
+ }
+ /* fall through */
+ case RECEIVE:
+ if (receive_state() == -1) {
+ return -1;
+ }
+ if (exch_state.opcode != EXCH_EXCH_TURNAROUND) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Conversation error - both sides in SEND state.\n");
+ return -1;
+ }
+ }
+ conversation = SEND;
+ return 0;
+}
+
+int
+api_exch_nextcommand()
+{
+ if (conversation != RECEIVE) {
+ if (enter_receive() == -1) {
+ return -1;
+ }
+ }
+ if (receive_state() == -1) {
+ return -1;
+ }
+ if (exch_state.opcode != EXCH_EXCH_COMMAND) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Expected a %s exchange, received a %s exchange.\n",
+ exch_to_ascii(EXCH_EXCH_COMMAND), exch_to_ascii(exch_state.opcode));
+ return -1;
+ }
+ return exch_state.command_or_type;
+}
+
+
+int
+api_exch_incommand(command)
+int command;
+{
+ int i;
+
+ if ((i = api_exch_nextcommand()) == -1) {
+ return -1;
+ }
+ if (i != command) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Expected API command 0x%x, got API command 0x%x.\n",
+ command, i);
+ return -1;
+ }
+ return 0;
+}
+
+
+int
+api_exch_outcommand(command)
+int command;
+{
+ if (conversation != SEND) {
+ if (enter_send() == -1) {
+ return -1;
+ }
+ }
+ exch_state.command_or_type = command;
+ exch_state.opcode = EXCH_EXCH_COMMAND;
+ if (send_state() == -1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+int
+api_exch_outtype(type, length, location)
+int
+ type,
+ length;
+char
+ *location;
+{
+ int netleng = length;
+
+ if (conversation != SEND) {
+ if (enter_send() == -1) {
+ return -1;
+ }
+ }
+ exch_state.opcode = EXCH_EXCH_TYPE;
+ exch_state.command_or_type = type;
+ exch_state.length = netleng;
+ if (send_state() == -1) {
+ return -1;
+ }
+ if (length) {
+ if (OBUFROOM() > length) {
+ OBUFADDBYTES(location, length);
+ } else {
+ if (outflush() == -1) {
+ return -1;
+ }
+ if (write(sock, location, length) != length) {
+ WHO_ARE_WE();
+ perror("write");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+int
+api_exch_intype(type, length, location)
+int
+ type,
+ length;
+char
+ *location;
+{
+ int netleng = length;
+
+ if (conversation != RECEIVE) {
+ if (enter_receive() == -1) {
+ return -1;
+ }
+ }
+ if (receive_state() == -1) {
+ return -1;
+ }
+ if (exch_state.opcode != EXCH_EXCH_TYPE) {
+ WHO_ARE_WE();
+ fprintf(stderr,
+ "Expected to receive a %s exchange, received a %s exchange.\n",
+ exch_to_ascii(EXCH_EXCH_TYPE), exch_to_ascii(exch_state.opcode));
+ return -1;
+ }
+ if (exch_state.command_or_type != type) {
+ WHO_ARE_WE();
+ fprintf(stderr, "Expected type 0x%x, got type 0x%x.\n",
+ type, exch_state.command_or_type);
+ return -1;
+ }
+ if (exch_state.length != netleng) {
+ fprintf(stderr, "Type 0x%x - expected length %d, received length %u.\n",
+ type, length, exch_state.length);
+ return -1;
+ }
+ if (iget(location, length) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+api_exch_flush()
+{
+ return outflush();
+}
+
+int
+api_exch_init(sock_number, ourname)
+int sock_number;
+char *ourname;
+{
+ extern char *strcpy();
+
+ sock = sock_number;
+ (void) strcpy(whoarewe, ourname); /* For error messages */
+
+ my_sequence = your_sequence = 0;
+
+ conversation = CONTENTION; /* We don't know which direction */
+
+ IBUFRESET();
+ OBUFRESET();
+
+ return 0;
+}
diff --git a/usr.bin/tn3270/api/api_exch.h b/usr.bin/tn3270/api/api_exch.h
new file mode 100644
index 0000000..24e6691
--- /dev/null
+++ b/usr.bin/tn3270/api/api_exch.h
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)api_exch.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This file describes the structures passed back and forth
+ * between the API client and API server on a Unix-based
+ * tn3270 implementation.
+ */
+
+/*
+ * The following are the low-level opcodes exchanged between the
+ * two sides. These are designed to allow for type, sequence number,
+ * and direction checking.
+ *
+ * We enforce conversation flow. There are three states: CONTENTION,
+ * SEND, and RECEIVE. Both sides start in CONTENTION.
+ * We never leave RECEIVE state without first reading a TURNAROUND
+ * opcode. We never leave SEND state without first writing a TURNAROUND
+ * opcode. This scheme ensures that we always have conversation flowing
+ * in a synchronized direction (or detect an application error), and that
+ * we never hang with both sides trying to read from the "wire".
+ *
+ * State event action
+ *
+ * CONTENTION read request send TURNAROUND
+ * read RTS
+ * enter RECEIVE
+ * CONTENTION write request send RTS
+ * read TURNAROUND
+ * enter SEND
+ *
+ * RECEIVE read request read whatever
+ * RECEIVE write request read TURNAROUND
+ *
+ * SEND read request send TURNAROUND
+ * SEND write write whatever
+ */
+
+#define EXCH_EXCH_COMMAND 0 /* The following is a command */
+#define EXCH_EXCH_TURNAROUND 1 /* Your turn to send */
+#define EXCH_EXCH_RTS 2 /* Request to send */
+#define EXCH_EXCH_TYPE 3 /* The following is a type */
+
+struct exch_exch {
+ char
+ opcode; /* COMMAND, TURNAROUND, or TYPE */
+ unsigned char
+ my_sequence, /* 0-ff, initially zero */
+ your_sequence, /* 0-ff, initially zero */
+ command_or_type; /* Application level command or type */
+ unsigned short
+ length; /* The length of any following data */
+};
+
+/*
+ * The following are the command codes which the higher level protocols
+ * send and receive.
+ */
+
+#define EXCH_CMD_ASSOCIATE 0 /* Connect [client->server] */
+ /*
+ * struct storage_desc
+ * char key[]
+ */
+#define EXCH_CMD_DISASSOCIATE 1 /* Disconnect [client->server] */
+#define EXCH_CMD_SEND_AUTH 2 /* Send password [server->client] */
+ /*
+ * struct storage_desc
+ * char prompt[]
+ * struct storage_desc
+ * char seed[]
+ */
+#define EXCH_CMD_AUTH 3 /* Authorization [client->server] */
+ /*
+ * struct storage_desc
+ * char authenticator[]
+ */
+#define EXCH_CMD_ASSOCIATED 4 /* Connected [server->client] */
+#define EXCH_CMD_REJECTED 5 /* Too bad [server->client] */
+ /*
+ * struct storage_desc
+ * char message[]
+ */
+
+#define EXCH_CMD_REQUEST 6 /* A request [client->server] */
+ /* struct regs,
+ * struct sregs,
+ * struct storage_desc
+ * char bytes[]
+ */
+#define EXCH_CMD_GIMME 7 /* Send storage [server->client] */
+ /*
+ * struct storage_desc
+ */
+#define EXCH_CMD_HEREIS 8 /* Here is storage [BOTH WAYS] */
+ /*
+ * struct storage_desc
+ * char bytes[]
+ */
+#define EXCH_CMD_REPLY 9 /* End of discussion */
+ /*
+ * struct regs,
+ * struct sregs,
+ */
+
+/*
+ * The following are typed parameters sent across the wire.
+ *
+ * This should be done much more generally, with some form of
+ * XDR or mapped conversation ability.
+ */
+
+#define EXCH_TYPE_REGS 0
+#define EXCH_TYPE_SREGS 1
+#define EXCH_TYPE_STORE_DESC 2
+#define EXCH_TYPE_BYTES 3
+
+/*
+ * each parameter that comes over looks like:
+ *
+ * char type of following
+ * short (2 bytes) length of following (network byte order)
+ * following
+ */
+
+struct storage_descriptor {
+ long location; /* In network byte order */
+ short length; /* In network byte order */
+};
diff --git a/usr.bin/tn3270/api/apilib.c b/usr.bin/tn3270/api/apilib.c
new file mode 100644
index 0000000..3146170
--- /dev/null
+++ b/usr.bin/tn3270/api/apilib.c
@@ -0,0 +1,411 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)apilib.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "../ctlr/api.h"
+
+#include "apilib.h"
+
+int
+ api_sup_errno = 0, /* Supervisor error number */
+ api_sup_fcn_id = 0, /* Supervisor function id (0x12) */
+ api_fcn_errno = 0, /* Function error number */
+ api_fcn_fcn_id = 0; /* Function ID (0x6b, etc.) */
+
+static int
+ gate_sessmgr = 0,
+ gate_keyboard = 0,
+ gate_copy = 0,
+ gate_oiam = 0;
+
+/*
+ * Issue an API request, with reg structures supplied by the caller.
+ *
+ * Only certain routines need this (supervisor services come to mind).
+ */
+
+static int
+api_issue_regs(ah, al, bh, bl, cx, dx, parms, length, regs, sregs)
+int ah, al, bh, bl, cx, dx;
+char *parms;
+int length;
+union REGS *regs;
+struct SREGS *sregs;
+{
+ char far *ourseg = parms;
+
+ regs->h.ah = ah;
+ regs->h.al = al;
+ regs->h.bh = bh;
+ regs->h.bl = bl;
+ regs->x.cx = cx;
+ regs->x.dx = dx;
+ sregs->es = FP_SEG(ourseg);
+ regs->x.di = FP_OFF(ourseg);
+
+#if defined(MSDOS)
+ int86x(API_INTERRUPT_NUMBER, regs, regs, sregs);
+#endif /* defined(MSDOS) */
+#if defined(unix)
+ api_exch_api(regs, sregs, parms, length);
+#endif /* defined(unix) */
+
+ if (regs->h.cl != 0) {
+ api_sup_errno = regs->h.cl;
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * Issue an API request without requiring caller to supply
+ * registers. Most routines use this.
+ */
+
+static int
+api_issue(ah, al, bh, bl, cx, dx, parms, length)
+int
+ ah,
+ al,
+ bh,
+ bl,
+ cx,
+ dx;
+char *parms;
+int length; /* Length of parms */
+{
+ union REGS regs;
+ struct SREGS sregs;
+
+ return api_issue_regs(ah, al, bh, bl, cx, dx, parms, length, &regs, &sregs);
+}
+
+/*
+ * Supervisor Services
+ */
+
+int
+api_name_resolve(name)
+char *name;
+{
+ NameResolveParms parms;
+ int i;
+ union REGS regs;
+ struct SREGS sregs;
+
+ for (i = 0; i < sizeof parms.gate_name; i++) {
+ if (*name) {
+ parms.gate_name[i] = *name++;
+ } else {
+ parms.gate_name[i] = ' ';
+ }
+ }
+
+ if (api_issue_regs(NAME_RESOLUTION, 0, 0, 0, 0, 0, (char *) &parms,
+ sizeof parms, &regs, &sregs) == -1) {
+ return -1;
+ } else {
+ return regs.x.dx;
+ }
+}
+
+#if defined(unix)
+/*
+ * Block until the oia or ps is modified.
+ */
+
+int
+api_ps_or_oia_modified()
+{
+ union REGS regs;
+ struct SREGS sregs;
+
+ if (api_issue_regs(PS_OR_OIA_MODIFIED, 0, 0, 0, 0, 0, (char *) 0,
+ 0, &regs, &sregs) == -1) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+#endif /* defined(unix) */
+
+/*
+ * Session Information Services
+ */
+
+api_query_session_id(parms)
+QuerySessionIdParms *parms;
+{
+ if (api_issue(0x09, QUERY_SESSION_ID, 0x80, 0x20, 0,
+ gate_sessmgr, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+
+api_query_session_parameters(parms)
+QuerySessionParametersParms *parms;
+{
+ if (api_issue(0x09, QUERY_SESSION_PARAMETERS, 0x80, 0x20, 0,
+ gate_sessmgr, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+api_query_session_cursor(parms)
+QuerySessionCursorParms *parms;
+{
+ if (api_issue(0x09, QUERY_SESSION_CURSOR, 0x80, 0x20, 0xff,
+ gate_sessmgr, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+/*
+ * Keyboard Services
+ */
+
+api_connect_to_keyboard(parms)
+ConnectToKeyboardParms *parms;
+{
+ if (api_issue(0x09, CONNECT_TO_KEYBOARD, 0x80, 0x20, 0,
+ gate_keyboard, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+
+api_disconnect_from_keyboard(parms)
+DisconnectFromKeyboardParms *parms;
+{
+ if (api_issue(0x09, DISCONNECT_FROM_KEYBOARD, 0x80, 0x20, 0,
+ gate_keyboard, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+
+api_write_keystroke(parms)
+WriteKeystrokeParms *parms;
+{
+ if (api_issue(0x09, WRITE_KEYSTROKE, 0x80, 0x20, 0,
+ gate_keyboard, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+
+api_disable_input(parms)
+DisableInputParms *parms;
+{
+ if (api_issue(0x09, DISABLE_INPUT, 0x80, 0x20, 0,
+ gate_keyboard, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+api_enable_input(parms)
+EnableInputParms *parms;
+{
+ if (api_issue(0x09, ENABLE_INPUT, 0x80, 0x20, 0,
+ gate_keyboard, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+/*
+ * Copy Services
+ */
+
+api_copy_string(parms)
+CopyStringParms *parms;
+{
+ if (api_issue(0x09, COPY_STRING, 0x80, 0x20, 0xff,
+ gate_copy, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+/*
+ * Operator Information Area Services
+ */
+
+api_read_oia_group(parms)
+ReadOiaGroupParms *parms;
+{
+ if (api_issue(0x09, READ_OIA_GROUP, 0x80, 0x20, 0xff,
+ gate_oiam, (char *)parms, sizeof *parms) == -1) {
+ api_fcn_errno = 0;
+ api_fcn_fcn_id = 0;
+ return -1;
+ } else if (parms->rc == 0) {
+ return 0;
+ } else {
+ api_fcn_errno = parms->rc;
+ api_fcn_fcn_id = parms->function_id;
+ return -1;
+ }
+}
+
+/*
+ * The "we are done" routine. This gets called last.
+ */
+
+api_finish()
+{
+#if defined(unix)
+ if (api_close_api() == -1) {
+ return -1;
+ } else {
+ return 0;
+ }
+#endif /* defined(unix) */
+}
+
+
+/*
+ * The initialization routine. Be sure to call this first.
+ */
+
+api_init()
+{
+#if defined(MSDOS)
+ union REGS regs;
+ struct SREGS sregs;
+
+ regs.h.ah = 0x35;
+ regs.h.al = API_INTERRUPT_NUMBER;
+ intdosx(&regs, &regs, &sregs);
+
+ if ((regs.x.bx == 0) && (sregs.es == 0)) {
+ return 0; /* Interrupt not being handled */
+ }
+#endif /* defined(MSDOS) */
+#if defined(unix)
+ if (api_open_api((char *)0) == -1) {
+ return 0;
+ }
+#endif /* defined(unix) */
+
+ gate_sessmgr = api_name_resolve("SESSMGR");
+ gate_keyboard = api_name_resolve("KEYBOARD");
+ gate_copy = api_name_resolve("COPY");
+ gate_oiam = api_name_resolve("OIAM");
+
+ if ((gate_sessmgr == gate_keyboard) ||
+ (gate_sessmgr == gate_copy) ||
+ (gate_sessmgr == gate_oiam) ||
+ (gate_keyboard == gate_copy) ||
+ (gate_keyboard == gate_oiam) ||
+ (gate_copy == gate_oiam)) {
+ return 0; /* Interrupt doesn't seem correct */
+ }
+ return 1;
+}
diff --git a/usr.bin/tn3270/api/apilib.h b/usr.bin/tn3270/api/apilib.h
new file mode 100644
index 0000000..3253e39
--- /dev/null
+++ b/usr.bin/tn3270/api/apilib.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)apilib.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * What one needs to specify
+ */
+
+extern int
+ api_sup_errno, /* Supervisor error number */
+ api_sup_fcn_id, /* Supervisor function id (0x12) */
+ api_fcn_errno, /* Function error number */
+ api_fcn_fcn_id; /* Function ID (0x6b, etc.) */
diff --git a/usr.bin/tn3270/api/asc_ebc.c b/usr.bin/tn3270/api/asc_ebc.c
new file mode 100644
index 0000000..1e999dc
--- /dev/null
+++ b/usr.bin/tn3270/api/asc_ebc.c
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)asc_ebc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Ascii<->Ebcdic translation tables.
+ */
+
+#include "asc_ebc.h"
+
+unsigned char asc_ebc[NASCII] = {
+
+/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 20 */ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
+/* 28 */ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+/* 30 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+/* 38 */ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+/* 40 */ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+/* 48 */ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+/* 50 */ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
+/* 58 */ 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
+/* 60 */ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+/* 68 */ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+/* 70 */ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+/* 78 */ 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x00,
+
+};
+
+/*
+ * ebcdic to ascii translation tables
+ */
+
+unsigned char ebc_asc[NEBC] = {
+/* 00 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 08 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 10 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 18 */ ' ', ' ', ' ', ' ', '*', ' ', ';', ' ',
+/* 20 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 28 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 30 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 38 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 40 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+
+/* 48 */ ' ', ' ',
+#if !defined(MSDOS)
+ /* 4A */ '\\',
+#else /* !defined(MSDOS) */
+ /* 4A */ '\233', /* PC cent sign */
+#endif /* !defined(MSDOS) */
+ /* 4B */ '.', '<', '(', '+', '|',
+
+/* 50 */ '&', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 58 */ ' ', ' ', '!', '$', '*', ')', ';', '^',
+/* 60 */ '-', '/', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 68 */ ' ', ' ', '|', ',', '%', '_', '>', '?',
+/* 70 */ ' ', '^', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 78 */ ' ', '`', ':', '#', '@', '\'', '=', '"',
+/* 80 */ ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 88 */ 'h', 'i', ' ', ' ', ' ', ' ', ' ', ' ',
+/* 90 */ ' ', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+/* 98 */ 'q', 'r', ' ', ' ', ' ', ' ', ' ', ' ',
+/* A0 */ ' ', '~', 's', 't', 'u', 'v', 'w', 'x',
+/* A8 */ 'y', 'z', ' ', ' ', ' ', '[', ' ', ' ',
+/* B0 */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+/* B8 */ ' ', ' ', ' ', ' ', ' ', ']', ' ', ' ',
+/* C0 */ '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+/* C8 */ 'H', 'I', ' ', ' ', ' ', ' ', ' ', ' ',
+/* D0 */ '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+/* D8 */ 'Q', 'R', ' ', ' ', ' ', ' ', ' ', ' ',
+/* E0 */ '\\', ' ', 'S', 'T', 'U', 'V', 'W', 'X',
+/* E8 */ 'Y', 'Z', ' ', ' ', ' ', ' ', ' ', ' ',
+/* F0 */ '0', '1', '2', '3', '4', '5', '6', '7',
+/* F8 */ '8', '9', ' ', ' ', ' ', ' ', ' ', ' ',
+};
diff --git a/usr.bin/tn3270/api/asc_ebc.h b/usr.bin/tn3270/api/asc_ebc.h
new file mode 100644
index 0000000..17c0488
--- /dev/null
+++ b/usr.bin/tn3270/api/asc_ebc.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)asc_ebc.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Definitions of translate tables used for ascii<->ebcdic translation.
+ */
+
+#define INCLUDED_ASCEBC
+
+/*
+ * ascii/ebcdic translation information
+ */
+
+#define NASCII 128 /* number of ascii characters */
+
+#define NEBC 256 /* number of ebcdic characters */
+
+extern unsigned char
+ asc_ebc[NASCII], ebc_asc[NEBC];
diff --git a/usr.bin/tn3270/api/astosc.c b/usr.bin/tn3270/api/astosc.c
new file mode 100644
index 0000000..ff641d6
--- /dev/null
+++ b/usr.bin/tn3270/api/astosc.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)astosc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+
+#include "../general/general.h"
+
+#include "../ctlr/function.h"
+
+#include "astosc.h"
+
+struct astosc astosc[256] = {
+#include "astosc.out"
+};
+
+/* compare two strings, ignoring case */
+
+static
+ustrcmp(string1, string2)
+register char *string1;
+register char *string2;
+{
+ register int c1, c2;
+
+ while ((c1 = (unsigned char) *string1++) != 0) {
+ if (isupper(c1)) {
+ c1 = tolower(c1);
+ }
+ if (isupper(c2 = (unsigned char) *string2++)) {
+ c2 = tolower(c2);
+ }
+ if (c1 < c2) {
+ return(-1);
+ } else if (c1 > c2) {
+ return(1);
+ }
+ }
+ if (*string2) {
+ return(-1);
+ } else {
+ return(0);
+ }
+}
+
+
+/*
+ * This routine takes a string and returns an integer. It may return
+ * -1 if there is no other integer which corresponds to the
+ * string. -1 implies an error.
+ */
+
+int
+ascii_to_index(string)
+register char *string;
+{
+ register struct astosc *this;
+
+ for (this = astosc; this <= &astosc[highestof(astosc)]; this++) {
+ if ((this->name != 0) && (ustrcmp(this->name, string) == 0)) {
+ return this-astosc;
+ }
+ }
+ return -1;
+}
diff --git a/usr.bin/tn3270/api/astosc.h b/usr.bin/tn3270/api/astosc.h
new file mode 100644
index 0000000..bbafd5f
--- /dev/null
+++ b/usr.bin/tn3270/api/astosc.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)astosc.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This defines the structure used to translate:
+ *
+ * ascii name ==> (scancode, shiftstate)
+ *
+ * (Actually, map3270 does "ascii name ==> index", and
+ * termin does "index ==> (scancode, shiftstate)". Both
+ * mappings use this structure.)
+ */
+
+#define INCLUDED_ASTOSC
+
+struct astosc {
+ unsigned char
+ scancode, /* Scan code for this function */
+ shiftstate; /* Shift state for this function */
+ enum ctlrfcn function; /* Internal function identifier */
+ char *name; /* Name of this function */
+};
+
+int ascii_to_index(); /* Function to feed InitControl() */
+
+extern struct astosc astosc[256];
diff --git a/usr.bin/tn3270/api/dctype.c b/usr.bin/tn3270/api/dctype.c
new file mode 100644
index 0000000..1fd8a90
--- /dev/null
+++ b/usr.bin/tn3270/api/dctype.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dctype.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "dctype.h"
+
+unsigned char dctype[192] = {
+/*00*/
+ D_SPACE,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+/*10*/
+ D_SPACE,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ 0,
+ 0,
+ 0,
+ 0,
+/*20*/
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ D_DIGIT|D_PRINT,
+ 0,
+ 0,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+/*30*/
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+ 0,
+ 0,
+ 0,
+ 0,
+ D_PUNCT|D_PRINT,
+ 0,
+ D_PUNCT|D_PRINT,
+ 0,
+ 0,
+/*40*/
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+/*50*/
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+/*60*/
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+/*70*/
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+/*80*/
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+/*90*/
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ D_LOWER|D_PRINT,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+/*A0*/
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+/*B0*/
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ D_UPPER|D_PRINT,
+ 0,
+ 0,
+ 0,
+ 0,
+ D_PUNCT|D_PRINT,
+ D_PUNCT|D_PRINT,
+};
diff --git a/usr.bin/tn3270/api/dctype.h b/usr.bin/tn3270/api/dctype.h
new file mode 100644
index 0000000..2b0c068
--- /dev/null
+++ b/usr.bin/tn3270/api/dctype.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dctype.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_ECTYPE
+
+#define D_UPPER 0x01
+#define D_LOWER 0x02
+#define D_DIGIT 0x04
+#define D_SPACE 0x08
+#define D_PUNCT 0x10
+#define D_PRINT 0x20
+
+#define Disalpha(c) (dctype[(c)]&(D_UPPER|D_LOWER))
+#define Disupper(c) (dctype[(c)]&D_UPPER)
+#define Dislower(c) (dctype[(c)]&D_LOWER)
+#define Disdigit(c) (dctype[(c)]&D_DIGIT)
+#define Disalnum(c) (dctype[(c)]&(D_UPPER|D_LOWER|D_DIGIT))
+#define Disspace(c) (dctype[(c)]&D_SPACE) /* blank or null */
+#define Dispunct(c) (dctype[(c)]&D_PUNCT)
+#define Disprint(c) (dctype[(c)]&D_PRINT)
+
+extern unsigned char dctype[192];
diff --git a/usr.bin/tn3270/api/disp_asc.c b/usr.bin/tn3270/api/disp_asc.c
new file mode 100644
index 0000000..85ba106
--- /dev/null
+++ b/usr.bin/tn3270/api/disp_asc.c
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)disp_asc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * There isn't much excuse for this file, but here it is.
+ */
+
+#include "disp_asc.h"
+
+#include "asc_disp.out"
+#include "disp_asc.out"
diff --git a/usr.bin/tn3270/api/disp_asc.h b/usr.bin/tn3270/api/disp_asc.h
new file mode 100644
index 0000000..5abe92b
--- /dev/null
+++ b/usr.bin/tn3270/api/disp_asc.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)disp_asc.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Define the translate tables used to go between 3270 display code
+ * and ascii
+ */
+
+extern unsigned char
+ disp_asc[256], /* Goes between display code and ascii */
+ asc_disp[256]; /* Goes between ascii and display code */
diff --git a/usr.bin/tn3270/api/ebc_disp.c b/usr.bin/tn3270/api/ebc_disp.c
new file mode 100644
index 0000000..a601099
--- /dev/null
+++ b/usr.bin/tn3270/api/ebc_disp.c
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ebc_disp.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Translate table to map EBCDIC into 3270 display codes.
+ */
+
+unsigned char ebc_disp[256] = {
+/*00*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*08*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*10*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*18*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*20*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*28*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*30*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*38*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/*40*/ 0x10, 0x0a, 0x0b, 0x1c, 0x1d, 0x1e, 0x1f, 0x2a,
+/*48*/ 0x2b, 0x37, 0x1b, 0x32, 0x09, 0x0d, 0x35, 0x16,
+/*50*/ 0x30, 0x38, 0x39, 0x3a, 0x3c, 0x3e, 0x3f, 0x40,
+/*58*/ 0x41, 0x42, 0x19, 0x1a, 0xbf, 0x0c, 0xbe, 0x36,
+/*60*/ 0x31, 0x14, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+/*68*/ 0x49, 0x4a, 0x17, 0x33, 0x2e, 0x2f, 0x08, 0x18,
+/*70*/ 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
+/*78*/ 0x53, 0x3d, 0x34, 0x2c, 0x2d, 0x12, 0x11, 0x13,
+/*80*/ 0x54, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
+/*88*/ 0x87, 0x88, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
+/*90*/ 0x5b, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+/*98*/ 0x90, 0x91, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61,
+/*A0*/ 0x62, 0x3b, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+/*A8*/ 0x98, 0x99, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+/*B0*/ 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+/*B8*/ 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+/*C0*/ 0x0f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+/*C8*/ 0xa7, 0xa8, 0x79, 0x7a, 0x7b, 0x7c, 0x01, 0x02,
+/*D0*/ 0x0e, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+/*D8*/ 0xb0, 0xb1, 0x7d, 0x7e, 0x7f, 0x03, 0x04, 0x05,
+/*E0*/ 0x15, 0x9a, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+/*E8*/ 0xb8, 0xb9, 0x9b, 0x9c, 0x9d, 0x06, 0x07, 0x9e,
+/*F0*/ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+/*F8*/ 0x28, 0x29, 0xba, 0xbb, 0xbc, 0xbd, 0x9f, 0x00,
+};
+
+/*
+ * Translate table to map 3270 display codes to EBCDIC.
+ */
+
+unsigned char disp_ebc[192] = {
+/*00*/ 0x00, 0xce, 0xcf, 0xdd, 0xde, 0xdf, 0xed, 0xee,
+/*08*/ 0x6e, 0x4c, 0x41, 0x42, 0x5d, 0x4d, 0xd0, 0xc0,
+/*10*/ 0x40, 0x7e, 0x7d, 0x7f, 0x61, 0xe0, 0x4f, 0x6a,
+/*18*/ 0x6f, 0x5a, 0x5b, 0x4a, 0x43, 0x44, 0x45, 0x46,
+/*20*/ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+/*28*/ 0xf8, 0xf9, 0x47, 0x48, 0x7b, 0x7c, 0x6c, 0x6d,
+/*30*/ 0x50, 0x60, 0x4b, 0x6b, 0x7a, 0x4e, 0x5f, 0x49,
+/*38*/ 0x51, 0x52, 0x53, 0xa1, 0x54, 0x79, 0x55, 0x56,
+/*40*/ 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66,
+/*48*/ 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74,
+/*50*/ 0x75, 0x76, 0x77, 0x78, 0x80, 0x8a, 0x8b, 0x8c,
+/*58*/ 0x8d, 0x8e, 0x8f, 0x90, 0x9a, 0x9b, 0x9c, 0x9d,
+/*60*/ 0x9e, 0x9f, 0xa0, 0xaa, 0xab, 0xac, 0xad, 0xae,
+/*68*/ 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+/*70*/ 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe,
+/*78*/ 0xbf, 0xca, 0xcb, 0xcc, 0xcd, 0xda, 0xdb, 0xdc,
+/*80*/ 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+/*88*/ 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+/*90*/ 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+/*98*/ 0xa8, 0xa9, 0xe1, 0xea, 0xeb, 0xec, 0xef, 0xfe,
+/*A0*/ 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+/*A8*/ 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+/*B0*/ 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+/*B8*/ 0xe8, 0xe9, 0xfa, 0xfb, 0xfc, 0xfd, 0x5e, 0x5c,
+};
diff --git a/usr.bin/tn3270/api/ebc_disp.h b/usr.bin/tn3270/api/ebc_disp.h
new file mode 100644
index 0000000..201c39d
--- /dev/null
+++ b/usr.bin/tn3270/api/ebc_disp.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ebc_disp.h 8.1 (Berkeley) 6/6/93
+ */
+
+extern unsigned char
+ ebc_disp[256],
+ disp_ebc[192];
diff --git a/usr.bin/tn3270/ascii/default.map b/usr.bin/tn3270/ascii/default.map
new file mode 100644
index 0000000..a4cf6e1
--- /dev/null
+++ b/usr.bin/tn3270/ascii/default.map
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)default.map 8.1 (Berkeley) 6/6/93
+ */
+
+/* default.map3270: This file is the system default for the key sequence
+ * if neither the user's TERM nor "unknown" are found in either of
+ * MAP3270 or /etc/map3270.
+ *
+ *
+ */
+#if defined(MSDOS)
+"tn3270pc{",
+" ENTER='^M';CLEAR='^Z'|'^Aw';NL='^N'|'^AO';TAB='^I';DP='^U';FM='^Y';",
+" BTAB='^B'|'^[^I'|'^A^O';LEFT='^H'|'^AK';RIGHT='^L'|'^AM';UP='^K'|'^AH';",
+" DOWN='^J'|'^AP';HOME='^^'|'^AG';DELETE='^AS'|'^D';EINP='^W';FLINP='^X';",
+" EEOF='^E'|'^Au';WERASE='^As';FERASE='^At';INSRT='^[ '|'^AR';CURSEL='^[.';",
+" PFK1='^A;'|'^F01'|'^[1'|'^Ax';PFK2='^A<'|'^F02'|'^[2'|'^Ay';SETTAB='^[;';",
+" PFK3='^A='|'^F03'|'^[3'|'^Az';CLRTAB='^[+'|'^[:';SETMRG='^[(';",
+" PFK4='^A>'|'^F04'|'^[4'|'^A{';PFK5='^A?'|'^F05'|'^[5'|'^A|';",
+" PFK6='^A@'|'^F06'|'^[6'|'^A}';PFK7='^AA'|'^AI'|'^F07'|'^[7'|'^A~';",
+" PFK8='^AB'|'^AQ'|'^F08'|'^[8'|'^A^?';PFK9='^AC'|'^F09'|'^[9'|'^A^A^@';",
+" PFK10='^AD'|'^F10'|'^[0'|'^A^A^A';SETHOM='^[!';COLTAB='^[i'|'^[I';",
+" COLBAK='^[b'|'^[B';INDENT='^[l'|'^[L';UNDENT='^[h'|'^[H';",
+" PFK11='^AT'|'^F11'|'^[-'|'^A^A^B';PFK12='^AU'|'^F12'|'^A^A^C'|'^[=';",
+" PFK13='^AV'|'^F13';PFK14='^AW'|'^F14';PFK15='^AX'|'^F15';",
+" PFK16='^AY'|'^F16';",
+" PFK17='^AZ'|'^F17';PFK18='^A['|'^F18';PFK19='^A\\\\'|'^F19';",
+" PFK20='^A]'|'^F20';PFK21='^A\\^'|'^F21';PFK22='^A_'|'^F22';PA3='^Aj'|'^P3';",
+" PFK23='^A`'|'^F23';PFK24='^Aa'|'^F24';PA1='^Ah'|'^P1';PA2='^Ai'|'^P2';",
+" RESET='^T'|'^R'; ",
+" MASTER_RESET='^G';RESHOW='^V';DELTAB='^[\\\'';ESCAPE='^C';",
+"}",
+#else /* defined(MSDOS) */
+"generic { clear = '^z'; flinp = '^x'; enter = '^m'; delete = '^d' | '^?';",
+" synch = '^r'; reshow = '^v'; eeof = '^e'; tab = '^i';",
+" btab = '^b'; nl = '^n'; left = '^h'; right = '^l';",
+" up = '^k'; down = '^j'; einp = '^w'; reset = '^t';",
+" xoff = '^s'; xon = '^q'; escape = '^c'; ferase = '^u';",
+" insrt = '\\E ';",
+" pa1 = '^p1'; pa2 = '^p2'; pa3 = '^p3';",
+" pfk1 = '\\E1'; pfk2 = '\\E2'; pfk3 = '\\E3'; pfk4 = '\\E4';",
+" pfk5 = '\\E5'; pfk6 = '\\E6'; pfk7 = '\\E7'; pfk8 = '\\E8';",
+" pfk9 = '\\E9'; pfk10 = '\\E0'; pfk11 = '\\E-'; pfk12 = '\\E=';",
+" pfk13 = '\\E!'; pfk14 = '\\E@'; pfk15 = '\\E#'; pfk16 = '\\E$';",
+" pfk17 = '\\E%'; pfk18 = '\\E\\^'; pfk19 = '\\E&'; pfk20 = '\\E*';",
+" pfk21 = '\\E('; pfk22 = '\\E)'; pfk23 = '\\E_'; pfk24 = '\\E+';",
+"}",
+#endif /* defined(MSDOS) */
diff --git a/usr.bin/tn3270/ascii/map3270.c b/usr.bin/tn3270/ascii/map3270.c
new file mode 100644
index 0000000..9ae1b2a
--- /dev/null
+++ b/usr.bin/tn3270/ascii/map3270.c
@@ -0,0 +1,934 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)map3270.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* This program reads a description file, somewhat like /etc/termcap,
+ that describes the mapping between the current terminal's keyboard and
+ a 3270 keyboard.
+ */
+#ifdef DOCUMENTATION_ONLY
+/* here is a sample (very small) entry...
+
+ # this table is sensitive to position on a line. In particular,
+ # a terminal definition for a terminal is terminated whenever a
+ # (non-comment) line beginning in column one is found.
+ #
+ # this is an entry to map tvi924 to 3270 keys...
+ v8|tvi924|924|televideo model 924 {
+ pfk1 = '\E1';
+ pfk2 = '\E2';
+ clear = '^z'; # clear the screen
+ }
+ */
+#endif /* DOCUMENTATION_ONLY */
+
+#include <stdio.h>
+#include <ctype.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+
+#define IsPrint(c) ((isprint(c) && !isspace(c)) || ((c) == ' '))
+
+#include "state.h"
+#include "map3270.h"
+
+#include "../general/globals.h"
+
+/* this is the list of types returned by the lex processor */
+#define LEX_CHAR 400 /* plain unadorned character */
+#define LEX_ESCAPED LEX_CHAR+1 /* escaped with \ */
+#define LEX_CARETED LEX_ESCAPED+1 /* escaped with ^ */
+#define LEX_END_OF_FILE LEX_CARETED+1 /* end of file encountered */
+#define LEX_ILLEGAL LEX_END_OF_FILE+1 /* trailing escape character */
+
+/* the following is part of our character set dependancy... */
+#define ESCAPE 0x1b
+#define TAB 0x09
+#define NEWLINE 0x0a
+#define CARRIAGE_RETURN 0x0d
+
+typedef struct {
+ int type; /* LEX_* - type of character */
+ int value; /* character this was */
+} lexicon;
+
+typedef struct {
+ int length; /* length of character string */
+ char array[500]; /* character string */
+} stringWithLength;
+
+#define panic(s) { fprintf(stderr, s); exit(1); }
+
+static state firstentry = { 0, STATE_NULL, 0, 0 };
+static state *headOfQueue = &firstentry;
+
+/* the following is a primitive adm3a table, to be used when nothing
+ * else seems to be avaliable.
+ */
+
+#ifdef DEBUG
+static int debug = 0; /* debug flag (for debuggin tables) */
+#endif /* DEBUG */
+
+static int (*GetTc)();
+static int doPaste = 1; /* should we have side effects */
+static int picky = 0; /* do we complain of unknown functions? */
+static char usePointer = 0; /* use pointer, or file */
+static FILE *ourFile= 0;
+static char *environPointer = 0;/* if non-zero, point to input
+ * string in core.
+ */
+static char **whichkey = 0;
+static char *keysgeneric[] = {
+#include "default.map" /* Define the default default */
+
+ 0, /* Terminate list of entries */
+};
+ ;
+
+static int Empty = 1, /* is the unget lifo empty? */
+ Full = 0; /* is the unget lifo full? */
+static lexicon lifo[200] = { 0 }; /* character stack for parser */
+static int rp = 0, /* read pointer into lifo */
+ wp = 0; /* write pointer into lifo */
+
+static int
+GetC()
+{
+ int character;
+
+ if (usePointer) {
+ if ((*environPointer) == 0) {
+ /*
+ * If we have reached the end of this string, go on to
+ * the next (if there is a next).
+ */
+ if (whichkey == 0) {
+ static char suffix = 'A'; /* From environment */
+ char envname[9];
+ extern char *getenv();
+
+ (void) sprintf(envname, "MAP3270%c", suffix++);
+ environPointer = getenv(envname);
+ } else {
+ whichkey++; /* default map */
+ environPointer = *whichkey;
+ }
+ }
+ if (*environPointer) {
+ character = 0xff&*environPointer++;
+ } else {
+ character = EOF;
+ }
+ } else {
+ character = getc(ourFile);
+ }
+ return(character);
+}
+
+static lexicon
+Get()
+{
+ static lexicon c;
+ register lexicon *pC = &c;
+ register int character;
+
+ if (!Empty) {
+ *pC = lifo[rp];
+ rp++;
+ if (rp == sizeof lifo/sizeof (lexicon)) {
+ rp = 0;
+ }
+ if (rp == wp) {
+ Empty = 1;
+ }
+ Full = 0;
+ } else {
+ character = GetC();
+ switch (character) {
+ case EOF:
+ pC->type = LEX_END_OF_FILE;
+ break;
+ case '^':
+ character = GetC();
+ if (!IsPrint(character)) {
+ pC->type = LEX_ILLEGAL;
+ } else {
+ pC->type = LEX_CARETED;
+ if (character == '?') {
+ character |= 0x40; /* rubout */
+ } else {
+ character &= 0x1f;
+ }
+ }
+ break;
+ case '\\':
+ character = GetC();
+ if (!IsPrint(character)) {
+ pC->type = LEX_ILLEGAL;
+ } else {
+ pC->type = LEX_ESCAPED;
+ switch (character) {
+ case 'E': case 'e':
+ character = ESCAPE;
+ break;
+ case 't':
+ character = TAB;
+ break;
+ case 'n':
+ character = NEWLINE;
+ break;
+ case 'r':
+ character = CARRIAGE_RETURN;
+ break;
+ default:
+ pC->type = LEX_ILLEGAL;
+ break;
+ }
+ }
+ break;
+ default:
+ if ((IsPrint(character)) || isspace(character)) {
+ pC->type = LEX_CHAR;
+ } else {
+ pC->type = LEX_ILLEGAL;
+ }
+ break;
+ }
+ pC->value = character;
+ }
+ return(*pC);
+}
+
+static void
+UnGet(c)
+lexicon c; /* character to unget */
+{
+ if (Full) {
+ fprintf(stderr, "attempt to put too many characters in lifo\n");
+ panic("map3270");
+ /* NOTREACHED */
+ } else {
+ lifo[wp] = c;
+ wp++;
+ if (wp == sizeof lifo/sizeof (lexicon)) {
+ wp = 0;
+ }
+ if (wp == rp) {
+ Full = 1;
+ }
+ Empty = 0;
+ }
+}
+
+/*
+ * Construct a control character sequence
+ * for a special character.
+ */
+char *
+uncontrol(c)
+ register int c;
+{
+ static char buf[3];
+
+ if (c == 0x7f)
+ return ("^?");
+ if (c == '\377') {
+ return "-1";
+ }
+ if (c >= 0x20) {
+ buf[0] = c;
+ buf[1] = 0;
+ } else {
+ buf[0] = '^';
+ buf[1] = '@'+c;
+ buf[2] = 0;
+ }
+ return (buf);
+}
+
+/* compare two strings, ignoring case */
+
+ustrcmp(string1, string2)
+register char *string1;
+register char *string2;
+{
+ register int c1, c2;
+
+ while ((c1 = (unsigned char) *string1++) != 0) {
+ if (isupper(c1)) {
+ c1 = tolower(c1);
+ }
+ if (isupper(c2 = (unsigned char) *string2++)) {
+ c2 = tolower(c2);
+ }
+ if (c1 < c2) {
+ return(-1);
+ } else if (c1 > c2) {
+ return(1);
+ }
+ }
+ if (*string2) {
+ return(-1);
+ } else {
+ return(0);
+ }
+}
+
+
+static stringWithLength *
+GetQuotedString()
+{
+ lexicon lex, *lp;
+ static stringWithLength output = { 0 }; /* where return value is held */
+ char *pointer = output.array;
+
+ lex = Get();
+ if ((lex.type != LEX_CHAR) || (lex.value != '\'')) {
+ UnGet(lex);
+ return(0);
+ }
+ while (1) {
+ lex = Get();
+ if ((lex.type == LEX_CHAR) && (lex.value == '\'')) {
+ break;
+ }
+ lp = &lex;
+ if ((lp->type == LEX_CHAR) && !IsPrint(lp->value)) {
+ UnGet(lex);
+ return(0); /* illegal character in quoted string */
+ }
+ if (pointer >= output.array+sizeof output.array) {
+ return(0); /* too long */
+ }
+ *pointer++ = lex.value;
+ }
+ output.length = pointer-output.array;
+ return(&output);
+}
+
+#ifdef NOTUSED
+static stringWithLength *
+GetCharString()
+{
+ lexicon lex;
+ static stringWithLength output;
+ char *pointer = output.array;
+
+ lex = Get();
+
+ while ((lex.type == LEX_CHAR) &&
+ !isspace(lex.value) && (lex.value != '=')) {
+ *pointer++ = lex.value;
+ lex = Get();
+ if (pointer >= output.array + sizeof output.array) {
+ return(0); /* too long */
+ }
+ }
+ UnGet(lex);
+ output.length = pointer-output.array;
+ return(&output);
+}
+#endif /* NOTUSED */
+
+static
+GetCharacter(character)
+int character; /* desired character */
+{
+ lexicon lex;
+
+ lex = Get();
+
+ if ((lex.type != LEX_CHAR) || (lex.value != character)) {
+ UnGet(lex);
+ return(0);
+ }
+ return(1);
+}
+
+#ifdef NOTUSED
+static
+GetString(string)
+char *string; /* string to get */
+{
+ lexicon lex;
+
+ while (*string) {
+ lex = Get();
+ if ((lex.type != LEX_CHAR) || (lex.value != *string&0xff)) {
+ UnGet(lex);
+ return(0); /* XXX restore to state on entry */
+ }
+ string++;
+ }
+ return(1);
+}
+#endif /* NOTUSED */
+
+
+static stringWithLength *
+GetAlphaMericString()
+{
+ lexicon lex, *lp;
+ static stringWithLength output = { 0 };
+ char *pointer = output.array;
+# define IsAlnum(c) (isalnum(c) || (c == '_') \
+ || (c == '-') || (c == '.'))
+
+ lex = Get();
+ lp = &lex;
+
+ if ((lp->type != LEX_CHAR) || !IsAlnum(lp->value)) {
+ UnGet(lex);
+ return(0);
+ }
+
+ while ((lp->type == LEX_CHAR) && IsAlnum(lp->value)) {
+ *pointer++ = lex.value;
+ lex = Get();
+ }
+ UnGet(lex);
+ *pointer = 0;
+ output.length = pointer-output.array;
+ return(&output);
+}
+
+
+/* eat up characters until a new line, or end of file. returns terminating
+ character.
+ */
+
+static lexicon
+EatToNL()
+{
+ lexicon lex;
+
+ lex = Get();
+
+ while (!((lex.type != LEX_ESCAPED) && (lex.type != LEX_CARETED) &&
+ (lex.value == '\n')) && (!(lex.type == LEX_END_OF_FILE))) {
+ lex = Get();
+ }
+ if (lex.type != LEX_END_OF_FILE) {
+ return(Get());
+ } else {
+ return(lex);
+ }
+}
+
+
+static void
+GetWS()
+{
+ lexicon lex, *lp;
+
+ lex = Get();
+ lp = &lex;
+
+ while ((lp->type == LEX_CHAR) &&
+ (isspace(lp->value) || (lp->value == '#'))) {
+ if (lex.value == '#') {
+ lex = EatToNL();
+ } else {
+ lex = Get();
+ }
+ }
+ UnGet(lex);
+}
+
+static void
+FreeState(pState)
+state *pState;
+{
+ extern int free();
+
+ free((char *)pState);
+}
+
+
+static state *
+GetState()
+{
+ state *pState;
+ extern char *malloc();
+
+ pState = (state *) malloc(sizeof (state));
+
+ pState->result = STATE_NULL;
+ pState->next = 0;
+
+ return(pState);
+}
+
+
+static state *
+FindMatchAtThisLevel(pState, character)
+state *pState;
+int character;
+{
+ while (pState) {
+ if (pState->match == character) {
+ return(pState);
+ }
+ pState = pState->next;
+ }
+ return(0);
+}
+
+
+static state *
+PasteEntry(head, string, count, identifier)
+state *head; /* points to who should point here... */
+char *string; /* which characters to paste */
+int count; /* number of character to do */
+char *identifier; /* for error messages */
+{
+ state *pState, *other;
+
+ if (!doPaste) { /* flag to not have any side effects */
+ return((state *)1);
+ }
+ if (!count) {
+ return(head); /* return pointer to the parent */
+ }
+ if ((head->result != STATE_NULL) && (head->result != STATE_GOTO)) {
+ /* this means that a previously defined sequence is an initial
+ * part of this one.
+ */
+ fprintf(stderr, "Conflicting entries found when scanning %s\n",
+ identifier);
+ return(0);
+ }
+# ifdef DEBUG
+ if (debug) {
+ fprintf(stderr, "%s", uncontrol(*string));
+ }
+# endif /* DEBUG */
+ pState = GetState();
+ pState->match = *string;
+ if (head->result == STATE_NULL) {
+ head->result = STATE_GOTO;
+ head->address = pState;
+ other = pState;
+ } else { /* search for same character */
+ if ((other = FindMatchAtThisLevel(head->address, *string)) != 0) {
+ FreeState(pState);
+ } else {
+ pState->next = head->address;
+ head->address = pState;
+ other = pState;
+ }
+ }
+ return(PasteEntry(other, string+1, count-1, identifier));
+}
+
+static
+GetInput(tc, identifier)
+int tc;
+char *identifier; /* entry being parsed (for error messages) */
+{
+ stringWithLength *outputString;
+ state *head;
+ state fakeQueue;
+
+ if (doPaste) {
+ head = headOfQueue; /* always points to level above this one */
+ } else {
+ head = &fakeQueue; /* don't have any side effects... */
+ }
+
+ if ((outputString = GetQuotedString()) == 0) {
+ return(0);
+ } else if (IsPrint(outputString->array[0])) {
+ fprintf(stderr,
+ "first character of sequence for %s is not a control type character\n",
+ identifier);
+ return(0);
+ } else {
+ if ((head = PasteEntry(head, outputString->array,
+ outputString->length, identifier)) == 0) {
+ return(0);
+ }
+ GetWS();
+ while ((outputString = GetQuotedString()) != 0) {
+ if ((head = PasteEntry(head, outputString->array,
+ outputString->length, identifier)) == 0) {
+ return(0);
+ }
+ GetWS();
+ }
+ }
+ if (!doPaste) {
+ return(1);
+ }
+ if ((head->result != STATE_NULL) && (head->result != tc)) {
+ /* this means that this sequence is an initial part
+ * of a previously defined one.
+ */
+ fprintf(stderr, "Conflicting entries found when scanning %s\n",
+ identifier);
+ return(0);
+ } else {
+ head->result = tc;
+ return(1); /* done */
+ }
+}
+
+static
+GetDefinition()
+{
+ stringWithLength *string;
+ int Tc;
+
+ GetWS();
+ if ((string = GetAlphaMericString()) == 0) {
+ return(0);
+ }
+ string->array[string->length] = 0;
+ if (doPaste) {
+ if ((Tc = (*GetTc)(string->array)) == -1) {
+ if (picky) {
+ fprintf(stderr, "%s: unknown 3270 key identifier\n",
+ string->array);
+ }
+ Tc = STATE_NULL;
+ }
+ } else {
+ Tc = STATE_NULL; /* XXX ? */
+ }
+ GetWS();
+ if (!GetCharacter('=')) {
+ fprintf(stderr,
+ "Required equal sign after 3270 key identifier %s missing\n",
+ string->array);
+ return(0);
+ }
+ GetWS();
+ if (!GetInput(Tc, string->array)) {
+ fprintf(stderr, "Missing definition part for 3270 key %s\n",
+ string->array);
+ return(0);
+ } else {
+ GetWS();
+ while (GetCharacter('|')) {
+# ifdef DEBUG
+ if (debug) {
+ fprintf(stderr, " or ");
+ }
+# endif /* DEBUG */
+ GetWS();
+ if (!GetInput(Tc, string->array)) {
+ fprintf(stderr, "Missing definition part for 3270 key %s\n",
+ string->array);
+ return(0);
+ }
+ GetWS();
+ }
+ }
+ GetWS();
+ if (!GetCharacter(';')) {
+ fprintf(stderr, "Missing semi-colon for 3270 key %s\n", string->array);
+ return(0);
+ }
+# ifdef DEBUG
+ if (debug) {
+ fprintf(stderr, ";\n");
+ }
+# endif /* DEBUG */
+ return(1);
+}
+
+
+static
+GetDefinitions()
+{
+ if (!GetDefinition()) {
+ return(0);
+ } else {
+ while (GetDefinition()) {
+ ;
+ }
+ }
+ return(1);
+}
+
+static
+GetBegin()
+{
+ GetWS();
+ if (!GetCharacter('{')) {
+ return(0);
+ }
+ return(1);
+}
+
+static
+GetEnd()
+{
+ GetWS();
+ if (!GetCharacter('}')) {
+ return(0);
+ }
+ return(1);
+}
+
+static
+GetName()
+{
+ if (!GetAlphaMericString()) {
+ return(0);
+ }
+ GetWS();
+ while (GetAlphaMericString()) {
+ GetWS();
+ }
+ return(1);
+}
+
+static
+GetNames()
+{
+ GetWS();
+ if (!GetName()) {
+ return(0);
+ } else {
+ GetWS();
+ while (GetCharacter('|')) {
+ GetWS();
+ if (!GetName()) {
+ return(0);
+ }
+ }
+ }
+ return(1);
+}
+
+static
+GetEntry0()
+{
+ if (!GetBegin()) {
+ fprintf(stderr, "no '{'\n");
+ return(0);
+ } else if (!GetDefinitions()) {
+ fprintf(stderr, "unable to parse the definitions\n");
+ return(0);
+ } else if (!GetEnd()) {
+ fprintf(stderr, "No '}' or scanning stopped early due to error.\n");
+ return(0);
+ } else {
+ /* done */
+ return(1);
+ }
+}
+
+
+static
+GetEntry()
+{
+ if (!GetNames()) {
+ fprintf(stderr, "Invalid name field in entry.\n");
+ return(0);
+ } else {
+ return(GetEntry0());
+ }
+}
+
+/* position ourselves within a given filename to the entry for the current
+ * KEYBD (or TERM) variable
+ */
+
+Position(filename, keybdPointer)
+char *filename;
+char *keybdPointer;
+{
+ lexicon lex;
+ stringWithLength *name = 0;
+ stringWithLength *oldName;
+# define Return(x) {doPaste = 1; return(x);}
+
+ doPaste = 0;
+
+ if ((ourFile = fopen(filename, "r")) == NULL) {
+# if !defined(MSDOS)
+ fprintf(stderr, "Unable to open file %s\n", filename);
+# endif /* !defined(MSDOS) */
+ Return(0);
+ }
+ lex = Get();
+ while (lex.type != LEX_END_OF_FILE) {
+ UnGet(lex);
+ /* now, find an entry that is our type. */
+ GetWS();
+ oldName = name;
+ if ((name = GetAlphaMericString()) != 0) {
+ if (!ustrcmp(name->array, keybdPointer)) {
+ /* need to make sure there is a name here... */
+ lex.type = LEX_CHAR;
+ lex.value = 'a';
+ UnGet(lex);
+ Return(1);
+ }
+ } else if (GetCharacter('|')) {
+ ; /* more names coming */
+ } else {
+ lex = Get();
+ UnGet(lex);
+ if (lex.type != LEX_END_OF_FILE) {
+ if (!GetEntry0()) { /* start of an entry */
+ fprintf(stderr,
+ "error was in entry for %s in file %s\n",
+ (oldName)? oldName->array:"(unknown)", filename);
+ Return(0);
+ }
+ }
+ }
+ lex = Get();
+ }
+#if !defined(MSDOS)
+ fprintf(stderr, "Unable to find entry for %s in file %s\n", keybdPointer,
+ filename);
+#endif /* !defined(MSDOS) */
+ Return(0);
+}
+
+char *
+strsave(string)
+char *string;
+{
+ char *p;
+ extern char *malloc();
+
+ p = malloc((unsigned int)strlen(string)+1);
+ if (p != 0) {
+ strcpy(p, string);
+ }
+ return(p);
+}
+
+
+/*
+ * InitControl - our interface to the outside. What we should
+ * do is figure out keyboard (or terminal) type, set up file pointer
+ * (or string pointer), etc.
+ */
+
+state *
+InitControl(keybdPointer, pickyarg, translator)
+char *keybdPointer;
+int pickyarg; /* Should we be picky? */
+int (*translator)(); /* Translates ascii string to integer */
+{
+ extern char *getenv();
+ int GotIt;
+
+ picky = pickyarg;
+ GetTc = translator;
+
+ if (keybdPointer == 0) {
+ keybdPointer = getenv("KEYBD");
+ }
+ if (keybdPointer == 0) {
+ keybdPointer = getenv("TERM");
+ }
+
+ /*
+ * Some environments have getenv() return
+ * out of a static area. So, save the keyboard name.
+ */
+ if (keybdPointer) {
+ keybdPointer = strsave(keybdPointer);
+ }
+ environPointer = getenv("MAP3270");
+ if (environPointer
+ && (environPointer[0] != '/')
+#if defined(MSDOS)
+ && (environPointer[0] != '\\')
+#endif /* defined(MSDOS) */
+ && (strncmp(keybdPointer, environPointer,
+ strlen(keybdPointer) != 0)
+ || (environPointer[strlen(keybdPointer)] != '{'))) /* } */
+ {
+ environPointer = 0;
+ }
+
+ if ((!environPointer)
+#if defined(MSDOS)
+ || (*environPointer == '\\')
+#endif /* defined(MSDOS) */
+ || (*environPointer == '/')) {
+ usePointer = 0;
+ GotIt = 0;
+ if (!keybdPointer) {
+#if !defined(MSDOS)
+ fprintf(stderr, "%s%s%s%s",
+ "Neither the KEYBD environment variable nor the TERM ",
+ "environment variable\n(one of which is needed to determine ",
+ "the type of keyboard you are using)\n",
+ "is set. To set it, say 'setenv KEYBD <type>'\n");
+#endif /* !defined(MSDOS) */
+ } else {
+ if (environPointer) {
+ GotIt = Position(environPointer, keybdPointer);
+ }
+ if (!GotIt) {
+ GotIt = Position("/etc/map3270", keybdPointer);
+ }
+ }
+ if (!GotIt) {
+ if (environPointer) {
+ GotIt = Position(environPointer, "unknown");
+ }
+ if (!GotIt) {
+ GotIt = Position("/etc/map3270", keybdPointer);
+ }
+ }
+ if (!GotIt) {
+#if !defined(MSDOS)
+ fprintf(stderr, "Using default key mappings.\n");
+#endif /* !defined(MSDOS) */
+ usePointer = 1; /* flag use of non-file */
+ whichkey = keysgeneric;
+ environPointer = *whichkey; /* use default table */
+ }
+ } else {
+ usePointer = 1;
+ }
+ (void) GetEntry();
+ return(firstentry.address);
+}
diff --git a/usr.bin/tn3270/ascii/map3270.h b/usr.bin/tn3270/ascii/map3270.h
new file mode 100644
index 0000000..f8d6e5c
--- /dev/null
+++ b/usr.bin/tn3270/ascii/map3270.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)map3270.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Declaration for map3270.c.
+ */
+
+extern state
+ *InitControl();
diff --git a/usr.bin/tn3270/ascii/mset.c b/usr.bin/tn3270/ascii/mset.c
new file mode 100644
index 0000000..b87a1ec
--- /dev/null
+++ b/usr.bin/tn3270/ascii/mset.c
@@ -0,0 +1,410 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mset.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * this program outputs the user's 3270 mapping table in a form suitable
+ * for inclusion in the environment. Typically, this might be used
+ * by:
+ * setenv MAP3270 "`mset`"
+ */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include "../ctlr/function.h"
+
+#include "state.h"
+#include "map3270.h"
+
+#include "../api/astosc.h"
+
+#include "../general/globals.h"
+
+struct regstate {
+ char *result;
+ char *match_start;
+ char *match_end; /* start of NEXT state's match string */
+ struct regstate *forward;
+ struct regstate *backward;
+};
+
+static struct regstate regstates[500], *rptr= 0; /* for sorting states */
+static char array[5000]; /* lot's of room */
+static int toshell = 0; /* export to shell */
+static int numbchars = 0; /* number of chars in envir. var */
+
+static int
+MyStrcmp(str1, str2)
+char *str1, *str2;
+{
+ if (strncmp(str1, "PFK", 3) == 0 && strncmp(str2, "PFK", 3) == 0
+ && strlen(str1) != strlen(str2)) {
+ return(strlen(str1) - strlen(str2));
+ }
+ return(strcmp(str1, str2));
+}
+
+static void
+forwRegister(regptr, sptr)
+struct regstate *regptr, *sptr;
+{
+
+ regptr->forward = sptr->forward;
+ regptr->backward = sptr;
+ (sptr->forward)->backward = regptr;
+ sptr->forward = regptr;
+}
+
+static void
+backRegister(regptr, sptr)
+struct regstate *regptr, *sptr;
+{
+
+ regptr->forward = sptr;
+ regptr->backward = sptr->backward;
+ (sptr->backward)->forward = regptr;
+ sptr->backward = regptr;
+}
+
+static struct regstate *
+doRegister(regptr)
+register struct regstate *regptr;
+{
+ static struct regstate *pivot = regstates;
+ register struct regstate *sptr = pivot;
+ int check;
+
+ if (pivot == regstates) { /* first time called */
+ pivot->forward = regptr;
+ regptr->backward = pivot++;
+ pivot->backward = regptr;
+ regptr->forward = pivot++;
+ return(++regptr);
+ }
+ if ((check = MyStrcmp(regptr->result, pivot->result)) < 0) {
+ while (check < 0) {
+ if (sptr->backward == regstates) {
+ backRegister(regptr, sptr);
+ pivot = pivot->backward;
+ return(++regptr);
+ }
+ sptr = sptr->backward;
+ check = MyStrcmp(regptr->result, sptr->result);
+ }
+ forwRegister(regptr, sptr);
+ pivot = pivot->backward;
+ return(++regptr);
+ }
+ while (check > 0) {
+ if ((sptr->forward)->result == 0) {
+ forwRegister(regptr, sptr);
+ pivot = pivot->forward;
+ return(++regptr);
+ }
+ sptr = sptr->forward;
+ check = MyStrcmp(regptr->result, sptr->result);
+ }
+ backRegister(regptr, sptr);
+ if (pivot->forward->result) {
+ pivot = pivot->forward;
+ }
+ return(++regptr);
+}
+
+static char *
+addString(strcount, character)
+int strcount;
+char character;
+{
+ static char *string = array;
+ int i;
+
+ if (rptr->match_start == 0) {
+ rptr->match_start = string;
+ for (i=0; i < strcount; i++) {
+ *string++ = *((rptr-1)->match_start+i);
+ }
+ }
+ *string++ = character;
+ return(string);
+}
+
+static char savename[20] = " "; /* for deciding if name is new */
+
+static void
+printString(string, begin, tc_name)
+register char *string;
+char *begin, *tc_name;
+{
+ register char *st1, *st2;
+ register int pchar;
+ static char suffix = 'A';
+ int new = strcmp(savename, tc_name);
+ char delim = new ? ';' : '|';
+ char *uncontrol();
+
+ st1 = begin;
+
+ numbchars += 5 + (new ? strlen(tc_name) : -1);
+ if (toshell && numbchars > 1011) {
+ new = 1;
+ delim = ';';
+ numbchars = 5 + strlen(tc_name);
+ printf(";\nsetenv MAP3270%c ", suffix++);
+ }
+ if (strcmp(" ", savename)) {
+ if (toshell) {
+ printf("%c%c", '\\', delim);
+ }
+ else {
+ printf("%c", delim);
+ }
+ }
+ else {
+ numbchars -= 2;
+ }
+ if (toshell && new) {
+ printf("%s=%c'", tc_name,'\\');
+ }
+ else if (new) {
+ printf("%s='", tc_name);
+ }
+ else if (toshell) {
+ printf("%c'", '\\');
+ }
+ else {
+ printf("'");
+ }
+ (void) strcpy(savename, tc_name);
+ while (st1 != string) {
+ if (toshell && numbchars >= 1016) { /* leave room for ctrl and delim */
+ numbchars = 0;
+ printf(";\nsetenv MAP3270%c ", suffix++);
+ }
+ pchar = 0xff&(*st1++);
+ switch (pchar) {
+ case '"':
+ case '!':
+ case '$':
+ case '(':
+ case ')':
+ case ' ':
+ case ';':
+ case '&':
+ case '|':
+ case '>':
+ case '<':
+ case '`':
+ case '#':
+ numbchars += 2;
+ if (toshell) {
+ printf("%c%c", '\\', pchar);
+ }
+ else {
+ printf("%c", pchar);
+ }
+ break;
+ case '\\':
+ case '\'':
+ numbchars += 4;
+ if (toshell) {
+ printf("%c%c%c%c", '\\', '\\', '\\', pchar);
+ }
+ else {
+ printf("%c%c", '\\', pchar);
+ }
+ break;
+ case '^':
+ numbchars += 3;
+ if (toshell) {
+ printf("%c%c%c", '\\', '\\', pchar);
+ }
+ else {
+ printf("%c%c", '\\', pchar);
+ }
+ break;
+ default:
+ st2 = uncontrol(pchar);
+ while ((pchar = *st2++) != 0) {
+ switch (pchar) {
+ case '"':
+ case '!':
+ case '$':
+ case '(':
+ case ')':
+ case ' ':
+ case ';':
+ case '&':
+ case '|':
+ case '>':
+ case '<':
+ case '`':
+ case '#':
+ case '\\':
+ case '\'':
+ if (toshell) {
+ numbchars += 2;
+ printf("%c%c", '\\', pchar);
+ }
+ else {
+ printf("%c", pchar);
+ }
+ break;
+ default:
+ numbchars++;
+ printf("%c", pchar);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ numbchars += 2;
+ if (toshell) {
+ printf("%c'", '\\');
+ }
+ else {
+ printf("'");
+ }
+}
+
+static void
+recurse(strcount, head)
+state *head;
+int strcount;
+{
+ /* if there is a left,
+ * recurse on left,
+ * if there is no down,
+ * print the string to here
+ * else,
+ * add the current match to the string,
+ * recurse.
+ * exit.
+ */
+
+ if (head->next) {
+ recurse(strcount, head->next);
+ }
+ if (head->result != STATE_GOTO) {
+ rptr->match_end = addString(strcount, head->match);
+ rptr->result = astosc[head->result].name;
+ rptr = doRegister(rptr);
+ } else {
+ (void) addString(strcount, head->match);
+ recurse(strcount+1, head->address);
+ strcount--;
+ }
+ return;
+}
+
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ state *head;
+ char *keybdPointer = (char *) 0;
+ char *commandName = argv[0];
+ extern char *getenv();
+ int picky = 0;
+
+ while ((argc > 1) && (argv[1][0] == '-')) {
+ if (!strcmp(argv[1], "-picky")) {
+ picky++;
+ } else if (!strcmp(argv[1], "-shell")) {
+ toshell++;
+ } else {
+ fprintf(stderr, "usage: %s [-picky] [-shell] [keyboardname]\n",
+ commandName);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ argv++;
+ argc--;
+ }
+ if (argc == 2) {
+ keybdPointer = argv[1];
+ } else if (argc > 2) {
+ fprintf(stderr, "usage: %s [-picky] [-shell] [keyboardname]\n",
+ commandName);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ head = InitControl(keybdPointer, picky, ascii_to_index);
+ if (!head) {
+ return(1);
+ }
+ if (keybdPointer == 0) {
+ keybdPointer = getenv("KEYBD");
+ }
+ if (keybdPointer == 0) {
+ keybdPointer = getenv("TERM");
+ }
+ if (keybdPointer == 0) {
+ keybdPointer = "3a"; /* use 3a as the terminal */
+ }
+ if (toshell) {
+ printf("set noglob;\nsetenv MAP3270 ");
+ }
+ printf("%s{", keybdPointer);
+ numbchars = 2 + strlen(keybdPointer);
+ /* now, run through the table registering entries */
+ rptr = regstates + 2;
+ recurse(0, head);
+ /* now print them out */
+ for (rptr = regstates[0].forward; rptr->result != 0;
+ rptr = rptr->forward) {
+ printString(rptr->match_end, rptr->match_start, rptr->result);
+ }
+ if (toshell) {
+ printf("%c;};\nunset noglob;\n", '\\');
+ }
+ else {
+ printf(";}\n");
+ }
+ return(0);
+}
diff --git a/usr.bin/tn3270/ascii/state.h b/usr.bin/tn3270/ascii/state.h
new file mode 100644
index 0000000..562522a
--- /dev/null
+++ b/usr.bin/tn3270/ascii/state.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)state.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_STATE
+
+/* this defines the state structure used by the key mapping routines */
+
+
+#define STATE_NULL -1 /* Falls off edge */
+#define STATE_GOTO -2 /* GOTO internal state */
+
+#define state struct State
+struct State {
+ int match; /* character to match */
+ int result; /* 3270 control code */
+ state *next; /* next entry in this same state */
+ state *address; /* if goto, where is next state */
+};
diff --git a/usr.bin/tn3270/ascii/termin.c b/usr.bin/tn3270/ascii/termin.c
new file mode 100644
index 0000000..a783189
--- /dev/null
+++ b/usr.bin/tn3270/ascii/termin.c
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)termin.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* this takes characters from the keyboard, and produces 3270 keystroke
+ codes
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "../general/general.h"
+#include "../ctlr/function.h"
+#include "../ctlr/externs.h"
+#include "../ctlr/declare.h"
+
+#include "../api/astosc.h"
+#include "state.h"
+
+#include "../general/globals.h"
+
+#define IsControl(c) (!isprint(c) || (isspace(c) && ((c) != ' ')))
+
+#define NextState(x) (x->next)
+
+/* XXX temporary - hard code in the state table */
+
+#define MATCH_ANY 0xff /* actually, match any character */
+
+
+static unsigned char
+ ourBuffer[100], /* where we store stuff */
+ *ourPHead = ourBuffer, /* first character in buffer */
+ *ourPTail = ourBuffer, /* where next character goes */
+ *TransPointer = 0; /* For transparent mode data */
+
+static int InControl;
+static int WaitingForSynch;
+
+static struct astosc
+ *spacePTR = 0; /* Space is hard to enter */
+
+static state
+ *headOfControl = 0; /* where we enter code state table */
+
+#define FullChar ((ourPTail+5) >= ourBuffer+sizeof ourBuffer)
+#define EmptyChar (ourPTail == ourPHead)
+
+
+/*
+ * init_keyboard()
+ *
+ * Initialize the keyboard variables.
+ */
+
+void
+init_keyboard()
+{
+ ourPHead = ourPTail = ourBuffer;
+ InControl = 0;
+ WaitingForSynch = 0;
+}
+
+
+/*
+ * Initialize the keyboard mapping file.
+ */
+
+void
+InitMapping()
+{
+ extern state *InitControl();
+ register struct astosc *ptr;
+
+ if (!headOfControl) {
+ /* need to initialize */
+ headOfControl = InitControl((char *)0, 0, ascii_to_index);
+ if (!headOfControl) { /* should not occur */
+ quit();
+ }
+ for (ptr = &astosc[0]; ptr <= &astosc[highestof(astosc)]; ptr++) {
+ if (ptr->function == FCN_SPACE) {
+ spacePTR = ptr;
+ }
+ }
+ }
+}
+
+
+/* AddChar - put a function index in our buffer */
+
+static void
+AddChar(c)
+int c;
+{
+ if (!FullChar) {
+ *ourPTail++ = c;
+ } else {
+ RingBell("Typeahead buffer full");
+ }
+}
+
+/* FlushChar - put everything where it belongs */
+
+static void
+FlushChar()
+{
+ ourPTail = ourBuffer;
+ ourPHead = ourBuffer;
+}
+
+/*ARGSUSED*/
+void
+TransInput(onoff, mode)
+int mode; /* Which KIND of transparent input */
+int onoff; /* Going in, or coming out */
+{
+ if (onoff) {
+ /* Flush pending input */
+ FlushChar();
+ TransPointer = ourBuffer;
+ } else {
+ }
+}
+
+int
+TerminalIn()
+{
+ /* send data from us to next link in stream */
+ int work = 0;
+ register struct astosc *ptr;
+
+ while (!EmptyChar) { /* send up the link */
+ if (*ourPHead == ' ') {
+ ptr = spacePTR;
+ } else {
+ ptr = &astosc[*ourPHead];
+ }
+ if (AcceptKeystroke(ptr->scancode, ptr->shiftstate) == 1) {
+ ourPHead++;
+ work = 1;
+ } else {
+ break;
+ }
+ }
+
+ if (EmptyChar) {
+ FlushChar();
+ }
+ /* return value answers question: "did we do anything useful?" */
+ return work;
+}
+
+int
+DataFromTerminal(buffer, count)
+register char *buffer; /* the data read in */
+register int count; /* how many bytes in this buffer */
+{
+ register state *regControlPointer;
+ register char c;
+ register int result;
+ int origCount;
+ extern int bellwinup;
+ static state *controlPointer;
+
+ if (TransPointer) {
+ int i;
+
+ if ((count+TransPointer) >= (ourBuffer+sizeof ourBuffer)) {
+ i = ourBuffer+sizeof ourBuffer-TransPointer;
+ } else {
+ i = count;
+ }
+ while (i--) {
+ c = (*buffer++)&0x7f;
+ *TransPointer++ = c|0x80;
+ if (c == '\r') {
+ SendTransparent((char *)ourBuffer, TransPointer-ourBuffer);
+ TransPointer = 0; /* Done */
+ break;
+ }
+ }
+ return count;
+ }
+
+ if (bellwinup) {
+ void BellOff();
+
+ BellOff();
+ }
+
+ origCount = count;
+
+ while (count) {
+ c = *buffer++&0x7f;
+ count--;
+
+ if (!InControl && !IsControl(c)) {
+ AddChar(c); /* add ascii character */
+ } else {
+ if (!InControl) { /* first character of sequence */
+ InControl = 1;
+ controlPointer = headOfControl;
+ }
+ /* control pointer points to current position in state table */
+ for (regControlPointer = controlPointer; ;
+ regControlPointer = NextState(regControlPointer)) {
+ if (!regControlPointer) { /* ran off end */
+ RingBell("Invalid control sequence");
+ regControlPointer = headOfControl;
+ InControl = 0;
+ count = 0; /* Flush current input */
+ break;
+ }
+ if ((regControlPointer->match == c) /* hit this character */
+ || (regControlPointer->match == MATCH_ANY)) {
+ result = regControlPointer->result;
+ if (result == STATE_GOTO) {
+ regControlPointer = regControlPointer->address;
+ break; /* go to next character */
+ }
+ if (WaitingForSynch) {
+ if (astosc[result].function == FCN_SYNCH) {
+ WaitingForSynch = 0;
+ } else {
+ void RingBell();
+
+ RingBell("Need to type synch character");
+ }
+ }
+ else if (astosc[result].function == FCN_FLINP) {
+ FlushChar(); /* Don't add FLINP */
+ } else {
+ if (astosc[result].function == FCN_MASTER_RESET) {
+ FlushChar();
+ }
+ AddChar(result); /* add this code */
+ }
+ InControl = 0; /* out of control now */
+ break;
+ }
+ }
+ controlPointer = regControlPointer; /* save state */
+ }
+ }
+ (void) TerminalIn(); /* try to send data */
+ return(origCount-count);
+}
diff --git a/usr.bin/tn3270/ctlr/3180.kbd b/usr.bin/tn3270/ctlr/3180.kbd
new file mode 100644
index 0000000..1fc7204
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/3180.kbd
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)3180.kbd 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * keynumber [ scancode [ unshifted [ shifted [ alted [ shiftalted ] ] ] ] ]
+ *
+ * keynumber is in decimal, and starts in column 1.
+ * scancode is hexadecimal.
+ * unshifted, etc. - these are either a single ascii character,
+ * or the name of a function or an AID-generating key.
+ *
+ * all fields are separated by a single space.
+ */
+1 0e ` ~
+2 16 1 VERTICAL_BAR
+3 1e 2 @
+4 26 3 #
+5 25 4 $
+6 2e 5 %
+7 36 6 ^
+8 3d 7 &
+9 3e 8 *
+10 46 9 (
+11 45 0 )
+12 4e - _
+13 55 = +
+14 5d
+15 66 LEFT
+16 0d TAB
+17 15 q Q
+18 1d w W
+19 24 e E
+20 2d r R
+21 2c t T
+22 35 y Y
+23 3c u U
+24 43 i I
+25 44 o O
+26 4d p P
+27 54 CENTSIGN !
+28 5b \ |
+29 5c
+30 14 CAPS_LOCK
+31 1c a A
+32 1b s S
+33 23 d D
+34 2b f F
+35 34 g G
+36 33 h H
+37 3b j J
+38 42 k K
+39 4b l L
+40 4c ; :
+41 52 ' "
+42 53 { }
+43 5a NL
+44 12 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+45 13 < >
+46 1a z Z
+47 22 x X
+48 21 c C
+49 2a v V
+50 32 b B
+51 31 n N
+52 3a m M
+53 41 , ,
+54 49 . .
+55 4a / ?
+56 51
+57 59 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+58 11 RESET NULL DVCNL
+59
+60 19 MAKE_ALT MAKE_ALT MAKE_ALT
+61 29 SPACE SPACE
+62 39 MAKE_ALT MAKE_ALT MAKE_ALT
+63
+64 58 ENTER
+65 06 CLEAR
+66 0c NULL NULL EINP
+67 0b EEOF
+68 0a
+69 09
+70 05 ATTN NULL TREQ
+71 04
+72 03
+73 83
+74 01
+75 67 PA1 DP
+76 64 BTAB
+77
+78 61 LEFT NULL LEFT2
+79
+80 6e PA2 FM
+81 65 INSRT
+82 63 UP
+83 62 NULL NULL HOME
+84 60 DOWN
+85 6f
+86 6d DELETE
+87
+88 6a RIGHT NULL RIGHT2
+89
+90 76
+91 6c 7
+92 6b 4
+93 69 1
+94 68
+95 77
+96 75 8
+97 73 5
+98 72 2
+99 70 0
+100 7e ,
+101 7d 9
+102 74 6
+103 7a 3
+104 71 .
+105 84 SPACE
+106 7c TAB
+107 7b -
+108 79 ENTER
+109 78
+110 07 PF1
+111 0f PF2
+112 17 PF3
+113 1f PF4
+114 27 PF5
+115 2f PF6
+116 37 PF7
+117 3f PF8 NULL MONOCASE
+118 47 PF9
+119 4f PF10
+120 56 PF11
+121 5e PF12
+122 08 PF13
+123 10 PF14
+124 18 PF15
+125 20 PF16
+126 28 PF17
+127 30 PF18
+128 38 PF19
+129 40 PF20
+130 48 PF21
+131 50 PF22
+132 57 PF23
+133 5f PF24
+134 92 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+135 D9 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+136 99 BREAK_ALT BREAK_ALT BREAK_ALT
+137 B9 BREAK_ALT BREAK_ALT BREAK_ALT
diff --git a/usr.bin/tn3270/ctlr/3270pc.kbd b/usr.bin/tn3270/ctlr/3270pc.kbd
new file mode 100644
index 0000000..df75046
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/3270pc.kbd
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)3270pc.kbd 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * keynumber [ scancode [ unshifted [ shifted [ alted [ shiftalted ] ] ] ] ]
+ *
+ * keynumber is in decimal, and starts in column 1.
+ * scancode is hexadecimal.
+ * unshifted, etc. - these are either a single ascii character,
+ * or the name of a function or an AID-generating key.
+ *
+ * all fields are separated by a single space.
+ */
+1 0e ` ~
+2 16 1 !
+3 1e 2 @
+4 26 3 #
+5 25 4 $
+6 2e 5 %
+7 36 6 ^
+8 3d 7 &
+9 3e 8 *
+10 46 9 (
+11 45 0 )
+12 4e - _
+13 55 = +
+14 5d
+15 66 LEFT
+16 0d TAB BTAB
+17 15 q Q
+18 1d w W
+19 24 e E
+20 2d r R
+21 2c t T
+22 35 y Y
+23 3c u U
+24 43 i I
+25 44 o O
+26 4d p P
+27 54 [ {
+28 5b \ |
+29 5c
+30 14 CAPS_LOCK
+31 1c a A
+32 1b s S
+33 23 d D
+34 2b f F
+35 34 g G
+36 33 h H
+37 3b j J
+38 42 k K
+39 4b l L
+40 4c ; :
+41 52 ' "
+42 53 ] }
+43 5a NL
+44 12 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+45 13 < >
+46 1a z Z
+47 22 x X
+48 21 c C
+49 2a v V
+50 32 b B
+51 31 n N
+52 3a m M
+53 41 , <
+54 49 . >
+55 4a / ?
+56 51
+57 59 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+58 11 RESET NULL DVCNL
+59
+60 19 MAKE_ALT MAKE_ALT MAKE_ALT
+61 29 SPACE SPACE
+62 39 MAKE_ALT MAKE_ALT MAKE_ALT
+63
+64 58 ENTER
+65 06 CLEAR NULL TEST
+66 0c NULL NULL ATTN
+67 0b EEOF NULL EINP
+68 0a
+69 09 MAKE_CTRL
+70 05 ATTN NULL TREQ
+71 04
+72 03
+73 83
+74 01
+75 67 PA1 DP
+76 64 BTAB
+77
+78 61 LEFT NULL LEFT2
+79
+80 6e PA2 FM
+81 65 INSRT
+82 63 UP
+83 62 NULL NULL HOME
+84 60 DOWN
+85 6f PA3
+86 6d DELETE
+87
+88 6a RIGHT NULL RIGHT2
+89
+90 76
+91 6c 7
+92 6b 4
+93 69 1
+94 68
+95 77
+96 75 8
+97 73 5
+98 72 2
+99 70 0
+100 7e ,
+101 7d 9
+102 74 6
+103 7a 3
+104 71 .
+105 84 SPACE
+106 7c TAB
+107 7b -
+108 79 ENTER
+109 78
+110 07 PF1
+111 0f PF2
+112 17 PF3
+113 1f PF4
+114 27 PF5
+115 2f PF6
+116 37 PF7
+117 3f PF8 NULL MONOCASE
+118 47 PF9
+119 4f PF10
+120 56 PF11
+121 5e PF12
+122 08 PF13
+123 10 PF14
+124 18 PF15
+125 20 PF16
+126 28 PF17
+127 30 PF18
+128 38 PF19
+129 40 PF20
+130 48 PF21
+131 50 PF22
+132 57 PF23
+133 5f PF24
+134 92 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+135 D9 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+136 99 BREAK_ALT BREAK_ALT BREAK_ALT
+137 B9 BREAK_ALT BREAK_ALT BREAK_ALT
diff --git a/usr.bin/tn3270/ctlr/api.c b/usr.bin/tn3270/ctlr/api.c
new file mode 100644
index 0000000..801e0ee
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/api.c
@@ -0,0 +1,760 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)api.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This file implements the API used in the PC version.
+ */
+
+#include <stdio.h>
+
+#include "api.h"
+#include "../general/general.h"
+
+#include "../api/disp_asc.h"
+
+#include "screen.h"
+#include "hostctlr.h"
+#include "oia.h"
+
+#include "../general/globals.h"
+
+int apitrace = 0;
+
+/*
+ * Some defines for things we use internally.
+ */
+
+#define PS_SESSION_ID 23
+#define BUF_SESSION_ID 0
+
+/*
+ * General utility routines.
+ */
+
+#if defined(MSDOS)
+
+#if defined(LINT_ARGS)
+static void movetous(char *, int, int, int);
+static void movetothem(int, int, char *, int);
+#endif /* defined(LINT_ARGS) */
+
+#define access_api(foo,length,copyin) (foo)
+#define unaccess_api(foo,goo,length,copyout)
+
+static void
+movetous(parms, es, di, length)
+char *parms;
+int es, di;
+int length;
+{
+ char far *farparms = parms;
+
+ movedata(es, di, FP_SEG(farparms), FP_OFF(farparms), length);
+ if (apitrace) {
+ Dump('(', parms, length);
+ }
+}
+
+static void
+movetothem(es, di, parms, length)
+int es, di;
+char *parms;
+int length;
+{
+ char far *farparms = parms;
+
+ movedata(FP_SEG(farparms), FP_OFF(farparms), es, di, length);
+ if (apitrace) {
+ Dump(')', parms, length);
+ }
+}
+#endif /* defined(MSDOS) */
+
+#if defined(unix)
+extern char *access_api();
+extern void movetous(), movetothem(), unaccess_api();
+#endif /* defined(unix) */
+
+
+/*
+ * Supervisor Services.
+ */
+
+static void
+name_resolution(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ NameResolveParms parms;
+
+ movetous((char *) &parms, sregs->es, regs->x.di, sizeof parms);
+
+ regs->h.cl = 0;
+ if (memcmp((char *)&parms, NAME_SESSMGR, sizeof parms.gate_name) == 0) {
+ regs->x.dx = GATE_SESSMGR;
+ } else if (memcmp((char *)&parms, NAME_KEYBOARD,
+ sizeof parms.gate_name) == 0) {
+ regs->x.dx = GATE_KEYBOARD;
+ } else if (memcmp((char *)&parms, NAME_COPY, sizeof parms.gate_name) == 0) {
+ regs->x.dx = GATE_COPY;
+ } else if (memcmp((char *)&parms, NAME_OIAM, sizeof parms.gate_name) == 0) {
+ regs->x.dx = GATE_OIAM;
+ } else {
+ regs->h.cl = 0x2e; /* Name not found */
+ }
+ regs->h.ch = 0x12;
+ regs->h.bh = 7;
+}
+
+/*
+ * Session Information Services.
+ */
+
+static void
+query_session_id(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ QuerySessionIdParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.option_code != 0x01) {
+ parms.rc = 0x0d; /* Invalid option code */
+#ifdef NOTOBS
+ } else if ((parms.data_code != 0x45) && (parms.data_code != 0x00/*OBS*/)) {
+ parms.rc = 0x0b;
+#endif /* NOTOBS */
+ } else {
+ NameArray list;
+
+ movetous((char *)&list, FP_SEG(parms.name_array),
+ FP_OFF(parms.name_array), sizeof list);
+ if ((list.length < 14) || (list.length > 170)) {
+ parms.rc = 0x12;
+ } else {
+ list.number_matching_session = 1;
+ list.name_array_element.short_name = parms.data_code;
+ list.name_array_element.type = TYPE_DFT;
+ list.name_array_element.session_id = PS_SESSION_ID;
+ memcpy(list.name_array_element.long_name, "ONLYSESS",
+ sizeof list.name_array_element.long_name);
+ movetothem(FP_SEG(parms.name_array),
+ FP_OFF(parms.name_array), (char *)&list, sizeof list);
+ parms.rc = 0;
+ }
+ }
+ parms.function_id = 0x6b;
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+static void
+query_session_parameters(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ QuerySessionParametersParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc !=0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else {
+ parms.rc = 0;
+ parms.session_type = TYPE_DFT;
+ parms.session_characteristics = 0; /* Neither EAB nor PSS */
+ parms.rows = MaxNumberLines;
+ parms.columns = MaxNumberColumns;
+ parms.presentation_space = 0;
+ }
+ parms.function_id = 0x6b;
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+static void
+query_session_cursor(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ QuerySessionCursorParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else {
+ parms.rc = 0;
+ parms.cursor_type = CURSOR_BLINKING; /* XXX what is inhibited? */
+ parms.row_address = ScreenLine(CursorAddress);
+ parms.column_address = ScreenLineOffset(CursorAddress);
+ }
+
+ parms.function_id = 0x6b;
+ movetothem(sregs->es, regs->x.di, (char *) &parms, sizeof parms);
+}
+
+/*
+ * Keyboard Services.
+ */
+
+
+static void
+connect_to_keyboard(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ ConnectToKeyboardParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else if (parms.intercept_options != 0) {
+ parms.rc = 0x01;
+ } else {
+ parms.rc = 0;
+ parms.first_connection_identifier = 0;
+ }
+ parms.function_id = 0x62;
+
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+static void
+disconnect_from_keyboard(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ DisconnectFromKeyboardParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else if (parms.connectors_task_id != 0) {
+ parms.rc = 04; /* XXX */
+ } else {
+ parms.rc = 0;
+ }
+ parms.function_id = 0x62;
+
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+static void
+write_keystroke(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ WriteKeystrokeParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else if (parms.connectors_task_id != 0) {
+ parms.rc = 0x04;
+ } else {
+ parms.number_of_keys_sent = 0;
+ parms.rc = 0;
+ if (parms.options == OPTION_SINGLE_KEYSTROKE) {
+ KeystrokeEntry *entry = &parms.keystroke_specifier.keystroke_entry;
+
+ if (AcceptKeystroke(entry->scancode, entry->shift_state) == 0) {
+ parms.rc = 0x10; /* XXX needs 0x12 too! */
+ }
+ parms.number_of_keys_sent++;
+ } else if (parms.options == OPTION_MULTIPLE_KEYSTROKES) {
+ KeystrokeList
+ list,
+ far *atlist = parms.keystroke_specifier.keystroke_list;
+ KeystrokeEntry
+ entry[10], /* 10 at a time */
+ *ourentry,
+ far *theirentry;
+ int
+ todo;
+
+ movetous((char *)&list, FP_SEG(atlist),
+ FP_OFF(atlist), sizeof *atlist);
+ todo = list.length/2;
+ ourentry = entry+(highestof(entry)+1);
+ theirentry = &atlist->keystrokes;
+
+ while (todo) {
+ if (ourentry > &entry[highestof(entry)]) {
+ int thistime;
+
+ thistime = todo;
+ if (thistime > numberof(entry)) {
+ thistime = numberof(entry);
+ }
+ movetous((char *)entry, FP_SEG(theirentry),
+ FP_OFF(theirentry), thistime*sizeof *theirentry);
+ theirentry += thistime;
+ ourentry = entry;
+ }
+ if (AcceptKeystroke(ourentry->scancode,
+ ourentry->shift_state) == 0) {
+ parms.rc = 0x10; /* XXX needs 0x12 too! */
+ break;
+ }
+ parms.number_of_keys_sent++;
+ ourentry++;
+ todo--;
+ }
+ } else {
+ parms.rc = 0x01;
+ }
+ }
+ parms.function_id = 0x62;
+
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+/* XXX */
+}
+
+
+static void
+disable_input(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ DisableInputParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else if (parms.connectors_task_id != 0) {
+ parms.rc = 0x04;
+ } else {
+ SetOiaApiInhibit(&OperatorInformationArea);
+ parms.rc = 0;
+ }
+ parms.function_id = 0x62;
+
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+static void
+enable_input(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ EnableInputParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else if (parms.connectors_task_id != 0) {
+ parms.rc = 0x04;
+ } else {
+ ResetOiaApiInhibit(&OperatorInformationArea);
+ parms.rc = 0;
+ }
+ parms.function_id = 0x62;
+
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+/*
+ * Copy Services.
+ */
+
+static
+copy_subroutine(target, source, parms, what_is_user, length)
+BufferDescriptor *target, *source;
+CopyStringParms *parms;
+int what_is_user;
+#define USER_IS_TARGET 0
+#define USER_IS_SOURCE 1
+{
+#define TARGET_NO_EAB 1
+#define SOURCE_NO_EAB 2
+#define TARGET_PC 4
+#define SOURCE_PC 8
+#define NO_FIELD_ATTRIBUTES 16
+ int needtodo = 0;
+ int access_length;
+ char far *input;
+ char far *output;
+ char far *access_pointer;
+
+ if ((target->characteristics^source->characteristics)
+ &CHARACTERISTIC_EAB) {
+ if (target->characteristics&CHARACTERISTIC_EAB) {
+ needtodo |= TARGET_NO_EAB; /* Need to bump for EAB in target */
+ } else {
+ needtodo |= SOURCE_NO_EAB; /* Need to bump for EAB in source */
+ }
+ }
+ if (target->session_type != source->session_type) {
+ if (target->session_type == TYPE_PC) {
+ needtodo |= TARGET_PC; /* scan codes to PC */
+ } else {
+ needtodo |= SOURCE_PC; /* PC to scan codes */
+ }
+ }
+ if ((parms->copy_mode&COPY_MODE_FIELD_ATTRIBUTES) == 0) {
+ needtodo |= NO_FIELD_ATTRIBUTES;
+ }
+ access_length = length;
+ if (what_is_user == USER_IS_TARGET) {
+ if (target->characteristics&CHARACTERISTIC_EAB) {
+ access_length *= 2;
+ }
+ input = (char far *) &Host[source->begin];
+ access_pointer = target->buffer;
+ output = access_api(target->buffer, access_length, 0);
+ } else {
+ if (source->characteristics&CHARACTERISTIC_EAB) {
+ access_length *= 2;
+ }
+ access_pointer = source->buffer;
+ input = access_api(source->buffer, access_length, 1);
+ output = (char far *) &Host[target->begin];
+ }
+ while (length--) {
+ if (needtodo&TARGET_PC) {
+ *output++ = disp_asc[*input++];
+ } else if (needtodo&SOURCE_PC) {
+ *output++ = asc_disp[*input++];
+ } else {
+ *output++ = *input++;
+ }
+ if (needtodo&TARGET_NO_EAB) {
+ input++;
+ } else if (needtodo&SOURCE_NO_EAB) {
+ *output++ = 0; /* Should figure out good EAB? */
+ }
+ }
+ if (what_is_user == USER_IS_TARGET) {
+ unaccess_api(target->buffer, access_pointer, access_length, 1);
+ } else {
+ unaccess_api(source->buffer, access_pointer, access_length, 0);
+ }
+}
+
+
+static void
+copy_string(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ CopyStringParms parms;
+ BufferDescriptor *target = &parms.target, *source = &parms.source;
+ int length;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ length = 1+parms.source_end-source->begin;
+ if ((parms.rc != 0) || (parms.function_id !=0)) {
+ parms.rc = 0x0c;
+ } else if (target->session_id == BUF_SESSION_ID) { /* Target is buffer */
+ if (source->session_id != PS_SESSION_ID) { /* A no-no */
+ parms.rc = 0x2;
+ } else {
+ if ((source->begin < 0) || (source->begin > highestof(Host))) {
+ parms.rc = 0x06; /* invalid source definition */
+ } else {
+ if ((source->begin+length) > highestof(Host)) {
+ length = highestof(Host)-source->begin;
+ parms.rc = 0x0f; /* Truncate */
+ }
+ if ((source->characteristics == target->characteristics) &&
+ (source->session_type == target->session_type)) {
+ if (source->characteristics&CHARACTERISTIC_EAB) {
+ length *= 2;
+ }
+ movetothem(FP_SEG(target->buffer),
+ FP_OFF(target->buffer),
+ (char *)&Host[source->begin], length);
+ } else {
+ copy_subroutine(target, source, &parms,
+ USER_IS_TARGET, length);
+ }
+ }
+ }
+ } else if (source->session_id != BUF_SESSION_ID) {
+ parms.rc = 0xd;
+ } else {
+ /* Send to presentation space (3270 buffer) */
+ if ((target->begin < 0) || (target->begin > highestof(Host))) {
+ parms.rc = 0x07; /* invalid target definition */
+ } if (!UnLocked) {
+ parms.rc = 0x03; /* Keyboard locked */
+ } else if (parms.copy_mode != 0) {
+ parms.rc = 0x0f; /* Copy of field attr's not allowed */
+ } else if (IsProtected(target->begin) || /* Make sure no protected */
+ (WhereAttrByte(target->begin) != /* in range */
+ WhereAttrByte(target->begin+length-1))) {
+ parms.rc = 0x0e; /* Attempt to write in protected */
+ } else {
+ if ((target->begin+length) > highestof(Host)) {
+ length = highestof(Host)-target->begin;
+ parms.rc = 0x0f; /* Truncate */
+ }
+ TurnOnMdt(target->begin); /* Things have changed */
+ if ((source->characteristics == target->characteristics) &&
+ (source->session_type == target->session_type)) {
+ if (source->characteristics&CHARACTERISTIC_EAB) {
+ length *= 2;
+ }
+ movetous((char *)&Host[target->begin],
+ FP_SEG(source->buffer),
+ FP_OFF(source->buffer), length);
+ } else {
+ copy_subroutine(target, source, &parms, USER_IS_SOURCE, length);
+ }
+ }
+ }
+ parms.function_id = 0x64;
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+
+/*
+ * Operator Information Area Services.
+ */
+
+static void
+read_oia_group(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ ReadOiaGroupParms parms;
+
+ movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
+
+ if ((parms.rc != 0) || (parms.function_id != 0)) {
+ parms.rc = 0x0c;
+ } else if (parms.session_id != PS_SESSION_ID) {
+ parms.rc = 0x02;
+ } else {
+ int group = parms.oia_group_number;
+ char *from;
+ int size;
+
+ if ((group != API_OIA_ALL_GROUPS) &&
+ ((group > API_OIA_LAST_LEGAL_GROUP) || (group < 0))) {
+ } else {
+ if (group == API_OIA_ALL_GROUPS) {
+ size = API_OIA_BYTES_ALL_GROUPS;
+ from = (char *)&OperatorInformationArea;
+ } else if (group == API_OIA_INPUT_INHIBITED) {
+ size = sizeof OperatorInformationArea.input_inhibited;
+ from = (char *)&OperatorInformationArea.input_inhibited[0];
+ } else {
+ size = 1;
+ from = ((char *)&OperatorInformationArea)+group;
+ }
+ movetothem(FP_SEG(parms.oia_buffer), FP_OFF(parms.oia_buffer),
+ from, size);
+ }
+ }
+ parms.function_id = 0x6d;
+ movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
+}
+
+/*ARGSUSED*/
+static void
+unknown_op(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+ regs->h.ch = 0x12;
+ regs->h.cl = 0x05;
+}
+
+
+handle_api(regs, sregs)
+union REGS *regs;
+struct SREGS *sregs;
+{
+/*
+ * Do we need to log this transaction?
+ */
+ if (apitrace) {
+ Dump('<', (char *)regs, sizeof *regs);
+ Dump('<', (char *)sregs, sizeof *sregs);
+ }
+ if (regs->h.ah == NAME_RESOLUTION) {
+ name_resolution(regs, sregs);
+#if defined(unix)
+ } else if (regs->h.ah == PS_OR_OIA_MODIFIED) {
+ while ((oia_modified == 0) && (ps_modified == 0)) {
+ (void) Scheduler(1);
+ }
+ oia_modified = ps_modified = 0;
+#endif /* defined(unix) */
+ } else if (regs->h.ah != 0x09) {
+ regs->h.ch = 0x12;
+ regs->h.cl = 0x0f; /* XXX Invalid environmental access */
+ } else if (regs->x.bx != 0x8020) {
+ regs->h.ch = 0x12;
+ regs->h.cl = 0x08; /* XXX Invalid wait specified */
+ } else if (regs->h.ch != 0) {
+ regs->x.cx = 0x1206; /* XXX Invalid priority */
+ } else {
+ switch (regs->x.dx) {
+ case GATE_SESSMGR:
+ switch (regs->h.al) {
+ case QUERY_SESSION_ID:
+ if (regs->h.cl != 0) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ query_session_id(regs, sregs);
+ }
+ break;
+ case QUERY_SESSION_PARAMETERS:
+ if (regs->h.cl != 0) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ query_session_parameters(regs, sregs);
+ }
+ break;
+ case QUERY_SESSION_CURSOR:
+ if ((regs->h.cl != 0xff) && (regs->h.cl != 0x00/*OBS*/)) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ query_session_cursor(regs, sregs);
+ }
+ break;
+ default:
+ unknown_op(regs, sregs);
+ break;
+ }
+ break;
+ case GATE_KEYBOARD:
+ if (regs->h.cl != 00) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ switch (regs->h.al) {
+ case CONNECT_TO_KEYBOARD:
+ connect_to_keyboard(regs, sregs);
+ break;
+ case DISABLE_INPUT:
+ disable_input(regs, sregs);
+ break;
+ case WRITE_KEYSTROKE:
+ write_keystroke(regs, sregs);
+ break;
+ case ENABLE_INPUT:
+ enable_input(regs, sregs);
+ break;
+ case DISCONNECT_FROM_KEYBOARD:
+ disconnect_from_keyboard(regs, sregs);
+ break;
+ default:
+ unknown_op(regs, sregs);
+ break;
+ }
+ }
+ break;
+ case GATE_COPY:
+ if (regs->h.cl != 0xff) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ switch (regs->h.al) {
+ case COPY_STRING:
+ copy_string(regs, sregs);
+ break;
+ default:
+ unknown_op(regs, sregs);
+ break;
+ }
+ }
+ break;
+ case GATE_OIAM:
+ if (regs->h.cl != 0xff) {
+ regs->x.cx = 0x1206;
+ } else {
+ regs->x.cx = 0x1200;
+ switch (regs->h.al) {
+ case READ_OIA_GROUP:
+ read_oia_group(regs, sregs);
+ break;
+ default:
+ unknown_op(regs, sregs);
+ break;
+ }
+ }
+ break;
+ default:
+ regs->h.ch = 0x12;
+ regs->h.cl = 0x34; /* Invalid GATE entry */
+ break;
+ }
+ }
+/*
+ * Do we need to log this transaction?
+ */
+ if (apitrace) {
+ Dump('>', (char *)regs, sizeof *regs);
+ Dump('>', (char *)sregs, sizeof *sregs);
+#ifdef MSDOS
+ {
+ int ch;
+
+ while ((ch = getchar()) != '\n' && ch != EOF)
+ ;
+ }
+#endif /* MSDOS */
+ }
+}
diff --git a/usr.bin/tn3270/ctlr/api.h b/usr.bin/tn3270/ctlr/api.h
new file mode 100644
index 0000000..ba0df8d
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/api.h
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)api.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This file contains header information used by the PC API routines.
+ */
+
+#if !defined(MSDOS)
+#define far /* For 'far *' checks */
+#endif /* !defined(MSDOS) */
+
+#define API_INTERRUPT_NUMBER 0x7A /* API Interrupt Number */
+
+/*
+ * Define the gate numbers. These are returned via the Name Resolution
+ * service.
+ */
+
+#define GATE_SESSMGR 1234
+#define GATE_KEYBOARD 5678
+#define GATE_COPY 9101
+#define GATE_OIAM 1121
+
+/*
+ * The names which correspond to the above gate numbers.
+ */
+
+#define NAME_SESSMGR "SESSMGR "
+#define NAME_KEYBOARD "KEYBOARD"
+#define NAME_COPY "COPY "
+#define NAME_OIAM "OIAM "
+
+
+/*
+ * Name Resolution is specified in AH.
+ */
+
+#define NAME_RESOLUTION 0x81
+
+#if defined(unix)
+/*
+ * In unix, we offer a service to allow the application to keep from
+ * having to poll us constantly.
+ */
+#define PS_OR_OIA_MODIFIED 0x99
+
+#endif /* defined(unix) */
+
+/*
+ * Codes specified in AL for various services.
+ */
+
+#define QUERY_SESSION_ID 0x01
+#define QUERY_SESSION_PARAMETERS 0x02
+#define QUERY_SESSION_CURSOR 0x0b
+
+#define CONNECT_TO_KEYBOARD 0x01
+#define DISCONNECT_FROM_KEYBOARD 0x02
+#define WRITE_KEYSTROKE 0x04
+#define DISABLE_INPUT 0x05
+#define ENABLE_INPUT 0x06
+
+#define COPY_STRING 0x01
+
+#define READ_OIA_GROUP 0x02
+
+/*
+ * For each service, we define the assoicated parameter blocks.
+ */
+
+/*
+ * Supervisor Services
+ */
+
+typedef struct {
+ char gate_name[8];
+} NameResolveParms;
+
+
+/*
+ * Session Information Services
+ */
+
+typedef struct {
+ char
+ short_name,
+ type,
+ session_id,
+ reserved,
+ long_name[8];
+} NameArrayElement;
+
+typedef struct {
+ unsigned char
+ length,
+ number_matching_session;
+ NameArrayElement
+ name_array_element; /* Variable number */
+} NameArray;
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ option_code,
+ data_code;
+ NameArray far
+ *name_array;
+ char
+ long_name[8];
+} QuerySessionIdParms;
+
+#define ID_OPTION_BY_NAME 0x01 /* By short (or long) name */
+#define ID_OPTION_ALL 0x00 /* All (of specified type */
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved,
+ session_type,
+ session_characteristics,
+ rows,
+ columns;
+ char far
+ *presentation_space;
+} QuerySessionParametersParms;
+
+#define TYPE_WSCTL 0x01 /* Work Station Control */
+#define TYPE_DFT 0x02 /* DFT Host Session */
+#define TYPE_CUT 0x03 /* CUT Host Session */
+#define TYPE_NOTEPAD 0x04 /* Notepad Session */
+#define TYPE_PC 0x05 /* Personal Computer Session */
+
+#define CHARACTERISTIC_EAB 0x80 /* Extended Attribute Buffer */
+#define CHARACTERISTIC_PSS 0x40 /* Program Symbols Supported */
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ cursor_type,
+ row_address, /* from 0 */
+ column_address; /* from 0 */
+} QuerySessionCursorParms;
+
+#define CURSOR_INHIBITED_AUTOSCROLL 0x10
+#define CURSOR_INHIBITED 0x04
+#define CURSOR_BLINKING 0x02
+#define CURSOR_BOX 0x01
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved;
+ short
+ event_queue_id,
+ input_queue_id;
+ char
+ intercept_options,
+ first_connection_identifier;
+} ConnectToKeyboardParms;
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved;
+ short
+ connectors_task_id;
+} DisconnectFromKeyboardParms;
+
+typedef struct {
+ unsigned char
+ scancode,
+ shift_state;
+} KeystrokeEntry;
+
+typedef struct {
+ short
+ length; /* Length (in bytes) of list */
+ KeystrokeEntry keystrokes; /* Variable size */
+} KeystrokeList;
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved;
+ short
+ connectors_task_id;
+ char
+ options,
+ number_of_keys_sent;
+ union {
+ KeystrokeEntry
+ keystroke_entry;
+ KeystrokeList far
+ *keystroke_list;
+ } keystroke_specifier;
+} WriteKeystrokeParms;
+
+#define OPTION_SINGLE_KEYSTROKE 0x20
+#define OPTION_MULTIPLE_KEYSTROKES 0x30
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved;
+ short
+ connectors_task_id;
+} DisableInputParms;
+
+typedef DisableInputParms EnableInputParms;
+
+typedef struct {
+ char
+ session_id,
+ reserved;
+ char far
+ *buffer;
+ char
+ characteristics,
+ session_type;
+ short
+ begin; /* Offset within buffer */
+} BufferDescriptor;
+
+typedef struct {
+ char
+ rc,
+ function_id;
+ BufferDescriptor
+ source;
+ short
+ source_end; /* Offset within source buffer */
+ BufferDescriptor
+ target;
+ char
+ copy_mode,
+ reserved;
+} CopyStringParms;
+
+#define COPY_MODE_7_COLOR 0x80 /* Else 4 color mode */
+#define COPY_MODE_FIELD_ATTRIBUTES 0x40 /* Else don't copy attributes */
+
+typedef struct {
+ char
+ rc,
+ function_id,
+ session_id,
+ reserved;
+ char far
+ *oia_buffer;
+ char
+ oia_group_number;
+} ReadOiaGroupParms;
+
+/* If the user wants all groups, we return API_OIA_BYTES_ALL_GROUPS bytes */
+#define API_OIA_ALL_GROUPS '\377'
+#define API_OIA_BYTES_ALL_GROUPS 22 /* 22 bytes of data */
+
+/* API_OIA_INPUT_INHIBITED is special. It returns more than on byte of data */
+#define API_OIA_INPUT_INHIBITED 8
+
+#define API_OIA_LAST_LEGAL_GROUP 18 /* Highest legal number */
+
+
+
+#if defined(MSDOS)
+
+#if !defined(FP_SEG)
+#include <dos.h>
+#endif /* !defined(FP_SEG) */
+
+#else /* defined(MSDOS) */
+
+/*
+ * These definitions are here to provide the descriptions of
+ * some registers which are, normally, defined in <dos.h> on
+ * a dos system.
+ */
+
+#define FP_SEG(x) ((unsigned int)(((unsigned long)(x))>>16))
+#define FP_OFF(y) ((unsigned int)(((unsigned long)(y))&0xFFFF))
+
+/*
+ * Undo the preceeding.
+ */
+
+#define SEG_OFF_BACK(x,y) (((x)<<16)|(y))
+
+/*
+ * Now, it is somewhat of a pain, but we need to keep
+ * 8086 conventions about which of the "highlow"'s map
+ * into which of the "words".
+ */
+
+#include <sys/param.h> /* Get ENDIAN from machine/endian.h */
+
+/* Determine endian'ess (if necessary) */
+
+#if !(defined(BYTE_ORDER) && defined(BIG_ENDIAN))
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+
+#if defined(vax) || defined(ns32000) || defined(i386) || (defined(mips)&&defined(MIPSEL))
+#define BYTE_ORDER LITTLE_ENDIAN
+#endif /* defined(vax) || defined(ns32000) */
+
+#if defined(sun) || defined(tahoe) || defined(ibm032) || defined(pyr) || defined(gould) || (defined(mips)&&defined(MIPSEB))
+#define BYTE_ORDER BIG_ENDIAN
+#endif /* defined(sun) || defined(tahoe) || defined(ibm032) || defined(pyr) || defined(gould) */
+
+#endif /* !(defined(BYTE_ORDER) && defined(BIG_ENDIAN)) */
+
+struct highlow {
+ unsigned char
+#if BYTE_ORDER == LITTLE_ENDIAN
+ al,
+ ah,
+ bl,
+ bh,
+ cl,
+ ch,
+ dl,
+ dh;
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+#if BYTE_ORDER == BIG_ENDIAN
+ ah,
+ al,
+ bh,
+ bl,
+ ch,
+ cl,
+ dh,
+ dl;
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+};
+
+struct words {
+ unsigned short
+ ax,
+ bx,
+ cx,
+ dx;
+ unsigned short
+ si,
+ di;
+};
+
+union REGS {
+ struct highlow h;
+ struct words x;
+};
+
+struct SREGS {
+ unsigned short
+ cs,
+ ds,
+ es,
+ ss;
+};
+#endif /* defined(MSDOS) (else section) */
diff --git a/usr.bin/tn3270/ctlr/declare.h b/usr.bin/tn3270/ctlr/declare.h
new file mode 100644
index 0000000..0400b33
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/declare.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)declare.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Declarations of routines from the controller.
+ */
+
+extern void
+ AddHost(),
+ DoReadModified(),
+ DoReadBuffer(),
+ OptInit(),
+ SendToIBM(),
+ SendTransparent();
+
+extern int
+ DataFrom3270(),
+ DataFromNetwork(),
+ OptOrder(),
+ OutputClock,
+ TransparentClock;
diff --git a/usr.bin/tn3270/ctlr/externs.h b/usr.bin/tn3270/ctlr/externs.h
new file mode 100644
index 0000000..11c983c
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/externs.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)externs.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * External references from the controller.
+ */
+
+#if !defined(MSDOS)
+extern char *access_api();
+extern void movetous(), movetothem(), unaccess_api();
+#endif /* !defined(MSDOS) */
+
+extern unsigned char
+ *memNSchr(); /* Search for a character ANDED, increment by stride */
+
+extern int
+ DataToNetwork(),
+ OutputClock,
+ suspend(),
+ TransparentClock,
+ UnLocked; /* keyboard is UnLocked? */
+
+extern void
+ command(),
+ ConnectScreen(),
+ ExitString(),
+ init_inbound(),
+ LocalClearScreen(),
+ RefreshScreen(),
+ RingBell(),
+ setconnmode(),
+ StopScreen(),
+ TransOut(),
+ TransStop();
diff --git a/usr.bin/tn3270/ctlr/function.c b/usr.bin/tn3270/ctlr/function.c
new file mode 100644
index 0000000..1844860
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/function.c
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)function.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This file, which never produces a function.o, is used solely to
+ * be run through the preprocessor.
+ *
+ * On a 4.3 system (or even msdos), "cc -E function.h" would produce
+ * the correct output. Unfortunately, 4.2 compilers aren't quite that
+ * useful.
+ */
+
+#include "function.h"
diff --git a/usr.bin/tn3270/ctlr/function.h b/usr.bin/tn3270/ctlr/function.h
new file mode 100644
index 0000000..a62018d
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/function.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)function.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * The following are the various functions which the keyboard can ask
+ * the controller to perform.
+ *
+ * Note that this file (the following entries) are scanned by mkhit.c,
+ * and that the format must remain more-or-less consistent
+ * [ \t]*TOKEN
+ */
+
+enum ctlrfcn {
+
+ undefined = 0, /* Not yet touched */
+
+ FCN_NULL, /* Illegal sequence */
+
+ FCN_RESET, /* unlock keyboard */
+ FCN_MAKE_SHIFT_LOCK,
+ FCN_BREAK_SHIFT_LOCK,
+
+ FCN_MAKE_SHIFT, /* shift key pressed DOWN */
+ FCN_BREAK_SHIFT, /* shift key released */
+
+ FCN_MAKE_ALT, /* alt key pressed DOWN */
+ FCN_BREAK_ALT, /* alt key released */
+
+ FCN_MAKE_CTRL,
+
+ FCN_CAPS_LOCK,
+
+ FCN_MONOCASE, /* DISPLAY in upper case */
+ FCN_DVCNL,
+
+ FCN_CHARACTER, /* Not one of the following, but ... */
+ FCN_VERTICAL_BAR, /* EBCDIC solid vertical bar */
+ FCN_CENTSIGN, /* EBCDIC cent sign */
+ FCN_SPACE, /* EBCDIC space */
+ FCN_DP, /* EBCDIC dup character */
+ FCN_FM, /* EBCDIC field mark */
+
+ FCN_AID, /* Some AID key */
+ FCN_ATTN,
+ FCN_CURSEL, /* Cursor select function (and aid) */
+ FCN_TEST, /* Test function */
+
+ FCN_EINP, /* erase input (dangerous) */
+ FCN_EEOF,
+ FCN_DELETE,
+ FCN_INSRT,
+ FCN_TAB,
+ FCN_BTAB,
+ FCN_NL,
+ FCN_HOME,
+ FCN_UP,
+ FCN_DOWN,
+ FCN_RIGHT,
+ FCN_LEFT,
+ FCN_LEFT2,
+ FCN_RIGHT2,
+
+#if !defined(PURE3274)
+ /*
+ * Local editing functions
+ */
+ FCN_SETTAB, /* set a column tab */
+ FCN_DELTAB,
+ FCN_COLTAB,
+ FCN_COLBAK,
+ FCN_INDENT, /* more margin over one col tab */
+ FCN_UNDENT,
+ FCN_SETMRG,
+ FCN_SETHOM,
+ FCN_CLRTAB,
+ FCN_ERASE, /* erase last character */
+ FCN_WERASE,
+ FCN_FERASE,
+ FCN_WORDTAB, /* tab to start of next word */
+ FCN_WORDBACKTAB,
+ FCN_WORDEND, /* find next end of word */
+ FCN_FIELDEND, /* find next end of field */
+
+ /*
+ * APL functions
+ */
+ FCN_APLON, /* start using apl character set */
+ FCN_APLOFF,
+ FCN_APLEND,
+
+ FCN_PCON,
+ FCN_PCOFF,
+ FCN_INIT, /* re-init screen */
+ FCN_SYNCH, /* synch up after line/control error */
+ FCN_FLINP, /* flush input buffer */
+ FCN_RESHOW, /* redraw screen */
+ FCN_MASTER_RESET, /* FLINP, RESET, RESHOW, + more */
+
+ FCN_DISC, /* suspend application */
+ FCN_ESCAPE, /* enter command mode */
+
+ FCN_ALTK, /* Dvorak keyboard */
+
+ FCN_XOFF, /* suspend output to screen */
+ FCN_XON, /* resume output to screen */
+
+ FCN_LPRT /* print screen on printer */
+#endif /* !defined(PURE3274) */
+};
+/*
+ * The following is the structure which defines what a 3270 keystroke
+ * can do.
+ */
+
+struct hits {
+ unsigned char keynumber;
+ struct hit {
+ enum ctlrfcn ctlrfcn;
+ unsigned char code; /* AID value or 3270 display code */
+ } hit[4]; /* plain, shifted, alted, shiftalted */
+};
+
+extern struct hits hits[];
+
+/*
+ * Definitions of the shift state (and the left/right shift key position).
+ */
+
+#define SHIFT_RIGHT 0x20 /* Right shift key is down */
+#define SHIFT_LEFT 0x10 /* Left shift key is down */
+#define SHIFT_CONTROL 0x08 /* Control shift state (unused) */
+#define SHIFT_ALT 0x04 /* ALT shift state */
+#define SHIFT_CAPS 0x02 /* Caps lock state */
+#define SHIFT_UPSHIFT 0x01 /* Upshift state */
diff --git a/usr.bin/tn3270/ctlr/hostctlr.h b/usr.bin/tn3270/ctlr/hostctlr.h
new file mode 100644
index 0000000..086d7ee
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/hostctlr.h
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hostctlr.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_HOST3270
+
+/* define orders given to 3270's */
+
+#define ORDER_SF 0x1d /* Start Field */
+#define ORDER_SFE 0x29 /* Start Field Extended */
+#define ORDER_SBA 0x11 /* Set Buffer Address (for output) */
+#define ORDER_SA 0x28 /* Set Attribute */
+#define ORDER_MF 0x2c /* Modify field */
+#define ORDER_IC 0x13 /* Insert Cursor (at buffer address) */
+#define ORDER_PT 0x05 /* Program Tab (absurdly complicated) */
+#define ORDER_RA 0x3c /* Repeat next character to some addr */
+#define ORDER_EUA 0x12 /* Null out every unprotected field
+ * to some address.
+ */
+#define ORDER_GE 0x08 /* Graphics Escape */
+#define ORDER_YALE 0x2b /* This is a special YALE order, which
+ * introduces YALE extended orders
+ * (like setting tabs, etc.).
+ */
+
+/* The following is defined for initialization and error messages. */
+
+struct orders_def {
+ int
+ code; /* As in 3270 data stream */
+ char
+ *short_name, /* Short name */
+ *long_name; /* Long name */
+};
+
+#define ORDERS_DEF { \
+ ORDER_SF, "SF", "Start Field", \
+ ORDER_SFE, "SFE", "Start Field Extended", \
+ ORDER_SBA, "SBA", "Set Buffer Address", \
+ ORDER_SA, "SA", "Set Attribute", \
+ ORDER_MF, "MF", "Modify Field", \
+ ORDER_IC, "IC", "Insert Cursor", \
+ ORDER_PT, "PT", "Program Tab", \
+ ORDER_RA, "RA", "Repeat to Address", \
+ ORDER_EUA, "EUA", "Erase Unprotected to Address", \
+ ORDER_GE, "GE", "Graphics Escape", \
+ ORDER_YALE, "YALE", "Yale Order" \
+ }
+
+
+#define ATTR_RESET 0x00 /* SA only - reset to default */
+# define ATTR_DEFAULT 0x00 /* reset to default */
+ /* Also for 0x41-43 below */
+#define ATTR_FIELD 0xC0 /* Field attributes */
+# define ATTR_MASK 0xc0 /* control bits */
+# define ATTR_PROT 0x20 /* protected bit */
+# define ATTR_NUMERIC 0x10 /* numeric field */
+# define ATTR_AUTO_SKIP_MASK 0x30 /* mask to check auto skip */
+# define ATTR_AUTO_SKIP_VALUE 0x30 /* value to have auto skip */
+# define ATTR_DSPD_MASK 0x0c /* highlighting, etc. */
+# define ATTR_DSPD_DNSPD 0x00 /* display, no select */
+# define ATTR_DSPD_DSPD 0x04 /* display, select */
+# define ATTR_DSPD_HIGH 0x08 /* highlighted, select */
+# define ATTR_DSPD_NONDISPLAY 0x0c /* non-display, no select */
+# define ATTR_MDT 0x01 /* modified data tag */
+
+#define ATTR_EXTENDED_HIGHLIGHT 0x41 /* Extended highlighting */
+# define ATTR_BLINK 0xf1 /* Blinking */
+# define ATTR_REVERSE_VIDEO 0xf2 /* Reverse video */
+# define ATTR_UNDERSCORE 0xf3 /* Underline */
+#define ATTR_COLOR 0x42 /* Color */
+# define ATTR_BLUE 0xf1
+# define ATTR_RED 0xf2
+# define ATTR_PINK 0xf3
+# define ATTR_GREEN 0xf4
+# define ATTR_TURQUOISE 0xf5
+# define ATTR_YELLOW 0xf6
+# define ATTR_WHITE 0xf7 /* for 3279; black for 3287; */
+ /* multicolor for triple */
+ /* plane symbol */
+#define ATTR_PROGRAMMED_SYMBOLS 0x43 /* Programmed Symbols */
+# define ATTR_SYMBOL_SET_LOW 0x40 /* Lowest loadable set ID */
+# define ATTR_SYMBOL_SET_HIGH 0xef /* Highest loadable set ID */
+# define ATTR_SYMBOL_SET_APLTEXT 0xf1
+
+/* Non-SNA control unit commands */
+
+#define CMD_ERASE_ALL_UNPROTECTED 0x0f
+#define CMD_ERASE_WRITE 0x05
+#define CMD_ERASE_WRITE_ALTERNATE 0x0d
+#define CMD_READ_BUFFER 0x02
+#define CMD_READ_MODIFIED 0x06
+#define CMD_WRITE 0x01
+#define CMD_WRITE_STRUCTURED_FIELD 0x11
+
+/* SNA control unit commands */
+
+#define CMD_SNA_COPY 0xf7
+#define CMD_SNA_ERASE_ALL_UNPROTECTED 0x6f
+#define CMD_SNA_ERASE_WRITE 0xf5
+#define CMD_SNA_ERASE_WRITE_ALTERNATE 0x7e
+#define CMD_SNA_READ_BUFFER 0xf2
+#define CMD_SNA_READ_MODIFIED 0xf6
+#define CMD_SNA_READ_MODIFIED_ALL 0x6e
+#define CMD_SNA_WRITE 0xf1
+#define CMD_SNA_WRITE_STRUCTURED_FIELD 0xf3
+
+
+#define WCC_RESET 0x40
+#define WCC_ALARM 0x04
+#define WCC_RESTORE 0x02
+#define WCC_RESET_MDT 0x01
+
+
+/* Special EBCDIC characters unique to a 3270 */
+
+#define EBCDIC_BLANK 0x40 /* Space */
+#define EBCDIC_CENTSIGN 0x4a /* Cent sign */
+#define EBCDIC_DUP 0x1c /* DUP character */
+#define EBCDIC_FM 0x1e /* Field mark character */
+#define EBCDIC_PERCENT 0x6c /* Percent sign */
+#define EBCDIC_SLASH 0x61 /* Slash */
+#define EBCDIC_SOH 0x01 /* Start of Heading */
+#define EBCDIC_STX 0x02 /* Start of Text */
+
+/* Structured field types */
+#define SF_3270DS 0x40 /* For write operations */
+#define SF_LPS 0x06 /* Load Programmed Symbols */
+#define SF_SRM 0x09 /* Set Reply Mode */
+#define SF_SWO 0x0b /* Set Window Origin */
+#define SF_READ_PARTITION 0x01 /* Read Partition (Query) */
+#define SF_ERASE_RESET 0x03 /* Erase (and/or Reset) */
+#define SF_SCS_DATA 0x41 /* SCS Data */
+#define SF_CREATE_PARTITION 0x0c /* Create a partition */
+
+/* AID characters sent to host.
+ *
+ * Note that this file (the following entries) are scanned by mkhit.c,
+ * and that the format must remain more-or-less consistent
+ * (#define\tAID_name\t[\t]*TOKEN)
+ */
+
+#define AID_NONE 0x60 /* No AID (display) */
+#define AID_NONE_PRINTER 0xe8 /* No AID (printer) */
+
+#define AID_PA1 0x6c
+#define AID_PA2 0x6e
+#define AID_PA3 0x6b
+#define AID_CLEAR 0x6d
+#define AID_TREQ 0xf0
+#define AID_ENTER 0x7d
+#define AID_SELPEN 0x7e /*
+ * Really, only SELPEN with DESIGNATOR
+ * = space or null
+ */
+#define AID_PF1 0xf1
+#define AID_PF2 0xf2
+#define AID_PF3 0xf3
+#define AID_PF4 0xf4
+#define AID_PF5 0xf5
+#define AID_PF6 0xf6
+#define AID_PF7 0xf7
+#define AID_PF8 0xf8
+#define AID_PF9 0xf9
+#define AID_PF10 0x7a
+#define AID_PF11 0x7b
+#define AID_PF12 0x7c
+#define AID_PF13 0xc1
+#define AID_PF14 0xc2
+#define AID_PF15 0xc3
+#define AID_PF16 0xc4
+#define AID_PF17 0xc5
+#define AID_PF18 0xc6
+#define AID_PF19 0xc7
+#define AID_PF20 0xc8
+#define AID_PF21 0xc9
+#define AID_PF22 0x4a
+#define AID_PF23 0x4b
+#define AID_PF24 0x4c
+#define AID_PF25 0xd1
+#define AID_PF26 0xd2
+#define AID_PF27 0xd3
+#define AID_PF28 0xd4
+#define AID_PF29 0xd5
+#define AID_PF30 0xd6
+#define AID_PF31 0xd7
+#define AID_PF32 0xd8
+#define AID_PF33 0xd9
+#define AID_PF34 0x5a
+#define AID_PF35 0x5b
+#define AID_PF36 0x5c
diff --git a/usr.bin/tn3270/ctlr/inbound.c b/usr.bin/tn3270/ctlr/inbound.c
new file mode 100644
index 0000000..6f530c3
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/inbound.c
@@ -0,0 +1,1194 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)inbound.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "../general/general.h"
+#include "function.h"
+#include "hostctlr.h"
+#include "oia.h"
+#include "scrnctlr.h"
+#include "screen.h"
+#include "options.h"
+#include "../api/dctype.h"
+#include "../api/ebc_disp.h"
+
+#include "../general/globals.h"
+#include "externs.h"
+#include "declare.h"
+
+#define EmptyChar() (ourPTail == ourPHead)
+#define FullChar() (ourPHead == ourBuffer+sizeof ourBuffer)
+
+
+/*
+ * We define something to allow us to to IsProtected() quickly
+ * on unformatted screens (with the current algorithm for fields,
+ * unprotected takes exponential time...).
+ *
+ * The idea is to call SetXIsProtected() BEFORE the
+ * loop, then use XIsProtected().
+ */
+
+#define SetXIsProtected() (XWasSF = 1)
+#define XIsProtected(p) (IsStartField(p)? \
+ XWasSF = 1 : \
+ (XWasSF? \
+ (XWasSF = 0, XProtected = IsProtected(p)) : \
+ XProtected))
+
+static char ourBuffer[400];
+
+static char *ourPHead = ourBuffer,
+ *ourPTail = ourBuffer;
+
+static int HadAid; /* Had an AID haven't sent */
+
+static int InsertMode; /* is the terminal in insert mode? */
+
+static unsigned int
+ rememberedshiftstate; /* Shift (alt) state of terminal */
+
+# define HITNUM(s) ((((s)&(SHIFT_CAPS|SHIFT_UPSHIFT))? 1:0) \
+ + ((((s)&SHIFT_ALT)? 1:0)<<1))
+
+static int XWasSF, XProtected; /* For optimizations */
+#if !defined(PURE3274)
+extern int TransparentClock, OutputClock;
+#endif /* !defined(PURE3274) */
+
+#include "kbd.out" /* Get keyboard mapping function */
+
+/* the following are global variables */
+
+extern int UnLocked; /* keyboard is UnLocked? */
+
+
+/*
+ * init_inbound :
+ *
+ * Reset variables to initial state.
+ */
+
+void
+init_inbound()
+{
+ ourPHead = ourPTail = ourBuffer;
+ HadAid = 0;
+ rememberedshiftstate = 0;
+ InsertMode = 0;
+}
+
+
+/* Tab() - sets cursor to the start of the next unprotected field */
+static void
+Tab()
+{
+ register int i, j;
+
+ i = CursorAddress;
+ j = WhereAttrByte(CursorAddress);
+ do {
+ if (IsStartField(i) && IsUnProtected(ScreenInc(i))) {
+ break;
+ }
+ i = FieldInc(i);
+ } while (i != j);
+ if (IsStartField(i) && IsUnProtected(ScreenInc(i))) {
+ CursorAddress = ScreenInc(i);
+ } else {
+ CursorAddress = SetBufferAddress(0,0);
+ }
+}
+
+
+/* BackTab() - sets cursor to the start of the most recent field */
+
+static void
+BackTab()
+{
+ register int i;
+
+ i = ScreenDec(CursorAddress);
+ for (;;) {
+ if (IsStartField(ScreenDec(i)) && IsUnProtected(i)) {
+ CursorAddress = i;
+ break;
+ }
+ if (i == CursorAddress) {
+ CursorAddress = SetBufferAddress(0,0);
+ break;
+ }
+ i = ScreenDec(i);
+ }
+}
+
+/*
+ * ModifyMdt() - Turn a modified data tag bit on or off (watch
+ * out for unformatted screens).
+ */
+
+ModifyMdt(x,on)
+int x;
+int on;
+{
+ int i = x;
+
+ if (IsStartField(i)) { /* If we are at a start field position... */
+ if (on) {
+ ModifyHost(i, |= ATTR_MDT); /* Turn it on */
+ } else {
+ ModifyHost(i, &= ~ATTR_MDT); /* Turn it off */
+ }
+ } else {
+ i = WhereAttrByte(i); /* Find beginning of field */
+ if (IsStartField(i)) { /* Is there one? */
+ if (on) {
+ ModifyHost(i, |= ATTR_MDT); /* Turn it on */
+ } else {
+ ModifyHost(i, &= ~ATTR_MDT); /* Turn it off */
+ }
+ } /* else, don't modify - this is an unformatted screen */
+ }
+}
+
+
+/* EraseEndOfField - erase all characters to the end of a field */
+
+static void
+EraseEndOfField()
+{
+ register int i;
+
+ if (IsProtected(CursorAddress)) {
+ RingBell("Protected Field");
+ } else {
+ TurnOnMdt(CursorAddress);
+ if (FormattedScreen()) {
+ i = CursorAddress;
+ do {
+ AddHost(i, 0);
+ i = ScreenInc(i);
+ } while ((i != CursorAddress) && !IsStartField(i));
+ } else { /* Screen is Unformatted */
+ i = CursorAddress;
+ do {
+ AddHost(i, 0);
+ i = ScreenInc(i);
+ } while (i != HighestScreen());
+ }
+ }
+}
+
+/* Delete() - deletes a character from the screen
+ *
+ * What we want to do is delete the section
+ * [where, from-1] from the screen,
+ * filling in with what comes at from.
+ *
+ * The deleting continues to the end of the field (or
+ * until the cursor wraps).
+ *
+ * From can be a start of a field. We
+ * check for that. However, there can't be any
+ * fields that start between where and from.
+ * We don't check for that.
+ *
+ * Also, we assume that the protection status of
+ * everything has been checked by the caller.
+ *
+ */
+
+static void
+Delete(where, from)
+register int where, /* Where to start deleting from */
+ from; /* Where to pull back from */
+{
+ register int i;
+
+ TurnOnMdt(where); /* Only do this once in this field */
+ i = where;
+ do {
+ if (IsStartField(from)) {
+ AddHost(i, 0); /* Stick the edge at the start field */
+ } else {
+ AddHost(i, (char)GetHost(from));
+ from = ScreenInc(from); /* Move the edge */
+ }
+ i = ScreenInc(i);
+ } while ((!IsStartField(i)) && (i != where));
+}
+
+static void
+ColBak()
+{
+ register int i;
+
+ i = ScreenLineOffset(CursorAddress);
+ for (i = i-1; i >= 0; i--) {
+ if (OptColTabs[i]) {
+ break;
+ }
+ }
+ if (i < 0) {
+ i = 0;
+ }
+ CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i);
+}
+
+static void
+ColTab()
+{
+ register int i;
+
+ i = ScreenLineOffset(CursorAddress);
+ for (i = i+1; i < NumberColumns; i++) {
+ if (OptColTabs[i]) {
+ break;
+ }
+ }
+ if (i >= NumberColumns) {
+ i = NumberColumns-1;
+ }
+ CursorAddress = SetBufferAddress(ScreenLine(CursorAddress), i);
+}
+
+static void
+Home()
+{
+ register int i;
+ register int j;
+
+ i = SetBufferAddress(OptHome, 0);
+ j = WhereLowByte(i);
+ /*
+ * If the initial value of i points to the field attribute of
+ * an unprotected field, we need to return the address of the
+ * first data byte in the field (assuming there are any!).
+ */
+ if (IsStartField(i) && IsUnProtected(j)) {
+ CursorAddress = j;
+ return;
+ }
+ do {
+ if (IsUnProtected(i)) {
+ CursorAddress = i;
+ return;
+ }
+ /* the following could be a problem if we got here with an
+ * unformatted screen. However, this is "impossible", since
+ * with an unformatted screen, the IsUnProtected(i) above
+ * should be true.
+ */
+ i = ScreenInc(FieldInc(i));
+ } while (i != j);
+ CursorAddress = LowestScreen();
+}
+
+static
+LastOfField(i)
+register int i; /* position to start from */
+{
+ register int j;
+ register int k;
+
+ k = j = i;
+ SetXIsProtected();
+ while (XIsProtected(i) || Disspace(GetHost(i))) {
+ i = ScreenInc(i);
+ if (i == j) {
+ break;
+ }
+ }
+ /* We are now IN a word IN an unprotected field (or wrapped) */
+ while (!XIsProtected(i)) {
+ if (!Disspace(GetHost(i))) {
+ k = i;
+ }
+ i = ScreenInc(i);
+ if (i == j) {
+ break;
+ }
+ }
+ return(k);
+}
+
+
+static void
+FlushChar()
+{
+ ourPTail = ourPHead = ourBuffer;
+}
+
+
+/*
+ * Add one EBCDIC (NOT display code) character to the buffer.
+ */
+
+static void
+AddChar(character)
+char character;
+{
+ if (FullChar()) {
+ ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail, 0);
+ if (EmptyChar()) {
+ FlushChar();
+ } else {
+ char buffer[100];
+
+ sprintf(buffer, "File %s, line %d: No room in network buffer!\n",
+ __FILE__, __LINE__);
+ ExitString(buffer, 1);
+ /*NOTREACHED*/
+ }
+ }
+ *ourPHead++ = character;
+}
+
+
+static void
+SendUnformatted()
+{
+ register int i, j;
+ register int Nulls;
+ register int c;
+
+ /* look for start of field */
+ Nulls = 0;
+ i = j = LowestScreen();
+ do {
+ c = GetHost(i);
+ if (c == 0) {
+ Nulls++;
+ } else {
+ while (Nulls) {
+ Nulls--;
+ AddChar(EBCDIC_BLANK); /* put in blanks */
+ }
+ AddChar((char)disp_ebc[c]);
+ }
+ i = ScreenInc(i);
+ } while (i != j);
+}
+
+static
+SendField(i, cmd)
+register int i; /* where we saw MDT bit */
+int cmd; /* The command code (type of read) */
+{
+ register int j;
+ register int k;
+ register int Nulls;
+ register int c;
+
+ /* look for start of field */
+ i = j = WhereLowByte(i);
+
+ /* On a test_request_read, don't send sba and address */
+ if ((AidByte != AID_TREQ)
+ || (cmd == CMD_SNA_READ_MODIFIED_ALL)) {
+ AddChar(ORDER_SBA); /* set start field */
+ AddChar(BufferTo3270_0(j)); /* set address of this field */
+ AddChar(BufferTo3270_1(j));
+ }
+ /*
+ * Only on read_modified_all do we return the contents
+ * of the field when the attention was caused by a
+ * selector pen.
+ */
+ if ((AidByte != AID_SELPEN)
+ || (cmd == CMD_SNA_READ_MODIFIED_ALL)) {
+ if (!IsStartField(j)) {
+ Nulls = 0;
+ k = ScreenInc(WhereHighByte(j));
+ do {
+ c = GetHost(j);
+ if (c == 0) {
+ Nulls++;
+ } else {
+ while (Nulls) {
+ Nulls--;
+ AddChar(EBCDIC_BLANK); /* put in blanks */
+ }
+ AddChar((char)disp_ebc[c]);
+ }
+ j = ScreenInc(j);
+ } while ((j != k) && (j != i));
+ }
+ } else {
+ j = FieldInc(j);
+ }
+ return(j);
+}
+
+/* Various types of reads... */
+void
+DoReadModified(cmd)
+int cmd; /* The command sent */
+{
+ register int i, j;
+
+ if (AidByte) {
+ if (AidByte != AID_TREQ) {
+ AddChar(AidByte);
+ } else {
+ /* Test Request Read header */
+ AddChar(EBCDIC_SOH);
+ AddChar(EBCDIC_PERCENT);
+ AddChar(EBCDIC_SLASH);
+ AddChar(EBCDIC_STX);
+ }
+ } else {
+ AddChar(AID_NONE);
+ }
+ if (((AidByte != AID_PA1) && (AidByte != AID_PA2)
+ && (AidByte != AID_PA3) && (AidByte != AID_CLEAR))
+ || (cmd == CMD_SNA_READ_MODIFIED_ALL)) {
+ if ((AidByte != AID_TREQ)
+ || (cmd == CMD_SNA_READ_MODIFIED_ALL)) {
+ /* Test request read_modified doesn't give cursor address */
+ AddChar(BufferTo3270_0(CursorAddress));
+ AddChar(BufferTo3270_1(CursorAddress));
+ }
+ i = j = WhereAttrByte(LowestScreen());
+ /* Is this an unformatted screen? */
+ if (!IsStartField(i)) { /* yes, handle separate */
+ SendUnformatted();
+ } else {
+ do {
+ if (HasMdt(i)) {
+ i = SendField(i, cmd);
+ } else {
+ i = FieldInc(i);
+ }
+ } while (i != j);
+ }
+ }
+ ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail, 1);
+ if (EmptyChar()) {
+ FlushChar();
+ HadAid = 0; /* killed that buffer */
+ }
+}
+
+/* A read buffer operation... */
+
+void
+DoReadBuffer()
+{
+ register int i, j;
+
+ if (AidByte) {
+ AddChar(AidByte);
+ } else {
+ AddChar(AID_NONE);
+ }
+ AddChar(BufferTo3270_0(CursorAddress));
+ AddChar(BufferTo3270_1(CursorAddress));
+ i = j = LowestScreen();
+ do {
+ if (IsStartField(i)) {
+ AddChar(ORDER_SF);
+ AddChar(BufferTo3270_1(FieldAttributes(i)));
+ } else {
+ AddChar((char)disp_ebc[GetHost(i)]);
+ }
+ i = ScreenInc(i);
+ } while (i != j);
+ ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail, 1);
+ if (EmptyChar()) {
+ FlushChar();
+ HadAid = 0; /* killed that buffer */
+ }
+}
+
+/* Send some transparent data to the host */
+
+void
+SendTransparent(buffer, count)
+char *buffer;
+int count;
+{
+ char stuff[3];
+
+ stuff[0] = AID_NONE_PRINTER;
+ stuff[1] = BufferTo3270_0(count);
+ stuff[2] = BufferTo3270_1(count);
+ DataToNetwork(stuff, sizeof stuff, 0);
+ DataToNetwork(buffer, count, 1);
+}
+
+
+/* Try to send some data to host */
+
+void
+SendToIBM()
+{
+#if !defined(PURE3274)
+ if (TransparentClock >= OutputClock) {
+ if (HadAid) {
+ AddChar(AidByte);
+ HadAid = 0;
+ } else {
+ AddChar(AID_NONE_PRINTER);
+ }
+ do {
+ ourPTail += DataToNetwork(ourPTail, ourPHead-ourPTail, 1);
+ } while (!EmptyChar());
+ FlushChar();
+ } else if (HadAid) {
+ DoReadModified(CMD_READ_MODIFIED);
+ }
+#else /* !defined(PURE3274) */
+ if (HadAid) {
+ DoReadModified(CMD_READ_MODIFIED);
+ }
+#endif /* !defined(PURE3274) */
+}
+
+/* This takes in one character from the keyboard and places it on the
+ * screen.
+ */
+
+static void
+OneCharacter(c, insert)
+int c; /* character (Ebcdic) to be shoved in */
+int insert; /* are we in insert mode? */
+{
+ register int i, j;
+
+ if (IsProtected(CursorAddress)) {
+ RingBell("Protected Field");
+ return;
+ }
+ if (insert) {
+ /* is the last character in the field a blank or null? */
+ i = ScreenDec(FieldInc(CursorAddress));
+ j = GetHost(i);
+ if (!Disspace(j)) {
+ RingBell("No more room for insert");
+ return;
+ } else {
+ for (j = ScreenDec(i); i != CursorAddress;
+ j = ScreenDec(j), i = ScreenDec(i)) {
+ AddHost(i, (char)GetHost(j));
+ }
+ }
+ }
+ AddHost(CursorAddress, c);
+ TurnOnMdt(CursorAddress);
+ CursorAddress = ScreenInc(CursorAddress);
+ if (IsStartField(CursorAddress) &&
+ ((FieldAttributes(CursorAddress)&ATTR_AUTO_SKIP_MASK) ==
+ ATTR_AUTO_SKIP_VALUE)) {
+ Tab();
+ }
+}
+
+/*
+ * AcceptKeystroke()
+ *
+ * Processes one keystroke.
+ *
+ * Returns:
+ *
+ * 0 if this keystroke was NOT processed.
+ * 1 if everything went OK.
+ */
+
+int
+AcceptKeystroke(scancode, shiftstate)
+unsigned int
+ scancode, /* 3270 scancode */
+ shiftstate; /* The shift state */
+{
+ register int c;
+ register int i;
+ register int j;
+ enum ctlrfcn ctlrfcn;
+
+ if (scancode >= numberof(hits)) {
+ ExitString(
+ "Unknown scancode encountered in AcceptKeystroke.\n", 1);
+ /*NOTREACHED*/
+ }
+ ctlrfcn = hits[scancode].hit[HITNUM(shiftstate)].ctlrfcn;
+ c = hits[scancode].hit[HITNUM(shiftstate)].code;
+
+ if (!UnLocked || HadAid) {
+ if (HadAid) {
+ SendToIBM();
+ if (!EmptyChar()) {
+ return 0; /* nothing to do */
+ }
+ }
+#if !defined(PURE3274)
+ if (!HadAid && EmptyChar()) {
+ if ((ctlrfcn == FCN_RESET) || (ctlrfcn == FCN_MASTER_RESET)) {
+ UnLocked = 1;
+ }
+ }
+#endif /* !defined(PURE3274) */
+ if (!UnLocked) {
+ return 0;
+ }
+ }
+
+ /* now, either empty, or haven't seen aid yet */
+
+#if !defined(PURE3274)
+ /*
+ * If we are in transparent (output) mode, do something special
+ * with keystrokes.
+ */
+ if (TransparentClock == OutputClock) {
+ if (ctlrfcn == FCN_AID) {
+ UnLocked = 0;
+ InsertMode = 0;
+ AidByte = (c);
+ HadAid = 1;
+ } else {
+ switch (ctlrfcn) {
+ case FCN_ESCAPE:
+ StopScreen(1);
+ command(0);
+ if (shell_active == 0) {
+ ConnectScreen();
+ }
+ break;
+
+ case FCN_RESET:
+ case FCN_MASTER_RESET:
+ UnLocked = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ }
+ }
+#endif /* !defined(PURE3274) */
+
+ if (ctlrfcn == FCN_CHARACTER) {
+ /* Add the character to the buffer */
+ OneCharacter(c, InsertMode);
+ } else if (ctlrfcn == FCN_AID) { /* got Aid */
+ if (c == AID_CLEAR) {
+ LocalClearScreen(); /* Side effect is to clear 3270 */
+ }
+ ResetOiaOnlineA(&OperatorInformationArea);
+ SetOiaTWait(&OperatorInformationArea);
+ ResetOiaInsert(&OperatorInformationArea);
+ InsertMode = 0; /* just like a 3278 */
+ SetOiaSystemLocked(&OperatorInformationArea);
+ SetOiaModified();
+ UnLocked = 0;
+ AidByte = c;
+ HadAid = 1;
+ SendToIBM();
+ } else {
+ switch (ctlrfcn) {
+
+ case FCN_CURSEL:
+ c = FieldAttributes(CursorAddress)&ATTR_DSPD_MASK;
+ if (!FormattedScreen()
+ || ((c != ATTR_DSPD_DSPD) && (c != ATTR_DSPD_HIGH))) {
+ RingBell("Cursor not in selectable field");
+ } else {
+ i = ScreenInc(WhereAttrByte(CursorAddress));
+ c = GetHost(i);
+ if (c == DISP_QUESTION) {
+ AddHost(i, DISP_GREATER_THAN);
+ TurnOnMdt(i);
+ } else if (c == DISP_GREATER_THAN) {
+ AddHost(i, DISP_QUESTION);
+ TurnOffMdt(i);
+ } else if (c == DISP_BLANK || c == DISP_NULL
+ || c == DISP_AMPERSAND) {
+ UnLocked = 0;
+ InsertMode = 0;
+ ResetOiaOnlineA(&OperatorInformationArea);
+ SetOiaTWait(&OperatorInformationArea);
+ SetOiaSystemLocked(&OperatorInformationArea);
+ ResetOiaInsert(&OperatorInformationArea);
+ SetOiaModified();
+ if (c == DISP_AMPERSAND) {
+ TurnOnMdt(i); /* Only for & type */
+ AidByte = AID_ENTER;
+ } else {
+ AidByte = AID_SELPEN;
+ }
+ HadAid = 1;
+ SendToIBM();
+ } else {
+ RingBell(
+ "Cursor not in a selectable field (designator)");
+ }
+ }
+ break;
+
+#if !defined(PURE3274)
+ case FCN_ERASE:
+ if (IsProtected(ScreenDec(CursorAddress))) {
+ RingBell("Protected Field");
+ } else {
+ CursorAddress = ScreenDec(CursorAddress);
+ Delete(CursorAddress, ScreenInc(CursorAddress));
+ }
+ break;
+ case FCN_WERASE:
+ j = CursorAddress;
+ i = ScreenDec(j);
+ if (IsProtected(i)) {
+ RingBell("Protected Field");
+ } else {
+ SetXIsProtected();
+ while ((!XIsProtected(i) && Disspace(GetHost(i)))
+ && (i != j)) {
+ i = ScreenDec(i);
+ }
+ /* we are pointing at a character in a word, or
+ * at a protected position
+ */
+ while ((!XIsProtected(i) && !Disspace(GetHost(i)))
+ && (i != j)) {
+ i = ScreenDec(i);
+ }
+ /* we are pointing at a space, or at a protected
+ * position
+ */
+ CursorAddress = ScreenInc(i);
+ Delete(CursorAddress, j);
+ }
+ break;
+
+ case FCN_FERASE:
+ if (IsProtected(CursorAddress)) {
+ RingBell("Protected Field");
+ } else {
+ CursorAddress = ScreenInc(CursorAddress); /* for btab */
+ BackTab();
+ EraseEndOfField();
+ }
+ break;
+
+ case FCN_RESET:
+ if (InsertMode) {
+ InsertMode = 0;
+ ResetOiaInsert(&OperatorInformationArea);
+ SetOiaModified();
+ }
+ break;
+ case FCN_MASTER_RESET:
+ if (InsertMode) {
+ InsertMode = 0;
+ ResetOiaInsert(&OperatorInformationArea);
+ SetOiaModified();
+ }
+ RefreshScreen();
+ break;
+#endif /* !defined(PURE3274) */
+
+ case FCN_UP:
+ CursorAddress = ScreenUp(CursorAddress);
+ break;
+
+ case FCN_LEFT:
+ CursorAddress = ScreenDec(CursorAddress);
+ break;
+
+ case FCN_RIGHT:
+ CursorAddress = ScreenInc(CursorAddress);
+ break;
+
+ case FCN_DOWN:
+ CursorAddress = ScreenDown(CursorAddress);
+ break;
+
+ case FCN_DELETE:
+ if (IsProtected(CursorAddress)) {
+ RingBell("Protected Field");
+ } else {
+ Delete(CursorAddress, ScreenInc(CursorAddress));
+ }
+ break;
+
+ case FCN_INSRT:
+ InsertMode = !InsertMode;
+ if (InsertMode) {
+ SetOiaInsert(&OperatorInformationArea);
+ } else {
+ ResetOiaInsert(&OperatorInformationArea);
+ }
+ SetOiaModified();
+ break;
+
+ case FCN_HOME:
+ Home();
+ break;
+
+ case FCN_NL:
+ /* The algorithm is to look for the first unprotected
+ * column after column 0 of the following line. Having
+ * found that unprotected column, we check whether the
+ * cursor-address-at-entry is at or to the right of the
+ * LeftMargin AND the LeftMargin column of the found line
+ * is unprotected. If this conjunction is true, then
+ * we set the found pointer to the address of the LeftMargin
+ * column in the found line.
+ * Then, we set the cursor address to the found address.
+ */
+ i = SetBufferAddress(ScreenLine(ScreenDown(CursorAddress)), 0);
+ j = ScreenInc(WhereAttrByte(CursorAddress));
+ do {
+ if (IsUnProtected(i)) {
+ break;
+ }
+ /* Again (see comment in Home()), this COULD be a problem
+ * with an unformatted screen.
+ */
+ /* If there was a field with only an attribute byte,
+ * we may be pointing to the attribute byte of the NEXT
+ * field, so just look at the next byte.
+ */
+ if (IsStartField(i)) {
+ i = ScreenInc(i);
+ } else {
+ i = ScreenInc(FieldInc(i));
+ }
+ } while (i != j);
+ if (!IsUnProtected(i)) { /* couldn't find unprotected */
+ i = SetBufferAddress(0,0);
+ }
+ if (OptLeftMargin <= ScreenLineOffset(CursorAddress)) {
+ if (IsUnProtected(SetBufferAddress(ScreenLine(i),
+ OptLeftMargin))) {
+ i = SetBufferAddress(ScreenLine(i), OptLeftMargin);
+ }
+ }
+ CursorAddress = i;
+ break;
+
+ case FCN_EINP:
+ if (!FormattedScreen()) {
+ i = CursorAddress;
+ TurnOffMdt(i);
+ do {
+ AddHost(i, 0);
+ i = ScreenInc(i);
+ } while (i != CursorAddress);
+ } else {
+ /*
+ * The algorithm is: go through each unprotected
+ * field on the screen, clearing it out. When
+ * we are at the start of a field, skip that field
+ * if its contents are protected.
+ */
+ i = j = FieldInc(CursorAddress);
+ do {
+ if (IsUnProtected(ScreenInc(i))) {
+ i = ScreenInc(i);
+ TurnOffMdt(i);
+ do {
+ AddHost(i, 0);
+ i = ScreenInc(i);
+ } while (!IsStartField(i));
+ } else {
+ i = FieldInc(i);
+ }
+ } while (i != j);
+ }
+ Home();
+ break;
+
+ case FCN_EEOF:
+ EraseEndOfField();
+ break;
+
+ case FCN_SPACE:
+ OneCharacter(DISP_BLANK, InsertMode); /* Add cent */
+ break;
+
+ case FCN_CENTSIGN:
+ OneCharacter(DISP_CENTSIGN, InsertMode); /* Add cent */
+ break;
+
+ case FCN_FM:
+ OneCharacter(DISP_FM, InsertMode); /* Add field mark */
+ break;
+
+ case FCN_DP:
+ if (IsProtected(CursorAddress)) {
+ RingBell("Protected Field");
+ } else {
+ OneCharacter(DISP_DUP, InsertMode);/* Add dup character */
+ Tab();
+ }
+ break;
+
+ case FCN_TAB:
+ Tab();
+ break;
+
+ case FCN_BTAB:
+ BackTab();
+ break;
+
+#ifdef NOTUSED /* Actually, this is superseded by unix flow
+ * control.
+ */
+ case FCN_XOFF:
+ Flow = 0; /* stop output */
+ break;
+
+ case FCN_XON:
+ if (!Flow) {
+ Flow = 1; /* turn it back on */
+ DoTerminalOutput();
+ }
+ break;
+#endif /* NOTUSED */
+
+#if !defined(PURE3274)
+ case FCN_ESCAPE:
+ /* FlushChar(); do we want to flush characters from before? */
+ StopScreen(1);
+ command(0);
+ if (shell_active == 0) {
+ ConnectScreen();
+ }
+ break;
+
+ case FCN_DISC:
+ StopScreen(1);
+ suspend();
+ setconnmode();
+ ConnectScreen();
+ break;
+
+ case FCN_RESHOW:
+ RefreshScreen();
+ break;
+
+ case FCN_SETTAB:
+ OptColTabs[ScreenLineOffset(CursorAddress)] = 1;
+ break;
+
+ case FCN_DELTAB:
+ OptColTabs[ScreenLineOffset(CursorAddress)] = 0;
+ break;
+
+ /*
+ * Clear all tabs, home line, and left margin.
+ */
+ case FCN_CLRTAB:
+ for (i = 0; i < sizeof OptColTabs; i++) {
+ OptColTabs[i] = 0;
+ }
+ OptHome = 0;
+ OptLeftMargin = 0;
+ break;
+
+ case FCN_COLTAB:
+ ColTab();
+ break;
+
+ case FCN_COLBAK:
+ ColBak();
+ break;
+
+ case FCN_INDENT:
+ ColTab();
+ OptLeftMargin = ScreenLineOffset(CursorAddress);
+ break;
+
+ case FCN_UNDENT:
+ ColBak();
+ OptLeftMargin = ScreenLineOffset(CursorAddress);
+ break;
+
+ case FCN_SETMRG:
+ OptLeftMargin = ScreenLineOffset(CursorAddress);
+ break;
+
+ case FCN_SETHOM:
+ OptHome = ScreenLine(CursorAddress);
+ break;
+
+ /*
+ * Point to first character of next unprotected word on
+ * screen.
+ */
+ case FCN_WORDTAB:
+ i = CursorAddress;
+ SetXIsProtected();
+ while (!XIsProtected(i) && !Disspace(GetHost(i))) {
+ i = ScreenInc(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ /* i is either protected, a space (blank or null),
+ * or wrapped
+ */
+ while (XIsProtected(i) || Disspace(GetHost(i))) {
+ i = ScreenInc(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ CursorAddress = i;
+ break;
+
+ case FCN_WORDBACKTAB:
+ i = ScreenDec(CursorAddress);
+ SetXIsProtected();
+ while (XIsProtected(i) || Disspace(GetHost(i))) {
+ i = ScreenDec(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ /* i is pointing to a character IN an unprotected word
+ * (or i wrapped)
+ */
+ while (!Disspace(GetHost(i))) {
+ i = ScreenDec(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ CursorAddress = ScreenInc(i);
+ break;
+
+ /* Point to last non-blank character of this/next
+ * unprotected word.
+ */
+ case FCN_WORDEND:
+ i = ScreenInc(CursorAddress);
+ SetXIsProtected();
+ while (XIsProtected(i) || Disspace(GetHost(i))) {
+ i = ScreenInc(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ /* we are pointing at a character IN an
+ * unprotected word (or we wrapped)
+ */
+ while (!Disspace(GetHost(i))) {
+ i = ScreenInc(i);
+ if (i == CursorAddress) {
+ break;
+ }
+ }
+ CursorAddress = ScreenDec(i);
+ break;
+
+ /* Get to last non-blank of this/next unprotected
+ * field.
+ */
+ case FCN_FIELDEND:
+ i = LastOfField(CursorAddress);
+ if (i != CursorAddress) {
+ CursorAddress = i; /* We moved; take this */
+ } else {
+ j = FieldInc(CursorAddress); /* Move to next field */
+ i = LastOfField(j);
+ if (i != j) {
+ CursorAddress = i; /* We moved; take this */
+ }
+ /* else - nowhere else on screen to be; stay here */
+ }
+ break;
+#endif /* !defined(PURE3274) */
+
+ default:
+ /* We don't handle this yet */
+ RingBell("Function not implemented");
+ }
+ }
+ return 1; /* We did something! */
+}
+
+
+/*
+ * We get data from the terminal. We keep track of the shift state
+ * (including ALT, CONTROL), and then call AcceptKeystroke to actually
+ * process any non-shift keys.
+ */
+
+int
+DataFrom3270(buffer, count)
+unsigned char *buffer; /* where the data is */
+int count; /* how much data there is */
+{
+ int origCount;
+
+ origCount = count;
+
+ while (count) {
+ if (*buffer >= numberof(hits)) {
+ ExitString("Unknown scancode encountered in DataFrom3270.\n", 1);
+ /*NOTREACHED*/
+ }
+
+ switch (hits[*buffer].hit[HITNUM(rememberedshiftstate)].ctlrfcn) {
+
+ case FCN_MAKE_SHIFT:
+ rememberedshiftstate |= (SHIFT_RIGHT|SHIFT_UPSHIFT);
+ break;
+ case FCN_BREAK_SHIFT:
+ rememberedshiftstate &= ~(SHIFT_RIGHT|SHIFT_UPSHIFT);
+ break;
+ case FCN_MAKE_ALT:
+ rememberedshiftstate |= SHIFT_ALT;
+ break;
+ case FCN_BREAK_ALT:
+ rememberedshiftstate &= ~SHIFT_ALT;
+ break;
+ default:
+ if (AcceptKeystroke(*buffer, rememberedshiftstate) == 0) {
+ return(origCount-count);
+ }
+ break;
+ }
+ buffer++;
+ count--;
+ }
+ return(origCount-count);
+}
diff --git a/usr.bin/tn3270/ctlr/oia.c b/usr.bin/tn3270/ctlr/oia.c
new file mode 100644
index 0000000..1f80359
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/oia.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)oia.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Routines to maintain the Operator Information Area.
+ */
+
+#include "../general/general.h"
+
+#include "oia.h"
+#include "../general/globals.h"
+
+
+init_oia()
+{
+ ClearElement(OperatorInformationArea);
+}
diff --git a/usr.bin/tn3270/ctlr/oia.h b/usr.bin/tn3270/ctlr/oia.h
new file mode 100644
index 0000000..a1414ca
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/oia.h
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)oia.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This file describes the Operator Information Area in the 3270.
+ *
+ * Our OIA looks like that used by the 3270 PC and PC 3270 products.
+ */
+
+#define INCLUDED_OIA
+
+typedef struct {
+ char
+ online_ownership,
+ character_selection,
+ shift_state,
+ pss_group_1,
+ highlight_group_1,
+ color_group_1,
+ insert,
+ input_inhibited[5],
+ pss_group_2,
+ highlight_group_2,
+ color_group_2,
+ comm_error_reminder,
+ printer_status,
+ reserved_group_14,
+ reserved_group_15,
+ autokey_play_record_status,
+ autokey_abort_pause_status,
+ enlarge_state;
+} OIA;
+
+/* Bits in online_ownership */
+#define OIA_SETUP 0x80
+#define OIA_TEST 0x40
+#define OIA_SSCP_LU 0x20
+#define OIA_LU_LU 0x10
+#define OIA_UNOWNED 0x08
+#define OIA_SUBSYSTEM_READY 0x04
+
+/* Bit in character_selection */
+#define OIA_EXTENDED_SELECT 0x80
+#define OIA_APL 0x40
+#define OIA_KANA 0x20
+#define OIA_ALPHA 0x10
+#define OIA_TEXT 0x08
+
+/* Bits in shift_state */
+#define OIA_NUMERIC 0x80
+#define OIA_UPPER_SHIFT 0x40
+
+/* Bits in pss_group_1, highlight_group_1, and color_group_1 */
+#define OIA_SELECTABLE 0x80
+#define OIA_FIELD_INHERIT 0x40
+
+/* Bits in insert */
+#define OIA_INSERT_MODE 0x80
+
+/* We define this to be a 'long' followed by a 'char' (5 bytes) */
+
+#define OIA_NON_RESETTABLE 0x80
+#define OIA_SECURITY_KEY 0x40
+#define OIA_MACHINE_CHECK 0x20
+#define OIA_COMM_CHECK 0x10
+#define OIA_PROGRAM_CHECK 0x08
+#define OIA_RETRY 0x04
+#define OIA_DEVICE_NOT_WORKING 0x02
+#define OIA_DEVICE_VERY_BUSY 0x01
+
+#define OIA_DEVICE_BUSY 0x80
+#define OIA_TERMINAL_WAIT 0x40
+#define OIA_MINUS_SYMBOL 0x20
+#define OIA_MINUS_FUNCTION 0x10
+#define OIA_TOO_MUCH_ENTERED 0x08
+#define OIA_NOT_ENOUGH_ENTERED 0x04
+#define OIA_WRONG_NUMBER 0x02
+#define OIA_NUMERIC_FIELD 0x01
+
+#define OIA_OP_UNAUTHORIZED 0x80
+#define OIA_OP_UNAUTHORIZED_MIN 0x40
+#define OIA_INVALID_DEAD_KEY_COMBO 0x20
+#define OIA_WRONG_PLACE 0x10
+
+#define OIA_MESSAGE_PENDING 0x80
+#define OIA_PARTITION_WAIT 0x40
+#define OIA_SYSTEM_WAIT 0x20
+#define OIA_HARDWARE_MISMATCH 0x10
+#define OIA_LOGICAL_TERM_NOT_CONF 0x08
+
+
+#define OIA_AUTOKEY_INHIBIT 0x80
+#define OIA_API_INHIBIT 0x40
+
+/* Bits in pss_group_2 */
+#define OIA_PS_SELECTED 0x80
+#define OIA_PC_DISPLAY_DISABLE 0x40
+
+/* Bits in highlight_group_2 and color_group_2 */
+#define OIA_SELECTED 0x80
+
+/* Bits in comm_error_reminder */
+#define OIA_COMM_ERROR 0x80
+#define OIA_RTM 0x40
+
+/* Bits in printer_status */
+#define OIA_PRINT_NOT_CUSTOM 0x80
+#define OIA_PRINTER_MALFUNCTION 0x40
+#define OIA_PRINTER_PRINTING 0x20
+#define OIA_ASSIGN_PRINTER 0x10
+#define OIA_WHAT_PRINTER 0x08
+#define OIA_PRINTER_ASSIGNMENT 0x04
+
+/* Bits in autokey_play_record_status */
+#define OIA_PLAY 0x80
+#define OIA_RECORD 0x40
+
+/* Bits in autokey_abort_pause_status */
+#define OIA_RECORDING_OVERFLOW 0x80
+#define OIA_PAUSE 0x40
+
+/* Bits in enlarge_state */
+#define OIA_WINDOW_IS_ENLARGED 0x80
+
+/* Define functions to set and read the oia */
+
+#define SetOiaOnlineA(oia) SetOiaMyJob((oia)) /* Side-effect */
+#define ResetOiaOnlineA(oia) \
+ /* Nothing defined for this */
+
+#define IsOiaReady3274(oia) ((oia)->online_ownership&OIA_SUBSYSTEM_READY)
+#define ResetOiaReady3274(oia) (oia)->online_ownership &= ~OIA_SUBSYSTEM_READY
+#define SetOiaReady3274(oia) (oia)->online_ownership |= OIA_SUBSYSTEM_READY
+
+#define IsOiaMyJob(oia) ((oia)->online_ownership&OIA_LU_LU)
+#define ResetOiaMyJob(oia) (oia)->online_ownership &= ~OIA_LU_LU
+#define SetOiaMyJob(oia) (oia)->online_ownership |= OIA_LU_LU
+
+#define IsOiaInsert(oia) ((oia)->online_ownership&OIA_INSERT_MODE)
+#define ResetOiaInsert(oia) (oia)->online_ownership &= ~OIA_INSERT_MODE
+#define SetOiaInsert(oia) (oia)->online_ownership |= OIA_INSERT_MODE
+
+#define IsOiaSystemLocked(oia) ((oia)->input_inhibited[3]&OIA_SYSTEM_WAIT)
+#define ResetOiaSystemLocked(oia) \
+ (oia)->input_inhibited[3] &= ~OIA_SYSTEM_WAIT
+#define SetOiaSystemLocked(oia) (oia)->input_inhibited[3] |= OIA_SYSTEM_WAIT
+
+#define IsOiaTWait(oia) ((oia)->input_inhibited[1]&OIA_TERMINAL_WAIT)
+#define ResetOiaTWait(oia) (oia)->input_inhibited[1] &= ~OIA_TERMINAL_WAIT
+#define SetOiaTWait(oia) (oia)->input_inhibited[1] |= OIA_TERMINAL_WAIT
+
+#define IsOiaApiInhibit(oia) ((oia)->input_inhibited[4] & OIA_API_INHIBIT)
+#define ResetOiaApiInhibit(oia) ((oia)->input_inhibited[4] &= ~OIA_API_INHIBIT)
+#define SetOiaApiInhibit(oia) ((oia)->input_inhibited[4] |= OIA_API_INHIBIT)
+
+/* A macro to let the world know that someone has modified the OIA. */
+#define SetOiaModified() oia_modified = 1
+#define SetPsModified() ps_modified = 1
diff --git a/usr.bin/tn3270/ctlr/options.c b/usr.bin/tn3270/ctlr/options.c
new file mode 100644
index 0000000..38a2d43
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/options.c
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * this file contains the definitions, initialization, and processing of
+ * commands to handle the various local options (APL ON, etc.)
+ */
+
+#include "options.h"
+
+#include "../general/globals.h"
+#include "declare.h"
+
+void
+OptInit()
+{
+ register int i;
+
+ OptAPLmode = 0;
+ OptNullProcessing = 1; /* improved null processing */
+ OptZonesMode = 0; /* zones mode off */
+ OptEnterNL = 0; /* regular enter/new line keys */
+ OptColFieldTab = 0; /* regular column/field tab keys */
+ OptPacing = 1; /* do pacing */
+ OptAlphaInNumeric = 0; /* allow alpha in numeric fields */
+ for (i = 0; i < sizeof OptColTabs; i++) {
+ OptColTabs[i] = ((i%8) == 0); /* every 8 columns */
+ }
+ OptHome = 0;
+ OptLeftMargin = 0;
+ OptWordWrap = 0;
+}
+
+OptOrder(pointer, count, control)
+unsigned char *pointer;
+int count;
+int control;
+{
+ int i, j, character, origCount;
+
+ origCount = count;
+
+ if (count == 0) {
+ return(0);
+ }
+ character = *pointer&0xff;
+ pointer++;
+ count--;
+ switch (character) {
+ case 0xa0:
+ OptAPLmode = 1;
+ break;
+ case 0x61:
+ OptAPLmode = 0;
+ break;
+ case 0x95:
+ OptNullProcessing = 0;
+ break;
+ case 0xd5:
+ OptNullProcessing = 1;
+ break;
+ case 0xa9:
+ OptZonesMode = 1;
+ break;
+ case 0xe9:
+ OptZonesMode = 0;
+ break;
+ case 0x85:
+ OptEnterNL = 1;
+ break;
+ case 0xc5:
+ OptEnterNL = 0;
+ break;
+ case 0x83:
+ OptColFieldTab = 1;
+ break;
+ case 0xc3:
+ OptColFieldTab = 0;
+ break;
+ case 0x97:
+ OptPacing = 0;
+ break;
+ case 0xd7:
+ OptPacing = 1;
+ break;
+ case 0xa5:
+ OptAlphaInNumeric = 1;
+ break;
+ case 0xe5:
+ OptAlphaInNumeric = 0;
+ break;
+ case 0xe3:
+ if (!control && count < 30) {
+ return(0); /* want more! */
+ }
+ for (i = 0; i < sizeof OptColTabs; i++) {
+ OptColTabs[i] = 0;
+ }
+ if (!count) {
+ break;
+ }
+ j = (*pointer&0xff)-0x40;
+ count--;
+ pointer++;
+ if (j < 0 || j >= 24) {
+ break;
+ }
+ OptHome = j;
+ if (!count) {
+ break;
+ }
+ j = (*pointer&0xff)-0x40;
+ count--;
+ pointer++;
+ if (j < 0 || j >= 80) {
+ break;
+ }
+ OptLeftMargin = j;
+ if (!count) {
+ break;
+ }
+ i = count;
+ if (i > 28) {
+ i = 28;
+ }
+ while (i) {
+ j = (*pointer&0xff)-0x40;
+ if (j < 0 || j >= sizeof OptColTabs) {
+ break;
+ }
+ OptColTabs[j] = 1;
+ i --;
+ pointer++;
+ count--;
+ }
+ break;
+ case 0xa6:
+ OptWordWrap = 1;
+ break;
+ case 0xe6:
+ OptWordWrap = 0;
+ break;
+ default:
+ break;
+ }
+ return(origCount - count);
+}
diff --git a/usr.bin/tn3270/ctlr/options.h b/usr.bin/tn3270/ctlr/options.h
new file mode 100644
index 0000000..fbf5b10
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/options.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)options.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * the various options that run our life. Very few of these are implemented
+ * as yet.
+ */
+
+#define INCLUDED_OPTIONS
diff --git a/usr.bin/tn3270/ctlr/outbound.c b/usr.bin/tn3270/ctlr/outbound.c
new file mode 100644
index 0000000..72432f2
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/outbound.c
@@ -0,0 +1,605 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)outbound.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "../general/general.h"
+
+#include "hostctlr.h"
+#include "oia.h"
+#include "screen.h"
+#include "../api/ebc_disp.h"
+
+#include "../general/globals.h"
+#include "externs.h"
+#include "declare.h"
+
+#define SetHighestLowest(position) { \
+ if (position < Lowest) { \
+ Lowest = position; \
+ } \
+ if (position > Highest) { \
+ Highest = position; \
+ } \
+ }
+
+
+static int LastWasTerminated = 1; /* was "control" = 1 last time? */
+
+/* some globals */
+
+#if !defined(PURE3274)
+int OutputClock; /* what time it is */
+int TransparentClock; /* time we were last in transparent */
+#endif /* !defined(PURE3274) */
+
+char CIABuffer[64] = {
+ 0x40, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f
+};
+
+static struct orders_def orders_def[] = ORDERS_DEF;
+
+/*
+ * init_ctlr()
+ *
+ * Initialize all data from the 'data' portion to their startup values.
+ */
+
+void
+init_ctlr()
+{
+ LastWasTerminated = 1;
+ init_inbound();
+ init_oia();
+}
+
+
+FieldInc(position)
+register int position; /* Position in previous field */
+{
+ register ScreenImage *ptr;
+
+ ptr = (ScreenImage *)memNSchr((char *)Host+position+1, ATTR_MASK,
+ HighestScreen()-position, ATTR_MASK, sizeof Host[0]);
+ if (ptr == 0) {
+ ptr = (ScreenImage *)memNSchr((char *)Host+LowestScreen(), ATTR_MASK,
+ position-LowestScreen(), ATTR_MASK, sizeof Host[0]);
+ if (ptr == 0) {
+ return LowestScreen();
+ }
+ }
+ return ptr-Host;
+}
+
+FieldDec(position)
+int position;
+{
+ register ScreenImage *ptr;
+
+ ptr = (ScreenImage *)memNSchr((char *)(Host+position)-1, ATTR_MASK,
+ position-LowestScreen(), ATTR_MASK, -sizeof Host[0]);
+ if (ptr == 0) {
+ ptr = (ScreenImage *)memNSchr((char *)Host+HighestScreen(), ATTR_MASK,
+ HighestScreen()-position, ATTR_MASK, -sizeof Host[0]);
+ if (ptr == 0) {
+ return LowestScreen();
+ }
+ }
+ return ptr-Host;
+}
+
+/* Clear3270 - called to clear the screen */
+
+void
+Clear3270()
+{
+ ClearArray(Host);
+ DeleteAllFields(); /* get rid of all fields */
+ BufferAddress = SetBufferAddress(0,0);
+ CursorAddress = SetBufferAddress(0,0);
+ Lowest = LowestScreen();
+ Highest = HighestScreen();
+}
+
+/* AddHost - called to add a character to the buffer.
+ * We use a macro in this module, since we call it so
+ * often from loops.
+ *
+ * NOTE: It is a macro, so don't go around using AddHost(p, *c++), or
+ * anything similar. (I don't define any temporary variables, again
+ * just for the speed.)
+ */
+void
+AddHost(position, character)
+int position;
+char character;
+{
+# define AddHostA(p,c) \
+ { \
+ if (IsStartField(p)) { \
+ DeleteField(p); \
+ Highest = HighestScreen(); \
+ Lowest = LowestScreen(); \
+ SetHighestLowest(p); \
+ } \
+ SetHost(p, c); \
+ }
+# define AddHost(p,c) \
+ { \
+ if (c != GetHost(p)) { \
+ SetHighestLowest(p); \
+ } \
+ AddHostA(p,c); \
+ } /* end of macro of AddHost */
+
+ AddHost(position, character);
+}
+
+/* returns the number of characters consumed */
+int
+DataFromNetwork(Buffer, count, control)
+char *Buffer; /* what the data is */
+register int count; /* and how much there is */
+int control; /* this buffer ended block? */
+{
+ int origCount;
+ register unsigned char *buffer = (unsigned char *)Buffer;
+ register int c;
+ register int i;
+ static int Command;
+ static int Wcc;
+
+ origCount = count;
+
+ /*
+ * If this is the start of a new data stream, then look
+ * for an op-code and (possibly) a WCC.
+ */
+ if (LastWasTerminated) {
+
+ if (count < 2) {
+ if (count == 0) {
+ ExitString("Short count received from host!\n", 1);
+ return(count);
+ }
+ Command = buffer[0];
+ switch (Command) { /* This had better be a read command */
+ case CMD_READ_MODIFIED:
+ case CMD_SNA_READ_MODIFIED:
+ case CMD_SNA_READ_MODIFIED_ALL:
+ SetOiaOnlineA(&OperatorInformationArea);
+ SetOiaModified();
+ DoReadModified(Command);
+ break;
+ case CMD_READ_BUFFER:
+ case CMD_SNA_READ_BUFFER:
+ SetOiaOnlineA(&OperatorInformationArea);
+ SetOiaModified();
+ DoReadBuffer();
+ break;
+ default:
+ {
+ char s_buffer[100];
+
+ sprintf(s_buffer,
+ "Unexpected read command code 0x%x received.\n",
+ Command);
+ ExitString(s_buffer, 1);
+ break;
+ }
+ }
+ return(1); /* We consumed everything */
+ }
+ Command = buffer[0];
+ Wcc = buffer[1];
+ if (Wcc & WCC_RESET_MDT) {
+ i = c = WhereAttrByte(LowestScreen());
+ do {
+ if (HasMdt(i)) {
+ TurnOffMdt(i);
+ }
+ i = FieldInc(i);
+ } while (i != c);
+ }
+
+ switch (Command) {
+ case CMD_ERASE_WRITE:
+ case CMD_ERASE_WRITE_ALTERNATE:
+ case CMD_SNA_ERASE_WRITE:
+ case CMD_SNA_ERASE_WRITE_ALTERNATE:
+ {
+ int newlines, newcolumns;
+
+ SetOiaOnlineA(&OperatorInformationArea);
+ ResetOiaTWait(&OperatorInformationArea);
+ SetOiaModified();
+ if ((Command == CMD_ERASE_WRITE)
+ || (Command == CMD_SNA_ERASE_WRITE)) {
+ newlines = 24;
+ newcolumns = 80;
+ } else {
+ newlines = MaxNumberLines;
+ newcolumns = MaxNumberColumns;
+ }
+ if ((newlines != NumberLines)
+ || (newcolumns != NumberColumns)) {
+ /*
+ * The LocalClearScreen() is really for when we
+ * are going from a larger screen to a smaller
+ * screen, and we need to clear off the stuff
+ * at the end of the lines, or the lines at
+ * the end of the screen.
+ */
+ LocalClearScreen();
+ NumberLines = newlines;
+ NumberColumns = newcolumns;
+ ScreenSize = NumberLines * NumberColumns;
+ }
+ Clear3270();
+#if !defined(PURE3274)
+ if (TransparentClock == OutputClock) {
+ TransStop();
+ }
+#endif /* !defined(PURE3274) */
+ break;
+ }
+
+ case CMD_ERASE_ALL_UNPROTECTED:
+ case CMD_SNA_ERASE_ALL_UNPROTECTED:
+ SetOiaOnlineA(&OperatorInformationArea);
+ ResetOiaTWait(&OperatorInformationArea);
+ SetOiaModified();
+ CursorAddress = HighestScreen()+1;
+ for (i = LowestScreen(); i <= HighestScreen(); i = ScreenInc(i)) {
+ if (IsUnProtected(i)) {
+ if (CursorAddress > i) {
+ CursorAddress = i;
+ }
+ AddHost(i, '\0');
+ }
+ if (HasMdt(i)) {
+ TurnOffMdt(i);
+ }
+ }
+ if (CursorAddress == HighestScreen()+1) {
+ CursorAddress = SetBufferAddress(0,0);
+ }
+ UnLocked = 1;
+ AidByte = 0;
+ ResetOiaSystemLocked(&OperatorInformationArea);
+ SetOiaModified();
+ TerminalIn();
+ break;
+ case CMD_WRITE:
+ case CMD_SNA_WRITE:
+ SetOiaOnlineA(&OperatorInformationArea);
+ ResetOiaTWait(&OperatorInformationArea);
+ SetOiaModified();
+ break;
+ default:
+ {
+ char s_buffer[100];
+
+ sprintf(s_buffer,
+ "Unexpected write command code 0x%x received.\n",
+ Command);
+ ExitString(s_buffer, 1);
+ break;
+ }
+ }
+
+ count -= 2; /* strip off command and wcc */
+ buffer += 2;
+
+ } else {
+#if !defined(PURE3274)
+ if (TransparentClock == OutputClock) {
+ TransOut(buffer, count, -1, control);
+ count = 0;
+ }
+#endif /* !defined(PURE3274) */
+ }
+ LastWasTerminated = 0; /* then, reset at end... */
+
+ while (count) {
+ count--;
+ c = *buffer++;
+ if (IsOrder(c)) {
+ /* handle an order */
+ switch (c) {
+# define Ensure(x) if (count < x) { \
+ if (!control) { \
+ return(origCount-(count+1)); \
+ } else { \
+ /* XXX - should not occur */ \
+ count = 0; \
+ break; \
+ } \
+ }
+ case ORDER_SF:
+ Ensure(1);
+ c = *buffer++;
+ count--;
+ if ( ! (IsStartField(BufferAddress) &&
+ FieldAttributes(BufferAddress) == c)) {
+ SetHighestLowest(BufferAddress);
+ NewField(BufferAddress,c);
+ }
+ BufferAddress = ScreenInc(BufferAddress);
+ break;
+ case ORDER_SBA:
+ Ensure(2);
+ i = buffer[0];
+ c = buffer[1];
+#if !defined(PURE3274)
+ /* Check for transparent write */
+ if ((i == 0) && ((c == 0) || (c == 1) || (c == 5))) {
+ TransparentClock = OutputClock+1;
+ TransOut(buffer+2, count-2, c, control);
+ buffer += count;
+ count -= count;
+ break;
+ }
+#endif /* !defined(PURE3274) */
+ BufferAddress = Addr3270(i, c);
+ buffer += 2;
+ count -= 2;
+ break;
+ case ORDER_IC:
+ CursorAddress = BufferAddress;
+ break;
+ /*
+ * XXX - PT is supposed to null fill the screen buffer
+ * under certain draconian conditions.
+ */
+ case ORDER_PT:
+ i = BufferAddress;
+ do {
+ if (IsStartField(i)) {
+ if (!IsProtected(ScreenInc(i))) {
+ break;
+ }
+ }
+ i = ScreenInc(i);
+ } while (i != HighestScreen());
+ BufferAddress = ScreenInc(i);
+ break;
+ case ORDER_RA:
+ Ensure(3);
+ i = Addr3270(buffer[0], buffer[1]);
+ if ((i < 0) || (i > HighestScreen())) {
+ char s_buffer[200];
+
+ sprintf(s_buffer, "tn3270: %s%d.\n\t%s%d%s%d%s\n",
+ "Invalid 3270 order 'Repeat to Address' to address ",
+ i,
+ "(Screen currently set to ",
+ NumberLines,
+ " by ",
+ NumberColumns,
+ ".)");
+ ExitString(s_buffer, 1);
+ /*NOTREACHED*/
+ }
+ c = buffer[2];
+ if (c == ORDER_GE) {
+ Ensure(4);
+ c = buffer[3];
+ buffer += 4;
+ count -= 4;
+ } else {
+ buffer += 3;
+ count -= 3;
+ }
+ do {
+ AddHost(BufferAddress, ebc_disp[c]);
+ BufferAddress = ScreenInc(BufferAddress);
+ } while (BufferAddress != i);
+ break;
+ case ORDER_EUA: /* (from [here,there), ie: half open interval] */
+ Ensure(2);
+ /*
+ * Compiler error - msc version 4.0:
+ * "expression too complicated".
+ */
+ i = WhereAttrByte(BufferAddress);
+ c = FieldAttributes(i);
+ i = Addr3270(buffer[0], buffer[1]);
+ if ((i < 0) || (i > HighestScreen())) {
+ char s_buffer[200];
+
+ sprintf(s_buffer, "tn3270: %s%d.\n\t%s%d%s%d%s\n",
+ "Invalid 3270 order 'Erase Unprotected to Address' to address ",
+ i,
+ "(Screen currently set to ",
+ NumberLines,
+ " by ",
+ NumberColumns,
+ ".)");
+ ExitString(s_buffer, 1);
+ /*NOTREACHED*/
+ }
+ do {
+ if (IsStartField(BufferAddress)) {
+ c = FieldAttributes(BufferAddress);
+ } else if (!IsProtectedAttr(BufferAddress, c)) {
+ AddHost(BufferAddress, 0);
+ }
+ BufferAddress = ScreenInc(BufferAddress);
+ } while (i != BufferAddress);
+ buffer += 2;
+ count -= 2;
+ break;
+ case ORDER_GE:
+ Ensure(2);
+ /* XXX Should do SOMETHING! */
+ /* XXX buffer += 0; */
+ /* XXX count -= 0; *//* For now, just use this character */
+ break;
+ case ORDER_YALE: /* special YALE defined order */
+ Ensure(2); /* need at least two characters */
+ if (*buffer == 0x5b) {
+ i = OptOrder(buffer+1, count-1, control);
+ if (i == 0) {
+ return(origCount-(count+1)); /* come here again */
+ } else {
+ buffer += 1 + i;
+ count -= (1 + i);
+ }
+ }
+ break;
+ default:
+ {
+ char s_buffer[100];
+ static struct orders_def unk_order
+ = { 0, "??", "(unknown)" };
+ struct orders_def *porder = &unk_order;
+ int s_i;
+
+ for (s_i = 0; s_i <= highestof(orders_def); s_i++) {
+ if (orders_def[s_i].code == c) {
+ porder = &orders_def[s_i];
+ break;
+ }
+ }
+ sprintf(s_buffer,
+ "Unsupported order '%s' (%s, 0x%x) received.\n",
+ porder->long_name, porder->short_name, c);
+ ExitString(s_buffer, 1);
+ /*NOTREACHED*/
+ }
+ }
+ if (count < 0) {
+ count = 0;
+ }
+ } else {
+ /* Data comes in large clumps - take it all */
+ i = BufferAddress;
+ AddHostA(i, ebc_disp[c]);
+ SetHighestLowest(i);
+ i = ScreenInc(i);
+ c = *buffer;
+ while (count && !IsOrder(c)) {
+ AddHostA(i, ebc_disp[c]);
+ i = ScreenInc(i);
+ if (i == LowestScreen()) {
+ SetHighestLowest(HighestScreen());
+ }
+ count--;
+ buffer++;
+ c = *buffer;
+ }
+ SetHighestLowest(i);
+ BufferAddress = i;
+ }
+ }
+ if (count == 0) {
+ if (control) {
+#if !defined(PURE3274)
+ OutputClock++; /* time rolls on */
+#endif /* !defined(PURE3274) */
+ if (Wcc & WCC_RESTORE) {
+#if !defined(PURE3274)
+ if (TransparentClock != OutputClock) {
+ AidByte = 0;
+ }
+#else /* !defined(PURE3274) */
+ AidByte = 0;
+#endif /* !defined(PURE3274) */
+ UnLocked = 1;
+ ResetOiaSystemLocked(&OperatorInformationArea);
+ SetOiaModified();
+ SetPsModified();
+ TerminalIn();
+ }
+ if (Wcc & WCC_ALARM) {
+ RingBell((char *)0);
+ }
+ }
+ LastWasTerminated = control; /* state for next time */
+ return(origCount);
+ } else {
+ return(origCount-count);
+ }
+}
+
+/*
+ * Init3270()
+ *
+ * Initialize any 3270 (controller) variables to an initial state
+ * in preparation for accepting a connection.
+ */
+
+void
+Init3270()
+{
+ int i;
+
+ OptInit(); /* initialize mappings */
+
+ ClearArray(Host);
+
+ ClearArray(Orders);
+ for (i = 0; i <= highestof(orders_def); i++) {
+ Orders[orders_def[i].code] = 1;
+ }
+
+ DeleteAllFields(); /* Clear screen */
+ Lowest = HighestScreen()+1;
+ Highest = LowestScreen()-1;
+ CursorAddress = BufferAddress = SetBufferAddress(0,0);
+ UnLocked = 1;
+#if !defined(PURE3274)
+ OutputClock = 1;
+ TransparentClock = -1;
+#endif /* !defined(PURE3274) */
+ SetOiaReady3274(&OperatorInformationArea);
+}
+
+
+void
+Stop3270()
+{
+ ResetOiaReady3274(&OperatorInformationArea);
+}
diff --git a/usr.bin/tn3270/ctlr/screen.h b/usr.bin/tn3270/ctlr/screen.h
new file mode 100644
index 0000000..9dd8595
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/screen.h
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)screen.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_SCREEN
+
+/* defines and defines to describe how to deal with the screen */
+
+#if !defined(MSDOS)
+#define MAXNUMBERLINES 43 /* 3278-4 */
+#define MAXNUMBERCOLUMNS 132 /* 3278-5 */
+#define MAXSCREENSIZE 3564 /* (27*132) 3278-5 */
+#else /* !defined(MSDOS) */ /* MSDOS has memory constraints */
+#define MAXNUMBERLINES 25 /* XXX */
+#define MAXNUMBERCOLUMNS 80
+#define MAXSCREENSIZE (MAXNUMBERLINES*MAXNUMBERCOLUMNS)
+#endif /* !defined(MSDOS) */ /* MSDOS has memory constraints */
+#define LowestScreen() 0
+#define HighestScreen() (ScreenSize-1)
+
+#define ScreenLineOffset(x) ((x)%NumberColumns)
+#define ScreenLine(x) ((int)((x)/NumberColumns))
+#define ScreenInc(x) (((x)==HighestScreen())? LowestScreen():x+1)
+#define ScreenDec(x) (((x)==LowestScreen())? HighestScreen():x-1)
+#define ScreenUp(x) (((x)+(ScreenSize-NumberColumns))%ScreenSize)
+#define ScreenDown(x) (((x)+NumberColumns)%ScreenSize)
+#define IsOrder(x) (Orders[x])
+#define BAIC(x) ((x)&0x3f)
+#define CIAB(x) (CIABuffer[(x)&0x3f])
+#define BufferTo3270_0(x) (CIABuffer[(int)((x)/0x40)])
+#define BufferTo3270_1(x) (CIABuffer[(x)&0x3f])
+#define Addr3270(x,y) (BAIC(x)*64+BAIC(y))
+#define SetBufferAddress(x,y) ((x)*NumberColumns+(y))
+
+/* These know how fields are implemented... */
+
+#define WhereAttrByte(p) (IsStartField(p)? p: FieldDec(p))
+#define WhereHighByte(p) ScreenDec(FieldInc(p))
+#define WhereLowByte(p) ScreenInc(WhereAttrByte(p))
+#define FieldAttributes(x) (IsStartField(x)? GetHost(x) : \
+ GetHost(WhereAttrByte(x)))
+#define FieldAttributesPointer(p) (IsStartFieldPointer(p)? \
+ GetHostPointer(p): \
+ GetHost(WhereAttrByte((p)-&Host[0])))
+
+/*
+ * The MDT functions need to protect against the case where the screen
+ * is unformatted (sigh).
+ */
+
+/* Turn off the Modified Data Tag */
+#define TurnOffMdt(x) \
+ if (HasMdt(WhereAttrByte(x))) { \
+ ModifyMdt(x, 0); \
+ }
+
+/* Turn on the Modified Data Tag */
+#define TurnOnMdt(x) \
+ if (!HasMdt(WhereAttrByte(x))) { \
+ ModifyMdt(x, 1); \
+ }
+
+/* If this location has the MDT bit turned on (implies start of field) ... */
+#define HasMdt(x) \
+ ((GetHost(x)&(ATTR_MDT|ATTR_MASK)) == (ATTR_MDT|ATTR_MASK))
+
+ /*
+ * Is the screen formatted? Some algorithms change depending
+ * on whether there are any attribute bytes lying around.
+ */
+#define FormattedScreen() \
+ ((WhereAttrByte(0) != 0) || ((GetHost(0)&ATTR_MASK) == ATTR_MASK))
+
+ /* field starts here */
+#define IsStartField(x) ((GetHost(x)&ATTR_MASK) == ATTR_MASK)
+#define IsStartFieldPointer(p) ((GetHostPointer(p)&ATTR_MASK) == ATTR_MASK)
+
+#define NewField(p,a) SetHost(p, (a)|ATTR_MASK)
+#define DeleteField(p) SetHost(p, 0)
+#define DeleteAllFields()
+
+/* The following are independent of the implementation of fields */
+#define IsProtectedAttr(p,a) (IsStartField(p) || ((a)&ATTR_PROT))
+#define IsProtected(p) IsProtectedAttr(p,FieldAttributes(p))
+
+#define IsUnProtected(x) (!IsProtected(x))
+
+#define IsAutoSkip(x) (FieldAttributes(x)&ATTR_AUTO_SKIP)
+
+#define IsNonDisplayAttr(c) (((c)&ATTR_DSPD_MASK) == ATTR_DSPD_NONDISPLAY)
+#define IsNonDisplay(p) IsNonDisplayAttr(FieldAttributes(p))
+
+#define IsHighlightedAttr(c) \
+ (((c)&ATTR_DSPD_MASK) == ATTR_DSPD_HIGH)
+#define IsHighlighted(p) \
+ (IsHighlightedAttr(FieldAttributes(p)) && !IsStartField(p))
+
+typedef unsigned char ScreenImage;
+
+extern int
+ FieldFind();
+
+extern char
+ CIABuffer[];
+
+#define GetGeneric(i,h) (h)[i]
+#define GetGenericPointer(p) (*(p))
+#define SetGeneric(i,c,h) ((h)[i] = (c))
+#define ModifyGeneric(i,what,h) {(h)[i] what;}
+
+#define GetHost(i) GetGeneric(i,Host)
+#define GetHostPointer(p) GetGenericPointer(p)
+#define SetHost(i,c) SetGeneric(i,c,Host)
+#define ModifyHost(i,what) ModifyGeneric(i,what,Host)
diff --git a/usr.bin/tn3270/ctlr/scrnctlr.h b/usr.bin/tn3270/ctlr/scrnctlr.h
new file mode 100644
index 0000000..8c140c6
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/scrnctlr.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)scrnctlr.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * definitions that have to do with the interface between the
+ * controller and the screen.
+ */
+
+#define DISP_AMPERSAND 0x30
+#define DISP_BLANK 0x10
+#define DISP_CENTSIGN 0x1b
+#define DISP_DUP 0x9f
+#define DISP_FM 0x9e
+#define DISP_GREATER_THAN 0x08
+#define DISP_NULL 0x00
+#define DISP_QUESTION 0x18
diff --git a/usr.bin/tn3270/ctlr/unix.kbd b/usr.bin/tn3270/ctlr/unix.kbd
new file mode 100644
index 0000000..ad41eec
--- /dev/null
+++ b/usr.bin/tn3270/ctlr/unix.kbd
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)unix.kbd 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * keynumber [ scancode [ unshifted [ shifted [ alted [ shiftalted ] ] ] ] ]
+ *
+ * keynumber is in decimal, and starts in column 1.
+ * scancode is hexadecimal.
+ * unshifted, etc. - these are either a single ascii character,
+ * or the name of a function or an AID-generating key.
+ *
+ * all fields are separated by a single space.
+ */
+
+extern struct hits hits[];
+1 0e ` ~ LPRT
+2 16 1 ! XON
+3 1e 2 @ XOFF
+4 26 3 # ALTK
+5 25 4 $ ESCAPE
+6 2e 5 % DISC
+7 36 6 ^ MASTER_RESET
+8 3d 7 & RESHOW
+9 3e 8 * FLINP
+10 46 9 ( SYNCH
+11 45 0 ) INIT
+12 4e - _ PCOFF
+13 55 = + PCON
+14 5d APLON APLOFF APLEND
+15 66 LEFT
+16 0d TAB BTAB
+17 15 q Q FIELDEND
+18 1d w W WORDEND
+19 24 e E WORDBACKTAB
+20 2d r R FERASE
+21 2c t T WERASE
+22 35 y Y ERASE
+23 3c u U CLRTAB
+24 43 i I SETHOM
+25 44 o O SETMRG
+26 4d p P UNDENT
+27 54 [ { INDENT
+28 5b \ | SETTAB
+29 5c DELTAB COLTAB COLBAK
+30 14 CAPS_LOCK
+31 1c a A WORDTAB
+32 1b s S CURSEL
+33 23 d D VERTICAL_BAR
+34 2b f F CENTSIGN
+35 34 g G PF25
+36 33 h H PF26
+37 3b j J PF27
+38 42 k K PF28
+39 4b l L PF29
+40 4c ; : PF30
+41 52 ' " PF31
+42 53 ] } PF32
+43 5a NL
+44 12 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+45 13 < > PF33
+46 1a z Z PF34
+47 22 x X PF35
+48 21 c C PF36
+49 2a v V
+50 32 b B
+51 31 n N
+52 3a m M
+53 41 , <
+54 49 . >
+55 4a / ?
+56 51
+57 59 MAKE_SHIFT MAKE_SHIFT MAKE_SHIFT
+58 11 RESET NULL DVCNL
+59
+60 19 MAKE_ALT MAKE_ALT MAKE_ALT
+61 29 SPACE SPACE
+62 39 MAKE_ALT MAKE_ALT MAKE_ALT
+63
+64 58 ENTER
+65 06 CLEAR NULL TEST
+66 0c NULL NULL ATTN
+67 0b EEOF NULL EINP
+68 0a
+69 09 MAKE_CTRL
+70 05 ATTN NULL TREQ
+71 04
+72 03
+73 83
+74 01
+75 67 PA1 DP
+76 64 BTAB
+77
+78 61 LEFT NULL LEFT2
+79
+80 6e PA2 FM
+81 65 INSRT
+82 63 UP
+83 62 NULL NULL HOME
+84 60 DOWN
+85 6f PA3
+86 6d DELETE
+87
+88 6a RIGHT NULL RIGHT2
+89
+90 76
+91 6c 7
+92 6b 4
+93 69 1
+94 68
+95 77
+96 75 8
+97 73 5
+98 72 2
+99 70 0
+100 7e ,
+101 7d 9
+102 74 6
+103 7a 3
+104 71 .
+105 84 SPACE
+106 7c TAB
+107 7b -
+108 79 ENTER
+109 78
+110 07 PF1
+111 0f PF2
+112 17 PF3
+113 1f PF4
+114 27 PF5
+115 2f PF6
+116 37 PF7
+117 3f PF8 NULL MONOCASE
+118 47 PF9
+119 4f PF10
+120 56 PF11
+121 5e PF12
+122 08 PF13
+123 10 PF14
+124 18 PF15
+125 20 PF16
+126 28 PF17
+127 30 PF18
+128 38 PF19
+129 40 PF20
+130 48 PF21
+131 50 PF22
+132 57 PF23
+133 5f PF24
+134 92 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+135 D9 BREAK_SHIFT BREAK_SHIFT BREAK_SHIFT
+136 99 BREAK_ALT BREAK_ALT BREAK_ALT
+137 B9 BREAK_ALT BREAK_ALT BREAK_ALT
diff --git a/usr.bin/tn3270/distribution/README b/usr.bin/tn3270/distribution/README
new file mode 100644
index 0000000..29afc73
--- /dev/null
+++ b/usr.bin/tn3270/distribution/README
@@ -0,0 +1,99 @@
+Welcome to the new tn3270 - version 4.1.1.
+
+This version consists entirely of bug fixes to the August 1987 beta release
+of tn3270. This version will now deal with CICS and VM/XA on the IBM
+side, and with SunOS 4.0 on Sun 3's and Sun 4's.
+
+This version has been tested on various versions of BSD Unix, including
+4.2 and 4.3 (but there are never vanilla versions) and the post-4.3 systems
+running at Berkeley. It has been tested on CCI's, Vaxen, Sun 3's and Sun 4's.
+However, it doesn't necessarily work on all systems (nor has the testing
+been as intensive as one might like).
+
+This version should build on any Berkeley-derived system.
+
+There are two alternate make files: ./makefile_4.2 and telnet/Makefile_ultrix.
+
+**** Try ./makefile_4.2 if you get compile-time errors, or get
+ "multiply defined" messages for "_putchar" from the loader.
+
+**** Try telnet/Makefile_ultrix if your make(1) utility doesn't
+ support VPATH. Also try this makefile if your ld(1) doesn't
+ support the -r flag correctly.
+
+The bad news is that I've had to drop MS-DOS support. The good news here is
+that there are various versions available for MS-DOS (from FTP Software in
+Cambridge, Mass.; from IBM; from Excelan; and probably from others). The
+hooks are still there, as well as some code to update the screen. However,
+I just haven't had the time to produce a fully integrated version that would
+"just make". I suspect that a future release may have MS-DOS support back
+in it.
+
+There is no Mac support. Contact Peter DiCamillo at Brown University if
+you need a Mac tn3270.
+
+The main code change in this version is to what used to be called "telnet.c".
+This is now replaced with a version of telnet (substantially what appeared
+in the "4.3tahoe" release from CSRG) which is broken into separate files.
+
+Here is an overview of the directory structure:
+
+ api/ General library of function needed by API
+ (and, to some extent, by the rest of tn3270).
+
+ arpa/ Location of "telnet.h" (for non-4.3 systems).
+
+ ascii/ Routines necessary to handle the case of running
+ from an ASCII-oriented system (ie: unix).
+
+ ctlr/ The main part of the emulator. Handles 3270 scan
+ codes, 3270 data stream, 3270 display codes,
+ and EBCDIC. Also, the internal API function
+ lives here.
+
+ general/ Some general subroutines and data structures of
+ interest to the emulator only.
+
+ sys_curses/ System-dependent code for a curses-based environment.
+
+ sys_dos/ System-dependent code for an MS-DOS-base environment.
+ Remember that this is included for your developmental
+ needs (ie: it doesn't work).
+
+ telnet/ Where the telnet portion of tn3720 is built.
+
+ tools/ Various tools. Most of these are used during the
+ build process. One (prt3270) is a debugging tool.
+ One (mkmake.y) is quite horrible, and attempts to
+ transform Unix makefiles into PC makefiles.
+
+ utilities/ The source for tnrecv, which receives files
+ (fairly slowly) from an IBM host. We don't
+ include the IBM side, because we really aren't
+ happy with very much of it (except that it does,
+ sometimes, work). Hopefully, when we get past
+ the beta stage we will have more robust (and
+ complete) code to share.
+
+The fact that system dependancies are isolated should make it easy
+to port to other systems. I would like to hear about problems porting
+to new areas.
+
+In the August, 1987 README file, the following appeared:
+
+> WHAT IS NOT IN THIS VERSION (sigh):
+
+> 1) We don't have a native X version yet. I am waiting for X version 11
+> (though this is mostly an excuse; I could have done version 10,
+> but I haven't had the time).
+
+> 2) We don't process structured fields.
+
+> 3) We don't do 3270-style graphics (ala 3193, say).
+
+> The above three items WILL be in the next version, which should come
+> along "any day now" (say 6 months) (but, they WON'T be in the production
+> release of this version).
+
+The next piece of bad news is that none of the above have happened yet,
+and I don't know when they might occur.
diff --git a/usr.bin/tn3270/distribution/arpa/makefile b/usr.bin/tn3270/distribution/arpa/makefile
new file mode 100644
index 0000000..2cf3bc0
--- /dev/null
+++ b/usr.bin/tn3270/distribution/arpa/makefile
@@ -0,0 +1,67 @@
+# @(#)makefile 8.1 (Berkeley) 6/6/93
+
+# msdos versus unix defines
+O = .o
+#PC_O = .obj
+
+X =
+#PC_X = .exe
+
+L =
+#PC_L = -link
+
+CC = cc
+#PC_CC = cl
+
+MV = mv
+#PC_MV = rename
+
+RM = rm -f
+#PC_RM= erase
+
+LINT_ARGS =
+#PC_LINT_ARGS = -DLINT_ARGS
+
+DEBUG_FLAGS = -g
+#PC_DEBUG_FLAGS = -Zi -Od
+
+AR = ar
+AR1 = cr
+AR2 =
+AR3 =
+#PC_AR = lib
+#PC_AR1 =
+#PC_AR2 = +
+#PC_AR3 = ";"
+
+RANLIB = ranlib
+#PC_RANLIB = echo "Done with "
+
+PRINT = lpr -p
+
+ALLC =
+ALLH = telnet.h
+
+ALLPRINT = ${ALLH} ${ALLC}
+
+ALLSOURCE = ${ALLPRINT} makefile makefile.mak
+
+clean:
+
+sccsclean:
+ -sccs clean
+ -sccs get makefile
+
+action:
+ ${ACTION}
+
+print:
+ ${PRINT} ${ALLPRINT}
+
+
+sourcelist: ${ALLSOURCE}
+ @for i in ${ALLSOURCE}; \
+ do (echo ${DIRPATH}$$i); done
+
+.DEFAULT:
+ sccs get $<
diff --git a/usr.bin/tn3270/distribution/arpa/telnet.h b/usr.bin/tn3270/distribution/arpa/telnet.h
new file mode 100644
index 0000000..eeb72c2
--- /dev/null
+++ b/usr.bin/tn3270/distribution/arpa/telnet.h
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)telnet.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Definitions for the TELNET protocol.
+ */
+#define IAC 255 /* interpret as command: */
+#define DONT 254 /* you are not to use option */
+#define DO 253 /* please, you use option */
+#define WONT 252 /* I won't use option */
+#define WILL 251 /* I will use option */
+#define SB 250 /* interpret as subnegotiation */
+#define GA 249 /* you may reverse the line */
+#define EL 248 /* erase the current line */
+#define EC 247 /* erase the current character */
+#define AYT 246 /* are you there */
+#define AO 245 /* abort output--but let prog finish */
+#define IP 244 /* interrupt process--permanently */
+#define BREAK 243 /* break */
+#define DM 242 /* data mark--for connect. cleaning */
+#define NOP 241 /* nop */
+#define SE 240 /* end sub negotiation */
+#define EOR 239 /* end of record (transparent mode) */
+#define ABORT 238 /* Abort process */
+#define SUSP 237 /* Suspend process */
+#define xEOF 236 /* End of file: EOF is already used... */
+
+#define SYNCH 242 /* for telfunc calls */
+
+#ifdef TELCMDS
+char *telcmds[] = {
+ "EOF", "SUSP", "ABORT", "EOR",
+ "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC",
+ "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC",
+};
+#define TELCMD_FIRST xEOF
+#define TELCMD_LAST IAC
+#define TELCMD_OK(x) ((x) <= TELCMD_LAST && (x) >= TELCMD_FIRST)
+#define TELCMD(x) telcmds[(x)-TELCMD_FIRST]
+#endif
+
+/* telnet options */
+#define TELOPT_BINARY 0 /* 8-bit data path */
+#define TELOPT_ECHO 1 /* echo */
+#define TELOPT_RCP 2 /* prepare to reconnect */
+#define TELOPT_SGA 3 /* suppress go ahead */
+#define TELOPT_NAMS 4 /* approximate message size */
+#define TELOPT_STATUS 5 /* give status */
+#define TELOPT_TM 6 /* timing mark */
+#define TELOPT_RCTE 7 /* remote controlled transmission and echo */
+#define TELOPT_NAOL 8 /* negotiate about output line width */
+#define TELOPT_NAOP 9 /* negotiate about output page size */
+#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
+#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
+#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
+#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
+#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
+#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
+#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
+#define TELOPT_XASCII 17 /* extended ascic character set */
+#define TELOPT_LOGOUT 18 /* force logout */
+#define TELOPT_BM 19 /* byte macro */
+#define TELOPT_DET 20 /* data entry terminal */
+#define TELOPT_SUPDUP 21 /* supdup protocol */
+#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
+#define TELOPT_SNDLOC 23 /* send location */
+#define TELOPT_TTYPE 24 /* terminal type */
+#define TELOPT_EOR 25 /* end or record */
+#define TELOPT_TUID 26 /* TACACS user identification */
+#define TELOPT_OUTMRK 27 /* output marking */
+#define TELOPT_TTYLOC 28 /* terminal location number */
+#define TELOPT_3270REGIME 29 /* 3270 regime */
+#define TELOPT_X3PAD 30 /* X.3 PAD */
+#define TELOPT_NAWS 31 /* window size */
+#define TELOPT_TSPEED 32 /* terminal speed */
+#define TELOPT_LFLOW 33 /* remote flow control */
+#define TELOPT_LINEMODE 34 /* Linemode option */
+#define TELOPT_EXOPL 255 /* extended-options-list */
+
+#define NTELOPTS (1+TELOPT_LINEMODE)
+#ifdef TELOPTS
+char *telopts[NTELOPTS] = {
+ "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME",
+ "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP",
+ "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS",
+ "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
+ "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT",
+ "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
+ "TACACS UID", "OUTPUT MARKING", "TTYLOC",
+ "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW",
+ "LINEMODE",
+};
+#define TELOPT_FIRST TELOPT_BINARY
+#define TELOPT_LAST TELOPT_LINEMODE
+#define TELOPT_OK(x) ((x) <= TELOPT_LAST && (x) >= TELOPT_FIRST)
+#define TELOPT(x) telopts[(x)-TELOPT_FIRST]
+#endif
+
+/* sub-option qualifiers */
+#define TELQUAL_IS 0 /* option is... */
+#define TELQUAL_SEND 1 /* send option */
+
+/*
+ * LINEMODE suboptions
+ */
+
+#define LM_MODE 1
+#define LM_FORWARDMASK 2
+#define LM_SLC 3
+
+#define MODE_EDIT 0x01
+#define MODE_TRAPSIG 0x02
+#define MODE_ACK 0x04
+
+#define MODE_MASK (MODE_EDIT|MODE_TRAPSIG|MODE_ACK)
+
+/* Not part of protocol, but needed to simplify things... */
+#define MODE_FLOW 0x40
+#define MODE_ECHO 0x80
+#define MODE_FORCE 0x20
+
+#define SLC_SYNCH 1
+#define SLC_BRK 2
+#define SLC_IP 3
+#define SLC_AO 4
+#define SLC_AYT 5
+#define SLC_EOR 6
+#define SLC_ABORT 7
+#define SLC_EOF 8
+#define SLC_SUSP 9
+#define SLC_EC 10
+#define SLC_EL 11
+#define SLC_EW 12
+#define SLC_RP 13
+#define SLC_LNEXT 14
+#define SLC_XON 15
+#define SLC_XOFF 16
+#define SLC_FORW1 17
+#define SLC_FORW2 18
+
+#define NSLC 18
+
+#define SLC_NAMES "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \
+ "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \
+ "LNEXT", "XON", "XOFF", "FORW1", "FORW2"
+
+#define SLC_NOSUPPORT 0
+#define SLC_CANTCHANGE 1
+#define SLC_VARIABLE 2
+#define SLC_DEFAULT 3
+#define SLC_LEVELBITS 0x03
+
+#define SLC_FUNC 0
+#define SLC_FLAGS 1
+#define SLC_VALUE 2
+
+#define SLC_ACK 0x80
+#define SLC_FLUSHIN 0x40
+#define SLC_FLUSHOUT 0x20
diff --git a/usr.bin/tn3270/distribution/makefile_4.2 b/usr.bin/tn3270/distribution/makefile_4.2
new file mode 100644
index 0000000..9f35a6c
--- /dev/null
+++ b/usr.bin/tn3270/distribution/makefile_4.2
@@ -0,0 +1,268 @@
+# @(#)makefile 3.5 (Berkeley) 5/15/88
+
+# This makefile will make tn3270 on Vax 4.2 systems. Notice, however,
+# that on an ultrix system you will need to use the Makefile_ultrix in
+# telnet/ rather than the Makefile already in that directory.
+
+# Makefile for tn3270 and friends...
+#
+# This is the makefile for tn3270. Note that we use the 4.3+ telnet
+# (compiled with special options; see below) to provide the telnet
+# support we need.
+#
+# The following are the defines that may be passed (via the cc
+# -D option) to the compiler.
+#
+# TN3270 - This is to be linked with tn3270. Necessary
+# for creating tn3270. Only for compiling
+# telnet.
+#
+# NOT43 - Allows the program to compile and run on
+# a 4.2BSD system.
+#
+# PUTCHAR - Within tn3270, on a NOT43 system,
+# allows the use of the 4.3 curses
+# (greater speed updating the screen).
+# You need the 4.3 curses for this to work.
+#
+# FD_SETSIZE - On whichever system, if this isn't defined,
+# we patch over the FD_SET, etc., macros with
+# some homebrewed ones.
+#
+# SO_OOBINLINE - This is a socket option which we would like
+# to set to allow TCP urgent data to come
+# to us "inline". This is NECESSARY for
+# CORRECT operation, and desireable for
+# simpler operation.
+#
+# LNOFLSH - Detects the presence of the LNOFLSH bit
+# in the tty structure.
+#
+#
+# Here are some which are used throughout the system:
+#
+# unix - Compiles in unix specific stuff.
+#
+# msdos - Compiles in msdos specific stuff.
+#
+
+# msdos versus unix defines
+O = .o
+#PC_O = .obj
+
+X =
+#PC_X = .exe
+
+L =
+#PC_L = -link
+
+CC = cc
+#PC_CC = cl
+
+MV = mv
+#PC_MV = rename
+
+RM = rm -f
+#PC_RM= erase
+
+LINT_ARGS =
+#PC_LINT_ARGS = -DLINT_ARGS
+
+DEBUG_FLAGS = -g
+#PC_DEBUG_FLAGS = -Zi -Od
+
+AR = ar
+AR1 = cr
+AR2 =
+AR3 =
+#PC_AR = lib
+#PC_AR1 =
+#PC_AR2 = +
+#PC_AR3 = ";"
+
+RANLIB = ranlib
+#PC_RANLIB = echo "Done with "
+
+
+PRINT = print
+ACTION = @sccs tell
+
+DEFINES = -DNOT43 ${LINT_ARGS}
+
+INCLUDES = -I. -I..
+
+OPTIMIZE = -O
+OPTIMIZE = ${DEBUG_FLAGS}
+
+CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES)
+
+# Lint flags
+LINTFLAGS = -hbxaz
+# How to install the bloody thing...
+
+DESTDIR=
+
+BINDIR = $(DESTDIR)/usr/ucb
+
+# Names for the terminal libraries...
+LIBCURSES = -lcurses
+LIBTERMCAP = -ltermcap
+
+#PC_LIBCURSES =
+#PC_LIBTERM =
+
+# The source files...
+ALLH = telnet.ext
+
+MSMAIN = ascii/mset.c
+
+ALLC =
+
+ALLO = mset$O
+
+ALLHC= ${ALLH} ${ALLC}
+ALLPRINT = ${ALLHC}
+
+ALLSOURCE = ${ALLPRINT} makefile makefile.mak makefile_4.2 README
+
+SYS = sys_curses
+#PC_SYS = sys_dos
+
+# The places where the various components live...
+
+SUBDIR = api ascii ctlr general ${SYS} telnet
+
+# The following are directories we don't do regular make's in, but
+# we do make everywhere, print, and sourcelist in.
+
+EXTRADIR = arpa sys_dos tools utilities
+
+# The libraries we use. The order here is important.
+# syslib.a and ctlrlib.a should come first, then the rest.
+SUBLIB = ${SYS}/syslib.a ctlr/ctlrlib.a \
+ ascii/asciilib.a general/generallib.a
+
+.s.o:
+ /lib/cpp -E $< | as -o $@
+
+.c.obj:
+ ${CC} ${CFLAGS} -c $<
+
+all: FRC tn3270$X mset$X
+
+FRC:
+ for i in ${SUBDIR}; \
+ do (cd $$i; make ${MFLAGS} "CFLAGS=${CFLAGS}"); done
+
+tn3270$X: telnet/telprog.o ${SUBLIB} api/apilib.a
+ ${CC} ${CFLAGS} -o tn3270 telnet/telprog.o \
+ $L ${SUBLIB} api/apilib.a ${LIBCURSES} ${LIBTERMCAP}
+
+#PC_tn3270$X:
+#PC_ link <@<
+#PC_ telnet
+#PC_ tn3270
+#PC_ nul
+#PC_ ${SUBLIB} api/apilib.a+
+#PC_ \lib\ublib\ubtcp
+#PC_ _PC_<
+
+mset$X: mset$O ascii/map3270$O
+ ${CC} ${CFLAGS} -o mset mset$O ascii/map3270$O $L api/apilib.a
+
+mset$O: $(MSMAIN)
+ $(CC) $(CFLAGS) -c $(MSMAIN)
+
+install: tn3270$X mset$X
+ install -m 755 -o bin -g bin -s tn3270 $(BINDIR)
+ install -m 755 -o bin -g bin -s mset $(BINDIR)
+
+action:
+ ${ACTION}
+
+clist: ${ALLHC}
+ @for i in ${SUBDIR}; \
+ do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \
+ clist); done
+
+hclist: ${ALLHC}
+ @for i in ${SUBDIR}; \
+ do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \
+ hclist); done
+
+everywhere: action
+ for i in ${SUBDIR} ${EXTRADIR}; \
+ do (echo "[$$i]"; cd $$i; make ${MFLAGS} action \
+ "ACTION=${ACTION}"); done
+
+clean:
+ for i in $(ALLO) mset tn3270 errs makefile.bak; \
+ do (${RM} $$i); done
+ for i in ${SUBDIR} ${EXTRADIR}; \
+ do (cd $$i; make ${MFLAGS} clean); done
+
+sccsclean:
+ -sccs clean
+ -sccs get makefile
+ for i in ${SUBDIR} ${EXTRADIR}; \
+ do (cd $$i; make ${MFLAGS} sccsclean); done
+
+print:
+ ${PRINT} ${ALLPRINT}
+ for i in ${SUBDIR} ${EXTRADIR}; \
+ do (cd $$i; make ${MFLAGS} "PRINT=${PRINT}" print); done
+
+tags: ${ALLC} ${ALLH}
+ ctags -t `make ${MFLAGS} hclist`
+
+sourcelist: ${ALLSOURCE}
+ @for i in ${ALLSOURCE}; \
+ do (echo ${DIRPATH}$$i); done
+ @for i in ${SUBDIR} ${EXTRADIR}; \
+ do (cd $$i; make ${MFLAGS} "DIRPATH=${DIRPATH}$$i/" \
+ sourcelist); done
+
+lint:
+ lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} -DTN3270 \
+ `make clist` -lcurses
+
+lintmset:
+ lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${MSMAIN} \
+ ascii/map3270.c -lcurses
+
+makefiles.pc: tools/mkmake
+ for i in . ${SUBDIR} ${EXTRADIR}; \
+ do (sed -e "s/lib\.a/.lib/g" -e "s/^#PC_//" < $$i/makefile | \
+ ./tools/mkmake | \
+ sed -e "sx/x\\\\xg" -e "s/[ ]*_PC_//" > $$i/makefile.mak); \
+ done
+
+tools/mkmake:
+ (cd tools; make mkmake)
+
+.DEFAULT:
+ sccs get $<
+
+depend: thisdepend
+ for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} depend); done
+
+thisdepend:
+ echo > eddep.c
+ grep '^#include' ${ALLC} eddep.c | grep -v '<' | \
+ sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \
+ -e 's/\.c/$$O/' | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' > makedep
+ echo '$$r makedep' >>eddep
+ echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
+ echo '$$r makedep' >>eddep
+ echo 'w' >>eddep
+ -rm -f makefile.bak
+ cp makefile makefile.bak
+ ed - makefile < eddep
+ rm eddep makedep eddep.c
+
+# DO NOT DELETE THIS LINE
+
diff --git a/usr.bin/tn3270/distribution/sys_dos/makefile b/usr.bin/tn3270/distribution/sys_dos/makefile
new file mode 100644
index 0000000..4188f9f
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/makefile
@@ -0,0 +1,127 @@
+# @(#)makefile 8.1 (Berkeley) 6/6/93
+
+# msdos versus unix defines
+O = .o
+#PC_O = .obj
+
+X =
+#PC_X = .exe
+
+L =
+#PC_L = -link
+
+CC = cc
+#PC_CC = cl
+
+MV = mv
+#PC_MV = rename
+
+RM = rm -f
+#PC_RM= erase
+
+LINT_ARGS =
+#PC_LINT_ARGS = -DLINT_ARGS
+
+DEBUG_FLAGS = -g
+#PC_DEBUG_FLAGS = -Zi -Od
+
+AR = ar
+AR1 = cr
+AR2 =
+AR3 =
+#PC_AR = lib
+#PC_AR1 =
+#PC_AR2 = +
+#PC_AR3 = ";"
+
+RANLIB = ranlib
+#PC_RANLIB = echo "Done with "
+
+PRINT = print
+
+DEFINES = ${LINT_ARGS}
+
+INCLUDES = -I.
+
+OPTIMIZE = -O
+OPTIMIZE = ${DEBUG_FLAGS}
+
+CFLAGS = $(OPTIMIZE) $(INCLUDES) $(DEFINES)
+
+# Lint flags
+LINTFLAGS = -hbxaz
+
+ALLH = spint.h video.h
+
+ALLC = spintc.c system.c termout.c
+
+ALLASM = spintasm.asm support.asm
+
+ALLO = spintasm$O spintc$O support$O system$O termout$O
+
+ALLPRINT = ${ALLH} ${ALLASM} ${ALLC}
+
+ALLSOURCE = ${ALLPRINT} makefile makefile.mak
+
+.c.obj:
+ ${CC} ${CFLAGS} -c $<
+
+syslib.a: $(ALLO)
+ ${RM} $@
+ for i in ${ALLO}; do (${AR} ${AR1} $@ ${AR2} $$i${AR3}); done
+ ${RANLIB} $@
+
+clean:
+ for i in $(ALLO) errs makefile.bak syslib.a; \
+ do (${RM} $$i); done
+
+sccsclean:
+ -sccs clean
+ -sccs get makefile
+
+sourcelist: ${ALLSOURCE}
+ @for i in ${ALLSOURCE}; \
+ do (echo ${DIRPATH}$$i); done
+
+print:
+ ${PRINT} ${ALLPRINT}
+
+tags: ${ALLC} ${ALLH}
+ ctags -t ${ALLC} ${ALLH}
+
+action:
+ ${ACTION}
+
+lint:
+ lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} -DTN3270 \
+ ${TNMAIN} ${MOSTC} -lcurses
+ lint ${LINTFLAGS} ${INCLUDES} ${DEFINES} ${MSMAIN} map3270.c -lcurses
+
+.DEFAULT:
+ sccs get $<
+
+depend:
+ grep '^#include' ${ALLC} ${ALLH} | grep -v '<' | \
+ sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \
+ -e 's/\.c/$$O/' | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' > makedep
+ echo '$$r makedep' >>eddep
+ echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
+ echo '$$r makedep' >>eddep
+ echo 'w' >>eddep
+ -rm -f makefile.bak
+ cp makefile makefile.bak
+ ed - makefile < eddep
+ rm eddep makedep
+
+# DO NOT DELETE THIS LINE
+
+spintc$O: ../general/general.h spint.h
+system$O: ../general/general.h ../ctlr/api.h spint.h ../general/globals.h
+termout$O: ../general/general.h ../api/disp_asc.h
+termout$O: ../ctlr/hostctlr.h
+termout$O: ../ctlr/oia.h
+termout$O: ../ctlr/screen.h ../general/globals.h video.h
diff --git a/usr.bin/tn3270/distribution/sys_dos/spint.h b/usr.bin/tn3270/distribution/sys_dos/spint.h
new file mode 100644
index 0000000..023ee52
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/spint.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)spint.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * The 'spint' (spawn and interrupt) routines use this structure.
+ *
+ * Note that spint_asm.asm contains an Assembly language version of
+ * the following, so keep changes in synch!
+ */
+
+typedef struct {
+ union REGS regs;
+ struct SREGS sregs;
+ int int_no; /* Which interrupt to wait on */
+ int done; /* Are we done, or just took an interrupt? */
+ int rc; /* return code */
+} Spint;
diff --git a/usr.bin/tn3270/distribution/sys_dos/spintasm.asm b/usr.bin/tn3270/distribution/sys_dos/spintasm.asm
new file mode 100644
index 0000000..72efdec
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/spintasm.asm
@@ -0,0 +1,252 @@
+; Copyright (c) 1988, 1993
+; The Regents of the University of California. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+; 1. Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+; 3. All advertising materials mentioning features or use of this software
+; must display the following acknowledgement:
+; This product includes software developed by the University of
+; California, Berkeley and its contributors.
+; 4. Neither the name of the University nor the names of its contributors
+; may be used to endorse or promote products derived from this software
+; without specific prior written permission.
+;
+; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+; @(#)spintasm.asm 8.1 (Berkeley) 6/6/93
+;
+
+; The code in this file complete the spint calls
+
+spint struc
+; union REGS
+spint_ax dw 1
+spint_bx dw 1
+spint_cx dw 1
+spint_dx dw 1
+spint_si dw 1
+spint_di dw 1
+spint_cflag dw 1
+; struct SREGS
+spint_es dw 1
+spint_cs dw 1
+spint_ss dw 1
+spint_ds dw 1
+; int intno
+spint_intno dw 1
+; int done
+spint_done dw 1
+; int rc
+spint_rc dw 1
+;
+spint ends
+
+
+ENTER MACRO
+ ; Begin enter
+ push bp
+ mov bp,sp
+
+ push ax
+ push bx
+ push cx
+ push dx
+ push bp
+ push di
+ push si
+ push ds
+ push es
+ pushf
+
+ mov cs:start_sp, sp
+ mov cs:start_ss, ss
+ ; End enter
+ ENDM
+
+LEAVE MACRO
+ ; Begin leave
+ cli
+ mov sp, cs:start_sp
+ mov ss, cs:start_ss
+ sti
+
+ popf
+ pop es
+ pop ds
+ pop si
+ pop di
+ pop bp
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+
+ mov sp,bp
+ pop bp
+ ret
+ ; End leave
+ ENDM
+
+GETREGS MACRO wherefrom
+ mov si, wherefrom
+ mov spint_segment, ds
+ mov spint_offset, si
+
+ mov ax, spint_ax[si]
+ mov bx, spint_bx[si]
+ mov cx, spint_cx[si]
+ mov dx, spint_dx[si]
+ ; XXX mov si, spint_si[si]
+ mov di, spint_di[si]
+ mov es, spint_es[si]
+ ; Now, need to do DS, SI
+ push spint_ds[si]
+ mov si, spint_si[si]
+ pop ds
+ ENDM
+
+
+SETREGS MACRO
+ mov cs:old_si, si
+ mov cs:old_ds, ds
+
+ mov ds, cs:spint_segment
+ mov si, cs:spint_offset
+
+ mov spint_ax[si], ax
+ mov spint_bx[si], bx
+ mov spint_cx[si], cx
+ mov spint_dx[si], dx
+
+ mov spint_si[si], si
+ mov spint_di[si], di
+
+ mov spint_cs[si], cs
+ mov spint_ds[si], ds
+ mov spint_es[si], es
+ mov spint_ss[si], ss
+ ; now, need to do SI, DS
+ mov ax, old_si
+ mov spint_si[si], ax
+ mov ax, old_ds
+ mov spint_ds[si], ax
+ ENDM
+
+
+_TEXT segment byte public 'CODE'
+_TEXT ends
+
+_DATA segment word public 'DATA'
+_DATA ends
+
+CONST segment word public 'CONST'
+CONST ends
+
+_BSS segment word public 'BSS'
+_BSS ends
+
+DGROUP group CONST, _BSS, _DATA
+
+ assume cs:_TEXT, ds:DGROUP, ss:DGROUP, es:DGROUP
+
+_TEXT segment
+
+start_sp dw 1 dup (?) ; For use in our 'longjmp'
+start_ss dw 1 dup (?) ; For use in our 'longjmp'
+
+spint_segment dw 1 dup (?) ; Segment of spawn control block
+spint_offset dw 1 dup (?) ; Offset of spawn control block
+
+old_si dw 1 dup (?) ; SI of interrupt issuer (temporary)
+old_ds dw 1 dup (?) ; DS of interrupt issuer (temporary)
+
+issuer_ss dw 1 dup (?) ; ss of person who called us (permanent)
+issuer_sp dw 1 dup (?) ; sp of person who called us (permanent)
+
+int21_stack db 100 dup (?) ; Stack for int21.
+
+;
+; _spint_int gets control on an interrupt. It switches the stack
+; and does a 'return' from _spint_start.
+;
+ public __spint_int
+
+__spint_int proc near
+ mov cs:issuer_sp, sp
+ mov cs:issuer_ss, ss
+ sti
+
+ SETREGS
+
+ LEAVE
+__spint_int endp
+
+;
+; _spint_start issues the dos interrupt after setting up the passed
+; registers. When control returns to it, it sets spint->done to non-zero.
+;
+ public __spint_start
+
+__spint_start proc near
+ ENTER
+
+ GETREGS 4[bp]
+
+ ; Now, switch to a different (short) stack. This is so
+ ; that our games won't mess up the stack int 21 (hardware and,
+ ; possibly, software) stores things on.
+
+ cli
+ mov cs:int21_stack, cs
+ mov ss, cs:int21_stack
+ mov sp, offset int21_stack
+ add sp, (length int21_stack) - 4
+ sti
+
+ int 21H ; Issue DOS interrupt
+
+ SETREGS
+
+ mov ds, cs:spint_segment
+ mov si, cs:spint_offset
+ mov spint_done[si], 1 ; We are done
+
+ LEAVE
+__spint_start endp
+
+;
+; After _spint_int has faked a return from start_spawn, we come here to
+; return to the interrupt issuer.
+;
+ public __spint_continue
+
+__spint_continue proc near
+ ENTER
+
+ GETREGS 4[bp]
+
+ mov sp, cs:issuer_sp ; Restore SP
+ mov ss, cs:issuer_ss ; Restore SS
+
+ iret
+__spint_continue endp
+
+_TEXT ends
+
+ end
diff --git a/usr.bin/tn3270/distribution/sys_dos/spintc.c b/usr.bin/tn3270/distribution/sys_dos/spintc.c
new file mode 100644
index 0000000..aa16405
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/spintc.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)spintc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <dos.h>
+#include <stdlib.h>
+
+#include "../general/general.h"
+#include "spint.h"
+
+#define PSP_ENVIRONMENT 0x2c
+#define PSP_FCB1 0x5c
+#define PSP_FCB2 0x6c
+
+typedef struct {
+ int
+ environment, /* Segment address of environment */
+ cmd_ptr_offset, /* Offset of command to execute */
+ cmd_ptr_segment, /* Segment where command lives */
+ fcb1_ptr_offset, /* Offset of FCB 1 */
+ fcb1_ptr_segment, /* Segment of FCB 1 */
+ fcb2_ptr_offset, /* Offset of FCB 2 */
+ fcb2_ptr_segment; /* Segment of FCB 2 */
+} ExecList;
+
+
+static int int_offset, int_segment;
+
+
+void
+spint_finish(spint)
+Spint *spint;
+{
+ union REGS regs;
+ struct SREGS sregs;
+
+ if (spint->done == 0) {
+ return; /* Not done yet */
+ }
+
+ /*
+ * Restore old interrupt handler.
+ */
+
+ regs.h.ah = 0x25;
+ regs.h.al = spint->int_no;
+ regs.x.dx = int_offset;
+ sregs.ds = int_segment;
+ intdosx(&regs, &regs, &sregs);
+
+ if (spint->regs.x.cflag) {
+ fprintf(stderr, "0x%x return code from EXEC.\n", spint->regs.x.ax);
+ spint->done = 1;
+ spint->rc = 99;
+ return;
+ }
+
+ regs.h.ah = 0x4d; /* Get return code */
+
+ intdos(&regs, &regs);
+
+ spint->rc = regs.x.ax;
+}
+
+void
+spint_continue(spint)
+Spint *spint;
+{
+ _spint_continue(spint); /* Return to caller */
+ spint_finish(spint);
+}
+
+
+void
+spint_start(command, spint)
+char *command;
+Spint *spint;
+{
+ ExecList mylist;
+ char *comspec;
+ void _spint_int();
+ union REGS regs;
+ struct SREGS sregs;
+
+ /*
+ * Get comspec.
+ */
+ comspec = getenv("COMSPEC");
+ if (comspec == 0) { /* Can't find where command.com is */
+ fprintf(stderr, "Unable to find COMSPEC in the environment.");
+ spint->done = 1;
+ spint->rc = 99; /* XXX */
+ return;
+ }
+
+ /*
+ * Now, hook up our interrupt routine.
+ */
+
+ regs.h.ah = 0x35;
+ regs.h.al = spint->int_no;
+ intdosx(&regs, &regs, &sregs);
+
+ /* Save old routine */
+ int_offset = regs.x.bx;
+ int_segment = sregs.es;
+
+ regs.h.ah = 0x25;
+ regs.h.al = spint->int_no;
+ regs.x.dx = (int) _spint_int;
+ segread(&sregs);
+ sregs.ds = sregs.cs;
+ intdosx(&regs, &regs, &sregs);
+
+ /*
+ * Read in segment registers.
+ */
+
+ segread(&spint->sregs);
+
+ /*
+ * Set up registers for the EXEC call.
+ */
+
+ spint->regs.h.ah = 0x4b;
+ spint->regs.h.al = 0;
+ spint->regs.x.dx = (int) comspec;
+ spint->sregs.es = spint->sregs.ds; /* Superfluous, probably */
+ spint->regs.x.bx = (int) &mylist;
+
+ /*
+ * Set up EXEC parameter list.
+ */
+
+ ClearElement(mylist);
+ mylist.cmd_ptr_offset = (int) command;
+ mylist.cmd_ptr_segment = spint->sregs.ds;
+ mylist.fcb1_ptr_offset = PSP_FCB1;
+ mylist.fcb1_ptr_segment = _psp;
+ mylist.fcb2_ptr_offset = PSP_FCB2;
+ mylist.fcb2_ptr_segment = _psp;
+ mylist.environment = *((int far *)(((long)_psp<<16)|PSP_ENVIRONMENT));
+
+ /*
+ * Call to assembly language routine to actually set up for
+ * the spint.
+ */
+
+ _spint_start(spint);
+
+ spint_finish(spint);
+}
diff --git a/usr.bin/tn3270/distribution/sys_dos/support.asm b/usr.bin/tn3270/distribution/sys_dos/support.asm
new file mode 100644
index 0000000..a51c41d
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/support.asm
@@ -0,0 +1,60 @@
+; Copyright (c) 1988, 1993
+; The Regents of the University of California. All rights reserved.
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+; 1. Redistributions of source code must retain the above copyright
+; notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+; notice, this list of conditions and the following disclaimer in the
+; documentation and/or other materials provided with the distribution.
+; 3. All advertising materials mentioning features or use of this software
+; must display the following acknowledgement:
+; This product includes software developed by the University of
+; California, Berkeley and its contributors.
+; 4. Neither the name of the University nor the names of its contributors
+; may be used to endorse or promote products derived from this software
+; without specific prior written permission.
+;
+; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+; SUCH DAMAGE.
+;
+; @(#)support.asm 8.1 (Berkeley) 6/6/93
+;
+
+_TEXT segment byte public 'CODE'
+_TEXT ends
+
+_DATA segment word public 'DATA'
+_DATA ends
+
+CONST segment word public 'CONST'
+CONST ends
+
+_BSS segment word public 'BSS'
+_BSS ends
+
+DGROUP group CONST, _BSS, _DATA
+
+ assume cs:_TEXT, ds:DGROUP, ss:DGROUP, es:DGROUP
+
+_TEXT segment
+ public _iret_subr
+
+_iret_subr proc far
+ iret
+_iret_subr endp
+
+_TEXT ends
+
+ end
diff --git a/usr.bin/tn3270/distribution/sys_dos/system.c b/usr.bin/tn3270/distribution/sys_dos/system.c
new file mode 100644
index 0000000..06c2194
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/system.c
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)system.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "../general/general.h"
+#include "../ctlr/api.h"
+#include "spint.h"
+
+#include "../general/globals.h"
+
+
+static Spint spinted;
+static char command[256];
+static int need_to_start = 0;
+
+/*
+ * shell_continue() actually runs the command, and looks for API
+ * requests coming back in.
+ *
+ * We are called from the main loop in telnet.c.
+ */
+
+int
+shell_continue()
+{
+ /*
+ * spint_start() returns when either the command has finished, or when
+ * the required interrupt comes in. In the latter case, the appropriate
+ * thing to do is to process the interrupt, and then return to
+ * the interrupt issuer by calling spint_continue().
+ */
+ if (need_to_start) {
+ need_to_start = 0;
+ spint_start(command, &spinted);
+ }
+
+ if (spinted.done == 0) {
+ /* Process request */
+ handle_api(&spinted.regs, &spinted.sregs);
+ spint_continue(&spinted);
+ } else {
+ int ch;
+
+ if (spinted.rc != 0) {
+ fprintf(stderr, "Process generated a return code of 0x%x.\n",
+ spinted.rc);
+ }
+ printf("[Hit return to continue]");
+ fflush(stdout);
+ while ((ch = getchar()) != '\n' && ch != EOF)
+ ;
+ shell_active = 0;
+ setconnmode();
+ ConnectScreen();
+ }
+ return shell_active;
+}
+
+
+/*
+ * Called from telnet.c to fork a lower command.com. We
+ * use the spint... routines so that we can pick up
+ * interrupts generated by application programs.
+ */
+
+
+int
+shell(argc,argv)
+int argc;
+char *argv[];
+{
+
+ ClearElement(spinted);
+ spinted.int_no = API_INTERRUPT_NUMBER;
+ if (argc == 1) {
+ command[0] = 0;
+ } else {
+ char *cmdptr;
+ int length;
+
+ argc--;
+ argv++;
+ strcpy(command, " /c");
+ cmdptr = command+strlen(command);
+ while (argc) {
+ if ((cmdptr+strlen(*argv)) >= (command+sizeof command)) {
+ fprintf(stderr, "Argument list too long at argument *%s*.\n",
+ *argv);
+ return 0;
+ }
+ *cmdptr++ = ' '; /* Blank separators */
+ strcpy(cmdptr, *argv);
+ cmdptr += strlen(cmdptr);
+ argc--;
+ argv++;
+ }
+ length = strlen(command)-1;
+ if (length < 0) {
+ length = 0;
+ }
+ command[0] = length;
+ }
+ need_to_start = 1;
+ shell_active = 1;
+ return 1; /* Go back to main loop */
+}
diff --git a/usr.bin/tn3270/distribution/sys_dos/termout.c b/usr.bin/tn3270/distribution/sys_dos/termout.c
new file mode 100644
index 0000000..0339072
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/termout.c
@@ -0,0 +1,514 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)termout.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <dos.h>
+#include "../general/general.h"
+
+#include "../telnet.ext"
+
+#include "../api/disp_asc.h"
+#include "../ascii/map3270.ext"
+
+#include "../ctlr/hostctlr.h"
+#include "../ctlr/externs.h"
+#include "../ctlr/declare.h"
+#include "../ctlr/oia.h"
+#include "../ctlr/screen.h"
+
+#include "../general/globals.h"
+
+#include "video.h"
+
+extern void EmptyTerminal();
+
+#define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
+ terminalCursorAddress:UnLocked? CursorAddress: HighestScreen())
+
+
+static int terminalCursorAddress; /* where the cursor is on term */
+static int screenInitd; /* the screen has been initialized */
+static int screenStopped; /* the screen has been stopped */
+
+static int needToRing; /* need to ring terinal bell */
+
+typedef struct {
+ char
+ data, /* The data for this position */
+ attr; /* The attributes for this position */
+} ScreenBuffer;
+
+ScreenBuffer Screen[MAXNUMBERLINES*MAXNUMBERCOLUMNS];
+ScreenBuffer saveScreen[sizeof Screen/sizeof Screen[0]];
+
+/* OurExitString - designed to keep us from going through infinite recursion */
+
+static void
+OurExitString(file, string, value)
+FILE *file;
+char *string;
+int value;
+{
+ static int recursion = 0;
+
+ if (!recursion) {
+ recursion = 1;
+ ExitString(file, string, value);
+ }
+}
+
+
+static void
+GoAway(from, where)
+char *from; /* routine that gave error */
+int where; /* cursor address */
+{
+ char foo[100];
+
+ sprintf(foo, "ERR from %s at %d (%d, %d)\n",
+ from, where, ScreenLine(where), ScreenLineOffset(where));
+ OurExitString(stderr, foo, 1);
+ /* NOTREACHED */
+}
+
+/*
+ * Routines to deal with the screen. These routines are lifted
+ * from mskermit.
+ */
+
+#define CRT_STATUS 0x3da /* Color card */
+#define DISPLAY_ENABLE 0x08 /* Enable */
+#define scrseg() ((crt_mode == 7)? 0xb000 : 0xb800)
+#define scrwait() if (crt_mode != 7) { \
+ while ((inp(CRT_STATUS)&DISPLAY_ENABLE) == 0) { \
+ ; \
+ } \
+ }
+static int
+ crt_mode,
+ crt_cols,
+ crt_lins,
+ curpage;
+
+/*
+ * Set the cursor position to where it belongs.
+ */
+
+static void
+setcursor(row, column, page)
+int
+ row,
+ column,
+ page;
+{
+ union REGS inregs, outregs;
+
+ inregs.h.dh = row;
+ inregs.h.dl = column;
+ inregs.h.bh = page;
+ inregs.h.ah = SetCursorPosition;
+
+ int86(BIOS_VIDEO, &inregs, &outregs);
+}
+/*
+ * Read the state of the video system. Put the cursor somewhere
+ * reasonable.
+ */
+
+static void
+scrini()
+{
+ union REGS inregs, outregs;
+
+ inregs.h.ah = CurrentVideoState;
+ int86(BIOS_VIDEO, &inregs, &outregs);
+
+ crt_mode = outregs.h.al;
+ crt_cols = outregs.h.ah;
+ crt_lins = 25;
+ curpage = outregs.h.bh;
+
+ inregs.h.ah = ReadCursorPosition;
+ inregs.h.bh = curpage;
+
+ int86(BIOS_VIDEO, &inregs, &outregs);
+
+ if (outregs.h.dh > crt_lins) {
+ outregs.h.dh = crt_lins;
+ }
+ if (outregs.h.dl > crt_cols) {
+ outregs.h.dl = crt_cols;
+ }
+ inregs.h.dh = outregs.h.dh;
+ inregs.h.dl = outregs.h.dl;
+ inregs.h.bh = curpage;
+
+ inregs.h.ah = SetCursorPosition;
+ int86(BIOS_VIDEO, &inregs, &outregs);
+}
+
+
+static void
+scrwrite(source, length, offset)
+ScreenBuffer *source;
+int
+ length,
+ offset;
+{
+ struct SREGS segregs;
+
+ segread(&segregs); /* read the current segment register */
+
+ scrwait();
+ movedata(segregs.ds, source, scrseg(), sizeof *source*offset,
+ sizeof *source*length);
+}
+
+static void
+scrsave(buffer)
+ScreenBuffer *buffer;
+{
+ struct SREGS segregs;
+
+ segread(&segregs); /* read the current segment register */
+
+ scrwait();
+ movedata(scrseg(), 0, segregs.ds, buffer, crt_cols*crt_lins*2);
+}
+
+static void
+scrrest(buffer)
+ScreenBuffer *buffer;
+{
+ scrwrite(buffer, crt_cols*crt_lins, 0);
+}
+
+static void
+TryToSend()
+{
+#define STANDOUT 0x0a /* Highlighted mode */
+#define NORMAL 0x02 /* Normal mode */
+#define NONDISPLAY 0x00 /* Don't display */
+
+#define DoAttribute(a) \
+ if (screenIsFormatted) { \
+ if (IsNonDisplayAttr(a)) { \
+ a = NONDISPLAY; /* don't display */ \
+ } else if (IsHighlightedAttr(a)) { \
+ a = STANDOUT; \
+ } else { \
+ a = NORMAL; \
+ } \
+ } else { \
+ a = NORMAL; /* do display on unformatted */\
+ }
+ ScreenImage *p, *upper;
+ ScreenBuffer *sp;
+ int fieldattr; /* spends most of its time == 0 or 1 */
+ int screenIsFormatted = FormattedScreen();
+
+/* OK. We want to do this a quickly as possible. So, we assume we
+ * only need to go from Lowest to Highest. However, if we find a
+ * field in the middle, we do the whole screen.
+ *
+ * In particular, we separate out the two cases from the beginning.
+ */
+ if ((Highest != HighestScreen()) || (Lowest != LowestScreen())) {
+ sp = &Screen[Lowest];
+ p = &Host[Lowest];
+ upper = &Host[Highest];
+ fieldattr = FieldAttributes(Lowest);
+ DoAttribute(fieldattr); /* Set standout, non-display status */
+
+ while (p <= upper) {
+ if (IsStartFieldPointer(p)) { /* New field? */
+ Highest = HighestScreen();
+ Lowest = LowestScreen();
+ TryToSend(); /* Recurse */
+ return;
+ } else if (fieldattr) { /* Should we display? */
+ /* Display translated data */
+ sp->data = disp_asc[GetHostPointer(p)];
+ } else {
+ sp->data = ' ';
+ }
+ sp->attr = fieldattr;
+ p++;
+ sp++;
+ }
+ } else { /* Going from Lowest to Highest */
+ ScreenImage *End = &Host[ScreenSize]-1;
+
+ sp = Screen;
+ p = Host;
+ fieldattr = FieldAttributes(LowestScreen());
+ DoAttribute(fieldattr); /* Set standout, non-display status */
+
+ while (p <= End) {
+ if (IsStartFieldPointer(p)) { /* New field? */
+ fieldattr = FieldAttributesPointer(p); /* Get attributes */
+ DoAttribute(fieldattr); /* Set standout, non-display */
+ }
+ if (fieldattr) { /* Should we display? */
+ /* Display translated data */
+ sp->data = disp_asc[GetHostPointer(p)];
+ } else {
+ sp->data = ' ';
+ }
+ sp->attr = fieldattr;
+ p++;
+ sp++;
+ }
+ }
+ terminalCursorAddress = CorrectTerminalCursor();
+ /*
+ * We might be here just to update the cursor address.
+ */
+ if (Highest >= Lowest) {
+ scrwrite(Screen+Lowest, (1+Highest-Lowest), Lowest);
+ }
+ setcursor(ScreenLine(terminalCursorAddress),
+ ScreenLineOffset(terminalCursorAddress), 0);
+ Lowest = HighestScreen()+1;
+ Highest = LowestScreen()-1;
+ if (needToRing) {
+ DataToTerminal("\7", 1);
+ needToRing = 0;
+ }
+ return;
+}
+
+/* InitTerminal - called to initialize the screen, etc. */
+
+void
+InitTerminal()
+{
+ InitMapping(); /* Go do mapping file (MAP3270) first */
+ if (!screenInitd) { /* not initialized */
+ MaxNumberLines = 24; /* XXX */
+ MaxNumberColumns = 80; /* XXX */
+ scrini();
+ scrsave(saveScreen); /* Save the screen buffer away */
+ ClearArray(Screen);
+ terminalCursorAddress = SetBufferAddress(0,0);
+ screenInitd = 1;
+ screenStopped = 0; /* Not stopped */
+ }
+}
+
+
+/* StopScreen - called when we are going away... */
+
+void
+StopScreen(doNewLine)
+int doNewLine;
+{
+ if (screenInitd && !screenStopped) {
+ scrrest(saveScreen);
+ setcursor(NumberLines-1, 1, 0);
+ if (doNewLine) {
+ StringToTerminal("\r\n");
+ }
+ EmptyTerminal();
+ screenStopped = 1;
+ }
+}
+
+
+/* RefreshScreen - called to cause the screen to be refreshed */
+
+void
+RefreshScreen()
+{
+ Highest = HighestScreen();
+ Lowest = LowestScreen();
+ TryToSend();
+}
+
+
+/* ConnectScreen - called to reconnect to the screen */
+
+void
+ConnectScreen()
+{
+ if (screenInitd) {
+ RefreshScreen();
+ screenStopped = 0;
+ }
+}
+
+/* LocalClearScreen() - clear the whole ball of wax, cheaply */
+
+void
+LocalClearScreen()
+{
+ Clear3270();
+ Lowest = LowestScreen(); /* everything in sync... */
+ Highest = HighestScreen();
+ TryToSend();
+}
+
+/*
+ * Implement the bell/error message function.
+ */
+
+int
+ bellwinup = 0; /* If != 0, length of bell message */
+static int
+ bell_len = 0; /* Length of error message */
+
+
+void
+BellOff()
+{
+ ScreenBuffer a[100];
+ int i;
+
+ if (bellwinup) {
+ unsigned char blank = ' ';
+
+ for (i = 0; i < bell_len; i++) {
+ a[i].attr = NORMAL;
+ a[i].data = ' ';
+ }
+ }
+ scrwrite(a, bell_len, 24*80); /* XXX */
+}
+
+
+void
+RingBell(s)
+char *s;
+{
+ needToRing = 1;
+ if (s) {
+ int i;
+ ScreenBuffer bellstring[100];
+
+ bell_len = strlen(s);
+ bellwinup = 1;
+ if (bell_len > sizeof bellstring-1) {
+ OurExitString(stderr, "Bell string too long.", 1);
+ }
+ for (i = 0; i < bell_len; i++) {
+ bellstring[i].attr = STANDOUT;
+ bellstring[i].data = s[i];
+ }
+ scrwrite(bellstring, bell_len, 24*80); /* XXX */
+ }
+}
+
+/*
+ * Update the OIA area.
+ */
+
+void
+ScreenOIA(oia)
+OIA *oia;
+{
+}
+
+
+/* returns a 1 if no more output available (so, go ahead and block),
+ or a 0 if there is more output available (so, just poll the other
+ sources/destinations, don't block).
+ */
+
+int
+DoTerminalOutput()
+{
+ /* called just before a select to conserve IO to terminal */
+ if (!(screenInitd||screenStopped)) {
+ return 1; /* No output if not initialized */
+ }
+ if ((Lowest <= Highest) || needToRing ||
+ (terminalCursorAddress != CorrectTerminalCursor())) {
+ TryToSend();
+ }
+ if (Lowest > Highest) {
+ return 1; /* no more output now */
+ } else {
+ return 0; /* more output for future */
+ }
+}
+
+/*
+ * The following are defined to handle transparent data.
+ */
+
+void
+TransStop()
+{
+ RefreshScreen();
+}
+
+void
+TransOut(buffer, count, kind, control)
+unsigned char *buffer;
+int count;
+int kind; /* 0 or 5 */
+int control; /* To see if we are done */
+{
+ char *ptr;
+
+ while (DoTerminalOutput() == 0) {
+ ;
+ }
+ for (ptr = buffer; ptr < buffer+count; ptr++) {
+ *ptr &= 0x7f; /* Turn off parity bit */
+ }
+ (void) DataToTerminal(buffer, count);
+ if (control && (kind == 0)) { /* Send in AID byte */
+ SendToIBM();
+ } else {
+ TransInput(1, kind); /* Go get some data */
+ }
+}
+
+/*
+ * init_screen()
+ *
+ * Initialize variables used by screen.
+ */
+
+void
+init_screen()
+{
+ bellwinup = 0;
+}
+
+
diff --git a/usr.bin/tn3270/distribution/sys_dos/termout.ext b/usr.bin/tn3270/distribution/sys_dos/termout.ext
new file mode 100644
index 0000000..d3c8fb4
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/termout.ext
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)termout.ext 8.1 (Berkeley) 6/6/93
+ */
+
+extern void
+ StartScreen(),
+ StopScreen(),
+ ConnectScreen(),
+ ClearScreen(),
+ LocalClearScreen(),
+ RefreshScreen(),
+ RingBell(),
+ AddHost();
+
+extern int
+ DoTerminalOutput();
diff --git a/usr.bin/tn3270/distribution/sys_dos/video.h b/usr.bin/tn3270/distribution/sys_dos/video.h
new file mode 100644
index 0000000..8fffe6c
--- /dev/null
+++ b/usr.bin/tn3270/distribution/sys_dos/video.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)video.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This is a header file describing the interface via int 10H to the
+ * video subsystem.
+ */
+
+#define BIOS_VIDEO 0x10
+
+typedef enum {
+ SetMode = 0,
+ SetCursorType,
+ SetCursorPosition,
+ ReadCursorPosition,
+ ReadLightPenPosition,
+ SelectActiveDisplayPage,
+ ScrollActivePageUp,
+ ScrollActivePageDown,
+ ReadAttribute_Character,
+ WriteAttribute_Character,
+ WriteCharacterOnly,
+ SetColorPalette,
+ WriteDot,
+ ReadDot,
+ WriteTeletypeToActivePage,
+ CurrentVideoState,
+ Reserved16,
+ Reserved17,
+ Reserved18,
+ WriteString
+} VideoOperationsType;
+
+typedef enum {
+ bw_40x25 = 0,
+ color_40x25,
+ bw_80x25,
+ color_80x25,
+ color_320x200,
+ bw_320x200,
+ bw_640x200,
+ internal_bw_80x25
+} VideoModeType;
diff --git a/usr.bin/tn3270/distribution/telnet/Makefile_ultrix b/usr.bin/tn3270/distribution/telnet/Makefile_ultrix
new file mode 100644
index 0000000..dbb93ea
--- /dev/null
+++ b/usr.bin/tn3270/distribution/telnet/Makefile_ultrix
@@ -0,0 +1,179 @@
+# @(#)Makefile 1.4 (Berkeley) 5/15/88
+
+# This is the makefile for an Ultrix system. The current Ultrix make(1) doesn't
+# support VPATH, so we expand everything out.
+
+# The following is the telnet makefile for tn3270, using the shared telnet
+# sources.
+
+#
+# TERMCAP Define this if your system is termcap based,
+# otherwise a terminfo based system is assumed.
+#
+# SRCRT Includes code to allow you to specify source routes.
+# Format is:
+# [!]@hop1@hop2...[@|:]dst
+# Leading ! means strict source route.
+#
+# NOSTRNCASECMP Define this if you do not have strncasecmp() in
+# your C libarary.
+#
+# USE_TERMIO Define this if you have System V termio structures.
+# What is here is how things are on Cray computers.
+#
+# KLUDGELINEMODE Define this to get the kludged up version of linemode
+# that was in 4.3BSD. This is a good thing to have
+# around for talking to older systems.
+#
+
+DEFINES= -DTERMCAP -DSRCRT -DKLUDGELINEMODE
+
+
+VPATH = ../../telnet/Source
+XINCLUDES= -I../../telnet/Source
+INCLUDES= -I.
+XDEFINES = -DTN3270
+OPTIMIZE= -O
+CFLAGS = ${OPTIMIZE} ${INCLUDES} ${DEFINES}
+XCFLAGS= ${XINCLUDES} ${XDEFINES}
+LD = ld
+LDFLAGS = -r
+PRINT = print
+ACTION = sccs tell
+LIBC= /lib/libc.a
+SD= ../../telnet/Source/
+ALLH= ${SD}defines.h ${SD}externs.h ${SD}fdset.h ${SD}general.h ${SD}ring.h ${SD}types.h
+SRCS= ${SD}commands.c ${SD}main.c ${SD}network.c ${SD}ring.c \
+ ${SD}sys_bsd.c ${SD}sys_dos.c ${SD}telnet.c ${SD}terminal.c \
+ ${SD}tn3270.c ${SD}utilities.c
+ALLHC= ${ALLH} ${SRCS}
+ALLPRINT = ${ALLHC}
+ALLSOURCE= ${ALLHC} Makefile Makefile_ultrix
+OBJS= commands.o main.o network.o ring.o sys_bsd.o sys_dos.o \
+ telnet.o terminal.o tn3270.o utilities.o
+
+.c.o:
+ ${CC} -c ${CFLAGS} ${XCFLAGS} $<
+
+telprog.o: ${OBJS} ${LIBC}
+ ${LD} ${LDFLAGS} -o $@ ${OBJS}
+
+clean: FRC
+ rm -f ${OBJS} core telnet
+
+depend: FRC ${SRCS}
+ mkdep ${CFLAGS} ${SRCS}
+
+lint: FRC ${SRCS}
+ lint ${CFLAGS} ${SRCS}
+
+tags: FRC ${ALLHC}
+ ctags ${ALLHC}
+
+print: FRC ${ALLPRINT}
+ ${PRINT} ${ALLPRINT}
+
+commands.o: ${SD}commands.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}commands.c
+
+main.o: ${SD}main.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}main.c
+
+network.o: ${SD}network.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}network.c
+
+ring.o: ${SD}ring.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}ring.c
+
+sys_bsd.o: ${SD}sys_bsd.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}sys_bsd.c
+
+sys_dos.o: ${SD}sys_dos.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}sys_dos.c
+
+telnet.o: ${SD}telnet.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}telnet.c
+
+terminal.o: ${SD}terminal.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}terminal.c
+
+tn3270.o: ${SD}tn3270.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}tn3270.c
+
+utilities.o: ${SD}utilities.c
+ ${CC} -c ${CFLAGS} ${XCFLAGS} ${SD}utilities.c
+
+
+action: FRC
+ ${ACTION}
+
+clist: FRC ${SRCS}
+ @for i in ${SRCS} ; \
+ do (echo ${DIRPATH}$$i); done
+
+hclist: FRC ${ALLHC}
+ @for i in ${ALLHC} ; \
+ do (echo ${DIRPATH}$$i); done
+
+sourcelist: FRC ${ALLSOURCE}
+ @for i in ${ALLSOURCE} ../../telnet/Makefile ; \
+ do (echo ${DIRPATH}$$i); done
+
+FRC:
+
+# DO NOT DELETE THIS LINE -- mkdep uses it.
+# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
+
+commands.o: ../../telnet/Source/commands.c /usr/include/sys/types.h
+commands.o: /usr/include/sys/socket.h /usr/include/netinet/in.h
+commands.o: /usr/include/signal.h /usr/include/machine/trap.h
+commands.o: /usr/include/netdb.h /usr/include/ctype.h
+commands.o: /usr/include/arpa/telnet.h ../../telnet/Source/ring.h
+commands.o: ../../telnet/Source/externs.h /usr/include/stdio.h
+commands.o: /usr/include/setjmp.h ../../telnet/Source/defines.h
+commands.o: ../../telnet/Source/types.h
+main.o: ../../telnet/Source/main.c /usr/include/sys/types.h
+main.o: ../../telnet/Source/ring.h ../../telnet/Source/externs.h
+main.o: /usr/include/stdio.h /usr/include/setjmp.h
+main.o: ../../telnet/Source/defines.h
+network.o: ../../telnet/Source/network.c /usr/include/sys/types.h
+network.o: /usr/include/sys/socket.h /usr/include/sys/time.h
+network.o: /usr/include/time.h /usr/include/errno.h /usr/include/arpa/telnet.h
+network.o: ../../telnet/Source/ring.h ../../telnet/Source/defines.h
+network.o: ../../telnet/Source/externs.h /usr/include/stdio.h
+network.o: /usr/include/setjmp.h ../../telnet/Source/fdset.h
+ring.o: ../../telnet/Source/ring.c /usr/include/stdio.h /usr/include/errno.h
+ring.o: /usr/include/sys/types.h /usr/include/sys/ioctl.h
+ring.o: /usr/include/sys/ttychars.h /usr/include/sys/ttydev.h
+ring.o: /usr/include/sys/socket.h ../../telnet/Source/ring.h
+ring.o: ../../telnet/Source/general.h
+sys_bsd.o: ../../telnet/Source/sys_bsd.c /usr/include/sys/ioctl.h
+sys_bsd.o: /usr/include/sys/ttychars.h /usr/include/sys/ttydev.h
+sys_bsd.o: /usr/include/sys/types.h /usr/include/sys/time.h /usr/include/time.h
+sys_bsd.o: /usr/include/sys/socket.h /usr/include/signal.h
+sys_bsd.o: /usr/include/machine/trap.h /usr/include/errno.h
+sys_bsd.o: ../../telnet/Source/ring.h ../../telnet/Source/fdset.h
+sys_bsd.o: ../../telnet/Source/defines.h ../../telnet/Source/externs.h
+sys_bsd.o: /usr/include/stdio.h /usr/include/setjmp.h
+sys_bsd.o: ../../telnet/Source/types.h
+sys_dos.o: ../../telnet/Source/sys_dos.c
+telnet.o: ../../telnet/Source/telnet.c /usr/include/sys/types.h
+telnet.o: /usr/include/curses.h /usr/include/stdio.h /usr/include/sgtty.h
+telnet.o: /usr/include/sys/ioctl.h /usr/include/sys/ttychars.h
+telnet.o: /usr/include/sys/ttydev.h /usr/include/arpa/telnet.h
+telnet.o: /usr/include/strings.h ../../telnet/Source/ring.h
+telnet.o: ../../telnet/Source/defines.h ../../telnet/Source/externs.h
+telnet.o: /usr/include/stdio.h /usr/include/setjmp.h
+telnet.o: ../../telnet/Source/types.h ../../telnet/Source/general.h
+telnet.o: /usr/include/varargs.h
+terminal.o: ../../telnet/Source/terminal.c /usr/include/arpa/telnet.h
+terminal.o: /usr/include/sys/types.h ../../telnet/Source/ring.h
+terminal.o: ../../telnet/Source/externs.h /usr/include/stdio.h
+terminal.o: /usr/include/setjmp.h ../../telnet/Source/types.h
+tn3270.o: ../../telnet/Source/tn3270.c ../../telnet/Source/fdset.h
+utilities.o: ../../telnet/Source/utilities.c /usr/include/arpa/telnet.h
+utilities.o: /usr/include/sys/types.h /usr/include/ctype.h
+utilities.o: ../../telnet/Source/ring.h ../../telnet/Source/externs.h
+utilities.o: /usr/include/stdio.h /usr/include/setjmp.h
+
+# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
diff --git a/usr.bin/tn3270/distribution/ultrix.curses b/usr.bin/tn3270/distribution/ultrix.curses
new file mode 100644
index 0000000..f5fcd94
--- /dev/null
+++ b/usr.bin/tn3270/distribution/ultrix.curses
@@ -0,0 +1,46 @@
+Return-Path: mtxinu!kinetics!minshall@ucbvax.Berkeley.EDU
+Received: from ucbvax.berkeley.edu
+ by violet.berkeley.edu (5.54 (CFC 4.22.3)/1.16.17l)
+ id AA23846; Wed, 30 Mar 88 19:54:24 PST
+Received: by ucbvax.berkeley.edu (5.59/1.26)
+ id AA09851; Tue, 29 Mar 88 23:25:19 PST
+Received: by mtxinu.UUCP (5.51/4.7)
+ id AA05135; Tue, 29 Mar 88 17:32:01 PST
+Message-Id: <8803300132.AA05135@mtxinu.UUCP>
+Date: Thu, 17 Mar 88 11:06:10 pst
+From: mtxinu!kinetics!minshall@ucbvax.Berkeley.EDU (Greg Minshall)
+To: mtxinu!minshall
+
+Path: kinetics!zehntel!varian!ptsfa!pacbell!ames!ll-xn!husc6!panda!teddy!jpn
+From: jpn@teddy.UUCP (John P. Nelson)
+Newsgroups: comp.bugs.4bsd
+Subject: Re: Ultrix curses problem
+Keywords: curses ultrix bug fix
+Message-ID: <4668@teddy.UUCP>
+Date: 14 Mar 88 19:43:39 GMT
+References: <535@hscfvax.harvard.edu>
+Reply-To: jpn@teddy.UUCP (John P. Nelson)
+Organization: GenRad, Inc., Concord, Mass.
+Lines: 21
+
+>I have found the bug, fixable as above, in both Ultrix 1.2 and 2.0. Feedback
+>from those brave souls who have brought up 2.2 would be most welcome.
+
+Yup, the bug exists in Ultrix 2.2 as well. I have reported this bug to
+DEC support every time we get a new release. I assume that my bug reports
+are getting filed (in the circular file).
+
+It is clear that whoever made this "fix" at DEC had no understanding
+of curses whatsoever. As in the earlier posting, the bug can be fixed
+by replacing the definitions of the nl() and nonl() macros in curses.h with:
+
+#define nl() (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
+#define nonl() (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
+
+- john nelson.
+
+P.S. I did reach someone at DEC who would listen (Thanks Chet!) My
+ understanding is that they have no plans to fix this bug, as curses
+ is now "owned" by some group in England, who is working on a port of the
+ terminfo curses for Ultrix 2.4. I'm just pissed that my bug reports
+ submitted for Ultrix 1.2 and 2.0 were ignored.
diff --git a/usr.bin/tn3270/distribution/utilities/adm3a.keys b/usr.bin/tn3270/distribution/utilities/adm3a.keys
new file mode 100644
index 0000000..4e65374
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/adm3a.keys
@@ -0,0 +1,78 @@
+1LSI ADM 3A Key Definitions for IBM 3277 Terminal Emulation 06/18/81
++LSI ADM 3A Key Definitions for IBM 3277 Terminal Emulation 06/18/81
+
+ 3277 Key ADM Key(s) | 3277 Key LSI ADM 3A Key(s)
+ ======== ========== | ======== ====================
+ |
+ Cursor Movement Keys | Command Keys
++______ ________ ____ _______ ____
+ |
+ New Line Ctrl-N -or- | Enter Return
+ Home | Clear Ctrl-Z
+ Tab Ctrl-I |
+ Back Tab Ctrl-B | Program Function Keys
++ _______ ________ ____
+ Cursor Left Ctrl-H |
+ Cursor Right Ctrl-L | PF1 Esc 1
+ Cursor Up Ctrl-K | PF2 Esc 2
+ Cursor Down Ctrl-J -or- | PF3 Esc 3
+ Line Feed | PF4 Esc 4
+ Home Ctrl-@ | PF5 Esc 5
+ | PF6 Esc 6
+ | PF7 Esc 7
+ Edit Control Keys | PF8 Esc 8
++____ _______ ____
+ | PF9 Esc 9
+ Delete Char Ctrl-D -OR- Rub | PF10 Esc 0
+ Erase EOF Ctrl-E | PF11 Esc Colon
+ Erase Input Ctrl-W | PF12 Esc Minus
+ Insert Mode Esc Space | PF13 Ctrl-F 1 3
+ End Insert Esc Space | PF14 Ctrl-F 1 4
+ Dup Ctrl-U | PF15 Ctrl-F 1 5
+ Field Mark Ctrl-Y | PF16 Ctrl-F 1 6
+ | PF17 Ctrl-F 1 7
+ Series-1 Control Keys | PF18 Ctrl-F 1 8
++________ _______ ____
+ | PF19 Ctrl-F 1 9
+ Reset After Error Ctrl-R | PF20 Ctrl-F 2 0
+ Keyboard Unlock Ctrl-T | PF21 Ctrl-F 2 1
+ Purge Input Buffer Ctrl-X | PF22 Ctrl-F 2 2
+ Stop Output Ctrl-S | PF23 Ctrl-F 2 3
+ Start Output Ctrl-Q | PF24 Ctrl-F 2 4
+ Re-Display Screen Ctrl-V |
+ | Program Attention Keys
++ _______ _________ ____
+ Miscellaneous 3277 Keys |
++_____________ ____ ____
+ | PA1 Ctrl-P 1
+ Cursor Select Esc Period | PA2 Ctrl-P 2
+ Test Request (not supported) | PA3 Ctrl-P 3
+
+ Series 1 Local Editing Keys
++______ _ _____ _______ ____
+
+ Set Tab Stop Esc Semicolon
+ Clear (all) Tab Stops Esc Plus
+ Set Left Margin (New Line) Esc LeftParen
+ Set Home (Home) Esc Exclamation
+ Tab to Next Local Tab Stop Esc i (or Esc I)
+ Tab to Previous Local Tab Stop Esc b (or Esc B)
+ Indent (to Next Local Tab Stop - New Line) Esc l (or Esc L)
+ Undent (to Previous Local Tab Stop - New Line) Esc h (or Esc H)
+
+
+ Note: If the terminal hangs up, enter "Ctrl-H" and see if the
+ cursor moves. If it does, you are trying to enter data
+ into a protected field, trying to insert a character when
+ the last character of the field is non-blank, or trying to
+ communicate to a virtual process which is not accepting data.
+ If the cursor did not move, a transmission error or terminal
+ lock has occurred. Enter the following sequence to unhang
+ the terminal: "Ctrl-R Ctrl-T Ctrl-X Ctrl-Q Ctrl-V".
+
+ ********** DRAFT ONLY - SPECIFICATION SUBJECT TO CHANGE **********
++********** DRAFT ONLY - SPECIFICATION SUBJECT TO CHANGE **********
++********** DRAFT ONLY - SPECIFICATION SUBJECT TO CHANGE **********
++********** DRAFT ONLY - SPECIFICATION SUBJECT TO CHANGE **********
+
+
diff --git a/usr.bin/tn3270/distribution/utilities/cross.c b/usr.bin/tn3270/distribution/utilities/cross.c
new file mode 100644
index 0000000..191aac0
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/cross.c
@@ -0,0 +1,55 @@
+/*
+ This program is, essentially, a cross product generator. It takes
+ an input file which is said to consist of a number of lines, and
+ expands each line. A line like
+ (a,b)(c,d)
+ will be expanded to lines like
+ ac
+ ad
+ bc
+ bd
+ (without regard for the ORDER of the output; ie: the lines can appear
+ in any order).
+
+ Parenthesis can be nested, so
+ (a,b)(c(d,e),f)
+ will produce
+ acd
+ ace
+ af
+ bcd
+ bce
+ bf
+ */
+
+
+#include <stdio.h>
+
+char leftParen, /* The left parenthesis character */
+ rightParen; /* The right parenthesis character */
+
+
+/* Finds next occurrence of 'character' at this level of nesting.
+ Returns 0 if no such character found.
+ */
+
+char *
+ThisLevel(string, character)
+char *string, character;
+{
+ int level; /* Level 0 is OUR level */
+
+ level = 0;
+
+ while (*string != '\0') {
+ if (*string == leftParen)
+ level++;
+ else if (*string == rightParen) {
+ level--;
+ if (level < 0)
+ return(0);
+ }
+ if ((level == 0) && (*string == character))
+ return(string);
+ string++;
+ }
diff --git a/usr.bin/tn3270/distribution/utilities/makefile b/usr.bin/tn3270/distribution/utilities/makefile
new file mode 100644
index 0000000..0cb5b3e
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/makefile
@@ -0,0 +1,121 @@
+#
+# Copyright (c) 1988 Regents of the University of California.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms are permitted
+# provided that the above copyright notice and this paragraph are
+# duplicated in all such forms and that any documentation,
+# advertising materials, and other materials related to such
+# distribution and use acknowledge that the software was developed
+# by the University of California, Berkeley. The name of the
+# University may not be used to endorse or promote products derived
+# from this software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# @(#)makefile 8.1 (Berkeley) 6/6/93
+#
+# msdos versus unix defines
+O = .o
+#PC_O = .obj
+
+X =
+#PC_X = .exe
+
+L =
+#PC_L = -link
+
+CC = cc
+#PC_CC = cl
+
+MV = mv
+#PC_MV = rename
+
+RM = rm -f
+#PC_RM= erase
+
+LINT_ARGS =
+#PC_LINT_ARGS = -DLINT_ARGS
+
+DEBUG_FLAGS = -g
+#PC_DEBUG_FLAGS = -Zi -Od
+
+AR = ar
+AR1 = cr
+AR2 =
+AR3 =
+#PC_AR = lib
+#PC_AR1 =
+#PC_AR2 = +
+#PC_AR3 = ";"
+
+RANLIB = ranlib
+#PC_RANLIB = echo "Done with "
+
+DEFINES = ${LINT_ARGS}
+
+CFLAGS = ${DEBUG_FLAGS} -I..
+
+PRINT = lpr -p
+
+ALLC = tnrecv.c
+ALLH = tncomp.h
+
+ALLPRINT = ${ALLH} ${ALLC}
+
+ALLSOURCE = ${ALLPRINT} makefile makefile.mak
+
+ALLO = tnrecv$O
+
+.c.obj:
+ ${CC} ${CFLAGS} -c $<
+
+all: tnrecv$X
+
+tnrecv$X: tnrecv$O
+ ${CC} ${CFLAGS} -o $@ tnrecv$O $L ../api/apilib.a
+
+clean:
+ for i in makefile.bak ${ALLO} errs tnrecv$X; \
+ do (${RM} $$i); done
+
+.DEFAULT:
+ sccs get $<
+
+sccsclean:
+ -sccs clean
+ -sccs get makefile
+
+action:
+ ${ACTION}
+
+print:
+ ${PRINT} ${ALLPRINT}
+
+sourcelist: ${ALLSOURCE} tarread.exe
+ @for i in ${ALLSOURCE} tarread.exe; \
+ do (echo ${DIRPATH}$$i); done
+
+depend:
+ grep '^#include' ${ALLC} | grep -v '<' | \
+ sed -e 's/:[^"]*"\([^"]*\)".*/: \1/' \
+ -e 's/\.c/$$O/' | \
+ awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
+ else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
+ else rec = rec " " $$2 } } \
+ END { print rec } ' > makedep
+ echo '$$r makedep' >>eddep
+ echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
+ echo '$$r makedep' >>eddep
+ echo 'w' >>eddep
+ -rm -f makefile.bak
+ cp makefile makefile.bak
+ ed - makefile < eddep
+ rm eddep makedep
+
+# DO NOT DELETE THIS LINE
+
+#include "tncomp.h" "../api/api.h" "../ctlr/function.h" "../ctlr/hostctlr.h"
+#include "../ctlr/oia.h" "../ctlr/screen.h" "../apilib/disp_asc.h"
+#include "../apilib/astosc.h" "../general/general.h"
diff --git a/usr.bin/tn3270/distribution/utilities/srccmd/tar/makefile b/usr.bin/tn3270/distribution/utilities/srccmd/tar/makefile
new file mode 100644
index 0000000..f4618af
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/srccmd/tar/makefile
@@ -0,0 +1,8 @@
+tarread.obj: tarread.c
+ cl /c /Zd tarread.c
+
+tarread.exe: tarread.obj
+ link tarread,,tarread/map/lin;
+
+tarread.sym: tarread.map
+ mapsym /l tarread
diff --git a/usr.bin/tn3270/distribution/utilities/srccmd/tar/tar.h b/usr.bin/tn3270/distribution/utilities/srccmd/tar/tar.h
new file mode 100644
index 0000000..0345756
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/srccmd/tar/tar.h
@@ -0,0 +1,19 @@
+/* tar.h */
+
+#define TBLOCK 512
+#define NAMSIZ 100
+
+union hblock {
+ char dummy[TBLOCK];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char gid[8];
+ char uid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ } dbuf;
+};
diff --git a/usr.bin/tn3270/distribution/utilities/srccmd/tar/tarread.c b/usr.bin/tn3270/distribution/utilities/srccmd/tar/tarread.c
new file mode 100644
index 0000000..6b71090
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/srccmd/tar/tarread.c
@@ -0,0 +1,208 @@
+/* tarread.c */
+/* Copyright (c) 1985, by Carnegie-Mellon University */
+
+#include <stdio.h>
+#include <v2tov3.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include "tar.h"
+
+char usage[] = "tarread: usage: tarread tx[vwz] tarfile\n";
+union hblock hbuf;
+
+int verbose = 0;
+int confirm = 0;
+int binary = 0;
+char cmd;
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ FILE *fp;
+ char *cp;
+
+ if (argc != 3) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+
+ for (cp = argv[1]; *cp; cp++)
+ switch (*cp) {
+ case 't':
+ case 'x':
+ cmd = *cp;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+ case 'z':
+ binary++;
+ break;
+ case 'w':
+ confirm++;
+ break;
+ default:
+ fprintf(stderr, "tarread: unknown switch %c\n", *cp);
+ fprintf(stderr, usage);
+ exit(1);
+ }
+
+ if ((fp = fopen(argv[2], "rb")) == NULL) {
+ fprintf(stderr, "tarrread: cannot open %s\n", argv[2]);
+ exit(1);
+ }
+
+ for (;;) {
+ if (fread(&hbuf, sizeof(hbuf), 1, fp) != 1) {
+ perror("fread");
+ exit(1);
+ }
+ if (!proc_file(fp))
+ break;
+ }
+}
+
+
+int proc_file(fp)
+FILE *fp;
+{
+ char name[NAMSIZ];
+ unsigned short mode;
+ short uid, gid;
+ long size, mtime;
+ char c;
+ int confrmd;
+ long skip;
+
+ if (hbuf.dbuf.name[0] == '\0')
+ return (NULL);
+
+ strcpy(name, hbuf.dbuf.name);
+ if (sscanf(hbuf.dbuf.mode, "%o", &mode) != 1)
+ fprintf("Couldn't read mode\n");
+ if (sscanf(hbuf.dbuf.uid, "%o", &uid) != 1)
+ fprintf("Couldn't read uid\n");
+ if (sscanf(hbuf.dbuf.gid, "%o", &gid) != 1)
+ fprintf("Couldn't read gid\n");
+ if (sscanf(hbuf.dbuf.size, "%12lo %12lo", &size, &mtime) != 2)
+ fprintf("Couldn't read size or mtime\n");
+
+ skip = (size + TBLOCK - 1) / TBLOCK * TBLOCK;
+
+ switch (cmd) {
+ case 't':
+ if (verbose)
+ printf("%8o %d/%d\t %6ld %.24s %s\n", mode,
+ uid, gid, size, ctime(&mtime), name);
+ else
+ printf("%s\n", name);
+
+ break;
+
+ case 'x':
+ if (verbose)
+ printf("x %s: ", name);
+ confrmd = 1;
+
+ if (confirm) {
+ confrmd = 0;
+ if ((c = getchar()) == 'y')
+ confrmd++;
+ while (c != '\n')
+ c = getchar();
+ if(!confrmd)
+ break;
+ }
+
+ if(extract(name, size, mode, mtime, fp))
+ skip = 0;
+
+ if (verbose)
+ printf("\n");
+ break;
+ }
+ if (fseek(fp, skip, 1)) {
+ perror("fseek");
+ exit(1);
+ }
+ return (1);
+}
+
+
+int extract(fname, size, mode, mtime, ifp)
+char *fname;
+long size;
+unsigned short mode;
+long mtime;
+FILE *ifp;
+{
+ FILE *ofp;
+ char fbuf[TBLOCK];
+ long copied, left;
+ char *s, *np, *strchr();
+ struct stat sbuf;
+
+ for(np = fname; s = strchr(np, '/'); np = s+1) {
+ *s = '\0';
+ if(stat(fname, &sbuf)) {
+ if(mkdir(fname))
+ perror("mkdir");
+ } else if(!(sbuf.st_mode & S_IFDIR)) {
+ fprintf(stderr, "\n%s: Not a directory", fname);
+ *s = '/';
+ fprintf(stderr, "\ntar: %s - cannot create", fname);
+ return (0);
+ }
+ *s = '/';
+ }
+ if(!*np)
+ return (0);
+
+ if (binary) {
+ if ((ofp = fopen(fname, "wb")) == NULL) {
+ perror("extract:");
+ return (0);
+ }
+ } else {
+ if ((ofp = fopen(fname, "w")) == NULL) {
+ perror("extract:");
+ return (0);
+ }
+ }
+
+ for(copied = 0; copied < size; copied += TBLOCK) {
+ if(fread(fbuf, TBLOCK, 1, ifp) != 1) {
+ perror("fread");
+ exit(1);
+ }
+ left = size - copied;
+ if(fwrite(fbuf, (int)min(left, TBLOCK), 1, ofp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ }
+
+ if(fclose(ofp)) {
+ perror("fclose");
+ exit(1);
+ }
+
+ /*
+ * Now, set modification time.
+ */
+ {
+#include <sys\utime.h>
+ struct utimbuf utim;
+
+ utim.modtime = mtime;
+
+ if (utime(fname, &utim) == -1) {
+ perror("utime");
+ exit(1);
+ }
+ }
+
+ return (1);
+}
diff --git a/usr.bin/tn3270/distribution/utilities/tncomp.h b/usr.bin/tn3270/distribution/utilities/tncomp.h
new file mode 100644
index 0000000..c726297
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/tncomp.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tncomp.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Where the fields fall on the formatted screen used by tncomp, tnrecv,
+ * and tnsend.
+ */
+
+#define SEND_SEQUENCE 1
+#define SEND_SEQUENCE_LENGTH 23
+
+#define ACK_SEQUENCE (SEND_SEQUENCE+SEND_SEQUENCE_LENGTH+1)
+#define ACK_SEQUENCE_LENGTH 22
+
+#define CHECKSUM (ACK_SEQUENCE+ACK_SEQUENCE_LENGTH+1)
+#define CHECKSUM_LENGTH 32
+
+#define DATA (CHECKSUM+CHECKSUM_LENGTH+1)
+#define DATA_LENGTH ((80*22)+79)
diff --git a/usr.bin/tn3270/distribution/utilities/tnrecv.c b/usr.bin/tn3270/distribution/utilities/tnrecv.c
new file mode 100644
index 0000000..4474691
--- /dev/null
+++ b/usr.bin/tn3270/distribution/utilities/tnrecv.c
@@ -0,0 +1,674 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tnrecv.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include <api/apilib.h>
+
+#include "tncomp.h"
+
+
+#include "../ctlr/api.h"
+#include "../ctlr/function.h"
+#include "../ctlr/hostctlr.h"
+#include "../ctlr/oia.h"
+#include "../ctlr/screen.h"
+
+#include "../api/disp_asc.h"
+#include "../api/astosc.h"
+
+#include "../general/general.h"
+
+ScreenImage Host[MAXSCREENSIZE];
+
+static char
+ a_send_sequence[SEND_SEQUENCE_LENGTH+1],
+ a_ack_sequence[ACK_SEQUENCE_LENGTH+1],
+ a_checksum[CHECKSUM_LENGTH+1],
+ data_array[DATA_LENGTH+1];
+
+static int
+ verbose,
+ blocks,
+ enter_index,
+ clear_index,
+ ScreenSize,
+ session_id;
+
+static unsigned int
+ send_sequence,
+ ack_sequence = -1,
+ checksum;
+
+api_perror(string)
+char *string;
+{
+ fprintf(stderr, "Error: [0x%x/0x%x:0x%x/0x%x] from %s.\n",
+ api_sup_fcn_id, api_sup_errno,
+ api_fcn_fcn_id, api_fcn_errno, string);
+}
+
+
+char *
+session_type(type)
+int type;
+{
+ switch (type) {
+ case TYPE_WSCTL:
+ return "work station control";
+ case TYPE_DFT:
+ return "distributed function terminal";
+ case TYPE_CUT:
+ return "control unit terminal";
+ case TYPE_NOTEPAD:
+ return "notepad";
+ case TYPE_PC:
+ return "personal computer";
+ default:
+ return "(UNKNOWN)";
+ }
+}
+
+static int
+wait_for_ps_or_oia()
+{
+#if defined(unix)
+ return api_ps_or_oia_modified();
+#endif /* defined(unix) */
+}
+
+
+static int
+wait_for_unlock()
+{
+ OIA oia;
+ ReadOiaGroupParms re;
+ static char zeroes[sizeof oia.input_inhibited] = { 0 };
+
+ do {
+ re.rc = re.function_id = 0;
+ re.session_id = session_id;
+ re.oia_buffer = (char far *) &oia;
+ re.oia_group_number = API_OIA_ALL_GROUPS;
+ if (api_read_oia_group(&re) == -1) {
+ api_perror("api_read_oia_group");
+ return -1;
+ } else if (verbose) {
+ if (IsOiaReady3274(&oia)) {
+ printf("3274 ready, ");
+ }
+ if (IsOiaMyJob(&oia)) {
+ printf("my job, ");
+ }
+ if (IsOiaInsert(&oia)) {
+ printf("insert mode, ");
+ }
+ if (IsOiaSystemLocked(&oia)) {
+ printf("system locked, ");
+ }
+ if (IsOiaTWait(&oia)) {
+ printf("terminal wait, ");
+ }
+ printf("are some bits from the OIA.\n");
+ }
+ /* We turned this on, so turn it off now */
+ ResetOiaApiInhibit(&oia);
+ if (memcmp(zeroes, oia.input_inhibited, sizeof oia.input_inhibited)) {
+ if (wait_for_ps_or_oia() == -1) {
+ return -1;
+ }
+ }
+ } while (memcmp(zeroes, oia.input_inhibited, sizeof oia.input_inhibited));
+ return 0;
+}
+
+static int
+initialize()
+{
+ QuerySessionIdParms id;
+ QuerySessionParametersParms pa;
+ QuerySessionCursorParms cu;
+ ConnectToKeyboardParms conn;
+ DisableInputParms disable;
+ NameArray namearray;
+
+ if (api_init() == 0) {
+ fprintf(stderr, "API function not available.\n");
+ return -1;
+ }
+
+ id.rc = 0;
+ id.function_id = 0;
+ id.option_code = ID_OPTION_BY_NAME;
+ id.data_code = 'E';
+ id.name_array = &namearray;
+ namearray.length = sizeof namearray;
+ if (api_query_session_id(&id)) {
+ api_perror("api_query_session_id");
+ } else if (namearray.number_matching_session == 0) {
+ fprintf(stderr, "query_session_id: No matching sessions!\n");
+ return -1;
+ } else if (verbose) {
+ printf("Session short name 0x%x, type is ",
+ namearray.name_array_element.short_name);
+ printf("%s", session_type(namearray.name_array_element.type));
+ printf(", session ID is: 0x%x\n",
+ namearray.name_array_element.session_id);
+ }
+ session_id = namearray.name_array_element.session_id;
+
+ pa.rc = pa.function_id = 0;
+ pa.session_id = session_id;
+ if (api_query_session_parameters(&pa) == -1) {
+ api_perror("api_query_session_parameters");
+ return -1;
+ } else if (verbose) {
+ printf("Session type %s, ", session_type(pa.session_type));
+ if (pa.session_characteristics&CHARACTERISTIC_EAB) {
+ printf(" has EAB, ");
+ }
+ if (pa.session_characteristics&CHARACTERISTIC_PSS) {
+ printf(" has PSS, ");
+ }
+ printf("%d rows, %d columns ", pa.rows, pa.columns);
+ if (pa.presentation_space) {
+ printf("presentation space at 0x%x:0x%x.\n",
+ FP_SEG(pa.presentation_space), FP_OFF(pa.presentation_space));
+ } else {
+ printf("(no direct presentation space access).\n");
+ }
+ }
+ ScreenSize = pa.rows*pa.columns;
+ if (pa.session_characteristics&CHARACTERISTIC_EAB) {
+ fprintf(stderr,
+ "tncomp utilities not designed to work with extended attribute buffers.\n");
+ return -1;
+ }
+
+ if (verbose) {
+ cu.rc = cu.function_id = 0;
+ cu.session_id = session_id;
+ if (api_query_session_cursor(&cu) == -1) {
+ api_perror("api_query_session_cursor");
+ } else {
+ printf("cursor");
+ if (cu.cursor_type&CURSOR_INHIBITED_AUTOSCROLL) {
+ printf(" inhibited autoscroll");
+ }
+ if (cu.cursor_type&CURSOR_INHIBITED) {
+ printf(" inhibited");
+ }
+ if (cu.cursor_type&CURSOR_BLINKING) {
+ printf(" blinking");
+ } else {
+ printf(" not blinking");
+ }
+ if (cu.cursor_type&CURSOR_BOX) {
+ printf(" box ");
+ } else {
+ printf(" not box ");
+ }
+ printf("at row %d, column %d.\n",
+ cu.row_address, cu.column_address);
+ }
+ }
+
+ conn.rc = conn.function_id = 0;
+ conn.session_id = session_id;
+ conn.event_queue_id = conn.input_queue_id = 0;
+ conn.intercept_options = 0;
+ if (api_connect_to_keyboard(&conn) == -1) {
+ api_perror("api_connect_to_keyboard");
+ } else if (verbose) {
+ if (conn.first_connection_identifier) {
+ printf("First keyboard connection.\n");
+ } else {
+ printf("Not first keyboard connection.\n");
+ }
+ }
+
+ disable.rc = disable.function_id = 0;
+ disable.session_id = session_id;
+ disable.connectors_task_id = 0;
+ if (api_disable_input(&disable) == -1) {
+ api_perror("api_disable_input");
+ return -1;
+ } else if (verbose) {
+ printf("Disabled.\n");
+ }
+
+ if ((enter_index = ascii_to_index("ENTER")) == -1) {
+ return -1;
+ }
+ if ((clear_index = ascii_to_index("CLEAR")) == -1) {
+ return -1;
+ }
+
+ return 0; /* all ok */
+}
+
+static int
+send_key(index)
+int index;
+{
+ WriteKeystrokeParms wr;
+ extern struct astosc astosc[];
+
+ wait_for_unlock();
+
+ wr.rc = wr.function_id = 0;
+ wr.session_id = session_id;
+ wr.connectors_task_id = 0;
+ wr.options = OPTION_SINGLE_KEYSTROKE;
+ wr.number_of_keys_sent = 0;
+ wr.keystroke_specifier.keystroke_entry.scancode = astosc[index].scancode;
+ wr.keystroke_specifier.keystroke_entry.shift_state
+ = astosc[index].shiftstate;
+ if (api_write_keystroke(&wr) == -1) {
+ api_perror("api_write_keystroke");
+ return -1;
+ } else if (wr.number_of_keys_sent != 1) {
+ fprintf(stderr, "write_keystroke claims to have sent %d keystrokes.\n",
+ wr.number_of_keys_sent);
+ return -1;
+ } else if (verbose) {
+ printf("Keystroke sent.\n");
+ }
+ if (wait_for_ps_or_oia() == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+terminate()
+{
+ EnableInputParms enable;
+ DisconnectFromKeyboardParms disc;
+
+ enable.rc = enable.function_id = 0;
+ enable.session_id = session_id;
+ enable.connectors_task_id = 0;
+ if (api_enable_input(&enable) == -1) {
+ api_perror("api_enable");
+ return -1;
+ } else if (verbose) {
+ printf("Enabled.\n");
+ }
+
+ disc.rc = disc.function_id = 0;
+ disc.session_id = session_id;
+ disc.connectors_task_id = 0;
+ if (api_disconnect_from_keyboard(&disc) == -1) {
+ api_perror("api_disconnect_from_keyboard");
+ return -1;
+ } else if (verbose) {
+ printf("Disconnected from keyboard.\n");
+ }
+
+ (void) api_finish();
+
+ return 0;
+}
+
+
+static int
+get_screen()
+{
+ CopyStringParms copy;
+ /* Time copy services */
+
+ wait_for_unlock();
+
+ copy.copy_mode = 0;
+ copy.rc = copy.function_id = 0;
+ copy.source.session_id = session_id;
+ copy.source.buffer = 0;
+ copy.source.characteristics = 0;
+ copy.source.session_type = TYPE_DFT;
+ copy.source.begin = 0;
+
+ copy.source_end = ScreenSize;
+
+ copy.target.session_id = 0;
+ copy.target.buffer = (char *) &Host[0];
+ copy.target.characteristics = 0;
+ copy.target.session_type = TYPE_DFT;
+
+ if (api_copy_string(&copy) == -1) {
+ api_perror("api_copy_string");
+ return -1;
+ }
+ return 0;
+}
+
+
+put_at(offset, from, length, attribute)
+int offset;
+char *from;
+int length;
+{
+ CopyStringParms copy;
+
+ wait_for_unlock();
+
+ copy.copy_mode = 0;
+ copy.rc = copy.function_id = 0;
+ copy.source.session_id = 0;
+ copy.source.buffer = from;
+ copy.source.characteristics = 0;
+ copy.source.session_type = TYPE_DFT;
+ copy.source.begin = 0;
+
+ copy.source_end = length-1;
+
+ copy.target.session_id = session_id;
+ copy.target.buffer = 0;
+ copy.target.characteristics = 0;
+ copy.target.session_type = TYPE_DFT;
+ copy.target.begin = offset;
+
+ if (api_copy_string(&copy) == -1) {
+ api_perror("api_copy_string");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+translate(input, output, table, length)
+char *input, *output, table[];
+int length;
+{
+ unsigned char *indices = (unsigned char *) input;
+
+ while (length--) {
+ *output++ = table[*indices++];
+ }
+}
+
+static int
+find_input_area(from)
+int from;
+{
+#define FieldDec(p) (0) /* We don't really use this */
+ register int i, attr;
+
+ for (i = from; i < MAXSCREENSIZE; ) {
+ if (IsStartField(i)) {
+ attr = FieldAttributes(i);
+ i++;
+ if (!IsProtectedAttr(i, attr)) {
+ return i;
+ }
+ } else {
+ i++;
+ }
+ }
+ return -1;
+}
+
+
+static void
+getascii(offset, to, length)
+int offset; /* Where in screen */
+char *to; /* Where it goes to */
+int length; /* Where to put it */
+{
+ translate(Host+offset, to, disp_asc, length);
+}
+
+static int
+putascii(offset, from, length, before)
+int offset; /* Where in screen */
+char *from; /* Where it comes from */
+int length; /* Where to put it */
+int before; /* How much else should go */
+{
+ translate(from, Host+offset, asc_disp, length);
+ if (put_at(offset-before,
+ (char *) Host+offset-before, length+before) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+ack()
+{
+ static char ack_blanks[sizeof a_ack_sequence] = {0};
+
+ if (ack_blanks[0] == 0) {
+ int i;
+
+ for (i = 0; i < sizeof ack_blanks; i++) {
+ ack_blanks[i] = ' ';
+ }
+ }
+
+ memcpy(a_ack_sequence, ack_blanks, sizeof a_ack_sequence);
+ sprintf(a_ack_sequence, "%d", ack_sequence);
+ a_ack_sequence[strlen(a_ack_sequence)] = ' ';
+ if (putascii(ACK_SEQUENCE, a_ack_sequence, ACK_SEQUENCE_LENGTH, 0) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int
+formatted_correct()
+{
+ if ((find_input_area(SEND_SEQUENCE-1) != SEND_SEQUENCE) ||
+ (find_input_area(SEND_SEQUENCE) != ACK_SEQUENCE) ||
+ (find_input_area(ACK_SEQUENCE) != CHECKSUM) ||
+ (find_input_area(CHECKSUM) != DATA)) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ register int i;
+ int data_length, input_length;
+ char ascii[8]; /* Lots of room */
+ FILE *outfile;
+ char *data;
+ char *argv0 = argv[0];
+
+ argc--;
+ argv++;
+ /* Process any flags */
+ while (argc && (argv[0][0] == '-')) {
+ switch (argv[0][1]) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'b':
+ blocks = 1;
+ break;
+ }
+ argc--;
+ argv++;
+ }
+
+ if ((argc) < 2) {
+ fprintf(stderr,
+ "usage: %s [-b] [-v] local.file remote.file [remote.options]\n",
+ argv0);
+ exit(1);
+ }
+
+ /* Open the local file */
+ if ((outfile = fopen(argv[0], "w")) == NULL) {
+ perror("fopen");
+ exit(2);
+ }
+ argc--;
+ argv++;
+
+ if (initialize() == -1) {
+ return -1;
+ }
+
+ /* build the command line */
+ data = data_array;
+ strcpy(data, "TNCOMP SEND");
+ data += strlen(data);
+ while (argc--) {
+ *data++ = ' ';
+ strcpy(data, argv[0]);
+ data += strlen(argv[0]);
+ argv++;
+ }
+ if (verbose) {
+ printf("%s\n", data_array);
+ }
+ if (get_screen() == -1) {
+ return -1;
+ }
+ data_length = strlen(data_array);
+ if ((i = find_input_area(0)) == -1) { /* Get an input area */
+ if (send_key(clear_index) == -1) {
+ return -1;
+ }
+ if ((i = find_input_area(0)) == -1) { /* Try again */
+ fprintf(stderr, "Unable to enter command line.\n");
+ return -1;
+ }
+ }
+ if (putascii(i, data_array, data_length, 0) == -1) {
+ return -1;
+ }
+ if (send_key(enter_index) == -1) {
+ return -1;
+ }
+ do {
+ if (get_screen() == -1) {
+ return -1;
+ }
+ } while (formatted_correct() == -1);
+
+ do {
+ if (get_screen() == -1) {
+ return -1;
+ }
+ /* For each screen */
+ if (formatted_correct() == -1) {
+ fprintf(stderr, "Bad screen written by host.\n");
+ return -1;
+ }
+ /* If MDT isn't reset in the sequence number, go around again */
+ if (Host[ACK_SEQUENCE-1]&ATTR_MDT) {
+ if (wait_for_ps_or_oia() == -1) {
+ return -1;
+ }
+ continue;
+ }
+ getascii(SEND_SEQUENCE, a_send_sequence, SEND_SEQUENCE_LENGTH);
+ send_sequence = atoi(a_send_sequence);
+ getascii(CHECKSUM, a_checksum, CHECKSUM_LENGTH);
+ checksum = atoi(a_checksum);
+ getascii(DATA, data_array, DATA_LENGTH);
+ data = data_array;
+ if (send_sequence != (ack_sequence+1)) {
+ if (ack() == -1) {
+ return -1;
+ }
+ data = "1234"; /* Keep loop from failing */
+ if (send_key(enter_index) == -1) {
+ return -1;
+ }
+ if (get_screen() == -1) {
+ return -1;
+ }
+ continue;
+ }
+
+ data_length = DATA_LENGTH;
+ while (data_length && memcmp(data, " EOF", 4)
+ && memcmp(data, " ", 4)) {
+ memcpy(ascii, data, 4);
+ data += 4;
+ data_length -= 4;
+ ascii[4] = 0;
+ input_length = atoi(ascii);
+ /* CMS can't live with zero length records */
+ if ((input_length > 1) ||
+ ((input_length == 1) && (data[0] != ' '))) {
+ if (fwrite(data, sizeof (char),
+ input_length, outfile) == 0) {
+ perror("fwrite");
+ exit(9);
+ }
+ }
+ fprintf(outfile, "\n");
+ data += input_length;
+ data_length -= input_length;
+ }
+
+ ack_sequence = send_sequence;
+ if (blocks) {
+ printf("#");
+ fflush(stdout);
+ }
+ if (ack() == -1) {
+ return -1;
+ }
+ if (send_key(enter_index) == -1) {
+ return -1;
+ }
+ } while (memcmp(data, " EOF", 4));
+
+ if (blocks) {
+ printf("\n");
+ }
+ if (terminate() == -1) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/usr.bin/tn3270/general/genbsubs.c b/usr.bin/tn3270/general/genbsubs.c
new file mode 100644
index 0000000..b5377a6
--- /dev/null
+++ b/usr.bin/tn3270/general/genbsubs.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)genbsubs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* The output of bunequal is the offset of the byte which didn't match;
+ * if all the bytes match, then we return n.
+ * bunequal(s1, s2, n) */
+
+int
+bunequal(s1, s2, n)
+register char *s1, *s2;
+register n;
+{
+ register int i = 0;
+
+ while (i++ < n) {
+ if (*s1++ != *s2++) {
+ break;
+ }
+ }
+ return(i-1);
+}
+
+/* bskip(s1, n, b) : finds the first occurrence of any byte != 'b' in the 'n'
+ * bytes beginning at 's1'.
+ */
+
+int
+bskip(s1, n, b)
+register char *s1;
+register int n;
+register int b;
+{
+ register int i = 0;
+
+ while (i++ < n) {
+ if (*s1++ != b) {
+ break;
+ }
+ }
+ return(i-1);
+}
+
+/*
+ * memNSchr(const void *s, int c, size_t n, int and)
+ *
+ * Like memchr, but the comparison is '((*s)&and) == c',
+ * and we increment our way through s by "stride" ('s += stride').
+ *
+ * We optimize for the most used strides of +1 and -1.
+ */
+
+unsigned char *
+memNSchr(s, c, n, and, stride)
+char *s;
+int c;
+unsigned int n;
+int and;
+int stride;
+{
+ register unsigned char _c, *_s, _and;
+
+ _and = and;
+ _c = (c&_and);
+ _s = (unsigned char *)s;
+ switch (stride) {
+ case 1:
+ while (n--) {
+ if (((*_s)&_and) == _c) {
+ return _s;
+ }
+ _s++;
+ }
+ break;
+ case -1:
+ while (n--) {
+ if (((*_s)&_and) == _c) {
+ return _s;
+ }
+ _s--;
+ }
+ break;
+ default:
+ while (n--) {
+ if (((*_s)&_and) == _c) {
+ return _s;
+ }
+ _s += stride;
+ }
+ }
+ return 0;
+}
diff --git a/usr.bin/tn3270/general/general.h b/usr.bin/tn3270/general/general.h
new file mode 100644
index 0000000..8fee245
--- /dev/null
+++ b/usr.bin/tn3270/general/general.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)general.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Some general definitions.
+ */
+
+#define numberof(x) (sizeof x/sizeof x[0])
+#define highestof(x) (numberof(x)-1)
+
+#if defined(unix)
+#define ClearElement(x) bzero((char *)&x, sizeof x)
+#define ClearArray(x) bzero((char *)x, sizeof x)
+#else /* defined(unix) */
+#define ClearElement(x) memset((char *)&x, 0, sizeof x)
+#define ClearArray(x) memset((char *)x, 0, sizeof x)
+#endif /* defined(unix) */
+
+#if defined(unix) /* Define BSD equivalent mem* functions */
+#define memcpy(dest,src,n) bcopy(src,dest,n)
+#define memmove(dest,src,n) bcopy(src,dest,n)
+#define memset(s,c,n) if (c == 0) { \
+ bzero(s,n); \
+ } else { \
+ register char *src = s; \
+ register int count = n; \
+ \
+ while (count--) { \
+ *src++ = c; \
+ } \
+ }
+#define memcmp(s1,s2,n) bcmp(s1,s2,n)
+#endif /* defined(unix) */
diff --git a/usr.bin/tn3270/general/globals.c b/usr.bin/tn3270/general/globals.c
new file mode 100644
index 0000000..11e565f
--- /dev/null
+++ b/usr.bin/tn3270/general/globals.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)globals.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Do the defining instances for the globals of tn3270.
+ */
+
+#include "../ctlr/hostctlr.h"
+#include "../ctlr/oia.h"
+#include "../ctlr/options.h"
+#include "../ctlr/screen.h"
+
+
+#define DEFINING_INSTANCES
+
+#include "globals.h"
+
+#include "../general/general.h"
+
+/*
+ * init_system()
+ *
+ * Initialize the global values in case of a restart.
+ */
+
+void
+init_system()
+{
+ OptHome = OptLeftMargin = OptAPLmode = OptNullProcessing = 0;
+ OptZonesMode = OptEnterNL = OptColFieldTab = OptPacing = 0;
+ OptAlphaInNumeric = OptHome = OptLeftMargin = OptWordWrap = 0;
+
+ ClearArray(Host);
+ CursorAddress = BufferAddress = 0;
+
+ Lowest = Highest = 0;
+
+ UnLocked = AidByte = 0;
+
+}
diff --git a/usr.bin/tn3270/general/globals.h b/usr.bin/tn3270/general/globals.h
new file mode 100644
index 0000000..4fb365c
--- /dev/null
+++ b/usr.bin/tn3270/general/globals.h
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)globals.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * This file contains all the globals used by tn3270.
+ *
+ * Since various files may want to reference this file,
+ * and since they may only want subsets of the globals,
+ * we assume they have #include'd all the other .h files
+ * first, and we only give those globals relevant to
+ * the #include'd .h files.
+ *
+ */
+
+#if defined(DEFINING_INSTANCES)
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+
+EXTERN int
+ /*
+ * shell_active ==>
+ * 1. Don't do input.
+ * 2. Don't do output.
+ * 3. Don't block in select.
+ * 4. When nothing to do, call shell_continue()
+ */
+ shell_active;
+
+
+#if defined(INCLUDED_OPTIONS)
+EXTERN int OptHome; /* where home should send us */
+
+EXTERN int OptLeftMargin; /* where new line should send us */
+
+EXTERN char OptColTabs[80]; /* local tab stops */
+
+EXTERN int OptAPLmode;
+
+EXTERN int OptNullProcessing; /* improved null processing */
+
+EXTERN int OptZonesMode; /* zones mode off */
+
+EXTERN int OptEnterNL; /* regular enter/new line keys */
+
+EXTERN int OptColFieldTab; /* regular column/field tab keys */
+
+EXTERN int OptPacing; /* do pacing */
+
+EXTERN int OptAlphaInNumeric; /* allow alpha in numeric fields */
+
+EXTERN int OptHome;
+
+EXTERN int OptLeftMargin;
+
+EXTERN int OptWordWrap;
+#endif
+
+#if defined(INCLUDED_SCREEN)
+EXTERN ScreenImage
+ Host[MAXSCREENSIZE]; /* host view of screen */
+
+EXTERN char Orders[256]; /* Non-zero for orders */
+
+ /* Run-time screen geometry */
+EXTERN int
+ MaxNumberLines, /* How many rows the 3270 COULD have */
+ MaxNumberColumns, /* How many columns the 3270 COULD have */
+ NumberLines, /* How many lines the 3270 screen contains */
+ NumberColumns, /* How many columns the 3270 screen contains */
+ ScreenSize;
+
+EXTERN int CursorAddress; /* where cursor is */
+EXTERN int BufferAddress; /* where writes are going */
+
+EXTERN int Lowest, Highest;
+
+extern char CIABuffer[];
+
+EXTERN int UnLocked; /* is the keyboard unlocked */
+EXTERN int AidByte;
+
+#endif
+
+#if defined(INCLUDED_STATE)
+#endif
+
+#if defined(INCLUDED_OIA)
+
+EXTERN OIA OperatorInformationArea;
+
+EXTERN int
+ oia_modified, /* Has the oia been modified */
+ ps_modified; /* Has the presentation space been modified */
+
+#endif /* defined(INCLUDED_OIA) */
diff --git a/usr.bin/tn3270/general/vaxbsubs.s b/usr.bin/tn3270/general/vaxbsubs.s
new file mode 100644
index 0000000..096f532
--- /dev/null
+++ b/usr.bin/tn3270/general/vaxbsubs.s
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vaxbsubs.s 8.1 (Berkeley) 6/6/93
+ */
+
+/* This is taken from bcmp.s from 4.2.
+ * The output of bunequal is the offset of the byte which didn't match;
+ * if all the bytes match, then we return n.
+ *
+ * BUGNOTE: This has no chance of working for lengths greater than 64K.
+ * (so, if you use this somewhere else, you may need to
+ * fix it...)
+ */
+
+/* bunequal(s1, s2, n) */
+
+#include "defs.h"
+
+ENTRY(bunequal)
+ movl 4(ap),r1
+ movl 8(ap),r3
+ movl 12(ap),r4
+1:
+ movzwl $65535,r0
+ cmpl r4,r0
+ jleq 2f
+ subl2 r0,r4
+ cmpc3 r0,(r1),(r3)
+ jeql 1b
+ addl2 r4,r0
+ /* changes... */
+ subl3 r0,12(ap),r0
+ /* end of changes for bunequal... */
+ ret
+2:
+ cmpc3 r4,(r1),(r3)
+ /* changes... */
+ subl3 r0,12(ap),r0
+ /* end of changes for bunequal... */
+ ret
+
+
+
+
+/* brand new code, using the above as base... */
+/* bskip(s1, n, b) : finds the first occurrence of any byte != 'b' in the 'n'
+ * bytes beginning at 's1'.
+ *
+ * BUGNOTE: This has no chance of working for lengths greater than 64K.
+ * (so, if you use this somewhere else, you may need to
+ * fix it...)
+ */
+
+ENTRY(bskip)
+ movl 4(ap),r1
+ movl 8(ap),r3
+ movl 12(ap),r4
+1:
+ movzwl $65535,r0
+ cmpl r3,r0
+ jleq 2f
+ subl2 r0,r3
+ skpc r4,r0,(r1)
+ jeql 1b
+ addl2 r3,r0
+ subl3 r0,8(ap),r0
+ ret
+2:
+ skpc r4,r3,(r1)
+ subl3 r0,8(ap),r0
+ ret
diff --git a/usr.bin/tn3270/mset/Makefile b/usr.bin/tn3270/mset/Makefile
new file mode 100644
index 0000000..753dd2f
--- /dev/null
+++ b/usr.bin/tn3270/mset/Makefile
@@ -0,0 +1,34 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mset
+CFLAGS+=-I${.CURDIR} -I.
+SRCS+= astosc.c map3270.c mset.c
+MAN1= mset.1
+MAN5= map3270.5
+.PATH: ${.CURDIR}/../api ${.CURDIR}/../ascii
+
+CLEANFILES+= astosc.OUT astosc.out
+
+# This and the dependency hacks below to make 'depend' target
+# work right...
+
+DEPSRCS+= astosc.OUT map3270.c mset.c
+
+astosc.o: astosc.OUT
+astosc.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h
+astosc.OUT: ${.CURDIR}/../ctlr/${KBD} ${.CURDIR}/../tools/mkastosc/obj/mkastosc
+ ${.CURDIR}/../tools/mkastosc/obj/mkastosc \
+ ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h \
+ < ${.CURDIR}/../ctlr/${KBD} > ${.TARGET}
+ rm -f astosc.out; ln -s astosc.OUT astosc.out
+
+# astosc.out
+${.CURDIR}/../tools/mkastosc/obj/mkastosc:
+ cd ${.CURDIR}/../tools/mkastosc; make
+
+depend: .depend
+.depend:${DEPSRCS}
+ mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c}
+
+.include <../../Makefile.inc>
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/mset/map3270.5 b/usr.bin/tn3270/mset/map3270.5
new file mode 100644
index 0000000..3904f3a
--- /dev/null
+++ b/usr.bin/tn3270/mset/map3270.5
@@ -0,0 +1,341 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)map3270.5 8.3 (Berkeley) 4/19/94
+.\"
+.TH MAP3270 5 "April 19, 1994"
+.UC 6
+.SH NAME
+map3270 \- database for mapping ascii keystrokes into IBM 3270 keys
+.SH SYNOPSIS
+.B map3270
+.SH DESCRIPTION
+When emulating IBM-style 3270 terminals under \s-1UNIX\s0 (see \fItn3270\fR(1)),
+a mapping must be performed between sequences of keys hit on
+a user's (ascii) keyboard, and the keys that are
+available on a 3270. For example, a 3270 has a key labeled
+.B EEOF
+which erases the contents of the current field from the
+location of the cursor to the end.
+In order to accomplish this function,
+the terminal user and a program emulating a 3270 must
+agree on what keys will be typed
+to invoke the
+.B EEOF
+function.
+.PP
+The requirements for these sequences are:
+.nf
+.ta 4n 9n
+.sp
+ 1) that the first character of the sequence be outside of the
+ standard ascii printable characters;
+.sp
+ 2) that no sequence \fIbe\fR an initial part of another (although
+ sequences may \fIshare\fR initial parts).
+.sp
+.fi
+.SH FORMAT
+The file consists of entries for various keyboards. The first part
+of an entry lists the names of the keyboards which use that entry.
+These names will often be the same as in
+.I /etc/termcap
+(see
+.IR termcap (5));
+however, note that often the terminals from various termcap entries will all
+use the same
+.I map3270
+entry; for example, both 925 and 925vb (for
+925 with visual bells) would probably use the same
+.I map3270
+entry.
+Additionally, there are occasions when the terminal type defines
+a window manager, and it will then be necessary to specify a
+keyboard name (via the
+.B KEYBD
+environment variable) as the name of the entry.
+After the names, separated by vertical bars (`|'), comes a left
+brace (`{'); the definitions; and, finally, a right brace
+(`}').
+.PP
+Each definition consists of a reserved keyword (see list below) which
+identifies the 3270 function (extended as defined below), followed
+by an equal sign (`='), followed by the various ways to generate
+this particular function, followed by a semi-colon (`;').
+Each way is a sequence of strings of
+.I printable
+ascii characters enclosed inside single quotes (`\(aa');
+various ways (alternatives) are separated by vertical bars (`|').
+.PP
+Inside the single quotes, a few characters are special.
+A caret
+(`^') specifies that the next character is
+the ``control'' character of whatever the character is.
+So, `^a'
+represents control-a, ie: hexadecimal 1
+(note that `^A' would generate the same code).
+To generate
+.B rubout
+(DEL),
+one enters `^?'.
+To represent a control character inside a file
+requires using the caret to represent a control sequence;
+simply typing control-A will not work.
+Note: the ctrl-caret sequence
+(to generate a hexadecimal 1E)
+is represented as `^^' (not `^\e^').
+.PP
+In addition to the caret, a letter may be preceded by a backslash (`\e').
+Since this has little effect for most characters,
+its use is usually not recommended.
+For the case of a single quote (`\(aa'), the backslash
+prevents that single quote from terminating the string.
+For the case of a caret (`^'), the backslash prevents
+the caret from having its special meaning.
+To have the backslash be part of the string, it is necessary to
+place two backslashes ('\e\e') in the file.
+.PP
+In addition, the following characters are special:
+.sp
+.nf
+.in +0.5i
+`\eE' means an escape character;
+`\en' means newline;
+`\et' means tab;
+`\er' means carriage return.
+.in -0.5i
+.fi
+.sp
+It is not necessary for each character in a string
+to be enclosed within single quotes.
+`\eE\eE\eE' means three escape characters.
+.PP
+Comments, which may appear anywhere on a line,
+begin with a hash mark (`#'), and terminate
+at the end of that line.
+However, comments cannot begin inside a quoted string;
+a hash mark inside a quoted string has no special meaning.
+.PP
+.SH 3270 KEYS SUPPORTED
+The following is the list of 3270 key names that are supported in this file.
+Note that some of the keys don't really exist on a 3270.
+In particular, the developers of this file have relied
+extensively on the work at the Yale University Computer Center with
+their 3270 emulator which runs in an IBM Series/1 front end.
+The following list corresponds closely to the functions
+that the developers of the Yale code offer in their product.
+.sp
+.B In the following list, the
+.B starred ("*")
+.B functions are not supported by
+.IR tn3270 (1).
+An unsupported function will cause
+.IR tn3270(1)
+to send a (possibly visual) bell sequence to the user's terminal.
+.sp
+.nf
+ 3270 Key Name Functional description
+
+ (*)LPRT local print
+ DP dup character
+ FM field mark character
+ CURSEL cursor select
+ CENTSIGN EBCDIC cent sign
+ RESHOW redisplay the screen
+ EINP erase input
+ EEOF erase end of field
+ DELETE delete character
+ INSRT toggle insert mode
+ TAB field tab
+ BTAB field back tab
+ COLTAB column tab
+ COLBAK column back tab
+ INDENT indent one tab stop
+ UNDENT undent one tab stop
+ NL new line
+ HOME home the cursor
+ UP up cursor
+ DOWN down cursor
+ RIGHT right cursor
+ LEFT left cursor
+ SETTAB set a column tab
+ DELTAB delete a columntab
+ SETMRG set left margin
+ SETHOM set home position
+ CLRTAB clear all column tabs
+ (*)APLON apl on
+ (*)APLOFF apl off
+ (*)APLEND treat input as ascii
+ (*)PCON xon/xoff on
+ (*)PCOFF xon/xoff off
+ DISC disconnect (suspend)
+ (*)INIT new terminal type
+ (*)ALTK alternate keyboard dvorak
+ FLINP flush input
+ ERASE erase last character
+ WERASE erase last word
+ FERASE erase field
+ SYNCH we are in synch with the user
+ RESET reset key-unlock keyboard
+ MASTER_RESET reset, unlock and redisplay
+ (*)XOFF please hold output
+ (*)XON please give me output
+ ESCAPE enter telnet command mode
+ WORDTAB tab to beginning of next word
+ WORDBACKTAB tab to beginning of current/last word
+ WORDEND tab to end of current/next word
+ FIELDEND tab to last non-blank of current/next
+ unprotected (writable) field.
+
+ PA1 program attention 1
+ PA2 program attention 2
+ PA3 program attention 3
+
+ CLEAR local clear of the 3270 screen
+ TREQ test request
+ ENTER enter key
+
+ PFK1 program function key 1
+ PFK2 program function key 2
+ etc. etc.
+ PFK36 program function key 36
+.SH A SAMPLE ENTRY
+The following entry is used by
+tn3270(1) when unable to locate a reasonable version in the
+user's environment and in /etc/map3270:
+.sp
+.nf
+ name { # actual name comes from TERM variable
+ clear = '^z';
+ flinp = '^x';
+ enter = '^m';
+ delete = '^d' | '^?'; # note that '^?' is delete (rubout)
+ synch = '^r';
+ reshow = '^v';
+ eeof = '^e';
+ tab = '^i';
+ btab = '^b';
+ nl = '^n';
+ left = '^h';
+ right = '^l';
+ up = '^k';
+ down = '^j';
+ einp = '^w';
+ reset = '^t';
+ xoff = '^s';
+ xon = '^q';
+ escape = '^c';
+ ferase = '^u';
+ insrt = '\E ';
+ # program attention keys
+ pa1 = '^p1'; pa2 = '^p2'; pa3 = '^p3';
+ # program function keys
+ pfk1 = '\eE1'; pfk2 = '\eE2'; pfk3 = '\eE3'; pfk4 = '\eE4';
+ pfk5 = '\eE5'; pfk6 = '\eE6'; pfk7 = '\eE7'; pfk8 = '\eE8';
+ pfk9 = '\eE9'; pfk10 = '\eE0'; pfk11 = '\eE-'; pfk12 = '\eE=';
+ pfk13 = '\eE!'; pfk14 = '\eE@'; pfk15 = '\eE#'; pfk16 = '\eE$';
+ pfk17 = '\eE%'; pfk18 = '\eE'; pfk19 = '\eE&'; pfk20 = '\eE*';
+ pfk21 = '\eE('; pfk22 = '\eE)'; pfk23 = '\eE_'; pfk24 = '\eE+';
+ }
+.fi
+.SH "IBM 3270 KEY DEFINITONS FOR AN ABOVE DEFINITION"
+The charts below show the proper keys to emulate
+each 3270 function when using the default key mapping supplied
+with
+.IR tn3270 (1)
+and
+.IR mset (1).
+.sp
+.nf
+ Command Keys IBM 3270 Key Default Key(s)
+ Enter RETURN
+ Clear control-z
+ Cursor Movement Keys
+ New Line control-n or
+ Home
+ Tab control-i
+ Back Tab control-b
+ Cursor Left control-h
+ Cursor Right control-l
+ Cursor Up control-k
+ Cursor Down control-j or
+ LINE FEED
+ Edit Control Keys
+ Delete Char control-d or
+ RUB
+ Erase EOF control-e
+ Erase Input control-w
+ Insert Mode ESC Space
+ End Insert ESC Space
+ Program Function Keys
+ PF1 ESC 1
+ PF2 ESC 2
+ ... ...
+ PF10 ESC 0
+ PF11 ESC -
+ PF12 ESC =
+ PF13 ESC !
+ PF14 ESC @
+ ... ...
+ PF24 ESC +
+ Program Attention Keys
+ PA1 control-p 1
+ PA2 control-p 2
+ PA3 control-p 3
+ Local Control Keys
+ Reset After Error control-r
+ Purge Input Buffer control-x
+ Keyboard Unlock control-t
+ Redisplay Screen control-v
+ Other Keys
+ Erase current field control-u
+.fi
+.SH FILES
+/etc/map3270
+.SH SEE ALSO
+tn3270(1), mset(1), \fIYale ASCII Terminal Communication
+System II Program Description/Operator's Manual\fR
+(IBM SB30-1911)
+.SH AUTHOR
+Greg Minshall
+.SH BUGS
+.I Tn3270
+doesn't yet understand how to process all the functions
+available in
+.I map3270;
+when such a function is requested
+.I tn3270
+will beep at you.
+.PP
+The definition of "word" (for "word erase", "word tab") should be a run-time
+option. Currently it is defined as the kernel tty driver defines it (strings
+of non-whitespace); more than one person would rather use the "vi" definition
+(strings of specials, strings of alphanumeric).
diff --git a/usr.bin/tn3270/mset/mset.1 b/usr.bin/tn3270/mset/mset.1
new file mode 100644
index 0000000..a806eaa
--- /dev/null
+++ b/usr.bin/tn3270/mset/mset.1
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mset.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt MSET 1
+.Os BSD 4.3
+.Sh NAME
+.Nm mset
+.Nd retrieve
+.Tn ASCII
+to
+.Tn IBM
+3270 keyboard map
+.Sh SYNOPSIS
+.Nm mset
+.Op Fl picky
+.Op Fl shell
+.Op Ar keyboardname
+.Sh DESCRIPTION
+.Nm Mset
+retrieves mapping information
+for the
+.Tn ASCII
+keyboard to
+.Tn IBM
+3270 terminal
+special functions.
+Normally, these mappings are found
+in
+.Pa /usr/share/misc/map3270
+(see
+.Xr map3270 5 ) .
+This information is used by the
+.Xr tn3270
+command (see
+.Xr tn3270 1 ) .
+.Pp
+The default
+.Nm mset
+output can be used to store the mapping information in the process environment
+in order to avoid scanning
+.Nm map3270
+each time
+.Nm tn3270
+is invoked.
+To do this, place the following command in your
+.Pa .login
+file:
+.Bd -literal -offset indent
+set noglob; setenv MAP3270 "\(gamset\(ga"; unset noglob
+.Ed
+.Pp
+If the
+.Ar keyboardname
+argument is not supplied,
+.Nm mset
+attempts to determine the name of the keyboard the user is using,
+by checking the
+.Ev KEYBD
+environment variable.
+If the
+.Ev KEYBD
+environment variable is not set, then
+.Nm mset
+uses the user's terminal type from the environment variable
+.Ev TERM
+as the keyboard name.
+Normally,
+.Nm mset
+then uses the file
+.Xr map3270 5
+to find the keyboard mapping for that terminal.
+However, if the environment variable
+.Ev MAP3270
+exists and contains the entry for the specified keyboard, then that
+definition is used.
+If the value of
+.Ev MAP3270
+begins with a slash (`/') then it is assumed to be the full pathname
+of an alternate mapping file and that file is searched first.
+In any case, if the mapping for the keyboard is not found in
+the environment, nor in an alternate map file, nor in the standard map file,
+then the same search is performed for an entry for a keyboard with the name
+.Ar unknown .
+If that search also fails,
+then a default mapping
+is used.
+.Pp
+The arguments to
+.Nm mset
+are:
+.Pp
+.Bl -tag -width Fl
+.It Fl picky
+When processing the various
+.Pa map3270
+entries (for the user's keyboard,
+and all those encountered before the one for the user's keyboard),
+.Nm mset
+normally will not complain about entries for unknown functions (like
+.Dq PFX1 ;
+the
+.Fl picky
+argument causes
+.Nm mset
+to issue warning messages about these unknown entries.
+.It Fl shell
+If the
+.Pa map3270
+entry is longer than the shell's 1024 environmental variable
+length limit, the default
+.Nm mset
+output cannot be used to store the mapping information in the process
+environment to avoid scanning
+.Pa map3270
+each time
+.Nm tn3270
+is invoked.
+The
+.Fl shell
+argument causes
+.Nm mset
+to generate shell commands to set the environmental variables
+.Ev MAP3270 ,
+.Ev MAP3270A ,
+and so on, breaking up the entry to fit within the shell environmental
+variable length limit.
+To set these variables, place the following command in your
+.Pa .login
+file:
+.Bd -literal -offset indent
+mset -shell > tmp ; source tmp ; /bin/rm tmp
+.Ed
+.It Ar keyboardname
+When searching for the
+.Pa map3270
+entry that matches the user's keyboard,
+.Nm mset
+will use
+.Ar keyboardname
+instead of determining the keyboard name from the
+.Ev KEYBD
+or
+.Ev TERM
+environmental variables.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/map3270 -compact
+.It Pa /usr/share/misc/map3270
+keyboard mapping for known keyboards
+.El
+.Sh SEE ALSO
+.Xr tn3270 1 ,
+.Xr map3270 5
+.Sh HISTORY
+The
+.Nm mset
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/tn3270/sys_curses/system.c b/usr.bin/tn3270/sys_curses/system.c
new file mode 100644
index 0000000..67b31af
--- /dev/null
+++ b/usr.bin/tn3270/sys_curses/system.c
@@ -0,0 +1,754 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)system.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#if defined(pyr)
+#define fd_set fdset_t
+#endif /* defined(pyr) */
+
+/*
+ * Wouldn't it be nice if these REALLY were in <sys/inode.h>? Or,
+ * equivalently, if <sys/inode.h> REALLY existed?
+ */
+#define IREAD 00400
+#define IWRITE 00200
+
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+extern int errno;
+
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+
+#include "../general/general.h"
+#include "../ctlr/api.h"
+#include "../api/api_exch.h"
+
+#include "../general/globals.h"
+
+#ifndef FD_SETSIZE
+/*
+ * The following is defined just in case someone should want to run
+ * this telnet on a 4.2 system.
+ *
+ */
+
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+
+#endif
+
+static int shell_pid = 0;
+static char key[50]; /* Actual key */
+static char *keyname; /* Name of file with key in it */
+
+static char *ourENVlist[200]; /* Lots of room */
+
+static int
+ sock = -1, /* Connected socket */
+ serversock; /* Server (listening) socket */
+
+static enum { DEAD, UNCONNECTED, CONNECTED } state;
+
+static long
+ storage_location; /* Address we have */
+static short
+ storage_length = 0; /* Length we have */
+static int
+ storage_must_send = 0, /* Storage belongs on other side of wire */
+ storage_accessed = 0; /* The storage is accessed (so leave alone)! */
+
+static long storage[1000];
+
+static union REGS inputRegs;
+static struct SREGS inputSregs;
+
+extern int apitrace;
+
+static void
+kill_connection()
+{
+ state = UNCONNECTED;
+ if (sock != -1) {
+ (void) close(sock);
+ sock = -1;
+ }
+}
+
+
+static int
+nextstore()
+{
+ struct storage_descriptor sd;
+
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ storage_length = 0;
+ return -1;
+ }
+ storage_length = sd.length;
+ storage_location = sd.location;
+ if (storage_length > sizeof storage) {
+ fprintf(stderr, "API client tried to send too much storage (%d).\n",
+ storage_length);
+ storage_length = 0;
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+ == -1) {
+ storage_length = 0;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+doreject(message)
+char *message;
+{
+ struct storage_descriptor sd;
+ int length = strlen(message);
+
+ if (api_exch_outcommand(EXCH_CMD_REJECTED) == -1) {
+ return -1;
+ }
+ sd.length = length;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, length, message) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * doassociate()
+ *
+ * Negotiate with the other side and try to do something.
+ *
+ * Returns:
+ *
+ * -1: Error in processing
+ * 0: Invalid password entered
+ * 1: Association OK
+ */
+
+static int
+doassociate()
+{
+ struct passwd *pwent;
+ char
+ promptbuf[100],
+ buffer[200];
+ struct storage_descriptor sd;
+ extern char *crypt();
+
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ sd.length = sd.length;
+ if (sd.length > sizeof buffer) {
+ doreject("(internal error) Authentication key too long");
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+
+ if (strcmp(buffer, key) != 0) {
+#if (!defined(sun)) || defined(BSD) && (BSD >= 43)
+ extern uid_t geteuid();
+#endif /* (!defined(sun)) || defined(BSD) && (BSD >= 43) */
+
+ if ((pwent = getpwuid((int)geteuid())) == 0) {
+ return -1;
+ }
+ sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name);
+ if (api_exch_outcommand(EXCH_CMD_SEND_AUTH) == -1) {
+ return -1;
+ }
+ sd.length = strlen(promptbuf);
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, strlen(promptbuf), promptbuf)
+ == -1) {
+ return -1;
+ }
+ sd.length = strlen(pwent->pw_name);
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES,
+ strlen(pwent->pw_name), pwent->pw_name) == -1) {
+ return -1;
+ }
+ if (api_exch_incommand(EXCH_CMD_AUTH) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ sd.length = sd.length;
+ if (sd.length > sizeof buffer) {
+ doreject("Password entered was too long");
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+
+ /* Is this the correct password? */
+ if (strlen(pwent->pw_name)) {
+ char *ptr;
+ int i;
+
+ ptr = pwent->pw_name;
+ i = 0;
+ while (i < sd.length) {
+ buffer[i++] ^= *ptr++;
+ if (*ptr == 0) {
+ ptr = pwent->pw_name;
+ }
+ }
+ }
+ if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) != 0) {
+ doreject("Invalid password");
+ sleep(10); /* Don't let us do too many of these */
+ return 0;
+ }
+ }
+ if (api_exch_outcommand(EXCH_CMD_ASSOCIATED) == -1) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+
+void
+freestorage()
+{
+ struct storage_descriptor sd;
+
+ if (storage_accessed) {
+ fprintf(stderr, "Internal error - attempt to free accessed storage.\n");
+ fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ if (storage_must_send == 0) {
+ return;
+ }
+ storage_must_send = 0;
+ if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) {
+ kill_connection();
+ return;
+ }
+ sd.length = storage_length;
+ sd.location = storage_location;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ kill_connection();
+ return;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+ == -1) {
+ kill_connection();
+ return;
+ }
+}
+
+
+static int
+getstorage(address, length, copyin)
+long
+ address;
+int
+ length,
+ copyin;
+{
+ struct storage_descriptor sd;
+
+ freestorage();
+ if (storage_accessed) {
+ fprintf(stderr,
+ "Internal error - attempt to get while storage accessed.\n");
+ fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ storage_must_send = 0;
+ if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) {
+ kill_connection();
+ return -1;
+ }
+ storage_location = address;
+ storage_length = length;
+ if (copyin) {
+ sd.location = (long)storage_location;
+ sd.length = storage_length;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC,
+ sizeof sd, (char *)&sd) == -1) {
+ kill_connection();
+ return -1;
+ }
+ if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) {
+ fprintf(stderr, "Bad data from other side.\n");
+ fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__);
+ return -1;
+ }
+ if (nextstore() == -1) {
+ kill_connection();
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*ARGSUSED*/
+void
+movetous(local, es, di, length)
+char
+ *local;
+unsigned int
+ es,
+ di;
+int
+ length;
+{
+ long where = SEG_OFF_BACK(es, di);
+
+ if (length > sizeof storage) {
+ fprintf(stderr, "Internal API error - movetous() length too long.\n");
+ fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+ quit();
+ } else if (length == 0) {
+ return;
+ }
+ getstorage(where, length, 1);
+ memcpy(local, (char *)(storage+((where-storage_location))), length);
+ if (apitrace) {
+ Dump('(', local, length);
+ }
+}
+
+/*ARGSUSED*/
+void
+movetothem(es, di, local, length)
+unsigned int
+ es,
+ di;
+char
+ *local;
+int
+ length;
+{
+ long where = SEG_OFF_BACK(es, di);
+
+ if (length > sizeof storage) {
+ fprintf(stderr, "Internal API error - movetothem() length too long.\n");
+ fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+ quit();
+ } else if (length == 0) {
+ return;
+ }
+ freestorage();
+ memcpy((char *)storage, local, length);
+ if (apitrace) {
+ Dump(')', local, length);
+ }
+ storage_length = length;
+ storage_location = where;
+ storage_must_send = 1;
+}
+
+
+char *
+access_api(location, length, copyin)
+char *
+ location;
+int
+ length,
+ copyin; /* Do we need to copy in initially? */
+{
+ if (storage_accessed) {
+ fprintf(stderr, "Internal error - storage accessed twice\n");
+ fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ } else if (length != 0) {
+ freestorage();
+ getstorage((long)location, length, copyin);
+ storage_accessed = 1;
+ }
+ return (char *) storage;
+}
+
+/*ARGSUSED*/
+void
+unaccess_api(location, local, length, copyout)
+char *location;
+char *local;
+int length;
+int copyout;
+{
+ if (storage_accessed == 0) {
+ fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n");
+ fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ storage_accessed = 0;
+ storage_must_send = copyout; /* if needs to go back */
+}
+
+/*
+ * Accept a connection from an API client, aborting if the child dies.
+ */
+
+static int
+doconnect()
+{
+ fd_set fdset;
+ int i;
+
+ sock = -1;
+ FD_ZERO(&fdset);
+ while (shell_active && (sock == -1)) {
+ FD_SET(serversock, &fdset);
+ if ((i = select(serversock+1, &fdset,
+ (fd_set *)0, (fd_set *)0, (struct timeval *)0)) < 0) {
+ if (errno = EINTR) {
+ continue;
+ } else {
+ perror("in select waiting for API connection");
+ return -1;
+ }
+ } else {
+ i = accept(serversock, (struct sockaddr *)0, (int *)0);
+ if (i == -1) {
+ perror("accepting API connection");
+ return -1;
+ }
+ sock = i;
+ }
+ }
+ /* If the process has already exited, we may need to close */
+ if ((shell_active == 0) && (sock != -1)) {
+ extern void setcommandmode();
+
+ (void) close(sock);
+ sock = -1;
+ setcommandmode(); /* In case child_died sneaked in */
+ }
+ return 0;
+}
+
+/*
+ * shell_continue() actually runs the command, and looks for API
+ * requests coming back in.
+ *
+ * We are called from the main loop in telnet.c.
+ */
+
+int
+shell_continue()
+{
+ int i;
+
+ switch (state) {
+ case DEAD:
+ pause(); /* Nothing to do */
+ break;
+ case UNCONNECTED:
+ if (doconnect() == -1) {
+ kill_connection();
+ return -1;
+ }
+ /* At this point, it is possible that we've gone away */
+ if (shell_active == 0) {
+ kill_connection();
+ return -1;
+ }
+ if (api_exch_init(sock, "server") == -1) {
+ return -1;
+ }
+ while (state == UNCONNECTED) {
+ if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
+ kill_connection();
+ return -1;
+ } else {
+ switch (doassociate()) {
+ case -1:
+ kill_connection();
+ return -1;
+ case 0:
+ break;
+ case 1:
+ state = CONNECTED;
+ }
+ }
+ }
+ break;
+ case CONNECTED:
+ switch (i = api_exch_nextcommand()) {
+ case EXCH_CMD_REQUEST:
+ if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
+ (char *)&inputRegs) == -1) {
+ kill_connection();
+ } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
+ (char *)&inputSregs) == -1) {
+ kill_connection();
+ } else if (nextstore() == -1) {
+ kill_connection();
+ } else {
+ handle_api(&inputRegs, &inputSregs);
+ freestorage(); /* Send any storage back */
+ if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
+ kill_connection();
+ } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
+ (char *)&inputRegs) == -1) {
+ kill_connection();
+ } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
+ (char *)&inputSregs) == -1) {
+ kill_connection();
+ }
+ /* Done, and it all worked! */
+ }
+ break;
+ case EXCH_CMD_DISASSOCIATE:
+ kill_connection();
+ break;
+ default:
+ if (i != -1) {
+ fprintf(stderr,
+ "Looking for a REQUEST or DISASSOCIATE command\n");
+ fprintf(stderr, "\treceived 0x%02x.\n", i);
+ }
+ kill_connection();
+ break;
+ }
+ }
+ return shell_active;
+}
+
+
+static void
+child_died(code)
+{
+ union wait status;
+ register int pid;
+
+ while ((pid = wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) {
+ if (pid == shell_pid) {
+ int ch;
+ extern void setconnmode();
+ extern void ConnectScreen();
+
+ shell_active = 0;
+ if (sock != -1) {
+ (void) close(sock);
+ sock = -1;
+ }
+ printf("[Hit return to continue]");
+ fflush(stdout);
+ while ((ch = getchar()) != '\n' && ch != EOF)
+ ;
+ setconnmode();
+ ConnectScreen(); /* Turn screen on (if need be) */
+ (void) close(serversock);
+ (void) unlink(keyname);
+ }
+ }
+ signal(SIGCHLD, child_died);
+}
+
+
+/*
+ * Called from telnet.c to fork a lower command.com. We
+ * use the spint... routines so that we can pick up
+ * interrupts generated by application programs.
+ */
+
+
+int
+shell(argc,argv)
+int argc;
+char *argv[];
+{
+ int length;
+ struct sockaddr_in server;
+ char sockNAME[100];
+ static char **whereAPI = 0;
+ int fd;
+ struct timeval tv;
+ long ikey;
+ extern long random();
+ extern char *mktemp();
+ extern char *strcpy();
+
+ /* First, create verification file. */
+ do {
+ keyname = mktemp(strdup("/tmp/apiXXXXXX"));
+ fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE);
+ } while ((fd == -1) && (errno == EEXIST));
+
+ if (fd == -1) {
+ perror("open");
+ return 0;
+ }
+
+ /* Now, get seed for random */
+
+ if (gettimeofday(&tv, (struct timezone *)0) == -1) {
+ perror("gettimeofday");
+ return 0;
+ }
+ srandom(tv.tv_usec); /* seed random number generator */
+ do {
+ ikey = random();
+ } while (ikey == 0);
+ sprintf(key, "%lu\n", (unsigned long) ikey);
+ if (write(fd, key, strlen(key)) != strlen(key)) {
+ perror("write");
+ return 0;
+ }
+ key[strlen(key)-1] = 0; /* Get rid of newline */
+
+ if (close(fd) == -1) {
+ perror("close");
+ return 0;
+ }
+
+ /* Next, create the socket which will be connected to */
+ serversock = socket(AF_INET, SOCK_STREAM, 0);
+ if (serversock < 0) {
+ perror("opening API socket");
+ return 0;
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+ server.sin_port = 0;
+ if (bind(serversock, (struct sockaddr *)&server, sizeof server) < 0) {
+ perror("binding API socket");
+ return 0;
+ }
+ length = sizeof server;
+ if (getsockname(serversock, (struct sockaddr *)&server, &length) < 0) {
+ perror("getting API socket name");
+ (void) close(serversock);
+ }
+ listen(serversock, 1);
+ /* Get name to advertise in address list */
+ strcpy(sockNAME, "API3270=");
+ gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
+ if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) {
+ fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
+ strcpy(sockNAME, "localhost");
+ }
+ sprintf(sockNAME+strlen(sockNAME), ":%u", ntohs(server.sin_port));
+ sprintf(sockNAME+strlen(sockNAME), ":%s", keyname);
+
+ if (whereAPI == 0) {
+ char **ptr, **nextenv;
+ extern char **environ;
+
+ ptr = environ;
+ nextenv = ourENVlist;
+ while (*ptr) {
+ if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
+ fprintf(stderr, "Too many environmental variables\n");
+ break;
+ }
+ *nextenv++ = *ptr++;
+ }
+ whereAPI = nextenv++;
+ *nextenv++ = 0;
+ environ = ourENVlist; /* New environment */
+ }
+ *whereAPI = sockNAME;
+
+ child_died(); /* Start up signal handler */
+ shell_active = 1; /* We are running down below */
+ if (shell_pid = vfork()) {
+ if (shell_pid == -1) {
+ perror("vfork");
+ (void) close(serversock);
+ } else {
+ state = UNCONNECTED;
+ }
+ } else { /* New process */
+ register int i;
+
+ for (i = 3; i < 30; i++) {
+ (void) close(i);
+ }
+ if (argc == 1) { /* Just get a shell */
+ char *cmdname;
+ extern char *getenv();
+
+ cmdname = getenv("SHELL");
+ execlp(cmdname, cmdname, 0);
+ perror("Exec'ing new shell...\n");
+ exit(1);
+ } else {
+ execvp(argv[1], &argv[1]);
+ perror("Exec'ing command.\n");
+ exit(1);
+ }
+ /*NOTREACHED*/
+ }
+ return shell_active; /* Go back to main loop */
+}
diff --git a/usr.bin/tn3270/sys_curses/telextrn.h b/usr.bin/tn3270/sys_curses/telextrn.h
new file mode 100644
index 0000000..becf82a2
--- /dev/null
+++ b/usr.bin/tn3270/sys_curses/telextrn.h
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)telextrn.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Definitions of external routines and variables for tn3270
+ */
+
+/*
+ * Pieces exported from the telnet susbsection.
+ */
+
+extern int
+#if defined(unix)
+ HaveInput,
+#endif /* defined(unix) */
+ tout,
+ tin;
+
+extern char *transcom;
+
+extern int
+ netflush(),
+ quit(),
+ TtyChars(),
+ DataToTerminal();
+
+extern void
+ outputPurge(),
+ EmptyTerminal(),
+ StringToTerminal(),
+ ExitPerror(),
+ setcommandmode();
+
+/*
+ * Pieces exported from other random locations.
+ */
+
+extern char
+ *strsave();
diff --git a/usr.bin/tn3270/sys_curses/terminal.h b/usr.bin/tn3270/sys_curses/terminal.h
new file mode 100644
index 0000000..d7041b7
--- /dev/null
+++ b/usr.bin/tn3270/sys_curses/terminal.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)terminal.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_TERMINAL
+
+/*
+ * In the situation where we have a copy of the terminal screen in front
+ * of us, here are some macros to deal with them.
+ */
+
+#define TermAttributes(x) (TermIsStartField(x)? GetTerminal(x)&0xff : \
+ GetTerminal(WhereTermAttrByte(x))&0xff)
+#define TermIsStartField(x) ((GetTerminal(x)&ATTR_MASK) == ATTR_MASK)
+#define TermNewField(p,a) SetTerminal(p, (a)|ATTR_MASK)
+#define TermDeleteField(p) SetTerminal(p, 0)
+#define TermIsNonDisplay(x) \
+ ((TermAttributes(x)&ATTR_DSPD_MASK) == ATTR_DSPD_NONDISPLAY)
+#define TermIsHighlighted(x) \
+ (((TermAttributes(x)&ATTR_DSPD_MASK) == ATTR_DSPD_HIGH) \
+ && !TermIsStartField(x))
+
+#define TerminalCharacterAttr(c,p,a) (IsNonDisplayAttr(a) ? ' ':c)
+#define TerminalCharacter(c,p) TerminalCharacterAttr(c,p,FieldAttributes(p))
+
+ /*
+ * Is the screen formatted? Some algorithms change depending
+ * on whether there are any attribute bytes lying around.
+ */
+#define TerminalFormattedScreen() \
+ ((WhereTermAttrByte(0) != 0) || ((GetTerminal(0)&ATTR_MASK) == ATTR_MASK))
+
+#define NeedToRedisplayFields(p) ((TermIsNonDisplay(p) != IsNonDisplay(p)) || \
+ (TermIsHighlighted(p) != IsHighlighted(p)))
+#define NeedToRedisplayFieldsAttr(p,c) ( \
+ (TermIsNonDisplay(p) != IsNonDisplayAttr(c)) || \
+ (TermIsHighlighted(p) != IsHighlightedAttr(c)))
+
+#define NotVisuallyCompatibleAttributes(p,c,d) ( \
+ (IsNonDisplayAttr(c) != IsNonDisplayAttr(d)) || \
+ (IsHighlightedAttr(c) != IsHighlightedAttr(d)))
+
+#define NeedToRedisplayAttr(c,p,a) \
+ ((c != GetTerminal(p)) || NeedToRedisplayFieldsAttr(p,a))
+#define NeedToRedisplay(c,p) NeedToRedisplayAttr(c,p,FieldAttributes(p))
+
+
+#define GetTerminal(i) GetGeneric(i, Terminal)
+#define GetTerminalPointer(p) GetGenericPointer(p)
+#define SetTerminal(i,c) SetGeneric(i,c,Terminal)
diff --git a/usr.bin/tn3270/sys_curses/termout.c b/usr.bin/tn3270/sys_curses/termout.c
new file mode 100644
index 0000000..bf5284c
--- /dev/null
+++ b/usr.bin/tn3270/sys_curses/termout.c
@@ -0,0 +1,957 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)termout.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#if defined(unix)
+#include <signal.h>
+#include <sgtty.h>
+#endif
+#include <stdio.h>
+#include <curses.h>
+#if defined(ultrix)
+/* Some version of this OS has a bad definition for nonl() */
+#undef nl
+#undef nonl
+
+#define nl() (_tty.sg_flags |= CRMOD,_pfast = _rawmode,stty(_tty_ch, &_tty))
+#define nonl() (_tty.sg_flags &= ~CRMOD, _pfast = TRUE, stty(_tty_ch, &_tty))
+#endif /* defined(ultrix) */
+
+#include "../general/general.h"
+
+#include "terminal.h"
+
+#include "../api/disp_asc.h"
+
+#include "../ctlr/hostctlr.h"
+#include "../ctlr/externs.h"
+#include "../ctlr/declare.h"
+#include "../ctlr/oia.h"
+#include "../ctlr/screen.h"
+#include "../ctlr/scrnctlr.h"
+
+#include "../general/globals.h"
+
+#include "telextrn.h"
+
+#define CorrectTerminalCursor() ((TransparentClock == OutputClock)? \
+ CursorAddress:UnLocked? CursorAddress: HighestScreen())
+
+
+static int terminalCursorAddress; /* where the cursor is on term */
+static int screenInitd; /* the screen has been initialized */
+static int screenStopped; /* the screen has been stopped */
+static int max_changes_before_poll; /* how many characters before looking */
+ /* at terminal and net again */
+
+static int needToRing; /* need to ring terinal bell */
+static char *bellSequence = "\07"; /* bell sequence (may be replaced by
+ * VB during initialization)
+ */
+static WINDOW *bellwin = 0; /* The window the bell message is in */
+int bellwinup = 0; /* Are we up with it or not */
+
+#if defined(unix)
+static char *myKS, *myKE;
+#endif /* defined(unix) */
+
+
+static int inHighlightMode = 0;
+ScreenImage Terminal[MAXSCREENSIZE];
+
+/* Variables for transparent mode */
+#if defined(unix)
+static int tcflag = -1; /* transparent mode command flag */
+static int savefd[2]; /* for storing fds during transcom */
+extern int tin, tout; /* file descriptors */
+#endif /* defined(unix) */
+
+
+/*
+ * init_screen()
+ *
+ * Initialize variables used by screen.
+ */
+
+void
+init_screen()
+{
+ bellwinup = 0;
+ inHighlightMode = 0;
+ ClearArray(Terminal);
+}
+
+
+/* OurExitString - designed to keep us from going through infinite recursion */
+
+static void
+OurExitString(string, value)
+char *string;
+int value;
+{
+ static int recursion = 0;
+
+ if (!recursion) {
+ recursion = 1;
+ ExitString(string, value);
+ }
+}
+
+
+/* DoARefresh */
+
+static void
+DoARefresh()
+{
+ if (ERR == refresh()) {
+ OurExitString("ERR from refresh\n", 1);
+ }
+}
+
+static void
+GoAway(from, where)
+char *from; /* routine that gave error */
+int where; /* cursor address */
+{
+ char foo[100];
+
+ sprintf(foo, "ERR from %s at %d (%d, %d)\n",
+ from, where, ScreenLine(where), ScreenLineOffset(where));
+ OurExitString(foo, 1);
+ /* NOTREACHED */
+}
+
+/* What is the screen address of the attribute byte for the terminal */
+
+static int
+WhereTermAttrByte(p)
+register int p;
+{
+ register int i;
+
+ i = p;
+
+ do {
+ if (TermIsStartField(i)) {
+ return(i);
+ }
+ i = ScreenDec(i);
+ } while (i != p);
+
+ return(LowestScreen()); /* unformatted screen... */
+}
+
+/*
+ * There are two algorithms for updating the screen.
+ * The first, SlowScreen() optimizes the line between the
+ * computer and the screen (say a 9600 baud line). To do
+ * this, we break out of the loop every so often to look
+ * at any pending input from the network (so that successive
+ * screens will only partially print until the final screen,
+ * the one the user possibly wants to see, is displayed
+ * in its entirety).
+ *
+ * The second algorithm tries to optimize CPU time (by
+ * being simpler) at the cost of the bandwidth to the
+ * screen.
+ *
+ * Of course, curses(3X) gets in here also.
+ */
+
+
+#if defined(NOT43)
+static int
+#else /* defined(NOT43) */
+static void
+#endif /* defined(NOT43) */
+SlowScreen()
+{
+ register int is, shouldbe, isattr, shouldattr;
+ register int pointer;
+ register int fieldattr, termattr;
+ register int columnsleft;
+
+#define NORMAL 0
+#define HIGHLIGHT 1 /* Mask bits */
+#define NONDISPLAY 4 /* Mask bits */
+#define UNDETERMINED 8 /* Mask bits */
+
+#define DoAttributes(x) \
+ switch (x&ATTR_DSPD_MASK) { \
+ case ATTR_DSPD_NONDISPLAY: \
+ x = NONDISPLAY; \
+ break; \
+ case ATTR_DSPD_HIGH: \
+ x = HIGHLIGHT; \
+ break; \
+ default: \
+ x = 0; \
+ break; \
+ }
+
+# define SetHighlightMode(x) \
+ { \
+ if ((x)&HIGHLIGHT) { \
+ if (!inHighlightMode) { \
+ inHighlightMode = HIGHLIGHT; \
+ standout(); \
+ } \
+ } else { \
+ if (inHighlightMode) { \
+ inHighlightMode = 0; \
+ standend(); \
+ } \
+ } \
+ }
+
+# define DoCharacterAt(c,p) { \
+ if (p != HighestScreen()) { \
+ c = disp_asc[c&0xff]; \
+ if (terminalCursorAddress != p) { \
+ if (ERR == mvaddch(ScreenLine(p), \
+ ScreenLineOffset(p), c)) {\
+ GoAway("mvaddch", p); \
+ } \
+ } else { \
+ if (ERR == addch(c)) {\
+ GoAway("addch", p); \
+ } \
+ } \
+ terminalCursorAddress = ScreenInc(p); \
+ } \
+ }
+
+
+ /* run through screen, printing out non-null lines */
+
+ /* There are two separate reasons for wanting to terminate this
+ * loop early. One is to respond to new input (either from
+ * the terminal or from the network [host]). For this reason,
+ * we expect to see 'HaveInput' come true when new input comes in.
+ *
+ * The second reason is a bit more difficult (for me) to understand.
+ * Basically, we don't want to get too far ahead of the characters that
+ * appear on the screen. Ideally, we would type out a few characters,
+ * wait until they appeared on the screen, then type out a few more.
+ * The reason for this is that the user, on seeing some characters
+ * appear on the screen may then start to type something. We would
+ * like to look at what the user types at about the same 'time'
+ * (measured by characters being sent to the terminal) that the
+ * user types them. For this reason, what we would like to do
+ * is update a bit, then call curses to do a refresh, flush the
+ * output to the terminal, then wait until the terminal data
+ * has been sent.
+ *
+ * Note that curses is useful for, among other things, deciding whether
+ * or not to send :ce: (clear to end of line), so we should call curses
+ * at end of lines (beginning of next lines).
+ *
+ * The problems here are the following: If we do lots of write(2)s,
+ * we will be doing lots of context switches, thus lots of overhead
+ * (which we have already). Second, if we do a select to wait for
+ * the output to drain, we have to contend with the fact that NOW
+ * we are scheduled to run, but who knows what the scheduler will
+ * decide when the output has caught up.
+ */
+
+ if (Highest >= HighestScreen()) { /* Could be > if screen shrunk... */
+ Highest = ScreenDec(Highest); /* else, while loop will never end */
+ }
+ if (Lowest < LowestScreen()) {
+ Lowest = LowestScreen(); /* could be -1 in some cases with
+ * unformatted screens.
+ */
+ }
+ if (Highest >= (pointer = Lowest)) {
+ /* if there is anything to do, do it. We won't terminate
+ * the loop until we've gone at least to Highest.
+ */
+ while ((pointer <= Highest) && !HaveInput) {
+
+ /* point at the next place of disagreement */
+ pointer += (bunequal(Host+pointer, Terminal+pointer,
+ (Highest-pointer+1)*sizeof Host[0])/sizeof Host[0]);
+
+ /*
+ * How many characters to change until the end of the
+ * current line
+ */
+ columnsleft = NumberColumns - ScreenLineOffset(pointer);
+ /*
+ * Make sure we are where we think we are.
+ */
+ move(ScreenLine(pointer), ScreenLineOffset(pointer));
+
+ /* what is the field attribute of the current position */
+ if (FormattedScreen()) {
+ fieldattr = FieldAttributes(pointer);
+ DoAttributes(fieldattr);
+ } else {
+ fieldattr = NORMAL;
+ }
+ if (TerminalFormattedScreen()) {
+ termattr = TermAttributes(pointer);
+ DoAttributes(termattr);
+ } else {
+ termattr = NORMAL;
+ }
+
+ SetHighlightMode(fieldattr);
+ /*
+ * The following will terminate at least when we get back
+ * to the original 'pointer' location (since we force
+ * things to be equal).
+ */
+ for (;;) {
+ if (IsStartField(pointer)) {
+ shouldbe = DISP_BLANK;
+ shouldattr = 0;
+ fieldattr = GetHost(pointer);
+ DoAttributes(fieldattr);
+ } else {
+ if (fieldattr&NONDISPLAY) {
+ shouldbe = DISP_BLANK;
+ } else {
+ shouldbe = GetHost(pointer);
+ }
+ shouldattr = fieldattr;
+ }
+ if (TermIsStartField(pointer)) {
+ is = DISP_BLANK;
+ isattr = 0;
+ termattr = UNDETERMINED; /* Need to find out AFTER update */
+ } else {
+ if (termattr&NONDISPLAY) {
+ is = DISP_BLANK;
+ } else {
+ is = GetTerminal(pointer);
+ }
+ isattr = termattr;
+ }
+ if ((shouldbe == is) && (shouldattr == isattr)
+ && (GetHost(pointer) == GetTerminal(pointer))
+ && (GetHost(ScreenInc(pointer))
+ == GetTerminal(ScreenInc(pointer)))) {
+ break;
+ }
+
+ if (shouldattr^inHighlightMode) {
+ SetHighlightMode(shouldattr);
+ }
+
+ DoCharacterAt(shouldbe, pointer);
+ if (IsStartField(pointer)) {
+ TermNewField(pointer, FieldAttributes(pointer));
+ termattr = GetTerminal(pointer);
+ DoAttributes(termattr);
+ } else {
+ SetTerminal(pointer, GetHost(pointer));
+ /*
+ * If this USED to be a start field location,
+ * recompute the terminal attributes.
+ */
+ if (termattr == UNDETERMINED) {
+ termattr = WhereTermAttrByte(pointer);
+ if ((termattr != 0) || TermIsStartField(0)) {
+ termattr = GetTerminal(termattr);
+ DoAttributes(termattr);
+ } else { /* Unformatted screen */
+ termattr = NORMAL;
+ }
+ }
+ }
+ pointer = ScreenInc(pointer);
+ if (!(--columnsleft)) {
+ DoARefresh();
+ EmptyTerminal();
+ if (HaveInput) { /* if input came in, take it */
+ int c, j;
+
+ /*
+ * We need to start a new terminal field
+ * at this location iff the terminal attributes
+ * of this location are not what we have had
+ * them as (ie: we've overwritten the terminal
+ * start field, a the previous field had different
+ * display characteristics).
+ */
+
+ isattr = TermAttributes(pointer);
+ DoAttributes(isattr);
+ if ((!TermIsStartField(pointer)) &&
+ (isattr != termattr)) {
+ /*
+ * Since we are going to leave a new field
+ * at this terminal position, we
+ * need to make sure that we get an actual
+ * non-highlighted blank on the screen.
+ */
+ if ((is != DISP_BLANK) || (termattr&HIGHLIGHT)) {
+ SetHighlightMode(0); /* Turn off highlight */
+ c = ScreenInc(pointer);
+ j = DISP_BLANK;
+ DoCharacterAt(j, c);
+ }
+ if (termattr&HIGHLIGHT) {
+ termattr = ATTR_DSPD_HIGH;
+ } else if (termattr&NONDISPLAY) {
+ termattr = ATTR_DSPD_NONDISPLAY;
+ } else {
+ termattr = 0;
+ }
+ TermNewField(pointer, termattr);
+ }
+ break;
+ }
+ move(ScreenLine(pointer), 0);
+ columnsleft = NumberColumns;
+ }
+ } /* end of for (;;) */
+ } /* end of while (...) */
+ }
+ DoARefresh();
+ Lowest = pointer;
+ if (Lowest > Highest) { /* if we finished input... */
+ Lowest = HighestScreen()+1;
+ Highest = LowestScreen()-1;
+ terminalCursorAddress = CorrectTerminalCursor();
+ if (ERR == move(ScreenLine(terminalCursorAddress),
+ ScreenLineOffset(terminalCursorAddress))) {
+ GoAway("move", terminalCursorAddress);
+ }
+ DoARefresh();
+ if (needToRing) {
+ StringToTerminal(bellSequence);
+ needToRing = 0;
+ }
+ }
+ EmptyTerminal(); /* move data along */
+ return;
+}
+
+#if defined(NOT43)
+static int
+#else /* defined(NOT43) */
+static void
+#endif /* defined(NOT43) */
+FastScreen()
+{
+#if defined(MSDOS)
+#define SaveCorner 0
+#else /* defined(MSDOS) */
+#define SaveCorner 1
+#endif /* defined(MSDOS) */
+
+#define DoAttribute(a) if (IsHighlightedAttr(a)) { \
+ standout(); \
+ } else { \
+ standend(); \
+ } \
+ if (IsNonDisplayAttr(a)) { \
+ a = 0; /* zero == don't display */ \
+ } \
+ if (!FormattedScreen()) { \
+ a = 1; /* one ==> do display on unformatted */\
+ }
+ ScreenImage *p, *upper;
+ int fieldattr; /* spends most of its time == 0 or 1 */
+
+/* OK. We want to do this a quickly as possible. So, we assume we
+ * only need to go from Lowest to Highest. However, if we find a
+ * field in the middle, we do the whole screen.
+ *
+ * In particular, we separate out the two cases from the beginning.
+ */
+ if ((Highest != HighestScreen()) || (Lowest != LowestScreen())) {
+ register int columnsleft;
+
+ move(ScreenLine(Lowest), ScreenLineOffset(Lowest));
+ p = &Host[Lowest];
+#if !defined(MSDOS)
+ if (Highest == HighestScreen()) {
+ Highest = ScreenDec(Highest);
+ }
+#endif /* !defined(MSDOS) */
+ upper = &Host[Highest];
+ fieldattr = FieldAttributes(Lowest);
+ DoAttribute(fieldattr); /* Set standout, non-display status */
+ columnsleft = NumberColumns-ScreenLineOffset(p-Host);
+
+ while (p <= upper) {
+ if (IsStartFieldPointer(p)) { /* New field? */
+ Highest = HighestScreen();
+ Lowest = LowestScreen();
+ FastScreen(); /* Recurse */
+ return;
+ } else if (fieldattr) { /* Should we display? */
+ /* Display translated data */
+ addch((char)disp_asc[GetTerminalPointer(p)]);
+ } else {
+ addch(' '); /* Display a blank */
+ }
+ /* If the physical screen is larger than what we
+ * are using, we need to make sure that each line
+ * starts at the beginning of the line. Otherwise,
+ * we will just string all the lines together.
+ */
+ p++;
+ if (--columnsleft == 0) {
+ int i = p-Host;
+
+ move(ScreenLine(i), 0);
+ columnsleft = NumberColumns;
+ }
+ }
+ } else { /* Going from Lowest to Highest */
+ unsigned char tmpbuf[MAXNUMBERCOLUMNS+1];
+ ScreenImage *End = &Host[ScreenSize]-1-SaveCorner;
+ register unsigned char *tmp = tmpbuf, *tmpend = tmpbuf+NumberColumns;
+
+ *tmpend = 0; /* terminate from the beginning */
+ move(0,0);
+ p = Host;
+ fieldattr = FieldAttributes(LowestScreen());
+ DoAttribute(fieldattr); /* Set standout, non-display status */
+
+ while (p <= End) {
+ if (IsStartFieldPointer(p)) { /* New field? */
+ if (tmp != tmpbuf) {
+ *tmp++ = 0; /* close out */
+ addstr((char *)tmpbuf);
+ tmp = tmpbuf;
+ tmpend = tmpbuf+NumberColumns-ScreenLineOffset(p-Host)-1;
+ }
+ standend();
+ addch(' ');
+ fieldattr = FieldAttributesPointer(p); /* Get attributes */
+ DoAttribute(fieldattr); /* Set standout, non-display */
+ } else {
+ if (fieldattr) { /* Should we display? */
+ /* Display translated data */
+ *tmp++ = disp_asc[GetTerminalPointer(p)];
+ } else {
+ *tmp++ = ' ';
+ }
+ }
+ /* If the physical screen is larger than what we
+ * are using, we need to make sure that each line
+ * starts at the beginning of the line. Otherwise,
+ * we will just string all the lines together.
+ */
+ p++;
+ if (tmp == tmpend) {
+ int i = p-Host; /* Be sure the "p++" happened first! */
+
+ *tmp++ = 0;
+ addstr((char *)tmpbuf);
+ tmp = tmpbuf;
+ move(ScreenLine(i), 0);
+ tmpend = tmpbuf + NumberColumns;
+ }
+ }
+ if (tmp != tmpbuf) {
+ *tmp++ = 0;
+ addstr((char *)tmpbuf);
+ tmp = tmpbuf;
+ }
+ }
+ Lowest = HighestScreen()+1;
+ Highest = LowestScreen()-1;
+ terminalCursorAddress = CorrectTerminalCursor();
+ if (ERR == move(ScreenLine(terminalCursorAddress),
+ ScreenLineOffset(terminalCursorAddress))) {
+ GoAway("move", terminalCursorAddress);
+ }
+ DoARefresh();
+ if (needToRing) {
+ StringToTerminal(bellSequence);
+ needToRing = 0;
+ }
+ EmptyTerminal(); /* move data along */
+ return;
+}
+
+
+/* TryToSend - send data out to user's terminal */
+
+#if defined(NOT43)
+int
+#else /* defined(NOT43) */
+void
+#endif /* defined(NOT43) */
+ (*TryToSend)() = FastScreen;
+
+/*ARGSUSED*/
+void
+ScreenOIA(oia)
+OIA *oia;
+{
+}
+
+
+/* InitTerminal - called to initialize the screen, etc. */
+
+void
+InitTerminal()
+{
+#if defined(unix)
+ struct sgttyb ourttyb;
+ static int speeds[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800,
+ 2400, 4800, 9600 };
+#endif
+ extern void InitMapping();
+
+ InitMapping(); /* Go do mapping file (MAP3270) first */
+ if (!screenInitd) { /* not initialized */
+#if defined(unix)
+ char KSEbuffer[2050];
+ char *lotsofspace = KSEbuffer;
+ extern void abort();
+ extern char *tgetstr();
+#endif /* defined(unix) */
+
+ if (initscr() == ERR) { /* Initialize curses to get line size */
+ ExitString("InitTerminal: Error initializing curses", 1);
+ /*NOTREACHED*/
+ }
+ MaxNumberLines = LINES;
+ MaxNumberColumns = COLS;
+ ClearArray(Terminal);
+ terminalCursorAddress = SetBufferAddress(0,0);
+#if defined(unix)
+ signal(SIGHUP, abort);
+#endif
+
+ TryToSend = FastScreen;
+#if defined(unix)
+ ioctl(1, TIOCGETP, (char *) &ourttyb);
+ if ((ourttyb.sg_ospeed < 0) || (ourttyb.sg_ospeed > B9600)) {
+ max_changes_before_poll = 1920;
+ } else {
+ max_changes_before_poll = speeds[ourttyb.sg_ospeed]/10;
+ if (max_changes_before_poll < 40) {
+ max_changes_before_poll = 40;
+ }
+ TryToSend = SlowScreen;
+ HaveInput = 1; /* get signals going */
+ }
+#endif /* defined(unix) */
+ setcommandmode();
+ /*
+ * By now, initscr() (in curses) has been called (from telnet.c),
+ * and the screen has been initialized.
+ */
+#if defined(unix)
+ nonl();
+ /* the problem is that curses catches SIGTSTP to
+ * be nice, but it messes us up.
+ */
+ signal(SIGTSTP, SIG_DFL);
+ if ((myKS = tgetstr("ks", &lotsofspace)) != 0) {
+ myKS = strsave(myKS);
+ StringToTerminal(myKS);
+ }
+ if ((myKE = tgetstr("ke", &lotsofspace)) != 0) {
+ myKE = strsave(myKE);
+ }
+ if (tgetstr("md", &lotsofspace) && tgetstr("me", &lotsofspace)) {
+ SO = strsave(tgetstr("md", &lotsofspace));
+ SE = strsave(tgetstr("me", &lotsofspace));
+ }
+#endif
+ DoARefresh();
+ setconnmode();
+ if (VB && *VB) {
+ bellSequence = VB; /* use visual bell */
+ }
+ screenInitd = 1;
+ screenStopped = 0; /* Not stopped */
+ }
+}
+
+
+/* StopScreen - called when we are going away... */
+
+void
+StopScreen(doNewLine)
+int doNewLine;
+{
+ if (screenInitd && !screenStopped) {
+ move(NumberLines-1, 1);
+ standend();
+ inHighlightMode = 0;
+ DoARefresh();
+ setcommandmode();
+ endwin();
+ setconnmode();
+#if defined(unix)
+ if (myKE) {
+ StringToTerminal(myKE);
+ }
+#endif /* defined(unix) */
+ if (doNewLine) {
+ StringToTerminal("\r\n");
+ }
+ EmptyTerminal();
+ screenStopped = 1; /* This is stopped */
+ }
+}
+
+
+/* RefreshScreen - called to cause the screen to be refreshed */
+
+void
+RefreshScreen()
+{
+ clearok(curscr, TRUE);
+ (*TryToSend)();
+}
+
+
+/* ConnectScreen - called to reconnect to the screen */
+
+void
+ConnectScreen()
+{
+ if (screenInitd) {
+#if defined(unix)
+ if (myKS) {
+ StringToTerminal(myKS);
+ }
+#endif /* defined(unix) */
+ RefreshScreen();
+ (*TryToSend)();
+ screenStopped = 0;
+ }
+}
+
+/* LocalClearScreen() - clear the whole ball of wax, cheaply */
+
+void
+LocalClearScreen()
+{
+ extern void Clear3270();
+
+ outputPurge(); /* flush all data to terminal */
+ clear(); /* clear in curses */
+ ClearArray(Terminal);
+ Clear3270();
+ Lowest = HighestScreen()+1; /* everything in sync... */
+ Highest = LowestScreen()+1;
+}
+
+
+void
+BellOff()
+{
+ if (bellwinup) {
+ delwin(bellwin);
+ bellwin = 0;
+ bellwinup = 0;
+ touchwin(stdscr);
+ DoARefresh();
+ }
+}
+
+
+void
+RingBell(s)
+char *s;
+{
+ needToRing = 1;
+ if (s) {
+ int len = strlen(s);
+
+ if (len > COLS-2) {
+ len = COLS-2;
+ }
+ if ((bellwin = newwin(3, len+2, LINES/2, 0)) == NULL) {
+ OurExitString("Error from newwin in RingBell", 1);
+ }
+ werase(bellwin);
+ wstandout(bellwin);
+ box(bellwin, '|', '-');
+ if (wmove(bellwin, 1, 1) == ERR) {
+ OurExitString("Error from wmove in RingBell", 1);
+ }
+ while (len--) {
+ if (waddch(bellwin, *s++) == ERR) {
+ OurExitString("Error from waddch in RingBell", 1);
+ }
+ }
+ wstandend(bellwin);
+ if (wrefresh(bellwin) == ERR) {
+ OurExitString("Error from wrefresh in RingBell", 1);
+ }
+ bellwinup = 1;
+ }
+}
+
+
+/* returns a 1 if no more output available (so, go ahead and block),
+ or a 0 if there is more output available (so, just poll the other
+ sources/destinations, don't block).
+ */
+
+int
+DoTerminalOutput()
+{
+ /* called just before a select to conserve IO to terminal */
+ if (!(screenInitd||screenStopped)) {
+ return 1; /* No output if not initialized */
+ }
+ if ((Lowest <= Highest) || needToRing ||
+ (terminalCursorAddress != CorrectTerminalCursor())) {
+ (*TryToSend)();
+ }
+ if (Lowest > Highest) {
+ return 1; /* no more output now */
+ } else {
+ return 0; /* more output for future */
+ }
+}
+
+/*
+ * The following are defined to handle transparent data.
+ */
+
+void
+TransStop()
+{
+#if defined(unix)
+ if (tcflag == 0) {
+ tcflag = -1;
+ (void) signal(SIGCHLD, SIG_DFL);
+ } else if (tcflag > 0) {
+ setcommandmode();
+ (void) close(tin);
+ (void) close(tout);
+ tin = savefd[0];
+ tout = savefd[1];
+ setconnmode();
+ tcflag = -1;
+ (void) signal(SIGCHLD, SIG_DFL);
+ }
+#endif /* defined(unix) */
+ RefreshScreen();
+}
+
+void
+TransOut(buffer, count, kind, control)
+unsigned char *buffer;
+int count;
+int kind; /* 0 or 5 */
+int control; /* To see if we are done */
+{
+#if defined(unix)
+ extern char *transcom;
+ int inpipefd[2], outpipefd[2];
+ static void aborttc();
+#endif /* defined(unix) */
+
+ while (DoTerminalOutput() == 0) {
+#if defined(unix)
+ HaveInput = 0;
+#endif /* defined(unix) */
+ }
+#if defined(unix)
+ if (transcom && tcflag == -1) {
+ while (1) { /* go thru once */
+ if (pipe(outpipefd) < 0) {
+ break;
+ }
+ if (pipe(inpipefd) < 0) {
+ break;
+ }
+ if ((tcflag = fork()) == 0) {
+ (void) close(outpipefd[1]);
+ (void) close(0);
+ if (dup(outpipefd[0]) < 0) {
+ exit(1);
+ }
+ (void) close(outpipefd[0]);
+ (void) close(inpipefd[0]);
+ (void) close(1);
+ if (dup(inpipefd[1]) < 0) {
+ exit(1);
+ }
+ (void) close(inpipefd[1]);
+ if (execl("/bin/csh", "csh", "-c", transcom, (char *) 0)) {
+ exit(1);
+ }
+ }
+ (void) close(inpipefd[1]);
+ (void) close(outpipefd[0]);
+ savefd[0] = tin;
+ savefd[1] = tout;
+ setcommandmode();
+ tin = inpipefd[0];
+ tout = outpipefd[1];
+ (void) signal(SIGCHLD, aborttc);
+ setconnmode();
+ tcflag = 1;
+ break;
+ }
+ if (tcflag < 1) {
+ tcflag = 0;
+ }
+ }
+#endif /* defined(unix) */
+ (void) DataToTerminal((char *)buffer, count);
+ if (control && (kind == 0)) { /* Send in AID byte */
+ SendToIBM();
+ } else {
+ extern void TransInput();
+
+ TransInput(1, kind); /* Go get some data */
+ }
+}
+
+
+#if defined(unix)
+static void
+aborttc(signo)
+ int signo;
+{
+ setcommandmode();
+ (void) close(tin);
+ (void) close(tout);
+ tin = savefd[0];
+ tout = savefd[1];
+ setconnmode();
+ tcflag = 0;
+}
+#endif /* defined(unix) */
diff --git a/usr.bin/tn3270/telnet/Makefile b/usr.bin/tn3270/telnet/Makefile
new file mode 100644
index 0000000..b4095e2
--- /dev/null
+++ b/usr.bin/tn3270/telnet/Makefile
@@ -0,0 +1,87 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+# The following is the telnet makefile for tn3270, using the shared telnet
+# sources.
+
+#
+# TERMCAP Define this if your system is termcap based,
+# otherwise a terminfo based system is assumed.
+#
+# SRCRT Includes code to allow you to specify source routes.
+# Format is:
+# [!]@hop1@hop2...[@|:]dst
+# Leading ! means strict source route.
+#
+# NOSTRNCASECMP Define this if you do not have strncasecmp() in
+# your C libarary.
+#
+# USE_TERMIO Define this if you have System V termio structures.
+# What is here is how things are on Cray computers.
+#
+# KLUDGELINEMODE Define this to get the kludged up version of linemode
+# that was in 4.3BSD. This is a good thing to have
+# around for talking to older systems.
+#
+
+DEFINES= -DTERMCAP -DSRCRT -DKLUDGELINEMODE -DUSE_TERMIO
+
+
+VPATH = ${.CURDIR}/../../telnet
+XINCLUDES= -I${.CURDIR}/../../telnet -I${.CURDIR}
+INCLUDES=
+XDEFINES = -DTN3270
+OPTIMIZE= -O
+CFLAGS = ${OPTIMIZE} ${INCLUDES} ${DEFINES}
+XCFLAGS= ${XINCLUDES} ${XDEFINES}
+LD = ld
+LDFLAGS = -r
+PRINT = print
+ACTION = sccs tell
+LIBC= /usr/lib/libc.a
+ALLH= defines.h externs.h fdset.h general.h ring.h types.h
+SRCS= commands.c main.c network.c ring.c \
+ sys_bsd.c telnet.c terminal.c \
+ tn3270.c utilities.c
+ALLHC= ${ALLH} ${SRCS}
+ALLPRINT = ${ALLHC}
+ALLSOURCE= ${ALLHC} Makefile Makefile_ultrix
+OBJS= commands.o main.o network.o ring.o sys_bsd.o \
+ telnet.o terminal.o tn3270.o utilities.o
+
+.c.o:
+ ${CC} -c ${CFLAGS} ${XCFLAGS} $<
+
+telprog.o: ${OBJS} ${LIBC}
+ ${LD} ${LDFLAGS} -o $@ ${OBJS}
+
+clean: FRC
+ rm -f telprog.o ${OBJS} core telnet
+
+depend: FRC ${SRCS}
+ mkdep ${CFLAGS} ${SRCS}
+
+lint: FRC ${SRCS}
+ lint ${CFLAGS} ${SRCS}
+
+tags: FRC ${ALLHC}
+ ctags ${ALLHC}
+
+print: FRC ${ALLPRINT}
+ ${PRINT} ${ALLPRINT}
+
+action: FRC
+ ${ACTION}
+
+clist: FRC ${SRCS}
+ @for i in ${SRCS} ; \
+ do (echo ${DIRPATH}$$i); done
+
+hclist: FRC ${ALLHC}
+ @for i in ${ALLHC} ; \
+ do (echo ${DIRPATH}$$i); done
+
+sourcelist: FRC ${ALLSOURCE}
+ @for i in ${ALLSOURCE} ../../telnet/Makefile ; \
+ do (echo ${DIRPATH}$$i); done
+
+FRC:
diff --git a/usr.bin/tn3270/tn3270/Makefile b/usr.bin/tn3270/tn3270/Makefile
new file mode 100644
index 0000000..75f029d
--- /dev/null
+++ b/usr.bin/tn3270/tn3270/Makefile
@@ -0,0 +1,104 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tn3270
+CFLAGS+=-I${.CURDIR} -I.
+LDADD+= -lcurses -ltermcap -ltelnet -lcrypt
+DPADD+= ${LIBCURSES} /usr/lib/libtermcap.a /usr/lib/libtelnet.a
+CLEANFILES+= asc_disp.OUT asc_disp.out disp_asc.OUT disp_asc.out TMPfunc.out
+.PATH: ${.CURDIR}/../api ${.CURDIR}/../ascii ${.CURDIR}/../ctlr
+.PATH: ${.CURDIR}/../general ${.CURDIR}/../sys_curses ${.CURDIR}/../../telnet
+
+MAN1= tn3270.1
+
+SRCS+= apilib.c api_bsd.c api_exch.c asc_ebc.c astosc.c dctype.c
+SRCS+= disp_asc.c ebc_disp.c
+SRCS+= map3270.c termin.c
+SRCS+= api.c function.c inbound.c oia.c options.c outbound.c
+SRCS+= genbsubs.c globals.c system.c termout.c
+SRCS+= commands.c main.c network.c ring.c sys_bsd.c telnet.c terminal.c
+SRCS+= tn3270.c utilities.c
+
+# This and the dependency hacks below to make 'depend' target
+# work right...
+
+DEPSRCS+= astosc.OUT asc_disp.OUT disp_asc.OUT kbd.OUT
+DEPSRCS+= apilib.c api_bsd.c api_exch.c asc_ebc.c dctype.c
+DEPSRCS+= ebc_disp.c
+DEPSRCS+= map3270.c termin.c
+DEPSRCS+= api.c function.c inbound.c oia.c options.c outbound.c
+DEPSRCS+= genbsubs.c globals.c system.c termout.c
+DEPSRCS+= commands.c main.c network.c ring.c sys_bsd.c telnet.c terminal.c
+DEPSRCS+= tn3270.c utilities.c
+
+.if exists(${.CURDIR}/../tools/mkastosc/obj)
+MKASTOSCDIR= ${.CURDIR}/../tools/mkastosc/obj
+.else
+MKASTOSCDIR= ${.CURDIR}/../tools/mkastosc
+.endif
+
+.if exists(${.CURDIR}/../tools/mkastods/obj)
+MKASTODSDIR= ${.CURDIR}/../tools/mkastods/obj
+.else
+MKASTODSDIR= ${.CURDIR}/../tools/mkastods
+.endif
+
+.if exists(${.CURDIR}/../tools/mkdstoas/obj)
+MKDSTOASDIR= ${.CURDIR}/../tools/mkdstoas/obj
+.else
+MKDSTOASDIR= ${.CURDIR}/../tools/mkdstoas
+.endif
+
+.if exists(${.CURDIR}/../tools/mkhits/obj)
+MKHITSDIR= ${.CURDIR}/../tools/mkhits/obj
+.else
+MKHITSDIR= ${.CURDIR}/../tools/mkhits
+.endif
+
+astosc.o: astosc.OUT
+CLEANFILES+= astosc.OUT astosc.out
+astosc.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/function.h
+astosc.OUT: ${.CURDIR}/../ctlr/${KBD} ${MKASTOSCDIR}/mkastosc
+ ${MKASTOSCDIR}/mkastosc \
+ ${.CURDIR}/../ctlr/hostctlr.h \
+ ${.CURDIR}/../ctlr/function.h < ${.CURDIR}/../ctlr/${KBD} \
+ > ${.TARGET}
+ rm -f astosc.out; ln -s astosc.OUT astosc.out
+
+disp_asc.o: asc_disp.OUT disp_asc.OUT
+asc_disp.OUT: ${MKASTODSDIR}/mkastods
+ ${MKASTODSDIR}/mkastods > ${.TARGET}
+ rm -f asc_disp.out; ln -s asc_disp.OUT asc_disp.out
+
+disp_asc.OUT: ${MKDSTOASDIR}/mkdstoas
+ ${MKDSTOASDIR}/mkdstoas > ${.TARGET}
+ rm -f disp_asc.out; ln -s disp_asc.OUT disp_asc.out
+
+inbound.o: kbd.OUT
+CLEANFILES += kbd.OUT kbd.out
+kbd.OUT: ${.CURDIR}/../ctlr/hostctlr.h ${.CURDIR}/../ctlr/${KBD}
+kbd.OUT: ${MKHITSDIR}/mkhits
+ ${CC} ${CFLAGS} -E ${.CURDIR}/../ctlr/function.c > TMPfunc.out
+ ${MKHITSDIR}/mkhits ${.CURDIR}/../ctlr/hostctlr.h \
+ TMPfunc.out < ${.CURDIR}/../ctlr/${KBD} > ${.TARGET}
+ rm -f kbd.out; ln -s kbd.OUT kbd.out
+
+# astosc.out
+# asc_disp.out disp_asc.out
+# default.map
+# kbd.out
+
+${MKASTOSCDIR}/mkastosc:
+ cd ${.CURDIR}/../tools/mkastosc; make
+${MKASTODSDIR}/mkastods:
+ cd ${.CURDIR}/../tools/mkastods; make
+${MKDSTOASDIR}/mkdstoas:
+ cd ${.CURDIR}/../tools/mkdstoas; make
+${MKHITSDIR}/mkhits:
+ cd ${.CURDIR}/../tools/mkhits; make
+
+depend: .depend
+.depend: ${DEPSRCS}
+ mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c}
+
+.include <../../Makefile.inc>
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tn3270/tn3270.1 b/usr.bin/tn3270/tn3270/tn3270.1
new file mode 100644
index 0000000..778302e
--- /dev/null
+++ b/usr.bin/tn3270/tn3270/tn3270.1
@@ -0,0 +1,339 @@
+.\" Copyright (c) 1986, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tn3270.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt TN3270 1
+.Os BSD 4.3
+.Sh NAME
+.Nm tn3270
+.Nd full-screen remote login to
+.Tn IBM VM/CMS
+.Sh SYNOPSIS
+.Nm tn3270
+.Op Fl d
+.Op Fl n Ar filename
+.Op Fl t Ar commandname
+.Op Ar sysname Op port
+.Sh DESCRIPTION
+.Nm Tn3270
+permits a full-screen, full-duplex connection
+from a
+.Tn UNIX
+machine
+to an
+.Tn IBM
+(or compatible) machine.
+.Nm Tn3270
+gives the appearance of being logged in
+to the remote machine
+from an
+.Tn IBM
+3270 terminal.
+Of course, you must have an account on the machine
+to which you connect in order to log in.
+.Nm Tn3270
+looks to the user in many respects
+like the Yale
+.Tn ASCII
+Terminal Communication System II.
+.Nm Tn3270
+is actually a modification of the Arpanet
+.Tn TELNET
+user interface (see
+.Xr telnet 1 )
+which will, in certain circumstances, interpret and generate
+raw 3270 control streams.
+.Pp
+The flags to
+.Nm tn3270
+are as follows:
+.Bl -tag -width Fl
+.It Fl d
+Turn on socket-level tracing (for super-user only)
+.Fl n Ns Ar filename
+Specify a file to receive network trace data
+output (from commands "toggle netdata" and
+"toggle options", see
+.Xr telnet 1 ) ;
+the default is for output
+to be directed to the standard error file.
+.Fl t Ns Ar commandname
+Specify a
+.Tn UNIX
+command to process
+.Tn IBM
+4994 style transparent mode
+data received from the remote
+.Tn IBM
+machine.
+.It Ar sysname
+The name of the remote system. If the remote name
+is NOT specified, the user will be prompted for a
+command (see below).
+.It Ar port
+The port to connect to on the remote system.
+Normally,
+.Nm tn3270
+attempts to connect to the
+standard
+.Tn TELNET
+port (port
+23) on the remote machine.
+.El
+.Pp
+When
+.Nm tn3270
+first connects to the remote system, it will negotiate to go into
+3270 mode.
+Part of this negotiation involves telling the remote system what model
+3270 it is emulating.
+In all cases,
+.Nm tn3270
+emulates a 3278 terminal.
+To decide which specific model,
+.Nm tn3270
+looks at the number of lines and columns on the actual terminal (as
+defined in the
+.Ev TERM
+environment variable; see
+.Xr termcap 5 ) .
+The terminal (or window in which
+.Nm tn3270
+is running, on multiple
+window systems) must have at least 80 columns and 24 lines, or
+.Nm tn3270
+will not go into emulation mode.
+If the terminal does have at least 80 columns and at least 24 lines,
+the following table describes the emulation:
+.Pp
+.ne 7v
+.Bd -filled -offset center
+.Bl -column (rows*columns)
+.It minimum_size emulated
+.It (rows*columns) terminal
+.It -------------- ------------
+.It 27*132 3278 model 5
+.It 43*80 3278 model 4
+.It 32*80 3278 model 3
+.It 24*80 3278 model 2.
+.El
+.Ed
+.Pp
+Emulation of the 3270 terminal is done in the
+.Tn UNIX
+process.
+This emulation involves mapping
+3270-style commands from the host
+into appropriate sequences to control the user's terminal screen.
+.Nm Tn3270
+uses
+.Xr curses 3
+and the
+.Pa /usr/share/misc/termcap
+file to do this.
+The emulation also involves simulating the special 3270 keyboard keys
+(program function keys, etc.)
+by mapping sequences of keystrokes
+from the
+.Tn ASCII
+keyboard into appropriate 3270 control strings.
+This mapping is terminal dependent and is specified
+in a description file,
+.Pa /usr/share/misc/map3270 ,
+(see
+.Xr map3270 5 )
+or in an environment variable
+.Ev MAP3270
+(and, if necessary,
+.Ev MAP3270A ,
+.Ev MAP3270B ,
+and so on - see
+.Xr mset 1 ) .
+Any special function keys on the
+.Tn ASCII
+keyboard are used whenever possible.
+If an entry for the user's terminal
+is not found,
+.Nm tn3270
+looks for an entry for the terminal type
+.Em unknown .
+If this is not found,
+.Nm tn3270
+uses a default keyboard mapping
+(see
+.Xr map3270 5 ) .
+.Pp
+The first character of each special keyboard mapping sequence
+is either an
+.Tn ASCII
+escape
+.Pq Tn ESC ,
+a control character, or an
+.Tn ASCII
+delete
+.Pq Tn DEL .
+If the user types an unrecognized function key sequence,
+.Nm tn3270
+sends an
+.Tn ASCII
+bell
+.Pq Tn BEL ,
+or a visual bell if
+defined in the user's termcap entry, to the user's terminal
+and nothing is sent to the
+.Tn IBM
+host.
+.Pp
+If
+.Nm tn3270
+is invoked without specifying a remote host system name,
+it enters local command mode,
+indicated by the prompt
+.Dq Li tn3270>\ .
+In this mode,
+.Nm tn3270
+accepts and executes
+all the commands of
+.Xr telnet 1 ,
+plus one additional command:
+.Pp
+.Bl -tag -width Ar
+.It Ic transcom
+Specify
+.Tn UNIX
+command for
+.Tn IBM
+4994 style transparent mode processing.
+.El
+.Pp
+.Nm Tn3270
+command mode may also be entered, after connecting to a host, by typing
+a special escape sequence.
+If
+.Nm tn3270
+has succeeded in negotiating 3270 mode with the remote host, the
+escape sequence will be as defined by the map3270 (see
+.Xr map3270 5 )
+entry for the user's terminal type
+(typically control-C);
+otherwise the escape sequence will initially be set to the
+single character
+.Sq Li \&^]
+(control right square bracket).
+.Pp
+While in command mode, any host login session is still alive
+but temporarily suspended.
+The host login session may be resumed by entering an empty line
+(press the
+.Tn RETURN
+key)
+in response to the command prompt.
+A session may be terminated by logging off the foreign host,
+or by typing ``quit'' or ``close'' while in local command mode.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/termcap -compact
+.It Pa /usr/share/misc/termcap
+.It Pa /usr/share/misc/map3270
+.El
+.\" .Sh AUTHOR
+.\" Greg Minshall
+.Sh NOTES
+The
+.Tn IBM
+4994 style transparent mode command is invoked when
+.Nm tn3270
+receives
+.Tn IBM
+4994 style transparent output from the remote host.
+Output and input pipes are created for communication between the two
+processes.
+The pipes are closed when a 3270 clear command is received from the remote
+hosts, signaling the end of transparent mode output.
+Transparent mode is necessary for sending
+.Tn ASCII
+control characters over the
+3270 terminal connection;
+.Tn ASCII
+graphics terminal support is accomplished this
+way.
+Developers of
+.Ic transcom
+commands should note that the
+.Ic transcom
+stdin pipe end will be in
+.Dv CBREAK
+mode, with
+.Dv ECHO
+and
+.Dv CRMOD
+turned off.
+.Sh ENVIRONMENT
+.Nm Tn3270
+checks the following environment variables:
+.Ev TERM ,
+.Ev MAP3270 ,
+.Ev MAP3270[A...] .
+Information on these can be found in
+.Xr mset 1 .
+.Nm Tn3270
+also checks
+.Ev SHELL ,
+.Ev KEYBD
+and
+.Ev API3270 .
+.Sh SEE ALSO
+.Xr mset 1 ,
+.Xr telnet 1 ,
+.Xr curses 3 ,
+.Xr termcap 3 ,
+.Xr termcap 5 ,
+.Xr map3270 5 ,
+.Rs
+.%T "Yale ASCII Terminal Communication"
+.%B "System II Program Description/Operator's Manual"
+.%R IBM SB30-1911
+.Re
+.Sh HISTORY
+The
+.Nm tn3270
+command appeared in
+.Bx 4.3 .
+.Sh BUGS
+Tn3270 is slow and uses system resources prodigiously.
+.Pp
+Not all 3270 functions are supported,
+nor all Yale enhancements.
+.Pp
+Error conditions (attempting to enter data in a protected field, for
+example) should cause a message to be sent to the user's terminal
+instead of just ringing a bell.
diff --git a/usr.bin/tn3270/tools/Makefile b/usr.bin/tn3270/tools/Makefile
new file mode 100644
index 0000000..0b64f8f
--- /dev/null
+++ b/usr.bin/tn3270/tools/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+SUBDIR= mkhits mkastosc mkastods mkdstoas mkdctype
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/tn3270/tools/mkastods/Makefile b/usr.bin/tn3270/tools/mkastods/Makefile
new file mode 100644
index 0000000..a2d36a1
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkastods/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkastods
+SRCS= mkastods.c asc_ebc.c ebc_disp.c
+CFLAGS+=-I${.CURDIR}/.. -I.
+NOMAN= noman
+.PATH: ${.CURDIR}/../../api
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tools/mkastods/mkastods.c b/usr.bin/tn3270/tools/mkastods/mkastods.c
new file mode 100644
index 0000000..6a9f72e
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkastods/mkastods.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkastods.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include <ctype.h>
+#include "../api/asc_ebc.h"
+#include "../api/ebc_disp.h"
+
+int
+main()
+{
+ int i;
+
+ /* For each ascii code, find the display code that matches */
+
+ printf("unsigned char asc_disp[256] = {");
+ for (i = 0; i < NASCII; i++) {
+ if ((i%8) == 0) {
+ printf("\n");
+ }
+ printf("\t0x%02x,", ebc_disp[asc_ebc[i]]);
+ }
+ for (i = sizeof disp_ebc; i < 256; i++) {
+ if ((i%8) == 0) {
+ printf("\n");
+ }
+ printf("\t0x%02x,", 0);
+ }
+ printf("\n};\n");
+
+ return 0;
+}
diff --git a/usr.bin/tn3270/tools/mkastosc/Makefile b/usr.bin/tn3270/tools/mkastosc/Makefile
new file mode 100644
index 0000000..a6268f5
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkastosc/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkastosc
+SRCS= mkastosc.c dohits.c asc_ebc.c ebc_disp.c
+CFLAGS+=-I${.CURDIR}/../mkhits -I${.CURDIR}/.. -I.
+NOMAN= noman
+.PATH: ${.CURDIR}/../mkhits ${.CURDIR}/../../api
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tools/mkastosc/mkastosc.c b/usr.bin/tn3270/tools/mkastosc/mkastosc.c
new file mode 100644
index 0000000..932a388
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkastosc/mkastosc.c
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkastosc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include <ctype.h>
+
+#include "../general/general.h"
+#include "../ctlr/function.h"
+
+#include "dohits.h"
+
+static struct tbl {
+ unsigned char
+ scancode,
+ used;
+ char
+ *shiftstate;
+} tbl[128];
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int scancode;
+ int asciicode;
+ int empty;
+ int i;
+ int c;
+ int found;
+ struct hits *ph;
+ struct Hits *Ph;
+ struct thing *this;
+ struct thing **attable;
+ struct tbl *Pt;
+ static char *shiftof[] =
+ { "0", "SHIFT_UPSHIFT", "SHIFT_ALT", "SHIFT_ALT|SHIFT_UPSHIFT" };
+ char *aidfile = 0, *fcnfile = 0;
+
+ if (argc > 1) {
+ if (argv[1][0] != '-') {
+ aidfile = argv[1];
+ }
+ }
+ if (argc > 2) {
+ if (argv[2][0] != '-') {
+ fcnfile = argv[2];
+ }
+ }
+
+ dohits(aidfile, fcnfile); /* Set up "Hits" */
+
+ printf("/*\n");
+ printf(" * Ascii to scancode conversion table. First\n");
+ printf(" * 128 bytes (0-127) correspond with actual Ascii\n");
+ printf(" * characters; the rest are functions from ctrl/function.h\n");
+ printf(" */\n");
+ /* Build the ascii part of the table. */
+ for (Ph = Hits, scancode = 0; Ph <= Hits+highestof(Hits);
+ Ph++, scancode++) {
+ ph = &Ph->hits;
+ for (i = 0; i < 4; i++) {
+ if (ph->hit[i].ctlrfcn == FCN_CHARACTER) {
+ c = Ph->name[i][0]; /* "name" of this one */
+ if (tbl[c].used == 0) {
+ tbl[c].used = 1;
+ tbl[c].shiftstate = shiftof[i];
+ tbl[c].scancode = scancode;
+ }
+ }
+ }
+ }
+ /* Now, output the table */
+ for (Pt = tbl, asciicode = 0; Pt <= tbl+highestof(tbl); Pt++, asciicode++) {
+ if (Pt->used == 0) {
+ if (isprint(asciicode) && (asciicode != ' ')) {
+ fprintf(stderr, "Unable to produce scancode sequence for");
+ fprintf(stderr, " ASCII character [%c]!\n", asciicode);
+ }
+ printf("\t{ 0, 0, undefined, 0 },\t");
+ } else {
+ printf("\t{ 0x%02x, %s, FCN_CHARACTER, 0 },",
+ Pt->scancode, Pt->shiftstate);
+ }
+ printf("\t/* 0x%x", asciicode);
+ if (isprint(asciicode)) {
+ printf(" [%c]", asciicode);
+ }
+ printf(" */\n");
+ }
+
+
+ for (attable = &table[0]; attable <= &table[highestof(table)]; attable++) {
+ for (this = *attable; this; this = this->next) {
+ Ph = this->hits;
+ if (Ph == 0) {
+ continue;
+ }
+ for (i = 0; i < 4; i++) {
+ if ((Ph->name[i] != 0) &&
+ (Ph->name[i][0] == this->name[0]) &&
+ (strcmp(Ph->name[i], this->name) == 0)) {
+ printf("\t{ 0x%02x, %s, ",
+ Ph-Hits, shiftof[i]);
+ if (memcmp("AID_", this->name, 4) == 0) { /* AID key */
+ printf("FCN_AID, ");
+ } else {
+ printf("%s, ", Ph->name[i]);
+ }
+ if (memcmp("PF", this->name+4, 2) == 0) {
+ printf("\"PFK%s\" },\n", Ph->name[i]+4+2);
+ } else {
+ printf("\"%s\" },\n", Ph->name[i]+4);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.bin/tn3270/tools/mkdctype/Makefile b/usr.bin/tn3270/tools/mkdctype/Makefile
new file mode 100644
index 0000000..818bb52
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdctype/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkdctype
+SRCS= mkdctype.c ebc_disp.c ectype.c
+CFLAGS+=-I${.CURDIR}/.. -I.
+NOMAN= noman
+.PATH: ${.CURDIR}/../../api
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tools/mkdctype/ectype.c b/usr.bin/tn3270/tools/mkdctype/ectype.c
new file mode 100644
index 0000000..ebdca0b
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdctype/ectype.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ectype.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ectype.h"
+
+char ectype[] = {
+/* 0x00 */
+ E_SPACE,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0x10 */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0x20 */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0x30 */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0x40 */
+ E_SPACE,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+/* 0x50 */
+ E_PRINT|E_PUNCT,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+/* 0x60 */
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+/* 0x70 */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_PUNCT,
+/* 0x80 */
+ 0x00,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0x90 */
+ 0x00,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xA0 */
+ 0x00,
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ E_PRINT|E_LOWER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xB0 */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xC0 */
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xD0 */
+ E_PRINT|E_PUNCT,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xE0 */
+ E_PRINT|E_PUNCT,
+ 0x00,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ E_PRINT|E_UPPER,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+/* 0xF0 */
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ E_PRINT|E_DIGIT,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00
+};
diff --git a/usr.bin/tn3270/tools/mkdctype/ectype.h b/usr.bin/tn3270/tools/mkdctype/ectype.h
new file mode 100644
index 0000000..15dfb86
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdctype/ectype.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ectype.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define INCLUDED_ECTYPE
+
+#define E_UPPER 0x01
+#define E_LOWER 0x02
+#define E_DIGIT 0x04
+#define E_SPACE 0x08
+#define E_PUNCT 0x10
+#define E_PRINT 0x20
+
+#define Eisalpha(c) (ectype[(c)&0xff]&(E_UPPER|E_LOWER))
+#define Eisupper(c) (ectype[(c)&0xff]&E_UPPER)
+#define Eislower(c) (ectype[(c)&0xff]&E_LOWER)
+#define Eisdigit(c) (ectype[(c)&0xff]&E_DIGIT)
+#define Eisalnum(c) (ectype[(c)&0xff]&(E_UPPER|E_LOWER|E_DIGIT))
+#define Eisspace(c) (ectype[(c)&0xff]&E_SPACE) /* blank or null */
+#define Eispunct(c) (ectype[(c)&0xff]&E_PUNCT)
+#define Eisprint(c) (ectype[(c)&0xff]&E_PRINT)
diff --git a/usr.bin/tn3270/tools/mkdctype/mkdctype.c b/usr.bin/tn3270/tools/mkdctype/mkdctype.c
new file mode 100644
index 0000000..24ee095
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdctype/mkdctype.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkdctype.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "../api/ebc_disp.h"
+#include "ectype.h"
+
+
+extern unsigned char ectype[256];
+
+
+void
+main()
+{
+ static unsigned char dctype[192] = { 0 };
+ int i;
+ char *orbar;
+ int type;
+
+ for (i = 0; i < sizeof ectype; i++) {
+ dctype[ebc_disp[i]] = ectype[i];
+ }
+
+ for (i = 0; i < sizeof dctype; i++) {
+ if ((i%16) == 0) {
+ printf("/*%02x*/\n", i);
+ }
+ printf("\t");
+ type = dctype[i];
+ orbar = "";
+ if (type & E_UPPER) {
+ printf("E_UPPER");
+ orbar = "|";
+ }
+ if (type & E_LOWER) {
+ printf("%sD_LOWER", orbar);
+ orbar = "|";
+ }
+ if (type & E_DIGIT) {
+ printf("%sD_DIGIT", orbar);
+ orbar = "|";
+ }
+ if (type & E_SPACE) {
+ printf("%sD_SPACE", orbar);
+ orbar = "|";
+ }
+ if (type & E_PUNCT) {
+ printf("%sD_PUNCT", orbar);
+ orbar = "|";
+ }
+ if (type & E_PRINT) {
+ printf("%sD_PRINT", orbar);
+ orbar = "|";
+ }
+ if (orbar[0] == 0) {
+ printf("0");
+ }
+ printf(",\n");
+ }
+}
diff --git a/usr.bin/tn3270/tools/mkdstoas/Makefile b/usr.bin/tn3270/tools/mkdstoas/Makefile
new file mode 100644
index 0000000..b2abeb3
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdstoas/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkdstoas
+SRCS= mkdstoas.c asc_ebc.c ebc_disp.c
+CFLAGS+=-I${.CURDIR}/.. -I.
+NOMAN= noman
+.PATH: ${.CURDIR}/../../api
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tools/mkdstoas/mkdstoas.c b/usr.bin/tn3270/tools/mkdstoas/mkdstoas.c
new file mode 100644
index 0000000..befd3c6
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkdstoas/mkdstoas.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkdstoas.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include <ctype.h>
+#include "../api/asc_ebc.h"
+#include "../api/ebc_disp.h"
+
+
+int
+main()
+{
+ int i;
+
+ /* For each display code, find the ascii code that matches */
+
+ printf("unsigned char disp_asc[256] = {");
+ for (i = 0; i < sizeof disp_ebc; i++) {
+ if ((i%8) == 0) {
+ printf("\n");
+ }
+ printf("\t0x%02x,", ebc_asc[disp_ebc[i]]);
+ }
+ for (i = sizeof disp_ebc; i < 256; i++) {
+ if ((i%8) == 0) {
+ printf("\n");
+ }
+ printf("\t0x%02x,", ' ');
+ }
+ printf("\n};\n");
+
+ return 0;
+}
diff --git a/usr.bin/tn3270/tools/mkhits/Makefile b/usr.bin/tn3270/tools/mkhits/Makefile
new file mode 100644
index 0000000..c7c8802
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkhits/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mkhits
+SRCS= mkhits.c dohits.c asc_ebc.c ebc_disp.c
+CFLAGS+=-I${.CURDIR}/.. -I.
+NOMAN= noman
+.PATH: ${.CURDIR}/../../api
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tn3270/tools/mkhits/dohits.c b/usr.bin/tn3270/tools/mkhits/dohits.c
new file mode 100644
index 0000000..7f75569
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkhits/dohits.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dohits.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This program scans a file which describes a keyboard. The output
+ * of the program is a series of 'C' declarations which describe a
+ * mapping between (scancode, shiftstate, altstate) and 3270 functions,
+ * characters, and AIDs.
+ *
+ * The format of the input file is as follows:
+ *
+ * keynumber [ scancode [ unshifted [ shifted [ alted [ shiftalted ] ] ] ] ]
+ *
+ * keynumber is in decimal, and starts in column 1.
+ * scancode is hexadecimal.
+ * unshifted, etc. - these are either a single ascii character,
+ * or the name of a function or an AID-generating key.
+ *
+ * all fields are separated by a single space.
+ */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include <ctype.h>
+#include "../general/general.h"
+#include "../api/asc_ebc.h"
+#include "../api/ebc_disp.h"
+#include "../ctlr/function.h"
+
+#include "dohits.h"
+
+struct Hits Hits[256]; /* one for each of 0x00-0xff */
+
+struct thing *table[100];
+
+extern char *malloc();
+
+unsigned int
+dohash(seed, string)
+unsigned int seed;
+char *string;
+{
+ register unsigned int i = seed;
+ register unsigned char c;
+
+ while (c = *string++) {
+ if (c >= 0x60) {
+ c -= (0x60+0x20);
+ } else {
+ c -= 0x20;
+ }
+ i = (i>>26) + (i<<6) + (c&0x3f);
+ }
+ return i;
+}
+
+void
+add(first, second, value)
+char *first, *second;
+int value;
+{
+ struct thing **item, *this;
+
+ item = &firstentry(second);
+ this = (struct thing *) malloc(sizeof *this);
+ memset(this, 0, sizeof *this);
+ this->next = *item;
+ *item = this;
+ this->value = value;
+ strcpy(this->name, first);
+ strcpy(this->name+strlen(this->name), second);
+}
+
+void
+scanwhite(file, prefix)
+char *file, /* Name of file to scan for whitespace prefix */
+ *prefix; /* prefix of what should be picked up */
+{
+ FILE *ourfile;
+ char compare[100];
+ char what[100], value[100];
+ char line[200];
+
+ sprintf(compare, " %s%%[^,\t \n]", prefix);
+ if ((ourfile = fopen(file, "r")) == NULL) {
+ perror("fopen");
+ exit(1);
+ }
+ while (!feof(ourfile)) {
+ if (fscanf(ourfile, compare, what) == 1) {
+ add(prefix, what, 0);
+ }
+ do {
+ if (fgets(line, sizeof line, ourfile) == NULL) {
+ if (!feof(ourfile)) {
+ perror("fgets");
+ }
+ break;
+ }
+ } while (line[strlen(line)-1] != '\n');
+ }
+}
+
+void
+scandefine(file, prefix)
+char *file, /* Name of file to scan for #define prefix */
+ *prefix; /* prefix of what should be picked up */
+{
+ FILE *ourfile;
+ char compare[100];
+ char what[100], value[100];
+ char line[200];
+ int whatitis;
+
+ sprintf(compare, "#define %s%%s %%s", prefix);
+ if ((ourfile = fopen(file, "r")) == NULL) {
+ perror("fopen");
+ exit(1);
+ }
+ while (!feof(ourfile)) {
+ if (fscanf(ourfile, compare, what, value) == 2) {
+ if (value[0] == '0') {
+ if ((value[1] == 'x') || (value[1] == 'X')) {
+ sscanf(value, "0x%x", &whatitis);
+ } else {
+ sscanf(value, "0%o", &whatitis);
+ }
+ } else {
+ sscanf(value, "%d", &whatitis);
+ }
+ add(prefix, what, whatitis);
+ }
+ do {
+ if (fgets(line, sizeof line, ourfile) == NULL) {
+ if (!feof(ourfile)) {
+ perror("fgets");
+ }
+ break;
+ }
+ } while (line[strlen(line)-1] != '\n');
+ }
+}
+
+char *savechr(c)
+unsigned char c;
+{
+ char *foo;
+
+ foo = malloc(sizeof c);
+ if (foo == 0) {
+ fprintf(stderr, "No room for ascii characters!\n");
+ exit(1);
+ }
+ *foo = c;
+ return foo;
+}
+
+char *
+doit(hit, type, hits)
+struct hit *hit;
+unsigned char *type;
+struct Hits *hits;
+{
+ struct thing *this;
+
+ hit->ctlrfcn = FCN_NULL;
+ if (type[0] == 0) {
+ return 0;
+ }
+ if (type[1] == 0) { /* character */
+ hit->ctlrfcn = FCN_CHARACTER;
+ hit->code = ebc_disp[asc_ebc[type[0]]];
+ return savechr(*type); /* The character is the name */
+ } else {
+ for (this = firstentry(type); this; this = this->next) {
+ if ((type[0] == this->name[4])
+ && (strcmp(type, this->name+4) == 0)) {
+ this->hits = hits;
+ if (this->name[0] == 'F') {
+ hit->ctlrfcn = FCN_NULL; /* XXX */
+ } else {
+ hit->ctlrfcn = FCN_AID;
+ }
+ return this->name;
+ }
+ }
+ fprintf(stderr, "Error: Unknown type %s.\n", type);
+ return 0;
+ }
+}
+
+
+void
+dohits(aidfile, fcnfile)
+char *aidfile, *fcnfile;
+{
+ unsigned char plain[100], shifted[100], alted[100], shiftalted[100];
+ unsigned char line[200];
+ int keynumber, scancode;
+ int empty;
+ int i;
+ struct hit *hit;
+ struct hits *ph;
+ struct Hits *Ph;
+
+ memset((char *)Hits, 0, sizeof Hits);
+
+ /*
+ * First, we read "host3270.h" to find the names/values of
+ * various AID; then we read kbd3270.h to find the names/values
+ * of various FCNs.
+ */
+
+ if (aidfile == 0) {
+ aidfile = "../ctlr/hostctlr.h";
+ }
+ scandefine(aidfile, "AID_");
+ if (fcnfile == 0) {
+ fcnfile = "../ctlr/function.h";
+ }
+ scanwhite(fcnfile, "FCN_");
+
+ while (fgets(line, sizeof line, stdin) != NULL) {
+ if (!isdigit(line[0])) {
+ continue;
+ }
+ plain[0] = shifted[0] = alted[0] = shiftalted[0] = 0;
+ keynumber = -1;
+ scancode = -1;
+ (void) sscanf(line, "%d %x %s %s %s %s", &keynumber,
+ &scancode, plain, shifted, alted, shiftalted);
+ if ((keynumber == -1) || (scancode == -1)
+ || ((plain[0] == 0)
+ && (shifted[0] == 0)
+ && (alted[0] == 0)
+ && (shiftalted[0] == 0))) {
+ continue;
+ }
+ if (scancode >= 256) {
+ fprintf(stderr,
+ "Error: scancode 0x%02x for keynumber %d\n", scancode,
+ keynumber);
+ break;
+ }
+ if (Hits[scancode].hits.hit[0].ctlrfcn != undefined) {
+ fprintf(stderr,
+ "Error: duplicate scancode 0x%02x for keynumber %d\n",
+ scancode, keynumber);
+ break;
+ }
+ hit = Hits[scancode].hits.hit;
+ Hits[scancode].hits.keynumber = keynumber;
+ Hits[scancode].name[0] = doit(hit, plain, &Hits[scancode]);
+ Hits[scancode].name[1] = doit(hit+1, shifted, &Hits[scancode]);
+ Hits[scancode].name[2] = doit(hit+2, alted, &Hits[scancode]);
+ Hits[scancode].name[3] = doit(hit+3, shiftalted, &Hits[scancode]);
+ }
+}
diff --git a/usr.bin/tn3270/tools/mkhits/dohits.h b/usr.bin/tn3270/tools/mkhits/dohits.h
new file mode 100644
index 0000000..9c26eca
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkhits/dohits.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)dohits.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define numberof(x) (sizeof x/sizeof x[0])
+#define highestof(x) (numberof(x)-1)
+
+#define firstentry(x) (table[dohash(0, (x))%highestof(table)])
+
+struct Hits {
+ struct hits hits;
+ char *name[4];
+};
+
+struct thing {
+ struct thing *next;
+ struct Hits *hits;
+ unsigned char value;
+ char name[100];
+};
+
+extern struct Hits Hits[256]; /* one for each of 0x00-0xff */
+extern struct thing *table[100];
+
+extern unsigned int dohash();
diff --git a/usr.bin/tn3270/tools/mkhits/mkhits.c b/usr.bin/tn3270/tools/mkhits/mkhits.c
new file mode 100644
index 0000000..0fafb8f
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkhits/mkhits.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkhits.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * This program scans a file which describes a keyboard. The output
+ * of the program is a series of 'C' declarations which describe a
+ * mapping between (scancode, shiftstate, altstate) and 3270 functions,
+ * characters, and AIDs.
+ *
+ * The format of the input file is as follows:
+ *
+ * keynumber [ scancode [ unshifted [ shifted [ alted [ shiftalted ] ] ] ] ]
+ *
+ * keynumber is in decimal, and starts in column 1.
+ * scancode is hexadecimal.
+ * unshifted, etc. - these are either a single ascii character,
+ * or the name of a function or an AID-generating key.
+ *
+ * all fields are separated by a single space.
+ */
+
+#include <stdio.h>
+#if defined(unix)
+#include <strings.h>
+#else /* defined(unix) */
+#include <string.h>
+#endif /* defined(unix) */
+#include <ctype.h>
+#include "../ctlr/function.h"
+
+#include "dohits.h"
+
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int scancode;
+ int empty;
+ int i;
+ struct hits *ph;
+ struct Hits *Ph;
+ char *aidfile = 0, *fcnfile = 0;
+
+ if (argc > 1) {
+ if (argv[1][0] != '-') {
+ aidfile = argv[1];
+ }
+ }
+ if (argc > 2) {
+ if (argv[2][0] != '-') {
+ fcnfile = argv[2];
+ }
+ }
+
+ dohits(aidfile, fcnfile); /* Set up "Hits" */
+
+ printf("struct hits hits[] = {\n");
+ empty = 0;
+ scancode = -1;
+ for (Ph = Hits; Ph < Hits+(sizeof Hits/sizeof Hits[0]); Ph++) {
+ ph = &Ph->hits;
+ scancode++;
+ if ((ph->hit[0].ctlrfcn == undefined)
+ && (ph->hit[1].ctlrfcn == undefined)
+ && (ph->hit[2].ctlrfcn == undefined)
+ && (ph->hit[3].ctlrfcn == undefined)) {
+ empty++;
+ continue;
+ } else {
+ while (empty) {
+ printf("\t{ 0, { {undefined}, {undefined}");
+ printf(", {undefined}, {undefined} } },\n");
+ empty--;
+ }
+ }
+ printf("\t{ %d, {\t/* 0x%02x */\n\t", ph->keynumber, scancode);
+ for (i = 0; i < 4; i++) {
+ printf("\t{ ");
+ switch (ph->hit[i].ctlrfcn) {
+ case undefined:
+ printf("undefined");
+ break;
+ case FCN_CHARACTER:
+ printf("FCN_CHARACTER, 0x%02x", ph->hit[i].code);
+ break;
+ case FCN_AID:
+ printf("FCN_AID, %s", Ph->name[i]);
+ break;
+ case FCN_NULL:
+ default:
+ if ((Ph->name[i] != 0)
+ && (strcmp(Ph->name[i], "FCN_NULL") != 0)) {
+ printf("%s", Ph->name[i]);
+ } else {
+ printf("undefined");
+ }
+ break;
+ }
+ printf(" },\n\t");
+ }
+ printf("} },\n");
+ }
+ printf("};\n");
+ return 0;
+}
diff --git a/usr.bin/tn3270/tools/mkmake.y b/usr.bin/tn3270/tools/mkmake.y
new file mode 100644
index 0000000..e9c2c6b
--- /dev/null
+++ b/usr.bin/tn3270/tools/mkmake.y
@@ -0,0 +1,1097 @@
+%{
+
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkmake.y 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+typedef struct string {
+ int
+ hashval,
+ length;
+ char
+ *string;
+ struct string
+ *next;
+} string_t;
+
+/*
+ * The deal with these is that they exist on various lists.
+ *
+ * First off, they are on a temporary list during the time they
+ * are in the active focus of the parser.
+ *
+ * Secondly, they live on one of three queues:
+ * 1. Variables
+ * 2. Targets
+ * 3. Actions
+ * (and, we restrict any given one to live on one and only one such list)
+ *
+ * Also, they may live on the list of values for someone else's variable,
+ * or as someone's dependancy.
+ */
+
+typedef struct same {
+ string_t
+ *string; /* My name */
+ struct same
+ *nexttoken, /* Next pointer */
+ *lasttoken, /* Back pointer */
+ *depend_list, /* If target, dependancies */
+ *action_list, /* If target, actions */
+ *value_list, /* If variable, value list */
+ *shell_item; /* If a shell variable, current value */
+} same_t;
+
+%}
+
+%union {
+ string_t *string;
+ same_t *same;
+ int intval;
+ }
+
+%start makefile
+%token <string> TOKEN QUOTED_STRING
+%token <intval> FOR IN DO DONE
+%token <intval> MACRO_CHAR NL WHITE_SPACE
+%token <intval> ':' '=' '$' '{' '}' ';' '-' '@' '(' ')' ' ' '\t'
+%type <same> target target1 assignment assign1 actions action
+%type <same> command_list list list_element
+%type <same> for_statement maybe_at_minus tokens token
+%type <same> maybe_white_space
+%type <intval> white_space macro_char
+%%
+
+makefile : lines;
+
+lines : line
+ | lines line
+ ;
+
+line : NL
+ | assignment
+ | target_action
+ ;
+
+assignment : assign1 tokens NL
+ {
+ assign($1, $2);
+ }
+ | assign1 NL
+ {
+ assign($1, same_copy(null));
+ }
+ ;
+
+assign1: token maybe_white_space '=' maybe_white_space
+ ;
+
+target_action: target actions
+ {
+ add_targets_actions($1, $2);
+ }
+ | target
+ {
+ add_targets_actions($1, 0);
+ }
+ ;
+
+target : target1 tokens NL
+ {
+ $$ = add_depends($1, $2);
+ }
+ | target1 NL
+ {
+ $$ = add_depends($1, same_copy(null));
+ }
+ ;
+
+target1: tokens maybe_white_space ':' maybe_white_space
+ {
+ $$ = ws_merge($1);
+ }
+ ;
+
+actions: action
+ | actions action
+ {
+ $$ = same_cat(same_cat($1, same_copy(newline)), $2);
+ }
+ ;
+
+action: white_space command_list NL
+ {
+ $$ = $2;
+ }
+ | white_space for_statement do command_list semi_colon done NL
+ {
+ $$ = do_command($2, $4);
+ }
+ ;
+
+for_statement: maybe_at_minus FOR white_space token
+ in tokens semi_colon
+ {
+ $$ = for_statement($1, $4, ws_merge(expand_variables($6, 0)));
+ }
+ ;
+
+in: white_space IN white_space
+do: white_space DO white_space
+ ;
+
+done: white_space DONE
+ ;
+
+semi_colon: ';'
+ ;
+
+command_list: list
+ | '(' list maybe_white_space ')'
+ {
+ $$ = same_cat($2, same_copy(cwd_line));
+ }
+ ;
+
+list: token
+ | list list_element
+ {
+ $$ = same_cat($1, $2);
+ }
+ | list white_space list_element
+ {
+ $$ = same_cat($1, same_cat(same_copy(blank), $3));
+ }
+ ;
+
+list_element: token
+ | semi_colon
+ {
+ $$ = same_copy(newline);
+ }
+ ;
+
+maybe_at_minus: /* empty */
+ {
+ $$ = same_copy(null);
+ }
+ | '@'
+ {
+ char buffer[2];
+
+ buffer[0] = $1;
+ buffer[1] = 0;
+ $$ = same_item(string_lookup(buffer));
+ }
+ | '-'
+ {
+ char buffer[2];
+
+ buffer[0] = $1;
+ buffer[1] = 0;
+ $$ = same_item(string_lookup(buffer));
+ }
+ ;
+
+tokens : token
+ | tokens maybe_white_space token
+ {
+ $$ = same_cat($1, same_cat($2, $3));
+ }
+ ;
+
+token: TOKEN
+ {
+ $$ = same_item($1);
+ }
+ | QUOTED_STRING
+ {
+ $$ = same_item($1);
+ }
+ | '$' macro_char
+ {
+ char buffer[3];
+
+ buffer[0] = '$';
+ buffer[1] = $2;
+ buffer[2] = 0;
+
+ $$ = same_item(string_lookup(buffer));
+ }
+ | '$' '$' TOKEN
+ {
+ $$ = shell_variable(same_item($3));
+ }
+ | MACRO_CHAR
+ {
+ $$ = same_char($1);
+ }
+ | '$' '{' TOKEN '}'
+ {
+ $$ = variable(same_item($3));
+ }
+ | '$' '(' TOKEN ')'
+ {
+ $$ = variable(same_item($3));
+ }
+ | '$' TOKEN
+ {
+ $$ = variable(same_item($2));
+ }
+ | '-'
+ {
+ $$ = same_char('-');
+ }
+ | '@'
+ {
+ $$ = same_char('@');
+ }
+ ;
+
+macro_char: MACRO_CHAR
+ | '@'
+ ;
+
+maybe_white_space:
+ {
+ $$ = same_copy(null);
+ }
+ | white_space
+ {
+ $$ = same_char($1);
+ }
+ ;
+
+white_space : WHITE_SPACE
+ | white_space WHITE_SPACE
+ ;
+%%
+#include <stdio.h>
+#include <ctype.h>
+
+static int last_char, last_saved = 0;
+static int column = 0, lineno = 1;
+
+
+static string_t
+ *strings = 0;
+
+static same_t
+ *shell_variables = 0,
+ *shell_special = 0,
+ *variables = 0,
+ *targets = 0,
+ *actions = 0;
+
+static same_t
+ *null,
+ *blank,
+ *cwd_line,
+ *newline;
+
+extern char *malloc();
+
+static unsigned int
+ clock = -1;
+
+struct {
+ same_t *first;
+ int next;
+} visit_stack[20]; /* 20 maximum */
+
+#define visit(what,via) \
+ (visit_stack[++clock].next = 0, visit_stack[clock].first = via = what)
+#define visited(via) (visitcheck(via) || ((via) == 0) \
+ || (visit_stack[clock].next && (via == visit_stack[clock].first)))
+#define visit_next(via) (visit_stack[clock].next = 1, (via) = (via)->nexttoken)
+#define visit_end() (clock--)
+
+yyerror(s)
+char *s;
+{
+ fprintf(stderr, "line %d, character %d: %s\n", lineno, column, s);
+ do_dump();
+}
+
+int
+visitcheck(same)
+same_t *same;
+{
+ if (same->string == 0) {
+ yyerror("BUG - freed 'same' in use...");
+ exit(1);
+ }
+ return 0;
+}
+
+int
+string_hashof(string, length)
+char *string;
+int length;
+{
+ register int i = 0;
+
+ while (length--) {
+ i = (i<<3) + *string ^ ((i>>28)&0x7);
+ }
+ return i;
+}
+
+int
+string_same(s1, s2)
+string_t
+ *s1, *s2;
+{
+ if ((s1->hashval == s2->hashval) && (s1->length == s2->length)
+ && (memcmp(s1->string, s2->string, s1->length) == 0)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+string_t *
+string_lookup(string)
+char *string;
+{
+ string_t ours;
+ string_t *ptr;
+
+ ours.length = strlen(string);
+ ours.hashval = string_hashof(string, ours.length);
+ ours.string = string;
+
+ for (ptr = strings; ptr; ptr = ptr->next) {
+ if (string_same(&ours, ptr)) {
+ return ptr;
+ }
+ }
+ if ((ptr = (string_t *)malloc(sizeof *ptr)) == 0) {
+ fprintf(stderr, "No space to add string *%s*!\n", string);
+ exit(1);
+ }
+ ptr->hashval = ours.hashval;
+ ptr->length = ours.length;
+ if ((ptr->string = malloc(ours.length+1)) == 0) {
+ fprintf(stderr, "No space to add literal *%s*!\n", string);
+ exit(1);
+ }
+ memcpy(ptr->string, string, ours.length+1);
+ ptr->next = strings;
+ strings = ptr;
+ return ptr;
+}
+
+#define same_singleton(s) ((s)->nexttoken == (s))
+
+same_t *
+same_search(list, token)
+same_t
+ *list,
+ *token;
+{
+ same_t *ptr;
+
+ ptr = list;
+ for (visit(list, ptr); !visited(ptr); visit_next(ptr)) {
+ string_t *string;
+
+ string = ptr->string;
+ if (string_same(string, token->string)) {
+ visit_end();
+ return ptr;
+ }
+ }
+ visit_end();
+ return 0;
+}
+
+same_t *
+same_cat(list, tokens)
+same_t
+ *list,
+ *tokens;
+{
+ same_t *last;
+
+ if (tokens == 0) {
+ return list;
+ }
+ if (list) {
+ last = tokens->lasttoken;
+ tokens->lasttoken = list->lasttoken;
+ list->lasttoken = last;
+ tokens->lasttoken->nexttoken = tokens;
+ last->nexttoken = list;
+ return list;
+ } else {
+ return tokens;
+ }
+}
+
+same_t *
+same_item(string)
+string_t *string;
+{
+ same_t *ptr;
+
+ if ((ptr = (same_t *)malloc(sizeof *ptr)) == 0) {
+ fprintf(stderr, "No more space for tokens!\n");
+ exit(1);
+ }
+ memset((char *)ptr, 0, sizeof *ptr);
+ ptr->nexttoken = ptr->lasttoken = ptr;
+ ptr->string = string;
+ return ptr;
+}
+
+same_t *
+same_copy(same)
+same_t *same;
+{
+ same_t *head, *copy;
+
+ head = 0;
+ for (visit(same, copy); !visited(copy); visit_next(copy)) {
+ same_t *ptr;
+
+ ptr = same_item(copy->string);
+ head = same_cat(head, ptr);
+ }
+ visit_end();
+ return head;
+}
+
+
+same_t *
+same_merge(t1, t2)
+same_t
+ *t1,
+ *t2;
+{
+ if (same_singleton(t1) && same_singleton(t2)) {
+ int length = strlen(t1->string->string)+strlen(t2->string->string);
+ char *buffer = malloc(length+1);
+ same_t *value;
+
+ if (buffer == 0) {
+ yyerror("No space to merge strings in same_merge!");
+ exit(1);
+ }
+ strcpy(buffer, t1->string->string);
+ strcat(buffer, t2->string->string);
+ value = same_item(string_lookup(buffer));
+ free(buffer);
+ return value;
+ } else {
+ yyerror("Internal error - same_merge with non-singletons");
+ exit(1);
+ }
+}
+
+
+void
+same_free(list)
+same_t *list;
+{
+ same_t *token, *ptr;
+
+ if (list == 0) {
+ return;
+ }
+
+ token = list;
+ do {
+ ptr = token->nexttoken;
+ token->string = 0;
+ (void) free((char *)token);
+ token = ptr;
+ } while (token != list);
+}
+
+same_t *
+same_unlink(token)
+same_t
+ *token;
+{
+ same_t *tmp;
+
+ if (token == 0) {
+ return 0;
+ }
+ if ((tmp = token->nexttoken) == token) {
+ tmp = 0;
+ }
+ token->lasttoken->nexttoken = token->nexttoken;
+ token->nexttoken->lasttoken = token->lasttoken;
+ token->nexttoken = token->lasttoken = token;
+ return tmp;
+}
+
+void
+same_replace(old, new)
+same_t
+ *old,
+ *new;
+{
+ new->lasttoken->nexttoken = old->nexttoken;
+ old->nexttoken->lasttoken = new->lasttoken;
+ new->lasttoken = old->lasttoken;
+ /* rather than
+ * old->lasttoken->nexttoken = new
+ * we update in place (for the case where there isn't anything else)
+ */
+ *old = *new;
+}
+
+
+same_t *
+same_char(ch)
+char ch;
+{
+ char buffer[2];
+
+ buffer[0] = ch;
+ buffer[1] = 0;
+
+ return same_item(string_lookup(buffer));
+}
+
+
+void
+add_target(target, actions)
+same_t
+ *target,
+ *actions;
+{
+ same_t *ptr;
+
+ if ((ptr = same_search(targets, target)) == 0) {
+ targets = same_cat(targets, target);
+ ptr = target;
+ } else {
+ ptr->depend_list = same_cat(ptr->depend_list, target->depend_list);
+ }
+ if (actions) {
+ if (ptr->action_list) {
+ same_free(ptr->action_list);
+ }
+ ptr->action_list = same_copy(actions);
+ }
+}
+
+
+same_t *
+add_targets_actions(target, actions)
+same_t
+ *target,
+ *actions;
+{
+ same_t *ptr;
+
+ if (target == 0) {
+ return 0;
+ }
+ do {
+ ptr = same_unlink(target);
+ add_target(target, actions);
+ target = ptr;
+ } while (target);
+
+ same_free(actions);
+ return 0;
+}
+
+same_t *
+add_depends(target, depends)
+same_t
+ *target,
+ *depends;
+{
+ same_t *original = target;
+
+ depends = same_cat(depends, same_copy(blank)); /* Separator */
+
+ for (visit(original, target); !visited(target); visit_next(target)) {
+ target->depend_list = same_cat(target->depend_list, same_copy(depends));
+ }
+ visit_end();
+ same_free(depends);
+
+ return original;
+}
+
+
+/*
+ * We know that variable is a singleton
+ */
+
+void
+assign(variable, value)
+same_t
+ *variable,
+ *value;
+{
+ same_t *ptr;
+
+ if ((ptr = same_search(variables, variable)) != 0) {
+ same_free(ptr->value_list);
+ variables = same_unlink(ptr);
+ same_free(ptr);
+ }
+ variable->value_list = value;
+ variables = same_cat(variables, variable);
+}
+
+same_t *
+value_of(variable)
+same_t *variable;
+{
+ same_t *ptr = same_search(variables, variable);
+
+ if (ptr == 0) {
+ return same_copy(null);
+ } else {
+ return same_copy(ptr->value_list);
+ }
+}
+
+
+same_t *
+expand_variables(token, free)
+same_t *token;
+int free;
+{
+ same_t *head = 0;
+
+ if (!free) {
+ token = same_copy(token); /* Get our private copy */
+ }
+
+ while (token) {
+ char *string = token->string->string;
+ same_t *tmp = same_unlink(token);
+
+ if ((string[0] == '$') && (string[1] == '{')) { /* Expand time */
+ int len = strlen(string);
+
+ string[len-1] = 0;
+ head = same_cat(head, expand_variables(
+ value_of(same_item(string_lookup(string+2))), 1));
+ string[len-1] = '}';
+ } else {
+ head = same_cat(head, token);
+ }
+ token = tmp;
+ }
+ return head;
+}
+
+
+same_t *
+ws_merge(list)
+same_t *list;
+{
+ same_t *newlist = 0, *item;
+ int what = 0;
+
+ while (list) {
+ switch (what) {
+ case 0:
+ if (isspace(list->string->string[0])) {
+ ;
+ } else {
+ item = same_item(list->string);
+ what = 1;
+ }
+ break;
+ case 1:
+ if (isspace(list->string->string[0])) {
+ newlist = same_cat(newlist, item);
+ item = 0;
+ what = 0;
+ } else {
+ item = same_merge(item, same_item(list->string));
+ what = 1;
+ }
+ break;
+ }
+ list = same_unlink(list);
+ }
+ return same_cat(newlist, item);
+}
+
+
+same_t *
+variable(var_name)
+same_t *var_name;
+{
+ int length = strlen(var_name->string->string);
+ same_t *resolved;
+ char *newname;
+
+ if ((newname = malloc(length+1+3)) == 0) {
+ fprintf("Out of space for a variable name.\n");
+ exit(1);
+ }
+ newname[0] = '$';
+ newname[1] = '{';
+ strcpy(newname+2, var_name->string->string);
+ strcat(newname, "}");
+ resolved = same_item(string_lookup(newname));
+ free(newname);
+
+ return resolved;
+}
+
+
+same_t *
+shell_variable(var_name)
+same_t *var_name;
+{
+ int length = strlen(var_name->string->string);
+ same_t *resolved;
+ char *newname;
+
+ if ((newname = malloc(length+1+2)) == 0) {
+ fprintf("Out of space for a variable name.\n");
+ exit(1);
+ }
+ newname[0] = '$';
+ newname[1] = '$';
+ strcpy(newname+2, var_name->string->string);
+ resolved = same_item(string_lookup(newname));
+ free(newname);
+
+ return resolved;
+}
+
+same_t *
+for_statement(special, variable, list)
+same_t
+ *special,
+ *variable,
+ *list;
+{
+ variable->shell_item = special;
+ variable->value_list = list;
+ return variable;
+}
+
+same_t *
+do_command(forlist, commands)
+same_t
+ *forlist,
+ *commands;
+{
+ same_t
+ *special,
+ *command_list = 0,
+ *new_commands,
+ *tmp,
+ *shell_item,
+ *value_list = forlist->value_list;
+ char
+ *tmpstr,
+ *variable_name = forlist->string->string;
+
+ special = forlist->shell_item;
+ if (same_unlink(forlist->shell_item) != 0) {
+ yyerror("Unexpected second item in special part of do_command");
+ exit(1);
+ }
+
+ while ((shell_item = value_list) != 0) {
+ value_list = same_unlink(shell_item);
+ /* Visit each item in commands. For each shell variable which
+ * matches ours, replace it with ours.
+ */
+ new_commands = same_copy(commands);
+ for (visit(new_commands, tmp); !visited(tmp); visit_next(tmp)) {
+ tmpstr = tmp->string->string;
+ if ((tmpstr[0] == '$') && (tmpstr[1] == '$')) {
+ if (strcmp(tmpstr+2, variable_name) == 0) {
+ same_replace(tmp, same_copy(shell_item));
+ }
+ }
+ }
+ visit_end();
+ command_list = same_cat(command_list, new_commands);
+ }
+ return same_cat(command_list, same_copy(newline));
+}
+
+
+int
+Getchar()
+{
+ if (last_saved) {
+ last_saved = 0;
+ return last_char;
+ } else {
+ int c;
+ c = getchar();
+ switch (c) {
+ case '\n':
+ lineno++;
+ column = 0;
+ break;
+ default:
+ column++;
+ }
+ return c;
+ }
+}
+
+
+int
+token_type(string)
+char *string;
+{
+ switch (string[0]) {
+ case 'f':
+ if (strcmp(string, "for") == 0) {
+ return FOR;
+ }
+ break;
+ case 'd':
+ if (string[1] == 'o') {
+ if (strcmp(string, "do") == 0) {
+ return DO;
+ } else if (strcmp(string, "done") == 0) {
+ return DONE;
+ }
+ }
+ break;
+ case 'i':
+ if (strcmp(string, "in") == 0) {
+ return IN;
+ }
+ break;
+ default:
+ break;
+ }
+ return TOKEN;
+}
+
+
+yylex()
+{
+#define ret_token(c) if (bufptr != buffer) { \
+ save(c); \
+ *bufptr = 0; \
+ bufptr = buffer; \
+ yylval.string = string_lookup(buffer); \
+ return token_type(buffer); \
+ }
+#define save(c) { last_char = c; last_saved = 1; }
+#if defined(YYDEBUG)
+#define Return(c) if (yydebug) { \
+ printf("[%d]", c); \
+ fflush(stdout); \
+ } \
+ yyval.intval = c; \
+ return c;
+#else /* defined(YYDEBUG) */
+#define Return(y,c) { yylval.intval = c; return y; }
+#endif /* defined(YYDEBUG) */
+
+
+ static char buffer[500], *bufptr = buffer;
+ static int eof_found = 0;
+ int c;
+
+ if (eof_found != 0) {
+ eof_found++;
+ if (eof_found > 2) {
+ fprintf(stderr, "End of file ignored.\n");
+ exit(1);
+ }
+ Return(EOF,0);
+ }
+ while ((c = Getchar()) != EOF) {
+ switch (c) {
+ case '#':
+ ret_token(c);
+ while (((c = Getchar()) != EOF) && (c != '\n')) {
+ ;
+ }
+ save(c);
+ break;
+ case '<':
+ case '?':
+ ret_token(c);
+ Return(MACRO_CHAR, c);
+ case '\t':
+ case ' ':
+ ret_token(c);
+ Return(WHITE_SPACE, c);
+ case '-':
+ case '@':
+ case ':':
+ case ';':
+ case '=':
+ case '$':
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ ret_token(c);
+ Return(c,c);
+ case '\'':
+ case '"':
+ if (bufptr != buffer) {
+ if (bufptr[-1] == '\\') {
+ bufptr[-1] = c;
+ }
+ break;
+ } else {
+ int newc;
+
+ ret_token(c);
+ *bufptr++ = c;
+ while (((newc = Getchar()) != EOF) && (newc != c)) {
+ *bufptr++ = newc;
+ }
+ *bufptr++ = c;
+ *bufptr = 0;
+ bufptr = buffer;
+ yylval.string = string_lookup(buffer);
+ return QUOTED_STRING;
+ }
+ case '\n':
+ if (bufptr != buffer) {
+ if (bufptr[-1] == '\\') {
+ bufptr--;
+ if ((c = Getchar()) != '\t') {
+ yyerror("continuation line doesn't begin with a tab");
+ save(c);
+ }
+ ret_token(c);
+ Return(WHITE_SPACE, c);
+ }
+ }
+ ret_token(c);
+ Return(NL, 0);
+ default:
+ *bufptr++ = c;
+ break;
+ }
+ }
+
+ eof_found = 1;
+
+ ret_token(' ');
+ Return(EOF, 0);
+}
+
+main()
+{
+#define YYDEBUG
+ extern int yydebug;
+
+ null = same_item(string_lookup(""));
+ newline = same_item(string_lookup("\n"));
+ blank = same_item(string_lookup(" "));
+ cwd_line = same_cat(same_copy(newline),
+ same_cat(same_item(string_lookup("cd ${CWD}")),
+ same_copy(newline)));
+
+ yyparse();
+
+ do_dump();
+
+ return 0;
+}
+
+#if defined(YYDEBUG)
+dump_same(same)
+same_t *same;
+{
+ same_t *same2;
+
+ for (visit(same, same2); !visited(same2); visit_next(same2)) {
+ printf(same2->string->string);
+ }
+ visit_end();
+}
+#endif /* YYDEBUG */
+
+do_dump()
+{
+ string_t *string;
+ same_t *same, *same2;
+
+ if (yydebug > 1) {
+ printf("strings...\n");
+ for (string = strings; string; string = string->next) {
+ printf("\t%s\n", string->string);
+ }
+ }
+
+ printf("# variables...\n");
+ for (visit(variables, same); !visited(same); visit_next(same)) {
+ printf("%s =\t", same->string->string);
+ for (visit(same->value_list, same2); !visited(same2);
+ visit_next(same2)) {
+ printf(same2->string->string);
+ }
+ visit_end();
+ printf("\n");
+ }
+ visit_end();
+
+ printf("\n\n#targets...\n");
+ for (visit(targets, same); !visited(same); visit_next(same)) {
+ printf("\n%s:\t", same->string->string);
+ for (visit(same->depend_list, same2); !visited(same2);
+ visit_next(same2)) {
+ printf(same2->string->string);
+ }
+ visit_end();
+ printf("\n\t");
+ for (visit(same->action_list, same2); !visited(same2);
+ visit_next(same2)) {
+ printf(same2->string->string);
+ if (same2->string->string[0] == '\n') {
+ printf("\t");
+ }
+ }
+ visit_end();
+ printf("\n");
+ }
+ visit_end();
+}
diff --git a/usr.bin/tn3270/tools/prt3270.c b/usr.bin/tn3270/tools/prt3270.c
new file mode 100644
index 0000000..55d72d5
--- /dev/null
+++ b/usr.bin/tn3270/tools/prt3270.c
@@ -0,0 +1,620 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)prt3270.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#if defined(unix)
+#endif
+#include <stdio.h>
+#include <ctype.h>
+
+#include "../general/general.h"
+
+#include "../api/asc_ebc.h"
+#include "../ctlr/hostctlr.h"
+#include "../ctlr/screen.h"
+#include "../ctlr/function.h"
+#include "../api/astosc.h"
+#include "../general/globals.h"
+
+#include "../ctlr/kbd.out"
+
+
+int NumberColumns = 80;
+
+int direction;
+
+int column = 1;
+int indenting = 0;
+int direction = '?';
+
+unsigned char printBuffer[200], *print = printBuffer;
+
+#define ColsLeft() (79-column) /* A little room for error */
+
+
+void
+putSpace()
+{
+ extern void Column1();
+ unsigned char *ourPrint = print;
+
+ print = printBuffer; /* For mutual calls */
+ *ourPrint = 0;
+ if (ColsLeft() < 0) {
+ Column1();
+ }
+ if (column != (indenting*8+1)) {
+ putchar(' ');
+ } else {
+ int i;
+
+ putchar(direction);
+ putchar(' ');
+ for (i = 0; i < indenting; i++) {
+ putchar('\t');
+ }
+ }
+ printf("%s", printBuffer);
+ column += strlen(printBuffer);
+}
+
+void
+Column1()
+{
+ if (print != printBuffer) {
+ putSpace();
+ }
+ if (column != (indenting*8+1)) {
+ putchar('\n');
+ column = indenting*8+1;
+ }
+}
+
+void
+Indent()
+{
+ if ((column != (indenting*8+1)) || (print != printBuffer)) {
+ Column1();
+ }
+ indenting++;
+ column = indenting*8+1;
+}
+
+void
+Undent()
+{
+ if ((column != (indenting*8+1)) || (print != printBuffer)) {
+ Column1();
+ }
+ indenting--;
+ if (indenting < 0) {
+ fflush(stdout);
+ fprintf(stderr, "INTERNAL ERROR: indenting < 0.\n");
+ fflush(stderr);
+ } else {
+ column = indenting*8+1;
+ }
+}
+
+void
+putChar(character)
+int character;
+{
+ *print++ = character;
+ column++;
+}
+
+void
+putstr(s)
+char *s;
+{
+ while (*s) {
+ putChar(*s++);
+ }
+}
+
+void
+put2hex(i)
+int i;
+{
+ char place[40];
+
+ sprintf(place, "%02x", i);
+ putstr(place);
+}
+
+
+void
+putdecimal(i)
+int i;
+{
+ char place[40];
+
+ sprintf(place, "%d", i);
+ putstr(place);
+}
+
+void
+puthex(i)
+int i;
+{
+ char place[40];
+
+ sprintf(place, "%x", i);
+ putstr(place);
+}
+
+void
+putEChar(character)
+int character;
+{
+ putChar(ebc_asc[character]);
+ if (ColsLeft() < 10) {
+ Column1();
+ }
+}
+
+void
+PrintAid(i)
+int i;
+{
+ struct astosc *this;
+
+ for (this = &astosc[0]; this <= &astosc[highestof(astosc)]; this++) {
+ if (this->function == FCN_AID) {
+ int j;
+
+ switch (this->shiftstate) {
+ case 0:
+ j = 0;
+ break;
+ case SHIFT_UPSHIFT:
+ j = 1;
+ break;
+ case SHIFT_ALT:
+ j = 2;
+ break;
+ case (SHIFT_UPSHIFT|SHIFT_ALT):
+ j = 3;
+ break;
+ default:
+ fprintf(stderr, "Bad shiftstate 0x%x.\n", this->shiftstate);
+ exit(1);
+ }
+ if (hits[this->scancode].hit[j].code == i) {
+ putstr(this->name);
+ return;
+ }
+ }
+ }
+
+ putstr("Unknown AID 0x");
+ put2hex(i);
+}
+
+void
+PrintAddr(i)
+int i;
+{
+ if (ColsLeft() < 9) {
+ Column1();
+ }
+ putChar('(');
+ putdecimal(ScreenLine(i));
+ putChar(',');
+ putdecimal(ScreenLineOffset(i));
+ putChar(')');
+}
+
+
+/* returns the number of characters consumed */
+int
+DataFromNetwork(buffer, count, control)
+register unsigned char *buffer; /* what the data is */
+register int count; /* and how much there is */
+int control; /* this buffer ended block? */
+{
+ int origCount;
+ register int c;
+ register int i;
+ static int Command;
+ static int Wcc;
+ static int LastWasTerminated = 1; /* was "control" = 1 last time? */
+
+ if (count == 0) {
+ Column1();
+ return 0;
+ }
+
+ origCount = count;
+
+ if (LastWasTerminated) {
+
+ if (count < 2) {
+ if (count == 0) {
+ fflush(stdout);
+ fprintf(stderr, "Short count received from host!\n");
+ fflush(stderr);
+ return(count);
+ }
+ Command = buffer[0];
+ switch (Command) { /* This had better be a read command */
+ case CMD_READ_MODIFIED:
+ putstr("read_modified command\n");
+ break;
+ case CMD_SNA_READ_MODIFIED:
+ putstr("sna_read_modified command\n");
+ break;
+ case CMD_SNA_READ_MODIFIED_ALL:
+ putstr("sna_read_modified_all command\n");
+ break;
+ case CMD_READ_BUFFER:
+ putstr("read_buffer command\n");
+ break;
+ case CMD_SNA_READ_BUFFER:
+ putstr("sna_read_buffer command\n");
+ break;
+ default:
+ break;
+ }
+ return(1); /* We consumed everything */
+ }
+ Command = buffer[0];
+ Wcc = buffer[1];
+ switch (Command) {
+ case CMD_ERASE_WRITE:
+ putstr("erase write command ");
+ break;
+ case CMD_ERASE_WRITE_ALTERNATE:
+ putstr("erase write alternate command ");
+ break;
+ case CMD_SNA_ERASE_WRITE:
+ putstr("sna erase write command ");
+ break;
+ case CMD_SNA_ERASE_WRITE_ALTERNATE:
+ putstr("erase write alternate command ");
+ break;
+ case CMD_ERASE_ALL_UNPROTECTED:
+ putstr("erase all unprotected command ");
+ break;
+ case CMD_SNA_ERASE_ALL_UNPROTECTED:
+ putstr("sna erase write command ");
+ break;
+ case CMD_WRITE:
+ putstr("write command ");
+ break;
+ case CMD_SNA_WRITE:
+ putstr("sna write command ");
+ break;
+ default:
+ putstr("Unexpected command code 0x");
+ puthex(Command);
+ putstr(" received.");
+ Column1();
+ break;
+ }
+ putstr("WCC is 0x");
+ puthex(Wcc);
+ Column1();
+
+ count -= 2; /* strip off command and wcc */
+ buffer += 2;
+
+ }
+ LastWasTerminated = 0; /* then, reset at end... */
+
+ while (count) {
+ count--;
+ c = *buffer++;
+ if (IsOrder(c)) {
+ /* handle an order */
+ switch (c) {
+# define Ensure(x) if (count < x) { \
+ if (!control) { \
+ return(origCount-(count+1)); \
+ } else { \
+ /* XXX - should not occur */ \
+ count = 0; \
+ break; \
+ } \
+ }
+ case ORDER_SF:
+ Ensure(1);
+ c = *buffer++;
+ count--;
+ putstr("SF (0x");
+ put2hex(c);
+ putstr(") ");
+ break;
+ case ORDER_SBA:
+ Ensure(2);
+ i = buffer[0];
+ c = buffer[1];
+ buffer += 2;
+ count -= 2;
+ putstr("SBA to ");
+ PrintAddr(Addr3270(i,c));
+ putSpace();
+ break;
+ case ORDER_IC:
+ putstr("IC");
+ putSpace();
+ break;
+ case ORDER_PT:
+ putstr("PT");
+ putSpace();
+ break;
+ case ORDER_RA:
+ Ensure(3);
+ i = Addr3270(buffer[0], buffer[1]);
+ c = buffer[2];
+ buffer += 3;
+ count -= 3;
+ putstr("RA to ");
+ PrintAddr(i);
+ putstr(" of 0x");
+ put2hex(c);
+ putSpace();
+ break;
+ case ORDER_EUA: /* (from [here,there), ie: half open interval] */
+ Ensure(2);
+ putstr("EUA to ");
+ PrintAddr(Addr3270(buffer[0], buffer[1]));
+ putSpace();
+ buffer += 2;
+ count -= 2;
+ break;
+ case ORDER_YALE: /* special YALE defined order */
+ Ensure(2); /* need at least two characters */
+ putstr("YALE order");
+ putSpace();
+ break;
+ default:
+ putstr("UNKNOWN ORDER: 0x");
+ put2hex(c);
+ putSpace();
+ break;
+ }
+ if (count < 0) {
+ count = 0;
+ }
+ } else {
+ /* Data comes in large clumps - take it all */
+ putstr("DATA:");
+ Indent();
+ putEChar(c);
+ c = *buffer;
+ while (count && !IsOrder(c)) {
+ putEChar(c);
+ count--;
+ buffer++;
+ c = *buffer;
+ }
+ Undent();
+ }
+ }
+ LastWasTerminated = control;
+ return origCount - count;
+}
+
+int
+DataToNetwork(buffer, count, control)
+unsigned char *buffer;
+int count;
+int control;
+{
+#define NEED_AID 0
+#define JUST_GOT_AID 1
+#define DATA 2
+#define DATA_CONTINUE 3
+ static int state = NEED_AID;
+ static int aid;
+ int origCount = count;
+
+ if (count == 0) {
+ if (control) {
+ state = NEED_AID;
+ }
+ Column1();
+ return 0;
+ }
+
+ switch (state) {
+ case NEED_AID:
+ aid = buffer[0];
+ buffer++;
+ count--;
+ PrintAid(aid);
+ putSpace();
+ if (aid == AID_TREQ) {
+ state = DATA;
+ } else {
+ state = JUST_GOT_AID;
+ }
+ return origCount - count + DataToNetwork(buffer, count, control);
+ case JUST_GOT_AID:
+ Ensure(2);
+ PrintAddr(Addr3270(buffer[0], buffer[1]));
+ putSpace();
+ buffer += 2;
+ count -= 2;
+ state = DATA;
+ return origCount - count + DataToNetwork(buffer, count, control);
+ case DATA:
+ case DATA_CONTINUE:
+ while (count) {
+ if (*buffer == ORDER_SBA) {
+ if (state == DATA_CONTINUE) {
+ Undent();
+ state = DATA;
+ }
+ putstr("SBA ");
+ PrintAddr(Addr3270(buffer[1], buffer[2]));
+ putSpace();
+ buffer += 3;
+ count -= 3;
+ } else {
+ if (state == DATA) {
+ putstr("DATA:");
+ Indent();
+ state = DATA_CONTINUE;
+ }
+ putEChar(*buffer);
+ buffer++;
+ count--;
+ }
+ }
+ if (control) {
+ if (state == DATA_CONTINUE) {
+ Undent();
+ }
+ state = NEED_AID;
+ }
+ return origCount-count;
+ }
+}
+
+int
+GetXValue(c)
+int c;
+{
+ if (!isascii(c)) {
+ fflush(stdout);
+ fprintf(stderr, "Non-hex digit 0x%x.\n");
+ fflush(stderr);
+ return 0;
+ } else {
+ if (islower(c)) {
+ return (c-'a')+10;
+ } else if (isupper(c)) {
+ return (c-'A')+10;
+ } else {
+ return c-'0';
+ }
+ }
+}
+
+unsigned char outbound[8192], inbound[8192],
+ *outnext = outbound, *innext = inbound, *p = 0;
+
+void
+termblock(old, new, control)
+int old,
+ new; /* old and new directions */
+{
+ int count;
+
+ if (p) {
+ if (old == '<') {
+ outnext = p;
+ count = DataFromNetwork(outbound, outnext-outbound, control);
+ if (outbound+count == outnext) {
+ outnext = outbound;
+ } else {
+ memcpy(outbound, outbound+count, outnext-(outbound+count));
+ outnext = outbound+count;
+ }
+ } else {
+ innext = p;
+ count = DataToNetwork(inbound, innext-inbound, control);
+ if (inbound+count == innext) {
+ innext = inbound;
+ } else {
+ memcpy(inbound, inbound+count, innext-(inbound+count));
+ innext = inbound+count;
+ }
+ }
+ }
+ if (new == '<') {
+ p = outnext;
+ } else if (new == '>') {
+ p = innext;
+ } else {
+ fprintf(stderr, "Bad direction character '%c'.\n", new);
+ exit(1);
+ }
+}
+
+main()
+{
+ int location;
+ char new;
+ int c, c1;
+
+ memset(Orders, 0, sizeof Orders);
+ Orders[ORDER_SF] = Orders[ORDER_SBA] = Orders[ORDER_IC]
+ = Orders[ORDER_PT] = Orders[ORDER_RA] = Orders[ORDER_EUA]
+ = Orders[ORDER_YALE] = 1;
+
+ while (scanf("%c 0x%x\t", &new, &location) != EOF) {
+ if (new != direction) {
+ termblock(direction, new, 0);
+ direction = new;
+ }
+ while (((c = getchar()) != EOF) && (c != '\n') && (isxdigit(c))) {
+#define NORMAL 0
+#define GOT0XFF 0xff
+ static int state = NORMAL;
+
+ c1 = getchar();
+ c = (GetXValue(c) << 4) + GetXValue(c1);
+ switch (state) {
+ case NORMAL:
+ if (c == 0xff) {
+ state = GOT0XFF;
+ } else {
+ *p++ = c;
+ }
+ break;
+ case GOT0XFF:
+ if (c == 0xef) {
+ termblock(direction, direction, 1);
+ } else {
+ *p++ = 0xff;
+ *p++ = c;
+ }
+ state = NORMAL;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/usr.bin/touch/Makefile b/usr.bin/touch/Makefile
new file mode 100644
index 0000000..5c153b3
--- /dev/null
+++ b/usr.bin/touch/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= touch
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/touch/touch.1 b/usr.bin/touch/touch.1
new file mode 100644
index 0000000..c6eeaa3
--- /dev/null
+++ b/usr.bin/touch/touch.1
@@ -0,0 +1,167 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)touch.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt TOUCH 1
+.Os
+.Sh NAME
+.Nm touch
+.Nd change file access and modification times
+.Sh SYNOPSIS
+.Nm touch
+.Op Fl acfm
+.Op Fl r Ar file
+.Op Fl t [[CC]YY]MMDDhhmm[.SS]
+.Ar file ...
+.Sh DESCRIPTION
+The
+.Nm touch
+utility sets the modification and access times of files to the
+current time of day.
+If the file doesn't exist, it is created with default permissions.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl a
+Change the access time of the file.
+The modification time of the file is not changed unless the
+.Fl m
+flag is also specified.
+.It Fl c
+Do not create the file if it does not exist.
+The
+.Nm touch
+utility does not treat this as an error.
+No error messages are displayed and the exit value is not affected.
+.It Fl f
+Attempt to force the update, even if the file permissions do not
+currently permit it.
+.It Fl m
+Change the modification time of the file.
+The access time of the file is not changed unless the
+.Fl a
+flag is also specified.
+.It Fl r
+Use the access and modifications times from the specified file
+instead of the current time of day.
+.It Fl t
+Change the access and modification times to the specified time.
+The argument should be in the form
+.Dq [[CC]YY]MMDDhhmm[.SS]
+where each pair of letters represents the following:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar YY
+The second two digits of the year.
+If
+.Dq YY
+is specified, but
+.Dq CC
+is not, a value for
+.Dq YY
+between 69 and 99 results in a
+.Dq YY
+value of 19.
+Otherwise, a
+.Dq YY
+value of 20 is used.
+.It Ar MM
+The month of the year, from 1 to 12.
+.It Ar DD
+the day of the month, from 1 to 31.
+.It Ar hh
+The hour of the day, from 0 to 23.
+.It Ar mm
+The minute of the hour, from 0 to 59.
+.It Ar SS
+The second of the minute, from 0 to 61.
+.El
+.Pp
+If the
+.Dq CC
+and
+.Dq YY
+letter pairs are not specified, the values default to the current
+year.
+If the
+.Dq SS
+letter pair is not specified, the value defaults to 0.
+.El
+.Pp
+The
+.Nm touch
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr utimes 2
+.Sh COMPATIBILITY
+The obsolescent form of
+.Nm touch ,
+where a time format is specified as the first argument, is supported.
+When no
+.Fl r
+or
+.Fl t
+option is specified, there are at least two arguments, and the first
+argument is a string of digits either eight or ten characters in length,
+the first argument is interpreted as a time specification of the form
+.Dq MMDDhhmm[YY] .
+.Pp
+The
+.Dq MM ,
+.Dq DD ,
+.Dq hh
+and
+.Dq mm
+letter pairs are treated as their counterparts specified to the
+.Fl t
+option.
+If the
+.Dq YY
+letter pair is in the range 69 to 99, the year is set to 1969 to 1999,
+otherwise, the year is set in the 21st century.
+.Sh HISTORY
+A
+.Nm touch
+command appeared in
+.At v7 .
+.Sh STANDARDS
+The
+.Nm touch
+function is expected to be a superset of the
+.St -p1003.2
+specification.
diff --git a/usr.bin/touch/touch.c b/usr.bin/touch/touch.c
new file mode 100644
index 0000000..d1de0bc
--- /dev/null
+++ b/usr.bin/touch/touch.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+int rw __P((char *, struct stat *, int));
+void stime_arg1 __P((char *, struct timeval *));
+void stime_arg2 __P((char *, int, struct timeval *));
+void stime_file __P((char *, struct timeval *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ struct timeval tv[2];
+ int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
+ char *p;
+
+ aflag = cflag = fflag = mflag = timeset = 0;
+ if (gettimeofday(&tv[0], NULL))
+ err(1, "gettimeofday");
+
+ while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF)
+ switch(ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'm':
+ mflag = 1;
+ break;
+ case 'r':
+ timeset = 1;
+ stime_file(optarg, tv);
+ break;
+ case 't':
+ timeset = 1;
+ stime_arg1(optarg, tv);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Default is both -a and -m. */
+ if (aflag == 0 && mflag == 0)
+ aflag = mflag = 1;
+
+ /*
+ * If no -r or -t flag, at least two operands, the first of which
+ * is an 8 or 10 digit number, use the obsolete time specification.
+ */
+ if (!timeset && argc > 1) {
+ (void)strtol(argv[0], &p, 10);
+ len = p - argv[0];
+ if (*p == '\0' && (len == 8 || len == 10)) {
+ timeset = 1;
+ stime_arg2(*argv++, len == 10, tv);
+ }
+ }
+
+ /* Otherwise use the current time of day. */
+ if (!timeset)
+ tv[1] = tv[0];
+
+ if (*argv == NULL)
+ usage();
+
+ for (rval = 0; *argv; ++argv) {
+ /* See if the file exists. */
+ if (stat(*argv, &sb))
+ if (!cflag) {
+ /* Create the file. */
+ fd = open(*argv,
+ O_WRONLY | O_CREAT, DEFFILEMODE);
+ if (fd == -1 || fstat(fd, &sb) || close(fd)) {
+ rval = 1;
+ warn("%s", *argv);
+ continue;
+ }
+
+ /* If using the current time, we're done. */
+ if (!timeset)
+ continue;
+ } else
+ continue;
+
+ if (!aflag)
+ TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
+ if (!mflag)
+ TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
+
+ /* Try utimes(2). */
+ if (!utimes(*argv, tv))
+ continue;
+
+ /* If the user specified a time, nothing else we can do. */
+ if (timeset) {
+ rval = 1;
+ warn("%s", *argv);
+ }
+
+ /*
+ * System V and POSIX 1003.1 require that a NULL argument
+ * set the access/modification times to the current time.
+ * The permission checks are different, too, in that the
+ * ability to write the file is sufficient. Take a shot.
+ */
+ if (!utimes(*argv, NULL))
+ continue;
+
+ /* Try reading/writing. */
+ if (rw(*argv, &sb, fflag))
+ rval = 1;
+ }
+ exit(rval);
+}
+
+#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
+
+void
+stime_arg1(arg, tvp)
+ char *arg;
+ struct timeval *tvp;
+{
+ struct tm *t;
+ int yearset;
+ char *p;
+ /* Start with the current time. */
+ if ((t = localtime(&tvp[0].tv_sec)) == NULL)
+ err(1, "localtime");
+ /* [[CC]YY]MMDDhhmm[.SS] */
+ if ((p = strchr(arg, '.')) == NULL)
+ t->tm_sec = 0; /* Seconds defaults to 0. */
+ else {
+ if (strlen(p + 1) != 2)
+ goto terr;
+ *p++ = '\0';
+ t->tm_sec = ATOI2(p);
+ }
+
+ yearset = 0;
+ switch(strlen(arg)) {
+ case 12: /* CCYYMMDDhhmm */
+ t->tm_year = ATOI2(arg);
+ t->tm_year *= 100;
+ yearset = 1;
+ /* FALLTHOUGH */
+ case 10: /* YYMMDDhhmm */
+ if (yearset) {
+ yearset = ATOI2(arg);
+ t->tm_year += yearset;
+ } else {
+ yearset = ATOI2(arg);
+ if (yearset < 69)
+ t->tm_year = yearset + 2000;
+ else
+ t->tm_year = yearset + 1900;
+ }
+ t->tm_year -= 1900; /* Convert to UNIX time. */
+ /* FALLTHROUGH */
+ case 8: /* MMDDhhmm */
+ t->tm_mon = ATOI2(arg);
+ --t->tm_mon; /* Convert from 01-12 to 00-11 */
+ t->tm_mday = ATOI2(arg);
+ t->tm_hour = ATOI2(arg);
+ t->tm_min = ATOI2(arg);
+ break;
+ default:
+ goto terr;
+ }
+
+ t->tm_isdst = -1; /* Figure out DST. */
+ tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
+ if (tvp[0].tv_sec == -1)
+terr: errx(1,
+ "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
+
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+}
+
+void
+stime_arg2(arg, year, tvp)
+ char *arg;
+ int year;
+ struct timeval *tvp;
+{
+ struct tm *t;
+ /* Start with the current time. */
+ if ((t = localtime(&tvp[0].tv_sec)) == NULL)
+ err(1, "localtime");
+
+ t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
+ --t->tm_mon; /* Convert from 01-12 to 00-11 */
+ t->tm_mday = ATOI2(arg);
+ t->tm_hour = ATOI2(arg);
+ t->tm_min = ATOI2(arg);
+ if (year)
+ t->tm_year = ATOI2(arg);
+
+ t->tm_isdst = -1; /* Figure out DST. */
+ tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
+ if (tvp[0].tv_sec == -1)
+ errx(1,
+ "out of range or illegal time specification: MMDDhhmm[yy]");
+
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+}
+
+void
+stime_file(fname, tvp)
+ char *fname;
+ struct timeval *tvp;
+{
+ struct stat sb;
+
+ if (stat(fname, &sb))
+ err(1, "%s", fname);
+ TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
+ TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
+}
+
+int
+rw(fname, sbp, force)
+ char *fname;
+ struct stat *sbp;
+ int force;
+{
+ int fd, needed_chmod, rval;
+ u_char byte;
+
+ /* Try regular files and directories. */
+ if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) {
+ warnx("%s: %s", fname, strerror(EFTYPE));
+ return (1);
+ }
+
+ needed_chmod = rval = 0;
+ if ((fd = open(fname, O_RDWR, 0)) == -1) {
+ if (!force || chmod(fname, DEFFILEMODE))
+ goto err;
+ if ((fd = open(fname, O_RDWR, 0)) == -1)
+ goto err;
+ needed_chmod = 1;
+ }
+
+ if (sbp->st_size != 0) {
+ if (read(fd, &byte, sizeof(byte)) != sizeof(byte))
+ goto err;
+ if (lseek(fd, (off_t)0, SEEK_SET) == -1)
+ goto err;
+ if (write(fd, &byte, sizeof(byte)) != sizeof(byte))
+ goto err;
+ } else {
+ if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
+err: rval = 1;
+ warn("%s", fname);
+ } else if (ftruncate(fd, (off_t)0)) {
+ rval = 1;
+ warn("%s: file modified", fname);
+ }
+ }
+
+ if (close(fd) && rval != 1) {
+ rval = 1;
+ warn("%s", fname);
+ }
+ if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) {
+ rval = 1;
+ warn("%s: permissions modified", fname);
+ }
+ return (rval);
+}
+
+__dead void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: touch [-acfm] [-r file] [-t time] file ...\n");
+ exit(1);
+}
diff --git a/usr.bin/tput/Makefile b/usr.bin/tput/Makefile
new file mode 100644
index 0000000..45d369f
--- /dev/null
+++ b/usr.bin/tput/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tput
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+MLINKS= tput.1 clear.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/clear.sh ${DESTDIR}/usr/bin/clear
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tput/clear.sh b/usr.bin/tput/clear.sh
new file mode 100644
index 0000000..add589b
--- /dev/null
+++ b/usr.bin/tput/clear.sh
@@ -0,0 +1,37 @@
+#!/bin/sh -
+#
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)clear.sh 8.1 (Berkeley) 6/6/93
+#
+
+exec tput clear
diff --git a/usr.bin/tput/tput.1 b/usr.bin/tput/tput.1
new file mode 100644
index 0000000..9a348431
--- /dev/null
+++ b/usr.bin/tput/tput.1
@@ -0,0 +1,117 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tput.1 8.2 (Berkeley) 3/19/94
+.\"
+.Dd March 19, 1994
+.Dt TPUT 1
+.Os BSD 4.4
+.Sh NAME
+.Nm tput
+.Nd terminal capability interface
+.Sh SYNOPSIS
+.Nm tput
+.Op Fl T Ar term
+.Ar attribute
+.Sh DESCRIPTION
+.Nm Tput
+makes terminal-dependent information available to users or shell
+applications.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl T
+The terminal name as specified in the
+.Xr termcap
+database, for example, ``vt100'' or ``xterm''.
+If not specified,
+.Nm tput
+retrieves the
+.Dq Ev TERM
+variable from the environment.
+.El
+.Pp
+.Nm Tput
+outputs a string if the
+.Ar attribute
+is of type string; a number if it is of type integer.
+Otherwise,
+.Nm tput
+exits 0 if the terminal has the capability and 1 if it does not,
+without further action.
+.Pp
+If the
+.Ar attribute
+is of type string, and takes arguments (e.g. cursor movement,
+the termcap
+.Dq cm
+sequence) the arguments are taken from the command line immediately
+following the attribute.
+.Pp
+The following special attributes are available:
+.Bl -tag -width Ar
+.It clear
+Clear the screen (the
+.Xr termcap
+``cl'' sequence).
+.It init
+Initialize the terminal (the
+.Xr termcap
+``is'' sequence).
+.It longname
+Print the descriptive name of the user's terminal type.
+.It reset
+Reset the terminal (the
+.Xr termcap
+``rs'' sequence).
+.Sh DIAGNOSTICS
+The exit value of
+.Nm tput
+is based on the last attribute specified.
+If the attribute is of type string or of type integer,
+.Nm tput
+exits 0 if the attribute is defined for this terminal type and 1
+if it is not.
+If the attribute is of type boolean,
+.Nm tput
+exits 0 if the terminal has this attribute, and 1 if it does not.
+.Nm Tput
+exits 2 if any error occurred.
+.Sh SEE ALSO
+.Xr termcap 3 ,
+.Xr termcap 5
+.Sh BUGS
+.Nm Tput
+can't really distinguish between different types of attributes.
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/tput/tput.c b/usr.bin/tput/tput.c
new file mode 100644
index 0000000..c45be50
--- /dev/null
+++ b/usr.bin/tput/tput.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tput.c 8.2 (Berkeley) 3/19/94";
+#endif /* not lint */
+
+#include <termios.h>
+
+#include <err.h>
+#include <termcap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#undef putchar
+#define outc putchar
+
+static void prlongname __P((char *));
+static void usage __P((void));
+static char **process __P((char *, char *, char **));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ int ch, exitval, n;
+ char *cptr, *p, *term, buf[1024], tbuf[1024];
+
+ term = NULL;
+ while ((ch = getopt(argc, argv, "T:")) != EOF)
+ switch(ch) {
+ case 'T':
+ term = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!term && !(term = getenv("TERM")))
+errx(2, "no terminal type specified and no TERM environmental variable.");
+ if (tgetent(tbuf, term) != 1)
+ err(2, "tgetent failure");
+ for (exitval = 0; (p = *argv) != NULL; ++argv) {
+ switch (*p) {
+ case 'c':
+ if (!strcmp(p, "clear"))
+ p = "cl";
+ break;
+ case 'i':
+ if (!strcmp(p, "init"))
+ p = "is";
+ break;
+ case 'l':
+ if (!strcmp(p, "longname")) {
+ prlongname(tbuf);
+ continue;
+ }
+ break;
+ case 'r':
+ if (!strcmp(p, "reset"))
+ p = "rs";
+ break;
+ }
+ cptr = buf;
+ if (tgetstr(p, &cptr))
+ argv = process(p, buf, argv);
+ else if ((n = tgetnum(p)) != -1)
+ (void)printf("%d\n", n);
+ else
+ exitval = !tgetflag(p);
+ }
+ exit(exitval);
+}
+
+static void
+prlongname(buf)
+ char *buf;
+{
+ int savech;
+ char *p, *savep;
+
+ for (p = buf; *p && *p != ':'; ++p);
+ savech = *(savep = p);
+ for (*p = '\0'; p >= buf && *p != '|'; --p);
+ (void)printf("%s\n", p + 1);
+ *savep = savech;
+}
+
+static char **
+process(cap, str, argv)
+ char *cap, *str, **argv;
+{
+ static char errfew[] =
+ "not enough arguments (%d) for capability `%s'";
+ static char errmany[] =
+ "too many arguments (%d) for capability `%s'";
+ static char erresc[] =
+ "unknown %% escape `%c' for capability `%s'";
+ char *cp;
+ int arg_need, arg_rows, arg_cols;
+
+ /* Count how many values we need for this capability. */
+ for (cp = str, arg_need = 0; *cp != '\0'; cp++)
+ if (*cp == '%')
+ switch (*++cp) {
+ case 'd':
+ case '2':
+ case '3':
+ case '.':
+ case '+':
+ arg_need++;
+ break;
+ case '%':
+ case '>':
+ case 'i':
+ case 'r':
+ case 'n':
+ case 'B':
+ case 'D':
+ break;
+ default:
+ /*
+ * hpux has lot's of them, but we complain
+ */
+ errx(2, erresc, *cp, cap);
+ }
+
+ /* And print them. */
+ switch (arg_need) {
+ case 0:
+ (void)tputs(str, 1, outc);
+ break;
+ case 1:
+ arg_cols = 0;
+
+ if (*++argv == NULL || *argv[0] == '\0')
+ errx(2, errfew, 1, cap);
+ arg_rows = atoi(*argv);
+
+ (void)tputs(tgoto(str, arg_cols, arg_rows), 1, outc);
+ break;
+ case 2:
+ if (*++argv == NULL || *argv[0] == '\0')
+ errx(2, errfew, 2, cap);
+ arg_cols = atoi(*argv);
+
+ if (*++argv == NULL || *argv[0] == '\0')
+ errx(2, errfew, 2, cap);
+ arg_rows = atoi(*argv);
+
+ (void) tputs(tgoto(str, arg_cols, arg_rows), arg_rows, outc);
+ break;
+
+ default:
+ errx(2, errmany, arg_need, cap);
+ }
+ return (argv);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: tput [-T term] attribute ...\n");
+ exit(1);
+}
diff --git a/usr.bin/tr/Makefile b/usr.bin/tr/Makefile
new file mode 100644
index 0000000..7124942
--- /dev/null
+++ b/usr.bin/tr/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tr
+SRCS= str.c tr.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tr/extern.h b/usr.bin/tr/extern.h
new file mode 100644
index 0000000..9e82d06
--- /dev/null
+++ b/usr.bin/tr/extern.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef struct {
+ enum { STRING1, STRING2 } which;
+ enum { EOS, INFINITE, NORMAL, RANGE, SEQUENCE, SET } state;
+ int cnt; /* character count */
+ int lastch; /* last character */
+ int equiv[2]; /* equivalence set */
+ int *set; /* set of characters */
+ char *str; /* user's string */
+} STR;
+
+#include <limits.h>
+#define NCHARS (UCHAR_MAX + 1) /* Number of possible characters. */
+#define OOBCH (UCHAR_MAX + 1) /* Out of band character value. */
+
+void err __P((const char *fmt, ...));
+int next __P((STR *));
diff --git a/usr.bin/tr/str.c b/usr.bin/tr/str.c
new file mode 100644
index 0000000..63ba44a
--- /dev/null
+++ b/usr.bin/tr/str.c
@@ -0,0 +1,330 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)str.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "extern.h"
+
+static int backslash __P((STR *));
+static int bracket __P((STR *));
+static int c_class __P((const void *, const void *));
+static void genclass __P((STR *));
+static void genequiv __P((STR *));
+static int genrange __P((STR *));
+static void genseq __P((STR *));
+
+int
+next(s)
+ register STR *s;
+{
+ register int ch;
+
+ switch (s->state) {
+ case EOS:
+ return (0);
+ case INFINITE:
+ return (1);
+ case NORMAL:
+ switch (ch = *s->str) {
+ case '\0':
+ s->state = EOS;
+ return (0);
+ case '\\':
+ s->lastch = backslash(s);
+ break;
+ case '[':
+ if (bracket(s))
+ return (next(s));
+ /* FALLTHROUGH */
+ default:
+ ++s->str;
+ s->lastch = ch;
+ break;
+ }
+
+ /* We can start a range at any time. */
+ if (s->str[0] == '-' && genrange(s))
+ return (next(s));
+ return (1);
+ case RANGE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ ++s->lastch;
+ return (1);
+ case SEQUENCE:
+ if (s->cnt-- == 0) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ return (1);
+ case SET:
+ if ((s->lastch = s->set[s->cnt++]) == OOBCH) {
+ s->state = NORMAL;
+ return (next(s));
+ }
+ return (1);
+ }
+ /* NOTREACHED */
+}
+
+static int
+bracket(s)
+ register STR *s;
+{
+ register char *p;
+
+ switch (s->str[1]) {
+ case ':': /* "[:class:]" */
+ if ((p = strstr(s->str + 2, ":]")) == NULL)
+ return (0);
+ *p = '\0';
+ s->str += 2;
+ genclass(s);
+ s->str = p + 2;
+ return (1);
+ case '=': /* "[=equiv=]" */
+ if ((p = strstr(s->str + 2, "=]")) == NULL)
+ return (0);
+ s->str += 2;
+ genequiv(s);
+ return (1);
+ default: /* "[\###*n]" or "[#*n]" */
+ if ((p = strpbrk(s->str + 2, "*]")) == NULL)
+ return (0);
+ if (p[0] != '*' || index(p, ']') == NULL)
+ return (0);
+ s->str += 1;
+ genseq(s);
+ return (1);
+ }
+ /* NOTREACHED */
+}
+
+typedef struct {
+ char *name;
+ int (*func) __P((int));
+ int *set;
+} CLASS;
+
+static CLASS classes[] = {
+ { "alnum", isalnum, },
+ { "alpha", isalpha, },
+ { "blank", isblank, },
+ { "cntrl", iscntrl, },
+ { "digit", isdigit, },
+ { "graph", isgraph, },
+ { "lower", islower, },
+ { "print", isprint, },
+ { "punct", ispunct, },
+ { "space", isspace, },
+ { "upper", isupper, },
+ { "xdigit", isxdigit, },
+};
+
+static void
+genclass(s)
+ STR *s;
+{
+ register int cnt, (*func) __P((int));
+ CLASS *cp, tmp;
+ int *p;
+
+ tmp.name = s->str;
+ if ((cp = (CLASS *)bsearch(&tmp, classes, sizeof(classes) /
+ sizeof(CLASS), sizeof(CLASS), c_class)) == NULL)
+ err("unknown class %s", s->str);
+
+ if ((cp->set = p = malloc((NCHARS + 1) * sizeof(int))) == NULL)
+ err("%s", strerror(errno));
+ bzero(p, (NCHARS + 1) * sizeof(int));
+ for (cnt = 0, func = cp->func; cnt < NCHARS; ++cnt)
+ if ((func)(cnt))
+ *p++ = cnt;
+ *p = OOBCH;
+
+ s->cnt = 0;
+ s->state = SET;
+ s->set = cp->set;
+}
+
+static int
+c_class(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((CLASS *)a)->name, ((CLASS *)b)->name));
+}
+
+/*
+ * English doesn't have any equivalence classes, so for now
+ * we just syntax check and grab the character.
+ */
+static void
+genequiv(s)
+ STR *s;
+{
+ if (*s->str == '\\') {
+ s->equiv[0] = backslash(s);
+ if (*s->str != '=')
+ err("misplaced equivalence equals sign");
+ } else {
+ s->equiv[0] = s->str[0];
+ if (s->str[1] != '=')
+ err("misplaced equivalence equals sign");
+ }
+ s->str += 2;
+ s->cnt = 0;
+ s->state = SET;
+ s->set = s->equiv;
+}
+
+static int
+genrange(s)
+ STR *s;
+{
+ int stopval;
+ char *savestart;
+
+ savestart = s->str;
+ stopval = *++s->str == '\\' ? backslash(s) : *s->str;
+ if (stopval < s->lastch) {
+ s->str = savestart;
+ return (0);
+ }
+ s->cnt = stopval - s->lastch + 1;
+ s->state = RANGE;
+ --s->lastch;
+ return (1);
+}
+
+static void
+genseq(s)
+ STR *s;
+{
+ char *ep;
+
+ if (s->which == STRING1)
+ err("sequences only valid in string2");
+
+ if (*s->str == '\\')
+ s->lastch = backslash(s);
+ else
+ s->lastch = *s->str++;
+ if (*s->str != '*')
+ err("misplaced sequence asterisk");
+
+ switch (*++s->str) {
+ case '\\':
+ s->cnt = backslash(s);
+ break;
+ case ']':
+ s->cnt = 0;
+ ++s->str;
+ break;
+ default:
+ if (isdigit(*s->str)) {
+ s->cnt = strtol(s->str, &ep, 0);
+ if (*ep == ']') {
+ s->str = ep + 1;
+ break;
+ }
+ }
+ err("illegal sequence count");
+ /* NOTREACHED */
+ }
+
+ s->state = s->cnt ? SEQUENCE : INFINITE;
+}
+
+/* Use the #defines isXXX() here, DON'T use them above. */
+#include <ctype.h>
+
+/*
+ * Translate \??? into a character. Up to 3 octal digits, if no digits either
+ * an escape code or a literal character.
+ */
+static int
+backslash(s)
+ register STR *s;
+{
+ register int ch, cnt, val;
+
+ for (cnt = val = 0;;) {
+ ch = *++s->str;
+ if (!isascii(ch) || !isdigit(ch))
+ break;
+ val = val * 8 + ch - '0';
+ if (++cnt == 3) {
+ ++s->str;
+ break;
+ }
+ }
+ if (cnt)
+ return (val);
+ if (ch != '\0')
+ ++s->str;
+ switch (ch) {
+ case 'a': /* escape characters */
+ return ('\7');
+ case 'b':
+ return ('\b');
+ case 'f':
+ return ('\f');
+ case 'n':
+ return ('\n');
+ case 'r':
+ return ('\r');
+ case 't':
+ return ('\t');
+ case 'v':
+ return ('\13');
+ case '\0': /* \" -> \ */
+ s->state = EOS;
+ return ('\\');
+ default: /* \x" -> x */
+ return (ch);
+ }
+}
diff --git a/usr.bin/tr/tr.1 b/usr.bin/tr/tr.1
new file mode 100644
index 0000000..5170463
--- /dev/null
+++ b/usr.bin/tr/tr.1
@@ -0,0 +1,292 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tr.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TR 1
+.Os
+.Sh NAME
+.Nm tr
+.Nd translate characters
+.Sh SYNOPSIS
+.Nm tr
+.Op Fl cs
+.Ar string1 string2
+.Nm tr
+.Op Fl c
+.Fl d
+.Ar string1
+.Nm tr
+.Op Fl c
+.Fl s
+.Ar string1
+.Nm tr
+.Op Fl c
+.Fl ds
+.Ar string1 string2
+.Sh DESCRIPTION
+The
+.Nm tr
+utility copies the standard input to the standard output with substitution
+or deletion of selected characters.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Complements the set of characters in
+.Ar string1 ,
+that is ``-c ab'' includes every character except for ``a'' and ``b''.
+.It Fl d
+The
+.Fl d
+option causes characters to be deleted from the input.
+.It Fl s
+The
+.Fl s
+option squeezes multiple occurrences of the characters listed in the last
+operand (either
+.Ar string1
+or
+.Ar string2 )
+in the input into a single instance of the character.
+This occurs after all deletion and translation is completed.
+.El
+.Pp
+In the first synopsis form, the characters in
+.Ar string1
+are translated into the characters in
+.Ar string2
+where the first character in
+.Ar string1
+is translated into the first character in
+.Ar string2
+and so on.
+If
+.Ar string1
+is longer than
+.Ar string2 ,
+the last character found in
+.Ar string2
+is duplicated until
+.Ar string1
+is exhausted.
+.Pp
+In the second synopsis form, the characters in
+.Ar string1
+are deleted from the input.
+.Pp
+In the third synopsis form, the characters in
+.Ar string1
+are compressed as described for the
+.Fl s
+option.
+.Pp
+In the fourth synopsis form, the characters in
+.Ar string1
+are deleted from the input, and the characters in
+.Ar string2
+are compressed as described for the
+.Fl s
+option.
+.Pp
+The following conventions can be used in
+.Ar string1
+and
+.Ar string2
+to specify sets of characters:
+.Bl -tag -width [:equiv:]
+.It character
+Any character not described by one of the following conventions
+represents itself.
+.It \eoctal
+A backslash followed by 1, 2 or 3 octal digits represents a character
+with that encoded value.
+To follow an octal sequence with a digit as a character, left zero-pad
+the octal sequence to the full 3 octal digits.
+.It \echaracter
+A backslash followed by certain special characters maps to special
+values.
+.sp
+.Bl -column
+.It \ea <alert character>
+.It \eb <backspace>
+.It \ef <form-feed>
+.It \en <newline>
+.It \er <carriage return>
+.It \et <tab>
+.It \ev <vertical tab>
+.El
+.sp
+A backslash followed by any other character maps to that character.
+.It c-c
+Represents the range of characters between the range endpoints, inclusively.
+.It [:class:]
+Represents all characters belonging to the defined character class.
+Class names are:
+.sp
+.Bl -column
+.It alnum <alphanumeric characters>
+.It alpha <alphabetic characters>
+.It cntrl <control characters>
+.It digit <numeric characters>
+.It graph <graphic characters>
+.It lower <lower-case alphabetic characters>
+.It print <printable characters>
+.It punct <punctuation characters>
+.It space <space characters>
+.It upper <upper-case characters>
+.It xdigit <hexadecimal characters>
+.El
+.Pp
+\." All classes may be used in
+\." .Ar string1 ,
+\." and in
+\." .Ar string2
+\." when both the
+\." .Fl d
+\." and
+\." .Fl s
+\." options are specified.
+\." Otherwise, only the classes ``upper'' and ``lower'' may be used in
+\." .Ar string2
+\." and then only when the corresponding class (``upper'' for ``lower''
+\." and vice-versa) is specified in the same relative position in
+\." .Ar string1 .
+\." .Pp
+With the exception of the ``upper'' and ``lower'' classes, characters
+in the classes are in unspecified order.
+In the ``upper'' and ``lower'' classes, characters are entered in
+ascending order.
+.Pp
+For specific information as to which ASCII characters are included
+in these classes, see
+.Xr ctype 3
+and related manual pages.
+.It [=equiv=]
+Represents all characters or collating (sorting) elements belonging to
+the same equivalence class as
+.Ar equiv .
+If
+there is a secondary ordering within the equivalence class, the characters
+are ordered in ascending sequence.
+Otherwise, they are ordered after their encoded values.
+An example of an equivalence class might be ``c'' and ``ch'' in Spanish;
+English has no equivalence classes.
+.It [#*n]
+Represents
+.Ar n
+repeated occurrences of the character represented by
+.Ar # .
+This
+expression is only valid when it occurs in
+.Ar string2 .
+If
+.Ar n
+is omitted or is zero, it is be interpreted as large enough to extend
+.Ar string2
+sequence to the length of
+.Ar string1 .
+If
+.Ar n
+has a leading zero, it is interpreted as an octal value, otherwise,
+it's interpreted as a decimal value.
+.El
+.Pp
+The
+.Nm tr
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+The following examples are shown as given to the shell:
+.sp
+Create a list of the words in file1, one per line, where a word is taken to
+be a maximal string of letters.
+.sp
+.D1 Li "tr -cs \*q[:alpha:]\*q \*q\en\*q < file1"
+.sp
+Translate the contents of file1 to upper-case.
+.sp
+.D1 Li "tr \*q[:lower:]\*q \*q[:upper:]\*q < file1"
+.sp
+Strip out non-printable characters from file1.
+.sp
+.D1 Li "tr -cd \*q[:print:]\*q < file1"
+.Sh COMPATIBILITY
+System V has historically implemented character ranges using the syntax
+``[c-c]'' instead of the ``c-c'' used by historic BSD implementations and
+standardized by POSIX.
+System V shell scripts should work under this implementation as long as
+the range is intended to map in another range, i.e. the command
+``tr [a-z] [A-Z]'' will work as it will map the ``['' character in
+.Ar string1
+to the ``['' character in
+.Ar string2.
+However, if the shell script is deleting or squeezing characters as in
+the command ``tr -d [a-z]'', the characters ``['' and ``]'' will be
+included in the deletion or compression list which would not have happened
+under an historic System V implementation.
+Additionally, any scripts that depended on the sequence ``a-z'' to
+represent the three characters ``a'', ``-'' and ``z'' will have to be
+rewritten as ``a\e-z''.
+.Pp
+The
+.Nm tr
+utility has historically not permitted the manipulation of NUL bytes in
+its input and, additionally, stripped NUL's from its input stream.
+This implementation has removed this behavior as a bug.
+.Pp
+The
+.Nm tr
+utility has historically been extremely forgiving of syntax errors,
+for example, the
+.Fl c
+and
+.Fl s
+options were ignored unless two strings were specified.
+This implementation will not permit illegal syntax.
+.Sh STANDARDS
+The
+.Nm tr
+utility is expected to be
+.St -p1003.2
+compatible.
+It should be noted that the feature wherein the last character of
+.Ar string2
+is duplicated if
+.Ar string2
+has less characters than
+.Ar string1
+is permitted by POSIX but is not required.
+Shell scripts attempting to be portable to other POSIX systems should use
+the ``[#*]'' convention instead of relying on this behavior.
diff --git a/usr.bin/tr/tr.c b/usr.bin/tr/tr.c
new file mode 100644
index 0000000..89b39b8
--- /dev/null
+++ b/usr.bin/tr/tr.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+static int string1[NCHARS] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* ASCII */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+}, string2[NCHARS];
+
+STR s1 = { STRING1, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL };
+STR s2 = { STRING2, NORMAL, 0, OOBCH, { 0, OOBCH }, NULL, NULL };
+
+static void setup __P((int *, char *, STR *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int ch, cnt, lastch, *p;
+ int cflag, dflag, sflag, isstring2;
+
+ cflag = dflag = sflag = 0;
+ while ((ch = getopt(argc, argv, "cds")) != EOF)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ default:
+ usage();
+ /* NOTREACHED */
+ case 1:
+ isstring2 = 0;
+ break;
+ case 2:
+ isstring2 = 1;
+ break;
+ }
+
+ /*
+ * tr -ds [-c] string1 string2
+ * Delete all characters (or complemented characters) in string1.
+ * Squeeze all characters in string2.
+ */
+ if (dflag && sflag) {
+ if (!isstring2)
+ usage();
+
+ setup(string1, argv[0], &s1, cflag);
+ setup(string2, argv[1], &s2, 0);
+
+ for (lastch = OOBCH; (ch = getchar()) != EOF;)
+ if (!string1[ch] && (!string2[ch] || lastch != ch)) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ exit(0);
+ }
+
+ /*
+ * tr -d [-c] string1
+ * Delete all characters (or complemented characters) in string1.
+ */
+ if (dflag) {
+ if (isstring2)
+ usage();
+
+ setup(string1, argv[0], &s1, cflag);
+
+ while ((ch = getchar()) != EOF)
+ if (!string1[ch])
+ (void)putchar(ch);
+ exit(0);
+ }
+
+ /*
+ * tr -s [-c] string1
+ * Squeeze all characters (or complemented characters) in string1.
+ */
+ if (sflag && !isstring2) {
+ setup(string1, argv[0], &s1, cflag);
+
+ for (lastch = OOBCH; (ch = getchar()) != EOF;)
+ if (!string1[ch] || lastch != ch) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ exit(0);
+ }
+
+ /*
+ * tr [-cs] string1 string2
+ * Replace all characters (or complemented characters) in string1 with
+ * the character in the same position in string2. If the -s option is
+ * specified, squeeze all the characters in string2.
+ */
+ if (!isstring2)
+ usage();
+
+ s1.str = argv[0];
+ s2.str = argv[1];
+
+ if (cflag)
+ for (cnt = NCHARS, p = string1; cnt--;)
+ *p++ = OOBCH;
+
+ if (!next(&s2))
+ err("empty string2");
+
+ /* If string2 runs out of characters, use the last one specified. */
+ if (sflag)
+ while (next(&s1)) {
+ string1[s1.lastch] = ch = s2.lastch;
+ string2[ch] = 1;
+ (void)next(&s2);
+ }
+ else
+ while (next(&s1)) {
+ string1[s1.lastch] = ch = s2.lastch;
+ (void)next(&s2);
+ }
+
+ if (cflag)
+ for (cnt = 0, p = string1; cnt < NCHARS; ++p, ++cnt)
+ *p = *p == OOBCH ? ch : cnt;
+
+ if (sflag)
+ for (lastch = OOBCH; (ch = getchar()) != EOF;) {
+ ch = string1[ch];
+ if (!string2[ch] || lastch != ch) {
+ lastch = ch;
+ (void)putchar(ch);
+ }
+ }
+ else
+ while ((ch = getchar()) != EOF)
+ (void)putchar(string1[ch]);
+ exit (0);
+}
+
+static void
+setup(string, arg, str, cflag)
+ int *string;
+ char *arg;
+ STR *str;
+ int cflag;
+{
+ register int cnt, *p;
+
+ str->str = arg;
+ bzero(string, NCHARS * sizeof(int));
+ while (next(str))
+ string[str->lastch] = 1;
+ if (cflag)
+ for (p = string, cnt = NCHARS; cnt--; ++p)
+ *p = !*p;
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: tr [-cs] string1 string2\n");
+ (void)fprintf(stderr, " tr [-c] -d string1\n");
+ (void)fprintf(stderr, " tr [-c] -s string1\n");
+ (void)fprintf(stderr, " tr [-c] -ds string1 string2\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "tr: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/true/Makefile b/usr.bin/true/Makefile
new file mode 100644
index 0000000..3bebb2d
--- /dev/null
+++ b/usr.bin/true/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= true
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/true/true.1 b/usr.bin/true/true.1
new file mode 100644
index 0000000..fc15a4e
--- /dev/null
+++ b/usr.bin/true/true.1
@@ -0,0 +1,62 @@
+.\" Copyright (c) 1983, 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)true.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt TRUE 1
+.Os
+.Sh NAME
+.Nm true
+.Nd return true value
+.Sh SYNOPSIS
+.Nm true
+.Sh DESCRIPTION
+.Nm True
+is normally used in a Bourne shell script.
+.Nm True
+tests for the appropriate status "false" before running
+(or failing to run) a list of commands.
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr false 1
+.Sh DIAGNOSTICS
+The
+.Nm true
+utility always returns with exit code zero.
+.Sh STANDARDS
+The
+.Nm true
+function is expected to be POSIX 1003.2 compatible.
diff --git a/usr.bin/true/true.c b/usr.bin/true/true.c
new file mode 100644
index 0000000..0847cfa
--- /dev/null
+++ b/usr.bin/true/true.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)true.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+main()
+{
+ exit(0);
+}
diff --git a/usr.bin/tset/Makefile b/usr.bin/tset/Makefile
new file mode 100644
index 0000000..126cf321
--- /dev/null
+++ b/usr.bin/tset/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= tset
+SRCS= map.c misc.c set.c term.c tset.c wrterm.c
+
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+LINKS= ${BINDIR}/tset ${BINDIR}/reset
+MLINKS= tset.1 reset.1
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tset/extern.h b/usr.bin/tset/extern.h
new file mode 100644
index 0000000..29acfac
--- /dev/null
+++ b/usr.bin/tset/extern.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/9/93
+ */
+
+#include <termcap.h>
+
+extern struct termios mode, oldmode;
+extern int columns, isreset, lines;
+extern int erasechar, intrchar, killchar;
+
+void add_mapping __P((char *, char *));
+void cat __P((char *));
+void err __P((const char *, ...));
+char *get_termcap_entry __P((char *, char **));
+char *mapped __P((char *));
+int outc __P((int));
+void reset_mode __P((void));
+void set_control_chars __P((void));
+void set_conversions __P((int));
+void set_init __P((void));
+void wrtermcap __P((char *));
diff --git a/usr.bin/tset/map.c b/usr.bin/tset/map.c
new file mode 100644
index 0000000..2e71d2c
--- /dev/null
+++ b/usr.bin/tset/map.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <termios.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+extern speed_t Ospeed;
+speed_t baudrate __P((char *));
+
+/* Baud rate conditionals for mapping. */
+#define GT 0x01
+#define EQ 0x02
+#define LT 0x04
+#define NOT 0x08
+#define GE (GT | EQ)
+#define LE (LT | EQ)
+
+typedef struct map {
+ struct map *next; /* Linked list of maps. */
+ char *porttype; /* Port type, or "" for any. */
+ char *type; /* Terminal type to select. */
+ int conditional; /* Baud rate conditionals bitmask. */
+ speed_t speed; /* Baud rate to compare against. */
+} MAP;
+
+MAP *cur, *maplist;
+
+/*
+ * Syntax for -m:
+ * [port-type][test baudrate]:terminal-type
+ * The baud rate tests are: >, <, @, =, !
+ */
+void
+add_mapping(port, arg)
+ char *port, *arg;
+{
+ MAP *mapp;
+ char *copy, *p, *termp;
+
+ copy = strdup(arg);
+ mapp = malloc((u_int)sizeof(MAP));
+ if (copy == NULL || mapp == NULL)
+ err("%s", strerror(errno));
+ mapp->next = NULL;
+ if (maplist == NULL)
+ cur = maplist = mapp;
+ else {
+ cur->next = mapp;
+ cur = mapp;
+ }
+
+ mapp->porttype = arg;
+ mapp->conditional = 0;
+
+ arg = strpbrk(arg, "><@=!:");
+
+ if (arg == NULL) { /* [?]term */
+ mapp->type = mapp->porttype;
+ mapp->porttype = NULL;
+ goto done;
+ }
+
+ if (arg == mapp->porttype) /* [><@=! baud]:term */
+ termp = mapp->porttype = NULL;
+ else
+ termp = arg;
+
+ for (;; ++arg) /* Optional conditionals. */
+ switch(*arg) {
+ case '<':
+ if (mapp->conditional & GT)
+ goto badmopt;
+ mapp->conditional |= LT;
+ break;
+ case '>':
+ if (mapp->conditional & LT)
+ goto badmopt;
+ mapp->conditional |= GT;
+ break;
+ case '@':
+ case '=': /* Not documented. */
+ mapp->conditional |= EQ;
+ break;
+ case '!':
+ mapp->conditional |= NOT;
+ break;
+ default:
+ goto next;
+ }
+
+next: if (*arg == ':') {
+ if (mapp->conditional)
+ goto badmopt;
+ ++arg;
+ } else { /* Optional baudrate. */
+ arg = index(p = arg, ':');
+ if (arg == NULL)
+ goto badmopt;
+ *arg++ = '\0';
+ mapp->speed = baudrate(p);
+ }
+
+ if (*arg == NULL) /* Non-optional type. */
+ goto badmopt;
+
+ mapp->type = arg;
+
+ /* Terminate porttype, if specified. */
+ if (termp != NULL)
+ *termp = '\0';
+
+ /* If a NOT conditional, reverse the test. */
+ if (mapp->conditional & NOT)
+ mapp->conditional = ~mapp->conditional & (EQ | GT | LT);
+
+ /* If user specified a port with an option flag, set it. */
+done: if (port) {
+ if (mapp->porttype)
+badmopt: err("illegal -m option format: %s", copy);
+ mapp->porttype = port;
+ }
+
+#ifdef MAPDEBUG
+ (void)printf("port: %s\n", mapp->porttype ? mapp->porttype : "ANY");
+ (void)printf("type: %s\n", mapp->type);
+ (void)printf("conditional: ");
+ p = "";
+ if (mapp->conditional & GT) {
+ (void)printf("GT");
+ p = "/";
+ }
+ if (mapp->conditional & EQ) {
+ (void)printf("%sEQ", p);
+ p = "/";
+ }
+ if (mapp->conditional & LT)
+ (void)printf("%sLT", p);
+ (void)printf("\nspeed: %d\n", mapp->speed);
+#endif
+}
+
+/*
+ * Return the type of terminal to use for a port of type 'type', as specified
+ * by the first applicable mapping in 'map'. If no mappings apply, return
+ * 'type'.
+ */
+char *
+mapped(type)
+ char *type;
+{
+ MAP *mapp;
+ int match;
+
+ for (mapp = maplist; mapp; mapp = mapp->next)
+ if (mapp->porttype == NULL || !strcmp(mapp->porttype, type)) {
+ switch (mapp->conditional) {
+ case 0: /* No test specified. */
+ match = 1;
+ break;
+ case EQ:
+ match = (Ospeed == mapp->speed);
+ break;
+ case GE:
+ match = (Ospeed >= mapp->speed);
+ break;
+ case GT:
+ match = (Ospeed > mapp->speed);
+ break;
+ case LE:
+ match = (Ospeed <= mapp->speed);
+ break;
+ case LT:
+ match = (Ospeed < mapp->speed);
+ break;
+ }
+ if (match)
+ return (mapp->type);
+ }
+ /* No match found; return given type. */
+ return (type);
+}
+
+typedef struct speeds {
+ char *string;
+ speed_t speed;
+} SPEEDS;
+
+SPEEDS speeds[] = {
+ "0", B0,
+ "134.5", B134,
+ "exta", B19200,
+ "extb", B38400,
+ NULL
+};
+
+speed_t
+baudrate(rate)
+ char *rate;
+{
+ SPEEDS *sp;
+ speed_t speed;
+
+ /* The baudrate number can be preceded by a 'B', which is ignored. */
+ if (*rate == 'B')
+ ++rate;
+
+ for (sp = speeds; sp->string; ++sp)
+ if (!strcasecmp(rate, sp->string))
+ return (sp->speed);
+ speed = atol(rate);
+ if (speed == 0)
+ err("unknown baud rate %s", rate);
+ return speed;
+}
diff --git a/usr.bin/tset/misc.c b/usr.bin/tset/misc.c
new file mode 100644
index 0000000..27a2bf6
--- /dev/null
+++ b/usr.bin/tset/misc.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+void
+cat(file)
+ char *file;
+{
+ register int fd, nr, nw;
+ char buf[1024];
+
+ if ((fd = open(file, O_RDONLY, 0)) < 0)
+ err("%s: %s", file, strerror(errno));
+
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ if ((nw = write(STDERR_FILENO, buf, nr)) == -1)
+ err("write to stderr: %s", strerror(errno));
+ if (nr != 0)
+ err("%s: %s", file, strerror(errno));
+ (void)close(fd);
+}
+
+int
+outc(c)
+ int c;
+{
+ return putc(c, stderr);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "tset: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/tset/set.c b/usr.bin/tset/set.c
new file mode 100644
index 0000000..e55ebbd
--- /dev/null
+++ b/usr.bin/tset/set.c
@@ -0,0 +1,320 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)set.c 8.2 (Berkeley) 2/28/94";
+#endif /* not lint */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "extern.h"
+
+#define CHK(val, dft) (val <= 0 ? dft : val)
+
+int set_tabs __P((void));
+
+/*
+ * Reset the terminal mode bits to a sensible state. Very useful after
+ * a child program dies in raw mode.
+ */
+void
+reset_mode()
+{
+ tcgetattr(STDERR_FILENO, &mode);
+
+#if defined(VDISCARD) && defined(CDISCARD)
+ mode.c_cc[VDISCARD] = CHK(mode.c_cc[VDISCARD], CDISCARD);
+#endif
+ mode.c_cc[VEOF] = CHK(mode.c_cc[VEOF], CEOF);
+ mode.c_cc[VERASE] = CHK(mode.c_cc[VERASE], CERASE);
+#if defined(VFLUSH) && defined(CFLUSH)
+ mode.c_cc[VFLUSH] = CHK(mode.c_cc[VFLUSH], CFLUSH);
+#endif
+ mode.c_cc[VINTR] = CHK(mode.c_cc[VINTR], CINTR);
+ mode.c_cc[VKILL] = CHK(mode.c_cc[VKILL], CKILL);
+#if defined(VLNEXT) && defined(CLNEXT)
+ mode.c_cc[VLNEXT] = CHK(mode.c_cc[VLNEXT], CLNEXT);
+#endif
+ mode.c_cc[VQUIT] = CHK(mode.c_cc[VQUIT], CQUIT);
+#if defined(VREPRINT) && defined(CRPRNT)
+ mode.c_cc[VREPRINT] = CHK(mode.c_cc[VREPRINT], CRPRNT);
+#endif
+ mode.c_cc[VSTART] = CHK(mode.c_cc[VSTART], CSTART);
+ mode.c_cc[VSTOP] = CHK(mode.c_cc[VSTOP], CSTOP);
+ mode.c_cc[VSUSP] = CHK(mode.c_cc[VSUSP], CSUSP);
+#if defined(VWERASE) && defined(CWERASE)
+ mode.c_cc[VWERASE] = CHK(mode.c_cc[VWERASE], CWERASE);
+#endif
+
+ mode.c_iflag &= ~(IGNBRK | PARMRK | INPCK | ISTRIP | INLCR | IGNCR
+#ifdef IUCLC
+ | IUCLC
+#endif
+#ifdef IXANY
+ | IXANY
+#endif
+ | IXOFF);
+
+ mode.c_iflag |= (BRKINT | IGNPAR | ICRNL | IXON
+#ifdef IMAXBEL
+ | IMAXBEL
+#endif
+ );
+
+ mode.c_oflag &= ~(0
+#ifdef OLCUC
+ | OLCUC
+#endif
+#ifdef OCRNL
+ | OCRNL
+#endif
+#ifdef ONOCR
+ | ONOCR
+#endif
+#ifdef ONLRET
+ | ONLRET
+#endif
+#ifdef OFILL
+ | OFILL
+#endif
+#ifdef OFDEL
+ | OFDEL
+#endif
+#ifdef NLDLY
+ | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY
+#endif
+ );
+
+ mode.c_oflag |= (OPOST
+#ifdef ONLCR
+ | ONLCR
+#endif
+ );
+
+ mode.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CLOCAL);
+ mode.c_cflag |= (CS8 | CREAD);
+ mode.c_lflag &= ~(ECHONL | NOFLSH | TOSTOP
+#ifdef ECHOPTR
+ | ECHOPRT
+#endif
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+
+ mode.c_lflag |= (ISIG | ICANON | ECHO | ECHOE | ECHOK
+#ifdef ECHOCTL
+ | ECHOCTL
+#endif
+#ifdef ECHOKE
+ | ECHOKE
+#endif
+ );
+
+ tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
+}
+
+/*
+ * Determine the erase, interrupt, and kill characters from the termcap
+ * entry and command line and update their values in 'mode'.
+ */
+void
+set_control_chars()
+{
+ char *bp, *p, bs_char, buf[1024];
+
+ bp = buf;
+ p = tgetstr("kb", &bp);
+ if (p == NULL || p[1] != '\0')
+ p = tgetstr("bc", &bp);
+ if (p != NULL && p[1] == '\0')
+ bs_char = p[0];
+ else if (tgetflag("bs"))
+ bs_char = CTRL('h');
+ else
+ bs_char = 0;
+
+ if (erasechar == 0 && bs_char != 0 && !tgetflag("os"))
+ erasechar = -1;
+ if (erasechar < 0)
+ erasechar = (bs_char != 0) ? bs_char : CTRL('h');
+
+ if (mode.c_cc[VERASE] == 0 || erasechar != 0)
+ mode.c_cc[VERASE] = erasechar ? erasechar : CERASE;
+
+ if (mode.c_cc[VINTR] == 0 || intrchar != 0)
+ mode.c_cc[VINTR] = intrchar ? intrchar : CINTR;
+
+ if (mode.c_cc[VKILL] == 0 || killchar != 0)
+ mode.c_cc[VKILL] = killchar ? killchar : CKILL;
+}
+
+/*
+ * Set up various conversions in 'mode', including parity, tabs, returns,
+ * echo, and case, according to the termcap entry. If the program we're
+ * running was named with a leading upper-case character, map external
+ * uppercase to internal lowercase.
+ */
+void
+set_conversions(usingupper)
+ int usingupper;
+{
+ if (tgetflag("UC") || usingupper) {
+#ifdef IUCLC
+ mode.c_iflag |= IUCLC;
+ mode.c_oflag |= OLCUC;
+#endif
+ } else if (tgetflag("LC")) {
+#ifdef IUCLC
+ mode.c_iflag &= ~IUCLC;
+ mode.c_oflag &= ~OLCUC;
+#endif
+ }
+ mode.c_iflag &= ~(PARMRK | INPCK);
+ mode.c_lflag |= ICANON;
+ if (tgetflag("EP")) {
+ mode.c_cflag |= PARENB;
+ mode.c_cflag &= ~PARODD;
+ }
+ if (tgetflag("OP")) {
+ mode.c_cflag |= PARENB;
+ mode.c_cflag |= PARODD;
+ }
+
+#ifdef ONLCR
+ mode.c_oflag |= ONLCR;
+#endif
+ mode.c_iflag |= ICRNL;
+ mode.c_lflag |= ECHO;
+ mode.c_oflag |= OXTABS;
+ if (tgetflag("NL")) { /* Newline, not linefeed. */
+#ifdef ONLCR
+ mode.c_oflag &= ~ONLCR;
+#endif
+ mode.c_iflag &= ~ICRNL;
+ }
+ if (tgetflag("HD")) /* Half duplex. */
+ mode.c_lflag &= ~ECHO;
+ if (tgetflag("pt")) /* Print tabs. */
+ mode.c_oflag &= ~OXTABS;
+ mode.c_lflag |= (ECHOE | ECHOK);
+}
+
+/* Output startup string. */
+void
+set_init()
+{
+ char *bp, buf[1024];
+ int settle;
+
+ bp = buf;
+ if (tgetstr("pc", &bp) != 0) /* Get/set pad character. */
+ PC = buf[0];
+
+#ifdef TAB3
+ if (oldmode.c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) {
+ oldmode.c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET);
+ tcsetattr(STDERR_FILENO, TCSADRAIN, &oldmode);
+ }
+#endif
+ settle = set_tabs();
+
+ if (isreset) {
+ bp = buf;
+ if (tgetstr("rs", &bp) != 0 || tgetstr("is", &bp) != 0) {
+ tputs(buf, 0, outc);
+ settle = 1;
+ }
+ bp = buf;
+ if (tgetstr("rf", &bp) != 0 || tgetstr("if", &bp) != 0) {
+ cat(buf);
+ settle = 1;
+ }
+ }
+
+ if (settle) {
+ (void)putc('\r', stderr);
+ (void)fflush(stderr);
+ (void)sleep(1); /* Settle the terminal. */
+ }
+}
+
+/*
+ * Set the hardware tabs on the terminal, using the ct (clear all tabs),
+ * st (set one tab) and ch (horizontal cursor addressing) capabilities.
+ * This is done before if and is, so they can patch in case we blow this.
+ * Return nonzero if we set any tab stops, zero if not.
+ */
+int
+set_tabs()
+{
+ int c;
+ char *capsp, *clear_tabs;
+ char *set_column, *set_pos, *set_tab, *tg_out;
+ char caps[1024];
+
+ capsp = caps;
+ set_tab = tgetstr("st", &capsp);
+
+ if (set_tab && (clear_tabs = tgetstr("ct", &capsp))) {
+ (void)putc('\r', stderr); /* Force to left margin. */
+ tputs(clear_tabs, 0, outc);
+ }
+
+ set_column = tgetstr("ch", &capsp);
+ set_pos = set_column ? NULL : tgetstr("cm", &capsp);
+
+ if (set_tab) {
+ for (c = 8; c < columns; c += 8) {
+ /*
+ * Get to the right column. "OOPS" is returned by
+ * tgoto() if it can't do the job. (*snarl*)
+ */
+ tg_out = "OOPS";
+ if (set_column)
+ tg_out = tgoto(set_column, 0, c);
+ if (*tg_out == 'O' && set_pos)
+ tg_out = tgoto(set_pos, c, lines - 1);
+ if (*tg_out != 'O')
+ tputs(tg_out, 1, outc);
+ else
+ (void)fprintf(stderr, "%s", " ");
+ /* Set the tab. */
+ tputs(set_tab, 0, outc);
+ }
+ putc('\r', stderr);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/tset/term.c b/usr.bin/tset/term.c
new file mode 100644
index 0000000..dc652e3
--- /dev/null
+++ b/usr.bin/tset/term.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)term.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <ttyent.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+char tbuf[1024]; /* Termcap entry. */
+
+char *askuser __P((char *));
+char *ttys __P((char *));
+
+/*
+ * Figure out what kind of terminal we're dealing with, and then read in
+ * its termcap entry.
+ */
+char *
+get_termcap_entry(userarg, tcapbufp)
+ char *userarg, **tcapbufp;
+{
+ struct ttyent *t;
+ int rval;
+ char *p, *ttype, *ttypath;
+
+ if (userarg) {
+ ttype = userarg;
+ goto found;
+ }
+
+ /* Try the environment. */
+ if (ttype = getenv("TERM"))
+ goto map;
+
+ /* Try ttyname(3); check for dialup or other mapping. */
+ if (ttypath = ttyname(STDERR_FILENO)) {
+ if (p = rindex(ttypath, '/'))
+ ++p;
+ else
+ p = ttypath;
+ if ((t = getttynam(p))) {
+ ttype = t->ty_type;
+ goto map;
+ }
+ }
+
+ /* If still undefined, use "unknown". */
+ ttype = "unknown";
+
+map: ttype = mapped(ttype);
+
+ /*
+ * If not a path, remove TERMCAP from the environment so we get a
+ * real entry from /etc/termcap. This prevents us from being fooled
+ * by out of date stuff in the environment.
+ */
+found: if ((p = getenv("TERMCAP")) != NULL && *p != '/')
+ unsetenv("TERMCAP");
+
+ /*
+ * ttype now contains a pointer to the type of the terminal.
+ * If the first character is '?', ask the user.
+ */
+ if (ttype[0] == '?')
+ if (ttype[1] != '\0')
+ ttype = askuser(ttype + 1);
+ else
+ ttype = askuser(NULL);
+
+ /* Find the termcap entry. If it doesn't exist, ask the user. */
+ while ((rval = tgetent(tbuf, ttype)) == 0) {
+ (void)fprintf(stderr,
+ "tset: terminal type %s is unknown\n", ttype);
+ ttype = askuser(NULL);
+ }
+ if (rval == -1)
+ err("termcap: %s", strerror(errno ? errno : ENOENT));
+ *tcapbufp = tbuf;
+ return (ttype);
+}
+
+/* Prompt the user for a terminal type. */
+char *
+askuser(dflt)
+ char *dflt;
+{
+ static char answer[256];
+ char *p;
+
+ /* We can get recalled; if so, don't continue uselessly. */
+ if (feof(stdin) || ferror(stdin)) {
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ }
+ for (;;) {
+ if (dflt)
+ (void)fprintf(stderr, "Terminal type? [%s] ", dflt);
+ else
+ (void)fprintf(stderr, "Terminal type? ");
+ (void)fflush(stderr);
+
+ if (fgets(answer, sizeof(answer), stdin) == NULL) {
+ if (dflt == NULL) {
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ }
+ return (dflt);
+ }
+
+ if (p = index(answer, '\n'))
+ *p = '\0';
+ if (answer[0])
+ return (answer);
+ if (dflt != NULL)
+ return (dflt);
+ }
+}
diff --git a/usr.bin/tset/tset.1 b/usr.bin/tset/tset.1
new file mode 100644
index 0000000..16b7344
--- /dev/null
+++ b/usr.bin/tset/tset.1
@@ -0,0 +1,405 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tset.1 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt TSET 1
+.Os BSD 4
+.Sh NAME
+.Nm tset
+.Nd terminal initialization
+.Sh SYNOPSIS
+.Nm tset
+.Op Fl IQrSs
+.Op Fl
+.Op Fl e Ar ch
+.Op Fl i Ar ch
+.Op Fl k Ar ch
+.Op Fl m Ar mapping
+.Op Ar terminal
+.br
+.Nm reset
+.Op Fl IQrSs
+.Op Fl
+.Op Fl e Ar ch
+.Op Fl i Ar ch
+.Op Fl k Ar ch
+.Op Fl m Ar mapping
+.Op Ar terminal
+.Sh DESCRIPTION
+.Nm Tset
+initializes terminals.
+.Nm Tset
+first determines the type of terminal that you are using.
+This determination is done as follows, using the first terminal type found.
+.sp
+.Bl -bullet -compact -offset indent
+.It
+The
+.Ar terminal
+argument specified on the command line.
+.It
+The value of the
+.Ev TERM
+environmental variable.
+.It
+The terminal type associated with the standard error output device in the
+.Pa /etc/ttys
+file.
+.It
+The default terminal type, ``unknown''.
+.El
+.Pp
+If the terminal type was not specified on the command-line, the
+.Fl m
+option mappings are then applied (see below for more information).
+Then, if the terminal type begins with a question mark (``?''), the user is
+prompted for confirmation of the terminal type.
+An empty response confirms the type, or, another type can be entered to
+specify a new type.
+Once the terminal type has been determined, the termcap entry for the terminal
+is retrieved.
+If no termcap entry is found for the type, the user is prompted for another
+terminal type.
+.Pp
+Once the termcap entry is retrieved, the window size, backspace, interrupt
+and line kill characters (among many other things) are set and the terminal
+and tab initialization strings are sent to the standard error output.
+Finally, if the erase, interrupt and line kill characters have changed,
+or are not set to their default values, their values are displayed to the
+standard error output.
+.Pp
+When invoked as
+.Nm reset ,
+.Nm tset
+sets cooked and echo modes, turns off cbreak and raw modes, turns on
+newline translation and resets any unset special characters to their
+default values before doing the terminal initialization described above.
+This is useful after a program dies leaving a terminal in a abnormal state.
+Note, you may have to type
+.Dq Li <LF>reset<LF>
+(the line-feed character is normally control-J) to get the terminal
+to work, as carriage-return may no longer work in the abnormal state.
+Also, the terminal will often not echo the command.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl
+The terminal type is displayed to the standard output, and the terminal is
+not initialized in any way.
+.It Fl e
+Set the erase character to
+.Ar ch .
+.It Fl I
+Do not send the terminal or tab initialization strings to the terminal.
+.It Fl i
+Set the interrupt character to
+.Ar ch .
+.It Fl k
+Set the line kill character to
+.Ar ch .
+.It Fl m
+Specify a mapping from a port type to a terminal.
+See below for more information.
+.It Fl Q
+Don't display any values for the erase, interrupt and line kill characters.
+.It Fl r
+Print the terminal type to the standard error output.
+.It Fl S
+Print the terminal type and the termcap entry to the standard output.
+See the section below on setting the environment for details.
+.It Fl s
+Print the sequence of shell commands to initialize the environment variables
+.Ev TERM
+and
+.Ev TERMCAP
+to the standard output.
+See the section below on setting the environment for details.
+.El
+.Pp
+The arguments for the
+.Fl e ,
+.Fl i
+and
+.Fl k
+options may either be entered as actual characters or by using the
+.Dq hat
+notation, i.e. control-h may be specified as
+.Dq Li ^H
+or
+.Dq Li ^h .
+.Sh SETTING THE ENVIRONMENT
+It is often desirable to enter the terminal type and information about
+the terminal's capabilities into the shell's environment.
+This is done using the
+.Fl S
+and
+.Fl s
+options.
+.Pp
+When the
+.Fl S
+option is specified, the terminal type and the termcap entry are written
+to the standard output, separated by a space and without a terminating
+newline.
+This can be assigned to an array by
+.Nm csh
+and
+.Nm ksh
+users and then used like any other shell array.
+.Pp
+When the
+.Fl s
+option is specified, the commands to enter the information into the
+shell's environment are written to the standard output.
+If the
+.Ev SHELL
+environmental variable ends in ``csh'', the commands are for the
+.Nm csh ,
+otherwise, they are for
+.Xr sh .
+Note, the
+.Nm csh
+commands set and unset the shell variable
+.Dq noglob ,
+leaving it unset.
+The following line in the
+.Pa .login
+or
+.Pa .profile
+files will initialize the environment correctly:
+.Bd -literal -offset indent
+eval \`tset -s options ... \`
+.Ed
+.Pp
+To demonstrate a simple use of the
+.Fl S
+option, the following lines in the
+.Pa .login
+file have an equivalent effect:
+.Bd -literal -offset indent
+set noglob
+set term=(`tset -S options ...`)
+setenv TERM $term[1]
+setenv TERMCAP "$term[2]"
+unset term
+unset noglob
+.Ed
+.Sh TERMINAL TYPE MAPPING
+When the terminal is not hardwired into the system (or the current system
+information is incorrect) the terminal type derived from the
+.Pa /etc/ttys
+file or the
+.Ev TERM
+environmental variable is often something generic like
+.Dq network ,
+.Dq dialup ,
+or
+.Dq unknown .
+When
+.Nm tset
+is used in a startup script
+.Pf ( Pa .profile
+for
+.Xr sh 1
+users or
+.Pa .login
+for
+.Xr csh 1
+users) it is often desirable to provide information about the type of
+terminal used on such ports.
+The purpose of the
+.Fl m
+option is to
+.Dq map
+from some set of conditions to a terminal type, that is, to
+tell
+.Nm tset
+``If I'm on this port at a particular speed, guess that I'm on that
+kind of terminal''.
+.Pp
+The argument to the
+.Fl m
+option consists of an optional port type, an optional operator, an optional
+baud rate specification, an optional colon (``:'') character and a terminal
+type.
+The port type is a string (delimited by either the operator or the colon
+character).
+The operator may be any combination of:
+.Dq Li \&> ,
+.Dq Li \&< ,
+.Dq Li \&@ ,
+and
+.Dq Li \&! ;
+.Dq Li \&>
+means greater than,
+.Dq Li \&<
+means less than,
+.Dq Li \&@
+means equal to
+and
+.Dq Li \&!
+inverts the sense of the test.
+The baud rate is specified as a number and is compared with the speed
+of the standard error output (which should be the control terminal).
+The terminal type is a string.
+.Pp
+If the terminal type is not specified on the command line, the
+.Fl m
+mappings are applied to the terminal type.
+If the port type and baud rate match the mapping, the terminal type specified
+in the mapping replaces the current type.
+If more than one mapping is specified, the first applicable mapping is used.
+.Pp
+For example, consider the following mapping:
+.Dq Li dialup>9600:vt100 .
+The port type is
+.Dq Li dialup ,
+the operator is
+.Dq Li > ,
+the baud rate specification is
+.Dq Li 9600 ,
+and the terminal type is
+.Dq Li vt100 .
+The result of this mapping is to specify that if the terminal type is
+.Dq Li dialup ,
+and the baud rate is greater than 9600 baud, a terminal type of
+.Dq Li vt100
+will be used.
+.Pp
+If no port type is specified, the terminal type will match any port type,
+for example,
+.Dq Li -m dialup:vt100 -m :?xterm
+will cause any dialup port, regardless of baud rate, to match the terminal
+type
+.Dq Li vt100 ,
+and any non-dialup port type to match the terminal type
+.Dq Li ?xterm .
+Note, because of the leading question mark, the user will be
+queried on a default port as to whether they are actually using an
+.Ar xterm
+terminal.
+.Pp
+No whitespace characters are permitted in the
+.Fl m
+option argument.
+Also, to avoid problems with metacharacters, it is suggested that the entire
+.Fl m
+option argument be placed within single quote characters, and that
+.Nm csh
+users insert a backslash character (``\e'') before any exclamation
+marks (``!'').
+.Sh ENVIRONMENT
+The
+.Nm tset
+command utilizes the
+.Ev SHELL
+and
+.Ev TERM
+environment variables.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/termcap -compact
+.It Pa /etc/ttys
+system port name to terminal type mapping database
+.It Pa /usr/share/misc/termcap
+terminal capability database
+.El
+.Sh SEE ALSO
+.Xr csh 1 ,
+.Xr sh 1 ,
+.Xr stty 1 ,
+.Xr tty 4 ,
+.Xr termcap 5 ,
+.Xr ttys 5 ,
+.Xr environ 7
+.Sh HISTORY
+The
+.Nm tset
+command appeared in
+.Bx 3.0 .
+.Sh COMPATIBILITY
+The
+.Fl A ,
+.Fl E ,
+.Fl h ,
+.Fl u
+and
+.Fl v
+options have been deleted from the
+.Nm tset
+utility.
+None of them were documented in 4.3BSD and all are of limited utility at
+best.
+The
+.Fl a ,
+.Fl d
+and
+.Fl p
+options are similarly not documented or useful, but were retained as they
+appear to be in widespread use.
+It is strongly recommended that any usage of these three options be
+changed to use the
+.Fl m
+option instead.
+The
+.Fl n
+option remains, but has no effect.
+It is still permissible to specify the
+.Fl e ,
+.Fl i
+and
+.Fl k
+options without arguments, although it is strongly recommended that such
+usage be fixed to explicitly specify the character.
+.Pp
+Executing
+.Nm tset
+as
+.Nm reset
+no longer implies the
+.Fl Q
+option.
+Also, the interaction between the
+.Fl
+option and the
+.Ar terminal
+argument in some historic implementations of
+.Nm tset
+has been removed.
+.Pp
+Finally, the
+.Nm tset
+implementation has been completely redone (as part of the addition to the
+system of a
+.St -p1003.1-88
+compliant terminal interface) and will no longer compile on systems with
+older terminal interfaces.
diff --git a/usr.bin/tset/tset.c b/usr.bin/tset/tset.c
new file mode 100644
index 0000000..46c2544
--- /dev/null
+++ b/usr.bin/tset/tset.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tset.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "extern.h"
+
+void obsolete __P((char *[]));
+void report __P((char *, int, u_int));
+void usage __P((void));
+
+struct termios mode, oldmode;
+
+int erasechar; /* new erase character */
+int intrchar; /* new interrupt character */
+int isreset; /* invoked as reset */
+int killchar; /* new kill character */
+int lines, columns; /* window size */
+speed_t Ospeed;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef TIOCGWINSZ
+ struct winsize win;
+#endif
+ int ch, noinit, noset, quiet, Sflag, sflag, showterm, usingupper;
+ char savech, *p, *t, *tcapbuf, *ttype;
+
+ if (tcgetattr(STDERR_FILENO, &mode) < 0)
+ err("standard error: %s", strerror(errno));
+
+ oldmode = mode;
+ Ospeed = cfgetospeed(&mode);
+
+ if (p = strrchr(*argv, '/'))
+ ++p;
+ else
+ p = *argv;
+ usingupper = isupper(*p);
+ if (!strcasecmp(p, "reset")) {
+ isreset = 1;
+ reset_mode();
+ }
+
+ obsolete(argv);
+ noinit = noset = quiet = Sflag = sflag = showterm = 0;
+ while ((ch = getopt(argc, argv, "-a:d:e:Ii:k:m:np:QSrs")) != EOF) {
+ switch (ch) {
+ case '-': /* display term only */
+ noset = 1;
+ break;
+ case 'a': /* OBSOLETE: map identifier to type */
+ add_mapping("arpanet", optarg);
+ break;
+ case 'd': /* OBSOLETE: map identifier to type */
+ add_mapping("dialup", optarg);
+ break;
+ case 'e': /* erase character */
+ erasechar = optarg[0] == '^' && optarg[1] != '\0' ?
+ optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
+ optarg[0];
+ break;
+ case 'I': /* no initialization strings */
+ noinit = 1;
+ break;
+ case 'i': /* interrupt character */
+ intrchar = optarg[0] == '^' && optarg[1] != '\0' ?
+ optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
+ optarg[0];
+ break;
+ case 'k': /* kill character */
+ killchar = optarg[0] == '^' && optarg[1] != '\0' ?
+ optarg[1] == '?' ? '\177' : CTRL(optarg[1]) :
+ optarg[0];
+ break;
+ case 'm': /* map identifier to type */
+ add_mapping(NULL, optarg);
+ break;
+ case 'n': /* OBSOLETE: set new tty driver */
+ break;
+ case 'p': /* OBSOLETE: map identifier to type */
+ add_mapping("plugboard", optarg);
+ break;
+ case 'Q': /* don't output control key settings */
+ quiet = 1;
+ break;
+ case 'S': /* output TERM/TERMCAP strings */
+ Sflag = 1;
+ break;
+ case 'r': /* display term on stderr */
+ showterm = 1;
+ break;
+ case 's': /* output TERM/TERMCAP strings */
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+
+ ttype = get_termcap_entry(*argv, &tcapbuf);
+
+ if (!noset) {
+ columns = tgetnum("co");
+ lines = tgetnum("li");
+
+#ifdef TIOCGWINSZ
+ /* Set window size */
+ (void)ioctl(STDERR_FILENO, TIOCGWINSZ, &win);
+ if (win.ws_row == 0 && win.ws_col == 0 &&
+ lines > 0 && columns > 0) {
+ win.ws_row = lines;
+ win.ws_col = columns;
+ (void)ioctl(STDERR_FILENO, TIOCSWINSZ, &win);
+ }
+#endif
+ set_control_chars();
+ set_conversions(usingupper);
+
+ if (!noinit)
+ set_init();
+
+ /* Set the modes if they've changed. */
+ if (memcmp(&mode, &oldmode, sizeof(mode)))
+ tcsetattr(STDERR_FILENO, TCSADRAIN, &mode);
+ }
+
+ if (noset)
+ (void)printf("%s\n", ttype);
+ else {
+ if (showterm)
+ (void)fprintf(stderr, "Terminal type is %s.\n", ttype);
+ /*
+ * If erase, kill and interrupt characters could have been
+ * modified and not -Q, display the changes.
+ */
+ if (!quiet) {
+ report("Erase", VERASE, CERASE);
+ report("Kill", VKILL, CKILL);
+ report("Interrupt", VINTR, CINTR);
+ }
+ }
+
+ if (Sflag) {
+ (void)printf("%s ", ttype);
+ wrtermcap(tcapbuf);
+ }
+
+ if (sflag) {
+ /*
+ * Figure out what shell we're using. A hack, we look for an
+ * environmental variable SHELL ending in "csh".
+ */
+ if ((p = getenv("SHELL")) &&
+ !strcmp(p + strlen(p) - 3, "csh")) {
+ p = "set noglob;\nsetenv TERM %s;\nsetenv TERMCAP '";
+ t = "';\nunset noglob;\n";
+ } else {
+ p = "TERM=%s;\nTERMCAP='";
+ t = "';\nexport TERMCAP TERM;\n";
+ }
+ (void)printf(p, ttype);
+ wrtermcap(tcapbuf);
+ (void)printf(t);
+ }
+
+ exit(0);
+}
+
+/*
+ * Tell the user if a control key has been changed from the default value.
+ */
+void
+report(name, which, def)
+ char *name;
+ int which;
+ u_int def;
+{
+ u_int old, new;
+
+ new = mode.c_cc[which];
+ old = oldmode.c_cc[which];
+
+ if (old == new && old == def)
+ return;
+
+ (void)fprintf(stderr, "%s %s ", name, old == new ? "is" : "set to");
+
+ if (new == 010)
+ (void)fprintf(stderr, "backspace.\n");
+ else if (new == 0177)
+ (void)fprintf(stderr, "delete.\n");
+ else if (new < 040) {
+ new ^= 0100;
+ (void)fprintf(stderr, "control-%c (^%c).\n", new, new);
+ } else
+ (void)fprintf(stderr, "%c.\n", new);
+}
+
+/*
+ * Convert the obsolete argument form into something that getopt can handle.
+ * This means that -e, -i and -k get default arguments supplied for them.
+ */
+void
+obsolete(argv)
+ char *argv[];
+{
+ for (; *argv; ++argv) {
+ if (argv[0][0] != '-' || argv[1] && argv[1][0] != '-' ||
+ argv[0][1] != 'e' && argv[0][1] != 'i' &&
+ argv[0][1] != 'k' || argv[0][2] != '\0')
+ continue;
+ switch(argv[0][1]) {
+ case 'e':
+ argv[0] = "-e^H";
+ break;
+ case 'i':
+ argv[0] = "-i^C";
+ break;
+ case 'k':
+ argv[0] = "-k^U";
+ break;
+ }
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: tset [-IQrSs] [-] [-e ch] [-i ch] [-k ch] [-m mapping] [terminal]\n");
+ exit(1);
+}
diff --git a/usr.bin/tset/wrterm.c b/usr.bin/tset/wrterm.c
new file mode 100644
index 0000000..5c33b49
--- /dev/null
+++ b/usr.bin/tset/wrterm.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wrterm.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "extern.h"
+
+/*
+ * Output termcap entry to stdout, quoting characters that would give the
+ * shell problems and omitting empty fields.
+ */
+void
+wrtermcap(bp)
+ char *bp;
+{
+ register int ch;
+ register char *p;
+ char *t, *sep;
+
+ /* Find the end of the terminal names. */
+ if ((t = index(bp, ':')) == NULL)
+ err("termcap names not colon terminated");
+ *t++ = '\0';
+
+ /* Output terminal names that don't have whitespace. */
+ sep = "";
+ while ((p = strsep(&bp, "|")) != NULL)
+ if (*p != '\0' && strpbrk(p, " \t") == NULL) {
+ (void)printf("%s%s", sep, p);
+ sep = "|";
+ }
+ (void)putchar(':');
+
+ /*
+ * Output fields, transforming any dangerous characters. Skip
+ * empty fields or fields containing only whitespace.
+ */
+ while ((p = strsep(&t, ":")) != NULL) {
+ while ((ch = *p) != '\0' && isspace(ch))
+ ++p;
+ if (ch == '\0')
+ continue;
+ while ((ch = *p++) != '\0')
+ switch(ch) {
+ case '\033':
+ (void)printf("\\E");
+ case ' ': /* No spaces. */
+ (void)printf("\\040");
+ break;
+ case '!': /* No csh history chars. */
+ (void)printf("\\041");
+ break;
+ case ',': /* No csh history chars. */
+ (void)printf("\\054");
+ break;
+ case '"': /* No quotes. */
+ (void)printf("\\042");
+ break;
+ case '\'': /* No quotes. */
+ (void)printf("\\047");
+ break;
+ case '`': /* No quotes. */
+ (void)printf("\\140");
+ break;
+ case '\\': /* Anything following is OK. */
+ case '^':
+ (void)putchar(ch);
+ if ((ch = *p++) == '\0')
+ break;
+ /* FALLTHROUGH */
+ default:
+ (void)putchar(ch);
+ }
+ (void)putchar(':');
+ }
+}
diff --git a/usr.bin/tsort/Makefile b/usr.bin/tsort/Makefile
new file mode 100644
index 0000000..e00f9ac
--- /dev/null
+++ b/usr.bin/tsort/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= tsort
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tsort/tsort.1 b/usr.bin/tsort/tsort.1
new file mode 100644
index 0000000..f3eb74b
--- /dev/null
+++ b/usr.bin/tsort/tsort.1
@@ -0,0 +1,83 @@
+.\" Copyright (c) 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This manual is derived from one contributed to Berkeley by
+.\" Michael Rendell of Memorial University of Newfoundland.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tsort.1 8.3 (Berkeley) 4/1/94
+.\"
+.Dd April 1, 1994
+.Dt TSORT 1
+.Os
+.Sh NAME
+.Nm tsort
+.Nd topological sort of a directed graph
+.Sh SYNOPSIS
+.Nm tsort
+.Op Fl l
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Tsort
+takes a list of pairs of node names representing directed arcs in
+a graph and prints the nodes in topological order on standard output.
+Input is taken from the named
+.Ar file ,
+or from standard input if no file
+is given.
+.Pp
+Node names in the input are separated by white space and there must
+be an even number of node pairs.
+.Pp
+Presence of a node in a graph can be represented by an arc from the node
+to itself.
+This is useful when a node is not connected to any other nodes.
+.Pp
+If the graph contains a cycle (and therefore cannot be properly sorted),
+one of the arcs in the cycle is ignored and the sort continues.
+Cycles are reported on standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl l
+Search for and display the longest cycle.
+Can take a very long time.
+.El
+.Sh SEE ALSO
+.Xr ar 1
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
+This
+.Nm tsort
+command and manual page are derived from sources contributed to Berkeley by
+Michael Rendell of Memorial University of Newfoundland.
diff --git a/usr.bin/tsort/tsort.c b/usr.bin/tsort/tsort.c
new file mode 100644
index 0000000..c1fc8e8
--- /dev/null
+++ b/usr.bin/tsort/tsort.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tsort.c 8.2 (Berkeley) 3/30/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Topological sort. Input is a list of pairs of strings separated by
+ * white space (spaces, tabs, and/or newlines); strings are written to
+ * standard output in sorted order, one per line.
+ *
+ * usage:
+ * tsort [-l] [inputfile]
+ * If no input file is specified, standard input is read.
+ *
+ * Should be compatable with AT&T tsort HOWEVER the output is not identical
+ * (i.e. for most graphs there is more than one sorted order, and this tsort
+ * usually generates a different one then the AT&T tsort). Also, cycle
+ * reporting seems to be more accurate in this version (the AT&T tsort
+ * sometimes says a node is in a cycle when it isn't).
+ *
+ * Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
+ */
+#define HASHSIZE 53 /* doesn't need to be big */
+#define NF_MARK 0x1 /* marker for cycle detection */
+#define NF_ACYCLIC 0x2 /* this node is cycle free */
+#define NF_NODEST 0x4 /* Unreachable */
+
+
+typedef struct node_str NODE;
+
+struct node_str {
+ NODE **n_prevp; /* pointer to previous node's n_next */
+ NODE *n_next; /* next node in graph */
+ NODE **n_arcs; /* array of arcs to other nodes */
+ int n_narcs; /* number of arcs in n_arcs[] */
+ int n_arcsize; /* size of n_arcs[] array */
+ int n_refcnt; /* # of arcs pointing to this node */
+ int n_flags; /* NF_* */
+ char n_name[1]; /* name of this node */
+};
+
+typedef struct _buf {
+ char *b_buf;
+ int b_bsize;
+} BUF;
+
+DB *db;
+NODE *graph, **cycle_buf, **longest_cycle;
+int debug, longest;
+
+void add_arc __P((char *, char *));
+int find_cycle __P((NODE *, NODE *, int, int));
+NODE *get_node __P((char *));
+void *grow_buf __P((void *, int));
+void remove_node __P((NODE *));
+void tsort __P((void));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register BUF *b;
+ register int c, n;
+ FILE *fp;
+ int bsize, ch, nused;
+ BUF bufs[2];
+
+ while ((ch = getopt(argc, argv, "dl")) != EOF)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'l':
+ longest = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 0:
+ fp = stdin;
+ break;
+ case 1:
+ if ((fp = fopen(*argv, "r")) == NULL)
+ err(1, "%s", *argv);
+ break;
+ default:
+ usage();
+ }
+
+ for (b = bufs, n = 2; --n >= 0; b++)
+ b->b_buf = grow_buf(NULL, b->b_bsize = 1024);
+
+ /* parse input and build the graph */
+ for (n = 0, c = getc(fp);;) {
+ while (c != EOF && isspace(c))
+ c = getc(fp);
+ if (c == EOF)
+ break;
+
+ nused = 0;
+ b = &bufs[n];
+ bsize = b->b_bsize;
+ do {
+ b->b_buf[nused++] = c;
+ if (nused == bsize)
+ b->b_buf = grow_buf(b->b_buf, bsize *= 2);
+ c = getc(fp);
+ } while (c != EOF && !isspace(c));
+
+ b->b_buf[nused] = '\0';
+ b->b_bsize = bsize;
+ if (n)
+ add_arc(bufs[0].b_buf, bufs[1].b_buf);
+ n = !n;
+ }
+ (void)fclose(fp);
+ if (n)
+ errx(1, "odd data count");
+
+ /* do the sort */
+ tsort();
+ exit(0);
+}
+
+/* double the size of oldbuf and return a pointer to the new buffer. */
+void *
+grow_buf(bp, size)
+ void *bp;
+ int size;
+{
+ if ((bp = realloc(bp, (u_int)size)) == NULL)
+ err(1, NULL);
+ return (bp);
+}
+
+/*
+ * add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in
+ * the graph, then add them.
+ */
+void
+add_arc(s1, s2)
+ char *s1, *s2;
+{
+ register NODE *n1;
+ NODE *n2;
+ int bsize, i;
+
+ n1 = get_node(s1);
+
+ if (!strcmp(s1, s2))
+ return;
+
+ n2 = get_node(s2);
+
+ /*
+ * Check if this arc is already here.
+ */
+ for (i = 0; i < n1->n_narcs; i++)
+ if (n1->n_arcs[i] == n2)
+ return;
+ /*
+ * Add it.
+ */
+ if (n1->n_narcs == n1->n_arcsize) {
+ if (!n1->n_arcsize)
+ n1->n_arcsize = 10;
+ bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2;
+ n1->n_arcs = grow_buf(n1->n_arcs, bsize);
+ n1->n_arcsize = bsize / sizeof(*n1->n_arcs);
+ }
+ n1->n_arcs[n1->n_narcs++] = n2;
+ ++n2->n_refcnt;
+}
+
+/* Find a node in the graph (insert if not found) and return a pointer to it. */
+NODE *
+get_node(name)
+ char *name;
+{
+ DBT data, key;
+ NODE *n;
+
+ if (db == NULL &&
+ (db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL)
+ err(1, "db: %s", name);
+
+ key.data = name;
+ key.size = strlen(name) + 1;
+
+ switch ((*db->get)(db, &key, &data, 0)) {
+ case 0:
+ bcopy(data.data, &n, sizeof(n));
+ return (n);
+ case 1:
+ break;
+ default:
+ case -1:
+ err(1, "db: %s", name);
+ }
+
+ if ((n = malloc(sizeof(NODE) + key.size)) == NULL)
+ err(1, NULL);
+
+ n->n_narcs = 0;
+ n->n_arcsize = 0;
+ n->n_arcs = NULL;
+ n->n_refcnt = 0;
+ n->n_flags = 0;
+ bcopy(name, n->n_name, key.size);
+
+ /* Add to linked list. */
+ if ((n->n_next = graph) != NULL)
+ graph->n_prevp = &n->n_next;
+ n->n_prevp = &graph;
+ graph = n;
+
+ /* Add to hash table. */
+ data.data = &n;
+ data.size = sizeof(n);
+ if ((*db->put)(db, &key, &data, 0))
+ err(1, "db: %s", name);
+ return (n);
+}
+
+
+/*
+ * Clear the NODEST flag from all nodes.
+ */
+void
+clear_cycle()
+{
+ NODE *n;
+
+ for (n = graph; n != NULL; n = n->n_next)
+ n->n_flags &= ~NF_NODEST;
+}
+
+/* do topological sort on graph */
+void
+tsort()
+{
+ register NODE *n, *next;
+ register int cnt, i;
+
+ while (graph != NULL) {
+ /*
+ * Keep getting rid of simple cases until there are none left,
+ * if there are any nodes still in the graph, then there is
+ * a cycle in it.
+ */
+ do {
+ for (cnt = 0, n = graph; n != NULL; n = next) {
+ next = n->n_next;
+ if (n->n_refcnt == 0) {
+ remove_node(n);
+ ++cnt;
+ }
+ }
+ } while (graph != NULL && cnt);
+
+ if (graph == NULL)
+ break;
+
+ if (!cycle_buf) {
+ /*
+ * Allocate space for two cycle logs - one to be used
+ * as scratch space, the other to save the longest
+ * cycle.
+ */
+ for (cnt = 0, n = graph; n != NULL; n = n->n_next)
+ ++cnt;
+ cycle_buf = malloc((u_int)sizeof(NODE *) * cnt);
+ longest_cycle = malloc((u_int)sizeof(NODE *) * cnt);
+ if (cycle_buf == NULL || longest_cycle == NULL)
+ err(1, NULL);
+ }
+ for (n = graph; n != NULL; n = n->n_next)
+ if (!(n->n_flags & NF_ACYCLIC))
+ if (cnt = find_cycle(n, n, 0, 0)) {
+ warnx("cycle in data");
+ for (i = 0; i < cnt; i++)
+ warnx("%s",
+ longest_cycle[i]->n_name);
+ remove_node(n);
+ clear_cycle();
+ break;
+ } else {
+ /* to avoid further checks */
+ n->n_flags |= NF_ACYCLIC;
+ clear_cycle();
+ }
+
+ if (n == NULL)
+ errx(1, "internal error -- could not find cycle");
+ }
+}
+
+/* print node and remove from graph (does not actually free node) */
+void
+remove_node(n)
+ register NODE *n;
+{
+ register NODE **np;
+ register int i;
+
+ (void)printf("%s\n", n->n_name);
+ for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++)
+ --(*np)->n_refcnt;
+ n->n_narcs = 0;
+ *n->n_prevp = n->n_next;
+ if (n->n_next)
+ n->n_next->n_prevp = n->n_prevp;
+}
+
+
+/* look for the longest? cycle from node from to node to. */
+int
+find_cycle(from, to, longest_len, depth)
+ NODE *from, *to;
+ int depth, longest_len;
+{
+ register NODE **np;
+ register int i, len;
+
+ /*
+ * avoid infinite loops and ignore portions of the graph known
+ * to be acyclic
+ */
+ if (from->n_flags & (NF_NODEST|NF_MARK|NF_ACYCLIC))
+ return (0);
+ from->n_flags |= NF_MARK;
+
+ for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) {
+ cycle_buf[depth] = *np;
+ if (*np == to) {
+ if (depth + 1 > longest_len) {
+ longest_len = depth + 1;
+ (void)memcpy((char *)longest_cycle,
+ (char *)cycle_buf,
+ longest_len * sizeof(NODE *));
+ }
+ } else {
+ if ((*np)->n_flags & (NF_MARK|NF_ACYCLIC|NF_NODEST))
+ continue;
+ len = find_cycle(*np, to, longest_len, depth + 1);
+
+ if (debug)
+ (void)printf("%*s %s->%s %d\n", depth, "",
+ from->n_name, to->n_name, len);
+
+ if (len == 0)
+ (*np)->n_flags |= NF_NODEST;
+
+ if (len > longest_len)
+ longest_len = len;
+
+ if (len > 0 && !longest)
+ break;
+ }
+ }
+ from->n_flags &= ~NF_MARK;
+ return (longest_len);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: tsort [-l] [file]\n");
+ exit(1);
+}
diff --git a/usr.bin/tty/Makefile b/usr.bin/tty/Makefile
new file mode 100644
index 0000000..5b914b2
--- /dev/null
+++ b/usr.bin/tty/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= tty
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/tty/tty.1 b/usr.bin/tty/tty.1
new file mode 100644
index 0000000..9607c11
--- /dev/null
+++ b/usr.bin/tty/tty.1
@@ -0,0 +1,78 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)tty.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TTY 1
+.Os
+.Sh NAME
+.Nm tty
+.Nd return user's terminal name
+.Sh SYNOPSIS
+.Nm tty
+.Op Fl s
+.Sh DESCRIPTION
+The
+.Nm tty
+utility writes the name of the terminal attached to standard input
+to standard output.
+The name that is written is the string returned by
+.Xr ttyname 3 .
+If the standard input is not a terminal, the message ``not a tty''
+is written.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl s
+Don't write the terminal name; only the exit status is affected
+when this option is specified.
+The
+.Fl s
+option is deprecated in favor of the
+.Dq Li "test -t 0"
+command.
+.El
+.Pp
+.Nm Tty
+exits 0 if the standard input is a terminal, 1 if the standard input is
+not a terminal, and >1 if an error occurs.
+.Sh SEE ALSO
+.Xr test 1 ,
+.Xr ttyname 3
+.Sh STANDARDS
+The
+.Nm tty
+function is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/tty/tty.c b/usr.bin/tty/tty.c
new file mode 100644
index 0000000..d07cab7
--- /dev/null
+++ b/usr.bin/tty/tty.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, sflag;
+ char *t, *ttyname();
+
+ sflag = 0;
+ while ((ch = getopt(argc, argv, "s")) != EOF)
+ switch((char)ch) {
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ fputs("usage: tty [-s]\n", stderr);
+ exit(2);
+ }
+
+ t = ttyname(0);
+ if (!sflag)
+ puts(t ? t : "not a tty");
+ exit(t ? 0 : 1);
+}
diff --git a/usr.bin/ul/Makefile b/usr.bin/ul/Makefile
new file mode 100644
index 0000000..645c291
--- /dev/null
+++ b/usr.bin/ul/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= ul
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ul/ul.1 b/usr.bin/ul/ul.1
new file mode 100644
index 0000000..701797a
--- /dev/null
+++ b/usr.bin/ul/ul.1
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ul.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UL 1
+.Os BSD 4
+.Sh NAME
+.Nm ul
+.Nd do underlining
+.Sh SYNOPSIS
+.Nm ul
+.Op Fl i
+.Op Fl t Ar terminal
+.Op Ar name Ar ...
+.Sh DESCRIPTION
+.Nm Ul
+reads the named files (or standard input if none are given)
+and translates occurrences of underscores to the sequence
+which indicates underlining for the terminal in use, as specified
+by the environment variable
+.Ev TERM .
+The file
+.Pa /etc/termcap
+is read to determine the appropriate sequences for underlining.
+If the terminal is incapable of underlining, but is capable of
+a standout mode then that is used instead.
+If the terminal can overstrike,
+or handles underlining automatically,
+.Nm ul
+degenerates to
+.Xr cat 1 .
+If the terminal cannot underline, underlining is ignored.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl i
+Underlining is indicated by a separate line containing appropriate
+dashes `\-'; this is useful when you want to look at the underlining
+which is present in an
+.Xr nroff
+output stream on a crt-terminal.
+.It Fl t Ar terminal
+Overrides the terminal type specified in the environment with
+.Ar terminal .
+.El
+.Sh ENVIRONMENT
+The following environment variable is used:
+.Bl -tag -width TERM
+.It Ev TERM
+The
+.Ev TERM
+variable is used to relate a tty device
+with its device capability description (see
+.Xr termcap 5 ) .
+.Ev TERM
+is set at login time, either by the default terminal type
+specified in
+.Pa /etc/ttys
+or as set during the login process by the user in their
+.Pa login
+file (see
+.Xr setenv 1 ) .
+.El
+.Sh SEE ALSO
+.Xr man 1 ,
+.Xr nroff 1 ,
+.Xr colcrt 1
+.Sh BUGS
+.Xr Nroff
+usually outputs a series of backspaces and underlines intermixed
+with the text to indicate underlining. No attempt is made to optimize
+the backward motion.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/ul/ul.c b/usr.bin/ul/ul.c
new file mode 100644
index 0000000..100059c
--- /dev/null
+++ b/usr.bin/ul/ul.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#define IESC '\033'
+#define SO '\016'
+#define SI '\017'
+#define HFWD '9'
+#define HREV '8'
+#define FREV '7'
+#define MAXBUF 512
+
+#define NORMAL 000
+#define ALTSET 001 /* Reverse */
+#define SUPERSC 002 /* Dim */
+#define SUBSC 004 /* Dim | Ul */
+#define UNDERL 010 /* Ul */
+#define BOLD 020 /* Bold */
+
+int must_use_uc, must_overstrike;
+char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
+ *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
+ *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
+
+struct CHAR {
+ char c_mode;
+ char c_char;
+} ;
+
+struct CHAR obuf[MAXBUF];
+int col, maxcol;
+int mode;
+int halfpos;
+int upln;
+int iflag;
+
+int outchar();
+#define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar)
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ int c;
+ char *termtype;
+ FILE *f;
+ char termcap[1024];
+ char *getenv(), *strcpy();
+
+ termtype = getenv("TERM");
+ if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
+ termtype = "lpr";
+ while ((c=getopt(argc, argv, "it:T:")) != EOF)
+ switch(c) {
+
+ case 't':
+ case 'T': /* for nroff compatibility */
+ termtype = optarg;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+
+ default:
+ fprintf(stderr,
+ "usage: %s [ -i ] [ -tTerm ] file...\n",
+ argv[0]);
+ exit(1);
+ }
+
+ switch(tgetent(termcap, termtype)) {
+
+ case 1:
+ break;
+
+ default:
+ fprintf(stderr,"trouble reading termcap");
+ /* fall through to ... */
+
+ case 0:
+ /* No such terminal type - assume dumb */
+ (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
+ break;
+ }
+ initcap();
+ if ( (tgetflag("os") && ENTER_BOLD==NULL ) ||
+ (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+ must_overstrike = 1;
+ initbuf();
+ if (optind == argc)
+ filter(stdin);
+ else for (; optind<argc; optind++) {
+ f = fopen(argv[optind],"r");
+ if (f == NULL) {
+ perror(argv[optind]);
+ exit(1);
+ } else
+ filter(f);
+ }
+ exit(0);
+}
+
+filter(f)
+ FILE *f;
+{
+ register c;
+
+ while ((c = getc(f)) != EOF) switch(c) {
+
+ case '\b':
+ if (col > 0)
+ col--;
+ continue;
+
+ case '\t':
+ col = (col+8) & ~07;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+
+ case '\r':
+ col = 0;
+ continue;
+
+ case SO:
+ mode |= ALTSET;
+ continue;
+
+ case SI:
+ mode &= ~ALTSET;
+ continue;
+
+ case IESC:
+ switch (c = getc(f)) {
+
+ case HREV:
+ if (halfpos == 0) {
+ mode |= SUPERSC;
+ halfpos--;
+ } else if (halfpos > 0) {
+ mode &= ~SUBSC;
+ halfpos--;
+ } else {
+ halfpos = 0;
+ reverse();
+ }
+ continue;
+
+ case HFWD:
+ if (halfpos == 0) {
+ mode |= SUBSC;
+ halfpos++;
+ } else if (halfpos < 0) {
+ mode &= ~SUPERSC;
+ halfpos++;
+ } else {
+ halfpos = 0;
+ fwd();
+ }
+ continue;
+
+ case FREV:
+ reverse();
+ continue;
+
+ default:
+ fprintf(stderr,
+ "Unknown escape sequence in input: %o, %o\n",
+ IESC, c);
+ exit(1);
+ }
+ continue;
+
+ case '_':
+ if (obuf[col].c_char)
+ obuf[col].c_mode |= UNDERL | mode;
+ else
+ obuf[col].c_char = '_';
+ case ' ':
+ col++;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+
+ case '\n':
+ flushln();
+ continue;
+
+ case '\f':
+ flushln();
+ putchar('\f');
+ continue;
+
+ default:
+ if (c < ' ') /* non printing */
+ continue;
+ if (obuf[col].c_char == '\0') {
+ obuf[col].c_char = c;
+ obuf[col].c_mode = mode;
+ } else if (obuf[col].c_char == '_') {
+ obuf[col].c_char = c;
+ obuf[col].c_mode |= UNDERL|mode;
+ } else if (obuf[col].c_char == c)
+ obuf[col].c_mode |= BOLD|mode;
+ else
+ obuf[col].c_mode = mode;
+ col++;
+ if (col > maxcol)
+ maxcol = col;
+ continue;
+ }
+ if (maxcol)
+ flushln();
+}
+
+flushln()
+{
+ register lastmode;
+ register i;
+ int hadmodes = 0;
+
+ lastmode = NORMAL;
+ for (i=0; i<maxcol; i++) {
+ if (obuf[i].c_mode != lastmode) {
+ hadmodes++;
+ setmode(obuf[i].c_mode);
+ lastmode = obuf[i].c_mode;
+ }
+ if (obuf[i].c_char == '\0') {
+ if (upln)
+ PRINT(CURS_RIGHT);
+ else
+ outc(' ');
+ } else
+ outc(obuf[i].c_char);
+ }
+ if (lastmode != NORMAL) {
+ setmode(0);
+ }
+ if (must_overstrike && hadmodes)
+ overstrike();
+ putchar('\n');
+ if (iflag && hadmodes)
+ iattr();
+ (void)fflush(stdout);
+ if (upln)
+ upln--;
+ initbuf();
+}
+
+/*
+ * For terminals that can overstrike, overstrike underlines and bolds.
+ * We don't do anything with halfline ups and downs, or Greek.
+ */
+overstrike()
+{
+ register int i;
+ char lbuf[256];
+ register char *cp = lbuf;
+ int hadbold=0;
+
+ /* Set up overstrike buffer */
+ for (i=0; i<maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL:
+ default:
+ *cp++ = ' ';
+ break;
+ case UNDERL:
+ *cp++ = '_';
+ break;
+ case BOLD:
+ *cp++ = obuf[i].c_char;
+ hadbold=1;
+ break;
+ }
+ putchar('\r');
+ for (*cp=' '; *cp==' '; cp--)
+ *cp = 0;
+ for (cp=lbuf; *cp; cp++)
+ putchar(*cp);
+ if (hadbold) {
+ putchar('\r');
+ for (cp=lbuf; *cp; cp++)
+ putchar(*cp=='_' ? ' ' : *cp);
+ putchar('\r');
+ for (cp=lbuf; *cp; cp++)
+ putchar(*cp=='_' ? ' ' : *cp);
+ }
+}
+
+iattr()
+{
+ register int i;
+ char lbuf[256];
+ register char *cp = lbuf;
+
+ for (i=0; i<maxcol; i++)
+ switch (obuf[i].c_mode) {
+ case NORMAL: *cp++ = ' '; break;
+ case ALTSET: *cp++ = 'g'; break;
+ case SUPERSC: *cp++ = '^'; break;
+ case SUBSC: *cp++ = 'v'; break;
+ case UNDERL: *cp++ = '_'; break;
+ case BOLD: *cp++ = '!'; break;
+ default: *cp++ = 'X'; break;
+ }
+ for (*cp=' '; *cp==' '; cp--)
+ *cp = 0;
+ for (cp=lbuf; *cp; cp++)
+ putchar(*cp);
+ putchar('\n');
+}
+
+initbuf()
+{
+
+ bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
+ col = 0;
+ maxcol = 0;
+ mode &= ALTSET;
+}
+
+fwd()
+{
+ register oldcol, oldmax;
+
+ oldcol = col;
+ oldmax = maxcol;
+ flushln();
+ col = oldcol;
+ maxcol = oldmax;
+}
+
+reverse()
+{
+ upln++;
+ fwd();
+ PRINT(CURS_UP);
+ PRINT(CURS_UP);
+ upln++;
+}
+
+initcap()
+{
+ static char tcapbuf[512];
+ char *bp = tcapbuf;
+ char *getenv(), *tgetstr();
+
+ /* This nonsense attempts to work with both old and new termcap */
+ CURS_UP = tgetstr("up", &bp);
+ CURS_RIGHT = tgetstr("ri", &bp);
+ if (CURS_RIGHT == NULL)
+ CURS_RIGHT = tgetstr("nd", &bp);
+ CURS_LEFT = tgetstr("le", &bp);
+ if (CURS_LEFT == NULL)
+ CURS_LEFT = tgetstr("bc", &bp);
+ if (CURS_LEFT == NULL && tgetflag("bs"))
+ CURS_LEFT = "\b";
+
+ ENTER_STANDOUT = tgetstr("so", &bp);
+ EXIT_STANDOUT = tgetstr("se", &bp);
+ ENTER_UNDERLINE = tgetstr("us", &bp);
+ EXIT_UNDERLINE = tgetstr("ue", &bp);
+ ENTER_DIM = tgetstr("mh", &bp);
+ ENTER_BOLD = tgetstr("md", &bp);
+ ENTER_REVERSE = tgetstr("mr", &bp);
+ EXIT_ATTRIBUTES = tgetstr("me", &bp);
+
+ if (!ENTER_BOLD && ENTER_REVERSE)
+ ENTER_BOLD = ENTER_REVERSE;
+ if (!ENTER_BOLD && ENTER_STANDOUT)
+ ENTER_BOLD = ENTER_STANDOUT;
+ if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
+ ENTER_UNDERLINE = ENTER_STANDOUT;
+ EXIT_UNDERLINE = EXIT_STANDOUT;
+ }
+ if (!ENTER_DIM && ENTER_STANDOUT)
+ ENTER_DIM = ENTER_STANDOUT;
+ if (!ENTER_REVERSE && ENTER_STANDOUT)
+ ENTER_REVERSE = ENTER_STANDOUT;
+ if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
+ EXIT_ATTRIBUTES = EXIT_STANDOUT;
+
+ /*
+ * Note that we use REVERSE for the alternate character set,
+ * not the as/ae capabilities. This is because we are modelling
+ * the model 37 teletype (since that's what nroff outputs) and
+ * the typical as/ae is more of a graphics set, not the greek
+ * letters the 37 has.
+ */
+
+ UNDER_CHAR = tgetstr("uc", &bp);
+ must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
+}
+
+outchar(c)
+ int c;
+{
+ putchar(c & 0177);
+}
+
+static int curmode = 0;
+
+outc(c)
+ int c;
+{
+ putchar(c);
+ if (must_use_uc && (curmode&UNDERL)) {
+ PRINT(CURS_LEFT);
+ PRINT(UNDER_CHAR);
+ }
+}
+
+setmode(newmode)
+ int newmode;
+{
+ if (!iflag) {
+ if (curmode != NORMAL && newmode != NORMAL)
+ setmode(NORMAL);
+ switch (newmode) {
+ case NORMAL:
+ switch(curmode) {
+ case NORMAL:
+ break;
+ case UNDERL:
+ PRINT(EXIT_UNDERLINE);
+ break;
+ default:
+ /* This includes standout */
+ PRINT(EXIT_ATTRIBUTES);
+ break;
+ }
+ break;
+ case ALTSET:
+ PRINT(ENTER_REVERSE);
+ break;
+ case SUPERSC:
+ /*
+ * This only works on a few terminals.
+ * It should be fixed.
+ */
+ PRINT(ENTER_UNDERLINE);
+ PRINT(ENTER_DIM);
+ break;
+ case SUBSC:
+ PRINT(ENTER_DIM);
+ break;
+ case UNDERL:
+ PRINT(ENTER_UNDERLINE);
+ break;
+ case BOLD:
+ PRINT(ENTER_BOLD);
+ break;
+ default:
+ /*
+ * We should have some provision here for multiple modes
+ * on at once. This will have to come later.
+ */
+ PRINT(ENTER_STANDOUT);
+ break;
+ }
+ }
+ curmode = newmode;
+}
diff --git a/usr.bin/uname/Makefile b/usr.bin/uname/Makefile
new file mode 100644
index 0000000..ae634ca
--- /dev/null
+++ b/usr.bin/uname/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uname
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1
new file mode 100644
index 0000000..50e0c52
--- /dev/null
+++ b/usr.bin/uname/uname.1
@@ -0,0 +1,97 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uname.1 8.3 (Berkeley) 4/8/94
+.\"
+.Dd "April 8, 1994"
+.Dt UNAME 1
+.Os
+.Sh NAME
+.Nm uname
+.Nd display information about the system
+.Sh SYNOPSIS
+.Nm uname
+.Op Fl amnrsv
+.Sh DESCRIPTION
+The
+.Nm uname
+command writes the name of the operating system implementation to
+standard output.
+When options are specified, strings representing one or more system
+characteristics are written to standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Behave as though the options
+.Fl m ,
+.Fl n ,
+.Fl r ,
+.Fl s ,
+and
+.Fl v
+were specified.
+.It Fl m
+Write the type of the current hardware platform to standard output.
+.It Fl n
+Write the name of the system to standard output.
+.It Fl r
+Write the current release level of the operating system
+to standard output.
+.It Fl s
+Write the name of the operating system implementation to standard output.
+.It Fl v
+Write the version level of this release of the operating system
+to standard output.
+.El
+.Pp
+If the
+.Fl a
+flag is specified, or multiple flags are specified, all
+output is written on a single line, separated by spaces.
+.Pp
+The
+.Nm uname
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr sysctl 8 ,
+.Xr sysctl 3 ,
+.Xr uname 3
+.Sh HISTORY
+The
+.Nm uname
+command appeared in 4.4BSD.
+.Sh STANDARDS
+The
+.Nm uname
+command is expected to conform to the
+.St -p1003.2
+specification.
diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c
new file mode 100644
index 0000000..2ee1547
--- /dev/null
+++ b/usr.bin/uname/uname.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uname.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#define MFLAG 0x01
+#define NFLAG 0x02
+#define RFLAG 0x04
+#define SFLAG 0x08
+#define VFLAG 0x10
+ u_int flags;
+ int ch, mib[2];
+ size_t len, tlen;
+ char *p, *prefix, buf[1024];
+
+ flags = 0;
+ while ((ch = getopt(argc, argv, "amnrsv")) != EOF)
+ switch(ch) {
+ case 'a':
+ flags |= (MFLAG | NFLAG | RFLAG | SFLAG | VFLAG);
+ break;
+ case 'm':
+ flags |= MFLAG;
+ break;
+ case 'n':
+ flags |= NFLAG;
+ break;
+ case 'r':
+ flags |= RFLAG;
+ break;
+ case 's':
+ flags |= SFLAG;
+ break;
+ case 'v':
+ flags |= VFLAG;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (!flags)
+ flags |= SFLAG;
+
+ prefix = "";
+
+ if (flags & SFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSTYPE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & NFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_HOSTNAME;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & RFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & VFLAG) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VERSION;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ for (p = buf, tlen = len; tlen--; ++p)
+ if (*p == '\n' || *p == '\t')
+ *p = ' ';
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ if (flags & MFLAG) {
+ mib[0] = CTL_HW;
+ mib[1] = HW_MACHINE;
+ len = sizeof(buf);
+ if (sysctl(mib, 2, &buf, &len, NULL, 0) == -1)
+ err(1, "sysctl");
+ (void)printf("%s%.*s", prefix, len, buf);
+ prefix = " ";
+ }
+ (void)printf("\n");
+ exit (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: uname [-amnrsv]\n");
+ exit(1);
+}
diff --git a/usr.bin/unexpand/Makefile b/usr.bin/unexpand/Makefile
new file mode 100644
index 0000000..d634f96
--- /dev/null
+++ b/usr.bin/unexpand/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= unexpand
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/unexpand/unexpand.c b/usr.bin/unexpand/unexpand.c
new file mode 100644
index 0000000..1078dc2
--- /dev/null
+++ b/usr.bin/unexpand/unexpand.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)unexpand.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * unexpand - put tabs into a file replacing blanks
+ */
+#include <stdio.h>
+
+char genbuf[BUFSIZ];
+char linebuf[BUFSIZ];
+int all;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *cp;
+
+ argc--, argv++;
+ if (argc > 0 && argv[0][0] == '-') {
+ if (strcmp(argv[0], "-a") != 0) {
+ fprintf(stderr, "usage: unexpand [ -a ] file ...\n");
+ exit(1);
+ }
+ all++;
+ argc--, argv++;
+ }
+ do {
+ if (argc > 0) {
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ perror(argv[0]);
+ exit(1);
+ }
+ argc--, argv++;
+ }
+ while (fgets(genbuf, BUFSIZ, stdin) != NULL) {
+ for (cp = linebuf; *cp; cp++)
+ continue;
+ if (cp > linebuf)
+ cp[-1] = 0;
+ tabify(all);
+ printf("%s", linebuf);
+ }
+ } while (argc > 0);
+ exit(0);
+}
+
+tabify(c)
+ char c;
+{
+ register char *cp, *dp;
+ register int dcol;
+ int ocol;
+
+ ocol = 0;
+ dcol = 0;
+ cp = genbuf, dp = linebuf;
+ for (;;) {
+ switch (*cp) {
+
+ case ' ':
+ dcol++;
+ break;
+
+ case '\t':
+ dcol += 8;
+ dcol &= ~07;
+ break;
+
+ default:
+ while (((ocol + 8) &~ 07) <= dcol) {
+ if (ocol + 1 == dcol)
+ break;
+ *dp++ = '\t';
+ ocol += 8;
+ ocol &= ~07;
+ }
+ while (ocol < dcol) {
+ *dp++ = ' ';
+ ocol++;
+ }
+ if (*cp == 0 || c == 0) {
+ strcpy(dp, cp);
+ return;
+ }
+ *dp++ = *cp;
+ ocol++, dcol++;
+ }
+ cp++;
+ }
+}
diff --git a/usr.bin/unifdef/Makefile b/usr.bin/unifdef/Makefile
new file mode 100644
index 0000000..88c14fc
--- /dev/null
+++ b/usr.bin/unifdef/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= unifdef
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/unifdef/unifdef.1 b/usr.bin/unifdef/unifdef.1
new file mode 100644
index 0000000..c27a9fd
--- /dev/null
+++ b/usr.bin/unifdef/unifdef.1
@@ -0,0 +1,165 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Dave Yost.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)unifdef.1 8.2 (Berkeley) 4/1/94
+.\"
+.Dd April 1, 1994
+.Dt UNIFDEF 1
+.Os BSD 4.3
+.Sh NAME
+.Nm unifdef
+.Nd remove ifdef'ed lines
+.Sh SYNOPSIS
+.Nm unifdef
+.Op Fl clt
+.Oo
+.Fl D Ns Ar sym
+.Fl U Ns Ar sym
+.Fl iD Ns Ar sym
+.Fl iD Ns Ar sym
+.Oc
+.Ar ...
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Unifdef
+is useful for removing ifdef'ed lines
+from a file while otherwise leaving the file alone.
+.Nm Unifdef
+acts on
+#ifdef, #ifndef, #else, and #endif lines,
+and it knows only enough about C
+to know when one of these is inactive
+because it is inside
+a comment,
+or a single or double quote.
+Parsing for quotes is very simplistic:
+when it finds an open quote,
+it ignores everything (except escaped quotes)
+until it finds a close quote, and
+it will not complain if it gets
+to the end of a line and finds no backslash for continuation.
+.Pp
+Available options:
+.Bl -tag -width Ds -compact
+.It Fl D Ns Ar sym
+.It Fl U Ns Ar sym
+Specify which symbols to define or undefine.
+and the lines inside those ifdefs will be copied to the output or removed as
+appropriate.
+The ifdef, ifndef, else, and endif lines associated with
+.Ar sym
+will also be removed.
+Ifdefs involving symbols you don't specify
+and ``#if'' control lines
+are untouched and copied out
+along with their associated
+ifdef, else, and endif lines.
+If an ifdef X occurs nested inside another ifdef X, then the
+inside ifdef is treated as if it were an unrecognized symbol.
+If the same symbol appears in more than one argument,
+the last occurrence dominates.
+.Pp
+.It Fl c
+If the
+.Fl c
+flag is specified,
+then the operation of
+.Nm unifdef
+is complemented,
+i.e. the lines that would have been removed or blanked
+are retained and vice versa.
+.Pp
+.It Fl l
+Replace removed lines with blank lines
+instead of deleting them.
+.It Fl t
+Disables parsing for C comments and quotes, which is useful
+for plain text.
+.Pp
+.It Fl iD Ns Ar sym
+.It Fl iU Ns Ar sym
+Ignore ifdefs.
+If your C code uses ifdefs to delimit non-C lines,
+such as comments
+or code which is under construction,
+then you must tell
+.Nm unifdef
+which symbols are used for that purpose so that it won't try to parse
+for quotes and comments
+inside those ifdefs.
+One specifies ignored ifdefs with
+.Fl iD Ns Ar sym
+and
+.Fl iU Ns Ar sym
+similar to
+.Fl D Ns Ar sym
+and
+.Fl U Ns Ar sym
+above.
+.El
+.Pp
+.Nm Unifdef
+copies its output to
+.Em stdout
+and will take its input from
+.Em stdin
+if no
+.Ar file
+argument is given.
+.Pp
+.Nm Unifdef
+works nicely with the
+.Fl D Ns Ar sym
+option added to
+.Xr diff 1
+as of the 4.1 Berkeley Software Distribution.
+.Sh SEE ALSO
+.Xr diff 1
+.Sh DIAGNOSTICS
+Inappropriate else or endif.
+.br
+Premature
+.Tn EOF
+with line numbers of the unterminated #ifdefs.
+.Pp
+Exit status is 0 if output is exact copy of input, 1 if not, 2 if trouble.
+.Sh BUGS
+Should try to deal with ``#if'' lines.
+.Pp
+Doesn't work correctly if input contains null characters.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/unifdef/unifdef.c b/usr.bin/unifdef/unifdef.c
new file mode 100644
index 0000000..0fe80fc
--- /dev/null
+++ b/usr.bin/unifdef/unifdef.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Dave Yost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)unifdef.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * unifdef - remove ifdef'ed lines
+ *
+ * Warning: will not work correctly if input contains null characters.
+ *
+ * Wishlist:
+ * provide an option which will append the name of the
+ * appropriate symbol after #else's and #endif's
+ * provide an option which will check symbols after
+ * #else's and #endif's to see that they match their
+ * corresponding #ifdef or #ifndef
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define BSS
+FILE *input;
+#ifndef YES
+#define YES 1
+#define NO 0
+#endif/*YES */
+typedef int Bool;
+
+char *progname BSS;
+char *filename BSS;
+char text BSS; /* -t option in effect: this is a text file */
+char lnblank BSS; /* -l option in effect: blank deleted lines */
+char complement BSS; /* -c option in effect: complement the operation */
+
+#define MAXSYMS 100
+char *symname[MAXSYMS] BSS; /* symbol name */
+char true[MAXSYMS] BSS; /* -Dsym */
+char ignore[MAXSYMS] BSS; /* -iDsym or -iUsym */
+char insym[MAXSYMS] BSS; /* state: false, inactive, true */
+#define SYM_INACTIVE 0 /* symbol is currently inactive */
+#define SYM_FALSE 1 /* symbol is currently false */
+#define SYM_TRUE 2 /* symbol is currently true */
+
+char nsyms BSS;
+char incomment BSS; /* inside C comment */
+
+#define QUOTE_NONE 0
+#define QUOTE_SINGLE 1
+#define QUOTE_DOUBLE 2
+char inquote BSS; /* inside single or double quotes */
+
+int exitstat BSS;
+char *skipcomment ();
+char *skipquote ();
+
+main (argc, argv)
+int argc;
+char **argv;
+{
+ char **curarg;
+ register char *cp;
+ register char *cp1;
+ char ignorethis;
+
+ progname = argv[0][0] ? argv[0] : "unifdef";
+
+ for (curarg = &argv[1]; --argc > 0; curarg++) {
+ if (*(cp1 = cp = *curarg) != '-')
+ break;
+ if (*++cp1 == 'i') {
+ ignorethis = YES;
+ cp1++;
+ } else
+ ignorethis = NO;
+ if ( ( *cp1 == 'D'
+ || *cp1 == 'U'
+ )
+ && cp1[1] != '\0'
+ ) {
+ register int symind;
+
+ if ((symind = findsym (&cp1[1])) < 0) {
+ if (nsyms >= MAXSYMS) {
+ prname ();
+ fprintf (stderr, "too many symbols.\n");
+ exit (2);
+ }
+ symind = nsyms++;
+ symname[symind] = &cp1[1];
+ insym[symind] = SYM_INACTIVE;
+ }
+ ignore[symind] = ignorethis;
+ true[symind] = *cp1 == 'D' ? YES : NO;
+ } else if (ignorethis)
+ goto unrec;
+ else if (strcmp (&cp[1], "t") == 0)
+ text = YES;
+ else if (strcmp (&cp[1], "l") == 0)
+ lnblank = YES;
+ else if (strcmp (&cp[1], "c") == 0)
+ complement = YES;
+ else {
+ unrec:
+ prname ();
+ fprintf (stderr, "unrecognized option: %s\n", cp);
+ goto usage;
+ }
+ }
+ if (nsyms == 0) {
+ usage:
+ fprintf (stderr, "\
+Usage: %s [-l] [-t] [-c] [[-Dsym] [-Usym] [-iDsym] [-iUsym]]... [file]\n\
+ At least one arg from [-D -U -iD -iU] is required\n", progname);
+ exit (2);
+ }
+
+ if (argc > 1) {
+ prname ();
+ fprintf (stderr, "can only do one file.\n");
+ } else if (argc == 1) {
+ filename = *curarg;
+ if ((input = fopen (filename, "r")) != NULL) {
+ pfile();
+ (void) fclose (input);
+ } else {
+ prname ();
+ fprintf (stderr, "can't open ");
+ perror(*curarg);
+ }
+ } else {
+ filename = "[stdin]";
+ input = stdin;
+ pfile();
+ }
+
+ (void) fflush (stdout);
+ exit (exitstat);
+}
+
+/* types of input lines: */
+typedef int Linetype;
+#define LT_PLAIN 0 /* ordinary line */
+#define LT_TRUE 1 /* a true #ifdef of a symbol known to us */
+#define LT_FALSE 2 /* a false #ifdef of a symbol known to us */
+#define LT_OTHER 3 /* an #ifdef of a symbol not known to us */
+#define LT_IF 4 /* an #ifdef of a symbol not known to us */
+#define LT_ELSE 5 /* #else */
+#define LT_ENDIF 6 /* #endif */
+#define LT_LEOF 7 /* end of file */
+extern Linetype checkline ();
+
+typedef int Reject_level;
+Reject_level reject BSS; /* 0 or 1: pass thru; 1 or 2: ignore comments */
+#define REJ_NO 0
+#define REJ_IGNORE 1
+#define REJ_YES 2
+
+int linenum BSS; /* current line number */
+int stqcline BSS; /* start of current coment or quote */
+char *errs[] = {
+#define NO_ERR 0
+ "",
+#define END_ERR 1
+ "",
+#define ELSE_ERR 2
+ "Inappropriate else",
+#define ENDIF_ERR 3
+ "Inappropriate endif",
+#define IEOF_ERR 4
+ "Premature EOF in ifdef",
+#define CEOF_ERR 5
+ "Premature EOF in comment",
+#define Q1EOF_ERR 6
+ "Premature EOF in quoted character",
+#define Q2EOF_ERR 7
+ "Premature EOF in quoted string"
+};
+
+/* States for inif arg to doif */
+#define IN_NONE 0
+#define IN_IF 1
+#define IN_ELSE 2
+
+pfile ()
+{
+ reject = REJ_NO;
+ (void) doif (-1, IN_NONE, reject, 0);
+ return;
+}
+
+int
+doif (thissym, inif, prevreject, depth)
+register int thissym; /* index of the symbol who was last ifdef'ed */
+int inif; /* YES or NO we are inside an ifdef */
+Reject_level prevreject;/* previous value of reject */
+int depth; /* depth of ifdef's */
+{
+ register Linetype lineval;
+ register Reject_level thisreject;
+ int doret; /* tmp return value of doif */
+ int cursym; /* index of the symbol returned by checkline */
+ int stline; /* line number when called this time */
+
+ stline = linenum;
+ for (;;) {
+ switch (lineval = checkline (&cursym)) {
+ case LT_PLAIN:
+ flushline (YES);
+ break;
+
+ case LT_TRUE:
+ case LT_FALSE:
+ thisreject = reject;
+ if (lineval == LT_TRUE)
+ insym[cursym] = SYM_TRUE;
+ else {
+ if (reject != REJ_YES)
+ reject = ignore[cursym] ? REJ_IGNORE : REJ_YES;
+ insym[cursym] = SYM_FALSE;
+ }
+ if (ignore[cursym])
+ flushline (YES);
+ else {
+ exitstat = 1;
+ flushline (NO);
+ }
+ if ((doret = doif (cursym, IN_IF, thisreject, depth + 1)) != NO_ERR)
+ return error (doret, stline, depth);
+ break;
+
+ case LT_IF:
+ case LT_OTHER:
+ flushline (YES);
+ if ((doret = doif (-1, IN_IF, reject, depth + 1)) != NO_ERR)
+ return error (doret, stline, depth);
+ break;
+
+ case LT_ELSE:
+ if (inif != IN_IF)
+ return error (ELSE_ERR, linenum, depth);
+ inif = IN_ELSE;
+ if (thissym >= 0) {
+ if (insym[thissym] == SYM_TRUE) {
+ reject = ignore[thissym] ? REJ_IGNORE : REJ_YES;
+ insym[thissym] = SYM_FALSE;
+ } else { /* (insym[thissym] == SYM_FALSE) */
+ reject = prevreject;
+ insym[thissym] = SYM_TRUE;
+ }
+ if (!ignore[thissym]) {
+ flushline (NO);
+ break;
+ }
+ }
+ flushline (YES);
+ break;
+
+ case LT_ENDIF:
+ if (inif == IN_NONE)
+ return error (ENDIF_ERR, linenum, depth);
+ if (thissym >= 0) {
+ insym[thissym] = SYM_INACTIVE;
+ reject = prevreject;
+ if (!ignore[thissym]) {
+ flushline (NO);
+ return NO_ERR;
+ }
+ }
+ flushline (YES);
+ return NO_ERR;
+
+ case LT_LEOF: {
+ int err;
+ err = incomment
+ ? CEOF_ERR
+ : inquote == QUOTE_SINGLE
+ ? Q1EOF_ERR
+ : inquote == QUOTE_DOUBLE
+ ? Q2EOF_ERR
+ : NO_ERR;
+ if (inif != IN_NONE) {
+ if (err != NO_ERR)
+ (void) error (err, stqcline, depth);
+ return error (IEOF_ERR, stline, depth);
+ } else if (err != NO_ERR)
+ return error (err, stqcline, depth);
+ else
+ return NO_ERR;
+ }
+ }
+ }
+}
+
+#define endsym(c) (!isalpha (c) && !isdigit (c) && c != '_')
+
+#define MAXLINE 256
+char tline[MAXLINE] BSS;
+
+Linetype
+checkline (cursym)
+int *cursym; /* if LT_TRUE or LT_FALSE returned, set this to sym index */
+{
+ register char *cp;
+ register char *symp;
+ char *scp;
+ Linetype retval;
+# define KWSIZE 8
+ char keyword[KWSIZE];
+
+ linenum++;
+ if (getlin (tline, sizeof tline, input, NO) == EOF)
+ return LT_LEOF;
+
+ retval = LT_PLAIN;
+ if ( *(cp = tline) != '#'
+ || incomment
+ || inquote == QUOTE_SINGLE
+ || inquote == QUOTE_DOUBLE
+ )
+ goto eol;
+
+ cp = skipcomment (++cp);
+ symp = keyword;
+ while (!endsym (*cp)) {
+ *symp = *cp++;
+ if (++symp >= &keyword[KWSIZE])
+ goto eol;
+ }
+ *symp = '\0';
+
+ if (strcmp (keyword, "ifdef") == 0) {
+ retval = YES;
+ goto ifdef;
+ } else if (strcmp (keyword, "ifndef") == 0) {
+ retval = NO;
+ ifdef:
+ scp = cp = skipcomment (++cp);
+ if (incomment) {
+ retval = LT_PLAIN;
+ goto eol;
+ }
+ {
+ int symind;
+
+ if ((symind = findsym (scp)) >= 0)
+ retval = (retval ^ true[*cursym = symind])
+ ? LT_FALSE : LT_TRUE;
+ else
+ retval = LT_OTHER;
+ }
+ } else if (strcmp (keyword, "if") == 0)
+ retval = LT_IF;
+ else if (strcmp (keyword, "else") == 0)
+ retval = LT_ELSE;
+ else if (strcmp (keyword, "endif") == 0)
+ retval = LT_ENDIF;
+
+ eol:
+ if (!text && reject != REJ_IGNORE)
+ for (; *cp; ) {
+ if (incomment)
+ cp = skipcomment (cp);
+ else if (inquote == QUOTE_SINGLE)
+ cp = skipquote (cp, QUOTE_SINGLE);
+ else if (inquote == QUOTE_DOUBLE)
+ cp = skipquote (cp, QUOTE_DOUBLE);
+ else if (*cp == '/' && cp[1] == '*')
+ cp = skipcomment (cp);
+ else if (*cp == '\'')
+ cp = skipquote (cp, QUOTE_SINGLE);
+ else if (*cp == '"')
+ cp = skipquote (cp, QUOTE_DOUBLE);
+ else
+ cp++;
+ }
+ return retval;
+}
+
+/*
+ * Skip over comments and stop at the next charaacter
+ * position that is not whitespace.
+ */
+char *
+skipcomment (cp)
+register char *cp;
+{
+ if (incomment)
+ goto inside;
+ for (;; cp++) {
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (text)
+ return cp;
+ if ( cp[0] != '/'
+ || cp[1] != '*'
+ )
+ return cp;
+ cp += 2;
+ if (!incomment) {
+ incomment = YES;
+ stqcline = linenum;
+ }
+ inside:
+ for (;;) {
+ for (; *cp != '*'; cp++)
+ if (*cp == '\0')
+ return cp;
+ if (*++cp == '/') {
+ incomment = NO;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Skip over a quoted string or character and stop at the next charaacter
+ * position that is not whitespace.
+ */
+char *
+skipquote (cp, type)
+register char *cp;
+register int type;
+{
+ register char qchar;
+
+ qchar = type == QUOTE_SINGLE ? '\'' : '"';
+
+ if (inquote == type)
+ goto inside;
+ for (;; cp++) {
+ if (*cp != qchar)
+ return cp;
+ cp++;
+ inquote = type;
+ stqcline = linenum;
+ inside:
+ for (; ; cp++) {
+ if (*cp == qchar)
+ break;
+ if ( *cp == '\0'
+ || *cp == '\\' && *++cp == '\0'
+ )
+ return cp;
+ }
+ inquote = QUOTE_NONE;
+ }
+}
+
+/*
+ * findsym - look for the symbol in the symbol table.
+ * if found, return symbol table index,
+ * else return -1.
+ */
+int
+findsym (str)
+char *str;
+{
+ register char *cp;
+ register char *symp;
+ register int symind;
+ register char chr;
+
+ for (symind = 0; symind < nsyms; ++symind) {
+ if (insym[symind] == SYM_INACTIVE) {
+ for ( symp = symname[symind], cp = str
+ ; *symp && *cp == *symp
+ ; cp++, symp++
+ )
+ continue;
+ chr = *cp;
+ if (*symp == '\0' && endsym (chr))
+ return symind;
+ }
+ }
+ return -1;
+}
+
+/*
+ * getlin - expands tabs if asked for
+ * and (if compiled in) treats form-feed as an end-of-line
+ */
+int
+getlin (line, maxline, inp, expandtabs)
+register char *line;
+int maxline;
+FILE *inp;
+int expandtabs;
+{
+ int tmp;
+ register int num;
+ register int chr;
+#ifdef FFSPECIAL
+ static char havechar = NO; /* have leftover char from last time */
+ static char svchar BSS;
+#endif/*FFSPECIAL */
+
+ num = 0;
+#ifdef FFSPECIAL
+ if (havechar) {
+ havechar = NO;
+ chr = svchar;
+ goto ent;
+ }
+#endif/*FFSPECIAL */
+ while (num + 8 < maxline) { /* leave room for tab */
+ chr = getc (inp);
+ if (isprint (chr)) {
+#ifdef FFSPECIAL
+ ent:
+#endif/*FFSPECIAL */
+ *line++ = chr;
+ num++;
+ } else
+ switch (chr) {
+ case EOF:
+ return EOF;
+
+ case '\t':
+ if (expandtabs) {
+ num += tmp = 8 - (num & 7);
+ do
+ *line++ = ' ';
+ while (--tmp);
+ break;
+ }
+ default:
+ *line++ = chr;
+ num++;
+ break;
+
+ case '\n':
+ *line = '\n';
+ num++;
+ goto end;
+
+#ifdef FFSPECIAL
+ case '\f':
+ if (++num == 1)
+ *line = '\f';
+ else {
+ *line = '\n';
+ havechar = YES;
+ svchar = chr;
+ }
+ goto end;
+#endif/*FFSPECIAL */
+ }
+ }
+ end:
+ *++line = '\0';
+ return num;
+}
+
+flushline (keep)
+Bool keep;
+{
+ if ((keep && reject != REJ_YES) ^ complement) {
+ register char *line = tline;
+ register FILE *out = stdout;
+ register char chr;
+
+ while (chr = *line++)
+ putc (chr, out);
+ } else if (lnblank)
+ putc ('\n', stdout);
+ return;
+}
+
+prname ()
+{
+ fprintf (stderr, "%s: ", progname);
+ return;
+}
+
+int
+error (err, line, depth)
+int err; /* type of error & index into error string array */
+int line; /* line number */
+int depth; /* how many ifdefs we are inside */
+{
+ if (err == END_ERR)
+ return err;
+
+ prname ();
+
+#ifndef TESTING
+ fprintf (stderr, "Error in %s line %d: %s.\n", filename, line, errs[err]);
+#else/* TESTING */
+ fprintf (stderr, "Error in %s line %d: %s. ", filename, line, errs[err]);
+ fprintf (stderr, "ifdef depth: %d\n", depth);
+#endif/*TESTING */
+
+ exitstat = 2;
+ return depth > 1 ? IEOF_ERR : END_ERR;
+}
diff --git a/usr.bin/uniq/Makefile b/usr.bin/uniq/Makefile
new file mode 100644
index 0000000..fa0db8b
--- /dev/null
+++ b/usr.bin/uniq/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uniq
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uniq/uniq.1 b/usr.bin/uniq/uniq.1
new file mode 100644
index 0000000..0898e0e
--- /dev/null
+++ b/usr.bin/uniq/uniq.1
@@ -0,0 +1,130 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uniq.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UNIQ 1
+.Os
+.Sh NAME
+.Nm uniq
+.Nd report or filter out repeated lines in a file
+.Sh SYNOPSIS
+.Nm uniq
+.Op Fl c | Fl d | Fl u
+.Op Fl f Ar fields
+.Op Fl s Ar chars
+.Oo
+.Ar input_file
+.Op Ar output_file
+.Oc
+.Sh DESCRIPTION
+The
+.Nm uniq
+utility reads the standard input comparing adjacent lines, and writes
+a copy of each unique input line to the standard output.
+The second and succeeding copies of identical adjacent input lines are
+not written.
+Repeated lines in the input will not be detected if they are not adjacent,
+so it may be necessary to sort the files first.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Precede each output line with the count of the number of times the line
+occurred in the input, followed by a single space.
+.It Fl d
+Don't output lines that are not repeated in the input.
+.It Fl f Ar fields
+Ignore the first
+.Ar fields
+in each input line when doing comparisons.
+A field is a string of non-blank characters separated from adjacent fields
+by blanks.
+Field numbers are one based, i.e. the first field is field one.
+.It Fl s Ar chars
+Ignore the first
+.Ar chars
+characters in each input line when doing comparisons.
+If specified in conjunction with the
+.Fl f
+option, the first
+.Ar chars
+characters after the first
+.Ar fields
+fields will be ignored.
+Character numbers are one based, i.e. the first character is character one.
+.It Fl u
+Don't output lines that are repeated in the input.
+.\".It Fl Ns Ar n
+.\"(Deprecated; replaced by
+.\".Fl f ) .
+.\"Ignore the first n
+.\"fields on each input line when doing comparisons,
+.\"where n is a number.
+.\"A field is a string of non-blank
+.\"characters separated from adjacent fields
+.\"by blanks.
+.\".It Cm \&\(pl Ns Ar n
+.\"(Deprecated; replaced by
+.\".Fl s ) .
+.\"Ignore the first
+.\".Ar m
+.\"characters when doing comparisons, where
+.\".Ar m
+.\"is a
+.\"number.
+.El
+.Pp
+If additional arguments are specified on the command line, the first
+such argument is used as the name of an input file, the second is used
+as the name of an output file.
+.Pp
+The
+.Nm uniq
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+The historic
+.Cm \&\(pl Ns Ar number
+and
+.Fl Ns Ar number
+options have been deprecated but are still supported in this implementation.
+.Sh SEE ALSO
+.Xr sort 1
+.Sh STANDARDS
+The
+.Nm uniq
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c
new file mode 100644
index 0000000..e84f82e
--- /dev/null
+++ b/usr.bin/uniq/uniq.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Case Larsen.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uniq.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAXLINELEN (8 * 1024)
+
+int cflag, dflag, uflag;
+int numchars, numfields, repeats;
+
+void err __P((const char *, ...));
+FILE *file __P((char *, char *));
+void show __P((FILE *, char *));
+char *skip __P((char *));
+void obsolete __P((char *[]));
+void usage __P((void));
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *t1, *t2;
+ FILE *ifp, *ofp;
+ int ch;
+ char *prevline, *thisline, *p;
+
+ obsolete(argv);
+ while ((ch = getopt(argc, argv, "-cdf:s:u")) != EOF)
+ switch (ch) {
+ case '-':
+ --optind;
+ goto done;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ numfields = strtol(optarg, &p, 10);
+ if (numfields < 0 || *p)
+ err("illegal field skip value: %s", optarg);
+ break;
+ case 's':
+ numchars = strtol(optarg, &p, 10);
+ if (numchars < 0 || *p)
+ err("illegal character skip value: %s", optarg);
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+done: argc -= optind;
+ argv +=optind;
+
+ /* If no flags are set, default is -d -u. */
+ if (cflag) {
+ if (dflag || uflag)
+ usage();
+ } else if (!dflag && !uflag)
+ dflag = uflag = 1;
+
+ switch(argc) {
+ case 0:
+ ifp = stdin;
+ ofp = stdout;
+ break;
+ case 1:
+ ifp = file(argv[0], "r");
+ ofp = stdout;
+ break;
+ case 2:
+ ifp = file(argv[0], "r");
+ ofp = file(argv[1], "w");
+ break;
+ default:
+ usage();
+ }
+
+ prevline = malloc(MAXLINELEN);
+ thisline = malloc(MAXLINELEN);
+ if (prevline == NULL || thisline == NULL)
+ err("%s", strerror(errno));
+
+ if (fgets(prevline, MAXLINELEN, ifp) == NULL)
+ exit(0);
+
+ while (fgets(thisline, MAXLINELEN, ifp)) {
+ /* If requested get the chosen fields + character offsets. */
+ if (numfields || numchars) {
+ t1 = skip(thisline);
+ t2 = skip(prevline);
+ } else {
+ t1 = thisline;
+ t2 = prevline;
+ }
+
+ /* If different, print; set previous to new value. */
+ if (strcmp(t1, t2)) {
+ show(ofp, prevline);
+ t1 = prevline;
+ prevline = thisline;
+ thisline = t1;
+ repeats = 0;
+ } else
+ ++repeats;
+ }
+ show(ofp, prevline);
+ exit(0);
+}
+
+/*
+ * show --
+ * Output a line depending on the flags and number of repetitions
+ * of the line.
+ */
+void
+show(ofp, str)
+ FILE *ofp;
+ char *str;
+{
+ if (cflag)
+ (void)fprintf(ofp, "%4d %s", repeats + 1, str);
+ if (dflag && repeats || uflag && !repeats)
+ (void)fprintf(ofp, "%s", str);
+}
+
+char *
+skip(str)
+ register char *str;
+{
+ register int infield, nchars, nfields;
+
+ for (nfields = numfields, infield = 0; nfields && *str; ++str)
+ if (isspace(*str)) {
+ if (infield) {
+ infield = 0;
+ --nfields;
+ }
+ } else if (!infield)
+ infield = 1;
+ for (nchars = numchars; nchars-- && *str; ++str);
+ return(str);
+}
+
+FILE *
+file(name, mode)
+ char *name, *mode;
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, mode)) == NULL)
+ err("%s: %s", name, strerror(errno));
+ return(fp);
+}
+
+void
+obsolete(argv)
+ char *argv[];
+{
+ int len;
+ char *ap, *p, *start;
+
+ while (ap = *++argv) {
+ /* Return if "--" or not an option of any form. */
+ if (ap[0] != '-') {
+ if (ap[0] != '+')
+ return;
+ } else if (ap[1] == '-')
+ return;
+ if (!isdigit(ap[1]))
+ continue;
+ /*
+ * Digit signifies an old-style option. Malloc space for dash,
+ * new option and argument.
+ */
+ len = strlen(ap);
+ if ((start = p = malloc(len + 3)) == NULL)
+ err("%s", strerror(errno));
+ *p++ = '-';
+ *p++ = ap[0] == '+' ? 's' : 'f';
+ (void)strcpy(p, ap + 1);
+ *argv = start;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: uniq [-c | -du] [-f fields] [-s chars] [input [output]]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "uniq: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/unvis/Makefile b/usr.bin/unvis/Makefile
new file mode 100644
index 0000000..a6807ff
--- /dev/null
+++ b/usr.bin/unvis/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= unvis
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/unvis/unvis.1 b/usr.bin/unvis/unvis.1
new file mode 100644
index 0000000..5d531af
--- /dev/null
+++ b/usr.bin/unvis/unvis.1
@@ -0,0 +1,57 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)unvis.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UNVIS 1
+.Os BSD 4.4
+.Sh NAME
+.Nm unvis
+.Nd "revert a visual representation of data back to original form"
+.Sh SYNOPSIS
+.Nm unvis
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Unvis
+is the inverse function of
+.Xr vis 1 .
+It reverts
+a visual representation of data back to its original form on standard output.
+.Sh SEE ALSO
+.Xr vis 1 ,
+.Xr unvis 3 ,
+.Xr vis 3
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/unvis/unvis.c b/usr.bin/unvis/unvis.c
new file mode 100644
index 0000000..82cad81
--- /dev/null
+++ b/usr.bin/unvis/unvis.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <vis.h>
+
+char *Program;
+#define usage() fprintf(stderr, "usage: %s %s\n", Program, USAGE)
+#define USAGE "[file...]"
+
+main(argc, argv)
+ char *argv[];
+{
+ FILE *fp;
+ extern char *optarg;
+ extern int optind;
+ int ch;
+
+ Program = argv[0];
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch((char)ch) {
+ case '?':
+ default:
+ usage();
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ while (*argv) {
+ if ((fp=fopen(*argv, "r")) != NULL)
+ process(fp, *argv);
+ else
+ syserror("%s", *argv);
+ argv++;
+ }
+ else
+ process(stdin, "<stdin>");
+ exit(0);
+}
+
+process(fp, filename)
+ FILE *fp;
+ char *filename;
+{
+ register int offset = 0, c, ret;
+ int state = 0;
+ char outc;
+
+ while ((c = getc(fp)) != EOF) {
+ offset++;
+ again:
+ switch(ret = unvis(&outc, (char)c, &state, 0)) {
+ case UNVIS_VALID:
+ putchar(outc);
+ break;
+ case UNVIS_VALIDPUSH:
+ putchar(outc);
+ goto again;
+ case UNVIS_SYNBAD:
+ error("%s: offset: %d: can't decode", filename, offset);
+ state = 0;
+ break;
+ case 0:
+ case UNVIS_NOCHAR:
+ break;
+ default:
+ error("bad return value (%d), can't happen", ret);
+ exit(1);
+ }
+ }
+ if (unvis(&outc, (char)0, &state, UNVIS_END) == UNVIS_VALID)
+ putchar(outc);
+}
+
+#include <varargs.h>
+
+error(va_alist)
+ va_dcl
+{
+ char *fmt;
+ va_list ap;
+ extern errno;
+
+ fprintf(stderr, "%s: ", Program);
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+
+syserror(va_alist)
+ va_dcl
+{
+ char *fmt;
+ va_list ap;
+ extern errno;
+
+ fprintf(stderr, "%s: ", Program);
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s\n", strerror(errno));
+}
diff --git a/usr.bin/users/Makefile b/usr.bin/users/Makefile
new file mode 100644
index 0000000..d0825d0
--- /dev/null
+++ b/usr.bin/users/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= users
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/users/users.1 b/usr.bin/users/users.1
new file mode 100644
index 0000000..57ae2db
--- /dev/null
+++ b/usr.bin/users/users.1
@@ -0,0 +1,59 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)users.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt USERS 1
+.Os BSD 3
+.Sh NAME
+.Nm users
+.Nd list current users
+.Sh SYNOPSIS
+.Nm users
+.Sh DESCRIPTION
+.Nm Users
+lists the login names of the users currently on the system,
+in sorted order, space separated, on a single line.
+.Sh FILES
+.Bl -tag -width /var/run/utmp
+.It Pa /var/run/utmp
+.El
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr last 1 ,
+.Xr who 1 ,
+.Xr utmp 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/users/users.c b/usr.bin/users/users.c
new file mode 100644
index 0000000..11571f2
--- /dev/null
+++ b/usr.bin/users/users.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1980, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)users.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <utmp.h>
+#include <stdio.h>
+
+#define MAXUSERS 200
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ register int cnt, ncnt;
+ struct utmp utmp;
+ char names[MAXUSERS][UT_NAMESIZE];
+ int ch, scmp();
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch(ch) {
+ case '?':
+ default:
+ (void)fprintf(stderr, "usage: users\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!freopen(_PATH_UTMP, "r", stdin)) {
+ (void)fprintf(stderr, "users: can't open %s.\n", _PATH_UTMP);
+ exit(1);
+ }
+ for (ncnt = 0;
+ fread((char *)&utmp, sizeof(utmp), 1, stdin) == 1;)
+ if (*utmp.ut_name) {
+ if (ncnt == MAXUSERS) {
+ (void)fprintf(stderr,
+ "users: too many users.\n");
+ break;
+ }
+ (void)strncpy(names[ncnt], utmp.ut_name, UT_NAMESIZE);
+ ++ncnt;
+ }
+
+ if (ncnt) {
+ qsort(names, ncnt, UT_NAMESIZE, scmp);
+ (void)printf("%.*s", UT_NAMESIZE, names[0]);
+ for (cnt = 1; cnt < ncnt; ++cnt)
+ if (strncmp(names[cnt], names[cnt - 1], UT_NAMESIZE))
+ (void)printf(" %.*s", UT_NAMESIZE, names[cnt]);
+ (void)printf("\n");
+ }
+ exit(0);
+}
+
+scmp(p, q)
+ char *p, *q;
+{
+ return(strncmp(p, q, UT_NAMESIZE));
+}
diff --git a/usr.bin/uucp/acucntrl/Makefile b/usr.bin/uucp/acucntrl/Makefile
new file mode 100644
index 0000000..8a2b44f
--- /dev/null
+++ b/usr.bin/uucp/acucntrl/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= acucntrl
+CFLAGS+=-I${.CURDIR}/../includes
+BINDIR= ${LIBDIR}
+BINOWN= root
+BINMODE=6550
+MAN8= acucntrl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uucp/acucntrl/acucntrl.8 b/usr.bin/uucp/acucntrl/acucntrl.8
new file mode 100644
index 0000000..b2b7f0e
--- /dev/null
+++ b/usr.bin/uucp/acucntrl/acucntrl.8
@@ -0,0 +1,164 @@
+.\" Copyright (c) 1985, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)acucntrl.8 8.2 (Berkeley) 12/11/93
+.\"
+.TH ACUCNTRL 8 "December 11, 1993"
+.UC 6
+.SH NAME
+acucntrl \- turn around tty line between dialin and dialout
+.SH SYNOPSIS
+.B /usr/lib/uucp/acucntrl
+keyword ttyline
+.SH DESCRIPTION
+.PP
+.I Acucntrl
+turns around the terminal line,
+enabling it to be used for both dialin and dialout.
+On dialin a terminal line is assumed to have modem control enabled and a getty
+process in existence waiting for logins. On dialout modem control is disabled
+and there is no getty process.
+.PP
+This program must be run setuid to root.
+.PP
+.I keyword
+is chosen from the list:
+.I disable
+or
+.IR dialout ,
+to condition a line for dialout;
+and
+.I enable
+or
+.IR dialin ,
+to condition a line for dialin.
+.PP
+When the line is conditioned for dialing out, the login name of the real uid
+of the process is placed in /etc/utmp in capitals.
+This declares that the line is in use and acts as an additional locking
+mechanism.
+.I Acucntrl
+will refuse to act if the /etc/utmp entry for the line is not null,
+is not the the user's login name (capitalized or not),
+and if the process is not running as the superuser.
+The last condition is to allow the superuser to clear the state of the line.
+.PP
+Turning modem control on or off is handled by poking into /dev/kmem.
+It is currently implemented for dz, dh, and dmf lines.
+.PP
+Under 4.2 BSD the program will also refuse to disable a line if carrier is
+sensed on it. This is to avoid the dead period where someone has just dialed
+in and made the connection but has not yet logged in.
+.PP
+.I Ttyline
+can be either of the form tty* or /dev/tty*.
+Enabling/disabling a line whose name does not begin with ttyd? is prohibited
+unless the real uid of the process is 0 or if the login name corresponding to
+the real uid is uucp. This is a security precaution.
+.PP
+Steps taken when disabling
+.RI ( i . e .
+setup for dialing out)
+.IP 1)
+check input arguments
+.IP 2)
+look in /etc/utmp to check that the line is not in use by another user
+.IP 3)
+disable modem control on line
+.IP 4)
+check for carrier on device
+.IP 5)
+change owner of device to real uid
+.IP 6)
+edit /etc/ttys, changing the first character of the appropriate line to 0
+.IP 7)
+send a hangup to process 1 to poke init to disable getty
+.IP 8)
+post uid name in capitals in /etc/utmp to let world know device has been grabbed
+.IP 9)
+make sure that DTR is on
+.PP
+Steps taken when enabling
+.RI ( i . e .
+setup for dialing in)
+.IP 1)
+check input arguments
+.IP 2)
+look in /etc/utmp to check that the line is not in use by another user
+.IP 3)
+make sure modem control on line is disabled
+.IP 4)
+turn off DTR to make sure line is hung up
+.IP 5)
+condition line: clear exclusive use and set hangup on close modes
+.IP 6)
+turn on modem control
+.IP 7)
+edit /etc/ttys, changing the first character of the appropriate line to 1
+.IP 8)
+send a hangup to process 1 to poke init to enable getty
+.IP 9)
+clear uid name for /etc/utmp
+.SH HISTORY
+.PP
+First written by Allan Wilkes (fisher!allan)
+.PP
+Modified June 8,1983 by W.Sebok (astrovax!wls) to poke the kernel rather
+than use a kernel hack to turn on/off modem control, using a subroutine
+stolen from a program written by Tsutomu Shimomura {astrovax,escher}!tsutomu
+.PP
+Worked over many times by W.Sebok
+.RI ( i . e .
+hacked to death)
+.SH FILES
+/dev/kmem, /kernel, /etc/ttys, /etc/utmp, /dev/tty*
+.SH BUGS
+.PP
+Sensing carrier requires the 4.2 BSD TIOCMGET ioctl call. Unfortunately this
+ioctl is not implemented in the vanilla 4.2 BSD dh driver even though the
+dz and dmf drivers use an emulation of the DH11's modem control bits. This
+has been fixed here.
+.PP
+Some time (currently 2 seconds) is required between disabling modem control
+and opening the device. This is probably because of a race with getty whose
+open is finally being allowed to complete. This time interval may not be
+enough on a loaded system. Because of this problem and the above problem with
+the dh driver there is deliberately no error message given when the TIOCMGET
+ioctl fails.
+.PP
+Previously there were similar synchronization problems with the init process.
+When dialins are disabled the capitalized name of the process cannot be posted
+into /etc/utmp until init has finished clearing /etc/utmp. However one does
+not know how long that will take, and, on a loaded system, it can take quite
+a while. This was solved by the strategy of 1) posting the name, 2) poking
+init, 3) going into a loop where the process repeatedly waits a second and
+checks whether the entry has been cleared from /etc/utmp, and 4) posting the
+name again.
diff --git a/usr.bin/uucp/acucntrl/acucntrl.c b/usr.bin/uucp/acucntrl/acucntrl.c
new file mode 100644
index 0000000..deba6ba
--- /dev/null
+++ b/usr.bin/uucp/acucntrl/acucntrl.c
@@ -0,0 +1,814 @@
+/*-
+ * Copyright (c) 1985, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1986, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)acucntrl.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/* acucntrl - turn around tty line between dialin and dialout
+ *
+ * Usage: acucntrl {enable,disable} /dev/ttydX
+ *
+ * History:
+ * First written by Allan Wilkes (fisher!allan)
+ *
+ * Modified June 8,1983 by W.Sebok (astrovax!wls) to poke kernel rather
+ * than use kernel hack to turn on/off modem control, using subroutine
+ * stolen from program written by Tsutomu Shimomura
+ * {astrovax,escher}!tsutomu
+ *
+ * Worked over many times by W.Sebok (i.e. hacked to death)
+ *
+ * Operation:
+ * disable (i.e. setup for dialing out)
+ * (1) check input arguments
+ * (2) look in _PATH_UTMP to check that the line is not in use by another
+ * (3) disable modem control on terminal
+ * (4) check for carrier on device
+ * (5) change owner of device to real id
+ * (6) edit _PATH_TTYS, changing the first character of the appropriate
+ * line to 0
+ * (7) send a hangup to process 1 to poke init to disable getty
+ * (8) post uid name in capitals in _PATH_UTMP to let world know device
+ * has been grabbed
+ * (9) make sure that DTR is on
+ *
+ * enable (i.e.) restore for dialin
+ * (1) check input arguments
+ * (2) look in _PATH_UTMP to check that the line is not in use by another
+ * (3) make sure modem control on terminal is disabled
+ * (4) turn off DTR to make sure line is hung up
+ * (5) condition line: clear exclusive use and set hangup on close modes
+ * (6) turn on modem control
+ * (7) edit _PATH_TTYS, changing the first character of the appropriate
+ * line to 1
+ * (8) send a hangup to process 1 to poke init to enable getty
+ * (9) clear uid name for _PATH_UTMP
+ */
+
+/* #define SENSECARRIER */
+
+#include "uucp.h"
+#ifdef DIALINOUT
+#include <sys/buf.h>
+#include <signal.h>
+#include <sys/conf.h>
+#ifdef vax
+#ifdef BSD4_2
+#include <vaxuba/ubavar.h>
+#else
+#include <sys/ubavar.h>
+#endif
+#endif /* vax */
+#include <sys/stat.h>
+#include <nlist.h>
+#include <sgtty.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include "pathnames.h"
+
+#define NDZLINE 8 /* lines/dz */
+#define NDHLINE 16 /* lines/dh */
+#define NDMFLINE 8 /* lines/dmf */
+
+#define DZ11 1
+#define DH11 2
+#define DMF 3
+
+#define NLVALUE(val) (nl[val].n_value)
+
+struct nlist nl[] = {
+#define CDEVSW 0
+ { "_cdevsw" },
+
+#define DZOPEN 1
+ { "_dzopen" },
+#define DZINFO 2
+ { "_dzinfo" },
+#define NDZ11 3
+ { "_dz_cnt" },
+#define DZSCAR 4
+ { "_dzsoftCAR" },
+
+#define DHOPEN 5
+ { "_dhopen" },
+#define DHINFO 6
+ { "_dhinfo" },
+#define NDH11 7
+ { "_ndh11" },
+#define DHSCAR 8
+ { "_dhsoftCAR" },
+
+#define DMFOPEN 9
+ { "_dmfopen" },
+#define DMFINFO 10
+ { "_dmfinfo" },
+#define NDMF 11
+ { "_ndmf" },
+#define DMFSCAR 12
+ { "_dmfsoftCAR" },
+
+ { "\0" }
+};
+
+#define ENABLE 1
+#define DISABLE 0
+
+char Etcttys[] = _PATH_TTYS;
+#ifdef BSD4_3
+FILE *ttysfile, *nttysfile;
+char NEtcttys[] = _PATH_NEWTTYS;
+extern long ftell();
+#endif BSD4_3
+char Devhome[] = _PATH_DEV;
+
+char usage[] = "Usage: acucntrl {dis|en}able ttydX\n";
+
+struct utmp utmp;
+char resettty, resetmodem;
+int etcutmp;
+off_t utmploc;
+off_t ttyslnbeg;
+extern int errno;
+extern char *sys_errlist[];
+off_t lseek();
+
+#define NAMSIZ sizeof(utmp.ut_name)
+#define LINSIZ sizeof(utmp.ut_line)
+
+main(argc, argv)
+int argc; char *argv[];
+{
+ register char *p;
+ register int i;
+ char uname[NAMSIZ], Uname[NAMSIZ];
+ int enable ;
+ char *device;
+ int devfile;
+ int uid, gid;
+ struct passwd *getpwuid();
+ char *rindex();
+
+ /* check input arguments */
+ if (argc!=3 && argc != 4) {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+
+ /* interpret command type */
+ if (prefix(argv[1], "disable") || strcmp(argv[1], "dialout")==0)
+ enable = 0;
+ else if (prefix(argv[1], "enable") || strcmp(argv[1], "dialin")==0)
+ enable = 1;
+ else {
+ fprintf(stderr, usage);
+ exit(1);
+ }
+
+ device = rindex(argv[2], '/');
+ device = (device == NULL) ? argv[2]: device+1;
+
+ opnttys(device);
+
+#ifdef vax
+ /* Get nlist info */
+ nlist(_PATH_UNIX, nl);
+#endif vax
+
+ /* Chdir to /dev */
+ if(chdir(Devhome) < 0) {
+ fprintf(stderr, "Cannot chdir to %s: %s\r\n",
+ Devhome, sys_errlist[errno]);
+ exit(1);
+ }
+
+ /* Get uid information */
+ uid = getuid();
+ gid = getgid();
+
+ p = getpwuid(uid)->pw_name;
+ if (p==NULL) {
+ fprintf(stderr, "cannot get uid name\n");
+ exit(1);
+ }
+
+ if (strcmp(p, "uucp") == 0 && argc == 4)
+ p = argv[3];
+
+ /* to upper case */
+ i = 0;
+ do {
+ uname[i] = *p;
+ Uname[i++] = (*p>='a' && *p<='z') ? (*p - ('a'-'A')) : *p;
+ } while (*p++ && i<NAMSIZ);
+
+ /* check to see if line is being used */
+ if( (etcutmp = open(_PATH_UTMP, 2)) < 0) {
+ fprintf(stderr, "On open %s open: %s\n",
+ _PATH_UTMP, sys_errlist[errno]);
+ exit(1);
+ }
+
+ (void)lseek(etcutmp, utmploc, 0);
+
+ i = read(etcutmp, (char *)&utmp, sizeof(struct utmp));
+
+ if(
+ i == sizeof(struct utmp) &&
+ utmp.ut_line[0] != '\0' &&
+ utmp.ut_name[0] != '\0' &&
+ (
+ !upcase(utmp.ut_name, NAMSIZ) ||
+ (
+ uid != 0 &&
+ strncmp(utmp.ut_name, Uname, NAMSIZ) != 0
+ )
+ )
+ ) {
+ fprintf(stderr, "%s in use by %s\n", device, utmp.ut_name);
+ exit(2);
+ }
+
+#ifndef sequent
+ /* Disable modem control */
+ if (setmodem(device, DISABLE) < 0) {
+ fprintf(stderr, "Unable to disable modem control\n");
+ exit(1);
+ }
+#endif !sequent
+
+ if (enable) {
+#ifdef sequent
+ if (setmodem(device, ENABLE) < 0) {
+ fprintf(stderr, "Cannot Enable modem control\n");
+ (void)setmodem(device, i);
+ exit(1);
+ }
+#endif sequent
+#ifndef sequent
+ if((devfile = open(device, 1)) < 0) {
+ fprintf(stderr, "On open of %s: %s\n",
+ device, sys_errlist[errno]);
+ (void)setmodem(device, resetmodem);
+ exit(1);
+ }
+ /* Try one last time to hang up */
+ if (ioctl(devfile, (int)TIOCCDTR, (char *)0) < 0)
+ fprintf(stderr, "On TIOCCDTR ioctl: %s\n",
+ sys_errlist[errno]);
+
+ if (ioctl(devfile, (int)TIOCNXCL, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot clear Exclusive Use on %s: %s\n",
+ device, sys_errlist[errno]);
+
+ if (ioctl(devfile, (int)TIOCHPCL, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot set hangup on close on %s: %s\n",
+ device, sys_errlist[errno]);
+
+#endif !sequent
+ i = resetmodem;
+
+#ifndef sequent
+ if (setmodem(device, ENABLE) < 0) {
+ fprintf(stderr, "Cannot Enable modem control\n");
+ (void)setmodem(device, i);
+ exit(1);
+ }
+#endif sequent
+ resetmodem=i;
+
+ if (settys(ENABLE)) {
+ fprintf(stderr, "%s already enabled\n", device);
+ } else {
+ pokeinit(device, Uname, enable);
+ }
+ post(device, "");
+
+ } else {
+#if defined(TIOCMGET) && defined(SENSECARRIER)
+ if (uid!=0) {
+ int linestat = 0;
+
+ /* check for presence of carrier */
+ sleep(2); /* need time after modem control turnoff */
+
+ if((devfile = open(device, 1)) < 0) {
+ fprintf(stderr, "On open of %s: %s\n",
+ device, sys_errlist[errno]);
+ (void)setmodem(device, resetmodem);
+ exit(1);
+ }
+
+ (void)ioctl(devfile, TIOCMGET, &linestat);
+
+ if (linestat&TIOCM_CAR) {
+ fprintf(stderr, "%s is in use (Carrier On)\n",
+ device);
+ (void)setmodem(device, resetmodem);
+ exit(2);
+ }
+ (void)close(devfile);
+ }
+#endif TIOCMGET
+ /* chown device */
+ if(chown(device, uid, gid) < 0)
+ fprintf(stderr, "Cannot chown %s: %s\n",
+ device, sys_errlist[errno]);
+
+
+ /* poke init */
+ if(settys(DISABLE)) {
+ fprintf(stderr, "%s already disabled\n", device);
+ } else {
+ pokeinit(device, Uname, enable);
+ }
+ post(device, Uname);
+#ifdef sequent
+ /* Disable modem control */
+ if (setmodem(device, DISABLE) < 0) {
+ fprintf(stderr, "Unable to disable modem control\n");
+ exit(1);
+ }
+#endif sequent
+ if((devfile = open(device, O_RDWR|O_NDELAY)) < 0) {
+ fprintf(stderr, "On %s open: %s\n",
+ device, sys_errlist[errno]);
+ } else {
+ if(ioctl(devfile, (int)TIOCSDTR, (char *)0) < 0)
+ fprintf(stderr,
+ "Cannot set DTR on %s: %s\n",
+ device, sys_errlist[errno]);
+ }
+ }
+
+ exit(0);
+}
+
+/* return true if no lower case */
+upcase(str, len)
+register char *str;
+register int len;
+{
+ for (; *str, --len >= 0 ; str++)
+ if (*str>='a' && *str<='z')
+ return(0);
+ return(1);
+}
+
+/* Post name to public */
+post(device, name)
+char *device, *name;
+{
+ (void)time((time_t *)&utmp.ut_time);
+ strncpy(utmp.ut_line, device, LINSIZ);
+ strncpy(utmp.ut_name, name, NAMSIZ);
+ if (lseek(etcutmp, utmploc, 0) < 0)
+ fprintf(stderr, "on lseek in %s: %s",
+ _PATH_UTMP, sys_errlist[errno]);
+ if (write(etcutmp, (char *)&utmp, sizeof(utmp)) < 0)
+ fprintf(stderr, "on write in %s: %s",
+ _PATH_UTMP, sys_errlist[errno]);
+}
+
+/* poke process 1 and wait for it to do its thing */
+pokeinit(device, uname, enable)
+char *uname, *device; int enable;
+{
+ struct utmp utmp;
+ register int i;
+
+ post(device, uname);
+
+ /* poke init */
+ if (kill(1, SIGHUP)) {
+ fprintf(stderr,
+ "Cannot send hangup to init process: %s\n",
+ sys_errlist[errno]);
+ (void)settys(resettty);
+ (void)setmodem(device, resetmodem);
+ exit(1);
+ }
+
+ if (enable)
+ return;
+
+ /* wait till init has responded, clearing the utmp entry */
+ i = 100;
+ do {
+ sleep(1);
+ if (lseek(etcutmp, utmploc, 0) < 0)
+ fprintf(stderr, "On lseek in %s: %s",
+ _PATH_UTMP, sys_errlist[errno]);
+ if (read(etcutmp, (char *)&utmp, sizeof utmp) < 0)
+ fprintf(stderr, "On read from %s: %s",
+ _PATH_UTMP, sys_errlist[errno]);
+ } while (utmp.ut_name[0] != '\0' && --i > 0);
+}
+
+#ifdef BSD4_3
+/* identify terminal line in ttys */
+opnttys(device)
+char *device;
+{
+ register int ndevice;
+ register char *p;
+ char *index();
+ char linebuf[BUFSIZ];
+
+ ttysfile = NULL;
+ do {
+ if (ttysfile != NULL) {
+ fclose(ttysfile);
+ sleep(5);
+ }
+ ttysfile = fopen(Etcttys, "r");
+ if(ttysfile == NULL) {
+ fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
+ sys_errlist[errno]);
+ exit(1);
+ }
+ } while (flock(fileno(ttysfile), LOCK_NB|LOCK_EX) < 0);
+ nttysfile = fopen(NEtcttys, "w");
+ if(nttysfile == NULL) {
+ fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
+ sys_errlist[errno]);
+ exit(1);
+ }
+
+ ndevice = strlen(device);
+#ifndef BRL4_2
+ utmploc = sizeof(utmp);
+#else BRL4_2
+ utmploc = 0;
+#endif BRL4_2
+
+ while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) {
+ if(strncmp(device, linebuf, ndevice) == 0)
+ return;
+ ttyslnbeg += strlen(linebuf);
+ if (linebuf[0] != '#' && linebuf[0] != '\0')
+ utmploc += sizeof(utmp);
+ if (fputs(linebuf, nttysfile) == NULL) {
+ fprintf(stderr, "On %s write: %s\n",
+ Etcttys, sys_errlist[errno]);
+ exit(1);
+ }
+
+ }
+ fprintf(stderr, "%s not found in %s\n", device, Etcttys);
+ exit(1);
+}
+
+/* modify appropriate line in _PATH_TTYS to turn on/off the device */
+settys(enable)
+int enable;
+{
+ register char *cp, *cp2;
+ char lbuf[BUFSIZ];
+ int i;
+ char c1, c2;
+
+ (void) fseek(ttysfile, ttyslnbeg, 0);
+ if(fgets(lbuf, BUFSIZ, ttysfile) == NULL) {
+ fprintf(stderr, "On %s read: %s\n",
+ Etcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ /* format is now */
+ /* ttyd0 std.100 dialup on secure # comment */
+ /* except, 2nd item may have embedded spaces inside quotes, Hubert */
+ cp = lbuf;
+ for (i=0;*cp && i<3;i++) {
+ if (*cp == '"') {
+ cp++;
+ while (*cp && *cp != '"')
+ cp++;
+ if (*cp != '\0')
+ cp++;
+ }else {
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ }
+ while (*cp && (*cp == ' ' || *cp == '\t'))
+ cp++;
+ }
+ if (*cp == '\0') {
+ fprintf(stderr,"Badly formatted line in %s:\n%s",
+ _PATH_TTYS, lbuf);
+ exit(1);
+ }
+ c1 = *--cp;
+ *cp++ = '\0';
+ cp2 = cp;
+ while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
+ cp++;
+ if (*cp == '\0') {
+ fprintf(stderr,"Badly formatted line in %s:\n%s",
+ _PATH_TTYS, lbuf);
+ exit(1);
+ }
+ c2 = *cp;
+ *cp++ = '\0';
+ while (*cp && (*cp == ' ' || *cp == '\t'))
+ cp++;
+ resettty = strcmp("on", cp2) != 0;
+ fprintf(nttysfile,"%s%c%s%c%s", lbuf, c1, enable ? "on" : "off", c2, cp);
+ if (ferror(nttysfile)) {
+ fprintf(stderr, "On %s fprintf: %s\n",
+ NEtcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ while(fgets(lbuf, sizeof(lbuf) - 1, ttysfile) != NULL) {
+ if (fputs(lbuf, nttysfile) == NULL) {
+ fprintf(stderr, "On %s write: %s\n",
+ NEtcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ }
+
+ if (enable^resettty)
+ (void) unlink(NEtcttys);
+ else {
+ struct stat statb;
+ if (stat(Etcttys, &statb) == 0) {
+ fchmod(fileno(nttysfile) ,statb.st_mode);
+ fchown(fileno(nttysfile), statb.st_uid, statb.st_gid);
+ }
+ (void) rename(NEtcttys, Etcttys);
+ }
+ (void) fclose(nttysfile);
+ (void) fclose(ttysfile);
+ return enable^resettty;
+}
+
+#else !BSD4_3
+
+/* identify terminal line in ttys */
+opnttys(device)
+char *device;
+{
+ register FILE *ttysfile;
+ register int ndevice, lnsiz;
+ register char *p;
+ char *index();
+ char linebuf[BUFSIZ];
+
+ ttysfile = fopen(Etcttys, "r");
+ if(ttysfile == NULL) {
+ fprintf(stderr, "Cannot open %s: %s\n", Etcttys,
+ sys_errlist[errno]);
+ exit(1);
+ }
+
+ ndevice = strlen(device);
+ ttyslnbeg = 0;
+ utmploc = 0;
+
+ while(fgets(linebuf, sizeof(linebuf) - 1, ttysfile) != NULL) {
+ lnsiz = strlen(linebuf);
+ if ((p = index(linebuf, '\n')) != NULL)
+ *p = '\0';
+ if(strncmp(device, &linebuf[2], ndevice) == 0) {
+ (void)fclose(ttysfile);
+#ifdef sequent
+ /* Why is the sequent off by one? */
+ utmploc += sizeof(utmp);
+#endif sequent
+ return;
+ }
+ ttyslnbeg += lnsiz;
+ utmploc += sizeof(utmp);
+ }
+ fprintf(stderr, "%s not found in %s\n", device, Etcttys);
+ exit(1);
+}
+
+/* modify appropriate line in _PATH_TTYS to turn on/off the device */
+settys(enable)
+int enable;
+{
+ int ittysfil;
+ char out, in;
+
+ ittysfil = open(Etcttys, 2);
+ if(ittysfil < 0) {
+ fprintf(stderr, "Cannot open %s for output: %s\n",
+ Etcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ (void)lseek(ittysfil, ttyslnbeg, 0);
+ if(read(ittysfil, &in, 1)<0) {
+ fprintf(stderr, "On %s write: %s\n",
+ Etcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ resettty = (in == '1');
+ out = enable ? '1' : '0';
+ (void)lseek(ittysfil, ttyslnbeg, 0);
+ if(write(ittysfil, &out, 1)<0) {
+ fprintf(stderr, "On %s write: %s\n",
+ Etcttys, sys_errlist[errno]);
+ exit(1);
+ }
+ (void)close(ittysfil);
+ return(in==out);
+}
+#endif !BSD4_3
+
+#ifdef sequent
+setmodem(ttyline, enable)
+char *ttyline; int enable;
+{
+ char *sysbuf[BUFSIZ];
+ sprintf(sysbuf,"/etc/ttyconfig /dev/%s -special %s", ttyline,
+ enable ? "-carrier" : "-nocarrier");
+ system(sysbuf);
+}
+#endif /* sequent */
+#ifdef vax
+/*
+ * Excerpted from (June 8, 1983 W.Sebok)
+ * > ttymodem.c - enable/disable modem control for tty lines.
+ * >
+ * > Knows about DZ11s and DH11/DM11s.
+ * > 23.3.83 - TS
+ * > modified to know about DMF's (hasn't been tested) Nov 8, 1984 - WLS
+ */
+
+
+setmodem(ttyline, enable)
+char *ttyline; int enable;
+{
+ dev_t dev;
+ int kmem;
+ int unit, line, nlines, addr, tflags;
+ int devtype=0;
+ char cflags; short sflags;
+#ifdef BSD4_2
+ int flags;
+#else
+ short flags;
+#endif
+ struct uba_device *ubinfo;
+ struct stat statb;
+ struct cdevsw cdevsw;
+
+ if(nl[CDEVSW].n_type == 0) {
+ fprintf(stderr, "No namelist.\n");
+ return(-1);
+ }
+
+ if((kmem = open(_PATH_KMEM, 2)) < 0) {
+ fprintf(stderr, "%s open: %s\n", _PATH_KMEM,
+ sys_errlist[errno]);
+ return(-1);
+ }
+
+ if(stat(ttyline, &statb) < 0) {
+ fprintf(stderr, "%s stat: %s\n", ttyline, sys_errlist[errno]);
+ return(-1);
+ }
+
+ if((statb.st_mode&S_IFMT) != S_IFCHR) {
+ fprintf(stderr, "%s is not a character device.\n",ttyline);
+ return(-1);
+ }
+
+ dev = statb.st_rdev;
+ (void)lseek(kmem,
+ (off_t) &(((struct cdevsw *)NLVALUE(CDEVSW))[major(dev)]),0);
+ (void)read(kmem, (char *) &cdevsw, sizeof cdevsw);
+
+ if((int)(cdevsw.d_open) == NLVALUE(DZOPEN)) {
+ devtype = DZ11;
+ unit = minor(dev) / NDZLINE;
+ line = minor(dev) % NDZLINE;
+ addr = (int) &(((int *)NLVALUE(DZINFO))[unit]);
+ (void)lseek(kmem, (off_t) NLVALUE(NDZ11), 0);
+ } else if((int)(cdevsw.d_open) == NLVALUE(DHOPEN)) {
+ devtype = DH11;
+ unit = minor(dev) / NDHLINE;
+ line = minor(dev) % NDHLINE;
+ addr = (int) &(((int *)NLVALUE(DHINFO))[unit]);
+ (void)lseek(kmem, (off_t) NLVALUE(NDH11), 0);
+ } else if((int)(cdevsw.d_open) == NLVALUE(DMFOPEN)) {
+ devtype = DMF;
+ unit = minor(dev) / NDMFLINE;
+ line = minor(dev) % NDMFLINE;
+ addr = (int) &(((int *)NLVALUE(DMFINFO))[unit]);
+ (void)lseek(kmem, (off_t) NLVALUE(NDMF), 0);
+ } else {
+ fprintf(stderr, "Device %s (%d/%d) unknown.\n", ttyline,
+ major(dev), minor(dev));
+ return(-1);
+ }
+
+ (void)read(kmem, (char *) &nlines, sizeof nlines);
+ if(minor(dev) >= nlines) {
+ fprintf(stderr, "Sub-device %d does not exist (only %d).\n",
+ minor(dev), nlines);
+ return(-1);
+ }
+
+ (void)lseek(kmem, (off_t)addr, 0);
+ (void)read(kmem, (char *) &ubinfo, sizeof ubinfo);
+ (void)lseek(kmem, (off_t) &(ubinfo->ui_flags), 0);
+ (void)read(kmem, (char *) &flags, sizeof flags);
+
+ tflags = 1<<line;
+ resetmodem = ((flags&tflags) == 0);
+ flags = enable ? (flags & ~tflags) : (flags | tflags);
+ (void)lseek(kmem, (off_t) &(ubinfo->ui_flags), 0);
+ (void)write(kmem, (char *) &flags, sizeof flags);
+ switch(devtype) {
+ case DZ11:
+ if((addr = NLVALUE(DZSCAR)) == 0) {
+ fprintf(stderr, "No dzsoftCAR.\n");
+ return(-1);
+ }
+ cflags = flags;
+ (void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0);
+ (void)write(kmem, (char *) &cflags, sizeof cflags);
+ break;
+ case DH11:
+ if((addr = NLVALUE(DHSCAR)) == 0) {
+ fprintf(stderr, "No dhsoftCAR.\n");
+ return(-1);
+ }
+ sflags = flags;
+ (void)lseek(kmem, (off_t) &(((short *)addr)[unit]), 0);
+ (void)write(kmem, (char *) &sflags, sizeof sflags);
+ break;
+ case DMF:
+ if((addr = NLVALUE(DMFSCAR)) == 0) {
+ fprintf(stderr, "No dmfsoftCAR.\n");
+ return(-1);
+ }
+ cflags = flags;
+ (void)lseek(kmem, (off_t) &(((char *)addr)[unit]), 0);
+ (void)write(kmem, (char *) &cflags, sizeof cflags);
+ break;
+ default:
+ fprintf(stderr, "Unknown device type\n");
+ return(-1);
+ }
+ return(0);
+}
+#endif /* vax */
+
+prefix(s1, s2)
+ register char *s1, *s2;
+{
+ register char c;
+
+ while ((c = *s1++) == *s2++)
+ if (c == '\0')
+ return (1);
+ return (c == '\0');
+}
+#else /* !DIALINOUT */
+main()
+{
+ fprintf(stderr,"acucntrl is not supported on this system\n");
+}
+#endif /* !DIALINOUT */
diff --git a/usr.bin/uucp/uupoll/Makefile b/usr.bin/uucp/uupoll/Makefile
new file mode 100644
index 0000000..97794d5
--- /dev/null
+++ b/usr.bin/uucp/uupoll/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uupoll
+CFLAGS+=-I${.CURDIR}/../includes
+BINMODE=6555
+DPADD= ${LIBCOMPAT}
+LDADD= ${LIBUU} -lcompat
+MAN8= uupoll.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uucp/uupoll/uupoll.8 b/usr.bin/uucp/uupoll/uupoll.8
new file mode 100644
index 0000000..f6ee49b
--- /dev/null
+++ b/usr.bin/uucp/uupoll/uupoll.8
@@ -0,0 +1,111 @@
+.\" Copyright (c) 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uupoll.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UUPOLL 8
+.Os BSD 4.3
+.Sh NAME
+.Nm uupoll
+.Nd poll a remote
+.Tn UUCP
+site
+.Sh SYNOPSIS
+.Nm uupoll
+.Op Fl g Ns Ar grade
+.Op Fl n
+.Ar system
+.Sh DESCRIPTION
+.Nm Uupoll
+is used to force a poll of a remote system. It queues a null job for the
+remote system and then invokes
+.Xr uucico 8 .
+.Pp
+The following options are available:
+.Bl -tag -width Fl
+.It Fl g Ns Ar grade
+Only send jobs of grade
+.Ar grade
+or higher on this call.
+.It Fl n
+Queue the null job, but do not invoke
+.Xr uucico .
+.El
+.Pp
+.Nm Uupoll
+is usually run by
+.Xr cron 5
+or by a user who wants to hurry a job along. A typical entry in
+.Em crontab
+could be:
+.Bd -literal
+0 0,8,16 * * * daemon /usr/bin/uupoll ihnp4
+0 4,12,20 * * * daemon /usr/bin/uupoll ucbvax
+.Ed
+.Pp
+This will poll
+.Em ihnp4
+at midnight, 0800, and 1600, and
+.Em ucbvax
+at 0400, noon, and 2000.
+.Pp
+If the local machine is already running
+.Xr uucico
+every
+hour and has a limited number of outgoing modems, a more elegant approach
+might be:
+.Bd -literal
+0 0,8,16 * * * daemon /usr/bin/uupoll -n ihnp4
+0 4,12,20 * * * daemon /usr/bin/uupoll -n ucbvax
+5 * * * * daemon /usr/lib/uucp/uucico -r1
+.Ed
+.Pp
+This will queue null jobs for the remote sites at the top of hour; they
+will be processed by
+.Xr uucico
+when it runs five minutes later.
+.Sh FILES
+.Bl -tag -width /usr/lib/uucp/UUCP -compact
+.It Pa /usr/lib/uucp/UUCP
+internal files/utilities
+.It Pa /var/spool/uucp/
+Spool directory
+.El
+.Sh SEE ALSO
+.Xr uucp 1 ,
+.Xr uux 1 ,
+.Xr uucico 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/uucp/uupoll/uupoll.c b/usr.bin/uucp/uupoll/uupoll.c
new file mode 100644
index 0000000..5d5e662
--- /dev/null
+++ b/usr.bin/uucp/uupoll/uupoll.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1986, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uupoll.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Poll named system(s).
+ *
+ * The poll occurs even if recent attempts have failed,
+ * but not if L.sys prohibits the call (e.g. wrong time of day).
+ *
+ * Original Author: Tom Truscott (rti!trt)
+ */
+
+#include "uucp.h"
+
+int TransferSucceeded = 1;
+struct timeb Now;
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char wrkpre[MAXFULLNAME];
+ char file[MAXFULLNAME];
+ char grade = 'A';
+ int nocall = 0;
+ int c;
+ char *sysname;
+ extern char *optarg;
+ extern int optind;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: uupoll [-gX] [-n] system ...\n");
+ cleanup(1);
+ }
+
+ if (chdir(Spool) < 0) {
+ syslog(LOG_WARNING, "chdir(%s) failed: %m", Spool);
+ cleanup(1);
+ }
+ strcpy(Progname, "uupoll");
+ uucpname(Myname);
+
+ while ((c = getopt(argc, argv, "g:n")) != EOF)
+ switch(c) {
+ case 'g':
+ grade = *optarg;
+ break;
+ case 'n':
+ nocall++;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "unknown option %s\n",
+ argv[optind-1]);
+ }
+
+ while(optind < argc) {
+ sysname = argv[optind++];
+ if (strcmp(sysname, Myname) == SAME) {
+ fprintf(stderr, "This *is* %s!\n", Myname);
+ continue;
+ }
+
+ if (versys(&sysname)) {
+ fprintf(stderr, "%s: unknown system.\n", sysname);
+ continue;
+ }
+ /* Remove any STST file that might stop the poll */
+ sprintf(wrkpre, "%s/LCK..%.*s", LOCKDIR, MAXBASENAME, sysname);
+ if (access(wrkpre, 0) < 0)
+ rmstat(sysname);
+ sprintf(wrkpre, "%c.%.*s", CMDPRE, SYSNSIZE, sysname);
+ if (!iswrk(file, "chk", Spool, wrkpre)) {
+ sprintf(file, "%s/%c.%.*s%cPOLL", subdir(Spool, CMDPRE),
+ CMDPRE, SYSNSIZE, sysname, grade);
+ close(creat(file, 0666));
+ }
+ /* Attempt the call */
+ if (!nocall)
+ xuucico(sysname);
+ }
+ cleanup(0);
+}
+
+cleanup(code)
+int code;
+{
+ exit(code);
+}
diff --git a/usr.bin/uucp/uuq/Makefile b/usr.bin/uucp/uuq/Makefile
new file mode 100644
index 0000000..60dbe0b
--- /dev/null
+++ b/usr.bin/uucp/uuq/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uuq
+CFLAGS+=-I${.CURDIR}/../includes
+BINMODE=6555
+DPADD= ${LIBCOMPAT}
+LDADD= ${LIBUU} -lcompat
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uucp/uuq/uuq.1 b/usr.bin/uucp/uuq/uuq.1
new file mode 100644
index 0000000..783d486
--- /dev/null
+++ b/usr.bin/uucp/uuq/uuq.1
@@ -0,0 +1,126 @@
+.\" Copyright (c) 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uuq.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UUQ 1
+.Os BSD 4.3
+.Sh NAME
+.Nm uuq
+.Nd examine or manipulate the uucp queue
+.Sh SYNOPSIS
+.Nm uuq
+.Op Fl l
+.Op Fl h
+.Op Fl s Ns Ar system
+.Op Fl u Ns Ar user
+.Op Fl d Ns Ar jobno
+.Op Fl r Ns Ar sdir
+.Op Fl b Ns Ar baud
+.Sh DESCRIPTION
+.Nm Uuq
+is used to examine (and possibly delete) entries in the uucp queue.
+.Pp
+When listing jobs,
+.Nm uuq
+uses a format reminiscent of
+.Xr ls .
+For the long format,
+information for each job listed includes
+job number, number of files to transfer, user who
+spooled the job, number of bytes to send, type of command requested
+(S for sending files, R for receiving files, X for remote uucp),
+and file or command desired.
+.Pp
+Several options are available:
+.Bl -tag -width Ar
+.It Fl h
+Print only the summary lines for each system. Summary lines give system
+name, number of jobs for the system, and total number of bytes to send.
+.It Fl l
+Specifies a long format listing. The default is to list only the
+job numbers sorted across the page.
+.It Fl s Ns Ar system
+Limit output to jobs for systems whose system names begin with
+.Ar system .
+.It Fl u Ns Ar user
+Limit output to jobs for users whose login names begin with
+.Ar user .
+.It Fl d Ns Ar jobno
+Delete job number
+.Ar jobno
+(as obtained from a previous
+.Nm uuq
+command)
+from the uucp queue.
+Only the
+.Tn UUCP
+Administrator is permitted to delete jobs.
+.It Fl r Ns Ar sdir
+Look for files in the spooling directory
+.Ar sdir
+instead of the default
+directory.
+.It Fl b Ns Ar baud
+Use
+.Ar baud
+to compute the transfer time instead of the default
+1200 baud.
+.El
+.Sh FILES
+.Bl -tag -width /usr/spool/uucp/Dhostname./D.x -compact
+.It Pa /usr/spool/uucp/
+Default spool directory
+.It Pa /usr/spool/uucp/C./C.*
+Control files
+.It Pa /usr/spool/uucp/D Ns Em hostname ./D.*
+Outgoing data files
+.It Pa /usr/spool/uucp/X./X.*
+Outgoing execution files
+.El
+.Sh SEE ALSO
+.Xr uucp 1 ,
+.Xr uux 1 ,
+.Xr uulog 1 ,
+.Xr uusnap 8
+.Sh BUGS
+No information is available on work requested by the remote machine.
+.Pp
+The user who requests a remote uucp command is unknown.
+.Pp
+.Dq Li uq \-l
+can be horrendously slow.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/uucp/uuq/uuq.c b/usr.bin/uucp/uuq/uuq.c
new file mode 100644
index 0000000..7abb25c
--- /dev/null
+++ b/usr.bin/uucp/uuq/uuq.c
@@ -0,0 +1,435 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uuq.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * uuq - looks at uucp queues
+ *
+ * Lou Salkind
+ * New York University
+ *
+ */
+
+#include "uucp.h"
+#include <stdio.h>
+
+#ifdef NDIR
+#include "libndir/ndir.h"
+#else !NDIR
+#include <sys/dir.h>
+#endif !NDIR
+#include <sys/stat.h>
+
+#define NOSYS (struct sys *)0
+
+#define W_TYPE wrkvec[0]
+#define W_FILE1 wrkvec[1]
+#define W_FILE2 wrkvec[2]
+#define W_USER wrkvec[3]
+#define W_OPTNS wrkvec[4]
+#define W_DFILE wrkvec[5]
+#define W_MODE wrkvec[6]
+#define WSUFSIZE 5 /* work file name suffix size */
+
+struct sys {
+ char s_name[8];
+ int s_njobs;
+ off_t s_bytes;
+ struct job *s_jobp;
+ struct sys *s_sysp;
+};
+
+struct job {
+ int j_files;
+ int j_flags;
+ char j_jobno[WSUFSIZE];
+ char j_user[22];
+ char j_fname[128];
+ char j_grade;
+ off_t j_bytes;
+ time_t j_date;
+ struct job *j_jobp;
+};
+
+struct sys *syshead;
+struct sys *getsys();
+int jcompare();
+char *sysname;
+char *user;
+char *rmjob;
+int hflag;
+int lflag;
+
+char *malloc(), *calloc();
+double atof();
+float baudrate = 2400.;
+char Username[BUFSIZ];
+char Filename[BUFSIZ];
+int Maxulen = 0;
+struct timeb Now;
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ register int i;
+ register struct sys *sp;
+ register struct job *jp;
+ struct job **sortjob;
+ int nsys;
+ extern char *optarg;
+ extern int optind;
+
+ strcpy(Progname, "uuq");
+ uucpname(Myname);
+
+ while ((i = getopt(argc, argv, "r:S:s:u:d:b:hl")) != EOF)
+ switch (i) {
+ case 'r':
+ case 'S':
+ Spool = optarg;
+ break;
+ case 's':
+ sysname = optarg;
+ if (strlen(sysname) > SYSNSIZE)
+ sysname[SYSNSIZE] = '\0';
+ break;
+ case 'u':
+ user = optarg;
+ break;
+ case 'd':
+ rmjob = optarg;
+ break;
+ case 'b':
+ baudrate = atof(optarg);
+ break;
+ case 'h':
+ hflag++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ default:
+ fprintf(stderr,
+ "usage: uuq [-l] [-h] [-ssystem] [-uuser] [-djobno] [-rspool] [-bbaudrate]\n");
+ exit(0);
+ }
+
+ subchdir(Spool);
+ baudrate *= 0.7; /* reduce speed because of protocol overhead */
+ baudrate *= 7.5; /* convert to chars/minute (60/8) */
+ gather();
+ nsys = 0;
+ for (sp = syshead; sp; sp = sp->s_sysp) {
+ if (sp->s_njobs == 0)
+ continue;
+ if (!hflag && nsys++ > 0)
+ putchar('\n');
+ printf("%s: %d %s", sp->s_name,
+ sp->s_njobs, sp->s_njobs > 1 ? "jobs" : "job");
+ if (lflag) {
+ float minutes;
+ int hours;
+ /* The 80 * njobs is because of the uucp handshaking */
+ minutes = (float)(sp->s_bytes + 80 * sp->s_njobs)/baudrate;
+ hours = minutes/60;
+ printf(", %ld bytes, ", sp->s_bytes);
+ if (minutes > 60){
+ printf("%d hour%s, ",hours,
+ hours > 1 ? "s": "");
+ minutes -= 60 * hours;
+ }
+ printf("%3.1f minutes (@ effective baudrate of %d)",
+ minutes,(int)(baudrate/6));
+ }
+ putchar('\n');
+ if (hflag)
+ continue;
+ /* sort them babies! */
+ sortjob = (struct job **)calloc(sp->s_njobs, sizeof (struct job *));
+ for (i=0, jp=sp->s_jobp; i < sp->s_njobs; i++, jp=jp->j_jobp)
+ sortjob[i] = jp;
+ qsort(sortjob, sp->s_njobs, sizeof (struct job *), jcompare);
+ for (i = 0; i < sp->s_njobs; i++) {
+ jp = sortjob[i];
+ if (lflag) {
+ printf("%s %2d %-*s%7ld%5.1f %-12.12s %c %.*s\n",
+ jp->j_jobno, jp->j_files, Maxulen, jp->j_user, jp->j_bytes, jp->j_bytes/baudrate,
+ ctime(&jp->j_date) + 4, jp->j_flags, sizeof (jp->j_fname), jp->j_fname
+ );
+ } else {
+ printf("%s", jp->j_jobno);
+ putchar((i+1)%10 ? '\t' : '\n');
+ }
+ /* There's no need to keep the force poll if jobs > 1*/
+ if (sp->s_njobs > 1 && strcmp("POLL", jp->j_jobno)==0) {
+ char pbuf[BUFSIZ];
+ sprintf(pbuf,"%s/%c.%s%cPOLL",
+ subdir(Spool, CMDPRE), CMDPRE,
+ sp->s_name, jp->j_grade);
+ (void) unlink(pbuf);
+ }
+ }
+ if (!lflag && (sp->s_njobs%10))
+ putchar('\n');
+ }
+ exit(0);
+}
+
+jcompare(j1, j2)
+struct job **j1, **j2;
+{
+ int delta;
+
+ delta = (*j1)->j_grade - (*j2)->j_grade;
+ if (delta)
+ return delta;
+ return(strcmp((*j1)->j_jobno,(*j2)->j_jobno));
+}
+
+/*
+ * Get all the command file names
+ */
+gather()
+{
+ struct direct *d;
+ DIR *df;
+
+ /*
+ * Find all the spool files in the spooling directory
+ */
+ if ((df = opendir(subdir(Spool, CMDPRE))) == NULL) {
+ fprintf(stderr, "can't examine spooling area\n");
+ exit(1);
+ }
+ for (;;) {
+ if ((d = readdir(df)) == NULL)
+ break;
+ if (d->d_namlen <= 2 || d->d_name[0] != CMDPRE ||
+ d->d_name[1] != '.')
+ continue;
+ if (analjob(d->d_name) < 0) {
+ fprintf(stderr, "out of memory\n");
+ break;
+ }
+ }
+ closedir(df);
+}
+
+/*
+ * analjob does the grunge work of verifying jobs
+ */
+#include <pwd.h>
+analjob(filename)
+char *filename;
+{
+ struct job *jp;
+ struct sys *sp;
+ char sbuf[MAXNAMLEN+1], str[256], nbuf[256];
+ char *jptr, *wrkvec[20];
+ char grade;
+ FILE *fp, *df;
+ struct stat statb;
+ int files, gotname, i;
+ off_t bytes;
+
+ strncpy(sbuf, filename, MAXNAMLEN);
+ sbuf[MAXNAMLEN] = '\0';
+ jptr = sbuf + strlen(sbuf) - WSUFSIZE;
+ grade = *jptr;
+ *jptr++ = 0;
+ /*
+ * sbuf+2 now points to sysname name (null terminated)
+ * jptr now points to job number (null terminated)
+ */
+ if (rmjob) {
+ if (strcmp(rmjob, jptr))
+ return(0);
+ } else {
+ if ((sp = getsys(sbuf+2)) == NOSYS)
+ return(0);
+ if (!lflag) {
+ /* SHOULD USE A SMALLER STRUCTURE HERE */
+ jp = (struct job *)malloc(sizeof(struct job));
+ if (jp == (struct job *)0)
+ return(-1);
+ strcpy(jp->j_jobno, jptr);
+ jp->j_jobp = sp->s_jobp;
+ jp->j_grade = grade;
+ sp->s_jobp = jp;
+ sp->s_njobs++;
+ return(1);
+ }
+ }
+ if ((fp = fopen(subfile(filename), "r")) == NULL) {
+ perror(subfile(filename));
+ return(0);
+ }
+ files = 0;
+ bytes = 0;
+ gotname = 0;
+ while (fgets(str, sizeof str, fp)) {
+ if (getargs(str, wrkvec, 20) <= 0)
+ continue;
+ if (rmjob) {
+ int myuid;
+ struct passwd *pw;
+ /*
+ * Make sure person who is removing data files is
+ * the person who created it or root.
+ */
+ myuid = getuid();
+ pw = getpwnam(W_USER);
+ if (myuid && (pw == NULL || myuid != pw->pw_uid)) {
+ fprintf(stderr, "Permission denied.\n");
+ exit(1);
+ }
+ if (W_TYPE[0] == 'S' && !index(W_OPTNS, 'c')) {
+ unlink(subfile(W_DFILE));
+ fprintf(stderr, "Removing data file %s\n", W_DFILE);
+ }
+ continue;
+ }
+ if (user && (W_TYPE[0] == 'X' || !prefix(user, W_USER))) {
+ fclose(fp);
+ return(0);
+ }
+ files++;
+ if (W_TYPE[0] == 'S') {
+ if (strcmp(W_DFILE, "D.0") &&
+ stat(subfile(W_DFILE), &statb) >= 0)
+ bytes += statb.st_size;
+ else if (stat(subfile(W_FILE1), &statb) >= 0)
+ bytes += statb.st_size;
+ }
+ /* amusing heuristic */
+#define isXfile(s) (s[0]=='D' && s[strlen(s)-WSUFSIZE]=='X')
+ if (gotname == 0 && isXfile(W_FILE1)) {
+ if ((df = fopen(subfile(W_FILE1), "r")) == NULL)
+ continue;
+ while (fgets(nbuf, sizeof nbuf, df)) {
+ nbuf[strlen(nbuf) - 1] = '\0';
+ if (nbuf[0] == 'C' && nbuf[1] == ' ') {
+ strcpy(Filename, nbuf+2);
+ gotname++;
+ } else if (nbuf[0] == 'R' && nbuf[1] == ' ') {
+ register char *p, *q, *r;
+ r = q = p = nbuf+2;
+ do {
+ if (*p == '!' || *p == '@'){
+ r = q;
+ q = p+1;
+ }
+ } while (*p++);
+
+ strcpy(Username, r);
+ W_USER = Username;
+ }
+ }
+ fclose(df);
+ }
+ }
+ fclose(fp);
+ if (rmjob) {
+ unlink(subfile(filename));
+ fprintf(stderr, "Removing command file %s\n", filename);
+ exit(0);
+ }
+ if (files == 0) {
+ static char *wtype = "X";
+ static char *wfile = "forced poll";
+ if (strcmp("POLL", &filename[strlen(filename)-4])) {
+ fprintf(stderr, "%.14s: empty command file\n", filename);
+ return(0);
+ }
+ W_TYPE = wtype;
+ W_FILE1 = wfile;
+ }
+ jp = (struct job *)malloc(sizeof(struct job));
+ if (jp == (struct job *)0)
+ return(-1);
+ strcpy(jp->j_jobno, jptr);
+ jp->j_files = files;
+ jp->j_bytes = bytes;
+ jp->j_grade = grade;
+ jp->j_flags = W_TYPE[0];
+ strncpy(jp->j_user, W_TYPE[0]=='X' ? "---" : W_USER, 20 );
+ jp->j_user[20] = '\0';
+ i = strlen(jp->j_user);
+ if (i > Maxulen)
+ Maxulen = i;
+ /* SHOULD ADD ALL INFORMATION IN THE WHILE LOOP */
+ if (gotname)
+ strncpy(jp->j_fname, Filename, sizeof jp->j_fname);
+ else
+ strncpy(jp->j_fname, W_FILE1, sizeof jp->j_fname);
+ stat(subfile(filename), &statb);
+ jp->j_date = statb.st_mtime;
+ jp->j_jobp = sp->s_jobp;
+ sp->s_jobp = jp;
+ sp->s_njobs++;
+ sp->s_bytes += jp->j_bytes;
+ return(1);
+}
+
+struct sys *
+getsys(s)
+register char *s;
+{
+ register struct sys *sp;
+
+ for (sp = syshead; sp; sp = sp->s_sysp)
+ if (strcmp(s, sp->s_name) == 0)
+ return(sp);
+ if (sysname && !prefix(sysname, s))
+ return(NOSYS);
+ sp = (struct sys *)malloc(sizeof(struct sys));
+ if (sp == NOSYS)
+ return(NOSYS);
+ strcpy(sp->s_name, s);
+ sp->s_njobs = 0;
+ sp->s_jobp = (struct job *)0;
+ sp->s_sysp = syshead;
+ sp->s_bytes = 0;
+ syshead = sp;
+ return(sp);
+}
diff --git a/usr.bin/uucp/uusend/Makefile b/usr.bin/uucp/uusend/Makefile
new file mode 100644
index 0000000..6c13fb4
--- /dev/null
+++ b/usr.bin/uucp/uusend/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uusend
+LINKS= ${BINDIR}/uusend ${BINDIR}/ruusend
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uucp/uusend/uusend.1 b/usr.bin/uucp/uusend/uusend.1
new file mode 100644
index 0000000..9379307
--- /dev/null
+++ b/usr.bin/uucp/uusend/uusend.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uusend.1 8.3 (Berkeley) 2/16/94
+.\"
+.Dd February 16, 1994
+.Dt UUSEND 1
+.Os BSD 4
+.Sh NAME
+.Nm uusend
+.Nd send a file to a remote host
+.Sh SYNOPSIS
+.Nm uusend
+.Op Fl m Ar mode
+.Ar sourcefile
+.Ar sys1!sys2!..!remotefile
+.Sh DESCRIPTION
+.Nm Uusend
+sends a file to a given location on a remote system.
+The system need not be directly connected to the local
+system, but a chain of
+.Xr uucp 1
+links must to connect the two systems.
+.Pp
+Available option:
+.Bl -tag -width Fl
+.It Fl m Ar mode
+The mode of the file on the remote
+end is taken from the octal number given.
+Otherwise, the mode of the input file will be used.
+.El
+.Pp
+The sourcefile
+can be
+.Ql Fl ,
+meaning to use the standard input.
+Both of these options are primarily intended for internal use of
+.Nm uusend .
+.Pp
+The remotefile can include the
+.Em ~userid
+syntax.
+.Sh DIAGNOSTICS
+If anything goes wrong any further away than the first system down
+the line, you will never hear about it.
+.Sh SEE ALSO
+.Xr uux 1 ,
+.Xr uucp 1 ,
+.Xr uuencode 1
+.Sh BUGS
+This command should not exist, since
+.Xr uucp
+should handle it.
+.Pp
+All systems along the line must have the
+.Nm uusend
+command available and allow remote execution of it.
+.Pp
+Some uucp systems have a bug where binary files cannot be the
+input to a
+.Xr uux 1
+command. If this bug exists in any system along the line,
+the file will show up severely munged.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/uucp/uusend/uusend.c b/usr.bin/uucp/uusend/uusend.c
new file mode 100644
index 0000000..207108e
--- /dev/null
+++ b/usr.bin/uucp/uusend/uusend.c
@@ -0,0 +1,403 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uusend.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * uusend: primitive operation to allow uucp like copy of binary files
+ * but handle indirection over systems.
+ *
+ * usage: uusend [-r] [-m ooo] localfile sysname1!sysname2!...!destfile
+ * uusend [-r] [-m ooo] - sysname1!sysname2!...!destfile
+ *
+ * Author: Mark Horton, May 1980.
+ *
+ * "-r" switch added. Has same effect as "-r" in uux. 11/82 CCW
+ *
+ * Error recovery (a la uucp) added & ifdefs for ruusend (as in rmail).
+ * Checks for illegal access to /usr/lib/uucp.
+ * February 1983 Christopher Woodbury
+ * Fixed mode set[ug]id loophole. 4/8/83 CCW
+ *
+ * Add '-f' to make uusend syntax more similar to UUCP. "destname"
+ * can now be a directory. June 1983 CCW
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <pwd.h>
+
+/*
+ * define RECOVER to permit requests like 'uusend file sys1!sys2!~uucp'
+ * (abbreviation for 'uusend file sys1!sys2!~uucp/file').
+ * define DEBUG to keep log of uusend uusage.
+ * define RUUSEND if neighboring sites permit 'ruusend',
+ * which they certainly should to avoid security holes
+ */
+#define RECOVER
+/*#define DEBUG "/usr/spool/uucp/uusend.log"/**/
+
+FILE *in, *out;
+FILE *dout;
+
+extern FILE *popen();
+extern char *index(), *strcpy(), *strcat(), *ctime();
+
+#ifdef RUUSEND
+int rsend;
+#endif RUUSEND
+int mode = -1; /* mode to chmod new file to */
+char *nextsys; /* next system in the chain */
+char dnbuf[200]; /* buffer for result of ~user/file */
+char cmdbuf[256]; /* buffer to build uux command in */
+char *rflg = ""; /* default value of rflg ccw -- 1 Nov '82 */
+
+struct passwd *user; /* entry in /etc/passwd for ~user */
+struct passwd *getpwnam();
+struct stat stbuf;
+
+char *excl; /* location of first ! in destname */
+char *sl; /* location of first / in destname */
+char *sourcename; /* argv[1] */
+char *destname; /* argv[2] */
+char *UULIB = "/usr/lib/uucp"; /* UUCP lib directory */
+
+#ifdef RECOVER
+char *UUPUB = "/usr/spool/uucppublic/"; /* public UUCP directory */
+char *filename; /* file name from end of destname */
+char *getfname(); /* routine to get filename from destname */
+int fflg;
+char f[100]; /* name of default output file */
+#else !RECOVER
+char *f = ""; /* so we waste a little space */
+#endif !RECOVER
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ register int c;
+ long count;
+ extern char **environ;
+
+#ifdef DEBUG
+ long t;
+ umask(022);
+ dout = fopen(DEBUG, "a");
+ if (dout == NULL) {
+ printf("Cannot append to %s\n", DEBUG);
+ exit(1);
+ }
+ freopen(DEBUG, "a", stdout);
+ fprintf(dout, "\nuusend run: ");
+ for (c=0; c<argc; c++)
+ fprintf(dout, "%s ", argv[c]);
+ time(&t);
+ fprintf(dout, "%s", ctime(&t));
+#endif DEBUG
+
+#ifdef RUUSEND
+ if(argv[0][0] == 'r')
+ rsend++;
+#endif RUUSEND
+ while (argc > 1 && argv[1][0] == '-' && argv[1][1]) {
+ switch(argv[1][1]) {
+ case 'm':
+ sscanf(argv[2], "%o", &mode);
+ mode &= 0777; /* fix set[ug]id loophole */
+ argc--; argv++;
+ break;
+ case 'r': /* -r flag for uux */
+ rflg = "-r ";
+ break;
+#ifdef RECOVER
+ case 'f':
+ fflg++;
+ strcpy(f, argv[1]);
+ break;
+#endif RECOVER
+ default:
+ fprintf(stderr, "Bad flag: %s\n", argv[1]);
+ break;
+ }
+ argc--; argv++;
+ }
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: uusend [-m ooo] [-r] -/file sys!sys!..!rfile\n");
+ exit(1);
+ }
+
+ sourcename = argv[1];
+ destname = argv[2];
+
+ if (sourcename[0] == '-')
+ in = stdin;
+ else {
+#ifdef RUUSEND
+ if (rsend) {
+ fprintf(stderr, "illegal input\n");
+ exit(2);
+ }
+#endif RUUSEND
+ in = fopen(sourcename, "r");
+ if (in == NULL) {
+ perror(argv[1]);
+ exit(2);
+ }
+ if (!fflg || f[2] == '\0') {
+ strcpy(f, "-f");
+ strcat(f, getfname(sourcename));
+ fflg++;
+ }
+ }
+
+ excl = index(destname, '!');
+ if (excl) {
+ /*
+ * destname is on a remote system.
+ */
+ nextsys = destname;
+ *excl++ = 0;
+ destname = excl;
+ if (mode < 0) {
+ fstat(fileno(in), &stbuf);
+ mode = stbuf.st_mode & 0777;
+ }
+#ifdef RUUSEND
+ sprintf(cmdbuf,"uux -gn -z %s- \"%s!ruusend %s -m %o - (%s)\"",
+#else !RUUSEND
+ sprintf(cmdbuf, "uux -gn -z %s- \"%s!uusend %s -m %o - (%s)\"",
+#endif !RUUSEND
+ rflg, nextsys, f, mode, destname);
+#ifdef DEBUG
+ fprintf(dout, "remote: nextsys='%s', destname='%s', cmd='%s'\n", nextsys, destname, cmdbuf);
+#endif DEBUG
+ out = popen(cmdbuf, "w");
+ } else {
+ /*
+ * destname is local.
+ */
+ if (destname[0] == '~') {
+#ifdef DEBUG
+ fprintf(dout, "before ~: '%s'\n", destname);
+fflush(dout);
+#endif DEBUG
+ sl = index(destname, '/');
+#ifdef RECOVER
+ if (sl == NULL && !fflg) {
+ fprintf(stderr, "Illegal ~user\n");
+ exit(3);
+ }
+ for (sl = destname; *sl != '\0'; sl++)
+ ; /* boy, is this a hack! */
+#else !RECOVER
+ if (sl == NULL) {
+ fprintf(stderr, "Illegal ~user\n");
+ exit(3);
+ }
+ *sl++ = 0;
+#endif !RECOVER
+ user = getpwnam(destname+1);
+ if (user == NULL) {
+ fprintf(stderr, "No such user as %s\n",
+ destname);
+#ifdef RECOVER
+ if ((filename =getfname(sl)) == NULL &&
+ !fflg)
+ exit(4);
+ strcpy(dnbuf, UUPUB);
+ if (fflg)
+ strcat(dnbuf, &f[2]);
+ else
+ strcat(dnbuf, filename);
+ }
+ else {
+ strcpy(dnbuf, user->pw_dir);
+ strcat(dnbuf, "/");
+ strcat(dnbuf, sl);
+ }
+#else !RECOVER
+ exit(4);
+ }
+ strcpy(dnbuf, user->pw_dir);
+ strcat(dnbuf, "/");
+ strcat(dnbuf, sl);
+#endif !RECOVER
+ destname = dnbuf;
+ }
+#ifdef RECOVER
+ else
+ destname = strcpy(dnbuf, destname);
+#endif !RECOVER
+ if(strncmp(UULIB, destname, strlen(UULIB)) == 0) {
+ fprintf(stderr, "illegal file: %s", destname);
+ exit(4);
+ }
+#ifdef RECOVER
+ if (stat(destname, &stbuf) == 0 &&
+ (stbuf.st_mode & S_IFMT) == S_IFDIR &&
+ fflg) {
+ strcat(destname, "/");
+ strcat(destname, &f[2]);
+ }
+#endif RECOVER
+ out = fopen(destname, "w");
+#ifdef DEBUG
+ fprintf(dout, "local, file='%s'\n", destname);
+#endif DEBUG
+ if (out == NULL) {
+ perror(destname);
+#ifdef RECOVER
+ if (strncmp(destname,UUPUB,strlen(UUPUB)) == 0)
+ exit(5); /* forget it! */
+ filename = getfname(destname);
+ if (destname == dnbuf) /* cmdbuf is scratch */
+ filename = strcpy(cmdbuf, filename);
+ destname = strcpy(dnbuf, UUPUB);
+ if (user != NULL) {
+ strcat(destname, user->pw_name);
+ if (stat(destname, &stbuf) == -1) {
+ mkdir(destname, 0777);
+ }
+ strcat(destname, "/");
+ }
+ if (fflg)
+ strcat(destname, &f[2]);
+ else
+ strcat(destname, filename);
+ if ((out = fopen(destname, "w")) == NULL)
+ exit(5); /* all for naught! */
+#else !RECOVER
+ exit(5);
+#endif !RECOVER
+ }
+ if (mode > 0)
+ chmod(destname, mode); /* don't bother to check it */
+ }
+
+ /*
+ * Now, in any case, copy from in to out.
+ */
+
+ count = 0;
+ while ((c=getc(in)) != EOF) {
+ putc(c, out);
+ count++;
+ }
+#ifdef DEBUG
+ fprintf(dout, "count %ld bytes\n", count);
+ fclose(dout);
+#endif DEBUG
+
+ fclose(in);
+ fclose(out); /* really should pclose in that case */
+ exit(0);
+}
+
+/*
+ * Return the ptr in sp at which the character c appears;
+ * NULL if not found. Included so I don't have to fight the
+ * index/strchr battle.
+ */
+
+#define NULL 0
+
+char *
+index(sp, c)
+register char *sp, c;
+{
+ do {
+ if (*sp == c)
+ return(sp);
+ } while (*sp++);
+ return(NULL);
+}
+
+#ifdef RECOVER
+char *
+getfname(p)
+register char *p;
+{
+ register char *s;
+ s = p;
+ while (*p != '\0')
+ p++;
+ if (p == s)
+ return (NULL);
+ for (;p != s; p--)
+ if (*p == '/') {
+ p++;
+ break;
+ }
+ return (p);
+}
+
+#ifndef BSD4_2
+makedir(dirname, mode)
+char *dirname;
+int mode;
+{
+ register int pid;
+ int retcode, status;
+ switch ((pid = fork())) {
+ case -1: /* error */
+ return (-1);
+ case 0: /* child */
+ umask(0);
+ execl("/bin/mkdir", "mkdir", dirname, (char *)0);
+ exit(1);
+ /* NOTREACHED */
+ default: /* parent */
+ while ((retcode=wait(&status)) != pid && retcode != -1)
+ ;
+ if (retcode == -1)
+ return -1;
+ else {
+ chmod(dirname, mode);
+ return status;
+ }
+ }
+ /* NOTREACHED */
+}
+#endif !BSD4_2
+#endif RECOVER
diff --git a/usr.bin/uucp/uusnap/Makefile b/usr.bin/uucp/uusnap/Makefile
new file mode 100644
index 0000000..d23ab2a
--- /dev/null
+++ b/usr.bin/uucp/uusnap/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uusnap
+CFLAGS+=-I${.CURDIR}/../includes
+BINMODE=6555
+MAN8= uusnap.8
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uucp/uusnap/uusnap.8 b/usr.bin/uucp/uusnap/uusnap.8
new file mode 100644
index 0000000..92f0e330
--- /dev/null
+++ b/usr.bin/uucp/uusnap/uusnap.8
@@ -0,0 +1,80 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uusnap.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UUSNAP 8
+.Os BSD 4.2
+.Sh NAME
+.Nm uusnap
+.Nd show snapshot of the
+.Tn UUCP
+system
+.Sh SYNOPSIS
+.Nm uusnap
+.Sh DESCRIPTION
+.Nm Uusnap
+displays in tabular format a synopsis of the current
+.Tn UUCP
+situation. The format of each line is as follows:
+.Bd -literal -offset indent -compact
+
+site N Cmds N Data N Xqts Message
+
+.Ed
+Where "site" is the name of the site with work, "N" is a count of
+each of the three possible types of work (command, data, or remote execute),
+and "Message" is the current status message for that
+site as found in the
+.Tn STST
+file.
+.Pp
+Included in "Message" may be the time left before
+.Tn UUCP
+can re-try the
+call, and the count of the number of times that
+.Tn UUCP
+has tried
+(unsuccessfully) to reach the site.
+.Sh SEE ALSO
+.Xr uucp 1 ,
+.Xr uux 1 ,
+.Xr uuq 1 ,
+.Xr uucico 8
+.Rs
+.%T "UUCP Implementation Guide"
+.Re
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.bin/uucp/uusnap/uusnap.c b/usr.bin/uucp/uusnap/uusnap.c
new file mode 100644
index 0000000..565c52a
--- /dev/null
+++ b/usr.bin/uucp/uusnap/uusnap.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Adams. Originally by RJKing WECo-MG6565 May 83.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uusnap.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "uucp.h"
+#include <sys/stat.h>
+#ifdef NDIR
+#include "ndir.h"
+#else
+#include <sys/dir.h>
+#endif
+#include <ctype.h>
+
+#define NSYSTEM 300 /* max # of systems queued */
+
+#define CMDSLEN 5 /* Length of trailer */
+#define DATALEN 5 /* Length of trailer */
+#define XEQTLEN 5 /* Length of trailer */
+#define NUMCTRS 3 /* # file types to count */
+#define CMDTYPE 0 /* Index into scnt.cntr */
+#define DATTYPE 1 /* Index into scnt.cntr */
+#define XEQTYPE 2 /* Index into scnt.cntr */
+
+struct scnt { /* System count structure */
+ char name[MAXBASENAME+1]; /* Name of system */
+ short cntr[NUMCTRS]; /* Count */
+ char stst[32]; /* STST Message */
+ time_t locked; /* If LCK..sys present */
+ int st_type; /* STST Type */
+ int st_count; /* STST Count */
+ time_t st_lastime; /* STST Last time tried */
+ time_t st_retry; /* STST Secs to retry */
+ };
+
+int sndx; /* Number of systems */
+struct scnt sys[NSYSTEM]; /* Systems queued */
+int xqtisrunning = 0;
+
+main()
+{
+ register int i, j, nlen = 0;
+ time_t curtime, t;
+
+ dodir(CMDSDIR, "C.", CMDSLEN, '\0', CMDTYPE);
+ dodir(DATADIR, "D.", DATALEN, '\0', DATTYPE);
+ dodir(XEQTDIR, "X.", XEQTLEN, 'X', XEQTYPE);
+ getstst(SPOOL);
+ time(&curtime);
+ for(i=0; i<sndx; ++i)
+ if((j = strlen(sys[i].name)) > nlen)
+ nlen = j;
+ for(i=0; i<sndx; ++i) {
+ t = (sys[i].st_lastime +sys[i].st_retry) - curtime;
+
+ /* decide if STST text is worth printing */
+ if (-t < ONEDAY*2 && sys[i].st_type == SS_WRONGTIME) {
+ sys[i].stst[0] = '\0';
+ if (sys[i].cntr[0]+sys[i].cntr[1]+sys[i].cntr[2] == 0)
+ continue; /* ignore entire line */
+ }
+
+ printf("%-*.*s ", nlen, nlen, sys[i].name);
+ if(sys[i].cntr[CMDTYPE])
+ printf("%3.d Cmd%s ", sys[i].cntr[CMDTYPE],
+ sys[i].cntr[CMDTYPE]>1?"s":" ");
+ else
+ printf(" --- ");
+ if(sys[i].cntr[DATTYPE])
+ printf("%3.d Data ", sys[i].cntr[DATTYPE]);
+ else
+ printf(" --- ");
+ if(sys[i].cntr[XEQTYPE])
+ printf("%3.d Xqt%s ", sys[i].cntr[XEQTYPE],
+ sys[i].cntr[XEQTYPE]>1?"s":" ");
+ else
+ printf(" --- ");
+ if(*sys[i].stst == '\0' || sys[i].locked > sys[i].st_lastime) {
+ if(sys[i].locked)
+ printf("LOCKED\n");
+ else
+ printf("\n");
+ continue;
+ }
+ printf("%s ", sys[i].stst);
+ /* decide if STST info is worth pursuing */
+ if (-t < ONEDAY*2 && (sys[i].st_count == 0
+ || sys[i].st_type == SS_WRONGTIME
+ || (sys[i].st_type == SS_INPROGRESS && sys[i].locked))) {
+ printf("\n");
+ continue;
+ }
+ t = (sys[i].st_lastime +sys[i].st_retry) - curtime;
+ if (-t < ONEDAY*2 && sys[i].st_type != SS_FAIL)
+ t = 0;
+
+ if (sys[i].st_count > MAXRECALLS)
+ printf("at MAX RECALLS");
+ else if (-t >= ONEDAY*2)
+ printf("%ld days ago", (long)-t/ONEDAY);
+ else if (t <= 0)
+ printf("Retry time reached");
+ else if (t < 60)
+ printf("Retry time %ld sec%s", (long)(t%60),
+ (t%60)!=1? "s": "");
+ else
+ printf("Retry time %ld min%s", (long)(t/60),
+ (t/60)!=1? "s": "");
+ if(sys[i].st_count > 1)
+ printf(" Count: %d\n", sys[i].st_count);
+ else
+ printf("\n");
+ }
+ if (xqtisrunning)
+ printf("\nUuxqt is running\n");
+ exit(0);
+}
+
+dodir(dnam, prfx, flen, fchr, type)
+char *dnam, *prfx;
+int flen;
+char fchr;
+int type;
+{
+ register struct direct *dentp;
+ register DIR *dirp;
+ register int i, fnamlen, plen;
+ char fnam[MAXNAMLEN+1];
+
+ plen = strlen(prfx);
+ if(chdir(dnam) < 0) {
+ perror(dnam);
+ exit(1);
+ }
+ if ((dirp = opendir(".")) == NULL) {
+ perror(dnam);
+ exit(1);
+ }
+ while((dentp = readdir(dirp)) != NULL) {
+ if(*dentp->d_name == '.')
+ continue;
+ if(strncmp(dentp->d_name, prfx, plen) != SAME) {
+ fprintf(stderr, "strange file (%s) in %s\n",
+ dentp->d_name, dnam);
+ continue;
+ }
+ strcpy(fnam, &dentp->d_name[plen]);
+ fnamlen = strlen(fnam);
+ if(flen > 0) {
+ fnamlen -= flen;
+ fnam[fnamlen] = '\0';
+ fnamlen = MAXBASENAME; /* yes, after = '\0'*/
+ } else {
+ for(; fnamlen>0; --fnamlen) {
+ if(fnam[fnamlen] == fchr) {
+ fnam[fnamlen] = '\0';
+ break;
+ }
+ }
+ fnamlen = MAXBASENAME;
+ }
+ for(i=0; i<sndx; ++i) {
+ if(strncmp(fnam, sys[i].name, fnamlen) == SAME) {
+ ++sys[i].cntr[type];
+ break;
+ }
+ }
+ if(i == sndx) {
+ strcpy(sys[i].name, fnam);
+ ++sys[i].cntr[type];
+ if(++sndx >= NSYSTEM) {
+ sndx = NSYSTEM-1;
+ fprintf(stderr,"Too many system names.\n");
+ }
+ }
+ }
+ closedir(dirp);
+}
+
+getstst(sdir)
+char *sdir;
+{
+ register int i, csys;
+ register char *tp;
+ char fnam[MAXNAMLEN+1], buff[128];
+ register struct direct *dentp;
+ register DIR *dirp;
+ register FILE *st;
+ struct stat stbuf;
+ long atol();
+
+ if (chdir(sdir) < 0) {
+ perror(sdir);
+ exit(1);
+ }
+ if ((dirp = opendir(LOCKDIR)) == NULL) {
+ perror(sdir);
+ exit(1);
+ }
+ while ((dentp = readdir(dirp)) != NULL) {
+ if (strcmp(&dentp->d_name[5], X_LOCK) == SAME) {
+ xqtisrunning++;
+ continue;
+ }
+ if(strncmp(dentp->d_name, "LCK..", 5) == SAME) {
+ if(strncmp(&dentp->d_name[5], "tty", 3) == SAME ||
+ strncmp(&dentp->d_name[5], "cul", 3) == SAME)
+ continue;
+ strcpy(fnam, dentp->d_name);
+ for(csys=0; csys<sndx; ++csys) {
+ if(strncmp(&fnam[5], sys[csys].name, SYSNSIZE)
+ == SAME)
+ break;
+ }
+ strcpy(sys[csys].name, &fnam[5]);
+ if(csys == sndx) {
+ ++sndx;
+ }
+ if (stat(fnam, &stbuf) < 0)
+ sys[csys].locked = 1;
+ else
+ sys[csys].locked = stbuf.st_mtime;
+ continue;
+ }
+ }
+ closedir(dirp);
+ if (chdir("STST") < 0) {
+ perror("STST");
+ exit(1);
+ }
+ if ((dirp = opendir(".")) == NULL) {
+ perror("STST");
+ exit(1);
+ }
+ while ((dentp = readdir(dirp)) != NULL) {
+ if(*dentp->d_name == '.')
+ continue;
+ strcpy(fnam, dentp->d_name);
+ for(csys=0; csys<sndx; ++csys) {
+ if(strncmp(fnam, sys[csys].name, SYSNSIZE) == SAME)
+ break;
+ }
+ strcpy(sys[csys].name, fnam);
+ if(csys == sndx) {
+ ++sndx;
+ }
+ if((st = fopen(fnam, "r")) == NULL) {
+ sys[csys].stst[0] = '\0';
+ continue;
+ }
+ buff[0] = '\0';
+ fgets(buff, sizeof(buff), st);
+ fclose(st);
+ if(tp = rindex(buff, ' '))
+ *tp = '\0'; /* drop system name */
+ else
+ continue;
+ for(i=0, tp=buff; i<4; ++i, ++tp)
+ if((tp = index(tp, ' ')) == NULL)
+ break;
+ if(i != 4)
+ continue;
+ strncpy(sys[csys].stst, tp, sizeof(sys[csys].stst));
+ tp = buff;
+ sys[csys].st_type = atoi(tp);
+ tp = index(tp+1, ' ');
+ sys[csys].st_count = atoi(tp+1);
+ tp = index(tp+1, ' ');
+ sys[csys].st_lastime = atol(tp+1);
+ tp = index(tp+1, ' ');
+ sys[csys].st_retry = atol(tp+1);
+ }
+}
+/*
+ * Return the ptr in sp at which the character c appears;
+ * NULL if not found
+ */
+
+char *
+index(sp, c)
+register char *sp, c;
+{
+ do {
+ if (*sp == c)
+ return sp;
+ } while (*sp++);
+ return NULL;
+}
+
+/*
+ * Return the ptr in sp at which the character c last
+ * appears; NULL if not found
+*/
+
+char *
+rindex(sp, c)
+register char *sp, c;
+{
+ register char *r;
+
+ r = NULL;
+ do {
+ if (*sp == c)
+ r = sp;
+ } while (*sp++);
+ return r;
+}
diff --git a/usr.bin/uudecode/Makefile b/usr.bin/uudecode/Makefile
new file mode 100644
index 0000000..6f515ae
--- /dev/null
+++ b/usr.bin/uudecode/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uudecode
+NOMAN= noman
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uudecode/uudecode.c b/usr.bin/uudecode/uudecode.c
new file mode 100644
index 0000000..af87847
--- /dev/null
+++ b/usr.bin/uudecode/uudecode.c
@@ -0,0 +1,187 @@
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uudecode.c 8.2 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * uudecode [file ...]
+ *
+ * create the specified file, decoding as you go.
+ * used with uuencode.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+char *filename;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int errno;
+ int rval;
+
+ if (*++argv) {
+ rval = 0;
+ do {
+ if (!freopen(filename = *argv, "r", stdin)) {
+ (void)fprintf(stderr, "uudecode: %s: %s\n",
+ *argv, strerror(errno));
+ rval = 1;
+ continue;
+ }
+ rval |= decode();
+ } while (*++argv);
+ } else {
+ filename = "stdin";
+ rval = decode();
+ }
+ exit(rval);
+}
+
+decode()
+{
+ extern int errno;
+ struct passwd *pw;
+ register int n;
+ register char ch, *p;
+ int mode, n1;
+ char buf[MAXPATHLEN];
+
+ /* search for header line */
+ do {
+ if (!fgets(buf, sizeof(buf), stdin)) {
+ (void)fprintf(stderr,
+ "uudecode: %s: no \"begin\" line\n", filename);
+ return(1);
+ }
+ } while (strncmp(buf, "begin ", 6));
+ (void)sscanf(buf, "begin %o %s", &mode, buf);
+
+ /* handle ~user/file format */
+ if (buf[0] == '~') {
+ if (!(p = index(buf, '/'))) {
+ (void)fprintf(stderr, "uudecode: %s: illegal ~user.\n",
+ filename);
+ return(1);
+ }
+ *p++ = NULL;
+ if (!(pw = getpwnam(buf + 1))) {
+ (void)fprintf(stderr, "uudecode: %s: no user %s.\n",
+ filename, buf);
+ return(1);
+ }
+ n = strlen(pw->pw_dir);
+ n1 = strlen(p);
+ if (n + n1 + 2 > MAXPATHLEN) {
+ (void)fprintf(stderr, "uudecode: %s: path too long.\n",
+ filename);
+ return(1);
+ }
+ bcopy(p, buf + n + 1, n1 + 1);
+ bcopy(pw->pw_dir, buf, n);
+ buf[n] = '/';
+ }
+
+ /* create output file, set mode */
+ if (!freopen(buf, "w", stdout) ||
+ fchmod(fileno(stdout), mode&0666)) {
+ (void)fprintf(stderr, "uudecode: %s: %s: %s\n", buf,
+ filename, strerror(errno));
+ return(1);
+ }
+
+ /* for each input line */
+ for (;;) {
+ if (!fgets(p = buf, sizeof(buf), stdin)) {
+ (void)fprintf(stderr, "uudecode: %s: short file.\n",
+ filename);
+ return(1);
+ }
+#define DEC(c) (((c) - ' ') & 077) /* single character decode */
+ /*
+ * `n' is used to avoid writing out all the characters
+ * at the end of the file.
+ */
+ if ((n = DEC(*p)) <= 0)
+ break;
+ for (++p; n > 0; p += 4, n -= 3)
+ if (n >= 3) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ putchar(ch);
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ putchar(ch);
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ putchar(ch);
+ }
+ else {
+ if (n >= 1) {
+ ch = DEC(p[0]) << 2 | DEC(p[1]) >> 4;
+ putchar(ch);
+ }
+ if (n >= 2) {
+ ch = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
+ putchar(ch);
+ }
+ if (n >= 3) {
+ ch = DEC(p[2]) << 6 | DEC(p[3]);
+ putchar(ch);
+ }
+ }
+ }
+ if (!fgets(buf, sizeof(buf), stdin) || strcmp(buf, "end\n")) {
+ (void)fprintf(stderr, "uudecode: %s: no \"end\" line.\n",
+ filename);
+ return(1);
+ }
+ return(0);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "usage: uudecode [file ...]\n");
+ exit(1);
+}
diff --git a/usr.bin/uuencode/Makefile b/usr.bin/uuencode/Makefile
new file mode 100644
index 0000000..cf14277
--- /dev/null
+++ b/usr.bin/uuencode/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= uuencode
+MAN1= uuencode.1
+MAN5= uuencode.format.5
+MLINKS= uuencode.1 uudecode.1 \
+ uuencode.format.5 uuencode.5
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/uuencode/uuencode.1 b/usr.bin/uuencode/uuencode.1
new file mode 100644
index 0000000..df1ebf2
--- /dev/null
+++ b/usr.bin/uuencode/uuencode.1
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uuencode.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UUENCODE 1
+.Os BSD 4
+.Sh NAME
+.Nm uuencode ,
+.Nm uudecode
+.Nd encode/decode a binary file
+.Sh SYNOPSIS
+.Nm uuencode
+.Op Ar file
+.Ar name
+.Nm uudecode
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Uuencode
+and
+.Nm uudecode
+are used to transmit binary files over transmission mediums
+that do not support other than simple
+.Tn ASCII
+data.
+.Pp
+.Nm Uuencode
+reads
+.Ar file
+(or by default the standard input) and writes an encoded version
+to the standard output.
+The encoding uses only printing
+.Tn ASCII
+characters and includes the
+mode of the file and the operand
+.Ar name
+for use by
+.Nm uudecode .
+.Pp
+.Nm Uudecode
+transforms
+.Em uuencoded
+files (or by default, the standard input) into the original form.
+The resulting file is named
+.Ar name
+and will have the mode of the original file except that setuid
+and execute bits are not retained.
+.Nm Uudecode
+ignores any leading and trailing lines.
+.Sh EXAMPLES
+The following example packages up a source tree, compresses it,
+uuencodes it and mails it to a user on another system.
+When
+.Nm uudecode
+is run on the target system, the file ``src_tree.tar.Z'' will be
+created which may then be uncompressed and extracted into the original
+tree.
+.Pp
+.Bd -literal -offset indent -compact
+tar cf \- src_tree \&| compress \&|
+uuencode src_tree.tar.Z \&| mail sys1!sys2!user
+.Ed
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr uucp 1 ,
+.Xr uuencode 5 ,
+.Xr format 5
+.Sh BUGS
+The encoded form of the file is expanded by 35% (3 bytes become 4 plus
+control information).
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/uuencode/uuencode.c b/usr.bin/uuencode/uuencode.c
new file mode 100644
index 0000000..774cee9
--- /dev/null
+++ b/usr.bin/uuencode/uuencode.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)uuencode.c 8.2 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * uuencode [input] output
+ *
+ * Encode a file so it can be mailed to a remote system.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern int errno;
+ struct stat sb;
+ int mode;
+ char *strerror();
+
+ while (getopt(argc, argv, "") != EOF)
+ usage();
+ argv += optind;
+ argc -= optind;
+
+ switch(argc) {
+ case 2: /* optional first argument is input file */
+ if (!freopen(*argv, "r", stdin) || fstat(fileno(stdin), &sb)) {
+ (void)fprintf(stderr, "uuencode: %s: %s.\n",
+ *argv, strerror(errno));
+ exit(1);
+ }
+#define RWX (S_IRWXU|S_IRWXG|S_IRWXO)
+ mode = sb.st_mode & RWX;
+ ++argv;
+ break;
+ case 1:
+#define RW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+ mode = RW & ~umask(RW);
+ break;
+ case 0:
+ default:
+ usage();
+ }
+
+ (void)printf("begin %o %s\n", mode, *argv);
+ encode();
+ (void)printf("end\n");
+ if (ferror(stdout)) {
+ (void)fprintf(stderr, "uuencode: write error.\n");
+ exit(1);
+ }
+ exit(0);
+}
+
+/* ENC is the basic 1 character encoding function to make a char printing */
+#define ENC(c) ((c) ? ((c) & 077) + ' ': '`')
+
+/*
+ * copy from in to out, encoding as you go along.
+ */
+encode()
+{
+ register int ch, n;
+ register char *p;
+ char buf[80];
+
+ while (n = fread(buf, 1, 45, stdin)) {
+ ch = ENC(n);
+ if (putchar(ch) == EOF)
+ break;
+ for (p = buf; n > 0; n -= 3, p += 3) {
+ ch = *p >> 2;
+ ch = ENC(ch);
+ if (putchar(ch) == EOF)
+ break;
+ ch = (*p << 4) & 060 | (p[1] >> 4) & 017;
+ ch = ENC(ch);
+ if (putchar(ch) == EOF)
+ break;
+ ch = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
+ ch = ENC(ch);
+ if (putchar(ch) == EOF)
+ break;
+ ch = p[2] & 077;
+ ch = ENC(ch);
+ if (putchar(ch) == EOF)
+ break;
+ }
+ if (putchar('\n') == EOF)
+ break;
+ }
+ if (ferror(stdin)) {
+ (void)fprintf(stderr, "uuencode: read error.\n");
+ exit(1);
+ }
+ ch = ENC('\0');
+ (void)putchar(ch);
+ (void)putchar('\n');
+}
+
+usage()
+{
+ (void)fprintf(stderr,"usage: uuencode [infile] remotefile\n");
+ exit(1);
+}
diff --git a/usr.bin/uuencode/uuencode.format.5 b/usr.bin/uuencode/uuencode.format.5
new file mode 100644
index 0000000..81591b8
--- /dev/null
+++ b/usr.bin/uuencode/uuencode.format.5
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uuencode.format.5 8.2 (Berkeley) 1/12/94
+.\"
+.Dd January 12, 1994
+.Dt UUENCODE 5
+.Os BSD 4
+.Sh NAME
+.Nm uuencode
+.Nd format of an encoded uuencode file
+.Sh DESCRIPTION
+Files output by
+.Xr uuencode 1
+consist of a header line,
+followed by a number of body lines,
+and a trailer line.
+The
+.Xr uudecode 1
+command
+will ignore any lines preceding the header or
+following the trailer.
+Lines preceding a header must not, of course,
+look like a header.
+.Pp
+The header line is distinguished by having the first
+6 characters
+.Dq begin\ \&
+(note the trailing space).
+The word
+.Em begin
+is followed by a mode (in octal),
+and a string which names the remote file.
+A space separates the three items in the header line.
+.Pp
+The body consists of a number of lines, each at most 62 characters
+long (including the trailing newline).
+These consist of a character count,
+followed by encoded characters,
+followed by a newline.
+The character count is a single printing character,
+and represents an integer, the number of bytes
+the rest of the line represents.
+Such integers are always in the range from 0 to 63 and can
+be determined by subtracting the character space (octal 40)
+from the character.
+.Pp
+Groups of 3 bytes are stored in 4 characters, 6 bits per character.
+All are offset by a space to make the characters printing.
+The last line may be shorter than the normal 45 bytes.
+If the size is not a multiple of 3, this fact can be determined
+by the value of the count on the last line.
+Extra garbage will be included to make the character count a multiple
+of 4.
+The body is terminated by a line with a count of zero.
+This line consists of one
+.Tn ASCII
+space.
+.Pp
+The trailer line consists of
+.Dq end
+on a line by itself.
+.Sh SEE ALSO
+.Xr uuencode 1 ,
+.Xr uudecode 1 ,
+.Xr uusend 1 ,
+.Xr uucp 1 ,
+.Xr mail 1
+.Sh HISTORY
+The
+.Nm uuencode
+file format appeared in
+.Bx 4.0 .
+.\" It was named uuencode.5 prior to 4.3
diff --git a/usr.bin/vacation/Makefile b/usr.bin/vacation/Makefile
new file mode 100644
index 0000000..130d439
--- /dev/null
+++ b/usr.bin/vacation/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= vacation
+DPADD= ${LIBDBM}
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vacation/vacation.1 b/usr.bin/vacation/vacation.1
new file mode 100644
index 0000000..0c74c39
--- /dev/null
+++ b/usr.bin/vacation/vacation.1
@@ -0,0 +1,171 @@
+.\" Copyright (c) 1985, 1987, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vacation.1 8.1 (Berkeley) 6/16/93
+.\"
+.Dd June 16, 1993
+.Dt VACATION 1
+.Os BSD 4.3
+.Sh NAME
+.Nm vacation
+.Nd return ``I am not here'' indication
+.Sh SYNOPSIS
+.Nm vacation
+.Fl i
+.Op Fl r Ar interval
+.Nm vacation
+.Op Fl a Ar alias
+.Ar login
+.Sh DESCRIPTION
+.Nm Vacation
+returns a message to the sender of a message telling them that you
+are currently not reading your mail. The intended use is in a
+.Pa .forward
+file. For example, your
+.Pa .forward
+file might have:
+.Bd -literal -offset indent
+\eeric, "|/usr/bin/vacation -a allman eric"
+.Ed
+which would send messages to you (assuming your login name was eric) and
+reply to any messages for
+.Dq eric
+or
+.Dq allman .
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl a Ar alias
+Handle messages for
+.Ar alias
+in the same manner as those received for the user's
+login name.
+.It Fl i
+Initialize the vacation database files. It should be used
+before you modify your
+.Pa .forward
+file.
+.It Fl r
+Set the reply interval to
+.Ar interval
+days. The default is one week. An interval of
+.Dq 0
+means that
+a reply is sent to each message, and an interval of
+.Dq Li infinite
+(actually, any non-numeric character) will never send more than
+one reply. It should be noted that intervals of
+.Dq Li \&0
+are quite
+dangerous, as it allows mailers to get into
+.Dq I am on vacation
+loops.
+.El
+.Pp
+No message will be sent unless
+.Ar login
+(or an
+.Ar alias
+supplied using the
+.Fl a
+option) is part of either the
+.Dq To:
+or
+.Dq Cc:
+headers of the mail.
+No messages from
+.Dq ???-REQUEST ,
+.Dq Postmaster ,
+.Dq Tn UUCP ,
+.Dq MAILER ,
+or
+.Dq MAILER-DAEMON
+will be replied to (where these strings are
+case insensitive) nor is a notification sent if a
+.Dq Precedence: bulk
+or
+.Dq Precedence: junk
+line is included in the mail headers.
+The people who have sent you messages are maintained as an
+.Xr ndbm 3
+database in the file
+.Pa .vacation.db
+in your home directory.
+.Pp
+.Nm Vacation
+expects a file
+.Pa .vacation.msg ,
+in your home directory, containing a message to be sent back to each
+sender. It should be an entire message (including headers). For
+example, it might contain:
+.Pp
+.Bd -unfilled -offset indent -compact
+From: eric@CS.Berkeley.EDU (Eric Allman)
+Subject: I am on vacation
+Delivered-By-The-Graces-Of: The Vacation program
+Precedence: bulk
+
+I am on vacation until July 22. If you have something urgent,
+please contact Keith Bostic <bostic@CS.Berkeley.EDU>.
+--eric
+.Ed
+.Pp
+.Nm Vacation
+reads the first line from the standard input for a
+.Ux
+.Dq From
+line to determine the sender.
+.Xr Sendmail 8
+includes this
+.Dq From
+line automatically.
+.Pp
+Fatal errors, such as calling
+.Nm vacation
+with incorrect arguments, or with non-existent
+.Ar login Ns Ar s ,
+are logged in the system log file, using
+.Xr syslog 8 .
+.Sh FILES
+.Bl -tag -width "vacation.dirxxx" -compact
+.It Pa ~/.vacation.db
+database file
+.It Pa ~/.vacation.msg
+message to send
+.El
+.Sh SEE ALSO
+.Xr sendmail 8 ,
+.Xr syslog 8
+.Sh HISTORY
+The
+.Nm vacation
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/vacation/vacation.c b/usr.bin/vacation/vacation.c
new file mode 100644
index 0000000..ae549c8
--- /dev/null
+++ b/usr.bin/vacation/vacation.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 1983, 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)vacation.c 8.2 (Berkeley) 1/26/94";
+#endif /* not lint */
+
+/*
+** Vacation
+** Copyright (c) 1983 Eric P. Allman
+** Berkeley, California
+*/
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <db.h>
+#include <time.h>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+
+/*
+ * VACATION -- return a message to the sender when on vacation.
+ *
+ * This program is invoked as a message receiver. It returns a
+ * message specified by the user to whomever sent the mail, taking
+ * care not to return a message too often to prevent "I am on
+ * vacation" loops.
+ */
+
+#define MAXLINE 1024 /* max line from mail header */
+#define VDB ".vacation.db" /* dbm's database */
+#define VMSG ".vacation.msg" /* vacation message */
+
+typedef struct alias {
+ struct alias *next;
+ char *name;
+} ALIAS;
+ALIAS *names;
+
+DB *db;
+
+char from[MAXLINE];
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind, opterr;
+ extern char *optarg;
+ struct passwd *pw;
+ ALIAS *cur;
+ time_t interval;
+ int ch, iflag;
+
+ opterr = iflag = 0;
+ interval = -1;
+ while ((ch = getopt(argc, argv, "a:Iir:")) != EOF)
+ switch((char)ch) {
+ case 'a': /* alias */
+ if (!(cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))))
+ break;
+ cur->name = optarg;
+ cur->next = names;
+ names = cur;
+ break;
+ case 'I': /* backward compatible */
+ case 'i': /* init the database */
+ iflag = 1;
+ break;
+ case 'r':
+ if (isdigit(*optarg)) {
+ interval = atol(optarg) * 86400;
+ if (interval < 0)
+ usage();
+ }
+ else
+ interval = LONG_MAX;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ if (!iflag)
+ usage();
+ if (!(pw = getpwuid(getuid()))) {
+ syslog(LOG_ERR,
+ "vacation: no such user uid %u.\n", getuid());
+ exit(1);
+ }
+ }
+ else if (!(pw = getpwnam(*argv))) {
+ syslog(LOG_ERR, "vacation: no such user %s.\n", *argv);
+ exit(1);
+ }
+ if (chdir(pw->pw_dir)) {
+ syslog(LOG_NOTICE,
+ "vacation: no such directory %s.\n", pw->pw_dir);
+ exit(1);
+ }
+
+ db = dbopen(VDB, O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
+ S_IRUSR|S_IWUSR, DB_HASH, NULL);
+ if (!db) {
+ syslog(LOG_NOTICE, "vacation: %s: %s\n", VDB, strerror(errno));
+ exit(1);
+ }
+
+ if (interval != -1)
+ setinterval(interval);
+
+ if (iflag) {
+ (void)(db->close)(db);
+ exit(0);
+ }
+
+ if (!(cur = malloc((u_int)sizeof(ALIAS))))
+ exit(1);
+ cur->name = pw->pw_name;
+ cur->next = names;
+ names = cur;
+
+ readheaders();
+ if (!recent()) {
+ setreply();
+ (void)(db->close)(db);
+ sendmessage(pw->pw_name);
+ }
+ else
+ (void)(db->close)(db);
+ exit(0);
+ /* NOTREACHED */
+}
+
+/*
+ * readheaders --
+ * read mail headers
+ */
+readheaders()
+{
+ register ALIAS *cur;
+ register char *p;
+ int tome, cont;
+ char buf[MAXLINE];
+
+ cont = tome = 0;
+ while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
+ switch(*buf) {
+ case 'F': /* "From " */
+ cont = 0;
+ if (!strncmp(buf, "From ", 5)) {
+ for (p = buf + 5; *p && *p != ' '; ++p);
+ *p = '\0';
+ (void)strcpy(from, buf + 5);
+ if (p = index(from, '\n'))
+ *p = '\0';
+ if (junkmail())
+ exit(0);
+ }
+ break;
+ case 'P': /* "Precedence:" */
+ cont = 0;
+ if (strncasecmp(buf, "Precedence", 10) ||
+ buf[10] != ':' && buf[10] != ' ' && buf[10] != '\t')
+ break;
+ if (!(p = index(buf, ':')))
+ break;
+ while (*++p && isspace(*p));
+ if (!*p)
+ break;
+ if (!strncasecmp(p, "junk", 4) ||
+ !strncasecmp(p, "bulk", 4) ||
+ !strncasecmp(p, "list", 4))
+ exit(0);
+ break;
+ case 'C': /* "Cc:" */
+ if (strncmp(buf, "Cc:", 3))
+ break;
+ cont = 1;
+ goto findme;
+ case 'T': /* "To:" */
+ if (strncmp(buf, "To:", 3))
+ break;
+ cont = 1;
+ goto findme;
+ default:
+ if (!isspace(*buf) || !cont || tome) {
+ cont = 0;
+ break;
+ }
+findme: for (cur = names; !tome && cur; cur = cur->next)
+ tome += nsearch(cur->name, buf);
+ }
+ if (!tome)
+ exit(0);
+ if (!*from) {
+ syslog(LOG_NOTICE, "vacation: no initial \"From\" line.\n");
+ exit(1);
+ }
+}
+
+/*
+ * nsearch --
+ * do a nice, slow, search of a string for a substring.
+ */
+nsearch(name, str)
+ register char *name, *str;
+{
+ register int len;
+
+ for (len = strlen(name); *str; ++str)
+ if (*str == *name && !strncasecmp(name, str, len))
+ return(1);
+ return(0);
+}
+
+/*
+ * junkmail --
+ * read the header and return if automagic/junk/bulk/list mail
+ */
+junkmail()
+{
+ static struct ignore {
+ char *name;
+ int len;
+ } ignore[] = {
+ "-request", 8, "postmaster", 10, "uucp", 4,
+ "mailer-daemon", 13, "mailer", 6, "-relay", 6,
+ NULL, NULL,
+ };
+ register struct ignore *cur;
+ register int len;
+ register char *p;
+
+ /*
+ * This is mildly amusing, and I'm not positive it's right; trying
+ * to find the "real" name of the sender, assuming that addresses
+ * will be some variant of:
+ *
+ * From site!site!SENDER%site.domain%site.domain@site.domain
+ */
+ if (!(p = index(from, '%')))
+ if (!(p = index(from, '@'))) {
+ if (p = rindex(from, '!'))
+ ++p;
+ else
+ p = from;
+ for (; *p; ++p);
+ }
+ len = p - from;
+ for (cur = ignore; cur->name; ++cur)
+ if (len >= cur->len &&
+ !strncasecmp(cur->name, p - cur->len, cur->len))
+ return(1);
+ return(0);
+}
+
+#define VIT "__VACATION__INTERVAL__TIMER__"
+
+/*
+ * recent --
+ * find out if user has gotten a vacation message recently.
+ * use bcopy for machines with alignment restrictions
+ */
+recent()
+{
+ DBT key, data;
+ time_t then, next;
+
+ /* get interval time */
+ key.data = VIT;
+ key.size = sizeof(VIT);
+ if ((db->get)(db, &key, &data, 0))
+ next = 86400 * 7;
+ else
+ bcopy(data.data, &next, sizeof(next));
+
+ /* get record for this address */
+ key.data = from;
+ key.size = strlen(from);
+ if (!(db->get)(db, &key, &data, 0)) {
+ bcopy(data.data, &then, sizeof(then));
+ if (next == LONG_MAX || then + next > time(NULL))
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * setinterval --
+ * store the reply interval
+ */
+setinterval(interval)
+ time_t interval;
+{
+ DBT key, data;
+
+ key.data = VIT;
+ key.size = sizeof(VIT);
+ data.data = &interval;
+ data.size = sizeof(interval);
+ (void)(db->put)(db, &key, &data, 0);
+}
+
+/*
+ * setreply --
+ * store that this user knows about the vacation.
+ */
+setreply()
+{
+ DBT key, data;
+ time_t now;
+
+ key.data = from;
+ key.size = strlen(from);
+ (void)time(&now);
+ data.data = &now;
+ data.size = sizeof(now);
+ (void)(db->put)(db, &key, &data, 0);
+}
+
+/*
+ * sendmessage --
+ * exec sendmail to send the vacation file to sender
+ */
+sendmessage(myname)
+ char *myname;
+{
+ FILE *mfp, *sfp;
+ int i;
+ int pvect[2];
+ char buf[MAXLINE];
+
+ mfp = fopen(VMSG, "r");
+ if (mfp == NULL) {
+ syslog(LOG_NOTICE, "vacation: no ~%s/%s file.\n", myname, VMSG);
+ exit(1);
+ }
+ if (pipe(pvect) < 0) {
+ syslog(LOG_ERR, "vacation: pipe: %s", strerror(errno));
+ exit(1);
+ }
+ i = vfork();
+ if (i < 0) {
+ syslog(LOG_ERR, "vacation: fork: %s", strerror(errno));
+ exit(1);
+ }
+ if (i == 0) {
+ dup2(pvect[0], 0);
+ close(pvect[0]);
+ close(pvect[1]);
+ fclose(mfp);
+ execl(_PATH_SENDMAIL, "sendmail", "-f", myname, from, NULL);
+ syslog(LOG_ERR, "vacation: can't exec %s: %s",
+ _PATH_SENDMAIL, strerror(errno));
+ exit(1);
+ }
+ close(pvect[0]);
+ sfp = fdopen(pvect[1], "w");
+ fprintf(sfp, "To: %s\n", from);
+ while (fgets(buf, sizeof buf, mfp))
+ fputs(buf, sfp);
+ fclose(mfp);
+ fclose(sfp);
+}
+
+usage()
+{
+ syslog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias] login\n",
+ getuid());
+ exit(1);
+}
diff --git a/usr.bin/vgrind/Makefile b/usr.bin/vgrind/Makefile
new file mode 100644
index 0000000..ea692f6
--- /dev/null
+++ b/usr.bin/vgrind/Makefile
@@ -0,0 +1,26 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= vfontedpr
+SRCS= regexp.c vfontedpr.c
+MAN1= vgrind.1
+MAN5= vgrindefs.5
+BINDIR= /usr/libexec
+EXTRA= vgrindefs.src.db
+CLEANFILES+= ${EXTRA}
+
+all: ${EXTRA}
+
+${EXTRA}: ${.CURDIR}/vgrindefs.src
+ cap_mkdb -f vgrindefs.src ${.CURDIR}/vgrindefs.src
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/vgrind.sh ${DESTDIR}/usr/bin/vgrind
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/vgrindefs.src \
+ ${DESTDIR}/usr/share/misc/vgrindefs
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ vgrindefs.src.db ${DESTDIR}/usr/share/misc/vgrindefs.db
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/tmac.vgrind \
+ ${DESTDIR}/usr/share/tmac
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vgrind/RETEST/Makefile b/usr.bin/vgrind/RETEST/Makefile
new file mode 100644
index 0000000..1e50c81
--- /dev/null
+++ b/usr.bin/vgrind/RETEST/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= retest
+SRCS= regexp.c retest.c
+.PATH: ${.CURDIR}/..
+NOMAN= noman
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vgrind/RETEST/retest.c b/usr.bin/vgrind/RETEST/retest.c
new file mode 100644
index 0000000..ce953cb
--- /dev/null
+++ b/usr.bin/vgrind/RETEST/retest.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)retest.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+
+int l_onecase = 0;
+char * _start;
+char * _escaped;
+char * convexp();
+char * expmatch();
+main()
+{
+ char reg[132];
+ char *ireg;
+ char str[132];
+ char *match;
+ char matstr[132];
+ char c;
+
+ while (1) {
+ printf ("\nexpr: ");
+ scanf ("%s", reg);
+ ireg = convexp(reg);
+ match = ireg;
+ while(*match) {
+ switch (*match) {
+
+ case '\\':
+ case '(':
+ case ')':
+ case '|':
+ printf ("%c", *match);
+ break;
+
+ default:
+ if (isalnum(*match))
+ printf("%c", *match);
+ else
+ printf ("<%03o>", *match);
+ break;
+ }
+ match++;
+ }
+ printf("\n");
+ getchar();
+ while(1) {
+ printf ("string: ");
+ match = str;
+ while ((c = getchar()) != '\n')
+ *match++ = c;
+ *match = 0;
+ if (str[0] == '#')
+ break;
+ matstr[0] = 0;
+ _start = str;
+ _escaped = 0;
+ match = expmatch (str, ireg, matstr);
+ if (match == 0)
+ printf ("FAILED\n");
+ else
+ printf ("match\nmatstr = %s\n", matstr);
+ }
+
+ }
+}
diff --git a/usr.bin/vgrind/extern.h b/usr.bin/vgrind/extern.h
new file mode 100644
index 0000000..0526d56
--- /dev/null
+++ b/usr.bin/vgrind/extern.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+typedef int boolean;
+
+extern boolean _escaped; /* if last character was an escape */
+extern char *_start; /* start of the current string */
+extern char *l_acmbeg; /* string introducing a comment */
+extern char *l_acmend; /* string ending a comment */
+extern char *l_blkbeg; /* string begining of a block */
+extern char *l_blkend; /* string ending a block */
+extern char *l_chrbeg; /* delimiter for character constant */
+extern char *l_chrend; /* delimiter for character constant */
+extern char *l_combeg; /* string introducing a comment */
+extern char *l_comend; /* string ending a comment */
+extern char l_escape; /* character used to escape characters */
+extern char *l_keywds[]; /* keyword table address */
+extern boolean l_onecase; /* upper and lower case are equivalent */
+extern char *l_prcbeg; /* regular expr for procedure begin */
+extern char *l_strbeg; /* delimiter for string constant */
+extern char *l_strend; /* delimiter for string constant */
+extern boolean l_toplex; /* procedures only defined at top lex level */
+extern char *language; /* the language indicator */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int STRNCMP __P((char *, char *, int));
+extern char *convexp __P((char *));
+extern char *expmatch __P((char *, char *, char *));
+__END_DECLS
+
diff --git a/usr.bin/vgrind/pathnames.h b/usr.bin/vgrind/pathnames.h
new file mode 100644
index 0000000..157f91f
--- /dev/null
+++ b/usr.bin/vgrind/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_VGRINDEFS "/usr/share/misc/vgrindefs"
diff --git a/usr.bin/vgrind/regexp.c b/usr.bin/vgrind/regexp.c
new file mode 100644
index 0000000..2022edb
--- /dev/null
+++ b/usr.bin/vgrind/regexp.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)regexp.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+
+#define FALSE 0
+#define TRUE !(FALSE)
+#define NIL 0
+
+static void expconv __P((void));
+
+boolean _escaped; /* true if we are currently _escaped */
+char *_start; /* start of string */
+boolean l_onecase; /* true if upper and lower equivalent */
+
+#define makelower(c) (isupper((c)) ? tolower((c)) : (c))
+
+/* STRNCMP - like strncmp except that we convert the
+ * first string to lower case before comparing
+ * if l_onecase is set.
+ */
+
+int
+STRNCMP(s1, s2, len)
+ register char *s1,*s2;
+ register int len;
+{
+ if (l_onecase) {
+ do
+ if (*s2 - makelower(*s1))
+ return (*s2 - makelower(*s1));
+ else {
+ s2++;
+ s1++;
+ }
+ while (--len);
+ } else {
+ do
+ if (*s2 - *s1)
+ return (*s2 - *s1);
+ else {
+ s2++;
+ s1++;
+ }
+ while (--len);
+ }
+ return(0);
+}
+
+/* The following routine converts an irregular expression to
+ * internal format.
+ *
+ * Either meta symbols (\a \d or \p) or character strings or
+ * operations ( alternation or perenthesizing ) can be
+ * specified. Each starts with a descriptor byte. The descriptor
+ * byte has STR set for strings, META set for meta symbols
+ * and OPER set for operations.
+ * The descriptor byte can also have the OPT bit set if the object
+ * defined is optional. Also ALT can be set to indicate an alternation.
+ *
+ * For metasymbols the byte following the descriptor byte identities
+ * the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '('). For
+ * strings the byte after the descriptor is a character count for
+ * the string:
+ *
+ * meta symbols := descriptor
+ * symbol
+ *
+ * strings := descriptor
+ * character count
+ * the string
+ *
+ * operatins := descriptor
+ * symbol
+ * character count
+ */
+
+/*
+ * handy macros for accessing parts of match blocks
+ */
+#define MSYM(A) (*(A+1)) /* symbol in a meta symbol block */
+#define MNEXT(A) (A+2) /* character following a metasymbol block */
+
+#define OSYM(A) (*(A+1)) /* symbol in an operation block */
+#define OCNT(A) (*(A+2)) /* character count */
+#define ONEXT(A) (A+3) /* next character after the operation */
+#define OPTR(A) (A+*(A+2)) /* place pointed to by the operator */
+
+#define SCNT(A) (*(A+1)) /* byte count of a string */
+#define SSTR(A) (A+2) /* address of the string */
+#define SNEXT(A) (A+2+*(A+1)) /* character following the string */
+
+/*
+ * bit flags in the descriptor
+ */
+#define OPT 1
+#define STR 2
+#define META 4
+#define ALT 8
+#define OPER 16
+
+static char *ccre; /* pointer to current position in converted exp*/
+static char *ure; /* pointer current position in unconverted exp */
+
+char *
+convexp(re)
+ char *re; /* unconverted irregular expression */
+{
+ register char *cre; /* pointer to converted regular expression */
+
+ /* allocate room for the converted expression */
+ if (re == NIL)
+ return (NIL);
+ if (*re == '\0')
+ return (NIL);
+ cre = malloc (4 * strlen(re) + 3);
+ ccre = cre;
+ ure = re;
+
+ /* start the conversion with a \a */
+ *cre = META | OPT;
+ MSYM(cre) = 'a';
+ ccre = MNEXT(cre);
+
+ /* start the conversion (its recursive) */
+ expconv ();
+ *ccre = 0;
+ return (cre);
+}
+
+static void
+expconv()
+{
+ register char *cs; /* pointer to current symbol in converted exp */
+ register char c; /* character being processed */
+ register char *acs; /* pinter to last alternate */
+ register int temp;
+
+ /* let the conversion begin */
+ acs = NIL;
+ cs = NIL;
+ while (*ure != NIL) {
+ switch (c = *ure++) {
+
+ case '\\':
+ switch (c = *ure++) {
+
+ /* escaped characters are just characters */
+ default:
+ if (cs == NIL || (*cs & STR) == 0) {
+ cs = ccre;
+ *cs = STR;
+ SCNT(cs) = 1;
+ ccre += 2;
+ } else
+ SCNT(cs)++;
+ *ccre++ = c;
+ break;
+
+ /* normal(?) metacharacters */
+ case 'a':
+ case 'd':
+ case 'e':
+ case 'p':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ break;
+ }
+ break;
+
+ /* just put the symbol in */
+ case '^':
+ case '$':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ break;
+
+ /* mark the last match sequence as optional */
+ case '?':
+ if (cs)
+ *cs = *cs | OPT;
+ break;
+
+ /* recurse and define a subexpression */
+ case '(':
+ if (acs != NIL && acs != cs) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = OPER;
+ OSYM(cs) = '(';
+ ccre = ONEXT(cs);
+ expconv ();
+ OCNT(cs) = ccre - cs; /* offset to next symbol */
+ break;
+
+ /* reurn from a recursion */
+ case ')':
+ if (acs != NIL) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ cs = ccre;
+ *cs = META;
+ MSYM(cs) = c;
+ ccre = MNEXT(cs);
+ return;
+
+ /* mark the last match sequence as having an alternate */
+ /* the third byte will contain an offset to jump over the */
+ /* alternate match in case the first did not fail */
+ case '|':
+ if (acs != NIL && acs != cs)
+ OCNT(ccre) = ccre - acs; /* make a back pointer */
+ else
+ OCNT(ccre) = 0;
+ *cs |= ALT;
+ cs = ccre;
+ *cs = OPER;
+ OSYM(cs) = '|';
+ ccre = ONEXT(cs);
+ acs = cs; /* remember that the pointer is to be filles */
+ break;
+
+ /* if its not a metasymbol just build a scharacter string */
+ default:
+ if (cs == NIL || (*cs & STR) == 0) {
+ cs = ccre;
+ *cs = STR;
+ SCNT(cs) = 1;
+ ccre = SSTR(cs);
+ } else
+ SCNT(cs)++;
+ *ccre++ = c;
+ break;
+ }
+ }
+ if (acs != NIL) {
+ do {
+ temp = OCNT(acs);
+ OCNT(acs) = ccre - acs;
+ acs -= temp;
+ } while (temp != 0);
+ acs = NIL;
+ }
+ return;
+}
+/* end of convertre */
+
+
+/*
+ * The following routine recognises an irregular expresion
+ * with the following special characters:
+ *
+ * \? - means last match was optional
+ * \a - matches any number of characters
+ * \d - matches any number of spaces and tabs
+ * \p - matches any number of alphanumeric
+ * characters. The
+ * characters matched will be copied into
+ * the area pointed to by 'name'.
+ * \| - alternation
+ * \( \) - grouping used mostly for alternation and
+ * optionality
+ *
+ * The irregular expression must be translated to internal form
+ * prior to calling this routine
+ *
+ * The value returned is the pointer to the first non \a
+ * character matched.
+ */
+
+char *
+expmatch (s, re, mstring)
+ register char *s; /* string to check for a match in */
+ register char *re; /* a converted irregular expression */
+ register char *mstring; /* where to put whatever matches a \p */
+{
+ register char *cs; /* the current symbol */
+ register char *ptr,*s1; /* temporary pointer */
+ boolean matched; /* a temporary boolean */
+
+ /* initial conditions */
+ if (re == NIL)
+ return (NIL);
+ cs = re;
+ matched = FALSE;
+
+ /* loop till expression string is exhausted (or at least pretty tired) */
+ while (*cs) {
+ switch (*cs & (OPER | STR | META)) {
+
+ /* try to match a string */
+ case STR:
+ matched = !STRNCMP (s, SSTR(cs), SCNT(cs));
+ if (matched) {
+
+ /* hoorah it matches */
+ s += SCNT(cs);
+ cs = SNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* alternation, skip to next expression */
+ cs = SNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* the match is optional */
+ cs = SNEXT(cs);
+ matched = 1; /* indicate a successful match */
+ } else {
+
+ /* no match, error return */
+ return (NIL);
+ }
+ break;
+
+ /* an operator, do something fancy */
+ case OPER:
+ switch (OSYM(cs)) {
+
+ /* this is an alternation */
+ case '|':
+ if (matched)
+
+ /* last thing in the alternation was a match, skip ahead */
+ cs = OPTR(cs);
+ else
+
+ /* no match, keep trying */
+ cs = ONEXT(cs);
+ break;
+
+ /* this is a grouping, recurse */
+ case '(':
+ ptr = expmatch (s, ONEXT(cs), mstring);
+ if (ptr != NIL) {
+
+ /* the subexpression matched */
+ matched = 1;
+ s = ptr;
+ } else if (*cs & ALT) {
+
+ /* alternation, skip to next expression */
+ matched = 0;
+ } else if (*cs & OPT) {
+
+ /* the match is optional */
+ matched = 1; /* indicate a successful match */
+ } else {
+
+ /* no match, error return */
+ return (NIL);
+ }
+ cs = OPTR(cs);
+ break;
+ }
+ break;
+
+ /* try to match a metasymbol */
+ case META:
+ switch (MSYM(cs)) {
+
+ /* try to match anything and remember what was matched */
+ case 'p':
+ /*
+ * This is really the same as trying the match the
+ * remaining parts of the expression to any subset
+ * of the string.
+ */
+ s1 = s;
+ do {
+ ptr = expmatch (s1, MNEXT(cs), mstring);
+ if (ptr != NIL && s1 != s) {
+
+ /* we have a match, remember the match */
+ strncpy (mstring, s, s1 - s);
+ mstring[s1 - s] = '\0';
+ return (ptr);
+ } else if (ptr != NIL && (*cs & OPT)) {
+
+ /* it was aoptional so no match is ok */
+ return (ptr);
+ } else if (ptr != NIL) {
+
+ /* not optional and we still matched */
+ return (NIL);
+ }
+ if (!isalnum(*s1) && *s1 != '_')
+ return (NIL);
+ if (*s1 == '\\')
+ _escaped = _escaped ? FALSE : TRUE;
+ else
+ _escaped = FALSE;
+ } while (*s1++);
+ return (NIL);
+
+ /* try to match anything */
+ case 'a':
+ /*
+ * This is really the same as trying the match the
+ * remaining parts of the expression to any subset
+ * of the string.
+ */
+ s1 = s;
+ do {
+ ptr = expmatch (s1, MNEXT(cs), mstring);
+ if (ptr != NIL && s1 != s) {
+
+ /* we have a match */
+ return (ptr);
+ } else if (ptr != NIL && (*cs & OPT)) {
+
+ /* it was aoptional so no match is ok */
+ return (ptr);
+ } else if (ptr != NIL) {
+
+ /* not optional and we still matched */
+ return (NIL);
+ }
+ if (*s1 == '\\')
+ _escaped = _escaped ? FALSE : TRUE;
+ else
+ _escaped = FALSE;
+ } while (*s1++);
+ return (NIL);
+
+ /* fail if we are currently _escaped */
+ case 'e':
+ if (_escaped)
+ return(NIL);
+ cs = MNEXT(cs);
+ break;
+
+ /* match any number of tabs and spaces */
+ case 'd':
+ ptr = s;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (s != ptr || s == _start) {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*s == '\n' || *s == '\0') {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* check for end of line */
+ case '$':
+ if (*s == '\0' || *s == '\n') {
+
+ /* match, be happy */
+ s++;
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* check for start of line */
+ case '^':
+ if (s == _start) {
+
+ /* match, be happy */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else if (*cs & ALT) {
+
+ /* try the next part */
+ matched = 0;
+ cs = MNEXT(cs);
+ } else if (*cs & OPT) {
+
+ /* doesn't matter */
+ matched = 1;
+ cs = MNEXT(cs);
+ } else
+
+ /* no match, error return */
+ return (NIL);
+ break;
+
+ /* end of a subexpression, return success */
+ case ')':
+ return (s);
+ }
+ break;
+ }
+ }
+ return (s);
+}
diff --git a/usr.bin/vgrind/tmac.vgrind b/usr.bin/vgrind/tmac.vgrind
new file mode 100644
index 0000000..d9d8af2
--- /dev/null
+++ b/usr.bin/vgrind/tmac.vgrind
@@ -0,0 +1,68 @@
+'ss 23
+'ds _ \d\(mi\u
+'ps 9p
+'vs 10p
+'ds - \(mi
+'ds / \\h'\\w' 'u-\\w'/'u'/
+'ds /* \\h'\\w' 'u-\\w'/'u'/*
+'bd B 3
+'bd S B 3
+'nr cm 0
+'nf
+'de vH
+'ev 2
+'if t 'if !\nv 'tl '\-\-''\-\-'
+'ft 1
+'sp .35i
+'tl '\s14\f3\\*(=F\fP\s0'\\*(=H'\f3\s14\\*(=F\fP\s0'
+'sp .25i
+'ft 1
+\f2\s12\h'\\n(.lu-\w'\\*(=f'u'\\*(=f\fP\s0\h'|0u'
+.sp .05i
+'ev
+'ds =G \\*(=F
+..
+'de vF
+'ev 2
+'sp .35i
+'ie o 'tl '\f2\\*(=M''Page % of \\*(=G\fP'
+'el 'tl '\f2Page % of \\*(=G''\\*(=M\fP'
+'bp
+'ev
+'ft 1
+'if \\n(cm=1 'ft 2
+..
+'de ()
+'pn 1
+..
+'de +C
+'nr cm 1
+'ft 2
+'ds +K
+'ds -K
+..
+'de -C
+'nr cm 0
+'ft 1
+'ds +K \f3
+'ds -K \fP
+..
+'+C
+'-C
+'am +C
+'ne 3
+..
+'de FN
+\f2\s14\h'\\n(.lu-\w'\\$1'u'\\$1\fP\s0\h'|0u'\c
+.if \\nx .tm \\$1 \\*(=F \\n%
+'ds =f \&...\\$1
+..
+'de FC
+.if \\nx .tm \\$1 \\*(=F \\n%
+'ds =f \&...\\$1
+..
+'de -F
+'rm =f
+..
+'ft 1
+'lg 0
diff --git a/usr.bin/vgrind/vfontedpr.c b/usr.bin/vgrind/vfontedpr.c
new file mode 100644
index 0000000..523e415
--- /dev/null
+++ b/usr.bin/vgrind/vfontedpr.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)vfontedpr.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "pathnames.h"
+#include "extern.h"
+
+#define FALSE 0
+#define TRUE !(FALSE)
+#define NIL 0
+#define STANDARD 0
+#define ALTERNATE 1
+
+/*
+ * Vfontedpr.
+ *
+ * Dave Presotto 1/12/81 (adapted from an earlier version by Bill Joy)
+ *
+ */
+
+#define STRLEN 10 /* length of strings introducing things */
+#define PNAMELEN 40 /* length of a function/procedure name */
+#define PSMAX 20 /* size of procedure name stacking */
+
+static int iskw __P((char *));
+static boolean isproc __P((char *));
+static void putKcp __P((char *, char *, boolean));
+static void putScp __P((char *));
+static void putcp __P((int));
+static int tabs __P((char *, char *));
+static int width __P((char *, char *));
+
+/*
+ * The state variables
+ */
+
+static boolean filter = FALSE; /* act as a filter (like eqn) */
+static boolean inchr; /* in a string constant */
+static boolean incomm; /* in a comment of the primary type */
+static boolean idx = FALSE; /* form an index */
+static boolean instr; /* in a string constant */
+static boolean nokeyw = FALSE; /* no keywords being flagged */
+static boolean pass = FALSE; /*
+ * when acting as a filter, pass indicates
+ * whether we are currently processing
+ * input.
+ */
+
+static int blklevel; /* current nesting level */
+static int comtype; /* type of comment */
+static char *defsfile[2] = { _PATH_VGRINDEFS, 0 };
+ /* name of language definitions file */
+static int margin;
+static int plstack[PSMAX]; /* the procedure nesting level stack */
+static char pname[BUFSIZ+1];
+static boolean prccont; /* continue last procedure */
+static int psptr; /* the stack index of the current procedure */
+static char pstack[PSMAX][PNAMELEN+1]; /* the procedure name stack */
+
+/*
+ * The language specific globals
+ */
+
+char *l_acmbeg; /* string introducing a comment */
+char *l_acmend; /* string ending a comment */
+char *l_blkbeg; /* string begining of a block */
+char *l_blkend; /* string ending a block */
+char *l_chrbeg; /* delimiter for character constant */
+char *l_chrend; /* delimiter for character constant */
+char *l_combeg; /* string introducing a comment */
+char *l_comend; /* string ending a comment */
+char l_escape; /* character used to escape characters */
+char *l_keywds[BUFSIZ/2]; /* keyword table address */
+char *l_prcbeg; /* regular expr for procedure begin */
+char *l_strbeg; /* delimiter for string constant */
+char *l_strend; /* delimiter for string constant */
+boolean l_toplex; /* procedures only defined at top lex level */
+char *language = "c"; /* the language indicator */
+
+#define ps(x) printf("%s", x)
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *fname = "";
+ struct stat stbuf;
+ char buf[BUFSIZ];
+ char *defs;
+ int needbp = 0;
+
+ argc--, argv++;
+ do {
+ char *cp;
+ int i;
+
+ if (argc > 0) {
+ if (!strcmp(argv[0], "-h")) {
+ if (argc == 1) {
+ printf("'ds =H\n");
+ argc = 0;
+ goto rest;
+ }
+ printf("'ds =H %s\n", argv[1]);
+ argc--, argv++;
+ argc--, argv++;
+ if (argc > 0)
+ continue;
+ goto rest;
+ }
+
+ /* act as a filter like eqn */
+ if (!strcmp(argv[0], "-f")) {
+ filter++;
+ argv[0] = argv[argc-1];
+ argv[argc-1] = "-";
+ continue;
+ }
+
+ /* take input from the standard place */
+ if (!strcmp(argv[0], "-")) {
+ argc = 0;
+ goto rest;
+ }
+
+ /* build an index */
+ if (!strcmp(argv[0], "-x")) {
+ idx++;
+ argv[0] = "-n";
+ }
+
+ /* indicate no keywords */
+ if (!strcmp(argv[0], "-n")) {
+ nokeyw++;
+ argc--, argv++;
+ continue;
+ }
+
+ /* specify the font size */
+ if (!strncmp(argv[0], "-s", 2)) {
+ i = 0;
+ cp = argv[0] + 2;
+ while (*cp)
+ i = i * 10 + (*cp++ - '0');
+ printf("'ps %d\n'vs %d\n", i, i+1);
+ argc--, argv++;
+ continue;
+ }
+
+ /* specify the language */
+ if (!strncmp(argv[0], "-l", 2)) {
+ language = argv[0]+2;
+ argc--, argv++;
+ continue;
+ }
+
+ /* specify the language description file */
+ if (!strncmp(argv[0], "-d", 2)) {
+ defsfile[0] = argv[1];
+ argc--, argv++;
+ argc--, argv++;
+ continue;
+ }
+
+ /* open the file for input */
+ if (freopen(argv[0], "r", stdin) == NULL) {
+ perror(argv[0]);
+ exit(1);
+ }
+ if (idx)
+ printf("'ta 4i 4.25i 5.5iR\n'in .5i\n");
+ fname = argv[0];
+ argc--, argv++;
+ }
+ rest:
+
+ /*
+ * get the language definition from the defs file
+ */
+ i = cgetent(&defs, defsfile, language);
+ if (i == -1) {
+ fprintf (stderr, "no entry for language %s\n", language);
+ exit (0);
+ } else if (i == -2) { fprintf(stderr,
+ "cannot find vgrindefs file %s\n", defsfile[0]);
+ exit (0);
+ } else if (i == -3) { fprintf(stderr,
+ "potential reference loop detected in vgrindefs file %s\n",
+ defsfile[0]);
+ exit(0);
+ }
+ if (cgetustr(defs, "kw", &cp) == -1)
+ nokeyw = TRUE;
+ else {
+ char **cpp;
+
+ cpp = l_keywds;
+ while (*cp) {
+ while (*cp == ' ' || *cp =='\t')
+ *cp++ = NULL;
+ if (*cp)
+ *cpp++ = cp;
+ while (*cp != ' ' && *cp != '\t' && *cp)
+ cp++;
+ }
+ *cpp = NIL;
+ }
+ cgetustr(defs, "pb", &cp);
+ l_prcbeg = convexp(cp);
+ cgetustr(defs, "cb", &cp);
+ l_combeg = convexp(cp);
+ cgetustr(defs, "ce", &cp);
+ l_comend = convexp(cp);
+ cgetustr(defs, "ab", &cp);
+ l_acmbeg = convexp(cp);
+ cgetustr(defs, "ae", &cp);
+ l_acmend = convexp(cp);
+ cgetustr(defs, "sb", &cp);
+ l_strbeg = convexp(cp);
+ cgetustr(defs, "se", &cp);
+ l_strend = convexp(cp);
+ cgetustr(defs, "bb", &cp);
+ l_blkbeg = convexp(cp);
+ cgetustr(defs, "be", &cp);
+ l_blkend = convexp(cp);
+ cgetustr(defs, "lb", &cp);
+ l_chrbeg = convexp(cp);
+ cgetustr(defs, "le", &cp);
+ l_chrend = convexp(cp);
+ l_escape = '\\';
+ l_onecase = (cgetcap(defs, "oc", ':') != NULL);
+ l_toplex = (cgetcap(defs, "tl", ':') != NULL);
+
+ /* initialize the program */
+
+ incomm = FALSE;
+ instr = FALSE;
+ inchr = FALSE;
+ _escaped = FALSE;
+ blklevel = 0;
+ for (psptr=0; psptr<PSMAX; psptr++) {
+ pstack[psptr][0] = NULL;
+ plstack[psptr] = 0;
+ }
+ psptr = -1;
+ ps("'-F\n");
+ if (!filter) {
+ printf(".ds =F %s\n", fname);
+ ps("'wh 0 vH\n");
+ ps("'wh -1i vF\n");
+ }
+ if (needbp) {
+ needbp = 0;
+ printf(".()\n");
+ printf(".bp\n");
+ }
+ if (!filter) {
+ fstat(fileno(stdin), &stbuf);
+ cp = ctime(&stbuf.st_mtime);
+ cp[16] = '\0';
+ cp[24] = '\0';
+ printf(".ds =M %s %s\n", cp+4, cp+20);
+ }
+
+ /*
+ * MAIN LOOP!!!
+ */
+ while (fgets(buf, sizeof buf, stdin) != NULL) {
+ if (buf[0] == '\f') {
+ printf(".bp\n");
+ }
+ if (buf[0] == '.') {
+ printf("%s", buf);
+ if (!strncmp (buf+1, "vS", 2))
+ pass = TRUE;
+ if (!strncmp (buf+1, "vE", 2))
+ pass = FALSE;
+ continue;
+ }
+ prccont = FALSE;
+ if (!filter || pass)
+ putScp(buf);
+ else
+ printf("%s", buf);
+ if (prccont && (psptr >= 0)) {
+ ps("'FC ");
+ ps(pstack[psptr]);
+ ps("\n");
+ }
+#ifdef DEBUG
+ printf ("com %o str %o chr %o ptr %d\n", incomm, instr, inchr, psptr);
+#endif
+ margin = 0;
+ }
+ needbp = 1;
+ } while (argc > 0);
+ exit(0);
+}
+
+#define isidchr(c) (isalnum(c) || (c) == '_')
+
+static void
+putScp(os)
+ char *os;
+{
+ register char *s = os; /* pointer to unmatched string */
+ char dummy[BUFSIZ]; /* dummy to be used by expmatch */
+ char *comptr; /* end of a comment delimiter */
+ char *acmptr; /* end of a comment delimiter */
+ char *strptr; /* end of a string delimiter */
+ char *chrptr; /* end of a character const delimiter */
+ char *blksptr; /* end of a lexical block start */
+ char *blkeptr; /* end of a lexical block end */
+
+ _start = os; /* remember the start for expmatch */
+ _escaped = FALSE;
+ if (nokeyw || incomm || instr)
+ goto skip;
+ if (isproc(s)) {
+ ps("'FN ");
+ ps(pname);
+ ps("\n");
+ if (psptr < PSMAX) {
+ ++psptr;
+ strncpy (pstack[psptr], pname, PNAMELEN);
+ pstack[psptr][PNAMELEN] = NULL;
+ plstack[psptr] = blklevel;
+ }
+ }
+skip:
+ do {
+ /* check for string, comment, blockstart, etc */
+ if (!incomm && !instr && !inchr) {
+
+ blkeptr = expmatch (s, l_blkend, dummy);
+ blksptr = expmatch (s, l_blkbeg, dummy);
+ comptr = expmatch (s, l_combeg, dummy);
+ acmptr = expmatch (s, l_acmbeg, dummy);
+ strptr = expmatch (s, l_strbeg, dummy);
+ chrptr = expmatch (s, l_chrbeg, dummy);
+
+ /* start of a comment? */
+ if (comptr != NIL)
+ if ((comptr < strptr || strptr == NIL)
+ && (comptr < acmptr || acmptr == NIL)
+ && (comptr < chrptr || chrptr == NIL)
+ && (comptr < blksptr || blksptr == NIL)
+ && (comptr < blkeptr || blkeptr == NIL)) {
+ putKcp (s, comptr-1, FALSE);
+ s = comptr;
+ incomm = TRUE;
+ comtype = STANDARD;
+ if (s != os)
+ ps ("\\c");
+ ps ("\\c\n'+C\n");
+ continue;
+ }
+
+ /* start of a comment? */
+ if (acmptr != NIL)
+ if ((acmptr < strptr || strptr == NIL)
+ && (acmptr < chrptr || chrptr == NIL)
+ && (acmptr < blksptr || blksptr == NIL)
+ && (acmptr < blkeptr || blkeptr == NIL)) {
+ putKcp (s, acmptr-1, FALSE);
+ s = acmptr;
+ incomm = TRUE;
+ comtype = ALTERNATE;
+ if (s != os)
+ ps ("\\c");
+ ps ("\\c\n'+C\n");
+ continue;
+ }
+
+ /* start of a string? */
+ if (strptr != NIL)
+ if ((strptr < chrptr || chrptr == NIL)
+ && (strptr < blksptr || blksptr == NIL)
+ && (strptr < blkeptr || blkeptr == NIL)) {
+ putKcp (s, strptr-1, FALSE);
+ s = strptr;
+ instr = TRUE;
+ continue;
+ }
+
+ /* start of a character string? */
+ if (chrptr != NIL)
+ if ((chrptr < blksptr || blksptr == NIL)
+ && (chrptr < blkeptr || blkeptr == NIL)) {
+ putKcp (s, chrptr-1, FALSE);
+ s = chrptr;
+ inchr = TRUE;
+ continue;
+ }
+
+ /* end of a lexical block */
+ if (blkeptr != NIL) {
+ if (blkeptr < blksptr || blksptr == NIL) {
+ putKcp (s, blkeptr - 1, FALSE);
+ s = blkeptr;
+ blklevel--;
+ if (psptr >= 0 && plstack[psptr] >= blklevel) {
+
+ /* end of current procedure */
+ if (s != os)
+ ps ("\\c");
+ ps ("\\c\n'-F\n");
+ blklevel = plstack[psptr];
+
+ /* see if we should print the last proc name */
+ if (--psptr >= 0)
+ prccont = TRUE;
+ else
+ psptr = -1;
+ }
+ continue;
+ }
+ }
+
+ /* start of a lexical block */
+ if (blksptr != NIL) {
+ putKcp (s, blksptr - 1, FALSE);
+ s = blksptr;
+ blklevel++;
+ continue;
+ }
+
+ /* check for end of comment */
+ } else if (incomm) {
+ comptr = expmatch (s, l_comend, dummy);
+ acmptr = expmatch (s, l_acmend, dummy);
+ if (((comtype == STANDARD) && (comptr != NIL)) ||
+ ((comtype == ALTERNATE) && (acmptr != NIL))) {
+ if (comtype == STANDARD) {
+ putKcp (s, comptr-1, TRUE);
+ s = comptr;
+ } else {
+ putKcp (s, acmptr-1, TRUE);
+ s = acmptr;
+ }
+ incomm = FALSE;
+ ps("\\c\n'-C\n");
+ continue;
+ } else {
+ putKcp (s, s + strlen(s) -1, TRUE);
+ s = s + strlen(s);
+ continue;
+ }
+
+ /* check for end of string */
+ } else if (instr) {
+ if ((strptr = expmatch (s, l_strend, dummy)) != NIL) {
+ putKcp (s, strptr-1, TRUE);
+ s = strptr;
+ instr = FALSE;
+ continue;
+ } else {
+ putKcp (s, s+strlen(s)-1, TRUE);
+ s = s + strlen(s);
+ continue;
+ }
+
+ /* check for end of character string */
+ } else if (inchr) {
+ if ((chrptr = expmatch (s, l_chrend, dummy)) != NIL) {
+ putKcp (s, chrptr-1, TRUE);
+ s = chrptr;
+ inchr = FALSE;
+ continue;
+ } else {
+ putKcp (s, s+strlen(s)-1, TRUE);
+ s = s + strlen(s);
+ continue;
+ }
+ }
+
+ /* print out the line */
+ putKcp (s, s + strlen(s) -1, FALSE);
+ s = s + strlen(s);
+ } while (*s);
+}
+
+static void
+putKcp (start, end, force)
+ char *start; /* start of string to write */
+ char *end; /* end of string to write */
+ boolean force; /* true if we should force nokeyw */
+{
+ int i;
+ int xfld = 0;
+
+ while (start <= end) {
+ if (idx) {
+ if (*start == ' ' || *start == '\t') {
+ if (xfld == 0)
+ printf("\001");
+ printf("\t");
+ xfld = 1;
+ while (*start == ' ' || *start == '\t')
+ start++;
+ continue;
+ }
+ }
+
+ /* take care of nice tab stops */
+ if (*start == '\t') {
+ while (*start == '\t')
+ start++;
+ i = tabs(_start, start) - margin / 8;
+ printf("\\h'|%dn'", i * 10 + 1 - margin % 8);
+ continue;
+ }
+
+ if (!nokeyw && !force)
+ if ((*start == '#' || isidchr(*start))
+ && (start == _start || !isidchr(start[-1]))) {
+ i = iskw(start);
+ if (i > 0) {
+ ps("\\*(+K");
+ do
+ putcp(*start++);
+ while (--i > 0);
+ ps("\\*(-K");
+ continue;
+ }
+ }
+
+ putcp (*start++);
+ }
+}
+
+
+static int
+tabs(s, os)
+ char *s, *os;
+{
+
+ return (width(s, os) / 8);
+}
+
+static int
+width(s, os)
+ register char *s, *os;
+{
+ register int i = 0;
+
+ while (s < os) {
+ if (*s == '\t') {
+ i = (i + 8) &~ 7;
+ s++;
+ continue;
+ }
+ if (*s < ' ')
+ i += 2;
+ else
+ i++;
+ s++;
+ }
+ return (i);
+}
+
+static void
+putcp(c)
+ register int c;
+{
+
+ switch(c) {
+
+ case 0:
+ break;
+
+ case '\f':
+ break;
+
+ case '{':
+ ps("\\*(+K{\\*(-K");
+ break;
+
+ case '}':
+ ps("\\*(+K}\\*(-K");
+ break;
+
+ case '\\':
+ ps("\\e");
+ break;
+
+ case '_':
+ ps("\\*_");
+ break;
+
+ case '-':
+ ps("\\*-");
+ break;
+
+ case '`':
+ ps("\\`");
+ break;
+
+ case '\'':
+ ps("\\'");
+ break;
+
+ case '.':
+ ps("\\&.");
+ break;
+
+ case '*':
+ ps("\\fI*\\fP");
+ break;
+
+ case '/':
+ ps("\\fI\\h'\\w' 'u-\\w'/'u'/\\fP");
+ break;
+
+ default:
+ if (c < 040)
+ putchar('^'), c |= '@';
+ case '\t':
+ case '\n':
+ putchar(c);
+ }
+}
+
+/*
+ * look for a process beginning on this line
+ */
+static boolean
+isproc(s)
+ char *s;
+{
+ pname[0] = NULL;
+ if (!l_toplex || blklevel == 0)
+ if (expmatch (s, l_prcbeg, pname) != NIL) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+
+/* iskw - check to see if the next word is a keyword
+ */
+
+static int
+iskw(s)
+ register char *s;
+{
+ register char **ss = l_keywds;
+ register int i = 1;
+ register char *cp = s;
+
+ while (++cp, isidchr(*cp))
+ i++;
+ while (cp = *ss++)
+ if (!STRNCMP(s,cp,i) && !isidchr(cp[i]))
+ return (i);
+ return (0);
+}
+
diff --git a/usr.bin/vgrind/vgrind.1 b/usr.bin/vgrind/vgrind.1
new file mode 100644
index 0000000..97ba7e2
--- /dev/null
+++ b/usr.bin/vgrind/vgrind.1
@@ -0,0 +1,224 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vgrind.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt VGRIND 1
+.Os BSD 4
+.Sh NAME
+.Nm vgrind
+.Nd grind nice listings of programs
+.Sh SYNOPSIS
+.Nm vgrind
+.Op Fl
+.Op Fl W
+.Op Fl d Ar file
+.Op Fl f
+.Op Fl h Ar header
+.Op Fl l Ar language
+.Op Fl n
+.Op Fl sn
+.Op Fl t
+.Op Fl x
+.Ar name Ar ...
+.Sh DESCRIPTION
+.Nm Vgrind
+formats the program sources which are arguments
+in a nice style using
+.Xr troff 1
+Comments are placed in italics, keywords in bold face,
+and the name of the current function is listed down the margin of each
+page as it is encountered.
+.Pp
+.Nm Vgrind
+runs in two basic modes, filter mode (see the
+.Fl f
+option) or regular mode. In filter mode
+.Nm vgrind
+acts as a filter in a manner similar to
+.Xr tbl 1 .
+The standard input is passed directly to the standard output except
+for lines bracketed by the
+.Em troff-like
+macros:
+.Bl -tag -width Ds
+.It \&.vS
+starts processing
+.It \&.vE
+ends processing
+.El
+.Pp
+These lines are formatted as described above. The output from this
+filter can be passed to
+.Xr troff
+for output. There need be no particular ordering with
+.Xr eqn 1
+or
+.Xr tbl 1 .
+.Pp
+In regular mode
+.Nm vgrind
+accepts input files, processes them, and passes them to
+.Xr troff 1
+for output.
+.Pp
+In both modes
+.Nm vgrind
+passes any lines beginning with a decimal point without conversion.
+.Pp
+The options are:
+.Bl -tag -width Ar
+.It Fl
+forces input to be taken from standard input (default if
+.Fl f
+is specified )
+.It Fl W
+forces output to the (wide) Versatec printer rather than the (narrow)
+Varian
+.It Fl d Ar file
+specifies an alternate language definitions
+file (default is
+.Pa /usr/share/misc/vgrindefs )
+.It Fl f
+forces filter mode
+.It Fl h Ar header
+specifies a particular header to put on every output page (default is
+the file name)
+.It Fl l
+specifies the language to use. Currently known are
+.Tn PASCAL
+.Pq Fl l Ns Ar p ,
+.Tn MODEL
+.Pq Fl l Ns Ar m ,
+C
+.Pf ( Fl l Ns Ar c
+or the default),
+.Tn CSH
+.Pq Fl l Ns Ar csh ,
+.Tn SHELL
+.Pq Fl l Ns Ar sh ,
+.Tn RATFOR
+.Pq Fl l Ns Ar r ,
+.Tn MODULA2
+.Pq Fl l Ns Ar mod2 ,
+.Tn YACC
+.Pq Fl l Ns Ar yacc ,
+.Tn LISP
+.Pq Fl l Ns Ar isp ,
+and
+.Tn ICON
+.Pq Fl l Ns Ar I .
+.It Fl n
+forces no keyword bolding
+.It Fl s
+specifies a point size to use on output (exactly the same as the argument
+of a .ps)
+.It Fl t
+similar to the same option in
+.Xr troff
+causing formatted text to go to the standard output
+.It Fl x
+outputs the index file in a ``pretty'' format.
+The index file itself is produced whenever
+.Nm vgrind
+is run with a file called
+.Pa index
+in the current directory.
+The index of function
+definitions can then be run off by giving
+.Nm vgrind
+the
+.Fl x
+option and the file
+.Pa index
+as argument.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/misc/vgrindefsxx -compact
+.It Pa index
+file where source for index is created
+.It Pa /usr/share/tmac/tmac.vgrind
+macro package
+.It Pa /usr/libexec/vfontedpr
+preprocessor
+.It Pa /usr/share/misc/vgrindefs
+language descriptions
+.El
+.Sh SEE ALSO
+.Xr getcap 3 ,
+.Xr vgrindefs 5
+.Sh BUGS
+Vfontedpr assumes that a certain programming style is followed:
+.Pp
+For
+.Tn C
+\- function names can be preceded on a line only by spaces, tabs, or an
+asterisk. The parenthesized arguments must also be on the same line.
+.Pp
+For
+.Tn PASCAL
+\- function names need to appear on the same line as the keywords
+.Em function
+or
+.Em procedure .
+.Pp
+For
+.Tn MODEL
+\- function names need to appear on the same line as the keywords
+.Em is beginproc .
+.Pp
+If these conventions are not followed, the indexing and marginal function
+name comment mechanisms will fail.
+.Pp
+More generally, arbitrary formatting styles for programs mostly look bad.
+The use of spaces to align source code fails miserably; if you plan to
+.Nm vgrind
+your program you should use tabs. This is somewhat inevitable since the
+font used by
+.Nm vgrind
+is variable width.
+.Pp
+The mechanism of
+.Xr ctags 1
+in recognizing functions should be used here.
+.Pp
+Filter mode does not work in documents using the
+.Fl me
+or
+.Fl ms
+macros.
+(So what use is it anyway?)
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/vgrind/vgrind.sh b/usr.bin/vgrind/vgrind.sh
new file mode 100644
index 0000000..fc05461
--- /dev/null
+++ b/usr.bin/vgrind/vgrind.sh
@@ -0,0 +1,143 @@
+#!/bin/csh -f
+#
+# Copyright (c) 1980, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vgrind.sh 8.1 (Berkeley) 6/6/93
+#
+
+set voptions=
+set options=
+set files=
+set f=''
+set head=""
+set vf=/usr/libexec/vfontedpr
+set tm=/usr/share/tmac
+top:
+if ($#argv > 0) then
+ switch ($1:q)
+
+ case -f:
+ set f='filter'
+ set options = "$options $1:q"
+ shift
+ goto top
+
+ case -t:
+ set voptions = "$voptions -t"
+ shift
+ goto top
+
+ case -o*:
+ set voptions="$voptions $1:q"
+ shift
+ goto top
+
+ case -W:
+ set voptions = "$voptions -W"
+ shift
+ goto top
+
+ case -d:
+ if ($#argv < 2) then
+ echo "vgrind: $1:q option must have argument"
+ goto done
+ else
+ set options = ($options $1:q $2)
+ shift
+ shift
+ goto top
+ endif
+
+ case -h:
+ if ($#argv < 2) then
+ echo "vgrind: $1:q option must have argument"
+ goto done
+ else
+ set head="$2"
+ shift
+ shift
+ goto top
+ endif
+
+ case -*:
+ set options = "$options $1:q"
+ shift
+ goto top
+
+ default:
+ set files = "$files $1:q"
+ shift
+ goto top
+ endsw
+endif
+if (-r index) then
+ echo > nindex
+ foreach i ($files)
+ # make up a sed delete command for filenames
+ # being careful about slashes.
+ echo "? $i ?d" | sed -e "s:/:\\/:g" -e "s:?:/:g" >> nindex
+ end
+ sed -f nindex index >xindex
+ if ($f == 'filter') then
+ if ("$head" != "") then
+ $vf $options -h "$head" $files | cat $tm/tmac.vgrind -
+ else
+ $vf $options $files | cat $tm/tmac.vgrind -
+ endif
+ else
+ if ("$head" != "") then
+ $vf $options -h "$head" $files | \
+ sh -c "psroff -rx1 $voptions -i -mvgrind 2>> xindex"
+ else
+ $vf $options $files | \
+ sh -c "psroff -rx1 $voptions -i -mvgrind 2>> xindex"
+ endif
+ endif
+ sort -df +0 -2 xindex >index
+ rm nindex xindex
+else
+ if ($f == 'filter') then
+ if ("$head" != "") then
+ $vf $options -h "$head" $files | cat $tm/tmac.vgrind -
+ else
+ $vf $options $files | cat $tm/tmac.vgrind -
+ endif
+ else
+ if ("$head" != "") then
+ $vf $options -h "$head" $files | psroff -i $voptions -mvgrind
+ else
+ $vf $options $files | psroff -i $voptions -mvgrind
+ endif
+ endif
+endif
+
+done:
diff --git a/usr.bin/vgrind/vgrindefs.5 b/usr.bin/vgrind/vgrindefs.5
new file mode 100644
index 0000000..4ac7522
--- /dev/null
+++ b/usr.bin/vgrind/vgrindefs.5
@@ -0,0 +1,158 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vgrindefs.5 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt VGRINDEFS 5
+.Os BSD 4.2
+.Sh NAME
+.Nm vgrindefs
+.Nd language definition data base for
+.Xr vgrind 1
+.Sh SYNOPSIS
+.Nm vgrindefs
+.Sh DESCRIPTION
+The
+.Nm vgrindefs
+file
+contains all language definitions for
+.Xr vgrind 1 .
+The data base is
+very similar to
+.Xr termcap 5 .
+.Sh FIELDS
+The following table names and describes each field.
+.Pp
+.Bl -column Namexxx Tpexxx
+.Sy Name Type Description
+.It "pb str regular expression for start of a procedure"
+.It "bb str regular expression for start of a lexical block"
+.It "be str regular expression for the end of a lexical block"
+.It "cb str regular expression for the start of a comment"
+.It "ce str regular expression for the end of a comment"
+.It "sb str regular expression for the start of a string"
+.It "se str regular expression for the end of a string"
+.It "lb str regular expression for the start of a character constant"
+.It "le str regular expression for the end of a character constant"
+.It "tl bool present means procedures are only defined at the top lexical level"
+.It "oc bool present means upper and lower case are equivalent"
+.It "kw str a list of keywords separated by spaces"
+.El
+.Pp
+.Sh EXAMPLES
+The following entry, which describes the C language, is
+typical of a language entry.
+.Bd -literal
+C|c:\
+:pb=^\ed?*?\ed?\ep\ed?\e(\ea?\e):bb={:be=}:cb=/*:ce=*/:sb=":se=\ee":\e
+:lb=':le=\ee':tl:\e
+:kw=asm auto break case char continue default do double else enum\e
+extern float for fortran goto if int long register return short\e
+sizeof static struct switch typedef union unsigned while #define\e
+#else #endif #if #ifdef #ifndef #include #undef # define else endif\e
+if ifdef ifndef include undef:
+.Ed
+.Pp
+Note that the first field is just the language name (and any variants
+of it). Thus the C language could be specified to
+.Xr vgrind 1
+as "c" or "C".
+.Pp
+Entries may continue onto multiple lines by giving a \e as the last
+character of a line.
+Capabilities in
+.Nm vgrindefs
+are of two types:
+Boolean capabilities which indicate that the language has
+some particular feature
+and string
+capabilities which give a regular expression or
+keyword list.
+.Sh REGULAR EXPRESSIONS
+.Nm Vgrindefs
+uses regular expression which are very similar to those of
+.Xr ex 1
+and
+.Xr lex 1 .
+The characters `^', `$', `:' and `\e'
+are reserved characters and must be
+"quoted" with a preceding
+.Ql \e
+if they
+are to be included as normal characters.
+The metasymbols and their meanings are:
+.Bl -tag -width indent
+.It $
+the end of a line
+.It \&^
+the beginning of a line
+.It \ed
+a delimiter (space, tab, newline, start of line)
+.It \ea
+matches any string of symbols (like .* in lex)
+.It \ep
+matches any alphanumeric name. In a procedure definition (pb) the string
+that matches this symbol is used as the procedure name.
+.It ()
+grouping
+.It \&|
+alternation
+.It ?
+last item is optional
+.It \ee
+preceding any string means that the string will not match an
+input string if the input string is preceded by an escape character (\e).
+This is typically used for languages (like C) which can include the
+string delimiter in a string by escaping it.
+.El
+.Pp
+Unlike other regular expressions in the system, these match words
+and not characters. Hence something like "(tramp|steamer)flies?"
+would match "tramp", "steamer", "trampflies", or "steamerflies".
+.Sh KEYWORD LIST
+The keyword list is just a list of keywords in the language separated
+by spaces. If the "oc" boolean is specified, indicating that upper
+and lower case are equivalent, then all the keywords should be
+specified in lower case.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/vgrindefs -compact
+.It Pa /usr/share/misc/vgrindefs
+File containing terminal descriptions.
+.El
+.Sh SEE ALSO
+.Xr vgrind 1 ,
+.Xr troff 1
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
diff --git a/usr.bin/vgrind/vgrindefs.c b/usr.bin/vgrind/vgrindefs.c
new file mode 100644
index 0000000..23f06bd
--- /dev/null
+++ b/usr.bin/vgrind/vgrindefs.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vgrindefs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#define BUFSIZ 1024
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#include <ctype.h>
+/*
+ * grindcap - routines for dealing with the language definitions data base
+ * (code stolen almost totally from termcap)
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static char *filename;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+char *tskip();
+char *tgetstr();
+char *tdecode();
+char *getenv();
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+tgetent(bp, name, file)
+ char *bp, *name, *file;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ char *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ filename = file;
+ tf = open(filename, 0);
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\'){
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+BUFSIZ) {
+ write(2,"Vgrind entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return(tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+tnchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad vgrind entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (tgetent(tcbuf, tcname, filename) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(2, "Vgrind entry too long\n", 23);
+ q[BUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+tnamatch(np)
+ char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return(0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ register char *bp;
+{
+
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+tgetnum(id)
+ char *id;
+{
+ register int i, base;
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+tgetflag(id)
+ char *id;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) {
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return(0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ register char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1])
+ continue;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ register char *str;
+ char **area;
+{
+ register char *cp;
+ register int c;
+ int i;
+
+ cp = *area;
+ while (c = *str++) {
+ if (c == ':' && *(cp-1) != '\\')
+ break;
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.bin/vgrind/vgrindefs.src b/usr.bin/vgrind/vgrindefs.src
new file mode 100644
index 0000000..91ef58f
--- /dev/null
+++ b/usr.bin/vgrind/vgrindefs.src
@@ -0,0 +1,146 @@
+# Copyright (c) 1987, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)vgrindefs.src 8.1 (Berkeley) 6/6/93
+#
+
+C|c:\
+ :pb=^\d?*?\d?\p\d?\(\a?\)(\d|{):bb={:be=}:cb=/*:ce=*/:sb=":se=\e":lb=':\
+ :le=\e':tl:\
+ :kw=asm auto break case char continue default do double else enum\
+ extern float for fortran goto if int long register return short\
+ sizeof static struct switch typedef union unsigned void while #define\
+ #else #endif #if #ifdef #ifndef #include #undef # define else endif\
+ if ifdef ifndef include undef:
+model|mod|m:\
+ :pb=^\d(space\d\p\drep)|(\p\dis|inline|public\dbeginproc):\
+ :bb=\dbeginproc|space|case\d:be=\dendproc|end\d|;:\
+ :cb=\$:ce=\$|$:sb=":se=":lb=':le=\a|$:\
+ :kw=abs and array beginproc boolean by case cdnl char copied dispose\
+ div do dynamic else elsif end endproc entry external FALSE false\
+ fi file for formal fortran global if iff ift\
+ in integer include inline is lbnd\
+ max min mod new NIL nil noresult not notin od of or procedure public\
+ read readln readonly record recursive rem rep repeat res\
+ result return set\
+ space string subscript such then TRUE true type ubnd union until\
+ varies while width:
+pascal|pasc|p:\
+ :pb=(^\d?procedure|function|program\d\p\d|\(|;|\:)|(=\d?record\d):\
+ :bb=\dcase|begin\d:be=\dend|forward\d|;:\
+ :cb={:ce=}:\
+ :ab=\(*:ae=*\):\
+ :sb=':se=':\
+ :kw=and array assert begin case const div do downto else end file for\
+ forward function goto if in label mod nil not of or packed procedure\
+ program record repeat set then to type until var while with oct hex\
+ external:
+ISP|isp|i:\
+ :cb=!:ce=!|$:oc:\
+ :kw=and begin decode define end eql eqv geq gtr if leave leq lss mod\
+ neq next not or otherwise repeat restart resume sr0 sr1 srd srr sl0 sl1\
+ sld slr tst xor:
+SH|sh:\
+ :bb={:be=}:cb=#:ce=$:sb=":se=\e":lb=':\
+ :le=\e':tl:\
+ :kw=break case cd continue do done \
+ elif else esac eval exec exit export \
+ fi for if in then while until \
+ read readonly set shift test trap umask wait:
+CSH|csh:\
+ :bb={:be=}:cb=#:ce=$:sb=":se=\e":lb=':\
+ :le=\e':tl:\
+ :kw=alias alloc break breaksw case cd chdir continue default\
+ echo else end endif endsw exec exit foreach \
+ glob goto history if logout nice nohup onintr repeat set\
+ setenv shift source switch then time \
+ while umask unalias unset wait while @ env \
+ argv child home ignoreeof noclobber noglob \
+ nomatch path prompt shell status verbose :
+ldl|LDL:\
+ :pb=^\p\::bb=\::be=;:cb=/*:ce=*/:sb=":se=\e":\
+ :kw=constant functions grammar reswords tokens add1 addste\
+ car cdr check colno cond cons copy defun divide empty enter\
+ eq equal findattr firstchild ge getattr getfield gt hash label\
+ lambda lastchild le leftsibling lookone lookup lt minus name ne\
+ newnode nextcom nil null parent plus precnl prevcom prog progn\
+ quote reglob return rightsibling self set setattr setfield setq\
+ stjoin sub1 t times tnull tokno ttype:
+Icon|icon|I:\
+ :pb=^\d?procedure\d\p\d?\(\a?\):\
+ :bb=(^\d?procedure\d\p\d?\(\a?\))|{:be=}|(^\d?end\d?$):\
+ :cb=#:ce=$:\
+ :sb=":se=\e":lb=':le=\e':tl:\
+ :kw=break by case create default do dynamic else end every external\
+ fail global if initial local next not of procedure record\
+ repeat return static suspend then to until using while\
+ &ascii &clock &cset &date &dateline &errout &fail &host &input\
+ &lcase &level &main &null &output &pos &random &source &subject\
+ &time &trace &ucase &version:
+ratfor|rat|r:\
+ :pb=(subroutine|function)\d\p\d?\(\a?\):\
+ :bb=(subroutine|function)\d\p\d?\(\a?\):be=^\d?end:\
+ :cb=#:ce=$:\
+ :sb=":se=\e":lb=':le=\e':oc:\
+ :kw=DRETURN DRIVER arith break case character default define do\
+ else elsedef enddef filedes for function goto if ifdef ifelse\
+ ifnotdef include incr integer linepointer next opeq pointer\
+ real repeat return select string subroutine substr until:
+modula2|mod2|m2:\
+ :pb=(^\d?(procedure|function|module)\d\p\d|\(|;|\:):\
+ :bb=\d(begin|case|for|if|loop|record|repeat|while|with)\d:\
+ :be=\dend|;:\
+ :cb={:ce=}:\
+ :ab=\(*:ae=*\):\
+ :sb=":se=":\
+ :oc:\
+ :kw=and array begin by case const\
+ definition div do else elsif end exit export\
+ for from if implementation import in\
+ loop mod module not of or pointer procedure qualified\
+ record repeat return set then to type\
+ until var while with:
+yacc|Yacc|y:\
+ :cb=/*:ce=*/:sb=":se=\e":lb=':le=\e':tl:\
+ :kw=%{ %} %% %union %token %type\
+ #else #endif #if #ifdef #ifndef #include #undef # define else endif\
+ if ifdef ifndef include undef:
+C++|c++:\
+ :pb=^\d?*?\d?\p\d?\(\a?\)(\d|{):bb={:be=}:cb=/*:ce=*/:ab=//:\
+ :ae=$:sb=":se=\e":lb=':\
+ :le=\e':tl:\
+ :kw=asm auto break case char continue default do double else enum\
+ extern float for fortran goto if int long register return short\
+ sizeof static struct switch typedef union unsigned while void #define\
+ #else #endif #if #ifdef #ifndef #include #undef # define endif\
+ ifdef ifndef include undef defined\
+ class const delete friend inline new operator overload private\
+ protected public virtual:
diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile
new file mode 100644
index 0000000..9777077
--- /dev/null
+++ b/usr.bin/vi/Makefile
@@ -0,0 +1,3 @@
+SUBDIR= common
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/vi/README b/usr.bin/vi/README
new file mode 100644
index 0000000..0fe3a12
--- /dev/null
+++ b/usr.bin/vi/README
@@ -0,0 +1,200 @@
+# @(#)README 8.86 (Berkeley) 8/17/94
+
+This is the README for version 1.34 of nex/nvi, a freely redistributable
+replacement for the Berkeley ex and vi text editors. The compressed or
+gzip'd archives for this and future versions, can be retrieved by using
+anonymous ftp to ftp.cs.berkeley.edu, from the file ucb/4bsd/nvi.tar.Z,
+or ucb/4bsd/nvi.tar.gz.
+
+If you have any questions about nvi, or problems making it work, please
+contact me by electronic mail at one of the following addresses:
+
+ uunet!bostic
+ bostic@cs.berkeley.edu
+
+Keith Bostic
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Redistribution:
+
+This software is copyrighted by the The Regents of the University of
+California, but may be freely redistributed (or sold, or used to line
+your birdcage) under the following conditions:
+
+/*-
+ * Copyright (c) 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Credit where it's due:
+
+ This software was originally derived from software contributed
+ to the University of California, Berkeley by Steve Kirkendall,
+ the author of the vi clone elvis. Without his work, this work
+ would have been far more difficult.
+
+ POSIX 1003.2 style regular expression support is courtesy of
+ Henry Spencer, for which I am *very* grateful.
+
+ The curses library was originally done by Ken Arnold. Scrolling
+ and general reworking for 4.4BSD was done by Elan Amir.
+
+o From the original vi acknowledgements, by William Joy and Mark Horton:
+
+ Bruce Englar encouraged the early development of this display
+ editor. Peter Kessler helped bring sanity to version 2's
+ command layout. Bill Joy wrote versions 1 and 2.0 through 2.7,
+ and created the framework that users see in the present editor.
+ Mark Horton added macros and other features and made the editor
+ work on a large number of terminals and Unix systems.
+
+o And...
+ The financial support of UUNET Communications Services is gratefully
+ acknowledged.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Status:
+
+This software is in beta test, and it's pretty stable. Almost all of
+the historic functionality in ex/vi is there, the only major missing
+pieces are open mode and the lisp option. (Also, the options hardtabs,
+optimize, redraw, and slowopen are recognized, but ignored.)
+
+Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left
+in during initial development to keep things simple. Wide character
+support will be integrated at the same time that it is made fully 8-bit
+clean.
+
+There aren't a lot of new features in nex/nvi, but there are a few things
+you might like. The "Additional Features" section of the reference page
+(USD.doc/vi.ref/vi.ref.txt, USD.doc/vi.ref/vi.ref.ps) has more information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Porting information:
+
+The directory "PORT" has directories for specific OS/machine combinations,
+including V7-style Makefiles, for building nex/nvi on different machines.
+See the file PORT/README for detailed information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Debugging:
+
+Code fixes are appreciated, of course, but if you can't provide them,
+please email me as much information as you can as to how to reproduce
+the bug, and I'll try to fix it locally. Stack traces of core dumps
+are only rarely helpful -- an example file with a set of keystrokes that
+causes the problem is almost invariably necessary.
+
+Please include the following in the bug report;
+
+ o The version of nvi you're running (use :version to get it).
+ o The row/column dimensions of the screen (80 x 32).
+ o Unless you're confident that they're not part of the problem,
+ your startup files (.exrc, .nexrc) and the environment variable
+ (EXININT, NEXINIT) values. (Cutting and pasting the output
+ of ":set all" is usually sufficient.)
+
+If you're running a memory checker (e.g. Purify) on nvi, you will want
+to recompile everything with "-DPURIFY" in the CFLAGS, first. By
+default, allocated pages are not initialized by the DB code, and they
+will show up as reads of uninitialized memory in the buffer write routines.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Directory layout:
+
+nvi/USD.doc:
+ Ex/vi documentation, both historic and current.
+
+ edit/ Roff source for "Edit: A tutorial", USD:14 in the
+ 4.3BSD manuals.
+ ex/ Roff source for "Ex Reference Manual -- Version
+ 3.7", USD:16 in the 4.3BSD manuals.
+ vi/ Roff source for "An Introduction to Display
+ Editing with Vi", USD:15 in the 4.3BSD manuals.
+ Includes the "Vi Quick Reference" card.
+ vi.man/ Manual page for nex/nvi; an updated version of
+ the document distributed with 4.4BSD-Lite.
+ vi.ref/ Reference document for nex/nvi; an updated version
+ of the document distributed with 4.4BSD-Lite.
+
+nvi/common:
+ Source files for pieces of code that are shared by all the editors,
+ like searching and logging code or code translating line numbers
+ into requests to the dbopen(3) database code. It also has the
+ interface code for modifying "records" in the underlying database.
+
+nvi/docs:
+ Random nvi documentation:
+
+ README -- Nvi main README file.
+ bugs.current -- Major known bugs in the current nvi.
+ changelog -- Log of changes from version to version.
+ features -- Todo list, suggested features list.
+ internals/
+ autowrite -- Vi autowrite option discussion.
+ gdb.script -- GDB debugging scripts.
+ input -- Vi maps, executable buffers, and input discussion.
+ quoting -- Vi quoting discussion.
+ structures -- Out-of-date nvi internal structure description.
+ tutorial/ -- Historic vi tutorial(s), of unknown quality.
+
+nvi/ex:
+ The ex source code. Because vi has the colon command, lots of
+ this code is used by vi. Generally, if functionality is shared
+ by both ex and vi, it's in nvi/ex. If it's vi only, it's in
+ nvi/vi. Files are generally named by the command(s) they support,
+ but occasionally with a name that describes their functionality.
+
+nvi/install:
+ Things to install on the local system.
+
+ recover.script -- Vi recovery script.
+
+nvi/PORT:
+ Porting directories, one per OS/architecture combination. See
+ nvi/PORT/README for porting information.
+
+ curses/ -- 4.4BSD curses implementation
+ db/ -- 4.4BSD DB routines.
+ regex/ -- Henry Spencer's POSIX.2 RE support.
+
+nvi/sex:
+ The screen support for the ex editor.
+
+nvi/svi:
+ The screen support for a curses based vi editor.
+
+nvi/vi:
+ The vi source code.
+
+nvi/xaw:
+ Place reserved for an X11 (Athena Widget) screen.
diff --git a/usr.bin/vi/USD.doc/edit/Makefile b/usr.bin/vi/USD.doc/edit/Makefile
new file mode 100644
index 0000000..3d30bc9
--- /dev/null
+++ b/usr.bin/vi/USD.doc/edit/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= usd/11.edit
+SRCS= edittut.ms
+MACROS= -msU
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+# index for versatec is different from the one in edit.tut
+# because the fonts are different and entries reference page
+# rather than section numbers. if you have a typesetter
+# you should just use the index in edit.tut, and ignore editvindex.
+
+editvindex:
+ ${TROFF} ${MACROS} -n22 edit.vindex
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/vi/USD.doc/edit/edit.vindex b/usr.bin/vi/USD.doc/edit/edit.vindex
new file mode 100644
index 0000000..2098f14
--- /dev/null
+++ b/usr.bin/vi/USD.doc/edit/edit.vindex
@@ -0,0 +1,115 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edit.vindex 8.1 (Berkeley) 6/8/93
+.\"
+.bd I
+.ND
+.TL
+Index
+.sp 3
+.2C
+.nf
+addressing, \fIsee\fR line numbers
+append mode, 4
+backslash (\\), 18
+buffer, 2
+command mode, 4
+context search, 8, 10, 13, 18
+control characters (``^'' notation), 8
+control-d, 6
+current filename, 19, 20
+current line (.), 9, 15
+diagnostic messages, 4
+disk, 2
+documentation, 21
+edit (to begin editing session), 3, 7
+editing commands:
+.in +2
+append (a), 4, 7
+change (c), 16
+copy (co), 13
+delete (d), 13-14
+edit (e), 12
+file (f), 19
+global (g), 18-19
+move (m), 12-13
+number (nu), 9
+preserve (pre), 20-21
+print (p), 8
+quit (q), 5, 11
+quit! (q!), 11
+read (r), 20
+recover (rec), 20
+substitute (s), 9-10, 17, 18
+undo (u), 14, 17
+write (w), 5-6, 11, 19-20
+z, 11
+.sp 10i
+! (shell escape), 19
+$= , 15
++, 15
+\-, 15
+//, 8, 18
+??, 18
+\&\fB.\fR, 9, 15
+\&\fB.\fR=, 9, 15
+.in -2
+erasing
+.ti +2
+characters (#), 8
+.ti +2
+lines (@), 8
+ex (text editor), 21
+\fIEx Reference Manual\fR, 21
+file, 1
+file recovery, 20
+filename, 2
+Interrupt (message), 7
+line numbers, \fIsee also\fR current line
+.ti +2
+dollar sign ($), 8, 12-13, 15
+.ti +2
+dot (.), 9, 15
+.ti +2
+relative (+ and \-), 15, 16
+logging out, 6
+login procedure, 2
+``magic'' characters, 21
+non-printing characters, 8
+``not found'' (message), 3
+program, 1
+recovery \fIsee\fR file recovery
+shell, 18
+shell escape (!), 19
+special characters (^, $, \e), 18
+text input mode, 4
+UNIX, 1
diff --git a/usr.bin/vi/USD.doc/edit/edittut.ms b/usr.bin/vi/USD.doc/edit/edittut.ms
new file mode 100644
index 0000000..5f4c28c
--- /dev/null
+++ b/usr.bin/vi/USD.doc/edit/edittut.ms
@@ -0,0 +1,2322 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edittut.ms 8.1 (Berkeley) 6/8/93
+.\"
+.EH 'USD:11-%''Edit: A Tutorial'
+.OH 'Edit: A Tutorial''USD:11-%'
+.LP
+.ds u \s-2UNIX\s0
+.ll 5i
+.nr LL 5i
+.ND
+.sp 4
+.ce
+\f3\s+2Edit: A Tutorial\s0\f1
+.sp
+.ce 3
+.I
+Ricki Blau
+.sp
+James Joyce
+.R
+.sp
+.ce 3
+Computing Services
+University of California
+Berkeley, California 94720
+.sp 3
+.ce
+.I
+ABSTRACT
+.R
+.sp
+.LP
+This narrative introduction to the use of the text editor
+.I edit
+assumes no prior familiarity with computers or with text editing.
+Its aim is to lead the beginning \s-2UNIX\(dg\s+2 user through the
+.FS
+\(dgUNIX is a trademark of Bell Laboratories.
+.FE
+fundamental steps of writing and revising a file of text.
+Edit,
+a version of the text editor
+.I ex,
+was designed to provide an informative environment
+for new and casual users.
+.PP
+We welcome comments and suggestions about this tutorial
+and the \s-2UNIX\s+2 documentation in general.
+.sp .5v
+September 1981
+.bp
+.ll 6.5i
+.nr LL 6.5i
+.nr LT 6.5i
+.ds u \s-2UNIX\s0
+.ce
+\s+2\f3Contents\f1\s0
+.LP
+.nf
+Introduction\ \ \ 3
+.sp
+Session 1\ \ \4
+.in +.5i
+Making contact with \s-2UNIX\s+2\ \ \ 4
+Logging in\ \ \4
+Asking for \fIedit\fR\ \ \ 4
+The ``Command not found'' message\ \ \ 5
+A summary\ \ \5
+Entering text\ \ \ 5
+Messages from \fIedit\fR\ \ \ 5
+Text input mode\ \ \ 6
+Making corrections\ \ \ 6
+Writing text to disk\ \ \ 7
+Signing off\ \ \7
+.in -.5i
+.sp
+Session 2\ \ \ 8
+.in +.5i
+Adding more text to the file\ \ \ 8
+Interrupt\ \ \ 8
+Making corrections\ \ \ 8
+Listing what's in the buffer (p)\ \ \ 9
+Finding things in the buffer\ \ \ 9
+The current line\ \ \ 10
+Numbering lines (nu)\ \ \ 10
+Substitute command (s)\ \ \ 10
+Another way to list what's in the buffer (z)\ \ \ 11
+Saving the modified text\ \ \ 12
+.in -.5i
+.sp
+Session 3\ \ \ 13
+.in +.5i
+Bringing text into the buffer (e)\ \ \ 13
+Moving text in the buffer (m)\ \ \ 13
+Copying lines (copy)\ \ \ 14
+Deleting lines (d)\ \ \ 14
+A word or two of caution\ \ \ 15
+Undo (u) to the rescue\ \ \ 15
+More about the dot (.) and buffer end ($)\ \ \ 16
+Moving around in the buffer (+ and \-)\ \ \ 16
+Changing lines (c)\ \ \ 17
+.in -.5i
+.sp
+Session 4\ \ \ 18
+.in +.5i
+Making commands global (g)\ \ \ 18
+More about searching and substituting\ \ \ 19
+Special characters\ \ \ 19
+Issuing \s-2UNIX\s+2 commands from the editor\ \ \ 20
+Filenames and file manipulation\ \ \ 20
+The file (f) command\ \ \ 20
+Reading additional files (r)\ \ \ 21
+Writing parts of the buffer\ \ \ 21
+Recovering files\ \ \ 21
+Other recovery techniques\ \ \ 21
+Further reading and other information\ \ \ 22
+Using \fIex\fR\ \ \ 22
+.in -.5i
+.sp
+Index\ \ \ 23
+.bp
+.SH
+.ce
+\s+2Introduction\s0
+.PP
+Text editing using a terminal connected to a computer
+allows you to create, modify, and print text
+easily.
+A
+.I
+text editor
+.R
+is a program
+that assists you
+as you create and modify text.
+The text editor you will learn here is named
+.I edit.
+Creating text using edit is as easy as typing it
+on an electric typewriter.
+Modifying text involves telling the text editor
+what you want to add, change, or delete.
+You can review your text
+by typing a command
+to print the file contents
+as they are currently.
+Another program (which we do not discuss in this
+document), a text formatter,
+rearranges your text
+for you into ``finished form.''
+.PP
+These lessons assume no prior familiarity with computers
+or with text editing.
+They consist of a series of text editing sessions
+which lead you through the fundamental steps
+of creating and revising text.
+After scanning each lesson and before beginning the next,
+you should try the examples at a terminal to get a feeling
+for the actual process of text editing.
+If you set aside some time for experimentation,
+you will soon become familiar with using the
+computer to write and modify text.
+In addition to the actual use of the text editor,
+other features of \s-2UNIX\s0 will be very important to your work.
+You can begin to
+learn about these other features by
+reading one of the other tutorials
+that provide a general introduction to the system.
+You will be ready to proceed with this lesson as soon as
+you are familiar with (1) your terminal and its special keys,
+(2) how to login,
+(3) and the ways of correcting typing errors.
+Let's first define some terms:
+.sp .5
+.IP program 12
+A set of instructions, given to the computer,
+describing the sequence of steps the computer performs
+in order to accomplish a specific task.
+The task must be specific,
+such as balancing your checkbook
+or editing your text.
+A general task,
+such as working for world peace,
+is something we can all do,
+but not something we can currently write programs to do.
+.IP UNIX
+\s-2UNIX\s0 is a special type of program,
+called an operating system, that supervises the machinery
+and all other programs comprising the total
+computer system.
+.IP edit
+.I edit
+is the name of the \s-2UNIX\s0 text editor you will be learning to use,
+and is a program that aids you in writing or revising text.
+Edit was designed for beginning users,
+and is a simplified version of an editor named
+.I ex.
+.IP file
+Each \s-2UNIX\s0 account is allotted
+space for the permanent storage of information,
+such as programs, data or text.
+A file is a logical unit of data,
+for example, an essay, a program,
+or a chapter from a book,
+which is stored on a computer system.
+Once you create a file,
+it is kept until you instruct the system to remove it.
+You may create a file during one \s-2UNIX\s0 session,
+end the session,
+and return to use it at a later time.
+Files contain anything you choose to write and store in them.
+The sizes of files vary to suit your needs;
+one file might hold only a single number,
+yet another might contain
+a very long document or program.
+The only way to save
+information from one session to the next is to store it in a file,
+which you will learn in Session 1.
+.IP filename
+Filenames are used to distinguish one file from another,
+serving the same purpose as the labels of manila
+folders in a file cabinet.
+In order to write or access information in a file,
+you use the name of that file in a \s-2UNIX\s0 command,
+and the system will automatically locate the file.
+.IP disk
+Files are stored on an input/output device called a disk,
+which looks something like a stack of phonograph records.
+Each surface is coated with a material similar to that
+on magnetic recording tape,
+and information is recorded on it.
+.IP buffer
+A temporary work space, made available to the user
+for the duration of a session of text editing
+and used for creating and modifying
+the text file.
+We can think of the buffer as a blackboard that is
+erased after each class, where each session with the editor
+is a class.
+.bp
+.SH
+.ce 1
+\s+2Session 1\s0
+.sp 1
+.SH
+Making contact with \s-1UNIX\s0
+.PP
+To use the editor you must first make contact with the computer
+by logging in to \s-2UNIX\s0.
+We'll quickly review the standard \s-2UNIX\s0 login procedure
+for the two ways you can make contact:
+on a terminal that is directly linked to the computer,
+or over a telephone line where the computer answers your call.
+.SH
+Directly-linked terminals
+.PP
+Turn on your terminal and press the \s-1RETURN\s0 key.
+You are now ready to login.
+.SH
+Dial-up terminals
+.PP
+If your terminal connects with the computer over a telephone line,
+turn on the terminal, dial the system access number,
+and, when you hear a high-pitched tone, place the
+telephone handset in the acoustic coupler, if you are using one.
+You are now ready to login.
+.SH
+Logging in
+.PP
+The message inviting you to login is:
+.DS I 1i
+login:
+.DE
+.LP
+Type your login name, which identifies you to \s-2UNIX\s0,
+on the same line as the login message,
+and press \s-2RETURN\s+2.
+If the terminal you are using
+has both upper and lower case,
+.B
+be sure you enter your login name in lower case;
+.R
+otherwise \s-2UNIX\s0 assumes your terminal
+has only upper case and will not recognize lower case
+letters you may type.
+\s-2UNIX\s0 types ``login:'' and you reply
+with your login name, for example ``susan'':
+.DS I 1i
+login: \fBsusan\fR \fI(and press the \s-2RETURN\s0 key)\fR
+.DE
+(In the examples, input you would type appears in
+.B "bold face"
+to distinguish it from the responses from \s-2UNIX\s0.)
+.PP
+\s-2UNIX\s0 will next respond with a request for a password
+as an additional precaution to prevent
+unauthorized people from using your account.
+The password will not appear when you type it,
+to prevent others from seeing it.
+The message is:
+.DS I 1i
+Password: \fI(type your password and press \s-2RETURN\s+2)\fR
+.DE
+If any of the information you gave during the login
+sequence was mistyped or incorrect,
+\s-2UNIX\s0 will respond with
+.DS I 1i
+Login incorrect.
+.if t .sp .2v
+.if n .sp 1
+login:
+.DE
+in which case you should start the login process anew.
+Assuming that you have successfully
+logged in, \s-2UNIX\s0
+will print the message of the day and eventually will present
+you with a % at the beginning of a fresh line.
+The % is the \s-2UNIX\s0 prompt symbol
+which tells you that \s-2UNIX\s0 is ready to accept a command.
+.bd I 3
+.SH
+Asking for \fIedit\fP
+.fl
+.bd I
+.PP
+You are ready to tell \s-2UNIX\s0 that you
+want to work with edit, the text editor.
+Now is a convenient time to choose
+a name for the file of text you are about to create.
+To begin your editing session,
+type
+.B edit
+followed by a space and then the filename
+you have selected; for example, ``text''.
+After that,
+press the \s-2RETURN\s0 key and wait for edit's response:
+.DS I 1i
+% \fBedit text\fP \fI(followed by a \s-2RETURN\s+2)\fR
+"text" No such file or directory
+:
+.DE
+If you typed the command correctly,
+you will now be in communication with edit.
+Edit has set aside a buffer for use as
+a temporary working space during your current editing session.
+Since ``text'' is a new file we are about to create
+the editor was unable to find that file, which it
+confirms by saying:
+.DS I 1i
+"text" No such file or directory
+.DE
+On the next line appears edit's prompt ``:'',
+announcing that you are in \f2command mode\f1 and
+edit expects a command from you.
+You may now begin to create the new file.
+.SH
+The ``Command not found'' message
+.PP
+If you misspelled edit by typing, say, ``editor'',
+this might appear:
+.DS I 1i
+% \fBeditor\fP
+editor: Command not found
+%
+.DE
+Your mistake in calling edit ``editor'' was
+treated by \s-2UNIX\s0 as a request
+for a program named ``editor''.
+Since there is no program
+named ``editor'',
+\s-2UNIX\s0 reported that the program was ``not found''.
+A new % indicates that \s-2UNIX\s0 is ready for another command,
+and you may then enter the correct command.
+.SH
+A summary
+.PP
+Your exchange with \s-2UNIX\s0 as you logged in and made contact with edit
+should look something like this:
+.DS I 1i
+login: \fBsusan\fP
+Password:
+\&... A Message of General Interest ...
+% \fBedit text\fP
+"text" No such file or directory
+:
+.DE
+.SH
+Entering text
+.PP
+You may now begin entering text into the buffer.
+This is done by \fIappending\fP (or adding) text to whatever
+is currently in the buffer.
+Since there is nothing in the buffer at the moment,
+you are appending text to nothing;
+in effect,
+since you are adding text to nothing
+you are creating text.
+Most edit commands have two equivalent forms:
+a word that suggests what the command does,
+and a shorter abbreviation of that word.
+Many beginners find the full command names
+easier to remember at first,
+but once you are familiar with editing you may
+prefer to type the shorter abbreviations.
+The command to input text is ``append''.
+(It may be abbreviated ``a''.)
+Type
+.B append
+and press the \s-2RETURN\s0 key.
+.DS I 1i
+% \fBedit text
+\fR:\|\fBappend
+.R
+.DE
+.SH
+.bd I 3
+Messages from
+.I edit
+.fl
+.bd I
+.PP
+If you make a mistake in entering a command and
+type something that edit does not recognize,
+edit will respond with a message
+intended to help you diagnose your error.
+For example, if you misspell the command to input text by typing,
+perhaps, ``add'' instead of ``append'' or ``a'',
+you will receive this message:
+.DS I 1i
+:\|\fBadd\fR
+add: Not an editor command
+:
+.DE
+When you receive a diagnostic message,
+check what you typed in order to determine what
+part of your command confused edit.
+The message above means that edit
+was unable to recognize your mistyped command
+and, therefore, did not execute it.
+Instead, a new ``:''
+appeared to let you know that
+edit is again ready to execute a command.
+.SH
+Text input mode
+.PP
+By giving the command ``append'' (or using the abbreviation ``a''),
+you entered
+.I
+text input mode,
+.R
+also known as
+.I
+append mode.
+.R
+When you enter text input mode,
+edit stops sending you a prompt.
+You will not receive any prompts
+or error messages
+while in text input mode.
+You can enter
+pretty much anything you want on the lines.
+The lines are transmitted one by one to the buffer
+and held there during the editing session.
+You may append as much text as you want, and
+.I
+when you wish to stop entering text lines you should
+type a period as the only character on the line
+and press the \s-2RETURN\s0 key.
+.R
+When you type the period and press \s-2RETURN\s0,
+you signal that you want to stop appending text,
+and edit responds by allowing
+you to exit text input mode and reenter command mode.
+Edit will again
+prompt you for a command by printing ``:''.
+.PP
+Leaving append mode does not destroy the text in
+the buffer.
+You have to leave append
+mode to do any of the other kinds of editing,
+such as changing, adding, or printing text.
+If you type a period as the first character and
+type any other character on the same line,
+edit will believe you want to remain in append mode
+and will not let you out.
+As this can be very frustrating,
+be sure to type
+.B only
+the period and the \s-2RETURN\s0 key.
+.PP
+This is a good place to learn an important
+lesson about computers and text: a blank space is
+a character as far as a computer is concerned.
+If you so much as type a period followed by a blank
+(that is, type a period and then the space bar on the keyboard),
+you will remain in append mode with the last line of text
+being:
+.DS I 1i
+.B
+.ps +2
+\&.
+.ps -2
+.R
+.DE
+Let's say that you enter the lines
+(try to type
+.B exactly
+what you see, including ``thiss''):
+.DS I 1i
+.B
+This is some sample text.
+And thiss is some more text.
+Text editing is strange, but nice.
+\&.
+.R
+.DE
+The last line is the period followed by a \s-2RETURN\s0
+that gets you out of append mode.
+.SH
+Making corrections
+.PP
+If you have read a general introduction to \s-2UNIX\s0,
+you will recall that it is possible to erase individual
+letters that you have typed.
+This is done by typing the designated erase character
+as many times as there are characters
+you want to erase.
+.PP
+The usual erase character varies from place to place and
+user to user. Often it
+is the backspace (control-H),
+so you can correct typing errors
+in the line you are typing
+by holding down the \s-1CTRL\s+1 key
+and typing the ``H'' key. (Sometimes it is the DEL key.)
+If you type the erase character
+you will notice
+that the terminal backspaces in the line you are on.
+You can backspace over your error,
+and then type what you want to be the rest of the line.
+.PP
+If you make a bad start
+in a line
+and would like to begin again,
+you can either backspace to the beginning of the line
+or you can use the at-sign ``@'' to erase everything on the line:
+.DS I 1i
+.B
+Text edtiing is strange, but@
+Text editing is strange, but nice.
+.R
+.fl
+.bd S
+.DE
+When you type the at-sign (@), you erase
+the entire line typed so far
+and are given a fresh line to type on.
+You may immediately begin to retype the line.
+This, unfortunately, does not work after you type the
+line and press \s-2RETURN\s+2.
+To make corrections in lines that have been completed,
+it is necessary to use the editing commands
+covered in the next sessions.
+.SH
+Writing text to disk
+.PP
+You are now ready to edit the text. One common operation
+is to write the text to disk as a file for safekeeping
+after the session is over.
+This is the only way to save information from one session to the next,
+since the editor's buffer is temporary and will last only until the
+end of the editing session.
+Learning how to write a file to disk is second in
+importance only to entering the text.
+To write the contents of the buffer to a disk
+file, use the command ``write''
+(or its abbreviation ``w''):
+.DS I 1i
+:\|\fBwrite
+.R
+.DE
+Edit will copy the contents of the buffer to a disk file.
+If the file does not yet exist,
+a new file will be created automatically
+and the presence of a ``[New file]'' will be noted.
+The newly-created file will be given the name specified when
+you entered the editor, in this case ``text''.
+To confirm that the disk file has been successfully written,
+edit will repeat the filename and give
+the number of lines and the total
+number of characters in the file.
+The buffer remains unchanged by the ``write'' command.
+All of the lines that were written to disk will still be
+in the buffer,
+should you want to modify or add to them.
+.PP
+Edit must have a name for the file to be written.
+If you forgot to indicate the name of the file
+when you began to edit,
+edit will print in response to your write command:
+.DS I 1i
+No current filename
+.DE
+If this happens, you can specify the filename in a new write command:
+.DS I 1i
+:\|\fBwrite text
+.R
+.DE
+After the ``write'' (or ``w''), type a space and then the name of the file.
+.SH
+Signing off
+.PP
+We have done enough for this first lesson on using the
+\s-2UNIX\s0 text editor, and are ready to quit the session with edit.
+To do this we type ``quit'' (or ``q'') and press \s-2RETURN\s+2:
+.DS I 1i
+:\|\fBwrite
+.R
+"text" [New file] 3 lines, 90 characters
+:\|\fBquit\fR
+%
+.DE
+The % is from \s-2UNIX\s0 to tell you that your session with edit is
+over and you may command \s-2UNIX\s0 further.
+Since we want
+to end the entire session at the terminal, we also need to
+exit from \s-2UNIX\s0.
+In response to the \s-2UNIX\s0 prompt of ``\|%\|''
+type the command
+.DS I 1i
+%\|\fBlogout\fR
+.DE
+This will end your session with \s-2UNIX\s0, and will ready the
+terminal for the next user.
+It is always important to type \fBlogout\fR at the end of a session
+to make absolutely sure no one
+could accidentally stumble into your abandoned
+session and thus gain access to your files,
+tempting even the most honest of souls.
+.sp 1
+.PP
+This is the end of the first session on \s-2UNIX\s0 text editing.
+.bp
+.TL
+Session 2
+.sp
+.PP
+Login with \s-2UNIX\s0 as in the first session:
+.DS I 1i
+login: \fBsusan\fP \fI(carriage return)\fR
+Password: \fI(give password and carriage return)\fR
+.if t .sp .2v
+.if n .sp 1
+\&... A Message of General Interest ...
+%
+.DE
+When you indicate you want to edit,
+you can specify the name of the file you worked on last time.
+This will
+start edit working, and it will fetch the contents of the
+file into the buffer, so that you can resume editing the same file.
+When edit has copied the file into the buffer, it
+will repeat its name and tell
+you the number of lines and characters it contains.
+Thus,
+.DS I 1i
+.B
+% edit text
+.R
+"text" 3 lines, 90 characters
+:
+.DE
+means you asked edit to fetch
+the file named ``text'' for editing,
+causing it to copy the
+90 characters of text into the buffer.
+Edit awaits
+your further instructions,
+and indicates this by its prompt character, the colon (:).
+In this session, we will append more text to our file,
+print the contents of the buffer, and learn to change the text of a line.
+.SH
+Adding more text to the file
+.PP
+If you want to add more to the end of your
+text you may do so by using the append command to enter text input mode.
+When ``append'' is the first command
+of your editing session,
+the lines you enter
+are placed at the end of the buffer.
+Here we'll use the abbreviation for the append command, ``a'':
+.DS I 1i
+:\|\fBa
+This is text added in Session 2.
+It doesn't mean much here, but
+it does illustrate the editor.
+\|\fB\s+2\&.\s-2
+.R
+.DE
+You may recall that once you enter append mode
+using the ``a'' (or ``append'') command,
+you need to type a line containing only a period (.)
+to exit append mode.
+.SH
+Interrupt
+.PP
+Should you press the \s-2RUB\s+2 key (sometimes labelled \s-2DELETE\s+2)
+while working with edit,
+it will send this message to you:
+.DS I 1i
+Interrupt
+:
+.DE
+Any command that edit might be executing
+is terminated by rub or delete,
+causing edit to prompt you for a new command.
+If you are appending text at the time,
+you will exit from append mode
+and be expected to give another command.
+The line of text you were typing
+when the append command was interrupted
+will not be entered into the buffer.
+.SH
+Making corrections
+.PP
+If while typing the line you hit an incorrect key,
+recall that
+you may delete the incorrect character
+or cancel the entire line of input by erasing in the usual way.
+Refer either
+to the last few pages of Session 1
+if you need to review
+the procedures for making a correction.
+The most important idea to remember is that
+erasing a character or cancelling a line must be done
+before you press the \s-2RETURN\s+2 key.
+.SH
+Listing what's in the buffer (p)
+.PP
+Having appended text to what you wrote in Session 1,
+you might want to see all the lines in the buffer.
+To print the contents of the buffer, type the command:
+.DS I 1i
+:\|\fB1,$p
+.R
+.DE
+The ``1''\(dg
+.FS
+\(dgThe numeral ``one'' is the top left-most key,
+and should not be confused with the letter ``el''.
+.FE
+stands for line 1 of the buffer,
+the ``$'' is a special symbol designating the last line
+of the buffer,
+and ``p'' (or \fBprint\fR) is the command to print from line 1
+to the end of the buffer.
+The command ``1,$p'' gives you:
+.DS I 1i
+This is some sample text.
+And thiss is some more text.
+Text editing is strange, but nice.
+This is text added in Session 2.
+It doesn't mean much here, but
+it does illustrate the editor.
+.DE
+Occasionally, you may accidentally
+type a character that can't be printed,
+which can be done by striking a key
+while the \s-2CTRL\s0 key is pressed.
+In printing lines, edit uses a special notation to
+show the existence of non-printing characters.
+Suppose you had introduced the non-printing character ``control-A''
+into the word ``illustrate''
+by accidently pressing the \s-2CTRL\s0 key while
+typing ``a''.
+This can happen on many terminals
+because the \s-2CTRL\s+2 key and the ``A'' key
+are beside each other.
+If your finger presses between the two keys,
+control-A results.
+When asked to print the contents of the buffer,
+edit would display
+.DS I 1i
+it does illustr^Ate the editor.
+.DE
+To represent the control-A, edit shows ``^A''.
+The sequence ``^'' followed by a capital
+letter stands for the one character
+entered by holding down the \s-2CTRL\s0 key and typing the letter
+which appears after the ``^''.
+We'll soon discuss the commands that can be used
+to correct this typing error.
+.PP
+In looking over the text we see that
+``this'' is typed as ``thiss'' in the second line,
+a deliberate error so we can learn to make corrections.
+Let's correct the spelling.
+.SH
+Finding things in the buffer
+.PP
+In order to change something in the buffer we first need to
+find it.
+We can find ``thiss'' in the text we have
+entered by looking at a listing
+of the lines.
+Physically speaking, we search the lines
+of text looking for ``thiss'' and stop searching when
+we have found it.
+The way to tell edit to search for something
+is to type it inside slash marks:
+.DS I 1i
+:\|\fB/thiss/
+.R
+.DE
+By typing
+.B /thiss/
+and pressing \s-1RETURN\s0,
+you instruct edit to search for ``thiss''.
+If you ask edit to look for a pattern of characters
+which it cannot find in the buffer,
+it will respond ``Pattern not found''.
+When edit finds
+the characters ``thiss'', it will print the line of text
+for your inspection:
+.DS I 1i
+And thiss is some more text.
+.DE
+Edit is now positioned in the buffer at the
+line it just printed,
+ready to make a change in the line.
+.bp
+.SH
+The current line
+.PP
+Edit keeps track of the line in the buffer where it is located
+at all times during an editing session.
+In general, the line that has been most recently
+printed, entered, or changed
+is the current location in the buffer.
+The editor is prepared to make changes
+at the current location in the buffer,
+unless you direct it to another location.
+.PP
+In particular,
+when you bring a file into the buffer,
+you will be located at the last line in the file,
+where the editor left off copying the lines
+from the file to the buffer.
+If your first editing command is ``append'',
+the lines you enter are added
+to the end of the file,
+after the current line \(em
+the last line in the file.
+.PP
+You can refer to your current location in the buffer by the
+symbol
+period (.) usually known by the name ``dot''.
+If you type ``.'' and carriage
+return you will be instructing edit to print the current line:
+.DS I 1i
+:\|\fB\s+2\&.\s-2
+.R
+And thiss is some more text.
+.DE
+.PP
+If you want to know the number of the current line,
+you can type
+.B \&.=
+and press \s-2RETURN\s+2,
+and edit will respond with the line number:
+.DS I 1i
+:\|\fB\s+2.\s-2=
+.R
+2
+.DE
+If you type the number of any line and press \s-2RETURN\s+2,
+edit will position you at that line and
+print its contents:
+.DS I 1i
+:\|\fB2
+.R
+And thiss is some more text.
+.DE
+You should experiment with these commands
+to gain experience in using them to make changes.
+.SH
+Numbering lines (nu)
+.PP
+The
+.B
+number (nu)
+.R
+command is similar to print,
+giving both the number and the text of each printed line.
+To see the number and the text of the current line type
+.DS I 1i
+:\|\fBnu
+.R
+\0\0\0\0\02\0\0And thiss is some more text.
+.DE
+Note that the shortest abbreviation for the number command is
+``nu'' (and not ``n'', which is used for a different command).
+You may specify a range of lines
+to be listed by the number command in the same way that lines
+are specified for print.
+For example, \f31,$nu\f1 lists all lines in the buffer with their
+corresponding line numbers.
+.SH
+Substitute command (s)
+.PP
+Now that you have found the misspelled word,
+you can change it from ``thiss'' to ``this''.
+As far as edit is concerned,
+changing things is a matter of
+substituting one thing for another.
+As
+.I a
+stood for
+.I append,
+so
+.I s
+stands for
+.I substitute.
+We will use the abbreviation ``s'' to reduce the chance
+of mistyping the substitute command.
+This command will instruct edit to make the change:
+.DS I 1i
+\f32s/thiss/this/\f1
+.DE
+We first indicate the line to be changed, line 2,
+and then
+type an ``s'' to indicate we want
+edit to make a substitution.
+Inside the first set of slashes
+are the characters that we want to change,
+followed by the characters to replace them,
+and then a closing slash mark.
+To summarize:
+.DS I 1i
+2s/ \fIwhat is to be changed\fR / \fIwhat to change it to \fR/
+.DE
+If edit finds an exact match of the characters to be
+changed it will make the change
+.B only
+in the first occurrence of the characters.
+If it does not find the characters
+to be changed, it will respond:
+.DS I 1i
+Substitute pattern match failed
+.DE
+indicating that your instructions could not be carried out.
+When edit does find the characters that you want to change,
+it will make the substitution and automatically print
+the changed line, so that you can check that the correct substitution
+was made.
+In the example,
+.DS I 1i
+:\|\fB2s/thiss/this/
+.R
+And this is some more text.
+.DE
+line 2 (and line 2 only) will be searched for the characters
+``thiss'', and when the first exact match is found, ``thiss''
+will be changed to ``this''.
+Strictly speaking, it was not necessary above to
+specify the number of the line to be changed.
+In
+.DS I 1i
+:\|\fBs/thiss/this/
+.R
+.DE
+edit will assume that we mean to change
+the line where we are currently located (``.'').
+In this case,
+the command without a line number would have produced the same result
+because we were already located
+at the line we wished to change.
+.PP
+For another illustration of the substitute command,
+let us choose the line:
+.DS I 1i
+Text editing is strange, but nice.
+.DE
+You can make this line a bit more positive
+by taking out the characters ``strange, but\ '' so the line
+reads:
+.DS I 1i
+Text editing is nice.
+.DE
+A command that will first position edit at the desired line
+and then make the substitution is:
+.DS I 1i
+:\|\fB/strange/s/strange, but //
+.R
+.DE
+.LP
+What we have done here is combine our search with
+our substitution.
+Such combinations are perfectly legal,
+and speed up editing quite a bit
+once you get used to them.
+That is, you do not necessarily have to use
+line numbers to identify a line to edit.
+Instead, you may identify the line you want to change
+by asking edit to search for a specified pattern of letters
+that occurs in that line.
+The parts of the above command are:
+.TS
+.in +1i
+.nr 35 \n(.u
+.nf
+.ds #d .d
+.if \(ts\n(.z\(ts\(ts .ds #d nl
+.nr 80 0
+.nr 38 \w\f3/strange/\fP
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \w\f3s\fP
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \w\f3/strange, but //\fP
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 81 0
+.nr 38 \wtells edit to find the characters ``strange'' in the text
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \wtells edit to make a substitution
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \wsubstitutes nothing at all for the characters ``strange, but ''
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 1n
+.nr 79 0
+.nr 40 \n(79+(0*\n(38)
+.nr 80 +\n(40
+.nr 41 \n(80+(3*\n(38)
+.nr 81 +\n(41
+.nr TW \n(81
+.if t .if (\n(TW+\n(.o)>7.75i .tm Table at line 307 file ed2.tbl is too wide - \n(TW units
+.fc  
+.nr #T 0
+.eo
+.de T#
+.ds #d .d
+.if \(ts\n(.z\(ts\(ts .ds #d nl
+.mk ##
+.nr ## -1v
+..
+.ec
+.ta \n(80u \n(81u
+\&\h'|\n(40u'\f3/strange/\fP\h'|\n(41u'tells edit to find the characters ``strange'' in the text
+.ta \n(80u \n(81u
+\&\h'|\n(40u'\f3s\fP\h'|\n(41u'tells edit to make a substitution
+.ta \n(80u \n(81u
+\&\h'|\n(40u'\f3/strange, but //\fP\h'|\n(41u'substitutes nothing at all for the characters ``strange, but ''
+.fc
+.nr T. 1
+.T# 1
+.if \n(35>0 .fi
+.in -1i
+.TE
+.PP
+You should note the space after ``but'' in ``/strange, but /''.
+If you do not indicate that the space is to be taken out,
+your line will read:
+.DS I 1i
+.if t Text editing is nice.
+.if n Text editing is nice.
+.DE
+which looks a little funny
+because of the extra space between ``is'' and ``nice''.
+Again, we realize from this that a blank space
+is a real character to a computer, and in editing text
+we need to be aware of spaces
+within a line just as we would be aware of an ``a'' or
+a ``4''.
+.SH
+Another way to list what's in the buffer (z)
+.PP
+Although the print command is useful for looking at specific lines
+in the buffer,
+other commands may be more convenient for
+viewing large sections of text.
+You can ask to see a screen full of text at a time
+by using the command
+.B z.
+If you type
+.DS I 1i
+:\|\fB1z
+.R
+.DE
+edit will start with line 1 and continue printing lines,
+stopping either when the screen of
+your terminal is full
+or when the last line in the buffer has been printed.
+If you want to read the next segment of text, type the command
+.DS I 1i
+:\|\fBz
+.DE
+If no starting line number is given for the z command,
+printing will start at the ``current'' line, in this case the
+last line printed.
+Viewing lines in the buffer one screen full at a time
+is known as \fIpaging\fR.
+Paging can also be used to print
+a section of text on a hard-copy terminal.
+.SH
+Saving the modified text
+.PP
+This seems to be a good place to pause in our work,
+and so we should end the second session.
+If you (in haste) type ``q'' to quit the session
+your dialogue with edit will be:
+.DS I 1i
+:\|\fBq
+.R
+No write since last change (:quit! overrides)
+:
+.DE
+This is edit's warning that you have not written
+the modified contents of the buffer to disk.
+You run the risk of losing the work you did
+during the editing session since you typed the latest write
+command.
+Because in this lesson we have not written
+to disk at all, everything we have done
+would have been lost
+if edit had obeyed the \fBq\fR command.
+If you did not want to save the work done during
+this editing session, you would have to type ``q!''
+or (``quit!'')
+to confirm that you indeed wanted to end the session
+immediately,
+leaving the file as it was
+after the most recent ``write'' command.
+However,
+since you want to save what
+you have edited, you need to type:
+.DS I 1i
+:\|\fBw
+.R
+"text" 6 lines, 171 characters
+.DE
+and then follow with the commands to quit and logout:
+.DS I 1i
+:\|\fBq
+% \fBlogout\fR
+.DE
+and hang up the phone or turn off the terminal when
+\s-2UNIX\s0 asks for a name.
+Terminals connected to the port selector
+will stop after the logout command,
+and pressing keys on the keyboard will do nothing.
+.sp 1
+.PP
+This is the end of the second session on \s-2UNIX\s0 text editing.
+.bp
+.TL
+Session 3
+.SH
+Bringing text into the buffer (e)
+.PP
+Login to \s-2UNIX\s0 and make contact with edit.
+You should try to login without
+looking at the notes, but if you must
+then by all means do.
+.PP
+Did you remember to give the name of the file
+you wanted to edit?
+That is, did you type
+.DS I 1i
+% \fBedit text\fR
+.DE
+or simply
+.DS I 1i
+% \fBedit\fR
+.DE
+Both ways get you in contact with edit, but the first way
+will bring a copy of the file named ``text'' into
+the buffer.
+If you did forget to tell edit the name of your file,
+you can get it into the buffer by
+typing:
+.DS I 1i
+:\|\fBe text
+.R
+"text" 6 lines, 171 characters
+.DE
+The command
+.B edit,
+which may be abbreviated \fBe\fR,
+tells edit that you want
+to erase anything that might already be in
+the buffer and bring a copy of the file ``text'' into the buffer
+for editing.
+You may also use the edit (e) command to change files in
+the middle of an editing session,
+or to give edit the name of a new file that you want to create.
+Because the edit command clears the buffer,
+you will receive a warning if you try to edit a new file without
+having saved a copy of the old file.
+This gives you a chance to write the contents of the buffer to disk
+before editing the next file.
+.SH
+Moving text in the buffer (m)
+.PP
+Edit allows you to move lines of text
+from one location in the buffer to another
+by means of the
+.B move
+(\fBm\fR) command.
+The first two examples are for illustration only,
+though after you have read this Session
+you are welcome to return to them for practice.
+The command
+.DS I 1i
+:\|\fB2,4m$
+.R
+.DE
+directs edit to move lines 2, 3, and 4
+to the end of the buffer ($).
+The format for the move command is that you specify
+the first line to be moved, the last line to be moved,
+the move command ``m'', and the line after which
+the moved text is to be placed.
+So,
+.DS I 1i
+:\|\fB1,3m6
+.R
+.DE
+would instruct edit to move lines 1 through 3 (inclusive)
+to a location after line 6 in the buffer.
+To move only one line, say, line 4,
+to a location in the buffer after line 5,
+the command would be ``4m5''.
+.PP
+Let's move some text using the command:
+.DS I 1i
+:\|\fB5,$m1
+.R
+2 lines moved
+it does illustrate the editor.
+.DE
+After executing a command that moves more than one line of the buffer,
+edit tells how many lines were affected by the move
+and prints the last moved line for your inspection.
+If you want to see more than just the last line,
+you can then
+use the print (p), z, or number (nu) command to view more text.
+The buffer should now contain:
+.DS I 1i
+This is some sample text.
+It doesn't mean much here, but
+it does illustrate the editor.
+And this is some more text.
+Text editing is nice.
+This is text added in Session 2.
+.DE
+You can restore the original order by typing:
+.DS I 1i
+:\|\fB4,$m1
+.R
+.DE
+or, combining context searching and the move command:
+.DS I 1i
+:\|\fB/And this is some/,/This is text/m/This is some sample/
+.R
+.DE
+(Do not type both examples here!)
+The problem with combining context searching
+with the move command
+is that your chance of making a typing error
+in such a long command is greater than
+if you type line numbers.
+.SH
+Copying lines (copy)
+.PP
+The
+.B copy
+command
+is used to make a second copy of specified lines,
+leaving the original lines where they were.
+Copy
+has the same format as the move command, for example:
+.DS I 1i
+:\|\fB2,5copy $
+.R
+.DE
+makes a copy of lines 2 through 5,
+placing the added lines after the buffer's end ($).
+Experiment with the copy command
+so that you can become familiar with how it works.
+Note that the shortest abbreviation for copy is
+\f3co\f1 (and
+not the letter ``c'', which has another meaning).
+.SH
+Deleting lines (d)
+.PP
+Suppose you want to delete
+the line
+.DS I 1i
+This is text added in Session 2.
+.DE
+from the buffer.
+If you know the number of the line to be deleted,
+you can type
+that number followed by
+\fBdelete\fR or \fBd\fR.
+This example deletes line 4,
+which is ``This is text added in Session 2.''
+if you typed the commands
+suggested so far.
+.DS I 1i
+:\|\fB4d
+.R
+It doesn't mean much here, but
+.DE
+Here ``4'' is the number of the line to be deleted,
+and ``delete'' or ``d'' is the command to delete the line.
+After executing the delete command,
+edit prints the line that has become the current line (``.'').
+.PP
+If you do not happen to know the line number
+you can search for the line and then delete it using this
+sequence of commands:
+.DS I 1i
+:\|\fB/added in Session 2./
+.R
+This is text added in Session 2.
+:\|\fBd
+.R
+It doesn't mean much here, but
+.DE
+The ``/added in Session 2./''
+asks edit to locate and print
+the line containing the indicated text,
+starting its search at the current line
+and moving line by line
+until it finds the text.
+Once you are sure that you have correctly specified the line
+you want to delete,
+you can enter the delete (d) command.
+In this case it is not necessary to
+specify a line number before the ``d''.
+If no line number is given,
+edit deletes the current line (``.''),
+that is, the line found by our search.
+After the deletion, your buffer should contain:
+.DS I 1i
+This is some sample text.
+And this is some more text.
+Text editing is nice.
+It doesn't mean much here, but
+it does illustrate the editor.
+And this is some more text.
+Text editing is nice.
+This is text added in Session 2.
+It doesn't mean much here, but
+.DE
+To delete both lines 2 and 3:
+.DS I 1i
+And this is some more text.
+Text editing is nice.
+.DE
+you type
+.DS I 1i
+:\|\f32,3d\f1
+2 lines deleted
+.DE
+which specifies the range of lines from 2 to 3,
+and the operation on those lines \(em ``d'' for delete.
+If you delete more than one line
+you will receive a message
+telling you the number of lines deleted,
+as indicated in the example above.
+.PP
+The previous example assumes that you know the line numbers for
+the lines to be deleted.
+If you do not you might combine the search command
+with the delete command:
+.DS I 1i
+:\|\fB/And this is some/,/Text editing is nice./d
+.R
+.DE
+.SH
+A word or two of caution
+.PP
+In using the search function to locate lines to
+be deleted you should be
+.B
+absolutely sure
+.R
+the characters you give as the basis for the search
+will take edit to the line you want deleted.
+Edit will search for the first
+occurrence of the characters starting from where
+you last edited \-
+that is, from the line you see printed if you type dot (.).
+.PP
+A search based on too few
+characters may result in the wrong lines being deleted,
+which edit will do as easily as if you had meant it.
+For this reason, it is usually safer
+to specify the search and then delete in two separate steps,
+at least until you become familiar enough with using the editor
+that you understand how best to specify searches.
+For a beginner it is not a bad idea to double-check
+each command before pressing \s-2RETURN\s+2 to send the command on its way.
+.SH
+Undo (u) to the rescue
+.PP
+The
+.B
+undo (u)
+.R
+command has the ability to
+reverse the effects of the last command that changed the buffer.
+To undo the previous command, type
+``u'' or ``undo''.
+Undo can rescue
+the contents of the buffer from many an unfortunate mistake.
+However, its powers are not unlimited,
+so it is still wise to be reasonably
+careful about the commands you give.
+.PP
+It is possible to undo only commands which
+have the power to change the buffer \(em for example,
+delete, append, move, copy, substitute, and even undo itself.
+The commands write (w) and edit (e), which interact with disk files,
+cannot be undone, nor can commands that do not change
+the buffer, such as print.
+Most importantly,
+the
+.B only
+command that can be reversed by undo
+is the
+last ``undo-able'' command you typed.
+You can use control-H and @ to change
+commands while you are typing them,
+and undo to reverse the effect of the commands
+after you have typed them and pressed \s-2RETURN\s+2.
+.PP
+To illustrate,
+let's issue an undo command.
+Recall that the last buffer-changing command we gave deleted
+the lines formerly numbered 2 and 3.
+Typing undo at this moment will reverse the effects
+of the deletion, causing those two lines to be
+replaced in the buffer.
+.DS I 1i
+:\|\fBu
+.R
+2 more lines in file after undo
+And this is some more text.
+.DE
+Here again, edit informs you if the command affects more
+than one line,
+and prints
+the text of the line which is now ``dot'' (the current line).
+.SH
+More about the dot (.) and buffer end ($)
+.PP
+The function assumed by the symbol dot depends on its context.
+It can be used:
+.IP
+1. to exit from append mode; we type dot (and only a dot) on
+a line and press \s-2RETURN\s+2;
+.IP
+2. to refer to the line we are at in the buffer.
+.LP
+Dot can also be combined with the equal sign to get
+the number of the line currently being edited:
+.DS I 1i
+:\|\fB\&.=
+.R
+.DE
+If we type ``\fB.\fR='' we are asking for the number of the line,
+and if we type ``\fB.\fR'' we are asking for the text of the line.
+.PP
+In this editing session and the last, we used the dollar
+sign to indicate the end of the buffer
+in commands such as print, copy, and move.
+The dollar sign as a command asks edit to print the last
+line in the buffer.
+If the dollar sign is combined with the equal sign (\f3$=\f1)
+edit will print the line number corresponding to the
+last line in the buffer.
+.PP
+``\fB.\fR'' and ``$'', then, represent line numbers.
+Whenever appropriate, these symbols can be used in
+place of line numbers in commands.
+For example
+.DS I 1i
+:\|\fB\s+2.\s-2,$d
+.R
+.DE
+instructs edit to delete all lines from the current line (\fB.\fR)
+to the end of the buffer.
+.SH
+Moving around in the buffer (+ and \-)
+.PP
+When you are editing
+you often want
+to go back and re-read a previous line.
+You could specify a context search for a line you want to
+read if you remember some of its text,
+but if you simply want to see what was written a few, say 3, lines
+ago, you can type
+.DS I 1i
+\-3p
+.DE
+This tells edit to move back to a position 3 lines
+before the current line (.)
+and print that line.
+You can move forward in the buffer similarly:
+.DS I 1i
++2p
+.DE
+instructs edit to print the line that is 2
+ahead of your current position.
+.PP
+You may use ``+'' and ``\-'' in any command where edit
+accepts line numbers.
+Line numbers specified with ``+'' or ``\-''
+can be combined to print a range of lines.
+The command
+.DS I 1i
+:\|\fB\-1,+2copy$
+.R
+.DE
+makes a copy of 4 lines: the current line, the line before it,
+and the two after it.
+The copied lines will be placed after the last line
+in the buffer ($),
+and the original lines referred to by ``\-1'' and ``+2''
+remain where they are.
+.PP
+Try typing only ``\-''; you will move back one line just as
+if you had typed ``\-1p''.
+Typing the command ``+'' works similarly.
+You might also try typing a few plus or minus signs in a row
+(such as ``+++'') to see edit's response.
+Typing \s-2RETURN\s+2 alone on a line is the equivalent
+of typing ``+1p''; it will move you one line ahead in the buffer
+and print that line.
+.PP
+If you are at the last line of the buffer and try
+to move further ahead, perhaps by typing a ``+'' or
+a carriage return alone on the line,
+edit will remind you that you are at the end of the buffer:
+.sp
+.nf
+.ti 1i
+At end-of-file
+.br
+or
+.ti 1i
+Not that many lines in buffer
+.fi
+.LP
+Similarly, if you try to move to a position before the first line,
+edit will print one of these messages:
+.sp
+.nf
+.ti 1i
+Nonzero address required on this command
+.br
+or
+.ti 1i
+Negative address \- first buffer line is 1
+.fi
+.LP
+The number associated with a buffer line is the line's ``address'',
+in that it can be used to locate the line.
+.SH
+Changing lines (c)
+.PP
+You can also delete certain lines and
+insert new text in their place.
+This can be accomplished easily with the
+.B "change (c)"
+command.
+The change command instructs edit to delete specified lines
+and then switch to text input mode to
+accept the text that will replace them.
+Let's say you want to change the first two lines in the buffer:
+.DS I 1i
+This is some sample text.
+And this is some more text.
+.DE
+to read
+.DS I 1i
+This text was created with the \s-2UNIX\s0 text editor.
+.DE
+To do so, you type:
+.DS I 1i
+:\|\fB1,2c
+.R
+2 lines changed
+.B
+This text was created with the \s-2UNIX\s0 text editor.
+\s+2\&.\s-2
+.R
+:
+.DE
+In the command
+.B 1,2c
+we specify that we want to change
+the range of lines beginning with 1 and ending with 2
+by giving line numbers as with the print command.
+These lines will be deleted.
+After you type \s-2RETURN\s+2 to end the change command,
+edit notifies you if more than one line will be changed
+and places you in text input mode.
+Any text typed on the following lines will be inserted into
+the position where lines were deleted by the change command.
+.B
+You will remain in text input mode until you exit in the usual way,
+by typing a period alone on a line.
+.R
+Note that the number of lines added to the buffer need not be
+the same as the number of lines deleted.
+.sp 1
+.PP
+This is the end of the third session on text editing with \s-2UNIX\s0.
+.bp
+.SH
+.ce 1
+\s+2Session 4\s0
+.sp
+.PP
+This lesson covers several topics, starting with
+commands that apply throughout the buffer,
+characters with special meanings,
+and how to issue \s-2UNIX\s0 commands while in the editor.
+The next topics deal with files:
+more on reading and writing,
+and methods of recovering files lost in a crash.
+The final section suggests sources of further information.
+.SH
+Making commands global (g)
+.PP
+One disadvantage to the commands we have used for
+searching or substituting is that if you
+have a number of instances of a word to change
+it appears that you have to type the command
+repeatedly, once for
+each time the change needs to be made.
+Edit, however, provides a way to make commands
+apply to the entire contents of the buffer \-
+the
+.B
+global (g)
+.R
+command.
+.PP
+To print all lines
+containing a certain sequence of characters
+(say, ``text'')
+the command is:
+.DS I 1i
+:\|\fBg/text/p
+.R
+.DE
+The ``g'' instructs edit to
+make a global search for all lines
+in the buffer containing the characters ``text''.
+The ``p'' prints the lines found.
+.PP
+To issue a global command, start by typing a ``g'' and then a search
+pattern identifying
+the lines to be affected.
+Then, on the same line, type the command to be
+executed for the identified lines.
+Global substitutions are frequently useful.
+For example,
+to change all instances of the word ``text'' to the word ``material''
+the command would be a combination of the global search and the
+substitute command:
+.DS I 1i
+:\|\fBg/text/s/text/material/g
+.R
+.DE
+Note the ``g'' at the end of the global command,
+which instructs edit to change
+each and every instance of ``text'' to ``material''.
+If you do not type the ``g'' at the end of the command
+only the
+.I first
+instance of ``text'' \fIin each line\fR will be changed
+(the normal result of the substitute command).
+The ``g'' at the end of the command is independent of the ``g''
+at the beginning.
+You may give a command such as:
+.DS I 1i
+:\|\fB5s/text/material/g
+.R
+.DE
+to change every instance of ``text'' in line 5 alone.
+Further, neither command will change ``text'' to ``material''
+if ``Text'' begins with a capital rather than a lower-case
+.I t.
+.PP
+Edit does not automatically print the lines modified by a
+global command.
+If you want the lines to be printed, type a ``p''
+at the end of the global command:
+.DS I 1i
+:\|\fBg/text/s/text/material/gp
+.R
+.DE
+You should be careful
+about using the global command in combination with any other \-
+in essence, be sure of what you are telling edit to do
+to the entire buffer.
+For example,
+.DS I 1i
+:\|\fBg/ /d
+.R
+72 less lines in file after global
+.DE
+will delete every line containing a blank anywhere in it.
+This could adversely affect
+your document, since most lines have spaces between words
+and thus would be deleted.
+After executing the global command,
+edit will print a warning if the command added or deleted more than one line.
+Fortunately, the undo command can reverse
+the effects of a global command.
+You should experiment with the global command
+on a small file of text to see what it can do for you.
+.SH
+More about searching and substituting
+.PP
+In using slashes to identify a character string
+that we want to search for or change,
+we have always specified the exact characters.
+There is a less tedious way to
+repeat the same string of characters.
+To change ``text'' to ``texts'' we may type either
+.DS I 1i
+:\|\fB/text/s/text/texts/
+.R
+.DE
+as we have done in the past,
+or a somewhat abbreviated command:
+.DS I 1i
+:\|\fB/text/s//texts/
+.R
+.DE
+In this example, the characters to be changed
+are not specified \-
+there are no characters, not even a space,
+between the two slash marks
+that indicate what is to be changed.
+This lack of characters between the slashes
+is taken by the editor to mean
+``use the characters we last searched for as the characters to be changed.''
+.PP
+Similarly, the last context search may be repeated
+by typing a pair of slashes with nothing between them:
+.DS I 1i
+:\|\fB/does/
+.R
+It doesn't mean much here, but
+:\|\fB//
+.R
+it does illustrate the editor.
+.DE
+(You should note that the search command found the characters ``does''
+in the word ``doesn't'' in the first search request.)
+Because no characters are specified for the second search,
+the editor scans the buffer for the next occurrence of the
+characters ``does''.
+.PP
+Edit normally searches forward through the buffer,
+wrapping around from the end of the buffer to the beginning,
+until the specified character string is found.
+If you want to search in the reverse direction,
+use question marks (?) instead of slashes
+to surround the characters you are searching for.
+.PP
+It is also possible
+to repeat the last substitution
+without having to retype the entire command.
+An ampersand (&) used as a command
+repeats the most recent substitute command,
+using the same search and replacement patterns.
+After altering the current line by typing
+.DS I 1i
+:\|\fBs/text/texts/
+.R
+.DE
+you type
+.DS I 1i
+:\|\fB/text/&
+.R
+.DE
+or simply
+.DS I 1i
+:\|\fB//&
+.R
+.DE
+to make the same change on the next line in the buffer
+containing the characters ``text''.
+.SH
+Special characters
+.PP
+Two characters have special meanings when
+used in specifying searches: ``$'' and ``^''.
+``$'' is taken by the editor to mean ``end of the line''
+and is used to identify strings
+that occur at the end of a line.
+.DS I 1i
+:\|\fBg/text.$/s//material./p
+.R
+.DE
+tells the editor to search for all lines ending in ``text.''
+(and nothing else, not even a blank space),
+to change each final ``text.'' to ``material.'',
+and print the changed lines.
+.PP
+The symbol ``^'' indicates the beginning of a line.
+Thus,
+.DS I 1i
+:\|\fBs/^/1. /
+.R
+.DE
+instructs the editor to insert ``1.'' and a space at the beginning
+of the current line.
+.PP
+The characters ``$'' and ``^'' have special meanings only in the context
+of searching.
+At other times, they are ordinary characters.
+If you ever need to search for a character that has a special meaning,
+you must indicate that the
+character is to lose temporarily
+its special significance by typing another special character,
+the backslash (\\), before it.
+.DS I 1i
+:\|\fBs/\\\\\&$/dollar/
+.R
+.DE
+looks for the character ``$'' in the current
+line and replaces it by the word ``dollar''.
+Were it not for the backslash, the ``$'' would have represented
+``the end of the line'' in your search
+rather than the character ``$''.
+The backslash retains its special significance
+unless it is preceded by another backslash.
+.SH
+Issuing \s-2UNIX\s0 commands from the editor
+.PP
+After creating several files with the editor,
+you may want to delete files
+no longer useful to you or ask for a list of your files.
+Removing and listing files are not functions of the editor,
+and so they require the use of \s-2UNIX\s0 system commands
+(also referred to as ``shell'' commands, as
+``shell'' is the name of the program that processes \s-2UNIX\s0 commands).
+You do not need to quit the editor to execute a \s-2UNIX\s0 command
+as long as you indicate that it
+is to be sent to the shell for execution.
+To use the \s-2UNIX\s0 command
+.B rm
+to remove the file named ``junk'' type:
+.DS I 1i
+:\|\fB!rm junk
+.R
+!
+:
+.DE
+The exclamation mark (!)
+indicates that the rest of the line is to be processed as a shell command.
+If the buffer contents have not been written since the last change,
+a warning will be printed before the command is executed:
+.DS I 1i
+[No write since last change]
+.DE
+The editor prints a ``!'' when the command is completed.
+Other tutorials describe useful features of the system,
+of which an editor is only one part.
+.SH
+Filenames and file manipulation
+.PP
+Throughout each editing session,
+edit keeps track of the name of the file being edited as the
+.I "current filename."
+Edit remembers as the current filename the name given
+when you entered the editor.
+The current filename changes whenever the edit (e) command
+is used to specify a new file.
+Once edit has recorded a current filename,
+it inserts that name into any command where a filename has been omitted.
+If a write command does not specify a file,
+edit, as we have seen, supplies the current filename.
+If you are editing a file named ``draft3'' having 283 lines in it,
+you can have the editor write onto a different file
+by including its name in the write command:
+.DS I 1i
+:\fB\|w chapter3
+.R
+"chapter3" [new file] 283 lines, 8698 characters
+.DE
+The current filename remembered by the editor
+.I
+will not be changed as a result of the write command.
+.R
+Thus, if the next write command
+does not specify a name,
+edit will write onto the current file (``draft3'')
+and not onto the file ``chapter3''.
+.SH
+The file (f) command
+.PP
+To ask for the current filename, type
+.B file
+(or
+.B f ).
+In response, the editor provides current information about the buffer,
+including the filename, your current position, the number of
+lines in the buffer,
+and the percent of the distance through the file
+your current location is.
+.DS I 1i
+:\|\fBf
+.R
+"text" [Modified] line 3 of 4 --75%--
+.DE
+.\"The expression ``[Edited]'' indicates that the buffer contains
+.\"either the editor's copy of the existing file ``text''
+.\"or a file which you are just now creating.
+If the contents of the buffer have changed
+since the last time the file was written,
+the editor will tell you that the file has been ``[Modified]''.
+After you save the changes by writing onto a disk file,
+the buffer will no longer be considered modified:
+.DS I 1i
+:\|\fBw
+.R
+"text" 4 lines, 88 characters
+:\|\fBf
+.R
+"text" line 3 of 4 --75%--
+.DE
+.SH
+Reading additional files (r)
+.PP
+The
+\f3read (r)\f1 command allows you to add the contents of a file
+to the buffer
+at a specified location,
+essentially copying new lines
+between two existing lines.
+To use it, specify the line after which the new text will be placed,
+the \f3read (r)\f1 command,
+and then the name of the file.
+If you have a file named ``example'', the command
+.DS I 1i
+:\|\fB$r example
+.R
+"example" 18 lines, 473 characters
+.DE
+reads the file ``example''
+and adds it to the buffer after the last line.
+The current filename is not changed by the read command.
+.SH
+Writing parts of the buffer
+.PP
+The
+.B
+write (w)
+.R
+command can write all or part of the buffer
+to a file you specify.
+We are already familiar with
+writing the entire contents of the
+buffer to a disk file.
+To write only part of the buffer onto a file,
+indicate the beginning and ending lines before the write command,
+for example
+.DS I 1i
+:\|\fB45,$w ending
+.R
+.DE
+Here all lines from 45 through the end of the buffer
+are written onto the file named
+.I ending.
+The lines remain in the buffer
+as part of the document you are editing,
+and you may continue to edit the entire buffer.
+Your original file is unaffected
+by your command to write part of the buffer
+to another file.
+Edit still remembers whether you have saved changes to the buffer
+in your original file or not.
+.SH
+Recovering files
+.PP
+Although it does not happen very often,
+there are times \s-2UNIX\s+2 stops working
+because of some malfunction.
+This situation is known as a \fIcrash\fR.
+Under most circumstances,
+edit's crash recovery feature
+is able to save work to within a few lines of changes
+before a crash (or an accidental phone hang up).
+If you lose the contents of an editing buffer in a system crash,
+you will normally receive mail when you login that gives
+the name of the recovered file.
+To recover the file,
+enter the editor and type the command
+.B recover
+(\fBrec\fR),
+followed by the name of the lost file.
+For example,
+to recover the buffer for an edit session
+involving the file ``chap6'', the command is:
+.DS I 1i
+.R
+:\|\fBrecover chap6
+.R
+.DE
+Recover is sometimes unable to save the entire buffer successfully,
+so always check the contents of the saved buffer carefully
+before writing it back onto the original file.
+For best results,
+write the buffer to a new file temporarily
+so you can examine it without risk to the original file.
+Unfortunately,
+you cannot use the recover command
+to retrieve a file you removed
+using the shell command \f3rm\f1.
+.SH
+Other recovery techniques
+.PP
+If something goes wrong when you are using the editor,
+it may be possible to save your work by using the command
+.B preserve
+(\fBpre\fR),
+which saves the buffer as if the system had crashed.
+If you are writing a file and you get the message
+``Quota exceeded'', you have tried to use more disk storage
+than is allotted to your account.
+.I
+Proceed with caution
+.R
+because it is likely that only a part
+of the editor's buffer is now present in the file you tried to write.
+In this case you should use the shell escape from the editor (!)
+to remove some files you don't need and try to write
+the file again.
+If this is not possible and you cannot find someone to help you,
+enter the command
+.DS I 1i
+:\|\fBpreserve
+.R
+.DE
+and wait for the reply,
+.DS I 1i
+File preserved.
+.DE
+If you do not receive this reply,
+seek help immediately.
+Do not simply leave the editor.
+If you do, the buffer will be lost,
+and you may not be able to save your file.
+If the reply is ``File preserved.''
+you can leave the editor
+(or logout)
+to remedy the situation.
+After a preserve, you can use the recover command
+once the problem has been corrected,
+or the \fB\-r\fR option of the edit command
+if you leave the editor and want to return.
+.PP
+If you make an undesirable change to the buffer
+and type a write command before discovering your mistake,
+the modified version will replace any previous version of the file.
+Should you ever lose a good version of a document in this way,
+do not panic and leave the editor.
+As long as you stay in the editor,
+the contents of the buffer remain accessible.
+Depending on the nature of the problem,
+it may be possible
+to restore the buffer to a more complete
+state with the undo command.
+After fixing the damaged buffer, you can again write the file
+to disk.
+.SH
+Further reading and other information
+.PP
+Edit is an editor designed for beginning and casual users.
+It is actually a version of a more powerful editor called
+.I ex.
+These lessons are intended to introduce you to the editor
+and its more commonly-used commands.
+We have not covered all of the editor's commands,
+but a selection of commands
+that should be sufficient to accomplish most of your editing tasks.
+You can find out more about the editor in the
+.I
+Ex Reference Manual,
+.R
+which is applicable to both
+.I ex
+and
+.I edit.
+One way to become familiar with the manual is to begin by reading
+the description of commands that you already know.
+.bd I 3
+.SH
+Using
+.I ex
+.fl
+.bd I
+.PP
+As you become more experienced with using the editor,
+you may still find that edit continues to meet your needs.
+However, should you become interested in using
+.I ex,
+it is easy to switch.
+To begin an editing session with
+.I ex,
+use the name
+.B ex
+in your command instead of
+.B edit.
+.PP
+Edit commands also work in
+.I ex,
+but the editing environment is somewhat different.
+You should be aware of a few differences
+between
+.I ex
+and
+.I edit.
+In edit, only the characters ``^'', ``$'', and ``\\'' have
+special meanings in searching the buffer
+or indicating characters to be changed by a substitute command.
+Several additional characters have special
+meanings in ex, as described in the
+.I
+Ex Reference Manual.
+.R
+Another feature of the edit environment prevents users from
+accidently entering two alternative modes of editing,
+.I open
+and
+.I visual,
+in which
+the editor behaves quite differently from normal command mode.
+If you are using ex and you encounter strange behavior,
+you may have accidently entered open mode by typing ``o''.
+Type the \s-2ESC\s0 key and then a ``Q''
+to get out of open or visual mode and back into
+the regular editor command mode.
+The document
+.I
+An Introduction to Display Editing with Vi\|\|
+.R
+provide full details of visual mode.
+.bp
+.SH
+.ce 1
+\s+2Index\s0
+.LP
+.sp 2
+.2C
+.nf
+addressing, \fIsee\fR line numbers
+ampersand, 20
+append mode, 6-7
+append (a) command, 6, 7, 9
+``At end of file'' (message), 18
+backslash (\\), 21
+buffer, 3
+caret (^), 10, 20
+change (c) command, 18
+command mode, 5-6
+``Command not found'' (message), 6
+context search, 10-12, 19-21
+control characters (``^'' notation), 10
+control-H, 7
+copy (co) command, 15
+corrections, 7, 16
+current filename, 21
+current line (\|.\|), 11, 17
+delete (d) command, 15-16
+dial-up, 5
+disk, 3
+documentation, 3, 23
+dollar ($), 10, 11, 17, 20-21
+dot (\f3\|.\|\f1) 11, 17
+edit (text editor), 3, 5, 23
+edit (e) command, 5, 9, 14
+editing commands:
+.in +.25i
+append (a), 6, 7, 9
+change (c), 18
+copy (co), 15
+delete (d), 15-16
+edit (text editor), 3, 5, 23
+edit (e), 5, 9, 14
+file (f), 21-22
+global (g), 19
+move (m), 14-15
+number (nu), 11
+preserve (pre), 22-23
+print (p), 10
+quit (q), 8, 13
+read (r), 22
+recover (rec), 22, 23
+substitute (s), 11-12, 19, 20
+undo (u), 16-17, 23
+write (w), 8, 13, 21, 22
+z, 12-13
+! (shell escape), 21
+$=, 17
++, 17
+\-, 17
+//, 12, 20
+??, 20
+\&., 11, 17
+\&.=, 11, 17
+.in -.25i
+entering text, 3, 6-7
+erasing
+.in +.25i
+characters (^H), 7
+lines (@), 7
+.in -.25i
+error corrections, 7, 16
+ex (text editor), 23
+\fIEx Reference Manual\fR, 23
+exclamation (!), 21
+file, 3
+file (f) command, 21-22
+file recovery, 22-23
+filename, 3, 21
+global (g) command, 19
+input mode, 6-7
+Interrupt (message), 9
+line numbers, \fIsee also\fR current line
+.in +.25i
+dollar sign ($), 10, 11, 17
+dot (\|.\|), 11, 17
+relative (+ and \-), 17
+.in -.25i
+list, 10
+logging in, 4-6
+logging out, 8
+``Login incorrect'' (message), 5
+minus (\-), 17
+move (m) command, 14-15
+``Negative address\(emfirst buffer line is 1'' (message), 18
+``No current filename'' (message), 8
+``No such file or directory'' (message), 5, 6
+``No write since last change'' (message), 21
+non-printing characters, 10
+``Nonzero address required'' (message), 18
+``Not an editor command'' (message), 6
+``Not that many lines in buffer'' (message), 18
+number (nu) command, 11
+password, 5
+period (\|.\|), 11, 17
+plus (+), 17
+preserve (pre) command, 22-23
+print (p) command, 10
+program, 3
+prompts
+.in .25i
+% (\s-2UNIX\s0), 5
+: (edit), 5, 6, 7
+\0 (append), 7
+.in -.25i
+question (?), 20
+quit (q) command, 8, 13
+read (r) command, 22
+recover (rec) command, 22, 23
+recovery, \fIsee\fR\| file recovery
+references, 3, 23
+remove (rm) command, 21, 22
+reverse command effects (undo), 16-17, 23
+searching, 10-12, 19-21
+shell, 21
+shell escape (!), 21
+slash (/), 11-12, 20
+special characters (^, $, \\), 10, 11, 17, 20-21
+substitute (s) command, 11-12, 19, 20
+terminals, 4-5
+text input mode, 7
+undo (u) command, 16-17, 23
+\s-1UNIX\s0, 3
+write (w) command, 8, 13, 21, 22
+z command, 12-13
+
diff --git a/usr.bin/vi/USD.doc/exref/Makefile b/usr.bin/vi/USD.doc/exref/Makefile
new file mode 100644
index 0000000..11f4e66
--- /dev/null
+++ b/usr.bin/vi/USD.doc/exref/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= usd/13.ex
+SRCS= ex.rm
+MACROS= -msU
+CLEANFILES=summary.*
+
+paper.ps: ${SRCS} summary.ps
+ ${ROFF} ${SRCS} > ${.TARGET}
+
+summary.ps: ex.summary
+ ${TBL} ex.summary | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/vi/USD.doc/exref/ex.rm b/usr.bin/vi/USD.doc/exref/ex.rm
new file mode 100644
index 0000000..79670c2
--- /dev/null
+++ b/usr.bin/vi/USD.doc/exref/ex.rm
@@ -0,0 +1,2230 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ex.rm 8.1 (Berkeley) 6/8/93
+.\"
+.EH 'USD:13-%''Ex Reference Manual'
+.OH 'Ex Reference Manual''USD:13-%'
+.de ZP
+.nr pd \\n()P
+.nr )P 0
+.if \\n(.$=0 .IP
+.if \\n(.$=1 .IP "\\$1"
+.if \\n(.$>=2 .IP "\\$1" "\\$2"
+.nr )P \\n(pd
+.rm pd
+..
+.de LC
+.br
+.sp .1i
+.ne 4
+.LP
+.ta 4.0i
+..
+.bd S B 3
+.\".RP
+.TL
+Ex Reference Manual
+.br
+Version 3.7
+.AU
+William Joy
+.AU
+Mark Horton
+.AI
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, Ca. 94720
+.AB
+.I Ex
+a line oriented text editor, which supports both command and display
+oriented editing.
+This reference manual describes the command oriented part of
+.I ex;
+the display editing features of
+.I ex
+are described in
+.I "An Introduction to Display Editing with Vi."
+Other documents about the editor include the introduction
+.I "Edit: A tutorial",
+the
+.I "Ex/edit Command Summary",
+and a
+.I "Vi Quick Reference"
+card.
+.AE
+.NH 1
+Starting ex
+.PP
+.FS
+The financial support of an \s-2IBM\s0 Graduate Fellowship and the National
+Science Foundation under grants MCS74-07644-A03 and MCS78-07291 is gratefully
+acknowledged.
+.FE
+Each instance of the editor has a set of options,
+which can be set to tailor it to your liking.
+The command
+.I edit
+invokes a version of
+.I ex
+designed for more casual or beginning
+users by changing the default settings of some of these options.
+To simplify the description which follows we
+assume the default settings of the options.
+.PP
+When invoked,
+.I ex
+determines the terminal type from the \s-2TERM\s0 variable in the environment.
+It there is a \s-2TERMCAP\s0 variable in the environment, and the type
+of the terminal described there matches the \s-2TERM\s0 variable,
+then that description
+is used. Also if the \s-2TERMCAP\s0 variable contains a pathname (beginning
+with a \fB/\fR) then the editor will seek the description of the terminal
+in that file (rather than the default /etc/termcap).
+If there is a variable \s-2EXINIT\s0 in the environment, then the editor
+will execute the commands in that variable,
+otherwise if there is a file
+.I \&.exrc
+in your \s-2HOME\s0 directory
+.I ex
+reads commands from that file, simulating a
+.I source
+command.
+Option setting commands placed in
+\s-2EXINIT\s0 or
+.I \&.exrc
+will be executed before each editor session.
+.PP
+A command to enter
+.I ex
+has the following prototype:\(dg
+.FS
+\(dg Brackets `[' `]' surround optional parameters here.
+.FE
+.DS
+\fBex\fP [ \fB\-\fP ] [ \fB\-v\fP ] [ \fB\-t\fP \fItag\fP ] [ \fB\-r\fP ] [ \fB\-l\fP ] [ \fB\-w\fP\fIn\fP ] [ \fB\-x\fP ] [ \fB\-R\fP ] [ \fB+\fP\fIcommand\fP ] name ...
+.DE
+The most common case edits a single file with no options, i.e.:
+.DS
+\fBex\fR name
+.DE
+The
+.B \-
+command line option
+option suppresses all interactive-user feedback
+and is useful in processing editor scripts in command files.
+The
+.B \-v
+option is equivalent to using
+.I vi
+rather than
+.I ex.
+The
+.B \-t
+option is equivalent to an initial
+.I tag
+command, editing the file containing the
+.I tag
+and positioning the editor at its definition.
+The
+.B \-r
+option is used in recovering after an editor or system crash,
+retrieving the last saved version of the named file or,
+if no file is specified,
+typing a list of saved files.
+The
+.B \-l
+option sets up for editing \s-2LISP\s0, setting the
+.I showmatch
+and
+.I lisp
+options.
+The
+.B \-w
+option sets the default window size to
+.I n,
+and is useful on dialups to start in small windows.
+The
+.B \-x
+option causes
+.I ex
+to prompt for a
+.I key ,
+which is used to encrypt and decrypt the contents of the file,
+which should already be encrypted using the same key,
+see
+.I crypt (1).
+The
+.B \-R
+option sets the
+.I readonly
+option at the start.
+.I Name
+arguments indicate files to be edited.
+An argument of the form
+\fB+\fIcommand\fR
+indicates that the editor should begin by executing the specified command.
+If
+.I command
+is omitted, then it defaults to ``$'', positioning the editor at the last
+line of the first file initially. Other useful commands here are scanning
+patterns of the form ``/pat'' or line numbers, e.g. ``+100'' starting
+at line 100.
+.NH 1
+File manipulation
+.NH 2
+Current file
+.PP
+.I Ex
+is normally editing the contents of a single file,
+whose name is recorded in the
+.I current
+file name.
+.I Ex
+performs all editing actions in a buffer
+(actually a temporary file)
+into which the text of the file is initially read.
+Changes made to the buffer have no effect on the file being
+edited unless and until the buffer contents are written out to the
+file with a
+.I write
+command.
+After the buffer contents are written,
+the previous contents of the written file are no longer accessible.
+When a file is edited,
+its name becomes the current file name,
+and its contents are read into the buffer.
+.PP
+The current file is almost always considered to be
+.I edited.
+This means that the contents of the buffer are logically
+connected with the current file name,
+so that writing the current buffer contents onto that file,
+even if it exists,
+is a reasonable action.
+If the current file is not
+.I edited
+then
+.I ex
+will not normally write on it if it already exists.*
+.FS
+* The
+.I file
+command will say ``[Not edited]'' if the current file is not considered
+edited.
+.FE
+.NH 2
+Alternate file
+.PP
+Each time a new value is given to the current file name,
+the previous current file name is saved as the
+.I alternate
+file name.
+Similarly if a file is mentioned but does not become the current file,
+it is saved as the alternate file name.
+.NH 2
+Filename expansion
+.PP
+Filenames within the editor may be specified using the normal
+shell expansion conventions.
+In addition,
+the character `%' in filenames is replaced by the
+.I current
+file name and the character
+`#' by the
+.I alternate
+file name.\(dg
+.FS
+\(dg This makes it easy to deal alternately with
+two files and eliminates the need for retyping the
+name supplied on an
+.I edit
+command after a
+.I "No write since last change"
+diagnostic is received.
+.FE
+.NH 2
+Multiple files and named buffers
+.PP
+If more than one file is given on the command line,
+then the first file is edited as described above.
+The remaining arguments are placed with the first file in the
+.I "argument list."
+The current argument list may be displayed with the
+.I args
+command.
+The next file in the argument list may be edited with the
+.I next
+command.
+The argument list may also be respecified by specifying
+a list of names to the
+.I next
+command.
+These names are expanded,
+the resulting list of names becomes the new argument list,
+and
+.I ex
+edits the first file on the list.
+.PP
+For saving blocks of text while editing, and especially when editing
+more than one file,
+.I ex
+has a group of named buffers.
+These are similar to the normal buffer, except that only a limited number
+of operations are available on them.
+The buffers have names
+.I a
+through
+.I z.\(dd
+.FS
+\(dd It is also possible to refer to
+.I A
+through
+.I Z;
+the upper case buffers are the same as the lower but commands
+append to named buffers rather than replacing
+if upper case names are used.
+.FE
+.NH 2
+Read only
+.PP
+It is possible to use
+.I ex
+in
+.I "read only"
+mode to look at files that you have no intention of modifying.
+This mode protects you from accidently overwriting the file.
+Read only mode is on when the
+.I readonly
+option is set.
+It can be turned on with the
+.B \-R
+command line option,
+by the
+.I view
+command line invocation,
+or by setting the
+.I readonly
+option.
+It can be cleared by setting
+.I noreadonly .
+It is possible to write, even while in read only mode, by indicating
+that you really know what you are doing.
+You can write to a different file, or can use the ! form of write,
+even while in read only mode.
+.NH 1
+Exceptional Conditions
+.NH 2
+Errors and interrupts
+.PP
+When errors occur
+.I ex
+(optionally) rings the terminal bell and, in any case, prints an error
+diagnostic. If the primary input is from a file, editor processing
+will terminate. If an interrupt signal is received,
+.I ex
+prints ``Interrupt'' and returns to its command level. If the primary
+input is a file, then
+.I ex
+will exit when this occurs.
+.NH 2
+Recovering from hangups and crashes
+.PP
+If a hangup signal is received and the buffer has been modified since
+it was last written out, or if the system crashes, either the editor
+(in the first case) or the system (after it reboots in the second) will
+attempt to preserve the buffer. The next time you log in you should be
+able to recover the work you were doing, losing at most a few lines of
+changes from the last point before the hangup or editor crash. To
+recover a file you can use the
+.B \-r
+option. If you were editing the file
+.I resume,
+then you should change
+to the directory where you were when the crash occurred, giving the command
+.DS
+\fBex \-r\fP\fI resume\fP
+.DE
+After checking that the retrieved file is indeed ok, you can
+.I write
+it over the previous contents of that file.
+.PP
+You will normally get mail from the system telling you when a file has
+been saved after a crash. The command
+.DS
+\fBex\fP \-\fBr\fP
+.DE
+will print a list of the files which have been saved for you.
+(In the case of a hangup,
+the file will not appear in the list,
+although it can be recovered.)
+.NH 1
+Editing modes
+.PP
+.I Ex
+has five distinct modes. The primary mode is
+.I command
+mode. Commands are entered in command mode when a `:' prompt is
+present, and are executed each time a complete line is sent. In
+.I "text input"
+mode
+.I ex
+gathers input lines and places them in the file. The
+.I append,
+.I insert,
+and
+.I change
+commands use text input mode.
+No prompt is printed when you are in text input mode.
+This mode is left by typing a `.' alone at the beginning of a line, and
+.I command
+mode resumes.
+.PP
+The last three modes are
+.I open
+and
+.I visual
+modes, entered by the commands of the same name, and, within open and
+visual modes
+.I "text insertion"
+mode.
+.I Open
+and
+.I visual
+modes allow local editing operations to be performed on the text in the
+file. The
+.I open
+command displays one line at a time on any terminal while
+.I visual
+works on \s-2CRT\s0 terminals with random positioning cursors, using the
+screen as a (single) window for file editing changes.
+These modes are described (only) in
+.I "An Introduction to Display Editing with Vi."
+.NH
+Command structure
+.PP
+Most command names are English words,
+and initial prefixes of the words are acceptable abbreviations.
+The ambiguity of abbreviations is resolved in favor of the more commonly
+used commands.*
+.FS
+* As an example, the command
+.I substitute
+can be abbreviated `s'
+while the shortest available abbreviation for the
+.I set
+command is `se'.
+.FE
+.NH 2
+Command parameters
+.PP
+Most commands accept prefix addresses specifying the lines in the file
+upon which they are to have effect.
+The forms of these addresses will be discussed below.
+A number of commands also may take a trailing
+.I count
+specifying the number of lines to be involved in the command.\(dg
+.FS
+\(dg Counts are rounded down if necessary.
+.FE
+Thus the command ``10p'' will print the tenth line in the buffer while
+``delete 5'' will delete five lines from the buffer,
+starting with the current line.
+.PP
+Some commands take other information or parameters,
+this information always being given after the command name.\(dd
+.FS
+\(dd Examples would be option names in a
+.I set
+command i.e. ``set number'',
+a file name in an
+.I edit
+command,
+a regular expression in a
+.I substitute
+command,
+or a target address for a
+.I copy
+command, i.e. ``1,5 copy 25''.
+.FE
+.NH 2
+Command variants
+.PP
+A number of commands have two distinct variants.
+The variant form of the command is invoked by placing an
+`!' immediately after the command name.
+Some of the default variants may be controlled by options;
+in this case, the `!' serves to toggle the default.
+.NH 2
+Flags after commands
+.PP
+The characters `#', `p' and `l' may be placed after many commands.**
+.FS
+**
+A `p' or `l' must be preceded by a blank or tab
+except in the single special case `dp'.
+.FE
+In this case, the command abbreviated by these characters
+is executed after the command completes.
+Since
+.I ex
+normally prints the new current line after each change, `p' is rarely necessary.
+Any number of `+' or `\-' characters may also be given with these flags.
+If they appear, the specified offset is applied to the current line
+value before the printing command is executed.
+.NH 2
+Comments
+.PP
+It is possible to give editor commands which are ignored.
+This is useful when making complex editor scripts
+for which comments are desired.
+The comment character is the double quote: ".
+Any command line beginning with " is ignored.
+Comments beginning with " may also be placed at the ends
+of commands, except in cases where they could be confused as part
+of text (shell escapes and the substitute and map commands).
+.NH 2
+Multiple commands per line
+.PP
+More than one command may be placed on a line by separating each pair
+of commands by a `|' character.
+However the
+.I global
+commands,
+comments,
+and the shell escape `!'
+must be the last command on a line, as they are not terminated by a `|'.
+.NH 2
+Reporting large changes
+.PP
+Most commands which change the contents of the editor buffer give
+feedback if the scope of the change exceeds a threshold given by the
+.I report
+option.
+This feedback helps to detect undesirably large changes so that they may
+be quickly and easily reversed with an
+.I undo.
+After commands with more global effect such as
+.I global
+or
+.I visual,
+you will be informed if the net change in the number of lines
+in the buffer during this command exceeds this threshold.
+.NH 1
+Command addressing
+.NH 2
+Addressing primitives
+.IP \fB.\fR 20
+The current line.
+Most commands leave the current line as the last line which they affect.
+The default address for most commands is the current line,
+thus `\fB.\fR' is rarely used alone as an address.
+.IP \fIn\fR 20
+The \fIn\fRth line in the editor's buffer, lines being numbered
+sequentially from 1.
+.IP \fB$\fR 20
+The last line in the buffer.
+.IP \fB%\fR 20
+An abbreviation for ``1,$'', the entire buffer.
+.IP \fI+n\fR\ \fI\-n\fR 20
+An offset relative to the current buffer line.\(dg
+.FS
+\(dg
+The forms `.+3' `+3' and `+++' are all equivalent;
+if the current line is line 100 they all address line 103.
+.FE
+.IP \fB/\fIpat\fR\fB/\fR\ \fB?\fIpat\fR\fB?\fR 20
+Scan forward and backward respectively for a line containing \fIpat\fR, a
+regular expression (as defined below). The scans normally wrap around the end
+of the buffer.
+If all that is desired is to print the next line containing \fIpat\fR, then
+the trailing \fB/\fR or \fB?\fR may be omitted.
+If \fIpat\fP is omitted or explicitly empty, then the last
+regular expression specified is located.\(dd
+.FS
+\(dd The forms \fB\e/\fP and \fB\e?\fP scan
+using the last regular expression used in a scan; after a substitute
+\fB//\fP and \fB??\fP would scan using the substitute's regular expression.
+.FE
+.IP \fB\(aa\(aa\fP\ \fB\(aa\fP\fIx\fP 20
+Before each non-relative motion of the current line `\fB.\fP',
+the previous current line is marked with a tag, subsequently referred to as
+`\(aa\(aa'.
+This makes it easy to refer or return to this previous context.
+Marks may also be established by the
+.I mark
+command, using single lower case letters
+.I x
+and the marked lines referred to as
+`\(aa\fIx\fR'.
+.NH 2
+Combining addressing primitives
+.PP
+Addresses to commands consist of a series of addressing primitives,
+separated by `,' or `;'.
+Such address lists are evaluated left-to-right.
+When addresses are separated by `;' the current line `\fB.\fR'
+is set to the value of the previous addressing expression
+before the next address is interpreted.
+If more addresses are given than the command requires,
+then all but the last one or two are ignored.
+If the command takes two addresses, the first addressed line must
+precede the second in the buffer.\(dg
+.FS
+\(dg Null address specifications are permitted in a list of addresses,
+the default in this case is the current line `.';
+thus `,100' is equivalent to `\fB.\fR,100'.
+It is an error to give a prefix address to a command which expects none.
+.FE
+.NH 1
+Command descriptions
+.PP
+The following form is a prototype for all
+.I ex
+commands:
+.DS
+\fIaddress\fR \fBcommand\fR \fI! parameters count flags\fR
+.DE
+All parts are optional; the degenerate case is the empty command which prints
+the next line in the file. For sanity with use from within
+.I visual
+mode,
+.I ex
+ignores a ``:'' preceding any command.
+.PP
+In the following command descriptions, the
+default addresses are shown in parentheses,
+which are
+.I not,
+however,
+part of the command.
+.LC
+\fBabbreviate\fR \fIword rhs\fP abbr: \fBab\fP
+.ZP
+Add the named abbreviation to the current list.
+When in input mode in visual, if
+.I word
+is typed as a complete word, it will be changed to
+.I rhs .
+.LC
+( \fB.\fR ) \fBappend\fR abbr: \fBa\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+Reads the input text and places it after the specified line.
+After the command, `\fB.\fR'
+addresses the last line input or the
+specified line if no lines were input.
+If address `0' is given,
+text is placed at the beginning of the buffer.
+.LC
+\fBa!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+The variant flag to
+.I append
+toggles the setting for the
+.I autoindent
+option during the input of
+.I text.
+.LC
+\fBargs\fR
+.ZP
+The members of the argument list are printed, with the current argument
+delimited by `[' and `]'.
+.ig
+.PP
+\fBcd\fR \fIdirectory\fR
+.ZP
+The
+.I cd
+command is a synonym for
+.I chdir.
+..
+.LC
+( \fB.\fP , \fB.\fP ) \fBchange\fP \fIcount\fP abbr: \fBc\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.ZP
+Replaces the specified lines with the input \fItext\fP.
+The current line becomes the last line input;
+if no lines were input it is left as for a
+\fIdelete\fP.
+.LC
+\fBc!\fP
+.br
+\fItext\fP
+.br
+\&\fB.\fP
+.ZP
+The variant toggles
+.I autoindent
+during the
+.I change.
+.ig
+.LC
+\fBchdir\fR \fIdirectory\fR
+.ZP
+The specified \fIdirectory\fR becomes the current directory.
+If no directory is specified, the current value of the
+.I home
+option is used as the target directory.
+After a
+.I chdir
+the current file is not considered to have been
+edited so that write restrictions on pre-existing files apply.
+..
+.LC
+( \fB.\fP , \fB.\fP )\|\fBcopy\fP \fIaddr\fP \fIflags\fP abbr: \fBco\fP
+.ZP
+A
+.I copy
+of the specified lines is placed after
+.I addr,
+which may be `0'.
+The current line
+`\fB.\fR'
+addresses the last line of the copy.
+The command
+.I t
+is a synonym for
+.I copy.
+.LC
+( \fB.\fR , \fB.\fR )\|\fBdelete\fR \fIbuffer\fR \fIcount\fR \fIflags\fR abbr: \fBd\fR
+.ZP
+Removes the specified lines from the buffer.
+The line after the last line deleted becomes the current line;
+if the lines deleted were originally at the end,
+the new last line becomes the current line.
+If a named
+.I buffer
+is specified by giving a letter,
+then the specified lines are saved in that buffer,
+or appended to it if an upper case letter is used.
+.LC
+\fBedit\fR \fIfile\fR abbr: \fBe\fR
+.br
+\fBex\fR \fIfile\fR
+.ZP
+Used to begin an editing session on a new file.
+The editor
+first checks to see if the buffer has been modified since the last
+.I write
+command was issued.
+If it has been,
+a warning is issued and the
+command is aborted.
+The
+command otherwise deletes the entire contents of the editor buffer,
+makes the named file the current file and prints the new filename.
+After insuring that this file is sensible\(dg
+.FS
+\(dg I.e., that it is not a binary file such as a directory,
+a block or character special file other than
+.I /dev/tty,
+a terminal,
+or a binary or executable file
+(as indicated by the first word).
+.FE
+the editor reads the file into its buffer.
+.IP
+If the read of the file completes without error,
+the number of lines and characters read is typed.
+If there were any non-\s-2ASCII\s0 characters
+in the file they are stripped of their non-\s-2ASCII\s0
+high bits,
+and any null characters in the file are discarded.
+If none of these errors occurred, the file is considered
+.I edited.
+If the last line of the input file is missing the trailing
+newline character, it will be supplied and a complaint will be issued.
+This command leaves the current line `\fB.\fR' at the last line read.\(dd
+.FS
+\(dd If executed from within
+.I open
+or
+.I visual,
+the current line is initially the first line of the file.
+.FE
+.LC
+\fBe!\fR \fIfile\fR
+.ZP
+The variant form suppresses the complaint about modifications having
+been made and not written from the editor buffer, thus
+discarding all changes which have been made before editing the new file.
+.LC
+\fBe\fR \fB+\fIn\fR \fIfile\fR
+.ZP
+Causes the editor to begin at line
+.I n
+rather than at the last line;
+\fIn\fR may also be an editor command containing no spaces, e.g.: ``+/pat''.
+.LC
+\fBfile\fR abbr: \fBf\fR
+.ZP
+Prints the current file name,
+whether it has been `[Modified]' since the last
+.I write
+command,
+whether it is
+.I "read only" ,
+the current line,
+the number of lines in the buffer,
+and the percentage of the way through the buffer of the current line.*
+.FS
+* In the rare case that the current file is `[Not edited]' this is
+noted also; in this case you have to use the form \fBw!\fR to write to
+the file, since the editor is not sure that a \fBwrite\fR will not
+destroy a file unrelated to the current contents of the buffer.
+.FE
+.LC
+\fBfile\fR \fIfile\fR
+.ZP
+The current file name is changed to
+.I file
+which is considered
+`[Not edited]'.
+.LC
+( 1 , $ ) \fBglobal\fR /\fIpat\|\fR/ \fIcmds\fR abbr: \fBg\fR
+.ZP
+First marks each line among those specified which matches
+the given regular expression.
+Then the given command list is executed with `\fB.\fR' initially
+set to each marked line.
+.IP
+The command list consists of the remaining commands on the current
+input line and may continue to multiple lines by ending all but the
+last such line with a `\e'.
+If
+.I cmds
+(and possibly the trailing \fB/\fR delimiter) is omitted, each line matching
+.I pat
+is printed.
+.I Append,
+.I insert,
+and
+.I change
+commands and associated input are permitted;
+the `\fB.\fR' terminating input may be omitted if it would be on the
+last line of the command list.
+.I Open
+and
+.I visual
+commands are permitted in the command list and take input from the terminal.
+.IP
+The
+.I global
+command itself may not appear in
+.I cmds.
+The
+.I undo
+command is also not permitted there,
+as
+.I undo
+instead can be used to reverse the entire
+.I global
+command.
+The options
+.I autoprint
+and
+.I autoindent
+are inhibited during a
+.I global,
+(and possibly the trailing \fB/\fR delimiter) and the value of the
+.I report
+option is temporarily infinite,
+in deference to a \fIreport\fR for the entire global.
+Finally, the context mark `\'\'' is set to the value of
+`.' before the global command begins and is not changed during a global
+command,
+except perhaps by an
+.I open
+or
+.I visual
+within the
+.I global.
+.LC
+\fBg!\fR \fB/\fIpat\fB/\fR \fIcmds\fR abbr: \fBv\fR
+.IP
+The variant form of \fIglobal\fR runs \fIcmds\fR at each line not matching
+\fIpat\fR.
+.LC
+( \fB.\fR )\|\fBinsert\fR abbr: \fBi\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+Places the given text before the specified line.
+The current line is left at the last line input;
+if there were none input it is left at the line before the addressed line.
+This command differs from
+.I append
+only in the placement of text.
+.KS
+.LC
+\fBi!\fR
+.br
+\fItext\fR
+.br
+\&\fB.\fR
+.ZP
+The variant toggles
+.I autoindent
+during the
+.I insert.
+.KE
+.LC
+( \fB.\fR , \fB.\fR+1 ) \fBjoin\fR \fIcount\fR \fIflags\fR abbr: \fBj\fR
+.ZP
+Places the text from a specified range of lines
+together on one line.
+White space is adjusted at each junction to provide at least
+one blank character, two if there was a `\fB.\fR' at the end of the line,
+or none if the first following character is a `)'.
+If there is already white space at the end of the line,
+then the white space at the start of the next line will be discarded.
+.LC
+\fBj!\fR
+.ZP
+The variant causes a simpler
+.I join
+with no white space processing; the characters in the lines are simply
+concatenated.
+.LC
+( \fB.\fR ) \fBk\fR \fIx\fR
+.ZP
+The
+.I k
+command is a synonym for
+.I mark.
+It does not require a blank or tab before the following letter.
+.LC
+( \fB.\fR , \fB.\fR ) \fBlist\fR \fIcount\fR \fIflags\fR
+.ZP
+Prints the specified lines in a more unambiguous way:
+tabs are printed as `^I'
+and the end of each line is marked with a trailing `$'.
+The current line is left at the last line printed.
+.LC
+\fBmap\fR \fIlhs\fR \fIrhs\fR
+.ZP
+The
+.I map
+command is used to define macros for use in
+.I visual
+mode.
+.I Lhs
+should be a single character, or the sequence ``#n'', for n a digit,
+referring to function key \fIn\fR. When this character or function key
+is typed in
+.I visual
+mode, it will be as though the corresponding \fIrhs\fR had been typed.
+On terminals without function keys, you can type ``#n''.
+See section 6.9 of the ``Introduction to Display Editing with Vi''
+for more details.
+.LC
+( \fB.\fR ) \fBmark\fR \fIx\fR
+.ZP
+Gives the specified line mark
+.I x,
+a single lower case letter.
+The
+.I x
+must be preceded by a blank or a tab.
+The addressing form `\'x' then addresses this line.
+The current line is not affected by this command.
+.LC
+( \fB.\fR , \fB.\fR ) \fBmove\fR \fIaddr\fR abbr: \fBm\fR
+.ZP
+The
+.I move
+command repositions the specified lines to be after
+.I addr .
+The first of the moved lines becomes the current line.
+.LC
+\fBnext\fR abbr: \fBn\fR
+.ZP
+The next file from the command line argument list is edited.
+.LC
+\fBn!\fR
+.ZP
+The variant suppresses warnings about the modifications to the buffer not
+having been written out, discarding (irretrievably) any changes which may
+have been made.
+.LC
+\fBn\fR \fIfilelist\fR
+.br
+\fBn\fR \fB+\fIcommand\fR \fIfilelist\fR
+.ZP
+The specified
+.I filelist
+is expanded and the resulting list replaces the
+current argument list;
+the first file in the new list is then edited.
+If
+.I command
+is given (it must contain no spaces), then it is executed after editing the first such file.
+.LC
+( \fB.\fR , \fB.\fR ) \fBnumber\fR \fIcount\fR \fIflags\fR abbr: \fB#\fR or \fBnu\fR
+.ZP
+Prints each specified line preceded by its buffer line
+number.
+The current line is left at the last line printed.
+.KS
+.LC
+( \fB.\fR ) \fBopen\fR \fIflags\fR abbr: \fBo\fR
+.br
+( \fB.\fR ) \fBopen\fR /\fIpat\|\fR/ \fIflags\fR
+.ZP
+Enters intraline editing \fIopen\fR mode at each addressed line.
+If
+.I pat
+is given,
+then the cursor will be placed initially at the beginning of the
+string matched by the pattern.
+To exit this mode use Q.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.KE
+.LC
+\fBpreserve\fR
+.ZP
+The current editor buffer is saved as though the system had just crashed.
+This command is for use only in emergencies when a
+.I write
+command has resulted in an error and you don't know how to save your work.
+After a
+.I preserve
+you should seek help.
+.LC
+( \fB.\fR , \fB.\fR )\|\fBprint\fR \fIcount\fR abbr: \fBp\fR or \fBP\fR
+.ZP
+Prints the specified lines
+with non-printing characters printed as control characters `^\fIx\fR\|';
+delete (octal 177) is represented as `^?'.
+The current line is left at the last line printed.
+.LC
+( \fB.\fR )\|\fBput\fR \fIbuffer\fR abbr: \fBpu\fR
+.ZP
+Puts back
+previously
+.I deleted
+or
+.I yanked
+lines.
+Normally used with
+.I delete
+to effect movement of lines,
+or with
+.I yank
+to effect duplication of lines.
+If no
+.I buffer
+is specified, then the last
+.I deleted
+or
+.I yanked
+text is restored.*
+.FS
+* But no modifying commands may intervene between the
+.I delete
+or
+.I yank
+and the
+.I put,
+nor may lines be moved between files without using a named buffer.
+.FE
+By using a named buffer, text may be restored that was saved there at any
+previous time.
+.LC
+\fBquit\fR abbr: \fBq\fR
+.ZP
+Causes
+.I ex
+to terminate.
+No automatic write of the editor buffer to a file is performed.
+However,
+.I ex
+issues a warning message if the file has changed
+since the last
+.I write
+command was issued, and does not
+.I quit.\(dg
+.FS
+\(dg \fIEx\fR
+will also issue a diagnostic if there are more files in the argument
+list.
+.FE
+Normally, you will wish to save your changes, and you
+should give a \fIwrite\fR command;
+if you wish to discard them, use the \fBq!\fR command variant.
+.LC
+\fBq!\fR
+.ZP
+Quits from the editor, discarding changes to the buffer without complaint.
+.LC
+( \fB.\fR ) \fBread\fR \fIfile\fR abbr: \fBr\fR
+.ZP
+Places a copy of the text of the given file in the
+editing buffer after the specified line.
+If no
+.I file
+is given the current file name is used.
+The current file name is not changed unless there is none in which
+case
+.I file
+becomes the current name.
+The sensibility restrictions for the
+.I edit
+command apply here also.
+If the file buffer is empty and there is no current name then
+.I ex
+treats this as an
+.I edit
+command.
+.IP
+Address `0' is legal for this command and causes the file to be read at
+the beginning of the buffer.
+Statistics are given as for the
+.I edit
+command when the
+.I read
+successfully terminates.
+After a
+.I read
+the current line is the last line read.\(dd
+.FS
+\(dd Within
+.I open
+and
+.I visual
+the current line is set to the first line read rather than the last.
+.FE
+.LC
+( \fB.\fR ) \fBread\fR \fB!\fR\fIcommand\fR
+.ZP
+Reads the output of the command
+.I command
+into the buffer after the specified line.
+This is not a variant form of the command, rather a read
+specifying a
+.I command
+rather than a
+.I filename;
+a blank or tab before the \fB!\fR is mandatory.
+.LC
+\fBrecover \fIfile\fR
+.ZP
+Recovers
+.I file
+from the system save area.
+Used after a accidental hangup of the phone**
+.FS
+** The system saves a copy of the file you were editing only if you
+have made changes to the file.
+.FE
+or a system crash** or
+.I preserve
+command.
+Except when you use
+.I preserve
+you will be notified by mail when a file is saved.
+.LC
+\fBrewind\fR abbr: \fBrew\fR
+.ZP
+The argument list is rewound, and the first file in the list is edited.
+.LC
+\fBrew!\fR
+.ZP
+Rewinds the argument list discarding any changes made to the current buffer.
+.LC
+\fBset\fR \fIparameter\fR
+.ZP
+With no arguments, prints those options whose values have been
+changed from their defaults;
+with parameter
+.I all
+it prints all of the option values.
+.IP
+Giving an option name followed by a `?'
+causes the current value of that option to be printed.
+The `?' is unnecessary unless the option is Boolean valued.
+Boolean options are given values either by the form
+`set \fIoption\fR' to turn them on or
+`set no\fIoption\fR' to turn them off;
+string and numeric options are assigned via the form
+`set \fIoption\fR=value'.
+.IP
+More than one parameter may be given to
+.I set \|;
+they are interpreted left-to-right.
+.LC
+\fBshell\fR abbr: \fBsh\fR
+.IP
+A new shell is created.
+When it terminates, editing resumes.
+.LC
+\fBsource\fR \fIfile\fR abbr: \fBso\fR
+.IP
+Reads and executes commands from the specified file.
+.I Source
+commands may be nested.
+.LC
+( \fB.\fR , \fB.\fR ) \fBsubstitute\fR /\fIpat\fR\|/\fIrepl\fR\|/ \fIoptions\fR \fIcount\fR \fIflags\fR abbr: \fBs\fR
+.IP
+On each specified line, the first instance of pattern
+.I pat
+is replaced by replacement pattern
+.I repl.
+If the
+.I global
+indicator option character `g'
+appears, then all instances are substituted;
+if the
+.I confirm
+indication character `c' appears,
+then before each substitution the line to be substituted
+is typed with the string to be substituted marked
+with `\(ua' characters.
+By typing an `y' one can cause the substitution to be performed,
+any other input causes no change to take place.
+After a
+.I substitute
+the current line is the last line substituted.
+.IP
+Lines may be split by substituting
+new-line characters into them.
+The newline in
+.I repl
+must be escaped by preceding it with a `\e'.
+Other metacharacters available in
+.I pat
+and
+.I repl
+are described below.
+.LC
+.B stop
+.ZP
+Suspends the editor, returning control to the top level shell.
+If
+.I autowrite
+is set and there are unsaved changes,
+a write is done first unless the form
+.B stop !
+is used.
+This commands is only available where supported by the teletype driver
+and operating system.
+.LC
+( \fB.\fR , \fB.\fR ) \fBsubstitute\fR \fIoptions\fR \fIcount\fR \fIflags\fR abbr: \fBs\fR
+.ZP
+If
+.I pat
+and
+.I repl
+are omitted, then the last substitution is repeated.
+This is a synonym for the
+.B &
+command.
+.LC
+( \fB.\fR , \fB.\fR ) \fBt\fR \fIaddr\fR \fIflags\fR
+.ZP
+The
+.I t
+command is a synonym for
+.I copy .
+.LC
+\fBta\fR \fItag\fR
+.ZP
+The focus of editing switches to the location of
+.I tag,
+switching to a different line in the current file where it is defined,
+or if necessary to another file.\(dd
+.FS
+\(dd If you have modified the current file before giving a
+.I tag
+command, you must write it out; giving another
+.I tag
+command, specifying no
+.I tag
+will reuse the previous tag.
+.FE
+.IP
+The tags file is normally created by a program such as
+.I ctags,
+and consists of a number of lines with three fields separated by blanks
+or tabs. The first field gives the name of the tag,
+the second the name of the file where the tag resides, and the third
+gives an addressing form which can be used by the editor to find the tag;
+this field is usually a contextual scan using `/\fIpat\fR/' to be immune
+to minor changes in the file. Such scans are always performed as if
+.I nomagic
+was set.
+.PP
+The tag names in the tags file must be sorted alphabetically.
+.LC
+\fBunabbreviate\fR \fIword\fP abbr: \fBuna\fP
+.ZP
+Delete
+.I word
+from the list of abbreviations.
+.LC
+\fBundo\fR abbr: \fBu\fR
+.ZP
+Reverses the changes made in the buffer by the last
+buffer editing command.
+Note that
+.I global
+commands are considered a single command for the purpose of
+.I undo
+(as are
+.I open
+and
+.I visual.)
+Also, the commands
+.I write
+and
+.I edit
+which interact with the
+file system cannot be undone.
+.I Undo
+is its own inverse.
+.IP
+.I Undo
+always marks the previous value of the current line `\fB.\fR'
+as `\'\''.
+After an
+.I undo
+the current line is the first line restored
+or the line before the first line deleted if no lines were restored.
+For commands with more global effect
+such as
+.I global
+and
+.I visual
+the current line regains it's pre-command value after an
+.I undo.
+.LC
+\fBunmap\fR \fIlhs\fR
+.ZP
+The macro expansion associated by
+.I map
+for
+.I lhs
+is removed.
+.LC
+( 1 , $ ) \fBv\fR /\fIpat\fR\|/ \fIcmds\fR
+.ZP
+A synonym for the
+.I global
+command variant \fBg!\fR, running the specified \fIcmds\fR on each
+line which does not match \fIpat\fR.
+.LC
+\fBversion\fR abbr: \fBve\fR
+.ZP
+Prints the current version number of the editor
+as well as the date the editor was last changed.
+.LC
+( \fB.\fR ) \fBvisual\fR \fItype\fR \fIcount\fR \fIflags\fR abbr: \fBvi\fR
+.ZP
+Enters visual mode at the specified line.
+.I Type
+is optional and may be `\-' , `\(ua' or `\fB.\fR'
+as in the
+.I z
+command to specify the placement of the specified line on the screen.
+By default, if
+.I type
+is omitted, the specified line is placed as the first on the screen.
+A
+.I count
+specifies an initial window size; the default is the value of the option
+.I window.
+See the document
+.I "An Introduction to Display Editing with Vi"
+for more details.
+To exit this mode, type Q.
+.LC
+\fBvisual\fP file
+.br
+\fBvisual\fP +\fIn\fP file
+.ZP
+From visual mode,
+this command is the same as edit.
+.LC
+( 1 , $ ) \fBwrite\fR \fIfile\fR abbr: \fBw\fR
+.ZP
+Writes changes made back to \fIfile\fR, printing the number of lines and
+characters written.
+Normally \fIfile\fR is omitted and the text goes back where it came from.
+If a \fIfile\fR is specified, then text will be written to that file.*
+.FS
+* The editor writes to a file only if it is
+the current file and is
+.I edited ,
+if the file does not exist,
+or if the file is actually a teletype,
+.I /dev/tty,
+.I /dev/null.
+Otherwise, you must give the variant form \fBw!\fR to force the write.
+.FE
+If the file does not exist it is created.
+The current file name is changed only if there is no current file
+name; the current line is never changed.
+.IP
+If an error occurs while writing the current and
+.I edited
+file, the editor
+considers that there has been ``No write since last change''
+even if the buffer had not previously been modified.
+.LC
+( 1 , $ ) \fBwrite>>\fR \fIfile\fR abbr: \fBw>>\fR
+.ZP
+Writes the buffer contents at the end of
+an existing file.
+.IP
+.LC
+\fBw!\fR \fIname\fR
+.ZP
+Overrides the checking of the normal \fIwrite\fR command,
+and will write to any file which the system permits.
+.LC
+( 1 , $ ) \fBw\fR \fB!\fR\fIcommand\fR
+.ZP
+Writes the specified lines into
+.I command.
+Note the difference between \fBw!\fR which overrides checks and
+\fBw\ \ !\fR which writes to a command.
+.LC
+\fBwq\fR \fIname\fR
+.ZP
+Like a \fIwrite\fR and then a \fIquit\fR command.
+.LC
+\fBwq!\fR \fIname\fR
+.ZP
+The variant overrides checking on the sensibility of the
+.I write
+command, as \fBw!\fR does.
+.LC
+\fBxit\fP \fIname\fR
+.ZP
+If any changes have been made and not written, writes the buffer out.
+Then, in any case, quits.
+.LC
+( \fB.\fR , \fB.\fR )\|\fByank\fR \fIbuffer\fR \fIcount\fR abbr: \fBya\fR
+.ZP
+Places the specified lines in the named
+.I buffer,
+for later retrieval via
+.I put.
+If no buffer name is specified, the lines go to a more volatile place;
+see the \fIput\fR command description.
+.LC
+( \fB.+1\fR ) \fBz\fR \fIcount\fR
+.ZP
+Print the next \fIcount\fR lines, default \fIwindow\fR.
+.LC
+( \fB.\fR ) \fBz\fR \fItype\fR \fIcount\fR
+.ZP
+Prints a window of text with the specified line at the top.
+If \fItype\fR is `\-' the line is placed at the bottom; a `\fB.\fR' causes
+the line to be placed in the center.*
+A count gives the number of lines to be displayed rather than
+double the number specified by the \fIscroll\fR option.
+On a \s-2CRT\s0 the screen is cleared before display begins unless a
+count which is less than the screen size is given.
+The current line is left at the last line printed.
+.FS
+* Forms `z=' and `z\(ua' also exist; `z=' places the current line in the
+center, surrounds it with lines of `\-' characters and leaves the current
+line at this line. The form `z\(ua' prints the window before `z\-'
+would. The characters `+', `\(ua' and `\-' may be repeated for cumulative
+effect.
+On some v2 editors, no
+.I type
+may be given.
+.FE
+.LC
+\fB!\fR \fIcommand\fR\fR
+.ZP
+The remainder of the line after the `!' character is sent to a shell
+to be executed.
+Within the text of
+.I command
+the characters
+`%' and `#' are expanded as in filenames and the character
+`!' is replaced with the text of the previous command.
+Thus, in particular,
+`!!' repeats the last such shell escape.
+If any such expansion is performed, the expanded line will be echoed.
+The current line is unchanged by this command.
+.IP
+If there has been ``[No\ write]'' of the buffer contents since the last
+change to the editing buffer, then a diagnostic will be printed
+before the command is executed as a warning.
+A single `!' is printed when the command completes.
+.LC
+( \fIaddr\fR , \fIaddr\fR ) \fB!\fR \fIcommand\fR\fR
+.ZP
+Takes the specified address range and supplies it as
+standard input to
+.I command;
+the resulting output then replaces the input lines.
+.LC
+( $ ) \fB=\fR
+.ZP
+Prints the line number of the
+addressed line.
+The current line is unchanged.
+.KS
+.LC
+( \fB.\fR , \fB.\fR ) \fB>\fR \fIcount\fR \fIflags\fR
+.br
+( \fB.\fR , \fB.\fR ) \fB<\fR \fIcount\fR \fIflags\fR
+.IP
+Perform intelligent shifting on the specified lines;
+\fB<\fR shifts left and \fB>\fR shift right.
+The quantity of shift is determined by the
+.I shiftwidth
+option and the repetition of the specification character.
+Only white space (blanks and tabs) is shifted;
+no non-white characters are discarded in a left-shift.
+The current line becomes the last line which changed due to the
+shifting.
+.KE
+.LC
+\fB^D\fR
+.ZP
+An end-of-file from a terminal input scrolls through the file.
+The
+.I scroll
+option specifies the size of the scroll, normally a half screen of text.
+.LC
+( \fB.\fR+1 , \fB.\fR+1 )
+.br
+( \fB.\fR+1 , \fB.\fR+1 ) |
+.ZP
+An address alone causes the addressed lines to be printed.
+A blank line prints the next line in the file.
+.LC
+( \fB.\fR , \fB.\fR ) \fB&\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+.ZP
+Repeats the previous
+.I substitute
+command.
+.LC
+( \fB.\fR , \fB.\fR ) \fB\s+2~\s0\fR \fIoptions\fR \fIcount\fR \fIflags\fR
+.ZP
+Replaces the previous regular expression with the previous
+replacement pattern from a substitution.
+.NH 1
+Regular expressions and substitute replacement patterns
+.NH 2
+Regular expressions
+.PP
+A regular expression specifies a set of strings of characters.
+A member of this set of strings is said to be
+.I matched
+by the regular expression.
+.I Ex
+remembers two previous regular expressions:
+the previous regular expression used in a
+.I substitute
+command
+and the previous regular expression used elsewhere
+(referred to as the previous \fIscanning\fR regular expression.)
+The previous regular expression
+can always be referred to by a null \fIre\fR, e.g. `//' or `??'.
+.NH 2
+Magic and nomagic
+.PP
+The regular expressions allowed by
+.I ex
+are constructed in one of two ways depending on the setting of
+the
+.I magic
+option.
+The
+.I ex
+and
+.I vi
+default setting of
+.I magic
+gives quick access to a powerful set of regular expression
+metacharacters.
+The disadvantage of
+.I magic
+is that the user must remember that these metacharacters are
+.I magic
+and precede them with the character `\e'
+to use them as ``ordinary'' characters.
+With
+.I nomagic,
+the default for
+.I edit,
+regular expressions are much simpler,
+there being only two metacharacters.
+The power of the other metacharacters is still available by preceding
+the (now) ordinary character with a `\e'.
+Note that `\e' is thus always a metacharacter.
+.PP
+The remainder of the discussion of regular expressions assumes
+that
+that the setting of this option is
+.I magic.\(dg
+.FS
+\(dg To discern what is true with
+.I nomagic
+it suffices to remember that the only
+special characters in this case will be `\(ua' at the beginning
+of a regular expression,
+`$' at the end of a regular expression,
+and `\e'.
+With
+.I nomagic
+the characters `\s+2~\s0' and `&' also lose their special meanings
+related to the replacement pattern of a substitute.
+.FE
+.NH 2
+Basic regular expression summary
+.PP
+The following basic constructs are used to construct
+.I magic
+mode regular expressions.
+.IP \fIchar\fR 15
+An ordinary character matches itself.
+The characters `\(ua' at the beginning of a line,
+`$' at the end of line,
+`*' as any character other than the first,
+`.', `\e', `[', and `\s+2~\s0' are not ordinary characters and
+must be escaped (preceded) by `\e' to be treated as such.
+.IP \fB\(ua\fR
+At the beginning of a pattern
+forces the match to succeed only at the beginning of a line.
+.IP \fB$\fR
+At the end of a regular expression forces the match to
+succeed only at the end of the line.
+.IP \&\fB.\fR
+Matches any single character except
+the new-line character.
+.IP \fB\e<\fR
+Forces the match
+to occur only at the beginning of a ``variable'' or ``word'';
+that is, either at the beginning of a line, or just before
+a letter, digit, or underline and after a character not one of
+these.
+.IP \fB\e>\fR
+Similar to `\e<', but matching the end of a ``variable''
+or ``word'', i.e. either the end of the line or before character
+which is neither a letter, nor a digit, nor the underline character.
+.IP \fB[\fIstring\fR]\fR
+Matches any (single) character in the class defined by
+.I string.
+Most characters in
+.I string
+define themselves.
+A pair of characters separated by `\-' in
+.I string
+defines the set of characters collating between the specified lower and upper
+bounds, thus `[a\-z]' as a regular expression matches
+any (single) lower-case letter.
+If the first character of
+.I string
+is an `\(ua' then the construct
+matches those characters which it otherwise would not;
+thus `[\(uaa\-z]' matches anything but a lower-case letter (and of course a
+newline).
+To place any of the characters
+`\(ua', `[', or `\-' in
+.I string
+you must escape them with a preceding `\e'.
+.NH 2
+Combining regular expression primitives
+.PP
+The concatenation of two regular expressions matches the leftmost and
+then longest string
+which can be divided with the first piece matching the first regular
+expression and the second piece matching the second.
+Any of the (single character matching) regular expressions mentioned
+above may be followed by the character `*' to form a regular expression
+which matches any number of adjacent occurrences (including 0) of characters
+matched by the regular expression it follows.
+.PP
+The character `\s+2~\s0' may be used in a regular expression,
+and matches the text which defined the replacement part
+of the last
+.I substitute
+command.
+A regular expression may be enclosed between the sequences
+`\e(' and `\e)' with side effects in the
+.I substitute
+replacement patterns.
+.NH 2
+Substitute replacement patterns
+.PP
+The basic metacharacters for the replacement pattern are
+`&' and `~'; these are
+given as `\e&' and `\e~' when
+.I nomagic
+is set.
+Each instance of `&' is replaced by the characters
+which the regular expression matched.
+The metacharacter `~' stands, in the replacement pattern,
+for the defining text of the previous replacement pattern.
+.PP
+Other metasequences possible in the replacement pattern
+are always introduced by the escaping character `\e'.
+The sequence `\e\fIn\fR' is replaced by the text matched
+by the \fIn\fR-th regular subexpression enclosed between
+`\e(' and `\e)'.\(dg
+.FS
+\(dg When nested, parenthesized subexpressions are present,
+\fIn\fR is determined by counting occurrences of `\e(' starting from the left.
+.FE
+The sequences `\eu' and `\el' cause the immediately following character in
+the replacement to be converted to upper- or lower-case respectively
+if this character is a letter.
+The sequences `\eU' and `\eL' turn such conversion on, either until
+`\eE' or `\ee' is encountered, or until the end of the replacement pattern.
+.de LC
+.br
+.sp .1i
+.ne 4
+.LP
+.ta 3i
+..
+.NH 1
+Option descriptions
+.PP
+.LC
+\fBautoindent\fR, \fBai\fR default: noai
+.ZP
+Can be used to ease the preparation of structured program text.
+At the beginning of each
+.I append ,
+.I change
+or
+.I insert
+command
+or when a new line is
+.I opened
+or created by an
+.I append ,
+.I change ,
+.I insert ,
+or
+.I substitute
+operation within
+.I open
+or
+.I visual
+mode,
+.I ex
+looks at the line being appended after,
+the first line changed
+or the line inserted before and calculates the amount of white space
+at the start of the line.
+It then aligns the cursor at the level of indentation so determined.
+.IP
+If the user then types lines of text in,
+they will continue to be justified at the displayed indenting level.
+If more white space is typed at the beginning of a line,
+the following line will start aligned with the first non-white character
+of the previous line.
+To back the cursor up to the preceding tab stop one can hit
+\fB^D\fR.
+The tab stops going backwards are defined at multiples of the
+.I shiftwidth
+option.
+You
+.I cannot
+backspace over the indent,
+except by sending an end-of-file with a \fB^D\fR.
+.IP
+Specially processed in this mode is a line with no characters added
+to it, which turns into a completely blank line (the white
+space provided for the
+.I autoindent
+is discarded.)
+Also specially processed in this mode are lines beginning with
+an `\(ua' and immediately followed by a \fB^D\fR.
+This causes the input to be repositioned at the beginning of the line,
+but retaining the previous indent for the next line.
+Similarly, a `0' followed by a \fB^D\fR
+repositions at the beginning but without
+retaining the previous indent.
+.IP
+.I Autoindent
+doesn't happen in
+.I global
+commands or when the input is not a terminal.
+.LC
+\fBautoprint\fR, \fBap\fR default: ap
+.ZP
+Causes the current line to be printed after each
+.I delete ,
+.I copy ,
+.I join ,
+.I move ,
+.I substitute ,
+.I t ,
+.I undo
+or
+shift command.
+This has the same effect as supplying a trailing `p'
+to each such command.
+.I Autoprint
+is suppressed in globals,
+and only applies to the last of many commands on a line.
+.LC
+\fBautowrite\fR, \fBaw\fR default: noaw
+.ZP
+Causes the contents of the buffer to be written to the current file
+if you have modified it and give a
+.I next,
+.I rewind,
+.I stop,
+.I tag,
+or
+.I !
+command, or a \fB^\(ua\fR (switch files) or \fB^]\fR (tag goto) command
+in
+.I visual.
+Note, that the
+.I edit
+and
+.I ex
+commands do
+.B not
+autowrite.
+In each case, there is an equivalent way of switching when autowrite
+is set to avoid the
+.I autowrite
+(\fIedit\fR
+for
+.I next ,
+.I rewind!
+for .I rewind ,
+.I stop!
+for
+.I stop ,
+.I tag!
+for
+.I tag ,
+.I shell
+for
+.I ! ,
+and
+\fB:e\ #\fR and a \fB:ta!\fR command from within
+.I visual).
+.LC
+\fBbeautify\fR, \fBbf\fR default: nobeautify
+.ZP
+Causes all control characters except tab, newline and form-feed
+to be discarded from the input.
+A complaint is registered the first time a
+backspace character is discarded.
+.I Beautify
+does not apply to command input.
+.LC
+\fBdirectory\fR, \fBdir\fR default: dir=/tmp
+.ZP
+Specifies the directory in which
+.I ex
+places its buffer file.
+If this directory in not
+writable, then the editor will exit abruptly when it fails to be
+able to create its buffer there.
+.LC
+\fBedcompatible\fR default: noedcompatible
+.ZP
+Causes the presence of absence of
+.B g
+and
+.B c
+suffixes on substitute commands to be remembered, and to be toggled
+by repeating the suffices. The suffix
+.B r
+makes the substitution be as in the
+.I ~
+command, instead of like
+.I &.
+.LC
+\fBerrorbells\fR, \fBeb\fR default: noeb
+.ZP
+Error messages are preceded by a bell.*
+.FS
+* Bell ringing in
+.I open
+and
+.I visual
+on errors is not suppressed by setting
+.I noeb.
+.FE
+If possible the editor always places the error message in a standout mode of the
+terminal (such as inverse video) instead of ringing the bell.
+.LC
+\fBhardtabs\fR, \fBht\fR default: ht=8
+.ZP
+Gives the boundaries on which terminal hardware tabs are set (or
+on which the system expands tabs).
+.LC
+\fBignorecase\fR, \fBic\fR default: noic
+.ZP
+All upper case characters in the text are mapped to lower case in regular
+expression matching.
+In addition, all upper case characters in regular expressions are mapped
+to lower case except in character class specifications.
+.LC
+\fBlisp\fR default: nolisp
+.ZP
+\fIAutoindent\fR indents appropriately for
+.I lisp
+code, and the \fB( ) { } [[\fR and \fB]]\fR commands in
+.I open
+and
+.I visual
+are modified to have meaning for \fIlisp\fR.
+.LC
+\fBlist\fR default: nolist
+.ZP
+All printed lines will be displayed (more) unambiguously,
+showing tabs and end-of-lines as in the
+.I list
+command.
+.LC
+\fBmagic\fR default: magic for \fIex\fR and \fIvi\fR\(dg
+.FS
+\(dg \fINomagic\fR for \fIedit\fR.
+.FE
+.ZP
+If
+.I nomagic
+is set, the number of regular expression metacharacters is greatly reduced,
+with only `\(ua' and `$' having special effects.
+In addition the metacharacters
+`~'
+and
+`&'
+of the replacement pattern are treated as normal characters.
+All the normal metacharacters may be made
+.I magic
+when
+.I nomagic
+is set by preceding them with a `\e'.
+.LC
+\fBmesg\fR default: mesg
+.ZP
+Causes write permission to be turned off to the terminal
+while you are in visual mode, if
+.I nomesg
+is set.
+.LC
+\fBmodeline\fR default: nomodeline
+.ZP
+If
+.I modeline
+is set, then the first 5 lines and the last five lines of the file
+will be checked for ex command lines and the comands issued.
+To be recognized as a command line, the line must have the string
+.B ex:
+or
+.B vi:
+preceeded by a tab or a space. This string may be anywhere in the
+line and anything after the
+.I :
+is interpeted as editor commands. This option defaults to off because
+of unexpected behavior when editting files such as
+.I /etc/passwd.
+.LC
+\fBnumber, nu\fR default: nonumber
+.ZP
+Causes all output lines to be printed with their
+line numbers.
+In addition each input line will be prompted for by supplying the line number
+it will have.
+.LC
+\fBopen\fR default: open
+.ZP
+If \fInoopen\fR, the commands
+.I open
+and
+.I visual
+are not permitted.
+This is set for
+.I edit
+to prevent confusion resulting from accidental entry to
+open or visual mode.
+.LC
+\fBoptimize, opt\fR default: optimize
+.ZP
+Throughput of text is expedited by setting the terminal
+to not do automatic carriage returns
+when printing more than one (logical) line of output,
+greatly speeding output on terminals without addressable
+cursors when text with leading white space is printed.
+.LC
+\fBparagraphs,\ para\fR default: para=IPLPPPQPP\0LIbp
+.ZP
+Specifies the paragraphs for the \fB{\fR and \fB}\fR operations in
+.I open
+and
+.I visual.
+The pairs of characters in the option's value are the names
+of the macros which start paragraphs.
+.LC
+\fBprompt\fR default: prompt
+.ZP
+Command mode input is prompted for with a `:'.
+.LC
+\fBredraw\fR default: noredraw
+.ZP
+The editor simulates (using great amounts of output), an intelligent
+terminal on a dumb terminal (e.g. during insertions in
+.I visual
+the characters to the right of the cursor position are refreshed
+as each input character is typed.)
+Useful only at very high speed.
+.LC
+\fBremap\fP default: remap
+.ZP
+If on, macros are repeatedly tried until they are unchanged.
+For example, if
+.B o
+is mapped to
+.B O ,
+and
+.B O
+is mapped to
+.B I ,
+then if
+.I remap
+is set,
+.B o
+will map to
+.B I ,
+but if
+.I noremap
+is set, it will map to
+.B O .
+.LC
+\fBreport\fR default: report=5\(dg
+.FS
+\(dg 2 for \fIedit\fR.
+.FE
+.ZP
+Specifies a threshold for feedback from commands.
+Any command which modifies more than the specified number of lines
+will provide feedback as to the scope of its changes.
+For commands such as
+.I global ,
+.I open ,
+.I undo ,
+and
+.I visual
+which have potentially more far reaching scope,
+the net change in the number of lines in the buffer is
+presented at the end of the command, subject to this same threshold.
+Thus notification is suppressed during a
+.I global
+command on the individual commands performed.
+.LC
+\fBscroll\fR default: scroll=\(12 window
+.ZP
+Determines the number of logical lines scrolled when an end-of-file
+is received from a terminal input in command mode,
+and the number of lines printed by a command mode
+.I z
+command (double the value of
+.I scroll ).
+.LC
+\fBsections\fR default: sections=SHNHH\0HU
+.ZP
+Specifies the section macros for the \fB[[\fR and \fB]]\fR operations
+in
+.I open
+and
+.I visual.
+The pairs of characters in the options's value are the names
+of the macros which start paragraphs.
+.LC
+\fBshell\fR, \fBsh\fR default: sh=/bin/sh
+.ZP
+Gives the path name of the shell forked for
+the shell escape command `!', and by the
+.I shell
+command.
+The default is taken from SHELL in the environment, if present.
+.LC
+\fBshiftwidth\fR, \fBsw\fR default: sw=8
+.ZP
+Gives the width a software tab stop,
+used in reverse tabbing with \fB^D\fR when using
+.I autoindent
+to append text,
+and by the shift commands.
+.LC
+\fBshowmatch, sm\fR default: nosm
+.ZP
+In
+.I open
+and
+.I visual
+mode, when a \fB)\fR or \fB}\fR is typed, move the cursor to the matching
+\fB(\fR or \fB{\fR for one second if this matching character is on the
+screen. Extremely useful with
+.I lisp.
+.LC
+\fBslowopen, slow\fR terminal dependent
+.ZP
+Affects the display algorithm used in
+.I visual
+mode, holding off display updating during input of new text to improve
+throughput when the terminal in use is both slow and unintelligent.
+See
+.I "An Introduction to Display Editing with Vi"
+for more details.
+.LC
+\fBtabstop,\ ts\fR default: ts=8
+.ZP
+The editor expands tabs in the input file to be on
+.I tabstop
+boundaries for the purposes of display.
+.LC
+\fBtaglength,\ tl\fR default: tl=0
+.ZP
+Tags are not significant beyond this many characters.
+A value of zero (the default) means that all characters are significant.
+.LC
+\fBtags\fR default: tags=tags /usr/lib/tags
+.ZP
+A path of files to be used as tag files for the
+.I tag
+command.
+A requested tag is searched for in the specified files, sequentially.
+By default, files called
+.B tags
+are searched for in the current directory and in /usr/lib
+(a master file for the entire system).
+.LC
+\fBterm\fR from environment TERM
+.ZP
+The terminal type of the output device.
+.LC
+\fBterse\fR default: noterse
+.ZP
+Shorter error diagnostics are produced for the experienced user.
+.LC
+\fBwarn\fR default: warn
+.ZP
+Warn if there has been `[No write since last change]' before a `!'
+command escape.
+.LC
+\fBwindow\fR default: window=speed dependent
+.ZP
+The number of lines in a text window in the
+.I visual
+command.
+The default is 8 at slow speeds (600 baud or less),
+16 at medium speed (1200 baud),
+and the full screen (minus one line) at higher speeds.
+.LC
+\fBw300,\ w1200\, w9600\fR
+.ZP
+These are not true options but set
+.B window
+only if the speed is slow (300), medium (1200), or high (9600),
+respectively.
+They are suitable for an EXINIT
+and make it easy to change the 8/16/full screen rule.
+.LC
+\fBwrapscan\fR, \fBws\fR default: ws
+.ZP
+Searches using the regular expressions in addressing
+will wrap around past the end of the file.
+.LC
+\fBwrapmargin\fR, \fBwm\fR default: wm=0
+.ZP
+Defines a margin for automatic wrapover of text during input in
+.I open
+and
+.I visual
+modes. See
+.I "An Introduction to Text Editing with Vi"
+for details.
+.LC
+\fBwriteany\fR, \fBwa\fR default: nowa
+.IP
+Inhibit the checks normally made before
+.I write
+commands, allowing a write to any file which the system protection
+mechanism will allow.
+.NH 1
+Limitations
+.PP
+Editor limits that the user is likely to encounter are as follows:
+1024 characters per line,
+256 characters per global command list,
+128 characters per file name,
+128 characters in the previous inserted and deleted text in
+.I open
+or
+.I visual,
+100 characters in a shell escape command,
+63 characters in a string valued option,
+and 30 characters in a tag name, and
+a limit of 250000 lines in the file is silently enforced.
+.PP
+The
+.I visual
+implementation limits the number of macros defined with map to
+32, and the total number of characters in macros to be less than 512.
+.LP
+.LP
+.I Acknowledgments.
+Chuck Haley contributed greatly to the early development of
+.I ex.
+Bruce Englar encouraged the redesign which led to
+.I ex
+version 1.
+Bill Joy wrote versions 1 and 2.0 through 2.7,
+and created the framework that users see in the present editor.
+Mark Horton added macros and other features and made the
+editor work on a large number of terminals and Unix systems.
diff --git a/usr.bin/vi/USD.doc/exref/ex.summary b/usr.bin/vi/USD.doc/exref/ex.summary
new file mode 100644
index 0000000..618da07
--- /dev/null
+++ b/usr.bin/vi/USD.doc/exref/ex.summary
@@ -0,0 +1,734 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ex.summary 8.1 (Berkeley) 6/8/93
+.\"
+.ds p \v'-0.2'.\v'+0.2'
+.ds U \s-2UNIX\s+2
+.ds c \v'-0.2':\v'+0.2'
+.nr PO .25i
+.nr LL 6.75i
+.lt 6.75i
+.ll 6.75i
+.ds CH
+.ds LF Computing Services, U.C. Berkeley
+.ds RF April 3, 1979
+.de SP
+.sp 1v
+..
+.nr PI 3n
+.nr PD 0
+.ND
+.ps 12
+.ft B
+.ce 1
+Ex/Edit Command Summary (Version 2.0)
+.ft R
+.nr VS 11
+.nr PS 9
+.nr HM 0.5i
+.nr CW
+.2C
+.PP
+.I Ex
+and
+.I edit
+are text editors, used for creating
+and modifying files of text on the \*U
+computer system.
+.I Edit
+is a variant of
+.I ex
+with features designed to
+make it less complicated
+to learn and use.
+In terms of command syntax and effect
+the editors are essentially identical,
+and this command summary applies to both.
+.PP
+The summary is meant as a quick reference
+for users already acquainted
+with
+.I edit
+or \fIex\fP.
+Fuller explanations of the editors are available
+in the documents
+.I
+Edit: A Tutorial
+.R
+(a self-teaching introduction) and the
+.I
+Ex Reference Manual
+.R
+(the comprehensive reference source for
+both \fIedit\fP and \fIex\fP).
+Both of these writeups are available in the
+Computing Services Library.
+.PP
+In the examples included with the
+summary, commands and text entered by
+the user are printed in \fBboldface\fR to
+distinguish them from responses printed
+by the computer.
+.sp 0.45v
+.LP
+.B
+The Editor Buffer
+.PP
+In order to perform its tasks
+the editor sets aside a temporary
+work space,
+called a \fIbuffer\fR,
+separate from the user's permanent
+file.
+Before starting to work on an existing
+file the editor makes a copy of it in the
+buffer, leaving the original untouched.
+All editing changes are made to the
+buffer copy, which must then
+be written back to the permanent
+file in order to update the
+old version.
+The buffer disappears
+at the end of the editing session.
+.sp 0.45v
+.LP
+.B
+Editing: Command and Text Input Modes
+.PP
+.R
+During an editing session there are
+two usual modes of operation:
+\fIcommand\fP mode and \fItext input\fP
+mode.
+(This disregards, for the moment,
+.I open
+and
+.I visual
+modes, discussed below.)
+In command mode, the editor issues a
+colon prompt (:)
+to show that it is ready to
+accept and execute a command.
+In text input mode, on the other hand, there is
+no prompt and the editor merely accepts text to
+be added to the buffer.
+Text input mode is initiated by the commands
+\fIappend\fP, \fIinsert\fP, and \fIchange\fP,
+and is terminated by typing a period as the
+first and only character on a line.
+.sp 0.45v
+.LP
+.B
+Line Numbers and Command Syntax
+.PP
+.R
+The editor keeps track of lines of text
+in the buffer by numbering them consecutively
+starting with 1 and renumbering
+as lines are added or deleted.
+At any given time the editor is positioned
+at one of these lines; this position is
+called the \fIcurrent line\fP.
+Generally, commands that change the
+contents of the buffer print the
+new current line at the end of their
+execution.
+.PP
+Most commands can be preceded by one or two
+line-number addresses which indicate the lines
+to be affected.
+If one number is given the command operates on
+that line only; if two, on an inclusive range
+of lines.
+Commands that can take line-number prefixes also
+assume default prefixes if none are given.
+The default assumed by each command is designed
+to make it convenient to use in many instances
+without any line-number prefix.
+For the most part, a command used without a
+prefix operates on the current line,
+though exceptions to this rule should be noted.
+The \fIprint\fP command
+by itself, for instance, causes
+one line, the current line, to be
+printed at the terminal.
+.PP
+The summary shows the number of line addresses
+that can be
+prefixed to each command as well as
+the defaults assumed if they are omitted.
+For example,
+.I (.,.)
+means that up to 2 line-numbers may be given,
+and that if none is given the
+command operates on the current line.
+(In the address prefix notation, ``.'' stands
+for the current line and ``$'' stands for
+the last line of the buffer.)
+If no such notation appears, no
+line-number prefix may be used.
+.PP
+Some commands take trailing
+information;
+only
+the more important instances of this
+are mentioned in the summary.
+.sp 0.25v
+.LP
+.B
+Open and Visual Modes
+.PP
+.R
+Besides command and text input modes,
+.I ex
+and
+.I edit
+provide on some CRT terminals other modes of editing,
+.I open
+and
+.I visual .
+In these modes the cursor can
+be moved to individual words
+or characters in a line.
+The commands then given are very different
+from the standard editor commands; most do not appear on the screen when
+typed.
+.I
+An Introduction to Display Editing with Vi
+.R
+provides a full discussion.
+.sp 0.25v
+.LP
+.B
+Special Characters
+.PP
+.R
+.fi
+Some characters take on special meanings
+when used in context searches
+and in patterns given to the \fIsubstitute\fP command.
+For \fIedit\fR, these are ``^'' and ``$'',
+meaning the beginning and end of a line,
+respectively.
+.I Ex
+has the following additional special characters:
+.B
+.ce 1
+\&. & * [ ] ~
+.R
+To use one of the special characters as its
+simple graphic representation
+rather than with its special meaning,
+precede it by a backslash (\\).
+The backslash always has a special meaning.
+.1C
+.rm LF
+.rm RF
+.rm CF
+.nr FM 0.4
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+(.)\fBappend a T{
+Begins text input mode,
+adding lines to the buffer after
+the line specified. Appending continues
+until ``.'' is typed alone at the
+beginning of a new line, followed by
+a carriage return. \fI0a\fR places
+lines at the beginning of the buffer.
+T} T{
+.nf
+\fR:\fBa
+Three lines of text
+are added to the buffer
+after the current line.
+\*p
+.R
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBchange c T{
+Deletes indicated line(s) and
+initiates text input mode to
+replace them with new text which follows.
+New text is terminated the same way
+as with \fIappend\fR.
+T} T{
+.nf
+:\fB5,6c
+Lines 5 and 6 are
+deleted and replaced by
+these three lines.
+\*p
+.R
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBcopy \fIaddr co T{
+Places a copy of the specified lines
+after the line indicated by \fIaddr\fR.
+The example places a copy of lines 8 through
+12, inclusive, after line 25.
+T} T{
+.nf
+\fR:\fB8,12co 25
+\fRLast line copied is printed
+\fR\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBdelete d T{
+Removes lines from the buffer
+and prints the current line after the deletion.
+T} T{
+.nf
+\fR:\fB13,15d
+\fRNew current line is printed
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+T{
+\fBedit \fIfile\fP
+.br
+\fBedit! \fIfile\fP
+T} T{
+e
+.br
+e!
+T} T{
+.fi
+\fRClears the editor buffer and then
+copies into it the named \fIfile\fR,
+which becomes the current file.
+This is a way of shifting to a different
+file
+without leaving the editor.
+The editor issues a warning
+message if this command is used before
+saving changes
+made to the file already in the buffer;
+using the form \fBe!\fR overrides this protective mechanism.
+T} T{
+.nf
+\fR:\fBe ch10\fR
+No write since last change
+:\fBe! ch10\fR
+"ch10" 3 lines, 62 characters
+\*c
+.fi
+T}
+.SP
+\fBfile \fIname\fR f T{
+\fRIf followed by a \fIname\fR, renames
+the current file to \fIname\fR.
+If used without \fIname\fR, prints
+the name of the current file.
+T} T{
+.nf
+\fR:\fBf ch9
+\fR"ch9" [Modified] 3 lines ...
+:\fBf
+\fR"ch9" [Modified] 3 lines ...
+\*c
+.fi
+T}
+.SP
+(1,$)\fBglobal g \fBglobal/\fIpattern\fB/\fIcommands T{
+.nf
+:\fBg/nonsense/d
+\fR\*c
+.fi
+T}
+\fR(1,$)\fBglobal! g!\fR or \fBv T{
+Searches the entire buffer (unless a smaller
+range is specified by line-number prefixes) and
+executes \fIcommands\fR on every line with
+an expression matching \fIpattern\fR.
+The second form, abbreviated
+either \fBg!\fR or \fBv\fR,
+executes \fIcommands\fR on lines that \fIdo
+not\fR contain the expression \fIpattern\fR.
+T} \^
+.SP
+\fR(.)\fBinsert i T{
+Inserts new lines of text immediately before the specified line.
+Differs from
+.I append
+only in that text is placed before, rather than after, the indicated line.
+In other words, \fB1i\fR has the same effect as \fB0a\fR.
+T} T{
+.nf
+:\fB1i
+These lines of text will
+be added prior to line 1.
+\&.
+\fR:
+.fi
+T} \^
+.SP
+\fR(.,.+1)\fBjoin j T{
+Join lines together, adjusting white space (spaces
+and tabs) as necessary.
+T} T{
+.nf
+:\fB2,5j\fR
+Resulting line is printed
+:
+.fi
+T} \^
+.TE
+.bp
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+\fR(.,.)\fBlist l T{
+\fRPrints lines in a more
+unambiguous way than the \fIprint\fR
+command does. The end of a line,
+for example, is marked with a ``$'',
+and tabs printed as ``^I''.
+T} T{
+.nf
+:\fB9l
+\fRThis is line 9$
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fR(.,.)\fBmove \fIaddr\fB m T{
+\fRMoves the specified lines
+to a position after the line
+indicated by \fIaddr\fR.
+T} T{
+.nf
+\fR:\fB12,15m 25\fR
+New current line is printed
+\*c
+.fi
+T}
+.SP
+\fR(.,.)\fBnumber nu T{
+Prints each line preceded
+by its buffer line number.
+T} T{
+.nf
+\fR:\fBnu
+\0\0\fR10\0 This is line 10
+\*c
+.fi
+T}
+.SP
+\fR(.)\fBopen o T{
+Too involved to discuss here,
+but if you enter open mode
+accidentally, press
+the \s-2ESC\s0 key followed by
+\fBq\fR to
+get back into normal editor
+command mode.
+\fIEdit\fP is designed to
+prevent accidental use of
+the open command.
+T}
+.SP
+\fBpreserve pre T{
+Saves a copy of the current buffer contents as though the system had
+just crashed. This is for use in an emergency when a
+.I write
+command has failed and you don't know how else to save your work.\(dg
+T} T{
+.nf
+:\fBpreserve\fR
+File preserved.
+:
+.fi
+T}
+.SP
+\fR(.,.)\fBprint p Prints the text of line(s). T{
+.nf
+:\fB+2,+3p\fR
+The second and third lines
+after the current line
+:
+.fi
+T}
+.TE
+.FS
+\(dg Seek assistance from a consultant as soon as possible
+after saving a file with the
+.I preserve
+command, because the file is saved on system storage space for only one week.
+.FE
+.SP
+.nf
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+T{
+.nf
+\fBquit
+quit!
+.fi
+T} T{
+.nf
+q
+q!
+T} T{
+.fi
+\fREnds the editing session.
+You will receive a
+warning if you have changed the buffer
+since last writing its contents
+to the file. In this event you
+must either type \fBw\fR to write,
+or type \fBq!\fR to exit from
+the editor without saving your changes.
+T} T{
+.nf
+\fR:\fBq
+\fRNo write since last change
+:\fBq!
+\fR%
+.fi
+T}
+.SP
+\fR(.)\fBread \fIfile\fP r T{
+.fi
+\fRPlaces a copy of \fIfile\fR in the
+buffer after the specified line.
+Address 0 is permissible and causes
+the copy of \fIfile\fR to be placed
+at the beginning of the buffer.
+The \fIread\fP command does not
+erase any text already in the buffer.
+If no line number is specified,
+\fIfile\fR is placed after the
+current line.
+T} T{
+.nf
+\fR:\fB0r newfile
+\fR"newfile" 5 lines, 86 characters
+\*c
+.fi
+T}
+.SP
+\fBrecover \fIfile\fP rec T{
+.fi
+Retrieves a copy of the editor buffer
+after a system crash, editor crash,
+phone line disconnection, or
+\fIpreserve\fR command.
+T}
+.SP
+\fR(.,.)\fBsubstitute s T{
+.nf
+\fBsubstitute/\fIpattern\fB/\fIreplacement\fB/
+substitute/\fIpattern\fB/\fIreplacement\fB/gc
+.fi
+\fRReplaces the first occurrence of \fIpattern\fR
+on a line
+with \fIreplacement\fP.
+Including a \fBg\fR after the command
+changes all occurrences of \fIpattern\fP
+on the line.
+The \fBc\fR option allows the user to
+confirm each substitution before it is
+made; see the manual for details.
+T} T{
+.nf
+:\fB3p
+\fRLine 3 contains a misstake
+:\fBs/misstake/mistake/
+\fRLine 3 contains a mistake
+\*c
+.fi
+T}
+.TE
+.bp
+.TS
+cp10 cp10 cp10 cp10
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+Name Abbr Description Examples
+.sp 1.75
+\fBundo u T{
+.fi
+\fRReverses the changes made in
+the buffer by the last buffer-editing
+command.
+Note that this example contains
+a notification about the number of
+lines affected.
+T} T{
+.nf
+\fR:\fB1,15d
+\fR15 lines deleted
+new line number 1 is printed
+:\fBu
+\fR15 more lines in file ...
+old line number 1 is printed
+\*c
+.fi
+T}
+.SP
+\fR(1,$)\fBwrite \fIfile\fR w T{
+.fi
+\fRCopies data from the buffer onto
+a permanent file. If no \fIfile\fR
+is named, the current filename
+is used.
+The file is automatically created
+if it does not yet exist.
+A response containing the number of
+lines and characters in the file
+indicates that the write
+has been completed successfully.
+The editor's built-in protections
+against overwriting existing files
+will in some circumstances
+inhibit a write.
+The form \fBw!\fR forces the
+write, confirming that
+an existing file is to be overwritten.
+T} T{
+.nf
+\fR:\fBw
+\fR"file7" 64 lines, 1122 characters
+:\fBw file8
+\fR"file8" File exists ...
+:\fBw! file8
+\fR"file8" 64 lines, 1122 characters
+\*c
+.fi
+T}
+\fR(1,$)\fBwrite! \fIfile\fP w! \^ \^
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fR(.)\fBz \fIcount\fP z T{
+.fi
+\fRPrints a screen full of text starting
+with the line indicated;
+or, if \fIcount\fR is specified,
+prints that number of lines.
+Variants of the \fIz\fR command
+are described in the manual.
+T}
+.SP
+\fB!\fIcommand T{
+.fi
+Executes the remainder of the line
+after \fB!\fR as a \*U command.
+The buffer is unchanged by this, and
+control is returned to the editor when
+the execution of \fIcommand\fR is complete.
+T} T{
+.nf
+\fR:\fB!date
+\fRFri Jun 9 12:15:11 PDT 1978
+!
+\*c
+.fi
+T}
+.SP
+\fRcontrol-d T{
+.fi
+Prints the next \fIscroll\fR of text,
+normally half of a screen. See the
+manual for details of the \fIscroll\fR
+option.
+T}
+.SP
+\fR(.+1)<cr> T{
+.fi
+An address alone followed by a carriage
+return causes the line to be printed.
+A carriage return by itself prints the
+line following the current line.
+T} T{
+.nf
+:\fR<cr>
+the line after the current line
+\*c
+.fi
+T}
+.TE
+.sp 0.5v
+.TS
+ltw(1.0i) lt2w(0.40i)fB ltw(3.0i) ltw(1.8i).
+\fB/\fIpattern\fB/ T{
+.fi
+\fRSearches for the next line in which
+\fIpattern\fR occurs and prints it.
+T} T{
+.nf
+\fR:\fB/This pattern/
+\fRThis pattern next occurs here.
+\*c
+.fi
+T}
+.SP
+\fB// T{
+Repeats the most recent search.
+T} T{
+.nf
+\fR:\fB//
+\fRThis pattern also occurs here.
+\*c
+.fi
+T}
+.SP
+\fB?\fIpattern\fB? T{
+Searches in the reverse direction
+for \fIpattern\fP.
+T}
+.SP
+\fB?? T{
+Repeats the most recent search,
+moving in the reverse direction
+through the buffer.
+T}
+.TE
+
diff --git a/usr.bin/vi/USD.doc/vi.man/Makefile b/usr.bin/vi/USD.doc/vi.man/Makefile
new file mode 100644
index 0000000..fd89b8e
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.man/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.5 (Berkeley) 7/16/94
+
+SRCS= vi.1
+DOCS= vi.0 vi.0.ps
+
+all: ${DOCS}
+
+vi.0: vi.1
+ groff -man -Tascii < vi.1 > $@
+vi.0.ps: vi.1
+ groff -man < vi.1 > $@
+
+clean:
+ rm -f ${DOCS}
diff --git a/usr.bin/vi/USD.doc/vi.man/vi.0 b/usr.bin/vi/USD.doc/vi.man/vi.0
new file mode 100644
index 0000000..e6d9972
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.man/vi.0
@@ -0,0 +1,798 @@
+EX/VI(1) BSD Reference Manual EX/VI(1)
+
+NNAAMMEE
+ eexx,, vvii,, vviieeww - text editors
+
+SSYYNNOOPPSSIISS
+ eexx [--eeFFRRrrssvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
+ vvii [--eeFFRRrrvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
+ vviieeww [--eeFFRRrrvv] [--cc _c_m_d] [--tt _t_a_g] [--ww _s_i_z_e] [_f_i_l_e _._._.]
+
+DDEESSCCRRIIPPTTIIOONN
+ VVii is a screen oriented text editor. EExx is a line-oriented text editor.
+ EExx and vvii are different interfaces to the same program, and it is possi-
+ ble to switch back and forth during an edit session. VViieeww is the equiva-
+ lent of using the --RR (read-only) option of vvii.
+
+ This manual page is the one provided with the nneexx//nnvvii versions of the
+ eexx//vvii text editors. NNeexx//nnvvii are intended as bug-for-bug compatible re-
+ placements for the original Fourth Berkeley Software Distribution (4BSD)
+ eexx and vvii programs. For the rest of this manual page, nneexx//nnvvii is used
+ only when it's necessary to distinguish it from the historic implementa-
+ tions of eexx//vvii.
+
+ This manual page is intended for users already familiar with eexx//vvii. Any-
+ one else should almost certainly read a good tutorial on the editor be-
+ fore this manual page. If you're in an unfamiliar environment, and you
+ absolutely have to get work done immediately, read the section after the
+ options description, entitled ``Fast Startup''. It's probably enough to
+ get you going.
+
+ The following options are available:
+
+ --cc Execute _c_m_d immediately after starting the edit session. Partic-
+ ularly useful for initial positioning in the file, however _c_m_d is
+ not limited to positioning commands. This is the POSIX 1003.2
+ interface for the historic ``+cmd'' syntax. NNeexx//nnvvii supports
+ both the old and new syntax.
+
+ --ee Start editing in ex mode, as if the command name were eexx.
+
+ --FF Don't copy the entire file when first starting to edit. (The de-
+ fault is to make a copy in case someone else modifies the file
+ during your edit session.)
+
+ --RR Start editing in read-only mode, as if the command name was vviieeww,
+ or the readonly option was set.
+
+ --rr Recover the specified files, or, if no files are specified, list
+ the files that could be recovered. If no recoverable files by
+ the specified name exist, the file is edited as if the --rr option
+ had not been specified.
+
+ --ss Enter batch mode; applicable only to eexx edit sessions. Batch
+ mode is useful when running eexx scripts. Prompts, informative
+ messages and other user oriented message are turned off, and no
+ startup files or environmental variables are read. This is the
+ POSIX 1003.2 interface for the historic ``-'' argument. NNeexx//nnvvii
+ supports both the old and new syntax.
+
+ --tt Start editing at the specified tag. (See ctags(1)).
+
+ --ww Set the initial window size to the specified number of lines.
+
+
+
+ --vv Start editing in vi mode, as if the command name was vvii or vviieeww.
+
+ --XX Reserved for X11 interfaces. _N_o _X_1_1 _s_u_p_p_o_r_t _i_s _c_u_r_r_e_n_t_l_y
+ _i_m_p_l_e_m_e_n_t_e_d_.
+
+ Command input for eexx//vvii is read from the standard input. In the vvii in-
+ terface, it is an error if standard input is not a terminal. In the eexx
+ interface, if standard input is not a terminal, eexx will read commands
+ from it regardless, however, the session will be a batch mode session,
+ exactly as if the --ss option had been specified.
+
+ EExx//vvii exits 0 on success, and greater than 0 if an error occurs.
+
+FFAASSTT SSTTAARRTTUUPP
+ This section will tell you the minimum amount that you need to do simple
+ editing tasks using vvii. If you've never used any screen editor before,
+ you're likely to have problems even with this simple introduction. In
+ that case you should find someone that already knows vvii and have them
+ walk you through this section.
+
+ VVii is a screen editor. This means that it takes up almost the entire
+ screen, displaying part of the file on each screen line, except for the
+ last line of the screen. The last line of the screen is used for you to
+ give commands to vvii, and for vvii to give information to you.
+
+ The other fact that you need to understand is that vvii is a modeful edi-
+ tor, i.e. you are either entering text or you are executing commands, and
+ you have to be in the right mode to do one or the other. You will be in
+ command mode when you first start editing a file. There are commands
+ that switch you into input mode. There is only one key that takes you
+ out of input mode, and that is the <escape> key. (Key names are written
+ using less-than and greater-than signs, e.g. <escape> means the
+ ``escape'' key, usually labeled ``esc'' on your terminal's keyboard.) If
+ you're ever confused as to which mode you're in, keep entering the <es-
+ cape> key until vvii beeps at you. (Generally, vvii will beep at you if you
+ try and do something that's not allowed. It will also display error mes-
+ sages.)
+
+ To start editing a file, enter the command ``vi file_name<carriage-
+ return>''. The command you should enter as soon as you start editing is
+ ``:set verbose showmode<carriage-return>''. This will make the editor
+ give you verbose error messages and display the current mode at the bot-
+ tom of the screen.
+
+ The commands to move around the file are:
+ hh Move the cursor left one character.
+ jj Move the cursor down one line.
+ kk Move the cursor up one line.
+ ll Move the cursor right one character.
+ <<ccuurrssoorr--aarrrroowwss>>
+ The cursor arrow keys should work, too.
+ //tteexxtt<<ccaarrrriiaaggee--rreettuurrnn>>
+ Search for the string ``text'' in the file, and move the cursor to
+ its first character.
+
+ The commands to enter new text are:
+ aa Append new text, _a_f_t_e_r the cursor.
+ ii Insert new text, _b_e_f_o_r_e the cursor.
+ oo Open a new line below the line the cursor is on, and start entering
+ text.
+ OO Open a new line above the line the cursor is on, and start entering
+ text.
+ <<eessccaappee>>
+ Once you've entered input mode using the one of the aa, ii, OO, or oo
+ commands, use <<eessccaappee>> to quit entering text and return to command
+ mode.
+
+ The commands to copy text are:
+ yyyy Copy the line the cursor is on.
+ pp Append the copied line after the line the cursor is on.
+
+ The commands to delete text are:
+ dddd Delete the line the cursor is on.
+ xx Delete the character the cursor is on.
+
+ The commands to write the file are:
+ ::ww<<ccaarrrriiaaggee--rreettuurrnn>>
+ Write the file back to the file with the name that you originally
+ used as an argument on the vvii command line.
+ ::ww ffiillee__nnaammee<<ccaarrrriiaaggee--rreettuurrnn>>
+ Write the file back to the file with the name ``file_name''.
+
+ The commands to quit editing and exit the editor are:
+ ::qq<<ccaarrrriiaaggee--rreettuurrnn>>
+ Quit editing and leave vi (if you've modified the file, but not
+ saved your changes, vvii will refuse to quit).
+ ::qq!!<<ccaarrrriiaaggee--rreettuurrnn>>
+ Quit, discarding any modifications that you may have made.
+
+ One final caution. Unusual characters can take up more than one column
+ on the screen, and long lines can take up more than a single screen line.
+ The above commands work on ``physical'' characters and lines, i.e. they
+ affect the entire line no matter how many screen lines it takes up and
+ the entire character no matter how many screen columns it takes up.
+
+VVII CCOOMMMMAANNDDSS
+ The following section describes the commands available in the command
+ mode of the vvii editor. In each entry below, the tag line is a usage syn-
+ opsis for the command character.
+
+ [[ccoouunntt]] <<ccoonnttrrooll--AA>>
+ Search forward count times for the current word.
+ [[ccoouunntt]] <<ccoonnttrrooll--BB>>
+ Page backwards count screens.
+ [[ccoouunntt]] <<ccoonnttrrooll--DD>>
+ Scroll forward count lines.
+ [[ccoouunntt]] <<ccoonnttrrooll--EE>>
+ Scroll forward count lines, leaving the current line and column as
+ is, if possible.
+ [[ccoouunntt]] <<ccoonnttrrooll--FF>>
+ Page forward count screens.
+ <<ccoonnttrrooll--GG>>
+ Display the file information.
+ <<ccoonnttrrooll--HH>>
+ [[ccoouunntt]] hh
+ Move the cursor back count characters in the current line.
+ [[ccoouunntt]] <<ccoonnttrrooll--JJ>>
+ [[ccoouunntt]] <<ccoonnttrrooll--NN>>
+ [[ccoouunntt]] jj
+ Move the cursor down count lines without changing the current col-
+ umn.
+ <<ccoonnttrrooll--LL>>
+ <<ccoonnttrrooll--RR>>
+ Repaint the screen.
+ [[ccoouunntt]] <<ccoonnttrrooll--MM>>
+ [[ccoouunntt]] ++
+ Move the cursor down count lines to the first nonblank character of
+ that line.
+ [[ccoouunntt]] <<ccoonnttrrooll--PP>>
+ [[ccoouunntt]] kk
+ Move the cursor up count lines, without changing the current col-
+
+ umn.
+ <<ccoonnttrrooll--TT>>
+ Return to the most recent tag context.
+ <<ccoonnttrrooll--UU>>
+ Scroll backwards count lines.
+ <<ccoonnttrrooll--WW>>
+ Switch to the next lower screen in the window, or, to the first
+ screen if there are no lower screens in the window.
+ <<ccoonnttrrooll--YY>>
+ Scroll backwards count lines, leaving the current line and column
+ as is, if possible.
+ <<ccoonnttrrooll--ZZ>>
+ Suspend the current editor session.
+ <<eessccaappee>>
+ Execute eexx commands or cancel partial commands.
+ <<ccoonnttrrooll--]]>>
+ Push a tag reference onto the tag stack.
+ <<ccoonnttrrooll--^^>>
+ Switch to the most recently edited file.
+ [[ccoouunntt]] <<ssppaaccee>>
+ [[ccoouunntt]] ll
+ Move the cursor forward count characters without changing the cur-
+ rent line.
+ [[ccoouunntt]] !! mmoottiioonn sshheellll--aarrgguummeenntt((ss))
+ Replace text with results from a shell command.
+ [[ccoouunntt]] ## ++||--||##
+ Increment or decrement the cursor number.
+ [[ccoouunntt]] $$
+ Move the cursor to the end of a line.
+ %% Move to the matching character.
+ && Repeat the previous substitution command on the current line.
+ ''<<cchhaarraacctteerr>>
+ ``<<cchhaarraacctteerr>>
+ Return to a context marked by the character <character>.
+ [[ccoouunntt]] ((
+ Back up count sentences.
+ [[ccoouunntt]] ))
+ Move forward count sentences.
+ [[ccoouunntt]] ,,
+ Reverse find character count times.
+ [[ccoouunntt]] --
+ Move to first nonblank of the previous line, count times.
+ [[ccoouunntt]] ..
+ Repeat the last vvii command that modified text.
+ //RREE<<ccaarrrriiaaggee--rreettuurrnn>>
+ //RREE// [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
+ ??RREE<<ccaarrrriiaaggee--rreettuurrnn>>
+ ??RREE?? [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
+ NN
+ nn Search forward or backward for a regular expression.
+ 00 Move to the first character in the current line.
+ : Execute an ex command.
+ [[ccoouunntt]] ;;
+ Repeat the last character find count times.
+ [[ccoouunntt]] << mmoottiioonn
+ [[ccoouunntt]] >> mmoottiioonn
+ Shift lines left or right.
+ @@ bbuuffffeerr
+ Execute a named buffer.
+ [[ccoouunntt]] AA
+ Enter input mode, appending the text after the end of the line.
+ [[ccoouunntt]] BB
+ Move backwards count bigwords.
+ [[bbuuffffeerr]] [[ccoouunntt]] CC
+
+
+ Change text from the current position to the end-of-line.
+ [[bbuuffffeerr]] DD
+ Delete text from the current position to the end-of-line.
+ [[ccoouunntt]] EE
+ Move forward count end-of-bigwords.
+ [[ccoouunntt]] FF <<cchhaarraacctteerr>>
+ Search count times backward through the current line for
+ <character>.
+ [[ccoouunntt]] GG
+ Move to line count, or the last line of the file if count not spec-
+ ified.
+ [[ccoouunntt]] HH
+ Move to the screen line count - 1 lines below the top of the
+ screen.
+ [[ccoouunntt]] II
+ Enter input mode, inserting the text at the beginning of the line.
+ [[ccoouunntt]] JJ
+ Join lines.
+ [[ccoouunntt]] LL
+ Move to the screen line count - 1 lines above the bottom of the
+ screen.
+ MM Move to the screen line in the middle of the screen.
+ [[ccoouunntt]] OO
+ Enter input mode, appending text in a new line above the current
+ line.
+ [[bbuuffffeerr]] PP
+ Insert text from a buffer.
+ QQ Exit vvii (or visual) mode and switch to eexx mode.
+ [[ccoouunntt]] RR
+ Enter input mode, replacing the characters in the current line.
+ [[bbuuffffeerr]] [[ccoouunntt]] SS
+ Substitute count lines.
+ [[ccoouunntt]] TT <<cchhaarraacctteerr>>
+ Search backwards, count times, through the current line for the
+ character _a_f_t_e_r the specified <character>.
+ UU Restore the current line to its state before the cursor last moved
+ to it.
+ [[ccoouunntt]] WW
+ Move forward count bigwords.
+ [[bbuuffffeerr]] [[ccoouunntt]] XX
+ Delete count characters before the cursor.
+ [[bbuuffffeerr]] [[ccoouunntt]] YY
+ Copy (or ``yank'') count lines into the specified buffer.
+ ZZZZ Write the file and exit vvii.
+ [[ccoouunntt]] [[[[
+ Back up count section boundaries.
+ [[ccoouunntt]] ]]]]
+ Move forward count section boundaries.
+ ^^ Move to first nonblank character on the current line.
+ [[ccoouunntt]] __
+ Move down count - 1 lines, to the first nonblank character.
+ [[ccoouunntt]] aa
+ Enter input mode, appending the text after the cursor.
+ [[ccoouunntt]] bb
+ Move backwards count words.
+ [[bbuuffffeerr]] [[ccoouunntt]] cc mmoottiioonn
+ Change a region of text.
+ [[bbuuffffeerr]] [[ccoouunntt]] dd mmoottiioonn
+ Delete a region of text.
+ [[ccoouunntt]] ee
+ Move forward count end-of-words.
+ [[ccoouunntt]] ff<<cchhaarraacctteerr>>
+ Search forward, count times, through the rest of the current line
+ for <character>.
+ [[ccoouunntt]] ii
+
+ Enter input mode, inserting the text before the cursor.
+ mm <<cchhaarraacctteerr>>
+ Save the current context (line and column) as <character>.
+ [[ccoouunntt]] oo
+ Enter input mode, appending text in a new line under the current
+ line.
+ [[bbuuffffeerr]] pp
+ Append text from a buffer.
+ [[ccoouunntt]] rr <<cchhaarraacctteerr>>
+ Replace count characters.
+ [[bbuuffffeerr]] [[ccoouunntt]] ss
+ Substitute count characters in the current line starting with the
+ current character.
+ [[ccoouunntt]] tt <<cchhaarraacctteerr>>
+ Search forward, count times, through the current line for the char-
+ acter immediately _b_e_f_o_r_e <character>.
+ uu Undo the last change made to the file.
+ [[ccoouunntt]] ww
+ Move forward count words.
+ [[bbuuffffeerr]] [[ccoouunntt]] xx
+ Delete count characters.
+ [[bbuuffffeerr]] [[ccoouunntt]] yy mmoottiioonn
+ Copy (or ``yank'') a text region specified by the count and motion
+ into a buffer.
+ [[ccoouunntt11]] zz [[ccoouunntt22]] --||..||++||^^||<<ccaarrrriiaaggee--rreettuurrnn>>
+ Redraw, optionally repositioning and resizing the screen.
+ [[ccoouunntt]] {{
+ Move backward count paragraphs.
+ [[ccoouunntt]] ||
+ Move to a specific _c_o_l_u_m_n position on the current line.
+ [[ccoouunntt]] }}
+ Move forward count paragraphs.
+ [[ccoouunntt]] ~~
+ Reverse the case of the next count character(s).
+ [[ccoouunntt]] ~~ mmoottiioonn
+ Reverse the case of the characters in a text region specified by
+ the count and motion.
+ <<iinntteerrrruupptt>>
+ Interrupt the current operation.
+
+VVII TTEEXXTT IINNPPUUTT CCOOMMMMAANNDDSS
+ The following section describes the commands available in the text input
+ mode of the vvii editor.
+
+ <<nnuull>>
+ Replay the previous input.
+ <<ccoonnttrrooll--DD>>
+ Erase the previous autoindent character.
+ ^^<<ccoonnttrrooll--DD>>
+ Erase all of the autoindent characters, and reset the autoindent
+ level.
+ 00<<ccoonnttrrooll--DD>>
+ Erase all of the autoindent characters.
+ <<ccoonnttrrooll--TT>>
+ Insert sufficient <tab> and <space> characters to move the cursor
+ forward to a column immediately after the next column which is an
+ even multiple of the sshhiiffttwwiiddtthh option.
+ <<eerraassee>>
+ <<ccoonnttrrooll--HH>>
+ Erase the last character.
+ <<lliitteerraall nneexxtt>>
+ Quote the next character.
+ <<eessccaappee>>
+ Resolve all text input into the file, and return to command mode.
+ <<lliinnee eerraassee>>
+
+ Erase the current line.
+ <<ccoonnttrrooll--WW>>
+ <<wwoorrdd eerraassee>>
+ Erase the last word. The definition of word is dependent on the
+ aallttwweerraassee and ttttyywweerraassee options.
+ <<ccoonnttrrooll--XX>>[[00--99AA--FFaa--ff]]**
+ Insert a character with the specified hexadecimal value into the
+ text.
+ <<iinntteerrrruupptt>>
+ Interrupt text input mode, returning to command mode.
+
+EEXX CCOOMMMMAANNDDSS
+ The following section describes the commands available in the eexx editor.
+ In each entry below, the tag line is a usage synopsis for the command.
+
+ <<eenndd--ooff--ffiillee>>
+ Scroll the screen.
+ !! aarrgguummeenntt((ss))
+ [[rraannggee]]!! aarrgguummeenntt((ss))
+ Execute a shell command, or filter lines through a shell command.
+ "" A comment.
+ [[rraannggee]] nnuu[[mmbbeerr]] [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] ## [[ccoouunntt]] [[ffllaaggss]]
+ Display the selected lines, each preceded with its line number.
+ @@ bbuuffffeerr
+ ** bbuuffffeerr
+ Execute a buffer.
+ [[rraannggee]] dd[[eelleettee]] [[bbuuffffeerr]] [[ccoouunntt]] [[ffllaaggss]]
+ Delete the lines from the file.
+ ddii[[ssppllaayy]] bb[[uuffffeerrss]] || ss[[ccrreeeennss]] || tt[[aaggss]]
+ Display buffers, screens or tags.
+ ee[[ddiitt]][[!!]] [[++ccmmdd]] [[ffiillee]]
+ eexx[[!!]] [[++ccmmdd]] [[ffiillee]]
+ Edit a different file.
+ eexxuu[[ssaaggee]] [[ccoommmmaanndd]]
+ Display usage for an eexx command.
+ ff[[iillee]] [[ffiillee]]
+ Display and optionally change the file name.
+ ffgg [[nnaammee]]
+ VVii mode only. Foreground the specified screen.
+ [[rraannggee]] gg[[lloobbaall]] //ppaatttteerrnn// [[ccoommmmaannddss]]
+ [[rraannggee]] vv //ppaatttteerrnn// [[ccoommmmaannddss]]
+ Apply commands to lines matching (or not matching) a pattern.
+ hhee[[llpp]]
+ Display a help message.
+ [[lliinnee]] ii[[nnsseerrtt]][[!!]]
+ The input text is inserted before the specified line.
+ [[rraannggee]] jj[[ooiinn]][[!!]] [[ccoouunntt]] [[ffllaaggss]]
+ Join lines of text together.
+ [[rraannggee]] ll[[iisstt]] [[ccoouunntt]] [[ffllaaggss]]
+ Display the lines unambiguously.
+ mmaapp[[!!]] [[llhhss rrhhss]]
+ Define or display maps (for vvii only).
+ [[lliinnee]] mmaa[[rrkk]] <<cchhaarraacctteerr>>
+ [[lliinnee]] kk <<cchhaarraacctteerr>>
+ Mark the line with the mark <character>.
+ [[rraannggee]] mm[[oovvee]] lliinnee
+ Move the specified lines after the target line.
+ mmkk[[eexxrrcc]][[!!]] ffiillee
+ Write the abbreviations, editor options and maps to the specified
+ file.
+ nn[[eexxtt]][[!!]] [[ffiillee ......]]
+ Edit the next file from the argument list.
+ [[lliinnee]] oo[[ppeenn]] //ppaatttteerrnn// [[ffllaaggss]]
+
+
+ Enter open mode.
+ pprree[[sseerrvvee]]
+ Save the file in a form that can later be recovered using the eexx --rr
+ option.
+ pprreevv[[iioouuss]][[!!]]
+ Edit the previous file from the argument list.
+ [[rraannggee]] pp[[rriinntt]] [[ccoouunntt]] [[ffllaaggss]]
+ Display the specified lines.
+ [[lliinnee]] ppuu[[tt]] [[bbuuffffeerr]]
+ Append buffer contents to the current line.
+ qq[[uuiitt]][[!!]]
+ End the editing session.
+ [[lliinnee]] rr[[eeaadd]][[!!]] [[ffiillee]]
+ Read a file.
+ rreecc[[oovveerr]] ffiillee
+ Recover file if it was previously saved.
+ rreess[[iizzee]] [[++||--]]ssiizzee
+ VVii mode only. Grow or shrink the current screen.
+ rreeww[[iinndd]][[!!]]
+ Rewind the argument list.
+ ssee[[tt]] [[ooppttiioonn[[==[[vvaalluuee]]]] ......]] [[nnooooppttiioonn ......]] [[ooppttiioonn?? ......]] [[aallll]]
+ Display or set editor options.
+ sshh[[eellll]]
+ Run a shell program.
+ ssoo[[uurrccee]] ffiillee
+ Read and execute eexx commands from a file.
+ sspp[[lliitt]] [[ffiillee ......]]
+ VVii mode only. Split the screen.
+ [[rraannggee]] ss[[uubbssttiittuuttee]] [[//ppaatttteerrnn//rreeppllaaccee//]] [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] && [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] ~~ [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
+ Make substitutions.
+ ssuu[[ssppeenndd]][[!!]]
+ sstt[[oopp]][[!!]]
+ <<ssuussppeenndd>>
+ Suspend the edit session.
+ ttaa[[gg]][[!!]] ttaaggssttrriinngg
+ Edit the file containing the specified tag.
+ ttaaggpp[[oopp]][[!!]] [[ffiillee || nnuummbbeerr]]
+ Pop to the specified tag in the tags stack.
+ uunnmm[[aapp]][[!!]] llhhss
+ Unmap a mapped string.
+ vvee[[rrssiioonn]]
+ Display the version of the eexx//vvii editor.
+ [[lliinnee]] vvii[[ssuuaall]] [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
+ EExx mode only. Enter vvii.
+ vvii[[ssuuaall]][[!!]] [[++ccmmdd]] [[ffiillee]]
+ VVii mode only. Edit a new file.
+ vviiuu[[ssaaggee]] [[ccoommmmaanndd]]
+ Display usage for a vvii command.
+ [[rraannggee]] ww[[rriittee]][[!!]] [[>>>>]] [[ffiillee]]
+ [[rraannggee]] ww[[rriittee]] [[!!]] [[ffiillee]]
+ [[rraannggee]] wwnn[[!!]] [[>>>>]] [[ffiillee]]
+ [[rraannggee]] wwqq[[!!]] [[>>>>]] [[ffiillee]]
+ Write the file.
+ [[rraannggee]] xx[[iitt]][[!!]] [[ffiillee]]
+ Write the file if it has been modified.
+ [[rraannggee]] yyaa[[nnkk]] [[bbuuffffeerr]] [[ccoouunntt]]
+ Copy the specified lines to a buffer.
+ [[lliinnee]] zz [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
+ Adjust the window.
+
+SSEETT OOPPTTIIOONNSS
+ There are a large number of options that may be set (or unset) to change
+ the editor's behavior. This section describes the options, their abbre-
+ viations and their default values.
+
+ In each entry below, the first part of the tag line is the full name of
+ the option, followed by any equivalent abbreviations. The part in square
+ brackets is the default value of the option. Most of the options are
+ boolean, i.e. they are either on or off, and do not have an associated
+ value.
+
+ Options apply to both eexx and vvii modes, unless otherwise specified.
+
+ aallttwweerraassee [[ooffff]]
+ VVii only. Select an alternate word erase algorithm.
+ aauuttooiinnddeenntt,, aaii [[ooffff]]
+ Automatically indent new lines.
+ aauuttoopprriinntt,, aapp [[ooffff]]
+ EExx only. Display the current line automatically.
+ aauuttoowwrriittee,, aaww [[ooffff]]
+ Write modified files automatically when changing files.
+ bbeeaauuttiiffyy,, bbff [[ooffff]]
+ Discard control characters.
+ ccddppaatthh [[eennvviirroonnmmeenntt vvaarriiaabbllee CCDDPPAATTHH,, oorr ccuurrrreenntt ddiirreeccttoorryy]]
+ The directory paths used as path prefixes for the ccdd command.
+ ccoolluummnnss,, ccoo [[8800]]
+ Set the number of columns in the screen.
+ ccoommmmeenntt [[ooffff]]
+ VVii only. Skip leading comments in files.
+ ddiirreeccttoorryy,, ddiirr [[eennvviirroonnmmeenntt vvaarriiaabbllee TTMMPPDDIIRR,, oorr //ttmmpp]]
+ The directory where temporary files are created.
+ eeddccoommppaattiibbllee,, eedd [[ooffff]]
+ Remember the values of the ``c'' and ``g'' suffices to the
+ ssuubbssttiittuuttee commands, instead of initializing them as unset for each
+ new command.
+ eerrrroorrbbeellllss,, eebb [[ooffff]]
+ EExx only. Announce error messages with a bell.
+ eexxrrcc,, eexx [[ooffff]]
+ Never read startup files in the local directory.
+ eexxtteennddeedd [[ooffff]]
+ Regular expressions are extended (i.e. egrep(1) style) expres-
+ sions.
+ ffllaasshh [[oonn]]
+ Flash the screen instead of beeping the keyboard on error.
+ hhaarrddttaabbss,, hhtt [[88]]
+ Set the spacing between hardware tab settings.
+ iiggnnoorreeccaassee,, iicc [[ooffff]]
+ Ignore case differences in regular expressions.
+ kkeeyyttiimmee [[66]]
+ The 10th's of a second eexx//vvii waits for a subsequent key to complete
+ a key mapping.
+ lleeffttrriigghhtt [[ooffff]]
+ VVii only. Do left-right scrolling.
+ lliinneess,, llii [[2244]]
+ VVii only. Set the number of lines in the screen.
+ lliisspp [[ooffff]]
+ VVii only. Modify various search commands and options to work with
+ Lisp.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+ lliisstt [[ooffff]]
+ Display lines in an unambiguous fashion.
+ mmaaggiicc [[oonn]]
+ Treat certain characters specially in regular expressions.
+ mmaattcchhttiimmee [[77]]
+ VVii only. The 10th's of a second eexx//vvii pauses on the matching char-
+ acter when the sshhoowwmmaattcchh option is set.
+ mmeessgg [[oonn]]
+
+
+ Permit messages from other users.
+ mmooddeelliinneess,, mmooddeelliinnee [[ooffff]]
+ Read the first and last few lines of each file for eexx commands.
+
+ _T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
+ nnuummbbeerr,, nnuu [[ooffff]]
+ Precede each line displayed with its current line number.
+ ooccttaall [[ooffff]]
+ Display unknown characters as octal numbers, instead of the default
+ hexadecimal.
+ ooppeenn [[oonn]]
+ EExx only. If this option is not set, the ooppeenn and vviissuuaall commands
+ are disallowed.
+ ooppttiimmiizzee,, oopptt [[oonn]]
+ VVii only. Optimize text throughput to dumb terminals.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+ ppaarraaggrraapphhss,, ppaarraa [[IIPPLLPPPPPPQQPPPP LLIIppppllppiippbbpp]]
+ VVii only. Define additional paragraph boundaries for the {{ and }}
+ commands.
+ pprroommpptt [[oonn]]
+ EExx only. Display a command prompt.
+ rreeaaddoonnllyy,, rroo [[ooffff]]
+ Mark the file as read-only.
+ rreeccddiirr [[//vvaarr//ttmmpp//vvii..rreeccoovveerr]]
+ The directory where recovery files are stored.
+ rreeddrraaww,, rree [[ooffff]]
+ VVii only. Simulate an intelligent terminal on a dumb one.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+ rreemmaapp [[oonn]]
+ Remap keys until resolved.
+ rreeppoorrtt [[55]]
+ Set the number of lines about which the editor reports changes or
+ yanks.
+ rruulleerr [[ooffff]]
+ VVii only. Display a row/column ruler on the colon command line.
+ ssccrroollll,, ssccrr [[wwiinnddooww // 22]]
+ Set the number of lines scrolled.
+ sseeccttiioonnss,, sseecctt [[NNHHSSHHHH HHUUnnhhsshh]]
+ VVii only. Define additional section boundaries for the [[[[ and ]]]]
+ commands.
+ sshheellll,, sshh [[eennvviirroonnmmeenntt vvaarriiaabbllee SSHHEELLLL,, oorr //bbiinn//sshh]]
+ Select the shell used by the editor.
+ sshhiiffttwwiiddtthh,, ssww [[88]]
+ Set the autoindent and shift command indentation width.
+ sshhoowwddiirrttyy [[ooffff]]
+ VVii only. Display an asterisk on the colon command line if the file
+ has been modified.
+ sshhoowwmmaattcchh,, ssmm [[ooffff]]
+ VVii only. Note matching ``{'' and ``('' for ``}'' and ``)'' charac-
+ ters.
+ sshhoowwmmooddee [[ooffff]]
+ VVii only. Display the current editor mode (command or input).
+ ssiiddeessccrroollll [[1166]]
+ VVii only. Set the amount a left-right scroll will shift.
+ sslloowwooppeenn,, ssllooww [[ooffff]]
+ Delay display updating during text input.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+ ssoouurrcceeaannyy [[ooffff]]
+ Read startup files not owned by the current user.
+
+ _T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
+ ttaabbssttoopp,, ttss [[88]]
+
+ This option sets tab widths for the editor display.
+ ttaagglleennggtthh,, ttll [[00]]
+ Set the number of significant characters in tag names.
+ ttaaggss,, ttaagg [[ttaaggss //vvaarr//ddbb//lliibbcc..ttaaggss //ssyyss//kkeerrnn//ttaaggss]]
+ Set the list of tags files.
+ tteerrmm,, ttttyyttyyppee,, ttttyy [[eennvviirroonnmmeenntt vvaarriiaabbllee TTEERRMM]]
+ Set the terminal type.
+ tteerrssee [[ooffff]]
+ This option has historically made editor messages less verbose. It
+ has no effect in this implementation.
+ ttiillddeeoopp
+ Modify the ~~ command to take an associated motion.
+ ttiimmeeoouutt,, ttoo [[oonn]]
+ Time out on keys which may be mapped.
+ ttttyywweerraassee [[ooffff]]
+ VVii only. Select an alternate erase algorithm.
+ vveerrbboossee [[ooffff]]
+ only. Display an error message for every error.
+ ww330000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is less than 1200
+ baud.
+ ww11220000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is equal to 1200
+ baud.
+ ww99660000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is greater than 1200
+ baud.
+ wwaarrnn [[oonn]]
+ EExx only. This option causes a warning message to the terminal if
+ the file has been modified, since it was last written, before a !!
+ command.
+ wwiinnddooww,, ww,, wwii [[eennvviirroonnmmeenntt vvaarriiaabbllee LLIINNEESS]]
+ Set the window size for the screen.
+ wwrraappmmaarrggiinn,, wwmm [[00]]
+ VVii only. Break lines automatically when they reach the right-hand
+ margin.
+ wwrraappssccaann,, wwss [[oonn]]
+ Set searches to wrap around the end or beginning of the file.
+ wwrriitteeaannyy,, wwaa [[ooffff]]
+ Turn off file-overwriting checks.
+
+EENNVVIIRROONNMMEENNTTAALL VVAARRIIAABBLLEESS
+ COLUMNS The number of columns on the screen. This value overrides any
+ system or terminal specific values. If the COLUMNS environ-
+ mental variable is not set when eexx//vvii runs, or the ccoolluummnnss op-
+ tion is explicitly reset by the user, eexx//vvii enters the value
+ into the environment.
+ EXINIT A list of eexx startup commands, read if the variable NEXINIT is
+ not set.
+ HOME The user's home directory, used as the initial directory path
+ for the startup _$_H_O_M_E_/_._n_e_x_r_c and _$_H_O_M_E_/_._e_x_r_c files. This val-
+ ue is also used as the default directory for the vvii ccdd com-
+ mand.
+ LINES The number of rows on the screen. This value overrides any
+ system or terminal specific values. If the LINES environmen-
+ tal variable is not set when eexx//vvii runs, or the lliinneess option
+ is explicitly reset by the user, eexx//vvii enters the value into
+ the environment.
+ NEXINIT A list of eexx startup commands.
+ SHELL The user's shell of choice (see also the sshheellll option).
+ TERM The user's terminal type. The default is the type
+ ``unknown''. If the TERM environmental variable is not set
+ when eexx//vvii runs, or the tteerrmm option is explicitly reset by the
+ user, eexx//vvii enters the value into the environment.
+ TMPDIR The location used to stored temporary files (see also the
+ ddiirreeccttoorryy option).
+
+AASSYYNNCCHHRROONNOOUUSS EEVVEENNTTSS
+ SIGALRM VVii//eexx uses this signal for periodic backups of file modifica-
+ tions and to display ``busy'' messages when operations are
+ likely to take a long time.
+ SIGHUP
+ SIGTERM If the current buffer has changed since it was last written
+ in its entirety, the editor attempts to save the modified
+ file so it can be later recovered. See the vvii//eexx Reference
+ manual section entitled ``Recovery'' for more information.
+ SIGINT When an interrupt occurs, the current operation is halted,
+ and the editor returns to the command level. If interrupted
+ during text input, the text already input is resolved into
+ the file as if the text input had been normally terminated.
+ SIGWINCH The screen is resized. See the vvii//eexx Reference manual sec-
+ tion entitled ``Sizing the Screen'' for more information.
+ SIGCONT
+ SIGQUIT
+ SIGTSTP VVii//eexx ignores these signals.
+
+BBUUGGSS
+ See the file _n_v_i_/_d_o_c_s_/_b_u_g_s_._c_u_r_r_e_n_t for a list of the known bugs in this
+ version.
+
+FFIILLEESS
+ /bin/sh The default user shell.
+ /etc/vi.exrc System-wide vi startup file.
+ /tmp Temporary file directory.
+ /var/tmp/vi.recover The default recovery file directory.
+ $HOME/.nexrc 1st choice for user's home directory startup file.
+ $HOME/.exrc 2nd choice for user's home directory startup file.
+ .nexrc 1st choice for local directory startup file.
+ .exrc 2nd choice for local directory startup file.
+
+SSEEEE AALLSSOO
+ ctags(1), more(1), curses(3), dbopen(3)
+
+ The ``Vi Quick Reference'' card.
+
+ ``An Introduction to Display Editing with Vi'', found in the ``UNIX
+ User's Manual Supplementary Documents'' section of both the 4.3BSD and
+ 4.4BSD manual sets. This document is the closest thing available to an
+ introduction to the vvii screen editor.
+
+ ``Ex Reference Manual (Version 3.7)'', found in the ``UNIX User's Manual
+ Supplementary Documents'' section of both the 4.3BSD and 4.4BSD manual
+ sets. This document is the final reference for the eexx editor, as dis-
+ tributed in most historic 4BSD and System V systems.
+
+ ``Edit: A tutorial'', found in the ``UNIX User's Manual Supplementary
+ Documents'' section of the 4.3BSD manual set. This document is an intro-
+ duction to a simple version of the eexx screen editor.
+
+ ``Ex/Vi Reference Manual'', found in the ``UNIX User's Manual
+ Supplementary Documents'' section of the 4.4BSD manual set. This docu-
+ ment is the final reference for the nneexx//nnvvii text editors, as distributed
+ in 4.4BSD and 4.4BSD-Lite.
+
+ RRooffff source for all of these documents is distributed with nneexx//nnvvii in the
+ _n_v_i_/_U_S_D_._d_o_c directory of the nneexx//nnvvii source code.
+
+ The files ``autowrite'', ``input'', ``quoting'', and ``structures'',
+ found in the _n_v_i_/_d_o_c_s_/_i_n_t_e_r_n_a_l_s directory of the nneexx//nnvvii source code.
+
+HHIISSTTOORRYY
+ The nneexx//nnvvii replacements for the eexx//vvii editor first appeared in 4.4BSD.
+
+SSTTAANNDDAARRDDSS
+ NNeexx//nnvvii is close to IEEE Std1003.2 (``POSIX''). That document differs
+ from historical eexx//vvii practice in several places; there are changes to be
+ made on both sides.
+
+4.4BSD July 15, 1994 13
diff --git a/usr.bin/vi/USD.doc/vi.man/vi.0.ps b/usr.bin/vi/USD.doc/vi.man/vi.0.ps
new file mode 100644
index 0000000..f6cfc03
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.man/vi.0.ps
@@ -0,0 +1,1063 @@
+%!PS-Adobe-3.0
+%%Creator: groff version 1.08
+%%DocumentNeededResources: font Times-Roman
+%%+ font Times-Bold
+%%+ font Courier-Bold
+%%+ font Courier-Oblique
+%%+ font Courier
+%%+ font Times-Italic
+%%+ font Symbol
+%%DocumentSuppliedResources: procset grops 1.08 0
+%%Pages: 14
+%%PageOrder: Ascend
+%%Orientation: Portrait
+%%EndComments
+%%BeginProlog
+%%BeginResource: procset grops 1.08 0
+/setpacking where{
+pop
+currentpacking
+true setpacking
+}if
+/grops 120 dict dup begin
+/SC 32 def
+/A/show load def
+/B{0 SC 3 -1 roll widthshow}bind def
+/C{0 exch ashow}bind def
+/D{0 exch 0 SC 5 2 roll awidthshow}bind def
+/E{0 rmoveto show}bind def
+/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
+/G{0 rmoveto 0 exch ashow}bind def
+/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
+/I{0 exch rmoveto show}bind def
+/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
+/K{0 exch rmoveto 0 exch ashow}bind def
+/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
+/M{rmoveto show}bind def
+/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
+/O{rmoveto 0 exch ashow}bind def
+/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
+/Q{moveto show}bind def
+/R{moveto 0 SC 3 -1 roll widthshow}bind def
+/S{moveto 0 exch ashow}bind def
+/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
+/SF{
+findfont exch
+[exch dup 0 exch 0 exch neg 0 0]makefont
+dup setfont
+[exch/setfont cvx]cvx bind def
+}bind def
+/MF{
+findfont
+[5 2 roll
+0 3 1 roll
+neg 0 0]makefont
+dup setfont
+[exch/setfont cvx]cvx bind def
+}bind def
+/level0 0 def
+/RES 0 def
+/PL 0 def
+/LS 0 def
+/PLG{
+gsave newpath clippath pathbbox grestore
+exch pop add exch pop
+}bind def
+/BP{
+/level0 save def
+1 setlinecap
+1 setlinejoin
+72 RES div dup scale
+LS{
+90 rotate
+}{
+0 PL translate
+}ifelse
+1 -1 scale
+}bind def
+/EP{
+level0 restore
+showpage
+}bind def
+/DA{
+newpath arcn stroke
+}bind def
+/SN{
+transform
+.25 sub exch .25 sub exch
+round .25 add exch round .25 add exch
+itransform
+}bind def
+/DL{
+SN
+moveto
+SN
+lineto stroke
+}bind def
+/DC{
+newpath 0 360 arc closepath
+}bind def
+/TM matrix def
+/DE{
+TM currentmatrix pop
+translate scale newpath 0 0 .5 0 360 arc closepath
+TM setmatrix
+}bind def
+/RC/rcurveto load def
+/RL/rlineto load def
+/ST/stroke load def
+/MT/moveto load def
+/CL/closepath load def
+/FL{
+currentgray exch setgray fill setgray
+}bind def
+/BL/fill load def
+/LW/setlinewidth load def
+/RE{
+findfont
+dup maxlength 1 index/FontName known not{1 add}if dict begin
+{
+1 index/FID ne{def}{pop pop}ifelse
+}forall
+/Encoding exch def
+dup/FontName exch def
+currentdict end definefont pop
+}bind def
+/DEFS 0 def
+/EBEGIN{
+moveto
+DEFS begin
+}bind def
+/EEND/end load def
+/CNT 0 def
+/level1 0 def
+/PBEGIN{
+/level1 save def
+translate
+div 3 1 roll div exch scale
+neg exch neg exch translate
+0 setgray
+0 setlinecap
+1 setlinewidth
+0 setlinejoin
+10 setmiterlimit
+[]0 setdash
+/setstrokeadjust where{
+pop
+false setstrokeadjust
+}if
+/setoverprint where{
+pop
+false setoverprint
+}if
+newpath
+/CNT countdictstack def
+userdict begin
+/showpage{}def
+}bind def
+/PEND{
+clear
+countdictstack CNT sub{end}repeat
+level1 restore
+}bind def
+end def
+/setpacking where{
+pop
+setpacking
+}if
+%%EndResource
+%%IncludeResource: font Times-Roman
+%%IncludeResource: font Times-Bold
+%%IncludeResource: font Courier-Bold
+%%IncludeResource: font Courier-Oblique
+%%IncludeResource: font Courier
+%%IncludeResource: font Times-Italic
+%%IncludeResource: font Symbol
+grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL
+792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron
+/Ydieresis/trademark/quotesingle/.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/hyphen/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/circumflex/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/tilde/.notdef/quotesinglbase
+/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger
+/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
+/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
+/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar
+/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus
+/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu
+/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright
+/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde
+/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
+/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
+/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
+/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute
+/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve
+/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex
+/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE
+/Courier@0 ENC0/Courier RE/Courier-Oblique@0 ENC0/Courier-Oblique RE
+/Courier-Bold@0 ENC0/Courier-Bold RE/Times-Bold@0 ENC0/Times-Bold RE
+/Times-Roman@0 ENC0/Times-Roman RE
+%%EndProlog
+%%Page: 1 1
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Times-Bold@0 SF -.2(NA)72
+108 S(ME).2 E/F2 10/Courier-Bold@0 SF(ex, vi, view)102 120 Q F0 2.5<ad74>2.5 G
+-.15(ex)187.42 120 S 2.5(te).15 G(ditors)206.43 120 Q F1(SYNOPSIS)72 144 Q F2
+(ex)102 156 Q F0([)3.333 E F2(\255eFRrsv)2.499 E F0 3.333(][).833 G F2<ad63>
+-.834 E/F3 10/Courier-Oblique@0 SF(cmd)6 E F0 3.333(][).833 G F2<ad74>-.834 E
+F3(tag)6 E F0 3.333(][).833 G F2<ad77>-.834 E F3(size)6 E F0 3.333(][).833 G F3
+(file ...)330.796 156 Q F0(]).833 E F2(vi)102 168 Q F0([)3.333 E F2(\255eFRrv)
+2.499 E F0 3.333(][).833 G F2<ad63>-.834 E F3(cmd)6 E F0 3.333(][).833 G F2
+<ad74>-.834 E F3(tag)6 E F0 3.333(][).833 G F2<ad77>-.834 E F3(size)6 E F0
+3.333(][).833 G F3(file ...)324.796 168 Q F0(]).833 E F2(view)102 180 Q F0([)
+3.333 E F2(\255eFRrv)2.499 E F0 3.333(][).833 G F2<ad63>-.834 E F3(cmd)6 E F0
+3.333(][).833 G F2<ad74>-.834 E F3(tag)6 E F0 3.333(][).833 G F2<ad77>-.834 E
+F3(size)6 E F0 3.333(][).833 G F3(file ...)336.796 180 Q F0(]).833 E F1
+(DESCRIPTION)72 204 Q F2(Vi)102 216 Q F0 .176(is a screen oriented te)2.676 F
+.176(xt editor)-.15 F(.)-.55 E F2(Ex)5.176 E F0 .176(is a line-oriented te)
+2.676 F .175(xt editor)-.15 F(.)-.55 E F2(Ex)5.175 E F0(and)2.675 E F2(vi)2.675
+E F0 .175(are dif)2.675 F .175(ferent interf)-.25 F .175(aces to the)-.1 F .56
+(same program, and it is possible to switch back and forth during an edit sess\
+ion.)102 228 R F2(View)5.561 E F0 .561(is the equi)3.061 F -.25(va)-.25 G .561
+(lent of).25 F(using the)102 240 Q F2<ad52>4.166 E F0(\(read-only\) option of)
+2.5 E F2(vi)2.5 E F0(.)A .216(This manual page is the one pro)102 258 R .215
+(vided with the)-.15 F F2(nex/nvi)2.715 E F0 -.15(ve)2.715 G .215
+(rsions of the).15 F F2(ex/vi)2.715 E F0(te)2.715 E .215(xt editors.)-.15 F F2
+(Nex/nvi)5.215 E F0(are)2.715 E 1.937(intended as b)102 270 R(ug-for)-.2 E(-b)
+-.2 E 1.937(ug compatible replacements for the original F)-.2 F 1.938
+(ourth Berk)-.15 F(ele)-.1 E 4.438(yS)-.15 G(oftw)456.982 270 Q 1.938
+(are Distrib)-.1 F(ution)-.2 E(\(4BSD\))102 282 Q F2(ex)3.008 E F0(and)3.008 E
+F2(vi)3.008 E F0 3.008(programs. F)3.008 F .508
+(or the rest of this manual page,)-.15 F F2(nex/nvi)3.008 E F0 .507
+(is used only when it')3.008 F 3.007(sn)-.55 G(ecessary)506.13 282 Q
+(to distinguish it from the historic implementations of)102 294 Q F2(ex/vi)2.5
+E F0(.)A .961(This manual page is intended for users already f)102 312 R .961
+(amiliar with)-.1 F F2(ex/vi)3.461 E F0 3.462(.A)C -.15(ny)397.982 312 S .962
+(one else should almost certainly).15 F .582
+(read a good tutorial on the editor before this manual page.)102 324 R .582
+(If you')5.582 F .581(re in an unf)-.5 F .581(amiliar en)-.1 F .581
+(vironment, and you)-.4 F .799(absolutely ha)102 336 R 1.099 -.15(ve t)-.2 H
+3.299(og).15 G .799(et w)184.317 336 R .799(ork done immediately)-.1 F 3.299
+(,r)-.65 G .8(ead the section after the options description, entitled `)299.803
+336 R(`F)-.74 E(ast)-.15 E(Startup')102 348 Q('. It')-.74 E 2.5(sp)-.55 G
+(robably enough to get you going.)162.09 348 Q(The follo)102 366 Q
+(wing options are a)-.25 E -.25(va)-.2 G(ilable:).25 E F2<ad63>103.666 384 Q F0
+(Ex)137 384 Q(ecute)-.15 E F3(cmd)2.675 E F0 .175
+(immediately after starting the edit session.)2.675 F -.15(Pa)5.175 G .174
+(rticularly useful for initial positioning in).15 F .624(the \214le, ho)137 396
+R(we)-.25 E -.15(ve)-.25 G(r).15 E F3(cmd)3.124 E F0 .625
+(is not limited to positioning commands.)3.124 F .625
+(This is the POSIX 1003.2 interf)5.625 F(ace)-.1 E(for the historic `)137 408 Q
+(`+cmd')-.74 E 2.5('s)-.74 G(yntax.)239.47 408 Q F2(Nex/nvi)5 E F0
+(supports both the old and ne)2.5 E 2.5(ws)-.25 G(yntax.)440.1 408 Q F2<ad65>
+103.666 426 Q F0(Start editing in e)137 426 Q 2.5(xm)-.15 G
+(ode, as if the command name were)218.52 426 Q F2(ex)2.5 E F0(.)A F2<ad46>
+103.666 444 Q F0(Don')137 444 Q 2.677(tc)-.18 G(op)167.267 444 Q 2.677(yt)-.1 G
+.177(he entire \214le when \214rst starting to edit.)187.624 444 R .177
+(\(The def)5.177 F .177(ault is to mak)-.1 F 2.677(eac)-.1 G(op)456.532 444 Q
+2.676(yi)-.1 G 2.676(nc)476.888 444 S .176(ase someone)489.004 444 R
+(else modi\214es the \214le during your edit session.\))137 456 Q F2<ad52>
+103.666 474 Q F0 .184
+(Start editing in read-only mode, as if the command name w)137 474 R(as)-.1 E
+F2(view)2.685 E F0 2.685(,o)C 2.685(rt)421.415 474 S .185(he readonly option w)
+430.21 474 R .185(as set.)-.1 F F2<ad72>103.666 492 Q F0(Reco)137 492 Q -.15
+(ve)-.15 G 2.627(rt).15 G .127(he speci\214ed \214les, or)175.427 492 R 2.627
+(,i)-.4 G 2.627(fn)263.305 492 S 2.627<6f8c>274.262 492 S .127
+(les are speci\214ed, list the \214les that could be reco)287.449 492 R -.15
+(ve)-.15 G 2.626(red. If).15 F .126(no re-)2.626 F(co)137 504 Q -.15(ve)-.15 G
+.4(rable \214les by the speci\214ed name e).15 F .401
+(xist, the \214le is edited as if the)-.15 F F2<ad72>4.567 E F0 .401
+(option had not been speci-)2.901 F(\214ed.)137 516 Q F2<ad73>103.666 534 Q F0
+1.621(Enter batch mode; applicable only to)137 534 R F2(ex)4.121 E F0 1.621
+(edit sessions.)4.121 F 1.62(Batch mode is useful when running)6.621 F F2(ex)
+4.12 E F0 2.647(scripts. Prompts,)137 546 R(informati)2.647 E .447 -.15(ve m)
+-.25 H .147(essages and other user oriented message are turned of).15 F .148
+(f, and no start-)-.25 F .067(up \214les or en)137 558 R .067(vironmental v)-.4
+F .066(ariables are read.)-.25 F .066(This is the POSIX 1003.2 interf)5.066 F
+.066(ace for the historic `)-.1 F(`\255')-.74 E(')-.74 E(ar)137 570 Q(gument.)
+-.18 E F2(Nex/nvi)5 E F0(supports both the old and ne)2.5 E 2.5(ws)-.25 G
+(yntax.)353 570 Q F2<ad74>103.666 588 Q F0
+(Start editing at the speci\214ed tag.)137 588 Q(\(See)5 E/F4 10/Courier@0 SF
+(ctags)2.5 E F0(\(1\)\).)A F2<ad77>103.666 606 Q F0(Set the initial windo)137
+606 Q 2.5(ws)-.25 G(ize to the speci\214ed number of lines.)231.2 606 Q F2
+<ad76>103.666 624 Q F0(Start editing in vi mode, as if the command name w)137
+624 Q(as)-.1 E F2(vi)2.5 E F0(or)2.5 E F2(view)2.5 E F0(.)A F2<ad58>103.666 642
+Q F0(Reserv)137 642 Q(ed for X11 interf)-.15 E(aces.)-.1 E/F5 10/Times-Italic@0
+SF(No X11 support is curr)5 E(ently implemented.)-.37 E F0 .35
+(Command input for)102 660 R F2(ex/vi)2.85 E F0 .35
+(is read from the standard input.)2.85 F .35(In the)5.35 F F2(vi)2.85 E F0
+(interf)2.85 E .35(ace, it is an error if standard in-)-.1 F .343
+(put is not a terminal.)102 672 R .343(In the)5.343 F F2(ex)2.843 E F0(interf)
+2.843 E .343(ace, if standard input is not a terminal,)-.1 F F2(ex)2.843 E F0
+.342(will read commands from it)2.843 F(re)102 684 Q -.05(ga)-.15 G .137
+(rdless, ho).05 F(we)-.25 E -.15(ve)-.25 G .937 -.4(r, t).15 H .137
+(he session will be a batch mode session, e).4 F .138(xactly as if the)-.15 F
+F2<ad73>4.304 E F0 .138(option had been speci\214ed.)2.638 F 172.465
+(4.4BSD July)72 750 R(15, 1994)2.5 E(1)535 750 Q EP
+%%Page: 2 2
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Courier-Bold@0 SF(Ex/vi)102
+96 Q F0 -.15(ex)2.5 G(its 0 on success, and greater than 0 if an error occurs.)
+.15 E/F2 10/Times-Bold@0 SF -.9(FA)72 120 S 1.666(ST ST).9 F(AR)-.9 E(TUP)-.4 E
+F0 .467(This section will tell you the minimum amount that you need to do simp\
+le editing tasks using)102 132 R F1(vi)2.966 E F0 2.966(.I)C 2.966(fy)506.584
+132 S(ou')517.88 132 Q -.15(ve)-.5 G(ne)102 144 Q -.15(ve)-.25 G 3.453(ru).15 G
+.953(sed an)132.263 144 R 3.453(ys)-.15 G .953(creen editor before, you')
+170.679 144 R .953(re lik)-.5 F .953(ely to ha)-.1 F 1.253 -.15(ve p)-.2 H .953
+(roblems e).15 F -.15(ve)-.25 G 3.453(nw).15 G .953
+(ith this simple introduction.)412.286 144 R(In)5.954 E
+(that case you should \214nd someone that already kno)102 156 Q(ws)-.25 E F1
+(vi)2.5 E F0(and ha)2.5 E .3 -.15(ve t)-.2 H(hem w).15 E
+(alk you through this section.)-.1 E F1(Vi)102 174 Q F0 .294
+(is a screen editor)2.794 F 5.294(.T)-.55 G .294(his means that it tak)198.51
+174 R .293
+(es up almost the entire screen, displaying part of the \214le on each)-.1 F
+.001(screen line, e)102 186 R .001(xcept for the last line of the screen.)-.15
+F .002(The last line of the screen is used for you to gi)5.001 F .302 -.15
+(ve c)-.25 H(ommands).15 E(to)102 198 Q F1(vi)2.5 E F0 2.5(,a)C(nd for)133.72
+198 Q F1(vi)2.5 E F0(to gi)2.5 E .3 -.15(ve i)-.25 H(nformation to you.).15 E
+.585(The other f)102 216 R .585(act that you need to understand is that)-.1 F
+F1(vi)3.085 E F0 .585(is a modeful editor)3.085 F 3.085(,i)-.4 G .584
+(.e. you are either entering te)406.125 216 R .584(xt or)-.15 F .836(you are e)
+102 228 R -.15(xe)-.15 G .836(cuting commands, and you ha).15 F 1.137 -.15
+(ve t)-.2 H 3.337(ob).15 G 3.337(ei)301.062 228 S 3.337(nt)311.619 228 S .837
+(he right mode to do one or the other)322.736 228 R 5.837(.Y)-.55 G .837
+(ou will be in)487.209 228 R 1.094
+(command mode when you \214rst start editing a \214le.)102 240 R 1.093
+(There are commands that switch you into input mode.)6.094 F .084
+(There is only one k)102 252 R .384 -.15(ey t)-.1 H .085(hat tak).15 F .085
+(es you out of input mode, and that is the <escape> k)-.1 F -.15(ey)-.1 G 5.085
+(.\()-.5 G -2.15 -.25(Ke y)449.895 252 T .085(names are written)2.835 F 1.473
+(using less-than and greater)102 264 R 1.473(-than signs, e.g.)-.2 F 1.473
+(<escape> means the `)6.473 F(`escape')-.74 E 3.973('k)-.74 G -.15(ey)420.59
+264 S 3.973(,u)-.5 G 1.473(sually labeled `)440.703 264 R(`esc')-.74 E 3.972
+('o)-.74 G(n)535 264 Q .553(your terminal')102 276 R 3.053(sk)-.55 G -.15(ey)
+171.336 276 S 3.053(board.\) If).15 F(you')3.053 E .554(re e)-.5 F -.15(ve)-.25
+G 3.054(rc).15 G .554(onfused as to which mode you')277.45 276 R .554(re in, k)
+-.5 F .554(eep entering the <escape>)-.1 F -.1(ke)102 288 S 2.615(yu)-.05 G
+(ntil)123.805 288 Q F1(vi)2.615 E F0 .115(beeps at you.)2.615 F(\(Generally)
+5.115 E(,)-.65 E F1(vi)2.615 E F0 .115
+(will beep at you if you try and do something that')2.615 F 2.614(sn)-.55 G
+.114(ot allo)484.472 288 R 2.614(wed. It)-.25 F
+(will also display error messages.\))102 300 Q 2.057 -.8(To s)102 318 T .457
+(tart editing a \214le, enter the command `).8 F(`)-.74 E/F3 10/Courier@0 SF
+.458(vi file_name<carriage-return>)B F0 -.74('')C 2.958(.T).74 G .458
+(he command you)470.204 318 R .333
+(should enter as soon as you start editing is `)102 330 R(`)-.74 E F3 .333
+(:set verbose showmode<carriage-return>)B F0 -.74('')C 2.833(.T).74 G(his)
+528.33 330 Q 1.441(will mak)102 342 R 3.941(et)-.1 G 1.441(he editor gi)149.782
+342 R 1.741 -.15(ve y)-.25 H 1.441(ou v).15 F 1.441
+(erbose error messages and display the current mode at the bottom of the)-.15 F
+(screen.)102 354 Q(The commands to mo)102 372 Q .3 -.15(ve a)-.15 H
+(round the \214le are:).15 E F2(h)102 384 Q F0(Mo)131 384 Q .3 -.15(ve t)-.15 H
+(he cursor left one character).15 E(.)-.55 E F2(j)102 396 Q F0(Mo)131 396 Q .3
+-.15(ve t)-.15 H(he cursor do).15 E(wn one line.)-.25 E F2(k)102 408 Q F0(Mo)
+131 408 Q .3 -.15(ve t)-.15 H(he cursor up one line.).15 E F2(l)102 420 Q F0
+(Mo)131 420 Q .3 -.15(ve t)-.15 H(he cursor right one character).15 E(.)-.55 E
+F2(<cursor)102 432 Q(-arr)-.37 E -.1(ow)-.18 G(s>).1 E F0(The cursor arro)131
+444 Q 2.5(wk)-.25 G -.15(ey)207.01 444 S 2.5(ss).15 G(hould w)226.58 444 Q
+(ork, too.)-.1 E F2(/text<carriage-r)102 456 Q(etur)-.18 E(n>)-.15 E F0
+(Search for the string `)131 468 Q(`te)-.74 E(xt')-.15 E 2.5('i)-.74 G 2.5(nt)
+246.84 468 S(he \214le, and mo)257.12 468 Q .3 -.15(ve t)-.15 H
+(he cursor to its \214rst character).15 E(.)-.55 E(The commands to enter ne)102
+486 Q 2.5(wt)-.25 G -.15(ex)220.34 486 S 2.5(ta).15 G(re:)239.35 486 Q F2(a)102
+498 Q F0(Append ne)131 498 Q 2.5(wt)-.25 G -.15(ex)186.85 498 S(t,).15 E/F4 10
+/Times-Italic@0 SF(after)2.5 E F0(the cursor)2.5 E(.)-.55 E F2(i)102 510 Q F0
+(Insert ne)131 510 Q 2.5(wt)-.25 G -.15(ex)177.96 510 S(t,).15 E F4(befor)2.5 E
+(e)-.37 E F0(the cursor)2.5 E(.)-.55 E F2(o)102 522 Q F0(Open a ne)131 522 Q
+2.5(wl)-.25 G(ine belo)183.79 522 Q 2.5(wt)-.25 G
+(he line the cursor is on, and start entering te)227.98 522 Q(xt.)-.15 E F2(O)
+102 534 Q F0(Open a ne)131 534 Q 2.5(wl)-.25 G(ine abo)183.79 534 Q .3 -.15
+(ve t)-.15 H(he line the cursor is on, and start entering te).15 E(xt.)-.15 E
+F2(<escape>)102 546 Q F0 .744(Once you')131 558 R 1.044 -.15(ve e)-.5 H .744
+(ntered input mode using the one of the).15 F F2(a)3.244 E F0(,)A F2(i)3.244 E
+F0(,)A F2(O)3.244 E F0 3.244(,o)C(r)390.542 558 Q F2(o)3.243 E F0 .743
+(commands, use)3.243 F F2(<escape>)3.243 E F0 .743(to quit)3.243 F(entering te)
+131 570 Q(xt and return to command mode.)-.15 E(The commands to cop)102 588 Q
+2.5(yt)-.1 G -.15(ex)200.78 588 S 2.5(ta).15 G(re:)219.79 588 Q F2(yy)102 600 Q
+F0(Cop)131 600 Q 2.5(yt)-.1 G(he line the cursor is on.)157.85 600 Q F2(p)102
+612 Q F0(Append the copied line after the line the cursor is on.)131 612 Q
+(The commands to delete te)102 630 Q(xt are:)-.15 E F2(dd)102 642 Q F0
+(Delete the line the cursor is on.)131 642 Q F2(x)102 654 Q F0
+(Delete the character the cursor is on.)131 654 Q
+(The commands to write the \214le are:)102 672 Q F2(:w<carriage-r)102 684 Q
+(etur)-.18 E(n>)-.15 E F0 .528(Write the \214le back to the \214le with the na\
+me that you originally used as an ar)131 696 R .528(gument on the)-.18 F F1(vi)
+3.028 E F0(com-)3.028 E 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(2)535 750 Q
+EP
+%%Page: 3 3
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(mand line.)131 96 Q/F1 10
+/Times-Bold@0 SF(:w \214le_name<carriage-r)102 108 Q(etur)-.18 E(n>)-.15 E F0
+(Write the \214le back to the \214le with the name `)131 120 Q(`\214le_name')
+-.74 E('.)-.74 E(The commands to quit editing and e)102 138 Q
+(xit the editor are:)-.15 E F1(:q<carriage-r)102 150 Q(etur)-.18 E(n>)-.15 E F0
+.848(Quit editing and lea)131 162 R 1.148 -.15(ve v)-.2 H 3.348(i\().15 G .848
+(if you')239.6 162 R 1.148 -.15(ve m)-.5 H .848(odi\214ed the \214le, b).15 F
+.848(ut not sa)-.2 F -.15(ve)-.2 G 3.348(dy).15 G .848(our changes,)415.454 162
+R/F2 10/Courier-Bold@0 SF(vi)3.347 E F0 .847(will refuse to)3.347 F(quit\).)131
+174 Q F1(:q!<carriage-r)102 186 Q(etur)-.18 E(n>)-.15 E F0(Quit, discarding an)
+131 198 Q 2.5(ym)-.15 G(odi\214cations that you may ha)222.51 198 Q .3 -.15
+(ve m)-.2 H(ade.).15 E .686(One \214nal caution.)102 216 R .686
+(Unusual characters can tak)5.686 F 3.187(eu)-.1 G 3.187(pm)302.483 216 S .687
+(ore than one column on the screen, and long lines can)318.45 216 R(tak)102 228
+Q 3.129(eu)-.1 G 3.129(pm)126.689 228 S .629(ore than a single screen line.)
+142.598 228 R .629(The abo)5.629 F .929 -.15(ve c)-.15 H .629(ommands w).15 F
+.629(ork on `)-.1 F(`ph)-.74 E(ysical')-.05 E 3.129('c)-.74 G .628
+(haracters and lines, i.e.)446.476 228 R(the)102 240 Q 2.74(ya)-.15 G -.25(ff)
+126.25 240 S .24(ect the entire line no matter ho).25 F 2.74(wm)-.25 G(an)
+273.79 240 Q 2.74(ys)-.15 G .241(creen lines it tak)294.71 240 R .241
+(es up and the entire character no matter ho)-.1 F(w)-.25 E(man)102 252 Q 2.5
+(ys)-.15 G(creen columns it tak)130.46 252 Q(es up.)-.1 E F1 1.666(VI COMMANDS)
+72 276 R F0 .186(The follo)102 288 R .186
+(wing section describes the commands a)-.25 F -.25(va)-.2 G .186
+(ilable in the command mode of the).25 F F2(vi)2.686 E F0(editor)2.686 E 5.186
+(.I)-.55 G 2.685(ne)498.54 288 S .185(ach en-)510.665 288 R(try belo)102 300 Q
+1.3 -.65(w, t)-.25 H(he tag line is a usage synopsis for the command character)
+.65 E(.)-.55 E F1([count] <contr)102 324 Q(ol-A>)-.18 E F0(Search forw)131 336
+Q(ard)-.1 E/F3 10/Courier@0 SF(count)2.5 E F0(times for the current w)2.5 E
+(ord.)-.1 E F1([count] <contr)102 348 Q(ol-B>)-.18 E F0 -.15(Pa)131 360 S
+(ge backw).15 E(ards)-.1 E F3(count)2.5 E F0(screens.)2.5 E F1([count] <contr)
+102 372 Q(ol-D>)-.18 E F0(Scroll forw)131 384 Q(ard)-.1 E F3(count)2.5 E F0
+(lines.)2.5 E F1([count] <contr)102 396 Q(ol-E>)-.18 E F0(Scroll forw)131 408 Q
+(ard)-.1 E F3(count)2.5 E F0(lines, lea)2.5 E
+(ving the current line and column as is, if possible.)-.2 E F1([count] <contr)
+102 420 Q(ol-F>)-.18 E F0 -.15(Pa)131 432 S(ge forw).15 E(ard)-.1 E F3(count)
+2.5 E F0(screens.)2.5 E F1(<contr)102 444 Q(ol-G>)-.18 E F0
+(Display the \214le information.)131 456 Q F1(<contr)102 468 Q(ol-H>)-.18 E
+([count] h)102 480 Q F0(Mo)131 492 Q .3 -.15(ve t)-.15 H(he cursor back).15 E
+F3(count)2.5 E F0(characters in the current line.)2.5 E F1([count] <contr)102
+504 Q(ol-J>)-.18 E([count] <contr)102 516 Q(ol-N>)-.18 E([count] j)102 528 Q F0
+(Mo)131 540 Q .3 -.15(ve t)-.15 H(he cursor do).15 E(wn)-.25 E F3(count)2.5 E
+F0(lines without changing the current column.)2.5 E F1(<contr)102 552 Q(ol-L>)
+-.18 E(<contr)102 564 Q(ol-R>)-.18 E F0(Repaint the screen.)131 576 Q F1
+([count] <contr)102 588 Q(ol-M>)-.18 E([count] +)102 600 Q F0(Mo)131 612 Q .3
+-.15(ve t)-.15 H(he cursor do).15 E(wn)-.25 E F3(count)2.5 E F0
+(lines to the \214rst nonblank character of that line.)2.5 E F1([count] <contr)
+102 624 Q(ol-P>)-.18 E([count] k)102 636 Q F0(Mo)131 648 Q .3 -.15(ve t)-.15 H
+(he cursor up).15 E F3(count)2.5 E F0
+(lines, without changing the current column.)2.5 E F1(<contr)102 660 Q(ol-T>)
+-.18 E F0(Return to the most recent tag conte)131 672 Q(xt.)-.15 E F1(<contr)
+102 684 Q(ol-U>)-.18 E F0 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(3)535 750
+Q EP
+%%Page: 4 4
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(Scroll backw)131 96 Q(ards)-.1 E
+/F1 10/Courier@0 SF(count)2.5 E F0(lines.)2.5 E/F2 10/Times-Bold@0 SF(<contr)
+102 108 Q(ol-W>)-.18 E F0 .635(Switch to the ne)131 120 R .635(xt lo)-.15 F
+.635(wer screen in the windo)-.25 F 1.935 -.65(w, o)-.25 H 1.435 -.4(r, t).65 H
+3.135(ot).4 G .635(he \214rst screen if there are no lo)353.205 120 R .635
+(wer screens in)-.25 F(the windo)131 132 Q -.65(w.)-.25 G F2(<contr)102 144 Q
+(ol-Y>)-.18 E F0(Scroll backw)131 156 Q(ards)-.1 E F1(count)2.5 E F0
+(lines, lea)2.5 E(ving the current line and column as is, if possible.)-.2 E F2
+(<contr)102 168 Q(ol-Z>)-.18 E F0(Suspend the current editor session.)131 180 Q
+F2(<escape>)102 192 Q F0(Ex)131 204 Q(ecute)-.15 E/F3 10/Courier-Bold@0 SF(ex)
+2.5 E F0(commands or cancel partial commands.)2.5 E F2(<contr)102 216 Q(ol-]>)
+-.18 E F0(Push a tag reference onto the tag stack.)131 228 Q F2(<contr)102 240
+Q(ol-^>)-.18 E F0(Switch to the most recently edited \214le.)131 252 Q F2
+([count] <space>)102 264 Q([count] l)102 276 Q F0(Mo)131 288 Q .3 -.15(ve t)
+-.15 H(he cursor forw).15 E(ard)-.1 E F1(count)2.5 E F0
+(characters without changing the current line.)2.5 E F2
+([count] ! motion shell-ar)102 300 Q(gument\(s\))-.1 E F0(Replace te)131 312 Q
+(xt with results from a shell command.)-.15 E F2([count] # +|-|#)102 324 Q F0
+(Increment or decrement the cursor number)131 336 Q(.)-.55 E F2([count] $)102
+348 Q F0(Mo)131 360 Q .3 -.15(ve t)-.15 H(he cursor to the end of a line.).15 E
+F2(%)102 372 Q F0(Mo)131 372 Q .3 -.15(ve t)-.15 H 2.5(ot).15 G
+(he matching character)169.59 372 Q(.)-.55 E F2(&)102 384 Q F0(Repeat the pre)
+131 384 Q(vious substitution command on the current line.)-.25 E F2
+('<character>)102 396 Q(`<character>)102 408 Q F0(Return to a conte)131 420 Q
+(xt mark)-.15 E(ed by the character)-.1 E F1(<character>)2.5 E F0(.)A F2
+([count] \()102 432 Q F0(Back up)131 444 Q F1(count)2.5 E F0(sentences.)2.5 E
+F2([count] \))102 456 Q F0(Mo)131 468 Q .3 -.15(ve f)-.15 H(orw).15 E(ard)-.1 E
+F1(count)2.5 E F0(sentences.)2.5 E F2([count] ,)102 480 Q F0(Re)131 492 Q -.15
+(ve)-.25 G(rse \214nd character).15 E F1(count)2.5 E F0(times.)2.5 E F2
+([count] -)102 504 Q F0(Mo)131 516 Q .3 -.15(ve t)-.15 H 2.5<6f8c>.15 G
+(rst nonblank of the pre)172.37 516 Q(vious line,)-.25 E F1(count)2.5 E F0
+(times.)2.5 E F2([count] .)102 528 Q F0(Repeat the last)131 540 Q F3(vi)2.5 E
+F0(command that modi\214ed te)2.5 E(xt.)-.15 E F2(/RE<carriage-r)102 552 Q
+(etur)-.18 E(n>)-.15 E(/RE/ [offset]<carriage-r)102 564 Q(etur)-.18 E(n>)-.15 E
+(?RE<carriage-r)102 576 Q(etur)-.18 E(n>)-.15 E(?RE? [offset]<carriage-r)102
+588 Q(etur)-.18 E(n>)-.15 E(N)102 600 Q(n)102 612 Q F0(Search forw)131 612 Q
+(ard or backw)-.1 E(ard for a re)-.1 E(gular e)-.15 E(xpression.)-.15 E F2(0)
+102 624 Q F0(Mo)131 624 Q .3 -.15(ve t)-.15 H 2.5(ot).15 G
+(he \214rst character in the current line.)169.59 624 Q 26.22(:E)102 636 S -.15
+(xe)137.11 636 S(cute an e).15 E 2.5(xc)-.15 G(ommand.)193.73 636 Q F2
+([count] ;)102 648 Q F0(Repeat the last character \214nd)131 660 Q F1(count)2.5
+E F0(times.)2.5 E F2([count] < motion)102 672 Q F0 172.465(4.4BSD July)72 750 R
+(15, 1994)2.5 E(4)535 750 Q EP
+%%Page: 5 5
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Times-Bold@0 SF
+([count] > motion)102 96 Q F0(Shift lines left or right.)131 108 Q F1 2.5(@b)
+102 120 S(uffer)119.16 120 Q F0(Ex)131 132 Q(ecute a named b)-.15 E(uf)-.2 E
+(fer)-.25 E(.)-.55 E F1([count] A)102 144 Q F0
+(Enter input mode, appending the te)131 156 Q(xt after the end of the line.)
+-.15 E F1([count] B)102 168 Q F0(Mo)131 180 Q .3 -.15(ve b)-.15 H(ackw).15 E
+(ards)-.1 E/F2 10/Courier@0 SF(count)2.5 E F0(bigw)2.5 E(ords.)-.1 E F1([b)102
+192 Q(uffer] [count] C)-.2 E F0(Change te)131 204 Q
+(xt from the current position to the end-of-line.)-.15 E F1([b)102 216 Q
+(uffer] D)-.2 E F0(Delete te)131 228 Q
+(xt from the current position to the end-of-line.)-.15 E F1([count] E)102 240 Q
+F0(Mo)131 252 Q .3 -.15(ve f)-.15 H(orw).15 E(ard)-.1 E F2(count)2.5 E F0
+(end-of-bigw)2.5 E(ords.)-.1 E F1([count] F <character>)102 264 Q F0(Search)131
+276 Q F2(count)2.5 E F0(times backw)2.5 E(ard through the current line for)-.1
+E F2(<character>)2.5 E F0(.)A F1([count] G)102 288 Q F0(Mo)131 300 Q .3 -.15
+(ve t)-.15 H 2.5(ol).15 G(ine)169.59 300 Q F2(count)2.5 E F0 2.5(,o)C 2.5(rt)
+224.31 300 S(he last line of the \214le if)232.92 300 Q F2(count)2.5 E F0
+(not speci\214ed.)2.5 E F1([count] H)102 312 Q F0(Mo)131 324 Q .3 -.15(ve t)
+-.15 H 2.5(ot).15 G(he screen line)169.59 324 Q F2(count - 1)2.5 E F0
+(lines belo)2.5 E 2.5(wt)-.25 G(he top of the screen.)334.43 324 Q F1
+([count] I)102 336 Q F0(Enter input mode, inserting the te)131 348 Q
+(xt at the be)-.15 E(ginning of the line.)-.15 E F1([count] J)102 360 Q F0
+(Join lines.)131 372 Q F1([count] L)102 384 Q F0(Mo)131 396 Q .3 -.15(ve t)-.15
+H 2.5(ot).15 G(he screen line)169.59 396 Q F2(count - 1)2.5 E F0(lines abo)2.5
+E .3 -.15(ve t)-.15 H(he bottom of the screen.).15 E F1(M)102 408 Q F0(Mo)131
+408 Q .3 -.15(ve t)-.15 H 2.5(ot).15 G
+(he screen line in the middle of the screen.)169.59 408 Q F1([count] O)102 420
+Q F0(Enter input mode, appending te)131 432 Q(xt in a ne)-.15 E 2.5(wl)-.25 G
+(ine abo)305.86 432 Q .3 -.15(ve t)-.15 H(he current line.).15 E F1([b)102 444
+Q(uffer] P)-.2 E F0(Insert te)131 456 Q(xt from a b)-.15 E(uf)-.2 E(fer)-.25 E
+(.)-.55 E F1(Q)102 468 Q F0(Exit)131 468 Q/F3 10/Courier-Bold@0 SF(vi)2.5 E F0
+(\(or visual\) mode and switch to)2.5 E F3(ex)2.5 E F0(mode.)2.5 E F1
+([count] R)102 480 Q F0
+(Enter input mode, replacing the characters in the current line.)131 492 Q F1
+([b)102 504 Q(uffer] [count] S)-.2 E F0(Substitute)131 516 Q F2(count)2.5 E F0
+(lines.)2.5 E F1([count] T <character>)102 528 Q F0 2.78(Search backw)131 540 R
+(ards,)-.1 E F2(count)5.28 E F0 2.779
+(times, through the current line for the character)5.28 F/F4 10/Times-Italic@0
+SF(after)5.279 E F0 2.779(the speci\214ed)5.279 F F2(<character>)131 552 Q F0
+(.)A F1(U)102 564 Q F0
+(Restore the current line to its state before the cursor last mo)131 564 Q -.15
+(ve)-.15 G 2.5(dt).15 G 2.5(oi)388.99 564 S(t.)399.27 564 Q F1([count] W)102
+576 Q F0(Mo)131 588 Q .3 -.15(ve f)-.15 H(orw).15 E(ard)-.1 E F2(count)2.5 E F0
+(bigw)2.5 E(ords.)-.1 E F1([b)102 600 Q(uffer] [count] X)-.2 E F0(Delete)131
+612 Q F2(count)2.5 E F0(characters before the cursor)2.5 E(.)-.55 E F1([b)102
+624 Q(uffer] [count] Y)-.2 E F0(Cop)131 636 Q 2.5(y\()-.1 G(or `)158.4 636 Q
+(`yank')-.74 E('\))-.74 E F2(count)2.5 E F0(lines into the speci\214ed b)2.5 E
+(uf)-.2 E(fer)-.25 E(.)-.55 E F1(ZZ)102 648 Q F0(Write the \214le and e)131 648
+Q(xit)-.15 E F3(vi)2.5 E F0(.)A F1([count] [[)102 660 Q F0(Back up)131 672 Q F2
+(count)2.5 E F0(section boundaries.)2.5 E F1([count] ]])102 684 Q F0 172.465
+(4.4BSD July)72 750 R(15, 1994)2.5 E(5)535 750 Q EP
+%%Page: 6 6
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(Mo)131 96 Q .3 -.15(ve f)-.15 H
+(orw).15 E(ard)-.1 E/F1 10/Courier@0 SF(count)2.5 E F0(section boundaries.)2.5
+E/F2 10/Times-Bold@0 SF(^)102 108 Q F0(Mo)131 108 Q .3 -.15(ve t)-.15 H 2.5
+<6f8c>.15 G(rst nonblank character on the current line.)172.37 108 Q F2
+([count] _)102 120 Q F0(Mo)131 132 Q .3 -.15(ve d)-.15 H -.25(ow).15 G(n).25 E
+F1(count - 1)2.5 E F0(lines, to the \214rst nonblank character)2.5 E(.)-.55 E
+F2([count] a)102 144 Q F0(Enter input mode, appending the te)131 156 Q
+(xt after the cursor)-.15 E(.)-.55 E F2([count] b)102 168 Q F0(Mo)131 180 Q .3
+-.15(ve b)-.15 H(ackw).15 E(ards)-.1 E F1(count)2.5 E F0 -.1(wo)2.5 G(rds.).1 E
+F2([b)102 192 Q(uffer] [count] c motion)-.2 E F0(Change a re)131 204 Q
+(gion of te)-.15 E(xt.)-.15 E F2([b)102 216 Q(uffer] [count] d motion)-.2 E F0
+(Delete a re)131 228 Q(gion of te)-.15 E(xt.)-.15 E F2([count] e)102 240 Q F0
+(Mo)131 252 Q .3 -.15(ve f)-.15 H(orw).15 E(ard)-.1 E F1(count)2.5 E F0
+(end-of-w)2.5 E(ords.)-.1 E F2([count] f<character>)102 264 Q F0(Search forw)
+131 276 Q(ard,)-.1 E F1(count)2.5 E F0
+(times, through the rest of the current line for)2.5 E F1(<character>)2.5 E F0
+(.)A F2([count] i)102 288 Q F0(Enter input mode, inserting the te)131 300 Q
+(xt before the cursor)-.15 E(.)-.55 E F2 2.5(m<)102 312 S(character>)118.53 312
+Q F0(Sa)131 324 Q .3 -.15(ve t)-.2 H(he current conte).15 E
+(xt \(line and column\) as)-.15 E F1(<character>)2.5 E F0(.)A F2([count] o)102
+336 Q F0(Enter input mode, appending te)131 348 Q(xt in a ne)-.15 E 2.5(wl)-.25
+G(ine under the current line.)305.86 348 Q F2([b)102 360 Q(uffer] p)-.2 E F0
+(Append te)131 372 Q(xt from a b)-.15 E(uf)-.2 E(fer)-.25 E(.)-.55 E F2
+([count] r <character>)102 384 Q F0(Replace)131 396 Q F1(count)2.5 E F0
+(characters.)2.5 E F2([b)102 408 Q(uffer] [count] s)-.2 E F0(Substitute)131 420
+Q F1(count)2.5 E F0
+(characters in the current line starting with the current character)2.5 E(.)
+-.55 E F2([count] t <character>)102 432 Q F0 3.435(Search forw)131 444 R(ard,)
+-.1 E F1(count)5.935 E F0 3.435
+(times, through the current line for the character immediately)5.935 F/F3 10
+/Times-Italic@0 SF(befor)5.935 E(e)-.37 E F1(<character>)131 456 Q F0(.)A F2(u)
+102 468 Q F0(Undo the last change made to the \214le.)131 468 Q F2([count] w)
+102 480 Q F0(Mo)131 492 Q .3 -.15(ve f)-.15 H(orw).15 E(ard)-.1 E F1(count)2.5
+E F0 -.1(wo)2.5 G(rds.).1 E F2([b)102 504 Q(uffer] [count] x)-.2 E F0(Delete)
+131 516 Q F1(count)2.5 E F0(characters.)2.5 E F2([b)102 528 Q
+(uffer] [count] y motion)-.2 E F0(Cop)131 540 Q 2.5(y\()-.1 G(or `)158.4 540 Q
+(`yank')-.74 E('\) a te)-.74 E(xt re)-.15 E(gion speci\214ed by the)-.15 E F1
+(count)2.5 E F0(and motion into a b)2.5 E(uf)-.2 E(fer)-.25 E(.)-.55 E F2
+([count1] z [count2] -|.|+|^|<carriage-r)102 552 Q(etur)-.18 E(n>)-.15 E F0
+(Redra)131 564 Q 1.3 -.65(w, o)-.15 H
+(ptionally repositioning and resizing the screen.).65 E F2([count] {)102 576 Q
+F0(Mo)131 588 Q .3 -.15(ve b)-.15 H(ackw).15 E(ard)-.1 E F1(count)2.5 E F0
+(paragraphs.)2.5 E F2([count] |)102 600 Q F0(Mo)131 612 Q .3 -.15(ve t)-.15 H
+2.5(oas).15 G(peci\214c)177.64 612 Q F3(column)2.5 E F0
+(position on the current line.)2.5 E F2([count] })102 624 Q F0(Mo)131 636 Q .3
+-.15(ve f)-.15 H(orw).15 E(ard)-.1 E F1(count)2.5 E F0(paragraphs.)2.5 E F2
+([count] ~)102 648 Q F0(Re)131 660 Q -.15(ve)-.25 G(rse the case of the ne).15
+E(xt)-.15 E F1(count)2.5 E F0(character\(s\).)2.5 E F2([count] ~ motion)102 672
+Q F0 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(6)535 750 Q EP
+%%Page: 7 7
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(Re)131 96 Q -.15(ve)-.25 G
+(rse the case of the characters in a te).15 E(xt re)-.15 E
+(gion speci\214ed by the)-.15 E/F1 10/Courier@0 SF(count)2.5 E F0(and)2.5 E F1
+(motion)2.5 E F0(.)A/F2 10/Times-Bold@0 SF(<interrupt>)102 108 Q F0
+(Interrupt the current operation.)131 120 Q F2 1.666(VI TEXT INPUT COMMANDS)72
+144 R F0(The follo)102 156 Q(wing section describes the commands a)-.25 E -.25
+(va)-.2 G(ilable in the te).25 E(xt input mode of the)-.15 E/F3 10
+/Courier-Bold@0 SF(vi)2.5 E F0(editor)2.5 E(.)-.55 E F2(<nul>)102 174 Q F0
+(Replay the pre)131 186 Q(vious input.)-.25 E F2(<contr)102 198 Q(ol-D>)-.18 E
+F0(Erase the pre)131 210 Q(vious autoindent character)-.25 E(.)-.55 E F2
+(^<contr)102 222 Q(ol-D>)-.18 E F0
+(Erase all of the autoindent characters, and reset the autoindent le)131 234 Q
+-.15(ve)-.25 G(l.).15 E F2(0<contr)102 246 Q(ol-D>)-.18 E F0
+(Erase all of the autoindent characters.)131 258 Q F2(<contr)102 270 Q(ol-T>)
+-.18 E F0 .076(Insert suf)131 282 R(\214cient)-.25 E F1(<tab>)2.576 E F0(and)
+2.576 E F1(<space>)2.576 E F0 .076(characters to mo)2.576 F .376 -.15(ve t)-.15
+H .076(he cursor forw).15 F .075(ard to a column immediate-)-.1 F
+(ly after the ne)131 294 Q(xt column which is an e)-.15 E -.15(ve)-.25 G 2.5
+(nm).15 G(ultiple of the)305.7 294 Q F2(shiftwidth)2.5 E F0(option.)2.5 E F2
+(<erase>)102 306 Q(<contr)102 318 Q(ol-H>)-.18 E F0(Erase the last character)
+131 330 Q(.)-.55 E F2(<literal next>)102 342 Q F0(Quote the ne)131 354 Q
+(xt character)-.15 E(.)-.55 E F2(<escape>)102 366 Q F0(Resolv)131 378 Q 2.5(ea)
+-.15 G(ll te)170.01 378 Q
+(xt input into the \214le, and return to command mode.)-.15 E F2(<line erase>)
+102 390 Q F0(Erase the current line.)131 402 Q F2(<contr)102 414 Q(ol-W>)-.18 E
+(<w)102 426 Q(ord erase>)-.1 E F0(Erase the last w)131 438 Q 2.5(ord. The)-.1 F
+(de\214nition of w)2.5 E(ord is dependent on the)-.1 E F2(altwerase)2.5 E F0
+(and)2.5 E F2(ttywerase)2.5 E F0(options.)2.5 E F2(<contr)102 450 Q
+(ol-X>[0-9A-F)-.18 E(a-f])-.25 E/F4 10/Symbol SF(*)A F0
+(Insert a character with the speci\214ed he)131 462 Q(xadecimal v)-.15 E
+(alue into the te)-.25 E(xt.)-.15 E F2(<interrupt>)102 474 Q F0(Interrupt te)
+131 486 Q(xt input mode, returning to command mode.)-.15 E F2 1.666
+(EX COMMANDS)72 510 R F0 .163(The follo)102 522 R .163
+(wing section describes the commands a)-.25 F -.25(va)-.2 G .163(ilable in the)
+.25 F F3(ex)2.663 E F0(editor)2.663 E 5.163(.I)-.55 G 2.663(ne)405.333 522 S
+.164(ach entry belo)417.436 522 R 1.464 -.65(w, t)-.25 H .164(he tag line is)
+.65 F 2.5(au)102 534 S(sage synopsis for the command.)113.94 534 Q F2
+(<end-of-\214le>)102 558 Q F0(Scroll the screen.)131 570 Q F2 2.5(!a)102 582 S
+-.1(rg)112.83 582 S(ument\(s\)).1 E([range]! ar)102 594 Q(gument\(s\))-.1 E F0
+(Ex)131 606 Q
+(ecute a shell command, or \214lter lines through a shell command.)-.15 E F2(")
+102 618 Q F0 2.5(Ac)131 618 S(omment.)145.16 618 Q F2
+([range] nu[mber] [count] [\215ags])102 630 Q([range] # [count] [\215ags])102
+642 Q F0(Display the selected lines, each preceded with its line number)131 654
+Q(.)-.55 E F2 2.5(@b)102 666 S(uffer)119.16 666 Q F4(*)102 678 Q F2 -.2(bu)2.5
+G(ffer).2 E F0 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(7)535 750 Q EP
+%%Page: 8 8
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(Ex)131 96 Q(ecute a b)-.15 E(uf)
+-.2 E(fer)-.25 E(.)-.55 E/F1 10/Times-Bold@0 SF([range] d[elete] [b)102 108 Q
+(uffer] [count] [\215ags])-.2 E F0(Delete the lines from the \214le.)131 120 Q
+F1(di[splay] b[uffers] | s[cr)102 132 Q(eens] | t[ags])-.18 E F0(Display b)131
+144 Q(uf)-.2 E(fers, screens or tags.)-.25 E F1(e[dit][!] [+cmd] [\214le])102
+156 Q(ex[!] [+cmd] [\214le])102 168 Q F0(Edit a dif)131 180 Q(ferent \214le.)
+-.25 E F1(exu[sage] [command])102 192 Q F0(Display usage for an)131 204 Q/F2 10
+/Courier-Bold@0 SF(ex)2.5 E F0(command.)2.5 E F1(f[ile] [\214le])102 216 Q F0
+(Display and optionally change the \214le name.)131 228 Q F1(fg [name])102 240
+Q F2(Vi)131 252 Q F0(mode only)2.5 E 5(.F)-.65 G(ore)200.26 252 Q
+(ground the speci\214ed screen.)-.15 E F1([range] g[lobal] /patter)102 264 Q
+(n/ [commands])-.15 E([range] v /patter)102 276 Q(n/ [commands])-.15 E F0
+(Apply commands to lines matching \(or not matching\) a pattern.)131 288 Q F1
+(he[lp])102 300 Q F0(Display a help message.)131 312 Q F1([line] i[nsert][!])
+102 324 Q F0(The input te)131 336 Q
+(xt is inserted before the speci\214ed line.)-.15 E F1
+([range] j[oin][!] [count] [\215ags])102 348 Q F0(Join lines of te)131 360 Q
+(xt together)-.15 E(.)-.55 E F1([range] l[ist] [count] [\215ags])102 372 Q F0
+(Display the lines unambiguously)131 384 Q(.)-.65 E F1(map[!] [lhs rhs])102 396
+Q F0(De\214ne or display maps \(for)131 408 Q F2(vi)2.5 E F0(only\).)2.5 E F1
+([line] ma[rk] <character>)102 420 Q([line] k <character>)102 432 Q F0
+(Mark the line with the mark)131 444 Q/F3 10/Courier@0 SF(<character>)2.5 E F0
+(.)A F1([range] m[o)102 456 Q -.1(ve)-.1 G 2.5(]l).1 G(ine)170.11 456 Q F0(Mo)
+131 468 Q .3 -.15(ve t)-.15 H(he speci\214ed lines after the tar).15 E
+(get line.)-.18 E F1(mk[exr)102 480 Q(c][!] \214le)-.18 E F0(Write the abbre)
+131 492 Q(viations, editor options and maps to the speci\214ed \214le.)-.25 E
+F1(n[ext][!] [\214le ...])102 504 Q F0(Edit the ne)131 516 Q
+(xt \214le from the ar)-.15 E(gument list.)-.18 E F1([line] o[pen] /patter)102
+528 Q(n/ [\215ags])-.15 E F0(Enter open mode.)131 540 Q F1(pr)102 552 Q(e[ser)
+-.18 E -.1(ve)-.1 G(]).1 E F0(Sa)131 564 Q .3 -.15(ve t)-.2 H
+(he \214le in a form that can later be reco).15 E -.15(ve)-.15 G(red using the)
+.15 E F2 -1.834(ex \255r)2.5 F F0(option.)2.5 E F1(pr)102 576 Q -.15(ev)-.18 G
+([ious][!]).15 E F0(Edit the pre)131 588 Q(vious \214le from the ar)-.25 E
+(gument list.)-.18 E F1([range] p[rint] [count] [\215ags])102 600 Q F0
+(Display the speci\214ed lines.)131 612 Q F1([line] pu[t] [b)102 624 Q(uffer])
+-.2 E F0(Append b)131 636 Q(uf)-.2 E(fer contents to the current line.)-.25 E
+F1(q[uit][!])102 648 Q F0(End the editing session.)131 660 Q F1
+([line] r[ead][!] [\214le])102 672 Q F0 172.465(4.4BSD July)72 750 R(15, 1994)
+2.5 E(8)535 750 Q EP
+%%Page: 9 9
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R(Read a \214le.)131 96 Q/F1 10
+/Times-Bold@0 SF -.18(re)102 108 S(c[o).18 E -.1(ve)-.1 G(r] \214le).1 E F0
+(Reco)131 120 Q -.15(ve)-.15 G(r).15 E/F2 10/Courier@0 SF(file)2.5 E F0
+(if it w)2.5 E(as pre)-.1 E(viously sa)-.25 E -.15(ve)-.2 G(d.).15 E F1 -.18
+(re)102 132 S(s[ize] [+|-]size).18 E/F3 10/Courier-Bold@0 SF(Vi)131 144 Q F0
+(mode only)2.5 E 5(.G)-.65 G(ro)202.07 144 Q 2.5(wo)-.25 G 2.5(rs)224.87 144 S
+(hrink the current screen.)234.59 144 Q F1 -.18(re)102 156 S(w[ind][!]).18 E F0
+(Re)131 168 Q(wind the ar)-.25 E(gument list.)-.18 E F1(se[t] [option[=[v)102
+180 Q(alue]] ...] [nooption ...] [option? ...] [all])-.1 E F0
+(Display or set editor options.)131 192 Q F1(sh[ell])102 204 Q F0
+(Run a shell program.)131 216 Q F1(so[ur)102 228 Q(ce] \214le)-.18 E F0
+(Read and e)131 240 Q -.15(xe)-.15 G(cute).15 E F3(ex)2.5 E F0
+(commands from a \214le.)2.5 E F1(sp[lit] [\214le ...])102 252 Q F3(Vi)131 264
+Q F0(mode only)2.5 E 5(.S)-.65 G(plit the screen.)200.41 264 Q F1
+([range] s[ubstitute] [/patter)102 276 Q(n/r)-.15 E
+(eplace/] [options] [count] [\215ags])-.18 E
+([range] & [options] [count] [\215ags])102 288 Q
+([range] ~ [options] [count] [\215ags])102 300 Q F0(Mak)131 312 Q 2.5(es)-.1 G
+(ubstitutions.)160.06 312 Q F1(su[spend][!])102 324 Q(st[op][!])102 336 Q
+(<suspend>)102 348 Q F0(Suspend the edit session.)131 360 Q F1
+(ta[g][!] tagstring)102 372 Q F0
+(Edit the \214le containing the speci\214ed tag.)131 384 Q F1
+(tagp[op][!] [\214le | number])102 396 Q F0
+(Pop to the speci\214ed tag in the tags stack.)131 408 Q F1(unm[ap][!] lhs)102
+420 Q F0(Unmap a mapped string.)131 432 Q F1 -.1(ve)102 444 S([rsion]).1 E F0
+(Display the v)131 456 Q(ersion of the)-.15 E F3(ex/vi)2.5 E F0(editor)2.5 E(.)
+-.55 E F1([line] vi[sual] [type] [count] [\215ags])102 468 Q F3(Ex)131 480 Q F0
+(mode only)2.5 E 5(.E)-.65 G(nter)200.96 480 Q F3(vi)2.5 E F0(.)A F1
+(vi[sual][!] [+cmd] [\214le])102 492 Q F3(Vi)131 504 Q F0(mode only)2.5 E 5(.E)
+-.65 G(dit a ne)200.96 504 Q 2.5<778c>-.25 G(le.)245.43 504 Q F1
+(viu[sage] [command])102 516 Q F0(Display usage for a)131 528 Q F3(vi)2.5 E F0
+(command.)2.5 E F1([range] w[rite][!] [>>] [\214le])102 540 Q
+([range] w[rite] [!] [\214le])102 552 Q([range] wn[!] [>>] [\214le])102 564 Q
+([range] wq[!] [>>] [\214le])102 576 Q F0(Write the \214le.)131 588 Q F1
+([range] x[it][!] [\214le])102 600 Q F0
+(Write the \214le if it has been modi\214ed.)131 612 Q F1([range] ya[nk] [b)102
+624 Q(uffer] [count])-.2 E F0(Cop)131 636 Q 2.5(yt)-.1 G
+(he speci\214ed lines to a b)157.85 636 Q(uf)-.2 E(fer)-.25 E(.)-.55 E F1
+([line] z [type] [count] [\215ags])102 648 Q F0(Adjust the windo)131 660 Q -.65
+(w.)-.25 G 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(9)535 750 Q EP
+%%Page: 10 10
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Times-Bold@0 SF 1.666
+(SET OPTIONS)72 96 R F0 .519(There are a lar)102 108 R .518
+(ge number of options that may be set \(or unset\) to change the editor')-.18 F
+3.018(sb)-.55 G(eha)453.614 108 Q(vior)-.2 E 5.518(.T)-.55 G .518(his section)
+496.982 108 R(describes the options, their abbre)102 120 Q
+(viations and their def)-.25 E(ault v)-.1 E(alues.)-.25 E .095
+(In each entry belo)102 138 R 1.395 -.65(w, t)-.25 H .095
+(he \214rst part of the tag line is the full name of the option, follo).65 F
+.095(wed by an)-.25 F 2.595(ye)-.15 G(qui)487.915 138 Q -.25(va)-.25 G .095
+(lent ab-).25 F(bre)102 150 Q 3.034(viations. The)-.25 F .534
+(part in square brack)3.034 F .533(ets is the def)-.1 F .533(ault v)-.1 F .533
+(alue of the option.)-.25 F .533(Most of the options are boolean,)5.533 F
+(i.e. the)102 162 Q 2.5(ya)-.15 G(re either on or of)140.73 162 Q
+(f, and do not ha)-.25 E .3 -.15(ve a)-.2 H 2.5(na).15 G(ssociated v)298.14 162
+Q(alue.)-.25 E(Options apply to both)102 180 Q/F2 10/Courier-Bold@0 SF(ex)2.5 E
+F0(and)2.5 E F2(vi)2.5 E F0(modes, unless otherwise speci\214ed.)2.5 E F1
+(altwerase [off])102 204 Q F2(Vi)131 216 Q F0(only)2.5 E 5(.S)-.65 G
+(elect an alternate w)175.69 216 Q(ord erase algorithm.)-.1 E F1
+(autoindent, ai [off])102 228 Q F0(Automatically indent ne)131 240 Q 2.5(wl)
+-.25 G(ines.)239.91 240 Q F1(autoprint, ap [off])102 252 Q F2(Ex)131 264 Q F0
+(only)2.5 E 5(.D)-.65 G(isplay the current line automatically)177.35 264 Q(.)
+-.65 E F1(auto)102 276 Q(write, aw [off])-.1 E F0
+(Write modi\214ed \214les automatically when changing \214les.)131 288 Q F1
+(beautify)102 300 Q 2.5(,b)-.55 G 2.5(f[)147.01 300 S(off])156.17 300 Q F0
+(Discard control characters.)131 312 Q F1(cdpath [en)102 324 Q(vir)-.4 E
+(onment v)-.18 E(ariable CDP)-.1 E -.95(AT)-.74 G(H, or curr).95 E(ent dir)-.18
+E(ectory])-.18 E F0(The directory paths used as path pre\214x)131 336 Q
+(es for the)-.15 E F1(cd)2.5 E F0(command.)2.5 E F1(columns, co [80])102 348 Q
+F0(Set the number of columns in the screen.)131 360 Q F1(comment [off])102 372
+Q F2(Vi)131 384 Q F0(only)2.5 E 5(.S)-.65 G(kip leading comments in \214les.)
+175.69 384 Q F1(dir)102 396 Q(ectory)-.18 E 2.5(,d)-.55 G(ir [en)151.26 396 Q
+(vir)-.4 E(onment v)-.18 E(ariable TMPDIR, or /tmp])-.1 E F0
+(The directory where temporary \214les are created.)131 408 Q F1
+(edcompatible, ed [off])102 420 Q F0 .279(Remember the v)131 432 R .279
+(alues of the `)-.25 F(`c')-.74 E 2.779('a)-.74 G .279(nd `)270.344 432 R(`g')
+-.74 E 2.779('s)-.74 G(uf)306.632 432 Q .279(\214ces to the)-.25 F F1
+(substitute)2.78 E F0 .28(commands, instead of initializing)2.78 F
+(them as unset for each ne)131 444 Q 2.5(wc)-.25 G(ommand.)246.27 444 Q F1(err)
+102 456 Q(orbells, eb [off])-.18 E F2(Ex)131 468 Q F0(only)2.5 E 5(.A)-.65 G
+(nnounce error messages with a bell.)177.35 468 Q F1(exr)102 480 Q(c, ex [off])
+-.18 E F0(Ne)131 492 Q -.15(ve)-.25 G 2.5(rr).15 G
+(ead startup \214les in the local directory)160.86 492 Q(.)-.65 E F1
+(extended [off])102 504 Q F0(Re)131 516 Q(gular e)-.15 E(xpressions are e)-.15
+E(xtended \(i.e.)-.15 E/F3 10/Courier@0 SF(egrep)5 E F0(\(1\) style\) e)A
+(xpressions.)-.15 E F1(\215ash [on])102 528 Q F0
+(Flash the screen instead of beeping the k)131 540 Q -.15(ey)-.1 G
+(board on error).15 E(.)-.55 E F1(hardtabs, ht [8])102 552 Q F0
+(Set the spacing between hardw)131 564 Q(are tab settings.)-.1 E F1(ignor)102
+576 Q(ecase, ic [off])-.18 E F0(Ignore case dif)131 588 Q(ferences in re)-.25 E
+(gular e)-.15 E(xpressions.)-.15 E F1 -.1(ke)102 600 S(ytime [6]).1 E F0
+(The 10th')131 612 Q 2.5(so)-.55 G 2.5(fas)181 612 S(econd)197.66 612 Q F2
+(ex/vi)2.5 E F0 -.1(wa)2.5 G(its for a subsequent k).1 E .3 -.15(ey t)-.1 H 2.5
+(oc).15 G(omplete a k)379.5 612 Q .3 -.15(ey m)-.1 H(apping.).15 E F1
+(leftright [off])102 624 Q F2(Vi)131 636 Q F0(only)2.5 E 5(.D)-.65 G 2.5(ol)
+177.35 636 S(eft-right scrolling.)187.63 636 Q F1(lines, li [24])102 648 Q F2
+(Vi)131 660 Q F0(only)2.5 E 5(.S)-.65 G(et the number of lines in the screen.)
+175.69 660 Q F1(lisp [off])102 672 Q F2(Vi)131 684 Q F0(only)2.5 E 5(.M)-.65 G
+(odify v)179.02 684 Q(arious search commands and options to w)-.25 E
+(ork with Lisp.)-.1 E 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(10)530 750 Q
+EP
+%%Page: 11 11
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Times-Italic@0 SF
+(This option is not yet implemented.)131 96 Q/F2 10/Times-Bold@0 SF(list [off])
+102 108 Q F0(Display lines in an unambiguous f)131 120 Q(ashion.)-.1 E F2
+(magic [on])102 132 Q F0 -.35(Tr)131 144 S
+(eat certain characters specially in re).35 E(gular e)-.15 E(xpressions.)-.15 E
+F2(matchtime [7])102 156 Q/F3 10/Courier-Bold@0 SF(Vi)131 168 Q F0(only)2.885 E
+5.385(.T)-.65 G .385(he 10th')177.01 168 R 2.885(so)-.55 G 2.885(fas)221.67 168
+S(econd)239.1 168 Q F3(ex/vi)2.884 E F0 .384
+(pauses on the matching character when the)2.884 F F2(sho)2.884 E(wmatch)-.1 E
+F0(op-)2.884 E(tion is set.)131 180 Q F2(mesg [on])102 192 Q F0
+(Permit messages from other users.)131 204 Q F2(modelines, modeline [off])102
+216 Q F0(Read the \214rst and last fe)131 228 Q 2.5(wl)-.25 G
+(ines of each \214le for)240.18 228 Q F3(ex)2.5 E F0(commands.)2.5 E F1
+(This option will ne)131 246 Q(ver be implemented.)-.15 E F2(number)102 258 Q
+2.5(,n)-.92 G 2.5(u[)145.53 258 S(off])156.92 258 Q F0
+(Precede each line displayed with its current line number)131 270 Q(.)-.55 E F2
+(octal [off])102 282 Q F0(Display unkno)131 294 Q
+(wn characters as octal numbers, instead of the def)-.25 E(ault he)-.1 E
+(xadecimal.)-.15 E F2(open [on])102 306 Q F3(Ex)131 318 Q F0(only)2.5 E 5(.I)
+-.65 G 2.5(ft)173.46 318 S(his option is not set, the)182.07 318 Q F2(open)2.5
+E F0(and)2.5 E F2(visual)2.5 E F0(commands are disallo)2.5 E(wed.)-.25 E F2
+(optimize, opt [on])102 330 Q F3(Vi)131 342 Q F0(only)2.5 E 5(.O)-.65 G
+(ptimize te)177.35 342 Q(xt throughput to dumb terminals.)-.15 E F1
+(This option is not yet implemented.)131 360 Q F2
+(paragraphs, para [IPLPPPQPP LIpplpipbp])102 372 Q F3(Vi)131 384 Q F0(only)2.5
+E 5(.D)-.65 G(e\214ne additional paragraph boundaries for the)177.35 384 Q F2
+({)2.5 E F0(and)2.5 E F2(})2.5 E F0(commands.)2.5 E F2(pr)102 396 Q(ompt [on])
+-.18 E F3(Ex)131 408 Q F0(only)2.5 E 5(.D)-.65 G(isplay a command prompt.)
+177.35 408 Q F2 -.18(re)102 420 S(adonly).18 E 2.5(,r)-.55 G 2.5(o[)148.31 420
+S(off])159.14 420 Q F0(Mark the \214le as read-only)131 432 Q(.)-.65 E F2 -.18
+(re)102 444 S(cdir [/v).18 E(ar/tmp/vi.r)-.1 E(eco)-.18 E -.1(ve)-.1 G(r]).1 E
+F0(The directory where reco)131 456 Q -.15(ve)-.15 G(ry \214les are stored.).15
+E F2 -.18(re)102 468 S(draw).18 E 2.5(,r)-.55 G 2.5(e[)141.63 468 S(off])151.9
+468 Q F3(Vi)131 480 Q F0(only)2.5 E 5(.S)-.65 G
+(imulate an intelligent terminal on a dumb one.)175.69 480 Q F1
+(This option is not yet implemented.)131 498 Q F2 -.18(re)102 510 S(map [on])
+.18 E F0(Remap k)131 522 Q -.15(ey)-.1 G 2.5(su).15 G(ntil resolv)187.41 522 Q
+(ed.)-.15 E F2 -.18(re)102 534 S(port [5]).18 E F0
+(Set the number of lines about which the editor reports changes or yanks.)131
+546 Q F2(ruler [off])102 558 Q F3(Vi)131 570 Q F0(only)2.5 E 5(.D)-.65 G
+(isplay a ro)177.35 570 Q(w/column ruler on the colon command line.)-.25 E F2
+(scr)102 582 Q(oll, scr [windo)-.18 E 2.5(w/2)-.1 G(])194.77 582 Q F0
+(Set the number of lines scrolled.)131 594 Q F2(sections, sect [NHSHH HUnhsh])
+102 606 Q F3(Vi)131 618 Q F0(only)2.5 E 5(.D)-.65 G
+(e\214ne additional section boundaries for the)177.35 618 Q F2([[)2.5 E F0(and)
+2.5 E F2(]])2.5 E F0(commands.)2.5 E F2(shell, sh [en)102 630 Q(vir)-.4 E
+(onment v)-.18 E(ariable SHELL, or /bin/sh])-.1 E F0
+(Select the shell used by the editor)131 642 Q(.)-.55 E F2(shiftwidth, sw [8])
+102 654 Q F0(Set the autoindent and shift command indentation width.)131 666 Q
+F2(sho)102 678 Q(wdirty [off])-.1 E F0 172.465(4.4BSD July)72 750 R(15, 1994)
+2.5 E(11)530 750 Q EP
+%%Page: 12 12
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Courier-Bold@0 SF(Vi)131 96
+Q F0(only)2.5 E 5(.D)-.65 G(isplay an asterisk on the colon command line if th\
+e \214le has been modi\214ed.)177.35 96 Q/F2 10/Times-Bold@0 SF(sho)102 108 Q
+(wmatch, sm [off])-.1 E F1(Vi)131 120 Q F0(only)2.5 E 5(.N)-.65 G
+(ote matching `)177.35 120 Q(`{')-.74 E 2.5('a)-.74 G(nd `)255.37 120 Q(`\(')
+-.74 E 2.5('f)-.74 G(or `)288.87 120 Q(`}')-.74 E 2.5('a)-.74 G(nd `)323.28 120
+Q(`\)')-.74 E 2.5('c)-.74 G(haracters.)357.89 120 Q F2(sho)102 132 Q
+(wmode [off])-.1 E F1(Vi)131 144 Q F0(only)2.5 E 5(.D)-.65 G
+(isplay the current editor mode \(command or input\).)177.35 144 Q F2(sidescr)
+102 156 Q(oll [16])-.18 E F1(Vi)131 168 Q F0(only)2.5 E 5(.S)-.65 G
+(et the amount a left-right scroll will shift.)175.69 168 Q F2(slo)102 180 Q
+-.1(wo)-.1 G(pen, slo).1 E 2.5(w[)-.1 G(off])170.87 180 Q F0
+(Delay display updating during te)131 192 Q(xt input.)-.15 E/F3 10
+/Times-Italic@0 SF(This option is not yet implemented.)131 210 Q F2(sour)102
+222 Q(ceany [off])-.18 E F0(Read startup \214les not o)131 234 Q
+(wned by the current user)-.25 E(.)-.55 E F3(This option will ne)131 252 Q
+(ver be implemented.)-.15 E F2(tabstop, ts [8])102 264 Q F0
+(This option sets tab widths for the editor display)131 276 Q(.)-.65 E F2
+(taglength, tl [0])102 288 Q F0
+(Set the number of signi\214cant characters in tag names.)131 300 Q F2
+(tags, tag [tags /v)102 312 Q(ar/db/libc.tags /sys/k)-.1 E(er)-.1 E(n/tags])
+-.15 E F0(Set the list of tags \214les.)131 324 Q F2(term, ttytype, tty [en)102
+336 Q(vir)-.4 E(onment v)-.18 E(ariable TERM])-.1 E F0(Set the terminal type.)
+131 348 Q F2(terse [off])102 360 Q F0 .759
+(This option has historically made editor messages less v)131 372 R 3.259
+(erbose. It)-.15 F .76(has no ef)3.259 F .76(fect in this implementa-)-.25 F
+(tion.)131 384 Q F2(tildeop)102 396 Q F0(Modify the)131 408 Q F2(~)2.5 E F0
+(command to tak)2.5 E 2.5(ea)-.1 G 2.5(na)259.77 408 S(ssociated motion.)271.71
+408 Q F2(timeout, to [on])102 420 Q F0 -.35(Ti)131 432 S(me out on k).35 E -.15
+(ey)-.1 G 2.5(sw).15 G(hich may be mapped.)209.84 432 Q F2(ttywerase [off])102
+444 Q F1(Vi)131 456 Q F0(only)2.5 E 5(.S)-.65 G
+(elect an alternate erase algorithm.)175.69 456 Q F2 -.1(ve)102 468 S
+(rbose [off]).1 E F0(only)131 480 Q 5(.D)-.65 G(isplay an error message for e)
+162.85 480 Q -.15(ve)-.25 G(ry error).15 E(.)-.55 E F2(w300 [no default])102
+492 Q F1(Vi)131 504 Q F0(only)2.5 E 5(.S)-.65 G(et the windo)175.69 504 Q 2.5
+(ws)-.25 G(ize if the baud rate is less than 1200 baud.)238.49 504 Q F2
+(w1200 [no default])102 516 Q F1(Vi)131 528 Q F0(only)2.5 E 5(.S)-.65 G
+(et the windo)175.69 528 Q 2.5(ws)-.25 G
+(ize if the baud rate is equal to 1200 baud.)238.49 528 Q F2
+(w9600 [no default])102 540 Q F1(Vi)131 552 Q F0(only)2.5 E 5(.S)-.65 G
+(et the windo)175.69 552 Q 2.5(ws)-.25 G
+(ize if the baud rate is greater than 1200 baud.)238.49 552 Q F2(war)102 564 Q
+2.5(n[)-.15 G(on])129.9 564 Q F1(Ex)131 576 Q F0(only)2.979 E 5.479(.T)-.65 G
+.479(his option causes a w)177.198 576 R .479
+(arning message to the terminal if the \214le has been modi\214ed, since it)-.1
+F -.1(wa)131 588 S 2.5(sl).1 G(ast written, before a)151.73 588 Q F2(!)2.5 E F0
+(command.)2.5 E F2(windo)102 600 Q 1.1 -.55(w, w, w)-.1 H 2.5(i[).55 G(en)
+167.19 600 Q(vir)-.4 E(onment v)-.18 E(ariable LINES])-.1 E F0(Set the windo)
+131 612 Q 2.5(ws)-.25 G(ize for the screen.)199.36 612 Q F2(wrapmar)102 624 Q
+(gin, wm [0])-.1 E F1(Vi)131 636 Q F0(only)2.5 E 5(.B)-.65 G
+(reak lines automatically when the)176.8 636 Q 2.5(yr)-.15 G
+(each the right-hand mar)321.9 636 Q(gin.)-.18 E F2(wrapscan, ws [on])102 648 Q
+F0(Set searches to wrap around the end or be)131 660 Q(ginning of the \214le.)
+-.15 E F2(writeany)102 672 Q 2.5(,w)-.55 G 2.5(a[)151.44 672 S(off])162.27 672
+Q F0 -.45(Tu)131 684 S(rn of).45 E 2.5<668c>-.25 G(le-o)171.96 684 Q -.15(ve)
+-.15 G(rwriting checks.).15 E 172.465(4.4BSD July)72 750 R(15, 1994)2.5 E(12)
+530 750 Q EP
+%%Page: 13 13
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R/F1 10/Times-Bold@0 SF(ENVIR)72 96
+Q(ONMENT)-.3 E 1.666(AL V)-.9 F(ARIABLES)-1.35 E/F2 10/Courier@0 SF(COLUMNS)102
+108 Q F0 .895(The number of columns on the screen.)161 108 R .896(This v)5.896
+F .896(alue o)-.25 F -.15(ve)-.15 G .896(rrides an).15 F 3.396(ys)-.15 G .896
+(ystem or terminal speci\214c)433.712 108 R -.25(va)161 120 S 5.317(lues. If)
+.25 F 2.816(the COLUMNS en)5.317 F 2.816(vironmental v)-.4 F 2.816
+(ariable is not set when)-.25 F/F3 10/Courier-Bold@0 SF(ex/vi)5.316 E F0 2.816
+(runs, or the)5.316 F F1(columns)161 132 Q F0(option is e)2.5 E
+(xplicitly reset by the user)-.15 E(,)-.4 E F3(ex/vi)2.5 E F0(enters the v)2.5
+E(alue into the en)-.25 E(vironment.)-.4 E F2(EXINIT)102 144 Q F0 2.5(Al)161
+144 S(ist of)173.5 144 Q F3(ex)2.5 E F0(startup commands, read if the v)2.5 E
+(ariable)-.25 E F2(NEXINIT)2.5 E F0(is not set.)2.5 E F2(HOME)102 156 Q F0 .677
+(The user')161 156 R 3.177(sh)-.55 G .677(ome directory)211.234 156 R 3.177(,u)
+-.65 G .678(sed as the initial directory path for the startup)277.758 156 R F2
+($HOME/.nexrc)3.178 E F0(and)161 168 Q F2($HOME/.exrc)2.865 E F0 2.865
+(\214les. This)2.865 F -.25(va)2.865 G .365(lue is also used as the def).25 F
+.365(ault directory for the)-.1 F F3(vi)2.865 E F1(cd)2.865 E F0(com-)2.865 E
+(mand.)161 180 Q F2(LINES)102 192 Q F0 .629(The number of ro)161 192 R .629
+(ws on the screen.)-.25 F .629(This v)5.629 F .629(alue o)-.25 F -.15(ve)-.15 G
+.629(rrides an).15 F 3.13(ys)-.15 G .63(ystem or terminal speci\214c v)416.08
+192 R(al-)-.25 E 3.123(ues. If)161 204 R .623(the LINES en)3.123 F .623
+(vironmental v)-.4 F .623(ariable is not set when)-.25 F F3(ex/vi)3.122 E F0
+.622(runs, or the)3.122 F F1(lines)3.122 E F0 .622(option is)3.122 F -.15(ex)
+161 216 S(plicitly reset by the user).15 E(,)-.4 E F3(ex/vi)2.5 E F0
+(enters the v)2.5 E(alue into the en)-.25 E(vironment.)-.4 E F2(NEXINIT)102 228
+Q F0 2.5(Al)161 228 S(ist of)173.5 228 Q F3(ex)2.5 E F0(startup commands.)2.5 E
+F2(SHELL)102 240 Q F0(The user')161 240 Q 2.5(ss)-.55 G
+(hell of choice \(see also the)208.77 240 Q F1(shell)2.5 E F0(option\).)2.5 E
+F2(TERM)102 252 Q F0 1.338(The user')161 252 R 3.838(st)-.55 G 1.338
+(erminal type.)210.336 252 R 1.338(The def)6.338 F 1.338(ault is the type `)-.1
+F(`unkno)-.74 E(wn')-.25 E 1.339('. If the TERM en)-.74 F(vironmental)-.4 E
+-.25(va)161 264 S .106(riable is not set when).25 F F3(ex/vi)2.606 E F0 .105
+(runs, or the)2.605 F F1(term)2.605 E F0 .105(option is e)2.605 F .105
+(xplicitly reset by the user)-.15 F(,)-.4 E F3(ex/vi)2.605 E F0(enters the v)
+161 276 Q(alue into the en)-.25 E(vironment.)-.4 E F2(TMPDIR)102 288 Q F0
+(The location used to stored temporary \214les \(see also the)161 288 Q F1(dir)
+2.5 E(ectory)-.18 E F0(option\).)2.5 E F1(ASYNCHR)72 312 Q 1.666(ONOUS EVENTS)
+-.3 F F0(SIGALRM)102 324 Q F3(Vi/ex)167 324 Q F0 1.58(uses this signal for per\
+iodic backups of \214le modi\214cations and to display `)4.08 F(`b)-.74 E(usy')
+-.2 E(')-.74 E(messages when operations are lik)167 336 Q(ely to tak)-.1 E 2.5
+(eal)-.1 G(ong time.)354.54 336 Q(SIGHUP)102 348 Q 18.61(SIGTERM If)102 360 R
+.12(the current b)2.62 F(uf)-.2 E .12(fer has changed since it w)-.25 F .12
+(as last written in its entirety)-.1 F 2.62(,t)-.65 G .12
+(he editor attempts to)457.7 360 R(sa)167 372 Q .493 -.15(ve t)-.2 H .193
+(he modi\214ed \214le so it can be later reco).15 F -.15(ve)-.15 G 2.693
+(red. See).15 F(the)2.694 E F3(vi/ex)2.694 E F0 .194(Reference manual section)
+2.694 F(entitled `)167 384 Q(`Reco)-.74 E -.15(ve)-.15 G(ry').15 E 2.5('f)-.74
+G(or more information.)255.19 384 Q 29.73(SIGINT When)102 396 R .594(an interr\
+upt occurs, the current operation is halted, and the editor returns to the com\
+-)3.094 F .364(mand le)167 408 R -.15(ve)-.25 G 2.864(l. If).15 F .364
+(interrupted during te)2.864 F .364(xt input, the te)-.15 F .364
+(xt already input is resolv)-.15 F .365(ed into the \214le as)-.15 F(if the te)
+167 420 Q(xt input had been normally terminated.)-.15 E 12.51(SIGWINCH The)102
+432 R 2.772(screen is resized.)5.272 F 2.772(See the)7.772 F F3(vi/ex)5.272 E
+F0 2.771(Reference manual section entitled `)5.272 F 2.771(`Sizing the)-.74 F
+(Screen')167 444 Q 2.5('f)-.74 G(or more information.)205.96 444 Q(SIGCONT)102
+456 Q(SIGQ)102 468 Q(UIT)-.1 E(SIGTSTP)102 480 Q F3(Vi/ex)167 480 Q F0
+(ignores these signals.)2.5 E F1 -.1(BU)72 504 S(GS).1 E F0(See the \214le)102
+516 Q F2(nvi/docs/bugs.current)2.5 E F0(for a list of the kno)2.5 E(wn b)-.25 E
+(ugs in this v)-.2 E(ersion.)-.15 E F1(FILES)72 540 Q F2(/bin/sh)102 552 Q F0
+(The def)221 552 Q(ault user shell.)-.1 E F2(/etc/vi.exrc)102 564 Q F0
+(System-wide vi startup \214le.)221 564 Q F2(/tmp)102 576 Q F0 -.7(Te)221 576 S
+(mporary \214le directory).7 E(.)-.65 E F2(/var/tmp/vi.recover)102 588 Q F0
+(The def)5 E(ault reco)-.1 E -.15(ve)-.15 G(ry \214le directory).15 E(.)-.65 E
+F2($HOME/.nexrc)102 600 Q F0(1st choice for user')221 600 Q 2.5(sh)-.55 G
+(ome directory startup \214le.)308.76 600 Q F2($HOME/.exrc)102 612 Q F0
+(2nd choice for user')221 612 Q 2.5(sh)-.55 G(ome directory startup \214le.)
+312.09 612 Q F2(.nexrc)102 624 Q F0
+(1st choice for local directory startup \214le.)221 624 Q F2(.exrc)102 636 Q F0
+(2nd choice for local directory startup \214le.)221 636 Q F1 1.666(SEE ALSO)72
+660 R F2(ctags)102 672 Q F0(\(1\),)A F2(more)5 E F0(\(1\),)A F2(curses)5 E F0
+(\(3\),)A F2(dbopen)5 E F0(\(3\))A(The `)102 696 Q(`V)-.74 E 2.5(iQ)-.6 G
+(uick Reference')145.09 696 Q 2.5('c)-.74 G(ard.)218.2 696 Q 172.465
+(4.4BSD July)72 750 R(15, 1994)2.5 E(13)530 750 Q EP
+%%Page: 14 14
+%%BeginPageSetup
+BP
+%%EndPageSetup
+/F0 10/Times-Roman@0 SF -.834(EX/VI \( 1 \))72 48 R(BSD Reference Manual)
+258.235 48 Q -.834(EX/VI \( 1 \))496.682 48 R -.74(``)102 96 S
+(An Introduction to Display Editing with V).74 E(i')-.6 E(', found in the `)
+-.74 E(`UNIX User')-.74 E 2.5(sM)-.55 G(anual Supplementary)412.2 96 Q
+(Documents')102 108 Q 2.5('s)-.74 G
+(ection of both the 4.3BSD and 4.4BSD manual sets.)159.86 108 Q
+(This document is the closest thing a)5 E -.25(va)-.2 G(il-).25 E
+(able to an introduction to the)102 120 Q/F1 10/Courier-Bold@0 SF(vi)2.5 E F0
+(screen editor)2.5 E(.)-.55 E -.74(``)102 144 S(Ex Reference Manual \(V).74 E
+(ersion 3.7\)')-1.11 E(', found in the `)-.74 E(`UNIX User')-.74 E 2.5(sM)-.55
+G(anual Supplementary Documents')381.92 144 Q 2.5('s)-.74 G(ec-)526.99 144 Q
+(tion of both the 4.3BSD and 4.4BSD manual sets.)102 156 Q
+(This document is the \214nal reference for the)5 E F1(ex)2.5 E F0(editor)2.5 E
+2.5(,a)-.4 G(s)528 156 Q(distrib)102 168 Q
+(uted in most historic 4BSD and System V systems.)-.2 E -.74(``)102 192 S
+(Edit: A tutorial').74 E(', found in the `)-.74 E(`UNIX User')-.74 E 2.5(sM)
+-.55 G(anual Supplementary Documents')300.58 192 Q 2.5('s)-.74 G
+(ection of the 4.3BSD)445.65 192 Q(manual set.)102 204 Q
+(This document is an introduction to a simple v)5 E(ersion of the)-.15 E F1(ex)
+2.5 E F0(screen editor)2.5 E(.)-.55 E -.74(``)102 228 S(Ex/V).74 E 2.5(iR)-.6 G
+(eference Manual')140.38 228 Q(', found in the `)-.74 E(`UNIX User')-.74 E 2.5
+(sM)-.55 G(anual Supplementary Documents')339.39 228 Q 2.5('s)-.74 G
+(ection of the)484.46 228 Q(4.4BSD manual set.)102 240 Q
+(This document is the \214nal reference for the)5 E F1(nex/nvi)2.5 E F0(te)2.5
+E(xt editors, as distrib)-.15 E(uted in)-.2 E(4.4BSD and 4.4BSD-Lite.)102 252 Q
+F1(Roff)102 270 Q F0(source for all of these documents is distrib)2.5 E
+(uted with)-.2 E F1(nex/nvi)2.5 E F0(in the)2.5 E/F2 10/Courier@0 SF
+(nvi/USD.doc)2.5 E F0(directory of the)2.5 E F1(nex/nvi)102 282 Q F0
+(source code.)2.5 E(The \214les `)102 306 Q(`auto)-.74 E(write')-.25 E(', `)
+-.74 E(`input')-.74 E(', `)-.74 E(`quoting')-.74 E(', and `)-.74 E
+(`structures')-.74 E(', found in the)-.74 E F2(nvi/docs/internals)2.5 E F0
+(direc-)2.5 E(tory of the)102 318 Q F1(nex/nvi)2.5 E F0(source code.)2.5 E/F3
+10/Times-Bold@0 SF(HIST)72 342 Q(OR)-.18 E(Y)-.35 E F0(The)102 354 Q F1
+(nex/nvi)2.5 E F0(replacements for the)2.5 E F1(ex/vi)2.5 E F0
+(editor \214rst appeared in 4.4BSD.)2.5 E F3(ST)72 378 Q(AND)-.9 E(ARDS)-.35 E
+F1(Nex/nvi)102 390 Q F0 .1(is close to IEEE Std1003.2 \(`)2.6 F(`POSIX')-.74 E
+2.6('\). That)-.74 F .1(document dif)2.6 F .1(fers from historical)-.25 F F1
+(ex/vi)2.6 E F0 .1(practice in)2.6 F(se)102 402 Q -.15(ve)-.25 G
+(ral places; there are changes to be made on both sides.).15 E 172.465
+(4.4BSD July)72 750 R(15, 1994)2.5 E(14)530 750 Q EP
+%%Trailer
+end
+%%EOF
diff --git a/usr.bin/vi/USD.doc/vi.man/vi.1 b/usr.bin/vi/USD.doc/vi.man/vi.1
new file mode 100644
index 0000000..25f2a2c
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.man/vi.1
@@ -0,0 +1,1294 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.1 8.12 (Berkeley) 7/15/94
+.\"
+.Dd "July 15, 1994"
+.Dt EX/VI 1
+.Os
+.Sh NAME
+.Nm ex, vi, view
+.Nd text editors
+.Sh SYNOPSIS
+.Nm \&ex
+.Op Fl eFRrsv
+.Op Fl c Ar cmd
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.\".Op Fl X Ar \&aw
+.Op Ar "file ..."
+.Nm \&vi
+.Op Fl eFRrv
+.Op Fl c Ar cmd
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.\".Op Fl X Ar \&aw
+.Op Ar "file ..."
+.Nm view
+.Op Fl eFRrv
+.Op Fl c Ar cmd
+.Op Fl t Ar tag
+.Op Fl w Ar size
+.\".Op Fl X Ar \&aw
+.Op Ar "file ..."
+.Sh DESCRIPTION
+.Nm \&Vi
+is a screen oriented text editor.
+.Nm \&Ex
+is a line-oriented text editor.
+.Nm \&Ex
+and
+.Nm \&vi
+are different interfaces to the same program,
+and it is possible to switch back and forth during an edit session.
+.Nm View
+is the equivalent of using the
+.Fl R
+(read-only) option of
+.Nm \&vi .
+.Pp
+This manual page is the one provided with the
+.Nm nex/nvi
+versions of the
+.Nm ex/vi
+text editors.
+.Nm Nex/nvi
+are intended as bug-for-bug compatible replacements for the original
+Fourth Berkeley Software Distribution (4BSD)
+.Nm \&ex
+and
+.Nm \&vi
+programs.
+For the rest of this manual page,
+.Nm nex/nvi
+is used only when it's necessary to distinguish it from the historic
+implementations of
+.Nm ex/vi .
+.Pp
+This manual page is intended for users already familiar with
+.Nm ex/vi .
+Anyone else should almost certainly read a good tutorial on the
+editor before this manual page.
+If you're in an unfamiliar environment, and you absolutely have to
+get work done immediately, read the section after the options
+description, entitled
+.Dq "Fast Startup" .
+It's probably enough to get you going.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+Execute
+.Ar cmd
+immediately after starting the edit session.
+Particularly useful for initial positioning in the file, however
+.Ar cmd
+is not limited to positioning commands.
+This is the POSIX 1003.2 interface for the historic
+.Dq "+cmd"
+syntax.
+.Nm Nex/nvi
+supports both the old and new syntax.
+.It Fl e
+Start editing in ex mode, as if the command name were
+.Nm \&ex .
+.It Fl F
+Don't copy the entire file when first starting to edit.
+(The default is to make a copy in case someone else modifies
+the file during your edit session.)
+.It Fl R
+Start editing in read-only mode, as if the command name was
+.Nm view ,
+or the readonly option was set.
+.It Fl r
+Recover the specified files, or, if no files are specified,
+list the files that could be recovered.
+If no recoverable files by the specified name exist,
+the file is edited as if the
+.Fl r
+option had not been specified.
+.It Fl s
+Enter batch mode; applicable only to
+.Nm \&ex
+edit sessions.
+Batch mode is useful when running
+.Nm \&ex
+scripts.
+Prompts, informative messages and other user oriented message
+are turned off,
+and no startup files or environmental variables are read.
+This is the POSIX 1003.2 interface for the historic
+.Dq \&\-
+argument.
+.Nm \&Nex/nvi
+supports both the old and new syntax.
+.It Fl t
+Start editing at the specified tag.
+(See
+.Xr ctags 1 ).
+.It Fl w
+Set the initial window size to the specified number of lines.
+.It Fl v
+Start editing in vi mode, as if the command name was
+.Nm \&vi
+or
+.Nm view .
+.It Fl X
+Reserved for X11 interfaces.
+.Em "No X11 support is currently implemented."
+.El
+.Pp
+Command input for
+.Nm ex/vi
+is read from the standard input.
+In the
+.Nm \&vi
+interface, it is an error if standard input is not a terminal.
+In the
+.Nm \&ex
+interface, if standard input is not a terminal,
+.Nm \&ex
+will read commands from it regardless, however, the session will be a
+batch mode session, exactly as if the
+.Fl s
+option had been specified.
+.Pp
+.Nm Ex/vi
+exits 0 on success, and greater than 0 if an error occurs.
+.Sh FAST STARTUP
+This section will tell you the minimum amount that you need to
+do simple editing tasks using
+.Nm \&vi .
+If you've never used any screen editor before, you're likely to have
+problems even with this simple introduction.
+In that case you should find someone that already knows
+.Nm \&vi
+and have them walk you through this section.
+.Pp
+.Nm \&Vi
+is a screen editor.
+This means that it takes up almost the entire screen, displaying part
+of the file on each screen line, except for the last line of the screen.
+The last line of the screen is used for you to give commands to
+.Nm \&vi ,
+and for
+.Nm \&vi
+to give information to you.
+.Pp
+The other fact that you need to understand is that
+.Nm \&vi
+is a modeful editor, i.e. you are either entering text or you
+are executing commands, and you have to be in the right mode
+to do one or the other.
+You will be in command mode when you first start editing a file.
+There are commands that switch you into input mode.
+There is only one key that takes you out of input mode,
+and that is the <escape> key.
+(Key names are written using less-than and greater-than signs, e.g.
+<escape> means the
+.Dq escape
+key, usually labeled
+.Dq esc
+on your terminal's keyboard.)
+If you're ever confused as to which mode you're in,
+keep entering the <escape> key until
+.Nm \&vi
+beeps at you.
+(Generally,
+.Nm \&vi
+will beep at you if you try and do something that's not allowed.
+It will also display error messages.)
+.Pp
+To start editing a file, enter the command
+.Dq Li "vi file_name<carriage-return>" .
+The command you should enter as soon as you start editing is
+.Dq Li ":set verbose showmode<carriage-return>" .
+This will make the editor give you verbose error messages and display
+the current mode at the bottom of the screen.
+.Pp
+The commands to move around the file are:
+.Bl -tag -width XXXX -compact
+.It Sy h
+Move the cursor left one character.
+.It Sy j
+Move the cursor down one line.
+.It Sy k
+Move the cursor up one line.
+.It Sy l
+Move the cursor right one character.
+.It Sy <cursor-arrows>
+The cursor arrow keys should work, too.
+.It Sy /text<carriage-return>
+Search for the string
+.Dq text
+in the file, and move the cursor to its first character.
+.El
+.Pp
+The commands to enter new text are:
+.Bl -tag -width XXXX -compact
+.It Sy a
+Append new text,
+.Em after
+the cursor.
+.It Sy i
+Insert new text,
+.Em before
+the cursor.
+.It Sy o
+Open a new line below the line the cursor is on, and start
+entering text.
+.It Sy O
+Open a new line above the line the cursor is on, and start
+entering text.
+.It Sy <escape>
+Once you've entered input mode using the one of the
+.Sy \&a ,
+.Sy \&i ,
+.Sy \&O ,
+or
+.Sy \&o
+commands, use
+.Sy <escape>
+to quit entering text and return to command mode.
+.El
+.Pp
+The commands to copy text are:
+.Bl -tag -width XXXX -compact
+.It Sy yy
+Copy the line the cursor is on.
+.It Sy p
+Append the copied line after the line the cursor is on.
+.El
+.Pp
+The commands to delete text are:
+.Bl -tag -width XXXX -compact
+.It Sy dd
+Delete the line the cursor is on.
+.It Sy x
+Delete the character the cursor is on.
+.El
+.Pp
+The commands to write the file are:
+.Bl -tag -width XXXX -compact
+.It Sy :w<carriage-return>
+Write the file back to the file with the name that you originally used
+as an argument on the
+.Nm \&vi
+command line.
+.It Sy :w file_name<carriage-return>
+Write the file back to the file with the name
+.Dq file_name .
+.El
+.Pp
+The commands to quit editing and exit the editor are:
+.Bl -tag -width XXXX -compact
+.It Sy :q<carriage-return>
+Quit editing and leave vi (if you've modified the file, but not
+saved your changes,
+.Nm \&vi
+will refuse to quit).
+.It Sy :q!<carriage-return>
+Quit, discarding any modifications that you may have made.
+.El
+.Pp
+One final caution.
+Unusual characters can take up more than one column on the screen,
+and long lines can take up more than a single screen line.
+The above commands work on
+.Dq physical
+characters and lines, i.e. they affect the entire line no matter
+how many screen lines it takes up and the entire character no matter
+how many screen columns it takes up.
+.Sh VI COMMANDS
+The following section describes the commands available in the command
+mode of the
+.Nm \&vi
+editor.
+In each entry below, the tag line is a usage synopsis for the command
+character.
+.sp
+.Bl -tag -width "XXXX" -compact
+.It Sy "[count] <control-A>"
+Search forward
+.Li count
+times for the current word.
+.It Sy "[count] <control-B>"
+Page backwards
+.Li count
+screens.
+.It Sy "[count] <control-D>"
+Scroll forward
+.Li count
+lines.
+.It Sy "[count] <control-E>"
+Scroll forward
+.Li count
+lines, leaving the current line and column as is, if possible.
+.It Sy "[count] <control-F>"
+Page forward
+.Li count
+screens.
+.It Sy "<control-G>"
+Display the file information.
+.It Sy "<control-H>"
+.It Sy "[count] h"
+Move the cursor back
+.Li count
+characters in the current line.
+.It Sy "[count] <control-J>"
+.It Sy "[count] <control-N>"
+.It Sy "[count] j"
+Move the cursor down
+.Li count
+lines without changing the current column.
+.It Sy "<control-L>"
+.It Sy "<control-R>"
+Repaint the screen.
+.It Sy "[count] <control-M>"
+.It Sy "[count] +"
+Move the cursor down
+.Li count
+lines to the first nonblank character of that line.
+.It Sy "[count] <control-P>"
+.It Sy "[count] k"
+Move the cursor up
+.Li count
+lines, without changing the current column.
+.It Sy "<control-T>"
+Return to the most recent tag context.
+.It Sy "<control-U>"
+Scroll backwards
+.Li count
+lines.
+.It Sy "<control-W>"
+Switch to the next lower screen in the window, or, to the first
+screen if there are no lower screens in the window.
+.It Sy "<control-Y>"
+Scroll backwards
+.Li count
+lines, leaving the current line and column as is, if possible.
+.It Sy "<control-Z>"
+Suspend the current editor session.
+.It Sy "<escape>"
+Execute
+.Nm \&ex
+commands or cancel partial commands.
+.It Sy "<control-]>"
+Push a tag reference onto the tag stack.
+.It Sy "<control-^>"
+Switch to the most recently edited file.
+.It Sy "[count] <space>"
+.It Sy "[count] l"
+Move the cursor forward
+.Li count
+characters without changing the current line.
+.It Sy "[count] ! motion shell-argument(s)"
+Replace text with results from a shell command.
+.It Sy "[count] # +|-|#"
+Increment or decrement the cursor number.
+.It Sy "[count] $"
+Move the cursor to the end of a line.
+.It Sy "%"
+Move to the matching character.
+.It Sy "&"
+Repeat the previous substitution command on the current line.
+.It Sy "'<character>"
+.It Sy "`<character>"
+Return to a context marked by the character
+.Li <character> .
+.It Sy "[count] ("
+Back up
+.Li count
+sentences.
+.It Sy "[count] )"
+Move forward
+.Li count
+sentences.
+.It Sy "[count] ,"
+Reverse find character
+.Li count
+times.
+.It Sy "[count] -"
+Move to first nonblank of the previous line,
+.Li count
+times.
+.It Sy "[count] ."
+Repeat the last
+.Nm \&vi
+command that modified text.
+.It Sy "/RE<carriage-return>"
+.It Sy "/RE/ [offset]<carriage-return>"
+.It Sy "?RE<carriage-return>"
+.It Sy "?RE? [offset]<carriage-return>"
+.It Sy "N"
+.It Sy "n"
+Search forward or backward for a regular expression.
+.It Sy "0"
+Move to the first character in the current line.
+.It Sy ":"
+Execute an ex command.
+.It Sy "[count] ;"
+Repeat the last character find
+.Li count
+times.
+.It Sy "[count] < motion"
+.It Sy "[count] > motion"
+Shift lines left or right.
+.It Sy "@ buffer"
+Execute a named buffer.
+.It Sy "[count] A"
+Enter input mode, appending the text after the end of the line.
+.It Sy "[count] B"
+Move backwards
+.Li count
+bigwords.
+.It Sy "[buffer] [count] C"
+Change text from the current position to the end-of-line.
+.It Sy "[buffer] D"
+Delete text from the current position to the end-of-line.
+.It Sy "[count] E"
+Move forward
+.Li count
+end-of-bigwords.
+.It Sy "[count] F <character>"
+Search
+.Li count
+times backward through the current line for
+.Li <character> .
+.It Sy "[count] G"
+Move to line
+.Li count ,
+or the last line of the file if
+.Li count
+not specified.
+.It Sy "[count] H"
+Move to the screen line
+.Li "count - 1"
+lines below the top of the screen.
+.It Sy "[count] I"
+Enter input mode, inserting the text at the beginning of the line.
+.It Sy "[count] J"
+Join lines.
+.It Sy "[count] L"
+Move to the screen line
+.Li "count - 1"
+lines above the bottom of the screen.
+.It Sy " M"
+Move to the screen line in the middle of the screen.
+.It Sy "[count] O"
+Enter input mode, appending text in a new line above the current line.
+.It Sy "[buffer] P"
+Insert text from a buffer.
+.It Sy "Q"
+Exit
+.Nm \&vi
+(or visual) mode and switch to
+.Nm \&ex
+mode.
+.It Sy "[count] R"
+Enter input mode, replacing the characters in the current line.
+.It Sy "[buffer] [count] S"
+Substitute
+.Li count
+lines.
+.It Sy "[count] T <character>"
+Search backwards,
+.Li count
+times,
+through the current line for the character
+.Em after
+the specified
+.Li <character> .
+.It Sy "U"
+Restore the current line to its state before the cursor last
+moved to it.
+.It Sy "[count] W"
+Move forward
+.Li count
+bigwords.
+.It Sy "[buffer] [count] X"
+Delete
+.Li count
+characters before the cursor.
+.It Sy "[buffer] [count] Y"
+Copy (or
+.Dq yank )
+.Li count
+lines into the specified buffer.
+.It Sy "ZZ"
+Write the file and exit
+.Nm \&vi .
+.It Sy "[count] [["
+Back up
+.Li count
+section boundaries.
+.It Sy "[count] ]]"
+Move forward
+.Li count
+section boundaries.
+.It Sy "\&^"
+Move to first nonblank character on the current line.
+.It Sy "[count] _"
+Move down
+.Li "count - 1"
+lines, to the first nonblank character.
+.It Sy "[count] a"
+Enter input mode, appending the text after the cursor.
+.It Sy "[count] b"
+Move backwards
+.Li count
+words.
+.It Sy "[buffer] [count] c motion"
+Change a region of text.
+.It Sy "[buffer] [count] d motion"
+Delete a region of text.
+.It Sy "[count] e"
+Move forward
+.Li count
+end-of-words.
+.It Sy "[count] f<character>"
+Search forward,
+.Li count
+times, through the rest of the current line for
+.Li <character> .
+.It Sy "[count] i"
+Enter input mode, inserting the text before the cursor.
+.It Sy "m <character>"
+Save the current context (line and column) as
+.Li <character> .
+.It Sy "[count] o"
+Enter input mode, appending text in a new line under the current line.
+.It Sy "[buffer] p"
+Append text from a buffer.
+.It Sy "[count] r <character>
+Replace
+.Li count
+characters.
+.It Sy "[buffer] [count] s"
+Substitute
+.Li count
+characters in the current line starting with the current character.
+.It Sy "[count] t <character>"
+Search forward,
+.Li count
+times, through the current line for the character immediately
+.Em before
+.Li <character> .
+.It Sy "u"
+Undo the last change made to the file.
+.It Sy "[count] w"
+Move forward
+.Li count
+words.
+.It Sy "[buffer] [count] x"
+Delete
+.Li count
+characters.
+.It Sy "[buffer] [count] y motion"
+Copy (or
+.Dq yank )
+a text region specified by the
+.Li count
+and motion into a buffer.
+.It Sy "[count1] z [count2] -|.|+|^|<carriage-return>"
+Redraw, optionally repositioning and resizing the screen.
+.It Sy "[count] {"
+Move backward
+.Li count
+paragraphs.
+.It Sy "[count] |"
+Move to a specific
+.Em column
+position on the current line.
+.It Sy "[count] }"
+Move forward
+.Li count
+paragraphs.
+.It Sy "[count] ~"
+Reverse the case of the next
+.Li count
+character(s).
+.It Sy "[count] ~ motion"
+Reverse the case of the characters in a text region specified by the
+.Li count
+and
+.Li motion .
+.It Sy "<interrupt>"
+Interrupt the current operation.
+.El
+.Sh VI TEXT INPUT COMMANDS
+The following section describes the commands available in the text
+input mode of the
+.Nm \&vi
+editor.
+.Pp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<nul>"
+Replay the previous input.
+.It Sy "<control-D>"
+Erase the previous autoindent character.
+.It Sy "^<control-D>"
+Erase all of the autoindent characters, and reset the autoindent level.
+.It Sy "0<control-D>"
+Erase all of the autoindent characters.
+.It Sy "<control-T>"
+Insert sufficient
+.Li <tab>
+and
+.Li <space>
+characters to move the cursor forward to a column immediately
+after the next column which is an even multiple of the
+.Sy shiftwidth
+option.
+.It Sy "<erase>
+.It Sy "<control-H>"
+Erase the last character.
+.It Sy "<literal next>"
+Quote the next character.
+.It Sy "<escape>
+Resolve all text input into the file, and return to command mode.
+.It Sy "<line erase>
+Erase the current line.
+.It Sy "<control-W>"
+.It Sy "<word erase>
+Erase the last word.
+The definition of word is dependent on the
+.Sy altwerase
+and
+.Sy ttywerase
+options.
+.It Sy "<control-X>[0-9A-Fa-f]*"
+Insert a character with the specified hexadecimal value into the text.
+.It Sy "<interrupt>"
+Interrupt text input mode, returning to command mode.
+.El
+.Sh EX COMMANDS
+The following section describes the commands available in the
+.Nm \&ex
+editor.
+In each entry below, the tag line is a usage synopsis for the command.
+.sp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<end-of-file>"
+Scroll the screen.
+.It Sy "! argument(s)"
+.It Sy "[range]! argument(s)"
+Execute a shell command, or filter lines through a shell command.
+.It Sy \&"
+A comment.
+.It Sy "[range] nu[mber] [count] [flags]"
+.It Sy "[range] # [count] [flags]"
+Display the selected lines, each preceded with its line number.
+.It Sy "@ buffer"
+.It Sy "* buffer"
+Execute a buffer.
+.It Sy "[range] d[elete] [buffer] [count] [flags]"
+Delete the lines from the file.
+.It Sy "di[splay] b[uffers] | s[creens] | t[ags]"
+Display buffers, screens or tags.
+.It Sy "e[dit][!] [+cmd] [file]"
+.It Sy "ex[!] [+cmd] [file]"
+Edit a different file.
+.It Sy "exu[sage] [command]"
+Display usage for an
+.Nm \&ex
+command.
+.It Sy "f[ile] [file]"
+Display and optionally change the file name.
+.It Sy "fg [name]"
+.Nm \&Vi
+mode only.
+Foreground the specified screen.
+.It Sy "[range] g[lobal] /pattern/ [commands]"
+.It Sy "[range] v /pattern/ [commands]
+Apply commands to lines matching (or not matching) a pattern.
+.It Sy "he[lp]"
+Display a help message.
+.It Sy "[line] i[nsert][!]"
+The input text is inserted before the specified line.
+.It Sy "[range] j[oin][!] [count] [flags]"
+Join lines of text together.
+.It Sy "[range] l[ist] [count] [flags]"
+Display the lines unambiguously.
+.It Sy "map[!] [lhs rhs]"
+Define or display maps (for
+.Nm \&vi
+only).
+.It Sy "[line] ma[rk] <character>"
+.It Sy "[line] k <character>"
+Mark the line with the mark
+.Li <character> .
+.It Sy "[range] m[ove] line"
+Move the specified lines after the target line.
+.It Sy "mk[exrc][!] file"
+Write the abbreviations, editor options and maps to the specified
+file.
+.It Sy "n[ext][!] [file ...]"
+Edit the next file from the argument list.
+.It Sy "[line] o[pen] /pattern/ [flags]"
+Enter open mode.
+.It Sy "pre[serve]"
+Save the file in a form that can later be recovered using the
+.Nm \&ex
+.Fl r
+option.
+.It Sy "prev[ious][!]"
+Edit the previous file from the argument list.
+.It Sy "[range] p[rint] [count] [flags]"
+Display the specified lines.
+.It Sy "[line] pu[t] [buffer]"
+Append buffer contents to the current line.
+.It Sy "q[uit][!]"
+End the editing session.
+.It Sy "[line] r[ead][!] [file]"
+Read a file.
+.It Sy "rec[over] file"
+Recover
+.Li file
+if it was previously saved.
+.It Sy "res[ize] [+|-]size"
+.Nm \&Vi
+mode only.
+Grow or shrink the current screen.
+.It Sy "rew[ind][!]"
+Rewind the argument list.
+.It Sy "se[t] [option[=[value]] ...] [nooption ...] [option? ...] [all]"
+Display or set editor options.
+.It Sy "sh[ell]"
+Run a shell program.
+.It Sy "so[urce] file"
+Read and execute
+.Nm \&ex
+commands from a file.
+.It Sy "sp[lit] [file ...]"
+.Nm \&Vi
+mode only.
+Split the screen.
+.It Sy "[range] s[ubstitute] [/pattern/replace/] [options] [count] [flags]"
+.It Sy "[range] & [options] [count] [flags]"
+.It Sy "[range] ~ [options] [count] [flags]"
+Make substitutions.
+.It Sy "su[spend][!]"
+.It Sy "st[op][!]"
+.It Sy <suspend>
+Suspend the edit session.
+.It Sy "ta[g][!] tagstring"
+Edit the file containing the specified tag.
+.It Sy "tagp[op][!] [file | number]"
+Pop to the specified tag in the tags stack.
+.It Sy "unm[ap][!] lhs"
+Unmap a mapped string.
+.It Sy "ve[rsion]"
+Display the version of the
+.Nm \&ex/vi
+editor.
+.It Sy "[line] vi[sual] [type] [count] [flags]"
+.Nm \&Ex
+mode only.
+Enter
+.Nm \&vi .
+.It Sy "vi[sual][!] [+cmd] [file]"
+.Nm \&Vi
+mode only.
+Edit a new file.
+.It Sy "viu[sage] [command]"
+Display usage for a
+.Nm \&vi
+command.
+.It Sy "[range] w[rite][!] [>>] [file]"
+.It Sy "[range] w[rite] [!] [file]"
+.It Sy "[range] wn[!] [>>] [file]"
+.It Sy "[range] wq[!] [>>] [file]"
+Write the file.
+.It Sy "[range] x[it][!] [file]"
+Write the file if it has been modified.
+.It Sy "[range] ya[nk] [buffer] [count]"
+Copy the specified lines to a buffer.
+.It Sy "[line] z [type] [count] [flags]"
+Adjust the window.
+.El
+.Sh SET OPTIONS
+There are a large number of options that may be set (or unset) to
+change the editor's behavior.
+This section describes the options, their abbreviations and their
+default values.
+.Pp
+In each entry below, the first part of the tag line is the full name
+of the option, followed by any equivalent abbreviations.
+The part in square brackets is the default value of the option.
+Most of the options are boolean, i.e. they are either on or off,
+and do not have an associated value.
+.Pp
+Options apply to both
+.Nm \&ex
+and
+.Nm \&vi
+modes, unless otherwise specified.
+.sp
+.Bl -tag -width "XXXX" -compact
+.It Sy "altwerase [off]"
+.Nm \&Vi
+only.
+Select an alternate word erase algorithm.
+.It Sy "autoindent, ai [off]"
+Automatically indent new lines.
+.It Sy "autoprint, ap [off]"
+.Nm \&Ex
+only.
+Display the current line automatically.
+.It Sy "autowrite, aw [off]"
+Write modified files automatically when changing files.
+.It Sy "beautify, bf [off]"
+Discard control characters.
+.It Sy "cdpath [environment variable CDPATH, or current directory]"
+The directory paths used as path prefixes for the
+.Sy cd
+command.
+.It Sy "columns, co [80]"
+Set the number of columns in the screen.
+.It Sy "comment [off]"
+.Nm \&Vi
+only.
+Skip leading comments in files.
+.It Sy "directory, dir [environment variable TMPDIR, or /tmp]"
+The directory where temporary files are created.
+.It Sy "edcompatible, ed [off]"
+Remember the values of the
+.Dq \&c
+and
+.Dq \&g
+suffices to the
+.Sy substitute
+commands, instead of initializing them as unset for each new
+command.
+.It Sy "errorbells, eb [off]"
+.Nm \&Ex
+only.
+Announce error messages with a bell.
+.It Sy "exrc, ex [off]"
+Never read startup files in the local directory.
+.It Sy "extended [off]"
+Regular expressions are extended (i.e.
+.Xr egrep 1
+style) expressions.
+.It Sy "flash [on]"
+Flash the screen instead of beeping the keyboard on error.
+.It Sy "hardtabs, ht [8]"
+Set the spacing between hardware tab settings.
+.It Sy "ignorecase, ic [off]"
+Ignore case differences in regular expressions.
+.It Sy "keytime [6]"
+The 10th's of a second
+.Nm ex/vi
+waits for a subsequent key to complete a key mapping.
+.It Sy "leftright [off]"
+.Nm \&Vi
+only.
+Do left-right scrolling.
+.It Sy "lines, li [24]"
+.Nm \&Vi
+only.
+Set the number of lines in the screen.
+.It Sy "lisp [off]"
+.Nm \&Vi
+only.
+Modify various search commands and options to work with Lisp.
+.Pp
+.Em "This option is not yet implemented."
+.It Sy "list [off]"
+Display lines in an unambiguous fashion.
+.It Sy "magic [on]"
+Treat certain characters specially in regular expressions.
+.It Sy "matchtime [7]"
+.Nm \&Vi
+only.
+The 10th's of a second
+.Nm ex/vi
+pauses on the matching character when the
+.Sy showmatch
+option is set.
+.It Sy "mesg [on]"
+Permit messages from other users.
+.It Sy "modelines, modeline [off]"
+Read the first and last few lines of each file for
+.Nm ex
+commands.
+.Pp
+.Em "This option will never be implemented."
+.It Sy "number, nu [off]"
+Precede each line displayed with its current line number.
+.It Sy "octal [off]"
+Display unknown characters as octal numbers, instead of the default
+hexadecimal.
+.It Sy "open [on]"
+.Nm \&Ex
+only.
+If this option is not set, the
+.Sy open
+and
+.Sy visual
+commands are disallowed.
+.It Sy "optimize, opt [on]"
+.Nm \&Vi
+only.
+Optimize text throughput to dumb terminals.
+.Pp
+.Em "This option is not yet implemented."
+.It Sy "paragraphs, para [IPLPPPQPP LIpplpipbp]"
+.Nm \&Vi
+only.
+Define additional paragraph boundaries for the
+.Sy \&{
+and
+.Sy \&}
+commands.
+.It Sy "prompt [on]"
+.Nm \&Ex
+only.
+Display a command prompt.
+.It Sy "readonly, ro [off]"
+Mark the file as read-only.
+.It Sy "recdir [/var/tmp/vi.recover]"
+The directory where recovery files are stored.
+.It Sy "redraw, re [off]"
+.Nm \&Vi
+only.
+Simulate an intelligent terminal on a dumb one.
+.Pp
+.Em "This option is not yet implemented."
+.It Sy "remap [on]"
+Remap keys until resolved.
+.It Sy "report [5]"
+Set the number of lines about which the editor reports changes
+or yanks.
+.It Sy "ruler [off]"
+.Nm \&Vi
+only.
+Display a row/column ruler on the colon command line.
+.It Sy "scroll, scr [window / 2]"
+Set the number of lines scrolled.
+.It Sy "sections, sect [NHSHH HUnhsh]"
+.Nm \&Vi
+only.
+Define additional section boundaries for the
+.Sy \&[[
+and
+.Sy \&]]
+commands.
+.It Sy "shell, sh [environment variable SHELL, or /bin/sh]"
+Select the shell used by the editor.
+.It Sy "shiftwidth, sw [8]"
+Set the autoindent and shift command indentation width.
+.It Sy "showdirty [off]"
+.Nm \&Vi
+only.
+Display an asterisk on the colon command line if the file has been modified.
+.It Sy "showmatch, sm [off]"
+.Nm \&Vi
+only.
+Note matching
+.Dq \&{
+and
+.Dq \&(
+for
+.Dq \&}
+and
+.Dq \&)
+characters.
+.It Sy "showmode [off]"
+.Nm \&Vi
+only.
+Display the current editor mode (command or input).
+.It Sy "sidescroll [16]"
+.Nm \&Vi
+only.
+Set the amount a left-right scroll will shift.
+.It Sy "slowopen, slow [off]"
+Delay display updating during text input.
+.Pp
+.Em "This option is not yet implemented."
+.It Sy "sourceany [off]"
+Read startup files not owned by the current user.
+.Pp
+.Em "This option will never be implemented."
+.It Sy "tabstop, ts [8]"
+This option sets tab widths for the editor display.
+.It Sy "taglength, tl [0]"
+Set the number of significant characters in tag names.
+.It Sy "tags, tag [tags /var/db/libc.tags /sys/kern/tags]"
+Set the list of tags files.
+.It Sy "term, ttytype, tty [environment variable TERM]"
+Set the terminal type.
+.It Sy "terse [off]"
+This option has historically made editor messages less verbose.
+It has no effect in this implementation.
+.It Sy "tildeop"
+Modify the
+.Sy \&~
+command to take an associated motion.
+.It Sy "timeout, to [on]"
+Time out on keys which may be mapped.
+.It Sy "ttywerase [off]"
+.Nm \&Vi
+only.
+Select an alternate erase algorithm.
+.It Sy "verbose [off]"
+.NM \&Vi
+only.
+Display an error message for every error.
+.It Sy "w300 [no default]"
+.Nm \&Vi
+only.
+Set the window size if the baud rate is less than 1200 baud.
+.It Sy "w1200 [no default]"
+.Nm \&Vi
+only.
+Set the window size if the baud rate is equal to 1200 baud.
+.It Sy "w9600 [no default]"
+.Nm \&Vi
+only.
+Set the window size if the baud rate is greater than 1200 baud.
+.It Sy "warn [on]"
+.Nm \&Ex
+only.
+This option causes a warning message to the terminal if the file has
+been modified, since it was last written, before a
+.Sy \&!
+command.
+.It Sy "window, w, wi [environment variable LINES]"
+Set the window size for the screen.
+.It Sy "wrapmargin, wm [0]"
+.Nm \&Vi
+only.
+Break lines automatically when they reach the right-hand margin.
+.It Sy "wrapscan, ws [on]"
+Set searches to wrap around the end or beginning of the file.
+.It Sy "writeany, wa [off]"
+Turn off file-overwriting checks.
+.El
+.Sh ENVIRONMENTAL VARIABLES
+.Bl -tag -width "COLUMNSXX" -compact
+.It Ev COLUMNS
+The number of columns on the screen.
+This value overrides any system or terminal specific values.
+If the COLUMNS environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy columns
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev EXINIT
+A list of
+.Nm \&ex
+startup commands, read if the variable
+.Ev NEXINIT
+is not set.
+.It Ev HOME
+The user's home directory, used as the initial directory path
+for the startup
+.Pa $HOME/.nexrc
+and
+.Pa $HOME/.exrc
+files.
+This value is also used as the default directory for the
+.Nm \&vi
+.Sy \&cd
+command.
+.It Ev LINES
+The number of rows on the screen.
+This value overrides any system or terminal specific values.
+If the LINES environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy lines
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev NEXINIT
+A list of
+.Nm \&ex
+startup commands.
+.It Ev SHELL
+The user's shell of choice (see also the
+.Sy shell
+option).
+.It Ev TERM
+The user's terminal type.
+The default is the type
+.Dq unknown .
+If the TERM environmental variable is not set when
+.Nm ex/vi
+runs, or the
+.Sy term
+option is explicitly reset by the user,
+.Nm ex/vi
+enters the value into the environment.
+.It Ev TMPDIR
+The location used to stored temporary files (see also the
+.Sy directory
+option).
+.El
+.Sh ASYNCHRONOUS EVENTS
+.Bl -tag -width "SIGWINCHXX" -compact
+.It SIGALRM
+.Nm \&Vi/ex
+uses this signal for periodic backups of file modifications
+and to display
+.Dq busy
+messages when operations are likely to take a long time.
+.It SIGHUP
+.It SIGTERM
+If the current buffer has changed since it was last written in its
+entirety, the editor attempts to save the modified file so it can
+be later recovered.
+See the
+.Nm \&vi/ex
+Reference manual section entitled
+.Dq Recovery
+for more information.
+.It SIGINT
+When an interrupt occurs,
+the current operation is halted,
+and the editor returns to the command level.
+If interrupted during text input,
+the text already input is resolved into the file as if the text
+input had been normally terminated.
+.It SIGWINCH
+The screen is resized.
+See the
+.Nm \&vi/ex
+Reference manual section entitled
+.Dq "Sizing the Screen"
+for more information.
+.It SIGCONT
+.It SIGQUIT
+.It SIGTSTP
+.Nm \&Vi/ex
+ignores these signals.
+.El
+.Sh BUGS
+See the file
+.Pa nvi/docs/bugs.current
+for a list of the known bugs in this version.
+.Sh FILES
+.Bl -tag -width /var/tmp/vi.recover -compact
+.It Pa /bin/sh
+The default user shell.
+.It Pa /etc/vi.exrc
+System-wide vi startup file.
+.It Pa /tmp
+Temporary file directory.
+.It Pa /var/tmp/vi.recover
+The default recovery file directory.
+.It Pa $HOME/.nexrc
+1st choice for user's home directory startup file.
+.It Pa $HOME/.exrc
+2nd choice for user's home directory startup file.
+.It Pa .nexrc
+1st choice for local directory startup file.
+.It Pa .exrc
+2nd choice for local directory startup file.
+.El
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr more 1 ,
+.Xr curses 3 ,
+.Xr dbopen 3
+.sp
+The
+.Dq "Vi Quick Reference"
+card.
+.sp
+.Dq "\&An Introduction to Display Editing with Vi" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents"
+section of both the 4.3BSD and 4.4BSD manual sets.
+This document is the closest thing available to an introduction to the
+.Nm \&vi
+screen editor.
+.sp
+.Dq "\&Ex Reference Manual (Version 3.7)" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents"
+section of both the 4.3BSD and 4.4BSD manual sets.
+This document is the final reference for the
+.Nm \&ex
+editor, as distributed in most historic 4BSD and System V systems.
+.sp
+.Dq "Edit: A tutorial" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents"
+section of the 4.3BSD manual set.
+This document is an introduction to a simple version of the
+.Nm \&ex
+screen editor.
+.sp
+.Dq "\&Ex/Vi Reference Manual" ,
+found in the
+.Dq "UNIX User's Manual Supplementary Documents"
+section of the 4.4BSD manual set.
+This document is the final reference for the
+.Nm \&nex/nvi
+text editors, as distributed in 4.4BSD and 4.4BSD-Lite.
+.Pp
+.Nm Roff
+source for all of these documents is distributed with
+.Nm nex/nvi
+in the
+.Pa nvi/USD.doc
+directory of the
+.Nm nex/nvi
+source code.
+.sp
+The files
+.Dq autowrite ,
+.Dq input ,
+.Dq quoting ,
+and
+.Dq structures ,
+found in the
+.Pa nvi/docs/internals
+directory of the
+.Nm nex/nvi
+source code.
+.Sh HISTORY
+The
+.Nm nex/nvi
+replacements for the
+.Nm ex/vi
+editor first appeared in 4.4BSD.
+.Sh STANDARDS
+.Nm \&Nex/nvi
+is close to IEEE Std1003.2 (``POSIX'').
+That document differs from historical
+.Nm ex/vi
+practice in several places; there are changes to be made on both sides.
diff --git a/usr.bin/vi/USD.doc/vi.ref/Makefile b/usr.bin/vi/USD.doc/vi.ref/Makefile
new file mode 100644
index 0000000..2690f86
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.16 (Berkeley) 8/15/94
+
+DIR= usd/13.viref
+SRCS= vi.ref ex.cmd.roff set.opt.roff vi.cmd.roff ref.so
+MACROS= -me
+CLEANFILES+=vi.ref.txt index index.so
+
+paper.ps: vi.ref index.so
+ soelim vi.ref | ${TBL} | ${ROFF} > ${.TARGET}
+
+vi.ref.txt: vi.ref index.so
+ soelim vi.ref | ${TBL} | groff ${MACROS} -Tascii > $@
+
+index.so: vi.ref
+ # Build index.so, side-effect of building the paper.
+ soelim vi.ref | ${TBL} | ${ROFF} > /dev/null
+ sed -e 's/MINUSSIGN/\\-/' \
+ -e 's/DOUBLEQUOTE/""/' \
+ -e "s/SQUOTE/'/" \
+ -e 's/ /__SPACE/g' < index | \
+ sort -u '-t ' +0 -1 +1n | awk -f merge.awk | \
+ sed -e 's/__SPACE/ /g' > index.so
+ rm -f index
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff b/usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff
new file mode 100644
index 0000000..a9cba6ae3
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/ex.cmd.roff
@@ -0,0 +1,1776 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ex.cmd.roff 8.24 (Berkeley) 7/17/94
+.\"
+.SH 1 "Ex Description"
+.pp
+The following words have special meanings for
+.CO ex
+commands.
+.KY "<eof>"
+.IP "<eof>"
+The end-of-file character is used to scroll the screen in the
+.CO ex
+editor.
+This character is normally
+.LI <control-D> ,
+however, whatever character is set for the current terminal is used.
+.KY "line"
+.IP "line"
+A single-line address, given in any of the forms described in the
+section entitled
+.QB "Ex Addressing" .
+The default for
+.LI line
+is the current line.
+.KY "range"
+.IP "range"
+A line, or a pair of line addresses, separated by a comma or semicolon.
+(See the section entitled
+.QB "Ex Addressing"
+for more information.)
+The default for range is the current line
+.i only ,
+i.e.
+.QT \&.,. .
+A percent sign
+.PQ %
+stands for the range
+.QT 1,$ .
+The starting address must be less than, or equal to, the ending address.
+.KY "count"
+.IP "count"
+A positive integer, specifying the number of lines to be affected by
+the command; the default is 1.
+Generally, a count past the end-of-file may be specified, e.g. the
+command
+.QT "p 3000"
+in a 10 line file is acceptable, and will print from the current line
+through the last line in the file.
+.KY "flags"
+.IP "flags"
+One or more of the characters
+.QQ # ,
+.QQ p ,
+and
+.QQ l .
+When a command that accepts these flags completes, the addressed line(s)
+are written out as if by the corresponding
+.CO # ,
+.CO l
+or
+.CO p
+commands.
+In addition, any number of
+.QT +
+or
+.QT \-
+characters can be specified before, after, or during the flags, in which
+case the line written is not necessarily the one affected by the command,
+but rather the line addressed by the offset address specified.
+The default for
+.LI flags
+is none.
+.KY "file"
+.IP "file"
+A pattern used to derive a pathname; the default is the current file.
+File names are subjected to normal
+.XR sh 1
+word expansions.
+.pp
+Anywhere a file name is specified, it is also possible to use
+the special string
+.QT /tmp .
+This will be replaced with a temporary file name which can be used
+for temporary work, e.g.
+.QT ":e /tmp"
+creates and edits a new file.
+.pp
+If both a count and a range are specified for commands that use either,
+the starting line for the command is the
+.i last
+line addressed by the range, and
+.LI count - 1
+subsequent lines are affected by the command, e.g. the command
+.QT 2,3p4
+prints out lines 3, 4, 5 and 6.
+.pp
+When only a line or range is specified, with no command, the implied
+command is either a
+.CO list ,
+.CO number
+or
+.CO print
+command.
+The command used is the most recent of the three commands to have been
+used (including any use as a flag).
+If none of these commands have been used before, the
+.CO print
+command is the implied command.
+When no range or count is specified and the command line is a blank line,
+the current line is incremented by 1 and then the current line is displayed.
+.pp
+Zero or more whitespace characters may precede or follow the addresses,
+count, flags, or command name.
+Any object following a command name (such as buffer, file, etc.),
+that begins with an alphabetic character,
+should be separated from the command name by at least one whitespace
+character.
+.pp
+Any character, including
+.LI <carriage-return> ,
+.QT %
+and
+.QT #
+retain their literal value when preceded by a backslash.
+.SH 1 "Ex Commands"
+.pp
+The following section describes the commands available in the
+.CO ex
+editor.
+In each entry below, the tag line is a usage synopsis for the command.
+.pp
+Each command can be entered as the abbreviation
+(those characters in the synopsis command word preceding the
+.QQ [
+character),
+the full command (all characters shown for the command word,
+omitting the
+.QQ [
+and
+.QQ ]
+characters),
+or any leading subset of the full command down to the abbreviation.
+For example, the args command (shown as
+.QT ar[gs]
+in the synopsis)
+can be entered as
+.QT ar ,
+.QT arg
+or
+.QT args .
+.pp
+Each
+.CO ex
+command described below notes the new current line after it
+is executed, as well as any options that affect the command.
+.KY DOUBLEQUOTE
+.IP """"
+A comment.
+Command lines beginning with the double-quote character
+.PQ """"
+are ignored.
+This permits comments in editor scripts and startup files.
+.KY "<end-of-file>"
+.IP "<end-of-file>"
+Scroll the screen.
+Write the next N lines, where N is the value of the
+.OP scroll
+option.
+The command is the end-of-file terminal character, which may be
+different on different terminals.
+Traditionally, it is the
+.LI <control-D>
+key.
+.sp
+Historically, the
+.CO eof
+command ignored any preceding count, and the
+.LI <end-of-file>
+character was ignored unless it was entered as the first character
+of the command.
+This implementation treats it as a command
+.i only
+if entered as the first character of the command line, and otherwise
+treats it as any other character.
+.SS
+.SP Line:
+Set to the last line written.
+.SP Options:
+None.
+.SE
+.KY "!"
+.IP "! argument(s)"
+.Ip "[range]! argument(s)"
+Execute a shell command, or filter lines through a shell command.
+In the first synopsis, the remainder of the line after the
+.QT !
+character is passed to the program named by the
+.OP shell
+option, as a single argument.
+.sp
+Within the rest of the line,
+.QT %
+and
+.QT #
+are expanded into the current and alternate pathnames, respectively.
+The character
+.QT !
+is expanded with the command text of the previous
+.CO !
+command.
+(Therefore, the command
+.CO !!
+repeats the previous
+.CO !
+command.)
+The special meanings of
+.QT % ,
+.QT # ,
+and
+.QT !
+can be overridden by escaping them with a backslash.
+If no
+.CO !
+or
+.CO :!
+command has yet been executed, it is an error to use an unescaped
+.QT !
+character.
+The
+.CO !
+command does
+.i not
+do shell expansion on the strings provided as arguments.
+If any of the above expansions change the command the user entered,
+the command is redisplayed at the bottom of the screen.
+.sp
+.CO Ex
+then executes the program named by the
+.OP shell
+option, with a
+.b \-c
+flag followed by the arguments (which are bundled into a single argument).
+.sp
+The
+.CO !
+command is permitted in an empty file.
+.sp
+If the file has been modified since it was last completely written,
+the
+.Co !
+command will warn you.
+.sp
+A single
+.QT !
+character is displayed when the command completes.
+.sp
+In the second form of the
+.CO !
+command, the remainder of the line after the
+.QT !
+is passed to the program named by the
+.OP shell
+option, as described above.
+The specified lines are passed to the program as standard input,
+and the standard and standard error output of the program replace
+the original lines.
+.SS
+.SP Line:
+Unchanged if no range was specified, otherwise set to the first
+line of the range.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY "number"
+.IP "[range] nu[mber] [count] [flags]"
+.KY "#"
+.Ip "[range] # [count] [flags]"
+Display the selected lines, each preceded with its line number.
+.sp
+The line number format is
+.QQ %6d ,
+followed by two spaces.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+None.
+.SE
+.KY "@"
+.IP "@ buffer"
+.KY "*"
+.Ip "* buffer"
+Execute a buffer.
+Each line in the named buffer is executed as an
+.CO ex
+command.
+If no buffer is specified, or if the specified buffer is
+.QT @
+or
+.QT * ,
+the last buffer executed is used.
+.KY <
+.IP "[range] <[< ...] [count] [flags]"
+Shift lines left or right.
+The specified lines are shifted to the left (for the
+.CO <
+command) or right (for the
+.CO >
+command), by the number of columns specified by the
+.OP shiftwidth
+option.
+Only leading whitespace characters are deleted when shifting left;
+once the first column of the line contains a nonblank character,
+the
+.CO shift
+command will succeed, but the line will not be modified.
+.sp
+If the command character
+.CO <
+or
+.CO >
+is repeated more than once, the command is repeated once for each
+additional command character.
+.SS
+.SP Line:
+If the current line is set to one of the lines that are affected
+by the command, it is unchanged.
+Otherwise, it is set to the first nonblank character of the lowest
+numbered line shifted.
+.SP Options:
+Affected by the
+.OP shiftwidth
+option.
+.SE
+.KY =
+.IP "[line] = [flags]"
+Display the line number.
+Display the line number of
+.LI line
+(which defaults to the last line in the file).
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY >
+.IP "[range] >[> ...] [count] [flags]"
+Shift right.
+The specified lines are shifted to the right by the number of columns
+specified by the
+.OP shiftwidth
+option, by inserting tab and space characters.
+Empty lines are not changed.
+.sp
+If the command character
+.QT >
+is repeated more than once, the command is repeated once for each
+additional command character.
+.SS
+.SP Line:
+Set to the last line modified by the command.
+.SP Options:
+None.
+.SE
+.KY abbrev
+.IP "ab[brev] lhs rhs"
+Add an abbreviation to the current abbreviation list.
+In
+.CO vi ,
+if
+.LI lhs
+is entered such that it is preceded and followed by characters
+that cannot be part of a word, it is replaced by the string
+.LI rhs .
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY append
+.IP "[line] a[ppend][!]"
+The input text is appended to the specified line.
+If line 0 is specified, the text is inserted at the beginning of the file.
+Set to the last line input.
+If no lines are input, then set to
+.LI line ,
+or to the first line of the file if a
+.LI line
+of 0 was specified.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option to be toggled for the duration of the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY args
+.IP "ar[gs]"
+Display the argument list.
+The current argument is displayed inside of
+.QT [
+and
+.QT ]
+characters.
+The argument list is the list of operands specified on startup,
+which can be replaced using the
+.CO next
+command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY bg
+.IP bg
+.CO Vi
+mode only.
+Background the current screen.
+.SS
+.SP Line:
+Set to the current line when the screen was last edited.
+.SP Options:
+None.
+.SE
+.KY change
+.IP "[range] c[hange][!] [count]"
+Replace the lines with input text.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option to be toggled for the duration of the command.
+.SS
+.SP Line:
+Set to the last line input, or, if no lines were input,
+set to the line before the target line, or to the first
+line of the file if there are no lines preceding the target line.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY cd
+.KY chdir
+.IP "chd[ir][!] [directory]"
+.Ip "cd[!] [directory]"
+Change the current working directory.
+The
+.LI directory
+argument is subjected to
+.XR sh 1
+word expansions.
+When invoked with no directory argument and the
+.LI HOME
+environment variable is set, the directory named by the
+.LI HOME
+environment variable becomes the new current directory.
+Otherwise, the new current directory becomes the directory returned
+by the
+.XR getpwent 3
+routine.
+.sp
+The
+.CO chdir
+command will fail if the file has been modified since the last complete
+write of the file.
+You can override this check by appending a
+.QT !
+character to the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP cdpath
+option.
+.SE
+.KY copy
+.KY t
+.IP "[range] co[py] line [flags]"
+.Ip "[range] t line [flags]"
+Copy the specified lines (range) after the destination line.
+Line 0 may be specified to insert the lines at the beginning of
+the file.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY delete
+.IP "[range] d[elete] [buffer] [count] [flags]"
+Delete the lines from the file.
+The deleted text is saved in the specified buffer, or, if no buffer
+is specified, in the unnamed buffer.
+If the command name is followed by a letter that could be interpreted
+as either a buffer name or a flag value (because neither a
+.LI count
+or
+.LI flags
+values were given),
+.CO ex
+treats the letter as a
+.LI flags
+value if the letter immediately follows the command name,
+without any whitespace separation.
+If the letter is preceded by whitespace characters,
+it treats it as a buffer name.
+.SS
+.SP Line:
+Set to the line following the deleted lines,
+or to the last line if the deleted lines were at the end.
+.SP Options:
+None.
+.SE
+.KY display
+.IP "di[splay] b[uffers] | s[creens] | t[ags]"
+Display buffers, screens or tags.
+The
+.CO display
+command takes one of three additional arguments, which are as follows:
+.SS
+.SP b[uffers]
+Display all buffers (including named, unnamed, and numeric)
+that contain text.
+.SP s[creens]
+Display the file names of all background screens.
+.SP t[ags]
+Display the tags stack.
+.SE
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY edit
+.IP "e[dit][!] [+cmd] [file]"
+.Ip "ex[!] [+cmd] [file]"
+Edit a different file.
+If the current buffer has been modified since the last complete write,
+the command will fail.
+You can override this by appending a
+.QT !
+character to the command name.
+.sp
+If the
+.QT +cmd
+option is specified, that
+.CO ex
+command will be executed in the new file.
+Any
+.CO ex
+command may be used, although the most common use of this feature is
+to specify a line number or search pattern to set the initial location
+in the new file.
+.SS
+.SP Line:
+If you have previously edited the file, the current line will be set
+to your last position in the file.
+If that position does not exist, or you have not previously edited the
+file, the current line will be set to the first line of the file if
+you are in
+.CO vi
+mode, and the last line of the file if you are in
+.CO ex .
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY exusage
+.IP "exu[sage] [command]"
+Display usage for an
+.CO ex
+command.
+If
+.LI command
+is specified, a usage statement for that command is displayed.
+Otherwise, usage statements for all
+.CO ex
+commands are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY file
+.IP "f[ile] [file]"
+Display and optionally change the file name.
+If a file name is specified, the current pathname is changed to the
+specified name.
+The current pathname, the number of lines, and the current position
+in the file are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY fg
+.IP "fg [name]"
+.CO Vi
+mode only.
+Foreground the specified screen.
+Swap the current screen with the specified backgrounded screen.
+If no screen is specified, the first background screen is foregrounded.
+.SS
+.SP Line:
+Set to the current line when the screen was last edited.
+.SP Options:
+None.
+.SE
+.KY global
+.IP "[range] g[lobal] /pattern/ [commands]"
+.KY v
+.Ip "[range] v /pattern/ [commands]"
+Apply commands to lines matching (or not matching) a pattern.
+The lines within the given range that match
+.PQ g[lobal] ,
+or do not match
+.PQ v
+the given pattern are selected.
+Then, the specified
+.CO ex
+command(s) are executed with the current line
+.PQ \&.
+set to each selected line.
+If no range is specified, the entire file is searched for matching,
+or not matching, lines.
+.sp
+Multiple commands can be specified, one per line, by escaping each
+.LI <newline>
+character with a backslash, or by separating commands with a
+.QT |
+character.
+If no commands are specified, the command defaults to the
+.CO print
+command.
+.sp
+For the
+.CO append ,
+.CO change
+and
+.CO insert
+commands, the input text must be part of the global command line.
+In this case, the terminating period can be omitted if it ends the commands.
+.sp
+The
+.CO visual
+command may also be specified as one of the
+.CO ex
+commands.
+In this mode, input is taken from the terminal.
+Entering a
+.CO Q
+command in
+.CO vi
+mode causes the next line matching the pattern to be selected and
+.CO vi
+to be reentered, until the list is exhausted.
+.sp
+The
+.CO global ,
+.CO v
+and
+.CO undo
+commands cannot be used as part of these commands.
+.sp
+The editor options
+.OP autoprint ,
+.OP autoindent ,
+and
+.OP report
+are turned off for the duration of the
+.CO global
+and
+.CO v
+commands.
+.SS
+.SP Line:
+The last line modified.
+.SP Options:
+None.
+.SE
+.KY help
+.IP "he[lp]"
+Display a help message.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY insert
+.IP "[line] i[nsert][!]"
+The input text is inserted before the specified line.
+Following the command name with a
+.QT !
+character causes the
+.OP autoindent
+option setting to be toggled for the duration of this command.
+.SS
+.SP Line:
+Set to the last line input; if no lines were input,
+set to the line before the target line, or to the first line
+of the file if there are no lines preceding the target line.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY join
+.IP "[range] j[oin][!] [count] [flags]"
+Join lines of text together.
+.sp
+A
+.LI count
+specified to the
+.Sy join
+command specifies that the last line of the
+.LI range
+plus
+.LI count
+subsequent lines will be joined.
+(Note, this differs by one from the general rule where only
+.LI count - 1
+subsequent lines are affected.)
+.sp
+If the current line ends with a whitespace character, all whitespace
+is stripped from the next line.
+Otherwise, if the next line starts with a open parenthesis
+.PQ ( ,
+do nothing.
+Otherwise, if the current line ends with a question mark
+.PQ ? ,
+period
+.PQ \&.
+or exclamation point
+.PQ ! ,
+insert two spaces.
+Otherwise, insert a single space.
+.sp
+Appending a
+.QT !
+character to the command name causes a simpler join with no
+white-space processing.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY list
+.IP "[range] l[ist] [count] [flags]"
+Display the lines unambiguously.
+Tabs are displayed as
+.QT ^I ,
+and the end of the line is marked with a
+.QT $
+character.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+None.
+.SE
+.KY map
+.IP "map[!] [lhs rhs]"
+Define or display maps (for
+.CO vi
+only).
+.sp
+If
+.QT lhs
+and
+.QT rhs
+are not specified, the current set of command mode maps are displayed.
+If a
+.QT !
+character is appended to to the command,
+the text input mode maps are displayed.
+.sp
+Otherwise, when the
+.QT lhs
+character sequence is entered in
+.CO vi ,
+the action is as if the corresponding
+.QT rhs
+had been entered.
+If a
+.QT !
+character is appended to the command name,
+the mapping is effective during text input mode,
+otherwise, it is effective during command mode.
+This allows
+.QT lhs
+to have two different macro definitions at the same time: one for command
+mode and one for input mode.
+.sp
+Whitespace characters require escaping with a
+.LI <literal next>
+character to be entered in the
+.LI lhs
+string in visual mode.
+.sp
+Normally, keys in the
+.LI rhs
+string are remapped (see the
+.OP remap
+option),
+and it is possible to create infinite loops.
+However, keys which map to themselves are not further remapped,
+regardless of the setting of the
+.OP remap
+option.
+For example, the command
+.QT ":map n nz."
+maps the
+.QT n
+key to the
+.CO n
+and
+.CO z
+commands.
+.sp
+To exit an infinitely looping map, use the terminal
+.LI <interrupt>
+character.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY mark
+.KY k
+.IP "[line] ma[rk] <character>"
+.Ip "[line] k <character>"
+Mark the line with the mark
+.LI <character> .
+The expressions
+.QT '<character>
+and
+.QT `<character>
+can then be used as an address in any command that uses one.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY move
+.IP "[range] m[ove] line"
+Move the specified lines after the target line.
+A target line of 0 places the lines at the beginning of the file.
+.SS
+.SP Line:
+Set to the first of the moved lines.
+.SP Options:
+None.
+.SE
+.KY mkexrc
+.IP "mk[exrc][!] file"
+Write the abbreviations, editor options and maps to the specified
+file.
+Information is written in a form which can later be read back in
+using the
+.CO ex
+.CO source
+command.
+If
+.LI file
+already exists, the
+.CO mkexrc
+command will fail.
+This check can be overridden by appending a
+.QT !
+character to the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY next
+.IP "n[ext][!] [file ...]"
+Edit the next file from the argument list.
+The
+.CO next
+command will fail if the file has been modified since the last complete
+write.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+The argument list can optionally be replaced by specifying a new one
+as arguments to this command.
+In this case, editing starts with the first file on the new list.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the options
+.OP autowrite
+and
+.OP writeany .
+.SE
+.KY open
+.IP "[line] o[pen] /pattern/ [flags]"
+Enter open mode.
+Open mode is the same as being in
+.CO vi ,
+but with a one-line window.
+All the standard
+.CO vi
+commands are available.
+If a match is found for the optional RE argument,
+the cursor is set to the start of the matching pattern.
+.sp
+.i "This command is not yet implemented."
+.SS
+.SP Line:
+Unchanged, unless the optional RE is specified, in which case it is
+set to the line where the matching pattern is found.
+.SP Options:
+Affected by the
+.OP open
+option.
+.SE
+.KY preserve
+.IP "pre[serve]"
+Save the file in a form that can later be recovered using the
+.CO ex
+.b \-r
+option.
+When the file is preserved, an email message is sent to the user.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY previous
+.IP "prev[ious][!]"
+Edit the previous file from the argument list.
+The
+.CO previous
+command will fail if the file has been modified since the last complete
+write.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the options
+.OP autowrite
+and
+.OP writeany .
+None.
+.SE
+.KY print
+.IP "[range] p[rint] [count] [flags]"
+Display the specified lines.
+.SS
+.SP Line:
+Set to the last line displayed.
+.SP Options:
+None.
+.SE
+.KY put
+.IP "[line] pu[t] [buffer]"
+Append buffer contents to the current line.
+If a buffer is specified, its contents are appended to the line,
+otherwise, the contents of the unnamed buffer are used.
+.SS
+.SP Line:
+Set to the line after the current line.
+.SP Options:
+None.
+.SE
+.KY quit
+.IP "q[uit][!]"
+End the editing session.
+If the file has been modified since the last complete write, the
+.CO quit
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command.
+.sp
+If there are more files to edit, the
+.CO quit
+command will fail.
+Appending a
+.QT !
+character to the command name or entering two
+.CO quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY read
+.IP "[line] r[ead][!] [file]"
+Read a file.
+A copy of the specified file is appended to the line.
+If
+.LI line
+is 0, the copy is inserted at the beginning of the file.
+If no file is specified, the current file is read; if there is no
+current file, then
+.LI file
+becomes the current file.
+If there is no current file and no
+.LI file
+is specified, then the
+.CO read
+command will fail.
+.sp
+If
+.LI file
+is preceded by a
+.QT !
+character,
+.LI file
+is treated as if it were a shell command, and passed to the program
+named by the
+.LI SHELL
+environment variable.
+The standard and standard error outputs of that command are read into
+the file after the specified line.
+The special meaning of the
+.QT !
+character can be overridden by escaping it with a backslash
+.PQ \e
+character.
+.SS
+.SP Line:
+When executed from
+.CO ex ,
+the current line is set to the last line read.
+When executed from
+.CO vi ,
+the current line is set to the first line read.
+.SP Options:
+None.
+.SE
+.KY recover
+.IP "rec[over] file"
+Recover
+.LI file
+if it was previously saved.
+If no saved file by that name exists, the
+.CO recover
+command behaves similarly to the
+.CO edit
+command.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+None.
+.SE
+.KY resize
+.IP "res[ize] [+|-]size"
+.CO Vi
+mode only.
+Grow or shrink the current screen.
+If
+.LI size
+is a positive, signed number, the current screen is grown by that many lines.
+If
+.LI size
+is a negative, signed number, the current screen is shrunk by that many lines.
+If
+.LI size
+is not signed, the current screen is set to the specified
+.LI size .
+Applicable only to split screens.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY rewind
+.IP "rew[ind][!]"
+Rewind the argument list.
+If the current file has been modified since the last complete write,
+the
+.CO rewind
+command will fail.
+This check may be overridden by appending the
+.QT !
+character to the command.
+.sp
+Otherwise, the current file is set to the first file in the argument
+list.
+.SS
+.SP Line:
+Set as described for the
+.CO edit
+command.
+.SP Options:
+Affected by the
+.OP autowrite
+and
+.OP writeany
+options.
+.SE
+.KY set
+.IP "se[t] [option[=[value]] ...] [nooption ...] [option? ...] [all]"
+Display or set editor options.
+When no arguments are specified, the editor option
+.OP term ,
+and any editor options whose values have been changed from the
+default settings are displayed.
+If the argument
+.LI all
+is specified, the values of all of editor options are displayed.
+.sp
+Specifying an option name followed by the character
+.QT ?
+causes the current value of that option to be displayed.
+The
+.QT ?
+can be separated from the option name by whitespace characters.
+The
+.QT ?
+is necessary only for Boolean valued options.
+Boolean options can be given values by the form
+.QT "set option"
+to turn them on, or
+.QT "set nooption"
+to turn them off.
+String and numeric options can be assigned by the form
+.QT "set option=value" .
+Any whitespace characters in strings can be included literally by preceding
+each with a backslash.
+More than one option can be set or listed by a single set command,
+by specifying multiple arguments, each separated from the next by
+whitespace characters.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY shell
+.IP "sh[ell]"
+Run a shell program.
+The program named by the
+.OP shell
+option is run with a
+.b \-i
+(for interactive) flag.
+Editing is resumed when that program exits.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY source
+.IP "so[urce] file"
+Read and execute
+.CO ex
+commands from a file.
+.CO Source
+commands may be nested.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY split
+.IP "sp[lit] [file ...]"
+.CO Vi
+mode only.
+Split the screen.
+The current screen is split into two screens, of approximately equal size.
+If the cursor is in the lower half of the screen, the screen will split
+up, i.e. the new screen will be above the old one.
+If the cursor is in the upper half of the screen, the new screen will be
+below the old one.
+.sp
+If
+.LI file
+is specified, the new screen is editing that file, otherwise, both
+screens are editing the same file, and changes in each will be
+be reflected in the other.
+The argument list for the new screen consists of the list of files
+specified as arguments to this command, or, the current pathname if
+no files are specified.
+.SS
+.SP Line:
+If
+.LI file
+is specified, set as for the
+.CO edit
+command, otherwise unchanged.
+.SP Options:
+None.
+.SE
+.KY substitute
+.IP "[range] s[ubstitute] [/pattern/replace/] [options] [count] [flags]"
+.KY &
+.Ip "[range] & [options] [count] [flags]"
+.KY ~
+.Ip "[range] ~ [options] [count] [flags]"
+Make substitutions.
+Replace the first instance of
+.LI pattern
+with the string
+.LI replace
+on the specified line(s).
+If the
+.QT /pattern/repl/
+argument is not specified, the
+.QT /pattern/repl/
+from the previous
+.CO substitute
+command is used.
+.sp
+If
+.LI options
+includes the letter
+.QT c
+(confirm), you will be prompted for confirmation before each replacement
+is done.
+An affirmative response (in English, a
+.QT y
+character) causes the replacement to be made.
+A quit response (in English, a
+.QT q
+character) causes the
+.CO substitute
+command to be terminated.
+Any other response causes the replacement not to be made, and the
+.CO substitute
+command continues.
+If
+.LI options
+includes the letter
+.QT g
+(global), all nonoverlapping instances of
+.LI pattern
+in the line are replaced.
+.sp
+The
+.CO &
+version of the command is the same as not specifying a pattern
+or replacement string to the
+.CO substitute
+command, and the
+.QT &
+is replaced by the pattern and replacement information from the
+previous substitute command.
+.sp
+The
+.CO ~
+version of the command is the same as
+.CO &
+and
+.CO s ,
+except that the search pattern used is the last RE used in
+.i any
+command, not necessarily the one used in the last
+.CO substitute
+command.
+.sp
+For example, in the sequence
+.ft C
+.(b
+s/red/blue/
+/green
+~
+.)b
+.ft R
+the
+.QT ~
+is equivalent to
+.QT s/green/blue/ .
+.sp
+The
+.CO substitute
+command may be interrupted, using the terminal interrupt character.
+All substitutions completed before the interrupt are retained.
+.SS
+.SP Line:
+Set to the last line upon which a substitution was made.
+.SP Options:
+None.
+.SE
+.KY suspend
+.IP "su[spend][!]"
+.KY stop
+.Ip "st[op][!]"
+.KY <control-Z>
+.Ip <control-Z>
+Suspend the edit session.
+Appending a
+.QT !
+character to these commands turns off the
+.OP autowrite
+option for the command.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP autowrite
+option.
+.SE
+.KY tag
+.IP "ta[g][!] tagstring"
+Edit the file containing the specified tag.
+Search for the tagstring, which can be in a different file.
+If the tag is in a different file, then the new file is edited.
+If the current file has been modified since the last complete write,
+the
+.CO tag
+command will fail.
+This check can be overridden by appending the
+.QT !
+character to the command name.
+.sp
+The
+.CO tag
+command searches for
+.LI tagstring
+in the tags file(s) specified by the
+.Op tags
+option.
+(See
+.XR ctags 1
+for more information on tags files.)
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite ,
+.OP taglength ,
+.OP tags
+and
+.OP writeany
+options.
+.SE
+.KY tagpop
+.IP "tagp[op][!] [file | number]"
+Pop to the specified tag in the tags stack.
+If neither
+.LI file
+or
+.LI number
+is specified, the
+.CO tagpop
+command pops to the most recent entry on the tags stack.
+If
+.LI file
+or
+.LI number
+is specified, the
+.CO tagpop
+command pops to the most recent entry in the tags stack for that file,
+or numbered entry in the tags stack, respectively.
+(See the
+.CO display
+command for information on displaying the tags stack.)
+.sp
+If the file has been modified since the last complete write, the
+.CO tagpop
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command name.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite ,
+and
+.OP writeany
+options.
+.SE
+.KY tagtop
+.IP "tagt[op][!]"
+Pop to the least recent tag on the tags stack, clearing the tags stack.
+.sp
+If the file has been modified since the last complete write, the
+.CO tagpop
+command will fail.
+This check may be overridden by appending a
+.QT !
+character to the command name.
+.SS
+.SP Line:
+Set to the line indicated by the tag.
+.SP Options:
+Affected by the
+.OP autowrite ,
+and
+.OP writeany
+options.
+.SE
+.KY unabbrev
+.IP "una[bbrev] lhs"
+Delete an abbreviation.
+Delete
+.LI lhs
+from the current list of abbreviations.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY undo
+.IP "u[ndo]"
+Undo the last change made to the file.
+Changes made by
+.CO global ,
+.CO v ,
+.CO visual
+and map sequences are considered a single command.
+If repeated, the
+.CO u
+command alternates between these two states, and is its own inverse.
+.SS
+.SP Line:
+Set to the last line modified by the command.
+.SP Options:
+None.
+.SE
+.KY unmap
+.IP "unm[ap][!] lhs"
+Unmap a mapped string.
+Delete the command mode map definition for
+.LI lhs .
+If a
+.QT !
+character is appended to the command name, delete the text input mode
+map definition instead.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY version
+.IP "ve[rsion]"
+Display the version of the
+.CO ex/vi
+editor.
+.KY visual
+.IP "[line] vi[sual] [type] [count] [flags]"
+.CO Ex
+mode only.
+Enter
+.CO vi .
+The
+.LI type
+is optional, and can be
+.QT \- ,
+.QT +
+or
+.QT ^ ,
+as in the
+.CO ex
+.CO z
+command, to specify the the position of the specified line in the screen
+window.
+(The default is to place the line at the top of the screen window.)
+A
+.LI count
+specifies the number of lines that will initially be displayed.
+(The default is the value of the
+.OP window
+editor option.)
+.SS
+.SP Line:
+Unchanged unless
+.LI line
+is specified, in which case it is set to that line.
+.SP Options:
+None.
+.SE
+.KY visual
+.IP "vi[sual][!] [+cmd] [file]"
+.CO Vi
+mode only.
+Edit a new file.
+Identical to the
+.QT "edit[!] [+cmd] [file]"
+command.
+.KY viusage
+.IP "viu[sage] [command]"
+Display usage for a
+.CO vi
+command.
+If
+.LI command
+is specified, a usage statement for that command is displayed.
+Otherwise, usage statements for all
+.CO vi
+commands are displayed.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY write
+.IP "[range] w[rite][!] [>>] [file]"
+.Ip "[range] w[rite] [!] [file]"
+.KY wn
+.Ip "[range] wn[!] [>>] [file]"
+.KY wq
+.Ip "[range] wq[!] [>>] [file]"
+Write the file.
+The specified lines (the entire file, if no range is given) is written
+to
+.LI file .
+If
+.LI file
+is not specified, the current pathname is used.
+If
+.LI file
+is specified, and it exists, or if the current pathname was set using the
+.CO file
+command, and the file already exists, these commands will fail.
+Appending a
+.QT !
+character to the command name will override this check and the write
+will be attempted, regardless.
+.sp
+Specifying the optional
+.QT >>
+string will cause the write to be appended to the file, in which case
+no tests are made for the file already existing.
+.sp
+If the file is preceded by a
+.QT !
+character, the program named in the SHELL environment variable is
+invoked with file as its second argument, and the specified lines
+are passed as standard input to that command.
+The
+.QT !
+in this usage must be separated from command name by at least one
+whitespace character.
+The special meaning of the
+.QT !
+may be overridden by escaping it with a backslash
+.PQ \e
+character.
+.sp
+The
+.CO wq
+version of the write command will exit the editor after writing the file,
+if there are no further files to edit.
+Appending a
+.QT !
+character to the command name or entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.sp
+The
+.CO wn
+version of the write command will move to the next file after writing
+the file, unless the write fails.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP readonly
+and
+.OP writeany
+options.
+.SE
+.KY xit
+.IP "[range] x[it][!] [file]"
+Write the file if it has been modified.
+The specified lines are written to
+.LI file ,
+if the file has been modified since the last complete write to any
+file.
+If no
+.LI range
+is specified, the entire file is written.
+.sp
+The
+.CO xit
+command will exit the editor after writing the file,
+if there are no further files to edit.
+Appending a
+.QT !
+character to the command name or entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row) will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+Affected by the
+.OP readonly
+and
+.OP writeany
+options.
+.SE
+.KY yank
+.IP "[range] ya[nk] [buffer] [count]"
+Copy the specified lines to a buffer.
+If no buffer is specified, the unnamed buffer is used.
+.SS
+.SP Line:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY z
+.IP "[line] z [type] [count] [flags]"
+Adjust the window.
+If no
+.LI type
+is specified, then
+.LI count
+lines following the specified line are displayed.
+The default
+.LI count
+is the value of the
+.OP window
+option.
+The
+.LI type
+argument changes the position at which
+.LI line
+is displayed on the screen by changing the number of lines
+displayed before and after
+.LI line .
+The following
+.LI type
+characters may be used:
+.SS
+.SP \-
+Place the line at the bottom of the screen.
+.SP +
+Place the line at the top of the screen.
+.SP \&.
+Place the line in the middle of the screen.
+.SP ^
+Write out count lines starting
+.LI "count * 2"
+lines before
+.LI line ;
+the net effect of this is that a
+.QT z^
+command following a
+.CO z
+command writes the previous page.
+.SP =
+Center
+.LI line
+on the screen with a line of hyphens displayed immediately before and
+after it.
+The number of preceding and following lines of text displayed are
+reduced to account for those lines.
+.SE
+.SS
+.SP Line:
+Set to the last line displayed, with the exception of the
+.Dq Li \&=
+.LI type ,
+where the current line is set to the line specified by the command.
+.SP Options:
+Affected by the
+.Sy window
+option.
+.SE
diff --git a/usr.bin/vi/USD.doc/vi.ref/merge.awk b/usr.bin/vi/USD.doc/vi.ref/merge.awk
new file mode 100644
index 0000000..16b5152
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/merge.awk
@@ -0,0 +1,16 @@
+# @(#)merge.awk 8.3 (Berkeley) 5/26/94
+#
+# merge index entries into one line per label
+$1 == prev {
+ printf ", %s", $2;
+ next;
+}
+{
+ if (NR != 1)
+ printf "\n";
+ printf "%s \t%s", $1, $2;
+ prev = $1;
+}
+END {
+ printf "\n"
+}
diff --git a/usr.bin/vi/USD.doc/vi.ref/paper.ps b/usr.bin/vi/USD.doc/vi.ref/paper.ps
new file mode 100644
index 0000000..e429d56b
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/paper.ps
@@ -0,0 +1,30924 @@
+%!PS-Adobe-1.0
+%%Creator: python.bostic.com:root (Charlie,458E,7750)
+%%Title: stdin (ditroff)
+%%CreationDate: Mon Aug 15 14:24:12 1994
+%%EndComments
+% @(#)psdit.pro 1.6 11/6/90
+% lib/psdit.pro -- prolog for psdit (ditroff) files
+% Copyright (c) 1984, 1985 Adobe Systems Incorporated. All Rights Reserved.
+% last edit: shore Sat Nov 23 20:28:03 1985
+% RCSID: $Header: psdit.pro,v 2.1 85/11/24 12:19:43 shore Rel $
+
+% Changed by Edward Wang (edward@ucbarpa.berkeley.edu) to handle graphics,
+% 17 Feb, 87.
+
+/$DITroff 140 dict def $DITroff begin
+/fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def
+/xi{0 72 11 mul translate 72 resolution div dup neg scale 0 0 moveto
+ /fontnum 1 def /fontsize 10 def /fontheight 10 def /fontslant 0 def F}def
+/PB{save /psv exch def currentpoint translate
+ resolution 72 div dup neg scale 0 0 moveto}def
+/PE{psv restore}def
+/arctoobig 90 def /arctoosmall .05 def
+/m1 matrix def /m2 matrix def /m3 matrix def /oldmat matrix def
+/tan{dup sin exch cos div}def
+/point{resolution 72 div mul}def
+/dround {transform round exch round exch itransform}def
+/xT{/devname exch def}def
+/xr{/mh exch def /my exch def /resolution exch def}def
+/xp{}def
+/xs{docsave restore end}def
+/xt{}def
+/xf{/fontname exch def /slotno exch def fontnames slotno get fontname eq not
+ {fonts slotno fontname findfont put fontnames slotno fontname put}if}def
+/xH{/fontheight exch def F}def
+/xS{/fontslant exch def F}def
+/s{/fontsize exch def /fontheight fontsize def F}def
+/f{/fontnum exch def F}def
+/F{fontheight 0 le{/fontheight fontsize def}if
+ fonts fontnum get fontsize point 0 0 fontheight point neg 0 0 m1 astore
+ fontslant 0 ne{1 0 fontslant tan 1 0 0 m2 astore m3 concatmatrix}if
+ makefont setfont .04 fontsize point mul 0 dround pop setlinewidth}def
+/X{exch currentpoint exch pop moveto show}def
+/N{3 1 roll moveto show}def
+/Y{exch currentpoint pop exch moveto show}def
+/S{show}def
+/ditpush{}def/ditpop{}def
+/AX{3 -1 roll currentpoint exch pop moveto 0 exch ashow}def
+/AN{4 2 roll moveto 0 exch ashow}def
+/AY{3 -1 roll currentpoint pop exch moveto 0 exch ashow}def
+/AS{0 exch ashow}def
+/MX{currentpoint exch pop moveto}def
+/MY{currentpoint pop exch moveto}def
+/MXY{moveto}def
+/cb{pop}def % action on unknown char -- nothing for now
+/n{}def/w{}def
+/p{pop showpage xi}def
+/Dt{/Dlinewidth exch def}def 1 Dt
+/Ds{/Ddash exch def}def -1 Ds
+/i{/Dstipple exch def}def 1 i
+/Dsetlinewidth{2 Dlinewidth mul setlinewidth}def
+/Dsetdash{Ddash 4 eq{[8 12]}{Ddash 16 eq{[32 36]}
+ {Ddash 20 eq{[32 12 8 12]}{[]}ifelse}ifelse}ifelse 0 setdash}def
+/Dstroke{gsave Dsetlinewidth Dsetdash 1 setlinecap stroke grestore
+ currentpoint newpath moveto}def
+/Dl{rlineto Dstroke}def
+/arcellipse{/diamv exch def /diamh exch def oldmat currentmatrix pop
+ currentpoint translate 1 diamv diamh div scale /rad diamh 2 div def
+ currentpoint exch rad add exch rad -180 180 arc oldmat setmatrix}def
+/Dc{dup arcellipse Dstroke}def
+/De{arcellipse Dstroke}def
+/Da{/endv exch def /endh exch def /centerv exch def /centerh exch def
+ /cradius centerv centerv mul centerh centerh mul add sqrt def
+ /eradius endv endv mul endh endh mul add sqrt def
+ /endang endv endh atan def
+ /startang centerv neg centerh neg atan def
+ /sweep startang endang sub dup 0 lt{360 add}if def
+ sweep arctoobig gt
+ {/midang startang sweep 2 div sub def /midrad cradius eradius add 2 div def
+ /midh midang cos midrad mul def /midv midang sin midrad mul def
+ midh neg midv neg endh endv centerh centerv midh midv Da
+ Da}
+ {sweep arctoosmall ge
+ {/controldelt 1 sweep 2 div cos sub 3 sweep 2 div sin mul div 4 mul def
+ centerv neg controldelt mul centerh controldelt mul
+ endv neg controldelt mul centerh add endh add
+ endh controldelt mul centerv add endv add
+ centerh endh add centerv endv add rcurveto Dstroke}
+ {centerh endh add centerv endv add rlineto Dstroke}
+ ifelse}
+ ifelse}def
+/Dpatterns[
+[%cf[widthbits]
+[8<0000000000000010>]
+[8<0411040040114000>]
+[8<0204081020408001>]
+[8<0000103810000000>]
+[8<6699996666999966>]
+[8<0000800100001008>]
+[8<81c36666c3810000>]
+[8<0f0e0c0800000000>]
+[8<0000000000000010>]
+[8<0411040040114000>]
+[8<0204081020408001>]
+[8<0000001038100000>]
+[8<6699996666999966>]
+[8<0000800100001008>]
+[8<81c36666c3810000>]
+[8<0f0e0c0800000000>]
+[8<0042660000246600>]
+[8<0000990000990000>]
+[8<0804020180402010>]
+[8<2418814242811824>]
+[8<6699996666999966>]
+[8<8000000008000000>]
+[8<00001c3e363e1c00>]
+[8<0000000000000000>]
+[32<00000040000000c00000004000000040000000e0000000000000000000000000>]
+[32<00000000000060000000900000002000000040000000f0000000000000000000>]
+[32<000000000000000000e0000000100000006000000010000000e0000000000000>]
+[32<00000000000000002000000060000000a0000000f00000002000000000000000>]
+[32<0000000e0000000000000000000000000000000f000000080000000e00000001>]
+[32<0000090000000600000000000000000000000000000007000000080000000e00>]
+[32<00010000000200000004000000040000000000000000000000000000000f0000>]
+[32<0900000006000000090000000600000000000000000000000000000006000000>]]
+[%ug
+[8<0000020000000000>]
+[8<0000020000002000>]
+[8<0004020000002000>]
+[8<0004020000402000>]
+[8<0004060000402000>]
+[8<0004060000406000>]
+[8<0006060000406000>]
+[8<0006060000606000>]
+[8<00060e0000606000>]
+[8<00060e000060e000>]
+[8<00070e000060e000>]
+[8<00070e000070e000>]
+[8<00070e020070e000>]
+[8<00070e020070e020>]
+[8<04070e020070e020>]
+[8<04070e024070e020>]
+[8<04070e064070e020>]
+[8<04070e064070e060>]
+[8<06070e064070e060>]
+[8<06070e066070e060>]
+[8<06070f066070e060>]
+[8<06070f066070f060>]
+[8<060f0f066070f060>]
+[8<060f0f0660f0f060>]
+[8<060f0f0760f0f060>]
+[8<060f0f0760f0f070>]
+[8<0e0f0f0760f0f070>]
+[8<0e0f0f07e0f0f070>]
+[8<0e0f0f0fe0f0f070>]
+[8<0e0f0f0fe0f0f0f0>]
+[8<0f0f0f0fe0f0f0f0>]
+[8<0f0f0f0ff0f0f0f0>]
+[8<1f0f0f0ff0f0f0f0>]
+[8<1f0f0f0ff1f0f0f0>]
+[8<1f0f0f8ff1f0f0f0>]
+[8<1f0f0f8ff1f0f0f8>]
+[8<9f0f0f8ff1f0f0f8>]
+[8<9f0f0f8ff9f0f0f8>]
+[8<9f0f0f9ff9f0f0f8>]
+[8<9f0f0f9ff9f0f0f9>]
+[8<9f8f0f9ff9f0f0f9>]
+[8<9f8f0f9ff9f8f0f9>]
+[8<9f8f1f9ff9f8f0f9>]
+[8<9f8f1f9ff9f8f1f9>]
+[8<bf8f1f9ff9f8f1f9>]
+[8<bf8f1f9ffbf8f1f9>]
+[8<bf8f1fdffbf8f1f9>]
+[8<bf8f1fdffbf8f1fd>]
+[8<ff8f1fdffbf8f1fd>]
+[8<ff8f1fdffff8f1fd>]
+[8<ff8f1ffffff8f1fd>]
+[8<ff8f1ffffff8f1ff>]
+[8<ff9f1ffffff8f1ff>]
+[8<ff9f1ffffff9f1ff>]
+[8<ff9f9ffffff9f1ff>]
+[8<ff9f9ffffff9f9ff>]
+[8<ffbf9ffffff9f9ff>]
+[8<ffbf9ffffffbf9ff>]
+[8<ffbfdffffffbf9ff>]
+[8<ffbfdffffffbfdff>]
+[8<ffffdffffffbfdff>]
+[8<ffffdffffffffdff>]
+[8<fffffffffffffdff>]
+[8<ffffffffffffffff>]]
+[%mg
+[8<8000000000000000>]
+[8<0822080080228000>]
+[8<0204081020408001>]
+[8<40e0400000000000>]
+[8<66999966>]
+[8<8001000010080000>]
+[8<81c36666c3810000>]
+[8<f0e0c08000000000>]
+[16<07c00f801f003e007c00f800f001e003c007800f001f003e007c00f801f003e0>]
+[16<1f000f8007c003e001f000f8007c003e001f800fc007e003f001f8007c003e00>]
+[8<c3c300000000c3c3>]
+[16<0040008001000200040008001000200040008000000100020004000800100020>]
+[16<0040002000100008000400020001800040002000100008000400020001000080>]
+[16<1fc03fe07df0f8f8f07de03fc01f800fc01fe03ff07df8f87df03fe01fc00f80>]
+[8<80>]
+[8<8040201000000000>]
+[8<84cc000048cc0000>]
+[8<9900009900000000>]
+[8<08040201804020100800020180002010>]
+[8<2418814242811824>]
+[8<66999966>]
+[8<8000000008000000>]
+[8<70f8d8f870000000>]
+[8<0814224180402010>]
+[8<aa00440a11a04400>]
+[8<018245aa45820100>]
+[8<221c224180808041>]
+[8<88000000>]
+[8<0855800080550800>]
+[8<2844004482440044>]
+[8<0810204080412214>]
+[8<00>]]]def
+/Dfill{
+ save 6 1 roll
+ transform /maxy exch def /maxx exch def
+ transform /miny exch def /minx exch def
+ minx maxx gt{/minx maxx /maxx minx def def}if
+ miny maxy gt{/miny maxy /maxy miny def def}if
+ Dpatterns Dstipple 1 sub get exch 1 sub get
+ aload pop /stip exch def /stipw exch def /stiph 128 def
+ /imatrix[stipw 0 0 stiph 0 0]def
+ /tmatrix[stipw 0 0 stiph 0 0]def
+ /minx minx cvi stiph idiv stiph mul def
+ /miny miny cvi stipw idiv stipw mul def
+ eoclip 0 setgray
+ miny stiph maxy{
+ tmatrix exch 5 exch put
+ minx stipw maxx{
+ tmatrix exch 4 exch put tmatrix setmatrix
+ stipw stiph true imatrix {stip} imagemask
+ }for
+ }for
+ restore
+}def
+/Dp{Dfill Dstroke}def
+/DP{Dfill currentpoint newpath moveto}def
+end
+
+/ditstart{$DITroff begin
+ /nfonts 60 def % NFONTS makedev/ditroff dependent!
+ /fonts[nfonts{0}repeat]def
+ /fontnames[nfonts{()}repeat]def
+/docsave save def
+}def
+
+% character outcalls
+/oc{
+ /pswid exch def /cc exch def /name exch def
+ /ditwid pswid fontsize mul resolution mul 72000 div def
+ /ditsiz fontsize resolution mul 72 div def
+ ocprocs name known{ocprocs name get exec}{name cb}ifelse
+}def
+/fractm [.65 0 0 .6 0 0] def
+/fraction{
+ /fden exch def /fnum exch def gsave /cf currentfont def
+ cf fractm makefont setfont 0 .3 dm 2 copy neg rmoveto
+ fnum show rmoveto currentfont cf setfont(\244)show setfont fden show
+ grestore ditwid 0 rmoveto
+}def
+/oce{grestore ditwid 0 rmoveto}def
+/dm{ditsiz mul}def
+/ocprocs 50 dict def ocprocs begin
+(14){(1)(4)fraction}def
+(12){(1)(2)fraction}def
+(34){(3)(4)fraction}def
+(13){(1)(3)fraction}def
+(23){(2)(3)fraction}def
+(18){(1)(8)fraction}def
+(38){(3)(8)fraction}def
+(58){(5)(8)fraction}def
+(78){(7)(8)fraction}def
+(sr){gsave 0 .06 dm rmoveto(\326)show oce}def
+(is){gsave 0 .15 dm rmoveto(\362)show oce}def
+(->){gsave 0 .02 dm rmoveto(\256)show oce}def
+(<-){gsave 0 .02 dm rmoveto(\254)show oce}def
+(==){gsave 0 .05 dm rmoveto(\272)show oce}def
+(uc){gsave currentpoint 400 .009 dm mul add translate
+ 8 -8 scale ucseal oce}def
+end
+
+% an attempt at a PostScript FONT to implement ditroff special chars
+% this will enable us to
+% cache the little buggers
+% generate faster, more compact PS out of psdit
+% confuse everyone (including myself)!
+50 dict dup begin
+/FontType 3 def
+/FontName /DIThacks def
+/FontMatrix [.001 0 0 .001 0 0] def
+/FontBBox [-260 -260 900 900] def% a lie but ...
+/Encoding 256 array def
+0 1 255{Encoding exch /.notdef put}for
+Encoding
+ dup 8#040/space put %space
+ dup 8#110/rc put %right ceil
+ dup 8#111/lt put %left top curl
+ dup 8#112/bv put %bold vert
+ dup 8#113/lk put %left mid curl
+ dup 8#114/lb put %left bot curl
+ dup 8#115/rt put %right top curl
+ dup 8#116/rk put %right mid curl
+ dup 8#117/rb put %right bot curl
+ dup 8#120/rf put %right floor
+ dup 8#121/lf put %left floor
+ dup 8#122/lc put %left ceil
+ dup 8#140/sq put %square
+ dup 8#141/bx put %box
+ dup 8#142/ci put %circle
+ dup 8#143/br put %box rule
+ dup 8#144/rn put %root extender
+ dup 8#145/vr put %vertical rule
+ dup 8#146/ob put %outline bullet
+ dup 8#147/bu put %bullet
+ dup 8#150/ru put %rule
+ dup 8#151/ul put %underline
+ pop
+/DITfd 100 dict def
+/BuildChar{0 begin
+ /cc exch def /fd exch def
+ /charname fd /Encoding get cc get def
+ /charwid fd /Metrics get charname get def
+ /charproc fd /CharProcs get charname get def
+ charwid 0 fd /FontBBox get aload pop setcachedevice
+ 2 setlinejoin 40 setlinewidth
+ newpath 0 0 moveto gsave charproc grestore
+ end}def
+/BuildChar load 0 DITfd put
+/CharProcs 50 dict def
+CharProcs begin
+/space{}def
+/.notdef{}def
+/ru{500 0 rls}def
+/rn{0 840 moveto 500 0 rls}def
+/vr{0 800 moveto 0 -770 rls}def
+/bv{0 800 moveto 0 -1000 rls}def
+/br{0 840 moveto 0 -1000 rls}def
+/ul{0 -140 moveto 500 0 rls}def
+/ob{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath stroke}def
+/bu{200 250 rmoveto currentpoint newpath 200 0 360 arc closepath fill}def
+/sq{80 0 rmoveto currentpoint dround newpath moveto
+ 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath stroke}def
+/bx{80 0 rmoveto currentpoint dround newpath moveto
+ 640 0 rlineto 0 640 rlineto -640 0 rlineto closepath fill}def
+/ci{500 360 rmoveto currentpoint newpath 333 0 360 arc
+ 50 setlinewidth stroke}def
+
+/lt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 add exch s4 a4p stroke}def
+/lb{0 800 moveto 0 -550 rlineto currx -200 2cx s4 add exch s4 a4p stroke}def
+/rt{0 -200 moveto 0 550 rlineto currx 800 2cx s4 sub exch s4 a4p stroke}def
+/rb{0 800 moveto 0 -500 rlineto currx -200 2cx s4 sub exch s4 a4p stroke}def
+/lk{0 800 moveto 0 300 -300 300 s4 arcto pop pop 1000 sub
+ 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def
+/rk{0 800 moveto 0 300 s2 300 s4 arcto pop pop 1000 sub
+ 0 300 4 2 roll s4 a4p 0 -200 lineto stroke}def
+/lf{0 800 moveto 0 -1000 rlineto s4 0 rls}def
+/rf{0 800 moveto 0 -1000 rlineto s4 neg 0 rls}def
+/lc{0 -200 moveto 0 1000 rlineto s4 0 rls}def
+/rc{0 -200 moveto 0 1000 rlineto s4 neg 0 rls}def
+end
+
+/Metrics 50 dict def Metrics begin
+/.notdef 0 def
+/space 500 def
+/ru 500 def
+/br 0 def
+/lt 416 def
+/lb 416 def
+/rt 416 def
+/rb 416 def
+/lk 416 def
+/rk 416 def
+/rc 416 def
+/lc 416 def
+/rf 416 def
+/lf 416 def
+/bv 416 def
+/ob 350 def
+/bu 350 def
+/ci 750 def
+/bx 750 def
+/sq 750 def
+/rn 500 def
+/ul 500 def
+/vr 0 def
+end
+
+DITfd begin
+/s2 500 def /s4 250 def /s3 333 def
+/a4p{arcto pop pop pop pop}def
+/2cx{2 copy exch}def
+/rls{rlineto stroke}def
+/currx{currentpoint pop}def
+/dround{transform round exch round exch itransform} def
+end
+end
+/DIThacks exch definefont pop
+ditstart
+(psc)xT
+576 1 1 xr
+1(Times-Roman)xf 1 f
+2(Times-Italic)xf 2 f
+3(Times-Bold)xf 3 f
+4(Times-BoldItalic)xf 4 f
+5(Helvetica)xf 5 f
+6(Helvetica-Bold)xf 6 f
+7(Courier)xf 7 f
+8(Courier-Bold)xf 8 f
+9(Symbol)xf 9 f
+10(DIThacks)xf 10 f
+10 s
+1 f
+xi
+%%EndProlog
+
+%%Page: 1 1
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+12 s
+1796 795(Ex/Vi)N
+2055(Reference)X
+2491(Manual)X
+2 f
+10 s
+2107 975(Keith)N
+2300(Bostic)X
+1 f
+1861 1155(Computer)N
+2201(Science)X
+2471(Division)X
+1328 1245(Department)N
+1727(of)X
+1814(Electrical)X
+2142(Engineering)X
+2554(and)X
+2690(Computer)X
+3030(Science)X
+1754 1335(University)N
+2112(of)X
+2199(California,)X
+2564(Berkeley)X
+1856 1425(Berkeley,)N
+2186(California)X
+2551(94720)X
+2038 1605(August)N
+2289(15,)X
+2409(1994)X
+2 f
+2168 1965(Abstract)N
+1 f
+776 2244(This)N
+948(document)X
+1294(is)X
+1377(the)X
+1505 0.4531(reference)AX
+1836(guide)X
+2045(for)X
+2170(the)X
+2299(4.4BSD)X
+2585(implementations)X
+3149(of)X
+3 f
+3247(nex)X
+1 f
+(/)S
+3 f
+3389(nvi)X
+1 f
+3495(,)X
+3546(which)X
+3773(are)X
+576 2334(reimplementations)N
+1192(of)X
+1279(the)X
+1397(historic)X
+1657(Berkeley)X
+3 f
+1967(ex)X
+1 f
+2043(/)X
+3 f
+2065(vi)X
+1 f
+2147(editors.)X
+2 f
+1996 2778(Acknowledgements)N
+1 f
+776 3063(Bruce)N
+1005(Englar)X
+1256(encouraged)X
+1664(the)X
+1799(early)X
+1997(development)X
+2448(of)X
+2552(the)X
+2687(historic)X
+3 f
+2965(ex)X
+1 f
+3041(/)X
+3 f
+3063(vi)X
+1 f
+3163(editor.)X
+3428(Peter)X
+3631(Kessler)X
+576 3153(helped)N
+811(bring)X
+1001(sanity)X
+1213(to)X
+1296(version)X
+1553(2's)X
+1672(command)X
+2009(layout.)X
+2270(Bill)X
+2410(Joy)X
+2541(wrote)X
+2744(versions)X
+3031(1)X
+3091(and)X
+3227(2.0)X
+3347(through)X
+3616(2.7,)X
+3756(and)X
+576 3243(created)N
+842(the)X
+973(framework)X
+1359(that)X
+1512(users)X
+1710(see)X
+1846(in)X
+1941(the)X
+2072(present)X
+2337(editor.)X
+2597(Mark)X
+2804(Horton)X
+3065(added)X
+3291(macros)X
+3557(and)X
+3707(other)X
+576 3333(features)N
+851(and)X
+987(made)X
+3 f
+1181(ex)X
+1 f
+1257(/)X
+3 f
+1279(vi)X
+1 f
+1361(work)X
+1546(on)X
+1646(a)X
+1702(large)X
+1883(number)X
+2148(of)X
+2235(terminals)X
+2553(and)X
+2689(Unix)X
+2869(systems.)X
+3 f
+776 3456(Nvi)N
+1 f
+918(is)X
+993(originally)X
+1326(derived)X
+1589(from)X
+1767(software)X
+2066(contributed)X
+2454(to)X
+2539(the)X
+2660(University)X
+3021(of)X
+3111(California,)X
+3479(Berkeley)X
+3792(by)X
+576 3546(Steve)N
+774(Kirkendall,)X
+1157(the)X
+1275(author)X
+1500(of)X
+1587(the)X
+3 f
+1705(vi)X
+1 f
+1787(clone)X
+3 f
+1981(elvis)X
+1 f
+2132(.)X
+776 3669(IEEE)N
+992(Standard)X
+1319(Portable)X
+1628(Operating)X
+1991(System)X
+2268(Interface)X
+2597(for)X
+2733(Computer)X
+3095(Environments)X
+3587(\(POSIX\))X
+576 3759(1003.2)N
+816(style)X
+987(Regular)X
+1261(Expression)X
+1637(support)X
+1897(was)X
+2042(done)X
+2218(by)X
+2318(Henry)X
+2539(Spencer.)X
+776 3882(The)N
+930(curses)X
+1161(library)X
+1405(was)X
+1560(originally)X
+1901(done)X
+2087(by)X
+2197(Ken)X
+2361(Arnold.)X
+2658(Scrolling)X
+2981(and)X
+3127(reworking)X
+3487(for)X
+3 f
+3611(nvi)X
+1 f
+3747(was)X
+576 3972(done)N
+752(by)X
+852(Elan)X
+1019(Amir.)X
+776 4095(The)N
+923(Institute)X
+1207(of)X
+1296(Electrical)X
+1626(and)X
+1764(Electronics)X
+2147(Engineers)X
+2490(has)X
+2619(given)X
+2820(us)X
+2914(permission)X
+3288(to)X
+3373(reprint)X
+3610(portions)X
+576 4185(of)N
+682(their)X
+868(documentation.)X
+1423(Portions)X
+1728(of)X
+1834(this)X
+1987(document)X
+2341(are)X
+2478(reprinted)X
+2806(and)X
+2960(reproduced)X
+3360(from)X
+3554(IEEE)X
+3766(Std)X
+576 4275(1003.2-1992,)N
+1031(IEEE)X
+1233(Standard)X
+1546(Portable)X
+1841(Operating)X
+2190(System)X
+2453(Interface)X
+2768(for)X
+2890(Computer)X
+3238(Environments)X
+3716(\(PO-)X
+576 4365(SIX\),)N
+772(copyright)X
+1099(1992)X
+1279(by)X
+1379(the)X
+1497(Institute)X
+1779(of)X
+1866(Electrical)X
+2194(and)X
+2330(Electronics)X
+2711(Engineers,)X
+3072(Inc.)X
+776 4488(The)N
+921(\256nancial)X
+1217(support)X
+1477(of)X
+1564(UUNET)X
+1856(Communications)X
+2422(Services)X
+2714(is)X
+2787(gratefully)X
+3119(acknowledged.)X
+
+3 p
+%%Page: 3 2
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3698(USD:13-3)X
+576 762(1.)N
+676(Description)X
+1 f
+3 f
+776 885(Vi)N
+1 f
+876(is)X
+949(a)X
+1005(screen)X
+1231(oriented)X
+1514(text)X
+1654(editor.)X
+3 f
+1902(Ex)X
+1 f
+2016(is)X
+2090(a)X
+2147(line-oriented)X
+2578(text)X
+2719(editor.)X
+3 f
+2967(Ex)X
+1 f
+3081(and)X
+3 f
+3218(vi)X
+1 f
+3301(are)X
+3421(different)X
+3719(interfaces)X
+576 975(to)N
+674(the)X
+808(same)X
+1009(program,)X
+1336(and)X
+1487(it)X
+1566(is)X
+1654(possible)X
+1951(to)X
+2048(switch)X
+2292(back)X
+2479(and)X
+2630(forth)X
+2821(during)X
+3065(an)X
+3176(edit)X
+3331(session.)X
+3 f
+3637(View)X
+1 f
+3846(is)X
+3934(the)X
+576 1065(equivalent)N
+930(of)X
+1017(using)X
+1210(the)X
+3 f
+9 f
+1328(-)X
+1330(-)X
+3 f
+1374(R)X
+1 f
+1452(\(read-only\))X
+1834(option)X
+2058(of)X
+3 f
+2145(vi)X
+1 f
+2207(.)X
+776 1188(This)N
+957 0.4531(reference)AX
+1297(manual)X
+1572(is)X
+1664(the)X
+1801(one)X
+1956(provided)X
+2280(with)X
+2461(the)X
+3 f
+2598(nex)X
+1 f
+(/)S
+3 f
+2740(nvi)X
+1 f
+2885(versions)X
+3191(of)X
+3297(the)X
+3 f
+3434(ex)X
+1 f
+3510(/)X
+3 f
+3532(vi)X
+1 f
+3634(text)X
+3794(editors.)X
+3 f
+576 1278(Nex)N
+1 f
+710(/)X
+3 f
+732(nvi)X
+1 f
+868(are)X
+997(intended)X
+1302(as)X
+1398(bug-for-bug)X
+1815(compatible)X
+2200(replacements)X
+2653(for)X
+2776(the)X
+2903(original)X
+3181(Fourth)X
+3423(Berkeley)X
+3742(Software)X
+576 1368(Distribution)N
+987(\(4BSD\))X
+3 f
+1261(ex)X
+1 f
+1337(/)X
+3 f
+1359(vi)X
+1 f
+1446(programs.)X
+1814(This)X
+1981 0.4531(reference)AX
+2307(manual)X
+2568(is)X
+2646(accompanied)X
+3095(by)X
+3201(a)X
+3263(traditional-style)X
+3796(manual)X
+576 1458(page.)N
+798(That)X
+975(manual)X
+1241(page)X
+1423(describes)X
+1752(the)X
+1880(functionality)X
+2319(found)X
+2536(in)X
+3 f
+2627(ex)X
+1 f
+2703(/)X
+3 f
+2725(vi)X
+1 f
+2816(in)X
+2907(far)X
+3026(less)X
+3175(detail)X
+3382(than)X
+3549(the)X
+3676(description)X
+576 1548(here.)N
+781(In)X
+874(addition,)X
+1182(it)X
+1252(describes)X
+1578(the)X
+1703(system)X
+1952(interface)X
+2261(to)X
+3 f
+2350(ex)X
+1 f
+2426(/)X
+3 f
+2448(vi)X
+1 f
+2510(,)X
+2557(e.g.)X
+2700(command)X
+3043(line)X
+3190(options,)X
+3472(session)X
+3730(recovery,)X
+576 1638(signals,)N
+838(environmental)X
+1321(variables,)X
+1651(and)X
+1787(similar)X
+2029(things.)X
+776 1761(This)N
+939 0.4531(reference)AX
+1261(is)X
+1336(intended)X
+1634(for)X
+1750(users)X
+1937(already)X
+2196(familiar)X
+2472(with)X
+3 f
+2636(ex)X
+1 f
+2712(/)X
+3 f
+2734(vi)X
+1 f
+2796(.)X
+2858(Anyone)X
+3134(else)X
+3281(should)X
+3516(almost)X
+3751(certainly)X
+576 1851(read)N
+737(a)X
+795(good)X
+977(tutorial)X
+1230(on)X
+1332(the)X
+1452(editor)X
+1661(\256rst.)X
+1847(If)X
+1923(you)X
+2065(are)X
+2186(in)X
+2270(an)X
+2368(unfamiliar)X
+2723(environment,)X
+3169(and)X
+3306(you)X
+3447(absolutely)X
+3797(have)X
+3970(to)X
+576 1941(get)N
+702(work)X
+895(done)X
+1079(immediately,)X
+1527(see)X
+1658(the)X
+1784(section)X
+2039(entitled)X
+2308(``)X
+3 f
+2362(Fast)X
+2538(Startup)X
+1 f
+2800('')X
+2883(in)X
+2974(the)X
+3101(manual)X
+3366(page.)X
+3587(It)X
+3665(is)X
+3747(probably)X
+576 2031(enough)N
+832(to)X
+914(get)X
+1032(you)X
+1172(started.)X
+776 2154(There)N
+984(are)X
+1103(a)X
+1159(few)X
+1300(features)X
+1575(in)X
+3 f
+1657(nex)X
+1 f
+(/)S
+3 f
+1799(nvi)X
+1 f
+1925(that)X
+2065(are)X
+2185(not)X
+2308(found)X
+2516(in)X
+2599(historic)X
+2860(versions)X
+3148(of)X
+3 f
+3236(ex)X
+1 f
+3312(/)X
+3 f
+3334(vi)X
+1 f
+3396(.)X
+3457(Some)X
+3660(of)X
+3748(the)X
+3867(more)X
+576 2244(interesting)N
+941(of)X
+1035(those)X
+1231(features)X
+1513(are)X
+1639(brie\257y)X
+1875(described)X
+2210(in)X
+2299(the)X
+2424(section)X
+2678(entitled)X
+2945(``)X
+3 f
+2999(Additional)X
+3389(Features)X
+1 f
+3688('')X
+3769(near)X
+3934(the)X
+576 2334(end)N
+719(of)X
+813(this)X
+955(document.)X
+1338(For)X
+1476(the)X
+1601(rest)X
+1744(of)X
+1838(this)X
+1980(document,)X
+3 f
+2343(nex)X
+1 f
+(/)S
+3 f
+2485(nvi)X
+1 f
+2618(is)X
+2698(used)X
+2872(only)X
+3042(when)X
+3244(it)X
+3316(is)X
+3397(necessary)X
+3738(to)X
+3828(distin-)X
+576 2424(guish)N
+769(it)X
+833(from)X
+1009(the)X
+1127(historic)X
+1387(implementations)X
+1940(of)X
+3 f
+2027(ex)X
+1 f
+2103(/)X
+3 f
+2125(vi)X
+1 f
+2187(.)X
+776 2547(Future)N
+1012(versions)X
+1306(of)X
+1400(this)X
+1542(software)X
+1846(will)X
+1997(be)X
+2100(periodically)X
+2510(made)X
+2712(available)X
+3030(by)X
+3138(anonymous)X
+3535(ftp,)X
+3672(and)X
+3816(can)X
+3956(be)X
+576 2637(retrieved)N
+882(from)X
+7 f
+1058(ftp.cs.berkeley.edu)X
+1 f
+(,)S
+2010(in)X
+2092(the)X
+2210(directory)X
+7 f
+2520(ucb/4bsd)X
+1 f
+(.)S
+3 f
+576 2823(2.)N
+676(Startup)X
+958(Information)X
+1 f
+3 f
+776 2946(Ex)N
+1 f
+869(/)X
+3 f
+891(vi)X
+1 f
+981(interprets)X
+1312(one)X
+1456(of)X
+1551(two)X
+1699(possible)X
+1989(environmental)X
+2480(variables)X
+2798(and)X
+2942(reads)X
+3140(up)X
+3248(to)X
+3338(three)X
+3527(of)X
+3622(\256ve)X
+3770(possible)X
+576 3036(\256les)N
+737(during)X
+974(startup.)X
+1260(The)X
+1413(variables)X
+1731(and)X
+1875(\256les)X
+2036(are)X
+2163(expected)X
+2477(to)X
+2567(contain)X
+3 f
+2831(ex)X
+1 f
+2935(commands,)X
+3330(not)X
+3 f
+3460(vi)X
+1 f
+3550(commands.)X
+3965(In)X
+576 3126(addition,)N
+879(they)X
+1038(are)X
+1158(interpreted)X
+2 f
+1527(before)X
+1 f
+1753(the)X
+1872(\256le)X
+1996(to)X
+2080(be)X
+2178(edited)X
+2396(is)X
+2471(read,)X
+2652(and)X
+2790(therefore)X
+3103(many)X
+3 f
+3303(ex)X
+1 f
+3401(commands)X
+3770(may)X
+3930(not)X
+576 3216(be)N
+683(used.)X
+901(Generally,)X
+1269(any)X
+1416(command)X
+1763(that)X
+1914(requires)X
+2204(output)X
+2439(to)X
+2532(the)X
+2660(screen)X
+2896(or)X
+2993(that)X
+3143(needs)X
+3356(a)X
+3422(\256le)X
+3554(upon)X
+3744(which)X
+3970(to)X
+576 3306(operate,)N
+853(will)X
+997(cause)X
+1196(an)X
+1292(error)X
+1469(if)X
+1538(included)X
+1834(in)X
+1916(a)X
+1972(startup)X
+2210(\256le)X
+2332(or)X
+2419(environmental)X
+2902(variable.)X
+776 3429(Because)N
+1071(the)X
+3 f
+1197(ex)X
+1 f
+1301(command)X
+1645(set)X
+1762(supported)X
+2106(by)X
+3 f
+2214(nex)X
+1 f
+(/)S
+3 f
+2356(nvi)X
+1 f
+2490(is)X
+2571(a)X
+2635(superset)X
+2926(of)X
+3021(the)X
+3147(command)X
+3491(set)X
+3608(supported)X
+3952(by)X
+576 3519(most)N
+753(historical)X
+1073(implementations)X
+1628(of)X
+3 f
+1717(ex)X
+1 f
+1793(,)X
+3 f
+1835(nex)X
+1 f
+(/)S
+3 f
+1977(nvi)X
+1 f
+2105(can)X
+2238(use)X
+2366(the)X
+2485(startup)X
+2724(\256les)X
+2878(created)X
+3132(for)X
+3247(the)X
+3366(historical)X
+3685(implemen-)X
+576 3609(tations,)N
+829(but)X
+951(the)X
+1069(converse)X
+1375(may)X
+1533(not)X
+1655(be)X
+1751(true.)X
+776 3732(If)N
+852(the)X
+3 f
+9 f
+973(-)X
+975(-)X
+3 f
+1019(s)X
+1 f
+1073(\(the)X
+1221(historic)X
+9 f
+1484(-)X
+1 f
+1551(option\))X
+1805(is)X
+1881(speci\256ed,)X
+2209(or)X
+2299(if)X
+2371(standard)X
+2666(input)X
+2853(is)X
+2929(redirected)X
+3274(from)X
+3453(a)X
+3512(\256le,)X
+3657(all)X
+3760(environ-)X
+576 3822(mental)N
+814(variables)X
+1124(and)X
+1260(startup)X
+1498(\256les)X
+1651(are)X
+1770(ignored.)X
+776 3945(Otherwise,)N
+1146(startup)X
+1384(\256les)X
+1537(and)X
+1673(environmental)X
+2156(variables)X
+2466(are)X
+2585(handled)X
+2859(in)X
+2941(the)X
+3059(following)X
+3390(order:)X
+616 4068(\(1\))N
+830(The)X
+975(\256le)X
+7 f
+1097(/etc/vi.exrc)X
+1 f
+1693(is)X
+1766(read,)X
+1945(as)X
+2032(long)X
+2194(as)X
+2281(it)X
+2345(is)X
+2418(owned)X
+2652(by)X
+2752(root)X
+2901(or)X
+2988(the)X
+3106(effective)X
+3408(user)X
+3562(ID)X
+3667(of)X
+3754(the)X
+3872(user.)X
+616 4191(\(2\))N
+830(The)X
+981(environmental)X
+1471(variable)X
+7 f
+1757(NEXINIT)X
+1 f
+2120(\(or)X
+2241(the)X
+2366(variable)X
+7 f
+2652(EXINIT)X
+1 f
+(,)S
+2987(if)X
+7 f
+3063(NEXINIT)X
+1 f
+3426(is)X
+3506(not)X
+3635(set\))X
+3778(is)X
+3858(inter-)X
+830 4281(preted.)N
+616 4404(\(3\))N
+830(If)X
+918(neither)X
+7 f
+1175(NEXINIT)X
+1 f
+1545(or)X
+7 f
+1646(EXINIT)X
+1 f
+1968(was)X
+2127(set,)X
+2270(and)X
+2420(the)X
+7 f
+2552(HOME)X
+1 f
+2778(environmental)X
+3275(variable)X
+3568(is)X
+3655(set,)X
+3798(the)X
+3930(\256le)X
+7 f
+830 4494($HOME/.nexrc)N
+1 f
+1430(\(or)X
+1548(the)X
+1670(\256le)X
+7 f
+1796($HOME/.exrc)X
+1 f
+(,)S
+2368(if)X
+7 f
+2441($HOME/.nexrc)X
+1 f
+3041(does)X
+3212(not)X
+3338(exist\))X
+3540(is)X
+3617(read,)X
+3800(as)X
+3890(long)X
+830 4584(as)N
+917(the)X
+1035(effective)X
+1337(user)X
+1491(ID)X
+1596(of)X
+1683(the)X
+1801(user)X
+1955(is)X
+2028(root)X
+2177(or)X
+2264(is)X
+2337(the)X
+2455(same)X
+2640(as)X
+2727(the)X
+2845(owner)X
+3066(of)X
+3153(the)X
+3271(\256le.)X
+616 4707(\(4\))N
+830(If)X
+921(the)X
+3 f
+1056(exrc)X
+1 f
+1241(option)X
+1482(was)X
+1644(turned)X
+1886(on)X
+2003(by)X
+2120(one)X
+2273(of)X
+2377(the)X
+2512(previous)X
+2825(startup)X
+3080(information)X
+3495(sources,)X
+3794(the)X
+3930(\256le)X
+7 f
+830 4797(.nexrc)N
+1 f
+1142(\(or)X
+1260(the)X
+1382(\256le)X
+7 f
+1508(.exrc)X
+1 f
+(,)S
+1792(if)X
+7 f
+1864(.nexrc)X
+1 f
+2175(does)X
+2345(not)X
+2470(exist\))X
+2671(is)X
+2747(read,)X
+2929(as)X
+3019(long)X
+3184(as)X
+3274(the)X
+3395(effective)X
+3700(user)X
+3857(ID)X
+3965(of)X
+830 4887(the)N
+948(user)X
+1102(is)X
+1175(the)X
+1293(same)X
+1478(as)X
+1565(the)X
+1683(owner)X
+1904(of)X
+1991(the)X
+2109(\256le.)X
+776 5010(No)N
+894(startup)X
+1132(\256le)X
+1254(is)X
+1327(read)X
+1486(if)X
+1555(it)X
+1619(is)X
+1692(writable)X
+1975(by)X
+2075(anyone)X
+2327(other)X
+2512(than)X
+2670(its)X
+2765(owner.)X
+776 5133(It)N
+845(is)X
+918(not)X
+1040(an)X
+1136(error)X
+1313(for)X
+1427(any)X
+1563(of)X
+1650(the)X
+1768(startup)X
+2006(environmental)X
+2489(variables)X
+2799(or)X
+2886(\256les)X
+3039(not)X
+3161(to)X
+3243(exist.)X
+776 5256(Once)N
+978(all)X
+1090(environmental)X
+1585(variables)X
+1907(are)X
+2038(interpreted,)X
+2438(and)X
+2586(all)X
+2698(startup)X
+2948(\256les)X
+3114(are)X
+3246(read,)X
+3438(the)X
+3569(\256rst)X
+3726(\256le)X
+3861(to)X
+3956(be)X
+576 5346(edited)N
+800(is)X
+881(read)X
+1048(in)X
+1138(\(or)X
+1260(a)X
+1324(temporary)X
+1682(\256le)X
+1812(is)X
+1893(created\).)X
+2221(Then,)X
+2434(any)X
+2578(commands)X
+2953(speci\256ed)X
+3266(using)X
+3467(the)X
+3 f
+9 f
+3593(-)X
+3595(-)X
+3 f
+3639(c)X
+1 f
+3702(option)X
+3933(are)X
+576 5436(executed,)N
+902(in)X
+984(the)X
+1102(context)X
+1358(of)X
+1445(that)X
+1585(\256le.)X
+
+4 p
+%%Page: 4 3
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-4)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+576 762(3.)N
+676(Recovery)X
+1 f
+776 885(There)N
+991(is)X
+1071(no)X
+1178(recovery)X
+1487(program)X
+1786(for)X
+3 f
+1907(nex)X
+1 f
+(/)S
+3 f
+2049(nvi)X
+1 f
+2155(,)X
+2202(nor)X
+2336(does)X
+3 f
+2510(nex)X
+1 f
+(/)S
+3 f
+2652(nvi)X
+1 f
+2785(run)X
+2919(setuid.)X
+3177(Recovery)X
+3512(\256les)X
+3672(are)X
+3799(created)X
+576 975(readable)N
+887(and)X
+1041(writable)X
+1342(by)X
+1460(the)X
+1596(owner)X
+1835(only.)X
+2055(Users)X
+2276(may)X
+2452(recover)X
+2732(any)X
+2886(\256le)X
+3026(which)X
+3260(they)X
+3436(can)X
+3585(read,)X
+3781(and)X
+3934(the)X
+576 1065(superuser)N
+904(may)X
+1062(recover)X
+1324(any)X
+1460(edit)X
+1600(session.)X
+776 1188(Edit)N
+952(sessions)X
+1257(are)X
+1399(backed)X
+1670(by)X
+1793(\256les)X
+1969(in)X
+2074(the)X
+2215(directory)X
+2548(named)X
+2805(by)X
+2929(the)X
+3 f
+3071(recdir)X
+1 f
+3325(option)X
+3573(\(the)X
+3742(directory)X
+7 f
+576 1278(/var/tmp/vi.recover)N
+1 f
+1508(by)X
+1608(default\),)X
+1898(and)X
+2034(are)X
+2153(named)X
+2387(``)X
+3 f
+2441(vi.XXXXXX)X
+1 f
+2871('',)X
+2965(where)X
+3182(``)X
+3 f
+3236(XXXXXX)X
+1 f
+3584('')X
+3658(is)X
+3731(a)X
+3787(number)X
+576 1368(related)N
+816(to)X
+899(the)X
+1018(process)X
+1281(ID.)X
+1428(When)X
+1642(a)X
+1700(\256le)X
+1824(is)X
+1899(\256rst)X
+2045(modi\256ed,)X
+2371(a)X
+2429(second)X
+2674(recovery)X
+2978(\256le)X
+3102(containing)X
+3462(an)X
+3560(email)X
+3760(message)X
+576 1458(for)N
+691(the)X
+810(user)X
+965(is)X
+1039(created,)X
+1313(and)X
+1450(is)X
+1524(named)X
+1759(``)X
+3 f
+1813 0.3077(recover.XXXXXX)AX
+1 f
+2441('',)X
+2536(where,)X
+2774(again,)X
+2989(``)X
+3 f
+3043(XXXXXX)X
+1 f
+3391('')X
+3466(is)X
+3540(associated)X
+3890(with)X
+576 1548(the)N
+698(process)X
+963(ID.)X
+1112(Both)X
+1291(\256les)X
+1448(are)X
+1571(removed)X
+1876(at)X
+1958(the)X
+2080(end)X
+2220(of)X
+2311(a)X
+2371(normal)X
+2622(edit)X
+2766(session,)X
+3041(but)X
+3168(will)X
+3317(remain)X
+3565(if)X
+3639(the)X
+3762(edit)X
+3907(ses-)X
+576 1638(sion)N
+729(is)X
+802(abnormally)X
+1187(terminated)X
+1550(or)X
+1637(the)X
+1755(user)X
+1909(runs)X
+2067(the)X
+3 f
+2185(ex)X
+2281(preserve)X
+1 f
+2596(command.)X
+776 1761(The)N
+3 f
+935(recdir)X
+1 f
+1180(option)X
+1419(may)X
+1592(be)X
+1703(set)X
+1827(in)X
+1924(either)X
+2142(the)X
+2275(user's)X
+2502(or)X
+2604(system's)X
+2919(startup)X
+3172(information,)X
+3605(changing)X
+3934(the)X
+576 1851(recovery)N
+887(directory.)X
+1246(\(Note,)X
+1478(however,)X
+1804(that)X
+1953(if)X
+2031(a)X
+2096(memory)X
+2392(based)X
+2604(\256le)X
+2735(system)X
+2985(is)X
+3066(used)X
+3241(as)X
+3336(the)X
+3462(backup)X
+3722(directory,)X
+576 1941(each)N
+752(system)X
+1002(reboot)X
+1235(will)X
+1388(delete)X
+1609(all)X
+1718(of)X
+1814(the)X
+1941(recovery)X
+2252(\256les!)X
+2461(The)X
+2615(same)X
+2809(caution)X
+3074(applies)X
+3330(to)X
+3421(directories)X
+3789(such)X
+3965(as)X
+7 f
+576 2031(/tmp)N
+1 f
+792(which)X
+1012(are)X
+1135(cleared)X
+1392(of)X
+1483(their)X
+1654(contents)X
+1945(by)X
+2049(a)X
+2108(system)X
+2353(reboot,)X
+2601(or)X
+7 f
+2691(/usr/tmp)X
+1 f
+3098(which)X
+3317(is)X
+3393(periodically)X
+3799(cleared)X
+576 2121(of)N
+663(old)X
+785(\256les)X
+938(on)X
+1038(many)X
+1236(systems.\))X
+776 2244(The)N
+926(recovery)X
+1233(directory)X
+1548(should)X
+1786(be)X
+1887(owned)X
+2126(by)X
+2231(root,)X
+2405(or)X
+2497(at)X
+2580(least)X
+2752(by)X
+2857(a)X
+2918(pseudo-user.)X
+3371(In)X
+3463(addition,)X
+3770(if)X
+3844(direc-)X
+576 2334(tory)N
+727(``sticky-bit'')X
+1159(semantics)X
+1497(are)X
+1618(available,)X
+1950(the)X
+2070(directory)X
+2382(should)X
+2617(have)X
+2791(the)X
+2911(sticky-bit)X
+3234(set)X
+3344(so)X
+3436(that)X
+3577(\256les)X
+3731(may)X
+3890(only)X
+576 2424(be)N
+676(removed)X
+981(by)X
+1085(their)X
+1256(owners.)X
+1552(The)X
+1702(recovery)X
+2009(directory)X
+2324(must)X
+2504(be)X
+2605(read,)X
+2789(write,)X
+2999(and)X
+3140(executable)X
+3509(by)X
+3614(any)X
+3755(user,)X
+3934(i.e.)X
+576 2514(mode)N
+774(1777.)X
+776 2637(If)N
+850(the)X
+968(recovery)X
+1270(directory)X
+1581(does)X
+1749(not)X
+1872(exist,)X
+3 f
+2064(ex)X
+1 f
+2140(/)X
+3 f
+2162(vi)X
+1 f
+2245(will)X
+2390(attempt)X
+2651(to)X
+2734(create)X
+2948(it.)X
+3053(This)X
+3216(can)X
+3349(result)X
+3548(in)X
+3631(the)X
+3750(recovery)X
+576 2727(directory)N
+895(being)X
+1102(owned)X
+1345(by)X
+1454(a)X
+1518(normal)X
+1773(user,)X
+1955(which)X
+2179(means)X
+2412(that)X
+2560(that)X
+2708(user)X
+2870(will)X
+3022(be)X
+3126(able)X
+3288(to)X
+3378(remove)X
+3647(other)X
+3840(user's)X
+576 2817(recovery)N
+880(and)X
+1018(backup)X
+1272(\256les.)X
+1467(This)X
+1631(is)X
+1706(annoying,)X
+2046(but)X
+2170(is)X
+2245(not)X
+2369(a)X
+2427(security)X
+2703(issue)X
+2886(as)X
+2976(the)X
+3097(user)X
+3254(cannot)X
+3491(otherwise)X
+3826(access)X
+576 2907(or)N
+663(modify)X
+914(the)X
+1032(\256les.)X
+776 3030(The)N
+924(recovery)X
+1229(\256le)X
+1354(has)X
+1484(all)X
+1587(of)X
+1677(the)X
+1798(necessary)X
+2134(information)X
+2535(in)X
+2621(it)X
+2689(to)X
+2775(enable)X
+3009(the)X
+3131(user)X
+3289(to)X
+3375(recover)X
+3641(the)X
+3763(edit)X
+3907(ses-)X
+576 3120(sion.)N
+774(In)X
+866(addition,)X
+1173(it)X
+1242(has)X
+1374(all)X
+1479(of)X
+1571(the)X
+1694(necessary)X
+2032(email)X
+2235(headers)X
+2505(for)X
+2 f
+2623(sendmail)X
+1 f
+2912(\(8\).)X
+3070(When)X
+3286(the)X
+3408(system)X
+3654(is)X
+3731(rebooted,)X
+576 3210(all)N
+677(of)X
+765(the)X
+884(\256les)X
+1038(in)X
+7 f
+1121(/var/tmp/vi.recover)X
+1 f
+2054(named)X
+2289(``)X
+3 f
+2343 0.3077(recover.XXXXXX)AX
+1 f
+2971('')X
+3046(should)X
+3280(be)X
+3377(sent)X
+3527(to)X
+3611(their)X
+3780(owners,)X
+576 3300(by)N
+684(email,)X
+910(using)X
+1111(the)X
+3 f
+9 f
+1237(-)X
+1239(-)X
+3 f
+1283(t)X
+1 f
+1338(option)X
+1570(of)X
+3 f
+1665(sendmail)X
+1 f
+1999(\(or)X
+2121(a)X
+2185(similar)X
+2434(mechanism)X
+2826(in)X
+2915(other)X
+3107(mailers\).)X
+3437(If)X
+3 f
+3518(ex)X
+1 f
+3594(/)X
+3 f
+3616(vi)X
+1 f
+3705(receives)X
+3996(a)X
+576 3390(hangup)N
+839(\(SIGHUP\))X
+1209(signal,)X
+1447(or)X
+1541(the)X
+1666(user)X
+1828(executes)X
+2133(the)X
+3 f
+2259(ex)X
+2363(preserve)X
+1 f
+2686(command,)X
+3 f
+3050(ex)X
+1 f
+3126(/)X
+3 f
+3148(vi)X
+1 f
+3238(will)X
+3390(automatically)X
+3854(email)X
+576 3480(the)N
+694(recovery)X
+996(information)X
+1394(to)X
+1476(the)X
+1594(user.)X
+776 3603(If)N
+853(your)X
+1023(system)X
+1268(does)X
+1438(not)X
+1564(have)X
+1740(the)X
+3 f
+1862(sendmail)X
+1 f
+2192(utility)X
+2406(\(or)X
+2524(a)X
+2584(mailer)X
+2813(program)X
+3109(which)X
+3329(supports)X
+3624(its)X
+3723(interface\))X
+576 3693(the)N
+700(source)X
+936(\256le)X
+7 f
+1063(nvi/common/recover.c)X
+1 f
+2048(will)X
+2197(have)X
+2374(to)X
+2461(be)X
+2562(modi\256ed)X
+2871(to)X
+2958(use)X
+3090(your)X
+3262(local)X
+3443(mail)X
+3610(delivery)X
+3898(pro-)X
+576 3783(grams.)N
+833(Note,)X
+1030(if)X
+3 f
+1100(nex)X
+1 f
+(/)S
+3 f
+1242(nvi)X
+1 f
+1369(is)X
+1443(changed)X
+1732(to)X
+1815(use)X
+1944(another)X
+2207(mailer,)X
+2454(it)X
+2520(is)X
+2595(important)X
+2928(to)X
+3012(remember)X
+3360(that)X
+3502(the)X
+3622(owner)X
+3845(of)X
+3934(the)X
+576 3873(\256le)N
+708(given)X
+916(to)X
+1008(the)X
+1136(mailer)X
+1371(is)X
+1454(the)X
+3 f
+1582(nex)X
+1 f
+(/)S
+3 f
+1724(nvi)X
+1 f
+1860(user,)X
+2044(so)X
+2145(nothing)X
+2419(in)X
+2511(the)X
+2638(\256le)X
+2769(should)X
+3011(be)X
+3116(trusted)X
+3363(as)X
+3459(it)X
+3532(may)X
+3699(have)X
+3880(been)X
+576 3963(modi\256ed)N
+880(in)X
+962(an)X
+1058(effort)X
+1257(to)X
+1339(compromise)X
+1755(the)X
+1873(system.)X
+776 4086(Finally,)N
+1046(the)X
+1168(owner)X
+1393(execute)X
+1663(bit)X
+1771(is)X
+1848(set)X
+1961(on)X
+2065(backup)X
+2321(\256les)X
+2478(when)X
+2676(they)X
+2838(are)X
+2961(created,)X
+3238(and)X
+3378(unset)X
+3571(when)X
+3770(they)X
+3933(are)X
+576 4176(\256rst)N
+724(modi\256ed,)X
+1052(e.g.)X
+1192(backup)X
+1448(\256les)X
+1605(that)X
+1749(have)X
+1925(no)X
+2029(associated)X
+2383(email)X
+2585(recovery)X
+2891(\256le)X
+3017(will)X
+3165(have)X
+3341(this)X
+3480(bit)X
+3588(set.)X
+3741(\(There)X
+3979(is)X
+576 4266(also)N
+735(a)X
+801(small)X
+1004(window)X
+1292(where)X
+1519(empty)X
+1749(\256les)X
+1912(can)X
+2054(be)X
+2160(created)X
+2424(and)X
+2571(not)X
+2704(yet)X
+2833(have)X
+3016(this)X
+3162(bit)X
+3277(set.)X
+3437(This)X
+3610(is)X
+3694(due)X
+3841(to)X
+3934(the)X
+576 4356(method)N
+836(in)X
+918(which)X
+1134(the)X
+1252(\256les)X
+1405(are)X
+1524(created.\))X
+1844(Such)X
+2024(\256les)X
+2177(should)X
+2410(be)X
+2506(deleted)X
+2758(when)X
+2952(the)X
+3070(system)X
+3312(reboots.)X
+776 4479(A)N
+884(simple)X
+1147(way)X
+1331(to)X
+1443(do)X
+1573(this)X
+1738(cleanup)X
+2038(is)X
+2141(to)X
+2253(insert)X
+2481(the)X
+2630(following)X
+2992(Bourne)X
+3279(shell)X
+3481(script)X
+3710(into)X
+3885(your)X
+7 f
+576 4569(/etc/rc.local)N
+1 f
+1224(\(or)X
+1342(other)X
+1531(startup\))X
+1800(\256le.)X
+1966(The)X
+2115(script)X
+2316(should)X
+2552(work)X
+2740(with)X
+2905(the)X
+3026(historic)X
+3289(Bourne)X
+3548(shell,)X
+3742(a)X
+3801(POSIX)X
+576 4659(1003.2)N
+818(shell)X
+991(or)X
+1080(the)X
+1201(Korn)X
+1389(shell.)X
+1603(\(A)X
+1711(copy)X
+1890(of)X
+1980(this)X
+2118(script)X
+2319(is)X
+2395(included)X
+2694(as)X
+7 f
+2784(nvi/install/recover.script)X
+1 f
+576 4749(in)N
+658(the)X
+3 f
+776(nex)X
+1 f
+(/)S
+3 f
+918(nvi)X
+1 f
+1044(distribution.\))X
+
+5 p
+%%Page: 5 4
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3698(USD:13-5)X
+7 f
+896 762(#)N
+1184(@\(#\)recover.script)X
+2336(8.4)X
+2528(\(Berkeley\))X
+3056(8/13/94)X
+896 852(#)N
+896 942(#)N
+992(Recover)X
+1376(nvi)X
+1568(editor)X
+1904(files:)X
+896 1032(RECDIR=/var/tmp/vi.recover)N
+896 1122(SENDMAIL=/usr/lib/sendmail)N
+896 1212(echo)N
+1136('Recovering)X
+1712(nvi)X
+1904(editor)X
+2240(sessions.')X
+896 1392(#)N
+992(Unmodified)X
+1520(nvi)X
+1712(editor)X
+2048(backup)X
+2384(files)X
+2672(are)X
+2864(either)X
+3200(zero)X
+3440(length)X
+3776(or)X
+896 1482(#)N
+992(have)X
+1232(the)X
+1424(execute)X
+1808(bit)X
+2000(set.)X
+2288(Delete)X
+2624(both)X
+2864(cases.)X
+896 1572(vibackup=`echo)N
+1616($RECDIR/vi.*`)X
+896 1662(if)N
+1040([)X
+1136("$vibackup")X
+1712(!=)X
+1856("$RECDIR/vi.*")X
+2576(];)X
+2720(then)X
+1184 1752(for)N
+1376(i)X
+1472(in)X
+1616($vibackup;)X
+2144(do)X
+1472 1842(if)N
+1616(test)X
+1856(-x)X
+2000($i)X
+2144(-o)X
+2288(!)X
+2384(-s)X
+2528($i;)X
+2720(then)X
+1760 1932(rm)N
+1904($i)X
+1472 2022(fi)N
+1184 2112(done)N
+896 2202(fi)N
+896 2382(#)N
+992(It)X
+1136(is)X
+1280(possible)X
+1712(to)X
+1856(get)X
+2048(incomplete)X
+2576(recovery)X
+3008(files,)X
+3344(if)X
+3488(the)X
+3680(editor)X
+896 2472(#)N
+992(crashes)X
+1376(at)X
+1520(the)X
+1712(right)X
+2000(time.)X
+2336(Delete)X
+2672(any)X
+2864(recovery)X
+3296(files)X
+3584(without)X
+896 2562(#)N
+992(corresponding)X
+1664(backup)X
+2000(files,)X
+2336(otherwise)X
+2816(send)X
+3056(mail)X
+3296(to)X
+3440(the)X
+3632(user.)X
+896 2652(virecovery=`echo)N
+1712($RECDIR/recover.*`)X
+896 2742(if)N
+1040([)X
+1136("$virecovery")X
+1808(!=)X
+1952("$RECDIR/recover.*")X
+2912(];)X
+3056(then)X
+1184 2832(for)N
+1376(i)X
+1472(in)X
+1616($virecovery;)X
+2240(do)X
+1472 2922(recfile=`awk)N
+2096('/\303X-vi-recover-path:/{print)X
+3488($2}')X
+3728(<)X
+3824($i`)X
+1472 3012(if)N
+1616(test)X
+1856(!)X
+1952(-n)X
+2096($recfile)X
+2528(-a)X
+2672(-s)X
+2816($recfile;)X
+3296(then)X
+1760 3102($SENDMAIL)N
+2240(-t)X
+2384(<)X
+2480($i)X
+1472 3192(else)N
+1760 3282(rm)N
+1904($i)X
+1472 3372(fi)N
+1184 3462(done)N
+896 3552(fi)N
+1 f
+776 3798(If)N
+859(you)X
+1008(are)X
+1136(not)X
+1267(using)X
+1469(the)X
+1596(default)X
+1848(value)X
+2051(for)X
+2174(the)X
+3 f
+2301(recdir)X
+1 f
+2540(option,)X
+2793(be)X
+2898(sure)X
+3062(to)X
+3154(substitute)X
+3490(the)X
+3618(value)X
+3822(you're)X
+576 3888(using)N
+769(for)X
+883(the)X
+7 f
+1001(RECDIR)X
+1 f
+1309(value)X
+1503(in)X
+1585(the)X
+1703(recovery)X
+2005(script.)X
+776 4011(If)N
+882(the)X
+1032(path)X
+1222(of)X
+1341(your)X
+1540(system's)X
+3 f
+1872(sendmail)X
+1 f
+2230(program)X
+2555(\(or)X
+2702(whatever)X
+3050(mailer)X
+3308(you're)X
+3571(using\))X
+3824(is)X
+3930(not)X
+7 f
+576 4101(/usr/lib/sendmail)N
+1 f
+(,)S
+1451(be)X
+1566(sure)X
+1739(to)X
+1840(substitute)X
+2184(the)X
+2320(correct)X
+2582(pathname)X
+2932(for)X
+3064(the)X
+7 f
+3200(SENDMAIL)X
+1 f
+3622(value)X
+3834(in)X
+3934(the)X
+576 4191(recovery)N
+878(script.)X
+1116(Consult)X
+1384(the)X
+1502(manual)X
+1758(page)X
+1930(for)X
+2044(details)X
+2273(on)X
+2373(recovering)X
+2737(preserved)X
+3070(or)X
+3157(aborted)X
+3418(editing)X
+3660(sessions.)X
+3 f
+576 4377(4.)N
+676(Sizing)X
+904(the)X
+1031(Screen)X
+1 f
+776 4500(The)N
+926(size)X
+1076(of)X
+1168(the)X
+1291(screen)X
+1522(can)X
+1659(be)X
+1760(set)X
+1874(in)X
+1961(a)X
+2022(number)X
+2292(of)X
+2384(ways.)X
+3 f
+2614(Ex)X
+1 f
+2707(/)X
+3 f
+2729(vi)X
+1 f
+2817(takes)X
+3008(the)X
+3132(following)X
+3469(steps)X
+3655(until)X
+3827(values)X
+576 4590(are)N
+695(obtained)X
+991(for)X
+1105(both)X
+1267(the)X
+1385(number)X
+1650(of)X
+1737(rows)X
+1913(and)X
+2049(number)X
+2314(of)X
+2401(columns)X
+2692(in)X
+2774(the)X
+2892(screen.)X
+616 4713(\(1\))N
+830(If)X
+904(the)X
+1022(environmental)X
+1505(variable)X
+7 f
+1784(LINES)X
+1 f
+2044(exists,)X
+2266(it)X
+2330(is)X
+2403(used)X
+2570(to)X
+2652(specify)X
+2904(the)X
+3022(number)X
+3287(of)X
+3374(rows)X
+3550(in)X
+3632(the)X
+3750(screen.)X
+616 4836(\(2\))N
+830(If)X
+909(the)X
+1032(environmental)X
+1520(variable)X
+7 f
+1804(COLUMNS)X
+1 f
+2165(exists,)X
+2393(it)X
+2463(is)X
+2542(used)X
+2715(to)X
+2803(specify)X
+3061(the)X
+3185(number)X
+3456(of)X
+3549(columns)X
+3846(in)X
+3934(the)X
+830 4926(screen.)N
+616 5049(\(3\))N
+830(The)X
+975(TIOCGWINSZ)X
+2 f
+1494(ioctl)X
+1 f
+1636(\(2\))X
+1750(is)X
+1823(attempted)X
+2159(on)X
+2259(the)X
+2377(standard)X
+2669(error)X
+2846(\256le)X
+2968(descriptor.)X
+616 5172(\(4\))N
+830(The)X
+978(termcap)X
+1260(entry)X
+1448(\(or)X
+1565(terminfo)X
+1864(entry)X
+2053(on)X
+2157(System)X
+2416(V)X
+2498(machines\))X
+2852(is)X
+2929(checked)X
+3217(for)X
+3335(the)X
+3457(``li'')X
+3633(entry)X
+3822(\(rows\))X
+830 5262(and)N
+966(the)X
+1084(``co'')X
+1288(entry)X
+1473(\(columns\).)X
+616 5385(\(5\))N
+830(The)X
+975(number)X
+1240(of)X
+1327(rows)X
+1503(is)X
+1576(set)X
+1685(to)X
+1767(24,)X
+1887(and)X
+2023(the)X
+2141(number)X
+2406(of)X
+2493(columns)X
+2784(is)X
+2857(set)X
+2966(to)X
+3048(80.)X
+776 5508(If)N
+851(a)X
+908(window)X
+1187(change)X
+1436(size)X
+1582(signal)X
+1794(\(SIGWINCH\))X
+2270(is)X
+2344(received,)X
+2658(the)X
+2777(new)X
+2932(window)X
+3211(size)X
+3357(is)X
+3431(retrieved)X
+3739(using)X
+3934(the)X
+576 5598(TIOCGWINSZ)N
+2 f
+1095(ioctl)X
+1 f
+1237(\(2\))X
+1351(call,)X
+1507(and)X
+1643(all)X
+1743(other)X
+1928(information)X
+2326(is)X
+2399(ignored.)X
+
+6 p
+%%Page: 6 5
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-6)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+576 762(5.)N
+676(Character)X
+1049(Display)X
+1 f
+776 885(In)N
+865(both)X
+3 f
+1030(ex)X
+1 f
+1129(and)X
+3 f
+1268(vi)X
+1 f
+1353(printable)X
+1661(characters)X
+2011(as)X
+2101(de\256ned)X
+2360(by)X
+2 f
+2463(isprint)X
+1 f
+2671(\(3\))X
+2788(are)X
+2910(displayed)X
+3240(using)X
+3436(the)X
+3557(local)X
+3736(character)X
+576 975(set.)N
+776 1098(Non-printable)N
+1255(characters,)X
+1631(for)X
+1754(which)X
+2 f
+1980(iscntrl)X
+1 f
+2184(\(3\))X
+2308(returns)X
+2561(true,)X
+2736(and)X
+2882(which)X
+3108(are)X
+3237(less)X
+3387(than)X
+3555(octal)X
+3741(\\076,)X
+3933(are)X
+576 1188(displayed)N
+915(as)X
+1014(the)X
+1144(string)X
+1357(``)X
+7 f
+1411(\303<character>)X
+1 f
+('',)S
+2092(where)X
+7 f
+2320(<character>)X
+1 f
+2879(is)X
+2963(the)X
+3092(character)X
+3419(that)X
+3570(is)X
+3654(the)X
+3783(original)X
+576 1278 0.3250(character's)AN
+961(value)X
+1166(offset)X
+1380(from)X
+1568(the)X
+1698(``)X
+7 f
+1752(@)X
+1 f
+('')S
+1886(character.)X
+2254(For)X
+2397(example,)X
+2721(the)X
+2851(octal)X
+3039(character)X
+3367(\\001)X
+3541(is)X
+3626(displayed)X
+3965(as)X
+576 1368(``)N
+7 f
+630(\303A)X
+1 f
+(''.)S
+845(If)X
+2 f
+924(iscntrl)X
+1 f
+1128(\(3\))X
+1247(returns)X
+1495(true)X
+1645(for)X
+1764(the)X
+1887(octal)X
+2068(character)X
+2389(\\177,)X
+2576(it)X
+2645(is)X
+2723(displayed)X
+3054(as)X
+3145(the)X
+3267(string)X
+3473(``)X
+7 f
+3527(\303?)X
+1 f
+(''.)S
+3741(All)X
+3867(other)X
+576 1458(characters)N
+947(are)X
+1090(displayed)X
+1441(as)X
+1552(either)X
+1779(hexadecimal)X
+2229(values,)X
+2498(in)X
+2604(the)X
+2746(form)X
+2946(``)X
+7 f
+3000(0x<high-halfbyte>)X
+3888(...)X
+576 1548(0x<low-halfbyte>)N
+1 f
+('',)S
+1469(or)X
+1587(as)X
+1705(octal)X
+1911(values,)X
+2186(in)X
+2298(the)X
+2446(form)X
+2652(``)X
+7 f
+2706(\\<high-one-or-two-bits>)X
+3888(...)X
+576 1638(\\<low-three-bits>)N
+1 f
+(''.)S
+1506(The)X
+1651(display)X
+1902(of)X
+1989(unknown)X
+2307(characters)X
+2654(is)X
+2727(based)X
+2930(on)X
+3030(the)X
+3148(value)X
+3342(of)X
+3429(the)X
+3 f
+3547(octal)X
+1 f
+3732(option.)X
+776 1761(In)N
+3 f
+864(vi)X
+1 f
+947(command)X
+1285(mode,)X
+1505(the)X
+1625(cursor)X
+1848(is)X
+1923(always)X
+2168(positioned)X
+2523(on)X
+2625(the)X
+2745(last)X
+2878(column)X
+3140(of)X
+3229(characters)X
+3578(which)X
+3796(take)X
+3952(up)X
+576 1851(more)N
+765(than)X
+927(one)X
+1067(column)X
+1331(on)X
+1435(the)X
+1557(screen.)X
+1827(In)X
+3 f
+1918(vi)X
+1 f
+2003(text)X
+2146(input)X
+2333(mode,)X
+2554(the)X
+2675(cursor)X
+2899(is)X
+2975(positioned)X
+3331(on)X
+3434(the)X
+3555(\256rst)X
+3702(column)X
+3965(of)X
+576 1941(characters)N
+923(which)X
+1139(take)X
+1293(up)X
+1393(more)X
+1578(than)X
+1736(one)X
+1872(column)X
+2132(on)X
+2232(the)X
+2350(screen.)X
+3 f
+576 2127(6.)N
+676(Multiple)X
+989(Screens)X
+1 f
+3 f
+776 2250(Nvi)N
+1 f
+916(supports)X
+1207(multiple)X
+1493(screens)X
+1750(by)X
+1851(dividing)X
+2138(the)X
+2257(window)X
+2536(into)X
+2681(regions.)X
+2978(It)X
+3048(also)X
+3198(supports)X
+3490(stacks)X
+3707(of)X
+3795(screens)X
+576 2340(by)N
+676(permitting)X
+1029(the)X
+1147(user)X
+1301(to)X
+1383(change)X
+1631(the)X
+1749(set)X
+1858(of)X
+1945(screens)X
+2202(that)X
+2342(are)X
+2461(currently)X
+2771(displayed.)X
+776 2463(The)N
+924(command)X
+3 f
+1263(split)X
+1 f
+1432(divides)X
+1686(the)X
+1807(current)X
+2058(screen)X
+2287(into)X
+2434(two)X
+2577(regions)X
+2836(of)X
+2926(approximately)X
+3412(equal)X
+3609(size.)X
+3797(If)X
+3875(a)X
+3935(list)X
+576 2553(of)N
+668(\256les)X
+826(are)X
+950(speci\256ed)X
+1260(as)X
+1352(arguments)X
+1711(to)X
+1798(the)X
+3 f
+1921(split)X
+1 f
+2092(command,)X
+2452(the)X
+2574(list)X
+2695(of)X
+2786(\256les)X
+2943(to)X
+3029(be)X
+3129(edited)X
+3349(is)X
+3426(initialized)X
+3770(as)X
+3861(if)X
+3934(the)X
+3 f
+576 2643(next)N
+1 f
+747(command)X
+1087(had)X
+1227(been)X
+1403(used.)X
+1614(If)X
+1692(no)X
+1796(\256les)X
+1953(are)X
+2076(speci\256ed,)X
+2405(the)X
+2527(new)X
+2685(screen)X
+2915(will)X
+3063(begin)X
+3265(by)X
+3370(editing)X
+3617(the)X
+3740(same)X
+3930(\256le)X
+576 2733(as)N
+663(the)X
+781(previous)X
+1077(screen.)X
+776 2856(When)N
+992(more)X
+1181(than)X
+1343(one)X
+1483(screen)X
+1713(is)X
+1790(editing)X
+2036(a)X
+2097(\256le,)X
+2244(changes)X
+2528(in)X
+2615(any)X
+2756(screen)X
+2987(are)X
+3111(re\257ected)X
+3413(in)X
+3500(all)X
+3605(other)X
+3795(screens)X
+576 2946(editing)N
+820(the)X
+940(same)X
+1127(\256le.)X
+1291(Exiting)X
+1548(any)X
+1686(screen)X
+1914(without)X
+2180(saving)X
+2410(any)X
+2547(changes)X
+2827(\(or)X
+2942(explicitly)X
+3265(discarding)X
+3620(them\))X
+3828(is)X
+3902(per-)X
+576 3036(mitted)N
+800(until)X
+966(the)X
+1084(last)X
+1215(screen)X
+1441(editing)X
+1683(the)X
+1801(\256le)X
+1923(is)X
+1996(exited.)X
+776 3159(The)N
+3 f
+923(resize)X
+1 f
+1142(command)X
+1480(permits)X
+1742(resizing)X
+2018(of)X
+2107(individual)X
+2453(screens.)X
+2752(Screens)X
+3024(may)X
+3184(be)X
+3282(grown,)X
+3529(shrunk)X
+3769(or)X
+3858(set)X
+3970(to)X
+576 3249(an)N
+672(absolute)X
+959(number)X
+1224(of)X
+1311(rows.)X
+776 3372(The)N
+3 f
+921(\303W)X
+1 f
+1048(command)X
+1384(is)X
+1457(used)X
+1624(to)X
+1706(switch)X
+1935(between)X
+2223(screens.)X
+2520(Each)X
+3 f
+2701(\303W)X
+1 f
+2829(moves)X
+3059(to)X
+3142(the)X
+3261(next)X
+3420(lower)X
+3624(screen)X
+3851(in)X
+3934(the)X
+576 3462(window,)N
+874(or)X
+961(to)X
+1043(the)X
+1161(\256rst)X
+1305(screen)X
+1531(in)X
+1613(the)X
+1731(window)X
+2009(if)X
+2078(there)X
+2259(are)X
+2378(no)X
+2478(lower)X
+2681(screens.)X
+776 3585(The)N
+3 f
+925(bg)X
+1 f
+1033(command)X
+1374(``backgrounds'')X
+1917(the)X
+2040(current)X
+2293(screen.)X
+2564(The)X
+2714(screen)X
+2945(disappears)X
+3309(from)X
+3490(the)X
+3613(window,)X
+3916(and)X
+576 3675(the)N
+702(rows)X
+886(it)X
+958(occupied)X
+1276(are)X
+1403(taken)X
+1605(over)X
+1776(by)X
+1884(a)X
+1948(neighboring)X
+2363(screen.)X
+2637(It)X
+2714(is)X
+2795(an)X
+2899(error)X
+3083(to)X
+3172(attempt)X
+3439(to)X
+3528(background)X
+3934(the)X
+576 3765(only)N
+738(screen)X
+964(in)X
+1046(the)X
+1164(window.)X
+776 3888(The)N
+3 f
+935(display)X
+1212(screens)X
+1 f
+1496(command)X
+1846(displays)X
+2142(the)X
+2274(names)X
+2513(of)X
+2614(the)X
+2747(\256les)X
+2915(associated)X
+3280(with)X
+3457(the)X
+3590(current)X
+3853(back-)X
+576 3978(grounded)N
+899(screens)X
+1156(in)X
+1238(the)X
+1356(window.)X
+776 4101(The)N
+3 f
+932(fg)X
+1031([\256le])X
+1 f
+1219(command)X
+1567(``foregrounds'')X
+2095(the)X
+2225(\256rst)X
+2381(screen)X
+2619(in)X
+2713(the)X
+2843(list)X
+2972(of)X
+3071(backgrounded)X
+3558(screens)X
+3827(that)X
+3979(is)X
+576 4191(associated)N
+934(with)X
+1103(its)X
+1205(argument.)X
+1575(If)X
+1656(no)X
+1763(\256le)X
+1892(argument)X
+2222(is)X
+2302(speci\256ed,)X
+2634(the)X
+2759(\256rst)X
+2910(screen)X
+3143(on)X
+3250(the)X
+3375(list)X
+3499(is)X
+3579(foregrounded.)X
+576 4281(Foregrounding)N
+1072(consists)X
+1345(of)X
+1432(backgrounding)X
+1933(the)X
+2051(current)X
+2299(screen,)X
+2545(and)X
+2681(replacing)X
+3000(its)X
+3095(space)X
+3294(in)X
+3376(the)X
+3494(window)X
+3772(with)X
+3934(the)X
+576 4371(foregrounded)N
+1029(screen.)X
+776 4494(If)N
+852(the)X
+972(last)X
+1105(screen)X
+1333(in)X
+1417(the)X
+1537(window)X
+1817(is)X
+1892(exited,)X
+2131(and)X
+2270(there)X
+2454(are)X
+2576(backgrounded)X
+3054(screens,)X
+3334(the)X
+3455(\256rst)X
+3602(screen)X
+3831(on)X
+3934(the)X
+576 4584(list)N
+693(of)X
+780(backgrounded)X
+1255(screens)X
+1512(takes)X
+1697(over)X
+1860(the)X
+1978(window.)X
+3 f
+576 4770(7.)N
+676(Regular)X
+972(Expressions)X
+1400(and)X
+1548(Replacement)X
+2014(Strings)X
+1 f
+776 4893(Regular)N
+1050(expressions)X
+1444(are)X
+1563(used)X
+1730(in)X
+1812(line)X
+1952(addresses,)X
+2301(as)X
+2389(the)X
+2508(\256rst)X
+2653(part)X
+2799(of)X
+2887(the)X
+3 f
+3006(ex)X
+3103(substitute)X
+1 f
+3436(,)X
+3 f
+3477(global)X
+1 f
+3685(,)X
+3726(and)X
+3 f
+3863(vglo-)X
+576 4983(bal)N
+1 f
+702(commands,)X
+1089(and)X
+1225(in)X
+1307(search)X
+1533(patterns.)X
+776 5106(The)N
+927(regular)X
+1181(expressions)X
+1581(supported)X
+1923(by)X
+3 f
+2030(ex)X
+1 f
+2106(/)X
+3 f
+2128(vi)X
+1 f
+2217(are,)X
+2363(by)X
+2470(default,)X
+2740(the)X
+2865(Basic)X
+3070(Regular)X
+3351(Expressions)X
+3765(\(BRE's\))X
+576 5196(described)N
+909(in)X
+996(the)X
+1119(IEEE)X
+1318(POSIX)X
+1574(Standard)X
+1884(1003.2.)X
+2169(The)X
+3 f
+2319(extended)X
+1 f
+2651(option)X
+2880(causes)X
+3115(all)X
+3220(regular)X
+3472(expressions)X
+3870(to)X
+3956(be)X
+576 5286(interpreted)N
+976(as)X
+1095(the)X
+1245(Extended)X
+1600(Regular)X
+1906(Expressions)X
+2345(\(ERE's\))X
+2661(described)X
+3022(by)X
+3155(the)X
+3306(same)X
+3524(standard.)X
+3889(\(See)X
+2 f
+576 5376(re_format)N
+1 f
+(\(7\))S
+1025(for)X
+1154(more)X
+1354(information.\))X
+1834(Generally)X
+2186(speaking,)X
+2526(BRE's)X
+2774(are)X
+2908(the)X
+3040(Regular)X
+3328(Expressions)X
+3749(found)X
+3970(in)X
+2 f
+576 5466(ed)N
+1 f
+652(\(1\))X
+766(and)X
+2 f
+902(grep)X
+1 f
+1049(\(1\),)X
+1183(and)X
+1319(ERE's)X
+1548(are)X
+1667(the)X
+1785(Regular)X
+2059(Expressions)X
+2466(found)X
+2673(in)X
+2 f
+2755(egrep)X
+1 f
+2938(\(1\).)X
+776 5589(The)N
+922(following)X
+1254(is)X
+1328(not)X
+1451(intended)X
+1748(to)X
+1831(provide)X
+2097(a)X
+2154(description)X
+2532(of)X
+2621(Regular)X
+2897(Expressions.)X
+3346(The)X
+3493(information)X
+3893(here)X
+576 5679(only)N
+742(describes)X
+1065(strings)X
+1302(and)X
+1442(characters)X
+1793(which)X
+2013(have)X
+2189(special)X
+2436(meanings)X
+2766(in)X
+2851(the)X
+3 f
+2972(ex)X
+1 f
+3048(/)X
+3 f
+3070(vi)X
+1 f
+3155(version)X
+3414(of)X
+3504(RE's,)X
+3707(or)X
+3797(options)X
+576 5769(which)N
+792(change)X
+1040(the)X
+1158(meanings)X
+1485(of)X
+1572(characters)X
+1919(that)X
+2059(normally)X
+2368(have)X
+2540(special)X
+2783(meanings)X
+3110(in)X
+3192(RE's.)X
+
+7 p
+%%Page: 7 6
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3698(USD:13-7)X
+1 f
+616 762(\(1\))N
+830(An)X
+948(empty)X
+1168(RE)X
+1290(\(e.g.)X
+1473(``)X
+7 f
+1527(//)X
+1 f
+('')S
+1697(or)X
+1784(``)X
+7 f
+1838(??)X
+1 f
+('')S
+2028(is)X
+2101(equivalent)X
+2455(to)X
+2537(the)X
+2655(last)X
+2786(RE)X
+2908(used.)X
+616 885(\(2\))N
+830(The)X
+975(construct)X
+1289(``)X
+7 f
+1343(\\<)X
+1 f
+('')S
+1513(matches)X
+1796(the)X
+1914(beginning)X
+2254(of)X
+2341(a)X
+2397(word.)X
+616 1008(\(3\))N
+830(The)X
+975(construct)X
+1289(``)X
+7 f
+1343(\\>)X
+1 f
+('')S
+1513(matches)X
+1796(the)X
+1914(end)X
+2050(of)X
+2137(a)X
+2193(word.)X
+616 1131(\(4\))N
+830(The)X
+975(character)X
+1291(``)X
+7 f
+1345(\304)X
+1 f
+('')S
+1467(matches)X
+1750(the)X
+1868(replacement)X
+2281(part)X
+2426(of)X
+2513(the)X
+2631(last)X
+3 f
+2762(substitute)X
+1 f
+3115(command.)X
+776 1254(When)N
+992(the)X
+3 f
+1114(magic)X
+1 f
+1343(option)X
+1571(is)X
+2 f
+1648(not)X
+1 f
+1774(set,)X
+1907(the)X
+2029(only)X
+2195(characters)X
+2546(with)X
+2712(special)X
+2959(meanings)X
+3290(are)X
+3413(a)X
+3473(``)X
+7 f
+3527(\303)X
+1 f
+('')S
+3653(character)X
+3974(at)X
+576 1344(the)N
+698(beginning)X
+1042(of)X
+1133(an)X
+1233(RE,)X
+1379(a)X
+1439(``)X
+7 f
+1493($)X
+1 f
+('')S
+1619(character)X
+1939(at)X
+2021(the)X
+2143(end)X
+2282(of)X
+2372(an)X
+2471(RE,)X
+2616(and)X
+2755(the)X
+2876(escaping)X
+3180(character)X
+3499(``)X
+7 f
+3553(\\)X
+1 f
+(''.)S
+3718(The)X
+3866(char-)X
+576 1434(acters)N
+788(``)X
+7 f
+842(.)X
+1 f
+('',)S
+988(``)X
+7 f
+1042(*)X
+1 f
+('',)S
+1188(``)X
+7 f
+1242([)X
+1 f
+('')S
+1368(and)X
+1508(``)X
+7 f
+1562(\304)X
+1 f
+('')S
+1688(are)X
+1811(treated)X
+2054(as)X
+2145(ordinary)X
+2441(characters)X
+2793(unless)X
+3018(preceded)X
+3334(by)X
+3439(a)X
+3500(``)X
+7 f
+3554(\\)X
+1 f
+('';)S
+3703(when)X
+3902(pre-)X
+576 1524(ceded)N
+784(by)X
+884(a)X
+940(``)X
+7 f
+994(\\)X
+1 f
+('')S
+1116(they)X
+1274(regain)X
+1495(their)X
+1662(special)X
+1905(meaning.)X
+776 1647(Replacement)N
+1215(strings)X
+1448(are)X
+1567(the)X
+1685(second)X
+1928(part)X
+2073(of)X
+2160(a)X
+3 f
+2216(substitute)X
+1 f
+2569(command.)X
+776 1770(The)N
+924(character)X
+1243(``)X
+7 f
+1297(&)X
+1 f
+('')S
+1422(\(or)X
+1539(``)X
+7 f
+1593(\\&)X
+1 f
+('')S
+1766(if)X
+1839(the)X
+3 f
+1961(magic)X
+1 f
+2190(option)X
+2418(is)X
+2 f
+2495(not)X
+1 f
+2621(set\))X
+2761(in)X
+2847(the)X
+2969(replacement)X
+3386(string)X
+3592(stands)X
+3816(for)X
+3934(the)X
+576 1860(text)N
+717(matched)X
+1010(by)X
+1111(the)X
+1230(RE)X
+1353(that)X
+1494(is)X
+1568(being)X
+1767(replaced.)X
+2101(The)X
+2247(character)X
+2564(``)X
+7 f
+2618(\304)X
+1 f
+('')S
+2741(\(or)X
+2856(``)X
+7 f
+2910(\\\304)X
+1 f
+('')S
+3081(if)X
+3151(the)X
+3 f
+3270(magic)X
+1 f
+3496(option)X
+3721(is)X
+2 f
+3794(not)X
+1 f
+3916(set\))X
+576 1950(stands)N
+806(for)X
+931(the)X
+1060(replacement)X
+1484(part)X
+1640(of)X
+1738(the)X
+1867(previous)X
+3 f
+2174(substitute)X
+1 f
+2538(command.)X
+2925(It)X
+3005(is)X
+3089(only)X
+3262(valid)X
+3453(after)X
+3632(a)X
+3 f
+3699(substitute)X
+1 f
+576 2040(command)N
+912(has)X
+1039(been)X
+1211(performed.)X
+776 2163(The)N
+922(string)X
+1125(``)X
+7 f
+1179(\\#)X
+1 f
+('',)S
+1370(where)X
+1588(``)X
+7 f
+1642(#)X
+1 f
+('')S
+1765(is)X
+1839(an)X
+1936(integer)X
+2180(value)X
+2376(from)X
+2554(1)X
+2616(to)X
+2700(9,)X
+2782(stands)X
+3004(for)X
+3120(the)X
+3240(text)X
+3382(matched)X
+3676(by)X
+3778(the)X
+3898(por-)X
+576 2253(tion)N
+728(of)X
+823(the)X
+949(RE)X
+1079(enclosed)X
+1388(in)X
+1478(the)X
+1604(``)X
+7 f
+1658(#)X
+1 f
+('''th)S
+1877(set)X
+1994(of)X
+2089(escaped)X
+2372(parentheses,)X
+2794(e.g.)X
+2957(``)X
+7 f
+3011(\\\()X
+1 f
+('')S
+3188(and)X
+3331(``)X
+7 f
+3385(\\\))X
+1 f
+(''.)S
+3602(For)X
+3740(example,)X
+576 2343(``)N
+7 f
+630(s/abc\\\(.*\\\)def/\\1/)X
+1 f
+('')S
+1568(deletes)X
+1811(the)X
+1929(strings)X
+2162(``)X
+7 f
+2216(abc)X
+1 f
+('')S
+2434(and)X
+2570(``)X
+7 f
+2624(def)X
+1 f
+('')S
+2842(from)X
+3018(the)X
+3136(matched)X
+3428(pattern.)X
+776 2466(The)N
+934(strings)X
+1180(``)X
+7 f
+1234(\\l)X
+1 f
+('',)S
+1437(``)X
+7 f
+1491(\\u)X
+1 f
+('',)S
+1694(``)X
+7 f
+1748(\\L)X
+1 f
+('')S
+1931(and)X
+2080(``)X
+7 f
+2134(\\U)X
+1 f
+('')S
+2317(can)X
+2462(be)X
+2571(used)X
+2752(to)X
+2848(modify)X
+3113(the)X
+3245(case)X
+3418(of)X
+3519(elements)X
+3838(in)X
+3934(the)X
+576 2556(replacement)N
+999(string.)X
+1251(The)X
+1406(string)X
+1618(``)X
+7 f
+1672(\\l)X
+1 f
+('')S
+1852(causes)X
+2092(the)X
+2220(next)X
+2388(character)X
+2714(to)X
+2806(be)X
+2912(converted)X
+3259(to)X
+3350(lowercase;)X
+3723(the)X
+3850(string)X
+576 2646(``)N
+7 f
+630(\\u)X
+1 f
+('')S
+815(behaves)X
+1109(similarly,)X
+1448(but)X
+1585(converts)X
+1892(to)X
+1989(uppercase)X
+2346(\(e.g.)X
+7 f
+2544(s/abc/\\U&/)X
+1 f
+3059(replaces)X
+3358(the)X
+3492(string)X
+7 f
+3710(abc)X
+1 f
+3890(with)X
+7 f
+576 2736(ABC)N
+1 f
+(\).)S
+810(The)X
+958(strings)X
+1194(``)X
+7 f
+1248(\\L)X
+1 f
+('')S
+1421(causes)X
+1654(characters)X
+2004(up)X
+2107(to)X
+2192(the)X
+2313(end)X
+2452(of)X
+2541(the)X
+2661(string)X
+2865(or)X
+2954(the)X
+3074(next)X
+3234 0.3611(occurrence)AX
+3610(of)X
+3699(the)X
+3819(strings)X
+576 2826(``)N
+7 f
+630(\\e)X
+1 f
+('')S
+806(or)X
+899(``)X
+7 f
+953(\\E)X
+1 f
+('')S
+1129(to)X
+1217(be)X
+1319(converted)X
+1663(to)X
+1752(lowercase;)X
+2123(the)X
+2248(string)X
+2457(``)X
+7 f
+2511(\\U)X
+1 f
+('')S
+2688(behaves)X
+2974(similarly,)X
+3305(but)X
+3434(converts)X
+3733(to)X
+3822(upper-)X
+576 2916(case.)N
+776 3039(If)N
+850(the)X
+968(entire)X
+1171(replacement)X
+1584(pattern)X
+1827(is)X
+1900(``)X
+7 f
+1954(%)X
+1 f
+('',)S
+2096(then)X
+2254(the)X
+2372(last)X
+2503(replacement)X
+2916(pattern)X
+3159(is)X
+3232(used)X
+3399(again.)X
+776 3162(In)N
+3 f
+867(vi)X
+1 f
+929(,)X
+973(inserting)X
+1278(a)X
+7 f
+1339(<control-M>)X
+1 f
+1892(into)X
+2041(the)X
+2164(replacement)X
+2582(string)X
+2789(will)X
+2938(cause)X
+3142(the)X
+3265(matched)X
+3562(line)X
+3707(to)X
+3794(be)X
+3895(split)X
+576 3252(into)N
+720(two)X
+860(lines)X
+1031(at)X
+1109(that)X
+1249(point.)X
+1473(\(The)X
+7 f
+1645(<control-M>)X
+1 f
+2193(will)X
+2337(be)X
+2433(discarded.\))X
+3 f
+576 3438(8.)N
+676(General)X
+972(Editor)X
+1214(Description)X
+1 f
+776 3561(When)N
+3 f
+989(ex)X
+1 f
+1086(or)X
+3 f
+1174(vi)X
+1 f
+1257(are)X
+1377(executed,)X
+1704(the)X
+1823(text)X
+1964(of)X
+2052(a)X
+2109(\256le)X
+2232(is)X
+2306(read)X
+2466(\(or)X
+2581(a)X
+2638(temporary)X
+2989(\256le)X
+3112(is)X
+3186(created\),)X
+3487(and)X
+3624(then)X
+3783(all)X
+3885(edit-)X
+576 3651(ing)N
+700(changes)X
+981(happen)X
+1235(within)X
+1461(the)X
+1581(context)X
+1838(of)X
+1926(the)X
+2045(copy)X
+2222(of)X
+2310(the)X
+2429(\256le.)X
+2 f
+2592(No)X
+2706(changes)X
+2990(affect)X
+3189(the)X
+3308(actual)X
+3529(\256le)X
+3648(until)X
+3815(the)X
+3934(\256le)X
+576 3741(is)N
+662(written)X
+921(out)X
+1 f
+1023(,)X
+1076(either)X
+1293(using)X
+1500(a)X
+1570(write)X
+1769(command)X
+2119(or)X
+2220(another)X
+2495(command)X
+2845(which)X
+3075(is)X
+3162(affected)X
+3456(by)X
+3570(the)X
+3 f
+3702(autowrite)X
+1 f
+576 3831(option.)N
+776 3954(All)N
+898(\256les)X
+1052(are)X
+1172(locked)X
+1407(\(using)X
+1628(the)X
+2 f
+1747(\257ock)X
+1 f
+1899(\(2\))X
+2014(or)X
+2 f
+2102(fcntl)X
+1 f
+2244(\(2\))X
+2359(interfaces\))X
+2720(during)X
+2950(the)X
+3069(edit)X
+3210(session,)X
+3482(to)X
+3565(avoid)X
+3764(inadver-)X
+576 4044(tently)N
+781(making)X
+1044(modi\256cations)X
+1502(to)X
+1587(multiple)X
+1876(copies)X
+2104(of)X
+2194(the)X
+2315(\256le.)X
+2480(If)X
+2557(a)X
+2616(lock)X
+2777(cannot)X
+3014(be)X
+3113(obtained)X
+3412(for)X
+3529(a)X
+3587(\256le)X
+3711(because)X
+3988(it)X
+576 4134(is)N
+657(locked)X
+899(by)X
+1007(another)X
+1276(process,)X
+1565(the)X
+1691(edit)X
+1840(session)X
+2100(is)X
+2182(read-only)X
+2519(\(as)X
+2642(if)X
+2720(the)X
+3 f
+2847(readonly)X
+1 f
+3178(option)X
+3411(or)X
+3507(the)X
+3 f
+9 f
+3634(-)X
+3636(-)X
+3 f
+3680(R)X
+1 f
+3767(\257ag)X
+3916(had)X
+576 4224(been)N
+754(speci\256ed\).)X
+1132(If)X
+1212(a)X
+1274(lock)X
+1438(cannot)X
+1678(be)X
+1780(obtained)X
+2082(for)X
+2202(other)X
+2393(reasons,)X
+2680(the)X
+2804(edit)X
+2950(session)X
+3207(will)X
+3357(continue,)X
+3679(but)X
+3807(the)X
+3930(\256le)X
+576 4314(status)N
+778(information)X
+1176(\(see)X
+1326(the)X
+3 f
+1444(<control-G>)X
+1 f
+1890(command\))X
+2253(will)X
+2397(re\257ect)X
+2618(this)X
+2753(fact.)X
+776 4437(Both)N
+3 f
+955(ex)X
+1 f
+1055(and)X
+3 f
+1195(vi)X
+1 f
+1281(are)X
+1405(modeful)X
+1697(editors,)X
+1960(i.e.)X
+2083(they)X
+2246(have)X
+2423(two)X
+2568(modes,)X
+2822(``command'')X
+3271(mode)X
+3474(and)X
+3615(``text)X
+3814(input'')X
+576 4527(mode.)N
+817(The)X
+965(former)X
+1207(is)X
+1283(intended)X
+1582(to)X
+1667(permit)X
+1898(you)X
+2040(to)X
+2124(enter)X
+2307(commands)X
+2676(which)X
+2894(modi\256es)X
+3191(already)X
+3450(existing)X
+3725(text.)X
+3907(The)X
+576 4617(latter)N
+762(is)X
+836(intended)X
+1133(to)X
+1216(permit)X
+1446(you)X
+1588(to)X
+1672(enter)X
+1855(new)X
+2011(text.)X
+2193(When)X
+3 f
+2407(ex)X
+1 f
+2505(\256rst)X
+2651(starts)X
+2842(running,)X
+3133(it)X
+3199(is)X
+3274(in)X
+3358(command)X
+3696(mode,)X
+3916(and)X
+576 4707(usually)N
+838(displays)X
+1131(a)X
+1198(prompt)X
+1460(\(see)X
+1621(the)X
+3 f
+1750(prompt)X
+1 f
+2039(option)X
+2274(for)X
+2398(more)X
+2593(information\).)X
+3068(The)X
+3223(prompt)X
+3484(is)X
+3567(a)X
+3633(single)X
+3854(colon)X
+576 4797(\(``)N
+7 f
+657(:)X
+1 f
+(''\))S
+817(character.)X
+1184(There)X
+1403(are)X
+1533(three)X
+1725(commands)X
+2103(that)X
+2255(switch)X
+3 f
+2496(ex)X
+1 f
+2604(into)X
+2760(text)X
+2912(input)X
+3108(mode:)X
+3 f
+3340(append)X
+1 f
+3592(,)X
+3 f
+3644(change)X
+1 f
+3916(and)X
+3 f
+576 4887(insert)N
+1 f
+772(.)X
+840(Once)X
+1038(in)X
+1128(input)X
+1320(mode,)X
+1546(entering)X
+1837(a)X
+1901(line)X
+2049(containing)X
+2415(only)X
+2585(a)X
+2649(single)X
+2868(period)X
+3101(\(``)X
+7 f
+3182(.)X
+1 f
+(''\))S
+3359(terminates)X
+3721(text)X
+3868(input)X
+576 4977(mode)N
+774(and)X
+910(returns)X
+1153(to)X
+1235(command)X
+1571(mode,)X
+1789(where)X
+2006(the)X
+2124(prompt)X
+2375(is)X
+2448(redisplayed.)X
+776 5100(When)N
+3 f
+989(vi)X
+1 f
+1072(\256rst)X
+1217(starts)X
+1407(running,)X
+1697(it)X
+1763(is)X
+1838(in)X
+1922(command)X
+2260(mode)X
+2460(as)X
+2549(well.)X
+2749(There)X
+2959(are)X
+3080(eleven)X
+3312(commands)X
+3681(that)X
+3823(switch)X
+3 f
+576 5190(vi)N
+1 f
+659(into)X
+804(text)X
+945(input)X
+1130(mode:)X
+3 f
+1351(A)X
+1 f
+1409(,)X
+3 f
+1450(a)X
+1 f
+(,)S
+3 f
+1531(C)X
+1 f
+1589(,)X
+3 f
+1629(c)X
+1 f
+1665(,)X
+3 f
+1705(I)X
+1 f
+1736(,)X
+3 f
+1776(i)X
+1 f
+1798(,)X
+3 f
+1838(O)X
+1 f
+1900(,)X
+3 f
+1940(o)X
+1 f
+(,)S
+3 f
+2020(R)X
+1 f
+2078(,)X
+3 f
+2118(S)X
+1 f
+2182(and)X
+3 f
+2318(s)X
+1 f
+2349(.)X
+2409(Once)X
+2599(in)X
+2681(input)X
+2865(mode,)X
+3083(entering)X
+3366(an)X
+7 f
+3462(<escape>)X
+1 f
+3866(char-)X
+576 5280(acter)N
+753(terminates)X
+1107(text)X
+1247(input)X
+1431(mode)X
+1629(and)X
+1765(returns)X
+2008(to)X
+2090(command)X
+2426(mode.)X
+776 5403(The)N
+921(following)X
+1252(words)X
+1468(have)X
+1640(special)X
+1883(meanings)X
+2210(in)X
+2292(both)X
+2454(the)X
+3 f
+2572(ex)X
+1 f
+2668(and)X
+3 f
+2804(vi)X
+1 f
+2886(command)X
+3222(descriptions:)X
+3 f
+576 5583(<interrupt>)N
+1 f
+776 5673(The)N
+927(interrupt)X
+1229(character)X
+1551(is)X
+1630(used)X
+1803(to)X
+1891(interrupt)X
+2193(the)X
+2317(current)X
+2571(operation.)X
+2940(Normally)X
+7 f
+3274(<control-C>)X
+1 f
+(,)S
+3849(what-)X
+776 5763(ever)N
+935(character)X
+1251(is)X
+1324(set)X
+1433(for)X
+1547(the)X
+1665(current)X
+1913(terminal)X
+2200(is)X
+2273(used.)X
+
+8 p
+%%Page: 8 7
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-8)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+576 762(<literal)N
+847(next>)X
+1 f
+776 852(The)N
+923(literal)X
+1132(next)X
+1292(character)X
+1610(is)X
+1685(used)X
+1854(to)X
+1938(escape)X
+2175(the)X
+2295(subsequent)X
+2673(character)X
+2991(from)X
+3169(any)X
+3307(special)X
+3552(meaning.)X
+3890(This)X
+776 942(character)N
+1102(is)X
+1185(always)X
+7 f
+1438(<control-V>)X
+1 f
+(.)S
+2036(If)X
+2120(the)X
+2248(terminal)X
+2545(is)X
+2627(not)X
+2758(set)X
+2876(up)X
+2985(to)X
+3076(do)X
+3185(XON/XOFF)X
+3614(\257ow)X
+3785(control,)X
+776 1032(then)N
+7 f
+934(<control-Q>)X
+1 f
+1482(is)X
+1555(used)X
+1722(to)X
+1804(mean)X
+1998(literal)X
+2205(next)X
+2363(as)X
+2450(well.)X
+3 f
+576 1212(current)N
+855(pathname)X
+1 f
+776 1302(The)N
+923(pathname)X
+1257(of)X
+1346(the)X
+1467(\256le)X
+1592(currently)X
+1905(being)X
+2106(edited)X
+2325(by)X
+2428(vi.)X
+2553(When)X
+2768(the)X
+2889(percent)X
+3149(character)X
+3468(\(``)X
+7 f
+3549(%)X
+1 f
+(''\))S
+3701(appears)X
+3970(in)X
+776 1392(a)N
+833(\256le)X
+956(name)X
+1151(entered)X
+1409(as)X
+1497(part)X
+1643(of)X
+1731(an)X
+3 f
+1828(ex)X
+1 f
+1925(command)X
+2262(argument,)X
+2606(it)X
+2671(is)X
+2745(replaced)X
+3039(by)X
+3140(the)X
+3259(current)X
+3508(pathname.)X
+3880(\(The)X
+776 1482(``)N
+7 f
+830(%)X
+1 f
+('')S
+952(character)X
+1268(can)X
+1400(be)X
+1496(escaped)X
+1771(by)X
+1871(preceding)X
+2208(it)X
+2272(with)X
+2434(a)X
+2490(backslash.\))X
+3 f
+576 1662(alternate)N
+904(pathname)X
+1 f
+776 1752(The)N
+921(name)X
+1115(of)X
+1202(the)X
+1320(last)X
+1451(\256le)X
+1573(name)X
+1767(mentioned)X
+2125(in)X
+2207(an)X
+3 f
+2304(ex)X
+1 f
+2401(command,)X
+2758(or,)X
+2866(the)X
+2985(previous)X
+3282(current)X
+3531(pathname)X
+3864(if)X
+3934(the)X
+776 1842(last)N
+913(\256le)X
+1041(mentioned)X
+1404(becomes)X
+1710(the)X
+1833(current)X
+2086(\256le.)X
+2253(When)X
+2470(the)X
+2593(hash)X
+2765(mark)X
+2955(character)X
+3276(\(``)X
+7 f
+3357(#)X
+1 f
+(''\))S
+3511(appears)X
+3782(in)X
+3869(a)X
+3930(\256le)X
+776 1932(name)N
+979(entered)X
+1245(as)X
+1341(part)X
+1495(of)X
+1591(an)X
+3 f
+1696(ex)X
+1 f
+1801(command)X
+2146(argument,)X
+2498(it)X
+2571(is)X
+2653(replaced)X
+2955(by)X
+3064(the)X
+3191(alternate)X
+3498(pathname.)X
+3880(\(The)X
+776 2022(``)N
+7 f
+830(#)X
+1 f
+('')S
+952(character)X
+1268(can)X
+1400(be)X
+1496(escaped)X
+1771(by)X
+1871(preceding)X
+2208(it)X
+2272(with)X
+2434(a)X
+2490(backslash.\))X
+3 f
+576 2202(buffer)N
+1 f
+776 2292(One)N
+931(of)X
+1019(a)X
+1076(number)X
+1342(of)X
+1430(named)X
+1665(areas)X
+1852(for)X
+1968(saving)X
+2199(copies)X
+2426(of)X
+2515(text.)X
+2697(Commands)X
+3083(that)X
+3225(change)X
+3475(or)X
+3564(delete)X
+3778(text)X
+3920(can)X
+776 2382(save)N
+941(the)X
+1061(changed)X
+1351(or)X
+1440(deleted)X
+1693(text)X
+1834(into)X
+1979(a)X
+2036(speci\256c)X
+2302(buffer,)X
+2540(for)X
+2655(later)X
+2819(use,)X
+2967(if)X
+3037(the)X
+3156(command)X
+3493(allows)X
+3723(it)X
+3788(\(i.e.)X
+3934(the)X
+3 f
+776 2472(ex)N
+872(change)X
+1 f
+1132(command)X
+1468(cannot)X
+1702(save)X
+1866(the)X
+1985(changed)X
+2274(text)X
+2415(in)X
+2498(a)X
+2555(named)X
+2790(buffer\).)X
+3075(Buffers)X
+3337(are)X
+3457(named)X
+3692(with)X
+3855(a)X
+3912(sin-)X
+776 2562(gle)N
+895(character,)X
+1232(preceded)X
+1544(by)X
+1645(a)X
+1702(double)X
+1941(quote,)X
+2160(e.g.)X
+7 f
+2317("<character>)X
+1 f
+(.)S
+2954(Historic)X
+3232(implementations)X
+3785(of)X
+3 f
+3872(ex)X
+1 f
+3948(/)X
+3 f
+3970(vi)X
+1 f
+776 2652(limited)N
+7 f
+1022(<character>)X
+1 f
+1570(to)X
+1652(the)X
+1770(alphanumeric)X
+2227(characters;)X
+3 f
+2596(nex)X
+1 f
+(/)S
+3 f
+2738(nvi)X
+1 f
+2864(permits)X
+3124(the)X
+3242(use)X
+3369(of)X
+3456(any)X
+3592(character.)X
+776 2832(Buffers)N
+1041(named)X
+1279(by)X
+1383(uppercase)X
+1729(characters)X
+2080(are)X
+2203(the)X
+2325(same)X
+2514(as)X
+2605(buffers)X
+2857(named)X
+3095(by)X
+3199(lowercase)X
+3545(characters,)X
+3916(e.g.)X
+776 2922(the)N
+904(buffer)X
+1131(named)X
+1375(by)X
+1485(the)X
+1613(English)X
+1887(character)X
+2213(``)X
+7 f
+2267(A)X
+1 f
+('')S
+2399(is)X
+2482(the)X
+2610(same)X
+2805(as)X
+2902(the)X
+3030(buffer)X
+3257(named)X
+3500(by)X
+3609(the)X
+3736(character)X
+776 3012(``)N
+7 f
+830(a)X
+1 f
+('',)S
+977(with)X
+1144(the)X
+1267(exception)X
+1604(that,)X
+1769(if)X
+1843(the)X
+1966(buffer)X
+2188(contents)X
+2480(are)X
+2604(being)X
+2807(changed)X
+3100(\(as)X
+3219(with)X
+3386(a)X
+3447(text)X
+3593(deletion)X
+3877(or)X
+3 f
+3970(vi)X
+776 3102(change)N
+1 f
+1036(command\),)X
+1419(the)X
+1537(text)X
+1677(is)X
+2 f
+1750(appended)X
+1 f
+2082(to)X
+2164(the)X
+2282(buffer,)X
+2519(instead)X
+2766(of)X
+2853(replacing)X
+3172(the)X
+3290(current)X
+3538(contents.)X
+776 3282(The)N
+925(buffers)X
+1177(named)X
+1415(by)X
+1519(the)X
+1641(numeric)X
+1928(characters)X
+2279(\(in)X
+2392(English,)X
+2680(``)X
+7 f
+2734(1)X
+1 f
+('')S
+2860(through)X
+3133(``)X
+7 f
+3187(9)X
+1 f
+(''\),)S
+3360(are)X
+3483(special,)X
+3751(in)X
+3838(that)X
+3983(if)X
+776 3372(at)N
+864(least)X
+1041(one)X
+1187(line)X
+1337(is)X
+1420(changed)X
+1718(or)X
+1814(deleted)X
+2075(in)X
+2166(the)X
+2293(\256le,)X
+2444(\(or)X
+2567(a)X
+2632(command)X
+2977(changes)X
+3265(or)X
+3361(deletes)X
+3613(a)X
+3678(region)X
+3912(that)X
+776 3462(crosses)N
+1032(a)X
+1093(line)X
+1238(boundary\))X
+1593(a)X
+1654(copy)X
+1835(of)X
+1927(the)X
+2050(text)X
+2195(is)X
+2273(placed)X
+2508(into)X
+2657(the)X
+2780(numeric)X
+3068(buffer)X
+3290(``)X
+7 f
+3344(1)X
+1 f
+('',)S
+3491(regardless)X
+3842(of)X
+3934(the)X
+776 3552(user)N
+933(specifying)X
+1290(another)X
+1553(buffer)X
+1772(in)X
+1856(which)X
+2074(to)X
+2158(save)X
+2323(it.)X
+2429(Before)X
+2670(this)X
+2807(copy)X
+2985(is)X
+3060(done,)X
+3258(the)X
+3378(previous)X
+3676(contents)X
+3965(of)X
+776 3642(buffer)N
+1000(``)X
+7 f
+1054(1)X
+1 f
+('')S
+1183(are)X
+1309(moved)X
+1555(into)X
+1707(buffer)X
+1932(``)X
+7 f
+1986(2)X
+1 f
+('',)S
+2136(``)X
+7 f
+2190(2)X
+1 f
+('')S
+2320(into)X
+2472(buffer)X
+2697(``)X
+7 f
+2751(3)X
+1 f
+('',)S
+2901(and)X
+3045(so)X
+3144(on.)X
+3292(The)X
+3445(contents)X
+3740(of)X
+3835(buffer)X
+776 3732(``)N
+7 f
+830(9)X
+1 f
+('')S
+961(are)X
+1089(discarded.)X
+1466(In)X
+3 f
+1562(vi)X
+1 f
+1624(,)X
+1672(text)X
+1820(may)X
+1986(be)X
+2090(explicitly)X
+2420(stored)X
+2644(into)X
+2796(the)X
+2922(numeric)X
+3213(buffers.)X
+3509(In)X
+3604(this)X
+3747(case,)X
+3934(the)X
+776 3822(buffer)N
+995(rotation)X
+1266(described)X
+1596(above)X
+1810(occurs)X
+2042(before)X
+2270(the)X
+2390(replacement)X
+2805(of)X
+2894(the)X
+3014(buffer's)X
+3291(contents.)X
+3621(\(Text)X
+3818(cannot)X
+776 3912(be)N
+874(explicitly)X
+1198(stored)X
+1416(into)X
+1562(the)X
+1682(numeric)X
+1966(buffers)X
+2215(in)X
+3 f
+2298(ex)X
+1 f
+2395(because)X
+2671(of)X
+2759(ambiguities)X
+3153(that)X
+3294(this)X
+3430(would)X
+3651(cause)X
+3851(in)X
+3934(the)X
+3 f
+776 4002(ex)N
+1 f
+872(command)X
+1208(syntax.\))X
+776 4182(When)N
+991(a)X
+3 f
+1051(vi)X
+1 f
+1137(command)X
+1477(synopsis)X
+1776(shows)X
+2000(both)X
+2166(a)X
+7 f
+2226([buffer])X
+1 f
+2634(and)X
+2774(a)X
+7 f
+2834([count])X
+1 f
+(,)S
+3214(they)X
+3376(may)X
+3538(be)X
+3638(presented)X
+3970(in)X
+776 4272(any)N
+912(order.)X
+776 4452(Finally,)N
+1052(all)X
+1162(buffers)X
+1420(are)X
+1549(either)X
+1762(``line'')X
+2020(or)X
+2117 0.3750(``character'')AX
+2551(oriented.)X
+2884(All)X
+3 f
+3016(ex)X
+1 f
+3122(commands)X
+3499(which)X
+3725(store)X
+3912(text)X
+776 4542(into)N
+922(buffers)X
+1172(are)X
+1293(line)X
+1435(oriented.)X
+1760(Some)X
+3 f
+1964(vi)X
+1 f
+2048(commands)X
+2417(which)X
+2635(store)X
+2813(text)X
+2955(into)X
+3101(buffers)X
+3351(are)X
+3471(line)X
+3612(oriented,)X
+3916(and)X
+776 4632(some)N
+980(are)X
+1114(character)X
+1445(oriented;)X
+1765(the)X
+1898(description)X
+2289(for)X
+2418(each)X
+2601(applicable)X
+3 f
+2966(vi)X
+1 f
+3063(command)X
+3414(notes)X
+3618(whether)X
+3912(text)X
+776 4722(copied)N
+1020(into)X
+1174(buffers)X
+1432(using)X
+1635(the)X
+1762(command)X
+2107(is)X
+2189(line)X
+2338(or)X
+2434(character)X
+2759(oriented.)X
+3091(In)X
+3187(addition,)X
+3498(the)X
+3 f
+3625(vi)X
+1 f
+3716(command)X
+3 f
+776 4812(display)N
+1053(buffers)X
+1 f
+1332(displays)X
+1628(the)X
+1760(current)X
+2023(orientation)X
+2405(for)X
+2534(each)X
+2717(buffer.)X
+2989(Generally,)X
+3361(the)X
+3494(only)X
+3671(importance)X
+776 4902(attached)N
+1074(to)X
+1166(this)X
+1311(orientation)X
+1688(is)X
+1771(that)X
+1921(if)X
+2000(the)X
+2128(buffer)X
+2355(is)X
+2438(subsequently)X
+2886(inserted)X
+3170(into)X
+3324(the)X
+3451(text,)X
+3620(line)X
+3769(oriented)X
+776 4992(buffers)N
+1027(create)X
+1243(new)X
+1400(lines)X
+1574(for)X
+1691(each)X
+1862(of)X
+1952(the)X
+2073(lines)X
+2247(they)X
+2408(contain,)X
+2687(and)X
+2826(character)X
+3145(oriented)X
+3431(buffers)X
+3682(create)X
+3898(new)X
+776 5082(lines)N
+951(for)X
+1069(any)X
+1209(lines)X
+2 f
+1384(other)X
+1 f
+1577(than)X
+1739(the)X
+1860(\256rst)X
+2007(and)X
+2146(last)X
+2280(lines)X
+2454(they)X
+2615(contain.)X
+2914(The)X
+3062(\256rst)X
+3209(and)X
+3348(last)X
+3482(lines)X
+3656(are)X
+3778(inserted)X
+776 5172(into)N
+927(the)X
+1052(text)X
+1199(at)X
+1284(the)X
+1409(current)X
+1664(cursor)X
+1892(position,)X
+2196(becoming)X
+2539(part)X
+2691(of)X
+2785(the)X
+2910(current)X
+3165(line.)X
+3352(If)X
+3433(there)X
+3621(is)X
+3701(more)X
+3894(than)X
+776 5262(one)N
+912(line)X
+1052(in)X
+1134(the)X
+1252(buffer,)X
+1489(however,)X
+1806(the)X
+1924(current)X
+2172(line)X
+2312(itself)X
+2492(will)X
+2636(be)X
+2732(split.)X
+3 f
+576 5442(unnamed)N
+915(buffer)X
+1 f
+776 5532(The)N
+924(unnamed)X
+1241(buffer)X
+1461(is)X
+1537(a)X
+1596(text)X
+1739(storage)X
+1994(area)X
+2152(which)X
+2371(is)X
+2447(used)X
+2617(by)X
+2720(commands)X
+3090(that)X
+3233(take)X
+3390(a)X
+3450(buffer)X
+3671(as)X
+3762(an)X
+3862(argu-)X
+776 5622(ment,)N
+976(when)X
+1170(no)X
+1270(buffer)X
+1487(is)X
+1560(speci\256ed)X
+1865(by)X
+1965(the)X
+2083(user.)X
+2277(There)X
+2485(is)X
+2558(no)X
+2658(way)X
+2812(to)X
+2894(explicitly)X
+3216 0.4531(reference)AX
+3537(this)X
+3672(buffer.)X
+
+9 p
+%%Page: 9 8
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3698(USD:13-9)X
+576 762(9.)N
+676(Vi)X
+776(Description)X
+1 f
+3 f
+776 885(Vi)N
+1 f
+879(takes)X
+1067(up)X
+1170(the)X
+1291(entire)X
+1497(screen)X
+1727(to)X
+1813(display)X
+2068(the)X
+2190(edited)X
+2410(\256le,)X
+2556(except)X
+2790(for)X
+2908(the)X
+3030(bottom)X
+3280(line)X
+3424(of)X
+3515(the)X
+3637(screen.)X
+3907(The)X
+576 975(bottom)N
+824(line)X
+966(of)X
+1055(the)X
+1175(screen)X
+1403(is)X
+1478(used)X
+1647(to)X
+1731(enter)X
+3 f
+1914(ex)X
+1 f
+2012(commands,)X
+2401(and)X
+2539(for)X
+3 f
+2655(vi)X
+1 f
+2739(error)X
+2918(and)X
+3056(informational)X
+3513(messages.)X
+3877(If)X
+3952(no)X
+576 1065(other)N
+777(information)X
+1191(is)X
+1281(being)X
+1496(displayed,)X
+1860(the)X
+1995(default)X
+2255(display)X
+2523(can)X
+2672(show)X
+2878(the)X
+3013(current)X
+3278(cursor)X
+3516(row)X
+3678(and)X
+3831(cursor)X
+576 1155(column,)N
+863(an)X
+966(indication)X
+1313(of)X
+1407(whether)X
+1693(the)X
+1818(\256le)X
+1947(has)X
+2081(been)X
+2260(modi\256ed,)X
+2591(and)X
+2734(the)X
+2859(current)X
+3114(mode)X
+3319(of)X
+3413(the)X
+3538(editor.)X
+3792(See)X
+3934(the)X
+3 f
+576 1245(ruler)N
+1 f
+750(,)X
+3 f
+790(showdirty)X
+1 f
+1152(and)X
+3 f
+1288(showmode)X
+1 f
+1668(options)X
+1923(for)X
+2037(more)X
+2222(information.)X
+776 1368(Empty)N
+1012(lines)X
+1186(do)X
+1289(not)X
+1414(have)X
+1589(any)X
+1729(special)X
+1976(representation)X
+2455(on)X
+2559(the)X
+2681(screen,)X
+2931(but)X
+3057(lines)X
+3232(on)X
+3336(the)X
+3458(screen)X
+3688(that)X
+3832(would)X
+576 1458(logically)N
+886(come)X
+1090(after)X
+1268(the)X
+1396(end)X
+1542(of)X
+1639(the)X
+1767(\256le)X
+1899(are)X
+2028(displayed)X
+2365(as)X
+2461(a)X
+2526(single)X
+2746(tilde)X
+2917(\(``)X
+7 f
+2998(\304)X
+1 f
+(''\))S
+3156(character.)X
+3521(To)X
+3639(differentiate)X
+576 1548(between)N
+871(empty)X
+1098(lines)X
+1276(and)X
+1419(lines)X
+1597(consisting)X
+1948(of)X
+2042(only)X
+2211(whitespace)X
+2596(characters,)X
+2971(use)X
+3106(the)X
+3 f
+3232(list)X
+1 f
+3362(option.)X
+3634(Historically,)X
+576 1638(implementations)N
+1130(of)X
+3 f
+1218(vi)X
+1 f
+1301(have)X
+1474(also)X
+1624(displayed)X
+1952(some)X
+2141(lines)X
+2312(as)X
+2399(single)X
+2610(asterisk)X
+2875(\(``)X
+7 f
+2956(@)X
+1 f
+(''\))S
+3105(characters.)X
+3492(These)X
+3704(were)X
+3881(lines)X
+576 1728(that)N
+717(were)X
+895(not)X
+1018(correctly)X
+1326(displayed,)X
+1675(i.e.)X
+1795(lines)X
+1968(on)X
+2070(the)X
+2190(screen)X
+2418(that)X
+2560(did)X
+2684(not)X
+2808(correspond)X
+3187(to)X
+3271(lines)X
+3444(in)X
+3528(the)X
+3648(\256le,)X
+3792(or)X
+3881(lines)X
+576 1818(that)N
+716(did)X
+838(not)X
+960(\256t)X
+1046(on)X
+1146(the)X
+1264(current)X
+1512(screen.)X
+3 f
+1778(Nvi)X
+1 f
+1918(never)X
+2117(displays)X
+2399(lines)X
+2570(in)X
+2652(this)X
+2787(fashion.)X
+3 f
+776 1941(Vi)N
+1 f
+881(is)X
+959(a)X
+1020(modeful)X
+1312(editor,)X
+1544(i.e.)X
+1667(it)X
+1736(has)X
+1868(two)X
+2013(modes,)X
+2268(``command'')X
+2718(mode)X
+2922(and)X
+3064(``text)X
+3264(input'')X
+3508(mode.)X
+3752(When)X
+3 f
+3970(vi)X
+1 f
+576 2031(\256rst)N
+724(starts,)X
+937(it)X
+1005(is)X
+1082(in)X
+1168(command)X
+1508(mode.)X
+1750(There)X
+1962(are)X
+2085(several)X
+2337(commands)X
+2708(that)X
+2852(change)X
+3 f
+3104(vi)X
+1 f
+3189(into)X
+3336(text)X
+3479(input)X
+3666(mode.)X
+3907(The)X
+7 f
+576 2121(<escape>)N
+1 f
+980(character)X
+1296(is)X
+1369(used)X
+1536(to)X
+1618(resolve)X
+1870(the)X
+1988(text)X
+2128(input)X
+2312(into)X
+2456(the)X
+2574(\256le,)X
+2716(and)X
+2852(exit)X
+2992(back)X
+3164(into)X
+3308(command)X
+3644(mode.)X
+3882(In)X
+3 f
+3970(vi)X
+1 f
+576 2211(command)N
+916(mode,)X
+1138(the)X
+1260(cursor)X
+1485(is)X
+1562(always)X
+1809(positioned)X
+2166(on)X
+2269(the)X
+2390(last)X
+2524(column)X
+2787(of)X
+2877(characters)X
+3227(which)X
+3446(take)X
+3603(up)X
+3706(more)X
+3894(than)X
+576 2301(one)N
+714(column)X
+976(on)X
+1078(the)X
+1198(screen.)X
+1466(In)X
+3 f
+1555(vi)X
+1 f
+1639(text)X
+1782(insert)X
+1983(mode,)X
+2204(the)X
+2325(cursor)X
+2549(is)X
+2625(positioned)X
+2981(on)X
+3084(the)X
+3205(\256rst)X
+3352(column)X
+3615(of)X
+3705(characters)X
+576 2391(which)N
+792(take)X
+946(up)X
+1046(more)X
+1231(than)X
+1389(one)X
+1525(column)X
+1785(on)X
+1885(the)X
+2003(screen.)X
+776 2514(Generally,)N
+1138(if)X
+1212(the)X
+1335(cursor)X
+1561(line)X
+1707(and)X
+1849(cursor)X
+2076(column)X
+2342(are)X
+2467(not)X
+2595(on)X
+2701(the)X
+2825(screen,)X
+3077(then)X
+3241(the)X
+3365(screen)X
+3597(is)X
+3676(scrolled)X
+3956(\(if)X
+576 2604(the)N
+695(target)X
+899(cursor)X
+1121(is)X
+1195(close\))X
+1407(or)X
+1494(repainted)X
+1813(\(if)X
+1909(the)X
+2027(target)X
+2230(cursor)X
+2451(is)X
+2524(far)X
+2634(away\))X
+2851(so)X
+2942(that)X
+3082(the)X
+3200(cursor)X
+3421(is)X
+3494(on)X
+3594(the)X
+3712(screen.)X
+3978(If)X
+576 2694(the)N
+699(screen)X
+930(is)X
+1009(scrolled,)X
+1309(it)X
+1379(is)X
+1458(moved)X
+1702(a)X
+1764(minimal)X
+2056(amount,)X
+2342(and)X
+2484(the)X
+2608(cursor)X
+2835(line)X
+2981(will)X
+3131(usually)X
+3388(appear)X
+3629(at)X
+3713(the)X
+3837(top)X
+3965(or)X
+576 2784(bottom)N
+832(of)X
+929(the)X
+1057(screen.)X
+1333(In)X
+1430(the)X
+1558(screen)X
+1794(is)X
+1877(repainted,)X
+2226(the)X
+2354(cursor)X
+2585(line)X
+2735(will)X
+2889(appear)X
+3134(in)X
+3226(the)X
+3354(center)X
+3581(of)X
+3678(the)X
+3806(screen,)X
+576 2874(unless)N
+799(the)X
+920(cursor)X
+1144(is)X
+1220(suf\256ciently)X
+1603(close)X
+1791(to)X
+1876(the)X
+1997(beginning)X
+2340(or)X
+2430(end)X
+2569(of)X
+2660(the)X
+2782(\256le)X
+2908(that)X
+3052(this)X
+3191(is)X
+3268(not)X
+3394(possible.)X
+3720(If)X
+3798(the)X
+3 f
+3920(lef-)X
+576 2964(tright)N
+1 f
+794(option)X
+1020(is)X
+1095(set,)X
+1226(the)X
+1346(screen)X
+1574(may)X
+1734(be)X
+1832(scrolled)X
+2108(or)X
+2197(repainted)X
+2518(in)X
+2602(a)X
+2660(horizontal)X
+3007(direction)X
+3314(as)X
+3403(well)X
+3563(as)X
+3651(in)X
+3734(a)X
+3791(vertical)X
+576 3054(one.)N
+776 3177(A)N
+869(major)X
+1091(difference)X
+1453(between)X
+1756(the)X
+1889(historical)X
+3 f
+2222(vi)X
+1 f
+2319(presentation)X
+2746(and)X
+3 f
+2897(nvi)X
+1 f
+3038(is)X
+3126(in)X
+3224(the)X
+3358(scrolling)X
+3674(and)X
+3826(screen)X
+576 3267(oriented)N
+893(position)X
+1204(commands,)X
+3 f
+1625(<control-B>)X
+1 f
+2042(,)X
+3 f
+2116(<control-D>)X
+1 f
+2538(,)X
+3 f
+2612(<control-E>)X
+1 f
+3029(,)X
+3 f
+3103(<control-F>)X
+1 f
+3516(,)X
+3 f
+3590(<control-U>)X
+1 f
+4012(,)X
+3 f
+576 3357(<control-Y>)N
+1 f
+998(,)X
+3 f
+1049(H)X
+1 f
+1111(,)X
+3 f
+1162(L)X
+1 f
+1246(and)X
+3 f
+1394(M)X
+1 f
+1470(.)X
+1542(In)X
+1641(historical)X
+1971(implementations)X
+2536(of)X
+3 f
+2635(vi)X
+1 f
+2697(,)X
+2749(these)X
+2946(commands)X
+3325(acted)X
+3527(on)X
+3639(physical)X
+3938(\(as)X
+576 3447(opposed)N
+877(to)X
+973(logical,)X
+1245(or)X
+1346(screen\))X
+1613(lines.)X
+1838(For)X
+1982(lines)X
+2166(that)X
+2319(were)X
+2509(suf\256ciently)X
+2902(long)X
+3077(in)X
+3172(relation)X
+3450(to)X
+3545(the)X
+3676(size)X
+3834(of)X
+3934(the)X
+576 3537(screen,)N
+822(this)X
+957(meant)X
+1174(that)X
+1315(single)X
+1527(line)X
+1668(scroll)X
+1867(commands)X
+2235(might)X
+2442(repaint)X
+2686(the)X
+2805(entire)X
+3009(screen,)X
+3256(scrolling)X
+3557(or)X
+3645(screen)X
+3872(posi-)X
+576 3627(tioning)N
+823(command)X
+1160(might)X
+1367(not)X
+1489(change)X
+1737(the)X
+1855(screen)X
+2081(or)X
+2168(move)X
+2366(the)X
+2484(cursor)X
+2705(at)X
+2783(all,)X
+2903(and)X
+3039(some)X
+3228(lines)X
+3399(simply)X
+3636(could)X
+3834(not)X
+3956(be)X
+576 3717(displayed,)N
+927(even)X
+1104(though)X
+3 f
+1351(vi)X
+1 f
+1438(would)X
+1663(edit)X
+1808(the)X
+1931(\256le)X
+2058(that)X
+2203(contained)X
+2540(them.)X
+2765(In)X
+3 f
+2857(nvi)X
+1 f
+2963(,)X
+3008(these)X
+3198(commands)X
+3570(act)X
+3689(on)X
+3794(logical,)X
+576 3807(i.e.)N
+718(screen)X
+968(lines.)X
+1203(You)X
+1385(are)X
+1528(unlikely)X
+1834(to)X
+1939(notice)X
+2178(any)X
+2337(difference)X
+2707(unless)X
+2950(you)X
+3113(are)X
+3255(editing)X
+3520(\256les)X
+3696(with)X
+3881(lines)X
+576 3897(signi\256cantly)N
+991(longer)X
+1216(than)X
+1374(a)X
+1430(screen)X
+1656(width.)X
+3 f
+776 4020(Vi)N
+1 f
+883(keeps)X
+1094(track)X
+1283(of)X
+1378(the)X
+1504(currently)X
+1822(``most)X
+2059(attractive'')X
+2440(cursor)X
+2669(position.)X
+2994(Each)X
+3183(command)X
+3527(description)X
+3911(\(for)X
+576 4110(commands)N
+945(that)X
+1087(can)X
+1221(change)X
+1471(the)X
+1591(current)X
+1841(cursor)X
+2064(position\),)X
+2390(speci\256es)X
+2688(if)X
+2759(the)X
+2879(cursor)X
+3101(is)X
+3175(set)X
+3285(to)X
+3368(a)X
+3425(speci\256c)X
+3691(location)X
+3970(in)X
+576 4200(the)N
+705(line,)X
+876(or)X
+974(if)X
+1054(it)X
+1129(is)X
+1213(moved)X
+1462(to)X
+1555(the)X
+1685(``most)X
+1926(attractive)X
+2257(cursor)X
+2490(position''.)X
+2873(The)X
+3030(latter)X
+3227(means)X
+3464(that)X
+3616(the)X
+3746(cursor)X
+3979(is)X
+576 4290(moved)N
+825(to)X
+918(the)X
+1047(cursor)X
+1279(position)X
+1567(that)X
+1717(is)X
+1800(vertically)X
+2133(as)X
+2230(close)X
+2425(as)X
+2522(possible)X
+2814(to)X
+2906(the)X
+3034(current)X
+3292(cursor)X
+3523(position.)X
+3850(If)X
+3934(the)X
+576 4380(current)N
+826(line)X
+968(is)X
+1043(shorter)X
+1288(than)X
+1448(the)X
+1568(cursor)X
+1791(position)X
+3 f
+2070(vi)X
+1 f
+2154(would)X
+2376(select,)X
+2601(the)X
+2722(cursor)X
+2946(is)X
+3022(positioned)X
+3378(on)X
+3481(the)X
+3602(last)X
+3736(character)X
+576 4470(in)N
+666(the)X
+792(line.)X
+980(\(If)X
+1089(the)X
+1215(line)X
+1363(is)X
+1444(empty,)X
+1692(the)X
+1818(cursor)X
+2047(is)X
+2128(positioned)X
+2489(on)X
+2596(the)X
+2721(\256rst)X
+2872(column)X
+3139(of)X
+3233(the)X
+3358(line.\))X
+3572(If)X
+3653(a)X
+3716(command)X
+576 4560(moves)N
+810(the)X
+933(cursor)X
+1159(to)X
+1246(the)X
+1369(most)X
+1549(attractive)X
+1873(position,)X
+2175(it)X
+2245(does)X
+2418(not)X
+2546(alter)X
+2715(the)X
+2839(current)X
+3093(cursor)X
+3320(position,)X
+3623(and)X
+3765(a)X
+3827(subse-)X
+576 4650(quent)N
+775(movement)X
+1134(will)X
+1279(again)X
+1474(attempt)X
+1735(to)X
+1818(move)X
+2017(the)X
+2136(cursor)X
+2358(to)X
+2441(that)X
+2581(position.)X
+2898(Therefore,)X
+3256(although)X
+3556(a)X
+3612(movement)X
+3970(to)X
+576 4740(a)N
+640(line)X
+788(shorter)X
+1039(than)X
+1205(the)X
+1331(currently)X
+1649(most)X
+1832(attractive)X
+2159(position)X
+2444(will)X
+2596(cause)X
+2803(the)X
+2929(cursor)X
+3158(to)X
+3248(move)X
+3454(to)X
+3544(the)X
+3671(end)X
+3816(of)X
+3912(that)X
+576 4830(line,)N
+737(a)X
+794(subsequent)X
+1171(movement)X
+1530(to)X
+1613(a)X
+1670(longer)X
+1896(line)X
+2037(will)X
+2182(cause)X
+2382(the)X
+2501(cursor)X
+2723(to)X
+2806(move)X
+3005(back)X
+3178(to)X
+3260(the)X
+3378(most)X
+3553(attractive)X
+3872(posi-)X
+576 4920(tion.)N
+776 5043(In)N
+864(addition,)X
+1167(the)X
+3 f
+1286($)X
+1 f
+1347(command)X
+1684(makes)X
+1910(the)X
+2029(end)X
+2166(of)X
+2254(each)X
+2423(line)X
+2564(the)X
+2684(most)X
+2861(attractive)X
+3182(cursor)X
+3405(position)X
+3684(rather)X
+3894(than)X
+576 5133(a)N
+632(speci\256c)X
+897(column.)X
+776 5256(Each)N
+3 f
+958(vi)X
+1 f
+1042(command)X
+1380(described)X
+1710(below)X
+1928(notes)X
+2119(where)X
+2338(the)X
+2458(cursor)X
+2681(ends)X
+2850(up)X
+2952(after)X
+3122(it)X
+3188(is)X
+3263(executed.)X
+3611(This)X
+3775(position)X
+576 5346(is)N
+653(described)X
+985(in)X
+1071(terms)X
+1273(of)X
+1364(characters)X
+1715(on)X
+1819(the)X
+1940(line,)X
+2103(i.e.)X
+2244(``the)X
+2419(previous)X
+2718 0.3409(character'',)AX
+3111(or,)X
+3221(``the)X
+3396(last)X
+3530(character)X
+3849(in)X
+3934(the)X
+576 5436(line''.)N
+810(This)X
+972(is)X
+1045(to)X
+1127(avoid)X
+1325(needing)X
+1599(to)X
+1681(continually)X
+2061(refer)X
+2234(to)X
+2316(on)X
+2416(what)X
+2592(part)X
+2737(of)X
+2824(the)X
+2942(character)X
+3258(the)X
+3376(cursor)X
+3597(rests.)X
+776 5559(The)N
+921(following)X
+1252(words)X
+1468(have)X
+1640(special)X
+1883(meaning)X
+2179(for)X
+3 f
+2293(vi)X
+1 f
+2375(commands.)X
+
+10 p
+%%Page: 10 9
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-10)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+576 762(previous)N
+889(context)X
+1 f
+776 852(The)N
+927(position)X
+1210(of)X
+1303(the)X
+1427(cursor)X
+1654(before)X
+1886(the)X
+2010(command)X
+2352(which)X
+2574(caused)X
+2819(the)X
+2944(last)X
+3082(absolute)X
+3376(movement)X
+3741(was)X
+3893(exe-)X
+776 942(cuted.)N
+1012(Each)X
+3 f
+1195(vi)X
+1 f
+1279(command)X
+1617(described)X
+1947(in)X
+2031(the)X
+2151(next)X
+2311(section)X
+2559(that)X
+2700(is)X
+2774(considered)X
+3143(an)X
+3240(absolute)X
+3528(movement)X
+3887(is)X
+3961(so)X
+776 1032(noted.)N
+1014(In)X
+1101(addition,)X
+1403(specifying)X
+2 f
+1757(any)X
+1 f
+1893(address)X
+2154(to)X
+2236(an)X
+3 f
+2332(ex)X
+1 f
+2428(command)X
+2764(is)X
+2837(considered)X
+3205(an)X
+3301(absolute)X
+3588(movement.)X
+3 f
+576 1212(motion)N
+1 f
+776 1302(A)N
+863(second)X
+3 f
+1115(vi)X
+1 f
+1206(command)X
+1551(can)X
+1693(be)X
+1799(used)X
+1976(as)X
+2073(an)X
+2179(optional)X
+2471(trailing)X
+2732(argument)X
+3065(to)X
+3157(the)X
+3 f
+3285(vi)X
+3377(!)X
+1 f
+3404(,)X
+3 f
+3454(<)X
+1 f
+3500(,)X
+3 f
+3550(>)X
+1 f
+3596(,)X
+3 f
+3646(c)X
+1 f
+3682(,)X
+3 f
+3732(d)X
+1 f
+3776(,)X
+3 f
+3826(y)X
+1 f
+(,)S
+3916(and)X
+776 1392(\(depending)N
+1158(on)X
+1259(the)X
+3 f
+1378(tildeop)X
+1 f
+1633(option\))X
+3 f
+1884(\304)X
+1 f
+1931(commands.)X
+2338(This)X
+2500(command)X
+2836(indicates)X
+3141(the)X
+3259(end)X
+3395(of)X
+3482(the)X
+3600(region)X
+3825(of)X
+3912(text)X
+776 1482(that's)N
+993(affected)X
+1292(by)X
+1411(the)X
+1548(command.)X
+1943(The)X
+2107(motion)X
+2372(command)X
+2727(may)X
+2904(be)X
+3019(either)X
+3242(the)X
+3380(command)X
+3736(character)X
+776 1572(repeated)N
+1070(\(in)X
+1180(which)X
+1397(case)X
+1557(it)X
+1622(means)X
+1848(the)X
+1967(current)X
+2216(line\))X
+2384(or)X
+2472(a)X
+2528(cursor)X
+2749(movement)X
+3107(command.)X
+3483(In)X
+3570(the)X
+3688(latter)X
+3873(case,)X
+776 1662(the)N
+894(region)X
+1119(affected)X
+1399(by)X
+1499(the)X
+1617(command)X
+1953(is)X
+2026(from)X
+2202(the)X
+2320(starting)X
+2581(or)X
+2669(stopping)X
+2965(cursor)X
+3187(position)X
+3465(which)X
+3682(comes)X
+3908(\256rst)X
+776 1752(in)N
+859(the)X
+978(\256le,)X
+1120(to)X
+1202(immediately)X
+1622(before)X
+1848(the)X
+1966(starting)X
+2226(or)X
+2313(stopping)X
+2608(cursor)X
+2829(position)X
+3106(which)X
+3322(comes)X
+3547(later)X
+3710(in)X
+3792(the)X
+3910(\256le.)X
+776 1842(Commands)N
+1166(that)X
+1313(operate)X
+1577(on)X
+1684(lines)X
+1862(instead)X
+2116(of)X
+2210(using)X
+2410(beginning)X
+2757(and)X
+2900(ending)X
+3145(cursor)X
+3373(positions)X
+3688(operate)X
+3952(on)X
+776 1932(all)N
+880(of)X
+971(the)X
+1093(lines)X
+1268(that)X
+1412(are)X
+1535(wholly)X
+1781(or)X
+1872(partially)X
+2163(in)X
+2248(the)X
+2369(region.)X
+2637(In)X
+2727(addition,)X
+3032(some)X
+3224(other)X
+3412(commands)X
+3782(become)X
+776 2022(line)N
+922(oriented)X
+1211(depending)X
+1571(on)X
+1677(where)X
+1900(in)X
+1988(the)X
+2112(text)X
+2258(they)X
+2422(are)X
+2548(used.)X
+2762(The)X
+2914(command)X
+3257(descriptions)X
+3671(below)X
+3894(note)X
+776 2112(these)N
+961(special)X
+1204(cases.)X
+776 2292(The)N
+921(following)X
+1252(commands)X
+1619(may)X
+1777(all)X
+1877(be)X
+1973(used)X
+2140(as)X
+2227(motion)X
+2473(components)X
+2880(for)X
+3 f
+2994(vi)X
+1 f
+3076(commands:)X
+7 f
+776 2505(<control-A>)N
+1472(<control-H>)X
+2120(<control-J>)X
+2768(<control-M>)X
+776 2595(<control-N>)N
+1472(<control-P>)X
+2312(<space>)X
+3248($)X
+1256 2685(%)N
+1424('<character>)X
+2600(\()X
+3248(\))X
+1256 2775(+)N
+1952(,)X
+2600(-)X
+3248(/)X
+1256 2865(0)N
+1952(;)X
+2600(?)X
+3248(B)X
+1256 2955(E)N
+1952(F)X
+2600(G)X
+3248(H)X
+1256 3045(L)N
+1952(M)X
+2600(N)X
+3248(T)X
+1256 3135(W)N
+1904([[)X
+2552(]])X
+3248(\303)X
+1256 3225(_)N
+1424(`<character>)X
+2600(b)X
+3248(e)X
+1256 3315(f)N
+1952(h)X
+2600(j)X
+3248(k)X
+1256 3405(l)N
+1952(n)X
+2600(t)X
+3248(w)X
+1256 3495({)N
+1952(|)X
+2600(})X
+1 f
+776 3708(The)N
+923(optional)X
+1207(count)X
+1407(pre\256x)X
+1617(available)X
+1930(for)X
+2047(some)X
+2239(of)X
+2329(the)X
+3 f
+2450(vi)X
+1 f
+2535(commands)X
+2905(that)X
+3048(take)X
+3205(motion)X
+3454(commands,)X
+3844(or)X
+3934(the)X
+776 3798(count)N
+975(pre\256x)X
+1183(available)X
+1494(for)X
+1609(the)X
+3 f
+1728(vi)X
+1 f
+1811(commands)X
+2179(that)X
+2320(are)X
+2439(used)X
+2606(as)X
+2693(motion)X
+2939(components,)X
+3366(may)X
+3524(be)X
+3620(included)X
+3916(and)X
+776 3888(is)N
+2 f
+850(always)X
+1 f
+1093(considered)X
+1463(part)X
+1610(of)X
+1699(the)X
+1819(motion)X
+2067(argument.)X
+2432(For)X
+2565(example,)X
+2879(the)X
+2999(commands)X
+3368(``)X
+7 f
+3422(c2w)X
+1 f
+('')S
+3642(and)X
+3780(``)X
+7 f
+3834(2cw)X
+1 f
+('')S
+776 3978(are)N
+903(equivalent,)X
+1285(and)X
+1429(the)X
+1555(region)X
+1788(affected)X
+2076(by)X
+2184(the)X
+3 f
+2310(c)X
+1 f
+2374(command)X
+2718(is)X
+2799(two)X
+2947(words)X
+3171(of)X
+3266(text.)X
+3454(In)X
+3549(addition,)X
+3858(if)X
+3934(the)X
+776 4068(optional)N
+1066(count)X
+1272(pre\256x)X
+1487(is)X
+1568(speci\256ed)X
+1881(for)X
+2003(both)X
+2173(the)X
+3 f
+2299(vi)X
+1 f
+2389(command)X
+2733(and)X
+2877(its)X
+2980(motion)X
+3234(component,)X
+3639(the)X
+3766(effect)X
+3979(is)X
+776 4158(multiplicative)N
+1246(and)X
+1388(is)X
+1467(considered)X
+1841(part)X
+1992(of)X
+2085(the)X
+2209(motion)X
+2461(argument.)X
+2830(For)X
+2967(example,)X
+3285(the)X
+3408(commands)X
+3780(``)X
+7 f
+3834(4cw)X
+1 f
+('')S
+776 4248(and)N
+912(``)X
+7 f
+966(2c2w)X
+1 f
+('')S
+1232(are)X
+1351(equivalent,)X
+1725(and)X
+1861(the)X
+1979(region)X
+2204(affected)X
+2484(by)X
+2584(the)X
+3 f
+2702(c)X
+1 f
+2758(command)X
+3094(is)X
+3167(four)X
+3321(words)X
+3537(of)X
+3624(text.)X
+3 f
+576 4428(count)N
+1 f
+776 4518(A)N
+859(positive)X
+1137(number)X
+1407(used)X
+1579(as)X
+1671(an)X
+1772(optional)X
+2059(argument)X
+2387(to)X
+2474(most)X
+2654(commands,)X
+3046(either)X
+3254(to)X
+3341(give)X
+3504(a)X
+3566(size)X
+3717(or)X
+3810(a)X
+3872(posi-)X
+776 4608(tion)N
+930(\(for)X
+1081(display)X
+1342(or)X
+1439(movement)X
+1807(commands\),)X
+2231(or)X
+2328(as)X
+2425(a)X
+2491(repeat)X
+2718(count)X
+2926(\(for)X
+3077(commands)X
+3454(that)X
+3604(modify)X
+3865(text\).)X
+776 4698(The)N
+932(count)X
+1141(argument)X
+1475(is)X
+1559(always)X
+1813(optional)X
+2106(and)X
+2254(defaults)X
+2540(to)X
+2634(1)X
+2706(unless)X
+2938(otherwise)X
+3282(noted)X
+3492(in)X
+3586(the)X
+3716(command)X
+776 4788(description.)N
+776 4968(When)N
+995(a)X
+3 f
+1059(vi)X
+1 f
+1149(command)X
+1493(synopsis)X
+1796(shows)X
+2024(both)X
+2194(a)X
+7 f
+2258([buffer])X
+1 f
+2670(and)X
+7 f
+2814([count])X
+1 f
+(,)S
+3198(they)X
+3364(may)X
+3530(be)X
+3634(presented)X
+3970(in)X
+776 5058(any)N
+912(order.)X
+3 f
+576 5238(bigword)N
+1 f
+776 5328(A)N
+854(set)X
+963(of)X
+1050(non-whitespace)X
+1575(characters)X
+1923(preceded)X
+2235(and)X
+2372(followed)X
+2678(by)X
+2779(whitespace)X
+3157(characters)X
+3505(or)X
+3593(the)X
+3712(beginning)X
+776 5418(or)N
+863(end)X
+999(of)X
+1086(the)X
+1204(\256le)X
+1326(or)X
+1413(line.)X
+776 5598(Groups)N
+1040(of)X
+1135(empty)X
+1363(lines)X
+1542(\(or)X
+1664(lines)X
+1843(containing)X
+2209(only)X
+2379(whitespace)X
+2764 0.3250(characters\))AX
+3146(are)X
+3274(treated)X
+3522(as)X
+3618(a)X
+3683(single)X
+3903(big-)X
+776 5688(word.)N
+
+11 p
+%%Page: 11 10
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-11)X
+576 762(word)N
+1 f
+776 852(Generally,)N
+1133(in)X
+1215(languages)X
+1556(where)X
+1773(it)X
+1837(is)X
+1910(applicable,)X
+3 f
+2280(vi)X
+1 f
+2362(recognizes)X
+2726(two)X
+2866(kinds)X
+3060(of)X
+3148(words.)X
+3405(First,)X
+3592(a)X
+3649(sequence)X
+3965(of)X
+776 942(letters,)N
+1027(digits)X
+1239(and)X
+1390(underscores,)X
+1829(delimited)X
+2166(at)X
+2259(both)X
+2435(ends)X
+2616(by:)X
+2752(characters)X
+3113(other)X
+3312(than)X
+3484(letters,)X
+3734(digits,)X
+3965(or)X
+776 1032(underscores;)N
+1206(the)X
+1328(beginning)X
+1672(or)X
+1763(end)X
+1903(of)X
+1994(a)X
+2054(line;)X
+2220(the)X
+2343(beginning)X
+2688(or)X
+2780(end)X
+2921(of)X
+3013(the)X
+3136(\256le.)X
+3303(Second,)X
+3584(a)X
+3645(sequence)X
+3965(of)X
+776 1122(characters)N
+1126(other)X
+1313(than)X
+1473(letters,)X
+1711(digits,)X
+1930(underscores,)X
+2356(or)X
+2445(whitespace)X
+2824(characters,)X
+3193(delimited)X
+3517(at)X
+3597(both)X
+3761(ends)X
+3930(by:)X
+776 1212(a)N
+838(letter,)X
+1049(digit,)X
+1241(underscore,)X
+1640(or)X
+1733(whitespace)X
+2116(character;)X
+2460(the)X
+2584(beginning)X
+2930(or)X
+3024(end)X
+3167(of)X
+3261(a)X
+3324(line;)X
+3493(the)X
+3618(beginning)X
+3965(or)X
+776 1302(end)N
+912(of)X
+999(the)X
+1117(\256le.)X
+776 1482(Groups)N
+1032(of)X
+1119(empty)X
+1339(lines)X
+1510(\(or)X
+1624(lines)X
+1795(containing)X
+2153(only)X
+2315(whitespace)X
+2692 0.3250(characters\))AX
+3066(are)X
+3185(treated)X
+3424(as)X
+3511(a)X
+3567(single)X
+3778(word.)X
+3 f
+576 1662(paragraph)N
+1 f
+776 1752(An)N
+898(area)X
+1057(of)X
+1148(text)X
+1292(that)X
+1436(begins)X
+1670(with)X
+1837(either)X
+2045(the)X
+2168(beginning)X
+2513(of)X
+2605(a)X
+2666(\256le,)X
+2813(an)X
+2914(empty)X
+3139(line,)X
+3304(or)X
+3396(a)X
+3457(section)X
+3709(boundary,)X
+776 1842(and)N
+912(continues)X
+1239(until)X
+1405(either)X
+1608(an)X
+1704(empty)X
+1924(line,)X
+2084(section)X
+2331(boundary,)X
+2674(or)X
+2761(the)X
+2879(end)X
+3015(of)X
+3102(the)X
+3220(\256le.)X
+776 2022(Groups)N
+1037(of)X
+1129(empty)X
+1355(lines)X
+1532(\(or)X
+1652(lines)X
+1829(containing)X
+2193(only)X
+2361(whitespace)X
+2744 0.3250(characters\))AX
+3124(are)X
+3249(treated)X
+3494(as)X
+3587(a)X
+3649(single)X
+3866(para-)X
+776 2112(graph.)N
+776 2292(Additional)N
+1138(paragraph)X
+1480(boundaries)X
+1852(can)X
+1984(be)X
+2080(de\256ned)X
+2336(using)X
+2529(the)X
+3 f
+2647(paragraph)X
+1 f
+3031(option.)X
+3 f
+576 2472(section)N
+1 f
+776 2562(An)N
+903(area)X
+1067(of)X
+1163(text)X
+1312(that)X
+1461(starts)X
+1659(with)X
+1830(the)X
+1957(beginning)X
+2306(of)X
+2402(the)X
+2529(\256le)X
+2660(or)X
+2756(a)X
+2822(line)X
+2972(whose)X
+3207(\256rst)X
+3361(character)X
+3687(is)X
+3770(an)X
+3876(open)X
+776 2652(brace)N
+971(\(``)X
+7 f
+1052({)X
+1 f
+(''\))S
+1201(and)X
+1337(continues)X
+1664(until)X
+1830(the)X
+1948(next)X
+2106(section)X
+2353(or)X
+2440(the)X
+2558(end)X
+2694(of)X
+2781(the)X
+2899(\256le.)X
+776 2832(Additional)N
+1138(section)X
+1385(boundaries)X
+1757(can)X
+1889(be)X
+1985(de\256ned)X
+2241(using)X
+2434(the)X
+3 f
+2552(sections)X
+1 f
+2839(option.)X
+3 f
+576 3012(sentence)N
+1 f
+776 3102(An)N
+895(area)X
+1051(of)X
+1139(text)X
+1280(that)X
+1421(begins)X
+1651(with)X
+1814(either)X
+2018(the)X
+2137(beginning)X
+2478(of)X
+2566(the)X
+2685(\256le)X
+2808(or)X
+2896(the)X
+3015(\256rst)X
+3160(nonblank)X
+3479(character)X
+3796(follow-)X
+776 3192(ing)N
+901(the)X
+1022(previous)X
+1321(sentence,)X
+1641(paragraph,)X
+2006(or)X
+2096(section)X
+2346(boundary)X
+2672(and)X
+2811(continues)X
+3141(until)X
+3310(the)X
+3431(end)X
+3570(of)X
+3660(the)X
+3781(\256le)X
+3906(or)X
+3996(a)X
+776 3282(or)N
+866(a)X
+926(period)X
+1155(\(``)X
+7 f
+1236(.)X
+1 f
+(''\))S
+1409(exclamation)X
+1825(point)X
+2013(\(``)X
+7 f
+2094(!)X
+1 f
+(''\))S
+2267(or)X
+2358(question)X
+2653(mark)X
+2842(\(``)X
+7 f
+2923(?)X
+1 f
+(''\))S
+3096(character,)X
+3436(followed)X
+3745(by)X
+3849(either)X
+776 3372(an)N
+882(end-of-line)X
+1269(or)X
+1366(two)X
+1516(whitespace)X
+1903(characters.)X
+2300(Any)X
+2468(number)X
+2743(of)X
+2840(closing)X
+3101(parentheses)X
+3505(\(``)X
+7 f
+3586(\))X
+1 f
+(''\),)S
+3764(brackets)X
+776 3462(\(``)N
+7 f
+857(])X
+1 f
+(''\))S
+1007(or)X
+1095(double-quote)X
+1540(\(``)X
+7 f
+1621(")X
+1 f
+(''\))S
+1772(characters)X
+2121(can)X
+2255(appear)X
+2492(between)X
+2782(the)X
+2902(period,)X
+3149(exclamation)X
+3563(point,)X
+3769(or)X
+3858(ques-)X
+776 3552(tion)N
+920(mark)X
+1105(and)X
+1241(the)X
+1359(whitespace)X
+1736(characters)X
+2083(or)X
+2170(end-of-line.)X
+776 3732(Groups)N
+1040(of)X
+1135(empty)X
+1363(lines)X
+1542(\(or)X
+1664(lines)X
+1843(containing)X
+2209(only)X
+2379(whitespace)X
+2764 0.3250(characters\))AX
+3146(are)X
+3273(treated)X
+3520(as)X
+3615(a)X
+3679(single)X
+3898(sen-)X
+776 3822(tence.)N
+3 f
+576 4008(10.)N
+716(Vi)X
+816(Commands)X
+1 f
+776 4131(The)N
+926(following)X
+1262(section)X
+1514(describes)X
+1838(the)X
+1961(commands)X
+2333(available)X
+2649(in)X
+2737(the)X
+2861(command)X
+3203(mode)X
+3407(of)X
+3500(the)X
+3 f
+3624(vi)X
+1 f
+3712(editor.)X
+3965(In)X
+576 4221(each)N
+745(entry)X
+931(below,)X
+1168(the)X
+1287(tag)X
+1406(line)X
+1547(is)X
+1621(a)X
+1678(usage)X
+1882(synopsis)X
+2178(for)X
+2293(the)X
+2412(command)X
+2749(character.)X
+3106(In)X
+3194(addition,)X
+3496(the)X
+3614(\256nal)X
+3776(line)X
+3916(and)X
+576 4311(column)N
+836(the)X
+954(cursor)X
+1175(rests)X
+1342(upon,)X
+1542(and)X
+1678(any)X
+1814(options)X
+2069(which)X
+2285(affect)X
+2489(the)X
+2607(command)X
+2943(are)X
+3062(noted.)X
+3 f
+576 4491([count])N
+841(<control-A>)X
+1 f
+776 4581(Search)N
+1034(forward)X
+7 f
+1328(count)X
+1 f
+1607(times)X
+1819(for)X
+1952(the)X
+2089(current)X
+2356(word.)X
+2600(The)X
+2764(current)X
+3031(word)X
+3236(begins)X
+3485(at)X
+3583(the)X
+3721(\256rst)X
+3885(non-)X
+776 4671(whitespace)N
+1164(character)X
+1491(on)X
+1602(or)X
+1699(after)X
+1877(the)X
+2005(current)X
+2263(cursor)X
+2494(position,)X
+2801(and)X
+2947(extends)X
+3222(up)X
+3332(to)X
+3424(the)X
+3552(next)X
+3720(non-word)X
+776 4761(character)N
+1095(or)X
+1185(the)X
+1306(end)X
+1445(of)X
+1535(the)X
+1656(line.)X
+1839(The)X
+1987(search)X
+2216(is)X
+2292(literal,)X
+2522(i.e.)X
+2643(no)X
+2746(characters)X
+3096(in)X
+3182(the)X
+3304(word)X
+3493(have)X
+3669(any)X
+3809(special)X
+776 4851(meaning)N
+1076(in)X
+1162(terms)X
+1364(of)X
+1455(Regular)X
+1733(Expressions.)X
+2184(It)X
+2257(is)X
+2334(an)X
+2434(error)X
+2615(if)X
+2687(no)X
+2790(matching)X
+3111(pattern)X
+3357(is)X
+3433(found)X
+3643(between)X
+3934(the)X
+776 4941(starting)N
+1036(position)X
+1313(and)X
+1449(the)X
+1567(end)X
+1703(of)X
+1790(the)X
+1908(\256le.)X
+776 5121(The)N
+3 f
+926(<control-A>)X
+1 f
+1373(command)X
+1714(is)X
+1792(an)X
+1893(absolute)X
+2185(movement.)X
+2588(The)X
+3 f
+2738(<control-A>)X
+1 f
+3185(command)X
+3526(may)X
+3690(be)X
+3792(used)X
+3965(as)X
+776 5211(the)N
+896(motion)X
+1144(component)X
+1522(of)X
+1611(other)X
+3 f
+1798(vi)X
+1 f
+1881(commands,)X
+2269(in)X
+2352(which)X
+2569(case)X
+2729(any)X
+2866(text)X
+3007(copied)X
+3242(into)X
+3387(a)X
+3444(buffer)X
+3662(is)X
+3736(character)X
+776 5301(oriented.)N
+776 5481(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(where)X
+1815(the)X
+1933(word)X
+2118(is)X
+2191(found.)X
+776 5571(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(of)X
+2005(the)X
+2123(word.)X
+776 5661(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(extended)X
+1 f
+1963(,)X
+3 f
+2003(ignorecase)X
+1 f
+2384(and)X
+3 f
+2520(wrapscan)X
+1 f
+2869(options.)X
+
+12 p
+%%Page: 12 11
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-12)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+576 762([count])N
+841(<control-B>)X
+1 f
+776 852(Page)N
+965(backward)X
+7 f
+1311(count)X
+1 f
+1585(screens.)X
+1896(Two)X
+2077(lines)X
+2262(of)X
+2363(overlap)X
+2638(are)X
+2771(maintained)X
+3161(by)X
+3275(displaying)X
+3642(the)X
+3774(window)X
+776 942(starting)N
+1042(at)X
+1126(line)X
+7 f
+1272(\(top_line)X
+1758(-)X
+1860(count)X
+2154(*)X
+2256(window_size\))X
+2886(+)X
+2988(2)X
+1 f
+(,)S
+3081(where)X
+7 f
+3303(window_size)X
+1 f
+3856(is)X
+3934(the)X
+776 1032(value)N
+973(of)X
+1063(the)X
+3 f
+1184(window)X
+1 f
+1473(option.)X
+1741(\(In)X
+1859(the)X
+1981(case)X
+2144(of)X
+2235(split)X
+2396(screens,)X
+2677(this)X
+2816(size)X
+2965(is)X
+3042(corrected)X
+3366(to)X
+3452(the)X
+3574(current)X
+3826(screen)X
+776 1122(size.\))N
+988(This)X
+1150(is)X
+1223(an)X
+1319(error)X
+1496(if)X
+1565(the)X
+1683(movement)X
+2041(is)X
+2114(past)X
+2263(the)X
+2381(beginning)X
+2721(of)X
+2808(the)X
+2926(\256le.)X
+776 1302(The)N
+3 f
+921(<control-B>)X
+1 f
+1358(command)X
+1694(is)X
+1767(an)X
+1863(absolute)X
+2150(movement.)X
+776 1482(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(of)X
+1816(text)X
+1956(displayed)X
+2283(on)X
+2383(the)X
+2501(screen.)X
+776 1572(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(line.)X
+776 1662(Options:)N
+1136(None.)X
+3 f
+576 1842([count])N
+841(<control-D>)X
+1 f
+776 1932(Scroll)N
+990(forward)X
+7 f
+1268(count)X
+1 f
+1531(lines.)X
+1745(If)X
+7 f
+1822(count)X
+1 f
+2085(is)X
+2161(not)X
+2286(speci\256ed,)X
+2614(scroll)X
+2815(forward)X
+3093(the)X
+3214(number)X
+3482(of)X
+3572(lines)X
+3747(speci\256ed)X
+776 2022(by)N
+881(the)X
+1004(last)X
+3 f
+1140(<control-D>)X
+1 f
+1587(or)X
+3 f
+1679(<control-U>)X
+1 f
+2126(command.)X
+2507(If)X
+2586(this)X
+2726(is)X
+2803(the)X
+2925(\256rst)X
+3 f
+3073(<control-D>)X
+1 f
+3519(or)X
+3 f
+3610(<control-U>)X
+1 f
+776 2112(command,)N
+1141(scroll)X
+1348(forward)X
+1632(half)X
+1786(the)X
+1913(number)X
+2188(of)X
+2285(lines)X
+2466(in)X
+2558(the)X
+2686(screen.)X
+2962(\(In)X
+3086(the)X
+3214(case)X
+3383(of)X
+3480(split)X
+3647(screens,)X
+3934(the)X
+776 2202(default)N
+1026(scrolling)X
+1333(distance)X
+1623(is)X
+1703(corrected)X
+2029(to)X
+2117(half)X
+2268(the)X
+2392(current)X
+2646(screen)X
+2878(size.\))X
+3096(This)X
+3264(is)X
+3343(an)X
+3445(error)X
+3628(if)X
+3703(the)X
+3827(move-)X
+776 2292(ment)N
+956(is)X
+1029(past)X
+1178(the)X
+1296(end)X
+1432(of)X
+1519(the)X
+1637(\256le.)X
+776 2472(The)N
+3 f
+921(<control-D>)X
+1 f
+1363(command)X
+1699(is)X
+1772(an)X
+1868(absolute)X
+2155(movement.)X
+776 2652(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(plus)X
+1999(the)X
+2117(number)X
+2382(of)X
+2469(lines)X
+2640(scrolled.)X
+776 2742(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(line.)X
+776 2832(Options:)N
+1136(None.)X
+3 f
+576 3012([count])N
+841(<control-E>)X
+1 f
+776 3102(Scroll)N
+987(forward)X
+7 f
+1262(count)X
+1 f
+1522(lines,)X
+1713(leaving)X
+1969(the)X
+2087(cursor)X
+2308(on)X
+2408(the)X
+2526(current)X
+2774(line)X
+2914(and)X
+3050(column,)X
+3330(if)X
+3399(possible.)X
+3721(This)X
+3883(is)X
+3956(an)X
+776 3192(error)N
+953(if)X
+1022(the)X
+1140(movement)X
+1498(is)X
+1571(past)X
+1720(the)X
+1838(end)X
+1974(of)X
+2061(the)X
+2179(\256le.)X
+776 3372(Line:)N
+1136(Unchanged)X
+1524(unless)X
+1747(the)X
+1868(current)X
+2119(line)X
+2262(scrolls)X
+2494(off)X
+2611(the)X
+2732(screen,)X
+2981(in)X
+3066(which)X
+3285(case)X
+3447(it)X
+3514(is)X
+3590(set)X
+3702(to)X
+3787(the)X
+3908(\256rst)X
+1136 3462(line)N
+1276(on)X
+1376(the)X
+1494(screen.)X
+776 3552(Column:)N
+1136(Unchanged)X
+1523(unless)X
+1744(the)X
+1863(current)X
+2112(line)X
+2253(scrolls)X
+2483(off)X
+2598(the)X
+2717(screen,)X
+2964(in)X
+3047(which)X
+3264(case)X
+3424(it)X
+3489(is)X
+3563(set)X
+3673(to)X
+3757(the)X
+3877(most)X
+1136 3642(attractive)N
+1455(cursor)X
+1676(position.)X
+776 3732(Options:)N
+1136(None.)X
+3 f
+576 3912([count])N
+841(<control-F>)X
+1 f
+776 4002(Page)N
+955(forward)X
+7 f
+1233(count)X
+1 f
+1496(screens.)X
+1797(Two)X
+1968(lines)X
+2143(of)X
+2234(overlap)X
+2499(are)X
+2622(maintained)X
+3002(by)X
+3106(displaying)X
+3463(the)X
+3585(window)X
+3867(start-)X
+776 4092(ing)N
+900(at)X
+980(line)X
+7 f
+1122(top_line)X
+1556(+)X
+1654(count)X
+1944(*)X
+2042(window_size)X
+2620(-)X
+2718(2)X
+1 f
+(,)S
+2808(where)X
+7 f
+3027(window_size)X
+1 f
+3577(is)X
+3651(the)X
+3770(value)X
+3965(of)X
+776 4182(the)N
+3 f
+903(window)X
+1 f
+1198(option.)X
+1471(\(In)X
+1594(the)X
+1721(case)X
+1889(of)X
+1985(split)X
+2151(screens,)X
+2437(this)X
+2581(size)X
+2735(is)X
+2817(corrected)X
+3146(to)X
+3238(the)X
+3366(current)X
+3624(screen)X
+3860(size.\))X
+776 4272(This)N
+938(is)X
+1011(an)X
+1107(error)X
+1284(if)X
+1353(the)X
+1471(movement)X
+1829(is)X
+1902(past)X
+2051(the)X
+2169(end)X
+2305(of)X
+2392(the)X
+2510(\256le.)X
+776 4452(The)N
+3 f
+921(<control-F>)X
+1 f
+1354(command)X
+1690(is)X
+1763(an)X
+1859(absolute)X
+2146(movement.)X
+776 4632(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(line)X
+1742(on)X
+1842(the)X
+1960(screen.)X
+776 4722(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(current)X
+2689(line.)X
+776 4812(Options:)N
+1136(None.)X
+3 f
+576 4992(<control-G>)N
+1 f
+776 5082(Display)N
+1055(the)X
+1183(\256le)X
+1315(information.)X
+1763(The)X
+1918(information)X
+2326(includes)X
+2624(the)X
+2753(current)X
+3012(pathname,)X
+3375(the)X
+3504(current)X
+3763(line,)X
+3934(the)X
+776 5172(number)N
+1043(of)X
+1132(total)X
+1296(lines)X
+1469(in)X
+1553(the)X
+1673(\256le,)X
+1817(the)X
+1937(current)X
+2187(line)X
+2329(as)X
+2418(a)X
+2476(percentage)X
+2847(of)X
+2936(the)X
+3056(total)X
+3220(lines)X
+3393(in)X
+3477(the)X
+3597(\256le,)X
+3741(if)X
+3811(the)X
+3930(\256le)X
+776 5262(has)N
+905(been)X
+1079(modi\256ed,)X
+1405(was)X
+1552(able)X
+1708(to)X
+1792(be)X
+1890(locked,)X
+2146(if)X
+2217(the)X
+2337(\256le's)X
+2519(name)X
+2715(has)X
+2844(been)X
+3018(changed,)X
+3328(and)X
+3466(if)X
+3537(the)X
+3658(edit)X
+3801(session)X
+776 5352(is)N
+849(read-only.)X
+776 5532(Line:)N
+1136(Unchanged.)X
+776 5622(Column:)N
+1136(Unchanged.)X
+776 5712(Options:)N
+1136(None.)X
+
+13 p
+%%Page: 13 12
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-13)X
+576 762(<control-H>)N
+576 852([count])N
+841(h)X
+1 f
+776 942(Move)N
+989(the)X
+1113(cursor)X
+1340(back)X
+7 f
+1518(count)X
+1 f
+1784(characters)X
+2137(in)X
+2225(the)X
+2349(current)X
+2603(line.)X
+2789(This)X
+2957(is)X
+3036(an)X
+3138(error)X
+3321(if)X
+3396(the)X
+3520(cursor)X
+3747(is)X
+3827(on)X
+3934(the)X
+776 1032(\256rst)N
+920(character)X
+1236(in)X
+1318(the)X
+1436(line.)X
+776 1212(The)N
+3 f
+923(<control-H>)X
+1 f
+1372(and)X
+3 f
+1511(h)X
+1 f
+1578(commands)X
+1948(may)X
+2109(be)X
+2208(used)X
+2378(as)X
+2468(the)X
+2589(motion)X
+2838(component)X
+3217(of)X
+3307(other)X
+3 f
+3495(vi)X
+1 f
+3580(commands,)X
+3970(in)X
+776 1302(which)N
+992(case)X
+1151(any)X
+1287(text)X
+1427(copied)X
+1661(into)X
+1805(a)X
+1861(buffer)X
+2078(is)X
+2151(character)X
+2467(oriented.)X
+776 1482(Line:)N
+1136(Unchanged.)X
+776 1572(Column:)N
+1136(Set)X
+1263(to)X
+1350(the)X
+7 f
+1473(current)X
+1862(-)X
+1963(count)X
+1 f
+2229(character,)X
+2571(or,)X
+2684(the)X
+2808(\256rst)X
+2958(character)X
+3280(in)X
+3368(the)X
+3492(line)X
+3638(if)X
+7 f
+3713(count)X
+1 f
+3979(is)X
+1136 1662(greater)N
+1380(than)X
+1538(or)X
+1625(equal)X
+1819(to)X
+1901(the)X
+2019(number)X
+2284(of)X
+2371(characters)X
+2718(in)X
+2800(the)X
+2918(line)X
+3058(before)X
+3284(the)X
+3402(cursor.)X
+776 1752(Options:)N
+1136(None.)X
+3 f
+576 1932([count])N
+841(<control-J>)X
+576 2022([count])N
+841(<control-N>)X
+576 2112([count])N
+841(j)X
+1 f
+776 2202(Move)N
+993(the)X
+1121(cursor)X
+1352(down)X
+7 f
+1560(count)X
+1 f
+1830(lines)X
+2011(without)X
+2285(changing)X
+2609(the)X
+2737(current)X
+2995(column.)X
+3305(This)X
+3477(is)X
+3560(an)X
+3666(error)X
+3854(if)X
+3934(the)X
+776 2292(movement)N
+1134(is)X
+1207(past)X
+1356(the)X
+1474(end)X
+1610(of)X
+1697(the)X
+1815(\256le.)X
+776 2472(The)N
+3 f
+927(<control-J>)X
+1 f
+1331(,)X
+3 f
+1377(<control-N>)X
+1 f
+1825(and)X
+3 f
+1967(j)X
+1 f
+2020(commands)X
+2393(may)X
+2557(be)X
+2659(used)X
+2832(as)X
+2925(the)X
+3049(motion)X
+3301(component)X
+3684(of)X
+3778(other)X
+3 f
+3970(vi)X
+1 f
+776 2562(commands,)N
+1163(in)X
+1245(which)X
+1461(case)X
+1620(any)X
+1756(text)X
+1896(copied)X
+2130(into)X
+2274(a)X
+2330(buffer)X
+2547(is)X
+2620(line)X
+2760(oriented.)X
+776 2742(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(plus)X
+7 f
+1999(count)X
+1 f
+(.)S
+776 2832(Column:)N
+1136(The)X
+1281(most)X
+1456(attractive)X
+1775(cursor)X
+1996(position.)X
+776 2922(Options:)N
+1136(None.)X
+3 f
+576 3102(<control-L>)N
+576 3192(<control-R>)N
+1 f
+776 3282(Repaint)N
+1045(the)X
+1163(screen.)X
+776 3462(Line:)N
+1136(Unchanged.)X
+776 3552(Column:)N
+1136(Unchanged.)X
+776 3642(Options:)N
+1136(None.)X
+3 f
+576 3822([count])N
+841(<control-M>)X
+576 3912([count])N
+841(+)X
+1 f
+776 4002(Move)N
+984(the)X
+1103(cursor)X
+1325(down)X
+7 f
+1524(count)X
+1 f
+1785(lines)X
+1957(to)X
+2040(the)X
+2159(\256rst)X
+2304(nonblank)X
+2623(character)X
+2940(of)X
+3028(that)X
+3169(line.)X
+3350(This)X
+3513(is)X
+3587(an)X
+3684(error)X
+3863(if)X
+3934(the)X
+776 4092(movement)N
+1134(is)X
+1207(past)X
+1356(the)X
+1474(end)X
+1610(of)X
+1697(the)X
+1815(\256le.)X
+776 4272(The)N
+3 f
+922(<control-M>)X
+1 f
+1384(and)X
+3 f
+1522(+)X
+1 f
+1590(commands)X
+1959(may)X
+2119(be)X
+2217(used)X
+2386(as)X
+2475(the)X
+2595(motion)X
+2843(component)X
+3221(of)X
+3310(other)X
+3 f
+3497(vi)X
+1 f
+3581(commands,)X
+3970(in)X
+776 4362(which)N
+992(case)X
+1151(any)X
+1287(text)X
+1427(copied)X
+1661(into)X
+1805(a)X
+1861(buffer)X
+2078(is)X
+2151(line)X
+2291(oriented.)X
+776 4542(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(plus)X
+7 f
+1999(count)X
+1 f
+(.)S
+776 4632(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 4722(Options:)N
+1136(None.)X
+3 f
+576 4902([count])N
+841(<control-P>)X
+576 4992([count])N
+841(k)X
+1 f
+776 5082(Move)N
+983(the)X
+1101(cursor)X
+1323(up)X
+7 f
+1424(count)X
+1 f
+1685(lines,)X
+1877(without)X
+2142(changing)X
+2457(the)X
+2576(current)X
+2825(column.)X
+3126(This)X
+3289(is)X
+3363(an)X
+3460(error)X
+3638(if)X
+3708(the)X
+3827(move-)X
+776 5172(ment)N
+956(is)X
+1029(past)X
+1178(the)X
+1296(beginning)X
+1636(of)X
+1723(the)X
+1841(\256le.)X
+776 5352(The)N
+3 f
+924(<control-P>)X
+1 f
+1360(and)X
+3 f
+1499(k)X
+1 f
+1566(commands)X
+1937(may)X
+2099(be)X
+2199(used)X
+2370(as)X
+2461(the)X
+2583(motion)X
+2833(component)X
+3213(of)X
+3304(other)X
+3 f
+3493(vi)X
+1 f
+3579(commands,)X
+3970(in)X
+776 5442(which)N
+992(case)X
+1151(any)X
+1287(text)X
+1427(copied)X
+1661(into)X
+1805(a)X
+1861(buffer)X
+2078(is)X
+2151(line)X
+2291(oriented.)X
+776 5622(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(minus)X
+2061(count.)X
+776 5712(Column:)N
+1136(The)X
+1281(most)X
+1456(attractive)X
+1775(cursor)X
+1996(position.)X
+
+14 p
+%%Page: 14 13
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-14)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(Options:)N
+1136(None.)X
+3 f
+576 942(<control-T>)N
+1 f
+776 1032(Return)N
+1014(to)X
+1096(the)X
+1214(most)X
+1389(recent)X
+1606(tag)X
+1724(context.)X
+2020(The)X
+3 f
+2165(<control-T>)X
+1 f
+2602(command)X
+2938(is)X
+3011(an)X
+3107(absolute)X
+3394(movement.)X
+776 1212(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(context)X
+1714(of)X
+1801(the)X
+1919(previous)X
+2215(tag)X
+2333(command.)X
+776 1302(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(context)X
+1714(of)X
+1801(the)X
+1919(previous)X
+2215(tag)X
+2333(command.)X
+776 1392(Options:)N
+1136(None.)X
+3 f
+576 1572(<control-U>)N
+1 f
+776 1662(Scroll)N
+1003(backward)X
+7 f
+1352(count)X
+1 f
+1628(lines.)X
+1856(If)X
+7 f
+1947(count)X
+1 f
+2224(is)X
+2314(not)X
+2453(speci\256ed,)X
+2795(scroll)X
+3010(backward)X
+3360(the)X
+3495(number)X
+3777(of)X
+3881(lines)X
+776 1752(speci\256ed)N
+1096(by)X
+1211(the)X
+1344(last)X
+3 f
+1490(<control-D>)X
+1 f
+1947(or)X
+3 f
+2049(<control-U>)X
+1 f
+2505(command.)X
+2895(If)X
+2983(this)X
+3132(is)X
+3219(the)X
+3351(\256rst)X
+3 f
+3509(<control-D>)X
+1 f
+3965(or)X
+3 f
+776 1842(<control-U>)N
+1 f
+1221(command,)X
+1580(scroll)X
+1781(backward)X
+2117(half)X
+2266(the)X
+2388(number)X
+2657(of)X
+2748(lines)X
+2923(in)X
+3009(the)X
+3131(screen.)X
+3401(\(In)X
+3519(the)X
+3641(case)X
+3804(of)X
+3895(split)X
+776 1932(screens,)N
+1057(the)X
+1179(default)X
+1426(scrolling)X
+1729(distance)X
+2015(is)X
+2091(corrected)X
+2414(to)X
+2499(half)X
+2647(the)X
+2768(current)X
+3019(screen)X
+3248(size.\))X
+3463(This)X
+3628(is)X
+3704(an)X
+3803(error)X
+3983(if)X
+776 2022(the)N
+894(movement)X
+1252(is)X
+1325(past)X
+1474(the)X
+1592(beginning)X
+1932(of)X
+2019(the)X
+2137(\256le.)X
+776 2202(The)N
+3 f
+921(<control-U>)X
+1 f
+1363(command)X
+1699(is)X
+1772(an)X
+1868(absolute)X
+2155(movement.)X
+776 2382(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(minus)X
+2061(the)X
+2179(amount)X
+2439(scrolled.)X
+776 2472(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 2562(Options:)N
+1136(None.)X
+3 f
+576 2742(<control-W>)N
+1 f
+776 2832(Switch)N
+1021(to)X
+1106(the)X
+1227(next)X
+1388(lower)X
+1594(screen)X
+1823(in)X
+1908(the)X
+2029(window,)X
+2330(or,)X
+2440(to)X
+2525(the)X
+2646(\256rst)X
+2793(screen)X
+3022(if)X
+3094(there)X
+3278(are)X
+3400(no)X
+3503(lower)X
+3709(screens)X
+3970(in)X
+776 2922(the)N
+894(window.)X
+776 3102(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(previous)X
+1754(cursor)X
+1975(position)X
+2252(in)X
+2334(the)X
+2452(window.)X
+776 3192(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(previous)X
+1754(cursor)X
+1975(position)X
+2252(in)X
+2334(the)X
+2452(window.)X
+776 3282(Options:)N
+1136(None.)X
+3 f
+576 3462(<control-Y>)N
+1 f
+776 3552(Scroll)N
+989(backward)X
+7 f
+1324(count)X
+1 f
+1586(lines,)X
+1779(leaving)X
+2037(the)X
+2157(current)X
+2407(line)X
+2550(and)X
+2689(column)X
+2952(as)X
+3042(is,)X
+3138(if)X
+3210(possible.)X
+3535(This)X
+3700(is)X
+3776(an)X
+3875(error)X
+776 3642(if)N
+845(the)X
+963(movement)X
+1321(is)X
+1394(past)X
+1543(the)X
+1661(beginning)X
+2001(of)X
+2088(the)X
+2206(\256le.)X
+776 3822(Line:)N
+1136(Unchanged)X
+1525(unless)X
+1748(the)X
+1869(current)X
+2120(line)X
+2263(scrolls)X
+2496(off)X
+2614(the)X
+2736(screen,)X
+2986(in)X
+3072(which)X
+3292(case)X
+3455(it)X
+3523(is)X
+3600(set)X
+3713(to)X
+3799(the)X
+3921(last)X
+1136 3912(line)N
+1276(of)X
+1363(text)X
+1503(displayed)X
+1830(on)X
+1930(the)X
+2048(screen.)X
+776 4002(Column:)N
+1136(Unchanged)X
+1536(unless)X
+1770(the)X
+1902(current)X
+2164(line)X
+2318(scrolls)X
+2561(off)X
+2689(the)X
+2821(screen,)X
+3081(in)X
+3177(which)X
+3407(case)X
+3580(it)X
+3658(is)X
+3745(the)X
+3877(most)X
+1136 4092(attractive)N
+1455(cursor)X
+1676(position.)X
+776 4182(Options:)N
+1136(None.)X
+3 f
+576 4362(<control-Z>)N
+1 f
+776 4452(Suspend)N
+1069(the)X
+1189(current)X
+1439(editor)X
+1648(session.)X
+1941(If)X
+2017(the)X
+2137(\256le)X
+2261(has)X
+2390(been)X
+2564(modi\256ed)X
+2870(since)X
+3057(it)X
+3124(was)X
+3272(last)X
+3406(completely)X
+3785(written,)X
+776 4542(and)N
+914(the)X
+3 f
+1034(autowrite)X
+1 f
+1386(option)X
+1612(is)X
+1687(set,)X
+1817(the)X
+1936(\256le)X
+2059(is)X
+2133(written)X
+2381(before)X
+2608(the)X
+2727(editor)X
+2935(session)X
+3187(is)X
+3261(suspended.)X
+3656(If)X
+3731(this)X
+3867(write)X
+776 4632(fails,)N
+954(the)X
+1072(editor)X
+1279(session)X
+1530(is)X
+1603(not)X
+1725(suspended.)X
+776 4812(Line:)N
+1136(Unchanged.)X
+776 4902(Column:)N
+1136(Unchanged.)X
+776 4992(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(autowrite)X
+1 f
+2006(option.)X
+3 f
+576 5172(<escape>)N
+1 f
+776 5262(Execute)N
+3 f
+1055(ex)X
+1 f
+1151(commands)X
+1518(or)X
+1605(cancel)X
+1831(partial)X
+2056(commands.)X
+2463(If)X
+2537(an)X
+3 f
+2633(ex)X
+1 f
+2729(command)X
+3065(is)X
+3138(being)X
+3336(entered)X
+3593(\(e.g.)X
+3 f
+3776(/)X
+1 f
+3798(,)X
+3 f
+3838(?)X
+1 f
+(,)S
+3 f
+3918(:)X
+1 f
+3965(or)X
+3 f
+776 5352(!)N
+1 f
+803(\),)X
+877(the)X
+1002(command)X
+1345(is)X
+1425(executed.)X
+1778(If)X
+1859(a)X
+1922(partial)X
+2154(command)X
+2497(has)X
+2631(been)X
+2810(entered,)X
+3093(e.g.)X
+3255(or)X
+3348(the)X
+3472(command)X
+3814(is)X
+3893(can-)X
+776 5442(celled.)N
+1028(Otherwise,)X
+1398(it)X
+1462(is)X
+1535(an)X
+1631(error.)X
+776 5622(Line:)N
+1136(When)X
+1350(an)X
+3 f
+1448(ex)X
+1 f
+1546(command)X
+1884(is)X
+1959(being)X
+2159(executed,)X
+2487(the)X
+2607(current)X
+2857(line)X
+2999(is)X
+3074(set)X
+3186(as)X
+3276(described)X
+3607(for)X
+3724(that)X
+3867(com-)X
+1136 5712(mand.)N
+1374(Otherwise,)X
+1744(unchanged.)X
+
+15 p
+%%Page: 15 14
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-15)X
+1 f
+776 762(Column:)N
+1136(When)X
+1354(an)X
+3 f
+1456(ex)X
+1 f
+1558(command)X
+1901(is)X
+1981(being)X
+2186(executed,)X
+2519(the)X
+2644(current)X
+2899(column)X
+3166(is)X
+3246(set)X
+3362(as)X
+3456(described)X
+3791(for)X
+3912(that)X
+1136 852(command.)N
+1512(Otherwise,)X
+1882(unchanged.)X
+776 942(Options:)N
+1136(None.)X
+3 f
+576 1122(<control-]>)N
+1 f
+776 1212(Push)N
+953(a)X
+1011(tag)X
+1131 0.4531(reference)AX
+1454(onto)X
+1618(the)X
+1738(tag)X
+1858(stack.)X
+2085(The)X
+2232(tags)X
+2383(\256les)X
+2538(\(see)X
+2691(the)X
+3 f
+2812(tags)X
+1 f
+2973(option)X
+3200(for)X
+3317(more)X
+3505(information\))X
+3933(are)X
+776 1302(searched)N
+1086(for)X
+1207(a)X
+1270(tag)X
+1395(matching)X
+1720(the)X
+1845(current)X
+2100(word.)X
+2332(The)X
+2484(current)X
+2739(word)X
+2931(begins)X
+3167(at)X
+3252(the)X
+3377(\256rst)X
+3528(non-whitespace)X
+776 1392(character)N
+1092(on)X
+1192(or)X
+1279(after)X
+1447(the)X
+1565(current)X
+1813(cursor)X
+2034(position,)X
+2331(and)X
+2468(extends)X
+2734(up)X
+2835(to)X
+2918(the)X
+3037(next)X
+3196(non-word)X
+3529(character)X
+3846(or)X
+3934(the)X
+776 1482(end)N
+914(of)X
+1003(the)X
+1123(line.)X
+1305(If)X
+1381(a)X
+1439(matching)X
+1759(tag)X
+1879(is)X
+1954(found,)X
+2183(the)X
+2302(current)X
+2551(\256le)X
+2674(is)X
+2748(discarded)X
+3077(and)X
+3214(the)X
+3333(\256le)X
+3456(containing)X
+3815(the)X
+3934(tag)X
+776 1572 0.4531(reference)AN
+1097(is)X
+1170(edited.)X
+776 1752(If)N
+851(the)X
+971(current)X
+1221(\256le)X
+1345(has)X
+1474(been)X
+1648(modi\256ed)X
+1954(since)X
+2141(it)X
+2207(was)X
+2354(last)X
+2487(completely)X
+2865(written,)X
+3134(the)X
+3254(command)X
+3592(will)X
+3738(fail.)X
+3907(The)X
+3 f
+776 1842(<control-]>)N
+1 f
+1187(command)X
+1523(is)X
+1596(an)X
+1692(absolute)X
+1979(movement.)X
+776 2022(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(matching)X
+2392(tag)X
+2510(string.)X
+776 2112(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(start)X
+1616(of)X
+1703(the)X
+1821(matching)X
+2139(tag)X
+2257(string.)X
+776 2202(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(tags)X
+1 f
+1814(and)X
+3 f
+1950(taglength)X
+1 f
+2290(options.)X
+3 f
+576 2382(<control-\303>)N
+1 f
+776 2472(Switch)N
+1018(to)X
+1100(the)X
+1218(most)X
+1393(recently)X
+1672(edited)X
+1888(\256le.)X
+776 2652(If)N
+852(the)X
+972(\256le)X
+1096(has)X
+1225(been)X
+1399(modi\256ed)X
+1705(since)X
+1892(it)X
+1958(was)X
+2105(last)X
+2238(completely)X
+2616(written,)X
+2886(and)X
+3025(the)X
+3 f
+3146(autowrite)X
+1 f
+3499(option)X
+3726(is)X
+3802(set,)X
+3934(the)X
+776 2742(\256le)N
+905(is)X
+984(written)X
+1237(out.)X
+1405(If)X
+1485(this)X
+1626(write)X
+1817(fails,)X
+2001(the)X
+2125(command)X
+2467(will)X
+2617(fail.)X
+2790(Otherwise,)X
+3166(if)X
+3241(the)X
+3365(current)X
+3619(\256le)X
+3747(has)X
+3880(been)X
+776 2832(modi\256ed)N
+1080(since)X
+1265(it)X
+1329(was)X
+1474(last)X
+1605(completely)X
+1981(written,)X
+2248(the)X
+2366(command)X
+2702(will)X
+2846(fail.)X
+776 3012(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(the)X
+1716(cursor)X
+1937(was)X
+2082(on)X
+2182(when)X
+2376(the)X
+2494(\256le)X
+2616(was)X
+2761(last)X
+2892(edited.)X
+776 3102(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(column)X
+1718(the)X
+1836(cursor)X
+2057(was)X
+2202(on)X
+2302(when)X
+2496(the)X
+2614(\256le)X
+2736(was)X
+2881(last)X
+3012(edited.)X
+776 3192(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(autowrite)X
+1 f
+2006(option.)X
+3 f
+576 3372([count])N
+841(<space>)X
+576 3462([count])N
+841(l)X
+1 f
+776 3552(Move)N
+984(the)X
+1103(cursor)X
+1325(forward)X
+7 f
+1602(count)X
+1 f
+1864(characters)X
+2213(without)X
+2479(changing)X
+2795(the)X
+2915(current)X
+3165(line.)X
+3347(This)X
+3511(is)X
+3586(an)X
+3684(error)X
+3863(if)X
+3934(the)X
+776 3642(cursor)N
+997(is)X
+1070(on)X
+1170(the)X
+1288(last)X
+1419(character)X
+1735(in)X
+1817(the)X
+1935(line.)X
+776 3822(The)N
+3 f
+921(<space>)X
+1 f
+1220(and)X
+3 f
+1356(l)X
+1 f
+1398(commands)X
+1765(may)X
+1923(be)X
+2019(used)X
+2186(as)X
+2273(the)X
+2391(motion)X
+2637(component)X
+3013(of)X
+3100(other)X
+3 f
+3285(vi)X
+1 f
+3367(commands,)X
+3754(in)X
+3836(which)X
+776 3912(case)N
+937(any)X
+1075(text)X
+1217(copied)X
+1453(into)X
+1599(a)X
+1657(buffer)X
+1876(is)X
+1951(character)X
+2269(oriented.)X
+2594(In)X
+2683(addition,)X
+2987(these)X
+3173(commands)X
+3541(may)X
+3700(be)X
+3797(used)X
+3965(as)X
+776 4002(the)N
+908(motion)X
+1168(components)X
+1589(of)X
+1690(other)X
+1889(commands)X
+2271(when)X
+2480(the)X
+2613(cursor)X
+2849(is)X
+2937(on)X
+3052(the)X
+3185(last)X
+3331(character)X
+3662(in)X
+3759(the)X
+3892(line,)X
+776 4092(without)N
+1040(error.)X
+776 4272(Line:)N
+1136(Unchanged.)X
+776 4362(Column:)N
+1136(Set)X
+1259(to)X
+1342(the)X
+1461(current)X
+1710(character)X
+2027(plus)X
+2181(the)X
+2300(next)X
+7 f
+2459(count)X
+1 f
+2720(characters,)X
+3088(or)X
+3177(to)X
+3261(the)X
+3381(last)X
+3514(character)X
+3832(on)X
+3934(the)X
+1136 4452(line)N
+1278(if)X
+7 f
+1349(count)X
+1 f
+1611(is)X
+1686(greater)X
+1932(than)X
+2092(the)X
+2212(number)X
+2478(of)X
+2566(characters)X
+2914(in)X
+2997(the)X
+3116(line)X
+3257(after)X
+3426(the)X
+3545(current)X
+3794(charac-)X
+1136 4542(ter.)N
+776 4632(Options:)N
+1136(None.)X
+3 f
+576 4812([count])N
+841(!)X
+888(motion)X
+1148(shell-argument\(s\))X
+1 f
+776 4902(Replace)N
+1055(text)X
+1195(with)X
+1357(results)X
+1586(from)X
+1762(a)X
+1818(shell)X
+1989(command.)X
+2366(Pass)X
+2529(the)X
+2648(lines)X
+2820(speci\256ed)X
+3126(by)X
+3227(the)X
+7 f
+3346(count)X
+1 f
+3607(and)X
+7 f
+3744(motion)X
+1 f
+776 4992(arguments)N
+1136(as)X
+1229(standard)X
+1527(input)X
+1717(to)X
+1805(the)X
+1928(program)X
+2225(named)X
+2464(by)X
+2569(the)X
+3 f
+2692(shell)X
+1 f
+2872(option,)X
+3121(and)X
+3262(replace)X
+3520(those)X
+3714(lines)X
+3890(with)X
+776 5082(the)N
+894(output)X
+1118(\(both)X
+1307(standard)X
+1599(error)X
+1776(and)X
+1912(standard)X
+2204(output\))X
+2455(of)X
+2542(that)X
+2682(command.)X
+776 5262(After)N
+966(the)X
+1084(motion)X
+1330(is)X
+1403(entered,)X
+3 f
+1680(vi)X
+1 f
+1762(prompts)X
+2044(for)X
+2158(arguments)X
+2512(to)X
+2594(the)X
+2712(shell)X
+2883(command.)X
+776 5442(Within)N
+1026(those)X
+1223(arguments,)X
+1605(``)X
+7 f
+1659(%)X
+1 f
+('')S
+1790(and)X
+1935(``)X
+7 f
+1989(#)X
+1 f
+('')S
+2120(characters)X
+2476(are)X
+2604(expanded)X
+2941(to)X
+3032(the)X
+3159(current)X
+3416(and)X
+3561(alternate)X
+3867(path-)X
+776 5532(names,)N
+1024(respectively.)X
+1475(The)X
+1623(``)X
+7 f
+1677(!)X
+1 f
+('')S
+1822(character)X
+2141(is)X
+2217(expanded)X
+2548(with)X
+2713(the)X
+2833(command)X
+3171(text)X
+3313(of)X
+3402(the)X
+3522(previous)X
+3 f
+3820(!)X
+1 f
+3889(or)X
+3 f
+3978(:!)X
+1 f
+776 5622(commands.)N
+1183 0.3125(\(Therefore,)AX
+1568(the)X
+1686(command)X
+3 f
+2022(!!)X
+1 f
+2116(repeats)X
+2364(the)X
+2482(previous)X
+3 f
+2778(!)X
+1 f
+2845(command.\))X
+3248(The)X
+3393(special)X
+3637(meanings)X
+3965(of)X
+776 5712(``)N
+7 f
+830(%)X
+1 f
+('',)S
+975(``)X
+7 f
+1029(#)X
+1 f
+('')S
+1154(and)X
+1293(``)X
+7 f
+1347(!)X
+1 f
+('')S
+1492(can)X
+1627(be)X
+1726(overridden)X
+2097(by)X
+2200(escaping)X
+2504(them)X
+2687(with)X
+2852(a)X
+2910(backslash.)X
+3284(If)X
+3360(no)X
+3 f
+3462(!)X
+1 f
+3531(or)X
+3 f
+3620(:!)X
+1 f
+3716(command)X
+776 5802(has)N
+907(yet)X
+1029(been)X
+1205(executed,)X
+1535(it)X
+1603(is)X
+1680(an)X
+1780(error)X
+1961(to)X
+2047(use)X
+2178(an)X
+2278(unescaped)X
+2637(``)X
+7 f
+2691(!)X
+1 f
+('')S
+2837(character.)X
+3197(The)X
+3 f
+3346(!)X
+1 f
+3417(command)X
+3758(does)X
+2 f
+3930(not)X
+
+16 p
+%%Page: 16 15
+10 s 10 xH 0 xS 2 f 1 i
+3 f
+576 474(USD:13-16)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(do)N
+880(shell)X
+1055(expansion)X
+1404(on)X
+1508(the)X
+1630(strings)X
+1867(provided)X
+2175(as)X
+2265(arguments.)X
+2662(If)X
+2739(any)X
+2878(of)X
+2968(the)X
+3089(above)X
+3304(expansions)X
+3683(change)X
+3934(the)X
+776 852(arguments)N
+1130(the)X
+1248(user)X
+1402(entered,)X
+1679(the)X
+1797(command)X
+2133(is)X
+2206(redisplayed)X
+2596(at)X
+2674(the)X
+2792(bottom)X
+3038(of)X
+3125(the)X
+3243(screen.)X
+3 f
+776 1032(Vi)N
+1 f
+882(then)X
+1046(executes)X
+1349(the)X
+1473(program)X
+1771(named)X
+2011(by)X
+2117(the)X
+3 f
+2241(shell)X
+1 f
+2422(option,)X
+2672(with)X
+2840(a)X
+3 f
+9 f
+2902(-)X
+2904(-)X
+3 f
+2948(c)X
+1 f
+3010(\257ag)X
+3156(followed)X
+3467(by)X
+3573(the)X
+3698(arguments)X
+776 1122(\(which)N
+1019(are)X
+1138(bundled)X
+1416(into)X
+1560(a)X
+1616(single)X
+1827(argument\).)X
+776 1302(The)N
+3 f
+921(!)X
+1 f
+988(command)X
+1324(is)X
+1397(permitted)X
+1724(in)X
+1806(an)X
+1902(empty)X
+2122(\256le.)X
+776 1482(If)N
+850(the)X
+968(\256le)X
+1090(has)X
+1217(been)X
+1389(modi\256ed)X
+1693(since)X
+1878(it)X
+1942(was)X
+2087(last)X
+2218(completely)X
+2594(written,)X
+2861(the)X
+3 f
+2979(!)X
+1 f
+3046(command)X
+3382(will)X
+3526(warn)X
+3707(you.)X
+776 1662(Line:)N
+1136(The)X
+1281(\256rst)X
+1425(line)X
+1565(of)X
+1652(the)X
+1770(replaced)X
+2063(text.)X
+776 1752(Column:)N
+1136(The)X
+1281(\256rst)X
+1425(column)X
+1685(of)X
+1772(the)X
+1890(replaced)X
+2183(text.)X
+776 1842(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(shell)X
+1 f
+1831(option.)X
+3 f
+576 2022([count])N
+841(#)X
+901(+|-|#)X
+1 f
+776 2112(Increment)N
+1135(or)X
+1235(decrement)X
+1603(the)X
+1734(current)X
+1996(number.)X
+2315(The)X
+2474(current)X
+2736(number)X
+3015(begins)X
+3258(at)X
+3350(the)X
+3482(\256rst)X
+3640(non-number)X
+776 2202(character)N
+1095(on)X
+1198(or)X
+1288(before)X
+1517(the)X
+1638(current)X
+1889(cursor)X
+2113(position,)X
+2413(or)X
+2503(the)X
+2624(beginning)X
+2967(of)X
+3057(the)X
+3178(line,)X
+3341(and)X
+3480(extends)X
+3748(up)X
+3850(to)X
+3934(the)X
+776 2292(\256rst)N
+920(non-number)X
+1332(character)X
+1649(on)X
+1750(or)X
+1838(after)X
+2007(the)X
+2126(current)X
+2375(cursor)X
+2597(position)X
+2875(or)X
+2963(the)X
+3082(end)X
+3219(of)X
+3307(the)X
+3426(line.)X
+3607(If)X
+3682(the)X
+3801(trailing)X
+776 2382(character)N
+1094(is)X
+1169(a)X
+7 f
+1227(+)X
+1 f
+(,)S
+1317(the)X
+1437(number)X
+1704(is)X
+1779(incremented)X
+2198(by)X
+7 f
+2300(count)X
+1 f
+(.)S
+2602(If)X
+2678(the)X
+2798(trailing)X
+3051(character)X
+3369(is)X
+3444(a)X
+7 f
+3502(-)X
+1 f
+(,)S
+3592(the)X
+3712(number)X
+3979(is)X
+776 2472(decremented)N
+1220(by)X
+7 f
+1333(count)X
+1 f
+(.)S
+1646(If)X
+1733(the)X
+1864(trailing)X
+2128(character)X
+2457(is)X
+2543(a)X
+7 f
+2612(#)X
+1 f
+(,)S
+2713(the)X
+2844(previous)X
+3154(increment)X
+3509(or)X
+3610(decrement)X
+3979(is)X
+776 2562(repeated.)N
+776 2742(The)N
+927(format)X
+1167(of)X
+1260(the)X
+1384(number)X
+1656(\(decimal,)X
+1984(hexadecimal,)X
+2437(and)X
+2580(octal,)X
+2783(and)X
+2926(leading)X
+3189(0's\))X
+3341(is)X
+3421(retained)X
+3707(unless)X
+3934(the)X
+776 2832(new)N
+930(value)X
+1124(cannot)X
+1358(be)X
+1454(represented)X
+1845(in)X
+1927(the)X
+2045(previous)X
+2341(format.)X
+776 3012(Line:)N
+1136(Unchanged.)X
+776 3102(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(in)X
+2000(the)X
+2118(cursor)X
+2339(word.)X
+776 3192(Options:)N
+1136(None.)X
+3 f
+576 3372([count])N
+841($)X
+1 f
+776 3462(Move)N
+989(the)X
+1113(cursor)X
+1340(to)X
+1428(the)X
+1552(end)X
+1694(of)X
+1787(a)X
+1849(line.)X
+2035(If)X
+7 f
+2115(count)X
+1 f
+2381(is)X
+2460(speci\256ed,)X
+2792(the)X
+2917(cursor)X
+3145(moves)X
+3381(down)X
+7 f
+3586(count)X
+3881(-)X
+3984(1)X
+1 f
+776 3552(lines.)N
+776 3732(It)N
+845(is)X
+918(not)X
+1040(an)X
+1136(error)X
+1313(to)X
+1395(use)X
+1522(the)X
+3 f
+1640($)X
+1 f
+1700(command)X
+2036(when)X
+2230(the)X
+2348(cursor)X
+2569(is)X
+2642(on)X
+2742(the)X
+2860(last)X
+2991(character)X
+3308(in)X
+3391(the)X
+3510(line)X
+3651(or)X
+3739(when)X
+3934(the)X
+776 3822(line)N
+916(is)X
+989(empty.)X
+776 4002(The)N
+3 f
+921($)X
+1 f
+981(command)X
+1317(may)X
+1475(be)X
+1571(used)X
+1739(as)X
+1827(the)X
+1946(motion)X
+2193(component)X
+2570(of)X
+2658(other)X
+3 f
+2844(vi)X
+1 f
+2927(commands,)X
+3315(in)X
+3398(which)X
+3615(case)X
+3775(any)X
+3912(text)X
+776 4092(copied)N
+1014(into)X
+1161(a)X
+1220(buffer)X
+1440(is)X
+1516(character)X
+1835(oriented,)X
+2141(unless)X
+2364(the)X
+2485(cursor)X
+2709(is)X
+2785(at,)X
+2886(or)X
+2976(before)X
+3205(the)X
+3326(\256rst)X
+3473(nonblank)X
+3794(charac-)X
+776 4182(ter)N
+885(in)X
+971(the)X
+1093(line,)X
+1257(in)X
+1343(which)X
+1563(case)X
+1726(it)X
+1794(is)X
+1871(line)X
+2015(oriented.)X
+2343(It)X
+2417(is)X
+2495(not)X
+2622(an)X
+2723(error)X
+2905(to)X
+2992(use)X
+3124(the)X
+3 f
+3247($)X
+1 f
+3312(command)X
+3653(as)X
+3745(a)X
+3806(motion)X
+776 4272(component)N
+1154(when)X
+1350(the)X
+1470(cursor)X
+1693(is)X
+1768(on)X
+1870(the)X
+1990(last)X
+2123(character)X
+2441(in)X
+2525(the)X
+2645(line,)X
+2807(although)X
+3109(it)X
+3175(is)X
+3249(an)X
+3346(error)X
+3524(when)X
+3719(the)X
+3838(line)X
+3979(is)X
+776 4362(empty.)N
+776 4542(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(plus)X
+7 f
+1999(count)X
+1 f
+2259(minus)X
+2474(1.)X
+776 4632(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(in)X
+1987(the)X
+2105(line.)X
+776 4722(Options:)N
+1136(None.)X
+3 f
+576 4902(%)N
+1 f
+776 4992(Move)N
+986(to)X
+1071(the)X
+1192(matching)X
+1513(character.)X
+1872(The)X
+2020(cursor)X
+2244(moves)X
+2477(to)X
+2563(the)X
+2685(parenthesis)X
+3070(or)X
+3161(curly)X
+3350(brace)X
+3549(which)X
+2 f
+3769(matches)X
+1 f
+776 5082(the)N
+900(parenthesis)X
+1287(or)X
+1380(curly)X
+1570(brace)X
+1770(found)X
+1982(at)X
+2065(the)X
+2188(current)X
+2441(cursor)X
+2667(position)X
+2949(or)X
+3041(which)X
+3262(is)X
+3340(the)X
+3463(closest)X
+3706(one)X
+3847(to)X
+3934(the)X
+776 5172(right)N
+964(of)X
+1068(the)X
+1203(cursor)X
+1441(on)X
+1558(the)X
+1693(line.)X
+1890(It)X
+1976(is)X
+2066(an)X
+2179(error)X
+2373(to)X
+2472(execute)X
+2756(the)X
+3 f
+2892(%)X
+1 f
+3010(command)X
+3364(on)X
+3482(a)X
+3556(line)X
+3714(without)X
+3996(a)X
+776 5262(parenthesis)N
+1157(or)X
+1244(curly)X
+1429(brace.)X
+1664(Historically,)X
+2082(any)X
+7 f
+2218(count)X
+1 f
+2478(speci\256ed)X
+2783(to)X
+2865(the)X
+3 f
+2983(%)X
+1 f
+3083(command)X
+3419(was)X
+3564(ignored.)X
+776 5442(The)N
+3 f
+921(%)X
+1 f
+1021(command)X
+1357(is)X
+1430(an)X
+1527(absolute)X
+1815(movement.)X
+2214(The)X
+3 f
+2360(%)X
+1 f
+2461(command)X
+2798(may)X
+2957(be)X
+3054(used)X
+3222(as)X
+3310(the)X
+3429(motion)X
+3676(component)X
+776 5532(of)N
+871(other)X
+3 f
+1064(vi)X
+1 f
+1153(commands,)X
+1547(in)X
+1636(which)X
+1859(case)X
+2025(any)X
+2168(text)X
+2315(copied)X
+2556(into)X
+2707(a)X
+2770(buffer)X
+2994(is)X
+3074(character)X
+3397(oriented,)X
+3707(unless)X
+3934(the)X
+776 5622(starting)N
+1036(point)X
+1220(of)X
+1307(the)X
+1425(region)X
+1650(is)X
+1723(at)X
+1801(or)X
+1888(before)X
+2115(the)X
+2234(\256rst)X
+2379(nonblank)X
+2698(character)X
+3015(on)X
+3116(its)X
+3212(line,)X
+3373(and)X
+3510(the)X
+3629(ending)X
+3868(point)X
+776 5712(is)N
+849(at)X
+927(or)X
+1014(after)X
+1182(the)X
+1300(last)X
+1431(nonblank)X
+1749(character)X
+2065(on)X
+2165(its)X
+2260(line,)X
+2420(in)X
+2502(which)X
+2718(case)X
+2877(it)X
+2941(is)X
+3014(line)X
+3154(oriented.)X
+
+17 p
+%%Page: 17 16
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-17)X
+1 f
+776 762(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(matching)X
+2392(character.)X
+776 852(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(matching)X
+1776(character.)X
+776 942(Options:)N
+1136(None.)X
+3 f
+576 1122(&)N
+1 f
+776 1212(Repeat)N
+1019(the)X
+1137(previous)X
+1433(substitution)X
+1825(command)X
+2161(on)X
+2261(the)X
+2379(current)X
+2627(line.)X
+776 1392(Historically,)N
+1194(any)X
+7 f
+1330(count)X
+1 f
+1590(speci\256ed)X
+1895(to)X
+1977(the)X
+3 f
+2095(&)X
+1 f
+2182(command)X
+2518(was)X
+2663(ignored.)X
+776 1572(Line:)N
+1136(Unchanged.)X
+776 1662(Column:)N
+1136(Unchanged)X
+1531(if)X
+1609(the)X
+1736(cursor)X
+1966(was)X
+2120(on)X
+2229(the)X
+2356(last)X
+2496(character)X
+2821(in)X
+2912(the)X
+3039(line,)X
+3208(otherwise,)X
+3569(set)X
+3688(to)X
+3780(the)X
+3908(\256rst)X
+1136 1752(nonblank)N
+1454(character)X
+1770(in)X
+1852(the)X
+1970(line.)X
+776 1842(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(edcompatible)X
+1 f
+2114(,)X
+3 f
+2154(extended)X
+1 f
+2461(,)X
+3 f
+2501(ignorecase)X
+1 f
+2882(and)X
+3 f
+3018(magic)X
+1 f
+3243(options.)X
+3 f
+576 2022 0.3182(\302<character>)AN
+576 2112 0.3182(`<character>)AN
+1 f
+776 2202(Return)N
+1014(to)X
+1096(a)X
+1152(context)X
+1408(marked)X
+1669(by)X
+1769(the)X
+1888(character)X
+7 f
+2205(<character>)X
+1 f
+(.)S
+2794(If)X
+7 f
+2869(<character>)X
+1 f
+3418(is)X
+3492(the)X
+3611(``)X
+7 f
+3665(')X
+1 f
+('')S
+3788(or)X
+3876(``)X
+7 f
+3930(`)X
+1 f
+('')S
+776 2292(character,)N
+1113(return)X
+1326(to)X
+1409(the)X
+1528(previous)X
+1825(context.)X
+2122(If)X
+7 f
+2197(<character>)X
+1 f
+2746(is)X
+2820(any)X
+2956(other)X
+3141(character,)X
+3477(return)X
+3689(to)X
+3771(the)X
+3889(con-)X
+776 2382(text)N
+921(marked)X
+1187(by)X
+1292(that)X
+1437(character)X
+1759(\(see)X
+1915(the)X
+3 f
+2039(m)X
+1 f
+2132(command)X
+2474(for)X
+2594(more)X
+2785(information\).)X
+3256(If)X
+3336(the)X
+3460(command)X
+3802(is)X
+3881(the)X
+3 f
+4005(\302)X
+1 f
+776 2472(command,)N
+1137(only)X
+1304(the)X
+1427(line)X
+1572(value)X
+1771(is)X
+1848(restored,)X
+2151(and)X
+2291(the)X
+2413(cursor)X
+2638(is)X
+2715(placed)X
+2949(on)X
+3053(the)X
+3175(\256rst)X
+3323(nonblank)X
+3645(character)X
+3965(of)X
+776 2562(that)N
+916(line.)X
+1096(If)X
+1170(the)X
+1288(command)X
+1624(is)X
+1697(the)X
+3 f
+1815(`)X
+1 f
+1862(command,)X
+2218(both)X
+2380(the)X
+2498(line)X
+2638(and)X
+2774(column)X
+3034(values)X
+3259(are)X
+3378(restored.)X
+776 2742(It)N
+849(is)X
+926(an)X
+1026(error)X
+1207(if)X
+1280(the)X
+1402(context)X
+1662(no)X
+1766(longer)X
+1995(exists)X
+2202(because)X
+2482(of)X
+2574(line)X
+2719(deletion.)X
+3042(\(Contexts)X
+3378(follow)X
+3612(lines)X
+3788(that)X
+3933(are)X
+776 2832(moved,)N
+1034(or)X
+1121(which)X
+1337(are)X
+1456(deleted)X
+1708(and)X
+1844(then)X
+2002(restored.\))X
+776 3012(The)N
+3 f
+924(\302)X
+1 f
+974(and)X
+3 f
+1113(`)X
+1 f
+1163(commands)X
+1533(are)X
+1655(both)X
+1820(absolute)X
+2110(movements.)X
+2542(They)X
+2730(may)X
+2891(be)X
+2990(used)X
+3160(as)X
+3250(a)X
+3309(motion)X
+3558(component)X
+3938(for)X
+776 3102(other)N
+3 f
+962(vi)X
+1 f
+1045(commands.)X
+1453(For)X
+1585(the)X
+3 f
+1704(\302)X
+1 f
+1752(command,)X
+2108(any)X
+2244(text)X
+2384(copied)X
+2618(into)X
+2762(a)X
+2818(buffer)X
+3035(is)X
+3108(line)X
+3248(oriented.)X
+3571(For)X
+3702(the)X
+3 f
+3820(`)X
+1 f
+3867(com-)X
+776 3192(mand,)N
+1002(any)X
+1146(text)X
+1294(copied)X
+1536(into)X
+1688(a)X
+1752(buffer)X
+1977(is)X
+2058(character)X
+2382(oriented,)X
+2693(unless)X
+2921(it)X
+2993(both)X
+3163(starts)X
+3360(and)X
+3504(stops)X
+3696(at)X
+3782(the)X
+3908(\256rst)X
+776 3282(character)N
+1099(in)X
+1188(the)X
+1313(line,)X
+1480(in)X
+1569(which)X
+1792(case)X
+1958(it)X
+2029(is)X
+2109(line)X
+2255(oriented.)X
+2584(In)X
+2677(addition,)X
+2985(when)X
+3185(using)X
+3384(the)X
+3 f
+3508(`)X
+1 f
+3561(command)X
+3903(as)X
+3996(a)X
+776 3372(motion)N
+1025(component,)X
+1424(commands)X
+1794(which)X
+2013(move)X
+2214(backward)X
+2550(and)X
+2689(started)X
+2926(at)X
+3007(the)X
+3128(\256rst)X
+3275(character)X
+3594(in)X
+3679(the)X
+3801(line,)X
+3965(or)X
+776 3462(move)N
+983(forward)X
+1267(and)X
+1412(ended)X
+1633(at)X
+1719(the)X
+1845(\256rst)X
+1997(character)X
+2321(in)X
+2411(the)X
+2537(line,)X
+2705(are)X
+2832(corrected)X
+3160(to)X
+3250(the)X
+3376(last)X
+3515(character)X
+3839(of)X
+3934(the)X
+776 3552(starting)N
+1036(and)X
+1172(ending)X
+1410(lines,)X
+1601(respectively.)X
+776 3732(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(from)X
+1774(the)X
+1892(context.)X
+776 3822(Column:)N
+1136(Set)X
+1261(to)X
+1346(the)X
+1467(\256rst)X
+1614(nonblank)X
+1935(character)X
+2254(in)X
+2340(the)X
+2462(line,)X
+2626(for)X
+2744(the)X
+3 f
+2866(\302)X
+1 f
+2917(command,)X
+3277(and)X
+3417(set)X
+3530(to)X
+3616(the)X
+3738(context's)X
+1136 3912(column)N
+1396(for)X
+1510(the)X
+3 f
+1628(`)X
+1 f
+1675(command.)X
+776 4002(Options:)N
+1136(None.)X
+3 f
+576 4182([count])N
+841(\()X
+1 f
+776 4272(Back)N
+961(up)X
+7 f
+1061(count)X
+1 f
+1321(sentences.)X
+776 4452(The)N
+3 f
+922(\()X
+1 f
+970(command)X
+1307(is)X
+1382(an)X
+1480(absolute)X
+1769(movement.)X
+2169(The)X
+3 f
+2316(\()X
+1 f
+2365(command)X
+2703(may)X
+2863(be)X
+2961(used)X
+3130(as)X
+3219(the)X
+3339(motion)X
+3587(component)X
+3965(of)X
+776 4542(other)N
+3 f
+963(vi)X
+1 f
+1047(commands,)X
+1436(in)X
+1520(which)X
+1738(case)X
+1899(any)X
+2036(text)X
+2177(copied)X
+2412(into)X
+2557(a)X
+2614(buffer)X
+2832(is)X
+2906(character)X
+3223(oriented,)X
+3527(unless)X
+3748(the)X
+3867(start-)X
+776 4632(ing)N
+913(and)X
+1064(stopping)X
+1374(points)X
+1604(of)X
+1706(the)X
+1839(region)X
+2079(are)X
+2213(the)X
+2346(\256rst)X
+2505(character)X
+2836(in)X
+2933(the)X
+3066(line,)X
+3241(in)X
+3338(which)X
+3569(case)X
+3743(it)X
+3823(is)X
+3912(line)X
+776 4722(oriented.)N
+1111(In)X
+1210(the)X
+1340(latter)X
+1537(case,)X
+1728(the)X
+1858(stopping)X
+2165(point)X
+2361(of)X
+2460(the)X
+2590(region)X
+2827(is)X
+2911(adjusted)X
+3209(to)X
+3302(be)X
+3409(the)X
+3538(end)X
+3685(of)X
+3783(the)X
+3912(line)X
+776 4812(immediately)N
+1196(before)X
+1422(it,)X
+1506(and)X
+1642(not)X
+1764(the)X
+1882(original)X
+2151(cursor)X
+2372(position.)X
+776 4992(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(beginning)X
+2414(of)X
+2501(the)X
+2619(sentence.)X
+776 5082(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(sentence.)X
+776 5172(Options:)N
+1136(None.)X
+3 f
+576 5352([count])N
+841(\))X
+1 f
+776 5442(Move)N
+983(forward)X
+7 f
+1258(count)X
+1 f
+1518(sentences.)X
+776 5622(The)N
+3 f
+922(\))X
+1 f
+970(command)X
+1307(is)X
+1382(an)X
+1480(absolute)X
+1769(movement.)X
+2169(The)X
+3 f
+2316(\))X
+1 f
+2365(command)X
+2703(may)X
+2863(be)X
+2961(used)X
+3130(as)X
+3219(the)X
+3339(motion)X
+3587(component)X
+3965(of)X
+776 5712(other)N
+3 f
+963(vi)X
+1 f
+1047(commands,)X
+1436(in)X
+1520(which)X
+1738(case)X
+1899(any)X
+2036(text)X
+2177(copied)X
+2412(into)X
+2557(a)X
+2614(buffer)X
+2832(is)X
+2906(character)X
+3223(oriented,)X
+3527(unless)X
+3748(the)X
+3867(start-)X
+776 5802(ing)N
+901(point)X
+1088(of)X
+1179(the)X
+1301(region)X
+1530(is)X
+1607(the)X
+1729(\256rst)X
+1877(character)X
+2197(in)X
+2283(the)X
+2405(line,)X
+2569(in)X
+2655(which)X
+2875(case)X
+3038(it)X
+3106(is)X
+3183(line)X
+3327(oriented.)X
+3654(In)X
+3745(the)X
+3867(latter)X
+
+18 p
+%%Page: 18 17
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-18)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(case,)N
+961(if)X
+1036(the)X
+1160(stopping)X
+1461(point)X
+1651(of)X
+1744(the)X
+1868(region)X
+2099(is)X
+2178(also)X
+2333(the)X
+2457(\256rst)X
+2607(character)X
+2929(in)X
+3017(the)X
+3141(line,)X
+3307(it)X
+3376(is)X
+3454(adjusted)X
+3746(to)X
+3833(be)X
+3934(the)X
+776 852(end)N
+912(of)X
+999(the)X
+1117(line)X
+1257(immediately)X
+1677(before)X
+1903(it.)X
+776 1032(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(beginning)X
+2414(of)X
+2501(the)X
+2619(sentence.)X
+776 1122(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(sentence.)X
+776 1212(Options:)N
+1136(None.)X
+3 f
+576 1392([count])N
+841(,)X
+1 f
+776 1482(Reverse)N
+1055(\256nd)X
+1199(character)X
+7 f
+1515(count)X
+1 f
+1775(times.)X
+2008(Reverse)X
+2288(the)X
+2407(last)X
+3 f
+2539(F)X
+1 f
+2588(,)X
+3 f
+2629(f)X
+1 f
+2656(,)X
+3 f
+2697(T)X
+1 f
+2771(or)X
+3 f
+2859(t)X
+1 f
+2907(command,)X
+3264(searching)X
+3593(the)X
+3712(other)X
+3898(way)X
+776 1572(in)N
+858(the)X
+976(line,)X
+7 f
+1136(count)X
+1 f
+1396(times.)X
+776 1752(The)N
+3 f
+922(,)X
+1 f
+963(command)X
+1300(may)X
+1460(be)X
+1558(used)X
+1727(as)X
+1816(the)X
+1936(motion)X
+2184(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 1842(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 2022(Line:)N
+1136(Unchanged.)X
+776 2112(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458 0.3068(searched-for)AX
+1881(character.)X
+776 2202(Options:)N
+1136(None.)X
+3 f
+576 2382([count])N
+9 f
+841(-)X
+843(-)X
+1 f
+776 2472(Move)N
+983(to)X
+1065(\256rst)X
+1209(nonblank)X
+1527(of)X
+1614(the)X
+1732(previous)X
+2028(line,)X
+7 f
+2188(count)X
+1 f
+2448(times.)X
+776 2652(This)N
+938(is)X
+1011(an)X
+1107(error)X
+1284(if)X
+1353(the)X
+1471(movement)X
+1829(is)X
+1902(past)X
+2051(the)X
+2169(beginning)X
+2509(of)X
+2596(the)X
+2714(\256le.)X
+776 2832(The)N
+3 f
+922(-)X
+1 f
+970(command)X
+1307(may)X
+1466(be)X
+1563(used)X
+1731(as)X
+1819(the)X
+1938(motion)X
+2185(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 2922(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(line)X
+1640(oriented.)X
+776 3102(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(line)X
+1846(minus)X
+7 f
+2061(count)X
+1 f
+(.)S
+776 3192(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 3282(Options:)N
+1136(None.)X
+3 f
+576 3462([count])N
+841(.)X
+1 f
+776 3552(Repeat)N
+1031(the)X
+1161(last)X
+3 f
+1304(vi)X
+1 f
+1398(command)X
+1746(that)X
+1898(modi\256ed)X
+2214(text.)X
+2406(The)X
+2563(repeated)X
+2869(command)X
+3218(may)X
+3389(be)X
+3498(a)X
+3567(command)X
+3916(and)X
+776 3642(motion)N
+1030(component)X
+1413(combination.)X
+1880(If)X
+7 f
+1961(count)X
+1 f
+2228(is)X
+2308(speci\256ed,)X
+2640(it)X
+2711(replaces)X
+2 f
+3002(both)X
+1 f
+3171(the)X
+3296(count)X
+3501(speci\256ed)X
+3813(for)X
+3934(the)X
+776 3732(repeated)N
+1093(command,)X
+1473(and,)X
+1653(if)X
+1746(applicable,)X
+2140(for)X
+2278(the)X
+2420(repeated)X
+2737(motion)X
+3007(component.)X
+3448(If)X
+7 f
+3547(count)X
+1 f
+3832(is)X
+3930(not)X
+776 3822(speci\256ed,)N
+1101(the)X
+1219(counts)X
+1448(originally)X
+1779(speci\256ed)X
+2084(to)X
+2166(the)X
+2284(command)X
+2620(being)X
+2818(repeated)X
+3111(are)X
+3230(used)X
+3397(again.)X
+776 4002(As)N
+887(a)X
+945(special)X
+1190(case,)X
+1371(if)X
+1442(the)X
+3 f
+1562(.)X
+1 f
+1624(command)X
+1962(is)X
+2037(executed)X
+2345(immediately)X
+2767(after)X
+2937(the)X
+3 f
+3057(u)X
+1 f
+3123(command,)X
+3482(the)X
+3603(change)X
+3854(log)X
+3979(is)X
+776 4092(rolled)N
+983(forward)X
+1258(or)X
+1345(backward,)X
+1698(depending)X
+2052(on)X
+2152(the)X
+2270(action)X
+2486(of)X
+2573(the)X
+3 f
+2691(u)X
+1 f
+2755(command.)X
+776 4272(Line:)N
+1136(Set)X
+1258(as)X
+1345(described)X
+1673(for)X
+1787(the)X
+1905(repeated)X
+2198(command.)X
+776 4362(Column:)N
+1136(Set)X
+1258(as)X
+1345(described)X
+1673(for)X
+1787(the)X
+1905(repeated)X
+2198(command.)X
+776 4452(Options:)N
+1136(None.)X
+3 f
+576 4632 0.1776(/RE<carriage-return>)AN
+576 4722(/RE/)N
+751 0.2500([offset]<carriage-return>)AX
+576 4812 0.1908(?RE<carriage-return>)AN
+576 4902(?RE?)N
+787 0.2500([offset]<carriage-return>)AX
+576 4992(N)N
+576 5082(n)N
+1 f
+776 5172(Search)N
+1017(forward)X
+1295(or)X
+1385(backward)X
+1721(for)X
+1838(a)X
+1897(regular)X
+2148(expression.)X
+2554(The)X
+2702(commands)X
+3072(beginning)X
+3415(with)X
+3580(a)X
+3639(slash)X
+3822(\(``)X
+7 f
+3903(/)X
+1 f
+(''\))S
+776 5262(character)N
+1095(are)X
+1217(forward)X
+1495(searches,)X
+1811(the)X
+1932(commands)X
+2302(beginning)X
+2644(with)X
+2808(a)X
+2866(question)X
+3159(mark)X
+3346(\(``)X
+7 f
+3427(?)X
+1 f
+(''\))S
+3598(are)X
+3719(backward)X
+776 5352(searches.)N
+3 f
+1117(Vi)X
+1 f
+1225(prompts)X
+1515(with)X
+1685(the)X
+1811(leading)X
+2075(character)X
+2399(on)X
+2507(the)X
+2633(last)X
+2772(line)X
+2920(of)X
+3015(the)X
+3142(screen)X
+3377(for)X
+3500(a)X
+3565(string.)X
+3816(It)X
+3894(then)X
+776 5442(searches)N
+1071(forward)X
+1348(or)X
+1437(backward)X
+1772(in)X
+1856(the)X
+1976(\256le)X
+2100(for)X
+2216(the)X
+2336(next)X
+2496 0.3611(occurrence)AX
+2872(of)X
+2961(the)X
+3081(string,)X
+3305(which)X
+3522(is)X
+3596(interpreted)X
+3965(as)X
+776 5532(a)N
+832(Basic)X
+1030(Regular)X
+1304(Expression.)X
+776 5712(The)N
+3 f
+928(/)X
+1 f
+977(and)X
+3 f
+1120(?)X
+1 f
+1207(commands)X
+1581(are)X
+1707(absolute)X
+2001(movements.)X
+2437(They)X
+2630(may)X
+2796(be)X
+2900(used)X
+3075(as)X
+3170(the)X
+3296(motion)X
+3550(components)X
+3965(of)X
+776 5802(other)N
+3 f
+974(vi)X
+1 f
+1069(commands,)X
+1469(in)X
+1564(which)X
+1793(case)X
+1965(any)X
+2114(text)X
+2267(copied)X
+2514(into)X
+2671(a)X
+2740(buffer)X
+2970(is)X
+3056(character)X
+3385(oriented,)X
+3701(unless)X
+3934(the)X
+
+19 p
+%%Page: 19 18
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-19)X
+1 f
+776 762(search)N
+1007(started)X
+1246(and)X
+1387(ended)X
+1604(on)X
+1709(the)X
+1832(\256rst)X
+1981(column)X
+2246(of)X
+2338(a)X
+2399(line,)X
+2564(in)X
+2651(which)X
+2872(case)X
+3036(it)X
+3105(is)X
+3183(line)X
+3328(oriented.)X
+3657(In)X
+3750(addition,)X
+776 852(forward)N
+1056(searches)X
+1354(ending)X
+1597(at)X
+1680(the)X
+1803(\256rst)X
+1951(character)X
+2271(of)X
+2362(a)X
+2422(line,)X
+2586(and)X
+2726(backward)X
+3063(searches)X
+3360(beginning)X
+3704(at)X
+3786(the)X
+3908(\256rst)X
+776 942(character)N
+1098(in)X
+1186(the)X
+1310(line,)X
+1476(are)X
+1601(corrected)X
+1927(to)X
+2015(begin)X
+2219(or)X
+2312(end)X
+2454(at)X
+2538(the)X
+2662(last)X
+2799(character)X
+3121(of)X
+3214(the)X
+3339(previous)X
+3642(line.)X
+3829(\(Note,)X
+776 1032(forward)N
+1061(and)X
+1207(backward)X
+1550(searches)X
+1853(can)X
+1995(occur)X
+2204(for)X
+2328(both)X
+3 f
+2500(/)X
+1 f
+2552(and)X
+3 f
+2698(?)X
+1 f
+2787(commands,)X
+3183(if)X
+3261(the)X
+3 f
+3388(wrapscan)X
+1 f
+3746(option)X
+3979(is)X
+776 1122(set.\))N
+776 1302(If)N
+852(an)X
+950(offset)X
+1155(from)X
+1333(the)X
+1453(matched)X
+1747(line)X
+1889(is)X
+1964(speci\256ed)X
+2271(\(i.e.)X
+2418(a)X
+2476(trailing)X
+2729(``)X
+7 f
+2783(/)X
+1 f
+('')S
+2907(or)X
+2996(``)X
+7 f
+3050(?)X
+1 f
+('')S
+3194(character)X
+3512(is)X
+3587(followed)X
+3894(by)X
+3996(a)X
+776 1392(signed)N
+1009(offset\),)X
+1263(the)X
+1385(buffer)X
+1606(will)X
+1754(always)X
+2001(be)X
+2100(line)X
+2243(oriented)X
+2529(\(e.g.)X
+2715(``)X
+7 f
+2769(/string/+0)X
+1 f
+('')S
+3326(will)X
+3473(always)X
+3719(guarantee)X
+776 1482(a)N
+832(line)X
+972(orientation\).)X
+776 1662(The)N
+3 f
+921(n)X
+1 f
+985(command)X
+1321(repeats)X
+1569(the)X
+1687(previous)X
+1983(search.)X
+776 1842(The)N
+3 f
+921(N)X
+1 f
+999(command)X
+1335(repeats)X
+1583(the)X
+1701(previous)X
+1997(search,)X
+2243(but)X
+2365(in)X
+2447(the)X
+2565(reverse)X
+2818(direction.)X
+776 2022(Missing)N
+1194(RE's)X
+1515(\(e.g.)X
+1839(``)X
+7 f
+1893(//<carriage-return>)X
+1 f
+('',)S
+3040(``)X
+7 f
+3094(/<carriage-return>)X
+1 f
+('',)S
+776 2112(``)N
+7 f
+830(??<carriage-return>)X
+1 f
+('',)S
+1847(or)X
+1945(``)X
+7 f
+1999(?<carriage-return>)X
+1 f
+('')S
+2948(search)X
+3185(for)X
+3310(the)X
+3439(last)X
+3581(search)X
+3818(RE,)X
+3970(in)X
+776 2202(the)N
+894(indicated)X
+1208(direction.)X
+776 2382(Searches)N
+1082(may)X
+1240(be)X
+1336(interrupted)X
+1708(using)X
+1901(the)X
+7 f
+2019(<interrupt>)X
+1 f
+2567(character.)X
+776 2562(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(in)X
+1680(which)X
+1896(the)X
+2014(match)X
+2230(occurred.)X
+776 2652(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(of)X
+2005(the)X
+2123(matched)X
+2415(string.)X
+776 2742(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(edcompatible)X
+1 f
+2114(,)X
+3 f
+2154(extended)X
+1 f
+2461(,)X
+3 f
+2501(ignorecase)X
+1 f
+2862(,)X
+3 f
+2902(magic)X
+1 f
+3107(,)X
+3147(and)X
+3 f
+3283(wrapscan)X
+1 f
+3632(options.)X
+3 f
+576 2922(0)N
+1 f
+776 3012(Move)N
+985(to)X
+1069(the)X
+1189(\256rst)X
+1335(character)X
+1654(in)X
+1739(the)X
+1860(current)X
+2111(line.)X
+2294(It)X
+2366(is)X
+2442(not)X
+2567(an)X
+2666(error)X
+2846(to)X
+2931(use)X
+3061(the)X
+3 f
+3182(0)X
+1 f
+3245(command)X
+3584(when)X
+3781(the)X
+3902(cur-)X
+776 3102(sor)N
+894(is)X
+967(on)X
+1067(the)X
+1185(\256rst)X
+1329(character)X
+1645(in)X
+1727(the)X
+1845(line,)X
+776 3282(The)N
+3 f
+923(0)X
+1 f
+986(command)X
+1325(may)X
+1486(be)X
+1585(used)X
+1755(as)X
+1845(the)X
+1966(motion)X
+2215(component)X
+2594(of)X
+2684(other)X
+3 f
+2872(vi)X
+1 f
+2957(commands,)X
+3347(in)X
+3432(which)X
+3651(case)X
+3813(it)X
+3880(is)X
+3956(an)X
+776 3372(error)N
+953(if)X
+1022(the)X
+1140(cursor)X
+1361(is)X
+1434(on)X
+1534(the)X
+1652(\256rst)X
+1796(character)X
+2112(in)X
+2194(the)X
+2312(line.)X
+776 3552(Line:)N
+1136(Unchanged.)X
+776 3642(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(in)X
+2000(the)X
+2118(line.)X
+776 3732(Options:)N
+1136(None.)X
+3 f
+576 3912(:)N
+1 f
+776 4002(Execute)N
+1055(an)X
+1152(ex)X
+1249(command.)X
+3 f
+1626(Vi)X
+1 f
+1727(prompts)X
+2010(for)X
+2125(an)X
+3 f
+2222(ex)X
+1 f
+2319(command)X
+2656(on)X
+2757(the)X
+2876(last)X
+3008(line)X
+3149(of)X
+3237(the)X
+3356(screen,)X
+3603(using)X
+3797(a)X
+3854(colon)X
+776 4092(\(``)N
+7 f
+857(:)X
+1 f
+(''\))S
+1029(character.)X
+1408(The)X
+1575(command)X
+1933(is)X
+2028(terminated)X
+2413(by)X
+2535(a)X
+7 f
+2613(<carriage-return>)X
+1 f
+(,)S
+7 f
+3491(<newline>)X
+1 f
+3965(or)X
+7 f
+776 4182(<escape>)N
+1 f
+1184(character;)X
+1526(all)X
+1630(of)X
+1721(these)X
+1910(characters)X
+2261(may)X
+2423(be)X
+2523(escaped)X
+2802(by)X
+2906(using)X
+3103(a)X
+7 f
+3164(<literal)X
+3601(next>)X
+1 f
+3866(char-)X
+776 4272(acter.)N
+993(The)X
+1138(command)X
+1474(is)X
+1547(then)X
+1705(executed.)X
+776 4452(If)N
+850(the)X
+3 f
+968(ex)X
+1 f
+1064(command)X
+1400(writes)X
+1616(to)X
+1698(the)X
+1816(screen,)X
+3 f
+2063(vi)X
+1 f
+2146(will)X
+2291(prompt)X
+2543(the)X
+2662(user)X
+2817(for)X
+2932(a)X
+7 f
+2989(<carriage-return>)X
+1 f
+3826(before)X
+776 4542(continuing)N
+1144(when)X
+1344(the)X
+3 f
+1468(ex)X
+1 f
+1570(command)X
+1912(\256nishes.)X
+2222(Large)X
+2436(amounts)X
+2733(of)X
+2826(output)X
+3056(from)X
+3238(the)X
+3 f
+3362(ex)X
+1 f
+3464(command)X
+3806(will)X
+3956(be)X
+776 4632(paged)N
+994(for)X
+1114(the)X
+1238(user,)X
+1418(and)X
+1560(the)X
+1684(user)X
+1844(prompted)X
+2177(for)X
+2297(a)X
+7 f
+2359(<carriage-return>)X
+1 f
+3201(or)X
+7 f
+3294(<space>)X
+1 f
+3657(key)X
+3800(to)X
+3889(con-)X
+776 4722(tinue.)N
+999(In)X
+1089(some)X
+1281(cases,)X
+1494(a)X
+1553(quit)X
+1700(\(normally)X
+2039(a)X
+2098(``q'')X
+2269 0.3750(character\))AX
+2615(or)X
+7 f
+2705(<interrupt>)X
+1 f
+3256(may)X
+3417(be)X
+3515(entered)X
+3774(to)X
+3858(inter-)X
+776 4812(rupt)N
+925(the)X
+3 f
+1043(ex)X
+1 f
+1139(command.)X
+776 4992(When)N
+988(the)X
+3 f
+1106(ex)X
+1 f
+1202(command)X
+1538(\256nishes,)X
+1822(and)X
+1958(the)X
+2076(user)X
+2230(is)X
+2303(prompted)X
+2631(to)X
+2714(resume)X
+2967(visual)X
+3179(mode,)X
+3398(it)X
+3463(is)X
+3537(also)X
+3687(possible)X
+3970(to)X
+776 5082(enter)N
+957(another)X
+1218(``)X
+7 f
+1272(:)X
+1 f
+('')S
+1394(character)X
+1710(followed)X
+2015(by)X
+2115(another)X
+3 f
+2376(ex)X
+1 f
+2472(command.)X
+776 5262(Line:)N
+1136(The)X
+1281(current)X
+1529(line)X
+1669(is)X
+1742(set)X
+1851(as)X
+1938(described)X
+2266(for)X
+2380(the)X
+3 f
+2498(ex)X
+1 f
+2594(command.)X
+776 5352(Column:)N
+1136(The)X
+1281(current)X
+1529(column)X
+1789(is)X
+1862(set)X
+1971(as)X
+2058(described)X
+2386(for)X
+2500(the)X
+3 f
+2618(ex)X
+1 f
+2714(command.)X
+776 5442(Options:)N
+1136(None.)X
+3 f
+576 5622([count])N
+841(;)X
+1 f
+776 5712(Repeat)N
+1025(the)X
+1149(last)X
+1286(character)X
+1608(\256nd)X
+7 f
+1758(count)X
+1 f
+2024(times.)X
+2263(The)X
+2415(last)X
+2553(character)X
+2876(\256nd)X
+3027(is)X
+3107(one)X
+3250(of)X
+3344(the)X
+3 f
+3469(F)X
+1 f
+3518(,)X
+3 f
+3565(f)X
+1 f
+3592(,)X
+3 f
+3639(T)X
+1 f
+3719(or)X
+3 f
+3813(t)X
+1 f
+3867(com-)X
+776 5802(mands.)N
+
+20 p
+%%Page: 20 19
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-20)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(The)N
+3 f
+922(;)X
+1 f
+970(command)X
+1307(may)X
+1466(be)X
+1563(used)X
+1731(as)X
+1819(the)X
+1938(motion)X
+2185(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 852(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 1032(Line:)N
+1136(Unchanged.)X
+776 1122(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458 0.3068(searched-for)AX
+1881(character.)X
+776 1212(Options:)N
+1136(None.)X
+3 f
+576 1392([count])N
+841(<)X
+907(motion)X
+576 1482([count])N
+841(>)X
+907(motion)X
+1 f
+776 1572(Shift)N
+956(lines)X
+1132(left)X
+1264(or)X
+1357(right.)X
+1574(Shift)X
+1755(the)X
+1879(number)X
+2150(of)X
+2243(lines)X
+2420(in)X
+2508(the)X
+2632(region)X
+2863(speci\256ed)X
+3174(by)X
+3280(the)X
+3404(motion)X
+3656(component,)X
+776 1662(times)N
+7 f
+980(count)X
+1 f
+(,)S
+1271(left)X
+1409(\(for)X
+1561(the)X
+3 f
+1690(<)X
+1 f
+1767(command\))X
+2141(or)X
+2239(right)X
+2421(\(for)X
+2573(the)X
+3 f
+2702(>)X
+1 f
+2778(command\))X
+3151(by)X
+3261(the)X
+3389(number)X
+3664(of)X
+3761(columns)X
+776 1752(speci\256ed)N
+1086(by)X
+1191(the)X
+3 f
+1314(shiftwidth)X
+1 f
+1685(option.)X
+1954(Only)X
+2139(whitespace)X
+2521(characters)X
+2873(are)X
+2997(deleted)X
+3255(when)X
+3455(shifting)X
+3725(left;)X
+3880(once)X
+776 1842(the)N
+901(\256rst)X
+1052(character)X
+1375(in)X
+1464(the)X
+1589(line)X
+1736(contains)X
+2030(a)X
+2092(nonblank)X
+2416(character,)X
+2758(the)X
+3 f
+2882(shift)X
+1 f
+3059(will)X
+3209(succeed,)X
+3510(but)X
+3638(the)X
+3762(line)X
+3908(will)X
+776 1932(not)N
+898(be)X
+994(modi\256ed.)X
+776 2112(Line:)N
+1136(Unchanged.)X
+776 2202(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 2292(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(shiftwidth)X
+1 f
+2022(option.)X
+3 f
+576 2472(@)N
+670(buffer)X
+1 f
+776 2562(Execute)N
+1065(a)X
+1131(named)X
+1375(buffer.)X
+1642(Execute)X
+1931(the)X
+2059(named)X
+2303(buffer)X
+2530(as)X
+3 f
+2627(vi)X
+1 f
+2719(commands.)X
+3136(The)X
+3292(buffer)X
+3520(may)X
+3689(include)X
+3 f
+3956(ex)X
+1 f
+776 2652(commands,)N
+1170(too,)X
+1319(but)X
+1448(they)X
+1613(must)X
+1795(be)X
+1898(expressed)X
+2241(as)X
+2334(a)X
+3 f
+2396(:)X
+1 f
+2449(command.)X
+2831(If)X
+2911(the)X
+3035(buffer)X
+3258(is)X
+3337(line)X
+3483(oriented,)X
+7 f
+3792(<new-)X
+776 2742(line>)N
+1 f
+1054(characters)X
+1419(are)X
+1556(logically)X
+1874(appended)X
+2220(to)X
+2320(each)X
+2506(line)X
+2664(of)X
+2769(the)X
+2905(buffer.)X
+3180(If)X
+3272(the)X
+3408(buffer)X
+3644(is)X
+3736(character)X
+776 2832(oriented,)N
+7 f
+1079(<newline>)X
+1 f
+1531(characters)X
+1878(are)X
+1997(logically)X
+2297(appended)X
+2625(to)X
+2707(all)X
+2807(but)X
+2929(the)X
+3047(last)X
+3178(line)X
+3318(in)X
+3400(the)X
+3518(buffer.)X
+776 3012(If)N
+861(the)X
+990(buffer)X
+1218(name)X
+1423(is)X
+1507(``)X
+7 f
+1561(@)X
+1 f
+('',)S
+1714(or)X
+1812(``)X
+7 f
+1866(*)X
+1 f
+('',)S
+2019(then)X
+2188(the)X
+2317(last)X
+2459(buffer)X
+2687(executed)X
+3004(shall)X
+3186(be)X
+3293(used.)X
+3511(It)X
+3591(is)X
+3675(an)X
+3782(error)X
+3970(to)X
+776 3102(specify)N
+1033(``)X
+7 f
+1087(@@)X
+1 f
+('')S
+1262(or)X
+1354(``)X
+7 f
+1408(**)X
+1 f
+('')S
+1583(if)X
+1657(there)X
+1843(were)X
+2025(no)X
+2130(buffer)X
+2351(previous)X
+2651(executions.)X
+3058(The)X
+3207(text)X
+3351(of)X
+3442(a)X
+3502(macro)X
+3727(may)X
+3889(con-)X
+776 3192(tain)N
+919(an)X
+3 f
+1019(@)X
+1 f
+1117(command,)X
+1477(and)X
+1617(it)X
+1685(is)X
+1762(possible)X
+2048(to)X
+2134(create)X
+2351(in\256nite)X
+2601(loops)X
+2798(in)X
+2884(this)X
+3023(manner.)X
+3328(\(The)X
+7 f
+3504(<interrupt>)X
+1 f
+776 3282(character)N
+1092(may)X
+1250(be)X
+1346(used)X
+1513(to)X
+1595(interrupt)X
+1891(the)X
+2009(loop.\))X
+776 3462(Line:)N
+1136(The)X
+1281(current)X
+1529(line)X
+1669(is)X
+1742(set)X
+1851(as)X
+1938(described)X
+2266(for)X
+2380(the)X
+2498(command\(s\).)X
+776 3552(Column:)N
+1136(The)X
+1281(current)X
+1529(column)X
+1789(is)X
+1862(set)X
+1971(as)X
+2058(described)X
+2386(for)X
+2500(the)X
+2618(command\(s\).)X
+776 3642(Options:)N
+1136(None.)X
+3 f
+576 3822([count])N
+841(A)X
+1 f
+776 3912(Enter)N
+981(input)X
+1176(mode,)X
+1405(appending)X
+1770(the)X
+1899(text)X
+2050(after)X
+2229(the)X
+2358(end)X
+2505(of)X
+2603(the)X
+2732(line.)X
+2923(If)X
+7 f
+3008(count)X
+1 f
+3279(is)X
+3363(speci\256ed,)X
+3699(the)X
+3828(text)X
+3979(is)X
+776 4002(repeatedly)N
+1131(input)X
+7 f
+1315(count)X
+1603(-)X
+1699(1)X
+1 f
+1767(more)X
+1952(times)X
+2145(after)X
+2313(input)X
+2497(mode)X
+2695(is)X
+2768(exited.)X
+776 4182(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 4272(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 4362(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 4452(gin)N
+1 f
+1262(options.)X
+3 f
+576 4632([count])N
+841(B)X
+1 f
+776 4722(Move)N
+995(backward)X
+7 f
+1340(count)X
+1 f
+1612(bigwords.)X
+1982(Move)X
+2201(the)X
+2331(cursor)X
+2564(backward)X
+2909(to)X
+3003(the)X
+3133(beginning)X
+3485(of)X
+3584(a)X
+3652(bigword)X
+3952(by)X
+776 4812(repeating)N
+1096(the)X
+1215(following)X
+1547(algorithm:)X
+1901(if)X
+1971(the)X
+2090(current)X
+2339(position)X
+2617(is)X
+2691(at)X
+2770(the)X
+2889(beginning)X
+3230(of)X
+3318(a)X
+3374(bigword)X
+3661(or)X
+3748(the)X
+3866(char-)X
+776 4902(acter)N
+955(at)X
+1036(the)X
+1157(current)X
+1408(position)X
+1688(cannot)X
+1925(be)X
+2024(part)X
+2172(of)X
+2262(a)X
+2321(bigword,)X
+2631(move)X
+2832(to)X
+2917(the)X
+3038(\256rst)X
+3185(character)X
+3504(of)X
+3594(the)X
+3715(preceding)X
+776 4992(bigword.)N
+1105(Otherwise,)X
+1477(move)X
+1677(to)X
+1761(the)X
+1881(\256rst)X
+2027(character)X
+2345(of)X
+2434(the)X
+2554(bigword)X
+2843(at)X
+2923(the)X
+3043(current)X
+3293(position.)X
+3612(If)X
+3688(no)X
+3790(preced-)X
+776 5082(ing)N
+908(bigword)X
+1205(exists)X
+1418(on)X
+1529(the)X
+1658(current)X
+1917(line,)X
+2088(move)X
+2297(to)X
+2390(the)X
+2519(\256rst)X
+2674(character)X
+3001(of)X
+3099(the)X
+3228(last)X
+3370(bigword)X
+3668(on)X
+3779(the)X
+3908(\256rst)X
+776 5172(preceding)N
+1113(line)X
+1253(that)X
+1393(contains)X
+1680(a)X
+1736(bigword.)X
+776 5352(The)N
+3 f
+921(B)X
+1 f
+994(command)X
+1330(may)X
+1488(be)X
+1584(used)X
+1751(as)X
+1838(the)X
+1956(motion)X
+2202(component)X
+2578(of)X
+2665(other)X
+3 f
+2850(vi)X
+1 f
+2932(commands,)X
+3319(in)X
+3401(which)X
+3617(case)X
+3776(any)X
+3912(text)X
+776 5442(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 5622(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(word)X
+2259(selected.)X
+776 5712(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(of)X
+2005(the)X
+2123(word)X
+2308(selected.)X
+
+21 p
+%%Page: 21 20
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-21)X
+1 f
+776 762(Options:)N
+1136(None.)X
+3 f
+576 942([buffer])N
+864([count])X
+1129(C)X
+1 f
+776 1032(Change)N
+1058(text)X
+1215(from)X
+1408(the)X
+1543(current)X
+1808(position)X
+2102(to)X
+2201(the)X
+2336(end-of-line.)X
+2770(If)X
+7 f
+2862(count)X
+1 f
+3140(is)X
+3231(speci\256ed,)X
+3574(the)X
+3710(input)X
+3912(text)X
+776 1122(replaces)N
+1060(from)X
+1236(the)X
+1354(current)X
+1602(position)X
+1879(to)X
+1961(the)X
+2079(end-of-line,)X
+2476(plus)X
+7 f
+2629(count)X
+2917(-)X
+3013(1)X
+1 f
+3081(subsequent)X
+3457(lines.)X
+776 1302(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 1392(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 1482(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 1572(gin)N
+1 f
+1262(options.)X
+3 f
+576 1752([buffer])N
+864(D)X
+1 f
+776 1842(Delete)N
+1006(text)X
+1146(from)X
+1322(the)X
+1440(current)X
+1688(position)X
+1965(to)X
+2047(the)X
+2165(end-of-line.)X
+776 2022(It)N
+845(is)X
+918(not)X
+1040(an)X
+1136(error)X
+1313(to)X
+1395(execute)X
+1661(the)X
+3 f
+1779(D)X
+1 f
+1857(command)X
+2193(on)X
+2293(an)X
+2389(empty)X
+2609(line.)X
+776 2202(Line:)N
+1136(Unchanged.)X
+776 2292(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+1774(before)X
+2000(the)X
+2118(current)X
+2367(character,)X
+2704(or,)X
+2812(column)X
+3073(1)X
+3134(if)X
+3204(the)X
+3323(cursor)X
+3545(was)X
+3691(on)X
+3792(column)X
+1136 2382(1.)N
+776 2472(Options:)N
+1136(None.)X
+3 f
+576 2652([count])N
+841(E)X
+1 f
+776 2742(Move)N
+983(forward)X
+7 f
+1258(count)X
+1 f
+1518(end-of-bigwords.)X
+2113(Move)X
+2320(the)X
+2438(cursor)X
+2659(forward)X
+2935(to)X
+3018(the)X
+3137(end)X
+3274(of)X
+3362(a)X
+3419(bigword)X
+3707(by)X
+3808(repeat-)X
+776 2832(ing)N
+906(the)X
+1032(following)X
+1371(algorithm:)X
+1732(if)X
+1809(the)X
+1935(current)X
+2191(position)X
+2476(is)X
+2557(the)X
+2683(end)X
+2827(of)X
+2922(a)X
+2986(bigword)X
+3281(or)X
+3376(the)X
+3502(character)X
+3826(at)X
+3912(that)X
+776 2922(position)N
+1061(cannot)X
+1303(be)X
+1407(part)X
+1560(of)X
+1655(a)X
+1719(bigword,)X
+2034(move)X
+2240(to)X
+2331(the)X
+2458(last)X
+2598(character)X
+2923(of)X
+3019(the)X
+3146(following)X
+3486(bigword.)X
+3822(Other-)X
+776 3012(wise,)N
+973(move)X
+1181(to)X
+1273(the)X
+1401(last)X
+1542(character)X
+1868(of)X
+1965(the)X
+2093(bigword)X
+2390(at)X
+2477(the)X
+2604(current)X
+2861(position.)X
+3187(If)X
+3270(no)X
+3379(succeeding)X
+3765(bigword)X
+776 3102(exists)N
+984(on)X
+1090(the)X
+1214(current)X
+1468(line,)X
+1634(move)X
+1838(to)X
+1927(the)X
+2052(last)X
+2190(character)X
+2513(of)X
+2607(the)X
+2732(\256rst)X
+2883(bigword)X
+3177(on)X
+3284(the)X
+3409(next)X
+3574(following)X
+3912(line)X
+776 3192(that)N
+916(contains)X
+1203(a)X
+1259(bigword.)X
+776 3372(The)N
+3 f
+921(E)X
+1 f
+994(command)X
+1330(may)X
+1488(be)X
+1584(used)X
+1751(as)X
+1838(the)X
+1956(motion)X
+2202(component)X
+2578(of)X
+2665(other)X
+3 f
+2850(vi)X
+1 f
+2932(commands,)X
+3319(in)X
+3401(which)X
+3617(case)X
+3776(any)X
+3912(text)X
+776 3462(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 3642(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(word)X
+2259(selected.)X
+776 3732(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(of)X
+1992(the)X
+2110(word)X
+2295(selected.)X
+776 3822(Options:)N
+1136(None.)X
+3 f
+576 4002([count])N
+841(F)X
+910 0.3125(<character>)AX
+1 f
+776 4092(Search)N
+7 f
+1015(count)X
+1 f
+1275(times)X
+1468(backward)X
+1801(through)X
+2070(the)X
+2188(current)X
+2436(line)X
+2576(for)X
+7 f
+2690(<character>)X
+1 f
+(.)S
+776 4272(The)N
+3 f
+921(F)X
+1 f
+990(command)X
+1326(may)X
+1484(be)X
+1580(used)X
+1747(as)X
+1834(the)X
+1952(motion)X
+2198(component)X
+2574(of)X
+2661(other)X
+3 f
+2846(vi)X
+1 f
+2928(commands,)X
+3315(in)X
+3398(which)X
+3615(case)X
+3775(any)X
+3912(text)X
+776 4362(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 4542(Line:)N
+1136(Unchanged.)X
+776 4632(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458 0.3068(searched-for)AX
+1881(character.)X
+776 4722(Options:)N
+1136(None.)X
+3 f
+576 4902([count])N
+841(G)X
+1 f
+776 4992(Move)N
+983(to)X
+1065(line)X
+7 f
+1205(count)X
+1 f
+(,)S
+1485(or)X
+1572(the)X
+1690(last)X
+1821(line)X
+1961(of)X
+2048(the)X
+2166(\256le)X
+2288(if)X
+7 f
+2357(count)X
+1 f
+2617(not)X
+2739(speci\256ed.)X
+776 5172(The)N
+3 f
+924(G)X
+1 f
+1009(command)X
+1348(is)X
+1424(an)X
+1523(absolute)X
+1813(movement.)X
+2214(The)X
+3 f
+2362(G)X
+1 f
+2447(command)X
+2786(may)X
+2947(be)X
+3046(used)X
+3216(as)X
+3306(the)X
+3427(motion)X
+3676(component)X
+776 5262(of)N
+863(other)X
+3 f
+1048(vi)X
+1 f
+1130(commands,)X
+1517(in)X
+1599(which)X
+1815(case)X
+1974(any)X
+2110(text)X
+2250(copied)X
+2484(into)X
+2628(a)X
+2684(buffer)X
+2901(is)X
+2974(line)X
+3114(oriented.)X
+776 5442(Line:)N
+1136(Set)X
+1258(to)X
+7 f
+1340(count)X
+1 f
+(,)S
+1620(if)X
+1689(speci\256ed,)X
+2014(otherwise,)X
+2366(the)X
+2484(last)X
+2615(line.)X
+776 5532(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 5622(Options:)N
+1136(None.)X
+
+22 p
+%%Page: 22 21
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-22)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+576 762([count])N
+841(H)X
+1 f
+776 852(Move)N
+983(to)X
+1065(the)X
+1183(screen)X
+1409(line)X
+7 f
+1549(count)X
+1837(-)X
+1933(1)X
+1 f
+2001(lines)X
+2172(below)X
+2388(the)X
+2506(top)X
+2628(of)X
+2715(the)X
+2833(screen.)X
+776 1032(The)N
+3 f
+924(H)X
+1 f
+1009(command)X
+1348(is)X
+1424(an)X
+1523(absolute)X
+1813(movement.)X
+2214(The)X
+3 f
+2362(H)X
+1 f
+2447(command)X
+2786(may)X
+2947(be)X
+3046(used)X
+3216(as)X
+3306(the)X
+3427(motion)X
+3676(component)X
+776 1122(of)N
+863(other)X
+3 f
+1048(vi)X
+1 f
+1130(commands,)X
+1517(in)X
+1599(which)X
+1815(case)X
+1974(any)X
+2110(text)X
+2250(copied)X
+2484(into)X
+2628(a)X
+2684(buffer)X
+2901(is)X
+2974(line)X
+3114(oriented.)X
+776 1302(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+7 f
+1598(count)X
+1886(-)X
+1982(1)X
+1 f
+2050(lines)X
+2221(below)X
+2437(the)X
+2555(top)X
+2677(of)X
+2764(the)X
+2882(screen.)X
+776 1392(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2 f
+2441(screen)X
+1 f
+2671(line.)X
+776 1482(Options:)N
+1136(None.)X
+3 f
+576 1662([count])N
+841(I)X
+1 f
+776 1752(Enter)N
+971(input)X
+1156(mode,)X
+1375(inserting)X
+1676(the)X
+1795(text)X
+1936(at)X
+2015(the)X
+2134(beginning)X
+2475(of)X
+2564(the)X
+2684(line.)X
+2866(If)X
+7 f
+2942(count)X
+1 f
+3204(is)X
+3279(speci\256ed,)X
+3606(the)X
+3726(text)X
+3868(input)X
+776 1842(is)N
+849(repeatedly)X
+1204(input)X
+7 f
+1388(count)X
+1676(-)X
+1772(1)X
+1 f
+1840(more)X
+2025(times.)X
+776 2022(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 2112(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 2202(Options:)N
+1136(None.)X
+3 f
+576 2382([count])N
+841(J)X
+1 f
+776 2472(Join)N
+929(lines.)X
+1140(If)X
+7 f
+1214(count)X
+1 f
+1474(is)X
+1547(speci\256ed,)X
+7 f
+1872(count)X
+1 f
+2132(lines)X
+2303(are)X
+2422(joined;)X
+2664(a)X
+2720(minimum)X
+3050(of)X
+3137(two)X
+3277(lines)X
+3448(are)X
+3568(always)X
+3812(joined,)X
+776 2562(regardless)N
+1122(of)X
+1209(the)X
+1327(value)X
+1521(of)X
+7 f
+1608(count)X
+1 f
+(.)S
+776 2742(If)N
+859(the)X
+986(current)X
+1244(line)X
+1394(ends)X
+1571(with)X
+1743(a)X
+1809(whitespace)X
+2196(character,)X
+2542(all)X
+2652(whitespace)X
+3039(is)X
+3122(stripped)X
+3410(from)X
+3596(the)X
+3724(next)X
+3892(line.)X
+776 2832(Otherwise,)N
+1148(if)X
+1219(the)X
+1338(next)X
+1497(line)X
+1638(starts)X
+1828(with)X
+1991(a)X
+2048(open)X
+2225(parenthesis)X
+2607(\(``)X
+7 f
+2688(\()X
+1 f
+(''\))S
+2838(do)X
+2939(nothing.)X
+3244(Otherwise,)X
+3615(if)X
+3685(the)X
+3804(current)X
+776 2922(line)N
+917(ends)X
+1086(with)X
+1250(a)X
+1308(question)X
+1601(mark)X
+1788(\(``)X
+7 f
+1869(?)X
+1 f
+(''\),)S
+2040(period)X
+2267(\(``)X
+7 f
+2348(.)X
+1 f
+(''\))S
+2519(or)X
+2608(exclamation)X
+3022(point)X
+3208(\(``)X
+7 f
+3289(!)X
+1 f
+(''\),)S
+3460(insert)X
+3660(two)X
+3802(spaces.)X
+776 3012(Otherwise,)N
+1146(insert)X
+1344(a)X
+1400(single)X
+1611(space.)X
+776 3192(It)N
+845(is)X
+918(not)X
+1040(an)X
+1136(error)X
+1313(to)X
+1395(join)X
+1539(lines)X
+1710(past)X
+1859(the)X
+1977(end)X
+2113(of)X
+2200(the)X
+2318(\256le,)X
+2460(i.e.)X
+2578(lines)X
+2749(that)X
+2889(do)X
+2989(not)X
+3111(exist.)X
+776 3372(Line:)N
+1136(Unchanged.)X
+776 3462(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+1774(after)X
+1942(the)X
+2060(last)X
+2191(character)X
+2507(of)X
+2594(the)X
+2712(next-to-last)X
+3097(joined)X
+3317(line.)X
+776 3552(Options:)N
+1136(None.)X
+3 f
+576 3732([count])N
+841(L)X
+1 f
+776 3822(Move)N
+983(to)X
+1065(the)X
+1183(screen)X
+1409(line)X
+7 f
+1549(count)X
+1837(-)X
+1933(1)X
+1 f
+2001(lines)X
+2172(above)X
+2384(the)X
+2502(bottom)X
+2748(of)X
+2835(the)X
+2953(screen.)X
+776 4002(The)N
+3 f
+925(L)X
+1 f
+1002(command)X
+1342(is)X
+1419(an)X
+1519(absolute)X
+1810(movement.)X
+2212(The)X
+3 f
+2361(L)X
+1 f
+2438(command)X
+2778(may)X
+2940(be)X
+3040(used)X
+3211(as)X
+3302(the)X
+3425(motion)X
+3676(component)X
+776 4092(of)N
+863(other)X
+3 f
+1048(vi)X
+1 f
+1130(commands,)X
+1517(in)X
+1599(which)X
+1815(case)X
+1974(any)X
+2110(text)X
+2250(copied)X
+2484(into)X
+2628(a)X
+2684(buffer)X
+2901(is)X
+2974(line)X
+3114(oriented.)X
+776 4272(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+7 f
+1598(count)X
+1886(-)X
+1982(1)X
+1 f
+2050(lines)X
+2221(above)X
+2433(the)X
+2551(bottom)X
+2797(of)X
+2884(the)X
+3002(screen.)X
+776 4362(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2 f
+2441(screen)X
+1 f
+2671(line.)X
+776 4452(Options:)N
+1136(None.)X
+3 f
+596 4632(M)N
+1 f
+776 4722(Move)N
+983(to)X
+1065(the)X
+1183(screen)X
+1409(line)X
+1549(in)X
+1631(the)X
+1749(middle)X
+1991(of)X
+2078(the)X
+2196(screen.)X
+776 4902(The)N
+3 f
+922(M)X
+1 f
+1019(command)X
+1356(is)X
+1430(an)X
+1527(absolute)X
+1815(movement.)X
+2214(The)X
+3 f
+2360(M)X
+1 f
+2457(command)X
+2794(may)X
+2953(be)X
+3050(used)X
+3219(as)X
+3308(the)X
+3428(motion)X
+3676(component)X
+776 4992(of)N
+863(other)X
+3 f
+1048(vi)X
+1 f
+1130(commands,)X
+1517(in)X
+1599(which)X
+1815(case)X
+1974(any)X
+2110(text)X
+2250(copied)X
+2484(into)X
+2628(a)X
+2684(buffer)X
+2901(is)X
+2974(line)X
+3114(oriented.)X
+776 5172(Historically,)N
+1194(any)X
+7 f
+1330(count)X
+1 f
+1590(speci\256ed)X
+1895(to)X
+1977(the)X
+3 f
+2095(M)X
+1 f
+2191(command)X
+2527(was)X
+2672(ignored.)X
+776 5352(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(in)X
+1680(the)X
+1798(middle)X
+2040(of)X
+2127(the)X
+2245(screen.)X
+776 5442(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2 f
+2441(screen)X
+1 f
+2671(line.)X
+776 5532(Options:)N
+1136(None.)X
+3 f
+576 5712([count])N
+841(O)X
+1 f
+776 5802(Enter)N
+970(input)X
+1154(mode,)X
+1372(appending)X
+1726(text)X
+1866(in)X
+1948(a)X
+2004(new)X
+2158(line)X
+2298(above)X
+2510(the)X
+2628(current)X
+2876(line.)X
+3057(If)X
+7 f
+3132(count)X
+1 f
+3393(is)X
+3467(speci\256ed,)X
+3793(the)X
+3912(text)X
+
+23 p
+%%Page: 23 22
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-23)X
+1 f
+776 762(input)N
+960(is)X
+1033(repeatedly)X
+1388(input)X
+7 f
+1572(count)X
+1860(-)X
+1956(1)X
+1 f
+2024(more)X
+2209(times.)X
+776 942(Historically,)N
+1194(any)X
+7 f
+1330(count)X
+1 f
+1590(speci\256ed)X
+1895(to)X
+1977(the)X
+3 f
+2095(O)X
+1 f
+2177(command)X
+2513(was)X
+2658(ignored.)X
+776 1122(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 1212(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 1302(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 1392(gin)N
+1 f
+1262(options.)X
+3 f
+576 1572([buffer])N
+864(P)X
+1 f
+776 1662(Insert)N
+980(text)X
+1121(from)X
+1298(a)X
+1355(buffer.)X
+1613(Text)X
+1782(from)X
+1960(the)X
+2080(buffer)X
+2299(\(the)X
+2446(unnamed)X
+2762(buffer)X
+2981(by)X
+3083(default\))X
+3355(is)X
+3430(inserted)X
+3706(before)X
+3934(the)X
+776 1752(current)N
+1024(column)X
+1284(or,)X
+1391(if)X
+1460(the)X
+1578(buffer)X
+1795(is)X
+1868(line)X
+2008(oriented,)X
+2311(before)X
+2537(the)X
+2655(current)X
+2903(line.)X
+776 1932(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(lowest)X
+1687(numbered)X
+2028(line)X
+2168(insert,)X
+2386(if)X
+2455(the)X
+2573(buffer)X
+2790(is)X
+2863(line)X
+3003(oriented,)X
+3306(otherwise)X
+3638(unchanged.)X
+776 2022(Column:)N
+1136(Set)X
+1261(to)X
+1346(the)X
+1467(\256rst)X
+1614(nonblank)X
+1935(character)X
+2254(of)X
+2344(the)X
+2465(appended)X
+2796(text,)X
+2959(if)X
+3032(the)X
+3154(buffer)X
+3375(is)X
+3452(line)X
+3596(oriented,)X
+3903(oth-)X
+1136 2112(erwise,)N
+1386(the)X
+1504(last)X
+1635(character)X
+1951(of)X
+2038(the)X
+2156(appended)X
+2484(text.)X
+776 2202(Options:)N
+1136(None.)X
+3 f
+576 2382(Q)N
+1 f
+776 2472(Exit)N
+3 f
+929(vi)X
+1 f
+1011(\(or)X
+1125(visual\))X
+1363(mode)X
+1561(and)X
+1697(switch)X
+1926(to)X
+3 f
+2008(ex)X
+1 f
+2104(mode.)X
+776 2652(Line:)N
+1136(Unchanged.)X
+776 2742(Column:)N
+1136(No)X
+1254(longer)X
+1479(relevant.)X
+776 2832(Options:)N
+1136(None.)X
+3 f
+576 3012([count])N
+841(R)X
+1 f
+776 3102(Enter)N
+971(input)X
+1156(mode,)X
+1375(replacing)X
+1695(the)X
+1814(characters)X
+2162(in)X
+2245(the)X
+2364(current)X
+2613(line.)X
+2794(If)X
+7 f
+2869(count)X
+1 f
+3130(is)X
+3204(speci\256ed,)X
+3531(the)X
+3651(text)X
+3793(input)X
+3979(is)X
+776 3192(repeatedly)N
+1131(input)X
+7 f
+1315(count)X
+1603(-)X
+1699(1)X
+1 f
+1767(more)X
+1952(times.)X
+776 3372(If)N
+855(the)X
+978(end)X
+1119(of)X
+1211(the)X
+1334(current)X
+1587(line)X
+1732(is)X
+1810(reached,)X
+2106(no)X
+2211(more)X
+2401(characters)X
+2753(are)X
+2877(replaced)X
+3176(and)X
+3318(any)X
+3460(further)X
+3705(characters)X
+776 3462(input)N
+960(are)X
+1079(appended)X
+1407(to)X
+1489(the)X
+1607(line.)X
+776 3642(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 3732(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 3822(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 3912(gin)N
+1 f
+1262(options.)X
+3 f
+576 4092([buffer])N
+864([count])X
+1129(S)X
+1 f
+776 4182(Substitute)N
+7 f
+1115(count)X
+1 f
+1375(lines.)X
+776 4362(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 4452(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 4542(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 4632(gin)N
+1 f
+1262(options.)X
+3 f
+576 4812([count])N
+841(T)X
+914 0.3125(<character>)AX
+1 f
+776 4902(Search)N
+1014(backward,)X
+7 f
+1366(count)X
+1 f
+1625(times,)X
+1837(through)X
+2105(the)X
+2222(current)X
+2469(line)X
+2608(for)X
+2721(the)X
+2838(character)X
+2 f
+3153(after)X
+1 f
+3323(the)X
+3440(speci\256ed)X
+7 f
+3744(<char-)X
+776 4992(acter>)N
+1 f
+(.)S
+776 5172(The)N
+3 f
+921(T)X
+1 f
+994(command)X
+1330(may)X
+1488(be)X
+1584(used)X
+1751(as)X
+1838(the)X
+1956(motion)X
+2202(component)X
+2578(of)X
+2665(other)X
+3 f
+2850(vi)X
+1 f
+2932(commands,)X
+3319(in)X
+3401(which)X
+3617(case)X
+3776(any)X
+3912(text)X
+776 5262(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 5442(Line:)N
+1136(Unchanged.)X
+776 5532(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+2 f
+1774(after)X
+1 f
+1945(the)X
+2063 0.3068(searched-for)AX
+2486(character.)X
+776 5622(Options:)N
+1136(None.)X
+
+24 p
+%%Page: 24 23
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-24)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+576 762(U)N
+1 f
+776 852(Restore)N
+1041(the)X
+1159(current)X
+1407(line)X
+1547(to)X
+1629(its)X
+1724(state)X
+1891(before)X
+2117(the)X
+2235(cursor)X
+2456(last)X
+2587(moved)X
+2825(to)X
+2907(it.)X
+776 1032(Line:)N
+1136(Unchanged.)X
+776 1122(Column:)N
+1136(The)X
+1281(\256rst)X
+1425(character)X
+1741(in)X
+1823(the)X
+1941(line.)X
+776 1212(Options:)N
+1136(None.)X
+3 f
+576 1392([count])N
+841(W)X
+1 f
+776 1482(Move)N
+985(forward)X
+7 f
+1262(count)X
+1 f
+1524(bigwords.)X
+1884(Move)X
+2094(the)X
+2215(cursor)X
+2439(forward)X
+2717(to)X
+2802(the)X
+2923(beginning)X
+3266(of)X
+3356(a)X
+3415(bigword)X
+3705(by)X
+3808(repeat-)X
+776 1572(ing)N
+903(the)X
+1026(following)X
+1362(algorithm:)X
+1720(if)X
+1794(the)X
+1917(current)X
+2170(position)X
+2452(is)X
+2530(within)X
+2759(a)X
+2820(bigword)X
+3112(or)X
+3204(the)X
+3326(character)X
+3646(at)X
+3728(that)X
+3872(posi-)X
+776 1662(tion)N
+926(cannot)X
+1166(be)X
+1268(part)X
+1419(of)X
+1512(a)X
+1574(bigword,)X
+1887(move)X
+2091(to)X
+2179(the)X
+2303(\256rst)X
+2453(character)X
+2775(of)X
+2868(the)X
+2992(next)X
+3156(bigword.)X
+3489(If)X
+3569(no)X
+3676(subsequent)X
+776 1752(bigword)N
+1066(exists)X
+1271(on)X
+1374(the)X
+1495(current)X
+1746(line,)X
+1909(move)X
+2110(to)X
+2195(the)X
+2316(\256rst)X
+2463(character)X
+2782(of)X
+2872(the)X
+2993(\256rst)X
+3139(bigword)X
+3428(on)X
+3530(the)X
+3650(\256rst)X
+3796(follow-)X
+776 1842(ing)N
+898(line)X
+1038(that)X
+1178(contains)X
+1465(a)X
+1521(bigword.)X
+776 2022(The)N
+3 f
+927(W)X
+1 f
+1033(command)X
+1375(may)X
+1539(be)X
+1641(used)X
+1814(as)X
+1908(the)X
+2033(motion)X
+2286(component)X
+2669(of)X
+2763(other)X
+3 f
+2955(vi)X
+1 f
+3044(commands,)X
+3438(in)X
+3527(which)X
+3750(case)X
+3916(any)X
+776 2112(text)N
+916(copied)X
+1150(into)X
+1294(a)X
+1350(buffer)X
+1567(is)X
+1640(character)X
+1956(oriented.)X
+776 2292(Line:)N
+1136(The)X
+1281(line)X
+1421(containing)X
+1779(the)X
+1897(word)X
+2082(selected.)X
+776 2382(Column:)N
+1136(The)X
+1281(\256rst)X
+1425(character)X
+1741(of)X
+1828(the)X
+1946(word)X
+2131(selected.)X
+776 2472(Options:)N
+1136(None.)X
+3 f
+576 2652([buffer])N
+864([count])X
+1129(X)X
+1 f
+776 2742(Delete)N
+7 f
+1008(count)X
+1 f
+1270(characters)X
+1619(before)X
+1847(the)X
+1967(cursor.)X
+2230(If)X
+2306(the)X
+2426(number)X
+2693(of)X
+2782(characters)X
+3132(to)X
+3217(be)X
+3316(deleted)X
+3571(is)X
+3647(greater)X
+3894(than)X
+776 2832(or)N
+874(equal)X
+1079(to)X
+1172(the)X
+1301(number)X
+1577(of)X
+1675(characters)X
+2033(to)X
+2126(the)X
+2255(beginning)X
+2606(of)X
+2704(the)X
+2833(line,)X
+3004(all)X
+3115(of)X
+3213(the)X
+3341(characters)X
+3698(before)X
+3934(the)X
+776 2922(current)N
+1024(cursor)X
+1245(position,)X
+1542(to)X
+1624(the)X
+1742(beginning)X
+2082(of)X
+2169(the)X
+2287(line,)X
+2447(are)X
+2566(deleted.)X
+776 3102(Line:)N
+1136(Unchanged.)X
+776 3192(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(current)X
+1706(character)X
+2022(minus)X
+7 f
+2237(count)X
+1 f
+(,)S
+2518(or)X
+2606(the)X
+2725(\256rst)X
+2870(character)X
+3187(if)X
+3257(count)X
+3456(is)X
+3530(greater)X
+3775(than)X
+3934(the)X
+1136 3282(number)N
+1401(of)X
+1488(characters)X
+1835(in)X
+1917(the)X
+2035(line)X
+2175(before)X
+2401(the)X
+2519(cursor.)X
+776 3372(Options:)N
+1136(None.)X
+3 f
+576 3552([buffer])N
+864([count])X
+1129(Y)X
+1 f
+776 3642(Copy)N
+969(\(or)X
+1083(``yank''\))X
+7 f
+1394(count)X
+1 f
+1654(lines)X
+1825(into)X
+1969(the)X
+2087(speci\256ed)X
+2392(buffer.)X
+776 3822(Line:)N
+1136(Unchanged.)X
+776 3912(Column:)N
+1136(Unchanged.)X
+776 4002(Options:)N
+1136(None.)X
+3 f
+576 4182(ZZ)N
+1 f
+776 4272(Write)N
+979(the)X
+1097(\256le)X
+1219(and)X
+1356(exit)X
+3 f
+1497(vi)X
+1 f
+1559(.)X
+1620(The)X
+1766(\256le)X
+1889(is)X
+1963(only)X
+2126(written)X
+2374(if)X
+2444(it)X
+2509(has)X
+2637(been)X
+2810(modi\256ed)X
+3115(since)X
+3301(the)X
+3420(last)X
+3552(complete)X
+3867(write)X
+776 4362(of)N
+863(the)X
+981(\256le)X
+1103(to)X
+1185(any)X
+1321(\256le.)X
+776 4542(The)N
+3 f
+922(ZZ)X
+1 f
+1050(command)X
+1388(will)X
+1534(exit)X
+1676(the)X
+1796(editor)X
+2005(after)X
+2175(writing)X
+2428(the)X
+2548(\256le,)X
+2692(if)X
+2763(there)X
+2946(are)X
+3067(no)X
+3169(further)X
+3410(\256les)X
+3565(to)X
+3649(edit.)X
+3831(Enter-)X
+776 4632(ing)N
+901(two)X
+1044(``quit'')X
+1299(commands)X
+1669(\(i.e.)X
+3 f
+1837(wq)X
+1 f
+1939(,)X
+3 f
+1981(quit)X
+1 f
+2118(,)X
+3 f
+2160(xit)X
+1 f
+2271(or)X
+3 f
+2360(ZZ)X
+1 f
+2466(\))X
+2515(in)X
+2599(a)X
+2657(row)X
+2804(will)X
+2950(override)X
+3240(this)X
+3377(check)X
+3587(and)X
+3725(the)X
+3845(editor)X
+776 4722(will)N
+920(exit,)X
+1080(ignoring)X
+1371(any)X
+1507(\256les)X
+1660(that)X
+1800(have)X
+1972(not)X
+2094(yet)X
+2212(been)X
+2384(edited.)X
+776 4902(Line:)N
+1136(Unchanged.)X
+776 4992(Column:)N
+1136(Unchanged.)X
+776 5082(Options:)N
+1136(None.)X
+3 f
+576 5262([count])N
+841([[)X
+1 f
+776 5352(Back)N
+961(up)X
+7 f
+1061(count)X
+1 f
+1321(section)X
+1568(boundaries.)X
+776 5532(The)N
+3 f
+925([[)X
+1 f
+1003(command)X
+1343(is)X
+1420(an)X
+1520(absolute)X
+1811(movement.)X
+2213(The)X
+3 f
+2362([[)X
+1 f
+2440(command)X
+2780(may)X
+2942(be)X
+3042(used)X
+3213(as)X
+3304(the)X
+3426(motion)X
+3676(component)X
+776 5622(of)N
+871(other)X
+3 f
+1064(vi)X
+1 f
+1153(commands,)X
+1547(in)X
+1636(which)X
+1859(case)X
+2025(any)X
+2168(text)X
+2315(copied)X
+2556(into)X
+2707(a)X
+2770(buffer)X
+2994(is)X
+3074(character)X
+3397(oriented,)X
+3707(unless)X
+3934(the)X
+776 5712(starting)N
+1036(position)X
+1313(is)X
+1386(column)X
+1646(0,)X
+1726(in)X
+1808(which)X
+2024(case)X
+2183(it)X
+2247(is)X
+2320(line)X
+2460(oriented.)X
+
+25 p
+%%Page: 25 24
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-25)X
+1 f
+776 762(This)N
+938(is)X
+1011(an)X
+1107(error)X
+1284(if)X
+1353(the)X
+1471(movement)X
+1829(is)X
+1902(past)X
+2051(the)X
+2169(beginning)X
+2509(of)X
+2596(the)X
+2714(\256le.)X
+776 942(Line:)N
+1136(Set)X
+1261(to)X
+1346(the)X
+1467(previous)X
+1766(line)X
+1909(that)X
+2052(is)X
+7 f
+2128(count)X
+1 f
+2391(section)X
+2641(boundaries)X
+3016(back,)X
+3212(or)X
+3303(the)X
+3425(\256rst)X
+3573(line)X
+3717(of)X
+3808(the)X
+3930(\256le)X
+1136 1032(if)N
+1205(no)X
+1305(more)X
+1490(section)X
+1737(boundaries)X
+2109(exist)X
+2280(preceding)X
+2617(the)X
+2735(current)X
+2983(line.)X
+776 1122(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 1212(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(sections)X
+1 f
+1943(option.)X
+3 f
+576 1392([count])N
+841(]])X
+1 f
+776 1482(Move)N
+983(forward)X
+7 f
+1258(count)X
+1 f
+1518(section)X
+1765(boundaries.)X
+776 1662(The)N
+3 f
+925(]])X
+1 f
+1003(command)X
+1343(is)X
+1420(an)X
+1520(absolute)X
+1811(movement.)X
+2213(The)X
+3 f
+2362(]])X
+1 f
+2440(command)X
+2780(may)X
+2942(be)X
+3042(used)X
+3213(as)X
+3304(the)X
+3426(motion)X
+3676(component)X
+776 1752(of)N
+871(other)X
+3 f
+1064(vi)X
+1 f
+1153(commands,)X
+1547(in)X
+1636(which)X
+1859(case)X
+2025(any)X
+2168(text)X
+2315(copied)X
+2556(into)X
+2707(a)X
+2770(buffer)X
+2994(is)X
+3074(character)X
+3397(oriented,)X
+3707(unless)X
+3934(the)X
+776 1842(starting)N
+1036(position)X
+1313(is)X
+1386(column)X
+1646(0,)X
+1726(in)X
+1808(which)X
+2024(case)X
+2183(it)X
+2247(is)X
+2320(line)X
+2460(oriented.)X
+776 2022(This)N
+938(is)X
+1011(an)X
+1107(error)X
+1284(if)X
+1353(the)X
+1471(movement)X
+1829(is)X
+1902(past)X
+2051(the)X
+2169(end)X
+2305(of)X
+2392(the)X
+2510(\256le.)X
+776 2202(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(that)X
+1738(is)X
+7 f
+1811(count)X
+1 f
+2072(section)X
+2320(boundaries)X
+2693(forward,)X
+2989(or)X
+3077(to)X
+3160(the)X
+3279(last)X
+3411(line)X
+3552(of)X
+3640(the)X
+3759(\256le)X
+3882(if)X
+3952(no)X
+1136 2292(more)N
+1321(section)X
+1568(boundaries)X
+1940(exist)X
+2111(following)X
+2442(the)X
+2560(current)X
+2808(line.)X
+776 2382(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 2472(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(sections)X
+1 f
+1943(option.)X
+3 f
+576 2652(\303)N
+1 f
+776 2742(Move)N
+983(to)X
+1065(\256rst)X
+1209(nonblank)X
+1527(character)X
+1843(on)X
+1943(the)X
+2061(current)X
+2309(line.)X
+776 2922(The)N
+3 f
+922(\303)X
+1 f
+970(command)X
+1307(may)X
+1466(be)X
+1563(used)X
+1731(as)X
+1819(the)X
+1938(motion)X
+2185(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 3012(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 3192(Line:)N
+1136(Unchanged.)X
+776 3282(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(of)X
+2323(the)X
+2441(current)X
+2689(line.)X
+776 3372(Options:)N
+1136(None.)X
+3 f
+576 3552([count])N
+841(_)X
+1 f
+776 3642(Move)N
+985(down)X
+7 f
+1185(count)X
+1475(-)X
+1573(1)X
+1 f
+1643(lines,)X
+1836(to)X
+1920(the)X
+2040(\256rst)X
+2186(nonblank)X
+2506(character.)X
+2864(The)X
+3 f
+3012(_)X
+1 f
+3075(command)X
+3414(may)X
+3575(be)X
+3674(used)X
+3844(as)X
+3934(the)X
+776 3732(motion)N
+1022(component)X
+1398(of)X
+1485(other)X
+3 f
+1670(vi)X
+1 f
+1752(commands,)X
+2139(in)X
+2221(which)X
+2437(case)X
+2596(any)X
+2732(text)X
+2872(copied)X
+3106(into)X
+3250(a)X
+3306(buffer)X
+3523(is)X
+3596(line)X
+3736(oriented.)X
+776 3912(It)N
+845(is)X
+918(not)X
+1040(an)X
+1136(error)X
+1313(to)X
+1395(execute)X
+1661(the)X
+3 f
+1779(_)X
+1 f
+1839(command)X
+2175(when)X
+2369(the)X
+2487(cursor)X
+2708(is)X
+2781(on)X
+2881(the)X
+2999(\256rst)X
+3143(character)X
+3459(in)X
+3541(the)X
+3659(line.)X
+776 4092(Line:)N
+1136(The)X
+1281(current)X
+1529(line)X
+1669(plus)X
+7 f
+1822(count)X
+2110(-)X
+2206(1)X
+1 f
+(.)S
+776 4182(Column:)N
+1136(The)X
+1281(\256rst)X
+1425(nonblank)X
+1743(character)X
+2059(in)X
+2141(the)X
+2259(line.)X
+776 4272(Options:)N
+1136(None.)X
+3 f
+576 4452([count])N
+841(a)X
+1 f
+776 4542(Enter)N
+971(input)X
+1156(mode,)X
+1375(appending)X
+1730(the)X
+1850(text)X
+1992(after)X
+2162(the)X
+2282(cursor.)X
+2545(If)X
+7 f
+2621(count)X
+1 f
+2883(is)X
+2958(speci\256ed,)X
+3285(the)X
+3405(text)X
+3547(input)X
+3733(is)X
+3808(repeat-)X
+776 4632(edly)N
+934(input)X
+7 f
+1118(count)X
+1406(-)X
+1502(1)X
+1 f
+1570(more)X
+1755(times.)X
+776 4812(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 4902(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 4992(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 5082(gin)N
+1 f
+1262(options.)X
+3 f
+576 5262([count])N
+841(b)X
+1 f
+776 5352(Move)N
+986(backward)X
+7 f
+1322(count)X
+1 f
+1585(words.)X
+1844(Move)X
+2054(the)X
+2175(cursor)X
+2400(backward)X
+2737(to)X
+2823(the)X
+2945(beginning)X
+3289(of)X
+3380(a)X
+3440(word)X
+3629(by)X
+3733(repeating)X
+776 5442(the)N
+895(following)X
+1227(algorithm:)X
+1581(if)X
+1651(the)X
+1770(current)X
+2018(position)X
+2295(is)X
+2368(at)X
+2446(the)X
+2564(beginning)X
+2904(of)X
+2991(a)X
+3047(word,)X
+3252(move)X
+3450(to)X
+3532(the)X
+3650(\256rst)X
+3794(charac-)X
+776 5532(ter)N
+883(of)X
+972(the)X
+1092(preceding)X
+1431(word.)X
+1658(Otherwise,)X
+2030(the)X
+2150(current)X
+2400(position)X
+2679(moves)X
+2910(to)X
+2994(the)X
+3114(\256rst)X
+3260(character)X
+3578(of)X
+3667(the)X
+3787(word)X
+3974(at)X
+776 5622(the)N
+895(current)X
+1144(position.)X
+1462(If)X
+1537(no)X
+1638(preceding)X
+1976(word)X
+2161(exists)X
+2363(on)X
+2463(the)X
+2581(current)X
+2829(line,)X
+2989(move)X
+3187(to)X
+3269(the)X
+3387(\256rst)X
+3531(character)X
+3847(of)X
+3934(the)X
+776 5712(last)N
+907(word)X
+1092(on)X
+1192(the)X
+1310(\256rst)X
+1454(preceding)X
+1791(line)X
+1931(that)X
+2071(contains)X
+2358(a)X
+2414(word.)X
+
+26 p
+%%Page: 26 25
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-26)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(The)N
+3 f
+921(b)X
+1 f
+985(command)X
+1321(may)X
+1479(be)X
+1575(used)X
+1742(as)X
+1829(the)X
+1947(motion)X
+2193(component)X
+2570(of)X
+2658(other)X
+3 f
+2844(vi)X
+1 f
+2927(commands,)X
+3315(in)X
+3398(which)X
+3615(case)X
+3775(any)X
+3912(text)X
+776 852(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 1032(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(word)X
+2259(selected.)X
+776 1122(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(of)X
+2005(the)X
+2123(word)X
+2308(selected.)X
+776 1212(Options:)N
+1136(None.)X
+3 f
+576 1392([buffer])N
+864([count])X
+1129(c)X
+1185(motion)X
+1 f
+776 1482(Change)N
+1041(a)X
+1097(region)X
+1322(of)X
+1409(text.)X
+1589(If)X
+1663(only)X
+1825(part)X
+1970(of)X
+2057(a)X
+2113(single)X
+2324(line)X
+2464(is)X
+2537(affected,)X
+2838(then)X
+2997(the)X
+3116(last)X
+3248(character)X
+3565(being)X
+3764(changed)X
+776 1572(is)N
+849(marked)X
+1110(with)X
+1272(a)X
+1328(``)X
+7 f
+1382($)X
+1 f
+(''.)S
+1544(Otherwise,)X
+1914(the)X
+2032(region)X
+2257(of)X
+2344(text)X
+2484(is)X
+2557(deleted,)X
+2829(and)X
+2965(input)X
+3149(mode)X
+3347(is)X
+3420(entered.)X
+776 1752(If)N
+7 f
+850(count)X
+1 f
+1110(is)X
+1183(speci\256ed,)X
+1508(it)X
+1572(is)X
+1645(applied)X
+1901(to)X
+1983(the)X
+7 f
+2101(motion)X
+1 f
+(.)S
+776 1932(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 2022(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 2112(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 2202(gin)N
+1 f
+1262(options.)X
+3 f
+576 2382([buffer])N
+864([count])X
+1129(d)X
+1193(motion)X
+1 f
+776 2472(Delete)N
+1006(a)X
+1062(region)X
+1287(of)X
+1374(text.)X
+1554(If)X
+7 f
+1628(count)X
+1 f
+1888(is)X
+1961(speci\256ed,)X
+2286(it)X
+2350(is)X
+2423(applied)X
+2679(to)X
+2761(the)X
+7 f
+2879(motion)X
+1 f
+(.)S
+776 2652(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(where)X
+1815(the)X
+1933(region)X
+2158(starts.)X
+776 2742(Column:)N
+1136(Set)X
+1261(to)X
+1346(the)X
+1467(\256rst)X
+1615(character)X
+1935(in)X
+2021(the)X
+2143(line)X
+2287(after)X
+2459(the)X
+2581(last)X
+2716(character)X
+3036(in)X
+3122(the)X
+3244(region.)X
+3513(If)X
+3591(no)X
+3695(such)X
+3866(char-)X
+1136 2832(acter)N
+1313(exists,)X
+1535(set)X
+1644(to)X
+1726(the)X
+1844(last)X
+1975(character)X
+2291(before)X
+2517(the)X
+2635(region.)X
+776 2922(Options:)N
+1136(None.)X
+3 f
+576 3102([count])N
+841(e)X
+1 f
+776 3192(Move)N
+984(forward)X
+7 f
+1260(count)X
+1 f
+1521(end-of-words.)X
+2015(Move)X
+2223(the)X
+2342(cursor)X
+2564(forward)X
+2840(to)X
+2923(the)X
+3042(end)X
+3179(of)X
+3267(a)X
+3324(word)X
+3511(by)X
+3613(repeating)X
+3934(the)X
+776 3282(following)N
+1108(algorithm:)X
+1462(if)X
+1532(the)X
+1651(current)X
+1900(position)X
+2178(is)X
+2252(the)X
+2371(end)X
+2508(of)X
+2596(a)X
+2653(word,)X
+2859(move)X
+3058(to)X
+3141(the)X
+3260(last)X
+3392(character)X
+3709(of)X
+3797(the)X
+3916(fol-)X
+776 3372(lowing)N
+1034(word.)X
+1275(Otherwise,)X
+1661(move)X
+1876(to)X
+1975(the)X
+2110(last)X
+2258(character)X
+2591(of)X
+2695(the)X
+2830(word)X
+3032(at)X
+3127(the)X
+3262(current)X
+3527(position.)X
+3861(If)X
+3952(no)X
+776 3462(succeeding)N
+1161(word)X
+1354(exists)X
+1564(on)X
+1672(the)X
+1798(current)X
+2053(line,)X
+2220(move)X
+2425(to)X
+2514(the)X
+2639(last)X
+2777(character)X
+3100(of)X
+3194(the)X
+3319(\256rst)X
+3470(word)X
+3662(on)X
+3769(the)X
+3894(next)X
+776 3552(following)N
+1107(line)X
+1247(that)X
+1387(contains)X
+1674(a)X
+1730(word.)X
+776 3732(The)N
+3 f
+921(e)X
+1 f
+978(command)X
+1315(may)X
+1474(be)X
+1571(used)X
+1739(as)X
+1827(the)X
+1946(motion)X
+2193(component)X
+2570(of)X
+2658(other)X
+3 f
+2844(vi)X
+1 f
+2927(commands,)X
+3315(in)X
+3398(which)X
+3615(case)X
+3775(any)X
+3912(text)X
+776 3822(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 4002(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(word)X
+2259(selected.)X
+776 4092(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(of)X
+1992(the)X
+2110(word)X
+2295(selected.)X
+776 4182(Options:)N
+1136(None.)X
+3 f
+576 4362([count])N
+841(f)X
+888 0.3125(<character>)AX
+1 f
+776 4452(Search)N
+1015(forward,)X
+7 f
+1310(count)X
+1 f
+1570(times,)X
+1783(through)X
+2052(the)X
+2170(rest)X
+2306(of)X
+2393(the)X
+2511(current)X
+2759(line)X
+2899(for)X
+7 f
+3013(<character>)X
+1 f
+(.)S
+776 4632(The)N
+3 f
+922(f)X
+1 f
+970(command)X
+1307(may)X
+1466(be)X
+1563(used)X
+1731(as)X
+1819(the)X
+1938(motion)X
+2185(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 4722(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 4902(Line:)N
+1136(Unchanged.)X
+776 4992(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458 0.3068(searched-for)AX
+1881(character.)X
+776 5082(Options:)N
+1136(None.)X
+3 f
+576 5262([count])N
+841(i)X
+1 f
+776 5352(Enter)N
+971(input)X
+1156(mode,)X
+1375(inserting)X
+1676(the)X
+1795(text)X
+1936(before)X
+2163(the)X
+2282(cursor.)X
+2545(If)X
+7 f
+2621(count)X
+1 f
+2883(is)X
+2958(speci\256ed,)X
+3285(the)X
+3405(text)X
+3547(input)X
+3733(is)X
+3808(repeat-)X
+776 5442(edly)N
+934(input)X
+7 f
+1118(count)X
+1406(-)X
+1502(1)X
+1 f
+1570(more)X
+1755(times.)X
+776 5622(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 5712(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+
+27 p
+%%Page: 27 26
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-27)X
+1 f
+776 762(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 852(gin)N
+1 f
+1262(options.)X
+3 f
+576 1032(m)N
+663 0.3125(<character>)AX
+1 f
+776 1122(Save)N
+956(the)X
+1078(current)X
+1330(context)X
+1590(\(line)X
+1761(and)X
+1902(column\))X
+2194(as)X
+7 f
+2286(<character>)X
+1 f
+(.)S
+2879(The)X
+3029(exact)X
+3224(position)X
+3506(is)X
+3584(referred)X
+3865(to)X
+3952(by)X
+776 1212(``)N
+7 f
+830(`<character>)X
+1 f
+(''.)S
+1520(The)X
+1665(line)X
+1805(is)X
+1878(referred)X
+2154(to)X
+2236(by)X
+2336(``)X
+7 f
+2390('<character>)X
+1 f
+(''.)S
+776 1392(Historically,)N
+7 f
+1205(<character>)X
+1 f
+1764(was)X
+1920(restricted)X
+2250(to)X
+2343(lower-case)X
+2723(letters)X
+2950(only,)X
+3 f
+3143(nvi)X
+1 f
+3280(permits)X
+3551(the)X
+3680(use)X
+3818(of)X
+3916(any)X
+776 1482(character.)N
+776 1662(Line:)N
+1136(Unchanged.)X
+776 1752(Column:)N
+1136(Unchanged.)X
+776 1842(Options:)N
+1136(None.)X
+3 f
+576 2022([count])N
+841(o)X
+1 f
+776 2112(Enter)N
+970(input)X
+1154(mode,)X
+1372(appending)X
+1727(text)X
+1868(in)X
+1951(a)X
+2008(new)X
+2163(line)X
+2304(under)X
+2508(the)X
+2627(current)X
+2876(line.)X
+3057(If)X
+7 f
+3132(count)X
+1 f
+3393(is)X
+3467(speci\256ed,)X
+3793(the)X
+3912(text)X
+776 2202(input)N
+960(is)X
+1033(repeatedly)X
+1388(input)X
+7 f
+1572(count)X
+1860(-)X
+1956(1)X
+1 f
+2024(more)X
+2209(times.)X
+776 2382(Historically,)N
+1194(any)X
+7 f
+1330(count)X
+1 f
+1590(speci\256ed)X
+1895(to)X
+1977(the)X
+3 f
+2095(o)X
+1 f
+2155(command)X
+2491(was)X
+2636(ignored.)X
+776 2562(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 2652(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 2742(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 2832(gin)N
+1 f
+1262(options.)X
+3 f
+576 3012([buffer])N
+864(p)X
+1 f
+776 3102(Append)N
+1055(text)X
+1200(from)X
+1381(a)X
+1442(buffer.)X
+1704(Text)X
+1876(from)X
+2057(the)X
+2180(buffer)X
+2402(\(the)X
+2552(unnamed)X
+2871(buffer)X
+3093(by)X
+3198(default\))X
+3473(is)X
+3551(appended)X
+3884(after)X
+776 3192(the)N
+894(current)X
+1142(column)X
+1402(or,)X
+1509(if)X
+1578(the)X
+1696(buffer)X
+1913(is)X
+1986(line)X
+2126(oriented,)X
+2429(after)X
+2597(the)X
+2715(current)X
+2963(line.)X
+776 3372(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(line)X
+1742(appended,)X
+2090(if)X
+2159(the)X
+2277(buffer)X
+2494(is)X
+2567(line)X
+2707(oriented,)X
+3010(otherwise)X
+3342(unchanged.)X
+776 3462(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2237(of)X
+2325(the)X
+2444(appended)X
+2773(text)X
+2914(if)X
+2984(the)X
+3103(buffer)X
+3321(is)X
+3395(line)X
+3536(oriented,)X
+3840(other-)X
+1136 3552(wise,)N
+1323(the)X
+1441(last)X
+1572(character)X
+1888(of)X
+1975(the)X
+2093(appended)X
+2421(text.)X
+776 3642(Options:)N
+1136(None.)X
+3 f
+576 3822([count])N
+841(r)X
+897 0.3125(<character>)AX
+1 f
+776 3912(Replace)N
+1073(characters.)X
+1478(The)X
+1641(next)X
+7 f
+1817(count)X
+1 f
+2095(characters)X
+2460(in)X
+2560(the)X
+2696(line)X
+2854(are)X
+2991(replaced)X
+3303(with)X
+7 f
+3484(<character>)X
+1 f
+(.)S
+776 4002(Replacing)N
+1121(characters)X
+1468(with)X
+7 f
+1630(<newline>)X
+1 f
+2082(characters)X
+2429(results)X
+2658(in)X
+2740(creating)X
+3019(new,)X
+3193(empty)X
+3413(lines)X
+3584(into)X
+3728(the)X
+3846(\256le.)X
+776 4182(If)N
+7 f
+850(<character>)X
+1 f
+1398(is)X
+7 f
+1471(<escape>)X
+1 f
+(,)S
+1895(the)X
+2013(command)X
+2349(is)X
+2422(cancelled.)X
+776 4362(Line:)N
+1136(Unchanged)X
+1527(unless)X
+1752(the)X
+1875(replacement)X
+2293(character)X
+2614(is)X
+2692(a)X
+7 f
+2753(<newline>)X
+1 f
+(,)S
+3231(in)X
+3319(which)X
+3541(case)X
+3706(it)X
+3776(is)X
+3855(set)X
+3970(to)X
+1136 4452(the)N
+1254(current)X
+1502(line)X
+1642(plus)X
+7 f
+1795(count)X
+2083(-)X
+2179(1)X
+1 f
+(.)S
+776 4542(Column:)N
+1136(Set)X
+1264(to)X
+1352(the)X
+1476(last)X
+1613(character)X
+1935(replaced,)X
+2254(unless)X
+2480(the)X
+2605(replacement)X
+3025(character)X
+3348(is)X
+3428(a)X
+7 f
+3491(<newline>)X
+1 f
+(,)S
+3970(in)X
+1136 4632(which)N
+1352(case)X
+1511(the)X
+1629(cursor)X
+1850(is)X
+1923(in)X
+2005(column)X
+2265(1)X
+2325(of)X
+2412(the)X
+2530(last)X
+2661(line)X
+2801(inserted.)X
+776 4722(Options:)N
+1136(None.)X
+3 f
+576 4902([buffer])N
+864([count])X
+1129(s)X
+1 f
+776 4992(Substitute)N
+7 f
+1115(count)X
+1 f
+1375(characters)X
+1722(in)X
+1804(the)X
+1922(current)X
+2170(line)X
+2310(starting)X
+2570(with)X
+2732(the)X
+2850(current)X
+3098(character.)X
+776 5172(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(line)X
+1729(upon)X
+1909(which)X
+2125(characters)X
+2472(were)X
+2649(entered.)X
+776 5262(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(last)X
+1589(character)X
+1905(entered.)X
+776 5352(Options:)N
+1136(Affected)X
+1440(by)X
+1542(the)X
+3 f
+1662(altwerase)X
+1 f
+1988(,)X
+3 f
+2030(autoindent)X
+1 f
+2398(,)X
+3 f
+2440(beautify)X
+1 f
+(,)S
+3 f
+2762(showmatch)X
+1 f
+3149(,)X
+3 f
+3191(ttywerase)X
+1 f
+3545(and)X
+3 f
+3684(wrapmar-)X
+1136 5442(gin)N
+1 f
+1262(options.)X
+3 f
+576 5622([count])N
+841(t)X
+888 0.3125(<character>)AX
+1 f
+776 5712(Search)N
+1014(forward,)X
+7 f
+1308(count)X
+1 f
+1567(times,)X
+1779(through)X
+2047(the)X
+2164(current)X
+2411(line)X
+2551(for)X
+2665(the)X
+2783(character)X
+3099(immediately)X
+2 f
+3519(before)X
+7 f
+3744(<char-)X
+776 5802(acter>)N
+1 f
+(.)S
+
+28 p
+%%Page: 28 27
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-28)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(The)N
+3 f
+922(t)X
+1 f
+970(command)X
+1307(may)X
+1466(be)X
+1563(used)X
+1731(as)X
+1819(the)X
+1938(motion)X
+2185(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 852(copied)N
+1010(into)X
+1154(a)X
+1210(buffer)X
+1427(is)X
+1500(character)X
+1816(oriented.)X
+776 1032(Line:)N
+1136(Unchanged.)X
+776 1122(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+2 f
+1774(before)X
+1 f
+1999(the)X
+2117 0.3068(searched-for)AX
+2540(character.)X
+776 1212(Options:)N
+1136(None.)X
+3 f
+576 1392(u)N
+1 f
+776 1482(Undo)N
+985(the)X
+1114(last)X
+1256(change)X
+1515(made)X
+1720(to)X
+1813(the)X
+1942(\256le.)X
+2115(If)X
+2200(repeated,)X
+2524(the)X
+3 f
+2653(u)X
+1 f
+2728(command)X
+3075(alternates)X
+3415(between)X
+3715(these)X
+3912(two)X
+776 1572(states,)N
+997(and)X
+1136(is)X
+1212(its)X
+1310(own)X
+1471(inverse.)X
+1766(When)X
+1981(used)X
+2151(after)X
+2322(an)X
+2421(insert)X
+2622(that)X
+2765(inserted)X
+3042(text)X
+3185(on)X
+3287(more)X
+3474(than)X
+3634(one)X
+3772(line,)X
+3934(the)X
+776 1662(lines)N
+947(are)X
+1066(saved)X
+1269(in)X
+1351(the)X
+1469(numeric)X
+1752(buffers.)X
+776 1842(The)N
+3 f
+928(.)X
+1 f
+995(command,)X
+1358(when)X
+1560(used)X
+1735(immediately)X
+2163(after)X
+2339(the)X
+3 f
+2465(u)X
+1 f
+2537(command,)X
+2901(causes)X
+3139(the)X
+3265(change)X
+3521(log)X
+3651(to)X
+3741(be)X
+3845(rolled)X
+776 1932(forward)N
+1051(or)X
+1138(backward,)X
+1491(depending)X
+1845(on)X
+1945(the)X
+2063(action)X
+2279(of)X
+2366(the)X
+3 f
+2484(u)X
+1 f
+2548(command.)X
+776 2112(Line:)N
+1136(Set)X
+1276(to)X
+1376(the)X
+1512(position)X
+1807(of)X
+1912(the)X
+2048(\256rst)X
+2211(line)X
+2370(changed,)X
+2697(if)X
+2785(the)X
+2922(reversal)X
+3216(affects)X
+3470(only)X
+3651(one)X
+3806(line)X
+3965(or)X
+1136 2202(represents)N
+1482(an)X
+1578(addition)X
+1860(or)X
+1947(change;)X
+2217(otherwise,)X
+2569(the)X
+2687(line)X
+2827(preceding)X
+3164(the)X
+3282(deleted)X
+3534(text.)X
+776 2292(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(cursor)X
+1679(position)X
+1956(before)X
+2182(the)X
+2300(change)X
+2548(was)X
+2693(made.)X
+776 2382(Options:)N
+1136(None.)X
+3 f
+576 2562([count])N
+841(w)X
+1 f
+776 2652(Move)N
+986(forward)X
+7 f
+1264(count)X
+1 f
+1527(words.)X
+1786(Move)X
+1996(the)X
+2117(cursor)X
+2341(forward)X
+2619(to)X
+2704(the)X
+2825(beginning)X
+3168(of)X
+3258(a)X
+3318(word)X
+3507(by)X
+3611(repeating)X
+3934(the)X
+776 2742(following)N
+1111(algorithm:)X
+1468(if)X
+1541(the)X
+1663(current)X
+1915(position)X
+2196(is)X
+2273(at)X
+2355(the)X
+2477(beginning)X
+2821(of)X
+2912(a)X
+2972(word,)X
+3181(move)X
+3383(to)X
+3468(the)X
+3589(\256rst)X
+3736(character)X
+776 2832(of)N
+866(the)X
+987(next)X
+1148(word.)X
+1376(If)X
+1453(no)X
+1556(subsequent)X
+1935(word)X
+2123(exists)X
+2328(on)X
+2431(the)X
+2552(current)X
+2803(line,)X
+2966(move)X
+3167(to)X
+3253(the)X
+3375(\256rst)X
+3523(character)X
+3843(of)X
+3934(the)X
+776 2922(\256rst)N
+920(word)X
+1105(on)X
+1205(the)X
+1323(\256rst)X
+1467(following)X
+1798(line)X
+1938(that)X
+2078(contains)X
+2365(a)X
+2421(word.)X
+776 3102(The)N
+3 f
+928(w)X
+1 f
+1014(command)X
+1358(may)X
+1524(be)X
+1628(used)X
+1803(as)X
+1898(the)X
+2024(motion)X
+2278(component)X
+2662(of)X
+2757(other)X
+3 f
+2950(vi)X
+1 f
+3040(commands,)X
+3435(in)X
+3525(which)X
+3749(case)X
+3916(any)X
+776 3192(text)N
+916(copied)X
+1150(into)X
+1294(a)X
+1350(buffer)X
+1567(is)X
+1640(character)X
+1956(oriented.)X
+776 3372(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(word)X
+2259(selected.)X
+776 3462(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(character)X
+1918(of)X
+2005(the)X
+2123(word)X
+2308(selected.)X
+776 3552(Options:)N
+1136(None.)X
+3 f
+576 3732([buffer])N
+864([count])X
+1129(x)X
+1 f
+776 3822(Delete)N
+7 f
+1007(count)X
+1 f
+1268(characters.)X
+1656(The)X
+1802(deletion)X
+2081(is)X
+2155(at)X
+2235(the)X
+2355(current)X
+2605(character)X
+2923(position.)X
+3242(If)X
+3318(the)X
+3438(number)X
+3705(of)X
+3794(charac-)X
+776 3912(ters)N
+915(to)X
+1000(be)X
+1099(deleted)X
+1354(is)X
+1430(greater)X
+1676(than)X
+1836(or)X
+1925(equal)X
+2121(to)X
+2205(the)X
+2325(number)X
+2592(of)X
+2681(characters)X
+3030(to)X
+3114(the)X
+3234(end)X
+3372(of)X
+3461(the)X
+3581(line,)X
+3743(all)X
+3845(of)X
+3934(the)X
+776 4002(characters)N
+1123(from)X
+1299(the)X
+1417(current)X
+1665(cursor)X
+1886(position)X
+2163(to)X
+2245(the)X
+2363(end)X
+2499(of)X
+2586(the)X
+2704(line)X
+2844(are)X
+2963(deleted.)X
+776 4182(Line:)N
+1136(Unchanged.)X
+776 4272(Column:)N
+1136(Unchanged)X
+1525(unless)X
+1748(the)X
+1869(last)X
+2003(character)X
+2322(in)X
+2407(the)X
+2528(line)X
+2671(is)X
+2747(deleted)X
+3002(and)X
+3141(the)X
+3263(cursor)X
+3488(is)X
+3565(not)X
+3691(already)X
+3952(on)X
+1136 4362(the)N
+1254(\256rst)X
+1398(character)X
+1714(in)X
+1796(the)X
+1914(line,)X
+2074(in)X
+2156(which)X
+2372(case)X
+2531(it)X
+2595(is)X
+2668(set)X
+2777(to)X
+2859(the)X
+2977(previous)X
+3273(character.)X
+776 4452(Options:)N
+1136(None.)X
+3 f
+576 4632([buffer])N
+864([count])X
+1129(y)X
+1189(motion)X
+1 f
+776 4722(Copy)N
+981(\(or)X
+1107(``yank''\))X
+1430(a)X
+1498(text)X
+1650(region)X
+1887(speci\256ed)X
+2204(by)X
+2316(the)X
+7 f
+2446(count)X
+1 f
+2718(and)X
+2866(motion)X
+3124(into)X
+3280(a)X
+3349(buffer.)X
+3619(If)X
+7 f
+3706(count)X
+1 f
+3979(is)X
+776 4812(speci\256ed,)N
+1101(it)X
+1165(is)X
+1238(applied)X
+1494(to)X
+1576(the)X
+7 f
+1694(motion)X
+1 f
+(.)S
+776 4992(Line:)N
+1136(Unchanged,)X
+1544(unless)X
+1766(the)X
+1886(region)X
+2113(covers)X
+2345(more)X
+2532(than)X
+2692(a)X
+2751(single)X
+2965(line,)X
+3128(in)X
+3213(which)X
+3432(case)X
+3594(it)X
+3661(is)X
+3737(set)X
+3849(to)X
+3934(the)X
+1136 5082(line)N
+1276(where)X
+1493(the)X
+1611(region)X
+1836(starts.)X
+776 5172(Column:)N
+1136(Unchanged,)X
+1544(unless)X
+1766(the)X
+1886(region)X
+2113(covers)X
+2345(more)X
+2532(than)X
+2692(a)X
+2751(single)X
+2965(line,)X
+3128(in)X
+3213(which)X
+3432(case)X
+3594(it)X
+3661(is)X
+3737(set)X
+3849(to)X
+3934(the)X
+1136 5262(character)N
+1452(were)X
+1629(the)X
+1747(region)X
+1972(starts.)X
+776 5352(Options:)N
+1136(None.)X
+3 f
+576 5532([count1])N
+881(z)X
+937([count2])X
+1242(type)X
+1 f
+776 5622(Redraw)N
+1049(the)X
+1170(screen)X
+1399(with)X
+1564(a)X
+1623(window)X
+7 f
+1904(count2)X
+1 f
+2215(lines)X
+2389(long,)X
+2574(with)X
+2740(line)X
+7 f
+2884(count1)X
+1 f
+3196(placed)X
+3430(as)X
+3521(speci\256ed)X
+3830(by)X
+3934(the)X
+7 f
+776 5712(type)N
+1 f
+1005(character.)X
+1378(If)X
+7 f
+1469(count1)X
+1 f
+1794(is)X
+1884(not)X
+2023(speci\256ed,)X
+2365(it)X
+2445(defaults)X
+2735(to)X
+2833(the)X
+2967(current)X
+3231(line.)X
+3427(If)X
+7 f
+3517(count2)X
+1 f
+3841(is)X
+3930(not)X
+776 5802(speci\256ed,)N
+1101(it)X
+1165(defaults)X
+1439(to)X
+1521(the)X
+1639(current)X
+1887(window)X
+2165(size.)X
+
+29 p
+%%Page: 29 28
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-29)X
+1 f
+776 762(The)N
+921(following)X
+7 f
+1252(type)X
+1 f
+1464(characters)X
+1811(may)X
+1969(be)X
+2065(used:)X
+776 942(+)N
+1136(If)X
+7 f
+1225(count1)X
+1 f
+1548(is)X
+1636(speci\256ed,)X
+1977(place)X
+2183(the)X
+2317(line)X
+7 f
+2473(count1)X
+1 f
+2797(at)X
+2891(the)X
+3025(top)X
+3163(of)X
+3266(the)X
+3400(screen.)X
+3682(Otherwise,)X
+1136 1032(display)N
+1387(the)X
+1505(screen)X
+1731(after)X
+1899(the)X
+2017(current)X
+2265(screen,)X
+2511(similarly)X
+2815(to)X
+2897(the)X
+3 f
+3015(<control-F>)X
+1 f
+3448(command.)X
+776 1122 0.2266(<carriage-return>)AN
+1136 1212(Place)N
+1330(the)X
+1448(line)X
+7 f
+1588(count1)X
+1 f
+1896(at)X
+1974(the)X
+2092(top)X
+2214(of)X
+2301(the)X
+2419(screen.)X
+776 1302(.)N
+1136(Place)X
+1330(the)X
+1448(line)X
+7 f
+1588(count1)X
+1 f
+1896(in)X
+1978(the)X
+2096(center)X
+2313(of)X
+2400(the)X
+2518(screen.)X
+9 f
+776 1392(-)N
+1 f
+1136(Place)X
+1330(the)X
+1448(line)X
+7 f
+1588(count1)X
+1 f
+1896(at)X
+1974(the)X
+2092(bottom)X
+2338(of)X
+2425(the)X
+2543(screen.)X
+776 1482(\303)N
+1136(If)X
+7 f
+1214(count1)X
+1 f
+1526(is)X
+1603(speci\256ed,)X
+1932(place)X
+2126(the)X
+2248(line)X
+2392(that)X
+2536(is)X
+2613(at)X
+2695(the)X
+2817(top)X
+2943(of)X
+3034(the)X
+3156(screen)X
+3386(when)X
+7 f
+3584(count1)X
+1 f
+3896(is)X
+3974(at)X
+1136 1572(the)N
+1264(bottom)X
+1520(of)X
+1617(the)X
+1745(screen,)X
+2001(at)X
+2089(the)X
+2217(bottom)X
+2472(of)X
+2568(the)X
+2695(screen,)X
+2950(i.e.)X
+3077(display)X
+3337(the)X
+3464(screen)X
+3699(before)X
+3934(the)X
+1136 1662(screen)N
+1362(before)X
+7 f
+1588(count1)X
+1 f
+(.)S
+1937(Otherwise,)X
+2308(display)X
+2560(the)X
+2679(screen)X
+2906(before)X
+3133(the)X
+3252(current)X
+3501(screen,)X
+3748(similarly)X
+1136 1752(to)N
+1218(the)X
+3 f
+1336(<control-B>)X
+1 f
+1773(command.)X
+776 1932(Line:)N
+1136(Set)X
+1263(to)X
+7 f
+1350(count1)X
+1 f
+1663(unless)X
+7 f
+1888(count1)X
+1 f
+2201(is)X
+2279(not)X
+2406(speci\256ed)X
+2716(and)X
+2857(the)X
+7 f
+2980(type)X
+1 f
+3197(character)X
+3518(was)X
+3668(either)X
+3876(``)X
+7 f
+3930(\303)X
+1 f
+('')S
+1136 2022(or)N
+1224(``)X
+7 f
+1278(+)X
+1 f
+('',)S
+1421(in)X
+1504(which)X
+1721(case)X
+1881(it)X
+1946(is)X
+2020(set)X
+2130(to)X
+2213(the)X
+2332(line)X
+2473(before)X
+2700(the)X
+2819(\256rst)X
+2964(line)X
+3105(on)X
+3206(the)X
+3325(previous)X
+3621(screen)X
+3847(or)X
+3934(the)X
+1136 2112(line)N
+1276(after)X
+1444(the)X
+1562(last)X
+1693(line)X
+1833(on)X
+1933(the)X
+2051(previous)X
+2347(screen,)X
+2593(respectively.)X
+776 2202(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 2292(Options:)N
+1136(None.)X
+3 f
+576 2472([count])N
+841({)X
+1 f
+776 2562(Move)N
+983(backward)X
+7 f
+1316(count)X
+1 f
+1576(paragraphs.)X
+776 2742(The)N
+3 f
+922({)X
+1 f
+975(command)X
+1312(is)X
+1386(an)X
+1483(absolute)X
+1771(movement.)X
+2170(The)X
+3 f
+2316({)X
+1 f
+2369(command)X
+2706(may)X
+2865(be)X
+2962(used)X
+3130(as)X
+3219(the)X
+3339(motion)X
+3587(component)X
+3965(of)X
+776 2832(other)N
+3 f
+963(vi)X
+1 f
+1047(commands,)X
+1436(in)X
+1520(which)X
+1738(case)X
+1899(any)X
+2036(text)X
+2177(copied)X
+2412(into)X
+2557(a)X
+2614(buffer)X
+2832(is)X
+2906(character)X
+3223(oriented,)X
+3527(unless)X
+3748(the)X
+3867(start-)X
+776 2922(ing)N
+898(character)X
+1214(is)X
+1287(the)X
+1405(\256rst)X
+1549(character)X
+1865(on)X
+1965(its)X
+2060(line,)X
+2220(in)X
+2302(which)X
+2518(case)X
+2677(it)X
+2741(is)X
+2814(line)X
+2954(oriented.)X
+776 3102(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(beginning)X
+2414(of)X
+2501(the)X
+2619(previous)X
+2915(paragraph.)X
+776 3192(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 3282(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(paragraph)X
+1 f
+2040(option.)X
+3 f
+576 3462([count])N
+841(|)X
+1 f
+776 3552(Move)N
+983(to)X
+1065(a)X
+1121(speci\256c)X
+2 f
+1386(column)X
+1 f
+1642(position)X
+1919(on)X
+2019(the)X
+2137(current)X
+2385(line.)X
+776 3732(The)N
+3 f
+922(|)X
+1 f
+962(command)X
+1300(may)X
+1460(be)X
+1558(used)X
+1727(as)X
+1816(the)X
+1936(motion)X
+2184(component)X
+2562(of)X
+2651(other)X
+3 f
+2838(vi)X
+1 f
+2922(commands,)X
+3311(in)X
+3395(which)X
+3613(case)X
+3774(any)X
+3912(text)X
+776 3822(copied)N
+1012(into)X
+1158(a)X
+1216(buffer)X
+1435(is)X
+1510(character)X
+1828(oriented.)X
+2153(It)X
+2224(is)X
+2299(an)X
+2397(error)X
+2576(to)X
+2660(use)X
+2789(the)X
+3 f
+2908(|)X
+1 f
+2947(command)X
+3284(as)X
+3372(a)X
+3429(motion)X
+3676(component)X
+776 3912(and)N
+912(for)X
+1026(the)X
+1144(cursor)X
+1365(not)X
+1487(to)X
+1569(move.)X
+776 4092(Line:)N
+1136(Unchanged.)X
+776 4182(Column:)N
+1136(Set)X
+1265(to)X
+1355(the)X
+1481(character)X
+1805(occupying)X
+2167(the)X
+2293(column)X
+2561(position)X
+2846(identi\256ed)X
+3176(by)X
+7 f
+3284(count)X
+1 f
+(,)S
+3572(if)X
+3649(the)X
+3775(position)X
+1136 4272(exists)N
+1339(in)X
+1422(the)X
+1541(line.)X
+1722(If)X
+1797(the)X
+1916(column)X
+2176(length)X
+2396(of)X
+2483(the)X
+2601(current)X
+2849(line)X
+2989(is)X
+3062(less)X
+3202(than)X
+7 f
+3360(count)X
+1 f
+(,)S
+3640(the)X
+3758(cursor)X
+3979(is)X
+1136 4362(moved)N
+1374(to)X
+1456(the)X
+1574(last)X
+1705(character)X
+2021(in)X
+2103(the)X
+2221(line.)X
+776 4452(Options:)N
+1136(None.)X
+3 f
+576 4632([count])N
+841(})X
+1 f
+776 4722(Move)N
+983(forward)X
+7 f
+1258(count)X
+1 f
+1518(paragraphs.)X
+776 4902(The)N
+3 f
+922(})X
+1 f
+975(command)X
+1312(is)X
+1386(an)X
+1483(absolute)X
+1771(movement.)X
+2170(The)X
+3 f
+2316(})X
+1 f
+2369(command)X
+2706(may)X
+2865(be)X
+2962(used)X
+3130(as)X
+3219(the)X
+3339(motion)X
+3587(component)X
+3965(of)X
+776 4992(other)N
+3 f
+963(vi)X
+1 f
+1047(commands,)X
+1436(in)X
+1520(which)X
+1738(case)X
+1899(any)X
+2036(text)X
+2177(copied)X
+2412(into)X
+2557(a)X
+2614(buffer)X
+2832(is)X
+2906(character)X
+3223(oriented,)X
+3527(unless)X
+3748(the)X
+3867(start-)X
+776 5082(ing)N
+898(character)X
+1214(is)X
+1287(at)X
+1365(or)X
+1452(before)X
+1678(any)X
+1814(nonblank)X
+2132(characters)X
+2479(in)X
+2561(its)X
+2656(line,)X
+2816(in)X
+2898(which)X
+3114(case)X
+3273(it)X
+3337(is)X
+3410(line)X
+3550(oriented.)X
+776 5262(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(containing)X
+1956(the)X
+2074(beginning)X
+2414(of)X
+2501(the)X
+2619(next)X
+2777(paragraph.)X
+776 5352(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(\256rst)X
+1602(nonblank)X
+1920(character)X
+2236(in)X
+2318(the)X
+2436(line.)X
+776 5442(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(paragraph)X
+1 f
+2040(option.)X
+3 f
+576 5622([count])N
+841(\304)X
+1 f
+776 5712(Reverse)N
+1062(the)X
+1187(case)X
+1353(of)X
+1447(the)X
+1572(next)X
+7 f
+1738(count)X
+1 f
+2006 0.3021(character\(s\).)AX
+2455(This)X
+2625(is)X
+2706(the)X
+2832(historic)X
+3100(semantic)X
+3413(for)X
+3535(the)X
+3 f
+3661(\304)X
+1 f
+3716(command)X
+776 5802(and)N
+912(it)X
+976(is)X
+1049(only)X
+1211(in)X
+1293(effect)X
+1497(if)X
+1566(the)X
+3 f
+1684(tildeop)X
+1 f
+1939(option)X
+2163(is)X
+2236(not)X
+2358(set.)X
+
+30 p
+%%Page: 30 29
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-30)N
+2826(Nvi/Nex)X
+3122 0.3906(Reference)AX
+3487(\(Vi)X
+3614(Commands\))X
+1 f
+776 762(Lowercase)N
+1148(alphabetic)X
+1501(characters)X
+1851(are)X
+1974(changed)X
+2266(to)X
+2352(uppercase,)X
+2718(and)X
+2858(uppercase)X
+3204(characters)X
+3555(are)X
+3678(changed)X
+3970(to)X
+776 852(lowercase.)N
+1158(No)X
+1276(other)X
+1461(characters)X
+1808(are)X
+1927(affected.)X
+776 1032(Historically,)N
+1203(the)X
+3 f
+1330(\304)X
+1 f
+1386(command)X
+1731(did)X
+1863(not)X
+1995(take)X
+2159(an)X
+2265(associated)X
+2625(count,)X
+2853(nor)X
+2990(did)X
+3122(it)X
+3196(move)X
+3404(past)X
+3563(the)X
+3691(end)X
+3837(of)X
+3934(the)X
+776 1122(current)N
+1025(line.)X
+1206(As)X
+1316(it)X
+1381(had)X
+1518(no)X
+1619(associated)X
+1970(motion)X
+2217(it)X
+2282(was)X
+2428(dif\256cult)X
+2701(to)X
+2783(change)X
+3031(the)X
+3149(case)X
+3308(of)X
+3395(large)X
+3576(blocks)X
+3805(of)X
+3892(text.)X
+776 1212(In)N
+3 f
+868(nvi)X
+1 f
+974(,)X
+1019(if)X
+1093(the)X
+1216(cursor)X
+1442(is)X
+1520(on)X
+1625(the)X
+1748(last)X
+1884(character)X
+2205(of)X
+2297(a)X
+2359(line,)X
+2525(and)X
+2667(there)X
+2854(are)X
+2979(more)X
+3170(lines)X
+3347(in)X
+3435(the)X
+3559(\256le,)X
+3707(the)X
+3831(cursor)X
+776 1302(moves)N
+1005(to)X
+1087(the)X
+1205(next)X
+1363(line.)X
+776 1482(It)N
+849(is)X
+926(not)X
+1052(an)X
+1152(error)X
+1333(to)X
+1419(specify)X
+1675(a)X
+1735(count)X
+1938(larger)X
+2151(than)X
+2314(the)X
+2437(number)X
+2707(of)X
+2799(characters)X
+3151(between)X
+3444(the)X
+3567(cursor)X
+3793(and)X
+3934(the)X
+776 1572(end)N
+912(of)X
+999(the)X
+1117(\256le.)X
+776 1752(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(of)X
+1685(the)X
+1803(character)X
+2119(after)X
+7 f
+2287(count)X
+1 f
+2547(characters,)X
+2914(or,)X
+3021(end)X
+3157(of)X
+3244(\256le.)X
+776 1842(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+1774(after)X
+7 f
+1942(count)X
+1 f
+2202(characters,)X
+2569(or,)X
+2676(end-of-\256le.)X
+776 1932(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(tildeop)X
+1 f
+1911(option.)X
+3 f
+576 2112([count])N
+841(\304)X
+888(motion)X
+1 f
+776 2202(Reverse)N
+1063(the)X
+1189(case)X
+1356(of)X
+1451(the)X
+1577(characters)X
+1932(in)X
+2022(a)X
+2086(text)X
+2234(region)X
+2467(speci\256ed)X
+2780(by)X
+2888(the)X
+7 f
+3014(count)X
+1 f
+3282(and)X
+7 f
+3426(motion)X
+1 f
+(.)S
+3782(Only)X
+3970(in)X
+776 2292(effect)N
+980(if)X
+1049(the)X
+3 f
+1167(tildeop)X
+1 f
+1422(option)X
+1646(is)X
+1719(set.)X
+776 2472(Lowercase)N
+1147(characters)X
+1496(are)X
+1617(changed)X
+1908(to)X
+1993(uppercase,)X
+2358(and)X
+2497(uppercase)X
+2842(characters)X
+3192(are)X
+3314(changed)X
+3605(to)X
+3690(lowercase.)X
+776 2562(No)N
+894(other)X
+1079(characters)X
+1426(are)X
+1545(affected.)X
+776 2742(Line:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(line)X
+1598(of)X
+1685(the)X
+1803(character)X
+2119(after)X
+2287(the)X
+2405(last)X
+2536(character)X
+2852(in)X
+2934(the)X
+3052(region.)X
+776 2832(Column:)N
+1136(Set)X
+1258(to)X
+1340(the)X
+1458(character)X
+1774(after)X
+1942(the)X
+2060(last)X
+2191(character)X
+2507(in)X
+2589(the)X
+2707(region.)X
+776 2922(Options:)N
+1136(Affected)X
+1438(by)X
+1538(the)X
+3 f
+1656(tildeop)X
+1 f
+1911(option.)X
+3 f
+576 3102(<interrupt>)N
+1 f
+776 3192(Interrupt)N
+1087(the)X
+1215(current)X
+1473(operation.)X
+1846(Many)X
+2063(of)X
+2160(the)X
+2289(potentially)X
+2662(long-running)X
+3 f
+3111(vi)X
+1 f
+3204(commands)X
+3582(may)X
+3751(be)X
+3858(inter-)X
+776 3282(rupted)N
+1014(using)X
+1220(the)X
+1351(terminal)X
+1650(interrupt)X
+1958(character.)X
+2326(These)X
+2550(operations)X
+2916(include)X
+3184(searches,)X
+3509(\256le)X
+3643(reading)X
+3916(and)X
+776 3372(writing,)N
+1059(\256lter)X
+1242(operations)X
+1608(and)X
+1756(map)X
+1927(character)X
+2256(expansion.)X
+2654(Interrupts)X
+2999(are)X
+3131(also)X
+3293(enabled)X
+3576(when)X
+3783(running)X
+776 3462(commands)N
+1143(outside)X
+1394(of)X
+3 f
+1481(vi)X
+1 f
+1543(.)X
+776 3642(If)N
+853(the)X
+7 f
+974(<interrupt>)X
+1 f
+1525(character)X
+1844(is)X
+1920(used)X
+2090(to)X
+2175(interrupt)X
+2474(while)X
+2675(entering)X
+2961(an)X
+3 f
+3060(ex)X
+1 f
+3159(command,)X
+3518(the)X
+3639(command)X
+3979(is)X
+776 3732(aborted,)N
+1057(the)X
+1175(cursor)X
+1396(returns)X
+1639(to)X
+1721(its)X
+1816(previous)X
+2112(position,)X
+2409(and)X
+3 f
+2545(vi)X
+1 f
+2627(remains)X
+2901(in)X
+2983(command)X
+3319(mode.)X
+776 3912(Generally,)N
+1145(if)X
+1226(the)X
+7 f
+1356(<interrupt>)X
+1 f
+1916(character)X
+2244(is)X
+2329(used)X
+2508(to)X
+2603(interrupt)X
+2912(any)X
+3061(operation,)X
+3417(any)X
+3566(changes)X
+3858(made)X
+776 4002(before)N
+1002(the)X
+1120(interrupt)X
+1416(are)X
+1535(left)X
+1662(in)X
+1744(place.)X
+776 4182(Line:)N
+1136(Dependent)X
+1504(on)X
+1604(the)X
+1722(operation)X
+2045(being)X
+2243(interrupted.)X
+776 4272(Column:)N
+1136(Dependent)X
+1504(on)X
+1604(the)X
+1722(operation)X
+2045(being)X
+2243(interrupted.)X
+776 4362(Options:)N
+1136(None.)X
+3 f
+776 4548(11.)N
+916(Vi)X
+1016(Text)X
+1192(Input)X
+1402(Commands)X
+1 f
+976 4671(The)N
+1121(following)X
+1452(section)X
+1699(describes)X
+2018(the)X
+2136(commands)X
+2503(available)X
+2813(in)X
+2895(the)X
+3013(text)X
+3153(input)X
+3337(mode)X
+3535(of)X
+3622(the)X
+3 f
+3740(vi)X
+1 f
+3822(editor.)X
+976 4794(Historically,)N
+3 f
+1394(vi)X
+1 f
+1476(implementations)X
+2030(only)X
+2193(permitted)X
+2521(the)X
+2640(characters)X
+2988(inserted)X
+3263(on)X
+3364(the)X
+3483(current)X
+3732(line)X
+3873(to)X
+3956(be)X
+776 4884(erased.)N
+1060(In)X
+1165(addition,)X
+1485(only)X
+1665(the)X
+7 f
+1801(<control-D>)X
+1 f
+2367(erase)X
+2571(character)X
+2905(and)X
+3059(the)X
+3195(``)X
+7 f
+3249(0<control-D>)X
+1 f
+('')S
+3916(and)X
+776 4974(``)N
+7 f
+830(\303<control-D>)X
+1 f
+('')S
+1488(erase)X
+1683(strings)X
+1925(could)X
+2132(erase)X
+2327(autoindent)X
+2694(characters.)X
+3090(This)X
+3261(implementation)X
+3792(permits)X
+776 5064(erasure)N
+1038(to)X
+1129(continue)X
+1434(past)X
+1592(the)X
+1719(beginning)X
+2068(of)X
+2164(the)X
+2291(current)X
+2548(line,)X
+2717(and)X
+2862(back)X
+3043(to)X
+3134(where)X
+3360(text)X
+3509(input)X
+3701(mode)X
+3907(was)X
+776 5154(entered.)N
+1080(In)X
+1174(addition,)X
+1483(autoindent)X
+1848(characters)X
+2202(may)X
+2367(be)X
+2471(erased)X
+2705(using)X
+2906(the)X
+3032(standard)X
+3332(erase)X
+3526(characters.)X
+3921(For)X
+776 5244(the)N
+908(line)X
+1061(and)X
+1210(word)X
+1408(erase)X
+1607(characters,)X
+1987(reaching)X
+2297(the)X
+2428(autoindent)X
+2799(characters)X
+3159(forms)X
+3379(a)X
+3448(``soft'')X
+3709(boundary,)X
+776 5334(denoting)N
+1081(the)X
+1204(end)X
+1345(of)X
+1437(the)X
+1561(current)X
+1815(word)X
+2006(or)X
+2099(line)X
+2245(erase.)X
+2477(Repeating)X
+2828(the)X
+2952(word)X
+3143(or)X
+3236(line)X
+3382(erase)X
+3574(key)X
+3716(will)X
+3866(erase)X
+776 5424(the)N
+894(autoindent)X
+1252(characters.)X
+976 5547(Historically,)N
+3 f
+1396(vi)X
+1 f
+1480(always)X
+1725(used)X
+7 f
+1894(<control-H>)X
+1 f
+2444(and)X
+7 f
+2582(<control-W>)X
+1 f
+3132(as)X
+3221(character)X
+3539(and)X
+3678(word)X
+3866(erase)X
+776 5637(characters,)N
+1148(respectively,)X
+1581(regardless)X
+1932(of)X
+2024(the)X
+2146(current)X
+2398(terminal)X
+2689(settings.)X
+2997(This)X
+3163(implementation)X
+3689(accepts,)X
+3970(in)X
+776 5727(addition)N
+1058(to)X
+1140(these)X
+1325(two)X
+1465(characters,)X
+1832(the)X
+1950(current)X
+2198(terminal)X
+2485(characters)X
+2832(for)X
+2946(those)X
+3135(operations.)X
+
+31 p
+%%Page: 31 30
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Vi)X
+1364(Commands\))X
+3658(USD:13-31)X
+776 762(<nul>)N
+1 f
+976 852(If)N
+1050(the)X
+1168(\256rst)X
+1312(character)X
+1628(of)X
+1715(the)X
+1833(input)X
+2017(is)X
+2090(a)X
+7 f
+2146(<nul>)X
+1 f
+(,)S
+2426(the)X
+2544(previous)X
+2840(input)X
+3024(is)X
+3097(replayed,)X
+3414(as)X
+3501(if)X
+3570(just)X
+3705(entered.)X
+3 f
+776 1032(<control-D>)N
+1 f
+976 1122(If)N
+1058(the)X
+1184(previous)X
+1488(character)X
+1813(on)X
+1922(the)X
+2049(line)X
+2198(was)X
+2352(an)X
+2457(autoindent)X
+2824(character,)X
+3169(erase)X
+3364(it.)X
+3477(Otherwise,)X
+3856(if)X
+3934(the)X
+976 1212(user)N
+1135(is)X
+1213(entering)X
+1501(the)X
+1624(\256rst)X
+1773(character)X
+2094(in)X
+2181(the)X
+2304(line,)X
+7 f
+2469(<control-D>)X
+1 f
+3022(is)X
+3100(ignored.)X
+3410(Otherwise,)X
+3785(a)X
+3845(literal)X
+7 f
+976 1302(<control-D>)N
+1 f
+1524(character)X
+1840(is)X
+1913(entered.)X
+3 f
+776 1482(\303<control-D>)N
+1 f
+976 1572(If)N
+1058(the)X
+1184(previous)X
+1488(character)X
+1812(on)X
+1920(the)X
+2047(line)X
+2196(was)X
+2350(an)X
+2455(autoindent)X
+2822(character,)X
+3167(erase)X
+3362(all)X
+3471(of)X
+3567(the)X
+3694(autoindent)X
+976 1662(characters)N
+1323(on)X
+1423(the)X
+1541(line.)X
+1721(In)X
+1808(addition,)X
+2110(the)X
+2228(autoindent)X
+2586(level)X
+2762(is)X
+2835(reset)X
+3007(to)X
+3089(0.)X
+3 f
+776 1842(0<control-D>)N
+1 f
+976 1932(If)N
+1058(the)X
+1184(previous)X
+1488(character)X
+1812(on)X
+1920(the)X
+2047(line)X
+2196(was)X
+2350(an)X
+2455(autoindent)X
+2822(character,)X
+3167(erase)X
+3362(all)X
+3471(of)X
+3567(the)X
+3694(autoindent)X
+976 2022(characters)N
+1323(on)X
+1423(the)X
+1541(line.)X
+3 f
+776 2202(<control-T>)N
+1 f
+976 2292(Insert)N
+1191(suf\256cient)X
+7 f
+1521(<tab>)X
+1 f
+1793(and)X
+7 f
+1941(<space>)X
+1 f
+2309(characters)X
+2669(to)X
+2764(move)X
+2975(the)X
+3106(cursor)X
+3340(forward)X
+3628(to)X
+3723(a)X
+3792(column)X
+976 2382(immediately)N
+1396(after)X
+1564(the)X
+1682(next)X
+1840(column)X
+2100(which)X
+2316(is)X
+2389(an)X
+2485(even)X
+2657(multiple)X
+2943(of)X
+3030(the)X
+3 f
+3148(shiftwidth)X
+1 f
+3514(option.)X
+976 2562(Historically,)N
+3 f
+1397(vi)X
+1 f
+1482(did)X
+1607(not)X
+1732(permit)X
+1965(the)X
+7 f
+2087(<control-T>)X
+1 f
+2639(command)X
+2979(to)X
+3065(be)X
+3165(used)X
+3336(unless)X
+3560(the)X
+3682(cursor)X
+3907(was)X
+976 2652(at)N
+1056(the)X
+1176(\256rst)X
+1322(column)X
+1584(of)X
+1673(a)X
+1731(new)X
+1887(line)X
+2029(or)X
+2117(it)X
+2182(was)X
+2328(preceded)X
+2640(only)X
+2803(by)X
+2904(autoindent)X
+3263(characters.)X
+3 f
+3651(Nvi)X
+1 f
+3792(permits)X
+976 2742(it)N
+1040(to)X
+1122(be)X
+1218(used)X
+1385(at)X
+1463(any)X
+1599(time)X
+1761(during)X
+1990(insert)X
+2188(mode.)X
+3 f
+776 2922(<erase>)N
+776 3012(<control-H>)N
+1 f
+976 3102(Erase)N
+1175(the)X
+1293(last)X
+1424(character.)X
+3 f
+776 3282(<literal)N
+1047(next>)X
+1 f
+976 3372(Quote)N
+1197(the)X
+1320(next)X
+1483(character.)X
+1844(The)X
+1994(next)X
+2157(character)X
+2479(will)X
+2629(not)X
+2757(be)X
+2859(mapped)X
+3139(\(see)X
+3295(the)X
+3 f
+3419(map)X
+1 f
+3596(command)X
+3938(for)X
+976 3462(more)N
+1162(information\))X
+1588(or)X
+1676(interpreted)X
+2045(specially.)X
+2391(A)X
+2470(carat)X
+2648(\(``)X
+7 f
+2729(\303)X
+1 f
+(''\))S
+2878(character)X
+3194(will)X
+3338(be)X
+3434(displayed)X
+3761(immedi-)X
+976 3552(ately)N
+1152(as)X
+1239(a)X
+1295(placeholder,)X
+1710(but)X
+1832(will)X
+1976(be)X
+2072(replaced)X
+2365(by)X
+2465(the)X
+2583(next)X
+2741(character.)X
+3 f
+776 3732(<escape>)N
+1 f
+976 3822(Resolve)N
+1254(all)X
+1354(text)X
+1494(input)X
+1678(into)X
+1822(the)X
+1940(\256le,)X
+2082(and)X
+2218(return)X
+2430(to)X
+2512(command)X
+2848(mode.)X
+3 f
+776 4002(<line)N
+966(erase>)X
+1 f
+976 4092(Erase)N
+1175(the)X
+1293(current)X
+1541(line.)X
+3 f
+776 4272(<control-W>)N
+776 4362(<word)N
+1020(erase>)X
+1 f
+976 4452(Erase)N
+1188(the)X
+1319(last)X
+1463(word.)X
+1701(The)X
+1859(de\256nition)X
+2198(of)X
+2298(word)X
+2496(is)X
+2582(dependent)X
+2945(on)X
+3059(the)X
+3 f
+3191(altwerase)X
+1 f
+3551(and)X
+3 f
+3701(ttywerase)X
+1 f
+976 4542(options.)N
+3 f
+776 4722 0.2102(<control-X>[0-9A-Fa-f]*)AN
+1 f
+976 4812(Insert)N
+1179(a)X
+1235(character)X
+1551(with)X
+1713(the)X
+1831(speci\256ed)X
+2136(hexadecimal)X
+2562(value)X
+2756(into)X
+2900(the)X
+3018(text.)X
+3 f
+776 4992(<interrupt>)N
+1 f
+976 5082(Interrupt)N
+1288(text)X
+1439(input)X
+1634(mode,)X
+1863(returning)X
+2188(to)X
+2281(command)X
+2628(mode.)X
+2877(If)X
+2962(the)X
+7 f
+3091(<interrupt>)X
+1 f
+3651(character)X
+3979(is)X
+976 5172(used)N
+1151(to)X
+1241(interrupt)X
+1545(inserting)X
+1852(text)X
+1999(into)X
+2150(the)X
+2275(\256le,)X
+2424(it)X
+2495(is)X
+2575(as)X
+2669(if)X
+2745(the)X
+7 f
+2870(<escape>)X
+1 f
+3281(character)X
+3604(was)X
+3756(used;)X
+3952(all)X
+976 5262(text)N
+1116(input)X
+1300(up)X
+1400(to)X
+1482(the)X
+1600(interruption)X
+1998(is)X
+2071(resolved)X
+2363(into)X
+2507(the)X
+2625(\256le.)X
+3 f
+776 5448(12.)N
+916(Ex)X
+1029(Addressing)X
+1 f
+976 5571(Addressing)N
+1363(in)X
+3 f
+1447(ex)X
+1 f
+1545(\(and)X
+1710(when)X
+3 f
+1906(ex)X
+1 f
+2004(commands)X
+2373(are)X
+2494(executed)X
+2802(from)X
+3 f
+2980(vi)X
+1 f
+3042(\))X
+3092(relates)X
+3325(to)X
+3410(the)X
+3531(current)X
+3782(line.)X
+3965(In)X
+776 5661(general,)N
+1054(the)X
+1172(current)X
+1420(line)X
+1560(is)X
+1633(the)X
+1751(last)X
+1882(line)X
+2022(affected)X
+2302(by)X
+2402(a)X
+2458(command.)X
+2834(The)X
+2979(exact)X
+3169(effect)X
+3373(on)X
+3473(the)X
+3591(current)X
+3839(line)X
+3979(is)X
+776 5751(discussed)N
+1103(under)X
+1306(the)X
+1424(description)X
+1800(of)X
+1888(each)X
+2057(command.)X
+2434(When)X
+2647(the)X
+2766(\256le)X
+2889(contains)X
+3177(no)X
+3278(lines,)X
+3470(the)X
+3589(current)X
+3838(line)X
+3979(is)X
+
+32 p
+%%Page: 32 31
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-32)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+1 f
+776 762(zero.)N
+976 885(Addresses)N
+1326(are)X
+1445(constructed)X
+1835(by)X
+1935(one)X
+2071(or)X
+2158(more)X
+2343(of)X
+2430(the)X
+2548(following)X
+2879(methods:)X
+816 1008(\(1\))N
+1030(The)X
+1175(address)X
+1436(``)X
+7 f
+1490(.)X
+1 f
+('')S
+1632(refers)X
+1836(to)X
+1918(the)X
+2036(current)X
+2284(line.)X
+816 1131(\(2\))N
+1030(The)X
+1175(address)X
+1436(``)X
+7 f
+1490($)X
+1 f
+('')S
+1612(refers)X
+1816(to)X
+1898(the)X
+2016(last)X
+2147(line)X
+2287(of)X
+2374(the)X
+2492(\256le.)X
+816 1254(\(3\))N
+1030(The)X
+1175(address)X
+1436(``)X
+7 f
+1490(N)X
+1 f
+('',)S
+1632(where)X
+7 f
+1849(N)X
+1 f
+1917(is)X
+1990(a)X
+2046(positive)X
+2319(number,)X
+2604(refers)X
+2808(to)X
+2890(the)X
+3008(N-th)X
+3175(line)X
+3315(of)X
+3402(the)X
+3520(\256le.)X
+816 1377(\(4\))N
+1030(The)X
+1178(address)X
+1442(``)X
+7 f
+1496('<character>)X
+1 f
+('')S
+2149(or)X
+2239(``)X
+7 f
+2293(`<character>)X
+1 f
+('')S
+2946(refers)X
+3153(to)X
+3238(the)X
+3359(line)X
+3503(marked)X
+3768(with)X
+3934(the)X
+1030 1467(name)N
+7 f
+1234(<character>)X
+1 f
+(.)S
+1832(\(See)X
+2005(the)X
+3 f
+2133(k)X
+1 f
+2207(or)X
+3 f
+2304(m)X
+1 f
+2400(commands)X
+2776(for)X
+2899(more)X
+3093(information)X
+3500(on)X
+3609(how)X
+3776(to)X
+3867(mark)X
+1030 1557(lines.\))N
+816 1680(\(5\))N
+1030(A)X
+1113(regular)X
+1366(expression)X
+1734(\(RE\))X
+1915(enclosed)X
+2221(by)X
+2326(slashes)X
+2578(\(``)X
+7 f
+2659(/)X
+1 f
+(''\))S
+2813(is)X
+2891(an)X
+2992(address,)X
+3278(and)X
+3419(it)X
+3488(refers)X
+3697(to)X
+3784(the)X
+3908(\256rst)X
+1030 1770(line)N
+1171(found)X
+1379(by)X
+1480(searching)X
+1809(forward)X
+2085(from)X
+2262(the)X
+2381(line)X
+2 f
+2522(after)X
+1 f
+2694(the)X
+2813(current)X
+3062(line)X
+3203(toward)X
+3447(the)X
+3566(end)X
+3703(of)X
+3791(the)X
+3910(\256le,)X
+1030 1860(and)N
+1170(stopping)X
+1469(at)X
+1551(the)X
+1673(\256rst)X
+1821(line)X
+1965(containing)X
+2327(a)X
+2387(string)X
+2593(matching)X
+2915(the)X
+3037(RE.)X
+3203(\(The)X
+3379(trailing)X
+3634(slash)X
+3819(can)X
+3956(be)X
+1030 1950(omitted)N
+1294(at)X
+1372(the)X
+1490(end)X
+1626(of)X
+1713(the)X
+1831(command)X
+2167(line.\))X
+1030 2130(If)N
+1104(no)X
+1204(RE)X
+1326(is)X
+1399(speci\256ed,)X
+1724(i.e.)X
+1842(the)X
+1960(pattern)X
+2203(is)X
+2276(``)X
+7 f
+2330(//)X
+1 f
+('',)S
+2520(the)X
+2638(last)X
+2769(RE)X
+2891(used)X
+3058(in)X
+3140(any)X
+3276(command)X
+3612(is)X
+3685(used)X
+3852(in)X
+3934(the)X
+1030 2220(search.)N
+1030 2400(If)N
+1111(the)X
+3 f
+1236(extended)X
+1 f
+1570(option)X
+1801(is)X
+1881(set,)X
+2017(the)X
+2142(RE)X
+2271(is)X
+2351(handled)X
+2632(as)X
+2726(an)X
+2829(extended)X
+3146(RE,)X
+3295(not)X
+3425(a)X
+3489(basic)X
+3682(RE.)X
+3852(If)X
+3934(the)X
+3 f
+1030 2490(wrapscan)N
+1 f
+1381(option)X
+1607(is)X
+1681(set,)X
+1811(the)X
+1930(search)X
+2157(wraps)X
+2370(around)X
+2614(to)X
+2697(the)X
+2816(beginning)X
+3157(of)X
+3245(the)X
+3364(\256le)X
+3487(and)X
+3624(continues)X
+3952(up)X
+1030 2580(to)N
+1112(and)X
+1248(including)X
+1570(the)X
+1688(current)X
+1936(line,)X
+2096(so)X
+2187(that)X
+2327(the)X
+2445(entire)X
+2648(\256le)X
+2770(is)X
+2843(searched.)X
+1030 2760(The)N
+1175(form)X
+1351(``)X
+7 f
+1405(\\/)X
+1 f
+('')S
+1575(is)X
+1648(accepted)X
+1950(for)X
+2064(historic)X
+2324(reasons,)X
+2605(and)X
+2741(is)X
+2814(identical)X
+3110(to)X
+3192(``)X
+7 f
+3246(//)X
+1 f
+(''.)S
+816 2883(\(6\))N
+1030(An)X
+1153(RE)X
+1280(enclosed)X
+1586(in)X
+1673(question)X
+1969(marks)X
+2190(\(``)X
+7 f
+2271(?)X
+1 f
+(''\))S
+2446(addresses)X
+2780(the)X
+2904(\256rst)X
+3054(line)X
+3200(found)X
+3413(by)X
+3519(searching)X
+3853(back-)X
+1030 2973(ward)N
+1212(from)X
+1389(the)X
+1508(line)X
+2 f
+1648(preceding)X
+1 f
+1989(the)X
+2107(current)X
+2355(line,)X
+2515(toward)X
+2758(the)X
+2876(beginning)X
+3216(of)X
+3303(the)X
+3421(\256le)X
+3543(and)X
+3679(stopping)X
+3974(at)X
+1030 3063(the)N
+1149(\256rst)X
+1294(line)X
+1435(containing)X
+1794(a)X
+1851(string)X
+2054(matching)X
+2373(the)X
+2492(RE.)X
+2655(\(The)X
+2828(trailing)X
+3080(question)X
+3372(mark)X
+3558(can)X
+3691(be)X
+3788(omitted)X
+1030 3153(at)N
+1108(the)X
+1226(end)X
+1362(of)X
+1449(a)X
+1505(command)X
+1841(line.\))X
+1030 3333(If)N
+1104(no)X
+1204(RE)X
+1326(is)X
+1399(speci\256ed,)X
+1724(i.e.)X
+1842(the)X
+1960(pattern)X
+2203(is)X
+2276(``)X
+7 f
+2330(??)X
+1 f
+('',)S
+2520(the)X
+2638(last)X
+2769(RE)X
+2891(used)X
+3058(in)X
+3140(any)X
+3276(command)X
+3612(is)X
+3685(used)X
+3852(in)X
+3934(the)X
+1030 3423(search.)N
+1030 3603(If)N
+1111(the)X
+3 f
+1236(extended)X
+1 f
+1570(option)X
+1801(is)X
+1881(set,)X
+2017(the)X
+2142(RE)X
+2271(is)X
+2351(handled)X
+2632(as)X
+2726(an)X
+2829(extended)X
+3146(RE,)X
+3295(not)X
+3425(a)X
+3489(basic)X
+3682(RE.)X
+3852(If)X
+3934(the)X
+3 f
+1030 3693(wrapscan)N
+1 f
+1382(option)X
+1609(is)X
+1685(set,)X
+1817(the)X
+1938(search)X
+2187(wraps)X
+2402(around)X
+2648(from)X
+2827(the)X
+2948(beginning)X
+3290(of)X
+3379(the)X
+3499(\256le)X
+3623(to)X
+3707(the)X
+3827(end)X
+3965(of)X
+1030 3783(the)N
+1148(\256le)X
+1270(and)X
+1406(continues)X
+1733(up)X
+1833(to)X
+1915(and)X
+2051(including)X
+2373(the)X
+2491(current)X
+2739(line,)X
+2899(so)X
+2990(that)X
+3130(the)X
+3248(entire)X
+3451(\256le)X
+3573(is)X
+3646(searched.)X
+1030 3963(The)N
+1175(form)X
+1351(``)X
+7 f
+1405(\\?)X
+1 f
+('')S
+1595(is)X
+1668(accepted)X
+1970(for)X
+2084(historic)X
+2344(reasons,)X
+2625(and)X
+2761(is)X
+2834(identical)X
+3130(to)X
+3212(``)X
+7 f
+3266(??)X
+1 f
+(''.)S
+816 4086(\(7\))N
+1030(An)X
+1148(address)X
+1409(followed)X
+1714(by)X
+1814(a)X
+1870(plus)X
+2023(sign)X
+2176(\(``)X
+7 f
+2257(+)X
+1 f
+(''\))S
+2406(or)X
+2494(a)X
+2551(minus)X
+2767(sign)X
+2921(\(``)X
+7 f
+3002(-)X
+1 f
+(''\))S
+3152(followed)X
+3458(by)X
+3559(a)X
+3616(number)X
+3882(is)X
+3956(an)X
+1030 4176(offset)N
+1237(address)X
+1502(and)X
+1642(refers)X
+1850(to)X
+1936(the)X
+2058(address)X
+2323(plus)X
+2480(\(or)X
+2598(minus\))X
+2844(the)X
+2966(indicated)X
+3284(number)X
+3553(of)X
+3643(lines.)X
+3857(If)X
+3934(the)X
+1030 4266(address)N
+1291(is)X
+1364(omitted,)X
+1648(the)X
+1766(addition)X
+2048(or)X
+2135(subtraction)X
+2511(is)X
+2584(done)X
+2760(with)X
+2922(respect)X
+3170(to)X
+3252(the)X
+3370(current)X
+3618(line.)X
+816 4389(\(8\))N
+1030(An)X
+1158(address)X
+1429(of)X
+1526(``)X
+7 f
+1580(+)X
+1 f
+('')S
+1712(or)X
+1809(``)X
+7 f
+9 f
+1863(-)X
+1 f
+1907('')X
+1991(followed)X
+2306(by)X
+2416(a)X
+2482(number)X
+2757(is)X
+2840(an)X
+2946(offset)X
+3159(from)X
+3345(the)X
+3473(current)X
+3731(line.)X
+3921(For)X
+1030 4479(example,)N
+1342(``)X
+7 f
+9 f
+1396(-)X
+7 f
+1440(5)X
+1 f
+('')S
+1562(is)X
+1635(the)X
+1753(same)X
+1938(as)X
+2025(``)X
+7 f
+2079(.)X
+9 f
+(-)S
+7 f
+2171(5)X
+1 f
+(''.)S
+816 4602(\(9\))N
+1030(An)X
+1151(address)X
+1415(ending)X
+1656(with)X
+1821(``)X
+7 f
+1875(+)X
+1 f
+('')S
+2000(or)X
+2090(``)X
+7 f
+2144(-)X
+1 f
+('')S
+2270(has)X
+2401(1)X
+2465(added)X
+2681(to)X
+2767(or)X
+2858(subtracted)X
+3212(from)X
+3392(the)X
+3514(address,)X
+3799(respec-)X
+1030 4692(tively.)N
+1276(As)X
+1389(a)X
+1449(consequence)X
+1884(of)X
+1975(this)X
+2114(rule)X
+2263(and)X
+2403(of)X
+2494(the)X
+2615(previous)X
+2914(rule,)X
+3082(the)X
+3203(address)X
+3467(``)X
+7 f
+9 f
+3521(-)X
+1 f
+3565('')X
+3642(refers)X
+3849(to)X
+3934(the)X
+1030 4782(line)N
+1173(preceding)X
+1513(the)X
+1634(current)X
+1886(line.)X
+2070(Moreover,)X
+2431(trailing)X
+2686(``)X
+7 f
+2740(+)X
+1 f
+('')S
+2866(and)X
+3006(``)X
+7 f
+9 f
+3060(-)X
+1 f
+3104('')X
+3182(characters)X
+3533(have)X
+3709(a)X
+3769(cumula-)X
+1030 4872(tive)N
+1170(effect.)X
+1414(For)X
+1545(example,)X
+1857(``)X
+7 f
+1911(++)X
+9 f
+(-)S
+7 f
+2051(++)X
+1 f
+('')S
+2221(refers)X
+2425(to)X
+2507(the)X
+2625(current)X
+2873(line)X
+3013(plus)X
+3166(3.)X
+816 4995(\(10\))N
+1030(A)X
+1108(percent)X
+1365(sign)X
+1518(\(``)X
+7 f
+1599(%)X
+1 f
+(''\))S
+1748(is)X
+1821(equivalent)X
+2175(to)X
+2257(the)X
+2375(address)X
+2636(range)X
+2835(``)X
+7 f
+2889(1,$)X
+1 f
+(''.)S
+3 f
+976 5118(Ex)N
+1 f
+1099(commands)X
+1476(require)X
+1734(zero,)X
+1923(one,)X
+2089(or)X
+2186(two)X
+2336(addresses.)X
+2714(It)X
+2793(is)X
+2876(an)X
+2982(error)X
+3169(to)X
+3261(specify)X
+3524(an)X
+3631(address)X
+3903(to)X
+3996(a)X
+776 5208(command)N
+1112(which)X
+1328(requires)X
+1607(zero)X
+1766(addresses.)X
+976 5331(If)N
+1053(the)X
+1174(user)X
+1331(provides)X
+1630(more)X
+1818(than)X
+1979(the)X
+2100(expected)X
+2409(number)X
+2677(of)X
+2768(addresses)X
+3100(to)X
+3186(any)X
+3 f
+3326(ex)X
+1 f
+3426(command,)X
+3786(the)X
+3908(\256rst)X
+776 5421(addresses)N
+1108(speci\256ed)X
+1417(are)X
+1540(discarded.)X
+1912(For)X
+2047(example,)X
+2362(``)X
+7 f
+2416(1,2,3,5)X
+1 f
+(''print)S
+2980(prints)X
+3185(lines)X
+3359(3)X
+3422(through)X
+3694(5,)X
+3777(because)X
+776 5511(the)N
+3 f
+894(print)X
+1 f
+1087(command)X
+1423(only)X
+1585(takes)X
+1770(two)X
+1910(addresses.)X
+976 5634(The)N
+1131(addresses)X
+1469(in)X
+1561(a)X
+1627(range)X
+1836(are)X
+1965(separated)X
+2299(from)X
+2485(each)X
+2663(other)X
+2858(by)X
+2968(a)X
+3034(comma)X
+3300(\(``)X
+7 f
+3381(,)X
+1 f
+(''\))S
+3540(or)X
+3637(a)X
+3703(semicolon)X
+776 5724(\(``)N
+7 f
+857(;)X
+1 f
+(''\).)S
+1058(In)X
+1157(the)X
+1287(latter)X
+1484(case,)X
+1675(the)X
+1805(current)X
+2065(line)X
+2217(\(``)X
+7 f
+2298(.)X
+1 f
+(''\))S
+2479(is)X
+2564(set)X
+2685(to)X
+2779(the)X
+2909(\256rst)X
+3065(address,)X
+3358(and)X
+3506(only)X
+3680(then)X
+3850(is)X
+3934(the)X
+
+33 p
+%%Page: 33 32
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-33)X
+1 f
+776 762(second)N
+1026(address)X
+1295(calculated.)X
+1689(This)X
+1859(feature)X
+2111(can)X
+2251(be)X
+2355(used)X
+2530(to)X
+2620(determine)X
+2969(the)X
+3095(starting)X
+3363(line)X
+3511(for)X
+3633(forward)X
+3916(and)X
+776 852(backward)N
+1116(searches)X
+1416(\(see)X
+1572(rules)X
+1754(\(5\))X
+1874(and)X
+2016(\(6\))X
+2136(above\).)X
+2421(The)X
+2572(second)X
+2821(address)X
+3088(of)X
+3181(any)X
+3323(two-address)X
+3737(sequence)X
+776 942(corresponds)N
+1193(to)X
+1284(a)X
+1349(line)X
+1498(that)X
+1647(follows,)X
+1936(in)X
+2027(the)X
+2154(\256le,)X
+2305(the)X
+2432(line)X
+2581(corresponding)X
+3069(to)X
+3160(the)X
+3288(\256rst)X
+3442(address.)X
+3753(The)X
+3908(\256rst)X
+776 1032(address)N
+1044(must)X
+1226(be)X
+1329(less)X
+1475(than)X
+1639(or)X
+1732(equal)X
+1932(to)X
+2020(the)X
+2144(second)X
+2393(address.)X
+2700(The)X
+2851(\256rst)X
+3001(address)X
+3268(must)X
+3449(be)X
+3551(greater)X
+3801(than)X
+3965(or)X
+776 1122(equal)N
+973(to)X
+1058(the)X
+1179(\256rst)X
+1326(line)X
+1469(of)X
+1559(the)X
+1680(\256le,)X
+1825(and)X
+1964(the)X
+2085(last)X
+2219(address)X
+2483(must)X
+2661(be)X
+2761(less)X
+2905(than)X
+3067(or)X
+3158(equal)X
+3356(to)X
+3442(the)X
+3564(last)X
+3699(line)X
+3843(of)X
+3934(the)X
+776 1212(\256le.)N
+3 f
+776 1398(13.)N
+916(Ex)X
+1029(Description)X
+1 f
+976 1521(The)N
+1121(following)X
+1452(words)X
+1668(have)X
+1840(special)X
+2083(meanings)X
+2410(for)X
+3 f
+2524(ex)X
+1 f
+2620(commands.)X
+3 f
+776 1701(<eof>)N
+1 f
+976 1791(The)N
+1130(end-of-\256le)X
+1498(character)X
+1823(is)X
+1905(used)X
+2081(to)X
+2172(scroll)X
+2379(the)X
+2506(screen)X
+2741(in)X
+2832(the)X
+3 f
+2959(ex)X
+1 f
+3064(editor.)X
+3320(This)X
+3491(character)X
+3816(is)X
+3898(nor-)X
+976 1881(mally)N
+7 f
+1178(<control-D>)X
+1 f
+(,)S
+1746(however,)X
+2063(whatever)X
+2378(character)X
+2694(is)X
+2767(set)X
+2876(for)X
+2990(the)X
+3108(current)X
+3356(terminal)X
+3643(is)X
+3716(used.)X
+3 f
+776 2061(line)N
+1 f
+976 2151(A)N
+1054(single-line)X
+1412(address,)X
+1693(given)X
+1891(in)X
+1973(any)X
+2109(of)X
+2196(the)X
+2314(forms)X
+2521(described)X
+2849(in)X
+2931(the)X
+3049(section)X
+3296(entitled)X
+3557(``)X
+3 f
+3611(Ex)X
+3725(Address-)X
+976 2241(ing)N
+1 f
+1082(''.)X
+1196(The)X
+1341(default)X
+1584(for)X
+7 f
+1698(line)X
+1 f
+1910(is)X
+1983(the)X
+2101(current)X
+2349(line.)X
+3 f
+776 2421(range)N
+1 f
+976 2511(A)N
+1058(line,)X
+1222(or)X
+1313(a)X
+1373(pair)X
+1522(of)X
+1613(line)X
+1757(addresses,)X
+2109(separated)X
+2437(by)X
+2541(a)X
+2601(comma)X
+2861(or)X
+2952(semicolon.)X
+3345(\(See)X
+3512(the)X
+3634(section)X
+3885(enti-)X
+976 2601(tled)N
+1123(``)X
+3 f
+1177(Ex)X
+1297(Addressing)X
+1 f
+1683('')X
+1764(for)X
+1884(more)X
+2075(information.\))X
+2546(The)X
+2697(default)X
+2946(for)X
+3066(range)X
+3271(is)X
+3350(the)X
+3474(current)X
+3728(line)X
+2 f
+3874(only)X
+1 f
+4012(,)X
+976 2691(i.e.)N
+1114(``)X
+7 f
+1168(.,.)X
+1 f
+(''.)S
+1426(A)X
+1504(percent)X
+1761(sign)X
+1914(\(``)X
+7 f
+1995(%)X
+1 f
+(''\))S
+2144(stands)X
+2364(for)X
+2479(the)X
+2598(range)X
+2798(``)X
+7 f
+2852(1,$)X
+1 f
+(''.)S
+3111(The)X
+3257(starting)X
+3518(address)X
+3780(must)X
+3956(be)X
+976 2781(less)N
+1116(than,)X
+1294(or)X
+1381(equal)X
+1575(to,)X
+1677(the)X
+1795(ending)X
+2033(address.)X
+3 f
+776 2961(count)N
+1 f
+976 3051(A)N
+1058(positive)X
+1335(integer,)X
+1602(specifying)X
+1961(the)X
+2084(number)X
+2354(of)X
+2446(lines)X
+2622(to)X
+2709(be)X
+2810(affected)X
+3095(by)X
+3200(the)X
+3323(command;)X
+3686(the)X
+3809(default)X
+976 3141(is)N
+1055(1.)X
+1161(Generally,)X
+1524(a)X
+1586(count)X
+1790(past)X
+1945(the)X
+2068(end-of-\256le)X
+2432(may)X
+2595(be)X
+2696(speci\256ed,)X
+3026(e.g.)X
+3167(the)X
+3290(command)X
+3631(``)X
+7 f
+3685(p)X
+3786(3000)X
+1 f
+('')S
+976 3231(in)N
+1064(a)X
+1127(10)X
+1234(line)X
+1381(\256le)X
+1510(is)X
+1590(acceptable,)X
+1977(and)X
+2120(will)X
+2271(print)X
+2449(from)X
+2632(the)X
+2757(current)X
+3012(line)X
+3159(through)X
+3435(the)X
+3560(last)X
+3698(line)X
+3845(in)X
+3934(the)X
+976 3321(\256le.)N
+3 f
+776 3501(\257ags)N
+1 f
+976 3591(One)N
+1132(or)X
+1221(more)X
+1408(of)X
+1497(the)X
+1617(characters)X
+1966(``#'',)X
+2156(``p'',)X
+2346(and)X
+2484(``l''.)X
+2677(When)X
+2892(a)X
+2951(command)X
+3290(that)X
+3433(accepts)X
+3693(these)X
+3881(\257ags)X
+976 3681(completes,)N
+1343(the)X
+1463(addressed)X
+1802(line\(s\))X
+2029(are)X
+2150(written)X
+2399(out)X
+2523(as)X
+2612(if)X
+2683(by)X
+2785(the)X
+2905(corresponding)X
+3 f
+3386(#)X
+1 f
+(,)S
+3 f
+3468(l)X
+1 f
+3512(or)X
+3 f
+3600(p)X
+1 f
+3665(commands.)X
+976 3771(In)N
+1064(addition,)X
+1367(any)X
+1504(number)X
+1770(of)X
+1858(``)X
+7 f
+1912(+)X
+1 f
+('')S
+2035(or)X
+2123(``)X
+7 f
+9 f
+2177(-)X
+1 f
+2221('')X
+2296(characters)X
+2644(can)X
+2777(be)X
+2874(speci\256ed)X
+3180(before,)X
+3427(after,)X
+3616(or)X
+3704(during)X
+3934(the)X
+976 3861(\257ags,)N
+1175(in)X
+1265(which)X
+1489(case)X
+1656(the)X
+1782(line)X
+1930(written)X
+2185(is)X
+2266(not)X
+2396(necessarily)X
+2780(the)X
+2905(one)X
+3048(affected)X
+3335(by)X
+3442(the)X
+3567(command,)X
+3930(but)X
+976 3951(rather)N
+1184(the)X
+1302(line)X
+1442(addressed)X
+1779(by)X
+1879(the)X
+1997(offset)X
+2200(address)X
+2461(speci\256ed.)X
+2806(The)X
+2951(default)X
+3194(for)X
+7 f
+3308(flags)X
+1 f
+3568(is)X
+3641(none.)X
+3 f
+776 4131(\256le)N
+1 f
+976 4221(A)N
+1055(pattern)X
+1299(used)X
+1467(to)X
+1550(derive)X
+1772(a)X
+1829(pathname;)X
+2184(the)X
+2303(default)X
+2547(is)X
+2621(the)X
+2740(current)X
+2989(\256le.)X
+3152(File)X
+3297(names)X
+3524(are)X
+3645(subjected)X
+3970(to)X
+976 4311(normal)N
+2 f
+1223(sh)X
+1 f
+1294(\(1\))X
+1408(word)X
+1593(expansions.)X
+976 4434(Anywhere)N
+1336(a)X
+1397(\256le)X
+1524(name)X
+1723(is)X
+1801(speci\256ed,)X
+2131(it)X
+2200(is)X
+2279(also)X
+2434(possible)X
+2722(to)X
+2810(use)X
+2943(the)X
+3067(special)X
+3316(string)X
+3524(``)X
+7 f
+3578(/tmp)X
+1 f
+(''.)S
+3890(This)X
+776 4524(will)N
+936(be)X
+1048(replaced)X
+1357(with)X
+1535(a)X
+1607(temporary)X
+1973(\256le)X
+2111(name)X
+2321(which)X
+2553(can)X
+2701(be)X
+2813(used)X
+2996(for)X
+3126(temporary)X
+3491(work,)X
+3711(e.g.)X
+3882(``)X
+7 f
+3936(:e)X
+776 4614(/tmp)N
+1 f
+('')S
+1042(creates)X
+1286(and)X
+1422(edits)X
+1593(a)X
+1649(new)X
+1803(\256le.)X
+976 4737(If)N
+1052(both)X
+1216(a)X
+1274(count)X
+1474(and)X
+1613(a)X
+1672(range)X
+1874(are)X
+1996(speci\256ed)X
+2304(for)X
+2421(commands)X
+2791(that)X
+2934(use)X
+3064(either,)X
+3290(the)X
+3411(starting)X
+3674(line)X
+3817(for)X
+3934(the)X
+776 4827(command)N
+1120(is)X
+1200(the)X
+2 f
+1325(last)X
+1 f
+1467(line)X
+1614(addressed)X
+1958(by)X
+2065(the)X
+2190(range,)X
+2416(and)X
+7 f
+2559(count)X
+1 f
+(-)S
+2853(subsequent)X
+3236(lines)X
+3414(are)X
+3540(affected)X
+3827(by)X
+3934(the)X
+776 4917(command,)N
+1132(e.g.)X
+1268(the)X
+1386(command)X
+1722(``)X
+7 f
+1776(2,3p4)X
+1 f
+('')S
+2090(prints)X
+2292(out)X
+2414(lines)X
+2585(3,)X
+2665(4,)X
+2745(5)X
+2805(and)X
+2941(6.)X
+976 5040(When)N
+1188(only)X
+1350(a)X
+1406(line)X
+1546(or)X
+1633(range)X
+1833(is)X
+1907(speci\256ed,)X
+2233(with)X
+2396(no)X
+2497(command,)X
+2854(the)X
+2973(implied)X
+3238(command)X
+3575(is)X
+3649(either)X
+3853(a)X
+3 f
+3910(list)X
+1 f
+4012(,)X
+3 f
+776 5130(number)N
+1 f
+1073(or)X
+3 f
+1166(print)X
+1 f
+1365(command.)X
+1747(The)X
+1898(command)X
+2240(used)X
+2413(is)X
+2492(the)X
+2616(most)X
+2797(recent)X
+3020(of)X
+3112(the)X
+3235(three)X
+3421(commands)X
+3793(to)X
+3880(have)X
+776 5220(been)N
+949(used)X
+1117(\(including)X
+1467(any)X
+1604(use)X
+1732(as)X
+1820(a)X
+1877(\257ag\).)X
+2085(If)X
+2160(none)X
+2337(of)X
+2425(these)X
+2611(commands)X
+2979(have)X
+3152(been)X
+3325(used)X
+3493(before,)X
+3740(the)X
+3 f
+3859(print)X
+1 f
+776 5310(command)N
+1117(is)X
+1195(the)X
+1318(implied)X
+1587(command.)X
+1968(When)X
+2185(no)X
+2290(range)X
+2494(or)X
+2585(count)X
+2787(is)X
+2864(speci\256ed)X
+3173(and)X
+3313(the)X
+3435(command)X
+3775(line)X
+3919(is)X
+3996(a)X
+776 5400(blank)N
+974(line,)X
+1134(the)X
+1252(current)X
+1500(line)X
+1640(is)X
+1713(incremented)X
+2130(by)X
+2230(1)X
+2290(and)X
+2426(then)X
+2584(the)X
+2702(current)X
+2950(line)X
+3090(is)X
+3163(displayed.)X
+976 5523(Zero)N
+1149(or)X
+1237(more)X
+1423(whitespace)X
+1801(characters)X
+2149(may)X
+2308(precede)X
+2580(or)X
+2668(follow)X
+2898(the)X
+3017(addresses,)X
+3366(count,)X
+3585(\257ags,)X
+3778(or)X
+3867(com-)X
+776 5613(mand)N
+980(name.)X
+1220(Any)X
+1384(object)X
+1606(following)X
+1943(a)X
+2004(command)X
+2345(name)X
+2544(\(such)X
+2743(as)X
+2835(buffer,)X
+3077(\256le,)X
+3224(etc.\),)X
+3410(that)X
+3555(begins)X
+3789(with)X
+3956(an)X
+776 5703(alphabetic)N
+1130(character,)X
+1471(should)X
+1709(be)X
+1810(separated)X
+2139(from)X
+2320(the)X
+2443(command)X
+2784(name)X
+2983(by)X
+3088(at)X
+3171(least)X
+3343(one)X
+3484(whitespace)X
+3866(char-)X
+776 5793(acter.)N
+
+34 p
+%%Page: 34 33
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-34)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+1 f
+976 762(Any)N
+1148(character,)X
+1498(including)X
+7 f
+1834(<carriage-return>)X
+1 f
+(,)S
+2704(``)X
+7 f
+2758(%)X
+1 f
+('')S
+2894(and)X
+3045(``)X
+7 f
+3099(#)X
+1 f
+('')S
+3236(retain)X
+3454(their)X
+3636(literal)X
+3858(value)X
+776 852(when)N
+970(preceded)X
+1281(by)X
+1381(a)X
+1437(backslash.)X
+3 f
+776 1038(14.)N
+916(Ex)X
+1029(Commands)X
+1 f
+976 1161(The)N
+1122(following)X
+1454(section)X
+1702(describes)X
+2022(the)X
+2141(commands)X
+2509(available)X
+2820(in)X
+2903(the)X
+3 f
+3023(ex)X
+1 f
+3121(editor.)X
+3370(In)X
+3459(each)X
+3629(entry)X
+3816(below,)X
+776 1251(the)N
+894(tag)X
+1012(line)X
+1152(is)X
+1225(a)X
+1281(usage)X
+1484(synopsis)X
+1779(for)X
+1893(the)X
+2011(command.)X
+976 1374(Each)N
+1161(command)X
+1501(can)X
+1637(be)X
+1737(entered)X
+1998(as)X
+2089(the)X
+2211(abbreviation)X
+2636(\(those)X
+2856(characters)X
+3207(in)X
+3293(the)X
+3416(synopsis)X
+3716(command)X
+776 1464(word)N
+966(preceding)X
+1307(the)X
+1429(``['')X
+1588 0.3375(character\),)AX
+1955(the)X
+2077(full)X
+2212(command)X
+2552(\(all)X
+2683(characters)X
+3034(shown)X
+3267(for)X
+3385(the)X
+3507(command)X
+3847(word,)X
+776 1554(omitting)N
+1067(the)X
+1187(``['')X
+1344(and)X
+1482(``]'')X
+1639 0.2955(characters\),)AX
+2035(or)X
+2124(any)X
+2262(leading)X
+2520(subset)X
+2742(of)X
+2831(the)X
+2951(full)X
+3084(command)X
+3422(down)X
+3622(to)X
+3706(the)X
+3826(abbre-)X
+776 1644(viation.)N
+1064(For)X
+1201(example,)X
+1519(the)X
+1643(args)X
+1803(command)X
+2145(\(shown)X
+2407(as)X
+2500(``)X
+7 f
+2554(ar[gs])X
+1 f
+('')S
+2922(in)X
+3010(the)X
+3134(synopsis\))X
+3462(can)X
+3600(be)X
+3702(entered)X
+3965(as)X
+776 1734(``)N
+7 f
+830(ar)X
+1 f
+('',)S
+1020(``)X
+7 f
+1074(arg)X
+1 f
+('')S
+1292(or)X
+1379(``)X
+7 f
+1433(args)X
+1 f
+(''.)S
+976 1857(Each)N
+3 f
+1163(ex)X
+1 f
+1265(command)X
+1607(described)X
+1941(below)X
+2163(notes)X
+2359(the)X
+2484(new)X
+2645(current)X
+2900(line)X
+3047(after)X
+3222(it)X
+3293(is)X
+3373(executed,)X
+3706(as)X
+3800(well)X
+3965(as)X
+776 1947(any)N
+912(options)X
+1167(that)X
+1307(affect)X
+1511(the)X
+1629(command.)X
+976 2127(A)N
+1063(comment.)X
+1430(Command)X
+1792(lines)X
+1972(beginning)X
+2321(with)X
+2492(the)X
+2619(double-quote)X
+3072(character)X
+3398(\(``)X
+7 f
+3479(")X
+1 f
+(''\))S
+3638(are)X
+3767(ignored.)X
+976 2217(This)N
+1138(permits)X
+1398(comments)X
+1747(in)X
+1829(editor)X
+2036(scripts)X
+2265(and)X
+2401(startup)X
+2639(\256les.)X
+3 f
+776 2397(<end-of-\256le>)N
+1 f
+976 2487(Scroll)N
+1189(the)X
+1309(screen.)X
+1577(Write)X
+1782(the)X
+1902(next)X
+2062(N)X
+2142(lines,)X
+2336(where)X
+2556(N)X
+2637(is)X
+2713(the)X
+2834(value)X
+3031(of)X
+3121(the)X
+3 f
+3242(scroll)X
+1 f
+3452(option.)X
+3719(The)X
+3867(com-)X
+976 2577(mand)N
+1191(is)X
+1281(the)X
+1416(end-of-\256le)X
+1792(terminal)X
+2096(character,)X
+2449(which)X
+2682(may)X
+2857(be)X
+2970(different)X
+3284(on)X
+3401(different)X
+3714(terminals.)X
+976 2667(Traditionally,)N
+1434(it)X
+1498(is)X
+1571(the)X
+7 f
+1689(<control-D>)X
+1 f
+2237(key.)X
+976 2847(Historically,)N
+1397(the)X
+3 f
+1518(eof)X
+1 f
+1644(command)X
+1984(ignored)X
+2253(any)X
+2393(preceding)X
+2734(count,)X
+2956(and)X
+3096(the)X
+7 f
+3218(<end-of-file>)X
+1 f
+3866(char-)X
+976 2937(acter)N
+1155(was)X
+1302(ignored)X
+1569(unless)X
+1791(it)X
+1857(was)X
+2004(entered)X
+2263(as)X
+2352(the)X
+2472(\256rst)X
+2618(character)X
+2936(of)X
+3025(the)X
+3145(command.)X
+3522(This)X
+3685(implemen-)X
+976 3027(tation)N
+1178(treats)X
+1372(it)X
+1436(as)X
+1523(a)X
+1579(command)X
+2 f
+1915(only)X
+1 f
+2073(if)X
+2142(entered)X
+2399(as)X
+2486(the)X
+2604(\256rst)X
+2748(character)X
+3064(of)X
+3151(the)X
+3269(command)X
+3605(line,)X
+3766(and)X
+3903(oth-)X
+976 3117(erwise)N
+1206(treats)X
+1400(it)X
+1464(as)X
+1551(any)X
+1687(other)X
+1872(character.)X
+976 3297(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(written.)X
+976 3387(Options:)N
+1336(None.)X
+3 f
+776 3567(!)N
+823(argument\(s\))X
+776 3657([range]!)N
+1073(argument\(s\))X
+1 f
+976 3747(Execute)N
+1263(a)X
+1327(shell)X
+1506(command,)X
+1870(or)X
+1966(\256lter)X
+2146(lines)X
+2326(through)X
+2604(a)X
+2669(shell)X
+2849(command.)X
+3234(In)X
+3330(the)X
+3457(\256rst)X
+3610(synopsis,)X
+3934(the)X
+976 3837(remainder)N
+1333(of)X
+1430(the)X
+1558(line)X
+1708(after)X
+1886(the)X
+2014(``)X
+7 f
+2068(!)X
+1 f
+('')S
+2220(character)X
+2546(is)X
+2629(passed)X
+2873(to)X
+2965(the)X
+3093(program)X
+3395(named)X
+3639(by)X
+3749(the)X
+3 f
+3877(shell)X
+1 f
+976 3927(option,)N
+1220(as)X
+1307(a)X
+1363(single)X
+1574(argument.)X
+976 4107(Within)N
+1227(the)X
+1354(rest)X
+1499(of)X
+1595(the)X
+1722(line,)X
+1891(``)X
+7 f
+1945(%)X
+1 f
+('')S
+2076(and)X
+2221(``)X
+7 f
+2275(#)X
+1 f
+('')S
+2407(are)X
+2536(expanded)X
+2874(into)X
+3028(the)X
+3156(current)X
+3414(and)X
+3560(alternate)X
+3867(path-)X
+976 4197(names,)N
+1223(respectively.)X
+1673(The)X
+1820(character)X
+2138(``)X
+7 f
+2192(!)X
+1 f
+('')S
+2336(is)X
+2411(expanded)X
+2741(with)X
+2904(the)X
+3023(command)X
+3360(text)X
+3501(of)X
+3589(the)X
+3708(previous)X
+3 f
+4005(!)X
+1 f
+976 4287(command.)N
+1354 0.3125(\(Therefore,)AX
+1741(the)X
+1861(command)X
+3 f
+2199(!!)X
+1 f
+2295(repeats)X
+2545(the)X
+2665(previous)X
+3 f
+2963(!)X
+1 f
+3032(command.\))X
+3437(The)X
+3585(special)X
+3831(mean-)X
+976 4377(ings)N
+1130(of)X
+1218(``)X
+7 f
+1272(%)X
+1 f
+('',)S
+1415(``)X
+7 f
+1469(#)X
+1 f
+('',)S
+1612(and)X
+1749(``)X
+7 f
+1803(!)X
+1 f
+('')S
+1946(can)X
+2079(be)X
+2176(overridden)X
+2545(by)X
+2646(escaping)X
+2948(them)X
+3129(with)X
+3292(a)X
+3349(backslash.)X
+3722(If)X
+3797(no)X
+3 f
+3898(!)X
+1 f
+3965(or)X
+3 f
+976 4467(:!)N
+1 f
+1075(command)X
+1416(has)X
+1548(yet)X
+1671(been)X
+1848(executed,)X
+2179(it)X
+2248(is)X
+2326(an)X
+2427(error)X
+2609(to)X
+2696(use)X
+2828(an)X
+2929(unescaped)X
+3290(``)X
+7 f
+3344(!)X
+1 f
+('')S
+3492(character.)X
+3854(The)X
+3 f
+4005(!)X
+1 f
+976 4557(command)N
+1325(does)X
+2 f
+1505(not)X
+1 f
+1640(do)X
+1752(shell)X
+1935(expansion)X
+2292(on)X
+2404(the)X
+2534(strings)X
+2779(provided)X
+3096(as)X
+3195(arguments.)X
+3601(If)X
+3687(any)X
+3835(of)X
+3934(the)X
+976 4647(above)N
+1197(expansions)X
+1582(change)X
+1839(the)X
+1966(command)X
+2311(the)X
+2438(user)X
+2602(entered,)X
+2889(the)X
+3017(command)X
+3363(is)X
+3446(redisplayed)X
+3846(at)X
+3934(the)X
+976 4737(bottom)N
+1222(of)X
+1309(the)X
+1427(screen.)X
+3 f
+976 4917(Ex)N
+1 f
+1092(then)X
+1253(executes)X
+1553(the)X
+1674(program)X
+1969(named)X
+2206(by)X
+2309(the)X
+3 f
+2430(shell)X
+1 f
+2608(option,)X
+2855(with)X
+3020(a)X
+3 f
+9 f
+3079(-)X
+3081(-)X
+3 f
+3125(c)X
+1 f
+3184(\257ag)X
+3327(followed)X
+3636(by)X
+3740(the)X
+3862(argu-)X
+976 5007(ments)N
+1187(\(which)X
+1430(are)X
+1549(bundled)X
+1827(into)X
+1971(a)X
+2027(single)X
+2238(argument\).)X
+976 5187(The)N
+3 f
+1121(!)X
+1 f
+1188(command)X
+1524(is)X
+1597(permitted)X
+1924(in)X
+2006(an)X
+2102(empty)X
+2322(\256le.)X
+976 5367(If)N
+1050(the)X
+1168(\256le)X
+1290(has)X
+1417(been)X
+1589(modi\256ed)X
+1893(since)X
+2078(it)X
+2142(was)X
+2287(last)X
+2418(completely)X
+2794(written,)X
+3061(the)X
+3179(command)X
+3515(will)X
+3659(warn)X
+3840(you.)X
+976 5547(A)N
+1054(single)X
+1265(``)X
+7 f
+1319(!)X
+1 f
+('')S
+1461(character)X
+1777(is)X
+1850(displayed)X
+2177(when)X
+2371(the)X
+2489(command)X
+2825(completes.)X
+976 5727(In)N
+1064(the)X
+1183(second)X
+1427(form)X
+1604(of)X
+1692(the)X
+3 f
+1811(!)X
+1 f
+1879(command,)X
+2236(the)X
+2355(remainder)X
+2702(of)X
+2790(the)X
+2909(line)X
+3051(after)X
+3221(the)X
+3341(``)X
+7 f
+3395(!)X
+1 f
+('')S
+3539(is)X
+3614(passed)X
+3850(to)X
+3934(the)X
+
+35 p
+%%Page: 35 34
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-35)X
+1 f
+976 762(program)N
+1273(named)X
+1512(by)X
+1617(the)X
+3 f
+1740(shell)X
+1 f
+1920(option,)X
+2169(as)X
+2261(described)X
+2594(above.)X
+2851(The)X
+3001(speci\256ed)X
+3311(lines)X
+3487(are)X
+3610(passed)X
+3848(to)X
+3934(the)X
+976 852(program)N
+1273(as)X
+1365(standard)X
+1662(input,)X
+1871(and)X
+2012(the)X
+2135(standard)X
+2432(and)X
+2573(standard)X
+2871(error)X
+3054(output)X
+3284(of)X
+3377(the)X
+3501(program)X
+3799(replace)X
+976 942(the)N
+1094(original)X
+1363(lines.)X
+976 1122(Line:)N
+1336(Unchanged)X
+1722(if)X
+1791(no)X
+1891(range)X
+2090(was)X
+2235(speci\256ed,)X
+2560(otherwise)X
+2892(set)X
+3001(to)X
+3083(the)X
+3201(\256rst)X
+3345(line)X
+3485(of)X
+3572(the)X
+3690(range.)X
+976 1212(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2206(and)X
+3 f
+2342(writeany)X
+1 f
+2665(options.)X
+3 f
+776 1392([range])N
+1046(nu[mber])X
+1391([count])X
+1656([\257ags])X
+776 1482([range])N
+1046(#)X
+1106([count])X
+1371([\257ags])X
+1 f
+976 1572(Display)N
+1245(the)X
+1363(selected)X
+1642(lines,)X
+1833(each)X
+2001(preceded)X
+2312(with)X
+2474(its)X
+2569(line)X
+2709(number.)X
+976 1752(The)N
+1121(line)X
+1261(number)X
+1526(format)X
+1760(is)X
+1833(``%6d'',)X
+2128(followed)X
+2433(by)X
+2533(two)X
+2673(spaces.)X
+976 1932(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(displayed.)X
+976 2022(Options:)N
+1336(None.)X
+3 f
+776 2202(@)N
+870(buffer)X
+776 2292(*)N
+836(buffer)X
+1 f
+976 2382(Execute)N
+1255(a)X
+1311(buffer.)X
+1568(Each)X
+1749(line)X
+1890(in)X
+1973(the)X
+2092(named)X
+2327(buffer)X
+2545(is)X
+2619(executed)X
+2926(as)X
+3014(an)X
+3 f
+3111(ex)X
+1 f
+3208(command.)X
+3585(If)X
+3660(no)X
+3761(buffer)X
+3979(is)X
+976 2472(speci\256ed,)N
+1301(or)X
+1388(if)X
+1457(the)X
+1575(speci\256ed)X
+1880(buffer)X
+2097(is)X
+2170(``)X
+7 f
+2224(@)X
+1 f
+('')S
+2346(or)X
+2433(``)X
+7 f
+2487(*)X
+1 f
+('',)S
+2629(the)X
+2747(last)X
+2878(buffer)X
+3095(executed)X
+3401(is)X
+3474(used.)X
+3 f
+776 2652([range])N
+1046(<[<)X
+1185(...])X
+1292([count])X
+1557([\257ags])X
+1 f
+976 2742(Shift)N
+1154(lines)X
+1328(left)X
+1458(or)X
+1548(right.)X
+1762(The)X
+1910(speci\256ed)X
+2218(lines)X
+2392(are)X
+2514(shifted)X
+2755(to)X
+2840(the)X
+2961(left)X
+3091(\(for)X
+3235(the)X
+3 f
+3356(<)X
+1 f
+3425(command\))X
+3791(or)X
+3881(right)X
+976 2832(\(for)N
+1130(the)X
+3 f
+1261(>)X
+1 f
+1340(command\),)X
+1736(by)X
+1849(the)X
+1980(number)X
+2257(of)X
+2356(columns)X
+2659(speci\256ed)X
+2976(by)X
+3088(the)X
+3 f
+3218(shiftwidth)X
+1 f
+3596(option.)X
+3872(Only)X
+976 2922(leading)N
+1237(whitespace)X
+1619(characters)X
+1971(are)X
+2095(deleted)X
+2352(when)X
+2552(shifting)X
+2822(left;)X
+2977(once)X
+3155(the)X
+3279(\256rst)X
+3429(column)X
+3695(of)X
+3788(the)X
+3912(line)X
+976 3012(contains)N
+1283(a)X
+1359(nonblank)X
+1697(character,)X
+2053(the)X
+3 f
+2191(shift)X
+1 f
+2382(command)X
+2738(will)X
+2901(succeed,)X
+3215(but)X
+3356(the)X
+3493(line)X
+3652(will)X
+3815(not)X
+3956(be)X
+976 3102(modi\256ed.)N
+976 3282(If)N
+1050(the)X
+1168(command)X
+1504(character)X
+3 f
+1820(<)X
+1 f
+1886(or)X
+3 f
+1973(>)X
+1 f
+2039(is)X
+2112(repeated)X
+2405(more)X
+2590(than)X
+2748(once,)X
+2941(the)X
+3060(command)X
+3397(is)X
+3471(repeated)X
+3765(once)X
+3938(for)X
+976 3372(each)N
+1144(additional)X
+1484(command)X
+1820(character.)X
+976 3552(Line:)N
+1336(If)X
+1416(the)X
+1540(current)X
+1794(line)X
+1940(is)X
+2019(set)X
+2134(to)X
+2222(one)X
+2364(of)X
+2457(the)X
+2581(lines)X
+2758(that)X
+2904(are)X
+3029(affected)X
+3315(by)X
+3421(the)X
+3545(command,)X
+3908(it)X
+3979(is)X
+1336 3642(unchanged.)N
+1751(Otherwise,)X
+2128(it)X
+2199(is)X
+2279(set)X
+2395(to)X
+2484(the)X
+2609(\256rst)X
+2760(nonblank)X
+3085(character)X
+3408(of)X
+3502(the)X
+3627(lowest)X
+3863(num-)X
+1336 3732(bered)N
+1535(line)X
+1675(shifted.)X
+976 3822(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(shiftwidth)X
+1 f
+2222(option.)X
+3 f
+776 4002([line])N
+974(=)X
+1040([\257ags])X
+1 f
+976 4092(Display)N
+1250(the)X
+1373(line)X
+1518(number.)X
+1828(Display)X
+2102(the)X
+2225(line)X
+2370(number)X
+2640(of)X
+7 f
+2732(line)X
+1 f
+2949(\(which)X
+3197(defaults)X
+3476(to)X
+3563(the)X
+3687(last)X
+3824(line)X
+3970(in)X
+976 4182(the)N
+1094(\256le\).)X
+976 4362(Line:)N
+1336(Unchanged.)X
+976 4452(Options:)N
+1336(None.)X
+3 f
+776 4632([range])N
+1046(>[>)X
+1185(...])X
+1292([count])X
+1557([\257ags])X
+1 f
+976 4722(Shift)N
+1155(right.)X
+1370(The)X
+1519(speci\256ed)X
+1828(lines)X
+2003(are)X
+2127(shifted)X
+2370(to)X
+2457(the)X
+2580(right)X
+2756(by)X
+2861(the)X
+2984(number)X
+3254(of)X
+3346(columns)X
+3642(speci\256ed)X
+3952(by)X
+976 4812(the)N
+3 f
+1094(shiftwidth)X
+1 f
+1460(option,)X
+1704(by)X
+1804(inserting)X
+2104(tab)X
+2222(and)X
+2358(space)X
+2557(characters.)X
+2944(Empty)X
+3177(lines)X
+3348(are)X
+3467(not)X
+3589(changed.)X
+976 4992(If)N
+1053(the)X
+1174(command)X
+1513(character)X
+1832(``)X
+7 f
+1886(>)X
+1 f
+('')S
+2011(is)X
+2087(repeated)X
+2383(more)X
+2571(than)X
+2732(once,)X
+2927(the)X
+3048(command)X
+3388(is)X
+3465(repeated)X
+3762(once)X
+3938(for)X
+976 5082(each)N
+1144(additional)X
+1484(command)X
+1820(character.)X
+976 5262(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(modi\256ed)X
+2233(by)X
+2333(the)X
+2451(command.)X
+976 5352(Options:)N
+1336(None.)X
+3 f
+776 5532(ab[brev])N
+1090(lhs)X
+1207(rhs)X
+1 f
+976 5622(Add)N
+1135(an)X
+1232(abbreviation)X
+1654(to)X
+1737(the)X
+1856(current)X
+2105(abbreviation)X
+2527(list.)X
+2686(In)X
+3 f
+2775(vi)X
+1 f
+2837(,)X
+2879(if)X
+7 f
+2950(lhs)X
+1 f
+3116(is)X
+3191(entered)X
+3450(such)X
+3619(that)X
+3761(it)X
+3827(is)X
+3902(pre-)X
+976 5712(ceded)N
+1194(and)X
+1340(followed)X
+1655(by)X
+1765(characters)X
+2122(that)X
+2272(cannot)X
+2516(be)X
+2622(part)X
+2777(of)X
+2874(a)X
+2940(word,)X
+3155(it)X
+3229(is)X
+3312(replaced)X
+3614(by)X
+3723(the)X
+3850(string)X
+7 f
+976 5802(rhs)N
+1 f
+(.)S
+
+36 p
+%%Page: 36 35
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-36)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+1 f
+976 762(Line:)N
+1336(Unchanged.)X
+976 852(Options:)N
+1336(None.)X
+3 f
+776 1032([line])N
+974(a[ppend][!])X
+1 f
+976 1122(The)N
+1123(input)X
+1309(text)X
+1451(is)X
+1526(appended)X
+1856(to)X
+1940(the)X
+2060(speci\256ed)X
+2367(line.)X
+2549(If)X
+2626(line)X
+2769(0)X
+2832(is)X
+2908(speci\256ed,)X
+3236(the)X
+3357(text)X
+3500(is)X
+3576(inserted)X
+3853(at)X
+3934(the)X
+976 1212(beginning)N
+1317(of)X
+1405(the)X
+1524(\256le.)X
+1687(Set)X
+1810(to)X
+1893(the)X
+2012(last)X
+2144(line)X
+2285(input.)X
+2510(If)X
+2585(no)X
+2686(lines)X
+2858(are)X
+2978(input,)X
+3183(then)X
+3342(set)X
+3451(to)X
+7 f
+3533(line)X
+1 f
+(,)S
+3765(or)X
+3852(to)X
+3934(the)X
+976 1302(\256rst)N
+1125(line)X
+1270(of)X
+1362(the)X
+1485(\256le)X
+1612(if)X
+1686(a)X
+7 f
+1747(line)X
+1 f
+1965(of)X
+2058(0)X
+2124(was)X
+2275(speci\256ed.)X
+2626(Following)X
+2980(the)X
+3104(command)X
+3446(name)X
+3646(with)X
+3814(a)X
+3876(``)X
+7 f
+3930(!)X
+1 f
+('')S
+976 1392(character)N
+1292(causes)X
+1522(the)X
+3 f
+1640(autoindent)X
+1 f
+2028(option)X
+2252(to)X
+2334(be)X
+2430(toggled)X
+2690(for)X
+2804(the)X
+2922(duration)X
+3209(of)X
+3296(the)X
+3414(command.)X
+976 1572(Line:)N
+1336(Unchanged.)X
+976 1662(Options:)N
+1336(Affected)X
+1661(by)X
+1784(the)X
+3 f
+1925(altwerase)X
+1 f
+2251(,)X
+3 f
+2314(autoindent)X
+1 f
+2682(,)X
+3 f
+2746(beautify)X
+1 f
+(,)S
+3 f
+3090(showmatch)X
+1 f
+3477(,)X
+3 f
+3541(ttywerase)X
+1 f
+3916(and)X
+3 f
+1336 1752(wrapmargin)N
+1 f
+1783(options.)X
+3 f
+776 1932(ar[gs])N
+1 f
+976 2022(Display)N
+1248(the)X
+1369(argument)X
+1695(list.)X
+1855(The)X
+2003(current)X
+2254(argument)X
+2580(is)X
+2657(displayed)X
+2988(inside)X
+3203(of)X
+3294(``)X
+7 f
+3348([)X
+1 f
+('')S
+3474(and)X
+3614(``)X
+7 f
+3668(])X
+1 f
+('')S
+3794(charac-)X
+976 2112(ters.)N
+1154(The)X
+1300(argument)X
+1624(list)X
+1742(is)X
+1816(the)X
+1935(list)X
+2053(of)X
+2141(operands)X
+2452(speci\256ed)X
+2758(on)X
+2859(startup,)X
+3118(which)X
+3335(can)X
+3468(be)X
+3565(replaced)X
+3859(using)X
+976 2202(the)N
+3 f
+1094(next)X
+1 f
+1261(command.)X
+976 2382(Line:)N
+1336(Unchanged.)X
+976 2472(Options:)N
+1336(None.)X
+3 f
+776 2652(bg)N
+976 2742(Vi)N
+1 f
+1076(mode)X
+1274(only.)X
+1476(Background)X
+1888(the)X
+2006(current)X
+2254(screen.)X
+976 2922(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(current)X
+1906(line)X
+2046(when)X
+2240(the)X
+2358(screen)X
+2584(was)X
+2729(last)X
+2860(edited.)X
+976 3012(Options:)N
+1336(None.)X
+3 f
+776 3192([range])N
+1046(c[hange][!])X
+1441([count])X
+1 f
+976 3282(Replace)N
+1256(the)X
+1375(lines)X
+1548(with)X
+1712(input)X
+1898(text.)X
+2080(Following)X
+2430(the)X
+2550(command)X
+2888(name)X
+3084(with)X
+3248(a)X
+3306(``)X
+7 f
+3360(!)X
+1 f
+('')S
+3504(character)X
+3822(causes)X
+976 3372(the)N
+3 f
+1094(autoindent)X
+1 f
+1482(option)X
+1706(to)X
+1788(be)X
+1884(toggled)X
+2144(for)X
+2258(the)X
+2376(duration)X
+2663(of)X
+2750(the)X
+2868(command.)X
+976 3552(Line:)N
+1336(Set)X
+1463(to)X
+1550(the)X
+1673(last)X
+1809(line)X
+1954(input,)X
+2163(or,)X
+2275(if)X
+2349(no)X
+2454(lines)X
+2630(were)X
+2812(input,)X
+3021(set)X
+3135(to)X
+3223(the)X
+3347(line)X
+3493(before)X
+3725(the)X
+3849(target)X
+1336 3642(line,)N
+1496(or)X
+1583(to)X
+1665(the)X
+1783(\256rst)X
+1927(line)X
+2067(of)X
+2154(the)X
+2272(\256le)X
+2394(if)X
+2463(there)X
+2644(are)X
+2763(no)X
+2863(lines)X
+3034(preceding)X
+3371(the)X
+3489(target)X
+3692(line.)X
+976 3732(Options:)N
+1336(Affected)X
+1661(by)X
+1784(the)X
+3 f
+1925(altwerase)X
+1 f
+2251(,)X
+3 f
+2314(autoindent)X
+1 f
+2682(,)X
+3 f
+2746(beautify)X
+1 f
+(,)S
+3 f
+3090(showmatch)X
+1 f
+3477(,)X
+3 f
+3541(ttywerase)X
+1 f
+3916(and)X
+3 f
+1336 3822(wrapmargin)N
+1 f
+1783(options.)X
+3 f
+776 4002(chd[ir][!])N
+1113([directory])X
+776 4092(cd[!])N
+957([directory])X
+1 f
+976 4182(Change)N
+1244(the)X
+1365(current)X
+1616(working)X
+1906(directory.)X
+2259(The)X
+7 f
+2407(directory)X
+1 f
+2862(argument)X
+3188(is)X
+3265(subjected)X
+3592(to)X
+2 f
+3678(sh)X
+1 f
+3749(\(1\))X
+3867(word)X
+976 4272(expansions.)N
+1395(When)X
+1610(invoked)X
+1891(with)X
+2056(no)X
+2159(directory)X
+2472(argument)X
+2798(and)X
+2937(the)X
+7 f
+3057(HOME)X
+1 f
+3271(environment)X
+3698(variable)X
+3979(is)X
+976 4362(set,)N
+1105(the)X
+1223(directory)X
+1533(named)X
+1767(by)X
+1867(the)X
+7 f
+1985(HOME)X
+1 f
+2197(environment)X
+2622(variable)X
+2901(becomes)X
+3202(the)X
+3320(new)X
+3474(current)X
+3722(directory.)X
+976 4452(Otherwise,)N
+1353(the)X
+1478(new)X
+1639(current)X
+1894(directory)X
+2211(becomes)X
+2519(the)X
+2644(directory)X
+2961(returned)X
+3256(by)X
+3363(the)X
+2 f
+3488(getpwent)X
+1 f
+3777(\(3\))X
+3898(rou-)X
+976 4542(tine.)N
+976 4722(The)N
+3 f
+1125(chdir)X
+1 f
+1331(command)X
+1671(will)X
+1819(fail)X
+1950(if)X
+2023(the)X
+2145(\256le)X
+2271(has)X
+2402(been)X
+2578(modi\256ed)X
+2886(since)X
+3075(the)X
+3197(last)X
+3333(complete)X
+3652(write)X
+3842(of)X
+3934(the)X
+976 4812(\256le.)N
+1138(You)X
+1296(can)X
+1428(override)X
+1716(this)X
+1851(check)X
+2059(by)X
+2159(appending)X
+2513(a)X
+2569(``)X
+7 f
+2623(!)X
+1 f
+('')S
+2765(character)X
+3081(to)X
+3163(the)X
+3281(command.)X
+976 4992(Line:)N
+1336(Unchanged.)X
+976 5082(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(cdpath)X
+1 f
+2111(option.)X
+3 f
+776 5262([range])N
+1046(co[py])X
+1280(line)X
+1424([\257ags])X
+776 5352([range])N
+1046(t)X
+1093(line)X
+1237([\257ags])X
+1 f
+976 5442(Copy)N
+1169(the)X
+1288(speci\256ed)X
+1594(lines)X
+1766(\(range\))X
+2020(after)X
+2189(the)X
+2308(destination)X
+2680(line.)X
+2861(Line)X
+3029(0)X
+3090(may)X
+3249(be)X
+3346(speci\256ed)X
+3652(to)X
+3735(insert)X
+3934(the)X
+976 5532(lines)N
+1147(at)X
+1225(the)X
+1343(beginning)X
+1683(of)X
+1770(the)X
+1888(\256le.)X
+976 5712(Line:)N
+1336(Unchanged.)X
+
+37 p
+%%Page: 37 36
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-37)X
+1 f
+976 762(Options:)N
+1336(None.)X
+3 f
+776 942([range])N
+1046(d[elete])X
+1321([buffer])X
+1609([count])X
+1874([\257ags])X
+1 f
+976 1032(Delete)N
+1208(the)X
+1329(lines)X
+1503(from)X
+1682(the)X
+1803(\256le.)X
+1968(The)X
+2116(deleted)X
+2371(text)X
+2514(is)X
+2590(saved)X
+2796(in)X
+2881(the)X
+3002(speci\256ed)X
+3310(buffer,)X
+3550(or,)X
+3660(if)X
+3732(no)X
+3835(buffer)X
+976 1122(is)N
+1051(speci\256ed,)X
+1378(in)X
+1462(the)X
+1582(unnamed)X
+1898(buffer.)X
+2157(If)X
+2233(the)X
+2353(command)X
+2691(name)X
+2887(is)X
+2962(followed)X
+3269(by)X
+3371(a)X
+3429(letter)X
+3616(that)X
+3757(could)X
+3956(be)X
+976 1212(interpreted)N
+1346(as)X
+1435(either)X
+1640(a)X
+1698(buffer)X
+1917(name)X
+2113(or)X
+2202(a)X
+2261(\257ag)X
+2404(value)X
+2601(\(because)X
+2906(neither)X
+3152(a)X
+7 f
+3211(count)X
+1 f
+3474(or)X
+7 f
+3564(flags)X
+1 f
+3827(values)X
+976 1302(were)N
+1160(given\),)X
+3 f
+1412(ex)X
+1 f
+1515(treats)X
+1716(the)X
+1841(letter)X
+2033(as)X
+2127(a)X
+7 f
+2190(flags)X
+1 f
+2457(value)X
+2658(if)X
+2734(the)X
+2859(letter)X
+3051(immediately)X
+3477(follows)X
+3743(the)X
+3867(com-)X
+976 1392(mand)N
+1174(name,)X
+1388(without)X
+1652(any)X
+1788(whitespace)X
+2165(separation.)X
+2555(If)X
+2629(the)X
+2747(letter)X
+2932(is)X
+3005(preceded)X
+3316(by)X
+3416(whitespace)X
+3794(charac-)X
+976 1482(ters,)N
+1132(it)X
+1196(treats)X
+1390(it)X
+1454(as)X
+1541(a)X
+1597(buffer)X
+1814(name.)X
+976 1662(Line:)N
+1336(Set)X
+1459(to)X
+1542(the)X
+1661(line)X
+1802(following)X
+2134(the)X
+2253(deleted)X
+2506(lines,)X
+2698(or)X
+2786(to)X
+2869(the)X
+2988(last)X
+3120(line)X
+3261(if)X
+3331(the)X
+3450(deleted)X
+3703(lines)X
+3875(were)X
+1336 1752(at)N
+1414(the)X
+1532(end.)X
+976 1842(Options:)N
+1336(None.)X
+3 f
+776 2022(di[splay])N
+1093(b[uffers])X
+1412(|)X
+1450(s[creens])X
+1774(|)X
+1812(t[ags])X
+1 f
+976 2112(Display)N
+1256(buffers,)X
+1535(screens)X
+1803(or)X
+1901(tags.)X
+2101(The)X
+3 f
+2257(display)X
+1 f
+2531(command)X
+2878(takes)X
+3074(one)X
+3221(of)X
+3319(three)X
+3511(additional)X
+3862(argu-)X
+976 2202(ments,)N
+1207(which)X
+1423(are)X
+1542(as)X
+1629(follows:)X
+976 2382(b[uffers])N
+1336(Display)X
+1605(all)X
+1705(buffers)X
+1953(\(including)X
+2302(named,)X
+2556(unnamed,)X
+2890(and)X
+3026(numeric\))X
+3336(that)X
+3476(contain)X
+3732(text.)X
+976 2472(s[creens])N
+1336(Display)X
+1605(the)X
+1723(\256le)X
+1845(names)X
+2070(of)X
+2157(all)X
+2257(background)X
+2656(screens.)X
+976 2562(t[ags])N
+1336(Display)X
+1605(the)X
+1723(tags)X
+1872(stack.)X
+976 2742(Line:)N
+1336(Unchanged.)X
+976 2832(Options:)N
+1336(None.)X
+3 f
+776 3012(e[dit][!])N
+1060([+cmd])X
+1327([\256le])X
+776 3102(ex[!])N
+953([+cmd])X
+1220([\256le])X
+1 f
+976 3192(Edit)N
+1133(a)X
+1193(different)X
+1494(\256le.)X
+1661(If)X
+1740(the)X
+1863(current)X
+2116(buffer)X
+2338(has)X
+2470(been)X
+2647(modi\256ed)X
+2956(since)X
+3146(the)X
+3269(last)X
+3405(complete)X
+3724(write,)X
+3934(the)X
+976 3282(command)N
+1324(will)X
+1480(fail.)X
+1659(You)X
+1829(can)X
+1972(override)X
+2271(this)X
+2417(by)X
+2528(appending)X
+2893(a)X
+2960(``)X
+7 f
+3014(!)X
+1 f
+('')S
+3167(character)X
+3494(to)X
+3587(the)X
+3716(command)X
+976 3372(name.)N
+976 3552(If)N
+1053(the)X
+1174(``)X
+7 f
+1228(+cmd)X
+1 f
+('')S
+1497(option)X
+1724(is)X
+1800(speci\256ed,)X
+2128(that)X
+3 f
+2271(ex)X
+1 f
+2370(command)X
+2709(will)X
+2856(be)X
+2955(executed)X
+3264(in)X
+3349(the)X
+3470(new)X
+3628(\256le.)X
+3794(Any)X
+3 f
+3956(ex)X
+1 f
+976 3642(command)N
+1326(may)X
+1498(be)X
+1607(used,)X
+1807(although)X
+2120(the)X
+2251(most)X
+2439(common)X
+2752(use)X
+2892(of)X
+2992(this)X
+3140(feature)X
+3397(is)X
+3483(to)X
+3578(specify)X
+3843(a)X
+3912(line)X
+976 3732(number)N
+1241(or)X
+1328(search)X
+1554(pattern)X
+1797(to)X
+1879(set)X
+1988(the)X
+2106(initial)X
+2312(location)X
+2590(in)X
+2672(the)X
+2790(new)X
+2944(\256le.)X
+976 3912(Line:)N
+1336(If)X
+1415(you)X
+1560(have)X
+1737(previously)X
+2100(edited)X
+2321(the)X
+2444(\256le,)X
+2591(the)X
+2714(current)X
+2967(line)X
+3112(will)X
+3261(be)X
+3362(set)X
+3476(to)X
+3563(your)X
+3735(last)X
+3872(posi-)X
+1336 4002(tion)N
+1482(in)X
+1566(the)X
+1686(\256le.)X
+1850(If)X
+1926(that)X
+2068(position)X
+2347(does)X
+2516(not)X
+2640(exist,)X
+2833(or)X
+2921(you)X
+3062(have)X
+3235(not)X
+3358(previously)X
+3717(edited)X
+3934(the)X
+1336 4092(\256le,)N
+1481(the)X
+1602(current)X
+1853(line)X
+1996(will)X
+2143(be)X
+2242(set)X
+2354(to)X
+2439(the)X
+2560(\256rst)X
+2707(line)X
+2850(of)X
+2940(the)X
+3061(\256le)X
+3186(if)X
+3258(you)X
+3401(are)X
+3523(in)X
+3 f
+3608(vi)X
+1 f
+3694(mode,)X
+3916(and)X
+1336 4182(the)N
+1454(last)X
+1585(line)X
+1725(of)X
+1812(the)X
+1930(\256le)X
+2052(if)X
+2121(you)X
+2261(are)X
+2380(in)X
+3 f
+2462(ex)X
+1 f
+2538(.)X
+976 4272(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2206(and)X
+3 f
+2342(writeany)X
+1 f
+2665(options.)X
+3 f
+776 4452(exu[sage])N
+1117([command])X
+1 f
+976 4542(Display)N
+1249(usage)X
+1456(for)X
+1574(an)X
+3 f
+1674(ex)X
+1 f
+1774(command.)X
+2154(If)X
+7 f
+2233(command)X
+1 f
+2594(is)X
+2672(speci\256ed,)X
+3002(a)X
+3063(usage)X
+3271(statement)X
+3603(for)X
+3722(that)X
+3867(com-)X
+976 4632(mand)N
+1174(is)X
+1247(displayed.)X
+1614(Otherwise,)X
+1984(usage)X
+2187(statements)X
+2545(for)X
+2659(all)X
+3 f
+2759(ex)X
+1 f
+2855(commands)X
+3222(are)X
+3341(displayed.)X
+976 4812(Line:)N
+1336(Unchanged.)X
+976 4902(Options:)N
+1336(None.)X
+3 f
+776 5082(f[ile])N
+957([\256le])X
+1 f
+976 5172(Display)N
+1249(and)X
+1389(optionally)X
+1737(change)X
+1989(the)X
+2111(\256le)X
+2237(name.)X
+2475(If)X
+2553(a)X
+2613(\256le)X
+2739(name)X
+2937(is)X
+3014(speci\256ed,)X
+3344(the)X
+3467(current)X
+3720(pathname)X
+976 5262(is)N
+1054(changed)X
+1347(to)X
+1434(the)X
+1557(speci\256ed)X
+1867(name.)X
+2106(The)X
+2256(current)X
+2509(pathname,)X
+2865(the)X
+2987(number)X
+3256(of)X
+3347(lines,)X
+3542(and)X
+3682(the)X
+3804(current)X
+976 5352(position)N
+1253(in)X
+1335(the)X
+1453(\256le)X
+1575(are)X
+1694(displayed.)X
+976 5532(Line:)N
+1336(Unchanged.)X
+976 5622(Options:)N
+1336(None.)X
+
+38 p
+%%Page: 38 37
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-38)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+776 762(fg)N
+863([name])X
+976 852(Vi)N
+1 f
+1085(mode)X
+1292(only.)X
+1503(Foreground)X
+1906(the)X
+2033(speci\256ed)X
+2347(screen.)X
+2622(Swap)X
+2829(the)X
+2956(current)X
+3213(screen)X
+3448(with)X
+3619(the)X
+3747(speci\256ed)X
+976 942(backgrounded)N
+1451(screen.)X
+1717(If)X
+1791(no)X
+1891(screen)X
+2117(is)X
+2190(speci\256ed,)X
+2515(the)X
+2633(\256rst)X
+2777(background)X
+3176(screen)X
+3402(is)X
+3475(foregrounded.)X
+976 1122(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(current)X
+1906(line)X
+2046(when)X
+2240(the)X
+2358(screen)X
+2584(was)X
+2729(last)X
+2860(edited.)X
+976 1212(Options:)N
+1336(None.)X
+3 f
+776 1392([range])N
+1046(g[lobal])X
+1328(/pattern/)X
+1646([commands])X
+776 1482([range])N
+1046(v)X
+1106(/pattern/)X
+1424([commands])X
+1 f
+976 1572(Apply)N
+1206(commands)X
+1583(to)X
+1675(lines)X
+1856(matching)X
+2184(\(or)X
+2308(not)X
+2440(matching\))X
+2795(a)X
+2861(pattern.)X
+3154(The)X
+3309(lines)X
+3490(within)X
+3725(the)X
+3854(given)X
+976 1662(range)N
+1191(that)X
+1347(match)X
+1579(\(``)X
+7 f
+1660(g[lobal])X
+1 f
+(''\),)S
+2181(or)X
+2284(do)X
+2400(not)X
+2538(match)X
+2770(\(``)X
+7 f
+2851(v)X
+1 f
+(''\))S
+3015(the)X
+3148(given)X
+3361(pattern)X
+3619(are)X
+3753(selected.)X
+976 1752(Then,)N
+1197(the)X
+1331(speci\256ed)X
+3 f
+1652(ex)X
+1 f
+1764(command\(s\))X
+2201(are)X
+2336(executed)X
+2658(with)X
+2836(the)X
+2970(current)X
+3235(line)X
+3392(\(``)X
+7 f
+3473(.)X
+1 f
+(''\))S
+3659(set)X
+3785(to)X
+3884(each)X
+976 1842(selected)N
+1257(line.)X
+1439(If)X
+1515(no)X
+1617(range)X
+1818(is)X
+1893(speci\256ed,)X
+2220(the)X
+2340(entire)X
+2545(\256le)X
+2669(is)X
+2744(searched)X
+3048(for)X
+3164(matching,)X
+3503(or)X
+3591(not)X
+3714(matching,)X
+976 1932(lines.)N
+976 2112(Multiple)N
+1279(commands)X
+1654(can)X
+1794(be)X
+1899(speci\256ed,)X
+2233(one)X
+2378(per)X
+2510(line,)X
+2679(by)X
+2788(escaping)X
+3098(each)X
+7 f
+3275(<newline>)X
+1 f
+3736(character)X
+976 2202(with)N
+1152(a)X
+1222(backslash,)X
+1588(or)X
+1689(by)X
+1803(separating)X
+2167(commands)X
+2548(with)X
+2724(a)X
+2794(``)X
+7 f
+2848(|)X
+1 f
+('')S
+2984(character.)X
+3353(If)X
+3440(no)X
+3553(commands)X
+3933(are)X
+976 2292(speci\256ed,)N
+1301(the)X
+1419(command)X
+1755(defaults)X
+2029(to)X
+2111(the)X
+3 f
+2229(print)X
+1 f
+2422(command.)X
+976 2472(For)N
+1111(the)X
+3 f
+1233(append)X
+1 f
+1485(,)X
+3 f
+1529(change)X
+1 f
+1793(and)X
+3 f
+1933(insert)X
+1 f
+2153(commands,)X
+2544(the)X
+2666(input)X
+2854(text)X
+2998(must)X
+3177(be)X
+3277(part)X
+3427(of)X
+3519(the)X
+3642(global)X
+3867(com-)X
+976 2562(mand)N
+1174(line.)X
+1354(In)X
+1441(this)X
+1576(case,)X
+1755(the)X
+1873(terminating)X
+2262(period)X
+2487(can)X
+2619(be)X
+2715(omitted)X
+2979(if)X
+3048(it)X
+3112(ends)X
+3279(the)X
+3397(commands.)X
+976 2742(The)N
+3 f
+1123(visual)X
+1 f
+1344(command)X
+1682(may)X
+1842(also)X
+1993(be)X
+2091(speci\256ed)X
+2398(as)X
+2487(one)X
+2625(of)X
+2714(the)X
+3 f
+2834(ex)X
+1 f
+2933(commands.)X
+3343(In)X
+3433(this)X
+3571(mode,)X
+3792(input)X
+3979(is)X
+976 2832(taken)N
+1173(from)X
+1352(the)X
+1473(terminal.)X
+1803(Entering)X
+2102(a)X
+3 f
+2161(Q)X
+1 f
+2246(command)X
+2585(in)X
+3 f
+2670(vi)X
+1 f
+2755(mode)X
+2956(causes)X
+3189(the)X
+3310(next)X
+3471(line)X
+3614(matching)X
+3934(the)X
+976 2922(pattern)N
+1219(to)X
+1301(be)X
+1397(selected)X
+1676(and)X
+3 f
+1812(vi)X
+1 f
+1894(to)X
+1976(be)X
+2072(reentered,)X
+2412(until)X
+2578(the)X
+2696(list)X
+2813(is)X
+2886(exhausted.)X
+976 3102(The)N
+3 f
+1121(global)X
+1 f
+1329(,)X
+3 f
+1369(v)X
+1 f
+1429(and)X
+3 f
+1565(undo)X
+1 f
+1757(commands)X
+2124(cannot)X
+2358(be)X
+2454(used)X
+2621(as)X
+2708(part)X
+2853(of)X
+2940(these)X
+3125(commands.)X
+976 3282(The)N
+1131(editor)X
+1348(options)X
+3 f
+1613(autoprint)X
+1 f
+1937(,)X
+3 f
+1987(autoindent)X
+1 f
+2355(,)X
+2405(and)X
+3 f
+2551(report)X
+1 f
+2800(are)X
+2929(turned)X
+3164(off)X
+3288(for)X
+3412(the)X
+3540(duration)X
+3837(of)X
+3934(the)X
+3 f
+976 3372(global)N
+1 f
+1204(and)X
+3 f
+1340(v)X
+1 f
+1400(commands.)X
+976 3552(Line:)N
+1336(The)X
+1481(last)X
+1612(line)X
+1752(modi\256ed.)X
+976 3642(Options:)N
+1336(None.)X
+3 f
+776 3822(he[lp])N
+1 f
+3912(Display)Y
+1245(a)X
+1301(help)X
+1459(message.)X
+976 4092(Line:)N
+1336(Unchanged.)X
+976 4182(Options:)N
+1336(None.)X
+3 f
+776 4362([line])N
+974(i[nsert][!])X
+1 f
+976 4452(The)N
+1123(input)X
+1309(text)X
+1451(is)X
+1526(inserted)X
+1803(before)X
+2032(the)X
+2153(speci\256ed)X
+2461(line.)X
+2644(Following)X
+2995(the)X
+3116(command)X
+3455(name)X
+3652(with)X
+3817(a)X
+3876(``)X
+7 f
+3930(!)X
+1 f
+('')S
+976 4542(character)N
+1292(causes)X
+1522(the)X
+3 f
+1640(autoindent)X
+1 f
+2028(option)X
+2252(setting)X
+2485(to)X
+2567(be)X
+2663(toggled)X
+2923(for)X
+3037(the)X
+3155(duration)X
+3442(of)X
+3529(this)X
+3664(command.)X
+976 4722(Line:)N
+1336(Set)X
+1460(to)X
+1544(the)X
+1664(last)X
+1797(line)X
+1939(input;)X
+2147(if)X
+2218(no)X
+2320(lines)X
+2493(were)X
+2672(input,)X
+2878(set)X
+2989(to)X
+3073(the)X
+3193(line)X
+3336(before)X
+3565(the)X
+3686(target)X
+3892(line,)X
+1336 4812(or)N
+1423(to)X
+1505(the)X
+1623(\256rst)X
+1767(line)X
+1907(of)X
+1994(the)X
+2112(\256le)X
+2234(if)X
+2303(there)X
+2484(are)X
+2603(no)X
+2703(lines)X
+2874(preceding)X
+3211(the)X
+3329(target)X
+3532(line.)X
+976 4902(Options:)N
+1336(Affected)X
+1661(by)X
+1784(the)X
+3 f
+1925(altwerase)X
+1 f
+2251(,)X
+3 f
+2314(autoindent)X
+1 f
+2682(,)X
+3 f
+2746(beautify)X
+1 f
+(,)S
+3 f
+3090(showmatch)X
+1 f
+3477(,)X
+3 f
+3541(ttywerase)X
+1 f
+3916(and)X
+3 f
+1336 4992(wrapmargin)N
+1 f
+1783(options.)X
+3 f
+776 5172([range])N
+1046(j[oin][!])X
+1334([count])X
+1599([\257ags])X
+1 f
+976 5262(Join)N
+1129(lines)X
+1300(of)X
+1387(text)X
+1527(together.)X
+976 5442(A)N
+7 f
+1056(count)X
+1 f
+1318(speci\256ed)X
+1625(to)X
+1709(the)X
+1829(command)X
+2167(speci\256es)X
+2465(that)X
+2607(the)X
+2727(last)X
+2860(line)X
+3002(of)X
+3091(the)X
+7 f
+3212(range)X
+1 f
+3475(plus)X
+7 f
+3631(count)X
+1 f
+3894(sub-)X
+976 5532(sequent)N
+1258(lines)X
+1445(will)X
+1605(be)X
+1717(joined.)X
+1993(\(Note,)X
+2232(this)X
+2383(differs)X
+2629(by)X
+2745(one)X
+2897(from)X
+3089(the)X
+3223(general)X
+3496(rule)X
+3657(where)X
+3890(only)X
+7 f
+976 5622(count)N
+1 f
+(-)S
+1263(subsequent)X
+1639(lines)X
+1810(are)X
+1929(affected.\))X
+976 5802(If)N
+1057(the)X
+1182(current)X
+1438(line)X
+1586(ends)X
+1761(with)X
+1931(a)X
+1995(whitespace)X
+2380(character,)X
+2724(all)X
+2832(whitespace)X
+3217(is)X
+3298(stripped)X
+3584(from)X
+3768(the)X
+3894(next)X
+
+39 p
+%%Page: 39 38
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-39)X
+1 f
+976 762(line.)N
+1160(Otherwise,)X
+1534(if)X
+1607(the)X
+1729(next)X
+1891(line)X
+2035(starts)X
+2228(with)X
+2394(a)X
+2454(open)X
+2634(parenthesis)X
+3019(\(``)X
+7 f
+3100(\()X
+1 f
+(''\),)S
+3272(do)X
+3375(nothing.)X
+3682(Otherwise,)X
+976 852(if)N
+1061(the)X
+1195(current)X
+1459(line)X
+1615(ends)X
+1798(with)X
+1976(a)X
+2049(question)X
+2357(mark)X
+2559(\(``)X
+7 f
+2640(?)X
+1 f
+(''\),)S
+2826(period)X
+3068(\(``)X
+7 f
+3149(.)X
+1 f
+(''\))S
+3335(or)X
+3439(exclamation)X
+3868(point)X
+976 942(\(``)N
+7 f
+1057(!)X
+1 f
+(''\),)S
+1226(insert)X
+1424(two)X
+1564(spaces.)X
+1834(Otherwise,)X
+2204(insert)X
+2402(a)X
+2458(single)X
+2669(space.)X
+976 1122(Appending)N
+1355(a)X
+1414(``)X
+7 f
+1468(!)X
+1 f
+('')S
+1613(character)X
+1932(to)X
+2017(the)X
+2138(command)X
+2477(name)X
+2674(causes)X
+2907(a)X
+2966(simpler)X
+3230(join)X
+3378(with)X
+3544(no)X
+3648(white-space)X
+976 1212(processing.)N
+976 1392(Line:)N
+1336(Unchanged.)X
+976 1482(Options:)N
+1336(None.)X
+3 f
+776 1662([range])N
+1046(l[ist])X
+1222([count])X
+1487([\257ags])X
+1 f
+976 1752(Display)N
+1258(the)X
+1389(lines)X
+1573(unambiguously.)X
+2139(Tabs)X
+2328(are)X
+2460(displayed)X
+2801(as)X
+2902(``)X
+7 f
+2956(\303I)X
+1 f
+('',)S
+3160(and)X
+3310(the)X
+3442(end)X
+3592(of)X
+3693(the)X
+3825(line)X
+3979(is)X
+976 1842(marked)N
+1237(with)X
+1399(a)X
+1455(``)X
+7 f
+1509($)X
+1 f
+('')S
+1631(character.)X
+976 2022(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(displayed.)X
+976 2112(Options:)N
+1336(None.)X
+3 f
+776 2292(map[!])N
+1028([lhs)X
+1172(rhs])X
+1 f
+976 2382(De\256ne)N
+1210(or)X
+1297(display)X
+1548(maps)X
+1737(\(for)X
+3 f
+1878(vi)X
+1 f
+1960(only\).)X
+976 2562(If)N
+1050(``)X
+7 f
+1104(lhs)X
+1 f
+('')S
+1322(and)X
+1458(``)X
+7 f
+1512(rhs)X
+1 f
+('')S
+1730(are)X
+1849(not)X
+1971(speci\256ed,)X
+2296(the)X
+2414(current)X
+2662(set)X
+2771(of)X
+2859(command)X
+3196(mode)X
+3395(maps)X
+3585(are)X
+3705(displayed.)X
+976 2652(If)N
+1050(a)X
+1106(``)X
+7 f
+1160(!)X
+1 f
+('')S
+1302(character)X
+1618(is)X
+1691(appended)X
+2019(to)X
+2101(to)X
+2183(the)X
+2301(command,)X
+2657(the)X
+2775(text)X
+2915(input)X
+3099(mode)X
+3297(maps)X
+3486(are)X
+3605(displayed.)X
+976 2832(Otherwise,)N
+1365(when)X
+1578(the)X
+1715(``)X
+7 f
+1769(lhs)X
+1 f
+('')S
+2006(character)X
+2342(sequence)X
+2677(is)X
+2770(entered)X
+3047(in)X
+3 f
+3149(vi)X
+1 f
+3211(,)X
+3271(the)X
+3409(action)X
+3645(is)X
+3738(as)X
+3845(if)X
+3934(the)X
+976 2922(corresponding)N
+1466(``)X
+7 f
+1520(rhs)X
+1 f
+('')S
+1749(had)X
+1896(been)X
+2079(entered.)X
+2387(If)X
+2472(a)X
+2539(``)X
+7 f
+2593(!)X
+1 f
+('')S
+2746(character)X
+3073(is)X
+3157(appended)X
+3496(to)X
+3588(the)X
+3716(command)X
+976 3012(name,)N
+1196(the)X
+1320(mapping)X
+1626(is)X
+1705(effective)X
+2013(during)X
+2249(text)X
+2396(input)X
+2587(mode,)X
+2812(otherwise,)X
+3171(it)X
+3242(is)X
+3322(effective)X
+3631(during)X
+3867(com-)X
+976 3102(mand)N
+1175(mode.)X
+1414(This)X
+1577(allows)X
+1807(``)X
+7 f
+1861(lhs)X
+1 f
+('')S
+2080(to)X
+2163(have)X
+2336(two)X
+2476(different)X
+2773(macro)X
+2994(de\256nitions)X
+3351(at)X
+3429(the)X
+3547(same)X
+3732(time:)X
+3916(one)X
+976 3192(for)N
+1090(command)X
+1426(mode)X
+1624(and)X
+1760(one)X
+1896(for)X
+2010(input)X
+2194(mode.)X
+976 3372(Whitespace)N
+1373(characters)X
+1722(require)X
+1972(escaping)X
+2275(with)X
+2439(a)X
+7 f
+2497(<literal)X
+1 f
+(next>)S
+3086(character)X
+3405(to)X
+3490(be)X
+3589(entered)X
+3849(in)X
+3934(the)X
+7 f
+976 3462(lhs)N
+1 f
+1140(string)X
+1342(in)X
+1424(visual)X
+1635(mode.)X
+976 3642(Normally,)N
+1330(keys)X
+1504(in)X
+1593(the)X
+7 f
+1718(rhs)X
+1 f
+1889(string)X
+2098(are)X
+2224(remapped)X
+2569(\(see)X
+2727(the)X
+3 f
+2853(remap)X
+1 f
+3104(option\),)X
+3383(and)X
+3527(it)X
+3599(is)X
+3680(possible)X
+3970(to)X
+976 3732(create)N
+1207(in\256nite)X
+1471(loops.)X
+1722(However,)X
+2075(keys)X
+2260(which)X
+2494(map)X
+2670(to)X
+2770(themselves)X
+3164(are)X
+3300(not)X
+3439(further)X
+3695(remapped,)X
+976 3822(regardless)N
+1327(of)X
+1419(the)X
+1542(setting)X
+1780(of)X
+1872(the)X
+3 f
+1995(remap)X
+1 f
+2243(option.)X
+2512(For)X
+2648(example,)X
+2966(the)X
+3090(command)X
+3432(``)X
+7 f
+3486(:map)X
+3732(n)X
+3834(nz.)X
+1 f
+('')S
+976 3912(maps)N
+1165(the)X
+1283(``)X
+7 f
+1337(n)X
+1 f
+('')S
+1459(key)X
+1595(to)X
+1677(the)X
+3 f
+1795(n)X
+1 f
+1859(and)X
+3 f
+1995(z)X
+1 f
+2051(commands.)X
+976 4092(To)N
+1085(exit)X
+1225(an)X
+1321(in\256nitely)X
+1629(looping)X
+1893(map,)X
+2071(use)X
+2198(the)X
+2316(terminal)X
+7 f
+2603(<interrupt>)X
+1 f
+3151(character.)X
+976 4272(Line:)N
+1336(Unchanged.)X
+976 4362(Options:)N
+1336(None.)X
+3 f
+776 4542([line])N
+974(ma[rk])X
+1235 0.3125(<character>)AX
+776 4632([line])N
+974(k)X
+1038 0.3125(<character>)AX
+1 f
+976 4722(Mark)N
+1189(the)X
+1326(line)X
+1485(with)X
+1666(the)X
+1803(mark)X
+7 f
+2007(<character>)X
+1 f
+(.)S
+2614(The)X
+2778(expressions)X
+3192(``)X
+7 f
+3246('<character>)X
+1 f
+('')S
+3916(and)X
+976 4812(``)N
+7 f
+1030(`<character>)X
+1 f
+('')S
+1680(can)X
+1812(then)X
+1970(be)X
+2066(used)X
+2233(as)X
+2320(an)X
+2416(address)X
+2677(in)X
+2759(any)X
+2895(command)X
+3231(that)X
+3371(uses)X
+3529(one.)X
+976 4992(Line:)N
+1336(Unchanged.)X
+976 5082(Options:)N
+1336(None.)X
+3 f
+776 5262([range])N
+1046(m[ove])X
+1303(line)X
+1 f
+976 5352(Move)N
+1188(the)X
+1312(speci\256ed)X
+1623(lines)X
+1800(after)X
+1974(the)X
+2098(target)X
+2307(line.)X
+2493(A)X
+2577(target)X
+2786(line)X
+2932(of)X
+3025(0)X
+3091(places)X
+3318(the)X
+3442(lines)X
+3619(at)X
+3703(the)X
+3827(begin-)X
+976 5442(ning)N
+1138(of)X
+1225(the)X
+1343(\256le.)X
+976 5622(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(\256rst)X
+1802(of)X
+1889(the)X
+2007(moved)X
+2245(lines.)X
+976 5712(Options:)N
+1336(None.)X
+
+40 p
+%%Page: 40 39
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-40)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+776 762 0.3250(mk[exrc][!])AN
+1190(\256le)X
+1 f
+976 852(Write)N
+1180(the)X
+1299(abbreviations,)X
+1772(editor)X
+1980(options)X
+2237(and)X
+2375(maps)X
+2566(to)X
+2650(the)X
+2770(speci\256ed)X
+3077(\256le.)X
+3241(Information)X
+3646(is)X
+3721(written)X
+3970(in)X
+976 942(a)N
+1034(form)X
+1212(which)X
+1430(can)X
+1564(later)X
+1729(be)X
+1827(read)X
+1988(back)X
+2162(in)X
+2246(using)X
+2441(the)X
+3 f
+2561(ex)X
+2659(source)X
+1 f
+2904(command.)X
+3282(If)X
+7 f
+3358(file)X
+1 f
+3572(already)X
+3830(exists,)X
+976 1032(the)N
+3 f
+1097(mkexrc)X
+1 f
+1379(command)X
+1718(will)X
+1865(fail.)X
+2035(This)X
+2200(check)X
+2411(can)X
+2546(be)X
+2645(overridden)X
+3016(by)X
+3119(appending)X
+3476(a)X
+3536(``)X
+7 f
+3590(!)X
+1 f
+('')S
+3736(character)X
+976 1122(to)N
+1058(the)X
+1176(command.)X
+976 1302(Line:)N
+1336(Unchanged.)X
+976 1392(Options:)N
+1336(None.)X
+3 f
+776 1572(n[ext][!])N
+1078([\256le)X
+1227(...])X
+1 f
+976 1662(Edit)N
+1142(the)X
+1273(next)X
+1444(\256le)X
+1579(from)X
+1768(the)X
+1899(argument)X
+2235(list.)X
+2405(The)X
+3 f
+2563(next)X
+1 f
+2743(command)X
+3092(will)X
+3249(fail)X
+3389(if)X
+3471(the)X
+3603(\256le)X
+3739(has)X
+3880(been)X
+976 1752(modi\256ed)N
+1287(since)X
+1478(the)X
+1602(last)X
+1739(complete)X
+2059(write.)X
+2290(This)X
+2458(check)X
+2672(can)X
+2810(be)X
+2912(overridden)X
+3286(by)X
+3392(appending)X
+3752(the)X
+3876(``)X
+7 f
+3930(!)X
+1 f
+('')S
+976 1842(character)N
+1294(to)X
+1378(the)X
+1498(command)X
+1836(name.)X
+2072(The)X
+2219(argument)X
+2544(list)X
+2663(can)X
+2797(optionally)X
+3143(be)X
+3241(replaced)X
+3536(by)X
+3639(specifying)X
+3996(a)X
+976 1932(new)N
+1131(one)X
+1268(as)X
+1355(arguments)X
+1709(to)X
+1791(this)X
+1926(command.)X
+2302(In)X
+2389(this)X
+2524(case,)X
+2703(editing)X
+2945(starts)X
+3134(with)X
+3296(the)X
+3414(\256rst)X
+3558(\256le)X
+3680(on)X
+3780(the)X
+3898(new)X
+976 2022(list.)N
+976 2202(Line:)N
+1336(Set)X
+1458(as)X
+1545(described)X
+1873(for)X
+1987(the)X
+3 f
+2105(edit)X
+1 f
+2254(command.)X
+976 2292(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+1856(options)X
+3 f
+2111(autowrite)X
+1 f
+2461(and)X
+3 f
+2597(writeany)X
+1 f
+2900(.)X
+3 f
+776 2472([line])N
+974(o[pen])X
+1212(/pattern/)X
+1530([\257ags])X
+1 f
+976 2562(Enter)N
+1172(open)X
+1350(mode.)X
+1590(Open)X
+1786(mode)X
+1987(is)X
+2063(the)X
+2184(same)X
+2372(as)X
+2462(being)X
+2663(in)X
+3 f
+2748(vi)X
+1 f
+2810(,)X
+2853(but)X
+2978(with)X
+3143(a)X
+3202(one-line)X
+3488(window.)X
+3809(All)X
+3934(the)X
+976 2652(standard)N
+3 f
+1278(vi)X
+1 f
+1370(commands)X
+1747(are)X
+1876(available.)X
+2236(If)X
+2320(a)X
+2386(match)X
+2612(is)X
+2694(found)X
+2910(for)X
+3033(the)X
+3160(optional)X
+3451(RE)X
+3582(argument,)X
+3934(the)X
+976 2742(cursor)N
+1197(is)X
+1270(set)X
+1379(to)X
+1461(the)X
+1579(start)X
+1737(of)X
+1824(the)X
+1942(matching)X
+2260(pattern.)X
+2 f
+976 2922(This)N
+1133(command)X
+1465(is)X
+1538(not)X
+1660(yet)X
+1774(implemented.)X
+1 f
+976 3102(Line:)N
+1336(Unchanged,)X
+1750(unless)X
+1978(the)X
+2104(optional)X
+2394(RE)X
+2524(is)X
+2605(speci\256ed,)X
+2938(in)X
+3028(which)X
+3253(case)X
+3421(it)X
+3494(is)X
+3576(set)X
+3694(to)X
+3785(the)X
+3912(line)X
+1336 3192(where)N
+1553(the)X
+1671(matching)X
+1989(pattern)X
+2232(is)X
+2305(found.)X
+976 3282(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(open)X
+1 f
+2040(option.)X
+3 f
+776 3462(pre[serve])N
+1 f
+976 3552(Save)N
+1161(the)X
+1288(\256le)X
+1419(in)X
+1510(a)X
+1575(form)X
+1760(that)X
+1909(can)X
+2050(later)X
+2222(be)X
+2327(recovered)X
+2674(using)X
+2877(the)X
+3 f
+3005(ex)X
+9 f
+3111(-)X
+3113(-)X
+3 f
+3157(r)X
+1 f
+3223(option.)X
+3497(When)X
+3719(the)X
+3847(\256le)X
+3979(is)X
+976 3642(preserved,)N
+1329(an)X
+1425(email)X
+1623(message)X
+1915(is)X
+1988(sent)X
+2137(to)X
+2219(the)X
+2337(user.)X
+976 3822(Line:)N
+1336(Unchanged.)X
+976 3912(Options:)N
+1336(None.)X
+3 f
+776 4092(prev[ious][!])N
+1 f
+976 4182(Edit)N
+1136(the)X
+1261(previous)X
+1564(\256le)X
+1693(from)X
+1876(the)X
+2001(argument)X
+2331(list.)X
+2495(The)X
+3 f
+2647(previous)X
+1 f
+2967(command)X
+3310(will)X
+3461(fail)X
+3595(if)X
+3671(the)X
+3796(\256le)X
+3925(has)X
+976 4272(been)N
+1155(modi\256ed)X
+1466(since)X
+1658(the)X
+1783(last)X
+1921(complete)X
+2241(write.)X
+2472(This)X
+2640(check)X
+2854(can)X
+2992(be)X
+3094(overridden)X
+3468(by)X
+3574(appending)X
+3934(the)X
+976 4362(``)N
+7 f
+1030(!)X
+1 f
+('')S
+1172(character)X
+1488(to)X
+1570(the)X
+1688(command)X
+2024(name.)X
+976 4542(Line:)N
+1336(Set)X
+1458(as)X
+1545(described)X
+1873(for)X
+1987(the)X
+3 f
+2105(edit)X
+1 f
+2254(command.)X
+976 4632(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+1856(options)X
+3 f
+2111(autowrite)X
+1 f
+2461(and)X
+3 f
+2597(writeany)X
+1 f
+2900(.)X
+2960(None.)X
+3 f
+776 4812([range])N
+1046(p[rint])X
+1293([count])X
+1558([\257ags])X
+1 f
+976 4902(Display)N
+1245(the)X
+1363(speci\256ed)X
+1668(lines.)X
+976 5082(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(displayed.)X
+976 5172(Options:)N
+1336(None.)X
+3 f
+776 5352([line])N
+974(pu[t])X
+1163([buffer])X
+1 f
+976 5442(Append)N
+1250(buffer)X
+1467(contents)X
+1755(to)X
+1838(the)X
+1957(current)X
+2206(line.)X
+2387(If)X
+2462(a)X
+2519(buffer)X
+2737(is)X
+2811(speci\256ed,)X
+3137(its)X
+3233(contents)X
+3521(are)X
+3641(appended)X
+3970(to)X
+976 5532(the)N
+1094(line,)X
+1254(otherwise,)X
+1606(the)X
+1724(contents)X
+2011(of)X
+2098(the)X
+2216(unnamed)X
+2530(buffer)X
+2747(are)X
+2866(used.)X
+976 5712(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(line)X
+1798(after)X
+1966(the)X
+2084(current)X
+2332(line.)X
+
+41 p
+%%Page: 41 40
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-41)X
+1 f
+976 762(Options:)N
+1336(None.)X
+3 f
+776 942(q[uit][!])N
+1 f
+976 1032(End)N
+1133(the)X
+1259(editing)X
+1509(session.)X
+1808(If)X
+1890(the)X
+2016(\256le)X
+2146(has)X
+2281(been)X
+2461(modi\256ed)X
+2773(since)X
+2966(the)X
+3092(last)X
+3231(complete)X
+3554(write,)X
+3768(the)X
+3 f
+3895(quit)X
+1 f
+976 1122(command)N
+1314(will)X
+1460(fail.)X
+1629(This)X
+1793(check)X
+2003(may)X
+2163(be)X
+2261(overridden)X
+2631(by)X
+2733(appending)X
+3089(a)X
+3147(``)X
+7 f
+3201(!)X
+1 f
+('')S
+3345(character)X
+3663(to)X
+3747(the)X
+3867(com-)X
+976 1212(mand.)N
+976 1392(If)N
+1053(there)X
+1237(are)X
+1359(more)X
+1547(\256les)X
+1703(to)X
+1788(edit,)X
+1951(the)X
+3 f
+2072(quit)X
+1 f
+2232(command)X
+2571(will)X
+2718(fail.)X
+2888(Appending)X
+3268(a)X
+3328(``)X
+7 f
+3382(!)X
+1 f
+('')S
+3528(character)X
+3848(to)X
+3934(the)X
+976 1482(command)N
+1315(name)X
+1511(or)X
+1600(entering)X
+1885(two)X
+3 f
+2027(quit)X
+1 f
+2186(commands)X
+2555(\(i.e.)X
+3 f
+2722(wq)X
+1 f
+2824(,)X
+3 f
+2866(quit)X
+1 f
+3003(,)X
+3 f
+3045(xit)X
+1 f
+3156(or)X
+3 f
+3245(ZZ)X
+1 f
+3351(\))X
+3400(in)X
+3484(a)X
+3542(row\))X
+3716(will)X
+3862(over-)X
+976 1572(ride)N
+1121(this)X
+1256(check)X
+1464(and)X
+1600(the)X
+1718(editor)X
+1925(will)X
+2069(exit.)X
+976 1752(Line:)N
+1336(Unchanged.)X
+976 1842(Options:)N
+1336(None.)X
+3 f
+776 2022([line])N
+974(r[ead][!])X
+1285([\256le])X
+1 f
+976 2112(Read)N
+1171(a)X
+1237(\256le.)X
+1409(A)X
+1497(copy)X
+1683(of)X
+1780(the)X
+1908(speci\256ed)X
+2223(\256le)X
+2355(is)X
+2438(appended)X
+2776(to)X
+2868(the)X
+2996(line.)X
+3186(If)X
+7 f
+3270(line)X
+1 f
+3492(is)X
+3575(0,)X
+3665(the)X
+3793(copy)X
+3979(is)X
+976 2202(inserted)N
+1252(at)X
+1332(the)X
+1452(beginning)X
+1794(of)X
+1883(the)X
+2003(\256le.)X
+2167(If)X
+2243(no)X
+2345(\256le)X
+2469(is)X
+2544(speci\256ed,)X
+2871(the)X
+2991(current)X
+3241(\256le)X
+3365(is)X
+3440(read;)X
+3623(if)X
+3694(there)X
+3877(is)X
+3952(no)X
+976 2292(current)N
+1233(\256le,)X
+1384(then)X
+7 f
+1552(file)X
+1 f
+1774(becomes)X
+2085(the)X
+2213(current)X
+2471(\256le.)X
+2643(If)X
+2727(there)X
+2918(is)X
+3001(no)X
+3111(current)X
+3369(\256le)X
+3501(and)X
+3647(no)X
+7 f
+3757(file)X
+1 f
+3979(is)X
+976 2382(speci\256ed,)N
+1301(then)X
+1459(the)X
+3 f
+1577(read)X
+1 f
+1753(command)X
+2089(will)X
+2233(fail.)X
+976 2562(If)N
+7 f
+1054(file)X
+1 f
+1270(is)X
+1347(preceded)X
+1662(by)X
+1766(a)X
+1826(``)X
+7 f
+1880(!)X
+1 f
+('')S
+2026(character,)X
+7 f
+2366(file)X
+1 f
+2582(is)X
+2659(treated)X
+2902(as)X
+2993(if)X
+3067(it)X
+3136(were)X
+3318(a)X
+3379(shell)X
+3555(command,)X
+3916(and)X
+976 2652(passed)N
+1213(to)X
+1298(the)X
+1419(program)X
+1713(named)X
+1949(by)X
+2051(the)X
+7 f
+2171(SHELL)X
+1 f
+2433(environment)X
+2860(variable.)X
+3181(The)X
+3328(standard)X
+3622(and)X
+3760(standard)X
+976 2742(error)N
+1155(outputs)X
+1412(of)X
+1501(that)X
+1643(command)X
+1981(are)X
+2102(read)X
+2263(into)X
+2409(the)X
+2529(\256le)X
+2654(after)X
+2825(the)X
+2946(speci\256ed)X
+3254(line.)X
+3437(The)X
+3585(special)X
+3831(mean-)X
+976 2832(ing)N
+1098(of)X
+1185(the)X
+1303(``)X
+7 f
+1357(!)X
+1 f
+('')S
+1499(character)X
+1815(can)X
+1947(be)X
+2043(overridden)X
+2411(by)X
+2511(escaping)X
+2812(it)X
+2876(with)X
+3038(a)X
+3094(backslash)X
+3426(\(``)X
+7 f
+3507(\\)X
+1 f
+(''\))S
+3656(character.)X
+976 3012(Line:)N
+1336(When)X
+1550(executed)X
+1858(from)X
+3 f
+2036(ex)X
+1 f
+2112(,)X
+2154(the)X
+2274(current)X
+2524(line)X
+2666(is)X
+2741(set)X
+2852(to)X
+2936(the)X
+3056(last)X
+3189(line)X
+3331(read.)X
+3532(When)X
+3746(executed)X
+1336 3102(from)N
+3 f
+1512(vi)X
+1 f
+1574(,)X
+1614(the)X
+1732(current)X
+1980(line)X
+2120(is)X
+2193(set)X
+2302(to)X
+2384(the)X
+2502(\256rst)X
+2646(line)X
+2786(read.)X
+976 3192(Options:)N
+1336(None.)X
+3 f
+776 3372 0.4063(rec[over])AN
+1110(\256le)X
+1 f
+976 3462(Recover)N
+7 f
+1273(file)X
+1 f
+1494(if)X
+1572(it)X
+1645(was)X
+1799(previously)X
+2166(saved.)X
+2418(If)X
+2501(no)X
+2610(saved)X
+2822(\256le)X
+2953(by)X
+3062(that)X
+3211(name)X
+3414(exists,)X
+3645(the)X
+3 f
+3772(recover)X
+1 f
+976 3552(command)N
+1312(behaves)X
+1591(similarly)X
+1895(to)X
+1977(the)X
+3 f
+2095(edit)X
+1 f
+2244(command.)X
+976 3732(Line:)N
+1336(Set)X
+1458(as)X
+1545(described)X
+1873(for)X
+1987(the)X
+3 f
+2105(edit)X
+1 f
+2254(command.)X
+976 3822(Options:)N
+1336(None.)X
+3 f
+776 4002(res[ize])N
+1047([+|-]size)X
+976 4092(Vi)N
+1 f
+1081(mode)X
+1284(only.)X
+1492(Grow)X
+1701(or)X
+1794(shrink)X
+2020(the)X
+2144(current)X
+2398(screen.)X
+2670(If)X
+7 f
+2750(size)X
+1 f
+2968(is)X
+3047(a)X
+3109(positive,)X
+3408(signed)X
+3643(number,)X
+3934(the)X
+976 4182(current)N
+1227(screen)X
+1456(is)X
+1532(grown)X
+1760(by)X
+1863(that)X
+2006(many)X
+2207(lines.)X
+2421(If)X
+7 f
+2498(size)X
+1 f
+2713(is)X
+2789(a)X
+2848(negative,)X
+3163(signed)X
+3395(number,)X
+3683(the)X
+3804(current)X
+976 4272(screen)N
+1212(is)X
+1295(shrunk)X
+1543(by)X
+1653(that)X
+1803(many)X
+2011(lines.)X
+2232(If)X
+7 f
+2316(size)X
+1 f
+2538(is)X
+2621(not)X
+2753(signed,)X
+3012(the)X
+3141(current)X
+3400(screen)X
+3637(is)X
+3721(set)X
+3841(to)X
+3934(the)X
+976 4362(speci\256ed)N
+7 f
+1281(size)X
+1 f
+(.)S
+1533(Applicable)X
+1905(only)X
+2067(to)X
+2149(split)X
+2306(screens.)X
+976 4542(Line:)N
+1336(Unchanged.)X
+976 4632(Options:)N
+1336(None.)X
+3 f
+776 4812(rew[ind][!])N
+1 f
+976 4902(Rewind)N
+1251(the)X
+1375(argument)X
+1704(list.)X
+1867(If)X
+1947(the)X
+2071(current)X
+2325(\256le)X
+2453(has)X
+2586(been)X
+2764(modi\256ed)X
+3074(since)X
+3265(the)X
+3389(last)X
+3526(complete)X
+3847(write,)X
+976 4992(the)N
+3 f
+1097(rewind)X
+1 f
+1360(command)X
+1699(will)X
+1846(fail.)X
+2016(This)X
+2180(check)X
+2390(may)X
+2550(be)X
+2648(overridden)X
+3018(by)X
+3120(appending)X
+3476(the)X
+3596(``)X
+7 f
+3650(!)X
+1 f
+('')S
+3794(charac-)X
+976 5082(ter)N
+1081(to)X
+1163(the)X
+1281(command.)X
+976 5262(Otherwise,)N
+1346(the)X
+1464(current)X
+1712(\256le)X
+1834(is)X
+1907(set)X
+2016(to)X
+2098(the)X
+2216(\256rst)X
+2360(\256le)X
+2482(in)X
+2564(the)X
+2682(argument)X
+3005(list.)X
+976 5442(Line:)N
+1336(Set)X
+1458(as)X
+1545(described)X
+1873(for)X
+1987(the)X
+3 f
+2105(edit)X
+1 f
+2254(command.)X
+976 5532(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2206(and)X
+3 f
+2342(writeany)X
+1 f
+2665(options.)X
+3 f
+776 5712(se[t])N
+944([option[=[value]])X
+1544(...])X
+1651([nooption)X
+1999(...])X
+2106([option?)X
+2410(...])X
+2517([all])X
+1 f
+976 5802(Display)N
+1252(or)X
+1346(set)X
+1462(editor)X
+1676(options.)X
+1979(When)X
+2199(no)X
+2307(arguments)X
+2669(are)X
+2796(speci\256ed,)X
+3129(the)X
+3255(editor)X
+3470(option)X
+3 f
+3702(term)X
+1 f
+3868(,)X
+3916(and)X
+
+42 p
+%%Page: 42 41
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-42)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+1 f
+976 762(any)N
+1115(editor)X
+1325(options)X
+1583(whose)X
+1811(values)X
+2039(have)X
+2214(been)X
+2389(changed)X
+2679(from)X
+2857(the)X
+2977(default)X
+3222(settings)X
+3488(are)X
+3609(displayed.)X
+3978(If)X
+976 852(the)N
+1094(argument)X
+7 f
+1417(all)X
+1 f
+1581(is)X
+1654(speci\256ed,)X
+1979(the)X
+2097(values)X
+2322(of)X
+2409(all)X
+2509(of)X
+2596(editor)X
+2803(options)X
+3058(are)X
+3177(displayed.)X
+976 1032(Specifying)N
+1353(an)X
+1459(option)X
+1693(name)X
+1897(followed)X
+2212(by)X
+2322(the)X
+2450(character)X
+2776(``)X
+7 f
+2830(?)X
+1 f
+('')S
+2982(causes)X
+3222(the)X
+3350(current)X
+3609(value)X
+3814(of)X
+3912(that)X
+976 1122(option)N
+1203(to)X
+1288(be)X
+1387(displayed.)X
+1757(The)X
+1905(``)X
+7 f
+1959(?)X
+1 f
+('')S
+2104(can)X
+2239(be)X
+2338(separated)X
+2665(from)X
+2843(the)X
+2963(option)X
+3189(name)X
+3385(by)X
+3487(whitespace)X
+3866(char-)X
+976 1212(acters.)N
+1225(The)X
+1371(``)X
+7 f
+1425(?)X
+1 f
+('')S
+1568(is)X
+1642(necessary)X
+1976(only)X
+2139(for)X
+2254(Boolean)X
+2543(valued)X
+2779(options.)X
+3076(Boolean)X
+3365(options)X
+3622(can)X
+3756(be)X
+3854(given)X
+976 1302(values)N
+1209(by)X
+1317(the)X
+1443(form)X
+1627(``)X
+7 f
+1681(set)X
+1881(option)X
+1 f
+('')S
+2251(to)X
+2341(turn)X
+2498(them)X
+2686(on,)X
+2814(or)X
+2909(``)X
+7 f
+2963(set)X
+3162(nooption)X
+1 f
+('')S
+3627(to)X
+3716(turn)X
+3872(them)X
+976 1392(off.)N
+1131(String)X
+1347(and)X
+1484(numeric)X
+1768(options)X
+2024(can)X
+2158(be)X
+2256(assigned)X
+2554(by)X
+2656(the)X
+2776(form)X
+2954(``)X
+7 f
+3008(set)X
+3202(option=value)X
+1 f
+(''.)S
+3894(Any)X
+976 1482(whitespace)N
+1359(characters)X
+1712(in)X
+1800(strings)X
+2039(can)X
+2176(be)X
+2277(included)X
+2578(literally)X
+2852(by)X
+2957(preceding)X
+3299(each)X
+3472(with)X
+3639(a)X
+3700(backslash.)X
+976 1572(More)N
+1180(than)X
+1349(one)X
+1496(option)X
+1731(can)X
+1874(be)X
+1981(set)X
+2101(or)X
+2199(listed)X
+2403(by)X
+2514(a)X
+2581(single)X
+2803(set)X
+2923(command,)X
+3290(by)X
+3401(specifying)X
+3766(multiple)X
+976 1662(arguments,)N
+1350(each)X
+1518(separated)X
+1842(from)X
+2018(the)X
+2136(next)X
+2294(by)X
+2394(whitespace)X
+2771(characters.)X
+976 1842(Line:)N
+1336(Unchanged.)X
+976 1932(Options:)N
+1336(None.)X
+3 f
+776 2112(sh[ell])N
+1 f
+976 2202(Run)N
+1131(a)X
+1189(shell)X
+1362(program.)X
+1696(The)X
+1843(program)X
+2137(named)X
+2373(by)X
+2475(the)X
+3 f
+2596(shell)X
+1 f
+2774(option)X
+3001(is)X
+3077(run)X
+3207(with)X
+3372(a)X
+3 f
+9 f
+3431(-)X
+3433(-)X
+3 f
+3477(i)X
+1 f
+3522(\(for)X
+3666(interactive\))X
+976 2292(\257ag.)N
+1156(Editing)X
+1411(is)X
+1484(resumed)X
+1776(when)X
+1970(that)X
+2110(program)X
+2402(exits.)X
+976 2472(Line:)N
+1336(Unchanged.)X
+976 2562(Options:)N
+1336(None.)X
+3 f
+776 2742(so[urce])N
+1073(\256le)X
+1 f
+976 2832(Read)N
+1161(and)X
+1297(execute)X
+3 f
+1563(ex)X
+1 f
+1659(commands)X
+2026(from)X
+2202(a)X
+2258(\256le.)X
+3 f
+2420(Source)X
+1 f
+2676(commands)X
+3043(may)X
+3201(be)X
+3297(nested.)X
+976 3012(Line:)N
+1336(Unchanged.)X
+976 3102(Options:)N
+1336(None.)X
+3 f
+776 3282(sp[lit])N
+996([\256le)X
+1145(...])X
+976 3372(Vi)N
+1 f
+1078(mode)X
+1278(only.)X
+1482(Split)X
+1655(the)X
+1776(screen.)X
+2045(The)X
+2193(current)X
+2444(screen)X
+2673(is)X
+2749(split)X
+2909(into)X
+3056(two)X
+3199(screens,)X
+3479(of)X
+3569(approximately)X
+976 3462(equal)N
+1173(size.)X
+1361(If)X
+1438(the)X
+1559(cursor)X
+1783(is)X
+1859(in)X
+1944(the)X
+2065(lower)X
+2271(half)X
+2419(of)X
+2509(the)X
+2630(screen,)X
+2879(the)X
+3000(screen)X
+3229(will)X
+3376(split)X
+3536(up,)X
+3658(i.e.)X
+3778(the)X
+3898(new)X
+976 3552(screen)N
+1213(will)X
+1368(be)X
+1475(above)X
+1699(the)X
+1829(old)X
+1963(one.)X
+2151(If)X
+2237(the)X
+2367(cursor)X
+2600(is)X
+2685(in)X
+2779(the)X
+2909(upper)X
+3124(half)X
+3281(of)X
+3380(the)X
+3510(screen,)X
+3768(the)X
+3898(new)X
+976 3642(screen)N
+1202(will)X
+1346(be)X
+1442(below)X
+1658(the)X
+1776(old)X
+1898(one.)X
+976 3822(If)N
+7 f
+1052(file)X
+1 f
+1267(is)X
+1343(speci\256ed,)X
+1671(the)X
+1792(new)X
+1949(screen)X
+2178(is)X
+2254(editing)X
+2499(that)X
+2642(\256le,)X
+2787(otherwise,)X
+3142(both)X
+3307(screens)X
+3567(are)X
+3689(editing)X
+3934(the)X
+976 3912(same)N
+1165(\256le,)X
+1310(and)X
+1449(changes)X
+1731(in)X
+1816(each)X
+1987(will)X
+2134(be)X
+2233(be)X
+2332(re\257ected)X
+2632(in)X
+2717(the)X
+2838(other.)X
+3066(The)X
+3214(argument)X
+3540(list)X
+3660(for)X
+3777(the)X
+3898(new)X
+976 4002(screen)N
+1204(consists)X
+1479(of)X
+1568(the)X
+1688(list)X
+1807(of)X
+1896(\256les)X
+2051(speci\256ed)X
+2358(as)X
+2447(arguments)X
+2803(to)X
+2888(this)X
+3026(command,)X
+3385(or,)X
+3495(the)X
+3616(current)X
+3867(path-)X
+976 4092(name)N
+1170(if)X
+1239(no)X
+1339(\256les)X
+1492(are)X
+1611(speci\256ed.)X
+976 4272(Line:)N
+1336(If)X
+7 f
+1410(file)X
+1 f
+1622(is)X
+1695(speci\256ed,)X
+2020(set)X
+2129(as)X
+2216(for)X
+2330(the)X
+3 f
+2448(edit)X
+1 f
+2597(command,)X
+2953(otherwise)X
+3285(unchanged.)X
+976 4362(Options:)N
+1336(None.)X
+3 f
+776 4542([range])N
+1046(s[ubstitute])X
+1453([/pattern/replace/])X
+2097([options])X
+2419([count])X
+2684([\257ags])X
+776 4632([range])N
+1046(&)X
+1133([options])X
+1455([count])X
+1720([\257ags])X
+776 4722([range])N
+1046(\304)X
+1093([options])X
+1415([count])X
+1680([\257ags])X
+1 f
+976 4812(Make)N
+1185(substitutions.)X
+1654(Replace)X
+1939(the)X
+2063(\256rst)X
+2214(instance)X
+2504(of)X
+7 f
+2598(pattern)X
+1 f
+2961(with)X
+3130(the)X
+3255(string)X
+7 f
+3464(replace)X
+1 f
+3827(on)X
+3934(the)X
+976 4902(speci\256ed)N
+1343(line\(s\).)X
+1670(If)X
+1806(the)X
+1986(``)X
+7 f
+2040(/pattern/repl/)X
+1 f
+('')S
+2847(argument)X
+3231(is)X
+3365(not)X
+3548(speci\256ed,)X
+3934(the)X
+976 4992(``)N
+7 f
+1030(/pattern/repl/)X
+1 f
+('')S
+1776(from)X
+1952(the)X
+2070(previous)X
+3 f
+2366(substitute)X
+1 f
+2719(command)X
+3055(is)X
+3128(used.)X
+976 5172(If)N
+7 f
+1054(options)X
+1 f
+1414(includes)X
+1705(the)X
+1827(letter)X
+2017(``)X
+7 f
+2071(c)X
+1 f
+('')S
+2198(\(con\256rm\),)X
+2546(you)X
+2691(will)X
+2840(be)X
+2941(prompted)X
+3273(for)X
+3392(con\256rmation)X
+3826(before)X
+976 5262(each)N
+1152(replacement)X
+1573(is)X
+1654(done.)X
+1878(An)X
+2004(af\256rmative)X
+2384(response)X
+2693(\(in)X
+2810(English,)X
+3101(a)X
+3164(``)X
+7 f
+3218(y)X
+1 f
+('')S
+3347 0.3750(character\))AX
+3697(causes)X
+3934(the)X
+976 5352(replacement)N
+1393(to)X
+1479(be)X
+1579(made.)X
+1817(A)X
+1899(quit)X
+2047(response)X
+2352(\(in)X
+2465(English,)X
+2753(a)X
+2813(``)X
+7 f
+2867(q)X
+1 f
+('')S
+2993 0.3750(character\))AX
+3341(causes)X
+3576(the)X
+3 f
+3699(substitute)X
+1 f
+976 5442(command)N
+1320(to)X
+1410(be)X
+1514(terminated.)X
+1925(Any)X
+2091(other)X
+2284(response)X
+2592(causes)X
+2829(the)X
+2954(replacement)X
+3374(not)X
+3503(to)X
+3592(be)X
+3695(made,)X
+3916(and)X
+976 5532(the)N
+3 f
+1094(substitute)X
+1 f
+1447(command)X
+1783(continues.)X
+2150(If)X
+7 f
+2224(options)X
+1 f
+2580(includes)X
+2867(the)X
+2985(letter)X
+3170(``)X
+7 f
+3224(g)X
+1 f
+('')S
+3346(\(global\),)X
+3641(all)X
+3742(nonover-)X
+976 5622(lapping)N
+1236(instances)X
+1550(of)X
+7 f
+1637(pattern)X
+1 f
+1993(in)X
+2075(the)X
+2193(line)X
+2333(are)X
+2452(replaced.)X
+976 5802(The)N
+3 f
+1122(&)X
+1 f
+1210(version)X
+1467(of)X
+1555(the)X
+1674(command)X
+2011(is)X
+2085(the)X
+2204(same)X
+2390(as)X
+2479(not)X
+2603(specifying)X
+2959(a)X
+3017(pattern)X
+3262(or)X
+3351(replacement)X
+3766(string)X
+3970(to)X
+
+43 p
+%%Page: 43 42
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-43)X
+1 f
+976 762(the)N
+3 f
+1098(substitute)X
+1 f
+1455(command,)X
+1815(and)X
+1955(the)X
+2077(``)X
+7 f
+2131(&)X
+1 f
+('')S
+2257(is)X
+2333(replaced)X
+2629(by)X
+2732(the)X
+2853(pattern)X
+3099(and)X
+3238(replacement)X
+3654(information)X
+976 852(from)N
+1152(the)X
+1270(previous)X
+1566(substitute)X
+1892(command.)X
+976 1032(The)N
+3 f
+1121(\304)X
+1 f
+1168(version)X
+1424(of)X
+1511(the)X
+1629(command)X
+1966(is)X
+2040(the)X
+2159(same)X
+2345(as)X
+3 f
+2433(&)X
+1 f
+2521(and)X
+3 f
+2658(s)X
+1 f
+2689(,)X
+2730(except)X
+2961(that)X
+3102(the)X
+3221(search)X
+3448(pattern)X
+3692(used)X
+3860(is)X
+3934(the)X
+976 1122(last)N
+1107(RE)X
+1229(used)X
+1396(in)X
+2 f
+1478(any)X
+1 f
+1614(command,)X
+1970(not)X
+2092(necessarily)X
+2469(the)X
+2587(one)X
+2723(used)X
+2890(in)X
+2972(the)X
+3090(last)X
+3 f
+3221(substitute)X
+1 f
+3574(command.)X
+976 1302(For)N
+1107(example,)X
+1419(in)X
+1501(the)X
+1619(sequence)X
+7 f
+1296 1425(s/red/blue/)N
+1296 1515(/green)N
+1296 1605(\304)N
+1 f
+976 1728(the)N
+1094(``)X
+7 f
+1148(\304)X
+1 f
+('')S
+1270(is)X
+1343(equivalent)X
+1697(to)X
+1779(``)X
+7 f
+1833(s/green/blue/)X
+1 f
+(''.)S
+976 1908(The)N
+3 f
+1126(substitute)X
+1 f
+1484(command)X
+1825(may)X
+1988(be)X
+2089(interrupted,)X
+2486(using)X
+2685(the)X
+2809(terminal)X
+3102(interrupt)X
+3404(character.)X
+3766(All)X
+3894(sub-)X
+976 1998(stitutions)N
+1288(completed)X
+1642(before)X
+1868(the)X
+1986(interrupt)X
+2282(are)X
+2401(retained.)X
+976 2178(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(upon)X
+2109(which)X
+2325(a)X
+2381(substitution)X
+2773(was)X
+2918(made.)X
+976 2268(Options:)N
+1336(None.)X
+3 f
+776 2448(su[spend][!])N
+776 2538(st[op][!])N
+776 2628(<control-Z>)N
+1 f
+976 2718(Suspend)N
+1285(the)X
+1422(edit)X
+1581(session.)X
+1891(Appending)X
+2286(a)X
+2361(``)X
+7 f
+2415(!)X
+1 f
+('')S
+2576(character)X
+2911(to)X
+3012(these)X
+3216(commands)X
+3602(turns)X
+3801(off)X
+3934(the)X
+3 f
+976 2808(autowrite)N
+1 f
+1326(option)X
+1550(for)X
+1664(the)X
+1782(command.)X
+976 2988(Line:)N
+1336(Unchanged.)X
+976 3078(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2206(option.)X
+3 f
+776 3258(ta[g][!])N
+1038(tagstring)X
+1 f
+976 3348(Edit)N
+1133(the)X
+1255(\256le)X
+1381(containing)X
+1743(the)X
+1865(speci\256ed)X
+2174(tag.)X
+2337(Search)X
+2581(for)X
+2700(the)X
+2823(tagstring,)X
+3148(which)X
+3369(can)X
+3506(be)X
+3607(in)X
+3694(a)X
+3755(different)X
+976 3438(\256le.)N
+1149(If)X
+1234(the)X
+1363(tag)X
+1492(is)X
+1576(in)X
+1669(a)X
+1736(different)X
+2044(\256le,)X
+2197(then)X
+2366(the)X
+2495(new)X
+2660(\256le)X
+2792(is)X
+2875(edited.)X
+3141(If)X
+3225(the)X
+3353(current)X
+3611(\256le)X
+3743(has)X
+3880(been)X
+976 3528(modi\256ed)N
+1283(since)X
+1471(the)X
+1592(last)X
+1726(complete)X
+2043(write,)X
+2251(the)X
+3 f
+2372(tag)X
+1 f
+2502(command)X
+2841(will)X
+2988(fail.)X
+3159(This)X
+3325(check)X
+3537(can)X
+3673(be)X
+3773(overrid-)X
+976 3618(den)N
+1112(by)X
+1212(appending)X
+1566(the)X
+1684(``)X
+7 f
+1738(!)X
+1 f
+('')S
+1880(character)X
+2196(to)X
+2278(the)X
+2396(command)X
+2732(name.)X
+976 3798(The)N
+3 f
+1128(tag)X
+1 f
+1262(command)X
+1605(searches)X
+1905(for)X
+7 f
+2026(tagstring)X
+1 f
+2485(in)X
+2574(the)X
+2699(tags)X
+2855(\256le\(s\))X
+3070(speci\256ed)X
+3383(by)X
+3491(the)X
+3617(option.)X
+3889(\(See)X
+2 f
+976 3888(ctags)N
+1 f
+1145(\(1\))X
+1259(for)X
+1373(more)X
+1558(information)X
+1956(on)X
+2056(tags)X
+2205(\256les.\))X
+976 4068(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(line)X
+1798(indicated)X
+2112(by)X
+2212(the)X
+2330(tag.)X
+976 4158(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2186(,)X
+3 f
+2226(taglength)X
+1 f
+(,)S
+3 f
+2586(tags)X
+1 f
+2744(and)X
+3 f
+2880(writeany)X
+1 f
+3203(options.)X
+3 f
+776 4338(tagp[op][!])N
+1166([\256le)X
+1315(|)X
+1353(number])X
+1 f
+976 4428(Pop)N
+1122(to)X
+1206(the)X
+1326(speci\256ed)X
+1633(tag)X
+1753(in)X
+1837(the)X
+1957(tags)X
+2108(stack.)X
+2335(If)X
+2411(neither)X
+7 f
+2656(file)X
+1 f
+2871(or)X
+7 f
+2961(number)X
+1 f
+3272(is)X
+3348(speci\256ed,)X
+3676(the)X
+3 f
+3797(tagpop)X
+1 f
+976 4518(command)N
+1313(pops)X
+1485(to)X
+1568(the)X
+1686(most)X
+1861(recent)X
+2078(entry)X
+2263(on)X
+2363(the)X
+2481(tags)X
+2630(stack.)X
+2855(If)X
+7 f
+2929(file)X
+1 f
+3141(or)X
+7 f
+3228(number)X
+1 f
+3536(is)X
+3609(speci\256ed,)X
+3934(the)X
+3 f
+976 4608(tagpop)N
+1 f
+1242(command)X
+1589(pops)X
+1771(to)X
+1864(the)X
+1993(most)X
+2179(recent)X
+2407(entry)X
+2603(in)X
+2696(the)X
+2825(tags)X
+2985(stack)X
+3181(for)X
+3306(that)X
+3458(\256le,)X
+3612(or)X
+3711(numbered)X
+976 4698(entry)N
+1165(in)X
+1251(the)X
+1373(tags)X
+1526(stack,)X
+1735(respectively.)X
+2186(\(See)X
+2352(the)X
+3 f
+2473(display)X
+1 f
+2739(command)X
+3078(for)X
+3195(information)X
+3596(on)X
+3699(displaying)X
+976 4788(the)N
+1094(tags)X
+1243(stack.\))X
+976 4968(If)N
+1051(the)X
+1170(\256le)X
+1293(has)X
+1421(been)X
+1594(modi\256ed)X
+1899(since)X
+2085(the)X
+2204(last)X
+2337(complete)X
+2653(write,)X
+2860(the)X
+3 f
+2980(tagpop)X
+1 f
+3237(command)X
+3575(will)X
+3721(fail.)X
+3890(This)X
+976 5058(check)N
+1184(may)X
+1342(be)X
+1438(overridden)X
+1806(by)X
+1906(appending)X
+2260(a)X
+2316(``)X
+7 f
+2370(!)X
+1 f
+('')S
+2512(character)X
+2828(to)X
+2910(the)X
+3028(command)X
+3364(name.)X
+976 5238(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(line)X
+1798(indicated)X
+2112(by)X
+2212(the)X
+2330(tag.)X
+976 5328(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2186(,)X
+2226(and)X
+3 f
+2362(writeany)X
+1 f
+2685(options.)X
+3 f
+776 5508(tagt[op][!])N
+1 f
+976 5598(Pop)N
+1120(to)X
+1202(the)X
+1320(least)X
+1487(recent)X
+1704(tag)X
+1822(on)X
+1922(the)X
+2040(tags)X
+2189(stack,)X
+2394(clearing)X
+2673(the)X
+2791(tags)X
+2940(stack.)X
+976 5778(If)N
+1051(the)X
+1170(\256le)X
+1293(has)X
+1421(been)X
+1594(modi\256ed)X
+1899(since)X
+2085(the)X
+2204(last)X
+2337(complete)X
+2653(write,)X
+2860(the)X
+3 f
+2980(tagpop)X
+1 f
+3237(command)X
+3575(will)X
+3721(fail.)X
+3890(This)X
+
+44 p
+%%Page: 44 43
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-44)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+1 f
+976 762(check)N
+1184(may)X
+1342(be)X
+1438(overridden)X
+1806(by)X
+1906(appending)X
+2260(a)X
+2316(``)X
+7 f
+2370(!)X
+1 f
+('')S
+2512(character)X
+2828(to)X
+2910(the)X
+3028(command)X
+3364(name.)X
+976 942(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(line)X
+1798(indicated)X
+2112(by)X
+2212(the)X
+2330(tag.)X
+976 1032(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(autowrite)X
+1 f
+2186(,)X
+2226(and)X
+3 f
+2362(writeany)X
+1 f
+2685(options.)X
+3 f
+776 1212(una[bbrev])N
+1178(lhs)X
+1 f
+976 1302(Delete)N
+1206(an)X
+1302(abbreviation.)X
+1763(Delete)X
+7 f
+1993(lhs)X
+1 f
+2157(from)X
+2333(the)X
+2451(current)X
+2699(list)X
+2816(of)X
+2903(abbreviations.)X
+976 1482(Line:)N
+1336(Unchanged.)X
+976 1572(Options:)N
+1336(None.)X
+3 f
+776 1752(u[ndo])N
+1 f
+976 1842(Undo)N
+1177(the)X
+1298(last)X
+1432(change)X
+1683(made)X
+1880(to)X
+1965(the)X
+2086(\256le.)X
+2251(Changes)X
+2550(made)X
+2747(by)X
+3 f
+2850(global)X
+1 f
+3058(,)X
+3 f
+3101(v)X
+1 f
+(,)S
+3 f
+3184(visual)X
+1 f
+3406(and)X
+3545(map)X
+3706(sequences)X
+976 1932(are)N
+1103(considered)X
+1479(a)X
+1543(single)X
+1762(command.)X
+2146(If)X
+2228(repeated,)X
+2549(the)X
+3 f
+2675(u)X
+1 f
+2747(command)X
+3090(alternates)X
+3425(between)X
+3720(these)X
+3912(two)X
+976 2022(states,)N
+1194(and)X
+1330(is)X
+1403(its)X
+1498(own)X
+1656(inverse.)X
+976 2202(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(modi\256ed)X
+2233(by)X
+2333(the)X
+2451(command.)X
+976 2292(Options:)N
+1336(None.)X
+3 f
+776 2472(unm[ap][!])N
+1170(lhs)X
+1 f
+976 2562(Unmap)N
+1235(a)X
+1294(mapped)X
+1571(string.)X
+1816(Delete)X
+2049(the)X
+2170(command)X
+2509(mode)X
+2710(map)X
+2872(de\256nition)X
+3202(for)X
+7 f
+3320(lhs)X
+1 f
+(.)S
+3528(If)X
+3606(a)X
+3666(``)X
+7 f
+3720(!)X
+1 f
+('')S
+3866(char-)X
+976 2652(acter)N
+1153(is)X
+1226(appended)X
+1554(to)X
+1636(the)X
+1754(command)X
+2090(name,)X
+2304(delete)X
+2516(the)X
+2634(text)X
+2774(input)X
+2958(mode)X
+3156(map)X
+3314(de\256nition)X
+3640(instead.)X
+976 2832(Line:)N
+1336(Unchanged.)X
+976 2922(Options:)N
+1336(None.)X
+3 f
+776 3102(ve[rsion])N
+1 f
+976 3192(Display)N
+1245(the)X
+1363(version)X
+1619(of)X
+1706(the)X
+3 f
+1824(ex/vi)X
+1 f
+2004(editor.)X
+3 f
+776 3372([line])N
+974(vi[sual])X
+1247([type])X
+1468([count])X
+1733([\257ags])X
+976 3462(Ex)N
+1 f
+1092(mode)X
+1293(only.)X
+1498(Enter)X
+3 f
+1695(vi)X
+1 f
+1757(.)X
+1820(The)X
+7 f
+1968(type)X
+1 f
+2183(is)X
+2259(optional,)X
+2564(and)X
+2703(can)X
+2838(be)X
+2937(``)X
+7 f
+9 f
+2991(-)X
+1 f
+3035('',)X
+3132(``)X
+7 f
+3186(+)X
+1 f
+('')S
+3311(or)X
+3401(``)X
+7 f
+3455(\303)X
+1 f
+('',)S
+3600(as)X
+3690(in)X
+3775(the)X
+3 f
+3896(ex)X
+3996(z)X
+1 f
+976 3552(command,)N
+1337(to)X
+1424(specify)X
+1681(the)X
+1804(the)X
+1926(position)X
+2207(of)X
+2298(the)X
+2420(speci\256ed)X
+2729(line)X
+2873(in)X
+2959(the)X
+3081(screen)X
+3311(window.)X
+3633(\(The)X
+3809(default)X
+976 3642(is)N
+1054(to)X
+1141(place)X
+1336(the)X
+1459(line)X
+1605(at)X
+1689(the)X
+1813(top)X
+1941(of)X
+2034(the)X
+2158(screen)X
+2390(window.\))X
+2741(A)X
+7 f
+2825(count)X
+1 f
+3091(speci\256es)X
+3393(the)X
+3517(number)X
+3788(of)X
+3881(lines)X
+976 3732(that)N
+1116(will)X
+1260(initially)X
+1528(be)X
+1624(displayed.)X
+1991(\(The)X
+2163(default)X
+2406(is)X
+2479(the)X
+2597(value)X
+2791(of)X
+2878(the)X
+3 f
+2996(window)X
+1 f
+3282(editor)X
+3489(option.\))X
+976 3912(Line:)N
+1336(Unchanged)X
+1722(unless)X
+7 f
+1942(line)X
+1 f
+2154(is)X
+2227(speci\256ed,)X
+2552(in)X
+2634(which)X
+2850(case)X
+3009(it)X
+3073(is)X
+3146(set)X
+3255(to)X
+3337(that)X
+3477(line.)X
+976 4002(Options:)N
+1336(None.)X
+3 f
+776 4182(vi[sual][!])N
+1130([+cmd])X
+1397([\256le])X
+976 4272(Vi)N
+1 f
+1076(mode)X
+1274(only.)X
+1476(Edit)X
+1629(a)X
+1685(new)X
+1839(\256le.)X
+2001(Identical)X
+2302(to)X
+2384(the)X
+2502(``)X
+7 f
+2556(edit[!])X
+2940([+cmd])X
+3276([file])X
+1 f
+('')S
+3638(command.)X
+3 f
+776 4452(viu[sage])N
+1103([command])X
+1 f
+976 4542(Display)N
+1253(usage)X
+1464(for)X
+1586(a)X
+3 f
+1650(vi)X
+1 f
+1740(command.)X
+2124(If)X
+7 f
+2206(command)X
+1 f
+2570(is)X
+2651(speci\256ed,)X
+2984(a)X
+3048(usage)X
+3259(statement)X
+3595(for)X
+3718(that)X
+3867(com-)X
+976 4632(mand)N
+1174(is)X
+1247(displayed.)X
+1614(Otherwise,)X
+1984(usage)X
+2187(statements)X
+2545(for)X
+2659(all)X
+3 f
+2759(vi)X
+1 f
+2841(commands)X
+3208(are)X
+3327(displayed.)X
+976 4812(Line:)N
+1336(Unchanged.)X
+976 4902(Options:)N
+1336(None.)X
+3 f
+776 5082([range])N
+1046 0.3611(w[rite][!])AX
+1380([>>])X
+1546([\256le])X
+776 5172([range])N
+1046(w[rite])X
+1299([!])X
+1400([\256le])X
+776 5262([range])N
+1046(wn[!])X
+1249([>>])X
+1415([\256le])X
+776 5352([range])N
+1046(wq[!])X
+1249([>>])X
+1415([\256le])X
+1 f
+976 5442(Write)N
+1183(the)X
+1305(\256le.)X
+1472(The)X
+1622(speci\256ed)X
+1932(lines)X
+2108(\(the)X
+2258(entire)X
+2466(\256le,)X
+2613(if)X
+2687(no)X
+2792(range)X
+2996(is)X
+3074(given\))X
+3304(is)X
+3382(written)X
+3634(to)X
+7 f
+3721(file)X
+1 f
+(.)S
+3978(If)X
+7 f
+976 5532(file)N
+1 f
+1195(is)X
+1275(not)X
+1403(speci\256ed,)X
+1734(the)X
+1858(current)X
+2112(pathname)X
+2450(is)X
+2529(used.)X
+2742(If)X
+7 f
+2822(file)X
+1 f
+3040(is)X
+3119(speci\256ed,)X
+3450(and)X
+3592(it)X
+3662(exists,)X
+3890(or)X
+3983(if)X
+976 5622(the)N
+1101(current)X
+1356(pathname)X
+1695(was)X
+1847(set)X
+1963(using)X
+2163(the)X
+3 f
+2288(\256le)X
+1 f
+2417(command,)X
+2780(and)X
+2923(the)X
+3049(\256le)X
+3179(already)X
+3444(exists,)X
+3674(these)X
+3867(com-)X
+976 5712(mands)N
+1212(will)X
+1363(fail.)X
+1537(Appending)X
+1919(a)X
+1981(``)X
+7 f
+2035(!)X
+1 f
+('')S
+2183(character)X
+2505(to)X
+2593(the)X
+2717(command)X
+3059(name)X
+3259(will)X
+3409(override)X
+3703(this)X
+3844(check)X
+976 5802(and)N
+1112(the)X
+1230(write)X
+1415(will)X
+1559(be)X
+1655(attempted,)X
+2011(regardless.)X
+
+45 p
+%%Page: 45 44
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+1237(\(Ex)X
+1377(Commands\))X
+3658(USD:13-45)X
+1 f
+976 762(Specifying)N
+1348(the)X
+1471(optional)X
+1758(``)X
+7 f
+1812(>>)X
+1 f
+('')S
+1987(string)X
+2194(will)X
+2344(cause)X
+2549(the)X
+2673(write)X
+2864(to)X
+2952(be)X
+3054(appended)X
+3388(to)X
+3476(the)X
+3600(\256le,)X
+3748(in)X
+3836(which)X
+976 852(case)N
+1135(no)X
+1235(tests)X
+1397(are)X
+1516(made)X
+1710(for)X
+1824(the)X
+1942(\256le)X
+2064(already)X
+2321(existing.)X
+976 1032(If)N
+1060(the)X
+1188(\256le)X
+1320(is)X
+1403(preceded)X
+1724(by)X
+1834(a)X
+1900(``)X
+7 f
+1954(!)X
+1 f
+('')S
+2106(character,)X
+2452(the)X
+2580(program)X
+2882(named)X
+3126(in)X
+3218(the)X
+3347(SHELL)X
+3627(environment)X
+976 1122(variable)N
+1258(is)X
+1334(invoked)X
+1615(with)X
+1780(\256le)X
+1905(as)X
+1995(its)X
+2093(second)X
+2339(argument,)X
+2685(and)X
+2824(the)X
+2945(speci\256ed)X
+3253(lines)X
+3427(are)X
+3549(passed)X
+3786(as)X
+3876(stan-)X
+976 1212(dard)N
+1143(input)X
+1331(to)X
+1417(that)X
+1561(command.)X
+1941(The)X
+2090(``)X
+7 f
+2144(!)X
+1 f
+('')S
+2291(in)X
+2378(this)X
+2518(usage)X
+2726(must)X
+2906(be)X
+3007(separated)X
+3336(from)X
+3517(command)X
+3858(name)X
+976 1302(by)N
+1080(at)X
+1162(least)X
+1333(one)X
+1473(whitespace)X
+1854(character.)X
+2214(The)X
+2363(special)X
+2610(meaning)X
+2910(of)X
+3001(the)X
+3122(``)X
+7 f
+3176(!)X
+1 f
+('')S
+3321(may)X
+3482(be)X
+3581(overridden)X
+3952(by)X
+976 1392(escaping)N
+1277(it)X
+1341(with)X
+1503(a)X
+1559(backslash)X
+1891(\(``)X
+7 f
+1972(\\)X
+1 f
+(''\))S
+2121(character.)X
+976 1572(The)N
+3 f
+1124(wq)X
+1 f
+1249(version)X
+1509(of)X
+1600(the)X
+1722(write)X
+1911(command)X
+2251(will)X
+2399(exit)X
+2543(the)X
+2665(editor)X
+2876(after)X
+3048(writing)X
+3303(the)X
+3425(\256le,)X
+3571(if)X
+3644(there)X
+3829(are)X
+3952(no)X
+976 1662(further)N
+1232(\256les)X
+1402(to)X
+1501(edit.)X
+1698(Appending)X
+2091(a)X
+2164(``)X
+7 f
+2218(!)X
+1 f
+('')S
+2377(character)X
+2710(to)X
+2809(the)X
+2944(command)X
+3297(name)X
+3508(or)X
+3612(entering)X
+3912(two)X
+976 1752(``quit'')N
+1232(commands)X
+1603(\(i.e.)X
+3 f
+1772(wq)X
+1 f
+1874(,)X
+3 f
+1918(quit)X
+1 f
+2055(,)X
+3 f
+2099(xit)X
+1 f
+2212(or)X
+3 f
+2303(ZZ)X
+1 f
+2409(\))X
+2461(in)X
+2548(a)X
+2609(row\))X
+2786(will)X
+2935(override)X
+3228(this)X
+3368(check)X
+3581(and)X
+3722(the)X
+3845(editor)X
+976 1842(will)N
+1120(exit,)X
+1280(ignoring)X
+1571(any)X
+1707(\256les)X
+1860(that)X
+2000(have)X
+2172(not)X
+2294(yet)X
+2412(been)X
+2584(edited.)X
+976 2022(The)N
+3 f
+1127(wn)X
+1 f
+1255(version)X
+1517(of)X
+1610(the)X
+1734(write)X
+1925(command)X
+2267(will)X
+2417(move)X
+2621(to)X
+2709(the)X
+2833(next)X
+2997(\256le)X
+3125(after)X
+3300(writing)X
+3558(the)X
+3683(\256le,)X
+3832(unless)X
+976 2112(the)N
+1094(write)X
+1279(fails.)X
+976 2292(Line:)N
+1336(Unchanged.)X
+976 2382(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(readonly)X
+1 f
+2178(and)X
+3 f
+2314(writeany)X
+1 f
+2637(options.)X
+3 f
+776 2562([range])N
+1046(x[it][!])X
+1290([\256le])X
+1 f
+976 2652(Write)N
+1185(the)X
+1309(\256le)X
+1437(if)X
+1512(it)X
+1582(has)X
+1715(been)X
+1894(modi\256ed.)X
+2245(The)X
+2397(speci\256ed)X
+2709(lines)X
+2887(are)X
+3013(written)X
+3267(to)X
+7 f
+3356(file)X
+1 f
+(,)S
+3595(if)X
+3671(the)X
+3796(\256le)X
+3925(has)X
+976 2742(been)N
+1149(modi\256ed)X
+1454(since)X
+1640(the)X
+1759(last)X
+1891(complete)X
+2206(write)X
+2392(to)X
+2475(any)X
+2612(\256le.)X
+2775(If)X
+2850(no)X
+7 f
+2951(range)X
+1 f
+3211(is)X
+3284(speci\256ed,)X
+3609(the)X
+3727(entire)X
+3930(\256le)X
+976 2832(is)N
+1049(written.)X
+976 3012(The)N
+3 f
+1126(xit)X
+1 f
+1240(command)X
+1581(will)X
+1730(exit)X
+1875(the)X
+1998(editor)X
+2210(after)X
+2383(writing)X
+2639(the)X
+2762(\256le,)X
+2909(if)X
+2983(there)X
+3169(are)X
+3294(no)X
+3400(further)X
+3645(\256les)X
+3804(to)X
+3892(edit.)X
+976 3102(Appending)N
+1362(a)X
+1428(``)X
+7 f
+1482(!)X
+1 f
+('')S
+1634(character)X
+1960(to)X
+2052(the)X
+2180(command)X
+2526(name)X
+2730(or)X
+2827(entering)X
+3120(two)X
+3270(``quit'')X
+3531(commands)X
+3907(\(i.e.)X
+3 f
+976 3192(wq)N
+1 f
+1078(,)X
+3 f
+1125(quit)X
+1 f
+1262(,)X
+3 f
+1309(xit)X
+1 f
+1425(or)X
+3 f
+1519(ZZ)X
+1 f
+1625(\))X
+1679(in)X
+1768(a)X
+1831(row\))X
+2010(will)X
+2161(override)X
+2456(this)X
+2598(check)X
+2813(and)X
+2956(the)X
+3082(editor)X
+3297(will)X
+3449(exit,)X
+3617(ignoring)X
+3916(any)X
+976 3282(\256les)N
+1129(that)X
+1269(have)X
+1441(not)X
+1563(yet)X
+1681(been)X
+1853(edited.)X
+976 3462(Line:)N
+1336(Unchanged.)X
+976 3552(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+3 f
+1856(readonly)X
+1 f
+2178(and)X
+3 f
+2314(writeany)X
+1 f
+2637(options.)X
+3 f
+776 3732([range])N
+1046(ya[nk])X
+1288([buffer])X
+1576([count])X
+1 f
+976 3822(Copy)N
+1169(the)X
+1287(speci\256ed)X
+1592(lines)X
+1763(to)X
+1845(a)X
+1901(buffer.)X
+2158(If)X
+2232(no)X
+2332(buffer)X
+2549(is)X
+2622(speci\256ed,)X
+2947(the)X
+3065(unnamed)X
+3379(buffer)X
+3596(is)X
+3669(used.)X
+976 4002(Line:)N
+1336(Unchanged.)X
+976 4092(Options:)N
+1336(None.)X
+3 f
+776 4272([line])N
+974(z)X
+1030([type])X
+1251([count])X
+1516([\257ags])X
+1 f
+976 4362(Adjust)N
+1210(the)X
+1329(window.)X
+1648(If)X
+1723(no)X
+7 f
+1824(type)X
+1 f
+2037(is)X
+2111(speci\256ed,)X
+2437(then)X
+7 f
+2596(count)X
+1 f
+2858(lines)X
+3031(following)X
+3364(the)X
+3484(speci\256ed)X
+3791(line)X
+3933(are)X
+976 4452(displayed.)N
+1362(The)X
+1526(default)X
+7 f
+1788(count)X
+1 f
+2067(is)X
+2159(the)X
+2296(value)X
+2509(of)X
+2614(the)X
+3 f
+2750(window)X
+1 f
+3054(option.)X
+3336(The)X
+7 f
+3499(type)X
+1 f
+3729(argument)X
+976 4542(changes)N
+1266(the)X
+1395(position)X
+1683(at)X
+1772(which)X
+7 f
+1999(line)X
+1 f
+2222(is)X
+2306(displayed)X
+2644(on)X
+2755(the)X
+2884(screen)X
+3121(by)X
+3232(changing)X
+3558(the)X
+3688(number)X
+3965(of)X
+976 4632(lines)N
+1147(displayed)X
+1474(before)X
+1700(and)X
+1836(after)X
+7 f
+2004(line)X
+1 f
+(.)S
+2256(The)X
+2401(following)X
+7 f
+2732(type)X
+1 f
+2944(characters)X
+3291(may)X
+3449(be)X
+3545(used:)X
+9 f
+976 4812(-)N
+1 f
+1336(Place)X
+1530(the)X
+1648(line)X
+1788(at)X
+1866(the)X
+1984(bottom)X
+2230(of)X
+2317(the)X
+2435(screen.)X
+976 4902(+)N
+1336(Place)X
+1530(the)X
+1648(line)X
+1788(at)X
+1866(the)X
+1984(top)X
+2106(of)X
+2193(the)X
+2311(screen.)X
+976 4992(.)N
+1336(Place)X
+1530(the)X
+1648(line)X
+1788(in)X
+1870(the)X
+1988(middle)X
+2230(of)X
+2317(the)X
+2435(screen.)X
+976 5082(\303)N
+1336(Write)X
+1540(out)X
+1663(count)X
+1862(lines)X
+2034(starting)X
+7 f
+2295(count)X
+2584(*)X
+2681(2)X
+1 f
+2750(lines)X
+2922(before)X
+7 f
+3149(line)X
+1 f
+(;)S
+3384(the)X
+3503(net)X
+3622(effect)X
+3828(of)X
+3917(this)X
+1336 5172(is)N
+1409(that)X
+1549(a)X
+1605(``)X
+7 f
+1659(z\303)X
+1 f
+('')S
+1829(command)X
+2165(following)X
+2496(a)X
+3 f
+2552(z)X
+1 f
+2608(command)X
+2944(writes)X
+3160(the)X
+3278(previous)X
+3574(page.)X
+976 5262(=)N
+1336(Center)X
+7 f
+1580(line)X
+1 f
+1802(on)X
+1912(the)X
+2040(screen)X
+2276(with)X
+2448(a)X
+2514(line)X
+2664(of)X
+2761(hyphens)X
+3058(displayed)X
+3395(immediately)X
+3826(before)X
+1336 5352(and)N
+1485(after)X
+1666(it.)X
+1783(The)X
+1941(number)X
+2219(of)X
+2319(preceding)X
+2669(and)X
+2817(following)X
+3160(lines)X
+3343(of)X
+3442(text)X
+3594(displayed)X
+3933(are)X
+1336 5442(reduced)N
+1611(to)X
+1693(account)X
+1963(for)X
+2077(those)X
+2266(lines.)X
+976 5622(Line:)N
+1336(Set)X
+1458(to)X
+1540(the)X
+1658(last)X
+1789(line)X
+1929(displayed,)X
+2276(with)X
+2438(the)X
+2556(exception)X
+2888(of)X
+2975(the)X
+7 f
+3093(type)X
+1 f
+(,)S
+3326(where)X
+3544(the)X
+3663(current)X
+3912(line)X
+1336 5712(is)N
+1409(set)X
+1518(to)X
+1600(the)X
+1718(line)X
+1858(speci\256ed)X
+2163(by)X
+2263(the)X
+2381(command.)X
+
+46 p
+%%Page: 46 45
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-46)N
+2813(Nvi/Nex)X
+3109 0.3906(Reference)AX
+3474(\(Ex)X
+3614(Commands\))X
+1 f
+976 762(Options:)N
+1336(Affected)X
+1638(by)X
+1738(the)X
+1856(option.)X
+3 f
+776 948(15.)N
+916(Set)X
+1043(Options)X
+1 f
+976 1071(There)N
+1186(are)X
+1307(a)X
+1365(large)X
+1548(number)X
+1815(of)X
+1904(options)X
+2161(that)X
+2303(may)X
+2463(be)X
+2561(set)X
+2672(\(or)X
+2788(unset\))X
+3006(to)X
+3091(change)X
+3342(the)X
+3463(editor's)X
+3731(behavior.)X
+776 1161(This)N
+938(section)X
+1185(describes)X
+1504(the)X
+1622(options,)X
+1897(their)X
+2064(abbreviations)X
+2516(and)X
+2652(their)X
+2819(default)X
+3062(values.)X
+976 1284(In)N
+1064(each)X
+1233(entry)X
+1419(below,)X
+1656(the)X
+1775(\256rst)X
+1920(part)X
+2066(of)X
+2154(the)X
+2273(tag)X
+2392(line)X
+2533(is)X
+2607(the)X
+2726(full)X
+2858(name)X
+3053(of)X
+3141(the)X
+3261(option,)X
+3507(followed)X
+3814(by)X
+3916(any)X
+776 1374(equivalent)N
+1135(abbreviations.)X
+1632(\(Regardless)X
+2036(of)X
+2128(the)X
+2251(abbreviations,)X
+2728(it)X
+2797(is)X
+2875(only)X
+3042(necessary)X
+3380(to)X
+3467(use)X
+3599(the)X
+3722(minimum)X
+776 1464(number)N
+1047(of)X
+1140(characters)X
+1493(necessary)X
+1832(to)X
+1920(distinguish)X
+2296(an)X
+2398(abbreviation)X
+2825(from)X
+3007(all)X
+3113(other)X
+3304(commands)X
+3677(for)X
+3797(it)X
+3867(to)X
+3956(be)X
+776 1554(accepted,)N
+1099(in)X
+3 f
+1182(nex)X
+1 f
+(/)S
+3 f
+1324(nvi)X
+1 f
+1430(.)X
+1491(Historically,)X
+1910(only)X
+2073(the)X
+2192(full)X
+2324(name)X
+2519(and)X
+2656(the)X
+2774(of\256cial)X
+3021(abbreviations)X
+3473(were)X
+3650(accepted)X
+3952(by)X
+3 f
+776 1644(ex)N
+1 f
+852(/)X
+3 f
+874(vi)X
+1 f
+936(.)X
+1002(Using)X
+1220(full)X
+1358(names)X
+1590(in)X
+1679(your)X
+1853(startup)X
+2098(\256les)X
+2258(and)X
+2401(environmental)X
+2891(variables)X
+3208(will)X
+3359(probably)X
+3671(make)X
+3872(them)X
+776 1734(more)N
+968(portable.\))X
+1325(The)X
+1477(part)X
+1629(in)X
+1718(square)X
+1955(brackets)X
+2250(is)X
+2330(the)X
+2454(default)X
+2703(value)X
+2903(of)X
+2996(the)X
+3120(option.)X
+3390(Most)X
+3580(of)X
+3673(the)X
+3797(options)X
+776 1824(are)N
+895(boolean,)X
+1189(i.e.)X
+1307(they)X
+1465(are)X
+1584(either)X
+1787(on)X
+1887(or)X
+1974(off,)X
+2108(and)X
+2244(do)X
+2344(not)X
+2466(have)X
+2638(an)X
+2734(associated)X
+3084(value.)X
+976 1947(Options)N
+1249(apply)X
+1447(to)X
+1529(both)X
+3 f
+1691(ex)X
+1 f
+1787(and)X
+3 f
+1923(vi)X
+1 f
+2005(modes,)X
+2254(unless)X
+2474(otherwise)X
+2806(speci\256ed.)X
+976 2070(For)N
+1107(information)X
+1505(on)X
+1605(modifying)X
+1958(the)X
+2076(options)X
+2331(or)X
+2419(to)X
+2502(display)X
+2754(the)X
+2873(options)X
+3129(and)X
+3266(their)X
+3434(current)X
+3683(values,)X
+3929(see)X
+776 2160(the)N
+894(``set'')X
+1111(command)X
+1447(in)X
+1529(the)X
+1647(section)X
+1894(entitled)X
+2154(``)X
+3 f
+2208(Ex)X
+2321(Commands)X
+1 f
+2712(''.)X
+3 f
+776 2340(altwerase)N
+1122([off])X
+976 2430(Vi)N
+1 f
+1084(only.)X
+1295(Change)X
+1569(how)X
+3 f
+1736(vi)X
+1 f
+1827(does)X
+2003(word)X
+2197(erase)X
+2392(during)X
+2630(text)X
+2779(input.)X
+3012(When)X
+3233(this)X
+3377(option)X
+3610(is)X
+3692(set,)X
+3830(text)X
+3979(is)X
+976 2520(broken)N
+1230(up)X
+1340(into)X
+1494(three)X
+1685(classes:)X
+1960(alphabetic,)X
+2340(numeric)X
+2633(and)X
+2779(underscore)X
+3162(characters,)X
+3539(other)X
+3734(nonblank)X
+976 2610(characters,)N
+1343(and)X
+1479(blank)X
+1677(characters.)X
+2064(Changing)X
+2395(from)X
+2571(one)X
+2707(class)X
+2884(to)X
+2967(another)X
+3229(marks)X
+3446(the)X
+3565(end)X
+3702(of)X
+3790(a)X
+3847(word.)X
+976 2700(In)N
+1070(addition,)X
+1379(the)X
+1504(class)X
+1687(of)X
+1781(the)X
+1906(\256rst)X
+2057(character)X
+2380(erased)X
+2612(is)X
+2691(ignored)X
+2962(\(which)X
+3211(is)X
+3290(exactly)X
+3548(what)X
+3730(you)X
+3876(want)X
+976 2790(when)N
+1170(erasing)X
+1422(pathname)X
+1754(components\).)X
+3 f
+776 2970(autoindent,)N
+1184(ai)X
+1266([off])X
+1 f
+976 3060(If)N
+1052(this)X
+1189(option)X
+1415(is)X
+1490(set,)X
+1621(whenever)X
+1956(you)X
+2098(create)X
+2313(a)X
+2371(new)X
+2527(line)X
+2669(\(using)X
+2891(the)X
+3 f
+3012(vi)X
+3097(A)X
+1 f
+3155(,)X
+3 f
+3198(a)X
+1 f
+(,)S
+3 f
+3281(C)X
+1 f
+3339(,)X
+3 f
+3382(c)X
+1 f
+3418(,)X
+3 f
+3461(I)X
+1 f
+3492(,)X
+3 f
+3535(i)X
+1 f
+3557(,)X
+3 f
+3600(O)X
+1 f
+3662(,)X
+3 f
+3705(o)X
+1 f
+(,)S
+3 f
+3788(R)X
+1 f
+3846(,)X
+3 f
+3889(r)X
+1 f
+3925(,)X
+3 f
+3968(S)X
+1 f
+4012(,)X
+976 3150(and)N
+3 f
+1117(s)X
+1 f
+1173(commands,)X
+1565(or)X
+1657(the)X
+3 f
+1780(ex)X
+1881(append)X
+1 f
+2133(,)X
+3 f
+2178(change)X
+1 f
+(,)S
+2463(and)X
+3 f
+2604(insert)X
+1 f
+2825(commands\))X
+3224(the)X
+3346(new)X
+3504(line)X
+3648(is)X
+3725(automati-)X
+976 3240(cally)N
+1156(indented)X
+1456(to)X
+1542(align)X
+1726(the)X
+1848(cursor)X
+2073(with)X
+2239(the)X
+2361(\256rst)X
+2509(nonblank)X
+2831(character)X
+3151(of)X
+3242(the)X
+3365(line)X
+3510(from)X
+3691(which)X
+3912(you)X
+976 3330(created)N
+1231(it.)X
+1337(Lines)X
+1537(are)X
+1658(indented)X
+1956(using)X
+2150(tab)X
+2269(characters)X
+2617(to)X
+2700(the)X
+2819(extent)X
+3036(possible)X
+3319(\(based)X
+3550(on)X
+3651(the)X
+3770(value)X
+3965(of)X
+976 3420(the)N
+3 f
+1094(tabstop)X
+1 f
+1367(option\))X
+1618(and)X
+1754(then)X
+1912(using)X
+2105(space)X
+2304(characters)X
+2651(as)X
+2738(necessary.)X
+3111(For)X
+3243(commands)X
+3611(inserting)X
+3912(text)X
+976 3510(into)N
+1124(the)X
+1246(middle)X
+1492(of)X
+1583(a)X
+1643(line,)X
+1807(any)X
+1947(blank)X
+2149(characters)X
+2500(to)X
+2586(the)X
+2708(right)X
+2883(of)X
+2974(the)X
+3096(cursor)X
+3321(are)X
+3444(discarded,)X
+3795(and)X
+3934(the)X
+976 3600(\256rst)N
+1120(nonblank)X
+1438(character)X
+1754(to)X
+1836(the)X
+1954(right)X
+2125(of)X
+2212(the)X
+2330(cursor)X
+2551(is)X
+2624(aligned)X
+2880(as)X
+2967(described)X
+3295(above.)X
+976 3780(The)N
+1128(indent)X
+1355(characters)X
+1709(are)X
+1835(themselves)X
+2218(somewhat)X
+2570(special.)X
+2860(If)X
+2941(you)X
+3088(do)X
+3195(not)X
+3324(enter)X
+3512(more)X
+3705(characters)X
+976 3870(on)N
+1085(the)X
+1212(new)X
+1375(line)X
+1524(before)X
+1759(moving)X
+2032(to)X
+2123(another)X
+2393(line,)X
+2562(or)X
+2658(entering)X
+7 f
+2950(<escape>)X
+1 f
+(,)S
+3382(the)X
+3508(indent)X
+3736(character)X
+976 3960(will)N
+1121(be)X
+1218(deleted)X
+1471(and)X
+1608(the)X
+1727(line)X
+1868(will)X
+2013(be)X
+2111(empty.)X
+2373(For)X
+2506(example,)X
+2820(if)X
+2891(you)X
+3033(enter)X
+7 f
+3216(<carriage-return>)X
+1 f
+976 4050(twice)N
+1181(in)X
+1274(succession,)X
+1668(the)X
+1797(line)X
+1947(created)X
+2210(by)X
+2320(the)X
+2448(\256rst)X
+7 f
+2602(<carriage-return>)X
+1 f
+3448(will)X
+3602(not)X
+3734(have)X
+3916(any)X
+976 4140(characters)N
+1323(in)X
+1405(it,)X
+1489(regardless)X
+1835(of)X
+1922(the)X
+2040(indentation)X
+2420(of)X
+2507(the)X
+2625(previous)X
+2921(or)X
+3008(subsequent)X
+3384(line.)X
+976 4320(Indent)N
+1210(characters)X
+1566(also)X
+1725(require)X
+1983(that)X
+2133(you)X
+2283(enter)X
+2474(additional)X
+2824(erase)X
+3020(characters)X
+3377(to)X
+3469(delete)X
+3691(them.)X
+3921(For)X
+976 4410(example,)N
+1290(if)X
+1361(you)X
+1503(have)X
+1677(an)X
+1775(indented)X
+2072(line,)X
+2233(containing)X
+2592(only)X
+2755(blanks,)X
+3005(the)X
+3124(\256rst)X
+7 f
+3269(<word-erase>)X
+1 f
+3866(char-)X
+976 4500(acter)N
+1156(you)X
+1299(enter)X
+1483(will)X
+1630(erase)X
+1819(up)X
+1922(to)X
+2007(end)X
+2146(of)X
+2236(the)X
+2357(indent)X
+2580(characters,)X
+2950(and)X
+3089(the)X
+3210(second)X
+3456(will)X
+3604(erase)X
+3794(back)X
+3970(to)X
+976 4590(the)N
+1094(beginning)X
+1434(of)X
+1521(the)X
+1639(line.)X
+1819(\(Historically,)X
+2264(only)X
+2426(the)X
+3 f
+2544(<control-D>)X
+1 f
+2986(key)X
+3122(would)X
+3342(erase)X
+3528(the)X
+3646(indent)X
+3866(char-)X
+976 4680(acters.)N
+1231(Both)X
+1413(the)X
+3 f
+1538(<control-D>)X
+1 f
+1988(key)X
+2132(and)X
+2276(the)X
+2402(usual)X
+2599(erase)X
+2793(keys)X
+2968(work)X
+3161(in)X
+3 f
+3251(nvi)X
+1 f
+3357(.\))X
+3452(In)X
+3547(addition,)X
+3857(if)X
+3934(the)X
+976 4770(cursor)N
+1211(is)X
+1298(positioned)X
+1665(at)X
+1756(the)X
+1887(end)X
+2036(of)X
+2136(the)X
+2267(indent)X
+2500(characters,)X
+2880(the)X
+3011(keys)X
+3191(``)X
+7 f
+3245(0<control-D>)X
+1 f
+('')S
+3908(will)X
+976 4860(erase)N
+1163(all)X
+1264(of)X
+1352(the)X
+1472(indent)X
+1694(characters)X
+2043(for)X
+2159(the)X
+2279(current)X
+2529(line,)X
+2691(resetting)X
+2989(the)X
+3109(indentation)X
+3491(level)X
+3669(to)X
+3753(0.)X
+3855(Simi-)X
+976 4950(larly,)N
+1168(the)X
+1291(keys)X
+1462(``)X
+7 f
+1516(\303<control-D>)X
+1 f
+('')S
+2170(will)X
+2318(erase)X
+2508(all)X
+2612(of)X
+2703(the)X
+2825(indent)X
+3049(characters)X
+3400(for)X
+3518(the)X
+3640(current)X
+3892(line,)X
+976 5040(leaving)N
+1232(the)X
+1350(indentation)X
+1730(level)X
+1906(for)X
+2020(future)X
+2232(created)X
+2485(lines)X
+2656(unaffected.)X
+976 5220(Finally,)N
+1243(if)X
+1313(the)X
+3 f
+1432(autoindent)X
+1 f
+1821(option)X
+2047(is)X
+2122(set,)X
+2253(the)X
+3 f
+2373(S)X
+1 f
+2439(and)X
+3 f
+2577(cc)X
+1 f
+2671(commands)X
+3040(change)X
+3290(from)X
+3468(the)X
+3588(\256rst)X
+3734(nonblank)X
+976 5310(of)N
+1063(the)X
+1181(line)X
+1321(to)X
+1403(the)X
+1521(end)X
+1657(of)X
+1744(the)X
+1862(line,)X
+2022(instead)X
+2269(of)X
+2356(from)X
+2532(the)X
+2650(beginning)X
+2990(of)X
+3077(the)X
+3195(line)X
+3335(to)X
+3417(the)X
+3535(end)X
+3671(of)X
+3758(the)X
+3876(line.)X
+3 f
+776 5490(autoprint,)N
+1140(ap)X
+1244([off])X
+976 5580(Ex)N
+1 f
+1099(only.)X
+1311(Cause)X
+1537(the)X
+1665(current)X
+1923(line)X
+2073(to)X
+2165(be)X
+2271(automatically)X
+2738(displayed)X
+3076(after)X
+3255(the)X
+3 f
+3384(ex)X
+1 f
+3491(commands)X
+3 f
+3869(<)X
+1 f
+3915(,)X
+3 f
+3966(>)X
+1 f
+4012(,)X
+3 f
+976 5670(copy)N
+1 f
+(,)S
+3 f
+1176(delete)X
+1 f
+1377(,)X
+3 f
+1417(join)X
+1 f
+1550(,)X
+3 f
+1590(move)X
+1 f
+1773(,)X
+3 f
+1813(put)X
+1 f
+1928(,)X
+3 f
+1968(t)X
+1 f
+1995(,)X
+3 f
+2035(Undo)X
+1 f
+2221(,)X
+2261(and)X
+3 f
+2397(undo)X
+1 f
+2569(.)X
+2629(This)X
+2791(automatic)X
+3127(display)X
+3378(is)X
+3451(suppressed)X
+3823(during)X
+3 f
+976 5760(global)N
+1 f
+1226(and)X
+3 f
+1384(vglobal)X
+1 f
+1675(commands,)X
+2085(and)X
+2244(for)X
+2381(any)X
+2540(command)X
+2899(where)X
+3139(optional)X
+3444(\257ags)X
+3638(are)X
+3780(used)X
+3970(to)X
+
+47 p
+%%Page: 47 46
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-47)X
+1 f
+976 762(explicitly)N
+1298(display)X
+1549(the)X
+1667(line.)X
+3 f
+776 942(autowrite,)N
+1146(aw)X
+1264([off])X
+1 f
+976 1032(If)N
+1053(this)X
+1191(option)X
+1418(is)X
+1494(set,)X
+1626(the)X
+3 f
+1747(vi)X
+1832(!)X
+1 f
+1859(,)X
+3 f
+1902(\303\303)X
+1 f
+1956(,)X
+3 f
+1999(\303])X
+1 f
+2076(and)X
+3 f
+2215(<control-Z>)X
+1 f
+2655(commands,)X
+3046(and)X
+3186(the)X
+3 f
+3308(ex)X
+3408(edit)X
+1 f
+3537(,)X
+3 f
+3581(next)X
+1 f
+3728(,)X
+3 f
+3772(rewind)X
+1 f
+(,)S
+3 f
+976 1122(stop)N
+1 f
+1118(,)X
+3 f
+1161(suspend)X
+1 f
+1435(,)X
+3 f
+1478(tag)X
+1 f
+1585(,)X
+3 f
+1628(tagpop)X
+1 f
+1863(,)X
+1906(and)X
+3 f
+2045(tagtop)X
+1 f
+2286(commands)X
+2656(automatically)X
+3115(write)X
+3302(the)X
+3422(current)X
+3672(\256le)X
+3796(back)X
+3970(to)X
+976 1212(the)N
+1103(current)X
+1360(\256le)X
+1491(name)X
+1694(if)X
+1772(it)X
+1845(has)X
+1981(been)X
+2163(modi\256ed)X
+2477(since)X
+2672(it)X
+2746(was)X
+2901(last)X
+3042(written.)X
+3339(If)X
+3423(the)X
+3551(write)X
+3746(fails,)X
+3934(the)X
+976 1302(command)N
+1312(fails)X
+1470(and)X
+1606(goes)X
+1773(no)X
+1873(further.)X
+976 1482(Appending)N
+1363(the)X
+1492(optional)X
+1785(force)X
+1982(\257ag)X
+2133(character)X
+2460(``)X
+7 f
+2514(!)X
+1 f
+('')S
+2668(to)X
+2762(the)X
+3 f
+2892(ex)X
+1 f
+3000(commands)X
+3 f
+3379(next)X
+1 f
+3526(,)X
+3 f
+3578(rewind)X
+1 f
+(,)S
+3 f
+3870(stop)X
+1 f
+4012(,)X
+3 f
+976 1572(suspend)N
+1 f
+1250(,)X
+3 f
+1290(tag)X
+1 f
+1397(,)X
+3 f
+1437(tagpop)X
+1 f
+1672(,)X
+1712(and)X
+3 f
+1848(tagtop)X
+1 f
+2086(stops)X
+2270(the)X
+2388(automatic)X
+2724(write)X
+2909(from)X
+3085(being)X
+3283(attempted.)X
+976 1752(\(Historically,)N
+1421(the)X
+3 f
+1539(next)X
+1 f
+1706(command)X
+2042(ignored)X
+2307(the)X
+2425(optional)X
+2707(force)X
+2894(\257ag.\))X
+3102(Note,)X
+3299(the)X
+3 f
+3418(ex)X
+1 f
+3515(commands)X
+3 f
+3883(edit)X
+1 f
+4012(,)X
+3 f
+976 1842(quit)N
+1 f
+1113(,)X
+3 f
+1153(shell)X
+1 f
+1308(,)X
+1348(and)X
+3 f
+1484(xit)X
+1 f
+1593(are)X
+2 f
+1712(not)X
+1 f
+1834(affected)X
+2114(by)X
+2214(the)X
+3 f
+2332(autowrite)X
+1 f
+2682(option.)X
+3 f
+776 2022(beautify,)N
+1096(bf)X
+1187([off])X
+1 f
+976 2112(If)N
+1061(this)X
+1207(option)X
+1442(is)X
+1527(set,)X
+1668(all)X
+1780(control)X
+2039(characters)X
+2398(that)X
+2550(are)X
+2681(not)X
+2815(currently)X
+3137(being)X
+3347(specially)X
+3664(interpreted,)X
+976 2202(other)N
+1166(than)X
+7 f
+1329(<tab>)X
+1 f
+(,)S
+7 f
+1613(<newline>)X
+1 f
+(,)S
+2089(and)X
+7 f
+2229(<form-feed>)X
+1 f
+(,)S
+2801(are)X
+2924(discarded)X
+3256(from)X
+3436(commands)X
+3807(read)X
+3970(in)X
+976 2292(by)N
+3 f
+1078(ex)X
+1 f
+1176(from)X
+1354(command)X
+1692(\256les,)X
+1867(and)X
+2005(from)X
+2183(input)X
+2369(text)X
+2511(entered)X
+2770(to)X
+3 f
+2854(vi)X
+1 f
+2938(\(either)X
+3170(into)X
+3316(the)X
+3436(\256le)X
+3560(or)X
+3649(to)X
+3733(the)X
+3854(colon)X
+976 2382(command)N
+1312(line\).)X
+1519(Text)X
+1686(\256les)X
+1839(read)X
+1998(by)X
+3 f
+2098(ex)X
+1 f
+2174(/)X
+3 f
+2196(vi)X
+1 f
+2278(are)X
+2 f
+2397(not)X
+1 f
+2519(affected)X
+2799(by)X
+2899(the)X
+3 f
+3017(beautify)X
+1 f
+3317(option.)X
+3 f
+776 2562(cdpath)N
+1031([environment)X
+1514(variable)X
+1814(CDPATH,)X
+2192(or)X
+2288(current)X
+2567(directory])X
+1 f
+976 2652(This)N
+1152(option)X
+1390(is)X
+1477(used)X
+1658(to)X
+1754(specify)X
+2020(a)X
+2090(colon)X
+2302(separated)X
+2640(list)X
+2771(of)X
+2872(directories)X
+3245(which)X
+3476(are)X
+3610(used)X
+3792(as)X
+3894(path)X
+976 2742(pre\256xes)N
+1252(for)X
+1368(any)X
+1506(relative)X
+1769(path)X
+1929(names)X
+2156(used)X
+2325(as)X
+2414(arguments)X
+2770(for)X
+2886(the)X
+3 f
+3006(cd)X
+1 f
+3108(command.)X
+3486(The)X
+3633(value)X
+3829(of)X
+3917(this)X
+976 2832(option)N
+1202(defaults)X
+1478(to)X
+1562(the)X
+1682(value)X
+1878(of)X
+1967(the)X
+2088(environmental)X
+2574(variable)X
+7 f
+2856(CDPATH)X
+1 f
+3167(if)X
+3239(it)X
+3306(is)X
+3382(set,)X
+3514(otherwise)X
+3849(to)X
+3934(the)X
+976 2922(current)N
+1232(directory.)X
+1590(For)X
+1729(compatibility)X
+2183(with)X
+2353(the)X
+2478(POSIX)X
+2736(1003.2)X
+2983(shell,)X
+3181(the)X
+3 f
+3306(cd)X
+1 f
+3413(command)X
+3756(does)X
+2 f
+3930(not)X
+1 f
+976 3012(check)N
+1200(the)X
+1334(current)X
+1598(directory)X
+1924(as)X
+2027(a)X
+2099(path)X
+2273(pre\256x)X
+2496(for)X
+2627(relative)X
+2905(path)X
+3080(names)X
+3322(unless)X
+3559(it)X
+3640(is)X
+3730(explicitly)X
+976 3102(speci\256ed.)N
+1334(It)X
+1416(may)X
+1586(be)X
+1694(so)X
+1797(speci\256ed)X
+2114(by)X
+2226(entering)X
+2521(an)X
+2629(empty)X
+2861(string)X
+3075(or)X
+3174(a)X
+3242(``)X
+7 f
+3296(.)X
+1 f
+('')S
+3450(character)X
+3778(into)X
+3934(the)X
+7 f
+976 3192(CDPATH)N
+1 f
+1284(variable)X
+1563(or)X
+1650(the)X
+1768(option)X
+1992(value.)X
+3 f
+776 3372(columns,)N
+1100(co)X
+1196([80])X
+1 f
+976 3462(The)N
+1132(number)X
+1408(of)X
+1506(columns)X
+1808(in)X
+1901(the)X
+2030(screen.)X
+2307(Setting)X
+2565(this)X
+2712(option)X
+2948(causes)X
+3 f
+3190(ex)X
+1 f
+3266(/)X
+3 f
+3288(vi)X
+1 f
+3382(to)X
+3476(set)X
+3597(\(or)X
+3723(reset\))X
+3934(the)X
+976 3552(environmental)N
+1464(variable)X
+7 f
+1748(COLUMNS)X
+1 f
+(.)S
+2149(See)X
+2290(the)X
+2413(section)X
+2665(entitled)X
+2930(``)X
+3 f
+2984(Sizing)X
+3217(the)X
+3349(Screen)X
+1 f
+3581('')X
+3660(more)X
+3849(infor-)X
+976 3642(mation.)N
+3 f
+776 3822(comment)N
+1113([off])X
+976 3912(Vi)N
+1 f
+1079(only.)X
+1284(If)X
+1361(the)X
+1482(\256rst)X
+1629(non-empty)X
+1999(line)X
+2142(of)X
+2232(the)X
+2354(\256le)X
+2480(begins)X
+2713(with)X
+2879(the)X
+3001(string)X
+3207(``)X
+7 f
+3261(/*)X
+1 f
+('',)S
+3455(this)X
+3594(option)X
+3822(causes)X
+3 f
+976 4002(vi)N
+1 f
+1072(to)X
+1167(skip)X
+1333(to)X
+1428(the)X
+1559(end)X
+1708(of)X
+1808(that)X
+1961(C-language)X
+2364(comment)X
+2695(\(probably)X
+3040(a)X
+3109(terribly)X
+3378(boring)X
+3620(legal)X
+3809(notice\))X
+976 4092(before)N
+1202(displaying)X
+1555(the)X
+1673(\256le.)X
+3 f
+776 4272(directory,)N
+1133(dir)X
+1255([environment)X
+1738(variable)X
+2038(TMPDIR,)X
+2403(or)X
+2499(/tmp])X
+1 f
+976 4362(The)N
+1123(directory)X
+1435(where)X
+1655(temporary)X
+2008(\256les)X
+2164(are)X
+2286(created.)X
+2582(The)X
+2730(environmental)X
+3216(variable)X
+7 f
+3498(TMPDIR)X
+1 f
+3809(is)X
+3885(used)X
+976 4452(as)N
+1063(the)X
+1181(default)X
+1424(value)X
+1618(if)X
+1687(it)X
+1751(exists,)X
+1973(otherwise)X
+7 f
+2305(/tmp)X
+1 f
+2517(is)X
+2590(used.)X
+3 f
+776 4632(edcompatible,)N
+1274(ed)X
+1374([off])X
+1 f
+976 4722(Remember)N
+1349(the)X
+1468(values)X
+1694(of)X
+1782(the)X
+1901(``c'')X
+2066(and)X
+2203(``g'')X
+2372(suf\256ces)X
+2638(to)X
+2721(the)X
+3 f
+2840(substitute)X
+1 f
+3194(commands,)X
+3583(instead)X
+3832(of)X
+3921(ini-)X
+976 4812(tializing)N
+1262(them)X
+1446(as)X
+1537(unset)X
+1730(for)X
+1848(each)X
+2020(new)X
+2178(command.)X
+2558(Specifying)X
+2929(pattern)X
+3176(and)X
+3316(replacement)X
+3733(strings)X
+3970(to)X
+976 4902(the)N
+3 f
+1094(substitute)X
+1 f
+1447(command)X
+1783(unsets)X
+2003(the)X
+2121(``c'')X
+2285(and)X
+2421(``g'')X
+2589(suf\256ces)X
+2854(as)X
+2941(well.)X
+3 f
+776 5082(errorbells,)N
+1155(eb)X
+1255([off])X
+976 5172(Ex)N
+1 f
+1089(only.)X
+3 f
+1292(Ex)X
+1 f
+1406(error)X
+1584(messages)X
+1908(are)X
+2028(normally)X
+2338(presented)X
+2667(in)X
+2750(inverse)X
+3003(video.)X
+3242(If)X
+3317(that)X
+3458(is)X
+3532(not)X
+3655(possible)X
+3938(for)X
+976 5262(the)N
+1096(terminal,)X
+1405(setting)X
+1640(this)X
+1777(option)X
+2003(causes)X
+2235(error)X
+2414(messages)X
+2739(to)X
+2823(be)X
+2921(announced)X
+3291(by)X
+3393(ringing)X
+3646(the)X
+3765(terminal)X
+976 5352(bell.)N
+3 f
+776 5532(exrc,)N
+964(ex)X
+1060([off])X
+1 f
+976 5622(If)N
+1061(this)X
+1207(option)X
+1442(is)X
+1526(turned)X
+1762(off)X
+1887(in)X
+1980(the)X
+2109(system)X
+2362(or)X
+2460($HOME)X
+2767(startup)X
+3016(\256les,)X
+3200(the)X
+3330(local)X
+3518(startup)X
+3768(\256les)X
+3933(are)X
+976 5712(never)N
+1177(read)X
+1338(\(unless)X
+1587(they)X
+1747(are)X
+1868(the)X
+1988(same)X
+2175(as)X
+2264(the)X
+2384(system)X
+2628(or)X
+2717($HOME)X
+3015(startup)X
+3255(\256les\).)X
+3477(Turning)X
+3757(it)X
+3823(on)X
+3925(has)X
+976 5802(no)N
+1091(effect,)X
+1330(i.e.)X
+1463(the)X
+1596(normal)X
+1858(checks)X
+2112(for)X
+2241(local)X
+2432(startup)X
+2685(\256les)X
+2854(are)X
+2989(performed,)X
+3380(regardless.)X
+3782(See)X
+3934(the)X
+
+48 p
+%%Page: 48 47
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-48)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+1 f
+976 762(section)N
+1223(entitled)X
+1483(``)X
+3 f
+1537(Startup)X
+1819(Information)X
+1 f
+2237('')X
+2311(for)X
+2425(more)X
+2610(information.)X
+3 f
+776 942(extended)N
+1103([off])X
+1 f
+976 1032(This)N
+1147(option)X
+1380(causes)X
+1619(all)X
+1728(regular)X
+1986(expressions)X
+2390(to)X
+2482(be)X
+2588(treated)X
+2837(as)X
+2934(POSIX)X
+3195(1003.2)X
+3445(Extended)X
+3778(Regular)X
+976 1122(Expressions)N
+1383(\(which)X
+1626(are)X
+1745(similar)X
+1987(to)X
+2069(historic)X
+2 f
+2329(egrep)X
+1 f
+2512(\(1\))X
+2626(style)X
+2797(expressions\).)X
+3 f
+776 1302(\257ash)N
+955([on])X
+1 f
+976 1392(This)N
+1139(option)X
+1364(causes)X
+1595(the)X
+1714(screen)X
+1941(to)X
+2025(\257ash)X
+2198(instead)X
+2447(of)X
+2536(beeping)X
+2812(the)X
+2932(keyboard,)X
+3273(on)X
+3375(error,)X
+3574(if)X
+3645(the)X
+3765(terminal)X
+976 1482(has)N
+1103(the)X
+1221(capability.)X
+3 f
+776 1662(hardtabs,)N
+1122(ht)X
+1213([8])X
+1 f
+976 1752(This)N
+1141(option)X
+1369(de\256nes)X
+1620(the)X
+1742(spacing)X
+2011(between)X
+2303(hardware)X
+2627(tab)X
+2749(settings,)X
+3037(i.e.)X
+3179(the)X
+3301(tab)X
+3423(expansion)X
+3772(done)X
+3952(by)X
+976 1842(the)N
+1099(operating)X
+1427(system)X
+1674(and/or)X
+1904(the)X
+2027(terminal)X
+2319(itself.)X
+2544(As)X
+3 f
+2658(nex)X
+1 f
+(/)S
+3 f
+2800(nvi)X
+1 f
+2931(never)X
+3135(writes)X
+7 f
+3355(<tab>)X
+1 f
+3619(characters)X
+3970(to)X
+976 1932(the)N
+1094(terminal,)X
+1401(unlike)X
+1621(historic)X
+1881(versions)X
+2168(of)X
+3 f
+2255(ex)X
+1 f
+2331(/)X
+3 f
+2353(vi)X
+1 f
+2415(,)X
+2455(this)X
+2590(option)X
+2814(does)X
+2981(not)X
+3103(currently)X
+3413(have)X
+3585(any)X
+3721(affect.)X
+3 f
+776 2112(ignorecase,)N
+1177(ic)X
+1255([off])X
+1 f
+976 2202(This)N
+1141(option)X
+1368(causes)X
+1602(regular)X
+1854(expressions,)X
+2272(both)X
+2438(in)X
+3 f
+2524(ex)X
+1 f
+2624(commands)X
+2995(and)X
+3135(in)X
+3221(searches,)X
+3538(to)X
+3624(be)X
+3724(evaluated)X
+976 2292(in)N
+1058(a)X
+1114(case-insensitive)X
+1642(manner.)X
+3 f
+776 2472(keytime)N
+1068([6])X
+1 f
+976 2562(The)N
+1121(10th's)X
+1341(of)X
+1428(a)X
+1484(second)X
+3 f
+1727(ex)X
+1 f
+1803(/)X
+3 f
+1825(vi)X
+1 f
+1907(waits)X
+2096(for)X
+2210(a)X
+2266(subsequent)X
+2642(key)X
+2778(to)X
+2860(complete)X
+3174(a)X
+3230(key)X
+3366(mapping.)X
+3 f
+776 2742(leftright)N
+1077([off])X
+976 2832(Vi)N
+1 f
+1083(only.)X
+1293(This)X
+1463(option)X
+1695(causes)X
+1933(the)X
+2059(screen)X
+2293(to)X
+2383(be)X
+2487(scrolled)X
+2769(left-right)X
+3082(to)X
+3172(view)X
+3356(lines)X
+3535(longer)X
+3768(than)X
+3934(the)X
+976 2922(screen,)N
+1232(instead)X
+1489(of)X
+1586(the)X
+1714(traditional)X
+3 f
+2073(vi)X
+1 f
+2165(screen)X
+2401(interface)X
+2713(which)X
+2939(folds)X
+3129(long)X
+3301(lines)X
+3482(at)X
+3570(the)X
+3698(right-hand)X
+976 3012(margin)N
+1223(of)X
+1310(the)X
+1428(terminal.)X
+3 f
+776 3192(lines,)N
+971(li)X
+1035([24])X
+976 3282(Vi)N
+1 f
+1076(only.)X
+1278(The)X
+1423(number)X
+1688(of)X
+1775(lines)X
+1946(in)X
+2028(the)X
+2146(screen.)X
+2412(Setting)X
+2658(this)X
+2793(option)X
+3017(causes)X
+3 f
+3247(ex)X
+1 f
+3323(/)X
+3 f
+3345(vi)X
+1 f
+3427(to)X
+3509(set)X
+3619(\(or)X
+3734(reset\))X
+3934(the)X
+976 3372(environmental)N
+1462(variable)X
+7 f
+1744(LINES)X
+1 f
+(.)S
+2047(See)X
+2186(the)X
+2307(section)X
+2557(entitled)X
+2820(``)X
+3 f
+2874(Sizing)X
+3105(the)X
+3235(Screen)X
+1 f
+3467('')X
+3544(for)X
+3661(more)X
+3849(infor-)X
+976 3462(mation.)N
+3 f
+776 3642(lisp)N
+915([off])X
+976 3732(Vi)N
+1 f
+1078(only.)X
+1282(This)X
+1447(option)X
+1674(changes)X
+1956(the)X
+2077(behavior)X
+2381(of)X
+2471(the)X
+3 f
+2592(vi)X
+2677(\()X
+1 f
+2704(,)X
+3 f
+2747(\))X
+1 f
+2774(,)X
+3 f
+2817({)X
+1 f
+2849(,)X
+3 f
+2892(})X
+1 f
+2924(,)X
+3 f
+2967([[)X
+1 f
+3044(and)X
+3 f
+3183(]])X
+1 f
+3260(commands)X
+3630(to)X
+3715(match)X
+3934(the)X
+976 3822(Lisp)N
+1138(language.)X
+1488(Also,)X
+1679(the)X
+3 f
+1797(autoindent)X
+1 f
+2185(option's)X
+2467(behavior)X
+2768(is)X
+2841(changed)X
+3129(to)X
+3211(be)X
+3307(appropriate)X
+3693(for)X
+3807(Lisp.)X
+2 f
+976 4002(This)N
+1133(option)X
+1357(is)X
+1430(not)X
+1552(yet)X
+1666(implemented.)X
+3 f
+776 4182(list)N
+898([off])X
+1 f
+976 4272(This)N
+1151(option)X
+1388(causes)X
+1631(lines)X
+1815(to)X
+1910(be)X
+2019(displayed)X
+2359(in)X
+2454(an)X
+2563(unambiguous)X
+3028(fashion.)X
+3338(Speci\256cally,)X
+3770(tabs)X
+3933(are)X
+976 4362(displayed)N
+1305(as)X
+1394(control)X
+1643(characters,)X
+2012(i.e.)X
+2152(``)X
+7 f
+2206(\303I)X
+1 f
+('',)S
+2398(and)X
+2536(the)X
+2656(ends)X
+2825(of)X
+2914(lines)X
+3087(are)X
+3207(marked)X
+3469(with)X
+3632(a)X
+3689(``)X
+7 f
+3743($)X
+1 f
+('')S
+3866(char-)X
+976 4452(acter.)N
+3 f
+776 4632(magic)N
+1001([on])X
+1 f
+976 4722(This)N
+1141(option)X
+1368(is)X
+1444(on)X
+1547(by)X
+1650(default.)X
+1936(Turning)X
+2217(the)X
+3 f
+2338(magic)X
+1 f
+2566(option)X
+2793(off)X
+2910(causes)X
+3143(all)X
+3247(regular)X
+3499(expression)X
+3866(char-)X
+976 4812(acters)N
+1189(except)X
+1423(for)X
+1541(``)X
+7 f
+1595(\303)X
+1 f
+('')S
+1721(and)X
+1861(``)X
+7 f
+1915($)X
+1 f
+('',)S
+2061(to)X
+2147(be)X
+2247(treated)X
+2490(as)X
+2581(ordinary)X
+2877(characters.)X
+3268(To)X
+3381(re-enable)X
+3705(characters)X
+976 4902(individually,)N
+1402(when)X
+1597(the)X
+3 f
+1716(magic)X
+1 f
+1942(option)X
+2167(is)X
+2241(off,)X
+2376(precede)X
+2648(them)X
+2829(with)X
+2992(a)X
+3049(backslash)X
+3382(``)X
+7 f
+3436(\\)X
+1 f
+('')S
+3559(character.)X
+3916(See)X
+976 4992(the)N
+1094(section)X
+1341(entitled)X
+1601(``)X
+3 f
+1655(Regular)X
+1951(Expressions)X
+2379(and)X
+2527(Replacement)X
+2993(Strings)X
+1 f
+3237('')X
+3311(for)X
+3425(more)X
+3610(information.)X
+3 f
+776 5172(matchtime)N
+1162([7])X
+976 5262(Vi)N
+1 f
+1078(only.)X
+1282(The)X
+1429(10th's)X
+1651(of)X
+1740(a)X
+1798(second)X
+3 f
+2043(ex)X
+1 f
+2119(/)X
+3 f
+2141(vi)X
+1 f
+2226(pauses)X
+2463(on)X
+2566(the)X
+2687(matching)X
+3008(character)X
+3327(when)X
+3524(the)X
+3 f
+3645(showmatch)X
+1 f
+976 5352(option)N
+1200(is)X
+1273(set.)X
+3 f
+776 5532(mesg)N
+970([on])X
+1 f
+976 5622(This)N
+1140(option)X
+1366(allows)X
+1597(other)X
+1784(users)X
+1972(to)X
+2057(contact)X
+2312(you)X
+2455(using)X
+2651(the)X
+2 f
+2772(talk)X
+1 f
+(\(1\))S
+3009(and)X
+2 f
+3148(write)X
+1 f
+3312(\(1\))X
+3429(utilities,)X
+3711(while)X
+3912(you)X
+976 5712(are)N
+1101(editing.)X
+3 f
+1389(Ex)X
+1 f
+1482(/)X
+3 f
+1504(vi)X
+1 f
+1592(does)X
+1764(not)X
+1891(turn)X
+2045(message)X
+2342(on,)X
+2467(i.e.)X
+2590(if)X
+2664(messages)X
+2992(were)X
+3174(turned)X
+3404(off)X
+3523(when)X
+3722(the)X
+3845(editor)X
+976 5802(was)N
+1124(invoked,)X
+1425(they)X
+1586(will)X
+1733(stay)X
+1885(turned)X
+2113(off.)X
+2270(This)X
+2435(option)X
+2662(only)X
+2827(permits)X
+3090(you)X
+3233(to)X
+3318(disallow)X
+3612(messages)X
+3938(for)X
+
+49 p
+%%Page: 49 48
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-49)X
+1 f
+976 762(the)N
+1094(edit)X
+1234(session.)X
+1525(See)X
+1661(the)X
+2 f
+1779(mesg)X
+1 f
+1944(\(1\))X
+2058(utility)X
+2268(for)X
+2382(more)X
+2567(information.)X
+3 f
+776 942(modelines,)N
+1158(modeline)X
+1489([off])X
+1 f
+976 1032(If)N
+1052(the)X
+3 f
+1172(modelines)X
+1 f
+1536(option)X
+1762(is)X
+1837(set,)X
+3 f
+1968(ex)X
+1 f
+2044(/)X
+3 f
+2066(vi)X
+1 f
+2150(has)X
+2279(historically)X
+2661(scanned)X
+2942(the)X
+3062(\256rst)X
+3208(and)X
+3346(last)X
+3479(\256ve)X
+3621(lines)X
+3794(of)X
+3884(each)X
+976 1122(\256le)N
+1101(as)X
+1191(it)X
+1258(is)X
+1334(read)X
+1496(for)X
+1613(editing,)X
+1877(looking)X
+2143(for)X
+2259(any)X
+3 f
+2397(ex)X
+1 f
+2495(commands)X
+2864(that)X
+3006(have)X
+3180(been)X
+3354(placed)X
+3586(in)X
+3670(those)X
+3861(lines.)X
+976 1212(After)N
+1167(the)X
+1286(startup)X
+1525(information)X
+1924(has)X
+2052(been)X
+2225(processed,)X
+2583(and)X
+2720(before)X
+2947(the)X
+3066(user)X
+3221(starts)X
+3411(editing)X
+3654(the)X
+3773(\256le,)X
+3916(any)X
+976 1302(commands)N
+1343(embedded)X
+1693(in)X
+1775(the)X
+1893(\256le)X
+2015(are)X
+2134(executed.)X
+976 1482(Commands)N
+1362(were)X
+1541(recognized)X
+1916(by)X
+2018(the)X
+2138(letters)X
+2356(``e'')X
+2522(or)X
+2611(``v'')X
+2781(followed)X
+3088(by)X
+3191(``x'')X
+3362(or)X
+3452(``i'',)X
+3625(at)X
+3706(the)X
+3827(begin-)X
+976 1572(ning)N
+1144(of)X
+1237(a)X
+1299(line)X
+1445(or)X
+1538(following)X
+1875(a)X
+1937(tab)X
+2061(or)X
+2154(space)X
+2359(character,)X
+2701(and)X
+2843(followed)X
+3153(by)X
+3258(a)X
+3319(``:'',)X
+3494(an)X
+3 f
+3595(ex)X
+1 f
+3696(command,)X
+976 1662(and)N
+1112(another)X
+1373(``:''.)X
+976 1842(This)N
+1144(option)X
+1374(is)X
+1453(a)X
+1516(security)X
+1797(problem)X
+2091(of)X
+2185(immense)X
+2501(proportions,)X
+2917(and)X
+3060(should)X
+3300(not)X
+3429(be)X
+3532(used)X
+3706(under)X
+3916(any)X
+976 1932(circumstances.)N
+2 f
+976 2112(This)N
+1133(option)X
+1357(will)X
+1496(never)X
+1695(be)X
+1791(implemented.)X
+3 f
+776 2292(number,)N
+1087(nu)X
+1195([off])X
+1 f
+976 2382(Precede)N
+1251(each)X
+1419(line)X
+1559(displayed)X
+1886(with)X
+2048(its)X
+2143(current)X
+2391(line)X
+2531(number.)X
+3 f
+776 2562(octal)N
+961([off])X
+1 f
+976 2652(Display)N
+1245(unknown)X
+1563(characters)X
+1910(as)X
+1997(octal)X
+2173(numbers,)X
+2489(instead)X
+2736(of)X
+2823(the)X
+2941(default)X
+3184(hexadecimal.)X
+3 f
+776 2832(open)N
+960([on])X
+976 2922(Ex)N
+1 f
+1089(only.)X
+1291(If)X
+1365(this)X
+1500(option)X
+1724(is)X
+1797(not)X
+1919(set,)X
+2048(the)X
+3 f
+2166(open)X
+1 f
+2350(and)X
+3 f
+2486(visual)X
+1 f
+2705(commands)X
+3072(are)X
+3191(disallowed.)X
+3 f
+776 3102(optimize,)N
+1110(opt)X
+1241([on])X
+976 3192(Vi)N
+1 f
+1081(only.)X
+1288(Throughput)X
+1691(of)X
+1783(text)X
+1929(is)X
+2008(expedited)X
+2346(by)X
+2452(setting)X
+2691(the)X
+2815(terminal)X
+3108(not)X
+3236(to)X
+3324(do)X
+3430(automatic)X
+3772(carriage)X
+976 3282(returns)N
+1222(when)X
+1419(printing)X
+1695(more)X
+1883(than)X
+2044(one)X
+2183(\(logical\))X
+2478(line)X
+2621(of)X
+2710(output,)X
+2956(greatly)X
+3201(speeding)X
+3508(output)X
+3734(on)X
+3836(termi-)X
+976 3372(nals)N
+1125(without)X
+1389(addressable)X
+1784(cursors)X
+2036(when)X
+2230(text)X
+2370(with)X
+2532(leading)X
+2788(white)X
+2986(space)X
+3185(is)X
+3258(printed.)X
+2 f
+976 3552(This)N
+1133(option)X
+1357(is)X
+1430(not)X
+1552(yet)X
+1666(implemented.)X
+3 f
+776 3732(paragraphs,)N
+1211(para)X
+1391([IPLPPPQPP)X
+1878 -0.3625(LIpplpipbp])AX
+976 3822(Vi)N
+1 f
+1079(only.)X
+1284(De\256ne)X
+1521(additional)X
+1864(paragraph)X
+2209(boundaries)X
+2584(for)X
+2701(the)X
+3 f
+2822({)X
+1 f
+2877(and)X
+3 f
+3016(})X
+1 f
+3071(commands.)X
+3481(The)X
+3629(value)X
+3826(of)X
+3917(this)X
+976 3912(option)N
+1200(must)X
+1375(be)X
+1471(a)X
+1527(character)X
+1843(string)X
+2045(consisting)X
+2389(of)X
+2476(zero)X
+2635(or)X
+2722(more)X
+2907(character)X
+3223(pairs.)X
+976 4092(In)N
+1070(the)X
+1195(text)X
+1342(to)X
+1431(be)X
+1534(edited,)X
+1777(the)X
+1902(character)X
+2226(string)X
+7 f
+2436(<newline>.<char-pair>)X
+1 f
+(,)S
+3492(\(where)X
+7 f
+3744(<char-)X
+976 4182(pair>)N
+1 f
+1241(is)X
+1319(one)X
+1460(of)X
+1552(the)X
+1675(character)X
+1996(pairs)X
+2177(in)X
+2264(the)X
+2386(option's)X
+2672(value\))X
+2897(de\256nes)X
+3148(a)X
+3208(paragraph)X
+3554(boundary.)X
+3921(For)X
+976 4272(example,)N
+1291(if)X
+1363(the)X
+1484(option)X
+1711(were)X
+1891(set)X
+2003(to)X
+7 f
+2088(LaA<space>##)X
+1 f
+(,)S
+2708(then)X
+2870(all)X
+2974(of)X
+3065(the)X
+3187(following)X
+3522(additional)X
+3866(para-)X
+976 4362(graph)N
+1179(boundaries)X
+1551(would)X
+1771(be)X
+1867(recognized:)X
+1296 4575(<newline>.La)N
+1296 4665(<newline>.A<space>)N
+1296 4755(<newline>.##)N
+3 f
+776 4968(prompt)N
+1054([on])X
+976 5058(Ex)N
+1 f
+1090(only.)X
+1293(This)X
+1456(option)X
+1681(causes)X
+3 f
+1912(ex)X
+1 f
+2009(to)X
+2092(prompt)X
+2344(for)X
+2459(command)X
+2796(input)X
+2981(with)X
+3144(a)X
+3201(``)X
+7 f
+3255(:)X
+1 f
+('')S
+3378(character;)X
+3717(when)X
+3913(it)X
+3979(is)X
+976 5148(not)N
+1098(set,)X
+1227(no)X
+1327(prompt)X
+1578(is)X
+1651(displayed.)X
+3 f
+776 5328(readonly,)N
+1118(ro)X
+1214([off])X
+1 f
+976 5418(This)N
+1138(option)X
+1362(causes)X
+1592(a)X
+1648(force)X
+1834(\257ag)X
+1974(to)X
+2056(be)X
+2152(required)X
+2440(to)X
+2522(attempt)X
+2782(to)X
+2864(write)X
+3049(the)X
+3167(\256le)X
+3289(back)X
+3461(to)X
+3543(the)X
+3661(original)X
+3930(\256le)X
+976 5508(name.)N
+1215(Setting)X
+1466(this)X
+1605(option)X
+1833(is)X
+1910(equivalent)X
+2268(to)X
+2354(using)X
+2551(the)X
+3 f
+9 f
+2673(-)X
+2675(-)X
+3 f
+2719(R)X
+1 f
+2801(command)X
+3141(line)X
+3285(option,)X
+3533(or)X
+3624(editing)X
+3870(a)X
+3930(\256le)X
+976 5598(which)N
+1192(lacks)X
+1377(write)X
+1562(permission.)X
+
+50 p
+%%Page: 50 49
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-50)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+776 762(recdir)N
+1006([/var/tmp/vi.recover])X
+1 f
+976 852(The)N
+1121(directory)X
+1431(where)X
+1648(recovery)X
+1950(\256les)X
+2103(are)X
+2222(stored.)X
+976 1032(If)N
+1051(you)X
+1192(change)X
+1441(the)X
+1561(value)X
+1757(of)X
+3 f
+1846(recdir)X
+1 f
+2056(,)X
+2098(be)X
+2196(careful)X
+2442(to)X
+2526(choose)X
+2771(a)X
+2829(directory)X
+3141(whose)X
+3368(contents)X
+3657(are)X
+3778(not)X
+3902(reg-)X
+976 1122(ularly)N
+1197(deleted.)X
+1503(Bad)X
+1666(choices)X
+1941(include)X
+2211(directories)X
+2584(in)X
+2680(memory)X
+2981(based)X
+3198(\256lesystems,)X
+3607(or)X
+7 f
+3707(/tmp)X
+1 f
+(,)S
+3952(on)X
+976 1212(most)N
+1151(systems,)X
+1444(as)X
+1531(their)X
+1698(contents)X
+1985(are)X
+2104(removed)X
+2405(when)X
+2599(the)X
+2717(machine)X
+3009(is)X
+3082(rebooted.)X
+976 1392(Public)N
+1219(directories)X
+1597(like)X
+7 f
+1757(/usr/tmp)X
+1 f
+2181(and)X
+7 f
+2337(/var/tmp)X
+1 f
+2761(are)X
+2900(usually)X
+3171(safe,)X
+3361(although)X
+3681(some)X
+3890(sites)X
+976 1482(periodically)N
+1387(prune)X
+1598(old)X
+1728(\256les)X
+1889(from)X
+2073(them.)X
+2301(There)X
+2517(is)X
+2598(no)X
+2706(requirement)X
+3122(that)X
+3270(you)X
+3418(use)X
+3553(a)X
+3617(public)X
+3844(direc-)X
+976 1572(tory,)N
+1145(e.g.)X
+1281(a)X
+1337(sub-directory)X
+1785(of)X
+1872(your)X
+2039(home)X
+2237(directory)X
+2547(will)X
+2691(work)X
+2876(\256ne.)X
+976 1752(Finally,)N
+1248(if)X
+1323(you)X
+1469(change)X
+1723(the)X
+1847(value)X
+2047(of)X
+3 f
+2140(recdir)X
+1 f
+2350(,)X
+2396(you)X
+2542(must)X
+2723(modify)X
+2980(the)X
+3104(recovery)X
+3412(script)X
+3617(to)X
+3706(operate)X
+3970(in)X
+976 1842(your)N
+1143(chosen)X
+1386(recovery)X
+1688(area.)X
+976 2022(See)N
+1112(the)X
+1230(section)X
+1477(entitled)X
+1737(``)X
+3 f
+1791(Recovery)X
+1 f
+2113('')X
+2187(for)X
+2301(further)X
+2540(information.)X
+3 f
+776 2202(redraw,)N
+1066(re)X
+1158([off])X
+976 2292(Vi)N
+1 f
+1087(only.)X
+1301(The)X
+1458(editor)X
+1677(simulates)X
+2011(\(using)X
+2243(great)X
+2436(amounts)X
+2739(of)X
+2838(output\),)X
+3121(an)X
+3229(intelligent)X
+3585(terminal)X
+3884(on)X
+3996(a)X
+976 2382(dumb)N
+1200(terminal)X
+1509(\(e.g.)X
+1694(during)X
+1945(insertions)X
+2297(in)X
+3 f
+2400(vi)X
+1 f
+2503(the)X
+2642(characters)X
+3010(to)X
+3113(the)X
+3252(right)X
+3444(of)X
+3552(the)X
+3691(cursor)X
+3933(are)X
+976 2472(refreshed)N
+1296(as)X
+1383(each)X
+1551(input)X
+1735(character)X
+2051(is)X
+2124(typed\).)X
+2 f
+976 2652(This)N
+1133(option)X
+1357(is)X
+1430(not)X
+1552(yet)X
+1666(implemented.)X
+3 f
+776 2832(remap)N
+1019([on])X
+1 f
+976 2922(If)N
+1055(this)X
+1195(option)X
+1424(is)X
+1502(set,)X
+1636(it)X
+1705(is)X
+1783(possible)X
+2070(to)X
+2157(de\256ne)X
+2378(macros)X
+2635(in)X
+2722(terms)X
+2926(of)X
+3019(other)X
+3210(macros.)X
+3508(Otherwise,)X
+3884(each)X
+976 3012(key)N
+1121(is)X
+1203(only)X
+1374(remapped)X
+1720(up)X
+1829(to)X
+1920(one)X
+2064(time.)X
+2274(For)X
+2413(example,)X
+2733(if)X
+2810(``)X
+7 f
+2864(A)X
+1 f
+('')S
+2994(is)X
+3075(mapped)X
+3357(to)X
+3447(``)X
+7 f
+3501(B)X
+1 f
+('',)S
+3651(and)X
+3795(``)X
+7 f
+3849(B)X
+1 f
+('')S
+3979(is)X
+976 3102(mapped)N
+1251(to)X
+1334(``)X
+7 f
+1388(C)X
+1 f
+('',)S
+1531(The)X
+1677(keystroke)X
+2010(``)X
+7 f
+2064(A)X
+1 f
+('')S
+2187(will)X
+2332(be)X
+2429(mapped)X
+2704(to)X
+2787(``)X
+7 f
+2841(C)X
+1 f
+('')S
+2964(if)X
+3035(the)X
+3 f
+3155(remap)X
+1 f
+3400(option)X
+3626(is)X
+3701(set,)X
+3832(and)X
+3970(to)X
+976 3192(``)N
+7 f
+1030(B)X
+1 f
+('')S
+1152(if)X
+1221(it)X
+1285(is)X
+1358(not)X
+1480(set.)X
+3 f
+776 3372(report)N
+1015([5])X
+1 f
+976 3462(Set)N
+1103(the)X
+1226(threshold)X
+1549(of)X
+1642(the)X
+1766(number)X
+2037(of)X
+2130(lines)X
+2307(that)X
+2453(need)X
+2631(to)X
+2719(be)X
+2821(changed)X
+3115(or)X
+3208(yanked)X
+3466(before)X
+3698(a)X
+3760(message)X
+976 3552(will)N
+1127(be)X
+1230(displayed)X
+1564(to)X
+1653(the)X
+1778(user.)X
+1979(For)X
+2117(everything)X
+2487(but)X
+2616(the)X
+2741(yank)X
+2924(command,)X
+3287(the)X
+3412(value)X
+3613(is)X
+3693(the)X
+3818(largest)X
+976 3642(value)N
+1172(about)X
+1372(which)X
+1590(the)X
+1711(editor)X
+1921(is)X
+1997(silent,)X
+2213(i.e.)X
+2334(by)X
+2437(default,)X
+2703(6)X
+2766(lines)X
+2940(must)X
+3118(be)X
+3217(deleted)X
+3472(before)X
+3701(the)X
+3822(user)X
+3979(is)X
+976 3732(noti\256ed.)N
+1283(However,)X
+1621(if)X
+1693(the)X
+1814(number)X
+2082(of)X
+2172(lines)X
+2346(yanked)X
+2601(is)X
+2677(greater)X
+2924(than)X
+2 f
+3085(or)X
+3179(equal)X
+3380(to)X
+1 f
+3465(the)X
+3586(set)X
+3697(value,)X
+3913(it)X
+3979(is)X
+976 3822(reported)N
+1264(to)X
+1346(the)X
+1464(user.)X
+3 f
+776 4002(ruler)N
+970([off])X
+976 4092(Vi)N
+1 f
+1076(only.)X
+1278(Display)X
+1547(a)X
+1603(row/column)X
+2010(ruler)X
+2182(on)X
+2282(the)X
+2400(colon)X
+2598(command)X
+2934(line.)X
+3 f
+776 4272(scroll,)N
+1003(scr)X
+1126([window)X
+1439(/)X
+1481(2])X
+1 f
+976 4362(Set)N
+1098(the)X
+1216(number)X
+1481(of)X
+1568(lines)X
+1739(scrolled)X
+2013(by)X
+2113(the)X
+3 f
+2231(vi)X
+2313(<control-D>)X
+1 f
+2755(and)X
+3 f
+2891(<control-U>)X
+1 f
+3333(commands.)X
+976 4542(Historically,)N
+1395(the)X
+3 f
+1514(ex)X
+1611(z)X
+1 f
+1668(command,)X
+2025(when)X
+2221(speci\256ed)X
+2528(without)X
+2794(a)X
+2852(count,)X
+3072(used)X
+3241(two)X
+3383(times)X
+3578(the)X
+3698(size)X
+3845(of)X
+3934(the)X
+976 4632(scroll)N
+1174(value;)X
+1390(the)X
+1508(POSIX)X
+1759(1003.2)X
+1999(standard)X
+2291(speci\256ed)X
+2596(the)X
+2714(window)X
+2992(size,)X
+3157(which)X
+3373(is)X
+3446(a)X
+3502(better)X
+3705(choice.)X
+3 f
+776 4812(sections,)N
+1083(sect)X
+1233([NHSHH)X
+1568(HUnhsh])X
+976 4902(Vi)N
+1 f
+1076(only.)X
+1278(De\256ne)X
+1512(additional)X
+1852(section)X
+2099(boundaries)X
+2471(for)X
+2585(the)X
+3 f
+2703([[)X
+1 f
+2777(and)X
+3 f
+2913(]])X
+1 f
+2987(commands.)X
+3394(The)X
+3 f
+3540(sections)X
+1 f
+3828(option)X
+976 4992(should)N
+1213(be)X
+1313(set)X
+1426(to)X
+1512(a)X
+1572(character)X
+1892(string)X
+2098(consisting)X
+2446(of)X
+2537(zero)X
+2700(or)X
+2791(more)X
+2979(character)X
+3298(pairs.)X
+3517(In)X
+3607(the)X
+3728(text)X
+3871(to)X
+3956(be)X
+976 5082(edited,)N
+1219(the)X
+1344(character)X
+1667(string)X
+7 f
+1876(<newline>.<char-pair>)X
+1 f
+(,)S
+2932(\(where)X
+7 f
+3184(<char-pair>)X
+1 f
+3740(is)X
+3821(one)X
+3965(of)X
+976 5172(the)N
+1100(character)X
+1422(pairs)X
+1604(in)X
+1692(the)X
+1816(option's)X
+2104(value\),)X
+2351(de\256nes)X
+2604(a)X
+2666(section)X
+2918(boundary)X
+3246(in)X
+3333(the)X
+3456(same)X
+3646(manner)X
+3912(that)X
+3 f
+976 5262(paragraph)N
+1 f
+1360(option)X
+1584(boundaries)X
+1956(are)X
+2075(de\256ned.)X
+3 f
+776 5442(shell,)N
+971(sh)X
+1066([environment)X
+1549(variable)X
+1849(SHELL,)X
+2154(or)X
+2250(/bin/sh])X
+1 f
+976 5532(Select)N
+1195(the)X
+1316(shell)X
+1490(used)X
+1660(by)X
+1763(the)X
+1884(editor.)X
+2134(The)X
+2282(speci\256ed)X
+2590(path)X
+2751(is)X
+2827(the)X
+2948(pathname)X
+3283(of)X
+3373(the)X
+3495(shell)X
+3670(invoked)X
+3952(by)X
+976 5622(the)N
+3 f
+1103(vi)X
+1194(!)X
+1 f
+1270(shell)X
+1450(escape)X
+1694(command)X
+2038(and)X
+2182(by)X
+2290(the)X
+3 f
+2416(ex)X
+2520(shell)X
+1 f
+2703(command.)X
+3087(This)X
+3257(program)X
+3557(is)X
+3638(also)X
+3795(used)X
+3970(to)X
+976 5712(resolve)N
+1228(any)X
+1364(shell)X
+1535 0.2679(meta-characters)AX
+2065(in)X
+3 f
+2147(ex)X
+1 f
+2243(commands.)X
+
+51 p
+%%Page: 51 50
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-51)X
+776 762(shiftwidth,)N
+1162(sw)X
+1271([8])X
+1 f
+976 852(Set)N
+1098(the)X
+1216(autoindent)X
+1575(and)X
+1712(shift)X
+1875(command)X
+2212(indentation)X
+2593(width.)X
+2836(This)X
+2999(width)X
+3202(is)X
+3276(used)X
+3444(by)X
+3545(the)X
+3 f
+3664(autoindent)X
+1 f
+976 942(option)N
+1200(and)X
+1336(by)X
+1436(the)X
+3 f
+1554(<)X
+1 f
+1600(,)X
+3 f
+1640(>)X
+1 f
+1686(,)X
+1726(and)X
+3 f
+1862(shift)X
+1 f
+2033(commands.)X
+3 f
+776 1122(showdirty)N
+1138([off])X
+976 1212(Vi)N
+1 f
+1076(only.)X
+1278(Display)X
+1547(an)X
+1643(asterisk)X
+1908(on)X
+2008(the)X
+2126(colon)X
+2324(command)X
+2660(line)X
+2800(if)X
+2869(the)X
+2987(\256le)X
+3109(has)X
+3236(been)X
+3408(modi\256ed.)X
+3 f
+776 1392(showmatch,)N
+1203(sm)X
+1321([off])X
+976 1482(Vi)N
+1 f
+1078(only.)X
+1282(This)X
+1446(option)X
+1673(causes)X
+3 f
+1906(vi)X
+1 f
+1968(,)X
+2011(when)X
+2208(a)X
+2267(``)X
+7 f
+2321(})X
+1 f
+('')S
+2446(or)X
+2536(``)X
+7 f
+2590(\))X
+1 f
+('')S
+2715(is)X
+2791(entered,)X
+3071(to)X
+3156(brie\257y)X
+3388(move)X
+3589(the)X
+3710(cursor)X
+3934(the)X
+976 1572(matching)N
+1294(``)X
+7 f
+1348({)X
+1 f
+('')S
+1470(or)X
+1557(``)X
+7 f
+1611(\()X
+1 f
+(''.)S
+1773(See)X
+1909(the)X
+3 f
+2027(matchtime)X
+1 f
+2413(option)X
+2637(for)X
+2751(more)X
+2936(information.)X
+3 f
+776 1752(showmode)N
+1156([off])X
+976 1842(Vi)N
+1 f
+1083(only.)X
+1292(This)X
+1461(option)X
+1693(causes)X
+3 f
+1931(vi)X
+1 f
+2021(to)X
+2111(display)X
+2370(a)X
+2434(string)X
+2644(identifying)X
+3023(the)X
+3149(current)X
+3405(editor)X
+3620(mode)X
+3826(on)X
+3934(the)X
+976 1932(colon)N
+1174(command)X
+1510(line.)X
+3 f
+776 2112(sidescroll)N
+1116([16])X
+976 2202(Vi)N
+1 f
+1078(only.)X
+1282(Sets)X
+1437(the)X
+1557(number)X
+1825(of)X
+1915(columns)X
+2209(that)X
+2352(are)X
+2474(shifted)X
+2715(to)X
+2800(the)X
+2921(left)X
+3051(or)X
+3141(right,)X
+3335(when)X
+3 f
+3532(vi)X
+1 f
+3617(is)X
+3693(doing)X
+3898(left-)X
+976 2292(right)N
+1151(scrolling)X
+1455(and)X
+1595(the)X
+1717(left)X
+1848(or)X
+1939(right)X
+2114(margin)X
+2365(is)X
+2442(crossed.)X
+2747(See)X
+2887(the)X
+3 f
+3009(leftright)X
+1 f
+3314(option)X
+3542(for)X
+3660(more)X
+3849(infor-)X
+976 2382(mation.)N
+3 f
+776 2562(slowopen,)N
+1131(slow)X
+1302([off])X
+1 f
+976 2652(This)N
+1141(option)X
+1368(affects)X
+1606(the)X
+1727(display)X
+1981(algorithm)X
+2315(used)X
+2485(by)X
+3 f
+2588(vi)X
+1 f
+2650(,)X
+2693(holding)X
+2960(off)X
+3077(display)X
+3331(updating)X
+3635(during)X
+3868(input)X
+976 2742(of)N
+1063(new)X
+1217(text)X
+1357(to)X
+1439(improve)X
+1726(throughput)X
+2097(when)X
+2291(the)X
+2409(terminal)X
+2696(in)X
+2778(use)X
+2905(is)X
+2978(slow)X
+3149(and)X
+3285(unintelligent.)X
+2 f
+976 2922(This)N
+1133(option)X
+1357(is)X
+1430(not)X
+1552(yet)X
+1666(implemented.)X
+3 f
+776 3102(sourceany)N
+1143([off])X
+1 f
+976 3192(If)N
+1054(this)X
+1193(option)X
+1421(is)X
+1498(turned)X
+1727(on,)X
+3 f
+1851(vi)X
+1 f
+1937(historically)X
+2321(read)X
+2485(startup)X
+2728(\256les)X
+2886(that)X
+3031(were)X
+3213(owned)X
+3452(by)X
+3557(someone)X
+3867(other)X
+976 3282(than)N
+1142(the)X
+1268(editor)X
+1483(user.)X
+1685(See)X
+1829(the)X
+1955(section)X
+2210(entitled)X
+2478(``)X
+3 f
+2532(Startup)X
+2822(Information)X
+1 f
+3240('')X
+3321(for)X
+3442(more)X
+3634(information.)X
+976 3372(This)N
+1144(option)X
+1374(is)X
+1453(a)X
+1516(security)X
+1797(problem)X
+2091(of)X
+2185(immense)X
+2501(proportions,)X
+2917(and)X
+3060(should)X
+3300(not)X
+3429(be)X
+3532(used)X
+3706(under)X
+3916(any)X
+976 3462(circumstances.)N
+2 f
+976 3642(This)N
+1133(option)X
+1357(will)X
+1496(never)X
+1695(be)X
+1791(implemented.)X
+3 f
+776 3822(tabstop,)N
+1069(ts)X
+1147([8])X
+1 f
+976 3912(This)N
+1138(option)X
+1362(sets)X
+1502(tab)X
+1620(widths)X
+1853(for)X
+1967(the)X
+2085(editor)X
+2292(display.)X
+3 f
+776 4092(taglength,)N
+1136(tl)X
+1205([0])X
+1 f
+976 4182(This)N
+1148(option)X
+1383(sets)X
+1534(the)X
+1663(maximum)X
+2018(number)X
+2294(of)X
+2392(characters)X
+2750(that)X
+2901(are)X
+3031(considered)X
+3410(signi\256cant)X
+3774(in)X
+3867(a)X
+3934(tag)X
+976 4272(name.)N
+1210(Setting)X
+1456(the)X
+1574(value)X
+1768(to)X
+1850(0)X
+1910(makes)X
+2135(all)X
+2235(of)X
+2322(the)X
+2440(characters)X
+2787(in)X
+2869(the)X
+2987(tag)X
+3105(name)X
+3299(signi\256cant.)X
+3 f
+776 4452(tags,)N
+954(tag)X
+1081([tags)X
+1266(/var/db/libc.tags)X
+1838(/sys/kern/tags])X
+1 f
+976 4542(Sets)N
+1129(the)X
+1247(list)X
+1364(of)X
+1451(tags)X
+1600(\256les,)X
+1773(in)X
+1855(search)X
+2081(order,)X
+2291(which)X
+2507(are)X
+2626(used)X
+2793(when)X
+2987(the)X
+3105(editor)X
+3312(searches)X
+3605(for)X
+3719(a)X
+3775(tag.)X
+3 f
+776 4722(term,)N
+982(ttytype,)X
+1263(tty)X
+1377([environment)X
+1860(variable)X
+2160(TERM])X
+1 f
+976 4812(Set)N
+1101(the)X
+1223(terminal)X
+1514(type.)X
+1716(Setting)X
+1966(this)X
+2105(option)X
+2333(causes)X
+3 f
+2567(ex)X
+1 f
+2643(/)X
+3 f
+2665(vi)X
+1 f
+2751(to)X
+2837(set)X
+2950(\(or)X
+3068(reset\))X
+3271(the)X
+3393(environmental)X
+3880(vari-)X
+976 4902(able)N
+7 f
+1130(TERM)X
+1 f
+(.)S
+3 f
+776 5082(terse)N
+962([off])X
+1 f
+976 5172(This)N
+1142(option)X
+1370(has)X
+1501(historically)X
+1885(made)X
+2083(editor)X
+2294(messages)X
+2621(less)X
+2765(verbose.)X
+3079(It)X
+3152(has)X
+3283(no)X
+3387(effect)X
+3596(in)X
+3683(this)X
+3823(imple-)X
+976 5262(mentation.)N
+1356(See)X
+1492(the)X
+3 f
+1610(verbose)X
+1 f
+1893(option)X
+2117(for)X
+2231(more)X
+2416(information.)X
+3 f
+776 5442(tildeop)N
+1 f
+976 5532(Modify)N
+1236(the)X
+3 f
+1354(\304)X
+1 f
+1401(command)X
+1737(to)X
+1819(take)X
+1973(an)X
+2069(associated)X
+2419(motion.)X
+3 f
+776 5712(timeout,)N
+1079(to)X
+1166([on])X
+1 f
+976 5802(If)N
+1057(this)X
+1199(option)X
+1431(is)X
+1512(set,)X
+3 f
+1649(ex)X
+1 f
+1725(/)X
+3 f
+1747(vi)X
+1 f
+1837(waits)X
+2034(for)X
+2156(a)X
+2220(speci\256c)X
+2493(period)X
+2726(for)X
+2848(a)X
+2912(subsequent)X
+3296(key)X
+3440(to)X
+3530(complete)X
+3852(a)X
+3916(key)X
+
+52 p
+%%Page: 52 51
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-52)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+1 f
+976 762(mapping)N
+1282(\(see)X
+1438(the)X
+3 f
+1561(keytime)X
+1 f
+1858(option\).)X
+2154(If)X
+2233(the)X
+2356(option)X
+2585(is)X
+2663(not)X
+2790(set,)X
+2924(the)X
+3047(editor)X
+3259(waits)X
+3453(until)X
+3624(enough)X
+3885(keys)X
+976 852(are)N
+1095(entered)X
+1352(to)X
+1434(resolve)X
+1686(the)X
+1804(ambiguity,)X
+2168(regardless)X
+2514(of)X
+2601(how)X
+2759(long)X
+2921(it)X
+2985(takes.)X
+3 f
+776 1032(ttywerase)N
+1127([off])X
+976 1122(Vi)N
+1 f
+1082(only.)X
+1290(This)X
+1458(option)X
+1688(changes)X
+1973(how)X
+3 f
+2137(vi)X
+1 f
+2225(does)X
+2398(word)X
+2589(erase)X
+2781(during)X
+3016(text)X
+3162(input.)X
+3392(If)X
+3472(this)X
+3613(option)X
+3843(is)X
+3923(set,)X
+976 1212(text)N
+1123(is)X
+1203(broken)X
+1452(up)X
+1558(into)X
+1708(two)X
+1854(classes,)X
+2123(blank)X
+2327(characters)X
+2680(and)X
+2822(nonblank)X
+3146(characters.)X
+3539(Changing)X
+3876(from)X
+976 1302(one)N
+1112(class)X
+1288(to)X
+1370(another)X
+1631(marks)X
+1847(the)X
+1965(end)X
+2101(of)X
+2188(a)X
+2244(word.)X
+3 f
+776 1482(verbose)N
+1059([off])X
+976 1572(Vi)N
+1 f
+1078(only.)X
+3 f
+1282(Vi)X
+1 f
+1384(historically)X
+1766(bells)X
+1939(the)X
+2059(terminal)X
+2348(for)X
+2464(many)X
+2665(obvious)X
+2941(mistakes,)X
+3264(e.g.)X
+3403(trying)X
+3617(to)X
+3702(move)X
+3903(past)X
+976 1662(the)N
+1108(left-hand)X
+1432(margin,)X
+1713(or)X
+1814(past)X
+1977(the)X
+2109(end)X
+2259(of)X
+2360(the)X
+2492(\256le.)X
+2668(If)X
+2756(this)X
+2905(option)X
+3143(is)X
+3230(set,)X
+3373(an)X
+3483(error)X
+3674(message)X
+3979(is)X
+976 1752(displayed)N
+1303(for)X
+1417(all)X
+1517(errors.)X
+3 f
+776 1932(w300)N
+974([no)X
+1105(default])X
+976 2022(Vi)N
+1 f
+1080(only.)X
+1286(Set)X
+1412(the)X
+1534(window)X
+1816(size)X
+1965(if)X
+2038(the)X
+2160(baud)X
+2340(rate)X
+2485(is)X
+2562(less)X
+2706(than)X
+2868(1200)X
+3052(baud.)X
+3273(See)X
+3414(the)X
+3 f
+3537(window)X
+1 f
+3828(option)X
+976 2112(for)N
+1090(more)X
+1275(information.)X
+3 f
+776 2292(w1200)N
+1014([no)X
+1145(default])X
+976 2382(Vi)N
+1 f
+1081(only.)X
+1288(Set)X
+1415(the)X
+1538(window)X
+1821(size)X
+1971(if)X
+2045(the)X
+2168(baud)X
+2349(rate)X
+2495(is)X
+2574(equal)X
+2774(to)X
+2862(1200)X
+3048(baud.)X
+3270(See)X
+3412(the)X
+3 f
+3536(window)X
+1 f
+3828(option)X
+976 2472(for)N
+1090(more)X
+1275(information.)X
+3 f
+776 2652(w9600)N
+1014([no)X
+1145(default])X
+976 2742(Vi)N
+1 f
+1087(only.)X
+1300(Set)X
+1433(the)X
+1562(window)X
+1851(size)X
+2007(if)X
+2087(the)X
+2216(baud)X
+2404(rate)X
+2557(is)X
+2642(greater)X
+2898(than)X
+3068(1200)X
+3260(baud.)X
+3488(See)X
+3636(the)X
+3 f
+3766(window)X
+1 f
+976 2832(option)N
+1200(for)X
+1314(more)X
+1499(information.)X
+3 f
+776 3012(warn)N
+974([on])X
+976 3102(Ex)N
+1 f
+1094(only.)X
+1302(This)X
+1470(option)X
+1700(causes)X
+1936(a)X
+1998(warning)X
+2287(message)X
+2585(to)X
+2673(the)X
+2797(terminal)X
+3090(if)X
+3165(the)X
+3289(\256le)X
+3417(has)X
+3550(been)X
+3728(modi\256ed,)X
+976 3192(since)N
+1161(it)X
+1225(was)X
+1370(last)X
+1501(written,)X
+1768(before)X
+1994(a)X
+3 f
+2050(!)X
+1 f
+2117(command.)X
+3 f
+776 3372(window,)N
+1082(w,)X
+1180(wi)X
+1280([environment)X
+1763(variable)X
+2063(LINES])X
+1 f
+976 3462(This)N
+1148(option)X
+1382(determines)X
+1764(the)X
+1892(default)X
+2145(number)X
+2420(of)X
+2517(lines)X
+2698(in)X
+2791(a)X
+2858(screenful,)X
+3204(as)X
+3302(written)X
+3560(by)X
+3671(the)X
+3 f
+3800(z)X
+1 f
+3867(com-)X
+976 3552(mand.)N
+1221(It)X
+1297(also)X
+1453(determines)X
+1832(the)X
+1957(number)X
+2229(of)X
+2323(lines)X
+2501(scrolled)X
+2782(by)X
+2889(the)X
+3 f
+3014(vi)X
+1 f
+3103(commands)X
+3 f
+3477(<control-F>)X
+1 f
+3916(and)X
+3 f
+976 3642(<control-B>)N
+1 f
+1393(.)X
+1457(The)X
+1606(value)X
+1804(of)X
+1895(window)X
+2177(can)X
+2313(be)X
+2413(unrelated)X
+2736(to)X
+2822(the)X
+2944(real)X
+3089(screen)X
+3319(size,)X
+3489(although)X
+3794(it)X
+3863(starts)X
+976 3732(out)N
+1110(as)X
+1209(the)X
+1338(number)X
+1614(of)X
+1712(lines)X
+1894(on)X
+2005(the)X
+2134(screen)X
+2371(\(see)X
+2532(the)X
+2661(section)X
+2919(entitled)X
+3190(``)X
+3 f
+3244(Sizing)X
+3483(the)X
+3621(Screen)X
+1 f
+3853('')X
+3938(for)X
+976 3822(more)N
+1165(information\).)X
+1634(Setting)X
+1884(the)X
+2006(value)X
+2204(of)X
+2295(the)X
+3 f
+2417(window)X
+1 f
+2707(option)X
+2935(is)X
+3012(the)X
+3135(same)X
+3325(as)X
+3417(using)X
+3615(the)X
+3 f
+9 f
+3738(-)X
+3740(-)X
+3 f
+3784(w)X
+1 f
+3867(com-)X
+976 3912(mand)N
+1174(line)X
+1314(option.)X
+976 4092(If)N
+1055(the)X
+1178(value)X
+1377(of)X
+1469(the)X
+3 f
+1592(window)X
+1 f
+1883(option)X
+2112(\(as)X
+2231(set)X
+2345(by)X
+2450(the)X
+3 f
+2574(window)X
+1 f
+2840(,)X
+3 f
+2886(w300)X
+1 f
+3064(,)X
+3 f
+3110(w1200)X
+1 f
+3354(or)X
+3 f
+3447(w9600)X
+1 f
+3691(options\))X
+3979(is)X
+976 4182(smaller)N
+1233(than)X
+1391(the)X
+1509(actual)X
+1721(size)X
+1866(of)X
+1953(the)X
+2071(screen,)X
+2317(large)X
+2498(screen)X
+2724(movements)X
+3113(will)X
+3257(result)X
+3455(in)X
+3537(displaying)X
+3890(only)X
+976 4272(that)N
+1116(smaller)X
+1373(number)X
+1639(of)X
+1727(lines)X
+1899(on)X
+2000(the)X
+2119(screen.)X
+2386(\(Further)X
+2670(movements)X
+3060(in)X
+3143(that)X
+3284(same)X
+3470(area)X
+3626(will)X
+3771(result)X
+3970(in)X
+976 4362(the)N
+1097(screen)X
+1325(being)X
+1525(\256lled.\))X
+1778(This)X
+1942(can)X
+2076(provide)X
+2343(a)X
+2401(performance)X
+2830(improvement)X
+3279(when)X
+3475(viewing)X
+3755(different)X
+976 4452(places)N
+1197(in)X
+1279(one)X
+1415(or)X
+1502(more)X
+1687(\256les)X
+1840(over)X
+2003(a)X
+2059(slow)X
+2230(link.)X
+3 f
+776 4632(wrapmargin,)N
+1243(wm)X
+1388([0])X
+976 4722(Vi)N
+1 f
+1077(only.)X
+1280(If)X
+1356(the)X
+1476(value)X
+1672(of)X
+1761(the)X
+3 f
+1881(wrapmargin)X
+1 f
+2330(option)X
+2556(is)X
+2631(non-zero,)X
+3 f
+2959(vi)X
+1 f
+3043(will)X
+3189(split)X
+3348(lines)X
+3521(so)X
+3614(that)X
+3756(they)X
+3916(end)X
+976 4812(at)N
+1057(least)X
+1227(that)X
+1370(number)X
+1638(of)X
+1728(characters)X
+2078(before)X
+2307(the)X
+2428(right-hand)X
+2785(margin)X
+3035(of)X
+3125(the)X
+3245(screen.)X
+3513(\(Note,)X
+3738(the)X
+3858(value)X
+976 4902(of)N
+3 f
+1063(wrapmargin)X
+1 f
+1510(is)X
+2 f
+1583(not)X
+1 f
+1705(a)X
+1761(text)X
+1901(length.)X
+2161(In)X
+2248(a)X
+2304(screen)X
+2530(that)X
+2670(is)X
+2743(80)X
+2843(columns)X
+3134(wide,)X
+3330(the)X
+3449(command)X
+3786(``)X
+7 f
+3840(:set)X
+976 4992(wrapmargin=8)N
+1 f
+('')S
+1626(attempts)X
+1917(to)X
+1999(keep)X
+2171(the)X
+2289(lines)X
+2460(less)X
+2600(than)X
+2758(or)X
+2845(equal)X
+3039(to)X
+3121(72)X
+3221(columns)X
+3512(wide.\))X
+976 5172(Lines)N
+1181(are)X
+1307(split)X
+1471(at)X
+1556(the)X
+1681(previous)X
+1984(whitespace)X
+2368(character)X
+2691(closest)X
+2936(to)X
+3025(the)X
+3150(number.)X
+3462(Any)X
+3627(trailing)X
+3885(whi-)X
+976 5262(tespace)N
+1239(characters)X
+1592(before)X
+1824(that)X
+1970(character)X
+2292(are)X
+2417(deleted.)X
+2715(If)X
+2795(the)X
+2919(line)X
+3065(is)X
+3143(split)X
+3305(because)X
+3585(of)X
+3677(an)X
+3778(inserted)X
+7 f
+976 5352(<space>)N
+1 f
+1346(or)X
+7 f
+1447(<tab>)X
+1 f
+1721(character,)X
+2072(and)X
+2223(you)X
+2378(then)X
+2551(enter)X
+2747(another)X
+7 f
+3023(<space>)X
+1 f
+3394(character,)X
+3745(it)X
+3824(is)X
+3912(dis-)X
+976 5442(carded.)N
+976 5622(If)N
+1052(wrapmargin)X
+1462(is)X
+1537(set)X
+1648(to)X
+1732(0,)X
+1815(or)X
+1905(if)X
+1977(there)X
+2161(is)X
+2237(no)X
+2340(blank)X
+2541(character)X
+2860(upon)X
+3043(which)X
+3262(to)X
+3347(split)X
+3507(the)X
+3628(line,)X
+3791(the)X
+3912(line)X
+976 5712(is)N
+1049(not)X
+1171(broken.)X
+
+53 p
+%%Page: 53 52
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-53)X
+776 762(wrapscan,)N
+1145(ws)X
+1254([on])X
+1 f
+976 852(This)N
+1138(option)X
+1362(causes)X
+1592(searches)X
+1885(to)X
+1967(wrap)X
+2148(around)X
+2391(the)X
+2509(end)X
+2645(or)X
+2732(the)X
+2850(beginning)X
+3191(of)X
+3279(the)X
+3398(\256le,)X
+3541(and)X
+3678(back)X
+3851(to)X
+3934(the)X
+976 942(starting)N
+1236(point.)X
+1460(Otherwise,)X
+1830(the)X
+1948(end)X
+2084(or)X
+2171(beginning)X
+2511(of)X
+2598(the)X
+2716(\256le)X
+2838(terminates)X
+3192(the)X
+3310(search.)X
+3 f
+776 1122(writeany,)N
+1119(wa)X
+1237([off])X
+1 f
+976 1212(If)N
+1056(this)X
+1197(option)X
+1427(is)X
+1506(set,)X
+1641(\256le-overwriting)X
+2170(checks)X
+2416(that)X
+2563(would)X
+2790(usually)X
+3048(be)X
+3151(made)X
+3352(before)X
+3585(the)X
+3 f
+3710(write)X
+1 f
+3916(and)X
+3 f
+976 1302(xit)N
+1 f
+1094(commands,)X
+1490(or)X
+1586(before)X
+1821(an)X
+1926(automatic)X
+2271(write)X
+2465(\(see)X
+2624(the)X
+3 f
+2751(autowrite)X
+1 f
+3110(option\),)X
+3390(are)X
+3518(not)X
+3648(made.)X
+3890(This)X
+976 1392(allows)N
+1205(a)X
+1261(write)X
+1446(to)X
+1528(any)X
+1664(\256le,)X
+1806(provided)X
+2111(the)X
+2229(\256le)X
+2351(permissions)X
+2753(allow)X
+2951(it.)X
+3 f
+776 1578(16.)N
+916(Additional)X
+1299(Features)X
+1618(in)X
+1704(Nex/Nvi)X
+1 f
+976 1701(There)N
+1190(are)X
+1315(a)X
+1377(few)X
+1524(features)X
+1805(in)X
+3 f
+1894(nex)X
+1 f
+(/)S
+3 f
+2036(nvi)X
+1 f
+2169(that)X
+2316(are)X
+2442(not)X
+2571(found)X
+2785(in)X
+2874(historic)X
+3141(versions)X
+3435(of)X
+3 f
+3529(ex)X
+1 f
+3605(/)X
+3 f
+3627(vi)X
+1 f
+3689(.)X
+3756(Some)X
+3965(of)X
+776 1791(the)N
+894(more)X
+1079(interesting)X
+1437(of)X
+1524(those)X
+1713(features)X
+1988(are)X
+2107(as)X
+2194(follows:)X
+3 f
+776 1971(8-bit)N
+956(clean)X
+1154(data,)X
+1345(large)X
+1539(lines,)X
+1734(\256les)X
+976 2061(Nex)N
+1 f
+1110(/)X
+3 f
+1132(nvi)X
+1 f
+1260(will)X
+1407(edit)X
+1550(any)X
+1689(format)X
+1926(\256le.)X
+2091(Line)X
+2261(lengths)X
+2515(are)X
+2637(limited)X
+2886(by)X
+2989(available)X
+3302(memory,)X
+3612(and)X
+3751(\256le)X
+3876(sizes)X
+976 2151(are)N
+1099(limited)X
+1349(by)X
+1453(available)X
+1767(disk)X
+1924(space.)X
+2167(The)X
+3 f
+2316(vi)X
+1 f
+2402(text)X
+2546(input)X
+2734(mode)X
+2935(command)X
+3 f
+3274(<control-X>)X
+1 f
+3719(can)X
+3854(insert)X
+976 2241(any)N
+1112(possible)X
+1394(character)X
+1710(value)X
+1904(into)X
+2048(the)X
+2166(text.)X
+3 f
+776 2421(Split)N
+955(screens)X
+1 f
+976 2511(The)N
+3 f
+1127(split)X
+1 f
+1299(command)X
+1641(divides)X
+1898(the)X
+2022(screen)X
+2254(into)X
+2404(multiple)X
+2696(editing)X
+2944(regions.)X
+3246(The)X
+3 f
+3397(<control-W>)X
+1 f
+3867(com-)X
+976 2601(mand)N
+1183(rotates)X
+1426(between)X
+1723(the)X
+1850(foreground)X
+2236(screens.)X
+2541(The)X
+3 f
+2694(resize)X
+1 f
+2919(command)X
+3263(can)X
+3403(be)X
+3507(used)X
+3682(to)X
+3772(grow)X
+3965(or)X
+976 2691(shrink)N
+1196(a)X
+1252(particular)X
+1580(screen.)X
+3 f
+776 2871(Background)N
+1217(and)X
+1365(foreground)X
+1772(screens)X
+1 f
+976 2961(The)N
+3 f
+1134(bg)X
+1 f
+1251(command)X
+1600(backgrounds)X
+2044(the)X
+2176(current)X
+2438(screen,)X
+2698(and)X
+2848(the)X
+3 f
+2980(fg)X
+1 f
+3081(command)X
+3431(foregrounds)X
+3853(back-)X
+976 3051(grounded)N
+1299(screens.)X
+1596(The)X
+3 f
+1741(display)X
+1 f
+2004(command)X
+2340(can)X
+2472(be)X
+2568(used)X
+2735(to)X
+2817(list)X
+2934(the)X
+3052(background)X
+3451(screens.)X
+3 f
+776 3231(Tag)N
+929(stacks)X
+1 f
+976 3321(Tags)N
+1159(are)X
+1285(now)X
+1450(maintained)X
+1834(in)X
+1924(a)X
+1988(stack.)X
+2221(The)X
+3 f
+2374(<control-T>)X
+1 f
+2819(command)X
+3163(returns)X
+3414(to)X
+3504(the)X
+3630(previous)X
+3934(tag)X
+976 3411(location.)N
+1299(The)X
+3 f
+1449(tagpop)X
+1 f
+1709(command)X
+2050(returns)X
+2298(to)X
+2385(the)X
+2508(most)X
+2688(recent)X
+2910(tag)X
+3033(location)X
+3316(by)X
+3421(default,)X
+3689(or,)X
+3801(option-)X
+976 3501(ally)N
+1119(to)X
+1204(a)X
+1263(speci\256c)X
+1531(tag)X
+1652(number)X
+1920(in)X
+2005(the)X
+2126(tag)X
+2248(stack,)X
+2457(or)X
+2548(the)X
+2670(most)X
+2849(recent)X
+3070(tag)X
+3192(from)X
+3372(a)X
+3432(speci\256ed)X
+3741(\256le.)X
+3907(The)X
+3 f
+976 3591(display)N
+1 f
+1241(command)X
+1579(can)X
+1713(be)X
+1810(used)X
+1978(to)X
+2061(list)X
+2179(the)X
+2298(tags)X
+2448(stack.)X
+2674(The)X
+3 f
+2820(tagtop)X
+1 f
+3059(command)X
+3396(returns)X
+3640(to)X
+3723(the)X
+3842(top)X
+3965(of)X
+976 3681(the)N
+1094(tag)X
+1212(stack.)X
+3 f
+776 3861(New)N
+948(displays)X
+1 f
+976 3951(The)N
+3 f
+1128(display)X
+1 f
+1398(command)X
+1741(can)X
+1880(be)X
+1984(used)X
+2159(to)X
+2249(display)X
+2508(the)X
+2634(current)X
+2890(buffers,)X
+3166(the)X
+3292(backgrounded)X
+3775(screens,)X
+976 4041(and)N
+1112(the)X
+1230(tags)X
+1379(stack.)X
+3 f
+776 4221(In\256nite)N
+1044(undo)X
+1 f
+976 4311(Changes)N
+1282(made)X
+1486(during)X
+1725(an)X
+1832(edit)X
+1983(session)X
+2245(may)X
+2414(be)X
+2521(rolled)X
+2739(backward)X
+3083(and)X
+3230(forward.)X
+3556(A)X
+3 f
+3645(.)X
+1 f
+3716(command)X
+976 4401(immediately)N
+1403(after)X
+1578(a)X
+3 f
+1640(u)X
+1 f
+1710(command)X
+2052(continues)X
+2385(either)X
+2594(forward)X
+2875(or)X
+2968(backward)X
+3307(depending)X
+3667(on)X
+3773(whether)X
+976 4491(the)N
+3 f
+1094(u)X
+1 f
+1158(command)X
+1494(was)X
+1639(an)X
+1735(undo)X
+1915(or)X
+2002(a)X
+2058(redo.)X
+3 f
+776 4671(Usage)N
+1001(information)X
+1 f
+976 4761(The)N
+3 f
+1126(exusage)X
+1 f
+1418(and)X
+3 f
+1559(viusage)X
+1 f
+1837(commands)X
+2209(provide)X
+2479(usage)X
+2688(information)X
+3092(for)X
+3212(all)X
+3318(of)X
+3411(the)X
+3 f
+3535(ex)X
+1 f
+3637(and)X
+3 f
+3779(vi)X
+1 f
+3867(com-)X
+976 4851(mands)N
+1205(by)X
+1305(default,)X
+1568(or,)X
+1675(optionally,)X
+2039(for)X
+2153(a)X
+2209(speci\256c)X
+2474(command)X
+2810(or)X
+2897(key.)X
+3 f
+776 5031(Extended)N
+1120(Regular)X
+1416(Expressions)X
+1 f
+976 5121(The)N
+3 f
+1133(extended)X
+1 f
+1472(option)X
+1708(causes)X
+1950(Regular)X
+2237(Expressions)X
+2657(to)X
+2752(be)X
+2861(interpreted)X
+3242(as)X
+3342(as)X
+3442(Extended)X
+3778(Regular)X
+976 5211(Expressions,)N
+1403(\(i.e.)X
+2 f
+1548(egrep)X
+1 f
+1731(\(1\))X
+1845(style)X
+2016(Regular)X
+2290(Expressions\).)X
+3 f
+776 5391(Word)N
+996(search)X
+1 f
+976 5481(The)N
+3 f
+1121(<control-A>)X
+1 f
+1563(command)X
+1899(searches)X
+2192(for)X
+2306(the)X
+2424(word)X
+2609 0.4028(referenced)AX
+2970(by)X
+3070(the)X
+3188(cursor.)X
+3 f
+776 5661(Number)N
+1081(increment)X
+1 f
+976 5751(The)N
+3 f
+1121(#)X
+1 f
+1181(command)X
+1517(increments)X
+1889(or)X
+1976(decrements)X
+2362(the)X
+2480(number)X
+2745 0.4028(referenced)AX
+3106(by)X
+3206(the)X
+3324(cursor.)X
+
+54 p
+%%Page: 54 53
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-54)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+776 762(Previous)N
+1094(\256le)X
+1 f
+976 852(The)N
+3 f
+1121(previous)X
+1 f
+1434(command)X
+1770(edits)X
+1941(the)X
+2059(previous)X
+2355(\256le)X
+2477(from)X
+2653(the)X
+2771(argument)X
+3094(list.)X
+3 f
+776 1032(Left-right)N
+1135(scrolling)X
+1 f
+976 1122(The)N
+3 f
+1130(leftright)X
+1 f
+1440(option)X
+1673(causes)X
+3 f
+1912(nvi)X
+1 f
+2047(to)X
+2138(do)X
+2248(left-right)X
+2563(screen)X
+2799(scrolling,)X
+3129(instead)X
+3386(of)X
+3483(the)X
+3611(traditional)X
+3 f
+3970(vi)X
+1 f
+976 1212(line)N
+1116(wrapping.)X
+
+55 p
+%%Page: 55 54
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(Nvi/Nex)N
+872 0.3906(Reference)AX
+3658(USD:13-55)X
+776 762(17.)N
+916(Index)X
+1 f
+776 885(.)N
+1549(18)X
+776 975(!)N
+1429(15,)X
+1549(34)X
+776 1065("")N
+1549(34)X
+776 1155(#)N
+1429(16,)X
+1549(35)X
+776 1245($)N
+1549(16)X
+776 1335(%)N
+1549(16)X
+776 1425(&)N
+1429(17,)X
+1549(42)X
+776 1515(\()N
+1549(17)X
+776 1605(\))N
+1549(17)X
+776 1695(*)N
+1549(35)X
+776 1785(+)N
+1549(13)X
+776 1875(,)N
+1549(18)X
+776 1965(/RE/)N
+1549(18)X
+776 2055(0)N
+1549(19)X
+776 2145(0<control-D>)N
+1549(31)X
+776 2235(:)N
+1549(19)X
+776 2325(;)N
+1549(19)X
+776 2415(<)N
+1429(20,)X
+1549(35)X
+776 2505(<control-A>)N
+1549(11)X
+776 2595(<control-B>)N
+1549(11)X
+776 2685(<control-D>)N
+1429(12,)X
+1549(31)X
+776 2775(<control-E>)N
+1549(12)X
+776 2865(<control-F>)N
+1549(12)X
+776 2955(<control-G>)N
+1549(12)X
+776 3045(<control-H>)N
+1429(12,)X
+1549(31)X
+776 3135(<control-J>)N
+1549(13)X
+776 3225(<control-L>)N
+1549(13)X
+776 3315(<control-M>)N
+1549(13)X
+776 3405(<control-N>)N
+1549(13)X
+776 3495(<control-P>)N
+1549(13)X
+776 3585(<control-R>)N
+1549(13)X
+776 3675(<control-T>)N
+1429(14,)X
+1549(31)X
+776 3765(<control-U>)N
+1549(14)X
+776 3855(<control-W>)N
+1429(14,)X
+1549(31)X
+776 3945(<control-X>)N
+1549(31)X
+776 4035(<control-Y>)N
+1549(14)X
+776 4125(<control-Z>)N
+1429(14,)X
+1549(43)X
+776 4215(<control-]>)N
+1549(15)X
+776 4305(<control-\303>)N
+1549(15)X
+776 4395(<end-of-\256le>)N
+1549(34)X
+776 4485(<eof>)N
+1549(33)X
+776 4575(<erase>)N
+1549(31)X
+776 4665(<escape>)N
+1429(14,)X
+1549(31)X
+776 4755(<interrupt>)N
+1349(7,)X
+1429(30,)X
+1549(31)X
+776 4845(<line)N
+961(erase>)X
+1549(31)X
+776 4935(<literal)N
+1028(next>)X
+1469(7,)X
+1549(31)X
+776 5025(<nul>)N
+1549(30)X
+776 5115(<space>)N
+1549(15)X
+776 5205(<word)N
+1006(erase>)X
+1549(31)X
+776 5295(=)N
+1549(35)X
+776 5385(>)N
+1429(20,)X
+1549(35)X
+776 5475(?RE?)N
+1549(18)X
+776 5565(@)N
+1429(20,)X
+1549(35)X
+776 5655(A)N
+1549(20)X
+776 5745(B)N
+1549(20)X
+2077 885(C)N
+2850(21)X
+2077 975(D)N
+2850(21)X
+2077 1065(E)N
+2850(21)X
+2077 1155(F)N
+2850(21)X
+2077 1245(G)N
+2850(21)X
+2077 1335(H)N
+2850(21)X
+2077 1425(I)N
+2850(22)X
+2077 1515(J)N
+2850(22)X
+2077 1605(L)N
+2850(22)X
+2077 1695(M)N
+2850(22)X
+2077 1785(N)N
+2850(18)X
+2077 1875(O)N
+2850(22)X
+2077 1965(P)N
+2850(23)X
+2077 2055(Q)N
+2850(23)X
+2077 2145(R)N
+2850(23)X
+2077 2235(S)N
+2850(23)X
+2077 2325(T)N
+2850(23)X
+2077 2415(U)N
+2850(23)X
+2077 2505(W)N
+2850(24)X
+2077 2595(X)N
+2850(24)X
+2077 2685(Y)N
+2850(24)X
+2077 2775(ZZ)N
+2850(24)X
+2077 2865([[)N
+2850(24)X
+9 f
+2077 2955(-)N
+1 f
+2850(18)X
+2077 3045(]])N
+2850(25)X
+2077 3135(\303)N
+2850(25)X
+2077 3225(\303<control-D>)N
+2850(31)X
+2077 3315(_)N
+2850(25)X
+2077 3405 0.2841(`<character>)AN
+2850(17)X
+2077 3495(a)N
+2850(25)X
+2077 3585(abbrev)N
+2850(35)X
+2077 3675(alternate)N
+2374(pathname)X
+2890(8)X
+2077 3765(altwerase)N
+2850(46)X
+2077 3855(append)N
+2850(36)X
+2077 3945(args)N
+2850(36)X
+2077 4035(autoindent)N
+2850(46)X
+2077 4125(autoprint)N
+2850(46)X
+2077 4215(autowrite)N
+2850(47)X
+2077 4305(b)N
+2850(25)X
+2077 4395(beautify)N
+2850(47)X
+2077 4485(bg)N
+2850(36)X
+2077 4575(bigword)N
+2850(10)X
+2077 4665(buffer)N
+2890(8)X
+2077 4755(c)N
+2850(26)X
+2077 4845(cd)N
+2850(36)X
+2077 4935(cdpath)N
+2850(47)X
+2077 5025(change)N
+2850(36)X
+2077 5115(chdir)N
+2850(36)X
+2077 5205(columns)N
+2850(47)X
+2077 5295(comment)N
+2850(47)X
+2077 5385(copy)N
+2850(36)X
+2077 5475(count)N
+2730(10,)X
+2850(33)X
+2077 5565(current)N
+2325(pathname)X
+2890(8)X
+2077 5655(d)N
+2850(26)X
+2077 5745(delete)N
+2850(37)X
+3378 885(directory)N
+4151(47)X
+3378 975(display)N
+4151(37)X
+3378 1065(e)N
+4151(26)X
+3378 1155(edcompatible)N
+4151(47)X
+3378 1245(edit)N
+4151(37)X
+3378 1335(errorbells)N
+4151(47)X
+3378 1425(exrc)N
+4151(47)X
+3378 1515(extended)N
+4151(48)X
+3378 1605(exusage)N
+4151(37)X
+3378 1695(f)N
+4151(26)X
+3378 1785(fg)N
+4151(37)X
+3378 1875(\256le)N
+4031(33,)X
+4151(37)X
+3378 1965(\257ags)N
+4151(33)X
+3378 2055(\257ash)N
+4151(48)X
+3378 2145(global)N
+4151(38)X
+3378 2235(hardtabs)N
+4151(48)X
+3378 2325(help)N
+4151(38)X
+3378 2415(i)N
+4151(26)X
+3378 2505(ignorecase)N
+4151(48)X
+3378 2595(insert)N
+4151(38)X
+3378 2685(j)N
+4151(13)X
+3378 2775(join)N
+4151(38)X
+3378 2865(k)N
+4031(13,)X
+4151(39)X
+3378 2955(keytime)N
+4151(48)X
+3378 3045(l)N
+4151(15)X
+3378 3135(leftright)N
+4151(48)X
+3378 3225(line)N
+4151(33)X
+3378 3315(lines)N
+4151(48)X
+3378 3405(lisp)N
+4151(48)X
+3378 3495(list)N
+4031(39,)X
+4151(48)X
+3378 3585(m)N
+4151(27)X
+3378 3675(magic)N
+4151(48)X
+3378 3765(map)N
+4151(39)X
+3378 3855(mark)N
+4151(39)X
+3378 3945(matchtime)N
+4151(48)X
+3378 4035(mesg)N
+4151(48)X
+3378 4125(mkexrc)N
+4151(39)X
+3378 4215(modelines)N
+4151(49)X
+3378 4305(motion)N
+4151(10)X
+3378 4395(move)N
+4151(39)X
+3378 4485(n)N
+4151(18)X
+3378 4575(next)N
+4151(40)X
+3378 4665(number)N
+4031(35,)X
+4151(49)X
+3378 4755(o)N
+4151(27)X
+3378 4845(octal)N
+4151(49)X
+3378 4935(open)N
+4031(40,)X
+4151(49)X
+3378 5025(optimize)N
+4151(49)X
+3378 5115(p)N
+4151(27)X
+3378 5205(paragraph)N
+4151(11)X
+3378 5295(paragraphs)N
+4151(49)X
+3378 5385(preserve)N
+4151(40)X
+3378 5475(previous)N
+4151(40)X
+3378 5565(previous)N
+3674(context)X
+4191(9)X
+3378 5655(print)N
+4151(40)X
+3378 5745(prompt)N
+4151(49)X
+
+56 p
+%%Page: 56 55
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-56)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+1 f
+776 762(put)N
+1549(40)X
+776 852(quit)N
+1549(41)X
+776 942(r)N
+1549(27)X
+776 1032(range)N
+1549(33)X
+776 1122(read)N
+1549(41)X
+776 1212(readonly)N
+1549(49)X
+776 1302(recdir)N
+1549(49)X
+776 1392(recover)N
+1549(41)X
+776 1482(redraw)N
+1549(50)X
+776 1572(remap)N
+1549(50)X
+776 1662(report)N
+1549(50)X
+776 1752(resize)N
+1549(41)X
+776 1842(rewind)N
+1549(41)X
+776 1932(ruler)N
+1549(50)X
+776 2022(s)N
+1549(27)X
+776 2112(scroll)N
+1549(50)X
+776 2202(section)N
+1549(11)X
+776 2292(sections)N
+1549(50)X
+776 2382(sentence)N
+1549(11)X
+776 2472(set)N
+1549(41)X
+776 2562(shell)N
+1429(42,)X
+1549(50)X
+776 2652(shiftwidth)N
+1549(50)X
+776 2742(showdirty)N
+1549(51)X
+776 2832(showmatch)N
+1549(51)X
+776 2922(showmode)N
+1549(51)X
+776 3012(sidescroll)N
+1549(51)X
+776 3102(slowopen)N
+1549(51)X
+776 3192(source)N
+1549(42)X
+776 3282(sourceany)N
+1549(51)X
+776 3372(split)N
+1549(42)X
+776 3462(stop)N
+1549(43)X
+776 3552(substitute)N
+1549(42)X
+776 3642(suspend)N
+1549(43)X
+776 3732(t)N
+1429(27,)X
+1549(36)X
+776 3822(tabstop)N
+1549(51)X
+776 3912(tag)N
+1549(43)X
+776 4002(taglength)N
+1549(51)X
+776 4092(tagpop)N
+1549(43)X
+776 4182(tags)N
+1549(51)X
+776 4272(tagtop)N
+1549(43)X
+776 4362(term)N
+1549(51)X
+776 4452(terse)N
+1549(51)X
+776 4542(tildeop)N
+1549(51)X
+776 4632(timeout)N
+1549(51)X
+776 4722(ttywerase)N
+1549(52)X
+776 4812(u)N
+1549(28)X
+776 4902(unabbrev)N
+1549(44)X
+776 4992(undo)N
+1549(44)X
+776 5082(unmap)N
+1549(44)X
+776 5172(unnamed)N
+1090(buffer)X
+1589(8)X
+776 5262(v)N
+1549(38)X
+776 5352(verbose)N
+1549(52)X
+776 5442(version)N
+1549(44)X
+776 5532(visual)N
+1549(44)X
+776 5622(viusage)N
+1549(44)X
+776 5712(w)N
+1549(28)X
+776 5802(w1200)N
+1549(52)X
+2077 762(w300)N
+2850(52)X
+2077 852(w9600)N
+2850(52)X
+2077 942(warn)N
+2850(52)X
+2077 1032(window)N
+2850(52)X
+2077 1122(wn)N
+2850(44)X
+2077 1212(word)N
+2850(10)X
+2077 1302(wq)N
+2850(44)X
+2077 1392(wrapmargin)N
+2850(52)X
+2077 1482(wrapscan)N
+2850(52)X
+2077 1572(write)N
+2850(44)X
+2077 1662(writeany)N
+2850(53)X
+2077 1752(x)N
+2850(28)X
+2077 1842(xit)N
+2850(45)X
+2077 1932(y)N
+2850(28)X
+2077 2022(yank)N
+2850(45)X
+2077 2112(z)N
+2730(28,)X
+2850(45)X
+2077 2202({)N
+2850(29)X
+2077 2292(|)N
+2850(29)X
+2077 2382(})N
+2850(29)X
+2077 2472(\304)N
+2610(29,)X
+2730(30,)X
+2850(42)X
+
+2 p
+%%Page: 2 56
+10 s 10 xH 0 xS 1 f 1 i
+3 f
+576 474(USD:13-2)N
+3391(Nvi/Nex)X
+3687 0.3906(Reference)AX
+12 s
+2039 762(Table)N
+2298(of)X
+2402(Contents)X
+1 f
+10 s
+776 961(Description)N
+1184(................................................................................................................................)X
+3992(3)X
+776 1070(Startup)N
+1027(Information)X
+1444(...................................................................................................................)X
+3992(3)X
+776 1179(Recovery)N
+1104(....................................................................................................................................)X
+3992(3)X
+776 1288(Sizing)N
+1000(the)X
+1118(Screen)X
+1364(.......................................................................................................................)X
+3992(5)X
+776 1397(Character)N
+1109(Display)X
+1384(......................................................................................................................)X
+3992(5)X
+776 1506(Multiple)N
+1071(Screens)X
+1344(........................................................................................................................)X
+3992(6)X
+776 1615(Regular)N
+1050(Expressions)X
+1457(and)X
+1593(Replacement)X
+2032(Strings)X
+2284(.........................................................................)X
+3992(6)X
+776 1724(General)N
+1051(Editor)X
+1271(Description)X
+1684(.......................................................................................................)X
+3992(7)X
+776 1833(Vi)N
+876(Description)X
+1284(...........................................................................................................................)X
+3992(8)X
+776 1942(Vi)N
+876(Commands)X
+1264(............................................................................................................................)X
+3952(11)X
+776 2051(Vi)N
+876(Text)X
+1043(Input)X
+1232(Commands)X
+1624(..........................................................................................................)X
+3952(30)X
+776 2160(Ex)N
+885(Addressing)X
+1284(...........................................................................................................................)X
+3952(31)X
+776 2269(Ex)N
+885(Description)X
+1284(...........................................................................................................................)X
+3952(33)X
+776 2378(Ex)N
+885(Commands)X
+1284(...........................................................................................................................)X
+3952(34)X
+776 2487(Set)N
+898(Options)X
+1184(................................................................................................................................)X
+3952(46)X
+776 2596(Additional)N
+1138(Features)X
+1430(in)X
+1512(Nex/Nvi)X
+1824(................................................................................................)X
+3952(53)X
+776 2705(Index)N
+984(..........................................................................................................................................)X
+3952(55)X
+
+56 p
+%%Trailer
+xt
+
+xs
diff --git a/usr.bin/vi/USD.doc/vi.ref/ref.so b/usr.bin/vi/USD.doc/vi.ref/ref.so
new file mode 100644
index 0000000..c0fddc3
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/ref.so
@@ -0,0 +1,127 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ref.so 8.6 (Berkeley) 7/15/94
+.\"
+.\"
+.\" indented paragraph, with spaces between the items, bold font
+.de IP
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.sp 1
+.nr PS \\n(ps
+.nr ps 0
+.ip "\fB\\$1\fP" \\$2
+.nr ps \\n(PS
+.br
+..
+.\" indented paragraph, no spaces between the items, bold font
+.de Ip
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.nr PS \\n(ps
+.nr ps 0
+.ns
+.ip "\fB\\$1\fP" \\$2
+.nr ps \\n(PS
+.br
+..
+.\" start nested .IP
+.de SS
+.sp
+.ba +5n
+..
+.\" end nested .IP
+.de SE
+.ba -5n
+..
+.\" nested .IP, no spaces, normal font
+.de SP
+.\".tm arg 1 \\$1 arg 2 \\$2 arg 3 \\$3
+.nr PS \\n(ps
+.nr ps 0
+.ns
+.ip "\\$1" 9n
+.nr ps \\n(PS
+..
+.\" typewriter font
+.de LI
+\&\fC\\$1\fP\\$2
+..
+.\" ex/vi names in command font
+.de EV
+\&\fB\\$1\fP/\fB\\$2\fP\\$3
+..
+.\" command names
+.de CO
+\&\fB\\$1\fP\\$2
+..
+.\" key words for index
+.de KY
+.sy echo >>index '\\$1 \\n%'
+..
+.\" option names
+.de OP
+\&\fB\\$1\fP\\$2
+..
+.\" paren quoted (typewriter font)
+.de PQ
+(\*(lq\fC\\$1\fP\*(rq)\\$2
+..
+.\" quoted bold
+.de QB
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted command
+.de QC
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted option
+.de QO
+\*(lq\fB\\$1\fP\*(rq\\$2
+..
+.\" quoted (no font change)
+.de QQ
+\*(lq\\$1\*(rq\\$2
+..
+.\" quoted (typewriter font)
+.de QT
+\*(lq\fC\\$1\fP\*(rq\\$2
+..
+.\" section macro to build TOC
+.de SH
+.(x
+\\$2
+.)x
+.sh \\$1 "\\$2"
+..
+.\" manual section
+.de XR
+\&\fI\\$1\fP(\\$2)\\$3
+..
diff --git a/usr.bin/vi/USD.doc/vi.ref/set.opt.roff b/usr.bin/vi/USD.doc/vi.ref/set.opt.roff
new file mode 100644
index 0000000..d59599d
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/set.opt.roff
@@ -0,0 +1,949 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)set.opt.roff 8.26 (Berkeley) 8/11/94
+.\"
+.SH 1 "Set Options"
+.pp
+There are a large number of options that may be set (or unset) to
+change the editor's behavior.
+This section describes the options, their abbreviations and their
+default values.
+.pp
+In each entry below, the first part of the tag line is the full name
+of the option, followed by any equivalent abbreviations.
+(Regardless of the abbreviations, it is only necessary to use the
+minimum number of characters necessary to distinguish an abbreviation
+from all other commands for it to be accepted, in
+.EV nex nvi .
+Historically, only the full name and the official abbreviations
+were accepted by
+.EV ex vi .
+Using full names in your startup files and environmental variables will
+probably make them more portable.)
+The part in square brackets is the default value of the option.
+Most of the options are boolean, i.e. they are either on or off,
+and do not have an associated value.
+.pp
+Options apply to both
+.CO ex
+and
+.CO vi
+modes, unless otherwise specified.
+.pp
+For information on modifying the options or to display the options and
+their current values, see the
+.QQ set
+command in the section entitled
+.QB "Ex Commands" .
+.KY altwerase
+.IP "altwerase [off]"
+.CO Vi
+only.
+Change how
+.CO vi
+does word erase during text input.
+When this option is set, text is broken up into three classes:
+alphabetic, numeric and underscore characters, other nonblank
+characters, and blank characters.
+Changing from one class to another marks the end of a word.
+In addition, the class of the first character erased is ignored
+(which is exactly what you want when erasing pathname components).
+.KY autoindent
+.IP "autoindent, ai [off]"
+If this option is set, whenever you create a new line (using the
+.CO vi
+.CO A ,
+.CO a ,
+.CO C ,
+.CO c ,
+.CO I ,
+.CO i ,
+.CO O ,
+.CO o ,
+.CO R ,
+.CO r ,
+.CO S ,
+and
+.CO s
+commands, or the
+.CO ex
+.CO append ,
+.CO change ,
+and
+.CO insert
+commands) the new line is automatically indented to align the cursor with
+the first nonblank character of the line from which you created it.
+Lines are indented using tab characters to the extent possible (based on
+the value of the
+.OP tabstop
+option) and then using space characters as necessary.
+For commands inserting text into the middle of a line, any blank characters
+to the right of the cursor are discarded, and the first nonblank character
+to the right of the cursor is aligned as described above.
+.sp
+The indent characters are themselves somewhat special.
+If you do not enter more characters on the new line before moving to
+another line, or entering
+.LI <escape> ,
+the indent character will be deleted and the line will be empty.
+For example, if you enter
+.LI <carriage-return>
+twice in succession,
+the line created by the first
+.LI <carriage-return>
+will not have any characters in it,
+regardless of the indentation of the previous or subsequent line.
+.sp
+Indent characters also require that you enter additional erase characters
+to delete them.
+For example,
+if you have an indented line, containing only blanks, the first
+.LI <word-erase>
+character you enter will erase up to end of the indent characters,
+and the second will erase back to the beginning of the line.
+(Historically, only the
+.CO <control-D>
+key would erase the indent characters.
+Both the
+.CO <control-D>
+key and the usual erase keys work in
+.CO nvi .)
+In addition, if the cursor is positioned at the end of the indent
+characters, the keys
+.QT 0<control-D>
+will erase all of the indent characters for the current line,
+resetting the indentation level to 0.
+Similarly, the keys
+.QT ^<control-D>
+will erase all of the indent characters for the current line,
+leaving the indentation level for future created lines unaffected.
+.sp
+Finally, if the
+.OP autoindent
+option is set, the
+.CO S
+and
+.CO cc
+commands change from the first nonblank of the line to the end of the
+line, instead of from the beginning of the line to the end of the line.
+.KY autoprint
+.IP "autoprint, ap [off]"
+.CO Ex
+only.
+Cause the current line to be automatically displayed after the
+.CO ex
+commands
+.CO < ,
+.CO > ,
+.CO copy ,
+.CO delete ,
+.CO join ,
+.CO move ,
+.CO put ,
+.CO t ,
+.CO Undo ,
+and
+.CO undo .
+This automatic display is suppressed during
+.CO global
+and
+.CO vglobal
+commands, and for any command where optional flags are used to explicitly
+display the line.
+.KY autowrite
+.IP "autowrite, aw [off]"
+If this option is set, the
+.CO vi
+.CO ! ,
+.CO ^^ ,
+.CO ^]
+and
+.CO <control-Z>
+commands, and the
+.CO ex
+.CO edit ,
+.CO next ,
+.CO rewind ,
+.CO stop ,
+.CO suspend ,
+.CO tag ,
+.CO tagpop ,
+and
+.CO tagtop
+commands automatically write the current file back to the current file name
+if it has been modified since it was last written.
+If the write fails, the command fails and goes no further.
+.sp
+Appending the optional force flag character
+.QT !
+to the
+.CO ex
+commands
+.CO next ,
+.CO rewind ,
+.CO stop ,
+.CO suspend ,
+.CO tag ,
+.CO tagpop ,
+and
+.CO tagtop
+stops the automatic write from being attempted.
+.sp
+(Historically, the
+.CO next
+command ignored the optional force flag.)
+Note, the
+.CO ex
+commands
+.CO edit ,
+.CO quit ,
+.CO shell ,
+and
+.CO xit
+are
+.i not
+affected by the
+.OP autowrite
+option.
+.KY beautify
+.IP "beautify, bf [off]"
+If this option is set, all control characters that are not currently being
+specially interpreted, other than
+.LI <tab> ,
+.LI <newline> ,
+and
+.LI <form-feed> ,
+are
+discarded from commands read in by
+.CO ex
+from command files, and from input text entered to
+.CO vi
+(either into the file or to the colon command line).
+Text files read by
+.EV ex vi
+are
+.i not
+affected by the
+.OP beautify
+option.
+.KY cdpath
+.IP "cdpath [environment variable CDPATH, or current directory]"
+This option is used to specify a colon separated list of directories
+which are used as path prefixes for any relative path names used as
+arguments for the
+.CO cd
+command.
+The value of this option defaults to the value of the environmental
+variable
+.LI CDPATH
+if it is set, otherwise to the current directory.
+For compatibility with the POSIX 1003.2 shell, the
+.CO cd
+command does
+.i not
+check the current directory as a path prefix for relative path names
+unless it is explicitly specified.
+It may be so specified by entering an empty string or a
+.QT \&.
+character into the
+.LI CDPATH
+variable or the option value.
+.KY columns
+.IP "columns, co [80]"
+The number of columns in the screen.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI COLUMNS .
+See the section entitled
+.QB "Sizing the Screen"
+more information.
+.KY comment
+.IP "comment [off]"
+.CO Vi
+only.
+If the first non-empty line of the file begins with the string
+.QT /\&* ,
+this option causes
+.CO vi
+to skip to the end of that C-language comment (probably a terribly boring
+legal notice) before displaying the file.
+.KY directory
+.IP "directory, dir [environment variable TMPDIR, or /tmp]"
+The directory where temporary files are created.
+The environmental variable
+.LI TMPDIR
+is used as the default value if it exists, otherwise
+.LI /tmp
+is used.
+.KY edcompatible
+.IP "edcompatible, ed [off]"
+Remember the values of the
+.QQ c
+and
+.QQ g
+suffices to the
+.CO substitute
+commands, instead of initializing them as unset for each new
+command.
+Specifying pattern and replacement strings to the
+.CO substitute
+command unsets the
+.QQ c
+and
+.QQ g
+suffices as well.
+.KY errorbells
+.IP "errorbells, eb [off]"
+.CO Ex
+only.
+.CO Ex
+error messages are normally presented in inverse video.
+If that is not possible for the terminal, setting this option causes
+error messages to be announced by ringing the terminal bell.
+.KY exrc
+.IP "exrc, ex [off]"
+If this option is turned off in the system or $HOME startup files,
+the local startup files are never read (unless they are the same
+as the system or $HOME startup files).
+Turning it on has no effect, i.e. the normal checks for local startup
+files are performed, regardless.
+See the section entitled
+.QB "Startup Information"
+for more information.
+.KY extended
+.IP "extended [off]"
+This option causes all regular expressions to be treated as POSIX
+1003.2 Extended Regular Expressions (which are similar to historic
+.XR egrep 1
+style expressions).
+.KY flash
+.IP "flash [on]"
+This option causes the screen to flash instead of beeping the keyboard,
+on error, if the terminal has the capability.
+.KY hardtabs
+.IP "hardtabs, ht [8]"
+This option defines the spacing between hardware tab settings, i.e.
+the tab expansion done by the operating system and/or the terminal
+itself.
+As
+.EV nex nvi
+never writes
+.LI <tab>
+characters to the terminal, unlike historic versions of
+.EV ex vi ,
+this option does not currently have any affect.
+.KY ignorecase
+.IP "ignorecase, ic [off]"
+This option causes regular expressions, both in
+.CO ex
+commands and in searches,
+to be evaluated in a case-insensitive manner.
+.KY keytime
+.IP "keytime [6]"
+The 10th's of a second
+.EV ex vi
+waits for a subsequent key to complete a key mapping.
+.KY leftright
+.IP "leftright [off]"
+.CO Vi
+only.
+This option causes the screen to be scrolled left-right to view
+lines longer than the screen, instead of the traditional
+.CO vi
+screen interface which folds long lines at the right-hand margin
+of the terminal.
+.KY lines
+.IP "lines, li [24]"
+.CO Vi
+only.
+The number of lines in the screen.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI LINES .
+See the section entitled
+.QB "Sizing the Screen"
+for more information.
+.KY lisp
+.IP "lisp [off]"
+.CO Vi
+only.
+This option changes the behavior of the
+.CO vi
+.CO ( ,
+.CO ) ,
+.CO { ,
+.CO } ,
+.CO [[
+and
+.CO ]]
+commands to match the Lisp language.
+Also, the
+.OP autoindent
+option's behavior is changed to be appropriate for Lisp.
+.sp
+.i "This option is not yet implemented."
+.KY list
+.IP "list [off]"
+This option causes lines to be displayed in an unambiguous fashion.
+Specifically, tabs are displayed as control characters, i.e.
+.QT ^I ,
+and the ends of lines are marked with a
+.QT $
+character.
+.KY magic
+.IP "magic [on]"
+This option is on by default.
+Turning the
+.OP magic
+option off causes all regular expression characters except for
+.QT ^
+and
+.QT $ ,
+to be treated as ordinary characters.
+To re-enable characters individually, when the
+.OP magic
+option is off,
+precede them with a backslash
+.QT \e
+character.
+See the section entitled
+.QB "Regular Expressions and Replacement Strings"
+for more information.
+.KY matchtime
+.IP "matchtime [7]"
+.CO Vi
+only.
+The 10th's of a second
+.EV ex vi
+pauses on the matching character when the
+.OP showmatch
+option is set.
+.KY mesg
+.IP "mesg [on]"
+This option allows other users to contact you using the
+.XR talk 1
+and
+.XR write 1
+utilities, while you are editing.
+.EV Ex vi
+does not turn message on, i.e. if messages were turned off when the
+editor was invoked, they will stay turned off.
+This option only permits you to disallow messages for the edit session.
+See the
+.XR mesg 1
+utility for more information.
+.KY modelines
+.IP "modelines, modeline [off]"
+If the
+.OP modelines
+option is set,
+.EV ex vi
+has historically scanned the first and last five lines of each file as
+it is read for editing, looking for any
+.CO ex
+commands that have been placed in those lines.
+After the startup information has been processed, and before the user
+starts editing the file, any commands embedded in the file are executed.
+.sp
+Commands were recognized by the letters
+.QQ e
+or
+.QQ v
+followed by
+.QQ x
+or
+.QQ i ,
+at the beginning of a line or following a tab or space character,
+and followed by a
+.QQ : ,
+an
+.CO ex
+command, and another
+.QQ : .
+.sp
+This option is a security problem of immense proportions,
+and should not be used under any circumstances.
+.sp
+.i "This option will never be implemented."
+.KY number
+.IP "number, nu [off]"
+Precede each line displayed with its current line number.
+.KY octal
+.IP "octal [off]"
+Display unknown characters as octal numbers, instead of the default
+hexadecimal.
+.KY open
+.IP "open [on]"
+.CO Ex
+only.
+If this option is not set, the
+.CO open
+and
+.CO visual
+commands are disallowed.
+.KY optimize
+.IP "optimize, opt [on]"
+.CO Vi
+only.
+Throughput of text is expedited by setting the terminal not to do automatic
+carriage returns when printing more than one (logical) line of output,
+greatly speeding output on terminals without addressable cursors when text
+with leading white space is printed.
+.sp
+.i "This option is not yet implemented."
+.KY paragraphs
+.IP "paragraphs, para [IPLPPPQPP LIpplpipbp]"
+.CO Vi
+only.
+Define additional paragraph boundaries for the
+.CO {
+and
+.CO }
+commands.
+The value of this option must be a character string consisting
+of zero or more character pairs.
+.sp
+In the text to be edited, the character string
+.LI "<newline>.<char-pair>" ,
+(where
+.LI <char-pair>
+is one of the character pairs in the option's value)
+defines a paragraph boundary.
+For example, if the option were set to
+.LI "LaA<space>##" ,
+then all of the following additional paragraph boundaries would be
+recognized:
+.sp
+.(l
+<newline>.La
+<newline>.A<space>
+<newline>.##
+.)l
+.KY prompt
+.IP "prompt [on]"
+.CO Ex
+only.
+This option causes
+.CO ex
+to prompt for command input with a
+.QT :
+character; when it is not set, no prompt is displayed.
+.KY readonly
+.IP "readonly, ro [off]"
+This option causes a force flag to be required to attempt to write
+the file back to the original file name.
+Setting this option is equivalent to using the
+.b \-R
+command line option, or editing a file which lacks write permission.
+.KY recdir
+.IP "recdir [/var/tmp/vi.recover]"
+The directory where recovery files are stored.
+.sp
+If you change the value of
+.CO recdir ,
+be careful to choose a directory whose contents are not regularly
+deleted.
+Bad choices include directories in memory based filesystems,
+or
+.LI /tmp ,
+on most systems,
+as their contents are removed when the machine is rebooted.
+.sp
+Public directories like
+.LI /usr/tmp
+and
+.LI /var/tmp
+are usually safe, although some sites periodically prune old files
+from them.
+There is no requirement that you use a public directory,
+e.g. a sub-directory of your home directory will work fine.
+.sp
+Finally, if you change the value of
+.CO recdir ,
+you must modify the recovery script to operate in your chosen recovery
+area.
+.sp
+See the section entitled
+.QB "Recovery"
+for further information.
+.KY redraw
+.IP "redraw, re [off]"
+.CO Vi
+only.
+The editor simulates (using great amounts of output), an intelligent
+terminal on a dumb terminal (e.g. during insertions in
+.CO vi
+the characters to the right of the cursor are refreshed as each input
+character is typed).
+.sp
+.i "This option is not yet implemented."
+.KY remap
+.IP "remap [on]"
+If this option is set,
+it is possible to define macros in terms of other macros.
+Otherwise, each key is only remapped up to one time.
+For example, if
+.QT A
+is mapped to
+.QT B ,
+and
+.QT B
+is mapped to
+.QT C ,
+The keystroke
+.QT A
+will be mapped to
+.QT C
+if the
+.OP remap
+option is set, and to
+.QT B
+if it is not set.
+.KY report
+.IP "report [5]"
+Set the threshold of the number of lines that need to be changed or
+yanked before a message will be displayed to the user.
+For everything but the yank command, the value is the largest value
+about which the editor is silent, i.e. by default, 6 lines must be
+deleted before the user is notified.
+However, if the number of lines yanked is greater than
+.i "or equal to"
+the set value, it is reported to the user.
+.KY ruler
+.IP "ruler [off]"
+.CO Vi
+only.
+Display a row/column ruler on the colon command line.
+.KY scroll
+.IP "scroll, scr [window / 2]"
+Set the number of lines scrolled by the
+.CO vi
+.CO <control-D>
+and
+.CO <control-U>
+commands.
+.sp
+Historically, the
+.CO ex
+.CO z
+command, when specified without a count, used two times the size of the
+scroll value; the POSIX 1003.2 standard specified the window size, which
+is a better choice.
+.KY sections
+.IP "sections, sect [NHSHH HUnhsh]"
+.CO Vi
+only.
+Define additional section boundaries for the
+.CO [[
+and
+.CO ]]
+commands.
+The
+.OP sections
+option should be set to a character string consisting of zero or
+more character pairs.
+In the text to be edited, the character string
+.LI "<newline>.<char-pair>" ,
+(where
+.LI <char-pair>
+is one of the character pairs in the option's value),
+defines a section boundary in the same manner that
+.OP paragraph
+option boundaries are defined.
+.KY shell
+.IP "shell, sh [environment variable SHELL, or /bin/sh]"
+Select the shell used by the editor.
+The specified path is the pathname of the shell invoked by the
+.CO vi
+.CO !
+shell escape command and by the
+.CO ex
+.CO shell
+command.
+This program is also used to resolve any shell meta-characters in
+.CO ex
+commands.
+.KY shiftwidth
+.IP "shiftwidth, sw [8]"
+Set the autoindent and shift command indentation width.
+This width is used by the
+.OP autoindent
+option and by the
+.CO < ,
+.CO > ,
+and
+.CO shift
+commands.
+.KY showdirty
+.IP "showdirty [off]"
+.CO Vi
+only.
+Display an asterisk on the colon command line if the file has been modified.
+.KY showmatch
+.IP "showmatch, sm [off]"
+.CO Vi
+only.
+This option causes
+.CO vi ,
+when a
+.QT }
+or
+.QT )
+is entered, to briefly move the cursor the matching
+.QT {
+or
+.QT ( .
+See the
+.OP matchtime
+option for more information.
+.KY showmode
+.IP "showmode [off]"
+.CO Vi
+only.
+This option causes
+.CO vi
+to display a string identifying the current editor mode on the
+colon command line.
+.KY sidescroll
+.IP "sidescroll [16]"
+.CO Vi
+only.
+Sets the number of columns that are shifted to the left or right,
+when
+.CO vi
+is doing left-right scrolling and the left or right margin is
+crossed.
+See the
+.OP leftright
+option for more information.
+.KY slowopen
+.IP "slowopen, slow [off]"
+This option affects the display algorithm used by
+.CO vi ,
+holding off display updating during input of new text to improve
+throughput when the terminal in use is slow and unintelligent.
+.sp
+.i "This option is not yet implemented."
+.KY sourceany
+.IP "sourceany [off]"
+If this option is turned on,
+.CO vi
+historically read startup files that were owned by someone other than
+the editor user.
+See the section entitled
+.QB "Startup Information"
+for more information.
+This option is a security problem of immense proportions,
+and should not be used under any circumstances.
+.sp
+.i "This option will never be implemented."
+.KY tabstop
+.IP "tabstop, ts [8]"
+This option sets tab widths for the editor display.
+.KY taglength
+.IP "taglength, tl [0]"
+This option sets the maximum number of characters that are considered
+significant in a tag name.
+Setting the value to 0 makes all of the characters in the tag name
+significant.
+.KY tags
+.IP "tags, tag [tags /var/db/libc.tags /sys/kern/tags]"
+Sets the list of tags files, in search order,
+which are used when the editor searches for a tag.
+.KY term
+.IP "term, ttytype, tty [environment variable TERM]"
+Set the terminal type.
+Setting this option causes
+.EV ex vi
+to set (or reset) the environmental variable
+.LI TERM .
+.KY terse
+.IP "terse [off]"
+This option has historically made editor messages less verbose.
+It has no effect in this implementation.
+See the
+.OP verbose
+option for more information.
+.KY tildeop
+.IP "tildeop"
+Modify the
+.CO ~
+command to take an associated motion.
+.KY timeout
+.IP "timeout, to [on]"
+If this option is set,
+.EV ex vi
+waits for a specific period for a subsequent key to complete a key
+mapping (see the
+.OP keytime
+option).
+If the option is not set, the editor waits until enough keys are
+entered to resolve the ambiguity, regardless of how long it takes.
+.KY ttywerase
+.IP "ttywerase [off]"
+.CO Vi
+only.
+This option changes how
+.CO vi
+does word erase during text input.
+If this option is set, text is broken up into two classes,
+blank characters and nonblank characters.
+Changing from one class to another marks the end of a word.
+.KY verbose
+.IP "verbose [off]"
+.CO Vi
+only.
+.CO Vi
+historically bells the terminal for many obvious mistakes, e.g. trying
+to move past the left-hand margin, or past the end of the file.
+If this option is set, an error message is displayed for all errors.
+.KY w300
+.IP "w300 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is less than 1200 baud.
+See the
+.OP window
+option for more information.
+.KY w1200
+.IP "w1200 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is equal to 1200 baud.
+See the
+.OP window
+option for more information.
+.KY w9600
+.IP "w9600 [no default]"
+.CO Vi
+only.
+Set the window size if the baud rate is greater than 1200 baud.
+See the
+.OP window
+option for more information.
+.KY warn
+.IP "warn [on]"
+.CO Ex
+only.
+This option causes a warning message to the terminal if the file has
+been modified, since it was last written, before a
+.CO !
+command.
+.KY window
+.IP "window, w, wi [environment variable LINES]"
+This option determines the default number of lines in a screenful,
+as written by the
+.CO z
+command.
+It also determines the number of lines scrolled by the
+.CO vi
+commands
+.CO <control-F>
+and
+.CO <control-B> .
+The value of window can be unrelated to the real screen size,
+although it starts out as the number of lines on the screen (see
+the section entitled
+.QB "Sizing the Screen"
+for more information).
+Setting the value of the
+.OP window
+option is the same as using the
+.b \-w
+command line option.
+.sp
+If the value of the
+.OP window
+option (as set by the
+.OP window ,
+.OP w300 ,
+.OP w1200
+or
+.OP w9600
+options) is smaller than the actual size of the screen, large screen
+movements will result in displaying only that smaller number of lines
+on the screen.
+(Further movements in that same area will result in the screen being
+filled.)
+This can provide a performance improvement when viewing different
+places in one or more files over a slow link.
+.KY wrapmargin
+.IP "wrapmargin, wm [0]"
+.CO Vi
+only.
+If the value of the
+.OP wrapmargin
+option is non-zero,
+.CO vi
+will split lines so that they end at least that number of characters
+before the right-hand margin of the screen.
+(Note, the value of
+.OP wrapmargin
+is
+.i not
+a text length.
+In a screen that is 80 columns wide, the command
+.QT ":set wrapmargin=8"
+attempts to keep the lines less than or equal to 72 columns wide.)
+.sp
+Lines are split at the previous whitespace character closest to the
+number.
+Any trailing whitespace characters before that character are deleted.
+If the line is split because of an inserted
+.LI <space>
+or
+.LI <tab>
+character, and you then enter another
+.LI <space>
+character, it is discarded.
+.sp
+If wrapmargin is set to 0,
+or if there is no blank character upon which to split the line,
+the line is not broken.
+.KY wrapscan
+.IP "wrapscan, ws [on]"
+This option causes searches to wrap around the end or the beginning
+of the file, and back to the starting point.
+Otherwise, the end or beginning of the file terminates the search.
+.KY writeany
+.IP "writeany, wa [off]"
+If this option is set, file-overwriting checks that would usually be
+made before the
+.CO write
+and
+.CO xit
+commands, or before an automatic write (see the
+.OP autowrite
+option), are not made.
+This allows a write to any file, provided the file permissions allow it.
diff --git a/usr.bin/vi/USD.doc/vi.ref/spell.ok b/usr.bin/vi/USD.doc/vi.ref/spell.ok
new file mode 100644
index 0000000..60084e3
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/spell.ok
@@ -0,0 +1,270 @@
+Amir
+Autoprint
+BRE's
+Bostic
+Bourne
+DOUBLEQUOTE
+Dq
+Ds
+ERE's
+EXINIT
+Englar
+Ev
+FF
+Fa
+Fl
+HUnhsh
+IPLPPPQPP
+Kirkendall
+Korn
+LIpplpipbp
+LaA
+Li
+Lowercase
+MINUSSIGN
+Makefiles
+NEX
+NEXINIT
+NHSHH
+NVI
+Nex
+Nvi
+OS
+POSIX
+PostScript
+RE's
+README
+RECDIR
+Reference''USD:13
+SIGHUP
+SIGWINCH
+SQUOTE
+Se
+Std
+Std1003.2
+Sy
+TANDARDS
+TIOCGWINSZ
+TMPDIR
+Todo
+USD.doc
+USD:13
+UUNET
+Vx
+Whitespace
+XOFF
+XON
+XOptions
+XXCOLUMNS
+XXXX
+XXXXXX
+XXb
+ZZ
+ab
+abbrev
+ags
+ai
+al
+altwerase
+arg
+args
+autoindent
+autoprint
+autowrite
+aw
+bbrev
+bf
+bigword
+bigwords
+bostic
+brev
+bugs.current
+c2w
+carat
+cdy
+changelog
+chd
+cmd
+count1
+count2
+creens
+cs.berkeley.edu
+db
+dbopen
+def
+di
+dir
+dit
+docs
+eFlRsv
+eFlRv
+ead
+eb
+edcompatible
+elete
+elvis
+email
+enum
+eof
+errorbells
+esc
+ex.cmd.roff
+exrc
+ext
+exu
+exusage
+fi
+filesystem
+filesystems
+ftp.cs.berkeley.edu
+ftp.uu.net
+gdb
+gdb.script
+gs
+gzip'd
+halfbyte
+hange
+hangup
+hardtabs
+ht
+ic
+ifdef
+ignorecase
+ile
+ind
+ious
+ir
+ist
+ize
+keystroke
+keystrokes
+keytime
+leftright
+lhs
+li
+lib
+libc.tags
+lobal
+lowercase
+lp
+matchtime
+mber
+meta
+mk
+mkexrc
+modeful
+modeline
+modelines
+ndo
+nex
+nexrc
+nk
+nomagic
+nooption
+nsert
+nul
+nvi
+nvi.tar.Z
+nvi.tar.z
+nz
+oin
+op
+ove
+para
+pathname
+pathnames
+ppend
+pu
+py
+rc.local
+readonly
+rec
+recdir
+recfile
+recover.XXXX
+recover.XXXXXX
+recover.c
+recover.script
+remapmax
+res
+rew
+rhs
+ript
+rk
+ro
+roff
+rsion
+sc
+scr
+screeen
+se
+set.opt.roff
+shiftwidth
+showmatch
+showmode
+sidescroll
+slowopen
+sm
+sourceany
+sp
+spell.ok
+st
+sual
+svi
+sw
+ta
+tabstop
+taglength
+tagp
+tagpop
+tagstring
+tagt
+tagtop
+terminfo
+th
+tildeop
+tl
+tmp
+ts
+ttytype
+ttywerase
+uR
+ubstitute
+ucb
+uffers
+uit
+una
+unabbrev
+unm
+uppercase
+urce
+uunet
+var
+ve
+vglobal
+vi.0.ps
+vi.0.txt
+vi.1
+vi.XXXX
+vi.XXXXXX
+vi.cmd.roff
+vi.exrc
+vi.recover
+vibackup
+virecovery
+viu
+viusage
+wa
+whitespace
+wi
+wm
+wn
+wq
+wrapmargin
+wrapscan
+writeany
+ws
+xaw
+xit
+ya
+yy
diff --git a/usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff b/usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff
new file mode 100644
index 0000000..57c0d37
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/vi.cmd.roff
@@ -0,0 +1,2984 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.cmd.roff 8.28 (Berkeley) 7/17/94
+.\"
+.SH 1 "Vi Description"
+.pp
+.CO Vi
+takes up the entire screen to display the edited file,
+except for the bottom line of the screen.
+The bottom line of the screen is used to enter
+.CO ex
+commands, and for
+.CO vi
+error and informational messages.
+If no other information is being displayed,
+the default display can show the current cursor row and cursor column,
+an indication of whether the file has been modified,
+and the current mode of the editor.
+See the
+.OP ruler ,
+.OP showdirty
+and
+.OP showmode
+options for more information.
+.pp
+Empty lines do not have any special representation on the screen,
+but lines on the screen that would logically come after the end of
+the file are displayed as a single tilde
+.PQ ~
+character.
+To differentiate between empty lines and lines consisting of only
+whitespace characters, use the
+.OP list
+option.
+Historically, implementations of
+.CO vi
+have also displayed some lines as single asterisk
+.PQ @
+characters.
+These were lines that were not correctly displayed, i.e. lines on the
+screen that did not correspond to lines in the file, or lines that did
+not fit on the current screen.
+.CO Nvi
+never displays lines in this fashion.
+.pp
+.CO Vi
+is a modeful editor, i.e. it has two modes,
+.QQ command
+mode and
+.QQ "text input"
+mode.
+When
+.CO vi
+first starts, it is in command mode.
+There are several commands that change
+.CO vi
+into text input mode.
+The
+.LI <escape>
+character is used to resolve the text input into the file,
+and exit back into command mode.
+In
+.CO vi
+command mode, the cursor is always positioned on the last column of
+characters which take up more than one column on the screen.
+In
+.CO vi
+text insert mode, the cursor is positioned on the first column of
+characters which take up more than one column on the screen.
+.pp
+Generally, if the cursor line and cursor column are not on the screen,
+then the screen is scrolled (if the target cursor is close) or repainted
+(if the target cursor is far away) so that the cursor is on the screen.
+If the screen is scrolled, it is moved a minimal amount, and the cursor
+line will usually appear at the top or bottom of the screen.
+In the screen is repainted, the cursor line will appear in the center of
+the screen, unless the cursor is sufficiently close to the beginning or
+end of the file that this is not possible.
+If the
+.OP leftright
+option is set, the screen may be scrolled or repainted in a horizontal
+direction as well as in a vertical one.
+.pp
+A major difference between the historical
+.CO vi
+presentation and
+.CO nvi
+is in the scrolling and screen oriented position commands,
+.CO <control-B> ,
+.CO <control-D> ,
+.CO <control-E> ,
+.CO <control-F> ,
+.CO <control-U> ,
+.CO <control-Y> ,
+.CO H ,
+.CO L
+and
+.CO M .
+In historical implementations of
+.CO vi ,
+these commands acted on physical (as opposed to logical, or screen)
+lines.
+For lines that were sufficiently long in relation to the size of the
+screen, this meant that single line scroll commands might repaint the
+entire screen, scrolling or screen positioning command might not change
+the screen or move the cursor at all, and some lines simply could not
+be displayed, even though
+.CO vi
+would edit the file that contained them.
+In
+.CO nvi ,
+these commands act on logical, i.e. screen lines.
+You are unlikely to notice any difference unless you are editing files
+with lines significantly longer than a screen width.
+.pp
+.CO Vi
+keeps track of the currently
+.QQ "most attractive"
+cursor position.
+Each command description (for commands that can change the current
+cursor position),
+specifies if the cursor is set to a specific location in the line,
+or if it is moved to the
+.QQ "most attractive cursor position" .
+The latter means that the cursor is moved to the cursor position
+that is vertically as close as possible to the current cursor
+position.
+If the current line is shorter than the cursor position
+.CO vi
+would select, the cursor is positioned on the last character in the line.
+(If the line is empty, the cursor is positioned on the first column
+of the line.)
+If a command moves the cursor to the most attractive position,
+it does not alter the current cursor position, and a subsequent
+movement will again attempt to move the cursor to that position.
+Therefore, although a movement to a line shorter than the currently
+most attractive position will cause the cursor to move to the end of
+that line, a subsequent movement to a longer line will cause the
+cursor to move back to the most attractive position.
+.pp
+In addition, the
+.CO $
+command makes the end of each line the most attractive cursor position
+rather than a specific column.
+.pp
+Each
+.CO vi
+command described below notes where the cursor ends up after it is
+executed.
+This position is described in terms of characters on the line, i.e.
+.QQ "the previous character" ,
+or,
+.QQ "the last character in the line" .
+This is to avoid needing to continually refer to on what part of the
+character the cursor rests.
+.pp
+The following words have special meaning for
+.CO vi
+commands.
+.KY "previous context"
+.IP "previous context"
+The position of the cursor before the command which caused the
+last absolute movement was executed.
+Each
+.CO vi
+command described in the next section that is considered an
+absolute movement is so noted.
+In addition, specifying
+.i any
+address to an
+.CO ex
+command is considered an absolute movement.
+.KY "motion"
+.IP "motion"
+A second
+.CO vi
+command can be used as an optional trailing argument to the
+.CO vi
+.CO \&! ,
+.CO \&< ,
+.CO \&> ,
+.CO \&c ,
+.CO \&d ,
+.CO \&y ,
+and (depending on the
+.OP tildeop
+option)
+.CO \&~
+commands.
+This command indicates the end of the region of text that's affected by
+the command.
+The motion command may be either the command character repeated (in
+which case it means the current line) or a cursor movement command.
+In the latter case, the region affected by the command is from the
+starting or stopping cursor position which comes first in the file,
+to immediately before the starting or stopping cursor position which
+comes later in the file.
+Commands that operate on lines instead of using beginning and ending
+cursor positions operate on all of the lines that are wholly or
+partially in the region.
+In addition, some other commands become line oriented depending on
+where in the text they are used.
+The command descriptions below note these special cases.
+.sp
+The following commands may all be used as motion components for
+.CO vi
+commands:
+.sp
+.ne 12v
+.ft C
+.TS
+r r r r.
+<control-A> <control-H> <control-J> <control-M>
+<control-N> <control-P> <space> $
+% '<character> ( )
++ , - /
+0 ; ? B
+E F G H
+L M N T
+W [[ ]] ^
+\&_ `<character> b e
+f h j k
+l n t w
+{ | }
+.TE
+.ft R
+.sp
+The optional count prefix available for some of the
+.CO vi
+commands that take motion commands, or the count prefix available
+for the
+.CO vi
+commands that are used as motion components,
+may be included and is
+.i always
+considered part of the motion argument.
+For example, the commands
+.QT c2w
+and
+.QT 2cw
+are equivalent, and the region affected by the
+.CO c
+command is two words of text.
+In addition, if the optional count prefix is specified for both the
+.CO vi
+command and its motion component, the effect is multiplicative and
+is considered part of the motion argument.
+For example, the commands
+.QT 4cw
+and
+.QT 2c2w
+are equivalent, and the region affected by the
+.CO c
+command is four words of text.
+.KY "count"
+.IP "count"
+A positive number used as an optional argument to most commands,
+either to give a size or a position (for display or movement commands),
+or as a repeat count (for commands that modify text).
+The count argument is always optional and defaults to 1 unless otherwise
+noted in the command description.
+.sp
+When a
+.CO vi
+command synopsis shows both a
+.LI [buffer]
+and
+.LI [count] ,
+they may be presented in any order.
+.KY "bigword"
+.IP "bigword"
+A set of non-whitespace characters preceded and followed by whitespace
+characters or the beginning or end of the file or line.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single bigword.
+.KY word
+.IP word
+Generally, in languages where it is applicable,
+.CO vi
+recognizes two kinds of words.
+First, a sequence of letters, digits and underscores, delimited at both
+ends by: characters other than letters, digits, or underscores; the
+beginning or end of a line; the beginning or end of the file.
+Second, a sequence of characters other than letters, digits, underscores,
+or whitespace characters, delimited at both ends by: a letter, digit,
+underscore, or whitespace character;
+the beginning or end of a line; the beginning or end of the file.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single word.
+.KY "paragraph"
+.IP "paragraph"
+An area of text that begins with either the beginning of a file,
+an empty line, or a section boundary, and continues until either
+an empty line, section boundary, or the end of the file.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single paragraph.
+.sp
+Additional paragraph boundaries can be defined using the
+.OP paragraph
+option.
+.KY "section"
+.IP "section"
+An area of text that starts with the beginning of the file or a line
+whose first character is an open brace
+.PQ {
+and continues until the next section or the end of the file.
+.sp
+Additional section boundaries can be defined using the
+.OP sections
+option.
+.KY "sentence"
+.IP "sentence"
+An area of text that begins with either the beginning of the file or the
+first nonblank character following the previous sentence, paragraph, or
+section boundary and continues until the end of the file or a or a period
+.PQ \&.
+exclamation point
+.PQ !
+or question mark
+.PQ ?
+character,
+followed by either an end-of-line or two whitespace characters.
+Any number of closing parentheses
+.PQ ) ,
+brackets
+.PQ ]
+or double-quote
+.PQ """"
+characters can appear between the period, exclamation point,
+or question mark and the whitespace characters or end-of-line.
+.sp
+Groups of empty lines (or lines containing only whitespace characters)
+are treated as a single sentence.
+.SH 1 "Vi Commands"
+.pp
+The following section describes the commands available in the command
+mode of the
+.CO vi
+editor.
+In each entry below, the tag line is a usage synopsis for the command
+character.
+In addition, the final line and column the cursor rests upon,
+and any options which affect the command are noted.
+.KY <control-A>
+.IP "[count] <control-A>"
+Search forward
+.LI count
+times for the current word.
+The current word begins at the first non-whitespace character on or
+after the current cursor position,
+and extends up to the next non-word character or the end of the line.
+The search is literal, i.e. no characters in the word have any special
+meaning in terms of Regular Expressions.
+It is an error if no matching pattern is found between the starting position
+and the end of the file.
+.sp
+The
+.CO <control-A>
+command is an absolute movement.
+The
+.CO <control-A>
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line where the word is found.
+.SP Column:
+Set to the first character of the word.
+.SP Options:
+Affected by the
+.OP extended ,
+.OP ignorecase
+and
+.OP wrapscan
+options.
+.SE
+.KY <control-B>
+.IP "[count] <control-B>"
+Page backward
+.LI count
+screens.
+Two lines of overlap are maintained by displaying the window
+starting at line
+.LI "(top_line - count * window_size) + 2" ,
+where
+.LI window_size
+is the value of the
+.OP window
+option.
+(In the case of split screens, this size is corrected to the
+current screen size.)
+This is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO <control-B>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the last line of text displayed on the screen.
+.SP Column:
+Set to the first nonblank character of the line.
+.SP Options:
+None.
+.SE
+.KY <control-D>
+.IP "[count] <control-D>"
+Scroll forward
+.LI count
+lines.
+If
+.LI count
+is not specified, scroll forward the number of lines specified by the last
+.CO <control-D>
+or
+.CO <control-U>
+command.
+If this is the first
+.CO <control-D>
+or
+.CO <control-U>
+command,
+scroll forward half the number of lines in the screen.
+(In the case of split screens, the default scrolling distance is
+corrected to half the current screen size.)
+This is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-D>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the current line plus the number of lines scrolled.
+.SP Column:
+Set to the first nonblank character of the line.
+.SP Options:
+None.
+.SE
+.KY <control-E>
+.IP "[count] <control-E>"
+Scroll forward
+.LI count
+lines, leaving the cursor on the current line and column, if possible.
+This is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the first line on the screen.
+.SP Column:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-F>
+.IP "[count] <control-F>"
+Page forward
+.LI count
+screens.
+Two lines of overlap are maintained by displaying the window
+starting at line
+.LI "top_line + count * window_size - 2" ,
+where
+.LI window_size
+is the value of the
+.OP window
+option.
+(In the case of split screens, this size is corrected to the
+current screen size.)
+This is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-F>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the first line on the screen.
+.SP Column:
+Set to the first nonblank character of the current line.
+.SP Options:
+None.
+.SE
+.KY <control-G>
+.IP "<control-G>"
+Display the file information.
+The information includes the current pathname, the current line,
+the number of total lines in the file, the current line as a percentage
+of the total lines in the file, if the file has been modified,
+was able to be locked, if the file's name has been changed,
+and if the edit session is read-only.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-H>
+.IP "<control-H>"
+.Ip "[count] h"
+Move the cursor back
+.LI count
+characters in the current line.
+This is an error if the cursor is on the first character in the line.
+.sp
+The
+.CO <control-H>
+and
+.CO h
+commands may be used as the motion component of other
+.CO vi
+commands,
+in which case any text copied into a buffer is character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the
+.LI "current - count"
+character, or, the first character in the line if
+.LI count
+is greater than or equal to the number of characters in the line
+before the cursor.
+.SP Options:
+None.
+.SE
+.KY <control-J>
+.IP "[count] <control-J>"
+.KY <control-N>
+.Ip "[count] <control-N>"
+.KY j
+.Ip "[count] j"
+Move the cursor down
+.LI count
+lines without changing the current column.
+This is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-J> ,
+.CO <control-N>
+and
+.CO j
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count .
+.SP Column:
+The most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-L>
+.IP "<control-L>"
+.KY <control-R>
+.Ip "<control-R>"
+Repaint the screen.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-M>
+.IP "[count] <control-M>"
+.KY +
+.Ip "[count] +"
+Move the cursor down
+.LI count
+lines to the first nonblank character of that line.
+This is an error if the movement is past the end of the file.
+.sp
+The
+.CO <control-M>
+and
+.CO +
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count .
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY <control-P>
+.IP "[count] <control-P>"
+.KY k
+.Ip "[count] k"
+Move the cursor up
+.LI count
+lines, without changing the current column.
+This is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO <control-P>
+and
+.CO k
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line minus count.
+.SP Column:
+The most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-T>
+.IP "<control-T>"
+Return to the most recent tag context.
+The
+.CO <control-T>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the context of the previous tag command.
+.SP Column:
+Set to the context of the previous tag command.
+.SP Options:
+None.
+.SE
+.KY <control-U>
+.IP "<control-U>"
+Scroll backward
+.LI count
+lines.
+If
+.LI count
+is not specified, scroll backward the number of lines specified by the
+last
+.CO <control-D>
+or
+.CO <control-U>
+command.
+If this is the first
+.CO <control-D>
+or
+.CO <control-U>
+command,
+scroll backward half the number of lines in the screen.
+(In the case of split screens, the default scrolling distance is
+corrected to half the current screen size.)
+This is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO <control-U>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the current line minus the amount scrolled.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY <control-W>
+.IP "<control-W>"
+Switch to the next lower screen in the window, or, to the first
+screen if there are no lower screens in the window.
+.SS
+.SP Line:
+Set to the previous cursor position in the window.
+.SP Column:
+Set to the previous cursor position in the window.
+.SP Options:
+None.
+.SE
+.KY <control-Y>
+.IP "<control-Y>"
+Scroll backward
+.LI count
+lines, leaving the current line and column as is, if possible.
+This is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Unchanged unless the current line scrolls off the screen,
+in which case it is set to the last line of text displayed
+on the screen.
+.SP Column:
+Unchanged unless the current line scrolls off the screen,
+in which case it is the most attractive cursor position.
+.SP Options:
+None.
+.SE
+.KY <control-Z>
+.IP "<control-Z>"
+Suspend the current editor session.
+If the file has been modified since it was last completely written,
+and the
+.OP autowrite
+option is set, the file is written before the editor session is
+suspended.
+If this write fails, the editor session is not suspended.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+Affected by the
+.OP autowrite
+option.
+.SE
+.KY <escape>
+.IP "<escape>"
+Execute
+.CO ex
+commands or cancel partial commands.
+If an
+.CO ex
+command is being entered (e.g.
+.CO / ,
+.CO ? ,
+.CO :
+or
+.CO ! ),
+the command is executed.
+If a partial command has been entered, e.g.
+.QL "[0-9]*" ,
+or
+.QL "[0-9]*[!<>cdy]" ,
+the command is cancelled.
+Otherwise, it is an error.
+.SS
+.SP Line:
+When an
+.CO ex
+command is being executed, the current line is set as described for
+that command.
+Otherwise, unchanged.
+.SP Column:
+When an
+.CO ex
+command is being executed, the current column is set as described for
+that command.
+Otherwise, unchanged.
+.SP Options:
+None.
+.SE
+.KY <control-]>
+.IP "<control-]>"
+Push a tag reference onto the tag stack.
+The tags files (see the
+.OP tags
+option for more information) are searched for a tag matching the
+current word.
+The current word begins at the first non-whitespace character on or
+after the current cursor position,
+and extends up to the next non-word character or the end of the line.
+If a matching tag is found, the current file is discarded and the
+file containing the tag reference is edited.
+.sp
+If the current file has been modified since it was last completely
+written, the command will fail.
+The
+.CO <control-]>
+command is an absolute movement.
+.SS
+.SP Line:
+Set to the line containing the matching tag string.
+.SP Column:
+Set to the start of the matching tag string.
+.SP Options:
+Affected by the
+.OP tags
+and
+.OP taglength
+options.
+.SE
+.KY <control-^>
+.IP "<control-^>"
+Switch to the most recently edited file.
+.sp
+If the file has been modified since it was last completely written,
+and the
+.OP autowrite
+option is set, the file is written out.
+If this write fails, the command will fail.
+Otherwise, if the current file has been modified since it was last
+completely written, the command will fail.
+.SS
+.SP Line:
+Set to the line the cursor was on when the file was last edited.
+.SP Column:
+Set to the column the cursor was on when the file was last edited.
+.SP Options:
+Affected by the
+.OP autowrite
+option.
+.SE
+.KY <space>
+.IP "[count] <space>"
+.KY l
+.Ip "[count] l"
+Move the cursor forward
+.LI count
+characters without changing the current line.
+This is an error if the cursor is on the last character in the line.
+.sp
+The
+.CO <space>
+and
+.CO \&l
+commands may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+In addition, these commands may be used as the motion components
+of other commands when the cursor is on the last character in the
+line, without error.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the current character plus the next
+.LI count
+characters, or to the last character on the line if
+.LI count
+is greater than the number of characters in the line after the
+current character.
+.SP Options:
+None.
+.SE
+.KY !
+.IP "[count] ! motion shell-argument(s)"
+Replace text with results from a shell command.
+Pass the lines specified by the
+.LI count
+and
+.LI motion
+arguments as standard input to the program named by the
+.OP shell
+option, and replace those lines with the output (both
+standard error and standard output) of that command.
+.sp
+After the motion is entered,
+.CO vi
+prompts for arguments to the shell command.
+.sp
+Within those arguments,
+.QT %
+and
+.QT #
+characters are expanded to the current and alternate pathnames,
+respectively.
+The
+.QT !
+character is expanded with the command text of the previous
+.CO !
+or
+.CO :!
+commands.
+(Therefore, the command
+.CO !!
+repeats the previous
+.CO !
+command.)
+The special meanings of
+.QT % ,
+.QT #
+and
+.QT !
+can be overridden by escaping them with a backslash.
+If no
+.CO !
+or
+.CO :!
+command has yet been executed, it is an error to use an unescaped
+.QT !
+character.
+The
+.CO !
+command does
+.i not
+do shell expansion on the strings provided as arguments.
+If any of the above expansions change the arguments the user entered,
+the command is redisplayed at the bottom of the screen.
+.sp
+.CO Vi
+then executes the program named by the
+.OP shell
+option, with a
+.b \-c
+flag followed by the arguments (which are bundled into a single argument).
+.sp
+The
+.CO !
+command is permitted in an empty file.
+.sp
+If the file has been modified since it was last completely written,
+the
+.CO !
+command will warn you.
+.SS
+.SP Line:
+The first line of the replaced text.
+.SP Column:
+The first column of the replaced text.
+.SP Options:
+Affected by the
+.OP shell
+option.
+.SE
+.KY #
+.IP "[count] # +|-|#"
+Increment or decrement the current number.
+The current number begins at the first non-number character on or
+before the current cursor position, or the beginning of the line,
+and extends up to the first non-number character on or after the
+current cursor position or the end of the line.
+If the trailing character is a
+.LI \&+ ,
+the number is incremented by
+.LI count .
+If the trailing character is a
+.LI \&- ,
+the number is decremented by
+.LI count .
+If the trailing character is a
+.LI \&# ,
+the previous increment or decrement is repeated.
+.sp
+The format of the number (decimal, hexadecimal, and octal,
+and leading 0's) is retained unless the new value cannot be
+represented in the previous format.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first character in the cursor word.
+.SP Options:
+None.
+.SE
+.KY $
+.IP "[count] $"
+Move the cursor to the end of a line.
+If
+.LI count
+is specified, the cursor moves down
+.LI "count - 1"
+lines.
+.sp
+It is not an error to use the
+.CO $
+command when the cursor is on the last character in the line or
+when the line is empty.
+.sp
+The
+.CO $
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the cursor is at, or before the first
+nonblank character in the line, in which case it is line oriented.
+It is not an error to use the
+.CO $
+command as a motion component when the cursor is on the last character
+in the line, although it is an error when the line is empty.
+.SS
+.SP Line:
+Set to the current line plus
+.LI count
+minus 1.
+.SP Column:
+Set to the last character in the line.
+.SP Options:
+None.
+.SE
+.KY %
+.IP %
+Move to the matching character.
+The cursor moves to the parenthesis or curly brace which
+.i matches
+the parenthesis or curly brace found at the current cursor position
+or which is the closest one to the right of the cursor on the line.
+It is an error to execute the
+.CO %
+command on a line without a parenthesis or curly brace.
+Historically, any
+.LI count
+specified to the
+.CO %
+command was ignored.
+.sp
+The
+.CO %
+command is an absolute movement.
+The
+.CO %
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting point of the region is at
+or before the first nonblank character on its line, and the ending
+point is at or after the last nonblank character on its line, in
+which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the matching character.
+.SP Column:
+Set to the matching character.
+.SP Options:
+None.
+.SE
+.KY &
+.IP "&"
+Repeat the previous substitution command on the current line.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO &
+command was ignored.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged if the cursor was on the last character in the line,
+otherwise, set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP edcompatible ,
+.OP extended ,
+.OP ignorecase
+and
+.OP magic
+options.
+.SE
+.KY SQUOTE<character>
+.IP \'<character>
+.KY `<character>
+.Ip `<character>
+Return to a context marked by the character
+.LI <character> .
+If
+.LI <character>
+is the
+.QT '
+or
+.QT `
+character, return to the previous context.
+If
+.LI <character>
+is any other character,
+return to the context marked by that character (see the
+.CO m
+command for more information).
+If the command is the
+.CO \'
+command, only the line value is restored,
+and the cursor is placed on the first nonblank character of that line.
+If the command is the
+.CO `
+command, both the line and column values are restored.
+.sp
+It is an error if the context no longer exists because of
+line deletion.
+(Contexts follow lines that are moved, or which are deleted
+and then restored.)
+.sp
+The
+.CO \'
+and
+.CO `
+commands are both absolute movements.
+They may be used as a motion component for other
+.CO vi
+commands.
+For the
+.CO \'
+command, any text copied into a buffer is line oriented.
+For the
+.CO `
+command, any text copied into a buffer is character oriented, unless
+it both starts and stops at the first character in the line, in which
+case it is line oriented.
+In addition, when using the
+.CO `
+command as a motion component,
+commands which move backward and started at the first character in the
+line, or move forward and ended at the first character in the line,
+are corrected to the last character of the starting and ending lines,
+respectively.
+.SS
+.SP Line:
+Set to the line from the context.
+.SP Column:
+Set to the first nonblank character in the line, for the
+.CO \'
+command, and set to the context's column for the
+.CO `
+command.
+.SP Options:
+None.
+.SE
+.KY (
+.IP "[count] ("
+Back up
+.LI count
+sentences.
+.sp
+The
+.CO (
+command is an absolute movement.
+The
+.CO (
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting and stopping points of the
+region are the first character in the line, in which case it is
+line oriented.
+In the latter case, the stopping point of the region is adjusted
+to be the end of the line immediately before it, and not the original
+cursor position.
+.SS
+.SP Line:
+Set to the line containing the beginning of the sentence.
+.SP Column:
+Set to the first nonblank character of the sentence.
+.SP Options:
+None.
+.SE
+.KY )
+.IP "[count] )"
+Move forward
+.LI count
+sentences.
+.sp
+The
+.CO )
+command is an absolute movement.
+The
+.CO )
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting point of the region is the
+first character in the line, in which case it is line oriented.
+In the latter case, if the stopping point of the region is also
+the first character in the line, it is adjusted to be the end of the
+line immediately before it.
+.SS
+.SP Line:
+Set to the line containing the beginning of the sentence.
+.SP Column:
+Set to the first nonblank character of the sentence.
+.SP Options:
+None.
+.SE
+.KY ,
+.IP "[count] ,"
+Reverse find character
+.LI count
+times.
+Reverse the last
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+command, searching the other way in the line,
+.LI count
+times.
+.sp
+The
+.CO ,
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY MINUSSIGN
+.IP "[count] \-"
+Move to first nonblank of the previous line,
+.LI count
+times.
+.sp
+This is an error if the movement is past the beginning of the file.
+.sp
+The
+.CO -
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the current line minus
+.LI count .
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY \&.
+.IP "[count] \&."
+Repeat the last
+.CO vi
+command that modified text.
+The repeated command may be a command and motion component combination.
+If
+.LI count
+is specified, it replaces
+.i both
+the count specified for the repeated command, and, if applicable, for
+the repeated motion component.
+If
+.LI count
+is not specified, the counts originally specified to the command being
+repeated are used again.
+.sp
+As a special case, if the
+.CO \.
+command is executed immediately after the
+.CO u
+command, the change log is rolled forward or backward, depending on
+the action of the
+.CO u
+command.
+.SS
+.SP Line:
+Set as described for the repeated command.
+.SP Column:
+Set as described for the repeated command.
+.SP Options:
+None.
+.SE
+.KY /RE/
+.IP "/RE<carriage-return>"
+.Ip "/RE/ [offset]<carriage-return>"
+.KY ?RE?
+.Ip "?RE<carriage-return>"
+.Ip "?RE? [offset]<carriage-return>"
+.KY N
+.Ip "N"
+.KY n
+.Ip "n"
+Search forward or backward for a regular expression.
+The commands beginning with a slash
+.PQ /
+character are forward searches, the commands beginning with a
+question mark
+.PQ ?
+are backward searches.
+.CO Vi
+prompts with the leading character on the last line of the screen
+for a string.
+It then searches forward or backward in the file for the next
+occurrence of the string, which is interpreted as a Basic Regular
+Expression.
+.sp
+The
+.CO /
+and
+.CO ?
+commands are absolute movements.
+They may be used as the motion components of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the search started and ended on
+the first column of a line, in which case it is line oriented.
+In addition, forward searches ending at the first character of a line,
+and backward searches beginning at the first character in the line,
+are corrected to begin or end at the last character of the previous line.
+(Note, forward and backward searches can occur for both
+.CO /
+and
+.CO ?
+commands, if the
+.OP wrapscan
+option is set.)
+.sp
+If an offset from the matched line is specified (i.e. a trailing
+.QT /
+or
+.QT ?
+character is followed by a signed offset), the buffer will always
+be line oriented (e.g.
+.QT /string/+0
+will always guarantee a line orientation).
+.sp
+The
+.CO n
+command repeats the previous search.
+.sp
+The
+.CO N
+command repeats the previous search, but in the reverse direction.
+.sp
+Missing RE's (e.g.
+.QT //<carriage-return> ,
+.QT /<carriage-return> ,
+.QT ??<carriage-return> ,
+or
+.QT ?<carriage-return>
+search for the last search RE, in the indicated direction.
+.sp
+Searches may be interrupted using the
+.LI <interrupt>
+character.
+.SS
+.SP Line:
+Set to the line in which the match occurred.
+.SP Column:
+Set to the first character of the matched string.
+.SP Options:
+Affected by the
+.OP edcompatible ,
+.OP extended ,
+.OP ignorecase ,
+.OP magic ,
+and
+.OP wrapscan
+options.
+.SE
+.KY 0
+.IP "0"
+Move to the first character in the current line.
+It is not an error to use the
+.CO 0
+command when the cursor is on the first character in the line,
+.sp
+The
+.CO 0
+command may be used as the motion component of other
+.CO vi
+commands, in which case it is an error if the cursor is on the
+first character in the line.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first character in the line.
+.SP Options:
+None.
+.SE
+.KY :
+.IP ":"
+Execute an ex command.
+.CO Vi
+prompts for an
+.CO ex
+command on the last line of the screen, using a colon
+.PQ :
+character.
+The command is terminated by a
+.LI <carriage-return> ,
+.LI <newline>
+or
+.LI <escape>
+character; all of these characters may be escaped by using a
+.LI "<literal next>"
+character.
+The command is then executed.
+.sp
+If the
+.CO ex
+command writes to the screen,
+.CO vi
+will prompt the user for a
+.LI <carriage-return>
+before continuing
+when the
+.CO ex
+command finishes.
+Large amounts of output from the
+.CO ex
+command will be paged for the user, and the user prompted for a
+.LI <carriage-return>
+or
+.LI <space>
+key to continue.
+In some cases, a quit (normally a
+.QQ q
+character) or
+.LI <interrupt>
+may be entered to interrupt the
+.CO ex
+command.
+.sp
+When the
+.CO ex
+command finishes, and the user is prompted to resume visual mode,
+it is also possible to enter another
+.QT :
+character followed by another
+.CO ex
+command.
+.SS
+.SP Line:
+The current line is set as described for the
+.CO ex
+command.
+.SP Column:
+The current column is set as described for the
+.CO ex
+command.
+.SP Options:
+None.
+.SE
+.KY ;
+.IP "[count] ;"
+Repeat the last character find
+.LI count
+times.
+The last character find is one of the
+.CO F ,
+.CO f ,
+.CO T
+or
+.CO t
+commands.
+.sp
+The
+.CO ;
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY <
+.IP "[count] < motion"
+.KY >
+.Ip "[count] > motion"
+Shift lines left or right.
+Shift the number of lines in the region specified by the motion component,
+times
+.LI count ,
+left (for the
+.CO <
+command) or right (for the
+.CO >
+command) by the number of columns specified by the
+.OP shiftwidth
+option.
+Only whitespace characters are deleted when shifting left;
+once the first character in the line contains a nonblank character,
+the
+.CO shift
+will succeed, but the line will not be modified.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP shiftwidth
+option.
+.SE
+.KY @
+.IP "@ buffer"
+Execute a named buffer.
+Execute the named buffer as
+.CO vi
+commands.
+The buffer may include
+.CO ex
+commands, too, but they must be expressed as a
+.CO :
+command.
+If the buffer is line oriented,
+.LI <newline>
+characters are logically appended to each line of the buffer.
+If the buffer is character oriented,
+.LI <newline>
+characters are logically appended to all but the last line in the buffer.
+.sp
+If the buffer name is
+.QT @ ,
+or
+.QT * ,
+then the last buffer executed shall be used.
+It is an error to specify
+.QT @@
+or
+.QT **
+if there were no buffer previous executions.
+The text of a macro may contain an
+.CO @
+command,
+and it is possible to create infinite loops in this manner.
+(The
+.LI <interrupt>
+character may be used to interrupt the loop.)
+.SS
+.SP Line:
+The current line is set as described for the command(s).
+.SP Column:
+The current column is set as described for the command(s).
+.SP Options:
+None.
+.SE
+.KY A
+.IP "[count] A"
+Enter input mode, appending the text after the end of the line.
+If
+.LI count
+is specified, the text is repeatedly input
+.LI "count - 1"
+more times after input mode is exited.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY B
+.IP "[count] B"
+Move backward
+.LI count
+bigwords.
+Move the cursor backward to the beginning of a bigword by repeating the
+following algorithm: if the current position is at the beginning of a
+bigword or the character at the current position cannot be part of a bigword,
+move to the first character of the preceding bigword.
+Otherwise, move to the first character of the bigword at the current position.
+If no preceding bigword exists on the current line, move to the first
+character of the last bigword on the first preceding line that contains a
+bigword.
+.sp
+The
+.CO B
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY C
+.IP "[buffer] [count] C"
+Change text from the current position to the end-of-line.
+If
+.LI count
+is specified, the input text replaces from the current position to
+the end-of-line, plus
+.LI "count - 1"
+subsequent lines.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY D
+.IP "[buffer] D"
+Delete text from the current position to the end-of-line.
+.sp
+It is not an error to execute the
+.CO D
+command on an empty line.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character before the current character, or, column 1 if
+the cursor was on column 1.
+.SP Options:
+None.
+.SE
+.KY E
+.IP "[count] E"
+Move forward
+.LI count
+end-of-bigwords.
+Move the cursor forward to the end of a bigword by repeating the
+following algorithm: if the current position is the end of a
+bigword or the character at that position cannot be part of a bigword,
+move to the last character of the following bigword.
+Otherwise, move to the last character of the bigword at the current
+position.
+If no succeeding bigword exists on the current line,
+move to the last character of the first bigword on the next following
+line that contains a bigword.
+.sp
+The
+.CO E
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the last character of the word selected.
+.SP Options:
+None.
+.SE
+.KY F
+.IP "[count] F <character>"
+Search
+.LI count
+times backward through the current line for
+.LI <character> .
+.sp
+The
+.CO F
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY G
+.IP "[count] G"
+Move to line
+.LI count ,
+or the last line of the file if
+.LI count
+not specified.
+.sp
+The
+.CO G
+command is an absolute movement.
+The
+.CO \&G
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to
+.LI count ,
+if specified, otherwise, the last line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY H
+.IP "[count] H"
+Move to the screen line
+.LI "count - 1"
+lines below the top of the screen.
+.sp
+The
+.CO H
+command is an absolute movement.
+The
+.CO H
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the line
+.LI "count - 1"
+lines below the top of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY I
+.IP "[count] I"
+Enter input mode, inserting the text at the beginning of the line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+None.
+.SE
+.KY J
+.IP "[count] J"
+Join lines.
+If
+.LI count
+is specified,
+.LI count
+lines are joined; a minimum of two lines are always joined,
+regardless of the value of
+.LI count .
+.sp
+If the current line ends with a whitespace character, all whitespace
+is stripped from the next line.
+Otherwise, if the next line starts with a open parenthesis
+.PQ (
+do nothing.
+Otherwise, if the current line ends with a question mark
+.PQ ? ,
+period
+.PQ \&.
+or exclamation point
+.PQ ! ,
+insert two spaces.
+Otherwise, insert a single space.
+.sp
+It is not an error to join lines past the end of the file,
+i.e. lines that do not exist.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character after the last character of the next-to-last
+joined line.
+.SP Options:
+None.
+.SE
+.KY L
+.IP "[count] L"
+Move to the screen line
+.LI "count - 1"
+lines above the bottom of the screen.
+.sp
+The
+.CO L
+command is an absolute movement.
+The
+.CO L
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.SS
+.SP Line:
+Set to the line
+.LI "count - 1"
+lines above the bottom of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY M
+.IP " M"
+Move to the screen line in the middle of the screen.
+.sp
+The
+.CO M
+command is an absolute movement.
+The
+.CO M
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO M
+command was ignored.
+.SS
+.SP Line:
+Set to the line in the middle of the screen.
+.SP Column:
+Set to the first nonblank character of the
+.i screen
+line.
+.SP Options:
+None.
+.SE
+.KY O
+.IP "[count] O"
+Enter input mode, appending text in a new line above the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO O
+command was ignored.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY P
+.IP "[buffer] P"
+Insert text from a buffer.
+Text from the buffer (the unnamed buffer by default) is inserted
+before the current column or, if the buffer is line oriented,
+before the current line.
+.SS
+.SP Line:
+Set to the lowest numbered line insert,
+if the buffer is line oriented, otherwise unchanged.
+.SP Column:
+Set to the first nonblank character of the appended text,
+if the buffer is line oriented, otherwise, the last character
+of the appended text.
+.SP Options:
+None.
+.SE
+.KY Q
+.IP "Q"
+Exit
+.CO vi
+(or visual) mode and switch to
+.CO ex
+mode.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+No longer relevant.
+.SP Options:
+None.
+.SE
+.KY R
+.IP "[count] R"
+Enter input mode, replacing the characters in the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+If the end of the current line is reached, no more characters are
+replaced and any further characters input are appended to the line.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY S
+.IP "[buffer] [count] S"
+Substitute
+.LI count
+lines.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY T
+.IP "[count] T <character>"
+Search backward,
+.LI count
+times,
+through the current line for the character
+.i after
+the specified
+.LI <character> .
+.sp
+The
+.CO T
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character
+.i after
+the searched-for character.
+.SP Options:
+None.
+.SE
+.KY U
+.IP "U"
+Restore the current line to its state before the cursor last
+moved to it.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+The first character in the line.
+.SP Options:
+None.
+.SE
+.KY W
+.IP "[count] W"
+Move forward
+.LI count
+bigwords.
+Move the cursor forward to the beginning of a bigword by repeating the
+following algorithm: if the current position is within a bigword or the
+character at that position cannot be part of a bigword, move to the first
+character of the next bigword.
+If no subsequent bigword exists on the current line,
+move to the first character of the first bigword on the first following
+line that contains a bigword.
+.sp
+The
+.CO W
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+The line containing the word selected.
+.SP Column:
+The first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY X
+.IP "[buffer] [count] X"
+Delete
+.LI count
+characters before the cursor.
+If the number of characters to be deleted is greater than or equal to
+the number of characters to the beginning of the line, all of the
+characters before the current cursor position, to the beginning of the
+line, are deleted.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the current character minus
+.LI count ,
+or the first character if count is greater than the number of
+characters in the line before the cursor.
+.SP Options:
+None.
+.SE
+.KY Y
+.IP "[buffer] [count] Y"
+Copy (or
+.QQ yank )
+.LI count
+lines into the specified buffer.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY ZZ
+.IP "ZZ"
+Write the file and exit
+.CO vi .
+The file is only written if it has been modified since the last
+complete write of the file to any file.
+.sp
+The
+.CO ZZ
+command will exit the editor after writing the file,
+if there are no further files to edit.
+Entering two
+.QQ quit
+commands (i.e.
+.CO wq ,
+.CO quit ,
+.CO xit
+or
+.CO ZZ )
+in a row will override this check and the editor will exit,
+ignoring any files that have not yet been edited.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY [[
+.IP "[count] [["
+Back up
+.LI count
+section boundaries.
+.sp
+The
+.CO [[
+command is an absolute movement.
+The
+.CO [[
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting position is column 0,
+in which case it is line oriented.
+.sp
+This is an error if the movement is past the beginning of the file.
+.SS
+.SP Line:
+Set to the previous line that is
+.LI count
+section boundaries back,
+or the first line of the file if no more section boundaries exist
+preceding the current line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP sections
+option.
+.SE
+.KY ]]
+.IP "[count] ]]"
+Move forward
+.LI count
+section boundaries.
+.sp
+The
+.CO ]]
+command is an absolute movement.
+The
+.CO ]]
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting position is column 0,
+in which case it is line oriented.
+.sp
+This is an error if the movement is past the end of the file.
+.SS
+.SP Line:
+Set to the line that is
+.LI count
+section boundaries forward,
+or to the last line of the file if no more section
+boundaries exist following the current line.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP sections
+option.
+.SE
+.KY ^
+.IP "\&^"
+Move to first nonblank character on the current line.
+.sp
+The
+.CO ^
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the first nonblank character of the current line.
+.SP Options:
+None.
+.SE
+.KY _
+.IP "[count] _"
+Move down
+.LI "count - 1"
+lines, to the first nonblank character.
+The
+.CO _
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+line oriented.
+.sp
+It is not an error to execute the
+.CO _
+command when the cursor is on the first character in the line.
+.SS
+.SP Line:
+The current line plus
+.LI "count - 1" .
+.SP Column:
+The first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY a
+.IP "[count] a"
+Enter input mode, appending the text after the cursor.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY b
+.IP "[count] b"
+Move backward
+.LI count
+words.
+Move the cursor backward to the beginning of a word by repeating the
+following algorithm: if the current position is at the beginning of a word,
+move to the first character of the preceding word.
+Otherwise, the current position moves to the first character of the word
+at the current position.
+If no preceding word exists on the current line, move to the first
+character of the last word on the first preceding line that contains
+a word.
+.sp
+The
+.CO b
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY c
+.IP "[buffer] [count] c motion"
+Change a region of text.
+If only part of a single line is affected, then the last character
+being changed is marked with a
+.QT $ .
+Otherwise, the region of text is deleted, and input mode is entered.
+.sp
+If
+.LI count
+is specified, it is applied to the
+.LI motion .
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY d
+.IP "[buffer] [count] d motion"
+Delete a region of text.
+If
+.LI count
+is specified, it is applied to the
+.LI motion .
+.SS
+.SP Line:
+Set to the line where the region starts.
+.SP Column:
+Set to the first character in the line after the last character in the
+region.
+If no such character exists, set to the last character before the region.
+.SP Options:
+None.
+.SE
+.KY e
+.IP "[count] e"
+Move forward
+.LI count
+end-of-words.
+Move the cursor forward to the end of a word by repeating the following
+algorithm: if the current position is the end of a word,
+move to the last character of the following word.
+Otherwise, move to the last character of the word at the current position.
+If no succeeding word exists on the current line, move to the last character
+of the first word on the next following line that contains a word.
+.sp
+The
+.CO e
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the last character of the word selected.
+.SP Options:
+None.
+.SE
+.KY f
+.IP "[count] f <character>"
+Search forward,
+.LI count
+times, through the rest of the current line for
+.LI <character> .
+.sp
+The
+.CO f
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the searched-for character.
+.SP Options:
+None.
+.SE
+.KY i
+.IP "[count] i"
+Enter input mode, inserting the text before the cursor.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY m
+.IP "m <character>"
+Save the current context (line and column) as
+.LI <character> .
+The exact position is referred to by
+.QT `<character> .
+The line is referred to by
+.QT '<character> .
+.sp
+Historically,
+.LI <character>
+was restricted to lower-case letters only,
+.CO nvi
+permits the use of any character.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged.
+.SP Options:
+None.
+.SE
+.KY o
+.IP "[count] o"
+Enter input mode, appending text in a new line under the current line.
+If
+.LI count
+is specified, the text input is repeatedly input
+.LI "count - 1"
+more times.
+.sp
+Historically, any
+.LI count
+specified to the
+.CO o
+command was ignored.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY p
+.IP "[buffer] p"
+Append text from a buffer.
+Text from the buffer (the unnamed buffer by default) is appended
+after the current column or, if the buffer is line oriented,
+after the current line.
+.SS
+.SP Line:
+Set to the first line appended, if the buffer is line oriented,
+otherwise unchanged.
+.SP Column:
+Set to the first nonblank character of the appended text if the buffer
+is line oriented, otherwise, the last character of the appended text.
+.SP Options:
+None.
+.SE
+.KY r
+.IP "[count] r <character>"
+Replace characters.
+The next
+.LI count
+characters in the line are replaced with
+.LI <character> .
+Replacing characters with
+.LI <newline>
+characters results in creating new, empty lines into the file.
+.sp
+If
+.LI <character>
+is
+.LI <escape> ,
+the command is cancelled.
+.SS
+.SP Line:
+Unchanged unless the replacement character is a
+.LI <newline> ,
+in which case it is set to the current line plus
+.LI "count - 1" .
+.SP Column:
+Set to the last character replaced,
+unless the replacement character is a
+.LI <newline> ,
+in which case the cursor is in column 1 of the last line inserted.
+.SP Options:
+None.
+.SE
+.KY s
+.IP "[buffer] [count] s"
+Substitute
+.LI count
+characters in the current line starting with the current character.
+.SS
+.SP Line:
+Set to the last line upon which characters were entered.
+.SP Column:
+Set to the last character entered.
+.SP Options:
+Affected by the
+.OP altwerase ,
+.OP autoindent ,
+.OP beautify ,
+.OP showmatch ,
+.OP ttywerase
+and
+.OP wrapmargin
+options.
+.SE
+.KY t
+.IP "[count] t <character>"
+Search forward,
+.LI count
+times, through the current line for the character immediately
+.i before
+.LI <character> .
+.sp
+The
+.CO t
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character
+.i before
+the searched-for character.
+.SP Options:
+None.
+.SE
+.KY u
+.IP "u"
+Undo the last change made to the file.
+If repeated, the
+.CO u
+command alternates between these two states, and is its own inverse.
+When used after an insert that inserted text on more than one line,
+the lines are saved in the numeric buffers.
+.sp
+The
+.CO \&.
+command, when used immediately after the
+.CO u
+command, causes the change log to be rolled forward or backward,
+depending on the action of the
+.CO u
+command.
+.SS
+.SP Line:
+Set to the position of the first line changed, if the reversal affects
+only one line or represents an addition or change; otherwise, the line
+preceding the deleted text.
+.SP Column:
+Set to the cursor position before the change was made.
+.SP Options:
+None.
+.SE
+.KY w
+.IP "[count] w"
+Move forward
+.LI count
+words.
+Move the cursor forward to the beginning of a word by repeating the
+following algorithm: if the current position is at the
+beginning of a word, move to the first character of the next word.
+If no subsequent word exists on the current line, move to the first
+character of the first word on the first following line that contains
+a word.
+.sp
+The
+.CO w
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+.SS
+.SP Line:
+Set to the line containing the word selected.
+.SP Column:
+Set to the first character of the word selected.
+.SP Options:
+None.
+.SE
+.KY x
+.IP "[buffer] [count] x"
+Delete
+.LI count
+characters.
+The deletion is at the current character position.
+If the number of characters to be deleted is greater than or equal to
+the number of characters to the end of the line, all of the characters
+from the current cursor position to the end of the line are deleted.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Unchanged unless the last character in the line is deleted and the cursor
+is not already on the first character in the line, in which case it is
+set to the previous character.
+.SP Options:
+None.
+.SE
+.KY y
+.IP "[buffer] [count] y motion"
+Copy (or
+.QQ yank )
+a text region specified by the
+.LI count
+and motion into a buffer.
+If
+.LI count
+is specified, it is applied to the
+.LI motion .
+.SS
+.SP Line:
+Unchanged, unless the region covers more than a single line,
+in which case it is set to the line where the region starts.
+.SP Column:
+Unchanged, unless the region covers more than a single line,
+in which case it is set to the character were the region starts.
+.SP Options:
+None.
+.SE
+.KY z
+.IP "[count1] z [count2] type"
+Redraw the screen with a window
+.LI count2
+lines long, with line
+.LI count1
+placed as specified by the
+.LI type
+character.
+If
+.LI count1
+is not specified, it defaults to the current line.
+If
+.LI count2
+is not specified, it defaults to the current window size.
+.sp
+The following
+.LI type
+characters may be used:
+.SS
+.SP +
+If
+.LI count1
+is specified, place the line
+.LI count1
+at the top of the screen.
+Otherwise, display the screen after the current screen, similarly to the
+.CO <control-F>
+command.
+.SP <carriage-return>
+Place the line
+.LI count1
+at the top of the screen.
+.SP \&.
+Place the line
+.LI count1
+in the center of the screen.
+.SP \-
+Place the line
+.LI count1
+at the bottom of the screen.
+.SP ^
+If
+.LI count1
+is specified, place the line that is at the top of the screen
+when
+.LI count1
+is at the bottom of the screen, at the bottom of the screen,
+i.e. display the screen before the screen before
+.LI count1 .
+Otherwise, display the screen before the current screen, similarly to the
+.CO <control-B>
+command.
+.SE
+.SS
+.SP Line:
+Set to
+.LI count1
+unless
+.LI count1
+is not specified and the
+.LI type
+character was either
+.QT ^
+or
+.QT + ,
+in which case it is set to the line before the first line on the
+previous screen or the line after the last line on the previous
+screen, respectively.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+None.
+.SE
+.KY {
+.IP "[count] {"
+Move backward
+.LI count
+paragraphs.
+.sp
+The
+.CO {
+command is an absolute movement.
+The
+.CO {
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting character is the first
+character on its line, in which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the beginning of the previous paragraph.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP paragraph
+option.
+.SE
+.KY |
+.IP "[count] |"
+Move to a specific
+.i column
+position on the current line.
+.sp
+The
+.CO |
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented.
+It is an error to use the
+.CO |
+command as a motion component and for the cursor not to move.
+.SS
+.SP Line:
+Unchanged.
+.SP Column:
+Set to the character occupying the column position identified by
+.LI count ,
+if the position exists in the line.
+If the column length of the current line is less than
+.LI count ,
+the cursor is moved to the last character in the line.
+.SP Options:
+None.
+.SE
+.KY }
+.IP "[count] }"
+Move forward
+.LI count
+paragraphs.
+.sp
+The
+.CO }
+command is an absolute movement.
+The
+.CO }
+command may be used as the motion component of other
+.CO vi
+commands, in which case any text copied into a buffer is
+character oriented, unless the starting character is at or
+before any nonblank characters in its line,
+in which case it is line oriented.
+.SS
+.SP Line:
+Set to the line containing the beginning of the next paragraph.
+.SP Column:
+Set to the first nonblank character in the line.
+.SP Options:
+Affected by the
+.OP paragraph
+option.
+.SE
+.KY ~
+.IP "[count] ~"
+Reverse the case of the next
+.LI count
+character(s).
+This is the historic semantic for the
+.CO ~
+command and it is only in effect if the
+.OP tildeop
+option is not set.
+.sp
+Lowercase alphabetic characters are changed to uppercase,
+and uppercase characters are changed to lowercase.
+No other characters are affected.
+.sp
+Historically, the
+.CO ~
+command did not take an associated count, nor did it move past the
+end of the current line.
+As it had no associated motion it was difficult to change the case
+of large blocks of text.
+In
+.CO nvi ,
+if the cursor is on the last character of a line, and there are
+more lines in the file, the cursor moves to the next line.
+.sp
+It is not an error to specify a count larger than the number of
+characters between the cursor and the end of the file.
+.SS
+.SP Line:
+Set to the line of the character after
+.LI count
+characters, or, end of file.
+.SP Column:
+Set to the character after
+.LI count
+characters, or, end-of-file.
+.SP Options:
+Affected by the
+.OP tildeop
+option.
+.SE
+.KY ~
+.IP "[count] ~ motion"
+Reverse the case of the characters in a text region specified by the
+.LI count
+and
+.LI motion .
+Only in effect if the
+.OP tildeop
+option is set.
+.sp
+Lowercase characters are changed to uppercase,
+and uppercase characters are changed to lowercase.
+No other characters are affected.
+.SS
+.SP Line:
+Set to the line of the character after the last character in the region.
+.SP Column:
+Set to the character after the last character in the region.
+.SP Options:
+Affected by the
+.OP tildeop
+option.
+.SE
+.KY <interrupt>
+.IP "<interrupt>"
+Interrupt the current operation.
+Many of the potentially long-running
+.CO vi
+commands may be interrupted using the terminal interrupt character.
+These operations include searches, file reading and writing, filter
+operations and map character expansion.
+Interrupts are also enabled when running commands outside of
+.CO vi .
+.sp
+If the
+.LI <interrupt>
+character is used to interrupt while entering an
+.CO ex
+command, the command is aborted, the cursor returns to its previous
+position, and
+.CO vi
+remains in command mode.
+.sp
+Generally, if the
+.LI <interrupt>
+character is used to interrupt any
+operation, any changes made before the interrupt are left in place.
+.SS
+.SP Line:
+Dependent on the operation being interrupted.
+.SP Column:
+Dependent on the operation being interrupted.
+.SP Options:
+None.
+.SH 1 "Vi Text Input Commands"
+.pp
+The following section describes the commands available in the text
+input mode of the
+.CO vi
+editor.
+.pp
+Historically,
+.CO vi
+implementations only permitted the characters inserted on the current
+line to be erased.
+In addition, only the
+.LI <control-D>
+erase character and the
+.QT 0<control-D>
+and
+.QT ^<control-D>
+erase strings could erase autoindent characters.
+This implementation permits erasure to continue past the beginning
+of the current line, and back to where text input mode was entered.
+In addition, autoindent characters may be erased using the standard
+erase characters.
+For the line and word erase characters, reaching the autoindent
+characters forms a
+.QQ soft
+boundary, denoting the end of the current word or line erase.
+Repeating the word or line erase key will erase the autoindent characters.
+.pp
+Historically,
+.CO vi
+always used
+.LI <control-H>
+and
+.LI <control-W>
+as character and word erase characters, respectively, regardless of
+the current terminal settings.
+This implementation accepts, in addition to these two characters,
+the current terminal characters for those operations.
+.KY <nul>
+.IP "<nul>"
+If the first character of the input is a
+.LI <nul> ,
+the previous input is replayed, as if just entered.
+.KY <control-D>
+.IP "<control-D>"
+If the previous character on the line was an autoindent character,
+erase it.
+Otherwise, if the user is entering the first character in the line,
+.LI <control-D>
+is ignored.
+Otherwise, a literal
+.LI <control-D>
+character is entered.
+.KY ^<control-D>
+.IP "^<control-D>"
+If the previous character on the line was an autoindent character,
+erase all of the autoindent characters on the line.
+In addition, the autoindent level is reset to 0.
+.KY 0<control-D>
+.IP "0<control-D>"
+If the previous character on the line was an autoindent character,
+erase all of the autoindent characters on the line.
+.KY <control-T>
+.IP "<control-T>"
+Insert sufficient
+.LI <tab>
+and
+.LI <space>
+characters to move the cursor forward to a column immediately
+after the next column which is an even multiple of the
+.OP shiftwidth
+option.
+.sp
+Historically,
+.CO vi
+did not permit the
+.LI <control-T>
+command to be used unless the cursor was at the first column of a new
+line or it was preceded only by autoindent characters.
+.CO Nvi
+permits it to be used at any time during insert mode.
+.KY <erase>
+.IP <erase>
+.KY <control-H>
+.Ip <control-H>
+Erase the last character.
+.KY "<literal next>"
+.IP "<literal next>"
+Quote the next character.
+The next character will not be mapped (see the
+.CO map
+command for more information)
+or interpreted specially.
+A carat
+.PQ ^
+character will be displayed immediately as a placeholder,
+but will be replaced by the next character.
+.KY <escape>
+.IP <escape>
+Resolve all text input into the file, and return to command mode.
+.KY "<line erase>"
+.IP "<line erase>"
+Erase the current line.
+.KY "<control-W>"
+.IP "<control-W>"
+.KY "<word erase>"
+.Ip "<word erase>"
+Erase the last word.
+The definition of word is dependent on the
+.OP altwerase
+and
+.OP ttywerase
+options.
+.KY "<control-X>"
+.IP "<control-X>[0-9A-Fa-f]*"
+Insert a character with the specified hexadecimal value into the text.
+.KY <interrupt>
+.IP "<interrupt>"
+Interrupt text input mode, returning to command mode.
+If the
+.LI <interrupt>
+character is used to interrupt inserting text into the file,
+it is as if the
+.LI <escape>
+character was used; all text input up to the interruption is
+resolved into the file.
diff --git a/usr.bin/vi/USD.doc/vi.ref/vi.ref b/usr.bin/vi/USD.doc/vi.ref/vi.ref
new file mode 100644
index 0000000..5058cc7
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/vi.ref
@@ -0,0 +1,1270 @@
+.\" Copyright (c) 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.ref 8.52 (Berkeley) 8/13/94
+.\"
+.\"
+.so ref.so
+.tp
+.(l C
+.ps 12
+.ft B
+Ex/Vi Reference Manual
+.ft
+.ps
+.sp
+.i "Keith Bostic"
+.sp
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, California 94720
+.sp 1
+\*(td
+.sp 3
+.i Abstract
+.sp
+.)l
+.(q
+.pp
+This document is the reference guide for the 4.4BSD
+implementations of
+.EV nex nvi ,
+which are reimplementations of the historic Berkeley
+.EV ex vi
+editors.
+.)q
+.sp 3
+.(l C
+.i Acknowledgements
+.)l
+.sp
+.(q
+.pp
+Bruce Englar encouraged the early development of the historic
+.EV ex vi
+editor.
+Peter Kessler helped bring sanity to version 2's command layout.
+Bill Joy wrote versions 1 and 2.0 through 2.7,
+and created the framework that users see in the present editor.
+Mark Horton added macros and other features and made
+.EV ex vi
+work on a large number of terminals and Unix systems.
+.pp
+.CO Nvi
+is originally derived from software contributed to the University of
+California, Berkeley by Steve Kirkendall, the author of the
+.CO vi
+clone
+.CO elvis .
+.pp
+IEEE Standard Portable Operating System Interface for Computer
+Environments (POSIX) 1003.2 style Regular Expression support was
+done by Henry Spencer.
+.pp
+The curses library was originally done by Ken Arnold.
+Scrolling and reworking for
+.CO nvi
+was done by Elan Amir.
+.pp
+The Institute of Electrical and Electronics Engineers has
+given us permission to reprint portions of their documentation.
+Portions of this document are reprinted and reproduced from
+IEEE Std 1003.2-1992, IEEE Standard Portable Operating
+System Interface for Computer Environments (POSIX),
+copyright 1992 by the Institute of Electrical and Electronics
+Engineers, Inc.
+.pp
+The financial support of UUNET Communications Services is gratefully
+acknowledged.
+.)q
+.sy echo -n >index
+.oh 'Nvi/Nex Reference''USD:13-%'
+.eh 'USD:13-%''Nvi/Nex Reference'
+.bp 3
+.SH 1 Description
+.pp
+.CO Vi
+is a screen oriented text editor.
+.CO Ex
+is a line-oriented text editor.
+.CO Ex
+and
+.CO vi
+are different interfaces to the same program,
+and it is possible to switch back and forth during an edit session.
+.CO View
+is the equivalent of using the
+.b \-R
+(read-only) option of
+.CO vi .
+.pp
+This reference manual is the one provided with the
+.EV nex nvi
+versions of the
+.EV ex vi
+text editors.
+.EV Nex nvi
+are intended as bug-for-bug compatible replacements for the original
+Fourth Berkeley Software Distribution (4BSD)
+.EV ex vi
+programs.
+This reference manual is accompanied by a traditional-style manual page.
+That manual page describes the functionality found in
+.EV ex vi
+in far less detail than the description here.
+In addition, it describes the system interface to
+.EV ex vi ,
+e.g. command line options, session recovery, signals,
+environmental variables, and similar things.
+.pp
+This reference is intended for users already familiar with
+.EV ex vi .
+Anyone else should almost certainly read a good tutorial on the
+editor first.
+If you are in an unfamiliar environment,
+and you absolutely have to get work done immediately,
+see the section entitled
+.QB "Fast Startup"
+in the manual page.
+It is probably enough to get you started.
+.pp
+There are a few features in
+.EV nex nvi
+that are not found in historic versions of
+.EV ex vi .
+Some of the more interesting of those features are briefly described
+in the section entitled
+.QB "Additional Features"
+near the end of this document.
+For the rest of this document,
+.EV nex nvi
+is used only when it is necessary to distinguish it from the historic
+implementations of
+.EV ex vi .
+.pp
+Future versions of this software will be periodically made available
+by anonymous ftp, and can be retrieved from
+.LI ftp.cs.berkeley.edu ,
+in the directory
+.LI ucb/4bsd .
+.SH 1 "Startup Information"
+.pp
+.EV Ex vi
+interprets one of two possible environmental variables and reads up to
+three of five possible files during startup.
+The variables and files are expected to contain
+.CO ex
+commands, not
+.CO vi
+commands.
+In addition, they are interpreted
+.i before
+the file to be edited is read, and therefore many
+.CO ex
+commands may not be used.
+Generally, any command that requires output to the screen or that
+needs a file upon which to operate, will cause an error if included
+in a startup file or environmental variable.
+.pp
+Because the
+.CO ex
+command set supported by
+.EV nex nvi
+is a superset of the command set supported by most historical
+implementations of
+.CO ex ,
+.EV nex nvi
+can use the startup files created for the historical implementations,
+but the converse may not be true.
+.pp
+If the
+.b \-s
+(the historic \- option)
+is specified, or if standard input is redirected from a file,
+all environmental variables and startup files are ignored.
+.pp
+Otherwise, startup files and environmental variables are handled
+in the following order:
+.np
+The file
+.LI /etc/vi.exrc
+is read,
+as long as it is owned by root or the effective user ID of the user.
+.np
+The environmental variable
+.LI NEXINIT
+(or the variable
+.LI EXINIT ,
+if
+.LI NEXINIT
+is not set) is interpreted.
+.np
+If neither
+.LI NEXINIT
+or
+.LI EXINIT
+was set, and the
+.LI HOME
+environmental variable is set, the file
+.LI $HOME/.nexrc
+(or the file
+.LI $HOME/.exrc ,
+if
+.LI $HOME/.nexrc
+does not exist) is read,
+as long as the effective user ID of the user is root or is the same as
+the owner of the file.
+.np
+If the
+.OP exrc
+option was turned on by one of the previous startup information
+sources, the file
+.LI \&.nexrc
+(or the file
+.LI \&.exrc ,
+if
+.LI \&.nexrc
+does not exist) is read, as long as the effective user ID of the user
+is the same as the owner of the file.
+.pp
+No startup file is read if it is writable by anyone other than its owner.
+.pp
+It is not an error for any of the startup environmental variables or files
+not to exist.
+.pp
+Once all environmental variables are interpreted,
+and all startup files are read,
+the first file to be edited is read in (or a temporary file is created).
+Then, any commands specified using the
+.b \-c
+option are executed, in the context of that file.
+.SH 1 Recovery
+.pp
+There is no recovery program for
+.EV nex nvi ,
+nor does
+.EV nex nvi
+run setuid.
+Recovery files are created readable and writable by the owner only.
+Users may recover any file which they can read,
+and the superuser may recover any edit session.
+.pp
+Edit sessions are backed by files in the directory named by the
+.OP recdir
+option (the directory
+.LI /var/tmp/vi.recover
+by default), and are named
+.QC vi.XXXXXX ,
+where
+.QC XXXXXX
+is a number related to the process ID.
+When a file is first modified,
+a second recovery file containing an email message for the user is created,
+and is named
+.QC recover.XXXXXX ,
+where, again,
+.QC XXXXXX
+is associated with the process ID.
+Both files are removed at the end of a normal edit session,
+but will remain if the edit session is abnormally terminated
+or the user runs the
+.CO ex
+.CO preserve
+command.
+.pp
+The
+.OP recdir
+option may be set in either the user's or system's startup information,
+changing the recovery directory.
+(Note, however, that if a memory based file system is used as the backup
+directory, each system reboot will delete all of the recovery files!
+The same caution applies to directories such as
+.LI /tmp
+which are cleared of their contents by a system reboot, or
+.LI /usr/tmp
+which is periodically cleared of old files on many systems.)
+.pp
+The recovery directory should be owned by root, or at least by a pseudo-user.
+In addition, if directory
+.QQ sticky-bit
+semantics are available, the directory should have the sticky-bit
+set so that files may only be removed by their owners.
+The recovery directory must be read, write, and executable by any user,
+i.e. mode 1777.
+.pp
+If the recovery directory does not exist,
+.EV ex vi
+will attempt to create it.
+This can result in the recovery directory being owned by a normal user,
+which means that that user will be able to remove other user's recovery
+and backup files.
+This is annoying, but is not a security issue as the user cannot
+otherwise access or modify the files.
+.pp
+The recovery file has all of the necessary information in it to enable the
+user to recover the edit session.
+In addition, it has all of the necessary email headers for
+.XR sendmail 8 .
+When the system is rebooted, all of the files in
+.LI /var/tmp/vi.recover
+named
+.QC recover.XXXXXX
+should be sent to their owners, by email, using the
+.b \-t
+option of
+.CO sendmail
+(or a similar mechanism in other mailers).
+If
+.EV ex vi
+receives a hangup (SIGHUP) signal, or the user executes the
+.CO ex
+.CO preserve
+command,
+.EV ex vi
+will automatically email the recovery information to the user.
+.pp
+If your system does not have the
+.CO sendmail
+utility (or a mailer program which supports its interface)
+the source file
+.LI nvi/common/recover.c
+will have to be modified to use your local mail delivery programs.
+Note, if
+.EV nex nvi
+is changed to use another mailer,
+it is important to remember that the owner of the file given to
+the mailer is the
+.EV nex nvi
+user, so nothing in the file should be trusted as it may have been
+modified in an effort to compromise the system.
+.pp
+Finally, the owner execute bit is set on backup files when they are
+created, and unset when they are first modified, e.g. backup files
+that have no associated email recovery file will have this bit set.
+(There is also a small window where empty files can be created and
+not yet have this bit set.
+This is due to the method in which the files are created.)
+Such files should be deleted when the system reboots.
+.pp
+A simple way to do this cleanup is to insert the following Bourne
+shell script into your
+.LI /etc/rc.local
+(or other startup) file.
+The script should work with the historic Bourne shell,
+a POSIX 1003.2 shell or the Korn shell.
+(A copy of this script is included as
+.LI nvi/install/recover.script
+in the
+.EV nex nvi
+distribution.)
+.sp
+.(b
+.ft C
+.so ../../install/recover.script
+.ft R
+.)b
+.sp
+.pp
+If you are not using the default value for the
+.OP recdir
+option, be sure to substitute the value you're using for the
+.LI RECDIR
+value in the recovery script.
+.pp
+If the path of your system's
+.CO sendmail
+program (or whatever mailer you're using) is not
+.LI /usr/lib/sendmail ,
+be sure to substitute the correct pathname for the
+.LI SENDMAIL
+value in the recovery script.
+Consult the manual page for details on recovering preserved or
+aborted editing sessions.
+.SH 1 "Sizing the Screen"
+.pp
+The size of the screen can be set in a number of ways.
+.EV Ex vi
+takes the following steps until values are obtained for both the
+number of rows and number of columns in the screen.
+.np
+If the environmental variable
+.LI LINES
+exists,
+it is used to specify the number of rows in the screen.
+.np
+If the environmental variable
+.LI COLUMNS
+exists,
+it is used to specify the number of columns in the screen.
+.np
+The TIOCGWINSZ
+.XR ioctl 2
+is attempted on the standard error file descriptor.
+.np
+The termcap entry (or terminfo entry on System V machines)
+is checked for the
+.QQ li
+entry (rows) and the
+.QQ co
+entry (columns).
+.np
+The number of rows is set to 24, and the number of columns is set to 80.
+.pp
+If a window change size signal (SIGWINCH) is received,
+the new window size is retrieved using the TIOCGWINSZ
+.XR ioctl 2
+call, and all other information is ignored.
+.SH 1 "Character Display"
+.pp
+In both
+.CO ex
+and
+.CO vi
+printable characters as defined by
+.XR isprint 3
+are displayed using the local character set.
+.pp
+Non-printable characters, for which
+.XR iscntrl 3
+returns true, and which are less than octal \e076,
+are displayed as the string
+.QT ^<character> ,
+where
+.LI <character>
+is the character that is the original character's value offset from the
+.QT @
+character.
+For example, the octal character \e001 is displayed as
+.QT ^A .
+If
+.XR iscntrl 3
+returns true for the octal character \e177,
+it is displayed as the string
+.QT ^? .
+All other characters are displayed as either hexadecimal values,
+in the form
+.QT "0x<high-halfbyte> ... 0x<low-halfbyte>" ,
+or as octal values, in the form
+.QT "\e<high-one-or-two-bits> ... \e<low-three-bits>" .
+The display of unknown characters is based on the value of the
+.OP octal
+option.
+.pp
+In
+.CO vi
+command mode, the cursor is always positioned on the last column of
+characters which take up more than one column on the screen.
+In
+.CO vi
+text input mode, the cursor is positioned on the first column of
+characters which take up more than one column on the screen.
+.SH 1 "Multiple Screens"
+.pp
+.CO Nvi
+supports multiple screens by dividing the window into regions.
+It also supports stacks of screens by permitting the user to change
+the set of screens that are currently displayed.
+.pp
+The command
+.CO split
+divides the current screen into two regions of approximately equal
+size.
+If a list of files are specified as arguments to the
+.CO split
+command, the list of files to be edited is initialized as if the
+.CO next
+command had been used.
+If no files are specified, the new screen will begin by editing the same
+file as the previous screen.
+.pp
+When more than one screen is editing a file, changes in any screen are
+reflected in all other screens editing the same file.
+Exiting any screen without saving any changes (or explicitly discarding
+them) is permitted until the last screen editing the file is exited.
+.pp
+The
+.CO resize
+command permits resizing of individual screens.
+Screens may be grown, shrunk or set to an absolute number of rows.
+.pp
+The
+.CO ^W
+command is used to switch between screens.
+Each
+.CO ^W
+moves to the next lower screen in the window, or to the first screen
+in the window if there are no lower screens.
+.pp
+The
+.CO bg
+command
+.QQ backgrounds
+the current screen.
+The screen disappears from the window,
+and the rows it occupied are taken over by a neighboring screen.
+It is an error to attempt to background the only screen in the window.
+.pp
+The
+.CO "display screens"
+command displays the names of the files associated with the current
+backgrounded screens in the window.
+.pp
+The
+.CO "fg [file]"
+command
+.QQ foregrounds
+the first screen in the list of backgrounded screens that is
+associated with its argument.
+If no file argument is specified, the first screen on the list is
+foregrounded.
+Foregrounding consists of backgrounding the current screen,
+and replacing its space in the window with the foregrounded screen.
+.pp
+If the last screen in the window is exited, and there are backgrounded
+screens, the first screen on the list of backgrounded screens takes over
+the window.
+.SH 1 "Regular Expressions and Replacement Strings"
+.pp
+Regular expressions are used in line addresses,
+as the first part of the
+.CO ex
+.CO substitute ,
+.CO global ,
+and
+.CO vglobal
+commands, and in search patterns.
+.pp
+The regular expressions supported by
+.EV ex vi
+are, by default, the Basic Regular Expressions (BRE's) described in the
+IEEE POSIX Standard 1003.2.
+The
+.OP extended
+option causes all regular expressions to be interpreted as the Extended
+Regular Expressions (ERE's) described by the same standard.
+(See
+.XR re_format 7
+for more information.)
+Generally speaking, BRE's are the Regular Expressions found in
+.XR ed 1
+and
+.XR grep 1 ,
+and ERE's are the Regular Expressions found in
+.XR egrep 1 .
+.pp
+The following is not intended to provide a description of Regular
+Expressions.
+The information here only describes strings and characters which
+have special meanings in the
+.EV ex vi
+version of RE's,
+or options which change the meanings of characters that normally
+have special meanings in RE's.
+.np
+An empty RE (e.g.
+.QT //
+or
+.QT ??
+is equivalent to the last RE used.
+.np
+The construct
+.QT \e<
+matches the beginning of a word.
+.np
+The construct
+.QT \e>
+matches the end of a word.
+.np
+The character
+.QT ~
+matches the replacement part of the last
+.CO substitute
+command.
+.pp
+When the
+.OP magic
+option is
+.i not
+set, the only characters with special meanings are a
+.QT ^
+character at the beginning of an RE, a
+.QT $
+character at the end of an RE, and the escaping character
+.QT \e .
+The characters
+.QT \&. ,
+.QT * ,
+.QT [
+and
+.QT ~
+are treated as ordinary characters unless preceded by a
+.QT \e ;
+when preceded by a
+.QT \e
+they regain their special meaning.
+.pp
+Replacement strings are the second part of a
+.CO substitute
+command.
+.pp
+The character
+.QT &
+(or
+.QT \e&
+if the
+.OP magic
+option is
+.i not
+set) in the replacement string stands for the text matched by the RE
+that is being replaced.
+The character
+.QT ~
+(or
+.QT \e~
+if the
+.OP magic
+option is
+.i not
+set) stands for the replacement part of the previous
+.CO substitute
+command.
+It is only valid after a
+.CO substitute
+command has been performed.
+.pp
+The string
+.QT \e# ,
+where
+.QT #
+is an integer value from 1 to 9, stands for the text matched by
+the portion of the RE enclosed in the
+.QT # 'th
+set of escaped parentheses, e.g.
+.QT \e(
+and
+.QT \e) .
+For example,
+.QT "s/abc\e(.*\e)def/\e1/"
+deletes the strings
+.QT abc
+and
+.QT def
+from the matched pattern.
+.pp
+The strings
+.QT \el ,
+.QT \eu ,
+.QT \eL
+and
+.QT \eU
+can be used to modify the case of elements in the replacement string.
+The string
+.QT \el
+causes the next character to be converted to lowercase;
+the string
+.QT \eu
+behaves similarly, but converts to uppercase
+(e.g.
+.LI s/abc/\eU&/
+replaces the string
+.LI abc
+with
+.LI ABC ).
+The strings
+.QT \eL
+causes characters up to the end of the string or the next occurrence
+of the strings
+.QT \ee
+or
+.QT \eE
+to be converted to lowercase;
+the string
+.QT \eU
+behaves similarly, but converts to uppercase.
+.pp
+If the entire replacement pattern is
+.QT % ,
+then the last replacement pattern is used again.
+.pp
+In
+.CO vi ,
+inserting a
+.LI <control-M>
+into the replacement string will cause
+the matched line to be split into two lines at that point.
+(The
+.LI <control-M>
+will be discarded.)
+.SH 1 "General Editor Description"
+.pp
+When
+.CO ex
+or
+.CO vi
+are executed,
+the text of a file is read (or a temporary file is created),
+and then all editing changes happen within the context of the
+copy of the file.
+.i "No changes affect the actual file until the file is written out" ,
+either using a write command or another command which is affected by the
+.OP autowrite
+option.
+.pp
+All files are locked (using the
+.XR flock 2
+or
+.XR fcntl 2
+interfaces) during the edit session,
+to avoid inadvertently making modifications to multiple copies of the file.
+If a lock cannot be obtained for a file because it is locked by another
+process, the edit session is read-only (as if the
+.OP readonly
+option or the
+.b \-R
+flag had been specified).
+If a lock cannot be obtained for other reasons, the edit session will
+continue, but the file status information
+(see the
+.CO <control-G>
+command) will reflect this fact.
+.pp
+Both
+.CO ex
+and
+.CO vi
+are modeful editors, i.e. they have two modes,
+.QQ command
+mode and
+.QQ "text input"
+mode.
+The former is intended to permit you to enter commands which modifies
+already existing text.
+The latter is intended to permit you to enter new text.
+When
+.CO ex
+first starts running, it is in command mode, and usually displays a prompt
+(see the
+.OP prompt
+option for more information).
+The prompt is a single colon
+.PQ :
+character.
+There are three commands that switch
+.CO ex
+into text input mode:
+.CO append ,
+.CO change
+and
+.CO insert .
+Once in input mode, entering a line containing only a single period
+.PQ \&.
+terminates text input mode and returns to command mode,
+where the prompt is redisplayed.
+.pp
+When
+.CO vi
+first starts running, it is in command mode as well.
+There are eleven commands that switch
+.CO vi
+into text input mode:
+.CO A ,
+.CO a ,
+.CO C ,
+.CO c ,
+.CO I ,
+.CO i ,
+.CO O ,
+.CO o ,
+.CO R ,
+.CO S
+and
+.CO s .
+Once in input mode, entering an
+.LI <escape>
+character terminates text input mode and returns to command mode.
+.pp
+The following words have special meanings in both the
+.CO ex
+and
+.CO vi
+command descriptions:
+.KY <interrupt>
+.IP <interrupt>
+The interrupt character is used to interrupt the current operation.
+Normally
+.LI <control-C> ,
+whatever character is set for the current terminal is used.
+.KY "<literal next>"
+.IP "<literal next>"
+The literal next character is used to escape the subsequent character
+from any special meaning.
+This character is always
+.LI <control-V> .
+If the terminal is not set up to do XON/XOFF flow control,
+then
+.LI <control-Q>
+is used to mean literal next as well.
+.KY "current pathname"
+.IP "current pathname"
+The pathname of the file currently being edited by vi.
+When the percent character
+.PQ %
+appears in a file name entered as part of an
+.CO ex
+command argument, it is replaced by the current pathname.
+(The
+.QT %
+character can be escaped by preceding it with a backslash.)
+.KY "alternate pathname"
+.IP "alternate pathname"
+The name of the last file name mentioned in an
+.CO ex
+command, or,
+the previous current pathname if the last file mentioned
+becomes the current file.
+When the hash mark character
+.PQ #
+appears in a file name entered as part of an
+.CO ex
+command argument, it is replaced by the alternate pathname.
+(The
+.QT #
+character can be escaped by preceding it with a backslash.)
+.KY buffer
+.IP buffer
+One of a number of named areas for saving copies of text.
+Commands that change or delete text can save the changed or deleted
+text into a specific buffer, for later use, if the command allows
+it (i.e. the
+.CO ex
+.CO change
+command cannot save the changed text in a named buffer).
+Buffers are named with a single character, preceded by a double quote,
+e.g.
+.LI """<character>" .
+Historic implementations of
+.EV ex vi
+limited
+.LI <character>
+to the alphanumeric characters;
+.EV nex nvi
+permits the use of any character.
+.sp
+Buffers named by uppercase characters are the same as buffers
+named by lowercase characters, e.g. the buffer named by the
+English character
+.QT A
+is the same as the buffer named by the character
+.QT a ,
+with the exception that, if the buffer contents are being changed (as
+with a text deletion or
+.CO vi
+.CO change
+command), the text is
+.i appended
+to the buffer, instead of replacing the current contents.
+.sp
+The buffers named by the numeric characters (in English,
+.QT 1
+through
+.QT 9 ),
+are special, in that if at least one line is changed or deleted in
+the file,
+(or a command changes or deletes a region that crosses a line boundary)
+a copy of the text is placed into the numeric buffer
+.QT 1 ,
+regardless of the user specifying another buffer in which to save it.
+Before this copy is done, the previous contents of buffer
+.QT 1
+are moved into buffer
+.QT 2 ,
+.QT 2
+into buffer
+.QT 3 ,
+and so on.
+The contents of buffer
+.QT 9
+are discarded.
+In
+.CO vi ,
+text may be explicitly stored into the numeric buffers.
+In this case, the buffer rotation described above occurs before the
+replacement of the buffer's contents.
+(Text cannot be explicitly stored into the numeric buffers in
+.CO ex
+because of ambiguities that this would cause in the
+.CO ex
+command syntax.)
+.sp
+When a
+.CO vi
+command synopsis shows both a
+.LI [buffer]
+and a
+.LI [count] ,
+they may be presented in any order.
+.sp
+Finally, all buffers are either
+.QQ line
+or
+.QQ character
+oriented.
+All
+.CO ex
+commands which store text into buffers are line oriented.
+Some
+.CO vi
+commands which store text into buffers are line oriented,
+and some are character oriented; the description for each applicable
+.CO vi
+command notes whether text copied into buffers using the command
+is line or character oriented.
+In addition, the
+.CO vi
+command
+.CO "display buffers"
+displays the current orientation for each buffer.
+Generally, the only importance attached to this orientation is that
+if the buffer is subsequently inserted into the text, line oriented
+buffers create new lines for each of the lines they contain, and
+character oriented buffers create new lines for any lines
+.i other
+than the first and last lines they contain.
+The first and last lines are inserted into the text at the current
+cursor position, becoming part of the current line.
+If there is more than one line in the buffer, however, the current
+line itself will be split.
+.KY "unnamed buffer"
+.IP "unnamed buffer"
+The unnamed buffer is a text storage area which is used by commands
+that take a buffer as an argument, when no buffer is specified by
+the user.
+There is no way to explicitly reference this buffer.
+.oh 'Nvi/Nex Reference (Vi Commands)''USD:13-%'
+.eh 'USD:13-%''Nvi/Nex Reference (Vi Commands)'
+.so vi.cmd.roff
+.oh 'Nvi/Nex Reference''USD:13-%'
+.eh 'USD:13-%''Nvi/Nex Reference'
+.SH 1 "Ex Addressing"
+.pp
+Addressing in
+.CO ex
+(and when
+.CO ex
+commands are executed from
+.CO vi )
+relates to the current line.
+In general, the current line is the last line affected by a command.
+The exact effect on the current line is discussed under the description
+of each command.
+When the file contains no lines, the current line is zero.
+.pp
+Addresses are constructed by one or more of the following methods:
+.np
+The address
+.QT \&.
+refers to the current line.
+.np
+The address
+.QT $
+refers to the last line of the file.
+.np
+The address
+.QT N ,
+where
+.LI N
+is a positive number, refers to the N-th line of the file.
+.np
+The address
+.QT '<character>
+or
+.QT `<character>
+refers to the line marked with the name
+.LI <character> .
+(See the
+.CO k
+or
+.CO m
+commands for more information on how to mark lines.)
+.np
+A regular expression (RE) enclosed by slashes
+.PQ /
+is an address,
+and it refers to the first line found by searching forward from the line
+.i after
+the current line toward the end of the file, and stopping at the
+first line containing a string matching the RE.
+(The trailing slash can be omitted at the end of the command line.)
+.sp
+If no RE is specified, i.e. the pattern is
+.QT // ,
+the last RE used in any command is used in the search.
+.sp
+If the
+.OP extended
+option is set, the RE is handled as an extended RE, not a basic RE.
+If the
+.OP wrapscan
+option is set, the search wraps around to the beginning of the file
+and continues up to and including the current line, so that the entire
+file is searched.
+.sp
+The form
+.QT \e/
+is accepted for historic reasons,
+and is identical to
+.QT // .
+.np
+An RE enclosed in question marks
+.PQ ?
+addresses the first line found by searching backward from the line
+.i preceding
+the current line, toward the beginning of the file and stopping at the
+first line containing a string matching the RE.
+(The trailing question mark can be omitted at the end of a command line.)
+.sp
+If no RE is specified, i.e. the pattern is
+.QT ?? ,
+the last RE used in any command is used in the search.
+.sp
+If the
+.OP extended
+option is set, the RE is handled as an extended RE, not a basic RE.
+If the
+.OP wrapscan
+option is set, the search wraps around from the beginning of the file to
+the end of the file and continues up to and including the current line,
+so that the entire file is searched.
+.sp
+The form
+.QT \e?
+is accepted for historic reasons, and is identical to
+.QT ?? .
+.np
+An address followed by a plus sign
+.PQ +
+or a minus sign
+.PQ -
+followed by a number is an offset address and refers to the address
+plus (or minus) the indicated number of lines.
+If the address is omitted, the addition or subtraction is done with
+respect to the current line.
+.np
+An address of
+.QT +
+or
+.QT \-
+followed by a number is an offset from the current line.
+For example,
+.QT \-5
+is the same as
+.QT \&.\-5 .
+.np
+An address ending with
+.QT +
+or
+.QT -
+has 1 added to or subtracted from the address, respectively.
+As a consequence of this rule and of the previous rule, the address
+.QT \-
+refers to the line preceding the current line.
+Moreover, trailing
+.QT +
+and
+.QT \-
+characters have a cumulative effect.
+For example,
+.QT ++\-++
+refers to the current line plus 3.
+.np
+A percent sign
+.PQ %
+is equivalent to the address range
+.QT 1,$ .
+.pp
+.CO Ex
+commands require zero, one, or two addresses.
+It is an error to specify an address to a command which requires zero
+addresses.
+.pp
+If the user provides more than the expected number of addresses to any
+.CO ex
+command, the first addresses specified are discarded.
+For example,
+.QT 1,2,3,5 print
+prints lines 3 through 5, because the
+.CO print
+command only takes two addresses.
+.pp
+The addresses in a range are separated from each other by a comma
+.PQ ,
+or a semicolon
+.PQ ; .
+In the latter case, the current line
+.PQ \&.
+is set to the first address, and only then is the second address calculated.
+This feature can be used to determine the starting line for forward and
+backward searches (see rules (5) and (6) above).
+The second address of any two-address sequence corresponds to a line that
+follows, in the file, the line corresponding to the first address.
+The first address must be less than or equal to the second address.
+The first address must be greater than or equal to the first line of the
+file, and the last address must be less than or equal to the last line
+of the file.
+.oh 'Nvi/Nex Reference (Ex Commands)''USD:13-%'
+.eh 'USD:13-%''Nvi/Nex Reference (Ex Commands)'
+.so ex.cmd.roff
+.oh 'Nvi/Nex Reference''USD:13-%'
+.eh 'USD:13-%''Nvi/Nex Reference'
+.so set.opt.roff
+.SH 1 "Additional Features in Nex/Nvi"
+.pp
+There are a few features in
+.EV nex nvi
+that are not found in historic versions of
+.EV ex vi .
+Some of the more interesting of those features are as follows:
+.IP "8-bit clean data, large lines, files"
+.EV Nex nvi
+will edit any format file.
+Line lengths are limited by available memory,
+and file sizes are limited by available disk space.
+The
+.CO vi
+text input mode command
+.CO <control-X>
+can insert any possible character value into the text.
+.IP "Split screens"
+The
+.CO split
+command divides the screen into multiple editing regions.
+The
+.CO <control-W>
+command rotates between the foreground screens.
+The
+.CO resize
+command can be used to grow or shrink a particular screen.
+.IP "Background and foreground screens"
+The
+.CO bg
+command backgrounds the current screen, and the
+.CO fg
+command foregrounds backgrounded screens.
+The
+.CO display
+command can be used to list the background screens.
+.\".IP "Shell screens"
+.\"The
+.\".CO ":sc[ript] [file ...]"
+.\"command runs a shell in the screen.
+.\"Editing is unchanged, with the exception that a \fC<carriage-return>\fP
+.\"enters the current line (stripped of any prompt) as input to the
+.\"shell.
+.IP "Tag stacks"
+Tags are now maintained in a stack.
+The
+.CO <control-T>
+command returns to the previous tag location.
+The
+.CO tagpop
+command returns to the most recent tag location by default, or,
+optionally to a specific tag number in the tag stack,
+or the most recent tag from a specified file.
+The
+.CO display
+command can be used to list the tags stack.
+The
+.CO tagtop
+command returns to the top of the tag stack.
+.IP "New displays"
+The
+.CO display
+command can be used to display the current buffers, the backgrounded
+screens, and the tags stack.
+.IP "Infinite undo"
+Changes made during an edit session may be rolled backward and forward.
+A
+.CO \&.
+command immediately after a
+.CO u
+command continues either forward or backward depending on whether the
+.CO u
+command was an undo or a redo.
+.IP "Usage information"
+The
+.CO exusage
+and
+.CO viusage
+commands provide usage information for all of the
+.CO ex
+and
+.CO vi
+commands by default, or, optionally, for a specific command or key.
+.IP "Extended Regular Expressions"
+The
+.CO extended
+option causes Regular Expressions to be interpreted as as Extended
+Regular Expressions, (i.e. \fIegrep\fP(1) style Regular Expressions).
+.IP "Word search"
+The
+.CO <control-A>
+command searches for the word referenced by the cursor.
+.IP "Number increment"
+The
+.CO \&#
+command increments or decrements the number referenced by the cursor.
+.IP "Previous file"
+The
+.CO previous
+command edits the previous file from the argument list.
+.IP "Left-right scrolling"
+The
+.CO leftright
+option causes
+.CO nvi
+to do left-right screen scrolling, instead of the traditional
+.CO vi
+line wrapping.
+.bp
+.SH 1 Index
+.lp
+.2c +0.5i 3
+.ta \n($luR
+.nf
+.so index.so
+.fi
+.bp 2
+.1c
+.ce 1
+\fB\s+2Table of Contents\s0\fP
+.sp
+.xp
diff --git a/usr.bin/vi/USD.doc/vi.ref/vi.ref.txt b/usr.bin/vi/USD.doc/vi.ref/vi.ref.txt
new file mode 100644
index 0000000..88a98c7
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vi.ref/vi.ref.txt
@@ -0,0 +1,5544 @@
+
+
+
+
+
+
+
+
+ EExx//VVii RReeffeerreennccee MMaannuuaall
+
+ _K_e_i_t_h _B_o_s_t_i_c
+
+ Computer Science Division
+ Department of Electrical Engineering and Computer Science
+ University of California, Berkeley
+ Berkeley, California 94720
+
+ August 15, 1994
+
+
+
+ _A_b_s_t_r_a_c_t
+
+
+
+
+ This document is the reference guide for the 4.4BSD
+implementations of nneexx/nnvvii, which are reimplementations
+of the historic Berkeley eexx/vvii editors.
+
+
+
+
+
+ _A_c_k_n_o_w_l_e_d_g_e_m_e_n_t_s
+
+
+
+
+ Bruce Englar encouraged the early development of
+the historic eexx/vvii editor. Peter Kessler helped bring
+sanity to version 2's command layout. Bill Joy wrote
+versions 1 and 2.0 through 2.7, and created the frame-
+work that users see in the present editor. Mark Horton
+added macros and other features and made eexx/vvii work on a
+large number of terminals and Unix systems.
+
+ NNvvii is originally derived from software contributed
+to the University of California, Berkeley by Steve Kirk-
+endall, the author of the vvii clone eellvviiss.
+
+ IEEE Standard Portable Operating System Interface
+for Computer Environments (POSIX) 1003.2 style Regular
+Expression support was done by Henry Spencer.
+
+ The curses library was originally done by Ken
+Arnold. Scrolling and reworking for nnvvii was done by
+Elan Amir.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The Institute of Electrical and Electronics Engi-
+neers has given us permission to reprint portions of
+their documentation. Portions of this document are
+reprinted and reproduced from IEEE Std 1003.2-1992, IEEE
+Standard Portable Operating System Interface for Comput-
+er Environments (POSIX), copyright 1992 by the Institute
+of Electrical and Electronics Engineers, Inc.
+
+ The financial support of UUNET Communications Ser-
+vices is gratefully acknowledged.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--33
+
+
+11.. DDeessccrriippttiioonn
+
+ VVii is a screen oriented text editor. EExx is a line-
+oriented text editor. EExx and vvii are different interfaces to
+the same program, and it is possible to switch back and
+forth during an edit session. VViieeww is the equivalent of
+using the --RR (read-only) option of vvii.
+
+ This reference manual is the one provided with the
+nneexx/nnvvii versions of the eexx/vvii text editors. NNeexx/nnvvii are
+intended as bug-for-bug compatible replacements for the
+original Fourth Berkeley Software Distribution (4BSD) eexx/vvii
+programs. This reference manual is accompanied by a tradi-
+tional-style manual page. That manual page describes the
+functionality found in eexx/vvii in far less detail than the
+description here. In addition, it describes the system
+interface to eexx/vvii, e.g. command line options, session
+recovery, signals, environmental variables, and similar
+things.
+
+ This reference is intended for users already familiar
+with eexx/vvii. Anyone else should almost certainly read a good
+tutorial on the editor first. If you are in an unfamiliar
+environment, and you absolutely have to get work done imme-
+diately, see the section entitled "FFaasstt SSttaarrttuupp" in the man-
+ual page. It is probably enough to get you started.
+
+ There are a few features in nneexx/nnvvii that are not found
+in historic versions of eexx/vvii. Some of the more interesting
+of those features are briefly described in the section enti-
+tled "AAddddiittiioonnaall FFeeaattuurreess" near the end of this document.
+For the rest of this document, nneexx/nnvvii is used only when it
+is necessary to distinguish it from the historic implementa-
+tions of eexx/vvii.
+
+ Future versions of this software will be periodically
+made available by anonymous ftp, and can be retrieved from
+ffttpp..ccss..bbeerrkkeelleeyy..eedduu, in the directory uuccbb//44bbssdd.
+
+22.. SSttaarrttuupp IInnffoorrmmaattiioonn
+
+ EExx/vvii interprets one of two possible environmental
+variables and reads up to three of five possible files dur-
+ing startup. The variables and files are expected to con-
+tain eexx commands, not vvii commands. In addition, they are
+interpreted _b_e_f_o_r_e the file to be edited is read, and there-
+fore many eexx commands may not be used. Generally, any com-
+mand that requires output to the screen or that needs a file
+upon which to operate, will cause an error if included in a
+startup file or environmental variable.
+
+ Because the eexx command set supported by nneexx/nnvvii is a
+superset of the command set supported by most historical
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--44 NNvvii//NNeexx RReeffeerreennccee
+
+
+implementations of eexx, nneexx/nnvvii can use the startup files
+created for the historical implementations, but the converse
+may not be true.
+
+ If the --ss (the historic - option) is specified, or if
+standard input is redirected from a file, all environmental
+variables and startup files are ignored.
+
+ Otherwise, startup files and environmental variables
+are handled in the following order:
+
+ (1) The file //eettcc//vvii..eexxrrcc is read, as long as it is owned
+ by root or the effective user ID of the user.
+
+ (2) The environmental variable NNEEXXIINNIITT (or the variable
+ EEXXIINNIITT, if NNEEXXIINNIITT is not set) is interpreted.
+
+ (3) If neither NNEEXXIINNIITT or EEXXIINNIITT was set, and the HHOOMMEE
+ environmental variable is set, the file $$HHOOMMEE//..nneexxrrcc
+ (or the file $$HHOOMMEE//..eexxrrcc, if $$HHOOMMEE//..nneexxrrcc does not
+ exist) is read, as long as the effective user ID of
+ the user is root or is the same as the owner of the
+ file.
+
+ (4) If the eexxrrcc option was turned on by one of the previ-
+ ous startup information sources, the file ..nneexxrrcc (or
+ the file ..eexxrrcc, if ..nneexxrrcc does not exist) is read, as
+ long as the effective user ID of the user is the same
+ as the owner of the file.
+
+ No startup file is read if it is writable by anyone
+other than its owner.
+
+ It is not an error for any of the startup environmental
+variables or files not to exist.
+
+ Once all environmental variables are interpreted, and
+all startup files are read, the first file to be edited is
+read in (or a temporary file is created). Then, any com-
+mands specified using the --cc option are executed, in the
+context of that file.
+
+33.. RReeccoovveerryy
+
+ There is no recovery program for nneexx/nnvvii, nor does
+nneexx/nnvvii run setuid. Recovery files are created readable and
+writable by the owner only. Users may recover any file
+which they can read, and the superuser may recover any edit
+session.
+
+ Edit sessions are backed by files in the directory
+named by the rreeccddiirr option (the directory
+//vvaarr//ttmmpp//vvii..rreeccoovveerr by default), and are named "vvii..XXXXXXXXXXXX",
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--55
+
+
+where "XXXXXXXXXXXX" is a number related to the process ID. When
+a file is first modified, a second recovery file containing
+an email message for the user is created, and is named
+"rreeccoovveerr..XXXXXXXXXXXX", where, again, "XXXXXXXXXXXX" is associated with
+the process ID. Both files are removed at the end of a nor-
+mal edit session, but will remain if the edit session is
+abnormally terminated or the user runs the eexx pprreesseerrvvee com-
+mand.
+
+ The rreeccddiirr option may be set in either the user's or
+system's startup information, changing the recovery direc-
+tory. (Note, however, that if a memory based file system is
+used as the backup directory, each system reboot will delete
+all of the recovery files! The same caution applies to
+directories such as //ttmmpp which are cleared of their contents
+by a system reboot, or //uussrr//ttmmpp which is periodically
+cleared of old files on many systems.)
+
+ The recovery directory should be owned by root, or at
+least by a pseudo-user. In addition, if directory "sticky-
+bit" semantics are available, the directory should have the
+sticky-bit set so that files may only be removed by their
+owners. The recovery directory must be read, write, and
+executable by any user, i.e. mode 1777.
+
+ If the recovery directory does not exist, eexx/vvii will
+attempt to create it. This can result in the recovery
+directory being owned by a normal user, which means that
+that user will be able to remove other user's recovery and
+backup files. This is annoying, but is not a security issue
+as the user cannot otherwise access or modify the files.
+
+ The recovery file has all of the necessary information
+in it to enable the user to recover the edit session. In
+addition, it has all of the necessary email headers for
+_s_e_n_d_m_a_i_l(8). When the system is rebooted, all of the files
+in //vvaarr//ttmmpp//vvii..rreeccoovveerr named "rreeccoovveerr..XXXXXXXXXXXX" should be sent
+to their owners, by email, using the --tt option of sseennddmmaaiill
+(or a similar mechanism in other mailers). If eexx/vvii
+receives a hangup (SIGHUP) signal, or the user executes the
+eexx pprreesseerrvvee command, eexx/vvii will automatically email the
+recovery information to the user.
+
+ If your system does not have the sseennddmmaaiill utility (or a
+mailer program which supports its interface) the source file
+nnvvii//ccoommmmoonn//rreeccoovveerr..cc will have to be modified to use your
+local mail delivery programs. Note, if nneexx/nnvvii is changed
+to use another mailer, it is important to remember that the
+owner of the file given to the mailer is the nneexx/nnvvii user,
+so nothing in the file should be trusted as it may have been
+modified in an effort to compromise the system.
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--66 NNvvii//NNeexx RReeffeerreennccee
+
+
+ Finally, the owner execute bit is set on backup files
+when they are created, and unset when they are first modi-
+fied, e.g. backup files that have no associated email recov-
+ery file will have this bit set. (There is also a small
+window where empty files can be created and not yet have
+this bit set. This is due to the method in which the files
+are created.) Such files should be deleted when the system
+reboots.
+
+ A simple way to do this cleanup is to insert the fol-
+lowing Bourne shell script into your //eettcc//rrcc..llooccaall (or other
+startup) file. The script should work with the historic
+Bourne shell, a POSIX 1003.2 shell or the Korn shell. (A
+copy of this script is included as
+nnvvii//iinnssttaallll//rreeccoovveerr..ssccrriipptt in the nneexx/nnvvii distribution.)
+
+
+ ## @@((##))rreeccoovveerr..ssccrriipptt 88..44 ((BBeerrkkeelleeyy)) 88//1133//9944
+ ##
+ ## RReeccoovveerr nnvvii eeddiittoorr ffiilleess::
+ RREECCDDIIRR==//vvaarr//ttmmpp//vvii..rreeccoovveerr
+ SSEENNDDMMAAIILL==//uussrr//lliibb//sseennddmmaaiill
+ eecchhoo ''RReeccoovveerriinngg nnvvii eeddiittoorr sseessssiioonnss..''
+
+ ## UUnnmmooddiiffiieedd nnvvii eeddiittoorr bbaacckkuupp ffiilleess aarree eeiitthheerr zzeerroo lleennggtthh oorr
+ ## hhaavvee tthhee eexxeeccuuttee bbiitt sseett.. DDeelleettee bbootthh ccaasseess..
+ vviibbaacckkuupp==``eecchhoo $$RREECCDDIIRR//vvii..**``
+ iiff [[ ""$$vviibbaacckkuupp"" !!== ""$$RREECCDDIIRR//vvii..**"" ]];; tthheenn
+ ffoorr ii iinn $$vviibbaacckkuupp;; ddoo
+ iiff tteesstt --xx $$ii --oo !! --ss $$ii;; tthheenn
+ rrmm $$ii
+ ffii
+ ddoonnee
+ ffii
+
+ ## IItt iiss ppoossssiibbllee ttoo ggeett iinnccoommpplleettee rreeccoovveerryy ffiilleess,, iiff tthhee eeddiittoorr
+ ## ccrraasshheess aatt tthhee rriigghhtt ttiimmee.. DDeelleettee aannyy rreeccoovveerryy ffiilleess wwiitthhoouutt
+ ## ccoorrrreessppoonnddiinngg bbaacckkuupp ffiilleess,, ootthheerrwwiissee sseenndd mmaaiill ttoo tthhee uusseerr..
+ vviirreeccoovveerryy==``eecchhoo $$RREECCDDIIRR//rreeccoovveerr..**``
+ iiff [[ ""$$vviirreeccoovveerryy"" !!== ""$$RREECCDDIIRR//rreeccoovveerr..**"" ]];; tthheenn
+ ffoorr ii iinn $$vviirreeccoovveerryy;; ddoo
+ rreeccffiillee==``aawwkk ''//^^XX--vvii--rreeccoovveerr--ppaatthh:://{{pprriinntt $$22}}'' << $$ii``
+ iiff tteesstt !! --nn $$rreeccffiillee --aa --ss $$rreeccffiillee;; tthheenn
+ $$SSEENNDDMMAAIILL --tt << $$ii
+ eellssee
+ rrmm $$ii
+ ffii
+ ddoonnee
+ ffii
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--77
+
+
+ If you are not using the default value for the rreeccddiirr
+option, be sure to substitute the value you're using for the
+RREECCDDIIRR value in the recovery script.
+
+ If the path of your system's sseennddmmaaiill program (or what-
+ever mailer you're using) is not //uussrr//lliibb//sseennddmmaaiill, be sure
+to substitute the correct pathname for the SSEENNDDMMAAIILL value in
+the recovery script. Consult the manual page for details on
+recovering preserved or aborted editing sessions.
+
+44.. SSiizziinngg tthhee SSccrreeeenn
+
+ The size of the screen can be set in a number of ways.
+EExx/vvii takes the following steps until values are obtained
+for both the number of rows and number of columns in the
+screen.
+
+ (1) If the environmental variable LLIINNEESS exists, it is
+ used to specify the number of rows in the screen.
+
+ (2) If the environmental variable CCOOLLUUMMNNSS exists, it is
+ used to specify the number of columns in the screen.
+
+ (3) The TIOCGWINSZ _i_o_c_t_l(2) is attempted on the standard
+ error file descriptor.
+
+ (4) The termcap entry (or terminfo entry on System V
+ machines) is checked for the "li" entry (rows) and
+ the "co" entry (columns).
+
+ (5) The number of rows is set to 24, and the number of
+ columns is set to 80.
+
+ If a window change size signal (SIGWINCH) is received,
+the new window size is retrieved using the TIOCGWINSZ
+_i_o_c_t_l(2) call, and all other information is ignored.
+
+55.. CChhaarraacctteerr DDiissppllaayy
+
+ In both eexx and vvii printable characters as defined by
+_i_s_p_r_i_n_t(3) are displayed using the local character set.
+
+ Non-printable characters, for which _i_s_c_n_t_r_l(3) returns
+true, and which are less than octal \076, are displayed as
+the string "^^<<cchhaarraacctteerr>>", where <<cchhaarraacctteerr>> is the charac-
+ter that is the original character's value offset from the
+"@@" character. For example, the octal character \001 is
+displayed as "^^AA". If _i_s_c_n_t_r_l(3) returns true for the octal
+character \177, it is displayed as the string "^^??". All
+other characters are displayed as either hexadecimal values,
+in the form "00xx<<hhiigghh--hhaallffbbyyttee>> ...... 00xx<<llooww--hhaallffbbyyttee>>", or as
+octal values, in the form "\\<<hhiigghh--oonnee--oorr--ttwwoo--bbiittss>> ......
+\\<<llooww--tthhrreeee--bbiittss>>". The display of unknown characters is
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--88 NNvvii//NNeexx RReeffeerreennccee
+
+
+based on the value of the ooccttaall option.
+
+ In vvii command mode, the cursor is always positioned on
+the last column of characters which take up more than one
+column on the screen. In vvii text input mode, the cursor is
+positioned on the first column of characters which take up
+more than one column on the screen.
+
+66.. MMuullttiippllee SSccrreeeennss
+
+ NNvvii supports multiple screens by dividing the window
+into regions. It also supports stacks of screens by permit-
+ting the user to change the set of screens that are cur-
+rently displayed.
+
+ The command sspplliitt divides the current screen into two
+regions of approximately equal size. If a list of files are
+specified as arguments to the sspplliitt command, the list of
+files to be edited is initialized as if the nneexxtt command had
+been used. If no files are specified, the new screen will
+begin by editing the same file as the previous screen.
+
+ When more than one screen is editing a file, changes in
+any screen are reflected in all other screens editing the
+same file. Exiting any screen without saving any changes
+(or explicitly discarding them) is permitted until the last
+screen editing the file is exited.
+
+ The rreessiizzee command permits resizing of individual
+screens. Screens may be grown, shrunk or set to an absolute
+number of rows.
+
+ The ^^WW command is used to switch between screens. Each
+^^WW moves to the next lower screen in the window, or to the
+first screen in the window if there are no lower screens.
+
+ The bbgg command "backgrounds" the current screen. The
+screen disappears from the window, and the rows it occupied
+are taken over by a neighboring screen. It is an error to
+attempt to background the only screen in the window.
+
+ The ddiissppllaayy ssccrreeeennss command displays the names of the
+files associated with the current backgrounded screens in
+the window.
+
+ The ffgg [[ffiillee]] command "foregrounds" the first screen in
+the list of backgrounded screens that is associated with its
+argument. If no file argument is specified, the first
+screen on the list is foregrounded. Foregrounding consists
+of backgrounding the current screen, and replacing its space
+in the window with the foregrounded screen.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--99
+
+
+ If the last screen in the window is exited, and there
+are backgrounded screens, the first screen on the list of
+backgrounded screens takes over the window.
+
+77.. RReegguullaarr EExxpprreessssiioonnss aanndd RReeppllaacceemmeenntt SSttrriinnggss
+
+ Regular expressions are used in line addresses, as the
+first part of the eexx ssuubbssttiittuuttee, gglloobbaall, and vvgglloobbaall com-
+mands, and in search patterns.
+
+ The regular expressions supported by eexx/vvii are, by
+default, the Basic Regular Expressions (BRE's) described in
+the IEEE POSIX Standard 1003.2. The eexxtteennddeedd option causes
+all regular expressions to be interpreted as the Extended
+Regular Expressions (ERE's) described by the same standard.
+(See _r_e___f_o_r_m_a_t(7) for more information.) Generally speak-
+ing, BRE's are the Regular Expressions found in _e_d(1) and
+_g_r_e_p(1), and ERE's are the Regular Expressions found in
+_e_g_r_e_p(1).
+
+ The following is not intended to provide a description
+of Regular Expressions. The information here only describes
+strings and characters which have special meanings in the
+eexx/vvii version of RE's, or options which change the meanings
+of characters that normally have special meanings in RE's.
+
+ (1) An empty RE (e.g. "////" or "????" is equivalent to the
+ last RE used.
+
+ (2) The construct "\\<<" matches the beginning of a word.
+
+ (3) The construct "\\>>" matches the end of a word.
+
+ (4) The character "~~" matches the replacement part of the
+ last ssuubbssttiittuuttee command.
+
+ When the mmaaggiicc option is _n_o_t set, the only characters
+with special meanings are a "^^" character at the beginning
+of an RE, a "$$" character at the end of an RE, and the
+escaping character "\\". The characters "..", "**", "[[" and
+"~~" are treated as ordinary characters unless preceded by a
+"\\"; when preceded by a "\\" they regain their special mean-
+ing.
+
+ Replacement strings are the second part of a ssuubbssttiittuuttee
+command.
+
+ The character "&&" (or "\\&&" if the mmaaggiicc option is _n_o_t
+set) in the replacement string stands for the text matched
+by the RE that is being replaced. The character "~~" (or
+"\\~~" if the mmaaggiicc option is _n_o_t set) stands for the replace-
+ment part of the previous ssuubbssttiittuuttee command. It is only
+valid after a ssuubbssttiittuuttee command has been performed.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--1100 NNvvii//NNeexx RReeffeerreennccee
+
+
+ The string "\\##", where "##" is an integer value from 1
+to 9, stands for the text matched by the portion of the RE
+enclosed in the "##"'th set of escaped parentheses, e.g.
+"\\((" and "\\))". For example, "ss//aabbcc\\((..**\\))ddeeff//\\11//" deletes
+the strings "aabbcc" and "ddeeff" from the matched pattern.
+
+ The strings "\\ll", "\\uu", "\\LL" and "\\UU" can be used to
+modify the case of elements in the replacement string. The
+string "\\ll" causes the next character to be converted to
+lowercase; the string "\\uu" behaves similarly, but converts
+to uppercase (e.g. ss//aabbcc//\\UU&&// replaces the string aabbcc with
+AABBCC). The strings "\\LL" causes characters up to the end of
+the string or the next occurrence of the strings "\\ee" or
+"\\EE" to be converted to lowercase; the string "\\UU" behaves
+similarly, but converts to uppercase.
+
+ If the entire replacement pattern is "%%", then the last
+replacement pattern is used again.
+
+ In vvii, inserting a <<ccoonnttrrooll--MM>> into the replacement
+string will cause the matched line to be split into two
+lines at that point. (The <<ccoonnttrrooll--MM>> will be discarded.)
+
+88.. GGeenneerraall EEddiittoorr DDeessccrriippttiioonn
+
+ When eexx or vvii are executed, the text of a file is read
+(or a temporary file is created), and then all editing
+changes happen within the context of the copy of the file.
+_N_o _c_h_a_n_g_e_s _a_f_f_e_c_t _t_h_e _a_c_t_u_a_l _f_i_l_e _u_n_t_i_l _t_h_e _f_i_l_e _i_s _w_r_i_t_t_e_n
+_o_u_t, either using a write command or another command which
+is affected by the aauuttoowwrriittee option.
+
+ All files are locked (using the _f_l_o_c_k(2) or _f_c_n_t_l(2)
+interfaces) during the edit session, to avoid inadvertently
+making modifications to multiple copies of the file. If a
+lock cannot be obtained for a file because it is locked by
+another process, the edit session is read-only (as if the
+rreeaaddoonnllyy option or the --RR flag had been specified). If a
+lock cannot be obtained for other reasons, the edit session
+will continue, but the file status information (see the
+<<ccoonnttrrooll--GG>> command) will reflect this fact.
+
+ Both eexx and vvii are modeful editors, i.e. they have two
+modes, "command" mode and "text input" mode. The former is
+intended to permit you to enter commands which modifies
+already existing text. The latter is intended to permit you
+to enter new text. When eexx first starts running, it is in
+command mode, and usually displays a prompt (see the pprroommpptt
+option for more information). The prompt is a single colon
+("::") character. There are three commands that switch eexx
+into text input mode: aappppeenndd, cchhaannggee and iinnsseerrtt. Once in
+input mode, entering a line containing only a single period
+("..") terminates text input mode and returns to command
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--1111
+
+
+mode, where the prompt is redisplayed.
+
+ When vvii first starts running, it is in command mode as
+well. There are eleven commands that switch vvii into text
+input mode: AA, aa, CC, cc, II, ii, OO, oo, RR, SS and ss. Once in
+input mode, entering an <<eessccaappee>> character terminates text
+input mode and returns to command mode.
+
+ The following words have special meanings in both the
+eexx and vvii command descriptions:
+
+<<iinntteerrrruupptt>>
+ The interrupt character is used to interrupt the cur-
+ rent operation. Normally <<ccoonnttrrooll--CC>>, whatever charac-
+ ter is set for the current terminal is used.
+
+<<lliitteerraall nneexxtt>>
+ The literal next character is used to escape the subse-
+ quent character from any special meaning. This charac-
+ ter is always <<ccoonnttrrooll--VV>>. If the terminal is not set
+ up to do XON/XOFF flow control, then <<ccoonnttrrooll--QQ>> is
+ used to mean literal next as well.
+
+ccuurrrreenntt ppaatthhnnaammee
+ The pathname of the file currently being edited by vi.
+ When the percent character ("%%") appears in a file name
+ entered as part of an eexx command argument, it is
+ replaced by the current pathname. (The "%%" character
+ can be escaped by preceding it with a backslash.)
+
+aalltteerrnnaattee ppaatthhnnaammee
+ The name of the last file name mentioned in an eexx com-
+ mand, or, the previous current pathname if the last
+ file mentioned becomes the current file. When the hash
+ mark character ("##") appears in a file name entered as
+ part of an eexx command argument, it is replaced by the
+ alternate pathname. (The "##" character can be escaped
+ by preceding it with a backslash.)
+
+bbuuffffeerr
+ One of a number of named areas for saving copies of
+ text. Commands that change or delete text can save the
+ changed or deleted text into a specific buffer, for
+ later use, if the command allows it (i.e. the eexx cchhaannggee
+ command cannot save the changed text in a named
+ buffer). Buffers are named with a single character,
+ preceded by a double quote, e.g. ""<<cchhaarraacctteerr>>. His-
+ toric implementations of eexx/vvii limited <<cchhaarraacctteerr>> to
+ the alphanumeric characters; nneexx/nnvvii permits the use of
+ any character.
+
+ Buffers named by uppercase characters are the same as
+ buffers named by lowercase characters, e.g. the buffer
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--1122 NNvvii//NNeexx RReeffeerreennccee
+
+
+ named by the English character "AA" is the same as the
+ buffer named by the character "aa", with the exception
+ that, if the buffer contents are being changed (as with
+ a text deletion or vvii cchhaannggee command), the text is
+ _a_p_p_e_n_d_e_d to the buffer, instead of replacing the cur-
+ rent contents.
+
+ The buffers named by the numeric characters (in
+ English, "11" through "99"), are special, in that if at
+ least one line is changed or deleted in the file, (or a
+ command changes or deletes a region that crosses a line
+ boundary) a copy of the text is placed into the numeric
+ buffer "11", regardless of the user specifying another
+ buffer in which to save it. Before this copy is done,
+ the previous contents of buffer "11" are moved into
+ buffer "22", "22" into buffer "33", and so on. The con-
+ tents of buffer "99" are discarded. In vvii, text may be
+ explicitly stored into the numeric buffers. In this
+ case, the buffer rotation described above occurs before
+ the replacement of the buffer's contents. (Text cannot
+ be explicitly stored into the numeric buffers in eexx
+ because of ambiguities that this would cause in the eexx
+ command syntax.)
+
+ When a vvii command synopsis shows both a [[bbuuffffeerr]] and a
+ [[ccoouunntt]], they may be presented in any order.
+
+ Finally, all buffers are either "line" or "character"
+ oriented. All eexx commands which store text into
+ buffers are line oriented. Some vvii commands which
+ store text into buffers are line oriented, and some are
+ character oriented; the description for each applicable
+ vvii command notes whether text copied into buffers using
+ the command is line or character oriented. In addi-
+ tion, the vvii command ddiissppllaayy bbuuffffeerrss displays the cur-
+ rent orientation for each buffer. Generally, the only
+ importance attached to this orientation is that if the
+ buffer is subsequently inserted into the text, line
+ oriented buffers create new lines for each of the lines
+ they contain, and character oriented buffers create new
+ lines for any lines _o_t_h_e_r than the first and last lines
+ they contain. The first and last lines are inserted
+ into the text at the current cursor position, becoming
+ part of the current line. If there is more than one
+ line in the buffer, however, the current line itself
+ will be split.
+
+uunnnnaammeedd bbuuffffeerr
+ The unnamed buffer is a text storage area which is used
+ by commands that take a buffer as an argument, when no
+ buffer is specified by the user. There is no way to
+ explicitly reference this buffer.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--1133
+
+
+99.. VVii DDeessccrriippttiioonn
+
+ VVii takes up the entire screen to display the edited
+file, except for the bottom line of the screen. The bottom
+line of the screen is used to enter eexx commands, and for vvii
+error and informational messages. If no other information
+is being displayed, the default display can show the current
+cursor row and cursor column, an indication of whether the
+file has been modified, and the current mode of the editor.
+See the rruulleerr, sshhoowwddiirrttyy and sshhoowwmmooddee options for more
+information.
+
+ Empty lines do not have any special representation on
+the screen, but lines on the screen that would logically
+come after the end of the file are displayed as a single
+tilde ("~~") character. To differentiate between empty lines
+and lines consisting of only whitespace characters, use the
+lliisstt option. Historically, implementations of vvii have also
+displayed some lines as single asterisk ("@@") characters.
+These were lines that were not correctly displayed, i.e.
+lines on the screen that did not correspond to lines in the
+file, or lines that did not fit on the current screen. NNvvii
+never displays lines in this fashion.
+
+ VVii is a modeful editor, i.e. it has two modes, "com-
+mand" mode and "text input" mode. When vvii first starts, it
+is in command mode. There are several commands that change
+vvii into text input mode. The <<eessccaappee>> character is used to
+resolve the text input into the file, and exit back into
+command mode. In vvii command mode, the cursor is always
+positioned on the last column of characters which take up
+more than one column on the screen. In vvii text insert mode,
+the cursor is positioned on the first column of characters
+which take up more than one column on the screen.
+
+ Generally, if the cursor line and cursor column are not
+on the screen, then the screen is scrolled (if the target
+cursor is close) or repainted (if the target cursor is far
+away) so that the cursor is on the screen. If the screen is
+scrolled, it is moved a minimal amount, and the cursor line
+will usually appear at the top or bottom of the screen. In
+the screen is repainted, the cursor line will appear in the
+center of the screen, unless the cursor is sufficiently
+close to the beginning or end of the file that this is not
+possible. If the lleeffttrriigghhtt option is set, the screen may be
+scrolled or repainted in a horizontal direction as well as
+in a vertical one.
+
+ A major difference between the historical vvii presenta-
+tion and nnvvii is in the scrolling and screen oriented posi-
+tion commands, <<ccoonnttrrooll--BB>>, <<ccoonnttrrooll--DD>>, <<ccoonnttrrooll--EE>>, <<ccoonn--
+ttrrooll--FF>>, <<ccoonnttrrooll--UU>>, <<ccoonnttrrooll--YY>>, HH, LL and MM. In histori-
+cal implementations of vvii, these commands acted on physical
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--1144 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+(as opposed to logical, or screen) lines. For lines that
+were sufficiently long in relation to the size of the
+screen, this meant that single line scroll commands might
+repaint the entire screen, scrolling or screen positioning
+command might not change the screen or move the cursor at
+all, and some lines simply could not be displayed, even
+though vvii would edit the file that contained them. In nnvvii,
+these commands act on logical, i.e. screen lines. You are
+unlikely to notice any difference unless you are editing
+files with lines significantly longer than a screen width.
+
+ VVii keeps track of the currently "most attractive" cur-
+sor position. Each command description (for commands that
+can change the current cursor position), specifies if the
+cursor is set to a specific location in the line, or if it
+is moved to the "most attractive cursor position". The lat-
+ter means that the cursor is moved to the cursor position
+that is vertically as close as possible to the current cur-
+sor position. If the current line is shorter than the cur-
+sor position vvii would select, the cursor is positioned on
+the last character in the line. (If the line is empty, the
+cursor is positioned on the first column of the line.) If a
+command moves the cursor to the most attractive position, it
+does not alter the current cursor position, and a subsequent
+movement will again attempt to move the cursor to that posi-
+tion. Therefore, although a movement to a line shorter than
+the currently most attractive position will cause the cursor
+to move to the end of that line, a subsequent movement to a
+longer line will cause the cursor to move back to the most
+attractive position.
+
+ In addition, the $$ command makes the end of each line
+the most attractive cursor position rather than a specific
+column.
+
+ Each vvii command described below notes where the cursor
+ends up after it is executed. This position is described in
+terms of characters on the line, i.e. "the previous charac-
+ter", or, "the last character in the line". This is to
+avoid needing to continually refer to on what part of the
+character the cursor rests.
+
+ The following words have special meaning for vvii com-
+mands.
+
+pprreevviioouuss ccoonntteexxtt
+ The position of the cursor before the command which
+ caused the last absolute movement was executed. Each
+ vvii command described in the next section that is con-
+ sidered an absolute movement is so noted. In addition,
+ specifying _a_n_y address to an eexx command is considered
+ an absolute movement.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--1155
+
+
+mmoottiioonn
+ A second vvii command can be used as an optional trailing
+ argument to the vvii !!, <<, >>, cc, dd, yy, and (depending on
+ the ttiillddeeoopp option) ~~ commands. This command indicates
+ the end of the region of text that's affected by the
+ command. The motion command may be either the command
+ character repeated (in which case it means the current
+ line) or a cursor movement command. In the latter
+ case, the region affected by the command is from the
+ starting or stopping cursor position which comes first
+ in the file, to immediately before the starting or
+ stopping cursor position which comes later in the file.
+ Commands that operate on lines instead of using begin-
+ ning and ending cursor positions operate on all of the
+ lines that are wholly or partially in the region. In
+ addition, some other commands become line oriented
+ depending on where in the text they are used. The com-
+ mand descriptions below note these special cases.
+
+ The following commands may all be used as motion compo-
+ nents for vvii commands:
+
+
+ <<ccoonnttrrooll--AA>> <<ccoonnttrrooll--HH>> <<ccoonnttrrooll--JJ>> <<ccoonnttrrooll--MM>>
+ <<ccoonnttrrooll--NN>> <<ccoonnttrrooll--PP>> <<ssppaaccee>> $$
+ %% ''<<cchhaarraacctteerr>> (( ))
+ ++ ,, -- //
+ 00 ;; ?? BB
+ EE FF GG HH
+ LL MM NN TT
+ WW [[[[ ]]]] ^^
+ __ ``<<cchhaarraacctteerr>> bb ee
+ ff hh jj kk
+ ll nn tt ww
+ {{ || }}
+
+
+ The optional count prefix available for some of the vvii
+ commands that take motion commands, or the count prefix
+ available for the vvii commands that are used as motion
+ components, may be included and is _a_l_w_a_y_s considered
+ part of the motion argument. For example, the commands
+ "cc22ww" and "22ccww" are equivalent, and the region affected
+ by the cc command is two words of text. In addition, if
+ the optional count prefix is specified for both the vvii
+ command and its motion component, the effect is multi-
+ plicative and is considered part of the motion argu-
+ ment. For example, the commands "44ccww" and "22cc22ww" are
+ equivalent, and the region affected by the cc command is
+ four words of text.
+
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--1166 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ccoouunntt
+ A positive number used as an optional argument to most
+ commands, either to give a size or a position (for dis-
+ play or movement commands), or as a repeat count (for
+ commands that modify text). The count argument is
+ always optional and defaults to 1 unless otherwise
+ noted in the command description.
+
+ When a vvii command synopsis shows both a [[bbuuffffeerr]] and
+ [[ccoouunntt]], they may be presented in any order.
+
+bbiiggwwoorrdd
+ A set of non-whitespace characters preceded and fol-
+ lowed by whitespace characters or the beginning or end
+ of the file or line.
+
+ Groups of empty lines (or lines containing only whites-
+ pace characters) are treated as a single bigword.
+
+wwoorrdd
+ Generally, in languages where it is applicable, vvii rec-
+ ognizes two kinds of words. First, a sequence of let-
+ ters, digits and underscores, delimited at both ends
+ by: characters other than letters, digits, or under-
+ scores; the beginning or end of a line; the beginning
+ or end of the file. Second, a sequence of characters
+ other than letters, digits, underscores, or whitespace
+ characters, delimited at both ends by: a letter, digit,
+ underscore, or whitespace character; the beginning or
+ end of a line; the beginning or end of the file.
+
+ Groups of empty lines (or lines containing only whites-
+ pace characters) are treated as a single word.
+
+ppaarraaggrraapphh
+ An area of text that begins with either the beginning
+ of a file, an empty line, or a section boundary, and
+ continues until either an empty line, section boundary,
+ or the end of the file.
+
+ Groups of empty lines (or lines containing only whites-
+ pace characters) are treated as a single paragraph.
+
+ Additional paragraph boundaries can be defined using
+ the ppaarraaggrraapphh option.
+
+sseeccttiioonn
+ An area of text that starts with the beginning of the
+ file or a line whose first character is an open brace
+ ("{{") and continues until the next section or the end
+ of the file.
+
+ Additional section boundaries can be defined using the
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--1177
+
+
+ sseeccttiioonnss option.
+
+sseenntteennccee
+ An area of text that begins with either the beginning
+ of the file or the first nonblank character following
+ the previous sentence, paragraph, or section boundary
+ and continues until the end of the file or a or a
+ period ("..") exclamation point ("!!") or question mark
+ ("??") character, followed by either an end-of-line or
+ two whitespace characters. Any number of closing
+ parentheses ("))"), brackets ("]]") or double-quote ("""")
+ characters can appear between the period, exclamation
+ point, or question mark and the whitespace characters
+ or end-of-line.
+
+ Groups of empty lines (or lines containing only whites-
+ pace characters) are treated as a single sentence.
+
+1100.. VVii CCoommmmaannddss
+
+ The following section describes the commands available
+in the command mode of the vvii editor. In each entry below,
+the tag line is a usage synopsis for the command character.
+In addition, the final line and column the cursor rests
+upon, and any options which affect the command are noted.
+
+[[ccoouunntt]] <<ccoonnttrrooll--AA>>
+ Search forward ccoouunntt times for the current word. The
+ current word begins at the first non-whitespace charac-
+ ter on or after the current cursor position, and
+ extends up to the next non-word character or the end of
+ the line. The search is literal, i.e. no characters in
+ the word have any special meaning in terms of Regular
+ Expressions. It is an error if no matching pattern is
+ found between the starting position and the end of the
+ file.
+
+ The <<ccoonnttrrooll--AA>> command is an absolute movement. The
+ <<ccoonnttrrooll--AA>> command may be used as the motion component
+ of other vvii commands, in which case any text copied
+ into a buffer is character oriented.
+
+ Line: Set to the line where the word is found.
+ Column: Set to the first character of the word.
+ Options: Affected by the eexxtteennddeedd, iiggnnoorreeccaassee and wwrraapp--
+ ssccaann options.
+
+[[ccoouunntt]] <<ccoonnttrrooll--BB>>
+ Page backward ccoouunntt screens. Two lines of overlap are
+ maintained by displaying the window starting at line
+ ((ttoopp__lliinnee -- ccoouunntt ** wwiinnddooww__ssiizzee)) ++ 22, where wwiinnddooww__ssiizzee
+ is the value of the wwiinnddooww option. (In the case of
+ split screens, this size is corrected to the current
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--1188 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ screen size.) This is an error if the movement is past
+ the beginning of the file.
+
+ The <<ccoonnttrrooll--BB>> command is an absolute movement.
+
+ Line: Set to the last line of text displayed on the
+ screen.
+ Column: Set to the first nonblank character of the
+ line.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--DD>>
+ Scroll forward ccoouunntt lines. If ccoouunntt is not specified,
+ scroll forward the number of lines specified by the
+ last <<ccoonnttrrooll--DD>> or <<ccoonnttrrooll--UU>> command. If this is
+ the first <<ccoonnttrrooll--DD>> or <<ccoonnttrrooll--UU>> command, scroll
+ forward half the number of lines in the screen. (In
+ the case of split screens, the default scrolling dis-
+ tance is corrected to half the current screen size.)
+ This is an error if the movement is past the end of the
+ file.
+
+ The <<ccoonnttrrooll--DD>> command is an absolute movement.
+
+ Line: Set to the current line plus the number of
+ lines scrolled.
+ Column: Set to the first nonblank character of the
+ line.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--EE>>
+ Scroll forward ccoouunntt lines, leaving the cursor on the
+ current line and column, if possible. This is an error
+ if the movement is past the end of the file.
+
+ Line: Unchanged unless the current line scrolls off
+ the screen, in which case it is set to the
+ first line on the screen.
+ Column: Unchanged unless the current line scrolls off
+ the screen, in which case it is set to the
+ most attractive cursor position.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--FF>>
+ Page forward ccoouunntt screens. Two lines of overlap are
+ maintained by displaying the window starting at line
+ ttoopp__lliinnee ++ ccoouunntt ** wwiinnddooww__ssiizzee -- 22, where wwiinnddooww__ssiizzee
+ is the value of the wwiinnddooww option. (In the case of
+ split screens, this size is corrected to the current
+ screen size.) This is an error if the movement is past
+ the end of the file.
+
+ The <<ccoonnttrrooll--FF>> command is an absolute movement.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--1199
+
+
+ Line: Set to the first line on the screen.
+ Column: Set to the first nonblank character of the
+ current line.
+ Options: None.
+
+<<ccoonnttrrooll--GG>>
+ Display the file information. The information includes
+ the current pathname, the current line, the number of
+ total lines in the file, the current line as a percent-
+ age of the total lines in the file, if the file has
+ been modified, was able to be locked, if the file's
+ name has been changed, and if the edit session is read-
+ only.
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: None.
+
+<<ccoonnttrrooll--HH>>
+[[ccoouunntt]] hh
+ Move the cursor back ccoouunntt characters in the current
+ line. This is an error if the cursor is on the first
+ character in the line.
+
+ The <<ccoonnttrrooll--HH>> and hh commands may be used as the
+ motion component of other vvii commands, in which case
+ any text copied into a buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the ccuurrrreenntt -- ccoouunntt character, or, the
+ first character in the line if ccoouunntt is
+ greater than or equal to the number of charac-
+ ters in the line before the cursor.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--JJ>>
+[[ccoouunntt]] <<ccoonnttrrooll--NN>>
+[[ccoouunntt]] jj
+ Move the cursor down ccoouunntt lines without changing the
+ current column. This is an error if the movement is
+ past the end of the file.
+
+ The <<ccoonnttrrooll--JJ>>, <<ccoonnttrrooll--NN>> and jj commands may be used
+ as the motion component of other vvii commands, in which
+ case any text copied into a buffer is line oriented.
+
+ Line: Set to the current line plus ccoouunntt.
+ Column: The most attractive cursor position.
+ Options: None.
+
+<<ccoonnttrrooll--LL>>
+<<ccoonnttrrooll--RR>>
+ Repaint the screen.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--2200 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--MM>>
+[[ccoouunntt]] ++
+ Move the cursor down ccoouunntt lines to the first nonblank
+ character of that line. This is an error if the move-
+ ment is past the end of the file.
+
+ The <<ccoonnttrrooll--MM>> and ++ commands may be used as the
+ motion component of other vvii commands, in which case
+ any text copied into a buffer is line oriented.
+
+ Line: Set to the current line plus ccoouunntt.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: None.
+
+[[ccoouunntt]] <<ccoonnttrrooll--PP>>
+[[ccoouunntt]] kk
+ Move the cursor up ccoouunntt lines, without changing the
+ current column. This is an error if the movement is
+ past the beginning of the file.
+
+ The <<ccoonnttrrooll--PP>> and kk commands may be used as the
+ motion component of other vvii commands, in which case
+ any text copied into a buffer is line oriented.
+
+ Line: Set to the current line minus count.
+ Column: The most attractive cursor position.
+ Options: None.
+
+<<ccoonnttrrooll--TT>>
+ Return to the most recent tag context. The <<ccoonnttrrooll--TT>>
+ command is an absolute movement.
+
+ Line: Set to the context of the previous tag com-
+ mand.
+ Column: Set to the context of the previous tag com-
+ mand.
+ Options: None.
+
+<<ccoonnttrrooll--UU>>
+ Scroll backward ccoouunntt lines. If ccoouunntt is not speci-
+ fied, scroll backward the number of lines specified by
+ the last <<ccoonnttrrooll--DD>> or <<ccoonnttrrooll--UU>> command. If this
+ is the first <<ccoonnttrrooll--DD>> or <<ccoonnttrrooll--UU>> command, scroll
+ backward half the number of lines in the screen. (In
+ the case of split screens, the default scrolling dis-
+ tance is corrected to half the current screen size.)
+ This is an error if the movement is past the beginning
+ of the file.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--2211
+
+
+ The <<ccoonnttrrooll--UU>> command is an absolute movement.
+
+ Line: Set to the current line minus the amount
+ scrolled.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: None.
+
+<<ccoonnttrrooll--WW>>
+ Switch to the next lower screen in the window, or, to
+ the first screen if there are no lower screens in the
+ window.
+
+ Line: Set to the previous cursor position in the
+ window.
+ Column: Set to the previous cursor position in the
+ window.
+ Options: None.
+
+<<ccoonnttrrooll--YY>>
+ Scroll backward ccoouunntt lines, leaving the current line
+ and column as is, if possible. This is an error if the
+ movement is past the beginning of the file.
+
+ Line: Unchanged unless the current line scrolls off
+ the screen, in which case it is set to the
+ last line of text displayed on the screen.
+ Column: Unchanged unless the current line scrolls off
+ the screen, in which case it is the most
+ attractive cursor position.
+ Options: None.
+
+<<ccoonnttrrooll--ZZ>>
+ Suspend the current editor session. If the file has
+ been modified since it was last completely written, and
+ the aauuttoowwrriittee option is set, the file is written before
+ the editor session is suspended. If this write fails,
+ the editor session is not suspended.
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: Affected by the aauuttoowwrriittee option.
+
+<<eessccaappee>>
+ Execute eexx commands or cancel partial commands. If an
+ eexx command is being entered (e.g. //, ??, :: or !!), the
+ command is executed. If a partial command has been
+ entered, e.g. or the command is cancelled. Otherwise,
+ it is an error.
+
+ Line: When an eexx command is being executed, the cur-
+ rent line is set as described for that com-
+ mand. Otherwise, unchanged.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--2222 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Column: When an eexx command is being executed, the cur-
+ rent column is set as described for that com-
+ mand. Otherwise, unchanged.
+ Options: None.
+
+<<ccoonnttrrooll--]]>>
+ Push a tag reference onto the tag stack. The tags
+ files (see the ttaaggss option for more information) are
+ searched for a tag matching the current word. The cur-
+ rent word begins at the first non-whitespace character
+ on or after the current cursor position, and extends up
+ to the next non-word character or the end of the line.
+ If a matching tag is found, the current file is dis-
+ carded and the file containing the tag reference is
+ edited.
+
+ If the current file has been modified since it was last
+ completely written, the command will fail. The <<ccoonn--
+ ttrrooll--]]>> command is an absolute movement.
+
+ Line: Set to the line containing the matching tag
+ string.
+ Column: Set to the start of the matching tag string.
+ Options: Affected by the ttaaggss and ttaagglleennggtthh options.
+
+<<ccoonnttrrooll--^^>>
+ Switch to the most recently edited file.
+
+ If the file has been modified since it was last com-
+ pletely written, and the aauuttoowwrriittee option is set, the
+ file is written out. If this write fails, the command
+ will fail. Otherwise, if the current file has been
+ modified since it was last completely written, the com-
+ mand will fail.
+
+ Line: Set to the line the cursor was on when the
+ file was last edited.
+ Column: Set to the column the cursor was on when the
+ file was last edited.
+ Options: Affected by the aauuttoowwrriittee option.
+
+[[ccoouunntt]] <<ssppaaccee>>
+[[ccoouunntt]] ll
+ Move the cursor forward ccoouunntt characters without chang-
+ ing the current line. This is an error if the cursor
+ is on the last character in the line.
+
+ The <<ssppaaccee>> and ll commands may be used as the motion
+ component of other vvii commands, in which case any text
+ copied into a buffer is character oriented. In addi-
+ tion, these commands may be used as the motion compo-
+ nents of other commands when the cursor is on the last
+ character in the line, without error.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--2233
+
+
+ Line: Unchanged.
+ Column: Set to the current character plus the next
+ ccoouunntt characters, or to the last character on
+ the line if ccoouunntt is greater than the number
+ of characters in the line after the current
+ character.
+ Options: None.
+
+[[ccoouunntt]] !! mmoottiioonn sshheellll--aarrgguummeenntt((ss))
+ Replace text with results from a shell command. Pass
+ the lines specified by the ccoouunntt and mmoottiioonn arguments
+ as standard input to the program named by the sshheellll
+ option, and replace those lines with the output (both
+ standard error and standard output) of that command.
+
+ After the motion is entered, vvii prompts for arguments
+ to the shell command.
+
+ Within those arguments, "%%" and "##" characters are
+ expanded to the current and alternate pathnames,
+ respectively. The "!!" character is expanded with the
+ command text of the previous !! or ::!! commands.
+ (Therefore, the command !!!! repeats the previous !!
+ command.) The special meanings of "%%", "##" and "!!"
+ can be overridden by escaping them with a backslash.
+ If no !! or ::!! command has yet been executed, it is an
+ error to use an unescaped "!!" character. The !! com-
+ mand does _n_o_t do shell expansion on the strings pro-
+ vided as arguments. If any of the above expansions
+ change the arguments the user entered, the command is
+ redisplayed at the bottom of the screen.
+
+ VVii then executes the program named by the sshheellll option,
+ with a --cc flag followed by the arguments (which are
+ bundled into a single argument).
+
+ The !! command is permitted in an empty file.
+
+ If the file has been modified since it was last com-
+ pletely written, the !! command will warn you.
+
+ Line: The first line of the replaced text.
+ Column: The first column of the replaced text.
+ Options: Affected by the sshheellll option.
+
+[[ccoouunntt]] ## ++||--||##
+ Increment or decrement the current number. The current
+ number begins at the first non-number character on or
+ before the current cursor position, or the beginning of
+ the line, and extends up to the first non-number char-
+ acter on or after the current cursor position or the
+ end of the line. If the trailing character is a ++, the
+ number is incremented by ccoouunntt. If the trailing
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--2244 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ character is a --, the number is decremented by ccoouunntt.
+ If the trailing character is a ##, the previous incre-
+ ment or decrement is repeated.
+
+ The format of the number (decimal, hexadecimal, and
+ octal, and leading 0's) is retained unless the new
+ value cannot be represented in the previous format.
+
+ Line: Unchanged.
+ Column: Set to the first character in the cursor word.
+ Options: None.
+
+[[ccoouunntt]] $$
+ Move the cursor to the end of a line. If ccoouunntt is
+ specified, the cursor moves down ccoouunntt -- 11 lines.
+
+ It is not an error to use the $$ command when the cursor
+ is on the last character in the line or when the line
+ is empty.
+
+ The $$ command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented, unless the cursor is at,
+ or before the first nonblank character in the line, in
+ which case it is line oriented. It is not an error to
+ use the $$ command as a motion component when the cursor
+ is on the last character in the line, although it is an
+ error when the line is empty.
+
+ Line: Set to the current line plus ccoouunntt minus 1.
+ Column: Set to the last character in the line.
+ Options: None.
+
+%%
+ Move to the matching character. The cursor moves to
+ the parenthesis or curly brace which _m_a_t_c_h_e_s the paren-
+ thesis or curly brace found at the current cursor posi-
+ tion or which is the closest one to the right of the
+ cursor on the line. It is an error to execute the %%
+ command on a line without a parenthesis or curly brace.
+ Historically, any ccoouunntt specified to the %% command was
+ ignored.
+
+ The %% command is an absolute movement. The %% command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting point of the
+ region is at or before the first nonblank character on
+ its line, and the ending point is at or after the last
+ nonblank character on its line, in which case it is
+ line oriented.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--2255
+
+
+ Line: Set to the line containing the matching char-
+ acter.
+ Column: Set to the matching character.
+ Options: None.
+
+&&
+ Repeat the previous substitution command on the current
+ line.
+
+ Historically, any ccoouunntt specified to the && command was
+ ignored.
+
+ Line: Unchanged.
+ Column: Unchanged if the cursor was on the last char-
+ acter in the line, otherwise, set to the first
+ nonblank character in the line.
+ Options: Affected by the eeddccoommppaattiibbllee, eexxtteennddeedd,
+ iiggnnoorreeccaassee and mmaaggiicc options.
+
+''<<cchhaarraacctteerr>>
+``<<cchhaarraacctteerr>>
+ Return to a context marked by the character <<cchhaarraacc--
+ tteerr>>. If <<cchhaarraacctteerr>> is the "''" or "``" character,
+ return to the previous context. If <<cchhaarraacctteerr>> is any
+ other character, return to the context marked by that
+ character (see the mm command for more information). If
+ the command is the '' command, only the line value is
+ restored, and the cursor is placed on the first non-
+ blank character of that line. If the command is the ``
+ command, both the line and column values are restored.
+
+ It is an error if the context no longer exists because
+ of line deletion. (Contexts follow lines that are
+ moved, or which are deleted and then restored.)
+
+ The '' and `` commands are both absolute movements. They
+ may be used as a motion component for other vvii com-
+ mands. For the '' command, any text copied into a
+ buffer is line oriented. For the `` command, any text
+ copied into a buffer is character oriented, unless it
+ both starts and stops at the first character in the
+ line, in which case it is line oriented. In addition,
+ when using the `` command as a motion component, com-
+ mands which move backward and started at the first
+ character in the line, or move forward and ended at the
+ first character in the line, are corrected to the last
+ character of the starting and ending lines, respec-
+ tively.
+
+ Line: Set to the line from the context.
+ Column: Set to the first nonblank character in the
+ line, for the '' command, and set to the con-
+ text's column for the `` command.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--2266 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Options: None.
+
+[[ccoouunntt]] ((
+ Back up ccoouunntt sentences.
+
+ The (( command is an absolute movement. The (( command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting and stopping
+ points of the region are the first character in the
+ line, in which case it is line oriented. In the latter
+ case, the stopping point of the region is adjusted to
+ be the end of the line immediately before it, and not
+ the original cursor position.
+
+ Line: Set to the line containing the beginning of
+ the sentence.
+ Column: Set to the first nonblank character of the
+ sentence.
+ Options: None.
+
+[[ccoouunntt]] ))
+ Move forward ccoouunntt sentences.
+
+ The )) command is an absolute movement. The )) command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting point of the
+ region is the first character in the line, in which
+ case it is line oriented. In the latter case, if the
+ stopping point of the region is also the first charac-
+ ter in the line, it is adjusted to be the end of the
+ line immediately before it.
+
+ Line: Set to the line containing the beginning of
+ the sentence.
+ Column: Set to the first nonblank character of the
+ sentence.
+ Options: None.
+
+[[ccoouunntt]] ,,
+ Reverse find character ccoouunntt times. Reverse the last
+ FF, ff, TT or tt command, searching the other way in the
+ line, ccoouunntt times.
+
+ The ,, command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the searched-for character.
+ Options: None.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--2277
+
+
+[[ccoouunntt]] --
+ Move to first nonblank of the previous line, ccoouunntt
+ times.
+
+ This is an error if the movement is past the beginning
+ of the file.
+
+ The -- command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is line oriented.
+
+ Line: Set to the current line minus ccoouunntt.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: None.
+
+[[ccoouunntt]] ..
+ Repeat the last vvii command that modified text. The
+ repeated command may be a command and motion component
+ combination. If ccoouunntt is specified, it replaces _b_o_t_h
+ the count specified for the repeated command, and, if
+ applicable, for the repeated motion component. If
+ ccoouunntt is not specified, the counts originally specified
+ to the command being repeated are used again.
+
+ As a special case, if the .. command is executed imme-
+ diately after the uu command, the change log is rolled
+ forward or backward, depending on the action of the uu
+ command.
+
+ Line: Set as described for the repeated command.
+ Column: Set as described for the repeated command.
+ Options: None.
+
+//RREE<<ccaarrrriiaaggee--rreettuurrnn>>
+//RREE// [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
+??RREE<<ccaarrrriiaaggee--rreettuurrnn>>
+??RREE?? [[ooffffsseett]]<<ccaarrrriiaaggee--rreettuurrnn>>
+NN
+nn
+ Search forward or backward for a regular expression.
+ The commands beginning with a slash ("//") character are
+ forward searches, the commands beginning with a ques-
+ tion mark ("??") are backward searches. VVii prompts
+ with the leading character on the last line of the
+ screen for a string. It then searches forward or back-
+ ward in the file for the next occurrence of the string,
+ which is interpreted as a Basic Regular Expression.
+
+ The // and ?? commands are absolute movements. They may
+ be used as the motion components of other vvii commands,
+ in which case any text copied into a buffer is charac-
+ ter oriented, unless the search started and ended on
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--2288 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ the first column of a line, in which case it is line
+ oriented. In addition, forward searches ending at the
+ first character of a line, and backward searches begin-
+ ning at the first character in the line, are corrected
+ to begin or end at the last character of the previous
+ line. (Note, forward and backward searches can occur
+ for both // and ?? commands, if the wwrraappssccaann option is
+ set.)
+
+ If an offset from the matched line is specified (i.e. a
+ trailing "//" or "??" character is followed by a signed
+ offset), the buffer will always be line oriented (e.g.
+ "//ssttrriinngg//++00" will always guarantee a line orientation).
+
+ The nn command repeats the previous search.
+
+ The NN command repeats the previous search, but in the
+ reverse direction.
+
+ Missing RE's (e.g. "////<<ccaarrrriiaaggee--rreettuurrnn>>", "//<<ccaarrrriiaaggee--
+ rreettuurrnn>>", "????<<ccaarrrriiaaggee--rreettuurrnn>>", or "??<<ccaarrrriiaaggee--
+ rreettuurrnn>>" search for the last search RE, in the indi-
+ cated direction.
+
+ Searches may be interrupted using the <<iinntteerrrruupptt>> char-
+ acter.
+
+ Line: Set to the line in which the match occurred.
+ Column: Set to the first character of the matched
+ string.
+ Options: Affected by the eeddccoommppaattiibbllee, eexxtteennddeedd,
+ iiggnnoorreeccaassee, mmaaggiicc, and wwrraappssccaann options.
+
+00
+ Move to the first character in the current line. It is
+ not an error to use the 00 command when the cursor is on
+ the first character in the line,
+
+ The 00 command may be used as the motion component of
+ other vvii commands, in which case it is an error if the
+ cursor is on the first character in the line.
+
+ Line: Unchanged.
+ Column: Set to the first character in the line.
+ Options: None.
+
+::
+ Execute an ex command. VVii prompts for an eexx command on
+ the last line of the screen, using a colon ("::") char-
+ acter. The command is terminated by a <<ccaarrrriiaaggee--
+ rreettuurrnn>>, <<nneewwlliinnee>> or <<eessccaappee>> character; all of these
+ characters may be escaped by using a <<lliitteerraall nneexxtt>>
+ character. The command is then executed.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--2299
+
+
+ If the eexx command writes to the screen, vvii will prompt
+ the user for a <<ccaarrrriiaaggee--rreettuurrnn>> before continuing when
+ the eexx command finishes. Large amounts of output from
+ the eexx command will be paged for the user, and the user
+ prompted for a <<ccaarrrriiaaggee--rreettuurrnn>> or <<ssppaaccee>> key to con-
+ tinue. In some cases, a quit (normally a "q" charac-
+ ter) or <<iinntteerrrruupptt>> may be entered to interrupt the eexx
+ command.
+
+ When the eexx command finishes, and the user is prompted
+ to resume visual mode, it is also possible to enter
+ another "::" character followed by another eexx command.
+
+ Line: The current line is set as described for the
+ eexx command.
+ Column: The current column is set as described for the
+ eexx command.
+ Options: None.
+
+[[ccoouunntt]] ;;
+ Repeat the last character find ccoouunntt times. The last
+ character find is one of the FF, ff, TT or tt commands.
+
+ The ;; command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the searched-for character.
+ Options: None.
+
+[[ccoouunntt]] << mmoottiioonn
+[[ccoouunntt]] >> mmoottiioonn
+ Shift lines left or right. Shift the number of lines
+ in the region specified by the motion component, times
+ ccoouunntt, left (for the << command) or right (for the >>
+ command) by the number of columns specified by the
+ sshhiiffttwwiiddtthh option. Only whitespace characters are
+ deleted when shifting left; once the first character in
+ the line contains a nonblank character, the sshhiifftt will
+ succeed, but the line will not be modified.
+
+ Line: Unchanged.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: Affected by the sshhiiffttwwiiddtthh option.
+
+@@ bbuuffffeerr
+ Execute a named buffer. Execute the named buffer as vvii
+ commands. The buffer may include eexx commands, too, but
+ they must be expressed as a :: command. If the buffer
+ is line oriented, <<nneewwlliinnee>> characters are logically
+ appended to each line of the buffer. If the buffer is
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--3300 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ character oriented, <<nneewwlliinnee>> characters are logically
+ appended to all but the last line in the buffer.
+
+ If the buffer name is "@@", or "**", then the last buffer
+ executed shall be used. It is an error to specify "@@@@"
+ or "****" if there were no buffer previous executions.
+ The text of a macro may contain an @@ command, and it is
+ possible to create infinite loops in this manner. (The
+ <<iinntteerrrruupptt>> character may be used to interrupt the
+ loop.)
+
+ Line: The current line is set as described for the
+ command(s).
+ Column: The current column is set as described for the
+ command(s).
+ Options: None.
+
+[[ccoouunntt]] AA
+ Enter input mode, appending the text after the end of
+ the line. If ccoouunntt is specified, the text is repeat-
+ edly input ccoouunntt -- 11 more times after input mode is
+ exited.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[ccoouunntt]] BB
+ Move backward ccoouunntt bigwords. Move the cursor backward
+ to the beginning of a bigword by repeating the follow-
+ ing algorithm: if the current position is at the begin-
+ ning of a bigword or the character at the current posi-
+ tion cannot be part of a bigword, move to the first
+ character of the preceding bigword. Otherwise, move to
+ the first character of the bigword at the current posi-
+ tion. If no preceding bigword exists on the current
+ line, move to the first character of the last bigword
+ on the first preceding line that contains a bigword.
+
+ The BB command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Set to the line containing the word selected.
+ Column: Set to the first character of the word
+ selected.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] CC
+ Change text from the current position to the end-of-
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--3311
+
+
+ line. If ccoouunntt is specified, the input text replaces
+ from the current position to the end-of-line, plus
+ ccoouunntt -- 11 subsequent lines.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[bbuuffffeerr]] DD
+ Delete text from the current position to the end-of-
+ line.
+
+ It is not an error to execute the DD command on an empty
+ line.
+
+ Line: Unchanged.
+ Column: Set to the character before the current char-
+ acter, or, column 1 if the cursor was on col-
+ umn 1.
+ Options: None.
+
+[[ccoouunntt]] EE
+ Move forward ccoouunntt end-of-bigwords. Move the cursor
+ forward to the end of a bigword by repeating the fol-
+ lowing algorithm: if the current position is the end of
+ a bigword or the character at that position cannot be
+ part of a bigword, move to the last character of the
+ following bigword. Otherwise, move to the last charac-
+ ter of the bigword at the current position. If no suc-
+ ceeding bigword exists on the current line, move to the
+ last character of the first bigword on the next follow-
+ ing line that contains a bigword.
+
+ The EE command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Set to the line containing the word selected.
+ Column: Set to the last character of the word
+ selected.
+ Options: None.
+
+[[ccoouunntt]] FF <<cchhaarraacctteerr>>
+ Search ccoouunntt times backward through the current line
+ for <<cchhaarraacctteerr>>.
+
+ The FF command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--3322 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Line: Unchanged.
+ Column: Set to the searched-for character.
+ Options: None.
+
+[[ccoouunntt]] GG
+ Move to line ccoouunntt, or the last line of the file if
+ ccoouunntt not specified.
+
+ The GG command is an absolute movement. The GG command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ line oriented.
+
+ Line: Set to ccoouunntt, if specified, otherwise, the
+ last line.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: None.
+
+[[ccoouunntt]] HH
+ Move to the screen line ccoouunntt -- 11 lines below the top
+ of the screen.
+
+ The HH command is an absolute movement. The HH command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ line oriented.
+
+ Line: Set to the line ccoouunntt -- 11 lines below the top
+ of the screen.
+ Column: Set to the first nonblank character of the
+ _s_c_r_e_e_n line.
+ Options: None.
+
+[[ccoouunntt]] II
+ Enter input mode, inserting the text at the beginning
+ of the line. If ccoouunntt is specified, the text input is
+ repeatedly input ccoouunntt -- 11 more times.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: None.
+
+[[ccoouunntt]] JJ
+ Join lines. If ccoouunntt is specified, ccoouunntt lines are
+ joined; a minimum of two lines are always joined,
+ regardless of the value of ccoouunntt.
+
+ If the current line ends with a whitespace character,
+ all whitespace is stripped from the next line. Other-
+ wise, if the next line starts with a open parenthesis
+ ("((") do nothing. Otherwise, if the current line ends
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--3333
+
+
+ with a question mark ("??"), period ("..") or exclama-
+ tion point ("!!"), insert two spaces. Otherwise, insert
+ a single space.
+
+ It is not an error to join lines past the end of the
+ file, i.e. lines that do not exist.
+
+ Line: Unchanged.
+ Column: Set to the character after the last character
+ of the next-to-last joined line.
+ Options: None.
+
+[[ccoouunntt]] LL
+ Move to the screen line ccoouunntt -- 11 lines above the bot-
+ tom of the screen.
+
+ The LL command is an absolute movement. The LL command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ line oriented.
+
+ Line: Set to the line ccoouunntt -- 11 lines above the bot-
+ tom of the screen.
+ Column: Set to the first nonblank character of the
+ _s_c_r_e_e_n line.
+ Options: None.
+
+ MM
+ Move to the screen line in the middle of the screen.
+
+ The MM command is an absolute movement. The MM command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ line oriented.
+
+ Historically, any ccoouunntt specified to the MM command was
+ ignored.
+
+ Line: Set to the line in the middle of the screen.
+ Column: Set to the first nonblank character of the
+ _s_c_r_e_e_n line.
+ Options: None.
+
+[[ccoouunntt]] OO
+ Enter input mode, appending text in a new line above
+ the current line. If ccoouunntt is specified, the text
+ input is repeatedly input ccoouunntt -- 11 more times.
+
+ Historically, any ccoouunntt specified to the OO command was
+ ignored.
+
+ Line: Set to the last line upon which characters
+ were entered.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--3344 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[bbuuffffeerr]] PP
+ Insert text from a buffer. Text from the buffer (the
+ unnamed buffer by default) is inserted before the cur-
+ rent column or, if the buffer is line oriented, before
+ the current line.
+
+ Line: Set to the lowest numbered line insert, if the
+ buffer is line oriented, otherwise unchanged.
+ Column: Set to the first nonblank character of the
+ appended text, if the buffer is line oriented,
+ otherwise, the last character of the appended
+ text.
+ Options: None.
+
+QQ
+ Exit vvii (or visual) mode and switch to eexx mode.
+
+ Line: Unchanged.
+ Column: No longer relevant.
+ Options: None.
+
+[[ccoouunntt]] RR
+ Enter input mode, replacing the characters in the cur-
+ rent line. If ccoouunntt is specified, the text input is
+ repeatedly input ccoouunntt -- 11 more times.
+
+ If the end of the current line is reached, no more
+ characters are replaced and any further characters
+ input are appended to the line.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[bbuuffffeerr]] [[ccoouunntt]] SS
+ Substitute ccoouunntt lines.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--3355
+
+
+[[ccoouunntt]] TT <<cchhaarraacctteerr>>
+ Search backward, ccoouunntt times, through the current line
+ for the character _a_f_t_e_r the specified <<cchhaarraacctteerr>>.
+
+ The TT command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the character _a_f_t_e_r the searched-for
+ character.
+ Options: None.
+
+UU
+ Restore the current line to its state before the cursor
+ last moved to it.
+
+ Line: Unchanged.
+ Column: The first character in the line.
+ Options: None.
+
+[[ccoouunntt]] WW
+ Move forward ccoouunntt bigwords. Move the cursor forward
+ to the beginning of a bigword by repeating the follow-
+ ing algorithm: if the current position is within a big-
+ word or the character at that position cannot be part
+ of a bigword, move to the first character of the next
+ bigword. If no subsequent bigword exists on the cur-
+ rent line, move to the first character of the first
+ bigword on the first following line that contains a
+ bigword.
+
+ The WW command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: The line containing the word selected.
+ Column: The first character of the word selected.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] XX
+ Delete ccoouunntt characters before the cursor. If the num-
+ ber of characters to be deleted is greater than or
+ equal to the number of characters to the beginning of
+ the line, all of the characters before the current cur-
+ sor position, to the beginning of the line, are
+ deleted.
+
+ Line: Unchanged.
+ Column: Set to the current character minus ccoouunntt, or
+ the first character if count is greater than
+ the number of characters in the line before
+ the cursor.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--3366 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] YY
+ Copy (or "yank") ccoouunntt lines into the specified buffer.
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: None.
+
+ZZZZ
+ Write the file and exit vvii. The file is only written
+ if it has been modified since the last complete write
+ of the file to any file.
+
+ The ZZZZ command will exit the editor after writing the
+ file, if there are no further files to edit. Entering
+ two "quit" commands (i.e. wwqq, qquuiitt, xxiitt or ZZZZ) in a
+ row will override this check and the editor will exit,
+ ignoring any files that have not yet been edited.
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: None.
+
+[[ccoouunntt]] [[[[
+ Back up ccoouunntt section boundaries.
+
+ The [[[[ command is an absolute movement. The [[[[ command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting position is
+ column 0, in which case it is line oriented.
+
+ This is an error if the movement is past the beginning
+ of the file.
+
+ Line: Set to the previous line that is ccoouunntt section
+ boundaries back, or the first line of the file
+ if no more section boundaries exist preceding
+ the current line.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: Affected by the sseeccttiioonnss option.
+
+[[ccoouunntt]] ]]]]
+ Move forward ccoouunntt section boundaries.
+
+ The ]]]] command is an absolute movement. The ]]]] command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting position is
+ column 0, in which case it is line oriented.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--3377
+
+
+ This is an error if the movement is past the end of the
+ file.
+
+ Line: Set to the line that is ccoouunntt section bound-
+ aries forward, or to the last line of the file
+ if no more section boundaries exist following
+ the current line.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: Affected by the sseeccttiioonnss option.
+
+^^
+ Move to first nonblank character on the current line.
+
+ The ^^ command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the first nonblank character of the
+ current line.
+ Options: None.
+
+[[ccoouunntt]] __
+ Move down ccoouunntt -- 11 lines, to the first nonblank char-
+ acter. The __ command may be used as the motion compo-
+ nent of other vvii commands, in which case any text
+ copied into a buffer is line oriented.
+
+ It is not an error to execute the __ command when the
+ cursor is on the first character in the line.
+
+ Line: The current line plus ccoouunntt -- 11.
+ Column: The first nonblank character in the line.
+ Options: None.
+
+[[ccoouunntt]] aa
+ Enter input mode, appending the text after the cursor.
+ If ccoouunntt is specified, the text input is repeatedly
+ input ccoouunntt -- 11 more times.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[ccoouunntt]] bb
+ Move backward ccoouunntt words. Move the cursor backward to
+ the beginning of a word by repeating the following
+ algorithm: if the current position is at the beginning
+ of a word, move to the first character of the preceding
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--3388 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ word. Otherwise, the current position moves to the
+ first character of the word at the current position.
+ If no preceding word exists on the current line, move
+ to the first character of the last word on the first
+ preceding line that contains a word.
+
+ The bb command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Set to the line containing the word selected.
+ Column: Set to the first character of the word
+ selected.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] cc mmoottiioonn
+ Change a region of text. If only part of a single line
+ is affected, then the last character being changed is
+ marked with a "$$". Otherwise, the region of text is
+ deleted, and input mode is entered.
+
+ If ccoouunntt is specified, it is applied to the mmoottiioonn.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[bbuuffffeerr]] [[ccoouunntt]] dd mmoottiioonn
+ Delete a region of text. If ccoouunntt is specified, it is
+ applied to the mmoottiioonn.
+
+ Line: Set to the line where the region starts.
+ Column: Set to the first character in the line after
+ the last character in the region. If no such
+ character exists, set to the last character
+ before the region.
+ Options: None.
+
+[[ccoouunntt]] ee
+ Move forward ccoouunntt end-of-words. Move the cursor for-
+ ward to the end of a word by repeating the following
+ algorithm: if the current position is the end of a
+ word, move to the last character of the following word.
+ Otherwise, move to the last character of the word at
+ the current position. If no succeeding word exists on
+ the current line, move to the last character of the
+ first word on the next following line that contains a
+ word.
+
+ The ee command may be used as the motion component of
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--3399
+
+
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Set to the line containing the word selected.
+ Column: Set to the last character of the word
+ selected.
+ Options: None.
+
+[[ccoouunntt]] ff <<cchhaarraacctteerr>>
+ Search forward, ccoouunntt times, through the rest of the
+ current line for <<cchhaarraacctteerr>>.
+
+ The ff command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the searched-for character.
+ Options: None.
+
+[[ccoouunntt]] ii
+ Enter input mode, inserting the text before the cursor.
+ If ccoouunntt is specified, the text input is repeatedly
+ input ccoouunntt -- 11 more times.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+mm <<cchhaarraacctteerr>>
+ Save the current context (line and column) as <<cchhaarraacc--
+ tteerr>>. The exact position is referred to by "``<<cchhaarraacc--
+ tteerr>>". The line is referred to by "''<<cchhaarraacctteerr>>".
+
+ Historically, <<cchhaarraacctteerr>> was restricted to lower-case
+ letters only, nnvvii permits the use of any character.
+
+ Line: Unchanged.
+ Column: Unchanged.
+ Options: None.
+
+[[ccoouunntt]] oo
+ Enter input mode, appending text in a new line under
+ the current line. If ccoouunntt is specified, the text
+ input is repeatedly input ccoouunntt -- 11 more times.
+
+ Historically, any ccoouunntt specified to the oo command was
+ ignored.
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--4400 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[bbuuffffeerr]] pp
+ Append text from a buffer. Text from the buffer (the
+ unnamed buffer by default) is appended after the cur-
+ rent column or, if the buffer is line oriented, after
+ the current line.
+
+ Line: Set to the first line appended, if the buffer
+ is line oriented, otherwise unchanged.
+ Column: Set to the first nonblank character of the
+ appended text if the buffer is line oriented,
+ otherwise, the last character of the appended
+ text.
+ Options: None.
+
+[[ccoouunntt]] rr <<cchhaarraacctteerr>>
+ Replace characters. The next ccoouunntt characters in the
+ line are replaced with <<cchhaarraacctteerr>>. Replacing charac-
+ ters with <<nneewwlliinnee>> characters results in creating new,
+ empty lines into the file.
+
+ If <<cchhaarraacctteerr>> is <<eessccaappee>>, the command is cancelled.
+
+ Line: Unchanged unless the replacement character is
+ a <<nneewwlliinnee>>, in which case it is set to the
+ current line plus ccoouunntt -- 11.
+ Column: Set to the last character replaced, unless the
+ replacement character is a <<nneewwlliinnee>>, in which
+ case the cursor is in column 1 of the last
+ line inserted.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] ss
+ Substitute ccoouunntt characters in the current line start-
+ ing with the current character.
+
+ Line: Set to the last line upon which characters
+ were entered.
+ Column: Set to the last character entered.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt, bbeeaauu--
+ ttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraappmmaarrggiinn
+ options.
+
+[[ccoouunntt]] tt <<cchhaarraacctteerr>>
+ Search forward, ccoouunntt times, through the current line
+ for the character immediately _b_e_f_o_r_e <<cchhaarraacctteerr>>.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--4411
+
+
+ The tt command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Unchanged.
+ Column: Set to the character _b_e_f_o_r_e the searched-for
+ character.
+ Options: None.
+
+uu
+ Undo the last change made to the file. If repeated,
+ the uu command alternates between these two states, and
+ is its own inverse. When used after an insert that
+ inserted text on more than one line, the lines are
+ saved in the numeric buffers.
+
+ The .. command, when used immediately after the uu com-
+ mand, causes the change log to be rolled forward or
+ backward, depending on the action of the uu command.
+
+ Line: Set to the position of the first line changed,
+ if the reversal affects only one line or rep-
+ resents an addition or change; otherwise, the
+ line preceding the deleted text.
+ Column: Set to the cursor position before the change
+ was made.
+ Options: None.
+
+[[ccoouunntt]] ww
+ Move forward ccoouunntt words. Move the cursor forward to
+ the beginning of a word by repeating the following
+ algorithm: if the current position is at the beginning
+ of a word, move to the first character of the next
+ word. If no subsequent word exists on the current
+ line, move to the first character of the first word on
+ the first following line that contains a word.
+
+ The ww command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented.
+
+ Line: Set to the line containing the word selected.
+ Column: Set to the first character of the word
+ selected.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] xx
+ Delete ccoouunntt characters. The deletion is at the cur-
+ rent character position. If the number of characters
+ to be deleted is greater than or equal to the number of
+ characters to the end of the line, all of the charac-
+ ters from the current cursor position to the end of the
+ line are deleted.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--4422 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Line: Unchanged.
+ Column: Unchanged unless the last character in the
+ line is deleted and the cursor is not already
+ on the first character in the line, in which
+ case it is set to the previous character.
+ Options: None.
+
+[[bbuuffffeerr]] [[ccoouunntt]] yy mmoottiioonn
+ Copy (or "yank") a text region specified by the ccoouunntt
+ and motion into a buffer. If ccoouunntt is specified, it is
+ applied to the mmoottiioonn.
+
+ Line: Unchanged, unless the region covers more than
+ a single line, in which case it is set to the
+ line where the region starts.
+ Column: Unchanged, unless the region covers more than
+ a single line, in which case it is set to the
+ character were the region starts.
+ Options: None.
+
+[[ccoouunntt11]] zz [[ccoouunntt22]] ttyyppee
+ Redraw the screen with a window ccoouunntt22 lines long, with
+ line ccoouunntt11 placed as specified by the ttyyppee character.
+ If ccoouunntt11 is not specified, it defaults to the current
+ line. If ccoouunntt22 is not specified, it defaults to the
+ current window size.
+
+ The following ttyyppee characters may be used:
+
+ + If ccoouunntt11 is specified, place the line ccoouunntt11
+ at the top of the screen. Otherwise, display
+ the screen after the current screen, similarly
+ to the <<ccoonnttrrooll--FF>> command.
+ <carriage-return>
+ Place the line ccoouunntt11 at the top of the
+ screen.
+ . Place the line ccoouunntt11 in the center of the
+ screen.
+ - Place the line ccoouunntt11 at the bottom of the
+ screen.
+ ^ If ccoouunntt11 is specified, place the line that is
+ at the top of the screen when ccoouunntt11 is at the
+ bottom of the screen, at the bottom of the
+ screen, i.e. display the screen before the
+ screen before ccoouunntt11. Otherwise, display the
+ screen before the current screen, similarly to
+ the <<ccoonnttrrooll--BB>> command.
+
+ Line: Set to ccoouunntt11 unless ccoouunntt11 is not specified
+ and the ttyyppee character was either "^^" or "++",
+ in which case it is set to the line before the
+ first line on the previous screen or the line
+ after the last line on the previous screen,
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--4433
+
+
+ respectively.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: None.
+
+[[ccoouunntt]] {{
+ Move backward ccoouunntt paragraphs.
+
+ The {{ command is an absolute movement. The {{ command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting character is
+ the first character on its line, in which case it is
+ line oriented.
+
+ Line: Set to the line containing the beginning of
+ the previous paragraph.
+ Column: Set to the first nonblank character in the
+ line.
+ Options: Affected by the ppaarraaggrraapphh option.
+
+[[ccoouunntt]] ||
+ Move to a specific _c_o_l_u_m_n position on the current line.
+
+ The || command may be used as the motion component of
+ other vvii commands, in which case any text copied into a
+ buffer is character oriented. It is an error to use
+ the || command as a motion component and for the cursor
+ not to move.
+
+ Line: Unchanged.
+ Column: Set to the character occupying the column
+ position identified by ccoouunntt, if the position
+ exists in the line. If the column length of
+ the current line is less than ccoouunntt, the cur-
+ sor is moved to the last character in the
+ line.
+ Options: None.
+
+[[ccoouunntt]] }}
+ Move forward ccoouunntt paragraphs.
+
+ The }} command is an absolute movement. The }} command
+ may be used as the motion component of other vvii com-
+ mands, in which case any text copied into a buffer is
+ character oriented, unless the starting character is at
+ or before any nonblank characters in its line, in which
+ case it is line oriented.
+
+ Line: Set to the line containing the beginning of
+ the next paragraph.
+ Column: Set to the first nonblank character in the
+ line.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--4444 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ Options: Affected by the ppaarraaggrraapphh option.
+
+[[ccoouunntt]] ~~
+ Reverse the case of the next ccoouunntt character(s). This
+ is the historic semantic for the ~~ command and it is
+ only in effect if the ttiillddeeoopp option is not set.
+
+ Lowercase alphabetic characters are changed to upper-
+ case, and uppercase characters are changed to lower-
+ case. No other characters are affected.
+
+ Historically, the ~~ command did not take an associated
+ count, nor did it move past the end of the current
+ line. As it had no associated motion it was difficult
+ to change the case of large blocks of text. In nnvvii, if
+ the cursor is on the last character of a line, and
+ there are more lines in the file, the cursor moves to
+ the next line.
+
+ It is not an error to specify a count larger than the
+ number of characters between the cursor and the end of
+ the file.
+
+ Line: Set to the line of the character after ccoouunntt
+ characters, or, end of file.
+ Column: Set to the character after ccoouunntt characters,
+ or, end-of-file.
+ Options: Affected by the ttiillddeeoopp option.
+
+[[ccoouunntt]] ~~ mmoottiioonn
+ Reverse the case of the characters in a text region
+ specified by the ccoouunntt and mmoottiioonn. Only in effect if
+ the ttiillddeeoopp option is set.
+
+ Lowercase characters are changed to uppercase, and
+ uppercase characters are changed to lowercase. No
+ other characters are affected.
+
+ Line: Set to the line of the character after the
+ last character in the region.
+ Column: Set to the character after the last character
+ in the region.
+ Options: Affected by the ttiillddeeoopp option.
+
+<<iinntteerrrruupptt>>
+ Interrupt the current operation. Many of the poten-
+ tially long-running vvii commands may be interrupted
+ using the terminal interrupt character. These opera-
+ tions include searches, file reading and writing, fil-
+ ter operations and map character expansion. Interrupts
+ are also enabled when running commands outside of vvii.
+
+ If the <<iinntteerrrruupptt>> character is used to interrupt while
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--4455
+
+
+ entering an eexx command, the command is aborted, the
+ cursor returns to its previous position, and vvii remains
+ in command mode.
+
+ Generally, if the <<iinntteerrrruupptt>> character is used to
+ interrupt any operation, any changes made before the
+ interrupt are left in place.
+
+ Line: Dependent on the operation being interrupted.
+ Column: Dependent on the operation being interrupted.
+ Options: None.
+
+ 1111.. VVii TTeexxtt IInnppuutt CCoommmmaannddss
+
+ The following section describes the commands
+ available in the text input mode of the vvii editor.
+
+ Historically, vvii implementations only permitted
+ the characters inserted on the current line to be
+ erased. In addition, only the <<ccoonnttrrooll--DD>> erase char-
+ acter and the "00<<ccoonnttrrooll--DD>>" and "^^<<ccoonnttrrooll--DD>>" erase
+ strings could erase autoindent characters. This imple-
+ mentation permits erasure to continue past the begin-
+ ning of the current line, and back to where text input
+ mode was entered. In addition, autoindent characters
+ may be erased using the standard erase characters. For
+ the line and word erase characters, reaching the
+ autoindent characters forms a "soft" boundary, denoting
+ the end of the current word or line erase. Repeating
+ the word or line erase key will erase the autoindent
+ characters.
+
+ Historically, vvii always used <<ccoonnttrrooll--HH>> and <<ccoonn--
+ ttrrooll--WW>> as character and word erase characters, respec-
+ tively, regardless of the current terminal settings.
+ This implementation accepts, in addition to these two
+ characters, the current terminal characters for those
+ operations.
+
+ <<nnuull>>
+ If the first character of the input is a <<nnuull>>,
+ the previous input is replayed, as if just
+ entered.
+
+ <<ccoonnttrrooll--DD>>
+ If the previous character on the line was an
+ autoindent character, erase it. Otherwise, if the
+ user is entering the first character in the line,
+ <<ccoonnttrrooll--DD>> is ignored. Otherwise, a literal
+ <<ccoonnttrrooll--DD>> character is entered.
+
+ ^^<<ccoonnttrrooll--DD>>
+ If the previous character on the line was an
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--4466 NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss))
+
+
+ autoindent character, erase all of the autoindent
+ characters on the line. In addition, the autoin-
+ dent level is reset to 0.
+
+ 00<<ccoonnttrrooll--DD>>
+ If the previous character on the line was an
+ autoindent character, erase all of the autoindent
+ characters on the line.
+
+ <<ccoonnttrrooll--TT>>
+ Insert sufficient <<ttaabb>> and <<ssppaaccee>> characters to
+ move the cursor forward to a column immediately
+ after the next column which is an even multiple of
+ the sshhiiffttwwiiddtthh option.
+
+ Historically, vvii did not permit the <<ccoonnttrrooll--TT>>
+ command to be used unless the cursor was at the
+ first column of a new line or it was preceded only
+ by autoindent characters. NNvvii permits it to be
+ used at any time during insert mode.
+
+ <<eerraassee>>
+ <<ccoonnttrrooll--HH>>
+ Erase the last character.
+
+ <<lliitteerraall nneexxtt>>
+ Quote the next character. The next character will
+ not be mapped (see the mmaapp command for more infor-
+ mation) or interpreted specially. A carat ("^^")
+ character will be displayed immediately as a
+ placeholder, but will be replaced by the next
+ character.
+
+ <<eessccaappee>>
+ Resolve all text input into the file, and return
+ to command mode.
+
+ <<lliinnee eerraassee>>
+ Erase the current line.
+
+ <<ccoonnttrrooll--WW>>
+ <<wwoorrdd eerraassee>>
+ Erase the last word. The definition of word is
+ dependent on the aallttwweerraassee and ttttyywweerraassee options.
+
+ <<ccoonnttrrooll--XX>>[[00--99AA--FFaa--ff]]**
+ Insert a character with the specified hexadecimal
+ value into the text.
+
+ <<iinntteerrrruupptt>>
+ Interrupt text input mode, returning to command
+ mode. If the <<iinntteerrrruupptt>> character is used to
+ interrupt inserting text into the file, it is as
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((VVii CCoommmmaannddss)) UUSSDD::1133--4477
+
+
+ if the <<eessccaappee>> character was used; all text input
+ up to the interruption is resolved into the file.
+
+ 1122.. EExx AAddddrreessssiinngg
+
+ Addressing in eexx (and when eexx commands are exe-
+ cuted from vvii) relates to the current line. In gen-
+ eral, the current line is the last line affected by a
+ command. The exact effect on the current line is dis-
+ cussed under the description of each command. When the
+ file contains no lines, the current line is zero.
+
+ Addresses are constructed by one or more of the
+ following methods:
+
+ (1) The address ".." refers to the current line.
+
+ (2) The address "$$" refers to the last line of the
+ file.
+
+ (3) The address "NN", where NN is a positive number,
+ refers to the N-th line of the file.
+
+ (4) The address "''<<cchhaarraacctteerr>>" or "``<<cchhaarraacctteerr>>"
+ refers to the line marked with the name <<cchhaarraacc--
+ tteerr>>. (See the kk or mm commands for more infor-
+ mation on how to mark lines.)
+
+ (5) A regular expression (RE) enclosed by slashes
+ ("//") is an address, and it refers to the first
+ line found by searching forward from the line
+ _a_f_t_e_r the current line toward the end of the
+ file, and stopping at the first line containing
+ a string matching the RE. (The trailing slash
+ can be omitted at the end of the command line.)
+
+ If no RE is specified, i.e. the pattern is "////",
+ the last RE used in any command is used in the
+ search.
+
+ If the eexxtteennddeedd option is set, the RE is handled
+ as an extended RE, not a basic RE. If the wwrraapp--
+ ssccaann option is set, the search wraps around to
+ the beginning of the file and continues up to
+ and including the current line, so that the
+ entire file is searched.
+
+ The form "\\//" is accepted for historic reasons,
+ and is identical to "////".
+
+ (6) An RE enclosed in question marks ("??")
+ addresses the first line found by searching
+ backward from the line _p_r_e_c_e_d_i_n_g the current
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--4488 NNvvii//NNeexx RReeffeerreennccee
+
+
+ line, toward the beginning of the file and stop-
+ ping at the first line containing a string
+ matching the RE. (The trailing question mark
+ can be omitted at the end of a command line.)
+
+ If no RE is specified, i.e. the pattern is "????",
+ the last RE used in any command is used in the
+ search.
+
+ If the eexxtteennddeedd option is set, the RE is handled
+ as an extended RE, not a basic RE. If the wwrraapp--
+ ssccaann option is set, the search wraps around
+ from the beginning of the file to the end of the
+ file and continues up to and including the cur-
+ rent line, so that the entire file is searched.
+
+ The form "\\??" is accepted for historic reasons,
+ and is identical to "????".
+
+ (7) An address followed by a plus sign ("++") or a
+ minus sign ("--") followed by a number is an off-
+ set address and refers to the address plus (or
+ minus) the indicated number of lines. If the
+ address is omitted, the addition or subtraction
+ is done with respect to the current line.
+
+ (8) An address of "++" or "--" followed by a number is
+ an offset from the current line. For example,
+ "--55" is the same as "..--55".
+
+ (9) An address ending with "++" or "--" has 1 added to
+ or subtracted from the address, respectively.
+ As a consequence of this rule and of the previ-
+ ous rule, the address "--" refers to the line
+ preceding the current line. Moreover, trailing
+ "++" and "--" characters have a cumulative effect.
+ For example, "++++--++++" refers to the current line
+ plus 3.
+
+ (10) A percent sign ("%%") is equivalent to the
+ address range "11,,$$".
+
+ EExx commands require zero, one, or two addresses.
+ It is an error to specify an address to a command which
+ requires zero addresses.
+
+ If the user provides more than the expected number
+ of addresses to any eexx command, the first addresses
+ specified are discarded. For example, "11,,22,,33,,55"print
+ prints lines 3 through 5, because the pprriinntt command
+ only takes two addresses.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--4499
+
+
+ The addresses in a range are separated from each
+ other by a comma (",,") or a semicolon (";;"). In the
+ latter case, the current line ("..") is set to the
+ first address, and only then is the second address cal-
+ culated. This feature can be used to determine the
+ starting line for forward and backward searches (see
+ rules (5) and (6) above). The second address of any
+ two-address sequence corresponds to a line that fol-
+ lows, in the file, the line corresponding to the first
+ address. The first address must be less than or equal
+ to the second address. The first address must be
+ greater than or equal to the first line of the file,
+ and the last address must be less than or equal to the
+ last line of the file.
+
+ 1133.. EExx DDeessccrriippttiioonn
+
+ The following words have special meanings for eexx
+ commands.
+
+ <<eeooff>>
+ The end-of-file character is used to scroll the
+ screen in the eexx editor. This character is nor-
+ mally <<ccoonnttrrooll--DD>>, however, whatever character is
+ set for the current terminal is used.
+
+ lliinnee
+ A single-line address, given in any of the forms
+ described in the section entitled "EExx AAddddrreessssiinngg".
+ The default for lliinnee is the current line.
+
+ rraannggee
+ A line, or a pair of line addresses, separated by
+ a comma or semicolon. (See the section entitled
+ "EExx AAddddrreessssiinngg" for more information.) The
+ default for range is the current line _o_n_l_y, i.e.
+ "..,,..". A percent sign ("%%") stands for the range
+ "11,,$$". The starting address must be less than, or
+ equal to, the ending address.
+
+ ccoouunntt
+ A positive integer, specifying the number of lines
+ to be affected by the command; the default is 1.
+ Generally, a count past the end-of-file may be
+ specified, e.g. the command "pp 33000000" in a 10 line
+ file is acceptable, and will print from the cur-
+ rent line through the last line in the file.
+
+ ffllaaggss
+ One or more of the characters "#", "p", and "l".
+ When a command that accepts these flags completes,
+ the addressed line(s) are written out as if by the
+ corresponding ##, ll or pp commands. In addition,
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--5500 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ any number of "++" or "--" characters can be speci-
+ fied before, after, or during the flags, in which
+ case the line written is not necessarily the one
+ affected by the command, but rather the line
+ addressed by the offset address specified. The
+ default for ffllaaggss is none.
+
+ ffiillee
+ A pattern used to derive a pathname; the default
+ is the current file. File names are subjected to
+ normal _s_h(1) word expansions.
+
+ Anywhere a file name is specified, it is also pos-
+ sible to use the special string "//ttmmpp". This will be
+ replaced with a temporary file name which can be used
+ for temporary work, e.g. "::ee //ttmmpp" creates and edits a
+ new file.
+
+ If both a count and a range are specified for com-
+ mands that use either, the starting line for the com-
+ mand is the _l_a_s_t line addressed by the range, and
+ ccoouunntt- subsequent lines are affected by the command,
+ e.g. the command "22,,33pp44" prints out lines 3, 4, 5 and
+ 6.
+
+ When only a line or range is specified, with no
+ command, the implied command is either a lliisstt, nnuummbbeerr
+ or pprriinntt command. The command used is the most recent
+ of the three commands to have been used (including any
+ use as a flag). If none of these commands have been
+ used before, the pprriinntt command is the implied command.
+ When no range or count is specified and the command
+ line is a blank line, the current line is incremented
+ by 1 and then the current line is displayed.
+
+ Zero or more whitespace characters may precede or
+ follow the addresses, count, flags, or command name.
+ Any object following a command name (such as buffer,
+ file, etc.), that begins with an alphabetic character,
+ should be separated from the command name by at least
+ one whitespace character.
+
+ Any character, including <<ccaarrrriiaaggee--rreettuurrnn>>, "%%"
+ and "##" retain their literal value when preceded by a
+ backslash.
+
+ 1144.. EExx CCoommmmaannddss
+
+ The following section describes the commands
+ available in the eexx editor. In each entry below, the
+ tag line is a usage synopsis for the command.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--5511
+
+
+ Each command can be entered as the abbreviation
+ (those characters in the synopsis command word preced-
+ ing the "[" character), the full command (all charac-
+ ters shown for the command word, omitting the "[" and
+ "]" characters), or any leading subset of the full com-
+ mand down to the abbreviation. For example, the args
+ command (shown as "aarr[[ggss]]" in the synopsis) can be
+ entered as "aarr", "aarrgg" or "aarrggss".
+
+ Each eexx command described below notes the new cur-
+ rent line after it is executed, as well as any options
+ that affect the command.
+
+ ""
+ A comment. Command lines beginning with the dou-
+ ble-quote character ("""") are ignored. This per-
+ mits comments in editor scripts and startup files.
+
+ <<eenndd--ooff--ffiillee>>
+ Scroll the screen. Write the next N lines, where
+ N is the value of the ssccrroollll option. The command
+ is the end-of-file terminal character, which may
+ be different on different terminals. Tradition-
+ ally, it is the <<ccoonnttrrooll--DD>> key.
+
+ Historically, the eeooff command ignored any preced-
+ ing count, and the <<eenndd--ooff--ffiillee>> character was
+ ignored unless it was entered as the first charac-
+ ter of the command. This implementation treats it
+ as a command _o_n_l_y if entered as the first charac-
+ ter of the command line, and otherwise treats it
+ as any other character.
+
+ Line: Set to the last line written.
+ Options: None.
+
+ !! aarrgguummeenntt((ss))
+ [[rraannggee]]!! aarrgguummeenntt((ss))
+ Execute a shell command, or filter lines through a
+ shell command. In the first synopsis, the remain-
+ der of the line after the "!!" character is passed
+ to the program named by the sshheellll option, as a
+ single argument.
+
+ Within the rest of the line, "%%" and "##" are
+ expanded into the current and alternate pathnames,
+ respectively. The character "!!" is expanded with
+ the command text of the previous !! command.
+ (Therefore, the command !!!! repeats the previous !!
+ command.) The special meanings of "%%", "##", and
+ "!!" can be overridden by escaping them with a
+ backslash. If no !! or ::!! command has yet been
+ executed, it is an error to use an unescaped "!!"
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--5522 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ character. The !! command does _n_o_t do shell
+ expansion on the strings provided as arguments.
+ If any of the above expansions change the command
+ the user entered, the command is redisplayed at
+ the bottom of the screen.
+
+ EExx then executes the program named by the sshheellll
+ option, with a --cc flag followed by the arguments
+ (which are bundled into a single argument).
+
+ The !! command is permitted in an empty file.
+
+ If the file has been modified since it was last
+ completely written, the command will warn you.
+
+ A single "!!" character is displayed when the com-
+ mand completes.
+
+ In the second form of the !! command, the remain-
+ der of the line after the "!!" is passed to the
+ program named by the sshheellll option, as described
+ above. The specified lines are passed to the pro-
+ gram as standard input, and the standard and stan-
+ dard error output of the program replace the orig-
+ inal lines.
+
+ Line: Unchanged if no range was specified, oth-
+ erwise set to the first line of the
+ range.
+ Options: Affected by the aauuttoowwrriittee and wwrriitteeaannyy
+ options.
+
+ [[rraannggee]] nnuu[[mmbbeerr]] [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] ## [[ccoouunntt]] [[ffllaaggss]]
+ Display the selected lines, each preceded with its
+ line number.
+
+ The line number format is "%6d", followed by two
+ spaces.
+
+ Line: Set to the last line displayed.
+ Options: None.
+
+ @@ bbuuffffeerr
+ ** bbuuffffeerr
+ Execute a buffer. Each line in the named buffer
+ is executed as an eexx command. If no buffer is
+ specified, or if the specified buffer is "@@" or
+ "**", the last buffer executed is used.
+
+ [[rraannggee]] <<[[<< ......]] [[ccoouunntt]] [[ffllaaggss]]
+ Shift lines left or right. The specified lines
+ are shifted to the left (for the << command) or
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--5533
+
+
+ right (for the >> command), by the number of
+ columns specified by the sshhiiffttwwiiddtthh option. Only
+ leading whitespace characters are deleted when
+ shifting left; once the first column of the line
+ contains a nonblank character, the sshhiifftt command
+ will succeed, but the line will not be modified.
+
+ If the command character << or >> is repeated more
+ than once, the command is repeated once for each
+ additional command character.
+
+ Line: If the current line is set to one of the
+ lines that are affected by the command,
+ it is unchanged. Otherwise, it is set to
+ the first nonblank character of the low-
+ est numbered line shifted.
+ Options: Affected by the sshhiiffttwwiiddtthh option.
+
+ [[lliinnee]] == [[ffllaaggss]]
+ Display the line number. Display the line number
+ of lliinnee (which defaults to the last line in the
+ file).
+
+ Line: Unchanged.
+ Options: None.
+
+ [[rraannggee]] >>[[>> ......]] [[ccoouunntt]] [[ffllaaggss]]
+ Shift right. The specified lines are shifted to
+ the right by the number of columns specified by
+ the sshhiiffttwwiiddtthh option, by inserting tab and space
+ characters. Empty lines are not changed.
+
+ If the command character ">>" is repeated more than
+ once, the command is repeated once for each addi-
+ tional command character.
+
+ Line: Set to the last line modified by the com-
+ mand.
+ Options: None.
+
+ aabb[[bbrreevv]] llhhss rrhhss
+ Add an abbreviation to the current abbreviation
+ list. In vvii, if llhhss is entered such that it is
+ preceded and followed by characters that cannot be
+ part of a word, it is replaced by the string rrhhss.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[lliinnee]] aa[[ppppeenndd]][[!!]]
+ The input text is appended to the specified line.
+ If line 0 is specified, the text is inserted at
+ the beginning of the file. Set to the last line
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--5544 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ input. If no lines are input, then set to lliinnee,
+ or to the first line of the file if a lliinnee of 0
+ was specified. Following the command name with a
+ "!!" character causes the aauuttooiinnddeenntt option to be
+ toggled for the duration of the command.
+
+ Line: Unchanged.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt,
+ bbeeaauuttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraapp--
+ mmaarrggiinn options.
+
+ aarr[[ggss]]
+ Display the argument list. The current argument
+ is displayed inside of "[[" and "]]" characters.
+ The argument list is the list of operands speci-
+ fied on startup, which can be replaced using the
+ nneexxtt command.
+
+ Line: Unchanged.
+ Options: None.
+
+ bbgg
+ VVii mode only. Background the current screen.
+
+ Line: Set to the current line when the screen
+ was last edited.
+ Options: None.
+
+ [[rraannggee]] cc[[hhaannggee]][[!!]] [[ccoouunntt]]
+ Replace the lines with input text. Following the
+ command name with a "!!" character causes the
+ aauuttooiinnddeenntt option to be toggled for the duration
+ of the command.
+
+ Line: Set to the last line input, or, if no
+ lines were input, set to the line before
+ the target line, or to the first line of
+ the file if there are no lines preceding
+ the target line.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt,
+ bbeeaauuttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraapp--
+ mmaarrggiinn options.
+
+ cchhdd[[iirr]][[!!]] [[ddiirreeccttoorryy]]
+ ccdd[[!!]] [[ddiirreeccttoorryy]]
+ Change the current working directory. The ddiirreecc--
+ ttoorryy argument is subjected to _s_h(1) word expan-
+ sions. When invoked with no directory argument
+ and the HHOOMMEE environment variable is set, the
+ directory named by the HHOOMMEE environment variable
+ becomes the new current directory. Otherwise, the
+ new current directory becomes the directory
+ returned by the _g_e_t_p_w_e_n_t(3) routine.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--5555
+
+
+ The cchhddiirr command will fail if the file has been
+ modified since the last complete write of the
+ file. You can override this check by appending a
+ "!!" character to the command.
+
+ Line: Unchanged.
+ Options: Affected by the ccddppaatthh option.
+
+ [[rraannggee]] ccoo[[ppyy]] lliinnee [[ffllaaggss]]
+ [[rraannggee]] tt lliinnee [[ffllaaggss]]
+ Copy the specified lines (range) after the desti-
+ nation line. Line 0 may be specified to insert
+ the lines at the beginning of the file.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[rraannggee]] dd[[eelleettee]] [[bbuuffffeerr]] [[ccoouunntt]] [[ffllaaggss]]
+ Delete the lines from the file. The deleted text
+ is saved in the specified buffer, or, if no buffer
+ is specified, in the unnamed buffer. If the com-
+ mand name is followed by a letter that could be
+ interpreted as either a buffer name or a flag
+ value (because neither a ccoouunntt or ffllaaggss values
+ were given), eexx treats the letter as a ffllaaggss value
+ if the letter immediately follows the command
+ name, without any whitespace separation. If the
+ letter is preceded by whitespace characters, it
+ treats it as a buffer name.
+
+ Line: Set to the line following the deleted
+ lines, or to the last line if the deleted
+ lines were at the end.
+ Options: None.
+
+ ddii[[ssppllaayy]] bb[[uuffffeerrss]] || ss[[ccrreeeennss]] || tt[[aaggss]]
+ Display buffers, screens or tags. The ddiissppllaayy
+ command takes one of three additional arguments,
+ which are as follows:
+
+ b[uffers]
+ Display all buffers (including named,
+ unnamed, and numeric) that contain text.
+ s[creens]
+ Display the file names of all background
+ screens.
+ t[ags] Display the tags stack.
+
+ Line: Unchanged.
+ Options: None.
+
+ ee[[ddiitt]][[!!]] [[++ccmmdd]] [[ffiillee]]
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--5566 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ eexx[[!!]] [[++ccmmdd]] [[ffiillee]]
+ Edit a different file. If the current buffer has
+ been modified since the last complete write, the
+ command will fail. You can override this by
+ appending a "!!" character to the command name.
+
+ If the "++ccmmdd" option is specified, that eexx command
+ will be executed in the new file. Any eexx command
+ may be used, although the most common use of this
+ feature is to specify a line number or search pat-
+ tern to set the initial location in the new file.
+
+ Line: If you have previously edited the file,
+ the current line will be set to your last
+ position in the file. If that position
+ does not exist, or you have not previ-
+ ously edited the file, the current line
+ will be set to the first line of the file
+ if you are in vvii mode, and the last line
+ of the file if you are in eexx.
+ Options: Affected by the aauuttoowwrriittee and wwrriitteeaannyy
+ options.
+
+ eexxuu[[ssaaggee]] [[ccoommmmaanndd]]
+ Display usage for an eexx command. If ccoommmmaanndd is
+ specified, a usage statement for that command is
+ displayed. Otherwise, usage statements for all eexx
+ commands are displayed.
+
+ Line: Unchanged.
+ Options: None.
+
+ ff[[iillee]] [[ffiillee]]
+ Display and optionally change the file name. If a
+ file name is specified, the current pathname is
+ changed to the specified name. The current path-
+ name, the number of lines, and the current posi-
+ tion in the file are displayed.
+
+ Line: Unchanged.
+ Options: None.
+
+ ffgg [[nnaammee]]
+ VVii mode only. Foreground the specified screen.
+ Swap the current screen with the specified back-
+ grounded screen. If no screen is specified, the
+ first background screen is foregrounded.
+
+ Line: Set to the current line when the screen
+ was last edited.
+ Options: None.
+
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--5577
+
+
+ [[rraannggee]] gg[[lloobbaall]] //ppaatttteerrnn// [[ccoommmmaannddss]]
+ [[rraannggee]] vv //ppaatttteerrnn// [[ccoommmmaannddss]]
+ Apply commands to lines matching (or not matching)
+ a pattern. The lines within the given range that
+ match ("gg[[lloobbaall]]"), or do not match ("vv") the
+ given pattern are selected. Then, the specified
+ eexx command(s) are executed with the current line
+ ("..") set to each selected line. If no range is
+ specified, the entire file is searched for match-
+ ing, or not matching, lines.
+
+ Multiple commands can be specified, one per line,
+ by escaping each <<nneewwlliinnee>> character with a back-
+ slash, or by separating commands with a "||" char-
+ acter. If no commands are specified, the command
+ defaults to the pprriinntt command.
+
+ For the aappppeenndd, cchhaannggee and iinnsseerrtt commands, the
+ input text must be part of the global command
+ line. In this case, the terminating period can be
+ omitted if it ends the commands.
+
+ The vviissuuaall command may also be specified as one of
+ the eexx commands. In this mode, input is taken
+ from the terminal. Entering a QQ command in vvii
+ mode causes the next line matching the pattern to
+ be selected and vvii to be reentered, until the list
+ is exhausted.
+
+ The gglloobbaall, vv and uunnddoo commands cannot be used as
+ part of these commands.
+
+ The editor options aauuttoopprriinntt, aauuttooiinnddeenntt, and
+ rreeppoorrtt are turned off for the duration of the
+ gglloobbaall and vv commands.
+
+ Line: The last line modified.
+ Options: None.
+
+ hhee[[llpp]]
+ Display a help message.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[lliinnee]] ii[[nnsseerrtt]][[!!]]
+ The input text is inserted before the specified
+ line. Following the command name with a "!!"
+ character causes the aauuttooiinnddeenntt option setting to
+ be toggled for the duration of this command.
+
+ Line: Set to the last line input; if no lines
+ were input, set to the line before the
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--5588 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ target line, or to the first line of the
+ file if there are no lines preceding the
+ target line.
+ Options: Affected by the aallttwweerraassee, aauuttooiinnddeenntt,
+ bbeeaauuttiiffyy, sshhoowwmmaattcchh, ttttyywweerraassee and wwrraapp--
+ mmaarrggiinn options.
+
+ [[rraannggee]] jj[[ooiinn]][[!!]] [[ccoouunntt]] [[ffllaaggss]]
+ Join lines of text together.
+
+ A ccoouunntt specified to the command specifies that
+ the last line of the rraannggee plus ccoouunntt subsequent
+ lines will be joined. (Note, this differs by one
+ from the general rule where only ccoouunntt- subsequent
+ lines are affected.)
+
+ If the current line ends with a whitespace charac-
+ ter, all whitespace is stripped from the next
+ line. Otherwise, if the next line starts with a
+ open parenthesis ("(("), do nothing. Otherwise, if
+ the current line ends with a question mark ("??"),
+ period ("..") or exclamation point ("!!"), insert
+ two spaces. Otherwise, insert a single space.
+
+ Appending a "!!" character to the command name
+ causes a simpler join with no white-space process-
+ ing.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[rraannggee]] ll[[iisstt]] [[ccoouunntt]] [[ffllaaggss]]
+ Display the lines unambiguously. Tabs are dis-
+ played as "^^II", and the end of the line is marked
+ with a "$$" character.
+
+ Line: Set to the last line displayed.
+ Options: None.
+
+ mmaapp[[!!]] [[llhhss rrhhss]]
+ Define or display maps (for vvii only).
+
+ If "llhhss" and "rrhhss" are not specified, the current
+ set of command mode maps are displayed. If a "!!"
+ character is appended to to the command, the text
+ input mode maps are displayed.
+
+ Otherwise, when the "llhhss" character sequence is
+ entered in vvii, the action is as if the correspond-
+ ing "rrhhss" had been entered. If a "!!" character
+ is appended to the command name, the mapping is
+ effective during text input mode, otherwise, it is
+ effective during command mode. This allows "llhhss"
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--5599
+
+
+ to have two different macro definitions at the
+ same time: one for command mode and one for input
+ mode.
+
+ Whitespace characters require escaping with a
+ <<lliitteerraallnext> character to be entered in the llhhss
+ string in visual mode.
+
+ Normally, keys in the rrhhss string are remapped (see
+ the rreemmaapp option), and it is possible to create
+ infinite loops. However, keys which map to them-
+ selves are not further remapped, regardless of the
+ setting of the rreemmaapp option. For example, the
+ command "::mmaapp nn nnzz.." maps the "nn" key to the nn
+ and zz commands.
+
+ To exit an infinitely looping map, use the termi-
+ nal <<iinntteerrrruupptt>> character.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[lliinnee]] mmaa[[rrkk]] <<cchhaarraacctteerr>>
+ [[lliinnee]] kk <<cchhaarraacctteerr>>
+ Mark the line with the mark <<cchhaarraacctteerr>>. The
+ expressions "''<<cchhaarraacctteerr>>" and "``<<cchhaarraacctteerr>>" can
+ then be used as an address in any command that
+ uses one.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[rraannggee]] mm[[oovvee]] lliinnee
+ Move the specified lines after the target line. A
+ target line of 0 places the lines at the beginning
+ of the file.
+
+ Line: Set to the first of the moved lines.
+ Options: None.
+
+ mmkk[[eexxrrcc]][[!!]] ffiillee
+ Write the abbreviations, editor options and maps
+ to the specified file. Information is written in
+ a form which can later be read back in using the
+ eexx ssoouurrccee command. If ffiillee already exists, the
+ mmkkeexxrrcc command will fail. This check can be over-
+ ridden by appending a "!!" character to the com-
+ mand.
+
+ Line: Unchanged.
+ Options: None.
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--6600 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ nn[[eexxtt]][[!!]] [[ffiillee ......]]
+ Edit the next file from the argument list. The
+ nneexxtt command will fail if the file has been modi-
+ fied since the last complete write. This check
+ can be overridden by appending the "!!" character
+ to the command name. The argument list can
+ optionally be replaced by specifying a new one as
+ arguments to this command. In this case, editing
+ starts with the first file on the new list.
+
+ Line: Set as described for the eeddiitt command.
+ Options: Affected by the options aauuttoowwrriittee and
+ wwrriitteeaannyy.
+
+ [[lliinnee]] oo[[ppeenn]] //ppaatttteerrnn// [[ffllaaggss]]
+ Enter open mode. Open mode is the same as being
+ in vvii, but with a one-line window. All the stan-
+ dard vvii commands are available. If a match is
+ found for the optional RE argument, the cursor is
+ set to the start of the matching pattern.
+
+ _T_h_i_s _c_o_m_m_a_n_d _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+
+ Line: Unchanged, unless the optional RE is
+ specified, in which case it is set to the
+ line where the matching pattern is found.
+ Options: Affected by the ooppeenn option.
+
+ pprree[[sseerrvvee]]
+ Save the file in a form that can later be recov-
+ ered using the eexx --rr option. When the file is
+ preserved, an email message is sent to the user.
+
+ Line: Unchanged.
+ Options: None.
+
+ pprreevv[[iioouuss]][[!!]]
+ Edit the previous file from the argument list.
+ The pprreevviioouuss command will fail if the file has
+ been modified since the last complete write. This
+ check can be overridden by appending the "!!"
+ character to the command name.
+
+ Line: Set as described for the eeddiitt command.
+ Options: Affected by the options aauuttoowwrriittee and
+ wwrriitteeaannyy. None.
+
+ [[rraannggee]] pp[[rriinntt]] [[ccoouunntt]] [[ffllaaggss]]
+ Display the specified lines.
+
+ Line: Set to the last line displayed.
+ Options: None.
+
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--6611
+
+
+ [[lliinnee]] ppuu[[tt]] [[bbuuffffeerr]]
+ Append buffer contents to the current line. If a
+ buffer is specified, its contents are appended to
+ the line, otherwise, the contents of the unnamed
+ buffer are used.
+
+ Line: Set to the line after the current line.
+ Options: None.
+
+ qq[[uuiitt]][[!!]]
+ End the editing session. If the file has been
+ modified since the last complete write, the qquuiitt
+ command will fail. This check may be overridden
+ by appending a "!!" character to the command.
+
+ If there are more files to edit, the qquuiitt command
+ will fail. Appending a "!!" character to the com-
+ mand name or entering two qquuiitt commands (i.e. wwqq,
+ qquuiitt, xxiitt or ZZZZ) in a row) will override this
+ check and the editor will exit.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[lliinnee]] rr[[eeaadd]][[!!]] [[ffiillee]]
+ Read a file. A copy of the specified file is
+ appended to the line. If lliinnee is 0, the copy is
+ inserted at the beginning of the file. If no file
+ is specified, the current file is read; if there
+ is no current file, then ffiillee becomes the current
+ file. If there is no current file and no ffiillee is
+ specified, then the rreeaadd command will fail.
+
+ If ffiillee is preceded by a "!!" character, ffiillee is
+ treated as if it were a shell command, and passed
+ to the program named by the SSHHEELLLL environment
+ variable. The standard and standard error outputs
+ of that command are read into the file after the
+ specified line. The special meaning of the "!!"
+ character can be overridden by escaping it with a
+ backslash ("\\") character.
+
+ Line: When executed from eexx, the current line
+ is set to the last line read. When exe-
+ cuted from vvii, the current line is set to
+ the first line read.
+ Options: None.
+
+ rreecc[[oovveerr]] ffiillee
+ Recover ffiillee if it was previously saved. If no
+ saved file by that name exists, the rreeccoovveerr com-
+ mand behaves similarly to the eeddiitt command.
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--6622 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ Line: Set as described for the eeddiitt command.
+ Options: None.
+
+ rreess[[iizzee]] [[++||--]]ssiizzee
+ VVii mode only. Grow or shrink the current screen.
+ If ssiizzee is a positive, signed number, the current
+ screen is grown by that many lines. If ssiizzee is a
+ negative, signed number, the current screen is
+ shrunk by that many lines. If ssiizzee is not signed,
+ the current screen is set to the specified ssiizzee.
+ Applicable only to split screens.
+
+ Line: Unchanged.
+ Options: None.
+
+ rreeww[[iinndd]][[!!]]
+ Rewind the argument list. If the current file has
+ been modified since the last complete write, the
+ rreewwiinndd command will fail. This check may be over-
+ ridden by appending the "!!" character to the com-
+ mand.
+
+ Otherwise, the current file is set to the first
+ file in the argument list.
+
+ Line: Set as described for the eeddiitt command.
+ Options: Affected by the aauuttoowwrriittee and wwrriitteeaannyy
+ options.
+
+ ssee[[tt]] [[ooppttiioonn[[==[[vvaalluuee]]]] ......]] [[nnooooppttiioonn ......]] [[ooppttiioonn??
+ ......]] [[aallll]]
+ Display or set editor options. When no arguments
+ are specified, the editor option tteerrmm, and any
+ editor options whose values have been changed from
+ the default settings are displayed. If the argu-
+ ment aallll is specified, the values of all of editor
+ options are displayed.
+
+ Specifying an option name followed by the charac-
+ ter "??" causes the current value of that option
+ to be displayed. The "??" can be separated from
+ the option name by whitespace characters. The "??"
+ is necessary only for Boolean valued options.
+ Boolean options can be given values by the form
+ "sseett ooppttiioonn" to turn them on, or "sseett nnooooppttiioonn" to
+ turn them off. String and numeric options can be
+ assigned by the form "sseett ooppttiioonn==vvaalluuee". Any
+ whitespace characters in strings can be included
+ literally by preceding each with a backslash.
+ More than one option can be set or listed by a
+ single set command, by specifying multiple argu-
+ ments, each separated from the next by whitespace
+ characters.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--6633
+
+
+ Line: Unchanged.
+ Options: None.
+
+ sshh[[eellll]]
+ Run a shell program. The program named by the
+ sshheellll option is run with a --ii (for interactive)
+ flag. Editing is resumed when that program exits.
+
+ Line: Unchanged.
+ Options: None.
+
+ ssoo[[uurrccee]] ffiillee
+ Read and execute eexx commands from a file. SSoouurrccee
+ commands may be nested.
+
+ Line: Unchanged.
+ Options: None.
+
+ sspp[[lliitt]] [[ffiillee ......]]
+ VVii mode only. Split the screen. The current
+ screen is split into two screens, of approximately
+ equal size. If the cursor is in the lower half of
+ the screen, the screen will split up, i.e. the new
+ screen will be above the old one. If the cursor
+ is in the upper half of the screen, the new screen
+ will be below the old one.
+
+ If ffiillee is specified, the new screen is editing
+ that file, otherwise, both screens are editing the
+ same file, and changes in each will be be
+ reflected in the other. The argument list for the
+ new screen consists of the list of files specified
+ as arguments to this command, or, the current
+ pathname if no files are specified.
+
+ Line: If ffiillee is specified, set as for the eeddiitt
+ command, otherwise unchanged.
+ Options: None.
+
+ [[rraannggee]] ss[[uubbssttiittuuttee]] [[//ppaatttteerrnn//rreeppllaaccee//]] [[ooppttiioonnss]]
+ [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] && [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
+ [[rraannggee]] ~~ [[ooppttiioonnss]] [[ccoouunntt]] [[ffllaaggss]]
+ Make substitutions. Replace the first instance of
+ ppaatttteerrnn with the string rreeppllaaccee on the specified
+ line(s). If the "//ppaatttteerrnn//rreeppll//" argument is not
+ specified, the "//ppaatttteerrnn//rreeppll//" from the previous
+ ssuubbssttiittuuttee command is used.
+
+ If ooppttiioonnss includes the letter "cc" (confirm), you
+ will be prompted for confirmation before each
+ replacement is done. An affirmative response (in
+ English, a "yy" character) causes the replacement
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--6644 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ to be made. A quit response (in English, a "qq"
+ character) causes the ssuubbssttiittuuttee command to be
+ terminated. Any other response causes the
+ replacement not to be made, and the ssuubbssttiittuuttee
+ command continues. If ooppttiioonnss includes the letter
+ "gg" (global), all nonoverlapping instances of ppaatt--
+ tteerrnn in the line are replaced.
+
+ The && version of the command is the same as not
+ specifying a pattern or replacement string to the
+ ssuubbssttiittuuttee command, and the "&&" is replaced by the
+ pattern and replacement information from the pre-
+ vious substitute command.
+
+ The ~~ version of the command is the same as && and
+ ss, except that the search pattern used is the last
+ RE used in _a_n_y command, not necessarily the one
+ used in the last ssuubbssttiittuuttee command.
+
+ For example, in the sequence
+
+ ss//rreedd//bblluuee//
+ //ggrreeeenn
+ ~~
+
+ the "~~" is equivalent to "ss//ggrreeeenn//bblluuee//".
+
+ The ssuubbssttiittuuttee command may be interrupted, using
+ the terminal interrupt character. All substitu-
+ tions completed before the interrupt are retained.
+
+ Line: Set to the last line upon which a substi-
+ tution was made.
+ Options: None.
+
+ ssuu[[ssppeenndd]][[!!]]
+ sstt[[oopp]][[!!]]
+ <<ccoonnttrrooll--ZZ>>
+ Suspend the edit session. Appending a "!!" char-
+ acter to these commands turns off the aauuttoowwrriittee
+ option for the command.
+
+ Line: Unchanged.
+ Options: Affected by the aauuttoowwrriittee option.
+
+ ttaa[[gg]][[!!]] ttaaggssttrriinngg
+ Edit the file containing the specified tag.
+ Search for the tagstring, which can be in a dif-
+ ferent file. If the tag is in a different file,
+ then the new file is edited. If the current file
+ has been modified since the last complete write,
+ the ttaagg command will fail. This check can be
+ overridden by appending the "!!" character to the
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--6655
+
+
+ command name.
+
+ The ttaagg command searches for ttaaggssttrriinngg in the tags
+ file(s) specified by the option. (See _c_t_a_g_s(1)
+ for more information on tags files.)
+
+ Line: Set to the line indicated by the tag.
+ Options: Affected by the aauuttoowwrriittee, ttaagglleennggtthh,
+ ttaaggss and wwrriitteeaannyy options.
+
+ ttaaggpp[[oopp]][[!!]] [[ffiillee || nnuummbbeerr]]
+ Pop to the specified tag in the tags stack. If
+ neither ffiillee or nnuummbbeerr is specified, the ttaaggppoopp
+ command pops to the most recent entry on the tags
+ stack. If ffiillee or nnuummbbeerr is specified, the ttaaggppoopp
+ command pops to the most recent entry in the tags
+ stack for that file, or numbered entry in the tags
+ stack, respectively. (See the ddiissppllaayy command for
+ information on displaying the tags stack.)
+
+ If the file has been modified since the last com-
+ plete write, the ttaaggppoopp command will fail. This
+ check may be overridden by appending a "!!" char-
+ acter to the command name.
+
+ Line: Set to the line indicated by the tag.
+ Options: Affected by the aauuttoowwrriittee, and wwrriitteeaannyy
+ options.
+
+ ttaaggtt[[oopp]][[!!]]
+ Pop to the least recent tag on the tags stack,
+ clearing the tags stack.
+
+ If the file has been modified since the last com-
+ plete write, the ttaaggppoopp command will fail. This
+ check may be overridden by appending a "!!" char-
+ acter to the command name.
+
+ Line: Set to the line indicated by the tag.
+ Options: Affected by the aauuttoowwrriittee, and wwrriitteeaannyy
+ options.
+
+ uunnaa[[bbbbrreevv]] llhhss
+ Delete an abbreviation. Delete llhhss from the cur-
+ rent list of abbreviations.
+
+ Line: Unchanged.
+ Options: None.
+
+ uu[[nnddoo]]
+ Undo the last change made to the file. Changes
+ made by gglloobbaall, vv, vviissuuaall and map sequences are
+ considered a single command. If repeated, the uu
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--6666 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ command alternates between these two states, and
+ is its own inverse.
+
+ Line: Set to the last line modified by the com-
+ mand.
+ Options: None.
+
+ uunnmm[[aapp]][[!!]] llhhss
+ Unmap a mapped string. Delete the command mode
+ map definition for llhhss. If a "!!" character is
+ appended to the command name, delete the text
+ input mode map definition instead.
+
+ Line: Unchanged.
+ Options: None.
+
+ vvee[[rrssiioonn]]
+ Display the version of the eexx//vvii editor.
+
+ [[lliinnee]] vvii[[ssuuaall]] [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
+ EExx mode only. Enter vvii. The ttyyppee is optional,
+ and can be "--", "++" or "^^", as in the eexx zz com-
+ mand, to specify the the position of the specified
+ line in the screen window. (The default is to
+ place the line at the top of the screen window.)
+ A ccoouunntt specifies the number of lines that will
+ initially be displayed. (The default is the value
+ of the wwiinnddooww editor option.)
+
+ Line: Unchanged unless lliinnee is specified, in
+ which case it is set to that line.
+ Options: None.
+
+ vvii[[ssuuaall]][[!!]] [[++ccmmdd]] [[ffiillee]]
+ VVii mode only. Edit a new file. Identical to the
+ "eeddiitt[[!!]] [[++ccmmdd]] [[ffiillee]]" command.
+
+ vviiuu[[ssaaggee]] [[ccoommmmaanndd]]
+ Display usage for a vvii command. If ccoommmmaanndd is
+ specified, a usage statement for that command is
+ displayed. Otherwise, usage statements for all vvii
+ commands are displayed.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[rraannggee]] ww[[rriittee]][[!!]] [[>>>>]] [[ffiillee]]
+ [[rraannggee]] ww[[rriittee]] [[!!]] [[ffiillee]]
+ [[rraannggee]] wwnn[[!!]] [[>>>>]] [[ffiillee]]
+ [[rraannggee]] wwqq[[!!]] [[>>>>]] [[ffiillee]]
+ Write the file. The specified lines (the entire
+ file, if no range is given) is written to ffiillee.
+ If ffiillee is not specified, the current pathname is
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss)) UUSSDD::1133--6677
+
+
+ used. If ffiillee is specified, and it exists, or if
+ the current pathname was set using the ffiillee com-
+ mand, and the file already exists, these commands
+ will fail. Appending a "!!" character to the com-
+ mand name will override this check and the write
+ will be attempted, regardless.
+
+ Specifying the optional ">>>>" string will cause the
+ write to be appended to the file, in which case no
+ tests are made for the file already existing.
+
+ If the file is preceded by a "!!" character, the
+ program named in the SHELL environment variable is
+ invoked with file as its second argument, and the
+ specified lines are passed as standard input to
+ that command. The "!!" in this usage must be sep-
+ arated from command name by at least one whites-
+ pace character. The special meaning of the "!!"
+ may be overridden by escaping it with a backslash
+ ("\\") character.
+
+ The wwqq version of the write command will exit the
+ editor after writing the file, if there are no
+ further files to edit. Appending a "!!" character
+ to the command name or entering two "quit" com-
+ mands (i.e. wwqq, qquuiitt, xxiitt or ZZZZ) in a row) will
+ override this check and the editor will exit,
+ ignoring any files that have not yet been edited.
+
+ The wwnn version of the write command will move to
+ the next file after writing the file, unless the
+ write fails.
+
+ Line: Unchanged.
+ Options: Affected by the rreeaaddoonnllyy and wwrriitteeaannyy
+ options.
+
+ [[rraannggee]] xx[[iitt]][[!!]] [[ffiillee]]
+ Write the file if it has been modified. The spec-
+ ified lines are written to ffiillee, if the file has
+ been modified since the last complete write to any
+ file. If no rraannggee is specified, the entire file
+ is written.
+
+ The xxiitt command will exit the editor after writing
+ the file, if there are no further files to edit.
+ Appending a "!!" character to the command name or
+ entering two "quit" commands (i.e. wwqq, qquuiitt, xxiitt
+ or ZZZZ) in a row) will override this check and the
+ editor will exit, ignoring any files that have not
+ yet been edited.
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--6688 NNvvii//NNeexx RReeffeerreennccee ((EExx CCoommmmaannddss))
+
+
+ Line: Unchanged.
+ Options: Affected by the rreeaaddoonnllyy and wwrriitteeaannyy
+ options.
+
+ [[rraannggee]] yyaa[[nnkk]] [[bbuuffffeerr]] [[ccoouunntt]]
+ Copy the specified lines to a buffer. If no
+ buffer is specified, the unnamed buffer is used.
+
+ Line: Unchanged.
+ Options: None.
+
+ [[lliinnee]] zz [[ttyyppee]] [[ccoouunntt]] [[ffllaaggss]]
+ Adjust the window. If no ttyyppee is specified, then
+ ccoouunntt lines following the specified line are dis-
+ played. The default ccoouunntt is the value of the
+ wwiinnddooww option. The ttyyppee argument changes the
+ position at which lliinnee is displayed on the screen
+ by changing the number of lines displayed before
+ and after lliinnee. The following ttyyppee characters may
+ be used:
+
+ - Place the line at the bottom of the
+ screen.
+ + Place the line at the top of the screen.
+ . Place the line in the middle of the
+ screen.
+ ^ Write out count lines starting ccoouunntt ** 22
+ lines before lliinnee; the net effect of this
+ is that a "zz^^" command following a zz com-
+ mand writes the previous page.
+ = Center lliinnee on the screen with a line of
+ hyphens displayed immediately before and
+ after it. The number of preceding and
+ following lines of text displayed are
+ reduced to account for those lines.
+
+ Line: Set to the last line displayed, with the
+ exception of the ttyyppee, where the current
+ line is set to the line specified by the
+ command.
+ Options: Affected by the option.
+
+ 1155.. SSeett OOppttiioonnss
+
+ There are a large number of options that may be
+ set (or unset) to change the editor's behavior. This
+ section describes the options, their abbreviations and
+ their default values.
+
+ In each entry below, the first part of the tag
+ line is the full name of the option, followed by any
+ equivalent abbreviations. (Regardless of the abbrevia-
+ tions, it is only necessary to use the minimum number
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--6699
+
+
+ of characters necessary to distinguish an abbreviation
+ from all other commands for it to be accepted, in
+ nneexx/nnvvii. Historically, only the full name and the
+ official abbreviations were accepted by eexx/vvii. Using
+ full names in your startup files and environmental
+ variables will probably make them more portable.) The
+ part in square brackets is the default value of the
+ option. Most of the options are boolean, i.e. they are
+ either on or off, and do not have an associated value.
+
+ Options apply to both eexx and vvii modes, unless oth-
+ erwise specified.
+
+ For information on modifying the options or to
+ display the options and their current values, see the
+ "set" command in the section entitled "EExx CCoommmmaannddss".
+
+ aallttwweerraassee [[ooffff]]
+ VVii only. Change how vvii does word erase during
+ text input. When this option is set, text is bro-
+ ken up into three classes: alphabetic, numeric and
+ underscore characters, other nonblank characters,
+ and blank characters. Changing from one class to
+ another marks the end of a word. In addition, the
+ class of the first character erased is ignored
+ (which is exactly what you want when erasing path-
+ name components).
+
+ aauuttooiinnddeenntt,, aaii [[ooffff]]
+ If this option is set, whenever you create a new
+ line (using the vvii AA, aa, CC, cc, II, ii, OO, oo, RR, rr,
+ SS, and ss commands, or the eexx aappppeenndd, cchhaannggee, and
+ iinnsseerrtt commands) the new line is automatically
+ indented to align the cursor with the first non-
+ blank character of the line from which you created
+ it. Lines are indented using tab characters to
+ the extent possible (based on the value of the
+ ttaabbssttoopp option) and then using space characters as
+ necessary. For commands inserting text into the
+ middle of a line, any blank characters to the
+ right of the cursor are discarded, and the first
+ nonblank character to the right of the cursor is
+ aligned as described above.
+
+ The indent characters are themselves somewhat spe-
+ cial. If you do not enter more characters on the
+ new line before moving to another line, or enter-
+ ing <<eessccaappee>>, the indent character will be deleted
+ and the line will be empty. For example, if you
+ enter <<ccaarrrriiaaggee--rreettuurrnn>> twice in succession, the
+ line created by the first <<ccaarrrriiaaggee--rreettuurrnn>> will
+ not have any characters in it, regardless of the
+ indentation of the previous or subsequent line.
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--7700 NNvvii//NNeexx RReeffeerreennccee
+
+
+ Indent characters also require that you enter
+ additional erase characters to delete them. For
+ example, if you have an indented line, containing
+ only blanks, the first <<wwoorrdd--eerraassee>> character you
+ enter will erase up to end of the indent charac-
+ ters, and the second will erase back to the begin-
+ ning of the line. (Historically, only the <<ccoonn--
+ ttrrooll--DD>> key would erase the indent characters.
+ Both the <<ccoonnttrrooll--DD>> key and the usual erase keys
+ work in nnvvii.) In addition, if the cursor is posi-
+ tioned at the end of the indent characters, the
+ keys "00<<ccoonnttrrooll--DD>>" will erase all of the indent
+ characters for the current line, resetting the
+ indentation level to 0. Similarly, the keys
+ "^^<<ccoonnttrrooll--DD>>" will erase all of the indent char-
+ acters for the current line, leaving the indenta-
+ tion level for future created lines unaffected.
+
+ Finally, if the aauuttooiinnddeenntt option is set, the SS
+ and cccc commands change from the first nonblank of
+ the line to the end of the line, instead of from
+ the beginning of the line to the end of the line.
+
+ aauuttoopprriinntt,, aapp [[ooffff]]
+ EExx only. Cause the current line to be automati-
+ cally displayed after the eexx commands <<, >>, ccooppyy,
+ ddeelleettee, jjooiinn, mmoovvee, ppuutt, tt, UUnnddoo, and uunnddoo. This
+ automatic display is suppressed during gglloobbaall and
+ vvgglloobbaall commands, and for any command where
+ optional flags are used to explicitly display the
+ line.
+
+ aauuttoowwrriittee,, aaww [[ooffff]]
+ If this option is set, the vvii !!, ^^^^, ^^]] and <<ccoonn--
+ ttrrooll--ZZ>> commands, and the eexx eeddiitt, nneexxtt, rreewwiinndd,
+ ssttoopp, ssuussppeenndd, ttaagg, ttaaggppoopp, and ttaaggttoopp commands
+ automatically write the current file back to the
+ current file name if it has been modified since it
+ was last written. If the write fails, the command
+ fails and goes no further.
+
+ Appending the optional force flag character "!!"
+ to the eexx commands nneexxtt, rreewwiinndd, ssttoopp, ssuussppeenndd,
+ ttaagg, ttaaggppoopp, and ttaaggttoopp stops the automatic write
+ from being attempted.
+
+ (Historically, the nneexxtt command ignored the
+ optional force flag.) Note, the eexx commands eeddiitt,
+ qquuiitt, sshheellll, and xxiitt are _n_o_t affected by the
+ aauuttoowwrriittee option.
+
+ bbeeaauuttiiffyy,, bbff [[ooffff]]
+ If this option is set, all control characters that
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--7711
+
+
+ are not currently being specially interpreted,
+ other than <<ttaabb>>, <<nneewwlliinnee>>, and <<ffoorrmm--ffeeeedd>>, are
+ discarded from commands read in by eexx from command
+ files, and from input text entered to vvii (either
+ into the file or to the colon command line). Text
+ files read by eexx/vvii are _n_o_t affected by the bbeeaauu--
+ ttiiffyy option.
+
+ ccddppaatthh [[eennvviirroonnmmeenntt vvaarriiaabbllee CCDDPPAATTHH,, oorr ccuurrrreenntt ddiirreecc--
+ ttoorryy]]
+ This option is used to specify a colon separated
+ list of directories which are used as path pre-
+ fixes for any relative path names used as argu-
+ ments for the ccdd command. The value of this
+ option defaults to the value of the environmental
+ variable CCDDPPAATTHH if it is set, otherwise to the
+ current directory. For compatibility with the
+ POSIX 1003.2 shell, the ccdd command does _n_o_t check
+ the current directory as a path prefix for rela-
+ tive path names unless it is explicitly specified.
+ It may be so specified by entering an empty string
+ or a ".." character into the CCDDPPAATTHH variable or
+ the option value.
+
+ ccoolluummnnss,, ccoo [[8800]]
+ The number of columns in the screen. Setting this
+ option causes eexx/vvii to set (or reset) the environ-
+ mental variable CCOOLLUUMMNNSS. See the section entitled
+ "SSiizziinngg tthhee SSccrreeeenn" more information.
+
+ ccoommmmeenntt [[ooffff]]
+ VVii only. If the first non-empty line of the file
+ begins with the string "//**", this option causes vvii
+ to skip to the end of that C-language comment
+ (probably a terribly boring legal notice) before
+ displaying the file.
+
+ ddiirreeccttoorryy,, ddiirr [[eennvviirroonnmmeenntt vvaarriiaabbllee TTMMPPDDIIRR,, oorr //ttmmpp]]
+ The directory where temporary files are created.
+ The environmental variable TTMMPPDDIIRR is used as the
+ default value if it exists, otherwise //ttmmpp is
+ used.
+
+ eeddccoommppaattiibbllee,, eedd [[ooffff]]
+ Remember the values of the "c" and "g" suffices to
+ the ssuubbssttiittuuttee commands, instead of initializing
+ them as unset for each new command. Specifying
+ pattern and replacement strings to the ssuubbssttiittuuttee
+ command unsets the "c" and "g" suffices as well.
+
+ eerrrroorrbbeellllss,, eebb [[ooffff]]
+ EExx only. EExx error messages are normally presented
+ in inverse video. If that is not possible for the
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--7722 NNvvii//NNeexx RReeffeerreennccee
+
+
+ terminal, setting this option causes error mes-
+ sages to be announced by ringing the terminal
+ bell.
+
+ eexxrrcc,, eexx [[ooffff]]
+ If this option is turned off in the system or
+ $HOME startup files, the local startup files are
+ never read (unless they are the same as the system
+ or $HOME startup files). Turning it on has no
+ effect, i.e. the normal checks for local startup
+ files are performed, regardless. See the section
+ entitled "SSttaarrttuupp IInnffoorrmmaattiioonn" for more informa-
+ tion.
+
+ eexxtteennddeedd [[ooffff]]
+ This option causes all regular expressions to be
+ treated as POSIX 1003.2 Extended Regular Expres-
+ sions (which are similar to historic _e_g_r_e_p(1)
+ style expressions).
+
+ ffllaasshh [[oonn]]
+ This option causes the screen to flash instead of
+ beeping the keyboard, on error, if the terminal
+ has the capability.
+
+ hhaarrddttaabbss,, hhtt [[88]]
+ This option defines the spacing between hardware
+ tab settings, i.e. the tab expansion done by the
+ operating system and/or the terminal itself. As
+ nneexx/nnvvii never writes <<ttaabb>> characters to the ter-
+ minal, unlike historic versions of eexx/vvii, this
+ option does not currently have any affect.
+
+ iiggnnoorreeccaassee,, iicc [[ooffff]]
+ This option causes regular expressions, both in eexx
+ commands and in searches, to be evaluated in a
+ case-insensitive manner.
+
+ kkeeyyttiimmee [[66]]
+ The 10th's of a second eexx/vvii waits for a subse-
+ quent key to complete a key mapping.
+
+ lleeffttrriigghhtt [[ooffff]]
+ VVii only. This option causes the screen to be
+ scrolled left-right to view lines longer than the
+ screen, instead of the traditional vvii screen
+ interface which folds long lines at the right-hand
+ margin of the terminal.
+
+ lliinneess,, llii [[2244]]
+ VVii only. The number of lines in the screen. Set-
+ ting this option causes eexx/vvii to set (or reset)
+ the environmental variable LLIINNEESS. See the section
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--7733
+
+
+ entitled "SSiizziinngg tthhee SSccrreeeenn" for more information.
+
+ lliisspp [[ooffff]]
+ VVii only. This option changes the behavior of the
+ vvii ((, )), {{, }}, [[[[ and ]]]] commands to match the
+ Lisp language. Also, the aauuttooiinnddeenntt option's
+ behavior is changed to be appropriate for Lisp.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+
+ lliisstt [[ooffff]]
+ This option causes lines to be displayed in an
+ unambiguous fashion. Specifically, tabs are dis-
+ played as control characters, i.e. "^^II", and the
+ ends of lines are marked with a "$$" character.
+
+ mmaaggiicc [[oonn]]
+ This option is on by default. Turning the mmaaggiicc
+ option off causes all regular expression charac-
+ ters except for "^^" and "$$", to be treated as
+ ordinary characters. To re-enable characters
+ individually, when the mmaaggiicc option is off, pre-
+ cede them with a backslash "\\" character. See the
+ section entitled "RReegguullaarr EExxpprreessssiioonnss aanndd RReeppllaaccee--
+ mmeenntt SSttrriinnggss" for more information.
+
+ mmaattcchhttiimmee [[77]]
+ VVii only. The 10th's of a second eexx/vvii pauses on
+ the matching character when the sshhoowwmmaattcchh option
+ is set.
+
+ mmeessgg [[oonn]]
+ This option allows other users to contact you
+ using the _t_a_l_k(1) and _w_r_i_t_e(1) utilities, while
+ you are editing. EExx/vvii does not turn message on,
+ i.e. if messages were turned off when the editor
+ was invoked, they will stay turned off. This
+ option only permits you to disallow messages for
+ the edit session. See the _m_e_s_g(1) utility for
+ more information.
+
+ mmooddeelliinneess,, mmooddeelliinnee [[ooffff]]
+ If the mmooddeelliinneess option is set, eexx/vvii has histori-
+ cally scanned the first and last five lines of
+ each file as it is read for editing, looking for
+ any eexx commands that have been placed in those
+ lines. After the startup information has been
+ processed, and before the user starts editing the
+ file, any commands embedded in the file are exe-
+ cuted.
+
+ Commands were recognized by the letters "e" or "v"
+ followed by "x" or "i", at the beginning of a line
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--7744 NNvvii//NNeexx RReeffeerreennccee
+
+
+ or following a tab or space character, and fol-
+ lowed by a ":", an eexx command, and another ":".
+
+ This option is a security problem of immense pro-
+ portions, and should not be used under any circum-
+ stances.
+
+ _T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
+
+ nnuummbbeerr,, nnuu [[ooffff]]
+ Precede each line displayed with its current line
+ number.
+
+ ooccttaall [[ooffff]]
+ Display unknown characters as octal numbers,
+ instead of the default hexadecimal.
+
+ ooppeenn [[oonn]]
+ EExx only. If this option is not set, the ooppeenn and
+ vviissuuaall commands are disallowed.
+
+ ooppttiimmiizzee,, oopptt [[oonn]]
+ VVii only. Throughput of text is expedited by set-
+ ting the terminal not to do automatic carriage
+ returns when printing more than one (logical) line
+ of output, greatly speeding output on terminals
+ without addressable cursors when text with leading
+ white space is printed.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+
+ ppaarraaggrraapphhss,, ppaarraa [[IIPPLLPPPPPPQQPPPP LLIIppppllppiippbbpp]]
+ VVii only. Define additional paragraph boundaries
+ for the {{ and }} commands. The value of this
+ option must be a character string consisting of
+ zero or more character pairs.
+
+ In the text to be edited, the character string
+ <<nneewwlliinnee>>..<<cchhaarr--ppaaiirr>>, (where <<cchhaarr--ppaaiirr>> is one
+ of the character pairs in the option's value)
+ defines a paragraph boundary. For example, if the
+ option were set to LLaaAA<<ssppaaccee>>####, then all of the
+ following additional paragraph boundaries would be
+ recognized:
+
+
+ <newline>.La
+ <newline>.A<space>
+ <newline>.##
+
+
+ pprroommpptt [[oonn]]
+ EExx only. This option causes eexx to prompt for
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--7755
+
+
+ command input with a "::" character; when it is not
+ set, no prompt is displayed.
+
+ rreeaaddoonnllyy,, rroo [[ooffff]]
+ This option causes a force flag to be required to
+ attempt to write the file back to the original
+ file name. Setting this option is equivalent to
+ using the --RR command line option, or editing a
+ file which lacks write permission.
+
+ rreeccddiirr [[//vvaarr//ttmmpp//vvii..rreeccoovveerr]]
+ The directory where recovery files are stored.
+
+ If you change the value of rreeccddiirr, be careful to
+ choose a directory whose contents are not regu-
+ larly deleted. Bad choices include directories in
+ memory based filesystems, or //ttmmpp, on most sys-
+ tems, as their contents are removed when the
+ machine is rebooted.
+
+ Public directories like //uussrr//ttmmpp and //vvaarr//ttmmpp are
+ usually safe, although some sites periodically
+ prune old files from them. There is no require-
+ ment that you use a public directory, e.g. a sub-
+ directory of your home directory will work fine.
+
+ Finally, if you change the value of rreeccddiirr, you
+ must modify the recovery script to operate in your
+ chosen recovery area.
+
+ See the section entitled "RReeccoovveerryy" for further
+ information.
+
+ rreeddrraaww,, rree [[ooffff]]
+ VVii only. The editor simulates (using great
+ amounts of output), an intelligent terminal on a
+ dumb terminal (e.g. during insertions in vvii the
+ characters to the right of the cursor are
+ refreshed as each input character is typed).
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+
+ rreemmaapp [[oonn]]
+ If this option is set, it is possible to define
+ macros in terms of other macros. Otherwise, each
+ key is only remapped up to one time. For example,
+ if "AA" is mapped to "BB", and "BB" is mapped to "CC",
+ The keystroke "AA" will be mapped to "CC" if the
+ rreemmaapp option is set, and to "BB" if it is not set.
+
+ rreeppoorrtt [[55]]
+ Set the threshold of the number of lines that need
+ to be changed or yanked before a message will be
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--7766 NNvvii//NNeexx RReeffeerreennccee
+
+
+ displayed to the user. For everything but the
+ yank command, the value is the largest value about
+ which the editor is silent, i.e. by default, 6
+ lines must be deleted before the user is notified.
+ However, if the number of lines yanked is greater
+ than _o_r _e_q_u_a_l _t_o the set value, it is reported to
+ the user.
+
+ rruulleerr [[ooffff]]
+ VVii only. Display a row/column ruler on the colon
+ command line.
+
+ ssccrroollll,, ssccrr [[wwiinnddooww // 22]]
+ Set the number of lines scrolled by the vvii <<ccoonn--
+ ttrrooll--DD>> and <<ccoonnttrrooll--UU>> commands.
+
+ Historically, the eexx zz command, when specified
+ without a count, used two times the size of the
+ scroll value; the POSIX 1003.2 standard specified
+ the window size, which is a better choice.
+
+ sseeccttiioonnss,, sseecctt [[NNHHSSHHHH HHUUnnhhsshh]]
+ VVii only. Define additional section boundaries for
+ the [[[[ and ]]]] commands. The sseeccttiioonnss option
+ should be set to a character string consisting of
+ zero or more character pairs. In the text to be
+ edited, the character string <<nneewwlliinnee>>..<<cchhaarr--
+ ppaaiirr>>, (where <<cchhaarr--ppaaiirr>> is one of the character
+ pairs in the option's value), defines a section
+ boundary in the same manner that ppaarraaggrraapphh option
+ boundaries are defined.
+
+ sshheellll,, sshh [[eennvviirroonnmmeenntt vvaarriiaabbllee SSHHEELLLL,, oorr //bbiinn//sshh]]
+ Select the shell used by the editor. The speci-
+ fied path is the pathname of the shell invoked by
+ the vvii !! shell escape command and by the eexx sshheellll
+ command. This program is also used to resolve any
+ shell meta-characters in eexx commands.
+
+ sshhiiffttwwiiddtthh,, ssww [[88]]
+ Set the autoindent and shift command indentation
+ width. This width is used by the aauuttooiinnddeenntt
+ option and by the <<, >>, and sshhiifftt commands.
+
+ sshhoowwddiirrttyy [[ooffff]]
+ VVii only. Display an asterisk on the colon command
+ line if the file has been modified.
+
+ sshhoowwmmaattcchh,, ssmm [[ooffff]]
+ VVii only. This option causes vvii, when a "}}" or "))"
+ is entered, to briefly move the cursor the match-
+ ing "{{" or "((". See the mmaattcchhttiimmee option for more
+ information.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--7777
+
+
+ sshhoowwmmooddee [[ooffff]]
+ VVii only. This option causes vvii to display a
+ string identifying the current editor mode on the
+ colon command line.
+
+ ssiiddeessccrroollll [[1166]]
+ VVii only. Sets the number of columns that are
+ shifted to the left or right, when vvii is doing
+ left-right scrolling and the left or right margin
+ is crossed. See the lleeffttrriigghhtt option for more
+ information.
+
+ sslloowwooppeenn,, ssllooww [[ooffff]]
+ This option affects the display algorithm used by
+ vvii, holding off display updating during input of
+ new text to improve throughput when the terminal
+ in use is slow and unintelligent.
+
+ _T_h_i_s _o_p_t_i_o_n _i_s _n_o_t _y_e_t _i_m_p_l_e_m_e_n_t_e_d_.
+
+ ssoouurrcceeaannyy [[ooffff]]
+ If this option is turned on, vvii historically read
+ startup files that were owned by someone other
+ than the editor user. See the section entitled
+ "SSttaarrttuupp IInnffoorrmmaattiioonn" for more information. This
+ option is a security problem of immense propor-
+ tions, and should not be used under any circum-
+ stances.
+
+ _T_h_i_s _o_p_t_i_o_n _w_i_l_l _n_e_v_e_r _b_e _i_m_p_l_e_m_e_n_t_e_d_.
+
+ ttaabbssttoopp,, ttss [[88]]
+ This option sets tab widths for the editor dis-
+ play.
+
+ ttaagglleennggtthh,, ttll [[00]]
+ This option sets the maximum number of characters
+ that are considered significant in a tag name.
+ Setting the value to 0 makes all of the characters
+ in the tag name significant.
+
+ ttaaggss,, ttaagg [[ttaaggss //vvaarr//ddbb//lliibbcc..ttaaggss //ssyyss//kkeerrnn//ttaaggss]]
+ Sets the list of tags files, in search order,
+ which are used when the editor searches for a tag.
+
+ tteerrmm,, ttttyyttyyppee,, ttttyy [[eennvviirroonnmmeenntt vvaarriiaabbllee TTEERRMM]]
+ Set the terminal type. Setting this option causes
+ eexx/vvii to set (or reset) the environmental variable
+ TTEERRMM.
+
+ tteerrssee [[ooffff]]
+ This option has historically made editor messages
+ less verbose. It has no effect in this
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--7788 NNvvii//NNeexx RReeffeerreennccee
+
+
+ implementation. See the vveerrbboossee option for more
+ information.
+
+ ttiillddeeoopp
+ Modify the ~~ command to take an associated motion.
+
+ ttiimmeeoouutt,, ttoo [[oonn]]
+ If this option is set, eexx/vvii waits for a specific
+ period for a subsequent key to complete a key map-
+ ping (see the kkeeyyttiimmee option). If the option is
+ not set, the editor waits until enough keys are
+ entered to resolve the ambiguity, regardless of
+ how long it takes.
+
+ ttttyywweerraassee [[ooffff]]
+ VVii only. This option changes how vvii does word
+ erase during text input. If this option is set,
+ text is broken up into two classes, blank charac-
+ ters and nonblank characters. Changing from one
+ class to another marks the end of a word.
+
+ vveerrbboossee [[ooffff]]
+ VVii only. VVii historically bells the terminal for
+ many obvious mistakes, e.g. trying to move past
+ the left-hand margin, or past the end of the file.
+ If this option is set, an error message is dis-
+ played for all errors.
+
+ ww330000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is
+ less than 1200 baud. See the wwiinnddooww option for
+ more information.
+
+ ww11220000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is
+ equal to 1200 baud. See the wwiinnddooww option for
+ more information.
+
+ ww99660000 [[nnoo ddeeffaauulltt]]
+ VVii only. Set the window size if the baud rate is
+ greater than 1200 baud. See the wwiinnddooww option for
+ more information.
+
+ wwaarrnn [[oonn]]
+ EExx only. This option causes a warning message to
+ the terminal if the file has been modified, since
+ it was last written, before a !! command.
+
+ wwiinnddooww,, ww,, wwii [[eennvviirroonnmmeenntt vvaarriiaabbllee LLIINNEESS]]
+ This option determines the default number of lines
+ in a screenful, as written by the zz command. It
+ also determines the number of lines scrolled by
+ the vvii commands <<ccoonnttrrooll--FF>> and <<ccoonnttrrooll--BB>>. The
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--7799
+
+
+ value of window can be unrelated to the real
+ screen size, although it starts out as the number
+ of lines on the screen (see the section entitled
+ "SSiizziinngg tthhee SSccrreeeenn" for more information). Set-
+ ting the value of the wwiinnddooww option is the same as
+ using the --ww command line option.
+
+ If the value of the wwiinnddooww option (as set by the
+ wwiinnddooww, ww330000, ww11220000 or ww99660000 options) is smaller
+ than the actual size of the screen, large screen
+ movements will result in displaying only that
+ smaller number of lines on the screen. (Further
+ movements in that same area will result in the
+ screen being filled.) This can provide a perfor-
+ mance improvement when viewing different places in
+ one or more files over a slow link.
+
+ wwrraappmmaarrggiinn,, wwmm [[00]]
+ VVii only. If the value of the wwrraappmmaarrggiinn option is
+ non-zero, vvii will split lines so that they end at
+ least that number of characters before the right-
+ hand margin of the screen. (Note, the value of
+ wwrraappmmaarrggiinn is _n_o_t a text length. In a screen that
+ is 80 columns wide, the command "::sseett wwrraappmmaarr--
+ ggiinn==88" attempts to keep the lines less than or
+ equal to 72 columns wide.)
+
+ Lines are split at the previous whitespace charac-
+ ter closest to the number. Any trailing whites-
+ pace characters before that character are deleted.
+ If the line is split because of an inserted
+ <<ssppaaccee>> or <<ttaabb>> character, and you then enter
+ another <<ssppaaccee>> character, it is discarded.
+
+ If wrapmargin is set to 0, or if there is no blank
+ character upon which to split the line, the line
+ is not broken.
+
+ wwrraappssccaann,, wwss [[oonn]]
+ This option causes searches to wrap around the end
+ or the beginning of the file, and back to the
+ starting point. Otherwise, the end or beginning
+ of the file terminates the search.
+
+ wwrriitteeaannyy,, wwaa [[ooffff]]
+ If this option is set, file-overwriting checks
+ that would usually be made before the wwrriittee and
+ xxiitt commands, or before an automatic write (see
+ the aauuttoowwrriittee option), are not made. This allows
+ a write to any file, provided the file permissions
+ allow it.
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--8800 NNvvii//NNeexx RReeffeerreennccee
+
+
+ 1166.. AAddddiittiioonnaall FFeeaattuurreess iinn NNeexx//NNvvii
+
+ There are a few features in nneexx/nnvvii that are not
+ found in historic versions of eexx/vvii. Some of the more
+ interesting of those features are as follows:
+
+ 88--bbiitt cclleeaann ddaattaa,, llaarrggee lliinneess,, ffiilleess
+ NNeexx/nnvvii will edit any format file. Line lengths
+ are limited by available memory, and file sizes
+ are limited by available disk space. The vvii text
+ input mode command <<ccoonnttrrooll--XX>> can insert any pos-
+ sible character value into the text.
+
+ SSpplliitt ssccrreeeennss
+ The sspplliitt command divides the screen into multiple
+ editing regions. The <<ccoonnttrrooll--WW>> command rotates
+ between the foreground screens. The rreessiizzee com-
+ mand can be used to grow or shrink a particular
+ screen.
+
+ BBaacckkggrroouunndd aanndd ffoorreeggrroouunndd ssccrreeeennss
+ The bbgg command backgrounds the current screen, and
+ the ffgg command foregrounds backgrounded screens.
+ The ddiissppllaayy command can be used to list the back-
+ ground screens.
+
+ TTaagg ssttaacckkss
+ Tags are now maintained in a stack. The <<ccoonnttrrooll--
+ TT>> command returns to the previous tag location.
+ The ttaaggppoopp command returns to the most recent tag
+ location by default, or, optionally to a specific
+ tag number in the tag stack, or the most recent
+ tag from a specified file. The ddiissppllaayy command
+ can be used to list the tags stack. The ttaaggttoopp
+ command returns to the top of the tag stack.
+
+ NNeeww ddiissppllaayyss
+ The ddiissppllaayy command can be used to display the
+ current buffers, the backgrounded screens, and the
+ tags stack.
+
+ IInnffiinniittee uunnddoo
+ Changes made during an edit session may be rolled
+ backward and forward. A .. command immediately
+ after a uu command continues either forward or
+ backward depending on whether the uu command was an
+ undo or a redo.
+
+ UUssaaggee iinnffoorrmmaattiioonn
+ The eexxuussaaggee and vviiuussaaggee commands provide usage
+ information for all of the eexx and vvii commands by
+ default, or, optionally, for a specific command or
+ key.
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--8811
+
+
+ EExxtteennddeedd RReegguullaarr EExxpprreessssiioonnss
+ The eexxtteennddeedd option causes Regular Expressions to
+ be interpreted as as Extended Regular Expressions,
+ (i.e. _e_g_r_e_p(1) style Regular Expressions).
+
+ WWoorrdd sseeaarrcchh
+ The <<ccoonnttrrooll--AA>> command searches for the word ref-
+ erenced by the cursor.
+
+ NNuummbbeerr iinnccrreemmeenntt
+ The ## command increments or decrements the number
+ referenced by the cursor.
+
+ PPrreevviioouuss ffiillee
+ The pprreevviioouuss command edits the previous file from
+ the argument list.
+
+ LLeefftt--rriigghhtt ssccrroolllliinngg
+ The lleeffttrriigghhtt option causes nnvvii to do left-right
+ screen scrolling, instead of the traditional vvii
+ line wrapping.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--8822 NNvvii//NNeexx RReeffeerreennccee
+
+
+ 1177.. IInnddeexx
+
+ ! 15, 34 @ 20, 35 columns 47
+ "" 34 A 20 comment 47
+ # 16, 35 B 20 copy 36
+ $ 16 C 21 count 10, 33
+ % 16 D 21 current pathname 8
+ & 17, 42 E 21 d 26
+ ( 17 F 21 delete 37
+ ) 17 G 21 directory 47
+ * 35 H 21 display 37
+ + 13 I 22 e 26
+ , 18 J 22 edcompatible47
+ /RE/ 18 L 22 edit 37
+ 0 19 M 22 errorbells 47
+ 0<control-D>31 N 18 exrc 47
+ : 19 O 22 extended 48
+ ; 19 P 23 exusage 37
+ < 20, 35 Q 23 f 26
+ <control-A> 11 R 23 fg 37
+ <control-B> 11 S 23 file 33, 37
+ <control-1D2>, 31 T 23 flags 33
+ <control-E> 12 U 23 flash 48
+ <control-F> 12 W 24 global 38
+ <control-G> 12 X 24 hardtabs 48
+ <control-1H2>, 31 Y 24 help 38
+ <control-J> 13 ZZ 24 i 26
+ <control-L> 13 [[ 24 ignorecase 48
+ <control-M> 13 - 18 insert 38
+ <control-N> 13 ]] 25 j 13
+ <control-P> 13 ^ 25 join 38
+ <control-R> 13 ^<control-D>31 k 13, 39
+ <control-1T4>, 31 _ 25 keytime 48
+ <control-U> 14 `<character>17 l 15
+ <control-1W4>, 31 a 25 leftright 48
+ <control-X> 31 abbrev 35 line 33
+ <control-Y> 14 alternate pathname 8 lines 48
+ <control-1Z4>, 43 altwerase 46 lisp 48
+ <control-]> 15 append 36 list 39, 48
+ <control-^> 15 args 36 m 27
+ <end-of-file> 34 autoindent 46 magic 48
+ <eof> 33 autoprint 46 map 39
+ <erase> 31 autowrite 47 mark 39
+ <escape>14, 31 b 25 matchtime 48
+ <inter7r,up3t0>, 31 beautify 47 mesg 48
+ <line erase>31 bg 36 mkexrc 39
+ <literal next> 7, 31 bigword 10 modelines 49
+ <nul> 30 buffer 8 motion 10
+ <space> 15 c 26 move 39
+ <word erase>31 cd 36 n 18
+ = 35 cdpath 47 next 40
+ > 20, 35 change 36 number 35, 49
+ ?RE? 18 chdir 36 o 27
+
+
+
+
+
+
+
+
+
+
+NNvvii//NNeexx RReeffeerreennccee UUSSDD::1133--8833
+
+
+ octal 49 tildeop 51
+ open 40, 49 timeout 51
+ optimize 49 ttywerase 52
+ p 27 u 28
+ paragraph 11 unabbrev 44
+ paragraphs 49 undo 44
+ preserve 40 unmap 44
+ previous 40 unnamed buffer 8
+ previous context 9 v 38
+ print 40 verbose 52
+ prompt 49 version 44
+ put 40 visual 44
+ quit 41 viusage 44
+ r 27 w 28
+ range 33 w1200 52
+ read 41 w300 52
+ readonly 49 w9600 52
+ recdir 49 warn 52
+ recover 41 window 52
+ redraw 50 wn 44
+ remap 50 word 10
+ report 50 wq 44
+ resize 41 wrapmargin 52
+ rewind 41 wrapscan 52
+ ruler 50 write 44
+ s 27 writeany 53
+ scroll 50 x 28
+ section 11 xit 45
+ sections 50 y 28
+ sentence 11 yank 45
+ set 41 z 28, 45
+ shell 42, 50 { 29
+ shiftwidth 50 | 29
+ showdirty 51 } 29
+ showmatch 51 ~ 29, 30, 42
+ showmode 51
+ sidescroll 51
+ slowopen 51
+ source 42
+ sourceany 51
+ split 42
+ stop 43
+ substitute 42
+ suspend 43
+ t 27, 36
+ tabstop 51
+ tag 43
+ taglength 51
+ tagpop 43
+ tags 51
+ tagtop 43
+ term 51
+ terse 51
+
+
+
+
+
+
+
+
+
+
+UUSSDD::1133--22 NNvvii//NNeexx RReeffeerreennccee
+
+
+ TTaabbllee ooff CCoonntteennttss
+
+ Description ...................................... 3
+ Startup Information .............................. 3
+ Recovery ......................................... 4
+ Sizing the Screen ................................ 7
+ Character Display ................................ 7
+ Multiple Screens ................................. 8
+ Regular Expressions and Replacement Strings ...... 9
+ General Editor Description ....................... 10
+ Vi Description ................................... 12
+ Vi Commands ...................................... 17
+ Vi Text Input Commands ........................... 45
+ Ex Addressing .................................... 47
+ Ex Description ................................... 49
+ Ex Commands ...................................... 50
+ Set Options ...................................... 68
+ Additional Features in Nex/Nvi ................... 79
+ Index ............................................ 82
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.bin/vi/USD.doc/vitut/Makefile b/usr.bin/vi/USD.doc/vitut/Makefile
new file mode 100644
index 0000000..bfb29bf
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vitut/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 8/14/93
+
+DIR= usd/12.vi
+SRCS= vi.in vi.chars
+MACROS= -msU
+CLEANFILES+=summary.* viapwh.*
+
+paper.ps: ${SRCS} summary.ps viapwh.ps
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+summary.ps: vi.summary
+ ${TBL} vi.summary | ${ROFF} > ${.TARGET}
+
+viapwh.ps: vi.apwh.ms
+ ${ROFF} vi.apwh.ms > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.bin/vi/USD.doc/vitut/vi.apwh.ms b/usr.bin/vi/USD.doc/vitut/vi.apwh.ms
new file mode 100644
index 0000000..d0b6261
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vitut/vi.apwh.ms
@@ -0,0 +1,1079 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.apwh.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+Vi Command & Function Reference
+.AU CB 2675
+Alan P.W. Hewett
+.sp
+Revised for version 2.12 by Mark Horton
+.CB
+.NH 1
+Author's Disclaimer
+.LP
+This document does not claim to be 100% complete. There are a
+few commands listed in the original document that I was unable
+to test either because I do not speak \fBlisp\fR, because they
+required programs we don't have, or because I wasn't able to make
+them work. In these cases I left the command out. The commands
+listed in this document have been tried and are known to work.
+It is expected that prospective users of this document will read
+it once to get the flavor of everything that \fBvi\fR can do
+and then use it as a reference document. Experimentation is
+recommended. If you don't understand a command, try it and
+see what happens.
+.LP
+[Note: In revising this document, I have attempted to make it
+completely reflect version 2.12 of
+.B vi .
+It does not attempt to document the VAX version (version 3),
+but with one or two exceptions (wrapmargin, arrow keys)
+everything said about 2.12 should apply to 3.1.
+.I "Mark Horton" ]
+.NH 1
+Notation
+.LP
+\fB[option]\fR is used to denote optional parts of a command.
+Many \fBvi\fR commands have an optional count. \fB[cnt]\fR
+means that an optional number may precede the command to
+multiply or iterate the command.
+\fB{variable item}\fR is used to denote parts of the command
+which must appear, but can take a number of different values.
+\fB<character [-character]>\fR means that the character or
+one of the characters in the range described between the
+two angle brackets is to be typed.
+For example \fB<esc>\fR means
+the \fBescape\fR key is to be typed. \fB<a-z>\fR means that a
+lower case letter is to be typed. \fB^<character>\fR means that
+the character is to be typed as a \fBcontrol\fR character, that is,
+with the \fB<cntl>\fR key held down while simultaneously typing
+the specified character. In this document control characters will
+be denoted using the \fIupper case\fR character, but
+^<uppercase chr> and ^<lowercase chr> are equivalent. That is, for
+example, \fB<^D>\fR is equal to \fB<^d>\fR.
+The most common character abbreviations
+used in this list are as follows:
+.VL 8
+.IP <esc> 8
+escape, octal 033
+.IP <cr> 8
+carriage return, ^M, octal 015
+.IP <lf> 8
+linefeed ^J, octal 012
+.IP <nl> 8
+newline, ^J, octal 012 (same as linefeed)
+.IP <bs> 8
+backspace, ^H, octal 010
+.IP <tab> 8
+tab, ^I, octal 011
+.IP <bell> 8
+bell, ^G, octal 07
+.IP <ff> 8
+formfeed, ^L, octal 014
+.IP <sp> 8
+space, octal 040
+.IP <del> 8
+delete, octal 0177
+.LE
+.sp 1
+.NH 1
+Basics
+.LP
+To run \fBvi\fR the shell variable \fBTERM\fR must be defined and
+exported to your environment.
+How you do this depends on which shell you are using.
+You can tell which shell you have by the character it
+prompts you for commands with.
+The Bourne shell prompts with `$', and the C shell prompts with `%'.
+For these examples, we will suppose
+that you are using an HP 2621 terminal, whose termcap name is ``2621''.
+.NH 2
+Bourne Shell
+.LP
+To manually set your terminal type to 2621 you would type:
+.DS
+TERM=2621
+export TERM
+.DE
+.PP
+There are various ways of having this automatically or
+semi-automatically done when you log in.
+Suppose you usually dial in on a 2621.
+You want to tell this to the machine, but still have it
+work when you use a hardwired terminal.
+The recommended way, if you have the
+.B tset
+program, is to use the sequence
+.DS
+tset \-s \-d 2621 > tset$$
+\&. tset$$
+rm tset$$
+.DE
+in your .login (for csh) or the same thing using `.' instead of `source'
+in your .profile (for sh).
+The above line says that if you are dialing in you are on a 2621,
+but if you are on a hardwired terminal it figures out your terminal
+type from an on-line list.
+.NH 2
+The C Shell
+.LP
+To manually set your terminal type to 2621 you would type:
+.DS
+setenv TERM 2621
+.DE
+.PP
+There are various ways of having this automatically or
+semi-automatically done when you log in.
+Suppose you usually dial in on a 2621.
+You want to tell this to the machine, but still have it
+work when you use a hardwired terminal.
+The recommended way, if you have the
+.B tset
+program, is to use the sequence
+.DS
+tset \-s \-d 2621 > tset$$
+source tset$$
+rm tset$$
+.DE
+in your .login.*
+.FS
+* On a version 6 system
+without environments, the invocation of tset
+is simpler, just add the line ``tset \-d 2621''
+to your .login or .profile.
+.FE
+The above line says that if you are dialing in you are on a 2621,
+but if you are on a hardwired terminal it figures out your terminal
+type from an on-line list.
+.NH 1
+Normal Commands
+.LP
+\fBVi\fR is a visual editor with a window on the file. What
+you see on the screen is \fBvi\fR's current notion of
+what your file will contain,
+(at this point in the file),
+when it is written out.
+Most commands do not cause any change in the screen until the
+complete command is typed. Should you get confused while
+typing a command, you can abort the command by typing an
+<del> character. You will know you are back to command level
+when you hear a <bell>. Usually typing an <esc> will produce the
+same result. When \fBvi\fR gets an improperly formatted command
+it rings the <bell>.
+Following are the \fBvi\fR commands broken down by function.
+.NH 2
+Entry and Exit
+.LP
+To enter
+.B vi
+on a particular
+.I file ,
+type
+.DS
+\fBvi\fP \fIfile\fP
+.DE
+The file will be read in and the cursor will be placed at the beginning
+of the first line.
+The first screenfull of the file will be displayed on the terminal.
+.PP
+To get out of the editor, type
+.DS
+ZZ
+.DE
+If you are in some special mode, such as input mode
+or the middle of a multi-keystroke command, it may
+be necessary to type <esc> first.
+.NH 2
+Cursor and Page Motion
+.LP
+.VL 16
+.B NOTE:
+The arrow keys (see the next four commands)
+on certain kinds of terminals will not work with the
+PDP-11 version of vi. The control versions or the hjkl versions will
+work on any terminal. Experienced users prefer the hjkl keys because
+they are always right under their fingers. Beginners often prefer
+the arrow keys, since they do not require memorization of which hjkl
+key is which.
+The mnemonic value of hjkl is clear from looking at the keyboard of an adm3a.
+.sp
+.IP "[cnt]<bs> or [cnt]h or [cnt]\(<-" 16
+.br
+Move the cursor to the left one character. Cursor stops at the left
+margin of the page.
+If cnt is given, these commands move that many spaces.
+.IP "[cnt]^N or [cnt]j or [cnt]\(da or [cnt]<lf>" 16
+.br
+Move down one line.
+Moving off the screen scrolls the window to force a new line
+onto the screen.
+Mnemonic: \fBN\fRext
+.IP "[cnt]^P or [cnt]k or [cnt]\(ua" 16
+.br
+Move up one line.
+Moving off the top of the screen forces new text onto the screen.
+Mnemonic: \fBP\fRrevious
+.IP "[cnt]<sp> or [cnt]l or [cnt]\(->" 16
+.br
+Move to the right one character.
+Cursor will not go beyond the end of the line.
+.IP [cnt]- 16
+Move the cursor up the screen to the beginning of the next line.
+Scroll if necessary.
+.IP "[cnt]+ or [cnt]<cr>" 16
+.sp 1
+Move the cursor down the screen to the beginning of the next line.
+Scroll up if necessary.
+.IP "[cnt]$" 16
+Move the cursor to the end of the line.
+If there is a count, move to the end of the line "cnt" lines
+forward in the file.
+.IP "^" 16
+Move the cursor to the beginning of the first word on the line.
+.IP "0" 16
+Move the cursor to the left margin of the current line.
+.IP "[cnt]|" 16
+Move the cursor to the column specified by the count. The default is
+column zero.
+.IP "[cnt]w" 16
+Move the cursor to the beginning of the next word. If there
+is a count, then move forward that many words and
+position the cursor at the beginning of the word.
+Mnemonic: next-\fBw\fRord
+.IP "[cnt]W" 16
+Move the cursor to the beginning of the next word which follows
+a "white space" (<sp>,<tab>, or <nl>). Ignore other punctuation.
+.IP "[cnt]b" 16
+Move the cursor to the preceding word. Mnemonic: \fBb\fRackup-word
+.IP "[cnt]B" 16
+Move the cursor to the preceding word that is separated from the
+current word by a "white space" (<sp>,<tab>, or <nl>).
+.IP "[cnt]e" 16
+Move the cursor to the end of the current word or the end of the
+"cnt"'th word hence. Mnemonic: \fBe\fRnd-of-word
+.IP "[cnt]E" 16
+Move the cursor to the end of the current word which is delimited by
+"white space" (<sp>,<tab>, or <nl>).
+.IP "[line number]G" 16
+.br
+Move the cursor to the line specified. Of particular use are the
+sequences "1G" and "G", which move the cursor to the beginning and
+the end of the file respectively. Mnemonic: \fBG\fRo-to
+.LP
+.B NOTE:
+The next four commands (^D, ^U, ^F, ^B)
+are not true motion commands, in that they
+cannot be used as the object of commands such as delete or change.
+.IP "[cnt]^D" 16
+Move the cursor down in the file by "cnt" lines (or the last "cnt"
+if a new count isn't given. The initial default is half a page.) The
+screen is simultaneously scrolled up. Mnemonic: \fBD\fRown
+.IP "[cnt]^U" 16
+Move the cursor up in the file by "cnt" lines. The screen is simultaneously
+scrolled down. Mnemonic: \fBU\fRp
+.IP "[cnt]^F" 16
+Move the cursor to the next page. A count moves that many pages.
+Two lines of the previous page are kept on the screen for continuity if
+possible. Mnemonic: \fBF\fRorward-a-page
+.IP "[cnt]^B" 16
+Move the cursor to the previous page. Two lines of the current page
+are kept if possible. Mnemonic: \fBB\fRackup-a-page
+.IP "[cnt](" 16
+Move the cursor to the beginning of the next sentence.
+A sentence is defined as ending with a ".", "!", or "?"
+followed by two spaces or a <nl>.
+.IP "[cnt])" 16
+Move the cursor backwards to the beginning of a sentence.
+.IP "[cnt]}" 16
+Move the cursor to the beginning of the next paragraph. This command
+works best inside \fBnroff\fR documents. It understands two sets of
+\fBnroff\fR macros, \fB\-ms\fR and \fB\-mm\fR, for which the
+commands ".IP", ".LP", ".PP", ".QP", "P", as well as the nroff command ".bp"
+are considered to be paragraph delimiters.
+A blank line also delimits a paragraph.
+The \fBnroff\fR macros that it accepts as paragraph delimiters is
+adjustable. See \fBparagraphs\fR under the \fBSet Commands\fR section.
+.IP "[cnt]{" 16
+Move the cursor backwards to the beginning of a paragraph.
+.IP "]]" 16
+Move the cursor to the next "section", where a section is defined by
+two sets of \fBnroff\fR macros, \fB\-ms\fR and \fB\-mm\fR, in which
+".NH", ".SH", and ".H" delimit a section. A line beginning with a <ff><nl>
+sequence, or a line beginning with a "{" are also considered to
+be section delimiters. The last option makes it
+useful for finding the beginnings of C functions.
+The \fBnroff\fR macros that are used for section delimiters can be adjusted.
+See \fBsections\fR under the \fBSet Commands\fR section.
+.IP "[[" 16
+Move the cursor backwards to the beginning of a section.
+.IP "%" 16
+Move the cursor to the matching parenthesis
+or brace. This is very useful in C or lisp code. If the
+cursor is sitting on a \fB( ) {\fR or \fB}\fR the cursor
+is moved to the matching character at the other end of the
+section. If the cursor is not sitting on a brace or a
+parenthesis, \fBvi\fR searches forward until it finds one
+and then jumps to the match mate.
+.IP "[cnt]H" 16
+If there is no count move the cursor to the top left position on the screen.
+If there is a count, then move the cursor to the beginning of the line
+"cnt" lines from the top of the screen. Mnemonic: \fBH\fRome
+.IP "[cnt]L" 16
+If there is no count move the cursor to the beginning
+of the last line on the screen.
+If there is a count, then move the cursor to the beginning of the line
+"cnt" lines from the bottom of the screen. Mnemonic: \fBL\fRast
+.IP "M" 16
+Move the cursor to the beginning of the middle line on the screen.
+Mnemonic: \fBM\fRiddle
+.IP "m<a-z>" 16
+This command does not move the cursor, but it \fBmarks\fR the place
+in the file and the character "<a-z>" becomes the label for referring
+to this location in the file. See the next two commands. Mnemonic:
+\fBm\fRark
+.B NOTE:
+The mark command is not a motion, and cannot be used as the target
+of commands such as delete.
+.IP "\(aa<a-z>" 16
+Move the cursor to the beginning of the line that is marked with the label
+"<a-z>".
+.IP "\(ga<a-z>" 16
+Move the cursor to the exact position on the line that was marked with
+with the label "<a-z>".
+.IP "\(aa\(aa" 16
+Move the cursor back to the beginning of the line where it was before the
+last "non-relative" move. A "non-relative" move is something such as a
+search or a jump to a specific line in the file, rather than moving the
+cursor or scrolling the screen.
+.IP "\(ga\(ga" 16
+Move the cursor back to the exact spot on the line where it was located
+before the last "non-relative" move.
+.LE
+.NH 2
+Searches
+.LP
+The following commands allow you to search for items in a file.
+.VL 16
+.IP [cnt]f{chr} 16
+.sp 1
+Search forward on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBat\fR the character
+of interest. Mnemonic: \fBf\fRind character
+.IP [cnt]F{chr} 16
+.sp 1
+Search backwards on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBat\fR the character
+of interest.
+.IP [cnt]t{chr} 16
+.sp 1
+Search forward on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBjust preceding\fR
+the character of interest. Mnemonic: move cursor up \fBt\fRo character
+.IP [cnt]T{chr} 16
+.sp 1
+Search backwards on the line for the next or "cnt"'th occurrence of
+the character "chr". The cursor is placed \fBjust preceding\fR
+the character of interest.
+.IP "[cnt];" 16
+Repeat the last "f", "F", "t" or "T" command.
+.IP "[cnt]," 16
+Repeat the last "f", "F", "t" or "T" command, but in the opposite
+search direction. This is useful if you overshoot.
+.IP "[cnt]/[string]/<nl>" 16
+.br
+Search forward for the next occurrence of "string".
+Wrap around at the end of the file
+does occur.
+The final \fB</>\fR is not required.
+.IP "[cnt]?[string]?<nl>" 16
+.br
+Search backwards for the next occurrence of "string". If a count is
+specified, the count becomes the new window size. Wrap around at the beginning
+of the file does occur.
+The final \fB<?>\fR is not required.
+.IP n 16
+Repeat the last /[string]/ or ?[string]? search. Mnemonic: \fBn\fRext
+occurrence.
+.IP N 16
+Repeat the last /[string]/ or ?[string]? search, but in the reverse
+direction.
+.IP ":g/[string]/[editor command]<nl>" 16
+.sp 1
+Using the \fB:\fR syntax it is possible to do global searches ala the
+standard UNIX "ed" editor.
+.LE
+.NH 2
+Text Insertion
+.LP
+The following commands allow for the insertion of text. All multicharacter
+text insertions are terminated with an <esc> character.
+The last change
+can always be \fBundone\fR by typing a \fBu\fR.
+The text insert in insertion mode can contain newlines.
+.VL 16
+.IP a{text}<esc> 16
+Insert text immediately following the cursor position.
+Mnemonic: \fBa\fRppend
+.IP A{text}<esc> 16
+Insert text at the end of the current line.
+Mnemonic: \fBA\fRppend
+.IP i{text}<esc> 16
+Insert text immediately preceding the cursor position.
+Mnemonic: \fBi\fRnsert
+.IP I{text}<esc> 16
+Insert text at the beginning of the current line.
+.IP o{text}<esc> 16
+Insert a new line after the line on which the cursor appears and
+insert text there. Mnemonic: \fBo\fRpen new line
+.IP O{text}<esc> 16
+Insert a new line preceding the line on which the cursor appears
+and insert text there.
+.LE
+.NH 2
+Text Deletion
+.LP
+The following commands allow the user to delete text in various ways.
+All changes can always be \fBundone\fR by typing the \fBu\fR command.
+.VL 16
+.IP "[cnt]x" 16
+Delete the character or characters starting at the cursor position.
+.IP "[cnt]X" 16
+Delete the character or characters starting at the character preceding
+the cursor position.
+.IP "D" 16
+Deletes the remainder of the line starting at the cursor.
+Mnemonic: \fBD\fRelete the rest of line
+.IP "[cnt]d{motion}" 16
+.br
+Deletes one or more occurrences of the specified motion.
+Any motion from sections 4.1 and 4.2 can be used here.
+The d can be stuttered (e.g. [cnt]dd) to delete cnt lines.
+.LE
+.NH 2
+Text Replacement
+.LP
+The following commands allow the user to simultaneously delete and
+insert new text. All such actions can be \fBundone\fR by typing
+\fBu\fR following the command.
+.VL 16
+.IP "r<chr>" 16
+Replaces the character at the current cursor position with <chr>. This
+is a one character replacement. No <esc> is required for termination.
+Mnemonic: \fBr\fReplace character
+.IP "R{text}<esc>" 16
+Starts overlaying the characters on the screen with whatever you type.
+It does not stop until an <esc> is typed.
+.IP "[cnt]s{text}<esc>" 16
+Substitute for "cnt" characters beginning at the current cursor
+position. A "$" will appear at the position in the text where the
+"cnt"'th character appears so you will know how much you are erasing.
+Mnemonic: \fBs\fRubstitute
+.IP "[cnt]S{text}<esc>" 16
+Substitute for the entire current line (or lines). If no count is given,
+a "$" appears at the end of the current line. If a count of more than
+1 is given, all the lines to be replaced are deleted before the insertion
+begins.
+.IP "[cnt]c{motion}{text}<esc>" 16
+.br
+Change the specified "motion" by replacing it with the
+insertion text. A "$" will appear at the end of the last item
+that is being deleted unless the deletion involves whole lines.
+Motion's can be any motion from sections 4.1 or 4.2.
+Stuttering the c (e.g. [cnt]cc) changes cnt lines.
+.LE
+.NH 2
+Moving Text
+.LP
+\fBVi\fR provides a number of ways of moving chunks of text around.
+There are nine buffers into which each piece of text which is deleted
+or "yanked" is put in addition to the "undo" buffer.
+The most recent deletion or yank is in the "undo" buffer and also
+usually in buffer
+1, the next most recent in buffer 2, and so forth. Each new deletion
+pushes down all the older deletions. Deletions older than 9
+disappear. There is also
+a set of named registers, a-z, into which text can optionally
+be placed. If any delete or replacement type command is preceded
+by \fB"<a-z>\fR, that named buffer will contain the text deleted
+after the command is executed. For example, \fB"a3dd\fR will delete
+three lines starting at the current line and put them in buffer \fB"a\fR.*
+.FS
+* Referring to an upper case letter as a buffer name (A-Z) is the
+same as referring to the lower case letter, except that text placed
+in such a buffer is appended to it instead of replacing it.
+.FE
+There are two more basic commands and
+some variations useful in getting and putting text into a file.
+.VL 16
+.IP ["<a-z>][cnt]y{motion} 16
+.sp 1
+Yank the specified item or "cnt" items and put in the "undo" buffer or
+the specified buffer. The variety of "items" that can be yanked
+is the same as those that can be deleted with the "d" command or
+changed with the "c" command. In the same way that "dd" means
+delete the current line and "cc" means replace the current line,
+"yy" means yank the current line.
+.IP ["<a-z>][cnt]Y 16
+Yank the current line or the "cnt" lines starting from the current
+line. If no buffer is specified, they will go into the "undo" buffer,
+like any delete would. It is equivalent to "yy".
+Mnemonic: \fBY\fRank
+.IP ["<a-z>]p 16
+Put "undo" buffer or the specified buffer down \fBafter\fR the cursor.
+If whole lines were yanked or deleted into the buffer, then they will be
+put down on the line following the line the cursor is on. If
+something else was deleted, like a word or sentence, then it will
+be inserted immediately following the cursor.
+Mnemonic: \fBp\fRut buffer
+.IP
+It should be noted that text in the named buffers remains there when you
+start editing a new file with the \fB:e file<esc>\fR command. Since
+this is so, it is possible to copy or delete text from one file and
+carry it over to another file in the buffers.
+However, the undo buffer and the ability to undo are lost when
+changing files.
+.IP ["<a-z>]P 16
+Put "undo" buffer or the specified buffer down \fBbefore\fR the cursor.
+If whole lines where yanked or deleted into the buffer, then they will be
+put down on the line preceding the line the cursor is on. If
+something else was deleted, like a word or sentence, then it will
+be inserted immediately preceding the cursor.
+.IP [cnt]>{motion} 16
+The shift operator will right shift all the text from the line on which
+the cursor is located to the line where the \fBmotion\fR is located.
+The text is shifted by one \fBshiftwidth\fR. (See section 6.)
+\fB>>\fR means right shift the current line or lines.
+.IP [cnt]<{motion} 16
+The shift operator will left shift all the text from the line on which
+the cursor is located to the line where the \fBitem\fR is located.
+The text is shifted by one \fBshiftwidth\fR. (See section 6.)
+\fB<<\fR means left shift the current line or lines.
+Once the line has reached the left margin it is not further affected.
+.IP [cnt]={motion} 16
+Prettyprints the indicated area according to
+.B lisp
+conventions.
+The area should be a lisp s-expression.
+.LE
+.NH 2
+Miscellaneous Commands
+.LP
+\fBVi\fR has a number of miscellaneous commands that are very
+useful. They are:
+.VL 16
+.IP ZZ 16
+This is the normal way to exit from vi.
+If any changes have been made, the file is written out.
+Then you are returned to the shell.
+.IP ^L 16
+Redraw the current screen. This is useful if someone "write"s you
+while you are in "vi" or if for any reason garbage gets onto the
+screen.
+.IP ^R 16
+On dumb terminals, those not having the "delete line" function
+(the vt100 is such a terminal), \fBvi\fR saves redrawing the
+screen when you delete a line by just marking the line with an
+"@" at the beginning and blanking the line. If you want to
+actually get rid of the lines marked with "@" and see what the
+page looks like, typing a ^R will do this.
+.IP \s+4.\s0 16
+"Dot" is a particularly useful command. It repeats the last
+text modifying command. Therefore you can type a command once and
+then to another place and repeat it by just typing ".".
+.IP u 16
+Perhaps the most important command in the editor,
+u undoes the last command that changed the buffer.
+Mnemonic: \fBu\fRndo
+.IP U 16
+Undo all the text modifying commands performed on the current line
+since the last time you moved onto it.
+.IP [cnt]J 16
+Join the current line and the following line. The <nl> is deleted
+and the two lines joined, usually with a space between the
+end of the first line and the beginning of what was the second
+line. If the first line ended with a "period", then two spaces
+are inserted.
+A count joins the next cnt lines.
+Mnemonic: \fBJ\fRoin lines
+.IP Q 16
+Switch to \fBex\fR editing mode.
+In this mode \fBvi\fR will behave very much like \fBed\fR.
+The editor in this mode will operate on single lines normally and
+will not attempt to keep the "window" up to date.
+Once in this mode it is also possible to switch to the \fBopen\fR
+mode of editing. By entering the command \fB[line number]open<nl>\fR
+you enter this mode. It is similar to the normal visual mode
+except the window is only \fBone\fR line long.
+Mnemonic: \fBQ\fRuit visual mode
+.IP ^] 16
+An abbreviation for a tag command.
+The cursor should be positioned at the beginning of a word.
+That word is taken as a tag name, and the tag with that
+name is found as if it had been typed in a :tag command.
+.IP [cnt]!{motion}{UNIX\ cmd}<nl> 16
+.br
+Any UNIX filter
+(e.g. command that reads the standard input and outputs something
+to the standard output) can be sent a section of the current file and
+have the output of the command replace the original text. Useful
+examples are programs like \fBcb\fR, \fBsort\fR, and
+\fBnroff\fR. For instance, using \fBsort\fR it would be possible to
+sort a section of the current file into a new list.
+Using \fB!!\fR means take a line or lines starting at the line the
+cursor is currently on and pass them to the UNIX command.
+.B NOTE:
+To just escape to the shell for one command,
+use :!{cmd}<nl>, see section 5.
+.IP z{cnt}<nl> 16
+This resets the current window size to "cnt" lines and redraws the screen.
+.LE
+.NH 2
+Special Insert Characters
+.LP
+There are some characters that have special meanings during
+insert modes. They are:
+.VL 16
+.IP ^V 16
+During inserts, typing a ^V allows you to quote control characters
+into the file. Any character typed after the ^V will be inserted
+into the file.
+.IP [^]^D\ or\ [0]^D 16
+<^D> without any argument backs up one \fBshiftwidth\fR. This is necessary
+to remove indentation that was inserted by the \fBautoindent\fR feature.
+^<^D> temporarily removes all the autoindentation, thus placing the cursor
+at the left margin. On the next line, the previous indent level will be
+restored. This is useful for putting "labels" at the left margin.
+0<^D> says remove all autoindents and stay that way. Thus the cursor
+moves to the left margin and stays there on successive lines until
+<tab>'s are typed. As with the <tab>, the <^D> is only effective before
+any other "non-autoindent" controlling characters are typed.
+Mnemonic: \fBD\fRelete a shiftwidth
+.IP ^W 16
+If the cursor is sitting on a word, <^W> moves the cursor back to the beginning
+of the word, thus erasing the word from the insert.
+Mnemonic: erase \fBW\fRord
+.IP <bs> 16
+The backspace always serves as an erase during insert modes in addition
+to your normal "erase" character. To insert a <bs> into your file, use
+the <^V> to quote it.
+.LE
+.NH 1
+\fB:\fR Commands
+.LP
+Typing a ":" during command mode causes \fBvi\fR to put the cursor at
+the bottom on the screen in preparation for a command. In the
+":" mode, \fBvi\fR can be given most \fBed\fR commands. It is
+also from this mode that you exit from \fBvi\fR or switch to different
+files. All commands of this variety are terminated by a <nl>, <cr>,
+or <esc>.
+.VL 16
+.IP ":w[!] [file]" 16
+Causes \fBvi\fR to write out the current text to the disk. It is
+written to the file you are editing unless "file" is supplied. If
+"file" is supplied, the write is directed to that file instead. If
+that file already exists, \fBvi\fR will not perform the write unless
+the "!" is supplied indicating you
+.I really
+want to destroy the older copy of the file.
+.IP :q[!] 16
+Causes \fBvi\fR to exit. If you have modified the file you are
+looking at currently and haven't written it out, \fBvi\fR will
+refuse to exit unless the "!" is supplied.
+.IP ":e[!] [+[cmd]] [file]" 16
+.sp 1
+Start editing a new file called "file" or start editing the current
+file over again. The command ":e!" says "ignore the changes I've made
+to this file and start over from the beginning". It is useful if
+you really mess up the file. The optional "+" says instead of starting
+at the beginning, start at the "end", or,
+if "cmd" is supplied, execute "cmd" first.
+Useful cases of this are where cmd is "n" (any integer) which starts
+at line number n,
+and "/text", which searches for "text" and starts at the line where
+it is found.
+.IP "^^" 16
+Switch back to the place you were before your last tag command.
+If your last tag command stayed within the file, ^^ returns to that tag.
+If you have no recent tag command, it will return to the
+same place in the previous file that it was showing when you switched
+to the current file.
+.IP ":n[!]" 16
+Start editing the next file in the argument list. Since \fBvi\fR
+can be called with multiple file names, the ":n" command tells it to
+stop work on the current file and switch to the next file. If the
+current file was modifies, it has to be written out before the ":n"
+will work or else the "!" must be supplied, which says discard the
+changes I made to the current file.
+.IP ":n[!] file [file file ...]" 16
+.sp
+Replace the current argument list with a new list of files and start
+editing the first file in this new list.
+.IP ":r file" 16
+Read in a copy of "file" on the line after the cursor.
+.IP ":r !cmd" 16
+Execute the "cmd" and take its output and put it into the file after
+the current line.
+.IP ":!cmd" 16
+Execute any UNIX shell command.
+.IP ":ta[!] tag" 16
+.B Vi
+looks in the file named
+.B tags
+in the current directory.
+.B Tags
+is a file of lines in the format:
+.sp 1
+.ti +8
+tag filename \fBvi\fR-search-command
+.sp 1
+If \fBvi\fR finds the tag you specified in the \fB:ta\fR command,
+it stops editing the current file if necessary and if the current file is
+up to date on the disk and switches to the file specified and uses the
+search pattern specified to find the "tagged" item of interest. This
+is particularly useful when editing multi-file C programs such as the
+operating system. There is a program called \fBctags\fR which will
+generate an appropriate \fBtags\fR file for C and f77
+programs so that by saying
+\fB:ta function<nl>\fR you will be switched to that function.
+It could also be useful when editing multi-file documents, though the
+\fBtags\fR file would have to be generated manually.
+.LE
+.NH 1
+Special Arrangements for Startup
+.PP
+\fBVi\fR takes the value of \fB$TERM\fR and looks up the characteristics
+of that terminal in the file \fB/etc/termcap\fR.
+If you don't know \fBvi\fR's name for the terminal you are working
+on, look in \fB/etc/termcap\fR.
+.PP
+When \fBvi\fR starts, it attempts to read the variable EXINIT
+from your environment.*
+If that exists, it takes the values in it as the default values
+for certain of its internal constants. See the section on "Set Values"
+for further details.
+If EXINIT doesn't exist you will get all the normal defaults.
+.FS
+* On version 6 systems
+Instead of EXINIT, put the startup commands in the file .exrc
+in your home directory.
+.FE
+.PP
+Should you inadvertently hang up the phone while inside
+.B vi ,
+or should the computer crash,
+all may not be lost.
+Upon returning to the system, type:
+.DS
+vi \-r file
+.DE
+This will normally recover the file. If there is more than one
+temporary file for a specific file name, \fBvi\fR recovers the
+newest one. You can get an older version by recovering the
+file more than once.
+The command "vi -r" without a file name gives you the list of files
+that were saved in the last system crash
+(but
+.I not
+the file just saved when the phone was hung up).
+.NH 1
+Set Commands
+.LP
+\fBVi\fR has a number of internal variables and switches which can be
+set to achieve special affects.
+These options come in three forms, those that are switches, which toggle
+from off to on and back, those that require a numeric value, and those
+that require an alphanumeric string value.
+The toggle options are set by a command of the form:
+.DS
+:set option<nl>
+.DE
+and turned off with the command:
+.DS
+:set nooption<nl>
+.DE
+Commands requiring a value are set with a command of the form:
+.DS
+:set option=value<nl>
+.DE
+To display the value of a specific option type:
+.DS
+:set option?<nl>
+.DE
+To display only those that you have changed type:
+.DS
+:set<nl>
+.DE
+and to display the long table of all the settable parameters and
+their current values type:
+.DS
+:set all<nl>
+.DE
+.PP
+Most of the options have a long form and an abbreviation. Both are
+listed in the following table as well as the normal default value.
+.PP
+To arrange to have values other than the default used every time you
+enter
+.B vi ,
+place the appropriate
+.B set
+command in EXINIT in your environment, e.g.
+.DS
+EXINIT='set ai aw terse sh=/bin/csh'
+export EXINIT
+.DE
+or
+.DS
+setenv EXINIT 'set ai aw terse sh=/bin/csh'
+.DE
+for
+.B sh
+and
+.B csh ,
+respectively.
+These are usually placed in your .profile or .login.
+If you are running a system without environments (such as version 6)
+you can place the set command in the file .exrc in your home
+directory.
+.VL 16
+.IP autoindent\ ai 16
+Default: noai Type: toggle
+.br
+When in autoindent mode, vi helps you indent code by starting each
+line in the same column as the preceding line.
+Tabbing to the right with <tab> or <^T> will move this boundary to
+the right, and it can be moved to the left with <^D>.
+.IP autoprint\ ap 16
+Default: ap Type: toggle
+.br
+Causes the current line to be printed after each ex text modifying command.
+This is not of much interest in the normal \fBvi\fR visual mode.
+.IP autowrite\ aw 16
+Default: noaw type: toggle
+.br
+Autowrite causes an automatic write to be done if there are unsaved
+changes before certain commands which change files or otherwise
+interact with the outside world.
+These commands are :!, :tag, :next, :rewind, ^^, and ^].
+.IP beautify\ bf 16
+Default: nobf Type: toggle
+.br
+Causes all control characters except <tab>, <nl>, and <ff> to be discarded.
+.IP directory\ dir 16
+Default: dir=/tmp Type: string
+.br
+This is the directory in which \fBvi\fR puts its temporary file.
+.IP errorbells\ eb 16
+Default: noeb Type: toggle
+.br
+Error messages are preceded by a <bell>.
+.IP hardtabs\ ht 16
+Default: hardtabs=8 Type: numeric
+.br
+This option contains the value of hardware tabs in your terminal, or
+of software tabs expanded by the Unix system.
+.IP ignorecase\ ic 16
+Default: noic Type: toggle
+.br
+All upper case characters are mapped to lower case in regular expression
+matching.
+.IP lisp 16
+Default: nolisp Type: toggle
+.br
+Autoindent for \fBlisp\fR code. The commands \fB( ) [[\fR and \fB]]\fR
+are modified appropriately to affect s-expressions and functions.
+.IP list 16
+Default: nolist Type: toggle
+.br
+All printed lines have the <tab> and <nl> characters displayed visually.
+.IP magic 16
+Default: magic Type: toggle
+.br
+Enable the metacharacters for matching. These include \fB. * < > [string]
+[^string]\fR and \fB[<chr>-<chr>]\fR.
+.IP number\ nu 16
+Default: nonu Type: toggle
+.br
+Each line is displayed with its line number.
+.IP open 16
+Default: open Type: toggle
+.br
+When set, prevents entering open or visual modes from ex or edit.
+Not of interest from vi.
+.IP optimize\ opt 16
+Default: opt Type: toggle
+.br
+Basically of use only when using the \fBex\fR capabilities. This
+option prevents automatic <cr>s from taking place,
+and speeds up output of indented lines,
+at the expense of losing typeahead on some versions of UNIX.
+.IP paragraphs\ para 16
+Default: para=IPLPPPQPP\ bp Type: string
+.br
+Each pair of characters in the string indicate \fBnroff\fR macros
+which are to be treated as the beginning of a paragraph for the
+\fB{\fR and \fB}\fR commands. The default string is for the \fB-ms\fR
+and \fB-mm\fR macros.
+To indicate one letter \fBnroff\fR macros, such as \fB.P\fR or \fB.H\fR,
+quote a space in for the second character position. For example:
+.sp 1
+.ti +8
+:set paragraphs=P\e bp<nl>
+.sp 1
+would cause \fBvi\fR to consider \fB.P\fR and \fB.bp\fR as paragraph
+delimiters.
+.IP prompt 16
+Default: prompt Type: toggle
+.br
+In
+.B ex
+command mode the prompt character \fB:\fR will be printed when
+\fBex\fR is waiting for a command. This is not of interest from vi.
+.IP redraw 16
+Default: noredraw Type: toggle
+.br
+On dumb terminals, force the screen to always be up to date,
+by sending great amounts of output. Useful only at high speeds.
+.IP report 16
+Default: report=5 Type: numeric
+.br
+This sets the threshold for the number of lines modified. When
+more than this number of lines are modified, removed, or yanked,
+\fBvi\fR will report the number of lines changed at the bottom of
+the screen.
+.IP scroll 16
+Default: scroll={1/2 window} Type: numeric
+.br
+This is the number of lines that the screen scrolls up or down when
+using the <^U> and <^D> commands.
+.IP sections 16
+Default: sections=SHNHH HU Type: string
+.br
+Each two character pair of this string specify \fBnroff\fR macro names
+which are to be treated as the beginning of a section by the
+\fB]]\fR and \fB[[\fR commands. The default string is for the \fB-ms\fR
+and \fB-mm\fR macros.
+To enter one letter \fBnroff\fR macros, use a quoted space as the
+second character.
+See \fBparagraphs\fR for a fuller explanation.
+.IP shell\ sh 16
+Default: sh=from environment SHELL or /bin/sh Type: string
+.br
+This is the name of the \fBsh\fR to be used for "escaped" commands.
+.IP shiftwidth\ sw 16
+Default: sw=8 Type: numeric
+.br
+This is the number of spaces that a <^T> or <^D> will move over for
+indenting, and the amount < and > shift by.
+.IP showmatch\ sm 16
+Default: nosm Type: toggle
+.br
+When a \fB)\fR or \fB}\fR is typed, show the matching \fB(\fR or \fB{\fR
+by moving the cursor to it for one second if it is on the current screen.
+.IP slowopen\ slow 16
+Default: terminal dependent Type: toggle
+.br
+On terminals that are slow and unintelligent, this option prevents the
+updating of the screen some of the time to improve speed.
+.IP tabstop\ ts 16
+Default: ts=8 Type: numeric
+.br
+<tab>s are expanded to boundaries that are multiples of this value.
+.IP taglength\ tl 16
+Default: tl=0 Type: numeric
+.br
+If nonzero, tag names are only significant to this many characters.
+.IP term 16
+Default: (from environment \fBTERM\fP, else dumb) Type: string
+.br
+This is the terminal and controls the visual displays. It cannot be
+changed when in "visual" mode,
+you have to Q to command mode, type a
+set term command, and do ``vi.'' to get back into visual.
+Or exit vi, fix $TERM, and reenter.
+The definitions that drive a particular
+terminal type are found in the file \fB/etc/termcap\fR.
+.IP terse 16
+Default: terse Type: toggle
+.br
+When set, the error diagnostics are short.
+.IP warn 16
+Default: warn Type: toggle
+.br
+The user is warned if she/he tries to escape to
+the shell without writing out the current changes.
+.IP window 16
+Default: window={8 at 600 baud or less, 16 at 1200 baud, and screen
+size \- 1 at 2400 baud or more} Type: numeric
+.br
+This is the number of lines in the window whenever \fBvi\fR must redraw
+an entire screen. It is useful to make this size smaller if you are
+on a slow line.
+.IP w300,\ w1200,\ w9600
+.br
+These set window, but only within the corresponding speed ranges.
+They are useful in an EXINIT to fine tune window sizes.
+For example,
+.DS
+set w300=4 w1200=12
+.DE
+causes a 4 lines window at speed up to 600 baud, a 12 line window at 1200
+baud, and a full screen (the default) at over 1200 baud.
+.IP wrapscan\ ws 16
+Default: ws Type: toggle
+.br
+Searches will wrap around the end of the file when is option is set. When
+it is off, the search will terminate when it reaches the end or the
+beginning of the file.
+.IP wrapmargin\ wm 16
+Default: wm=0 Type: numeric
+.br
+\fBVi\fR will automatically insert a <nl> when it finds a natural
+break point (usually a <sp> between words) that occurs within
+"wm" spaces of the right margin.
+Therefore with "wm=0" the option is off. Setting it to 10 would
+mean that any time you are within 10 spaces of the right margin
+\fBvi\fR would be looking for a <sp> or <tab> which it could
+replace with a <nl>. This is convenient for people who forget
+to look at the screen while they type.
+(In version 3, wrapmargin behaves more like nroff, in that the
+boundary specified by the distance from the right edge of the screen
+is taken as the rightmost edge of the area where a break is allowed,
+instead of the leftmost edge.)
+.IP writeany\ wa 16
+Default: nowa Type: toggle
+.br
+\fBVi\fR normally makes a number of checks before it writes out a file.
+This prevents the user from inadvertently destroying a file. When the
+"writeany" option is enabled, \fBvi\fR no longer makes these checks.
+.LE
diff --git a/usr.bin/vi/USD.doc/vitut/vi.chars b/usr.bin/vi/USD.doc/vitut/vi.chars
new file mode 100644
index 0000000..147c4ff
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vitut/vi.chars
@@ -0,0 +1,644 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.chars 8.1 (Berkeley) 6/8/93
+.\"
+.bd S 3
+..pn 21
+.de iP
+.IP "\fB\\$1\fR" \\$2
+..
+.SH
+Appendix: character functions
+.PP
+This appendix gives the uses the editor makes of each character. The
+characters are presented in their order in the \s-2ASCII\s0 character
+set: Control characters come first, then most special characters, then
+the digits, upper and then lower case characters.
+.PP
+For each character we tell a meaning it has as a command and any meaning it
+has during an insert.
+If it has only meaning as a command, then only this is discussed.
+Section numbers in parentheses indicate where the character is discussed;
+a `f' after the section number means that the character is mentioned
+in a footnote.
+.iP "^@" 15
+Not a command character.
+If typed as the first character of an insertion it is replaced with the
+last text inserted, and the insert terminates. Only 128 characters are
+saved from the last insert; if more characters were inserted the mechanism
+is not available.
+A \fB^@\fR cannot be part of the file due to the editor implementation
+(7.5f).
+.iP "^A" 15
+Unused.
+.iP "^B" 15
+Backward window.
+A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.iP "^C" 15
+Unused.
+.iP "^D" 15
+As a command, scrolls down a half-window of text.
+A count gives the number of (logical) lines to scroll, and is remembered
+for future \fB^D\fR and \fB^U\fR commands (2.1, 7.2).
+During an insert, backtabs over \fIautoindent\fR white space at the beginning
+of a line (6.6, 7.5); this white space cannot be backspaced over.
+.iP "^E" 15
+Exposes one more line below the current screen in the file, leaving
+the cursor where it is if possible.
+(Version 3 only.)
+.iP "^F" 15
+Forward window. A count specifies repetition.
+Two lines of continuity are kept if possible (2.1, 6.1, 7.2).
+.iP "^G" 15
+Equivalent to \fB:f\fR\s-2CR\s0, printing the current file, whether
+it has been modified, the current line number and the number of lines
+in the file, and the percentage of the way through the file that you
+are.
+.iP "^H (\fR\s-2BS\s0\fP)" 15
+Same as
+.B "left arrow" .
+(See
+.B h ).
+During an insert, eliminates the last input character, backing over it
+but not erasing it; it remains so you can see what you typed if you
+wish to type something only slightly different (3.1, 7.5).
+.iP "^I\ (\fR\s-2TAB\s0\fP)" 15
+Not a command character.
+When inserted it prints as some
+number of spaces.
+When the cursor is at a tab character it rests at the last of the spaces
+which represent the tab.
+The spacing of tabstops is controlled by the \fItabstop\fR option (4.1, 6.6).
+.iP "^J\ (\fR\s-2LF\s0\fP)" 15
+Same as
+.B "down arrow"
+(see
+.B j ).
+.iP "^K" 15
+Unused.
+.iP "^L" 15
+The \s-2ASCII\s0 formfeed character, this causes the screen to be cleared
+and redrawn. This is useful after a transmission error, if characters
+typed by a program other than the editor scramble the screen,
+or after output is stopped by an interrupt (5.4, 7.2f).
+.iP "^M\ (\fR\s-2CR\s0\fP)" 15
+A carriage return advances to the next line, at the first non-white position
+in the line. Given a count, it advances that many lines (2.3).
+During an insert, a \s-2CR\s0 causes the insert to continue onto
+another line (3.1).
+.iP "^N" 15
+Same as
+.B "down arrow"
+(see
+.B j ).
+.iP "^O" 15
+Unused.
+.iP "^P" 15
+Same as
+.B "up arrow"
+(see
+.B k ).
+.iP "^Q" 15
+Not a command character.
+In input mode,
+.B ^Q
+quotes the next character, the same as
+.B ^V ,
+except that some teletype drivers will eat the
+.B ^Q
+so that the editor never sees it.
+.iP "^R" 15
+Redraws the current screen, eliminating logical lines not corresponding
+to physical lines (lines with only a single @ character on them).
+On hardcopy terminals in \fIopen\fR mode, retypes the current line
+(5.4, 7.2, 7.8).
+.iP "^S" 15
+Unused. Some teletype drivers use
+.B ^S
+to suspend output until
+.B ^Q is pressed.
+.iP "^T" 15
+Not a command character.
+During an insert, with \fIautoindent\fR set and at the beginning of the
+line, inserts \fIshiftwidth\fR white space.
+.iP "^U" 15
+Scrolls the screen up, inverting \fB^D\fR which scrolls down. Counts work as
+they do for \fB^D\fR, and the previous scroll amount is common to both.
+On a dumb terminal, \fB^U\fR will often necessitate clearing and redrawing
+the screen further back in the file (2.1, 7.2).
+.iP "^V" 15
+Not a command character.
+In input mode, quotes the next character so that it is possible
+to insert non-printing and special characters into the file (4.2, 7.5).
+.iP "^W" 15
+Not a command character.
+During an insert, backs up as \fBb\fR would in command mode; the deleted
+characters remain on the display (see \fB^H\fR) (7.5).
+.iP "^X" 15
+Unused.
+.iP "^Y" 15
+Exposes one more line above the current screen, leaving the cursor where
+it is if possible. (No mnemonic value for this key; however, it is next
+to \fB^U\fR which scrolls up a bunch.)
+(Version 3 only.)
+.iP "^Z" 15
+If supported by the Unix system,
+stops the editor, exiting to the top level shell.
+Same as \fB:stop\fP\s-2CR\s0.
+Otherwise, unused.
+.iP "^[\ (\fR\s-2ESC\s0\fP)" 15
+Cancels a partially formed command, such as a \fBz\fR when no following
+character has yet been given; terminates inputs on the last line (read
+by commands such as \fB: /\fR and \fB?\fR); ends insertions of new text
+into the buffer.
+If an \s-2ESC\s0 is given when quiescent in command state, the editor
+rings the bell or flashes the screen. You can thus hit \s-2ESC\s0 if
+you don't know what is happening till the editor rings the bell.
+If you don't know if you are in insert mode you can type \s-2ESC\s0\fBa\fR,
+and then material to be input; the material will be inserted correctly
+whether or not you were in insert mode when you started (1.5, 3.1, 7.5).
+.iP "^\e" 15
+Unused.
+.iP "^]" 15
+Searches for the word which is after the cursor as a tag. Equivalent
+to typing \fB:ta\fR, this word, and then a \s-2CR\s0.
+Mnemonically, this command is ``go right to'' (7.3).
+.iP "^\(ua" 15
+Equivalent to \fB:e #\fR\s-2CR\s0, returning to the previous position
+in the last edited file, or editing a file which you specified if you
+got a `No write since last change diagnostic' and do not want to have
+to type the file name again (7.3).
+(You have to do a \fB:w\fR before \fB^\(ua\fR
+will work in this case. If you do not wish to write the file you should
+do \fB:e!\ #\fR\s-2CR\s0 instead.)
+.iP "^_" 15
+Unused.
+Reserved as the command character for the
+Tektronix 4025 and 4027 terminal.
+.iP "\fR\s-2SPACE\s0\fP" 15
+Same as
+.B "right arrow"
+(see
+.B l ).
+.iP "!" 15
+An operator, which processes lines from the buffer with reformatting commands.
+Follow \fB!\fR with the object to be processed, and then the command name
+terminated by \s-2CR\s0. Doubling \fB!\fR and preceding it by a count
+causes count lines to be filtered; otherwise the count
+is passed on to the object after the \fB!\fR. Thus \fB2!}\fR\fIfmt\fR\s-2CR\s0
+reformats the next two paragraphs by running them through the program
+\fIfmt\fR. If you are working on \s-2LISP\s0,
+the command \fB!%\fR\fIgrind\fR\s-2CR\s0,*
+.FS
+*Both
+.I fmt
+and
+.I grind
+are Berkeley programs and may not be present at all installations.
+.FE
+given at the beginning of a
+function, will run the text of the function through the \s-2LISP\s0 grinder
+(6.7, 7.3).
+To read a file or the output of a command into the buffer use \fB:r\fR (7.3).
+To simply execute a command use \fB:!\fR (7.3).
+.tr "
+.iP  15
+Precedes a named buffer specification. There are named buffers \fB1\-9\fR
+used for saving deleted text and named buffers \fBa\-z\fR into which you can
+place text (4.3, 6.3)
+.tr 
+.iP "#" 15
+The macro character which, when followed by a number, will substitute
+for a function key on terminals without function keys (6.9).
+In input mode,
+if this is your erase character, it will delete the last character
+you typed in input mode, and must be preceded with a \fB\e\fR to insert
+it, since it normally backs over the last input character you gave.
+.iP "$" 15
+Moves to the end of the current line. If you \fB:se list\fR\s-2CR\s0,
+then the end of each line will be shown by printing a \fB$\fR after the
+end of the displayed text in the line. Given a count, advances to the
+count'th following end of line; thus \fB2$\fR advances to the end of the
+following line.
+.iP "%" 15
+Moves to the parenthesis or brace \fB{ }\fR which balances the parenthesis
+or brace at the current cursor position.
+.iP "&" 15
+A synonym for \fB:&\fR\s-2CR\s0, by analogy with the
+.I ex
+.B &
+command.
+.iP "\(aa" 15
+When followed by a \fB\(aa\fR returns to the previous context at the
+beginning of a line. The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the line which
+was marked with this letter with a \fBm\fR command, at the first non-white
+character in the line. (2.2, 5.3).
+When used with an operator such as \fBd\fR, the operation takes place
+over complete lines; if you use \fB\(ga\fR, the operation takes place
+from the exact marked place to the current cursor position within the
+line.
+.iP "(" 15
+Retreats to the beginning of a
+sentence, or to the beginning of a \s-2LISP\s0 s-expression
+if the \fIlisp\fR option is set.
+A sentence ends at a \fB. !\fR or \fB?\fR which is followed by either
+the end of a line or by two spaces. Any number of closing \fB) ] "\fR
+and \fB\(aa\fR characters may appear after the \fB. !\fR or \fB?\fR,
+and before the spaces or end of line. Sentences also begin
+at paragraph and section boundaries
+(see \fB{\fR and \fB[[\fR below).
+A count advances that many sentences (4.2, 6.8).
+.iP ")" 15
+Advances to the beginning of a sentence.
+A count repeats the effect.
+See \fB(\fR above for the definition of a sentence (4.2, 6.8).
+.iP "*" 15
+Unused.
+.iP "+" 15
+Same as \s-2CR\s0 when used as a command.
+.iP "," 15
+Reverse of the last \fBf F t\fR or \fBT\fR command, looking the other way
+in the current line. Especially useful after hitting too many \fB;\fR
+characters. A count repeats the search.
+.iP "\-" 15
+Retreats to the previous line at the first non-white character.
+This is the inverse of \fB+\fR and \s-2RETURN\s0.
+If the line moved to is not on the screen, the screen is scrolled, or
+cleared and redrawn if this is not possible.
+If a large amount of scrolling would be required the screen is also cleared
+and redrawn, with the current line at the center (2.3).
+.iP "\&." 15
+Repeats the last command which changed the buffer. Especially useful
+when deleting words or lines; you can delete some words/lines and then
+hit \fB.\fR to delete more and more words/lines.
+Given a count, it passes it on to the command being repeated. Thus after
+a \fB2dw\fR, \fB3.\fR deletes three words (3.3, 6.3, 7.2, 7.4).
+.iP "/" 15
+Reads a string from the last line on the screen, and scans forward for
+the next occurrence of this string. The normal input editing sequences may
+be used during the input on the bottom line; an returns to command state
+without ever searching.
+The search begins when you hit \s-2CR\s0 to terminate the pattern;
+the cursor moves to the beginning of the last line to indicate that the search
+is in progress; the search may then
+be terminated with a \s-2DEL\s0 or \s-2RUB\s0, or by backspacing when
+at the beginning of the bottom line, returning the cursor to
+its initial position.
+Searches normally wrap end-around to find a string
+anywhere in the buffer.
+.IP
+When used with an operator the enclosed region is normally affected.
+By mentioning an
+offset from the line matched by the pattern you can force whole lines
+to be affected. To do this give a pattern with a closing
+a closing \fB/\fR and then an offset \fB+\fR\fIn\fR or \fB\-\fR\fIn\fR.
+.IP
+To include the character \fB/\fR in the search string, you must escape
+it with a preceding \fB\e\fR.
+A \fB\(ua\fR at the beginning of the pattern forces the match to occur
+at the beginning of a line only; this speeds the search. A \fB$\fR at
+the end of the pattern forces the match to occur at the end of a line
+only.
+More extended pattern matching is available, see section 7.4;
+unless you set \fBnomagic\fR in your \fI\&.exrc\fR file you will have
+to preceed the characters \fB. [ *\fR and \fB~\fR in the search pattern
+with a \fB\e\fR to get them to work as you would naively expect (1.5, 2,2,
+6.1, 7.2, 7.4).
+.iP "0" 15
+Moves to the first character on the current line.
+Also used, in forming numbers, after an initial \fB1\fR\-\fB9\fR.
+.iP "1\-9" 15
+Used to form numeric arguments to commands (2.3, 7.2).
+.iP ":" 15
+A prefix to a set of commands for file and option manipulation and escapes
+to the system. Input is given on the bottom line and terminated with
+an \s-2CR\s0, and the command then executed. You can return to where
+you were by hitting \s-2DEL\s0 or \s-2RUB\s0 if you hit \fB:\fR accidentally
+(see primarily 6.2 and 7.3).
+.iP ";" 15
+Repeats the last single character find which used \fBf F t\fR or \fBT\fR.
+A count iterates the basic scan (4.1).
+.iP "<" 15
+An operator which shifts lines left one \fIshiftwidth\fR, normally 8
+spaces. Like all operators, affects lines when repeated, as in
+\fB<<\fR. Counts are passed through to the basic object, thus \fB3<<\fR
+shifts three lines (6.6, 7.2).
+.iP "=" 15
+Reindents line for \s-2LISP\s0, as though they were typed in with \fIlisp\fR
+and \fIautoindent\fR set (6.8).
+.iP ">" 15
+An operator which shifts lines right one \fIshiftwidth\fR, normally 8
+spaces. Affects lines when repeated as in \fB>>\fR. Counts repeat the
+basic object (6.6, 7.2).
+.iP "?" 15
+Scans backwards, the opposite of \fB/\fR. See the \fB/\fR description
+above for details on scanning (2.2, 6.1, 7.4).
+.iP "@" 15
+A macro character (6.9). If this is your kill character, you must escape it with a \e
+to type it in during input mode, as it normally backs over the input you
+have given on the current line (3.1, 3.4, 7.5).
+.iP "A" 15
+Appends at the end of line, a synonym for \fB$a\fR (7.2).
+.iP "B" 15
+Backs up a word, where words are composed of non-blank sequences, placing
+the cursor at the beginning of the word. A count repeats the effect
+(2.4).
+.iP "C" 15
+Changes the rest of the text on the current line; a synonym for \fBc$\fR.
+.iP "D" 15
+Deletes the rest of the text on the current line; a synonym for \fBd$\fR.
+.iP "E" 15
+Moves forward to the end of a word, defined as blanks and non-blanks,
+like \fBB\fR and \fBW\fR. A count repeats the effect.
+.iP "F" 15
+Finds a single following character, backwards in the current line.
+A count repeats this search that many times (4.1).
+.iP "G" 15
+Goes to the line number given as preceding argument, or the end of the
+file if no preceding count is given. The screen is redrawn with the
+new current line in the center if necessary (7.2).
+.iP "H" 15
+.B "Home arrow" .
+Homes the cursor to the top line on the screen. If a count is given,
+then the cursor is moved to the count'th line on the screen.
+In any case the cursor is moved to the first non-white character on the
+line. If used as the target of an operator, full lines are affected
+(2.3, 3.2).
+.iP "I" 15
+Inserts at the beginning of a line; a synonym for \fB\(uai\fR.
+.iP "J" 15
+Joins together lines, supplying appropriate white space: one space between
+words, two spaces after a \fB.\fR, and no spaces at all if the first
+character of the joined on line is \fB)\fR. A count causes that many
+lines to be joined rather than the default two (6.5, 7.1f).
+.iP "K" 15
+Unused.
+.iP "L" 15
+Moves the cursor to the first non-white character of the last line on
+the screen. With a count, to the first non-white of the count'th line
+from the bottom. Operators affect whole lines when used with \fBL\fR
+(2.3).
+.iP "M" 15
+Moves the cursor to the middle line on the screen, at the first non-white
+position on the line (2.3).
+.iP "N" 15
+Scans for the next match of the last pattern given to
+\fB/\fR or \fB?\fR, but in the reverse direction; this is the reverse
+of \fBn\fR.
+.iP "O" 15
+Opens a new line above the current line and inputs text there up to an
+\s-2ESC\s0. A count can be used on dumb terminals to specify a number
+of lines to be opened; this is generally obsolete, as the \fIslowopen\fR
+option works better (3.1).
+.iP "P" 15
+Puts the last deleted text back before/above the cursor. The text goes
+back as whole lines above the cursor if it was deleted as whole lines.
+Otherwise the text is inserted between the characters before and at the
+cursor. May be preceded by a named buffer specification \fB"\fR\fIx\fR
+to retrieve the contents of the buffer; buffers \fB1\fR\-\fB9\fR contain
+deleted material, buffers \fBa\fR\-\fBz\fR are available for general
+use (6.3).
+.iP "Q" 15
+Quits from \fIvi\fR to \fIex\fR command mode. In this mode, whole lines
+form commands, ending with a \s-2RETURN\s0. You can give all the \fB:\fR
+commands; the editor supplies the \fB:\fR as a prompt (7.7).
+.iP "R" 15
+Replaces characters on the screen with characters you type (overlay fashion).
+Terminates with an \s-2ESC\s0.
+.iP "S" 15
+Changes whole lines, a synonym for \fBcc\fR. A count substitutes for
+that many lines. The lines are saved in the numeric buffers, and erased
+on the screen before the substitution begins.
+.iP "T" 15
+Takes a single following character, locates the character before the
+cursor in the current line, and places the cursor just after that character.
+A count repeats the effect. Most useful with operators such as \fBd\fR
+(4.1).
+.iP "U" 15
+Restores the current line to its state before you started changing it
+(3.5).
+.iP "V" 15
+Unused.
+.iP "W" 15
+Moves forward to the beginning of a word in the current line,
+where words are defined as sequences of blank/non-blank characters.
+A count repeats the effect (2.4).
+.iP "X" 15
+Deletes the character before the cursor. A count repeats the effect,
+but only characters on the current line are deleted.
+.iP "Y" 15
+Yanks a copy of the current line into the unnamed buffer, to be put back
+by a later \fBp\fR or \fBP\fR; a very useful synonym for \fByy\fR.
+A count yanks that many lines. May be preceded by a buffer name to put
+lines in that buffer (7.4).
+.iP "ZZ" 15
+Exits the editor.
+(Same as \fB:x\fP\s-2CR\s0.)
+If any changes have been made, the buffer is written out to the current file.
+Then the editor quits.
+.iP "[[" 15
+Backs up to the previous section boundary. A section begins at each
+macro in the \fIsections\fR option,
+normally a `.NH' or `.SH' and also at lines which which start
+with a formfeed \fB^L\fR. Lines beginning with \fB{\fR also stop \fB[[\fR;
+this makes it useful for looking backwards, a function at a time, in C
+programs. If the option \fIlisp\fR is set, stops at each \fB(\fR at the
+beginning of a line, and is thus useful for moving backwards at the top
+level \s-2LISP\s0 objects. (4.2, 6.1, 6.6, 7.2).
+.iP "\e" 15
+Unused.
+.iP "]]" 15
+Forward to a section boundary, see \fB[[\fR for a definition (4.2, 6.1,
+6.6, 7.2).
+.iP "\(ua" 15
+Moves to the first non-white position on the current line (4.4).
+.iP "_" 15
+Unused.
+.iP "\(ga" 15
+When followed by a \fB\(ga\fR returns to the previous context.
+The previous context is set whenever the current
+line is moved in a non-relative way.
+When followed by a letter \fBa\fR\-\fBz\fR, returns to the position which
+was marked with this letter with a \fBm\fR command.
+When used with an operator such as \fBd\fR, the operation takes place
+from the exact marked place to the current position within the line;
+if you use \fB\(aa\fR, the operation takes place over complete lines
+(2.2, 5.3).
+.iP "a" 15
+Appends arbitrary text after the current cursor position; the insert
+can continue onto multiple lines by using \s-2RETURN\s0 within the insert.
+A count causes the inserted text to be replicated, but only if the inserted
+text is all on one line.
+The insertion terminates with an \s-2ESC\s0 (3.1, 7.2).
+.iP "b" 15
+Backs up to the beginning of a word in the current line. A word is a
+sequence of alphanumerics, or a sequence of special characters.
+A count repeats the effect (2.4).
+.iP "c" 15
+An operator which changes the following object, replacing it with the
+following input text up to an \s-2ESC\s0. If more than part of a single
+line is affected, the text which is changed away is saved in the numeric named
+buffers. If only part of the current line is affected, then the last
+character to be changed away is marked with a \fB$\fR.
+A count causes that many objects to be affected, thus both
+\fB3c)\fR and \fBc3)\fR change the following three sentences (7.4).
+.iP "d" 15
+An operator which deletes the following object. If more than part of
+a line is affected, the text is saved in the numeric buffers.
+A count causes that many objects to be affected; thus \fB3dw\fR is the
+same as \fBd3w\fR (3.3, 3.4, 4.1, 7.4).
+.iP "e" 15
+Advances to the end of the next word, defined as for \fBb\fR and \fBw\fR.
+A count repeats the effect (2.4, 3.1).
+.iP "f" 15
+Finds the first instance of the next character following the cursor on
+the current line. A count repeats the find (4.1).
+.iP "g" 15
+Unused.
+.sp
+Arrow keys
+.B h ,
+.B j ,
+.B k ,
+.B l ,
+and
+.B H .
+.iP "h" 15
+.B "Left arrow" .
+Moves the cursor one character to the left.
+Like the other arrow keys, either
+.B h ,
+the
+.B "left arrow"
+key, or one of the synonyms (\fB^H\fP) has the same effect.
+On v2 editors, arrow keys on certain kinds of terminals
+(those which send escape sequences, such as vt52, c100, or hp)
+cannot be used.
+A count repeats the effect (3.1, 7.5).
+.iP "i" 15
+Inserts text before the cursor, otherwise like \fBa\fR (7.2).
+.iP "j" 15
+.B "Down arrow" .
+Moves the cursor one line down in the same column.
+If the position does not exist,
+.I vi
+comes as close as possible to the same column.
+Synonyms include
+.B ^J
+(linefeed) and
+.B ^N .
+.iP "k" 15
+.B "Up arrow" .
+Moves the cursor one line up.
+.B ^P
+is a synonym.
+.iP "l" 15
+.B "Right arrow" .
+Moves the cursor one character to the right.
+\s-2SPACE\s0 is a synonym.
+.iP "m" 15
+Marks the current position of the cursor in the mark register which is
+specified by the next character \fBa\fR\-\fBz\fR. Return to this position
+or use with an operator using \fB\(ga\fR or \fB\(aa\fR (5.3).
+.iP "n" 15
+Repeats the last \fB/\fR or \fB?\fR scanning commands (2.2).
+.iP "o" 15
+Opens new lines below the current line; otherwise like \fBO\fR (3.1).
+.iP "p" 15
+Puts text after/below the cursor; otherwise like \fBP\fR (6.3).
+.iP "q" 15
+Unused.
+.iP "r" 15
+Replaces the single character at the cursor with a single character you
+type. The new character may be a \s-2RETURN\s0; this is the easiest
+way to split lines. A count replaces each of the following count characters
+with the single character given; see \fBR\fR above which is the more
+usually useful iteration of \fBr\fR (3.2).
+.iP "s" 15
+Changes the single character under the cursor to the text which follows
+up to an \s-2ESC\s0; given a count, that many characters from the current
+line are changed. The last character to be changed is marked with \fB$\fR
+as in \fBc\fR (3.2).
+.iP "t" 15
+Advances the cursor upto the character before the next character typed.
+Most useful with operators such as \fBd\fR and \fBc\fR to delete the
+characters up to a following character. You can use \fB.\fR to delete
+more if this doesn't delete enough the first time (4.1).
+.iP "u" 15
+Undoes the last change made to the current buffer. If repeated, will
+alternate between these two states, thus is its own inverse. When used
+after an insert which inserted text on more than one line, the lines are
+saved in the numeric named buffers (3.5).
+.iP "v" 15
+Unused.
+.iP "w" 15
+Advances to the beginning of the next word, as defined by \fBb\fR (2.4).
+.iP "x" 15
+Deletes the single character under the cursor. With a count deletes
+deletes that many characters forward from the cursor position, but only
+on the current line (6.5).
+.iP "y" 15
+An operator, yanks the following object into the unnamed temporary buffer.
+If preceded by a named buffer specification, \fB"\fR\fIx\fR, the text
+is placed in that buffer also. Text can be recovered by a later \fBp\fR
+or \fBP\fR (7.4).
+.iP "z" 15
+Redraws the screen with the current line placed as specified by the following
+character: \s-2RETURN\s0 specifies the top of the screen, \fB.\fR the
+center of the screen, and \fB\-\fR at the bottom of the screen.
+A count may be given after the \fBz\fR and before the following character
+to specify the new screen size for the redraw.
+A count before the \fBz\fR gives the number of the line to place in the
+center of the screen instead of the default current line. (5.4)
+.iP "{" 15
+Retreats to the beginning of the beginning of the preceding paragraph.
+A paragraph begins at each macro in the \fIparagraphs\fR option, normally
+`.IP', `.LP', `.PP', `.QP' and `.bp'.
+A paragraph also begins after a completely
+empty line, and at each section boundary (see \fB[[\fR above) (4.2, 6.8,
+7.6).
+.iP "|" 15
+Places the cursor on the character in the column specified
+by the count (7.1, 7.2).
+.iP "}" 15
+Advances to the beginning of the next paragraph. See \fB{\fR for the
+definition of paragraph (4.2, 6.8, 7.6).
+.iP "~" 15
+Unused.
+.iP "^?\ (\s-2\fRDEL\fP\s0)" 15
+Interrupts the editor, returning it to command accepting state (1.5,
+7.5)
+.bp
+\&.
diff --git a/usr.bin/vi/USD.doc/vitut/vi.in b/usr.bin/vi/USD.doc/vitut/vi.in
new file mode 100644
index 0000000..3bdfeb9
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vitut/vi.in
@@ -0,0 +1,2064 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.in 8.1 (Berkeley) 6/8/93
+.\"
+.EH 'USD:12-%''An Introduction to Display Editing with Vi'
+.OH 'An Introduction to Display Editing with Vi''USD:12-%'
+.bd S 3
+.if t .ds dg \(dg
+.if n .ds dg +
+.if t .ds dd \(dd
+.if n .ds dd ++
+.\".RP
+.TL
+An Introduction to Display Editing with Vi
+.AU
+William Joy
+.AU
+Mark Horton
+.AI
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, Ca. 94720
+.AB
+.PP
+.I Vi
+(visual) is a display oriented interactive text editor.
+When using
+.I vi
+the screen of your terminal acts as a window into the file which you
+are editing. Changes which you make to the file are reflected
+in what you see.
+.PP
+Using
+.I vi
+you can insert new text any place in the file quite easily.
+Most of the commands to
+.I vi
+move the cursor around in the file.
+There are commands to move the cursor
+forward and backward in units of characters, words,
+sentences and paragraphs.
+A small set of operators, like
+.B d
+for delete and
+.B c
+for change, are combined with the motion commands to form operations
+such as delete word or change paragraph, in a simple and natural way.
+This regularity and the mnemonic assignment of commands to keys makes the
+editor command set easy to remember and to use.
+.PP
+.I Vi
+will work on a large number of display terminals,
+and new terminals are easily driven after editing a terminal description file.
+While it is advantageous to have an intelligent terminal which can locally
+insert and delete lines and characters from the display, the editor will
+function quite well on dumb terminals over slow phone lines.
+The editor makes allowance for the low bandwidth in these situations
+and uses smaller window sizes and
+different display updating algorithms to make best use of the
+limited speed available.
+.PP
+It is also possible to use the command set of
+.I vi
+on hardcopy terminals, storage tubes and ``glass tty's'' using a one line
+editing window; thus
+.I vi's
+command set is available on all terminals.
+The full command set of the more traditional, line
+oriented editor
+.I ex
+is available within
+.I vi;
+it is quite simple to switch between the two modes of editing.
+.AE
+.NH 1
+Getting started
+.PP
+.FS
+The financial support of an \s-2IBM\s0 Graduate Fellowship and the
+National Science Foundation under grants MCS74-07644-A03 and MCS78-07291
+is gratefully acknowledged.
+.FE
+This document provides a quick introduction to
+.I vi.
+(Pronounced \fIvee-eye\fP.)
+You should be running
+.I vi
+on a file you are familiar with while you are reading this.
+The first part of this document (sections 1 through 5)
+describes the basics of using
+.I vi.
+Some topics of special interest are presented in section 6, and
+some nitty-gritty details of how the editor functions are saved for section
+7 to avoid cluttering the presentation here.
+.PP
+There is also a short appendix here, which gives for each character the
+special meanings which this character has in \fIvi\fR. Attached to
+this document should be a quick reference card.
+This card summarizes the commands of
+.I vi
+in a very compact format. You should have the card handy while you are
+learning
+.I vi.
+.NH 2
+Specifying terminal type
+.PP
+Before you can start
+.I vi
+you must tell the system what kind of terminal you are using.
+Here is a (necessarily incomplete) list of terminal type codes.
+If your terminal does not appear here, you should consult with one of
+the staff members on your system to find out the code for your terminal.
+If your terminal does not have a code, one can be assigned and a description
+for the terminal can be created.
+.LP
+.TS
+center;
+ab ab ab
+a a a.
+Code Full name Type
+_
+2621 Hewlett-Packard 2621A/P Intelligent
+2645 Hewlett-Packard 264x Intelligent
+act4 Microterm ACT-IV Dumb
+act5 Microterm ACT-V Dumb
+adm3a Lear Siegler ADM-3a Dumb
+adm31 Lear Siegler ADM-31 Intelligent
+c100 Human Design Concept 100 Intelligent
+dm1520 Datamedia 1520 Dumb
+dm2500 Datamedia 2500 Intelligent
+dm3025 Datamedia 3025 Intelligent
+fox Perkin-Elmer Fox Dumb
+h1500 Hazeltine 1500 Intelligent
+h19 Heathkit h19 Intelligent
+i100 Infoton 100 Intelligent
+mime Imitating a smart act4 Intelligent
+t1061 Teleray 1061 Intelligent
+vt52 Dec VT-52 Dumb
+.TE
+.PP
+Suppose for example that you have a Hewlett-Packard HP2621A
+terminal. The code used by the system for this terminal is `2621'.
+In this case you can use one of the following commands to tell the system
+the type of your terminal:
+.DS
+% \fBsetenv TERM\fP 2621
+.DE
+This command works with the
+.I csh
+shell.
+If you are using the standard Bourne shell
+.I sh
+then you should give the commands
+.DS
+$ \fBTERM=\fP2621
+$ \fBexport TERM\fP
+.DE
+.PP
+If you want to arrange to have your terminal type set up automatically
+when you log in, you can use the
+.I tset
+program.
+If you dial in on a
+.I mime ,
+but often use hardwired ports, a typical line for your
+.I .login
+file (if you use csh) would be
+.DS
+\fBsetenv TERM \(gatset\fP \- \-d mime\(ga
+.DE
+or for your
+.I .profile
+file (if you use sh)
+.DS
+\fBTERM=\(gatse\fPt \- \-d mime\(ga
+.DE
+.I Tset
+knows which terminals are hardwired to each port
+and needs only to be told that when you dial in you
+are probably on a
+.I mime .
+.I Tset
+is usually used to change the erase and kill characters, too.
+.NH 2
+Editing a file
+.PP
+After telling the system which kind of terminal you have, you should
+make a copy of a file you are familiar with, and run
+.I vi
+on this file, giving the command
+.DS
+% \fBvi\fR \fIname\fR
+.DE
+replacing \fIname\fR with the name of the copy file you just created.
+The screen should clear and the text of your file should appear on the
+screen. If something else happens refer to the footnote.\*(dd
+.FS
+\*(dd If you gave the system an incorrect terminal type code then the
+editor may have just made a mess out of your screen. This happens when
+it sends control codes for one kind of terminal to some other
+kind of terminal. In this case hit
+the keys \fB:q\fR (colon and the q key) and then hit the \s-2RETURN\s0 key.
+This should get you back to the command level interpreter.
+Figure out what you did wrong (ask someone else if necessary) and try again.
+ Another thing which can go wrong is that you typed the wrong file name and
+the editor just printed an error diagnostic. In this case you should
+follow the above procedure for getting out of the editor, and try again
+this time spelling the file name correctly.
+ If the editor doesn't seem to respond to the commands which you type
+here, try sending an interrupt to it by hitting the \s-2DEL\s0 or \s-2RUB\s0
+key on your terminal, and then hitting the \fB:q\fR command again followed
+by a carriage return.
+.sp
+.FE
+.NH 2
+The editor's copy: the buffer
+.PP
+The editor does not directly modify the file which you are editing.
+Rather, the editor makes a copy of this file, in a place called the
+.I buffer,
+and remembers the file's
+name. You do not affect the contents of the file unless and until you
+write the changes you make back into the original file.
+.NH 2
+Notational conventions
+.PP
+In our examples, input which must be typed as is will be presented in
+\fBbold face\fR. Text which should be replaced with appropriate input
+will be given in \fIitalics\fR. We will represent special characters
+in \s-2SMALL CAPITALS\s0.
+.NH 2
+Arrow keys
+.PP
+The editor command set is independent of the terminal
+you are using. On most terminals with cursor positioning keys, these keys
+will also work within the editor.
+If you don't have cursor positioning keys, or even if you do, you can use
+the \fBh j k\fR and \fBl\fR keys as cursor positioning
+keys (these are labelled with arrows on an
+.I adm3a).*
+.PP
+(Particular note for the HP2621: on this terminal the function keys
+must be \fIshifted\fR (ick) to send to the machine, otherwise they
+only act locally. Unshifted use will leave the cursor positioned
+incorrectly.)
+.FS
+* As we will see later,
+.I h
+moves back to the left (like control-h which is a backspace),
+.I j
+moves down (in the same column),
+.I k
+moves up (in the same column),
+and
+.I l
+moves to the right.
+.FE
+.NH 2
+Special characters: \s-2ESC\s0, \s-2CR\s0 and \s-2DEL\s0
+.PP
+Several of these special characters are very important, so be sure to
+find them right now. Look on your keyboard for a key labelled \s-2ESC\s0
+or \s-2ALT\s0. It should be near the upper left corner of your terminal.
+Try hitting this key a few times. The editor will ring the bell
+to indicate that it is in a quiescent state.\*(dd
+.FS
+\*(dd On smart terminals where it is possible, the editor will quietly
+flash the screen rather than ringing the bell.
+.FE
+Partially formed commands are cancelled by \s-2ESC\s0, and when you insert
+text in the file you end the text insertion
+with \s-2ESC\s0. This key is a fairly
+harmless one to hit, so you can just hit it if you don't know
+what is going on until the editor rings the bell.
+.PP
+The \s-2CR\s0 or \s-2RETURN\s0 key is important because it is used
+to terminate certain commands.
+It is usually at the right side of the keyboard,
+and is the same command used at the end of each shell command.
+.PP
+Another very useful key is the \s-2DEL\s0 or \s-2RUB\s0 key, which generates
+an interrupt, telling the editor to stop what it is doing.
+It is a forceful way of making the editor listen
+to you, or to return it to the quiescent state if you don't know or don't
+like what is going on. Try hitting the `/' key on your terminal. This
+key is used when you want to specify a string to be searched for. The
+cursor should now be positioned at the bottom line of the terminal after
+a `/' printed as a prompt. You can get the cursor back to the current
+position by hitting the \s-2DEL\s0 or \s-2RUB\s0 key; try this now.*
+.FS
+* Backspacing over the `/' will also cancel the search.
+.FE
+From now on we will simply refer to hitting the \s-2DEL\s0 or \s-2RUB\s0
+key as ``sending an interrupt.''**
+.FS
+** On some systems, this interruptibility comes at a price: you cannot type
+ahead when the editor is computing with the cursor on the bottom line.
+.FE
+.PP
+The editor often echoes your commands on the last line of the terminal.
+If the cursor is on the first position of this last line, then the editor
+is performing a computation, such as computing a new position in the
+file after a search or running a command to reformat part of the buffer.
+When this is happening you can stop the editor by
+sending an interrupt.
+.NH 2
+Getting out of the editor
+.PP
+After you have worked with this introduction for a while, and you wish
+to do something else, you can give the command \fBZZ\fP
+to the editor.
+This will write the contents of the editor's buffer back into
+the file you are editing, if you made any changes, and then quit from
+the editor. You can also end an editor
+session by giving the command \fB:q!\fR\s-2CR\s0;\*(dg
+.FS
+\*(dg All commands which read from the last display line can also be
+terminated with a \s-2ESC\s0 as well as an \s-2CR\s0.
+.FE
+this is a dangerous but occasionally essential
+command which ends the editor session and discards all your changes.
+You need to know about this command in case you change the editor's
+copy of a file you wish only to look at. Be very careful
+not to give this command when you really want to save
+the changes you have made.
+.NH 1
+Moving around in the file
+.NH 2
+Scrolling and paging
+.PP
+The editor has a number of commands for moving around in the file.
+The most useful of these is generated by hitting the control and D keys
+at the same time, a control-D or `^D'. We will use this two character
+notation for referring to these control keys from now on. You may have
+a key labelled `^' on your terminal. This key will be represented as `\(ua'
+in this document; `^' is exclusively used as part of the `^x' notation
+for control characters.\*(dd
+.FS
+\*(dd If you don't have a `^' key on your terminal
+then there is probably a key labelled `\(ua'; in any case these characters
+are one and the same.
+.FE
+.PP
+As you know now if you tried hitting \fB^D\fR, this command scrolls down in
+the file. The \fBD\fR thus stands for down. Many editor commands are mnemonic
+and this makes them much easier to remember. For instance the command
+to scroll up is \fB^U\fR. Many dumb terminals can't scroll up at all, in which
+case hitting \fB^U\fR clears the screen and refreshes it
+with a line which is farther back in the file at the top.
+.PP
+If you want to see more of the file below where you are, you can
+hit \fB^E\fR to expose one more line at the bottom of the screen,
+leaving the cursor where it is.
+The command \fB^Y\fR (which is hopelessly non-mnemonic, but next to \fB^U\fR
+on the keyboard) exposes one more line at the top of the screen.
+.PP
+There are other ways to move around in the file; the keys \fB^F\fR and \fB^B\fR
+move forward and backward a page,
+keeping a couple of lines of continuity between screens
+so that it is possible to read through a file using these rather than
+\fB^D\fR and \fB^U\fR if you wish.
+.PP
+Notice the difference between scrolling and paging. If you are trying
+to read the text in a file, hitting \fB^F\fR to move forward a page
+will leave you only a little context to look back at. Scrolling on the
+other hand leaves more context, and happens more smoothly. You can continue
+to read the text as scrolling is taking place.
+.NH 2
+Searching, goto, and previous context
+.PP
+Another way to position yourself in the file is by giving the editor a string
+to search for. Type the character \fB/\fR followed by a string of characters
+terminated by \s-2CR\s0. The editor will position the cursor
+at the next occurrence of this string.
+Try hitting \fBn\fR to then go to the next occurrence of this string.
+The character \fB?\fR will search backwards from where you are, and is
+otherwise like \fB/\fR.\*(dg
+.FS
+\*(dg These searches will normally wrap around the end of the file, and thus
+find the string even if it is not on a line in the direction you search
+provided it is anywhere else in the file. You can disable this wraparound
+in scans by giving the command \fB:se nowrapscan\fR\s-2CR\s0,
+or more briefly \fB:se nows\fR\s-2CR\s0.
+.FE
+.PP
+If the search string you give the editor is not present in the
+file the editor will print
+a diagnostic on the last line of the screen, and the cursor will be returned
+to its initial position.
+.PP
+If you wish the search to match only at the beginning of a line, begin
+the search string with an \fB\(ua\fR. To match only at the end of
+a line, end the search string with a \fB$\fR.
+Thus \fB/\(uasearch\fR\s-2CR\s0 will search for the word `search' at
+the beginning of a line, and \fB/last$\fR\s-2CR\s0 searches for the
+word `last' at the end of a line.*
+.FS
+*Actually, the string you give to search for here can be a
+.I "regular expression"
+in the sense of the editors
+.I ex (1)
+and
+.I ed (1).
+If you don't wish to learn about this yet, you can disable this more
+general facility by doing
+\fB:se\ nomagic\fR\s-2CR\s0;
+by putting this command in
+EXINIT
+in your environment, you can have this always be in effect (more
+about
+.I EXINIT
+later.)
+.FE
+.PP
+The command \fBG\fR, when preceded by a number will position the cursor
+at that line in the file.
+Thus \fB1G\fR will move the cursor to
+the first line of the file. If you give \fBG\fR no count, then it moves
+to the end of the file.
+.PP
+If you are near the end of the file, and the last line is not at the bottom
+of the screen, the editor will place only the character `~' on each remaining
+line. This indicates that the last line in the file is on the screen;
+that is, the `~' lines are past the end of the file.
+.PP
+You can find out the state of the file you are editing by typing a \fB^G\fR.
+The editor will show you the name of the file you are editing, the number
+of the current line, the number of lines in the buffer, and the percentage
+of the way through the buffer which you are.
+Try doing this now, and remember the number of the line you are on.
+Give a \fBG\fR command to get to the end and then another \fBG\fR command
+to get back where you were.
+.PP
+You can also get back to a previous position by using the command
+\fB\(ga\(ga\fR (two back quotes).
+This is often more convenient than \fBG\fR because it requires no advance
+preparation.
+Try giving a \fBG\fR or a search with \fB/\fR or \fB?\fR and then a
+\fB\(ga\(ga\fR to get back to where you were. If you accidentally hit
+\fBn\fR or any command which moves you far away from a context of interest, you
+can quickly get back by hitting \fB\(ga\(ga\fR.
+.NH 2
+Moving around on the screen
+.PP
+Now try just moving the cursor around on the screen.
+If your terminal has arrow keys (4 or 5 keys with arrows
+going in each direction) try them and convince yourself
+that they work.
+If you don't have working arrow keys, you can always use
+.B h ,
+.B j ,
+.B k ,
+and
+.B l .
+Experienced users of
+.I vi
+prefer these keys to arrow keys,
+because they are usually right underneath their fingers.
+.PP
+Hit the \fB+\fR key. Each time you do, notice that the cursor
+advances to the next line in the file, at the first non-white position
+on the line. The \fB\-\fR key is like \fB+\fR but goes the other way.
+.PP
+These are very common keys for moving up and down lines in the file.
+Notice that if you go off the bottom or top with these keys then the
+screen will scroll down (and up if possible) to bring a line at a time
+into view. The \s-2RETURN\s0 key has the same effect as the \fB+\fR
+key.
+.PP
+.I Vi
+also has commands to take you to the top, middle and bottom of the screen.
+\fBH\fR will take you to the top (home) line on the screen.
+Try preceding it with a
+number as in \fB3H\fR.
+This will take you to the third line on the screen.
+Many
+.I vi
+commands take preceding numbers and do interesting things with them.
+Try \fBM\fR,
+which takes you to the middle line on the screen,
+and \fBL\fR,
+which takes you to the last line on the screen.
+\fBL\fR also takes counts, thus
+\fB5L\fR will take you to the fifth line from the bottom.
+.NH 2
+Moving within a line
+.PP
+Now try picking a word on some line on the screen, not the
+first word on the line.
+move the cursor using \s-2RETURN\s0 and \fB\-\fR to be on the line where
+the word is.
+Try hitting the \fBw\fR key. This will advance the cursor to the
+next word on the line.
+Try hitting the \fBb\fR key to back up words
+in the line.
+Also try the \fBe\fR key which advances you to the end of the current
+word rather than to the beginning of the next word.
+Also try \s-2SPACE\s0 (the space bar) which moves right one character
+and the \s-2BS\s0 (backspace or \fB^H\fR) key which moves left one character.
+The key \fBh\fR works as \fB^H\fR does and is useful if you don't have
+a \s-2BS\s0 key.
+(Also, as noted just above, \fBl\fR will move to the right.)
+.PP
+If the line had punctuation in it you may have noticed that
+that the \fBw\fR and \fBb\fR
+keys stopped at each group of punctuation. You can also go back and
+forwards words without stopping at punctuation by using \fBW\fR and \fBB\fR
+rather than the lower case equivalents. Think of these as bigger words.
+Try these on a few lines with punctuation to see how they differ from
+the lower case \fBw\fR and \fBb\fR.
+.PP
+The word keys wrap around the end of line,
+rather than stopping at the end. Try moving to a word on a line below
+where you are by repeatedly hitting \fBw\fR.
+.NH 2
+Summary
+.IP
+.TS
+lw(.50i)b a.
+\fR\s-2SPACE\s0\fP advance the cursor one position
+^B backwards to previous page
+^D scrolls down in the file
+^E exposes another line at the bottom
+^F forward to next page
+^G tell what is going on
+^H backspace the cursor
+^N next line, same column
+^P previous line, same column
+^U scrolls up in the file
+^Y exposes another line at the top
++ next line, at the beginning
+\- previous line, at the beginning
+/ scan for a following string forwards
+? scan backwards
+B back a word, ignoring punctuation
+G go to specified line, last default
+H home screen line
+M middle screen line
+L last screen line
+W forward a word, ignoring punctuation
+b back a word
+e end of current word
+n scan for next instance of \fB/\fR or \fB?\fR pattern
+w word after this word
+.TE
+.NH 2
+View
+.PP
+If you want to use the editor to look at a file,
+rather than to make changes,
+invoke it as
+.I view
+instead of
+.I vi .
+This will set the
+.I readonly
+option which will prevent you from
+accidently overwriting the file.
+.NH 1
+Making simple changes
+.NH 2
+Inserting
+.PP
+One of the most useful commands is the
+\fBi\fR (insert) command.
+After you type \fBi\fR, everything you type until you hit \s-2ESC\s0
+is inserted into the file.
+Try this now; position yourself to some word in the file and try inserting
+text before this word.
+If you are on an dumb terminal it will seem, for a minute,
+that some of the characters in your line have been overwritten, but they will
+reappear when you hit \s-2ESC\s0.
+.PP
+Now try finding a word which can, but does not, end in an `s'.
+Position yourself at this word and type \fBe\fR (move to end of word), then
+\fBa\fR for append and then `s\s-2ESC\s0' to terminate the textual insert.
+This sequence of commands can be used to easily pluralize a word.
+.PP
+Try inserting and appending a few times to make sure you understand how
+this works; \fBi\fR placing text to the left of the cursor, \fBa\fR to
+the right.
+.PP
+It is often the case that you want to add new lines to the file you are
+editing, before or after some specific line in the file. Find a line
+where this makes sense and then give the command \fBo\fR to create a
+new line after the line you are on, or the command \fBO\fR to create
+a new line before the line you are on. After you create a new line in
+this way, text you type up to an \s-2ESC\s0 is inserted on the new line.
+.PP
+Many related editor commands
+are invoked by the same letter key and differ only in that one is given
+by a lower
+case key and the other is given by
+an upper case key. In these cases, the
+upper case key often differs from the lower case key in its sense of
+direction, with
+the upper case key working backward and/or up, while the lower case
+key moves forward and/or down.
+.PP
+Whenever you are typing in text, you can give many lines of input or
+just a few characters.
+To type in more than one line of text,
+hit a \s-2RETURN\s0 at the middle of your input. A new line will be created
+for text, and you can continue to type. If you are on a slow
+and dumb terminal the editor may choose to wait to redraw the
+tail of the screen, and will let you type over the existing screen lines.
+This avoids the lengthy delay which would occur if the editor attempted
+to keep the tail of the screen always up to date. The tail of the screen will
+be fixed up, and the missing lines will reappear, when you hit \s-2ESC\s0.
+.PP
+While you are inserting new text, you can use the characters you normally use
+at the system command level (usually \fB^H\fR or \fB#\fR) to backspace
+over the last
+character which you typed, and the character which you use to kill input lines
+(usually \fB@\fR, \fB^X\fR, or \fB^U\fR)
+to erase the input you have typed on the current line.\*(dg
+.FS
+\*(dg In fact, the character \fB^H\fR (backspace) always works to erase the
+last input character here, regardless of what your erase character is.
+.FE
+The character \fB^W\fR
+will erase a whole word and leave you after the space after the previous
+word; it is useful for quickly backing up in an insert.
+.PP
+Notice that when you backspace during an insertion the characters you
+backspace over are not erased; the cursor moves backwards, and the characters
+remain on the display. This is often useful if you are planning to type
+in something similar. In any case the characters disappear when when
+you hit \s-2ESC\s0; if you want to get rid of them immediately, hit an
+\s-2ESC\s0 and then \fBa\fR again.
+.PP
+Notice also that you can't erase characters which you didn't insert, and that
+you can't backspace around the end of a line. If you need to back up
+to the previous line to make a correction, just hit \s-2ESC\s0 and move
+the cursor back to the previous line. After making the correction you
+can return to where you were and use the insert or append command again.
+.NH 2
+Making small corrections
+.PP
+You can make small corrections in existing text quite easily.
+Find a single character which is wrong or just pick any character.
+Use the arrow keys to find the character, or
+get near the character with the word motion keys and then either
+backspace (hit the \s-2BS\s0 key or \fB^H\fR or even just \fBh\fR) or
+\s-2SPACE\s0 (using the space bar)
+until the cursor is on the character which is wrong.
+If the character is not needed then hit the \fBx\fP key; this deletes
+the character from the file. It is analogous to the way you \fBx\fP
+out characters when you make mistakes on a typewriter (except it's not
+as messy).
+.PP
+If the character
+is incorrect, you can replace it with the correct character by giving
+the command \fBr\fR\fIc\fR,
+where \fIc\fR is replaced by the correct character.
+Finally if the character which is incorrect should be replaced
+by more than one character, give the command \fBs\fR which substitutes
+a string of characters, ending with \s-2ESC\s0, for it.
+If there are a small number of characters
+which are wrong you can precede \fBs\fR with a count of the number of
+characters to be replaced. Counts are also useful with \fBx\fR to specify
+the number of characters to be deleted.
+.NH 2
+More corrections: operators
+.PP
+You already know almost enough to make changes at a higher level.
+All you need to know now is that the
+.B d
+key acts as a delete operator. Try the command
+.B dw
+to delete a word.
+Try hitting \fB.\fR a few times. Notice that this repeats the effect
+of the \fBdw\fR. The command \fB.\fR repeats the last command which
+made a change. You can remember it by analogy with an ellipsis `\fB...\fR'.
+.PP
+Now try
+\fBdb\fR.
+This deletes a word backwards, namely the preceding word.
+Try
+\fBd\fR\s-2SPACE\s0. This deletes a single character, and is equivalent
+to the \fBx\fR command.
+.PP
+Another very useful operator is
+.B c
+or change. The command
+.B cw
+thus changes the text of a single word.
+You follow it by the replacement text ending with an \s-2ESC\s0.
+Find a word which you can change to another, and try this
+now.
+Notice that the end of the text to be changed was marked with the character
+`$' so that you can see this as you are typing in the new material.
+.NH 2
+Operating on lines
+.PP
+It is often the case that you want to operate on lines.
+Find a line which you want to delete, and type
+\fBdd\fR,
+the
+.B d
+operator twice. This will delete the line.
+If you are on a dumb terminal, the editor may just erase the line on
+the screen, replacing it with a line with only an @ on it. This line
+does not correspond to any line in your file, but only acts as a place
+holder. It helps to avoid a lengthy redraw of the rest of the screen
+which would be necessary to close up the hole created by the deletion
+on a terminal without a delete line capability.
+.PP
+Try repeating the
+.B c
+operator twice; this will change a whole line, erasing its previous contents and
+replacing them with text you type up to an \s-2ESC\s0.\*(dg
+.FS
+\*(dg The command \fBS\fR is a convenient synonym for for \fBcc\fR, by
+analogy with \fBs\fR. Think of \fBS\fR as a substitute on lines, while
+\fBs\fR is a substitute on characters.
+.FE
+.PP
+You can delete or change more than one line by preceding the
+.B dd
+or
+.B cc
+with a count, i.e. \fB5dd\fR deletes 5 lines.
+You can also give a command like \fBdL\fR to delete all the lines up to
+and including
+the last line on the screen, or \fBd3L\fR to delete through the third from
+the bottom line. Try some commands like this now.*
+.FS
+* One subtle point here involves using the \fB/\fR search after a \fBd\fR.
+This will normally delete characters from the current position to the
+point of the match. If what is desired is to delete whole lines
+including the two points, give the pattern as \fB/pat/+0\fR, a line address.
+.FE
+Notice that the editor lets you know when you change a large number of
+lines so that you can see the extent of the change.
+The editor will also always tell you when a change you make affects text which
+you cannot see.
+.NH 2
+Undoing
+.PP
+Now suppose that the last change which you made was incorrect;
+you could use the insert, delete and append commands to put the correct
+material back. However, since it is often the case that we regret a
+change or make a change incorrectly, the editor provides a
+.B u
+(undo) command to reverse the last change which you made.
+Try this a few times, and give it twice in a row to notice that an
+.B u
+also undoes a
+.B u.
+.PP
+The undo command lets you reverse only a single change. After you make
+a number of changes to a line, you may decide that you would rather have
+the original state of the line back. The
+.B U
+command restores the current line to the state before you started changing
+it.
+.PP
+You can recover text which you delete, even if
+undo will not bring it back; see the section on recovering lost text
+below.
+.NH 2
+Summary
+.IP
+.TS
+lw(.50i)b a.
+\fR\s-2SPACE\s0\fP advance the cursor one position
+^H backspace the cursor
+^W erase a word during an insert
+\fRerase\fP your erase (usually ^H or #), erases a character during an insert
+\fRkill\fP your kill (usually @, ^X, or ^U), kills the insert on this line
+\&\fB.\fP repeats the changing command
+O opens and inputs new lines, above the current
+U undoes the changes you made to the current line
+a appends text after the cursor
+c changes the object you specify to the following text
+d deletes the object you specify
+i inserts text before the cursor
+o opens and inputs new lines, below the current
+u undoes the last change
+.TE
+.NH 1
+Moving about; rearranging and duplicating text
+.NH 2
+Low level character motions
+.PP
+Now move the cursor to a line where there is a punctuation or a bracketing
+character such as a parenthesis or a comma or period. Try the command
+\fBf\fR\fIx\fR where \fIx\fR is this character. This command finds
+the next \fIx\fR character to the right of the cursor in the current
+line. Try then hitting a \fB;\fR, which finds the next instance of the
+same character. By using the \fBf\fR command and then a sequence of
+\fB;\fR's you can often
+get to a particular place in a line much faster than with a sequence
+of word motions or \s-2SPACE\s0s.
+There is also a \fBF\fR command, which is like \fBf\fR, but searches
+backward. The \fB;\fR command repeats \fBF\fR also.
+.PP
+When you are operating on the text in a line it is often desirable to
+deal with the characters up to, but not including, the first instance of
+a character. Try \fBdf\fR\fIx\fR for some \fIx\fR now and
+notice that the \fIx\fR character is deleted. Undo this with \fBu\fR
+and then try \fBdt\fR\fIx\fR; the \fBt\fR here stands for to, i.e.
+delete up to the next \fIx\fR, but not the \fIx\fR. The command \fBT\fR
+is the reverse of \fBt\fR.
+.PP
+When working with the text of a single line, an \fB\(ua\fR moves the
+cursor to the first non-white position on the line, and a
+\fB$\fR moves it to the end of the line. Thus \fB$a\fR will append new
+text at the end of the current line.
+.PP
+Your file may have tab (\fB^I\fR) characters in it. These
+characters are represented as a number of spaces expanding to a tab stop,
+where tab stops are every 8 positions.*
+.FS
+* This is settable by a command of the form \fB:se ts=\fR\fIx\fR\s-2CR\s0,
+where \fIx\fR is 4 to set tabstops every four columns. This has
+effect on the screen representation within the editor.
+.FE
+When the cursor is at a tab, it sits on the last of the several spaces
+which represent that tab. Try moving the cursor back and forth over
+tabs so you understand how this works.
+.PP
+On rare occasions, your file may have nonprinting characters in it.
+These characters are displayed in the same way they are represented in
+this document, that is with a two character code, the first character
+of which is `^'. On the screen non-printing characters resemble a `^'
+character adjacent to another, but spacing or backspacing over the character
+will reveal that the two characters are, like the spaces representing
+a tab character, a single character.
+.PP
+The editor sometimes discards control characters,
+depending on the character and the setting of the
+.I beautify
+option,
+if you attempt to insert them in your file.
+You can get a control character in the file by beginning
+an insert and then typing a \fB^V\fR before the control
+character. The
+\fB^V\fR quotes the following character, causing it to be
+inserted directly into the file.
+.PP
+.NH 2
+Higher level text objects
+.PP
+In working with a document it is often advantageous to work in terms
+of sentences, paragraphs, and sections. The operations \fB(\fR and \fB)\fR
+move to the beginning of the previous and next sentences respectively.
+Thus the command \fBd)\fR will delete the rest of the current sentence;
+likewise \fBd(\fR will delete the previous sentence if you are at the
+beginning of the current sentence, or the current sentence up to where
+you are if you are not at the beginning of the current sentence.
+.PP
+A sentence is defined to end at a `.', `!' or `?' which is followed by
+either the end of a line, or by two spaces. Any number of closing `)',
+`]', `"' and `\(aa' characters may appear after the `.', `!' or `?' before
+the spaces or end of line.
+.PP
+The operations \fB{\fR and \fB}\fR move over paragraphs and the operations
+\fB[[\fR and \fB]]\fR move over sections.\*(dg
+.FS
+\*(dg The \fB[[\fR and \fB]]\fR operations
+require the operation character to be doubled because they can move the
+cursor far from where it currently is. While it is easy to get back
+with the command \fB\(ga\(ga\fP,
+these commands would still be frustrating
+if they were easy to hit accidentally.
+.FE
+.PP
+A paragraph begins after each empty line, and also
+at each of a set of paragraph macros, specified by the pairs of characters
+in the definition of the string valued option \fIparagraphs\fR.
+The default setting for this option defines the paragraph macros of the
+\fI\-ms\fR and \fI\-mm\fR macro packages, i.e. the `.IP', `.LP', `.PP'
+and `.QP', `.P' and `.LI' macros.\*(dd
+.FS
+\*(dd You can easily change or extend this set of macros by assigning a
+different string to the \fIparagraphs\fR option in your EXINIT.
+See section 6.2 for details.
+The `.bp' directive is also considered to start a paragraph.
+.FE
+Each paragraph boundary is also a sentence boundary. The sentence
+and paragraph commands can
+be given counts to operate over groups of sentences and paragraphs.
+.PP
+Sections in the editor begin after each macro in the \fIsections\fR option,
+normally `.NH', `.SH', `.H' and `.HU', and each line with a formfeed \fB^L\fR
+in the first column.
+Section boundaries are always line and paragraph boundaries also.
+.PP
+Try experimenting with the sentence and paragraph commands until you are
+sure how they work. If you have a large document, try looking through
+it using the section commands.
+The section commands interpret a preceding count as a different window size in
+which to redraw the screen at the new location, and this window size
+is the base size for newly drawn windows until another size is specified.
+This is very useful
+if you are on a slow terminal and are looking for a particular section.
+You can give the first section command a small count to then see each successive
+section heading in a small window.
+.NH 2
+Rearranging and duplicating text
+.PP
+The editor has a single unnamed buffer where the last deleted or
+changed away text is saved, and a set of named buffers \fBa\fR\-\fBz\fR
+which you can use to save copies of text and to move text around in
+your file and between files.
+.PP
+The operator
+.B y
+yanks a copy of the object which follows into the unnamed buffer.
+If preceded by a buffer name, \fB"\fR\fIx\fR\|\fBy\fR, where
+\fIx\fR here is replaced by a letter \fBa\-z\fR, it places the text in the named
+buffer. The text can then be put back in the file with the commands
+.B p
+and
+.B P;
+\fBp\fR puts the text after or below the cursor, while \fBP\fR puts the text
+before or above the cursor.
+.PP
+If the text which you
+yank forms a part of a line, or is an object such as a sentence which
+partially spans more than one line, then when you put the text back,
+it will be placed after the cursor (or before if you
+use \fBP\fR). If the yanked text forms whole lines, they will be put
+back as whole lines, without changing the current line. In this case,
+the put acts much like a \fBo\fR or \fBO\fR command.
+.PP
+Try the command \fBYP\fR. This makes a copy of the current line and
+leaves you on this copy, which is placed before the current line.
+The command \fBY\fR is a convenient abbreviation for \fByy\fR.
+The command \fBYp\fR will also make a copy of the current line, and place
+it after the current line. You can give \fBY\fR a count of lines to
+yank, and thus duplicate several lines; try \fB3YP\fR.
+.PP
+To move text within the buffer, you need to delete it in one place, and
+put it back in another. You can precede a delete operation by the
+name of a buffer in which the text is to be stored as in \fB"a5dd\fR
+deleting 5 lines into the named buffer \fIa\fR. You can then move the
+cursor to the eventual resting place of the these lines and do a \fB"ap\fR
+or \fB"aP\fR to put them back.
+In fact, you can switch and edit another file before you put the lines
+back, by giving a command of the form \fB:e \fR\fIname\fR\s-2CR\s0 where
+\fIname\fR is the name of the other file you want to edit. You will
+have to write back the contents of the current editor buffer (or discard
+them) if you have made changes before the editor will let you switch
+to the other file.
+An ordinary delete command saves the text in the unnamed buffer,
+so that an ordinary put can move it elsewhere.
+However, the unnamed buffer is lost when you change files,
+so to move text from one file to another you should use an unnamed buffer.
+.NH 2
+Summary.
+.IP
+.TS
+lw(.50i)b a.
+\(ua first non-white on line
+$ end of line
+) forward sentence
+} forward paragraph
+]] forward section
+( backward sentence
+{ backward paragraph
+[[ backward section
+f\fIx\fR find \fIx\fR forward in line
+p put text back, after cursor or below current line
+y yank operator, for copies and moves
+t\fIx\fR up to \fIx\fR forward, for operators
+F\fIx\fR f backward in line
+P put text back, before cursor or above current line
+T\fIx\fR t backward in line
+.TE
+.NH 1
+High level commands
+.NH 2
+Writing, quitting, editing new files
+.PP
+So far we have seen how to enter
+.I vi
+and to write out our file using either
+\fBZZ\fR or \fB:w\fR\s-2CR\s0. The first exits from
+the editor,
+(writing if changes were made),
+the second writes and stays in the editor.
+.PP
+If you have changed the editor's copy of the file but do not wish to
+save your changes, either because you messed up the file or decided that the
+changes are not an improvement to the file, then you can give the command
+\fB:q!\fR\s-2CR\s0 to quit from the editor without writing the changes.
+You can also reedit the same file (starting over) by giving the command
+\fB:e!\fR\s-2CR\s0. These commands should be used only rarely, and with
+caution, as it is not possible to recover the changes you have made after
+you discard them in this manner.
+.PP
+You can edit a different file without leaving the editor by giving the
+command \fB:e\fR\ \fIname\fR\s-2CR\s0. If you have not written out
+your file before you try to do this, then the editor will tell you this,
+and delay editing the other file. You can then give the command
+\fB:w\fR\s-2CR\s0 to save your work and then the \fB:e\fR\ \fIname\fR\s-2CR\s0
+command again, or carefully give the command \fB:e!\fR\ \fIname\fR\s-2CR\s0,
+which edits the other file discarding the changes you have made to the
+current file.
+To have the editor automatically save changes,
+include
+.I "set autowrite"
+in your EXINIT,
+and use \fB:n\fP instead of \fB:e\fP.
+.NH 2
+Escaping to a shell
+.PP
+You can get to a shell to execute a single command by giving a
+.I vi
+command of the form \fB:!\fIcmd\fR\s-2CR\s0.
+The system will run the single command
+.I cmd
+and when the command finishes, the editor will ask you to hit a \s-2RETURN\s0
+to continue. When you have finished looking at the output on the screen,
+you should hit \s-2RETURN\s0 and the editor will clear the screen and
+redraw it. You can then continue editing.
+You can also give another \fB:\fR command when it asks you for a \s-2RETURN\s0;
+in this case the screen will not be redrawn.
+.PP
+If you wish to execute more than one command in the shell, then you can
+give the command \fB:sh\fR\s-2CR\s0.
+This will give you a new shell, and when you finish with the shell, ending
+it by typing a \fB^D\fR, the editor will clear the screen and continue.
+.PP
+On systems which support it, \fB^Z\fP will suspend the editor
+and return to the (top level) shell.
+When the editor is resumed, the screen will be redrawn.
+.NH 2
+Marking and returning
+.PP
+The command \fB\(ga\(ga\fR returned to the previous place
+after a motion of the cursor by a command such as \fB/\fR, \fB?\fR or
+\fBG\fR. You can also mark lines in the file with single letter tags
+and return to these marks later by naming the tags. Try marking the
+current line with the command \fBm\fR\fIx\fR, where you should pick some
+letter for \fIx\fR, say `a'. Then move the cursor to a different line
+(any way you like) and hit \fB\(gaa\fR. The cursor will return to the
+place which you marked.
+Marks last only until you edit another file.
+.PP
+When using operators such as
+.B d
+and referring to marked lines, it is often desirable to delete whole lines
+rather than deleting to the exact position in the line marked by \fBm\fR.
+In this case you can use the form \fB\(aa\fR\fIx\fR rather than
+\fB\(ga\fR\fIx\fR. Used without an operator, \fB\(aa\fR\fIx\fR will move to
+the first non-white character of the marked line; similarly \fB\(aa\(aa\fR
+moves to the first non-white character of the line containing the previous
+context mark \fB\(ga\(ga\fR.
+.NH 2
+Adjusting the screen
+.PP
+If the screen image is messed up because of a transmission error to your
+terminal, or because some program other than the editor wrote output
+to your terminal, you can hit a \fB^L\fR, the \s-2ASCII\s0 form-feed
+character, to cause the screen to be refreshed.
+.PP
+On a dumb terminal, if there are @ lines in the middle of the screen
+as a result of line deletion, you may get rid of these lines by typing
+\fB^R\fR to cause the editor to retype the screen, closing up these holes.
+.PP
+Finally, if you wish to place a certain line on the screen at the top
+middle or bottom of the screen, you can position the cursor to that line,
+and then give a \fBz\fR command.
+You should follow the \fBz\fR command with a \s-2RETURN\s0 if you want
+the line to appear at the top of the window, a \fB.\fR if you want it
+at the center, or a \fB\-\fR if you want it at the bottom.
+.NH 1
+Special topics
+.NH 2
+Editing on slow terminals
+.PP
+When you are on a slow terminal, it is important to limit the amount
+of output which is generated to your screen so that you will not suffer
+long delays, waiting for the screen to be refreshed. We have already
+pointed out how the editor optimizes the updating of the screen during
+insertions on dumb terminals to limit the delays, and how the editor erases
+lines to @ when they are deleted on dumb terminals.
+.PP
+The use of the slow terminal insertion mode is controlled by the
+.I slowopen
+option. You can force the editor to use this mode even on faster terminals
+by giving the command \fB:se slow\fR\s-2CR\s0. If your system is sluggish
+this helps lessen the amount of output coming to your terminal.
+You can disable this option by \fB:se noslow\fR\s-2CR\s0.
+.PP
+The editor can simulate an intelligent terminal on a dumb one. Try
+giving the command \fB:se redraw\fR\s-2CR\s0. This simulation generates
+a great deal of output and is generally tolerable only on lightly loaded
+systems and fast terminals. You can disable this by giving the command
+ \fB:se noredraw\fR\s-2CR\s0.
+.PP
+The editor also makes editing more pleasant at low speed by starting
+editing in a small window, and letting the window expand as you edit.
+This works particularly well on intelligent terminals. The editor can
+expand the window easily when you insert in the middle of the screen
+on these terminals. If possible, try the editor on an intelligent terminal
+to see how this works.
+.PP
+You can control the size of the window which is redrawn each time the
+screen is cleared by giving window sizes as argument to the commands
+which cause large screen motions:
+.DS
+.B ": / ? [[ ]] \(ga \(aa"
+.DE
+Thus if you are searching for a particular instance of a common string
+in a file you can precede the first search command by a small number,
+say 3, and the editor will draw three line windows around each instance
+of the string which it locates.
+.PP
+You can easily expand or contract the window, placing the current line
+as you choose, by giving a number on a \fBz\fR command, after the \fBz\fR
+and before the following \s-2RETURN\s0, \fB.\fR or \fB\-\fR. Thus the
+command \fBz5.\fR redraws the screen with the current line in the center
+of a five line window.\*(dg
+.FS
+\*(dg Note that the command \fB5z.\fR has an entirely different effect,
+placing line 5 in the center of a new window.
+.FE
+.PP
+If the editor is redrawing or otherwise updating large portions of the
+display, you can interrupt this updating by hitting a \s-2DEL\s0 or \s-2RUB\s0
+as usual. If you do this you may partially confuse the editor about
+what is displayed on the screen. You can still edit the text on
+the screen if you wish; clear up the confusion
+by hitting a \fB^L\fR; or move or search again, ignoring the
+current state of the display.
+.PP
+See section 7.8 on \fIopen\fR mode for another way to use the
+.I vi
+command set on slow terminals.
+.NH 2
+Options, set, and editor startup files
+.PP
+The editor has a set of options, some of which have been mentioned above.
+The most useful options are given in the following table.
+.KF
+.TS
+lb lb lb lb
+l l l a.
+Name Default Description
+_
+autoindent noai Supply indentation automatically
+autowrite noaw Automatic write before \fB:n\fR, \fB:ta\fR, \fB^\(ua\fR, \fB!\fR
+ignorecase noic Ignore case in searching
+lisp nolisp \fB( { ) }\fR commands deal with S-expressions
+list nolist Tabs print as ^I; end of lines marked with $
+magic nomagic The characters . [ and * are special in scans
+number nonu Lines are displayed prefixed with line numbers
+paragraphs para=IPLPPPQPbpP LI Macro names which start paragraphs
+redraw nore Simulate a smart terminal on a dumb one
+sections sect=NHSHH HU Macro names which start new sections
+shiftwidth sw=8 Shift distance for <, > and input \fB^D\fP and \fB^T\fR
+showmatch nosm Show matching \fB(\fP or \fB{\fP as \fB)\fP or \fB}\fR is typed
+slowopen slow Postpone display updates during inserts
+term dumb The kind of terminal you are using.
+.TE
+.KE
+.PP
+The options are of three kinds: numeric options, string options, and
+toggle options. You can set numeric and string options by a statement
+of the form
+.DS
+\fBset\fR \fIopt\fR\fB=\fR\fIval\fR
+.DE
+and toggle options can be set or unset by statements of one of the forms
+.DS
+\fBset\fR \fIopt\fR
+\fBset\fR \fBno\fR\fIopt\fR
+.DE
+These statements can be placed in your EXINIT in your environment,
+or given while you are running
+.I vi
+by preceding them with a \fB:\fR and following them with a \s-2CR\s0.
+.PP
+You can get a list of all options which you have changed by the
+command \fB:set\fR\s-2CR\s0, or the value of a single option by the
+command \fB:set\fR \fIopt\fR\fB?\fR\s-2CR\s0.
+A list of all possible options and their values is generated by
+\fB:set all\fP\s-2CR\s0.
+Set can be abbreviated \fBse\fP.
+Multiple options can be placed on one line, e.g.
+\fB:se ai aw nu\fP\s-2CR\s0.
+.PP
+Options set by the \fBset\fP command only last
+while you stay in the editor.
+It is common to want to have certain options set whenever you
+use the editor.
+This can be accomplished by creating a list of \fIex\fP commands\*(dg
+.FS
+\*(dg
+All commands which start with
+.B :
+are \fIex\fP commands.
+.FE
+which are to be run every time you start up \fIex\fP, \fIedit\fP,
+or \fIvi\fP.
+A typical list includes a \fBset\fP command, and possibly a few
+\fBmap\fP commands.
+Since it is advisable to get these commands on one line, they can
+be separated with the | character, for example:
+.DS
+\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x
+.DE
+which sets the options \fIautoindent\fP, \fIautowrite\fP, \fIterse\fP,
+(the
+.B set
+command),
+makes @ delete a line,
+(the first
+.B map ),
+and makes # delete a character,
+(the second
+.B map ).
+(See section 6.9 for a description of the \fBmap\fP command)
+This string should be placed in the variable EXINIT in your environment.
+If you use the shell \fIcsh\fP,
+put this line in the file
+.I .login
+in your home directory:
+.DS
+setenv EXINIT \(aa\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x\(aa
+.DE
+If you use the standard shell \fIsh\fP,
+put these lines in the file
+.I .profile
+in your home directory:
+.DS
+EXINIT=\(aa\fBset\fP ai aw terse|\fBmap\fP @ dd|\fBmap\fP # x\(aa
+export EXINIT
+.DE
+Of course, the particulars of the line would depend on which options
+you wanted to set.
+.NH 2
+Recovering lost lines
+.PP
+You might have a serious problem if you delete a number of lines and then
+regret that they were deleted. Despair not, the editor saves the last
+9 deleted blocks of text in a set of numbered registers 1\-9.
+You can get the \fIn\fR'th previous deleted text back in your file by
+the command
+"\fR\fIn\fR\|\fBp\fR.
+The "\fR here says that a buffer name is to follow,
+\fIn\fR is the number of the buffer you wish to try
+(use the number 1 for now),
+and
+.B p
+is the put command, which puts text in the buffer after the cursor.
+If this doesn't bring back the text you wanted, hit
+.B u
+to undo this and then
+\fB\&.\fR
+(period)
+to repeat the put command.
+In general the
+\fB\&.\fR
+command will repeat the last change you made.
+As a special case, when the last command refers to a numbered text buffer,
+the \fB.\fR command increments the number of the buffer before repeating
+the command. Thus a sequence of the form
+.DS
+\fB"1pu.u.u.\fR
+.DE
+will, if repeated long enough, show you all the deleted text which has
+been saved for you.
+You can omit the
+.B u
+commands here to gather up all this text in the buffer, or stop after any
+\fB\&.\fR command to keep just the then recovered text.
+The command
+.B P
+can also be used rather than
+.B p
+to put the recovered text before rather than after the cursor.
+.NH 2
+Recovering lost files
+.PP
+If the system crashes, you can recover the work you were doing
+to within a few changes. You will normally receive mail when you next
+login giving you the name of the file which has been saved for you.
+You should then change to the directory where you were when the system
+crashed and give a command of the form:
+.DS
+% \fBvi \-r\fR \fIname\fR
+.DE
+replacing \fIname\fR with the name of the file which you were editing.
+This will recover your work to a point near where you left off.\*(dg
+.FS
+\*(dg In rare cases, some of the lines of the file may be lost. The
+editor will give you the numbers of these lines and the text of the lines
+will be replaced by the string `LOST'. These lines will almost always
+be among the last few which you changed. You can either choose to discard
+the changes which you made (if they are easy to remake) or to replace
+the few lost lines by hand.
+.FE
+.PP
+You can get a listing of the files which are saved for you by giving
+the command:
+.DS
+% \fBvi \-r\fR
+.DE
+If there is more than one instance of a particular file saved, the editor
+gives you the newest instance each time you recover it. You can thus
+get an older saved copy back by first recovering the newer copies.
+.PP
+For this feature to work,
+.I vi
+must be correctly installed by a super user on your system,
+and the
+.I mail
+program must exist to receive mail.
+The invocation ``\fIvi -r\fP'' will not always list all saved files,
+but they can be recovered even if they are not listed.
+.NH 2
+Continuous text input
+.PP
+When you are typing in large amounts of text it is convenient to have
+lines broken near the right margin automatically. You can cause this
+to happen by giving the command
+\fB:se wm=10\fR\s-2CR\s0.
+This causes all lines to be broken at a space at least 10 columns
+from the right hand edge of the screen.
+.PP
+If the editor breaks an input line and you wish to put it back together
+you can tell it to join the lines with \fBJ\fR. You can give \fBJ\fR
+a count of the number of lines to be joined as in \fB3J\fR to join 3
+lines. The editor supplies white space, if appropriate,
+at the juncture of the joined
+lines, and leaves the cursor at this white space.
+You can kill the white space with \fBx\fR if you don't want it.
+.NH 2
+Features for editing programs
+.PP
+The editor has a number of commands for editing programs.
+The thing that most distinguishes editing of programs from editing of text
+is the desirability of maintaining an indented structure to the body of
+the program. The editor has a
+.I autoindent
+facility for helping you generate correctly indented programs.
+.PP
+To enable this facility you can give the command \fB:se ai\fR\s-2CR\s0.
+Now try opening a new line with \fBo\fR and type some characters on the
+line after a few tabs. If you now start another line, notice that the
+editor supplies white space at the beginning of the line to line it up
+with the previous line. You cannot backspace over this indentation,
+but you can use \fB^D\fR key to backtab over the supplied indentation.
+.PP
+Each time you type \fB^D\fR you back up one position, normally to an
+8 column boundary. This amount is settable; the editor has an option
+called
+.I shiftwidth
+which you can set to change this value.
+Try giving the command \fB:se sw=4\fR\s-2CR\s0
+and then experimenting with autoindent again.
+.PP
+For shifting lines in the program left and right, there are operators
+.B <
+and
+.B >.
+These shift the lines you specify right or left by one
+.I shiftwidth.
+Try
+.B <<
+and
+.B >>
+which shift one line left or right, and
+.B <L
+and
+.B >L
+shifting the rest of the display left and right.
+.PP
+If you have a complicated expression and wish to see how the parentheses
+match, put the cursor at a left or right parenthesis and hit \fB%\fR.
+This will show you the matching parenthesis.
+This works also for braces { and }, and brackets [ and ].
+.PP
+If you are editing C programs, you can use the \fB[[\fR and \fB]]\fR keys
+to advance or retreat to a line starting with a \fB{\fR, i.e. a function
+declaration at a time. When \fB]]\fR is used with an operator it stops
+after a line which starts with \fB}\fR; this is sometimes useful with
+\fBy]]\fR.
+.NH 2
+Filtering portions of the buffer
+.PP
+You can run system commands over portions of the buffer using the operator
+\fB!\fR.
+You can use this to sort lines in the buffer, or to reformat portions
+of the buffer with a pretty-printer.
+Try typing in a list of random words, one per line and ending them
+with a blank line. Back up to the beginning of the list, and then give
+the command \fB!}sort\fR\s-2CR\s0. This says to sort the next paragraph
+of material, and the blank line ends a paragraph.
+.NH 2
+Commands for editing \s-2LISP\s0
+.PP
+If you are editing a \s-2LISP\s0 program you should set the option
+.I lisp
+by doing
+\fB:se\ lisp\fR\s-2CR\s0.
+This changes the \fB(\fR and \fB)\fR commands to move backward and forward
+over s-expressions.
+The \fB{\fR and \fB}\fR commands are like \fB(\fR and \fB)\fR but don't
+stop at atoms. These can be used to skip to the next list, or through
+a comment quickly.
+.PP
+The
+.I autoindent
+option works differently for \s-2LISP\s0, supplying indent to align at
+the first argument to the last open list. If there is no such argument
+then the indent is two spaces more than the last level.
+.PP
+There is another option which is useful for typing in \s-2LISP\s0, the
+.I showmatch
+option.
+Try setting it with
+\fB:se sm\fR\s-2CR\s0
+and then try typing a `(' some words and then a `)'. Notice that the
+cursor shows the position of the `(' which matches the `)' briefly.
+This happens only if the matching `(' is on the screen, and the cursor
+stays there for at most one second.
+.PP
+The editor also has an operator to realign existing lines as though they
+had been typed in with
+.I lisp
+and
+.I autoindent
+set. This is the \fB=\fR operator.
+Try the command \fB=%\fR at the beginning of a function. This will realign
+all the lines of the function declaration.
+.PP
+When you are editing \s-2LISP\s0,, the \fB[[\fR and \fR]]\fR advance
+and retreat to lines beginning with a \fB(\fR, and are useful for dealing
+with entire function definitions.
+.NH 2
+Macros
+.PP
+.I Vi
+has a parameterless macro facility, which lets you set it up so that
+when you hit a single keystroke, the editor will act as though
+you had hit some longer sequence of keys. You can set this up if
+you find yourself typing the same sequence of commands repeatedly.
+.PP
+Briefly, there are two flavors of macros:
+.IP a)
+Ones where you put the macro body in a buffer register, say \fIx\fR.
+You can then type \fB@x\fR to invoke the macro. The \fB@\fR may be followed
+by another \fB@\fR to repeat the last macro.
+.IP b)
+You can use the
+.I map
+command from
+.I vi
+(typically in your
+.I EXINIT )
+with a command of the form:
+.DS
+:map \fIlhs\fR \fIrhs\fR\s-2CR\f0
+.DE
+mapping
+.I lhs
+into
+.I rhs.
+There are restrictions:
+.I lhs
+should be one keystroke (either 1 character or one function key)
+since it must be entered within one second
+(unless
+.I notimeout
+is set, in which case you can type it as slowly as you wish,
+and
+.I vi
+will wait for you to finish it before it echoes anything).
+The
+.I lhs
+can be no longer than 10 characters, the
+.I rhs
+no longer than 100.
+To get a space, tab or newline into
+.I lhs
+or
+.I rhs
+you should escape them with a \fB^V\fR.
+(It may be necessary to double the \fB^V\fR if the map
+command is given inside
+.I vi,
+rather than in
+.I ex.)
+Spaces and tabs inside the
+.I rhs
+need not be escaped.
+.PP
+Thus to make the \fBq\fR key write and exit the editor, you can give
+the command
+.DS
+:map q :wq\fB^V^V\fP\s-2CR CR\s0
+.DE
+which means that whenever you type \fBq\fR, it will be as though you
+had typed the four characters \fB:wq\fR\s-2CR\s0.
+A \fB^V\fR's is needed because without it the \s-2CR\s0 would end the
+\fB:\fR command, rather than becoming part of the
+.I map
+definition.
+There are two
+.B ^V 's
+because from within
+.I vi ,
+two
+.B ^V 's
+must be typed to get one.
+The first \s-2CR\s0 is part of the
+.I rhs ,
+the second terminates the : command.
+.PP
+Macros can be deleted with
+.DS
+unmap lhs
+.DE
+.PP
+If the
+.I lhs
+of a macro is ``#0'' through ``#9'', this maps the particular function key
+instead of the 2 character ``#'' sequence. So that terminals without
+function keys can access such definitions, the form ``#x'' will mean function
+key
+.I x
+on all terminals (and need not be typed within one second.)
+The character ``#'' can be changed by using a macro in the usual way:
+.DS
+:map \fB^V^V^I\fP #
+.DE
+to use tab, for example. (This won't affect the
+.I map
+command, which still uses
+.B #,
+but just the invocation from visual mode.
+.PP
+The undo command reverses an entire macro call as a unit,
+if it made any changes.
+.PP
+Placing a `!' after the word
+.B map
+causes the mapping to apply
+to input mode, rather than command mode.
+Thus, to arrange for \fB^T\fP to be the same as 4 spaces in input mode,
+you can type:
+.DS
+:map \fB^T\fP \fB^V\fP\o'b/'\o'b/'\o'b/'\o'b/'
+.DE
+where
+.B \o'b/'
+is a blank.
+The \fB^V\fP is necessary to prevent the blanks from being taken as
+white space between the
+.I lhs
+and
+.I rhs .
+.NH
+Word Abbreviations
+.PP
+A feature similar to macros in input mode is word abbreviation.
+This allows you to type a short word and have it expanded into
+a longer word or words.
+The commands are
+.B :abbreviate
+and
+.B :unabbreviate
+(\fB:ab\fP
+and
+.B :una )
+and have the same syntax as
+.B :map .
+For example:
+.DS
+:ab eecs Electrical Engineering and Computer Sciences
+.DE
+causes the word `eecs' to always be changed into the
+phrase `Electrical Engineering and Computer Sciences'.
+Word abbreviation is different from macros in that
+only whole words are affected.
+If `eecs' were typed as part of a larger word, it would
+be left alone.
+Also, the partial word is echoed as it is typed.
+There is no need for an abbreviation to be a single keystroke,
+as it should be with a macro.
+.NH 2
+Abbreviations
+.PP
+The editor has a number of short
+commands which abbreviate longer commands which we
+have introduced here. You can find these commands easily
+on the quick reference card.
+They often save a bit of typing and you can learn them as convenient.
+.NH 1
+Nitty-gritty details
+.NH 2
+Line representation in the display
+.PP
+The editor folds long logical lines onto many physical lines in the display.
+Commands which advance lines advance logical lines and will skip
+over all the segments of a line in one motion. The command \fB|\fR moves
+the cursor to a specific column, and may be useful for getting near the
+middle of a long line to split it in half. Try \fB80|\fR on a line which
+is more than 80 columns long.\*(dg
+.FS
+\*(dg You can make long lines very easily by using \fBJ\fR to join together
+short lines.
+.FE
+.PP
+The editor only puts full lines on the display; if there is not enough
+room on the display to fit a logical line, the editor leaves the physical
+line empty, placing only an @ on the line as a place holder. When you
+delete lines on a dumb terminal, the editor will often just clear the
+lines to @ to save time (rather than rewriting the rest of the screen.)
+You can always maximize the information on the screen by giving the \fB^R\fR
+command.
+.PP
+If you wish, you can have the editor place line numbers before each line
+on the display. Give the command \fB:se nu\fR\s-2CR\s0 to enable
+this, and the command \fB:se nonu\fR\s-2CR\s0 to turn it off.
+You can have tabs represented as \fB^I\fR and the ends of lines indicated
+with `$' by giving the command \fB:se list\fR\s-2CR\s0;
+\fB:se nolist\fR\s-2CR\s0 turns this off.
+.PP
+Finally, lines consisting of only the character `~' are displayed when
+the last line in the file is in the middle of the screen. These represent
+physical lines which are past the logical end of file.
+.NH 2
+Counts
+.PP
+Most
+.I vi
+commands will use a preceding count to affect their behavior in some way.
+The following table gives the common ways in which the counts are used:
+.DS
+.TS
+l lb.
+new window size : / ? [[ ]] \` \'
+scroll amount ^D ^U
+line/column number z G |
+repeat effect \fRmost of the rest\fP
+.TE
+.DE
+.PP
+The editor maintains a notion of the current default window size.
+On terminals which run at speeds greater than 1200 baud
+the editor uses the full terminal screen.
+On terminals which are slower than 1200 baud
+(most dialup lines are in this group)
+the editor uses 8 lines as the default window size.
+At 1200 baud the default is 16 lines.
+.PP
+This size is the size used when the editor clears and refills the screen
+after a search or other motion moves far from the edge of the current window.
+The commands which take a new window size as count all often cause the
+screen to be redrawn. If you anticipate this, but do not need as large
+a window as you are currently using, you may wish to change the screen
+size by specifying the new size before these commands.
+In any case, the number of lines used on the screen will expand if you
+move off the top with a \fB\-\fR or similar command or off the bottom
+with a command such as \s-2RETURN\s0 or \fB^D\fR.
+The window will revert to the last specified size the next time it is
+cleared and refilled.\*(dg
+.FS
+\*(dg But not by a \fB^L\fR which just redraws the screen as it is.
+.FE
+.PP
+The scroll commands \fB^D\fR and \fB^U\fR likewise remember the amount
+of scroll last specified, using half the basic window size initially.
+The simple insert commands use a count to specify a repetition of the
+inserted text. Thus \fB10a+\-\-\-\-\fR\s-2ESC\s0 will insert a grid-like
+string of text.
+A few commands also use a preceding count as a line or column number.
+.PP
+Except for a few commands which ignore any counts (such as \fB^R\fR),
+the rest of the editor commands use a count to indicate a simple repetition
+of their effect. Thus \fB5w\fR advances five words on the current line,
+while \fB5\fR\s-2RETURN\s0 advances five lines. A very useful instance
+of a count as a repetition is a count given to the \fB.\fR command, which
+repeats the last changing command. If you do \fBdw\fR and then \fB3.\fR,
+you will delete first one and then three words. You can then delete
+two more words with \fB2.\fR.
+.NH 2
+More file manipulation commands
+.PP
+The following table lists the file manipulation commands which you can
+use when you are in
+.I vi.
+.KF
+.DS
+.TS
+lb l.
+:w write back changes
+:wq write and quit
+:x write (if necessary) and quit (same as ZZ).
+:e \fIname\fP edit file \fIname\fR
+:e! reedit, discarding changes
+:e + \fIname\fP edit, starting at end
+:e +\fIn\fP edit, starting at line \fIn\fP
+:e # edit alternate file
+:w \fIname\fP write file \fIname\fP
+:w! \fIname\fP overwrite file \fIname\fP
+:\fIx,y\fPw \fIname\fP write lines \fIx\fP through \fIy\fP to \fIname\fP
+:r \fIname\fP read file \fIname\fP into buffer
+:r !\fIcmd\fP read output of \fIcmd\fP into buffer
+:n edit next file in argument list
+:n! edit next file, discarding changes to current
+:n \fIargs\fP specify new argument list
+:ta \fItag\fP edit file containing tag \fItag\fP, at \fItag\fP
+.TE
+.DE
+.KE
+All of these commands are followed by a \s-2CR\s0 or \s-2ESC\s0.
+The most basic commands are \fB:w\fR and \fB:e\fR.
+A normal editing session on a single file will end with a \fBZZ\fR command.
+If you are editing for a long period of time you can give \fB:w\fR commands
+occasionally after major amounts of editing, and then finish
+with a \fBZZ\fR. When you edit more than one file, you can finish
+with one with a \fB:w\fR and start editing a new file by giving a \fB:e\fR
+command,
+or set
+.I autowrite
+and use \fB:n\fP <file>.
+.PP
+If you make changes to the editor's copy of a file, but do not wish to
+write them back, then you must give an \fB!\fR after the command you
+would otherwise use; this forces the editor to discard any changes
+you have made. Use this carefully.
+.PP
+The \fB:e\fR command can be given a \fB+\fR argument to start at the
+end of the file, or a \fB+\fR\fIn\fR argument to start at line \fIn\fR\^.
+In actuality, \fIn\fR may be any editor command not containing a space,
+usefully a scan like \fB+/\fIpat\fR or \fB+?\fIpat\fR.
+In forming new names to the \fBe\fR command, you can use the character
+\fB%\fR which is replaced by the current file name, or the character
+\fB#\fR which is replaced by the alternate file name.
+The alternate file name is generally the last name you typed other than
+the current file. Thus if you try to do a \fB:e\fR and get a diagnostic
+that you haven't written the file, you can give a \fB:w\fR command and
+then a \fB:e #\fR command to redo the previous \fB:e\fR.
+.PP
+You can write part of the buffer to a file by finding out the lines
+that bound the range to be written using \fB^G\fR, and giving these
+numbers after the \fB:\fR
+and before the \fBw\fP, separated by \fB,\fR's.
+You can also mark these lines with \fBm\fR and
+then use an address of the form \fB\(aa\fR\fIx\fR\fB,\fB\(aa\fR\fIy\fR
+on the \fBw\fR command here.
+.PP
+You can read another file into the buffer after the current line by using
+the \fB:r\fR command.
+You can similarly read in the output from a command, just use \fB!\fR\fIcmd\fR
+instead of a file name.
+.PP
+If you wish to edit a set of files in succession, you can give all the
+names on the command line, and then edit each one in turn using the command
+\fB:n\fR. It is also possible to respecify the list of files to be edited
+by giving the \fB:n\fR command a list of file names, or a pattern to
+be expanded as you would have given it on the initial
+.I vi
+command.
+.PP
+If you are editing large programs, you will find the \fB:ta\fR command
+very useful. It utilizes a data base of function names and their locations,
+which can be created by programs such as
+.I ctags,
+to quickly find a function whose name you give.
+If the \fB:ta\fR command will require the editor to switch files, then
+you must \fB:w\fR or abandon any changes before switching. You can repeat
+the \fB:ta\fR command without any arguments to look for the same tag
+again.
+.NH 2
+More about searching for strings
+.PP
+When you are searching for strings in the file with \fB/\fR and \fB?\fR,
+the editor normally places you at the next or previous occurrence
+of the string. If you are using an operator such as \fBd\fR,
+\fBc\fR or \fBy\fR, then you may well wish to affect lines up to the
+line before the line containing the pattern. You can give a search of
+the form \fB/\fR\fIpat\fR\fB/\-\fR\fIn\fR to refer to the \fIn\fR'th line
+before the next line containing \fIpat\fR, or you can use \fB\+\fR instead
+of \fB\-\fR to refer to the lines after the one containing \fIpat\fR.
+If you don't give a line offset, then the editor will affect characters
+up to the match place, rather than whole lines; thus use ``+0'' to affect
+to the line which matches.
+.PP
+You can have the editor ignore the case of words in the searches it does
+by giving the command \fB:se ic\fR\s-2CR\s0.
+The command \fB:se noic\fR\s-2CR\s0 turns this off.
+.PP
+Strings given to searches may actually be regular expressions.
+If you do not want or need this facility, you should
+.DS
+set nomagic
+.DE
+in your EXINIT.
+In this case,
+only the characters \fB\(ua\fR and \fB$\fR are special in patterns.
+The character \fB\e\fR is also then special (as it is most everywhere in
+the system), and may be used to get at the
+an extended pattern matching facility.
+It is also necessary to use a \e before a
+\fB/\fR in a forward scan or a \fB?\fR in a backward scan, in any case.
+The following table gives the extended forms when \fBmagic\fR is set.
+.DS
+.TS
+bl l.
+\(ua at beginning of pattern, matches beginning of line
+$ at end of pattern, matches end of line
+\fB\&.\fR matches any character
+\e< matches the beginning of a word
+\e> matches the end of a word
+[\fIstr\fP] matches any single character in \fIstr\fP
+[\(ua\fIstr\fP] matches any single character not in \fIstr\fP
+[\fIx\fP\-\fIy\fP] matches any character between \fIx\fP and \fIy\fP
+* matches any number of the preceding pattern
+.TE
+.DE
+If you use \fBnomagic\fR mode, then
+the \fB. [\fR and \fB*\fR primitives are given with a preceding
+\e.
+.NH 2
+More about input mode
+.PP
+There are a number of characters which you can use to make corrections
+during input mode. These are summarized in the following table.
+.DS
+.TS
+lb l.
+^H deletes the last input character
+^W deletes the last input word, defined as by \fBb\fR
+erase your erase character, same as \fB^H\fP
+kill your kill character, deletes the input on this line
+\e escapes a following \fB^H\fP and your erase and kill
+\s-2ESC\s0 ends an insertion
+\s-2DEL\s0 interrupts an insertion, terminating it abnormally
+\s-2CR\s0 starts a new line
+^D backtabs over \fIautoindent\fP
+0^D kills all the \fIautoindent\fP
+\(ua^D same as \fB0^D\fP, but restores indent next line
+^V quotes the next non-printing character into the file
+.TE
+.DE
+.PP
+The most usual way of making corrections to input is by typing \fB^H\fR
+to correct a single character, or by typing one or more \fB^W\fR's to
+back over incorrect words. If you use \fB#\fR as your erase character
+in the normal system, it will work like \fB^H\fR.
+.PP
+Your system kill character, normally \fB@\fR, \fB^X\fP or \fB^U\fR,
+will erase all
+the input you have given on the current line.
+In general, you can neither
+erase input back around a line boundary nor can you erase characters
+which you did not insert with this insertion command. To make corrections
+on the previous line after a new line has been started you can hit \s-2ESC\s0
+to end the insertion, move over and make the correction, and then return
+to where you were to continue. The command \fBA\fR which appends at the
+end of the current line is often useful for continuing.
+.PP
+If you wish to type in your erase or kill character (say # or @) then
+you must precede it with a \fB\e\fR, just as you would do at the normal
+system command level. A more general way of typing non-printing characters
+into the file is to precede them with a \fB^V\fR. The \fB^V\fR echoes
+as a \fB\(ua\fR character on which the cursor rests. This indicates that
+the editor expects you to type a control character. In fact you may
+type any character and it will be inserted into the file at that point.*
+.FS
+* This is not quite true. The implementation of the editor does
+not allow the \s-2NULL\s0 (\fB^@\fR) character to appear in files. Also
+the \s-2LF\s0 (linefeed or \fB^J\fR) character is used by the editor
+to separate lines in the file, so it cannot appear in the middle of a
+line. You can insert any other character, however, if you wait for the
+editor to echo the \fB\(ua\fR before you type the character. In fact,
+the editor will treat a following letter as a request for the corresponding
+control character. This is the only way to type \fB^S\fR or \fB^Q\fP,
+since the system normally uses them to suspend and resume output
+and never gives them to the editor to process.
+.FE
+.PP
+If you are using \fIautoindent\fR you can backtab over the indent which
+it supplies by typing a \fB^D\fR. This backs up to a \fIshiftwidth\fR
+boundary.
+This only works immediately after the supplied \fIautoindent\fR.
+.PP
+When you are using \fIautoindent\fR you may wish to place a label at
+the left margin of a line. The way to do this easily is to type \fB\(ua\fR
+and then \fB^D\fR. The editor will move the cursor to the left margin
+for one line, and restore the previous indent on the next. You can also
+type a \fB0\fR followed immediately by a \fB^D\fR if you wish to kill
+all the indent and not have it come back on the next line.
+.NH 2
+Upper case only terminals
+.PP
+If your terminal has only upper case, you can still use
+.I vi
+by using the normal
+system convention for typing on such a terminal.
+Characters which you normally type are converted to lower case, and you
+can type upper case letters by preceding them with a \e.
+The characters { ~ } | \(ga are not available on such terminals, but you
+can escape them as \e( \e\(ua \e) \e! \e\(aa.
+These characters are represented on the display in the same way they
+are typed.\*(dd
+.FS
+\*(dd The \e character you give will not echo until you type another
+key.
+.FE
+.NH 2
+Vi and ex
+.PP
+.I Vi
+is actually one mode of editing within the editor
+.I ex.
+When you are running
+.I vi
+you can escape to the line oriented editor of
+.I ex
+by giving the command
+\fBQ\fR.
+All of the
+.B :
+commands which were introduced above are available in
+.I ex.
+Likewise, most
+.I ex
+commands can be invoked from
+.I vi
+using :.
+Just give them without the \fB:\fR and follow them with a \s-2CR\s0.
+.PP
+In rare instances, an internal error may occur in
+.I vi.
+In this case you will get a diagnostic and be left in the command mode of
+.I ex.
+You can then save your work and quit if you wish by giving a command
+\fBx\fR after the \fB:\fR which \fIex\fR prompts you with, or you can
+reenter \fIvi\fR by giving
+.I ex
+a
+.I vi
+command.
+.PP
+There are a number of things which you can do more easily in
+.I ex
+than in
+.I vi.
+Systematic changes in line oriented material are particularly easy.
+You can read the advanced editing documents for the editor
+.I ed
+to find out a lot more about this style of editing.
+Experienced
+users often mix their use of
+.I ex
+command mode and
+.I vi
+command mode to speed the work they are doing.
+.NH 2
+Open mode: vi on hardcopy terminals and ``glass tty's''
+\(dd
+.PP
+If you are on a hardcopy terminal or a terminal which does not have a cursor
+which can move off the bottom line, you can still use the command set of
+.I vi,
+but in a different mode.
+When you give a
+.I vi
+command, the editor will tell you that it is using
+.I open
+mode.
+This name comes from the
+.I open
+command in
+.I ex,
+which is used to get into the same mode.
+.PP
+The only difference between
+.I visual
+mode
+and
+.I open
+mode is the way in which the text is displayed.
+.PP
+In
+.I open
+mode the editor uses a single line window into the file, and moving backward
+and forward in the file causes new lines to be displayed, always below the
+current line.
+Two commands of
+.I vi
+work differently in
+.I open:
+.B z
+and
+\fB^R\fR.
+The
+.B z
+command does not take parameters, but rather draws a window of context around
+the current line and then returns you to the current line.
+.PP
+If you are on a hardcopy terminal,
+the
+.B ^R
+command will retype the current line.
+On such terminals, the editor normally uses two lines to represent the
+current line.
+The first line is a copy of the line as you started to edit it, and you work
+on the line below this line.
+When you delete characters, the editor types a number of \e's to show
+you the characters which are deleted. The editor also reprints the current
+line soon after such changes so that you can see what the line looks
+like again.
+.PP
+It is sometimes useful to use this mode on very slow terminals which
+can support
+.I vi
+in the full screen mode.
+You can do this by entering
+.I ex
+and using an
+.I open
+command.
+.LP
+.SH
+Acknowledgements
+.PP
+Bruce Englar encouraged the early development of this display editor.
+Peter Kessler helped bring sanity to version 2's command layout.
+Bill Joy wrote versions 1 and 2.0 through 2.7,
+and created the framework that users see in the present editor.
+Mark Horton added macros and other features and made the
+editor work on a large number of terminals and Unix systems.
diff --git a/usr.bin/vi/USD.doc/vitut/vi.summary b/usr.bin/vi/USD.doc/vitut/vi.summary
new file mode 100644
index 0000000..a7d9938
--- /dev/null
+++ b/usr.bin/vi/USD.doc/vitut/vi.summary
@@ -0,0 +1,468 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vi.summary 8.1 (Berkeley) 6/8/93
+.\"
+.ds CH
+.ds CF
+.de TS
+.br
+.if !\\n(1T .RT
+.ul 0
+.ti \\n(.iu
+.if t .sp 0.25
+.if n .sp
+.if \\$1H .TQ
+.nr IX 1
+..
+.nr PS 9
+.ps 9
+.nr VS 11
+.vs 11
+.nr HM .50i
+.nr FM .25i
+.nr PO 0
+.po 0
+.nr LL 3.5i
+.ll 3.5i
+.de nc
+.bp
+..
+.de h
+.LG
+.B
+\\$1
+.R
+.NL
+..
+.LG
+.LG
+.B
+.ce
+Ex Quick Reference
+.R
+.NL
+.LP
+.LP
+.h "Entering/leaving ex"
+.TS
+aw(1.4i)b aw(1.8i).
+% ex \fIname\fP edit \fIname\fP, start at end
+% ex +\fIn\fP \fIname\fP ... at line \fIn\fP
+% ex \-t \fItag\fP start at \fItag\fP
+% ex \-r list saved files
+% ex \-r \fIname\fP recover file \fIname\fP
+% ex \fIname\fP ... edit first; rest via \fB:n\fP
+% ex \-R \fIname\fP read only mode
+: x exit, saving changes
+: q! exit, discarding changes
+.TE
+.h "Ex states"
+.TS
+lw(1i) lw(2.0i).
+Command T{
+Normal and initial state. Input prompted for by \fB:\fP.
+Your kill character cancels partial command.
+T}
+Insert T{
+Entered by \fBa\fP \fBi\fP and \fBc\fP.
+Arbitrary text then terminates with line having only \fB.\fP
+character on it or abnormally with interrupt.
+T}
+Open/visual T{
+Entered by \fBopen\fP or \fBvi\fP, terminates with \fBQ\fP
+or ^\e.
+T}
+.TE
+.h "Ex commands"
+.TS
+lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b lw(.45i) lw(.08i)b.
+abbrev ab next n unabbrev una
+append a number nu undo u
+args ar open o unmap unm
+change c preserve pre version ve
+copy co print p visual vi
+delete d put pu write w
+edit e quit q xit x
+file f read re yank ya
+global g recover rec \fIwindow\fP z
+insert i rewind rew \fIescape\fP !
+join j set se \fIlshift\fP <
+list l shell sh \fIprint next\fP \fRCR\fP
+map source so \fIresubst\fP &
+mark ma stop st \fIrshift\fP >
+move m substitute s \fIscroll\fP ^D
+.TE
+.h "Ex command addresses"
+.TS
+lw(.3i)b lw(0.8i) lw(.3i)b lw(0.8i).
+\fIn\fP line \fIn\fP /\fIpat\fP next with \fIpat\fP
+\&. current ?\fIpat\fP previous with \fIpat\fP
+$ last \fIx\fP-\fIn\fP \fIn\fP before \fIx\fP
++ next \fIx\fP,\fIy\fP \fIx\fP through \fIy\fP
+\- previous \(aa\fIx\fP marked with \fIx\fP
++\fIn\fP \fIn\fP forward \(aa\(aa previous context
+% 1,$
+.TE
+.nc
+.h "Specifying terminal type"
+.TS
+aw(1.7i)b aw(1.5i).
+% setenv TERM \fItype\fP \fIcsh\fP and all version 6
+$ TERM=\fItype\fP; export TERM \fIsh\fP in Version 7
+See also \fItset\fR(1)
+.TE
+.h "Some terminal types"
+.TS
+lw(.4i) lw(.4i) lw(.4i) lw(.4i) lw(.4i).
+2621 43 adm31 dw1 h19
+2645 733 adm3a dw2 i100
+300s 745 c100 gt40 mime
+33 act4 dm1520 gt42 owl
+37 act5 dm2500 h1500 t1061
+4014 adm3 dm3025 h1510 vt52
+.TE
+.h "Initializing options"
+.TS
+lw(.9i)b aw(1.5i).
+EXINIT place \fBset\fP's here in environment var.
+set \fIx\fP enable option
+set no\fIx\fP disable option
+set \fIx\fP=\fIval\fP give value \fIval\fP
+set show changed options
+set all show all options
+set \fIx\fP? show value of option \fIx\fP
+.TE
+.h "Useful options"
+.TS
+lw(.9i)b lw(.3i) lw(1.0i).
+autoindent ai supply indent
+autowrite aw write before changing files
+ignorecase ic in scanning
+lisp \fB( ) { }\fP are s-exp's
+list print ^I for tab, $ at end
+magic \fB. [ *\fP special in patterns
+number nu number lines
+paragraphs para macro names which start ...
+redraw simulate smart terminal
+scroll command mode lines
+sections sect macro names ...
+shiftwidth sw for \fB< >\fP, and input \fB^D\fP
+showmatch sm to \fB)\fP and \fB}\fP as typed
+slowopen slow choke updates during insert
+window visual mode lines
+wrapscan ws around end of buffer?
+wrapmargin wm automatic line splitting
+.TE
+.LP
+.h "Scanning pattern formation"
+.TS
+aw(.9i)b aw(1.0i).
+\(ua beginning of line
+$ end of line
+\fB.\fR any character
+\e< beginning of word
+\e> end of word
+[\fIstr\fP] any char in \fIstr\fP
+[\(ua\fIstr\fP] ... not in \fIstr\fP
+[\fIx\-y\fP] ... between \fIx\fP and \fIy\fP
+* any number of preceding
+.TE
+.nc
+.LP
+.LG
+.LG
+.B
+.ce
+Vi Quick Reference
+.NL
+.R
+.LP
+.LP
+.h "Entering/leaving vi"
+.TS
+aw(1.4i)b aw(1.8i).
+% vi \fIname\fP edit \fIname\fP at top
+% vi +\fIn\fP \fIname\fP ... at line \fIn\fP
+% vi + \fIname\fP ... at end
+% vi \-r list saved files
+% vi \-r \fIname\fP recover file \fIname\fP
+% vi \fIname\fP ... edit first; rest via \fB:n\fP
+% vi \-t \fItag\fP start at \fItag\fP
+% vi +/\fIpat\fP \fIname\fP search for \fIpat\fP
+% view \fIname\fP read only mode
+ZZ exit from vi, saving changes
+^Z stop vi for later resumption
+.TE
+.h "The display"
+.TS
+lw(.75i) lw(2.2i).
+Last line T{
+Error messages, echoing input to \fB: / ?\fP and \fB!\fR,
+feedback about i/o and large changes.
+T}
+@ lines On screen only, not in file.
+~ lines Lines past end of file.
+^\fIx\fP Control characters, ^? is delete.
+tabs Expand to spaces, cursor at last.
+.TE
+.LP
+.h "Vi states"
+.TS
+lw(.75i) lw(2.2i).
+Command T{
+Normal and initial state. Others return here.
+ESC (escape) cancels partial command.
+T}
+Insert T{
+Entered by \fBa i A I o O c C s S\fP \fBR\fP.
+Arbitrary text then terminates with ESC character,
+or abnormally with interrupt.
+T}
+Last line T{
+Reading input for \fB: / ?\fP or \fB!\fP; terminate
+with ESC or CR to execute, interrupt to cancel.
+T}
+.TE
+.h "Counts before vi commands"
+.TS
+lw(1.5i) lw(1.7i)b.
+line/column number z G |
+scroll amount ^D ^U
+replicate insert a i A I
+repeat effect \fRmost rest\fP
+.TE
+.h "Simple commands"
+.TS
+lw(1.5i)b lw(1.7i).
+dw delete a word
+de ... leaving punctuation
+dd delete a line
+3dd ... 3 lines
+i\fItext\fP\fRESC\fP insert text \fIabc\fP
+cw\fInew\fP\fRESC\fP change word to \fInew\fP
+ea\fIs\fP\fRESC\fP pluralize word
+xp transpose characters
+.TE
+.nc
+.h "Interrupting, cancelling"
+.TS
+aw(0.75i)b aw(1.6i).
+ESC end insert or incomplete cmd
+^? (delete or rubout) interrupts
+^L reprint screen if \fB^?\fR scrambles it
+.TE
+.h "File manipulation"
+.TS
+aw(0.75i)b aw(1.6i).
+:w write back changes
+:wq write and quit
+:q quit
+:q! quit, discard changes
+:e \fIname\fP edit file \fIname\fP
+:e! reedit, discard changes
+:e + \fIname\fP edit, starting at end
+:e +\fIn\fR edit starting at line \fIn\fR
+:e # edit alternate file
+^\(ua synonym for \fB:e #\fP
+:w \fIname\fP write file \fIname\fP
+:w! \fIname\fP overwrite file \fIname\fP
+:sh run shell, then return
+:!\fIcmd\fP run \fIcmd\fR, then return
+:n edit next file in arglist
+:n \fIargs\fP specify new arglist
+:f show current file and line
+^G synonym for \fB:f\fP
+:ta \fItag\fP to tag file entry \fItag\fP
+^] \fB:ta\fP, following word is \fItag\fP
+.TE
+.h "Positioning within file"
+.TS
+aw(0.75i)b aw(1.6i).
+^F forward screenfull
+^B backward screenfull
+^D scroll down half screen
+^U scroll up half screen
+G goto line (end default)
+/\fIpat\fR next line matching \fIpat\fR
+?\fIpat\fR prev line matching \fIpat\fR
+n repeat last \fB/\fR or \fB?\fR
+N reverse last \fB/\fR or \fB?\fR
+/\fIpat\fP/+\fIn\fP n'th line after \fIpat\fR
+?\fIpat\fP?\-\fIn\fP n'th line before \fIpat\fR
+]] next section/function
+[[ previous section/function
+% find matching \fB( ) {\fP or \fB}\fP
+.TE
+.h "Adjusting the screen"
+.TS
+aw(0.75i)b aw(1.6i).
+^L clear and redraw
+^R retype, eliminate @ lines
+z\fRCR\fP redraw, current at window top
+z\- ... at bottom
+z\|. ... at center
+/\fIpat\fP/z\- \fIpat\fP line at bottom
+z\fIn\fP\|. use \fIn\fP line window
+^E scroll window down 1 line
+^Y scroll window up 1 line
+.TE
+.nc
+.h "Marking and returning
+.TS
+aw(0.5i)b aw(2.0i).
+\(ga\(ga previous context
+\(aa\(aa ... at first non-white in line
+m\fIx\fP mark position with letter \fIx\fP
+\(ga\fIx\fP to mark \fIx\fP
+\(aa\fIx\fP ... at first non-white in line
+.TE
+.h "Line positioning"
+.TS
+aw(0.5i)b aw(2.0i).
+H home window line
+L last window line
+M middle window line
++ next line, at first non-white
+\- previous line, at first non-white
+\fRCR\fP return, same as +
+\(da \fRor\fP j next line, same column
+\(ua \fRor\fP k previous line, same column
+.TE
+.h "Character positioning"
+.TS
+aw(0.5i)b aw(2.0i).
+\(ua first non white
+0 beginning of line
+$ end of line
+h \fRor\fP \(-> forward
+l \fRor\fP \(<- backwards
+^H same as \fB\(<-\fP
+\fRspace\fP same as \fB\(->\fP
+f\fIx\fP find \fIx\fP forward
+F\fIx\fP \fBf\fR backward
+t\fIx\fP upto \fIx\fP forward
+T\fIx\fP back upto \fIx\fP
+; repeat last \fBf F t\fP or \fBT\fP
+, inverse of \fB;\fP
+| to specified column
+% find matching \fB( { )\fP or \fB}\fR
+.TE
+.h "Words, sentences, paragraphs"
+.TS
+aw(0.5i)b aw(2.0i).
+w word forward
+b back word
+e end of word
+) to next sentence
+} to next paragraph
+( back sentence
+{ back paragraph
+W blank delimited word
+B back \fBW\fP
+E to end of \fBW\fP
+.TE
+.h "Commands for \s-2LISP\s0"
+.TS
+aw(0.5i)b aw(2.0i).
+) Forward s-expression
+} ... but don't stop at atoms
+( Back s-expression
+{ ... but don't stop at atoms
+.TE
+.nc
+.h "Corrections during insert"
+.TS
+aw(.5i)b aw(2.0i).
+^H erase last character
+^W erases last word
+\fRerase\fP your erase, same as \fB^H\fP
+\fRkill\fP your kill, erase input this line
+\e escapes \fB^H\fR, your erase and kill
+\fRESC\fP ends insertion, back to command
+^? interrupt, terminates insert
+^D backtab over \fIautoindent\fP
+\(ua^D kill \fIautoindent\fP, save for next
+0^D ... but at margin next also
+^V quote non-printing character
+.TE
+.h "Insert and replace"
+.TS
+aw(.5i)b aw(2.0i).
+a append after cursor
+i insert before
+A append at end of line
+I insert before first non-blank
+o open line below
+O open above
+r\fIx\fP replace single char with \fIx\fP
+R replace characters
+.TE
+.h "Operators (double to affect lines)"
+.TS
+aw(0.5i)b aw(2.0i).
+d delete
+c change
+< left shift
+> right shift
+! filter through command
+\&\= indent for \s-2LISP\s0
+y yank lines to buffer
+.TE
+.h "Miscellaneous operations"
+.TS
+aw(0.5i)b aw(2.0i).
+C change rest of line
+D delete rest of line
+s substitute chars
+S substitute lines
+J join lines
+x delete characters
+X ... before cursor
+Y yank lines
+.TE
+.h "Yank and put"
+.TS
+aw(0.5i)b aw(2.0i).
+p put back lines
+P put before
+"\fIx\fPp put from buffer \fIx\fP
+"\fIx\fPy yank to buffer \fIx\fP
+"\fIx\fPd delete into buffer \fIx\fP
+.TE
+.h "Undo, redo, retrieve"
+.TS
+aw(0.5i)b aw(2.0i).
+u undo last change
+U restore current line
+\fB.\fP repeat last change
+"\fId\fP\|p retrieve \fId\fP'th last delete
+.TE
diff --git a/usr.bin/vi/common/Makefile b/usr.bin/vi/common/Makefile
new file mode 100644
index 0000000..3a28b44
--- /dev/null
+++ b/usr.bin/vi/common/Makefile
@@ -0,0 +1,105 @@
+# @(#)Makefile 8.51 (Berkeley) 8/17/94
+
+VI= nvi
+EX= nex
+VIEW= nview
+PROG= nvi
+LINKS= ${BINDIR}/${VI} ${BINDIR}/${EX} ${BINDIR}/${VI} ${BINDIR}/${VIEW}
+LINKS+= ${BINDIR}/${VI} ${BINDIR}/vi ${BINDIR}/${EX} ${BINDIR}/ex
+LINKS+= ${BINDIR}/${VI} ${BINDIR}/view
+MAN1= ${.CURDIR}/../USD.doc/vi.man/vi.1
+
+CFLAGS+=-I. -I${.CURDIR}
+DPADD+= ${LIBCURSES} ${LIBTERMCAP} ${LIBUTIL}
+LDADD+= -lcurses -ltermcap -lutil
+
+.PATH: ${.CURDIR}/../common ${.CURDIR}/../ex ${.CURDIR}/../sex \
+ ${.CURDIR}/../vi ${.CURDIR}/../svi ${.CURDIR}/../xaw
+
+SPECHDR=compat.h excmd.h options.h
+
+CLEANFILES+=${SPECHDR} ${EX}
+
+# General sources.
+SRCS= cut.c delete.c exf.c line.c log.c main.c mark.c msg.c options.c \
+ options_f.c put.c screen.c search.c seq.c signal.c recover.c \
+ term.c trace.c util.c ${SPECHDR}
+
+# Ex source.
+SRCS+= ex.c ex_abbrev.c ex_append.c ex_args.c ex_argv.c ex_at.c ex_bang.c \
+ ex_cd.c ex_delete.c ex_digraph.c ex_display.c ex_edit.c ex_equal.c \
+ ex_exit.c ex_file.c ex_global.c ex_init.c ex_join.c ex_map.c \
+ ex_mark.c ex_mkexrc.c ex_move.c ex_open.c ex_preserve.c ex_print.c \
+ ex_put.c ex_read.c ex_screen.c ex_script.c ex_set.c ex_shell.c \
+ ex_shift.c ex_source.c ex_stop.c ex_subst.c ex_tag.c ex_undo.c \
+ ex_usage.c ex_util.c ex_version.c ex_visual.c ex_write.c ex_yank.c \
+ ex_z.c excmd.c filter.c
+
+# Ex screen source.
+SRCS+= sex_confirm.c sex_get.c sex_refresh.c sex_screen.c sex_term.c \
+ sex_util.c sex_window.c
+
+# Vi source.
+SRCS+= getc.c v_ch.c v_delete.c v_ex.c v_increment.c v_init.c v_left.c \
+ v_mark.c v_match.c v_ntext.c v_paragraph.c v_put.c v_redraw.c \
+ v_replace.c v_right.c v_screen.c v_scroll.c v_search.c v_section.c \
+ v_sentence.c v_status.c v_stop.c v_text.c v_ulcase.c v_undo.c \
+ v_util.c v_word.c v_xchar.c v_yank.c v_z.c v_zexit.c vcmd.c vi.c
+
+# Vi curses screen source.
+SRCS+= svi_confirm.c svi_curses.c svi_ex.c svi_get.c svi_line.c \
+ svi_refresh.c svi_relative.c svi_screen.c svi_smap.c svi_split.c \
+ svi_term.c svi_util.c
+
+# Athena widget set screen source.
+SRCS+= xaw_screen.c
+
+all: ${VI} ${EX}
+
+all: nvi nex
+nex: nvi
+ rm -f nex
+ ln nvi nex
+
+${EX}: ${VI}
+ rm -f ${EX}
+ ln ${VI} ${EX}
+
+compat.h:
+ :> compat.h
+
+excmd.h: excmd.h.stub excmd.c excmd.awk
+ rm -f excmd.h
+ cat ${.CURDIR}/../ex/excmd.h.stub > excmd.h
+ awk -f ${.CURDIR}/../ex/excmd.awk ${.CURDIR}/../ex/excmd.c >> excmd.h
+
+options.h: options.h.stub options.c options.awk
+ rm -f options.h
+ cat ${.CURDIR}/options.h.stub > options.h
+ awk -f ${.CURDIR}/options.awk ${.CURDIR}/options.c >> options.h
+
+excmd.h: excmd.h.stub excmd.c excmd.awk
+ rm -f excmd.h
+ cat ${.CURDIR}/../ex/excmd.h.stub > excmd.h
+ awk -f ${.CURDIR}/../ex/excmd.awk ${.CURDIR}/../ex/excmd.c >> excmd.h
+
+tags::
+ -(cd ${.CURDIR} && rm -f tags && \
+ ctags ../common/*.[ch] ../common/*.stub ../ex/*.[ch] ../ex/*.stub \
+ ../vi/*.[ch] ../sex/*.[ch] ../svi/*.[ch] ../xaw/*.[ch])
+
+warn:: ${SRCS}
+ -(cd ${.CURDIR} && gcc -Wall -O4 -DDEBUG \
+ -Iobj -I. ${.ALLSRC} -lcurses -ltermcap 2>&1 | \
+ sed -e "/warning: .*sccsid.*defined but not used/d" \
+ -e "/warning: suggest parentheses around/d" \
+ -e "/In function /d" \
+ -e "/At top level:/d" \
+ -e "/warning: .*inline call to/d" \
+ -e "/warning: comparison is always 1 due /d") > \
+ ${.CURDIR}/WARN.OUT
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
+
+.depend: ${SPECHDR}
diff --git a/usr.bin/vi/common/args.h b/usr.bin/vi/common/args.h
new file mode 100644
index 0000000..5e127de
--- /dev/null
+++ b/usr.bin/vi/common/args.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)args.h 8.5 (Berkeley) 7/17/94
+ */
+
+/*
+ * Structure for building "argc/argv" vector of arguments.
+ *
+ * !!!
+ * All arguments are nul terminated as well as having an associated length.
+ * The argument vector is NOT necessarily NULL terminated. The proper way
+ * to check the number of arguments is to use the argc value in the EXCMDARG
+ * structure or to walk the array until an ARGS structure with a length of 0
+ * is found.
+ */
+typedef struct _args {
+ CHAR_T *bp; /* Argument. */
+ size_t blen; /* Buffer length. */
+ size_t len; /* Argument length. */
+
+#define A_ALLOCATED 0x01 /* If allocated space. */
+ u_int8_t flags;
+} ARGS;
diff --git a/usr.bin/vi/common/cut.c b/usr.bin/vi/common/cut.c
new file mode 100644
index 0000000..5700990
--- /dev/null
+++ b/usr.bin/vi/common/cut.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cut.c 8.34 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+static int cb_rotate __P((SCR *));
+
+/*
+ * cut --
+ * Put a range of lines/columns into a TEXT buffer.
+ *
+ * There are two buffer areas, both found in the global structure. The first
+ * is the linked list of all the buffers the user has named, the second is the
+ * unnamed buffer storage. There is a pointer, too, which is the current
+ * default buffer, i.e. it may point to the unnamed buffer or a named buffer
+ * depending on into what buffer the last text was cut. Logically, in both
+ * delete and yank operations, if the user names a buffer, the text is cut
+ * into it. If it's a delete of information on more than a single line, the
+ * contents of the numbered buffers are rotated up one, the contents of the
+ * buffer named '9' are discarded, and the text is cut into the buffer named
+ * '1'. The text is always cut into the unnamed buffer.
+ *
+ * In all cases, upper-case buffer names are the same as lower-case names,
+ * with the exception that they cause the buffer to be appended to instead
+ * of replaced. Note, however, that if text is appended to a buffer, the
+ * default buffer only contains the appended text, not the entire contents
+ * of the buffer.
+ *
+ * !!!
+ * The contents of the default buffer would disappear after most operations
+ * in historic vi. It's unclear that this is useful, so we don't bother.
+ *
+ * When users explicitly cut text into the numeric buffers, historic vi became
+ * genuinely strange. I've never been able to figure out what was supposed to
+ * happen. It behaved differently if you deleted text than if you yanked text,
+ * and, in the latter case, the text was appended to the buffer instead of
+ * replacing the contents. Hopefully it's not worth getting right, and here
+ * we just treat the numeric buffers like any other named buffer.
+ */
+int
+cut(sp, ep, namep, fm, tm, flags)
+ SCR *sp;
+ EXF *ep;
+ CHAR_T *namep;
+ int flags;
+ MARK *fm, *tm;
+{
+ CB *cbp;
+ CHAR_T name;
+ recno_t lno;
+ int append, copy_one, copy_def;
+
+ /*
+ * If the user specified a buffer, put it there. (This may require
+ * a copy into the numeric buffers. We do the copy so that we don't
+ * have to reference count and so we don't have to deal with things
+ * like appends to buffers that are used multiple times.)
+ *
+ * Otherwise, if it's supposed to be put in a numeric buffer (usually
+ * a delete) put it there. The rules for putting things in numeric
+ * buffers were historically a little strange. There were three cases.
+ *
+ * 1: Some motions are always line mode motions, which means
+ * that the cut always goes into the numeric buffers.
+ * 2: Some motions aren't line mode motions, e.g. d10w, but
+ * can cross line boundaries. For these commands, if the
+ * cut crosses a line boundary, it goes into the numeric
+ * buffers. This includes most of the commands.
+ * 3: Some motions aren't line mode motions, e.g. d`<char>,
+ * but always go into the numeric buffers, regardless. This
+ * was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
+ *
+ * Otherwise, put it in the unnamed buffer.
+ */
+ append = copy_one = copy_def = 0;
+ if (namep != NULL) {
+ name = *namep;
+ if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
+ (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
+ copy_one = 1;
+ cb_rotate(sp);
+ }
+ if ((append = isupper(name)) == 1) {
+ if (!copy_one)
+ copy_def = 1;
+ name = tolower(name);
+ }
+namecb: CBNAME(sp, cbp, name);
+ } else if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
+ (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
+ name = '1';
+ cb_rotate(sp);
+ goto namecb;
+ } else
+ cbp = &sp->gp->dcb_store;
+
+copyloop:
+ /*
+ * If this is a new buffer, create it and add it into the list.
+ * Otherwise, if it's not an append, free its current contents.
+ */
+ if (cbp == NULL) {
+ CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB));
+ cbp->name = name;
+ CIRCLEQ_INIT(&cbp->textq);
+ LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
+ } else if (!append) {
+ text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ }
+
+
+#define ENTIRE_LINE 0
+ /* In line mode, it's pretty easy, just cut the lines. */
+ if (LF_ISSET(CUT_LINEMODE)) {
+ cbp->flags |= CB_LMODE;
+ for (lno = fm->lno; lno <= tm->lno; ++lno)
+ if (cut_line(sp, ep, lno, 0, 0, cbp))
+ goto cut_line_err;
+ } else {
+ /*
+ * Get the first line. A length of 0 causes cut_line
+ * to cut from the MARK to the end of the line.
+ */
+ if (cut_line(sp, ep, fm->lno, fm->cno, fm->lno != tm->lno ?
+ ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp))
+ goto cut_line_err;
+
+ /* Get the intermediate lines. */
+ for (lno = fm->lno; ++lno < tm->lno;)
+ if (cut_line(sp, ep, lno, 0, ENTIRE_LINE, cbp))
+ goto cut_line_err;
+
+ /* Get the last line. */
+ if (tm->lno != fm->lno &&
+ cut_line(sp, ep, lno, 0, tm->cno + 1, cbp)) {
+cut_line_err: text_lfree(&cbp->textq);
+ cbp->len = 0;
+ cbp->flags = 0;
+ return (1);
+ }
+ }
+
+ append = 0; /* Only append to the named buffer. */
+ sp->gp->dcbp = cbp; /* Repoint the default buffer on each pass. */
+
+ if (copy_one) { /* Copy into numeric buffer 1. */
+ name = '1';
+ CBNAME(sp, cbp, name);
+ copy_one = 0;
+ goto copyloop;
+ }
+ if (copy_def) { /* Copy into the default buffer. */
+ cbp = &sp->gp->dcb_store;
+ copy_def = 0;
+ goto copyloop;
+ }
+ return (0);
+}
+
+/*
+ * cb_rotate --
+ * Rotate the numbered buffers up one.
+ */
+static int
+cb_rotate(sp)
+ SCR *sp;
+{
+ CB *cbp, *del_cbp;
+
+ del_cbp = NULL;
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next)
+ switch(cbp->name) {
+ case '1':
+ cbp->name = '2';
+ break;
+ case '2':
+ cbp->name = '3';
+ break;
+ case '3':
+ cbp->name = '4';
+ break;
+ case '4':
+ cbp->name = '5';
+ break;
+ case '5':
+ cbp->name = '6';
+ break;
+ case '6':
+ cbp->name = '7';
+ break;
+ case '7':
+ cbp->name = '8';
+ break;
+ case '8':
+ cbp->name = '9';
+ break;
+ case '9':
+ del_cbp = cbp;
+ break;
+ }
+ if (del_cbp != NULL) {
+ LIST_REMOVE(del_cbp, q);
+ text_lfree(&del_cbp->textq);
+ FREE(del_cbp, sizeof(CB));
+ }
+ return (0);
+}
+
+/*
+ * cut_line --
+ * Cut a portion of a single line.
+ */
+int
+cut_line(sp, ep, lno, fcno, clen, cbp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t fcno, clen;
+ CB *cbp;
+{
+ TEXT *tp;
+ size_t len;
+ char *p;
+
+ /* Get the line. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ /* Create a TEXT structure that can hold the entire line. */
+ if ((tp = text_init(sp, NULL, 0, len)) == NULL)
+ return (1);
+
+ /*
+ * If the line isn't empty and it's not the entire line,
+ * copy the portion we want, and reset the TEXT length.
+ */
+ if (len != 0) {
+ if (clen == 0)
+ clen = len - fcno;
+ memmove(tp->lb, p + fcno, clen);
+ tp->len = clen;
+ }
+
+ /* Append to the end of the cut buffer. */
+ CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
+ cbp->len += tp->len;
+
+ return (0);
+}
+
+/*
+ * text_init --
+ * Allocate a new TEXT structure.
+ */
+TEXT *
+text_init(sp, p, len, total_len)
+ SCR *sp;
+ const char *p;
+ size_t len, total_len;
+{
+ TEXT *tp;
+
+ CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT));
+ if (tp == NULL)
+ return (NULL);
+ /* ANSI C doesn't define a call to malloc(2) for 0 bytes. */
+ if ((tp->lb_len = total_len) != 0) {
+ MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
+ if (tp->lb == NULL) {
+ free(tp);
+ return (NULL);
+ }
+ if (p != NULL && len != 0)
+ memmove(tp->lb, p, len);
+ }
+ tp->len = len;
+ return (tp);
+}
+
+/*
+ * text_lfree --
+ * Free a chain of text structures.
+ */
+void
+text_lfree(headp)
+ TEXTH *headp;
+{
+ TEXT *tp;
+
+ while ((tp = headp->cqh_first) != (void *)headp) {
+ CIRCLEQ_REMOVE(headp, tp, q);
+ text_free(tp);
+ }
+}
+
+/*
+ * text_free --
+ * Free a text structure.
+ */
+void
+text_free(tp)
+ TEXT *tp;
+{
+ if (tp->lb != NULL)
+ FREE(tp->lb, tp->lb_len);
+ if (tp->wd != NULL)
+ FREE(tp->wd, tp->wd_len);
+ FREE(tp, sizeof(TEXT));
+}
diff --git a/usr.bin/vi/common/cut.h b/usr.bin/vi/common/cut.h
new file mode 100644
index 0000000..3113411
--- /dev/null
+++ b/usr.bin/vi/common/cut.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cut.h 8.19 (Berkeley) 7/28/94
+ */
+
+typedef struct _texth TEXTH; /* TEXT list head structure. */
+CIRCLEQ_HEAD(_texth, _text);
+
+/* Cut buffers. */
+struct _cb {
+ LIST_ENTRY(_cb) q; /* Linked list of cut buffers. */
+ TEXTH textq; /* Linked list of TEXT structures. */
+ CHAR_T name; /* Cut buffer name. */
+ size_t len; /* Total length of cut text. */
+
+#define CB_LMODE 0x01 /* Cut was in line mode. */
+ u_int8_t flags;
+};
+
+/* Lines/blocks of text. */
+struct _text { /* Text: a linked list of lines. */
+ CIRCLEQ_ENTRY(_text) q; /* Linked list of text structures. */
+ char *lb; /* Line buffer. */
+ size_t lb_len; /* Line buffer length. */
+ size_t len; /* Line length. */
+
+ /* These fields are used by the vi text input routine. */
+ recno_t lno; /* 1-N: line number. */
+ size_t ai; /* 0-N: autoindent bytes. */
+ size_t insert; /* 0-N: bytes to insert (push). */
+ size_t offset; /* 0-N: initial, unerasable chars. */
+ size_t owrite; /* 0-N: chars to overwrite. */
+ size_t R_erase; /* 0-N: 'R' erase count. */
+ size_t sv_cno; /* 0-N: Saved line cursor. */
+ size_t sv_len; /* 0-N: Saved line length. */
+
+ /* These fields are used by the ex text input routine. */
+ u_char *wd; /* Width buffer. */
+ size_t wd_len; /* Width buffer length. */
+};
+
+/*
+ * Get named buffer 'name'.
+ * Translate upper-case buffer names to lower-case buffer names.
+ */
+#define CBNAME(sp, cbp, nch) { \
+ CHAR_T __name; \
+ __name = isupper(nch) ? tolower(nch) : (nch); \
+ for (cbp = sp->gp->cutq.lh_first; \
+ cbp != NULL; cbp = cbp->q.le_next) \
+ if (cbp->name == __name) \
+ break; \
+}
+
+#define CUT_LINEMODE 0x01 /* Cut in line mode. */
+#define CUT_NUMOPT 0x02 /* Numeric buffer: optional. */
+#define CUT_NUMREQ 0x04 /* Numeric buffer: required. */
+int cut __P((SCR *, EXF *, CHAR_T *, MARK *, MARK *, int));
+
+int cut_line __P((SCR *, EXF *, recno_t, size_t, size_t, CB *));
+int delete __P((SCR *, EXF *, MARK *, MARK *, int));
+int put __P((SCR *, EXF *, CB *, CHAR_T *, MARK *, MARK *, int));
+
+void text_free __P((TEXT *));
+TEXT *text_init __P((SCR *, const char *, size_t, size_t));
+void text_lfree __P((TEXTH *));
diff --git a/usr.bin/vi/common/delete.c b/usr.bin/vi/common/delete.c
new file mode 100644
index 0000000..85eae5f
--- /dev/null
+++ b/usr.bin/vi/common/delete.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)delete.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * delete --
+ * Delete a range of text.
+ */
+int
+delete(sp, ep, fm, tm, lmode)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm;
+ int lmode;
+{
+ recno_t lno;
+ size_t blen, len, nlen, tlen;
+ char *bp, *p;
+ int eof;
+
+ bp = NULL;
+
+ /* Case 1 -- delete in line mode. */
+ if (lmode) {
+ for (lno = tm->lno; lno >= fm->lno; --lno)
+ if (file_dline(sp, ep, lno))
+ return (1);
+ goto vdone;
+ }
+
+ /*
+ * Case 2 -- delete to EOF. This is a special case because it's
+ * easier to pick it off than try and find it in the other cases.
+ */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (tm->lno >= lno) {
+ if (tm->lno == lno) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ eof = tm->cno >= len ? 1 : 0;
+ } else
+ eof = 1;
+ if (eof) {
+ for (lno = tm->lno; lno > fm->lno; --lno) {
+ if (file_dline(sp, ep, lno))
+ return (1);
+ ++sp->rptlines[L_DELETED];
+ }
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ GET_SPACE_RET(sp, bp, blen, fm->cno);
+ memmove(bp, p, fm->cno);
+ if (file_sline(sp, ep, fm->lno, bp, fm->cno))
+ return (1);
+ goto done;
+ }
+ }
+
+ /* Case 3 -- delete within a single line. */
+ if (tm->lno == fm->lno) {
+ if ((p = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ if (len != 0) {
+ GET_SPACE_RET(sp, bp, blen, len);
+ if (fm->cno != 0)
+ memmove(bp, p, fm->cno);
+ memmove(bp + fm->cno, p + (tm->cno + 1), len - (tm->cno + 1));
+ if (file_sline(sp, ep, fm->lno,
+ bp, len - ((tm->cno - fm->cno) + 1)))
+ goto err;
+ }
+ goto done;
+ }
+
+ /*
+ * Case 4 -- delete over multiple lines.
+ *
+ * Copy the start partial line into place.
+ */
+ if ((tlen = fm->cno) != 0) {
+ if ((p = file_gline(sp, ep, fm->lno, NULL)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ GET_SPACE_RET(sp, bp, blen, tlen + 256);
+ memmove(bp, p, tlen);
+ }
+
+ /* Copy the end partial line into place. */
+ if ((p = file_gline(sp, ep, tm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, tm->lno);
+ goto err;
+ }
+ if (len != 0 && tm->cno != len - 1) {
+ /*
+ * XXX
+ * We can overflow memory here, if the total length is greater
+ * than SIZE_T_MAX. The only portable way I've found to test
+ * is depending on the overflow being less than the value.
+ */
+ nlen = (len - (tm->cno + 1)) + tlen;
+ if (tlen > nlen) {
+ msgq(sp, M_ERR, "Error: line length overflow");
+ goto err;
+ }
+ if (tlen == 0) {
+ GET_SPACE_RET(sp, bp, blen, nlen);
+ } else
+ ADD_SPACE_RET(sp, bp, blen, nlen);
+
+ memmove(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
+ tlen += len - (tm->cno + 1);
+ }
+
+ /* Set the current line. */
+ if (file_sline(sp, ep, fm->lno, bp, tlen))
+ goto err;
+
+ /* Delete the last and intermediate lines. */
+ for (lno = tm->lno; lno > fm->lno; --lno)
+ if (file_dline(sp, ep, lno))
+ goto err;
+
+ /* Reporting. */
+vdone: sp->rptlines[L_DELETED] += tm->lno - fm->lno + 1;
+
+done: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+
+ return (0);
+
+ /* Free memory. */
+err: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+}
diff --git a/usr.bin/vi/common/exf.c b/usr.bin/vi/common/exf.c
new file mode 100644
index 0000000..10ff7f1
--- /dev/null
+++ b/usr.bin/vi/common/exf.c
@@ -0,0 +1,837 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)exf.c 8.97 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * We include <sys/file.h>, because the flock(2) and open(2) #defines
+ * were found there on historical systems. We also include <fcntl.h>
+ * because the open(2) #defines are found there on newer systems.
+ */
+#include <sys/file.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * file_add --
+ * Insert a file name into the FREF list, if it doesn't already
+ * appear in it.
+ *
+ * !!!
+ * The "if it doesn't already appear" changes vi's semantics slightly. If
+ * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
+ * will reflect the line/column of the previous edit session. Historic nvi
+ * did not do this. The change is a logical extension of the change where
+ * vi now remembers the last location in any file that it has ever edited,
+ * not just the previously edited file.
+ */
+FREF *
+file_add(sp, name)
+ SCR *sp;
+ CHAR_T *name;
+{
+ FREF *frp;
+
+ /*
+ * Return it if it already exists. Note that we test against the
+ * user's name, whatever that happens to be, including if it's a
+ * temporary file.
+ */
+ if (name != NULL)
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ if (!strcmp(frp->name, name))
+ return (frp);
+
+ /* Allocate and initialize the FREF structure. */
+ CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
+ if (frp == NULL)
+ return (NULL);
+
+ /*
+ * If no file name specified, or if the file name is a request
+ * for something temporary, file_init() will allocate the file
+ * name. Temporary files are always ignored.
+ */
+ if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
+ (frp->name = strdup(name)) == NULL) {
+ FREE(frp, sizeof(FREF));
+ msgq(sp, M_SYSERR, NULL);
+ return (NULL);
+ }
+
+ /* Append into the chain of file names. */
+ CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q);
+
+ return (frp);
+}
+
+/*
+ * file_init --
+ * Start editing a file, based on the FREF structure. If successsful,
+ * let go of any previous file. Don't release the previous file until
+ * absolutely sure we have the new one.
+ */
+int
+file_init(sp, frp, rcv_name, force)
+ SCR *sp;
+ FREF *frp;
+ char *rcv_name;
+ int force;
+{
+ EXF *ep;
+ RECNOINFO oinfo;
+ struct stat sb;
+ size_t psize;
+ int fd;
+ char *oname, tname[MAXPATHLEN];
+
+ /*
+ * If the file is a recovery file, let the recovery code handle it.
+ * Clear the FR_RECOVER flag first -- the recovery code does set up,
+ * and then calls us! If the recovery call fails, it's probably
+ * because the named file doesn't exist. So, move boldly forward,
+ * presuming that there's an error message the user will get to see.
+ */
+ if (F_ISSET(frp, FR_RECOVER)) {
+ F_CLR(frp, FR_RECOVER);
+ return (rcv_read(sp, frp));
+ }
+
+ /*
+ * Required FRP initialization; the only flag we keep is the
+ * cursor information.
+ */
+ F_CLR(frp, ~FR_CURSORSET);
+
+ /*
+ * Required EXF initialization:
+ * Flush the line caches.
+ * Default recover mail file fd to -1.
+ * Set initial EXF flag bits.
+ */
+ CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
+ ep->c_lno = ep->c_nlines = OOBLNO;
+ ep->rcv_fd = ep->fcntl_fd = -1;
+ LIST_INIT(&ep->marks);
+ F_SET(ep, F_FIRSTMODIFY);
+
+ /*
+ * If no name or backing file, create a backing temporary file, saving
+ * the temp file name so we can later unlink it. If the user never
+ * named this file, copy the temporary file name to the real name (we
+ * display that until the user renames it).
+ */
+ if ((oname = frp->name) == NULL || stat(oname, &sb)) {
+ (void)snprintf(tname,
+ sizeof(tname), "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
+ if ((fd = mkstemp(tname)) == -1) {
+ msgq(sp, M_SYSERR, "Temporary file");
+ goto err;
+ }
+ (void)close(fd);
+
+ if (frp->name == NULL)
+ F_SET(frp, FR_TMPFILE);
+ if ((frp->tname = strdup(tname)) == NULL ||
+ frp->name == NULL && (frp->name = strdup(tname)) == NULL) {
+ if (frp->tname != NULL)
+ free(frp->tname);
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(tname);
+ goto err;
+ }
+ oname = frp->tname;
+ psize = 4 * 1024;
+ F_SET(frp, FR_NEWFILE);
+ } else {
+ /*
+ * Try to keep it at 10 pages or less per file. This
+ * isn't friendly on a loaded machine, btw.
+ */
+ if (sb.st_size < 40 * 1024)
+ psize = 4 * 1024;
+ else if (sb.st_size < 320 * 1024)
+ psize = 32 * 1024;
+ else
+ psize = 64 * 1024;
+
+ ep->mtime = sb.st_mtime;
+
+ if (!S_ISREG(sb.st_mode))
+ msgq(sp, M_ERR,
+ "Warning: %s is not a regular file", oname);
+ }
+
+ /* Set up recovery. */
+ memset(&oinfo, 0, sizeof(RECNOINFO));
+ oinfo.bval = '\n'; /* Always set. */
+ oinfo.psize = psize;
+ oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
+ if (rcv_name == NULL) {
+ if (!rcv_tmp(sp, ep, frp->name))
+ oinfo.bfname = ep->rcv_path;
+ } else {
+ if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ }
+ oinfo.bfname = ep->rcv_path;
+ F_SET(ep, F_MODIFIED);
+ }
+
+ /* Open a db structure. */
+ if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
+ O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
+ msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
+ goto err;
+ }
+
+ /*
+ * Do the remaining things that can cause failure of the new file,
+ * mark and logging initialization.
+ */
+ if (mark_init(sp, ep) || log_init(sp, ep))
+ goto err;
+
+ /*
+ * Close the previous file; if that fails, close the new one and
+ * run for the border.
+ *
+ * !!!
+ * There's a nasty special case. If the user edits a temporary file,
+ * and then does an ":e! %", we need to re-initialize the backing
+ * file, but we can't change the name. (It's worse -- we're dealing
+ * with *names* here, we can't even detect that it happened.) Set a
+ * flag so that the file_end routine ignores the backing information
+ * of the old file if it happens to be the same as the new one.
+ *
+ * !!!
+ * Side-effect: after the call to file_end(), sp->frp may be NULL.
+ */
+ F_SET(frp, FR_DONTDELETE);
+ if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
+ (void)file_end(sp, ep, 1);
+ goto err;
+ }
+ F_CLR(frp, FR_DONTDELETE);
+
+ /*
+ * Lock the file; if it's a recovery file, it should already be
+ * locked. Note, we acquire the lock after the previous file
+ * has been ended, so that we don't get an "already locked" error
+ * for ":edit!".
+ *
+ * XXX
+ * While the user can't interrupt us between the open and here,
+ * there's a race between the dbopen() and the lock. Not much
+ * we can do about it.
+ *
+ * XXX
+ * We don't make a big deal of not being able to lock the file. As
+ * locking rarely works over NFS, and often fails if the file was
+ * mmap(2)'d, it's far too common to do anything like print an error
+ * message, let alone make the file readonly. At some future time,
+ * when locking is a little more reliable, this should change to be
+ * an error.
+ */
+ if (rcv_name == NULL)
+ switch (file_lock(oname,
+ &ep->fcntl_fd, ep->db->fd(ep->db), 0)) {
+ case LOCK_FAILED:
+ F_SET(frp, FR_UNLOCKED);
+ break;
+ case LOCK_UNAVAIL:
+ msgq(sp, M_INFO,
+ "%s already locked, session is read-only", oname);
+ F_SET(frp, FR_RDONLY);
+ break;
+ case LOCK_SUCCESS:
+ break;
+ }
+
+ /*
+ * The -R flag, or doing a "set readonly" during a session causes
+ * all files edited during the session (using an edit command, or
+ * even using tags) to be marked read-only. Changing the file name
+ * (see ex/ex_file.c), clears this flag.
+ *
+ * Otherwise, try and figure out if a file is readonly. This is a
+ * dangerous thing to do. The kernel is the only arbiter of whether
+ * or not a file is writeable, and the best that a user program can
+ * do is guess. Obvious loopholes are files that are on a file system
+ * mounted readonly (access catches this one on a few systems), or
+ * alternate protection mechanisms, ACL's for example, that we can't
+ * portably check. Lots of fun, and only here because users whined.
+ *
+ * !!!
+ * Historic vi displayed the readonly message if none of the file
+ * write bits were set, or if an an access(2) call on the path
+ * failed. This seems reasonable. If the file is mode 444, root
+ * users may want to know that the owner of the file did not expect
+ * it to be written.
+ *
+ * Historic vi set the readonly bit if no write bits were set for
+ * a file, even if the access call would have succeeded. This makes
+ * the superuser force the write even when vi expects that it will
+ * succeed. I'm less supportive of this semantic, but it's historic
+ * practice and the conservative approach to vi'ing files as root.
+ *
+ * It would be nice if there was some way to update this when the user
+ * does a "^Z; chmod ...". The problem is that we'd first have to
+ * distinguish between readonly bits set because of file permissions
+ * and those set for other reasons. That's not too hard, but deciding
+ * when to reevaluate the permissions is trickier. An alternative
+ * might be to turn off the readonly bit if the user forces a write
+ * and it succeeds.
+ *
+ * XXX
+ * Access(2) doesn't consider the effective uid/gid values. This
+ * probably isn't a problem for vi when it's running standalone.
+ */
+ if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
+ (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
+ access(frp->name, W_OK)))
+ F_SET(frp, FR_RDONLY);
+
+ /*
+ * Set the alternate file name to be the file we've just discarded.
+ *
+ * !!!
+ * If the current file was a temporary file, the call to file_end()
+ * unlinked it and free'd the name. So, there is no previous file,
+ * and there is no alternate file name. This matches historical
+ * practice, although in historical vi it could only happen as the
+ * result of the initial command, i.e. if vi was executed without a
+ * file name.
+ */
+ set_alt_name(sp, sp->frp == NULL ? NULL : sp->frp->name);
+
+ /*
+ * Switch...
+ *
+ * !!!
+ * Note, because the EXF structure is examine at interrupt time,
+ * the underlying DB structures have to be consistent as soon as
+ * it's assigned to an SCR structure.
+ */
+ ++ep->refcnt;
+ sp->ep = ep;
+ sp->frp = frp;
+ return (0);
+
+err: if (frp->name != NULL) {
+ free(frp->name);
+ frp->name = NULL;
+ }
+ if (frp->tname != NULL) {
+ (void)unlink(frp->tname);
+ free(frp->tname);
+ frp->tname = NULL;
+ }
+ if (ep->rcv_path != NULL) {
+ free(ep->rcv_path);
+ ep->rcv_path = NULL;
+ }
+ if (ep->db != NULL)
+ (void)ep->db->close(ep->db);
+ FREE(ep, sizeof(EXF));
+ return (1);
+}
+
+/*
+ * file_end --
+ * Stop editing a file.
+ */
+int
+file_end(sp, ep, force)
+ SCR *sp;
+ EXF *ep;
+ int force;
+{
+ FREF *frp;
+
+ /*
+ * Clean up the FREF structure.
+ *
+ * Save the cursor location.
+ *
+ * XXX
+ * It would be cleaner to do this somewhere else, but by the time
+ * ex or vi knows that we're changing files it's already happened.
+ */
+ frp = sp->frp;
+ frp->lno = sp->lno;
+ frp->cno = sp->cno;
+ F_SET(frp, FR_CURSORSET);
+
+ /*
+ * We may no longer need the temporary backing file, so clean it
+ * up. We don't need the FREF structure either, if the file was
+ * never named, so lose it.
+ *
+ * !!!
+ * Re: FR_DONTDELETE, see the comment above in file_init().
+ */
+ if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
+ if (unlink(frp->tname))
+ msgq(sp, M_SYSERR, "%s: remove", frp->tname);
+ free(frp->tname);
+ frp->tname = NULL;
+ if (F_ISSET(frp, FR_TMPFILE)) {
+ CIRCLEQ_REMOVE(&sp->frefq, frp, q);
+ free(frp->name);
+ free(frp);
+ }
+ sp->frp = NULL;
+ }
+
+ /*
+ * Clean up the EXF structure.
+ *
+ * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
+ *
+ * If multiply referenced, just decrement the count and return.
+ */
+ if (--ep->refcnt != 0)
+ return (0);
+
+ /* Close the db structure. */
+ if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
+ msgq(sp, M_ERR, "%s: close: %s", frp->name, strerror(errno));
+ ++ep->refcnt;
+ return (1);
+ }
+
+ /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
+
+ /* Stop logging. */
+ (void)log_end(sp, ep);
+
+ /* Free up any marks. */
+ (void)mark_end(sp, ep);
+
+ /*
+ * Delete recovery files, close the open descriptor, free recovery
+ * memory. See recover.c for a description of the protocol.
+ *
+ * XXX
+ * Unlink backup file first, we can detect that the recovery file
+ * doesn't reference anything when the user tries to recover it.
+ * There's a race, here, obviously, but it's fairly small.
+ */
+ if (!F_ISSET(ep, F_RCV_NORM)) {
+ if (ep->rcv_path != NULL && unlink(ep->rcv_path))
+ msgq(sp, M_ERR,
+ "%s: remove: %s", ep->rcv_path, strerror(errno));
+ if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
+ msgq(sp, M_ERR,
+ "%s: remove: %s", ep->rcv_mpath, strerror(errno));
+ }
+ if (ep->fcntl_fd != -1)
+ (void)close(ep->fcntl_fd);
+ if (ep->rcv_fd != -1)
+ (void)close(ep->rcv_fd);
+ if (ep->rcv_path != NULL)
+ free(ep->rcv_path);
+ if (ep->rcv_mpath != NULL)
+ free(ep->rcv_mpath);
+
+ FREE(ep, sizeof(EXF));
+ return (0);
+}
+
+/*
+ * file_write --
+ * Write the file to disk. Historic vi had fairly convoluted
+ * semantics for whether or not writes would happen. That's
+ * why all the flags.
+ */
+int
+file_write(sp, ep, fm, tm, name, flags)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm;
+ char *name;
+ int flags;
+{
+ struct stat sb;
+ FILE *fp;
+ FREF *frp;
+ MARK from, to;
+ u_long nlno, nch;
+ int btear, fd, noname, oflags, rval;
+ char *msg;
+
+ frp = sp->frp;
+ if (name == NULL) {
+ noname = 1;
+ name = frp->name;
+ } else
+ noname = 0;
+
+ /* Can't write files marked read-only, unless forced. */
+ if (!LF_ISSET(FS_FORCE) && noname && F_ISSET(frp, FR_RDONLY)) {
+ if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "Read-only file, not written; use ! to override");
+ else
+ msgq(sp, M_ERR, "Read-only file, not written");
+ return (1);
+ }
+
+ /* If not forced, not appending, and "writeany" not set ... */
+ if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
+ /* Don't overwrite anything but the original file. */
+ if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
+ !stat(name, &sb)) {
+ if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "%s exists, not written; use ! to override", name);
+ else
+ msgq(sp, M_ERR, "%s exists, not written", name);
+ return (1);
+ }
+
+ /*
+ * Don't write part of any existing file. Only test for the
+ * original file, the previous test catches anything else.
+ */
+ if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
+ if (LF_ISSET(FS_POSSIBLE))
+ msgq(sp, M_ERR,
+ "Use ! to write a partial file");
+ else
+ msgq(sp, M_ERR, "Partial file, not written");
+ return (1);
+ }
+ }
+
+ /*
+ * Figure out if the file already exists -- if it doesn't, we display
+ * the "new file" message. The stat might not be necessary, but we
+ * just repeat it because it's easier than hacking the previous tests.
+ * The information is only used for the user message and modification
+ * time test, so we can ignore the obvious race condition.
+ *
+ * If the user is overwriting a file other than the original file, and
+ * O_WRITEANY was what got us here (neither force nor append was set),
+ * display the "existing file" messsage. Since the FR_NAMECHANGE flag
+ * is cleared on a successful write, the message only appears once when
+ * the user changes a file name. This is historic practice.
+ *
+ * One final test. If we're not forcing or appending, and we have a
+ * saved modification time, stop the user if it's been written since
+ * we last edited or wrote it, and make them force it.
+ */
+ if (stat(name, &sb))
+ msg = ": new file";
+ else {
+ msg = "";
+ if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
+ if (ep->mtime && sb.st_mtime > ep->mtime) {
+ msgq(sp, M_ERR,
+ "%s: file modified more recently than this copy%s",
+ name, LF_ISSET(FS_POSSIBLE) ?
+ "; use ! to override" : "");
+ return (1);
+ }
+ if (!noname || F_ISSET(frp, FR_NAMECHANGE))
+ msg = ": existing file";
+ }
+ }
+
+ /* Set flags to either append or truncate. */
+ oflags = O_CREAT | O_WRONLY;
+ if (LF_ISSET(FS_APPEND))
+ oflags |= O_APPEND;
+ else
+ oflags |= O_TRUNC;
+
+ /* Open the file. */
+ if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+
+ /* Use stdio for buffering. */
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ (void)close(fd);
+ msgq(sp, M_SYSERR, name);
+ return (1);
+ }
+
+ /* Build fake addresses, if necessary. */
+ if (fm == NULL) {
+ from.lno = 1;
+ from.cno = 0;
+ fm = &from;
+ if (file_lline(sp, ep, &to.lno))
+ return (1);
+ to.cno = 0;
+ tm = &to;
+ }
+
+ /* Turn on the busy message. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Writing...");
+ rval = ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch);
+ if (btear)
+ busy_off(sp);
+
+ /*
+ * Save the new last modification time -- even if the write fails
+ * we re-init the time. That way the user can clean up the disk
+ * and rewrite without having to force it.
+ */
+ ep->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
+
+ /* If the write failed, complain loudly. */
+ if (rval) {
+ if (!LF_ISSET(FS_APPEND))
+ msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
+ return (1);
+ }
+
+ /*
+ * Once we've actually written the file, it doesn't matter that the
+ * file name was changed -- if it was, we've already whacked it.
+ */
+ F_CLR(frp, FR_NAMECHANGE);
+
+ /*
+ * If wrote the entire file clear the modified bit. If the file was
+ * written back to the original file name and the file is a temporary,
+ * set the "no exit" bit. This permits the user to write the file and
+ * use it in the context of the file system, but still keeps them from
+ * losing their changes by exiting.
+ */
+ if (LF_ISSET(FS_ALL)) {
+ F_CLR(ep, F_MODIFIED);
+ if (F_ISSET(frp, FR_TMPFILE))
+ if (noname)
+ F_SET(frp, FR_TMPEXIT);
+ else
+ F_CLR(frp, FR_TMPEXIT);
+ }
+
+ msgq(sp, M_INFO, "%s%s%s: %lu line%s, %lu characters",
+ INTERRUPTED(sp) ? "Interrupted write: " : "",
+ name, msg, nlno, nlno == 1 ? "" : "s", nch);
+
+ return (0);
+}
+
+/*
+ * file_m1 --
+ * First modification check routine. The :next, :prev, :rewind, :tag,
+ * :tagpush, :tagpop, ^^ modifications check.
+ */
+int
+file_m1(sp, ep, force, flags)
+ SCR *sp;
+ EXF *ep;
+ int force, flags;
+{
+ /*
+ * If the file has been modified, we'll want to write it back or
+ * fail. If autowrite is set, we'll write it back automatically,
+ * unless force is also set. Otherwise, we fail unless forced or
+ * there's another open screen on this file.
+ */
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (!force &&
+ file_write(sp, ep, NULL, NULL, NULL, flags))
+ return (1);
+ } else if (ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+ "File modified since last complete write; write or use %s to override",
+ LF_ISSET(FS_POSSIBLE) ? "!" : ":edit!");
+ return (1);
+ }
+
+ return (file_m3(sp, ep, force));
+}
+
+/*
+ * file_m2 --
+ * Second modification check routine. The :edit, :quit, :recover
+ * modifications check.
+ */
+int
+file_m2(sp, ep, force)
+ SCR *sp;
+ EXF *ep;
+ int force;
+{
+ /*
+ * If the file has been modified, we'll want to fail, unless forced
+ * or there's another open screen on this file.
+ */
+ if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+ "File modified since last complete write; write or use ! to override");
+ return (1);
+ }
+
+ return (file_m3(sp, ep, force));
+}
+
+/*
+ * file_m3 --
+ * Third modification check routine.
+ */
+int
+file_m3(sp, ep, force)
+ SCR *sp;
+ EXF *ep;
+ int force;
+{
+ /*
+ * Don't exit while in a temporary files if the file was ever modified.
+ * The problem is that if the user does a ":wq", we write and quit,
+ * unlinking the temporary file. Not what the user had in mind at all.
+ * We permit writing to temporary files, so that user maps using file
+ * system names work with temporary files.
+ */
+ if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) {
+ msgq(sp, M_ERR,
+ "File is a temporary; exit will discard modifications");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * file_lock --
+ * Get an exclusive lock on a file.
+ *
+ * XXX
+ * The default locking is flock(2) style, not fcntl(2). The latter is
+ * known to fail badly on some systems, and its only advantage is that
+ * it occasionally works over NFS.
+ *
+ * Furthermore, the semantics of fcntl(2) are wrong. The problems are
+ * two-fold: you can't close any file descriptor associated with the file
+ * without losing all of the locks, and you can't get an exclusive lock
+ * unless you have the file open for writing. Someone ought to be shot,
+ * but it's probably too late, they may already have reproduced. To get
+ * around these problems, nvi opens the files for writing when it can and
+ * acquires a second file descriptor when it can't. The recovery files
+ * are examples of the former, they're always opened for writing. The DB
+ * files can't be opened for writing because the semantics of DB are that
+ * files opened for writing are flushed back to disk when the DB session
+ * is ended. So, in that case we have to acquire an extra file descriptor.
+ */
+enum lockt
+file_lock(name, fdp, fd, iswrite)
+ char *name;
+ int fd, *fdp, iswrite;
+{
+#if !defined(USE_FCNTL) && defined(LOCK_EX)
+ /* Hurrah! We've got flock(2). */
+ /*
+ * !!!
+ * We need to distinguish a lock not being available for the file
+ * from the file system not supporting locking. Flock is documented
+ * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume
+ * they are the former. There's no portable way to do this.
+ */
+ errno = 0;
+ return (flock(fd, LOCK_EX | LOCK_NB) ?
+ errno == EAGAIN || errno == EWOULDBLOCK ?
+ LOCK_UNAVAIL : LOCK_FAILED : LOCK_SUCCESS);
+
+#else /* Gag me. We've got fcntl(2). */
+ struct flock arg;
+ int didopen, sverrno;
+
+ arg.l_type = F_WRLCK;
+ arg.l_whence = 0; /* SEEK_SET */
+ arg.l_start = arg.l_len = 0;
+ arg.l_pid = 0;
+
+ /* If the file descriptor isn't opened for writing, it must fail. */
+ if (!iswrite) {
+ if (name == NULL || fdp == NULL)
+ return (LOCK_FAILED);
+ if ((fd = open(name, O_RDWR, 0)) == -1)
+ return (LOCK_FAILED);
+ *fdp = fd;
+ didopen = 1;
+ }
+
+ errno = 0;
+ if (!fcntl(fd, F_SETLK, &arg))
+ return (LOCK_SUCCESS);
+ if (didopen) {
+ sverrno = errno;
+ (void)close(fd);
+ errno = sverrno;
+ }
+
+ /*
+ * !!!
+ * We need to distinguish a lock not being available for the file
+ * from the file system not supporting locking. Fcntl is documented
+ * as returning EACCESS and EAGAIN; add EWOULDBLOCK for good measure,
+ * and assume they are the former. There's no portable way to do this.
+ */
+ return (errno == EACCES || errno == EAGAIN || errno == EWOULDBLOCK ?
+ LOCK_UNAVAIL : LOCK_FAILED);
+#endif
+}
diff --git a/usr.bin/vi/common/exf.h b/usr.bin/vi/common/exf.h
new file mode 100644
index 0000000..1b06649
--- /dev/null
+++ b/usr.bin/vi/common/exf.h
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)exf.h 8.35 (Berkeley) 8/4/94
+ */
+ /* Undo direction. */
+/*
+ * exf --
+ * The file structure.
+ */
+struct _exf {
+ int refcnt; /* Reference count. */
+
+ /* Underlying database state. */
+ DB *db; /* File db structure. */
+ char *c_lp; /* Cached line. */
+ size_t c_len; /* Cached line length. */
+ recno_t c_lno; /* Cached line number. */
+ recno_t c_nlines; /* Cached lines in the file. */
+
+ DB *log; /* Log db structure. */
+ char *l_lp; /* Log buffer. */
+ size_t l_len; /* Log buffer length. */
+ recno_t l_high; /* Log last + 1 record number. */
+ recno_t l_cur; /* Log current record number. */
+ MARK l_cursor; /* Log cursor position. */
+ enum direction lundo; /* Last undo direction. */
+
+ LIST_HEAD(_markh, _lmark) marks;/* Linked list of file MARK's. */
+
+ time_t mtime; /* Last modification time. */
+
+ int fcntl_fd; /* Fcntl locking fd; see exf.c. */
+
+ /*
+ * Recovery in general, and these fields specifically, are described
+ * in recover.c.
+ */
+#define RCV_PERIOD 120 /* Sync every two minutes. */
+ char *rcv_path; /* Recover file name. */
+ char *rcv_mpath; /* Recover mail file name. */
+ int rcv_fd; /* Locked mail file descriptor. */
+ struct timeval rcv_tod; /* ITIMER_REAL: recovery time-of-day. */
+
+#define F_FIRSTMODIFY 0x001 /* File not yet modified. */
+#define F_MODIFIED 0x002 /* File is currently dirty. */
+#define F_MULTILOCK 0x004 /* Multiple processes running, lock. */
+#define F_NOLOG 0x008 /* Logging turned off. */
+#define F_RCV_NORM 0x010 /* Don't delete recovery files. */
+#define F_RCV_ON 0x020 /* Recovery is possible. */
+#define F_UNDO 0x040 /* No change since last undo. */
+ u_int8_t flags;
+};
+
+#define GETLINE_ERR(sp, lno) { \
+ msgq(sp, M_ERR, \
+ "Error: %s/%d: unable to retrieve line %u", \
+ tail(__FILE__), __LINE__, lno); \
+}
+
+/* EXF routines. */
+FREF *file_add __P((SCR *, CHAR_T *));
+int file_end __P((SCR *, EXF *, int));
+int file_init __P((SCR *, FREF *, char *, int));
+int file_m1 __P((SCR *, EXF *, int, int));
+int file_m2 __P((SCR *, EXF *, int));
+int file_m3 __P((SCR *, EXF *, int));
+
+enum lockt { LOCK_FAILED, LOCK_SUCCESS, LOCK_UNAVAIL };
+enum lockt
+ file_lock __P((char *, int *, int, int));
+
+#define FS_ALL 0x01 /* Write the entire file. */
+#define FS_APPEND 0x02 /* Append to the file. */
+#define FS_FORCE 0x04 /* Force is set. */
+#define FS_POSSIBLE 0x08 /* Force could be set. */
+int file_write __P((SCR *, EXF *, MARK *, MARK *, char *, int));
+
+/* Recovery routines. */
+int rcv_init __P((SCR *, EXF *));
+int rcv_list __P((SCR *));
+int rcv_on __P((SCR *, EXF *));
+int rcv_read __P((SCR *, FREF *));
+
+#define RCV_EMAIL 0x01 /* Send the user email, IFF file modified. */
+#define RCV_ENDSESSION 0x02 /* End the file session. */
+#define RCV_PRESERVE 0x04 /* Preserve backup file, IFF file modified. */
+#define RCV_SNAPSHOT 0x08 /* Snapshot the recovery, and send email. */
+int rcv_sync __P((SCR *, EXF *, u_int));
+int rcv_tmp __P((SCR *, EXF *, char *));
+
+/* DB interface routines */
+int file_aline __P((SCR *, EXF *, int, recno_t, char *, size_t));
+int file_dline __P((SCR *, EXF *, recno_t));
+char *file_gline __P((SCR *, EXF *, recno_t, size_t *));
+int file_iline __P((SCR *, EXF *, recno_t, char *, size_t));
+int file_lline __P((SCR *, EXF *, recno_t *));
+char *file_rline __P((SCR *, EXF *, recno_t, size_t *));
+int file_sline __P((SCR *, EXF *, recno_t, char *, size_t));
diff --git a/usr.bin/vi/common/gs.h b/usr.bin/vi/common/gs.h
new file mode 100644
index 0000000..36343f4
--- /dev/null
+++ b/usr.bin/vi/common/gs.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gs.h 8.39 (Berkeley) 7/23/94
+ */
+
+struct _gs {
+ CIRCLEQ_HEAD(_dqh, _scr) dq; /* Displayed screens. */
+ CIRCLEQ_HEAD(_hqh, _scr) hq; /* Hidden screens. */
+
+ mode_t origmode; /* Original terminal mode. */
+ struct termios
+ original_termios; /* Original terminal values. */
+
+ MSGH msgq; /* User message list. */
+
+ char *tmp_bp; /* Temporary buffer. */
+ size_t tmp_blen; /* Size of temporary buffer. */
+
+ sigset_t blockset; /* Signal mask. */
+
+#ifdef DEBUG
+ FILE *tracefp; /* Trace file pointer. */
+#endif
+
+/* INFORMATION SHARED BY ALL SCREENS. */
+ IBUF *tty; /* Key input buffer. */
+
+ CB *dcbp; /* Default cut buffer pointer. */
+ CB dcb_store; /* Default cut buffer storage. */
+ LIST_HEAD(_cuth, _cb) cutq; /* Linked list of cut buffers. */
+
+#define MAX_BIT_SEQ 128 /* Max + 1 fast check character. */
+ LIST_HEAD(_seqh, _seq) seqq; /* Linked list of maps, abbrevs. */
+ bitstr_t bit_decl(seqb, MAX_BIT_SEQ);
+
+#define MAX_FAST_KEY 254 /* Max fast check character.*/
+#define KEY_LEN(sp, ch) \
+ ((ch) <= MAX_FAST_KEY ? \
+ sp->gp->cname[ch].len : __key_len(sp, ch))
+#define KEY_NAME(sp, ch) \
+ ((ch) <= MAX_FAST_KEY ? \
+ sp->gp->cname[ch].name : __key_name(sp, ch))
+ struct {
+ CHAR_T name[MAX_CHARACTER_COLUMNS + 1];
+ u_int8_t len;
+ } cname[MAX_FAST_KEY + 1]; /* Fast lookup table. */
+
+#define KEY_VAL(sp, ch) \
+ ((ch) <= MAX_FAST_KEY ? sp->gp->special_key[ch] : \
+ (ch) > sp->gp->max_special ? 0 : __key_val(sp, ch))
+ CHAR_T max_special; /* Max special character. */
+ u_char /* Fast lookup table. */
+ special_key[MAX_FAST_KEY + 1];
+
+/* Interrupt macros. */
+#define INTERRUPTED(sp) \
+ (F_ISSET((sp), S_INTERRUPTED) || F_ISSET((sp)->gp, G_SIGINT))
+#define CLR_INTERRUPT(sp) { \
+ F_CLR((sp), S_INTERRUPTED | S_INTERRUPTIBLE); \
+ F_CLR((sp)->gp, G_SIGINT); \
+}
+
+#define G_ABBREV 0x0001 /* If have abbreviations. */
+#define G_BELLSCHED 0x0002 /* Bell scheduled. */
+#define G_RECOVER_SET 0x0004 /* Recover system initialized. */
+#define G_SETMODE 0x0008 /* Tty mode changed. */
+#define G_SIGALRM 0x0010 /* SIGALRM arrived. */
+#define G_SIGINT 0x0020 /* SIGINT arrived. */
+#define G_SIGWINCH 0x0040 /* SIGWINCH arrived. */
+#define G_SNAPSHOT 0x0080 /* Always snapshot files. */
+#define G_STDIN_TTY 0x0100 /* Standard input is a tty. */
+#define G_TERMIOS_SET 0x0200 /* Termios structure is valid. */
+#define G_TMP_INUSE 0x0400 /* Temporary buffer in use. */
+ u_int16_t flags;
+};
+
+extern GS *__global_list; /* List of screens. */
diff --git a/usr.bin/vi/common/line.c b/usr.bin/vi/common/line.c
new file mode 100644
index 0000000..847c132
--- /dev/null
+++ b/usr.bin/vi/common/line.c
@@ -0,0 +1,492 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)line.c 8.32 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static __inline int scr_update
+ __P((SCR *, EXF *, recno_t, enum operation, int));
+
+/*
+ * file_gline --
+ * Look in the text buffers for a line; if it's not there
+ * call file_rline to retrieve it from the database.
+ */
+char *
+file_gline(sp, ep, lno, lenp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno; /* Line number. */
+ size_t *lenp; /* Length store. */
+{
+ TEXT *tp;
+ recno_t l1, l2;
+
+ /*
+ * The underlying recno stuff handles zero by returning NULL, but
+ * have to have an oob condition for the look-aside into the input
+ * buffer anyway.
+ */
+ if (lno == 0)
+ return (NULL);
+
+ /*
+ * Look-aside into the TEXT buffers and see if the line we want
+ * is there.
+ */
+ if (F_ISSET(sp, S_INPUT)) {
+ l1 = ((TEXT *)sp->tiqp->cqh_first)->lno;
+ l2 = ((TEXT *)sp->tiqp->cqh_last)->lno;
+ if (l1 <= lno && l2 >= lno) {
+ for (tp = sp->tiqp->cqh_first;
+ tp->lno != lno; tp = tp->q.cqe_next);
+ if (lenp)
+ *lenp = tp->len;
+ return (tp->lb);
+ }
+ /*
+ * Adjust the line number for the number of lines used
+ * by the text input buffers.
+ */
+ if (lno > l2)
+ lno -= l2 - l1;
+ }
+ return (file_rline(sp, ep, lno, lenp));
+}
+
+/*
+ * file_rline --
+ * Look in the cache for a line; if it's not there retrieve
+ * it from the file.
+ */
+char *
+file_rline(sp, ep, lno, lenp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno; /* Line number. */
+ size_t *lenp; /* Length store. */
+{
+ DBT data, key;
+
+ /* Check the cache. */
+ if (lno == ep->c_lno) {
+ if (lenp)
+ *lenp = ep->c_len;
+ return (ep->c_lp);
+ }
+ ep->c_lno = OOBLNO;
+
+ /* Get the line from the underlying database. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ switch (ep->db->get(ep->db, &key, &data, 0)) {
+ case -1:
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to get line %u: %s",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ /* FALLTHROUGH */
+ case 1:
+ return (NULL);
+ /* NOTREACHED */
+ }
+ if (lenp)
+ *lenp = data.size;
+
+ /* Fill the cache. */
+ ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+ return (data.data);
+}
+
+/*
+ * file_dline --
+ * Delete a line from the file.
+ */
+int
+file_dline(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ DBT key;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "delete line %lu\n", lno);
+#endif
+ /*
+ * XXX
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ */
+ mark_insdel(sp, ep, LINE_DELETE, lno);
+ global_insdel(sp, ep, LINE_DELETE, lno);
+
+ /* Log change. */
+ log_line(sp, ep, lno, LOG_LINE_DELETE);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ SIGBLOCK(sp->gp);
+ if (ep->db->del(ep->db, &key, 0) == 1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to delete line %u: %s",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno <= ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ --ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_DELETE, 1));
+}
+
+/*
+ * file_aline --
+ * Append a line into the file.
+ */
+int
+file_aline(sp, ep, update, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ int update;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ recno_t lline;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+ /*
+ * XXX
+ * Very nasty special case. The historic vi code displays a single
+ * space (or a '$' if the list option is set) for the first line in
+ * an "empty" file. If we "insert" a line, that line gets scrolled
+ * down, not repainted, so it's incorrect when we refresh the the
+ * screen. This is really hard to find and fix in the vi code -- the
+ * text input functions detect it explicitly and don't insert a new
+ * line. The hack here is to repaint the screen if we're appending
+ * to an empty file. The reason that the test is in file_aline, and
+ * not in file_iline or file_sline, is that all of the ex commands
+ * that work in empty files end up here.
+ */
+ if (lno == 0) {
+ if (file_lline(sp, ep, &lline))
+ return (1);
+ if (lline == 0)
+ F_SET(sp, S_REDRAW);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK(sp->gp);
+ if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to append to line %u: %s",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno < ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ ++ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
+
+ /*
+ * XXX
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ *
+ * XXX
+ * See comment above about empty files. If the file was empty,
+ * then we're adding the first line, which is a replacement, not
+ * an append. So, we shouldn't whack the marks.
+ */
+ if (lno != 0) {
+ mark_insdel(sp, ep, LINE_INSERT, lno + 1);
+ global_insdel(sp, ep, LINE_INSERT, lno + 1);
+ }
+
+ /*
+ * Update screen.
+ *
+ * XXX
+ * Nasty hack. If multiple lines are input by the user, they aren't
+ * committed until an <ESC> is entered. The problem is the screen was
+ * updated/scrolled as each line was entered. So, when this routine
+ * is called to copy the new lines from the cut buffer into the file,
+ * it has to know not to update the screen again.
+ */
+ return (scr_update(sp, ep, lno, LINE_APPEND, update));
+}
+
+/*
+ * file_iline --
+ * Insert a line into the file.
+ */
+int
+file_iline(sp, ep, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+ recno_t lline;
+
+#if defined(DEBUG) && 0
+ TRACE(sp,
+ "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+
+ /* Very nasty special case. See comment in file_aline(). */
+ if (lno == 1) {
+ if (file_lline(sp, ep, &lline))
+ return (1);
+ if (lline == 0)
+ F_SET(sp, S_REDRAW);
+ }
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK(sp->gp);
+ if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to insert at line %u: %s",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* Flush the cache, update line count, before screen update. */
+ if (lno >= ep->c_lno)
+ ep->c_lno = OOBLNO;
+ if (ep->c_nlines != OOBLNO)
+ ++ep->c_nlines;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log change. */
+ log_line(sp, ep, lno, LOG_LINE_INSERT);
+
+ /*
+ * XXX
+ * Marks and global commands have to know when lines are
+ * inserted or deleted.
+ */
+ mark_insdel(sp, ep, LINE_INSERT, lno);
+ global_insdel(sp, ep, LINE_INSERT, lno);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_INSERT, 1));
+}
+
+/*
+ * file_sline --
+ * Store a line in the file.
+ */
+int
+file_sline(sp, ep, lno, p, len)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ char *p;
+ size_t len;
+{
+ DBT data, key;
+
+#if defined(DEBUG) && 0
+ TRACE(sp,
+ "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
+#endif
+ /* Log before change. */
+ log_line(sp, ep, lno, LOG_LINE_RESET_B);
+
+ /* Update file. */
+ key.data = &lno;
+ key.size = sizeof(lno);
+ data.data = p;
+ data.size = len;
+ SIGBLOCK(sp->gp);
+ if (ep->db->put(ep->db, &key, &data, 0) == -1) {
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to store line %u: %s",
+ tail(__FILE__), __LINE__, lno, strerror(errno));
+ return (1);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* Flush the cache, before logging or screen update. */
+ if (lno == ep->c_lno)
+ ep->c_lno = OOBLNO;
+
+ /* File now dirty. */
+ if (F_ISSET(ep, F_FIRSTMODIFY))
+ (void)rcv_init(sp, ep);
+ F_SET(ep, F_MODIFIED);
+
+ /* Log after change. */
+ log_line(sp, ep, lno, LOG_LINE_RESET_F);
+
+ /* Update screen. */
+ return (scr_update(sp, ep, lno, LINE_RESET, 1));
+}
+
+/*
+ * file_lline --
+ * Return the number of lines in the file.
+ */
+int
+file_lline(sp, ep, lnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t *lnop;
+{
+ DBT data, key;
+ recno_t lno;
+
+ /* Check the cache. */
+ if (ep->c_nlines != OOBLNO) {
+ *lnop = (F_ISSET(sp, S_INPUT) &&
+ ((TEXT *)sp->tiqp->cqh_last)->lno > ep->c_nlines ?
+ ((TEXT *)sp->tiqp->cqh_last)->lno : ep->c_nlines);
+ return (0);
+ }
+
+ key.data = &lno;
+ key.size = sizeof(lno);
+
+ switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
+ case -1:
+ msgq(sp, M_ERR,
+ "Error: %s/%d: unable to get last line: %s",
+ tail(__FILE__), __LINE__, strerror(errno));
+ *lnop = 0;
+ return (1);
+ case 1:
+ *lnop = 0;
+ return (0);
+ default:
+ break;
+ }
+
+ /* Fill the cache. */
+ memmove(&lno, key.data, sizeof(lno));
+ ep->c_nlines = ep->c_lno = lno;
+ ep->c_len = data.size;
+ ep->c_lp = data.data;
+
+ /* Return the value. */
+ *lnop = (F_ISSET(sp, S_INPUT) &&
+ ((TEXT *)sp->tiqp->cqh_last)->lno > lno ?
+ ((TEXT *)sp->tiqp->cqh_last)->lno : lno);
+ return (0);
+}
+
+/*
+ * scr_update --
+ * Update all of the screens that are backed by the file that
+ * just changed.
+ */
+static __inline int
+scr_update(sp, ep, lno, op, current)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum operation op;
+ int current;
+{
+ SCR *tsp;
+
+ if (ep->refcnt != 1)
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (sp != tsp && tsp->ep == ep)
+ (void)sp->s_change(tsp, ep, lno, op);
+ return (current && sp->s_change(sp, ep, lno, op));
+}
diff --git a/usr.bin/vi/common/log.c b/usr.bin/vi/common/log.c
new file mode 100644
index 0000000..b3a2295
--- /dev/null
+++ b/usr.bin/vi/common/log.c
@@ -0,0 +1,698 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)log.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * The log consists of records, each containing a type byte and a variable
+ * length byte string, as follows:
+ *
+ * LOG_CURSOR_INIT MARK
+ * LOG_CURSOR_END MARK
+ * LOG_LINE_APPEND recno_t char *
+ * LOG_LINE_DELETE recno_t char *
+ * LOG_LINE_INSERT recno_t char *
+ * LOG_LINE_RESET_F recno_t char *
+ * LOG_LINE_RESET_B recno_t char *
+ * LOG_MARK LMARK
+ *
+ * We do before image physical logging. This means that the editor layer
+ * MAY NOT modify records in place, even if simply deleting or overwriting
+ * characters. Since the smallest unit of logging is a line, we're using
+ * up lots of space. This may eventually have to be reduced, probably by
+ * doing logical logging, which is a much cooler database phrase.
+ *
+ * The implementation of the historic vi 'u' command, using roll-forward and
+ * roll-back, is simple. Each set of changes has a LOG_CURSOR_INIT record,
+ * followed by a number of other records, followed by a LOG_CURSOR_END record.
+ * LOG_LINE_RESET records come in pairs. The first is a LOG_LINE_RESET_B
+ * record, and is the line before the change. The second is LOG_LINE_RESET_F,
+ * and is the line after the change. Roll-back is done by backing up to the
+ * first LOG_CURSOR_INIT record before a change. Roll-forward is done in a
+ * similar fashion.
+ *
+ * The 'U' command is implemented by rolling backward to a LOG_CURSOR_END
+ * record for a line different from the current one. It should be noted that
+ * this means that a subsequent 'u' command will make a change based on the
+ * new position of the log's cursor. This is okay, and, in fact, historic vi
+ * behaved that way.
+ */
+
+static int log_cursor1 __P((SCR *, EXF *, int));
+#if defined(DEBUG) && 0
+static void log_trace __P((SCR *, char *, recno_t, u_char *));
+#endif
+
+/* Try and restart the log on failure, i.e. if we run out of memory. */
+#define LOG_ERR { \
+ msgq(sp, M_ERR, "Error: %s/%d: put log error: %s", \
+ tail(__FILE__), __LINE__, strerror(errno)); \
+ (void)ep->log->close(ep->log); \
+ if (!log_init(sp, ep)) \
+ msgq(sp, M_ERR, "Log restarted"); \
+ return (1); \
+}
+
+/*
+ * log_init --
+ * Initialize the logging subsystem.
+ */
+int
+log_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Initialize the buffer. The logging subsystem has its own
+ * buffers because the global ones are almost by definition
+ * going to be in use when the log runs.
+ */
+ ep->l_lp = NULL;
+ ep->l_len = 0;
+ ep->l_cursor.lno = 1; /* XXX Any valid recno. */
+ ep->l_cursor.cno = 0;
+ ep->l_high = ep->l_cur = 1;
+
+ ep->log = dbopen(NULL, O_CREAT | O_NONBLOCK | O_RDWR,
+ S_IRUSR | S_IWUSR, DB_RECNO, NULL);
+ if (ep->log == NULL) {
+ msgq(sp, M_ERR, "log db: %s", strerror(errno));
+ F_SET(ep, F_NOLOG);
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * log_end --
+ * Close the logging subsystem.
+ */
+int
+log_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ if (ep->log != NULL) {
+ (void)(ep->log->close)(ep->log);
+ ep->log = NULL;
+ }
+ if (ep->l_lp != NULL) {
+ free(ep->l_lp);
+ ep->l_lp = NULL;
+ }
+ ep->l_len = 0;
+ ep->l_cursor.lno = 1; /* XXX Any valid recno. */
+ ep->l_cursor.cno = 0;
+ ep->l_high = ep->l_cur = 1;
+ return (0);
+}
+
+/*
+ * log_cursor --
+ * Log the current cursor position, starting an event.
+ */
+int
+log_cursor(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * If any changes were made since the last cursor init,
+ * put out the ending cursor record.
+ */
+ if (ep->l_cursor.lno == OOBLNO) {
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (log_cursor1(sp, ep, LOG_CURSOR_END));
+ }
+ ep->l_cursor.lno = sp->lno;
+ ep->l_cursor.cno = sp->cno;
+ return (0);
+}
+
+/*
+ * log_cursor1 --
+ * Actually push a cursor record out.
+ */
+static int
+log_cursor1(sp, ep, type)
+ SCR *sp;
+ EXF *ep;
+ int type;
+{
+ DBT data, key;
+
+ BINC_RET(sp, ep->l_lp, ep->l_len, sizeof(u_char) + sizeof(MARK));
+ ep->l_lp[0] = type;
+ memmove(ep->l_lp + sizeof(u_char), &ep->l_cursor, sizeof(MARK));
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = sizeof(u_char) + sizeof(MARK);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "%lu: %s: %u/%u\n", ep->l_cur,
+ type == LOG_CURSOR_INIT ? "log_cursor_init" : "log_cursor_end",
+ sp->lno, sp->cno);
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+
+ return (0);
+}
+
+/*
+ * log_line --
+ * Log a line change.
+ */
+int
+log_line(sp, ep, lno, action)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ u_int action;
+{
+ DBT data, key;
+ size_t len;
+ char *lp;
+
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /*
+ * XXX
+ *
+ * Kluge for vi. Clear the EXF undo flag so that the
+ * next 'u' command does a roll-back, regardless.
+ */
+ F_CLR(ep, F_UNDO);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ /*
+ * Put out the changes. If it's a LOG_LINE_RESET_B call, it's a
+ * special case, avoid the caches. Also, if it fails and it's
+ * line 1, it just means that the user started with an empty file,
+ * so fake an empty length line.
+ */
+ if (action == LOG_LINE_RESET_B) {
+ if ((lp = file_rline(sp, ep, lno, &len)) == NULL) {
+ if (lno != 1) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ len = 0;
+ lp = "";
+ }
+ } else
+ if ((lp = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ BINC_RET(sp,
+ ep->l_lp, ep->l_len, len + sizeof(u_char) + sizeof(recno_t));
+ ep->l_lp[0] = action;
+ memmove(ep->l_lp + sizeof(u_char), &lno, sizeof(recno_t));
+ memmove(ep->l_lp + sizeof(u_char) + sizeof(recno_t), lp, len);
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = len + sizeof(u_char) + sizeof(recno_t);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ switch (action) {
+ case LOG_LINE_APPEND:
+ TRACE(sp, "%u: log_line: append: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_DELETE:
+ TRACE(sp, "%lu: log_line: delete: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_INSERT:
+ TRACE(sp, "%lu: log_line: insert: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_RESET_F:
+ TRACE(sp, "%lu: log_line: reset_f: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ case LOG_LINE_RESET_B:
+ TRACE(sp, "%lu: log_line: reset_b: %lu {%u}\n",
+ ep->l_cur, lno, len);
+ break;
+ }
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+
+ return (0);
+}
+
+/*
+ * log_mark --
+ * Log a mark position. For the log to work, we assume that there
+ * aren't any operations that just put out a log record -- this
+ * would mean that undo operations would only reset marks, and not
+ * cause any other change.
+ */
+int
+log_mark(sp, ep, lmp)
+ SCR *sp;
+ EXF *ep;
+ LMARK *lmp;
+{
+ DBT data, key;
+
+ if (F_ISSET(ep, F_NOLOG))
+ return (0);
+
+ /* Put out one initial cursor record per set of changes. */
+ if (ep->l_cursor.lno != OOBLNO) {
+ if (log_cursor1(sp, ep, LOG_CURSOR_INIT))
+ return (1);
+ ep->l_cursor.lno = OOBLNO;
+ }
+
+ BINC_RET(sp, ep->l_lp,
+ ep->l_len, sizeof(u_char) + sizeof(LMARK));
+ ep->l_lp[0] = LOG_MARK;
+ memmove(ep->l_lp + sizeof(u_char), lmp, sizeof(LMARK));
+
+ key.data = &ep->l_cur;
+ key.size = sizeof(recno_t);
+ data.data = ep->l_lp;
+ data.size = sizeof(u_char) + sizeof(LMARK);
+ if (ep->log->put(ep->log, &key, &data, 0) == -1)
+ LOG_ERR;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "%lu: mark %c: %lu/%u\n",
+ ep->l_cur, lmp->name, lmp->lno, lmp->cno);
+#endif
+ /* Reset high water mark. */
+ ep->l_high = ++ep->l_cur;
+ return (0);
+}
+
+/*
+ * Log_backward --
+ * Roll the log backward one operation.
+ */
+int
+log_backward(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ DBT key, data;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, undo not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == 1) {
+ msgq(sp, M_BERR, "No changes to undo");
+ return (1);
+ }
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+ for (didop = 0;;) {
+ --ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_backward", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_INIT:
+ if (didop) {
+ memmove(rp, p + sizeof(u_char), sizeof(MARK));
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_END:
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_dline(sp, ep, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_DELETE:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_iline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_ADDED];
+ break;
+ case LOG_LINE_RESET_F:
+ break;
+ case LOG_LINE_RESET_B:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, ep, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_setline --
+ * Reset the line to its original appearance.
+ *
+ * XXX
+ * There's a bug in this code due to our not logging cursor movements
+ * unless a change was made. If you do a change, move off the line,
+ * then move back on and do a 'U', the line will be restored to the way
+ * it was before the original change.
+ */
+int
+log_setline(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ DBT key, data;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, undo not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == 1)
+ return (1);
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+
+ for (;;) {
+ --ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_setline", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_INIT:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (m.lno != sp->lno || ep->l_cur == 1) {
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_END:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ if (m.lno != sp->lno) {
+ ++ep->l_cur;
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ case LOG_LINE_DELETE:
+ case LOG_LINE_RESET_F:
+ break;
+ case LOG_LINE_RESET_B:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (lno == sp->lno &&
+ file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ case LOG_MARK:
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, ep, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+/*
+ * Log_forward --
+ * Roll the log forward one operation.
+ */
+int
+log_forward(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ DBT key, data;
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+ int didop;
+ u_char *p;
+
+ if (F_ISSET(ep, F_NOLOG)) {
+ msgq(sp, M_ERR,
+ "Logging not being performed, roll-forward not possible");
+ return (1);
+ }
+
+ if (ep->l_cur == ep->l_high) {
+ msgq(sp, M_BERR, "No changes to re-do");
+ return (1);
+ }
+
+ F_SET(ep, F_NOLOG); /* Turn off logging. */
+
+ key.data = &ep->l_cur; /* Initialize db request. */
+ key.size = sizeof(recno_t);
+ for (didop = 0;;) {
+ ++ep->l_cur;
+ if (ep->log->get(ep->log, &key, &data, 0))
+ LOG_ERR;
+#if defined(DEBUG) && 0
+ log_trace(sp, "log_forward", ep->l_cur, data.data);
+#endif
+ switch (*(p = (u_char *)data.data)) {
+ case LOG_CURSOR_END:
+ if (didop) {
+ ++ep->l_cur;
+ memmove(rp, p + sizeof(u_char), sizeof(MARK));
+ F_CLR(ep, F_NOLOG);
+ return (0);
+ }
+ break;
+ case LOG_CURSOR_INIT:
+ break;
+ case LOG_LINE_APPEND:
+ case LOG_LINE_INSERT:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_iline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ ++sp->rptlines[L_ADDED];
+ break;
+ case LOG_LINE_DELETE:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_dline(sp, ep, lno))
+ goto err;
+ ++sp->rptlines[L_DELETED];
+ break;
+ case LOG_LINE_RESET_B:
+ break;
+ case LOG_LINE_RESET_F:
+ didop = 1;
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ if (file_sline(sp, ep, lno, p + sizeof(u_char) +
+ sizeof(recno_t), data.size - sizeof(u_char) -
+ sizeof(recno_t)))
+ goto err;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ break;
+ case LOG_MARK:
+ didop = 1;
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ m.lno = lm.lno;
+ m.cno = lm.cno;
+ if (mark_set(sp, ep, lm.name, &m, 0))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+err: F_CLR(ep, F_NOLOG);
+ return (1);
+}
+
+#if defined(DEBUG) && 0
+static void
+log_trace(sp, msg, rno, p)
+ SCR *sp;
+ char *msg;
+ recno_t rno;
+ u_char *p;
+{
+ LMARK lm;
+ MARK m;
+ recno_t lno;
+
+ switch (*p) {
+ case LOG_CURSOR_INIT:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ TRACE(sp, "%lu: %s: C_INIT: %u/%u\n", rno, msg, m.lno, m.cno);
+ break;
+ case LOG_CURSOR_END:
+ memmove(&m, p + sizeof(u_char), sizeof(MARK));
+ TRACE(sp, "%lu: %s: C_END: %u/%u\n", rno, msg, m.lno, m.cno);
+ break;
+ case LOG_LINE_APPEND:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: APPEND: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_INSERT:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: INSERT: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_DELETE:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: DELETE: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_RESET_F:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: RESET_F: %lu\n", rno, msg, lno);
+ break;
+ case LOG_LINE_RESET_B:
+ memmove(&lno, p + sizeof(u_char), sizeof(recno_t));
+ TRACE(sp, "%lu: %s: RESET_B: %lu\n", rno, msg, lno);
+ break;
+ case LOG_MARK:
+ memmove(&lm, p + sizeof(u_char), sizeof(LMARK));
+ TRACE(sp,
+ "%lu: %s: MARK: %u/%u\n", rno, msg, lm.lno, lm.cno);
+ break;
+ default:
+ abort();
+ }
+}
+#endif
diff --git a/usr.bin/vi/common/log.h b/usr.bin/vi/common/log.h
new file mode 100644
index 0000000..2974df4
--- /dev/null
+++ b/usr.bin/vi/common/log.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)log.h 8.5 (Berkeley) 3/16/94
+ */
+
+#define LOG_NOTYPE 0
+#define LOG_CURSOR_INIT 1
+#define LOG_CURSOR_END 2
+#define LOG_LINE_APPEND 3
+#define LOG_LINE_DELETE 4
+#define LOG_LINE_INSERT 5
+#define LOG_LINE_RESET_F 6
+#define LOG_LINE_RESET_B 7
+#define LOG_MARK 8
+
+int log_backward __P((SCR *, EXF *, MARK *));
+int log_cursor __P((SCR *, EXF *));
+int log_end __P((SCR *, EXF *));
+int log_forward __P((SCR *, EXF *, MARK *));
+int log_init __P((SCR *, EXF *));
+int log_line __P((SCR *, EXF *, recno_t, u_int));
+int log_mark __P((SCR *, EXF *, LMARK *));
+int log_setline __P((SCR *, EXF *));
diff --git a/usr.bin/vi/common/main.c b/usr.bin/vi/common/main.c
new file mode 100644
index 0000000..33cb4b2
--- /dev/null
+++ b/usr.bin/vi/common/main.c
@@ -0,0 +1,720 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.106 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../ex/tag.h"
+
+enum rc { NOEXIST, NOPERM, OK };
+
+static enum rc exrc_isok __P((SCR *, struct stat *, char *, int, int));
+static void gs_end __P((GS *));
+static GS *gs_init __P((void));
+static void obsolete __P((char *[]));
+static void usage __P((int));
+
+GS *__global_list; /* GLOBAL: List of screens. */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ static int reenter; /* STATIC: Re-entrancy check. */
+ struct stat hsb, lsb;
+ GS *gp;
+ FREF *frp;
+ SCR *sp;
+ u_int flags, saved_vi_mode;
+ int ch, eval, flagchk, readonly, silent, snapshot;
+ char *excmdarg, *myname, *p, *tag_f, *trace_f, *wsizearg;
+ char path[MAXPATHLEN];
+
+ /* Stop if indirecting through a NULL pointer. */
+ if (reenter++)
+ abort();
+
+#ifdef GDBATTACH
+ (void)printf("%u waiting...\n", getpid());
+ (void)read(0, &eval, 1);
+#endif
+
+ /* Set screen type and mode based on the program name. */
+ readonly = 0;
+ if ((myname = strrchr(*argv, '/')) == NULL)
+ myname = *argv;
+ else
+ ++myname;
+ if (!strcmp(myname, "ex") || !strcmp(myname, "nex"))
+ LF_INIT(S_EX);
+ else {
+ /* View is readonly. */
+ if (!strcmp(myname, "view"))
+ readonly = 1;
+ LF_INIT(S_VI_CURSES);
+ }
+ saved_vi_mode = S_VI_CURSES;
+
+ /* Convert old-style arguments into new-style ones. */
+ obsolete(argv);
+
+ /* Parse the arguments. */
+ flagchk = '\0';
+ excmdarg = tag_f = trace_f = wsizearg = NULL;
+ silent = 0;
+ snapshot = 1;
+ while ((ch = getopt(argc, argv, "c:eFRrsT:t:vw:X:")) != EOF)
+ switch (ch) {
+ case 'c': /* Run the command. */
+ excmdarg = optarg;
+ break;
+ case 'e': /* Ex mode. */
+ LF_CLR(S_SCREENS);
+ LF_SET(S_EX);
+ break;
+ case 'F': /* No snapshot. */
+ snapshot = 0;
+ break;
+ case 'R': /* Readonly. */
+ readonly = 1;
+ break;
+ case 'r': /* Recover. */
+ if (flagchk == 't')
+ errx(1,
+ "only one of -r and -t may be specified.");
+ flagchk = 'r';
+ break;
+ case 's':
+ silent = 1;
+ break;
+ case 'T': /* Trace. */
+ trace_f = optarg;
+ break;
+ case 't': /* Tag. */
+ if (flagchk == 'r')
+ errx(1,
+ "only one of -r and -t may be specified.");
+ if (flagchk == 't')
+ errx(1,
+ "only one tag file may be specified.");
+ flagchk = 't';
+ tag_f = optarg;
+ break;
+ case 'v': /* Vi mode. */
+ LF_CLR(S_SCREENS);
+ LF_SET(S_VI_CURSES);
+ break;
+ case 'w':
+ wsizearg = optarg;
+ break;
+ case 'X':
+ if (!strcmp(optarg, "aw")) {
+ LF_CLR(S_SCREENS);
+ LF_SET(S_VI_XAW);
+ saved_vi_mode = S_VI_XAW;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '?':
+ default:
+ usage(LF_ISSET(S_EX));
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Silent is only applicable to ex. */
+ if (silent && !LF_ISSET(S_EX))
+ errx(1, "-s only applicable to ex.");
+
+ /* Build and initialize the GS structure. */
+ __global_list = gp = gs_init();
+
+ /*
+ * If not reading from a terminal, it's like -s was specified.
+ * Vi always reads from the terminal, so fail if it's not a
+ * terminal.
+ */
+ if (!F_ISSET(gp, G_STDIN_TTY)) {
+ silent = 1;
+ if (!LF_ISSET(S_EX)) {
+ msgq(NULL, M_ERR,
+ "Vi's standard input must be a terminal");
+ goto err;
+ }
+ }
+
+ /*
+ * Build and initialize the first/current screen. This is a bit
+ * tricky. If an error is returned, we may or may not have a
+ * screen structure. If we have a screen structure, put it on a
+ * display queue so that the error messages get displayed.
+ *
+ * !!!
+ * Signals not on, no need to block them for queue manipulation.
+ */
+ if (screen_init(NULL, &sp, flags)) {
+ if (sp != NULL)
+ CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
+ goto err;
+ }
+ sp->saved_vi_mode = saved_vi_mode;
+ CIRCLEQ_INSERT_HEAD(&__global_list->dq, sp, q);
+
+ if (trace_f != NULL) {
+#ifdef DEBUG
+ if ((gp->tracefp = fopen(trace_f, "w")) == NULL)
+ err(1, "%s", trace_f);
+ (void)fprintf(gp->tracefp, "\n===\ntrace: open %s\n", trace_f);
+#else
+ msgq(sp, M_ERR, "-T support not compiled into this version");
+#endif
+ }
+
+ if (opts_init(sp)) /* Options initialization. */
+ goto err;
+ if (readonly) /* Global read-only bit. */
+ O_SET(sp, O_READONLY);
+ if (silent) { /* Ex batch mode. */
+ O_CLR(sp, O_AUTOPRINT);
+ O_CLR(sp, O_PROMPT);
+ O_CLR(sp, O_VERBOSE);
+ O_CLR(sp, O_WARN);
+ F_SET(sp, S_EXSILENT);
+ }
+ if (wsizearg != NULL) {
+ ARGS *av[2], a, b;
+ errno = 0;
+ if (strtol(wsizearg, &p, 10) < 0 || errno || *p)
+ errx(1, "illegal window size -- %s.", wsizearg);
+ (void)snprintf(path, sizeof(path), "window=%s", wsizearg);
+ a.bp = (CHAR_T *)path;
+ a.len = strlen(path);
+ b.bp = NULL;
+ b.len = 0;
+ av[0] = &a;
+ av[1] = &b;
+ if (opts_set(sp, NULL, av))
+ msgq(sp, M_ERR,
+ "Unable to set command line window size option");
+ }
+
+ /* Keymaps, special keys, must follow option initializations. */
+ if (term_init(sp))
+ goto err;
+
+#ifdef DIGRAPHS
+ if (digraph_init(sp)) /* Digraph initialization. */
+ goto err;
+#endif
+
+ /*
+ * Source the system, environment, $HOME and local .exrc values.
+ * Vi historically didn't check $HOME/.exrc if the environment
+ * variable EXINIT was set. This is all done before the file is
+ * read in, because things in the .exrc information can set, for
+ * example, the recovery directory.
+ *
+ * !!!
+ * While nvi can handle any of the options settings of historic vi,
+ * the converse is not true. Since users are going to have to have
+ * files and environmental variables that work with both, we use nvi
+ * versions of both the $HOME and local startup files if they exist,
+ * otherwise the historic ones.
+ *
+ * !!!
+ * For a discussion of permissions and when what .exrc files are
+ * read, see the the comment above the exrc_isok() function below.
+ *
+ * !!!
+ * If the user started the historic of vi in $HOME, vi read the user's
+ * .exrc file twice, as $HOME/.exrc and as ./.exrc. We avoid this, as
+ * it's going to make some commands behave oddly, and I can't imagine
+ * anyone depending on it.
+ */
+ if (!silent) {
+ switch (exrc_isok(sp, &hsb, _PATH_SYSEXRC, 1, 0)) {
+ case NOEXIST:
+ case NOPERM:
+ break;
+ case OK:
+ (void)ex_cfile(sp, NULL, _PATH_SYSEXRC, 0);
+ break;
+ }
+
+ if ((p = getenv("NEXINIT")) != NULL ||
+ (p = getenv("EXINIT")) != NULL)
+ if ((p = strdup(p)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ } else {
+ F_SET(sp, S_VLITONLY);
+ (void)ex_icmd(sp, NULL, p, strlen(p), 0);
+ F_CLR(sp, S_VLITONLY);
+ free(p);
+ }
+ else if ((p = getenv("HOME")) != NULL && *p) {
+ (void)snprintf(path,
+ sizeof(path), "%s/%s", p, _PATH_NEXRC);
+ switch (exrc_isok(sp, &hsb, path, 0, 1)) {
+ case NOEXIST:
+ (void)snprintf(path,
+ sizeof(path), "%s/%s", p, _PATH_EXRC);
+ if (exrc_isok(sp, &hsb, path, 0, 1) == OK)
+ (void)ex_cfile(sp, NULL, path, 0);
+ break;
+ case NOPERM:
+ break;
+ case OK:
+ (void)ex_cfile(sp, NULL, path, 0);
+ break;
+ }
+ }
+
+ if (O_ISSET(sp, O_EXRC))
+ switch (exrc_isok(sp, &lsb, _PATH_NEXRC, 0, 0)) {
+ case NOEXIST:
+ if (exrc_isok(sp,
+ &lsb, _PATH_EXRC, 0, 0) == OK &&
+ (lsb.st_dev != hsb.st_dev ||
+ lsb.st_ino != hsb.st_ino))
+ (void)ex_cfile(sp, NULL, _PATH_EXRC, 0);
+ break;
+ case NOPERM:
+ break;
+ case OK:
+ if (lsb.st_dev != hsb.st_dev ||
+ lsb.st_ino != hsb.st_ino)
+ (void)ex_cfile(sp,
+ NULL, _PATH_NEXRC, 0);
+ break;
+ }
+ }
+
+ /* List recovery files if -r specified without file arguments. */
+ if (flagchk == 'r' && argv[0] == NULL)
+ exit(rcv_list(sp));
+
+ /* Set the file snapshot flag. */
+ if (snapshot)
+ F_SET(gp, G_SNAPSHOT);
+
+ /* Use a tag file if specified. */
+ if (tag_f != NULL && ex_tagfirst(sp, tag_f))
+ goto err;
+
+ /*
+ * Append any remaining arguments as file names. Files are
+ * recovery files if -r specified.
+ */
+ if (*argv != NULL) {
+ sp->argv = sp->cargv = argv;
+ F_SET(sp, S_ARGNOFREE);
+ if (flagchk == 'r')
+ F_SET(sp, S_ARGRECOVER);
+ }
+
+ /*
+ * If the tag option hasn't already created a file, create one.
+ * If no files as arguments, use a temporary file.
+ */
+ if (tag_f == NULL) {
+ if ((frp = file_add(sp,
+ sp->argv == NULL ? NULL : (CHAR_T *)(sp->argv[0]))) == NULL)
+ goto err;
+ if (F_ISSET(sp, S_ARGRECOVER))
+ F_SET(frp, FR_RECOVER);
+ if (file_init(sp, frp, NULL, 0))
+ goto err;
+ }
+
+ /*
+ * If there's an initial command, push it on the command stack.
+ * Historically, it was always an ex command, not vi in vi mode
+ * or ex in ex mode. So, make it look like an ex command to vi.
+ *
+ * !!!
+ * Historically, all such commands were executed with the last
+ * line of the file as the current line, and not the first, so
+ * set up vi to be at the end of the file.
+ */
+ if (excmdarg != NULL)
+ if (IN_EX_MODE(sp)) {
+ if (term_push(sp, "\n", 1, 0))
+ goto err;
+ if (term_push(sp, excmdarg, strlen(excmdarg), 0))
+ goto err;
+ } else if (IN_VI_MODE(sp)) {
+ if (term_push(sp, "\n", 1, 0))
+ goto err;
+ if (term_push(sp, excmdarg, strlen(excmdarg), 0))
+ goto err;
+ if (term_push(sp, ":", 1, 0))
+ goto err;
+ if (file_lline(sp, sp->ep, &sp->frp->lno))
+ goto err;
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+
+ /* Set up signals. */
+ if (sig_init(sp))
+ goto err;
+
+ for (;;) {
+ /* Ignore errors -- other screens may succeed. */
+ (void)sp->s_edit(sp, sp->ep);
+
+ /*
+ * Edit the next screen on the display queue, or, move
+ * a screen from the hidden queue to the display queue.
+ */
+ if ((sp = __global_list->dq.cqh_first) ==
+ (void *)&__global_list->dq)
+ if ((sp = __global_list->hq.cqh_first) !=
+ (void *)&__global_list->hq) {
+ SIGBLOCK(__global_list);
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+ SIGUNBLOCK(__global_list);
+ } else
+ break;
+
+ /*
+ * The screen type may have changed -- reinitialize the
+ * functions in case it has.
+ */
+ switch (F_ISSET(sp, S_SCREENS)) {
+ case S_EX:
+ if (sex_screen_init(sp))
+ goto err;
+ break;
+ case S_VI_CURSES:
+ if (svi_screen_init(sp))
+ goto err;
+ break;
+ case S_VI_XAW:
+ if (xaw_screen_init(sp))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ eval = 0;
+ if (0)
+err: eval = 1;
+
+ /*
+ * NOTE: sp may be GONE when the screen returns, so only
+ * the gp can be trusted.
+ */
+ gs_end(gp);
+
+ exit(eval);
+}
+
+/*
+ * gs_init --
+ * Build and initialize the GS structure.
+ */
+static GS *
+gs_init()
+{
+ GS *gp;
+ int fd;
+
+ CALLOC_NOMSG(NULL, gp, GS *, 1, sizeof(GS));
+ if (gp == NULL)
+ err(1, NULL);
+
+ /*
+ * !!!
+ * Signals not on, no need to block them for queue manipulation.
+ */
+ CIRCLEQ_INIT(&gp->dq);
+ CIRCLEQ_INIT(&gp->hq);
+ LIST_INIT(&gp->msgq);
+
+ /* Structures shared by screens so stored in the GS structure. */
+ CALLOC_NOMSG(NULL, gp->tty, IBUF *, 1, sizeof(IBUF));
+ if (gp->tty == NULL)
+ err(1, NULL);
+
+ CIRCLEQ_INIT(&gp->dcb_store.textq);
+ LIST_INIT(&gp->cutq);
+ LIST_INIT(&gp->seqq);
+
+ /* Set a flag if we're reading from the tty. */
+ if (isatty(STDIN_FILENO))
+ F_SET(gp, G_STDIN_TTY);
+
+ /*
+ * Set the G_STDIN_TTY flag. It's purpose is to avoid setting and
+ * resetting the tty if the input isn't from there.
+ *
+ * Set the G_TERMIOS_SET flag. It's purpose is to avoid using the
+ * original_termios information (mostly special character values)
+ * if it's not valid. We expect that if we've lost our controlling
+ * terminal that the open() (but not the tcgetattr()) will fail.
+ */
+ if (F_ISSET(gp, G_STDIN_TTY)) {
+ if (tcgetattr(STDIN_FILENO, &gp->original_termios) == -1)
+ err(1, "tcgetattr");
+ F_SET(gp, G_TERMIOS_SET);
+ } else if ((fd = open(_PATH_TTY, O_RDONLY, 0)) != -1) {
+ if (tcgetattr(fd, &gp->original_termios) == -1)
+ err(1, "tcgetattr");
+ F_SET(gp, G_TERMIOS_SET);
+ (void)close(fd);
+ }
+ return (gp);
+}
+
+
+/*
+ * gs_end --
+ * End the GS structure.
+ */
+static void
+gs_end(gp)
+ GS *gp;
+{
+ MSG *mp;
+ SCR *sp;
+ char *tty;
+
+ /* Default buffer storage. */
+ (void)text_lfree(&gp->dcb_store.textq);
+
+ /* Reset anything that needs resetting. */
+ if (gp->flags & G_SETMODE) /* O_MESG */
+ if ((tty = ttyname(STDERR_FILENO)) == NULL)
+ warn("ttyname");
+ else if (chmod(tty, gp->origmode) < 0)
+ warn("%s", tty);
+
+ /* Ring the bell if scheduled. */
+ if (F_ISSET(gp, G_BELLSCHED))
+ (void)fprintf(stderr, "\07"); /* \a */
+
+ /* If there are any remaining screens, flush their messages. */
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr,
+ "%.*s.\n", (int)mp->len, mp->mbuf);
+ for (sp = __global_list->hq.cqh_first;
+ sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr,
+ "%.*s.\n", (int)mp->len, mp->mbuf);
+ /* Flush messages on the global queue. */
+ for (mp = gp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next)
+ (void)fprintf(stderr, "%.*s.\n", (int)mp->len, mp->mbuf);
+
+ /*
+ * DON'T FREE THE GLOBAL STRUCTURE -- WE DIDN'T TURN
+ * OFF SIGNALS/TIMERS, SO IT MAY STILL BE REFERENCED.
+ */
+}
+
+/*
+ * exrc_isok --
+ * Check a .exrc file for source-ability.
+ *
+ * !!!
+ * Historically, vi read the $HOME and local .exrc files if they were owned
+ * by the user's real ID, or the "sourceany" option was set, regardless of
+ * any other considerations. We no longer support the sourceany option as
+ * it's a security problem of mammoth proportions. We require the system
+ * .exrc file to be owned by root, the $HOME .exrc file to be owned by the
+ * user's effective ID (or that the user's effective ID be root) and the
+ * local .exrc files to be owned by the user's effective ID. In all cases,
+ * the file cannot be writeable by anyone other than its owner.
+ *
+ * In O'Reilly ("Learning the VI Editor", Fifth Ed., May 1992, page 106),
+ * it notes that System V release 3.2 and later has an option "[no]exrc".
+ * The behavior is that local .exrc files are read only if the exrc option
+ * is set. The default for the exrc option was off, so, by default, local
+ * .exrc files were not read. The problem this was intended to solve was
+ * that System V permitted users to give away files, so there's no possible
+ * ownership or writeability test to ensure that the file is safe.
+ *
+ * POSIX 1003.2-1992 standardized exrc as an option. It required the exrc
+ * option to be off by default, thus local .exrc files are not to be read
+ * by default. The Rationale noted (incorrectly) that this was a change
+ * to historic practice, but correctly noted that a default of off improves
+ * system security. POSIX also required that vi check the effective user
+ * ID instead of the real user ID, which is why we've switched from historic
+ * practice.
+ *
+ * We initialize the exrc variable to off. If it's turned on by the system
+ * or $HOME .exrc files, and the local .exrc file passes the ownership and
+ * writeability tests, then we read it. This breaks historic 4BSD practice,
+ * but it gives us a measure of security on systems where users can give away
+ * files.
+ */
+static enum rc
+exrc_isok(sp, sbp, path, rootown, rootid)
+ SCR *sp;
+ struct stat *sbp;
+ char *path;
+ int rootown, rootid;
+{
+ uid_t euid;
+ char *emsg, buf[MAXPATHLEN];
+
+ /* Check for the file's existence. */
+ if (stat(path, sbp))
+ return (NOEXIST);
+
+ /* Check ownership permissions. */
+ euid = geteuid();
+ if (!(rootown && sbp->st_uid == 0) &&
+ !(rootid && euid == 0) && sbp->st_uid != euid) {
+ emsg = rootown ?
+ "not owned by you or root" : "not owned by you";
+ goto denied;
+ }
+
+ /* Check writeability. */
+ if (sbp->st_mode & (S_IWGRP | S_IWOTH)) {
+ emsg = "writeable by a user other than the owner";
+denied: if (strchr(path, '/') == NULL &&
+ getcwd(buf, sizeof(buf)) != NULL)
+ msgq(sp, M_ERR,
+ "%s/%s: not sourced: %s", buf, path, emsg);
+ else
+ msgq(sp, M_ERR,
+ "%s: not sourced: %s", path, emsg);
+ return (NOPERM);
+ }
+ return (OK);
+}
+
+static void
+obsolete(argv)
+ char *argv[];
+{
+ size_t len;
+ char *p;
+
+ /*
+ * Translate old style arguments into something getopt will like.
+ * Make sure it's not text space memory, because ex changes the
+ * strings.
+ * Change "+" into "-c$".
+ * Change "+<anything else>" into "-c<anything else>".
+ * Change "-" into "-s"
+ */
+ while (*++argv)
+ if (argv[0][0] == '+') {
+ if (argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 4);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ (void)strcpy(argv[0], "-c$");
+ } else {
+ p = argv[0];
+ len = strlen(argv[0]);
+ MALLOC_NOMSG(NULL, argv[0], char *, len + 2);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ argv[0][0] = '-';
+ argv[0][1] = 'c';
+ (void)strcpy(argv[0] + 2, p + 1);
+ }
+ } else if (argv[0][0] == '-' && argv[0][1] == '\0') {
+ MALLOC_NOMSG(NULL, argv[0], char *, 3);
+ if (argv[0] == NULL)
+ err(1, NULL);
+ (void)strcpy(argv[0], "-s");
+ }
+}
+
+static void
+usage(is_ex)
+ int is_ex;
+{
+#define EX_USAGE \
+ "ex [-eFRrsv] [-c command] [-t tag] [-w size] [files ...]"
+#define VI_USAGE \
+ "vi [-eFRrv] [-c command] [-t tag] [-w size] [files ...]"
+
+ (void)fprintf(stderr, "usage: %s\n", is_ex ? EX_USAGE : VI_USAGE);
+ exit(1);
+}
diff --git a/usr.bin/vi/common/mark.c b/usr.bin/vi/common/mark.c
new file mode 100644
index 0000000..930ce6e
--- /dev/null
+++ b/usr.bin/vi/common/mark.c
@@ -0,0 +1,272 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mark.c 8.21 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+static LMARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T));
+
+/*
+ * Marks are maintained in a key sorted doubly linked list. We can't
+ * use arrays because we have no idea how big an index key could be.
+ * The underlying assumption is that users don't have more than, say,
+ * 10 marks at any one time, so this will be is fast enough.
+ *
+ * Marks are fixed, and modifications to the line don't update the mark's
+ * position in the line. This can be hard. If you add text to the line,
+ * place a mark in that text, undo the addition and use ` to move to the
+ * mark, the location will have disappeared. It's tempting to try to adjust
+ * the mark with the changes in the line, but this is hard to do, especially
+ * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi
+ * would move to the first non-blank on the line when the mark location was
+ * past the end of the line. This can be complicated by deleting to a mark
+ * that has disappeared using the ` command. Historic vi vi treated this as
+ * a line-mode motion and deleted the line. This implementation complains to
+ * the user.
+ *
+ * In historic vi, marks returned if the operation was undone, unless the
+ * mark had been subsequently reset. Tricky. This is hard to start with,
+ * but in the presence of repeated undo it gets nasty. When a line is
+ * deleted, we delete (and log) any marks on that line. An undo will create
+ * the mark. Any mark creations are noted as to whether the user created
+ * it or if it was created by an undo. The former cannot be reset by another
+ * undo, but the latter may.
+ *
+ * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of
+ * the absolute mark locations sets both, so that "m'" and "m`" work like
+ * they, ah, for lack of a better word, "should".
+ */
+
+/*
+ * mark_init --
+ * Set up the marks.
+ */
+int
+mark_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ LMARK *lmp;
+
+ /*
+ * Make sure the marks have been set up. If they
+ * haven't, do so, and create the absolute mark.
+ */
+ MALLOC_RET(sp, lmp, LMARK *, sizeof(LMARK));
+ lmp->lno = 1;
+ lmp->cno = 0;
+ lmp->name = ABSMARK1;
+ lmp->flags = 0;
+ LIST_INSERT_HEAD(&ep->marks, lmp, q);
+ return (0);
+}
+
+/*
+ * mark_end --
+ * Free up the marks.
+ */
+int
+mark_end(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ LMARK *lmp;
+
+ while ((lmp = ep->marks.lh_first) != NULL) {
+ LIST_REMOVE(lmp, q);
+ FREE(lmp, sizeof(LMARK));
+ }
+ return (0);
+}
+
+/*
+ * mark_get --
+ * Get the location referenced by a mark.
+ */
+int
+mark_get(sp, ep, key, mp)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+ MARK *mp;
+{
+ LMARK *lmp;
+ size_t len;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ lmp = mark_find(sp, ep, key);
+ if (lmp == NULL || lmp->name != key) {
+ msgq(sp, M_BERR, "Mark %s: not set", KEY_NAME(sp, key));
+ return (1);
+ }
+ if (F_ISSET(lmp, MARK_DELETED)) {
+ msgq(sp, M_BERR,
+ "Mark %s: the line was deleted", KEY_NAME(sp, key));
+ return (1);
+ }
+ if (file_gline(sp, ep, lmp->lno, &len) == NULL ||
+ lmp->cno > len || lmp->cno == len && len != 0) {
+ msgq(sp, M_BERR, "Mark %s: cursor position no longer exists",
+ KEY_NAME(sp, key));
+ return (1);
+ }
+ mp->lno = lmp->lno;
+ mp->cno = lmp->cno;
+ return (0);
+}
+
+/*
+ * mark_set --
+ * Set the location referenced by a mark.
+ */
+int
+mark_set(sp, ep, key, value, userset)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+ MARK *value;
+ int userset;
+{
+ LMARK *lmp, *lmt;
+
+ if (key == ABSMARK2)
+ key = ABSMARK1;
+
+ /*
+ * The rules are simple. If the user is setting a mark (if it's a
+ * new mark this is always true), it always happens. If not, it's
+ * an undo, and we set it if it's not already set or if it was set
+ * by a previous undo.
+ */
+ lmp = mark_find(sp, ep, key);
+ if (lmp == NULL || lmp->name != key) {
+ MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
+ if (lmp == NULL) {
+ LIST_INSERT_HEAD(&ep->marks, lmt, q);
+ } else
+ LIST_INSERT_AFTER(lmp, lmt, q);
+ lmp = lmt;
+ } else if (!userset &&
+ !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
+ return (0);
+
+ lmp->lno = value->lno;
+ lmp->cno = value->cno;
+ lmp->name = key;
+ lmp->flags = userset ? MARK_USERSET : 0;
+ return (0);
+}
+
+/*
+ * mark_find --
+ * Find the requested mark, or, the slot immediately before
+ * where it would go.
+ */
+static LMARK *
+mark_find(sp, ep, key)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T key;
+{
+ LMARK *lmp, *lastlmp;
+
+ /*
+ * Return the requested mark or the slot immediately before
+ * where it should go.
+ */
+ for (lastlmp = NULL, lmp = ep->marks.lh_first;
+ lmp != NULL; lastlmp = lmp, lmp = lmp->q.le_next)
+ if (lmp->name >= key)
+ return (lmp->name == key ? lmp : lastlmp);
+ return (lastlmp);
+}
+
+/*
+ * mark_insdel --
+ * Update the marks based on an insertion or deletion.
+ */
+void
+mark_insdel(sp, ep, op, lno)
+ SCR *sp;
+ EXF *ep;
+ enum operation op;
+ recno_t lno;
+{
+ LMARK *lmp;
+
+ switch (op) {
+ case LINE_APPEND:
+ return;
+ case LINE_DELETE:
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->lno >= lno)
+ if (lmp->lno == lno) {
+ F_SET(lmp, MARK_DELETED);
+ (void)log_mark(sp, ep, lmp);
+ } else
+ --lmp->lno;
+ return;
+ case LINE_INSERT:
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->lno >= lno)
+ ++lmp->lno;
+ return;
+ case LINE_RESET:
+ return;
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/common/mark.h b/usr.bin/vi/common/mark.h
new file mode 100644
index 0000000..2c42e50
--- /dev/null
+++ b/usr.bin/vi/common/mark.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mark.h 8.9 (Berkeley) 7/17/94
+ */
+
+/*
+ * The MARK and LMARK structures define positions in the file. There are
+ * two structures because the mark subroutines are the only places where
+ * anything cares about something other than line and column.
+ *
+ * Because of the different interfaces used by the db(3) package, curses,
+ * and users, the line number is 1 based and the column number is 0 based.
+ * Additionally, it is known that the out-of-band line number is less than
+ * any legal line number. The line number is of type recno_t, as that's
+ * the underlying type of the database. The column number is of type size_t,
+ * guaranteeing that we can malloc a line.
+ */
+struct _mark {
+#define OOBLNO 0 /* Out-of-band line number. */
+ recno_t lno; /* Line number. */
+ size_t cno; /* Column number. */
+};
+
+struct _lmark {
+ LIST_ENTRY(_lmark) q; /* Linked list of marks. */
+ recno_t lno; /* Line number. */
+ size_t cno; /* Column number. */
+ CHAR_T name; /* Mark name. */
+
+#define MARK_DELETED 0x01 /* Mark was deleted. */
+#define MARK_USERSET 0x02 /* User set this mark. */
+ u_int8_t flags;
+};
+
+#define ABSMARK1 '\'' /* Absolute mark name. */
+#define ABSMARK2 '`' /* Absolute mark name. */
+
+/* Mark routines. */
+int mark_end __P((SCR *, EXF *));
+int mark_get __P((SCR *, EXF *, ARG_CHAR_T, MARK *));
+int mark_init __P((SCR *, EXF *));
+void mark_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int mark_set __P((SCR *, EXF *, ARG_CHAR_T, MARK *, int));
diff --git a/usr.bin/vi/common/mem.h b/usr.bin/vi/common/mem.h
new file mode 100644
index 0000000..53aeb79
--- /dev/null
+++ b/usr.bin/vi/common/mem.h
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mem.h 8.8 (Berkeley) 8/16/94
+ */
+
+/* Increase the size of a malloc'd buffer. Two versions, one that
+ * returns, one that jumps to an error label.
+ */
+#define BINC_GOTO(sp, lp, llen, nlen) { \
+ void *__bincp; \
+ if ((nlen) > llen) { \
+ if ((__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
+ goto binc_err; \
+ /* \
+ * !!! \
+ * Possible pointer conversion. \
+ */ \
+ lp = __bincp; \
+ } \
+}
+#define BINC_RET(sp, lp, llen, nlen) { \
+ void *__bincp; \
+ if ((nlen) > llen) { \
+ if ((__bincp = binc(sp, lp, &(llen), nlen)) == NULL) \
+ return (1); \
+ /* \
+ * !!! \
+ * Possible pointer conversion. \
+ */ \
+ lp = __bincp; \
+ } \
+}
+
+/*
+ * Get some temporary space, preferably from the global temporary buffer,
+ * from a malloc'd buffer otherwise. Two versions, one that returns, one
+ * that jumps to an error label.
+ */
+#define GET_SPACE_GOTO(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (F_ISSET(__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_GOTO(sp, bp, blen, nlen); \
+ } else { \
+ BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } \
+}
+#define GET_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (F_ISSET(__gp, G_TMP_INUSE)) { \
+ bp = NULL; \
+ blen = 0; \
+ BINC_RET(sp, bp, blen, nlen); \
+ } else { \
+ BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } \
+}
+
+/*
+ * Add space to a GET_SPACE returned buffer. Two versions, one that
+ * returns, one that jumps to an error label.
+ */
+#define ADD_SPACE_GOTO(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (bp == __gp->tmp_bp) { \
+ F_CLR(__gp, G_TMP_INUSE); \
+ BINC_GOTO(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } else \
+ BINC_GOTO(sp, bp, blen, nlen); \
+}
+#define ADD_SPACE_RET(sp, bp, blen, nlen) { \
+ GS *__gp = (sp)->gp; \
+ if (bp == __gp->tmp_bp) { \
+ F_CLR(__gp, G_TMP_INUSE); \
+ BINC_RET(sp, __gp->tmp_bp, __gp->tmp_blen, nlen); \
+ bp = __gp->tmp_bp; \
+ blen = __gp->tmp_blen; \
+ F_SET(__gp, G_TMP_INUSE); \
+ } else \
+ BINC_RET(sp, bp, blen, nlen); \
+}
+
+/* Free memory, optionally making pointers unusable. */
+#ifdef DEBUG
+#define FREE(p, sz) { \
+ memset(p, 0xff, sz); \
+ free(p); \
+}
+#else
+#define FREE(p, sz) free(p);
+#endif
+
+/* Free a GET_SPACE returned buffer. */
+#define FREE_SPACE(sp, bp, blen) { \
+ if (bp == sp->gp->tmp_bp) \
+ F_CLR(sp->gp, G_TMP_INUSE); \
+ else \
+ FREE(bp, blen); \
+}
+
+/*
+ * Malloc a buffer, casting the return pointer. Various versions.
+ *
+ * !!!
+ * The cast should be unnecessary, malloc(3) and friends return void *'s,
+ * which is all we need. However, some systems that nvi needs to run on
+ * don't do it right yet, resulting in the compiler printing out roughly
+ * a million warnings. After awhile, it seemed easier to put the casts
+ * in instead of explaining it all the time.
+ */
+#define CALLOC_NOMSG(sp, p, cast, nmemb, size) { \
+ p = (cast)calloc(nmemb, size); \
+}
+#define CALLOC(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define CALLOC_RET(sp, p, cast, nmemb, size) { \
+ if ((p = (cast)calloc(nmemb, size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+#define MALLOC_NOMSG(sp, p, cast, size) { \
+ p = (cast)malloc(size); \
+}
+#define MALLOC(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+#define MALLOC_RET(sp, p, cast, size) { \
+ if ((p = (cast)malloc(size)) == NULL) { \
+ msgq(sp, M_SYSERR, NULL); \
+ return (1); \
+ } \
+}
+/*
+ * XXX
+ * Don't depend on realloc(NULL, size) working.
+ */
+#define REALLOC(sp, p, cast, size) { \
+ if ((p = (cast)(p == NULL ? \
+ malloc(size) : realloc(p, size))) == NULL) \
+ msgq(sp, M_SYSERR, NULL); \
+}
+
+/*
+ * Versions of memmove(3) and memset(3) that use the size of the
+ * initial pointer to figure out how much memory to manipulate.
+ */
+#define MEMMOVE(p, t, len) memmove(p, t, (len) * sizeof(*(p)))
+#define MEMSET(p, value, len) memset(p, value, (len) * sizeof(*(p)))
+
+void *binc __P((SCR *, void *, size_t *, size_t));
diff --git a/usr.bin/vi/common/msg.c b/usr.bin/vi/common/msg.c
new file mode 100644
index 0000000..36639c1
--- /dev/null
+++ b/usr.bin/vi/common/msg.c
@@ -0,0 +1,428 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)msg.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * msgq --
+ * Display a message.
+ */
+void
+#ifdef __STDC__
+msgq(SCR *sp, enum msgtype mt, const char *fmt, ...)
+#else
+msgq(sp, mt, fmt, va_alist)
+ SCR *sp;
+ enum msgtype mt;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ size_t len;
+ char msgbuf[1024];
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ /*
+ * It's possible to enter msg when there's no screen to hold
+ * the message. If sp is NULL, ignore the special cases and
+ * just build the message, using __global_list.
+ */
+ if (sp == NULL)
+ goto nullsp;
+
+ switch (mt) {
+ case M_BERR:
+ if (!F_ISSET(sp, S_EXSILENT) &&
+ F_ISSET(sp->gp, G_STDIN_TTY) && !O_ISSET(sp, O_VERBOSE)) {
+ F_SET(sp, S_BELLSCHED);
+ return;
+ }
+ mt = M_ERR;
+ break;
+ case M_VINFO:
+ if (!O_ISSET(sp, O_VERBOSE))
+ return;
+ mt = M_INFO;
+ /* FALLTHROUGH */
+ case M_INFO:
+ if (F_ISSET(sp, S_EXSILENT))
+ return;
+ break;
+ case M_ERR:
+ case M_SYSERR:
+ break;
+ default:
+ abort();
+ }
+
+nullsp: len = 0;
+
+#define EPREFIX "Error: "
+ if (mt == M_SYSERR) {
+ memmove(msgbuf, EPREFIX, sizeof(EPREFIX) - 1);
+ len += sizeof(EPREFIX) - 1;
+ }
+
+ if (sp != NULL && sp->if_name != NULL) {
+ len += snprintf(msgbuf + len, sizeof(msgbuf) - len,
+ "%s, %d: ", sp->if_name, sp->if_lno);
+ if (len >= sizeof(msgbuf))
+ goto err;
+ }
+
+ if (fmt != NULL) {
+ len += vsnprintf(msgbuf + len, sizeof(msgbuf) - len, fmt, ap);
+ if (len >= sizeof(msgbuf))
+ goto err;
+ }
+
+ if (mt == M_SYSERR) {
+ len += snprintf(msgbuf + len,
+ sizeof(msgbuf) - len, ": %s", strerror(errno));
+ if (len >= sizeof(msgbuf))
+ goto err;
+ }
+
+ /*
+ * If len >= the size, some characters were discarded.
+ * Ignore trailing nul.
+ */
+err: if (len >= sizeof(msgbuf))
+ len = sizeof(msgbuf) - 1;
+
+#ifdef DEBUG
+ if (sp != NULL)
+ TRACE(sp, "%.*s\n", len, msgbuf);
+#endif
+ msg_app(__global_list, sp, mt == M_ERR ? 1 : 0, msgbuf, len);
+}
+
+/*
+ * msg_app --
+ * Append a message into the queue. This can fail, but there's
+ * nothing we can do if it does.
+ */
+void
+msg_app(gp, sp, inv_video, p, len)
+ GS *gp;
+ SCR *sp;
+ int inv_video;
+ char *p;
+ size_t len;
+{
+ static int reenter; /* STATIC: Re-entrancy check. */
+ MSG *mp, *nmp;
+
+ /*
+ * It's possible to reenter msg when it allocates space.
+ * We're probably dead anyway, but no reason to drop core.
+ */
+ if (reenter)
+ return;
+ reenter = 1;
+
+ /*
+ * We can be entered as the result of a signal arriving, trying
+ * to sync the file and failing. This shouldn't be a hot spot,
+ * block the signals.
+ */
+ SIGBLOCK(gp);
+
+ /*
+ * Find an empty structure, or allocate a new one. Use the
+ * screen structure if it exists, otherwise the global one.
+ */
+ if (sp != NULL) {
+ if ((mp = sp->msgq.lh_first) == NULL) {
+ CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+ if (mp == NULL)
+ goto ret;
+ LIST_INSERT_HEAD(&sp->msgq, mp, q);
+ goto store;
+ }
+ } else if ((mp = gp->msgq.lh_first) == NULL) {
+ CALLOC(sp, mp, MSG *, 1, sizeof(MSG));
+ if (mp == NULL)
+ goto ret;
+ LIST_INSERT_HEAD(&gp->msgq, mp, q);
+ goto store;
+ }
+ while (!F_ISSET(mp, M_EMPTY) && mp->q.le_next != NULL)
+ mp = mp->q.le_next;
+ if (!F_ISSET(mp, M_EMPTY)) {
+ CALLOC(sp, nmp, MSG *, 1, sizeof(MSG));
+ if (nmp == NULL)
+ goto ret;
+ LIST_INSERT_AFTER(mp, nmp, q);
+ mp = nmp;
+ }
+
+ /* Get enough memory for the message. */
+store: if (len > mp->blen &&
+ (mp->mbuf = binc(sp, mp->mbuf, &mp->blen, len)) == NULL)
+ goto ret;
+
+ /* Store the message. */
+ memmove(mp->mbuf, p, len);
+ mp->len = len;
+ mp->flags = inv_video ? M_INV_VIDEO : 0;
+
+ret: reenter = 0;
+ SIGUNBLOCK(gp);
+}
+
+/*
+ * msg_rpt --
+ * Report on the lines that changed.
+ *
+ * !!!
+ * Historic vi documentation (USD:15-8) claimed that "The editor will also
+ * always tell you when a change you make affects text which you cannot see."
+ * This isn't true -- edit a large file and do "100d|1". We don't implement
+ * this semantic as it would require that we track each line that changes
+ * during a command instead of just keeping count.
+ *
+ * Line counts weren't right in historic vi, either. For example, given the
+ * file:
+ * abc
+ * def
+ * the command 2d}, from the 'b' would report that two lines were deleted,
+ * not one.
+ */
+int
+msg_rpt(sp, is_message)
+ SCR *sp;
+ int is_message;
+{
+ static char * const action[] = {
+ "added", "changed", "deleted", "joined", "moved",
+ "left shifted", "right shifted", "yanked",
+ NULL,
+ };
+ recno_t total;
+ u_long rptval;
+ int first, cnt;
+ size_t blen, len;
+ char * const *ap;
+ char *bp, *p, number[40];
+
+ if (F_ISSET(sp, S_EXSILENT))
+ return (0);
+
+ if ((rptval = O_VAL(sp, O_REPORT)) == 0)
+ goto norpt;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+ p = bp;
+
+ total = 0;
+ for (ap = action, cnt = 0, first = 1; *ap != NULL; ++ap, ++cnt)
+ if (sp->rptlines[cnt] != 0) {
+ total += sp->rptlines[cnt];
+ len = snprintf(number, sizeof(number),
+ "%s%lu lines %s",
+ first ? "" : "; ", sp->rptlines[cnt], *ap);
+ memmove(p, number, len);
+ p += len;
+ first = 0;
+ }
+
+ /*
+ * If nothing to report, return.
+ *
+ * !!!
+ * And now, a special vi clone test. Historically, vi reported if
+ * the number of changed lines was > than the value, not >=. Which
+ * means that users can't report on single line changes, btw.) In
+ * any case, if it was a yank command, it was >=, not >. No lie. I
+ * got complaints, so we do it right.
+ */
+ if (total > rptval || sp->rptlines[L_YANKED] >= rptval) {
+ *p = '\0';
+ if (is_message)
+ msgq(sp, M_INFO, "%s", bp);
+ else
+ ex_printf(EXCOOKIE, "%s\n", bp);
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ /* Clear after each report. */
+norpt: sp->rptlchange = OOBLNO;
+ memset(sp->rptlines, 0, sizeof(sp->rptlines));
+ return (0);
+}
+
+/*
+ * msg_status --
+ * Report on the file's status.
+ */
+int
+msg_status(sp, ep, lno, showlast)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ int showlast;
+{
+ recno_t last;
+ char *mo, *nc, *nf, *pid, *ro, *ul;
+#ifdef DEBUG
+ char pbuf[50];
+
+ (void)snprintf(pbuf, sizeof(pbuf), " (pid %u)", getpid());
+ pid = pbuf;
+#else
+ pid = "";
+#endif
+ /*
+ * See nvi/exf.c:file_init() for a description of how and
+ * when the read-only bit is set.
+ *
+ * !!!
+ * The historic display for "name changed" was "[Not edited]".
+ */
+ if (F_ISSET(sp->frp, FR_NEWFILE)) {
+ F_CLR(sp->frp, FR_NEWFILE);
+ nf = "new file";
+ mo = nc = "";
+ } else {
+ nf = "";
+ if (F_ISSET(sp->frp, FR_NAMECHANGE)) {
+ nc = "name changed";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ ", modified" : ", unmodified";
+ } else {
+ nc = "";
+ mo = F_ISSET(ep, F_MODIFIED) ?
+ "modified" : "unmodified";
+ }
+ }
+ ro = F_ISSET(sp->frp, FR_RDONLY) ? ", readonly" : "";
+ ul = F_ISSET(sp->frp, FR_UNLOCKED) ? ", UNLOCKED" : "";
+ if (showlast) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (last >= 1)
+ msgq(sp, M_INFO,
+ "%s: %s%s%s%s%s: line %lu of %lu [%ld%%]%s",
+ sp->frp->name, nf, nc, mo, ul, ro, lno,
+ last, (lno * 100) / last, pid);
+ else
+ msgq(sp, M_INFO, "%s: %s%s%s%s%s: empty file%s",
+ sp->frp->name, nf, nc, mo, ul, ro, pid);
+ } else
+ msgq(sp, M_INFO, "%s: %s%s%s%s%s: line %lu%s",
+ sp->frp->name, nf, nc, mo, ul, ro, lno, pid);
+ return (0);
+}
+
+#ifdef MSG_CATALOG
+/*
+ * get_msg --
+ * Return a format based on a message number.
+ */
+char *
+get_msg(sp, msgno)
+ SCR *sp;
+ char *s_msgno;
+{
+ DBT data, key;
+ GS *gp;
+ recno_t msgno;
+ char *msg, *p;
+
+ gp = sp == NULL ? __global_list : sp->gp;
+ if (gp->msgdb == NULL) {
+ p = sp == NULL ? _PATH_MSGDEF : O_STR(sp, O_CATALOG);
+ if ((gp->msgdb = dbopen(p,
+ O_NONBLOCK | O_RDONLY, 444, DB_RECNO, NULL)) == NULL) {
+ if ((fmt = malloc(256)) == NULL)
+ return ("");
+ (void)snprintf(fmt,
+ "unable to open %s: %s", p, strerror(errno));
+ return (fmt);
+ }
+ }
+ msgno = atoi(s_msgno);
+ key.data = &msgno;
+ key.size = sizeof(recno_t);
+ switch (gp->msgdb->get(gp->msgdb, &key, &data, 0)) {
+ case 0:
+ return (data.data);
+ case 1:
+ p = "no catalog record %ls";
+ break;
+ case -1:
+ p = "catalog record %s: %s";
+ break;
+ }
+ if ((fmt = malloc(256)) == NULL)
+ return ("");
+ (void)snprintf(fmt, p, msgno, strerror(errno));
+ return (fmt);
+}
+#endif
diff --git a/usr.bin/vi/common/msg.h b/usr.bin/vi/common/msg.h
new file mode 100644
index 0000000..fc6e365
--- /dev/null
+++ b/usr.bin/vi/common/msg.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)msg.h 8.13 (Berkeley) 8/8/94
+ */
+
+/*
+ * Message types.
+ *
+ * !!!
+ * In historical vi, O_VERBOSE didn't exist, and O_TERSE made the error
+ * messages shorter. In this implementation, O_TERSE has no effect and
+ * O_VERBOSE results in informational displays about common errors for
+ * naive users.
+ *
+ * M_BERR Error: M_ERR if O_VERBOSE, else bell.
+ * M_ERR Error: Display in inverse video.
+ * M_INFO Info: Display in normal video.
+ * M_SYSERR Error: M_ERR, using strerror(3) message.
+ * M_VINFO Info: M_INFO if O_VERBOSE, else ignore.
+ */
+enum msgtype { M_BERR, M_ERR, M_INFO, M_SYSERR, M_VINFO };
+
+typedef struct _msgh MSGH; /* MESG list head structure. */
+LIST_HEAD(_msgh, _msg);
+
+struct _msg {
+ LIST_ENTRY(_msg) q; /* Linked list of messages. */
+ char *mbuf; /* Message buffer. */
+ size_t blen; /* Message buffer length. */
+ size_t len; /* Message length. */
+
+#define M_EMPTY 0x01 /* No message. */
+#define M_INV_VIDEO 0x02 /* Inverse video. */
+ u_int8_t flags;
+};
+
+/*
+ * Define MSG_CATALOG for the Makefile compile command
+ * line to enable message catalogs.
+ */
+#ifdef MSG_CATALOG
+#define M(number, fmt) number
+char *get_msg __P((SCR *, char *));
+#else
+#define M(number, fmt) fmt
+#endif
+
+/* Messages. */
+void msg_app __P((GS *, SCR *, int, char *, size_t));
+int msg_rpt __P((SCR *, int));
+int msg_status __P((SCR *, EXF *, recno_t, int));
+void msgq __P((SCR *, enum msgtype, const char *, ...));
diff --git a/usr.bin/vi/common/options.awk b/usr.bin/vi/common/options.awk
new file mode 100644
index 0000000..a6755d9
--- /dev/null
+++ b/usr.bin/vi/common/options.awk
@@ -0,0 +1,9 @@
+# @(#)options.awk 8.1 (Berkeley) 4/17/94
+
+/^\/\* O_[0-9A-Z_]*/ {
+ printf("#define %s %d\n", $2, cnt++);
+ next;
+}
+END {
+ printf("#define O_OPTIONCOUNT %d\n", cnt);
+}
diff --git a/usr.bin/vi/common/options.c b/usr.bin/vi/common/options.c
new file mode 100644
index 0000000..61396a3
--- /dev/null
+++ b/usr.bin/vi/common/options.c
@@ -0,0 +1,890 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options.c 8.66 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static int opts_abbcmp __P((const void *, const void *));
+static int opts_cmp __P((const void *, const void *));
+static OPTLIST const *opts_prefix __P((char *));
+static int opts_print __P((SCR *, OPTLIST const *));
+
+/*
+ * O'Reilly noted options and abbreviations are from "Learning the VI Editor",
+ * Fifth Edition, May 1992. There's no way of knowing what systems they are
+ * actually from.
+ *
+ * HPUX noted options and abbreviations are from "The Ultimate Guide to the
+ * VI and EX Text Editors", 1990.
+ */
+static OPTLIST const optlist[] = {
+/* O_ALTWERASE 4.4BSD */
+ {"altwerase", f_altwerase, OPT_0BOOL, 0},
+/* O_AUTOINDENT 4BSD */
+ {"autoindent", NULL, OPT_0BOOL, 0},
+/* O_AUTOPRINT 4BSD */
+ {"autoprint", NULL, OPT_1BOOL, 0},
+/* O_AUTOWRITE 4BSD */
+ {"autowrite", NULL, OPT_0BOOL, 0},
+/* O_BEAUTIFY 4BSD */
+ {"beautify", NULL, OPT_0BOOL, 0},
+/* O_CDPATH 4.4BSD */
+ {"cdpath", f_cdpath, OPT_STR, 0},
+/* O_COLUMNS 4.4BSD */
+ {"columns", f_columns, OPT_NUM, OPT_NOSAVE},
+/* O_COMMENT 4.4BSD */
+ {"comment", NULL, OPT_0BOOL, 0},
+/* O_DIGRAPH XXX: Elvis */
+ {"digraph", NULL, OPT_0BOOL, 0},
+/* O_DIRECTORY 4BSD */
+ {"directory", NULL, OPT_STR, 0},
+/* O_EDCOMPATIBLE 4BSD */
+ {"edcompatible",NULL, OPT_0BOOL, 0},
+/* O_ERRORBELLS 4BSD */
+ {"errorbells", NULL, OPT_0BOOL, 0},
+/* O_EXRC System V (undocumented) */
+ {"exrc", NULL, OPT_0BOOL, 0},
+/* O_EXTENDED 4.4BSD */
+ {"extended", NULL, OPT_0BOOL, 0},
+/* O_FLASH HPUX */
+ {"flash", NULL, OPT_1BOOL, 0},
+/* O_HARDTABS 4BSD */
+ {"hardtabs", NULL, OPT_NUM, 0},
+/* O_IGNORECASE 4BSD */
+ {"ignorecase", NULL, OPT_0BOOL, 0},
+/* O_KEYTIME 4.4BSD */
+ {"keytime", NULL, OPT_NUM, 0},
+/* O_LEFTRIGHT 4.4BSD */
+ {"leftright", f_leftright, OPT_0BOOL, 0},
+/* O_LINES 4.4BSD */
+ {"lines", f_lines, OPT_NUM, OPT_NOSAVE},
+/* O_LISP 4BSD */
+/*
+ * XXX
+ * When the lisp option is implemented, delete
+ * the OPT_NOSAVE flag, so that :mkexrc dumps it.
+ */
+ {"lisp", f_lisp, OPT_0BOOL, OPT_NOSAVE},
+/* O_LIST 4BSD */
+ {"list", f_list, OPT_0BOOL, 0},
+/* O_MAGIC 4BSD */
+ {"magic", NULL, OPT_1BOOL, 0},
+/* O_MATCHTIME 4.4BSD */
+ {"matchtime", NULL, OPT_NUM, 0},
+/* O_MESG 4BSD */
+ {"mesg", f_mesg, OPT_1BOOL, 0},
+/* O_META 4.4BSD */
+ {"meta", NULL, OPT_STR, 0},
+/* O_MODELINE 4BSD */
+ {"modeline", f_modeline, OPT_0BOOL, 0},
+/* O_NUMBER 4BSD */
+ {"number", f_number, OPT_0BOOL, 0},
+/* O_OCTAL 4.4BSD */
+ {"octal", f_octal, OPT_0BOOL, 0},
+/* O_OPEN 4BSD */
+ {"open", NULL, OPT_1BOOL, 0},
+/* O_OPTIMIZE 4BSD */
+ {"optimize", NULL, OPT_1BOOL, 0},
+/* O_PARAGRAPHS 4BSD */
+ {"paragraphs", f_paragraph, OPT_STR, 0},
+/* O_PROMPT 4BSD */
+ {"prompt", NULL, OPT_1BOOL, 0},
+/* O_READONLY 4BSD (undocumented) */
+ {"readonly", f_readonly, OPT_0BOOL, 0},
+/* O_RECDIR 4.4BSD */
+ {"recdir", NULL, OPT_STR, 0},
+/* O_REDRAW 4BSD */
+ {"redraw", NULL, OPT_0BOOL, 0},
+/* O_REMAP 4BSD */
+ {"remap", NULL, OPT_1BOOL, 0},
+/* O_REPORT 4BSD */
+ {"report", NULL, OPT_NUM, OPT_NOSTR},
+/* O_RULER 4.4BSD */
+ {"ruler", NULL, OPT_0BOOL, 0},
+/* O_SCROLL 4BSD */
+ {"scroll", NULL, OPT_NUM, 0},
+/* O_SECTIONS 4BSD */
+ {"sections", f_section, OPT_STR, 0},
+/* O_SHELL 4BSD */
+ {"shell", NULL, OPT_STR, 0},
+/* O_SHIFTWIDTH 4BSD */
+ {"shiftwidth", f_shiftwidth, OPT_NUM, 0},
+/* O_SHOWDIRTY 4.4BSD */
+ {"showdirty", NULL, OPT_0BOOL, 0},
+/* O_SHOWMATCH 4BSD */
+ {"showmatch", NULL, OPT_0BOOL, 0},
+/* O_SHOWMODE 4.4BSD */
+ {"showmode", NULL, OPT_0BOOL, 0},
+/* O_SIDESCROLL 4.4BSD */
+ {"sidescroll", NULL, OPT_NUM, 0},
+/* O_SLOWOPEN 4BSD */
+ {"slowopen", NULL, OPT_0BOOL, 0},
+/* O_SOURCEANY 4BSD (undocumented) */
+ {"sourceany", f_sourceany, OPT_0BOOL, 0},
+/* O_TABSTOP 4BSD */
+ {"tabstop", f_tabstop, OPT_NUM, 0},
+/* O_TAGLENGTH 4BSD */
+ {"taglength", NULL, OPT_NUM, OPT_NOSTR},
+/* O_TAGS 4BSD */
+ {"tags", f_tags, OPT_STR, 0},
+/* O_TERM 4BSD */
+ {"term", f_term, OPT_STR, OPT_NOSAVE},
+/* O_TERSE 4BSD */
+ {"terse", NULL, OPT_0BOOL, 0},
+/* O_TILDEOP 4.4BSD */
+ {"tildeop", NULL, OPT_0BOOL, 0},
+/* O_TIMEOUT 4BSD (undocumented) */
+ {"timeout", NULL, OPT_1BOOL, 0},
+/* O_TTYWERASE 4.4BSD */
+ {"ttywerase", f_ttywerase, OPT_0BOOL, 0},
+/* O_VERBOSE 4.4BSD */
+ {"verbose", NULL, OPT_0BOOL, 0},
+/* O_W1200 4BSD */
+ {"w1200", f_w1200, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
+/* O_W300 4BSD */
+ {"w300", f_w300, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
+/* O_W9600 4BSD */
+ {"w9600", f_w9600, OPT_NUM, OPT_NEVER|OPT_NOSAVE},
+/* O_WARN 4BSD */
+ {"warn", NULL, OPT_1BOOL, 0},
+/* O_WINDOW 4BSD */
+ {"window", f_window, OPT_NUM, 0},
+/* O_WRAPMARGIN 4BSD */
+ {"wrapmargin", NULL, OPT_NUM, OPT_NOSTR},
+/* O_WRAPSCAN 4BSD */
+ {"wrapscan", NULL, OPT_1BOOL, 0},
+/* O_WRITEANY 4BSD */
+ {"writeany", NULL, OPT_0BOOL, 0},
+ {NULL},
+};
+
+typedef struct abbrev {
+ char *name;
+ int offset;
+} OABBREV;
+
+static OABBREV const abbrev[] = {
+ {"ai", O_AUTOINDENT}, /* 4BSD */
+ {"ap", O_AUTOPRINT}, /* 4BSD */
+ {"aw", O_AUTOWRITE}, /* 4BSD */
+ {"bf", O_BEAUTIFY}, /* 4BSD */
+ {"co", O_COLUMNS}, /* 4.4BSD */
+ {"dir", O_DIRECTORY}, /* 4BSD */
+ {"eb", O_ERRORBELLS}, /* 4BSD */
+ {"ed", O_EDCOMPATIBLE}, /* 4BSD */
+ {"ex", O_EXRC}, /* System V (undocumented) */
+ {"ht", O_HARDTABS}, /* 4BSD */
+ {"ic", O_IGNORECASE}, /* 4BSD */
+ {"li", O_LINES}, /* 4.4BSD */
+ {"modelines", O_MODELINE}, /* HPUX */
+ {"nu", O_NUMBER}, /* 4BSD */
+ {"opt", O_OPTIMIZE}, /* 4BSD */
+ {"para", O_PARAGRAPHS}, /* 4BSD */
+ {"re", O_REDRAW}, /* O'Reilly */
+ {"ro", O_READONLY}, /* 4BSD (undocumented) */
+ {"scr", O_SCROLL}, /* 4BSD (undocumented) */
+ {"sect", O_SECTIONS}, /* O'Reilly */
+ {"sh", O_SHELL}, /* 4BSD */
+ {"slow", O_SLOWOPEN}, /* 4BSD */
+ {"sm", O_SHOWMATCH}, /* 4BSD */
+ {"sw", O_SHIFTWIDTH}, /* 4BSD */
+ {"tag", O_TAGS}, /* 4BSD (undocumented) */
+ {"tl", O_TAGLENGTH}, /* 4BSD */
+ {"to", O_TIMEOUT}, /* 4BSD (undocumented) */
+ {"ts", O_TABSTOP}, /* 4BSD */
+ {"tty", O_TERM}, /* 4BSD (undocumented) */
+ {"ttytype", O_TERM}, /* 4BSD (undocumented) */
+ {"w", O_WINDOW}, /* O'Reilly */
+ {"wa", O_WRITEANY}, /* 4BSD */
+ {"wi", O_WINDOW}, /* 4BSD (undocumented) */
+ {"wm", O_WRAPMARGIN}, /* 4BSD */
+ {"ws", O_WRAPSCAN}, /* 4BSD */
+ {NULL},
+};
+
+/*
+ * opts_init --
+ * Initialize some of the options. Since the user isn't really
+ * "setting" these variables, don't set their OPT_SET bits.
+ */
+int
+opts_init(sp)
+ SCR *sp;
+{
+ ARGS *argv[2], a, b;
+ OPTLIST const *op;
+ u_long v;
+ int cnt;
+ char *s, b1[1024];
+
+ a.bp = b1;
+ a.len = 0;
+ b.bp = NULL;
+ b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;
+
+#define SET_DEF(opt, str) { \
+ if (str != b1) /* GCC puts strings in text-space. */ \
+ (void)strcpy(b1, str); \
+ a.len = strlen(b1); \
+ if (opts_set(sp, NULL, argv)) { \
+ msgq(sp, M_ERR, \
+ "Unable to set default %s option", optlist[opt]); \
+ return (1); \
+ } \
+ F_CLR(&sp->opts[opt], OPT_SET); \
+}
+ /* Set default values. */
+ for (op = optlist, cnt = 0; op->name != NULL; ++op, ++cnt)
+ if (op->type == OPT_0BOOL)
+ O_CLR(sp, cnt);
+ else if (op->type == OPT_1BOOL)
+ O_SET(sp, cnt);
+
+ (void)snprintf(b1, sizeof(b1), "cdpath=%s",
+ (s = getenv("CDPATH")) == NULL ? ":" : s);
+ SET_DEF(O_CDPATH, b1);
+
+ /*
+ * !!!
+ * Vi historically stored temporary files in /var/tmp. We store them
+ * in /tmp by default, hoping it's a memory based file system. There
+ * are two ways to change this -- the user can set either the directory
+ * option or the TMPDIR environmental variable.
+ */
+ (void)snprintf(b1, sizeof(b1), "directory=%s",
+ (s = getenv("TMPDIR")) == NULL ? _PATH_TMP : s);
+ SET_DEF(O_DIRECTORY, b1);
+ SET_DEF(O_KEYTIME, "keytime=6");
+ SET_DEF(O_MATCHTIME, "matchtime=7");
+ SET_DEF(O_META, "meta=~{[*?$`'\"\\");
+ SET_DEF(O_REPORT, "report=5");
+ SET_DEF(O_PARAGRAPHS, "paragraphs=IPLPPPQPP LIpplpipbp");
+ (void)snprintf(b1, sizeof(b1), "recdir=%s", _PATH_PRESERVE);
+ SET_DEF(O_RECDIR, b1);
+ SET_DEF(O_SECTIONS, "sections=NHSHH HUnhsh");
+ (void)snprintf(b1, sizeof(b1), "shell=%s",
+ (s = getenv("SHELL")) == NULL ? _PATH_BSHELL : s);
+ SET_DEF(O_SHELL, b1);
+ SET_DEF(O_SHIFTWIDTH, "shiftwidth=8");
+ SET_DEF(O_SIDESCROLL, "sidescroll=16");
+ SET_DEF(O_TABSTOP, "tabstop=8");
+ (void)snprintf(b1, sizeof(b1), "tags=%s", _PATH_TAGS);
+ SET_DEF(O_TAGS, b1);
+ (void)snprintf(b1, sizeof(b1), "term=%s",
+ (s = getenv("TERM")) == NULL ? "unknown" : s);
+ SET_DEF(O_TERM, b1);
+
+ /*
+ * XXX
+ * Initialize ^D, ^U scrolling value here, after TERM. (We didn't
+ * have the options information when the screen was initialized.)
+ * Initializing term should have created a LINES/COLUMNS value.
+ */
+ sp->defscroll = O_VAL(sp, O_LINES) / 2;
+ (void)snprintf(b1, sizeof(b1), "scroll=%ld", sp->defscroll);
+ SET_DEF(O_SCROLL, b1);
+
+ /*
+ * The default window option values are:
+ * 8 if baud rate <= 600
+ * 16 if baud rate <= 1200
+ * LINES - 1 if baud rate > 1200
+ */
+ v = baud_from_bval(sp);
+ if (v <= 600)
+ v = 8;
+ else if (v <= 1200)
+ v = 16;
+ else
+ v = O_VAL(sp, O_LINES) - 1;
+ (void)snprintf(b1, sizeof(b1), "window=%lu", v);
+ SET_DEF(O_WINDOW, b1);
+
+ SET_DEF(O_WRAPMARGIN, "wrapmargin=0");
+
+ /*
+ * By default, the historic vi always displayed information
+ * about two options, redraw and term. Term seems sufficient.
+ */
+ F_SET(&sp->opts[O_TERM], OPT_SET);
+ return (0);
+}
+
+/*
+ * opts_set --
+ * Change the values of one or more options.
+ */
+int
+opts_set(sp, usage, argv)
+ SCR *sp;
+ char *usage;
+ ARGS *argv[];
+{
+ enum optdisp disp;
+ OABBREV atmp, *ap;
+ OPTLIST const *op;
+ OPTLIST otmp;
+ OPTION *spo;
+ u_long value, turnoff;
+ int ch, equals, offset, qmark, rval;
+ char *endp, *name, *p, *sep;
+
+ disp = NO_DISPLAY;
+ for (rval = 0; argv[0]->len != 0; ++argv) {
+ /*
+ * The historic vi dumped the options for each occurrence of
+ * "all" in the set list. Puhleeze.
+ */
+ if (!strcmp(argv[0]->bp, "all")) {
+ disp = ALL_DISPLAY;
+ continue;
+ }
+
+ /* Find equals sign or question mark. */
+ for (sep = NULL, equals = qmark = 0,
+ p = name = argv[0]->bp; (ch = *p) != '\0'; ++p)
+ if (ch == '=' || ch == '?') {
+ if (p == name) {
+ if (usage != NULL)
+ msgq(sp,
+ M_ERR, "Usage: %s", usage);
+ return (1);
+ }
+ sep = p;
+ if (ch == '=')
+ equals = 1;
+ else
+ qmark = 1;
+ break;
+ }
+
+ turnoff = 0;
+ op = NULL;
+ if (sep != NULL)
+ *sep++ = '\0';
+
+ /* Check list of abbreviations. */
+ atmp.name = name;
+ if ((ap = bsearch(&atmp, abbrev,
+ sizeof(abbrev) / sizeof(OABBREV) - 1,
+ sizeof(OABBREV), opts_abbcmp)) != NULL) {
+ op = optlist + ap->offset;
+ goto found;
+ }
+
+ /* Check list of options. */
+ otmp.name = name;
+ if ((op = bsearch(&otmp, optlist,
+ sizeof(optlist) / sizeof(OPTLIST) - 1,
+ sizeof(OPTLIST), opts_cmp)) != NULL)
+ goto found;
+
+ /* Try the name without any leading "no". */
+ if (name[0] == 'n' && name[1] == 'o') {
+ turnoff = 1;
+ name += 2;
+ } else
+ goto prefix;
+
+ /* Check list of abbreviations. */
+ atmp.name = name;
+ if ((ap = bsearch(&atmp, abbrev,
+ sizeof(abbrev) / sizeof(OABBREV) - 1,
+ sizeof(OABBREV), opts_abbcmp)) != NULL) {
+ op = optlist + ap->offset;
+ goto found;
+ }
+
+ /* Check list of options. */
+ otmp.name = name;
+ if ((op = bsearch(&otmp, optlist,
+ sizeof(optlist) / sizeof(OPTLIST) - 1,
+ sizeof(OPTLIST), opts_cmp)) != NULL)
+ goto found;
+
+ /* Check for prefix match. */
+prefix: op = opts_prefix(name);
+
+found: if (op == NULL) {
+ msgq(sp, M_ERR,
+ "no %s option: 'set all' gives all option values",
+ name);
+ continue;
+ }
+
+ /* Find current option values. */
+ offset = op - optlist;
+ spo = sp->opts + offset;
+
+ /*
+ * !!!
+ * Historically, the question mark could be a separate
+ * argument.
+ */
+ if (!equals && !qmark &&
+ argv[1]->len == 1 && argv[1]->bp[0] == '?') {
+ ++argv;
+ qmark = 1;
+ }
+
+ /* Set name, value. */
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (equals) {
+ msgq(sp, M_ERR,
+ "set: [no]%s option doesn't take a value",
+ name);
+ break;
+ }
+ if (qmark) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+ if (op->func != NULL) {
+ if (op->func(sp, spo, NULL, turnoff)) {
+ rval = 1;
+ break;
+ }
+ } else if (turnoff)
+ O_CLR(sp, offset);
+ else
+ O_SET(sp, offset);
+ goto change;
+ case OPT_NUM:
+ /*
+ * !!!
+ * Extension to historic vi. If the OPT_NOSTR flag is
+ * set, a numeric option may be turned off by using a
+ * "no" prefix, e.g. "nowrapmargin". (We assume that
+ * setting the value to 0 turns a numeric option off.)
+ */
+ if (turnoff) {
+ if (F_ISSET(op, OPT_NOSTR)) {
+ value = 0;
+ goto nostr;
+ }
+ msgq(sp, M_ERR,
+ "set: %s option isn't a boolean", name);
+ break;
+ }
+ if (qmark || !equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+ value = strtol(sep, &endp, 10);
+ if (*endp && !isblank(*endp)) {
+ msgq(sp, M_ERR,
+ "set %s: illegal number %s", name, sep);
+ break;
+ }
+nostr: if (op->func != NULL) {
+ if (op->func(sp, spo, sep, value)) {
+ rval = 1;
+ break;
+ }
+ } else
+ O_VAL(sp, offset) = value;
+ goto change;
+ case OPT_STR:
+ if (turnoff) {
+ msgq(sp, M_ERR,
+ "set: %s option isn't a boolean", name);
+ break;
+ }
+ if (qmark || !equals) {
+ if (!disp)
+ disp = SELECT_DISPLAY;
+ F_SET(spo, OPT_SELECTED);
+ break;
+ }
+ if (op->func != NULL) {
+ if (op->func(sp, spo, sep, (u_long)0)) {
+ rval = 1;
+ break;
+ }
+ } else {
+ if (F_ISSET(&sp->opts[offset], OPT_ALLOCATED))
+ free(O_STR(sp, offset));
+ if ((O_STR(sp, offset) = strdup(sep)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ rval = 1;
+ break;
+ } else
+ F_SET(&sp->opts[offset], OPT_ALLOCATED);
+ }
+change: if (sp->s_optchange != NULL)
+ (void)sp->s_optchange(sp, offset);
+ F_SET(&sp->opts[offset], OPT_SET);
+ break;
+ default:
+ abort();
+ }
+ }
+ if (disp != NO_DISPLAY)
+ opts_dump(sp, disp);
+ return (rval);
+}
+
+/*
+ * opts_dump --
+ * List the current values of selected options.
+ */
+void
+opts_dump(sp, type)
+ SCR *sp;
+ enum optdisp type;
+{
+ OPTLIST const *op;
+ int base, b_num, cnt, col, colwidth, curlen, s_num;
+ int numcols, numrows, row;
+ int b_op[O_OPTIONCOUNT], s_op[O_OPTIONCOUNT];
+ char nbuf[20];
+
+ /*
+ * XXX
+ * It's possible to get here by putting "set option" in the
+ * .exrc file. I can't think of a clean way to layer this,
+ * or a reasonable check to make, so we block it here.
+ */
+ if (sp->stdfp == NULL) {
+ msgq(sp, M_ERR,
+ "Option display requires that the screen be initialized");
+ return;
+ }
+
+ /*
+ * Options are output in two groups -- those that fit in a column and
+ * those that don't. Output is done on 6 character "tab" boundaries
+ * for no particular reason. (Since we don't output tab characters,
+ * we can ignore the terminal's tab settings.) Ignore the user's tab
+ * setting because we have no idea how reasonable it is.
+ *
+ * Find a column width we can live with.
+ */
+ for (cnt = 6; cnt > 1; --cnt) {
+ colwidth = (sp->cols - 1) / cnt & ~(STANDARD_TAB - 1);
+ if (colwidth >= 10) {
+ colwidth =
+ (colwidth + STANDARD_TAB) & ~(STANDARD_TAB - 1);
+ break;
+ }
+ colwidth = 0;
+ }
+
+ /*
+ * Get the set of options to list, entering them into
+ * the column list or the overflow list.
+ */
+ for (b_num = s_num = 0, op = optlist; op->name != NULL; ++op) {
+ cnt = op - optlist;
+
+ /* If OPT_NEVER set, it's never displayed. */
+ if (F_ISSET(op, OPT_NEVER))
+ continue;
+
+ switch (type) {
+ case ALL_DISPLAY: /* Display all. */
+ break;
+ case CHANGED_DISPLAY: /* Display changed. */
+ if (!F_ISSET(&sp->opts[cnt], OPT_SET))
+ continue;
+ break;
+ case SELECT_DISPLAY: /* Display selected. */
+ if (!F_ISSET(&sp->opts[cnt], OPT_SELECTED))
+ continue;
+ break;
+ default:
+ case NO_DISPLAY:
+ abort();
+ /* NOTREACHED */
+ }
+ F_CLR(&sp->opts[cnt], OPT_SELECTED);
+
+ curlen = strlen(op->name);
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (!O_ISSET(sp, cnt))
+ curlen += 2;
+ break;
+ case OPT_NUM:
+ (void)snprintf(nbuf,
+ sizeof(nbuf), "%ld", O_VAL(sp, cnt));
+ curlen += strlen(nbuf);
+ break;
+ case OPT_STR:
+ curlen += strlen(O_STR(sp, cnt)) + 3;
+ break;
+ }
+ /* Offset by two so there's a gap. */
+ if (curlen < colwidth - 2)
+ s_op[s_num++] = cnt;
+ else
+ b_op[b_num++] = cnt;
+ }
+
+ if (s_num > 0) {
+ /* Figure out the number of columns. */
+ numcols = (sp->cols - 1) / colwidth;
+ if (s_num > numcols) {
+ numrows = s_num / numcols;
+ if (s_num % numcols)
+ ++numrows;
+ } else
+ numrows = 1;
+
+ /* Display the options in sorted order. */
+ for (row = 0; row < numrows;) {
+ for (base = row, col = 0; col < numcols; ++col) {
+ cnt = opts_print(sp, &optlist[s_op[base]]);
+ if ((base += numrows) >= s_num)
+ break;
+ (void)ex_printf(EXCOOKIE,
+ "%*s", (int)(colwidth - cnt), "");
+ }
+ if (++row < numrows || b_num)
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ }
+
+ for (row = 0; row < b_num;) {
+ (void)opts_print(sp, &optlist[b_op[row]]);
+ if (++row < b_num)
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+}
+
+/*
+ * opts_print --
+ * Print out an option.
+ */
+static int
+opts_print(sp, op)
+ SCR *sp;
+ OPTLIST const *op;
+{
+ int curlen, offset;
+
+ curlen = 0;
+ offset = op - optlist;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ curlen += ex_printf(EXCOOKIE,
+ "%s%s", O_ISSET(sp, offset) ? "" : "no", op->name);
+ break;
+ case OPT_NUM:
+ curlen += ex_printf(EXCOOKIE,
+ "%s=%ld", op->name, O_VAL(sp, offset));
+ break;
+ case OPT_STR:
+ curlen += ex_printf(EXCOOKIE,
+ "%s=\"%s\"", op->name, O_STR(sp, offset));
+ break;
+ }
+ return (curlen);
+}
+
+/*
+ * opts_save --
+ * Write the current configuration to a file.
+ */
+int
+opts_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ OPTLIST const *op;
+ int ch, cnt;
+ char *p;
+
+ for (op = optlist; op->name != NULL; ++op) {
+ if (F_ISSET(op, OPT_NOSAVE))
+ continue;
+ cnt = op - optlist;
+ switch (op->type) {
+ case OPT_0BOOL:
+ case OPT_1BOOL:
+ if (O_ISSET(sp, cnt))
+ (void)fprintf(fp, "set %s\n", op->name);
+ else
+ (void)fprintf(fp, "set no%s\n", op->name);
+ break;
+ case OPT_NUM:
+ (void)fprintf(fp,
+ "set %s=%-3d\n", op->name, O_VAL(sp, cnt));
+ break;
+ case OPT_STR:
+ (void)fprintf(fp, "set ");
+ for (p = op->name; (ch = *p) != '\0'; ++p) {
+ if (isblank(ch) || ch == '\\')
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('=', fp);
+ for (p = O_STR(sp, cnt); (ch = *p) != '\0'; ++p) {
+ if (isblank(ch) || ch == '\\')
+ (void)putc('\\', fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ break;
+ }
+ if (ferror(fp)) {
+ msgq(sp, M_ERR, "I/O error: %s", strerror(errno));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * opts_prefix --
+ * Check to see if the name is the prefix of one (and only one)
+ * option. If so, return the option.
+ */
+static OPTLIST const *
+opts_prefix(name)
+ char *name;
+{
+ OPTLIST const *op, *save_op;
+ size_t len;
+
+ save_op = NULL;
+ len = strlen(name);
+ for (op = optlist; op->name != NULL; ++op) {
+ if (op->name[0] < name[0])
+ continue;
+ if (op->name[0] > name[0])
+ break;
+ if (!memcmp(op->name, name, len)) {
+ if (save_op != NULL)
+ return (NULL);
+ save_op = op;
+ }
+ }
+ return (save_op);
+}
+
+static int
+opts_abbcmp(a, b)
+ const void *a, *b;
+{
+ return(strcmp(((OABBREV *)a)->name, ((OABBREV *)b)->name));
+}
+
+static int
+opts_cmp(a, b)
+ const void *a, *b;
+{
+ return(strcmp(((OPTLIST *)a)->name, ((OPTLIST *)b)->name));
+}
+
+/*
+ * opts_free --
+ * Free all option strings
+ */
+void
+opts_free(sp)
+ SCR *sp;
+{
+ int cnt;
+ char *p;
+
+ for (cnt = 0; cnt < O_OPTIONCOUNT; ++cnt)
+ if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED)) {
+ p = O_STR(sp, cnt);
+ FREE(p, strlen(p) + 1);
+ }
+}
+
+/*
+ * opts_copy --
+ * Copy a screen's OPTION array.
+ */
+int
+opts_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ OPTION *op;
+ int cnt;
+
+ /* Copy most everything without change. */
+ memmove(sp->opts, orig->opts, sizeof(orig->opts));
+
+ /*
+ * Allocate copies of the strings -- keep trying to reallocate
+ * after ENOMEM failure, otherwise end up with more than one
+ * screen referencing the original memory.
+ */
+ for (op = sp->opts, cnt = 0; cnt < O_OPTIONCOUNT; ++cnt, ++op)
+ if (F_ISSET(&sp->opts[cnt], OPT_ALLOCATED) &&
+ (O_STR(sp, cnt) = strdup(O_STR(sp, cnt))) == NULL) {
+ msgq(orig, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/common/options.h.stub b/usr.bin/vi/common/options.h.stub
new file mode 100644
index 0000000..0e2051d
--- /dev/null
+++ b/usr.bin/vi/common/options.h.stub
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)options.h.stub 8.22 (Berkeley) 8/8/94
+ */
+
+struct _option {
+ union {
+ u_long val; /* Value or boolean. */
+ char *str; /* String. */
+ } o_u;
+ size_t len; /* String length. */
+
+#define OPT_ALLOCATED 0x01 /* Allocated space. */
+#define OPT_SELECTED 0x02 /* Selected for display. */
+#define OPT_SET 0x04 /* Set (display for the user). */
+ u_char flags;
+};
+
+struct _optlist {
+ char *name; /* Name. */
+ /* Change function. */
+ int (*func) __P((SCR *, OPTION *, char *, u_long));
+ /* Type of object. */
+ enum { OPT_0BOOL, OPT_1BOOL, OPT_NUM, OPT_STR } type;
+
+#define OPT_NEVER 0x01 /* Never display the option. */
+#define OPT_NOSAVE 0x02 /* Mkexrc command doesn't save. */
+#define OPT_NOSTR 0x04 /* String that takes a "no". */
+ u_int flags;
+};
+
+/* Clear, set, test boolean options. */
+#define O_SET(sp, o) (sp)->opts[(o)].o_u.val = 1
+#define O_CLR(sp, o) (sp)->opts[(o)].o_u.val = 0
+#define O_ISSET(sp, o) ((sp)->opts[(o)].o_u.val)
+
+/* Get option values. */
+#define O_LEN(sp, o) (sp)->opts[(o)].len
+#define O_STR(sp, o) (sp)->opts[(o)].o_u.str
+#define O_VAL(sp, o) (sp)->opts[(o)].o_u.val
+
+/* Option routines. */
+u_long baud_from_bval __P((SCR *));
+
+int opts_copy __P((SCR *, SCR *));
+void opts_free __P((SCR *));
+int opts_init __P((SCR *));
+int opts_save __P((SCR *, FILE *));
+int opts_set __P((SCR *, char *, ARGS *[]));
+
+enum optdisp { NO_DISPLAY, ALL_DISPLAY, CHANGED_DISPLAY, SELECT_DISPLAY };
+void opts_dump __P((SCR *, enum optdisp));
+
+/* Per-option change routines. */
+int f_altwerase __P((SCR *, OPTION *, char *, u_long));
+int f_cdpath __P((SCR *, OPTION *, char *, u_long));
+int f_columns __P((SCR *, OPTION *, char *, u_long));
+int f_leftright __P((SCR *, OPTION *, char *, u_long));
+int f_lines __P((SCR *, OPTION *, char *, u_long));
+int f_lisp __P((SCR *, OPTION *, char *, u_long));
+int f_list __P((SCR *, OPTION *, char *, u_long));
+int f_mesg __P((SCR *, OPTION *, char *, u_long));
+int f_modeline __P((SCR *, OPTION *, char *, u_long));
+int f_number __P((SCR *, OPTION *, char *, u_long));
+int f_octal __P((SCR *, OPTION *, char *, u_long));
+int f_paragraph __P((SCR *, OPTION *, char *, u_long));
+int f_readonly __P((SCR *, OPTION *, char *, u_long));
+int f_section __P((SCR *, OPTION *, char *, u_long));
+int f_shiftwidth __P((SCR *, OPTION *, char *, u_long));
+int f_sourceany __P((SCR *, OPTION *, char *, u_long));
+int f_tabstop __P((SCR *, OPTION *, char *, u_long));
+int f_tags __P((SCR *, OPTION *, char *, u_long));
+int f_term __P((SCR *, OPTION *, char *, u_long));
+int f_ttywerase __P((SCR *, OPTION *, char *, u_long));
+int f_w1200 __P((SCR *, OPTION *, char *, u_long));
+int f_w300 __P((SCR *, OPTION *, char *, u_long));
+int f_w9600 __P((SCR *, OPTION *, char *, u_long));
+int f_window __P((SCR *, OPTION *, char *, u_long));
diff --git a/usr.bin/vi/common/options_f.c b/usr.bin/vi/common/options_f.c
new file mode 100644
index 0000000..bb3cf1b
--- /dev/null
+++ b/usr.bin/vi/common/options_f.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options_f.c 8.36 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../ex/tag.h"
+
+static int opt_dup __P((SCR *, int, char *));
+static int opt_putenv __P((char *));
+
+#define DECL(f) \
+ int \
+ f(sp, op, str, val) \
+ SCR *sp; \
+ OPTION *op; \
+ char *str; \
+ u_long val;
+#define CALL(f) \
+ f(sp, op, str, val)
+
+#define turnoff val
+
+DECL(f_altwerase)
+{
+ if (turnoff)
+ O_CLR(sp, O_ALTWERASE);
+ else {
+ O_SET(sp, O_ALTWERASE);
+ O_CLR(sp, O_TTYWERASE);
+ }
+ return (0);
+}
+
+DECL(f_cdpath)
+{
+ return (opt_dup(sp, O_CDPATH, str));
+}
+
+DECL(f_columns)
+{
+ char buf[25];
+
+ /* Validate the number. */
+ if (val < MINIMUM_SCREEN_COLS) {
+ msgq(sp, M_ERR, "Screen columns too small, less than %d",
+ MINIMUM_SCREEN_COLS);
+ return (1);
+ }
+ /* Set the columns value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "COLUMNS=%lu", val);
+ if (opt_putenv(buf))
+ return (1);
+
+ /* This is expensive, don't do it unless it's necessary. */
+ if (O_VAL(sp, O_COLUMNS) == val)
+ return (0);
+
+ /* Set the value. */
+ O_VAL(sp, O_COLUMNS) = val;
+
+ F_SET(sp, S_RESIZE);
+ return (0);
+}
+
+DECL(f_leftright)
+{
+ if (turnoff)
+ O_CLR(sp, O_LEFTRIGHT);
+ else
+ O_SET(sp, O_LEFTRIGHT);
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_lines)
+{
+ char buf[25];
+
+ /* Validate the number. */
+ if (val < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Screen lines too small, less than %d",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /* Set the rows value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "ROWS=%lu", val);
+ if (opt_putenv(buf))
+ return (1);
+
+ /* This is expensive, don't do it unless it's necessary. */
+ if (O_VAL(sp, O_LINES) == val)
+ return (0);
+
+ /* Set the value. */
+ O_VAL(sp, O_LINES) = val;
+
+ /*
+ * If no window value set, set a new default window and,
+ * optionally, a new scroll value.
+ */
+ if (!F_ISSET(&sp->opts[O_WINDOW], OPT_SET)) {
+ O_VAL(sp, O_WINDOW) = val - 1;
+ if (!F_ISSET(&sp->opts[O_SCROLL], OPT_SET))
+ O_VAL(sp, O_SCROLL) = val / 2;
+ }
+
+ F_SET(sp, S_RESIZE);
+ return (0);
+}
+
+DECL(f_lisp)
+{
+ msgq(sp, M_ERR, "The lisp option is not implemented");
+ return (0);
+}
+
+DECL(f_list)
+{
+ if (turnoff)
+ O_CLR(sp, O_LIST);
+ else
+ O_SET(sp, O_LIST);
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_mesg)
+{
+ struct stat sb;
+ char *tty;
+
+ /* Find the tty. */
+ if ((tty = ttyname(STDERR_FILENO)) == NULL) {
+ msgq(sp, M_ERR, "ttyname: %s", strerror(errno));
+ return (1);
+ }
+
+ /* Save the tty mode for later; only save it once. */
+ if (!F_ISSET(sp->gp, G_SETMODE)) {
+ F_SET(sp->gp, G_SETMODE);
+ if (stat(tty, &sb) < 0) {
+ msgq(sp, M_ERR, "%s: %s", tty, strerror(errno));
+ return (1);
+ }
+ sp->gp->origmode = sb.st_mode;
+ }
+
+ if (turnoff) {
+ if (chmod(tty, sp->gp->origmode & ~S_IWGRP) < 0) {
+ msgq(sp, M_ERR, "messages not turned off: %s: %s",
+ tty, strerror(errno));
+ return (1);
+ }
+ O_CLR(sp, O_MESG);
+ } else {
+ if (chmod(tty, sp->gp->origmode | S_IWGRP) < 0) {
+ msgq(sp, M_ERR, "messages not turned on: %s: %s",
+ tty, strerror(errno));
+ return (1);
+ }
+ O_SET(sp, O_MESG);
+ }
+ return (0);
+}
+
+/*
+ * f_modeline --
+ * This has been documented in historical systems as both "modeline"
+ * and as "modelines". Regardless of the name, this option represents
+ * a security problem of mammoth proportions, not to mention a stunning
+ * example of what your intro CS professor referred to as the perils of
+ * mixing code and data. Don't add it, or I will kill you.
+ */
+DECL(f_modeline)
+{
+ if (!turnoff)
+ msgq(sp, M_ERR, "The modeline(s) option may never be set");
+ return (0);
+}
+
+DECL(f_number)
+{
+ if (turnoff)
+ O_CLR(sp, O_NUMBER);
+ else
+ O_SET(sp, O_NUMBER);
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_octal)
+{
+ if (turnoff)
+ O_CLR(sp, O_OCTAL);
+ else
+ O_SET(sp, O_OCTAL);
+
+ key_init(sp);
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_paragraph)
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "Paragraph options must be in sets of two characters");
+ return (1);
+ }
+ return (opt_dup(sp, O_PARAGRAPHS, str));
+}
+
+DECL(f_readonly)
+{
+ if (turnoff) {
+ O_CLR(sp, O_READONLY);
+ if (sp->frp != NULL)
+ F_CLR(sp->frp, FR_RDONLY);
+ } else {
+ O_SET(sp, O_READONLY);
+ if (sp->frp != NULL)
+ F_SET(sp->frp, FR_RDONLY);
+ }
+ return (0);
+}
+
+DECL(f_section)
+{
+ if (strlen(str) & 1) {
+ msgq(sp, M_ERR,
+ "Section options must be in sets of two characters");
+ return (1);
+ }
+ return (opt_dup(sp, O_SECTIONS, str));
+}
+
+DECL(f_shiftwidth)
+{
+ if (val == 0) {
+ msgq(sp, M_ERR, "The shiftwidth can't be set to 0");
+ return (1);
+ }
+ O_VAL(sp, O_SHIFTWIDTH) = val;
+ return (0);
+}
+
+/*
+ * f_sourceany --
+ * Historic vi, on startup, source'd $HOME/.exrc and ./.exrc, if they
+ * were owned by the user. The sourceany option was an undocumented
+ * feature of historic vi which permitted the startup source'ing of
+ * .exrc files the user didn't own. This is an obvious security problem,
+ * and we ignore the option.
+ */
+DECL(f_sourceany)
+{
+ if (!turnoff)
+ msgq(sp, M_ERR, "The sourceany option may never be set");
+ return (0);
+}
+
+DECL(f_tabstop)
+{
+ if (val == 0) {
+ msgq(sp, M_ERR, "Tab stops can't be set to 0");
+ return (1);
+ }
+#define MAXTABSTOP 20
+ if (val > MAXTABSTOP) {
+ msgq(sp, M_ERR,
+ "Tab stops can't be larger than %d", MAXTABSTOP);
+ return (1);
+ }
+ O_VAL(sp, O_TABSTOP) = val;
+
+ F_SET(sp, S_REFORMAT | S_REDRAW);
+ return (0);
+}
+
+DECL(f_tags)
+{
+ return (opt_dup(sp, O_TAGS, str));
+}
+
+DECL(f_term)
+{
+ char buf[256];
+
+ if (opt_dup(sp, O_TERM, str))
+ return (1);
+
+ /* Set the terminal value in the environment for curses. */
+ (void)snprintf(buf, sizeof(buf), "TERM=%s", str);
+ if (opt_putenv(buf))
+ return (1);
+ return (0);
+}
+
+DECL(f_ttywerase)
+{
+ if (turnoff)
+ O_CLR(sp, O_TTYWERASE);
+ else {
+ O_SET(sp, O_TTYWERASE);
+ O_CLR(sp, O_ALTWERASE);
+ }
+ return (0);
+}
+
+DECL(f_w300)
+{
+ /* Historical behavior for w300 was < 1200. */
+ if (baud_from_bval(sp) >= 1200)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W300) = val;
+ return (0);
+}
+
+DECL(f_w1200)
+{
+ u_long v;
+
+ /* Historical behavior for w1200 was == 1200. */
+ v = baud_from_bval(sp);
+ if (v < 1200 || v > 4800)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W1200) = val;
+ return (0);
+}
+
+DECL(f_w9600)
+{
+ speed_t v;
+
+ /* Historical behavior for w9600 was > 1200. */
+ v = baud_from_bval(sp);
+ if (v <= 4800)
+ return (0);
+
+ if (CALL(f_window))
+ return (1);
+
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_W9600) = val;
+ return (0);
+}
+
+DECL(f_window)
+{
+ if (val < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Window too small, less than %d",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+ if (val > O_VAL(sp, O_LINES) - 1)
+ val = O_VAL(sp, O_LINES) - 1;
+ O_VAL(sp, O_WINDOW) = val;
+ O_VAL(sp, O_SCROLL) = val / 2;
+
+ return (0);
+}
+
+/*
+ * opt_dup --
+ * Copy a string value for user display.
+ */
+static int
+opt_dup(sp, opt, str)
+ SCR *sp;
+ int opt;
+ char *str;
+{
+ char *p;
+
+ /* Copy for user display. */
+ if ((p = strdup(str)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* Free the old contents. */
+ if (F_ISSET(&sp->opts[opt], OPT_ALLOCATED))
+ free(O_STR(sp, opt));
+
+ /* Note that it's set and allocated. */
+ F_SET(&sp->opts[opt], OPT_ALLOCATED | OPT_SET);
+
+ /* Assign new contents. */
+ O_STR(sp, opt) = p;
+ return (0);
+}
+
+/*
+ * opt_putenv --
+ * Put a value into the environment. We use putenv(3) because it's
+ * more portable. The following hack is because some moron decided
+ * to keep a reference to the memory passed to putenv(3), instead of
+ * having it allocate its own. Someone clearly needs to get promoted
+ * into management.
+ */
+static int
+opt_putenv(s)
+ char *s;
+{
+ char *t;
+
+ /*
+ * XXX
+ * Memory leak.
+ */
+ if ((t = strdup(s)) == NULL)
+ return (1);
+ return (putenv(t));
+}
+
+/*
+ * baud_from_bval --
+ * Return the baud rate using the standard defines.
+ */
+u_long
+baud_from_bval(sp)
+ SCR *sp;
+{
+ if (!F_ISSET(sp->gp, G_TERMIOS_SET))
+ return (9600);
+
+ /*
+ * XXX
+ * There's no portable way to get a "baud rate" -- cfgetospeed(3)
+ * returns the value associated with some #define, which we may
+ * never have heard of, or which may be a purely local speed. Vi
+ * only cares if it's SLOW (w300), slow (w1200) or fast (w9600).
+ * Try and detect the slow ones, and default to fast.
+ */
+ switch (cfgetospeed(&sp->gp->original_termios)) {
+ case B50:
+ case B75:
+ case B110:
+ case B134:
+ case B150:
+ case B200:
+ case B300:
+ case B600:
+ return (600);
+ case B1200:
+ return (1200);
+ }
+ return (9600);
+}
diff --git a/usr.bin/vi/common/pathnames.h b/usr.bin/vi/common/pathnames.h
new file mode 100644
index 0000000..6ad165f
--- /dev/null
+++ b/usr.bin/vi/common/pathnames.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.7 (Berkeley) 3/28/94
+ */
+
+#define _PATH_BSHELL "/bin/sh"
+#define _PATH_DEVNULL "/dev/null"
+#define _PATH_EXRC ".exrc"
+#define _PATH_NEXRC ".nexrc"
+#define _PATH_PRESERVE "/var/tmp/vi.recover"
+#define _PATH_SENDMAIL "/usr/sbin/sendmail"
+#define _PATH_SYSEXRC "/etc/vi.exrc"
+#define _PATH_TAGS "tags"
+#define _PATH_TMP "/tmp"
+#define _PATH_TTY "/dev/tty"
diff --git a/usr.bin/vi/common/put.c b/usr.bin/vi/common/put.c
new file mode 100644
index 0000000..6ed14a8
--- /dev/null
+++ b/usr.bin/vi/common/put.c
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)put.c 8.11 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * put --
+ * Put text buffer contents into the file.
+ */
+int
+put(sp, ep, cbp, namep, cp, rp, append)
+ SCR *sp;
+ EXF *ep;
+ CB *cbp;
+ CHAR_T *namep;
+ MARK *cp, *rp;
+ int append;
+{
+ CHAR_T name;
+ TEXT *ltp, *tp;
+ recno_t lno;
+ size_t blen, clen, len;
+ int rval;
+ char *bp, *p, *t;
+
+ if (cbp == NULL)
+ if (namep == NULL) {
+ cbp = sp->gp->dcbp;
+ if (cbp == NULL) {
+ msgq(sp, M_ERR, "The default buffer is empty");
+ return (1);
+ }
+ } else {
+ name = *namep;
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ msgq(sp, M_ERR,
+ "Buffer %s is empty", KEY_NAME(sp, name));
+ return (1);
+ }
+ }
+ tp = cbp->textq.cqh_first;
+
+ /*
+ * It's possible to do a put into an empty file, meaning that the cut
+ * buffer simply becomes the file. It's a special case so that we can
+ * ignore it in general.
+ *
+ * !!!
+ * Historically, pasting into a file with no lines in vi would preserve
+ * the single blank line. This is surely a result of the fact that the
+ * historic vi couldn't deal with a file that had no lines in it. This
+ * implementation treats that as a bug, and does not retain the blank
+ * line.
+ *
+ * Historical practice is that the cursor ends at the first character
+ * in the file.
+ */
+ if (cp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ for (; tp != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->lno = 1;
+ rp->cno = 0;
+ return (0);
+ }
+ }
+
+ /* If a line mode buffer, append each new line into the file. */
+ if (F_ISSET(cbp, CB_LMODE)) {
+ lno = append ? cp->lno : cp->lno - 1;
+ rp->lno = lno + 1;
+ for (; tp != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ return (1);
+ rp->cno = 0;
+ (void)nonblank(sp, ep, rp->lno, &rp->cno);
+ return (0);
+ }
+
+ /*
+ * If buffer was cut in character mode, replace the current line with
+ * one built from the portion of the first line to the left of the
+ * split plus the first line in the CB. Append each intermediate line
+ * in the CB. Append a line built from the portion of the first line
+ * to the right of the split plus the last line in the CB.
+ *
+ * Get the first line.
+ */
+ lno = cp->lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, tp->len + len + 1);
+ t = bp;
+
+ /* Original line, left of the split. */
+ if (len > 0 && (clen = cp->cno + (append ? 1 : 0)) > 0) {
+ memmove(bp, p, clen);
+ p += clen;
+ t += clen;
+ }
+
+ /* First line from the CB. */
+ memmove(t, tp->lb, tp->len);
+ t += tp->len;
+
+ /* Calculate length left in original line. */
+ clen = len ? len - cp->cno - (append ? 1 : 0) : 0;
+
+ /*
+ * If no more lines in the CB, append the rest of the original
+ * line and quit. Otherwise, build the last line before doing
+ * the intermediate lines, because the line changes will lose
+ * the cached line.
+ */
+ rval = 0;
+ if (tp->q.cqe_next == (void *)&cbp->textq) {
+ /*
+ * Historical practice is that if a non-line mode put
+ * is inside a single line, the cursor ends up on the
+ * last character inserted.
+ */
+ rp->lno = lno;
+ rp->cno = (t - bp) - 1;
+
+ if (clen > 0) {
+ memmove(t, p, clen);
+ t += clen;
+ }
+ if (file_sline(sp, ep, lno, bp, t - bp))
+ goto mem;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+ } else {
+ /*
+ * Have to build both the first and last lines of the
+ * put before doing any sets or we'll lose the cached
+ * line. Build both the first and last lines in the
+ * same buffer, so we don't have to have another buffer
+ * floating around.
+ *
+ * Last part of original line; check for space, reset
+ * the pointer into the buffer.
+ */
+ ltp = cbp->textq.cqh_last;
+ len = t - bp;
+ ADD_SPACE_RET(sp, bp, blen, ltp->len + clen);
+ t = bp + len;
+
+ /* Add in last part of the CB. */
+ memmove(t, ltp->lb, ltp->len);
+ if (clen)
+ memmove(t + ltp->len, p, clen);
+ clen += ltp->len;
+
+ /*
+ * Now: bp points to the first character of the first
+ * line, t points to the last character of the last
+ * line, t - bp is the length of the first line, and
+ * clen is the length of the last. Just figured you'd
+ * want to know.
+ *
+ * Output the line replacing the original line.
+ */
+ if (file_sline(sp, ep, lno, bp, t - bp))
+ goto mem;
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+
+ /*
+ * Historical practice is that if a non-line mode put
+ * covers multiple lines, the cursor ends up on the
+ * first character inserted. (Of course.)
+ */
+ rp->lno = lno;
+ rp->cno = (t - bp) - 1;
+
+ /* Output any intermediate lines in the CB. */
+ for (tp = tp->q.cqe_next;
+ tp->q.cqe_next != (void *)&cbp->textq;
+ ++lno, ++sp->rptlines[L_ADDED], tp = tp->q.cqe_next)
+ if (file_aline(sp, ep, 1, lno, tp->lb, tp->len))
+ goto mem;
+
+ if (file_aline(sp, ep, 1, lno, t, clen))
+mem: rval = 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/common/recover.c b/usr.bin/vi/common/recover.c
new file mode 100644
index 0000000..a94b65a
--- /dev/null
+++ b/usr.bin/vi/common/recover.c
@@ -0,0 +1,869 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)recover.c 8.74 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+/*
+ * We include <sys/file.h>, because the open #defines were found there
+ * on historical systems. We also include <fcntl.h> because the open(2)
+ * #defines are found there on newer systems.
+ */
+#include <sys/file.h>
+
+#include <netdb.h> /* MAXHOSTNAMELEN on some systems. */
+
+#include <bitstring.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+
+/*
+ * Recovery code.
+ *
+ * The basic scheme is as follows. In the EXF structure, we maintain full
+ * paths of a b+tree file and a mail recovery file. The former is the file
+ * used as backing store by the DB package. The latter is the file that
+ * contains an email message to be sent to the user if we crash. The two
+ * simple states of recovery are:
+ *
+ * + first starting the edit session:
+ * the b+tree file exists and is mode 700, the mail recovery
+ * file doesn't exist.
+ * + after the file has been modified:
+ * the b+tree file exists and is mode 600, the mail recovery
+ * file exists, and is exclusively locked.
+ *
+ * In the EXF structure we maintain a file descriptor that is the locked
+ * file descriptor for the mail recovery file. NOTE: we sometimes have to
+ * do locking with fcntl(2). This is a problem because if you close(2) any
+ * file descriptor associated with the file, ALL of the locks go away. Be
+ * sure to remember that if you have to modify the recovery code. (It has
+ * been rhetorically asked of what the designers could have been thinking
+ * when they did that interface. The answer is simple: they weren't.)
+ *
+ * To find out if a recovery file/backing file pair are in use, try to get
+ * a lock on the recovery file.
+ *
+ * To find out if a backing file can be deleted at boot time, check for an
+ * owner execute bit. (Yes, I know it's ugly, but it's either that or put
+ * special stuff into the backing file itself, or correlate the files at
+ * boot time, neither or which looks like fun.) Note also that there's a
+ * window between when the file is created and the X bit is set. It's small,
+ * but it's there. To fix the window, check for 0 length files as well.
+ *
+ * To find out if a file can be recovered, check the F_RCV_ON bit. Note,
+ * this DOES NOT mean that any initialization has been done, only that we
+ * haven't yet failed at setting up or doing recovery.
+ *
+ * To preserve a recovery file/backing file pair, set the F_RCV_NORM bit.
+ * If that bit is not set when ending a file session:
+ * If the EXF structure paths (rcv_path and rcv_mpath) are not NULL,
+ * they are unlink(2)'d, and free(3)'d.
+ * If the EXF file descriptor (rcv_fd) is not -1, it is closed.
+ *
+ * The backing b+tree file is set up when a file is first edited, so that
+ * the DB package can use it for on-disk caching and/or to snapshot the
+ * file. When the file is first modified, the mail recovery file is created,
+ * the backing file permissions are updated, the file is sync(2)'d to disk,
+ * and the timer is started. Then, at RCV_PERIOD second intervals, the
+ * b+tree file is synced to disk. RCV_PERIOD is measured using SIGALRM, which
+ * means that the data structures (SCR, EXF, the underlying tree structures)
+ * must be consistent when the signal arrives.
+ *
+ * The recovery mail file contains normal mail headers, with two additions,
+ * which occur in THIS order, as the FIRST TWO headers:
+ *
+ * X-vi-recover-file: file_name
+ * X-vi-recover-path: recover_path
+ *
+ * Since newlines delimit the headers, this means that file names cannot have
+ * newlines in them, but that's probably okay. As these files aren't intended
+ * to be long-lived, changing their format won't be too painful.
+ *
+ * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX".
+ */
+
+#define VI_FHEADER "X-vi-recover-file: "
+#define VI_PHEADER "X-vi-recover-path: "
+
+static int rcv_copy __P((SCR *, int, char *));
+static void rcv_email __P((SCR *, char *));
+static char *rcv_gets __P((char *, size_t, int));
+static int rcv_mailfile __P((SCR *, EXF *, int, char *));
+static int rcv_mktemp __P((SCR *, char *, char *, int));
+
+/*
+ * rcv_tmp --
+ * Build a file name that will be used as the recovery file.
+ */
+int
+rcv_tmp(sp, ep, name)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+{
+ struct stat sb;
+ int fd;
+ char *dp, *p, path[MAXPATHLEN];
+
+ /*
+ * If the recovery directory doesn't exist, try and create it. As
+ * the recovery files are themselves protected from reading/writing
+ * by other than the owner, the worst that can happen is that a user
+ * would have permission to remove other user's recovery files. If
+ * the sticky bit has the BSD semantics, that too will be impossible.
+ */
+ dp = O_STR(sp, O_RECDIR);
+ if (stat(dp, &sb)) {
+ if (errno != ENOENT || mkdir(dp, 0)) {
+ msgq(sp, M_SYSERR, "%s", dp);
+ goto err;
+ }
+ (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX);
+ }
+
+ /* Newlines delimit the mail messages. */
+ for (p = name; *p; ++p)
+ if (*p == '\n') {
+ msgq(sp, M_ERR,
+ "Files with newlines in the name are unrecoverable");
+ goto err;
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, path, dp, S_IRWXU)) == -1)
+ goto err;
+ (void)close(fd);
+
+ if ((ep->rcv_path = strdup(path)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ (void)unlink(path);
+err: msgq(sp, M_ERR,
+ "Modifications not recoverable if the session fails");
+ return (1);
+ }
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_init --
+ * Force the file to be snapshotted for recovery.
+ */
+int
+rcv_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ recno_t lno;
+ int btear;
+
+ /* Only do this once. */
+ F_CLR(ep, F_FIRSTMODIFY);
+
+ /* If we already know the file isn't recoverable, we're done. */
+ if (!F_ISSET(ep, F_RCV_ON))
+ return (0);
+
+ /* Turn off recoverability until we figure out if this will work. */
+ F_CLR(ep, F_RCV_ON);
+
+ /* Test if we're recovering a file, not editing one. */
+ if (ep->rcv_mpath == NULL) {
+ /* Build a file to mail to the user. */
+ if (rcv_mailfile(sp, ep, 0, NULL))
+ goto err;
+
+ /* Force a read of the entire file. */
+ if (file_lline(sp, ep, &lno))
+ goto err;
+
+ /* Turn on a busy message, and sync it to backing store. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 :
+ !busy_on(sp, "Copying file for recovery...");
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ msgq(sp, M_ERR, "Preservation failed: %s: %s",
+ ep->rcv_path, strerror(errno));
+ if (btear)
+ busy_off(sp);
+ goto err;
+ }
+ if (btear)
+ busy_off(sp);
+ }
+
+ /* Turn on the recovery timer, if it's not yet running. */
+ if (!F_ISSET(sp->gp, G_RECOVER_SET) && rcv_on(sp, ep)) {
+err: msgq(sp, M_ERR,
+ "Modifications not recoverable if the session fails");
+ return (1);
+ }
+
+ /* Turn off the owner execute bit. */
+ (void)chmod(ep->rcv_path, S_IRUSR | S_IWUSR);
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_sync --
+ * Sync the file, optionally:
+ * flagging the backup file to be preserved
+ * snapshotting the backup file and send email to the user
+ * sending email to the user if the file was modified
+ * ending the file session
+ */
+int
+rcv_sync(sp, ep, flags)
+ SCR *sp;
+ EXF *ep;
+ u_int flags;
+{
+ int btear, fd, rval;
+ char *dp, buf[1024];
+
+ /* Make sure that there's something to recover/sync. */
+ if (ep == NULL || !F_ISSET(ep, F_RCV_ON))
+ return (0);
+
+ /* Sync the file if it's been modified. */
+ if (F_ISSET(ep, F_MODIFIED)) {
+ if (ep->db->sync(ep->db, R_RECNOSYNC)) {
+ F_CLR(ep, F_RCV_ON | F_RCV_NORM);
+ msgq(sp, M_SYSERR,
+ "File backup failed: %s", ep->rcv_path);
+ return (1);
+ }
+
+ /* REQUEST: don't remove backing file on exit. */
+ if (LF_ISSET(RCV_PRESERVE))
+ F_SET(ep, F_RCV_NORM);
+
+ /* REQUEST: send email. */
+ if (LF_ISSET(RCV_EMAIL))
+ rcv_email(sp, ep->rcv_mpath);
+ }
+
+ /*
+ * !!!
+ * Each time the user exec's :preserve, we have to snapshot all of
+ * the recovery information, i.e. it's like the user re-edited the
+ * file. We copy the DB(3) backing file, and then create a new mail
+ * recovery file, it's simpler than exiting and reopening all of the
+ * underlying files.
+ *
+ * REQUEST: snapshot the file.
+ */
+ rval = 0;
+ if (LF_ISSET(RCV_SNAPSHOT)) {
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 :
+ !busy_on(sp, "Copying file for recovery...");
+ dp = O_STR(sp, O_RECDIR);
+ (void)snprintf(buf, sizeof(buf), "%s/vi.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, buf, dp, S_IRUSR | S_IWUSR)) == -1)
+ goto e1;
+ if (rcv_copy(sp, fd, ep->rcv_path) || close(fd))
+ goto e2;
+ if (rcv_mailfile(sp, ep, 1, buf)) {
+e2: (void)unlink(buf);
+e1: if (fd != -1)
+ (void)close(fd);
+ rval = 1;
+ }
+ if (btear)
+ busy_off(sp);
+ }
+
+ /* REQUEST: end the file session. */
+ if (LF_ISSET(RCV_ENDSESSION) && file_end(sp, ep, 1))
+ rval = 1;
+
+ return (rval);
+}
+
+/*
+ * rcv_mailfile --
+ * Build the file to mail to the user.
+ */
+static int
+rcv_mailfile(sp, ep, issync, cp_path)
+ SCR *sp;
+ EXF *ep;
+ int issync;
+ char *cp_path;
+{
+ struct passwd *pw;
+ size_t len;
+ time_t now;
+ uid_t uid;
+ int fd;
+ char *dp, *p, *t, buf[4096], host[MAXHOSTNAMELEN], mpath[MAXPATHLEN];
+ char *t1, *t2, *t3;
+
+ if ((pw = getpwuid(uid = getuid())) == NULL) {
+ msgq(sp, M_ERR, "Information on user id %u not found", uid);
+ return (1);
+ }
+
+ dp = O_STR(sp, O_RECDIR);
+ (void)snprintf(mpath, sizeof(mpath), "%s/recover.XXXXXX", dp);
+ if ((fd = rcv_mktemp(sp, mpath, dp, S_IRUSR | S_IWUSR)) == -1)
+ return (1);
+
+ /*
+ * XXX
+ * We keep an open lock on the file so that the recover option can
+ * distinguish between files that are live and those that need to
+ * be recovered. There's an obvious window between the mkstemp call
+ * and the lock, but it's pretty small.
+ */
+ if (file_lock(NULL, NULL, fd, 1) != LOCK_SUCCESS)
+ msgq(sp, M_SYSERR, "Unable to lock recovery file");
+ if (!issync) {
+ /* Save the recover file descriptor, and mail path. */
+ ep->rcv_fd = fd;
+ if ((ep->rcv_mpath = strdup(mpath)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ goto err;
+ }
+ cp_path = ep->rcv_path;
+ }
+
+ /*
+ * XXX
+ * We can't use stdio(3) here. The problem is that we may be using
+ * fcntl(2), so if ANY file descriptor into the file is closed, the
+ * lock is lost. So, we could never close the FILE *, even if we
+ * dup'd the fd first.
+ */
+ t = sp->frp->name;
+ if ((p = strrchr(t, '/')) == NULL)
+ p = t;
+ else
+ ++p;
+ (void)time(&now);
+ (void)gethostname(host, sizeof(host));
+ len = snprintf(buf, sizeof(buf),
+ "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n",
+ VI_FHEADER, t, /* Non-standard. */
+ VI_PHEADER, cp_path, /* Non-standard. */
+ "Reply-To: root",
+ "From: root (Nvi recovery program)",
+ "To: ", pw->pw_name,
+ "Subject: Nvi saved the file ", p,
+ "Precedence: bulk"); /* For vacation(1). */
+ if (len > sizeof(buf) - 1)
+ goto lerr;
+ if (write(fd, buf, len) != len)
+ goto werr;
+
+ len = snprintf(buf, sizeof(buf), "%s%.24s%s%s%s%s%s%s%s%s%s%s%s\n\n",
+ "On ", ctime(&now), ", the user ", pw->pw_name,
+ " was editing a file named ", t, " on the machine ",
+ host, ", when it was saved for recovery. ",
+ "You can recover most, if not all, of the changes ",
+ "to this file using the -r option to nex or nvi:\n\n",
+ "\tnvi -r ", t);
+ if (len > sizeof(buf) - 1) {
+lerr: msgq(sp, M_ERR, "recovery file buffer overrun");
+ goto err;
+ }
+
+ /*
+ * Format the message. (Yes, I know it's silly.)
+ * Requires that the message end in a <newline>.
+ */
+#define FMTCOLS 60
+ for (t1 = buf; len > 0; len -= t2 - t1, t1 = t2) {
+ /* Check for a short length. */
+ if (len <= FMTCOLS) {
+ t2 = t1 + (len - 1);
+ goto wout;
+ }
+
+ /* Check for a required <newline>. */
+ t2 = strchr(t1, '\n');
+ if (t2 - t1 <= FMTCOLS)
+ goto wout;
+
+ /* Find the closest space, if any. */
+ for (t3 = t2; t2 > t1; --t2)
+ if (*t2 == ' ') {
+ if (t2 - t1 <= FMTCOLS)
+ goto wout;
+ t3 = t2;
+ }
+ t2 = t3;
+
+ /* t2 points to the last character to display. */
+wout: *t2++ = '\n';
+
+ /* t2 points one after the last character to display. */
+ if (write(fd, t1, t2 - t1) != t2 - t1) {
+werr: msgq(sp, M_SYSERR, "recovery file");
+ goto err;
+ }
+ }
+
+ if (issync)
+ rcv_email(sp, mpath);
+
+ return (0);
+
+err: if (!issync)
+ ep->rcv_fd = -1;
+ if (fd != -1)
+ (void)close(fd);
+ return (1);
+}
+
+/*
+ * people making love
+ * never exactly the same
+ * just like a snowflake
+ *
+ * rcv_list --
+ * List the files that can be recovered by this user.
+ */
+int
+rcv_list(sp)
+ SCR *sp;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ FILE *fp;
+ int found;
+ char *p, *t, file[MAXPATHLEN], path[MAXPATHLEN];
+
+ /*
+ * XXX
+ * Messages aren't yet set up.
+ */
+ if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) {
+ (void)fprintf(stderr,
+ "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno));
+ return (1);
+ }
+
+ for (found = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+
+ /*
+ * If it's readable, it's recoverable.
+ *
+ * XXX
+ * Should be "r", we don't want to write the file. However,
+ * if we're using fcntl(2), there's no way to lock a file
+ * descriptor that's not open for writing.
+ */
+ if ((fp = fopen(dp->d_name, "r+")) == NULL)
+ continue;
+
+ switch (file_lock(NULL, NULL, fileno(fp), 1)) {
+ case LOCK_FAILED:
+ /*
+ * XXX
+ * Assume that a lock can't be acquired, but that we
+ * should permit recovery anyway. If this is wrong,
+ * and someone else is using the file, we're going to
+ * die horribly.
+ */
+ break;
+ case LOCK_SUCCESS:
+ break;
+ case LOCK_UNAVAIL:
+ /* If it's locked, it's live. */
+ (void)fclose(fp);
+ continue;
+ }
+
+ /* Check the headers. */
+ if (fgets(file, sizeof(file), fp) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL ||
+ fgets(path, sizeof(path), fp) == NULL ||
+ strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+ (t = strchr(path, '\n')) == NULL) {
+ msgq(sp, M_ERR,
+ "%s: malformed recovery file", dp->d_name);
+ goto next;
+ }
+ *p = *t = '\0';
+
+ /*
+ * If the file doesn't exist, it's an orphaned recovery file,
+ * toss it.
+ *
+ * XXX
+ * This can occur if the backup file was deleted and we crashed
+ * before deleting the email file.
+ */
+ errno = 0;
+ if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
+ errno == ENOENT) {
+ (void)unlink(dp->d_name);
+ goto next;
+ }
+
+ /* Get the last modification time and display. */
+ (void)fstat(fileno(fp), &sb);
+ (void)printf("%s: %s",
+ file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime));
+ found = 1;
+
+ /* Close, discarding lock. */
+next: (void)fclose(fp);
+ }
+ if (found == 0)
+ (void)printf("vi: no files to recover.\n");
+ (void)closedir(dirp);
+ return (0);
+}
+
+/*
+ * rcv_read --
+ * Start a recovered file as the file to edit.
+ */
+int
+rcv_read(sp, frp)
+ SCR *sp;
+ FREF *frp;
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ EXF *ep;
+ time_t rec_mtime;
+ int fd, found, locked, requested, sv_fd;
+ char *name, *p, *t, *recp, *pathp;
+ char file[MAXPATHLEN], path[MAXPATHLEN], recpath[MAXPATHLEN];
+
+ if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) {
+ msgq(sp, M_ERR,
+ "%s: %s", O_STR(sp, O_RECDIR), strerror(errno));
+ return (1);
+ }
+
+ name = frp->name;
+ sv_fd = -1;
+ rec_mtime = 0;
+ recp = pathp = NULL;
+ for (found = requested = 0; (dp = readdir(dirp)) != NULL;) {
+ if (strncmp(dp->d_name, "recover.", 8))
+ continue;
+ (void)snprintf(recpath, sizeof(recpath),
+ "%s/%s", O_STR(sp, O_RECDIR), dp->d_name);
+
+ /*
+ * If it's readable, it's recoverable. It would be very
+ * nice to use stdio(3), but, we can't because that would
+ * require closing and then reopening the file so that we
+ * could have a lock and still close the FP. Another tip
+ * of the hat to fcntl(2).
+ *
+ * XXX
+ * Should be O_RDONLY, we don't want to write it. However,
+ * if we're using fcntl(2), there's no way to lock a file
+ * descriptor that's not open for writing.
+ */
+ if ((fd = open(recpath, O_RDWR, 0)) == -1)
+ continue;
+
+ switch (file_lock(NULL, NULL, fd, 1)) {
+ case LOCK_FAILED:
+ /*
+ * XXX
+ * Assume that a lock can't be acquired, but that we
+ * should permit recovery anyway. If this is wrong,
+ * and someone else is using the file, we're going to
+ * die horribly.
+ */
+ locked = 0;
+ break;
+ case LOCK_SUCCESS:
+ locked = 1;
+ break;
+ case LOCK_UNAVAIL:
+ /* If it's locked, it's live. */
+ (void)close(fd);
+ continue;
+ }
+
+ /* Check the headers. */
+ if (rcv_gets(file, sizeof(file), fd) == NULL ||
+ strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) ||
+ (p = strchr(file, '\n')) == NULL ||
+ rcv_gets(path, sizeof(path), fd) == NULL ||
+ strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) ||
+ (t = strchr(path, '\n')) == NULL) {
+ msgq(sp, M_ERR,
+ "%s: malformed recovery file", recpath);
+ goto next;
+ }
+ *p = *t = '\0';
+ ++found;
+
+ /*
+ * If the file doesn't exist, it's an orphaned recovery file,
+ * toss it.
+ *
+ * XXX
+ * This can occur if the backup file was deleted and we crashed
+ * before deleting the email file.
+ */
+ errno = 0;
+ if (stat(path + sizeof(VI_PHEADER) - 1, &sb) &&
+ errno == ENOENT) {
+ (void)unlink(dp->d_name);
+ goto next;
+ }
+
+ /* Check the file name. */
+ if (strcmp(file + sizeof(VI_FHEADER) - 1, name))
+ goto next;
+
+ ++requested;
+
+ /*
+ * If we've found more than one, take the most recent.
+ *
+ * XXX
+ * Since we're using st_mtime, for portability reasons,
+ * we only get a single second granularity, instead of
+ * getting it right.
+ */
+ (void)fstat(fd, &sb);
+ if (recp == NULL || rec_mtime < sb.st_mtime) {
+ p = recp;
+ t = pathp;
+ if ((recp = strdup(recpath)) == NULL) {
+ msgq(sp, M_ERR,
+ "vi: Error: %s.\n", strerror(errno));
+ recp = p;
+ goto next;
+ }
+ if ((pathp = strdup(path)) == NULL) {
+ msgq(sp, M_ERR,
+ "vi: Error: %s.\n", strerror(errno));
+ FREE(recp, strlen(recp) + 1);
+ recp = p;
+ pathp = t;
+ goto next;
+ }
+ if (p != NULL) {
+ FREE(p, strlen(p) + 1);
+ FREE(t, strlen(t) + 1);
+ }
+ rec_mtime = sb.st_mtime;
+ if (sv_fd != -1)
+ (void)close(sv_fd);
+ sv_fd = fd;
+ } else
+next: (void)close(fd);
+ }
+ (void)closedir(dirp);
+
+ if (recp == NULL) {
+ msgq(sp, M_INFO,
+ "No files named %s, readable by you, to recover", name);
+ return (1);
+ }
+ if (found) {
+ if (requested > 1)
+ msgq(sp, M_INFO,
+ "There are older versions of this file for you to recover");
+ if (found > requested)
+ msgq(sp, M_INFO,
+ "There are other files for you to recover");
+ }
+
+ /*
+ * Create the FREF structure, start the btree file.
+ *
+ * XXX
+ * file_init() is going to set ep->rcv_path.
+ */
+ if (file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) {
+ free(recp);
+ free(pathp);
+ (void)close(sv_fd);
+ return (1);
+ }
+
+ /*
+ * We keep an open lock on the file so that the recover option can
+ * distinguish between files that are live and those that need to
+ * be recovered. The lock is already acquired, just copy it.
+ */
+ ep = sp->ep;
+ ep->rcv_mpath = recp;
+ ep->rcv_fd = sv_fd;
+ if (!locked)
+ F_SET(frp, FR_UNLOCKED);
+
+ /* We believe the file is recoverable. */
+ F_SET(ep, F_RCV_ON);
+ return (0);
+}
+
+/*
+ * rcv_copy --
+ * Copy a recovery file.
+ */
+static int
+rcv_copy(sp, wfd, fname)
+ SCR *sp;
+ int wfd;
+ char *fname;
+{
+ int nr, nw, off, rfd;
+ char buf[8 * 1024];
+
+ if ((rfd = open(fname, O_RDONLY, 0)) == -1)
+ goto err;
+ while ((nr = read(rfd, buf, sizeof(buf))) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, nr)) < 0)
+ goto err;
+ if (nr == 0)
+ return (0);
+
+err: msgq(sp, M_SYSERR, "%s", fname);
+ return (1);
+}
+
+/*
+ * rcv_gets --
+ * Fgets(3) for a file descriptor.
+ */
+static char *
+rcv_gets(buf, len, fd)
+ char *buf;
+ size_t len;
+ int fd;
+{
+ ssize_t nr;
+ char *p;
+
+ if ((nr = read(fd, buf, len - 1)) == -1)
+ return (NULL);
+ if ((p = strchr(buf, '\n')) == NULL)
+ return (NULL);
+ (void)lseek(fd, (off_t)((p - buf) + 1), SEEK_SET);
+ return (buf);
+}
+
+/*
+ * rcv_mktemp --
+ * Paranoid make temporary file routine.
+ */
+static int
+rcv_mktemp(sp, path, dname, perms)
+ SCR *sp;
+ char *path, *dname;
+ int perms;
+{
+ int fd;
+
+ /*
+ * !!!
+ * We expect mkstemp(3) to set the permissions correctly. On
+ * historic System V systems, mkstemp didn't. Do it here, on
+ * GP's.
+ *
+ * XXX
+ * The variable perms should really be a mode_t, and it would
+ * be nice to use fchmod(2) instead of chmod(2), here.
+ */
+ if ((fd = mkstemp(path)) == -1)
+ msgq(sp, M_SYSERR, "%s", dname);
+ else
+ (void)chmod(path, perms);
+ return (fd);
+}
+
+/*
+ * rcv_email --
+ * Send email.
+ */
+static void
+rcv_email(sp, fname)
+ SCR *sp;
+ char *fname;
+{
+ struct stat sb;
+ char buf[MAXPATHLEN * 2 + 20];
+
+ if (stat(_PATH_SENDMAIL, &sb))
+ msgq(sp, M_SYSERR, "not sending email: %s", _PATH_SENDMAIL);
+ else {
+ /*
+ * !!!
+ * If you need to port this to a system that doesn't have
+ * sendmail, the -t flag causes sendmail to read the message
+ * for the recipients instead of specifying them some other
+ * way.
+ */
+ (void)snprintf(buf, sizeof(buf),
+ "%s -t < %s", _PATH_SENDMAIL, fname);
+ (void)system(buf);
+ }
+}
diff --git a/usr.bin/vi/common/screen.c b/usr.bin/vi/common/screen.c
new file mode 100644
index 0000000..2756969
--- /dev/null
+++ b/usr.bin/vi/common/screen.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)screen.c 8.67 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "../ex/tag.h"
+
+/*
+ * screen_init --
+ * Do the default initialization of an SCR structure.
+ */
+int
+screen_init(orig, spp, flags)
+ SCR *orig, **spp;
+ u_int flags;
+{
+ SCR *sp;
+ size_t len;
+
+ *spp = NULL;
+ CALLOC_RET(orig, sp, SCR *, 1, sizeof(SCR));
+ *spp = sp;
+
+/* INITIALIZED AT SCREEN CREATE. */
+ sp->gp = __global_list; /* All ref the GS structure. */
+
+ LIST_INIT(&sp->msgq);
+ CIRCLEQ_INIT(&sp->frefq);
+
+ sp->ccnt = 2; /* Anything > 1 */
+
+ FD_ZERO(&sp->rdfd);
+
+ /*
+ * XXX
+ * sp->defscroll is initialized by the opts_init() code because
+ * we don't have the option information yet.
+ */
+
+ sp->tiqp = &sp->__tiq;
+ CIRCLEQ_INIT(&sp->__tiq);
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ sp->searchdir = NOTSET;
+
+ switch (flags & S_SCREENS) {
+ case S_EX:
+ if (sex_screen_init(sp))
+ return (1);
+ break;
+ case S_VI_CURSES:
+ if (svi_screen_init(sp))
+ return (1);
+ break;
+ case S_VI_XAW:
+ if (xaw_screen_init(sp))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ sp->flags = flags;
+ } else {
+ if (orig->alt_name != NULL &&
+ (sp->alt_name = strdup(orig->alt_name)) == NULL)
+ goto mem;
+
+ /* Retain all searching/substitution information. */
+ if (F_ISSET(orig, S_SRE_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->sre = orig->sre;
+ }
+ if (F_ISSET(orig, S_SUBRE_SET)) {
+ F_SET(sp, S_SUBRE_SET);
+ sp->subre = orig->subre;
+ }
+ sp->searchdir = orig->searchdir == NOTSET ? NOTSET : FORWARD;
+
+ if (orig->repl_len) {
+ MALLOC(sp, sp->repl, char *, orig->repl_len);
+ if (sp->repl == NULL)
+ goto mem;
+ sp->repl_len = orig->repl_len;
+ memmove(sp->repl, orig->repl, orig->repl_len);
+ }
+ if (orig->newl_len) {
+ len = orig->newl_len * sizeof(size_t);
+ MALLOC(sp, sp->newl, size_t *, len);
+ if (sp->newl == NULL)
+ goto mem;
+ sp->newl_len = orig->newl_len;
+ sp->newl_cnt = orig->newl_cnt;
+ memmove(sp->newl, orig->newl, len);
+ }
+
+ sp->saved_vi_mode = orig->saved_vi_mode;
+
+ if (opts_copy(orig, sp)) {
+mem: msgq(orig, M_SYSERR, "new screen attributes");
+ (void)screen_end(sp);
+ return (1);
+ }
+
+ sp->s_bell = orig->s_bell;
+ sp->s_bg = orig->s_bg;
+ sp->s_busy = orig->s_busy;
+ sp->s_change = orig->s_change;
+ sp->s_clear = orig->s_clear;
+ sp->s_colpos = orig->s_colpos;
+ sp->s_column = orig->s_column;
+ sp->s_confirm = orig->s_confirm;
+ sp->s_crel = orig->s_crel;
+ sp->s_edit = orig->s_edit;
+ sp->s_end = orig->s_end;
+ sp->s_ex_cmd = orig->s_ex_cmd;
+ sp->s_ex_run = orig->s_ex_run;
+ sp->s_ex_write = orig->s_ex_write;
+ sp->s_fg = orig->s_fg;
+ sp->s_fill = orig->s_fill;
+ sp->s_get = orig->s_get;
+ sp->s_key_read = orig->s_key_read;
+ sp->s_fmap = orig->s_fmap;
+ sp->s_optchange = orig->s_optchange;
+ sp->s_position = orig->s_position;
+ sp->s_rabs = orig->s_rabs;
+ sp->s_rcm = orig->s_rcm;
+ sp->s_refresh = orig->s_refresh;
+ sp->s_scroll = orig->s_scroll;
+ sp->s_split = orig->s_split;
+ sp->s_suspend = orig->s_suspend;
+ sp->s_window = orig->s_window;
+
+ F_SET(sp, F_ISSET(orig, S_SCREENS));
+ }
+
+ if (xaw_screen_copy(orig, sp)) /* Init S_VI_XAW screen. */
+ return (1);
+ if (svi_screen_copy(orig, sp)) /* Init S_VI_CURSES screen. */
+ return (1);
+ if (sex_screen_copy(orig, sp)) /* Init S_EX screen. */
+ return (1);
+ if (v_screen_copy(orig, sp)) /* Init vi. */
+ return (1);
+ if (ex_screen_copy(orig, sp)) /* Init ex. */
+ return (1);
+
+ *spp = sp;
+ return (0);
+}
+
+/*
+ * screen_end --
+ * Release a screen.
+ */
+int
+screen_end(sp)
+ SCR *sp;
+{
+ int rval;
+
+ rval = 0;
+ if (xaw_screen_end(sp)) /* End S_VI_XAW screen. */
+ rval = 1;
+ if (svi_screen_end(sp)) /* End S_VI_CURSES screen. */
+ rval = 1;
+ if (sex_screen_end(sp)) /* End S_EX screen. */
+ rval = 1;
+ if (v_screen_end(sp)) /* End vi. */
+ rval = 1;
+ if (ex_screen_end(sp)) /* End ex. */
+ rval = 1;
+
+ /* Free FREF's. */
+ { FREF *frp;
+ while ((frp = sp->frefq.cqh_first) != (FREF *)&sp->frefq) {
+ CIRCLEQ_REMOVE(&sp->frefq, frp, q);
+ if (frp->name != NULL)
+ free(frp->name);
+ if (frp->tname != NULL)
+ free(frp->tname);
+ FREE(frp, sizeof(FREF));
+ }
+ }
+
+ /* Free file names. */
+ { char **ap;
+ if (!F_ISSET(sp, S_ARGNOFREE) && sp->argv != NULL) {
+ for (ap = sp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(sp->argv);
+ }
+ }
+
+ /* Free any text input. */
+ text_lfree(&sp->__tiq);
+
+ /* Free any script information. */
+ if (F_ISSET(sp, S_SCRIPT))
+ sscr_end(sp);
+
+ /* Free alternate file name. */
+ if (sp->alt_name != NULL)
+ free(sp->alt_name);
+
+ /* Free up search information. */
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ if (sp->newl != NULL)
+ FREE(sp->newl, sp->newl_len);
+
+ /* Free all the options */
+ opts_free(sp);
+
+ /*
+ * Free the message chain last, so previous failures have a place
+ * to put messages. Copy messages to (in order) a related screen,
+ * any screen, the global area.
+ */
+ { SCR *c_sp; MSG *mp, *next;
+ if ((c_sp = sp->q.cqe_prev) != (void *)&sp->gp->dq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else if ((c_sp = sp->q.cqe_next) != (void *)&sp->gp->dq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else if ((c_sp =
+ sp->gp->hq.cqh_first) != (void *)&sp->gp->hq) {
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(c_sp, S_BELLSCHED);
+ } else {
+ c_sp = NULL;
+ if (F_ISSET(sp, S_BELLSCHED))
+ F_SET(sp->gp, G_BELLSCHED);
+ }
+
+ for (mp = sp->msgq.lh_first; mp != NULL; mp = next) {
+ if (!F_ISSET(mp, M_EMPTY))
+ msg_app(sp->gp, c_sp,
+ mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+ next = mp->q.le_next;
+ if (mp->mbuf != NULL)
+ FREE(mp->mbuf, mp->blen);
+ FREE(mp, sizeof(MSG));
+ }
+ }
+
+ /* Remove the screen from the displayed queue. */
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->dq, sp, q);
+ SIGUNBLOCK(sp->gp);
+
+ /* Free the screen itself. */
+ FREE(sp, sizeof(SCR));
+
+ return (rval);
+}
diff --git a/usr.bin/vi/common/screen.h b/usr.bin/vi/common/screen.h
new file mode 100644
index 0000000..31df988
--- /dev/null
+++ b/usr.bin/vi/common/screen.h
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)screen.h 8.127 (Berkeley) 8/8/94
+ */
+
+/*
+ * There are minimum values that vi has to have to display a screen. The
+ * row minimum is fixed at 1 line for the text, and 1 line for any error
+ * messages. The column calculation is a lot trickier. For example, you
+ * have to have enough columns to display the line number, not to mention
+ * guaranteeing that tabstop and shiftwidth values are smaller than the
+ * current column value. It's a lot simpler to have a fixed value and not
+ * worry about it.
+ *
+ * XXX
+ * MINIMUM_SCREEN_COLS is probably wrong.
+ */
+#define MINIMUM_SCREEN_ROWS 2
+#define MINIMUM_SCREEN_COLS 20
+
+enum adjust { /* Screen adjustment operations. */
+ A_DECREASE, A_INCREASE, A_SET };
+enum operation { /* Line operations. */
+ LINE_APPEND, LINE_DELETE, LINE_INSERT, LINE_RESET };
+enum position { /* Position operations. */
+ P_BOTTOM, P_FILL, P_MIDDLE, P_TOP };
+enum sctype { /* Scroll operations. */
+ CNTRL_B, CNTRL_D, CNTRL_E, CNTRL_F, CNTRL_U, CNTRL_Y, Z_CARAT, Z_PLUS };
+
+/*
+ * Structure for holding file references. Each SCR structure contains a
+ * linked list of these. The structure contains the name of the file,
+ * along with the information that follows the name.
+ *
+ * !!!
+ * The read-only bit follows the file name, not the file itself.
+ *
+ * XXX
+ * The mtime field should be a struct timespec, but time_t is more portable.
+ */
+struct _fref {
+ CIRCLEQ_ENTRY(_fref) q; /* Linked list of file references. */
+ char *name; /* File name. */
+ char *tname; /* Backing temporary file name. */
+
+ recno_t lno; /* 1-N: file cursor line. */
+ size_t cno; /* 0-N: file cursor column. */
+
+#define FR_CURSORSET 0x001 /* If lno/cno values valid. */
+#define FR_DONTDELETE 0x002 /* Don't delete the temporary file. */
+#define FR_FNONBLANK 0x004 /* Move to the first non-<blank>. */
+#define FR_NAMECHANGE 0x008 /* If the name changed. */
+#define FR_NEWFILE 0x010 /* File doesn't really exist yet. */
+#define FR_RDONLY 0x020 /* File is read-only. */
+#define FR_READNAMED 0x040 /* Read renamed the file. */
+#define FR_RECOVER 0x080 /* File is being recovered. */
+#define FR_TMPEXIT 0x100 /* Modified temporary file, no exit. */
+#define FR_TMPFILE 0x200 /* If file has no name. */
+#define FR_UNLOCKED 0x400 /* File couldn't be locked. */
+ u_int16_t flags;
+};
+
+#define TEMPORARY_FILE_STRING "/tmp" /* Default temporary file name. */
+
+/*
+ * SCR --
+ * The screen structure. To the extent possible, all screen information
+ * is stored in the various private areas. The only information here
+ * is used by global routines or is shared by too many screens.
+ */
+struct _scr {
+/* INITIALIZED AT SCREEN CREATE. */
+ CIRCLEQ_ENTRY(_scr) q; /* Screens. */
+
+ GS *gp; /* Pointer to global area. */
+
+ SCR *nextdisp; /* Next display screen. */
+
+ EXF *ep; /* Screen's current EXF structure. */
+
+ MSGH msgq; /* Message list. */
+ /* FREF list. */
+ CIRCLEQ_HEAD(_frefh, _fref) frefq;
+ FREF *frp; /* FREF being edited. */
+
+ char **argv; /* NULL terminated file name array. */
+ char **cargv; /* Current file name. */
+
+ u_long ccnt; /* Command count. */
+ u_long q_ccnt; /* Quit or ZZ command count. */
+
+ /* Screen's: */
+ size_t rows; /* 1-N: number of rows. */
+ size_t cols; /* 1-N: number of columns. */
+ size_t woff; /* 0-N: row offset in screen. */
+ size_t t_rows; /* 1-N: cur number of text rows. */
+ size_t t_maxrows; /* 1-N: max number of text rows. */
+ size_t t_minrows; /* 1-N: min number of text rows. */
+
+ /* Cursor's: */
+ recno_t lno; /* 1-N: file line. */
+ size_t cno; /* 0-N: file character in line. */
+
+ size_t rcm; /* Vi: 0-N: Most attractive column. */
+ int rcm_last; /* Cursor drawn to the last column. */
+
+#define L_ADDED 0 /* Added lines. */
+#define L_CHANGED 1 /* Changed lines. */
+#define L_DELETED 2 /* Deleted lines. */
+#define L_JOINED 3 /* Joined lines. */
+#define L_MOVED 4 /* Moved lines. */
+#define L_LSHIFT 5 /* Left shift lines. */
+#define L_RSHIFT 6 /* Right shift lines. */
+#define L_YANKED 7 /* Yanked lines. */
+ recno_t rptlchange; /* Ex/vi: last L_CHANGED lno. */
+ recno_t rptlines[L_YANKED + 1];/* Ex/vi: lines changed by last op. */
+
+ FILE *stdfp; /* Ex output file pointer. */
+
+ char *if_name; /* Ex input file name, for messages. */
+ recno_t if_lno; /* Ex input file line, for messages. */
+
+ fd_set rdfd; /* Ex/vi: read fd select mask. */
+
+ TEXTH __tiq; /* Ex/vi: text input queue. */
+ TEXTH *tiqp; /* Ex/vi: text input queue reference. */
+
+ SCRIPT *script; /* Vi: script mode information .*/
+
+ recno_t defscroll; /* Vi: ^D, ^U scroll information. */
+
+ struct timeval busy_tod; /* ITIMER_REAL: busy time-of-day. */
+ char const *busy_msg; /* ITIMER_REAL: busy message. */
+
+ /* Display character. */
+ CHAR_T cname[MAX_CHARACTER_COLUMNS + 1];
+ size_t clen; /* Length of display character. */
+
+#define MAX_MODE_NAME 12
+ char *showmode; /* Mode. */
+
+ void *ex_private; /* Ex private area. */
+ void *sex_private; /* Ex screen private area. */
+ void *vi_private; /* Vi private area. */
+ void *svi_private; /* Vi curses screen private area. */
+ void *xaw_private; /* Vi XAW screen private area. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ char *alt_name; /* Ex/vi: alternate file name. */
+
+ /* Ex/vi: search/substitute info. */
+ regex_t sre; /* Last search RE. */
+ regex_t subre; /* Last substitute RE. */
+ enum direction searchdir; /* File search direction. */
+ char *repl; /* Substitute replacement. */
+ size_t repl_len; /* Substitute replacement length.*/
+ size_t *newl; /* Newline offset array. */
+ size_t newl_len; /* Newline array size. */
+ size_t newl_cnt; /* Newlines in replacement. */
+ u_char c_suffix; /* Edcompatible 'c' suffix value. */
+ u_char g_suffix; /* Edcompatible 'g' suffix value. */
+
+ u_int saved_vi_mode; /* Saved vi display type. */
+
+ OPTION opts[O_OPTIONCOUNT]; /* Options. */
+
+/*
+ * SCREEN SUPPORT ROUTINES.
+ *
+ * A SCR * MUST be the first argument to these routines.
+ */
+ /* Ring the screen bell. */
+ void (*s_bell) __P((SCR *));
+ /* Background the screen. */
+ int (*s_bg) __P((SCR *));
+ /* Put up a busy message. */
+ int (*s_busy) __P((SCR *, char const *));
+ /* Change a screen line. */
+ int (*s_change) __P((SCR *, EXF *, recno_t, enum operation));
+ /* Clear the screen. */
+ int (*s_clear) __P((SCR *));
+ /* Return column close to specified. */
+ size_t (*s_colpos) __P((SCR *, EXF *, recno_t, size_t));
+ /* Return the logical cursor column. */
+ int (*s_column) __P((SCR *, EXF *, size_t *));
+ enum confirm /* Confirm an action with the user. */
+ (*s_confirm) __P((SCR *, EXF *, MARK *, MARK *));
+ /* Change the relative screen size. */
+ int (*s_crel) __P((SCR *, long));
+ /* Edit a file. */
+ int (*s_edit) __P((SCR *, EXF *));
+ /* End a screen. */
+ int (*s_end) __P((SCR *));
+ /* Run a single ex command. */
+ int (*s_ex_cmd) __P((SCR *, EXF *, EXCMDARG *, MARK *));
+ /* Run user's ex commands. */
+ int (*s_ex_run) __P((SCR *, EXF *, MARK *));
+ /* Screen's ex write function. */
+ int (*s_ex_write) __P((void *, const char *, int));
+ /* Foreground the screen. */
+ int (*s_fg) __P((SCR *, CHAR_T *));
+ /* Fill the screen's map. */
+ int (*s_fill) __P((SCR *, EXF *, recno_t, enum position));
+ enum input /* Get a line from the user. */
+ (*s_get) __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
+ enum input /* Get a key from the user. */
+ (*s_key_read) __P((SCR *, int *, struct timeval *));
+ /* Map a function key. */
+ int (*s_fmap) __P((SCR *,
+ enum seqtype, CHAR_T *, size_t, CHAR_T *, size_t));
+ /* Tell the screen an option changed. */
+ int (*s_optchange) __P((SCR *, int));
+ /* Return column at screen position. */
+ int (*s_position) __P((SCR *, EXF *,
+ MARK *, u_long, enum position));
+ /* Change the absolute screen size. */
+ int (*s_rabs) __P((SCR *, long, enum adjust));
+ /* Return column close to selection. */
+ size_t (*s_rcm) __P((SCR *, EXF *, recno_t));
+ /* Refresh the screen. */
+ int (*s_refresh) __P((SCR *, EXF *));
+ /* Move down the screen. */
+ int (*s_scroll) __P((SCR *, EXF *, MARK *, recno_t, enum sctype));
+ /* Split the screen. */
+ int (*s_split) __P((SCR *, ARGS *[], int));
+ /* Suspend the screen. */
+ int (*s_suspend) __P((SCR *));
+ /* Set the window size. */
+ int (*s_window) __P((SCR *, int));
+
+/* Editor screens. */
+#define S_EX 0x0000001 /* Ex screen. */
+#define S_VI_CURSES 0x0000002 /* Vi: curses screen. */
+#define S_VI_XAW 0x0000004 /* Vi: Athena widgets screen. */
+
+#define IN_EX_MODE(sp) /* If in ex mode. */ \
+ (F_ISSET(sp, S_EX))
+#define IN_VI_MODE(sp) /* If in vi mode. */ \
+ (F_ISSET(sp, S_VI_CURSES | S_VI_XAW))
+#define S_SCREENS /* Screens. */ \
+ (S_EX | S_VI_CURSES | S_VI_XAW)
+
+/* Major screen/file changes. */
+#define S_EXIT 0x0000008 /* Exiting (not forced). */
+#define S_EXIT_FORCE 0x0000010 /* Exiting (forced). */
+#define S_FSWITCH 0x0000020 /* Switch files. */
+#define S_SSWITCH 0x0000040 /* Switch screens. */
+#define S_MAJOR_CHANGE /* Screen or file changes. */ \
+ (S_EXIT | S_EXIT_FORCE | S_FSWITCH | S_SSWITCH)
+
+#define S_ARGNOFREE 0x0000080 /* Argument list wasn't allocated. */
+#define S_ARGRECOVER 0x0000100 /* Argument list is recovery files. */
+#define S_BELLSCHED 0x0000200 /* Bell scheduled. */
+#define S_CONTINUE 0x0000400 /* Need to ask the user to continue. */
+#define S_EXSILENT 0x0000800 /* Ex batch script. */
+#define S_GLOBAL 0x0001000 /* Doing a global command. */
+#define S_INPUT 0x0002000 /* Doing text input. */
+#define S_INTERRUPTED 0x0004000 /* If have been interrupted. */
+#define S_INTERRUPTIBLE 0x0008000 /* If can be interrupted. */
+#define S_IVIDEO 0x0010000 /* Display in inverse video. */
+#define S_REDRAW 0x0020000 /* Redraw the screen. */
+#define S_REFORMAT 0x0040000 /* Reformat the screen. */
+#define S_REFRESH 0x0080000 /* Refresh the screen. */
+#define S_RENUMBER 0x0100000 /* Renumber the screen. */
+#define S_RESIZE 0x0200000 /* Resize the screen. */
+#define S_SCRIPT 0x0400000 /* Window is a shell script. */
+#define S_SRE_SET 0x0800000 /* The search RE has been set. */
+#define S_SUBRE_SET 0x1000000 /* The substitute RE has been set. */
+#define S_UPDATE_MODE 0x2000000 /* Don't repaint modeline. */
+#define S_VLITONLY 0x4000000 /* ^V literal next only. */
+ u_int32_t flags;
+};
+
+/*
+ * Signals/timers have no structure, so it's all here.
+ *
+ * Block all signals that are being handled. Used to keep the underlying DB
+ * system calls from being interrupted and not restarted, as it could cause
+ * consistency problems. Also used when vi forks child processes, to avoid
+ * a signal arriving after the fork and before the exec, causing both parent
+ * and child to attempt recovery processing.
+ */
+#define SIGBLOCK(gp) \
+ (void)sigprocmask(SIG_BLOCK, &(gp)->blockset, NULL);
+#define SIGUNBLOCK(gp) \
+ (void)sigprocmask(SIG_UNBLOCK, &(gp)->blockset, NULL);
+
+void busy_off __P((SCR *));
+int busy_on __P((SCR *, char const *));
+void sig_end __P((void));
+int sig_init __P((SCR *));
+
+/* Generic routines to start/stop a screen. */
+int screen_end __P((SCR *));
+int screen_init __P((SCR *, SCR **, u_int));
+
+/* Public interfaces to the underlying screens. */
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_screen_init __P((SCR *));
+int sex_screen_copy __P((SCR *, SCR *));
+int sex_screen_end __P((SCR *));
+int sex_screen_init __P((SCR *));
+int svi_screen_copy __P((SCR *, SCR *));
+int svi_screen_end __P((SCR *));
+int svi_screen_init __P((SCR *));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+int v_screen_init __P((SCR *));
+int xaw_screen_copy __P((SCR *, SCR *));
+int xaw_screen_end __P((SCR *));
+int xaw_screen_init __P((SCR *));
diff --git a/usr.bin/vi/common/search.c b/usr.bin/vi/common/search.c
new file mode 100644
index 0000000..f7e96eb
--- /dev/null
+++ b/usr.bin/vi/common/search.c
@@ -0,0 +1,833 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)search.c 8.48 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+static int check_delta __P((SCR *, EXF *, long, recno_t));
+static int ctag_conv __P((SCR *, char **, int *));
+static int get_delta __P((SCR *, char **, long *, u_int *));
+static int resetup __P((SCR *, regex_t **, enum direction,
+ char *, char **, long *, u_int *));
+
+/*
+ * resetup --
+ * Set up a search for a regular expression.
+ */
+static int
+resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
+ SCR *sp;
+ regex_t **rep;
+ enum direction dir;
+ char *ptrn, **epp;
+ long *deltap;
+ u_int *flagp;
+{
+ u_int flags;
+ int delim, eval, re_flags, replaced;
+ char *p, *t;
+
+ /* Set return information the default. */
+ *deltap = 0;
+
+ /*
+ * Use saved pattern if no pattern supplied, or if only a delimiter
+ * character is supplied. Only the pattern was saved, historic vi
+ * did not reuse any delta supplied.
+ */
+ flags = *flagp;
+ if (ptrn == NULL)
+ goto prev;
+ if (ptrn[1] == '\0') {
+ if (epp != NULL)
+ *epp = ptrn + 1;
+ goto prev;
+ }
+ if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
+ if (epp != NULL)
+ *epp = ptrn + 2;
+prev: if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous search pattern");
+ return (1);
+ }
+ *rep = &sp->sre;
+
+ /* Empty patterns set the direction. */
+ if (LF_ISSET(SEARCH_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->searchdir = dir;
+ sp->sre = **rep;
+ }
+ return (0);
+ }
+
+ re_flags = 0; /* Set RE flags. */
+ if (O_ISSET(sp, O_EXTENDED))
+ re_flags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ re_flags |= REG_ICASE;
+
+ replaced = 0;
+ if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
+ /* Set delimiter. */
+ delim = *ptrn++;
+
+ /* Find terminating delimiter, handling escaped delimiters. */
+ for (p = t = ptrn;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ *t = '\0';
+ break;
+ }
+ if (p[1] == delim && p[0] == '\\')
+ ++p;
+ *t++ = *p++;
+ }
+
+ /*
+ * If characters after the terminating delimiter, it may
+ * be an error, or may be an offset. In either case, we
+ * return the end of the string, whatever it may be.
+ */
+ if (*p) {
+ if (get_delta(sp, &p, deltap, flagp))
+ return (1);
+ if (*p && LF_ISSET(SEARCH_TERM)) {
+ msgq(sp, M_ERR,
+ "Characters after search string and/or delta");
+ return (1);
+ }
+ }
+ if (epp != NULL)
+ *epp = p;
+
+ /* Check for "/ " or other such silliness. */
+ if (*ptrn == '\0')
+ goto prev;
+
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+ } else if (LF_ISSET(SEARCH_TAG)) {
+ if (ctag_conv(sp, &ptrn, &replaced))
+ return (1);
+ re_flags &= ~(REG_EXTENDED | REG_ICASE);
+ }
+
+ /* Compile the RE. */
+ if (eval = regcomp(*rep, ptrn, re_flags))
+ re_error(sp, eval, *rep);
+ else if (LF_ISSET(SEARCH_SET)) {
+ F_SET(sp, S_SRE_SET);
+ sp->searchdir = dir;
+ sp->sre = **rep;
+ }
+
+ /* Free up any extra memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+ return (eval);
+}
+
+/*
+ * ctag_conv --
+ * Convert a tags search path into something that the POSIX
+ * 1003.2 RE functions can handle.
+ */
+static int
+ctag_conv(sp, ptrnp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ int *replacedp;
+{
+ size_t blen, len;
+ int lastdollar;
+ char *bp, *p, *t;
+
+ *replacedp = 0;
+
+ len = strlen(p = *ptrnp);
+
+ /* Max memory usage is 2 times the length of the string. */
+ GET_SPACE_RET(sp, bp, blen, len * 2);
+
+ t = bp;
+
+ /* The last character is a '/' or '?', we just strip it. */
+ if (p[len - 1] == '/' || p[len - 1] == '?')
+ p[len - 1] = '\0';
+
+ /* The next-to-last character is a '$', and it's magic. */
+ if (p[len - 2] == '$') {
+ lastdollar = 1;
+ p[len - 2] = '\0';
+ } else
+ lastdollar = 0;
+
+ /* The first character is a '/' or '?', we just strip it. */
+ if (p[0] == '/' || p[0] == '?')
+ ++p;
+
+ /* The second character is a '^', and it's magic. */
+ if (p[0] == '^')
+ *t++ = *p++;
+
+ /*
+ * Escape every other magic character we can find, stripping the
+ * backslashes ctags inserts to escape the search delimiter
+ * characters.
+ */
+ while (p[0]) {
+ /* Ctags escapes the search delimiter characters. */
+ if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
+ ++p;
+ else if (strchr("^.[]$*", p[0]))
+ *t++ = '\\';
+ *t++ = *p++;
+ }
+ if (lastdollar)
+ *t++ = '$';
+ *t++ = '\0';
+
+ *ptrnp = bp;
+ *replacedp = 1;
+ return (0);
+}
+
+#define EMPTYMSG "File empty; nothing to search"
+#define EOFMSG "Reached end-of-file without finding the pattern"
+#define NOTFOUND "Pattern not found"
+#define SOFMSG "Reached top-of-file without finding the pattern"
+#define WRAPMSG "Search wrapped"
+
+int
+f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ u_int *flagp;
+{
+ regmatch_t match[1];
+ regex_t *re, lre;
+ recno_t lno;
+ size_t coff, len;
+ long delta;
+ u_int flags;
+ int btear, eval, rval, wrapped;
+ char *l;
+
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ flags = *flagp;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ return (1);
+ }
+
+ re = &lre;
+ if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
+ return (1);
+
+ /*
+ * Start searching immediately after the cursor. If at the end of the
+ * line, start searching on the next line. This is incompatible (read
+ * bug fix) with the historic vi -- searches for the '$' pattern never
+ * moved forward, and "-t foo" didn't work if "foo" was the first thing
+ * in the file.
+ */
+ if (LF_ISSET(SEARCH_FILE)) {
+ lno = 1;
+ coff = 0;
+ } else {
+ if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, fm->lno);
+ return (1);
+ }
+ if (fm->cno + 1 >= len) {
+ if (fm->lno == lno) {
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EOFMSG);
+ return (1);
+ }
+ lno = 1;
+ } else
+ lno = fm->lno + 1;
+ coff = 0;
+ } else {
+ lno = fm->lno;
+ coff = fm->cno + 1;
+ }
+ }
+
+ /* Turn on busy message. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
+
+ for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
+ if (INTERRUPTED(sp)) {
+ msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+ if (wrapped && lno > fm->lno ||
+ (l = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EOFMSG);
+ break;
+ }
+ lno = 0;
+ wrapped = 1;
+ continue;
+ }
+
+ /* If already at EOL, just keep going. */
+ if (len && coff == len)
+ continue;
+
+ /* Set the termination. */
+ match[0].rm_so = coff;
+ match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "F search: %lu from %u to %u\n",
+ lno, coff, len ? len - 1 : len);
+#endif
+ /* Search the line. */
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ break;
+ }
+
+ /* Warn if wrapped. */
+ if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_VINFO, WRAPMSG);
+
+ /*
+ * If an offset, see if it's legal. It's possible to match
+ * past the end of the line with $, so check for that case.
+ */
+ if (delta) {
+ if (check_delta(sp, ep, delta, lno))
+ break;
+ rm->lno = delta + lno;
+ rm->cno = 0;
+ } else {
+#if defined(DEBUG) && 0
+ TRACE(sp, "found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ rm->lno = lno;
+ rm->cno = match[0].rm_so;
+
+ /*
+ * If a change command, it's possible to move beyond
+ * the end of a line. Historic vi generally got this
+ * wrong (try "c?$<cr>"). Not all that sure this gets
+ * it right, there are lots of strange cases.
+ */
+ if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
+ rm->cno = len ? len - 1 : 0;
+ }
+ rval = 0;
+ break;
+ }
+
+ /* Turn off busy message, interrupts. */
+ if (btear)
+ busy_off(sp);
+ return (rval);
+}
+
+int
+b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *rm;
+ char *ptrn, **eptrn;
+ u_int *flagp;
+{
+ regmatch_t match[1];
+ regex_t *re, lre;
+ recno_t lno;
+ size_t coff, len, last;
+ long delta;
+ u_int flags;
+ int btear, eval, rval, wrapped;
+ char *l;
+
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ flags = *flagp;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ return (1);
+ }
+
+ re = &lre;
+ if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
+ return (1);
+
+ /* If in the first column, start searching on the previous line. */
+ if (fm->cno == 0) {
+ if (fm->lno == 1) {
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, SOFMSG);
+ return (1);
+ }
+ } else
+ lno = fm->lno - 1;
+ } else
+ lno = fm->lno;
+
+ /* Turn on busy message. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Searching...");
+
+ for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
+ if (INTERRUPTED(sp)) {
+ msgq(sp, M_INFO, "Interrupted.");
+ break;
+ }
+ if (wrapped && lno < fm->lno || lno == 0) {
+ if (wrapped) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, NOTFOUND);
+ break;
+ }
+ if (!O_ISSET(sp, O_WRAPSCAN)) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, SOFMSG);
+ break;
+ }
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ if (LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_INFO, EMPTYMSG);
+ break;
+ }
+ ++lno;
+ wrapped = 1;
+ continue;
+ }
+
+ if ((l = file_gline(sp, ep, lno, &len)) == NULL)
+ goto err;
+
+ /* Set the termination. */
+ match[0].rm_so = 0;
+ match[0].rm_eo = len;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
+#endif
+ /* Search the line. */
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ continue;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ break;
+ }
+
+ /* Check for a match starting past the cursor. */
+ if (coff != 0 && match[0].rm_so >= coff)
+ continue;
+
+ /* Warn if wrapped. */
+ if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
+ msgq(sp, M_VINFO, WRAPMSG);
+
+ if (delta) {
+ if (check_delta(sp, ep, delta, lno))
+ break;
+ rm->lno = delta + lno;
+ rm->cno = 0;
+ } else {
+#if defined(DEBUG) && 0
+ TRACE(sp, "found: %qu to %qu\n",
+ match[0].rm_so, match[0].rm_eo);
+#endif
+ /*
+ * We now have the first match on the line. Step
+ * through the line character by character until we
+ * find the last acceptable match. This is painful,
+ * we need a better interface to regex to make this
+ * work.
+ */
+ for (;;) {
+ last = match[0].rm_so++;
+ if (match[0].rm_so >= len)
+ break;
+ match[0].rm_eo = len;
+ eval = regexec(re, l, 1, match,
+ (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
+ REG_STARTEND);
+ if (eval == REG_NOMATCH)
+ break;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ goto err;
+ }
+ if (coff && match[0].rm_so >= coff)
+ break;
+ }
+ rm->lno = lno;
+
+ /* See comment in f_search(). */
+ if (!LF_ISSET(SEARCH_EOL) && last >= len)
+ rm->cno = len ? len - 1 : 0;
+ else
+ rm->cno = last;
+ }
+ rval = 0;
+ break;
+ }
+
+ /* Turn off busy message, interrupts. */
+err: if (btear)
+ busy_off(sp);
+ return (rval);
+}
+
+/*
+ * re_conv --
+ * Convert vi's regular expressions into something that the
+ * the POSIX 1003.2 RE functions can handle.
+ *
+ * There are three conversions we make to make vi's RE's (specifically
+ * the global, search, and substitute patterns) work with POSIX RE's.
+ *
+ * 1: If O_MAGIC is not set, strip backslashes from the magic character
+ * set (.[]*~) that have them, and add them to the ones that don't.
+ * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
+ * from the last substitute command's replacement string. If O_MAGIC
+ * is set, it's the string "~".
+ * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
+ * new RE escapes.
+ */
+int
+re_conv(sp, ptrnp, replacedp)
+ SCR *sp;
+ char **ptrnp;
+ int *replacedp;
+{
+ size_t blen, needlen;
+ int magic;
+ char *bp, *p, *t;
+
+ /*
+ * First pass through, we figure out how much space we'll need.
+ * We do it in two passes, on the grounds that most of the time
+ * the user is doing a search and won't have magic characters.
+ * That way we can skip the malloc and memmove's.
+ */
+ for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
+ switch (*p) {
+ case '\\':
+ switch (*++p) {
+ case '<':
+ magic = 1;
+ needlen += sizeof(RE_WSTART);
+ break;
+ case '>':
+ magic = 1;
+ needlen += sizeof(RE_WSTOP);
+ break;
+ case '~':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += 1;
+ }
+ break;
+ default:
+ needlen += 2;
+ }
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC)) {
+ magic = 1;
+ needlen += 2;
+ }
+ break;
+ default:
+ needlen += 1;
+ break;
+ }
+
+ if (!magic) {
+ *replacedp = 0;
+ return (0);
+ }
+
+ /*
+ * Get enough memory to hold the final pattern.
+ *
+ * XXX
+ * It's nul-terminated, for now.
+ */
+ GET_SPACE_RET(sp, bp, blen, needlen + 1);
+
+ for (p = *ptrnp, t = bp; *p != '\0'; ++p)
+ switch (*p) {
+ case '\\':
+ switch (*++p) {
+ case '<':
+ memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
+ t += sizeof(RE_WSTART) - 1;
+ break;
+ case '>':
+ memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
+ t += sizeof(RE_WSTOP) - 1;
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '~';
+ else {
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ }
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = '\\';
+ *t++ = *p;
+ }
+ break;
+ case '~':
+ if (O_ISSET(sp, O_MAGIC)) {
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ } else
+ *t++ = '~';
+ break;
+ case '.':
+ case '[':
+ case ']':
+ case '*':
+ if (!O_ISSET(sp, O_MAGIC))
+ *t++ = '\\';
+ *t++ = *p;
+ break;
+ default:
+ *t++ = *p;
+ break;
+ }
+ *t = '\0';
+
+ *ptrnp = bp;
+ *replacedp = 1;
+ return (0);
+}
+
+/*
+ * get_delta --
+ * Get a line delta. The trickiness is that the delta can be pretty
+ * complicated, i.e. "+3-2+3++- ++" is allowed.
+ *
+ * !!!
+ * In historic vi, if you had a delta on a search pattern which was used as
+ * a motion command, the command became a line mode command regardless of the
+ * cursor positions. A fairly common trick is to use a delta of "+0" to make
+ * the command a line mode command. This is the only place that knows about
+ * delta's, so we set the return flag information here.
+ */
+static int
+get_delta(sp, dp, valp, flagp)
+ SCR *sp;
+ char **dp;
+ long *valp;
+ u_int *flagp;
+{
+ char *p;
+ long val, tval;
+
+ for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
+ if (isblank(*p)) {
+ ++p;
+ continue;
+ }
+ if (*p == '+' || *p == '-') {
+ if (!isdigit(*(p + 1))) {
+ if (*p == '+') {
+ if (tval == LONG_MAX)
+ goto overflow;
+ ++tval;
+ } else {
+ if (tval == LONG_MIN)
+ goto underflow;
+ --tval;
+ }
+ ++p;
+ continue;
+ }
+ } else
+ if (!isdigit(*p))
+ break;
+
+ errno = 0;
+ val = strtol(p, &p, 10);
+ if (errno == ERANGE) {
+ if (val == LONG_MAX)
+overflow: msgq(sp, M_ERR, "Delta value overflow");
+ else if (val == LONG_MIN)
+underflow: msgq(sp, M_ERR, "Delta value underflow");
+ else
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ if (val >= 0) {
+ if (LONG_MAX - val < tval)
+ goto overflow;
+ } else
+ if (-(LONG_MIN - tval) > val)
+ goto underflow;
+ tval += val;
+ }
+ *dp = p;
+ *valp = tval;
+ return (0);
+}
+
+/*
+ * check_delta --
+ * Check a line delta to see if it's legal.
+ */
+static int
+check_delta(sp, ep, delta, lno)
+ SCR *sp;
+ EXF *ep;
+ long delta;
+ recno_t lno;
+{
+ /* A delta can overflow a record number. */
+ if (delta < 0) {
+ if (lno < LONG_MAX && delta >= (long)lno) {
+ msgq(sp, M_ERR, "Search offset before line 1");
+ return (1);
+ }
+ } else {
+ if (ULONG_MAX - lno < delta) {
+ msgq(sp, M_ERR, "Delta value overflow");
+ return (1);
+ }
+ if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
+ msgq(sp, M_ERR, "Search offset past end-of-file");
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * re_error --
+ * Report a regular expression error.
+ */
+void
+re_error(sp, errcode, preg)
+ SCR *sp;
+ int errcode;
+ regex_t *preg;
+{
+ size_t s;
+ char *oe;
+
+ s = regerror(errcode, preg, "", 0);
+ if ((oe = malloc(s)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+ else {
+ (void)regerror(errcode, preg, oe, s);
+ msgq(sp, M_ERR, "RE error: %s", oe);
+ }
+ free(oe);
+}
diff --git a/usr.bin/vi/common/search.h b/usr.bin/vi/common/search.h
new file mode 100644
index 0000000..1483a04
--- /dev/null
+++ b/usr.bin/vi/common/search.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)search.h 8.9 (Berkeley) 3/16/94
+ */
+
+#define RE_WSTART "[[:<:]]" /* Not-in-word search patterns. */
+#define RE_WSTOP "[[:>:]]"
+
+#define SEARCH_DELTA 0x001 /* A delta part of the search.*/
+#define SEARCH_EOL 0x002 /* Offset past EOL is okay. */
+#define SEARCH_FILE 0x004 /* Search the entire file. */
+#define SEARCH_MSG 0x008 /* Display search warning messages. */
+#define SEARCH_PARSE 0x010 /* Parse the search pattern. */
+#define SEARCH_SET 0x020 /* Set search direction. */
+#define SEARCH_TAG 0x040 /* Search pattern is a tag pattern. */
+#define SEARCH_TERM 0x080 /* Search pattern should terminate. */
+
+enum direction { NOTSET, FORWARD, BACKWARD };
+
+/* Search functions. */
+int b_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int f_search __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+int re_conv __P((SCR *, char **, int *));
+void re_error __P((SCR *, int, regex_t *));
diff --git a/usr.bin/vi/common/seq.c b/usr.bin/vi/common/seq.c
new file mode 100644
index 0000000..75b67a6
--- /dev/null
+++ b/usr.bin/vi/common/seq.c
@@ -0,0 +1,351 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)seq.c 8.33 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * seq_set --
+ * Internal version to enter a sequence.
+ */
+int
+seq_set(sp, name, nlen, input, ilen, output, olen, stype, flags)
+ SCR *sp;
+ CHAR_T *name, *input, *output;
+ size_t nlen, ilen, olen;
+ enum seqtype stype;
+ int flags;
+{
+ CHAR_T *p;
+ SEQ *lastqp, *qp;
+ int sv_errno;
+
+ /*
+ * An input string must always be present. The output string
+ * can be NULL, when set internally, that's how we throw away
+ * input.
+ *
+ * Just replace the output field if the string already set.
+ */
+ if ((qp = seq_find(sp, &lastqp, input, ilen, stype, NULL)) != NULL) {
+ if (output == NULL || olen == 0) {
+ p = NULL;
+ olen = 0;
+ } else if ((p = v_strdup(sp, output, olen)) == NULL) {
+ sv_errno = errno;
+ goto mem1;
+ }
+ if (qp->output != NULL)
+ free(qp->output);
+ qp->olen = olen;
+ qp->output = p;
+ return (0);
+ }
+
+ /* Allocate and initialize SEQ structure. */
+ CALLOC(sp, qp, SEQ *, 1, sizeof(SEQ));
+ if (qp == NULL) {
+ sv_errno = errno;
+ goto mem1;
+ }
+
+ /* Name. */
+ if (name == NULL || nlen == 0)
+ qp->name = NULL;
+ else if ((qp->name = v_strdup(sp, name, nlen)) == NULL) {
+ sv_errno = errno;
+ goto mem2;
+ }
+ qp->nlen = nlen;
+
+ /* Input. */
+ if ((qp->input = v_strdup(sp, input, ilen)) == NULL) {
+ sv_errno = errno;
+ goto mem3;
+ }
+ qp->ilen = ilen;
+
+ /* Output. */
+ if (output == NULL) {
+ qp->output = NULL;
+ olen = 0;
+ } else if ((qp->output = v_strdup(sp, output, olen)) == NULL) {
+ sv_errno = errno;
+ free(qp->input);
+mem3: if (qp->name != NULL)
+ free(qp->name);
+mem2: FREE(qp, sizeof(SEQ));
+mem1: errno = sv_errno;
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ qp->olen = olen;
+
+ /* Type, flags. */
+ qp->stype = stype;
+ qp->flags = flags;
+
+ /* Link into the chain. */
+ if (lastqp == NULL) {
+ LIST_INSERT_HEAD(&sp->gp->seqq, qp, q);
+ } else {
+ LIST_INSERT_AFTER(lastqp, qp, q);
+ }
+
+ /* Set the fast lookup bit. */
+ if (qp->input[0] < MAX_BIT_SEQ)
+ bit_set(sp->gp->seqb, qp->input[0]);
+
+ return (0);
+}
+
+/*
+ * seq_delete --
+ * Delete a sequence.
+ */
+int
+seq_delete(sp, input, ilen, stype)
+ SCR *sp;
+ CHAR_T *input;
+ size_t ilen;
+ enum seqtype stype;
+{
+ SEQ *qp;
+
+ if ((qp = seq_find(sp, NULL, input, ilen, stype, NULL)) == NULL)
+ return (1);
+ return (seq_mdel(qp));
+}
+
+/*
+ * seq_mdel --
+ * Delete a map entry, without lookup.
+ */
+int
+seq_mdel(qp)
+ SEQ *qp;
+{
+ LIST_REMOVE(qp, q);
+ if (qp->name != NULL)
+ free(qp->name);
+ free(qp->input);
+ if (qp->output != NULL)
+ free(qp->output);
+ FREE(qp, sizeof(SEQ));
+ return (0);
+}
+
+/*
+ * seq_find --
+ * Search the sequence list for a match to a buffer, if ispartial
+ * isn't NULL, partial matches count.
+ */
+SEQ *
+seq_find(sp, lastqp, input, ilen, stype, ispartialp)
+ SCR *sp;
+ SEQ **lastqp;
+ CHAR_T *input;
+ size_t ilen;
+ enum seqtype stype;
+ int *ispartialp;
+{
+ SEQ *lqp, *qp;
+ int diff;
+
+ /*
+ * Ispartialp is a location where we return if there was a
+ * partial match, i.e. if the string were extended it might
+ * match something.
+ *
+ * XXX
+ * Overload the meaning of ispartialp; only the terminal key
+ * search doesn't want the search limited to complete matches,
+ * i.e. ilen may be longer than the match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 0;
+ for (lqp = NULL, qp = sp->gp->seqq.lh_first;
+ qp != NULL; lqp = qp, qp = qp->q.le_next) {
+ /* Fast checks on the first character and type. */
+ if (qp->input[0] > input[0])
+ break;
+ if (qp->input[0] < input[0] ||
+ qp->stype != stype || F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+
+ /* Check on the real comparison. */
+ diff = memcmp(qp->input, input, MIN(qp->ilen, ilen));
+ if (diff > 0)
+ break;
+ if (diff < 0)
+ continue;
+ /*
+ * If the entry is the same length as the string, return a
+ * match. If the entry is shorter than the string, return a
+ * match if called from the terminal key routine. Otherwise,
+ * keep searching for a complete match.
+ */
+ if (qp->ilen <= ilen) {
+ if (qp->ilen == ilen || ispartialp != NULL) {
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (qp);
+ }
+ continue;
+ }
+ /*
+ * If the entry longer than the string, return partial match
+ * if called from the terminal key routine. Otherwise, no
+ * match.
+ */
+ if (ispartialp != NULL)
+ *ispartialp = 1;
+ break;
+ }
+ if (lastqp != NULL)
+ *lastqp = lqp;
+ return (NULL);
+}
+
+/*
+ * seq_dump --
+ * Display the sequence entries of a specified type.
+ */
+int
+seq_dump(sp, stype, isname)
+ SCR *sp;
+ enum seqtype stype;
+ int isname;
+{
+ CHAR_T *p;
+ SEQ *qp;
+ int cnt, len, olen;
+
+ cnt = 0;
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (stype != qp->stype || F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ ++cnt;
+ for (p = qp->input,
+ olen = qp->ilen, len = 0; olen > 0; --olen, ++p)
+ len += ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
+ for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
+ len -= ex_printf(EXCOOKIE, " ");
+
+ if (qp->output != NULL)
+ for (p = qp->output,
+ olen = qp->olen, len = 0; olen > 0; --olen, ++p)
+ len +=
+ ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
+ else
+ len = 0;
+
+ if (isname && qp->name != NULL) {
+ for (len = STANDARD_TAB - len % STANDARD_TAB; len > 0;)
+ len -= ex_printf(EXCOOKIE, " ");
+ for (p = qp->name,
+ olen = qp->nlen; olen > 0; --olen, ++p)
+ (void)ex_printf(EXCOOKIE,
+ "%s", KEY_NAME(sp, *p));
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ return (cnt);
+}
+
+/*
+ * seq_save --
+ * Save the sequence entries to a file.
+ */
+int
+seq_save(sp, fp, prefix, stype)
+ SCR *sp;
+ FILE *fp;
+ char *prefix;
+ enum seqtype stype;
+{
+ CHAR_T *p;
+ SEQ *qp;
+ size_t olen;
+ int ch;
+
+ /* Write a sequence command for all keys the user defined. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (stype != qp->stype ||
+ F_ISSET(qp, SEQ_FUNCMAP) || !F_ISSET(qp, SEQ_USERDEF))
+ continue;
+ if (prefix)
+ (void)fprintf(fp, "%s", prefix);
+ for (p = qp->input, olen = qp->ilen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == CH_LITERAL || ch == '|' ||
+ isblank(ch) || KEY_VAL(sp, ch) == K_NL)
+ (void)putc(CH_LITERAL, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc(' ', fp);
+ if (qp->output != NULL)
+ for (p = qp->output,
+ olen = qp->olen; olen > 0; --olen) {
+ ch = *p++;
+ if (ch == CH_LITERAL || ch == '|' ||
+ KEY_VAL(sp, ch) == K_NL)
+ (void)putc(CH_LITERAL, fp);
+ (void)putc(ch, fp);
+ }
+ (void)putc('\n', fp);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/common/seq.h b/usr.bin/vi/common/seq.h
new file mode 100644
index 0000000..8eede33
--- /dev/null
+++ b/usr.bin/vi/common/seq.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)seq.h 8.12 (Berkeley) 8/16/94
+ */
+
+/*
+ * Map and abbreviation structures.
+ *
+ * The map structure is doubly linked list, sorted by input string and by
+ * input length within the string. (The latter is necessary so that short
+ * matches will happen before long matches when the list is searched.)
+ * Additionally, there is a bitmap which has bits set if there are entries
+ * starting with the corresponding character. This keeps us from walking
+ * the list unless it's necessary.
+ *
+ * The name and the output fields of a SEQ can be empty, i.e. NULL.
+ * Only the input field is required.
+ *
+ * XXX
+ * The fast-lookup bits are never turned off -- users don't usually unmap
+ * things, though, so it's probably not a big deal.
+ */
+ /* Sequence type. */
+enum seqtype { SEQ_ABBREV, SEQ_COMMAND, SEQ_INPUT };
+
+struct _seq {
+ LIST_ENTRY(_seq) q; /* Linked list of all sequences. */
+ enum seqtype stype; /* Sequence type. */
+ CHAR_T *name; /* Sequence name (if any). */
+ size_t nlen; /* Name length. */
+ CHAR_T *input; /* Sequence input keys. */
+ size_t ilen; /* Input keys length. */
+ CHAR_T *output; /* Sequence output keys. */
+ size_t olen; /* Output keys length. */
+
+#define SEQ_FUNCMAP 0x01 /* If unresolved function key.*/
+#define SEQ_SCREEN 0x02 /* If screen specific. */
+#define SEQ_USERDEF 0x04 /* If user defined. */
+ u_int8_t flags;
+};
+
+int seq_delete __P((SCR *, CHAR_T *, size_t, enum seqtype));
+int seq_dump __P((SCR *, enum seqtype, int));
+SEQ *seq_find __P((SCR *, SEQ **, CHAR_T *, size_t, enum seqtype, int *));
+void seq_init __P((SCR *));
+int seq_mdel __P((SEQ *));
+int seq_save __P((SCR *, FILE *, char *, enum seqtype));
+int seq_set __P((SCR *, CHAR_T *, size_t,
+ CHAR_T *, size_t, CHAR_T *, size_t, enum seqtype, int));
diff --git a/usr.bin/vi/common/signal.c b/usr.bin/vi/common/signal.c
new file mode 100644
index 0000000..362c0fb
--- /dev/null
+++ b/usr.bin/vi/common/signal.c
@@ -0,0 +1,569 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)signal.c 8.34 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+static void h_alrm __P((int));
+static void h_hup __P((int));
+static void h_int __P((int));
+static void h_term __P((int));
+static void h_winch __P((int));
+static void sig_sync __P((int, u_int));
+
+/*
+ * There are seven normally asynchronous actions about which vi cares:
+ * SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
+ *
+ * The assumptions:
+ * 1: The DB routines are not reentrant.
+ * 2: The curses routines may not be reentrant.
+ *
+ * SIGALRM, SIGHUP, SIGTERM
+ * Used for file recovery. The DB routines can't be reentered, so
+ * the vi routines that call DB block all three signals (see line.c).
+ * This means that DB routines can be called at interrupt time.
+ *
+ * SIGALRM
+ * Used to paint busy messages on the screen. The curses routines
+ * can't be reentered, so this function of SIGALRM can only be used
+ * in sections of code that do not use any curses functions (see
+ * busy_on, busy_off in signal.c). This means that curses can be
+ * called at interrupt time.
+ *
+ * SIGQUIT
+ * Disabled by the signal initialization routines. Historically,
+ * ^\ switched vi into ex mode, and we continue that practice.
+ *
+ * SIGWINCH:
+ * The interrupt routine sets a global bit which is checked by the
+ * key-read routine, so there are no reentrancy issues. This means
+ * that the screen will not resize until vi runs out of keys, but
+ * that doesn't seem like a problem.
+ *
+ * SIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has
+ * to permit the user to interrupt long-running operations. Generally, a
+ * search, substitution or read/write is done on a large file, or, the user
+ * creates a key mapping with an infinite loop. This problem will become
+ * worse as more complex semantics are added to vi. There are four major
+ * solutions on the table, each of which have minor permutations.
+ *
+ * 1: Run in raw mode.
+ *
+ * The up side is that there's no asynchronous behavior to worry about,
+ * and obviously no reentrancy problems. The down side is that it's easy
+ * to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
+ * like an interrupt) and it's easy to get into places where we won't see
+ * interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
+ * historic implementations of vi). Periodically reading the terminal
+ * input buffer might solve the latter problem, but it's not going to be
+ * pretty.
+ *
+ * Also, we're going to be checking for ^C's and ^Z's both, all over
+ * the place -- I hate to litter the source code with that. For example,
+ * the historic version of vi didn't permit you to suspend the screen if
+ * you were on the colon command line. This isn't right. ^Z isn't a vi
+ * command, it's a terminal event. (Dammit.)
+ *
+ * 2: Run in cbreak mode. There are two problems in this area. First, the
+ * current curses implementations (both System V and Berkeley) don't give
+ * you clean cbreak modes. For example, the IEXTEN bit is left on, turning
+ * on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
+ * the exception that flow control and signals are turned on, and curses
+ * cbreak mode doesn't give you this.
+ *
+ * We can either set raw mode and twiddle the tty, or cbreak mode and
+ * twiddle the tty. I chose to use raw mode, on the grounds that raw
+ * mode is better defined and I'm less likely to be surprised by a curses
+ * implementation down the road. The twiddling consists of setting ISIG,
+ * IXON/IXOFF, and disabling some of the interrupt characters (see the
+ * comments in svi/svi_screen.c). This is all found in historic System
+ * V (SVID 3) and POSIX 1003.1-1992, so it should be fairly portable.
+ *
+ * The second problem is that vi permits you to enter literal signal
+ * characters, e.g. ^V^C. There are two possible solutions. First, you
+ * can turn off signals when you get a ^V, but that means that a network
+ * packet containing ^V and ^C will lose, since the ^C may take effect
+ * before vi reads the ^V. (This is particularly problematic if you're
+ * talking over a protocol that recognizes signals locally and sends OOB
+ * packets when it sees them.) Second, you can turn the ^C into a literal
+ * character in vi, but that means that there's a race between entering
+ * ^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
+ * Also, the second solution doesn't work for flow control characters, as
+ * they aren't delivered to the program as signals.
+ *
+ * Generally, this is what historic vi did. (It didn't have the curses
+ * problems because it didn't use curses.) It entered signals following
+ * ^V characters into the input stream, (which is why there's no way to
+ * enter a literal flow control character).
+ *
+ * 3: Run in mostly raw mode; turn signals on when doing an operation the
+ * user might want to interrupt, but leave them off most of the time.
+ *
+ * This works well for things like file reads and writes. This doesn't
+ * work well for trying to detect infinite maps. The problem is that
+ * you can write the code so that you don't have to turn on interrupts
+ * per keystroke, but the code isn't pretty and it's hard to make sure
+ * that an optimization doesn't cover up an infinite loop. This also
+ * requires interaction or state between the vi parser and the key
+ * reading routines, as an infinite loop may still be returning keys
+ * to the parser.
+ *
+ * Also, if the user inserts an interrupt into the tty queue while the
+ * interrupts are turned off, the key won't be treated as an interrupt,
+ * and requiring the user to pound the keyboard to catch an interrupt
+ * window is nasty.
+ *
+ * 4: Run in mostly raw mode, leaving signals on all of the time. Done
+ * by setting raw mode, and twiddling the tty's termios ISIG bit.
+ *
+ * This works well for the interrupt cases, because the code only has
+ * to check to see if the interrupt flag has been set, and can otherwise
+ * ignore signals. It's also less likely that we'll miss a case, and we
+ * don't have to worry about synchronizing between the vi parser and the
+ * key read routines.
+ *
+ * The down side is that we have to turn signals off if the user wants
+ * to enter a literal character (e.g. ^V^C). If the user enters the
+ * combination fast enough, or as part of a single network packet,
+ * the text input routines will treat it as a signal instead of as a
+ * literal character. To some extent, we have this problem already,
+ * since we turn off flow control so that the user can enter literal
+ * XON/XOFF characters.
+ *
+ * This is probably the easiest to code, and provides the smoothest
+ * programming interface.
+ *
+ * There are a couple of other problems to consider.
+ *
+ * First, System V's curses doesn't handle SIGTSTP correctly. If you use the
+ * newterm() interface, the TSTP signal will leave you in raw mode, and the
+ * final endwin() will leave you in the correct shell mode. If you use the
+ * initscr() interface, the TSTP signal will return you to the correct shell
+ * mode, but the final endwin() will leave you in raw mode. There you have
+ * it: proof that drug testing is not making any significant headway in the
+ * computer industry. The 4BSD curses is deficient in that it does not have
+ * an interface to the terminal keypad. So, regardless, we have to do our
+ * own SIGTSTP handling.
+ *
+ * The problem with this is that if we do our own SIGTSTP handling, in either
+ * models #3 or #4, we're going to have to call curses routines at interrupt
+ * time, which means that we might be reentering curses, which is something we
+ * don't want to do.
+ *
+ * Second, SIGTSTP has its own little problems. It's broadcast to the entire
+ * process group, not sent to a single process. The scenario goes something
+ * like this: the shell execs the mail program, which execs vi. The user hits
+ * ^Z, and all three programs get the signal, in some random order. The mail
+ * program goes to sleep immediately (since it probably didn't have a SIGTSTP
+ * handler in place). The shell gets a SIGCHLD, does a wait, and finds out
+ * that the only child in its foreground process group (of which it's aware)
+ * is asleep. It then optionally resets the terminal (because the modes aren't
+ * how it left them), and starts prompting the user for input. The problem is
+ * that somewhere in the middle of all of this, vi is resetting the terminal,
+ * and getting ready to send a SIGTSTP to the process group in order to put
+ * itself to sleep. There's a solution to all of this: when vi starts, it puts
+ * itself into its own process group, and then only it (and possible child
+ * processes) receive the SIGTSTP. This permits it to clean up the terminal
+ * and switch back to the original process group, where it sends that process
+ * group a SIGTSTP, putting everyone to sleep and waking the shell.
+ *
+ * Third, handing SIGTSTP asynchronously is further complicated by the child
+ * processes vi may fork off. If vi calls ex, ex resets the terminal and
+ * starts running some filter, and SIGTSTP stops them both, vi has to know
+ * when it restarts that it can't repaint the screen until ex's child has
+ * finished running. This is solveable, but it's annoying.
+ *
+ * Well, somebody had to make a decision, and this is the way it's going to be
+ * (unless I get talked out of it). SIGINT is handled asynchronously, so
+ * that we can pretty much guarantee that the user can interrupt any operation
+ * at any time. SIGTSTP is handled synchronously, so that we don't have to
+ * reenter curses and so that we don't have to play the process group games.
+ * ^Z is recognized in the standard text input and command modes. (^Z should
+ * also be recognized during operations that may potentially take a long time.
+ * The simplest solution is probably to twiddle the tty, install a handler for
+ * SIGTSTP, and then restore normal tty modes when the operation is complete.)
+ */
+
+/*
+ * sig_init --
+ * Initialize signals.
+ */
+int
+sig_init(sp)
+ SCR *sp;
+{
+ GS *gp;
+ struct sigaction act;
+
+ /* Initialize the signals. */
+ gp = sp->gp;
+ (void)sigemptyset(&gp->blockset);
+
+ /*
+ * Use sigaction(2), not signal(3), since we don't always want to
+ * restart system calls. The example is when waiting for a command
+ * mode keystroke and SIGWINCH arrives. Try to set the restart bit
+ * (SA_RESTART) on SIGALRM anyway, it should result in a lot fewer
+ * interruptions. We also block every other signal that we can block
+ * when a signal arrives. This is because the signal functions call
+ * other nvi functions, which aren't guaranteed to be reentrant.
+ */
+
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+#define SETSIG(signal, flags, handler) { \
+ if (sigaddset(&gp->blockset, signal)) \
+ goto err; \
+ act.sa_handler = handler; \
+ sigfillset(&act.sa_mask); \
+ act.sa_flags = flags; \
+ if (sigaction(signal, &act, NULL)) \
+ goto err; \
+}
+ SETSIG(SIGALRM, SA_RESTART, h_alrm);
+ SETSIG(SIGHUP, 0, h_hup);
+ SETSIG(SIGINT, 0, h_int);
+ SETSIG(SIGTERM, 0, h_term);
+ SETSIG(SIGWINCH, 0, h_winch);
+ return (0);
+
+err: msgq(sp, M_SYSERR, "signal init");
+ return (1);
+}
+
+/*
+ * sig_end --
+ * End signal setup.
+ */
+void
+sig_end()
+{
+ /*
+ * POSIX 1003.1-1990 requires that fork (and, presumably, vfork) clear
+ * pending alarms, and that the exec functions clear pending signals.
+ * In addition, after an exec, the child continues to ignore signals
+ * ignored in the parent, and the child's action for signals caught in
+ * the parent is set to the default action. So, as we currently don't
+ * ignore any signals, there's no cleanup to be done. This routine is
+ * left here as a stub function.
+ */
+ return;
+}
+
+/*
+ * busy_on --
+ * Set a busy message timer.
+ */
+int
+busy_on(sp, msg)
+ SCR *sp;
+ char const *msg;
+{
+ struct itimerval value;
+ struct timeval tod;
+
+ /*
+ * Give the oldest busy message precedence, since it's
+ * the longer running operation.
+ */
+ if (sp->busy_msg != NULL)
+ return (1);
+
+ /* Get the current time of day, and create a target time. */
+ if (gettimeofday(&tod, NULL))
+ return (1);
+#define USER_PATIENCE_USECS (8 * 100000L)
+ sp->busy_tod.tv_sec = tod.tv_sec;
+ sp->busy_tod.tv_usec = tod.tv_usec + USER_PATIENCE_USECS;
+
+ /* We depend on this being an atomic instruction. */
+ sp->busy_msg = msg;
+
+ /*
+ * Busy messages turn around fast. Reset the timer regardless
+ * of its current state.
+ */
+ value.it_value.tv_sec = 0;
+ value.it_value.tv_usec = USER_PATIENCE_USECS;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_usec = 0;
+ if (setitimer(ITIMER_REAL, &value, NULL))
+ msgq(sp, M_SYSERR, "timer: setitimer");
+ return (0);
+}
+
+/*
+ * busy_off --
+ * Turn off a busy message timer.
+ */
+void
+busy_off(sp)
+ SCR *sp;
+{
+ /* We depend on this being an atomic instruction. */
+ sp->busy_msg = NULL;
+}
+
+/*
+ * rcv_on --
+ * Turn on recovery timer.
+ */
+int
+rcv_on(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct itimerval value;
+ struct timeval tod;
+
+ /* Get the current time of day. */
+ if (gettimeofday(&tod, NULL))
+ return (1);
+
+ /* Create target time of day. */
+ ep->rcv_tod.tv_sec = tod.tv_sec + RCV_PERIOD;
+ ep->rcv_tod.tv_usec = 0;
+
+ /*
+ * If there's a busy message happening, we're done, the
+ * interrupt handler will start our timer as necessary.
+ */
+ if (sp->busy_msg != NULL)
+ return (0);
+
+ value.it_value.tv_sec = RCV_PERIOD;
+ value.it_value.tv_usec = 0;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_usec = 0;
+ if (setitimer(ITIMER_REAL, &value, NULL)) {
+ msgq(sp, M_SYSERR, "timer: setitimer");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * h_alrm --
+ * Handle SIGALRM.
+ *
+ * There are two uses of the ITIMER_REAL timer (SIGALRM) in nvi. The first
+ * is to push the recovery information out to disk at periodic intervals.
+ * The second is to display a "busy" message if an operation takes more time
+ * that users are willing to wait before seeing something happen. The SCR
+ * structure has a wall clock timer structure for each of these. Since the
+ * busy timer has a much faster timeout than the recovery timer, most of the
+ * code ignores the recovery timer unless it's the only thing running.
+ *
+ * XXX
+ * It would be nice to reimplement this with two timers, a la POSIX 1003.1,
+ * but not many systems offer them yet.
+ */
+static void
+h_alrm(signo)
+ int signo;
+{
+ struct itimerval value;
+ struct timeval ntod, tod;
+ SCR *sp;
+ EXF *ep;
+ int sverrno;
+
+ sverrno = errno;
+
+ /* XXX: Get the current time of day; if this fails, we're dead. */
+ if (gettimeofday(&tod, NULL))
+ goto ret;
+
+ /*
+ * Fire any timers that are past due, or any that are due
+ * in a tenth of a second or less.
+ */
+ for (ntod.tv_sec = 0, sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) {
+
+ /* Check the busy timer if the msg pointer is set. */
+ if (sp->busy_msg == NULL)
+ goto skip_busy;
+ if (sp->busy_tod.tv_sec > tod.tv_sec ||
+ sp->busy_tod.tv_sec == tod.tv_sec &&
+ sp->busy_tod.tv_usec > tod.tv_usec &&
+ sp->busy_tod.tv_usec - tod.tv_usec > 100000L) {
+ if (ntod.tv_sec == 0 ||
+ ntod.tv_sec > sp->busy_tod.tv_sec ||
+ ntod.tv_sec == sp->busy_tod.tv_sec &&
+ ntod.tv_usec > sp->busy_tod.tv_usec)
+ ntod = sp->busy_tod;
+ } else {
+ (void)sp->s_busy(sp, sp->busy_msg);
+ sp->busy_msg = NULL;
+ }
+
+ /*
+ * Sync the file if the recovery timer has fired. If
+ * the sync fails, we don't reschedule future sync's.
+ */
+skip_busy: ep = sp->ep;
+ if (ep->rcv_tod.tv_sec < tod.tv_sec ||
+ ep->rcv_tod.tv_sec == tod.tv_sec &&
+ ep->rcv_tod.tv_usec < tod.tv_usec + 100000L) {
+ if (rcv_sync(sp, ep, 0))
+ continue;
+ ep->rcv_tod = tod;
+ ep->rcv_tod.tv_sec += RCV_PERIOD;
+ }
+ if (ntod.tv_sec == 0 ||
+ ntod.tv_sec > ep->rcv_tod.tv_sec ||
+ ntod.tv_sec == ep->rcv_tod.tv_sec &&
+ ntod.tv_usec > ep->rcv_tod.tv_usec)
+ ntod = ep->rcv_tod;
+ }
+
+ if (ntod.tv_sec == 0)
+ goto ret;
+
+ /* XXX: Set the timer; if this fails, we're dead. */
+ value.it_value.tv_sec = ntod.tv_sec - tod.tv_sec;
+ value.it_value.tv_usec = ntod.tv_usec - tod.tv_usec;
+ value.it_interval.tv_sec = 0;
+ value.it_interval.tv_usec = 0;
+ (void)setitimer(ITIMER_REAL, &value, NULL);
+
+ret: errno = sverrno;
+}
+
+/*
+ * h_hup --
+ * Handle SIGHUP.
+ */
+static void
+h_hup(signo)
+ int signo;
+{
+ sig_sync(SIGHUP, RCV_EMAIL);
+ /* NOTREACHED */
+}
+
+/*
+ * h_int --
+ * Handle SIGINT.
+ *
+ * XXX
+ * This isn't right if windows are independent of each other.
+ */
+static void
+h_int(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGINT);
+}
+
+/*
+ * h_term --
+ * Handle SIGTERM.
+ */
+static void
+h_term(signo)
+ int signo;
+{
+ sig_sync(SIGTERM, 0);
+ /* NOTREACHED */
+}
+
+/*
+ * h_winch --
+ * Handle SIGWINCH.
+ *
+ * XXX
+ * This isn't right if windows are independent of each other.
+ */
+static void
+h_winch(signo)
+ int signo;
+{
+ F_SET(__global_list, G_SIGWINCH);
+}
+
+
+/*
+ * sig_sync --
+ *
+ * Sync the files based on a signal.
+ */
+static void
+sig_sync(signo, flags)
+ int signo;
+ u_int flags;
+{
+ SCR *sp;
+
+ /*
+ * Walk the lists of screens, sync'ing the files; only sync
+ * each file once.
+ */
+ for (sp = __global_list->dq.cqh_first;
+ sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
+ rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);
+ for (sp = __global_list->hq.cqh_first;
+ sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
+ rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);
+
+ /*
+ * Die with the proper exit status. Don't bother using
+ * sigaction(2) 'cause we want the default behavior.
+ */
+ (void)signal(signo, SIG_DFL);
+ (void)kill(getpid(), signo);
+ /* NOTREACHED */
+
+ exit (1);
+}
diff --git a/usr.bin/vi/common/term.c b/usr.bin/vi/common/term.c
new file mode 100644
index 0000000..463b8f7
--- /dev/null
+++ b/usr.bin/vi/common/term.c
@@ -0,0 +1,732 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)term.c 8.81 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+/*
+ * XXX
+ * DON'T INCLUDE <curses.h> HERE, IT BREAKS OSF1 V2.0 WHERE IT
+ * CHANGES THE VALUES OF VERASE/VKILL/VWERASE TO INCORRECT ONES.
+ */
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+static int keycmp __P((const void *, const void *));
+static enum input term_key_queue __P((SCR *));
+static void term_key_set __P((GS *, int, int));
+
+/*
+ * If we're reading less than 20 characters, up the size of the tty buffer.
+ * This shouldn't ever happen, other than the first time through, but it's
+ * possible if a map is large enough.
+ */
+#define term_read_grow(sp, tty) \
+ (tty)->nelem - ((tty)->cnt + (tty)->next) >= 20 ? \
+ 0 : __term_read_grow(sp, tty, 64)
+static int __term_read_grow __P((SCR *, IBUF *, int));
+
+/*
+ * !!!
+ * Historic vi always used:
+ *
+ * ^D: autoindent deletion
+ * ^H: last character deletion
+ * ^W: last word deletion
+ * ^Q: quote the next character (if not used in flow control).
+ * ^V: quote the next character
+ *
+ * regardless of the user's choices for these characters. The user's erase
+ * and kill characters worked in addition to these characters. Nvi wires
+ * down the above characters, but in addition permits the VEOF, VERASE, VKILL
+ * and VWERASE characters described by the user's termios structure.
+ *
+ * Ex was not consistent with this scheme, as it historically ran in tty
+ * cooked mode. This meant that the scroll command and autoindent erase
+ * characters were mapped to the user's EOF character, and the character
+ * and word deletion characters were the user's tty character and word
+ * deletion characters. This implementation makes it all consistent, as
+ * described above for vi.
+ *
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A SPECIAL KEY SET.
+ */
+KEYLIST keylist[] = {
+ {K_CARAT, '^'}, /* ^ */
+ {K_CNTRLD, '\004'}, /* ^D */
+ {K_CNTRLR, '\022'}, /* ^R */
+ {K_CNTRLT, '\024'}, /* ^T */
+ {K_CNTRLZ, '\032'}, /* ^Z */
+ {K_COLON, ':'}, /* : */
+ {K_CR, '\r'}, /* \r */
+ {K_ESCAPE, '\033'}, /* ^[ */
+ {K_FORMFEED, '\f'}, /* \f */
+ {K_HEXCHAR, '\030'}, /* ^X */
+ {K_NL, '\n'}, /* \n */
+ {K_RIGHTBRACE, '}'}, /* } */
+ {K_RIGHTPAREN, ')'}, /* ) */
+ {K_TAB, '\t'}, /* \t */
+ {K_VERASE, '\b'}, /* \b */
+ {K_VKILL, '\025'}, /* ^U */
+ {K_VLNEXT, '\021'}, /* ^Q */
+ {K_VLNEXT, '\026'}, /* ^V */
+ {K_VWERASE, '\027'}, /* ^W */
+ {K_ZERO, '0'}, /* 0 */
+ {K_NOTUSED, 0}, /* VEOF, VERASE, VKILL, VWERASE */
+ {K_NOTUSED, 0},
+ {K_NOTUSED, 0},
+ {K_NOTUSED, 0},
+};
+static int nkeylist = (sizeof(keylist) / sizeof(keylist[0])) - 4;
+
+/*
+ * term_init --
+ * Initialize the special key lookup table.
+ */
+int
+term_init(sp)
+ SCR *sp;
+{
+ GS *gp;
+ KEYLIST *kp;
+ int cnt;
+
+ /*
+ * XXX
+ * 8-bit only, for now. Recompilation should get you any
+ * 8-bit character set, as long as nul isn't a character.
+ */
+ (void)setlocale(LC_ALL, "");
+ key_init(sp);
+
+ gp = sp->gp;
+#ifdef VEOF
+ term_key_set(gp, VEOF, K_CNTRLD);
+#endif
+#ifdef VERASE
+ term_key_set(gp, VERASE, K_VERASE);
+#endif
+#ifdef VKILL
+ term_key_set(gp, VKILL, K_VKILL);
+#endif
+#ifdef VWERASE
+ term_key_set(gp, VWERASE, K_VWERASE);
+#endif
+
+ /* Sort the special key list. */
+ qsort(keylist, nkeylist, sizeof(keylist[0]), keycmp);
+
+ /* Initialize the fast lookup table. */
+ for (gp->max_special = 0, kp = keylist, cnt = nkeylist; cnt--; ++kp) {
+ if (gp->max_special < kp->value)
+ gp->max_special = kp->value;
+ if (kp->ch <= MAX_FAST_KEY)
+ gp->special_key[kp->ch] = kp->value;
+ }
+ return (0);
+}
+
+/*
+ * term_key_set --
+ * Set keys found in the termios structure. VERASE and VKILL are required
+ * by POSIX 1003.1-1990, VWERASE is a 4.4BSD extension. We've left three
+ * open slots in the keylist table, if these values exist, put them into
+ * place. Note, they may reset (or duplicate) values already in the table,
+ * so we check for that first.
+ */
+static void
+term_key_set(gp, name, val)
+ GS *gp;
+ int name, val;
+{
+ KEYLIST *kp;
+ cc_t ch;
+
+ if (!F_ISSET(gp, G_TERMIOS_SET))
+ return;
+ if ((ch = gp->original_termios.c_cc[name]) == _POSIX_VDISABLE)
+ return;
+
+ /* Check for duplication. */
+ for (kp = keylist; kp->value != K_NOTUSED; ++kp)
+ if (kp->ch == ch) {
+ kp->value = val;
+ return;
+ }
+ /* Add a new entry. */
+ if (kp->value == K_NOTUSED) {
+ keylist[nkeylist].ch = ch;
+ keylist[nkeylist].value = val;
+ ++nkeylist;
+ }
+}
+
+/*
+ * key_init --
+ * Build the fast-lookup key display array.
+ */
+void
+key_init(sp)
+ SCR *sp;
+{
+ CHAR_T ch;
+
+ for (ch = 0; ch <= MAX_FAST_KEY; ++ch) {
+ (void)__key_name(sp, ch);
+ (void)memmove(sp->gp->cname[ch].name, sp->cname, sp->clen);
+ sp->gp->cname[ch].len = sp->clen;
+ }
+}
+
+/*
+ * __key_len --
+ * Return the length of the string that will display the key.
+ * This routine is the backup for the KEY_LEN() macro.
+ */
+size_t
+__key_len(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ (void)__key_name(sp, ch);
+ return (sp->clen);
+}
+
+/*
+ * __key_name --
+ * Return the string that will display the key. This routine
+ * is the backup for the KEY_NAME() macro.
+ */
+CHAR_T *
+__key_name(sp, ach)
+ SCR *sp;
+ ARG_CHAR_T ach;
+{
+ static const CHAR_T hexdigit[] = "0123456789abcdef";
+ static const CHAR_T octdigit[] = "01234567";
+ CHAR_T ch, *chp, mask;
+ size_t len;
+ int cnt, shift;
+
+ /*
+ * Historical (ARPA standard) mappings. Printable characters are left
+ * alone. Control characters less than '\177' are represented as '^'
+ * followed by the character offset from the '@' character in the ASCII
+ * map. '\177' is represented as '^' followed by '?'.
+ *
+ * XXX
+ * The following code depends on the current locale being identical to
+ * the ASCII map from '\100' to '\076' (\076 since that's the largest
+ * character for which we can offset from '@' and get something that's
+ * a printable character in ASCII. I'm told that this is a reasonable
+ * assumption...
+ *
+ * XXX
+ * This code will only work with CHAR_T's that are multiples of 8-bit
+ * bytes.
+ *
+ * XXX
+ * NB: There's an assumption here that all printable characters take
+ * up a single column on the screen. This is not always correct.
+ */
+ ch = ach;
+ if (isprint(ch)) {
+ sp->cname[0] = ch;
+ len = 1;
+ } else if (ch <= '\076' && iscntrl(ch)) {
+ sp->cname[0] = '^';
+ sp->cname[1] = ch == '\177' ? '?' : '@' + ch;
+ len = 2;
+ } else if (O_ISSET(sp, O_OCTAL)) {
+#define BITS (sizeof(CHAR_T) * 8)
+#define SHIFT (BITS - BITS % 3)
+#define TOPMASK (BITS % 3 == 2 ? 3 : 1) << (BITS - BITS % 3)
+ sp->cname[0] = '\\';
+ sp->cname[1] = octdigit[(ch & TOPMASK) >> SHIFT];
+ shift = SHIFT - 3;
+ for (len = 2, mask = 7 << (SHIFT - 3),
+ cnt = BITS / 3; cnt-- > 0; mask >>= 3, shift -= 3)
+ sp->cname[len++] = octdigit[(ch & mask) >> shift];
+ } else {
+ sp->cname[0] = '0';
+ sp->cname[1] = 'x';
+ for (len = 2, chp = (u_int8_t *)&ch,
+ cnt = sizeof(CHAR_T); cnt-- > 0; ++chp) {
+ sp->cname[len++] = hexdigit[(*chp & 0xf0) >> 4];
+ sp->cname[len++] = hexdigit[*chp & 0x0f];
+ }
+ }
+ sp->cname[sp->clen = len] = '\0';
+ return (sp->cname);
+}
+
+/*
+ * term_push --
+ * Push keys onto the front of a buffer.
+ *
+ * There is a single input buffer in ex/vi. Characters are read onto the
+ * end of the buffer by the terminal input routines, and pushed onto the
+ * front of the buffer by various other functions in ex/vi. Each key has
+ * an associated flag value, which indicates if it has already been quoted,
+ * if it is the result of a mapping or an abbreviation, as well as a count
+ * of the number of times it has been mapped.
+ */
+int
+term_push(sp, s, nchars, flags)
+ SCR *sp;
+ CHAR_T *s; /* Characters. */
+ size_t nchars; /* Number of chars. */
+ u_int flags; /* CH_* flags. */
+{
+ IBUF *tty;
+ size_t total;
+
+ /* If we have room, stuff the keys into the buffer. */
+ tty = sp->gp->tty;
+ if (nchars <= tty->next ||
+ (tty->ch != NULL && tty->cnt == 0 && nchars <= tty->nelem)) {
+ if (tty->cnt != 0)
+ tty->next -= nchars;
+ tty->cnt += nchars;
+ MEMMOVE(tty->ch + tty->next, s, nchars);
+ MEMSET(tty->chf + tty->next, flags, nchars);
+ return (0);
+ }
+
+ /*
+ * If there are currently characters in the queue, shift them up,
+ * leaving some extra room. Get enough space plus a little extra.
+ */
+#define TERM_PUSH_SHIFT 30
+ total = tty->cnt + tty->next + nchars + TERM_PUSH_SHIFT;
+ if (total >= tty->nelem && __term_read_grow(sp, tty, MAX(total, 64)))
+ return (1);
+ if (tty->cnt) {
+ MEMMOVE(tty->ch + TERM_PUSH_SHIFT + nchars,
+ tty->ch + tty->next, tty->cnt);
+ MEMMOVE(tty->chf + TERM_PUSH_SHIFT + nchars,
+ tty->chf + tty->next, tty->cnt);
+ }
+
+ /* Put the new characters into the queue. */
+ tty->next = TERM_PUSH_SHIFT;
+ tty->cnt += nchars;
+ MEMMOVE(tty->ch + TERM_PUSH_SHIFT, s, nchars);
+ MEMSET(tty->chf + TERM_PUSH_SHIFT, flags, nchars);
+ return (0);
+}
+
+/*
+ * Remove characters from the queue, simultaneously clearing the flag
+ * and map counts.
+ */
+#define QREM_HEAD(q, len) { \
+ size_t __off = (q)->next; \
+ if (len == 1) \
+ tty->chf[__off] = 0; \
+ else \
+ MEMSET(tty->chf + __off, 0, len); \
+ if (((q)->cnt -= len) == 0) \
+ (q)->next = 0; \
+ else \
+ (q)->next += len; \
+}
+#define QREM_TAIL(q, len) { \
+ size_t __off = (q)->next + (q)->cnt - 1; \
+ if (len == 1) \
+ tty->chf[__off] = 0; \
+ else \
+ MEMSET(tty->chf + __off, 0, len); \
+ if (((q)->cnt -= len) == 0) \
+ (q)->next = 0; \
+}
+
+/*
+ * term_key --
+ * Get the next key.
+ *
+ * !!!
+ * The flag TXT_MAPNODIGIT probably needs some explanation. First, the idea
+ * of mapping keys is that one or more keystrokes act like a function key.
+ * What's going on is that vi is reading a number, and the character following
+ * the number may or may not be mapped (TXT_MAPCOMMAND). For example, if the
+ * user is entering the z command, a valid command is "z40+", and we don't want
+ * to map the '+', i.e. if '+' is mapped to "xxx", we don't want to change it
+ * into "z40xxx". However, if the user enters "35x", we want to put all of the
+ * characters through the mapping code.
+ *
+ * Historical practice is a bit muddled here. (Surprise!) It always permitted
+ * mapping digits as long as they weren't the first character of the map, e.g.
+ * ":map ^A1 xxx" was okay. It also permitted the mapping of the digits 1-9
+ * (the digit 0 was a special case as it doesn't indicate the start of a count)
+ * as the first character of the map, but then ignored those mappings. While
+ * it's probably stupid to map digits, vi isn't your mother.
+ *
+ * The way this works is that the TXT_MAPNODIGIT causes term_key to return the
+ * end-of-digit without "looking" at the next character, i.e. leaving it as the
+ * user entered it. Presumably, the next term_key call will tell us how the
+ * user wants it handled.
+ *
+ * There is one more complication. Users might map keys to digits, and, as
+ * it's described above, the commands "map g 1G|d2g" would return the keys
+ * "d2<end-of-digits>1G", when the user probably wanted "d21<end-of-digits>G".
+ * So, if a map starts off with a digit we continue as before, otherwise, we
+ * pretend that we haven't mapped the character and return <end-of-digits>.
+ *
+ * Now that that's out of the way, let's talk about Energizer Bunny macros.
+ * It's easy to create macros that expand to a loop, e.g. map x 3x. It's
+ * fairly easy to detect this example, because it's all internal to term_key.
+ * If we're expanding a macro and it gets big enough, at some point we can
+ * assume it's looping and kill it. The examples that are tough are the ones
+ * where the parser is involved, e.g. map x "ayyx"byy. We do an expansion
+ * on 'x', and get "ayyx"byy. We then return the first 4 characters, and then
+ * find the looping macro again. There is no way that we can detect this
+ * without doing a full parse of the command, because the character that might
+ * cause the loop (in this case 'x') may be a literal character, e.g. the map
+ * map x "ayy"xyy"byy is perfectly legal and won't cause a loop.
+ *
+ * Historic vi tried to detect looping macros by disallowing obvious cases in
+ * the map command, maps that that ended with the same letter as they started
+ * (which wrongly disallowed "map x 'x"), and detecting macros that expanded
+ * too many times before keys were returned to the command parser. It didn't
+ * get many (most?) of the tricky cases right, however, and it was certainly
+ * possible to create macros that ran forever. And, even if it did figure out
+ * what was going on, the user was usually tossed into ex mode. Finally, any
+ * changes made before vi realized that the macro was recursing were left in
+ * place. We recover gracefully, but the only recourse the user has in an
+ * infinite macro loop is to interrupt.
+ *
+ * !!!
+ * It is historic practice that mapping characters to themselves as the first
+ * part of the mapped string was legal, and did not cause infinite loops, i.e.
+ * ":map! { {^M^T" and ":map n nz." were known to work. The initial, matching
+ * characters were returned instead of being remapped.
+ *
+ * XXX
+ * The final issue is recovery. It would be possible to undo all of the work
+ * that was done by the macro if we entered a record into the log so that we
+ * knew when the macro started, and, in fact, this might be worth doing at some
+ * point. Given that this might make the log grow unacceptably (consider that
+ * cursor keys are done with maps), for now we leave any changes made in place.
+ */
+enum input
+term_key(sp, chp, flags)
+ SCR *sp;
+ CH *chp;
+ u_int flags;
+{
+ enum input rval;
+ struct timeval t, *tp;
+ CHAR_T ch;
+ GS *gp;
+ IBUF *tty;
+ SEQ *qp;
+ int init_nomap, ispartial, nr;
+
+ /* If we've been interrupted, return an error. */
+ if (INTERRUPTED(sp))
+ return (INP_INTR);
+
+ gp = sp->gp;
+ tty = gp->tty;
+
+ /*
+ * If the queue is empty, read more keys in. Since no timeout is
+ * requested, s_key_read will either return an error or will read
+ * some number of characters.
+ */
+loop: if (tty->cnt == 0) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if ((rval = sp->s_key_read(sp, &nr, NULL)) != INP_OK)
+ return (rval);
+ /*
+ * If there's something on the mode line that we wanted
+ * the user to see, they just entered a character so we
+ * can presume they saw it.
+ */
+ if (F_ISSET(sp, S_UPDATE_MODE))
+ F_CLR(sp, S_UPDATE_MODE);
+ }
+
+ /* If the key is mappable and should be mapped, look it up. */
+ if (!(tty->chf[tty->next] & CH_NOMAP) &&
+ LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT)) {
+ /* Set up timeout value. */
+ if (O_ISSET(sp, O_TIMEOUT)) {
+ tp = &t;
+ t.tv_sec = O_VAL(sp, O_KEYTIME) / 10;
+ t.tv_usec = (O_VAL(sp, O_KEYTIME) % 10) * 100000L;
+ } else
+ tp = NULL;
+
+ /* Get the next key. */
+newmap: ch = tty->ch[tty->next];
+ if (ch < MAX_BIT_SEQ && !bit_test(gp->seqb, ch))
+ goto nomap;
+
+ /* Search the map. */
+remap: qp = seq_find(sp, NULL, &tty->ch[tty->next], tty->cnt,
+ LF_ISSET(TXT_MAPCOMMAND) ? SEQ_COMMAND : SEQ_INPUT,
+ &ispartial);
+
+ /* If we've been interrupted, return an error. */
+ if (INTERRUPTED(sp))
+ return (INP_INTR);
+
+ /*
+ * If get a partial match, read more characters and retry
+ * the map. If no characters read, return the characters
+ * unmapped.
+ */
+ if (ispartial) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if ((rval = sp->s_key_read(sp, &nr, tp)) != INP_OK)
+ return (rval);
+ if (nr)
+ goto remap;
+ goto nomap;
+ }
+
+ /* If no map, return the character. */
+ if (qp == NULL)
+ goto nomap;
+
+ /*
+ * If looking for the end of a digit string, and the first
+ * character of the map is it, pretend we haven't seen the
+ * character.
+ */
+ if (LF_ISSET(TXT_MAPNODIGIT) &&
+ qp->output != NULL && !isdigit(qp->output[0]))
+ goto not_digit_ch;
+
+ /* Find out if the initial segments are identical. */
+ init_nomap = !memcmp(&tty->ch[tty->next], qp->output, qp->ilen);
+
+ /* Delete the mapped characters from the queue. */
+ QREM_HEAD(tty, qp->ilen);
+
+ /* If keys mapped to nothing, go get more. */
+ if (qp->output == NULL)
+ goto loop;
+
+ /* If remapping characters, push the character on the queue. */
+ if (O_ISSET(sp, O_REMAP)) {
+ if (init_nomap) {
+ if (term_push(sp, qp->output + qp->ilen,
+ qp->olen - qp->ilen, CH_MAPPED))
+ return (INP_ERR);
+ if (term_push(sp,
+ qp->output, qp->ilen, CH_NOMAP | CH_MAPPED))
+ return (INP_ERR);
+ goto nomap;
+ } else
+ if (term_push(sp,
+ qp->output, qp->olen, CH_MAPPED))
+ return (INP_ERR);
+ goto newmap;
+ }
+
+ /* Else, push the characters on the queue and return one. */
+ if (term_push(sp, qp->output, qp->olen, CH_MAPPED | CH_NOMAP))
+ return (INP_ERR);
+ }
+
+nomap: ch = tty->ch[tty->next];
+ if (LF_ISSET(TXT_MAPNODIGIT) && !isdigit(ch)) {
+not_digit_ch: chp->ch = CH_NOT_DIGIT;
+ chp->value = 0;
+ chp->flags = 0;
+ return (INP_OK);
+ }
+
+ /* Fill in the return information. */
+ chp->ch = ch;
+ chp->flags = tty->chf[tty->next];
+ chp->value = KEY_VAL(sp, ch);
+
+ /* Delete the character from the queue. */
+ QREM_HEAD(tty, 1);
+ return (INP_OK);
+}
+
+/*
+ * term_flush --
+ * Flush any flagged keys.
+ */
+void
+term_flush(sp, msg, flags)
+ SCR *sp;
+ char *msg;
+ u_int flags;
+{
+ IBUF *tty;
+
+ tty = sp->gp->tty;
+ if (!tty->cnt || !(tty->chf[tty->next] & flags))
+ return;
+ do {
+ QREM_HEAD(tty, 1);
+ } while (tty->cnt && tty->chf[tty->next] & flags);
+ msgq(sp, M_ERR, "%s: keys flushed", msg);
+}
+
+/*
+ * term_user_key --
+ * Get the next key, but require the user enter one.
+ */
+enum input
+term_user_key(sp, chp)
+ SCR *sp;
+ CH *chp;
+{
+ enum input rval;
+ IBUF *tty;
+ int nr;
+
+ /*
+ * Read any keys the user has waiting. Make the race
+ * condition as short as possible.
+ */
+ if ((rval = term_key_queue(sp)) != INP_OK)
+ return (rval);
+
+ /* Wait and read another key. */
+ if ((rval = sp->s_key_read(sp, &nr, NULL)) != INP_OK)
+ return (rval);
+
+ /* Fill in the return information. */
+ tty = sp->gp->tty;
+ chp->ch = tty->ch[tty->next + (tty->cnt - 1)];
+ chp->flags = 0;
+ chp->value = KEY_VAL(sp, chp->ch);
+
+ QREM_TAIL(tty, 1);
+ return (INP_OK);
+}
+
+/*
+ * term_key_queue --
+ * Read the keys off of the terminal queue until it's empty.
+ */
+static enum input
+term_key_queue(sp)
+ SCR *sp;
+{
+ enum input rval;
+ struct timeval t;
+ IBUF *tty;
+ int nr;
+
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ for (tty = sp->gp->tty;;) {
+ if (term_read_grow(sp, tty))
+ return (INP_ERR);
+ if ((rval = sp->s_key_read(sp, &nr, &t)) != INP_OK)
+ return (rval);
+ if (nr == 0)
+ break;
+ }
+ return (INP_OK);
+}
+
+/*
+ * __key_val --
+ * Fill in the value for a key. This routine is the backup
+ * for the KEY_VAL() macro.
+ */
+int
+__key_val(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ KEYLIST k, *kp;
+
+ k.ch = ch;
+ kp = bsearch(&k, keylist, nkeylist, sizeof(keylist[0]), keycmp);
+ return (kp == NULL ? K_NOTUSED : kp->value);
+}
+
+/*
+ * __term_read_grow --
+ * Grow the terminal queue. This routine is the backup for
+ * the term_read_grow() macro.
+ */
+static int
+__term_read_grow(sp, tty, add)
+ SCR *sp;
+ IBUF *tty;
+ int add;
+{
+ size_t new_nelem, olen;
+
+ new_nelem = tty->nelem + add;
+ olen = tty->nelem * sizeof(tty->ch[0]);
+ BINC_RET(sp, tty->ch, olen, new_nelem * sizeof(tty->ch[0]));
+
+ olen = tty->nelem * sizeof(tty->chf[0]);
+ BINC_RET(sp, tty->chf, olen, new_nelem * sizeof(tty->chf[0]));
+
+ tty->nelem = olen / sizeof(tty->chf[0]);
+ return (0);
+}
+
+static int
+keycmp(ap, bp)
+ const void *ap, *bp;
+{
+ return (((KEYLIST *)ap)->ch - ((KEYLIST *)bp)->ch);
+}
diff --git a/usr.bin/vi/common/term.h b/usr.bin/vi/common/term.h
new file mode 100644
index 0000000..623a94c
--- /dev/null
+++ b/usr.bin/vi/common/term.h
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)term.h 8.48 (Berkeley) 7/25/94
+ */
+
+/*
+ * Fundamental character types.
+ *
+ * CHAR_T An integral type that can hold any character.
+ * ARG_CHAR_T The type of a CHAR_T when passed as an argument using
+ * traditional promotion rules. It should also be able
+ * to be compared against any CHAR_T for equality without
+ * problems.
+ * MAX_CHAR_T The maximum value of any character.
+ *
+ * If no integral type can hold a character, don't even try the port.
+ */
+typedef u_char CHAR_T;
+typedef u_int ARG_CHAR_T;
+#define MAX_CHAR_T 0xff
+
+/* The maximum number of columns any character can take up on a screen. */
+#define MAX_CHARACTER_COLUMNS 4
+
+/* Structure to return a character and associated information. */
+struct _ch {
+ CHAR_T ch; /* Character. */
+
+#define K_NOTUSED 0
+#define K_CARAT 1
+#define K_CNTRLD 2
+#define K_CNTRLR 3
+#define K_CNTRLT 4
+#define K_CNTRLZ 5
+#define K_COLON 6
+#define K_CR 7
+#define K_ESCAPE 8
+#define K_FORMFEED 9
+#define K_HEXCHAR 10
+#define K_NL 11
+#define K_RIGHTBRACE 12
+#define K_RIGHTPAREN 13
+#define K_TAB 14
+#define K_VERASE 15
+#define K_VKILL 16
+#define K_VLNEXT 17
+#define K_VWERASE 18
+#define K_ZERO 19
+ u_int8_t value; /* Special character flag values. */
+
+#define CH_ABBREVIATED 0x01 /* Character from an abbreviation. */
+#define CH_MAPPED 0x02 /* Character from a map. */
+#define CH_NOMAP 0x04 /* Do not map the character. */
+#define CH_QUOTED 0x08 /* Character is already quoted. */
+ u_int8_t flags;
+};
+
+typedef struct _keylist {
+ u_int8_t value; /* Special value. */
+ CHAR_T ch; /* Key. */
+} KEYLIST;
+
+extern KEYLIST keylist[];
+
+/* Structure for the key input buffer. */
+struct _ibuf {
+ CHAR_T *ch; /* Array of characters. */
+ u_int8_t *chf; /* Array of character flags (CH_*). */
+
+ size_t cnt; /* Count of remaining characters. */
+ size_t nelem; /* Numer of array elements. */
+ size_t next; /* Offset of next array entry. */
+};
+ /* Return if more keys in queue. */
+#define KEYS_WAITING(sp) ((sp)->gp->tty->cnt)
+#define MAPPED_KEYS_WAITING(sp) \
+ (KEYS_WAITING(sp) && sp->gp->tty->chf[sp->gp->tty->next] & CH_MAPPED)
+
+/*
+ * Routines that return a key as a side-effect return:
+ *
+ * INP_OK Returning a character; must be 0.
+ * INP_EOF EOF.
+ * INP_ERR Error.
+ * INP_INTR Interrupted.
+ *
+ * The vi structure depends on the key routines being able to return INP_EOF
+ * multiple times without failing -- eventually enough things will end due to
+ * INP_EOF that vi will reach the command level for the screen, at which point
+ * the exit flags will be set and vi will exit.
+ */
+enum input { INP_OK=0, INP_EOF, INP_ERR, INP_INTR };
+
+/*
+ * Routines that return a confirmation return:
+ *
+ * CONF_NO User answered no.
+ * CONF_QUIT User answered quit, eof or an error.
+ * CONF_YES User answered yes.
+ */
+enum confirm { CONF_NO, CONF_QUIT, CONF_YES };
+
+/*
+ * Ex/vi commands are generally separated by whitespace characters. We
+ * can't use the standard isspace(3) macro because it returns true for
+ * characters like ^K in the ASCII character set. The 4.4BSD isblank(3)
+ * macro does exactly what we want, but it's not portable yet.
+ *
+ * XXX
+ * Note side effect, ch is evaluated multiple times.
+ */
+#ifndef isblank
+#define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+/* The "standard" tab width, for displaying things to users. */
+#define STANDARD_TAB 6
+
+/* Various special characters, messages. */
+#define CH_BSEARCH '?' /* Backward search prompt. */
+#define CH_CURSOR ' ' /* Cursor character. */
+#define CH_ENDMARK '$' /* End of a range. */
+#define CH_EXPROMPT ':' /* Ex prompt. */
+#define CH_FSEARCH '/' /* Forward search prompt. */
+#define CH_HEX '\030' /* Leading hex character. */
+#define CH_LITERAL '\026' /* ASCII ^V. */
+#define CH_NO 'n' /* No. */
+#define CH_NOT_DIGIT 'a' /* A non-isdigit() character. */
+#define CH_QUIT 'q' /* Quit. */
+#define CH_YES 'y' /* Yes. */
+
+#define STR_CONFIRM "confirm? [ynq]"
+#define STR_CMSG "Enter return to continue: "
+#define STR_QMSG "Enter return to continue [q to quit]: "
+
+/* Flags describing how input is handled. */
+#define TXT_AICHARS 0x00000001 /* Leading autoindent chars. */
+#define TXT_ALTWERASE 0x00000002 /* Option: altwerase. */
+#define TXT_APPENDEOL 0x00000004 /* Appending after EOL. */
+#define TXT_AUTOINDENT 0x00000008 /* Autoindent set this line. */
+#define TXT_BACKSLASH 0x00000010 /* Backslashes escape characters. */
+#define TXT_BEAUTIFY 0x00000020 /* Only printable characters. */
+#define TXT_BS 0x00000040 /* Backspace returns the buffer. */
+#define TXT_CNTRLD 0x00000080 /* Control-D is a special command. */
+#define TXT_CNTRLT 0x00000100 /* Control-T is an indent special. */
+#define TXT_CR 0x00000200 /* CR returns the buffer. */
+#define TXT_DOTTERM 0x00000400 /* Leading '.' terminates the input. */
+#define TXT_EMARK 0x00000800 /* End of replacement mark. */
+#define TXT_ESCAPE 0x00001000 /* Escape returns the buffer. */
+#define TXT_EXSUSPEND 0x00002000 /* ^Z should suspend the session. */
+#define TXT_INFOLINE 0x00004000 /* Editing the info line. */
+#define TXT_MAPCOMMAND 0x00008000 /* Apply the command map. */
+#define TXT_MAPINPUT 0x00010000 /* Apply the input map. */
+#define TXT_MAPNODIGIT 0x00020000 /* Return to a digit. */
+#define TXT_NLECHO 0x00040000 /* Echo the newline. */
+#define TXT_OVERWRITE 0x00080000 /* Overwrite characters. */
+#define TXT_PROMPT 0x00100000 /* Display a prompt. */
+#define TXT_RECORD 0x00200000 /* Record for replay. */
+#define TXT_REPLACE 0x00400000 /* Replace; don't delete overwrite. */
+#define TXT_REPLAY 0x00800000 /* Replay the last input. */
+#define TXT_RESOLVE 0x01000000 /* Resolve the text into the file. */
+#define TXT_SHOWMATCH 0x02000000 /* Option: showmatch. */
+#define TXT_TTYWERASE 0x04000000 /* Option: ttywerase. */
+#define TXT_WRAPMARGIN 0x08000000 /* Option: wrapmargin. */
+
+/* Support keyboard routines. */
+size_t __key_len __P((SCR *, ARG_CHAR_T));
+CHAR_T *__key_name __P((SCR *, ARG_CHAR_T));
+int __key_val __P((SCR *, ARG_CHAR_T));
+void key_init __P((SCR *));
+void term_flush __P((SCR *, char *, u_int));
+enum input term_key __P((SCR *, CH *, u_int));
+enum input term_user_key __P((SCR *, CH *));
+int term_init __P((SCR *));
+int term_push __P((SCR *, CHAR_T *, size_t, u_int));
diff --git a/usr.bin/vi/common/trace.c b/usr.bin/vi/common/trace.c
new file mode 100644
index 0000000..1f63f66
--- /dev/null
+++ b/usr.bin/vi/common/trace.c
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)trace.c 8.2 (Berkeley) 3/8/94
+ */
+
+#ifdef DEBUG
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#ifdef __STDC__
+TRACE(SCR *sp, const char *fmt, ...)
+#else
+TRACE(sp, fmt, va_alist)
+ SCR *sp;
+ char *fmt;
+ va_dcl
+#endif
+{
+ FILE *tfp;
+ va_list ap;
+
+ if ((tfp = sp->gp->tracefp) == NULL)
+ return;
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(tfp, fmt, ap);
+ va_end(ap);
+
+ (void)fflush(tfp);
+}
+#endif
diff --git a/usr.bin/vi/common/util.c b/usr.bin/vi/common/util.c
new file mode 100644
index 0000000..0268c0c
--- /dev/null
+++ b/usr.bin/vi/common/util.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c 8.74 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * binc --
+ * Increase the size of a buffer.
+ */
+void *
+binc(sp, bp, bsizep, min)
+ SCR *sp; /* sp MAY BE NULL!!! */
+ void *bp;
+ size_t *bsizep, min;
+{
+ size_t csize;
+
+ /* If already larger than the minimum, just return. */
+ if (min && *bsizep >= min)
+ return (bp);
+
+ csize = *bsizep + MAX(min, 256);
+ REALLOC(sp, bp, void *, csize);
+
+ if (bp == NULL) {
+ /*
+ * Theoretically, realloc is supposed to leave any already
+ * held memory alone if it can't get more. Don't trust it.
+ */
+ *bsizep = 0;
+ return (NULL);
+ }
+ /*
+ * Memory is guaranteed to be zero-filled, various parts of
+ * nvi depend on this.
+ */
+ memset((char *)bp + *bsizep, 0, csize - *bsizep);
+ *bsizep = csize;
+ return (bp);
+}
+
+/*
+ * nonblank --
+ * Set the column number of the first non-blank character
+ * including or after the starting column. On error, set
+ * the column to 0, it's safest.
+ */
+int
+nonblank(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ char *p;
+ size_t cnt, len, off;
+
+ /* Default. */
+ off = *cnop;
+ *cnop = 0;
+
+ /* Get the line. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ return (0);
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+
+ /* Set the offset. */
+ if (len == 0 || off >= len)
+ return (0);
+
+ for (cnt = off, p = &p[off],
+ len -= off; len && isblank(*p); ++cnt, ++p, --len);
+
+ /* Set the return. */
+ *cnop = len ? cnt : cnt - 1;
+ return (0);
+}
+
+/*
+ * tail --
+ * Return tail of a path.
+ */
+char *
+tail(path)
+ char *path;
+{
+ char *p;
+
+ if ((p = strrchr(path, '/')) == NULL)
+ return (path);
+ return (p + 1);
+}
+
+/*
+ * set_alt_name --
+ * Set the alternate file name.
+ *
+ * Swap the alternate file name. It's a routine because I wanted some place
+ * to hang this comment. The alternate file name (normally referenced using
+ * the special character '#' during file expansion) is set by many
+ * operations. In the historic vi, the commands "ex", and "edit" obviously
+ * set the alternate file name because they switched the underlying file.
+ * Less obviously, the "read", "file", "write" and "wq" commands set it as
+ * well. In this implementation, some new commands have been added to the
+ * list. Where it gets interesting is that the alternate file name is set
+ * multiple times by some commands. If an edit attempt fails (for whatever
+ * reason, like the current file is modified but as yet unwritten), it is
+ * set to the file name that the user was unable to edit. If the edit
+ * succeeds, it is set to the last file name that was edited. Good fun.
+ *
+ * If the user edits a temporary file, there are time when there isn't an
+ * alternative file name. A name argument of NULL turns it off.
+ */
+void
+set_alt_name(sp, name)
+ SCR *sp;
+ char *name;
+{
+ if (sp->alt_name != NULL)
+ free(sp->alt_name);
+ if (name == NULL)
+ sp->alt_name = NULL;
+ else if ((sp->alt_name = strdup(name)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+}
+
+/*
+ * v_strdup --
+ * Strdup for wide character strings with an associated length.
+ */
+CHAR_T *
+v_strdup(sp, str, len)
+ SCR *sp;
+ CHAR_T *str;
+ size_t len;
+{
+ CHAR_T *copy;
+
+ MALLOC(sp, copy, CHAR_T *, len + 1);
+ if (copy == NULL)
+ return (NULL);
+ memmove(copy, str, len * sizeof(CHAR_T));
+ copy[len] = '\0';
+ return (copy);
+}
+
+/*
+ * vi_putchar --
+ * Functional version of putchar, for tputs.
+ */
+void
+vi_putchar(ch)
+ int ch;
+{
+ (void)putchar(ch);
+}
diff --git a/usr.bin/vi/common/vi.h b/usr.bin/vi/common/vi.h
new file mode 100644
index 0000000..f98b870
--- /dev/null
+++ b/usr.bin/vi/common/vi.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vi.h 8.46 (Berkeley) 8/8/94
+ */
+
+/*
+ * Forward structure declarations. Not pretty, but the include files
+ * are far too interrelated for a clean solution.
+ */
+typedef struct _cb CB;
+typedef struct _ch CH;
+typedef struct _excmdarg EXCMDARG;
+typedef struct _exf EXF;
+typedef struct _fref FREF;
+typedef struct _gs GS;
+typedef struct _ibuf IBUF;
+typedef struct _lmark LMARK;
+typedef struct _mark MARK;
+typedef struct _msg MSG;
+typedef struct _option OPTION;
+typedef struct _optlist OPTLIST;
+typedef struct _scr SCR;
+typedef struct _script SCRIPT;
+typedef struct _seq SEQ;
+typedef struct _tag TAG;
+typedef struct _tagf TAGF;
+typedef struct _text TEXT;
+
+/*
+ * Local includes.
+ */
+#include "term.h" /* Required by args.h. */
+#include "args.h" /* Required by options.h. */
+#include "options.h" /* Required by screen.h. */
+#include "search.h" /* Required by screen.h. */
+
+#include "msg.h" /* Required by gs.h. */
+#include "cut.h" /* Required by gs.h. */
+#include "seq.h" /* Required by screen.h. */
+#include "gs.h" /* Required by screen.h. */
+#include "screen.h" /* Required by exf.h. */
+#include "mark.h" /* Required by exf.h. */
+#include "exf.h"
+#include "log.h"
+#include "mem.h"
+
+#if FWOPEN_NOT_AVAILABLE /* See PORT/clib/fwopen.c. */
+#define EXCOOKIE sp
+int ex_fflush __P((SCR *));
+int ex_printf __P((SCR *, const char *, ...));
+FILE *fwopen __P((SCR *, void *));
+#else
+#define EXCOOKIE sp->stdfp
+#define ex_fflush fflush
+#define ex_printf fprintf
+#endif
+
+/* Macros to set/clear/test flags. */
+#define F_SET(p, f) (p)->flags |= (f)
+#define F_CLR(p, f) (p)->flags &= ~(f)
+#define F_ISSET(p, f) ((p)->flags & (f))
+
+#define LF_INIT(f) flags = (f)
+#define LF_SET(f) flags |= (f)
+#define LF_CLR(f) flags &= ~(f)
+#define LF_ISSET(f) (flags & (f))
+
+/*
+ * XXX
+ * MIN/MAX have traditionally been in <sys/param.h>. Don't
+ * try to get them from there, it's just not worth the effort.
+ */
+#ifndef MAX
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+/* Function prototypes that don't seem to belong anywhere else. */
+int nonblank __P((SCR *, EXF *, recno_t, size_t *));
+void set_alt_name __P((SCR *, char *));
+char *tail __P((char *));
+CHAR_T *v_strdup __P((SCR *, CHAR_T *, size_t));
+void vi_putchar __P((int));
+
+#ifdef DEBUG
+void TRACE __P((SCR *, const char *, ...));
+#endif
+
+/* Digraphs (not currently real). */
+int digraph __P((SCR *, int, int));
+int digraph_init __P((SCR *));
+void digraph_save __P((SCR *, int));
diff --git a/usr.bin/vi/docs/README b/usr.bin/vi/docs/README
new file mode 100644
index 0000000..0fe3a12
--- /dev/null
+++ b/usr.bin/vi/docs/README
@@ -0,0 +1,200 @@
+# @(#)README 8.86 (Berkeley) 8/17/94
+
+This is the README for version 1.34 of nex/nvi, a freely redistributable
+replacement for the Berkeley ex and vi text editors. The compressed or
+gzip'd archives for this and future versions, can be retrieved by using
+anonymous ftp to ftp.cs.berkeley.edu, from the file ucb/4bsd/nvi.tar.Z,
+or ucb/4bsd/nvi.tar.gz.
+
+If you have any questions about nvi, or problems making it work, please
+contact me by electronic mail at one of the following addresses:
+
+ uunet!bostic
+ bostic@cs.berkeley.edu
+
+Keith Bostic
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Redistribution:
+
+This software is copyrighted by the The Regents of the University of
+California, but may be freely redistributed (or sold, or used to line
+your birdcage) under the following conditions:
+
+/*-
+ * Copyright (c) 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+o Credit where it's due:
+
+ This software was originally derived from software contributed
+ to the University of California, Berkeley by Steve Kirkendall,
+ the author of the vi clone elvis. Without his work, this work
+ would have been far more difficult.
+
+ POSIX 1003.2 style regular expression support is courtesy of
+ Henry Spencer, for which I am *very* grateful.
+
+ The curses library was originally done by Ken Arnold. Scrolling
+ and general reworking for 4.4BSD was done by Elan Amir.
+
+o From the original vi acknowledgements, by William Joy and Mark Horton:
+
+ Bruce Englar encouraged the early development of this display
+ editor. Peter Kessler helped bring sanity to version 2's
+ command layout. Bill Joy wrote versions 1 and 2.0 through 2.7,
+ and created the framework that users see in the present editor.
+ Mark Horton added macros and other features and made the editor
+ work on a large number of terminals and Unix systems.
+
+o And...
+ The financial support of UUNET Communications Services is gratefully
+ acknowledged.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Status:
+
+This software is in beta test, and it's pretty stable. Almost all of
+the historic functionality in ex/vi is there, the only major missing
+pieces are open mode and the lisp option. (Also, the options hardtabs,
+optimize, redraw, and slowopen are recognized, but ignored.)
+
+Nvi is mostly 8-bit clean. This isn't difficult to fix, and was left
+in during initial development to keep things simple. Wide character
+support will be integrated at the same time that it is made fully 8-bit
+clean.
+
+There aren't a lot of new features in nex/nvi, but there are a few things
+you might like. The "Additional Features" section of the reference page
+(USD.doc/vi.ref/vi.ref.txt, USD.doc/vi.ref/vi.ref.ps) has more information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Porting information:
+
+The directory "PORT" has directories for specific OS/machine combinations,
+including V7-style Makefiles, for building nex/nvi on different machines.
+See the file PORT/README for detailed information.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Debugging:
+
+Code fixes are appreciated, of course, but if you can't provide them,
+please email me as much information as you can as to how to reproduce
+the bug, and I'll try to fix it locally. Stack traces of core dumps
+are only rarely helpful -- an example file with a set of keystrokes that
+causes the problem is almost invariably necessary.
+
+Please include the following in the bug report;
+
+ o The version of nvi you're running (use :version to get it).
+ o The row/column dimensions of the screen (80 x 32).
+ o Unless you're confident that they're not part of the problem,
+ your startup files (.exrc, .nexrc) and the environment variable
+ (EXININT, NEXINIT) values. (Cutting and pasting the output
+ of ":set all" is usually sufficient.)
+
+If you're running a memory checker (e.g. Purify) on nvi, you will want
+to recompile everything with "-DPURIFY" in the CFLAGS, first. By
+default, allocated pages are not initialized by the DB code, and they
+will show up as reads of uninitialized memory in the buffer write routines.
+
+=-=-=-=-=-=-=-=-=-=-=
+o Directory layout:
+
+nvi/USD.doc:
+ Ex/vi documentation, both historic and current.
+
+ edit/ Roff source for "Edit: A tutorial", USD:14 in the
+ 4.3BSD manuals.
+ ex/ Roff source for "Ex Reference Manual -- Version
+ 3.7", USD:16 in the 4.3BSD manuals.
+ vi/ Roff source for "An Introduction to Display
+ Editing with Vi", USD:15 in the 4.3BSD manuals.
+ Includes the "Vi Quick Reference" card.
+ vi.man/ Manual page for nex/nvi; an updated version of
+ the document distributed with 4.4BSD-Lite.
+ vi.ref/ Reference document for nex/nvi; an updated version
+ of the document distributed with 4.4BSD-Lite.
+
+nvi/common:
+ Source files for pieces of code that are shared by all the editors,
+ like searching and logging code or code translating line numbers
+ into requests to the dbopen(3) database code. It also has the
+ interface code for modifying "records" in the underlying database.
+
+nvi/docs:
+ Random nvi documentation:
+
+ README -- Nvi main README file.
+ bugs.current -- Major known bugs in the current nvi.
+ changelog -- Log of changes from version to version.
+ features -- Todo list, suggested features list.
+ internals/
+ autowrite -- Vi autowrite option discussion.
+ gdb.script -- GDB debugging scripts.
+ input -- Vi maps, executable buffers, and input discussion.
+ quoting -- Vi quoting discussion.
+ structures -- Out-of-date nvi internal structure description.
+ tutorial/ -- Historic vi tutorial(s), of unknown quality.
+
+nvi/ex:
+ The ex source code. Because vi has the colon command, lots of
+ this code is used by vi. Generally, if functionality is shared
+ by both ex and vi, it's in nvi/ex. If it's vi only, it's in
+ nvi/vi. Files are generally named by the command(s) they support,
+ but occasionally with a name that describes their functionality.
+
+nvi/install:
+ Things to install on the local system.
+
+ recover.script -- Vi recovery script.
+
+nvi/PORT:
+ Porting directories, one per OS/architecture combination. See
+ nvi/PORT/README for porting information.
+
+ curses/ -- 4.4BSD curses implementation
+ db/ -- 4.4BSD DB routines.
+ regex/ -- Henry Spencer's POSIX.2 RE support.
+
+nvi/sex:
+ The screen support for the ex editor.
+
+nvi/svi:
+ The screen support for a curses based vi editor.
+
+nvi/vi:
+ The vi source code.
+
+nvi/xaw:
+ Place reserved for an X11 (Athena Widget) screen.
diff --git a/usr.bin/vi/docs/bugs.current b/usr.bin/vi/docs/bugs.current
new file mode 100644
index 0000000..500f3ba
--- /dev/null
+++ b/usr.bin/vi/docs/bugs.current
@@ -0,0 +1,44 @@
+List of known bugs:
+
++ The number option doesn't display line numbers in ex append/insert
+ mode.
+
++ The option sidescroll is completely wrong, and setting it does more
+ harm than good.
+
++ When nvi edits files that don't have trailing newlines, it appends
+ one, regardless.
+
++ Open mode is not yet implemented.
+
++ ^C isn't passed to the shell in the script windows as an interrupt
+ character.
+
++ The options:
+
+ hardtabs, lisp, optimize, redraw, slowopen
+
+ are recognized, but not implemented. These options are unlikely to
+ be implemented, so if you want them you might want to say something!
+ I will implement lisp if anyone ever documents how it worked.
+
++ Screen repainting over slow lines, for some screen changes, isn't
+ as good as the historic vi's.
+
++ The line movement commands ('k', 'j' are easy examples) don't find the
+ most attractive cursor position correctly when wrapped lines are longer
+ than 80 characters, and they're on the second or subsequent lines.
+
++ Colon commands longer than a single line cause the display to be
+ incorrect.
+
++ The usages of S_{REDRAW,REFORMAT,REFRESH,RENUMBER,RESIZE} are
+ inconsistent, and should be reviewed. In particular, S_REFRESH
+ in any screen redraws all screens.
+
++ Historic vi permitted :g/xxx/vi, i.e. you could execute ex/vi as
+ global commands. Need to review all of the old commands to verify
+ which ones could/could not be used as global commands.
+
++ If you run out of space in the recovery directory, the recovery
+ file is left in place.
diff --git a/usr.bin/vi/docs/changelog b/usr.bin/vi/docs/changelog
new file mode 100644
index 0000000..54e98a2
--- /dev/null
+++ b/usr.bin/vi/docs/changelog
@@ -0,0 +1,528 @@
+1.33 -> 1.34 Wed Aug 17 14:37:32 1994 (PUBLICLY AVAILABLE VERSION)
+ + Back out sccsid string fix, it won't work on SunOS 4.1.
+1.32 -> 1.33 Wed Aug 17 09:31:41 1994 (PUBLICLY AVAILABLE VERSION)
+ + Get back 5K of data space for the sccsid strings.
+ + Fix bug where cG fix in version 1.31 broke cw cursor positioning
+ when the change command extended the line.
+ + Fix core dump in map/seq code if character larger than 7 bits.
+ + Block signals when manipulating the SCR chains.
+ + Fix memory allocation for machines with multiple pointer sizes.
+1.31 -> 1.32 Mon Aug 15 14:27:49 1994
+ + Turn off recno mmap call for Solaris 2.4/SunOS 5.4.
+1.30 -> 1.31 Sun Aug 14 13:13:35 1994
+ + Fix bug were cG on the last line of a file wasn't done in line mode,
+ and where the cursor wasn't positioned correctly after exiting text
+ insert mode.
+ + Add termcap workaround to make function keys greater than 9 work
+ correctly (or fail if old-style termcap support).
+ + Change ex/vi to not flush mapped keys on error -- this is historic
+ practice, and people depended on it.
+ + Rework vi parser so that no command including a mapped key ever
+ becomes the '.' command, matching historic practice.
+ + Make <escape> cancellation in the vi parser match POSIX 1003.2.
+ + Fix curses bug where standout string was written for each standout
+ character, and where standout mode was never exited explicitly.
+ Fix bugs in curses SF/sf and SR/sr scrolling, as seen on Sun and
+ x86 consoles.
+ + The v/global commands execute the print command by default.
+ + The number option historically applies to ex as well as vi.
+1.29 -> 1.30 Mon Aug 8 10:30:42 1994
+ + Make first read into a temporary set the file's name.
+ + Permit any key to continue scrolling or ex commands -- this
+ allows stacked colon commands, and matches historic practice.
+ + Don't output normal ! command commentary in ex silent mode.
+ + Allow +/- flags after substitute commands, make line (flag)
+ offsets from vi mode match historic practice.
+ + Return <eof> to ex immediately, even if preceded by spaces. Rework
+ ex parser to do erase the prompt instead of depending on the print
+ routines to do it. Minor fixes to the ex parser for display of
+ default and scrolling commands. MORE EX PARSER CHANGES.
+1.28 -> 1.29 Fri Aug 5 10:18:07 1994
+ + Make the abbreviated ex delete command work (:dele---###lll for
+ example, is historically legal.
+ + When autoprint fires, multiple flags may be set, use ex_print
+ directly instead of the stub routines.
+ + Change v/global commands to turn off autoprint while running.
+ + Minor changes to make the ! command display match historic output.
+ + Rework the ex parser to permit multiple command separators without
+ commands -- MAJOR CHANGE, likely to introduce all sorts of new bugs.
+ + Fix cd command to expand argument in the context of each element
+ of the cdpath option, make relative paths always relative to the
+ current directory.
+ + Rework write/quit cases for temporary files, so that user's don't
+ discard them accidentally.
+ + Check for window size changes when continuing after a suspend.
+ + Fix memory problem in svi_screen, used free'd memory.
+ + Change the ex change, insert, append commands to match historic
+ cursor positions if no data entered by the user.
+ + Change ex format flags (#, l, p) to affect future commands, not
+ just the current one, to match historic practice.
+ + Make the user's EOF character an additional scroll character in ex.
+ + Fix ex ^D scrolling to be the value of the scroll option, not half
+ the screen.
+ + Fix buffer execution to match historic practice -- bugs where the
+ '*' command didn't work, and @<carriage-return> didn't work.
+ + Fix doubled reporting of deleted lines in filters.
+ + Rework the % ` / ? ( ) N n { and ^A commands to always cut into
+ numeric buffers regardless of the location or length of the cut.
+ This matches historic practice.
+ + Fix the { command to check the current line if the cursor doesn't
+ start on the first character of the line.
+ + Do '!' expansion in the ex read command arguments, it's historic
+ practice. In addition, it sets the last '!' command.
+1.27 -> 1.28 Wed Jul 27 21:29:18 1994
+ + Add support for scrolling using the CS and SF/sf/SR/sr termcap
+ strings to the 4BSD curses.
+ + Rework of getkey() introduced a bug where command interrupt put
+ nvi into an infinite loop.
+ + Piping through a filter historically cut the replaced lines into
+ the default buffer, although not the numeric ones.
+ + Read of a filter and !! historically moved to the first nonblank
+ of the resulting cursor line (most of the time).
+ + Rework cursor motion flags, to support '!' as a motion command.
+1.26 -> 1.27 Tue Jul 26 10:27:58 1994
+ + Add the meta option, to specify characters the shell will expand.
+ + Fix the read command to match historic practice, the white space
+ and bang characters weren't getting parsed correctly.
+ + Change SIGALRM handler to save and restore errno.
+ + Change SunOS include/compat.h to include <vfork.h> so that the
+ ex/filter.c code works again.
+ + Don't put lines deleted by the ex delete command into the numeric
+ buffers, matching historic practice.
+ + Fix; if appending to a buffer, default buffer historically only
+ references the appended text, not the resulting text.
+ + Support multiple, semi-colon separated search strings, and 'z'
+ commands after search strings.
+ + Make previous context mark setting match historic practice (see
+ docs/internals/context).
+ + Fix the set command to permit whitespace between the option and
+ the question mark, fix question marks in general.
+ + Fix bug where ex error messages could be accidentally preceded
+ by a single space.
+ + Fix bug where curses reorganization could lose screen specific
+ mappings as soon as any screen exited.
+ + Fix bug in paragraph code where invalid macros could be matched.
+ Make paragraph motions stop at formfeed (^L) characters.
+ + Change 'c' to match historic practice, it cut text into numeric
+ buffers.
+1.25 -> 1.26 Tue Jul 19 17:46:24 1994
+ + Ignore SIGWINCH if the screen size is unchanged; SunOS systems
+ deliver one when a screen is uncovered.
+ + Fix: don't permit a command with a motion component to wrap due
+ to wrapscan and return to the original cursor position.
+ + Fix: ^E wasn't beeping when reaching the bottom of the file.
+ + Fix bg/fg bug where tmp file exiting caused a NULL dereference.
+ + Rework file locking code to use fcntl(2) explicitly.
+ + Fix bug in section code where invalid macros could be matched.
+ + Fix bug where line number reset by vi's Q command.
+ + Add explicit character mode designation to character mode buffers.
+ + Add <sys/ioctl.h> include to sex/sex_window.c, needed by NET/2
+ vintage systems.
+ + Change to always flush a character during suspend, 4BSD curses
+ has the optimization where it doesn't flush after a standend().
+ + Fix bug on OSF1 where <curses.h> changes the values of VERASE,
+ VKILL and VWERASE to incorrect ones.
+ + Fix bug where optarg used incorrectly in main.c.
+ + Block all signals when acting on a signal delivery.
+ + Fix recovery bug where RCV_EMAIL could fire even if there wasn't
+ a backing file; format recovery message.
+1.24 -> 1.25 Sun Jul 17 14:33:38 1994
+ + Stop allowing keyboard suspends (^Z) in insert mode, it's hard
+ to get autowrite correct, and it's not historic practice.
+ + Fix z^, z+ to match historic practice.
+ + Bug in message handling, "vi +35 non-existent_file" lost the
+ status message because the "+35" pushed onto the stack erased
+ it. For now, change so that messages aren't displayed if there
+ are keys waiting -- may need to add a "don't-erase" bit to the
+ character in the stack instead.
+ + Bug in svi_msgflush(), where error messages could come out in
+ normal video.
+1.23 -> 1.24 Sat Jul 16 18:30:18 1994
+ + Fix core dump in exf.c, where editing a non-existent file and
+ exiting could cause already free'd memory to be free'd.
+ + Clean up numerous memory errors, courtesy of Purify.
+ + Change process wait code to fail if wait fails, and not attempt
+ to interpret the wait return information.
+ + Open recovery and DB files for writing as well as reading, System
+ V (fcntl) won't let you acquire LOCK_EX locks otherwise.
+ + Fix substitute bug where could malloc 0 bytes (AIX breaks).
+ + Permit the mapping of <carriage-return>, it's historic practice.
+ + Historic vi didn't eat <blank> characters before the force
+ flag, match historic practice.
+ + Bug in ex argument parsing, corrected for literal characters
+ twice.
+ + Delete screen specific maps when the screen closes.
+ + Move to the first non-<blank> in the line on startup; historic
+ practice.
+ + Change the ex visual command to move directly to a line if no
+ trailing 'z' command.
+ + Fix "[[" and "]]" to match historic practice (yet again...).
+ + Fix "yb" and "y{" commands to update the cursor correctly.
+ + Change "~<motion>" to match the yank cursor movement semantics
+ exactly.
+ + Move all of the curses related code into sex/svi -- major rework,
+ but should help in future ports.
+ + Fix bug in split code caused by new file naming code, where would
+ drop core when a split screen exited.
+ + Change svi_ex_write to do character display translation, so that
+ messages with file names in them are displayed correctly.
+ + Display the file name on split screens instead of a divider line.
+ + Fix move bug, wasn't copying lines before putting them.
+ + Fix bug were :n dropped core if no arguments supplied.
+ + Don't quote characters in executed buffer: "ifoo<esc>" should leave
+ insert mode after the buffer is executed.
+ + Tagpop and tagpush should set the absolute mark in case only moving
+ within a file.
+ + Skip leading whitespace characters before tags and cursor word
+ searches.
+ + Fix bug in ex_global where re_conv() was allocating the temporary
+ buffer and not freeing it.
+1.22 -> 1.23: Wed Jun 29 19:22:33 1994
+ + New <sys/cdefs.h> required "inline" to change to "__inline"
+ + Fix System V curses code for new ^Z support.
+ + Fix off-by-one in the move code, avoid ":1,$mo$" with only one
+ line in the buffer.
+ + Line orientation of motion commands was remembered too long,
+ i.e. '.' command could be incorrectly marked as line oriented.
+ + Move file modification time into EXF, so it's shared across
+ split screens.
+ + Put the prev[ious] command back in, people complained.
+ + Random fixes to next/prev semantics changed in 1.22.
+ + Historically vi doesn't only move to the last address if there's
+ ANYTHING after the addresses, e.g. ":3" moves to line 3, ":3|"
+ prints line 3.
+1.21 -> 1.22: Mon Jun 27 11:01:41 1994
+ + Make the line between split screens inverse video again.
+ + Delete the prev[ious] command, it's not useful enough to keep.
+ + Rework :args/file name handling from scratch -- MAJOR CHANGE,
+ likely to introduce all sorts of new bugs.
+ + Fix RE bug where no subexpressions in the pattern but there were
+ subexpressions referenced in the replacement, e.g. "s/XXX/\1/g".
+ + Change recovery to not leave unmodified files around after a
+ crash, by using the owner 'x' bit on unmodified backup files.
+ MAJOR CHANGE, the system recovery script has to change!
+ + Change -r option to delete recovery.* files that reference non-
+ existent vi.* files.
+ + Rework recovery locking so that fcntl(2) locking will work.
+ + Fix append (upper-case) buffers, broken by cut fixes.
+ + Fix | to not set the absolute motion mark.
+ + Read $HOME/.exrc file on startup if the effective user ID is
+ root. This makes running vi while su(1)'d work correctly.
+ + Use the full pathname of the file as the recovery name, not
+ just the last component. Matches historic practice.
+ + Keep marks in empty files from being destroyed.
+ + Block all caught signals before calling the DB routines.
+ + Make the line change report match historic practice (yanked
+ lines were different than everything else).
+ + Add section on multiple screens to the reference manual.
+ + Display all messages at once, combine onto a single line if
+ possible. Delete the trailing period from all messages.
+1.20 -> 1.21: Thu May 19 12:21:58 1994
+ + Delete the -l flag from the recover mail.
+ + Send the user email if ex command :preserve executed, this matches
+ historic practice. Lots of changes to the preserve and recovery
+ code, change preserve to snapshot files (again, historic practice).
+ + Make buffers match historic practice: "add logically stores text
+ into buffer a, buffer 1, and the unnamed buffer.
+ + Print <tab> characters as ^I on the colon command line if the
+ list option set.
+ + Adjust ^F and ^B scroll values in the presence of split screens
+ and small windows.
+ + Break msg* routines out from util.c into msg.c, start thinking
+ about message catalogs.
+ + Add tildeop set option, based on stevie's option of the same name.
+ Changes the ~ command into "[count] ~ motion", i.e. ~ takes a
+ trailing motion.
+ + Chose NOT to match historic practice on cursor positioning after
+ consecutive undo commands on a single line; see vi/v_undo.c for
+ the comment.
+ + Add a one line cache so that multiple changes to the same line
+ are only counted once (e.g. "dl35p" changes one line, not 35).
+ + Rework signals some more. Block file sync signals in vi routines
+ that interface to DB, so can sync the files at interrupt time.
+ Write up all of the signal handling arguments, see signal.c.
+1.19 -> 1.20: Thu May 5 19:24:57 1994
+ + Return ^Z to synchronous handling. See the dicussion in signal.c
+ and svi_screen.c:svi_curses_init().
+ + Fix bug where line change report was wrong in util.c:msg_rpt().
+1.18 -> 1.19: Thu May 5 12:59:51 1994
+ + Block DSUSP so that ^Y isn't delivered at SIGTSTP.
+ + Fix bug -- put into an empty file leaves the cursor at 1,0,
+ not the first nonblank.
+ + Fix bug were number of lines reported for the 'P' command was
+ off-by-one.
+ + Fix bug were 0^D wasn't being handled correctly.
+ + Delete remnants of ^Z as a raw character.
+ + Fix bug where if a map was an entire colon command, it may never
+ have been displayed.
+ + Final cursor position fixes for the vi T and t commands.
+ + The ex :next command took an optional ex command as it's first
+ argument similar to the :edit commands. Match historic practice.
+1.17 -> 1.18: Wed May 4 13:57:10 1994
+ + Rework curses information in the PORT/Makefile's.
+ + Minor fixes to ^Z asynchronous code.
+1.16 -> 1.17: Wed May 4 11:15:56 1994
+ + Make ex comment handling match historic practice.
+ + Make ^Z work asynchronously, we can no longer use the SIGTSTP
+ handler in the curses library.
+1.15 -> 1.16: Mon May 2 19:42:07 1994
+ + Make the 'p' and 'P' commands support counts, i.e. "Y10p" works.
+ + Make characters that map to themselves as the first part of the
+ mapping work, it's historic practice.
+ + Fix bug where "s/./\& /" discarded the space in the replacement
+ string.
+ + Add support for up/down cursor arrows in text input mode, rework
+ left/right support to match industry practice.
+ + Fix bug were enough character remapping could corrupt memory.
+ + Delete O_REMAPMAX in favor of setting interrupts after N mapped
+ characters without a read, delete the map counter per character.
+ MAJOR CHANGE. All of the interrupt signal handling has been
+ reworked so that interrupts are always turned on instead of
+ being turned on periodically, when an interruptible operation is
+ pending.
+ + Fix bug where vi wait() was interrupted by the recovery alarm.
+ + Make +cmd's and initial commands execute with the current line
+ set to the last line of the file. This is historic practice.
+ + Change "lock failed" error message to a file status message.
+ It always fails over NFS, and making all NFS files readonly
+ isn't going to fly.
+ + Use the historic line number format, but check for overflow.
+ + Fix bug where vi command parser ignored buffers specified as
+ part of the motion command.
+ + Make [@*]buffer commands on character mode buffers match historic
+ practice.
+ + Fix bug where the cmap/chf entries of the tty structure weren't
+ being cleared when new characters were read.
+ + Fix bug where the default command motion flags were being set
+ when the command was a motion component.
+ + Fix wrapmargin bug; if appending characters, and wrapmargin breaks
+ the line, an additional space is eaten.
+1.14 -> 1.15: Fri Apr 29 07:44:57 1994
+ + Make the ex delete command work in any empty file.
+ + Fix bug where 't' command placed the cursor on the character
+ instead of to its left.
+ + ^D and ^U didn't set the scroll option value historically.
+ Note, this change means that any user set value (e.g. 15^D)
+ will be lost when splitting the screen, since the split code
+ now resets the scroll value regardless.
+ + Fix the ( command to set the absolute movement mark.
+ + Only use TIOCGWINSZ for window information if SIGWINCH signal
+ caught.
+ + Delete the -l flag, and make -r work for multiple arguments.
+ Add the ex "recover[!] file" command.
+ + Switch into ex terminal mode and use the sex routines when
+ append/change/insert called from vi mode.
+ + Make ^F and ^B match historic practice. This required a fairly
+ extensive rework of the svi scrolling code.
+ + Cursor positioning in H, M, L, G (first non-blank for 1G) wasn't
+ being done correctly. Delete the SETLFNB flag. H, M, and L stay
+ logical movements (SETNNB) and G always moves to the first nonblank.
+ + System V uses "lines" and "cols", not "li" and "co", change as
+ necessary. Check termcap function returns for errors.
+ + Fix `<character> command to do start/end of line correction,
+ and to set line mode if starting and stopping at column 0.
+ + Fix bug in delete code where dropped core if deleted in character
+ mode to an empty line. (Rework the delete code for efficiency.)
+ + Give up on SunOS 4.1.X, and use "cc" instead of /usr/5bin/cc.
+ + Protect ex_getline routine from interrupted system calls (if
+ possible, set SA_RESTART on SIGALRM, too).
+ + Fix leftright scrolling bug, when moving to a shorter line.
+ + Do validity checking on the copy, move, t command target line
+ numbers.
+ + Change for System V % pattern broke trailing flags for empty
+ replacement strings.
+ + Fix bug when RCM flags retained in the saved dot structure.
+ + Make the ex '=' command work for empty files.
+ + Fix bug where special_key array was being free'd (it's no longer
+ allocated).
+ + Matches cut in line mode only if the starting cursor is at or
+ before the first nonblank in its line, and the ending cursor is
+ at or after the last nonblank in its line.
+ + Add the :wn command, so you can write a file and switch to a new
+ file in one command.
+ + Allow only a single key as an argument to :viusage.
+ + New movement code broke filter/paragraph operations in empty
+ files ("!}date" in an empty file was dropping core).
+1.12 -> 1.14: Mon Apr 18 11:05:10 1994 (PUBLICLY AVAILABLE VERSION, 4.4BSD)
+ + Fix FILE structure leakage in the ex filter code.
+ + Rework suspend code for System V curses. Nvi has to do the
+ the work, there's no way to get curses to do it right.
+ + Revert SunOS 4.1.X ports to the distributed curses. There's
+ a bug in Sun's implementation that we can't live with.
+ + Quit immediately if row/column values are unreasonable.
+ + Fix the function keys to match vi historic behavior.
+ + Replace the echo/awk magic in the Makefile's with awk scripts.
+1.11 -> 1.12: Thu Apr 14 11:10:19 1994
+ + Fix bug where only the first vi key was checked for validity.
+ + Make 'R' continue to overwrite after a <carriage-return>.
+ + Only display the "no recovery" message once.
+ + Rework line backup code to restore the line to its previous
+ condition.
+ + Don't permit :q in a .exrc file or EXINIT variable.
+ + Fix wrapscan option bug where forward searches become backward
+ searches and do cursor correction accordingly.
+ + Change "dd" to move the cursor to the first non-blank on the line.
+ + Delete cursor attraction to the first non-blank, change non-blank
+ motions to set the most attractive cursor position instead.
+ + Fix 'r' substitute option to set the RE to the last RE, not the
+ last substitute RE.
+ + Fix 'c' and 'g' substitute options to always toggle, and fix
+ edcompatible option to not reset them.
+ + Display ex error messages in inverse video.
+ + Fix errorbells option to match historic practice.
+ + Delete fixed character display table in favor of table built based
+ on the current locale.
+ + Add ":set octal" option, that displays unknown characters as octal
+ values instead of the default hexadecimal.
+ + Make all command and text input modes interruptible.
+ + Fix ex input mode to display error messages immediately, instead
+ of waiting for the lines to be resolved.
+ + Fix bug where vi calling append could overwrite the command.
+ + Fix off-by-one in the ex print routine tab code.
+ + Fix incorrect ^D test in vi text input routines.
+ + Add autoindent support for ex text insert routines.
+ + Add System V substitute command replacement pattern semantics,
+ where '%' means the last replacement pattern.
+ + Fix bug that \ didn't escape newlines in ex commands.
+ + Regularize the names of special characters to CH_*.
+ + Change hex insert character from ^Vx<hex_char> to ^X<hex_char>
+ + Integrate System V style curses, so SunOS and Solaris ports can
+ use the native curses implementation.
+1.10 -> 1.11: Thu Mar 24 16:07:45 EST 1994 (PUBLICLY AVAILABLE VERSION)
+ + Change H, M, and L to set the absolute mark, historical practice.
+ + Fix bug in stepping through multiple tags files.
+ + Add "remapmax" option that turns off map counts so you can remap
+ infinitely. If it's off, term_key() can be interrupted from the
+ keyboard, which will cause the buffers to flush. I also dropped
+ the default max number of remaps to 50. (Only Dave Hitz's TM
+ macros and maze appear to go over that limit.)
+ + Change :mkexrc to not dump w{300,1200,9600}, lisp options.
+ + Fix backward search within a line bug.
+ + Change all the includes of "pathnames.h" to use <>'s so that the
+ PORT versions can use -I. to replace it with their own versions.
+ + Make reads and writes interruptible. Rework code that enters and
+ leaves ex for '!' and filter commands, rework all interrupt and
+ timer code.
+ + Fix core dump when user displayed option in .exrc file.
+ + Fix bug where writing empty files didn't update the saved
+ modification time.
+ + Fix bug where /pattern/ addressing was always a backward search.
+ + Fix bug triggered by autoindent of more than 32 characters, where
+ nvi wasn't checking the right TEXT length.
+ + Fix bug where joining only empty lines caused a core dump.
+1.09 -> 1.10: Sat Mar 19 15:40:29 EST 1994
+ + Fix "set all" core dump.
+1.08 -> 1.09: Sat Mar 19 10:11:14 EST 1994
+ + If the tag's file path is relative, and it doesn't exist, check
+ relative to the tag file location.
+ + Fix ~ command to free temporary buffer on error return.
+ + Create vi.ref, a first cut at a reference document for vi.
+ The manual page and the reference document only document the
+ set options, so far.
+ + Fix 1G bug not always going to the first non-blank.
+ + Upgrade PORT/regex to release alpha3.4, from Henry Spencer.
+ + Add MKS vi's "cdpath" option, supporting a cd search path.
+ + Handle if search as a motion was discarded, i.e. "d/<erase>".
+ + Change nvi to not create multiple recovery files if modifying
+ a recovered file.
+ + Decide to ignore that the cursor is before the '$' when inserting
+ in list mode. It's too hard to fix.
+1.07 -> 1.08: Wed Mar 16 07:37:36 EST 1994
+ + Leftright and big line scrolling fixes. This meant more changes
+ to the screen display code, so there may be new problems.
+ + Don't permit search-style addresses until a file has been read.
+ + "c[Ww]" command incorrectly handled the "in whitespace" case.
+ + Fix key space allocation bug triggered by cut/paste under SunOS.
+ + Ex move command got the final cursor position wrong.
+ + Delete "optimize option not implemented" message.
+ + Make the literal-next character turn off mapping for the next
+ character in text input mode.
+1.06 -> 1.07: Mon Mar 14 11:10:33 EST 1994
+ + The "wire down" change in 1.05 broke ex command parsing, there
+ wasn't a corresponding change to handle multiple K_VLNEXT chars.
+ + Fix final position for vi's 't' command.
+1.05 -> 1.06: Sun Mar 13 16:12:52 EST 1994
+ + Wire down ^D, ^H, ^W, and ^V, regardless of the user's termios
+ values.
+ + Add ^D as the ex scroll command.
+ + Support ^Q as a literal-next character.
+ + Rework abbreviations to be delimited by any !inword() character.
+ + Add options description to the manual page.
+ + Minor screen cache fix for svi_get.c.
+ + Rework beautify option support to match historical practice.
+ + Exit immediately if not reading from a tty and a command fails.
+ + Default the SunOS 4.* ports to the distributed curses, not SMI's.
+1.04 -> 1.05: Thu Mar 24 16:07:45 EST 1994
+ + Make cursor keys work in input mode.
+ + Rework screen column code in vi curses screen. MAJOR CHANGE --
+ after this, we'll be debugging curses screen presentation from
+ scratch.
+ + Explode include files in vi.h into the source files.
+1.03 -> 1.04: Sun Mar 6 14:14:16 EST 1994
+ + Make the ex move command keep the marks on the moved lines.
+ + Change resize semantics so you can set the screen size to a
+ specific value. A couple of screen fixes for the resize code.
+ + Fixes for foreground/background due to SIGWINCH.
+ + Complete rework of all of vi's cursor movements. The underlying
+ assumption in the old code was that the starting cursor position
+ was part of the range of lines cut or deleted. The command
+ "d[[" is an example where this isn't true. Change it so that all
+ motion component commands set the final cursor position separately
+ from the range, as it can't be done correctly later. This is a
+ MAJOR CHANGE -- after this change, we'll be debugging the cursor
+ positioning from scratch.
+ + Rewrite the B, b, E, e commands to use vi's getc() interface
+ instead of rolling their own.
+ + Add a second MARK structure, LMARK, which is the larger mark
+ needed by the logging and mark queue code. Everything else uses
+ the reworked MARK structure, which is simply a line/column pair.
+ + Rework cut/delete to not expect 1-past-the-end in the range, but
+ to act on text to the end of the range, inclusive.
+ + Sync on write's, to force NFS to flush.
+1.01 -> 1.03: Sun Jan 23 17:50:35 EST 1994 (PUBLICLY AVAILABLE VERSION)
+ + Tag stack fixes, was returning to the tag, not the position from
+ which the user tagged.
+ + Only use from the cursor to the end of the word in cursor word
+ searches and tags. (Matches historical vi behavior.)
+ + Fix delete-last-line bug when line number option set.
+ + Fix usage line for :split command.
+ + If O_NUMBER set, long input lines would eventually fail, the column
+ count for the second screen of long lines wasn't set correctly.
+ + Fix for [[ reaching SOF with a column longer than the first line.
+ + Fix for multiple error messages if no screen displayed.
+ + Fix :read to set alternate file name as in historical practice.
+ + Fix cut to rotate the numeric buffers if line mode flag set.
+1.00 -> 1.01: Wed Jan 12 13:37:18 EST 1994
+ + Don't put cut items into numeric buffers if cutting less than
+ parts of two lines.
+0.94 -> 1.00: Mon Jan 10 02:27:27 EST 1994
+ + Read-ahead not there; BSD tty driver problem, SunOS curses
+ problem.
+ + Global command could error if it deleted the last line of
+ the file.
+ + Change '.' to only apply to the 'u' if entered immediately
+ after the 'u' command. "1pu.u.u. is still broken, but I
+ expect that it's going to be sacrificed for multiple undo.
+ + If backward motion on a command, now move to the point; get
+ yank cursor positioning correct.
+ + Rework cut buffers to match historic practice -- yank/delete
+ numeric buffers redone sensibly, ignoring historic practice.
+0.92 -> 0.93: Mon Dec 20 19:52:14 EST 1993
+ + Christos Zoulas reimplemented the script windows using pty's,
+ which means that they now work reasonably. The down side of
+ this is that almost all ports other than 4.4BSD need to include
+ two new files, login_tty.c and pty.c from the PORT/clib directory.
+ I've added them to the Makefiles.
+ + All calloc/malloc/realloc functions now cast their pointers, for
+ SunOS -- there should be far fewer warning messages, during the
+ build. The remaining messages are where CHAR_T's meet char *'s,
+ i.e. where 8-bit clean meets strcmp.
+ + The user's argument list handling has been reworked so that there
+ is always a single consistent position for use by :next, :prev and
+ :rewind.
+ + All of the historical options are now at least accepted, although
+ not all of them are implemented. (Edcompatible, hardtabs, lisp,
+ optimize, redraw, and slowopen aren't implemented.)
+ + The RE's have been reworked so that matches of length 0 are handled
+ in the same way as vi used to handle them.
+ + Several more mapping fixes and ex parser addressing fixes.
diff --git a/usr.bin/vi/docs/ev b/usr.bin/vi/docs/ev
new file mode 100644
index 0000000..144295a
--- /dev/null
+++ b/usr.bin/vi/docs/ev
@@ -0,0 +1,55 @@
+# @(#)ev 8.4 (Berkeley) 4/29/94
+
+Ev: Vi: Result:
+<CK> <CK> (Cursor keys). Move around the file.
+
+Meta key commands:
+^A<#> <#>G Goto line #.
+^A$ G Goto the end of the file.
+^A/ / Prompt and execute a forward search.
+^A: : Prompt and execute an ex command.
+^A? ? Prompt and execute a backward search.
+^Ac y'<c> Copy to mark in line mode (or copy the current line).
+^AC y`<c> Copy to mark in character mode.
+^Ad d'<c> Delete to mark in line mode (or delete the current line).
+^AD d`<c> Delete to mark in character mode.
+^Aj J Join lines.
+^Am m<c> Mark the current cursor position.
+^AN N Repeat search in the reverse direction.
+^An ^A Search for the word under the cursor.
+^Ar u Redo a command.
+^Au u Undo a command.
+
+Single key commands:
+^B ^B Page up a screen.
+^C ^C Interrupt long-running commands.
+^D ^D Page down a half-screen.
+^E $ End of line.
+^F ^F Page down a screen.
+^G ^G File status/information.
+^H X Delete the character to the left of the cursor.
+^I (TAB)
+^J j Cursor down one line.
+^K k Cursor up one line.
+^L ^L Redraw the screen.
+^M (CR) ^M In insert mode, split the line at the current cursor,
+ creating a new line.
+ In overwrite mode, cursor down one line.
+^N n Repeat previous search, in previous direction.
+^O (UNUSED)
+^P p Paste the cut text at the cursor position.
+^Q (XON/XOFF)
+^R (UNUSED)
+^S (XON/XOFF)
+^T D Truncate the line at the cursor position.
+^U ^U Page up a half-screen.
+^V<c> ^V<c> Insert/overwrite with a literal next character.
+^W w Move forward one whitespace separated word.
+^X x Delete the current character.
+^Y (UNUSED)
+^Z ^Z Suspend.
+
+New ex mode commands:
+
+^A:set ov[erwrite] Toggle "insert" mode, so that input keys overwrite
+ the existing characters.
diff --git a/usr.bin/vi/docs/features b/usr.bin/vi/docs/features
new file mode 100644
index 0000000..ed78564
--- /dev/null
+++ b/usr.bin/vi/docs/features
@@ -0,0 +1,87 @@
+List of things that should be added:
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
++ X11 (Tk, Motif, Xaw) interface.
++ Interpreted language (Perl5, Scheme, Tcl)
++ Ports: Windows, Windows NT, MSDOS
++ Message catalogs.
++ Forms editing package; use RE's to verify field contents.
++ Internationalization, including wide character support.
++ Support for single line window editing, including full editing
+ capability on the vi colon command line.
++ Rob Pike's sam style RE's.
+
+List of suggested features:
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
++ Filename completion. While on the subject of completion, it would be
+ nice to have the completion mechanism found in tcsh version >= 6.03.
+ For instance, the completion for the `:cd' command will be directories
+ only. The completion for the `:set' command will be all options not
+ set at that moment, and for `:set un' will be all options that are set
+ at that moment. The completion for `:< count' will be the flags.
+
++ Add a "push" command that would push a file on the tags stack.
+ (Essentially make tags a special case of the stack, and make
+ the stack more general purpose.)
+
++ Make :script just run a command and edit the output, and :interactive,
+ which allows interactive shell session, instead of just the current
+ :script.
+
++ Add versioning based on a "set version" variable, that would
+ create backup copies when the file was written back, i.e. the
+ ":w" and autowrite's would copy the original.
+
++ Add tagging information to the man page so that users can display
+ the part of the man page that discusses the command in which they're
+ interested.
+
++ Add a zone option so that you can declare that top/bottom few lines
+ of the screen aren't filled except by accident, so that the text
+ you ask for is always concentrated in the center of the screen.
+
++ Add "set searchdir" for a list of directories to look in for
+ files to edit. The semantic is that ":e foo" is replaced with
+ the file name that is found, so there's no confusion as to
+ which file is written.
+
++ Change
+ :di[splay] tags -> :tags
+ :di[splay] screens -> :screens
+ :di[splay] buffers -> :buffers
+
++ A macro record function. Add the ability to record a sequence
+ of keystrokes into a named buffer for later use. Handy when
+ you're trying to build a semi-complex macro.
+
++ The semantics of :split, :bg, and :fg aren't right. Someone needs to
+ rethink how they should interact. The main problem arises when users
+ want to get a window into a new file. Currently, the necessary sequence
+ is ":split newfile|^W|:bg". It would be nice if you could simply
+ background the current screen and edit a new one.
+
++ An option to turn on a ``quarter plane'' model so that you can
+ go as far to the right or down as you wish. The File or the
+ current line is only extended if you actually put down a char at
+ the new location. Very handy for ascii graphics and tables.
+
++ Some way of replacing the command bindings. For this to work
+ cleanly the notion of a command must be separate from that of a
+ key. (Simulate the Rand editor?)
+
++ Vertical splitting, so you can see files side by side.
+
++ Tracking. Two or more files are associated so that when one file
+ is scrolled up/down/left/right other files track by the same amount.
+ Tracking may be constrained such that two files only track vertically
+ or horizontally. This is relatively easy to implement.
+
++ A status file so that the next time invocation of the editor returns
+ to the same place, with the same number of windows etc. In case of
+ change of the screen size, reasonable defaults are used. For each
+ window size and location of the window, name of the file and position
+ in it, any tab settings, any other settings for the window (such as
+ insert/overwrite mode, auto indent etc). Last search RE and maybe
+ direction. If a file does not exist the next time you invoke the
+ editor, its window is left in the same place but with some default
+ message.
diff --git a/usr.bin/vi/docs/internals/autowrite b/usr.bin/vi/docs/internals/autowrite
new file mode 100644
index 0000000..55cd13b
--- /dev/null
+++ b/usr.bin/vi/docs/internals/autowrite
@@ -0,0 +1,88 @@
+# @(#)autowrite 8.2 (Berkeley) 9/28/93
+
+Vi autowrite behavior, the fields with *'s are "don't cares".
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that are affected only by autowrite:
+
+Command File Autowrite? Action:
+ modified?
+-----------------------------------------------
+^Z Y Y Write file and suspend.
+^Z Y N Suspend.
+^Z N * Suspend.
+
+# This behavior is NOT identical to :edit.
+^ Y Y Write file and jump.
+^ Y N Error.
+^ N * Jump.
+
+# The new nvi command ^T (:tagpop) behaves identically to ^].
+# This behavior is identical to :tag, :tagpop, and :tagpush with
+# force always set to N.
+^] Y Y Write file and jump.
+^] Y N Error.
+^] N * Jump.
+
+# There's no way to specify a force flag to the '!' command.
+:! Y Y Write file and execute.
+:! Y N Warn (if warn option) and execute.
+:! N * Execute.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that are affected by both autowrite and force:
+
+NOTE: the "force" flag is never passed on, i.e. the write
+to the file caused by the autowrite flag is never forced.
+
+Command File Autowrite? Force? Action:
+ modified? (!)
+-------------------------------------------------------
+# The first rule (YYY) is historic practice, but seems wrong.
+# In nvi, :next and :prev commands behave identically to :rewind.
+:next Y Y Y Write changes and jump.
+:next Y Y N Write changes and jump.
+:next Y N Y Abandon changes and jump.
+:next Y N N Error.
+:next N * * Jump.
+
+:rewind Y Y Y Abandon changes and jump.
+:rewind Y Y N Write changes and jump.
+:rewind Y N Y Abandon changes and jump.
+:rewind Y N N Error.
+:rewind N * * Jump.
+
+# The new nvi commands, :tagpop and :tagtop, behave identically to :tag.
+# Note, this behavior is the same as :rewind and friends, as well.
+:tag Y Y Y Abandon changes and jump.
+:tag Y Y N Write changes and jump.
+:tag Y N Y Abandon changes and jump.
+:tag Y N N Error.
+:tag N * * Jump.
+
+# The command :suspend behaves identically to :stop.
+:stop Y Y Y Suspend.
+:stop Y Y N Write changes and suspend.
+:stop Y N Y Suspend.
+:stop Y N N Suspend.
+:stop N * * Suspend.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+Commands that might be affected by autowrite, but aren't:
+
+Command File Autowrite? Force? Action:
+ modified? (!)
+-------------------------------------------------------
+#:ex, and :vi (executed while in vi mode) behave identically to :edit.
+:edit Y * Y Abandon changes and jump.
+:edit Y * N Error.
+:edit N * * Jump.
+
+:quit Y * Y Quit.
+:quit Y * N Error.
+:quit N * * Quit.
+
+:shell * * * Execute shell.
+
+:xit Y * * Write changes and exit.
+:xit N * * Exit.
diff --git a/usr.bin/vi/docs/internals/context b/usr.bin/vi/docs/internals/context
new file mode 100644
index 0000000..139a1c3
--- /dev/null
+++ b/usr.bin/vi/docs/internals/context
@@ -0,0 +1,32 @@
+# @(#)context 8.5 (Berkeley) 7/23/94
+
+In historic vi, the previous context mark was always set:
+
+ex address:
+ any number, <question-mark>, <slash>, <dollar-sign>,
+ <single-quote>, <backslash>
+
+ex commands: undo, "z.", global, vglobal
+
+vi commands: (, ), {, }, %, [[, ]], ^]
+
+nvi adds the vi command ^T to this list.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set if the
+line changed:
+
+vi commands: '<mark>, G, H, L, M, z
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set if the
+line or column changed:
+
+vi commands: `<mark>, /, ?, N, n
+
+nvi adds the vi command ^A to this list.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+In historic vi, the previous context mark was set in non-visual
+mode for ^R and ^L if the line changed, but I have yet to figure
+out how the line could change.
diff --git a/usr.bin/vi/docs/internals/gdb.script b/usr.bin/vi/docs/internals/gdb.script
new file mode 100644
index 0000000..c875bf7
--- /dev/null
+++ b/usr.bin/vi/docs/internals/gdb.script
@@ -0,0 +1,77 @@
+# @(#)gdb.script 8.3 (Berkeley) 8/16/94
+
+# display the SVI screen map
+# usage dmap(sp)
+define dmap
+ set $h = ((SVI_PRIVATE *)$arg0->svi_private)->h_smap
+ set $t = ((SVI_PRIVATE *)$arg0->svi_private)->t_smap
+ while ($h <= $t)
+ printf "lno: %d; off %d ", (int)$h->lno, (int)$h->off
+ if ($h->c_ecsize == 0)
+ printf "flushed\n"
+ else
+ printf "\n\tsboff %d; scoff %d\n", \
+ (int)$h->c_sboff, (int)$h->c_scoff
+ printf "\teboff %d; eclen %d; ecsize %d\n", \
+ (int)$h->c_eboff, (int)$h->c_eclen, \
+ (int)$h->c_ecsize
+ end
+ set $h = $h + 1
+ end
+end
+
+# display the tail of the SVI screen map
+define tmap
+ set $h = ((SVI_PRIVATE *)$arg0->svi_private)->h_smap
+ set $t = ((SVI_PRIVATE *)$arg0->svi_private)->t_smap
+ while ($t >= $h)
+ printf "lno: %d; off %d ", (int)$t->lno, (int)$t->off
+ if ($t->c_ecsize == 0)
+ printf "flushed\n"
+ else
+ printf "\n\tsboff %d; scoff %d\n", \
+ (int)$t->c_sboff, (int)$t->c_scoff
+ printf "\teboff %d; eclen %d; ecsize %d\n", \
+ (int)$t->c_eboff, (int)$t->c_eclen, \
+ (int)$t->c_ecsize
+ end
+ set $t = $t - 1
+ end
+end
+
+# display the private structures
+define vip
+ print *((VI_PRIVATE *)sp->vi_private)
+end
+define svp
+ print *((SVI_PRIVATE *)sp->svi_private)
+end
+define exp
+ print *((EX_PRIVATE *)sp->ex_private)
+end
+define sxp
+ print *((SEX_PRIVATE *)sp->sex_private)
+end
+
+# display the marks
+define markp
+ set $h = sp->ep->marks.next
+ set $t = &sp->ep->marks
+ while ($h != 0 && $h != $t)
+ printf "key %c lno: %d cno: %d flags: %x\n", \
+ ((MARK *)$h)->name, ((MARK *)$h)->lno, \
+ ((MARK *)$h)->cno, ((MARK *)$h)->flags
+ set $h = ((MARK *)$h)->next
+ end
+end
+
+# display the tags
+define tagp
+ set $h = sp->taghdr.next
+ set $t = &sp->taghdr
+ while ($h != 0 && $h != $t)
+ printf "tag: %s lno %d cno %d\n", ((TAG *)$h)->frp->fname, \
+ ((TAG *)$h)->lno, ((TAG *)$h)->cno
+ set $h= ((TAG *)$h)->next
+ end
+end
diff --git a/usr.bin/vi/docs/internals/input b/usr.bin/vi/docs/internals/input
new file mode 100644
index 0000000..9a7506e
--- /dev/null
+++ b/usr.bin/vi/docs/internals/input
@@ -0,0 +1,350 @@
+# @(#)input 5.5 (Berkeley) 7/2/94
+
+MAPS, EXECUTABLE BUFFERS AND INPUT IN EX/VI:
+
+The basic rule is that input in ex/vi is a stack. Every time a key which
+gets expanded is encountered, it is expanded and the expansion is treated
+as if it were input from the user. So, maps and executable buffers are
+simply pushed onto the stack from which keys are returned. The exception
+is that if the "remap" option is turned off, only a single map expansion
+is done. I intend to be fully backward compatible with this.
+
+Historically, if the mode of the editor changed (ex to vi or vice versa),
+any queued input was silently discarded. I don't see any reason to either
+support or not support this semantic. I intend to retain the queued input,
+mostly because it's simpler than throwing it away.
+
+Historically, neither the initial command on the command line (the + flag)
+or the +cmd associated with the ex and edit commands was subject to mapping.
+Also, while the +cmd appears to be subject to "@buffer" expansion, once
+expanded it doesn't appear to work correctly. I don't see any reason to
+either support or not support these semantics, so, for consistency, I intend
+to pass both the initial command and the command associated with ex and edit
+commands through the standard mapping and @ buffer expansion.
+
+One other difference between the historic ex/vi and nex/nvi is that nex
+displays the executed buffers as it executes them. This means that if
+the file is:
+
+ set term=xterm
+ set term=yterm
+ set term=yterm
+
+the user will see the following during a typical edit session:
+
+ nex testfile
+ testfile: unmodified: line 3
+ :1,$yank a
+ :@a
+ :set term=zterm
+ :set term=yterm
+ :set term=xterm
+ :q!
+
+This seems like a feature and unlikely to break anything, so I don't
+intend to match historic practice in this area.
+
+The rest of this document is a set of conclusions as to how I believe
+the historic maps and @ buffers work. The summary is as follows:
+
+1: For buffers that are cut in "line mode", or buffers that are not cut
+ in line mode but which contain portions of more than a single line, a
+ trailing <newline> character appears in the input for each line in the
+ buffer when it is executed. For buffers not cut in line mode and which
+ contain portions of only a single line, no additional characters
+ appear in the input.
+2: Executable buffers that execute other buffers don't load their
+ contents until they execute them.
+3: Maps and executable buffers are copied when they are executed --
+ they can be modified by the command but that does not change their
+ actions.
+4: Historically, executable buffers are discarded if the editor
+ switches between ex and vi modes.
+5: Executable buffers inside of map commands are expanded normally.
+ Maps inside of executable buffers are expanded normally.
+6: If an error is encountered while executing a mapped command or buffer,
+ the rest of the mapped command/buffer is discarded. No user input
+ characters are discarded.
+7: Characters in executable buffers are remapped.
+8: Characters in executable buffers are not quoted.
+
+Individual test cases follow. Note, in the test cases, control characters
+are not literal and will have to be replaced to make the test cases work.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+1: For buffers that are cut in "line mode", or buffers that are not cut
+ in line mode but which contain portions of more than a single line, a
+ trailing <newline> character appears in the input for each line in the
+ buffer when it is executed. For buffers not cut in line mode and which
+ contain portions of only a single line, no additional characters
+ appear in the input.
+
+=== test file ===
+3Gw
+w
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+=== end test file ===
+
+ If the first line is loaded into 'a' and executed:
+
+1G"ayy@a
+
+ The cursor ends up on the '2', a result of pushing "3Gw^J" onto
+ the stack.
+
+ If the first two lines are loaded into 'a' and executed:
+
+1G2"ayy@a
+
+ The cursor ends up on the 'f' in "foo" in the fifth line of the
+ file, a result of pushing "3Gw^Jw^J" onto the stack.
+
+ If the first line is loaded into 'a', but not using line mode,
+ and executed:
+
+1G"ay$@a
+
+ The cursor ends up on the '1', a result of pushing "3Gw" onto
+ the stack
+
+ If the first two lines are loaded into 'a', but not using line mode,
+ and executed:
+
+1G2"ay$@a
+
+ The cursor ends up on the 'f' in "foo" in the fifth line of the
+ file, a result of pushing "3Gw^Jw^J" onto the stack.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+2: Executable buffers that execute other buffers don't load their
+ contents until they execute them.
+
+=== test file ===
+cwLOAD B^[
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"byy
+=== end test file ===
+
+ The command is loaded into 'e', and then executed. 'e' executes
+ 'a', which loads 'b', then 'e' executes 'b'.
+
+5G"eyy6G"ayy1G@e
+
+ The output should be:
+
+=== output file ===
+cwLOAD B^[
+LOAD B 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"byy
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+3: Maps and executable buffers are copied when they are executed --
+ they can be modified by the command but that does not change their
+ actions.
+
+ Executable buffers:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"eyy
+cwEXECUTE B^[
+=== end test file ===
+
+4G"eyy5G"ayy6G"byy1G@eG"ep
+
+ The command is loaded into 'e', and then executed. 'e' executes
+ 'a', which loads 'e', then 'e' executes 'b' anyway.
+
+ The output should be:
+
+=== output file ===
+line 1 foo bar baz
+EXECUTE B 2 foo bar baz
+line 3 foo bar baz
+@a@b
+"eyy
+cwEXECUTE B^[
+line 1 foo bar baz
+=== end output file ===
+
+ Maps:
+
+=== test file ===
+Cine 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+=== end test file ===
+
+ Entering the command ':map = :map = rB^V^MrA^M1G==' shows that
+ the first time the '=' is entered the '=' map is set and the
+ character is changed to 'A', the second time the character is
+ changed to 'B'.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+4: Historically, executable buffers are discarded if the editor
+ switches between ex and vi modes.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwCHANGE^[Q:set
+set|visual|1Gwww
+=== end test file ===
+
+vi testfile
+4G"ayy@a
+
+ex testfile
+$p
+yank a
+@a
+
+ In vi, the command is loaded into 'a' and then executed. The command
+ subsequent to the 'Q' is (historically, silently) discarded.
+
+ In ex, the command is loaded into 'a' and then executed. The command
+ subsequent to the 'visual' is (historically, silently) discarded. The
+ first set command is output by ex, although refreshing the screen usually
+ causes it not to be seen.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+5: Executable buffers inside of map commands are expanded normally.
+ Maps inside of executable buffers are expanded normally.
+
+ Buffers inside of map commands:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwREPLACE BY A^[
+=== end test file ===
+
+4G"ay$:map x @a
+1Gx
+
+ The output should be:
+
+=== output file ===
+REPLACE BY A 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+cwREPLACE BY A^[
+=== end output file ===
+
+ Maps commands inside of executable buffers:
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+=== end test file ===
+
+:map X cwREPLACE BY XMAP^[
+4G"ay$1G@a
+
+ The output should be:
+
+=== output file ===
+REPLACE BY XMAP 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+=== end output file ===
+
+ Here's a test that does both, repeatedly.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+Y
+cwREPLACED BY C^[
+blank line
+=== end test file ===
+
+:map x @a
+4G"ay$
+:map X @b
+5G"by$
+:map Y @c
+6G"cy$
+1Gx
+
+ The output should be:
+
+=== output file ===
+REPLACED BY C 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+X
+Y
+cwREPLACED BY C^[
+blank line
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+6: If an error is encountered while executing a mapped command or
+ a buffer, the rest of the mapped command/buffer is discarded. No
+ user input characters are discarded.
+
+=== test file ===
+line 1 foo bar baz
+line 2 foo bar baz
+line 3 foo bar baz
+:map = 10GcwREPLACMENT^V^[^[
+=== end test file ===
+
+ The above mapping fails, however, if the 10G is changed to 1, 2,
+ or 3G, it will succeed.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+7: Characters in executable buffers are remapped.
+
+=== test file ===
+abcdefghijklmnnop
+ggg
+=== end test file ===
+
+:map g x
+2G"ay$1G@a
+
+ The output should be:
+
+=== output file ===
+defghijklmnnop
+ggg
+=== end output file ===
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+8: Characters in executable buffers are not quoted.
+
+=== test file ===
+iFOO^[
+
+=== end test file ===
+
+1G"ay$2G@a
+
+ The output should be:
+
+=== output file ===
+iFOO^[
+FOO
+=== end output file ===
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
diff --git a/usr.bin/vi/docs/internals/quoting b/usr.bin/vi/docs/internals/quoting
new file mode 100644
index 0000000..f20bd3f
--- /dev/null
+++ b/usr.bin/vi/docs/internals/quoting
@@ -0,0 +1,219 @@
+# @(#)quoting 5.4 (Berkeley) 8/20/93
+
+QUOTING IN EX/VI:
+
+There are two escape characters in historic ex/vi, ^V (or whatever
+character the user specified as their literal next character) and
+backslashes. There are two different areas in ex/vi where escaping
+is interesting: the command and text input modes, and within the ex
+commands themselves. In the examples below, ^V is used as the
+typical literal next character.
+
+1: Escaping characters in ex and vi command and text input modes.
+ The set of characters that users might want to escape are as
+ follows:
+
+ vi text input mode (a, i, o, etc.):
+
+ carriage return (^M)
+ escape (^[)
+ autoindent characters
+ (^D, 0, ^, ^T)
+ erase, word erase, and line erase
+ (^H, ^W, ^U)
+ newline (^J) (not historic practice)
+ suspend (^Z) (not historic practice)
+ repaint (^L) (not historic practice)
+
+ vi command line (:colon commands):
+
+ carriage return (^M)
+ escape (^[)
+ erase, word erase, and line erase
+ (^H, ^W, ^U)
+ newline (^J) (not historic practice)
+ suspend (^Z) (not historic practice)
+ repaint (^L) (not historic practice)
+
+ ex text input mode (a, i, o, etc.):
+
+ carriage return (^M)
+ erase, word erase, and line erase
+ (^H, ^W, ^U)
+ newline (^J) (not historic practice)
+
+ ex command line:
+
+ carriage return (^M)
+ erase, word erase, and line erase
+ (^H, ^W, ^U)
+ newline (^J) (not historic practice)
+ suspend (^Z)
+
+ I intend to follow historic practice for all of these cases, which
+ was that ^V was the only way to escape any of these characters, and
+ that whatever character followed the ^V was taken literally, i.e.
+ ^V^V is a single ^V.
+
+ The historic ex/vi disallowed the insertion of various control
+ characters (^D, ^T, whatever) during various different modes, or,
+ permitted the insertion of only a single one, or lots of other random
+ behaviors (you can use ^D to enter a command in ex). I have
+ regularized this behavior in nvi, there are no characters that cannot
+ be entered or which have special meaning other than the ones listed
+ above.
+
+ One comment regarding the autoindent characters. In historic vi,
+ if you entered "^V0^D" autoindent erasure was still triggered,
+ although it wasn't if you entered "0^V^D". In nvi, if you escape
+ either character, autoindent erasure is not triggered.
+
+ This doesn't permit whitespace in command names, but that wasn't
+ historic practice and doesn't seem worth doing.
+
+ Fun facts to know and tell:
+ The historic vi implementation for the 'r' command requires
+ *three* ^V's to replace a single character with ^V.
+
+2: Ex commands:
+
+ Ex commands are delimited by '|' or newline characters. Within
+ the commands, whitespace characters delimit the arguments.
+
+ I intend to treat ^V, followed by any character, as that literal
+ character.
+
+ This is historic behavior in vi, although there are special
+ cases where it's impossible to escape a character, generally
+ a whitespace character.
+
+3: Escaping characters in file names in ex commands:
+
+ :cd [directory] (directory)
+ :chdir [directory] (directory)
+ :edit [+cmd] [file] (file)
+ :ex [+cmd] [file] (file)
+ :file [file] (file)
+ :next [file ...] (file ...)
+ :read [!cmd | file] (file)
+ :source [file] (file)
+ :write [!cmd | file] (file)
+ :wq [file] (file)
+ :xit [file] (file)
+
+ I intend to treat a backslash in a file name, followed by any
+ character, as that literal character.
+
+ This is historic behavior in vi.
+
+ In addition, since file names are also subject to word expansion,
+ the rules for escape characters in section 3 of this document also
+ apply. This is NOT historic behavior in vi, making it impossible
+ to insert a whitespace, newline or carriage return character into
+ a file name. This change could cause a problem if there were files
+ with ^V's in their names, but I think that's unlikely.
+
+4: Escaping characters in non-file arguments in ex commands:
+
+ :abbreviate word string (word, string)
+* :edit [+cmd] [file] (+cmd)
+* :ex [+cmd] [file] (+cmd)
+ :k key (key)
+ :map word string (word, string)
+ :mark key (key)
+* :set [option ...] (option)
+* :tag string (string)
+ :unabbreviate word (word)
+ :unmap word (word)
+
+ These commands use whitespace to delimit their arguments, and use
+ ^V to escape those characters. The exceptions are starred in the
+ above list, and are discussed below.
+
+ In general, I intend to treat a ^V in any argument, followed by
+ any character, as that literal character. This will permit
+ editing of files name "foo|", for example, by using the string
+ "foo\^V|", where the literal next character protects the pipe
+ from the ex command parser and the backslash protects it from the
+ shell expansion.
+
+ This is backward compatible with historical vi, although there
+ were a number of special cases where vi wasn't consistent.
+
+4.1: The edit/ex commands:
+
+ The edit/ex commands are a special case because | symbols may
+ occur in the "+cmd" field, for example:
+
+ :edit +10|s/abc/ABC/ file.c
+
+ In addition, the edit and ex commands have historically ignored
+ literal next characters in the +cmd string, so that the following
+ command won't work.
+
+ :edit +10|s/X/^V / file.c
+
+ I intend to handle the literal next character in edit/ex consistently
+ with how it is handled in other commands.
+
+ More fun facts to know and tell:
+ The acid test for the ex/edit commands:
+
+ date > file1; date > file2
+ vi
+ :edit +1|s/./XXX/|w file1| e file2|1 | s/./XXX/|wq
+
+ No version of vi, of which I'm aware, handles it.
+
+4.2: The set command:
+
+ The set command treats ^V's as literal characters, so the following
+ command won't work. Backslashes do work in this case, though, so
+ the second version of the command does work.
+
+ set tags=tags_file1^V tags_file2
+ set tags=tags_file1\ tags_file2
+
+ I intend to continue permitting backslashes in set commands, but
+ to also permit literal next characters to work as well. This is
+ backward compatible, but will also make set consistent with the
+ other commands. I think it's unlikely to break any historic
+ .exrc's, given that there are probably very few files with ^V's
+ in their name.
+
+4.3: The tag command:
+
+ The tag command ignores ^V's and backslashes; there's no way to
+ get a space into a tag name.
+
+ I think this is a don't care, and I don't intend to fix it.
+
+5: Regular expressions:
+
+ :global /pattern/ command
+ :substitute /pattern/replace/
+ :vglobal /pattern/ command
+
+ I intend to treat a backslash in the pattern, followed by the
+ delimiter character or a backslash, as that literal character.
+
+ This is historic behavior in vi. It would get rid of a fairly
+ hard-to-explain special case if we could just use the character
+ immediately following the backslash in all cases, or, if we
+ changed nvi to permit using the literal next character as a
+ pattern escape character, but that would probably break historic
+ scripts.
+
+ There is an additional escaping issue for regular expressions.
+ Within the pattern and replacement, the '|' character did not
+ delimit ex commands. For example, the following is legal.
+
+ :substitute /|/PIPE/|s/P/XXX/
+
+ This is a special case that I will support.
+
+6: Ending anything with an escape character:
+
+ In all of the above rules, an escape character (either ^V or a
+ backslash) at the end of an argument or file name is not handled
+ specially, but used as a literal character.
diff --git a/usr.bin/vi/docs/internals/structures b/usr.bin/vi/docs/internals/structures
new file mode 100644
index 0000000..d49ab65
--- /dev/null
+++ b/usr.bin/vi/docs/internals/structures
@@ -0,0 +1,61 @@
+# @(#)structures 5.2 (Berkeley) 11/1/93
+
+There are three major data structures in this package. The first is a
+single global structure (named GS) which contains information common to
+all files and screens. It's really pretty tiny, and functions more as a
+single place to hang things than anything else.
+
+The second and third structures are the file structures (named EXF) and
+the screen structures (named SCR). They contain information theoretically
+unique to a screen or file, respectively. Each SCR structure has a set
+of functions which update the screen and/or return information about the
+screen from the underlying screen package.
+
+The GS structure contains linked lists SCR structures. The structures
+can also be classed by persistence. The GS structure never goes away
+and the SCR structure persists over instances of files.
+
+File names have different properties than files themselves, so the name
+information for a file is held in an FREF structure which is chained from
+the SCR structure.
+
+In general, functions are always passed an SCR structure and often an EXF
+structure as well. The SCR structure is necessary for any routine that
+wishes to talk to the screen, the EXF structure is necessary for any
+routine that wants to modify the file. The relationship between an SCR
+structure and its underlying EXF structure is not fixed, and although you
+can translate from an SCR to the underlying EXF, it is discouraged. If
+this becomes too onerous, I suspect I'll just stop passing around the EXF
+in the future.
+
+The naming of the structures is consistent across the program. (Macros
+even depend on it, so don't try and change it!) The global structure is
+"gp", the screen structure is "sp", and the file structure is "ep".
+
+A few other data structures:
+
+TEXT In nvi/cut.h. This structure describes a portion of a line,
+ and is used by the input routines and as the "line" part of a
+ cut buffer.
+
+CB In nvi/cut.h. A cut buffer. A cut buffer is a place to
+ hang a list of TEXT structures.
+
+MARK In nvi/mark.h. A cursor position, consisting of a line number
+ and a column number.
+
+MSG In nvi/msg.h. A chain of messages for the user.
+
+SEQ In nvi/seq.h. An abbreviation or a map entry.
+
+EXCMDARG
+ In nvi/ex/excmd.h.stub. The structure that gets passed around
+ to the functions that implement the ex commands. (The main
+ ex command loop (see nvi/ex/ex.c) builds this up and then passes
+ it to the ex functions.)
+
+VICMDARG
+ In nvi/vi/vcmd.h. The structure that gets passed around to the
+ functions that implement the vi commands. (The main vi command
+ loop (see nvi/vi/vi.c) builds this up and then passes it to the
+ vi functions.)
diff --git a/usr.bin/vi/docs/tutorial/vi.advanced b/usr.bin/vi/docs/tutorial/vi.advanced
new file mode 100644
index 0000000..f757ad1
--- /dev/null
+++ b/usr.bin/vi/docs/tutorial/vi.advanced
@@ -0,0 +1,1458 @@
+Section 26: Index to the rest of the tutorial
+
+The remainder of the tutorial can be perused at your leisure. Simply find the
+topic of interest in the following list, and {/Section xx:/^M} to get to the
+appropriate section. (Remember that ^M means the return key)
+
+The material in the following sections is not necessarily in a bottom up
+order. It should be fairly obvious that if a section mentions something with
+which you are not familiar, say, buffers, you might {/buffer/^M} followed by
+several {n} to do a keyword search of the file for more details on that item.
+Another point to remember is that commands are surrounded by curly-braces and
+can therefore be found rather easily. To see where, say, the X command is
+used try {/{X}/^M}. Subsequent {n} will show you other places the command was
+used. We have tried to maintain the convention of placing the command letter
+surrounded by curly-braces on the section line where that command is
+mentioned.
+
+Finally, you should have enough 'savvy' at this point to be able to do your
+own experimentation with commands without too much hand-holding on the part of
+the tutorial. Experimentation is the best way to learn the effects of the
+commands.
+
+ Section Topic - description
+ ------- -------------------
+(Sections 1 through 25 are located in the file vi.beginner.)
+ 1 introduction: {^F} {ZZ}
+ 2 introduction (con't) and positioning: {^F} {^B}
+ 3 introduction (con't) and positioning: {^F} {^B}
+ 4 positioning: {^F} {^B} ^M (return key)
+ 5 quitting: {:q!} ^M key
+ 6 marking, cursor and screen positioning: {m} {G} {'} {z}
+ 7 marking, cursor and screen positioning: {m} {G} {'} {z}
+ 8 marking, cursor and screen positioning: {z} {m} {'}
+ 9 marking and positioning: {m} {''}
+ 10 line positioning: {^M} {-}
+ 11 scrolling with {^M}
+ 12 scrolling with {-} and screen adjustment {z}
+ 13 notes on use of tutorial
+ 14 other scrolling and postioning commands: {^E} {^Y} {^D} {^U}
+ 15 searching: {/ .. /^M}
+ 16 searching: {? .. ?^M} {n} (in search strings ^ $)
+ 17 searching: \ and magic-characters in search strings
+ 18 colon commands, exiting: {:} {ZZ}
+ 19 screen positioning: {H} {M} {L}
+ 20 character positioning: {w} {b} {0} {W} {B} {e} {E} {'} {`}
+ 21 cursor positioning: {l} {k} {j} {h}
+ 22 adding text: {i} {a} {I} {A} {o} {O} ^[ (escape key)
+ 23 character manipulation: {f} {x} {X} {w} {l} {r} {R} {s} {S} {J}
+ 24 undo: {u} {U}
+ 25 review
+(The following sections are in this file.)
+ 26 Index to the rest of the tutorial ******** YOU ARE HERE *******
+ 27 discussion of repeat counts and the repeat command: {.}
+ 28 more on low-level character motions: {t} {T} {|}
+ 29 advanced correction operators: {d} {c}
+ 30 updating the screen: {^R}
+ 31 text buffers: {"}
+ 32 rearranging and duplicating text: {p} {P} {y} {Y}
+ 33 recovering lost lines
+ 34 advanced file manipulation with vi
+ 34.1 more than one file at a time: {:n}
+ 34.2 reading files and command output: {:r}
+ 34.3 invoking vi from within vi: {:e} {:vi}
+ 34.4 escaping to a shell: {:sh} {:!}
+ 34.5 writing parts of a file: {:w}
+ 34.6 filtering portions of text: {!}
+ 35 advanced searching: magic patterns
+ 36 advanced substitution: {:s}
+ 37 advanced line addressing: {:p} {:g} {:v}
+ 38 higher level text objects and nroff: ( ) { } [[ ]]
+ 39 more about inserting text
+ 40 more on operators: {d} {c} {<} {>} {!} {=} {y}
+ 41 abbreviations: {:ab}
+ 42 vi's relationship with the ex editor: {:}
+ 43 vi on hardcopy terminals and dumb terminals: open mode
+ 44 options: {:set} {setenv EXINIT}
+ 44.1 autoindent
+ 44.2 autoprint
+ 44.3 autowrite
+ 44.4 beautify
+ 44.5 directory
+ 44.6 edcompatible
+ 44.7 errorbells
+ 44.8 hardtabs
+ 44.9 ignorecase
+ 44.10 lisp
+ 44.11 list
+ 44.12 magic
+ 44.13 mesg
+ 44.14 number
+ 44.15 open
+ 44.16 optimize
+ 44.17 paragraphs
+ 44.18 prompt
+ 44.19 readonly
+ 44.20 redraw
+ 44.21 remap
+ 44.22 report
+ 44.23 scroll
+ 44.24 sections
+ 44.25 shell
+ 44.26 shiftwidth
+ 44.27 showmatch
+ 44.28 slowopen
+ 44.29 tabstop
+ 44.30 tags
+ 44.31 taglength
+ 44.32 term
+ 44.33 terse
+ 44.34 timeout
+ 44.35 ttytype
+ 44.36 warn
+ 44.37 window
+ 44.38 wrapscan
+ 44.39 wrapmargin
+ 44.40 writeany
+ 44.41 w300, w1200, w9600
+
+Section 27: repetition counts and the repeat command {.}
+
+Most vi commands will use a preceding count to affect their behavior in some
+way. We have already seen how {3x} deletes three characters, and {22G} moves
+us to line 22 of the file. For almost all of the commands, one can survive by
+thinking of these leading numbers as a 'repeat count' specifying that the
+command is to be repeated so many number of times.
+
+Other commands use the repeat count slightly differently, like the {G} command
+which use it as a line number.
+
+For example:
+
+{3^D} means scroll down in the file three lines. Subsequent {^D} OR {^U} will
+scroll only three lines in their respective directions!
+
+{3z^M} says put line three of the file at the top of the screen, while {3z.}
+says put line three as close to the middle of the screen as possible.
+
+{50|} moves the cursor to column fifty in the current line.
+
+{3^F} says move forward 3 screenfulls. This is a repetition count. The
+documents advertise that {3^B} should move BACK three screenfulls, but I
+can't get it to work.
+
+Position the cursor on some text and try {3r.}. This replaces three characters
+with '...'. However, {3s.....^[} is the same as {3xi.....^[}.
+
+Try {10a+----^[}.
+
+A very useful instance of a repetition count is one given to the '.' command,
+which repeats the last 'change' command. If you {dw} and then {3.}, you will
+delete first one and then three words. You can then delete two more words with
+{2.}. If you {3dw}, you will delete three words. A subsequent {.} will delete
+three more words. But a subsequent {2.} will delete only two words, not three
+times two words.
+
+Caveat: The author has noticed that any repetition count with {^B} will NOT
+work: indeed, if you are at the end of your file and try {3^B} sufficiently
+often, the editor will hang you in an infinite loop. Please don't try it:
+take my word for it.
+
+Section 28: {t} {T} {|}
+
+Position the cursor on line 13 below:
+
+Line 13: Four score and seven years ago, our forefathers brought ...
+
+Note that {fv} moves the cursor on/over the 'v' in 'seven'. Do a {0} to return
+to the beginning of the line and try a {tv}. The cursor is now on/over the
+first 'e' in 'seven'. The {f} command finds the next occurrence of the
+specified letter and moves the cursor to it. The {t} command finds the
+specified letter and moves the cursor to the character immediately preceding
+it. {T} searches backwards, as does {F}.
+
+Now try {60|}: the cursor is now on the 'o' in 'brought', which is the
+sixtieth character on the line.
+
+Section 29: {d} {c}
+
+Due to their complexity we have delayed discussion of two of the most powerful
+operators in vi until now. Effective use of these operators requires more
+explanation than was deemed appropriate for the first half of the tutorial.
+
+{d} and {c} are called operators instead of commands because they consist of
+three parts: a count specification or a buffer specification (see section
+#BUFFERS), the {d} or {c}, and the object or range description. We will not
+discuss buffers at this stage, but will limit ourselves to count
+specifications. Examples speak louder than words: position the cursor at the
+beginning of line 14:
+
+Line 14: Euclid alone has looked on beauty bear.
+
+Obviously, there is something wrong with this quotation. Type {2fb} to
+position the cursor on the 'b' of 'bear'. Now, type {cwbare^[}
+and observe the results. The {cw} specifies that the change command {c} is to
+operate on a word object. More accurately, it specifies that the range of the
+change command includes the next word.
+
+Position the cursor on the period in Line 14. (one way is to use {f.})
+Now, type {cbbeast^[}. This specifies the range of the change command to be the
+previous word (the 'b' reminiscent of the {b} command). If we had wished to
+delete the word rather than change it, we would have used the {d} operator,
+rather than the {c} operator.
+
+Position the cursor at the beginning of the line with {0}. Type
+{d/look/^M}. The search string specified the range of the delete.
+Everything UP TO the word 'looking' was deleted from the line.
+
+In general, almost any command that would move the cursor will specify a range
+for these commands. The most confusing exception to this rule is when {dd} or
+{cc} is entered: they refer to the whole line. Following is a summary of the
+suffixes (suffices? suffici?) and the ranges they specify:
+
+ suffix will delete{d}/change{c}
+ ------ ------------------------
+ ^[ cancels the command
+ w the word to the right of the cursor
+ W ditto, but ignoring punctuation
+ b the word to the left of the cursor
+ B ditto, but ignoring punctuation
+ e see below.
+ E ditto
+ (space) a character
+ $ to the end of the line
+ ^ to the beginning of the line
+ / .. / up to, but not including, the string
+ ? .. ? back to and including the string
+ fc up to and including the occurrence of c
+ Fc back to and including the occurrence of c
+ tc up to but not including the occurrence of c
+ Tc back to but not including the occurrence of c
+ ^M TWO lines (that's right: two)
+ (number)^M that many lines plus one
+ (number)G up to and including line (number)
+ ( the previous sentence if you are at the beginning of
+ the current sentence, or the current sentence up to where
+ you are if you are not at the beginning of the current
+ sentence. Here, 'sentence' refers to the intuitive
+ notion of an English sentence, ending with '!', '?',
+ or '.' and followed by an end of line or two spaces.
+ ) the rest of the current sentence
+ { analogous to '(', but in reference to paragraphs:
+ sections of text surrounded by blank lines
+ } analogous to ')', but in reference to paragraphs
+ [[ analogous to '(', but in reference to sections
+ ]] analogous to ')', but in reference to sections
+ H the first line on the screen
+ M the middle line on the screen
+ L the last line on the screen
+ 3L through the third line from the bottom of the screen
+ ^F forward a screenful
+ ^B backward a screenful
+ :
+ : etc. etc. etc.
+
+This list is not exhaustive, but it should be sufficient to get the idea
+across: after the {c} or {d} operator, you can specify a range with another
+move-the-cursor command, and that is the region of text over which the command
+will be effective.
+
+Section 30: updating the screen {^R}
+
+Vi tries to be very intelligent about the type of terminal you are working on
+and tries to use the in-terminal computing power (if any) of your terminal.
+Also if the terminal is running at a low baud rate (say 1200 or below), vi sets
+various parameters to make things easier for you. For example, if you were
+running on a 300 baud terminal (that's 30 characters per second transmission
+rate) not all 24 lines of the screen would be used by vi. In addition, there
+is a large portion of the editor keeping track of what your screen currently
+looks like, and what it would look like after a command has been executed. Vi
+then compares the two, and updates only those portions of the screen that have
+changed.
+
+Furthermore, some of you may have noticed (it depends on your terminal) that
+deleting lines or changing large portions of text may leave some lines on the
+screen looking like:
+@
+meaning that this line of the screen does not correspond to any line in your
+file. It would cost more to update the line than to leave it blank for the
+moment. If you would like to see your screen fully up-to-date with the
+contents of your file, type {^R}.
+
+To see it in action, delete several lines with {5dd}, type {^R}, and then type
+{u} to get the lines back.
+
+Here is as good a place as any to mention that if the editor is displaying the
+end of your file, there may be lines on the screen that look like:
+~
+indicating that that screen line would not be affected by {^R}. These lines
+simply indicate the end of the file.
+
+Section 31: text buffers {"}
+
+Vi gives you the ability to store text away in "buffers". This feature is very
+convenient for moving text around in your file. There are a total of thirty-
+five buffers available in vi. There is the "unnamed" buffer that is used by all
+commands that delete text, including the change operator {c}, the substitute
+and replace commands {s} and {r}, as well as the delete operator {d} and delete
+commands {x} and {X}. This buffer is filled each time any of these commands
+are used. However, the undo command {u} has no effect on the unnamed buffer.
+
+There are twenty-six buffers named 'a' through 'z' which are available for the
+user. If the name of the buffer is capitalized, then the buffer is not
+overwritten but appended to. For example, the command {"qdd} will delete one
+line and store that line in the 'q' buffer, destroying the previous contents of
+the buffer. However, {"Qdd} will delete one line of text and append that line
+to the current contents of the 'q' buffer.
+
+Finally, there are nine buffers named '1' through '9' in which the last nine
+deletes are stored. Buffer 1 is the default buffer for the modify commands and
+is sometimes called the unnamed buffer.
+
+To reference a specific buffer, use the double-quote command {"} followed by
+the name of the buffer. The next two sections show how buffers can be used to
+advantage.
+
+Section 32: rearranging and duplicating text: {y} {Y} {p} {P}
+
+Position yourself on line 15 below and {z^M}:
+
+Line 15: A tree as lovely as a poem ...
+Line 16: I think that I shall never see
+
+Type {dd}. Line 15 has disappeared and been replaced with the empty line (one
+with the single character @ on it) or (again depending on your terminal) Line
+16 has moved up and taken its place. We could recover Line 15 with an undo
+{u} but that would simply return it to its original location. Obviously, the
+two lines are reversed, so we want to put line 15 AFTER line 16. This is
+simply done with the put command {p}, which you should type now. What has
+happened is that {dd} put Line 15 into the unnamed buffer, and the {p} command
+retrieved the line from the unnamed buffer.
+
+Now type {u} and observe that Line 15 disappears again (the put was undone
+without affecting the unnamed buffer). Type {P} and see that the capital {P}
+puts the line BEFORE the cursor.
+
+To get Line 15 where it belongs again type {dd}{p}.
+
+Also in Line 15 note that the words 'tree' and 'poem' are reversed. Using the
+unnamed buffer again: {ft}{dw}{ma}{fp}{P}{w}{dw}{`aP} will set things aright
+(note the use of the reverse quote).
+
+The put commands {p} and {P} do not affect the contents of the buffer.
+Therefore, multiple {p} or {P} will put multiple copies of the unnamed buffer
+into your file.
+
+Experiment with {d} and {p} on words, paragraphs, etc. Whatever {d}
+deletes, {p} can put.
+
+Position the cursor on Line 17 and {z^M}:
+
+Line 17: interest apple cat elephant boy dog girl hay farmer
+
+Our task is to alphabetize the words on line 17. With the named buffers (and a
+contrived example) it is quite easy:
+
+{"idw}{"adw}{"cdw}{"edw}{"bdw}{"ddw}{"gdw}{"hdw}{"fdw}
+
+stores each of the words in the named buffer corresponding to the first letter
+of each of the words ('interest' goes in buffer "i, 'apple' goes in buffer "a,
+etc.). Now to put the words in order type:
+
+{"ap$}{"bp$}{"cp$}{"dp$}{"ep$}{"fp$}{"gp$}{"hp$}{"ip$}
+
+Notice that, because 'farmer' was at the end of the line, {dw} did not include
+a space after it, and that, therefore, there is no space between 'farmer' and
+'girl'. This is corrected with {Fg}{i ^[}.
+
+This example could have been done just as easily with lines as with
+words.
+
+You do not have to delete the text in order to put it into a buffer. If all
+you wish to do is to copy the text somewhere else, don't use {d}, rather use
+the yank commands {y} or {Y}. {y} is like {d} and {c} - an operator rather
+than a command. It, too, takes a buffer specification and a range
+specification. Therefore, instead of {dw}{P} to load the unnamed buffer with a
+word without deleting the word, use {yw} (yank a word).
+
+{Y} is designed yank lines, and not arbitrary ranges. That is, {Y} is
+equivalent to {yy} (remember that operators doubled means the current line),
+and {3Y} is equivalent to {3yy}.
+
+If the text you yank or modify forms a part of a line, or is an object such as
+a sentence which partially spans more than one line, then when you put the text
+back, it will be placed after the cursor (or before if you use {P}). If the
+yanked text forms whole lines, they will be put back as whole lines, without
+changing the current line. In this case, the put acts much like the {o} or {O}
+command.
+
+The named buffers "a through "z are not affected by changing edit files.
+However, the unnamed buffer is lost when you change files, so to move text from
+one file to another you should use a named buffer.
+
+Section 33: recovering lost lines
+
+Vi also keeps track of the last nine deletes, whether you ask for it or not.
+This is very convenient if you would like to recover some text that was
+accidentally deleted or modified. Position the cursor on line 18 following,
+and {z^M}.
+
+
+Line 18: line 1
+Line 19: line 2
+Line 20: line 3
+Line 21: line 4
+Line 22: line 5
+Line 23: line 6
+Line 24: line 7
+Line 25: line 8
+Line 26: line 9
+Type {dd} nine times: now don't cheat with {9dd}! That is totally different.
+
+The command {"1p} will retrieve the last delete. Furthermore, when the
+numbered buffers are used, the repeat-command command {.} will increment the
+buffer numbers before executing, so that subsequent {.} will recover all nine
+of the deleted lines, albeit in reverse order. If you would like to review the
+last nine deletes without affecting the buffers or your file, do an undo {u}
+after each put {p} and {.}:
+
+{"1p}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}{u}{.}
+
+will show you all the buffers and leave them and your file intact.
+
+If you had cheated above and deleted the nine lines with {9dd}, all nine lines
+would have been stored in both the unnamed buffer and in buffer number 1.
+(Obviously, buffer number 1 IS the unnamed buffer and is just the default
+buffer for the modify commands.)
+
+Section 34: advanced file manipulation: {:r} {:e} {:n} {:w} {!} {:!}
+
+We've already looked at writing out the file you are editing with the
+{:w} command. Now let's look at some other vi commands to make editing
+more efficient.
+
+Section 34.1: more than one file at a time {:n} {:args}
+
+Many times you will want to edit more than one file in an editing session.
+Instead of entering vi and editing the first file, exiting, entering vi and
+editing the second, etc., vi will allow you to specify ALL files that you wish
+to edit on the invocation line. Therefore, if you wanted to edit file1 and
+file2:
+
+% vi file1 file2
+
+will set up file1 for editing. When you are done editing file one, write it
+out {:w^M} and then type {:n^M} to get the next file on the list. On large
+programming projects with many source files, it is often convenient just to
+specify all source files with, say:
+
+% vi *.c
+
+If {:n^M} brings in a file that does not need any editing, another {:n^M}
+will bring in the next file.
+
+If you have made changes to the first file, but decide to discard these changes
+and proceed to the next file, {:n!^M} forces the editor to discard the current
+contents of the editor.
+
+You can specify a new list of files after {:n}; e.g., {:n f1 f2 f3^M}. This
+will replace the current list of files (if any).
+
+You can see the current list of files being edited with {:args^M}.
+
+Section 34.2: reading files and command output: {:r}
+
+Typing {:r fname^M} will read the contents of file fname into the editor and
+put the contents AFTER the cursor line.
+
+Typing {:r !cmd^M} will read the output of the command cmd and place that
+output after the cursor line.
+
+Section 34.3: invoking vi from within vi: {:e} {:vi}
+
+To edit another file not mentioned on the invocation line, type {:e filename^M}
+or {:vi filename^M}. If you wish to discard the changes to the current file,
+use the exclamation point after the command, e.g. {:e! filename^M}.
+
+Section 34.4: escaping to a shell: {:sh} {:!} {^Z}
+
+Occasionally, it is useful to interrupt the current editing session to perform
+a UNIX task. However, there is no need to write the current file out, exit
+the editor, perform the task, and then reinvoke the editor on the same file.
+One thing to do is to spin off another process. If there are several UNIX
+commands you will need to execute, simply create another shell with {:sh^M}.
+At this point, the editor is put to sleep and will be reawakened when you log
+out of the shell.
+
+If it is a single command that you want to execute, type {:!cmd^M}, where cmd
+is the command that you wish to run. The output of the command will come to
+the terminal as normal, and will not be made part of your file. The message
+"[Hit return to continue]" will be displayed by vi after the command is
+finished. Hitting return will then repaint the screen. Typing another
+{:!cmd^M} at this point is also acceptable.
+
+However, there is a quicker, easier way: type {^Z}. Now this is a little
+tricky, but hang in there. When you logged into UNIX, the first program you
+began communicating with was a program that is called a "shell" (i.e. it 'lays
+over' the operating system protecting you from it, sort of like a considerate
+porcupine). When you got your first prompt on the terminal (probably a '%'
+character) this was the shell telling you to type your first command. When
+you typed {vi filename} for some file, the shell did not go away, it just went
+to sleep. The shell is now the parent of vi. When you type {^Z} the editor
+goes to sleep, the shell wakes up and says "you rang?" in the form of another
+prompt (probably '%'). At this point you are talking to the shell again and
+you can do anything that you could before including edit another file! (The
+only thing you can't do is log out: you will get the message "There are
+stopped jobs.")
+
+When your business with the shell is done, type {fg} for 'foreground' and the
+last process which you ^Z'd out of will be reawakened and the shell will go
+back to sleep. I will refer you to the documentation for the Berkeley shell
+'csh' for more information on this useful capability.
+
+Section 34.5: writing parts of a file: {:w}
+
+The {:w} command will accept a range specifier that will then write only a
+selected range of lines to a file. To write this section to a file, position
+the cursor on the section line (e.g. {/^Section 34.5:/^M}) and {z^M}. Now type
+{^G} to find out the line number (it will be something like "line 513"). Now
+{/^Section 34.6:/-1^M} to find the last line of this section, and {^G} to find
+its line number (it will be something like 542). To write out this section of
+text by itself to a separate file which we will call "sepfile", type
+{:510,542w sepfile^M}. If sepfile already exists, you will have to use the
+exclamation point: {:1147,1168w! sepfile^M} or write to a different, non-
+existent file.
+
+{:!cat sepfile^M} will display the file just written, and it should be the
+contents of this section.
+
+There is an alternate method of determining the line numbers for the write.
+{:set number^M} will repaint the screen with each line numbered. When the file
+is written and the numbers no longer needed, {:set nonumber^M} will remove the
+numbers, and {^R} will adjust the screen.
+
+Or, if you remember your earlier lessons about marking lines of text,
+mark the beginning and ending lines. Suppose we had used {ma} to mark the
+first line of the section and {mb} to mark the last. Then the command
+{:'a,'bw sepfile^M} will write the section into "sepfile". In general,
+you can replace a line number with the 'name' of a marked line (a single-quote
+followed by the letter used to mark the line)
+
+
+Section 34.6: filtering portions of text: {!}
+
+{!} is an operator like {c} and {d}. That is, it consists of a repetition
+count, {!}, and a range specifier. Once the {!} operator is entered in its
+entirety, a prompt will be given at the bottom of the screen for a UNIX
+command. The text specified by the {!} operator is then deleted and
+passed/filtered/piped to the UNIX command you type. The output of the UNIX
+command is then placed in your file. For example, place the cursor at the
+beginning of the following line and {z^M}:
+
+ls -l vi.tutorial
+********* marks the bottom of the output from the ls command **********
+
+Now type {!!csh^M}. The line will be replaced with the output from the ls
+command. The {u} command works on {!}, also.
+
+Here is an extended exercise to display some of these capabilities. When this
+tutorial was prepared, certain auxiliary programs were created to aid in its
+development. Of major concern was the formatting of sections of the tutorial
+to fit on a single screen, particularly the first few sections. What was
+needed was a vi command that would 'format' a paragraph; that is, fill out
+lines with as many words as would fit in eighty columns. There is no such vi
+command. Therefore, another method had to be found.
+
+Of course, nroff was designed to do text formatting. However, it produces a
+'page'; meaning that there may be many blank lines at the end of a formatted
+paragraph from nroff. The awk program was used to strip these blank lines from
+the output from nroff. Below are the two files used for this purpose: I refer
+you to documentation on nroff and awk for a full explanation of their function.
+Position the cursor on the next line and {z^M}.
+
+******** contents of file f **********
+#
+nroff -i form.mac | awk "length != 0 { print }"
+***** contents of file form.mac ******
+.na
+.nh
+.ll 79
+.ec 
+.c2 
+.cc 
+**************************************
+
+Determine the line numbers of the two lines of file f. They should be
+something like 574 and 575, although you better double check: this file is
+under constant revision and the line numbers may change inadvertently. Then
+{:574,575w f^M}. Do the same for the lines of file form.mac. They will be
+approximately 577 and 582. Then {:577,582w form.mac^M}. File f must have
+execute privileges as a shell file: {:!chmod 744 f^M}.
+
+Observe that this paragraph is
+rather ratty in appearance. With our newly created files we can
+clean it up dramatically. Position the cursor at the beginning
+of this paragraph and type the following sequence of
+characters
+(note that we must abandon temporarily our convention
+of curly braces since the command itself contains a curly brace - we
+will use square brackets for the nonce): [!}f^M].
+
+Here is a brief explanation of what has happened. By typing [!}f^M] we
+specified that the paragraph (all text between the cursor and the first blank
+line) will be removed from the edit file and piped to a UNIX program called
+"f". This is a shell command file that we have created. This shell file runs
+nroff, pipes its output to awk to remove blank lines, and the output from awk
+is then read back into our file in the place of the old, ratty paragraph. The
+file form.mac is a list of commands to nroff to get it to produce paragraphs
+to our taste (the right margin is not justified, the line is 79 characters
+long, words are not hyphenated, and three nroff characters are renamed to
+avoid conflict: note that in this file, the {^G} you see there is vi's display
+of the control-G character, and not the two separate characters ^ up-arrow and
+G upper-case g).
+
+This example was created before the existence of the fmt program. I now type
+[!}fmt^M] to get the same effect much faster. Actually, I don't type those
+six keys each time: I have an abbreviation (which see).
+
+Section 35: searching with magic patterns
+
+The documentation available for "magic patterns" (i.e. regular expressions) is
+very scanty. The following should explain this possibly very confusing feature
+of the editor. This section assumes that the magic option is on. To make
+sure, you might want to type {:set magic^M}.
+
+By "magic pattern" we mean a general description of a piece of text that the
+editor attempts to find during a search. Most search patterns consist of
+strings of characters that must be matched exactly, e.g. {/card/^M} searches
+for a specific string of four characters. Let us suppose that you have
+discovered that you consistently have mistyped this simple word as either ccrd
+or czrd (this is not so far-fetched for touch typists). You could {/ccrd/^M}
+and {n} until there are no more of this spelling, followed by {/czrd/^M} and
+{n} until there are no more of these. Or you could {/c.rd/^M} and catch all of
+them on the first pass. Try typing {/c.rd/^M} followed by several {n} and
+observe the effect.
+
+Line 27: card cord curd ceard
+
+When '.' is used in a search string, it has the effect of matching any single
+character.
+
+The character '^' (up-arrow) used at the beginning of a search string means
+the beginning of the line. {/^Line 27/^M} will find the example line above,
+while {/Line 27/^M} will find an occurrence of this string anywhere in the
+line.
+
+Similarly, {/ the$/^M} will find all occurrences of the word 'the' occurring
+at the end of a line. There are several of them in this file.
+
+Note that {:set nomagic^M} will turn off the special meaning of these magic
+characters EXCEPT for '^' and '$' which retain their special meanings at the
+beginning and end of a search string. Within the search string they hold no
+special meaning. Try {/\/ the$\//^M} and note that the dollar-sign is not the
+last character in the search string. Let the dollar-sign be the last
+character in the search string, as in {/\/ the$/^M} and observe the result.
+
+Observe the result of {/back.*file/^M}. This command, followed by sufficient
+{n}, will show you all lines in the file that contain both the words 'back'
+and 'file' on the same line. The '*' magic character specifies that the
+previous regular expression (the '.' in our example) is to be repeatedly
+matched zero or more times. In our example we specified that the words 'back'
+and 'file' must appear on the same line (they may be parts of words such as
+'backwards' or 'workfile') separated by any number (including zero) of
+characters.
+
+We could have specified that 'back' and 'file' are to be words by themselves by
+using the magic sequences '\<' or '\>'. E.g. {/\<back\>.*\<file\>/^M}. The
+sequence '\<' specifies that this point of the search string must match the
+beginning of a word, while '\>' specifies a match at the end of a word. By
+surrounding a string with these characters we have specified that they must be
+words.
+
+To find all words that begin with an 'l' or a 'w', followed by an 'a' or an
+'e', and ending in 'ing', try {/\<[lw][ea][a-z]*ing\>/^M}. This will match
+words like 'learning', 'warning', and 'leading'. The '[..]' notation matches
+exactly ONE character. The character matched will be one of the characters
+enclosed in the square brackets. The characters may be specified individually
+as in [abcd] or a '-' may be used to specify a range of characters as in [a-d].
+That is, [az] will match the letter 'a' OR the letter 'z', while [a-z] will
+match any of the lower case letters from 'a' through 'z'. If you would like to
+match either an 'a', a '-', or a 'z', then the '-' must be escaped: [a\-z] will
+match ONE of the three characters 'a', '-', or 'z'.
+
+If you wish to find all Capitalized words, try {/\<[A-Z][a-z]*\>/^M}. The
+following will find all character sequences that do NOT begin with an
+uncapitalized letter by applying a special meaning to the '^' character in
+square brackets: {/\<[^a-z][a-z]*\>/^M}. When '^' is the first character of a
+square-bracket expression, it specifies "all but these characters". (No
+one claimed vi was consistent.)
+
+To find all variable names (the first character is alphabetic, the remaining
+characters are alphanumeric): try {/\<[A-Za-z][A-Za-z0-9]*\>/^M}.
+
+In summary, here are the primitives for building regular expressions:
+
+ ^ at beginning of pattern, matches beginning of line
+ $ at end of pattern, matches end of line
+ . matches any single character
+ \< matches the beginning of a word
+ \> matches the end of a word
+ [str] matches any single character in str
+ [^str] matches any single character NOT in str
+ [x-y] matches any character in the ASCII range between x and y
+ * matches any number (including zero) of the preceding pattern
+
+Section 36: advanced substitution: {:s}
+
+The straightforward colon-substitute command looks like the substitute
+command of most line-oriented editors. Indeed, vi is nothing more than a
+superstructure on the line-oriented editor ex and the colon commands are
+simply a way of accessing commands within ex (see section #EX). This gives us
+a lot of global file processing not usually found in visual oriented editors.
+
+The colon-substitute command looks like: {:s/ .. / .. /^M} and will find the
+pattern specified after the first slash (this is called the search pattern),
+and replace it with the pattern specified after the second slash (called,
+obviously enough, the replacement pattern). E.g. position the cursor on line
+28 below and {:s/esample/example/^M}:
+
+Line 28: This is an esample.
+
+The {u} and {U} commands work for {:s}. The first pattern (the search pattern)
+may be a regular expression just as for the search command (after all, it IS a
+search, albeit limited to the current line). Do an {u} on the above line, and
+try the following substitute, which will do almost the same thing:
+{:s/s[^ ]/x/^M}.
+Better undo it with {u}. The first pattern {s[^ ]} matches an 's'
+NOT followed by a blank: the search therefore ignores the 's'es in 'This' and
+'is'. However, the character matched by {[^ ]} must appear in the replacement
+pattern. But, in general, we do not know what that character is! (In this
+particular example we obviously do, but more complicated examples will follow.)
+Therefore, vi (really ex) has a duplication mechanism to copy patterns matched
+in the search string into the replacement string. Line 29 below is a copy of
+line 28 above so you can adjust your screen.
+
+Line 29: This is an esample.
+
+In general, you can nest parts of the search pattern in \( .. \) and refer to
+it in the replacement pattern as \n, where n is a digit. The problem outlined
+in the previous paragraph is solved with {:s/s\([^ ]\)/x\1/^M}: try it. Here
+\1 refers to the first pattern grouping \( .. \) in the search string.
+
+Obviously, for a single line, this is rather tedious. Where it becomes
+powerful, if not necessary, is in colon-substitutes that cover a range of
+lines. (See the next section for a particularly comprehensive example.)
+
+If the entire character sequence matched by the search pattern is needed in
+the replacement pattern, then the unescaped character '&' can be used. On
+Line 29 above, try {:s/an e.ample/not &/^M}. If another line is to have the
+word 'not' prepended to a pattern, then '~' can save you from re-typing the
+replacement pattern. E.g. {:s/some pattern/~/^M} after the previous example
+would be equivalent to {:s/some pattern/not &/^M}.
+
+One other useful replacement pattern allows you to change the case of
+individual letters. The sequences {\u} and {\l} cause the immediately
+following character in the replacement to be converted to upper- or lower-case,
+respectively, if this character is a letter. The sequences {\U} and {\L} turn
+such conversion on, either until {\E} or {\e} is encountered, or until the end
+of the replacement pattern.
+
+For example, position the cursor on a line: pick a line, any line. Type
+{:s/.*/\U&/^M} and observe the result. You can undo it with {u}.
+
+The search pattern may actually match more than once on a single line.
+However, only the first pattern is substituted. If you would like ALL
+patterns matched on the line to be substituted, append a 'g' after the
+replacement pattern: {:s/123/456/g^M} will substitute EVERY occurrence
+on the line of 123 with 456.
+
+Section 37: advanced line addressing: {:p} {:g} {:v}
+
+Ex (available through the colon command in vi) offers several methods for
+specifying the lines on which a set of commands will act. For example, if you
+would like to see lines 50 through 100 of your file: {:50,100p^M} will display
+them, wait for you to [Hit return to continue], and leave you on line 100.
+Obviously, it would be easier just to do {100G} from within vi. But
+what if you would like to make changes to just those lines? Then the
+addressing is important and powerful.
+
+Line 30: This is a text.
+Line 31: Here is another text.
+Line 32: One more text line.
+
+The lines above contain a typing error that the author of this tutorial tends
+to make every time he attempts to type the word 'test'. To change all of these
+'text's into 'test's, try the following:
+{:/^Line 30/,/^Line 32/s/text/test/^M}. This finds the beginning and end of
+the portion of text to be changed, and limits the substitution to each of the
+lines in that range. The {u} command applies to ALL of the substitutions as
+a group.
+
+This provides a mechanism for powerful text manipulations.
+And very complicated examples.
+
+Line 33: This test is a.
+Line 34: Here test is another.
+Line 35: One line more test.
+
+The above three lines have the second word out of order. The following command
+string will put things right. Be very careful when typing this: it is very
+long, full of special characters, and easy to mess up. You may want to
+consider reading the following section to understand it before trying the
+experiment. Don't worry about messing up the rest of the file, though: the
+address range is specified.
+
+{:/^Line 33/,/^Line 35/s/\([^:]*\): \([^ ]*\) \([^ ]*\) \([^.]*\)/\1: \2 \4 \3/^M}
+
+There are several things to note about this command string. First of all, the
+range of the substitute was limited by the address specification {/^Line
+33/,/^Line 35/^M}. It might have been simpler to do {:set number^M} to see the
+line numbers directly, and then, in place of the two searches, typed
+the line numbers, e.g. {1396,1398}. Or to mark the lines with {ma} and {mb}
+and use {'a,'b}.
+
+Then follows the substitute pattern itself. To make it easier to understand
+what the substitute is doing, the command is duplicated below with the various
+patterns named for easier reference:
+
+ s/\([^:]*\): \([^ ]*\) \([^ ]*\) \([^.]*\)/\1: \2 \4 \3/
+ |--\1---| |--\2---| |--\3---| |--\4---|
+ |--------search pattern------------------|-replacement|
+ |--pattern---|
+
+In overview, the substitute looks for a particular pattern made up of
+sub-patterns, which are named \1, \2, \3, and \4. These patterns are specified
+by stating what they are NOT. Pattern \1 is the sequence of characters that
+are NOT colons: in the search string, {[^:]} will match exactly one character
+that is not a colon, while appending the asterisk {[^:]*} specifies that the
+'not a colon' pattern is to be repeated until no longer satisfied, and
+{\([^:]*\)} then gives the pattern its name, in this case \1. Outside of the
+specification of \1 comes {: }, specifying that the next two characters must be
+a colon followed by a blank.
+
+Patterns \2 and \3 are similar, specifying character sequences that are
+not blanks. Pattern \4 matches up to the period at the end of the line.
+
+The replacement pattern then consists of specifying the new order of the
+patterns.
+
+This is a particularly complicated example, perhaps the most complicated
+in this tutorial/reference. For our small examples, it is obviously
+tedious and error prone. For large files, however, it may be the most
+efficient way to make the desired modifications.
+
+(The reader is advised to look at the documentation for awk. This tool is very
+powerful and slightly simpler to use than vi for this kind of file
+manipulation. But, it is another command language to learn.)
+
+Many times, you will not want to operate on every line in a certain
+range. Rather you will want to make changes on lines that satisfy
+certain patterns; e.g. for every line that has the string 'NPS' on it,
+change 'NPS' to 'Naval Postgraduate School'. The {:g} addressing
+command was designed for this purpose. The example of this paragraph
+could be typed as {:g/NPS/s//Naval Postgraduate School/^M}.
+
+The general format of the command is {:g/(pattern)/cmds^M} and it
+works in the following way: all lines that match the pattern
+following the {:g} are 'tagged' in a special way. Then each of these
+lines have the commands following the pattern executed over them.
+
+Line 36: ABC rhino george farmer Dick jester lest
+Line 37: george farmer rhino lest jester ABC
+Line 38: rhino lest george Dick farmer ABC jester
+
+Type:
+
+{:g/^Line.*ABC/s/Dick/Harry Binswanger/|s/george farmer/gentleman george/p^M}
+
+There are several things of note here. First, lines 36, 37, and 38 above are
+tagged by the {:g}. Type {:g/^Line.*ABC/p^M} to verify this. Second, there
+are two substitutes on the same line separated by '|'. In general, any colon
+commands can be strung together with '|'. Third, both substitutes operate on
+all three lines, even though the first stubstitute works on only two of the
+lines (36 and 38). Fourth, the second substitute works on only two lines (36
+and 37) and those are the two lines printed by the trailing 'p'.
+
+The {:v} command works similarly to the {:g} command, except that the sense of
+the test for 'tagging' the lines is reversed: all lines NOT matching the search
+pattern are tagged and operated on by the commands.
+
+Using {^V} to quote carriage return (see section 39) can be used in global
+substitutions to split two lines. For example, the command
+{:g/\. /s//.^V^M/g^M} will change your file so that each sentence is on a
+separate line. (Note that we have to 'escape' the '.', because '.' by itself
+matches any character. Our command says to find any line which contains a
+period followed by 2 spaces, and inserts a carriage return after the period.)
+
+Caveat: In some of the documentation for ex and vi you may find the
+comment to the effect that {\^M} can be used between commands following
+{:g}. The author of this tutorial has never gotten this to work and has
+crashed the editor trying.
+
+Section 38: higher level text objects and nroff: {(} {)} [{] [}] {[[} {]]}
+
+(Note: this section may be a little confusing because of our command
+notation. Using curly braces to surround command strings works fine as
+long as the command string does not contain any curly braces itself.
+However, the curly braces are legitimate commands in vi. Therefore, for
+any command sequence that contains curly braces, we will surround that
+sequence with SQUARE braces, as on the previous Section line.)
+
+In working with a document, particularly if using the text formatting
+programs nroff or troff, it is often advantageous to work in terms of
+sentences, paragraphs, and sections. The operations {(} and {)} move to
+the beginning of the previous and next sentences, respectively. Thus
+the command {d)} will delete the rest of the current sentence; likewise
+{d(} will delete the previous sentence if you are at the beginning of
+the current sentence, or, if you are not at the beginning of a sentence,
+it will delete the current sentence from the beginning
+up to where you are.
+
+A sentence is defined to end at a '.', '!', or '?' which is followed
+by either the end of a line, or by two spaces. Any number of closing
+')', ']', '"', and ''' characters may appear after the '.', '!', or '?'
+before the spaces or end of line. Therefore, the {(} and {)} commands
+would recognize only one sentence in the following line, but two
+sentences on the second following line.
+
+Line 39: This is one sentence. Even though it looks like two.
+Line 40: This is two sentences. Because it has two spaces after the '.'.
+
+The operations [{] and [}] move over paragraphs and the operations {[[}
+and {]]} move over sections.
+
+A paragraph begins after each empty line, and also at each of a set of nroff
+paragraph macros. A section begins after each line with a form-feed ^L in the
+first column, and at each of a set of nroff section macros. When preparing a
+text file as input to nroff, you will probably be using a set of nroff macros
+to make the formatting specifications easier, or more to your taste. These
+macros are invoked by beginning a line with a period followed by the one or two
+letter macro name. Vi has been programmed to recognize these nroff macros, and
+if it doesn't recognize your particular macro you can use the {:set paragraphs}
+or {:set sections} commands so that it will.
+
+Section 39: more about inserting text
+
+There are a number of characters which you can use to make correnctions
+during input mode. These are summarized in the following table.
+
+ ^H deletes the last input character
+ ^W deletes the last input word
+ (erase) same as ^H; each terminal can define its own erase character;
+ for some it is ^H, for others it is the DELETE key, and for
+ others it is '@'.
+ (kill) deletes the input on this line; each terminal can define its
+ own line-kill character; for some it is ^U, for others it is
+ '@'; you will need to experiment on your terminal to find
+ out what your line-kill and erase characters are.
+ \ escapes a following ^H, (kill), and (erase) characters: i.e.
+ this is how to put these characters in your file.
+ ^[ escape key; ends insertion mode
+ ^? the delete key; interrupts an insertion, terminating it
+ abnormally.
+ ^M the return key; starts a new line.
+ ^D backtabs over the indentation set by the autoindent option
+ 0^D backtabs over all indentation back to the beginning of the line
+ ^^D (up-arrow followed by control-d)same as 0^D, except the indentation
+ will be restored at the beginning of the next line.
+ ^V quotes the next non-printing character into the file
+
+If you wish to type in your erase or kill character (say # or @ or ^U) then you
+must precede it with a \, just as you would do at the normal system command
+level. A more general way of typing non-printing characters into the file is
+to precede them with a ^V. The ^V echoes as a ^ character on which the cursor
+rests. This indicates that the editor expects you to type a control character
+and it will be inserted into the file at that point. There are a few
+exceptions to note. The implementation of the editor does not allow the null
+character ^@ to appear in files. Also the linefeed character ^J is used by the
+editor to separate lines in the file, so it cannot appear in the middle of a
+line. (Trying to insert a ^M into a file, or putting it in the replacement
+part of a substitution string will result in the matched line being split in
+two. This, in effect, is how to split lines by using a substitution.) You can
+insert any other character, however, if you wait for the editor to echo the ^
+before you type the character. In fact, the editor will treat a following
+letter as a request for the corresponding control character. This is the only
+way to type ^S or ^Q, since the system normally uses them to suspend and resume
+output and never gives them to the editor to process.
+
+If you are using the autoindent option you can backtab over the indent which it
+supplies by typing a ^D. This backs up to the boundary specified by the
+shiftwidth option. This only works immediately after the supplied autoindent.
+
+When you are using the autoindent option you may wish to place a label at the
+left margin of a line. The way to do this easily is to type ^ (up-arrow) and
+then ^D. The editor will move the cursor to the left margin for one line, and
+restore the previous indent on the next. You can also type a 0 followed
+immediately by a ^D if you wish to kill all indentation and not have it resume
+on the next line.
+
+Section 40: more on operators: {d} {c} {<} {>} {!} {=} {y}
+
+Below is a non-exhaustive list of commands that can follow the operators
+to affect the range over which the operators will work. However, note
+that the operators {<}, {>}, {!}, and {=} do not operate on any object
+less than a line. Try {!w} and you will get a beep. To get the
+operator to work on just the current line, double it. E.g. {<<}.
+
+ suffix will operate on
+ ------ ------------------------
+ ^[ cancels the command
+ w the word to the right of the cursor
+ W ditto, but ignoring punctuation
+ b the word to the left of the cursor
+ B ditto, but ignoring punctuation
+ e see below.
+ E ditto
+ (space) a character
+ $ to the end of the line
+ ^ to the beginning of the line
+ / .. / up to, but not including, the string
+ ? .. ? back to and including the string
+ fc up to and including the occurrence of c
+ Fc back to and including the occurrence of c
+ tc up to but not including the occurrence of c
+ Tc back to but not including the occurrence of c
+ ^M TWO lines (that's right: two)
+ (number)^M that many lines plus one
+ (number)G up to and including line (number)
+ ( the previous sentence if you are at the beginning of
+ the current sentence, or the current sentence up to where
+ you are if you are not at the beginning of the current
+ sentence. Here, 'sentence' refers to the intuitive
+ notion of an English sentence, ending with '!', '?',
+ or '.' and followed by an end of line or two spaces.
+ ) the rest of the current sentence
+ { analogous to '(', but in reference to paragraphs:
+ sections of text surrounded by blank lines
+ } analogous to ')', but in reference to paragraphs
+ [[ analogous to '(', but in reference to sections
+ ]] analogous to ')', but in reference to sections
+ H the first line on the screen
+ M the middle line on the screen
+ L the last line on the screen
+ 3L through the third line from the bottom of the screen
+ ^F forward a screenful
+ ^B backward a screenful
+ :
+ : etc. etc. etc.
+
+This list is not exhaustive, but it should be sufficient to get the idea
+across: after the operator, you can specify a range with a move-the-cursor
+command, and that is the region of text over which the operator will be
+effective.
+
+Section 41: abbreviations: {:ab}
+
+When typing large documents you may find yourself typing a large phrase
+over and over. Vi gives you the ability to specify an abbreviation for
+a long string such that typing the abbreviation will automatically
+expand into the longer phrase.
+
+Type {:ab nps Naval Postgraduate School^M}. Now type:
+
+{iThis is to show off the nps's UNIX editor.^M^[}
+
+Section 42: vi's relationship with the ex editor: {:}
+
+Vi is actually one mode of editing within the editor ex. When you are
+running vi you can escape to the line oriented editor of ex by giving
+the command {Q}. All of the colon-commands which were introduced above
+are available in ex. Likewise, most ex commands can be invoked from vi
+using {:}.
+
+In rare instances, an internal error may occur in vi. In this case you
+will get a diagnostic and will be left in the command mode of ex. You can
+then save your work and quit if you wish by giving the command {x} after
+the colon prompt of ex. Or you can reenter vi (if you are brave) by
+giving ex the command {vi}.
+
+Section 43: vi on hardcopy terminals and dumb terminals: open mode
+
+(The author has not checked the following documentation for accuracy. It is
+abstracted from the Introduction to Vi Editing document.)
+
+If you are on a hardcopy terminal or a terminal which does not have a cursor
+which can move off the bottom line, you can still use the command set of vi,
+but in a different mode. When you give the vi command to UNIX, the editor will
+tell you that it is using open mode. This name comes from the open command in
+ex, which is used to get into the same mode.
+
+The only difference between visual mode (normal vi) and open mode is the way in
+which the text is displayed.
+
+In open mode the editor uses a single line window into the file, and moving
+backward and forward in the file causes new lines to be displayed, always below
+the current line. Two commands of vi work differently in open: {z} and {^R}.
+The {z} command does not take parameters, but rather draws a window of context
+around the current line and then returns you to the current line.
+
+If you are on a hardcopy terminal, the {^R} command will retype the current
+line. On such terminals, the editor normally uses two lines to represent the
+current line. The first line is a copy of the line as you started to edit it,
+and you work on the line below this line. When you delete characters, the
+editor types a number of \'s to show you the characters which are deleted. The
+editor also reprints the current line soon after such changes so that you can
+see what the line looks like again.
+
+It is sometimes useful to use this mode on very slow terminals which can
+support vi in the full screen mode. You can do this by entering ex and using
+an {open} command.
+
+*********************************************************************
+Section 44: options: {:set} {setenv EXINIT}
+
+You will discover options as you need them. Do not worry about them very much
+on the first pass through this document. My advice is to glance through them,
+noting the ones that look interesting, ignoring the ones you don't understand,
+and try re-scanning them in a couple of weeks.
+
+If you decide that you have a favorite set of options and would like to change
+the default values for the editor, place a {setenv EXINIT} command in your
+.login file. When you are given an account under UNIX your directory has
+placed in it a file that is executed each time you log in. If one of the
+commands in this file sets the environment variable EXINIT to a string of vi
+commands, you can have many things done for you each time you invoke vi. For
+example, if you decide that you don't like tabstops placed every eight columns
+but prefer every four columns, and that you wish the editor to insert linefeeds
+for you when your typing gets you close to column 72, and you want
+autoindentation, then include the following line in your .login file:
+
+setenv EXINIT='set tabstop=4 wrapmargin=8 autoindent'
+
+or equivalently
+
+setenv EXINIT='se ts=4 wm=8 ai'
+
+Each time you bring up vi, this command will be executed and the options set.
+
+There are forty options in the vi/ex editor that the user can set for his/her
+own convenience. They are described in more detail in individual sections
+below. The section line will show the full spelling of the option name, the
+abbreviation, and the default value of the option. The text itself
+comes from the ex reference manual and is not the epitome of clarity.
+
+Section 44.1: {autoindent}, {ai} default: noai
+
+Can be used to ease the preparation of structured program text. At the
+beginning of each append, change or insert command or when a new line is opened
+or created by an append, change, insert, or substitute operation within open or
+visual mode, ex looks at the line being appended after, the first line changed
+or the line inserted before and calculates the amount of white space at the
+start of the line. It then aligns the cursor at the level of indentation so
+determined.
+
+If the user then types lines of text in, they will continue to be justified at
+the displayed indenting level. If more white space is typed at the beginning
+of a line, the following line will start aligned with the first non-white
+character of the previous line. To back the cursor up to the preceding tab
+stop one can hit {^D}. The tab stops going backwards are defined at multiples
+of the shiftwidth option. You cannot backspace over the indent, except by
+sending an end-of-file with a {^D}. A line with no characters added to it
+turns into a completely blank line (the white space provided for the autoindent
+is discarded). Also specially processed in this mode are lines beginning with
+an up-arrow `^' and immediately followed by a {^D}. This causes the input to
+be repositioned at the beginning of the line, but retaining the previous indent
+for the next line. Similarly, a `0' followed by a {^D} repositions at the
+beginning but without retaining the previous indent. Autoindent doesn't happen
+in global commands or when the input is not a terminal.
+
+Section 44.2: {autoprint}, {ap} default: ap
+
+Causes the current line to be printed after each delete, copy, join, move,
+substitute, t, undo or shift command. This has the same effect as supplying a
+trailing `p' to each such command. Autoprint is suppressed in globals, and
+only applies to the last of many commands on a line.
+
+Section 44.3: {autowrite}, {aw} default: noaw
+
+Causes the contents of the buffer to be written to the current file if you have
+modified it and give a next, rewind, stop, tag, or {!} command, or a control-
+up-arrow {^^} (switch files) or {^]} (tag goto) command in visual. Note, that
+the edit and ex commands do not autowrite. In each case, there is an
+equivalent way of switching when autowrite is set to avoid the autowrite
+({edit} for next, rewind! for rewind, stop! for stop, tag! for tag, shell
+for {!}, and {:e #} and a {:ta!} command from within visual).
+
+Section 44.4: {beautify}, {bf} default: nobeautify
+
+Causes all control characters except tab ^I, newline ^M and form-feed ^L to be
+discarded from the input. A complaint is registered the first time a backspace
+character is discarded. Beautify does not apply to command input.
+
+Section 44.5: {directory}, {dir} default: dir=/tmp
+
+Specifies the directory in which ex places its buffer file. If this directory
+in not writable, then the editor will exit abruptly when it fails to be able to
+create its buffer there.
+
+Section 44.6: {edcompatible} default: noedcompatible
+
+Causes the presence or absence of g and c suffixes on substitute commands to be
+remembered, and to be toggled by repeating the suffices. The suffix r makes
+the substitution be as in the {~} command, instead of like {&}.
+
+[Author's note: this should not concern users of vi.]
+
+Section 44.7: {errorbells}, {eb} default: noeb
+
+Error messages are preceded by a bell. However, bell ringing in open and
+visual modes on errors is not suppressed by setting noeb. If possible the
+editor always places the error message in a standout mode of the terminal (such
+as inverse video) instead of ringing the bell.
+
+Section 44.8: {hardtabs}, {ht} default: ht=8
+
+Gives the boundaries on which terminal hardware tabs are set (or on which the
+system expands tabs).
+
+Section 44.9: {ignorecase}, {ic} default: noic
+
+All upper case characters in the text are mapped to lower case in regular
+expression matching. In addition, all upper case characters in regular
+expressions are mapped to lower case except in character class specifications
+(that is, character in square brackets).
+
+Section 44.10: {lisp} default: nolisp
+
+Autoindent indents appropriately for lisp code, and the {(}, {)}, [{], [}],
+{[[}, and {]]} commands in open and visual modes are modified in a
+striaghtforward, intuitive fashion to have meaning for lisp.
+
+[Author's note: but don't ask me to define them precisely.]
+
+Section 44.11: {list} default: nolist
+
+All printed lines will be displayed (more) unambiguously, showing tabs as ^I
+and end-of-lines with `$'. This is the same as in the ex command {list}.
+
+Section 44.12: {magic} default: magic for {ex} and {vi}, nomagic for edit.
+
+If nomagic is set, the number of regular expression metacharacters is greatly
+reduced, with only up-arrow `^' and `$' having special effects. In addition
+the metacharacters `~' and `&' of the replacement pattern are treated as normal
+characters. All the normal metacharacters may be made magic when nomagic is
+set by preceding them with a `\'.
+
+[Author's note: In other words, if magic is set a back-slant turns the magic
+off for the following character, and if nomagic is set a back-slant turns the
+magic ON for the following character. And, no, we are not playing Dungeons and
+Dragons, although I think the writers of these option notes must have played it
+all the time.]
+
+Section 44.13: {mesg} default: mesg
+
+Causes write permission to be turned off to the terminal while you are in
+visual mode, if nomesg is set.
+
+[Author's note: I don't know if anyone could have made any one sentence
+paragraph more confusing than this one. What it says is: mesg allows people to
+write to you even if you are in visual or open mode; nomesg locks your terminal
+so they can't write to you and mess up your screen.]
+
+Section 44.14: {number, nu} default: nonumber
+
+Causes all output lines to be printed with their line numbers. In addition
+each input line will be prompted with its line number.
+
+Section 44.15: {open} default: open
+
+If {noopen}, the commands open and visual are not permitted. This is set for
+edit to prevent confusion resulting from accidental entry to open or visual
+mode.
+
+[Author's note: As you may have guessed by now, there are actually three
+editors available under Berkeley UNIX that are in reality the same
+program, ex, with different options set: ex itself, vi, and edit.]
+
+Section 44.16: {optimize, opt} default: optimize
+
+Throughput of text is expedited by setting the terminal to not do automatic
+carriage returns when printing more than one (logical) line of output, greatly
+speeding output on terminals without addressable cursors when text with leading
+white space is printed.
+
+[Author's note: I still don't know what this option does.]
+
+Section 44.17: {paragraphs, para} default: para=IPLPPPQPP LIbp
+
+Specifies the paragraphs for the [{] and [}] operations in open and visual.
+The pairs of characters in the option's value are the names of the nroff macros
+which start paragraphs.
+
+Section 44.18: {prompt} default: prompt
+
+Command mode input is prompted for with a `:'.
+
+[Author's note: Doesn't seem to have any effect on vi.]
+
+Section 44.19: {readonly}, {ro} default: noro, unless invoked with -R
+ or insufficient privileges on file
+
+This option allows you to guarantee that you won't clobber your file by
+accident. You can set the option and writes will fail unless you use an `!'
+after the write. Commands such as {x}, {ZZ}, the autowrite option, and in
+general anything that writes is affected. This option is turned on if you
+invoke the editor with the -R flag.
+
+Section 44.20: {redraw} default: noredraw
+
+The editor simulates (using great amounts of output), an intelligent terminal
+on a dumb terminal (e.g. during insertions in visual the characters to the
+right of the cursor position are refreshed as each input character is typed).
+Useful only at very high baud rates, and should be used only if the system is
+not heavily loaded: you will notice the performance degradation yourself.
+
+Section 44.21: {remap} default: remap
+
+If on, macros are repeatedly tried until they are unchanged. For example, if o
+is mapped to O, and O is mapped to I, then if remap is set, o will map to I,
+but if noremap is set, it will map to O .
+
+Section 44.22: {report} default: report=5 for ex and vi, 2 for edit
+
+Specifies a threshold for feedback from commands. Any command which modifies
+more than the specified number of lines will provide feedback as to the scope
+of its changes. For commands such as global, open, undo, and visual which have
+potentially more far reaching scope, the net change in the number of lines in
+the buffer is presented at the end of the command, subject to this same
+threshold. Thus notification is suppressed during a global command on the
+individual commands performed.
+
+Section 44.23: {scroll} default: scroll=1/2 window
+
+Determines the number of logical lines scrolled when a {^D} is received from a
+terminal in command mode, and determines the number of lines printed by a
+command mode z command (double the value of scroll).
+
+[Author's note: Doesn't seem to affect {^D} and {z} in visual (vi) mode.]
+
+Section 44.24: sections {sections} default: sections=SHNHH HU
+
+Specifies the section macros from nroff for the {[[} and {]]} operations in
+open and visual. The pairs of characters in the options's value are the names
+of the macros which start paragraphs.
+
+Section 44.25: {shell}, {sh} default: sh=/bin/sh
+
+Gives the path name of the shell forked for the shell escape command `!', and
+by the shell command. The default is taken from SHELL in the environment, if
+present.
+
+[Editor's note: I would suggest that you place the following line in
+your .login file:
+setenv SHELL '/bin/csh'
+]
+
+Section 44.26: {shiftwidth}, {sw} default: sw=8
+
+Used in reverse tabbing with {^D} when using autoindent to append text, and
+used by the shift commands. Should probably be the same value as the tabstop
+option.
+
+Section 44.27: {showmatch}, {sm} default: nosm
+
+In open and visual mode, when a `)' or `}' is typed, if the matching `(' or `{'
+is on the screen, move the cursor to it for one second. Extremely useful with
+complicated nested expressions, or with lisp.
+
+Section 44.28: {slowopen}, {slow} default: terminal dependent
+
+Affects the display algorithm used in visual mode, holding off display updating
+during input of new text to improve throughput when the terminal in use is both
+slow and unintelligent. See "An Introduction to Display Editing with Vi" for
+more details.
+
+Section 44.29: {tabstop}, {ts} default: ts=8
+
+The editor expands tabs ^I to tabstop boundaries in the display.
+
+Section 44.30: {taglength}, {tl} default: tl=0
+
+Tags are not significant beyond this many characters.
+A value of zero (the default) means that all characters are significant.
+
+Section 44.31: {tags} default: tags=tags /usr/lib/tags
+
+A path of files to be used as tag files for the tag command. A requested tag
+is searched for in the specified files, sequentially. By default files called
+tags are searched for in the current directory and in /usr/lib (a master file
+for the entire system).
+
+[Author's note: The author of this tutorial has never used this option, nor
+seen it used. I'm not even sure I know what they are talking about.]
+
+Section 44.32: {term} default: from environment variable TERM
+
+The terminal type of the output device.
+
+Section 44.33: {terse} default: noterse
+
+Shorter error diagnostics are produced for the experienced user.
+
+Section 44.34: {timeout} default: timeout
+
+Causes macros to time out after one second. Turn it off and they will
+wait forever. This is useful if you want multi-character macros, but if
+your terminal sends escape sequences for arrow keys, it will be
+necessary to hit escape twice to get a beep.
+
+[Editor's note: Another paragraph which requires a cryptographer.]
+
+Section 44.35: ttytype
+
+[Editor's note: I have found no documentation for this option at all.]
+
+Section 44.36: {warn} default: warn
+
+Warn if there has been `[No write since last change]' before a `!' command
+escape.
+
+Section 44.37: {window} default: window=speed dependent
+
+The number of lines in a text window in the visual command. The default is 8
+at slow speeds (600 baud or less), 16 at medium speed (1200 baud), and the full
+screen (minus one line) at higher speeds.
+
+Section 44.38: {wrapscan}, {ws} default: ws
+
+Searches using the regular expressions in addressing will wrap around past the
+end of the file.
+
+Section 44.39: {wrapmargin}, {wm} default: wm=0
+
+Defines a margin for automatic wrapover of text during input in open and visual
+modes. The numeric value is the number of columns from the right edge of the
+screen around which vi looks for a convenient place to insert a new-line
+character (wm=0 is OFF). This is very convenient for touch typists.
+Wrapmargin behaves much like fill/nojustify mode does in nroff.
+
+Section 44.40: {writeany}, {wa} default: nowa
+
+Inhibit the checks normally made before write commands, allowing a write to any
+file which the system protection mechanism will allow.
+
+Section 44.41: {w300}, {w1200}, {w9600} defaults: w300=8
+ w1200=16
+ w9600=full screen minus one
+
+These are not true options but set the default size of the window for when the
+speed is slow (300), medium (1200), or high (9600), respectively. They are
+suitable for an EXINIT and make it easy to change the 8/16/full screen rule.
+
+Section 45: Limitations
+
+Here are some editor limits that the user is likely to encounter:
+ 1024 characters per line
+ 256 characters per global command list
+ 128 characters per file name
+ 128 characters in the previous inserted and deleted text in open or
+ visual
+ 100 characters in a shell escape command
+ 63 characters in a string valued option
+ 30 characters in a tag name
+ 250000 lines in the file (this is silently enforced).
+
+The visual implementation limits the number of macros defined with map to 32,
+and the total number of characters in macros to be less than 512.
+
+[Editor's note: these limits may not apply to versions after 4.1BSD.]
diff --git a/usr.bin/vi/docs/tutorial/vi.beginner b/usr.bin/vi/docs/tutorial/vi.beginner
new file mode 100644
index 0000000..3bf35ac
--- /dev/null
+++ b/usr.bin/vi/docs/tutorial/vi.beginner
@@ -0,0 +1,741 @@
+Section 1: {^F} {ZZ}
+
+To get out of this tutorial, type: ZZ (two capital Z's).
+
+Learning a new computer system implies learning a new text editor. These
+tutorial lessons were created by Dain Samples to help you come to grips with
+UC Berkeley's screen oriented editor called vi (for VIsual). This tutorial
+uses the vi editor itself as the means of presentation.
+
+For best use of this tutorial, read all of a screen before performing any of
+the indicated actions. This tutorial (or, at least, the first half of it) has
+been designed to systematically present the vi commands IF THE INSTRUCTIONS
+ARE FOLLOWED! If you are too adventuresome, you may find yourself lost. If
+you ever find yourself stuck, remember the first line of this section.
+
+OK, now find the control key on your keyboard; it usually has CTL or CTRL
+written on its upper surface. Your first assignment is to hold the control
+key down while you press the 'F' key on your keyboard. Please do so now.
+
+
+
+Section 2: {^F} {^B}
+Many of vi's commands use the control key and some other key in combination,
+as with the control and the 'F' key above. This is abbreviated CTL-F, or ^F.
+
+As you have probably guessed by now, ^F (CTL-F) moves you forward a fixed
+number of lines in the file. Throughout the remainder of the tutorial when
+you are ready to advance to the next section of text, hit ^F.
+
+The opposite command is ^B. Just for fun, you might want to try a ^B to see
+the previous section again. Be sure to do a ^F to return you here.
+
+Determine what the cursor looks like on your screen. Whatever it is (a box,
+an underscore, blinking, flashing, inverse, etc.) it should now be positioned
+in the upper left-hand corner of your screen under or on the S of Section.
+Become familiar with your cursor: to use vi correctly it is important to
+always know where the cursor is.
+
+Did you notice that when you do a ^F the cursor is left at the top of the
+screen, and a ^B leaves the cursor near the bottom of the screen? Try the two
+commands ^B^F again. And now do another ^F to see the next section.
+
+Section 3: {^F} {^B}
+You now have two basic commands for examining a file, both forwards (^F) and
+backwards (^B).
+
+Note that these are vi text editing commands: they are not commands for the
+tutorial. Indeed, this tutorial is nothing but a text file which you are now
+editing. Everything you do and learn in this tutorial will be applicable to
+editing text files.
+
+Therefore, when you are editing a file and are ready to see more of the text,
+entering ^F will get you to the next section of the file. Entering ^B will
+show you the previous section.
+
+Time for you to do another ^F.
+
+
+
+
+
+
+
+Section 4: {^F} {^B} {^M} (return key)
+We will adopt the notation of putting commands in curly braces so we can write
+them unambiguously. For example, if you are to type the command sequence
+"control B control F" (as we asked you to do above) it would appear as {^B^F}.
+This allows clear delineation of the command strings from the text. Remember
+that the curly braces are NOT part of the command string you are to type. Do
+NOT type the curly braces.
+
+Sometimes, the command string in the curly braces will be rather long, and may
+be such that the first couple of characters of the command will erase from
+the screen the string you are trying to read and type. It is suggested that
+you write down the longer commands BEFORE you type them so you won't forget
+them once they disappear.
+
+Now locate the return key on your keyboard: it is usually marked 'RETURN',
+indicate hitting the return key. In fact, the control-M key sequence is
+exactly the same as if you hit the return key, and vice versa.
+
+Now type {^F}.
+
+
+Section 5: {:q!} {ZZ} {^M} (return key)
+Recognize that this tutorial is nothing more than a text file that you
+are editing. This means that if you do something wrong, it is possible
+for you to destroy the information in this file. Don't worry. If this
+happens, type {ZZ} (two capital Z's) or {:q!^M} to leave the tutorial.
+Restart the tutorial. Once in the tutorial, you can then page forward
+with {^F} until you are back to where you want to be. (There are
+easier ways to do this, some of which will be discussed later, but this
+is the most straightforward.)
+
+You may want to write these commands down in a convenient place for quick
+reference: {:q!^M} and {ZZ}
+
+We will assume that you now know to do a {^F} to advance the file
+
+
+
+
+
+
+
+Section 6: {m} {G} {'} {z}
+Now that you know how to get around in the file via ^F and ^B let's look at
+other ways of examining a text file. Sometimes it is necessary, in the midst
+of editing a file, to examine another part of the file. You are then faced
+with the problem of remembering your place in the file, looking at the other
+text, and then getting back to your original location. Vi has a 'mark'
+command, m. Type {mp}. You have just 'marked' your current location in the
+file and given it the name 'p'. The command string below will do three
+things: position you at the beginning of the file (line 1), then return you to
+the location 'p' that you just marked with the 'm' command, and, since the
+screen will not look exactly the same as it does right now, the 'z' command
+will reposition the screen. (You may want to write the string down before
+typing it: once you type {1G} it will no longer be on the screen.)
+
+So now type {1G'pz^M} - a one followed by a capital G, followed by the quote
+mark, followed by a lower case 'p', then a lower case 'z', then a return
+(which is the same as a ^M). The {1G} moves you to line 1, i.e. the beginning
+of the file. The {'p} moves you to the location you marked with {mp}. The
+{z^M} command will repaint the screen putting the cursor at the top of the
+screen. (Now {^F}.)
+
+Section 7: {m} {G} {'} {z}
+Let's look at some variations on those commands. If you wanted to look at
+line 22 in the file and return to this location you could type {mp22G'p}. Do
+so now, observing that {22G} puts your cursor at the beginning of section 2 in
+the middle of the screen.
+
+Also note that, without the {z^M} command, the line with 'Section 7' on it is
+now in the MIDDLE of the screen, and not at the top. Our cursor is on the
+correct line (where we did the {mp} command) but the line is not where we
+might like it to be on the screen. That is the function of the {z^M} command.
+(Remember, ^M is the same as the 'return' key on your keyboard.) Type {z^M}
+now and observe the effect.
+
+As you can see, the 'Section 7' line is now at the top of the screen with the
+cursor happily under the capital S. If you would like the cursor line (i.e.
+the line which the cursor is on) in the middle of the screen again, you would
+type {z.}. If you wanted the cursor line to be at the BOTTOM of the screen,
+type {z-}. Try typing {z-z.z^M} and watch what happens.
+
+{^F}
+
+Section 8: {z} {m} {'}
+
+Note that the z command does not change the position of our cursor in the file
+itself, it simply moves the cursor around on the screen by moving the contents
+of the file around on the screen. The cursor stays on the same line of the
+file when using the z command.
+
+This brings up an important point. There are two questions that the users of
+vi continually need to know the answer to: "Where am I in the file?" and
+"Where am I on the screen?" The cursor on your terminal shows the answer to
+both questions. Some commands will move you around in the file, usually
+changing the location of the cursor on the screen as well. Other commands
+move the cursor around on the screen without changing your location in the
+file.
+
+Now type {ma}. Your location in the file has been given the name 'a'. If you
+type {'p'a} you will see the previous location we marked in section 7, and
+then will be returned to the current location. (You will want to do a {z^M}
+to repaint the screen afterwards.) Try it.
+{^F}
+
+Section 9: {m} {''}
+Now we can move about in our file pretty freely. By using the {m} command we
+can give the current cursor position a lower-case-character name, like 'p',
+'a', 'e', 'm', or 'b'. Using the {G} command preceded by a line number we can
+look at any line in the file we like. Using the single quote command {'}
+followed by a character used in an {m} command, we can return to any location
+in the file we have marked.
+
+However, try {m3}, or {mM}. You should hear a beep, or bell. Only lower-case
+letters are acceptable to the {m} and {'} commands: numbers, upper-case
+letters, and special characters are not acceptable.
+
+If you type the {'} command with a character that is lower-case alphabetic but
+that has not been used in an {m} command, or for which the 'marked' text has
+been deleted, you will also get a beep. Try {'i}. You should get a beep
+because the command {mi} has never been issued. (Unless you've been
+experimenting.)
+
+The command {''} attempts to return you to the location at which you last
+modified some part of your file. However, my experience has been that it is
+difficult to predict exactly where you will end up.
+Section 10: {^M} {-}
+Now do {ma}, marking your position at the top of the screen. Now hit {^M} (or
+return) until the cursor is right ...
+* <- here, over/under the asterisk. Now
+type {mb'a'b} and watch the cursor move from the asterisk to the top of the
+screen and back again.
+
+The {^M} command moves the cursor to the beginning of the next line. Now type
+{^M} until the cursor is right ...
+* <- here. The command to move the cursor to the beginning of the
+previous line is {-}. Practice moving the cursor around on the screen by using
+{^M} and {-}. BE CAREFUL to not move the cursor OFF the screen just yet. If
+you do, type {'az^M}.
+
+Now we can move to any line within the screen. Practice moving around in the
+file using the {^F}, {^B}, {-}, {^M}, {z}, and {'} commands. When you are
+fairly confident that you can get to where you need to be in the file, and
+position the cursor on the screen where you want it type {'az^M^F} (which, of
+course, moves you back to the beginning of this section, repositions the
+cursor at the top of the screen, and advances you to the next section).
+
+Section 11: scrolling: {^M}
+The cursor should now be on the S of 'Section 11', and this should be on the
+first line of the screen. If it is not, do {^M} or {-} as appropriate to put
+the cursor on the section line, and type {z^M}.
+
+Type {mc} to mark your place.
+
+Now type {^M} until the cursor is on the last line of this screen. Now do one
+more {^M} and observe the result. This is called scrolling. When you
+attempted to move to a line not displayed on the screen, the line at the top of
+the screen was 'scrolled off', and a line at the bottom of the screen was
+'scrolled on'. The top line with 'Section 11' should no longer be visible.
+
+Now type {'cz^M} to reset the screen and type {^F} for the next section.
+
+
+
+
+
+
+
+Section 12: {-} {z}
+
+The {-} command moves the cursor to the previous line in the file. Now type
+{-}, which attempts to move the cursor to the previous line in this file.
+However, that line is not on the screen. The resulting action will depend on
+your terminal. (Do a {^Mz^M} to reposition the file). On intelligent
+terminals (e.g. VT100s, Z19s, Concept 100s), a top line is 'scrolled on' and
+the bottom line is 'scrolled off'. Other terminals, however, may not have
+this 'reverse scrolling' feature. They will simply repaint the screen with
+the cursor line in the middle of the screen. On such terminals it is
+necessary to type {z^M} to get the cursor line back to the top of the screen.
+
+
+
+
+
+
+
+
+
+
+Section 13:
+Up until this point, the tutorial has always tried to make sure that the first
+line of each screen has on it the section number and a list of the commands
+covered in that section. This will no longer be strictly maintained. If you
+want the section line at the top of the screen, you now know enough commands to
+do it easily: do {^M} or {-} until the cursor is on the section line and
+then {z^M}. Also, from this point on, it may not be the case that a {^F} will
+put you at the beginning of the next section. Therefore, be aware of where you
+are in the file as we look at other commands. You may have to find your way
+back to a particular section without any help from the tutorial. If you do not
+feel comfortable with this, then it is suggested that you practice moving from
+section 1 to section 13, back and forth, using {^M}, {-}, {^F}, and {^B}
+commands for a while.
+
+Also make liberal use of the mark command {m}: if, for example, you make a
+habit of using {mz} to mark your current location in the file, then you will
+always be able to return to that location with {'z} if the editor does
+something strange and you have no idea where you are or what happened.
+
+And finally, the proscription against experimentation is hereby lifted: play
+with the editor. Feel free to try out variations on the commands and move
+around in the file. By this time you should be able to recover from any gross
+errors.
+
+Section 14: {^E} {^Y} {^D} {^U}
+Let us now look at a few other commands for moving around in the file, and
+moving the file around on the screen. Note that the commands we have already
+looked at are sufficient: you really don't need any more commands for looking
+in a file. The following commands are not absolutely necessary. However,
+they can make editing more convenient, and you should take note of their
+existence. But it would be perfectly valid to decide to ignore them on this
+first pass: you can learn them later when you see a need for them, if you ever
+do.
+
+First, let's clear up some potentially confusing language. In at least one
+place in the official document ('An Introduction to Display Editing with Vi'
+by William Joy, and Mark Horton, September 1980), the expression "to scroll
+down text" means that the cursor is moved down in your file. However, note
+that this may result in the text on the screen moving UP. This use of the
+word 'scroll' refers to the action of the cursor within the file. However,
+another legitimate use of the word refers to the action of the text on the
+screen. That is, if the lines on your screen move up toward the top of the
+screen, this would be 'scrolling the screen up'. If the lines move down
+toward the bottom of the screen, this would be refered to as scrolling down.
+
+I have tried to maintain the following jargon: 'scrolling' refers to what the
+text does on the screen, not to what the cursor does within the file. For the
+latter I will refer to the cursor 'moving', or to 'moving the cursor'. I
+realize that this is not necessarily consistent with Joy and Horton, but they
+were wrong.
+
+{^E} scrolls the whole screen up one line, keeping the cursor on the same line,
+if possible. However, if the cursor line is the first line on the screen, then
+the cursor is moved to the next line in the file. Try typing {^E}.
+
+{^Y} scrolls the screen down one line, keeping the cursor on the same line, if
+possible. However, if the cursor line is the last line on the screen, then the
+cursor is moved to the previous line in the file. Try it.
+
+{^D} moves the cursor down into the file, scrolling the screen up.
+
+{^U} moves the cursor up into the file, also scrolling the screen if the
+terminal you are on has the reverse scroll capability. Otherwise the
+screen is repainted.
+
+Note that {^E} and {^Y} move the cursor on the screen while trying to keep the
+cursor at the same place in the file (if possible: however, the cursor can
+never move off screen), while {^D} and {^U} keep the cursor at the same place
+on the screen while moving the cursor within the file.
+
+Section 15: {/ .. /^M}
+
+Another way to position yourself in the file is by giving the editor a string
+to search for. Type the following: {/Here 1/^M} and the cursor should end up
+right ...........................here ^. Now type {/Section 15:/^M} and the
+cursor will end up over/on .....................here ^. Now type {//^M} and
+observe that the cursor is now over the capital S five lines above this line.
+Typing {//^M} several more times will bounce the cursor back and forth between
+the two occurrences of the string. In other words, when you type a string
+between the two slashes, it is searched for. Typing the slashes with nothing
+between them acts as if you had typed the previous string again.
+
+Observe that the string you type between the two slashes is entered on the
+bottom line of the screen. Now type {/Search for x /^M} except replace the 'x'
+in the string with some other character, say 'b'. The message "Pattern not
+found" should appear on the bottom of the screen. If you hadn't replaced the
+'x', then you would have found the string. Try it.
+
+Section 16: {? .. ?^M} {n} (search strings: ^ $)
+
+When you surround the sought-for string with slashes as in {/Search/}, the
+file is searched beginning from your current position in the file. If the
+string is not found by the end of the file, searching is restarted at the
+beginning of the file. However, if you do want the search to find the
+PREVIOUS rather than the NEXT occurrence of the string, surround the string
+with question marks instead of slash marks.
+
+Below are several occurrences of the same string.
+Here 2 Here 2 Here 2
+ Here 2 Here 2.
+Observe the effect of the following search commands (try them in the
+sequence shown):
+{/Here 2/^M} {//^M} {??^M}
+{/^Here 2/^M} {//^M} {??^M}
+{/Here 2$/^M} {//^M} {??^M}
+
+The first command looks for the next occurrence of the string 'Here 2'.
+However the second line of commands looks for an occurrence of 'Here 2' that
+is at the beginning of the line. When the up-arrow is the first character of
+a search string it stands for the beginning of the line. When the dollar-sign
+is the last character of the search string it stands for the end of the line.
+Therefore, the third line of commands searches for the string only when it is
+at the end of the line. Since there is only one place the string begins a
+line, and only one place the string ends the line, subsequent {//^M} and
+{??^M} will find those same strings over and over.
+
+The {n} command will find the next occurrence of the / or ? search
+string. Try {/Here 2/^M} followed by several {n} and observe the
+effect. Then try {??^M} followed by several {n}. The {n} command
+remembers the direction of the last search. It is just a way to save a
+few keystrokes.
+
+Section 17: \ and magic-characters in search strings
+
+Now type {/Here 3$/^M}. You might expect the cursor to end up
+right......^ here. However, you will get "Pattern not found" at the bottom of
+the screen. Remember that the dollar-sign stands for the end of the line.
+Somehow, you must tell vi that you do not want the end of the line, but a
+dollar-sign. In other words, you must take away the special meaning that the
+dollar-sign has for the search mechanism. You do this (for any special
+character, including the up-arrow ^) by putting a back-slash ('\', not '/') in
+front of the character.
+
+Now try {/Here 3\$/^M} and you should end up nine lines above this one. Try
+{//^M} and note that it returns you to the same place, and not to the first
+line of this paragraph: the back-slash character is not part of the search
+string and will not be found. To find the string in the first line of this
+paragraph, type {/Here 3\\\$/^M}. There are three back-slashes: the first takes
+away the special meaning from the second, and the third takes away the special
+meaning from the dollar-sign.
+
+Following is a list of the characters that have special meanings in search
+strings. If you wish to find a string containing one of these characters, you
+will have to be precede the character with a backslash. These characters are
+called magic characters because of the fun and games you can have with them
+and they can have with you, if you aren't aware of what they do.
+
+ ^ - (up-arrow) beginning of a line
+ $ - (dollar-sign) end of a line
+ . - (period) matches any character
+ \ - (backslant) the escape character itself
+ [ - (square bracket) for finding patterns (see section #SEARCH)
+ ] - (square bracket) ditto
+ * - (asterisk) ditto
+
+Without trying to explain it here, note that {:set nomagic^M} turns off the
+special meanings of all but the ^ up-arrow, $ dollar-sign, and backslash
+characters.
+
+Section 18: {: (colon commands)} {ZZ}
+
+In this section we will discuss getting into and out of the editor in more
+detail. If you are editing a file and wish to save the results the command
+sequence {:w^M} writes the current contents of the file out to disk, using the
+file name you used when you invoked the editor. That is, if you are at the
+command level in Unix, and you invoke vi with {vi foo} where foo is the name
+of the file you wish to edit, then foo is the name of the file used by the
+{:w^M} command.
+
+If you are done, the write and quit commands can be combined into a single
+command {:wq^M}. An even simpler way is the command {ZZ} (two capital Z's).
+
+If, for some reason, you wish to exit without saving any changes you have made,
+{:q!^M} does the trick. If you have not made any changes, the exclamation
+point is not necessary: {:q^M}. Vi is pretty good about not letting you
+get out without warning you that you haven't saved your file.
+
+We have mentioned before that you are currently in the vi editor, editing a
+file. If you wish to start the tutorial over from the very beginning, you
+could {ZZ}, and then type {vi.tut beginner} in response to the Unix prompt.
+This will create a fresh copy of this file for you, which might be necessary
+if you accidentally destroyed the copy you were working with. Just do a
+search for the last section you were in: e.g. {/Section 18:/^Mz^M}.
+
+Section 19: {H} {M} {L}
+
+Here are a few more commands that will move you around on the screen. Again,
+they are not absolutely necessary, but they can make screen positioning easier:
+
+{H} - puts the cursor at the top of the screen (the 'home' position)
+
+{M} - puts the cursor in the middle of the screen
+
+{L} - puts the cursor at the bottom of the screen.
+
+Try typing {HML} and watch the cursor.
+
+Try typing {5HM5L} and note that 5H puts you five lines from the top of the
+screen, and 5L puts you five lines from the bottom of the screen.
+
+Section 20: {w} {b} {0} {W} {B} {e} {E} {'} {`}
+
+Up to this point we have concentrated on positioning in the file, and
+positioning on the screen. Now let's look at positioning in a line. Put the
+cursor at the beginning of the following line and type {z^M}:
+
+This is a test line: your cursor should initially be at its beginning.
+
+The test line should now be at the top of your screen. Type {w} several times.
+Note that it moves you forward to the beginning of the next word. Now type
+{b} (back to the beginning of the word) several times till you are at the
+beginning of the line. (If you accidentally type too many {b}, type {w} until
+you are on the beginning of the line again.) Type {wwwww} (five w's) and note
+that the cursor is now on the colon in the sentence. The lower-case w command
+moves you forward one word, paying attention to certain characters such as
+colon and period as delimiters and counting them as words themselves. Now
+type {0} (zero, not o 'oh'): this moves you to the beginning of the current
+line. Now type {5w} and notice that this has the effect of repeating {w} five
+times and that you are now back on the colon. Type {0} (zero) again. To
+ignore the delimiters and to move to the beginning of the next word using only
+blanks, tabs and carriage-returns (these are called white-space characters) to
+delimit the words, use the {W} command: upper-case W. {B} takes you back a
+word using white-space characters as word delimiters.
+
+Note that the commands {wbWB} do not stop at the beginning or end of a line:
+they will continue to the next word on the next line in the direction specified
+(a blank line counts as a word).
+
+If you are interested in the END of the word, and not the BEGINNING, then use
+the {e} and {E} commands. These commands only move forward and there are no
+corresponding 'reverse search' commands for the end of a word.
+
+Also, we have been using the {'} command to move the cursor to a position that
+we have previously marked with the {m} command. However, position the cursor
+in the middle of a line (any line, just pick one) and type {mk}, marking that
+position with the letter k. Now type a few returns {^M} and type {'k}.
+Observe that the cursor is now at the beginning of the line that you marked.
+Now try {`k}: note that this is the reverse apostrophe, or back-quote, or grave
+accent, or whatever you want to call it. Also note that it moves you to the
+character that was marked, not just to the line that was marked.
+
+In addition, the {``} command works just like the {''} command except that you
+are taken to the exact character, not just to the line. (I'm still not
+sure which exact character, just as I'm still not sure which line.)
+
+Section 21: {l} {k} {j} {h}
+
+There are several commands to move around on the screen on a character by
+character basis:
+
+l - moves the cursor one character to the RIGHT
+k - moves the cursor UP one line
+j - moves the cursor DOWN one line
+h - moves the cursor one character to the LEFT
+
+Section 22: {i} {a} {I} {A} {o} {O} ^[ (escape key)
+
+For this and following sections you will need to use the ESCAPE key on your
+terminal. It is usually marked ESC. Since the escape key is the same as
+typing {^[} we will use ^[ for the escape key.
+
+Probably the most often used command in an editor is the insert command. Below
+are two lines of text, the first correct, the second incorrect. Position your
+cursor at the beginning of Line 1 and type {z^M}.
+
+Line 1: This is an example of the insert command.
+Line 2: This is an of the insert command.
+
+To make line 2 look like line 1, we are going to insert the characters
+'example ' before the word 'of'. So, now move the cursor so that it is
+positioned on the 'o' of 'of'. (You can do this by typing {^M} to move
+to the beginning of line 2, followed by {6w} or {wwwwww} to position the cursor
+on the word 'of'.)
+
+Now carefully type the following string and observe the effects:
+ {iexample ^[} (remember: ^[ is the escape key)}
+The {i} begins the insert mode, and 'example ' is inserted into the line:
+be sure to notice the blank in 'example '. The ^[ ends insertion mode,
+and the line is updated to include the new string. Line 1 should look exactly
+like Line 2.
+
+Move the cursor to the beginning of Line 3 below and type {z^M}:
+
+Line 3: These lines are examples for the 'a' command.
+Line 4: These line are examples for the '
+
+We will change line four to look like line three by using the append command.
+We need to append an 's' to the word 'line'. Position the cursor on the 'e'
+of 'line'. You can do this in several ways, one way is the following:
+First, type {/line /^M}. This puts us on the word 'line' in Line 4
+(the blank in the search string is important!). Next, type {e}. The 'e' puts
+us at the end of the word. Now, type {as^[ (^[ is the escape character)}.
+The 'a' puts us in insert mode, AFTER the current character. We appended the
+'s', and the escape ^[ ended the insert mode.
+
+The difference between {i} (insert) and {a} (append) is that {i} begins
+inserting text BEFORE the cursor, and {a} begins inserting AFTER the cursor.
+
+Now type {Aa' command.^[}. The cursor is moved to the end of the line and the
+string following {A} is inserted into the text. Line 4 should now look like
+line 3.
+
+Just as {A} moves you to the end of the line to begin inserting, {I} would
+begin inserting at the FRONT of the line.
+
+To begin the insertion of a line after the cursor line, type {o}. To insert a
+line before the cursor line, type {O}. In other words {o123^[} is equivalent
+to {A^M123^[}, and {O123^[} is equivalent to {I123^M^[}. The text after the
+{o} or {O} is ended with an escape ^[.
+
+This paragraph contains information that is terminal dependent: you will just
+have to experiment to discover what your terminal does. Once in the insert
+mode, if you make a mistake in the typing, ^H will delete the previous
+character up to the beginning of the current insertion. ^W will delete the
+previous word, and one of ^U, @, or ^X will delete the current line (up to the
+beginning of the current insertion). You will need to experiment with ^U, @,
+and ^X to determine which works for your terminal.
+
+Section 23: {f} {x} {X} {w} {l} {r} {R} {s} {S} {J}
+
+Position the cursor at the beginning of line 5 and {z^M}:
+
+Line 5: The line as it should be.
+Line 6: The line as it shouldn't be.
+
+To make Line 6 like Line 5, we have to delete the 'n', the apostrophe, and the
+'t'. There are several ways to position ourselves at the 'n'. Choose
+whichever one suits your fancy:
+
+{/n't/^M}
+{^M7w6l} or {^M7w6 } (note the space)
+{^M3fn} (finds the 3rd 'n' on the line)
+
+Now {xxx} will delete the three characters, as will {3x}.
+
+Note that {X} deletes the character just BEFORE the cursor, as opposed
+to the character AT the cursor.
+
+Position the cursor at line 7 and {z^M}:
+
+Line 7: The line as it would be.
+Line 8: The line as it could be.
+
+To change line 8 into line 7 we need to change the 'c' in 'could' into a 'w'.
+The 'r' (replace) command was designed for this. Typing {rc} is the same as
+typing {xic^[} (i.e. delete the 'bad' character and insert the correct
+new character). Therefore, assuming that you have positioned the cursor on the
+'c' of 'could', the easiest way to change 'could' into 'would' is {rw}.
+
+If you would like to now change the 'would' into 'should', use the substitute
+command, 's': {ssh^[}. The difference between 'r' and 's' is that 'r'
+(replace) replaces the current character with another character, while 's'
+(substitute) substitutes the current character with a string, ended with an
+escape.
+
+The capital letter version of replace {R} replaces each character by a
+character one at a time until you type an escape, ^[. The 'S' command
+substitutes the whole line.
+
+Position your cursor at the beginning of line 9 and {z^M}.
+
+Line 9: Love is a many splendored thing.
+Line 10: Love is a most splendored thing.
+
+To change line 10 into line 9, position the cursor at the beginning of 'most',
+and type {Rmany^[}.
+
+You may have noticed that, when inserting text, a new line is formed by typing
+{^M}. When changing, replacing, or substituting text you can make a new line
+by typing {^M}. However, neither {x} nor {X} will remove ^M to make two lines
+into one line. To do this, position the cursor on the first of the two lines
+you wish to make into a single line and type {J} (uppercase J for 'Join').
+
+Section 24: {u} {U}
+
+Finally, before we review, let's look at the undo command. Position
+your cursor on line 11 below and {z^M}.
+
+Line 11: The quick brown fox jumped over the lazy hound dog.
+Line 12: the qwick black dog dumped over the laxy poune fox.
+
+Type the following set of commands, and observe carefully the effect of each
+of the commands:
+
+{/^Line 12:/^M} {ft} {rT} {fw} {ru} {w} {Rbrown fox^[} {w} {rj}
+{fx} {rz} {w} {Rhound dog^[}
+
+Line 12 now matches line 11. Now type {U} - capital 'U'. And line 12 now
+looks like it did before you typed in the command strings. Now type:
+
+{ft} {rT} {fw} {ru} {^M} {^M}
+
+and then type {u}: the cursor jumps back to the line containing the second
+change you made and 'undoes' it. That is, {U} 'undoes' all the changes on the
+line, and {u} 'undoes' only the last change. Type {u} several times and
+observe what happens: {u} can undo a previous {u}!
+
+Caveat: {U} only works as long as the cursor is still on the line. Move the
+cursor off the line and {U} will have no effect, except to possibly beep at
+you. However, {u} will undo the last change, no matter where it occurred.
+
+Section 25: review
+
+At this point, you have all the commands you need in order to make use of vi.
+The remainder of this tutorial will discuss variations on these commands as
+well as introduce new commands that make the job of editing more efficient.
+Here is a brief review of the basic commands we have covered. They are listed
+in the order of increasing complexity and/or decreasing necessity (to say that
+a command is less necessary is not to say that it is less useful!). These
+commands allow you to comfortably edit any text file. There are other
+commands that will make life easier but will require extra time to learn,
+obviously. You may want to consider setting this tutorial aside for several
+weeks and returning to it later after gaining experience with vi and getting
+comfortable with it. The convenience of some of the more exotic commands may
+then be apparent and worth the extra investment of time and effort
+required to master them.
+
+to get into the editor from Unix: {vi filename}
+to exit the editor
+ saving all changes {ZZ} or {:wq^M}
+ throwing away all changes {:q!^M}
+ when no changes have been made {:q^M}
+save a file without exiting the editor {:w^M}
+write the file into another file {:w filename^M}
+insert text
+ before the cursor {i ...text... ^[}
+ at the beginning of the line {I ...text... ^[}
+ after the cursor (append) {a ...text... ^[}
+ at the end of the line {A ...text... ^[}
+ after the current line {o ...text... ^[}
+ before the current line {O ...text... ^[}
+delete the character ...
+ under the cursor {x}
+ to the left of the cursor {X}
+delete n characters {nx} or {nX} (for n a number)
+make two lines into one line (Join) {J}
+find a string in the file ...
+ searching forward {/ ...string... /^M}
+ searching backwards {? ...string... ?^M}
+repeat the last search command {n}
+repeat the last search command in the
+ opposite direction {N}
+find the character c on this line ...
+ searching forward {fc}
+ searching backward {Fc}
+repeat the last 'find character' command {;}
+replace a character with character x {rx}
+substitute a single character with text {s ...text... ^[}
+substitute n characters with text {ns ...text... ^[}
+replace characters one-by-one with text {R ...text... ^[}
+undo all changes to the current line {U}
+undo the last single change {u}
+move forward in the file a "screenful" {^F}
+move back in the file a "screenful" {^B}
+move forward in the file one line {^M} or {+}
+move backward in the file one line {-}
+move to the beginning of the line {0}
+move to the end of the line {$}
+move forward one word {w}
+move forward one word, ignoring punctuation {W}
+move forward to the end of the next word {e}
+to the end of the word, ignoring punctuation{E}
+move backward one word {b}
+move back one word, ignoring punctuation {B}
+return to the last line modified {''}
+scroll a line onto the top of the screen {^Y}
+scroll a line onto the bottom of the screen {^E}
+move "up" in the file a half-screen {^U}
+move "down" in the file a half-screen {^D}
+move the cursor to the top screen line {H}
+move the cursor to the bottom screen line {L}
+move the cursor to the middle line {M}
+move LEFT one character position {h} or {^H}
+move RIGHT one character position {l} or { }
+move UP in the same column {k} or {^P}
+move DOWN in the same column {j} or {^N}
+mark the current position, name it x {mx}
+move to the line marked/named x {'x}
+move to the character position named x {`x}
+move to the beginning of the file {1G}
+move to the end of the file {G}
+move to line 23 in the file {23G}
+repaint the screen with the cursor line
+ at the top of the screen {z^M}
+ in the middle of the screen {z.}
+ at the bottom of the screen {z-}
+
+More information on vi can be found in the file vi.advanced, which you can
+peruse at your leisure. From UNIX, type {vi.tut advanced^M}.
diff --git a/usr.bin/vi/docs/tutorial/vi.tut.csh b/usr.bin/vi/docs/tutorial/vi.tut.csh
new file mode 100755
index 0000000..01554bc
--- /dev/null
+++ b/usr.bin/vi/docs/tutorial/vi.tut.csh
@@ -0,0 +1,24 @@
+#!/bin/csh -f
+#
+# This makes the user's EXINIT variable set to the 'correct' things.
+# I don't know what will happen if they also have a .exrc file!
+#
+# XXX
+# Make sure that user is using a 24 line window!!!
+#
+if ($1 != "beginner" && $1 != "advanced") then
+ echo Usage: $0 beginner or $0 advanced
+ exit
+endif
+
+if ($?EXINIT) then
+ set oexinit="$EXINIT"
+ setenv EXINIT 'se ts=4 wm=8 sw=4'
+endif
+
+vi vi.{$1}
+
+onintr:
+ if ($?oexinit) then
+ setenv EXINIT "$oexinit"
+endif
diff --git a/usr.bin/vi/ex/ex.c b/usr.bin/vi/ex/ex.c
new file mode 100644
index 0000000..66dddb0
--- /dev/null
+++ b/usr.bin/vi/ex/ex.c
@@ -0,0 +1,1866 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex.c 8.157 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static void badlno __P((SCR *, recno_t));
+static __inline EXCMDLIST const *
+ ex_comm_search __P((char *, size_t));
+static int ep_line __P((SCR *, EXF *, MARK *, char **, size_t *, int *));
+static int ep_range __P((SCR *, EXF *, EXCMDARG *, char **, size_t *));
+
+/*
+ * ex --
+ * Read an ex command and execute it.
+ */
+int
+ex(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ enum input irval;
+ TEXT *tp;
+ u_int flags, saved_mode;
+ int eval;
+
+ if (ex_init(sp, ep))
+ return (1);
+
+ if (sp->s_refresh(sp, ep))
+ return (ex_end(sp));
+
+ /* If reading from a file, messages should have line info. */
+ if (!F_ISSET(sp->gp, G_STDIN_TTY)) {
+ sp->if_lno = 1;
+ sp->if_name = strdup("input");
+ }
+
+ /*
+ * !!!
+ * Historically, the beautify option applies to ex command input read
+ * from a file. In addition, the first time a ^H was discarded from
+ * the input, a message "^H discarded" was displayed. We don't bother.
+ */
+ LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR | TXT_EXSUSPEND);
+
+ for (eval = 0;; ++sp->if_lno) {
+ /* Set the flags that the user can change. */
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+ else
+ LF_CLR(TXT_BEAUTIFY);
+ if (O_ISSET(sp, O_PROMPT))
+ LF_SET(TXT_PROMPT);
+ else
+ LF_CLR(TXT_PROMPT);
+
+ /*
+ * Get the next command. Interrupt flag manipulation is
+ * safe because ex_icmd clears them all.
+ */
+ CLR_INTERRUPT(sp);
+ F_SET(sp, S_INTERRUPTIBLE);
+ irval = sp->s_get(sp, ep, sp->tiqp, ':', flags);
+ if (INTERRUPTED(sp)) {
+ (void)fputc('\n', stdout);
+ (void)fflush(stdout);
+ goto refresh;
+ }
+ switch (irval) {
+ case INP_OK:
+ break;
+ case INP_EOF:
+ case INP_ERR:
+ F_SET(sp, S_EXIT_FORCE);
+ /* FALLTHROUGH */
+ case INP_INTR:
+ goto ret;
+ }
+
+ /*
+ * If the user entered a carriage return, send ex_cmd()
+ * a separator -- it discards single newlines.
+ */
+ tp = sp->tiqp->cqh_first;
+ if (tp->len == 0) {
+ tp->len = 1;
+ tp->lb[0] = ' ';
+ }
+
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ if (ex_icmd(sp, ep,
+ tp->lb, tp->len, 1) && !F_ISSET(sp->gp, G_STDIN_TTY))
+ F_SET(sp, S_EXIT_FORCE);
+ (void)msg_rpt(sp, 0);
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+refresh: if (sp->s_refresh(sp, ep)) {
+ eval = 1;
+ break;
+ }
+ }
+ret: if (sp->if_name != NULL) {
+ FREE(sp->if_name, strlen(sp->if_name) + 1);
+ sp->if_name = NULL;
+ }
+ return (ex_end(sp) || eval);
+}
+
+/*
+ * ex_cfile --
+ * Execute ex commands from a file.
+ */
+int
+ex_cfile(sp, ep, filename, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *filename;
+ int needsep;
+{
+ struct stat sb;
+ int fd, len, rval;
+ char *bp;
+
+ bp = NULL;
+ if ((fd = open(filename, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+ goto err;
+
+ /*
+ * XXX
+ * We'd like to test if the file is too big to malloc. Since we don't
+ * know what size or type off_t's or size_t's are, what the largest
+ * unsigned integral type is, or what random insanity the local C
+ * compiler will perpetrate, doing the comparison in a portable way
+ * is flatly impossible. Hope that malloc fails if the file is too
+ * large.
+ */
+ MALLOC(sp, bp, char *, (size_t)sb.st_size + 1);
+ if (bp == NULL)
+ goto err;
+
+ len = read(fd, bp, (int)sb.st_size);
+ if (len == -1 || len != sb.st_size) {
+ if (len != sb.st_size)
+ errno = EIO;
+err: rval = 1;
+ msgq(sp, M_SYSERR, filename);
+ } else {
+ bp[sb.st_size] = '\0'; /* XXX */
+
+ /*
+ * Run the command. Messages include file/line information,
+ * but we don't care if we can't get space.
+ */
+ sp->if_lno = 1;
+ sp->if_name = strdup(filename);
+ F_SET(sp, S_VLITONLY);
+ rval = ex_icmd(sp, ep, bp, len, needsep);
+ F_CLR(sp, S_VLITONLY);
+ free(sp->if_name);
+ sp->if_name = NULL;
+ }
+
+ /*
+ * !!!
+ * THE UNDERLYING EXF MAY HAVE CHANGED.
+ */
+ if (bp != NULL)
+ FREE(bp, sb.st_size);
+ if (fd >= 0)
+ (void)close(fd);
+ return (rval);
+}
+
+/*
+ * ex_icmd --
+ * Call ex_cmd() after turning off interruptible bits.
+ */
+int
+ex_icmd(sp, ep, cmd, len, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t len;
+ int needsep;
+{
+ /*
+ * Ex goes through here for each vi :colon command and for each ex
+ * command, however, globally executed commands don't go through
+ * here, instead, they call ex_cmd directly. So, reset all of the
+ * interruptible flags now.
+ *
+ * !!!
+ * Previous versions of nvi cleared mapped characters on error. This
+ * feature was removed when users complained that it wasn't historic
+ * practice.
+ */
+ CLR_INTERRUPT(sp);
+ return (ex_cmd(sp, ep, cmd, len, needsep));
+}
+
+/* Special command structure for :s as a repeat substitution command. */
+static EXCMDLIST const cmd_subagain =
+ {"s", ex_subagain, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] s [cgr] [count] [#lp]",
+ "repeat the last subsitution"};
+
+/* Special command structure for :d[flags]. */
+static EXCMDLIST const cmd_del2 =
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "1bca1",
+ "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
+ "delete lines from the file"};
+
+/*
+ * ex_cmd --
+ * Parse and execute a string containing ex commands.
+ */
+int
+ex_cmd(sp, ep, cmd, cmdlen, needsep)
+ SCR *sp;
+ EXF *ep;
+ char *cmd;
+ size_t cmdlen;
+ int needsep;
+{
+ enum { NOTSET, NEEDSEP_N, NEEDSEP_NR, NONE } sep;
+ EX_PRIVATE *exp;
+ EXCMDARG exc;
+ EXCMDLIST const *cp;
+ MARK cur;
+ recno_t lno, num;
+ size_t arg1_len, len, save_cmdlen;
+ long flagoff;
+ u_int saved_mode;
+ int blank, ch, cnt, delim, flags, namelen, nl;
+ int optnum, uselastcmd, tmp, vi_address;
+ char *arg1, *save_cmd, *p, *s, *t;
+
+ /* Init. */
+ nl = 0;
+ sep = needsep ? NOTSET : NONE;
+loop: if (nl) {
+ nl = 0;
+ ++sp->if_lno;
+ }
+ arg1 = NULL;
+ save_cmdlen = 0;
+
+ /* It's possible that we've been interrupted during a command. */
+ if (INTERRUPTED(sp))
+ return (0);
+
+ /* Skip <blank>s, empty lines. */
+ for (blank = 0; cmdlen > 0; ++cmd, --cmdlen)
+ if ((ch = *cmd) == '\n')
+ ++sp->if_lno;
+ else if (isblank(ch))
+ blank = 1;
+ else
+ break;
+
+ /*
+ * !!!
+ * Permit extra colons at the start of the line. Historically,
+ * ex/vi allowed a single extra one. It's simpler not to count.
+ * The stripping is done here because, historically, any command
+ * could have preceding colons, e.g. ":g/pattern/:p" worked.
+ */
+ if (cmdlen != 0 && ch == ':') {
+ if (sep == NOTSET)
+ sep = NEEDSEP_N;
+ while (--cmdlen > 0 && (ch = *++cmd) == ':');
+ }
+
+ /*
+ * Command lines that start with a double-quote are comments.
+ *
+ * !!!
+ * Historically, there was no escape or delimiter for a comment,
+ * e.g. :"foo|set was a single comment and nothing was output.
+ * Since nvi permits users to escape <newline> characters into
+ * command lines, we have to check for that case.
+ */
+ if (cmdlen != 0 && ch == '"') {
+ while (--cmdlen > 0 && *++cmd != '\n');
+ if (*cmd == '\n') {
+ nl = 1;
+ ++cmd;
+ --cmdlen;
+ }
+ goto loop;
+ }
+
+ /* Skip whitespace. */
+ for (; cmdlen > 0; ++cmd, --cmdlen) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * The last point at which an empty line can mean do nothing.
+ *
+ * !!!
+ * Historically, in ex mode, lines containing only <blank> characters
+ * were the same as a single <carriage-return>, i.e. a default command.
+ * In vi mode, they were ignored.
+ *
+ * In .exrc files this was a serious annoyance, as vi kept trying to
+ * treat them as print commands. We ignore backward compatibility in
+ * this case, and discard lines containing only <blank> characters from
+ * .exrc files.
+ */
+ if (cmdlen == 0 && (!IN_EX_MODE(sp) || ep == NULL || !blank))
+ return (0);
+
+ /* Initialize the structure passed to underlying functions. */
+ memset(&exc, 0, sizeof(EXCMDARG));
+ exp = EXP(sp);
+ if (argv_init(sp, ep, &exc))
+ goto err;
+
+ /*
+ * Check to see if this is a command for which we may want to output
+ * a \r separator instead of a \n. (The command :1<CR> puts out a \n,
+ * but the command :<CR> puts out a \r.) If the line is empty except
+ * for <blank>s, <carriage-return> or <eof>, we'll probably want to
+ * output \r. I don't think there's any way to get <blank> characters
+ * *after* the command character, but this is the ex parser, and I've
+ * been wrong before.
+ */
+ if (sep == NOTSET)
+ sep = cmdlen == 0 || cmdlen == 1 && cmd[0] == '\004' ?
+ NEEDSEP_NR : NEEDSEP_N;
+
+ /* Parse command addresses. */
+ if (ep_range(sp, ep, &exc, &cmd, &cmdlen))
+ goto err;
+
+ /* Skip whitespace. */
+ for (; cmdlen > 0; ++cmd, --cmdlen) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * If no command, ex does the last specified of p, l, or #, and vi
+ * moves to the line. Otherwise, determine the length of the command
+ * name by looking for the first non-alphabetic character. (There
+ * are a few non-alphabetic characters in command names, but they're
+ * all single character commands.) This isn't a great test, because
+ * it means that, for the command ":e +cut.c file", we'll report that
+ * the command "cut" wasn't known. However, it makes ":e+35 file" work
+ * correctly.
+ *
+ * !!!
+ * Historically, lines with multiple adjacent (or <blank> separated)
+ * command separators were very strange. For example, the command
+ * |||<carriage-return>, when the cursor was on line 1, displayed
+ * lines 2, 3 and 5 of the file. In addition, the command " | "
+ * would only display the line after the next line, instead of the
+ * next two lines. No ideas why. It worked reasonably when executed
+ * from vi mode, and displayed lines 2, 3, and 4, so we do a default
+ * command for each separator.
+ */
+#define SINGLE_CHAR_COMMANDS "\004!#&*<=>@~"
+ if (cmdlen != 0 && cmd[0] != '|' && cmd[0] != '\n') {
+ if (strchr(SINGLE_CHAR_COMMANDS, *cmd)) {
+ p = cmd;
+ ++cmd;
+ --cmdlen;
+ namelen = 1;
+ } else {
+ for (p = cmd; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isalpha(*cmd))
+ break;
+ if ((namelen = cmd - p) == 0) {
+ msgq(sp, M_ERR, "Unknown command name");
+ goto err;
+ }
+ }
+
+ /*
+ * !!!
+ * Historic vi permitted flags to immediately follow any
+ * subset of the 'delete' command, but then did not permit
+ * further arguments (flag, buffer, count). Make it work.
+ * Permit further arguments for the few shreds of dignity
+ * it offers.
+ *
+ * !!!
+ * Note, adding commands that start with 'd', and match
+ * "delete" up to a l, p, +, - or # character can break
+ * this code.
+ */
+ if (p[0] == 'd') {
+ for (s = p,
+ t = cmds[C_DELETE].name; *s == *t; ++s, ++t);
+ if (s[0] == 'l' || s[0] == 'p' ||
+ s[0] == '+' || s[0] == '-' || s[0] == '#') {
+ len = (cmd - p) - (s - p);
+ cmd -= len;
+ cmdlen += len;
+ cp = &cmd_del2;
+ goto skip;
+ }
+ }
+
+ /*
+ * Search the table for the command.
+ *
+ * !!!
+ * Historic vi permitted the mark to immediately follow the
+ * 'k' in the 'k' command. Make it work.
+ *
+ * !!!
+ * Historic vi permitted pretty much anything to follow the
+ * substitute command, e.g. "s/e/E/|s|sgc3p" was fine. Make
+ * the command "sgc" work.
+ */
+ if ((cp = ex_comm_search(p, namelen)) == NULL)
+ switch (p[0]) {
+ case 's':
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmd_subagain;
+ break;
+ case 'k':
+ if (p[1] && !p[2]) {
+ cmd -= namelen - 1;
+ cmdlen += namelen - 1;
+ cp = &cmds[C_K];
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ msgq(sp, M_ERR,
+ "The %.*s command is unknown", namelen, p);
+ goto err;
+ }
+
+ /* Some commands are either not implemented or turned off. */
+skip: if (F_ISSET(cp, E_NOPERM)) {
+ msgq(sp, M_ERR,
+ "The %s command is not currently supported",
+ cp->name);
+ goto err;
+ }
+
+ /* Some commands aren't okay in globals. */
+ if (F_ISSET(sp, S_GLOBAL) && F_ISSET(cp, E_NOGLOBAL)) {
+ msgq(sp, M_ERR,
+ "The %s command can't be used as part of a global command",
+ cp->name);
+ goto err;
+ }
+
+ /*
+ * Multiple < and > characters; another "feature". Note,
+ * The string passed to the underlying function may not be
+ * nul terminated in this case.
+ */
+ if ((cp == &cmds[C_SHIFTL] && *p == '<') ||
+ (cp == &cmds[C_SHIFTR] && *p == '>')) {
+ for (ch = *p; cmdlen > 0; --cmdlen, ++cmd)
+ if (*cmd != ch)
+ break;
+ if (argv_exp0(sp, ep, &exc, p, cmd - p))
+ goto err;
+ }
+
+ /*
+ * The visual command has a different syntax when called
+ * from ex than when called from a vi colon command. FMH.
+ */
+ if (cp == &cmds[C_VISUAL_EX] && IN_VI_MODE(sp))
+ cp = &cmds[C_VISUAL_VI];
+
+ /* Set the format style flags for the next command. */
+ if (cp == &cmds[C_HASH])
+ exp->fdef = E_F_HASH;
+ else if (cp == &cmds[C_LIST])
+ exp->fdef = E_F_LIST;
+ else if (cp == &cmds[C_PRINT])
+ exp->fdef = E_F_PRINT;
+ uselastcmd = 0;
+ } else {
+ /* Print is the default command. */
+ cp = &cmds[C_PRINT];
+
+ /* Set the saved format flags. */
+ F_SET(&exc, exp->fdef);
+
+ /*
+ * !!!
+ * If no address was specified, and it's not a global command,
+ * we up the address by one. (I have not an idea why global
+ * commands are exempted, but it's (ahem) historic practice.
+ */
+ if (exc.addrcnt == 0 && !F_ISSET(sp, S_GLOBAL)) {
+ exc.addrcnt = 1;
+ exc.addr1.lno = sp->lno + 1;
+ exc.addr1.cno = sp->cno;
+ }
+
+ uselastcmd = 1;
+ }
+
+ /*
+ * !!!
+ * Historically, the number option applied to both ex and vi. One
+ * strangeness was that ex didn't switch display formats until a
+ * command was entered, e.g. <CR>'s after the set didn't change to
+ * the new format, but :1p would.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ optnum = 1;
+ F_SET(&exc, E_F_HASH);
+ } else
+ optnum = 0;
+
+ /* Initialize local flags to the command flags. */
+ LF_INIT(cp->flags);
+
+ /*
+ * File state must be checked throughout this code, because it is
+ * called when reading the .exrc file and similar things. There's
+ * this little chicken and egg problem -- if we read the file first,
+ * we won't know how to display it. If we read/set the exrc stuff
+ * first, we can't allow any command that requires file state. We
+ * don't have a "reading an rc" bit, because we want the commands
+ * to work when the user source's the rc file later. Historic vi
+ * generally took the easy way out and dropped core.
+ */
+ if (LF_ISSET(E_NORC) && ep == NULL) {
+ msgq(sp, M_ERR,
+ "The %s command requires that a file have already been read in",
+ cp->name);
+ goto err;
+ }
+
+ /*
+ * There are three normal termination cases for an ex command. They
+ * are the end of the string (cmdlen), or unescaped (by literal next
+ * characters) newline or '|' characters. As we're past any addresses,
+ * we can now determine how long the command is, so we don't have to
+ * look for all the possible terminations. There are three exciting
+ * special cases:
+ *
+ * 1: The bang, global, vglobal and the filter versions of the read and
+ * write commands are delimited by newlines (they can contain shell
+ * pipes).
+ * 2: The ex, edit, next and visual in vi mode commands all take ex
+ * commands as their first arguments.
+ * 3: The substitute command takes an RE as its first argument, and
+ * wants it to be specially delimited.
+ *
+ * Historically, '|' characters in the first argument of the ex, edit,
+ * next, vi visual, and substitute commands didn't delimit the command.
+ * And, in the filter cases for read and write, and the bang, global
+ * and vglobal commands, they did not delimit the command at all.
+ *
+ * For example, the following commands were legal:
+ *
+ * :edit +25|s/abc/ABC/ file.c
+ * :substitute s/|/PIPE/
+ * :read !spell % | columnate
+ * :global/pattern/p|l
+ *
+ * It's not quite as simple as it sounds, however. The command:
+ *
+ * :substitute s/a/b/|s/c/d|set
+ *
+ * was also legal, i.e. the historic ex parser (using the word loosely,
+ * since "parser" implies some regularity) delimited the RE's based on
+ * its delimiter and not anything so irretrievably vulgar as a command
+ * syntax.
+ *
+ * One thing that makes this easier is that we can ignore most of the
+ * command termination conditions for the commands that want to take
+ * the command up to the next newline. None of them are legal in .exrc
+ * files, so if we're here, we only dealing with a single line, and we
+ * can just eat it.
+ *
+ * Anyhow, the following code makes this all work. First, for the
+ * special cases we move past their special argument(s). Then, we
+ * do normal command processing on whatever is left. Barf-O-Rama.
+ */
+ arg1_len = 0;
+ save_cmd = cmd;
+ if (cp == &cmds[C_EDIT] || cp == &cmds[C_EX] ||
+ cp == &cmds[C_NEXT] || cp == &cmds[C_VISUAL_VI]) {
+ /*
+ * Move to the next non-whitespace character. A '!'
+ * immediately following the command is eaten as a
+ * force flag.
+ */
+ if (cmdlen > 0 && *cmd == '!') {
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_FORCE);
+
+ /* Reset, don't reparse. */
+ save_cmd = cmd;
+ }
+ for (tmp = 0; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(*cmd))
+ break;
+ /*
+ * QUOTING NOTE:
+ *
+ * The historic implementation ignored all escape characters
+ * so there was no way to put a space or newline into the +cmd
+ * field. We do a simplistic job of fixing it by moving to the
+ * first whitespace character that isn't escaped by a literal
+ * next character. The literal next characters are stripped
+ * as they're no longer useful.
+ */
+ if (cmdlen > 0 && *cmd == '+') {
+ ++cmd;
+ --cmdlen;
+ for (arg1 = p = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ --cmdlen;
+ ch = *++cmd;
+ } else if (isblank(ch))
+ break;
+ *p++ = ch;
+ }
+ arg1_len = cmd - arg1;
+
+ /* Reset, so the first argument isn't reparsed. */
+ save_cmd = cmd;
+ }
+ } else if (cp == &cmds[C_BANG] ||
+ cp == &cmds[C_GLOBAL] || cp == &cmds[C_VGLOBAL]) {
+ cmd += cmdlen;
+ cmdlen = 0;
+ } else if (cp == &cmds[C_READ] || cp == &cmds[C_WRITE]) {
+ /*
+ * Move to the next character. If it's a '!', it's a filter
+ * command and we want to eat it all, otherwise, we're done.
+ */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen > 0 && ch == '!') {
+ cmd += cmdlen;
+ cmdlen = 0;
+ }
+ } else if (cp == &cmds[C_SUBSTITUTE]) {
+ /*
+ * Move to the next non-whitespace character, we'll use it as
+ * the delimiter. If the character isn't an alphanumeric or
+ * a '|', it's the delimiter, so parse it. Otherwise, we're
+ * into something like ":s g", so use the special substitute
+ * command.
+ */
+ for (; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(cmd[0]))
+ break;
+
+ if (isalnum(cmd[0]) || cmd[0] == '|')
+ cp = &cmd_subagain;
+ else if (cmdlen > 0) {
+ /*
+ * QUOTING NOTE:
+ *
+ * Backslashes quote delimiter characters for RE's.
+ * The backslashes are NOT removed since they'll be
+ * used by the RE code. Move to the third delimiter
+ * that's not escaped (or the end of the command).
+ */
+ delim = *cmd;
+ ++cmd;
+ --cmdlen;
+ for (cnt = 2; cmdlen > 0 && cnt; --cmdlen, ++cmd)
+ if (cmd[0] == '\\' && cmdlen > 1) {
+ ++cmd;
+ --cmdlen;
+ } else if (cmd[0] == delim)
+ --cnt;
+ }
+ }
+ /*
+ * Use normal quoting and termination rules to find the end of this
+ * command.
+ *
+ * QUOTING NOTE:
+ *
+ * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
+ * file. It was almost certainly a bug, but that's what bug-for-bug
+ * compatibility means, Grasshopper. Also, ^V's escape the command
+ * delimiters. Literal next quote characters in front of the newlines,
+ * '|' characters or literal next characters are stripped as as they're
+ * no longer useful.
+ */
+ vi_address = cmdlen != 0 && cmd[0] != '\n';
+ for (p = cmd, cnt = 0; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = cmd[0];
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ tmp = cmd[1];
+ if (tmp == '\n' || tmp == '|') {
+ if (tmp == '\n')
+ ++sp->if_lno;
+ --cmdlen;
+ ++cmd;
+ ++cnt;
+ ch = tmp;
+ }
+ } else if (ch == '\n' || ch == '|') {
+ if (ch == '\n')
+ nl = 1;
+ --cmdlen;
+ break;
+ }
+ *p++ = ch;
+ }
+
+ /*
+ * Save off the next command information, go back to the
+ * original start of the command.
+ */
+ p = cmd + 1;
+ cmd = save_cmd;
+ save_cmd = p;
+ save_cmdlen = cmdlen;
+ cmdlen = ((save_cmd - cmd) - 1) - cnt;
+
+ /*
+ * !!!
+ * The "set tags" command historically used a backslash, not the
+ * user's literal next character, to escape whitespace. Handle
+ * it here instead of complicating the argv_exp3() code. Note,
+ * this isn't a particularly complex trap, and if backslashes were
+ * legal in set commands, this would have to be much more complicated.
+ */
+ if (cp == &cmds[C_SET])
+ for (p = cmd, len = cmdlen; len > 0; --len, ++p)
+ if (*p == '\\')
+ *p = CH_LITERAL;
+
+ /*
+ * Set the default addresses. It's an error to specify an address for
+ * a command that doesn't take them. If two addresses are specified
+ * for a command that only takes one, lose the first one. Two special
+ * cases here, some commands take 0 or 2 addresses. For most of them
+ * (the E_ADDR2_ALL flag), 0 defaults to the entire file. For one
+ * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
+ *
+ * Also, if the file is empty, some commands want to use an address of
+ * 0, i.e. the entire file is 0 to 0, and the default first address is
+ * 0. Otherwise, an entire file is 1 to N and the default line is 1.
+ * Note, we also add the E_ZERO flag to the command flags, for the case
+ * where the 0 address is only valid if it's a default address.
+ *
+ * Also, set a flag if we set the default addresses. Some commands
+ * (ex: z) care if the user specified an address of if we just used
+ * the current cursor.
+ */
+ switch (LF_ISSET(E_ADDR1|E_ADDR2|E_ADDR2_ALL|E_ADDR2_NONE)) {
+ case E_ADDR1: /* One address: */
+ switch (exc.addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ exc.addrcnt = 1;
+ F_SET(&exc, E_ADDRDEF);
+ if (LF_ISSET(E_ZERODEF)) {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ exc.addr1.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = sp->lno;
+ } else
+ exc.addr1.lno = sp->lno;
+ exc.addr1.cno = sp->cno;
+ break;
+ case 1:
+ break;
+ case 2: /* Lose the first address. */
+ exc.addrcnt = 1;
+ exc.addr1 = exc.addr2;
+ }
+ break;
+ case E_ADDR2_NONE: /* Zero/two addresses: */
+ if (exc.addrcnt == 0) /* Default to nothing. */
+ break;
+ goto two;
+ case E_ADDR2_ALL: /* Zero/two addresses: */
+ if (exc.addrcnt == 0) { /* Default entire/empty file. */
+ exc.addrcnt = 2;
+ F_SET(&exc, E_ADDRDEF);
+ if (file_lline(sp, ep, &exc.addr2.lno))
+ goto err;
+ if (LF_ISSET(E_ZERODEF) && exc.addr2.lno == 0) {
+ exc.addr1.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = 1;
+ exc.addr1.cno = exc.addr2.cno = 0;
+ F_SET(&exc, E_ADDR2_ALL);
+ break;
+ }
+ /* FALLTHROUGH */
+ case E_ADDR2: /* Two addresses: */
+two: switch (exc.addrcnt) {
+ case 0: /* Default cursor/empty file. */
+ exc.addrcnt = 2;
+ F_SET(&exc, E_ADDRDEF);
+ if (LF_ISSET(E_ZERODEF) && sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno == 0) {
+ exc.addr1.lno = exc.addr2.lno = 0;
+ LF_SET(E_ZERO);
+ } else
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ } else
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ break;
+ case 1: /* Default to first address. */
+ exc.addrcnt = 2;
+ exc.addr2 = exc.addr1;
+ break;
+ case 2:
+ break;
+ }
+ break;
+ default:
+ if (exc.addrcnt) /* Error. */
+ goto usage;
+ }
+
+ /*
+ * !!!
+ * The ^D scroll command historically scrolled the value of the scroll
+ * option or to EOF. It was an error if the cursor was already at EOF.
+ * (Leading addresses were permitted, but were then ignored.)
+ */
+ if (cp == &cmds[C_SCROLL]) {
+ exc.addrcnt = 2;
+ exc.addr1.lno = sp->lno + 1;
+ exc.addr2.lno = sp->lno + O_VAL(sp, O_SCROLL);
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (lno != 0 && lno > sp->lno && exc.addr2.lno > lno)
+ exc.addr2.lno = lno;
+ }
+
+ flagoff = 0;
+ for (p = cp->syntax; *p != '\0'; ++p) {
+ /*
+ * The force flag is sensitive to leading whitespace, i.e.
+ * "next !" is different from "next!". Handle it before
+ * skipping leading <blank>s.
+ */
+ if (*p == '!') {
+ if (cmdlen > 0 && *cmd == '!') {
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_FORCE);
+ }
+ continue;
+ }
+
+ /* Skip leading <blank>s. */
+ for (; cmdlen > 0; --cmdlen, ++cmd)
+ if (!isblank(*cmd))
+ break;
+
+ /*
+ * Quit when reach the end of the command, unless it's a
+ * command that does its own parsing, in which case we want
+ * to build a reasonable argv for it. This code guarantees
+ * that there will be an argv when the function gets called,
+ * so the correct test is for a length of 0, not for the
+ * argc > 0. Since '!' can precede commands that do their
+ * own parsing, we have to have already handled it.
+ */
+ if (cmdlen == 0 && *p != 'S' && *p != 's')
+ break;
+
+ switch (*p) {
+ case '1': /* +, -, #, l, p */
+ /*
+ * !!!
+ * Historically, some flags were ignored depending
+ * on where they occurred in the command line. For
+ * example, in the command, ":3+++p--#", historic vi
+ * acted on the '#' flag, but ignored the '-' flags.
+ * It's unambiguous what the flags mean, so we just
+ * handle them regardless of the stupidity of their
+ * location.
+ */
+ for (; cmdlen; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '+':
+ ++flagoff;
+ break;
+ case '-':
+ --flagoff;
+ break;
+ case '#':
+ optnum = 0;
+ F_SET(&exc, E_F_HASH);
+ exp->fdef |= E_F_HASH;
+ break;
+ case 'l':
+ F_SET(&exc, E_F_LIST);
+ exp->fdef |= E_F_LIST;
+ break;
+ case 'p':
+ F_SET(&exc, E_F_PRINT);
+ exp->fdef |= E_F_PRINT;
+ break;
+ default:
+ goto end1;
+ }
+end1: break;
+ case '2': /* -, ., +, ^ */
+ case '3': /* -, ., +, ^, = */
+ for (; cmdlen; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '-':
+ F_SET(&exc, E_F_DASH);
+ break;
+ case '.':
+ F_SET(&exc, E_F_DOT);
+ break;
+ case '+':
+ F_SET(&exc, E_F_PLUS);
+ break;
+ case '^':
+ F_SET(&exc, E_F_CARAT);
+ break;
+ case '=':
+ if (*p == '3') {
+ F_SET(&exc, E_F_EQUAL);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ goto end2;
+ }
+end2: break;
+ case 'b': /* buffer */
+ /*
+ * !!!
+ * Historically, "d #" was a delete with a flag, not a
+ * delete into the '#' buffer. If the current command
+ * permits a flag, don't use one as a buffer. However,
+ * the 'l' and 'p' flags were legal buffer names in the
+ * historic ex, and were used as buffers, not flags.
+ */
+ if ((cmd[0] == '+' || cmd[0] == '-' || cmd[0] == '#') &&
+ strchr(p, '1') != NULL)
+ break;
+ /*
+ * !!!
+ * Digits can't be buffer names in ex commands, or the
+ * command "d2" would be a delete into buffer '2', and
+ * not a two-line deletion.
+ */
+ if (!isdigit(cmd[0])) {
+ exc.buffer = *cmd;
+ ++cmd;
+ --cmdlen;
+ F_SET(&exc, E_BUFFER);
+ }
+ break;
+ case 'c': /* count [01+a] */
+ ++p;
+ /* Validate any signed value. */
+ if (!isdigit(*cmd) &&
+ (*p != '+' || (*cmd != '+' && *cmd != '-')))
+ break;
+ /* If a signed value, set appropriate flags. */
+ if (*cmd == '-')
+ F_SET(&exc, E_COUNT_NEG);
+ else if (*cmd == '+')
+ F_SET(&exc, E_COUNT_POS);
+/* 8-bit XXX */ if ((lno = strtol(cmd, &t, 10)) == 0 && *p != '0') {
+ msgq(sp, M_ERR, "Count may not be zero");
+ goto err;
+ }
+ cmdlen -= (t - cmd);
+ cmd = t;
+ /*
+ * Count as address offsets occur in commands taking
+ * two addresses. Historic vi practice was to use
+ * the count as an offset from the *second* address.
+ *
+ * Set a count flag; some underlying commands (see
+ * join) do different things with counts than with
+ * line addresses.
+ */
+ if (*p == 'a') {
+ exc.addr1 = exc.addr2;
+ exc.addr2.lno = exc.addr1.lno + lno - 1;
+ } else
+ exc.count = lno;
+ F_SET(&exc, E_COUNT);
+ break;
+ case 'f': /* file */
+ if (argv_exp2(sp, ep,
+ &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+ goto err;
+ goto countchk;
+ case 'l': /* line */
+ if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+ goto err;
+ /* Line specifications are always required. */
+ if (!tmp) {
+ msgq(sp, M_ERR,
+ "%s: bad line specification", cmd);
+ goto err;
+ }
+ /* The line must exist for these commands. */
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (cur.lno > lno) {
+ badlno(sp, lno);
+ goto err;
+ }
+ exc.lineno = cur.lno;
+ break;
+ case 'S': /* string, file exp. */
+ if (argv_exp1(sp, ep,
+ &exc, cmd, cmdlen, cp == &cmds[C_BANG]))
+ goto err;
+ goto addr2;
+ case 's': /* string */
+ if (argv_exp0(sp, ep, &exc, cmd, cmdlen))
+ goto err;
+ goto addr2;
+ case 'W': /* word string */
+ /*
+ * QUOTING NOTE:
+ *
+ * Literal next characters escape the following
+ * character. Quoting characters are stripped
+ * here since they are no longer useful.
+ *
+ * First there was the word.
+ */
+ for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ --cmdlen;
+ *p++ = *++cmd;
+ } else if (isblank(ch)) {
+ ++cmd;
+ --cmdlen;
+ break;
+ } else
+ *p++ = ch;
+ }
+ if (argv_exp0(sp, ep, &exc, t, p - t))
+ goto err;
+
+ /* Delete intervening whitespace. */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen == 0)
+ goto usage;
+
+ /* Followed by the string. */
+ for (p = t = cmd; cmdlen > 0; --cmdlen, ++cmd, ++p) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ --cmdlen;
+ *p = *++cmd;
+ } else
+ *p = ch;
+ }
+ if (argv_exp0(sp, ep, &exc, t, p - t))
+ goto err;
+ goto addr2;
+ case 'w': /* word */
+ if (argv_exp3(sp, ep, &exc, cmd, cmdlen))
+ goto err;
+countchk: if (*++p != 'N') { /* N */
+ /*
+ * If a number is specified, must either be
+ * 0 or that number, if optional, and that
+ * number, if required.
+ */
+ num = *p - '0';
+ if ((*++p != 'o' || exp->argsoff != 0) &&
+ exp->argsoff != num)
+ goto usage;
+ }
+ goto addr2;
+ default:
+ msgq(sp, M_ERR,
+ "Internal syntax table error (%s: %c)",
+ cp->name, *p);
+ }
+ }
+
+ /* Skip trailing whitespace. */
+ for (; cmdlen; --cmdlen) {
+ ch = *cmd++;
+ if (!isblank(ch))
+ break;
+ }
+
+ /*
+ * There shouldn't be anything left, and no more required
+ * fields, i.e neither 'l' or 'r' in the syntax string.
+ */
+ if (cmdlen || strpbrk(p, "lr")) {
+usage: msgq(sp, M_ERR, "Usage: %s", cp->usage);
+ goto err;
+ }
+
+ /* Verify that the addresses are legal. */
+addr2: switch (exc.addrcnt) {
+ case 2:
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ /*
+ * Historic ex/vi permitted commands with counts to go past
+ * EOF. So, for example, if the file only had 5 lines, the
+ * ex command "1,6>" would fail, but the command ">300"
+ * would succeed. Since we don't want to have to make all
+ * of the underlying commands handle random line numbers,
+ * fix it here.
+ */
+ if (exc.addr2.lno > lno)
+ if (F_ISSET(&exc, E_COUNT))
+ exc.addr2.lno = lno;
+ else {
+ badlno(sp, lno);
+ goto err;
+ }
+ /* FALLTHROUGH */
+ case 1:
+ num = exc.addr1.lno;
+ /*
+ * If it's a "default vi command", zero is okay. Historic
+ * vi allowed this, note, it's also the hack that allows
+ * "vi +100 nonexistent_file" to work.
+ */
+ if (num == 0 && (IN_EX_MODE(sp) || uselastcmd != 1) &&
+ !LF_ISSET(E_ZERO)) {
+ msgq(sp, M_ERR,
+ "The %s command doesn't permit an address of 0",
+ cp->name);
+ goto err;
+ }
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (num > lno) {
+ badlno(sp, lno);
+ goto err;
+ }
+ break;
+ }
+
+ /*
+ * If doing a default command and there's nothing left on the line,
+ * vi just moves to the line. For example, ":3" and ":'a,'b" just
+ * move to line 3 and line 'b, respectively, but ":3|" prints line 3.
+ *
+ * !!!
+ * This is done before the absolute mark gets set; historically,
+ * "/a/,/b/" did NOT set vi's absolute mark, but "/a/,/b/d" did.
+ */
+ if (IN_VI_MODE(sp) && uselastcmd && vi_address == 0) {
+ switch (exc.addrcnt) {
+ case 2:
+ sp->lno = exc.addr2.lno ? exc.addr2.lno : 1;
+ sp->cno = exc.addr2.cno;
+ break;
+ case 1:
+ sp->lno = exc.addr1.lno ? exc.addr1.lno : 1;
+ sp->cno = exc.addr1.cno;
+ break;
+ }
+ cmd = save_cmd;
+ cmdlen = save_cmdlen;
+ goto loop;
+ }
+
+ /*
+ * Set the absolute mark -- we have to set it for vi here, in case
+ * it's a compound command, e.g. ":5p|6" should set the absolute
+ * mark for vi.
+ */
+ if (F_ISSET(exp, EX_ABSMARK)) {
+ cur.lno = sp->lno;
+ cur.cno = sp->cno;
+ F_CLR(exp, EX_ABSMARK);
+ if (mark_set(sp, ep, ABSMARK1, &cur, 1))
+ goto err;
+ }
+
+ /* Final setup for the command. */
+ exc.cmd = cp;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "ex_cmd: %s", exc.cmd->name);
+ if (exc.addrcnt > 0) {
+ TRACE(sp, "\taddr1 %d", exc.addr1.lno);
+ if (exc.addrcnt > 1)
+ TRACE(sp, " addr2: %d", exc.addr2.lno);
+ TRACE(sp, "\n");
+ }
+ if (exc.lineno)
+ TRACE(sp, "\tlineno %d", exc.lineno);
+ if (exc.flags)
+ TRACE(sp, "\tflags %0x", exc.flags);
+ if (F_ISSET(&exc, E_BUFFER))
+ TRACE(sp, "\tbuffer %c", exc.buffer);
+ TRACE(sp, "\n");
+ if (exc.argc) {
+ for (cnt = 0; cnt < exc.argc; ++cnt)
+ TRACE(sp, "\targ %d: {%s}", cnt, exc.argv[cnt]);
+ TRACE(sp, "\n");
+ }
+#endif
+ /* Clear autoprint flag. */
+ F_CLR(exp, EX_AUTOPRINT);
+
+ /* Increment the command count if not called from vi. */
+ if (IN_EX_MODE(sp))
+ ++sp->ccnt;
+
+ /*
+ * If file state available, and not doing a global command,
+ * log the start of an action.
+ */
+ if (ep != NULL && !F_ISSET(sp, S_GLOBAL))
+ (void)log_cursor(sp, ep);
+
+ /*
+ * !!!
+ * There are two special commands for the purposes of this code: the
+ * default command (<carriage-return>) or the scrolling commands (^D
+ * and <EOF>) as the first non-<blank> characters in the line.
+ *
+ * If this is the first command in the command line, we received the
+ * command from the ex command loop and we're talking to a tty, and
+ * and there's nothing else on the command line, and it's one of the
+ * special commands, we erase the prompt character with a '\r'. Else,
+ * we put out a newline character to separate the command from the
+ * output from the command. It's OK if vi calls us -- we won't be in
+ * ex mode so we'll do nothing.
+ *
+ * !!!
+ * Historically, ex only put out a \r, so, if the displayed line was
+ * only a single character long, and <eof> was represented as ^D, the
+ * output wouldn't overwrite the user's input. Sex currently doesn't
+ * display the <eof> character if it's going to be the scroll command,
+ * i.e. if it's the first non-<blank> character in the line. If sex
+ * is changed to run in cooked mode, i.e. <eof> is displayed, this code
+ * will have to overwrite it. We also don't treat lines with extra
+ * prompt characters as empty -- it's not worth the effort since we'd
+ * have to overwrite some indeterminate number of columns with spaces
+ * to clean up. For now, put out enough spaces to overwrite the prompt.
+ */
+ if (sep != NONE) {
+ if (ep != NULL &&
+ IN_EX_MODE(sp) && F_ISSET(sp->gp, G_STDIN_TTY))
+ if (sep == NEEDSEP_NR &&
+ (uselastcmd || cp == &cmds[C_SCROLL])) {
+ (void)putchar('\r');
+ for (len = KEY_LEN(sp, PROMPTCHAR); len--;)
+ (void)putchar(' ');
+ (void)putchar('\r');
+ } else
+ (void)putchar('\n');
+ sep = NONE;
+ }
+
+ /* Save the current mode. */
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+
+ /* Do the command. */
+ if (cp->fn(sp, ep, &exc))
+ goto err;
+
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+ F_CLR(sp->gp, G_TMP_INUSE);
+ msgq(sp, M_ERR, "Error: ex: temporary buffer not released");
+ goto err;
+ }
+#endif
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE)) {
+ /*
+ * Only here if the mode of the underlying file changed, e.g.
+ * the user switched files or is exiting. Two things that we
+ * might have to save: first, any "+cmd" field set up for an
+ * ex/edit command will have to be saved for later, also, any
+ * part of the current ex command that hasn't been executed
+ * yet. For example:
+ *
+ * :edit +25 file.c|s/abc/ABC/|1
+ *
+ * !!!
+ * The historic vi just hung, of course; nvi handles it by
+ * pushing the keys onto the tty queue. Since the commands
+ * are intended as ex commands, add additional characters
+ * to make it all work if we're switching modes to vi. Also,
+ * + commands were oriented to the last line in the file,
+ * historically, make the cursor start out there.
+ *
+ * For the fun of it, if you want to see if a vi clone got the
+ * ex argument parsing right, try:
+ *
+ * echo 'foo|bar' > file1; echo 'foo/bar' > file2;
+ * vi
+ * :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
+ */
+ if (arg1_len == 0 && save_cmdlen == 0)
+ return (0);
+ if (term_push(sp, "\n", 1, 0))
+ goto err;
+ if (save_cmdlen != 0)
+ if (term_push(sp, save_cmd, save_cmdlen, 0))
+ goto err;
+ if (arg1 != NULL) {
+ if (IN_VI_MODE(sp) && save_cmdlen != 0 &&
+ term_push(sp, "|", 1, 0))
+ goto err;
+ if (term_push(sp, arg1, arg1_len, 0))
+ goto err;
+ if (file_lline(sp, ep, &sp->frp->lno))
+ goto err;
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+ if (IN_VI_MODE(sp) && term_push(sp, ":", 1, 0))
+ goto err;
+ return (0);
+ }
+
+ /*
+ * Integrate any offset parsed by the underlying command, and make
+ * sure the referenced line exists.
+ *
+ * XXX
+ * May not match historic practice (I've never been able to completely
+ * figure it out.) For example, the '=' command from vi mode often
+ * got the offset wrong, and complained it was too large, but didn't
+ * seem to have a problem with the cursor. If anyone complains, ask
+ * them how it's supposed to work, they probably know.
+ */
+ if (ep != NULL && (flagoff += exc.flagoff)) {
+ if (flagoff < 0) {
+ if (sp->lno <= -flagoff) {
+ msgq(sp, M_ERR, "Flag offset before line 1");
+ goto err;
+ }
+ } else {
+ if (file_lline(sp, ep, &lno))
+ goto err;
+ if (sp->lno + flagoff > lno) {
+ msgq(sp, M_ERR, "Flag offset past end-of-file");
+ goto err;
+ }
+ }
+ sp->lno += flagoff;
+ }
+
+ /*
+ * If the command was successful and we're in ex command mode, we
+ * may want to display a line. Make sure there's a line to display.
+ */
+ if (ep != NULL &&
+ IN_EX_MODE(sp) && !F_ISSET(sp, S_GLOBAL) && sp->lno != 0) {
+ /*
+ * The print commands have already handled the `print' flags.
+ * If so, clear them.
+ */
+ if (LF_ISSET(E_F_PRCLEAR))
+ F_CLR(&exc, E_F_HASH | E_F_LIST | E_F_PRINT);
+
+ /* If hash only set because of the number option, discard it. */
+ if (optnum)
+ F_CLR(&exc, E_F_HASH);
+
+ /*
+ * If there was an explicit flag to display the new cursor
+ * line, or we're in ex mode, autoprint is set, and a change
+ * was made, display the line. If any print flags set use
+ * them, otherwise default to print.
+ */
+ LF_INIT(F_ISSET(&exc, E_F_HASH | E_F_LIST | E_F_PRINT));
+ if (!LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT) &&
+ O_ISSET(sp, O_AUTOPRINT) &&
+ (F_ISSET(exp, EX_AUTOPRINT) || F_ISSET(cp, E_AUTOPRINT)))
+ LF_INIT(E_F_PRINT);
+
+ if (LF_ISSET(E_F_HASH | E_F_LIST | E_F_PRINT)) {
+ memset(&exc, 0, sizeof(EXCMDARG));
+ exc.addrcnt = 2;
+ exc.addr1.lno = exc.addr2.lno = sp->lno;
+ exc.addr1.cno = exc.addr2.cno = sp->cno;
+ (void)ex_print(sp, ep, &exc.addr1, &exc.addr2, flags);
+ }
+ }
+
+ cmd = save_cmd;
+ cmdlen = save_cmdlen;
+ goto loop;
+ /* NOTREACHED */
+
+ /*
+ * If we haven't put out a separator line, do it now. For more
+ * detailed comments, see above.
+ */
+err: if (sep != NONE &&
+ ep != NULL && IN_EX_MODE(sp) && F_ISSET(sp->gp, G_STDIN_TTY))
+ (void)fputc('\n', stdout);
+ /*
+ * On error, we discard any keys we have left, as well as any keys
+ * that were mapped. The test of save_cmdlen isn't necessarily
+ * correct. If we fail early enough we don't know if the entire
+ * string was a single command or not. Try and guess, it's useful
+ * to know if part of the command was discarded.
+ */
+ if (save_cmdlen == 0)
+ for (; cmdlen; --cmdlen) {
+ ch = *cmd++;
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ --cmdlen;
+ ++cmd;
+ } else if (ch == '\n' || ch == '|') {
+ if (cmdlen > 1)
+ save_cmdlen = 1;
+ break;
+ }
+ }
+ if (save_cmdlen != 0)
+ msgq(sp, M_ERR,
+ "Ex command failed: remaining command input discarded");
+ /*
+ * !!!
+ * Previous versions of nvi cleared mapped characters on error. This
+ * feature was removed when users complained that it wasn't historic
+ * practice.
+ */
+ return (1);
+}
+
+/*
+ * ep_range --
+ * Get a line range for ex commands.
+ */
+static int
+ep_range(sp, ep, excp, cmdp, cmdlenp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char **cmdp;
+ size_t *cmdlenp;
+{
+ MARK cur, savecursor;
+ size_t cmdlen;
+ int savecursor_set, tmp;
+ char *cmd;
+
+ /* Percent character is all lines in the file. */
+ cmd = *cmdp;
+ cmdlen = *cmdlenp;
+ if (*cmd == '%') {
+ excp->addr1.lno = 1;
+ if (file_lline(sp, ep, &excp->addr2.lno))
+ return (1);
+
+ /* If an empty file, then the first line is 0, not 1. */
+ if (excp->addr2.lno == 0)
+ excp->addr1.lno = 0;
+ excp->addr1.cno = excp->addr2.cno = 0;
+ excp->addrcnt = 2;
+
+ ++*cmdp;
+ --*cmdlenp;
+ return (0);
+ }
+
+ /* Parse comma or semi-colon delimited line specs. */
+ for (savecursor_set = 0, excp->addrcnt = 0; cmdlen > 0;)
+ switch (*cmd) {
+ case ';': /* Semi-colon delimiter. */
+ /*
+ * Comma delimiters delimit; semi-colon delimiters
+ * change the current address for the 2nd address
+ * to be the first address. Trailing or multiple
+ * delimiters are discarded.
+ */
+ if (excp->addrcnt == 0)
+ goto done;
+ if (!savecursor_set) {
+ savecursor.lno = sp->lno;
+ savecursor.cno = sp->cno;
+ sp->lno = excp->addr1.lno;
+ sp->cno = excp->addr1.cno;
+ savecursor_set = 1;
+ }
+ ++cmd;
+ --cmdlen;
+ break;
+ case ',': /* Comma delimiter. */
+ /* If no addresses yet, defaults to ".". */
+ if (excp->addrcnt == 0) {
+ excp->addr1.lno = sp->lno;
+ excp->addr1.cno = sp->cno;
+ excp->addrcnt = 1;
+ }
+ /* FALLTHROUGH */
+ case ' ': /* Whitespace. */
+ case '\t': /* Whitespace. */
+ ++cmd;
+ --cmdlen;
+ break;
+ default:
+ if (ep_line(sp, ep, &cur, &cmd, &cmdlen, &tmp))
+ return (1);
+ if (!tmp)
+ goto done;
+
+ /*
+ * Extra addresses are discarded, starting with
+ * the first.
+ */
+ switch (excp->addrcnt) {
+ case 0:
+ excp->addr1 = cur;
+ excp->addrcnt = 1;
+ break;
+ case 1:
+ excp->addr2 = cur;
+ excp->addrcnt = 2;
+ break;
+ case 2:
+ excp->addr1 = excp->addr2;
+ excp->addr2 = cur;
+ break;
+ }
+ break;
+ }
+
+ /*
+ * XXX
+ * This is probably not the right behavior for savecursor --
+ * need to figure out what the historical ex did for ";,;,;5p"
+ * or similar stupidity.
+ */
+done: if (savecursor_set) {
+ sp->lno = savecursor.lno;
+ sp->cno = savecursor.cno;
+ }
+ if (excp->addrcnt == 2 && excp->addr2.lno < excp->addr1.lno) {
+ msgq(sp, M_ERR,
+ "The second address is smaller than the first");
+ return (1);
+ }
+ *cmdp = cmd;
+ *cmdlenp = cmdlen;
+ return (0);
+}
+
+/*
+ * Get a single line address specifier.
+ *
+ * The way the "previous context" mark worked was that any "non-relative"
+ * motion set it. While ex/vi wasn't totally consistent about this, ANY
+ * numeric address, search pattern, '$', or mark reference in an address
+ * was considered non-relative, and set the value. Which should explain
+ * why we're hacking marks down here. The problem was that the mark was
+ * only set if the command was called, i.e. we have to set a flag and test
+ * it later.
+ *
+ * XXX
+ * This is not exactly historic practice, although it's fairly close.
+ */
+static int
+ep_line(sp, ep, cur, cmdp, cmdlenp, addr_found)
+ SCR *sp;
+ EXF *ep;
+ MARK *cur;
+ char **cmdp;
+ size_t *cmdlenp;
+ int *addr_found;
+{
+ EX_PRIVATE *exp;
+ MARK m;
+ long total;
+ u_int flags;
+ size_t cmdlen;
+ int (*sf) __P((SCR *, EXF *, MARK *, MARK *, char *, char **, u_int *));
+ char *cmd, *endp;
+
+ exp = EXP(sp);
+ *addr_found = 0;
+
+ cmd = *cmdp;
+ cmdlen = *cmdlenp;
+ switch (*cmd) {
+ case '$': /* Last line in the file. */
+ *addr_found = 1;
+ F_SET(exp, EX_ABSMARK);
+
+ cur->cno = 0;
+ if (file_lline(sp, ep, &cur->lno))
+ return (1);
+ ++cmd;
+ --cmdlen;
+ break; /* Absolute line number. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ *addr_found = 1;
+ F_SET(exp, EX_ABSMARK);
+
+ cur->cno = 0;
+/* 8-bit XXX */ cur->lno = strtol(cmd, &endp, 10);
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ break;
+ case '\'': /* Use a mark. */
+ *addr_found = 1;
+ F_SET(exp, EX_ABSMARK);
+
+ if (cmdlen == 1) {
+ msgq(sp, M_ERR, "No mark name supplied");
+ return (1);
+ }
+ if (mark_get(sp, ep, cmd[1], cur))
+ return (1);
+ cmd += 2;
+ cmdlen -= 2;
+ break;
+ case '\\': /* Search: forward/backward. */
+ /*
+ * !!!
+ * I can't find any difference between // and \/ or between
+ * ?? and \?. Mark Horton doesn't remember there being any
+ * difference. C'est la vie.
+ */
+ if (cmdlen < 2 || cmd[1] != '/' && cmd[1] != '?') {
+ msgq(sp, M_ERR, "\\ not followed by / or ?");
+ return (1);
+ }
+ ++cmd;
+ --cmdlen;
+ sf = cmd[0] == '/' ? f_search : b_search;
+ goto search;
+ case '/': /* Search forward. */
+ sf = f_search;
+ goto search;
+ case '?': /* Search backward. */
+ sf = b_search;
+search: F_SET(exp, EX_ABSMARK);
+
+ if (ep == NULL) {
+ msgq(sp, M_ERR,
+ "A search address requires that a file have already been read in");
+ return (1);
+ }
+ *addr_found = 1;
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ flags = SEARCH_MSG | SEARCH_PARSE | SEARCH_SET;
+ if (sf(sp, ep, &m, &m, cmd, &endp, &flags))
+ return (1);
+ cur->lno = m.lno;
+ cur->cno = m.cno;
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ break;
+ case '.': /* Current position. */
+ *addr_found = 1;
+ cur->cno = sp->cno;
+
+ /* If an empty file, then '.' is 0, not 1. */
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &cur->lno))
+ return (1);
+ if (cur->lno != 0)
+ cur->lno = 1;
+ } else
+ cur->lno = sp->lno;
+ ++cmd;
+ --cmdlen;
+ break;
+ }
+
+ /*
+ * Evaluate any offset. Offsets are +/- any number, or any number
+ * of +/- signs, or any combination thereof. If no address found
+ * yet, offset is relative to ".".
+ */
+ for (total = 0; cmdlen > 0 && (cmd[0] == '-' || cmd[0] == '+');) {
+ if (!*addr_found) {
+ cur->lno = sp->lno;
+ cur->cno = sp->cno;
+ *addr_found = 1;
+ }
+
+ if (cmdlen > 1 && isdigit(cmd[1])) {
+/* 8-bit XXX */ total += strtol(cmd, &endp, 10);
+ cmdlen -= (endp - cmd);
+ cmd = endp;
+ } else {
+ total += cmd[0] == '-' ? -1 : 1;
+ --cmdlen;
+ ++cmd;
+ }
+ }
+
+ if (*addr_found) {
+ if (total < 0 && -total > cur->lno) {
+ msgq(sp, M_ERR,
+ "Reference to a line number less than 0");
+ return (1);
+ }
+ cur->lno += total;
+
+ *cmdp = cmd;
+ *cmdlenp = cmdlen;
+ }
+ return (0);
+}
+
+/*
+ * ex_is_abbrev -
+ * The vi text input routine needs to know if ex thinks this is
+ * an [un]abbreviate command, so it can turn off abbreviations.
+ * Usual ranting in the vi/v_ntext:txt_abbrev() routine.
+ */
+int
+ex_is_abbrev(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ return ((cp = ex_comm_search(name, len)) != NULL &&
+ (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
+}
+
+/*
+ * ex_is_unmap -
+ * The vi text input routine needs to know if ex thinks this is
+ * an unmap command, so it can turn off input mapping. Usual
+ * ranting in the vi/v_ntext:txt_unmap() routine.
+ */
+int
+ex_is_unmap(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ /*
+ * The command the vi input routines are really interested in
+ * is "unmap!", not just unmap.
+ */
+ if (name[len - 1] != '!')
+ return (0);
+ --len;
+ return ((cp = ex_comm_search(name, len)) != NULL &&
+ cp == &cmds[C_UNMAP]);
+}
+
+static __inline EXCMDLIST const *
+ex_comm_search(name, len)
+ char *name;
+ size_t len;
+{
+ EXCMDLIST const *cp;
+
+ for (cp = cmds; cp->name != NULL; ++cp) {
+ if (cp->name[0] > name[0])
+ return (NULL);
+ if (cp->name[0] != name[0])
+ continue;
+ if (!memcmp(name, cp->name, len))
+ return (cp);
+ }
+ return (NULL);
+}
+
+static void
+badlno(sp, lno)
+ SCR *sp;
+ recno_t lno;
+{
+ if (lno == 0)
+ msgq(sp, M_ERR, "Illegal address: the file is empty");
+ else
+ msgq(sp, M_ERR, "Illegal address: only %lu line%s in the file",
+ lno, lno > 1 ? "s" : "");
+}
diff --git a/usr.bin/vi/ex/ex_abbrev.c b/usr.bin/vi/ex/ex_abbrev.c
new file mode 100644
index 0000000..1360030
--- /dev/null
+++ b/usr.bin/vi/ex/ex_abbrev.c
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_abbrev.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../vi/vcmd.h"
+
+/*
+ * ex_abbr -- :abbreviate [key replacement]
+ * Create an abbreviation or display abbreviations.
+ */
+int
+ex_abbr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CHAR_T *p;
+ size_t len;
+
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, SEQ_ABBREV, 0) == 0)
+ msgq(sp, M_INFO, "No abbreviations to display");
+ return (0);
+ case 2:
+ break;
+ default:
+ abort();
+ }
+
+ /* Check for illegal characters. */
+ for (p = cmdp->argv[0]->bp, len = cmdp->argv[0]->len; len--; ++p)
+ if (!inword(*p)) {
+ msgq(sp, M_ERR,
+ "%s may not be part of an abbreviated word",
+ KEY_NAME(sp, *p));
+ return (1);
+ }
+
+ if (seq_set(sp, NULL, 0, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, SEQ_ABBREV, SEQ_USERDEF))
+ return (1);
+
+ F_SET(sp->gp, G_ABBREV);
+ return (0);
+}
+
+/*
+ * ex_unabbr -- :unabbreviate key
+ * Delete an abbreviation.
+ */
+int
+ex_unabbr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+
+ ap = cmdp->argv[0];
+ if (!F_ISSET(sp->gp, G_ABBREV) ||
+ seq_delete(sp, ap->bp, ap->len, SEQ_ABBREV)) {
+ msgq(sp, M_ERR, "\"%s\" is not an abbreviation", ap->bp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * abbr_save --
+ * Save the abbreviation sequences to a file.
+ */
+int
+abbr_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ return (seq_save(sp, fp, "abbreviate ", SEQ_ABBREV));
+}
diff --git a/usr.bin/vi/ex/ex_append.c b/usr.bin/vi/ex/ex_append.c
new file mode 100644
index 0000000..bb3f618
--- /dev/null
+++ b/usr.bin/vi/ex/ex_append.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_append.c 8.24 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../sex/sex_screen.h"
+
+enum which {APPEND, CHANGE, INSERT};
+
+static int aci __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_append -- :[line] a[ppend][!]
+ * Append one or more lines of new text after the specified line,
+ * or the current line if no address is specified.
+ */
+int
+ex_append(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, APPEND));
+}
+
+/*
+ * ex_change -- :[line[,line]] c[hange][!] [count]
+ * Change one or more lines to the input text.
+ */
+int
+ex_change(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, CHANGE));
+}
+
+/*
+ * ex_insert -- :[line] i[nsert][!]
+ * Insert one or more lines of new text before the specified line,
+ * or the current line if no address is specified.
+ */
+int
+ex_insert(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (aci(sp, ep, cmdp, INSERT));
+}
+
+static int
+aci(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ MARK m;
+ TEXTH *sv_tiqp, tiq;
+ TEXT *tp;
+ struct termios t;
+ u_int flags;
+ int rval;
+
+ rval = 0;
+
+ /*
+ * Set input flags; the ! flag turns off autoindent for append,
+ * change and insert.
+ */
+ LF_INIT(TXT_DOTTERM | TXT_NLECHO);
+ if (!F_ISSET(cmdp, E_FORCE) && O_ISSET(sp, O_AUTOINDENT))
+ LF_SET(TXT_AUTOINDENT);
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+
+ /* Input is interruptible. */
+ F_SET(sp, S_INTERRUPTIBLE);
+
+ /*
+ * If this code is called by vi, the screen TEXTH structure (sp->tiqp)
+ * may already be in use, e.g. ":append|s/abc/ABC/" would fail as we're
+ * only halfway through the line when the append code fires. Use the
+ * local structure instead.
+ *
+ * If this code is called by vi, we want to reset the terminal and use
+ * ex's s_get() routine. It actually works fine if we use vi's s_get()
+ * routine, but it doesn't look as nice. Maybe if we had a separate
+ * window or something, but getting a line at a time looks awkward.
+ */
+ if (IN_VI_MODE(sp)) {
+ memset(&tiq, 0, sizeof(TEXTH));
+ CIRCLEQ_INIT(&tiq);
+ sv_tiqp = sp->tiqp;
+ sp->tiqp = &tiq;
+
+ if (F_ISSET(sp->gp, G_STDIN_TTY))
+ SEX_RAW(t);
+ (void)write(STDOUT_FILENO, "\n", 1);
+ LF_SET(TXT_NLECHO);
+
+ }
+
+ /* Set the line number, so that autoindent works correctly. */
+ sp->lno = cmdp->addr1.lno;
+
+ if (sex_get(sp, ep, sp->tiqp, 0, flags) != INP_OK)
+ goto err;
+
+ /*
+ * If doing a change, replace lines for as long as possible. Then,
+ * append more lines or delete remaining lines. Changes to an empty
+ * file are just appends, and inserts are the same as appends to the
+ * previous line.
+ *
+ * !!!
+ * Adjust the current line number for the commands to match historic
+ * practice if the user doesn't enter anything, and set the address
+ * to which we'll append. This is safe because an address of 0 is
+ * illegal for change and insert.
+ */
+ m = cmdp->addr1;
+ switch (cmd) {
+ case INSERT:
+ --m.lno;
+ /* FALLTHROUGH */
+ case APPEND:
+ if (sp->lno == 0)
+ sp->lno = 1;
+ break;
+ case CHANGE:
+ --m.lno;
+ if (sp->lno != 1)
+ --sp->lno;
+ break;
+ }
+
+ /*
+ * !!!
+ * Cut into the unnamed buffer.
+ */
+ if (cmd == CHANGE &&
+ (cut(sp, ep, NULL, &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE) ||
+ delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1)))
+ goto err;
+
+ for (tp = sp->tiqp->cqh_first;
+ tp != (TEXT *)sp->tiqp; tp = tp->q.cqe_next) {
+ if (file_aline(sp, ep, 1, m.lno, tp->lb, tp->len)) {
+err: rval = 1;
+ break;
+ }
+ sp->lno = ++m.lno;
+ }
+
+ if (IN_VI_MODE(sp)) {
+ sp->tiqp = sv_tiqp;
+ text_lfree(&tiq);
+
+ /* Reset the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY)) {
+ if (SEX_NORAW(t))
+ rval = 1;
+ F_SET(sp, S_REFRESH);
+ }
+ }
+ return (rval);
+}
diff --git a/usr.bin/vi/ex/ex_args.c b/usr.bin/vi/ex/ex_args.c
new file mode 100644
index 0000000..4dfc18e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_args.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_args.c 8.29 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_next -- :next [+cmd] [files]
+ * Edit the next file, optionally setting the list of files.
+ *
+ * !!!
+ * The :next command behaved differently from the :rewind command in
+ * historic vi. See nvi/docs/autowrite for details, but the basic
+ * idea was that it ignored the force flag if the autowrite flag was
+ * set. This implementation handles them all identically.
+ */
+int
+ex_next(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS **argv, **pc;
+ FREF *frp;
+ int noargs;
+ char **ap;
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /*
+ * If the first argument is a plus sign, '+', it's an initial
+ * ex command.
+ */
+ argv = cmdp->argv;
+ if (cmdp->argc && argv[0]->bp[0] == '+') {
+ --cmdp->argc;
+ pc = argv++;
+ } else
+ pc = NULL;
+
+ /* Any other arguments are a replacement file list. */
+ if (cmdp->argc) {
+ /* Free the current list. */
+ if (!F_ISSET(sp, S_ARGNOFREE) && sp->argv != NULL) {
+ for (ap = sp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(sp->argv);
+ }
+ F_CLR(sp, S_ARGNOFREE | S_ARGRECOVER);
+ sp->cargv = NULL;
+
+ /* Create a new list. */
+ CALLOC_RET(sp,
+ sp->argv, char **, cmdp->argc + 1, sizeof(char *));
+ for (ap = sp->argv,
+ argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ return (1);
+ *ap = NULL;
+
+ /* Switch to the first one. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ noargs = 0;
+ } else {
+ if (sp->cargv == NULL || sp->cargv[1] == NULL) {
+ msgq(sp, M_ERR, "No more files to edit");
+ return (1);
+ }
+ if ((frp = file_add(sp, sp->cargv[1])) == NULL)
+ return (1);
+ if (F_ISSET(sp, S_ARGRECOVER))
+ F_SET(frp, FR_RECOVER);
+ noargs = 1;
+ }
+
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ if (noargs)
+ ++sp->cargv;
+
+ /* Push the initial command onto the stack. */
+ if (pc != NULL)
+ if (IN_EX_MODE(sp))
+ (void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
+ else if (IN_VI_MODE(sp)) {
+ (void)term_push(sp, "\n", 1, 0);
+ (void)term_push(sp, pc[0]->bp, pc[0]->len, 0);
+ (void)term_push(sp, ":", 1, 0);
+ (void)file_lline(sp, sp->ep, &sp->frp->lno);
+ F_SET(sp->frp, FR_CURSORSET);
+ }
+
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_prev -- :prev
+ * Edit the previous file.
+ */
+int
+ex_prev(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp;
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ if (sp->cargv == sp->argv) {
+ msgq(sp, M_ERR, "No previous files to edit");
+ return (1);
+ }
+ if ((frp = file_add(sp, sp->cargv[-1])) == NULL)
+ return (1);
+
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ --sp->cargv;
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_rew -- :rew
+ * Re-edit the list of files.
+ */
+int
+ex_rew(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ FREF *frp;
+
+ /*
+ * !!!
+ * Historic practice -- you can rewind to the current file.
+ */
+ if (sp->argv == NULL) {
+ msgq(sp, M_ERR, "No previous files to rewind");
+ return (1);
+ }
+
+ if (file_m1(sp, ep, F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+
+ /*
+ * !!!
+ * Historic practice, start at the beginning of the file.
+ */
+ for (frp = sp->frefq.cqh_first;
+ frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next)
+ F_CLR(frp, FR_CURSORSET | FR_FNONBLANK);
+
+ /* Switch to the first one. */
+ sp->cargv = sp->argv;
+ if ((frp = file_add(sp, *sp->cargv)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
+
+/*
+ * ex_args -- :args
+ * Display the list of files.
+ */
+int
+ex_args(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int cnt, col, len, sep;
+ char **ap;
+
+ if (sp->argv == NULL) {
+ (void)ex_printf(EXCOOKIE, "No file list to display.\n");
+ return (0);
+ }
+
+ col = len = sep = 0;
+ for (cnt = 1, ap = sp->argv; *ap != NULL; ++ap) {
+ col += len = strlen(*ap) + sep + (ap == sp->cargv ? 2 : 0);
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_printf(EXCOOKIE, " ");
+ }
+ ++cnt;
+
+ if (ap == sp->cargv)
+ (void)ex_printf(EXCOOKIE, "[%s]", *ap);
+ else
+ (void)ex_printf(EXCOOKIE, "%s", *ap);
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_argv.c b/usr.bin/vi/ex/ex_argv.c
new file mode 100644
index 0000000..63d820e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_argv.c
@@ -0,0 +1,609 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_argv.c 8.38 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static int argv_alloc __P((SCR *, size_t));
+static int argv_fexp __P((SCR *, EXCMDARG *,
+ char *, size_t, char *, size_t *, char **, size_t *, int));
+static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
+
+/*
+ * argv_init --
+ * Build a prototype arguments list.
+ */
+int
+argv_init(sp, ep, excp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ exp->argsoff = 0;
+ argv_alloc(sp, 1);
+
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ return (0);
+}
+
+/*
+ * argv_exp0 --
+ * Append a string to the argument list.
+ */
+int
+argv_exp0(sp, ep, excp, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+
+ exp = EXP(sp);
+ argv_alloc(sp, cmdlen);
+ memmove(exp->args[exp->argsoff]->bp, cmd, cmdlen);
+ exp->args[exp->argsoff]->bp[cmdlen] = '\0';
+ exp->args[exp->argsoff]->len = cmdlen;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ return (0);
+}
+
+/*
+ * argv_exp1 --
+ * Do file name expansion on a string, and append it to the
+ * argument list.
+ */
+int
+argv_exp1(sp, ep, excp, cmd, cmdlen, is_bang)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ size_t blen, len;
+ char *bp, *p, *t;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+
+ len = 0;
+ exp = EXP(sp);
+ if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+
+ /* If it's empty, we're done. */
+ if (len != 0) {
+ for (p = bp, t = bp + len; p < t; ++p)
+ if (!isblank(*p))
+ break;
+ if (p == t)
+ goto ret;
+ } else
+ goto ret;
+
+ (void)argv_exp0(sp, ep, excp, bp, len);
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (0);
+}
+
+/*
+ * argv_exp2 --
+ * Do file name and shell expansion on a string, and append it to
+ * the argument list.
+ */
+int
+argv_exp2(sp, ep, excp, cmd, cmdlen, is_bang)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+ int is_bang;
+{
+ size_t blen, len, n;
+ int rval;
+ char *bp, *mp, *p;
+
+ GET_SPACE_RET(sp, bp, blen, 512);
+
+#define SHELLECHO "echo "
+#define SHELLOFFSET (sizeof(SHELLECHO) - 1)
+ memmove(bp, SHELLECHO, SHELLOFFSET);
+ p = bp + SHELLOFFSET;
+ len = SHELLOFFSET;
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd);
+#endif
+
+ if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, is_bang)) {
+ rval = 1;
+ goto err;
+ }
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "before shell: %d: {%s}\n", len, bp);
+#endif
+
+ /*
+ * Do shell word expansion -- it's very, very hard to figure out what
+ * magic characters the user's shell expects. Historically, it was a
+ * union of v7 shell and csh meta characters. We match that practice
+ * by default, so ":read \%" tries to read a file named '%'. It would
+ * make more sense to pass any special characters through the shell,
+ * but then, if your shell was csh, the above example will behave
+ * differently in nvi than in vi. If you want to get other characters
+ * passed through to your shell, change the "meta" option.
+ *
+ * To avoid a function call per character, we do a first pass through
+ * the meta characters looking for characters that aren't expected
+ * to be there.
+ */
+ for (p = mp = O_STR(sp, O_META); *p != '\0'; ++p)
+ if (isblank(*p) || isalnum(*p))
+ break;
+ if (*p != '\0') {
+ for (p = bp, n = len; n > 0; --n, ++p)
+ if (strchr(mp, *p) != NULL)
+ break;
+ } else
+ for (p = bp, n = len; n > 0; --n, ++p)
+ if (!isblank(*p) &&
+ !isalnum(*p) && strchr(mp, *p) != NULL)
+ break;
+ if (n > 0) {
+ if (argv_sexp(sp, &bp, &blen, &len)) {
+ rval = 1;
+ goto err;
+ }
+ p = bp;
+ } else {
+ p = bp + SHELLOFFSET;
+ len -= SHELLOFFSET;
+ }
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "after shell: %d: {%s}\n", len, bp);
+#endif
+
+ rval = argv_exp3(sp, ep, excp, p, len);
+
+err: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * argv_exp3 --
+ * Take a string and break it up into an argv, which is appended
+ * to the argument list.
+ */
+int
+argv_exp3(sp, ep, excp, cmd, cmdlen)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *excp;
+ char *cmd;
+ size_t cmdlen;
+{
+ EX_PRIVATE *exp;
+ size_t len;
+ int ch, off;
+ char *ap, *p;
+
+ for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
+ /* Skip any leading whitespace. */
+ for (; cmdlen > 0; --cmdlen, ++cmd) {
+ ch = *cmd;
+ if (!isblank(ch))
+ break;
+ }
+ if (cmdlen == 0)
+ break;
+
+ /*
+ * Determine the length of this whitespace delimited
+ * argument.
+ *
+ * QUOTING NOTE:
+ *
+ * Skip any character preceded by the user's quoting
+ * character.
+ */
+ for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) {
+ ch = *cmd;
+ if (IS_ESCAPE(sp, ch) && cmdlen > 1) {
+ ++cmd;
+ --cmdlen;
+ } else if (isblank(ch))
+ break;
+ }
+
+ /*
+ * Copy the argument into place.
+ *
+ * QUOTING NOTE:
+ *
+ * Lose quote chars.
+ */
+ argv_alloc(sp, len);
+ off = exp->argsoff;
+ exp->args[off]->len = len;
+ for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++)
+ if (IS_ESCAPE(sp, *ap))
+ ++ap;
+ *p = '\0';
+ }
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+
+#if defined(DEBUG) && 0
+ for (cnt = 0; cnt < exp->argsoff; ++cnt)
+ TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]);
+#endif
+ return (0);
+}
+
+/*
+ * argv_fexp --
+ * Do file name and bang command expansion.
+ */
+static int
+argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
+ SCR *sp;
+ EXCMDARG *excp;
+ char *cmd, *p, **bpp;
+ size_t cmdlen, *lenp, *blenp;
+ int is_bang;
+{
+ EX_PRIVATE *exp;
+ char *bp, *t;
+ size_t blen, len, tlen;
+
+ /* Replace file name characters. */
+ for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
+ switch (*cmd) {
+ case '!':
+ if (!is_bang)
+ goto ins_ch;
+ exp = EXP(sp);
+ if (exp->lastbcomm == NULL) {
+ msgq(sp, M_ERR,
+ "No previous command to replace \"!\"");
+ return (1);
+ }
+ len += tlen = strlen(exp->lastbcomm);
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, exp->lastbcomm, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '%':
+ if ((t = sp->frp->name) == NULL) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for %%");
+ return (1);
+ }
+ tlen = strlen(t);
+ len += tlen;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '#':
+ if ((t = sp->alt_name) == NULL) {
+ msgq(sp, M_ERR,
+ "No filename to substitute for #");
+ return (1);
+ }
+ len += tlen = strlen(t);
+ ADD_SPACE_RET(sp, bp, blen, len);
+ memmove(p, t, tlen);
+ p += tlen;
+ F_SET(excp, E_MODIFY);
+ break;
+ case '\\':
+ /*
+ * QUOTING NOTE:
+ *
+ * Strip any backslashes that protected the file
+ * expansion characters.
+ */
+ if (cmdlen > 1 && (cmd[1] == '%' || cmd[1] == '#')) {
+ ++cmd;
+ --cmdlen;
+ }
+ /* FALLTHROUGH */
+ default:
+ins_ch: ++len;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ *p++ = *cmd;
+ }
+
+ /* Nul termination. */
+ ++len;
+ ADD_SPACE_RET(sp, bp, blen, len);
+ *p = '\0';
+
+ /* Return the new string length, buffer, buffer length. */
+ *lenp = len - 1;
+ *bpp = bp;
+ *blenp = blen;
+ return (0);
+}
+
+/*
+ * argv_alloc --
+ * Make more space for arguments.
+ */
+static int
+argv_alloc(sp, len)
+ SCR *sp;
+ size_t len;
+{
+ ARGS *ap;
+ EX_PRIVATE *exp;
+ int cnt, off;
+
+ /*
+ * Allocate room for another argument, always leaving
+ * enough room for an ARGS structure with a length of 0.
+ */
+#define INCREMENT 20
+ exp = EXP(sp);
+ off = exp->argsoff;
+ if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) {
+ cnt = exp->argscnt + INCREMENT;
+ REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *));
+ if (exp->args == NULL) {
+ (void)argv_free(sp);
+ goto mem;
+ }
+ memset(&exp->args[off], 0, INCREMENT * sizeof(ARGS *));
+ exp->argscnt = cnt;
+ }
+
+ /* First argument. */
+ if (exp->args[off] == NULL) {
+ CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+ if (exp->args[off] == NULL)
+ goto mem;
+ }
+
+ /* First argument buffer. */
+ ap = exp->args[off];
+ ap->len = 0;
+ if (ap->blen < len + 1) {
+ ap->blen = len + 1;
+ REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T));
+ if (ap->bp == NULL) {
+ ap->bp = NULL;
+ ap->blen = 0;
+ F_CLR(ap, A_ALLOCATED);
+mem: msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ F_SET(ap, A_ALLOCATED);
+ }
+
+ /* Second argument. */
+ if (exp->args[++off] == NULL) {
+ CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS));
+ if (exp->args[off] == NULL)
+ goto mem;
+ }
+ /* 0 length serves as end-of-argument marker. */
+ exp->args[off]->len = 0;
+ return (0);
+}
+
+/*
+ * argv_free --
+ * Free up argument structures.
+ */
+int
+argv_free(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ int off;
+
+ exp = EXP(sp);
+ if (exp->args != NULL) {
+ for (off = 0; off < exp->argscnt; ++off) {
+ if (exp->args[off] == NULL)
+ continue;
+ if (F_ISSET(exp->args[off], A_ALLOCATED))
+ free(exp->args[off]->bp);
+ FREE(exp->args[off], sizeof(ARGS));
+ }
+ FREE(exp->args, exp->argscnt * sizeof(ARGS *));
+ }
+ exp->args = NULL;
+ exp->argscnt = 0;
+ exp->argsoff = 0;
+ return (0);
+}
+
+/*
+ * argv_sexp --
+ * Fork a shell, pipe a command through it, and read the output into
+ * a buffer.
+ */
+static int
+argv_sexp(sp, bpp, blenp, lenp)
+ SCR *sp;
+ char **bpp;
+ size_t *blenp, *lenp;
+{
+ FILE *ifp;
+ pid_t pid;
+ size_t blen, len;
+ int ch, rval, output[2];
+ char *bp, *p, *sh, *sh_path;
+
+ bp = *bpp;
+ blen = *blenp;
+
+ sh_path = O_STR(sp, O_SHELL);
+ if ((sh = strrchr(sh_path, '/')) == NULL)
+ sh = sh_path;
+ else
+ ++sh;
+
+ /*
+ * There are two different processes running through this code.
+ * They are named the utility and the parent. The utility reads
+ * from standard input and writes to the parent. The parent reads
+ * from the utility and writes into the buffer. The parent reads
+ * from output[0], and the utility writes to output[1].
+ */
+ if (pipe(output) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ return (1);
+ }
+ if ((ifp = fdopen(output[0], "r")) == NULL) {
+ msgq(sp, M_SYSERR, "fdopen");
+ goto err;
+ }
+
+ /*
+ * Do the minimal amount of work possible, the shell is going
+ * to run briefly and then exit. Hopefully.
+ */
+ SIGBLOCK(sp->gp);
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+err: (void)close(output[0]);
+ (void)close(output[1]);
+ return (1);
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ /* Redirect stdout/stderr to the write end of the pipe. */
+ (void)dup2(output[1], STDOUT_FILENO);
+ (void)dup2(output[1], STDERR_FILENO);
+
+ /* Close the utility's file descriptors. */
+ (void)close(output[0]);
+ (void)close(output[1]);
+
+ /* Assumes that all shells have -c. */
+ execl(sh_path, sh, "-c", bp, NULL);
+ msgq(sp, M_ERR,
+ "Error: execl: %s: %s", sh_path, strerror(errno));
+ _exit(127);
+ default: /* Parent. */
+ SIGUNBLOCK(sp->gp);
+
+ /* Close the pipe end the parent won't use. */
+ (void)close(output[1]);
+ break;
+ }
+
+ rval = 0;
+
+ /*
+ * Copy process output into a buffer.
+ *
+ * !!!
+ * Historic vi apparently discarded leading \n and \r's from
+ * the shell output stream. We don't on the grounds that any
+ * shell that does that is broken.
+ */
+ for (p = bp, len = 0, ch = EOF;
+ (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
+ if (blen < 5) {
+ ADD_SPACE_GOTO(sp, bp, blen, *blenp * 2);
+ p = bp + len;
+ blen = *blenp - len;
+ }
+
+ /* Delete the final newline, nul terminate the string. */
+ if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) {
+ --len;
+ *--p = '\0';
+ } else
+ *p = '\0';
+ *lenp = len;
+
+ if (ferror(ifp)) {
+ msgq(sp, M_ERR, "I/O error: %s", sh);
+binc_err: rval = 1;
+ }
+ (void)fclose(ifp);
+
+ *bpp = bp; /* *blenp is already updated. */
+
+ /* Wait for the process. */
+ return (proc_wait(sp, (long)pid, sh, 0) || rval);
+}
diff --git a/usr.bin/vi/ex/ex_at.c b/usr.bin/vi/ex/ex_at.c
new file mode 100644
index 0000000..147261d
--- /dev/null
+++ b/usr.bin/vi/ex/ex_at.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_at.c 8.27 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_at -- :@[@ | buffer]
+ * :*[* | buffer]
+ *
+ * Execute the contents of the buffer.
+ */
+int
+ex_at(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CB *cbp;
+ EX_PRIVATE *exp;
+ TEXT *tp;
+ int name;
+
+ exp = EXP(sp);
+
+ /*
+ * !!!
+ * Historically, [@*]<carriage-return> and [@*][@*] executed the most
+ * recently executed buffer in ex mode. In vi mode, only @@ repeated
+ * the last buffer. We change historic practice and make @* work from
+ * vi mode as well, it's simpler and more consistent.
+ */
+ name = F_ISSET(cmdp, E_BUFFER) ? cmdp->buffer : '@';
+ if (name == '@' || name == '*') {
+ if (!exp->at_lbuf_set) {
+ msgq(sp, M_ERR, "No previous buffer to execute");
+ return (1);
+ }
+ name = exp->at_lbuf;
+ }
+
+ CBNAME(sp, cbp, name);
+ if (cbp == NULL) {
+ msgq(sp, M_ERR, "Buffer %s is empty", KEY_NAME(sp, name));
+ return (1);
+ }
+
+ /* Save for reuse. */
+ exp->at_lbuf = name;
+ exp->at_lbuf_set = 1;
+
+ /*
+ * !!!
+ * Historic practice is that if the buffer was cut in line mode,
+ * <newlines> were appended to each line as it was pushed onto
+ * the stack. If the buffer was cut in character mode, <newlines>
+ * were appended to all lines but the last one.
+ */
+ for (tp = cbp->textq.cqh_last;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_prev)
+ if ((F_ISSET(cbp, CB_LMODE) ||
+ tp->q.cqe_next != (void *)&cbp->textq) &&
+ term_push(sp, "\n", 1, 0) ||
+ term_push(sp, tp->lb, tp->len, 0))
+ return (1);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_bang.c b/usr.bin/vi/ex/ex_bang.c
new file mode 100644
index 0000000..d2ffaa4
--- /dev/null
+++ b/usr.bin/vi/ex/ex_bang.c
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_bang.c 8.35 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * ex_bang -- :[line [,line]] ! command
+ *
+ * Pass the rest of the line after the ! character to the program named by
+ * the O_SHELL option.
+ *
+ * Historical vi did NOT do shell expansion on the arguments before passing
+ * them, only file name expansion. This means that the O_SHELL program got
+ * "$t" as an argument if that is what the user entered. Also, there's a
+ * special expansion done for the bang command. Any exclamation points in
+ * the user's argument are replaced by the last, expanded ! command.
+ *
+ * There's some fairly amazing slop in this routine to make the different
+ * ways of getting here display the right things. It took a long time to
+ * get it right (wrong?), so be careful.
+ */
+int
+ex_bang(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum filtertype ftype;
+ ARGS *ap;
+ EX_PRIVATE *exp;
+ MARK rm;
+ recno_t lno;
+ size_t blen;
+ int rval;
+ char *bp, *msg;
+
+ ap = cmdp->argv[0];
+ if (ap->len == 0) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ /* Set the last bang command. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ if ((exp->lastbcomm = strdup(ap->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /*
+ * If the command was modified by the expansion, we redisplay it.
+ * Redisplaying it in vi mode is tricky, and handled separately
+ * in each case below. If we're in ex mode, it's easy, so we just
+ * do it here.
+ */
+ bp = NULL;
+ if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, S_EXSILENT)) {
+ if (IN_EX_MODE(sp)) {
+ (void)ex_printf(EXCOOKIE, "!%s\n", ap->bp);
+ (void)ex_fflush(EXCOOKIE);
+ }
+ /*
+ * Vi: Display the command if modified. Historic vi displayed
+ * the command if it was modified due to file name and/or bang
+ * expansion. If piping lines, it was immediately overwritten
+ * by any error or line change reporting. We don't the user to
+ * have to page through the responses, so we only post it until
+ * it's erased by something else. Otherwise, pass it on to the
+ * ex_exec_proc routine to display after the screen has been
+ * cleaned up.
+ */
+ if (IN_VI_MODE(sp)) {
+ GET_SPACE_RET(sp, bp, blen, ap->len + 3);
+ bp[0] = '!';
+ memmove(bp + 1, ap->bp, ap->len);
+ bp[ap->len + 1] = '\n';
+ bp[ap->len + 2] = '\0';
+ }
+ }
+
+ /*
+ * If addresses were specified, pipe lines from the file through the
+ * command.
+ *
+ * Historically, vi lines were replaced by both the stdout and stderr
+ * lines of the command, but ex by only the stdout lines. This makes
+ * no sense to me, so nvi makes it consistent for both, and matches
+ * vi's historic behavior.
+ */
+ if (cmdp->addrcnt != 0) {
+ /* Autoprint is set historically, even if the command fails. */
+ F_SET(exp, EX_AUTOPRINT);
+
+ /* Vi gets a busy message. */
+ if (bp != NULL)
+ (void)sp->s_busy(sp, bp);
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file. When it
+ * happens, we get called with two addresses of 1,1 and a
+ * bad attitude. The simple solution is to turn it into a
+ * FILTER_READ operation, but that means that we don't put
+ * an empty line into the default cut buffer as did historic
+ * vi. Tough.
+ */
+ ftype = FILTER;
+ if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ cmdp->addr1.lno = cmdp->addr2.lno = 0;
+ ftype = FILTER_READ;
+ }
+ }
+ rval = filtercmd(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);
+
+ /*
+ * If in vi mode, move to the first nonblank.
+ *
+ * !!!
+ * Historic vi wasn't consistent in this area -- if you used
+ * a forward motion it moved to the first nonblank, but if you
+ * did a backward motion it didn't. And, if you followed a
+ * backward motion with a forward motion, it wouldn't move to
+ * the nonblank for either. Going to the nonblank generally
+ * seems more useful, so we do it.
+ */
+ if (rval == 0) {
+ sp->lno = rm.lno;
+ if (IN_VI_MODE(sp)) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+ }
+ goto ret2;
+ }
+
+ /*
+ * If no addresses were specified, run the command. If the file
+ * has been modified and autowrite is set, write the file back.
+ * If the file has been modified, autowrite is not set and the
+ * warn option is set, tell the user about the file.
+ */
+ msg = NULL;
+ if (F_ISSET(ep, F_MODIFIED))
+ if (O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL)) {
+ rval = 1;
+ goto ret1;
+ }
+ } else if (O_ISSET(sp, O_WARN) && !F_ISSET(sp, S_EXSILENT))
+ msg = "File modified since last write.\n";
+
+ /* Run the command. */
+ rval = ex_exec_proc(sp, ap->bp, bp, msg);
+
+ /* Vi requires user permission to continue. */
+ if (IN_VI_MODE(sp))
+ F_SET(sp, S_CONTINUE);
+
+ret2: if (IN_EX_MODE(sp)) {
+ /*
+ * Put ex error messages out so they aren't confused with
+ * the autoprint output.
+ */
+ if (rval)
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Ex terminates with a bang, even if the command fails. */
+ if (!F_ISSET(sp, S_EXSILENT))
+ (void)write(STDOUT_FILENO, "!\n", 2);
+ }
+
+ /* Free the extra space. */
+ret1: if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+
+ /*
+ * XXX
+ * The ! commands never return an error, so that autoprint always
+ * happens in the ex parser.
+ */
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_cd.c b/usr.bin/vi/ex/ex_cd.c
new file mode 100644
index 0000000..8a3d0f1b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_cd.c
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_cd.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_cd -- :cd[!] [directory]
+ * Change directories.
+ */
+int
+ex_cd(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct passwd *pw;
+ ARGS *ap;
+ CDPATH *cdp;
+ char *dir; /* XXX END OF THE STACK, DON'T TRUST GETCWD. */
+ char buf[MAXPATHLEN * 2];
+
+ /*
+ * !!!
+ * Historic practice is that the cd isn't attempted if the file has
+ * been modified, unless its name begins with a leading '/' or the
+ * force flag is set.
+ */
+ if (F_ISSET(ep, F_MODIFIED) &&
+ !F_ISSET(cmdp, E_FORCE) && sp->frp->name[0] != '/') {
+ msgq(sp, M_ERR,
+ "File modified since last complete write; write or use ! to override");
+ return (1);
+ }
+
+ switch (cmdp->argc) {
+ case 0:
+ /* If no argument, change to the user's home directory. */
+ if ((dir = getenv("HOME")) == NULL) {
+ if ((pw = getpwuid(getuid())) == NULL ||
+ pw->pw_dir == NULL || pw->pw_dir[0] == '\0') {
+ msgq(sp, M_ERR,
+ "Unable to find home directory location");
+ return (1);
+ }
+ dir = pw->pw_dir;
+ }
+ break;
+ case 1:
+ dir = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /* Try the current directory first. */
+ if (!chdir(dir))
+ goto ret;
+
+ /*
+ * If moving to the user's home directory, or, the path begins with
+ * "/", "./" or "../", it's the only place we try.
+ */
+ if (cmdp->argc == 0 ||
+ (ap = cmdp->argv[0])->bp[0] == '/' ||
+ ap->len == 1 && ap->bp[0] == '.' ||
+ ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' &&
+ (ap->bp[2] == '/' || ap->bp[2] == '\0'))
+ goto err;
+
+ /* If the user has a CDPATH variable, try its elements. */
+ for (cdp = EXP(sp)->cdq.tqh_first; cdp != NULL; cdp = cdp->q.tqe_next) {
+ (void)snprintf(buf, sizeof(buf), "%s/%s", cdp->path, dir);
+ if (!chdir(buf)) {
+ret: if (getcwd(buf, sizeof(buf)) != NULL)
+ msgq(sp, M_INFO, "New directory: %s", buf);
+ return (0);
+ }
+ }
+err: msgq(sp, M_SYSERR, "%s", dir);
+ return (1);
+}
+
+#define FREE_CDPATH(cdp) { \
+ TAILQ_REMOVE(&exp->cdq, (cdp), q); \
+ free((cdp)->path); \
+ FREE((cdp), sizeof(CDPATH)); \
+}
+/*
+ * ex_cdalloc --
+ * Create a new list of cd paths.
+ */
+int
+ex_cdalloc(sp, str)
+ SCR *sp;
+ char *str;
+{
+ EX_PRIVATE *exp;
+ CDPATH *cdp;
+ size_t len;
+ int founddot;
+ char *p, *t;
+
+ /* Free current queue. */
+ exp = EXP(sp);
+ while ((cdp = exp->cdq.tqh_first) != NULL)
+ FREE_CDPATH(cdp);
+
+ /*
+ * Create new queue. The CDPATH environmental variable (and the
+ * user's manual entry) are delimited by colon characters.
+ */
+ for (p = t = str, founddot = 0;; ++p) {
+ if (*p == '\0' || *p == ':') {
+ /*
+ * Empty strings specify ".". The only way to get an
+ * empty string is a leading colon, colons in a row,
+ * or a trailing colon. Or, to put it the other way,
+ * if the the length is zero, then it's either ":XXX",
+ * "XXX::XXXX" , "XXX:", or "", and the only failure
+ * mode is the last one. Note, the string ":" gives
+ * us two entries of '.', so we only include one of
+ * them.
+ */
+ if ((len = p - t) == 0) {
+ if (p == str && *p == '\0')
+ break;
+ if (founddot) {
+ if (*p == '\0')
+ break;
+ continue;
+ }
+ len = 1;
+ t = ".";
+ founddot = 1;
+ }
+ MALLOC_RET(sp, cdp, CDPATH *, sizeof(CDPATH));
+ MALLOC(sp, cdp->path, char *, len + 1);
+ if (cdp->path == NULL) {
+ free(cdp);
+ return (1);
+ }
+ memmove(cdp->path, t, len);
+ cdp->path[len] = '\0';
+ TAILQ_INSERT_TAIL(&exp->cdq, cdp, q);
+ t = p + 1;
+ }
+ if (*p == '\0')
+ break;
+ }
+ return (0);
+}
+ /* Free previous queue. */
+/*
+ * ex_cdfree --
+ * Free the cd path list.
+ */
+int
+ex_cdfree(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ CDPATH *cdp;
+
+ /* Free up cd path information. */
+ exp = EXP(sp);
+ while ((cdp = exp->cdq.tqh_first) != NULL)
+ FREE_CDPATH(cdp);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_delete.c b/usr.bin/vi/ex/ex_delete.c
new file mode 100644
index 0000000..4e1d241
--- /dev/null
+++ b/usr.bin/vi/ex/ex_delete.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_delete.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_delete: [line [,line]] d[elete] [buffer] [count] [flags]
+ *
+ * Delete lines from the file.
+ */
+int
+ex_delete(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ /*
+ * !!!
+ * Historically, lines deleted in ex were not placed in the numeric
+ * buffers. We follow historic practice so that we don't overwrite
+ * vi buffers accidentally.
+ */
+ if (cut(sp, ep,
+ F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE))
+ return (1);
+
+ /* Delete the lines. */
+ if (delete(sp, ep, &cmdp->addr1, &cmdp->addr2, 1))
+ return (1);
+
+ /* Set the cursor to the line after the last line deleted. */
+ sp->lno = cmdp->addr1.lno;
+
+ /* Or the last line in the file if deleted to the end of the file. */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (sp->lno > lno)
+ sp->lno = lno;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_digraph.c b/usr.bin/vi/ex/ex_digraph.c
new file mode 100644
index 0000000..0db20bb
--- /dev/null
+++ b/usr.bin/vi/ex/ex_digraph.c
@@ -0,0 +1,324 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_digraph.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#ifndef NO_DIGRAPH
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static void do_digraph __P((SCR *, EXF *, int, u_char *));
+
+/* This stuff is used to build the default digraphs table. */
+static u_char digtable[][4] = {
+# ifdef CS_IBMPC
+ "C,\200", "u\"\1", "e'\2", "a^\3",
+ "a\"\4", "a`\5", "a@\6", "c,\7",
+ "e^\10", "e\"\211", "e`\12", "i\"\13",
+ "i^\14", "i`\15", "A\"\16", "A@\17",
+ "E'\20", "ae\21", "AE\22", "o^\23",
+ "o\"\24", "o`\25", "u^\26", "u`\27",
+ "y\"\30", "O\"\31", "U\"\32", "a'\240",
+ "i'!", "o'\"", "u'#", "n~$",
+ "N~%", "a-&", "o-'", "~?(",
+ "~!-", "\"<.", "\">/",
+# ifdef CS_SPECIAL
+ "2/+", "4/,", "^+;", "^q<",
+ "^c=", "^r>", "^t?", "pp]",
+ "^^^", "oo_", "*a`", "*ba",
+ "*pc", "*Sd", "*se", "*uf",
+ "*tg", "*Ph", "*Ti", "*Oj",
+ "*dk", "*Hl", "*hm", "*En",
+ "*No", "eqp", "pmq", "ger",
+ "les", "*It", "*iu", "*/v",
+ "*=w", "sq{", "^n|", "^2}",
+ "^3~", "^_\377",
+# endif /* CS_SPECIAL */
+# endif /* CS_IBMPC */
+# ifdef CS_LATIN1
+ "~!!", "a-*", "\">+", "o-:",
+ "\"<>", "~??",
+
+ "A`@", "A'A", "A^B", "A~C",
+ "A\"D", "A@E", "AEF", "C,G",
+ "E`H", "E'I", "E^J", "E\"K",
+ "I`L", "I'M", "I^N", "I\"O",
+ "-DP", "N~Q", "O`R", "O'S",
+ "O^T", "O~U", "O\"V", "O/X",
+ "U`Y", "U'Z", "U^[", "U\"\\",
+ "Y'_",
+
+ "a``", "a'a", "a^b", "a~c",
+ "a\"d", "a@e", "aef", "c,g",
+ "e`h", "e'i", "e^j", "e\"k",
+ "i`l", "i'm", "i^n", "i\"o",
+ "-dp", "n~q", "o`r", "o's",
+ "o^t", "o~u", "o\"v", "o/x",
+ "u`y", "u'z", "u^{", "u\"|",
+ "y'~",
+# endif /* CS_LATIN1 */
+ ""
+};
+
+int
+digraph_init(sp)
+ SCR *sp;
+{
+ int i;
+
+ for (i = 0; *digtable[i]; i++)
+ do_digraph(sp, NULL, 0, digtable[i]);
+ do_digraph(sp, NULL, 0, NULL);
+ return (0);
+}
+
+int
+ex_digraph(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ do_digraph(sp, ep, F_ISSET(cmdp, E_FORCE), cmdp->argv[0]->bp);
+ return (0);
+}
+
+static struct _DIG
+{
+ struct _DIG *next;
+ char key1;
+ char key2;
+ char dig;
+ char save;
+} *digs;
+
+int
+digraph(sp, key1, key2)
+ SCR *sp;
+ char key1; /* the underlying character */
+ char key2; /* the second character */
+{
+ int new_key;
+ register struct _DIG *dp;
+
+ /* if digraphs are disabled, then just return the new char */
+ if (O_ISSET(sp, O_DIGRAPH))
+ {
+ return key2;
+ }
+
+ /* remember the new key, so we can return it if this isn't a digraph */
+ new_key = key2;
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (key1 > key2)
+ {
+ key2 = key1;
+ key1 = new_key;
+ }
+
+ /* scan through the digraph chart */
+ for (dp = digs;
+ dp && (dp->key1 != key1 || dp->key2 != key2);
+ dp = dp->next)
+ {
+ }
+
+ /* if this combination isn't in there, just use the new key */
+ if (!dp)
+ {
+ return new_key;
+ }
+
+ /* else use the digraph key */
+ return dp->dig;
+}
+
+/* this function lists or defines digraphs */
+static void
+do_digraph(sp, ep, bang, extra)
+ SCR *sp;
+ EXF *ep;
+ int bang;
+ u_char *extra;
+{
+ int dig;
+ register struct _DIG *dp;
+ struct _DIG *prev;
+ static int user_defined = 0; /* boolean: are all later digraphs user-defined? */
+ char listbuf[8];
+
+ /* if "extra" is NULL, then we've reached the end of the built-ins */
+ if (!extra)
+ {
+ user_defined = 1;
+ return;
+ }
+
+ /* if no args, then display the existing digraphs */
+ if (*extra < ' ')
+ {
+ listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
+ listbuf[7] = '\0';
+ for (dig = 0, dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save || bang)
+ {
+ dig += 7;
+ if (dig >= sp->cno)
+ {
+ addch('\n');
+ refresh();
+ dig = 7;
+ }
+ listbuf[3] = dp->key1;
+ listbuf[4] = dp->key2;
+ listbuf[6] = dp->dig;
+ addstr(listbuf);
+ }
+ }
+ addch('\n');
+ refresh();
+ return;
+ }
+
+ /* make sure we have at least two characters */
+ if (!extra[1])
+ {
+ msgq(sp, M_ERR,
+ "Digraphs must be composed of two characters");
+ return;
+ }
+
+ /* sort key1 and key2, so that their original order won't matter */
+ if (extra[0] > extra[1])
+ {
+ dig = extra[0];
+ extra[0] = extra[1];
+ extra[1] = dig;
+ }
+
+ /* locate the new digraph character */
+ for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
+ {
+ }
+ dig = extra[dig];
+ if (!bang && dig)
+ {
+ dig |= 0x80;
+ }
+
+ /* search for the digraph */
+ for (prev = (struct _DIG *)0, dp = digs;
+ dp && (dp->key1 != extra[0] || dp->key2 != extra[1]);
+ prev = dp, dp = dp->next)
+ {
+ }
+
+ /* deleting the digraph? */
+ if (!dig)
+ {
+ if (!dp)
+ {
+#ifndef CRUNCH
+ msgq(sp, M_ERR,
+ "%c%c not a digraph", extra[0], extra[1]);
+#endif
+ return;
+ }
+ if (prev)
+ prev->next = dp->next;
+ else
+ digs = dp->next;
+ free(dp);
+ return;
+ }
+
+ /* if necessary, create a new digraph struct for the new digraph */
+ if (dig && !dp)
+ {
+ MALLOC(sp, dp, struct _DIG *, sizeof(struct _DIG));
+ if (dp == NULL)
+ return;
+ if (prev)
+ prev->next = dp;
+ else
+ digs = dp;
+ dp->next = (struct _DIG *)0;
+ }
+
+ /* assign it the new digraph value */
+ dp->key1 = extra[0];
+ dp->key2 = extra[1];
+ dp->dig = dig;
+ dp->save = user_defined;
+}
+
+void
+digraph_save(sp, fd)
+ SCR *sp;
+ int fd;
+{
+ static char buf[] = "digraph! XX Y\n";
+ register struct _DIG *dp;
+
+ for (dp = digs; dp; dp = dp->next)
+ {
+ if (dp->save)
+ {
+ buf[9] = dp->key1;
+ buf[10] = dp->key2;
+ buf[12] = dp->dig;
+ write(fd, buf, (unsigned)14);
+ }
+ }
+}
+#endif
diff --git a/usr.bin/vi/ex/ex_display.c b/usr.bin/vi/ex/ex_display.c
new file mode 100644
index 0000000..7f90ef2
--- /dev/null
+++ b/usr.bin/vi/ex/ex_display.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_display.c 8.23 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "tag.h"
+#include "excmd.h"
+
+static int bdisplay __P((SCR *, EXF *));
+static void db __P((SCR *, CB *, CHAR_T *));
+
+/*
+ * ex_display -- :display b[uffers] | s[creens] | t[ags]
+ *
+ * Display buffers, tags or screens.
+ */
+int
+ex_display(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ switch (cmdp->argv[0]->bp[0]) {
+ case 'b':
+#undef ARG
+#define ARG "buffers"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (bdisplay(sp, ep));
+ case 's':
+#undef ARG
+#define ARG "screens"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_sdisplay(sp, ep));
+ case 't':
+#undef ARG
+#define ARG "tags"
+ if (cmdp->argv[0]->len >= sizeof(ARG) ||
+ memcmp(cmdp->argv[0]->bp, ARG, cmdp->argv[0]->len))
+ break;
+ return (ex_tagdisplay(sp, ep));
+ }
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+}
+
+/*
+ * bdisplay --
+ *
+ * Display buffers.
+ */
+static int
+bdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ CB *cbp;
+
+ if (sp->gp->cutq.lh_first == NULL && sp->gp->dcbp == NULL) {
+ (void)ex_printf(EXCOOKIE, "No cut buffers to display.\n");
+ return (0);
+ }
+
+ /* Buffers can be infinitely long, make it interruptible. */
+ F_SET(sp, S_INTERRUPTIBLE);
+
+ /* Display regular cut buffers. */
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+ if (isdigit(cbp->name))
+ continue;
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ db(sp, cbp, NULL);
+ if (INTERRUPTED(sp))
+ return (0);
+ }
+ /* Display numbered buffers. */
+ for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next) {
+ if (!isdigit(cbp->name))
+ continue;
+ if (cbp->textq.cqh_first != (void *)&cbp->textq)
+ db(sp, cbp, NULL);
+ if (INTERRUPTED(sp))
+ return (0);
+ }
+ /* Display default buffer. */
+ if ((cbp = sp->gp->dcbp) != NULL)
+ db(sp, cbp, "default buffer");
+ return (0);
+}
+
+/*
+ * db --
+ * Display a buffer.
+ */
+static void
+db(sp, cbp, name)
+ SCR *sp;
+ CB *cbp;
+ CHAR_T *name;
+{
+ CHAR_T *p;
+ TEXT *tp;
+ size_t len;
+
+ (void)ex_printf(EXCOOKIE, "********** %s%s\n",
+ name == NULL ? KEY_NAME(sp, cbp->name) : name,
+ F_ISSET(cbp, CB_LMODE) ? " (line mode)" : " (character mode)");
+ for (tp = cbp->textq.cqh_first;
+ tp != (void *)&cbp->textq; tp = tp->q.cqe_next) {
+ for (len = tp->len, p = tp->lb; len--; ++p) {
+ (void)ex_printf(EXCOOKIE, "%s", KEY_NAME(sp, *p));
+ if (INTERRUPTED(sp))
+ return;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+}
diff --git a/usr.bin/vi/ex/ex_edit.c b/usr.bin/vi/ex/ex_edit.c
new file mode 100644
index 0000000..5a54e73
--- /dev/null
+++ b/usr.bin/vi/ex/ex_edit.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_edit.c 8.20 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_edit -- :e[dit][!] [+cmd] [file]
+ * :vi[sual][!] [+cmd] [file]
+ *
+ * Edit a file; if none specified, re-edit the current file. The second
+ * form of the command can only be executed while in vi mode. See the
+ * hack in ex.c:ex_cmd().
+ *
+ * !!!
+ * Historic vi didn't permit the '+' command form without specifying
+ * a file name as well.
+ */
+int
+ex_edit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ FREF *frp;
+
+ frp = sp->frp;
+ switch (cmdp->argc) {
+ case 0:
+ /*
+ * If the name has been changed, we edit that file, not the
+ * original name. If the user was editing a temporary file,
+ * create another one. The reason for this is that we do
+ * special exit processing of temporary files, and reusing
+ * them is tricky.
+ */
+ if (F_ISSET(frp, FR_TMPFILE)) {
+ if ((frp = file_add(sp, NULL)) == NULL)
+ return (1);
+ } else {
+ if ((frp = file_add(sp, frp->name)) == NULL)
+ return (1);
+ set_alt_name(sp, sp->frp->name);
+ }
+ break;
+ case 1:
+ ap = cmdp->argv[0];
+ if ((frp = file_add(sp, ap->bp)) == NULL)
+ return (1);
+ set_alt_name(sp, ap->bp);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * Check for modifications.
+ *
+ * !!!
+ * Contrary to POSIX 1003.2-1992, autowrite did not affect :edit.
+ */
+ if (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ /* Switch files. */
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_equal.c b/usr.bin/vi/ex/ex_equal.c
new file mode 100644
index 0000000..ec979a0
--- /dev/null
+++ b/usr.bin/vi/ex/ex_equal.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_equal.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_equal -- :address =
+ */
+int
+ex_equal(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ /*
+ * Print out the line number matching the specified address,
+ * or the number of the last line in the file if no address
+ * specified.
+ *
+ * !!!
+ * Historically, ":0=" displayed 0, and ":=" or ":1=" in an
+ * empty file displayed 1. Until somebody complains loudly,
+ * we're going to do it right. The tables in excmd.c permit
+ * lno to get away with any address from 0 to the end of the
+ * file, which, in an empty file, is 0.
+ */
+ if (F_ISSET(cmdp, E_ADDRDEF)) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ } else
+ lno = cmdp->addr1.lno;
+
+ (void)ex_printf(EXCOOKIE, "%ld\n", lno);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_exit.c b/usr.bin/vi/ex/ex_exit.c
new file mode 100644
index 0000000..79c4039
--- /dev/null
+++ b/usr.bin/vi/ex/ex_exit.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_exit.c 8.15 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_quit -- :quit[!]
+ * Quit.
+ */
+int
+ex_quit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ /* Check for modifications. */
+ if (file_m2(sp, ep, force))
+ return (1);
+
+ /* Check for more files to edit. */
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_file.c b/usr.bin/vi/ex/ex_file.c
new file mode 100644
index 0000000..cf5f329
--- /dev/null
+++ b/usr.bin/vi/ex/ex_file.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_file.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_file -- :f[ile] [name]
+ * Change the file's name and display the status line.
+ */
+int
+ex_file(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CHAR_T *p;
+ FREF *frp;
+
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ frp = sp->frp;
+
+ /* Make sure can allocate enough space. */
+ if ((p = v_strdup(sp,
+ cmdp->argv[0]->bp, cmdp->argv[0]->len)) == NULL)
+ return (1);
+
+ /* If already have a file name, it becomes the alternate. */
+ if (!F_ISSET(frp, FR_TMPFILE))
+ set_alt_name(sp, frp->name);
+
+ /* Free the previous name. */
+ free(frp->name);
+ frp->name = p;
+
+ /*
+ * The read-only bit follows the file name; clear it.
+ * The file has a real name, it's no longer a temporary.
+ */
+ F_CLR(frp, FR_RDONLY | FR_TMPFILE);
+
+ /* Have to force a write if the file exists, next time. */
+ F_SET(frp, FR_NAMECHANGE);
+ break;
+ default:
+ abort();
+ }
+ return (msg_status(sp, ep, sp->lno, 1));
+}
diff --git a/usr.bin/vi/ex/ex_global.c b/usr.bin/vi/ex/ex_global.c
new file mode 100644
index 0000000..5fb134d
--- /dev/null
+++ b/usr.bin/vi/ex/ex_global.c
@@ -0,0 +1,400 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_global.c 8.43 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {GLOBAL, VGLOBAL};
+
+static int global __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_global -- [line [,line]] g[lobal][!] /pattern/ [commands]
+ * Exec on lines matching a pattern.
+ */
+int
+ex_global(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (global(sp, ep,
+ cmdp, F_ISSET(cmdp, E_FORCE) ? VGLOBAL : GLOBAL));
+}
+
+/*
+ * ex_vglobal -- [line [,line]] v[global] /pattern/ [commands]
+ * Exec on lines not matching a pattern.
+ */
+int
+ex_vglobal(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (global(sp, ep, cmdp, VGLOBAL));
+}
+
+static int
+global(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ MARK abs;
+ RANGE *rp;
+ EX_PRIVATE *exp;
+ recno_t elno, lno;
+ regmatch_t match[1];
+ regex_t *re, lre;
+ size_t clen, len;
+ int delim, eval, reflags, replaced, rval;
+ char *cb, *ptrn, *p, *t;
+
+ /*
+ * Skip leading white space. Historic vi allowed any non-
+ * alphanumeric to serve as the global command delimiter.
+ */
+ for (p = cmdp->argv[0]->bp; isblank(*p); ++p);
+ if (*p == '\0' || isalnum(*p)) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+ delim = *p++;
+
+ /*
+ * Get the pattern string, toss escaped characters.
+ *
+ * QUOTING NOTE:
+ * Only toss an escaped character if it escapes a delimiter.
+ */
+ for (ptrn = t = p;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ /*
+ * !!!
+ * Nul terminate the pattern string -- it's passed
+ * to regcomp which doesn't understand anything else.
+ */
+ *t = '\0';
+ break;
+ }
+ if (p[0] == '\\' && p[1] == delim)
+ ++p;
+ *t++ = *p++;
+ }
+
+ /* If the pattern string is empty, use the last one. */
+ if (*ptrn == '\0') {
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression");
+ return (1);
+ }
+ re = &sp->sre;
+ } else {
+ /* Set RE flags. */
+ reflags = 0;
+ if (O_ISSET(sp, O_EXTENDED))
+ reflags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ reflags |= REG_ICASE;
+
+ /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+
+ /* Compile the RE. */
+ re = &lre;
+ eval = regcomp(re, ptrn, reflags);
+
+ /* Free up any allocated memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+
+ if (eval) {
+ re_error(sp, eval, re);
+ return (1);
+ }
+
+ /*
+ * Set saved RE. Historic practice is that
+ * globals set direction as well as the RE.
+ */
+ sp->sre = lre;
+ sp->searchdir = FORWARD;
+ F_SET(sp, S_SRE_SET);
+ }
+
+ /*
+ * Get a copy of the command string; the default command is print.
+ * Don't worry about a set of <blank>s with no command, that will
+ * default to print in the ex parser.
+ */
+ if ((clen = strlen(p)) == 0) {
+ p = "p";
+ clen = 1;
+ }
+ MALLOC_RET(sp, cb, char *, clen);
+ memmove(cb, p, clen);
+
+ /*
+ * The global commands sets the substitute RE as well as
+ * the everything-else RE.
+ */
+ sp->subre = sp->sre;
+ F_SET(sp, S_SUBRE_SET);
+
+ /* Set the global flag. */
+ F_SET(sp, S_GLOBAL);
+
+ /* The global commands always set the previous context mark. */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ if (mark_set(sp, ep, ABSMARK1, &abs, 1))
+ goto err;
+
+ /*
+ * For each line... The semantics of global matching are that we first
+ * have to decide which lines are going to get passed to the command,
+ * and then pass them to the command, ignoring other changes. There's
+ * really no way to do this in a single pass, since arbitrary line
+ * creation, deletion and movement can be done in the ex command. For
+ * example, a good vi clone test is ":g/X/mo.-3", or "g/X/.,.+1d".
+ * What we do is create linked list of lines that are tracked through
+ * each ex command. There's a callback routine which the DB interface
+ * routines call when a line is created or deleted. This doesn't help
+ * the layering much.
+ */
+ exp = EXP(sp);
+ for (rval = 0, lno = cmdp->addr1.lno,
+ elno = cmdp->addr2.lno; lno <= elno; ++lno) {
+ /* Someone's unhappy, time to stop. */
+ if (INTERRUPTED(sp))
+ goto interrupted;
+
+ /* Get the line and search for a match. */
+ if ((t = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ goto err;
+ }
+ match[0].rm_so = 0;
+ match[0].rm_eo = len;
+ switch(eval = regexec(re, t, 1, match, REG_STARTEND)) {
+ case 0:
+ if (cmd == VGLOBAL)
+ continue;
+ break;
+ case REG_NOMATCH:
+ if (cmd == GLOBAL)
+ continue;
+ break;
+ default:
+ re_error(sp, eval, re);
+ goto err;
+ }
+
+ /* If follows the last entry, extend the last entry's range. */
+ if ((rp = exp->rangeq.cqh_last) != (void *)&exp->rangeq &&
+ rp->stop == lno - 1) {
+ ++rp->stop;
+ continue;
+ }
+
+ /* Allocate a new range, and append it to the list. */
+ CALLOC(sp, rp, RANGE *, 1, sizeof(RANGE));
+ if (rp == NULL)
+ goto err;
+ rp->start = rp->stop = lno;
+ CIRCLEQ_INSERT_TAIL(&exp->rangeq, rp, q);
+ }
+
+ exp = EXP(sp);
+ exp->range_lno = OOBLNO;
+ for (;;) {
+ /*
+ * Start at the beginning of the range each time, it may have
+ * been changed (or exhausted) if lines were inserted/deleted.
+ */
+ if ((rp = exp->rangeq.cqh_first) == (void *)&exp->rangeq)
+ break;
+ if (rp->start > rp->stop) {
+ CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+ free(rp);
+ continue;
+ }
+
+ /*
+ * Execute the command, setting the cursor to the line so that
+ * relative addressing works. This means that the cursor moves
+ * to the last line sent to the command, by default, even if
+ * the command fails.
+ */
+ exp->range_lno = sp->lno = rp->start++;
+ if (ex_cmd(sp, ep, cb, clen, 0))
+ goto err;
+
+ /* Someone's unhappy, time to stop. */
+ if (INTERRUPTED(sp)) {
+interrupted: msgq(sp, M_INFO, "Interrupted");
+ break;
+ }
+ }
+
+ /* Set the cursor to the new value, making sure it exists. */
+ if (exp->range_lno != OOBLNO) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ sp->lno =
+ lno < exp->range_lno ? (lno ? lno : 1) : exp->range_lno;
+ }
+ if (0) {
+err: rval = 1;
+ }
+
+ /* Command we ran may have set the autoprint flag, clear it. */
+ F_CLR(exp, EX_AUTOPRINT);
+
+ /* Clear the global flag. */
+ F_CLR(sp, S_GLOBAL);
+
+ /* Free any remaining ranges and the command buffer. */
+ while ((rp = exp->rangeq.cqh_first) != (void *)&exp->rangeq) {
+ CIRCLEQ_REMOVE(&exp->rangeq, exp->rangeq.cqh_first, q);
+ free(rp);
+ }
+ free(cb);
+ return (rval);
+}
+
+/*
+ * global_insdel --
+ * Update the ranges based on an insertion or deletion.
+ */
+void
+global_insdel(sp, ep, op, lno)
+ SCR *sp;
+ EXF *ep;
+ enum operation op;
+ recno_t lno;
+{
+ EX_PRIVATE *exp;
+ RANGE *nrp, *rp;
+
+ exp = EXP(sp);
+
+ switch (op) {
+ case LINE_APPEND:
+ return;
+ case LINE_DELETE:
+ for (rp = exp->rangeq.cqh_first;
+ rp != (void *)&exp->rangeq; rp = nrp) {
+ nrp = rp->q.cqe_next;
+ /* If range less than the line, ignore it. */
+ if (rp->stop < lno)
+ continue;
+ /* If range greater than the line, decrement range. */
+ if (rp->start > lno) {
+ --rp->start;
+ --rp->stop;
+ continue;
+ }
+ /* Lno is inside the range, decrement the end point. */
+ if (rp->start > --rp->stop) {
+ CIRCLEQ_REMOVE(&exp->rangeq, rp, q);
+ free(rp);
+ }
+ }
+ break;
+ case LINE_INSERT:
+ for (rp = exp->rangeq.cqh_first;
+ rp != (void *)&exp->rangeq; rp = rp->q.cqe_next) {
+ /* If range less than the line, ignore it. */
+ if (rp->stop < lno)
+ continue;
+ /* If range greater than the line, increment range. */
+ if (rp->start >= lno) {
+ ++rp->start;
+ ++rp->stop;
+ continue;
+ }
+ /*
+ * Lno is inside the range, so the range must be split.
+ * Since we're inserting a new element, neither range
+ * can be exhausted.
+ */
+ CALLOC(sp, nrp, RANGE *, 1, sizeof(RANGE));
+ if (nrp == NULL) {
+ F_SET(sp, S_INTERRUPTED);
+ return;
+ }
+ nrp->start = lno + 1;
+ nrp->stop = rp->stop + 1;
+ rp->stop = lno - 1;
+ CIRCLEQ_INSERT_AFTER(&exp->rangeq, rp, nrp, q);
+ rp = nrp;
+ }
+ break;
+ case LINE_RESET:
+ return;
+ }
+ /*
+ * If the command deleted/inserted lines, the cursor moves to
+ * the line after the deleted/inserted line.
+ */
+ exp->range_lno = lno;
+}
diff --git a/usr.bin/vi/ex/ex_init.c b/usr.bin/vi/ex/ex_init.c
new file mode 100644
index 0000000..46f9c37
--- /dev/null
+++ b/usr.bin/vi/ex/ex_init.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_init.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+/*
+ * ex_screen_copy --
+ * Copy ex screen.
+ */
+int
+ex_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ EX_PRIVATE *oexp, *nexp;
+
+ /* Create the private ex structure. */
+ CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE));
+ sp->ex_private = nexp;
+
+ /* Initialize queues. */
+ TAILQ_INIT(&nexp->tagq);
+ TAILQ_INIT(&nexp->tagfq);
+ TAILQ_INIT(&nexp->cdq);
+ CIRCLEQ_INIT(&nexp->rangeq);
+
+ if (orig == NULL) {
+ nexp->at_lbuf_set = 0;
+ } else {
+ oexp = EXP(orig);
+
+ nexp->at_lbuf = oexp->at_lbuf;
+ nexp->at_lbuf_set = oexp->at_lbuf_set;
+
+ if (oexp->lastbcomm != NULL &&
+ (nexp->lastbcomm = strdup(oexp->lastbcomm)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return(1);
+ }
+
+ if (ex_tagcopy(orig, sp))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * ex_screen_end --
+ * End a vi screen.
+ */
+int
+ex_screen_end(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ int rval;
+
+ rval = 0;
+ exp = EXP(sp);
+
+ if (argv_free(sp))
+ rval = 1;
+
+ if (exp->ibp != NULL)
+ FREE(exp->ibp, exp->ibp_len);
+
+ if (exp->lastbcomm != NULL)
+ FREE(exp->lastbcomm, strlen(exp->lastbcomm) + 1);
+
+ if (ex_tagfree(sp))
+ rval = 1;
+
+ if (ex_cdfree(sp))
+ rval = 1;
+
+ /* Free private memory. */
+ FREE(exp, sizeof(EX_PRIVATE));
+ sp->ex_private = NULL;
+
+ return (rval);
+}
+
+/*
+ * ex_init --
+ * Initialize ex.
+ */
+int
+ex_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t len;
+
+ /*
+ * The default address is the last line of the file. If the address
+ * set bit is on for this file, load the address, ensuring that it
+ * exists.
+ */
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ } else if (sp->cno >= len)
+ sp->cno = 0;
+ } else {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+
+ /* Display the status line. */
+ return (msg_status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * ex_end --
+ * End ex session.
+ */
+int
+ex_end(sp)
+ SCR *sp;
+{
+ return (0);
+}
+
+/*
+ * ex_optchange --
+ * Handle change of options for vi.
+ */
+int
+ex_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_CDPATH:
+ return (ex_cdalloc(sp, O_STR(sp, O_CDPATH)));
+ case O_TAGS:
+ return (ex_tagalloc(sp, O_STR(sp, O_TAGS)));
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_join.c b/usr.bin/vi/ex/ex_join.c
new file mode 100644
index 0000000..4ac045b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_join.c
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_join.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_join -- :[line [,line]] j[oin][!] [count] [flags]
+ * Join lines.
+ */
+int
+ex_join(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t from, to;
+ size_t blen, clen, len, tlen;
+ int echar, extra, first;
+ char *bp, *p, *tbp;
+
+ from = cmdp->addr1.lno;
+ to = cmdp->addr2.lno;
+
+ /* Check for no lines to join. */
+ if ((p = file_gline(sp, ep, from + 1, &len)) == NULL) {
+ msgq(sp, M_ERR, "No following lines to join");
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /*
+ * The count for the join command was off-by-one,
+ * historically, to other counts for other commands.
+ */
+ if (F_ISSET(cmdp, E_COUNT))
+ ++cmdp->addr2.lno;
+
+ /*
+ * If only a single address specified, or, the same address
+ * specified twice, the from/two addresses will be the same.
+ */
+ if (cmdp->addr1.lno == cmdp->addr2.lno)
+ ++cmdp->addr2.lno;
+
+ clen = tlen = 0;
+ for (first = 1,
+ from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
+ /*
+ * Get next line. Historic versions of vi allowed "10J" while
+ * less than 10 lines from the end-of-file, so we do too.
+ */
+ if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+ cmdp->addr2.lno = from - 1;
+ break;
+ }
+
+ /* Empty lines just go away. */
+ if (len == 0)
+ continue;
+
+ /*
+ * Get more space if necessary. Note, tlen isn't the length
+ * of the new line, it's roughly the amount of space needed.
+ * tbp - bp is the length of the new line.
+ */
+ tlen += len + 2;
+ ADD_SPACE_RET(sp, bp, blen, tlen);
+ tbp = bp + clen;
+
+ /*
+ * Historic practice:
+ *
+ * If force specified, join without modification.
+ * If the current line ends with whitespace, strip leading
+ * whitespace from the joined line.
+ * If the next line starts with a ), do nothing.
+ * If the current line ends with ., ? or !, insert two spaces.
+ * Else, insert one space.
+ *
+ * Echar is the last character in the last line joined.
+ */
+ extra = 0;
+ if (!first && !F_ISSET(cmdp, E_FORCE)) {
+ if (isblank(echar))
+ for (; len && isblank(*p); --len, ++p);
+ else if (p[0] != ')') {
+ if (strchr(".?!", echar)) {
+ *tbp++ = ' ';
+ ++clen;
+ extra = 1;
+ }
+ *tbp++ = ' ';
+ ++clen;
+ for (; len && isblank(*p); --len, ++p);
+ }
+ }
+
+ if (len != 0) {
+ memmove(tbp, p, len);
+ tbp += len;
+ clen += len;
+ echar = p[len - 1];
+ } else
+ echar = ' ';
+
+ /*
+ * Historic practice for vi was to put the cursor at the first
+ * inserted whitespace character, if there was one, or the
+ * first character of the joined line, if there wasn't, or the
+ * last character of the line if joined to an empty line. If
+ * a count was specified, the cursor was moved as described
+ * for the first line joined, ignoring subsequent lines. If
+ * the join was a ':' command, the cursor was placed at the
+ * first non-blank character of the line unless the cursor was
+ * "attracted" to the end of line when the command was executed
+ * in which case it moved to the new end of line. There are
+ * probably several more special cases, but frankly, my dear,
+ * I don't give a damn. This implementation puts the cursor
+ * on the first inserted whitespace character, the first
+ * character of the joined line, or the last character of the
+ * line regardless. Note, if the cursor isn't on the joined
+ * line (possible with : commands), it is reset to the starting
+ * line.
+ */
+ if (first) {
+ sp->cno = (tbp - bp) - (1 + extra);
+ first = 0;
+ } else
+ sp->cno = (tbp - bp) - len - (1 + extra);
+ }
+ sp->lno = cmdp->addr1.lno;
+
+ /* Delete the joined lines. */
+ for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; to > from; --to)
+ if (file_dline(sp, ep, to))
+ goto err;
+
+ /* If the original line changed, reset it. */
+ if (!first && file_sline(sp, ep, from, bp, tbp - bp)) {
+err: FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ sp->rptlines[L_JOINED] += (cmdp->addr2.lno - cmdp->addr1.lno) + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_map.c b/usr.bin/vi/ex/ex_map.c
new file mode 100644
index 0000000..ec67dbd
--- /dev/null
+++ b/usr.bin/vi/ex/ex_map.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_map.c 8.19 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_map -- :map[!] [input] [replacement]
+ * Map a key/string or display mapped keys.
+ *
+ * Historical note:
+ * Historic vi maps were fairly bizarre, and likely to differ in
+ * very subtle and strange ways from this implementation. Two
+ * things worth noting are that vi would often hang or drop core
+ * if the map was strange enough (ex: map X "xy$@x^V), or, simply
+ * not work. One trick worth remembering is that if you put a
+ * mark at the start of the map, e.g. map X mx"xy ...), or if you
+ * put the map in a .exrc file, things would often work much better.
+ * No clue why.
+ */
+int
+ex_map(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum seqtype stype;
+ CHAR_T *input, *p;
+
+ stype = F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND;
+
+ switch (cmdp->argc) {
+ case 0:
+ if (seq_dump(sp, stype, 1) == 0)
+ msgq(sp, M_INFO, "No %s map entries",
+ stype == SEQ_INPUT ? "input" : "command");
+ return (0);
+ case 2:
+ input = cmdp->argv[0]->bp;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * If the mapped string is #[0-9]* (and wasn't quoted) then store
+ * the function key mapping, and call the screen specific routine.
+ * Note, if the screen specific routine is able to create the
+ * mapping, the SEQ_FUNCMAP type stays around, maybe the next screen
+ * type can get it right.
+ */
+ if (input[0] == '#') {
+ for (p = input + 1; isdigit(*p); ++p);
+ if (p[0] != '\0')
+ goto nofunc;
+
+ if (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_FUNCMAP))
+ return (1);
+ return (sp->s_fmap(sp, stype, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len));
+ }
+
+ /* Some single keys may not be remapped in command mode. */
+nofunc: if (stype == SEQ_COMMAND && input[1] == '\0')
+ switch (KEY_VAL(sp, input[0])) {
+ case K_COLON:
+ case K_ESCAPE:
+ case K_NL:
+ msgq(sp, M_ERR, "The %s character may not be remapped",
+ KEY_NAME(sp, input[0]));
+ return (1);
+ }
+ return (seq_set(sp, NULL, 0, input, cmdp->argv[0]->len,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len, stype, SEQ_USERDEF));
+}
+
+/*
+ * ex_unmap -- (:unmap[!] key)
+ * Unmap a key.
+ */
+int
+ex_unmap(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (seq_delete(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len,
+ F_ISSET(cmdp, E_FORCE) ? SEQ_INPUT : SEQ_COMMAND)) {
+ msgq(sp, M_INFO, "\"%s\" isn't mapped", cmdp->argv[0]->bp);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * map_save --
+ * Save the mapped sequences to a file.
+ */
+int
+map_save(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ if (seq_save(sp, fp, "map ", SEQ_COMMAND))
+ return (1);
+ return (seq_save(sp, fp, "map! ", SEQ_INPUT));
+}
diff --git a/usr.bin/vi/ex/ex_mark.c b/usr.bin/vi/ex/ex_mark.c
new file mode 100644
index 0000000..b74bfb1
--- /dev/null
+++ b/usr.bin/vi/ex/ex_mark.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mark.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_mark(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (cmdp->argv[0]->len != 1) {
+ msgq(sp, M_ERR, "Mark names must be a single character");
+ return (1);
+ }
+ return (mark_set(sp, ep, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
+}
diff --git a/usr.bin/vi/ex/ex_mkexrc.c b/usr.bin/vi/ex/ex_mkexrc.c
new file mode 100644
index 0000000..158fb23
--- /dev/null
+++ b/usr.bin/vi/ex/ex_mkexrc.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_mkexrc.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_mkexrc -- :mkexrc[!] [file]
+ *
+ * Create (or overwrite) a .exrc file with the current info.
+ */
+int
+ex_mkexrc(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct stat sb;
+ FILE *fp;
+ int fd, sverrno;
+ char *fname;
+
+ switch (cmdp->argc) {
+ case 0:
+ fname = _PATH_EXRC;
+ break;
+ case 1:
+ fname = cmdp->argv[0]->bp;
+ set_alt_name(sp, fname);
+ break;
+ default:
+ abort();
+ }
+
+ if (!F_ISSET(cmdp, E_FORCE) && !stat(fname, &sb)) {
+ msgq(sp, M_ERR,
+ "%s exists, not written; use ! to override", fname);
+ return (1);
+ }
+
+ /* Create with max permissions of rw-r--r--. */
+ if ((fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+ msgq(sp, M_SYSERR, fname);
+ return (1);
+ }
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ sverrno = errno;
+ (void)close(fd);
+ errno = sverrno;
+ goto e2;
+ }
+
+ if (abbr_save(sp, fp) || ferror(fp))
+ goto e1;
+ if (map_save(sp, fp) || ferror(fp))
+ goto e1;
+ if (opts_save(sp, fp) || ferror(fp))
+ goto e1;
+#ifndef NO_DIGRAPH
+ digraph_save(sp, fd);
+#endif
+ if (fclose(fp))
+ goto e2;
+
+ msgq(sp, M_INFO, "New .exrc file: %s. ", fname);
+ return (0);
+
+e1: sverrno = errno;
+ (void)fclose(fp);
+ errno = sverrno;
+e2: msgq(sp, M_ERR, "%s: incomplete: %s", fname, strerror(errno));
+ return (1);
+}
diff --git a/usr.bin/vi/ex/ex_move.c b/usr.bin/vi/ex/ex_move.c
new file mode 100644
index 0000000..52a8f53
--- /dev/null
+++ b/usr.bin/vi/ex/ex_move.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_move.c 8.19 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_copy -- :[line [,line]] co[py] line [flags]
+ * Copy selected lines.
+ */
+int
+ex_copy(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ CB cb;
+ MARK fm1, fm2, m, tm;
+ recno_t cnt;
+ int rval;
+
+ rval = 0;
+
+ /*
+ * It's possible to copy things into the area that's being
+ * copied, e.g. "2,5copy3" is legitimate. Save the text to
+ * a cut buffer.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ memset(&cb, 0, sizeof(cb));
+ CIRCLEQ_INIT(&cb.textq);
+ for (cnt = fm1.lno; cnt <= fm2.lno; ++cnt)
+ if (cut_line(sp, ep, cnt, 0, 0, &cb)) {
+ rval = 1;
+ goto err;
+ }
+ cb.flags |= CB_LMODE;
+
+ /* Put the text into place. */
+ tm.lno = cmdp->lineno;
+ tm.cno = 0;
+ if (put(sp, ep, &cb, NULL, &tm, &m, 1))
+ rval = 1;
+ else {
+ /*
+ * Copy puts the cursor on the last line copied. The cursor
+ * returned by the put routine is the first line put, not the
+ * last, because that's the historic semantic of vi.
+ */
+ cnt = (fm2.lno - fm1.lno) + 1;
+ sp->lno = m.lno + (cnt - 1);
+ sp->cno = 0;
+ }
+err: text_lfree(&cb.textq);
+ return (rval);
+}
+
+/*
+ * ex_move -- :[line [,line]] mo[ve] line
+ * Move selected lines.
+ */
+int
+ex_move(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ LMARK *lmp;
+ MARK fm1, fm2;
+ recno_t cnt, diff, fl, tl, mfl, mtl;
+ size_t blen, len;
+ int mark_reset;
+ char *bp, *p;
+
+ /*
+ * It's not possible to move things into the area that's being
+ * moved.
+ */
+ fm1 = cmdp->addr1;
+ fm2 = cmdp->addr2;
+ if (cmdp->lineno >= fm1.lno && cmdp->lineno <= fm2.lno) {
+ msgq(sp, M_ERR, "Destination line is inside move range");
+ return (1);
+ }
+
+ /*
+ * Log the positions of any marks in the to-be-deleted lines. This
+ * has to work with the logging code. What happens is that we log
+ * the old mark positions, make the changes, then log the new mark
+ * positions. Then the marks end up in the right positions no matter
+ * which way the log is traversed.
+ *
+ * XXX
+ * Reset the MARK_USERSET flag so that the log can undo the mark.
+ * This isn't very clean, and should probably be fixed.
+ */
+ fl = fm1.lno;
+ tl = cmdp->lineno;
+
+ /* Log the old positions of the marks. */
+ mark_reset = 0;
+ for (lmp = ep->marks.lh_first; lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= fl && lmp->lno <= tl) {
+ mark_reset = 1;
+ F_CLR(lmp, MARK_USERSET);
+ (void)log_mark(sp, ep, lmp);
+ }
+
+ /* Get memory for the copy. */
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ /* Move the lines. */
+ diff = (fm2.lno - fm1.lno) + 1;
+ if (tl > fl) { /* Destination > source. */
+ mfl = tl - diff;
+ mtl = tl;
+ for (cnt = diff; cnt--;) {
+ if ((p = file_gline(sp, ep, fl, &len)) == NULL)
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ if (file_aline(sp, ep, 1, tl, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl + 1;
+ if (file_dline(sp, ep, fl))
+ return (1);
+ }
+ } else { /* Destination < source. */
+ mfl = tl;
+ mtl = tl + diff;
+ for (cnt = diff; cnt--;) {
+ if ((p = file_gline(sp, ep, fl, &len)) == NULL)
+ return (1);
+ BINC_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ if (file_aline(sp, ep, 1, tl++, bp, len))
+ return (1);
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno == fl)
+ lmp->lno = tl;
+ ++fl;
+ if (file_dline(sp, ep, fl))
+ return (1);
+ }
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ sp->lno = tl; /* Last line moved. */
+ sp->cno = 0;
+
+ /* Log the new positions of the marks. */
+ if (mark_reset)
+ for (lmp = ep->marks.lh_first;
+ lmp != NULL; lmp = lmp->q.le_next)
+ if (lmp->name != ABSMARK1 &&
+ lmp->lno >= mfl && lmp->lno <= mtl)
+ (void)log_mark(sp, ep, lmp);
+
+
+ sp->rptlines[L_MOVED] += diff;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_open.c b/usr.bin/vi/ex/ex_open.c
new file mode 100644
index 0000000..187a29b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_open.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_open.c 8.6 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_open -- :[line] o[pen] [/pattern/] [flags]
+ *
+ * Switch to single line "open" mode.
+ */
+int
+ex_open(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* If open option off, disallow open command. */
+ if (!O_ISSET(sp, O_OPEN)) {
+ msgq(sp, M_ERR,
+ "The open command requires that the open option be set");
+ return (1);
+ }
+
+ msgq(sp, M_ERR, "The open command is not yet implemented");
+ return (1);
+}
diff --git a/usr.bin/vi/ex/ex_preserve.c b/usr.bin/vi/ex/ex_preserve.c
new file mode 100644
index 0000000..7bef07e
--- /dev/null
+++ b/usr.bin/vi/ex/ex_preserve.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_preserve.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_preserve -- :pre[serve]
+ * Push the file to recovery.
+ */
+int
+ex_preserve(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ recno_t lno;
+
+ if (!F_ISSET(ep, F_RCV_ON)) {
+ msgq(sp, M_ERR, "Preservation of this file not possible");
+ return (1);
+ }
+
+ /* If recovery not initialized, do so. */
+ if (F_ISSET(ep, F_FIRSTMODIFY) && rcv_init(sp, ep))
+ return (1);
+
+ /* Force the file to be read in, in case it hasn't yet. */
+ if (file_lline(sp, ep, &lno))
+ return (1);
+
+ /* Sync to disk. */
+ if (rcv_sync(sp, ep, RCV_SNAPSHOT))
+ return (1);
+
+ msgq(sp, M_INFO, "File preserved");
+ return (0);
+}
+
+/*
+ * ex_recover -- :rec[over][!] file
+ *
+ * Recover the file.
+ */
+int
+ex_recover(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ FREF *frp;
+
+ ap = cmdp->argv[0];
+
+ /* Set the alternate file name. */
+ set_alt_name(sp, ap->bp);
+
+ /*
+ * Check for modifications. Autowrite did not historically
+ * affect :recover.
+ */
+ if (file_m2(sp, ep, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+
+ /* Get a file structure for the file. */
+ if ((frp = file_add(sp, ap->bp)) == NULL)
+ return (1);
+
+ /* Set the recover bit. */
+ F_SET(frp, FR_RECOVER);
+
+ /* Switch files. */
+ if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE)))
+ return (1);
+ F_SET(sp, S_FSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_print.c b/usr.bin/vi/ex/ex_print.c
new file mode 100644
index 0000000..6bcab28
--- /dev/null
+++ b/usr.bin/vi/ex/ex_print.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_print.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_list -- :[line [,line]] l[ist] [count] [flags]
+ *
+ * Display the addressed lines such that the output is unambiguous.
+ */
+int
+ex_list(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_LIST))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_number -- :[line [,line]] nu[mber] [count] [flags]
+ *
+ * Display the addressed lines with a leading line number.
+ */
+int
+ex_number(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, cmdp->flags | E_F_HASH))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_pr -- :[line [,line]] p[rint] [count] [flags]
+ *
+ * Display the addressed lines.
+ */
+int
+ex_pr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (ex_print(sp, ep, &cmdp->addr1, &cmdp->addr2, cmdp->flags))
+ return (1);
+ sp->lno = cmdp->addr2.lno;
+ sp->cno = cmdp->addr2.cno;
+ return (0);
+}
+
+/*
+ * ex_print --
+ * Print the selected lines.
+ */
+int
+ex_print(sp, ep, fp, tp, flags)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+ register int flags;
+{
+ recno_t from, to;
+ size_t col, len;
+ char *p;
+
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (from = fp->lno, to = tp->lno; from <= to; ++from) {
+ /*
+ * Display the line number. The %6 format is specified
+ * by POSIX 1003.2, and is almost certainly large enough.
+ * Check, though, just in case.
+ */
+ if (LF_ISSET(E_F_HASH))
+ if (from <= 999999)
+ col = ex_printf(EXCOOKIE, "%6ld ", from);
+ else
+ col = ex_printf(EXCOOKIE, "TOOBIG ");
+ else
+ col = 0;
+
+ /*
+ * Display the line. The format for E_F_PRINT isn't very good,
+ * especially in handling end-of-line tabs, but they're almost
+ * backward compatible.
+ */
+ if ((p = file_gline(sp, ep, from, &len)) == NULL) {
+ GETLINE_ERR(sp, from);
+ return (1);
+ }
+
+ if (len == 0 && !LF_ISSET(E_F_LIST))
+ (void)ex_printf(EXCOOKIE, "\n");
+ else if (ex_ldisplay(sp, p, len, col, flags))
+ return (1);
+
+ if (INTERRUPTED(sp))
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * ex_ldisplay --
+ * Display a line.
+ */
+int
+ex_ldisplay(sp, lp, len, col, flags)
+ SCR *sp;
+ CHAR_T *lp;
+ size_t len, col;
+ u_int flags;
+{
+ CHAR_T ch, *kp;
+ u_long ts;
+ size_t tlen;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ for (;; --len) {
+ if (len > 0)
+ ch = *lp++;
+ else if (LF_ISSET(E_F_LIST))
+ ch = '$';
+ else
+ break;
+ if (ch == '\t' && !LF_ISSET(E_F_LIST))
+ for (tlen = ts - col % ts;
+ col < sp->cols && tlen--; ++col)
+ (void)ex_printf(EXCOOKIE, " ");
+ else {
+ kp = KEY_NAME(sp, ch);
+ tlen = KEY_LEN(sp, ch);
+ if (col + tlen < sp->cols) {
+ (void)ex_printf(EXCOOKIE, "%s", kp);
+ col += tlen;
+ } else
+ for (; tlen--; ++kp, ++col) {
+ if (col == sp->cols) {
+ col = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ }
+ (void)ex_printf(EXCOOKIE, "%c", *kp);
+ }
+ }
+ if (len == 0)
+ break;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_put.c b/usr.bin/vi/ex/ex_put.c
new file mode 100644
index 0000000..a947d85
--- /dev/null
+++ b/usr.bin/vi/ex/ex_put.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_put.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_put -- [line] pu[t] [buffer]
+ *
+ * Append a cut buffer into the file.
+ */
+int
+ex_put(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK m;
+
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (put(sp, ep, NULL, F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &m, 1))
+ return (1);
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_read.c b/usr.bin/vi/ex/ex_read.c
new file mode 100644
index 0000000..cef1e3a
--- /dev/null
+++ b/usr.bin/vi/ex/ex_read.c
@@ -0,0 +1,300 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_read.c 8.41 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_read -- :read [file]
+ * :read [!cmd]
+ * Read from a file or utility.
+ *
+ * !!!
+ * Historical vi wouldn't undo a filter read, for no apparent reason.
+ */
+int
+ex_read(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ struct stat sb;
+ CHAR_T *arg, *name;
+ EX_PRIVATE *exp;
+ FILE *fp;
+ MARK rm;
+ recno_t nlines;
+ size_t arglen, blen, len;
+ int btear, farg, rval;
+ char *p;
+
+ /*
+ * 0 args: we're done.
+ * 1 args: check for "read !arg".
+ * 2 args: check for "read ! arg".
+ * >2 args: object, too many args.
+ */
+ farg = 0;
+ switch (cmdp->argc) {
+ case 0:
+ break;
+ case 1:
+ arg = cmdp->argv[0]->bp;
+ arglen = cmdp->argv[0]->len;
+ if (*arg == '!') {
+ ++arg;
+ --arglen;
+ farg = 1;
+ }
+ break;
+ case 2:
+ if (cmdp->argv[0]->len == 1 && cmdp->argv[0]->bp[0] == '!') {
+ arg = cmdp->argv[1]->bp;
+ arglen = cmdp->argv[1]->len;
+ farg = 2;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ goto badarg;
+ }
+
+ if (farg != 0) {
+ /* File name and bang expand the user's argument. */
+ if (argv_exp1(sp, ep, cmdp, arg, arglen, 1))
+ return (1);
+
+ /* If argc unchanged, there wasn't anything to expand. */
+ if (cmdp->argc == farg)
+ goto usage;
+
+ /* Set the last bang command. */
+ exp = EXP(sp);
+ if (exp->lastbcomm != NULL)
+ free(exp->lastbcomm);
+ if ((exp->lastbcomm = strdup(cmdp->argv[farg]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ /* Redisplay the user's argument if it's changed. */
+ if (F_ISSET(cmdp, E_MODIFY) && IN_VI_MODE(sp)) {
+ len = cmdp->argv[farg]->len;
+ GET_SPACE_RET(sp, p, blen, len + 2);
+ p[0] = '!';
+ memmove(p + 1,
+ cmdp->argv[farg]->bp, cmdp->argv[farg]->len + 1);
+ (void)sp->s_busy(sp, p);
+ FREE_SPACE(sp, p, blen);
+ }
+
+ if (filtercmd(sp, ep, &cmdp->addr1,
+ NULL, &rm, cmdp->argv[farg]->bp, FILTER_READ))
+ return (1);
+
+ /* The filter version of read set the autoprint flag. */
+ F_SET(EXP(sp), EX_AUTOPRINT);
+
+ /* If in vi mode, move to the first nonblank. */
+ sp->lno = rm.lno;
+ if (IN_VI_MODE(sp)) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+ return (0);
+ }
+
+ /* Shell and file name expand the user's argument. */
+ if (argv_exp2(sp, ep, cmdp, arg, arglen, 0))
+ return (1);
+
+ /*
+ * 0 args: no arguments, read the current file, don't set the
+ * alternate file name.
+ * 1 args: read it, switching to it or settgin the alternate file
+ * name.
+ * >1 args: object, too many args.
+ */
+ switch (cmdp->argc) {
+ case 1:
+ name = sp->frp->name;
+ break;
+ case 2:
+ name = cmdp->argv[1]->bp;
+ /*
+ * !!!
+ * Historically, if you had an "unnamed" file, the read command
+ * renamed the file.
+ */
+ if (F_ISSET(sp->frp, FR_TMPFILE) &&
+ !F_ISSET(sp->frp, FR_READNAMED)) {
+ if ((p = v_strdup(sp,
+ cmdp->argv[1]->bp, cmdp->argv[1]->len)) != NULL) {
+ free(sp->frp->name);
+ sp->frp->name = p;
+ }
+ F_SET(sp->frp, FR_NAMECHANGE | FR_READNAMED);
+ } else
+ set_alt_name(sp, name);
+ break;
+ default:
+badarg: msgq(sp, M_ERR,
+ "%s expanded into too many file names", cmdp->argv[0]->bp);
+usage: msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically, vi did not permit reads from non-regular files,
+ * nor did it distinguish between "read !" and "read!", so there
+ * was no way to "force" it.
+ */
+ if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
+ msgq(sp, M_SYSERR, "%s", name);
+ return (1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ (void)fclose(fp);
+ msgq(sp, M_ERR, "Only regular files may be read");
+ return (1);
+ }
+
+ /* Turn on busy message. */
+ btear = F_ISSET(sp, S_EXSILENT) ? 0 : !busy_on(sp, "Reading...");
+ rval = ex_readfp(sp, ep, name, fp, &cmdp->addr1, &nlines, 1);
+ if (btear)
+ busy_off(sp);
+
+ /*
+ * Set the cursor to the first line read in, if anything read
+ * in, otherwise, the address. (Historic vi set it to the
+ * line after the address regardless, but since that line may
+ * not exist we don't bother.)
+ */
+ sp->lno = cmdp->addr1.lno;
+ if (nlines)
+ ++sp->lno;
+
+ return (rval);
+}
+
+/*
+ * ex_readfp --
+ * Read lines into the file.
+ */
+int
+ex_readfp(sp, ep, name, fp, fm, nlinesp, success_msg)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+ FILE *fp;
+ MARK *fm;
+ recno_t *nlinesp;
+ int success_msg;
+{
+ EX_PRIVATE *exp;
+ recno_t lcnt, lno;
+ size_t len;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ int rval;
+
+ rval = 0;
+ exp = EXP(sp);
+
+ /*
+ * Add in the lines from the output. Insertion starts at the line
+ * following the address.
+ */
+ ccnt = 0;
+ lcnt = 0;
+ for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) {
+ if (INTERRUPTED(sp)) {
+ if (!success_msg)
+ msgq(sp, M_INFO, "Interrupted");
+ break;
+ }
+ if (file_aline(sp, ep, 1, lno, exp->ibp, len)) {
+ rval = 1;
+ break;
+ }
+ ccnt += len;
+ }
+
+ if (ferror(fp)) {
+ msgq(sp, M_SYSERR, "%s", name);
+ rval = 1;
+ }
+
+ if (fclose(fp)) {
+ msgq(sp, M_SYSERR, "%s", name);
+ return (1);
+ }
+
+ if (rval)
+ return (1);
+
+ /* Return the number of lines read in. */
+ if (nlinesp != NULL)
+ *nlinesp = lcnt;
+
+ if (success_msg)
+ msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters",
+ INTERRUPTED(sp) ? "Interrupted read: " : "",
+ name, lcnt, lcnt == 1 ? "" : "s", ccnt);
+
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_screen.c b/usr.bin/vi/ex/ex_screen.c
new file mode 100644
index 0000000..e190588
--- /dev/null
+++ b/usr.bin/vi/ex/ex_screen.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_screen.c 8.15 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_split -- :s[plit] [file ...]
+ * Split the screen, optionally setting the file list.
+ */
+int
+ex_split(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_split(sp, cmdp->argc ? cmdp->argv : NULL, cmdp->argc));
+}
+
+/*
+ * ex_bg -- :bg
+ * Hide the screen.
+ */
+int
+ex_bg(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_bg(sp));
+}
+
+/*
+ * ex_fg -- :fg [file]
+ * Show the screen.
+ */
+int
+ex_fg(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (sp->s_fg(sp, cmdp->argc ? cmdp->argv[0]->bp : NULL));
+}
+
+/*
+ * ex_resize -- :resize [+-]rows
+ * Change the screen size.
+ */
+int
+ex_resize(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum adjust adj;
+
+ if (!F_ISSET(cmdp, E_COUNT)) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+ if (F_ISSET(cmdp, E_COUNT_NEG))
+ adj = A_DECREASE;
+ else if (F_ISSET(cmdp, E_COUNT_POS))
+ adj = A_INCREASE;
+ else
+ adj = A_SET;
+ return (sp->s_rabs(sp, cmdp->count, adj));
+}
+
+/*
+ * ex_sdisplay --
+ * Display the list of screens.
+ */
+int
+ex_sdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ int cnt, col, len, sep;
+
+ if ((tsp = sp->gp->hq.cqh_first) == (void *)&sp->gp->hq) {
+ (void)ex_printf(EXCOOKIE,
+ "No backgrounded screens to display.\n");
+ return (0);
+ }
+
+ col = len = sep = 0;
+ for (cnt = 1; tsp != (void *)&sp->gp->hq; tsp = tsp->q.cqe_next) {
+ col += len = strlen(tsp->frp->name) + sep;
+ if (col >= sp->cols - 1) {
+ col = len;
+ sep = 0;
+ (void)ex_printf(EXCOOKIE, "\n");
+ } else if (cnt != 1) {
+ sep = 1;
+ (void)ex_printf(EXCOOKIE, " ");
+ }
+ (void)ex_printf(EXCOOKIE, "%s", tsp->frp->name);
+ ++cnt;
+ }
+ (void)ex_printf(EXCOOKIE, "\n");
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_script.c b/usr.bin/vi/ex/ex_script.c
new file mode 100644
index 0000000..a364e2b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_script.c
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_script.c 8.19 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "script.h"
+
+/*
+ * XXX
+ */
+int openpty __P((int *, int *, char *, struct termios *, struct winsize *));
+
+static int sscr_getprompt __P((SCR *, EXF *));
+static int sscr_init __P((SCR *, EXF *));
+static int sscr_matchprompt __P((SCR *, char *, size_t, size_t *));
+static int sscr_setprompt __P((SCR *, char *, size_t));
+
+/*
+ * ex_script -- : sc[ript][!] [file]
+ *
+ * Switch to script mode.
+ */
+int
+ex_script(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* Vi only command. */
+ if (!IN_VI_MODE(sp)) {
+ msgq(sp, M_ERR,
+ "The script command is only available in vi mode");
+ return (1);
+ }
+
+ /* Switch to the new file. */
+ if (cmdp->argc != 0 && ex_edit(sp, ep, cmdp))
+ return (1);
+
+ /*
+ * Create the shell, figure out the prompt.
+ *
+ * !!!
+ * The files just switched, use sp->ep.
+ */
+ if (sscr_init(sp, sp->ep))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * sscr_init --
+ * Create a pty setup for a shell.
+ */
+static int
+sscr_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCRIPT *sc;
+ char *sh, *sh_path;
+
+ MALLOC_RET(sp, sc, SCRIPT *, sizeof(SCRIPT));
+ sp->script = sc;
+ sc->sh_prompt = NULL;
+ sc->sh_prompt_len = 0;
+
+ /*
+ * There are two different processes running through this code.
+ * They are the shell and the parent.
+ */
+ sc->sh_master = sc->sh_slave = -1;
+
+ if (tcgetattr(STDIN_FILENO, &sc->sh_term) == -1) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+
+ /*
+ * Turn off output postprocessing and echo.
+ */
+ sc->sh_term.c_oflag &= ~OPOST;
+ sc->sh_term.c_cflag &= ~(ECHO|ECHOE|ECHONL|ECHOK);
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ goto err;
+ }
+
+ if (openpty(&sc->sh_master,
+ &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) {
+ msgq(sp, M_SYSERR, "openpty");
+ goto err;
+ }
+
+ /*
+ * Don't use vfork() here, because the signal semantics differ from
+ * implementation to implementation.
+ */
+ SIGBLOCK(sp->gp);
+ switch (sc->sh_pid = fork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "fork");
+err: if (sc->sh_master != -1)
+ (void)close(sc->sh_master);
+ if (sc->sh_slave != -1)
+ (void)close(sc->sh_slave);
+ return (1);
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ /*
+ * XXX
+ * So that shells that do command line editing turn it off.
+ */
+ (void)putenv("TERM=emacs");
+ (void)putenv("TERMCAP=emacs:");
+ (void)putenv("EMACS=t");
+
+ (void)setsid();
+#ifdef TIOCSCTTY
+ /*
+ * 4.4BSD allocates a controlling terminal using the TIOCSCTTY
+ * ioctl, not by opening a terminal device file. POSIX 1003.1
+ * doesn't define a portable way to do this. If TIOCSCTTY is
+ * not available, hope that the open does it.
+ */
+ (void)ioctl(sc->sh_slave, TIOCSCTTY, 0);
+#endif
+ (void)close(sc->sh_master);
+ (void)dup2(sc->sh_slave, STDIN_FILENO);
+ (void)dup2(sc->sh_slave, STDOUT_FILENO);
+ (void)dup2(sc->sh_slave, STDERR_FILENO);
+ (void)close(sc->sh_slave);
+
+ /* Assumes that all shells have -i. */
+ sh_path = O_STR(sp, O_SHELL);
+ if ((sh = strrchr(sh_path, '/')) == NULL)
+ sh = sh_path;
+ else
+ ++sh;
+ execl(sh_path, sh, "-i", NULL);
+ msgq(sp, M_ERR,
+ "Error: execl: %s: %s", sh_path, strerror(errno));
+ _exit(127);
+ default: /* Parent. */
+ SIGUNBLOCK(sp->gp);
+ break;
+ }
+
+ if (sscr_getprompt(sp, ep))
+ return (1);
+
+ F_SET(sp, S_REDRAW | S_SCRIPT);
+ return (0);
+
+}
+
+/*
+ * sscr_getprompt --
+ * Eat lines printed by the shell until a line with no trailing
+ * carriage return comes; set the prompt from that line.
+ */
+static int
+sscr_getprompt(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct timeval tv;
+ CHAR_T *endp, *p, *t, buf[1024];
+ SCRIPT *sc;
+ fd_set fdset;
+ recno_t lline;
+ size_t llen, len;
+ u_int value;
+ int nr;
+
+ FD_ZERO(&fdset);
+ endp = buf;
+ len = sizeof(buf);
+
+ /* Wait up to a second for characters to read. */
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ sc = sp->script;
+ FD_SET(sc->sh_master, &fdset);
+ switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "select");
+ goto prompterr;
+ case 0: /* Timeout */
+ msgq(sp, M_ERR, "Error: timed out");
+ goto prompterr;
+ case 1: /* Characters to read. */
+ break;
+ }
+
+ /* Read the characters. */
+more: len = sizeof(buf) - (endp - buf);
+ switch (nr = read(sc->sh_master, endp, len)) {
+ case 0: /* EOF. */
+ msgq(sp, M_ERR, "Error: shell: EOF");
+ goto prompterr;
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "shell");
+ goto prompterr;
+ default:
+ endp += nr;
+ break;
+ }
+
+ /* If any complete lines, push them into the file. */
+ for (p = t = buf; p < endp; ++p) {
+ value = KEY_VAL(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ if (file_lline(sp, ep, &lline) ||
+ file_aline(sp, ep, 0, lline, t, p - t))
+ goto prompterr;
+ t = p + 1;
+ }
+ }
+ if (p > buf) {
+ memmove(buf, t, endp - t);
+ endp = buf + (endp - t);
+ }
+ if (endp == buf)
+ goto more;
+
+ /* Wait up 1/10 of a second to make sure that we got it all. */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ switch (select(sc->sh_master + 1, &fdset, NULL, NULL, &tv)) {
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "select");
+ goto prompterr;
+ case 0: /* Timeout */
+ break;
+ case 1: /* Characters to read. */
+ goto more;
+ }
+
+ /* Timed out, so theoretically we have a prompt. */
+ llen = endp - buf;
+ endp = buf;
+
+ /* Append the line into the file. */
+ if (file_lline(sp, ep, &lline) ||
+ file_aline(sp, ep, 0, lline, buf, llen)) {
+prompterr: sscr_end(sp);
+ return (1);
+ }
+
+ return (sscr_setprompt(sp, buf, llen));
+}
+
+/*
+ * sscr_exec --
+ * Take a line and hand it off to the shell.
+ */
+int
+sscr_exec(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SCRIPT *sc;
+ recno_t last_lno;
+ size_t blen, len, last_len, tlen;
+ int matchprompt, nw, rval;
+ char *bp, *p;
+
+ /* If there's a prompt on the last line, append the command. */
+ if (file_lline(sp, ep, &last_lno))
+ return (1);
+ if ((p = file_gline(sp, ep, last_lno, &last_len)) == NULL) {
+ GETLINE_ERR(sp, last_lno);
+ return (1);
+ }
+ if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) {
+ matchprompt = 1;
+ GET_SPACE_RET(sp, bp, blen, last_len + 128);
+ memmove(bp, p, last_len);
+ } else
+ matchprompt = 0;
+
+ /* Get something to execute. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ goto err1;
+ if (lno == 0)
+ goto empty;
+ else
+ GETLINE_ERR(sp, lno);
+ goto err1;
+ }
+
+ /* Empty lines aren't interesting. */
+ if (len == 0)
+ goto empty;
+
+ /* Delete any prompt. */
+ if (sscr_matchprompt(sp, p, len, &tlen)) {
+ if (tlen == len) {
+empty: msgq(sp, M_BERR, "Nothing to execute");
+ goto err1;
+ }
+ p += (len - tlen);
+ len = tlen;
+ }
+
+ /* Push the line to the shell. */
+ sc = sp->script;
+ if ((nw = write(sc->sh_master, p, len)) != len)
+ goto err2;
+ rval = 0;
+ if (write(sc->sh_master, "\n", 1) != 1) {
+err2: if (nw == 0)
+ errno = EIO;
+ msgq(sp, M_SYSERR, "shell");
+ goto err1;
+ }
+
+ if (matchprompt) {
+ ADD_SPACE_RET(sp, bp, blen, last_len + len);
+ memmove(bp + last_len, p, len);
+ if (file_sline(sp, ep, last_lno, bp, last_len + len))
+err1: rval = 1;
+ }
+ if (matchprompt)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * sscr_input --
+ * Take a line from the shell and insert it into the file.
+ */
+int
+sscr_input(sp)
+ SCR *sp;
+{
+ struct timeval tv;
+ CHAR_T *endp, *p, *t;
+ EXF *ep;
+ SCRIPT *sc;
+ recno_t lno;
+ size_t blen, len, tlen;
+ u_int value;
+ int nr, rval;
+ char *bp;
+
+ /* Find out where the end of the file is. */
+ ep = sp->ep;
+ if (file_lline(sp, ep, &lno))
+ return (1);
+
+#define MINREAD 1024
+ GET_SPACE_RET(sp, bp, blen, MINREAD);
+ endp = bp;
+
+ /* Read the characters. */
+ rval = 1;
+ sc = sp->script;
+more: switch (nr = read(sc->sh_master, endp, MINREAD)) {
+ case 0: /* EOF; shell just exited. */
+ sscr_end(sp);
+ F_CLR(sp, S_SCRIPT);
+ rval = 0;
+ goto ret;
+ case -1: /* Error or interrupt. */
+ msgq(sp, M_SYSERR, "shell");
+ goto ret;
+ default:
+ endp += nr;
+ break;
+ }
+
+ /* Append the lines into the file. */
+ for (p = t = bp; p < endp; ++p) {
+ value = KEY_VAL(sp, *p);
+ if (value == K_CR || value == K_NL) {
+ len = p - t;
+ if (file_aline(sp, ep, 1, lno++, t, len))
+ goto ret;
+ t = p + 1;
+ }
+ }
+ if (p > t) {
+ len = p - t;
+ /*
+ * If the last thing from the shell isn't another prompt, wait
+ * up to 1/10 of a second for more stuff to show up, so that
+ * we don't break the output into two separate lines. Don't
+ * want to hang indefinitely because some program is hanging,
+ * confused the shell, or whatever.
+ */
+ if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ FD_SET(sc->sh_master, &sp->rdfd);
+ FD_CLR(STDIN_FILENO, &sp->rdfd);
+ if (select(sc->sh_master + 1,
+ &sp->rdfd, NULL, NULL, &tv) == 1) {
+ memmove(bp, t, len);
+ endp = bp + len;
+ goto more;
+ }
+ }
+ if (sscr_setprompt(sp, t, len))
+ return (1);
+ if (file_aline(sp, ep, 1, lno++, t, len))
+ goto ret;
+ }
+
+ /* The cursor moves to EOF. */
+ sp->lno = lno;
+ sp->cno = len ? len - 1 : 0;
+ rval = sp->s_refresh(sp, ep);
+
+ret: FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * sscr_setprompt --
+ *
+ * Set the prompt to the last line we got from the shell.
+ *
+ */
+static int
+sscr_setprompt(sp, buf, len)
+ SCR *sp;
+ char* buf;
+ size_t len;
+{
+ SCRIPT *sc;
+
+ sc = sp->script;
+ if (sc->sh_prompt)
+ FREE(sc->sh_prompt, sc->sh_prompt_len);
+ MALLOC(sp, sc->sh_prompt, char *, len + 1);
+ if (sc->sh_prompt == NULL) {
+ sscr_end(sp);
+ return (1);
+ }
+ memmove(sc->sh_prompt, buf, len);
+ sc->sh_prompt_len = len;
+ sc->sh_prompt[len] = '\0';
+ return (0);
+}
+
+/*
+ * sscr_matchprompt --
+ * Check to see if a line matches the prompt. Nul's indicate
+ * parts that can change, in both content and size.
+ */
+static int
+sscr_matchprompt(sp, lp, line_len, lenp)
+ SCR *sp;
+ char *lp;
+ size_t line_len, *lenp;
+{
+ SCRIPT *sc;
+ size_t prompt_len;
+ char *pp;
+
+ sc = sp->script;
+ if (line_len < (prompt_len = sc->sh_prompt_len))
+ return (0);
+
+ for (pp = sc->sh_prompt;
+ prompt_len && line_len; --prompt_len, --line_len) {
+ if (*pp == '\0') {
+ for (; prompt_len && *pp == '\0'; --prompt_len, ++pp);
+ if (!prompt_len)
+ return (0);
+ for (; line_len && *lp != *pp; --line_len, ++lp);
+ if (!line_len)
+ return (0);
+ }
+ if (*pp++ != *lp++)
+ break;
+ }
+
+ if (prompt_len)
+ return (0);
+ if (lenp != NULL)
+ *lenp = line_len;
+ return (1);
+}
+
+/*
+ * sscr_end --
+ * End the pipe to a shell.
+ */
+int
+sscr_end(sp)
+ SCR *sp;
+{
+ SCRIPT *sc;
+ int rval;
+
+ if ((sc = sp->script) == NULL)
+ return (0);
+
+ /* Turn off the script flag. */
+ F_CLR(sp, S_SCRIPT);
+
+ /* Close down the parent's file descriptors. */
+ if (sc->sh_master != -1)
+ (void)close(sc->sh_master);
+ if (sc->sh_slave != -1)
+ (void)close(sc->sh_slave);
+
+ /* This should have killed the child. */
+ rval = proc_wait(sp, (long)sc->sh_pid, "script-shell", 0);
+
+ /* Free memory. */
+ FREE(sc->sh_prompt, sc->sh_prompt_len);
+ FREE(sc, sizeof(SCRIPT));
+ sp->script = NULL;
+
+ return (rval);
+}
diff --git a/usr.bin/vi/ex/ex_set.c b/usr.bin/vi/ex/ex_set.c
new file mode 100644
index 0000000..a2abb08
--- /dev/null
+++ b/usr.bin/vi/ex/ex_set.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_set.c 8.6 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+int
+ex_set(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ switch(cmdp->argc) {
+ case 0:
+ opts_dump(sp, CHANGED_DISPLAY);
+ break;
+ default:
+ opts_set(sp, cmdp->cmd->usage, cmdp->argv);
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_shell.c b/usr.bin/vi/ex/ex_shell.c
new file mode 100644
index 0000000..b6f9aed
--- /dev/null
+++ b/usr.bin/vi/ex/ex_shell.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shell.c 8.26 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../svi/svi_screen.h"
+
+/*
+ * ex_shell -- :sh[ell]
+ * Invoke the program named in the SHELL environment variable
+ * with the argument -i.
+ */
+int
+ex_shell(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ char buf[MAXPATHLEN];
+
+ (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
+ return (ex_exec_proc(sp, buf, "\n", NULL));
+}
+
+/*
+ * ex_exec_proc --
+ * Run a separate process.
+ */
+int
+ex_exec_proc(sp, cmd, p1, p2)
+ SCR *sp;
+ char *cmd, *p1, *p2;
+{
+ const char *name;
+ pid_t pid;
+ int rval, teardown;
+
+ /* Clear the rest of the screen. */
+ if (sp->s_clear(sp))
+ return (1);
+
+ /* Save ex/vi terminal settings, and restore the original ones. */
+ teardown = !ex_sleave(sp);
+
+ /*
+ * Flush waiting messages (autowrite, for example) so the output
+ * matches historic practice.
+ */
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Put out various messages. */
+ if (p1 != NULL)
+ (void)write(STDOUT_FILENO, p1, strlen(p1));
+ if (p2 != NULL)
+ (void)write(STDOUT_FILENO, p2, strlen(p2));
+
+ SIGBLOCK(sp->gp);
+ switch (pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+ rval = 1;
+ break;
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+ name = O_STR(sp, O_SHELL);
+ else
+ ++name;
+ execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+ msgq(sp, M_ERR, "Error: execl: %s: %s",
+ O_STR(sp, O_SHELL), strerror(errno));
+ _exit(127);
+ /* NOTREACHED */
+ default: /* Parent. */
+ SIGUNBLOCK(sp->gp);
+
+ rval = proc_wait(sp, (long)pid, cmd, 0);
+ break;
+ }
+
+ /* Restore ex/vi terminal settings. */
+ if (teardown)
+ ex_rleave(sp);
+
+ /*
+ * XXX
+ * Stat of the tty structures (see ex_sleave, ex_rleave) only give
+ * us 1-second resolution on the tty changes. A fast '!' command,
+ * e.g. ":!pwd" can beat us to the refresh. When there's better
+ * resolution from the stat(2) timers, this can go away.
+ */
+ F_SET(sp, S_REFRESH);
+
+ return (rval);
+}
diff --git a/usr.bin/vi/ex/ex_shift.c b/usr.bin/vi/ex/ex_shift.c
new file mode 100644
index 0000000..61264c0
--- /dev/null
+++ b/usr.bin/vi/ex/ex_shift.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_shift.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {LEFT, RIGHT};
+static int shift __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+int
+ex_shiftl(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (shift(sp, ep, cmdp, LEFT));
+}
+
+int
+ex_shiftr(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (shift(sp, ep, cmdp, RIGHT));
+}
+
+static int
+shift(sp, ep, cmdp, rl)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which rl;
+{
+ recno_t from, to;
+ size_t blen, len, newcol, newidx, oldcol, oldidx, sw;
+ int curset;
+ char *p, *bp, *tbp;
+
+ if (O_VAL(sp, O_SHIFTWIDTH) == 0) {
+ msgq(sp, M_INFO, "shiftwidth option set to 0");
+ return (0);
+ }
+
+ /*
+ * The historic version of vi permitted the user to string any number
+ * of '>' or '<' characters together, resulting in an indent of the
+ * appropriate levels. There's a special hack in ex_cmd() so that
+ * cmdp->argv[0] points to the string of '>' or '<' characters.
+ *
+ * Q: What's the difference between the people adding features
+ * to vi and the Girl Scouts?
+ * A: The Girl Scouts have mint cookies and adult supervision.
+ */
+ for (p = cmdp->argv[0]->bp, sw = 0; *p == '>' || *p == '<'; ++p)
+ sw += O_VAL(sp, O_SHIFTWIDTH);
+
+ GET_SPACE_RET(sp, bp, blen, 256);
+
+ curset = 0;
+ for (from = cmdp->addr1.lno, to = cmdp->addr2.lno; from <= to; ++from) {
+ if ((p = file_gline(sp, ep, from, &len)) == NULL)
+ goto err;
+ if (!len) {
+ if (sp->lno == from)
+ curset = 1;
+ continue;
+ }
+
+ /*
+ * Calculate the old indent amount and the number of
+ * characters it used.
+ */
+ for (oldidx = 0, oldcol = 0; oldidx < len; ++oldidx)
+ if (p[oldidx] == ' ')
+ ++oldcol;
+ else if (p[oldidx] == '\t')
+ oldcol += O_VAL(sp, O_TABSTOP) -
+ oldcol % O_VAL(sp, O_TABSTOP);
+ else
+ break;
+
+ /* Calculate the new indent amount. */
+ if (rl == RIGHT)
+ newcol = oldcol + sw;
+ else {
+ newcol = oldcol < sw ? 0 : oldcol - sw;
+ if (newcol == oldcol) {
+ if (sp->lno == from)
+ curset = 1;
+ continue;
+ }
+ }
+
+ /* Get a buffer that will hold the new line. */
+ ADD_SPACE_RET(sp, bp, blen, newcol + len);
+
+ /*
+ * Build a new indent string and count the number of
+ * characters it uses.
+ */
+ for (tbp = bp, newidx = 0;
+ newcol >= O_VAL(sp, O_TABSTOP); ++newidx) {
+ *tbp++ = '\t';
+ newcol -= O_VAL(sp, O_TABSTOP);
+ }
+ for (; newcol > 0; --newcol, ++newidx)
+ *tbp++ = ' ';
+
+ /* Add the original line. */
+ memmove(tbp, p + oldidx, len - oldidx);
+
+ /* Set the replacement line. */
+ if (file_sline(sp, ep, from, bp, (tbp + (len - oldidx)) - bp)) {
+err: FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * The shift command in historic vi had the usual bizarre
+ * collection of cursor semantics. If called from vi, the
+ * cursor was repositioned to the first non-blank character
+ * of the lowest numbered line shifted. If called from ex,
+ * the cursor was repositioned to the first non-blank of the
+ * highest numbered line shifted. Here, if the cursor isn't
+ * part of the set of lines that are moved, move it to the
+ * first non-blank of the last line shifted. (This makes
+ * ":3>>" in vi work reasonably.) If the cursor is part of
+ * the shifted lines, it doesn't get moved at all. This
+ * permits shifting of marked areas, i.e. ">'a." shifts the
+ * marked area twice, something that couldn't be done with
+ * historic vi.
+ */
+ if (sp->lno == from) {
+ curset = 1;
+ if (newidx > oldidx)
+ sp->cno += newidx - oldidx;
+ else if (sp->cno >= oldidx - newidx)
+ sp->cno -= oldidx - newidx;
+ }
+ }
+ if (!curset) {
+ sp->lno = to;
+ sp->cno = 0;
+ (void)nonblank(sp, ep, to, &sp->cno);
+ }
+
+ FREE_SPACE(sp, bp, blen);
+
+ sp->rptlines[rl == RIGHT ? L_RSHIFT : L_LSHIFT] +=
+ cmdp->addr2.lno - cmdp->addr1.lno + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_source.c b/usr.bin/vi/ex/ex_source.c
new file mode 100644
index 0000000..b8af556
--- /dev/null
+++ b/usr.bin/vi/ex/ex_source.c
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_source.c 8.7 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_source -- :source file
+ * Execute ex commands from a file.
+ */
+int
+ex_source(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (ex_cfile(sp, ep, cmdp->argv[0]->bp, 0));
+}
diff --git a/usr.bin/vi/ex/ex_stop.c b/usr.bin/vi/ex/ex_stop.c
new file mode 100644
index 0000000..543f649
--- /dev/null
+++ b/usr.bin/vi/ex/ex_stop.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_stop.c 8.9 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * ex_stop -- :stop[!]
+ * :suspend[!]
+ * Suspend execution.
+ */
+int
+ex_stop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ /* For some strange reason, the force flag turns off autowrite. */
+ if (!F_ISSET(cmdp, E_FORCE) &&
+ F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE) &&
+ file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/ex/ex_subst.c b/usr.bin/vi/ex/ex_subst.c
new file mode 100644
index 0000000..de413f8
--- /dev/null
+++ b/usr.bin/vi/ex/ex_subst.c
@@ -0,0 +1,1001 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_subst.c 8.59 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+#define SUB_FIRST 0x01 /* The 'r' flag isn't reasonable. */
+#define SUB_MUSTSETR 0x02 /* The 'r' flag is required. */
+
+static __inline int regsub __P((SCR *, char *,
+ char **, size_t *, size_t *, regmatch_t [10]));
+static int substitute __P((SCR *, EXF *,
+ EXCMDARG *, char *, regex_t *, u_int));
+
+/*
+ * ex_substitute --
+ * [line [,line]] s[ubstitute] [[/;]pat[/;]/repl[/;] [cgr] [count] [#lp]]
+ *
+ * Substitute on lines matching a pattern.
+ */
+int
+ex_substitute(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ regex_t *re, lre;
+ size_t blen, len;
+ u_int flags;
+ int delim, eval, reflags, replaced;
+ char *bp, *ptrn, *rep, *p, *t;
+
+ /*
+ * Skip leading white space.
+ *
+ * !!!
+ * Historic vi allowed any non-alphanumeric to serve as the
+ * substitution command delimiter.
+ *
+ * !!!
+ * If the arguments are empty, it's the same as &, i.e. we
+ * repeat the last substitution.
+ */
+ for (p = cmdp->argv[0]->bp,
+ len = cmdp->argv[0]->len; len > 0; --len, ++p) {
+ if (!isblank(*p))
+ break;
+ }
+ if (len == 0)
+ return (ex_subagain(sp, ep, cmdp));
+ delim = *p++;
+ if (isalnum(delim))
+ return (substitute(sp, ep,
+ cmdp, p, &sp->subre, SUB_MUSTSETR));
+
+ /*
+ * !!!
+ * The full-blown substitute command reset the remembered
+ * state of the 'c' and 'g' suffices.
+ */
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * Get the pattern string, toss escaped characters.
+ *
+ * !!!
+ * Historic vi accepted any of the following forms:
+ *
+ * :s/abc/def/ change "abc" to "def"
+ * :s/abc/def change "abc" to "def"
+ * :s/abc/ delete "abc"
+ * :s/abc delete "abc"
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escape character if it escapes a delimiter.
+ * This means that "s/A/\\\\f" replaces "A" with "\\f". It
+ * would be nice to be more regular, i.e. for each layer of
+ * escaping a single escape character is removed, but that's
+ * not how the historic vi worked.
+ */
+ for (ptrn = t = p;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ /*
+ * !!!
+ * Nul terminate the pattern string -- it's passed
+ * to regcomp which doesn't understand anything else.
+ */
+ *t = '\0';
+ break;
+ }
+ if (p[0] == '\\')
+ if (p[1] == delim)
+ ++p;
+ else if (p[1] == '\\')
+ *t++ = *p++;
+ *t++ = *p++;
+ }
+
+ /*
+ * If the pattern string is empty, use the last RE (not just the
+ * last substitution RE).
+ */
+ if (*ptrn == '\0') {
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression");
+ return (1);
+ }
+ re = &sp->sre;
+ flags = 0;
+ } else {
+ /* Set RE flags. */
+ reflags = 0;
+ if (O_ISSET(sp, O_EXTENDED))
+ reflags |= REG_EXTENDED;
+ if (O_ISSET(sp, O_IGNORECASE))
+ reflags |= REG_ICASE;
+
+ /* Convert vi-style RE's to POSIX 1003.2 RE's. */
+ if (re_conv(sp, &ptrn, &replaced))
+ return (1);
+
+ /* Compile the RE. */
+ eval = regcomp(&lre, (char *)ptrn, reflags);
+
+ /* Free up any allocated memory. */
+ if (replaced)
+ FREE_SPACE(sp, ptrn, 0);
+
+ if (eval) {
+ re_error(sp, eval, &lre);
+ return (1);
+ }
+
+ /*
+ * Set saved RE.
+ *
+ * !!!
+ * Historic practice is that substitutes set the search
+ * direction as well as both substitute and search RE's.
+ */
+ sp->searchdir = FORWARD;
+ sp->sre = lre;
+ F_SET(sp, S_SRE_SET);
+ sp->subre = lre;
+ F_SET(sp, S_SUBRE_SET);
+
+ re = &lre;
+ flags = SUB_FIRST;
+ }
+
+ /*
+ * Get the replacement string.
+ *
+ * The special character & (\& if O_MAGIC not set) matches the
+ * entire RE. No handling of & is required here, it's done by
+ * regsub().
+ *
+ * The special character ~ (\~ if O_MAGIC not set) inserts the
+ * previous replacement string into this replacement string.
+ * Count ~'s to figure out how much space we need. We could
+ * special case nonexistent last patterns or whether or not
+ * O_MAGIC is set, but it's probably not worth the effort.
+ *
+ * QUOTING NOTE:
+ *
+ * Only toss an escape character if it escapes a delimiter or
+ * if O_MAGIC is set and it escapes a tilde.
+ *
+ * !!!
+ * If the entire replacement pattern is "%", then use the last
+ * replacement pattern. This semantic was added to vi in System
+ * V and then percolated elsewhere, presumably around the time
+ * that it was added to their version of ed(1).
+ */
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ if (sp->repl != NULL)
+ FREE(sp->repl, sp->repl_len);
+ sp->repl = NULL;
+ sp->repl_len = 0;
+ } else if (p[0] == '%' && (p[1] == '\0' || p[1] == delim))
+ p += p[1] == delim ? 2 : 1;
+ else {
+ for (rep = p, len = 0;
+ p[0] != '\0' && p[0] != delim; ++p, ++len)
+ if (p[0] == '~')
+ len += sp->repl_len;
+ GET_SPACE_RET(sp, bp, blen, len);
+ for (t = bp, len = 0, p = rep;;) {
+ if (p[0] == '\0' || p[0] == delim) {
+ if (p[0] == delim)
+ ++p;
+ break;
+ }
+ if (p[0] == '\\') {
+ if (p[1] == delim)
+ ++p;
+ else if (p[1] == '\\') {
+ *t++ = *p++;
+ ++len;
+ } else if (p[1] == '~') {
+ ++p;
+ if (!O_ISSET(sp, O_MAGIC))
+ goto tilde;
+ }
+ } else if (p[0] == '~' && O_ISSET(sp, O_MAGIC)) {
+tilde: ++p;
+ memmove(t, sp->repl, sp->repl_len);
+ t += sp->repl_len;
+ len += sp->repl_len;
+ continue;
+ }
+ *t++ = *p++;
+ ++len;
+ }
+ if ((sp->repl_len = len) != 0) {
+ if (sp->repl != NULL)
+ free(sp->repl);
+ if ((sp->repl = malloc(len)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ FREE_SPACE(sp, bp, blen);
+ return (1);
+ }
+ memmove(sp->repl, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+ }
+ return (substitute(sp, ep, cmdp, p, re, flags));
+}
+
+/*
+ * ex_subagain --
+ * [line [,line]] & [cgr] [count] [#lp]]
+ *
+ * Substitute using the last substitute RE and replacement pattern.
+ */
+int
+ex_subagain(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (!F_ISSET(sp, S_SUBRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression");
+ return (1);
+ }
+ return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->subre, 0));
+}
+
+/*
+ * ex_subtilde --
+ * [line [,line]] ~ [cgr] [count] [#lp]]
+ *
+ * Substitute using the last RE and last substitute replacement pattern.
+ */
+int
+ex_subtilde(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR, "No previous regular expression");
+ return (1);
+ }
+ return (substitute(sp, ep, cmdp, cmdp->argv[0]->bp, &sp->sre, 0));
+}
+
+/*
+ * The nasty part of the substitution is what happens when the replacement
+ * string contains newlines. It's a bit tricky -- consider the information
+ * that has to be retained for "s/f\(o\)o/^M\1^M\1/". The solution here is
+ * to build a set of newline offsets which we use to break the line up later,
+ * when the replacement is done. Don't change it unless you're pretty damned
+ * confident.
+ */
+#define NEEDNEWLINE(sp) { \
+ if (sp->newl_len == sp->newl_cnt) { \
+ sp->newl_len += 25; \
+ REALLOC(sp, sp->newl, size_t *, \
+ sp->newl_len * sizeof(size_t)); \
+ if (sp->newl == NULL) { \
+ sp->newl_len = 0; \
+ return (1); \
+ } \
+ } \
+}
+
+#define BUILD(sp, l, len) { \
+ if (lbclen + (len) > lblen) { \
+ lblen += MAX(lbclen + (len), 256); \
+ REALLOC(sp, lb, char *, lblen); \
+ if (lb == NULL) { \
+ lbclen = 0; \
+ return (1); \
+ } \
+ } \
+ memmove(lb + lbclen, l, len); \
+ lbclen += len; \
+}
+
+#define NEEDSP(sp, len, pnt) { \
+ if (lbclen + (len) > lblen) { \
+ lblen += MAX(lbclen + (len), 256); \
+ REALLOC(sp, lb, char *, lblen); \
+ if (lb == NULL) { \
+ lbclen = 0; \
+ return (1); \
+ } \
+ pnt = lb + lbclen; \
+ } \
+}
+
+/*
+ * substitute --
+ * Do the substitution. This stuff is *really* tricky. There are
+ * lots of special cases, and general nastiness. Don't mess with it
+ * unless you're pretty confident.
+ */
+static int
+substitute(sp, ep, cmdp, s, re, flags)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ char *s;
+ regex_t *re;
+ u_int flags;
+{
+ MARK from, to;
+ recno_t elno, lno;
+ regmatch_t match[10];
+ size_t blen, cnt, last, lbclen, lblen, len, llen, offset, saved_offset;
+ int cflag, lflag, nflag, pflag, rflag;
+ int didsub, do_eol_match, eflags, empty_ok, eval;
+ int linechanged, matched, quit, rval;
+ char *bp, *lb;
+
+ /*
+ * !!!
+ * Historically, the 'g' and 'c' suffices were always toggled as flags,
+ * so ":s/A/B/" was the same as ":s/A/B/ccgg". If O_EDCOMPATIBLE was
+ * not set, they were initialized to 0 for all substitute commands. If
+ * O_EDCOMPATIBLE was set, they were initialized to 0 only if the user
+ * specified substitute/replacement patterns (see ex_substitute()).
+ */
+ if (!O_ISSET(sp, O_EDCOMPATIBLE))
+ sp->c_suffix = sp->g_suffix = 0;
+
+ /*
+ * Historic vi permitted the '#', 'l' and 'p' options in vi mode, but
+ * it only displayed the last change. I'd disallow them, but they are
+ * useful in combination with the [v]global commands. In the current
+ * model the problem is combining them with the 'c' flag -- the screen
+ * would have to flip back and forth between the confirm screen and the
+ * ex print screen, which would be pretty awful. We do display all
+ * changes, though, for what that's worth.
+ *
+ * !!!
+ * Historic vi was fairly strict about the order of "options", the
+ * count, and "flags". I'm somewhat fuzzy on the difference between
+ * options and flags, anyway, so this is a simpler approach, and we
+ * just take it them in whatever order the user gives them. (The ex
+ * usage statement doesn't reflect this.)
+ */
+ cflag = lflag = nflag = pflag = rflag = 0;
+ for (lno = OOBLNO; *s != '\0'; ++s)
+ switch (*s) {
+ case ' ':
+ case '\t':
+ continue;
+ case '+':
+ ++cmdp->flagoff;
+ break;
+ case '-':
+ --cmdp->flagoff;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (lno != OOBLNO)
+ goto usage;
+ errno = 0;
+ lno = strtoul(s, &s, 10);
+ if (*s == '\0') /* Loop increment correction. */
+ --s;
+ if (errno == ERANGE) {
+ if (lno == LONG_MAX)
+ msgq(sp, M_ERR, "Count overflow");
+ else if (lno == LONG_MIN)
+ msgq(sp, M_ERR, "Count underflow");
+ else
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ /*
+ * In historic vi, the count was inclusive from the
+ * second address.
+ */
+ cmdp->addr1.lno = cmdp->addr2.lno;
+ cmdp->addr2.lno += lno - 1;
+ break;
+ case '#':
+ nflag = 1;
+ break;
+ case 'c':
+ sp->c_suffix = !sp->c_suffix;
+ break;
+ case 'g':
+ sp->g_suffix = !sp->g_suffix;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'r':
+ if (LF_ISSET(SUB_FIRST)) {
+ msgq(sp, M_ERR,
+ "Regular expression specified; r flag meaningless");
+ return (1);
+ }
+ if (!F_ISSET(sp, S_SRE_SET)) {
+ msgq(sp, M_ERR,
+ "No previous regular expression");
+ return (1);
+ }
+ rflag = 1;
+ re = &sp->sre;
+ break;
+ default:
+ goto usage;
+ }
+
+ if (*s != '\0' || !rflag && LF_ISSET(SUB_MUSTSETR)) {
+usage: msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ if (IN_VI_MODE(sp) && sp->c_suffix && (lflag || nflag || pflag)) {
+ msgq(sp, M_ERR,
+ "The #, l and p flags may not be combined with the c flag in vi mode");
+ return (1);
+ }
+
+ /*
+ * bp: if interactive, line cache
+ * blen: if interactive, line cache length
+ * lb: build buffer pointer.
+ * lbclen: current length of built buffer.
+ * lblen; length of build buffer.
+ */
+ bp = lb = NULL;
+ blen = lbclen = lblen = 0;
+
+ /* For each line... */
+ for (matched = quit = 0, lno = cmdp->addr1.lno,
+ elno = cmdp->addr2.lno; !quit && lno <= elno; ++lno) {
+
+ /* Someone's unhappy, time to stop. */
+ if (INTERRUPTED(sp)) {
+ if (!F_ISSET(sp, S_GLOBAL))
+ msgq(sp, M_INFO, "Interrupted");
+ break;
+ }
+
+ /* Get the line. */
+ if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ goto ret1;
+ }
+
+ /*
+ * Make a local copy if doing confirmation -- when calling
+ * the confirm routine we're likely to lose the cached copy.
+ */
+ if (sp->c_suffix) {
+ if (bp == NULL) {
+ GET_SPACE_RET(sp, bp, blen, llen);
+ } else
+ ADD_SPACE_RET(sp, bp, blen, llen);
+ memmove(bp, s, llen);
+ s = bp;
+ }
+
+ /* Start searching from the beginning. */
+ offset = 0;
+ len = llen;
+
+ /* Reset the build buffer offset. */
+ lbclen = 0;
+
+ /* Reset empty match flag. */
+ empty_ok = 1;
+
+ /*
+ * We don't want to have to do a setline if the line didn't
+ * change -- keep track of whether or not this line changed.
+ * If doing confirmations, don't want to keep setting the
+ * line if change is refused -- keep track of substitutions.
+ */
+ didsub = linechanged = 0;
+
+ /* New line, do an EOL match. */
+ do_eol_match = 1;
+
+ /* It's not nul terminated, but we pretend it is. */
+ eflags = REG_STARTEND;
+
+ /*
+ * The search area is from s + offset to the EOL.
+ *
+ * Generally, match[0].rm_so is the offset of the start
+ * of the match from the start of the search, and offset
+ * is the offset of the start of the last search.
+ */
+nextmatch: match[0].rm_so = 0;
+ match[0].rm_eo = len;
+
+ /* Get the next match. */
+ eval = regexec(re, (char *)s + offset, 10, match, eflags);
+
+ /*
+ * There wasn't a match or if there was an error, deal with
+ * it. If there was a previous match in this line, resolve
+ * the changes into the database. Otherwise, just move on.
+ */
+ if (eval == REG_NOMATCH)
+ goto endmatch;
+ if (eval != 0) {
+ re_error(sp, eval, re);
+ goto ret1;
+ }
+ matched = 1;
+
+ /* Only the first search can match an anchored expression. */
+ eflags |= REG_NOTBOL;
+
+ /*
+ * !!!
+ * It's possible to match 0-length strings -- for example, the
+ * command s;a*;X;, when matched against the string "aabb" will
+ * result in "XbXbX", i.e. the matches are "aa", the space
+ * between the b's and the space between the b's and the end of
+ * the string. There is a similar space between the beginning
+ * of the string and the a's. The rule that we use (because vi
+ * historically used it) is that any 0-length match, occurring
+ * immediately after a match, is ignored. Otherwise, the above
+ * example would have resulted in "XXbXbX". Another example is
+ * incorrectly using " *" to replace groups of spaces with one
+ * space.
+ *
+ * The way we do this is that if we just had a successful match,
+ * the starting offset does not skip characters, and the match
+ * is empty, ignore the match and move forward. If there's no
+ * more characters in the string, we were attempting to match
+ * after the last character, so quit.
+ */
+ if (!empty_ok && match[0].rm_so == 0 && match[0].rm_eo == 0) {
+ empty_ok = 1;
+ if (len == 0)
+ goto endmatch;
+ BUILD(sp, s + offset, 1)
+ ++offset;
+ --len;
+ goto nextmatch;
+ }
+
+ /* Confirm change. */
+ if (sp->c_suffix) {
+ /*
+ * Set the cursor position for confirmation. Note,
+ * if we matched on a '$', the cursor may be past
+ * the end of line.
+ *
+ * XXX
+ * We may want to "fix" this in the confirm routine,
+ * if the confirm routine should be able to display
+ * a cursor past EOL.
+ */
+ from.lno = to.lno = lno;
+ from.cno = match[0].rm_so + offset;
+ to.cno = match[0].rm_eo;
+ if (llen == 0)
+ from.cno = to.cno = 0;
+ else {
+ if (to.cno >= llen)
+ to.cno = llen - 1;
+ if (from.cno >= llen)
+ from.cno = llen - 1;
+ }
+ switch (sp->s_confirm(sp, ep, &from, &to)) {
+ case CONF_YES:
+ break;
+ case CONF_NO:
+ didsub = 0;
+ BUILD(sp, s +offset, match[0].rm_eo);
+ goto skip;
+ case CONF_QUIT:
+ /* Set the quit flag. */
+ quit = 1;
+
+ /* If interruptible, pass the info back. */
+ if (F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+
+ /*
+ * If any changes, resolve them, otherwise
+ * return to the main loop.
+ */
+ goto endmatch;
+ }
+ }
+
+ /* Copy the bytes before the match into the build buffer. */
+ BUILD(sp, s + offset, match[0].rm_so);
+
+ /* Substitute the matching bytes. */
+ didsub = 1;
+ if (regsub(sp, s + offset, &lb, &lbclen, &lblen, match))
+ goto ret1;
+
+ /* Set the change flag so we know this line was modified. */
+ linechanged = 1;
+
+ /* Move past the matched bytes. */
+skip: offset += match[0].rm_eo;
+ len -= match[0].rm_eo;
+
+ /* A match cannot be followed by an empty pattern. */
+ empty_ok = 0;
+
+ /*
+ * If doing a global change with confirmation, we have to
+ * update the screen. The basic idea is to store the line
+ * so the screen update routines can find it, and restart.
+ */
+ if (didsub && sp->c_suffix && sp->g_suffix) {
+ /*
+ * The new search offset will be the end of the
+ * modified line.
+ */
+ saved_offset = lbclen;
+
+ /* Copy the rest of the line. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* Set the new offset. */
+ offset = saved_offset;
+
+ /* Store inserted lines, adjusting the build buffer. */
+ last = 0;
+ if (sp->newl_cnt) {
+ for (cnt = 0;
+ cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+ if (file_iline(sp, ep, lno,
+ lb + last, sp->newl[cnt] - last))
+ goto ret1;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ offset -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store and retrieve the line. */
+ if (file_sline(sp, ep, lno, lb + last, lbclen))
+ goto ret1;
+ if ((s = file_gline(sp, ep, lno, &llen)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ goto ret1;
+ }
+ ADD_SPACE_RET(sp, bp, blen, llen)
+ memmove(bp, s, llen);
+ s = bp;
+ len = llen - offset;
+
+ /* Restart the build. */
+ lbclen = 0;
+ BUILD(sp, s, offset);
+
+ /*
+ * If we haven't already done the after-the-string
+ * match, do one. Set REG_NOTEOL so the '$' pattern
+ * only matches once.
+ */
+ if (!do_eol_match)
+ goto endmatch;
+ if (offset == len) {
+ do_eol_match = 0;
+ eflags |= REG_NOTEOL;
+ }
+ goto nextmatch;
+ }
+
+ /*
+ * If it's a global:
+ *
+ * If at the end of the string, do a test for the after
+ * the string match. Set REG_NOTEOL so the '$' pattern
+ * only matches once.
+ */
+ if (sp->g_suffix && do_eol_match) {
+ if (len == 0) {
+ do_eol_match = 0;
+ eflags |= REG_NOTEOL;
+ }
+ goto nextmatch;
+ }
+
+endmatch: if (!linechanged)
+ continue;
+
+ /* Copy any remaining bytes into the build buffer. */
+ if (len)
+ BUILD(sp, s + offset, len)
+
+ /* Store inserted lines, adjusting the build buffer. */
+ last = 0;
+ if (sp->newl_cnt) {
+ for (cnt = 0;
+ cnt < sp->newl_cnt; ++cnt, ++lno, ++elno) {
+ if (file_iline(sp, ep,
+ lno, lb + last, sp->newl[cnt] - last))
+ goto ret1;
+ last = sp->newl[cnt] + 1;
+ ++sp->rptlines[L_ADDED];
+ }
+ lbclen -= last;
+ sp->newl_cnt = 0;
+ }
+
+ /* Store the changed line. */
+ if (file_sline(sp, ep, lno, lb + last, lbclen))
+ goto ret1;
+
+ /* Update changed line counter. */
+ if (sp->rptlchange != lno) {
+ sp->rptlchange = lno;
+ ++sp->rptlines[L_CHANGED];
+ }
+
+ /*
+ * !!!
+ * Display as necessary. Historic practice is to only
+ * display the last line of a line split into multiple
+ * lines.
+ */
+ if (lflag || nflag || pflag) {
+ from.lno = to.lno = lno;
+ from.cno = to.cno = 0;
+ if (lflag)
+ ex_print(sp, ep, &from, &to, E_F_LIST);
+ if (nflag)
+ ex_print(sp, ep, &from, &to, E_F_HASH);
+ if (pflag)
+ ex_print(sp, ep, &from, &to, E_F_PRINT);
+ }
+
+ if (!sp->c_suffix)
+ sp->lno = lno;
+
+ /*
+ * !!!
+ * Move the cursor to the last line changed.
+ */
+ if (!sp->c_suffix)
+ sp->lno = lno;
+ }
+
+ /*
+ * !!!
+ * Move the cursor to the first non-blank of the last line change.
+ *
+ * XXX
+ * This is NOT backward compatible with historic vi, which always
+ * moved to the last line actually changed.
+ */
+ if (!sp->c_suffix) {
+ sp->cno = 0;
+ (void)nonblank(sp, ep, sp->lno, &sp->cno);
+ }
+
+ /*
+ * If not in a global command, and nothing matched, say so.
+ * Else, if none of the lines displayed, put something up.
+ */
+ if (!matched) {
+ if (!F_ISSET(sp, S_GLOBAL))
+ msgq(sp, M_INFO, "No match found");
+ } else if (!lflag && !nflag && !pflag)
+ F_SET(EXP(sp), EX_AUTOPRINT);
+
+ rval = 0;
+ if (0) {
+ret1: rval = 1;
+ }
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ if (lb != NULL)
+ free(lb);
+ return (rval);
+}
+
+/*
+ * regsub --
+ * Do the substitution for a regular expression.
+ */
+static __inline int
+regsub(sp, ip, lbp, lbclenp, lblenp, match)
+ SCR *sp;
+ char *ip; /* Input line. */
+ char **lbp;
+ size_t *lbclenp, *lblenp;
+ regmatch_t match[10];
+{
+ enum { C_NOTSET, C_LOWER, C_ONELOWER, C_ONEUPPER, C_UPPER } conv;
+ size_t lbclen, lblen; /* Local copies. */
+ size_t mlen; /* Match length. */
+ size_t rpl; /* Remaining replacement length. */
+ char *rp; /* Replacement pointer. */
+ int ch;
+ int no; /* Match replacement offset. */
+ char *p, *t; /* Buffer pointers. */
+ char *lb; /* Local copies. */
+
+ lb = *lbp; /* Get local copies. */
+ lbclen = *lbclenp;
+ lblen = *lblenp;
+
+ /*
+ * QUOTING NOTE:
+ *
+ * There are some special sequences that vi provides in the
+ * replacement patterns.
+ * & string the RE matched (\& if nomagic set)
+ * \# n-th regular subexpression
+ * \E end \U, \L conversion
+ * \e end \U, \L conversion
+ * \l convert the next character to lower-case
+ * \L convert to lower-case, until \E, \e, or end of replacement
+ * \u convert the next character to upper-case
+ * \U convert to upper-case, until \E, \e, or end of replacement
+ *
+ * Otherwise, since this is the lowest level of replacement, discard
+ * all escape characters. This (hopefully) follows historic practice.
+ */
+#define ADDCH(ch) { \
+ CHAR_T __ch = (ch); \
+ u_int __value = KEY_VAL(sp, __ch); \
+ if (__value == K_CR || __value == K_NL) { \
+ NEEDNEWLINE(sp); \
+ sp->newl[sp->newl_cnt++] = lbclen; \
+ } else if (conv != C_NOTSET) { \
+ switch (conv) { \
+ case C_ONELOWER: \
+ conv = C_NOTSET; \
+ /* FALLTHROUGH */ \
+ case C_LOWER: \
+ if (isupper(__ch)) \
+ __ch = tolower(__ch); \
+ break; \
+ case C_ONEUPPER: \
+ conv = C_NOTSET; \
+ /* FALLTHROUGH */ \
+ case C_UPPER: \
+ if (islower(__ch)) \
+ __ch = toupper(__ch); \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } \
+ NEEDSP(sp, 1, p); \
+ *p++ = __ch; \
+ ++lbclen; \
+}
+ conv = C_NOTSET;
+ for (rp = sp->repl, rpl = sp->repl_len, p = lb + lbclen; rpl--;) {
+ switch (ch = *rp++) {
+ case '&':
+ if (O_ISSET(sp, O_MAGIC)) {
+ no = 0;
+ goto subzero;
+ }
+ break;
+ case '\\':
+ if (rpl == 0)
+ break;
+ --rpl;
+ switch (ch = *rp) {
+ case '&':
+ ++rp;
+ if (!O_ISSET(sp, O_MAGIC)) {
+ no = 0;
+ goto subzero;
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ no = *rp++ - '0';
+subzero: if (match[no].rm_so == -1 ||
+ match[no].rm_eo == -1)
+ break;
+ mlen = match[no].rm_eo - match[no].rm_so;
+ for (t = ip + match[no].rm_so; mlen--; ++t)
+ ADDCH(*t);
+ continue;
+ case 'e':
+ case 'E':
+ ++rp;
+ conv = C_NOTSET;
+ continue;
+ case 'l':
+ ++rp;
+ conv = C_ONELOWER;
+ continue;
+ case 'L':
+ ++rp;
+ conv = C_LOWER;
+ continue;
+ case 'u':
+ ++rp;
+ conv = C_ONEUPPER;
+ continue;
+ case 'U':
+ ++rp;
+ conv = C_UPPER;
+ continue;
+ default:
+ ++rp;
+ break;
+ }
+ }
+ ADDCH(ch);
+ }
+
+ *lbp = lb; /* Update caller's information. */
+ *lbclenp = lbclen;
+ *lblenp = lblen;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_tag.c b/usr.bin/vi/ex/ex_tag.c
new file mode 100644
index 0000000..4378803
--- /dev/null
+++ b/usr.bin/vi/ex/ex_tag.c
@@ -0,0 +1,905 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_tag.c 8.45 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "tag.h"
+
+static char *binary_search __P((char *, char *, char *));
+static int compare __P((char *, char *, char *));
+static char *linear_search __P((char *, char *, char *));
+static int search __P((SCR *, char *, char *, char **));
+static int tag_get __P((SCR *, char *, char **, char **, char **));
+
+/*
+ * ex_tagfirst --
+ * The tag code can be entered from main, i.e. "vi -t tag".
+ */
+int
+ex_tagfirst(sp, tagarg)
+ SCR *sp;
+ char *tagarg;
+{
+ FREF *frp;
+ MARK m;
+ long tl;
+ u_int flags;
+ int sval;
+ char *p, *tag, *name, *search;
+
+ /* Taglength may limit the number of characters. */
+ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(tagarg) > tl)
+ tagarg[tl] = '\0';
+
+ /* Get the tag information. */
+ if (tag_get(sp, tagarg, &tag, &name, &search))
+ return (1);
+
+ /* Create the file entry. */
+ if ((frp = file_add(sp, name)) == NULL)
+ return (1);
+ if (file_init(sp, frp, NULL, 0))
+ return (1);
+
+ /*
+ * !!!
+ * The historic tags file format (from a long, long time ago...)
+ * used a line number, not a search string. I got complaints, so
+ * people are still using the format.
+ */
+ if (isdigit(search[0])) {
+ m.lno = atoi(search);
+ m.cno = 0;
+ } else {
+ /*
+ * Search for the tag; cheap fallback for C functions if
+ * the name is the same but the arguments have changed.
+ */
+ m.lno = 1;
+ m.cno = 0;
+ flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+ sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+ if (sval && (p = strrchr(search, '(')) != NULL) {
+ p[1] = '\0';
+ sval = f_search(sp, sp->ep,
+ &m, &m, search, NULL, &flags);
+ }
+ if (sval)
+ msgq(sp, M_ERR, "%s: search pattern not found", tag);
+ }
+
+ /* Set up the screen. */
+ frp->lno = m.lno;
+ frp->cno = m.cno;
+ F_SET(frp, FR_CURSORSET);
+
+ /* Might as well make this the default tag. */
+ if ((EXP(sp)->tlast = strdup(tagarg)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
+
+/* Free a tag or tagf structure from a queue. */
+#define FREETAG(tp) { \
+ TAILQ_REMOVE(&exp->tagq, (tp), q); \
+ if ((tp)->search != NULL) \
+ free((tp)->search); \
+ FREE((tp), sizeof(TAGF)); \
+}
+#define FREETAGF(tfp) { \
+ TAILQ_REMOVE(&exp->tagfq, (tfp), q); \
+ free((tfp)->name); \
+ FREE((tfp), sizeof(TAGF)); \
+}
+
+/*
+ * ex_tagpush -- :tag [file]
+ * Move to a new tag.
+ *
+ * The tags stacks in nvi are a bit tricky. Each tag contains a file name,
+ * search string, and line/column numbers. The search string is only used
+ * for the first access and for user display. The first record on the stack
+ * is the place where we first did a tag, so it has no search string. The
+ * second record is the first tag, and so on. Note, this means that the
+ * "current" tag is always on the stack. Each tag has a line/column which is
+ * the location from which the user tagged the following TAG entry, and which
+ * is used as the return location.
+ */
+int
+ex_tagpush(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ enum {TC_CHANGE, TC_CURRENT} which;
+ EX_PRIVATE *exp;
+ FREF *frp;
+ MARK m;
+ TAG *tp;
+ u_int flags;
+ int sval;
+ long tl;
+ char *name, *p, *search, *tag;
+
+ exp = EXP(sp);
+ switch (cmdp->argc) {
+ case 1:
+ if (exp->tlast != NULL)
+ FREE(exp->tlast, strlen(exp->tlast) + 1);
+ if ((exp->tlast = strdup(cmdp->argv[0]->bp)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ break;
+ case 0:
+ if (exp->tlast == NULL) {
+ msgq(sp, M_ERR, "No previous tag entered");
+ return (1);
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Taglength may limit the number of characters. */
+ if ((tl = O_VAL(sp, O_TAGLENGTH)) != 0 && strlen(exp->tlast) > tl)
+ exp->tlast[tl] = '\0';
+
+ /* Get the tag information. */
+ if (tag_get(sp, exp->tlast, &tag, &name, &search))
+ return (1);
+
+ /* Get the (possibly new) FREF structure. */
+ if ((frp = file_add(sp, name)) == NULL)
+ goto err;
+
+ if (sp->frp == frp)
+ which = TC_CURRENT;
+ else {
+ if (file_m1(sp, sp->ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ goto err;
+ which = TC_CHANGE;
+ }
+
+ /*
+ * Get a tag structure -- if this is the first tag, push it on the
+ * stack as a placeholder and get another tag structure. Set the
+ * line/column of the most recent element on the stack to be the
+ * current values, including the file pointer. Then push the new
+ * TAG onto the stack with the new file and search string for user
+ * display.
+ */
+ CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+ if (tp != NULL && exp->tagq.tqh_first == NULL) {
+ TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+ CALLOC(sp, tp, TAG *, 1, sizeof(TAG));
+ }
+ if (exp->tagq.tqh_first != NULL) {
+ exp->tagq.tqh_first->frp = sp->frp;
+ exp->tagq.tqh_first->lno = sp->lno;
+ exp->tagq.tqh_first->cno = sp->cno;
+ }
+ if (tp != NULL) {
+ if ((tp->search = strdup(search)) == NULL)
+ msgq(sp, M_SYSERR, NULL);
+ else
+ tp->slen = strlen(search);
+ tp->frp = frp;
+ TAILQ_INSERT_HEAD(&exp->tagq, tp, q);
+ }
+
+ /* Switch files. */
+ if (which == TC_CHANGE && file_init(sp, frp, NULL, 0)) {
+ if (tp != NULL)
+ FREETAG(tp);
+ /* Handle special, first-tag case. */
+ if (exp->tagq.tqh_first->q.tqe_next == NULL)
+ TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q);
+err: free(tag);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historic vi accepted a line number as well as a search
+ * string, and people are apparently still using the format.
+ */
+ if (isdigit(search[0])) {
+ m.lno = atoi(search);
+ m.cno = 0;
+ sval = 0;
+ } else {
+ /*
+ * Search for the tag; cheap fallback for C functions
+ * if the name is the same but the arguments have changed.
+ */
+ m.lno = 1;
+ m.cno = 0;
+ flags = SEARCH_FILE | SEARCH_TAG | SEARCH_TERM;
+ sval = f_search(sp, sp->ep, &m, &m, search, NULL, &flags);
+ if (sval && (p = strrchr(search, '(')) != NULL) {
+ p[1] = '\0';
+ sval = f_search(sp, sp->ep,
+ &m, &m, search, NULL, &flags);
+ p[1] = '(';
+ }
+ if (sval)
+ msgq(sp, M_ERR, "%s: search pattern not found", tag);
+ }
+ free(tag);
+
+ switch (which) {
+ case TC_CHANGE:
+ frp->lno = m.lno;
+ frp->cno = m.cno;
+ F_SET(frp, FR_CURSORSET);
+ F_SET(sp, S_FSWITCH);
+ break;
+ case TC_CURRENT:
+ if (sval)
+ return (1);
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ex_tagpop -- :tagp[op][!] [number | file]
+ * Pop the tag stack.
+ */
+int
+ex_tagpop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *ntp, *tp;
+ long off;
+ size_t arglen;
+ char *arg, *p, *t;
+
+ /* Check for an empty stack. */
+ exp = EXP(sp);
+ if (exp->tagq.tqh_first == NULL) {
+ msgq(sp, M_INFO, "The tags stack is empty");
+ return (1);
+ }
+
+ switch (cmdp->argc) {
+ case 0: /* Pop one tag. */
+ ntp = exp->tagq.tqh_first;
+ break;
+ case 1: /* Name or number. */
+ arg = cmdp->argv[0]->bp;
+ off = strtol(arg, &p, 10);
+ if (*p == '\0') {
+ if (off < 1)
+ return (0);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL && --off > 1; tp = tp->q.tqe_next);
+ if (tp == NULL) {
+ msgq(sp, M_ERR,
+"Less than %s entries on the tags stack; use :display to see the tags stack",
+ arg);
+ return (1);
+ }
+ ntp = tp;
+ } else {
+ arglen = strlen(arg);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL; ntp = tp, tp = tp->q.tqe_next) {
+ /* Use the user's original file name. */
+ p = tp->frp->name;
+ if ((t = strrchr(p, '/')) == NULL)
+ t = p;
+ else
+ ++t;
+ if (!strncmp(arg, t, arglen))
+ break;
+ }
+ if (tp == NULL) {
+ msgq(sp, M_ERR,
+"No file named %s on the tags stack; use :display to see the tags stack",
+ arg);
+ return (1);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor from the saved TAG information. */
+ tp = ntp->q.tqe_next;
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ if (file_m1(sp, ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+ if (file_init(sp, tp->frp, NULL, 0))
+ return (1);
+
+ tp->frp->lno = tp->lno;
+ tp->frp->cno = tp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_FSWITCH);
+ }
+
+ /* Pop entries off the queue up to ntp. */
+ for (;;) {
+ tp = exp->tagq.tqh_first;
+ FREETAG(tp);
+ if (tp == ntp)
+ break;
+ }
+
+ /* If returning to the first tag, the stack is now empty. */
+ if (exp->tagq.tqh_first->q.tqe_next == NULL)
+ TAILQ_REMOVE(&exp->tagq, exp->tagq.tqh_first, q);
+ return (0);
+}
+
+/*
+ * ex_tagtop -- :tagt[op][!]
+ * Clear the tag stack.
+ */
+int
+ex_tagtop(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+
+ /* Find oldest saved information. */
+ exp = EXP(sp);
+ for (tp = exp->tagq.tqh_first;
+ tp != NULL && tp->q.tqe_next != NULL; tp = tp->q.tqe_next);
+ if (tp == NULL) {
+ msgq(sp, M_INFO, "The tags stack is empty");
+ return (1);
+ }
+
+ /* If not switching files, it's easy; else do the work. */
+ if (tp->frp == sp->frp) {
+ sp->lno = tp->lno;
+ sp->cno = tp->cno;
+ } else {
+ if (file_m1(sp, sp->ep,
+ F_ISSET(cmdp, E_FORCE), FS_ALL | FS_POSSIBLE))
+ return (1);
+ if (file_init(sp, tp->frp, NULL, 0))
+ return (1);
+
+ tp->frp->lno = tp->lno;
+ tp->frp->cno = tp->cno;
+
+ F_SET(sp->frp, FR_CURSORSET);
+ F_SET(sp, S_FSWITCH);
+ }
+
+ /* Empty out the queue. */
+ while ((tp = exp->tagq.tqh_first) != NULL)
+ FREETAG(tp);
+ return (0);
+}
+
+/*
+ * ex_tagdisplay --
+ * Display the list of tags.
+ */
+int
+ex_tagdisplay(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ size_t len, maxlen;
+ int cnt;
+ char *name;
+
+ exp = EXP(sp);
+ if ((tp = exp->tagq.tqh_first) == NULL) {
+ (void)ex_printf(EXCOOKIE, "No tags to display.\n");
+ return (0);
+ }
+
+ /*
+ * Figure out the formatting. MNOC is the maximum
+ * number of file name columns before we split the line.
+ */
+#define MNOC 15
+ for (maxlen = 0,
+ tp = exp->tagq.tqh_first; tp != NULL; tp = tp->q.tqe_next) {
+ len = strlen(name = tp->frp->name); /* The original name. */
+ if (maxlen < len && len < MNOC)
+ maxlen = len;
+ }
+
+ for (cnt = 1, tp = exp->tagq.tqh_first; tp != NULL;
+ ++cnt, tp = tp->q.tqe_next) {
+ len = strlen(name = tp->frp->name); /* The original name. */
+ if (len > maxlen || len + tp->slen > sp->cols)
+ if (tp == NULL || tp->search == NULL)
+ (void)ex_printf(EXCOOKIE,
+ "%2d %s\n", cnt, name);
+ else
+ (void)ex_printf(EXCOOKIE,
+ "%2d %s\n** %*.*s %s\n", cnt, name,
+ (int)maxlen, (int)maxlen, "", tp->search);
+ else
+ if (tp == NULL || tp->search == NULL)
+ (void)ex_printf(EXCOOKIE, "%2d %*.*s\n",
+ cnt, (int)maxlen, (int)len, name);
+ else
+ (void)ex_printf(EXCOOKIE, "%2d %*.*s %s\n",
+ cnt, (int)maxlen, (int)len, name,
+ tp->search);
+ }
+ return (0);
+}
+
+/*
+ * ex_tagalloc --
+ * Create a new list of tag files.
+ */
+int
+ex_tagalloc(sp, str)
+ SCR *sp;
+ char *str;
+{
+ EX_PRIVATE *exp;
+ TAGF *tp;
+ size_t len;
+ char *p, *t;
+
+ /* Free current queue. */
+ exp = EXP(sp);
+ while ((tp = exp->tagfq.tqh_first) != NULL)
+ FREETAGF(tp);
+
+ /* Create new queue. */
+ for (p = t = str;; ++p) {
+ if (*p == '\0' || isblank(*p)) {
+ if ((len = p - t) > 1) {
+ MALLOC_RET(sp, tp, TAGF *, sizeof(TAGF));
+ MALLOC(sp, tp->name, char *, len + 1);
+ if (tp->name == NULL) {
+ FREE(tp, sizeof(TAGF));
+ return (1);
+ }
+ memmove(tp->name, t, len);
+ tp->name[len] = '\0';
+ tp->flags = 0;
+ TAILQ_INSERT_TAIL(&exp->tagfq, tp, q);
+ }
+ t = p + 1;
+ }
+ if (*p == '\0')
+ break;
+ }
+ return (0);
+}
+ /* Free previous queue. */
+/*
+ * ex_tagfree --
+ * Free the tags file list.
+ */
+int
+ex_tagfree(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ TAG *tp;
+ TAGF *tfp;
+
+ /* Free up tag information. */
+ exp = EXP(sp);
+ while ((tp = exp->tagq.tqh_first) != NULL)
+ FREETAG(tp);
+ while ((tfp = exp->tagfq.tqh_first) != NULL)
+ FREETAGF(tfp);
+ if (exp->tlast != NULL)
+ free(exp->tlast);
+ return (0);
+}
+
+/*
+ * ex_tagcopy --
+ * Copy a screen's tag structures.
+ */
+int
+ex_tagcopy(orig, sp)
+ SCR *orig, *sp;
+{
+ EX_PRIVATE *oexp, *nexp;
+ TAG *ap, *tp;
+ TAGF *atfp, *tfp;
+
+ /* Copy tag stack. */
+ oexp = EXP(orig);
+ nexp = EXP(sp);
+ for (ap = oexp->tagq.tqh_first; ap != NULL; ap = ap->q.tqe_next) {
+ MALLOC(sp, tp, TAG *, sizeof(TAG));
+ if (tp == NULL)
+ goto nomem;
+ *tp = *ap;
+ if (ap->search != NULL &&
+ (tp->search = strdup(ap->search)) == NULL)
+ goto nomem;
+ TAILQ_INSERT_TAIL(&nexp->tagq, tp, q);
+ }
+
+ /* Copy list of tag files. */
+ for (atfp = oexp->tagfq.tqh_first;
+ atfp != NULL; atfp = atfp->q.tqe_next) {
+ MALLOC(sp, tfp, TAGF *, sizeof(TAGF));
+ if (tfp == NULL)
+ goto nomem;
+ *tfp = *atfp;
+ if ((tfp->name = strdup(atfp->name)) == NULL)
+ goto nomem;
+ TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
+ }
+
+ /* Copy the last tag. */
+ if (oexp->tlast != NULL &&
+ (nexp->tlast = strdup(oexp->tlast)) == NULL) {
+nomem: msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * tag_get --
+ * Get a tag from the tags files.
+ */
+static int
+tag_get(sp, tag, tagp, filep, searchp)
+ SCR *sp;
+ char *tag, **tagp, **filep, **searchp;
+{
+ struct stat sb;
+ EX_PRIVATE *exp;
+ TAGF *tfp;
+ size_t plen, slen, tlen;
+ int dne;
+ char *p, pbuf[MAXPATHLEN];
+
+ /*
+ * Find the tag, only display missing file messages once, and
+ * then only if we didn't find the tag.
+ */
+ dne = 0;
+ exp = EXP(sp);
+ for (p = NULL, tfp = exp->tagfq.tqh_first;
+ tfp != NULL && p == NULL; tfp = tfp->q.tqe_next) {
+ errno = 0;
+ F_CLR(tfp, TAGF_DNE);
+ if (search(sp, tfp->name, tag, &p))
+ if (errno == ENOENT) {
+ if (!F_ISSET(tfp, TAGF_DNE_WARN)) {
+ dne = 1;
+ F_SET(tfp, TAGF_DNE);
+ }
+ } else
+ msgq(sp, M_SYSERR, tfp->name);
+ else
+ if (p != NULL)
+ break;
+ }
+
+ if (p == NULL) {
+ msgq(sp, M_ERR, "%s: tag not found", tag);
+ if (dne)
+ for (tfp = exp->tagfq.tqh_first;
+ tfp != NULL; tfp = tfp->q.tqe_next)
+ if (F_ISSET(tfp, TAGF_DNE)) {
+ errno = ENOENT;
+ msgq(sp, M_SYSERR, tfp->name);
+ F_SET(tfp, TAGF_DNE_WARN);
+ }
+ return (1);
+ }
+
+ /*
+ * Set the return pointers; tagp points to the tag, and, incidentally
+ * the allocated string, filep points to the file name, and searchp
+ * points to the search string. All three are nul-terminated.
+ */
+ for (*tagp = p; *p && !isblank(*p); ++p);
+ if (*p == '\0')
+ goto malformed;
+ for (*p++ = '\0'; isblank(*p); ++p);
+ for (*filep = p; *p && !isblank(*p); ++p);
+ if (*p == '\0')
+ goto malformed;
+ for (*p++ = '\0'; isblank(*p); ++p);
+ *searchp = p;
+ if (*p == '\0') {
+malformed: free(*tagp);
+ msgq(sp, M_ERR, "%s: corrupted tag in %s", tag, tfp->name);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * If the tag file path is a relative path, see if it exists. If it
+ * doesn't, look relative to the tags file path. It's okay for a tag
+ * file to not exist, and, historically, vi simply displayed a "new"
+ * file. However, if the path exists relative to the tag file, it's
+ * pretty clear what's happening, so we may as well do it right.
+ */
+ if ((*filep)[0] != '/'
+ && stat(*filep, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
+ *p = '\0';
+ plen = snprintf(pbuf, sizeof(pbuf), "%s/%s", tfp->name, *filep);
+ *p = '/';
+ if (stat(pbuf, &sb) == 0) {
+ slen = strlen(*searchp);
+ tlen = strlen(*tagp);
+ MALLOC(sp, p, char *, plen + slen + tlen + 5);
+ if (p != NULL) {
+ memmove(p, *tagp, tlen);
+ free(*tagp);
+ *tagp = p;
+ *(p += tlen) = '\0';
+ memmove(++p, pbuf, plen);
+ *filep = p;
+ *(p += plen) = '\0';
+ memmove(++p, *searchp, slen);
+ *searchp = p;
+ *(p += slen) = '\0';
+ }
+ }
+ }
+ return (0);
+}
+
+#define EQUAL 0
+#define GREATER 1
+#define LESS (-1)
+
+/*
+ * search --
+ * Search a file for a tag.
+ */
+static int
+search(sp, name, tname, tag)
+ SCR *sp;
+ char *name, *tname, **tag;
+{
+ struct stat sb;
+ int fd, len;
+ char *endp, *back, *front, *map, *p;
+
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ return (1);
+
+ /*
+ * XXX
+ * We'd like to test if the file is too big to mmap. Since we don't
+ * know what size or type off_t's or size_t's are, what the largest
+ * unsigned integral type is, or what random insanity the local C
+ * compiler will perpetrate, doing the comparison in a portable way
+ * is flatly impossible. Hope that malloc fails if the file is too
+ * large.
+ */
+ if (fstat(fd, &sb) || (map = mmap(NULL, (size_t)sb.st_size,
+ PROT_READ, MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
+ (void)close(fd);
+ return (1);
+ }
+ front = map;
+ back = front + sb.st_size;
+
+ front = binary_search(tname, front, back);
+ front = linear_search(tname, front, back);
+
+ if (front == NULL || (endp = strchr(front, '\n')) == NULL) {
+ *tag = NULL;
+ goto done;
+ }
+
+ len = endp - front;
+ MALLOC(sp, p, char *, len + 1);
+ if (p == NULL) {
+ *tag = NULL;
+ goto done;
+ }
+ memmove(p, front, len);
+ p[len] = '\0';
+ *tag = p;
+
+done: if (munmap(map, (size_t)sb.st_size))
+ msgq(sp, M_SYSERR, "munmap");
+ if (close(fd))
+ msgq(sp, M_SYSERR, "close");
+ return (0);
+}
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ *
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string". Relaxing the constraint
+ * this way simplifies the algorithm.
+ *
+ * Invariants:
+ * front points to the beginning of a line at or before the first
+ * matching string.
+ *
+ * back points to the beginning of a line at or after the first
+ * matching line.
+ *
+ * Base of the Invariants.
+ * front = NULL;
+ * back = EOF;
+ *
+ * Advancing the Invariants:
+ *
+ * p = first newline after halfway point from front to back.
+ *
+ * If the string at "p" is not greater than the string to match,
+ * p is the new front. Otherwise it is the new back.
+ *
+ * Termination:
+ *
+ * The definition of the routine allows it return at any point,
+ * since front is always at or before the line to print.
+ *
+ * In fact, it returns when the chosen "p" equals "back". This
+ * implies that there exists a string is least half as long as
+ * (back - front), which in turn implies that a linear search will
+ * be no more expensive than the cost of simply printing a string or two.
+ *
+ * Trying to continue with binary search at this point would be
+ * more trouble than it's worth.
+ */
+#define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
+
+static char *
+binary_search(string, front, back)
+ register char *string, *front, *back;
+{
+ register char *p;
+
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+
+ while (p != back) {
+ if (compare(string, p, back) == GREATER)
+ front = p;
+ else
+ back = p;
+ p = front + (back - front) / 2;
+ SKIP_PAST_NEWLINE(p, back);
+ }
+ return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ *
+ * Return NULL for no such line.
+ *
+ * This routine assumes:
+ *
+ * o front points at the first character in a line.
+ * o front is before or at the first line to be printed.
+ */
+static char *
+linear_search(string, front, back)
+ char *string, *front, *back;
+{
+ while (front < back) {
+ switch (compare(string, front, back)) {
+ case EQUAL: /* Found it. */
+ return (front);
+ case LESS: /* No such string. */
+ return (NULL);
+ case GREATER: /* Keep going. */
+ break;
+ }
+ SKIP_PAST_NEWLINE(front, back);
+ }
+ return (NULL);
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares
+ * with string2 (s1 ??? s2).
+ *
+ * o Matches up to len(s1) are EQUAL.
+ * o Matches up to len(s2) are GREATER.
+ *
+ * The string "s1" is null terminated. The string s2 is '\t', space, (or
+ * "back") terminated.
+ *
+ * !!!
+ * Reasonably modern ctags programs use tabs as separators, not spaces.
+ * However, historic programs did use spaces, and, I got complaints.
+ */
+static int
+compare(s1, s2, back)
+ register char *s1, *s2, *back;
+{
+ for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
+ if (*s1 != *s2)
+ return (*s1 < *s2 ? LESS : GREATER);
+ return (*s1 ? GREATER : s2 < back &&
+ (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);
+}
diff --git a/usr.bin/vi/ex/ex_undo.c b/usr.bin/vi/ex/ex_undo.c
new file mode 100644
index 0000000..1b8ba69
--- /dev/null
+++ b/usr.bin/vi/ex/ex_undo.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_undo.c 8.9 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_undo -- u
+ * Undo the last change.
+ */
+int
+ex_undo(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK m;
+
+ /*
+ * !!!
+ * Historic undo always set the previous context mark.
+ */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ if (mark_set(sp, ep, ABSMARK1, &m, 1))
+ return (1);
+
+ /*
+ * !!!
+ * Multiple undo isn't available in ex, as there's no '.' command.
+ * Whether 'u' is undo or redo is toggled each time, unless there
+ * was a change since the last undo, in which case it's an undo.
+ */
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = FORWARD;
+ }
+ switch (ep->lundo) {
+ case BACKWARD:
+ if (log_forward(sp, ep, &m))
+ return (1);
+ ep->lundo = FORWARD;
+ break;
+ case FORWARD:
+ if (log_backward(sp, ep, &m))
+ return (1);
+ ep->lundo = BACKWARD;
+ break;
+ case NOTSET:
+ abort();
+ }
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_usage.c b/usr.bin/vi/ex/ex_usage.c
new file mode 100644
index 0000000..424b79b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_usage.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_usage.c 8.21 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../vi/vcmd.h"
+
+/*
+ * ex_help -- :help
+ * Display help message.
+ */
+int
+ex_help(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ (void)ex_printf(EXCOOKIE,
+ "To see the list of vi commands, enter \":viusage<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "To see the list of ex commands, enter \":exusage<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "For an ex command usage statement enter \":exusage [cmd]<CR>\"\n");
+ (void)ex_printf(EXCOOKIE,
+ "For a vi key usage statement enter \":viusage [key]<CR>\"\n");
+ (void)ex_printf(EXCOOKIE, "To exit, enter \":q!\"\n");
+ return (0);
+}
+
+/*
+ * ex_usage -- :exusage [cmd]
+ * Display ex usage strings.
+ */
+int
+ex_usage(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ ARGS *ap;
+ EXCMDLIST const *cp;
+ char *name;
+
+ switch (cmdp->argc) {
+ case 1:
+ ap = cmdp->argv[0];
+ for (cp = cmds; cp->name != NULL &&
+ memcmp(ap->bp, cp->name, ap->len); ++cp);
+ if (cp->name == NULL)
+ (void)ex_printf(EXCOOKIE,
+ "The %.*s command is unknown",
+ (int)ap->len, ap->bp);
+ else {
+ (void)ex_printf(EXCOOKIE,
+ "Command: %s\n Usage: %s\n", cp->help, cp->usage);
+ /*
+ * !!!
+ * The "visual" command has two modes, one from ex,
+ * one from the vi colon line. Don't ask.
+ */
+ if (cp != &cmds[C_VISUAL_EX] &&
+ cp != &cmds[C_VISUAL_VI])
+ break;
+ if (cp == &cmds[C_VISUAL_EX])
+ cp = &cmds[C_VISUAL_VI];
+ else
+ cp = &cmds[C_VISUAL_EX];
+ (void)ex_printf(EXCOOKIE,
+ "Command: %s\n Usage: %s\n", cp->help, cp->usage);
+ }
+ break;
+ case 0:
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (cp = cmds; cp->name != NULL; ++cp) {
+ /* The ^D command has an unprintable name. */
+ if (cp == &cmds[C_SCROLL])
+ name = "^D";
+ else
+ name = cp->name;
+ (void)ex_printf(EXCOOKIE,
+ "%*s: %s\n", MAXCMDNAMELEN, name, cp->help);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+
+/*
+ * ex_viusage -- :viusage [key]
+ * Display vi usage strings.
+ */
+int
+ex_viusage(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ VIKEYS const *kp;
+ int key;
+
+ switch (cmdp->argc) {
+ case 1:
+ if (cmdp->argv[0]->len != 1) {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+ key = cmdp->argv[0]->bp[0];
+ if (key > MAXVIKEY)
+ goto nokey;
+
+ /* Special case: '[' and ']' commands. */
+ if ((key == '[' || key == ']') && cmdp->argv[0]->bp[1] != key)
+ goto nokey;
+
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ kp = &vikeys[key];
+
+ if (kp->func == NULL)
+nokey: (void)ex_printf(EXCOOKIE,
+ "The %s key has no current meaning",
+ KEY_NAME(sp, key));
+ else
+ (void)ex_printf(EXCOOKIE,
+ " Key:%s%s\nUsage: %s\n",
+ isblank(*kp->help) ? "" : " ", kp->help, kp->usage);
+ break;
+ case 0:
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (key = 0; key <= MAXVIKEY; ++key) {
+ /* Special case: ~ command. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+ else
+ kp = &vikeys[key];
+ if (kp->help != NULL)
+ (void)ex_printf(EXCOOKIE, "%s\n", kp->help);
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_util.c b/usr.bin/vi/ex/ex_util.c
new file mode 100644
index 0000000..a32b2a4
--- /dev/null
+++ b/usr.bin/vi/ex/ex_util.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_util.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_getline --
+ * Return a line from the terminal.
+ */
+int
+ex_getline(sp, fp, lenp)
+ SCR *sp;
+ FILE *fp;
+ size_t *lenp;
+{
+ EX_PRIVATE *exp;
+ size_t off;
+ int ch;
+ char *p;
+
+ exp = EXP(sp);
+ for (errno = 0, off = 0, p = exp->ibp;;) {
+ if (off >= exp->ibp_len) {
+ BINC_RET(sp, exp->ibp, exp->ibp_len, off + 1);
+ p = exp->ibp + off;
+ }
+ if ((ch = getc(fp)) == EOF && !feof(fp)) {
+ if (errno == EINTR) {
+ errno = 0;
+ clearerr(fp);
+ continue;
+ }
+ return (1);
+ }
+ if (ch == EOF || ch == '\n') {
+ if (ch == EOF && !off)
+ return (1);
+ *lenp = off;
+ return (0);
+ }
+ *p++ = ch;
+ ++off;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * ex_sleave --
+ * Save the terminal/signal state, screen modification time.
+ * Specific to ex/filter.c and ex/ex_shell.c.
+ */
+int
+ex_sleave(sp)
+ SCR *sp;
+{
+ struct stat sb;
+ EX_PRIVATE *exp;
+
+ /* Ignore sessions not using tty's. */
+ if (!F_ISSET(sp->gp, G_STDIN_TTY))
+ return (1);
+
+ exp = EXP(sp);
+ if (tcgetattr(STDIN_FILENO, &exp->leave_term)) {
+ msgq(sp, M_SYSERR, "tcgetattr");
+ return (1);
+ }
+ if (tcsetattr(STDIN_FILENO,
+ TCSANOW | TCSASOFT, &sp->gp->original_termios)) {
+ msgq(sp, M_SYSERR, "tcsetattr");
+ return (1);
+ }
+ /*
+ * The process may write to the terminal. Save the access time
+ * (read) and modification time (write) of the tty; if they have
+ * changed when we restore the modes, will have to refresh the
+ * screen.
+ */
+ if (fstat(STDIN_FILENO, &sb)) {
+ msgq(sp, M_SYSERR, "stat: stdin");
+ exp->leave_atime = exp->leave_mtime = 0;
+ } else {
+ exp->leave_atime = sb.st_atime;
+ exp->leave_mtime = sb.st_mtime;
+ }
+ return (0);
+}
+
+/*
+ * ex_rleave --
+ * Return the terminal/signal state, not screen modification time.
+ * Specific to ex/filter.c and ex/ex_shell.c.
+ */
+void
+ex_rleave(sp)
+ SCR *sp;
+{
+ EX_PRIVATE *exp;
+ struct stat sb;
+
+ exp = EXP(sp);
+
+ /* Restore the terminal modes. */
+ if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &exp->leave_term))
+ msgq(sp, M_SYSERR, "tcsetattr");
+
+ /* If the terminal was used, refresh the screen. */
+ if (fstat(STDIN_FILENO, &sb) || exp->leave_atime == 0 ||
+ exp->leave_atime != sb.st_atime || exp->leave_mtime != sb.st_mtime)
+ F_SET(sp, S_REFRESH);
+}
+
+/*
+ * ex_ncheck --
+ * Check for more files to edit.
+ */
+int
+ex_ncheck(sp, force)
+ SCR *sp;
+ int force;
+{
+ /*
+ * !!!
+ * Historic practice: quit! or two quit's done in succession
+ * (where ZZ counts as a quit) didn't check for other files.
+ */
+ if (!force && sp->ccnt != sp->q_ccnt + 1 &&
+ sp->cargv != NULL && sp->cargv[1] != NULL) {
+ sp->q_ccnt = sp->ccnt;
+ msgq(sp, M_ERR,
+ "More files to edit; use n[ext] to go to the next file, q[uit]! to quit");
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_version.c b/usr.bin/vi/ex/ex_version.c
new file mode 100644
index 0000000..011c257
--- /dev/null
+++ b/usr.bin/vi/ex/ex_version.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_version.c 8.66 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_version -- :version
+ * Display the program version.
+ */
+int
+ex_version(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ static const time_t then = 777148668;
+
+ (void)ex_printf(EXCOOKIE,
+"Version 1.34, %sThe CSRG, University of California, Berkeley.\n",
+ ctime(&then));
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_visual.c b/usr.bin/vi/ex/ex_visual.c
new file mode 100644
index 0000000..acfd537
--- /dev/null
+++ b/usr.bin/vi/ex/ex_visual.c
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_visual.c 8.15 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_visual -- :[line] vi[sual] [^-.+] [window_size] [flags]
+ *
+ * Switch to visual mode.
+ */
+int
+ex_visual(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ size_t len;
+ int pos;
+ char buf[256];
+
+ /* If open option off, disallow visual command. */
+ if (!O_ISSET(sp, O_OPEN)) {
+ msgq(sp, M_ERR,
+ "The visual command requires that the open option be set");
+ return (1);
+ }
+
+ /* If a line specified, move to that line. */
+ if (cmdp->addrcnt)
+ sp->lno = cmdp->addr1.lno;
+
+ /*
+ * Push a command based on the line position flags. If no
+ * flag specified, the line goes at the top of the screen.
+ */
+ switch (F_ISSET(cmdp, E_F_CARAT | E_F_DASH | E_F_DOT | E_F_PLUS)) {
+ case E_F_CARAT:
+ pos = '^';
+ break;
+ case E_F_DASH:
+ pos = '-';
+ break;
+ case E_F_DOT:
+ pos = '.';
+ break;
+ case E_F_PLUS:
+ pos = '+';
+ break;
+ default:
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = 0;
+ F_SET(sp->frp, FR_CURSORSET | FR_FNONBLANK);
+ goto nopush;
+ }
+
+ if (F_ISSET(cmdp, E_COUNT))
+ len = snprintf(buf, sizeof(buf),
+ "%luz%c%lu", sp->lno, pos, cmdp->count);
+ else
+ len = snprintf(buf, sizeof(buf), "%luz%c", sp->lno, pos);
+ (void)term_push(sp, buf, len, CH_NOMAP | CH_QUOTED);
+
+ /*
+ * !!!
+ * Historically, if no line address was specified, the [p#l] flags
+ * caused the cursor to be moved to the last line of the file, which
+ * was then positioned as described above. This seems useless, so
+ * I haven't implemented it.
+ */
+ switch (F_ISSET(cmdp, E_F_HASH | E_F_LIST | E_F_PRINT)) {
+ case E_F_HASH:
+ O_SET(sp, O_NUMBER);
+ break;
+ case E_F_LIST:
+ O_SET(sp, O_LIST);
+ break;
+ case E_F_PRINT:
+ break;
+ }
+
+ /* Switch modes. */
+nopush: F_CLR(sp, S_SCREENS);
+ F_SET(sp, sp->saved_vi_mode);
+
+ return (0);
+}
diff --git a/usr.bin/vi/ex/ex_write.c b/usr.bin/vi/ex/ex_write.c
new file mode 100644
index 0000000..e5273e4
--- /dev/null
+++ b/usr.bin/vi/ex/ex_write.c
@@ -0,0 +1,327 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_write.c 8.38 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+enum which {WN, WQ, WRITE, XIT};
+
+static int exwr __P((SCR *, EXF *, EXCMDARG *, enum which));
+
+/*
+ * ex_wn -- :wn[!] [>>] [file]
+ * Write to a file and switch to the next one.
+ */
+int
+ex_wn(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ if (exwr(sp, ep, cmdp, WN))
+ return (1);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ /* The file name isn't a new file to edit. */
+ cmdp->argc = 0;
+
+ return (ex_next(sp, ep, cmdp));
+}
+
+/*
+ * ex_wq -- :wq[!] [>>] [file]
+ * Write to a file and quit.
+ */
+int
+ex_wq(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ if (exwr(sp, ep, cmdp, WQ))
+ return (1);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
+
+/*
+ * ex_write -- :write[!] [>>] [file]
+ * :write [!] [cmd]
+ * Write to a file.
+ */
+int
+ex_write(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (exwr(sp, ep, cmdp, WRITE));
+}
+
+
+/*
+ * ex_xit -- :x[it]! [file]
+ *
+ * Write out any modifications and quit.
+ */
+int
+ex_xit(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ int force;
+
+ if (F_ISSET((ep), F_MODIFIED) && exwr(sp, ep, cmdp, XIT))
+ return (1);
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ force = F_ISSET(cmdp, E_FORCE);
+
+ if (ex_ncheck(sp, force))
+ return (1);
+
+ F_SET(sp, force ? S_EXIT_FORCE : S_EXIT);
+ return (0);
+}
+
+/*
+ * exwr --
+ * The guts of the ex write commands.
+ */
+static int
+exwr(sp, ep, cmdp, cmd)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+ enum which cmd;
+{
+ EX_PRIVATE *exp;
+ MARK rm;
+ int flags;
+ char *name, *p;
+
+ /* All write commands can have an associated '!'. */
+ LF_INIT(FS_POSSIBLE);
+ if (F_ISSET(cmdp, E_FORCE))
+ LF_SET(FS_FORCE);
+
+ /* Skip any leading whitespace. */
+ if (cmdp->argc != 0)
+ for (p = cmdp->argv[0]->bp; *p && isblank(*p); ++p);
+
+ /* If no arguments, just write the file back. */
+ if (cmdp->argc == 0 || *p == '\0') {
+ if (F_ISSET(cmdp, E_ADDR2_ALL))
+ LF_SET(FS_ALL);
+ return (file_write(sp, ep,
+ &cmdp->addr1, &cmdp->addr2, NULL, flags));
+ }
+
+ /* If "write !" it's a pipe to a utility. */
+ exp = EXP(sp);
+ if (cmd == WRITE && *p == '!') {
+ for (++p; *p && isblank(*p); ++p);
+ if (*p == '\0') {
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+ /* Expand the argument. */
+ if (argv_exp1(sp, ep, cmdp, p, strlen(p), 0))
+ return (1);
+ if (filtercmd(sp, ep, &cmdp->addr1, &cmdp->addr2,
+ &rm, cmdp->argv[1]->bp, FILTER_WRITE))
+ return (1);
+ sp->lno = rm.lno;
+ return (0);
+ }
+
+ /* If "write >>" it's an append to a file. */
+ if (cmd != XIT && p[0] == '>' && p[1] == '>') {
+ LF_SET(FS_APPEND);
+
+ /* Skip ">>" and whitespace. */
+ for (p += 2; *p && isblank(*p); ++p);
+ }
+
+ /* Build an argv so we get an argument count and file expansion. */
+ if (argv_exp2(sp, ep, cmdp, p, strlen(p), 0))
+ return (1);
+
+ switch (cmdp->argc) {
+ case 1:
+ /*
+ * Nothing to expand, write the current file.
+ * XXX
+ * Should never happen, already checked this case.
+ */
+ name = NULL;
+ break;
+ case 2:
+ /* One new argument, write it. */
+ name = cmdp->argv[exp->argsoff - 1]->bp;
+ set_alt_name(sp, name);
+ break;
+ default:
+ /* If expanded to more than one argument, object. */
+ msgq(sp, M_ERR, "%s expanded into too many file names",
+ cmdp->argv[0]->bp);
+ msgq(sp, M_ERR, "Usage: %s", cmdp->cmd->usage);
+ return (1);
+ }
+
+ if (F_ISSET(cmdp, E_ADDR2_ALL))
+ LF_SET(FS_ALL);
+ return (file_write(sp, ep, &cmdp->addr1, &cmdp->addr2, name, flags));
+}
+
+/*
+ * ex_writefp --
+ * Write a range of lines to a FILE *.
+ */
+int
+ex_writefp(sp, ep, name, fp, fm, tm, nlno, nch)
+ SCR *sp;
+ EXF *ep;
+ char *name;
+ FILE *fp;
+ MARK *fm, *tm;
+ u_long *nlno, *nch;
+{
+ struct stat sb;
+ u_long ccnt; /* XXX: can't print off_t portably. */
+ recno_t fline, tline, lcnt;
+ size_t len;
+ int sv_errno;
+ char *p;
+
+ fline = fm->lno;
+ tline = tm->lno;
+
+ if (nlno != NULL) {
+ *nch = 0;
+ *nlno = 0;
+ }
+
+ /*
+ * The vi filter code has multiple processes running simultaneously,
+ * and one of them calls ex_writefp(). The "unsafe" function calls
+ * in this code are to file_gline() and msgq(). File_gline() is safe,
+ * see the comment in filter.c:filtercmd() for details. We don't call
+ * msgq if the multiple process bit in the EXF is set.
+ *
+ * !!!
+ * Historic vi permitted files of 0 length to be written. However,
+ * since the way vi got around dealing with "empty" files was to
+ * always have a line in the file no matter what, it wrote them as
+ * files of a single, empty line. We write empty files.
+ *
+ * "Alex, I'll take vi trivia for $1000."
+ */
+ ccnt = 0;
+ lcnt = 0;
+ if (tline != 0) {
+ for (; fline <= tline; ++fline, ++lcnt) {
+ /* Caller has to provide any interrupt message. */
+ if (INTERRUPTED(sp))
+ break;
+ if ((p = file_gline(sp, ep, fline, &len)) == NULL)
+ break;
+ if (fwrite(p, 1, len, fp) != len) {
+ msgq(sp, M_SYSERR, name);
+ (void)fclose(fp);
+ return (1);
+ }
+ ccnt += len;
+ if (putc('\n', fp) != '\n')
+ break;
+ ++ccnt;
+ }
+ }
+
+ /* If it's a regular file, sync it so that NFS is forced to flush. */
+ if (!fstat(fileno(fp), &sb) &&
+ S_ISREG(sb.st_mode) && fsync(fileno(fp))) {
+ sv_errno = errno;
+ (void)fclose(fp);
+ errno = sv_errno;
+ goto err;
+ }
+ if (fclose(fp))
+ goto err;
+ if (nlno != NULL) {
+ *nch = ccnt;
+ *nlno = lcnt;
+ }
+ return (0);
+
+err: if (!F_ISSET(ep, F_MULTILOCK))
+ msgq(sp, M_SYSERR, name);
+ return (1);
+}
diff --git a/usr.bin/vi/ex/ex_yank.c b/usr.bin/vi/ex/ex_yank.c
new file mode 100644
index 0000000..aa44d2b
--- /dev/null
+++ b/usr.bin/vi/ex/ex_yank.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_yank.c 8.7 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_yank -- :[line [,line]] ya[nk] [buffer] [count]
+ *
+ * Yank the lines into a buffer.
+ */
+int
+ex_yank(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ return (cut(sp, ep,
+ F_ISSET(cmdp, E_BUFFER) ? &cmdp->buffer : NULL,
+ &cmdp->addr1, &cmdp->addr2, CUT_LINEMODE));
+}
diff --git a/usr.bin/vi/ex/ex_z.c b/usr.bin/vi/ex/ex_z.c
new file mode 100644
index 0000000..d130646
--- /dev/null
+++ b/usr.bin/vi/ex/ex_z.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ex_z.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * ex_z -- :[line] z [^-.+=] [count] [flags]
+ *
+ * Adjust window.
+ */
+int
+ex_z(sp, ep, cmdp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *cmdp;
+{
+ MARK abs;
+ recno_t cnt, equals, lno;
+ int eofcheck;
+
+ /*
+ * !!!
+ * If no count specified, use either two times the size of the
+ * scrolling region, or the size of the window option. POSIX
+ * 1003.2 claims that the latter is correct, but historic ex/vi
+ * documentation and practice appear to use the scrolling region.
+ * I'm using the window size as it means that the entire screen
+ * is used instead of losing a line to roundoff. Note, we drop
+ * a line from the cnt if using the window size to leave room for
+ * the next ex prompt.
+ */
+ if (F_ISSET(cmdp, E_COUNT))
+ cnt = cmdp->count;
+ else
+#ifdef HISTORIC_PRACTICE
+ cnt = O_VAL(sp, O_SCROLL) * 2;
+#else
+ cnt = O_VAL(sp, O_WINDOW) - 1;
+#endif
+
+ equals = 0;
+ eofcheck = 0;
+ lno = cmdp->addr1.lno;
+
+ switch (F_ISSET(cmdp,
+ E_F_CARAT | E_F_DASH | E_F_DOT | E_F_EQUAL | E_F_PLUS)) {
+ case E_F_CARAT: /* Display cnt * 2 before the line. */
+ eofcheck = 1;
+ if (lno > cnt * 2)
+ cmdp->addr1.lno = (lno - cnt * 2) + 1;
+ else
+ cmdp->addr1.lno = 1;
+ cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
+ break;
+ case E_F_DASH: /* Line goes at the bottom of the screen. */
+ cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
+ cmdp->addr2.lno = lno;
+ break;
+ case E_F_DOT: /* Line goes in the middle of the screen. */
+ /*
+ * !!!
+ * Historically, the "middleness" of the line overrode the
+ * count, so that "3z.19" or "3z.20" would display the first
+ * 12 lines of the file, i.e. (N - 1) / 2 lines before and
+ * after the specified line.
+ */
+ eofcheck = 1;
+ cnt = (cnt - 1) / 2;
+ cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+ cmdp->addr2.lno = lno + cnt;
+
+ /*
+ * !!!
+ * Historically, z. set the absolute cursor mark.
+ */
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ (void)mark_set(sp, ep, ABSMARK1, &abs, 1);
+ break;
+ case E_F_EQUAL: /* Center with hyphens. */
+ /*
+ * !!!
+ * Strangeness. The '=' flag is like the '.' flag (see the
+ * above comment, it applies here as well) but with a special
+ * little hack. Print out lines of hyphens before and after
+ * the specified line. Additionally, the cursor remains set
+ * on that line.
+ */
+ eofcheck = 1;
+ cnt = (cnt - 1) / 2;
+ cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
+ cmdp->addr2.lno = lno - 1;
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ (void)ex_printf(EXCOOKIE,
+ "%s", "----------------------------------------\n");
+ cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ (void)ex_printf(EXCOOKIE,
+ "%s", "----------------------------------------\n");
+ cmdp->addr1.lno = lno + 1;
+ cmdp->addr2.lno = (lno + cnt) - 1;
+ break;
+ default:
+ /* If no line specified, move to the next one. */
+ if (F_ISSET(cmdp, E_ADDRDEF))
+ ++lno;
+ /* FALLTHROUGH */
+ case E_F_PLUS: /* Line goes at the top of the screen. */
+ eofcheck = 1;
+ cmdp->addr1.lno = lno;
+ cmdp->addr2.lno = (lno + cnt) - 1;
+ break;
+ }
+
+ if (eofcheck) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (cmdp->addr2.lno > lno)
+ cmdp->addr2.lno = lno;
+ }
+
+ if (ex_pr(sp, ep, cmdp))
+ return (1);
+ if (equals)
+ sp->lno = equals;
+ return (0);
+}
diff --git a/usr.bin/vi/ex/excmd.awk b/usr.bin/vi/ex/excmd.awk
new file mode 100644
index 0000000..2890220
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.awk
@@ -0,0 +1,6 @@
+# @(#)excmd.awk 8.1 (Berkeley) 4/17/94
+
+/^\/\* C_[0-9A-Z_]* \*\/$/ {
+ printf("#define %s %d\n", $2, cnt++);
+ next;
+}
diff --git a/usr.bin/vi/ex/excmd.c b/usr.bin/vi/ex/excmd.c
new file mode 100644
index 0000000..422578a
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)excmd.c 8.60 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+/*
+ * This array maps ex command names to command functions.
+ *
+ * The order in which command names are listed below is important --
+ * ambiguous abbreviations are resolved to be the first possible match,
+ * e.g. "r" means "read", not "rewind", because "read" is listed before
+ * "rewind".
+ *
+ * The syntax of the ex commands is unbelievably irregular, and a special
+ * case from beginning to end. Each command has an associated "syntax
+ * script" which describes the "arguments" that are possible. The script
+ * syntax is as follows:
+ *
+ * ! -- ! flag
+ * 1 -- flags: [+-]*[pl#][+-]*
+ * 2 -- flags: [-.+^]
+ * 3 -- flags: [-.+^=]
+ * b -- buffer
+ * c[01+a] -- count (0-N, 1-N, signed 1-N, address offset)
+ * f[N#][or] -- file (a number or N, optional or required)
+ * l -- line
+ * S -- string with file name expansion
+ * s -- string
+ * W -- word string
+ * w[N#][or] -- word (a number or N, optional or required)
+ */
+EXCMDLIST const cmds[] = {
+/* C_SCROLL */
+ {"\004", ex_pr, E_ADDR2|E_NORC,
+ "",
+ "^D",
+ "scroll lines"},
+/* C_BANG */
+ {"!", ex_bang, E_ADDR2_NONE|E_NORC,
+ "S",
+ "[line [,line]] ! command",
+ "filter lines through commands or run commands"},
+/* C_HASH */
+ {"#", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "ca1",
+ "[line [,line]] # [count] [l]",
+ "display numbered lines"},
+/* C_SUBAGAIN */
+ {"&", ex_subagain, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] & [cgr] [count] [#lp]",
+ "repeat the last subsitution"},
+/* C_STAR */
+ {"*", ex_at, 0,
+ "b",
+ "* [buffer]",
+ "execute a buffer"},
+/* C_SHIFTL */
+ {"<", ex_shiftl, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "ca1",
+ "[line [,line]] <[<...] [count] [flags]",
+ "shift lines left"},
+/* C_EQUAL */
+ {"=", ex_equal, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+ "1",
+ "[line] = [flags]",
+ "display line number"},
+/* C_SHIFTR */
+ {">", ex_shiftr, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "ca1",
+ "[line [,line]] >[>...] [count] [flags]",
+ "shift lines right"},
+/* C_AT */
+ {"@", ex_at, 0,
+ "b",
+ "@ [buffer]",
+ "execute a buffer"},
+/* C_APPEND */
+ {"append", ex_append, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+ "!",
+ "[line] a[ppend][!]",
+ "append input to a line"},
+/* C_ABBR */
+ {"abbreviate", ex_abbr, E_NOGLOBAL,
+ "W",
+ "ab[brev] [word replace]",
+ "specify an input abbreviation"},
+/* C_ARGS */
+ {"args", ex_args, E_NOGLOBAL|E_NORC,
+ "",
+ "ar[gs]",
+ "display file argument list"},
+/* C_BG */
+ {"bg", ex_bg, E_NOGLOBAL|E_NORC,
+ "",
+ "bg",
+ "background the current screen"},
+/* C_CHANGE */
+ {"change", ex_change, E_ADDR2|E_NORC|E_ZERODEF,
+ "!ca",
+ "[line [,line]] c[hange][!] [count]",
+ "change lines to input"},
+/* C_CD */
+ {"cd", ex_cd, E_NOGLOBAL,
+ "!f1o",
+ "cd[!] [directory]",
+ "change the current directory"},
+/* C_CHDIR */
+ {"chdir", ex_cd, E_NOGLOBAL,
+ "!f1o",
+ "chd[ir][!] [directory]",
+ "change the current directory"},
+/* C_COPY */
+ {"copy", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l1",
+ "[line [,line]] co[py] line [flags]",
+ "copy lines elsewhere in the file"},
+/*
+ * !!!
+ * Adding new commands starting with 'd' may break the delete command code
+ * in ex_cmd() (the ex parser). Read through the comments there, first.
+ */
+/* C_DELETE */
+ {"delete", ex_delete, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "bca1",
+ "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
+ "delete lines from the file"},
+/* C_DISPLAY */
+ {"display", ex_display, E_NOGLOBAL|E_NORC,
+ "w1r",
+ "display b[uffers] | s[creens] | t[ags]",
+ "display buffers, screens or tags"},
+/* C_DIGRAPH */
+ {"digraph", ex_digraph, E_NOGLOBAL|E_NOPERM|E_NORC,
+ "",
+ "digraph",
+ "specify digraphs (not implemented)"},
+/* C_EDIT */
+ {"edit", ex_edit, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "e[dit][!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EX */
+ {"ex", ex_edit, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "ex[!] [+cmd] [file]",
+ "begin editing another file"},
+/* C_EXUSAGE */
+ {"exusage", ex_usage, E_NOGLOBAL|E_NORC,
+ "w1o",
+ "[exu]sage [command]",
+ "display ex command usage statement"},
+/* C_FILE */
+ {"file", ex_file, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "f[ile] [name]",
+ "display (and optionally set) file name"},
+/* C_FG */
+ {"fg", ex_fg, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "fg [file]",
+ "switch the current screen and a backgrounded screen"},
+/* C_GLOBAL */
+ {"global", ex_global, E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+ "!s",
+ "[line [,line]] g[lobal][!] [;/]RE[;/] [commands]",
+ "execute a global command on lines matching an RE"},
+/* C_HELP */
+ {"help", ex_help, E_NOGLOBAL|E_NORC,
+ "",
+ "he[lp]",
+ "display help statement"},
+/* C_INSERT */
+ {"insert", ex_insert, E_ADDR1|E_NORC,
+ "!",
+ "[line] i[nsert][!]",
+ "insert input before a line"},
+/* C_JOIN */
+ {"join", ex_join, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "!ca1",
+ "[line [,line]] j[oin][!] [count] [flags]",
+ "join lines into a single line"},
+/* C_K */
+ {"k", ex_mark, E_ADDR1|E_NORC,
+ "w1r",
+ "[line] k key",
+ "mark a line position"},
+/* C_LIST */
+ {"list", ex_list, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "ca1",
+ "[line [,line]] l[ist] [count] [#]",
+ "display lines in an unambiguous form"},
+/* C_MOVE */
+ {"move", ex_move, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l",
+ "[line [,line]] m[ove] line",
+ "move lines elsewhere in the file"},
+/* C_MARK */
+ {"mark", ex_mark, E_ADDR1|E_NORC,
+ "w1r",
+ "[line] ma[rk] key",
+ "mark a line position"},
+/* C_MAP */
+ {"map", ex_map, 0,
+ "!W",
+ "map[!] [keys replace]",
+ "map input or commands to one or more keys"},
+/* C_MKEXRC */
+ {"mkexrc", ex_mkexrc, E_NOGLOBAL|E_NORC,
+ "!f1r",
+ "mkexrc[!] file",
+ "write a .exrc file"},
+/* C_NEXT */
+ {"next", ex_next, E_NOGLOBAL|E_NORC,
+ "!fN",
+ "n[ext][!] [+cmd] [file ...]",
+ "edit (and optionally specify) the next file"},
+/* C_NUMBER */
+ {"number", ex_number, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "ca1",
+ "[line [,line]] nu[mber] [count] [l]",
+ "change display to number lines"},
+/* C_OPEN */
+ {"open", ex_open, E_ADDR1,
+ "s",
+ "[line] o[pen] [/RE/] [flags]",
+ "enter \"open\" mode (not implemented)"},
+/* C_PRINT */
+ {"print", ex_pr, E_ADDR2|E_F_PRCLEAR|E_NORC,
+ "ca1",
+ "[line [,line]] p[rint] [count] [#l]",
+ "display lines"},
+/* C_PRESERVE */
+ {"preserve", ex_preserve, E_NOGLOBAL|E_NORC,
+ "",
+ "pre[serve]",
+ "preserve an edit session for recovery"},
+/* C_PREVIOUS */
+ {"previous", ex_prev, E_NOGLOBAL|E_NORC,
+ "!",
+ "prev[ious][!]",
+ "edit the previous file in the file argument list"},
+/* C_PUT */
+ {"put", ex_put, E_ADDR1|E_AUTOPRINT|E_NORC|E_ZERO,
+ "b",
+ "[line] pu[t] [buffer]",
+ "append a cut buffer to the line"},
+/* C_QUIT */
+ {"quit", ex_quit, E_NOGLOBAL|E_NORC,
+ "!",
+ "q[uit][!]",
+ "exit ex/vi"},
+/* C_READ */
+ {"read", ex_read, E_ADDR1|E_NORC|E_ZERO|E_ZERODEF,
+ "s",
+ "[line] r[ead] [!cmd | [file]]",
+ "append input from a command or file to the line"},
+/* C_RECOVER */
+ {"recover", ex_recover, E_NOGLOBAL|E_NORC,
+ "!f1r",
+ "recover[!] file",
+ "recover a saved file"},
+/* C_RESIZE */
+ {"resize", ex_resize, E_NOGLOBAL|E_NORC,
+ "c+",
+ "resize [+-]rows",
+ "grow or shrink the current screen"},
+/* C_REWIND */
+ {"rewind", ex_rew, E_NOGLOBAL|E_NORC,
+ "!",
+ "rew[ind][!]",
+ "re-edit all the files in the file argument list"},
+/* C_SUBSTITUTE */
+ {"substitute", ex_substitute, E_ADDR2|E_NORC,
+ "s",
+"[line [,line]] s[ubstitute] [[/;]RE[/;]/repl[/;] [cgr] [count] [#lp]]",
+ "substitute on lines matching an RE"},
+/* C_SCRIPT */
+ {"script", ex_script, E_NOGLOBAL|E_NORC,
+ "!f1o",
+ "sc[ript][!] [file]",
+ "run a shell in a screen"},
+/* C_SET */
+ {"set", ex_set, E_NOGLOBAL,
+ "wN",
+ "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
+ "set options (use \":set all\" to see all options)"},
+/* C_SHELL */
+ {"shell", ex_shell, E_NOGLOBAL|E_NORC,
+ "",
+ "sh[ell]",
+ "suspend editing and run a shell"},
+/* C_SOURCE */
+ {"source", ex_source, E_NOGLOBAL,
+ "f1r",
+ "so[urce] file",
+ "read a file of ex commands"},
+/* C_SPLIT */
+ {"split", ex_split, E_NOGLOBAL|E_NORC,
+ "fNo",
+ "sp[lit] [file ...]",
+ "split the current screen into two screens"},
+/* C_STOP */
+ {"stop", ex_stop, E_NOGLOBAL|E_NORC,
+ "!",
+ "st[op][!]",
+ "suspend the edit session"},
+/* C_SUSPEND */
+ {"suspend", ex_stop, E_NOGLOBAL|E_NORC,
+ "!",
+ "su[spend][!]",
+ "suspend the edit session"},
+/* C_T */
+ {"t", ex_copy, E_ADDR2|E_AUTOPRINT|E_NORC,
+ "l1",
+ "[line [,line]] t line [flags]",
+ "copy lines elsewhere in the file"},
+/* C_TAG */
+ {"tag", ex_tagpush, E_NOGLOBAL,
+ "!w1o",
+ "ta[g][!] [string]",
+ "edit the file containing the tag"},
+/* C_TAGPOP */
+ {"tagpop", ex_tagpop, E_NOGLOBAL|E_NORC,
+ "!w1o",
+ "tagp[op][!] [number | file]",
+ "return to a previous tag"},
+/* C_TAGTOP */
+ {"tagtop", ex_tagtop, E_NOGLOBAL|E_NORC,
+ "!",
+ "tagt[op][!]",
+ "return to the first tag"},
+/* C_UNDO */
+ {"undo", ex_undo, E_AUTOPRINT|E_NOGLOBAL|E_NORC,
+ "",
+ "u[ndo]",
+ "undo the most recent change"},
+/* C_UNABBREVIATE */
+ {"unabbreviate",ex_unabbr, E_NOGLOBAL,
+ "w1r",
+ "una[bbrev] word",
+ "delete an abbreviation"},
+/* C_UNMAP */
+ {"unmap", ex_unmap, E_NOGLOBAL,
+ "!w1r",
+ "unm[ap][!] word",
+ "delete an input or command map"},
+/* C_VGLOBAL */
+ {"vglobal", ex_vglobal, E_ADDR2_ALL|E_NOGLOBAL|E_NORC,
+ "s",
+ "[line [,line]] v[global] [;/]RE[;/] [commands]",
+ "execute a global command on lines NOT matching an RE"},
+/* C_VERSION */
+ {"version", ex_version, E_NOGLOBAL|E_NORC,
+ "",
+ "version",
+ "display the program version information"},
+/* C_VISUAL_EX */
+ {"visual", ex_visual, E_ADDR1|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "2c11",
+ "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
+ "enter visual (vi) mode from ex mode"},
+/* C_VISUAL_VI */
+ {"visual", ex_edit, E_NOGLOBAL|E_NORC,
+ "f1o",
+ "vi[sual][!] [+cmd] [file]",
+ "edit another file (from vi mode only)"},
+/* C_VIUSAGE */
+ {"viusage", ex_viusage, E_NOGLOBAL|E_NORC,
+ "w1o",
+ "[viu]sage [key]",
+ "display vi key usage statement"},
+/* C_WRITE */
+ {"write", ex_write, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] w[rite][!] [!cmd | [>>] [file]]",
+ "write the file"},
+/* C_WN */
+ {"wn", ex_wn, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] wn[!] [>>] [file]",
+ "write the file and switch to the next file"},
+/* C_WQ */
+ {"wq", ex_wq, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!s",
+ "[line [,line]] wq[!] [>>] [file]",
+ "write the file and exit"},
+/* C_XIT */
+ {"xit", ex_xit, E_ADDR2_ALL|E_NOGLOBAL|E_NORC|E_ZERODEF,
+ "!f1o",
+ "[line [,line]] x[it][!] [file]",
+ "exit"},
+/* C_YANK */
+ {"yank", ex_yank, E_ADDR2|E_NORC,
+ "bca",
+ "[line [,line]] ya[nk] [buffer] [count]",
+ "copy lines to a cut buffer"},
+/* C_Z */
+ {"z", ex_z, E_ADDR1|E_NOGLOBAL|E_NORC,
+ "3c01",
+ "[line] z [-|.|+|^|=] [count] [flags]",
+ "display different screens of the file"},
+/* C_SUBTILDE */
+ {"~", ex_subtilde, E_ADDR2|E_NORC,
+ "s",
+ "[line [,line]] ~ [cgr] [count] [#lp]",
+ "replace previous RE with previous replacement string,"},
+ {NULL},
+};
diff --git a/usr.bin/vi/ex/excmd.h.stub b/usr.bin/vi/ex/excmd.h.stub
new file mode 100644
index 0000000..3b4051b
--- /dev/null
+++ b/usr.bin/vi/ex/excmd.h.stub
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)excmd.h.stub 8.73 (Berkeley) 8/9/94
+ */
+
+#define PROMPTCHAR ':' /* Prompt character. */
+
+/* Ex command structure. */
+typedef struct _excmdlist {
+ char *name; /* Command name. */
+ /* Underlying function. */
+ int (*fn) __P((SCR *, EXF *, EXCMDARG *));
+
+#define E_ADDR1 0x0000001 /* One address. */
+#define E_ADDR2 0x0000002 /* Two address. */
+#define E_ADDR2_ALL 0x0000004 /* Zero/two addresses; zero == all. */
+#define E_ADDR2_NONE 0x0000008 /* Zero/two addresses; zero == none. */
+#define E_ADDRDEF 0x0000010 /* Default addresses used. */
+#define E_AUTOPRINT 0x0000020 /* Command always sets autoprint. */
+#define E_BUFFER 0x0000040 /* Buffer name supplied. */
+#define E_COUNT 0x0000080 /* Count supplied. */
+#define E_COUNT_NEG 0x0000100 /* Count was signed negative. */
+#define E_COUNT_POS 0x0000200 /* Count was signed positive. */
+#define E_FORCE 0x0000400 /* ! */
+
+#define E_F_CARAT 0x0000800 /* ^ flag. */
+#define E_F_DASH 0x0001000 /* - flag. */
+#define E_F_DOT 0x0002000 /* . flag. */
+#define E_F_EQUAL 0x0004000 /* = flag. */
+#define E_F_HASH 0x0008000 /* # flag. */
+#define E_F_LIST 0x0010000 /* l flag. */
+#define E_F_PLUS 0x0020000 /* + flag. */
+#define E_F_PRINT 0x0040000 /* p flag. */
+
+#define E_F_PRCLEAR 0x0080000 /* Clear the print (#, l, p) flags. */
+#define E_MODIFY 0x0100000 /* File name expansion modified arg. */
+#define E_NOGLOBAL 0x0200000 /* Not in a global. */
+#define E_NOPERM 0x0400000 /* Permission denied for now. */
+#define E_NORC 0x0800000 /* Not from a .exrc or EXINIT. */
+#define E_ZERO 0x1000000 /* 0 is a legal addr1. */
+#define E_ZERODEF 0x2000000 /* 0 is default addr1 of empty files. */
+ u_int32_t flags;
+ char *syntax; /* Syntax script. */
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+} EXCMDLIST;
+#define MAXCMDNAMELEN 12 /* Longest command name. */
+extern EXCMDLIST const cmds[]; /* List of ex commands. */
+
+/*
+ * Structure passed around to functions implementing ex commands.
+ * There are several commands in vi that build one of these and
+ * call ex directly. See vi/v_ex.c for details.
+ */
+struct _excmdarg {
+ EXCMDLIST const *cmd; /* Command entry in command table. */
+ CHAR_T buffer; /* Named buffer. */
+ recno_t lineno; /* Line number. */
+ long count; /* Signed, specified count. */
+ long flagoff; /* Signed, flag offset parsed by command. */
+ int addrcnt; /* Number of addresses (0, 1 or 2). */
+ MARK addr1; /* 1st address. */
+ MARK addr2; /* 2nd address. */
+ ARGS **argv; /* Array of arguments. */
+ int argc; /* Count of arguments. */
+ u_int32_t flags; /* Selected flags from EXCMDLIST. */
+};
+
+/* Global ranges. */
+typedef struct _range RANGE;
+struct _range {
+ CIRCLEQ_ENTRY(_range) q; /* Linked list of ranges. */
+ recno_t start, stop; /* Start/stop of the range. */
+};
+
+/* Cd paths. */
+typedef struct _cdpath CDPATH;
+struct _cdpath {
+ TAILQ_ENTRY(_cdpath) q; /* Linked list of cd paths. */
+ char *path; /* Path. */
+};
+
+/* Ex private, per-screen memory. */
+typedef struct _ex_private {
+ ARGS **args; /* Arguments. */
+ int argscnt; /* Argument count. */
+ int argsoff; /* Offset into arguments. */
+
+ CHAR_T at_lbuf; /* Last executed at buffer's name. */
+ int at_lbuf_set; /* If at_lbuf is set. */
+
+ char *ibp; /* Line input buffer. */
+ size_t ibp_len; /* Line input buffer length. */
+
+ u_int32_t fdef; /* Default command flags. */
+
+ CHAR_T *lastbcomm; /* Last bang command. */
+
+ struct termios leave_term; /* ex_[sr]leave tty state. */
+ /* XXX: Should be struct timespec's, but time_t is more portable. */
+ time_t leave_atime; /* ex_[sr]leave old access time. */
+ time_t leave_mtime; /* ex_[sr]leave old mod time. */
+
+ TAILQ_HEAD(_tagh, _tag) tagq; /* Tag list (stack). */
+ TAILQ_HEAD(_tagfh, _tagf) tagfq;/* Tag file list. */
+ char *tlast; /* Saved last tag. */
+
+ TAILQ_HEAD(_cdh, _cdpath) cdq; /* Cd path list. */
+
+ /* Linked list of ranges. */
+ CIRCLEQ_HEAD(_rangeh, _range) rangeq;
+ recno_t range_lno; /* Range set line number. */
+
+#define EX_ABSMARK 0x01 /* Set the absolute mark. */
+#define EX_AUTOPRINT 0x02 /* Autoprint flag. */
+ u_int8_t flags;
+} EX_PRIVATE;
+#define EXP(sp) ((EX_PRIVATE *)((sp)->ex_private))
+
+/*
+ * !!!
+ * Historically, .exrc files and EXINIT variables could only use ^V
+ * as an escape character, neither ^Q or a user specified character
+ * worked. We enforce that here, just in case someone depends on it.
+ */
+#define IS_ESCAPE(sp, ch) \
+ (F_ISSET(sp, S_VLITONLY) ? \
+ (ch) == CH_LITERAL : KEY_VAL(sp, ch) == K_VLNEXT)
+
+/*
+ * Filter actions:
+ *
+ * FILTER Filter text through the utility.
+ * FILTER_READ Read from the utility into the file.
+ * FILTER_WRITE Write to the utility, display its output.
+ */
+enum filtertype { FILTER, FILTER_READ, FILTER_WRITE };
+int filtercmd __P((SCR *, EXF *,
+ MARK *, MARK *, MARK *, char *, enum filtertype));
+
+/* Argument expansion routines. */
+int argv_init __P((SCR *, EXF *, EXCMDARG *));
+int argv_exp0 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int argv_exp1 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int argv_exp2 __P((SCR *, EXF *, EXCMDARG *, char *, size_t, int));
+int argv_exp3 __P((SCR *, EXF *, EXCMDARG *, char *, size_t));
+int argv_free __P((SCR *));
+
+/* Ex function prototypes. */
+int ex __P((SCR *, EXF *));
+int ex_cfile __P((SCR *, EXF *, char *, int));
+int ex_cmd __P((SCR *, EXF *, char *, size_t, int));
+int ex_cdalloc __P((SCR *, char *));
+int ex_cdfree __P((SCR *));
+int ex_end __P((SCR *));
+int ex_exec_proc __P((SCR *, char *, char *, char *));
+int ex_gb __P((SCR *, EXF *, TEXTH *, int, u_int));
+int ex_getline __P((SCR *, FILE *, size_t *));
+int ex_icmd __P((SCR *, EXF *, char *, size_t, int));
+int ex_init __P((SCR *, EXF *));
+int ex_is_abbrev __P((char *, size_t));
+int ex_is_unmap __P((char *, size_t));
+int ex_ldisplay __P((SCR *, CHAR_T *, size_t, size_t, u_int));
+int ex_ncheck __P((SCR *, int));
+int ex_optchange __P((SCR *, int));
+int ex_print __P((SCR *, EXF *, MARK *, MARK *, int));
+int ex_readfp __P((SCR *, EXF *, char *, FILE *, MARK *, recno_t *, int));
+void ex_refresh __P((SCR *, EXF *));
+void ex_rleave __P((SCR *));
+int ex_screen_copy __P((SCR *, SCR *));
+int ex_screen_end __P((SCR *));
+int ex_sdisplay __P((SCR *, EXF *));
+int ex_sleave __P((SCR *));
+int ex_suspend __P((SCR *));
+int ex_tdisplay __P((SCR *, EXF *));
+int ex_writefp __P((SCR *, EXF *,
+ char *, FILE *, MARK *, MARK *, u_long *, u_long *));
+void global_insdel __P((SCR *, EXF *, enum operation, recno_t));
+int proc_wait __P((SCR *, long, const char *, int));
+int sscr_end __P((SCR *));
+int sscr_exec __P((SCR *, EXF *, recno_t));
+int sscr_input __P((SCR *));
+
+int abbr_save __P((SCR *, FILE *));
+int map_save __P((SCR *, FILE *));
+
+#define EXPROTO(name) int name __P((SCR *, EXF *, EXCMDARG *))
+EXPROTO(ex_abbr);
+EXPROTO(ex_append);
+EXPROTO(ex_args);
+EXPROTO(ex_at);
+EXPROTO(ex_bang);
+EXPROTO(ex_bg);
+EXPROTO(ex_cd);
+EXPROTO(ex_change);
+EXPROTO(ex_color);
+EXPROTO(ex_copy);
+EXPROTO(ex_debug);
+EXPROTO(ex_delete);
+EXPROTO(ex_digraph);
+EXPROTO(ex_display);
+EXPROTO(ex_edit);
+EXPROTO(ex_equal);
+EXPROTO(ex_fg);
+EXPROTO(ex_file);
+EXPROTO(ex_global);
+EXPROTO(ex_help);
+EXPROTO(ex_insert);
+EXPROTO(ex_join);
+EXPROTO(ex_list);
+EXPROTO(ex_map);
+EXPROTO(ex_mark);
+EXPROTO(ex_mkexrc);
+EXPROTO(ex_move);
+EXPROTO(ex_next);
+EXPROTO(ex_number);
+EXPROTO(ex_open);
+EXPROTO(ex_pr);
+EXPROTO(ex_preserve);
+EXPROTO(ex_prev);
+EXPROTO(ex_put);
+EXPROTO(ex_quit);
+EXPROTO(ex_read);
+EXPROTO(ex_recover);
+EXPROTO(ex_resize);
+EXPROTO(ex_rew);
+EXPROTO(ex_script);
+EXPROTO(ex_set);
+EXPROTO(ex_shell);
+EXPROTO(ex_shiftl);
+EXPROTO(ex_shiftr);
+EXPROTO(ex_source);
+EXPROTO(ex_split);
+EXPROTO(ex_stop);
+EXPROTO(ex_subagain);
+EXPROTO(ex_substitute);
+EXPROTO(ex_subtilde);
+EXPROTO(ex_tagpop);
+EXPROTO(ex_tagpush);
+EXPROTO(ex_tagtop);
+EXPROTO(ex_unabbr);
+EXPROTO(ex_undo);
+EXPROTO(ex_unmap);
+EXPROTO(ex_usage);
+EXPROTO(ex_validate);
+EXPROTO(ex_version);
+EXPROTO(ex_vglobal);
+EXPROTO(ex_visual);
+EXPROTO(ex_viusage);
+EXPROTO(ex_wn);
+EXPROTO(ex_wq);
+EXPROTO(ex_write);
+EXPROTO(ex_xit);
+EXPROTO(ex_yank);
+EXPROTO(ex_z);
diff --git a/usr.bin/vi/ex/filter.c b/usr.bin/vi/ex/filter.c
new file mode 100644
index 0000000..abe7ca1
--- /dev/null
+++ b/usr.bin/vi/ex/filter.c
@@ -0,0 +1,414 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)filter.c 8.45 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+#include <pathnames.h>
+
+#include "vi.h"
+#include "excmd.h"
+
+static int filter_ldisplay __P((SCR *, FILE *));
+
+/*
+ * filtercmd --
+ * Run a range of lines through a filter utility and optionally
+ * replace the original text with the stdout/stderr output of
+ * the utility.
+ */
+int
+filtercmd(sp, ep, fm, tm, rp, cmd, ftype)
+ SCR *sp;
+ EXF *ep;
+ MARK *fm, *tm, *rp;
+ char *cmd;
+ enum filtertype ftype;
+{
+ FILE *ifp, *ofp;
+ pid_t parent_writer_pid, utility_pid;
+ recno_t nread;
+ int input[2], output[2], rval, teardown;
+ char *name;
+
+ /* Set return cursor position; guard against a line number of zero. */
+ *rp = *fm;
+ if (fm->lno == 0)
+ rp->lno = 1;
+
+ /*
+ * There are three different processes running through this code.
+ * They are the utility, the parent-writer and the parent-reader.
+ * The parent-writer is the process that writes from the file to
+ * the utility, the parent reader is the process that reads from
+ * the utility.
+ *
+ * Input and output are named from the utility's point of view.
+ * The utility reads from input[0] and the parent(s) write to
+ * input[1]. The parent(s) read from output[0] and the utility
+ * writes to output[1].
+ *
+ * In the FILTER_READ case, the utility isn't expected to want
+ * input. Redirect its input from /dev/null. Otherwise open
+ * up utility input pipe.
+ */
+ teardown = 0;
+ ofp = NULL;
+ input[0] = input[1] = output[0] = output[1] = -1;
+ if (ftype == FILTER_READ) {
+ if ((input[0] = open(_PATH_DEVNULL, O_RDONLY, 0)) < 0) {
+ msgq(sp, M_ERR,
+ "filter: %s: %s", _PATH_DEVNULL, strerror(errno));
+ return (1);
+ }
+ } else
+ if (pipe(input) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ goto err;
+ }
+
+ /* Open up utility output pipe. */
+ if (pipe(output) < 0) {
+ msgq(sp, M_SYSERR, "pipe");
+ goto err;
+ }
+ if ((ofp = fdopen(output[0], "r")) == NULL) {
+ msgq(sp, M_SYSERR, "fdopen");
+ goto err;
+ }
+
+ /*
+ * Save ex/vi terminal settings, and restore the original ones.
+ * Restoration so that users can do things like ":r! cat /dev/tty".
+ */
+ teardown = ftype != FILTER_WRITE && !ex_sleave(sp);
+
+ /* Fork off the utility process. */
+ SIGBLOCK(sp->gp);
+ switch (utility_pid = vfork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "vfork");
+err: if (input[0] != -1)
+ (void)close(input[0]);
+ if (input[1] != -1)
+ (void)close(input[1]);
+ if (ofp != NULL)
+ (void)fclose(ofp);
+ else if (output[0] != -1)
+ (void)close(output[0]);
+ if (output[1] != -1)
+ (void)close(output[1]);
+ rval = 1;
+ goto ret;
+ case 0: /* Utility. */
+ /* The utility has default signal behavior. */
+ sig_end();
+
+ /*
+ * Redirect stdin from the read end of the input pipe, and
+ * redirect stdout/stderr to the write end of the output pipe.
+ *
+ * !!!
+ * Historically, ex only directed stdout into the input pipe,
+ * letting stderr come out on the terminal as usual. Vi did
+ * not, directing both stdout and stderr into the input pipe.
+ * We match that practice for both ex and vi for consistency.
+ */
+ (void)dup2(input[0], STDIN_FILENO);
+ (void)dup2(output[1], STDOUT_FILENO);
+ (void)dup2(output[1], STDERR_FILENO);
+
+ /* Close the utility's file descriptors. */
+ (void)close(input[0]);
+ (void)close(input[1]);
+ (void)close(output[0]);
+ (void)close(output[1]);
+
+ if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
+ name = O_STR(sp, O_SHELL);
+ else
+ ++name;
+
+ execl(O_STR(sp, O_SHELL), name, "-c", cmd, NULL);
+ msgq(sp, M_ERR, "Error: execl: %s: %s",
+ O_STR(sp, O_SHELL), strerror(errno));
+ _exit (127);
+ /* NOTREACHED */
+ default: /* Parent-reader, parent-writer. */
+ SIGUNBLOCK(sp->gp);
+
+ /* Close the pipe ends neither parent will use. */
+ (void)close(input[0]);
+ (void)close(output[1]);
+ break;
+ }
+
+ /*
+ * FILTER_READ:
+ *
+ * Reading is the simple case -- we don't need a parent writer,
+ * so the parent reads the output from the read end of the output
+ * pipe until it finishes, then waits for the child. Ex_readfp
+ * appends to the MARK, and closes ofp.
+ *
+ * !!!
+ * Set the return cursor to the last line read in. Historically,
+ * this behaves differently from ":r file" command, which leaves
+ * the cursor at the first line read in. Check to make sure that
+ * it's not past EOF because we were reading into an empty file.
+ */
+ if (ftype == FILTER_READ) {
+ rval = ex_readfp(sp, ep, "filter", ofp, fm, &nread, 0);
+ sp->rptlines[L_ADDED] += nread;
+ if (fm->lno == 0)
+ rp->lno = nread;
+ else
+ rp->lno += nread;
+ goto uwait;
+ }
+
+ /*
+ * FILTER, FILTER_WRITE
+ *
+ * Here we need both a reader and a writer. Temporary files are
+ * expensive and we'd like to avoid disk I/O. Using pipes has the
+ * obvious starvation conditions. It's done as follows:
+ *
+ * fork
+ * child
+ * write lines out
+ * exit
+ * parent
+ * FILTER:
+ * read lines into the file
+ * delete old lines
+ * FILTER_WRITE
+ * read and display lines
+ * wait for child
+ *
+ * XXX
+ * We get away without locking the underlying database because we know
+ * that none of the records that we're reading will be modified until
+ * after we've read them. This depends on the fact that the current
+ * B+tree implementation doesn't balance pages or similar things when
+ * it inserts new records. When the DB code has locking, we should
+ * treat vi as if it were multiple applications sharing a database, and
+ * do the required locking. If necessary a work-around would be to do
+ * explicit locking in the line.c:file_gline() code, based on the flag
+ * set here.
+ */
+ rval = 0;
+ F_SET(ep, F_MULTILOCK);
+
+ SIGBLOCK(sp->gp);
+ switch (parent_writer_pid = fork()) {
+ case -1: /* Error. */
+ SIGUNBLOCK(sp->gp);
+
+ msgq(sp, M_SYSERR, "fork");
+ (void)close(input[1]);
+ (void)close(output[0]);
+ rval = 1;
+ break;
+ case 0: /* Parent-writer. */
+ /*
+ * Write the selected lines to the write end of the input
+ * pipe. This instance of ifp is closed by ex_writefp.
+ */
+ (void)close(output[0]);
+ if ((ifp = fdopen(input[1], "w")) == NULL)
+ _exit (1);
+ _exit(ex_writefp(sp, ep, "filter", ifp, fm, tm, NULL, NULL));
+
+ /* NOTREACHED */
+ default: /* Parent-reader. */
+ SIGUNBLOCK(sp->gp);
+
+ (void)close(input[1]);
+ if (ftype == FILTER_WRITE)
+ /*
+ * Read the output from the read end of the output
+ * pipe and display it. Filter_ldisplay closes ofp.
+ */
+ rval = filter_ldisplay(sp, ofp);
+ else {
+ /*
+ * Read the output from the read end of the output
+ * pipe. Ex_readfp appends to the MARK and closes
+ * ofp.
+ */
+ rval = ex_readfp(sp, ep, "filter", ofp, tm, &nread, 0);
+ sp->rptlines[L_ADDED] += nread;
+ }
+
+ /* Wait for the parent-writer. */
+ rval |= proc_wait(sp,
+ (long)parent_writer_pid, "parent-writer", 1);
+
+ /* Delete any lines written to the utility. */
+ if (rval == 0 && ftype == FILTER &&
+ (cut(sp, ep, NULL, fm, tm, CUT_LINEMODE) ||
+ delete(sp, ep, fm, tm, 1))) {
+ rval = 1;
+ break;
+ }
+
+ /*
+ * If the filter had no output, we may have just deleted
+ * the cursor. Don't do any real error correction, we'll
+ * try and recover later.
+ */
+ if (rp->lno > 1 && file_gline(sp, ep, rp->lno, NULL) == NULL)
+ --rp->lno;
+ break;
+ }
+ F_CLR(ep, F_MULTILOCK);
+
+uwait: rval |= proc_wait(sp, (long)utility_pid, cmd, 0);
+
+ /* Restore ex/vi terminal settings. */
+ret: if (teardown)
+ ex_rleave(sp);
+ return (rval);
+}
+
+/*
+ * proc_wait --
+ * Wait for one of the processes.
+ *
+ * !!!
+ * The pid_t type varies in size from a short to a long depending on the
+ * system. It has to be cast into something or the standard promotion
+ * rules get you. I'm using a long based on the belief that nobody is
+ * going to make it unsigned and it's unlikely to be a quad.
+ */
+int
+proc_wait(sp, pid, cmd, okpipe)
+ SCR *sp;
+ long pid;
+ const char *cmd;
+ int okpipe;
+{
+ extern const char *const sys_siglist[];
+ size_t len;
+ int pstat;
+
+ /*
+ * Wait for the utility to finish. We can get interrupted
+ * by SIGALRM, just ignore it.
+ */
+ for (;;) {
+ errno = 0;
+ if (waitpid((pid_t)pid, &pstat, 0) != -1)
+ break;
+ if (errno != EINTR) {
+ msgq(sp, M_SYSERR, "wait error");
+ return (1);
+ }
+ }
+
+ /*
+ * Display the utility's exit status. Ignore SIGPIPE from the
+ * parent-writer, as that only means that the utility chose to
+ * exit before reading all of its input.
+ */
+ if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
+ for (; isblank(*cmd); ++cmd);
+ len = strlen(cmd);
+ msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
+ MIN(len, 20), cmd, len > 20 ? "..." : "",
+ sys_siglist[WTERMSIG(pstat)],
+ WCOREDUMP(pstat) ? "; core dumped" : "");
+ return (1);
+ }
+ if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
+ for (; isblank(*cmd); ++cmd);
+ len = strlen(cmd);
+ msgq(sp, M_ERR, "%.*s%s: exited with status %d",
+ MIN(len, 20), cmd, len > 20 ? "..." : "",
+ WEXITSTATUS(pstat));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * filter_ldisplay --
+ * Display output from a utility.
+ *
+ * !!!
+ * Historically, the characters were passed unmodified to the terminal.
+ * We use the ex print routines to make sure they're printable.
+ */
+static int
+filter_ldisplay(sp, fp)
+ SCR *sp;
+ FILE *fp;
+{
+ size_t len;
+
+ EX_PRIVATE *exp;
+
+ F_SET(sp, S_INTERRUPTIBLE);
+ for (exp = EXP(sp); !ex_getline(sp, fp, &len);) {
+ if (ex_ldisplay(sp, exp->ibp, len, 0, 0))
+ break;
+ if (INTERRUPTED(sp))
+ break;
+ }
+ if (ferror(fp))
+ msgq(sp, M_SYSERR, "filter input");
+ (void)fclose(fp);
+ return (0);
+}
diff --git a/usr.bin/vi/ex/script.h b/usr.bin/vi/ex/script.h
new file mode 100644
index 0000000..b21c63a
--- /dev/null
+++ b/usr.bin/vi/ex/script.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)script.h 8.2 (Berkeley) 4/17/94
+ */
+
+struct _script {
+ pid_t sh_pid; /* Shell pid. */
+ int sh_master; /* Master pty fd. */
+ int sh_slave; /* Slave pty fd. */
+ char *sh_prompt; /* Prompt. */
+ size_t sh_prompt_len; /* Prompt length. */
+ char sh_name[64]; /* Pty name */
+ struct winsize sh_win; /* Window size. */
+ struct termios sh_term; /* Terminal information. */
+};
diff --git a/usr.bin/vi/ex/tag.h b/usr.bin/vi/ex/tag.h
new file mode 100644
index 0000000..a9fd59d
--- /dev/null
+++ b/usr.bin/vi/ex/tag.h
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tag.h 8.13 (Berkeley) 7/17/94
+ */
+
+struct _tagf { /* Tag file. */
+ TAILQ_ENTRY(_tagf) q; /* Linked list of tag files. */
+ char *name; /* Tag file name. */
+
+#define TAGF_DNE 0x01 /* Didn't exist. */
+#define TAGF_DNE_WARN 0x02 /* DNE error reported. */
+ u_int8_t flags;
+};
+
+struct _tag { /* Tag stack. */
+ TAILQ_ENTRY(_tag) q; /* Linked list of tags. */
+ FREF *frp; /* Saved file name. */
+ recno_t lno; /* Saved line number. */
+ size_t cno; /* Saved column number. */
+ char *search; /* Search string. */
+ size_t slen; /* Search string length. */
+};
+
+int ex_tagalloc __P((SCR *, char *));
+int ex_tagcopy __P((SCR *, SCR *));
+int ex_tagdisplay __P((SCR *, EXF *));
+int ex_tagfirst __P((SCR *, char *));
+int ex_tagfree __P((SCR *));
diff --git a/usr.bin/vi/install/recover.script b/usr.bin/vi/install/recover.script
new file mode 100644
index 0000000..2fbce72
--- /dev/null
+++ b/usr.bin/vi/install/recover.script
@@ -0,0 +1,46 @@
+# @(#)recover.script 8.7 (Berkeley) 8/16/94
+#
+# Script to recover nvi edit sessions.
+#
+RECDIR=/var/tmp/vi.recover
+SENDMAIL=/usr/lib/sendmail
+echo 'Recovering nvi editor sessions.'
+
+# Check editor backup files.
+vibackup=`echo $RECDIR/vi.*`
+if [ "$vibackup" != "$RECDIR/vi.*" ]; then
+ for i in $vibackup; do
+ # Only test files that are readable.
+ if test ! -r $i; then
+ continue
+ fi
+
+ # Unmodified nvi editor backup files either have the
+ # execute bit set or are zero length. Delete them.
+ if test -x $i -o ! -s $i; then
+ rm $i
+ fi
+ done
+fi
+
+# It is possible to get incomplete recovery files, if the editor crashes
+# at the right time.
+virecovery=`echo $RECDIR/recover.*`
+if [ "$virecovery" != "$RECDIR/recover.*" ]; then
+ for i in $virecovery; do
+ # Only test files that are readable.
+ if test ! -r $i; then
+ continue
+ fi
+
+ # Delete any recovery files that are zero length, corrupted,
+ # or that have no corresponding backup file. Else send mail
+ # to the user.
+ recfile=`awk '/^X-vi-recover-path:/{print $2}' < $i`
+ if test -n "$recfile" -a -s "$recfile"; then
+ $SENDMAIL -t < $i
+ else
+ rm $i
+ fi
+ done
+fi
diff --git a/usr.bin/vi/sex/sex_confirm.c b/usr.bin/vi/sex/sex_confirm.c
new file mode 100644
index 0000000..f2bdcb9
--- /dev/null
+++ b/usr.bin/vi/sex/sex_confirm.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_confirm.c 8.9 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+enum confirm
+sex_confirm(sp, ep, fp, tp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+{
+ CH ikey;
+ recno_t cnt;
+
+ if (ex_print(sp, ep, fp, tp, 0))
+ return (CONF_QUIT);
+
+ for (cnt = fp->cno; cnt; --cnt)
+ (void)putc(' ', stdout);
+ for (cnt = tp->cno; cnt; --cnt)
+ (void)putc('^', stdout);
+ (void)fprintf(stdout, "[ynq]");
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (CONF_QUIT);
+ switch (ikey.ch) {
+ case CH_YES:
+ return (CONF_YES);
+ case CH_QUIT:
+ return (CONF_QUIT);
+ default:
+ case CH_NO:
+ return (CONF_NO);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/sex/sex_get.c b/usr.bin/vi/sex/sex_get.c
new file mode 100644
index 0000000..45092af
--- /dev/null
+++ b/usr.bin/vi/sex/sex_get.c
@@ -0,0 +1,514 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_get.c 8.39 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../vi/vcmd.h"
+#include "sex_screen.h"
+
+/*
+ * !!!
+ * The ex input didn't have escape characters like ^V. The only special
+ * character was the backslash character, and that only when it preceded
+ * a newline as part of a substitution replacement pattern. For example,
+ * the command input ":a\<cr>" failed immediately with an error, as the
+ * <cr> wasn't part of a substitution replacement pattern. This implies
+ * a frightening integration of the editor and the RE engine. There's no
+ * way we're going to reproduce those semantics. So, if backslashes are
+ * special, this code inserts the backslash and the next character into the
+ * string, without regard for the character or the command being entered.
+ * Since "\<cr>" was illegal historically (except for the one special case),
+ * and the command will fail eventually, historical scripts shouldn't break
+ * (presuming they didn't depend on the failure mode itself or the characters
+ * remaining when failure occurred.
+ */
+static void txt_display __P((SCR *, TEXT *, size_t, size_t *));
+static int txt_outdent __P((SCR *, TEXT *));
+
+#define ERASECH { \
+ for (cnt = tp->wd[tp->len]; cnt-- > 0; --col) \
+ (void)printf("\b \b"); \
+}
+
+/*
+ * sex_get --
+ * Get lines from the terminal for ex.
+ */
+enum input
+sex_get(sp, ep, tiqh, prompt, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ ARG_CHAR_T prompt;
+ u_int flags;
+{
+ /* State of the "[^0]^D" sequences. */
+ enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st;
+ TEXT *ntp, *tp, ait; /* Input and autoindent text structures. */
+ CH ikey; /* Input character. */
+ size_t col; /* 0-N: screen column. */
+ size_t cnt;
+ int rval, istty;
+
+ /*
+ * !!!
+ * Most of the special capabilities (like autoindent, erase,
+ * etc.) are turned off if ex isn't talking to a terminal.
+ */
+ istty = F_ISSET(sp->gp, G_STDIN_TTY);
+
+ /*
+ * Get a TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.)
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, NULL, 0, 32)) == NULL)
+ return (INP_ERR);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ if (istty) {
+ /* Display the prompt. */
+ if (LF_ISSET(TXT_PROMPT)) {
+ col = KEY_LEN(sp, prompt);
+ (void)printf("%s", KEY_NAME(sp, prompt));
+ }
+
+ /* Initialize autoindent value and print it out. */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ if (txt_auto(sp, ep, sp->lno, NULL, 0, tp))
+ return (INP_ERR);
+ BINC_GOTO(sp, tp->wd, tp->wd_len, tp->len + 1);
+ for (cnt = 0; cnt < tp->ai; ++cnt)
+ txt_display(sp, tp, cnt, &col);
+ }
+ } else {
+ col = 0;
+
+ /* Turn off autoindent here, less special casing below. */
+ LF_CLR(TXT_AUTOINDENT);
+ }
+
+ for (carat_st = C_NOTSET;;) {
+ if (istty)
+ (void)fflush(stdout);
+ /*
+ * !!!
+ * Historically, ex never mapped commands or keys.
+ */
+ if (rval = term_key(sp, &ikey, 0))
+ return (rval);
+
+ if (INTERRUPTED(sp))
+ return (INP_INTR);
+
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+ BINC_GOTO(sp, tp->wd, tp->wd_len, tp->len + 1);
+
+ switch (ikey.value) {
+ case K_CR:
+ case K_NL:
+ /* '\' can escape <carriage-return>/<newline>. */
+ if (LF_ISSET(TXT_BACKSLASH) &&
+ tp->len != 0 && tp->lb[tp->len - 1] == '\\')
+ goto ins_ch;
+
+ /* Echo the newline if requested. */
+ if (istty && LF_ISSET(TXT_NLECHO)) {
+ (void)putc('\r', stdout);
+ (void)putc('\n', stdout);
+ (void)fflush(stdout);
+ }
+
+ /*
+ * CR returns from the ex command line, interrupt
+ * always returns.
+ */
+ if (LF_ISSET(TXT_CR)) {
+ /* Terminate with a nul, needed by filter. */
+ tp->lb[tp->len] = '\0';
+ return (INP_OK);
+ }
+
+ /* '.' terminates ex input modes. */
+ if (LF_ISSET(TXT_DOTTERM) &&
+ tp->len == tp->ai + 1 &&
+ tp->lb[tp->len - 1] == '.') {
+ /* Release the current TEXT. */
+ ntp = tp->q.cqe_prev;
+ CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+ tp = ntp;
+ return (INP_OK);
+ }
+
+ /*
+ * If we echoed the newline, display any accumulated
+ * error messages.
+ */
+ if (LF_ISSET(TXT_NLECHO) && sex_refresh(sp, ep))
+ return (INP_ERR);
+
+ /* Set up bookkeeping for the new line. */
+ if ((ntp = text_init(sp, NULL, 0, 32)) == NULL)
+ return (INP_ERR);
+ ntp->lno = tp->lno + 1;
+
+ /*
+ * Reset the autoindent line value. 0^D keeps the ai
+ * line from changing, ^D changes the level, even if
+ * there are no characters in the old line. Note,
+ * if using the current tp structure, use the cursor
+ * as the length, the user may have erased autoindent
+ * characters.
+ */
+ col = 0;
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ if (carat_st == C_NOCHANGE) {
+ if (txt_auto(sp, ep,
+ OOBLNO, &ait, ait.ai, ntp))
+ return (INP_ERR);
+ FREE_SPACE(sp, ait.lb, ait.lb_len);
+ } else
+ if (txt_auto(sp, ep,
+ OOBLNO, tp, tp->len, ntp))
+ return (INP_ERR);
+ carat_st = C_NOTSET;
+
+ if (ntp->ai) {
+ BINC_GOTO(sp,
+ ntp->wd, ntp->wd_len, ntp->len + 1);
+ for (cnt = 0; cnt < ntp->ai; ++cnt)
+ txt_display(sp, ntp, cnt, &col);
+ }
+ }
+ /*
+ * Swap old and new TEXT's, and insert the new TEXT
+ * into the queue.
+ */
+ tp = ntp;
+ CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
+ break;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && tp->len <= tp->ai)
+ carat_st = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && tp->len <= tp->ai)
+ carat_st = C_ZEROSET;
+ goto ins_ch;
+ case K_CNTRLD: /* Delete autoindent char. */
+ /*
+ * !!!
+ * Historically, the ^D command took (but then ignored)
+ * a count. For simplicity, we don't return it unless
+ * it's the first character entered. The check for len
+ * equal to 0 is okay, TXT_AUTOINDENT won't be set.
+ */
+ if (LF_ISSET(TXT_CNTRLD)) {
+ for (cnt = 0; cnt < tp->len; ++cnt)
+ if (!isblank(tp->lb[cnt]))
+ break;
+ if (cnt == tp->len) {
+ tp->len = 1;
+ tp->lb[0] = '\004';
+ tp->lb[1] = '\0';
+ return (INP_OK);
+ }
+ }
+
+ /*
+ * If in the first column or no characters to erase,
+ * ignore the ^D (this matches historic practice). If
+ * not doing autoindent or already inserted non-ai
+ * characters, it's a literal. The latter test is done
+ * in the switch, as the CARAT forms are N + 1, not N.
+ */
+ if (!LF_ISSET(TXT_AUTOINDENT))
+ goto ins_ch;
+ if (tp->len == 0)
+ break;
+ switch (carat_st) {
+ case C_CARATSET: /* ^^D */
+ if (tp->len > tp->ai + 1)
+ goto ins_ch;
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);
+ memmove(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat_st = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (tp->len > tp->ai + 1)
+ goto ins_ch;
+ carat_st = C_NOTSET;
+leftmargin: (void)printf("\b \r");
+ tp->ai = tp->len = 0;
+ break;
+ case C_NOTSET: /* ^D */
+ if (tp->len > tp->ai)
+ goto ins_ch;
+ if (txt_outdent(sp, tp))
+ return (INP_ERR);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case K_VERASE:
+ if (!istty)
+ goto ins_ch;
+ if (tp->len) {
+ --tp->len;
+ if (tp->lb[tp->len] == '\n' ||
+ tp->lb[tp->len] == '\r')
+ goto repaint;
+ ERASECH;
+ }
+ break;
+ case K_VWERASE:
+ if (!istty)
+ goto ins_ch;
+
+ /* Move to the last non-space character. */
+ while (tp->len) {
+ --tp->len;
+ if (tp->lb[tp->len] == '\n' ||
+ tp->lb[tp->len] == '\r')
+ goto repaint;
+ if (!isblank(tp->lb[tp->len])) {
+ ++tp->len;
+ break;
+ } else
+ ERASECH;
+ }
+
+ /* Move to the last space character. */
+ while (tp->len) {
+ --tp->len;
+ if (tp->lb[tp->len] == '\n' ||
+ tp->lb[tp->len] == '\r')
+ goto repaint;
+ if (isblank(tp->lb[tp->len])) {
+ ++tp->len;
+ break;
+ } else
+ ERASECH;
+ }
+ break;
+ case K_VKILL:
+ if (!istty)
+ goto ins_ch;
+ while (tp->len) {
+ --tp->len;
+ if (tp->lb[tp->len] == '\n' ||
+ tp->lb[tp->len] == '\r') {
+ tp->len = 0;
+ goto repaint;
+ }
+ ERASECH;
+ }
+ break;
+ /*
+ * XXX
+ * Historic practice is that ^Z suspended command mode, and
+ * that it was unaffected by the autowrite option. ^Z ended
+ * insert mode, retaining all but the current line of input,
+ * which was discarded. When ex was foregrounded, it was in
+ * command mode. I don't want to discard input because a user
+ * tried to enter a ^Z, and I'd like to be consistent with vi.
+ * So, nex matches vi's historic practice, and doesn't permit
+ * ^Z in input mode.
+ */
+ case K_CNTRLZ:
+ if (!istty || !LF_ISSET(TXT_EXSUSPEND))
+ goto ins_ch;
+ sex_suspend(sp);
+ goto repaint;
+ case K_CNTRLR:
+ if (!istty)
+ goto ins_ch;
+repaint: if (LF_ISSET(TXT_PROMPT)) {
+ col = KEY_LEN(sp, prompt);
+ (void)printf("\r%s", KEY_NAME(sp, prompt));
+ } else {
+ col = 0;
+ (void)putc('\r', stdout);
+ }
+ for (cnt = 0; cnt < tp->len; ++cnt)
+ txt_display(sp, tp, cnt, &col);
+ break;
+ default:
+ /*
+ * See the TXT_BEAUTIFY comment in vi/v_ntext.c.
+ *
+ * Silently eliminate any iscntrl() character that
+ * wasn't already handled specially, except for <tab>
+ * and <ff>.
+ */
+ins_ch: if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ikey.ch) &&
+ ikey.value != K_FORMFEED && ikey.value != K_TAB)
+ break;
+ tp->lb[tp->len] = ikey.ch;
+ if (istty)
+ txt_display(sp, tp, tp->len, &col);
+ ++tp->len;
+ break;
+ }
+ }
+ /* NOTREACHED */
+
+binc_err:
+ return (INP_ERR);
+}
+
+/*
+ * txt_display --
+ * Display the character.
+ */
+static void
+txt_display(sp, tp, off, colp)
+ SCR *sp;
+ TEXT *tp;
+ size_t off, *colp;
+{
+ CHAR_T ch;
+ size_t width;
+
+ switch (ch = tp->lb[off]) {
+ case '\t':
+ *colp += tp->wd[off] = width =
+ O_VAL(sp, O_TABSTOP) - *colp % O_VAL(sp, O_TABSTOP);
+ while (width--)
+ putc(' ', stdout);
+ break;
+ case '\n':
+ case '\r':
+ (void)putc('\r', stdout);
+ (void)putc('\n', stdout);
+ break;
+ default:
+ *colp += tp->wd[off] = KEY_LEN(sp, ch);
+ (void)printf("%s", KEY_NAME(sp, ch));
+ }
+}
+
+/*
+ * txt_outdent --
+ * Handle ^D outdents.
+ *
+ * Ex version of vi/v_ntext.c:txt_outdent(). See that code for the
+ * usual ranting and raving.
+ */
+static int
+txt_outdent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, cnt, off, scno, spaces;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < tp->len; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Get the previous shiftwidth column. */
+ for (cno = scno; --scno % sw != 0;);
+
+ /* Decrement characters until less than or equal to that slot. */
+ for (; cno > scno; --tp->ai) {
+ for (cnt = tp->wd[--tp->len]; cnt-- > 0;)
+ (void)printf("\b \b");
+ if (tp->lb[--off] == '\t')
+ cno -= STOP_OFF(cno, ts);
+ else
+ --cno;
+ }
+
+ /* Spaces needed to get to the target. */
+ spaces = scno - cno;
+
+ /* Maybe just a delete. */
+ if (spaces == 0)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces);
+
+ /* Maybe that was enough. */
+ if (spaces == 0)
+ return (0);
+
+ /* Add new space characters. */
+ for (; spaces--; ++tp->ai)
+ tp->lb[tp->len++] = ' ';
+ return (0);
+}
diff --git a/usr.bin/vi/sex/sex_refresh.c b/usr.bin/vi/sex/sex_refresh.c
new file mode 100644
index 0000000..d665268
--- /dev/null
+++ b/usr.bin/vi/sex/sex_refresh.c
@@ -0,0 +1,140 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_refresh.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "sex_screen.h"
+
+#ifndef SYSV_CURSES
+#define A_NORMAL 1
+#define A_STANDOUT 2
+#define vidattr(attr) Xvidattr(sp, attr)
+
+static int Xvidattr __P((SCR *, int));
+#endif
+
+/*
+ * sex_refresh --
+ * In ex, just display any messages.
+ */
+int
+sex_refresh(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MSG *mp;
+
+ /* Check for screen resize. */
+ if (F_ISSET(sp, S_RESIZE)) {
+ sp->rows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ F_CLR(sp, S_RESIZE);
+ }
+
+ /* Ring the bell. */
+ if (F_ISSET(sp, S_BELLSCHED)) {
+ sex_bell(sp);
+ F_CLR(sp, S_BELLSCHED);
+ }
+
+ /* Display messages. */
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !(F_ISSET(mp, M_EMPTY)); mp = mp->q.le_next) {
+ if (F_ISSET(mp, M_INV_VIDEO) &&
+ vidattr(A_STANDOUT) == ERR && O_ISSET(sp, O_ERRORBELLS))
+ (void)printf("\07");
+ (void)printf("%.*s.\n", (int)mp->len, mp->mbuf);
+ F_SET(mp, M_EMPTY);
+
+ if (F_ISSET(mp, M_INV_VIDEO))
+ vidattr(A_NORMAL);
+ (void)fflush(stdout);
+ }
+ return (0);
+}
+
+#ifndef SYSV_CURSES
+/*
+ * Xvidattr --
+ * Set the video attributes to a value.
+ *
+ * XXX
+ * Just enough to make the above code work when using non-System V
+ * curses.
+ */
+static int
+Xvidattr(sp, attr)
+ SCR *sp;
+ int attr;
+{
+ SEX_PRIVATE *sxp;
+
+ sxp = SXP(sp);
+
+ /* Check to see if standout isn't available. */
+ if (sxp->SO == NULL)
+ return (ERR);
+
+ switch (attr) {
+ case A_NORMAL:
+ (void)tputs(SXP(sp)->SE, 1, vi_putchar);
+ break;
+ case A_STANDOUT:
+ (void)tputs(SXP(sp)->SO, 1, vi_putchar);
+ break;
+ default:
+ abort();
+ }
+ return (0);
+}
+#endif
diff --git a/usr.bin/vi/sex/sex_screen.c b/usr.bin/vi/sex/sex_screen.c
new file mode 100644
index 0000000..606ff5e
--- /dev/null
+++ b/usr.bin/vi/sex/sex_screen.c
@@ -0,0 +1,340 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_screen.c 8.49 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+#include "../svi/svi_screen.h"
+
+static void sex_abort __P((void));
+static int sex_noop __P((void));
+static int sex_nope __P((SCR *));
+static int sex_term_init __P((SCR *));
+static void so_se_init __P((SCR *));
+
+/*
+ * sex_screen_init --
+ * Initialize the ex screen.
+ */
+int
+sex_screen_init(sp)
+ SCR *sp;
+{
+ /* Initialize support routines. */
+ sp->s_bell = sex_bell;
+ sp->s_bg = (int (*)())sex_nope;
+ sp->s_busy = (int (*)())sex_busy;
+ sp->s_change = (int (*)())sex_noop;
+ sp->s_clear = (int (*)())sex_noop;
+ sp->s_colpos = (size_t (*)())sex_abort;
+ sp->s_column = (int (*)())sex_abort;
+ sp->s_confirm = sex_confirm;
+ sp->s_crel = (int (*)())sex_nope;
+ sp->s_edit = sex_screen_edit;
+ sp->s_end = (int (*)())sex_noop;
+ sp->s_ex_cmd = (int (*)())sex_abort;
+ sp->s_ex_run = (int (*)())sex_abort;
+ sp->s_ex_write = (int (*)())sex_abort;
+ sp->s_fg = (int (*)())sex_nope;
+ sp->s_fill = (int (*)())sex_abort;
+ sp->s_get = sex_get;
+ sp->s_key_read = sex_key_read;
+ sp->s_optchange = sex_optchange;
+ sp->s_fmap = (int (*)())sex_noop;
+ sp->s_position = (int (*)())sex_abort;
+ sp->s_rabs = (int (*)())sex_nope;
+ sp->s_rcm = (size_t (*)())sex_abort;
+ sp->s_refresh = sex_refresh;
+ sp->s_scroll = (int (*)())sex_abort;
+ sp->s_split = (int (*)())sex_nope;
+ sp->s_suspend = sex_suspend;
+ sp->s_window = sex_window;
+
+ return (0);
+}
+
+/*
+ * sex_screen_copy --
+ * Copy to a new screen.
+ */
+int
+sex_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ SEX_PRIVATE *osex, *nsex;
+
+ /* Create the private screen structure. */
+ CALLOC_RET(orig, nsex, SEX_PRIVATE *, 1, sizeof(SEX_PRIVATE));
+ sp->sex_private = nsex;
+
+/* INITIALIZED AT SCREEN CREATE. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ } else {
+ osex = SXP(orig);
+#ifndef SYSV_CURSES
+ if (osex->SE != NULL && (nsex->SE = strdup(osex->SE)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+ if (osex->SO != NULL && (nsex->SO = strdup(osex->SO)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ free(osex->SE);
+ return (1);
+ }
+#endif
+ }
+
+ return (0);
+}
+
+/*
+ * sex_screen_end --
+ * End a screen.
+ */
+int
+sex_screen_end(sp)
+ SCR *sp;
+{
+#ifndef SYSV_CURSES
+ /* Free inverse video strings. */
+ if (SXP(sp)->SE != NULL)
+ free(SXP(sp)->SE);
+ if (SXP(sp)->SO != NULL)
+ free(SXP(sp)->SO);
+#endif
+
+ /* Free private memory. */
+ FREE(SXP(sp), sizeof(SEX_PRIVATE));
+ sp->sex_private = NULL;
+
+ return (0);
+}
+
+/*
+ * sex_screen_edit --
+ * Main ex screen loop. The ex screen is relatively uncomplicated.
+ * As long as it has a stdio FILE pointer for output, it's happy.
+ */
+int
+sex_screen_edit(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct termios t;
+ GS *gp;
+ int force, rval;
+
+ /* Initialize the terminal state. */
+ gp = sp->gp;
+ if (F_ISSET(gp, G_STDIN_TTY))
+ SEX_RAW(t);
+
+ /* Write to the terminal. */
+ sp->stdfp = stdout;
+
+ /*
+ * The resize bit is probably set, but clear it, we're
+ * going to initialize the screen right now.
+ */
+ F_CLR(sp, S_RESIZE);
+
+ /* Initialize the termcap buffer. */
+ if (sex_term_init(sp))
+ return (1);
+
+ for (;;) {
+ /*
+ * Run ex. If ex fails, sex data structures
+ * may be corrupted, be careful what you do.
+ */
+ if (rval = ex(sp, sp->ep)) {
+ (void)rcv_sync(sp, sp->ep,
+ RCV_EMAIL | RCV_ENDSESSION | RCV_PRESERVE);
+ (void)screen_end(sp); /* General SCR info. */
+ break;
+ }
+
+ force = 0;
+ switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+ case S_EXIT_FORCE:
+ force = 1;
+ /* FALLTHROUGH */
+ case S_EXIT:
+ F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+ if (file_end(sp, sp->ep, force))
+ break;
+ (void)screen_end(sp); /* General SCR info. */
+ goto ret;
+ case 0: /* Changing from ex mode. */
+ goto ret;
+ case S_FSWITCH:
+ F_CLR(sp, S_FSWITCH);
+ break;
+ case S_SSWITCH:
+ default:
+ abort();
+ }
+ }
+
+ /* Reset the terminal state. */
+ret: if (F_ISSET(gp, G_STDIN_TTY) && SEX_NORAW(t))
+ rval = 1;
+ return (rval);
+}
+
+/*
+ * sex_term_init --
+ * Initialize ex's relationship with the termcap/terminfo entry.
+ */
+static int
+sex_term_init(sp)
+ SCR *sp;
+{
+
+#ifndef SYSV_CURSES
+ /* Initialize standout information. */
+ so_se_init(sp);
+#endif
+
+ sp->rows = O_VAL(sp, O_LINES);
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ return (0);
+}
+
+#ifndef SYSV_CURSES
+/*
+ * so_se_init --
+ * Initialize the inverse video strings.
+ */
+static void
+so_se_init(sp)
+ SCR *sp;
+{
+ SEX_PRIVATE *sxp;
+ size_t len;
+ char *s, *t, buf[128], tbuf[2048];
+
+ if (tgetent(tbuf, O_STR(sp, O_TERM)) != 1)
+ return;
+
+ sxp = SXP(sp);
+
+ /* Get SE. */
+ t = buf;
+ if ((t = tgetstr("se", &t)) == NULL)
+ return;
+ if ((len = strlen(t)) == 0)
+ return;
+ MALLOC_NOMSG(sp, s, char *, len + 1);
+ if (s == NULL)
+ return;
+ memmove(s, buf, len);
+ s[len] = '\0';
+ sxp->SE = s;
+
+ /* Get SO. */
+ t = buf;
+ if ((t = tgetstr("so", &t)) == NULL)
+ goto err;
+ if ((len = strlen(t)) == 0)
+ goto err;
+ MALLOC_NOMSG(sp, s, char *, len + 1);
+ if (s == NULL)
+ goto err;
+ memmove(s, buf, len);
+ s[len] = '\0';
+ sxp->SO = s;
+
+ return;
+
+err: free(sxp->SE);
+ sxp->SE = NULL;
+ return;
+}
+#endif
+
+/*
+ * sex_abort --
+ * Fake function. Die.
+ */
+static void
+sex_abort()
+{
+ abort();
+}
+
+/*
+ * sex_noop --
+ * Fake function. Do nothing.
+ */
+static int
+sex_noop()
+{
+ return (0);
+}
+
+/*
+ * sex_nope --
+ * Fake function. Not in ex, you don't.
+ */
+static int
+sex_nope(sp)
+ SCR *sp;
+{
+ msgq(sp, M_ERR, "Command not applicable to ex mode");
+ return (1);
+}
diff --git a/usr.bin/vi/sex/sex_screen.h b/usr.bin/vi/sex/sex_screen.h
new file mode 100644
index 0000000..150e38f
--- /dev/null
+++ b/usr.bin/vi/sex/sex_screen.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sex_screen.h 8.20 (Berkeley) 8/8/94
+ */
+
+#define SEX_NORAW(t) \
+ tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &(t))
+
+#define SEX_RAW(t) { \
+ struct termios __rawt; \
+ if (tcgetattr(STDIN_FILENO, &(t))) \
+ return (1); \
+ __rawt = (t); \
+ __rawt.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|INLCR|IGNCR|ICRNL); \
+ __rawt.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); \
+ __rawt.c_oflag |= OPOST | ONLCR; \
+ __rawt.c_cc[VMIN] = 1; \
+ __rawt.c_cc[VTIME] = 0; \
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN | TCSASOFT, &__rawt)) \
+ return (1); \
+}
+
+typedef struct _sex_private {
+/* INITIALIZED AT SCREEN CREATE. */
+ int __unused; /* Make sure it's not empty. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+#ifndef SYSV_CURSES
+ char *SE, *SO; /* Inverse video termcap strings. */
+#endif
+} SEX_PRIVATE;
+
+#define SXP(sp) ((SEX_PRIVATE *)((sp)->sex_private))
+
+void sex_bell __P((SCR *));
+void sex_busy __P((SCR *, char const *));
+enum confirm
+ sex_confirm __P((SCR *, EXF *, MARK *, MARK *));
+enum input
+ sex_get __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
+enum input
+ sex_key_read __P((SCR *, int *, struct timeval *));
+int sex_optchange __P((SCR *, int));
+int sex_refresh __P((SCR *, EXF *));
+int sex_screen_copy __P((SCR *, SCR *));
+int sex_screen_edit __P((SCR *, EXF *));
+int sex_screen_end __P((SCR *));
+int sex_suspend __P((SCR *));
+int sex_window __P((SCR *, int));
diff --git a/usr.bin/vi/sex/sex_term.c b/usr.bin/vi/sex/sex_term.c
new file mode 100644
index 0000000..1eaf34b
--- /dev/null
+++ b/usr.bin/vi/sex/sex_term.c
@@ -0,0 +1,217 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_term.c 8.40 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "../ex/script.h"
+
+/*
+ * sex_key_read --
+ * Read characters from the input.
+ */
+enum input
+sex_key_read(sp, nrp, timeout)
+ SCR *sp;
+ int *nrp;
+ struct timeval *timeout;
+{
+ struct timeval t, *tp;
+ GS *gp;
+ IBUF *tty;
+ int maxfd, nr;
+
+ *nrp = 0;
+ gp = sp->gp;
+ tty = gp->tty;
+
+ /*
+ * We're about to block; check for signals. If a signal received,
+ * clear it immediately, so that if it's reset while being serviced
+ * we won't miss it.
+ *
+ * These signal recipients set global flags. None of this has
+ * anything to do with input keys, but it's something that can't
+ * be done asynchronously without adding locking to handle race
+ * conditions, and which needs to be done periodically.
+ */
+sigchk: while (F_ISSET(gp, G_SIGINT | G_SIGWINCH)) {
+ if (F_ISSET(gp, G_SIGINT))
+ return (INP_INTR);
+ if (F_ISSET(gp, G_SIGWINCH)) {
+ F_CLR(gp, G_SIGWINCH);
+ if (!sp->s_window(sp, 1))
+ (void)sp->s_refresh(sp, sp->ep);
+ }
+ }
+
+ /*
+ * There are three cases here:
+ *
+ * 1: A read from a file or a pipe. In this case, the reads
+ * never timeout regardless. This means that we can hang
+ * when trying to complete a map, but we're going to hang
+ * on the next read anyway.
+ */
+ if (!F_ISSET(gp, G_STDIN_TTY)) {
+ if ((nr = read(STDIN_FILENO,
+ tty->ch + tty->next + tty->cnt,
+ tty->nelem - (tty->next + tty->cnt))) > 0)
+ goto success;
+ return (INP_EOF);
+ }
+
+ /*
+ * 2: A read with an associated timeout. In this case, we are trying
+ * to complete a map sequence. Ignore script windows and timeout
+ * as specified. If input arrives, we fall into #3, but because
+ * timeout isn't NULL, don't read anything but command input.
+ *
+ * If interrupted, go back and check to see what it was.
+ */
+ if (timeout != NULL) {
+ if (F_ISSET(sp, S_SCRIPT))
+ FD_CLR(sp->script->sh_master, &sp->rdfd);
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ for (;;) {
+ switch (select(STDIN_FILENO + 1,
+ &sp->rdfd, NULL, NULL, timeout)) {
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+ goto err;
+ case 1: /* Characters ready. */
+ break;
+ case 0: /* Timeout. */
+ return (INP_OK);
+ }
+ break;
+ }
+ }
+
+ /*
+ * 3: At this point, we'll take anything that comes. Select on the
+ * command file descriptor and the file descriptor for the script
+ * window if there is one. Poll the fd's, increasing the timeout
+ * each time each time we don't get anything until we're blocked
+ * on I/O.
+ *
+ * If interrupted, go back and check to see what it was.
+ */
+ for (t.tv_sec = t.tv_usec = 0;;) {
+ /*
+ * Reset each time -- sscr_input() may call other
+ * routines which could reset bits.
+ */
+ if (timeout == NULL && F_ISSET(sp, S_SCRIPT)) {
+ tp = &t;
+
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ if (F_ISSET(sp, S_SCRIPT)) {
+ FD_SET(sp->script->sh_master, &sp->rdfd);
+ maxfd =
+ MAX(STDIN_FILENO, sp->script->sh_master);
+ } else
+ maxfd = STDIN_FILENO;
+ } else {
+ tp = NULL;
+
+ FD_SET(STDIN_FILENO, &sp->rdfd);
+ if (F_ISSET(sp, S_SCRIPT))
+ FD_CLR(sp->script->sh_master, &sp->rdfd);
+ maxfd = STDIN_FILENO;
+ }
+
+ switch (select(maxfd + 1, &sp->rdfd, NULL, NULL, tp)) {
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+err: msgq(sp, M_SYSERR, "select");
+ return (INP_ERR);
+ case 0: /* Timeout. */
+ if (t.tv_usec) {
+ ++t.tv_sec;
+ t.tv_usec = 0;
+ } else
+ t.tv_usec += 500000;
+ continue;
+ }
+
+ if (timeout == NULL && F_ISSET(sp, S_SCRIPT) &&
+ FD_ISSET(sp->script->sh_master, &sp->rdfd)) {
+ sscr_input(sp);
+ continue;
+ }
+
+ switch (nr = read(STDIN_FILENO,
+ tty->ch + tty->next + tty->cnt,
+ tty->nelem - (tty->next + tty->cnt))) {
+ case 0: /* EOF. */
+ return (INP_EOF);
+ case -1: /* Error or interrupt. */
+ if (errno == EINTR)
+ goto sigchk;
+ msgq(sp, M_SYSERR, "read");
+ return (INP_ERR);
+ default:
+ goto success;
+ }
+ /* NOTREACHED */
+ }
+
+success:
+ MEMSET(tty->chf + tty->next + tty->cnt, 0, nr);
+ tty->cnt += *nrp = nr;
+ return (INP_OK);
+}
diff --git a/usr.bin/vi/sex/sex_util.c b/usr.bin/vi/sex/sex_util.c
new file mode 100644
index 0000000..482c358
--- /dev/null
+++ b/usr.bin/vi/sex/sex_util.c
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_util.c 8.17 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "sex_screen.h"
+
+/*
+ * sex_bell --
+ * Ring the bell.
+ */
+void
+sex_bell(sp)
+ SCR *sp;
+{
+ (void)write(STDOUT_FILENO, "\07", 1); /* \a */
+}
+
+void
+sex_busy(sp, msg)
+ SCR *sp;
+ char const *msg;
+{
+ (void)fprintf(stdout, "%s\n", msg);
+ (void)fflush(stdout);
+}
+
+/*
+ * sex_optchange --
+ * Screen specific "option changed" routine.
+ */
+int
+sex_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_TERM:
+ /* Reset the screen size. */
+ if (sp->s_window(sp, 0))
+ return (1);
+ F_SET(sp, S_RESIZE);
+ break;
+ }
+
+ (void)ex_optchange(sp, opt);
+
+ return (0);
+}
+
+/*
+ * sex_suspend --
+ * Suspend an ex screen.
+ */
+int
+sex_suspend(sp)
+ SCR *sp;
+{
+ struct termios t;
+ GS *gp;
+ int rval;
+
+ rval = 0;
+
+ /* Save current terminal settings, and restore the original ones. */
+ gp = sp->gp;
+ if (F_ISSET(gp, G_STDIN_TTY)) {
+ if (tcgetattr(STDIN_FILENO, &t)) {
+ msgq(sp, M_SYSERR, "suspend: tcgetattr");
+ return (1);
+ }
+ if (F_ISSET(gp, G_TERMIOS_SET) && tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &gp->original_termios)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr original");
+ return (1);
+ }
+ }
+
+ /* Push out any waiting messages. */
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Stop the process group. */
+ if (kill(0, SIGTSTP)) {
+ msgq(sp, M_SYSERR, "suspend: kill");
+ rval = 1;
+ }
+
+ /* Time passes ... */
+
+ /* Restore current terminal settings. */
+ if (F_ISSET(gp, G_STDIN_TTY) &&
+ tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr current");
+ rval = 1;
+ }
+ return (rval);
+}
diff --git a/usr.bin/vi/sex/sex_window.c b/usr.bin/vi/sex/sex_window.c
new file mode 100644
index 0000000..61000fb
--- /dev/null
+++ b/usr.bin/vi/sex/sex_window.c
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sex_window.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * sex_window --
+ * Set the window size.
+ */
+int
+sex_window(sp, sigwinch)
+ SCR *sp;
+ int sigwinch;
+{
+ struct winsize win;
+ size_t col, row;
+ int rval, user_set;
+ ARGS *argv[2], a, b;
+ char *s, buf[2048];
+
+ /*
+ * Get the screen rows and columns. If the values are wrong, it's
+ * not a big deal -- as soon as the user sets them explicitly the
+ * environment will be set and the screen package will use the new
+ * values.
+ *
+ * Try TIOCGWINSZ.
+ */
+ errno = row = col = 0;
+#ifdef TIOCGWINSZ
+ if (ioctl(STDERR_FILENO, TIOCGWINSZ, &win) != -1) {
+ row = win.ws_row;
+ col = win.ws_col;
+ }
+#endif
+ /* If here because of a signal, TIOCGWINSZ is all we trust. */
+ if (sigwinch) {
+ if (row == 0 || col == 0) {
+ if (errno > 0)
+ msgq(sp, M_SYSERR, "TIOCGWINSZ");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * SunOS systems deliver SIGWINCH when windows are uncovered
+ * as well as when they change size. In addition, we call
+ * here when continuing after being suspended since the window
+ * may have changed size. Since we don't want to background
+ * all of the screens just because the window was uncovered,
+ * ignore the signal if there's no change.
+ */
+ if (row == O_VAL(sp, O_LINES) && col == O_VAL(sp, O_COLUMNS))
+ return (1);
+
+ goto sigw;
+ }
+
+ /*
+ * !!!
+ * If TIOCGWINSZ failed, or had entries of 0, try termcap. This
+ * routine is called before any termcap or terminal information
+ * has been set up. If there's no TERM environmental variable set,
+ * let it go, at least ex can run.
+ */
+ if (row == 0 || col == 0) {
+ if ((s = getenv("TERM")) == NULL)
+ goto noterm;
+#ifdef SYSV_CURSES
+ if (row == 0)
+ if ((rval = tigetnum("lines")) < 0)
+ msgq(sp, M_SYSERR, "tigetnum: lines");
+ else
+ row = rval;
+ if (col == 0)
+ if ((rval = tigetnum("cols")) < 0)
+ msgq(sp, M_SYSERR, "tigetnum: cols");
+ else
+ col = rval;
+#else
+ switch (tgetent(buf, s)) {
+ case -1:
+ msgq(sp, M_SYSERR, "tgetent: %s", s);
+ return (1);
+ case 0:
+ msgq(sp, M_ERR, "%s: unknown terminal type", s);
+ return (1);
+ }
+ if (row == 0)
+ if ((rval = tgetnum("li")) < 0)
+ msgq(sp, M_ERR,
+ "no \"li\" capability for %s", s);
+ else
+ row = rval;
+ if (col == 0)
+ if ((rval = tgetnum("co")) < 0)
+ msgq(sp, M_ERR,
+ "no \"co\" capability for %s", s);
+ else
+ col = rval;
+#endif
+ }
+
+ /* If nothing else, well, it's probably a VT100. */
+noterm: if (row == 0)
+ row = 24;
+ if (col == 0)
+ col = 80;
+
+ /* POSIX 1003.2 requires the environment to override. */
+ if ((s = getenv("LINES")) != NULL)
+ row = strtol(s, NULL, 10);
+ if ((s = getenv("COLUMNS")) != NULL)
+ col = strtol(s, NULL, 10);
+
+sigw: a.bp = buf;
+ b.bp = NULL;
+ b.len = 0;
+ argv[0] = &a;
+ argv[1] = &b;;
+
+ /*
+ * Tell the options code that the screen size has changed.
+ * Since the user didn't do the set, clear the set bits.
+ */
+ user_set = F_ISSET(&sp->opts[O_LINES], OPT_SET);
+ a.len = snprintf(buf, sizeof(buf), "lines=%u", row);
+ if (opts_set(sp, NULL, argv))
+ return (1);
+ if (user_set)
+ F_CLR(&sp->opts[O_LINES], OPT_SET);
+
+ user_set = F_ISSET(&sp->opts[O_COLUMNS], OPT_SET);
+ a.len = snprintf(buf, sizeof(buf), "columns=%u", col);
+ if (opts_set(sp, NULL, argv))
+ return (1);
+ if (user_set)
+ F_CLR(&sp->opts[O_COLUMNS], OPT_SET);
+
+ F_SET(sp, S_RESIZE);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_confirm.c b/usr.bin/vi/svi/svi_confirm.c
new file mode 100644
index 0000000..e3f703e
--- /dev/null
+++ b/usr.bin/vi/svi/svi_confirm.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_confirm.c 8.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+enum confirm
+svi_confirm(sp, ep, fp, tp)
+ SCR *sp;
+ EXF *ep;
+ MARK *fp, *tp;
+{
+ CH ikey;
+ size_t oldy, oldx;
+
+ /*
+ * Refresh the cursor first -- this means that we won't have to
+ * set S_UPDATE_MODE to keep refresh from erasing the mode line
+ * or SVI_CUR_INVALID because we sneaked the cursor off somewhere
+ * else.
+ */
+ sp->lno = fp->lno;
+ sp->cno = fp->cno;
+ if (svi_paint(sp, ep))
+ return (CONF_QUIT);
+
+ getyx(stdscr, oldy, oldx);
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ ADDNSTR(STR_CONFIRM, sizeof(STR_CONFIRM) - 1);
+ MOVEA(sp, oldy, oldx);
+ refresh();
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (CONF_QUIT);
+ switch (ikey.ch) {
+ case CH_YES:
+ return (CONF_YES);
+ case CH_QUIT:
+ return (CONF_QUIT);
+ default:
+ case CH_NO:
+ return (CONF_NO);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/svi/svi_curses.c b/usr.bin/vi/svi/svi_curses.c
new file mode 100644
index 0000000..6fec895
--- /dev/null
+++ b/usr.bin/vi/svi/svi_curses.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_curses.c 8.5 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_curses_init --
+ * Initialize curses.
+ */
+int
+svi_curses_init(sp)
+ SCR *sp;
+{
+ struct termios t;
+ char *p;
+
+#ifdef SYSV_CURSES
+ /*
+ * The SunOS/System V initscr() isn't reentrant. Don't even think
+ * about trying to use it. It fails in subtle ways (e.g. select(2)
+ * on fileno(stdin) stops working). We don't care about the SCREEN
+ * reference returned by newterm, we never have more than one SCREEN
+ * at a time.
+ */
+ errno = 0;
+ if (newterm(O_STR(sp, O_TERM), stdout, stdin) == NULL) {
+ msgq(sp, errno ? M_SYSERR : M_ERR, "newterm failed");
+ return (1);
+ }
+#else
+ /*
+ * Initscr() doesn't provide useful error values or messages. The
+ * reasonable guess is that either malloc failed or the terminal was
+ * unknown or lacking some essential feature. Try and guess so the
+ * user isn't even more pissed off because of the error message.
+ */
+ errno = 0;
+ if (initscr() == NULL) {
+ char kbuf[2048];
+ msgq(sp, errno ? M_SYSERR : M_ERR, "initscr failed");
+ if ((p = getenv("TERM")) == NULL || !strcmp(p, "unknown"))
+ msgq(sp, M_ERR,
+ "No TERM environment variable set, or TERM set to \"unknown\"");
+ else if (tgetent(kbuf, p) != 1)
+ msgq(sp, M_ERR,
+"%s: unknown terminal type, or terminal lacks necessary features", p);
+ else
+ msgq(sp, M_ERR,
+ "%s: terminal type lacks necessary features", p);
+ return (1);
+ }
+#endif
+ /*
+ * We use raw mode. What we want is 8-bit clean, however, signals
+ * and flow control should continue to work. Admittedly, it sounds
+ * like cbreak, but it isn't. Using cbreak() can get you additional
+ * things like IEXTEN, which turns on things like DISCARD and LNEXT.
+ *
+ * !!!
+ * If raw isn't turning off echo and newlines, something's wrong.
+ * However, it doesn't hurt.
+ */
+ noecho(); /* No character echo. */
+ nonl(); /* No CR/NL translation. */
+ raw(); /* 8-bit clean. */
+ idlok(stdscr, 1); /* Use hardware insert/delete line. */
+
+ /*
+ * XXX
+ * Historic implementations of curses handled SIGTSTP signals
+ * in one of three ways. They either:
+ *
+ * 1: Set their own handler, regardless.
+ * 2: Did not set a handler if a handler was already installed.
+ * 3: Set their own handler, but then called any previously set
+ * handler after completing their own cleanup.
+ *
+ * We don't try and figure out which behavior is in place, we
+ * just set it to SIG_DFL after initializing the curses interface.
+ */
+ (void)signal(SIGTSTP, SIG_DFL);
+
+ /*
+ * If flow control was on, turn it back on. Turn signals on. ISIG
+ * turns on VINTR, VQUIT, VDSUSP and VSUSP. See signal.c:sig_init()
+ * for a discussion of what's going on here. To sum up, sig_init()
+ * already installed a handler for VINTR. We're going to disable the
+ * other three.
+ *
+ * XXX
+ * We want to use ^Y as a vi scrolling command. If the user has the
+ * DSUSP character set to ^Y (common practice) clean it up. As it's
+ * equally possible that the user has VDSUSP set to 'a', we disable
+ * it regardless. It doesn't make much sense to suspend vi at read,
+ * so I don't think anyone will care. Alternatively, we could look
+ * it up in the table of legal command characters and turn it off if
+ * it matches one. VDSUSP wasn't in POSIX 1003.1-1990, so we test for
+ * it.
+ *
+ * XXX
+ * We don't check to see if the user had signals enabled to start with.
+ * If they didn't, it's unclear what we're supposed to do here, but it
+ * is also pretty unlikely.
+ */
+ if (!tcgetattr(STDIN_FILENO, &t)) {
+ if (sp->gp->original_termios.c_iflag & IXON)
+ t.c_iflag |= IXON;
+ if (sp->gp->original_termios.c_iflag & IXOFF)
+ t.c_iflag |= IXOFF;
+
+ t.c_lflag |= ISIG;
+#ifdef VDSUSP
+ t.c_cc[VDSUSP] = _POSIX_VDISABLE;
+#endif
+ t.c_cc[VQUIT] = _POSIX_VDISABLE;
+ t.c_cc[VSUSP] = _POSIX_VDISABLE;
+
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+ }
+
+ /* Put the cursor keys into application mode. */
+ svi_keypad(sp, 1);
+
+ /*
+ * The first screen in the list gets it all. All other screens
+ * are hidden and lose their maps.
+ */
+ svi_dtoh(sp, "Window resize");
+
+ /* Initialize terminal values. */
+ SVP(sp)->srows = O_VAL(sp, O_LINES);
+
+ /*
+ * Initialize screen values.
+ *
+ * Small windows: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * Setup:
+ * t_minrows is the minimum rows to display
+ * t_maxrows is the maximum rows to display (rows - 1)
+ * t_rows is the rows currently being displayed
+ */
+ sp->rows = SVP(sp)->srows;
+ sp->cols = O_VAL(sp, O_COLUMNS);
+ sp->woff = 0;
+ sp->t_rows = sp->t_minrows = O_VAL(sp, O_WINDOW);
+ if (sp->t_rows > sp->rows - 1) {
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+ msgq(sp, M_INFO,
+ "Windows option value is too large, max is %u", sp->t_rows);
+ }
+ sp->t_maxrows = sp->rows - 1;
+
+ /* Create the screen map. */
+ CALLOC(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ if (HMAP == NULL) {
+ if (endwin() == ERR)
+ msgq(sp, M_SYSERR, "endwin");
+ return (1);
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ F_SET(SVP(sp), SVI_CUR_INVALID); /* Cursor is invalid. */
+ F_SET(SVP(sp), SVI_CURSES_INIT); /* It's initialized. */
+
+ return (0);
+}
+
+/*
+ * svi_curses_end --
+ * Move to the bottom of the screen, end curses.
+ */
+int
+svi_curses_end(sp)
+ SCR *sp;
+{
+ /*
+ * XXX
+ * By the time we get here, the screen private area (SVI_PRIVATE)
+ * is probably gone. Don't use it, and don't call any routines
+ * that do.
+ *
+ * Restore the cursor keys to normal mode.
+ */
+ svi_keypad(sp, 0);
+
+ /* Move to the bottom of the screen. */
+ if (move(INFOLINE(sp), 0) == OK) {
+ clrtoeol();
+ refresh();
+ }
+
+ /* End curses window. */
+ if (endwin() == ERR)
+ msgq(sp, M_SYSERR, "endwin");
+
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_ex.c b/usr.bin/vi/svi/svi_ex.c
new file mode 100644
index 0000000..0d20b01
--- /dev/null
+++ b/usr.bin/vi/svi/svi_ex.c
@@ -0,0 +1,650 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_ex.c 8.56 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int svi_ex_divider __P((SCR *));
+static int svi_ex_done __P((SCR *, EXF *, MARK *));
+static int svi_ex_inv __P((SCR *));
+static int svi_ex_scroll __P((SCR *, int, CH *));
+
+#define MSGS_WAITING(sp) \
+ ((sp)->msgq.lh_first != NULL && \
+ !F_ISSET((sp)->msgq.lh_first, M_EMPTY))
+
+/*
+ * svi_ex_cmd --
+ * Execute an ex command.
+ */
+int
+svi_ex_cmd(sp, ep, exp, rp)
+ SCR *sp;
+ EXF *ep;
+ EXCMDARG *exp;
+ MARK *rp;
+{
+ SVI_PRIVATE *svp;
+ int rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ (void)svi_busy(sp, NULL);
+ rval = exp->cmd->fn(sp, ep, exp);
+
+ (void)msg_rpt(sp, 0);
+ (void)ex_fflush(EXCOOKIE);
+
+ /*
+ * If displayed anything, figure out if we have to wait. If the
+ * screen wasn't trashed, only one line output and there are no
+ * waiting messages, don't wait, but don't overwrite it with mode
+ * information either.
+ */
+ if (svp->extotalcount > 0)
+ if (!F_ISSET(sp, S_REFRESH) &&
+ svp->extotalcount == 1 && !MSGS_WAITING(sp)) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ (void)svi_ex_inv(sp);
+ } else {
+ /* This message isn't interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+ (void)svi_ex_scroll(sp, 1, NULL);
+ }
+ return (svi_ex_done(sp, ep, rp) || rval);
+}
+
+/*
+ * svi_ex_run --
+ * Execute strings of ex commands.
+ */
+int
+svi_ex_run(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ enum input (*get) __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
+ struct termios t;
+ CH ikey;
+ SVI_PRIVATE *svp;
+ TEXT *tp;
+ int flags, in_exmode, rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ /*
+ * There's some tricky stuff going on here to handle when a user has
+ * mapped a key to multiple ex commands. Historic practice was that
+ * vi ran without any special actions, as if the user were entering
+ * the characters, until ex trashed the screen, e.g. something like a
+ * '!' command. At that point, we no longer know what the screen
+ * looks like, so we can't afford to overwrite anything. The solution
+ * is to go into real ex mode until we get to the end of the command
+ * strings.
+ */
+ get = svi_get;
+ flags = TXT_BS | TXT_PROMPT;
+ for (in_exmode = rval = 0;;) {
+ /*
+ * Get the next command. Interrupt flag manipulation is safe
+ * because ex_icmd clears them all.
+ */
+ F_SET(sp, S_INTERRUPTIBLE);
+ if (get(sp, ep, sp->tiqp, ':', flags) != INP_OK) {
+ rval = 1;
+ break;
+ }
+ if (INTERRUPTED(sp))
+ break;
+
+ /*
+ * Len is 0 if the user backspaced over the prompt,
+ * 1 if only a CR was entered.
+ */
+ tp = sp->tiqp->cqh_first;
+ if (tp->len == 0)
+ break;
+
+ if (!in_exmode)
+ (void)svi_busy(sp, NULL);
+
+ /* Ignore return, presumably an error message was displayed. */
+ (void)ex_icmd(sp, ep, tp->lb, tp->len, 0);
+ (void)ex_fflush(EXCOOKIE);
+
+ /*
+ * The file or screen may have changed, in which case, the
+ * main editor loop takes care of it.
+ */
+ if (F_ISSET(sp, S_MAJOR_CHANGE))
+ break;
+
+ /*
+ * If continue not required, and one or no lines, and there
+ * are no waiting messages, don't wait, but don't overwrite
+ * it with mode information either.
+ */
+ if (!F_ISSET(sp, S_CONTINUE) && (svp->extotalcount == 0 ||
+ svp->extotalcount == 1 && !MSGS_WAITING(sp))) {
+ if (svp->extotalcount == 1) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ svi_ex_inv(sp);
+ }
+ break;
+ }
+
+ if (INTERRUPTED(sp))
+ break;
+
+ /*
+ * If the screen is trashed, or there are messages waiting,
+ * go into ex mode.
+ */
+ if (!in_exmode &&
+ (F_ISSET(sp, S_REFRESH) || MSGS_WAITING(sp))) {
+ /* Initialize the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY))
+ SEX_RAW(t);
+ get = sex_get;
+ flags = TXT_CR | TXT_NLECHO | TXT_PROMPT;
+ in_exmode = 1;
+ }
+
+ /* Display any waiting messages. */
+ if (MSGS_WAITING(sp))
+ (void)sex_refresh(sp, ep);
+
+ /*
+ * Get a continue character; users may continue in ex mode by
+ * entering a ':'.
+ *
+ * !!!
+ * Historic practice is that any key can be used to continue.
+ * Nvi used to require that the user enter a <carriage-return>
+ * or <newline>, but this broke historic users.
+ */
+ if (in_exmode) {
+ (void)write(STDOUT_FILENO,
+ STR_CMSG, sizeof(STR_CMSG) - 1);
+ if (term_key(sp, &ikey, 0) != INP_OK) {
+ rval = 1;
+ goto ret;
+ }
+ } else {
+ /* This message isn't interruptible. */
+ F_CLR(sp, S_INTERRUPTIBLE);
+ (void)svi_ex_scroll(sp, 1, &ikey);
+ }
+ if (ikey.ch != ':')
+ break;
+
+ if (in_exmode)
+ (void)write(STDOUT_FILENO, "\n", 1);
+ else {
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ }
+ }
+
+ret: if (in_exmode) {
+ /* Reset the terminal state. */
+ if (F_ISSET(sp->gp, G_STDIN_TTY) && SEX_NORAW(t))
+ rval = 1;
+ F_SET(sp, S_REFRESH);
+ } else
+ if (svi_ex_done(sp, ep, rp))
+ rval = 1;
+
+ F_CLR(sp, S_CONTINUE);
+ return (rval);
+}
+
+/*
+ * svi_msgflush --
+ * Flush any accumulated messages.
+ */
+int
+svi_msgflush(sp)
+ SCR *sp;
+{
+ enum {INVERSE, NORMAL} inverse;
+ SVI_PRIVATE *svp;
+ MSG *mp;
+ int rval;
+
+ svp = SVP(sp);
+ svp->exlcontinue = svp->exlinecount = svp->extotalcount = 0;
+
+ /*
+ * XXX
+ * S_IVIDEO is a bit of a kluge. We can only pass a single magic
+ * cookie into the svi_ex_write routine, and it has to be the SCR
+ * structure. So, the inverse video bit has to be there.
+ */
+ inverse = NORMAL;
+ for (mp = sp->msgq.lh_first;
+ mp != NULL && !F_ISSET(mp, M_EMPTY); mp = mp->q.le_next) {
+ /*
+ * If the second and subsequent messages fit on the current
+ * line, write a separator. Otherwise, put out a newline
+ * and break the line.
+ */
+ if (mp != sp->msgq.lh_first)
+ if (mp->len + svp->exlcontinue + 3 >= sp->cols) {
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ".\n", 2);
+ F_CLR(sp, S_IVIDEO);
+ } else {
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ";", 1);
+ F_CLR(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, " ", 2);
+ }
+
+ inverse = F_ISSET(mp, M_INV_VIDEO) ? INVERSE : NORMAL;
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, mp->mbuf, mp->len);
+ F_CLR(sp, S_IVIDEO);
+
+ F_SET(mp, M_EMPTY);
+ }
+
+ /*
+ * None of the messages end with periods, we do it in the message
+ * flush routine, which makes it possible to join messages.
+ */
+ if (inverse == INVERSE)
+ F_SET(sp, S_IVIDEO);
+ (void)svi_ex_write(sp, ".", 1);
+ F_CLR(sp, S_IVIDEO);
+
+ /*
+ * Figure out if we have to wait. Don't wait for only one line,
+ * but don't overwrite it with mode information either.
+ */
+ if (svp->extotalcount == 1) {
+ F_SET(sp, S_UPDATE_MODE);
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ svi_ex_inv(sp);
+ return (0);
+ }
+
+ rval = svi_ex_scroll(sp, 1, NULL);
+ if (svi_ex_done(sp, sp->ep, NULL))
+ rval = 1;
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ return (rval);
+}
+
+/*
+ * svi_ex_done --
+ * Cleanup from dipping into ex.
+ */
+static int
+svi_ex_done(sp, ep, rp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+{
+ SMAP *smp;
+ SVI_PRIVATE *svp;
+ recno_t lno;
+ size_t cnt, len;
+
+ /*
+ * The file or screen may have changed, in which case,
+ * the main editor loop takes care of it.
+ */
+ if (F_ISSET(sp, S_MAJOR_CHANGE))
+ return (0);
+
+ /*
+ * Otherwise, the only cursor modifications will be real, however, the
+ * underlying line may have changed; don't trust anything. This code
+ * has been a remarkably fertile place for bugs.
+ *
+ * Repaint the entire screen if at least half the screen is trashed.
+ * Else, repaint only over the overwritten lines. The "-2" comes
+ * from one for the mode line and one for the fact that it's an offset.
+ * Note the check for small screens.
+ *
+ * Don't trust ANYTHING.
+ */
+ svp = SVP(sp);
+ if (svp->extotalcount >= HALFTEXT(sp))
+ F_SET(sp, S_REDRAW);
+ else
+ for (cnt = sp->rows - 2; svp->extotalcount--; --cnt)
+ if (cnt > sp->t_rows) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ } else {
+ smp = HMAP + cnt;
+ SMAP_FLUSH(smp);
+ if (svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ }
+
+ /* Ignore the cursor if the caller doesn't care. */
+ if (rp == NULL)
+ return (0);
+
+ /*
+ * Do a reality check on a cursor value, and make sure it's okay.
+ * If necessary, change it. Ex keeps track of the line number,
+ * but it doesn't care about the column and it may have disappeared.
+ */
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ GETLINE_ERR(sp, sp->lno);
+ sp->lno = 1;
+ sp->cno = 0;
+ } else if (sp->cno >= len)
+ sp->cno = len ? len - 1 : 0;
+
+ rp->lno = sp->lno;
+ rp->cno = sp->cno;
+ return (0);
+}
+
+/*
+ * svi_ex_write --
+ * Write out the ex messages.
+ */
+int
+svi_ex_write(cookie, line, llen)
+ void *cookie;
+ const char *line;
+ int llen;
+{
+ SCR *sp;
+ SVI_PRIVATE *svp;
+ size_t oldy, oldx;
+ int len, rlen, tlen;
+ const char *p, *t;
+
+ /*
+ * XXX
+ * If it's a 4.4BSD system, we could just use fpurge(3).
+ * This shouldn't be too expensive, though.
+ */
+ sp = cookie;
+ svp = SVP(sp);
+ if (INTERRUPTED(sp))
+ return (llen);
+
+ p = line; /* In case of a write of 0. */
+ for (rlen = llen; llen;) {
+ /* Get the next line. */
+ if ((p = memchr(line, '\n', llen)) == NULL)
+ len = llen;
+ else
+ len = p - line;
+
+ /*
+ * The max is sp->cols characters, and we may
+ * have already written part of the line.
+ */
+ if (len + svp->exlcontinue > sp->cols)
+ len = sp->cols - svp->exlcontinue;
+
+ /*
+ * If the first line output, do nothing.
+ * If the second line output, draw the divider line.
+ * If drew a full screen, remove the divider line.
+ * If it's a continuation line, move to the continuation
+ * point, else, move the screen up.
+ */
+ if (svp->exlcontinue == 0) {
+ if (svp->extotalcount == 1) {
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ clrtoeol();
+ if (svi_ex_divider(sp))
+ return (-1);
+ F_SET(svp, SVI_DIVIDER);
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ }
+ if (svp->extotalcount == sp->t_maxrows &&
+ F_ISSET(svp, SVI_DIVIDER)) {
+ --svp->extotalcount;
+ --svp->exlinecount;
+ F_CLR(svp, SVI_DIVIDER);
+ }
+ if (svp->extotalcount != 0 &&
+ svi_ex_scroll(sp, 0, NULL))
+ return (-1);
+ MOVE(sp, INFOLINE(sp), 0);
+ ++svp->extotalcount;
+ ++svp->exlinecount;
+ if (F_ISSET(sp, S_INTERRUPTIBLE) && INTERRUPTED(sp))
+ break;
+ } else
+ MOVE(sp, INFOLINE(sp), svp->exlcontinue);
+
+ /* Display the line, doing character translation. */
+ if (F_ISSET(sp, S_IVIDEO))
+ standout();
+ for (t = line, tlen = len; tlen--; ++t)
+ ADDCH(*t);
+ if (F_ISSET(sp, S_IVIDEO))
+ standend();
+
+ /* Clear to EOL. */
+ getyx(stdscr, oldy, oldx);
+ if (oldx < sp->cols)
+ clrtoeol();
+
+ /* If we loop, it's a new line. */
+ svp->exlcontinue = 0;
+
+ /* Reset for the next line. */
+ line += len;
+ llen -= len;
+ if (p != NULL) {
+ ++line;
+ --llen;
+ }
+ }
+ /* Refresh the screen, even if it's a partial. */
+ refresh();
+
+ /* Set up next continuation line. */
+ if (p == NULL)
+ getyx(stdscr, oldy, svp->exlcontinue);
+ return (rlen);
+}
+
+/*
+ * svi_ex_scroll --
+ * Scroll the screen for ex output.
+ */
+static int
+svi_ex_scroll(sp, mustwait, chp)
+ SCR *sp;
+ int mustwait;
+ CH *chp;
+{
+ CH ikey;
+ SVI_PRIVATE *svp;
+
+ /*
+ * Scroll the screen. Instead of scrolling the entire screen, delete
+ * the line above the first line output so preserve the maximum amount
+ * of the screen.
+ */
+ svp = SVP(sp);
+ if (svp->extotalcount >= sp->rows) {
+ MOVE(sp, 0, 0);
+ } else
+ MOVE(sp, INFOLINE(sp) - svp->extotalcount, 0);
+
+ deleteln();
+
+ /* If there are screens below us, push them back into place. */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ MOVE(sp, INFOLINE(sp), 0);
+ insertln();
+ }
+
+ /* If just displayed a full screen, wait. */
+ if (mustwait || svp->exlinecount == sp->t_maxrows) {
+ MOVE(sp, INFOLINE(sp), 0);
+ if (F_ISSET(sp, S_INTERRUPTIBLE)) {
+ ADDNSTR(STR_QMSG, (int)sizeof(STR_QMSG) - 1);
+ } else {
+ ADDNSTR(STR_CMSG, (int)sizeof(STR_CMSG) - 1);
+ }
+ clrtoeol();
+ refresh();
+ /*
+ * !!!
+ * Historic practice is that any key can be used to continue.
+ * Nvi used to require that the user enter a <carriage-return>
+ * or <newline>, but this broke historic users.
+ */
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (-1);
+ if (ikey.ch == CH_QUIT && F_ISSET(sp, S_INTERRUPTIBLE))
+ F_SET(sp, S_INTERRUPTED);
+ if (chp != NULL)
+ *chp = ikey;
+ svp->exlinecount = 0;
+ }
+ return (0);
+}
+
+/*
+ * svi_ex_inv --
+ * Change whatever is on the info line to inverse video so we have
+ * a divider line between split screens.
+ */
+static int
+svi_ex_inv(sp)
+ SCR *sp;
+{
+ CHAR_T ch;
+ size_t spcnt, col, row;
+
+ row = INFOLINE(sp);
+
+ /*
+ * Walk through the line, retrieving each character and writing
+ * it back out in inverse video. Since curses doesn't have an
+ * EOL marker, only put out trailing spaces if we find another
+ * character.
+ *
+ * XXX
+ * This is a major kluge -- curses should have an interface
+ * that allows us to change attributes on a per line basis.
+ */
+ MOVE(sp, row, 0);
+ standout();
+ for (spcnt = col = 0;;) {
+ ch = winch(stdscr);
+ if (isspace(ch)) {
+ ++spcnt;
+ if (++col >= sp->cols)
+ break;
+ MOVE(sp, row, col);
+ } else {
+ if (spcnt) {
+ MOVE(sp, row, col - spcnt);
+ for (; spcnt > 0; --spcnt)
+ ADDCH(' ');
+ }
+ ADDCH(ch);
+ if (++col >= sp->cols)
+ break;
+ }
+ }
+ standend();
+ return (0);
+}
+
+/*
+ * svi_ex_divider --
+ * Draw a dividing line between the screens.
+ */
+static int
+svi_ex_divider(sp)
+ SCR *sp;
+{
+ size_t len;
+
+#define DIVIDESTR "+=+=+=+=+=+=+=+"
+ len = sizeof(DIVIDESTR) - 1 > sp->cols ?
+ sp->cols : sizeof(DIVIDESTR) - 1;
+ standout();
+ ADDNSTR(DIVIDESTR, len);
+ standend();
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_get.c b/usr.bin/vi/svi/svi_get.c
new file mode 100644
index 0000000..b58100b
--- /dev/null
+++ b/usr.bin/vi/svi/svi_get.c
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_get.c 8.27 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "svi_screen.h"
+
+/*
+ * svi_get --
+ * Fill a buffer from the terminal for vi.
+ */
+enum input
+svi_get(sp, ep, tiqh, prompt, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ ARG_CHAR_T prompt;
+ u_int flags;
+{
+ MARK save;
+ SMAP *esmp;
+ recno_t bot_lno;
+ size_t bot_off, cnt;
+ int eval;
+
+ /*
+ * The approach used is to fake like the user is doing input on
+ * the last line of the screen. This makes all of the scrolling
+ * work correctly, and allows us the use of the vi text editing
+ * routines, not to mention practically infinite length ex commands.
+ *
+ * Save the current location.
+ */
+ bot_lno = TMAP->lno;
+ bot_off = TMAP->off;
+ save.lno = sp->lno;
+ save.cno = sp->cno;
+
+ /*
+ * If it's a small screen, TMAP may be small for the screen.
+ * Fix it, filling in fake lines as we go.
+ */
+ if (ISSMALLSCREEN(sp))
+ for (esmp = HMAP + (sp->t_maxrows - 1); TMAP < esmp; ++TMAP) {
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].off = 1;
+ }
+
+ /* Build the fake entry. */
+ TMAP[1].lno = TMAP[0].lno + 1;
+ TMAP[1].off = 1;
+ SMAP_FLUSH(&TMAP[1]);
+ ++TMAP;
+
+ /* Move to it. */
+ sp->lno = TMAP[0].lno;
+ sp->cno = 0;
+
+ if (O_ISSET(sp, O_ALTWERASE))
+ LF_SET(TXT_ALTWERASE);
+ if (O_ISSET(sp, O_TTYWERASE))
+ LF_SET(TXT_TTYWERASE);
+ LF_SET(TXT_APPENDEOL |
+ TXT_CR | TXT_ESCAPE | TXT_INFOLINE | TXT_MAPINPUT);
+
+ /* Don't update the modeline for now. */
+ F_SET(SVP(sp), SVI_INFOLINE);
+
+ eval = v_ntext(sp, ep, tiqh, NULL, NULL, 0, NULL, prompt, 0, flags);
+
+ F_CLR(SVP(sp), SVI_INFOLINE);
+
+ /* Put it all back. */
+ --TMAP;
+ sp->lno = save.lno;
+ sp->cno = save.cno;
+
+ /*
+ * If it's a small screen, TMAP may be wrong. Clear any
+ * lines that might have been overwritten.
+ */
+ if (ISSMALLSCREEN(sp)) {
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+ }
+
+ /*
+ * The map may be wrong if the user entered more than one
+ * (logical) line. Fix it. If the user entered a whole
+ * screen, this will be slow, but it's not worth caring.
+ */
+ while (bot_lno != TMAP->lno || bot_off != TMAP->off)
+ if (svi_sm_1down(sp, ep))
+ return (INP_ERR);
+
+ /*
+ * Invalidate the cursor and the line size cache, the line never
+ * really existed. This fixes bugs where the user searches for
+ * the last line on the screen + 1 and the refresh routine thinks
+ * that's where we just were.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ return (eval ? INP_ERR : INP_OK);
+}
diff --git a/usr.bin/vi/svi/svi_line.c b/usr.bin/vi/svi/svi_line.c
new file mode 100644
index 0000000..e1d50da
--- /dev/null
+++ b/usr.bin/vi/svi/svi_line.c
@@ -0,0 +1,441 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_line.c 8.27 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+#if defined(DEBUG) && 0
+#define TABCH '-'
+#define TABSTR "--------------------"
+#else
+#define TABSTR " "
+#define TABCH ' '
+#endif
+
+/*
+ * svi_line --
+ * Update one line on the screen.
+ */
+int
+svi_line(sp, ep, smp, yp, xp)
+ SCR *sp;
+ EXF *ep;
+ SMAP *smp;
+ size_t *xp, *yp;
+{
+ SMAP *tsmp;
+ size_t chlen, cols_per_screen, cno_cnt, len, scno, skip_screens;
+ size_t offset_in_char, offset_in_line;
+ size_t oldy, oldx;
+ int ch, is_cached, is_infoline, is_partial, is_tab;
+ int list_tab, list_dollar;
+ char *p, nbuf[10];
+
+#if defined(DEBUG) && 0
+ TRACE(sp, "svi_line: row %u: line: %u off: %u\n",
+ smp - HMAP, smp->lno, smp->off);
+#endif
+
+ /*
+ * Assume that, if the cache entry for the line is filled in, the
+ * line is already on the screen, and all we need to do is return
+ * the cursor position. If the calling routine doesn't need the
+ * cursor position, we can just return.
+ */
+ is_cached = SMAP_CACHE(smp);
+ if (yp == NULL && is_cached)
+ return (0);
+
+ /*
+ * A nasty side effect of this routine is that it returns the screen
+ * position for the "current" character. Not pretty, but this is the
+ * only routine that really knows what's out there.
+ *
+ * Move to the line. This routine can be called by svi_sm_position(),
+ * which uses it to fill in the cache entry so it can figure out what
+ * the real contents of the screen are. Because of this, we have to
+ * return to whereever we started from.
+ */
+ getyx(stdscr, oldy, oldx);
+ MOVE(sp, smp - HMAP, 0);
+
+ /* Get a copy of the line. */
+ p = file_gline(sp, ep, smp->lno, &len);
+
+ /*
+ * Special case if we're printing the info/mode line. Skip printing
+ * the leading number, as well as other minor setup. If painting the
+ * line between two screens, it's always in reverse video. The only
+ * time this code paints the mode line is when the user is entering
+ * text for a ":" command, so we can put the code here instead of
+ * dealing with the empty line logic below. This is a kludge, but it's
+ * pretty much confined to this module.
+ *
+ * Set the number of screens to skip until a character is displayed.
+ * Left-right screens are special, because we don't bother building
+ * a buffer to be skipped over.
+ *
+ * Set the number of columns for this screen.
+ */
+ cols_per_screen = sp->cols;
+ list_tab = O_ISSET(sp, O_LIST);
+ if (is_infoline = ISINFOLINE(sp, smp)) {
+ list_dollar = 0;
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ skip_screens = 0;
+ else
+ skip_screens = smp->off - 1;
+ } else {
+ list_dollar = list_tab;
+ skip_screens = smp->off - 1;
+
+ /*
+ * If O_NUMBER is set and it's line number 1 or the line exists
+ * and this is the first screen of a folding line or any left-
+ * right line, display the line number.
+ */
+ if (O_ISSET(sp, O_NUMBER)) {
+ cols_per_screen -= O_NUMBER_LENGTH;
+ if ((smp->lno == 1 || p != NULL) && skip_screens == 0) {
+ (void)snprintf(nbuf,
+ sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+ ADDSTR(nbuf);
+ }
+ }
+ }
+
+ /*
+ * Special case non-existent lines and the first line of an empty
+ * file. In both cases, the cursor position is 0, but corrected
+ * for the O_NUMBER field if it was displayed.
+ */
+ if (p == NULL || len == 0) {
+ /* Fill in the cursor. */
+ if (yp != NULL && smp->lno == sp->lno) {
+ *yp = smp - HMAP;
+ *xp = sp->cols - cols_per_screen;
+ }
+
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+
+ /* Set line cacheing information. */
+ smp->c_sboff = smp->c_eboff = 0;
+ smp->c_scoff = smp->c_eclen = 0;
+
+ /* Lots of special cases for empty lines. */
+ if (skip_screens == 0)
+ if (p == NULL) {
+ if (smp->lno == 1) {
+ if (list_dollar) {
+ ch = '$';
+ goto empty;
+ }
+ } else {
+ ch = '~';
+ goto empty;
+ }
+ } else
+ if (list_dollar) {
+ ch = '$';
+empty: ADDCH(ch);
+ }
+
+ clrtoeol();
+ MOVEA(sp, oldy, oldx);
+ return (0);
+ }
+
+ /*
+ * If we wrote a line that's this or a previous one, we can do this
+ * much more quickly -- we cached the starting and ending positions
+ * of that line. The way it works is we keep information about the
+ * lines displayed in the SMAP. If we're painting the screen in
+ * the forward, this saves us from reformatting the physical line for
+ * every line on the screen. This wins big on binary files with 10K
+ * lines.
+ *
+ * Test for the first screen of the line, then the current screen line,
+ * then the line behind us, then do the hard work. Note, it doesn't
+ * do us any good to have a line in front of us -- it would be really
+ * hard to try and figure out tabs in the reverse direction, i.e. how
+ * many spaces a tab takes up in the reverse direction depends on
+ * what characters preceded it.
+ */
+ if (smp->off == 1) {
+ smp->c_sboff = offset_in_line = 0;
+ smp->c_scoff = offset_in_char = 0;
+ p = &p[offset_in_line];
+ } else if (is_cached) {
+ offset_in_line = smp->c_sboff;
+ offset_in_char = smp->c_scoff;
+ p = &p[offset_in_line];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } else if (smp != HMAP &&
+ SMAP_CACHE(tsmp = smp - 1) && tsmp->lno == smp->lno) {
+ if (tsmp->c_eclen != tsmp->c_ecsize) {
+ offset_in_line = tsmp->c_eboff;
+ offset_in_char = tsmp->c_eclen;
+ } else {
+ offset_in_line = tsmp->c_eboff + 1;
+ offset_in_char = 0;
+ }
+
+ /* Put starting info for this line in the cache. */
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char;
+ p = &p[offset_in_line];
+ if (skip_screens != 0)
+ cols_per_screen = sp->cols;
+ } else {
+ offset_in_line = 0;
+ offset_in_char = 0;
+
+ /* This is the loop that skips through screens. */
+ if (skip_screens == 0) {
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char;
+ } else for (scno = 0; offset_in_line < len; ++offset_in_line) {
+ scno += chlen =
+ (ch = *(u_char *)p++) == '\t' && !list_tab ?
+ TAB_OFF(sp, scno) : KEY_LEN(sp, ch);
+ if (scno < cols_per_screen)
+ continue;
+ /*
+ * Reset cols_per_screen to second and subsequent line
+ * length.
+ */
+ scno -= cols_per_screen;
+ cols_per_screen = sp->cols;
+
+ /*
+ * If crossed the last skipped screen boundary, start
+ * displaying the characters.
+ */
+ if (--skip_screens)
+ continue;
+
+ /* Put starting info for this line in the cache. */
+ if (scno) {
+ smp->c_sboff = offset_in_line;
+ smp->c_scoff = offset_in_char = chlen - scno;
+ --p;
+ } else {
+ smp->c_sboff = ++offset_in_line;
+ smp->c_scoff = 0;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Set the number of characters to skip before reaching the cursor
+ * character. Offset by 1 and use 0 as a flag value. Svi_line is
+ * called repeatedly with a valid pointer to a cursor position.
+ * Don't fill anything in unless it's the right line and the right
+ * character, and the right part of the character...
+ */
+ if (yp == NULL ||
+ smp->lno != sp->lno || sp->cno < offset_in_line ||
+ offset_in_line + cols_per_screen < sp->cno) {
+ cno_cnt = 0;
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+ } else
+ cno_cnt = (sp->cno - offset_in_line) + 1;
+
+ /* This is the loop that actually displays characters. */
+ for (is_partial = 0, scno = 0;
+ offset_in_line < len; ++offset_in_line, offset_in_char = 0) {
+ if ((ch = *(u_char *)p++) == '\t' && !list_tab) {
+ scno += chlen = TAB_OFF(sp, scno) - offset_in_char;
+ is_tab = 1;
+ } else {
+ scno += chlen = KEY_LEN(sp, ch) - offset_in_char;
+ is_tab = 0;
+ }
+
+ /*
+ * Only display up to the right-hand column. Set a flag if
+ * the entire character wasn't displayed for use in setting
+ * the cursor. If reached the end of the line, set the cache
+ * info for the screen. Don't worry about there not being
+ * characters to display on the next screen, its lno/off won't
+ * match up in that case.
+ */
+ if (scno >= cols_per_screen) {
+ smp->c_ecsize = chlen;
+ chlen -= scno - cols_per_screen;
+ smp->c_eclen = chlen;
+ smp->c_eboff = offset_in_line;
+ if (scno > cols_per_screen)
+ is_partial = 1;
+
+ /* Terminate the loop. */
+ offset_in_line = len;
+ }
+
+ /*
+ * If the caller wants the cursor value, and this was the
+ * cursor character, set the value. There are two ways to
+ * put the cursor on a character -- if it's normal display
+ * mode, it goes on the last column of the character. If
+ * it's input mode, it goes on the first. In normal mode,
+ * set the cursor only if the entire character was displayed.
+ */
+ if (cno_cnt &&
+ --cno_cnt == 0 && (F_ISSET(sp, S_INPUT) || !is_partial)) {
+ *yp = smp - HMAP;
+ if (F_ISSET(sp, S_INPUT))
+ *xp = scno - chlen;
+ else
+ *xp = scno - 1;
+ if (O_ISSET(sp, O_NUMBER) &&
+ !is_infoline && smp->off == 1)
+ *xp += O_NUMBER_LENGTH;
+
+ /* If the line is on the screen, quit. */
+ if (is_cached)
+ goto ret;
+ }
+
+ /* If the line is on the screen, don't display anything. */
+ if (is_cached)
+ continue;
+
+ /*
+ * Display the character. If it's a tab and tabs aren't some
+ * ridiculous length, do it fast. (We do tab expansion here
+ * because curses doesn't have a way to set the tab length.)
+ */
+ if (is_tab) {
+ if (chlen <= sizeof(TABSTR) - 1) {
+ ADDNSTR(TABSTR, chlen);
+ } else
+ while (chlen--)
+ ADDCH(TABCH);
+ } else
+ ADDNSTR(KEY_NAME(sp, ch) + offset_in_char, chlen);
+ }
+
+ if (scno < cols_per_screen) {
+ /* If didn't paint the whole line, update the cache. */
+ smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch);
+ smp->c_eboff = len - 1;
+
+ /*
+ * If not the info/mode line, and O_LIST set, and at the
+ * end of the line, and the line ended on this screen,
+ * add a trailing $.
+ */
+ if (list_dollar) {
+ ++scno;
+ ADDCH('$');
+ }
+
+ /* If still didn't paint the whole line, clear the rest. */
+ if (scno < cols_per_screen)
+ clrtoeol();
+ }
+
+ret: MOVEA(sp, oldy, oldx);
+ return (0);
+}
+
+/*
+ * svi_number --
+ * Repaint the numbers on all the lines.
+ */
+int
+svi_number(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SMAP *smp;
+ size_t oldy, oldx;
+ char *lp, nbuf[10];
+
+ /*
+ * Try and avoid getting the last line in the file, by getting the
+ * line after the last line in the screen -- if it exists, we know
+ * we have to to number all the lines in the screen. Get the one
+ * after the last instead of the last, so that the info line doesn't
+ * fool us.
+ *
+ * If that test fails, we have to check each line for existence.
+ *
+ * XXX
+ * The problem is that file_lline will lie, and tell us that the
+ * info line is the last line in the file.
+ */
+ lp = file_gline(sp, ep, TMAP->lno + 1, NULL);
+
+ getyx(stdscr, oldy, oldx);
+ for (smp = HMAP; smp <= TMAP; ++smp) {
+ if (smp->off != 1)
+ continue;
+ if (ISINFOLINE(sp, smp))
+ break;
+ if (smp->lno != 1 && lp == NULL &&
+ file_gline(sp, ep, smp->lno, NULL) == NULL)
+ break;
+ MOVE(sp, smp - HMAP, 0);
+ (void)snprintf(nbuf, sizeof(nbuf), O_NUMBER_FMT, smp->lno);
+ ADDSTR(nbuf);
+ }
+ MOVEA(sp, oldy, oldx);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_refresh.c b/usr.bin/vi/svi/svi_refresh.c
new file mode 100644
index 0000000..9cb2dce
--- /dev/null
+++ b/usr.bin/vi/svi/svi_refresh.c
@@ -0,0 +1,818 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_refresh.c 8.62 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+static int svi_modeline __P((SCR *, EXF *));
+
+int
+svi_refresh(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ u_int paintbits;
+
+ /*
+ * 1: Resize the screen.
+ *
+ * Notice that a resize is requested, and set up everything so that
+ * the file gets reinitialized. Done here, instead of in the vi loop
+ * because there may be other initialization that other screens need
+ * to do. The actual changing of the row/column values was done by
+ * calling the ex options code which put them into the environment,
+ * which is used by curses. Stupid, but ugly.
+ */
+ if (F_ISSET(sp, S_RESIZE)) {
+ /* Reinitialize curses. */
+ if (svi_curses_end(sp) || svi_curses_init(sp))
+ return (1);
+
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ /*
+ * Fill the map, incidentally losing any svi_line()
+ * cached information.
+ */
+ if (svi_sm_fill(sp, ep, sp->lno, P_FILL))
+ return (1);
+ F_CLR(sp, S_RESIZE | S_REFORMAT);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /*
+ * 2: S_REFRESH
+ *
+ * If S_REFRESH is set in the current screen, repaint everything
+ * that we can find.
+ */
+ if (F_ISSET(sp, S_REFRESH))
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp)
+ F_SET(tsp, S_REDRAW);
+ /*
+ * 3: Related or dirtied screens, or screens with messages.
+ *
+ * If related screens share a view into a file, they may have been
+ * modified as well. Refresh any screens with paint or dirty bits
+ * set, or where messages are waiting. Finally, if we refresh any
+ * screens other than the current one, the cursor will be trashed.
+ */
+ paintbits = S_REDRAW | S_REFORMAT | S_REFRESH;
+ if (O_ISSET(sp, O_NUMBER))
+ paintbits |= S_RENUMBER;
+ for (tsp = sp->gp->dq.cqh_first;
+ tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
+ if (tsp != sp &&
+ (F_ISSET(tsp, paintbits) ||
+ F_ISSET(SVP(tsp), SVI_SCREENDIRTY) ||
+ tsp->msgq.lh_first != NULL &&
+ !F_ISSET(tsp->msgq.lh_first, M_EMPTY))) {
+ (void)svi_paint(tsp, tsp->ep);
+ F_CLR(SVP(tsp), SVI_SCREENDIRTY);
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ }
+
+ /*
+ * 4: Refresh the current screen.
+ *
+ * Always refresh the current screen, it may be a cursor movement.
+ * Also, always do it last -- that way, S_REFRESH can be set in
+ * the current screen only, and the screen won't flash.
+ */
+ F_CLR(sp, SVI_SCREENDIRTY);
+ return (svi_paint(sp, ep));
+}
+
+/*
+ * svi_paint --
+ * This is the guts of the vi curses screen code. The idea is that
+ * the SCR structure passed in contains the new coordinates of the
+ * screen. What makes this hard is that we don't know how big
+ * characters are, doing input can put the cursor in illegal places,
+ * and we're frantically trying to avoid repainting unless it's
+ * absolutely necessary. If you change this code, you'd better know
+ * what you're doing. It's subtle and quick to anger.
+ */
+int
+svi_paint(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SMAP *smp, tmp;
+ SVI_PRIVATE *svp;
+ recno_t lastline, lcnt;
+ size_t cwtotal, cnt, len, x, y;
+ int ch, didpaint, leftright_warp;
+ char *p;
+
+#define LNO sp->lno
+#define OLNO svp->olno
+#define CNO sp->cno
+#define OCNO svp->ocno
+#define SCNO svp->sc_col
+
+ didpaint = leftright_warp = 0;
+ svp = SVP(sp);
+
+ /*
+ * 1: Reformat the lines.
+ *
+ * If the lines themselves have changed (:set list, for example),
+ * fill in the map from scratch. Adjust the screen that's being
+ * displayed if the leftright flag is set.
+ */
+ if (F_ISSET(sp, S_REFORMAT)) {
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ /* Toss svi_line() cached information. */
+ if (svi_sm_fill(sp, ep, HMAP->lno, P_TOP))
+ return (1);
+ if (O_ISSET(sp, O_LEFTRIGHT) &&
+ (cnt = svi_opt_screens(sp, ep, LNO, &CNO)) != 1)
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ F_CLR(sp, S_REFORMAT);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /*
+ * 2: Line movement.
+ *
+ * Line changes can cause the top line to change as well. As
+ * before, if the movement is large, the screen is repainted.
+ *
+ * 2a: Tiny screens.
+ *
+ * Tiny screens cannot be permitted into the "scrolling" parts of
+ * the smap code for two reasons. If the screen size is 1 line,
+ * HMAP == TMAP and the code will quickly drop core. If the screen
+ * size is 2, none of the divisions by 2 will work, and scrolling
+ * won't work. In fact, because no line change will be less than
+ * HALFTEXT(sp), we always ending up "filling" the map, with a
+ * P_MIDDLE flag, which isn't what the user wanted. Tiny screens
+ * can go into the "fill" portions of the smap code, however.
+ */
+ if (sp->t_rows <= 2) {
+ if (LNO < HMAP->lno) {
+ if (svi_sm_fill(sp, ep, LNO, P_TOP))
+ return (1);
+ } else if (LNO > TMAP->lno)
+ if (svi_sm_fill(sp, ep, LNO, P_BOTTOM))
+ return (1);
+ if (sp->t_rows == 1) {
+ HMAP->off = svi_opt_screens(sp, ep, LNO, &CNO);
+ goto paint;
+ }
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+
+ /*
+ * 2b: Small screens.
+ *
+ * Users can use the window, w300, w1200 and w9600 options to make
+ * the screen artificially small. The behavior of these options
+ * in the historic vi wasn't all that consistent, and, in fact, it
+ * was never documented how various screen movements affected the
+ * screen size. Generally, one of three things would happen:
+ * 1: The screen would expand in size, showing the line
+ * 2: The screen would scroll, showing the line
+ * 3: The screen would compress to its smallest size and
+ * repaint.
+ * In general, scrolling didn't cause compression (200^D was handled
+ * the same as ^D), movement to a specific line would (:N where N
+ * was 1 line below the screen caused a screen compress), and cursor
+ * movement would scroll if it was 11 lines or less, and compress if
+ * it was more than 11 lines. (And, no, I have no idea where the 11
+ * comes from.)
+ *
+ * What we do is try and figure out if the line is less than half of
+ * a full screen away. If it is, we expand the screen if there's
+ * room, and then scroll as necessary. The alternative is to compress
+ * and repaint.
+ *
+ * !!!
+ * This code is a special case from beginning to end. Unfortunately,
+ * home modems are still slow enough that it's worth having.
+ *
+ * XXX
+ * If the line a really long one, i.e. part of the line is on the
+ * screen but the column offset is not, we'll end up in the adjust
+ * code, when we should probably have compressed the screen.
+ */
+ if (ISSMALLSCREEN(sp))
+ if (LNO < HMAP->lno) {
+ lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ ++TMAP;
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ }
+ else
+ goto small_fill;
+ } else if (LNO > TMAP->lno) {
+ lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, sp->t_maxrows);
+ if (lcnt <= HALFSCREEN(sp))
+ for (; lcnt && sp->t_rows != sp->t_maxrows;
+ --lcnt, ++sp->t_rows) {
+ if (svi_sm_next(sp, ep, TMAP, TMAP + 1))
+ return (1);
+ ++TMAP;
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+ }
+ else {
+small_fill: MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (; sp->t_rows > sp->t_minrows;
+ --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ if (svi_sm_fill(sp, ep, LNO, P_FILL))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+ }
+
+ /*
+ * 3a: Line down, or current screen.
+ */
+ if (LNO >= HMAP->lno) {
+ /* Current screen. */
+ if (LNO <= TMAP->lno)
+ goto adjust;
+
+ /*
+ * If less than half a screen above the line, scroll down
+ * until the line is on the screen.
+ */
+ lcnt = svi_sm_nlines(sp, ep, TMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ while (lcnt--)
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ goto adjust;
+ }
+ goto bottom;
+ }
+
+ /*
+ * 3b: Line up.
+ */
+ lcnt = svi_sm_nlines(sp, ep, HMAP, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ /*
+ * If less than half a screen below the line, scroll up until
+ * the line is the first line on the screen. Special check so
+ * that if the screen has been emptied, we refill it.
+ */
+ if (file_gline(sp, ep, HMAP->lno, &len) != NULL) {
+ while (lcnt--)
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ goto adjust;
+ }
+
+ /*
+ * If less than a full screen from the bottom of the file,
+ * put the last line of the file on the bottom of the screen.
+ */
+bottom: if (file_lline(sp, ep, &lastline))
+ return (1);
+ tmp.lno = LNO;
+ tmp.off = 1;
+ lcnt = svi_sm_nlines(sp, ep, &tmp, lastline, sp->t_rows);
+ if (lcnt < sp->t_rows) {
+ if (svi_sm_fill(sp, ep, lastline, P_BOTTOM))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ goto adjust;
+ }
+ /* It's not close, just put the line in the middle. */
+ goto middle;
+ }
+
+ /*
+ * If less than half a screen from the top of the file, put the first
+ * line of the file at the top of the screen. Otherwise, put the line
+ * in the middle of the screen.
+ */
+ tmp.lno = 1;
+ tmp.off = 1;
+ lcnt = svi_sm_nlines(sp, ep, &tmp, LNO, HALFTEXT(sp));
+ if (lcnt < HALFTEXT(sp)) {
+ if (svi_sm_fill(sp, ep, 1, P_TOP))
+ return (1);
+ } else
+middle: if (svi_sm_fill(sp, ep, LNO, P_MIDDLE))
+ return (1);
+ F_SET(sp, S_REDRAW);
+
+ /*
+ * At this point we know part of the line is on the screen. Since
+ * scrolling is done using logical lines, not physical, all of the
+ * line may not be on the screen. While that's not necessarily bad,
+ * if the part the cursor is on isn't there, we're going to lose.
+ * This can be tricky; if the line covers the entire screen, lno
+ * may be the same as both ends of the map, that's why we test BOTH
+ * the top and the bottom of the map. This isn't a problem for
+ * left-right scrolling, the cursor movement code handles the problem.
+ *
+ * There's a performance issue here if editing *really* long lines.
+ * This gets to the right spot by scrolling, and, in a binary, by
+ * scrolling hundreds of lines. If the adjustment looks like it's
+ * going to be a serious problem, refill the screen and repaint.
+ */
+adjust: if (!O_ISSET(sp, O_LEFTRIGHT) &&
+ (LNO == HMAP->lno || LNO == TMAP->lno)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO);
+ if (LNO == HMAP->lno && cnt < HMAP->off)
+ if ((HMAP->off - cnt) > HALFTEXT(sp)) {
+ HMAP->off = cnt;
+ svi_sm_fill(sp, ep, OOBLNO, P_TOP);
+ F_SET(sp, S_REDRAW);
+ } else
+ while (cnt < HMAP->off)
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (LNO == TMAP->lno && cnt > TMAP->off)
+ if ((cnt - TMAP->off) > HALFTEXT(sp)) {
+ TMAP->off = cnt;
+ svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM);
+ F_SET(sp, S_REDRAW);
+ } else
+ while (cnt > TMAP->off)
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ }
+
+ /*
+ * If the screen needs to be repainted, skip cursor optimization.
+ * However, in the code above we skipped leftright scrolling on
+ * the grounds that the cursor code would handle it. Make sure
+ * the right screen is up.
+ */
+ if (F_ISSET(sp, S_REDRAW)) {
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO);
+ if (HMAP->off != cnt)
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ }
+ goto paint;
+ }
+
+ /*
+ * 4: Cursor movements.
+ *
+ * Decide cursor position. If the line has changed, the cursor has
+ * moved over a tab, or don't know where the cursor was, reparse the
+ * line. Otherwise, we've just moved over fixed-width characters,
+ * and can calculate the left/right scrolling and cursor movement
+ * without reparsing the line. Note that we don't know which (if any)
+ * of the characters between the old and new cursor positions changed.
+ *
+ * XXX
+ * With some work, it should be possible to handle tabs quickly, at
+ * least in obvious situations, like moving right and encountering
+ * a tab, without reparsing the whole line.
+ */
+
+ /* If the line we're working with has changed, reparse. */
+ if (F_ISSET(SVP(sp), SVI_CUR_INVALID) || LNO != OLNO) {
+ F_CLR(SVP(sp), SVI_CUR_INVALID);
+ goto slow;
+ }
+
+ /* Otherwise, if nothing's changed, go fast. */
+ if (CNO == OCNO)
+ goto fast;
+
+ /*
+ * Get the current line. If this fails, we either have an empty
+ * file and can just repaint, or there's a real problem. This
+ * isn't a performance issue because there aren't any ways to get
+ * here repeatedly.
+ */
+ if ((p = file_gline(sp, ep, LNO, &len)) == NULL) {
+ if (file_lline(sp, ep, &lastline))
+ return (1);
+ if (lastline == 0)
+ goto slow;
+ GETLINE_ERR(sp, LNO);
+ return (1);
+ }
+
+#ifdef DEBUG
+ /* This is just a test. */
+ if (CNO >= len && len != 0) {
+ msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
+ tail(__FILE__), __LINE__, CNO, len);
+ return (1);
+ }
+#endif
+ /*
+ * The basic scheme here is to look at the characters in between
+ * the old and new positions and decide how big they are on the
+ * screen, and therefore, how many screen positions to move.
+ */
+ if (CNO < OCNO) {
+ /*
+ * 4a: Cursor moved left.
+ *
+ * Point to the old character. The old cursor position can
+ * be past EOL if, for example, we just deleted the rest of
+ * the line. In this case, since we don't know the width of
+ * the characters we traversed, we have to do it slowly.
+ */
+ p += OCNO;
+ cnt = (OCNO - CNO) + 1;
+ if (OCNO >= len)
+ goto slow;
+
+ /*
+ * Quick sanity check -- it's hard to figure out exactly when
+ * we cross a screen boundary as we do in the cursor right
+ * movement. If cnt is so large that we're going to cross the
+ * boundary no matter what, stop now.
+ */
+ if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
+ goto lscreen;
+
+ /*
+ * Count up the widths of the characters. If it's a tab
+ * character, go do it the the slow way.
+ */
+ for (cwtotal = 0; cnt--; cwtotal += KEY_LEN(sp, ch))
+ if ((ch = *(u_char *)p--) == '\t')
+ goto slow;
+
+ /*
+ * Decrement the screen cursor by the total width of the
+ * characters minus 1.
+ */
+ cwtotal -= 1;
+
+ /*
+ * If we're moving left, and there's a wide character in the
+ * current position, go to the end of the character.
+ */
+ if (KEY_LEN(sp, ch) > 1)
+ cwtotal -= KEY_LEN(sp, ch) - 1;
+
+ /*
+ * If the new column moved us off of the current logical line,
+ * calculate a new one. If doing leftright scrolling, we've
+ * moved off of the current screen, as well. Since most files
+ * don't have more than two screens, we optimize moving from
+ * screen 2 to screen 1.
+ */
+ if (SCNO < cwtotal) {
+lscreen: if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = HMAP->off == 2 ? 1 :
+ svi_opt_screens(sp, ep, LNO, &CNO);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ leftright_warp = 1;
+ goto paint;
+ }
+ goto slow;
+ }
+ SCNO -= cwtotal;
+ } else {
+ /*
+ * 4b: Cursor moved right.
+ *
+ * Point to the first character to the right.
+ */
+ p += OCNO + 1;
+ cnt = CNO - OCNO;
+
+ /*
+ * Count up the widths of the characters. If it's a tab
+ * character, go do it the the slow way. If we cross a
+ * screen boundary, we can quit.
+ */
+ for (cwtotal = SCNO; cnt--;) {
+ if ((ch = *(u_char *)p++) == '\t')
+ goto slow;
+ if ((cwtotal += KEY_LEN(sp, ch)) >= SCREEN_COLS(sp))
+ break;
+ }
+
+ /*
+ * Increment the screen cursor by the total width of the
+ * characters.
+ */
+ SCNO = cwtotal;
+
+ /* See screen change comment in section 4a. */
+ if (SCNO >= SCREEN_COLS(sp)) {
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ leftright_warp = 1;
+ goto paint;
+ }
+ goto slow;
+ }
+ }
+
+ /*
+ * 4c: Fast cursor update.
+ *
+ * Retrieve the current cursor position, and correct it
+ * for split screens.
+ */
+fast: getyx(stdscr, y, x);
+ y -= sp->woff;
+ goto number;
+
+ /*
+ * 4d: Slow cursor update.
+ *
+ * Walk through the map and find the current line. If doing left-right
+ * scrolling and the cursor movement has changed the screen displayed,
+ * scroll the screen left or right, unless we're updating the info line
+ * in which case we just scroll that one line. Then update the screen
+ * lines for this file line until we have a new screen cursor position.
+ */
+slow: for (smp = HMAP; smp->lno != LNO; ++smp);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ cnt = svi_opt_screens(sp, ep, LNO, &CNO) % SCREEN_COLS(sp);
+ if (cnt != HMAP->off) {
+ if (ISINFOLINE(sp, smp))
+ smp->off = cnt;
+ else {
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ smp->off = cnt;
+ leftright_warp = 1;
+ }
+ goto paint;
+ }
+ }
+ for (y = -1; smp <= TMAP && smp->lno == LNO; ++smp) {
+ if (svi_line(sp, ep, smp, &y, &SCNO))
+ return (1);
+ if (y != -1)
+ break;
+ }
+ goto number;
+
+ /*
+ * 5: Repaint the entire screen.
+ *
+ * Lost big, do what you have to do. We flush the cache as S_REDRAW
+ * gets set when the screen isn't worth fixing, and it's simpler to
+ * repaint. So, don't trust anything that we think we know about it.
+ */
+paint: for (smp = HMAP; smp <= TMAP; ++smp)
+ SMAP_FLUSH(smp);
+ for (smp = HMAP; smp <= TMAP; ++smp)
+ if (svi_line(sp, ep, smp, &y, &SCNO))
+ return (1);
+ /*
+ * If it's a small screen and we're redrawing, clear the unused lines,
+ * ex may have overwritten them.
+ */
+ if (F_ISSET(sp, S_REDRAW)) {
+ if (ISSMALLSCREEN(sp))
+ for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ F_CLR(sp, S_REDRAW);
+ }
+
+ didpaint = 1;
+
+ /*
+ * 6: Repaint the line numbers.
+ *
+ * If O_NUMBER is set and the S_RENUMBER bit is set, and we didn't
+ * repaint the screen, repaint all of the line numbers, they've
+ * changed.
+ */
+number: if (O_ISSET(sp, O_NUMBER) && F_ISSET(sp, S_RENUMBER) && !didpaint) {
+ if (svi_number(sp, ep))
+ return (1);
+ F_CLR(sp, S_RENUMBER);
+ }
+
+ /*
+ * 7: Refresh the screen.
+ *
+ * If the screen was corrupted, refresh it.
+ */
+ if (F_ISSET(sp, S_REFRESH)) {
+ wrefresh(curscr);
+ F_CLR(sp, S_REFRESH);
+ }
+
+ if (F_ISSET(sp, S_BELLSCHED))
+ svi_bell(sp);
+ /*
+ * If the bottom line isn't in use by the colon command, and
+ * we're not in the middle of a map:
+ *
+ * Display any messages. Don't test S_UPDATE_MODE. The
+ * message printing routine set it to avoid anyone else
+ * destroying the message we're about to display.
+ *
+ * If the bottom line isn't in use by anyone, put out the
+ * standard status line.
+ */
+ if (!F_ISSET(SVP(sp), SVI_INFOLINE) && !KEYS_WAITING(sp))
+ if (sp->msgq.lh_first != NULL &&
+ !F_ISSET(sp->msgq.lh_first, M_EMPTY))
+ svi_msgflush(sp);
+ else if (!F_ISSET(sp, S_UPDATE_MODE))
+ svi_modeline(sp, ep);
+
+ /* Update saved information. */
+ OCNO = CNO;
+ OLNO = LNO;
+
+ /* Place the cursor. */
+ MOVE(sp, y, SCNO);
+
+ /* Flush it all out. */
+ refresh();
+
+ /*
+ * XXX
+ * Recalculate the "most favorite" cursor position. Vi doesn't know
+ * that we've warped the screen and it's going to have a completely
+ * wrong idea about where the cursor should be. This is vi's problem,
+ * and fixing it here is a gross violation of layering.
+ */
+ if (leftright_warp)
+ (void)svi_column(sp, ep, &sp->rcm);
+
+ return (0);
+}
+
+/*
+ * svi_modeline --
+ * Update the mode line.
+ */
+static int
+svi_modeline(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t cols, curlen, endpoint, len, midpoint;
+ char *p, buf[20];
+
+ /* Clear the mode line. */
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+
+ /*
+ * We put down the file name, the ruler, the mode and the dirty flag.
+ * If there's not enough room, there's not enough room, we don't play
+ * any special games. We try to put the ruler in the middle and the
+ * mode and dirty flag at the end.
+ *
+ * !!!
+ * Leave the last character blank, in case it's a really dumb terminal
+ * with hardware scroll. Second, don't paint the last character in the
+ * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
+ */
+ cols = sp->cols - 1;
+
+ curlen = 0;
+ if (sp->q.cqe_next != (void *)&sp->gp->dq) {
+ for (p = sp->frp->name; *p != '\0'; ++p);
+ while (--p > sp->frp->name) {
+ if (*p == '/') {
+ ++p;
+ break;
+ }
+ if ((curlen += KEY_LEN(sp, *p)) > cols) {
+ curlen -= KEY_LEN(sp, *p);
+ ++p;
+ break;
+ }
+ }
+
+ MOVE(sp, INFOLINE(sp), 0);
+ standout();
+ for (; *p != '\0'; ++p)
+ ADDCH(*p);
+ standend();
+ }
+
+ /*
+ * Display the ruler. If we're not at the midpoint yet, move there.
+ * Otherwise, just add in two extra spaces.
+ *
+ * XXX
+ * Assume that numbers, commas, and spaces only take up a single
+ * column on the screen.
+ */
+ if (O_ISSET(sp, O_RULER)) {
+ len = snprintf(buf,
+ sizeof(buf), "%lu,%lu", sp->lno, sp->cno + 1);
+ midpoint = (cols - ((len + 1) / 2)) / 2;
+ if (curlen < midpoint) {
+ MOVE(sp, INFOLINE(sp), midpoint);
+ ADDSTR(buf);
+ curlen += len;
+ } else if (curlen + 2 + len < cols) {
+ ADDSTR(" ");
+ ADDSTR(buf);
+ curlen += 2 + len;
+ }
+ }
+
+ /*
+ * Display the mode and the modified flag, as close to the end of the
+ * line as possible, but guaranteeing at least two spaces between the
+ * ruler and the modified flag.
+ *
+ * XXX
+ * Assume that mode name characters, asterisks, and spaces only take
+ * up a single column on the screen.
+ */
+ endpoint = cols;
+ if (O_ISSET(sp, O_SHOWDIRTY) && F_ISSET(ep, F_MODIFIED))
+ --endpoint;
+
+#define MODESIZE 9
+ if (O_ISSET(sp, O_SHOWMODE))
+ endpoint -= MAX_MODE_NAME;
+
+ if (endpoint < curlen + 2)
+ return (0);
+
+ MOVE(sp, INFOLINE(sp), endpoint);
+ if (O_ISSET(sp, O_SHOWDIRTY) && F_ISSET(ep, F_MODIFIED))
+ ADDSTR("*");
+ if (O_ISSET(sp, O_SHOWMODE))
+ ADDSTR(sp->showmode);
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c
new file mode 100644
index 0000000..31575b9
--- /dev/null
+++ b/usr.bin/vi/svi/svi_relative.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_relative.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+static size_t svi_screens
+ __P((SCR *, EXF *, char *, size_t, recno_t, size_t *));
+
+/*
+ * svi_column --
+ * Return the logical column of the cursor.
+ */
+int
+svi_column(sp, ep, cp)
+ SCR *sp;
+ EXF *ep;
+ size_t *cp;
+{
+ size_t col;
+
+ col = SVP(sp)->sc_col;
+ if (O_ISSET(sp, O_NUMBER))
+ col -= O_NUMBER_LENGTH;
+ *cp = col;
+ return (0);
+}
+
+/*
+ * svi_opt_screens --
+ * Return the screen columns necessary to display the line, or
+ * if specified, the physical character column within the line,
+ * including space required for the O_NUMBER and O_LIST options.
+ */
+size_t
+svi_opt_screens(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t cols, screens;
+
+ /*
+ * Check for a cached value. We maintain a cache because, if the
+ * line is large, this routine gets called repeatedly. One other
+ * hack, lots of time the cursor is on column one, which is an easy
+ * one.
+ */
+ if (cnop == NULL) {
+ if (SVP(sp)->ss_lno == lno)
+ return (SVP(sp)->ss_screens);
+ } else if (*cnop == 0)
+ return (1);
+
+ /* Figure out how many columns the line/column needs. */
+ cols = svi_screens(sp, ep, NULL, 0, lno, cnop);
+
+ /* Leading number if O_NUMBER option set. */
+ if (O_ISSET(sp, O_NUMBER))
+ cols += O_NUMBER_LENGTH;
+
+ /* Trailing '$' if O_LIST option set. */
+ if (O_ISSET(sp, O_LIST) && cnop == NULL)
+ cols += KEY_LEN(sp, '$');
+
+ screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+ if (screens == 0)
+ screens = 1;
+
+ /* Cache the value. */
+ if (cnop == NULL) {
+ SVP(sp)->ss_lno = lno;
+ SVP(sp)->ss_screens = screens;
+ }
+ return (screens);
+}
+
+/*
+ * svi_screens --
+ * Return the screen columns necessary to display the line, or,
+ * if specified, the physical character column within the line.
+ */
+static size_t
+svi_screens(sp, ep, lp, llen, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ char *lp;
+ size_t llen;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t chlen, cno, len, scno, tab_off;
+ int ch, listset;
+ char *p;
+
+ /* Need the line to go any further. */
+ if (lp == NULL)
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+#define SET_CHLEN { \
+ chlen = (ch = *(u_char *)p++) == '\t' && \
+ !listset ? TAB_OFF(sp, tab_off) : KEY_LEN(sp, ch); \
+}
+#define TAB_RESET { \
+ /* \
+ * If past the end of the screen, and the character was a tab, \
+ * reset the screen column to 0. Otherwise, display the rest \
+ * of the character on the next line. \
+ */ \
+ if ((tab_off += chlen) >= sp->cols) \
+ if (ch == '\t') { \
+ tab_off = 0; \
+ scno -= scno % sp->cols; \
+ } else \
+ tab_off -= sp->cols; \
+}
+ p = lp;
+ len = llen;
+ scno = tab_off = 0;
+ if (cnop == NULL)
+ while (len--) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ }
+ else
+ for (cno = *cnop; len--; --cno) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ if (cno == 0)
+ break;
+ }
+ return (scno);
+}
+
+/*
+ * svi_rcm --
+ * Return the physical column from the line that will display a
+ * character closest to the currently most attractive character
+ * position (which is stored as a screen column).
+ */
+size_t
+svi_rcm(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ size_t len;
+
+ /* Last character is easy, and common. */
+ if (sp->rcm_last)
+ return (file_gline(sp,
+ ep, lno, &len) == NULL || len == 0 ? 0 : len - 1);
+
+ /* First character is easy, and common. */
+ if (HMAP->off == 1 && sp->rcm == 0)
+ return (0);
+
+ /*
+ * Get svi_cm_private() to do the hard work. If doing leftright
+ * scrolling, we use the current screen offset, otherwise, use
+ * the first screen, i.e. an offset of 1.
+ *
+ * XXX
+ * I'm not sure that an offset of 1 is right. What happens is that
+ * the vi main loop calls us for the VM_RCM case. By using an offset
+ * of 1, we're assuming that every VM_RCM command changes lines, and
+ * that we want to position on the first screen for that line. This
+ * is currently the way it works, but it's not clean. I'd prefer it if
+ * we could find the SMAP entry the cursor references, and use that
+ * screen offset. Unfortunately, that's not going to be easy, as we
+ * don't keep that information around and it may be expensive to get.
+ */
+ return (svi_cm_private(sp, ep, lno,
+ O_ISSET(sp, O_LEFTRIGHT) ? HMAP->off : 1, sp->rcm));
+}
+
+/*
+ * svi_cm_public --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column.
+ *
+ * The extra interface is because it's called by vi, which doesn't
+ * have a handle on the SMAP structure.
+ */
+size_t
+svi_cm_public(sp, ep, lno, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t cno;
+{
+ return (svi_cm_private(sp, ep, lno, HMAP->off, cno));
+}
+
+/*
+ * svi_cm_private --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column, taking into
+ * account the screen offset.
+ *
+ * The offset is for the commands that move logical distances, i.e.
+ * if it's a logical scroll the closest physical distance is based
+ * on the logical line, not the physical line.
+ */
+size_t
+svi_cm_private(sp, ep, lno, off, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t off, cno;
+{
+ size_t chlen, len, llen, scno, tab_off;
+ int ch, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+ /* Discard screen (logical) lines. */
+ for (scno = 0, p = lp, len = llen; --off;) {
+ for (; len && scno < sp->cols; --len)
+ scno += (ch = *(u_char *)p++) == '\t' &&
+ !listset ? TAB_OFF(sp, scno) : KEY_LEN(sp, ch);
+
+ /*
+ * If reached the end of the physical line, return
+ * the last physical character in the line.
+ */
+ if (len == 0)
+ return (llen - 1);
+
+ /*
+ * If the character was a tab, reset the screen column to 0.
+ * Otherwise, the rest of the character is displayed on the
+ * next line.
+ */
+ if (ch == '\t')
+ scno = 0;
+ else
+ scno -= sp->cols;
+ }
+
+ /* Step through the line until reach the right character or EOL. */
+ for (tab_off = scno; len--;) {
+ SET_CHLEN;
+
+ /*
+ * If we've reached the specific character, there are three
+ * cases.
+ *
+ * 1: scno == cno, i.e. the current character ends at the
+ * screen character we care about.
+ * a: off < llen - 1, i.e. not the last character in
+ * the line, return the offset of the next character.
+ * b: else return the offset of the last character.
+ * 2: scno != cno, i.e. this character overruns the character
+ * we care about, return the offset of this character.
+ */
+ if ((scno += chlen) >= cno) {
+ off = p - lp;
+ return (scno == cno ?
+ (off < llen - 1 ? off : llen - 1) : off - 1);
+ }
+
+ TAB_RESET;
+ }
+
+ /* No such character; return the start of the last character. */
+ return (llen - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.c b/usr.bin/vi/svi/svi_screen.c
new file mode 100644
index 0000000..f7d3f88
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_screen.c 8.94 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * svi_screen_init --
+ * Initialize a screen.
+ */
+int
+svi_screen_init(sp)
+ SCR *sp;
+{
+ /* Initialize support routines. */
+ sp->s_bell = svi_bell;
+ sp->s_bg = svi_bg;
+ sp->s_busy = svi_busy;
+ sp->s_change = svi_change;
+ sp->s_clear = svi_clear;
+ sp->s_colpos = svi_cm_public;
+ sp->s_column = svi_column;
+ sp->s_confirm = svi_confirm;
+ sp->s_crel = svi_crel;
+ sp->s_edit = svi_screen_edit;
+ sp->s_end = svi_screen_end;
+ sp->s_ex_cmd = svi_ex_cmd;
+ sp->s_ex_run = svi_ex_run;
+ sp->s_ex_write = svi_ex_write;
+ sp->s_fg = svi_fg;
+ sp->s_fill = svi_sm_fill;
+ sp->s_get = svi_get;
+ sp->s_key_read = sex_key_read;
+ sp->s_optchange = svi_optchange;
+ sp->s_fmap = svi_fmap;
+ sp->s_position = svi_sm_position;
+ sp->s_rabs = svi_rabs;
+ sp->s_rcm = svi_rcm;
+ sp->s_refresh = svi_refresh;
+ sp->s_scroll = svi_sm_scroll;
+ sp->s_split = svi_split;
+ sp->s_suspend = svi_suspend;
+ sp->s_window = sex_window;
+
+ return (0);
+}
+
+/*
+ * svi_screen_copy --
+ * Copy to a new screen.
+ */
+int
+svi_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ SVI_PRIVATE *osvi, *nsvi;
+
+ /* Create the private screen structure. */
+ CALLOC_RET(orig, nsvi, SVI_PRIVATE *, 1, sizeof(SVI_PRIVATE));
+ sp->svi_private = nsvi;
+
+/* INITIALIZED AT SCREEN CREATE. */
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(nsvi);
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ if (orig == NULL) {
+ } else {
+ osvi = SVP(orig);
+ nsvi->srows = osvi->srows;
+ if (osvi->VB != NULL && (nsvi->VB = strdup(osvi->VB)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ F_SET(nsvi, F_ISSET(osvi, SVI_CURSES_INIT));
+ }
+ return (0);
+}
+
+/*
+ * svi_screen_end --
+ * End a screen.
+ */
+int
+svi_screen_end(sp)
+ SCR *sp;
+{
+ SVI_PRIVATE *svp;
+
+ svp = SVP(sp);
+
+ /* Free the screen map. */
+ if (HMAP != NULL)
+ FREE(HMAP, SIZE_HMAP(sp) * sizeof(SMAP));
+
+ /* Free the visual bell string. */
+ if (svp->VB != NULL)
+ free(svp->VB);
+
+ /* Free private memory. */
+ FREE(svp, sizeof(SVI_PRIVATE));
+ sp->svi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * We use a single curses "window" for each vi screen. The model would be
+ * simpler with two windows (one for the text, and one for the modeline)
+ * because scrolling the text window down would work correctly then, not
+ * affecting the mode line. As it is we have to play games to make it look
+ * right. The reason for this choice is that it would be difficult for
+ * curses to optimize the movement, i.e. detect that the downward scroll
+ * isn't going to change the modeline, set the scrolling region on the
+ * terminal and only scroll the first part of the text window. (Even if
+ * curses did detect it, the set-scrolling-region terminal commands can't
+ * be used by curses because it's indeterminate where the cursor ends up
+ * after they are sent.)
+ */
+/*
+ * svi_screen_edit --
+ * Main vi curses screen loop.
+ */
+int
+svi_screen_edit(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ SCR *tsp;
+ int ecurses, escreen, force, rval;
+
+ escreen = ecurses = rval = 0;
+
+ /* Initialize curses. */
+ if (svi_curses_init(sp)) {
+ escreen = 1;
+ goto err;
+ }
+ ecurses = 1;
+
+ /*
+ * The resize bit is probably set, as a result of the terminal being
+ * set. We clear it as we just finished initializing the screen.
+ * However, we will want to fill in the map from scratch, so provide
+ * a line number just in case, and set the reformat flag.
+ */
+ HMAP->lno = 1;
+ F_CLR(sp, S_RESIZE);
+ F_SET(sp, S_REFORMAT);
+
+ /*
+ * The historic 4BSD curses had an uneasy relationship with termcap.
+ * Termcap used a static buffer to hold the terminal information,
+ * which was was then used by the curses functions. We want to use
+ * it too, for lots of random things, but we've put it off until after
+ * svi_curses_init:initscr() was called. Do it now.
+ */
+ if (svi_term_init(sp))
+ goto err;
+
+ for (;;) {
+ /* Reset the cursor. */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * Run vi. If vi fails, svi data structures may be
+ * corrupted, be extremely careful what you free up.
+ */
+ if (vi(sp, sp->ep)) {
+ (void)rcv_sync(sp, sp->ep,
+ RCV_EMAIL | RCV_ENDSESSION | RCV_PRESERVE);
+ escreen = 1;
+ goto err;
+ }
+
+ force = 0;
+ switch (F_ISSET(sp, S_MAJOR_CHANGE)) {
+ case S_EXIT_FORCE:
+ force = 1;
+ /* FALLTHROUGH */
+ case S_EXIT:
+ F_CLR(sp, S_EXIT_FORCE | S_EXIT);
+ if (file_end(sp, sp->ep, force))/* File end. */
+ break;
+ /*
+ * !!!
+ * NB: sp->frp may now be NULL, if it was a tmp file.
+ */
+ (void)svi_join(sp, &tsp); /* Find a new screen. */
+ if (tsp == NULL)
+ (void)svi_swap(sp, &tsp, NULL);
+ if (tsp == NULL) {
+ escreen = 1;
+ goto ret;
+ }
+ (void)screen_end(sp); /* Screen end. */
+ sp = tsp;
+ break;
+ case 0: /* Exit vi mode. */
+ svi_dtoh(sp, "Exit from vi");
+ goto ret;
+ case S_FSWITCH: /* File switch. */
+ F_CLR(sp, S_FSWITCH);
+ F_SET(sp, S_REFORMAT);
+ break;
+ case S_SSWITCH: /* Screen switch. */
+ F_CLR(sp, S_SSWITCH);
+ sp = sp->nextdisp;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ if (0) {
+err: rval = 1;
+ }
+
+ret: if (svi_term_end(sp)) /* Terminal end (uses sp). */
+ rval = 1;
+ if (ecurses && svi_curses_end(sp)) /* Curses end (uses sp). */
+ rval = 1;
+ if (escreen && screen_end(sp)) /* Screen end. */
+ rval = 1;
+ return (rval);
+}
+
+/*
+ * svi_crel --
+ * Change the relative size of the current screen.
+ */
+int
+svi_crel(sp, count)
+ SCR *sp;
+ long count;
+{
+ /* Can't grow beyond the size of the window. */
+ if (count > O_VAL(sp, O_WINDOW))
+ count = O_VAL(sp, O_WINDOW);
+
+ sp->t_minrows = sp->t_rows = count;
+ if (sp->t_rows > sp->rows - 1)
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+ TMAP = HMAP + (sp->t_rows - 1);
+ F_SET(sp, S_REDRAW);
+ return (0);
+}
+
+/*
+ * svi_dtoh --
+ * Move all but the current screen to the hidden queue.
+ */
+void
+svi_dtoh(sp, emsg)
+ SCR *sp;
+ char *emsg;
+{
+ SCR *tsp;
+ int hidden;
+
+ for (hidden = 0;
+ (tsp = sp->gp->dq.cqh_first) != (void *)&sp->gp->dq; ++hidden) {
+ if (_HMAP(tsp) != NULL) {
+ FREE(_HMAP(tsp), SIZE_HMAP(tsp) * sizeof(SMAP));
+ _HMAP(tsp) = NULL;
+ }
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->dq, tsp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->hq, tsp, q);
+ SIGUNBLOCK(sp->gp);
+ }
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_TAIL(&sp->gp->dq, sp, q);
+ SIGUNBLOCK(sp->gp);
+ if (hidden > 1)
+ msgq(sp, M_INFO,
+ "%s backgrounded %d screens; use :display to list the screens",
+ emsg, hidden - 1);
+}
diff --git a/usr.bin/vi/svi/svi_screen.h b/usr.bin/vi/svi/svi_screen.h
new file mode 100644
index 0000000..3b4643d
--- /dev/null
+++ b/usr.bin/vi/svi/svi_screen.h
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)svi_screen.h 8.52 (Berkeley) 7/20/94
+ */
+
+/*
+ * Structure for mapping lines to the screen. An SMAP is an array, with one
+ * structure element per screen line, which holds information describing the
+ * physical line which is displayed in the screen line. The first two fields
+ * (lno and off) are all that are necessary to describe a line. The rest of
+ * the information is useful to keep information from being re-calculated.
+ *
+ * Lno is the line number. Off is the screen offset into the line. For
+ * example, the pair 2:1 would be the first screen of line 2, and 2:2 would
+ * be the second. If doing left-right scrolling, all of the offsets will be
+ * the same, i.e. for the second screen, 1:2, 2:2, 3:2, etc. If doing the
+ * standard vi scrolling, it will be staggered, i.e. 1:1, 1:2, 1:3, 2:1, 3:1,
+ * etc.
+ *
+ * The SMAP is always as large as the physical screen, plus a slot for the
+ * info line, so that there is room to add any screen into another one at
+ * screen exit.
+ */
+typedef struct _smap {
+ recno_t lno; /* 1-N: Physical file line number. */
+ size_t off; /* 1-N: Screen offset in the line. */
+
+ /* svi_line() cache information. */
+ size_t c_sboff; /* 0-N: offset of first character byte. */
+ size_t c_eboff; /* 0-N: offset of last character byte. */
+ u_char c_scoff; /* 0-N: offset into the first character. */
+ u_char c_eclen; /* 1-N: columns from the last character. */
+ u_char c_ecsize; /* 1-N: size of the last character. */
+} SMAP;
+
+ /* Macros to flush/test cached information. */
+#define SMAP_CACHE(smp) ((smp)->c_ecsize != 0)
+#define SMAP_FLUSH(smp) ((smp)->c_ecsize = 0)
+
+typedef struct _svi_private {
+/* INITIALIZED AT SCREEN CREATE. */
+ SMAP *h_smap; /* First slot of the line map. */
+ SMAP *t_smap; /* Last slot of the line map. */
+
+ size_t exlinecount; /* Ex overwrite count. */
+ size_t extotalcount; /* Ex overwrite count. */
+ size_t exlcontinue; /* Ex line continue value. */
+
+ /* svi_opt_screens() cache information. */
+#define SVI_SCR_CFLUSH(svp) svp->ss_lno = OOBLNO
+ recno_t ss_lno; /* 1-N: Line number. */
+ size_t ss_screens; /* Return value. */
+
+ recno_t olno; /* 1-N: old cursor file line. */
+ size_t ocno; /* 0-N: old file cursor column. */
+ size_t sc_col; /* 0-N: LOGICAL screen column. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ size_t srows; /* 1-N: Rows in the terminal/window. */
+
+ char *VB; /* Visual bell termcap string. */
+
+#define SVI_CURSES_INIT 0x001 /* Curses/termcap initialized. */
+#define SVI_CUR_INVALID 0x002 /* Cursor position is unknown. */
+#define SVI_DIVIDER 0x004 /* Screen divider is displayed. */
+#define SVI_INFOLINE 0x008 /* The infoline is being used by v_ntext(). */
+#define SVI_SCREENDIRTY 0x010 /* Screen needs refreshing. */
+ u_int8_t flags;
+} SVI_PRIVATE;
+
+#define SVP(sp) ((SVI_PRIVATE *)((sp)->svi_private))
+#define HMAP (SVP(sp)->h_smap)
+#define TMAP (SVP(sp)->t_smap)
+#define _HMAP(sp) (SVP(sp)->h_smap)
+#define _TMAP(sp) (SVP(sp)->t_smap)
+
+/*
+ * One extra slot is always allocated for the map so that we can use
+ * it to do vi :colon command input; see svi_get().
+ */
+#define SIZE_HMAP(sp) (SVP(sp)->srows + 1)
+
+#define O_NUMBER_FMT "%7lu " /* O_NUMBER format, length. */
+#define O_NUMBER_LENGTH 8
+ /* Columns on a screen. */
+#define SCREEN_COLS(sp) \
+ ((O_ISSET(sp, O_NUMBER) ? (sp)->cols - O_NUMBER_LENGTH : (sp)->cols))
+
+#define HALFSCREEN(sp) ((sp)->t_maxrows / 2) /* Half the screen. */
+#define HALFTEXT(sp) ((sp)->t_rows / 2) /* Half the text. */
+
+#define INFOLINE(sp) ((sp)->t_maxrows) /* Info line test, offset. */
+#define ISINFOLINE(sp, smp) (((smp) - HMAP) == INFOLINE(sp))
+
+ /* Small screen test. */
+#define ISSMALLSCREEN(sp) ((sp)->t_minrows != (sp)->t_maxrows)
+
+/*
+ * Next tab offset.
+ *
+ * !!!
+ * There are problems with how the historical vi handled tabs. For example,
+ * by doing "set ts=3" and building lines that fold, you can get it to step
+ * through tabs as if they were spaces and move inserted characters to new
+ * positions when <esc> is entered. I think that nvi does tabs correctly,
+ * but there may be some historical incompatibilities.
+ */
+#define TAB_OFF(sp, c) (O_VAL(sp, O_TABSTOP) - (c) % O_VAL(sp, O_TABSTOP))
+
+/* Move in a screen (absolute), and fail if it doesn't work. */
+#ifdef DEBUG
+#define MOVEA(sp, lno, cno) { \
+ if (move(lno, cno) == ERR) { \
+ msgq(sp, M_ERR, \
+ "Error: %s/%d: move:l(%u), c(%u), abs", \
+ tail(__FILE__), __LINE__, lno, cno); \
+ return (1); \
+ } \
+}
+#else
+#define MOVEA(sp, lno, cno) (void)move(lno, cno)
+#endif
+
+/* Move in a window, and fail if it doesn't work. */
+#ifdef DEBUG
+#define MOVE(sp, lno, cno) { \
+ size_t __lno = (sp)->woff + (lno); \
+ if (move(__lno, cno) == ERR) { \
+ msgq(sp, M_ERR, \
+ "Error: %s/%d: move:l(%u), c(%u), o(%u)", \
+ tail(__FILE__), __LINE__, lno, cno, sp->woff); \
+ return (1); \
+ } \
+}
+#else
+#define MOVE(sp, lno, cno) (void)move((sp)->woff + (lno), cno)
+#endif
+
+/* Add a character. */
+#define ADDCH(ch) { \
+ CHAR_T __ch = ch; \
+ ADDNSTR(KEY_NAME(sp, __ch), KEY_LEN(sp, __ch)); \
+}
+
+/* Add a string len bytes long. */
+#ifdef DEBUG
+#define ADDNSTR(str, len) { \
+ if (addnstr(str, len) == ERR) { \
+ int __x, __y; \
+ getyx(stdscr, __y, __x); \
+ msgq(sp, M_ERR, "Error: %s/%d: addnstr: (%d/%u)", \
+ tail(__FILE__), __LINE__, __y, __x); \
+ return (1); \
+ } \
+}
+#else
+#define ADDNSTR(str, len) (void)addnstr(str, len)
+#endif
+
+/* Add a string. */
+#ifdef DEBUG
+#define ADDSTR(str) { \
+ if (addstr(str) == ERR) { \
+ int __x, __y; \
+ getyx(stdscr, __y, __x); \
+ msgq(sp, M_ERR, "Error: %s/%d: addstr: (%d/%u)", \
+ tail(__FILE__), __LINE__, __y, __x); \
+ return (1); \
+ } \
+}
+#else
+#define ADDSTR(str) (void)addstr(str);
+#endif
+
+/* Public routines. */
+void svi_bell __P((SCR *));
+int svi_bg __P((SCR *));
+int svi_busy __P((SCR *, char const *));
+int svi_change __P((SCR *, EXF *, recno_t, enum operation));
+size_t svi_cm_public __P((SCR *, EXF *, recno_t, size_t));
+int svi_column __P((SCR *, EXF *, size_t *));
+enum confirm
+ svi_confirm __P((SCR *, EXF *, MARK *, MARK *));
+int svi_clear __P((SCR *));
+int svi_crel __P((SCR *, long));
+int svi_ex_cmd __P((SCR *, EXF *, struct _excmdarg *, MARK *));
+int svi_ex_run __P((SCR *, EXF *, MARK *));
+int svi_ex_write __P((void *, const char *, int));
+int svi_fg __P((SCR *, CHAR_T *));
+int svi_fmap __P((SCR *, enum seqtype, CHAR_T *, size_t, CHAR_T *, size_t));
+enum input
+ svi_get __P((SCR *, EXF *, TEXTH *, ARG_CHAR_T, u_int));
+int svi_optchange __P((SCR *, int));
+int svi_rabs __P((SCR *, long, enum adjust));
+size_t svi_rcm __P((SCR *, EXF *, recno_t));
+int svi_refresh __P((SCR *, EXF *));
+int svi_screen_copy __P((SCR *, SCR *));
+int svi_screen_edit __P((SCR *, EXF *));
+int svi_screen_end __P((SCR *));
+int svi_sm_fill __P((SCR *, EXF *, recno_t, enum position));
+int svi_sm_position __P((SCR *, EXF *, MARK *, u_long, enum position));
+int svi_sm_scroll __P((SCR *, EXF *, MARK *, recno_t, enum sctype));
+int svi_split __P((SCR *, ARGS *[], int));
+int svi_suspend __P((SCR *));
+int svi_swap __P((SCR *, SCR **, char *));
+
+/* Private routines. */
+size_t svi_cm_private __P((SCR *, EXF *, recno_t, size_t, size_t));
+int svi_curses_end __P((SCR *));
+int svi_curses_init __P((SCR *));
+void svi_dtoh __P((SCR *, char *));
+int svi_init __P((SCR *));
+int svi_join __P((SCR *, SCR **));
+void svi_keypad __P((SCR *, int));
+int svi_line __P((SCR *, EXF *, SMAP *, size_t *, size_t *));
+int svi_msgflush __P((SCR *));
+int svi_number __P((SCR *, EXF *));
+size_t svi_opt_screens __P((SCR *, EXF *, recno_t, size_t *));
+int svi_paint __P((SCR *, EXF *));
+int svi_sm_1down __P((SCR *, EXF *));
+int svi_sm_1up __P((SCR *, EXF *));
+int svi_sm_cursor __P((SCR *, EXF *, SMAP **));
+int svi_sm_next __P((SCR *, EXF *, SMAP *, SMAP *));
+recno_t svi_sm_nlines __P((SCR *, EXF *, SMAP *, recno_t, size_t));
+int svi_sm_prev __P((SCR *, EXF *, SMAP *, SMAP *));
+int svi_term_end __P((SCR *sp));
+int svi_term_init __P((SCR *sp));
+
+/* Private debugging routines. */
+#ifdef DEBUG
+int svi_gdbrefresh __P((void));
+#endif
diff --git a/usr.bin/vi/svi/svi_smap.c b/usr.bin/vi/svi/svi_smap.c
new file mode 100644
index 0000000..a9205b1
--- /dev/null
+++ b/usr.bin/vi/svi/svi_smap.c
@@ -0,0 +1,1216 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_smap.c 8.48 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "svi_screen.h"
+
+static int svi_deleteln __P((SCR *, int));
+static int svi_insertln __P((SCR *, int));
+static int svi_sm_delete __P((SCR *, EXF *, recno_t));
+static int svi_sm_down __P((SCR *, EXF *,
+ MARK *, recno_t, enum sctype, SMAP *));
+static int svi_sm_erase __P((SCR *));
+static int svi_sm_insert __P((SCR *, EXF *, recno_t));
+static int svi_sm_reset __P((SCR *, EXF *, recno_t));
+static int svi_sm_up __P((SCR *, EXF *,
+ MARK *, recno_t, enum sctype, SMAP *));
+
+/*
+ * svi_change --
+ * Make a change to the screen.
+ */
+int
+svi_change(sp, ep, lno, op)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum operation op;
+{
+ SMAP *p;
+ size_t oldy, oldx;
+
+ /* Appending is the same as inserting, if the line is incremented. */
+ if (op == LINE_APPEND) {
+ ++lno;
+ op = LINE_INSERT;
+ }
+
+ /* Ignore the change if the line is after the map. */
+ if (lno > TMAP->lno)
+ return (0);
+
+ /*
+ * If the line is before the map, and it's a decrement, decrement
+ * the map. If it's an increment, increment the map. Otherwise,
+ * ignore it.
+ */
+ if (lno < HMAP->lno) {
+ switch (op) {
+ case LINE_APPEND:
+ abort();
+ /* NOTREACHED */
+ case LINE_DELETE:
+ for (p = HMAP; p <= TMAP; ++p)
+ --p->lno;
+ if (sp->lno >= lno)
+ --sp->lno;
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_INSERT:
+ for (p = HMAP; p <= TMAP; ++p)
+ ++p->lno;
+ if (sp->lno >= lno)
+ ++sp->lno;
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_RESET:
+ break;
+ }
+ return (0);
+ }
+
+ F_SET(SVP(sp), SVI_SCREENDIRTY);
+
+ /* Invalidate the cursor, if it's on this line. */
+ if (sp->lno == lno)
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* Invalidate the line size cache. */
+ SVI_SCR_CFLUSH(SVP(sp));
+
+ getyx(stdscr, oldy, oldx);
+
+ switch (op) {
+ case LINE_DELETE:
+ if (svi_sm_delete(sp, ep, lno))
+ return (1);
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_INSERT:
+ if (svi_sm_insert(sp, ep, lno))
+ return (1);
+ F_SET(sp, S_RENUMBER);
+ break;
+ case LINE_RESET:
+ if (svi_sm_reset(sp, ep, lno))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ MOVEA(sp, oldy, oldx);
+
+ return (0);
+}
+
+/*
+ * svi_sm_fill --
+ * Fill in the screen map, placing the specified line at the
+ * right position. There isn't any way to tell if an SMAP
+ * entry has been filled in, so this routine had better be
+ * called with P_FILL set before anything else is done.
+ *
+ * !!!
+ * Unexported interface: if lno is OOBLNO, P_TOP means that the HMAP
+ * slot is already filled in, P_BOTTOM means that the TMAP slot is
+ * already filled in, and we just finish up the job.
+ */
+int
+svi_sm_fill(sp, ep, lno, pos)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ enum position pos;
+{
+ SMAP *p, tmp;
+
+ /* Flush all cached information from the SMAP. */
+ for (p = HMAP; p <= TMAP; ++p)
+ SMAP_FLUSH(p);
+
+ /* If the map is filled, the screen must be redrawn. */
+ F_SET(sp, S_REDRAW);
+
+ switch (pos) {
+ case P_FILL:
+ tmp.lno = 1;
+ tmp.off = 1;
+
+ /* See if less than half a screen from the top. */
+ if (svi_sm_nlines(sp, ep,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* See if less than half a screen from the bottom. */
+ if (file_lline(sp, ep, &tmp.lno))
+ return (1);
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ tmp.off = svi_opt_screens(sp, ep, tmp.lno, NULL);
+ if (svi_sm_nlines(sp, ep,
+ &tmp, lno, HALFTEXT(sp)) <= HALFTEXT(sp)) {
+ TMAP->lno = tmp.lno;
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ TMAP->off = tmp.off;
+ goto bottom;
+ }
+ goto middle;
+ case P_TOP:
+ if (lno != OOBLNO) {
+top: HMAP->lno = lno;
+ HMAP->off = 1;
+ }
+ /* If we fail, just punt. */
+ for (p = HMAP; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ goto err;
+ break;
+ case P_MIDDLE:
+ /* If we fail, guess that the file is too small. */
+middle: p = HMAP + (TMAP - HMAP) / 2;
+ for (p->lno = lno, p->off = 1; p > HMAP; --p)
+ if (svi_sm_prev(sp, ep, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+
+ /* If we fail, just punt. */
+ p = HMAP + (TMAP - HMAP) / 2;
+ for (; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ goto err;
+ break;
+ case P_BOTTOM:
+ if (lno != OOBLNO) {
+ TMAP->lno = lno;
+ if (!O_ISSET(sp, O_LEFTRIGHT))
+ TMAP->off = svi_opt_screens(sp, ep, lno, NULL);
+ }
+ /* If we fail, guess that the file is too small. */
+bottom: for (p = TMAP; p > HMAP; --p)
+ if (svi_sm_prev(sp, ep, p, p - 1)) {
+ lno = 1;
+ goto top;
+ }
+ break;
+ default:
+ abort();
+ }
+ return (0);
+
+ /*
+ * Try and put *something* on the screen. If this fails,
+ * we have a serious hard error.
+ */
+err: HMAP->lno = 1;
+ HMAP->off = 1;
+ for (p = HMAP; p < TMAP; ++p)
+ if (svi_sm_next(sp, ep, p, p + 1))
+ return (1);
+ return (0);
+}
+
+/*
+ * For the routines svi_sm_reset, svi_sm_delete and svi_sm_insert: if the
+ * screen only contains one line, or, if the line is the entire screen, this
+ * gets fairly exciting. Skip the fun and simply return if there's only one
+ * line in the screen, or just call fill. Fill may not be entirely accurate,
+ * i.e. we may be painting the screen with something not even close to the
+ * cursor, but it's not like we're into serious performance issues here, and
+ * the refresh routine will fix it for us.
+ */
+#define TOO_WEIRD { \
+ if (cnt_orig >= sp->t_rows) { \
+ if (cnt_orig == 1) \
+ return (0); \
+ if (file_gline(sp, ep, lno, NULL) == NULL) \
+ if (file_lline(sp, ep, &lno)) \
+ return (1); \
+ F_SET(sp, S_REDRAW); \
+ return (svi_sm_fill(sp, ep, lno, P_TOP)); \
+ } \
+}
+
+/*
+ * svi_sm_delete --
+ * Delete a line out of the SMAP.
+ */
+static int
+svi_sm_delete(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig;
+
+ /*
+ * Find the line in the map, and count the number of screen lines
+ * which display any part of the deleted line.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ cnt_orig = 1;
+ else
+ for (cnt_orig = 1, t = p + 1;
+ t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+
+ TOO_WEIRD;
+
+ /* Delete that many lines from the screen. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_deleteln(sp, cnt_orig))
+ return (1);
+
+ /* Shift the screen map up. */
+ memmove(p, p + cnt_orig, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
+
+ /* Decrement the line numbers for the rest of the map. */
+ for (t = TMAP - cnt_orig; p <= t; ++p)
+ --p->lno;
+
+ /* Display the new lines. */
+ for (p = TMAP - cnt_orig;;) {
+ if (p < TMAP && svi_sm_next(sp, ep, p, p + 1))
+ return (1);
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, ++p, NULL, NULL))
+ return (1);
+ if (p == TMAP)
+ break;
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_insert --
+ * Insert a line into the SMAP.
+ */
+static int
+svi_sm_insert(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig, cnt;
+
+ /*
+ * Find the line in the map, find out how many screen lines
+ * needed to display the line.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ cnt_orig = 1;
+ else
+ cnt_orig = svi_opt_screens(sp, ep, lno, NULL);
+
+ TOO_WEIRD;
+
+ /*
+ * The lines left in the screen override the number of screen
+ * lines in the inserted line.
+ */
+ cnt = (TMAP - p) + 1;
+ if (cnt_orig > cnt)
+ cnt_orig = cnt;
+
+ /* Push down that many lines. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_insertln(sp, cnt_orig))
+ return (1);
+
+ /* Shift the screen map down. */
+ memmove(p + cnt_orig, p, (((TMAP - p) - cnt_orig) + 1) * sizeof(SMAP));
+
+ /* Increment the line numbers for the rest of the map. */
+ for (t = p + cnt_orig; t <= TMAP; ++t)
+ ++t->lno;
+
+ /* Fill in the SMAP for the new lines, and display. */
+ for (cnt = 1, t = p; cnt <= cnt_orig; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_reset --
+ * Reset a line in the SMAP.
+ */
+static int
+svi_sm_reset(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ SMAP *p, *t;
+ size_t cnt_orig, cnt_new, cnt, diff;
+
+ /*
+ * See if the number of on-screen rows taken up by the old display
+ * for the line is the same as the number needed for the new one.
+ * If so, repaint, otherwise do it the hard way.
+ */
+ for (p = HMAP; p->lno != lno; ++p);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t = p;
+ cnt_orig = cnt_new = 1;
+ } else {
+ for (cnt_orig = 0,
+ t = p; t <= TMAP && t->lno == lno; ++cnt_orig, ++t);
+ cnt_new = svi_opt_screens(sp, ep, lno, NULL);
+ }
+
+ TOO_WEIRD;
+
+ if (cnt_orig == cnt_new) {
+ do {
+ SMAP_FLUSH(p);
+ if (svi_line(sp, ep, p, NULL, NULL))
+ return (1);
+ } while (++p < t);
+ return (0);
+ }
+
+ if (cnt_orig < cnt_new) {
+ /* Get the difference. */
+ diff = cnt_new - cnt_orig;
+
+ /*
+ * The lines left in the screen override the number of screen
+ * lines in the inserted line.
+ */
+ cnt = (TMAP - p) + 1;
+ if (diff > cnt)
+ diff = cnt;
+
+ /* Push down the extra lines. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_insertln(sp, diff))
+ return (1);
+
+ /* Shift the screen map down. */
+ memmove(p + diff, p, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
+
+ /* Fill in the SMAP for the replaced line, and display. */
+ for (cnt = 1, t = p; cnt_new-- && t <= TMAP; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+ } else {
+ /* Get the difference. */
+ diff = cnt_orig - cnt_new;
+
+ /* Delete that many lines from the screen. */
+ MOVE(sp, p - HMAP, 0);
+ if (svi_deleteln(sp, diff))
+ return (1);
+
+ /* Shift the screen map up. */
+ memmove(p, p + diff, (((TMAP - p) - diff) + 1) * sizeof(SMAP));
+
+ /* Fill in the SMAP for the replaced line, and display. */
+ for (cnt = 1, t = p; cnt_new--; ++t, ++cnt) {
+ t->lno = lno;
+ t->off = cnt;
+ SMAP_FLUSH(t);
+ if (svi_line(sp, ep, t, NULL, NULL))
+ return (1);
+ }
+
+ /* Display the new lines at the bottom of the screen. */
+ for (t = TMAP - diff;;) {
+ if (t < TMAP && svi_sm_next(sp, ep, t, t + 1))
+ return (1);
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, ++t, NULL, NULL))
+ return (1);
+ if (t == TMAP)
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_scroll
+ * Scroll the SMAP up/down count logical lines. Different
+ * semantics based on the vi command, *sigh*.
+ */
+int
+svi_sm_scroll(sp, ep, rp, count, scmd)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ enum sctype scmd;
+{
+ SMAP *smp;
+
+ /*
+ * Invalidate the cursor. The line is probably going to change,
+ * (although for ^E and ^Y it may not). In any case, the scroll
+ * routines move the cursor to draw things.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /* Find the cursor in the screen. */
+ if (svi_sm_cursor(sp, ep, &smp))
+ return (1);
+
+ switch (scmd) {
+ case CNTRL_B:
+ case CNTRL_U:
+ case CNTRL_Y:
+ case Z_CARAT:
+ if (svi_sm_down(sp, ep, rp, count, scmd, smp))
+ return (1);
+ break;
+ case CNTRL_D:
+ case CNTRL_E:
+ case CNTRL_F:
+ case Z_PLUS:
+ if (svi_sm_up(sp, ep, rp, count, scmd, smp))
+ return (1);
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * !!!
+ * If we're at the start of a line, go for the first non-blank.
+ * This makes it look like the old vi, even though we're moving
+ * around by logical lines, not physical ones.
+ *
+ * XXX
+ * In the presence of a long line, which has more than a screen
+ * width of leading spaces, this code can cause a cursor warp.
+ * Live with it.
+ */
+ if (scmd != CNTRL_E && scmd != CNTRL_Y &&
+ rp->cno == 0 && nonblank(sp, ep, rp->lno, &rp->cno))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * svi_sm_up --
+ * Scroll the SMAP up count logical lines.
+ */
+static int
+svi_sm_up(sp, ep, rp, count, scmd, smp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ enum sctype scmd;
+ recno_t count;
+ SMAP *smp;
+{
+ int cursor_set, echanged, zset;
+ SMAP s1, s2;
+
+ /*
+ * Check to see if movement is possible.
+ *
+ * Get the line after the map. If that line is a new one (and if
+ * O_LEFTRIGHT option is set, this has to be true), and the next
+ * line doesn't exist, and the cursor doesn't move, or the cursor
+ * isn't even on the screen, or the cursor is already at the last
+ * line in the map, it's an error. If that test succeeded because
+ * the cursor wasn't at the end of the map, test to see if the map
+ * is mostly empty.
+ */
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+ if (s1.lno > TMAP->lno && !file_gline(sp, ep, s1.lno, NULL)) {
+ if (scmd == CNTRL_E || scmd == Z_PLUS || smp == TMAP) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ if (svi_sm_next(sp, ep, smp, &s1))
+ return (1);
+ if (s1.lno > smp->lno && !file_gline(sp, ep, s1.lno, NULL)) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 2b.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves to the
+ * first line displayed. Otherwise, erase/compress and repaint,
+ * and move the cursor to the first line in the screen. Note,
+ * the ^F command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = 0;
+ if (ISSMALLSCREEN(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_F) {
+ s1 = TMAP[0];
+ if (svi_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (svi_sm_next(sp, ep, &s1, &s2))
+ return (1);
+ if (s2.lno != s1.lno &&
+ !file_gline(sp, ep, s2.lno, NULL))
+ break;
+ }
+ TMAP[0] = s2;
+ if (svi_sm_fill(sp, ep, OOBLNO, P_BOTTOM))
+ return (1);
+ return (svi_sm_position(sp, ep, rp, 0, P_TOP));
+ }
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+ if (TMAP->lno != s1.lno &&
+ !file_gline(sp, ep, s1.lno, NULL))
+ break;
+ *++TMAP = s1;
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+
+ if (scmd != CNTRL_E && !cursor_set) {
+ cursor_set = 1;
+ rp->lno = TMAP->lno;
+ rp->cno = TMAP->c_sboff;
+ }
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (echanged = zset = 0; count; --count) {
+ /* Decide what would show up on the screen. */
+ if (svi_sm_next(sp, ep, TMAP, &s1))
+ return (1);
+
+ /* If the line doesn't exist, we're done. */
+ if (TMAP->lno != s1.lno && !file_gline(sp, ep, s1.lno, NULL))
+ break;
+
+ /* Scroll the screen cursor up one logical line. */
+ if (svi_sm_1up(sp, ep))
+ return (1);
+ switch (scmd) {
+ case CNTRL_E:
+ if (smp > HMAP)
+ --smp;
+ else
+ echanged = 1;
+ break;
+ case Z_PLUS:
+ if (zset) {
+ if (smp > HMAP)
+ --smp;
+ } else {
+ smp = TMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_E:
+ /*
+ * On a ^E that was forced to change lines, try and keep the
+ * cursor as close as possible to the last position, but also
+ * set it up so that the next "real" movement will return the
+ * cursor to the closest position to the last real movement.
+ */
+ if (echanged) {
+ rp->lno = smp->lno;
+ rp->cno =
+ svi_cm_private(sp, ep, smp->lno, smp->off, sp->rcm);
+ }
+ return (0);
+ case CNTRL_F:
+ /*
+ * If there are more lines, the ^F command is always
+ * positioned at the first line of the screen.
+ */
+ if (!count) {
+ smp = HMAP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_D:
+ /*
+ * The ^D and ^F commands move the cursor towards EOF
+ * if there are more lines to move. Check to be sure
+ * the lines actually exist. (They may not if the
+ * file is smaller than the screen.)
+ */
+ for (; count; --count, ++smp)
+ if (smp == TMAP ||
+ !file_gline(sp, ep, smp[1].lno, NULL))
+ break;
+ break;
+ case Z_PLUS:
+ /* The z+ command moves the cursor to the first new line. */
+ break;
+ default:
+ abort();
+ }
+
+ if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+ return (0);
+}
+
+/*
+ * svi_sm_1up --
+ * Scroll the SMAP up one.
+ */
+int
+svi_sm_1up(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Delete the top line of the screen. Shift the screen map up.
+ * Display a new line at the bottom of the screen.
+ */
+ MOVE(sp, 0, 0);
+ if (svi_deleteln(sp, 1))
+ return (1);
+
+ /* One-line screens can fail. */
+ if (HMAP == TMAP) {
+ if (svi_sm_next(sp, ep, TMAP, TMAP))
+ return (1);
+ } else {
+ memmove(HMAP, HMAP + 1, (sp->rows - 1) * sizeof(SMAP));
+ if (svi_sm_next(sp, ep, TMAP - 1, TMAP))
+ return (1);
+ }
+ /* svi_sm_next() flushed the cache. */
+ if (svi_line(sp, ep, TMAP, NULL, NULL))
+ return (1);
+ return (0);
+}
+
+/*
+ * svi_deleteln --
+ * Delete a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+svi_deleteln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ size_t oldy, oldx;
+
+ getyx(stdscr, oldy, oldx);
+ while (cnt--) {
+ deleteln();
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ insertln();
+ MOVEA(sp, oldy, oldx);
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_down --
+ * Scroll the SMAP down count logical lines.
+ */
+static int
+svi_sm_down(sp, ep, rp, count, scmd, smp)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ recno_t count;
+ SMAP *smp;
+ enum sctype scmd;
+{
+ SMAP s1, s2;
+ int cursor_set, ychanged, zset;
+
+ /* Check to see if movement is possible. */
+ if (HMAP->lno == 1 && HMAP->off == 1 &&
+ (scmd == CNTRL_Y || scmd == Z_CARAT || smp == HMAP)) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 2b.
+ *
+ * If it's a small screen, and the movement isn't larger than a
+ * screen, i.e some context will remain, open up the screen and
+ * display by scrolling. In this case, the cursor moves to the
+ * first line displayed. Otherwise, erase/compress and repaint,
+ * and move the cursor to the first line in the screen. Note,
+ * the ^B command is always in the latter case, for historical
+ * reasons.
+ */
+ cursor_set = scmd == CNTRL_Y;
+ if (ISSMALLSCREEN(sp)) {
+ if (count >= sp->t_maxrows || scmd == CNTRL_B) {
+ s1 = HMAP[0];
+ if (svi_sm_erase(sp))
+ return (1);
+ for (; count--; s1 = s2) {
+ if (svi_sm_prev(sp, ep, &s1, &s2))
+ return (1);
+ if (s2.lno == 1 && s2.off == 1)
+ break;
+ }
+ HMAP[0] = s2;
+ if (svi_sm_fill(sp, ep, OOBLNO, P_TOP))
+ return (1);
+ return (svi_sm_position(sp, ep, rp, 0, P_BOTTOM));
+ }
+ for (; count &&
+ sp->t_rows != sp->t_maxrows; --count, ++sp->t_rows) {
+ if (HMAP->lno == 1 || HMAP->off == 1)
+ break;
+ ++TMAP;
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ if (scmd != CNTRL_Y && !cursor_set) {
+ cursor_set = 1;
+ if (svi_sm_position(sp, ep, rp, 0, P_BOTTOM))
+ return (1);
+ }
+ }
+ if (count == 0)
+ return (0);
+ }
+
+ for (ychanged = zset = 0; count; --count) {
+ /* If the line doesn't exist, we're done. */
+ if (HMAP->lno == 1 && HMAP->off == 1)
+ break;
+
+ /* Scroll the screen and cursor down one logical line. */
+ if (svi_sm_1down(sp, ep))
+ return (1);
+ switch (scmd) {
+ case CNTRL_Y:
+ if (smp < TMAP)
+ ++smp;
+ else
+ ychanged = 1;
+ break;
+ case Z_CARAT:
+ if (zset) {
+ if (smp < TMAP)
+ ++smp;
+ } else {
+ smp = HMAP;
+ zset = 1;
+ }
+ /* FALLTHROUGH */
+ default:
+ break;
+ }
+ }
+
+ if (scmd != CNTRL_Y && cursor_set)
+ return(0);
+
+ switch (scmd) {
+ case CNTRL_B:
+ /*
+ * If there are more lines, the ^B command is always
+ * positioned at the last line of the screen.
+ */
+ if (!count) {
+ smp = TMAP;
+ break;
+ }
+ /* FALLTHROUGH */
+ case CNTRL_U:
+ /*
+ * The ^B and ^U commands move the cursor towards SOF
+ * if there are more lines to move.
+ */
+ if (count < smp - HMAP)
+ smp -= count;
+ else
+ smp = HMAP;
+ break;
+ case CNTRL_Y:
+ /*
+ * On a ^Y that was forced to change lines, try and keep the
+ * cursor as close as possible to the last position, but also
+ * set it up so that the next "real" movement will return the
+ * cursor to the closest position to the last real movement.
+ */
+ if (ychanged) {
+ rp->lno = smp->lno;
+ rp->cno =
+ svi_cm_private(sp, ep, smp->lno, smp->off, sp->rcm);
+ }
+ return (0);
+ case Z_CARAT:
+ /* The z^ command moves the cursor to the first new line. */
+ break;
+ default:
+ abort();
+ }
+
+ if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+ return (0);
+}
+
+/*
+ * svi_sm_erase --
+ * Erase the small screen area for the scrolling functions.
+ */
+static int
+svi_sm_erase(sp)
+ SCR *sp;
+{
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ for (; sp->t_rows > sp->t_minrows; --sp->t_rows, --TMAP) {
+ MOVE(sp, TMAP - HMAP, 0);
+ clrtoeol();
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_1down --
+ * Scroll the SMAP down one.
+ */
+int
+svi_sm_1down(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ /*
+ * Clear the bottom line of the screen, insert a line at the top
+ * of the screen. Shift the screen map down, display a new line
+ * at the top of the screen.
+ */
+ MOVE(sp, sp->t_rows, 0);
+ clrtoeol();
+ MOVE(sp, 0, 0);
+ if (svi_insertln(sp, 1))
+ return (1);
+ memmove(HMAP + 1, HMAP, (sp->rows - 1) * sizeof(SMAP));
+ if (svi_sm_prev(sp, ep, HMAP + 1, HMAP))
+ return (1);
+ /* svi_sm_prev() flushed the cache. */
+ if (svi_line(sp, ep, HMAP, NULL, NULL))
+ return (1);
+ return (0);
+}
+
+/*
+ * svi_insertln --
+ * Insert a line a la curses, make sure to put the information
+ * line and other screens back.
+ */
+static int
+svi_insertln(sp, cnt)
+ SCR *sp;
+ int cnt;
+{
+ size_t oldy, oldx;
+
+ getyx(stdscr, oldy, oldx);
+ while (cnt--) {
+ MOVE(sp, INFOLINE(sp) - 1, 0);
+ deleteln();
+ MOVEA(sp, oldy, oldx);
+ insertln();
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_next --
+ * Fill in the next entry in the SMAP.
+ */
+int
+svi_sm_next(sp, ep, p, t)
+ SCR *sp;
+ EXF *ep;
+ SMAP *p, *t;
+{
+ size_t lcnt;
+
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno + 1;
+ t->off = p->off;
+ } else {
+ lcnt = svi_opt_screens(sp, ep, p->lno, NULL);
+ if (lcnt == p->off) {
+ t->lno = p->lno + 1;
+ t->off = 1;
+ } else {
+ t->lno = p->lno;
+ t->off = p->off + 1;
+ }
+ }
+ return (0);
+}
+
+/*
+ * svi_sm_prev --
+ * Fill in the previous entry in the SMAP.
+ */
+int
+svi_sm_prev(sp, ep, p, t)
+ SCR *sp;
+ EXF *ep;
+ SMAP *p, *t;
+{
+ SMAP_FLUSH(t);
+ if (O_ISSET(sp, O_LEFTRIGHT)) {
+ t->lno = p->lno - 1;
+ t->off = p->off;
+ } else if (p->off != 1) {
+ t->lno = p->lno;
+ t->off = p->off - 1;
+ } else {
+ t->lno = p->lno - 1;
+ t->off = svi_opt_screens(sp, ep, t->lno, NULL);
+ }
+ return (t->lno == 0);
+}
+
+/*
+ * svi_sm_cursor --
+ * Return the SMAP entry referenced by the cursor.
+ */
+int
+svi_sm_cursor(sp, ep, smpp)
+ SCR *sp;
+ EXF *ep;
+ SMAP **smpp;
+{
+ SMAP *p;
+
+ /* See if the cursor is not in the map. */
+ if (sp->lno < HMAP->lno || sp->lno > TMAP->lno)
+ return (1);
+
+ /* Find the first occurence of the line. */
+ for (p = HMAP; p->lno != sp->lno; ++p);
+
+ /* Fill in the map information until we find the right line. */
+ for (; p <= TMAP; ++p) {
+ /* Short lines are common and easy to detect. */
+ if (p != TMAP && (p + 1)->lno != p->lno) {
+ *smpp = p;
+ return (0);
+ }
+ if (!SMAP_CACHE(p) && svi_line(sp, ep, p, NULL, NULL))
+ return (1);
+ if (p->c_eboff >= sp->cno) {
+ *smpp = p;
+ return (0);
+ }
+ }
+
+ /* It was past the end of the map after all. */
+ return (1);
+}
+
+/*
+ * svi_sm_position --
+ * Return the line/column of the top, middle or last line on the screen.
+ * (The vi H, M and L commands.) Here because only the screen routines
+ * know what's really out there.
+ */
+int
+svi_sm_position(sp, ep, rp, cnt, pos)
+ SCR *sp;
+ EXF *ep;
+ MARK *rp;
+ u_long cnt;
+ enum position pos;
+{
+ SMAP *smp;
+ recno_t last;
+
+ switch (pos) {
+ case P_TOP:
+ /*
+ * !!!
+ * Historically, an invalid count to the H command failed.
+ * We do nothing special here, just making sure that H in
+ * an empty screen works.
+ */
+ if (cnt > TMAP - HMAP)
+ goto sof;
+ smp = HMAP + cnt;
+ if (cnt && file_gline(sp, ep, smp->lno, NULL) == NULL) {
+sof: msgq(sp, M_BERR, "Movement past the end-of-screen");
+ return (1);
+ }
+ break;
+ case P_MIDDLE:
+ /*
+ * !!!
+ * Historically, a count to the M command was ignored.
+ * If the screen isn't filled, find the middle of what's
+ * real and move there.
+ */
+ if (file_gline(sp, ep, TMAP->lno, NULL) == NULL) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ for (smp = TMAP; smp->lno > last && smp > HMAP; --smp);
+ if (smp > HMAP)
+ smp -= (smp - HMAP) / 2;
+ } else
+ smp = (HMAP + (TMAP - HMAP) / 2) + cnt;
+ break;
+ case P_BOTTOM:
+ /*
+ * !!!
+ * Historically, an invalid count to the L command failed.
+ * If the screen isn't filled, find the bottom of what's
+ * real and try to offset from there.
+ */
+ if (cnt > TMAP - HMAP)
+ goto eof;
+ smp = TMAP - cnt;
+ if (file_gline(sp, ep, smp->lno, NULL) == NULL) {
+ if (file_lline(sp, ep, &last))
+ return (1);
+ for (; smp->lno > last && smp > HMAP; --smp);
+ if (cnt > smp - HMAP) {
+eof: msgq(sp, M_BERR,
+ "Movement past the beginning-of-screen");
+ return (1);
+ }
+ smp -= cnt;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Make sure that the cached information is valid. */
+ if (!SMAP_CACHE(smp) && svi_line(sp, ep, smp, NULL, NULL))
+ return (1);
+ rp->lno = smp->lno;
+ rp->cno = smp->c_sboff;
+
+ return (0);
+}
+
+/*
+ * svi_sm_nlines --
+ * Return the number of screen lines from an SMAP entry to the
+ * start of some file line, less than a maximum value.
+ */
+recno_t
+svi_sm_nlines(sp, ep, from_sp, to_lno, max)
+ SCR *sp;
+ EXF *ep;
+ SMAP *from_sp;
+ recno_t to_lno;
+ size_t max;
+{
+ recno_t lno, lcnt;
+
+ if (O_ISSET(sp, O_LEFTRIGHT))
+ return (from_sp->lno > to_lno ?
+ from_sp->lno - to_lno : to_lno - from_sp->lno);
+
+ if (from_sp->lno == to_lno)
+ return (from_sp->off - 1);
+
+ if (from_sp->lno > to_lno) {
+ lcnt = from_sp->off - 1; /* Correct for off-by-one. */
+ for (lno = from_sp->lno; --lno >= to_lno && lcnt <= max;)
+ lcnt += svi_opt_screens(sp, ep, lno, NULL);
+ } else {
+ lno = from_sp->lno;
+ lcnt = (svi_opt_screens(sp, ep, lno, NULL) - from_sp->off) + 1;
+ for (; ++lno < to_lno && lcnt <= max;)
+ lcnt += svi_opt_screens(sp, ep, lno, NULL);
+ }
+ return (lcnt);
+}
diff --git a/usr.bin/vi/svi/svi_split.c b/usr.bin/vi/svi/svi_split.c
new file mode 100644
index 0000000..dbf151c
--- /dev/null
+++ b/usr.bin/vi/svi/svi_split.c
@@ -0,0 +1,635 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_split.c 8.49 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+/*
+ * svi_split --
+ * Split the screen.
+ */
+int
+svi_split(sp, argv, argc)
+ SCR *sp;
+ ARGS *argv[];
+ int argc;
+{
+ MSG *mp, *next;
+ SCR *tsp, saved_sp;
+ SVI_PRIVATE saved_svp;
+ SMAP *smp;
+ size_t cnt, half;
+ int issmallscreen, splitup;
+ char **ap;
+
+ /* Check to see if it's possible. */
+ half = sp->rows / 2;
+ if (half < MINIMUM_SCREEN_ROWS) {
+ msgq(sp, M_ERR, "Screen must be larger than %d to split",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+
+ /* Get a new screen. */
+ if (screen_init(sp, &tsp, 0))
+ return (1);
+ CALLOC(sp, _HMAP(tsp), SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ if (_HMAP(tsp) == NULL)
+ return (1);
+
+ /*
+ * We're about to modify the current screen. Save the contents
+ * in case something goes horribly, senselessly wrong.
+ */
+ saved_sp = *sp;
+ saved_svp = *SVP(sp);
+
+/* INITIALIZED AT SCREEN CREATE. */
+
+/* PARTIALLY OR COMPLETELY COPIED FROM PREVIOUS SCREEN. */
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ * Set a flag so we know to fix the screen up later.
+ */
+ issmallscreen = ISSMALLSCREEN(sp);
+
+ /*
+ * Split the screen, and link the screens together. If the cursor
+ * is in the top half of the current screen, the new screen goes
+ * under the current screen. Else, it goes above the current screen.
+ *
+ * The columns in the screen don't change.
+ */
+ tsp->cols = sp->cols;
+
+ cnt = svi_sm_cursor(sp, sp->ep, &smp) ? 0 : smp - HMAP;
+ if (cnt <= half) { /* Parent is top half. */
+ /* Child. */
+ tsp->rows = sp->rows - half;
+ tsp->woff = sp->woff + half;
+ tsp->t_maxrows = tsp->rows - 1;
+
+ /* Parent. */
+ sp->rows = half;
+ sp->t_maxrows = sp->rows - 1;
+
+ splitup = 0;
+ } else { /* Parent is bottom half. */
+ /* Child. */
+ tsp->rows = sp->rows - half;
+ tsp->woff = sp->woff;
+ tsp->t_maxrows = tsp->rows - 1;
+
+ /* Parent. */
+ sp->rows = half;
+ sp->woff += tsp->rows;
+ sp->t_maxrows = sp->rows - 1;
+
+ /* Shift the parent's map down. */
+ memmove(_HMAP(sp),
+ _HMAP(sp) + tsp->rows, sp->t_maxrows * sizeof(SMAP));
+
+ splitup = 1;
+ }
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * The child may have different screen options sizes than the
+ * parent, so use them. Make sure that the text counts aren't
+ * larger than the new screen sizes.
+ */
+ if (issmallscreen) {
+ /* Fix the text line count for the parent. */
+ if (splitup)
+ sp->t_rows -= tsp->rows;
+
+ /* Fix the parent screen. */
+ if (sp->t_rows > sp->t_maxrows)
+ sp->t_rows = sp->t_maxrows;
+ if (sp->t_minrows > sp->t_maxrows)
+ sp->t_minrows = sp->t_maxrows;
+
+ /* Fix the child screen. */
+ tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+ if (tsp->t_rows > tsp->t_maxrows)
+ tsp->t_rows = tsp->t_maxrows;
+ if (tsp->t_minrows > tsp->t_maxrows)
+ tsp->t_minrows = tsp->t_maxrows;
+
+ /*
+ * If we split up, i.e. the child is on top, lines that
+ * were painted in the parent may not be painted in the
+ * child. Clear any lines not being used in the child
+ * screen.
+ *
+ */
+ if (splitup)
+ for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+ MOVE(tsp, cnt, 0);
+ clrtoeol();
+ }
+ } else {
+ sp->t_minrows = sp->t_rows = sp->rows - 1;
+
+ /*
+ * The new screen may be a small screen, even though the
+ * parent was not. Don't complain if O_WINDOW is too large,
+ * we're splitting the screen so the screen is much smaller
+ * than normal. Clear any lines not being used in the child
+ * screen.
+ */
+ tsp->t_minrows = tsp->t_rows = O_VAL(sp, O_WINDOW);
+ if (tsp->t_rows > tsp->rows - 1)
+ tsp->t_minrows = tsp->t_rows = tsp->rows - 1;
+ else
+ for (cnt = tsp->t_rows; ++cnt <= tsp->t_maxrows;) {
+ MOVE(tsp, cnt, 0);
+ clrtoeol();
+ }
+ }
+
+ /* Adjust the ends of both maps. */
+ _TMAP(sp) = _HMAP(sp) + (sp->t_rows - 1);
+ _TMAP(tsp) = _HMAP(tsp) + (tsp->t_rows - 1);
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = sp->t_maxrows / 2;
+ tsp->defscroll = tsp->t_maxrows / 2;
+
+ /*
+ * If files specified, build the file list, else, link to the
+ * current file.
+ */
+ if (argv == NULL) {
+ if ((tsp->frp = file_add(tsp, sp->frp->name)) == NULL)
+ goto err;
+ } else {
+ /* Create a new argument list. */
+ CALLOC(sp, tsp->argv, char **, argc + 1, sizeof(char *));
+ if (tsp->argv == NULL)
+ goto err;
+ for (ap = tsp->argv, argv; argv[0]->len != 0; ++ap, ++argv)
+ if ((*ap =
+ v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
+ goto err;
+ *ap = NULL;
+
+ /* Switch to the first one. */
+ tsp->cargv = tsp->argv;
+ if ((tsp->frp = file_add(tsp, *tsp->cargv)) == NULL)
+ goto err;
+ }
+
+ /*
+ * Copy the file state flags, start the file. Fill the child's
+ * screen map. If the file is unchanged, keep the screen and
+ * cursor the same.
+ */
+ if (argv == NULL) {
+ tsp->ep = sp->ep;
+ ++sp->ep->refcnt;
+
+ tsp->frp->flags = sp->frp->flags;
+ tsp->frp->lno = sp->lno;
+ tsp->frp->cno = sp->cno;
+ F_SET(tsp->frp, FR_CURSORSET);
+
+ /* Copy the parent's map into the child's map. */
+ memmove(_HMAP(tsp), _HMAP(sp), tsp->t_rows * sizeof(SMAP));
+ } else {
+ if (file_init(tsp, tsp->frp, NULL, 0))
+ goto err;
+ (void)svi_sm_fill(tsp, tsp->ep, 1, P_TOP);
+ }
+
+ /* Everything's initialized, put the screen on the displayed queue.*/
+ SIGBLOCK(sp->gp);
+ if (splitup) {
+ /* Link in before the parent. */
+ CIRCLEQ_INSERT_BEFORE(&sp->gp->dq, sp, tsp, q);
+ } else {
+ /* Link in after the parent. */
+ CIRCLEQ_INSERT_AFTER(&sp->gp->dq, sp, tsp, q);
+ }
+ SIGUNBLOCK(sp->gp);
+
+ /* Clear the current information lines in both screens. */
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ MOVE(tsp, INFOLINE(tsp), 0);
+ clrtoeol();
+
+ /* Redraw the status line for the parent screen. */
+ (void)msg_status(sp, sp->ep, sp->lno, 0);
+
+ /* Save the parent screen's cursor information. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ /* Completely redraw the child screen. */
+ F_SET(tsp, S_REDRAW);
+
+ /* Switch screens. */
+ sp->nextdisp = tsp;
+ F_SET(sp, S_SSWITCH);
+ return (0);
+
+ /* Recover the original screen. */
+err: *sp = saved_sp;
+ *SVP(sp) = saved_svp;
+
+ /* Copy any (probably error) messages in the new screen. */
+ for (mp = tsp->msgq.lh_first; mp != NULL; mp = next) {
+ if (!F_ISSET(mp, M_EMPTY))
+ msg_app(sp->gp, sp,
+ mp->flags & M_INV_VIDEO, mp->mbuf, mp->len);
+ next = mp->q.le_next;
+ if (mp->mbuf != NULL)
+ free(mp->mbuf);
+ free(mp);
+ }
+
+ /* Free the new screen. */
+ if (tsp->argv != NULL) {
+ for (ap = tsp->argv; *ap != NULL; ++ap)
+ free(*ap);
+ free(tsp->argv);
+ }
+ free(_HMAP(tsp));
+ free(SVP(tsp));
+ FREE(tsp, sizeof(SCR));
+ return (1);
+}
+
+/*
+ * svi_bg --
+ * Background the screen, and switch to the next one.
+ */
+int
+svi_bg(csp)
+ SCR *csp;
+{
+ SCR *sp;
+
+ /* Try and join with another screen. */
+ if ((svi_join(csp, &sp)))
+ return (1);
+ if (sp == NULL) {
+ msgq(csp, M_ERR,
+ "You may not background your only displayed screen");
+ return (1);
+ }
+
+ /* Move the old screen to the hidden queue. */
+ SIGBLOCK(csp->gp);
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+ SIGUNBLOCK(csp->gp);
+
+ /* Switch screens. */
+ csp->nextdisp = sp;
+ F_SET(csp, S_SSWITCH);
+
+ return (0);
+}
+
+/*
+ * svi_join --
+ * Join the screen into a related screen, if one exists,
+ * and return that screen.
+ */
+int
+svi_join(csp, nsp)
+ SCR *csp, **nsp;
+{
+ SCR *sp;
+ size_t cnt;
+
+ /*
+ * If a split screen, add space to parent/child. Make no effort
+ * to clean up the screen's values. If it's not exiting, we'll
+ * get it when the user asks to show it again.
+ */
+ if ((sp = csp->q.cqe_prev) == (void *)&csp->gp->dq) {
+ if ((sp = csp->q.cqe_next) == (void *)&csp->gp->dq) {
+ *nsp = NULL;
+ return (0);
+ }
+ sp->woff = csp->woff;
+ }
+ sp->rows += csp->rows;
+ if (ISSMALLSCREEN(sp)) {
+ sp->t_maxrows += csp->rows;
+ for (cnt = sp->t_rows; ++cnt <= sp->t_maxrows;) {
+ MOVE(sp, cnt, 0);
+ clrtoeol();
+ }
+ TMAP = HMAP + (sp->t_rows - 1);
+ } else {
+ sp->t_maxrows += csp->rows;
+ sp->t_rows = sp->t_minrows = sp->t_maxrows;
+ TMAP = HMAP + (sp->t_rows - 1);
+ if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+ return (1);
+ F_SET(sp, S_REDRAW);
+ }
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = sp->t_maxrows / 2;
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), if the underlying file was a tmp
+ * file it may have gone away.
+ */
+ if (csp->frp != NULL) {
+ csp->frp->lno = csp->lno;
+ csp->frp->cno = csp->cno;
+ F_SET(csp->frp, FR_CURSORSET);
+ }
+
+ *nsp = sp;
+ return (0);
+}
+
+/*
+ * svi_fg --
+ * Background the current screen, and foreground a new one.
+ */
+int
+svi_fg(csp, name)
+ SCR *csp;
+ CHAR_T *name;
+{
+ SCR *sp;
+
+ if (svi_swap(csp, &sp, name))
+ return (1);
+ if (sp == NULL) {
+ if (name == NULL)
+ msgq(csp, M_ERR, "There are no background screens");
+ else
+ msgq(csp, M_ERR,
+ "There's no background screen editing a file named %s",
+ name);
+ return (1);
+ }
+
+ /* Move the old screen to the hidden queue. */
+ SIGBLOCK(csp->gp);
+ CIRCLEQ_REMOVE(&csp->gp->dq, csp, q);
+ CIRCLEQ_INSERT_TAIL(&csp->gp->hq, csp, q);
+ SIGUNBLOCK(csp->gp);
+
+ return (0);
+}
+
+/*
+ * svi_swap --
+ * Swap the current screen with a hidden one.
+ */
+int
+svi_swap(csp, nsp, name)
+ SCR *csp, **nsp;
+ char *name;
+{
+ SCR *sp;
+ int issmallscreen;
+
+ /* Find the screen, or, if name is NULL, the first screen. */
+ for (sp = csp->gp->hq.cqh_first;
+ sp != (void *)&csp->gp->hq; sp = sp->q.cqe_next)
+ if (name == NULL || !strcmp(sp->frp->name, name))
+ break;
+ if (sp == (void *)&csp->gp->hq) {
+ *nsp = NULL;
+ return (0);
+ }
+ *nsp = sp;
+
+ /*
+ * Save the old screen's cursor information.
+ *
+ * XXX
+ * If called after file_end(), if the underlying file was a tmp
+ * file it may have gone away.
+ */
+ if (csp->frp != NULL) {
+ csp->frp->lno = csp->lno;
+ csp->frp->cno = csp->cno;
+ F_SET(csp->frp, FR_CURSORSET);
+ }
+
+ /* Switch screens. */
+ csp->nextdisp = sp;
+ F_SET(csp, S_SSWITCH);
+
+ /* Initialize terminal information. */
+ SVP(sp)->srows = SVP(csp)->srows;
+
+ issmallscreen = ISSMALLSCREEN(sp);
+
+ /* Initialize screen information. */
+ sp->rows = csp->rows;
+ sp->cols = csp->cols;
+ sp->woff = csp->woff;
+
+ /*
+ * Small screens: see svi/svi_refresh.c:svi_refresh, section 3b.
+ *
+ * The new screens may have different screen options sizes than the
+ * old one, so use them. Make sure that text counts aren't larger
+ * than the new screen sizes.
+ */
+ if (issmallscreen) {
+ sp->t_minrows = sp->t_rows = O_VAL(sp, O_WINDOW);
+ if (sp->t_rows > csp->t_maxrows)
+ sp->t_rows = sp->t_maxrows;
+ if (sp->t_minrows > csp->t_maxrows)
+ sp->t_minrows = sp->t_maxrows;
+ } else
+ sp->t_rows = sp->t_maxrows = sp->t_minrows = sp->rows - 1;
+
+ /* Reset the length of the default scroll. */
+ sp->defscroll = sp->t_maxrows / 2;
+
+ /*
+ * Don't change the screen's cursor information other than to
+ * note that the cursor is wrong.
+ */
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+
+ /*
+ * The HMAP may be NULL, if the screen got resized and
+ * a bunch of screens had to be hidden.
+ */
+ if (HMAP == NULL)
+ CALLOC_RET(sp, HMAP, SMAP *, SIZE_HMAP(sp), sizeof(SMAP));
+ TMAP = HMAP + (sp->t_rows - 1);
+
+ /* Fill the map. */
+ if (svi_sm_fill(sp, sp->ep, sp->lno, P_FILL))
+ return (1);
+
+ /*
+ * The new screen replaces the old screen in the parent/child list.
+ * We insert the new screen after the old one. If we're exiting,
+ * the exit will delete the old one, if we're foregrounding, the fg
+ * code will move the old one to the hidden queue.
+ */
+ SIGBLOCK(sp->gp);
+ CIRCLEQ_REMOVE(&sp->gp->hq, sp, q);
+ CIRCLEQ_INSERT_AFTER(&csp->gp->dq, csp, sp, q);
+ SIGUNBLOCK(sp->gp);
+
+ F_SET(sp, S_REDRAW);
+ return (0);
+}
+
+/*
+ * svi_rabs --
+ * Change the absolute size of the current screen.
+ */
+int
+svi_rabs(sp, count, adj)
+ SCR *sp;
+ long count;
+ enum adjust adj;
+{
+ SCR *g, *s;
+
+ /*
+ * Figure out which screens will grow, which will shrink, and
+ * make sure it's possible.
+ */
+ if (count == 0)
+ return (0);
+ if (adj == A_SET) {
+ if (sp->t_maxrows == count)
+ return (0);
+ if (sp->t_maxrows > count) {
+ adj = A_DECREASE;
+ count = sp->t_maxrows - count;
+ } else {
+ adj = A_INCREASE;
+ count = count - sp->t_maxrows;
+ }
+ }
+ if (adj == A_DECREASE) {
+ if (count < 0)
+ count = -count;
+ s = sp;
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ goto toosmall;
+ if ((g = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+ if ((g = sp->q.cqe_next) == (void *)&sp->gp->dq)
+ goto toobig;
+ g->woff -= count;
+ } else
+ s->woff += count;
+ } else {
+ g = sp;
+ if ((s = sp->q.cqe_next) != (void *)&sp->gp->dq)
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count)
+ s = NULL;
+ else
+ s->woff += count;
+ else
+ s = NULL;
+ if (s == NULL) {
+ if ((s = sp->q.cqe_prev) == (void *)&sp->gp->dq) {
+toobig: msgq(sp, M_BERR, "The screen cannot %s",
+ adj == A_DECREASE ? "shrink" : "grow");
+ return (1);
+ }
+ if (s->t_maxrows < MINIMUM_SCREEN_ROWS + count) {
+toosmall: msgq(sp, M_BERR,
+ "The screen can only shrink to %d rows",
+ MINIMUM_SCREEN_ROWS);
+ return (1);
+ }
+ g->woff -= count;
+ }
+ }
+
+ /*
+ * Update the screens; we could optimize the reformatting of the
+ * screen, but this isn't likely to be a common enough operation
+ * to make it worthwhile.
+ */
+ g->rows += count;
+ g->t_rows += count;
+ if (g->t_minrows == g->t_maxrows)
+ g->t_minrows += count;
+ g->t_maxrows += count;
+ _TMAP(g) += count;
+ (void)msg_status(g, g->ep, g->lno, 0);
+ F_SET(g, S_REFORMAT);
+
+ s->rows -= count;
+ s->t_rows -= count;
+ s->t_maxrows -= count;
+ if (s->t_minrows > s->t_maxrows)
+ s->t_minrows = s->t_maxrows;
+ _TMAP(s) -= count;
+ (void)msg_status(s, s->ep, s->lno, 0);
+ F_SET(s, S_REFORMAT);
+
+ return (0);
+}
diff --git a/usr.bin/vi/svi/svi_term.c b/usr.bin/vi/svi/svi_term.c
new file mode 100644
index 0000000..a6ff7bd
--- /dev/null
+++ b/usr.bin/vi/svi/svi_term.c
@@ -0,0 +1,310 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_term.c 8.7 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+
+/*
+ * XXX
+ * THIS REQUIRES THAT ALL SCREENS SHARE A TERMINAL TYPE.
+ */
+typedef struct _tklist {
+ char *ts; /* Key's termcap string. */
+ char *output; /* Corresponding vi command. */
+ char *name; /* Name. */
+ u_char value; /* Special value (for lookup). */
+} TKLIST;
+static TKLIST const c_tklist[] = { /* Command mappings. */
+#ifdef SYSV_CURSES
+ {"kil1", "O", "insert line"},
+ {"kdch1", "x", "delete character"},
+ {"kcud1", "j", "cursor down"},
+ {"kel", "D", "delete to eol"},
+ {"kind", "\004", "scroll down"},
+ {"kll", "$", "go to eol"},
+ {"khome", "^", "go to sol"},
+ {"kich1", "i", "insert at cursor"},
+ {"kdl1", "dd", "delete line"},
+ {"kcub1", "h", "cursor left"},
+ {"knp", "\006", "page down"},
+ {"kpp", "\002", "page up"},
+ {"kri", "\025", "scroll up"},
+ {"ked", "dG", "delete to end of screen"},
+ {"kcuf1", "l", "cursor right"},
+ {"kcuu1", "k", "cursor up"},
+#else
+ {"kA", "O", "insert line"},
+ {"kD", "x", "delete character"},
+ {"kd", "j", "cursor down"},
+ {"kE", "D", "delete to eol"},
+ {"kF", "\004", "scroll down"},
+ {"kH", "$", "go to eol"},
+ {"kh", "^", "go to sol"},
+ {"kI", "i", "insert at cursor"},
+ {"kL", "dd", "delete line"},
+ {"kl", "h", "cursor left"},
+ {"kN", "\006", "page down"},
+ {"kP", "\002", "page up"},
+ {"kR", "\025", "scroll up"},
+ {"kS", "dG", "delete to end of screen"},
+ {"kr", "l", "cursor right"},
+ {"ku", "k", "cursor up"},
+#endif
+ {NULL},
+};
+static TKLIST const m1_tklist[] = { /* Input mappings (lookup). */
+ {NULL},
+};
+static TKLIST const m2_tklist[] = { /* Input mappings (set or delete). */
+#ifdef SYSV_CURSES
+ {"kcud1", "\033ja", "cursor down"},
+ {"kcub1", "\033ha", "cursor left"},
+ {"kcuu1", "\033ka", "cursor up"},
+ {"kcuf1", "\033la", "cursor right"},
+#else
+ {"kd", "\033ja", "cursor down"},
+ {"kl", "\033ha", "cursor left"},
+ {"ku", "\033ka", "cursor up"},
+ {"kr", "\033la", "cursor right"},
+#endif
+ {NULL},
+};
+
+/*
+ * svi_term_init --
+ * Initialize the special keys defined by the termcap/terminfo entry.
+ */
+int
+svi_term_init(sp)
+ SCR *sp;
+{
+ KEYLIST *kp;
+ SEQ *qp;
+ TKLIST const *tkp;
+ size_t len;
+ char *sbp, *s, *t, sbuf[1024];
+
+ /* Command mappings. */
+ for (tkp = c_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ if (seq_set(sp, tkp->name, strlen(tkp->name), t, strlen(t),
+ tkp->output, strlen(tkp->output), SEQ_COMMAND, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings needing to be looked up. */
+ for (tkp = m1_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ for (kp = keylist;; ++kp)
+ if (kp->value == tkp->value)
+ break;
+ if (kp == NULL)
+ continue;
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), &kp->ch, 1, SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Input mappings that are already set or are text deletions. */
+ for (tkp = m2_tklist; tkp->name != NULL; ++tkp) {
+#ifdef SYSV_CURSES
+ if ((t = tigetstr(tkp->ts)) == NULL || t == (char *)-1)
+ continue;
+#else
+ sbp = sbuf;
+ if ((t = tgetstr(tkp->ts, &sbp)) == NULL)
+ continue;
+#endif
+ /*
+ * !!!
+ * Some terminals' <cursor_left> keys send single <backspace>
+ * characters. This is okay in command mapping, but not okay
+ * in input mapping. That combination is the only one we'll
+ * ever see, hopefully, so kluge it here for now.
+ */
+ if (!strcmp(t, "\b"))
+ continue;
+ if (tkp->output == NULL) {
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), NULL, 0, SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ } else
+ if (seq_set(sp, tkp->name, strlen(tkp->name),
+ t, strlen(t), tkp->output, strlen(tkp->output),
+ SEQ_INPUT, SEQ_SCREEN))
+ return (1);
+ }
+
+ /* Rework any function key mappings. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = qp->q.le_next) {
+ if (!F_ISSET(qp, SEQ_FUNCMAP))
+ continue;
+ (void)svi_fmap(sp, qp->stype,
+ qp->input, qp->ilen, qp->output, qp->olen);
+ }
+
+ /* Set up the visual bell information. */
+ t = sbuf;
+ if (tgetstr("vb", &t) != NULL && (len = t - sbuf) != 0) {
+ MALLOC_RET(sp, s, char *, len);
+ memmove(s, sbuf, len);
+ if (SVP(sp)->VB != NULL)
+ free(SVP(sp)->VB);
+ SVP(sp)->VB = s;
+ return (0);
+ }
+
+ return (0);
+}
+
+/*
+ * svi_term_end --
+ * End the special keys defined by the termcap/terminfo entry.
+ */
+int
+svi_term_end(sp)
+ SCR *sp;
+{
+ SEQ *qp, *nqp;
+
+ /* Delete screen specific mappings. */
+ for (qp = sp->gp->seqq.lh_first; qp != NULL; qp = nqp) {
+ nqp = qp->q.le_next;
+ if (!F_ISSET(qp, SEQ_SCREEN))
+ continue;
+ (void)seq_mdel(qp);
+ }
+ return (0);
+}
+
+/*
+ * svi_fmap --
+ * Map a function key.
+ */
+int
+svi_fmap(sp, stype, from, flen, to, tlen)
+ SCR *sp;
+ enum seqtype stype;
+ CHAR_T *from, *to;
+ size_t flen, tlen;
+{
+ char *t, keyname[64];
+ size_t nlen;
+
+ /* If the terminal isn't initialized, there's nothing to do. */
+ if (!F_ISSET(SVP(sp), SVI_CURSES_INIT))
+ return (0);
+
+#ifdef SYSV_CURSES
+ (void)snprintf(keyname, sizeof(keyname), "kf%d", atoi(from + 1));
+ if ((t = tigetstr(keyname)) == NULL || t == (char *)-1)
+ t = NULL;
+#else
+ /*
+ * !!!
+ * Historically, the 4BSD termcap code didn't support functions keys
+ * greater than 9. This was silently enforced -- asking for key k12
+ * returned the value for k1. We try and get around this by using
+ * the tables specified in the terminfo(TI_ENV) man page from the 3rd
+ * Edition SVID. This assumes that the implementors of any System V
+ * compatibility code or an extended termcap used those codes.
+ */
+ { int n; char *sbp, sbuf[1024];
+ static const char codes[] = {
+/* 0-10 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ';',
+/* 11-19 */ '1', '2', '3', '4', '5', '6', '7', '8', '9',
+/* 20-63 */ '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',
+ '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',
+ };
+ if ((n = atoi(from + 1)) > 63) {
+ msgq(sp, M_ERR,
+ "Termcap has no code for the %s function key",
+ from);
+ return (1);
+ }
+ (void)snprintf(keyname, sizeof(keyname),
+ "%c%c", n <= 10 ? 'k' : 'F', codes[n]);
+ sbp = sbuf;
+ t = tgetstr(keyname, &sbp);
+ }
+#endif
+ if (t == NULL) {
+ msgq(sp, M_ERR, "This terminal has no %s key", from);
+ return (1);
+ }
+ nlen = snprintf(keyname,
+ sizeof(keyname), "function key %d", atoi(from + 1));
+ return (seq_set(sp, keyname, nlen, t, strlen(t),
+ to, tlen, stype, SEQ_SCREEN | SEQ_USERDEF));
+}
diff --git a/usr.bin/vi/svi/svi_util.c b/usr.bin/vi/svi/svi_util.c
new file mode 100644
index 0000000..65422ed
--- /dev/null
+++ b/usr.bin/vi/svi/svi_util.c
@@ -0,0 +1,347 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_util.c 8.55 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <curses.h>
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "../vi/vcmd.h"
+#include "excmd.h"
+#include "svi_screen.h"
+#include "../sex/sex_screen.h"
+
+/*
+ * svi_bell --
+ * Ring the bell.
+ */
+void
+svi_bell(sp)
+ SCR *sp;
+{
+#ifdef SYSV_CURSES
+ if (O_ISSET(sp, O_FLASH))
+ flash();
+ else
+ beep();
+#else
+ if (O_ISSET(sp, O_FLASH) && SVP(sp)->VB != NULL) {
+ (void)tputs(SVP(sp)->VB, 1, vi_putchar);
+ (void)fflush(stdout);
+ } else
+ (void)write(STDOUT_FILENO, "\007", 1); /* '\a' */
+#endif
+ F_CLR(sp, S_BELLSCHED);
+}
+
+/*
+ * svi_optchange --
+ * Screen specific "option changed" routine.
+ */
+int
+svi_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_TERM:
+ /* Toss any saved visual bell information. */
+ if (SVP(sp)->VB != NULL) {
+ FREE(SVP(sp)->VB, strlen(SVP(sp)->VB) + 1);
+ SVP(sp)->VB = NULL;
+ }
+
+ /* Reset the screen size. */
+ if (sp->s_window(sp, 0))
+ return (1);
+ F_SET(sp, S_RESIZE);
+ break;
+ case O_WINDOW:
+ if (svi_crel(sp, O_VAL(sp, O_WINDOW)))
+ return (1);
+ break;
+ }
+
+ (void)v_optchange(sp, opt);
+ (void)ex_optchange(sp, opt);
+
+ return (0);
+}
+
+/*
+ * svi_busy --
+ * Put the cursor somewhere so the user will think we're busy.
+ */
+int
+svi_busy(sp, msg)
+ SCR *sp;
+ char const *msg;
+{
+ /*
+ * search.c:f_search() is called from ex/ex_tag.c:ex_tagfirst(),
+ * which runs before the screen really exists. Make sure we don't
+ * step on anything.
+ *
+ * If the terminal isn't initialized, there's nothing to do.
+ */
+ if (!F_ISSET(SVP(sp), SVI_CURSES_INIT))
+ return (0);
+
+ MOVE(sp, INFOLINE(sp), 0);
+ if (msg) {
+ ADDSTR(msg);
+ clrtoeol();
+ }
+ refresh();
+ F_SET(SVP(sp), SVI_CUR_INVALID);
+ return (0);
+}
+
+/*
+ * svi_keypad --
+ * Put the keypad/cursor arrows into or out of application mode.
+ */
+void
+svi_keypad(sp, on)
+ SCR *sp;
+ int on;
+{
+#ifdef SYSV_CURSES
+ keypad(stdscr, on ? TRUE : FALSE);
+#else
+ char *sbp, *t, sbuf[128];
+
+ sbp = sbuf;
+ if ((t = tgetstr(on ? "ks" : "ke", &sbp)) == NULL)
+ return;
+ (void)tputs(t, 0, vi_putchar);
+ (void)fflush(stdout);
+#endif
+}
+
+/*
+ * svi_clear --
+ * Clear from the row down to the end of the screen.
+ */
+int
+svi_clear(sp)
+ SCR *sp;
+{
+ size_t oldy, oldx, row;
+
+ getyx(stdscr, oldy, oldx);
+ for (row = SVP(sp)->srows - 1; row >= oldy; --row) {
+ MOVEA(sp, row, 0);
+ clrtoeol();
+ }
+ MOVEA(sp, oldy, oldx);
+ refresh();
+ return (0);
+}
+
+/*
+ * svi_suspend --
+ * Suspend an svi screen.
+ *
+ * See signal.c for a long discussion of what's going on here. Let
+ * me put it this way, it's NOT my fault.
+ */
+int
+svi_suspend(sp)
+ SCR *sp;
+{
+ struct termios sv_term;
+ sigset_t set;
+ int oldx, oldy, rval;
+ char *sbp, *t, sbuf[128];
+
+ rval = 0;
+
+ /*
+ * Block SIGALRM, because vi uses timers to decide when to paint
+ * busy messages on the screen.
+ */
+ (void)sigemptyset(&set);
+ (void)sigaddset(&set, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &set, NULL)) {
+ msgq(sp, M_SYSERR, "suspend: sigblock");
+ return (1);
+ }
+
+ /* Save the current cursor position. */
+ getyx(stdscr, oldy, oldx);
+
+ /*
+ * Move the cursor to the bottom of the screen.
+ *
+ * XXX
+ * Some curses implementations don't turn off inverse video when
+ * standend() is called, waiting to see what the next character is
+ * going to be, instead. Write a character to force inverse video
+ * off, and then clear the line.
+ */
+ MOVE(sp, INFOLINE(sp), 0);
+ ADDCH('.');
+ refresh();
+ MOVE(sp, INFOLINE(sp), 0);
+ clrtoeol();
+ refresh();
+
+ /* Restore the cursor keys to normal mode. */
+ svi_keypad(sp, 0);
+
+ /* Send VE/TE. */
+#ifdef SYSV_CURSES
+ if ((t = tigetstr("cnorm")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+ if ((t = tigetstr("rmcup")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+#else
+ sbp = sbuf;
+ if ((t = tgetstr("ve", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+ sbp = sbuf;
+ if ((t = tgetstr("te", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+#endif
+ (void)fflush(stdout);
+
+ /* Save current terminal settings, and restore the original ones. */
+ if (tcgetattr(STDIN_FILENO, &sv_term)) {
+ msgq(sp, M_SYSERR, "suspend: tcgetattr");
+ return (1);
+ }
+ if (tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &sp->gp->original_termios)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr original");
+ return (1);
+ }
+
+ /* Push out any waiting messages. */
+ (void)write(STDOUT_FILENO, "\n", 1);
+ (void)sex_refresh(sp, sp->ep);
+
+ /* Stop the process group. */
+ if (kill(0, SIGTSTP)) {
+ msgq(sp, M_SYSERR, "suspend: kill");
+ rval = 1;
+ }
+
+ /* Time passes ... */
+
+ /* Restore current terminal settings. */
+ if (tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &sv_term)) {
+ msgq(sp, M_SYSERR, "suspend: tcsetattr current");
+ rval = 1;
+ }
+
+ /* Send TI/VS. */
+#ifdef SYSV_CURSES
+ if ((t = tigetstr("smcup")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+ if ((t = tigetstr("cvvis")) != NULL && t != (char *)-1)
+ (void)tputs(t, 0, vi_putchar);
+#else
+ sbp = sbuf;
+ if ((t = tgetstr("ti", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+ sbp = sbuf;
+ if ((t = tgetstr("vs", &sbp)) != NULL)
+ (void)tputs(t, 0, vi_putchar);
+#endif
+ (void)fflush(stdout);
+
+ /* Put the cursor keys into application mode. */
+ svi_keypad(sp, 1);
+
+ /*
+ * If the screen changed size, do a full refresh. Otherwise,
+ * System V has curses repaint it. 4BSD curses will repaint
+ * it in the wrefresh() call below.
+ */
+ if (!sp->s_window(sp, 1))
+ (void)sp->s_refresh(sp, sp->ep);
+#ifdef SYSV_CURSES
+ else
+ redrawwin(stdscr);
+#endif
+
+ /*
+ * Restore the cursor.
+ *
+ * !!!
+ * Don't use MOVE/MOVEA, we don't want to return without resetting
+ * the signals, regardless.
+ */
+ (void)move(oldy, oldx);
+ (void)wrefresh(curscr);
+
+ /* Reset the signals. */
+ if (sigprocmask(SIG_UNBLOCK, &set, NULL)) {
+ msgq(sp, M_SYSERR, "suspend: sigblock");
+ rval = 1;
+ }
+ return (rval);
+}
+
+/*
+ * svi_gdbrefresh --
+ * Stub routine so can flush out screen changes using gdb.
+ */
+#ifdef DEBUG
+int
+svi_gdbrefresh()
+{
+ refresh();
+ return (0);
+}
+#endif
diff --git a/usr.bin/vi/vi/getc.c b/usr.bin/vi/vi/getc.c
new file mode 100644
index 0000000..388994d
--- /dev/null
+++ b/usr.bin/vi/vi/getc.c
@@ -0,0 +1,268 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getc.c 8.11 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * Character stream routines --
+ * These routines return the file a character at a time. There are two
+ * special cases. First, the end of a line, end of a file, start of a
+ * file and empty lines are returned as special cases, and no character
+ * is returned. Second, empty lines include lines that have only white
+ * space in them, because the vi search functions don't care about white
+ * space, and this makes it easier for them to be consistent.
+ */
+
+/*
+ * cs_init --
+ * Initialize character stream routines.
+ */
+int
+cs_init(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t lno;
+
+ if ((csp->cs_bp =
+ file_gline(sp, ep, csp->cs_lno, &csp->cs_len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ msgq(sp, M_BERR, "Empty file");
+ else
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ return (0);
+}
+
+/*
+ * cs_next --
+ * Retrieve the next character.
+ */
+int
+cs_next(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get next line. */
+ case CS_EOL: /* EOL; get next line. */
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp =
+ file_gline(sp, ep, ++csp->cs_lno, &csp->cs_len)) == NULL) {
+ csp->cs_lno = slno;
+ if (file_lline(sp, ep, &slno))
+ return (1);
+ if (slno > csp->cs_lno) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ return (1);
+ }
+ csp->cs_flags = CS_EOF;
+ } else if (csp->cs_len == 0 ||
+ v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno = 0];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == csp->cs_len - 1)
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[++csp->cs_cno];
+ break;
+ case CS_EOF: /* EOF. */
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_fspace --
+ * If on a space, eat forward until something other than a
+ * whitespace character.
+ *
+ * XXX
+ * Semantics of checking the current character were coded for the fword()
+ * function -- once the other word routines are converted, they may have
+ * to change.
+ */
+int
+cs_fspace(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ return (0);
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags != 0 || !isblank(csp->cs_ch))
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_fblank --
+ * Eat forward to the next non-whitespace character.
+ */
+int
+cs_fblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_next(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
+
+/*
+ * cs_prev --
+ * Retrieve the previous character.
+ */
+int
+cs_prev(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ recno_t slno;
+
+ switch (csp->cs_flags) {
+ case CS_EMP: /* EMP; get previous line. */
+ case CS_EOL: /* EOL; get previous line. */
+ if (csp->cs_lno == 1) { /* SOF. */
+ csp->cs_flags = CS_SOF;
+ break;
+ }
+ slno = csp->cs_lno; /* Save current line. */
+ if ((csp->cs_bp = /* Line should exist. */
+ file_gline(sp, ep, --csp->cs_lno, &csp->cs_len)) == NULL) {
+ GETLINE_ERR(sp, csp->cs_lno);
+ csp->cs_lno = slno;
+ return (1);
+ }
+ if (csp->cs_len == 0 || v_isempty(csp->cs_bp, csp->cs_len)) {
+ csp->cs_cno = 0;
+ csp->cs_flags = CS_EMP;
+ } else {
+ csp->cs_flags = 0;
+ csp->cs_cno = csp->cs_len - 1;
+ csp->cs_ch = csp->cs_bp[csp->cs_cno];
+ }
+ break;
+ case 0:
+ if (csp->cs_cno == 0)
+ if (csp->cs_lno == 1)
+ csp->cs_flags = CS_SOF;
+ else
+ csp->cs_flags = CS_EOL;
+ else
+ csp->cs_ch = csp->cs_bp[--csp->cs_cno];
+ break;
+ case CS_SOF: /* SOF. */
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
+ }
+ return (0);
+}
+
+/*
+ * cs_bblank --
+ * Eat backward to the next non-whitespace character.
+ */
+int
+cs_bblank(sp, ep, csp)
+ SCR *sp;
+ EXF *ep;
+ VCS *csp;
+{
+ for (;;) {
+ if (cs_prev(sp, ep, csp))
+ return (1);
+ if (csp->cs_flags == CS_EOL || csp->cs_flags == CS_EMP ||
+ csp->cs_flags == 0 && isblank(csp->cs_ch))
+ continue;
+ break;
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_ch.c b/usr.bin/vi/vi/v_ch.c
new file mode 100644
index 0000000..514efc6
--- /dev/null
+++ b/usr.bin/vi/vi/v_ch.c
@@ -0,0 +1,340 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ch.c 8.15 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static void notfound __P((SCR *, ARG_CHAR_T));
+static void noprev __P((SCR *));
+
+/*
+ * v_chrepeat -- [count];
+ * Repeat the last F, f, T or t search.
+ */
+int
+v_chrepeat(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ vp->character = VIP(sp)->lastckey;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ return (v_chF(sp, ep, vp));
+ case fSEARCH:
+ return (v_chf(sp, ep, vp));
+ case TSEARCH:
+ return (v_chT(sp, ep, vp));
+ case tSEARCH:
+ return (v_cht(sp, ep, vp));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * v_chrrepeat -- [count],
+ * Repeat the last F, f, T or t search in the reverse direction.
+ */
+int
+v_chrrepeat(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum cdirection savedir;
+ int rval;
+
+ vp->character = VIP(sp)->lastckey;
+ savedir = VIP(sp)->csearchdir;
+
+ switch (VIP(sp)->csearchdir) {
+ case CNOTSET:
+ noprev(sp);
+ return (1);
+ case FSEARCH:
+ rval = v_chf(sp, ep, vp);
+ break;
+ case fSEARCH:
+ rval = v_chF(sp, ep, vp);
+ break;
+ case TSEARCH:
+ rval = v_cht(sp, ep, vp);
+ break;
+ case tSEARCH:
+ rval = v_chT(sp, ep, vp);
+ break;
+ default:
+ abort();
+ }
+ VIP(sp)->csearchdir = savedir;
+ return (rval);
+}
+
+/*
+ * v_cht -- [count]tc
+ * Search forward in the line for the character before the next
+ * occurrence of the specified character.
+ */
+int
+v_cht(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (v_chf(sp, ep, vp))
+ return (1);
+
+ /*
+ * v_chf places the cursor on the character, where the 't'
+ * command wants it to its left. We know this is safe since
+ * we had to move right for v_chf() to have succeeded.
+ */
+ --vp->m_stop.cno;
+
+ /*
+ * Make any necessary correction to the motion decision made
+ * by the v_chf routine.
+ */
+ if (!ISMOTION(vp))
+ vp->m_final = vp->m_stop;
+
+ VIP(sp)->csearchdir = tSEARCH;
+ return (0);
+}
+
+/*
+ * v_chf -- [count]fc
+ * Search forward in the line for the next occurrence of the
+ * specified character.
+ */
+int
+v_chf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t len;
+ recno_t lno;
+ u_long cnt;
+ int key;
+ char *endp, *p, *startp;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which we're
+ * searching, e.g. in "df1|f2|.|;", the ';' searches for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = fSEARCH;
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+
+ endp = (startp = p) + len;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (++p < endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = p - startp;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D
+ * and VC_Y stay at the start. Ignore VC_C and VC_DEF.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_chT -- [count]Tc
+ * Search backward in the line for the character after the next
+ * occurrence of the specified character.
+ */
+int
+v_chT(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (v_chF(sp, ep, vp))
+ return (1);
+
+ /*
+ * v_chF places the cursor on the character, where the 'T'
+ * command wants it to its right. We know this is safe since
+ * we had to move left for v_chF() to have succeeded.
+ */
+ ++vp->m_stop.cno;
+
+ /*
+ * Make any necessary correction to the motion decision made
+ * by the v_chF routine.
+ *
+ * XXX
+ * If you change this, notice that v_chF changes vp->m_start
+ * AFTER setting vp->m_final.
+ */
+ if (!F_ISSET(vp, VC_Y))
+ vp->m_final = vp->m_stop;
+
+ VIP(sp)->csearchdir = TSEARCH;
+ return (0);
+}
+
+/*
+ * v_chF -- [count]Fc
+ * Search backward in the line for the next occurrence of the
+ * specified character.
+ */
+int
+v_chF(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+ u_long cnt;
+ int key;
+ char *endp, *p;
+
+ /*
+ * !!!
+ * If it's a dot command, it doesn't reset the key for which
+ * we're searching, e.g. in "df1|f2|.|;", the ';' searches
+ * for a '2'.
+ */
+ key = vp->character;
+ if (!F_ISSET(vp, VC_ISDOT))
+ VIP(sp)->lastckey = key;
+ VIP(sp)->csearchdir = FSEARCH;
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0) {
+ notfound(sp, key);
+ return (1);
+ }
+
+ endp = p - 1;
+ p += vp->m_start.cno;
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ while (--p > endp && *p != key);
+ if (p == endp) {
+ notfound(sp, key);
+ return (1);
+ }
+ }
+
+ vp->m_stop.cno = (p - endp) - 1;
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_DEF. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+static void
+noprev(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "No previous F, f, T or t search");
+}
+
+static void
+notfound(sp, ch)
+ SCR *sp;
+ ARG_CHAR_T ch;
+{
+ msgq(sp, M_BERR, "%s not found", KEY_NAME(sp, ch));
+}
diff --git a/usr.bin/vi/vi/v_delete.c b/usr.bin/vi/vi/v_delete.c
new file mode 100644
index 0000000..83eb961
--- /dev/null
+++ b/usr.bin/vi/vi/v_delete.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_delete.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Delete -- [buffer][count]D
+ * Delete line command.
+ */
+int
+v_Delete(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ return (0);
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ if (len == 0)
+ return (0);
+
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = len - 1;
+
+ /* Yank the lines. */
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_NUMOPT))
+ return (1);
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, 0))
+ return (1);
+
+ vp->m_final.lno = vp->m_start.lno;
+ vp->m_final.cno = vp->m_start.cno ? vp->m_start.cno - 1 : 0;
+ return (0);
+}
+
+/*
+ * v_delete -- [buffer][count]d[count]motion
+ * Delete a range of text.
+ */
+int
+v_delete(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t nlines;
+ size_t len;
+ int lmode;
+
+ lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
+
+ /* Yank the lines. */
+ if (cut(sp, ep, F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop,
+ lmode | (F_ISSET(vp, VM_CUTREQ) ? CUT_NUMREQ : CUT_NUMOPT)))
+ return (1);
+
+ /* Delete the lines. */
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /*
+ * Check for deletion of the entire file. Try to check a close
+ * by line so we don't go to the end of the file unnecessarily.
+ */
+ if (file_gline(sp, ep, vp->m_final.lno + 1, &len) == NULL) {
+ if (file_lline(sp, ep, &nlines))
+ return (1);
+ if (nlines == 0) {
+ vp->m_final.lno = 1;
+ vp->m_final.cno = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * One special correction, in case we've deleted the current line or
+ * character. We check it here instead of checking in every command
+ * that can be a motion component.
+ */
+ if (file_gline(sp, ep, vp->m_final.lno, &len) == NULL) {
+ if (file_gline(sp, ep, nlines, &len) == NULL) {
+ GETLINE_ERR(sp, nlines);
+ return (1);
+ }
+ vp->m_final.lno = nlines;
+ }
+ if (vp->m_final.cno >= len)
+ vp->m_final.cno = len ? len - 1 : 0;
+
+ /*
+ * !!!
+ * The "dd" command moved to the first non-blank; "d<motion>" didn't.
+ */
+ if (F_ISSET(vp, VM_LDOUBLE)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_ex.c b/usr.bin/vi/vi/v_ex.c
new file mode 100644
index 0000000..99d36bf
--- /dev/null
+++ b/usr.bin/vi/vi/v_ex.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ex.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+static void excmd __P((EXCMDARG *,
+ int, int, recno_t, recno_t, int, ARGS *[], ARGS *, char *));
+
+/*
+ * v_again -- &
+ * Repeat the previous substitution.
+ */
+int
+v_again(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_SUBAGAIN,
+ 2, vp->m_start.lno, vp->m_start.lno, 1, ap, &a, "");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_at -- @
+ * Execute a buffer.
+ */
+int
+v_at(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_AT, 0, OOBLNO, OOBLNO, 0, ap, &a, NULL);
+ if (F_ISSET(vp, VC_BUFFER)) {
+ F_SET(&cmd, E_BUFFER);
+ cmd.buffer = vp->buffer;
+ }
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_ex -- :
+ * Execute a colon command line.
+ */
+int
+v_ex(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (sp->s_ex_run(sp, ep, &vp->m_final));
+}
+
+/*
+ * v_exmode -- Q
+ * Switch the editor into EX mode.
+ */
+int
+v_exmode(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Save the current line/column number. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ /* Switch to ex mode. */
+ sp->saved_vi_mode = F_ISSET(sp, S_VI_CURSES | S_VI_XAW);
+ F_CLR(sp, S_SCREENS);
+ F_SET(sp, S_EX);
+ return (0);
+}
+
+/*
+ * v_filter -- [count]!motion command(s)
+ * Run range through shell commands, replacing text.
+ */
+int
+v_filter(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+ TEXT *tp;
+
+ /*
+ * !!!
+ * Historical vi permitted "!!" in an empty file, and it's handled
+ * as a special case in the ex_bang routine. Don't modify this setup
+ * without understanding that one. In particular, note that we're
+ * manipulating the ex argument structures behind ex's back.
+ *
+ * !!!
+ * Historical vi did not permit the '!' command to be associated with
+ * a non-line oriented motion command, in general, although it did
+ * with search commands. So, !f; and !w would fail, but !/;<CR>
+ * would succeed, even if they all moved to the same location in the
+ * current line. I don't see any reason to disallow '!' using any of
+ * the possible motion commands.
+ */
+ excmd(&cmd, C_BANG,
+ 2, vp->m_start.lno, vp->m_stop.lno, 0, ap, &a, NULL);
+ EXP(sp)->argsoff = 0; /* XXX */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ if (argv_exp1(sp, ep, &cmd, "!", 1, 1))
+ return (1);
+ } else {
+ /* Get the command from the user. */
+ if (sp->s_get(sp, ep, sp->tiqp,
+ '!', TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+ /*
+ * Len is 0 if backspaced over the prompt,
+ * 1 if only CR entered.
+ */
+ tp = sp->tiqp->cqh_first;
+ if (tp->len <= 1)
+ return (0);
+
+ if (argv_exp1(sp, ep, &cmd, tp->lb + 1, tp->len - 1, 1))
+ return (1);
+ }
+ cmd.argc = EXP(sp)->argsoff; /* XXX */
+ cmd.argv = EXP(sp)->args; /* XXX */
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_join -- [count]J
+ * Join lines together.
+ */
+int
+v_join(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+ int lno;
+
+ /*
+ * YASC.
+ * The general rule is that '#J' joins # lines, counting the current
+ * line. However, 'J' and '1J' are the same as '2J', i.e. join the
+ * current and next lines. This doesn't map well into the ex command
+ * (which takes two line numbers), so we handle it here. Note that
+ * we never test for EOF -- historically going past the end of file
+ * worked just fine.
+ */
+ lno = vp->m_start.lno + 1;
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 2)
+ lno = vp->m_start.lno + (vp->count - 1);
+
+ excmd(&cmd, C_JOIN, 2, vp->m_start.lno, lno, 0, ap, &a, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_shiftl -- [count]<motion
+ * Shift lines left.
+ */
+int
+v_shiftl(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_SHIFTL,
+ 2, vp->m_start.lno, vp->m_stop.lno, 0, ap, &a, "<");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_shiftr -- [count]>motion
+ * Shift lines right.
+ */
+int
+v_shiftr(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_SHIFTR,
+ 2, vp->m_start.lno, vp->m_stop.lno, 0, ap, &a, ">");
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_switch -- ^^
+ * Switch to the previous file.
+ */
+int
+v_switch(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+ char *name;
+
+ /*
+ * Try the alternate file name, then the previous file
+ * name. Use the real name, not the user's current name.
+ */
+ if ((name = sp->alt_name) == NULL) {
+ msgq(sp, M_ERR, "No previous file to edit");
+ return (1);
+ }
+
+ /* If autowrite is set, write out the file. */
+ if (file_m1(sp, ep, 0, FS_ALL))
+ return (1);
+
+ excmd(&cmd, C_EDIT, 0, OOBLNO, OOBLNO, 0, ap, &a, name);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_tagpush -- ^[
+ * Do a tag search on a the cursor keyword.
+ */
+int
+v_tagpush(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_TAG, 0, OOBLNO, 0, 0, ap, &a, vp->keyword);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * v_tagpop -- ^T
+ * Pop the tags stack.
+ */
+int
+v_tagpop(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ ARGS *ap[2], a;
+ EXCMDARG cmd;
+
+ excmd(&cmd, C_TAGPOP, 0, OOBLNO, 0, 0, ap, &a, NULL);
+ return (sp->s_ex_cmd(sp, ep, &cmd, &vp->m_final));
+}
+
+/*
+ * excmd --
+ * Build an EX command structure.
+ */
+static void
+excmd(cmdp, cmd_id, naddr, lno1, lno2, force, ap, a, arg)
+ EXCMDARG *cmdp;
+ int cmd_id, force, naddr;
+ recno_t lno1, lno2;
+ ARGS *ap[2], *a;
+ char *arg;
+{
+ memset(cmdp, 0, sizeof(EXCMDARG));
+ cmdp->cmd = &cmds[cmd_id];
+ cmdp->addrcnt = naddr;
+ cmdp->addr1.lno = lno1;
+ cmdp->addr2.lno = lno2;
+ cmdp->addr1.cno = cmdp->addr2.cno = 1;
+ if (force)
+ cmdp->flags |= E_FORCE;
+ if ((a->bp = arg) == NULL) {
+ cmdp->argc = 0;
+ a->len = 0;
+ } else {
+ cmdp->argc = 1;
+ a->len = strlen(arg);
+ }
+ ap[0] = a;
+ ap[1] = NULL;
+ cmdp->argv = ap;
+}
diff --git a/usr.bin/vi/vi/v_increment.c b/usr.bin/vi/vi/v_increment.c
new file mode 100644
index 0000000..e24f496
--- /dev/null
+++ b/usr.bin/vi/vi/v_increment.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_increment.c 8.11 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static char * const fmt[] = {
+#define DEC 0
+ "%ld",
+#define SDEC 1
+ "%+ld",
+#define HEXC 2
+ "%#0.*lX",
+#define HEXL 3
+ "%#0.*lx",
+#define OCTAL 4
+ "%#0.*lo",
+};
+
+/*
+ * v_increment -- [count]#[#+-]
+ * Increment/decrement a keyword number.
+ */
+int
+v_increment(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VI_PRIVATE *vip;
+ u_long ulval;
+ long lval;
+ size_t blen, len, nlen;
+ int rval;
+ char *bp, *ntype, *p, nbuf[100];
+
+ vip = VIP(sp);
+
+ /* Do repeat operations. */
+ if (vp->character == '#')
+ vp->character = vip->inc_lastch;
+
+ /* Get new value. */
+ if (F_ISSET(vp, VC_C1SET))
+ vip->inc_lastval = vp->count;
+
+ if (vp->character != '+' && vp->character != '-') {
+ msgq(sp, M_ERR, "usage: %s", vp->kp->usage);
+ return (1);
+ }
+ vip->inc_lastch = vp->character;
+
+ /* Figure out the resulting type and number. */
+ p = vp->keyword;
+ len = vp->klen;
+ if (len > 1 && p[0] == '0') {
+ if (vp->character == '+') {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ULONG_MAX - ulval < vip->inc_lastval)
+ goto overflow;
+ ulval += vip->inc_lastval;
+ } else {
+ ulval = strtoul(vp->keyword, NULL, 0);
+ if (ulval < vip->inc_lastval)
+ goto underflow;
+ ulval -= vip->inc_lastval;
+ }
+ ntype = fmt[OCTAL];
+ if (len > 2)
+ if (p[1] == 'X')
+ ntype = fmt[HEXC];
+ else if (p[1] == 'x')
+ ntype = fmt[HEXL];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, len, ulval);
+ } else {
+ if (vp->character == '+') {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval > 0 && LONG_MAX - lval < vip->inc_lastval) {
+overflow: msgq(sp, M_ERR, "Resulting number too large");
+ return (1);
+ }
+ lval += vip->inc_lastval;
+ } else {
+ lval = strtol(vp->keyword, NULL, 0);
+ if (lval < 0 && -(LONG_MIN - lval) < vip->inc_lastval) {
+underflow: msgq(sp, M_ERR, "Resulting number too small");
+ return (1);
+ }
+ lval -= vip->inc_lastval;
+ }
+ ntype = lval != 0 &&
+ (*vp->keyword == '+' || *vp->keyword == '-') ?
+ fmt[SDEC] : fmt[DEC];
+ nlen = snprintf(nbuf, sizeof(nbuf), ntype, lval);
+ }
+
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ GET_SPACE_RET(sp, bp, blen, len + nlen);
+ memmove(bp, p, vp->m_start.cno);
+ memmove(bp + vp->m_start.cno, nbuf, nlen);
+ memmove(bp + vp->m_start.cno + nlen,
+ p + vp->m_start.cno + vp->klen, len - vp->m_start.cno - vp->klen);
+ len = len - vp->klen + nlen;
+
+ rval = file_sline(sp, ep, vp->m_start.lno, bp, len);
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_init.c b/usr.bin/vi/vi/v_init.c
new file mode 100644
index 0000000..e39f06b
--- /dev/null
+++ b/usr.bin/vi/vi/v_init.c
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_init.c 8.28 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int v_comment __P((SCR *, EXF *));
+
+/*
+ * v_screen_copy --
+ * Copy vi screen.
+ */
+int
+v_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ VI_PRIVATE *ovip, *nvip;
+
+ /* Create the private vi structure. */
+ CALLOC_RET(orig, nvip, VI_PRIVATE *, 1, sizeof(VI_PRIVATE));
+ sp->vi_private = nvip;
+
+ if (orig == NULL) {
+ nvip->inc_lastch = '+';
+ nvip->inc_lastval = 1;
+ nvip->csearchdir = CNOTSET;
+ } else {
+ ovip = VIP(orig);
+
+ /* User can replay the last input, but nothing else. */
+ if (ovip->rep_len != 0) {
+ MALLOC(orig, nvip->rep, char *, ovip->rep_len);
+ if (nvip->rep != NULL) {
+ memmove(nvip->rep, ovip->rep, ovip->rep_len);
+ nvip->rep_len = ovip->rep_len;
+ }
+ }
+
+ nvip->inc_lastch = ovip->inc_lastch;
+ nvip->inc_lastval = ovip->inc_lastval;
+
+ if (ovip->ps != NULL &&
+ (nvip->ps = strdup(ovip->ps)) == NULL) {
+ msgq(sp, M_SYSERR, NULL);
+ return (1);
+ }
+
+ nvip->lastckey = ovip->lastckey;
+ nvip->csearchdir = ovip->csearchdir;
+ }
+ return (0);
+}
+
+/*
+ * v_screen_end --
+ * End a vi screen.
+ */
+int
+v_screen_end(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+
+ vip = VIP(sp);
+
+ if (vip->rep != NULL)
+ free(vip->rep);
+
+ if (vip->ps != NULL)
+ free(vip->ps);
+
+ /* Free private memory. */
+ FREE(vip, sizeof(VI_PRIVATE));
+ sp->vi_private = NULL;
+
+ return (0);
+}
+
+/*
+ * v_init --
+ * Initialize vi.
+ */
+int
+v_init(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ size_t len;
+
+ /*
+ * The default address is line 1, column 0. If the address set
+ * bit is on for this file, load the address, ensuring that it
+ * exists.
+ */
+ if (F_ISSET(sp->frp, FR_CURSORSET)) {
+ sp->lno = sp->frp->lno;
+ sp->cno = sp->frp->cno;
+
+ if (file_gline(sp, ep, sp->lno, &len) == NULL) {
+ if (sp->lno != 1 || sp->cno != 0) {
+ if (file_lline(sp, ep, &sp->lno))
+ return (1);
+ if (sp->lno == 0)
+ sp->lno = 1;
+ sp->cno = 0;
+ }
+ } else if (sp->cno >= len)
+ sp->cno = 0;
+
+ if (F_ISSET(sp->frp, FR_FNONBLANK)) {
+ sp->cno = 0;
+ if (nonblank(sp, ep, sp->lno, &sp->cno))
+ return (1);
+
+ /* Reset strange attraction. */
+ sp->rcm = 0;
+ sp->rcm_last = 0;
+ }
+ } else {
+ sp->lno = 1;
+ sp->cno = 0;
+
+ if (O_ISSET(sp, O_COMMENT) && v_comment(sp, ep))
+ return (1);
+
+ /* Vi always starts up on the first non-<blank>. */
+ if (nonblank(sp, ep, sp->lno, &sp->cno))
+ return (1);
+
+ /* Reset strange attraction. */
+ sp->rcm = 0;
+ sp->rcm_last = 0;
+ }
+
+ /* Make ex display to a special function. */
+ if ((sp->stdfp = fwopen(sp, sp->s_ex_write)) == NULL) {
+ msgq(sp, M_SYSERR, "ex output");
+ return (1);
+ }
+#ifdef MAKE_EX_OUTPUT_LINE_BUFFERED
+ (void)setvbuf(sp->stdfp, NULL, _IOLBF, 0);
+#endif
+
+ /* Display the status line. */
+ return (msg_status(sp, ep, sp->lno, 0));
+}
+
+/*
+ * v_end --
+ * End vi session.
+ */
+int
+v_end(sp)
+ SCR *sp;
+{
+ /* Close down ex output file descriptor. */
+ (void)fclose(sp->stdfp);
+
+ return (0);
+}
+
+/*
+ * v_optchange --
+ * Handle change of options for vi.
+ */
+int
+v_optchange(sp, opt)
+ SCR *sp;
+ int opt;
+{
+ switch (opt) {
+ case O_PARAGRAPHS:
+ case O_SECTIONS:
+ return (v_buildps(sp));
+ }
+ return (0);
+}
+
+/*
+ * v_comment --
+ * Skip the first comment.
+ */
+static int
+v_comment(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ recno_t lno;
+ size_t len;
+ char *p;
+
+ for (lno = 1;
+ (p = file_gline(sp, ep, lno, &len)) != NULL && len == 0; ++lno);
+ if (p == NULL || len <= 1 || memcmp(p, "/*", 2))
+ return (0);
+ do {
+ for (; len; --len, ++p)
+ if (p[0] == '*' && len > 1 && p[1] == '/') {
+ sp->lno = lno;
+ return (0);
+ }
+ } while ((p = file_gline(sp, ep, ++lno, &len)) != NULL);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_left.c b/usr.bin/vi/vi/v_left.c
new file mode 100644
index 0000000..69741dd
--- /dev/null
+++ b/usr.bin/vi/vi/v_left.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_left.c 8.11 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_left -- [count]^H, [count]h
+ * Move left by columns.
+ */
+int
+v_left(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t cnt;
+
+ /*
+ * !!!
+ * The ^H and h commands always failed in the first column.
+ */
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /* Find the end of the range. */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.cno > cnt)
+ vp->m_stop.cno = vp->m_start.cno - cnt;
+ else
+ vp->m_stop.cno = 0;
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_DEF. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+/*
+ * v_cfirst -- [count]_
+ * Move to the first non-blank character in a line.
+ */
+int
+v_cfirst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t cnt;
+
+ /*
+ * !!!
+ * If the _ is a motion component, it makes the command a line motion
+ * e.g. "d_" deletes the line. It also means that the cursor doesn't
+ * move.
+ *
+ * The _ command never failed in the first column.
+ */
+ if (ISMOTION(vp))
+ F_SET(vp, VM_LMODE);
+ /*
+ * !!!
+ * Historically a specified count makes _ move down count - 1
+ * rows, so, "3_" is the same as "2j_".
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt != 1) {
+ --vp->count;
+ return (v_down(sp, ep, vp));
+ }
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case _ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_DEF.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_first -- ^
+ * Move to the first non-blank character in this line.
+ */
+int
+v_first(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * !!!
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+
+ /*
+ * Move to the first non-blank.
+ *
+ * Can't just use RCM_SET_FNB, in case ^ is used as the motion
+ * component of another command.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+
+ /*
+ * !!!
+ * The ^ command succeeded if used as a command without a whitespace
+ * character preceding the cursor in the line, but failed if used as
+ * a motion component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_DEF. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
+
+/*
+ * v_ncol -- [count]|
+ * Move to column count or the first column on this line. If the
+ * requested column is past EOL, move to EOL. The nasty part is
+ * that we have to know character column widths to make this work.
+ */
+int
+v_ncol(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (F_ISSET(vp, VC_C1SET) && vp->count > 1) {
+ --vp->count;
+ vp->m_stop.cno =
+ sp->s_colpos(sp, ep, vp->m_start.lno, (size_t)vp->count);
+ /*
+ * !!!
+ * The | command succeeded if used as a command and the cursor
+ * didn't move, but failed if used as a motion component in the
+ * same situation.
+ */
+ if (ISMOTION(vp) && vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+ } else {
+ /*
+ * !!!
+ * The | command succeeded if used as a command in column 0
+ * without a count, but failed if used as a motion component
+ * in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ }
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * VC_D and VC_Y stay at the start. If moving left, non-motion and
+ * VC_D commands move to the end of the range. VC_Y remains at the
+ * start. Ignore VC_C and VC_DEF. Motion left commands adjust the
+ * starting point to the character before the current one.
+ */
+ if (vp->m_start.cno < vp->m_stop.cno)
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ else {
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_Y))
+ vp->m_final = vp->m_start;
+ --vp->m_start.cno;
+ }
+ }
+ return (0);
+}
+
+/*
+ * v_zero -- 0
+ * Move to the first column on this line.
+ */
+int
+v_zero(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * !!!
+ * The 0 command succeeded if used as a command in the first column
+ * but failed if used as a motion component in the same situation.
+ */
+ if (ISMOTION(vp) && vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ /*
+ * VC_D and non-motion commands move to the end of the range,
+ * VC_Y stays at the start. Ignore VC_C and VC_DEF. Motion
+ * commands adjust the starting point to the character before
+ * the current one.
+ */
+ vp->m_stop.cno = 0;
+ vp->m_final = F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_mark.c b/usr.bin/vi/vi/v_mark.c
new file mode 100644
index 0000000..973430c
--- /dev/null
+++ b/usr.bin/vi/vi/v_mark.c
@@ -0,0 +1,210 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_mark.c 8.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_mark -- m[a-z]
+ * Set a mark.
+ */
+int
+v_mark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark_set(sp, ep, vp->character, &vp->m_start, 1));
+}
+
+enum which {BMARK, FMARK};
+static int mark __P((SCR *, EXF *, VICMDARG *, enum which));
+
+
+/*
+ * v_bmark -- `['`a-z]
+ * Move to a mark.
+ *
+ * Moves to a mark, setting both row and column.
+ *
+ * !!!
+ * Although not commonly known, the "'`" and "'`" forms are historically
+ * valid. The behavior is determined by the first character, so "`'" is
+ * the same as "``". Remember this fact -- you'll be amazed at how many
+ * people don't know it and will be delighted that you are able to tell
+ * them.
+ */
+int
+v_bmark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark(sp, ep, vp, BMARK));
+}
+
+/*
+ * v_fmark -- '['`a-z]
+ * Move to a mark.
+ *
+ * Move to the first nonblank character of the line containing the mark.
+ */
+int
+v_fmark(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (mark(sp, ep, vp, FMARK));
+}
+
+static int
+mark(sp, ep, vp, cmd)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which cmd;
+{
+ MARK m;
+ size_t len;
+ enum direction dir;
+
+ if (mark_get(sp, ep, vp->character, &vp->m_stop))
+ return (1);
+
+ /* Forward marks move to the first non-blank. */
+ if (cmd == FMARK) {
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ }
+
+ /* Non-motion commands move to the end of the range. */
+ if (!ISMOTION(vp)) {
+ vp->m_final = vp->m_stop;
+ return (0);
+ }
+
+ /*
+ * !!!
+ * If a motion component, the cursor has to move.
+ */
+ if (vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno == vp->m_start.cno) {
+ v_nomove(sp);
+ return (1);
+ }
+
+ /*
+ * If the motion is in the reverse direction, switch the start and
+ * stop MARK's so that it's in a forward direction. (There's no
+ * reason for this other than to make the tests below easier. The
+ * code in vi.c:vi() would have done the switch.) Both forward
+ * and backward motions can happen for either kind of mark command.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno) {
+ dir = BACKWARD;
+ m = vp->m_start;
+ vp->m_start = vp->m_stop;
+ vp->m_stop = m;
+ } else
+ dir = FORWARD;
+
+ /*
+ * BACKWARD:
+ * VC_D commands move to the end of the range. VC_Y stays at
+ * the start unless the end of the range is on a different line,
+ * when it moves to the end of the range. Ignore VC_C and
+ * VC_DEF.
+ *
+ * FORWARD:
+ * VC_D and VC_Y commands don't move. Ignore VC_C and VC_DEF.
+ */
+ if (dir == BACKWARD)
+ if (F_ISSET(vp, VC_D) ||
+ F_ISSET(vp, VC_Y) && vp->m_start.lno != vp->m_stop.lno)
+ vp->m_final = vp->m_start;
+ else
+ vp->m_final = vp->m_stop;
+ else
+ vp->m_final = vp->m_start;
+
+ if (cmd == FMARK)
+ return (0);
+
+ /*
+ * Forward marks are always line oriented, and it's set in the
+ * vcmd.c table. Backward marks that start and stop at column
+ * 0 of the line are also line mode commands.
+ */
+ if (vp->m_start.cno == 0 && vp->m_stop.cno == 0)
+ F_SET(vp, VM_LMODE);
+
+ /*
+ * BMARK'S that move backward and start at column 0, or move forward
+ * and end at column 0 are corrected to the last column of the previous
+ * line. Else, adjust the starting/ending point to the character
+ * before the current one (this is safe because we know the command had
+ * to move to succeed).
+ */
+ if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
+ if (file_gline(sp, ep, --vp->m_stop.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else
+ --vp->m_stop.cno;
+
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_match.c b/usr.bin/vi/vi/v_match.c
new file mode 100644
index 0000000..3df887c
--- /dev/null
+++ b/usr.bin/vi/vi/v_match.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_match.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_match -- %
+ * Search to matching character.
+ */
+int
+v_match(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VCS cs;
+ MARK *mp;
+ recno_t lno;
+ size_t cno, len, off;
+ int cnt, matchc, startc, (*gc)__P((SCR *, EXF *, VCS *));
+ char *p;
+
+ /*
+ * !!!
+ * Historic practice; ignore the count.
+ */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ goto nomatch;
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historical practice was to search for the initial character
+ * in the forward direction only.
+ */
+ for (off = vp->m_start.cno;; ++off) {
+ if (off >= len) {
+nomatch: msgq(sp, M_BERR, "No match character on this line");
+ return (1);
+ }
+ switch (startc = p[off]) {
+ case '(':
+ matchc = ')';
+ gc = cs_next;
+ break;
+ case ')':
+ matchc = '(';
+ gc = cs_prev;
+ break;
+ case '[':
+ matchc = ']';
+ gc = cs_next;
+ break;
+ case ']':
+ matchc = '[';
+ gc = cs_prev;
+ break;
+ case '{':
+ matchc = '}';
+ gc = cs_next;
+ break;
+ case '}':
+ matchc = '{';
+ gc = cs_prev;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = off;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+ for (cnt = 1;;) {
+ if (gc(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF)
+ break;
+ continue;
+ }
+ if (cs.cs_ch == startc)
+ ++cnt;
+ else if (cs.cs_ch == matchc && --cnt == 0)
+ break;
+ }
+ if (cnt) {
+ msgq(sp, M_BERR, "Matching character not found");
+ return (1);
+ }
+
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * If moving right, non-motion commands move to the end of the range.
+ * VC_D and VC_Y stay at the start. If moving left, non-motion and
+ * VC_D commands move to the end of the range. VC_Y remains at the
+ * start. Ignore VC_C and VC_DEF.
+ *
+ * !!!
+ * Don't correct for leftward movement -- historic vi deleted the
+ * starting cursor position when deleting to a match.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno < vp->m_stop.cno)
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ else
+ vp->m_final = ISMOTION(vp) &&
+ F_ISSET(vp, VC_Y) ? vp->m_start : vp->m_stop;
+
+ /*
+ * !!!
+ * If the motion is across lines, and the earliest cursor position
+ * is at or before any non-blank characters in the line, i.e. the
+ * movement is cutting all of the line's text, and the later cursor
+ * position has nothing other than whitespace characters between it
+ * and the end of its line, the buffer is in line mode.
+ */
+ if (!ISMOTION(vp) || vp->m_start.lno == vp->m_stop.lno)
+ return (0);
+ mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_start : &vp->m_stop;
+ if (mp->cno != 0) {
+ cno = 0;
+ if (nonblank(sp, ep, mp->lno, &cno))
+ return (1);
+ if (cno < mp->cno)
+ return (0);
+ }
+ mp = vp->m_start.lno < vp->m_stop.lno ? &vp->m_stop : &vp->m_start;
+ if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, mp->lno);
+ return (1);
+ }
+ for (p += mp->cno + 1, len -= mp->cno; --len; ++p)
+ if (!isblank(*p))
+ return (0);
+ F_SET(vp, VM_LMODE);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_ntext.c b/usr.bin/vi/vi/v_ntext.c
new file mode 100644
index 0000000..d4b26c5
--- /dev/null
+++ b/usr.bin/vi/vi/v_ntext.c
@@ -0,0 +1,1899 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ntext.c 8.122 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+#include "excmd.h"
+
+static int txt_abbrev __P((SCR *, TEXT *, CHAR_T *, int, int *, int *));
+static void txt_ai_resolve __P((SCR *, TEXT *));
+static TEXT *txt_backup __P((SCR *, EXF *, TEXTH *, TEXT *, u_int *));
+static void txt_err __P((SCR *, EXF *, TEXTH *));
+static int txt_hex __P((SCR *, TEXT *));
+static int txt_indent __P((SCR *, TEXT *));
+static int txt_margin __P((SCR *,
+ TEXT *, CHAR_T *, TEXT *, u_int, int *));
+static int txt_outdent __P((SCR *, TEXT *));
+static void txt_Rcleanup __P((SCR *,
+ TEXTH *, TEXT *, const char *, const size_t));
+static int txt_resolve __P((SCR *, EXF *, TEXTH *, u_int));
+static void txt_showmatch __P((SCR *, EXF *));
+static void txt_unmap __P((SCR *, TEXT *, u_int *));
+
+/* Cursor character (space is hard to track on the screen). */
+#if defined(DEBUG) && 0
+#undef CH_CURSOR
+#define CH_CURSOR '+'
+#endif
+
+/*
+ * v_ntext --
+ * Read in text from the user.
+ *
+ * !!!
+ * Historic vi did a special screen optimization for tab characters. For
+ * the keystrokes "iabcd<esc>0C<tab>", the tab would overwrite the rest of
+ * the string when it was displayed. Because this implementation redisplays
+ * the entire line on each keystroke, the "bcd" gets pushed to the right as
+ * we ignore that the user has "promised" to change the rest of the characters.
+ * Users have noticed, but this isn't worth fixing, and, the way that the
+ * historic vi did it results in an even worse bug. Given the keystrokes
+ * "iabcd<esc>0R<tab><esc>", the "bcd" disappears, and magically reappears
+ * on the second <esc> key.
+ */
+int
+v_ntext(sp, ep, tiqh, tm, lp, len, rp, prompt, ai_line, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ MARK *tm; /* To MARK. */
+ const char *lp; /* Input line. */
+ const size_t len; /* Input line length. */
+ MARK *rp; /* Return MARK. */
+ ARG_CHAR_T prompt; /* Prompt to display. */
+ recno_t ai_line; /* Line number to use for autoindent count. */
+ u_int flags; /* TXT_ flags. */
+{
+ /* State of abbreviation checks. */
+ enum { A_NOTSET, A_NOTWORD, A_INWORD } abb;
+ /* State of the "[^0]^D" sequences. */
+ enum { C_NOTSET, C_CARATSET, C_NOCHANGE, C_ZEROSET } carat_st;
+ /* State of the hex input character. */
+ enum { H_NOTSET, H_NEXTCHAR, H_INHEX } hex;
+ /* State of quotation. */
+ enum { Q_NOTSET, Q_NEXTCHAR, Q_THISCHAR } quoted;
+ enum input tval;
+ struct termios t; /* Terminal characteristics. */
+ CH ikey; /* Input character structure. */
+ CHAR_T ch; /* Input character. */
+ TEXT *tp, *ntp, ait; /* Input and autoindent text structures. */
+ TEXT wmt; /* Wrapmargin text structure. */
+ size_t owrite, insert; /* Temporary copies of TEXT fields. */
+ size_t rcol; /* 0-N: insert offset in the replay buffer. */
+ size_t col; /* Current column. */
+ u_long margin; /* Wrapmargin value. */
+ u_int iflags; /* Input flags. */
+ int ab_cnt, ab_turnoff; /* Abbreviation count, if turned off. */
+ int eval; /* Routine return value. */
+ int replay; /* If replaying a set of input. */
+ int showmatch; /* Showmatch set on this character. */
+ int sig_ix, sig_reset; /* Signal information. */
+ int testnr; /* Test first character for nul replay. */
+ int max, tmp;
+ int unmap_tst; /* Input map needs testing. */
+ int wmset, wmskip; /* Wrapmargin happened, blank skip flags. */
+ char *p;
+
+ /*
+ * Set the input flag, so tabs get displayed correctly
+ * and everyone knows that the text buffer is in use.
+ */
+ F_SET(sp, S_INPUT);
+
+ /* Local initialization. */
+ eval = 0;
+
+ /*
+ * Get one TEXT structure with some initial buffer space, reusing
+ * the last one if it's big enough. (All TEXT bookkeeping fields
+ * default to 0 -- text_init() handles this.) If changing a line,
+ * copy it into the TEXT buffer.
+ */
+ if (tiqh->cqh_first != (void *)tiqh) {
+ tp = tiqh->cqh_first;
+ if (tp->q.cqe_next != (void *)tiqh || tp->lb_len < len + 32) {
+ text_lfree(tiqh);
+ goto newtp;
+ }
+ tp->ai = tp->insert = tp->offset = tp->owrite = 0;
+ if (lp != NULL) {
+ tp->len = len;
+ memmove(tp->lb, lp, len);
+ } else
+ tp->len = 0;
+ } else {
+newtp: if ((tp = text_init(sp, lp, len, len + 32)) == NULL)
+ return (1);
+ CIRCLEQ_INSERT_HEAD(tiqh, tp, q);
+ }
+
+ /* Set the starting line number. */
+ tp->lno = sp->lno;
+
+ /*
+ * Set the insert and overwrite counts. If overwriting characters,
+ * do insertion afterward. If not overwriting characters, assume
+ * doing insertion. If change is to a mark, emphasize it with an
+ * CH_ENDMARK
+ */
+ if (len) {
+ if (LF_ISSET(TXT_OVERWRITE)) {
+ tp->owrite = (tm->cno - sp->cno) + 1;
+ tp->insert = (len - tm->cno) - 1;
+ } else
+ tp->insert = len - sp->cno;
+
+ if (LF_ISSET(TXT_EMARK))
+ tp->lb[tm->cno] = CH_ENDMARK;
+ }
+
+ /*
+ * Many of the special cases in this routine are to handle autoindent
+ * support. Somebody decided that it would be a good idea if "^^D"
+ * and "0^D" deleted all of the autoindented characters. In an editor
+ * that takes single character input from the user, this beggars the
+ * imagination. Note also, "^^D" resets the next lines' autoindent,
+ * but "0^D" doesn't.
+ *
+ * We assume that autoindent only happens on empty lines, so insert
+ * and overwrite will be zero. If doing autoindent, figure out how
+ * much indentation we need and fill it in. Update input column and
+ * screen cursor as necessary.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT) && ai_line != OOBLNO) {
+ if (txt_auto(sp, ep, ai_line, NULL, 0, tp))
+ return (1);
+ sp->cno = tp->ai;
+ } else {
+ /*
+ * The cc and S commands have a special feature -- leading
+ * <blank> characters are handled as autoindent characters.
+ * Beauty!
+ */
+ if (LF_ISSET(TXT_AICHARS)) {
+ tp->offset = 0;
+ tp->ai = sp->cno;
+ } else
+ tp->offset = sp->cno;
+ }
+
+ /* If getting a command buffer from the user, there may be a prompt. */
+ if (LF_ISSET(TXT_PROMPT)) {
+ tp->lb[sp->cno++] = prompt;
+ ++tp->len;
+ ++tp->offset;
+ }
+
+ /*
+ * If appending after the end-of-line, add a space into the buffer
+ * and move the cursor right. This space is inserted, i.e. pushed
+ * along, and then deleted when the line is resolved. Assumes that
+ * the cursor is already positioned at the end of the line. This
+ * avoids the nastiness of having the cursor reside on a magical
+ * column, i.e. a column that doesn't really exist. The only down
+ * side is that we may wrap lines or scroll the screen before it's
+ * strictly necessary. Not a big deal.
+ */
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ tp->lb[sp->cno] = CH_CURSOR;
+ ++tp->len;
+ ++tp->insert;
+ }
+
+ /*
+ * Historic practice is that the wrapmargin value was a distance
+ * from the RIGHT-HAND column, not the left. It's more useful to
+ * us as a distance from the left-hand column.
+ *
+ * !!!/XXX
+ * Replay commands were not affected by the wrapmargin option in the
+ * historic 4BSD vi. What I found surprising was that people depend
+ * on it, as in this gem of a macro which centers lines:
+ *
+ * map #c $mq81a ^V^[81^V|D`qld0:s/ / /g^V^M$p
+ *
+ * Other historic versions of vi, notably Sun's, applied wrapmargin
+ * to replay lines as well.
+ *
+ * XXX
+ * Setting margin causes a significant performance hit. Normally
+ * we don't update the screen if there are keys waiting, but we
+ * have to if margin is set, otherwise the screen routines don't
+ * know where the cursor is.
+ *
+ * !!!
+ * One more special case. If an inserted <blank> character causes
+ * wrapmargin to split the line, the next user entered character is
+ * discarded if it's a <space> character.
+ */
+ if (LF_ISSET(TXT_REPLAY) || !LF_ISSET(TXT_WRAPMARGIN))
+ margin = 0;
+ else if ((margin = O_VAL(sp, O_WRAPMARGIN)) != 0)
+ margin = sp->cols - margin;
+ wmset = wmskip = 0;
+
+ /* Initialize abbreviations checks. */
+ if (F_ISSET(sp->gp, G_ABBREV) && LF_ISSET(TXT_MAPINPUT)) {
+ abb = A_INWORD;
+ ab_cnt = ab_turnoff = 0;
+ } else
+ abb = A_NOTSET;
+
+ /*
+ * Set up the dot command. Dot commands are done by saving the
+ * actual characters and replaying the input. We have to push
+ * the characters onto the key stack and then handle them normally,
+ * otherwise things like wrapmargin will fail.
+ *
+ * XXX
+ * It would be nice if we could swallow backspaces and such, but
+ * it's not all that easy to do. Another possibility would be to
+ * recognize full line insertions, which could be performed quickly,
+ * without replay.
+ */
+nullreplay:
+ rcol = 0;
+ if (replay = LF_ISSET(TXT_REPLAY)) {
+ /*
+ * !!!
+ * Historically, it wasn't an error to replay non-existent
+ * input. This test is necessary, we get here by the user
+ * doing an input command followed by a nul.
+ *
+ * !!!
+ * Historically, vi did not remap or reabbreviate replayed
+ * input. It did, however, beep at you if you changed an
+ * abbreviation and then replayed the input. We're not that
+ * compatible.
+ */
+ if (VIP(sp)->rep == NULL)
+ return (0);
+ if (term_push(sp, VIP(sp)->rep, VIP(sp)->rep_cnt, CH_NOMAP))
+ return (1);
+ testnr = 0;
+ abb = A_NOTSET;
+ LF_CLR(TXT_RECORD);
+ } else
+ testnr = 1;
+
+ unmap_tst = LF_ISSET(TXT_MAPINPUT) && LF_ISSET(TXT_INFOLINE);
+ iflags = LF_ISSET(TXT_MAPCOMMAND | TXT_MAPINPUT);
+ for (showmatch = 0, sig_reset = 0,
+ carat_st = C_NOTSET, hex = H_NOTSET, quoted = Q_NOTSET;;) {
+ /*
+ * Reset the line and update the screen. (The txt_showmatch()
+ * code refreshes the screen for us.) Don't refresh unless
+ * we're about to wait on a character or we need to know where
+ * the cursor really is.
+ */
+ if (showmatch || margin || !KEYS_WAITING(sp)) {
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+ if (showmatch) {
+ showmatch = 0;
+ txt_showmatch(sp, ep);
+ } else if (sp->s_refresh(sp, ep))
+ goto err;
+ }
+
+ /* Get the next character. */
+next_ch: tval = term_key(sp, &ikey, quoted == Q_THISCHAR ?
+ iflags & ~(TXT_MAPCOMMAND | TXT_MAPINPUT) : iflags);
+ ch = ikey.ch;
+
+ /* Restore the terminal state if it was modified. */
+ if (sig_reset && !tcgetattr(STDIN_FILENO, &t)) {
+ t.c_lflag |= ISIG;
+ t.c_iflag |= sig_ix;
+ sig_reset = 0;
+ (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &t);
+ }
+
+ /*
+ * !!!
+ * Historically, <interrupt> exited the user from text input
+ * mode or cancelled a colon command, and returned to command
+ * mode. It also beeped the terminal, but that seems a bit
+ * excessive.
+ */
+ if (tval != INP_OK) {
+ if (tval == INP_INTR)
+ goto k_escape;
+ goto err;
+ }
+
+ /* Abbreviation check. See comment in txt_abbrev(). */
+#define MAX_ABBREVIATION_EXPANSION 256
+ if (ikey.flags & CH_ABBREVIATED) {
+ if (++ab_cnt > MAX_ABBREVIATION_EXPANSION) {
+ term_flush(sp,
+ "Abbreviation exceeded maximum number of characters",
+ CH_ABBREVIATED);
+ ab_cnt = 0;
+ continue;
+ }
+ } else
+ ab_cnt = 0;
+
+ /* Wrapmargin check. */
+ if (wmskip) {
+ wmskip = 0;
+ if (ch == ' ')
+ goto next_ch;
+ }
+
+ /*
+ * !!!
+ * Historic feature. If the first character of the input is
+ * a nul, replay the previous input. This isn't documented
+ * anywhere, and is a great test of vi clones.
+ */
+ if (ch == '\0' && testnr) {
+ LF_SET(TXT_REPLAY);
+ goto nullreplay;
+ }
+ testnr = 0;
+
+ /*
+ * Check to see if the character fits into the input (and
+ * replay, if necessary) buffers. It isn't necessary to
+ * have tp->len bytes, since it doesn't consider overwrite
+ * characters, but not worth fixing.
+ */
+ if (LF_ISSET(TXT_RECORD)) {
+ BINC_GOTO(sp, VIP(sp)->rep, VIP(sp)->rep_len, rcol + 1);
+ VIP(sp)->rep[rcol++] = ch;
+ }
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+
+ /*
+ * If the character was quoted, replace the last character
+ * (the literal mark) with the new character. If quoted
+ * by someone else, simply insert the character.
+ */
+ if (ikey.flags & CH_QUOTED)
+ goto insq_ch;
+ if (quoted == Q_THISCHAR) {
+ --sp->cno;
+ ++tp->owrite;
+ quoted = Q_NOTSET;
+ goto insq_ch;
+ }
+ /*
+ * !!!
+ * Extension. If the user enters "<CH_HEX>[isxdigit()]*" we
+ * will try to use the value as a character. Anything else
+ * inserts the <CH_HEX> character, and resets hex mode.
+ */
+ if (hex == H_INHEX && !isxdigit(ch)) {
+ if (txt_hex(sp, tp))
+ goto err;
+ hex = H_NOTSET;
+ }
+
+ switch (ikey.value) {
+ case K_CR: /* Carriage return. */
+ case K_NL: /* New line. */
+ /* Return in script windows and the command line. */
+k_cr: if (LF_ISSET(TXT_CR)) {
+ /*
+ * If this was a map, we may have not displayed
+ * the line. Display it, just in case.
+ *
+ * If a script window and not the colon line,
+ * push a <cr> so it gets executed.
+ */
+ if (LF_ISSET(TXT_INFOLINE)) {
+ if (sp->s_change(sp,
+ ep, tp->lno, LINE_RESET))
+ goto err;
+ } else if (F_ISSET(sp, S_SCRIPT))
+ (void)term_push(sp, "\r", 1, CH_NOMAP);
+ goto k_escape;
+ }
+
+#define LINE_RESOLVE { \
+ /* \
+ * Handle abbreviations. If there was one, \
+ * discard the replay characters. \
+ */ \
+ if (abb == A_INWORD && !replay) { \
+ if (txt_abbrev(sp, tp, &ch, \
+ LF_ISSET(TXT_INFOLINE), &tmp, \
+ &ab_turnoff)) \
+ goto err; \
+ if (tmp) { \
+ if (LF_ISSET(TXT_RECORD)) \
+ rcol -= tmp; \
+ goto next_ch; \
+ } \
+ } \
+ if (abb != A_NOTSET) \
+ abb = A_NOTWORD; \
+ if (unmap_tst) \
+ txt_unmap(sp, tp, &iflags); \
+ /* Delete any appended cursor. */ \
+ if (LF_ISSET(TXT_APPENDEOL)) { \
+ --tp->len; \
+ --tp->insert; \
+ } \
+}
+ LINE_RESOLVE;
+
+ /*
+ * Save the current line information for restoration
+ * in txt_backup(). Set the new line length.
+ */
+ tp->sv_len = tp->len;
+ tp->sv_cno = sp->cno;
+ tp->len = sp->cno;
+
+ /* Update the old line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_RESET))
+ goto err;
+
+ /*
+ * Historic practice was to delete <blank> characters
+ * following the inserted newline. This affected the
+ * 'R', 'c', and 's' commands; 'c' and 's' retained
+ * the insert characters only, 'R' moved overwrite and
+ * insert characters into the next TEXT structure.
+ * All other commands simply deleted the overwrite
+ * characters. We have to keep track of the number of
+ * characters erased for the 'R' command so that we
+ * can get the final resolution of the line correct.
+ */
+ tp->R_erase = 0;
+ owrite = tp->owrite;
+ insert = tp->insert;
+ if (LF_ISSET(TXT_REPLACE) && owrite != 0) {
+ for (p = tp->lb + sp->cno;
+ owrite > 0 && isblank(*p);
+ ++p, --owrite, ++tp->R_erase);
+ if (owrite == 0)
+ for (; insert > 0 && isblank(*p);
+ ++p, ++tp->R_erase, --insert);
+ } else {
+ for (p = tp->lb + sp->cno + owrite;
+ insert > 0 && isblank(*p); ++p, --insert);
+ owrite = 0;
+ }
+
+ /* Set up bookkeeping for the new line. */
+ if ((ntp = text_init(sp, p,
+ insert + owrite, insert + owrite + 32)) == NULL)
+ goto err;
+ ntp->insert = insert;
+ ntp->owrite = owrite;
+ ntp->lno = tp->lno + 1;
+
+ /*
+ * Reset the autoindent line value. 0^D keeps the ai
+ * line from changing, ^D changes the level, even if
+ * there are no characters in the old line. Note,
+ * if using the current tp structure, use the cursor
+ * as the length, the user may have erased autoindent
+ * characters.
+ */
+ if (LF_ISSET(TXT_AUTOINDENT)) {
+ if (carat_st == C_NOCHANGE) {
+ if (txt_auto(sp, ep,
+ OOBLNO, &ait, ait.ai, ntp))
+ goto err;
+ FREE_SPACE(sp, ait.lb, ait.lb_len);
+ } else
+ if (txt_auto(sp, ep,
+ OOBLNO, tp, sp->cno, ntp))
+ goto err;
+ carat_st = C_NOTSET;
+ }
+
+ /* Reset the cursor. */
+ sp->lno = ntp->lno;
+ sp->cno = ntp->ai;
+
+ /*
+ * If we're here because wrapmargin was set and we've
+ * broken a line, there may be additional information
+ * (i.e. the start of a line) in the wmt structure.
+ */
+ if (wmset) {
+ if (wmt.len != 0 ||
+ wmt.insert != 0 || wmt.owrite != 0) {
+ BINC_GOTO(sp, ntp->lb, ntp->lb_len,
+ ntp->len + wmt.len + 32);
+ memmove(ntp->lb + sp->cno, wmt.lb,
+ wmt.len + wmt.insert + wmt.owrite);
+ ntp->len +=
+ wmt.len + wmt.insert + wmt.owrite;
+ ntp->insert = wmt.insert;
+ ntp->owrite = wmt.owrite;
+ sp->cno += wmt.len;
+ }
+ wmset = 0;
+ }
+
+ /* New lines are TXT_APPENDEOL. */
+ if (ntp->owrite == 0 && ntp->insert == 0) {
+ BINC_GOTO(sp,
+ ntp->lb, ntp->lb_len, ntp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ ntp->lb[sp->cno] = CH_CURSOR;
+ ++ntp->insert;
+ ++ntp->len;
+ }
+
+ /*
+ * Swap old and new TEXT's, and insert the new TEXT
+ * into the queue.
+ *
+ * !!!
+ * DON'T insert until the old line has been updated,
+ * or the inserted line count in line.c:file_gline()
+ * will be wrong.
+ */
+ tp = ntp;
+ CIRCLEQ_INSERT_TAIL(tiqh, tp, q);
+
+ /* Update the new line. */
+ if (sp->s_change(sp, ep, tp->lno, LINE_INSERT))
+ goto err;
+
+ /* Set the renumber bit. */
+ F_SET(sp, S_RENUMBER);
+
+ /* Refresh if nothing waiting. */
+ if (margin || !KEYS_WAITING(sp))
+ if (sp->s_refresh(sp, ep))
+ goto err;
+ goto next_ch;
+ case K_ESCAPE: /* Escape. */
+ if (!LF_ISSET(TXT_ESCAPE))
+ goto ins_ch;
+k_escape: LINE_RESOLVE;
+
+ /*
+ * Clean up for the 'R' command, restoring overwrite
+ * characters, and making them into insert characters.
+ */
+ if (LF_ISSET(TXT_REPLACE))
+ txt_Rcleanup(sp, tiqh, tp, lp, len);
+
+ /*
+ * If there are any overwrite characters, copy down
+ * any insert characters, and decrement the length.
+ */
+ if (tp->owrite) {
+ if (tp->insert)
+ memmove(tp->lb + sp->cno,
+ tp->lb + sp->cno + tp->owrite,
+ tp->insert);
+ tp->len -= tp->owrite;
+ }
+
+ /*
+ * Optionally resolve the lines into the file. Clear
+ * the input flag, the look-aside buffer is no longer
+ * valid. If not resolving the lines into the file,
+ * end it with a nul.
+ *
+ * XXX
+ * This is wrong, should pass back a length.
+ */
+ if (LF_ISSET(TXT_RESOLVE)) {
+ if (txt_resolve(sp, ep, tiqh, flags))
+ goto err;
+ F_CLR(sp, S_INPUT);
+ } else {
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+ tp->lb[tp->len] = '\0';
+ }
+
+ /*
+ * Set the return cursor position to rest on the last
+ * inserted character.
+ */
+ if (rp != NULL) {
+ rp->lno = tp->lno;
+ rp->cno = sp->cno ? sp->cno - 1 : 0;
+ if (sp->s_change(sp, ep, rp->lno, LINE_RESET))
+ goto err;
+ }
+ goto ret;
+ case K_CARAT: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_CARATSET;
+ goto ins_ch;
+ case K_ZERO: /* Delete autoindent chars. */
+ if (LF_ISSET(TXT_AUTOINDENT) && sp->cno <= tp->ai)
+ carat_st = C_ZEROSET;
+ goto ins_ch;
+ case K_CNTRLD: /* Delete autoindent char. */
+ /*
+ * If in the first column or no characters to erase,
+ * ignore the ^D (this matches historic practice). If
+ * not doing autoindent or already inserted non-ai
+ * characters, it's a literal. The latter test is done
+ * in the switch, as the CARAT forms are N + 1, not N.
+ */
+ if (!LF_ISSET(TXT_AUTOINDENT))
+ goto ins_ch;
+ if (sp->cno == 0)
+ break;
+ switch (carat_st) {
+ case C_CARATSET: /* ^^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+ /* Save the ai string for later. */
+ ait.lb = NULL;
+ ait.lb_len = 0;
+ BINC_GOTO(sp, ait.lb, ait.lb_len, tp->ai);
+ memmove(ait.lb, tp->lb, tp->ai);
+ ait.ai = ait.len = tp->ai;
+
+ carat_st = C_NOCHANGE;
+ goto leftmargin;
+ case C_ZEROSET: /* 0^D */
+ if (sp->cno > tp->ai + tp->offset + 1)
+ goto ins_ch;
+ carat_st = C_NOTSET;
+leftmargin: tp->lb[sp->cno - 1] = ' ';
+ tp->owrite += sp->cno - tp->offset;
+ tp->ai = 0;
+ sp->cno = tp->offset;
+ break;
+ case C_NOTSET: /* ^D */
+ if (sp->cno > tp->ai + tp->offset)
+ goto ins_ch;
+ (void)txt_outdent(sp, tp);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case K_VERASE: /* Erase the last character. */
+ /*
+ * If can erase over the prompt, return. Len is 0
+ * if backspaced over the prompt, 1 if only CR entered.
+ */
+ if (LF_ISSET(TXT_BS) && sp->cno <= tp->offset) {
+ tp->len = 0;
+ goto ret;
+ }
+
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ break;
+ }
+
+ /* If nothing to erase, bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase");
+ break;
+ }
+
+ /* Drop back one character. */
+ --sp->cno;
+
+ /*
+ * Increment overwrite, decrement ai if deleted.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ ++tp->owrite;
+ if (sp->cno < tp->ai)
+ --tp->ai;
+ break;
+ case K_VWERASE: /* Skip back one word. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /*
+ * If at offset, nothing to erase so bell the user.
+ */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase");
+ break;
+ }
+
+ /*
+ * First werase goes back to any autoindent
+ * and second werase goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+
+ /* Skip over trailing space characters. */
+ while (sp->cno > max && isblank(tp->lb[sp->cno - 1])) {
+ --sp->cno;
+ ++tp->owrite;
+ }
+ if (sp->cno == max)
+ break;
+ /*
+ * There are three types of word erase found on UNIX
+ * systems. They can be identified by how the string
+ * /a/b/c is treated -- as 1, 3, or 6 words. Historic
+ * vi had two classes of characters, and strings were
+ * delimited by them and <blank>'s, so, 6 words. The
+ * historic tty interface used <blank>'s to delimit
+ * strings, so, 1 word. The algorithm offered in the
+ * 4.4BSD tty interface (as stty altwerase) treats it
+ * as 3 words -- there are two classes of characters,
+ * and strings are delimited by them and <blank>'s.
+ * The difference is that the type of the first erased
+ * character erased is ignored, which is exactly right
+ * when erasing pathname components. Here, the options
+ * TXT_ALTWERASE and TXT_TTYWERASE specify the 4.4BSD
+ * tty interface and the historic tty driver behavior,
+ * respectively, and the default is the same as the
+ * historic vi behavior.
+ */
+ if (LF_ISSET(TXT_TTYWERASE))
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ else {
+ if (LF_ISSET(TXT_ALTWERASE)) {
+ --sp->cno;
+ ++tp->owrite;
+ if (isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ if (sp->cno > max)
+ tmp = inword(tp->lb[sp->cno - 1]);
+ while (sp->cno > max) {
+ --sp->cno;
+ ++tp->owrite;
+ if (tmp != inword(tp->lb[sp->cno - 1])
+ || isblank(tp->lb[sp->cno - 1]))
+ break;
+ }
+ }
+ break;
+ case K_VKILL: /* Restart this line. */
+ /*
+ * If at the beginning of the line, try and drop back
+ * to a previously inserted line.
+ */
+ if (sp->cno == 0) {
+ if ((ntp = txt_backup(sp,
+ ep, tiqh, tp, &flags)) == NULL)
+ goto err;
+ tp = ntp;
+ }
+
+ /* If at offset, nothing to erase so bell the user. */
+ if (sp->cno <= tp->offset) {
+ msgq(sp, M_BERR,
+ "No more characters to erase");
+ break;
+ }
+
+ /*
+ * First kill goes back to any autoindent
+ * and second kill goes back to the offset.
+ *
+ * !!!
+ * Historic vi did not permit users to use erase
+ * characters to delete autoindent characters.
+ */
+ if (tp->ai && sp->cno > tp->ai)
+ max = tp->ai;
+ else {
+ tp->ai = 0;
+ max = tp->offset;
+ }
+ tp->owrite += sp->cno - max;
+ sp->cno = max;
+ break;
+ case K_CNTRLT: /* Add autoindent char. */
+ if (!LF_ISSET(TXT_CNTRLT))
+ goto ins_ch;
+ if (txt_indent(sp, tp))
+ goto err;
+ goto ebuf_chk;
+#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_SUSPEND
+ case K_CNTRLZ:
+ /*
+ * XXX
+ * Note, historically suspend triggered an autowrite.
+ * That needs to be done to make this work correctly.
+ */
+ (void)sp->s_suspend(sp);
+ break;
+#endif
+#ifdef HISTORIC_PRACTICE_IS_TO_INSERT_NOT_REPAINT
+ case K_FORMFEED:
+ F_SET(sp, S_REFRESH);
+ break;
+#endif
+ case K_RIGHTBRACE:
+ case K_RIGHTPAREN:
+ showmatch = LF_ISSET(TXT_SHOWMATCH);
+ goto ins_ch;
+ case K_VLNEXT: /* Quote the next character. */
+ ch = '^';
+ quoted = Q_NEXTCHAR;
+ /*
+ * If there are no keys in the queue, reset the tty
+ * so that the user can enter a ^C, ^Q, ^S. There's
+ * an obvious race here, if the user entered the ^C
+ * already. There's nothing that we can do to fix
+ * that problem.
+ */
+ if (!KEYS_WAITING(sp) && !tcgetattr(STDIN_FILENO, &t)) {
+ t.c_lflag &= ~ISIG;
+ sig_ix = t.c_iflag & (IXON | IXOFF);
+ t.c_iflag &= ~(IXON | IXOFF);
+ sig_reset = 1;
+ (void)tcsetattr(STDIN_FILENO,
+ TCSASOFT | TCSADRAIN, &t);
+ }
+ /*
+ * XXX
+ * Pass the tests for abbreviations, so ":ab xa XA",
+ * "ixa^V<space>" works. Historic vi did something
+ * weird here: ":ab x y", "ix\<space>" resulted in
+ * "<space>x\", for some unknown reason. Had to be
+ * a bug.
+ */
+ goto insl_ch;
+ case K_HEXCHAR:
+ hex = H_NEXTCHAR;
+ goto insq_ch;
+ default: /* Insert the character. */
+ins_ch: /*
+ * Historically, vi eliminated nul's out of hand. If
+ * the beautify option was set, it also deleted any
+ * unknown ASCII value less than space (040) and the
+ * del character (0177), except for tabs. Unknown is
+ * a key word here. Most vi documentation claims that
+ * it deleted everything but <tab>, <nl> and <ff>, as
+ * that's what the original 4BSD documentation said.
+ * This is obviously wrong, however, as <esc> would be
+ * included in that list. What we do is eliminate any
+ * unquoted, iscntrl() character that wasn't a replay
+ * and wasn't handled specially, except <tab> or <ff>.
+ */
+ if (LF_ISSET(TXT_BEAUTIFY) && iscntrl(ch) &&
+ ikey.value != K_FORMFEED && ikey.value != K_TAB) {
+ msgq(sp, M_BERR,
+ "Illegal character; quote to enter");
+ break;
+ }
+insq_ch: /*
+ * If entering a non-word character after a word, check
+ * for abbreviations. If there was one, discard the
+ * replay characters. If entering a blank character,
+ * check for unmap commands, as well.
+ */
+ if (!inword(ch)) {
+ if (abb == A_INWORD && !replay) {
+ if (txt_abbrev(sp, tp, &ch,
+ LF_ISSET(TXT_INFOLINE),
+ &tmp, &ab_turnoff))
+ goto err;
+ if (tmp) {
+ if (LF_ISSET(TXT_RECORD))
+ rcol -= tmp;
+ goto next_ch;
+ }
+ }
+ if (isblank(ch) && unmap_tst)
+ txt_unmap(sp, tp, &iflags);
+ }
+ if (abb != A_NOTSET)
+ abb = inword(ch) ? A_INWORD : A_NOTWORD;
+
+insl_ch: if (tp->owrite) /* Overwrite a character. */
+ --tp->owrite;
+ else if (tp->insert) { /* Insert a character. */
+ ++tp->len;
+ if (tp->insert == 1)
+ tp->lb[sp->cno + 1] = tp->lb[sp->cno];
+ else
+ memmove(tp->lb + sp->cno + 1,
+ tp->lb + sp->cno, tp->insert);
+ }
+
+ tp->lb[sp->cno++] = ch;
+
+ /* Check to see if we've crossed the margin. */
+ if (margin) {
+ if (sp->s_column(sp, ep, &col))
+ goto err;
+ if (col >= margin) {
+ if (txt_margin(sp,
+ tp, &ch, &wmt, flags, &tmp))
+ goto err;
+ if (tmp) {
+ if (isblank(ch))
+ wmskip = 1;
+ wmset = 1;
+ goto k_cr;
+ }
+ }
+ }
+
+ /*
+ * If we've reached the end of the buffer, then we
+ * need to switch into insert mode. This happens
+ * when there's a change to a mark and the user puts
+ * in more characters than the length of the motion.
+ */
+ebuf_chk: if (sp->cno >= tp->len) {
+ BINC_GOTO(sp, tp->lb, tp->lb_len, tp->len + 1);
+ LF_SET(TXT_APPENDEOL);
+ tp->lb[sp->cno] = CH_CURSOR;
+ ++tp->insert;
+ ++tp->len;
+ }
+
+ if (hex == H_NEXTCHAR)
+ hex = H_INHEX;
+ if (quoted == Q_NEXTCHAR)
+ quoted = Q_THISCHAR;
+ break;
+ }
+#if defined(DEBUG) && 1
+ if (sp->cno + tp->insert + tp->owrite != tp->len)
+ msgq(sp, M_ERR,
+ "len %u != cno: %u ai: %u insert %u overwrite %u",
+ tp->len, sp->cno, tp->ai, tp->insert, tp->owrite);
+ tp->len = sp->cno + tp->insert + tp->owrite;
+#endif
+ }
+
+ /* Clear input flag. */
+ret: F_CLR(sp, S_INPUT);
+
+ if (LF_ISSET(TXT_RECORD))
+ VIP(sp)->rep_cnt = rcol;
+ return (eval);
+
+err: /* Error jumps. */
+binc_err:
+ eval = 1;
+ txt_err(sp, ep, tiqh);
+ goto ret;
+}
+
+/*
+ * txt_abbrev --
+ * Handle abbreviations.
+ */
+static int
+txt_abbrev(sp, tp, pushcp, isinfoline, didsubp, turnoffp)
+ SCR *sp;
+ TEXT *tp;
+ CHAR_T *pushcp;
+ int isinfoline, *didsubp, *turnoffp;
+{
+ CHAR_T ch;
+ SEQ *qp;
+ size_t len, off;
+ char *p;
+
+ /*
+ * Find the start of the "word". Historically, abbreviations
+ * could be preceded by any non-word character or the beginning
+ * of the entry, .e.g inserting an abbreviated string in the
+ * middle of another string triggered the replacement.
+ */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (!inword(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * !!!
+ * Historic vi exploded abbreviations on the command line. This has
+ * obvious problems in that unabbreviating the string can be extremely
+ * tricky, particularly if the string has, say, an embedded escape
+ * character. Personally, I think it's a stunningly bad idea. Other
+ * examples of problems this caused in historic vi are:
+ * :ab foo bar
+ * :ab foo baz
+ * results in "bar" being abbreviated to "baz", which wasn't what the
+ * user had in mind at all. Also, the commands:
+ * :ab foo bar
+ * :unab foo<space>
+ * resulted in an error message that "bar" wasn't mapped. Finally,
+ * since the string was already exploded by the time the unabbreviate
+ * command got it, all it knew was that an abbreviation had occurred.
+ * Cleverly, it checked the replacement string for its unabbreviation
+ * match, which meant that the commands:
+ * :ab foo1 bar
+ * :ab foo2 bar
+ * :unab foo2
+ * unabbreviate "foo1", and the commands:
+ * :ab foo bar
+ * :ab bar baz
+ * unabbreviate "foo"!
+ *
+ * Anyway, people neglected to first ask my opinion before they wrote
+ * macros that depend on this stuff, so, we make this work as follows.
+ * When checking for an abbreviation on the command line, if we get a
+ * string which is <blank> terminated and which starts at the beginning
+ * of the line, we check to see it is the abbreviate or unabbreviate
+ * commands. If it is, turn abbreviations off and return as if no
+ * abbreviation was found. Note also, minor trickiness, so that if
+ * the user erases the line and starts another command, we turn the
+ * abbreviations back on.
+ *
+ * This makes the layering look like a Nachos Supreme.
+ */
+ *didsubp = 0;
+ if (isinfoline)
+ if (off == tp->ai || off == tp->offset)
+ if (ex_is_abbrev(p, len)) {
+ *turnoffp = 1;
+ return (0);
+ } else
+ *turnoffp = 0;
+ else
+ if (*turnoffp)
+ return (0);
+
+ /* Check for any abbreviations. */
+ if ((qp = seq_find(sp, NULL, p, len, SEQ_ABBREV, NULL)) == NULL)
+ return (0);
+
+ /*
+ * Push the abbreviation onto the tty stack. Historically, characters
+ * resulting from an abbreviation expansion were themselves subject to
+ * map expansions, O_SHOWMATCH matching etc. This means the expanded
+ * characters will be re-tested for abbreviations. It's difficult to
+ * know what historic practice in this case was, since abbreviations
+ * were applied to :colon command lines, so entering abbreviations that
+ * looped was tricky, although possible. In addition, obvious loops
+ * didn't work as expected. (The command ':ab a b|ab b c|ab c a' will
+ * silently only implement and/or display the last abbreviation.)
+ *
+ * This implementation doesn't recover well from such abbreviations.
+ * The main input loop counts abbreviated characters, and, when it
+ * reaches a limit, discards any abbreviated characters on the queue.
+ * It's difficult to back up to the original position, as the replay
+ * queue would have to be adjusted, and the line state when an initial
+ * abbreviated character was received would have to be saved.
+ */
+ ch = *pushcp;
+ if (term_push(sp, &ch, 1, CH_ABBREVIATED))
+ return (1);
+ if (term_push(sp, qp->output, qp->olen, CH_ABBREVIATED))
+ return (1);
+
+ /* Move to the start of the abbreviation, adjust the length. */
+ sp->cno -= len;
+ tp->len -= len;
+
+ /* Copy any insert characters back. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len, tp->insert);
+
+ /*
+ * We return the length of the abbreviated characters. This is so
+ * the calling routine can replace the replay characters with the
+ * abbreviation. This means that subsequent '.' commands will produce
+ * the same text, regardless of intervening :[un]abbreviate commands.
+ * This is historic practice.
+ */
+ *didsubp = len;
+ return (0);
+}
+
+/*
+ * txt_unmap --
+ * Handle the unmap command.
+ */
+static void
+txt_unmap(sp, tp, iflagsp)
+ SCR *sp;
+ TEXT *tp;
+ u_int *iflagsp;
+{
+ size_t len, off;
+ char *p;
+
+ /* Find the beginning of this "word". */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off) {
+ if (isblank(*p)) {
+ ++p;
+ break;
+ }
+ ++len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+
+ /*
+ * !!!
+ * Historic vi exploded input mappings on the command line. See the
+ * txt_abbrev() routine for an explanation of the problems inherent
+ * in this.
+ *
+ * We make this work as follows. If we get a string which is <blank>
+ * terminated and which starts at the beginning of the line, we check
+ * to see it is the unmap command. If it is, we return that the input
+ * mapping should be turned off. Note also, minor trickiness, so that
+ * if the user erases the line and starts another command, we go ahead
+ * an turn mapping back on.
+ */
+ if ((off == tp->ai || off == tp->offset) && ex_is_unmap(p, len))
+ *iflagsp &= ~TXT_MAPINPUT;
+ else
+ *iflagsp |= TXT_MAPINPUT;
+}
+
+/*
+ * txt_ai_resolve --
+ * When a line is resolved by <esc> or <cr>, review autoindent
+ * characters.
+ */
+static void
+txt_ai_resolve(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long ts;
+ int del;
+ size_t cno, len, new, old, scno, spaces, tab_after_sp, tabs;
+ char *p;
+
+ /*
+ * If the line is empty, has an offset, or no autoindent
+ * characters, we're done.
+ */
+ if (!tp->len || tp->offset || !tp->ai)
+ return;
+
+ /*
+ * If the length is less than or equal to the autoindent
+ * characters, delete them.
+ */
+ if (tp->len <= tp->ai) {
+ tp->len = tp->ai = 0;
+ if (tp->lno == sp->lno)
+ sp->cno = 0;
+ return;
+ }
+
+ /*
+ * The autoindent characters plus any leading <blank> characters
+ * in the line are resolved into the minimum number of characters.
+ * Historic practice.
+ */
+ ts = O_VAL(sp, O_TABSTOP);
+
+ /* Figure out the last <blank> screen column. */
+ for (p = tp->lb, scno = 0, len = tp->len,
+ spaces = tab_after_sp = 0; len-- && isblank(*p); ++p)
+ if (*p == '\t') {
+ if (spaces)
+ tab_after_sp = 1;
+ scno += STOP_OFF(scno, ts);
+ } else {
+ ++spaces;
+ ++scno;
+ }
+
+ /*
+ * If there are no spaces, or no tabs after spaces and less than
+ * ts spaces, it's already minimal.
+ */
+ if (!spaces || !tab_after_sp && spaces < ts)
+ return;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = 0, tabs = 0; cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /*
+ * Figure out how many characters we're dropping -- if we're not
+ * dropping any, it's already minimal, we're done.
+ */
+ old = p - tp->lb;
+ new = spaces + tabs;
+ if (old == new)
+ return;
+
+ /* Shift the rest of the characters down, adjust the counts. */
+ del = old - new;
+ memmove(p - del, p, tp->len - old);
+ tp->len -= del;
+
+ /* If the cursor was on this line, adjust it as well. */
+ if (sp->lno == tp->lno)
+ sp->cno -= del;
+
+ /* Fill in space/tab characters. */
+ for (p = tp->lb; tabs--;)
+ *p++ = '\t';
+ while (spaces--)
+ *p++ = ' ';
+}
+
+/*
+ * txt_auto --
+ * Handle autoindent. If aitp isn't NULL, use it, otherwise,
+ * retrieve the line.
+ */
+int
+txt_auto(sp, ep, lno, aitp, len, tp)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t len;
+ TEXT *aitp, *tp;
+{
+ size_t nlen;
+ char *p, *t;
+
+ if (aitp == NULL) {
+ /*
+ * If the ex append command is executed with an address of 0,
+ * it's possible to get here with a line number of 0. Return
+ * an indent of 0.
+ */
+ if (lno == 0) {
+ tp->ai = 0;
+ return (0);
+ }
+ if ((t = file_gline(sp, ep, lno, &len)) == NULL)
+ return (1);
+ } else
+ t = aitp->lb;
+
+ /* Count whitespace characters. */
+ for (p = t; len > 0; ++p, --len)
+ if (!isblank(*p))
+ break;
+
+ /* Set count, check for no indentation. */
+ if ((nlen = (p - t)) == 0)
+ return (0);
+
+ /* Make sure the buffer's big enough. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + nlen);
+
+ /* Copy the buffer's current contents up. */
+ if (tp->len != 0)
+ memmove(tp->lb + nlen, tp->lb, tp->len);
+ tp->len += nlen;
+
+ /* Copy the indentation into the new buffer. */
+ memmove(tp->lb, t, nlen);
+
+ /* Set the autoindent count. */
+ tp->ai = nlen;
+ return (0);
+}
+
+/*
+ * txt_backup --
+ * Back up to the previously edited line.
+ */
+static TEXT *
+txt_backup(sp, ep, tiqh, tp, flagsp)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ TEXT *tp;
+ u_int *flagsp;
+{
+ TEXT *ntp;
+ u_int flags;
+
+ /* Get a handle on the previous TEXT structure. */
+ if ((ntp = tp->q.cqe_prev) == (void *)tiqh) {
+ msgq(sp, M_BERR, "Already at the beginning of the insert");
+ return (tp);
+ }
+
+ /* Reset the cursor, bookkeeping. */
+ sp->lno = ntp->lno;
+ sp->cno = ntp->sv_cno;
+ ntp->len = ntp->sv_len;
+
+ /* Handle appending to the line. */
+ flags = *flagsp;
+ if (ntp->owrite == 0 && ntp->insert == 0) {
+ ntp->lb[ntp->len] = CH_CURSOR;
+ ++ntp->insert;
+ ++ntp->len;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ LF_CLR(TXT_APPENDEOL);
+ *flagsp = flags;
+
+ /* Release the current TEXT. */
+ CIRCLEQ_REMOVE(tiqh, tp, q);
+ text_free(tp);
+
+ /* Update the old line on the screen. */
+ if (sp->s_change(sp, ep, ntp->lno + 1, LINE_DELETE))
+ return (NULL);
+
+ /* Return the new/current TEXT. */
+ return (ntp);
+}
+
+/*
+ * txt_err --
+ * Handle an error during input processing.
+ */
+static void
+txt_err(sp, ep, tiqh)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+{
+ recno_t lno;
+ size_t len;
+
+ /*
+ * The problem with input processing is that the cursor is at an
+ * indeterminate position since some input may have been lost due
+ * to a malloc error. So, try to go back to the place from which
+ * the cursor started, knowing that it may no longer be available.
+ *
+ * We depend on at least one line number being set in the text
+ * chain.
+ */
+ for (lno = tiqh->cqh_first->lno;
+ file_gline(sp, ep, lno, &len) == NULL && lno > 0; --lno);
+
+ sp->lno = lno == 0 ? 1 : lno;
+ sp->cno = 0;
+
+ /* Redraw the screen, just in case. */
+ F_SET(sp, S_REDRAW);
+}
+
+/*
+ * txt_hex --
+ * Let the user insert any character value they want.
+ *
+ * !!!
+ * This is an extension. The pattern "^X[0-9a-fA-F]*" is a way
+ * for the user to specify a character value which their keyboard
+ * may not be able to enter.
+ */
+static int
+txt_hex(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ CHAR_T savec;
+ size_t len, off;
+ u_long value;
+ char *p, *wp;
+
+ /*
+ * Null-terminate the string. Since nul isn't a legal hex value,
+ * this should be okay, and lets us use a local routine, which
+ * presumably understands the character set, to convert the value.
+ */
+ savec = tp->lb[sp->cno];
+ tp->lb[sp->cno] = 0;
+
+ /* Find the previous CH_HEX character. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --p, --off, ++len) {
+ if (*p == CH_HEX) {
+ wp = p + 1;
+ break;
+ }
+ /* Not on this line? Shouldn't happen. */
+ if (off == tp->ai || off == tp->offset)
+ goto nothex;
+ }
+
+ /* If length of 0, then it wasn't a hex value. */
+ if (len == 0)
+ goto nothex;
+
+ /* Get the value. */
+ errno = 0;
+ value = strtol(wp, NULL, 16);
+ if (errno || value > MAX_CHAR_T) {
+nothex: tp->lb[sp->cno] = savec;
+ return (0);
+ }
+
+ /* Restore the original character. */
+ tp->lb[sp->cno] = savec;
+
+ /* Adjust the bookkeeping. */
+ sp->cno -= len;
+ tp->len -= len;
+ tp->lb[sp->cno - 1] = value;
+
+ /* Copy down any overwrite characters. */
+ if (tp->owrite)
+ memmove(tp->lb + sp->cno,
+ tp->lb + sp->cno + len, tp->owrite);
+
+ /* Copy down any insert characters. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + tp->owrite,
+ tp->lb + sp->cno + tp->owrite + len, tp->insert);
+
+ return (0);
+}
+
+/*
+ * Txt_indent and txt_outdent are truly strange. ^T and ^D do movements
+ * to the next or previous shiftwidth value, i.e. for a 1-based numbering,
+ * with shiftwidth=3, ^T moves a cursor on the 7th, 8th or 9th column to
+ * the 10th column, and ^D moves it back.
+ *
+ * !!!
+ * The ^T and ^D characters in historical vi only had special meaning when
+ * they were the first characters typed after entering text input mode.
+ * Since normal erase characters couldn't erase autoindent (in this case
+ * ^T) characters, this meant that inserting text into previously existing
+ * text was quite strange, ^T only worked if it was the first keystroke,
+ * and then it could only be erased by using ^D. This implementation treats
+ * ^T specially anywhere it occurs in the input, and permits the standard
+ * erase characters to erase characters inserted using it.
+ *
+ * XXX
+ * Technically, txt_indent, txt_outdent should part of the screen interface,
+ * as they require knowledge of the size of a space character on the screen.
+ * (Not the size of tabs, because tabs are logically composed of spaces.)
+ * They're left in the text code because they're complicated, not to mention
+ * the gruesome awareness that if spaces aren't a single column on the screen
+ * for any language, we're into some serious, ah, for lack of a better word,
+ * "issues".
+ */
+
+/*
+ * txt_indent --
+ * Handle ^T indents.
+ */
+static int
+txt_indent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces, tabs;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Count up spaces/tabs needed to get to the target. */
+ for (cno = scno, scno += STOP_OFF(scno, sw), tabs = 0;
+ cno + STOP_OFF(cno, ts) <= scno; ++tabs)
+ cno += STOP_OFF(cno, ts);
+ spaces = scno - cno;
+
+ /* Put space/tab characters in place of any overwrite characters. */
+ for (; tp->owrite && tabs; --tp->owrite, --tabs, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; tp->owrite && spaces; --tp->owrite, --spaces, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+
+ if (!tabs && !spaces)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces + tabs);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces + tabs,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space/tab characters. */
+ for (; tabs--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = '\t';
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_outdent --
+ * Handle ^D outdents.
+ *
+ */
+static int
+txt_outdent(sp, tp)
+ SCR *sp;
+ TEXT *tp;
+{
+ u_long sw, ts;
+ size_t cno, off, scno, spaces;
+
+ ts = O_VAL(sp, O_TABSTOP);
+ sw = O_VAL(sp, O_SHIFTWIDTH);
+
+ /* Get the current screen column. */
+ for (off = scno = 0; off < sp->cno; ++off)
+ if (tp->lb[off] == '\t')
+ scno += STOP_OFF(scno, ts);
+ else
+ ++scno;
+
+ /* Get the previous shiftwidth column. */
+ for (cno = scno; --scno % sw != 0;);
+
+ /* Decrement characters until less than or equal to that slot. */
+ for (; cno > scno; --sp->cno, --tp->ai, ++tp->owrite)
+ if (tp->lb[--off] == '\t')
+ cno -= STOP_OFF(cno, ts);
+ else
+ --cno;
+
+ /* Spaces needed to get to the target. */
+ spaces = scno - cno;
+
+ /* Maybe just a delete. */
+ if (spaces == 0)
+ return (0);
+
+ /* Make sure there's enough room. */
+ BINC_RET(sp, tp->lb, tp->lb_len, tp->len + spaces);
+
+ /* Use up any overwrite characters. */
+ for (; tp->owrite && spaces; --spaces, ++tp->ai, --tp->owrite)
+ tp->lb[sp->cno++] = ' ';
+
+ /* Maybe that was enough. */
+ if (spaces == 0)
+ return (0);
+
+ /* Move the insert characters out of the way. */
+ if (tp->insert)
+ memmove(tp->lb + sp->cno + spaces,
+ tp->lb + sp->cno, tp->insert);
+
+ /* Add new space characters. */
+ for (; spaces--; ++tp->len, ++tp->ai)
+ tp->lb[sp->cno++] = ' ';
+ return (0);
+}
+
+/*
+ * txt_resolve --
+ * Resolve the input text chain into the file.
+ */
+static int
+txt_resolve(sp, ep, tiqh, flags)
+ SCR *sp;
+ EXF *ep;
+ TEXTH *tiqh;
+ u_int flags;
+{
+ TEXT *tp;
+ recno_t lno;
+
+ /*
+ * The first line replaces a current line, and all subsequent lines
+ * are appended into the file. Resolve autoindented characters for
+ * each line before committing it.
+ */
+ tp = tiqh->cqh_first;
+ if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp);
+ if (file_sline(sp, ep, tp->lno, tp->lb, tp->len))
+ return (1);
+
+ for (lno = tp->lno; (tp = tp->q.cqe_next) != (void *)sp->tiqp; ++lno) {
+ if (LF_ISSET(TXT_AUTOINDENT))
+ txt_ai_resolve(sp, tp);
+ if (file_aline(sp, ep, 0, lno, tp->lb, tp->len))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * txt_showmatch --
+ * Show a character match.
+ *
+ * !!!
+ * Historic vi tried to display matches even in the :colon command line.
+ * I think not.
+ */
+static void
+txt_showmatch(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ struct timeval second;
+ VCS cs;
+ MARK m;
+ fd_set zero;
+ int cnt, endc, startc;
+
+ /*
+ * Do a refresh first, in case the v_ntext() code hasn't done
+ * one in awhile, so the user can see what we're complaining
+ * about.
+ */
+ if (sp->s_refresh(sp, ep))
+ return;
+ /*
+ * We don't display the match if it's not on the screen. Find
+ * out what the first character on the screen is.
+ */
+ if (sp->s_position(sp, ep, &m, 0, P_TOP))
+ return;
+
+ /* Initialize the getc() interface. */
+ cs.cs_lno = sp->lno;
+ cs.cs_cno = sp->cno - 1;
+ if (cs_init(sp, ep, &cs))
+ return;
+ startc = (endc = cs.cs_ch) == ')' ? '(' : '{';
+
+ /* Search for the match. */
+ for (cnt = 1;;) {
+ if (cs_prev(sp, ep, &cs))
+ return;
+ if (cs.cs_lno < m.lno ||
+ cs.cs_lno == m.lno && cs.cs_cno < m.cno)
+ return;
+ if (cs.cs_flags != 0) {
+ if (cs.cs_flags == CS_EOF || cs.cs_flags == CS_SOF) {
+ (void)sp->s_bell(sp);
+ return;
+ }
+ continue;
+ }
+ if (cs.cs_ch == endc)
+ ++cnt;
+ else if (cs.cs_ch == startc && --cnt == 0)
+ break;
+ }
+
+ /* Move to the match. */
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ sp->lno = cs.cs_lno;
+ sp->cno = cs.cs_cno;
+ (void)sp->s_refresh(sp, ep);
+
+ /*
+ * Sleep(3) is eight system calls. Do it fast -- besides,
+ * I don't want to wait an entire second.
+ */
+ FD_ZERO(&zero);
+ second.tv_sec = O_VAL(sp, O_MATCHTIME) / 10;
+ second.tv_usec = (O_VAL(sp, O_MATCHTIME) % 10) * 100000L;
+ (void)select(0, &zero, &zero, &zero, &second);
+
+ /* Return to the current location. */
+ sp->lno = m.lno;
+ sp->cno = m.cno;
+ (void)sp->s_refresh(sp, ep);
+}
+
+/*
+ * txt_margin --
+ * Handle margin wrap.
+ */
+static int
+txt_margin(sp, tp, chp, wmtp, flags, didbreak)
+ SCR *sp;
+ TEXT *tp, *wmtp;
+ CHAR_T *chp;
+ int *didbreak;
+ u_int flags;
+{
+ size_t len, off;
+ char *p, *wp;
+
+ /* Find the nearest previous blank. */
+ for (off = sp->cno - 1, p = tp->lb + off, len = 0;; --off, --p, ++len) {
+ if (isblank(*p)) {
+ wp = p + 1;
+ break;
+ }
+
+ /*
+ * If reach the start of the line, there's nowhere to break.
+ *
+ * !!!
+ * Historic vi belled each time a character was entered after
+ * crossing the margin until a space was entered which could
+ * be used to break the line. I don't as it tends to wake the
+ * cats.
+ */
+ if (off == tp->ai || off == tp->offset) {
+ *didbreak = 0;
+ return (0);
+ }
+ }
+
+ /*
+ * Store saved information about the rest of the line in the
+ * wrapmargin TEXT structure.
+ */
+ wmtp->lb = p + 1;
+ wmtp->len = len;
+ wmtp->insert = LF_ISSET(TXT_APPENDEOL) ? tp->insert - 1 : tp->insert;
+ wmtp->owrite = tp->owrite;
+
+ /* Correct current bookkeeping information. */
+ sp->cno -= len;
+ if (LF_ISSET(TXT_APPENDEOL)) {
+ tp->len -= len + tp->owrite + (tp->insert - 1);
+ tp->insert = 1;
+ } else {
+ tp->len -= len + tp->owrite + tp->insert;
+ tp->insert = 0;
+ }
+ tp->owrite = 0;
+
+ /*
+ * !!!
+ * Delete any trailing whitespace from the current line.
+ */
+ for (;; --p, --off) {
+ if (!isblank(*p))
+ break;
+ --sp->cno;
+ --tp->len;
+ if (off == tp->ai || off == tp->offset)
+ break;
+ }
+ *didbreak = 1;
+ return (0);
+}
+
+/*
+ * txt_Rcleanup --
+ * Resolve the input line for the 'R' command.
+ */
+static void
+txt_Rcleanup(sp, tiqh, tp, lp, olen)
+ SCR *sp;
+ TEXTH *tiqh;
+ TEXT *tp;
+ const char *lp;
+ const size_t olen;
+{
+ TEXT *ttp;
+ size_t ilen, tmp;
+
+ /*
+ * Check to make sure that the cursor hasn't moved beyond
+ * the end of the line.
+ */
+ if (tp->owrite == 0)
+ return;
+
+ /*
+ * Calculate how many characters the user has entered,
+ * plus the blanks erased by <carriage-return>/<newline>s.
+ */
+ for (ttp = tiqh->cqh_first, ilen = 0;;) {
+ ilen += ttp == tp ? sp->cno : ttp->len + ttp->R_erase;
+ if ((ttp = ttp->q.cqe_next) == (void *)sp->tiqp)
+ break;
+ }
+
+ /*
+ * If the user has entered less characters than the original line
+ * was long, restore any overwriteable characters to the original
+ * characters, and make them insert characters. We don't copy them
+ * anywhere, because the 'R' command doesn't have insert characters.
+ */
+ if (ilen < olen) {
+ tmp = MIN(tp->owrite, olen - ilen);
+ memmove(tp->lb + sp->cno, lp + ilen, tmp);
+ tp->owrite -= tmp;
+ tp->insert += tmp;
+ }
+}
diff --git a/usr.bin/vi/vi/v_paragraph.c b/usr.bin/vi/vi/v_paragraph.c
new file mode 100644
index 0000000..de417ec
--- /dev/null
+++ b/usr.bin/vi/vi/v_paragraph.c
@@ -0,0 +1,370 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_paragraph.c 8.20 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+#define INTEXT_CHECK { \
+ if (len == 0 || v_isempty(p, len)) { \
+ if (!--cnt) \
+ goto found; \
+ pstate = P_INBLANK; \
+ } \
+ /* \
+ * !!! \
+ * Historic documentation (USD:15-11, 4.2) said that formfeed \
+ * characters (^L) in the first column delimited paragraphs. \
+ * The historic vi code mentions formfeed characters, but never \
+ * implements them. It seems reasonable, do it. \
+ */ \
+ if (p[0] == '\014') { \
+ if (!--cnt) \
+ goto found; \
+ continue; \
+ } \
+ if (p[0] != '.' || len < 2) \
+ continue; \
+ for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2) \
+ if (lp[0] == p[1] && \
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) && \
+ !--cnt) \
+ goto found; \
+}
+
+/*
+ * v_paragraphf -- [count]}
+ * Move forward count paragraphs.
+ *
+ * Paragraphs are empty lines after text, formfeed characters, or values
+ * from the paragraph or section options.
+ */
+int
+v_paragraphf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t lastlen, len;
+ recno_t cnt, lastlno, lno;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * If the starting cursor position is at or before any non-blank
+ * characters in the line, i.e. the movement is cutting all of the
+ * line's text, the buffer is in line mode. It's a lot easier to
+ * check here, because we know that the end is going to be the start
+ * or end of a line.
+ *
+ * This was historical practice in vi, with a single exception. If
+ * the paragraph movement was from the start of the last line to EOF,
+ * then all the characters were deleted from the last line, but the
+ * line itself remained. If somebody complains, don't pause, don't
+ * hesitate, just hit them.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ else {
+ vp->m_stop = vp->m_start;
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ if (vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+ }
+
+ /* Figure out what state we're currently in. */
+ lno = vp->m_start.lno;
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto eof;
+
+ /*
+ * If we start in text, we want to switch states
+ * (2 * N - 1) times, in non-text, (2 * N) times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+ }
+
+ for (;;) {
+ lastlno = lno;
+ lastlen = len;
+ if ((p = file_gline(sp, ep, ++lno, &len)) == NULL)
+ goto eof;
+ switch (pstate) {
+ case P_INTEXT:
+ INTEXT_CHECK;
+ break;
+ case P_INBLANK:
+ if (len == 0 || v_isempty(p, len))
+ break;
+ if (--cnt) {
+ pstate = P_INTEXT;
+ break;
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range,
+ * VC_D and VC_Y stay at the start. Ignore VC_C and
+ * VC_DEF. Adjust the end of the range for motion
+ * commands; historically, a motion component was to
+ * the end of the previous line, whereas the movement
+ * command was to the start of the new "paragraph".
+ */
+found: if (ISMOTION(vp)) {
+ vp->m_stop.lno = lastlno;
+ vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
+ vp->m_final = vp->m_start;
+ } else {
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ vp->m_final = vp->m_stop;
+ }
+ return (0);
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * !!!
+ * Adjust end of the range for motion commands; EOF is a movement
+ * sink. The } command historically moved to the end of the last
+ * line, not the beginning, from any position before the end of the
+ * last line. It also historically worked on empty files, so we
+ * have to make it okay.
+ */
+eof: if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) {
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (vp->m_start.lno != 1 || lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ vp->m_start.cno = 0;
+ return (0);
+ }
+ if (vp->m_start.cno == (len ? len - 1 : 0)) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+ }
+ /*
+ * !!!
+ * Non-motion commands move to the end of the range, VC_D
+ * and VC_Y stay at the start. Ignore VC_C and VC_DEF.
+ *
+ * If deleting the line (which happens if deleting to EOF), then
+ * cursor movement is to the first nonblank.
+ */
+ if (F_ISSET(vp, VC_D)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ }
+ vp->m_stop.lno = lno - 1;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_paragraphb -- [count]{
+ * Move backward count paragraphs.
+ */
+int
+v_paragraphb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { P_INTEXT, P_INBLANK } pstate;
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *lp;
+
+ /*
+ * !!!
+ * Check for SOF. The historic vi didn't complain if users hit SOF
+ * repeatedly, unless it was part of a motion command. There is no
+ * question but that Emerson's editor of choice was vi.
+ *
+ * The { command historically moved to the beginning of the first
+ * line if invoked on the first line.
+ *
+ * !!!
+ * If the starting cursor position is in the first column (backward
+ * paragraph movements did NOT historically pay attention to non-blank
+ * characters) i.e. the movement is cutting the entire line, the buffer
+ * is in line mode. Cuts from the beginning of the line also did not
+ * cut the current line, but started at the previous EOL.
+ *
+ * Correct for a left motion component while we're thinking about it.
+ */
+ lno = vp->m_start.lno;
+
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0) {
+ if (vp->m_start.lno == 1) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ } else
+ --vp->m_start.lno;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+
+ if (vp->m_start.lno <= 1)
+ goto sof;
+
+ /* Figure out what state we're currently in. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ goto sof;
+
+ /*
+ * If we start in text, we want to switch states
+ * (2 * N - 1) times, in non-text, (2 * N) times.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cnt *= 2;
+ if (len == 0 || v_isempty(p, len))
+ pstate = P_INBLANK;
+ else {
+ --cnt;
+ pstate = P_INTEXT;
+
+ /*
+ * !!!
+ * If the starting cursor is past the first column,
+ * the current line is checked for a paragraph.
+ */
+ if (vp->m_start.cno > 0)
+ ++lno;
+ }
+
+ for (;;) {
+ if ((p = file_gline(sp, ep, --lno, &len)) == NULL)
+ goto sof;
+ switch (pstate) {
+ case P_INTEXT:
+ INTEXT_CHECK;
+ break;
+ case P_INBLANK:
+ if (len != 0 && !v_isempty(p, len)) {
+ if (!--cnt)
+ goto found;
+ pstate = P_INTEXT;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /* SOF is a movement sink. */
+sof: lno = 1;
+
+found: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+
+ /*
+ * All commands move to the end of the range. (We already
+ * adjusted the start of the range for motion commands).
+ */
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_buildps --
+ * Build the paragraph command search pattern.
+ */
+int
+v_buildps(sp)
+ SCR *sp;
+{
+ VI_PRIVATE *vip;
+ size_t p_len, s_len;
+ char *p, *p_p, *s_p;
+
+ /*
+ * The vi paragraph command searches for either a paragraph or
+ * section option macro.
+ */
+ p_len = (p_p = O_STR(sp, O_PARAGRAPHS)) == NULL ? 0 : strlen(p_p);
+ s_len = (s_p = O_STR(sp, O_SECTIONS)) == NULL ? 0 : strlen(s_p);
+
+ if (p_len == 0 && s_len == 0)
+ return (0);
+
+ MALLOC_RET(sp, p, char *, p_len + s_len + 1);
+
+ vip = VIP(sp);
+ if (vip->ps != NULL)
+ free(vip->ps);
+
+ if (p_p != NULL)
+ memmove(p, p_p, p_len + 1);
+ if (s_p != NULL)
+ memmove(p + p_len, s_p, s_len + 1);
+ vip->ps = p;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_put.c b/usr.bin/vi/vi/v_put.c
new file mode 100644
index 0000000..af7fcc5
--- /dev/null
+++ b/usr.bin/vi/vi/v_put.c
@@ -0,0 +1,168 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_put.c 8.11 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static void inc_buf __P((SCR *, VICMDARG *));
+
+/*
+ * v_Put -- [buffer]P
+ * Insert the contents of the buffer before the cursor.
+ */
+int
+v_Put(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_long cnt;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ /*
+ * !!!
+ * Historic vi did not support a count with the 'p' and 'P'
+ * commands. It's useful, so we do.
+ */
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (put(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 0))
+ return (1);
+ vp->m_start = vp->m_final;
+ }
+ return (0);
+}
+
+/*
+ * v_put -- [buffer]p
+ * Insert the contents of the buffer after the cursor.
+ */
+int
+v_put(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_long cnt;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ inc_buf(sp, vp);
+
+ /*
+ * !!!
+ * Historic vi did not support a count with the 'p' and 'P'
+ * commands. It's useful, so we do.
+ */
+ for (cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (put(sp, ep, NULL,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_final, 1))
+ return (1);
+ vp->m_start = vp->m_final;
+ }
+ return (0);
+}
+
+/*
+ * !!!
+ * Historical whackadoo. The dot command `puts' the numbered buffer
+ * after the last one put. For example, `"4p.' would put buffer #4
+ * and buffer #5. If the user continued to enter '.', the #9 buffer
+ * would be repeatedly output. This was not documented, and is a bit
+ * tricky to reconstruct. Historical versions of vi also dropped the
+ * contents of the default buffer after each put, so after `"4p' the
+ * default buffer would be empty. This makes no sense to me, so we
+ * don't bother. Don't assume sequential order of numeric characters.
+ *
+ * And, if that weren't exciting enough, failed commands don't normally
+ * set the dot command. Well, boys and girls, an exception is that
+ * the buffer increment gets done regardless of the success of the put.
+ */
+static void
+inc_buf(sp, vp)
+ SCR *sp;
+ VICMDARG *vp;
+{
+ CHAR_T v;
+
+ switch (vp->buffer) {
+ case '1':
+ v = '2';
+ break;
+ case '2':
+ v = '3';
+ break;
+ case '3':
+ v = '4';
+ break;
+ case '4':
+ v = '5';
+ break;
+ case '5':
+ v = '6';
+ break;
+ case '6':
+ v = '7';
+ break;
+ case '7':
+ v = '8';
+ break;
+ case '8':
+ v = '9';
+ break;
+ default:
+ return;
+ }
+ VIP(sp)->sdot.buffer = vp->buffer = v;
+}
diff --git a/usr.bin/vi/vi/v_redraw.c b/usr.bin/vi/vi/v_redraw.c
new file mode 100644
index 0000000..f028111
--- /dev/null
+++ b/usr.bin/vi/vi/v_redraw.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_redraw.c 8.6 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_redraw -- ^R
+ * Redraw the screen.
+ */
+int
+v_redraw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ F_SET(sp, S_REFRESH);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_replace.c b/usr.bin/vi/vi/v_replace.c
new file mode 100644
index 0000000..40d0bbc
--- /dev/null
+++ b/usr.bin/vi/vi/v_replace.c
@@ -0,0 +1,194 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_replace.c 8.20 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_replace -- [count]rc
+ *
+ * !!!
+ * The r command in historic vi was almost beautiful in its badness. For
+ * example, "r<erase>" and "r<word erase>" beeped the terminal and deleted
+ * a single character. "Nr<carriage return>", where N was greater than 1,
+ * inserted a single carriage return. This may not be right, but at least
+ * it's not insane.
+ */
+int
+v_replace(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ CH ikey;
+ TEXT *tp;
+ recno_t lno;
+ size_t blen, len;
+ u_long cnt;
+ int rval;
+ char *bp, *p;
+
+ /*
+ * If the line doesn't exist, or it's empty, replacement isn't
+ * allowed. It's not hard to implement, but:
+ *
+ * 1: It's historic practice.
+ * 2: For consistency, this change would require that the more
+ * general case, "Nr", when the user is < N characters from
+ * the end of the line, also work.
+ * 3: Replacing a newline has somewhat odd semantics.
+ */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ goto nochar;
+ }
+ if (len == 0) {
+nochar: msgq(sp, M_BERR, "No characters to replace");
+ return (1);
+ }
+
+ /*
+ * Figure out how many characters to be replace. For no particular
+ * reason (other than that the semantics of replacing the newline
+ * are confusing) only permit the replacement of the characters in
+ * the current line. I suppose we could append replacement characters
+ * to the line, but I see no compelling reason to do so.
+ */
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = vp->m_start.cno + cnt - 1;
+ if (vp->m_stop.cno > len - 1) {
+ v_eol(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /*
+ * Get the character. Literal escapes escape any character,
+ * single escapes return.
+ */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ ikey.ch = VIP(sp)->rlast;
+ ikey.value = KEY_VAL(sp, ikey.ch);
+ } else {
+ sp->showmode = "Replace char";
+ (void)sp->s_refresh(sp, ep);
+
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ switch (ikey.value) {
+ case K_ESCAPE:
+ return (0);
+ case K_VLNEXT:
+ if (term_key(sp, &ikey, 0) != INP_OK)
+ return (1);
+ break;
+ }
+ VIP(sp)->rlast = ikey.ch;
+ }
+
+ /* Copy the line. */
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, p, len);
+ p = bp;
+
+ if (ikey.value == K_CR || ikey.value == K_NL) {
+ /* Set return line. */
+ vp->m_stop.lno = vp->m_start.lno + cnt;
+ vp->m_stop.cno = 0;
+
+ /* The first part of the current line. */
+ if (file_sline(sp, ep, vp->m_start.lno, p, vp->m_start.cno))
+ goto err_ret;
+
+ /*
+ * The rest of the current line. And, of course, now it gets
+ * tricky. Any white space after the replaced character is
+ * stripped, and autoindent is applied. Put the cursor on the
+ * last indent character as did historic vi.
+ */
+ for (p += vp->m_start.cno + cnt, len -= vp->m_start.cno + cnt;
+ len && isblank(*p); --len, ++p);
+
+ if ((tp = text_init(sp, p, len, len)) == NULL)
+ goto err_ret;
+ if (txt_auto(sp, ep, vp->m_start.lno, NULL, 0, tp))
+ goto err_ret;
+ vp->m_stop.cno = tp->ai ? tp->ai - 1 : 0;
+ if (file_aline(sp, ep, 1, vp->m_start.lno, tp->lb, tp->len))
+ goto err_ret;
+ text_free(tp);
+
+ rval = 0;
+
+ /* All of the middle lines. */
+ while (--cnt)
+ if (file_aline(sp, ep, 1, vp->m_start.lno, "", 0)) {
+err_ret: rval = 1;
+ break;
+ }
+ } else {
+ memset(bp + vp->m_start.cno, ikey.ch, cnt);
+ rval = file_sline(sp, ep, vp->m_start.lno, bp, len);
+ }
+ FREE_SPACE(sp, bp, blen);
+
+ vp->m_final = vp->m_stop;
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_right.c b/usr.bin/vi/vi/v_right.c
new file mode 100644
index 0000000..b71f40f
--- /dev/null
+++ b/usr.bin/vi/vi/v_right.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_right.c 8.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_right -- [count]' ', [count]l
+ * Move right by columns.
+ */
+int
+v_right(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /* It's always illegal to move right on empty lines. */
+ if (len == 0) {
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_DEF. Adjust the
+ * end of the range for motion commands.
+ *
+ * !!!
+ * Historically, "[cdsy]l" worked at the end of a line. Also,
+ * EOL is a count sink.
+ */
+ vp->m_stop.cno = vp->m_start.cno +
+ (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (vp->m_start.cno == len - 1 && !ISMOTION(vp)) {
+ v_eol(sp, ep, NULL);
+ return (1);
+ }
+ if (vp->m_stop.cno >= len) {
+ vp->m_stop.cno = len - 1;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ } else if (ISMOTION(vp)) {
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_dollar -- [count]$
+ * Move to the last column.
+ */
+int
+v_dollar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ /*
+ * !!!
+ * A count moves down count - 1 rows, so, "3$" is the same as "2j$".
+ */
+ if ((F_ISSET(vp, VC_C1SET) ? vp->count : 1) != 1) {
+ /*
+ * !!!
+ * Historically, if the $ is a motion, and deleting from
+ * at or before the first non-blank of the line, it's a
+ * line motion, and the line motion flag is set.
+ */
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_stop.cno))
+ return (1);
+ if (ISMOTION(vp) && vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+
+ --vp->count;
+ if (v_down(sp, ep, vp))
+ return (1);
+ }
+
+ if (file_gline(sp, ep, vp->m_stop.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eol(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D
+ * and VC_Y stay at the start. Ignore VC_C and VC_DEF.
+ */
+ vp->m_stop.cno = len ? len - 1 : 0;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_screen.c b/usr.bin/vi/vi/v_screen.c
new file mode 100644
index 0000000..83cccb9
--- /dev/null
+++ b/usr.bin/vi/vi/v_screen.c
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_screen.c 8.14 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_screen -- ^W
+ * Switch screens.
+ */
+int
+v_screen(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Try for the next lower screen, or, go back to the first
+ * screen on the stack.
+ */
+ if (sp->q.cqe_next != (void *)&sp->gp->dq)
+ sp->nextdisp = sp->q.cqe_next;
+ else if (sp->gp->dq.cqh_first == sp) {
+ msgq(sp, M_ERR, "No other screen to switch to");
+ return (1);
+ } else
+ sp->nextdisp = sp->gp->dq.cqh_first;
+
+ /*
+ * Display the old screen's status line so the user can
+ * find the screen they want.
+ */
+ (void)msg_status(sp, ep, vp->m_start.lno, 0);
+
+ /* Save the old screen's cursor information. */
+ sp->frp->lno = sp->lno;
+ sp->frp->cno = sp->cno;
+ F_SET(sp->frp, FR_CURSORSET);
+
+ F_SET(sp, S_SSWITCH);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_scroll.c b/usr.bin/vi/vi/v_scroll.c
new file mode 100644
index 0000000..e188865
--- /dev/null
+++ b/usr.bin/vi/vi/v_scroll.c
@@ -0,0 +1,486 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_scroll.c 8.22 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+static void goto_adjust __P((VICMDARG *));
+
+/*
+ * The historic vi had a problem in that all movements were by physical
+ * lines, not by logical, or screen lines. Arguments can be made that this
+ * is the right thing to do. For example, single line movements, such as
+ * 'j' or 'k', should probably work on physical lines. Commands like "dj",
+ * or "j.", where '.' is a change command, make more sense for physical lines
+ * than they do for logical lines.
+ *
+ * These arguments, however, don't apply to scrolling commands like ^D and
+ * ^F -- if the window is fairly small, using physical lines can result in
+ * a half-page scroll repainting the entire screen, which is not what the
+ * user wanted. Second, if the line is larger than the screen, using physical
+ * lines can make it impossible to display parts of the line -- there aren't
+ * any commands that don't display the beginning of the line in historic vi,
+ * and if both the beginning and end of the line can't be on the screen at
+ * the same time, you lose. This is even worse in the case of the H, L, and
+ * M commands -- for large lines, they may all refer to the same line and
+ * will result in no movement at all.
+ *
+ * Another issue is that page and half-page scrolling commands historically
+ * moved to the first non-blank character in the new line. If the line is
+ * approximately the same size as the screen, this loses because the cursor
+ * before and after a ^D, may refer to the same location on the screen. In
+ * this implementation, scrolling commands set the cursor to the first non-
+ * blank character if the line changes because of the scroll. Otherwise,
+ * the cursor is left alone.
+ *
+ * This implementation does the scrolling (^B, ^D, ^F, ^U, ^Y, ^E), and the
+ * cursor positioning commands (H, L, M) commands using logical lines, not
+ * physical.
+ */
+
+/*
+ * v_lgoto -- [count]G
+ * Go to first non-blank character of the line count, the last line
+ * of the file by default.
+ */
+int
+v_lgoto(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t nlines;
+
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (file_gline(sp, ep, vp->count, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->count;
+ } else {
+ if (file_lline(sp, ep, &nlines))
+ return (1);
+ vp->m_stop.lno = nlines ? nlines : 1;
+ }
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_home -- [count]H
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the top of the screen, 0 by default.
+ */
+int
+v_home(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (sp->s_position(sp, ep, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_TOP))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_middle -- M
+ * Move to the first non-blank character of the logical line
+ * in the middle of the screen.
+ */
+int
+v_middle(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Yielding to none in our quest for compatibility with every
+ * historical blemish of vi, no matter how strange it might be,
+ * we permit the user to enter a count and then ignore it.
+ */
+ if (sp->s_position(sp, ep, &vp->m_stop, 0, P_MIDDLE))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+/*
+ * v_bottom -- [count]L
+ * Move to the first non-blank character of the logical line
+ * count - 1 from the bottom of the screen, 0 by default.
+ */
+int
+v_bottom(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ if (sp->s_position(sp, ep, &vp->m_stop,
+ F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0, P_BOTTOM))
+ return (1);
+ goto_adjust(vp);
+ return (0);
+}
+
+static void
+goto_adjust(vp)
+ VICMDARG *vp;
+{
+ /* Guess that it's the end of the range. */
+ vp->m_final = vp->m_stop;
+
+ /*
+ * Non-motion commands move the cursor to the end of the range, and
+ * then to the NEXT nonblank of the line. Historic vi always moved
+ * to the first nonblank in the line; since the H, M, and L commands
+ * are logical motions in this implementation, we do the next nonblank
+ * so that it looks approximately the same to the user. To make this
+ * happen, the VM_RCM_SETNNB flag is set in the vcmd.c command table.
+ *
+ * If it's a motion, it's more complicated. The best possible solution
+ * is probably to display the first nonblank of the line the cursor
+ * will eventually rest on. This is tricky, particularly given that if
+ * the associated command is a delete, we don't yet know what line that
+ * will be. So, we clear the VM_RCM_SETNNB flag, and set the first
+ * nonblank flag (VM_RCM_SETFNB). Note, if the lines are sufficiently
+ * long, this can cause the cursor to warp out of the screen. It's too
+ * hard to fix.
+ *
+ * XXX
+ * The G command is always first nonblank, so it's okay to reset it.
+ */
+ if (ISMOTION(vp)) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+ } else
+ return;
+
+ /*
+ * If moving backward in the file, VC_D and VC_Y move to the end
+ * of the range, unless the line didn't change, in which case VC_Y
+ * doesn't move. If moving forward in the file, VC_D and VC_Y stay
+ * at the start of the range. Ignore VC_C and VC_DEF.
+ */
+ if (vp->m_stop.lno < vp->m_start.lno ||
+ vp->m_stop.lno == vp->m_start.lno &&
+ vp->m_stop.cno < vp->m_start.cno) {
+ if (F_ISSET(vp, VC_Y) && vp->m_stop.lno == vp->m_start.lno)
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_start;
+}
+
+/*
+ * v_up -- [count]^P, [count]k, [count]-
+ * Move up by lines.
+ */
+int
+v_up(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+
+ lno = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (vp->m_start.lno <= lno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = vp->m_start.lno - lno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_cr -- [count]^M
+ * In a script window, send the line to the shell.
+ * In a regular window, move down by lines.
+ */
+int
+v_cr(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * If it's a script window, exec the line,
+ * otherwise it's the same as v_down().
+ */
+ return (F_ISSET(sp, S_SCRIPT) ?
+ sscr_exec(sp, ep, vp->m_start.lno) : v_down(sp, ep, vp));
+}
+
+/*
+ * v_down -- [count]^J, [count]^N, [count]j, [count]^M, [count]+
+ * Move down by lines.
+ */
+int
+v_down(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+
+ lno = vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count : 1);
+ if (file_gline(sp, ep, lno, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ vp->m_stop.lno = lno;
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpageup -- [count]^U
+ * Page up half screens.
+ */
+int
+v_hpageup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Half screens always succeed unless already at SOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command
+ * ultimately failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ sp->defscroll = vp->count;
+ if (sp->s_scroll(sp, ep, &vp->m_stop, sp->defscroll, CNTRL_U))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_hpagedown -- [count]^D
+ * Page down half screens.
+ */
+int
+v_hpagedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Half screens always succeed unless already at EOF.
+ *
+ * !!!
+ * Half screens set the scroll value, even if the command
+ * ultimately failed, in historic vi. Probably a don't care.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ sp->defscroll = vp->count;
+ if (sp->s_scroll(sp, ep, &vp->m_stop, sp->defscroll, CNTRL_D))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_pagedown -- [count]^F
+ * Page down full screens.
+ * !!!
+ * Historic vi did not move to the EOF if the screen couldn't move, i.e.
+ * if EOF was already displayed on the screen. This implementation does
+ * move to EOF in that case, making ^F more like the the historic ^D.
+ */
+int
+v_pagedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t offset;
+
+ /*
+ * !!!
+ * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
+ *
+ * top_line = top_line + count * (window - 2);
+ *
+ * which was historically wrong. The correct one is:
+ *
+ * top_line = top_line + count * window - 2;
+ *
+ * i.e. the two line "overlap" was only subtracted once. Which
+ * makes no sense, but then again, an overlap makes no sense for
+ * any screen but the "next" one anyway. We do it the historical
+ * was as there's no good reason to change it.
+ *
+ * If the screen has been split, use the smaller of the current
+ * window size and the window option value.
+ *
+ * Given a one-line screen with the cursor on line 1, it would be
+ * possible for this to fail, i.e. "1 + 1 * 1 - 2 = 0". Move at
+ * least one line.
+ */
+#define IS_SPLIT_SCREEN(sp) \
+ ((sp)->q.cqe_prev != (void *)&(sp)->gp->dq || \
+ (sp)->q.cqe_next != (void *)&(sp)->gp->dq)
+
+ offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) *
+ (IS_SPLIT_SCREEN(sp) ?
+ MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW)) - 2;
+ if (offset == 0)
+ offset = 1;
+ if (sp->s_scroll(sp, ep, &vp->m_stop, offset, CNTRL_F))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_pageup -- [count]^B
+ * Page up full screens.
+ *
+ * !!!
+ * Historic vi did not move to the SOF if the screen couldn't move, i.e.
+ * if SOF was already displayed on the screen. This implementation does
+ * move to SOF in that case, making ^B more like the the historic ^U.
+ */
+int
+v_pageup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t offset;
+
+ /*
+ * !!!
+ * The calculation in IEEE Std 1003.2-1992 (POSIX) is:
+ *
+ * top_line = top_line - count * (window - 2);
+ *
+ * which was historically wrong. The correct one is:
+ *
+ * top_line = (top_line - count * window) + 2;
+ *
+ * A simpler expression is that, as with ^F, we scroll exactly:
+ *
+ * count * window - 2
+ *
+ * lines.
+ *
+ * Bizarre. As with ^F, an overlap makes no sense for anything
+ * but the first screen. We do it the historical way as there's
+ * no good reason to change it.
+ *
+ * If the screen has been split, use the smaller of the current
+ * window size and the window option value.
+ *
+ * Given a one-line screen with the cursor on line 1, it would be
+ * possible for this to fail, i.e. "1 + 1 * 1 - 2 = 0". Move at
+ * least one line.
+ */
+ offset = (F_ISSET(vp, VC_C1SET) ? vp->count : 1) *
+ (IS_SPLIT_SCREEN(sp) ?
+ MIN(sp->t_maxrows, O_VAL(sp, O_WINDOW)) : O_VAL(sp, O_WINDOW)) - 2;
+ if (offset == 0)
+ offset = 1;
+ if (sp->s_scroll(sp, ep, &vp->m_stop, offset, CNTRL_B))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_lineup -- [count]^Y
+ * Page up by lines.
+ */
+int
+v_lineup(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * The cursor moves down, staying with its original line, unless it
+ * reaches the bottom of the screen.
+ */
+ if (sp->s_scroll(sp, ep,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_Y))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_linedown -- [count]^E
+ * Page down by lines.
+ */
+int
+v_linedown(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * The cursor moves up, staying with its original line, unless it
+ * reaches the top of the screen.
+ */
+ if (sp->s_scroll(sp, ep,
+ &vp->m_stop, F_ISSET(vp, VC_C1SET) ? vp->count : 1, CNTRL_E))
+ return (1);
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_search.c b/usr.bin/vi/vi/v_search.c
new file mode 100644
index 0000000..ad0b0ca
--- /dev/null
+++ b/usr.bin/vi/vi/v_search.c
@@ -0,0 +1,414 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_search.c 8.34 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int correct __P((SCR *, EXF *, VICMDARG *, u_int));
+static int getptrn __P((SCR *, EXF *, ARG_CHAR_T, char **, size_t *));
+static int search __P((SCR *,
+ EXF *, VICMDARG *, char *, size_t, u_int, enum direction));
+
+/*
+ * v_searchn -- n
+ * Repeat last search.
+ */
+int
+v_searchn(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (search(sp, ep, vp, NULL, 0, SEARCH_MSG, sp->searchdir));
+}
+
+/*
+ * v_searchN -- N
+ * Reverse last search.
+ */
+int
+v_searchN(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum direction dir;
+
+ switch (sp->searchdir) {
+ case BACKWARD:
+ dir = FORWARD;
+ break;
+ case FORWARD:
+ dir = BACKWARD;
+ break;
+ default: /* NOTSET handled in search(). */
+ dir = sp->searchdir;
+ break;
+ }
+ return (search(sp, ep, vp, NULL, 0, SEARCH_MSG, dir));
+}
+
+/*
+ * v_searchb -- [count]?RE[? offset]
+ * Search backward.
+ */
+int
+v_searchb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t len;
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, CH_BSEARCH, &ptrn, &len))
+ return (1);
+ if (len == 0) {
+ F_SET(vp, VM_NOMOTION);
+ return (0);
+ }
+ }
+ return (search(sp, ep, vp, ptrn, len,
+ SEARCH_MSG | SEARCH_PARSE | SEARCH_SET, BACKWARD));
+}
+
+/*
+ * v_searchf -- [count]/RE[/ offset]
+ * Search forward.
+ */
+int
+v_searchf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t len;
+ char *ptrn;
+
+ if (F_ISSET(vp, VC_ISDOT))
+ ptrn = NULL;
+ else {
+ if (getptrn(sp, ep, CH_FSEARCH, &ptrn, &len))
+ return (1);
+ if (len == 0) {
+ F_SET(vp, VM_NOMOTION);
+ return (0);
+ }
+ }
+ return (search(sp, ep, vp, ptrn, len,
+ SEARCH_MSG | SEARCH_PARSE | SEARCH_SET, FORWARD));
+}
+
+/*
+ * v_searchw -- [count]^A
+ * Search for the word under the cursor.
+ */
+int
+v_searchw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t blen, len;
+ int rval;
+ char *bp;
+
+ len = vp->kbuflen + sizeof(RE_WSTART) + sizeof(RE_WSTOP);
+ GET_SPACE_RET(sp, bp, blen, len);
+ (void)snprintf(bp, blen, "%s%s%s", RE_WSTART, vp->keyword, RE_WSTOP);
+
+ rval = search(sp, ep, vp, bp, 0, SEARCH_MSG | SEARCH_TERM, FORWARD);
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+static int
+search(sp, ep, vp, ptrn, len, flags, dir)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int flags;
+ char *ptrn;
+ size_t len;
+ enum direction dir;
+{
+ char *eptrn;
+
+ if (ISMOTION(vp))
+ flags |= SEARCH_EOL;
+
+ for (;;) {
+ switch (dir) {
+ case BACKWARD:
+ if (b_search(sp, ep,
+ &vp->m_start, &vp->m_stop, ptrn, &eptrn, &flags))
+ return (1);
+ break;
+ case FORWARD:
+ if (f_search(sp, ep,
+ &vp->m_start, &vp->m_stop, ptrn, &eptrn, &flags))
+ return (1);
+ break;
+ case NOTSET:
+ msgq(sp, M_ERR, "No previous search pattern");
+ return (1);
+ default:
+ abort();
+ }
+
+ /*
+ * !!!
+ * Historically, vi permitted trailing <blank>'s, multiple
+ * search strings (separated by semi-colons) and full-blown
+ * z commands after / and ? search strings. In the case of
+ * multiple search strings, leading <blank>'s on the second
+ * and subsequent strings was eaten as well.
+ *
+ * !!!
+ * However, the command "/STRING/; " failed, apparently it
+ * confused the parser. We're not *that* compatible.
+ *
+ * The N, n, and ^A commands also get to here, but they've
+ * set ptrn to NULL, len to 0, or the SEARCH_TERM flag, or
+ * some combination thereof.
+ */
+ if (ptrn == NULL || len == 0)
+ break;
+ len -= eptrn - ptrn;
+ for (; len > 0 && isblank(*eptrn); ++eptrn, --len);
+ if (len == 0)
+ break;
+
+ switch (*eptrn) {
+ case ';':
+ for (++eptrn; --len > 0 && isblank(*eptrn); ++eptrn);
+ ptrn = eptrn;
+ switch (*eptrn) {
+ case '/':
+ dir = FORWARD;
+ break;
+ case '?':
+ dir = BACKWARD;
+ break;
+ default:
+ goto usage;
+ }
+ ptrn = eptrn;
+ vp->m_start = vp->m_stop;
+ continue;
+ case 'z':
+ if (term_push(sp, eptrn, len, CH_NOMAP | CH_QUOTED))
+ return (1);
+ goto ret;
+ default:
+usage: msgq(sp, M_ERR,
+ "Characters after search string and/or delta");
+ return (1);
+ }
+ }
+
+ /* Non-motion commands move to the end of the range. */
+ret: if (ISMOTION(vp)) {
+ if (correct(sp, ep, vp, flags))
+ return (1);
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * getptrn --
+ * Get the search pattern.
+ */
+static int
+getptrn(sp, ep, prompt, ptrnp, lenp)
+ SCR *sp;
+ EXF *ep;
+ ARG_CHAR_T prompt;
+ char **ptrnp;
+ size_t *lenp;
+{
+ TEXT *tp;
+
+ if (sp->s_get(sp, ep, sp->tiqp, prompt,
+ TXT_BS | TXT_CR | TXT_ESCAPE | TXT_PROMPT) != INP_OK)
+ return (1);
+
+ /* Len is 0 if backspaced over the prompt, 1 if only CR entered. */
+ tp = sp->tiqp->cqh_first;
+ *ptrnp = tp->lb;
+ *lenp = tp->len;
+ return (0);
+}
+
+/*
+ * correct --
+ * Handle command with a search as the motion.
+ *
+ * !!!
+ * Historically, commands didn't affect the line searched to/from if the
+ * motion command was a search and the final position was the start/end
+ * of the line. There were some special cases and vi was not consistent;
+ * it was fairly easy to confuse it. For example, given the two lines:
+ *
+ * abcdefghi
+ * ABCDEFGHI
+ *
+ * placing the cursor on the 'A' and doing y?$ would so confuse it that 'h'
+ * 'k' and put would no longer work correctly. In any case, we try to do
+ * the right thing, but it's not going to exactly match historic practice.
+ */
+static int
+correct(sp, ep, vp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int flags;
+{
+ enum direction dir;
+ MARK m;
+ size_t len;
+
+ /*
+ * !!!
+ * We may have wrapped if wrapscan was set, and we may have returned
+ * to the position where the cursor started. Historic vi didn't cope
+ * with this well. Yank wouldn't beep, but the first put after the
+ * yank would move the cursor right one column (without adding any
+ * text) and the second would put a copy of the current line. The
+ * change and delete commands would beep, but would leave the cursor
+ * on the colon command line. I believe that there are macros that
+ * depend on delete, at least, failing. For now, commands that use
+ * search as a motion component fail when the search returns to the
+ * original cursor position.
+ */
+ if (vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno == vp->m_stop.cno) {
+ msgq(sp, M_BERR, "Search wrapped to original position");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Searches become line mode operations if there was a delta
+ * specified to the search pattern.
+ */
+ if (LF_ISSET(SEARCH_DELTA))
+ F_SET(vp, VM_LMODE);
+
+ /*
+ * If the motion is in the reverse direction, switch the start and
+ * stop MARK's so that it's in a forward direction. (There's no
+ * reason for this other than to make the tests below easier. The
+ * code in vi.c:vi() would have done the switch.) Both forward
+ * and backward motions can happen for any kind of search command
+ * because of the wrapscan option.
+ */
+ if (vp->m_start.lno > vp->m_stop.lno ||
+ vp->m_start.lno == vp->m_stop.lno &&
+ vp->m_start.cno > vp->m_stop.cno) {
+ dir = BACKWARD;
+ m = vp->m_start;
+ vp->m_start = vp->m_stop;
+ vp->m_stop = m;
+ } else
+ dir = FORWARD;
+
+ /*
+ * BACKWARD:
+ * VC_D commands move to the end of the range. VC_Y stays at
+ * the start unless the end of the range is on a different line,
+ * when it moves to the end of the range. Ignore VC_C and
+ * VC_DEF.
+ *
+ * FORWARD:
+ * VC_D and VC_Y commands don't move. Ignore VC_C and VC_DEF.
+ */
+ if (dir == BACKWARD)
+ if (F_ISSET(vp, VC_D) ||
+ F_ISSET(vp, VC_Y) && vp->m_start.lno != vp->m_stop.lno)
+ vp->m_final = vp->m_start;
+ else
+ vp->m_final = vp->m_stop;
+ else
+ vp->m_final = vp->m_start;
+
+ /*
+ * !!!
+ * Backward searches starting at column 0, and forward searches ending
+ * at column 0 are corrected to the last column of the previous line.
+ * Otherwise, adjust the starting/ending point to the character before
+ * the current one (this is safe because we know the search had to move
+ * to succeed).
+ *
+ * Searches become line mode operations if they start at column 0 and
+ * end at column 0 of another line.
+ */
+ if (vp->m_start.lno < vp->m_stop.lno && vp->m_stop.cno == 0) {
+ if (file_gline(sp, ep, --vp->m_stop.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else
+ --vp->m_stop.cno;
+
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_section.c b/usr.bin/vi/vi/v_section.c
new file mode 100644
index 0000000..a22773a
--- /dev/null
+++ b/usr.bin/vi/vi/v_section.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_section.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * In historic vi, the section commands ignored empty lines, unlike the
+ * paragraph commands, which was probably okay. However, they also moved
+ * to the start of the last line when there where no more sections instead
+ * of the end of the last line like the paragraph commands. I've changed
+ * the latter behavior to match the paragraph commands.
+ *
+ * In historic vi, a section was defined as the first character(s) of the
+ * line matching, which could be followed by anything. This implementation
+ * follows that historic practice.
+ *
+ * !!!
+ * The historic vi documentation (USD:15-10) claimed:
+ * The section commands interpret a preceding count as a different
+ * window size in which to redraw the screen at the new location,
+ * and this window size is the base size for newly drawn windows
+ * until another size is specified. This is very useful if you are
+ * on a slow terminal ...
+ *
+ * I can't get the 4BSD vi to do this, it just beeps at me. For now, a
+ * count to the section commands simply repeats the command.
+ */
+
+/*
+ * v_sectionf -- [count]]]
+ * Move forward count sections/functions.
+ *
+ * !!!
+ * Using ]] as a motion command was a bit special, historically. It could
+ * match } as well as the usual { and section values. If it matched a { or
+ * a section, it did NOT include the matched line. If it matched a }, it
+ * did include the line. No clue why.
+ */
+int
+v_sectionf(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t cnt, lno;
+ size_t len;
+ char *p, *list, *lp;
+
+ /* Get the macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ /*
+ * !!!
+ * If the starting cursor position is at or before any non-blank
+ * characters in the line, i.e. the movement is cutting all of the
+ * line's text, the buffer is in line mode. It's a lot easier to
+ * check here, because we know that the end is going to be the start
+ * or end of a line.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0)
+ F_SET(vp, VM_LMODE);
+ else {
+ vp->m_stop = vp->m_start;
+ vp->m_stop.cno = 0;
+ if (nonblank(sp, ep, vp->m_stop.lno, &vp->m_stop.cno))
+ return (1);
+ if (vp->m_start.cno <= vp->m_stop.cno)
+ F_SET(vp, VM_LMODE);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = vp->m_start.lno;
+ (p = file_gline(sp, ep, ++lno, &len)) != NULL;) {
+ if (len == 0)
+ continue;
+ if (p[0] == '{' || ISMOTION(vp) && p[0] == '}') {
+ if (!--cnt) {
+ if (p[0] == '{')
+ goto adjust1;
+ goto adjust2;
+ }
+ continue;
+ }
+ /*
+ * !!!
+ * Historic documentation (USD:15-11, 4.2) said that formfeed
+ * characters (^L) in the first column delimited sections.
+ * The historic code mentions formfeed characters, but never
+ * implements them. Seems reasonable, do it.
+ */
+ if (p[0] == '\014') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ if (p[0] != '.' || len < 2)
+ continue;
+ for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
+ !--cnt) {
+ /*
+ * !!!
+ * If not cutting this line, adjust to the end
+ * of the previous one. Otherwise, position to
+ * column 0.
+ */
+adjust1: if (ISMOTION(vp))
+ goto ret1;
+
+adjust2: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ goto ret2;
+ }
+ }
+
+ /* If moving forward, reached EOF, check to see if we started there. */
+ if (vp->m_start.lno == lno - 1) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+
+ret1: if (file_gline(sp, ep, --lno, &len) == NULL)
+ return (1);
+ vp->m_stop.lno = lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+
+ /*
+ * Non-motion commands go to the end of the range. VC_D and
+ * VC_Y stay at the start of the range. Ignore VC_C and VC_DEF.
+ */
+ret2: if (ISMOTION(vp)) {
+ vp->m_final = vp->m_start;
+ if (F_ISSET(vp, VM_LMODE))
+ vp->m_final.cno = 0;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_sectionb -- [count][[
+ * Move backward count sections/functions.
+ */
+int
+v_sectionb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ size_t len;
+ recno_t cnt, lno;
+ char *p, *list, *lp;
+
+ /* An empty file or starting from line 1 is always illegal. */
+ if (vp->m_start.lno <= 1) {
+ v_sof(sp, NULL);
+ return (1);
+ }
+
+ /* Get the macro list. */
+ if ((list = O_STR(sp, O_SECTIONS)) == NULL)
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ for (lno = vp->m_start.lno;
+ (p = file_gline(sp, ep, --lno, &len)) != NULL;) {
+ if (len == 0)
+ continue;
+ if (p[0] == '{') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ /*
+ * !!!
+ * Historic documentation (USD:15-11, 4.2) said that formfeed
+ * characters (^L) in the first column delimited sections.
+ * The historic code mentions formfeed characters, but never
+ * implements them. Seems reasonable, do it.
+ */
+ if (p[0] == '\014') {
+ if (!--cnt)
+ goto adjust1;
+ continue;
+ }
+ if (p[0] != '.' || len < 2)
+ continue;
+ for (lp = list; *lp != '\0'; lp += 2 * sizeof(*lp))
+ if (lp[0] == p[1] &&
+ (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&
+ !--cnt) {
+adjust1: vp->m_stop.lno = lno;
+ vp->m_stop.cno = 0;
+ goto ret1;
+ }
+ }
+
+ /*
+ * If moving backward, reached SOF, which is a movement sink.
+ * We already checked for starting there.
+ */
+ vp->m_stop.lno = 1;
+ vp->m_stop.cno = 0;
+
+ /*
+ * All commands move to the end of the range.
+ *
+ * !!!
+ * Historic practice is the section cut was in line mode if it started
+ * from column 0 and was in the backward direction. Otherwise, left
+ * motion commands adjust the starting point to the character before
+ * the current one. What makes this worse is that if it cut to line
+ * mode it also went to the first non-<blank>.
+ */
+ret1: if (vp->m_start.cno == 0) {
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SETFNB);
+
+ --vp->m_start.lno;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_sentence.c b/usr.bin/vi/vi/v_sentence.c
new file mode 100644
index 0000000..0b7543a
--- /dev/null
+++ b/usr.bin/vi/vi/v_sentence.c
@@ -0,0 +1,386 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_sentence.c 8.17 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * In historic vi, a sentence was delimited by a '.', '?' or '!' character
+ * followed by TWO spaces or a newline. One or more empty lines was also
+ * treated as a separate sentence. The Berkeley documentation for historical
+ * vi states that any number of ')', ']', '"' and '\'' characters can be
+ * between the delimiter character and the spaces or end of line, however,
+ * the historical implementation did not handle additional '"' characters.
+ * We follow the documentation here, not the implementation.
+ *
+ * Once again, historical vi didn't do sentence movements associated with
+ * counts consistently, mostly in the presence of lines containing only
+ * white-space characters.
+ *
+ * This implementation also permits a single tab to delimit sentences, and
+ * treats lines containing only white-space characters as empty lines.
+ * Finally, tabs are eaten (along with spaces) when skipping to the start
+ * of the text following a "sentence".
+ */
+
+/*
+ * v_sentencef -- [count])
+ * Move forward count sentences.
+ */
+int
+v_sentencef(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ enum { BLANK, NONE, PERIOD } state;
+ VCS cs;
+ size_t len;
+ u_long cnt;
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * !!!
+ * If in white-space, the next start of sentence counts as one.
+ * This may not handle " . " correctly, but it's real unclear
+ * what correctly means in that case.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0) {
+ if (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.cno != cs.cs_cno)
+ goto okret;
+ return (1);
+ }
+ }
+
+ for (state = NONE;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ if ((state == PERIOD || state == BLANK) && --cnt == 0) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 &&
+ isblank(cs.cs_ch) && cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ state = NONE;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) { /* An EMP is two sentences. */
+ if (--cnt == 0)
+ goto okret;
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (--cnt == 0)
+ goto okret;
+ state = NONE;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ state = PERIOD;
+ break;
+ case ')':
+ case ']':
+ case '"':
+ case '\'':
+ if (state != PERIOD)
+ state = NONE;
+ break;
+ case '\t':
+ if (state == PERIOD)
+ state = BLANK;
+ /* FALLTHROUGH */
+ case ' ':
+ if (state == PERIOD) {
+ state = BLANK;
+ break;
+ }
+ if (state == BLANK && --cnt == 0) {
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ goto okret;
+ }
+ /* FALLTHROUGH */
+ default:
+ state = NONE;
+ break;
+ }
+ }
+
+ /* EOF is a movement sink, but it's an error not to have moved. */
+ if (vp->m_start.lno == cs.cs_lno && vp->m_start.cno == cs.cs_cno) {
+ v_eof(sp, ep, NULL);
+ return (1);
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * Historic, uh, features, yeah, that's right, call 'em features.
+ * If the ending cursor position is at the first column in the
+ * line, i.e. the movement is cutting an entire line, the buffer
+ * is in line mode, and the ending position is the last character
+ * of the previous line.
+ *
+ * Non-motion commands move to the end of the range. VC_D and
+ * VC_Y stay at the start. Ignore VC_C and VC_DEF. Adjust the
+ * end of the range for motion commands.
+ */
+ if (ISMOTION(vp)) {
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (file_gline(sp, ep,
+ --vp->m_stop.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_stop.lno);
+ return (1);
+ }
+ vp->m_stop.cno = len ? len - 1 : 0;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_stop.cno;
+ vp->m_final = vp->m_start;
+ } else
+ vp->m_final = vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_sentenceb -- [count](
+ * Move backward count sentences.
+ */
+int
+v_sentenceb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ VCS cs;
+ recno_t slno;
+ size_t len, scno;
+ u_long cnt;
+ int last;
+
+ /*
+ * !!!
+ * Historic vi permitted the user to hit SOF repeatedly.
+ */
+ if (vp->m_start.lno == 1 && vp->m_start.cno == 0)
+ return (0);
+
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+
+ /*
+ * !!!
+ * In empty lines, skip to the previous non-white-space character.
+ * If in text, skip to the prevous white-space character. Believe
+ * it or not, in the paragraph:
+ * ab cd.
+ * AB CD.
+ * if the cursor is on the 'A' or 'B', ( moves to the 'a'. If it
+ * is on the ' ', 'C' or 'D', it moves to the 'A'. Yes, Virginia,
+ * Berkeley was once a major center of drug activity.
+ */
+ if (cs.cs_flags == CS_EMP) {
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != CS_EOL)
+ break;
+ }
+ } else if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+
+ for (last = 0;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF) /* SOF is a movement sink. */
+ break;
+ if (cs.cs_flags == CS_EOL) {
+ last = 1;
+ continue;
+ }
+ if (cs.cs_flags == CS_EMP) {
+ if (--cnt == 0)
+ goto ret;
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ last = 0;
+ continue;
+ }
+ switch (cs.cs_ch) {
+ case '.':
+ case '?':
+ case '!':
+ if (!last || --cnt != 0) {
+ last = 0;
+ continue;
+ }
+
+ret: slno = cs.cs_lno;
+ scno = cs.cs_cno;
+
+ /*
+ * Move to the start of the sentence, skipping blanks
+ * and special characters.
+ */
+ do {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ } while (!cs.cs_flags &&
+ (cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\''));
+ if ((cs.cs_flags || isblank(cs.cs_ch)) &&
+ cs_fblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * If it was ". xyz", with the cursor on the 'x', or
+ * "end. ", with the cursor in the spaces, or the
+ * beginning of a sentence preceded by an empty line,
+ * we can end up where we started. Fix it.
+ */
+ if (vp->m_start.lno != cs.cs_lno ||
+ vp->m_start.cno != cs.cs_cno)
+ goto okret;
+
+ /*
+ * Well, if an empty line preceded possible blanks
+ * and the sentence, it could be a real sentence.
+ */
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOL)
+ continue;
+ if (cs.cs_flags == 0 && isblank(cs.cs_ch))
+ continue;
+ break;
+ }
+ if (cs.cs_flags == CS_EMP)
+ goto okret;
+
+ /* But it wasn't; try again. */
+ ++cnt;
+ cs.cs_lno = slno;
+ cs.cs_cno = scno;
+ last = 0;
+ break;
+ case '\t':
+ last = 1;
+ break;
+ default:
+ last =
+ cs.cs_flags == CS_EOL || isblank(cs.cs_ch) ||
+ cs.cs_ch == ')' || cs.cs_ch == ']' ||
+ cs.cs_ch == '"' || cs.cs_ch == '\'' ? 1 : 0;
+ }
+ }
+
+okret: vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * !!!
+ * If the starting and stopping cursor positions are at the first
+ * columns in the line, i.e. the movement is cutting an entire line,
+ * the buffer is in line mode, and the starting position is the last
+ * character of the previous line.
+ *
+ * All commands move to the end of the range. Adjust the start of
+ * the range for motion commands.
+ */
+ if (ISMOTION(vp))
+ if (vp->m_start.cno == 0 &&
+ (cs.cs_flags != 0 || vp->m_stop.cno == 0)) {
+ if (file_gline(sp, ep,
+ --vp->m_start.lno, &len) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ vp->m_start.cno = len ? len - 1 : 0;
+ F_SET(vp, VM_LMODE);
+ } else
+ --vp->m_start.cno;
+ vp->m_final = vp->m_stop;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_status.c b/usr.bin/vi/vi/v_status.c
new file mode 100644
index 0000000..16e6c2b
--- /dev/null
+++ b/usr.bin/vi/vi/v_status.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_status.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_status -- ^G
+ * Show the file status.
+ */
+int
+v_status(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+
+ /*
+ * ^G in historic vi reset the cursor column to the first
+ * non-blank character in the line. This doesn't seem of
+ * any usefulness whatsoever, so I don't bother.
+ */
+ return (msg_status(sp, ep, vp->m_start.lno, 1));
+}
diff --git a/usr.bin/vi/vi/v_stop.c b/usr.bin/vi/vi/v_stop.c
new file mode 100644
index 0000000..6a4cd72
--- /dev/null
+++ b/usr.bin/vi/vi/v_stop.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_stop.c 8.9 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_stop -- ^Z
+ * Suspend vi.
+ */
+int
+v_stop(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* If autowrite is set, write out the file. */
+ if (F_ISSET(ep, F_MODIFIED) && O_ISSET(sp, O_AUTOWRITE)) {
+ if (file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+ if (sp->s_refresh(sp, ep))
+ return (1);
+ }
+ return (sp->s_suspend(sp));
+}
diff --git a/usr.bin/vi/vi/v_text.c b/usr.bin/vi/vi/v_text.c
new file mode 100644
index 0000000..5ad042c
--- /dev/null
+++ b/usr.bin/vi/vi/v_text.c
@@ -0,0 +1,886 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_text.c 8.43 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * !!!
+ * Repeated input in the historic vi is mostly wrong and this isn't very
+ * backward compatible. For example, if the user entered "3Aab\ncd" in
+ * the historic vi, the "ab" was repeated 3 times, and the "\ncd" was then
+ * appended to the result. There was also a hack which I don't remember
+ * right now, where "3o" would open 3 lines and then let the user fill them
+ * in, to make screen movements on 300 baud modems more tolerable. I don't
+ * think it's going to be missed.
+ *
+ * !!!
+ * There's a problem with the way that we do logging for change commands with
+ * implied motions (e.g. A, I, O, cc, etc.). Since the main vi loop logs the
+ * starting cursor position before the change command "moves" the cursor, the
+ * cursor position to which we return on undo will be where the user entered
+ * the change command, not the start of the change. Several of the following
+ * routines re-log the cursor to make this work correctly. Historic vi tried
+ * to do the same thing, and mostly got it right. (The only spectacular way
+ * it fails is if the user entered 'o' from anywhere but the last character of
+ * the line, the undo returned the cursor to the start of the line. If the
+ * user was on the last character of the line, the cursor returned to that
+ * position.) We also check for mapped keys waiting, i.e. if we're in the
+ * middle of a map, don't bother logging the cursor.
+ */
+#define LOG_CORRECT { \
+ if (!MAPPED_KEYS_WAITING(sp)) \
+ (void)log_cursor(sp, ep); \
+}
+#define LOG_CORRECT_FIRST { \
+ if (first == 1) { \
+ LOG_CORRECT; \
+ first = 0; \
+ } \
+}
+
+static u_int set_txt_std __P((SCR *, VICMDARG *, u_int));
+static int v_CS __P((SCR *, EXF *, VICMDARG *, u_int));
+
+/*
+ * v_iA -- [count]A
+ * Append text to the end of the line.
+ */
+int
+v_iA(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ sp->showmode = "Append";
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL);
+ for (first = 1, lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /* Move the cursor to the end of the line + 1. */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ /* Correct logging for implied cursor motion. */
+ if (first == 1) {
+ sp->cno = len == 0 ? 0 : len - 1;
+ LOG_CORRECT;
+ first = 0;
+ }
+
+ /* Start the change after the line. */
+ sp->cno = len;
+ }
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ia -- [count]a
+ * Append text to the cursor position.
+ */
+int
+v_ia(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ u_int flags;
+ size_t len;
+ char *p;
+
+ sp->showmode = "Append";
+ flags = set_txt_std(sp, vp, 0);
+ for (lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor one column to the right and
+ * repaint the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else if (len) {
+ if (len == sp->cno + 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ ++sp->cno;
+ } else
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ flags = set_txt_std(sp, vp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iI -- [count]I
+ * Insert text at the first non-blank character in the line.
+ */
+int
+v_iI(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ int first;
+ char *p;
+
+ sp->showmode = "Insert";
+ flags = set_txt_std(sp, vp, 0);
+ for (first = 1, lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ /*
+ * Move the cursor to the start of the line and repaint
+ * the screen.
+ */
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ } else {
+ sp->cno = 0;
+ if (nonblank(sp, ep, lno, &sp->cno))
+ return (1);
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+ }
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ flags = set_txt_std(sp, vp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_ii -- [count]i
+ * Insert text at the cursor position.
+ */
+int
+v_ii(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ sp->showmode = "Insert";
+ flags = set_txt_std(sp, vp, 0);
+ for (lno = vp->m_start.lno,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ lno = 1;
+ len = 0;
+ }
+ /* If len == sp->cno, it's a replay caused by a count. */
+ if (len == 0 || len == sp->cno)
+ LF_SET(TXT_APPENDEOL);
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * On replay, if the line isn't empty, advance the insert
+ * by one (make it an append).
+ */
+ flags = set_txt_std(sp, vp, TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ if ((sp->cno = vp->m_final.cno) != 0)
+ ++sp->cno;
+ }
+ return (0);
+}
+
+/*
+ * v_iO -- [count]O
+ * Insert text above this line.
+ */
+int
+v_iO(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ sp->showmode = "Insert";
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL);
+ for (first = 1, cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+
+ if (file_iline(sp, ep, sp->lno, p, 0))
+ return (1);
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno + 1;
+ }
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, ai_line, flags))
+ return (1);
+
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_io -- [count]o
+ * Insert text after this line.
+ */
+int
+v_io(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t ai_line, lno;
+ size_t len;
+ u_long cnt;
+ u_int flags;
+ int first;
+ char *p;
+
+ sp->showmode = "Insert";
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL);
+ for (first = 1,
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt--;) {
+ if (sp->lno == 1) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0)
+ goto insert;
+ p = NULL;
+ len = 0;
+ ai_line = OOBLNO;
+ } else {
+insert: p = "";
+ sp->cno = 0;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT_FIRST;
+
+ len = 0;
+ if (file_aline(sp, ep, 1, sp->lno, p, len))
+ return (1);
+ if ((p = file_gline(sp, ep, ++sp->lno, &len)) == NULL) {
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+ ai_line = sp->lno - 1;
+ }
+
+ if (v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, ai_line, flags))
+ return (1);
+
+ flags = set_txt_std(sp, vp, TXT_APPENDEOL | TXT_REPLAY);
+ sp->lno = lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+ }
+ return (0);
+}
+
+/*
+ * v_Change -- [buffer][count]C
+ * Change line command.
+ */
+int
+v_Change(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (v_CS(sp, ep, vp, 0));
+}
+
+/*
+ * v_Subst -- [buffer][count]S
+ * Line substitute command.
+ */
+int
+v_Subst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_int flags;
+
+ /*
+ * The S command is the same as a 'C' command from the beginning
+ * of the line. This is hard to do in the parser, so do it here.
+ *
+ * If autoindent is on, the change is from the first *non-blank*
+ * character of the line, not the first character. And, to make
+ * it just a bit more exciting, the initial space is handled as
+ * auto-indent characters.
+ */
+ LF_INIT(0);
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ vp->m_start.cno = 0;
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_start.cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ } else
+ vp->m_start.cno = 0;
+ sp->cno = vp->m_start.cno;
+ return (v_CS(sp, ep, vp, flags));
+}
+
+/*
+ * v_CS --
+ * C and S commands.
+ */
+static int
+v_CS(sp, ep, vp, iflags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ u_int iflags;
+{
+ MARK *tm;
+ recno_t lno;
+ size_t len;
+ char *p;
+ u_int flags;
+
+ sp->showmode = "Change";
+ flags = set_txt_std(sp, vp, iflags);
+
+ /*
+ * There are two cases -- if a count is supplied, we do a line
+ * mode change where we delete the lines and then insert text
+ * into a new line. Otherwise, we replace the current line.
+ */
+ vp->m_stop.lno =
+ vp->m_start.lno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (vp->m_start.lno != vp->m_stop.lno) {
+ /* Make sure that the to line is real. */
+ if (file_gline(sp, ep,
+ vp->m_stop.lno, &vp->m_stop.cno) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ if (vp->m_stop.cno != 0)
+ --vp->m_stop.cno;
+
+ /*
+ * Cut the lines.
+ *
+ * !!!
+ * Historic practice, C and S did not cut into the numeric
+ * buffers, only the unnamed one.
+ */
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_LINEMODE))
+ return (1);
+
+ /* Insert a line while we still can... */
+ if (file_iline(sp, ep, vp->m_start.lno, "", 0))
+ return (1);
+ ++vp->m_start.lno;
+ ++vp->m_stop.lno;
+
+ /* Delete the lines. */
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, 1))
+ return (1);
+
+ /* Get the inserted line. */
+ if ((p = file_gline(sp, ep, --vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ tm = NULL;
+ sp->lno = vp->m_start.lno;
+ sp->cno = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ /* The line may be empty, but that's okay. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ vp->m_stop.cno = len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0) {
+ vp->m_stop.cno = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else
+ vp->m_stop.cno = len - 1;
+ /*
+ * !!!
+ * Historic practice, C and S did not cut into the
+ * numeric buffers, only the unnamed one.
+ */
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, CUT_LINEMODE))
+ return (1);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ tm = &vp->m_stop;
+ }
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT;
+
+ return (v_ntext(sp, ep,
+ sp->tiqp, tm, p, len, &vp->m_final, 0, OOBLNO, flags));
+}
+
+/*
+ * v_change -- [buffer][count]c[count]motion
+ * Change command.
+ */
+int
+v_change(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t blen, len;
+ u_int flags;
+ int lmode, rval;
+ char *bp, *p;
+
+ sp->showmode = "Change";
+ flags = set_txt_std(sp, vp, 0);
+
+ /*
+ * Move the cursor to the start of the change. Note, if autoindent
+ * is turned on, the cc command in line mode changes from the first
+ * *non-blank* character of the line, not the first character. And,
+ * to make it just a bit more exciting, the initial space is handled
+ * as auto-indent characters.
+ */
+ lmode = F_ISSET(vp, VM_LMODE) ? CUT_LINEMODE : 0;
+ if (lmode) {
+ vp->m_start.cno = 0;
+ if (O_ISSET(sp, O_AUTOINDENT)) {
+ if (nonblank(sp, ep, vp->m_start.lno, &vp->m_start.cno))
+ return (1);
+ LF_SET(TXT_AICHARS);
+ }
+ }
+ sp->lno = vp->m_start.lno;
+ sp->cno = vp->m_start.cno;
+
+ /* Correct logging for implied cursor motion. */
+ LOG_CORRECT;
+
+ /*
+ * 'c' can be combined with motion commands that set the resulting
+ * cursor position, i.e. "cG". Clear the VM_RCM flags and make the
+ * resulting cursor position stick, inserting text has its own rules
+ * for cursor positioning.
+ */
+ F_CLR(vp, VM_RCM_MASK);
+ F_SET(vp, VM_RCM_SET);
+
+ /*
+ * If not in line mode and changing within a single line, the line
+ * either currently has text or it doesn't. If it doesn't, insert
+ * some. Otherwise, copy it and overwrite it.
+ */
+ if (!lmode && vp->m_start.lno == vp->m_stop.lno) {
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (p == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ }
+ vp->m_stop.cno = len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ /*
+ * !!!
+ * Historic practice, c cut into the numeric buffers,
+ * as well as the unnamed one.
+ */
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode | CUT_NUMOPT))
+ return (1);
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+ return (v_ntext(sp, ep, sp->tiqp,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags));
+ }
+
+ /*
+ * It's trickier if changing over multiple lines. If we're in
+ * line mode we delete all of the lines and insert a replacement
+ * line which the user edits. If there was leading whitespace
+ * in the first line being changed, we copy it and use it as the
+ * replacement. If we're not in line mode, we just delete the
+ * text and start inserting.
+ *
+ * !!!
+ * Historic practice, c cut into the numeric buffers, as well as the
+ * unnamed one.
+ *
+ * Copy the text.
+ */
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode | CUT_NUMOPT))
+ return (1);
+
+ /* If replacing entire lines and there's leading text. */
+ if (lmode && vp->m_start.cno) {
+ /* Get a copy of the first line changed. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ /* Copy the leading text elsewhere. */
+ GET_SPACE_RET(sp, bp, blen, vp->m_start.cno);
+ memmove(bp, p, vp->m_start.cno);
+ } else
+ bp = NULL;
+
+ /* Delete the text. */
+ if (delete(sp, ep, &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ /* If replacing entire lines, insert a replacement line. */
+ if (lmode) {
+ if (file_iline(sp, ep, vp->m_start.lno, bp, vp->m_start.cno))
+ return (1);
+ sp->lno = vp->m_start.lno;
+ len = sp->cno = vp->m_start.cno;
+ }
+
+ /* Get the line we're editing. */
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ }
+
+ /* Check to see if we're appending to the line. */
+ if (vp->m_start.cno >= len)
+ LF_SET(TXT_APPENDEOL);
+
+ rval = v_ntext(sp, ep,
+ sp->tiqp, NULL, p, len, &vp->m_final, 0, OOBLNO, flags);
+
+ if (bp != NULL)
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
+
+/*
+ * v_Replace -- [count]R
+ * Overwrite multiple characters.
+ */
+int
+v_Replace(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ u_long cnt;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ sp->showmode = "Replace";
+ flags = set_txt_std(sp, vp, 0);
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno = len ? len - 1 : 0;
+ if (v_ntext(sp, ep, sp->tiqp,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+
+ /*
+ * Special case. The historic vi handled [count]R badly, in that R
+ * would replace some number of characters, and then the count would
+ * append count-1 copies of the replacing chars to the replaced space.
+ * This seems wrong, so this version counts R commands. There is some
+ * trickiness in moving back to where the user stopped replacing after
+ * each R command. Basically, if the user ended with a newline, we
+ * want to use vp->m_final.cno (which will be 0). Otherwise, use the
+ * column after the returned cursor, unless it would be past the end of
+ * the line, in which case we append to the line.
+ */
+ while (--cnt) {
+ if ((p = file_gline(sp, ep, vp->m_final.lno, &len)) == NULL)
+ GETLINE_ERR(sp, vp->m_final.lno);
+ flags = set_txt_std(sp, vp, TXT_REPLAY);
+
+ sp->lno = vp->m_final.lno;
+
+ if (len == 0 || vp->m_final.cno == len - 1) {
+ sp->cno = len;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ sp->cno = vp->m_final.cno;
+ if (vp->m_final.cno != 0)
+ ++sp->cno;
+ LF_SET(TXT_OVERWRITE | TXT_REPLACE);
+ }
+
+ vp->m_stop.lno = sp->lno;
+ vp->m_stop.cno = sp->cno;
+ if (v_ntext(sp, ep, sp->tiqp,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * v_subst -- [buffer][count]s
+ * Substitute characters.
+ */
+int
+v_subst(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+ u_int flags;
+ char *p;
+
+ sp->showmode = "Change";
+ flags = set_txt_std(sp, vp, 0);
+ if ((p = file_gline(sp, ep, vp->m_start.lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno != 0) {
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ len = 0;
+ LF_SET(TXT_APPENDEOL);
+ } else {
+ if (len == 0)
+ LF_SET(TXT_APPENDEOL);
+ LF_SET(TXT_EMARK | TXT_OVERWRITE);
+ }
+
+ vp->m_stop.lno = vp->m_start.lno;
+ vp->m_stop.cno =
+ vp->m_start.cno + (F_ISSET(vp, VC_C1SET) ? vp->count - 1 : 0);
+ if (vp->m_stop.cno > len - 1)
+ vp->m_stop.cno = len - 1;
+
+ if (p != NULL && cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+
+ return (v_ntext(sp, ep, sp->tiqp,
+ &vp->m_stop, p, len, &vp->m_final, 0, OOBLNO, flags));
+}
+
+/*
+ * set_txt_std --
+ * Initialize text processing flags.
+ */
+static u_int
+set_txt_std(sp, vp, init)
+ SCR *sp;
+ VICMDARG *vp;
+ u_int init;
+{
+ u_int flags;
+
+ /* Text operations are all interruptible. */
+ F_SET(sp, S_INTERRUPTIBLE);
+
+ LF_INIT(init);
+ LF_SET(TXT_CNTRLT |
+ TXT_ESCAPE | TXT_MAPINPUT | TXT_RECORD | TXT_RESOLVE);
+ if (O_ISSET(sp, O_ALTWERASE))
+ LF_SET(TXT_ALTWERASE);
+ if (O_ISSET(sp, O_AUTOINDENT))
+ LF_SET(TXT_AUTOINDENT);
+ if (O_ISSET(sp, O_BEAUTIFY))
+ LF_SET(TXT_BEAUTIFY);
+ if (O_ISSET(sp, O_SHOWMATCH))
+ LF_SET(TXT_SHOWMATCH);
+ if (O_ISSET(sp, O_WRAPMARGIN))
+ LF_SET(TXT_WRAPMARGIN);
+ if (F_ISSET(sp, S_SCRIPT))
+ LF_SET(TXT_CR);
+ if (O_ISSET(sp, O_TTYWERASE))
+ LF_SET(TXT_TTYWERASE);
+ if (F_ISSET(vp, VC_ISDOT))
+ LF_SET(TXT_REPLAY);
+ return (flags);
+}
diff --git a/usr.bin/vi/vi/v_ulcase.c b/usr.bin/vi/vi/v_ulcase.c
new file mode 100644
index 0000000..959fdbb
--- /dev/null
+++ b/usr.bin/vi/vi/v_ulcase.c
@@ -0,0 +1,208 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_ulcase.c 8.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int ulcase __P((SCR *, EXF *,
+ recno_t, CHAR_T *, size_t, size_t, size_t));
+
+/*
+ * v_ulcase -- [count]~
+ * Toggle upper & lower case letters.
+ *
+ * !!!
+ * Historic vi didn't permit ~ to cross newline boundaries. I can
+ * think of no reason why it shouldn't, which at least lets the user
+ * auto-repeat through a paragraph.
+ *
+ * !!!
+ * In historic vi, the count was ignored. It would have been better
+ * if there had been an associated motion, but it's too late to make
+ * that the default now.
+ */
+int
+v_ulcase(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t cno, lcnt, len;
+ u_long cnt;
+ char *p;
+
+ lno = vp->m_start.lno;
+ cno = vp->m_start.cno;
+
+ /* EOF is an infinite count sink. */
+ for (cnt =
+ F_ISSET(vp, VC_C1SET) ? vp->count : 1; cnt > 0; cno = 0, ++lno) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL)
+ break;
+
+ /* Empty lines decrement the count by one. */
+ if (len == 0) {
+ --cnt;
+ vp->m_final.lno = lno + 1;
+ vp->m_final.cno = 0;
+ } else {
+ if (cno + cnt >= len) {
+ lcnt = len - 1;
+ cnt -= len - cno;
+
+ vp->m_final.lno = lno + 1;
+ vp->m_final.cno = 0;
+ } else {
+ lcnt = cno + cnt - 1;
+ cnt = 0;
+
+ vp->m_final.lno = lno;
+ vp->m_final.cno = lcnt + 1;
+ }
+
+ if (ulcase(sp, ep, lno, p, len, cno, lcnt))
+ return (1);
+ }
+ }
+
+ /* Check to see if we tried to move past EOF. */
+ if (file_gline(sp, ep, vp->m_final.lno, &len) == NULL) {
+ (void)file_gline(sp, ep, --vp->m_final.lno, &len);
+ vp->m_final.cno = len == 0 ? 0 : len - 1;
+ }
+ return (0);
+}
+
+/*
+ * v_mulcase -- [count]~[count]motion
+ * Toggle upper & lower case letters over a range.
+ */
+int
+v_mulcase(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ CHAR_T *p;
+ size_t len;
+ recno_t lno;
+
+ for (lno = vp->m_start.lno;;) {
+ if ((p = file_gline(sp, ep, lno, &len)) == NULL) {
+ GETLINE_ERR(sp, lno);
+ return (1);
+ }
+ if (len != 0 &&
+ ulcase(sp, ep, lno, p, len,
+ lno == vp->m_start.lno ? vp->m_start.cno : 0,
+ !F_ISSET(vp, VM_LMODE) &&
+ lno == vp->m_stop.lno ? vp->m_stop.cno : len))
+ return (1);
+
+ if (++lno > vp->m_stop.lno)
+ break;
+ }
+
+ /*
+ * XXX
+ * I didn't create a new motion command when I added motion semantics
+ * for ~. While that's the correct way to do it, that choice would
+ * have required changes all over the vi directory for little gain.
+ * Instead, we pretend it's a yank command. Note, this means that we
+ * follow the cursor motion rules for yank commands, but that seems
+ * reasonable to me.
+ */
+ return (0);
+}
+
+/*
+ * ulcase --
+ * Change part of a line's case.
+ */
+static int
+ulcase(sp, ep, lno, lp, len, scno, ecno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ CHAR_T *lp;
+ size_t len, scno, ecno;
+{
+ size_t blen;
+ int change, rval;
+ CHAR_T ch, *p, *t;
+ char *bp;
+
+ GET_SPACE_RET(sp, bp, blen, len);
+ memmove(bp, lp, len);
+
+ change = rval = 0;
+ for (p = bp + scno, t = bp + ecno + 1; p < t; ++p) {
+ ch = *(u_char *)p;
+ if (islower(ch)) {
+ *p = toupper(ch);
+ change = 1;
+ } else if (isupper(ch)) {
+ *p = tolower(ch);
+ change = 1;
+ }
+ }
+
+ if (change && file_sline(sp, ep, lno, bp, len))
+ rval = 1;
+
+ FREE_SPACE(sp, bp, blen);
+ return (rval);
+}
diff --git a/usr.bin/vi/vi/v_undo.c b/usr.bin/vi/vi/v_undo.c
new file mode 100644
index 0000000..52e502c
--- /dev/null
+++ b/usr.bin/vi/vi/v_undo.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_undo.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_Undo -- U
+ * Undo changes to this line.
+ */
+int
+v_Undo(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /*
+ * Historically, U reset the cursor to the first column in the line
+ * (not the first non-blank). This seems a bit non-intuitive, but,
+ * considering that we may have undone multiple changes, anything
+ * else (including the cursor position stored in the logging records)
+ * is going to appear random.
+ */
+ vp->m_final.cno = 0;
+
+ /*
+ * !!!
+ * Set up the flags so that an immediately subsequent 'u' will roll
+ * forward, instead of backward. In historic vi, a 'u' following a
+ * 'U' redid all of the changes to the line. Given that the user has
+ * explicitly discarded those changes by entering 'U', it seems likely
+ * that the user wants something between the original and end forms of
+ * the line, so starting to replay the changes seems the best way to
+ * get to there.
+ */
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+
+ return (log_setline(sp, ep));
+}
+
+/*
+ * v_undo -- u
+ * Undo the last change.
+ */
+int
+v_undo(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Set the command count. */
+ VIP(sp)->u_ccnt = sp->ccnt;
+
+ /*
+ * !!!
+ * In historic vi, 'u' toggled between "undo" and "redo", i.e. 'u'
+ * undid the last undo. However, if there has been a change since
+ * the last undo/redo, we always do an undo. To make this work when
+ * the user can undo multiple operations, we leave the old semantic
+ * unchanged, but make '.' after a 'u' do another undo/redo operation.
+ * This has two problems.
+ *
+ * The first is that 'u' didn't set '.' in historic vi. So, if a
+ * user made a change, realized it was in the wrong place, does a
+ * 'u' to undo it, moves to the right place and then does '.', the
+ * change was reapplied. To make this work, we only apply the '.'
+ * to the undo command if it's the command immediately following an
+ * undo command. See vi/vi.c:getcmd() for the details.
+ *
+ * The second is that the traditional way to view the numbered cut
+ * buffers in vi was to enter the commands "1pu.u.u.u. which will
+ * no longer work because the '.' immediately follows the 'u' command.
+ * Since we provide a much better method of viewing buffers, and
+ * nobody can think of a better way of adding in multiple undo, this
+ * remains broken.
+ *
+ * !!!
+ * There is change to historic practice for the final cursor position
+ * in this implementation. In historic vi, if an undo was isolated to
+ * a single line, the cursor moved to the start of the change, and
+ * then, subsequent 'u' commands would not move it again. (It has been
+ * pointed out that users used multiple undo commands to get the cursor
+ * to the start of the changed text.) Nvi toggles between the cursor
+ * position before and after the change was made. One final issue is
+ * that historic vi only did this if the user had not moved off of the
+ * line before entering the undo command; otherwise, vi would move the
+ * cursor to the most attractive position on the changed line.
+ *
+ * It would be difficult to match historic practice in this area. You
+ * not only have to know that the changes were isolated to one line,
+ * but whether it was the first or second undo command as well. And,
+ * to completely match historic practice, we'd have to track users line
+ * changes, too. This isn't worth the effort.
+ */
+ if (!F_ISSET(ep, F_UNDO)) {
+ F_SET(ep, F_UNDO);
+ ep->lundo = BACKWARD;
+ } else if (!F_ISSET(vp, VC_ISDOT))
+ ep->lundo = ep->lundo == BACKWARD ? FORWARD : BACKWARD;
+
+ switch (ep->lundo) {
+ case BACKWARD:
+ return (log_backward(sp, ep, &vp->m_final));
+ case FORWARD:
+ return (log_forward(sp, ep, &vp->m_final));
+ default:
+ abort();
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/vi/v_util.c b/usr.bin/vi/vi/v_util.c
new file mode 100644
index 0000000..cd20c6d
--- /dev/null
+++ b/usr.bin/vi/vi/v_util.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_util.c 8.12 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_eof --
+ * Vi end-of-file error.
+ */
+void
+v_eof(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ recno_t lno;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-file");
+ else {
+ if (file_lline(sp, ep, &lno))
+ return;
+ if (mp->lno >= lno)
+ msgq(sp, M_BERR, "Already at end-of-file");
+ else
+ msgq(sp, M_BERR, "Movement past the end-of-file");
+ }
+}
+
+/*
+ * v_eol --
+ * Vi end-of-line error.
+ */
+void
+v_eol(sp, ep, mp)
+ SCR *sp;
+ EXF *ep;
+ MARK *mp;
+{
+ size_t len;
+
+ if (mp == NULL)
+ msgq(sp, M_BERR, "Already at end-of-line");
+ else {
+ if (file_gline(sp, ep, mp->lno, &len) == NULL) {
+ GETLINE_ERR(sp, mp->lno);
+ return;
+ }
+ if (mp->cno == len - 1)
+ msgq(sp, M_BERR, "Already at end-of-line");
+ else
+ msgq(sp, M_BERR, "Movement past the end-of-line");
+ }
+}
+
+/*
+ * v_nomove --
+ * Vi no cursor movement error.
+ */
+void
+v_nomove(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "No cursor movement made");
+}
+
+/*
+ * v_sof --
+ * Vi start-of-file error.
+ */
+void
+v_sof(sp, mp)
+ SCR *sp;
+ MARK *mp;
+{
+ if (mp == NULL || mp->lno == 1)
+ msgq(sp, M_BERR, "Already at the beginning of the file");
+ else
+ msgq(sp, M_BERR, "Movement past the beginning of the file");
+}
+
+/*
+ * v_sol --
+ * Vi start-of-line error.
+ */
+void
+v_sol(sp)
+ SCR *sp;
+{
+ msgq(sp, M_BERR, "Already in the first column");
+}
+
+/*
+ * v_isempty --
+ * Return if the line contains nothing but white-space characters.
+ */
+int
+v_isempty(p, len)
+ char *p;
+ size_t len;
+{
+ for (; len--; ++p)
+ if (!isblank(*p))
+ return (0);
+ return (1);
+}
diff --git a/usr.bin/vi/vi/v_word.c b/usr.bin/vi/vi/v_word.c
new file mode 100644
index 0000000..95d960e
--- /dev/null
+++ b/usr.bin/vi/vi/v_word.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_word.c 8.23 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * There are two types of "words". Bigwords are easy -- groups of anything
+ * delimited by whitespace. Normal words are trickier. They are either a
+ * group of characters, numbers and underscores, or a group of anything but,
+ * delimited by whitespace. When for a word, if you're in whitespace, it's
+ * easy, just remove the whitespace and go to the beginning or end of the
+ * word. Otherwise, figure out if the next character is in a different group.
+ * If it is, go to the beginning or end of that group, otherwise, go to the
+ * beginning or end of the current group. The historic version of vi didn't
+ * get this right, so, for example, there were cases where "4e" was not the
+ * same as "eeee" -- in particular, single character words, and commands that
+ * began in whitespace were almost always handled incorrectly. To get it right
+ * you have to resolve the cursor after each search so that the look-ahead to
+ * figure out what type of "word" the cursor is in will be correct.
+ *
+ * Empty lines, and lines that consist of only white-space characters count
+ * as a single word, and the beginning and end of the file counts as an
+ * infinite number of words.
+ *
+ * Movements associated with commands are different than movement commands.
+ * For example, in "abc def", with the cursor on the 'a', "cw" is from
+ * 'a' to 'c', while "w" is from 'a' to 'd'. In general, trailing white
+ * space is discarded from the change movement. Another example is that,
+ * in the same string, a "cw" on any white space character replaces that
+ * single character, and nothing else. Ain't nothin' in here that's easy.
+ *
+ * One historic note -- in the original vi, the 'w', 'W' and 'B' commands
+ * would treat groups of empty lines as individual words, i.e. the command
+ * would move the cursor to each new empty line. The 'e' and 'E' commands
+ * would treat groups of empty lines as a single word, i.e. the first use
+ * would move past the group of lines. The 'b' command would just beep at
+ * you, or, if you did it from the start of the line as part of a motion
+ * command, go absolutely nuts. If the lines contained only white-space
+ * characters, the 'w' and 'W' commands would just beep at you, and the 'B',
+ * 'b', 'E' and 'e' commands would treat the group as a single word, and
+ * the 'B' and 'b' commands will treat the lines as individual words. This
+ * implementation treats all of these cases as a single white-space word.
+ */
+
+enum which {BIGWORD, LITTLEWORD};
+
+static int bword __P((SCR *, EXF *, VICMDARG *, enum which));
+static int eword __P((SCR *, EXF *, VICMDARG *, enum which));
+static int fword __P((SCR *, EXF *, VICMDARG *, enum which));
+
+/*
+ * v_wordW -- [count]W
+ * Move forward a bigword at a time.
+ */
+int
+v_wordW(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (fword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_wordw -- [count]w
+ * Move forward a word at a time.
+ */
+int
+v_wordw(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (fword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * fword --
+ * Move forward by words.
+ */
+static int
+fword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * If in white-space:
+ * If the count is 1, and it's a change command, we're done.
+ * Else, move to the first non-white-space character, which
+ * counts as a single word move. If it's a motion command,
+ * don't move off the end of the line.
+ */
+ if (cs.cs_flags == CS_EMP || cs.cs_flags == 0 && isblank(cs.cs_ch)) {
+ if (cs.cs_flags != CS_EMP && cnt == 1) {
+ if (F_ISSET(vp, VC_C))
+ return (0);
+ if (F_ISSET(vp, VC_D | VC_Y)) {
+ if (cs_fspace(sp, ep, &cs))
+ return (1);
+ goto ret;
+ }
+ }
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ --cnt;
+ }
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'w' command, the definition of a word keeps
+ * switching.
+ */
+ if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * If a motion command and we're at the end of the
+ * last word, we're done. Delete and yank eat any
+ * trailing blanks, but we don't move off the end
+ * of the line regardless.
+ */
+ if (cnt == 0 && ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0 && ISMOTION(vp)) {
+ if (F_ISSET(vp, VC_D | VC_Y) &&
+ cs_fspace(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /* Adjust the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+ if (ISMOTION(vp) && cs.cs_flags == 0)
+ --vp->m_stop.cno;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D
+ * and VC_Y stay at the start. Ignore VC_C and VC_DEF.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_wordE -- [count]E
+ * Move forward to the end of the bigword.
+ */
+int
+v_wordE(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (eword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_worde -- [count]e
+ * Move forward to the end of the word.
+ */
+int
+v_worde(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (eword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * eword --
+ * Move forward to the end of the word.
+ */
+static int
+eword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the next character is whitespace, move past
+ * it. (This doesn't count as a word move.) Stay at the character
+ * past the current one, it sets word "state" for the 'e' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the next word -- this involves skipping
+ * over word characters and then any trailing non-word characters.
+ * Note, for the 'e' command, the definition of a word keeps
+ * switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the start of the word after the last
+ * word, we're done. If we changed state, back up one
+ * to the end of the previous word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_prev(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_next(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_prev(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_fblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_EOF)
+ goto ret;
+ }
+
+ /*
+ * If we didn't move, we must be at EOF.
+ *
+ * !!!
+ * That's okay for motion commands, however.
+ */
+ret: if (!ISMOTION(vp) &&
+ cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * Non-motion commands move to the end of the range. VC_D
+ * and VC_Y stay at the start. Ignore VC_C and VC_DEF.
+ */
+ vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
+ return (0);
+}
+
+/*
+ * v_WordB -- [count]B
+ * Move backward a bigword at a time.
+ */
+int
+v_wordB(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (bword(sp, ep, vp, BIGWORD));
+}
+
+/*
+ * v_wordb -- [count]b
+ * Move backward a word at a time.
+ */
+int
+v_wordb(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ return (bword(sp, ep, vp, LITTLEWORD));
+}
+
+/*
+ * bword --
+ * Move backward by words.
+ */
+static int
+bword(sp, ep, vp, type)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+ enum which type;
+{
+ enum { INWORD, NOTWORD } state;
+ VCS cs;
+ u_long cnt;
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ cs.cs_lno = vp->m_start.lno;
+ cs.cs_cno = vp->m_start.cno;
+ if (cs_init(sp, ep, &cs))
+ return (1);
+
+ /*
+ * !!!
+ * If in whitespace, or the previous character is whitespace, move
+ * past it. (This doesn't count as a word move.) Stay at the
+ * character before the current one, it sets word "state" for the
+ * 'b' command.
+ */
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch)) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == 0 && !isblank(cs.cs_ch))
+ goto start;
+ }
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+
+ /*
+ * Cyclically move to the beginning of the previous word -- this
+ * involves skipping over word characters and then any trailing
+ * non-word characters. Note, for the 'b' command, the definition
+ * of a word keeps switching.
+ */
+start: if (type == BIGWORD)
+ while (cnt--) {
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ }
+ /*
+ * When we reach the end of the word before the last
+ * word, we're done. If we changed state, move forward
+ * one to the end of the next word.
+ */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_next(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+ else
+ while (cnt--) {
+ state = cs.cs_flags == 0 &&
+ inword(cs.cs_ch) ? INWORD : NOTWORD;
+ for (;;) {
+ if (cs_prev(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ break;
+ if (state == INWORD) {
+ if (!inword(cs.cs_ch))
+ break;
+ } else
+ if (inword(cs.cs_ch))
+ break;
+ }
+ /* See comment above. */
+ if (cnt == 0) {
+ if (cs.cs_flags == 0 && cs_next(sp, ep, &cs))
+ return (1);
+ break;
+ }
+
+ /* Eat whitespace characters. */
+ if (cs.cs_flags != 0 || isblank(cs.cs_ch))
+ if (cs_bblank(sp, ep, &cs))
+ return (1);
+ if (cs.cs_flags == CS_SOF)
+ goto ret;
+ }
+
+ /* If we didn't move, we must be at SOF. */
+ret: if (cs.cs_lno == vp->m_start.lno && cs.cs_cno == vp->m_start.cno) {
+ v_sof(sp, &vp->m_start);
+ return (1);
+ }
+
+ /* Set the end of the range for motion commands. */
+ vp->m_stop.lno = cs.cs_lno;
+ vp->m_stop.cno = cs.cs_cno;
+
+ /*
+ * All commands move to the end of the range. Motion commands
+ * adjust the starting point to the character before the current
+ * one.
+ *
+ * !!!
+ * The historic vi didn't get this right -- the `yb' command yanked
+ * the right stuff and even updated the cursor value, but the cursor
+ * was not actually updated on the screen.
+ */
+ vp->m_final = vp->m_stop;
+ if (ISMOTION(vp))
+ --vp->m_start.cno;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_xchar.c b/usr.bin/vi/vi/v_xchar.c
new file mode 100644
index 0000000..7ecc1b5
--- /dev/null
+++ b/usr.bin/vi/vi/v_xchar.c
@@ -0,0 +1,136 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_xchar.c 8.10 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_xchar -- [count]x
+ * Deletes the character(s) on which the cursor sits.
+ */
+int
+v_xchar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t lno;
+ size_t len;
+
+ if (file_gline(sp, ep, vp->m_start.lno, &len) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ goto nodel;
+ GETLINE_ERR(sp, vp->m_start.lno);
+ return (1);
+ }
+ if (len == 0) {
+nodel: msgq(sp, M_BERR, "No characters to delete");
+ return (1);
+ }
+
+ /*
+ * Delete from the cursor toward the end of line, w/o moving the
+ * cursor.
+ *
+ * !!!
+ * Note, "2x" at EOL isn't the same as "xx" because the left movement
+ * of the cursor as part of the 'x' command isn't taken into account.
+ * Historically correct.
+ */
+ if (F_ISSET(vp, VC_C1SET))
+ vp->m_stop.cno += vp->count - 1;
+ if (vp->m_stop.cno >= len - 1) {
+ vp->m_stop.cno = len - 1;
+ vp->m_final.cno = vp->m_start.cno ? vp->m_start.cno - 1 : 0;
+ } else
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (delete(sp, ep, &vp->m_start, &vp->m_stop, 0));
+}
+
+/*
+ * v_Xchar -- [count]X
+ * Deletes the character(s) immediately before the current cursor
+ * position.
+ */
+int
+v_Xchar(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ u_long cnt;
+
+ if (vp->m_start.cno == 0) {
+ v_sol(sp);
+ return (1);
+ }
+
+ cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
+ if (cnt >= vp->m_start.cno)
+ vp->m_start.cno = 0;
+ else
+ vp->m_start.cno -= cnt;
+ --vp->m_stop.cno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, 0))
+ return (1);
+ return (delete(sp, ep, &vp->m_start, &vp->m_stop, 0));
+}
diff --git a/usr.bin/vi/vi/v_yank.c b/usr.bin/vi/vi/v_yank.c
new file mode 100644
index 0000000..762ece6
--- /dev/null
+++ b/usr.bin/vi/vi/v_yank.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_yank.c 8.16 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_yank -- [buffer][count]Y
+ * [buffer][count]y[count][motion]
+ * Yank text (or lines of text) into a cut buffer.
+ *
+ * !!!
+ * Historic vi moved the cursor to the from MARK if it was before the current
+ * cursor and on a different line, e.g., "yj" moves the cursor but "yk" and
+ * "yh" do not. Unfortunately, it's too late to change this now. Matching
+ * the historic semantics isn't easy. The line number was always changed and
+ * column movement was usually relative. However, "y'a" moved the cursor to
+ * the first non-blank of the line marked by a, while "y`a" moved the cursor
+ * to the line and column marked by a. Hopefully, the motion component code
+ * got it right... Unlike delete, we make no adjustments here.
+ */
+int
+v_yank(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ int lmode;
+
+ /* The line may not exist in line mode cuts, check to be sure. */
+ if (F_ISSET(vp, VM_LMODE)) {
+ if (file_gline(sp, ep, vp->m_stop.lno, NULL) == NULL) {
+ v_eof(sp, ep, &vp->m_start);
+ return (1);
+ }
+ lmode = CUT_LINEMODE;
+ } else
+ lmode = 0;
+ if (cut(sp, ep,
+ F_ISSET(vp, VC_BUFFER) ? &vp->buffer : NULL,
+ &vp->m_start, &vp->m_stop, lmode))
+ return (1);
+
+ sp->rptlines[L_YANKED] += (vp->m_stop.lno - vp->m_start.lno) + 1;
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_z.c b/usr.bin/vi/vi/v_z.c
new file mode 100644
index 0000000..5c6fdd0
--- /dev/null
+++ b/usr.bin/vi/vi/v_z.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_z.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * v_z -- [count]z[count][-.+^<CR>]
+ * Move the screen.
+ */
+int
+v_z(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ recno_t last, lno;
+ u_int value;
+
+ /*
+ * The first count is the line to use. If the value doesn't
+ * exist, use the last line.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ lno = vp->count;
+ if (file_lline(sp, ep, &last))
+ return (1);
+ if (lno > last)
+ lno = last;
+ } else
+ lno = vp->m_start.lno;
+
+ /* Set default return cursor values. */
+ vp->m_final.lno = lno;
+ vp->m_final.cno = vp->m_start.cno;
+
+ /*
+ * The second count is the displayed window size, i.e. the 'z'
+ * command is another way to get artificially small windows.
+ *
+ * !!!
+ * A window size of 0 was historically allowed, and simply ignored.
+ * Also, this could be much more simply done by modifying the value
+ * of the O_WINDOW option, but that's not how it worked historically.
+ */
+ if (F_ISSET(vp, VC_C2SET) &&
+ vp->count2 != 0 && sp->s_crel(sp, vp->count2))
+ return (1);
+
+ switch (vp->character) {
+ case '-': /* Put the line at the bottom. */
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ break;
+ case '.': /* Put the line in the middle. */
+ if (sp->s_fill(sp, ep, lno, P_MIDDLE))
+ return (1);
+ break;
+ case '+':
+ /*
+ * If the user specified a line number, put that line at the
+ * top and move the cursor to it. Otherwise, scroll forward
+ * a screen from the current screen.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (sp->s_fill(sp, ep, lno, P_TOP))
+ return (1);
+ if (sp->s_position(sp, ep, &vp->m_final, 0, P_TOP))
+ return (1);
+ } else
+ if (sp->s_scroll(sp, ep,
+ &vp->m_final, sp->t_rows, Z_PLUS))
+ return (1);
+ break;
+ case '^':
+ /*
+ * If the user specified a line number, put that line at the
+ * bottom, move the cursor to it, and then display the screen
+ * before that one. Otherwise, scroll backward a screen from
+ * the current screen.
+ *
+ * !!!
+ * Note, we match the off-by-one characteristics of historic
+ * vi, here.
+ */
+ if (F_ISSET(vp, VC_C1SET)) {
+ if (sp->s_fill(sp, ep, lno, P_BOTTOM))
+ return (1);
+ if (sp->s_position(sp, ep, &vp->m_final, 0, P_TOP))
+ return (1);
+ if (sp->s_fill(sp, ep, vp->m_final.lno, P_BOTTOM))
+ return (1);
+ } else
+ if (sp->s_scroll(sp, ep,
+ &vp->m_final, sp->t_rows, Z_CARAT))
+ return (1);
+ break;
+ default: /* Put the line at the top for <cr>. */
+ value = KEY_VAL(sp, vp->character);
+ if (value != K_CR && value != K_NL) {
+ msgq(sp, M_ERR, "usage: %s", vp->kp->usage);
+ return (1);
+ }
+ if (sp->s_fill(sp, ep, lno, P_TOP))
+ return (1);
+ break;
+ }
+
+
+ return (0);
+}
diff --git a/usr.bin/vi/vi/v_zexit.c b/usr.bin/vi/vi/v_zexit.c
new file mode 100644
index 0000000..d699db3
--- /dev/null
+++ b/usr.bin/vi/vi/v_zexit.c
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)v_zexit.c 8.13 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "excmd.h"
+#include "vcmd.h"
+
+/*
+ * v_zexit -- ZZ
+ * Save the file and exit.
+ */
+int
+v_zexit(sp, ep, vp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *vp;
+{
+ /* Write back any modifications. */
+ if (F_ISSET(ep, F_MODIFIED) &&
+ file_write(sp, ep, NULL, NULL, NULL, FS_ALL))
+ return (1);
+
+ /* Check to make sure it's not a temporary file. */
+ if (file_m3(sp, ep, 0))
+ return (1);
+
+ /* Check for more files to edit. */
+ if (ex_ncheck(sp, 0))
+ return (1);
+
+ F_SET(sp, S_EXIT);
+ return (0);
+}
diff --git a/usr.bin/vi/vi/vcmd.c b/usr.bin/vi/vi/vcmd.c
new file mode 100644
index 0000000..f82aeb5
--- /dev/null
+++ b/usr.bin/vi/vi/vcmd.c
@@ -0,0 +1,533 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vcmd.c 8.41 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+/*
+ * This array maps keystrokes to vi command functions. It is known
+ * in ex/ex_usage.c that it takes four columns to name a vi character.
+ */
+VIKEYS const vikeys [MAXVIKEY + 1] = {
+/* 000 NUL -- The code in vi.c expects key 0 to be undefined. */
+ {NULL},
+/* 001 ^A */
+ {v_searchw, V_ABS|V_CNT|V_MOVE|V_KEYW|VM_CUTREQ|VM_RCM_SET,
+ "[count]^A",
+ "^A search forward for cursor word"},
+/* 002 ^B */
+ {v_pageup, V_CNT|VM_RCM_SET,
+ "[count]^B",
+ "^B scroll up by screens"},
+/* 003 ^C */
+ {NULL, 0,
+ "^C",
+ "^C interrupt an operation (e.g. read, write, search)"},
+/* 004 ^D */
+ {v_hpagedown, V_CNT|VM_RCM_SET,
+ "[count]^D",
+ "^D scroll down by half screens (setting count)"},
+/* 005 ^E */
+ {v_linedown, V_CNT,
+ "[count]^E",
+ "^E scroll down by lines"},
+/* 006 ^F */
+ {v_pagedown, V_CNT|VM_RCM_SET,
+ "[count]^F",
+ "^F scroll down by screens"},
+/* 007 ^G */
+ {v_status, 0,
+ "^G",
+ "^G file status"},
+/* 010 ^H */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]^H",
+ "^H move left by characters"},
+/* 011 ^I */
+ {NULL},
+/* 012 ^J */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^J",
+ "^J move down by lines"},
+/* 013 ^K */
+ {NULL},
+/* 014 ^L */
+ {v_redraw, 0,
+ "^L",
+ "^L redraw screen"},
+/* 015 ^M */
+ {v_cr, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]^M",
+ "^M move down by lines (to first non-blank)"},
+/* 016 ^N */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^N",
+ "^N move down by lines"},
+/* 017 ^O */
+ {NULL},
+/* 020 ^P */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]^P",
+ "^P move up by lines"},
+/* 021 ^Q -- not available, used for hardware flow control. */
+ {NULL},
+/* 022 ^R */
+ {v_redraw, 0,
+ "^R",
+ "^R redraw screen"},
+/* 023 ^S -- not available, used for hardware flow control. */
+ {NULL},
+/* 024 ^T */
+ {v_tagpop, V_ABS|VM_RCM_SET,
+ "^T",
+ "^T tag pop"},
+/* 025 ^U */
+ {v_hpageup, V_CNT|VM_RCM_SET,
+ "[count]^U",
+ "^U half page up (set count)"},
+/* 026 ^V */
+ {NULL, 0,
+ "^V",
+ "^V input a literal character"},
+/* 027 ^W */
+ {v_screen, 0,
+ "^W",
+ "^W move to next screen"},
+/* 030 ^X */
+ {NULL},
+/* 031 ^Y */
+ {v_lineup, V_CNT,
+ "[count]^Y",
+ "^Y page up by lines"},
+/* 032 ^Z */
+ {v_stop, 0,
+ "^Z",
+ "^Z suspend editor"},
+/* 033 ^[ */
+ {NULL, 0,
+ "^[ <escape>",
+ "^[ <escape> leave input mode, return to command mode"},
+/* 034 ^\ */
+ {v_exmode, 0,
+ "^\\",
+ " ^\\ switch to ex mode"},
+/* 035 ^] */
+ {v_tagpush, V_ABS|V_KEYW|VM_RCM_SET,
+ "^]",
+ "^] tag push cursor word"},
+/* 036 ^^ */
+ {v_switch, 0,
+ "^^",
+ "^^ switch to previous file"},
+/* 037 ^_ */
+ {NULL},
+/* 040 ' ' */
+ {v_right, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]' '",
+ " <space> move right by columns"},
+/* 041 ! */
+ {v_filter, V_CNT|V_DOT|V_MOTION|VC_DEF|VM_RCM_SET,
+ "[count]![count]motion command(s)",
+ " ! filter through command(s) to motion"},
+/* 042 " */
+ {NULL},
+/* 043 # */
+ {v_increment, V_CHAR|V_CNT|V_DOT|V_KEYNUM|VM_RCM_SET,
+ "[count]#[#+-]",
+ " # number increment/decrement"},
+/* 044 $ */
+ {v_dollar, V_CNT|V_MOVE|VM_RCM_SETLAST,
+ " [count]$",
+ " $ move to last column"},
+/* 045 % */
+ {v_match, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "%",
+ " % move to match"},
+/* 046 & */
+ {v_again, 0,
+ "&",
+ " & repeat substitution"},
+/* 047 ' */
+ {v_fmark, V_ABS_L|V_CHAR|V_MOVE|VM_LMODE,
+ "'['a-z]",
+ " ' move to mark (to first non-blank)"},
+/* 050 ( */
+ {v_sentenceb, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count](",
+ " ( move back sentence"},
+/* 051 ) */
+ {v_sentencef, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count])",
+ " ) move forward sentence"},
+/* 052 * */
+ {NULL},
+/* 053 + */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]+",
+ " + move down by lines (to first non-blank)"},
+/* 054 , */
+ {v_chrrepeat, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count],",
+ " , reverse last F, f, T or t search"},
+/* 055 - */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]-",
+ " - move up by lines (to first non-blank)"},
+/* 056 . */
+ {NULL, 0,
+ ".",
+ " . repeat the last command"},
+/* 057 / */
+ {v_searchf, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "/RE[/ offset]",
+ " / search forward"},
+/* 060 0 */
+ {v_zero, V_MOVE|VM_RCM_SET,
+ "0",
+ " 0 move to first character"},
+/* 061 1 */
+ {NULL},
+/* 062 2 */
+ {NULL},
+/* 063 3 */
+ {NULL},
+/* 064 4 */
+ {NULL},
+/* 065 5 */
+ {NULL},
+/* 066 6 */
+ {NULL},
+/* 067 7 */
+ {NULL},
+/* 070 8 */
+ {NULL},
+/* 071 9 */
+ {NULL},
+/* 072 : */
+ {v_ex, 0,
+ ":command [| command] ...",
+ " : ex command"},
+/* 073 ; */
+ {v_chrepeat, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count];",
+ " ; repeat last F, f, T or t search"},
+/* 074 < */
+ {v_shiftl, V_CNT|V_DOT|V_MOTION|VC_DEF|VM_RCM_SET,
+ "[count]<[count]motion",
+ " < shift lines left to motion"},
+/* 075 = */
+ {NULL},
+/* 076 > */
+ {v_shiftr, V_CNT|V_DOT|V_MOTION|VC_DEF|VM_RCM_SET,
+ "[count]>[count]motion",
+ " > shift lines right to motion"},
+/* 077 ? */
+ {v_searchb, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "?RE[? offset]",
+ " ? search backward"},
+/* 100 @ */
+ {v_at, V_RBUF|VM_RCM_SET,
+ "@buffer",
+ " @ execute buffer"},
+/* 101 A */
+ {v_iA, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]A",
+ " A append to the line"},
+/* 102 B */
+ {v_wordB, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]B",
+ " B move back bigword"},
+/* 103 C */
+ {v_Change, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]C",
+ " C change to end-of-line"},
+/* 104 D */
+ {v_Delete, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]D",
+ " D delete to end-of-line"},
+/* 105 E */
+ {v_wordE, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]E",
+ " E move to end of bigword"},
+/* 106 F */
+ {v_chF, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]F character",
+ " F character in line backward search"},
+/* 107 G */
+ {v_lgoto, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETFNB,
+ "[count]G",
+ " G move to line"},
+/* 110 H */
+ {v_home, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]H",
+ " H move to count lines from screen top"},
+/* 111 I */
+ {v_iI, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]I",
+ " I insert at line beginning"},
+/* 112 J */
+ {v_join, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]J",
+ " J join lines"},
+/* 113 K */
+ {NULL},
+/* 114 L */
+ {v_bottom, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "[count]L",
+ " L move to screen bottom"},
+/* 115 M */
+ {v_middle, V_ABS_L|V_CNT|V_MOVE|VM_LMODE|VM_RCM_SETNNB,
+ "M",
+ " M move to screen middle"},
+/* 116 N */
+ {v_searchN, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "n",
+ " N reverse last search"},
+/* 117 O */
+ {v_iO, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]O",
+ " O insert above line"},
+/* 120 P */
+ {v_Put, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer]P",
+ " P insert before cursor from buffer"},
+/* 121 Q */
+ {v_exmode, 0,
+ "Q",
+ " Q switch to ex mode"},
+/* 122 R */
+ {v_Replace, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]R",
+ " R replace characters"},
+/* 123 S */
+ {v_Subst, V_CNT|V_DOT|V_OBUF|VM_LMODE|VM_RCM_SET,
+ "[buffer][count]S",
+ " S substitute for the line(s)"},
+/* 124 T */
+ {v_chT, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]T character",
+ " T before character in line backward search"},
+/* 125 U */
+ {v_Undo, VM_RCM_SET,
+ "U",
+ " U Restore the current line"},
+/* 126 V */
+ {NULL},
+/* 127 W */
+ {v_wordW, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]W",
+ " W move to next bigword"},
+/* 130 X */
+ {v_Xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]X",
+ " X delete character before cursor"},
+/* 131 Y */
+ {v_yank, V_CNT|VM_LMODE|V_OBUF,
+ "[buffer][count]Y",
+ " Y copy line"},
+/* 132 Z */
+ {v_zexit, 0,
+ "ZZ",
+ "ZZ save file and exit"},
+/* 133 [ */
+ {v_sectionb, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "[[",
+ "[[ move back section"},
+/* 134 \ */
+ {NULL},
+/* 135 ] */
+ {v_sectionf, V_ABS|V_CNT|V_MOVE|VM_RCM_SET,
+ "]]",
+ "]] move forward section"},
+/* 136 ^ */
+ /*
+ * DON'T set the VM_RCM_SETFNB flag, the function has to do the work
+ * anyway, in case it's a motion component. DO set VM_RCM_SET, so
+ * that any motion that's part of a command is preserved.
+ */
+ {v_first, V_CNT|V_MOVE|VM_RCM_SET,
+ "^",
+ " ^ move to first non-blank"},
+/* 137 _ */
+ /*
+ * Needs both to set the VM_RCM_SETFNB flag, and to do the work
+ * in the function, in case it's a delete.
+ */
+ {v_cfirst, V_CNT|V_MOVE|VM_RCM_SETFNB,
+ "_",
+ " _ move to first non-blank"},
+/* 140 ` */
+ {v_bmark, V_ABS_C|V_CHAR|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "`[`a-z]",
+ " ` move to mark"},
+/* 141 a */
+ {v_ia, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]a",
+ " a append after cursor"},
+/* 142 b */
+ {v_wordb, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]b",
+ " b move back word"},
+/* 143 c */
+ {v_change, V_CNT|V_DOT|V_MOTION|V_OBUF|VC_C|VM_RCM_SET,
+ "[buffer][count]c[count]motion",
+ " c change to motion"},
+/* 144 d */
+ {v_delete, V_CNT|V_DOT|V_MOTION|V_OBUF|VC_D|VM_RCM_SET,
+ "[buffer][count]d[count]motion",
+ " d delete to motion"},
+/* 145 e */
+ {v_worde, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]e",
+ " e move to end of word"},
+/* 146 f */
+ {v_chf, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]f character",
+ " f character in line forward search"},
+/* 147 g */
+ {NULL},
+/* 150 h */
+ {v_left, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]h",
+ " h move left by columns"},
+/* 151 i */
+ {v_ii, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]i",
+ " i insert before cursor"},
+/* 152 j */
+ {v_down, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]j",
+ " j move down by lines"},
+/* 153 k */
+ {v_up, V_CNT|V_MOVE|VM_LMODE|VM_RCM,
+ "[count]k",
+ " k move up by lines"},
+/* 154 l */
+ {v_right, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]l",
+ " l move right by columns"},
+/* 155 m */
+ {v_mark, V_CHAR,
+ "m[a-z]",
+ " m set mark"},
+/* 156 n */
+ {v_searchn, V_ABS_C|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "n",
+ " n repeat last search"},
+/* 157 o */
+ {v_io, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]o",
+ " o append after line"},
+/* 160 p */
+ {v_put, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer]p",
+ " p insert after cursor from buffer"},
+/* 161 q */
+ {NULL},
+/* 162 r */
+ {v_replace, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]r character",
+ " r replace character"},
+/* 163 s */
+ {v_subst, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]s",
+ " s substitute character"},
+/* 164 t */
+ {v_cht, V_CHAR|V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]t character",
+ " t before character in line forward search"},
+/* 165 u */
+ /*
+ * DON'T set the V_DOT flag, it' more complicated than that.
+ * See vi/vi.c for details.
+ */
+ {v_undo, VM_RCM_SET,
+ "u",
+ " u undo last change"},
+/* 166 v */
+ {NULL},
+/* 167 w */
+ {v_wordw, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]w",
+ " w move to next word"},
+/* 170 x */
+ {v_xchar, V_CNT|V_DOT|V_OBUF|VM_RCM_SET,
+ "[buffer][count]x",
+ " x delete character"},
+/* 171 y */
+ {v_yank, V_CNT|V_MOTION|V_OBUF|VC_Y|VM_RCM_SET,
+ "[buffer][count]y[count]motion",
+ " y copy text to motion into a cut buffer"},
+/* 172 z */
+ /*
+ * DON'T set the V_CHAR flag, the char isn't required,
+ * so it's handled specially in getcmd().
+ */
+ {v_z, V_ABS_L|V_CNT|VM_RCM_SET,
+ "[line]z[window_size][-|.|+|^|<CR>]",
+ " z redraw window"},
+/* 173 { */
+ {v_paragraphb, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count]{",
+ " { move back paragraph"},
+/* 174 | */
+ {v_ncol, V_CNT|V_MOVE|VM_RCM_SET,
+ "[count]|",
+ " | move to column"},
+/* 175 } */
+ {v_paragraphf, V_ABS|V_CNT|V_MOVE|VM_CUTREQ|VM_RCM_SET,
+ "[count]}",
+ " } move forward paragraph"},
+/* 176 ~ */
+ {v_ulcase, V_CNT|V_DOT|VM_RCM_SET,
+ "[count]~",
+ " ~ reverse case"},
+};
diff --git a/usr.bin/vi/vi/vcmd.h b/usr.bin/vi/vi/vcmd.h
new file mode 100644
index 0000000..758c97b
--- /dev/null
+++ b/usr.bin/vi/vi/vcmd.h
@@ -0,0 +1,346 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vcmd.h 8.40 (Berkeley) 8/5/94
+ */
+
+typedef struct _vikeys VIKEYS;
+
+/* Structure passed around to functions implementing vi commands. */
+typedef struct _vicmdarg {
+#define vp_startzero buffer /* START ZERO OUT. */
+ CHAR_T key; /* Command key. */
+ CHAR_T buffer; /* Buffer. */
+ CHAR_T character; /* Character. */
+ u_long count; /* Count. */
+ u_long count2; /* Second count (only used by z). */
+ VIKEYS const *kp; /* VIKEYS key. */
+ size_t klen; /* Keyword length. */
+
+ /*
+ * Historic vi allowed "dl" when the cursor was on the last column,
+ * deleting the last character, and similarly allowed "dw" when
+ * the cursor was on the last column of the file. It didn't allow
+ * "dh" when the cursor was on column 1, although these cases are
+ * not strictly analogous. The point is that some movements would
+ * succeed if they were associated with a motion command, and fail
+ * otherwise. This is part of the off-by-1 schizophrenia that
+ * plagued vi. Other examples are that "dfb" deleted everything
+ * up to and including the next 'b' character, while "d/b" deleted
+ * everything up to the next 'b' character. While this implementation
+ * regularizes the interface to the extent possible, there are many
+ * special cases that can't be fixed. The special cases are handled
+ * by setting flags per command so that the underlying command and
+ * motion routines know what's really going on.
+ *
+ * The VC_* and VM_* flags are set in the vikeys array, and the VM_*
+ * flags may be set by the underlying functions (motion component or
+ * command) as well. For this reason, the flags in the VICMDARG and
+ * VIKEYS structures live in the same name space.
+ */
+#define VC_C 0x00000001 /* The 'c' command. */
+#define VC_D 0x00000002 /* The 'd' command. */
+#define VC_DEF 0x00000004 /* The '<', '>' and '!' commands. */
+#define VC_Y 0x00000008 /* The 'y' command. */
+#define VC_COMMASK 0x0000000f /* Mask for VC flags. */
+#define ISMOTION(vp) F_ISSET(vp, VC_COMMASK)
+
+#define VM_CUTREQ 0x00000010 /* Always cut into numeric buffers. */
+#define VM_LDOUBLE 0x00000020 /* Doubled command for line mode. */
+#define VM_LMODE 0x00000040 /* Motion is line oriented. */
+#define VM_NOMOTION 0x00000080 /* Motion command not entered. */
+#define VM_COMMASK 0x000000f0 /* Mask for VM flags. */
+
+ /*
+ * The VM_RCM_* flags are single usage, i.e. if you set one, you have
+ * to clear the others.
+ */
+#define VM_RCM 0x00000100 /* Use relative cursor movment (RCM). */
+#define VM_RCM_SET 0x00000200 /* RCM: set to current position. */
+#define VM_RCM_SETFNB 0x00000400 /* RCM: set to first non-blank (FNB). */
+#define VM_RCM_SETLAST 0x00000800 /* RCM: set to last character. */
+#define VM_RCM_SETNNB 0x00001000 /* RCM: set to next non-blank. */
+#define VM_RCM_MASK 0x00001f00 /* Mask for RCM flags. */
+
+ /* Flags for the underlying function. */
+#define VC_BUFFER 0x00002000 /* The buffer was set. */
+#define VC_C1RESET 0x00004000 /* Reset C1SET flag for dot commands. */
+#define VC_C1SET 0x00008000 /* Count 1 was set. */
+#define VC_C2SET 0x00010000 /* Count 2 was set. */
+#define VC_ISDOT 0x00020000 /* Command was the dot command. */
+ u_int32_t flags;
+
+#define vp_endzero keyword /* END ZERO OUT. */
+ char *keyword; /* Keyword. */
+ size_t kbuflen; /* Keyword buffer length. */
+ /*
+ * There are four cursor locations that we worry about: the initial
+ * cursor position, the start of the range, the end of the range,
+ * and the final cursor position. The initial cursor position and
+ * the start of the range are both m_start, and are always the same.
+ * All locations are initialized to the starting cursor position by
+ * the main vi routines, and the underlying functions depend on this.
+ *
+ * Commands that can be motion components set the end of the range
+ * cursor position, m_stop. All commands must set the ending cursor
+ * position, m_final. The reason that m_stop isn't the same as m_final
+ * is that there are situations where the final position of the cursor
+ * is outside of the cut/delete range (e.g. 'd[[' from the first column
+ * of a line). The final cursor position often varies based on the
+ * direction of the movement, as well as the command. The only special
+ * case that the delete code handles is that it will make adjustments
+ * if the final cursor position is deleted.
+ *
+ * The reason for all of this is that the historic vi semantics were
+ * defined command-by-command. Every function has to roll its own
+ * starting and stopping positions, and adjust them if it's being used
+ * as a motion component. The general rules are as follows:
+ * 1: If not a motion component, the final cursor is at the end
+ * of the range.
+ * 2: If moving backward in the file:
+ * a: VC_D moves to the end of the range.
+ * b: If the line hasn't changed, VC_Y doesn't move, else it
+ * moves to the end of the range.
+ * 3: If moving forward in the file, VC_D and VC_Y stay at the
+ * start of the range.
+ *
+ * Usually, if moving backward in the file and it's a motion component,
+ * the starting cursor is decremented by a single character (or, in a
+ * few cases, to the end of the previous line) so that the starting
+ * cursor character isn't cut or deleted. No cursor adjustment is
+ * needed for moving forward, because the cut/delete routines handle
+ * m_stop inclusively, i.e. the last character in the range is cut or
+ * deleted. This makes cutting to the EOF/EOL reasonable.
+ *
+ * The 'c', '<', '>', and '!' commands are special cases. We ignore
+ * the final cursor position for all of them: for 'c', the text input
+ * routines set the cursor to the last character inserted; for '<',
+ * '>' and '!', the underlying ex commands that do the operation will
+ * set the cursor for us. We still need a VC_C flag because there are
+ * special motion semantics that are associated with the 'c' command.
+ * We don't need VC_ flags for the others, because, as far as I know,
+ * there are no special semantics associated with their motions. So,
+ * we group them under a single VC_ flag so that the standard motion
+ * adjustments get done.
+ */
+ MARK m_start; /* mark: initial cursor, range start. */
+ MARK m_stop; /* mark: range end. */
+ MARK m_final; /* mark: final cursor position. */
+} VICMDARG;
+
+/* Vi command structure. */
+struct _vikeys { /* Underlying function. */
+ int (*func) __P((SCR *, EXF *, VICMDARG *));
+#define V_ABS 0x00040000 /* Absolute movement, set '' mark. */
+#define V_ABS_C 0x00080000 /* V_ABS: if the line/column changed. */
+#define V_ABS_L 0x00100000 /* V_ABS: if the line changed. */
+#define V_CHAR 0x00200000 /* Character (required, trailing). */
+#define V_CNT 0x00400000 /* Count (optional, leading). */
+#define V_DOT 0x00800000 /* On success, sets dot command. */
+#define V_KEYNUM 0x01000000 /* Cursor referenced number. */
+#define V_KEYW 0x02000000 /* Cursor referenced word. */
+#define V_MOTION 0x04000000 /* Motion (required, trailing). */
+#define V_MOVE 0x08000000 /* Command defines movement. */
+#define V_OBUF 0x10000000 /* Buffer (optional, leading). */
+#define V_RBUF 0x20000000 /* Buffer (required, trailing). */
+ u_int32_t flags;
+ char *usage; /* Usage line. */
+ char *help; /* Help line. */
+};
+#define MAXVIKEY 126 /* List of vi commands. */
+extern VIKEYS const vikeys[MAXVIKEY + 1];
+extern VIKEYS const tmotion; /* XXX Hacked ~ command. */
+
+/* Definition of a vi "word". */
+#define inword(ch) (isalnum(ch) || (ch) == '_')
+
+/* Offset to next column of stop size. */
+#define STOP_OFF(c, stop) (stop - (c) % stop)
+
+/* Character stream structure, prototypes. */
+typedef struct _vcs {
+ recno_t cs_lno; /* Line. */
+ size_t cs_cno; /* Column. */
+ CHAR_T *cs_bp; /* Buffer. */
+ size_t cs_len; /* Length. */
+ CHAR_T cs_ch; /* Character. */
+#define CS_EMP 1 /* Empty line. */
+#define CS_EOF 2 /* End-of-file. */
+#define CS_EOL 3 /* End-of-line. */
+#define CS_SOF 4 /* Start-of-file. */
+ int cs_flags; /* Return flags. */
+} VCS;
+
+int cs_bblank __P((SCR *, EXF *, VCS *));
+int cs_fblank __P((SCR *, EXF *, VCS *));
+int cs_fspace __P((SCR *, EXF *, VCS *));
+int cs_init __P((SCR *, EXF *, VCS *));
+int cs_next __P((SCR *, EXF *, VCS *));
+int cs_prev __P((SCR *, EXF *, VCS *));
+
+ /* Character search information. */
+enum cdirection { CNOTSET, FSEARCH, fSEARCH, TSEARCH, tSEARCH };
+
+/* Vi private, per-screen memory. */
+typedef struct _vi_private {
+ VICMDARG sdot; /* Saved dot, motion command. */
+ VICMDARG sdotmotion;
+
+ CHAR_T rlast; /* Last 'r' command character. */
+
+ char *rep; /* Input replay buffer. */
+ size_t rep_len; /* Input replay buffer length. */
+ size_t rep_cnt; /* Input replay buffer characters. */
+
+ CHAR_T inc_lastch; /* Last increment character. */
+ long inc_lastval; /* Last increment value. */
+
+ char *ps; /* Paragraph plus section list. */
+
+ u_long u_ccnt; /* Undo command count. */
+
+ CHAR_T lastckey; /* Last search character. */
+ enum cdirection csearchdir; /* Character search direction. */
+} VI_PRIVATE;
+
+#define VIP(sp) ((VI_PRIVATE *)((sp)->vi_private))
+
+/* Vi function prototypes. */
+int txt_auto __P((SCR *, EXF *, recno_t, TEXT *, size_t, TEXT *));
+int v_buildps __P((SCR *));
+int v_end __P((SCR *));
+void v_eof __P((SCR *, EXF *, MARK *));
+void v_eol __P((SCR *, EXF *, MARK *));
+int v_exwrite __P((void *, const char *, int));
+int v_init __P((SCR *, EXF *));
+int v_isempty __P((char *, size_t));
+int v_msgflush __P((SCR *));
+void v_nomove __P((SCR *));
+int v_ntext __P((SCR *, EXF *, TEXTH *, MARK *,
+ const char *, const size_t, MARK *, ARG_CHAR_T, recno_t, u_int));
+int v_optchange __P((SCR *, int));
+int v_screen_copy __P((SCR *, SCR *));
+int v_screen_end __P((SCR *));
+void v_sof __P((SCR *, MARK *));
+void v_sol __P((SCR *));
+int vi __P((SCR *, EXF *));
+
+#define VIPROTO(name) int name __P((SCR *, EXF *, VICMDARG *))
+VIPROTO(v_again);
+VIPROTO(v_at);
+VIPROTO(v_bmark);
+VIPROTO(v_bottom);
+VIPROTO(v_cfirst);
+VIPROTO(v_Change);
+VIPROTO(v_change);
+VIPROTO(v_chF);
+VIPROTO(v_chf);
+VIPROTO(v_chrepeat);
+VIPROTO(v_chrrepeat);
+VIPROTO(v_chT);
+VIPROTO(v_cht);
+VIPROTO(v_cr);
+VIPROTO(v_Delete);
+VIPROTO(v_delete);
+VIPROTO(v_dollar);
+VIPROTO(v_down);
+VIPROTO(v_ex);
+VIPROTO(v_exmode);
+VIPROTO(v_filter);
+VIPROTO(v_first);
+VIPROTO(v_fmark);
+VIPROTO(v_home);
+VIPROTO(v_hpagedown);
+VIPROTO(v_hpageup);
+VIPROTO(v_iA);
+VIPROTO(v_ia);
+VIPROTO(v_iI);
+VIPROTO(v_ii);
+VIPROTO(v_increment);
+VIPROTO(v_iO);
+VIPROTO(v_io);
+VIPROTO(v_join);
+VIPROTO(v_left);
+VIPROTO(v_lgoto);
+VIPROTO(v_linedown);
+VIPROTO(v_lineup);
+VIPROTO(v_mark);
+VIPROTO(v_match);
+VIPROTO(v_middle);
+VIPROTO(v_mulcase);
+VIPROTO(v_ncol);
+VIPROTO(v_pagedown);
+VIPROTO(v_pageup);
+VIPROTO(v_paragraphb);
+VIPROTO(v_paragraphf);
+VIPROTO(v_Put);
+VIPROTO(v_put);
+VIPROTO(v_redraw);
+VIPROTO(v_Replace);
+VIPROTO(v_replace);
+VIPROTO(v_right);
+VIPROTO(v_screen);
+VIPROTO(v_searchb);
+VIPROTO(v_searchf);
+VIPROTO(v_searchN);
+VIPROTO(v_searchn);
+VIPROTO(v_searchw);
+VIPROTO(v_sectionb);
+VIPROTO(v_sectionf);
+VIPROTO(v_sentenceb);
+VIPROTO(v_sentencef);
+VIPROTO(v_shiftl);
+VIPROTO(v_shiftr);
+VIPROTO(v_status);
+VIPROTO(v_stop);
+VIPROTO(v_Subst);
+VIPROTO(v_subst);
+VIPROTO(v_switch);
+VIPROTO(v_tagpop);
+VIPROTO(v_tagpush);
+VIPROTO(v_ulcase);
+VIPROTO(v_Undo);
+VIPROTO(v_undo);
+VIPROTO(v_up);
+VIPROTO(v_wordB);
+VIPROTO(v_wordb);
+VIPROTO(v_wordE);
+VIPROTO(v_worde);
+VIPROTO(v_wordW);
+VIPROTO(v_wordw);
+VIPROTO(v_Xchar);
+VIPROTO(v_xchar);
+VIPROTO(v_Yank);
+VIPROTO(v_yank);
+VIPROTO(v_z);
+VIPROTO(v_zero);
+VIPROTO(v_zexit);
diff --git a/usr.bin/vi/vi/vi.c b/usr.bin/vi/vi/vi.c
new file mode 100644
index 0000000..40ed3e7
--- /dev/null
+++ b/usr.bin/vi/vi/vi.c
@@ -0,0 +1,937 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vi.c 8.91 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "vcmd.h"
+
+static int getcmd __P((SCR *, EXF *,
+ VICMDARG *, VICMDARG *, VICMDARG *, int *, int *));
+static __inline int
+ getcount __P((SCR *, ARG_CHAR_T, u_long *));
+static __inline int
+ getkey __P((SCR *, CH *, u_int));
+static int getkeyword __P((SCR *, EXF *, VICMDARG *, u_int));
+static int getmotion __P((SCR *, EXF *, VICMDARG *, VICMDARG *, int *));
+
+/*
+ * Side-effect:
+ * The dot structure can be set by the underlying vi functions,
+ * see v_Put() and v_put().
+ */
+#define DOT (&VIP(sp)->sdot)
+#define DOTMOTION (&VIP(sp)->sdotmotion)
+
+/*
+ * vi --
+ * Main vi command loop.
+ */
+int
+vi(sp, ep)
+ SCR *sp;
+ EXF *ep;
+{
+ MARK abs;
+ VICMDARG cmd, *vp;
+ u_int flags, saved_mode;
+ int comcount, eval, mapped;
+
+ /* Start vi and paint the screen. */
+ if (v_init(sp, ep))
+ return (1);
+
+ /* Command initialization. */
+ memset(&cmd, 0, sizeof(VICMDARG));
+
+ for (eval = 0, vp = &cmd;;) {
+ /* Refresh the screen. */
+ sp->showmode = "Command";
+ if (sp->s_refresh(sp, ep)) {
+ eval = 1;
+ break;
+ }
+
+ /* Set the new favorite position. */
+ if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
+ sp->rcm_last = 0;
+ (void)sp->s_column(sp, ep, &sp->rcm);
+ }
+
+ /*
+ * If not currently in a map, log the cursor position,
+ * and set a flag so that this command can become the
+ * DOT command.
+ */
+ if (MAPPED_KEYS_WAITING(sp))
+ mapped = 1;
+ else {
+ if (log_cursor(sp, ep))
+ goto err;
+ mapped = 0;
+ }
+
+ /*
+ * We get a command, which may or may not have an associated
+ * motion. If it does, we get it too, calling its underlying
+ * function to get the resulting mark. We then call the
+ * command setting the cursor to the resulting mark.
+ */
+ if (getcmd(sp, ep, DOT, vp, NULL, &comcount, &mapped))
+ goto err;
+
+ /*
+ * Historical practice: if a dot command gets a new count,
+ * any motion component goes away, i.e. "d3w2." deletes a
+ * total of 5 words.
+ */
+ if (F_ISSET(vp, VC_ISDOT) && comcount)
+ DOTMOTION->count = 1;
+
+ /* Copy the key flags into the local structure. */
+ F_SET(vp, vp->kp->flags);
+
+ /* Get any associated keyword. */
+ if (F_ISSET(vp, V_KEYNUM | V_KEYW) &&
+ getkeyword(sp, ep, vp, vp->flags))
+ goto err;
+
+ /* Prepare to set the previous context. */
+ if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
+ abs.lno = sp->lno;
+ abs.cno = sp->cno;
+ }
+
+ /*
+ * Set the three cursor locations to the current cursor. The
+ * underlying routines don't bother if the cursor doesn't move.
+ * This also handles line commands (e.g. Y) defaulting to the
+ * current line.
+ */
+ vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
+ vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;
+
+ /*
+ * Do any required motion; getmotion sets the from MARK and the
+ * line mode flag. We save off the RCM mask and only restore
+ * it if it no RCM flags are set by the motion command. This
+ * means that the motion command is expected to determine where
+ * the cursor ends up!
+ */
+ if (F_ISSET(vp, V_MOTION)) {
+ flags = F_ISSET(vp, VM_RCM_MASK);
+ F_CLR(vp, VM_RCM_MASK);
+ if (getmotion(sp, ep, DOTMOTION, vp, &mapped))
+ goto err;
+ if (F_ISSET(vp, VM_NOMOTION))
+ goto err;
+ if (!F_ISSET(vp, VM_RCM_MASK))
+ F_SET(vp, flags);
+ }
+
+ /*
+ * If a count is set and the command is line oriented, set the
+ * to MARK here relative to the cursor/from MARK. This is for
+ * commands that take both counts and motions, i.e. "4yy" and
+ * "y%". As there's no way the command can know which the user
+ * did, we have to do it here. (There are commands that are
+ * line oriented and that take counts ("#G", "#H"), for which
+ * this calculation is either completely meaningless or wrong.
+ * Each command must validate the value for itself.
+ */
+ if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
+ vp->m_stop.lno += vp->count - 1;
+
+ /* Increment the command count. */
+ ++sp->ccnt;
+
+ /* Save the mode and call the function. */
+ saved_mode = F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE);
+ if ((vp->kp->func)(sp, ep, vp))
+ goto err;
+#ifdef DEBUG
+ /* Make sure no function left the temporary space locked. */
+ if (F_ISSET(sp->gp, G_TMP_INUSE)) {
+ msgq(sp, M_ERR,
+ "Error: vi: temporary buffer not released");
+ return (1);
+ }
+#endif
+ /*
+ * If that command took us out of vi or changed the screen,
+ * then exit the loop without further action.
+ */
+ if (saved_mode != F_ISSET(sp, S_SCREENS | S_MAJOR_CHANGE))
+ break;
+
+ /*
+ * Set the dot command structure.
+ *
+ * !!!
+ * Historically, no command which used any mapped keys became
+ * the dot command.
+ */
+ if (F_ISSET(vp, V_DOT) && !mapped) {
+ *DOT = cmd;
+ F_SET(DOT, VC_ISDOT);
+
+ /*
+ * If a count was supplied for both the command and
+ * its motion, the count was used only for the motion.
+ * Turn the count back on for the dot structure.
+ */
+ if (F_ISSET(vp, VC_C1RESET))
+ F_SET(DOT, VC_C1SET);
+
+ /* VM flags aren't retained. */
+ F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
+ }
+
+ /*
+ * Some vi row movements are "attracted" to the last position
+ * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
+ * commands' candle. It's broken into two parts. Here we deal
+ * with the command flags. In sp->relative(), we deal with the
+ * screen flags. If the movement is to the EOL the vi command
+ * handles it. If it's to the beginning, we handle it here.
+ *
+ * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
+ * flag, but do the work themselves. The reason is that they
+ * have to modify the column in case they're being used as a
+ * motion component. Other similar commands (e.g. +, -) don't
+ * have to modify the column because they are always line mode
+ * operations when used as motions, so the column number isn't
+ * of any interest.
+ *
+ * Does this totally violate the screen and editor layering?
+ * You betcha. As they say, if you think you understand it,
+ * you don't.
+ */
+ switch (F_ISSET(vp, VM_RCM_MASK)) {
+ case 0:
+ case VM_RCM_SET:
+ break;
+ case VM_RCM:
+ vp->m_final.cno = sp->s_rcm(sp, ep, vp->m_final.lno);
+ break;
+ case VM_RCM_SETLAST:
+ sp->rcm_last = 1;
+ break;
+ case VM_RCM_SETFNB:
+ vp->m_final.cno = 0;
+ /* FALLTHROUGH */
+ case VM_RCM_SETNNB:
+ if (nonblank(sp, ep, vp->m_final.lno, &vp->m_final.cno))
+ goto err;
+ break;
+ default:
+ abort();
+ }
+
+ /* Update the cursor. */
+ sp->lno = vp->m_final.lno;
+ sp->cno = vp->m_final.cno;
+
+ /*
+ * Set the absolute mark -- set even if a tags or similar
+ * command, since the tag may be moving to the same file.
+ */
+ if ((F_ISSET(vp, V_ABS) ||
+ F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno ||
+ F_ISSET(vp, V_ABS_C) &&
+ (sp->lno != abs.lno || sp->cno != abs.cno)) &&
+ mark_set(sp, ep, ABSMARK1, &abs, 1))
+ goto err;
+
+ if (!MAPPED_KEYS_WAITING(sp))
+ (void)msg_rpt(sp, 1);
+
+ /*
+ * Check and clear the interrupts. There's an obvious race,
+ * but it's not worth cleaning up. This is done after the
+ * err: lable, so that if the "error" was an interupt it gets
+ * cleaned up.
+ *
+ * !!!
+ * Previous versions of nvi cleared mapped characters on error,
+ * even if it wasn't an interrupt. This feature was removed as
+ * users complained that it wasn't historic practice and that
+ * they used leading (illegal) <escape> characters in the map
+ * to clean up vi state before the map was interpreted.
+ */
+err: if (INTERRUPTED(sp))
+ term_flush(sp, "Interrupted", CH_MAPPED);
+ CLR_INTERRUPT(sp);
+ }
+
+ /* Free allocated keyword memory. */
+ if (cmd.keyword != NULL)
+ free(cmd.keyword);
+
+ return (v_end(sp) || eval);
+}
+
+#define KEY(key, map) { \
+ if (getkey(sp, &ikey, map)) \
+ return (1); \
+ if (ikey.value == K_ESCAPE) \
+ goto esc; \
+ if (F_ISSET(&ikey, CH_MAPPED)) \
+ *mappedp = 1; \
+ key = ikey.ch; \
+}
+
+/*
+ * The O_TILDEOP option makes the ~ command take a motion instead
+ * of a straight count. This is the replacement structure we use
+ * instead of the one currently in the VIKEYS table.
+ *
+ * XXX
+ * Note, I used VC_Y instead of creating a new motion command, it's
+ * a lot easier.
+ */
+VIKEYS const tmotion = {
+ v_mulcase, V_CNT|V_DOT|V_MOTION|VC_Y|VM_RCM_SET,
+ "[count]~[count]motion",
+ " ~ change case to motion"
+};
+
+/*
+ * getcmd --
+ *
+ * The command structure for vi is less complex than ex (and don't think
+ * I'm not grateful!) The command syntax is:
+ *
+ * [count] [buffer] [count] key [[motion] | [buffer] [character]]
+ *
+ * and there are several special cases. The motion value is itself a vi
+ * command, with the syntax:
+ *
+ * [count] key [character]
+ */
+static int
+getcmd(sp, ep, dp, vp, ismotion, comcountp, mappedp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dp, *vp;
+ VICMDARG *ismotion; /* Previous key if getting motion component. */
+ int *comcountp, *mappedp;
+{
+ enum { COMMANDMODE, ISPARTIAL, NOTPARTIAL } cpart;
+ VIKEYS const *kp;
+ u_int flags;
+ CH ikey;
+ CHAR_T key;
+ char *s;
+
+ /* Refresh the command structure. */
+ memset(&vp->vp_startzero, 0,
+ (char *)&vp->vp_endzero - (char *)&vp->vp_startzero);
+
+ /*
+ * Get a key.
+ *
+ * <escape> cancels partial commands, i.e. a command where at least
+ * one non-numeric character has been entered. Otherwise, it beeps
+ * the terminal.
+ *
+ * !!!
+ * POSIX 1003.2-1992 explicitly disallows cancelling commands where
+ * all that's been entered is a number, requiring that the terminal
+ * be alerted.
+ */
+ cpart = ismotion == NULL ? COMMANDMODE : ISPARTIAL;
+ KEY(key, TXT_MAPCOMMAND);
+ if (ismotion == NULL)
+ cpart = NOTPARTIAL;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ cpart = ISPARTIAL;
+ if (ismotion != NULL) {
+ msgq(sp, M_BERR,
+ "Buffers should be specified before the command");
+ return (1);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /*
+ * Pick up optional count, where a leading 0 is not a count,
+ * it's a command.
+ */
+ if (isdigit(key) && key != '0') {
+ if (getcount(sp, key, &vp->count))
+ return (1);
+ F_SET(vp, VC_C1SET);
+ *comcountp = 1;
+
+ KEY(key, TXT_MAPCOMMAND);
+ } else
+ *comcountp = 0;
+
+ /* Pick up optional buffer. */
+ if (key == '"') {
+ cpart = ISPARTIAL;
+ if (F_ISSET(vp, VC_BUFFER)) {
+ msgq(sp, M_ERR, "Only one buffer can be specified");
+ return (1);
+ }
+ if (ismotion != NULL) {
+ msgq(sp, M_BERR,
+ "Buffers should be specified before the command");
+ return (1);
+ }
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+
+ KEY(key, TXT_MAPCOMMAND);
+ }
+
+ /* Check for an OOB command key. */
+ cpart = ISPARTIAL;
+ if (key > MAXVIKEY) {
+ msgq(sp, M_BERR, "%s isn't a vi command", KEY_NAME(sp, key));
+ return (1);
+ }
+ kp = &vikeys[vp->key = key];
+
+ /* The tildeop option makes the ~ command take a motion. */
+ if (key == '~' && O_ISSET(sp, O_TILDEOP))
+ kp = &tmotion;
+
+ vp->kp = kp;
+
+ /*
+ * Find the command. The only legal command with no underlying
+ * function is dot. It's historic practice that <escape> doesn't
+ * just erase the preceding number, it beeps the terminal as well.
+ * It's a common problem, so just beep the terminal unless verbose
+ * was set.
+ */
+ if (kp->func == NULL) {
+ if (key != '.') {
+ msgq(sp, ikey.value == K_ESCAPE ? M_BERR : M_ERR,
+ "%s isn't a vi command", KEY_NAME(sp, key));
+ return (1);
+ }
+
+ /* If called for a motion command, stop now. */
+ if (dp == NULL)
+ goto usage;
+
+ /* A repeatable command must have been executed. */
+ if (!F_ISSET(dp, VC_ISDOT)) {
+ msgq(sp, M_ERR, "No command to repeat");
+ return (1);
+ }
+
+ /*
+ * !!!
+ * If a '.' is immediately entered after an undo command, we
+ * replay the log instead of redoing the last command. This
+ * is necessary because 'u' can't set the dot command -- see
+ * vi/v_undo.c:v_undo for details.
+ */
+ if (VIP(sp)->u_ccnt == sp->ccnt) {
+ vp->kp = &vikeys['u'];
+ F_SET(vp, VC_ISDOT);
+ return (0);
+ }
+
+ /* Set new count/buffer, if any, and return. */
+ if (F_ISSET(vp, VC_C1SET)) {
+ F_SET(dp, VC_C1SET);
+ dp->count = vp->count;
+ }
+ if (F_ISSET(vp, VC_BUFFER))
+ dp->buffer = vp->buffer;
+ *vp = *dp;
+ return (0);
+ }
+
+ /* Set the flags based on the command flags. */
+ flags = kp->flags;
+
+ /* Check for illegal count. */
+ if (F_ISSET(vp, VC_C1SET) && !LF_ISSET(V_CNT))
+ goto usage;
+
+ /* Illegal motion command. */
+ if (ismotion == NULL) {
+ /* Illegal buffer. */
+ if (!LF_ISSET(V_OBUF) && F_ISSET(vp, VC_BUFFER))
+ goto usage;
+
+ /* Required buffer. */
+ if (LF_ISSET(V_RBUF)) {
+ KEY(vp->buffer, 0);
+ F_SET(vp, VC_BUFFER);
+ }
+ }
+
+ /*
+ * Special case: '[', ']' and 'Z' commands. Doesn't the fact that
+ * the *single* characters don't mean anything but the *doubled*
+ * characters do just frost your shorts?
+ */
+ if (vp->key == '[' || vp->key == ']' || vp->key == 'Z') {
+ /*
+ * Historically, half entered [[, ]] or Z commands weren't
+ * cancelled by <escape>, the terminal was beeped instead.
+ * POSIX.2-1992 probably didn't notice, and requires that
+ * they be cancelled instead of beeping. Seems fine to me.
+ */
+ KEY(key, TXT_MAPCOMMAND);
+
+ if (vp->key != key) {
+usage: if (ismotion == NULL)
+ s = kp->usage;
+ else if (ismotion->key == '~' && O_ISSET(sp, O_TILDEOP))
+ s = tmotion.usage;
+ else
+ s = vikeys[ismotion->key].usage;
+ msgq(sp, M_ERR, "Usage: %s", s);
+ return (1);
+ }
+ }
+ /* Special case: 'z' command. */
+ if (vp->key == 'z') {
+ KEY(vp->character, 0);
+ if (isdigit(vp->character)) {
+ if (getcount(sp, vp->character, &vp->count2))
+ return (1);
+ F_SET(vp, VC_C2SET);
+ KEY(vp->character, 0);
+ }
+ }
+
+ /*
+ * Commands that have motion components can be doubled to
+ * imply the current line.
+ */
+ if (ismotion != NULL && ismotion->key != key && !LF_ISSET(V_MOVE)) {
+ msgq(sp, M_ERR, "%s may not be used as a motion command",
+ KEY_NAME(sp, key));
+ return (1);
+ }
+
+ /* Required character. */
+ if (LF_ISSET(V_CHAR))
+ KEY(vp->character, 0);
+
+ return (0);
+
+esc: switch (cpart) {
+ case COMMANDMODE:
+ msgq(sp, M_BERR, "Already in command mode");
+ break;
+ case ISPARTIAL:
+ break;
+ case NOTPARTIAL:
+ (void)sp->s_bell(sp);
+ break;
+ }
+ return (1);
+}
+
+/*
+ * getmotion --
+ *
+ * Get resulting motion mark.
+ */
+static int
+getmotion(sp, ep, dm, vp, mappedp)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *dm, *vp;
+ int *mappedp;
+{
+ MARK m;
+ VICMDARG motion;
+ size_t len;
+ u_long cnt;
+ int notused;
+
+ /*
+ * If '.' command, use the dot motion, else get the motion command.
+ * Clear any line motion flags, the subsequent motion isn't always
+ * the same, i.e. "/aaa" may or may not be a line motion.
+ */
+ if (F_ISSET(vp, VC_ISDOT)) {
+ motion = *dm;
+ F_SET(&motion, VC_ISDOT);
+ F_CLR(&motion, VM_COMMASK);
+ } else if (getcmd(sp, ep, NULL, &motion, vp, &notused, mappedp))
+ return (1);
+
+ /*
+ * A count may be provided both to the command and to the motion, in
+ * which case the count is multiplicative. For example, "3y4y" is the
+ * same as "12yy". This count is provided to the motion command and
+ * not to the regular function.
+ */
+ cnt = motion.count = F_ISSET(&motion, VC_C1SET) ? motion.count : 1;
+ if (F_ISSET(vp, VC_C1SET)) {
+ motion.count *= vp->count;
+ F_SET(&motion, VC_C1SET);
+
+ /*
+ * Set flags to restore the original values of the command
+ * structure so dot commands can change the count values,
+ * e.g. "2dw" "3." deletes a total of five words.
+ */
+ F_CLR(vp, VC_C1SET);
+ F_SET(vp, VC_C1RESET);
+ }
+
+ /*
+ * Some commands can be repeated to indicate the current line. In
+ * this case, or if the command is a "line command", set the flags
+ * appropriately. If not a doubled command, run the function to get
+ * the resulting mark.
+ */
+ if (vp->key == motion.key) {
+ F_SET(vp, VM_LDOUBLE | VM_LMODE);
+
+ /* Set the origin of the command. */
+ vp->m_start.lno = sp->lno;
+ vp->m_start.cno = 0;
+
+ /*
+ * Set the end of the command.
+ *
+ * If the current line is missing, i.e. the file is empty,
+ * historic vi permitted a "cc" or "!!" command to insert
+ * text.
+ */
+ vp->m_stop.lno = sp->lno + motion.count - 1;
+ if (file_gline(sp, ep, vp->m_stop.lno, &len) == NULL) {
+ if (vp->m_stop.lno != 1 ||
+ vp->key != 'c' && vp->key != '!') {
+ m.lno = sp->lno;
+ m.cno = sp->cno;
+ v_eof(sp, ep, &m);
+ return (1);
+ }
+ vp->m_stop.cno = 0;
+ } else
+ vp->m_stop.cno = len ? len - 1 : 0;
+ } else {
+ /*
+ * Motion commands change the underlying movement (*snarl*).
+ * For example, "l" is illegal at the end of a line, but "dl"
+ * is not. Set flags so the function knows the situation.
+ */
+ F_SET(&motion, vp->kp->flags & VC_COMMASK);
+
+ /*
+ * Copy the key flags into the local structure, except for
+ * the RCM flags, the motion command will set the RCM flags
+ * in the vp structure as necessary.
+ */
+ F_SET(&motion, motion.kp->flags & ~VM_RCM_MASK);
+
+ /*
+ * Set the three cursor locations to the current cursor. This
+ * permits commands like 'j' and 'k', that are line oriented
+ * motions and have special cursor suck semantics when they are
+ * used as standalone commands, to ignore column positioning.
+ */
+ motion.m_final.lno =
+ motion.m_stop.lno = motion.m_start.lno = sp->lno;
+ motion.m_final.cno =
+ motion.m_stop.cno = motion.m_start.cno = sp->cno;
+
+ /* Run the function. */
+ if ((motion.kp->func)(sp, ep, &motion))
+ return (1);
+
+ /*
+ * Copy cut buffer, line mode and cursor position information
+ * from the motion command structure, i.e. anything that the
+ * motion command can set for us. The commands can flag the
+ * movement as a line motion (see v_sentence) as well as set
+ * the VM_RCM_* flags explicitly.
+ */
+ F_SET(vp, F_ISSET(&motion, VM_COMMASK | VM_RCM_MASK));
+
+ /*
+ * Motion commands can reset all of the cursor information.
+ * If the motion is in the reverse direction, switch the
+ * from and to MARK's so that it's in a forward direction.
+ * Motions are from the from MARK to the to MARK (inclusive).
+ */
+ if (motion.m_start.lno > motion.m_stop.lno ||
+ motion.m_start.lno == motion.m_stop.lno &&
+ motion.m_start.cno > motion.m_stop.cno) {
+ vp->m_start = motion.m_stop;
+ vp->m_stop = motion.m_start;
+ } else {
+ vp->m_start = motion.m_start;
+ vp->m_stop = motion.m_stop;
+ }
+ vp->m_final = motion.m_final;
+ }
+
+ /*
+ * If the command sets dot, save the motion structure. The motion
+ * count was changed above and needs to be reset, that's why this
+ * is done here, and not in the calling routine.
+ */
+ if (F_ISSET(vp->kp, V_DOT)) {
+ *dm = motion;
+ dm->count = cnt;
+ }
+ return (0);
+}
+
+#define innum(c) (isdigit(c) || strchr("abcdefABCDEF", c))
+
+/*
+ * getkeyword --
+ * Get the "word" the cursor is on.
+ */
+static int
+getkeyword(sp, ep, kp, flags)
+ SCR *sp;
+ EXF *ep;
+ VICMDARG *kp;
+ u_int flags;
+{
+ recno_t lno;
+ size_t beg, end, len;
+ char *p;
+
+ if ((p = file_gline(sp, ep, sp->lno, &len)) == NULL) {
+ if (file_lline(sp, ep, &lno))
+ return (1);
+ if (lno == 0)
+ v_eof(sp, ep, NULL);
+ else
+ GETLINE_ERR(sp, sp->lno);
+ return (1);
+ }
+
+ /*
+ * !!!
+ * Historically, tag commands skipped over any leading whitespace
+ * characters.
+ */
+ for (beg = sp->cno; beg < len && isspace(p[beg]); ++beg);
+
+ if (beg >= len ||
+ LF_ISSET(V_KEYW) && !inword(p[beg]) ||
+ LF_ISSET(V_KEYNUM) && !innum(p[beg]) &&
+ p[beg] != '-' && p[beg] != '+')
+ goto noword;
+
+ /*
+ * !!!
+ * Find the beginning/end of the keyword. Keywords (V_KEYW) are
+ * used for cursor-word searching and for tags. Historical vi
+ * only used the word in a tag search from the cursor to the end
+ * of the word, i.e. if the cursor was on the 'b' in " abc ", the
+ * tag was "bc". For no particular reason, we make the cursor
+ * word searches follow the same rule.
+ */
+ if (beg != 0)
+ if (LF_ISSET(V_KEYW)) {
+#ifdef MOVE_TO_KEYWORD_BEGINNING
+ for (;;) {
+ --beg;
+ if (!inword(p[beg])) {
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+#endif
+ } else {
+ for (;;) {
+ --beg;
+ if (!innum(p[beg])) {
+ if (beg > 0 && p[beg - 1] == '0' &&
+ (p[beg] == 'X' || p[beg] == 'x'))
+ --beg;
+ else
+ ++beg;
+ break;
+ }
+ if (beg == 0)
+ break;
+ }
+
+ /* Skip possible leading sign. */
+ if (beg != 0 && p[beg] != '0' &&
+ (p[beg - 1] == '+' || p[beg - 1] == '-'))
+ --beg;
+ }
+
+ if (LF_ISSET(V_KEYW)) {
+ for (end = beg; ++end < len && inword(p[end]););
+ --end;
+ } else {
+ for (end = beg; ++end < len;) {
+ if (p[end] == 'X' || p[end] == 'x') {
+ if (end != beg + 1 || p[beg] != '0')
+ break;
+ continue;
+ }
+ if (!innum(p[end]))
+ break;
+ }
+
+ /* Just a sign isn't a number. */
+ if (end == beg && (p[beg] == '+' || p[beg] == '-')) {
+noword: msgq(sp, M_BERR, "Cursor not in a %s",
+ LF_ISSET(V_KEYW) ? "word" : "number");
+ return (1);
+ }
+ --end;
+ }
+
+ /*
+ * Getting a keyword implies moving the cursor to its beginning.
+ * Refresh now.
+ */
+ if (beg != sp->cno) {
+ sp->cno = beg;
+ sp->s_refresh(sp, ep);
+ }
+
+ /*
+ * XXX
+ * 8-bit clean problem. Numeric keywords are handled using strtol(3)
+ * and friends. This would have to be fixed in v_increment and here
+ * to not depend on a trailing NULL.
+ */
+ len = (end - beg) + 2; /* XXX */
+ kp->klen = (end - beg) + 1;
+ BINC_RET(sp, kp->keyword, kp->kbuflen, len);
+ memmove(kp->keyword, p + beg, kp->klen);
+ kp->keyword[kp->klen] = '\0'; /* XXX */
+ return (0);
+}
+
+/*
+ * getcount --
+ * Return the next count.
+ */
+static __inline int
+getcount(sp, fkey, countp)
+ SCR *sp;
+ ARG_CHAR_T fkey;
+ u_long *countp;
+{
+ u_long count, tc;
+ CH ikey;
+
+ ikey.ch = fkey;
+ count = tc = 0;
+ do {
+ /* Assume that overflow results in a smaller number. */
+ tc = count * 10 + ikey.ch - '0';
+ if (count > tc) {
+ /* Toss to the next non-digit. */
+ do {
+ if (getkey(sp, &ikey,
+ TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ msgq(sp, M_ERR, "Number larger than %lu", ULONG_MAX);
+ return (1);
+ }
+ count = tc;
+ if (getkey(sp, &ikey, TXT_MAPCOMMAND | TXT_MAPNODIGIT))
+ return (1);
+ } while (isdigit(ikey.ch));
+ *countp = count;
+ return (0);
+}
+
+/*
+ * getkey --
+ * Return the next key.
+ */
+static __inline int
+getkey(sp, ikeyp, map)
+ SCR *sp;
+ CH *ikeyp;
+ u_int map;
+{
+ switch (term_key(sp, ikeyp, map)) {
+ case INP_EOF:
+ case INP_ERR:
+ F_SET(sp, S_EXIT_FORCE);
+ return (1);
+ case INP_INTR:
+ /*
+ * !!!
+ * Historically, vi beeped on command level interrupts.
+ *
+ * Historically, vi exited to ex mode if no file was named
+ * on the command line, and two interrupts were generated
+ * in a row. (Just figured you might want to know that.)
+ */
+ (void)sp->s_bell(sp);
+ return (1);
+ case INP_OK:
+ return (0);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.bin/vi/xaw/xaw_screen.c b/usr.bin/vi/xaw/xaw_screen.c
new file mode 100644
index 0000000..0e78619
--- /dev/null
+++ b/usr.bin/vi/xaw/xaw_screen.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)xaw_screen.c 8.8 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+
+/*
+ * xaw_init --
+ * Athena widget screen initialization.
+ */
+int
+xaw_screen_init(sp)
+ SCR *sp;
+{
+ msgq(sp, M_ERR, "The Athena widget screen not yet implemented");
+ return (1);
+}
+
+/*
+ * xaw_screen_copy --
+ * Copy to a new screen.
+ */
+int
+xaw_screen_copy(orig, sp)
+ SCR *orig, *sp;
+{
+ return (0);
+}
+
+/*
+ * xaw_screen_end --
+ * End a screen.
+ */
+int
+xaw_screen_end(sp)
+ SCR *sp;
+{
+ return (0);
+}
+
+/*
+ * xaw --
+ * Main vi Athena widget screen loop.
+ */
+int
+xaw(sp, ep, spp)
+ SCR *sp, **spp;
+ EXF *ep;
+{
+ *spp = NULL;
+ return (0);
+}
diff --git a/usr.bin/vis/Makefile b/usr.bin/vis/Makefile
new file mode 100644
index 0000000..8f83a14
--- /dev/null
+++ b/usr.bin/vis/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= vis
+SRCS= vis.c foldit.c
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vis/foldit.c b/usr.bin/vis/foldit.c
new file mode 100644
index 0000000..86f360c
--- /dev/null
+++ b/usr.bin/vis/foldit.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)foldit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+foldit(chunk, col, max)
+ char *chunk;
+{
+ register char *cp;
+
+ /*
+ * Keep track of column position. Insert hidden newline
+ * if this chunk puts us over the limit.
+ */
+again:
+ cp = chunk;
+ while (*cp) {
+ switch(*cp) {
+ case '\n':
+ case '\r':
+ col = 0;
+ break;
+ case '\t':
+ col = col + 8 &~ 07;
+ break;
+ case '\b':
+ col = col ? col - 1 : 0;
+ break;
+ default:
+ col++;
+ }
+ if (col > (max - 2)) {
+ printf("\\\n");
+ col = 0;
+ goto again;
+ }
+ cp++;
+ }
+ return (col);
+}
diff --git a/usr.bin/vis/vis.1 b/usr.bin/vis/vis.1
new file mode 100644
index 0000000..784e56c
--- /dev/null
+++ b/usr.bin/vis/vis.1
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1989, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vis.1 8.4 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt VIS 1
+.Os BSD 4.4
+.Sh NAME
+.Nm vis
+.Nd display non-printable characters in a visual format
+.Sh SYNOPSIS
+.Nm vis
+.Op Fl cbflnostw
+.Op Fl F Ar foldwidth
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Vis
+is a filter for converting non-printable characters
+into a visual representation. It differs from
+.Ql cat -v
+in that
+the form is unique and invertible. By default, all non-graphic
+characters except space, tab, and newline are encoded.
+A detailed description of the
+various visual formats is given in
+.Xr vis 3 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Turns off prepending of backslash before up-arrow control sequences
+and meta characters, and disables the doubling of backslashes. This
+produces output which is neither invertible or precise, but does
+represent a minimum of change to the input. It is similar to
+.Dq Li cat -v .
+.It Fl c
+Request a format which displays a small subset of the
+non-printable characters using C-style backslash sequences.
+.It Fl F
+Causes
+.Nm vis
+to fold output lines to foldwidth columns (default 80), like
+.Xr fold 1 ,
+except
+that a hidden newline sequence is used, (which is removed
+when inverting the file back to its original form with
+.Xr unvis 1 ) .
+If the last character in the encoded file does not end in a newline,
+a hidden newline sequence is appended to the output.
+This makes
+the output usable with various editors and other utilities which
+typically don't work with partial lines.
+.It Fl f
+Same as
+.Fl F .
+.It Fl l
+Mark newlines with the visible sequence
+.Ql \e$ ,
+followed by the newline.
+.It Fl n
+Turns off any encoding, except for the fact that backslashes are
+still doubled and hidden newline sequences inserted if
+.Fl f
+or
+.Fl F
+is selected. When combined with the
+.Fl f
+flag,
+.Nm vis
+becomes like
+an invertible version of the
+.Xr fold 1
+utility. That is, the output
+can be unfolded by running the output through
+.It Fl o
+Request a format which displays non-printable characters as
+an octal number, \eddd.
+.It Fl s
+Only characters considered unsafe to send to a terminal are encoded.
+This flag allows backspace, bell, and carriage return in addition
+to the default space, tab and newline.
+.Xr unvis 1 .
+.It Fl t
+Tabs are also encoded.
+.It Fl w
+White space (space-tab-newline) is also encoded.
+.El
+.Sh SEE ALSO
+.Xr unvis 1 ,
+.Xr vis 3
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/vis/vis.c b/usr.bin/vis/vis.c
new file mode 100644
index 0000000..1706801
--- /dev/null
+++ b/usr.bin/vis/vis.c
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)vis.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <vis.h>
+
+int eflags, fold, foldwidth=80, none, markeol, debug;
+
+main(argc, argv)
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ extern int errno;
+ FILE *fp;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "nwctsobfF:ld")) != EOF)
+ switch((char)ch) {
+ case 'n':
+ none++;
+ break;
+ case 'w':
+ eflags |= VIS_WHITE;
+ break;
+ case 'c':
+ eflags |= VIS_CSTYLE;
+ break;
+ case 't':
+ eflags |= VIS_TAB;
+ break;
+ case 's':
+ eflags |= VIS_SAFE;
+ break;
+ case 'o':
+ eflags |= VIS_OCTAL;
+ break;
+ case 'b':
+ eflags |= VIS_NOSLASH;
+ break;
+ case 'F':
+ if ((foldwidth = atoi(optarg))<5) {
+ fprintf(stderr,
+ "vis: can't fold lines to less than 5 cols\n");
+ exit(1);
+ }
+ /*FALLTHROUGH*/
+ case 'f':
+ fold++; /* fold output lines to 80 cols */
+ break; /* using hidden newline */
+ case 'l':
+ markeol++; /* mark end of line with \$ */
+ break;
+#ifdef DEBUG
+ case 'd':
+ debug++;
+ break;
+#endif
+ case '?':
+ default:
+ fprintf(stderr,
+ "usage: vis [-nwctsobf] [-F foldwidth]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (*argv)
+ while (*argv) {
+ if ((fp=fopen(*argv, "r")) != NULL)
+ process(fp, *argv);
+ else
+ fprintf(stderr, "vis: %s: %s\n", *argv,
+ (char *)strerror(errno));
+ argv++;
+ }
+ else
+ process(stdin, "<stdin>");
+ exit(0);
+}
+
+process(fp, filename)
+ FILE *fp;
+ char *filename;
+{
+ static int col = 0;
+ register char *cp = "\0"+1; /* so *(cp-1) starts out != '\n' */
+ register int c, rachar;
+ register char nc;
+ char buff[5];
+
+ c = getc(fp);
+ while (c != EOF) {
+ rachar = getc(fp);
+ if (none) {
+ cp = buff;
+ *cp++ = c;
+ if (c == '\\')
+ *cp++ = '\\';
+ *cp = '\0';
+ } else if (markeol && c == '\n') {
+ cp = buff;
+ if ((eflags & VIS_NOSLASH) == 0)
+ *cp++ = '\\';
+ *cp++ = '$';
+ *cp++ = '\n';
+ *cp = '\0';
+ } else
+ (void) vis(buff, (char)c, eflags, (char)rachar);
+
+ cp = buff;
+ if (fold) {
+#ifdef DEBUG
+ if (debug)
+ printf("<%02d,", col);
+#endif
+ col = foldit(cp, col, foldwidth);
+#ifdef DEBUG
+ if (debug)
+ printf("%02d>", col);
+#endif
+ }
+ do {
+ putchar(*cp);
+ } while (*++cp);
+ c = rachar;
+ }
+ /*
+ * terminate partial line with a hidden newline
+ */
+ if (fold && *(cp-1) != '\n')
+ printf("\\\n");
+}
diff --git a/usr.bin/vmstat/Makefile b/usr.bin/vmstat/Makefile
new file mode 100644
index 0000000..b082cb6
--- /dev/null
+++ b/usr.bin/vmstat/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= vmstat
+CFLAGS+=-I${.CURDIR}/../../sys
+MAN8= vmstat.8
+BINGRP= kmem
+BINMODE=2555
+DPADD= names.c ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/vmstat/names.c b/usr.bin/vmstat/names.c
new file mode 100644
index 0000000..90c95f9
--- /dev/null
+++ b/usr.bin/vmstat/names.c
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)names.c 8.1 (Berkeley) 6/6/93
+ */
+
+#if !defined(hp300) && !defined(tahoe) && !defined(vax) && \
+ !defined(luna68k) && !defined(mips) && !defined(i386)
+char *defdrives[] = { 0 };
+#endif
+
+#if defined(i386)
+/*
+ * i386 support added by Rodney W. Grimes.
+ */
+#include <i386/isa/isa_device.h>
+
+char *defdrives[] = { "wd0", "wd1", "sd0", "sd1" };
+
+int
+read_names()
+{
+ u_long dk_names;
+ static char thenames[DK_NDRIVE][DK_NAMELEN];
+ int i;
+
+ dk_names = namelist[X_DK_NAMES].n_value;
+ if (dk_names == 0) {
+ warnx("disk name info not in namelist");
+ return(0);
+ }
+
+ kvm_read(kd, dk_names, thenames, sizeof thenames);
+ for(i = 0; thenames[i][0]; i++) {
+ dr_name[i] = thenames[i];
+ }
+ return(1);
+}
+#endif /* i386 */
+
+#if defined(hp300) || defined(luna68k)
+#if defined(hp300)
+#include <hp/dev/device.h>
+#else
+#include <luna68k/dev/device.h>
+#endif
+
+char *defdrives[] = { "sd0", "sd1", "sd2", "rd0", "rd1", "rd2", 0 };
+
+int
+read_names()
+{
+ register char *p;
+ register u_long hp;
+ static char buf[BUFSIZ];
+ struct hp_device hdev;
+ struct driver hdrv;
+ char name[10];
+
+ hp = namelist[X_HPDINIT].n_value;
+ if (hp == 0) {
+ (void)fprintf(stderr,
+ "disk init info not in namelist\n");
+ return (0);
+ }
+ p = buf;
+ for (;; hp += sizeof hdev) {
+ (void)kvm_read(kd, hp, &hdev, sizeof hdev);
+ if (hdev.hp_driver == 0)
+ break;
+ if (hdev.hp_dk < 0 || hdev.hp_alive == 0 ||
+ hdev.hp_cdriver == 0)
+ continue;
+ (void)kvm_read(kd, (u_long)hdev.hp_driver, &hdrv, sizeof hdrv);
+ (void)kvm_read(kd, (u_long)hdrv.d_name, name, sizeof name);
+ dr_name[hdev.hp_dk] = p;
+ p += sprintf(p, "%s%d", name, hdev.hp_unit) + 1;
+ }
+ return (1);
+}
+#endif /* hp300 || luna68k */
+
+#ifdef tahoe
+#include <tahoe/vba/vbavar.h>
+
+char *defdrives[] = { "dk0", "dk1", "dk2", 0 };
+
+int
+read_names()
+{
+ register char *p;
+ struct vba_device udev, *up;
+ struct vba_driver udrv;
+ char name[10];
+ static char buf[BUFSIZ];
+
+ up = (struct vba_device *)namelist[X_VBDINIT].n_value;
+ if (up == 0) {
+ (void) fprintf(stderr,
+ "disk init info not in namelist\n");
+ return (0);
+ }
+ p = buf;
+ for (;; up += sizeof udev) {
+ (void)kvm_read(kd, up, &udev, sizeof udev);
+ if (udev.ui_driver == 0)
+ break;
+ if (udev.ui_dk < 0 || udev.ui_alive == 0)
+ continue;
+ (void)kvm_read(kd, udev.ui_driver, &udrv, sizeof udrv);
+ (void)kvm_read(kd, udrv.ud_dname, name, sizeof name);
+ dr_name[udev.ui_dk] = p;
+ p += sprintf(p, "%s%d", name, udev.ui_unit);
+ }
+ return (1);
+}
+#endif /* tahoe */
+
+#ifdef vax
+#include <vax/uba/ubavar.h>
+#include <vax/mba/mbavar.h>
+
+char *defdrives[] = { "hp0", "hp1", "hp2", 0 };
+
+int
+read_names()
+{
+ register char *p;
+ unsigned long mp, up;
+ struct mba_device mdev;
+ struct mba_driver mdrv;
+ struct uba_device udev;
+ struct uba_driver udrv;
+ char name[10];
+ static char buf[BUFSIZ];
+
+ mp = namelist[X_MBDINIT].n_value;
+ up = namelist[X_UBDINIT].n_value;
+ if (mp == 0 && up == 0) {
+ (void)fprintf(stderr,
+ "disk init info not in namelist\n");
+ return (0);
+ }
+ p = buf;
+ if (mp)
+ for (;; mp += sizeof mdev) {
+ (void)kvm_read(kd, mp, &mdev, sizeof mdev);
+ if (mdev.mi_driver == 0)
+ break;
+ if (mdev.mi_dk < 0 || mdev.mi_alive == 0)
+ continue;
+ (void)kvm_read(kd, mdev.mi_driver, &mdrv, sizeof mdrv);
+ (void)kvm_rea(kd, mdrv.md_dname, name, sizeof name);
+ dr_name[mdev.mi_dk] = p;
+ p += sprintf(p, "%s%d", name, mdev.mi_unit);
+ }
+ if (up)
+ for (;; up += sizeof udev) {
+ (void)kvm_read(kd, up, &udev, sizeof udev);
+ if (udev.ui_driver == 0)
+ break;
+ if (udev.ui_dk < 0 || udev.ui_alive == 0)
+ continue;
+ (void)kvm_read(kd, udev.ui_driver, &udrv, sizeof udrv);
+ (void)kvm_read(kd, udrv.ud_dname, name, sizeof name);
+ dr_name[udev.ui_dk] = p;
+ p += sprintf(p, "%s%d", name, udev.ui_unit);
+ }
+ return (1);
+}
+#endif /* vax */
+
+#ifdef sun
+#include <sundev/mbvar.h>
+
+int
+read_names()
+{
+ static int once = 0;
+ struct mb_device mdev;
+ struct mb_driver mdrv;
+ short two_char;
+ char *cp = (char *) &two_char;
+ register struct mb_device *mp;
+
+ mp = (struct mb_device *)namelist[X_MBDINIT].n_value;
+ if (mp == 0) {
+ (void)fprintf(stderr,
+ "disk init info not in namelist\n");
+ return (0);
+ }
+ for (;; ++mp) {
+ (void)kvm_read(kd, mp++, &mdev, sizeof(mdev));
+ if (mdev.md_driver == 0)
+ break;
+ if (mdev.md_dk < 0 || mdev.md_alive == 0)
+ continue;
+ (void)kvm_read(kd, mdev.md_driver, &mdrv, sizeof(mdrv));
+ (void)kvm_read(kd, mdrv.mdr_dname, &two_char, sizeof(two_char));
+ (void)sprintf(dr_name[mdev.md_dk],
+ "%c%c%d", cp[0], cp[1], mdev.md_unit);
+ }
+ return(1);
+}
+#endif /* sun */
+
+#if defined(mips)
+#include <pmax/dev/device.h>
+
+char *defdrives[] = { "rz0", "rz1", "rz2", "rz3", "rz4", "rz5", "rz6", 0 };
+
+int
+read_names()
+{
+ register char *p;
+ register u_long sp;
+ static char buf[BUFSIZ];
+ struct scsi_device sdev;
+ struct driver hdrv;
+ char name[10];
+
+ sp = namelist[X_SCSI_DINIT].n_value;
+ if (sp == 0) {
+ (void)fprintf(stderr, "disk init info not in namelist\n");
+ return (0);
+ }
+ p = buf;
+ for (;; sp += sizeof sdev) {
+ (void)kvm_read(kd, sp, &sdev, sizeof sdev);
+ if (sdev.sd_driver == 0)
+ break;
+ if (sdev.sd_dk < 0 || sdev.sd_alive == 0 ||
+ sdev.sd_cdriver == 0)
+ continue;
+ (void)kvm_read(kd, (u_long)sdev.sd_driver, &hdrv, sizeof hdrv);
+ (void)kvm_read(kd, (u_long)hdrv.d_name, name, sizeof name);
+ dr_name[sdev.sd_dk] = p;
+ p += sprintf(p, "%s%d", name, sdev.sd_unit) + 1;
+ }
+ return (1);
+}
+#endif /* mips */
diff --git a/usr.bin/vmstat/vmstat.8 b/usr.bin/vmstat/vmstat.8
new file mode 100644
index 0000000..4c21b9a
--- /dev/null
+++ b/usr.bin/vmstat/vmstat.8
@@ -0,0 +1,206 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vmstat.8 8.1 (Berkeley) 6/6/93
+.\"
+.TH VMSTAT 8 "June 6, 1993"
+.UC 4
+.SH NAME
+vmstat \- report virtual memory statistics
+.SH SYNOPSIS
+.nf
+.ft B
+vmstat [ \-fimst ] [ \-c count ] [ \-M core ] [ \-N system ]
+.ti +5
+[ \-w wait ] [ disks ]
+.ft R
+.fi
+.SH DESCRIPTION
+.I Vmstat
+reports certain kernel statistics kept about process, virtual memory,
+disk, trap and cpu activity.
+.PP
+The options are as follows:
+.TP
+\-c
+Repeat the display
+.I count
+times.
+The first display is for the time since a reboot and each subsequent report
+is for the time period since the last display.
+If no
+.I wait
+interval is specified, the default is 1 second.
+.TP
+\-f
+Report on the number
+.IR fork (2)
+and
+.IR vfork (2)
+system calls since system startup, and the number of pages of virtual memory
+involved in each.
+.TP
+\-i
+Report on the number of interrupts taken by each device since system
+startup.
+.TP
+\-M
+Extract values associated with the name list from the specified core
+instead of the default ``/dev/kmem''.
+.TP
+\-N
+Extract the name list from the specified system instead of the default
+``/kernel''.
+.TP
+\-m
+Report on the usage of kernel dynamic memory listed first by size of
+allocation and then by type of usage.
+.TP
+\-s
+Display the contents of the
+.I sum
+structure, giving the total number of several kinds of paging related
+events which have occurred since system startup.
+.TP
+\-t
+Report on the number of page in and page reclaims since system startup,
+and the amount of time required by each.
+.TP
+\-w
+Pause
+.I wait
+seconds between each display.
+If no repeat
+.I count
+is specified, the default is infinity.
+.PP
+By default,
+.I vmstat
+displays the following information:
+.PP
+.TP
+procs
+Information about the numbers of processes in various states.
+.sp
+.RS
+.nf
+r in run queue
+b blocked for resources (i/o, paging, etc.)
+w runnable or short sleeper (< 20 secs) but swapped
+.fi
+.RE
+.TP
+memory
+Information about the usage of virtual and real memory.
+Virtual pages (reported in units of 1024 bytes) are considered active if
+they belong to processes which are running or have run in the last 20
+seconds.
+.sp
+.RS
+.nf
+avm active virtual pages
+fre size of the free list
+.fi
+.RE
+.TP
+page
+Information about page faults and paging activity.
+These are averaged each five seconds, and given in units per second.
+.sp
+.RS
+.nf
+re page reclaims (simulating reference bits)
+at pages attached (found in free list)
+pi pages paged in
+po pages paged out
+fr pages freed per second
+de anticipated short term memory shortfall
+sr pages scanned by clock algorithm, per-second
+.fi
+.RE
+.TP
+disks
+Disk operations per second (this field is system dependent).
+Typically paging will be split across the available drives.
+The header of the field is the first character of the disk name and
+the unit number.
+If more than four disk drives are configured in the system,
+.I vmstat
+displays only the first four drives.
+To force
+.I vmstat
+to display specific drives, their names may be supplied on the command line.
+.TP
+faults
+Trap/interrupt rate averages per second over last 5 seconds.
+.sp
+.RS
+.nf
+in device interrupts per interval (including clock interrupts)
+sy system calls per interval
+cs cpu context switch rate (switches/interval)
+.fi
+.RE
+.TP
+cpu
+Breakdown of percentage usage of CPU time.
+.sp
+.RS
+.nf
+us user time for normal and low priority processes
+sy system time
+id cpu idle
+.fi
+.RE
+.SH EXAMPLES
+The command ``vmstat -i 5'' will print what the system is doing every five
+seconds; this is a good choice of printing interval since this is how often
+some of the statistics are sampled in the system.
+Others vary every second and running the output for a while will make it
+apparent which are recomputed every second.
+.SH FILES
+.ta \w'/dev/kmem 'u
+/kernel default kernel namelist
+.br
+/dev/kmem default memory file
+.SH SEE ALSO
+.IR fstat (1),
+.IR netstat (1),
+.IR nfsstat (1),
+.IR ps (1),
+.IR systat (1),
+.IR iostat (8),
+.IR pstat (8)
+.sp
+The sections starting with ``Interpreting system activity'' in
+.IR "Installing and Operating 4.3BSD" .
+.SH BUGS
+The \-c and \-w options are only available with the default output.
diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c
new file mode 100644
index 0000000..8776fc2
--- /dev/null
+++ b/usr.bin/vmstat/vmstat.c
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 1980, 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)vmstat.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <vm/vm.h>
+#include <time.h>
+#include <nlist.h>
+#include <kvm.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+#include <limits.h>
+
+struct nlist namelist[] = {
+#define X_CPTIME 0
+ { "_cp_time" },
+#define X_DK_NDRIVE 1
+ { "_dk_ndrive" },
+#define X_SUM 2
+ { "_cnt" },
+#define X_BOOTTIME 3
+ { "_boottime" },
+#define X_DKXFER 4
+ { "_dk_xfer" },
+#define X_HZ 5
+ { "_hz" },
+#define X_STATHZ 6
+ { "_stathz" },
+#define X_NCHSTATS 7
+ { "_nchstats" },
+#define X_INTRNAMES 8
+ { "_intrnames" },
+#define X_EINTRNAMES 9
+ { "_eintrnames" },
+#define X_INTRCNT 10
+ { "_intrcnt" },
+#define X_EINTRCNT 11
+ { "_eintrcnt" },
+#define X_KMEMSTAT 12
+ { "_kmemstats" },
+#define X_KMEMBUCKETS 13
+ { "_bucket" },
+#ifdef notdef
+#define X_DEFICIT 14
+ { "_deficit" },
+#define X_FORKSTAT 15
+ { "_forkstat" },
+#define X_REC 16
+ { "_rectime" },
+#define X_PGIN 17
+ { "_pgintime" },
+#define X_XSTATS 18
+ { "_xstats" },
+#define X_END 19
+#else
+#define X_END 14
+#endif
+#if defined(hp300) || defined(luna68k)
+#define X_HPDINIT (X_END)
+ { "_hp_dinit" },
+#endif
+#if defined(i386)
+#define X_DK_NAMES (X_END)
+ { "_dk_names" },
+#endif
+#ifdef mips
+#define X_SCSI_DINIT (X_END)
+ { "_scsi_dinit" },
+#endif
+#ifdef tahoe
+#define X_VBDINIT (X_END)
+ { "_vbdinit" },
+#define X_CKEYSTATS (X_END+1)
+ { "_ckeystats" },
+#define X_DKEYSTATS (X_END+2)
+ { "_dkeystats" },
+#endif
+#ifdef vax
+#define X_MBDINIT (X_END)
+ { "_mbdinit" },
+#define X_UBDINIT (X_END+1)
+ { "_ubdinit" },
+#endif
+ { "" },
+};
+
+struct _disk {
+ long time[CPUSTATES];
+ long *xfer;
+} cur, last;
+
+struct vmmeter sum, osum;
+char **dr_name;
+int *dr_select, dk_ndrive, ndrives;
+
+int winlines = 20;
+
+kvm_t *kd;
+
+#define FORKSTAT 0x01
+#define INTRSTAT 0x02
+#define MEMSTAT 0x04
+#define SUMSTAT 0x08
+#define TIMESTAT 0x10
+#define VMSTAT 0x20
+
+#include "names.c" /* disk names -- machine dependent */
+
+void cpustats(), dkstats(), dointr(), domem(), dosum();
+void dovmstat(), kread(), usage();
+#ifdef notdef
+void dotimes(), doforkst();
+#endif
+
+main(argc, argv)
+ register int argc;
+ register char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ register int c, todo;
+ u_int interval;
+ int reps;
+ char *memf, *nlistf;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ memf = nlistf = NULL;
+ interval = reps = todo = 0;
+ while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != EOF) {
+ switch (c) {
+ case 'c':
+ reps = atoi(optarg);
+ break;
+#ifndef notdef
+ case 'f':
+ todo |= FORKSTAT;
+ break;
+#endif
+ case 'i':
+ todo |= INTRSTAT;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'm':
+ todo |= MEMSTAT;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 's':
+ todo |= SUMSTAT;
+ break;
+#ifndef notdef
+ case 't':
+ todo |= TIMESTAT;
+ break;
+#endif
+ case 'w':
+ interval = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (todo == 0)
+ todo = VMSTAT;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+ if (kd == 0) {
+ (void)fprintf(stderr,
+ "vmstat: kvm_openfiles: %s\n", errbuf);
+ exit(1);
+ }
+
+ if ((c = kvm_nlist(kd, namelist)) != 0) {
+ if (c > 0) {
+ (void)fprintf(stderr,
+ "vmstat: undefined symbols:");
+ for (c = 0;
+ c < sizeof(namelist)/sizeof(namelist[0]); c++)
+ if (namelist[c].n_type == 0)
+ fprintf(stderr, " %s",
+ namelist[c].n_name);
+ (void)fputc('\n', stderr);
+ } else
+ (void)fprintf(stderr, "vmstat: kvm_nlist: %s\n",
+ kvm_geterr(kd));
+ exit(1);
+ }
+
+ if (todo & VMSTAT) {
+ char **getdrivedata();
+ struct winsize winsize;
+
+ argv = getdrivedata(argv);
+ winsize.ws_row = 0;
+ (void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
+ if (winsize.ws_row > 0)
+ winlines = winsize.ws_row;
+
+ }
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ interval = atoi(*argv);
+ if (*++argv)
+ reps = atoi(*argv);
+ }
+#endif
+
+ if (interval) {
+ if (!reps)
+ reps = -1;
+ } else if (reps)
+ interval = 1;
+
+#ifdef notdef
+ if (todo & FORKSTAT)
+ doforkst();
+#endif
+ if (todo & MEMSTAT)
+ domem();
+ if (todo & SUMSTAT)
+ dosum();
+#ifdef notdef
+ if (todo & TIMESTAT)
+ dotimes();
+#endif
+ if (todo & INTRSTAT)
+ dointr();
+ if (todo & VMSTAT)
+ dovmstat(interval, reps);
+ exit(0);
+}
+
+char **
+getdrivedata(argv)
+ char **argv;
+{
+ register int i;
+ register char **cp;
+ char buf[30];
+
+ kread(X_DK_NDRIVE, &dk_ndrive, sizeof(dk_ndrive));
+ if (dk_ndrive < 0) {
+ (void)fprintf(stderr, "vmstat: dk_ndrive %d\n", dk_ndrive);
+ exit(1);
+ }
+ dr_select = calloc((size_t)dk_ndrive, sizeof(int));
+ dr_name = calloc((size_t)dk_ndrive, sizeof(char *));
+ for (i = 0; i < dk_ndrive; i++)
+ dr_name[i] = NULL;
+ cur.xfer = calloc((size_t)dk_ndrive, sizeof(long));
+ last.xfer = calloc((size_t)dk_ndrive, sizeof(long));
+ if (!read_names())
+ exit (1);
+ for (i = 0; i < dk_ndrive; i++)
+ if (dr_name[i] == NULL) {
+ (void)sprintf(buf, "??%d", i);
+ dr_name[i] = strdup(buf);
+ }
+
+ /*
+ * Choose drives to be displayed. Priority goes to (in order) drives
+ * supplied as arguments, default drives. If everything isn't filled
+ * in and there are drives not taken care of, display the first few
+ * that fit.
+ */
+#define BACKWARD_COMPATIBILITY
+ for (ndrives = 0; *argv; ++argv) {
+#ifdef BACKWARD_COMPATIBILITY
+ if (isdigit(**argv))
+ break;
+#endif
+ for (i = 0; i < dk_ndrive; i++) {
+ if (strcmp(dr_name[i], *argv))
+ continue;
+ dr_select[i] = 1;
+ ++ndrives;
+ break;
+ }
+ }
+ for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
+ if (dr_select[i])
+ continue;
+ for (cp = defdrives; *cp; cp++)
+ if (strcmp(dr_name[i], *cp) == 0) {
+ dr_select[i] = 1;
+ ++ndrives;
+ break;
+ }
+ }
+ for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
+ if (dr_select[i])
+ continue;
+ dr_select[i] = 1;
+ ++ndrives;
+ }
+ return(argv);
+}
+
+long
+getuptime()
+{
+ static time_t now, boottime;
+ time_t uptime;
+
+ if (boottime == 0)
+ kread(X_BOOTTIME, &boottime, sizeof(boottime));
+ (void)time(&now);
+ uptime = now - boottime;
+ if (uptime <= 0 || uptime > 60*60*24*365*10) {
+ (void)fprintf(stderr,
+ "vmstat: time makes no sense; namelist must be wrong.\n");
+ exit(1);
+ }
+ return(uptime);
+}
+
+int hz, hdrcnt;
+
+void
+dovmstat(interval, reps)
+ u_int interval;
+ int reps;
+{
+ struct vmtotal total;
+ time_t uptime, halfuptime;
+ void needhdr();
+ int mib[2], size;
+
+ uptime = getuptime();
+ halfuptime = uptime / 2;
+ (void)signal(SIGCONT, needhdr);
+
+ if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0)
+ kread(X_STATHZ, &hz, sizeof(hz));
+ if (!hz)
+ kread(X_HZ, &hz, sizeof(hz));
+
+ for (hdrcnt = 1;;) {
+ if (!--hdrcnt)
+ printhdr();
+ kread(X_CPTIME, cur.time, sizeof(cur.time));
+ kread(X_DKXFER, cur.xfer, sizeof(*cur.xfer) * dk_ndrive);
+ kread(X_SUM, &sum, sizeof(sum));
+ size = sizeof(total);
+ mib[0] = CTL_VM;
+ mib[1] = VM_METER;
+ if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
+ printf("Can't get kerninfo: %s\n", strerror(errno));
+ bzero(&total, sizeof(total));
+ }
+ (void)printf("%2d%2d%2d",
+ total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
+#define pgtok(a) ((a) * sum.v_page_size >> 10)
+#define rate(x) (((x) + halfuptime) / uptime) /* round */
+ (void)printf("%6ld%6ld ",
+ pgtok(total.t_avm), pgtok(total.t_free));
+ (void)printf("%4lu ", rate(sum.v_vm_faults - osum.v_vm_faults));
+ (void)printf("%3lu ",
+ rate(sum.v_reactivated - osum.v_reactivated));
+ (void)printf("%3lu ", rate(sum.v_swapin + sum.v_vnodein -
+ (osum.v_swapin + osum.v_vnodein)));
+ (void)printf("%3lu ", rate(sum.v_swapout + sum.v_vnodeout -
+ (osum.v_swapout + osum.v_vnodeout)));
+ (void)printf("%3lu ", rate(sum.v_tfree - osum.v_tfree));
+ (void)printf("%3lu ", rate(sum.v_pdpages - osum.v_pdpages));
+ dkstats();
+ (void)printf("%4lu %4lu %3lu ",
+ rate(sum.v_intr - osum.v_intr),
+ rate(sum.v_syscall - osum.v_syscall),
+ rate(sum.v_swtch - osum.v_swtch));
+ cpustats();
+ (void)printf("\n");
+ (void)fflush(stdout);
+ if (reps >= 0 && --reps <= 0)
+ break;
+ osum = sum;
+ uptime = interval;
+ /*
+ * We round upward to avoid losing low-frequency events
+ * (i.e., >= 1 per interval but < 1 per second).
+ */
+ if (interval != 1)
+ halfuptime = (uptime + 1) / 2;
+ else
+ halfuptime = 0;
+ (void)sleep(interval);
+ }
+}
+
+printhdr()
+{
+ register int i;
+
+ (void)printf(" procs memory page%*s", 20, "");
+ if (ndrives > 1)
+ (void)printf("disks %*s faults cpu\n",
+ ndrives * 3 - 6, "");
+ else
+ (void)printf("%*s faults cpu\n", ndrives * 3, "");
+ (void)printf(" r b w avm fre flt re pi po fr sr ");
+ for (i = 0; i < dk_ndrive; i++)
+ if (dr_select[i])
+ (void)printf("%c%c ", dr_name[i][0],
+ dr_name[i][strlen(dr_name[i]) - 1]);
+ (void)printf(" in sy cs us sy id\n");
+ hdrcnt = winlines - 2;
+}
+
+/*
+ * Force a header to be prepended to the next output.
+ */
+void
+needhdr()
+{
+
+ hdrcnt = 1;
+}
+
+#ifdef notdef
+void
+dotimes()
+{
+ u_int pgintime, rectime;
+
+ kread(X_REC, &rectime, sizeof(rectime));
+ kread(X_PGIN, &pgintime, sizeof(pgintime));
+ kread(X_SUM, &sum, sizeof(sum));
+ (void)printf("%u reclaims, %u total time (usec)\n",
+ sum.v_pgrec, rectime);
+ (void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec);
+ (void)printf("\n");
+ (void)printf("%u page ins, %u total time (msec)\n",
+ sum.v_pgin, pgintime / 10);
+ (void)printf("average: %8.1f msec / page in\n",
+ pgintime / (sum.v_pgin * 10.0));
+}
+#endif
+
+pct(top, bot)
+ long top, bot;
+{
+ long ans;
+
+ if (bot == 0)
+ return(0);
+ ans = (quad_t)top * 100 / bot;
+ return (ans);
+}
+
+#define PCT(top, bot) pct((long)(top), (long)(bot))
+
+#if defined(tahoe)
+#include <machine/cpu.h>
+#endif
+
+void
+dosum()
+{
+ struct nchstats nchstats;
+ long nchtotal;
+#if defined(tahoe)
+ struct keystats keystats;
+#endif
+
+ kread(X_SUM, &sum, sizeof(sum));
+ (void)printf("%9u cpu context switches\n", sum.v_swtch);
+ (void)printf("%9u device interrupts\n", sum.v_intr);
+ (void)printf("%9u software interrupts\n", sum.v_soft);
+#ifdef vax
+ (void)printf("%9u pseudo-dma dz interrupts\n", sum.v_pdma);
+#endif
+ (void)printf("%9u traps\n", sum.v_trap);
+ (void)printf("%9u system calls\n", sum.v_syscall);
+ (void)printf("%9u swap pager pageins\n", sum.v_swapin);
+ (void)printf("%9u swap pager pages paged in\n", sum.v_swappgsin);
+ (void)printf("%9u swap pager pageouts\n", sum.v_swapout);
+ (void)printf("%9u swap pager pages paged out\n", sum.v_swappgsout);
+ (void)printf("%9u vnode pager pageins\n", sum.v_vnodein);
+ (void)printf("%9u vnode pager pages paged in\n", sum.v_vnodepgsin);
+ (void)printf("%9u vnode pager pageouts\n", sum.v_vnodeout);
+ (void)printf("%9u vnode pager pages paged out\n", sum.v_vnodepgsout);
+ (void)printf("%9u page daemon wakeups\n", sum.v_pdwakeups);
+ (void)printf("%9u pages examined by the page daemon\n", sum.v_pdpages);
+ (void)printf("%9u pages reactivated\n", sum.v_reactivated);
+ (void)printf("%9u copy-on-write faults\n", sum.v_cow_faults);
+ (void)printf("%9u zero fill pages zeroed\n", sum.v_zfod);
+ (void)printf("%9u intransit blocking page faults\n", sum.v_intrans);
+ (void)printf("%9u total VM faults taken\n", sum.v_vm_faults);
+ (void)printf("%9u pages freed\n", sum.v_tfree);
+ (void)printf("%9u pages freed by daemon\n", sum.v_dfree);
+ (void)printf("%9u pages freed by exiting processes\n", sum.v_pfree);
+ (void)printf("%9u pages active\n", sum.v_active_count);
+ (void)printf("%9u pages inactive\n", sum.v_inactive_count);
+ (void)printf("%9u pages in VM cache\n", sum.v_cache_count);
+ (void)printf("%9u pages wired down\n", sum.v_wire_count);
+ (void)printf("%9u pages free\n", sum.v_free_count);
+ (void)printf("%9u bytes per page\n", sum.v_page_size);
+ kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
+ nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
+ nchstats.ncs_badhits + nchstats.ncs_falsehits +
+ nchstats.ncs_miss + nchstats.ncs_long;
+ (void)printf("%9ld total name lookups\n", nchtotal);
+ (void)printf(
+ "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n",
+ "", PCT(nchstats.ncs_goodhits, nchtotal),
+ PCT(nchstats.ncs_neghits, nchtotal),
+ PCT(nchstats.ncs_pass2, nchtotal));
+ (void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
+ PCT(nchstats.ncs_badhits, nchtotal),
+ PCT(nchstats.ncs_falsehits, nchtotal),
+ PCT(nchstats.ncs_long, nchtotal));
+#if defined(tahoe)
+ kread(X_CKEYSTATS, &keystats, sizeof(keystats));
+ (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n",
+ keystats.ks_allocs, "code cache keys allocated",
+ PCT(keystats.ks_allocfree, keystats.ks_allocs),
+ PCT(keystats.ks_norefs, keystats.ks_allocs),
+ PCT(keystats.ks_taken, keystats.ks_allocs),
+ PCT(keystats.ks_shared, keystats.ks_allocs));
+ kread(X_DKEYSTATS, &keystats, sizeof(keystats));
+ (void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n",
+ keystats.ks_allocs, "data cache keys allocated",
+ PCT(keystats.ks_allocfree, keystats.ks_allocs),
+ PCT(keystats.ks_norefs, keystats.ks_allocs),
+ PCT(keystats.ks_taken, keystats.ks_allocs),
+ PCT(keystats.ks_shared, keystats.ks_allocs));
+#endif
+}
+
+#ifdef notdef
+void
+doforkst()
+{
+ struct forkstat fks;
+
+ kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
+ (void)printf("%d forks, %d pages, average %.2f\n",
+ fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
+ (void)printf("%d vforks, %d pages, average %.2f\n",
+ fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
+}
+#endif
+
+void
+dkstats()
+{
+ register int dn, state;
+ double etime;
+ long tmp;
+
+ for (dn = 0; dn < dk_ndrive; ++dn) {
+ tmp = cur.xfer[dn];
+ cur.xfer[dn] -= last.xfer[dn];
+ last.xfer[dn] = tmp;
+ }
+ etime = 0;
+ for (state = 0; state < CPUSTATES; ++state) {
+ tmp = cur.time[state];
+ cur.time[state] -= last.time[state];
+ last.time[state] = tmp;
+ etime += cur.time[state];
+ }
+ if (etime == 0)
+ etime = 1;
+ etime /= hz;
+ for (dn = 0; dn < dk_ndrive; ++dn) {
+ if (!dr_select[dn])
+ continue;
+ (void)printf("%2.0f ", cur.xfer[dn] / etime);
+ }
+}
+
+void
+cpustats()
+{
+ register int state;
+ double pct, total;
+
+ total = 0;
+ for (state = 0; state < CPUSTATES; ++state)
+ total += cur.time[state];
+ if (total)
+ pct = 100 / total;
+ else
+ pct = 0;
+ (void)printf("%2.0f ", (cur.time[CP_USER] + cur.time[CP_NICE]) * pct);
+ (void)printf("%2.0f ", (cur.time[CP_SYS] + cur.time[CP_INTR]) * pct);
+ (void)printf("%2.0f", cur.time[CP_IDLE] * pct);
+}
+
+void
+dointr()
+{
+ register long *intrcnt, inttotal, uptime;
+ register int nintr, inamlen;
+ register char *intrname;
+
+ uptime = getuptime();
+ nintr = namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value;
+ inamlen =
+ namelist[X_EINTRNAMES].n_value - namelist[X_INTRNAMES].n_value;
+ intrcnt = malloc((size_t)nintr);
+ intrname = malloc((size_t)inamlen);
+ if (intrcnt == NULL || intrname == NULL) {
+ (void)fprintf(stderr, "vmstat: %s.\n", strerror(errno));
+ exit(1);
+ }
+ kread(X_INTRCNT, intrcnt, (size_t)nintr);
+ kread(X_INTRNAMES, intrname, (size_t)inamlen);
+ (void)printf("interrupt total rate\n");
+ inttotal = 0;
+ nintr /= sizeof(long);
+ while (--nintr >= 0) {
+ if (*intrcnt)
+ (void)printf("%-12s %8ld %8ld\n", intrname,
+ *intrcnt, *intrcnt / uptime);
+ intrname += strlen(intrname) + 1;
+ inttotal += *intrcnt++;
+ }
+ (void)printf("Total %8ld %8ld\n", inttotal, inttotal / uptime);
+}
+
+/*
+ * These names are defined in <sys/malloc.h>.
+ */
+char *kmemnames[] = INITKMEMNAMES;
+
+void
+domem()
+{
+ register struct kmembuckets *kp;
+ register struct kmemstats *ks;
+ register int i, j;
+ int len, size, first;
+ long totuse = 0, totfree = 0, totreq = 0;
+ char *name;
+ struct kmemstats kmemstats[M_LAST];
+ struct kmembuckets buckets[MINBUCKET + 16];
+
+ kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
+ (void)printf("Memory statistics by bucket size\n");
+ (void)printf(
+ "Size In Use Free Requests HighWater Couldfree\n");
+ for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) {
+ if (kp->kb_calls == 0)
+ continue;
+ size = 1 << i;
+ if(size < 1024)
+ (void)printf("%4d",size);
+ else
+ (void)printf("%3dK",size>>10);
+ (void)printf(" %8ld %6ld %10ld %7ld %10ld\n",
+ kp->kb_total - kp->kb_totalfree,
+ kp->kb_totalfree, kp->kb_calls,
+ kp->kb_highwat, kp->kb_couldfree);
+ totfree += size * kp->kb_totalfree;
+ }
+
+ kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats));
+ (void)printf("\nMemory usage type by bucket size\n");
+ (void)printf("Size Type(s)\n");
+ kp = &buckets[MINBUCKET];
+ for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
+ if (kp->kb_calls == 0)
+ continue;
+ first = 1;
+ len = 8;
+ for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
+ if (ks->ks_calls == 0)
+ continue;
+ if ((ks->ks_size & j) == 0)
+ continue;
+ name = kmemnames[i] ? kmemnames[i] : "undefined";
+ len += 2 + strlen(name);
+ if (first && j < 1024)
+ printf("%4d %s", j, name);
+ else if (first)
+ printf("%3dK %s", j>>10, name);
+ else
+ printf(",");
+ if (len >= 79) {
+ printf("\n\t ");
+ len = 10 + strlen(name);
+ }
+ if (!first)
+ printf(" %s", name);
+ first = 0;
+ }
+ printf("\n");
+ }
+
+ (void)printf(
+ "\nMemory statistics by type Type Kern\n");
+ (void)printf(
+" Type InUse MemUse HighUse Limit Requests Limit Limit Size(s)\n");
+ for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
+ if (ks->ks_calls == 0)
+ continue;
+ (void)printf("%13s%6ld%6ldK%7ldK%6ldK%9ld%5u%6u",
+ kmemnames[i] ? kmemnames[i] : "undefined",
+ ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
+ (ks->ks_maxused + 1023) / 1024,
+ (ks->ks_limit + 1023) / 1024, ks->ks_calls,
+ ks->ks_limblocks, ks->ks_mapblocks);
+ first = 1;
+ for (j = 1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
+ if ((ks->ks_size & j) == 0)
+ continue;
+ if (first)
+ printf(" ");
+ else
+ printf(",");
+ if(j<1024)
+ printf("%d",j);
+ else
+ printf("%dK",j>>10);
+ first = 0;
+ }
+ printf("\n");
+ totuse += ks->ks_memuse;
+ totreq += ks->ks_calls;
+ }
+ (void)printf("\nMemory Totals: In Use Free Requests\n");
+ (void)printf(" %7ldK %6ldK %8ld\n",
+ (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
+}
+
+/*
+ * kread reads something from the kernel, given its nlist index.
+ */
+void
+kread(nlx, addr, size)
+ int nlx;
+ void *addr;
+ size_t size;
+{
+ char *sym;
+
+ if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
+ sym = namelist[nlx].n_name;
+ if (*sym == '_')
+ ++sym;
+ (void)fprintf(stderr,
+ "vmstat: symbol %s not defined\n", sym);
+ exit(1);
+ }
+ if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
+ sym = namelist[nlx].n_name;
+ if (*sym == '_')
+ ++sym;
+ (void)fprintf(stderr, "vmstat: %s: %s\n", sym, kvm_geterr(kd));
+ exit(1);
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: vmstat [-ims] [-c count] [-M core] \
+[-N system] [-w wait] [disks]\n");
+ exit(1);
+}
diff --git a/usr.bin/w/Makefile b/usr.bin/w/Makefile
new file mode 100644
index 0000000..c7296b0
--- /dev/null
+++ b/usr.bin/w/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= w
+SRCS= fmt.c pr_time.c proc_compare.c w.c
+MAN1= w.1 uptime.1
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+BINGRP= kmem
+BINMODE=2555
+LINKS= ${BINDIR}/w ${BINDIR}/uptime
+
+.PATH: ${.CURDIR}/../../bin/ps
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/w/extern.h b/usr.bin/w/extern.h
new file mode 100644
index 0000000..1ee237c
--- /dev/null
+++ b/usr.bin/w/extern.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct proc;
+void pr_attime __P((time_t *, time_t *));
+void pr_idle __P((time_t));
+int proc_compare __P((struct proc *, struct proc *));
diff --git a/usr.bin/w/pr_time.c b/usr.bin/w/pr_time.c
new file mode 100644
index 0000000..dfbe154
--- /dev/null
+++ b/usr.bin/w/pr_time.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pr_time.c 8.2 (Berkeley) 4/4/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "extern.h"
+
+/*
+ * pr_attime --
+ * Print the time since the user logged in.
+ *
+ * Note: SCCS forces the bizarre string manipulation, things like
+ * 8.2 get replaced in the source code.
+ */
+void
+pr_attime(started, now)
+ time_t *started, *now;
+{
+ static char buf[256];
+ struct tm *tp;
+ time_t diff;
+ char fmt[20];
+
+ tp = localtime(started);
+ diff = *now - *started;
+
+ /* If more than a week, use day-month-year. */
+ if (diff > 86400 * 7)
+ (void)strcpy(fmt, "%d%b%y");
+
+ /* If not today, use day-hour-am/pm. */
+ else if (*now / 86400 != *started / 86400) {
+ (void)strcpy(fmt, __CONCAT("%a%", "I%p"));
+ }
+
+ /* Default is hh:mm{am,pm}. */
+ else {
+ (void)strcpy(fmt, __CONCAT("%l:%", "M%p"));
+ }
+
+ (void)strftime(buf, sizeof(buf), fmt, tp);
+ (void)printf("%s", buf);
+}
+
+/*
+ * pr_idle --
+ * Display the idle time.
+ */
+void
+pr_idle(idle)
+ time_t idle;
+{
+ /* If idle more than 36 hours, print as a number of days. */
+ if (idle >= 36 * 3600) {
+ int days = idle / 86400;
+ (void)printf(" %dday%s ", days, days > 1 ? "s" : " " );
+ }
+
+ /* If idle more than an hour, print as HH:MM. */
+ else if (idle >= 3600)
+ (void)printf(" %2d:%02d ",
+ idle / 3600, (idle % 3600) / 60);
+
+ else if (idle / 60 == 0)
+ (void)printf(" - ");
+
+ /* Else print the minutes idle. */
+ else
+ (void)printf(" %2d ", idle / 60);
+}
diff --git a/usr.bin/w/proc_compare.c b/usr.bin/w/proc_compare.c
new file mode 100644
index 0000000..e95359c
--- /dev/null
+++ b/usr.bin/w/proc_compare.c
@@ -0,0 +1,120 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)proc_compare.c 8.2 (Berkeley) 9/23/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+
+#include "extern.h"
+
+/*
+ * Returns 1 if p2 is "better" than p1
+ *
+ * The algorithm for picking the "interesting" process is thus:
+ *
+ * 1) Only foreground processes are eligible - implied.
+ * 2) Runnable processes are favored over anything else. The runner
+ * with the highest cpu utilization is picked (p_estcpu). Ties are
+ * broken by picking the highest pid.
+ * 3) The sleeper with the shortest sleep time is next. With ties,
+ * we pick out just "short-term" sleepers (P_SINTR == 0).
+ * 4) Further ties are broken by picking the highest pid.
+ *
+ * If you change this, be sure to consider making the change in the kernel
+ * too (^T in kern/tty.c).
+ *
+ * TODO - consider whether pctcpu should be used.
+ */
+
+#define ISRUN(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL))
+#define TESTAB(a, b) ((a)<<1 | (b))
+#define ONLYA 2
+#define ONLYB 1
+#define BOTH 3
+
+int
+proc_compare(p1, p2)
+ register struct proc *p1, *p2;
+{
+
+ if (p1 == NULL)
+ return (1);
+ /*
+ * see if at least one of them is runnable
+ */
+ switch (TESTAB(ISRUN(p1), ISRUN(p2))) {
+ case ONLYA:
+ return (0);
+ case ONLYB:
+ return (1);
+ case BOTH:
+ /*
+ * tie - favor one with highest recent cpu utilization
+ */
+ if (p2->p_estcpu > p1->p_estcpu)
+ return (1);
+ if (p1->p_estcpu > p2->p_estcpu)
+ return (0);
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+ }
+ /*
+ * weed out zombies
+ */
+ switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) {
+ case ONLYA:
+ return (1);
+ case ONLYB:
+ return (0);
+ case BOTH:
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+ }
+ /*
+ * pick the one with the smallest sleep time
+ */
+ if (p2->p_slptime > p1->p_slptime)
+ return (0);
+ if (p1->p_slptime > p2->p_slptime)
+ return (1);
+ /*
+ * favor one sleeping in a non-interruptible sleep
+ */
+ if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0)
+ return (1);
+ if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0)
+ return (0);
+ return (p2->p_pid > p1->p_pid); /* tie - return highest pid */
+}
diff --git a/usr.bin/w/uptime.1 b/usr.bin/w/uptime.1
new file mode 100644
index 0000000..8589bbc
--- /dev/null
+++ b/usr.bin/w/uptime.1
@@ -0,0 +1,60 @@
+.\" Copyright (c) 1980, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)uptime.1 8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt UPTIME 1
+.Os BSD 3
+.Sh NAME
+.Nm uptime
+.Nd show how long system has been running
+.Sh SYNOPSIS
+.Nm uptime
+.Sh DESCRIPTION
+The
+.Nm uptime
+utility displays the current time,
+the length of time the system has been up,
+the number of users, and the load average of the system over the last
+1, 5, and 15 minutes.
+.Sh FILES
+.Bl -tag -width /kernel
+.It Pa /kernel
+system name list
+.El
+.Sh SEE ALSO
+.Xr w 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/w/w.1 b/usr.bin/w/w.1
new file mode 100644
index 0000000..9034ebe
--- /dev/null
+++ b/usr.bin/w/w.1
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1980, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)w.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt W 1
+.Os BSD 4
+.Sh NAME
+.Nm w
+.Nd "who present users are and what they are doing"
+.Sh SYNOPSIS
+.Nm w
+.Op Fl hin
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Ar user
+.Sh DESCRIPTION
+The
+.Nm w
+utility prints a summary of the current activity on the system,
+including what each user is doing.
+The first line displays the current time of day, how long the system has
+been running, the number of users logged into the system, and the load
+averages.
+The load average numbers give the number of jobs in the run queue averaged
+over 1, 5 and 15 minutes.
+.Pp
+The fields output are the user's login name, the name of the terminal the
+user is on, the host from which the user is logged in, the time the user
+logged on, the time since the user last typed anything,
+and the name and arguments of the current process.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl h
+Suppress the heading.
+.It Fl i
+Output is sorted by idle time.
+.It Fl M
+Extract values associated with the name list from the specified
+core instead of the default
+.Dq /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the
+default
+.Dq /kernel .
+.It Fl n
+Show network addresses as numbers (normally
+.Nm w
+interprets addresses and attempts to display them symbolically).
+.El
+.Pp
+If a
+.Ar user
+name is specified, the output is restricted to that user.
+.Sh FILES
+.Bl -tag -width /var/run/utmp -compact
+.It Pa /var/run/utmp
+list of users on the system
+.El
+.Sh SEE ALSO
+.Xr who 1 ,
+.Xr finger 1 ,
+.Xr ps 1 ,
+.Xr uptime 1 ,
+.Sh BUGS
+The notion of the
+.Dq current process
+is muddy.
+The current algorithm is ``the highest numbered process on the terminal
+that is not ignoring interrupts, or, if there is none, the highest numbered
+process on the terminal''.
+This fails, for example, in critical sections of programs like the shell
+and editor, or when faulty programs running in the background fork and fail
+to ignore interrupts.
+(In cases where no process can be found,
+.Nm w
+prints
+.Dq \- . )
+.Pp
+The
+.Tn CPU
+time is only an estimate, in particular, if someone leaves a background
+process running after logging out, the person currently on that terminal is
+.Dq charged
+with the time.
+.Pp
+Background processes are not shown, even though they account for
+much of the load on the system.
+.Pp
+Sometimes processes, typically those in the background, are printed with
+null or garbaged arguments.
+In these cases, the name of the command is printed in parentheses.
+.Pp
+The
+.Nm w
+utility does not know about the new conventions for detection of background
+jobs.
+It will sometimes find a background job instead of the right one.
+.Sh COMPATIBILITY
+The
+.Fl f ,
+.Fl l ,
+.Fl s ,
+and
+.Fl w
+flags are no longer supported.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Ux 3.0 .
diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c
new file mode 100644
index 0000000..664d56f
--- /dev/null
+++ b/usr.bin/w/w.c
@@ -0,0 +1,435 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94";
+#endif /* not lint */
+
+/*
+ * w - print system status (who and what)
+ *
+ * This program is similar to the systat command on Tenex/Tops 10/20
+ *
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/tty.h>
+
+#include <machine/cpu.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <vis.h>
+
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "extern.h"
+
+struct timeval boottime;
+struct utmp utmp;
+struct winsize ws;
+kvm_t *kd;
+time_t now; /* the current time of day */
+time_t uptime; /* time of last reboot & elapsed time since */
+int ttywidth; /* width of tty */
+int argwidth; /* width of tty */
+int header = 1; /* true if -h flag: don't print heading */
+int nflag; /* true if -n flag: don't convert addrs */
+int sortidle; /* sort bu idle time */
+char *sel_user; /* login of particular user selected */
+char domain[MAXHOSTNAMELEN];
+
+/*
+ * One of these per active utmp entry.
+ */
+struct entry {
+ struct entry *next;
+ struct utmp utmp;
+ dev_t tdev; /* dev_t of terminal */
+ time_t idle; /* idle time of terminal in seconds */
+ struct kinfo_proc *kp; /* `most interesting' proc */
+ char *args; /* arg list of interesting process */
+} *ep, *ehead = NULL, **nextp = &ehead;
+
+static void pr_header __P((time_t *, int));
+static struct stat
+ *ttystat __P((char *));
+static void usage __P((int));
+
+char *fmt_argv __P((char **, char *, int)); /* ../../bin/ps/fmt.c */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *__progname;
+ struct kinfo_proc *kp;
+ struct hostent *hp;
+ struct stat *stp;
+ FILE *ut;
+ u_long l;
+ size_t arglen;
+ int ch, i, nentries, nusers, wcmd;
+ char *memf, *nlistf, *p, *vis_args, *x;
+ char buf[MAXHOSTNAMELEN], errbuf[256];
+
+ /* Are we w(1) or uptime(1)? */
+ p = __progname;
+ if (*p == '-')
+ p++;
+ if (*p == 'u') {
+ wcmd = 0;
+ p = "";
+ } else {
+ wcmd = 1;
+ p = "hiflM:N:nsuw";
+ }
+
+ memf = nlistf = NULL;
+ while ((ch = getopt(argc, argv, p)) != EOF)
+ switch (ch) {
+ case 'h':
+ header = 0;
+ break;
+ case 'i':
+ sortidle = 1;
+ break;
+ case 'M':
+ header = 0;
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'f': case 'l': case 's': case 'u': case 'w':
+ warnx("[-flsuw] no longer supported");
+ /* FALLTHROUGH */
+ case '?':
+ default:
+ usage(wcmd);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!(_res.options & RES_INIT))
+ res_init();
+ _res.retrans = 2; /* resolver timeout to 2 seconds per try */
+ _res.retry = 1; /* only try once.. */
+
+ if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
+ errx(1, "%s", errbuf);
+
+ (void)time(&now);
+ if ((ut = fopen(_PATH_UTMP, "r")) == NULL)
+ err(1, "%s", _PATH_UTMP);
+
+ if (*argv)
+ sel_user = *argv;
+
+ for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) {
+ if (utmp.ut_name[0] == '\0')
+ continue;
+ ++nusers;
+ if (wcmd == 0 || (sel_user &&
+ strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0))
+ continue;
+ if ((ep = calloc(1, sizeof(struct entry))) == NULL)
+ err(1, NULL);
+ *nextp = ep;
+ nextp = &(ep->next);
+ memmove(&(ep->utmp), &utmp, sizeof(struct utmp));
+ stp = ttystat(ep->utmp.ut_line);
+ ep->tdev = stp->st_rdev;
+#ifdef CPU_CONSDEV
+ /*
+ * If this is the console device, attempt to ascertain
+ * the true console device dev_t.
+ */
+ if (ep->tdev == 0) {
+ int mib[2];
+ size_t size;
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_CONSDEV;
+ size = sizeof(dev_t);
+ (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
+ }
+#endif
+ if ((ep->idle = now - stp->st_atime) < 0)
+ ep->idle = 0;
+ }
+ (void)fclose(ut);
+
+ if (header || wcmd == 0) {
+ pr_header(&now, nusers);
+ if (wcmd == 0)
+ exit (0);
+
+#define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n"
+#define WUSED (sizeof (HEADER) - sizeof ("WHAT\n"))
+ (void)printf(HEADER);
+ }
+
+ if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
+ err(1, "%s", kvm_geterr(kd));
+ for (i = 0; i < nentries; i++, kp++) {
+ struct proc *p = &kp->kp_proc;
+ struct eproc *e;
+
+ if (p->p_stat == SIDL || p->p_stat == SZOMB)
+ continue;
+ e = &kp->kp_eproc;
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) {
+ /*
+ * Proc is in foreground of this terminal
+ */
+ if (proc_compare(&ep->kp->kp_proc, p))
+ ep->kp = kp;
+ break;
+ }
+ }
+ }
+ if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
+ ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
+ ttywidth = 79;
+ else
+ ttywidth = ws.ws_col - 1;
+ argwidth = ttywidth - WUSED;
+ if (argwidth < 4)
+ argwidth = 8;
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ if (ep->kp == NULL) {
+ ep->args = "-";
+ continue;
+ }
+ ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
+ ep->kp->kp_proc.p_comm, MAXCOMLEN);
+ if (ep->args == NULL)
+ err(1, NULL);
+ }
+ /* sort by idle time */
+ if (sortidle && ehead != NULL) {
+ struct entry *from = ehead, *save;
+
+ ehead = NULL;
+ while (from != NULL) {
+ for (nextp = &ehead;
+ (*nextp) && from->idle >= (*nextp)->idle;
+ nextp = &(*nextp)->next)
+ continue;
+ save = from;
+ from = from->next;
+ save->next = *nextp;
+ *nextp = save;
+ }
+ }
+
+ if (!nflag)
+ if (gethostname(domain, sizeof(domain) - 1) < 0 ||
+ (p = strchr(domain, '.')) == 0)
+ domain[0] = '\0';
+ else {
+ domain[sizeof(domain) - 1] = '\0';
+ memmove(domain, p, strlen(p) + 1);
+ }
+
+ if ((vis_args = malloc(argwidth * 4 + 1)) == NULL)
+ err(1, NULL);
+ for (ep = ehead; ep != NULL; ep = ep->next) {
+ p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
+ if ((x = strchr(p, ':')) != NULL)
+ *x++ = '\0';
+ if (!nflag && isdigit(*p) &&
+ (long)(l = inet_addr(p)) != -1 &&
+ (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) {
+ if (domain[0] != '\0') {
+ p = hp->h_name;
+ p += strlen(hp->h_name);
+ p -= strlen(domain);
+ if (p > hp->h_name && strcmp(p, domain) == 0)
+ *p = '\0';
+ }
+ p = hp->h_name;
+ }
+ if (x) {
+ (void)snprintf(buf, sizeof(buf), "%s:%.*s", p,
+ ep->utmp.ut_host + UT_HOSTSIZE - x, x);
+ p = buf;
+ }
+ (void)printf("%-*.*s %-3.3s %-*.*s ",
+ UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
+ strncmp(ep->utmp.ut_line, "tty", 3) &&
+ strncmp(ep->utmp.ut_line, "cua", 3) ?
+ ep->utmp.ut_line : ep->utmp.ut_line + 3,
+ UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-");
+ pr_attime(&ep->utmp.ut_time, &now);
+ pr_idle(ep->idle);
+ if (ep->args != NULL) {
+ arglen = strlen(ep->args);
+ strvisx(vis_args, ep->args,
+ arglen > argwidth ? argwidth : arglen,
+ VIS_TAB | VIS_NL | VIS_NOSLASH);
+ }
+ (void)printf("%.*s\n", argwidth, ep->args);
+ }
+ exit(0);
+}
+
+static void
+pr_header(nowp, nusers)
+ time_t *nowp;
+ int nusers;
+{
+ double avenrun[3];
+ time_t uptime;
+ int days, hrs, i, mins;
+ int mib[2];
+ size_t size;
+ char buf[256];
+
+ /*
+ * Print time of day.
+ *
+ * SCCS forces the string manipulation below, as it replaces
+ * %, M, and % in a character string with the file name.
+ */
+ (void)strftime(buf, sizeof(buf),
+ __CONCAT("%l:%","M%p"), localtime(nowp));
+ (void)printf("%s ", buf);
+
+ /*
+ * Print how long system has been up.
+ * (Found by looking getting "boottime" from the kernel)
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ size = sizeof(boottime);
+ if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 &&
+ boottime.tv_sec != 0) {
+ uptime = now - boottime.tv_sec;
+ uptime += 30;
+ days = uptime / 86400;
+ uptime %= 86400;
+ hrs = uptime / 3600;
+ uptime %= 3600;
+ mins = uptime / 60;
+ (void)printf(" up");
+ if (days > 0)
+ (void)printf(" %d day%s,", days, days > 1 ? "s" : "");
+ if (hrs > 0 && mins > 0)
+ (void)printf(" %2d:%02d,", hrs, mins);
+ else {
+ if (hrs > 0)
+ (void)printf(" %d hr%s,",
+ hrs, hrs > 1 ? "s" : "");
+ if (mins > 0)
+ (void)printf(" %d min%s,",
+ mins, mins > 1 ? "s" : "");
+ }
+ }
+
+ /* Print number of users logged in to system */
+ (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s");
+
+ /*
+ * Print 1, 5, and 15 minute load averages.
+ */
+ if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
+ (void)printf(", no load average information available\n");
+ else {
+ (void)printf(", load averages:");
+ for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) {
+ if (i > 0)
+ (void)printf(",");
+ (void)printf(" %.2f", avenrun[i]);
+ }
+ (void)printf("\n");
+ }
+}
+
+static struct stat *
+ttystat(line)
+ char *line;
+{
+ static struct stat sb;
+ char ttybuf[MAXPATHLEN];
+
+ (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, line);
+ if (stat(ttybuf, &sb))
+ err(1, "%s", ttybuf);
+ return (&sb);
+}
+
+static void
+usage(wcmd)
+ int wcmd;
+{
+ if (wcmd)
+ (void)fprintf(stderr,
+ "usage: w: [-hin] [-M core] [-N system] [user]\n");
+ else
+ (void)fprintf(stderr, "uptime\n");
+ exit (1);
+}
diff --git a/usr.bin/wall/Makefile b/usr.bin/wall/Makefile
new file mode 100644
index 0000000..65abb61
--- /dev/null
+++ b/usr.bin/wall/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= wall
+SRCS= ttymsg.c wall.c
+BINGRP= tty
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/wall/ttymsg.c b/usr.bin/wall/ttymsg.c
new file mode 100644
index 0000000..6a7c3d1
--- /dev/null
+++ b/usr.bin/wall/ttymsg.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Display the contents of a uio structure on a terminal. Used by wall(1),
+ * syslogd(8), and talkd(8). Forks and finishes in child if write would block,
+ * waiting up to tmout seconds. Returns pointer to error string on unexpected
+ * error; string is not newline-terminated. Various "normal" errors are
+ * ignored (exclusive-use, lack of permission, etc.).
+ */
+char *
+ttymsg(iov, iovcnt, line, tmout)
+ struct iovec *iov;
+ int iovcnt;
+ char *line;
+ int tmout;
+{
+ static char device[MAXNAMLEN] = _PATH_DEV;
+ static char errbuf[1024];
+ register int cnt, fd, left, wret;
+ struct iovec localiov[6];
+ int forked = 0;
+
+ if (iovcnt > sizeof(localiov) / sizeof(localiov[0]))
+ return ("too many iov's (change code in wall/ttymsg.c)");
+
+ (void) strcpy(device + sizeof(_PATH_DEV) - 1, line);
+ if (strchr(device + sizeof(_PATH_DEV) - 1, '/')) {
+ /* A slash is an attempt to break security... */
+ (void) snprintf(errbuf, sizeof(errbuf), "'/' in \"%s\"",
+ device);
+ return (errbuf);
+ }
+
+ /*
+ * open will fail on slip lines or exclusive-use lines
+ * if not running as root; not an error.
+ */
+ if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
+ if (errno == EBUSY || errno == EACCES)
+ return (NULL);
+ (void) snprintf(errbuf, sizeof(errbuf),
+ "%s: %s", device, strerror(errno));
+ return (errbuf);
+ }
+
+ for (cnt = left = 0; cnt < iovcnt; ++cnt)
+ left += iov[cnt].iov_len;
+
+ for (;;) {
+ wret = writev(fd, iov, iovcnt);
+ if (wret >= left)
+ break;
+ if (wret >= 0) {
+ left -= wret;
+ if (iov != localiov) {
+ bcopy(iov, localiov,
+ iovcnt * sizeof(struct iovec));
+ iov = localiov;
+ }
+ for (cnt = 0; wret >= iov->iov_len; ++cnt) {
+ wret -= iov->iov_len;
+ ++iov;
+ --iovcnt;
+ }
+ if (wret) {
+ iov->iov_base += wret;
+ iov->iov_len -= wret;
+ }
+ continue;
+ }
+ if (errno == EWOULDBLOCK) {
+ int cpid, off = 0;
+
+ if (forked) {
+ (void) close(fd);
+ _exit(1);
+ }
+ cpid = fork();
+ if (cpid < 0) {
+ (void) snprintf(errbuf, sizeof(errbuf),
+ "fork: %s", strerror(errno));
+ (void) close(fd);
+ return (errbuf);
+ }
+ if (cpid) { /* parent */
+ (void) close(fd);
+ return (NULL);
+ }
+ forked++;
+ /* wait at most tmout seconds */
+ (void) signal(SIGALRM, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL); /* XXX */
+ (void) sigsetmask(0);
+ (void) alarm((u_int)tmout);
+ (void) fcntl(fd, O_NONBLOCK, &off);
+ continue;
+ }
+ /*
+ * We get ENODEV on a slip line if we're running as root,
+ * and EIO if the line just went away.
+ */
+ if (errno == ENODEV || errno == EIO)
+ break;
+ (void) close(fd);
+ if (forked)
+ _exit(1);
+ (void) snprintf(errbuf, sizeof(errbuf),
+ "%s: %s", device, strerror(errno));
+ return (errbuf);
+ }
+
+ (void) close(fd);
+ if (forked)
+ _exit(0);
+ return (NULL);
+}
diff --git a/usr.bin/wall/wall.1 b/usr.bin/wall/wall.1
new file mode 100644
index 0000000..ed2f4c9
--- /dev/null
+++ b/usr.bin/wall/wall.1
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)wall.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt WALL 1
+.Os BSD 4
+.Sh NAME
+.Nm wall
+.Nd write a message to users
+.Sh SYNOPSIS
+.Nm wall
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Wall
+displays the contents of
+.Ar file
+or, by default, its standard input, on the terminals of all
+currently logged in users.
+.Pp
+Only the super-user can write on the
+terminals of users who have chosen
+to deny messages or are using a program which
+automatically denies messages.
+.Sh SEE ALSO
+.Xr mesg 1 ,
+.Xr talk 1 ,
+.Xr write 1 ,
+.Xr shutdown 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c
new file mode 100644
index 0000000..083f0f5
--- /dev/null
+++ b/usr.bin/wall/wall.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1988, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93";
+#endif /* not lint */
+
+/*
+ * This program is not related to David Wall, whose Stanford Ph.D. thesis
+ * is entitled "Mechanisms for Broadcast and Selective Broadcast".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmp.h>
+
+void makemsg __P((char *));
+
+#define IGNOREUSER "sleeper"
+
+int nobanner;
+int mbufsize;
+char *mbuf;
+
+/* ARGSUSED */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ int ch;
+ struct iovec iov;
+ struct utmp utmp;
+ FILE *fp;
+ char *p, *ttymsg();
+ char line[sizeof(utmp.ut_line) + 1];
+
+ while ((ch = getopt(argc, argv, "n")) != EOF)
+ switch (ch) {
+ case 'n':
+ /* undoc option for shutdown: suppress banner */
+ if (geteuid() == 0)
+ nobanner = 1;
+ break;
+ case '?':
+ default:
+usage:
+ (void)fprintf(stderr, "usage: wall [file]\n");
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 1)
+ goto usage;
+
+ makemsg(*argv);
+
+ if (!(fp = fopen(_PATH_UTMP, "r"))) {
+ (void)fprintf(stderr, "wall: cannot read %s.\n", _PATH_UTMP);
+ exit(1);
+ }
+ iov.iov_base = mbuf;
+ iov.iov_len = mbufsize;
+ /* NOSTRICT */
+ while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) {
+ if (!utmp.ut_name[0] ||
+ !strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name)))
+ continue;
+ strncpy(line, utmp.ut_line, sizeof(utmp.ut_line));
+ line[sizeof(utmp.ut_line)] = '\0';
+ if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL)
+ (void)fprintf(stderr, "wall: %s\n", p);
+ }
+ exit(0);
+}
+
+void
+makemsg(fname)
+ char *fname;
+{
+ register int ch, cnt;
+ struct tm *lt;
+ struct passwd *pw;
+ struct stat sbuf;
+ time_t now, time();
+ FILE *fp;
+ int fd;
+ char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[15];
+ char *getlogin(), *strcpy(), *ttyname();
+
+ (void)strcpy(tmpname, _PATH_TMP);
+ (void)strcat(tmpname, "/wall.XXXXXX");
+ if (!(fd = mkstemp(tmpname)) || !(fp = fdopen(fd, "r+"))) {
+ (void)fprintf(stderr, "wall: can't open temporary file.\n");
+ exit(1);
+ }
+ (void)unlink(tmpname);
+
+ if (!nobanner) {
+ if (!(whom = getlogin()))
+ whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
+ (void)gethostname(hostname, sizeof(hostname));
+ (void)time(&now);
+ lt = localtime(&now);
+
+ /*
+ * all this stuff is to blank out a square for the message;
+ * we wrap message lines at column 79, not 80, because some
+ * terminals wrap after 79, some do not, and we can't tell.
+ * Which means that we may leave a non-blank character
+ * in column 80, but that can't be helped.
+ */
+ (void)fprintf(fp, "\r%79s\r\n", " ");
+ (void)sprintf(lbuf, "Broadcast Message from %s@%s",
+ whom, hostname);
+ (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
+ (void)sprintf(lbuf, " (%s) at %d:%02d ...", ttyname(2),
+ lt->tm_hour, lt->tm_min);
+ (void)fprintf(fp, "%-79.79s\r\n", lbuf);
+ }
+ (void)fprintf(fp, "%79s\r\n", " ");
+
+ if (fname && !(freopen(fname, "r", stdin))) {
+ (void)fprintf(stderr, "wall: can't read %s.\n", fname);
+ exit(1);
+ }
+ while (fgets(lbuf, sizeof(lbuf), stdin))
+ for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) {
+ if (cnt == 79 || ch == '\n') {
+ for (; cnt < 79; ++cnt)
+ putc(' ', fp);
+ putc('\r', fp);
+ putc('\n', fp);
+ cnt = 0;
+ } else if (!isprint(ch) && !isspace(ch) && ch != '\007')
+ {
+ putc('^', fp);
+ putc(ch^0x40, fp); /* DEL to ?, others to a
+lpha */
+ } else
+ putc(ch, fp);
+ }
+ (void)fprintf(fp, "%79s\r\n", " ");
+ rewind(fp);
+
+ if (fstat(fd, &sbuf)) {
+ (void)fprintf(stderr, "wall: can't stat temporary file.\n");
+ exit(1);
+ }
+ mbufsize = sbuf.st_size;
+ if (!(mbuf = malloc((u_int)mbufsize))) {
+ (void)fprintf(stderr, "wall: out of memory.\n");
+ exit(1);
+ }
+ if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) {
+ (void)fprintf(stderr, "wall: can't read temporary file.\n");
+ exit(1);
+ }
+ (void)close(fd);
+}
diff --git a/usr.bin/watch/Makefile b/usr.bin/watch/Makefile
new file mode 100644
index 0000000..7a9f602
--- /dev/null
+++ b/usr.bin/watch/Makefile
@@ -0,0 +1,6 @@
+PROG= watch
+MAN8= watch.8
+BINMODE=500
+LDADD= -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/watch/snp.4 b/usr.bin/watch/snp.4
new file mode 100644
index 0000000..47b97d8
--- /dev/null
+++ b/usr.bin/watch/snp.4
@@ -0,0 +1,49 @@
+.Dd Februar 17, 1995
+.Dt SNP(4)
+.Os
+.Sh NAME
+
+ snp - tty snoop interface.
+
+.Sh SYNOPSIS
+#include <sys/snoop.h>
+
+.Sh DESCRIPTION
+ /dev/snp? are snoop devices which allow user to attach to any tty
+and watch activities on it. Device can be used by root only.
+ To connect device to tty use special iocontrol SNPSTTY.Argument to
+it is a structure 'snptty'.
+ struct snptty {
+ int st_type;
+ int st_unit;
+ }
+
+st_type - type of tty to attach. Currently supported are :
+ ST_PTY - pseudo-tty devices.
+ ST_VTY - virtual ttys (syscons).
+ ST_SIO - serial ttys.
+
+st_unit - number of tty unit.
+By specifying ever st_type or st_unit -1 user can detach snp
+device from tty.
+
+ SNPGTTY - iocontrol returns current tty attached.
+ FIONREAD - iocontrol returns ever positive value equal to number of
+ characters in read buffer. Also special values defined:
+ SNP_OFLOW - device overflow occured,device automatically
+ detached.
+ SNP_TTYCLOSE - tty still has not been attached.
+ SNP_DETACH - snp device has been detached by user or
+ tty device has been closed and detached
+ automatically.
+
+.Sh SEE ALSO
+
+watch(8), pty(4), sio(4)
+.Sh BUGS
+
+While in line mode,user input can't be seen.
+Has no ability to write to attached tty device.
+
+.Sh HISTORY
+FreeBSD 2.1 by Ugen J.S.Antsilevich <ugen@NetVision.net.il>
diff --git a/usr.bin/watch/watch.8 b/usr.bin/watch/watch.8
new file mode 100644
index 0000000..a57a70c
--- /dev/null
+++ b/usr.bin/watch/watch.8
@@ -0,0 +1,77 @@
+.\"
+.\" @(#)watch.8 1.1 (FreeBSD) 2/17/95
+.\"
+.Dd February 17, 1995
+.Dt WATCH 8
+.Os
+.Sh NAME
+.Nm watch
+.Nd snoop on another tty line
+.Sh SYNOPSYS
+.Nm watch
+.Op Fl ciot
+.Ar tty
+.\" watch [-ciot] [<tty name>]
+.Sh DESCRIPTION
+.Nm Watch
+allows the superuser to examine all data coming through a specified tty.
+.Nm Watch
+writes to standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width "-l nul "
+.It Fl c
+Reconnect on close. If the tty observed by
+.Nm watch
+is closed, automatically reattach to the same tty.
+If this option is not specified,
+.Nm watch
+will request a new tty if running in interactive mode or exit if running
+without a controlling tty.
+.It Fl i
+Force interactive mode.
+Interactive mode is a default if
+.Nm watch
+is started from a tty.
+If output is redirected to a file, interactive mode can still be requested
+by specifying this option.
+.It Fl o
+Reconnect on overflow.
+The behaviour of
+.Nm watch
+if the observed tty overflows is similar to the behavior if the observed tty
+is closed.
+For more info see
+.Xr snp 4 .
+.It Fl t
+Print the date and time when observation of a given tty is started.
+.It Ar tty
+Tty may be specified as a remote pseudo tty device, a virtual console, or
+a serial line.
+Names may be preceded by "/dev/".
+.Sh OPERATION
+While running in interactive mode, all user input is discarded except for:
+.Pp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<control-C>"
+Exit
+.Nm watch .
+.It Sy "<control-W>"
+Clear screen.
+.It Sy "<control-X>"
+Change attached tty.
+.Sh RESTRICTIONS
+Only the superuser can run
+.Nm watch .
+.Sh SEE ALSO
+.Xr snpl4 ,
+.Xr pty 4 ,
+.Xr sio 4 .
+.Sh BUGS
+No terminal emulation is performed.
+All user output is reproduced as-is.
+.Sh AUTHOR
+Ugen J.S. Antsilevich <ugen@NetVision.net.il>
+.Sh HISTORY
+.Nm Watch
+first appeared in FreeBSD 2.1
diff --git a/usr.bin/watch/watch.c b/usr.bin/watch/watch.c
new file mode 100644
index 0000000..f5e4007
--- /dev/null
+++ b/usr.bin/watch/watch.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 1995 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * Snoop stuff.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h>
+#include <sys/snoop.h>
+
+
+#define MSG_INIT "Snoop started."
+#define MSG_OFLOW "Snoop stopped due to overflow.Reconnecting."
+#define MSG_CLOSED "Snoop stopped due to tty close.Reconnecting."
+#define MSG_CHANGE "Snoop device change by user request."
+
+
+#define DEV_NAME_LEN 12 /* for /dev/ttyXX++ */
+#define MIN_SIZE 256
+
+#define CHR_SWITCH 24 /* Ctrl+X */
+#define CHR_CLEAR 23 /* Ctrl+V */
+
+
+int opt_reconn_close = 0;
+int opt_reconn_oflow = 0;
+int opt_interactive = 1;
+int opt_timestamp = 0;
+
+char dev_name[DEV_NAME_LEN];
+int snp_io;
+struct snptty snp_tty;
+int std_in = 0, std_out = 1;
+
+
+int clear_ok = 0;
+struct sgttyb sgo;
+char tbuf[1024], buf[1024];
+
+
+void
+clear()
+{
+ if (clear_ok)
+ tputs(buf, 1, putchar);
+ fflush(stdout);
+}
+
+void
+timestamp(buf)
+ char *buf;
+{
+ time_t t;
+ char btmp[1024];
+ clear();
+ printf("\n---------------------------------------------\n");
+ t = time(NULL);
+ strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
+ printf("%s\n", btmp);
+ printf("%s\n", buf);
+ printf("---------------------------------------------\n");
+ fflush(stdout);
+}
+
+void
+set_tty()
+{
+ struct sgttyb sgn;
+ ioctl(std_in, TIOCGETP, &sgo);
+ /* bcopy(&sgn, &sgo, sizeof(struct sgttyb)); */
+ sgn = sgo;
+ sgn.sg_flags |= CBREAK;
+ sgn.sg_flags &= ~ECHO;
+ ioctl(std_in, TIOCSETP, &sgn);
+}
+
+void
+unset_tty()
+{
+ ioctl(std_in, TIOCSETP, &sgo);
+}
+
+
+void
+fatal(buf)
+ char *buf;
+{
+ unset_tty();
+ if (buf)
+ fprintf(stderr, "Fatal: %s\n", buf);
+ exit(1);
+}
+
+int
+open_snp()
+{
+ char snp[DEV_NAME_LEN] = "/dev/snpX";
+ char c;
+ int f;
+ for (c = '0'; c <= '9'; c++) {
+ snp[8] = c;
+ if ((f = open(snp, O_RDONLY)) < 0)
+ continue;
+ return f;
+ }
+ fatal("Cannot open snoop device.");
+}
+
+
+void
+cleanup()
+{
+ if (opt_timestamp)
+ timestamp("Logging Exited.");
+ close(snp_io);
+ unset_tty();
+ exit(0);
+}
+
+
+void
+show_usage()
+{
+ printf("watch -[ciot] [tty name]\n");
+ exit(1);
+}
+
+void
+setup_scr()
+{
+ char *cbuf = buf, *term;
+ if (!opt_interactive)
+ return;
+ if ((term = getenv("TERM")))
+ if (tgetent(tbuf, term) == 1)
+ if (tgetstr("cl", &cbuf))
+ clear_ok = 1;
+ clear();
+ set_tty();
+}
+
+
+int
+ctoh(c)
+ char c;
+{
+ if (c >= '0' && c <= '9')
+ return (int) (c - '0');
+
+ if (c >= 'a' && c <= 'f')
+ return (int) (c - 'a' + 10);
+
+ fatal("Bad tty number.");
+}
+
+
+void
+detach_snp()
+{
+ struct snptty st;
+ st.st_type = -1;
+ st.st_unit = -1;
+ ioctl(snp_io, SNPSTTY, &st);
+}
+
+void
+attach_snp()
+{
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal("Cannot attach to tty.");
+ if (opt_timestamp)
+ timestamp("Logging Started.");
+}
+
+
+void
+set_dev(name)
+ char *name;
+{
+ char buf[DEV_NAME_LEN], num[DEV_NAME_LEN];
+ int unitbase = 0;
+
+ if (strlen(name) > 5 && !strncmp(name, "/dev/", 5))
+ strcpy(buf, &(name[5]));
+ else
+ strcpy(buf, name);
+
+ if (strlen(buf) < 4)
+ fatal("Bad tty name.");
+
+ if (!strncmp(buf, "tty", 3))
+ switch (buf[3]) {
+ case 'v':
+ snp_tty.st_unit = ctoh(buf[4]);
+ snp_tty.st_type = ST_VTY;
+ goto got_num;
+ case 'r':
+ unitbase += 16;
+ case 'q':
+ unitbase += 16;
+ case 'p':
+ snp_tty.st_unit = ctoh(buf[4]) + unitbase;
+ snp_tty.st_type = ST_PTY;
+ goto got_num;
+ case '0':
+ case 'd':
+ snp_tty.st_unit = ctoh(buf[4]);
+ snp_tty.st_type = ST_SIO;
+ goto got_num;
+ default:
+ fatal("Bad tty name.");
+
+ }
+
+
+ if (!strncmp(buf, "vty", 3)) {
+ strcpy(num, &(buf[3]));
+ snp_tty.st_unit = atoi(num);
+ snp_tty.st_type = ST_VTY;
+ goto got_num;
+ }
+ if (!strncmp(buf, "pty", 3)) {
+ strcpy(num, &(buf[3]));
+ snp_tty.st_unit = atoi(num);
+ snp_tty.st_type = ST_PTY;
+ goto got_num;
+ }
+ if (!strncmp(buf, "sio", 3) || !strncmp(buf, "cua", 3)) {
+ strcpy(num, &(buf[3]));
+ snp_tty.st_unit = atoi(num);
+ snp_tty.st_type = ST_SIO;
+ goto got_num;
+ }
+ fatal("Bad tty name.");
+got_num:
+ attach_snp();
+}
+
+void
+ask_dev(dev_name, msg)
+ char *dev_name, *msg;
+{
+ char buf[DEV_NAME_LEN];
+ int len;
+
+ clear();
+ unset_tty();
+
+ if (msg)
+ printf("%s\n", msg);
+ if (dev_name)
+ printf("Enter device name [%s]:", dev_name);
+ else
+ printf("Enter device name:");
+
+ if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ if (buf[0] != '\0' && buf[0] != ' ')
+ strcpy(dev_name, buf);
+ }
+ set_tty();
+}
+
+
+void
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int res, nread, b_size = MIN_SIZE;
+ extern int optind;
+ char ch, *buf;
+ fd_set fd_s;
+
+ if (getuid() != 0)
+ fatal(NULL);
+
+ if (isatty(std_out))
+ opt_interactive = 1;
+ else
+ opt_interactive = 0;
+
+
+ while ((ch = getopt(ac, av, "ciot")) != EOF)
+ switch (ch) {
+ case 'c':
+ opt_reconn_close = 1;
+ break;
+ case 'i':
+ opt_interactive = 1;
+ break;
+ case 'o':
+ opt_reconn_oflow = 1;
+ break;
+ case 't':
+ opt_timestamp = 1;
+ break;
+ case '?':
+ default:
+ show_usage();
+ exit(1);
+ }
+
+ signal(SIGINT, cleanup);
+
+ setup_scr();
+ snp_io = open_snp();
+
+ if (*(av += optind) == NULL) {
+ if (opt_interactive)
+ ask_dev(dev_name, MSG_INIT);
+ else
+ fatal("No device name given.");
+ } else
+ strncpy(dev_name, *av, DEV_NAME_LEN);
+
+ set_dev(dev_name);
+
+ if (!(buf = (char *) malloc(b_size)))
+ fatal("Cannot malloc().");
+
+ FD_ZERO(&fd_s);
+
+ while (1) {
+ if (opt_interactive)
+ FD_SET(std_in, &fd_s);
+ FD_SET(snp_io, &fd_s);
+ res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
+ if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
+ switch (ch = getchar()) {
+ case CHR_CLEAR:
+ clear();
+ break;
+ case CHR_SWITCH:
+ /* detach_snp(); */
+ ask_dev(dev_name, MSG_CHANGE);
+ set_dev(dev_name);
+ break;
+ default:
+ }
+ }
+ if (!FD_ISSET(snp_io, &fd_s))
+ continue;
+
+ if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
+ fatal("ioctl() failed.");
+
+ switch (nread) {
+ case SNP_OFLOW:
+ if (opt_reconn_oflow)
+ attach_snp();
+ else if (opt_interactive) {
+ ask_dev(dev_name, MSG_OFLOW);
+ set_dev(dev_name);
+ } else
+ cleanup();
+ case SNP_DETACH:
+ case SNP_TTYCLOSE:
+ if (opt_reconn_close)
+ attach_snp();
+ else if (opt_interactive) {
+ ask_dev(dev_name, MSG_CLOSED);
+ set_dev(dev_name);
+ } else
+ cleanup();
+ default:
+ if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
+ free(buf);
+ if (!(buf = (char *) malloc(b_size / 2)))
+ fatal("Cannot malloc()");
+ b_size = b_size / 2;
+ }
+ if (nread > b_size) {
+ b_size = (nread % 2) ? (nread + 1) : (nread);
+ free(buf);
+ if (!(buf = (char *) malloc(b_size)))
+ fatal("Cannot malloc()");
+ }
+ if (read(snp_io, buf, nread) < nread)
+ fatal("read failed.");
+ if (write(std_out, buf, nread) < nread)
+ fatal("write failed.");
+ }
+ } /* While */
+}
diff --git a/usr.bin/wc/Makefile b/usr.bin/wc/Makefile
new file mode 100644
index 0000000..07da67a
--- /dev/null
+++ b/usr.bin/wc/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= wc
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/wc/wc.1 b/usr.bin/wc/wc.1
new file mode 100644
index 0000000..64c047b
--- /dev/null
+++ b/usr.bin/wc/wc.1
@@ -0,0 +1,109 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)wc.1 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt WC 1
+.Os
+.Sh NAME
+.Nm wc
+.Nd word, line, and byte count
+.Sh SYNOPSIS
+.Nm wc
+.Op Fl clw
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm wc
+utility displays the number of lines, words, and bytes contained in each
+input
+.Ar file
+(or standard input, by default) to the standard output.
+A line is defined as a string of characters delimited by a <newline>
+character,
+and a word is defined as a string of characters delimited by white space
+characters.
+White space characters are the set of characters for which the
+.Xr isspace 3
+function returns true.
+If more than one input file is specified, a line of cumulative counts
+for all the files is displayed on a separate line after the output for
+the last file.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl c
+The number of bytes in each input file
+is written to the standard output.
+.It Fl l
+The number of lines in each input file
+is written to the standard output.
+.It Fl w
+The number of words in each input file
+is written to the standard output.
+.El
+.Pp
+When an option is specified,
+.Nm wc
+only reports the information requested by that option.
+The default action is equivalent to specifying all of the flags.
+.Pp
+If no files are specified, the standard input is used and no
+file name is displayed.
+.Pp
+The
+.Nm wc
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr isspace 3
+.Sh COMPATIBILITY
+Historically, the
+.Nm wc
+utility was documented to define a word as a ``maximal string of
+characters delimited by <space>, <tab> or <newline> characters''.
+The implementation, however, didn't handle non-printing characters
+correctly so that `` ^D^E '' counted as 6 spaces, while ``foo^D^Ebar''
+counted as 8 characters.
+4BSD systems after 4.3BSD modified the implementation to be consistent
+with the documentation.
+This implementation defines a ``word'' in terms of the
+.Xr isspace 3
+function, as required by
+.St -p1003.2 .
+.Sh STANDARDS
+The
+.Nm wc
+function conforms to
+.St -p1003.2 .
diff --git a/usr.bin/wc/wc.c b/usr.bin/wc/wc.c
new file mode 100644
index 0000000..a50196c
--- /dev/null
+++ b/usr.bin/wc/wc.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 1980, 1987, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)wc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+u_long tlinect, twordct, tcharct;
+int doline, doword, dochar;
+
+void cnt __P((char *));
+void err __P((const char *, ...));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int ch;
+ int total;
+
+ while ((ch = getopt(argc, argv, "lwc")) != EOF)
+ switch((char)ch) {
+ case 'l':
+ doline = 1;
+ break;
+ case 'w':
+ doword = 1;
+ break;
+ case 'c':
+ dochar = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ /* Wc's flags are on by default. */
+ if (doline + doword + dochar == 0)
+ doline = doword = dochar = 1;
+
+ total = 0;
+ if (!*argv) {
+ cnt(NULL);
+ (void)printf("\n");
+ }
+ else do {
+ cnt(*argv);
+ (void)printf(" %s\n", *argv);
+ ++total;
+ } while(*++argv);
+
+ if (total > 1) {
+ if (doline)
+ (void)printf(" %7ld", tlinect);
+ if (doword)
+ (void)printf(" %7ld", twordct);
+ if (dochar)
+ (void)printf(" %7ld", tcharct);
+ (void)printf(" total\n");
+ }
+ exit(0);
+}
+
+void
+cnt(file)
+ char *file;
+{
+ register u_char *p;
+ register short gotsp;
+ register int ch, len;
+ register u_long linect, wordct, charct;
+ struct stat sb;
+ int fd;
+ u_char buf[MAXBSIZE];
+
+ fd = STDIN_FILENO;
+ linect = wordct = charct = 0;
+ if (file) {
+ if ((fd = open(file, O_RDONLY, 0)) < 0)
+ err("%s: %s", file, strerror(errno));
+ if (doword)
+ goto word;
+ /*
+ * Line counting is split out because it's a lot faster to get
+ * lines than to get words, since the word count requires some
+ * logic.
+ */
+ if (doline) {
+ while (len = read(fd, buf, MAXBSIZE)) {
+ if (len == -1)
+ err("%s: %s", file, strerror(errno));
+ charct += len;
+ for (p = buf; len--; ++p)
+ if (*p == '\n')
+ ++linect;
+ }
+ tlinect += linect;
+ (void)printf(" %7lu", linect);
+ if (dochar) {
+ tcharct += charct;
+ (void)printf(" %7lu", charct);
+ }
+ (void)close(fd);
+ return;
+ }
+ /*
+ * If all we need is the number of characters and it's a
+ * regular or linked file, just stat the puppy.
+ */
+ if (dochar) {
+ if (fstat(fd, &sb))
+ err("%s: %s", file, strerror(errno));
+ if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) {
+ (void)printf(" %7qu", sb.st_size);
+ tcharct += sb.st_size;
+ (void)close(fd);
+ return;
+ }
+ }
+ }
+
+ /* Do it the hard way... */
+word: for (gotsp = 1; len = read(fd, buf, MAXBSIZE);) {
+ if (len == -1)
+ err("%s: %s", file, strerror(errno));
+ /*
+ * This loses in the presence of multi-byte characters.
+ * To do it right would require a function to return a
+ * character while knowing how many bytes it consumed.
+ */
+ charct += len;
+ for (p = buf; len--;) {
+ ch = *p++;
+ if (ch == '\n')
+ ++linect;
+ if (isspace(ch))
+ gotsp = 1;
+ else if (gotsp) {
+ gotsp = 0;
+ ++wordct;
+ }
+ }
+ }
+ if (doline) {
+ tlinect += linect;
+ (void)printf(" %7lu", linect);
+ }
+ if (doword) {
+ twordct += wordct;
+ (void)printf(" %7lu", wordct);
+ }
+ if (dochar) {
+ tcharct += charct;
+ (void)printf(" %7lu", charct);
+ }
+ (void)close(fd);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: wc [-clw] [files]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "wc: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/what/Makefile b/usr.bin/what/Makefile
new file mode 100644
index 0000000..41a3f42
--- /dev/null
+++ b/usr.bin/what/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= what
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/what/what.1 b/usr.bin/what/what.1
new file mode 100644
index 0000000..e3556cc
--- /dev/null
+++ b/usr.bin/what/what.1
@@ -0,0 +1,68 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)what.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt WHAT 1
+.Os BSD 4
+.Sh NAME
+.Nm what
+.Nd "show what versions of object modules were used to construct a file"
+.Sh SYNOPSIS
+.Nm what
+.Ar name Ar ...
+.Sh DESCRIPTION
+.Nm What
+reads each file
+.Ar name
+and searches for sequences of the form
+.Dq \&@(#)
+as inserted by the source code control system. It prints the remainder
+of the string following this marker, up to a null character, newline, double
+quote, or
+.Dq \&> character.
+.Sh BUGS
+As
+.Bx
+is not licensed to distribute
+.Tn SCCS
+this is a rewrite of the
+.Nm what
+command which is part of
+.Tn SCCS ,
+and may not behave exactly the same as that
+command does.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/what/what.c b/usr.bin/what/what.c
new file mode 100644
index 0000000..ee90cee
--- /dev/null
+++ b/usr.bin/what/what.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1980, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)what.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+/*
+ * what
+ */
+/* ARGSUSED */
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (!*++argv)
+ search();
+ else do {
+ if (!freopen(*argv, "r", stdin)) {
+ perror(*argv);
+ exit(1);
+ }
+ printf("%s\n", *argv);
+ search();
+ } while(*++argv);
+ exit(0);
+}
+
+search()
+{
+ register int c;
+
+ while ((c = getchar()) != EOF) {
+loop: if (c != '@')
+ continue;
+ if ((c = getchar()) != '(')
+ goto loop;
+ if ((c = getchar()) != '#')
+ goto loop;
+ if ((c = getchar()) != ')')
+ goto loop;
+ putchar('\t');
+ while ((c = getchar()) != EOF && c && c != '"' &&
+ c != '>' && c != '\n')
+ putchar(c);
+ putchar('\n');
+ }
+}
diff --git a/usr.bin/whatis/Makefile b/usr.bin/whatis/Makefile
new file mode 100644
index 0000000..8655a7c
--- /dev/null
+++ b/usr.bin/whatis/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= whatis
+SRCS= whatis.c config.c
+.PATH: ${.CURDIR}/../man
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/whatis/whatis.1 b/usr.bin/whatis/whatis.1
new file mode 100644
index 0000000..0f1b0a1
--- /dev/null
+++ b/usr.bin/whatis/whatis.1
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whatis.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt WHATIS 1
+.Os BSD 4
+.Sh NAME
+.Nm whatis
+.Nd describe what a command is
+.Sh SYNOPSIS
+.Nm whatis
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Ar command Ar ...
+.Sh DESCRIPTION
+.Nm Whatis
+looks up a given command and gives the header line from the manual page.
+You can then use the
+.Xr man 1
+command to get more information.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl M Ar path
+Override the list of standard directories
+.Nm whatis
+searches for its database named
+.Dq Pa whatis.db .
+The supplied
+.Ar path
+must be a colon
+.Dq \&:
+separated list of directories.
+This search path may also be set using the environment variable
+.Ev MANPATH .
+.It Fl m Ar path
+Augment the list of standard directories
+.Nm whatis
+searches for its database named
+.Dq Pa whatis.db .
+The supplied
+.Ar path
+must be a colon
+.Dq \&:
+separated list of directories.
+These directories will be searched before the standard directories
+or the directories supplied with the
+.Fl M
+option or the
+.Ev MANPATH
+environment variable are searched.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATH
+.It Ev MANPATH
+The standard search path used by
+.Xr man 1
+may be overridden by specifying a path in the
+.Ev MANPATH
+environment variable.
+.El
+.Sh FILES
+.Bl -tag -width whatis.db
+.It Pa whatis.db
+name of the whatis database
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr man 1 ,
+.Xr whereis 1
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/whatis/whatis.c b/usr.bin/whatis/whatis.c
new file mode 100644
index 0000000..904f0e4
--- /dev/null
+++ b/usr.bin/whatis/whatis.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)whatis.c 8.5 (Berkeley) 1/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../man/config.h"
+#include "../man/pathnames.h"
+
+#define MAXLINELEN 256 /* max line handled */
+
+static int *found, foundman;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ ENTRY *ep;
+ TAG *tp;
+ int ch, rv;
+ char *beg, *conffile, **p, *p_augment, *p_path;
+
+ conffile = NULL;
+ p_augment = p_path = NULL;
+ while ((ch = getopt(argc, argv, "C:M:m:P:")) != EOF)
+ switch (ch) {
+ case 'C':
+ conffile = optarg;
+ break;
+ case 'M':
+ case 'P': /* backward compatible */
+ p_path = optarg;
+ break;
+ case 'm':
+ p_augment = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1)
+ usage();
+
+ if ((found = malloc((u_int)argc * sizeof(int))) == NULL)
+ err(1, NULL);
+ memset(found, 0, argc * sizeof(int));
+
+ for (p = argv; *p; ++p) /* trim full paths */
+ if (beg = rindex(*p, '/'))
+ *p = beg + 1;
+
+ if (p_augment)
+ whatis(argv, p_augment, 1);
+ if (p_path || (p_path = getenv("MANPATH")))
+ whatis(argv, p_path, 1);
+ else {
+ config(conffile);
+ ep = (tp = getlist("_whatdb")) == NULL ?
+ NULL : tp->list.tqh_first;
+ for (; ep != NULL; ep = ep->q.tqe_next)
+ whatis(argv, ep->s, 0);
+ }
+
+ if (!foundman) {
+ fprintf(stderr, "whatis: no %s file found.\n", _PATH_WHATIS);
+ exit(1);
+ }
+ rv = 1;
+ for (p = argv; *p; ++p)
+ if (found[p - argv])
+ rv = 0;
+ else
+ printf("%s: not found\n", *p);
+ exit(rv);
+}
+
+whatis(argv, path, buildpath)
+ char **argv, *path;
+ int buildpath;
+{
+ register char *end, *name, **p;
+ char buf[MAXLINELEN + 1], wbuf[MAXLINELEN + 1];
+
+ for (name = path; name; name = end) { /* through name list */
+ if (end = index(name, ':'))
+ *end++ = '\0';
+
+ if (buildpath) {
+ char hold[MAXPATHLEN + 1];
+
+ (void)sprintf(hold, "%s/%s", name, _PATH_WHATIS);
+ name = hold;
+ }
+
+ if (!freopen(name, "r", stdin))
+ continue;
+
+ foundman = 1;
+
+ /* for each file found */
+ while (fgets(buf, sizeof(buf), stdin)) {
+ dashtrunc(buf, wbuf);
+ for (p = argv; *p; ++p)
+ if (match(wbuf, *p)) {
+ printf("%s", buf);
+ found[p - argv] = 1;
+
+ /* only print line once */
+ while (*++p)
+ if (match(wbuf, *p))
+ found[p - argv] = 1;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * match --
+ * match a full word
+ */
+match(bp, str)
+ register char *bp, *str;
+{
+ register int len;
+ register char *start;
+
+ if (!*str || !*bp)
+ return(0);
+ for (len = strlen(str);;) {
+ for (; *bp && !isdigit(*bp) && !isalpha(*bp); ++bp);
+ if (!*bp)
+ break;
+ for (start = bp++;
+ *bp && (*bp == '_' || isdigit(*bp) || isalpha(*bp)); ++bp);
+ if (bp - start == len && !strncasecmp(start, str, len))
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * dashtrunc --
+ * truncate a string at " - "
+ */
+dashtrunc(from, to)
+ register char *from, *to;
+{
+ register int ch;
+
+ for (; (ch = *from) && ch != '\n' &&
+ (ch != ' ' || from[1] != '-' || from[2] != ' '); ++from)
+ *to++ = ch;
+ *to = '\0';
+}
+
+/*
+ * usage --
+ * print usage message and die
+ */
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: whatis [-C file] [-M path] [-m path] command ...\n");
+ exit(1);
+}
diff --git a/usr.bin/whereis/Makefile b/usr.bin/whereis/Makefile
new file mode 100644
index 0000000..87bc659
--- /dev/null
+++ b/usr.bin/whereis/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= whereis
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/whereis/whereis.1 b/usr.bin/whereis/whereis.1
new file mode 100644
index 0000000..fa1f5a4
--- /dev/null
+++ b/usr.bin/whereis/whereis.1
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whereis.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt WHEREIS 1
+.Os BSD 3
+.Sh NAME
+.Nm whereis
+.Nd locate programs
+.Sh SYNOPSIS
+.Nm whereis
+.Op Ar program ...
+.Sh DESCRIPTION
+The
+.Nm whereis
+utility checks the standard binary directories for the specified programs,
+printing out the paths of any it finds.
+.Pp
+The path searched is the string returned by the
+.Xr sysctl 8
+utility for the
+.Dq user.cs_path
+string.
+.Sh SEE ALSO
+.Xr sysctl 8 ,
+.Sh COMPATIBILITY
+The historic flags and arguments for the
+.Nm whereis
+utility are no longer available in this version.
+.Sh HISTORY
+The
+.Nm whereis
+command appeared in 3.0BSD.
diff --git a/usr.bin/whereis/whereis.c b/usr.bin/whereis/whereis.c
new file mode 100644
index 0000000..8c75f5e
--- /dev/null
+++ b/usr.bin/whereis/whereis.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)whereis.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat sb;
+ size_t len;
+ int ch, sverrno, mib[2];
+ char *p, *t, *std, path[MAXPATHLEN];
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Retrieve the standard path. */
+ mib[0] = CTL_USER;
+ mib[1] = USER_CS_PATH;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1)
+ return (-1);
+ if (len == 0)
+ err(1, "user_cs_path: sysctl: zero length\n");
+ if ((std = malloc(len)) == NULL)
+ err(1, NULL);
+ if (sysctl(mib, 2, std, &len, NULL, 0) == -1) {
+ sverrno = errno;
+ free(std);
+ errno = sverrno;
+ err(1, "sysctl: user_cs_path");
+ }
+
+ /* For each path, for each program... */
+ for (; *argv; ++argv)
+ for (p = std;; *p++ = ':') {
+ t = p;
+ if ((p = strchr(p, ':')) != NULL) {
+ *p = '\0';
+ if (t == p)
+ t = ".";
+ } else
+ if (strlen(t) == 0)
+ t = ".";
+ (void)snprintf(path, sizeof(path), "%s/%s", t, *argv);
+ if (!stat(path, &sb))
+ (void)printf("%s\n", path);
+ if (p == NULL)
+ break;
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "whereis: program ...\n");
+ exit (1);
+}
diff --git a/usr.bin/which/Makefile b/usr.bin/which/Makefile
new file mode 100644
index 0000000..af5532e
--- /dev/null
+++ b/usr.bin/which/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+MAN1= which.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/which.pl ${DESTDIR}${BINDIR}/which
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/which/which.1 b/usr.bin/which/which.1
new file mode 100644
index 0000000..b944188
--- /dev/null
+++ b/usr.bin/which/which.1
@@ -0,0 +1,64 @@
+.\" Manpage Copyright (c) 1995, Jordan Hubbard <jkh@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the FreeBSD Project
+.\" its contributors.
+.\" 4. Neither the name of the FreeBSD Project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+#
+.\" THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: which.1,v 1.2 1995/01/28 17:35:54 asami Exp $
+.Dd January 26, 1995
+.Dt WHICH 1
+.Os FreeBSD
+.Sh NAME
+.Nm which
+.Nd "locate a program file in the user's path"
+.Sh SYNOPSIS
+.Nm which
+.Op Fl as
+.Op Ar command
+.Ar ...
+.Sh DESCRIPTION
+.Nm Which
+takes a list of command names and searches the path for each executable
+file that would be run had these commands actually been invoked.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+List all instances of executables found (instead of just the first one
+of each).
+.It Fl s
+No output, just return 0 if any of the executables are found, or 1 if
+none are found.
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD 2.1
+.Sh SEE ALSO
+.Xr perl 1
+.Sh AUTHOR
+The PERL script for this more modern version of
+.Nm which
+was written by Wolfram Schneider <wosch@cs.tu-berlin.de>.
diff --git a/usr.bin/which/which.pl b/usr.bin/which/which.pl
new file mode 100755
index 0000000..edf4c20
--- /dev/null
+++ b/usr.bin/which/which.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+#
+# Copyright (C) 1995, Wolfram Schneider <wosch@cs.tu-berlin.de>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Wolfram Schneider
+# and the FreeBSD Project.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# [whew!]
+#
+# $Id: which.pl,v 1.5 1995/07/31 04:22:07 asami Exp $
+
+$all = 0;
+$silent = 0;
+$found = 0;
+@path = split(/:/, $ENV{'PATH'});
+
+if ($ARGV[0] eq "-a") {
+ $all = 1; shift @ARGV;
+} elsif ($ARGV[0] eq "-s") {
+ $silent = 1; shift @ARGV;
+} elsif ($ARGV[0] =~ /^-(h|help|\?)$/) {
+ die "usage:\n\twhich [-a] [-s] program ...\n";
+}
+
+foreach $prog (@ARGV) {
+ if ("$prog" =~ '/' && -x "$prog") {
+ print "$prog\n" unless $silent;
+ $found = 1;
+ } else {
+ foreach $e (@path) {
+ if (-x "$e/$prog") {
+ print "$e/$prog\n" unless $silent;
+ $found = 1;
+ last unless $all;
+ }
+ }
+ }
+}
+
+exit (!$found);
diff --git a/usr.bin/which/which.sh b/usr.bin/which/which.sh
new file mode 100755
index 0000000..7167140
--- /dev/null
+++ b/usr.bin/which/which.sh
@@ -0,0 +1,62 @@
+#!/bin/csh
+#
+#
+# Copyright (C) 1994, Joerg Wunsch <joerg@freebsd.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# [whew!]
+#
+# $Id$
+
+foreach i ($argv)
+ echo -n "${i}: "
+
+ set a = `alias $i`
+
+ if ("$a" != "") then # test for alias
+ echo "aliased to $a"
+ else if ("$i" =~ /*) then # absolute path name
+ if (! -x "$i") then
+ echo "not found"
+ else
+ echo "$i"
+ endif
+ else # search $path
+ set found = 0
+ foreach p ($path)
+
+ if (-x "$p/$i") then
+ echo "$p/$i"
+ set found = 1
+ break
+ endif
+ end
+ if ($found == 0) echo "not found"
+ endif
+end
diff --git a/usr.bin/who/Makefile b/usr.bin/who/Makefile
new file mode 100644
index 0000000..8695ca2
--- /dev/null
+++ b/usr.bin/who/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= who
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/who/who.1 b/usr.bin/who/who.1
new file mode 100644
index 0000000..f8ac307
--- /dev/null
+++ b/usr.bin/who/who.1
@@ -0,0 +1,105 @@
+.\" Copyright (c) 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)who.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt WHO 1
+.Os
+.Sh NAME
+.Nm who
+.Nd display who is logged in
+.Sh SYNOPSIS
+.Nm who
+.Op Ar am I
+.Op Ar file
+.Sh DESCRIPTION
+The utility
+.Nm who
+displays
+a list of all users currently logged on, showing for each user
+the login name,
+tty name, the date and time of login, and hostname if not local.
+.Pp
+Available options:
+.Pp
+.Bl -tag -width file
+.It Ar \&am I
+Returns the invoker's real user name.
+.It Ar file
+By default,
+.Nm who
+gathers information from the file
+.Pa /var/run/utmp .
+An alternate
+.Ar file
+may be specified which is usually
+.Pa /var/run/wtmp
+(or
+.Pa /var/run/wtmp.[0-6]
+depending on site policy as
+.Pa wtmp
+can grow quite large and daily versions may or may not
+be kept around after compression by
+.Xr ac 8 ) .
+The
+.Pa wtmp
+file contains a record of every login, logout,
+crash, shutdown and date change
+since
+.Pa wtmp
+was last truncated or
+created.
+.El
+.Pp
+If
+.Pa /var/log/wtmp
+is being used as the file, the user name may be empty
+or one of the special characters '|', '}' and '~'. Logouts produce
+an output line without any user name. For more information on the
+special characters, see
+.Xr utmp 5 .
+.Sh FILES
+.Bl -tag -width /var/log/wtmp.[0-6] -compact
+.It Pa /var/run/utmp
+.It Pa /var/log/wtmp
+.It Pa /var/log/wtmp.[0-6]
+.El
+.Sh SEE ALSO
+.Xr last 1 ,
+.Xr users 1 ,
+.Xr getuid 2 ,
+.Xr utmp 5
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/who/who.c b/usr.bin/who/who.c
new file mode 100644
index 0000000..77a131c
--- /dev/null
+++ b/usr.bin/who/who.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Fischbein.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <time.h>
+#include <pwd.h>
+#include <utmp.h>
+#include <stdio.h>
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *p;
+ struct utmp usr;
+ struct passwd *pw;
+ FILE *ufp, *file();
+ char *t, *rindex(), *strcpy(), *strncpy(), *ttyname();
+
+ switch (argc) {
+ case 1: /* who */
+ ufp = file(_PATH_UTMP);
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
+ if (*usr.ut_name && *usr.ut_line)
+ output(&usr);
+ break;
+ case 2: /* who utmp_file */
+ ufp = file(argv[1]);
+ /* all entries */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
+ output(&usr);
+ break;
+ case 3: /* who am i */
+ ufp = file(_PATH_UTMP);
+
+ /* search through the utmp and find an entry for this tty */
+ if (p = ttyname(0)) {
+ /* strip any directory component */
+ if (t = rindex(p, '/'))
+ p = t + 1;
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
+ if (usr.ut_name && !strcmp(usr.ut_line, p)) {
+ output(&usr);
+ exit(0);
+ }
+ /* well, at least we know what the tty is */
+ (void)strncpy(usr.ut_line, p, UT_LINESIZE);
+ } else
+ (void)strcpy(usr.ut_line, "tty??");
+ pw = getpwuid(getuid());
+ (void)strncpy(usr.ut_name, pw ? pw->pw_name : "?", UT_NAMESIZE);
+ (void)time(&usr.ut_time);
+ *usr.ut_host = '\0';
+ output(&usr);
+ break;
+ default:
+ (void)fprintf(stderr, "usage: who [ file ]\n who am i\n");
+ exit(1);
+ }
+ exit(0);
+}
+
+output(up)
+ struct utmp *up;
+{
+ char buf[80];
+
+ (void)printf("%-*.*s %-*.*s", UT_NAMESIZE, UT_NAMESIZE, up->ut_name,
+ UT_LINESIZE, UT_LINESIZE, up->ut_line);
+ (void)strftime(buf, sizeof(buf), "%c", localtime(&up->ut_time));
+ (void)printf("%.12s", buf + 4);
+ if (*up->ut_host)
+ printf("\t(%.*s)", UT_HOSTSIZE, up->ut_host);
+ (void)putchar('\n');
+}
+
+FILE *
+file(name)
+ char *name;
+{
+ extern int errno;
+ FILE *ufp;
+ char *strerror();
+
+ if (!(ufp = fopen(name, "r"))) {
+ (void)fprintf(stderr, "who: %s: %s.\n", name, strerror(errno));
+ exit(1);
+ }
+ return(ufp);
+}
diff --git a/usr.bin/whois/Makefile b/usr.bin/whois/Makefile
new file mode 100644
index 0000000..e6ab08f
--- /dev/null
+++ b/usr.bin/whois/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= whois
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/whois/whois.1 b/usr.bin/whois/whois.1
new file mode 100644
index 0000000..dd9d81a
--- /dev/null
+++ b/usr.bin/whois/whois.1
@@ -0,0 +1,80 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whois.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt WHOIS 1
+.Os BSD 4.3
+.Sh NAME
+.Nm whois
+.Nd Internet user name directory service
+.Sh SYNOPSIS
+.Nm whois
+.Op Fl h Ar hostname
+.Ar name ...
+.Sh DESCRIPTION
+.Nm Whois
+looks up records in the Network Information Center
+.Pq Tn NIC
+database.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl h
+Use the specified host instead of the default NIC (nic.ddn.mil).
+.El
+.Pp
+The operands specified to
+.Nm whois
+are concatenated together (separated by white-space) and presented to
+the
+.Nm whois
+server.
+.Pp
+The default action, unless directed otherwise with a special
+.Ar name ,
+is to do a very broad search, looking for matches to
+.Ar name
+in all types of records and most fields (name, nicknames, hostname, net
+address, etc.) in the database.
+For more information as to what
+.Ar name
+operands have special meaning, and how to guide the search, use
+the special name
+.Dq Ar help .
+.Sh SEE ALSO
+RFC 812: Nicname/Whois
+.Sh HISTORY
+The
+.Nm whois
+command appeared in
+.Bx 4.3 .
diff --git a/usr.bin/whois/whois.c b/usr.bin/whois/whois.c
new file mode 100644
index 0000000..77d6c50
--- /dev/null
+++ b/usr.bin/whois/whois.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)whois.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#define NICHOST "whois.internic.net"
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ register FILE *sfi, *sfo;
+ register int ch;
+ struct sockaddr_in sin;
+ struct hostent *hp;
+ struct servent *sp;
+ int s;
+ char *host;
+
+ host = NICHOST;
+ while ((ch = getopt(argc, argv, "h:")) != EOF)
+ switch((char)ch) {
+ case 'h':
+ host = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ (void)fprintf(stderr, "whois: %s: ", host);
+ herror((char *)NULL);
+ exit(1);
+ }
+ host = hp->h_name;
+ s = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("whois: socket");
+ exit(1);
+ }
+ bzero((caddr_t)&sin, sizeof (sin));
+ sin.sin_family = hp->h_addrtype;
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("whois: bind");
+ exit(1);
+ }
+ bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
+ sp = getservbyname("whois", "tcp");
+ if (sp == NULL) {
+ (void)fprintf(stderr, "whois: whois/tcp: unknown service\n");
+ exit(1);
+ }
+ sin.sin_port = sp->s_port;
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ perror("whois: connect");
+ exit(1);
+ }
+ sfi = fdopen(s, "r");
+ sfo = fdopen(s, "w");
+ if (sfi == NULL || sfo == NULL) {
+ perror("whois: fdopen");
+ (void)close(s);
+ exit(1);
+ }
+ while (argc-- > 1)
+ (void)fprintf(sfo, "%s ", *argv++);
+ (void)fprintf(sfo, "%s\r\n", *argv);
+ (void)fflush(sfo);
+ while ((ch = getc(sfi)) != EOF)
+ putchar(ch);
+ exit(0);
+}
+
+usage()
+{
+ (void)fprintf(stderr, "usage: whois [-h hostname] name ...\n");
+ exit(1);
+}
diff --git a/usr.bin/window/:tt b/usr.bin/window/:tt
new file mode 100644
index 0000000..7ec4352
--- /dev/null
+++ b/usr.bin/window/:tt
@@ -0,0 +1,11 @@
+./"init"16t"end"n2p
++/"move"16t"insline"16t"delline"16t"delchar"n4p
++/"write"16t"putc"16tn2p
++/"clreol"16t"clreos"16t"clear"n3p
++/"scroll_down"16t"scroll_up"16t"setscroll"n3p
++/"setinsert"16t"setmodes"n2p
++/"modes"8t"nmodes"8t"insert"8t"ninsert"n4b
++/"row"16t"col"16t"scrtop"16t"sclbot"n4D
++/"nrow"16t"ncol"16t"hasins"8t"avlmods"8t"wrap"8t"retain"n2D4b
++/"frame"
+*./16b
diff --git a/usr.bin/window/:tty b/usr.bin/window/:tty
new file mode 100644
index 0000000..2eb61ef
--- /dev/null
+++ b/usr.bin/window/:tty
@@ -0,0 +1,6 @@
+./"sgttyb:"8t"ispeed"8t"ospeed"8t"erase"8t"kill"8t"flags"n8tbbC8tC8tx
++/"tchars:"8t"intrc"8t"quitc"8t"startc"8t"stopc"8t"eofc"8t"brkc"
++/8tC8tC8tC8tC8tC8tC
++/"ltchars:suspc"8t"dsuspc"8t"rprntc"8t"flushc"8t"werasc"8t"lnextc"
++/8tC8tC8tC8tC8tC8tC2+
++/"lmode"16t"ldisc"16t"fflags"nXDX
diff --git a/usr.bin/window/:var b/usr.bin/window/:var
new file mode 100644
index 0000000..ff3de6b
--- /dev/null
+++ b/usr.bin/window/:var
@@ -0,0 +1,2 @@
+./"left"16t"right"16t"v_type"8t"v_val"npp4+b3+x
+*(.+8)/"name: "S
diff --git a/usr.bin/window/:ww b/usr.bin/window/:ww
new file mode 100644
index 0000000..0e99f0a
--- /dev/null
+++ b/usr.bin/window/:ww
@@ -0,0 +1,19 @@
+./"state"8t"oflags"nbb++
++/"forw"16t"back"16t"index"8t"order"nppbb++
++/"w.nr"16t"w.nc"nDD
++/"w.t"16t"w.b"16t"w.l"16t"w.r"nDDDD
++/"b.nr"16t"b.nc"nDD
++/"b.t"16t"b.b"16t"b.l"16t"b.r"nDDDD
++/"i.nr"16t"i.nc"nDD
++/"i.t"16t"i.b"16t"i.l"16t"i.r"nDDDD
++/"cur.r"16t"cur.c"nDD
++/"win"16t"buf"16t"fmap"16t"nvis"npppp
++/"wstate"8t"modes"8t"insert"8t"mapnl"8t"noupd"n5b
++/"unctrl"8t"nointr"8t"hascurs"8t"hasframe"n4b
++/"ispty"8t"stopped"8t"pty"16t"socket"16t"pid"nbb+3D
++/"ttyname"n11C+
++/"ob"16t"obe"16t"obp"16t"obq"n4p
++/"center"8t"id"8t"label"nbb++p
++/"alt.nr"16t"alt.nc"nDD
++/"alt.t"16t"alt.b"16t"alt.l"16t"alt.r"nDDDD
++/t
diff --git a/usr.bin/window/Makefile b/usr.bin/window/Makefile
new file mode 100644
index 0000000..fba3587
--- /dev/null
+++ b/usr.bin/window/Makefile
@@ -0,0 +1,21 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= window
+SRCS= char.c cmd.c cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c \
+ context.c error.c lcmd.c lcmd1.c lcmd2.c main.c mloop.c parser1.c \
+ parser2.c parser3.c parser4.c parser5.c scanner.c startup.c string.c \
+ ttf100.c ttgeneric.c tth19.c tth29.c ttinit.c ttoutput.c tttermcap.c \
+ tttvi925.c ttwyse60.c ttwyse75.c ttzapple.c ttzentec.c var.c win.c \
+ wwadd.c wwalloc.c wwbox.c wwchild.c wwclose.c wwclreol.c wwclreos.c \
+ wwcursor.c wwdata.c wwdelchar.c wwdelete.c wwdelline.c wwdump.c \
+ wwend.c wwenviron.c wwerror.c wwflush.c wwframe.c wwgets.c wwinit.c \
+ wwinschar.c wwinsline.c wwiomux.c wwlabel.c wwmisc.c wwmove.c \
+ wwopen.c wwprintf.c wwpty.c wwputc.c wwputs.c wwredraw.c \
+ wwredrawwin.c wwrint.c wwscroll.c wwsize.c wwspawn.c wwsuspend.c \
+ wwterminfo.c wwtty.c wwunframe.c wwupdate.c wwwrite.c xx.c xxflush.c \
+ compress.c
+MAN= window.1
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/window/README b/usr.bin/window/README
new file mode 100644
index 0000000..42d45b3
--- /dev/null
+++ b/usr.bin/window/README
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)README 8.1 (Berkeley) 6/6/93
+ */
+
+Compilation notes:
+
+ Compiler options:
+
+ BYTE_ORDER (used only in ww.h)
+ It should already be defined in machine/endian.h.
+ The code knows about BIG_ENDIAN, LITTLE_ENDIAN, and PDP_ENDIAN.
+ It only cares about byte order in words, so PDP_ENDIAN
+ is the same as LITTLE_ENDIAN.
+ OLD_TTY
+ If you don't have Posix termios, then define this.
+ VMIN_BUG
+ Even if you have Posix termios, define this if the MIN and TIME
+ feature in noncanonical mode doesn't work correctly.
+
+ Ok, there's another one, STR_DEBUG. It turns on consistency checks
+ in the string allocator. It's been left on since performace doesn't
+ seem to suffer. There's an abort() somewhere when an inconsistency
+ is found. It hasn't happened in years.
+
+ The file local.h contains locally tunable constants.
+
+ The makefile used to be updated with mkmf; it has been changed
+at various times to use cpp -M and, currently, mkdep. The only library
+it needs is termcap.
+
+ Window, as is, only runs on 4.3 (or later) machines.
+
+ On 4.2 machines, at least these modifications must be done:
+
+ delete uses of window size ioctls: TIOCGWINSZ, TIOCSWINSZ,
+ struct winsize
+ add to ww.h
+ typedef int fd_set;
+ #define FD_ZERO(s) (*(s) = 0)
+ #define FD_SET(b, s) (*(s) |= 1 << (b))
+ #define FD_ISSET(b, s) (*(s) & 1 << (b))
+ add to ww.h
+ #define sigmask(s) (1 << (s) - 1)
+
+
+A few notes about the internals:
+
+ The window package. Windows are opened by calling wwopen().
+Wwwrite() is the primitive for writing to windows. Wwputc(), wwputs(),
+and wwprintf() are also supported. Some of the outputs to windows are
+delayed. Wwupdate() updates the terminal to match the internal screen
+buffer. Wwspawn() spawns a child process on the other end of a window,
+with its environment tailored to the window. Visible windows are
+doubly linked in the order of their overlap. Wwadd() inserts a window
+into the list at a given place. Wwdelete() deletes it. Windows not in
+the list are not visible, though wwwrite() still works. Window was
+written before the days of X and Sunview, so some of the terminology
+is not standard.
+
+ Most functions return -1 on error. Wwopen() returns the null
+pointer. An error number is saved in wwerrno. Wwerror() returns an
+error string based on wwerrno suitable for printing.
+
+ The terminal drivers perform all output to the physical terminal,
+including special functions like character and line insertion and
+deletion. The window package keeps a list of known terminals. At
+initialization time, the terminal type is matched against the list to
+find the right terminal driver to use. The last driver, the generic
+driver, matches all terminals and uses the termcap database. The
+interface between the window package the terminal driver is the `tt'
+structure. It contains pointers to functions to perform special
+functions and terminal output, as well as flags about the
+characteristics of the terminal. Most of these ideas are borrowed
+from the Maryland window package, which in turn is based on Goslin's
+Emacs.
+
+ The IO system is semi-synchronous. Terminal input is signal
+driven, and everything else is done synchronously with a single
+select(). It is roughly event-driven, though not in a clean way.
+
+ Normally, in both conversation mode and command mode, window
+sleeps in a select() in wwiomux() waiting for data from the
+pseudo-terminals. At the same time, terminal input causes SIGIO which
+is caught by wwrint(). The select() returns when at least one of the
+pseudo-terminals becomes ready for reading.
+
+ Wwrint() is the interrupt handler for tty input. It reads input
+into a linear buffer accessed through four pointers:
+
+ +-------+--------------+----------------+
+ | empty | data | empty |
+ +-------+--------------+----------------+
+ ^ ^ ^ ^
+ | | | |
+ wwib wwibp wwibq wwibe
+
+Wwrint() appends characters at the end and increments wwibq (*wwibq++
+= c), and characters are taken off the buffer at wwibp using the
+wwgetc() and wwpeekc() macros. As is the convention in C, wwibq
+and wwibe point to one position beyond the end. In addition,
+wwrint() will do a longjmp(wwjmpbuf) if wwsetjmp is true. This is
+used by wwiomux() to interrupt the select() which would otherwise
+resume after the interrupt. (Actually, I hear this is not true,
+but the longjmp feature is used to avoid a race condition as well.
+Anyway, it means I didn't have to depend on a feature in a
+daily-changing kernel, but that's another story.) The macro
+wwinterrupt() returns true if the input buffer is non-empty.
+Wwupdate(), wwwrite(), and wwiomux() check this condition and will
+return at the first convenient opportunity when it becomes true.
+In the case of wwwrite(), the flag ww_nointr in the window structure
+overrides this. This feature allows the user to interrupt lengthy
+outputs safely. The structure of the input buffer is designed to
+avoid race conditions without blocking interrupts.
+
+ Actually, wwsetjmp and wwinterrupt() are part of a software
+interrupt scheme used by the two interrupt catchers wwrint() and
+wwchild(). Asserting the interrupt lets the synchronous parts of
+the program know that there's an interesting asynchronous condition
+(i.e., got a keyboard character, or a child process died) that they
+might want to process before anything else. The synchronous routines
+can check for this condition with wwinterrupt() or by arranging
+that a longjmp() be done.
+
+ Wwiomux() copies pseudo-terminal output into their corresponding
+windows. Without anything to do, it blocks in a select(), waiting for
+read ready on pseudo-terminals. Reads are done into per-window buffers
+in the window structures. When there is at least one buffer non-empty,
+wwiomux() finds the top most of these windows and writes it using
+wwwrite(). Then the process is repeated. A non-blocking select() is
+done after a wwwrite() to pick up any output that may have come in
+during the write, which may take a long time. Specifically, we use
+this to stop output or flush buffer when a pseudo-terminal tells us to
+(we use pty packet mode). The select() blocks only when all of the
+windows' buffers are empty. A wwupdate() is done prior to this, which
+is the only time the screen is guaranteed to be completely up to date.
+Wwiomux() loops until wwinterrupt() becomes true.
+
+ The top level routine for all this is mloop(). In conversation
+mode, it simply calls wwiomux(), which only returns when input is
+available. The input buffer is then written to the pseudo-terminal of
+the current window. If the escape character is found in the input,
+command mode is entered. Otherwise, the process is repeated. In
+command mode, control is transferred to docmd() which returns only when
+conversation mode is reentered. Docmd() and other command processing
+routines typically wait for input in a loop:
+
+ while (wwpeekc() < 0)
+ wwiomux();
+
+When the loop terminates, wwgetc() is used to read the input buffer.
+
+ Output to the physical terminal is handled by the lowest level
+routines of the window package, in the files ttoutput.c and tt.h. The
+standard IO package is not used, to get better control over buffering
+and to use non-blocking reads in wwrint(). The buffer size is set to
+approximately one second of output time, based on the baudrate.
+
+ The result of all this complexity is faster response time,
+especially in output stopping and flushing. Wwwrite() checks
+wwinterrupt() after every line. It also calls wwupdate() for each line
+it writes. The output buffer is limited to one second of output time.
+Thus, there is usually only a delay of one to two lines plus one second
+after a ^C or ^S. Also, commands that produce lengthy output can be
+aborted without actually showing all of it on the terminal. (Try the
+'?' command followed by escape immediately.)
diff --git a/usr.bin/window/alias.h b/usr.bin/window/alias.h
new file mode 100644
index 0000000..ea63b49
--- /dev/null
+++ b/usr.bin/window/alias.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)alias.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define alias var
+#define a_name r_name
+#define a_buf r_val.v_str
+#define a_flags r_val.v_type
+
+ /* a_flags bits, must not interfere with v_type values */
+#define A_INUSE 0x010 /* already inuse */
+
+#define alias_set(n, s) var_setstr1(&alias_head, n, s)
+#define alias_walk(f, a) var_walk1(alias_head, f, a)
+#define alias_unset(n) var_unset1(&alias_head, n)
+#define alias_lookup(n) (*var_lookup1(&alias_head, n))
+
+struct var *alias_head;
diff --git a/usr.bin/window/char.c b/usr.bin/window/char.c
new file mode 100644
index 0000000..670d5f1
--- /dev/null
+++ b/usr.bin/window/char.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)char.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "char.h"
+
+char *_unctrl[] = {
+ "^@", "^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", "^[", "^\\", "^]", "^^", "^_",
+ " ", "!", "\"", "#", "$", "%", "&", "'",
+ "(", ")", "*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", ":", ";", "<", "=", ">", "?",
+ "@", "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", "[", "\\", "]", "^", "_",
+ "`", "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", "{", "|", "}", "~", "^?",
+ "\\200","\\201","\\202","\\203","\\204","\\205","\\206","\\207",
+ "\\210","\\211","\\212","\\213","\\214","\\215","\\216","\\217",
+ "\\220","\\221","\\222","\\223","\\224","\\225","\\226","\\227",
+ "\\230","\\231","\\232","\\233","\\234","\\235","\\236","\\237",
+ "\\240","\\241","\\242","\\243","\\244","\\245","\\246","\\247",
+ "\\250","\\251","\\252","\\253","\\254","\\255","\\256","\\257",
+ "\\260","\\261","\\262","\\263","\\264","\\265","\\266","\\267",
+ "\\270","\\271","\\272","\\273","\\274","\\275","\\276","\\277",
+ "\\300","\\301","\\302","\\303","\\304","\\305","\\306","\\307",
+ "\\310","\\311","\\312","\\313","\\314","\\315","\\316","\\317",
+ "\\320","\\321","\\322","\\323","\\324","\\325","\\326","\\327",
+ "\\330","\\331","\\332","\\333","\\334","\\335","\\336","\\337",
+ "\\340","\\341","\\342","\\343","\\344","\\345","\\346","\\347",
+ "\\350","\\351","\\352","\\353","\\354","\\355","\\356","\\357",
+ "\\360","\\361","\\362","\\363","\\364","\\365","\\366","\\367",
+ "\\370","\\371","\\372","\\373","\\374","\\375","\\376","\\377"
+};
diff --git a/usr.bin/window/char.h b/usr.bin/window/char.h
new file mode 100644
index 0000000..dd4ad27
--- /dev/null
+++ b/usr.bin/window/char.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)char.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Macros and things to deal with control characters.
+ *
+ * Unctrl() is just like the standard function, except we don't want
+ * to include curses.
+ * Isctrl() returns true for all characters less than space and
+ * greater than or equal to delete.
+ * Isprt() is tab and all characters not isctrl(). It's used
+ * by wwwrite().
+ * Isunctrl() includes all characters that should be expanded
+ * using unctrl() by wwwrite() if ww_unctrl is set.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+extern char *_unctrl[];
+#define ctrl(c) (c & 0x1f)
+#define unctrl(c) (_unctrl[(unsigned char) (c)])
+#define isctrl(c) iscntrl((unsigned char)(c))
+#define isprt(c) isprint((unsigned char)(c))
+#define isunctrl(c) (strchr("\b\t\n\r", (c)) == NULL && isctrl(c))
diff --git a/usr.bin/window/cmd.c b/usr.bin/window/cmd.c
new file mode 100644
index 0000000..391e346
--- /dev/null
+++ b/usr.bin/window/cmd.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "char.h"
+
+docmd()
+{
+ register c;
+ register struct ww *w;
+ char out = 0;
+
+ while (!out && !quit) {
+ if ((c = wwgetc()) < 0) {
+ if (terse)
+ wwsetcursor(0, 0);
+ else {
+ wwputs("Command: ", cmdwin);
+ wwcurtowin(cmdwin);
+ }
+ do
+ wwiomux();
+ while ((c = wwgetc()) < 0);
+ }
+ if (!terse)
+ wwputc('\n', cmdwin);
+ switch (c) {
+ default:
+ if (c != escapec)
+ break;
+ case 'h': case 'j': case 'k': case 'l':
+ case 'y': case 'p':
+ case ctrl('y'):
+ case ctrl('e'):
+ case ctrl('u'):
+ case ctrl('d'):
+ case ctrl('b'):
+ case ctrl('f'):
+ case ctrl('s'):
+ case ctrl('q'):
+ case ctrl('['):
+ if (selwin == 0) {
+ error("No window.");
+ continue;
+ }
+ }
+ switch (c) {
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if ((w = window[c - '1']) == 0) {
+ error("%c: No such window.", c);
+ break;
+ }
+ setselwin(w);
+ if (checkproc(selwin) >= 0)
+ out = 1;
+ break;
+ case '%':
+ if ((w = getwin()) != 0)
+ setselwin(w);
+ break;
+ case ctrl('^'):
+ if (lastselwin != 0) {
+ setselwin(lastselwin);
+ if (checkproc(selwin) >= 0)
+ out = 1;
+ } else
+ error("No previous window.");
+ break;
+ case 'c':
+ if ((w = getwin()) != 0)
+ closewin(w);
+ break;
+ case 'w':
+ c_window();
+ break;
+ case 'm':
+ if ((w = getwin()) != 0)
+ c_move(w);
+ break;
+ case 'M':
+ if ((w = getwin()) != 0)
+ movewin(w, w->ww_alt.t, w->ww_alt.l);
+ break;
+ case 's':
+ if ((w = getwin()) != 0)
+ c_size(w);
+ break;
+ case 'S':
+ if ((w = getwin()) != 0)
+ sizewin(w, w->ww_alt.nr, w->ww_alt.nc);
+ break;
+ case 'y':
+ c_yank();
+ break;
+ case 'p':
+ c_put();
+ break;
+ case ':':
+ c_colon();
+ break;
+ case 'h':
+ (void) wwwrite(selwin, "\b", 1);
+ break;
+ case 'j':
+ (void) wwwrite(selwin, "\n", 1);
+ break;
+ case 'k':
+ (void) wwwrite(selwin, "\033A", 2);
+ break;
+ case 'l':
+ (void) wwwrite(selwin, "\033C", 2);
+ break;
+ case ctrl('e'):
+ wwscroll(selwin, 1);
+ break;
+ case ctrl('y'):
+ wwscroll(selwin, -1);
+ break;
+ case ctrl('d'):
+ wwscroll(selwin, selwin->ww_w.nr / 2);
+ break;
+ case ctrl('u'):
+ wwscroll(selwin, - selwin->ww_w.nr / 2);
+ break;
+ case ctrl('f'):
+ wwscroll(selwin, selwin->ww_w.nr);
+ break;
+ case ctrl('b'):
+ wwscroll(selwin, - selwin->ww_w.nr);
+ break;
+ case ctrl('s'):
+ stopwin(selwin);
+ break;
+ case ctrl('q'):
+ startwin(selwin);
+ break;
+ case ctrl('l'):
+ wwredraw();
+ break;
+ case '?':
+ c_help();
+ break;
+ case ctrl('['):
+ if (checkproc(selwin) >= 0)
+ out = 1;
+ break;
+ case ctrl('z'):
+ wwsuspend();
+ break;
+ case 'q':
+ c_quit();
+ break;
+ /* debugging stuff */
+ case '&':
+ if (debug) {
+ c_debug();
+ break;
+ }
+ default:
+ if (c == escapec) {
+ if (checkproc(selwin) >= 0) {
+ (void) write(selwin->ww_pty,
+ &escapec, 1);
+ out = 1;
+ }
+ } else {
+ if (!terse)
+ wwbell();
+ error("Type ? for help.");
+ }
+ }
+ }
+ if (!quit)
+ setcmd(0);
+}
+
+struct ww *
+getwin()
+{
+ register int c;
+ struct ww *w = 0;
+
+ if (!terse)
+ wwputs("Which window? ", cmdwin);
+ wwcurtowin(cmdwin);
+ while ((c = wwgetc()) < 0)
+ wwiomux();
+ if (debug && c == 'c')
+ w = cmdwin;
+ else if (debug && c == 'f')
+ w = framewin;
+ else if (debug && c == 'b')
+ w = boxwin;
+ else if (c >= '1' && c < NWINDOW + '1')
+ w = window[c - '1'];
+ else if (c == '+')
+ w = selwin;
+ else if (c == '-')
+ w = lastselwin;
+ if (w == 0)
+ wwbell();
+ if (!terse)
+ wwputc('\n', cmdwin);
+ return w;
+}
+
+checkproc(w)
+struct ww *w;
+{
+ if (w->ww_state != WWS_HASPROC) {
+ error("No process in window.");
+ return -1;
+ }
+ return 0;
+}
+
+setcmd(new)
+char new;
+{
+ if (new && !incmd) {
+ if (!terse)
+ wwadd(cmdwin, &wwhead);
+ if (selwin != 0)
+ wwcursor(selwin, 1);
+ wwcurwin = 0;
+ } else if (!new && incmd) {
+ if (!terse) {
+ wwdelete(cmdwin);
+ reframe();
+ }
+ if (selwin != 0)
+ wwcursor(selwin, 0);
+ wwcurwin = selwin;
+ }
+ incmd = new;
+}
+
+setterse(new)
+char new;
+{
+ if (incmd)
+ if (new && !terse) {
+ wwdelete(cmdwin);
+ reframe();
+ } else if (!new && terse)
+ wwadd(cmdwin, &wwhead);
+ terse = new;
+}
+
+/*
+ * Set the current window.
+ */
+setselwin(w)
+struct ww *w;
+{
+ if (selwin == w)
+ return;
+ if (selwin != 0)
+ lastselwin = selwin;
+ if ((selwin = w) != 0)
+ front(selwin, 1);
+}
diff --git a/usr.bin/window/cmd1.c b/usr.bin/window/cmd1.c
new file mode 100644
index 0000000..6fdc9da
--- /dev/null
+++ b/usr.bin/window/cmd1.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd1.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "char.h"
+
+c_window()
+{
+ int col, row, xcol, xrow;
+ int id;
+
+ if ((id = findid()) < 0)
+ return;
+ if (!terse)
+ wwputs("New window (upper left corner): ", cmdwin);
+ col = 0;
+ row = 1;
+ wwadd(boxwin, framewin->ww_back);
+ for (;;) {
+ wwbox(boxwin, row - 1, col - 1, 3, 3);
+ wwsetcursor(row, col);
+ while (wwpeekc() < 0)
+ wwiomux();
+ switch (getpos(&row, &col, row > 1, 0,
+ wwnrow - 1, wwncol - 1)) {
+ case 3:
+ wwunbox(boxwin);
+ wwdelete(boxwin);
+ return;
+ case 2:
+ wwunbox(boxwin);
+ break;
+ case 1:
+ wwunbox(boxwin);
+ case 0:
+ continue;
+ }
+ break;
+ }
+ if (!terse)
+ wwputs("\nNew window (lower right corner): ", cmdwin);
+ xcol = col;
+ xrow = row;
+ for (;;) {
+ wwbox(boxwin, row - 1, col - 1,
+ xrow - row + 3, xcol - col + 3);
+ wwsetcursor(xrow, xcol);
+ while (wwpeekc() < 0)
+ wwiomux();
+ switch (getpos(&xrow, &xcol, row, col, wwnrow - 1, wwncol - 1))
+ {
+ case 3:
+ wwunbox(boxwin);
+ wwdelete(boxwin);
+ return;
+ case 2:
+ wwunbox(boxwin);
+ break;
+ case 1:
+ wwunbox(boxwin);
+ case 0:
+ continue;
+ }
+ break;
+ }
+ wwdelete(boxwin);
+ if (!terse)
+ wwputc('\n', cmdwin);
+ wwcurtowin(cmdwin);
+ (void) openwin(id, row, col, xrow-row+1, xcol-col+1, default_nline,
+ (char *) 0, 1, 1, default_shellfile, default_shell);
+}
+
+getpos(row, col, minrow, mincol, maxrow, maxcol)
+register int *row, *col;
+int minrow, mincol;
+int maxrow, maxcol;
+{
+ static int scount;
+ int count;
+ int c;
+ int oldrow = *row, oldcol = *col;
+
+ while ((c = wwgetc()) >= 0) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ scount = scount * 10 + c - '0';
+ continue;
+ }
+ count = scount ? scount : 1;
+ scount = 0;
+ switch (c) {
+ case 'h':
+ if ((*col -= count) < mincol)
+ *col = mincol;
+ break;
+ case 'H':
+ *col = mincol;
+ break;
+ case 'l':
+ if ((*col += count) > maxcol)
+ *col = maxcol;
+ break;
+ case 'L':
+ *col = maxcol;
+ break;
+ case 'j':
+ if ((*row += count) > maxrow)
+ *row = maxrow;
+ break;
+ case 'J':
+ *row = maxrow;
+ break;
+ case 'k':
+ if ((*row -= count) < minrow)
+ *row = minrow;
+ break;
+ case 'K':
+ *row = minrow;
+ break;
+ case ctrl('['):
+ if (!terse)
+ wwputs("\nCancelled. ", cmdwin);
+ return 3;
+ case '\r':
+ return 2;
+ default:
+ if (!terse)
+ wwputs("\nType [hjklHJKL] to move, return to enter position, escape to cancel.", cmdwin);
+ wwbell();
+ }
+ }
+ return oldrow != *row || oldcol != *col;
+}
diff --git a/usr.bin/window/cmd2.c b/usr.bin/window/cmd2.c
new file mode 100644
index 0000000..f6239e9
--- /dev/null
+++ b/usr.bin/window/cmd2.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd2.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+char *help_shortcmd[] = {
+ "# Select window # and return to conversation mode",
+ "%# Select window # but stay in command mode",
+ "escape Return to conversation mode without changing window",
+ "^^ Return to conversation mode and change to previous window",
+ "c# Close window #",
+ "w Open a new window",
+ "m# Move window #",
+ "M# Move window # to its previous position",
+ "s# Change the size of window #",
+ "S# Change window # to its previous size",
+ "^Y Scroll up one line",
+ "^E Scroll down one line",
+ "^U Scroll up half a window",
+ "^D Scroll down half a window",
+ "^B Scroll up a full window",
+ "^F Scroll down a full window",
+ "h Move cursor left",
+ "j Move cursor down",
+ "k Move cursor up",
+ "l Move cursor right",
+ "y Yank",
+ "p Put",
+ "^S Stop output in current window",
+ "^Q Restart output in current window",
+ "^L Redraw screen",
+ "^Z Suspend",
+ "q Quit",
+ ": Enter a long command",
+ 0
+};
+char *help_longcmd[] = {
+ ":alias name string ... Make `name' an alias for `string ...'",
+ ":alias Show all aliases",
+ ":close # ... Close windows",
+ ":close all Close all windows",
+ ":cursor modes Set the cursor modes",
+ ":echo # string ... Print `string ...' in window #",
+ ":escape c Set escape character to `c'",
+ ":foreground # flag Make # a foreground window, if `flag' is true",
+ ":label # string Set label of window # to `string'",
+ ":list List all open windows",
+ ":default_nline lines Set default window buffer size to `lines'",
+ ":default_shell string ...",
+ " Set default shell to `string ...'",
+ ":default_smooth flag Set default smooth scroll flag",
+ ":select # Select window #",
+ ":smooth # flag Set window # to smooth scroll mode",
+ ":source filename Execute commands in `filename'",
+ ":terse flag Set terse mode",
+ ":unalias name Undefine `name' as an alias",
+ ":unset variable Deallocate `variable'",
+ ":variable List all variables",
+ ":window [row col nrow ncol nline label pty frame mapnl keepopen smooth shell]",
+ " Open a window at `row', `col' of size `nrow', `ncol',",
+ " with `nline' lines in the buffer, and `label'",
+ ":write # string ... Write `string ...' to window # as input",
+ 0
+};
+
+c_help()
+{
+ register struct ww *w;
+
+ if ((w = openiwin(wwnrow - 3, "Help")) == 0) {
+ error("Can't open help window: %s.", wwerror());
+ return;
+ }
+ wwprintf(w, "The escape character is %c.\n", escapec);
+ wwprintf(w, "(# represents one of the digits from 1 to 9.)\n\n");
+ if (help_print(w, "Short commands", help_shortcmd) >= 0)
+ (void) help_print(w, "Long commands", help_longcmd);
+ closeiwin(w);
+}
+
+help_print(w, name, list)
+register struct ww *w;
+char *name;
+register char **list;
+{
+ wwprintf(w, "%s:\n\n", name);
+ while (*list)
+ switch (more(w, 0)) {
+ case 0:
+ wwputs(*list++, w);
+ wwputc('\n', w);
+ break;
+ case 1:
+ wwprintf(w, "%s: (continued)\n\n", name);
+ break;
+ case 2:
+ return -1;
+ }
+ return more(w, 1) == 2 ? -1 : 0;
+}
+
+c_quit()
+{
+ char oldterse = terse;
+
+ setterse(0);
+ wwputs("Really quit [yn]? ", cmdwin);
+ wwcurtowin(cmdwin);
+ while (wwpeekc() < 0)
+ wwiomux();
+ if (wwgetc() == 'y') {
+ wwputs("Yes", cmdwin);
+ quit++;
+ } else
+ wwputc('\n', cmdwin);
+ setterse(!quit && oldterse);
+}
diff --git a/usr.bin/window/cmd3.c b/usr.bin/window/cmd3.c
new file mode 100644
index 0000000..950ddf6
--- /dev/null
+++ b/usr.bin/window/cmd3.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd3.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "string.h"
+
+setescape(esc)
+register char *esc;
+{
+ if (*esc == '^') {
+ if (esc[1] != 0)
+ escapec = esc[1] & 0x1f;
+ else
+ escapec = '^';
+ } else
+ escapec = *esc;
+}
+
+setlabel(w, label)
+register struct ww *w;
+char *label;
+{
+ if (w->ww_label != 0)
+ str_free(w->ww_label);
+ if ((w->ww_label = str_cpy(label)) == 0)
+ return -1;
+ return 0;
+}
diff --git a/usr.bin/window/cmd4.c b/usr.bin/window/cmd4.c
new file mode 100644
index 0000000..5dca8f8
--- /dev/null
+++ b/usr.bin/window/cmd4.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd4.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+c_colon()
+{
+ char oldterse = terse;
+ char buf[512];
+
+ setterse(0);
+ wwputc(':', cmdwin);
+ wwgets(buf, wwncol - 3, cmdwin);
+ wwputc('\n', cmdwin);
+ wwcurtowin(cmdwin);
+ setterse(oldterse);
+ if (dolongcmd(buf, (struct value *)0, 0) < 0)
+ error("Out of memory.");
+}
diff --git a/usr.bin/window/cmd5.c b/usr.bin/window/cmd5.c
new file mode 100644
index 0000000..456fdb9
--- /dev/null
+++ b/usr.bin/window/cmd5.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd5.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Window movement.
+ */
+
+c_move(w)
+register struct ww *w;
+{
+ int col, row;
+ int mincol, minrow;
+ int maxcol, maxrow;
+ int curcol, currow;
+
+ if (!terse)
+ wwputs("New window position: ", cmdwin);
+ col = w->ww_w.l;
+ row = w->ww_w.t;
+ wwadd(boxwin, framewin->ww_back);
+ for (;;) {
+ wwbox(boxwin, row - 1, col - 1, w->ww_w.nr + 2, w->ww_w.nc + 2);
+ getminmax(row, w->ww_w.nr, 1, wwnrow,
+ &currow, &minrow, &maxrow);
+ getminmax(col, w->ww_w.nc, 0, wwncol,
+ &curcol, &mincol, &maxcol);
+ wwsetcursor(currow, curcol);
+ while (wwpeekc() < 0)
+ wwiomux();
+ switch (getpos(&row, &col, minrow, mincol, maxrow, maxcol)) {
+ case 3:
+ wwunbox(boxwin);
+ wwdelete(boxwin);
+ return;
+ case 2:
+ wwunbox(boxwin);
+ break;
+ case 1:
+ wwunbox(boxwin);
+ case 0:
+ continue;
+ }
+ break;
+ }
+ wwdelete(boxwin);
+ if (!terse)
+ wwputc('\n', cmdwin);
+ wwcurtowin(cmdwin);
+ movewin(w, row, col);
+}
+
+movewin(w, row, col)
+register struct ww *w;
+{
+ struct ww *back = w->ww_back;
+
+ w->ww_alt.t = w->ww_w.t;
+ w->ww_alt.l = w->ww_w.l;
+ wwdelete(w);
+ wwmove(w, row, col);
+ wwadd(w, back);
+ reframe();
+}
+
+/*
+ * Weird stufff, don't ask.
+ */
+getminmax(x, n, a, b, curx, minx, maxx)
+register x, n, a, b;
+int *curx, *minx, *maxx;
+{
+ if (x < 0)
+ *curx = x + n - 1;
+ else
+ *curx = x;
+
+ if (x <= a)
+ *minx = 1 - n;
+ else if (x <= b - n)
+ *minx = a;
+ else
+ *minx = b - n;
+
+ if (x >= b - n)
+ *maxx = b - 1;
+ else if (x >= a)
+ *maxx = b - n;
+ else
+ *maxx = a;
+}
diff --git a/usr.bin/window/cmd6.c b/usr.bin/window/cmd6.c
new file mode 100644
index 0000000..fc85485
--- /dev/null
+++ b/usr.bin/window/cmd6.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd6.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "string.h"
+#include "char.h"
+
+/*
+ * Debugging commands.
+ */
+
+c_debug()
+{
+ register struct ww *w;
+
+ if (!terse)
+ wwputs("[m(smap) n(ns) o(os) s(string) v(nvis) w(win)]? ", cmdwin);
+ wwcurtowin(cmdwin);
+ while (wwpeekc() < 0)
+ wwiomux();
+ if (!terse)
+ wwputc('\n', cmdwin);
+ switch (wwgetc()) {
+ case 'm':
+ wwdumpsmap();
+ break;
+ case 'n':
+ wwdumpns();
+ break;
+ case 'o':
+ wwdumpos();
+ break;
+ case 's':
+ debug_str();
+ break;
+ case 'v':
+ if ((w = getwin()) != 0)
+ wwdumpnvis(w);
+ break;
+ case 'w':
+ if ((w = getwin()) != 0)
+ wwdumpwin(w);
+ break;
+ default:
+ wwbell();
+ }
+}
+
+#ifdef STR_DEBUG
+debug_str()
+{
+ register struct ww *w;
+ struct string *s;
+
+ if ((w = openiwin(wwnrow - 3, "Allocated Strings")) == 0) {
+ error("Can't open string window: %s.", wwerror());
+ return;
+ }
+ for (s = str_head.s_forw; s != &str_head; s = s->s_forw) {
+ if (more(w, 0) == 2)
+ goto out;
+ wwprintf(w, "(0x%x)\t\"%s\"\n", s->s_data, s->s_data);
+ }
+ waitnl(w);
+out:
+ closeiwin(w);
+}
+#else
+debug_str()
+{
+ error("No string debugging.");
+}
+#endif
diff --git a/usr.bin/window/cmd7.c b/usr.bin/window/cmd7.c
new file mode 100644
index 0000000..6bd4c4e
--- /dev/null
+++ b/usr.bin/window/cmd7.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmd7.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "string.h"
+
+/*
+ * Window size.
+ */
+
+c_size(w)
+register struct ww *w;
+{
+ int col, row;
+
+ if (!terse)
+ wwputs("New window size (lower right corner): ", cmdwin);
+ col = MIN(w->ww_w.r, wwncol) - 1;
+ row = MIN(w->ww_w.b, wwnrow) - 1;
+ wwadd(boxwin, framewin->ww_back);
+ for (;;) {
+ wwbox(boxwin, w->ww_w.t - 1, w->ww_w.l - 1,
+ row - w->ww_w.t + 3, col - w->ww_w.l + 3);
+ wwsetcursor(row, col);
+ while (wwpeekc() < 0)
+ wwiomux();
+ switch (getpos(&row, &col, w->ww_w.t, w->ww_w.l,
+ wwnrow - 1, wwncol - 1)) {
+ case 3:
+ wwunbox(boxwin);
+ wwdelete(boxwin);
+ return;
+ case 2:
+ wwunbox(boxwin);
+ break;
+ case 1:
+ wwunbox(boxwin);
+ case 0:
+ continue;
+ }
+ break;
+ }
+ wwdelete(boxwin);
+ if (!terse)
+ wwputc('\n', cmdwin);
+ wwcurtowin(cmdwin);
+ sizewin(w, row - w->ww_w.t + 1, col - w->ww_w.l + 1);
+}
+
+/*
+ * Yank and put
+ */
+
+struct yb {
+ char *line;
+ int length;
+ struct yb *link;
+};
+struct yb *yb_head, *yb_tail;
+
+c_yank()
+{
+ struct ww *w = selwin;
+ int col1, row1;
+ int col2, row2;
+ int r, c;
+
+ if (!terse)
+ wwputs("Yank starting position: ", cmdwin);
+ wwcursor(w, 0);
+ row1 = w->ww_cur.r;
+ col1 = w->ww_cur.c;
+ for (;;) {
+ wwsetcursor(row1, col1);
+ while (wwpeekc() < 0)
+ wwiomux();
+ switch (getpos(&row1, &col1, w->ww_i.t, w->ww_i.l,
+ w->ww_i.b - 1, w->ww_i.r - 1)) {
+ case 3:
+ goto out;
+ case 2:
+ break;
+ case 1:
+ case 0:
+ continue;
+ }
+ break;
+ }
+ if (!terse)
+ wwputs("\nYank ending position: ", cmdwin);
+ row2 = row1;
+ col2 = col1;
+ for (;;) {
+ wwsetcursor(row2, col2);
+ while (wwpeekc() < 0)
+ wwiomux();
+ r = row2;
+ c = col2;
+ switch (getpos(&row2, &col2, w->ww_i.t, w->ww_i.l,
+ w->ww_i.b - 1, w->ww_i.r - 1)) {
+ case 3:
+ yank_highlight(row1, col1, r, c);
+ goto out;
+ case 2:
+ break;
+ case 1:
+ yank_highlight(row1, col1, r, c);
+ yank_highlight(row1, col1, row2, col2);
+ case 0:
+ continue;
+ }
+ break;
+ }
+ if (row2 < row1 || row2 == row1 && col2 < col1) {
+ r = row1;
+ c = col1;
+ row1 = row2;
+ col1 = col2;
+ row2 = r;
+ col2 = c;
+ }
+ unyank();
+ c = col1;
+ for (r = row1; r < row2; r++) {
+ yank_line(r, c, w->ww_b.r);
+ c = w->ww_b.l;
+ }
+ yank_line(r, c, col2);
+ yank_highlight(row1, col1, row2, col2);
+ if (!terse)
+ wwputc('\n', cmdwin);
+out:
+ wwcursor(w, 1);
+}
+
+yank_highlight(row1, col1, row2, col2)
+{
+ struct ww *w = selwin;
+ int r, c;
+
+ if ((wwavailmodes & WWM_REV) == 0)
+ return;
+ if (row2 < row1 || row2 == row1 && col2 < col1) {
+ r = row1;
+ c = col1;
+ row1 = row2;
+ col1 = col2;
+ row2 = r;
+ col2 = c;
+ }
+ c = col1;
+ for (r = row1; r < row2; r++) {
+ yank_highlight_line(r, c, w->ww_b.r);
+ c = w->ww_b.l;
+ }
+ yank_highlight_line(r, c, col2);
+}
+
+yank_highlight_line(r, c, cend)
+{
+ struct ww *w = selwin;
+ char *win;
+
+ if (r < w->ww_i.t || r >= w->ww_i.b)
+ return;
+ if (c < w->ww_i.l)
+ c = w->ww_i.l;
+ if (cend >= w->ww_i.r)
+ cend = w->ww_i.r;
+ for (win = w->ww_win[r] + c; c < cend; c++, win++) {
+ *win ^= WWM_REV;
+ if (wwsmap[r][c] == w->ww_index) {
+ if (*win == 0)
+ w->ww_nvis[r]++;
+ else if (*win == WWM_REV)
+ w->ww_nvis[r]--;
+ wwns[r][c].c_m ^= WWM_REV;
+ wwtouched[r] |= WWU_TOUCHED;
+ }
+ }
+}
+
+unyank()
+{
+ struct yb *yp, *yq;
+
+ for (yp = yb_head; yp; yp = yq) {
+ yq = yp->link;
+ str_free(yp->line);
+ free((char *) yp);
+ }
+ yb_head = yb_tail = 0;
+}
+
+yank_line(r, c, cend)
+{
+ struct yb *yp;
+ int nl = 0;
+ int n;
+ union ww_char *bp;
+ char *cp;
+
+ if (c == cend)
+ return;
+ if ((yp = (struct yb *) malloc(sizeof *yp)) == 0)
+ return;
+ yp->link = 0;
+ nl = cend == selwin->ww_b.r;
+ bp = selwin->ww_buf[r];
+ for (cend--; cend >= c; cend--)
+ if (bp[cend].c_c != ' ')
+ break;
+ yp->length = n = cend - c + 1;
+ if (nl)
+ yp->length++;
+ yp->line = str_alloc(yp->length + 1);
+ for (bp += c, cp = yp->line; --n >= 0;)
+ *cp++ = bp++->c_c;
+ if (nl)
+ *cp++ = '\n';
+ *cp = 0;
+ if (yb_head)
+ yb_tail = yb_tail->link = yp;
+ else
+ yb_head = yb_tail = yp;
+}
+
+c_put()
+{
+ struct yb *yp;
+
+ for (yp = yb_head; yp; yp = yp->link)
+ (void) write(selwin->ww_pty, yp->line, yp->length);
+}
diff --git a/usr.bin/window/compress.c b/usr.bin/window/compress.c
new file mode 100644
index 0000000..d71b510
--- /dev/null
+++ b/usr.bin/window/compress.c
@@ -0,0 +1,900 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)compress.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+ /* special */
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+int cc_trace = 0;
+FILE *cc_trace_fp;
+
+ /* tunable parameters */
+
+int cc_reverse = 1;
+int cc_sort = 0;
+int cc_chop = 0;
+
+int cc_token_max = 8; /* <= TOKEN_MAX */
+int cc_token_min = 2; /* > tt.tt_put_token_cost */
+int cc_npass0 = 1;
+int cc_npass1 = 1;
+
+int cc_bufsize = 1024 * 3; /* XXX, or 80 * 24 * 2 */
+
+int cc_ntoken = 8192;
+
+#define cc_weight XXX
+#ifndef cc_weight
+int cc_weight = 0;
+#endif
+
+#define TOKEN_MAX 16
+
+struct cc {
+ char string[TOKEN_MAX];
+ char length;
+ char flag;
+#ifndef cc_weight
+ short weight;
+#endif
+ long time; /* time last seen */
+ short bcount; /* count in this buffer */
+ short ccount; /* count in compression */
+ short places; /* places in the buffer */
+ short code; /* token code */
+ struct cc *qforw, *qback;
+ struct cc *hforw, **hback;
+};
+
+short cc_thresholds[TOKEN_MAX + 1];
+#define thresh(length) (cc_thresholds[length])
+#define threshp(code, count, length) \
+ ((code) >= 0 || (short) (count) >= cc_thresholds[length])
+
+#ifndef cc_weight
+short cc_wthresholds[TOKEN_MAX + 1];
+#define wthresh(length) (cc_wthresholds[length])
+#define wthreshp(weight, length) ((short) (weight) >= cc_wthresholds[length])
+#else
+#define wthreshp(weight, length) (0)
+#endif
+
+#ifndef cc_weight
+short cc_wlimits[TOKEN_MAX + 1];
+#define wlimit(length) (cc_wlimits[length])
+#endif
+
+#define put_token_score(length) ((length) - tt.tt_put_token_cost)
+
+int cc_score_adjustments[TOKEN_MAX + 1][8]; /* XXX, 8 > max of cc_thresholds */
+#define score_adjust(score, p) \
+ do { \
+ int length = (p)->length; \
+ int ccount = (p)->ccount; \
+ if (threshp((p)->code, ccount, length) || \
+ wthreshp((p)->weight, length)) /* XXX */ \
+ (score) -= length - tt.tt_put_token_cost; \
+ else \
+ (score) += cc_score_adjustments[length][ccount]; \
+ } while (0)
+
+int cc_initial_scores[TOKEN_MAX + 1][8]; /* XXX, 8 > max of cc_thresholds */
+
+struct cc cc_q0a, cc_q0b, cc_q1a, cc_q1b;
+
+#define qinsert(p1, p2) \
+ do { \
+ register struct cc *forw = (p1)->qforw; \
+ register struct cc *back = (p1)->qback; \
+ back->qforw = forw; \
+ forw->qback = back; \
+ forw = (p2)->qforw; \
+ (p1)->qforw = forw; \
+ forw->qback = (p1); \
+ (p2)->qforw = (p1); \
+ (p1)->qback = (p2); \
+ } while (0)
+
+#define qinsertq(q, p) \
+ ((q)->qforw == (q) ? 0 : \
+ ((q)->qback->qforw = (p)->qforw, \
+ (p)->qforw->qback = (q)->qback, \
+ (q)->qforw->qback = (p), \
+ (p)->qforw = (q)->qforw, \
+ (q)->qforw = (q), \
+ (q)->qback = (q)))
+
+#define H (14)
+#define HSIZE (1 << H)
+#define hash(h, c) ((((h) >> H - 8 | (h) << 8) ^ (c)) & HSIZE - 1)
+
+char *cc_buffer;
+struct cc **cc_output; /* the output array */
+short *cc_places[TOKEN_MAX + 1];
+short *cc_hashcodes; /* for computing hashcodes */
+struct cc **cc_htab; /* the hash table */
+struct cc **cc_tokens; /* holds all the active tokens */
+struct cc_undo {
+ struct cc **pos;
+ struct cc *val;
+} *cc_undo;
+
+long cc_time, cc_time0;
+
+char *cc_tt_ob, *cc_tt_obe;
+
+ccinit()
+{
+ register i, j;
+ register struct cc *p;
+
+ if (tt.tt_token_max > cc_token_max)
+ tt.tt_token_max = cc_token_max;
+ if (tt.tt_token_min < cc_token_min)
+ tt.tt_token_min = cc_token_min;
+ if (tt.tt_token_min > tt.tt_token_max) {
+ tt.tt_ntoken = 0;
+ return 0;
+ }
+ if (tt.tt_ntoken > cc_ntoken / 2) /* not likely */
+ tt.tt_ntoken = cc_ntoken / 2;
+#define C(x) (sizeof (x) / sizeof *(x))
+ for (i = 0; i < C(cc_thresholds); i++) {
+ int h = i - tt.tt_put_token_cost;
+ if (h > 0)
+ cc_thresholds[i] =
+ (tt.tt_set_token_cost + 1 + h - 1) / h + 1;
+ else
+ cc_thresholds[i] = 0;
+ }
+ for (i = 0; i < C(cc_score_adjustments); i++) {
+ int t = cc_thresholds[i];
+ for (j = 0; j < C(*cc_score_adjustments); j++) {
+ if (j >= t)
+ cc_score_adjustments[i][j] =
+ - (i - tt.tt_put_token_cost);
+ else if (j < t - 1)
+ cc_score_adjustments[i][j] = 0;
+ else
+ /*
+ * cost now is
+ * length * (ccount + 1) a
+ * cost before was
+ * set-token-cost + length +
+ * ccount * put-token-cost b
+ * the score adjustment is (b - a)
+ */
+ cc_score_adjustments[i][j] =
+ tt.tt_set_token_cost + i +
+ j * tt.tt_put_token_cost -
+ i * (j + 1);
+ if (j >= t)
+ cc_initial_scores[i][j] = 0;
+ else
+ /*
+ * - (set-token-cost +
+ * (length - put-token-cost) -
+ * (length - put-token-cost) * ccount)
+ */
+ cc_initial_scores[i][j] =
+ - (tt.tt_set_token_cost +
+ (i - tt.tt_put_token_cost) -
+ (i - tt.tt_put_token_cost) * j);
+ }
+ }
+#ifndef cc_weight
+ for (i = 1; i < C(cc_wthresholds); i++) {
+ cc_wthresholds[i] =
+ ((tt.tt_set_token_cost + tt.tt_put_token_cost) / i +
+ i / 5 + 1) *
+ cc_weight + 1;
+ cc_wlimits[i] = cc_wthresholds[i] + cc_weight;
+ }
+#endif
+#undef C
+ if ((cc_output = (struct cc **)
+ malloc((unsigned) cc_bufsize * sizeof *cc_output)) == 0)
+ goto nomem;
+ if ((cc_hashcodes = (short *)
+ malloc((unsigned) cc_bufsize * sizeof *cc_hashcodes)) == 0)
+ goto nomem;
+ if ((cc_htab = (struct cc **) malloc(HSIZE * sizeof *cc_htab)) == 0)
+ goto nomem;
+ if ((cc_tokens = (struct cc **)
+ malloc((unsigned)
+ (cc_ntoken + tt.tt_token_max - tt.tt_token_min + 1) *
+ sizeof *cc_tokens)) == 0)
+ goto nomem;
+ if ((cc_undo = (struct cc_undo *)
+ malloc((unsigned) cc_bufsize * sizeof *cc_undo)) == 0)
+ goto nomem;
+ for (i = tt.tt_token_min; i <= tt.tt_token_max; i++)
+ if ((cc_places[i] = (short *)
+ malloc((unsigned) cc_bufsize * sizeof **cc_places)) == 0)
+ goto nomem;
+ cc_q0a.qforw = cc_q0a.qback = &cc_q0a;
+ cc_q0b.qforw = cc_q0b.qback = &cc_q0b;
+ cc_q1a.qforw = cc_q1a.qback = &cc_q1a;
+ cc_q1b.qforw = cc_q1b.qback = &cc_q1b;
+ if ((p = (struct cc *) malloc((unsigned) cc_ntoken * sizeof *p)) == 0)
+ goto nomem;
+ for (i = 0; i < tt.tt_ntoken; i++) {
+ p->code = i;
+ p->time = -1;
+ p->qback = cc_q0a.qback;
+ p->qforw = &cc_q0a;
+ p->qback->qforw = p;
+ cc_q0a.qback = p;
+ p++;
+ }
+ for (; i < cc_ntoken; i++) {
+ p->code = -1;
+ p->time = -1;
+ p->qback = cc_q1a.qback;
+ p->qforw = &cc_q1a;
+ p->qback->qforw = p;
+ cc_q1a.qback = p;
+ p++;
+ }
+ cc_tt_ob = tt_ob;
+ cc_tt_obe = tt_obe;
+ if ((cc_buffer = malloc((unsigned) cc_bufsize)) == 0)
+ goto nomem;
+ return 0;
+nomem:
+ wwerrno = WWE_NOMEM;
+ return -1;
+}
+
+ccstart()
+{
+ int ccflush();
+
+ ttflush();
+ tt_obp = tt_ob = cc_buffer;
+ tt_obe = tt_ob + cc_bufsize;
+ tt.tt_flush = ccflush;
+ if (cc_trace) {
+ cc_trace_fp = fopen("window-trace", "a");
+ (void) fcntl(fileno(cc_trace_fp), F_SETFD, 1);
+ }
+ ccreset();
+}
+
+ccreset()
+{
+ register struct cc *p;
+
+ bzero((char *) cc_htab, HSIZE * sizeof *cc_htab);
+ for (p = cc_q0a.qforw; p != &cc_q0a; p = p->qforw)
+ p->hback = 0;
+ for (p = cc_q1a.qforw; p != &cc_q1a; p = p->qforw)
+ p->hback = 0;
+}
+
+ccend()
+{
+
+ ttflush();
+ tt_obp = tt_ob = cc_tt_ob;
+ tt_obe = cc_tt_obe;
+ tt.tt_flush = 0;
+ if (cc_trace_fp != NULL) {
+ (void) fclose(cc_trace_fp);
+ cc_trace_fp = NULL;
+ }
+}
+
+ccflush()
+{
+ int bufsize = tt_obp - tt_ob;
+ int n;
+
+ if (tt_ob != cc_buffer)
+ abort();
+ if (cc_trace_fp != NULL) {
+ (void) fwrite(tt_ob, 1, bufsize, cc_trace_fp);
+ (void) putc(-1, cc_trace_fp);
+ }
+ tt.tt_flush = 0;
+ (*tt.tt_compress)(1);
+ if (bufsize < tt.tt_token_min) {
+ ttflush();
+ goto out;
+ }
+ tt_obp = tt_ob = cc_tt_ob;
+ tt_obe = cc_tt_obe;
+ cc_time0 = cc_time;
+ cc_time += bufsize;
+ n = cc_sweep_phase(cc_buffer, bufsize, cc_tokens);
+ cc_compress_phase(cc_output, bufsize, cc_tokens, n);
+ cc_output_phase(cc_buffer, cc_output, bufsize);
+ ttflush();
+ tt_obp = tt_ob = cc_buffer;
+ tt_obe = cc_buffer + cc_bufsize;
+out:
+ (*tt.tt_compress)(0);
+ tt.tt_flush = ccflush;
+}
+
+cc_sweep_phase(buffer, bufsize, tokens)
+ char *buffer;
+ struct cc **tokens;
+{
+ register struct cc **pp = tokens;
+ register i, n;
+#ifdef STATS
+ int nn, ii;
+#endif
+
+#ifdef STATS
+ if (verbose >= 0)
+ time_begin();
+ if (verbose > 0)
+ printf("Sweep:");
+#endif
+ cc_sweep0(buffer, bufsize, tt.tt_token_min - 1);
+#ifdef STATS
+ ntoken_stat = 0;
+ nn = 0;
+ ii = 0;
+#endif
+ for (i = tt.tt_token_min; i <= tt.tt_token_max; i++) {
+#ifdef STATS
+ if (verbose > 0) {
+ if (ii > 7) {
+ printf("\n ");
+ ii = 0;
+ }
+ ii++;
+ printf(" (%d", i);
+ (void) fflush(stdout);
+ }
+#endif
+ n = cc_sweep(buffer, bufsize, pp, i);
+ pp += n;
+#ifdef STATS
+ if (verbose > 0) {
+ if (--n > 0) {
+ printf(" %d", n);
+ nn += n;
+ }
+ putchar(')');
+ }
+#endif
+ }
+ qinsertq(&cc_q1b, &cc_q1a);
+#ifdef STATS
+ if (verbose > 0)
+ printf("\n %d tokens, %d candidates\n",
+ ntoken_stat, nn);
+ if (verbose >= 0)
+ time_end();
+#endif
+ return pp - tokens;
+}
+
+cc_sweep0(buffer, n, length)
+ char *buffer;
+{
+ register char *p;
+ register short *hc;
+ register i;
+ register short c;
+ register short pc = tt.tt_padc;
+
+ /* n and length are at least 1 */
+ p = buffer++;
+ hc = cc_hashcodes;
+ i = n;
+ do {
+ if ((*hc++ = *p++) == pc)
+ hc[-1] = -1;
+ } while (--i);
+ while (--length) {
+ p = buffer++;
+ hc = cc_hashcodes;
+ for (i = n--; --i;) {
+ if ((c = *p++) == pc || *hc < 0)
+ c = -1;
+ else
+ c = hash(*hc, c);
+ *hc++ = c;
+ }
+ }
+}
+
+cc_sweep(buffer, bufsize, tokens, length)
+ char *buffer;
+ struct cc **tokens;
+ register length;
+{
+ register struct cc *p;
+ register char *cp;
+ register i;
+ short *hc;
+ short *places = cc_places[length];
+ struct cc **pp = tokens;
+ short threshold = thresh(length);
+#ifndef cc_weight
+ short wthreshold = wthresh(length);
+ short limit = wlimit(length);
+#endif
+ int time;
+ short pc = tt.tt_padc;
+
+ i = length - 1;
+ bufsize -= i;
+ cp = buffer + i;
+ hc = cc_hashcodes;
+ time = cc_time0;
+ for (i = 0; i < bufsize; i++, time++) {
+ struct cc **h;
+
+ {
+ register short *hc1 = hc;
+ register short c = *cp++;
+ register short hh;
+ if ((hh = *hc1) < 0 || c == pc) {
+ *hc1++ = -1;
+ hc = hc1;
+ continue;
+ }
+ h = cc_htab + (*hc1++ = hash(hh, c));
+ hc = hc1;
+ }
+ for (p = *h; p != 0; p = p->hforw)
+ if (p->length == (char) length) {
+ register char *p1 = p->string;
+ register char *p2 = cp - length;
+ register n = length;
+ do
+ if (*p1++ != *p2++)
+ goto fail;
+ while (--n);
+ break;
+ fail:;
+ }
+ if (p == 0) {
+ p = cc_q1a.qback;
+ if (p == &cc_q1a ||
+ p->time >= cc_time0 && p->length == (char) length)
+ continue;
+ if (p->hback != 0)
+ if ((*p->hback = p->hforw) != 0)
+ p->hforw->hback = p->hback;
+ {
+ register char *p1 = p->string;
+ register char *p2 = cp - length;
+ register n = length;
+ do
+ *p1++ = *p2++;
+ while (--n);
+ }
+ p->length = length;
+#ifndef cc_weight
+ p->weight = cc_weight;
+#endif
+ p->time = time;
+ p->bcount = 1;
+ p->ccount = 0;
+ p->flag = 0;
+ if ((p->hforw = *h) != 0)
+ p->hforw->hback = &p->hforw;
+ *h = p;
+ p->hback = h;
+ qinsert(p, &cc_q1a);
+ places[i] = -1;
+ p->places = i;
+#ifdef STATS
+ ntoken_stat++;
+#endif
+ } else if (p->time < cc_time0) {
+#ifndef cc_weight
+ if ((p->weight += p->time - time) < 0)
+ p->weight = cc_weight;
+ else if ((p->weight += cc_weight) > limit)
+ p->weight = limit;
+#endif
+ p->time = time;
+ p->bcount = 1;
+ p->ccount = 0;
+ if (p->code >= 0) {
+ p->flag = 1;
+ *pp++ = p;
+ } else
+#ifndef cc_weight
+ if (p->weight >= wthreshold) {
+ p->flag = 1;
+ *pp++ = p;
+ qinsert(p, &cc_q1b);
+ } else
+#endif
+ {
+ p->flag = 0;
+ qinsert(p, &cc_q1a);
+ }
+ places[i] = -1;
+ p->places = i;
+#ifdef STATS
+ ntoken_stat++;
+#endif
+ } else if (p->time + length > time) {
+ /*
+ * overlapping token, don't count as two and
+ * don't update time, but do adjust weight to offset
+ * the difference
+ */
+#ifndef cc_weight
+ if (cc_weight != 0) { /* XXX */
+ p->weight += time - p->time;
+ if (!p->flag && p->weight >= wthreshold) {
+ p->flag = 1;
+ *pp++ = p;
+ qinsert(p, &cc_q1b);
+ }
+ }
+#endif
+ places[i] = p->places;
+ p->places = i;
+ } else {
+#ifndef cc_weight
+ if ((p->weight += p->time - time) < 0)
+ p->weight = cc_weight;
+ else if ((p->weight += cc_weight) > limit)
+ p->weight = limit;
+#endif
+ p->time = time;
+ p->bcount++;
+ if (!p->flag &&
+ /* code must be < 0 if flag false here */
+ (p->bcount >= threshold
+#ifndef cc_weight
+ || p->weight >= wthreshold
+#endif
+ )) {
+ p->flag = 1;
+ *pp++ = p;
+ qinsert(p, &cc_q1b);
+ }
+ places[i] = p->places;
+ p->places = i;
+ }
+ }
+ if ((i = pp - tokens) > 0) {
+ *pp = 0;
+ if (cc_reverse)
+ cc_sweep_reverse(tokens, places);
+ if (cc_sort && i > 1) {
+ int cc_token_compare();
+ qsort((char *) tokens, i, sizeof *tokens,
+ cc_token_compare);
+ }
+ if (cc_chop) {
+ if ((i = i * cc_chop / 100) == 0)
+ i = 1;
+ tokens[i] = 0;
+ }
+ i++;
+ }
+ return i;
+}
+
+cc_sweep_reverse(pp, places)
+ register struct cc **pp;
+ register short *places;
+{
+ register struct cc *p;
+ register short front, back, t;
+
+ while ((p = *pp++) != 0) {
+ back = -1;
+ t = p->places;
+ /* the list is never empty */
+ do {
+ front = places[t];
+ places[t] = back;
+ back = t;
+ } while ((t = front) >= 0);
+ p->places = back;
+ }
+}
+
+cc_compress_phase(output, bufsize, tokens, ntoken)
+ struct cc **output;
+ struct cc **tokens;
+{
+ register i;
+
+ bzero((char *) output, bufsize * sizeof *output);
+ for (i = 0; i < cc_npass0; i++)
+ cc_compress_phase1(output, tokens, ntoken, 0);
+ for (i = 0; i < cc_npass1; i++)
+ cc_compress_phase1(output, tokens, ntoken, 1);
+ cc_compress_cleanup(output, bufsize);
+}
+
+cc_compress_phase1(output, tokens, ntoken, flag)
+ register struct cc **output;
+ struct cc **tokens;
+{
+ register struct cc **pp;
+#ifdef STATS
+ register int i = 0;
+ int nt = 0, cc = 0, nc = 0;
+#endif
+
+#ifdef STATS
+ if (verbose >= 0)
+ time_begin();
+ if (verbose > 0)
+ printf("Compress:");
+#endif
+ pp = tokens;
+ while (pp < tokens + ntoken) {
+#ifdef STATS
+ if (verbose > 0) {
+ ntoken_stat = 0;
+ ccount_stat = 0;
+ ncover_stat = 0;
+ if (i > 2) {
+ printf("\n ");
+ i = 0;
+ }
+ i++;
+ printf(" (%d", (*pp)->length);
+ (void) fflush(stdout);
+ }
+#endif
+ pp += cc_compress(output, pp, flag);
+#ifdef STATS
+ if (verbose > 0) {
+ printf(" %dt %du %dc)", ntoken_stat, ccount_stat,
+ ncover_stat);
+ nt += ntoken_stat;
+ cc += ccount_stat;
+ nc += ncover_stat;
+ }
+#endif
+ }
+#ifdef STATS
+ if (verbose > 0)
+ printf("\n total: (%dt %du %dc)\n", nt, cc, nc);
+ if (verbose >= 0)
+ time_end();
+#endif
+}
+
+cc_compress_cleanup(output, bufsize)
+ register struct cc **output;
+{
+ register struct cc **end;
+
+ /* the previous output phase may have been interrupted */
+ qinsertq(&cc_q0b, &cc_q0a);
+ for (end = output + bufsize; output < end;) {
+ register struct cc *p;
+ register length;
+ if ((p = *output) == 0) {
+ output++;
+ continue;
+ }
+ length = p->length;
+ if (!p->flag) {
+ } else if (p->code >= 0) {
+ qinsert(p, &cc_q0b);
+ p->flag = 0;
+ } else if (p->ccount == 0) {
+ *output = 0;
+ } else if (p->ccount >= thresh(length)
+#ifndef cc_weight
+ || wthreshp(p->weight, length)
+#endif
+ ) {
+ p->flag = 0;
+ } else {
+ p->ccount = 0;
+ *output = 0;
+ }
+ output += length;
+ }
+}
+
+cc_compress(output, tokens, flag)
+ struct cc **output;
+ struct cc **tokens;
+ char flag;
+{
+ struct cc **pp = tokens;
+ register struct cc *p = *pp++;
+ int length = p->length;
+ int threshold = thresh(length);
+#ifndef cc_weight
+ short wthreshold = wthresh(length);
+#endif
+ short *places = cc_places[length];
+ int *initial_scores = cc_initial_scores[length];
+ int initial_score0 = put_token_score(length);
+
+ do {
+ int score;
+ register struct cc_undo *undop;
+ int ccount;
+#ifdef STATS
+ int ncover;
+#endif
+ int i;
+
+ ccount = p->ccount;
+ if ((short) ccount >= p->bcount)
+ continue;
+ if (p->code >= 0 || ccount >= threshold)
+ score = 0;
+#ifndef cc_weight
+ else if (p->weight >= wthreshold)
+ /* allow one fewer match than normal */
+ /* XXX, should adjust for ccount */
+ score = - tt.tt_set_token_cost;
+#endif
+ else
+ score = initial_scores[ccount];
+ undop = cc_undo;
+#ifdef STATS
+ ncover = 0;
+#endif
+ for (i = p->places; i >= 0; i = places[i]) {
+ register struct cc **jp;
+ register struct cc *x;
+ register struct cc **ip = output + i;
+ register score0 = initial_score0;
+ struct cc **iip = ip + length;
+ struct cc_undo *undop1 = undop;
+
+ if ((x = *(jp = ip)) != 0)
+ goto z;
+ while (--jp >= output)
+ if ((x = *jp) != 0) {
+ if (jp + x->length > ip)
+ goto z;
+ break;
+ }
+ jp = ip + 1;
+ while (jp < iip) {
+ if ((x = *jp) == 0) {
+ jp++;
+ continue;
+ }
+ z:
+ if (x == p)
+ goto undo;
+#ifdef STATS
+ ncover++;
+#endif
+ undop->pos = jp;
+ undop->val = x;
+ undop++;
+ *jp = 0;
+ x->ccount--;
+ score_adjust(score0, x);
+ if (score0 < 0 && flag)
+ goto undo;
+ jp += x->length;
+ }
+ undop->pos = ip;
+ undop->val = 0;
+ undop++;
+ *ip = p;
+ ccount++;
+ score += score0;
+ continue;
+ undo:
+ while (--undop >= undop1)
+ if (*undop->pos = x = undop->val)
+ x->ccount++;
+ undop++;
+ }
+ if (score > 0) {
+#ifdef STATS
+ ccount_stat += ccount - p->ccount;
+ ntoken_stat++;
+ ncover_stat += ncover;
+#endif
+ p->ccount = ccount;
+ } else {
+ register struct cc_undo *u = cc_undo;
+ while (--undop >= u) {
+ register struct cc *x;
+ if (*undop->pos = x = undop->val)
+ x->ccount++;
+ }
+ }
+ } while ((p = *pp++) != 0);
+ return pp - tokens;
+}
+
+cc_output_phase(buffer, output, bufsize)
+ register char *buffer;
+ register struct cc **output;
+ register bufsize;
+{
+ register i;
+ register struct cc *p, *p1;
+
+ for (i = 0; i < bufsize;) {
+ if ((p = output[i]) == 0) {
+ ttputc(buffer[i]);
+ i++;
+ } else if (p->code >= 0) {
+ if (--p->ccount == 0)
+ qinsert(p, &cc_q0a);
+ (*tt.tt_put_token)(p->code, p->string, p->length);
+ wwntokuse++;
+ wwntoksave += put_token_score(p->length);
+ i += p->length;
+ } else if ((p1 = cc_q0a.qback) != &cc_q0a) {
+ p->code = p1->code;
+ p1->code = -1;
+ qinsert(p1, &cc_q1a);
+ if (--p->ccount == 0)
+ qinsert(p, &cc_q0a);
+ else
+ qinsert(p, &cc_q0b);
+ (*tt.tt_set_token)(p->code, p->string, p->length);
+ wwntokdef++;
+ wwntoksave -= tt.tt_set_token_cost;
+ i += p->length;
+ } else {
+ p->ccount--;
+ ttwrite(p->string, p->length);
+ wwntokbad++;
+ i += p->length;
+ }
+ }
+ wwntokc += bufsize;
+}
+
+cc_token_compare(p1, p2)
+ struct cc **p1, **p2;
+{
+ return (*p2)->bcount - (*p1)->bcount;
+}
diff --git a/usr.bin/window/context.c b/usr.bin/window/context.c
new file mode 100644
index 0000000..cd43cdb
--- /dev/null
+++ b/usr.bin/window/context.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)context.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "value.h"
+#include "string.h"
+#include "context.h"
+#include <fcntl.h>
+
+/*
+ * Context push/pop for nested command files.
+ */
+
+char *malloc();
+
+cx_alloc()
+{
+ register struct context *xp;
+
+ if (cx.x_type != 0) {
+ xp = (struct context *)
+ malloc((unsigned) sizeof (struct context));
+ if (xp == 0)
+ return -1;
+ *xp = cx;
+ cx.x_link = xp;
+ cx.x_type = 0;
+ }
+ cx.x_erred = 0;
+ cx.x_synerred = 0;
+ cx.x_abort = 0;
+ return 0;
+}
+
+cx_free()
+{
+ struct context *xp;
+
+ if ((xp = cx.x_link) != 0) {
+ cx = *xp;
+ free((char *)xp);
+ } else
+ cx.x_type = 0;
+}
+
+cx_beginfile(filename)
+char *filename;
+{
+ if (cx_alloc() < 0)
+ return -1;
+ cx.x_type = X_FILE;
+ if ((cx.x_filename = str_cpy(filename)) == 0)
+ goto bad;
+ cx.x_fp = fopen(filename, "r");
+ if (cx.x_fp == 0)
+ goto bad;
+ (void) fcntl(fileno(cx.x_fp), F_SETFD, 1);
+ cx.x_bol = 1;
+ cx.x_lineno = 0;
+ cx.x_errwin = 0;
+ cx.x_noerr = 0;
+ return 0;
+bad:
+ if (cx.x_filename != 0)
+ str_free(cx.x_filename);
+ cx_free();
+ return -1;
+}
+
+cx_beginbuf(buf, arg, narg)
+char *buf;
+struct value *arg;
+int narg;
+{
+ if (cx_alloc() < 0)
+ return -1;
+ cx.x_type = X_BUF;
+ cx.x_bufp = cx.x_buf = buf;
+ cx.x_arg = arg;
+ cx.x_narg = narg;
+ return 0;
+}
+
+cx_end()
+{
+ switch (cx.x_type) {
+ case X_BUF:
+ break;
+ case X_FILE:
+ (void) fclose(cx.x_fp);
+ str_free(cx.x_filename);
+ break;
+ }
+ cx_free();
+}
diff --git a/usr.bin/window/context.h b/usr.bin/window/context.h
new file mode 100644
index 0000000..4863abe
--- /dev/null
+++ b/usr.bin/window/context.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)context.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <stdio.h>
+
+struct context {
+ struct context *x_link; /* nested contexts */
+ char x_type; /* tag for union */
+ union {
+ struct { /* input is a file */
+ char *X_filename; /* input file name */
+ FILE *X_fp; /* input stream */
+ short X_lineno; /* current line number */
+ char X_bol; /* at beginning of line */
+ char X_noerr; /* don't report errors */
+ struct ww *X_errwin; /* error window */
+ } x_f;
+ struct { /* input is a buffer */
+ char *X_buf; /* input buffer */
+ char *X_bufp; /* current position in buf */
+ struct value *X_arg; /* argument for alias */
+ int X_narg; /* number of arguments */
+ } x_b;
+ } x_un;
+ /* holding place for current token */
+ int x_token; /* the token */
+ struct value x_val; /* values associated with token */
+ /* parser error flags */
+ unsigned x_erred :1; /* had an error */
+ unsigned x_synerred :1; /* had syntax error */
+ unsigned x_abort :1; /* fatal error */
+};
+#define x_buf x_un.x_b.X_buf
+#define x_bufp x_un.x_b.X_bufp
+#define x_arg x_un.x_b.X_arg
+#define x_narg x_un.x_b.X_narg
+#define x_filename x_un.x_f.X_filename
+#define x_fp x_un.x_f.X_fp
+#define x_lineno x_un.x_f.X_lineno
+#define x_bol x_un.x_f.X_bol
+#define x_errwin x_un.x_f.X_errwin
+#define x_noerr x_un.x_f.X_noerr
+
+ /* x_type values, 0 is reserved */
+#define X_FILE 1 /* input is a file */
+#define X_BUF 2 /* input is a buffer */
+
+struct context cx; /* the current context */
diff --git a/usr.bin/window/defs.h b/usr.bin/window/defs.h
new file mode 100644
index 0000000..59eb729
--- /dev/null
+++ b/usr.bin/window/defs.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include "ww.h"
+#include <sys/time.h>
+
+#define NWINDOW 9
+
+struct timeval starttime;
+
+struct ww *window[NWINDOW]; /* the windows */
+struct ww *selwin; /* the selected window */
+struct ww *lastselwin; /* the last selected window */
+struct ww *cmdwin; /* the command window */
+struct ww *framewin; /* the window for framing */
+struct ww *boxwin; /* the window for the box */
+struct ww *fgwin; /* the last foreground window */
+
+#define isfg(w) ((w)->ww_order <= fgwin->ww_order)
+
+char *default_shell[128]; /* default shell argv */
+char *default_shellfile; /* default shell program */
+int default_nline; /* default buffer size for new windows */
+int default_smooth; /* default "smooth" parameter */
+char escapec; /* the escape character */
+
+ /* flags */
+char quit; /* quit command issued */
+char terse; /* terse mode */
+char debug; /* debug mode */
+char incmd; /* in command mode */
+
+struct ww *getwin();
+struct ww *openwin();
+struct ww *vtowin();
+struct ww *openiwin();
diff --git a/usr.bin/window/error.c b/usr.bin/window/error.c
new file mode 100644
index 0000000..63e0614
--- /dev/null
+++ b/usr.bin/window/error.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)error.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "value.h"
+#include "context.h"
+#include "char.h"
+
+#define ERRLINES 10 /* number of lines for errwin */
+
+/*VARARGS1*/
+error(fmt, a, b, c, d, e, f, g, h)
+char *fmt;
+{
+ register struct context *x;
+ register struct ww *w;
+
+ for (x = &cx; x != 0 && x->x_type != X_FILE; x = x->x_link)
+ ;
+ if (x == 0) {
+ if (terse)
+ wwbell();
+ else {
+ wwprintf(cmdwin, fmt, a, b, c, d, e, f, g, h);
+ wwputs(" ", cmdwin);
+ }
+ return;
+ }
+ if (x->x_noerr)
+ return;
+ if ((w = x->x_errwin) == 0) {
+ char buf[512];
+
+ (void) sprintf(buf, "Errors from %s", x->x_filename);
+ if ((w = x->x_errwin = openiwin(ERRLINES, buf)) == 0) {
+ wwputs("Can't open error window. ", cmdwin);
+ x->x_noerr = 1;
+ return;
+ }
+ }
+ if (more(w, 0) == 2) {
+ x->x_noerr = 1;
+ return;
+ }
+ wwprintf(w, "line %d: ", x->x_lineno);
+ wwprintf(w, fmt, a, b, c, d, e, f, g, h);
+ wwputc('\n', w);
+}
+
+err_end()
+{
+ if (cx.x_type == X_FILE && cx.x_errwin != 0) {
+ if (!cx.x_noerr)
+ waitnl(cx.x_errwin);
+ closeiwin(cx.x_errwin);
+ cx.x_errwin = 0;
+ }
+}
diff --git a/usr.bin/window/lcmd.c b/usr.bin/window/lcmd.c
new file mode 100644
index 0000000..3938aa7
--- /dev/null
+++ b/usr.bin/window/lcmd.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lcmd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "value.h"
+#include "lcmd.h"
+
+int l_alias();
+int l_close();
+int l_cursormodes();
+int l_debug();
+int l_def_nline();
+int l_def_shell();
+int l_def_smooth();
+int l_echo();
+int l_escape();
+int l_foreground();
+int l_iostat();
+int l_label();
+int l_list();
+int l_select();
+int l_smooth();
+int l_source();
+int l_terse();
+int l_time();
+int l_unalias();
+int l_unset();
+int l_variable();
+int l_window();
+int l_write();
+
+extern struct lcmd_arg arg_alias[];
+extern struct lcmd_arg arg_cursormodes[];
+extern struct lcmd_arg arg_debug[];
+extern struct lcmd_arg arg_echo[];
+extern struct lcmd_arg arg_escape[];
+extern struct lcmd_arg arg_foreground[];
+extern struct lcmd_arg arg_label[];
+extern struct lcmd_arg arg_def_nline[];
+extern struct lcmd_arg arg_def_shell[];
+extern struct lcmd_arg arg_def_smooth[];
+extern struct lcmd_arg arg_close[];
+extern struct lcmd_arg arg_select[];
+extern struct lcmd_arg arg_smooth[];
+extern struct lcmd_arg arg_source[];
+extern struct lcmd_arg arg_terse[];
+extern struct lcmd_arg arg_time[];
+extern struct lcmd_arg arg_unalias[];
+extern struct lcmd_arg arg_unset[];
+extern struct lcmd_arg arg_window[];
+extern struct lcmd_arg arg_write[];
+struct lcmd_arg arg_null[1] = { { 0 } };
+
+struct lcmd_tab lcmd_tab[] = {
+ "alias", 1, l_alias, arg_alias,
+ "close", 2, l_close, arg_close,
+ "cursormodes", 2, l_cursormodes, arg_cursormodes,
+ "debug", 1, l_debug, arg_debug,
+ "default_nlines", 9, l_def_nline, arg_def_nline,
+ "default_shell", 10, l_def_shell, arg_def_shell,
+ "default_smooth", 10, l_def_smooth, arg_def_smooth,
+ "echo", 2, l_echo, arg_echo,
+ "escape", 2, l_escape, arg_escape,
+ "foreground", 1, l_foreground, arg_foreground,
+ "iostat", 1, l_iostat, arg_null,
+ "label", 2, l_label, arg_label,
+ "list", 2, l_list, arg_null,
+ "nlines", 1, l_def_nline, arg_def_nline,
+ "select", 2, l_select, arg_select,
+ "shell", 2, l_def_shell, arg_def_shell,
+ "smooth", 2, l_smooth, arg_smooth,
+ "source", 2, l_source, arg_source,
+ "terse", 2, l_terse, arg_terse,
+ "time", 2, l_time, arg_time,
+ "unalias", 3, l_unalias, arg_unalias,
+ "unset", 3, l_unset, arg_unset,
+ "variable", 1, l_variable, arg_null,
+ "window", 2, l_window, arg_window,
+ "write", 2, l_write, arg_write,
+ 0
+};
+
+struct lcmd_tab *
+lcmd_lookup(name)
+char *name;
+{
+ register struct lcmd_tab *p;
+
+ for (p = lcmd_tab; p->lc_name != 0; p++)
+ if (str_match(name, p->lc_name, p->lc_minlen))
+ return p;
+ return 0;
+}
+
+dosource(filename)
+char *filename;
+{
+ if (cx_beginfile(filename) < 0)
+ return -1;
+ p_start();
+ err_end();
+ cx_end();
+ return 0;
+}
+
+dolongcmd(buffer, arg, narg)
+char *buffer;
+struct value *arg;
+int narg;
+{
+ if (cx_beginbuf(buffer, arg, narg) < 0)
+ return -1;
+ p_start();
+ err_end();
+ cx_end();
+ return 0;
+}
diff --git a/usr.bin/window/lcmd.h b/usr.bin/window/lcmd.h
new file mode 100644
index 0000000..b3d1394
--- /dev/null
+++ b/usr.bin/window/lcmd.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lcmd.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define LCMD_NARG 20 /* maximum number of arguments */
+
+struct lcmd_tab {
+ char *lc_name;
+ int lc_minlen;
+ int (*lc_func)();
+ struct lcmd_arg *lc_arg;
+};
+
+struct lcmd_arg {
+ char *arg_name;
+ int arg_minlen;
+ int arg_flags;
+};
+
+ /* arg_flags bits */
+#define ARG_TYPE 0x0f /* type of arg */
+#define ARG_ANY 0x00 /* any type */
+#define ARG_NUM 0x01 /* must be a number */
+#define ARG_STR 0x02 /* must be a string */
+#define ARG_LIST 0x10 /* this arg can be a list */
+
+struct lcmd_tab *lcmd_lookup();
diff --git a/usr.bin/window/lcmd1.c b/usr.bin/window/lcmd1.c
new file mode 100644
index 0000000..ff96cac
--- /dev/null
+++ b/usr.bin/window/lcmd1.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lcmd1.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "string.h"
+#include "value.h"
+#include "lcmd.h"
+#include "var.h"
+
+struct lcmd_arg arg_window[] = {
+ { "row", 1, ARG_NUM },
+ { "column", 1, ARG_NUM },
+ { "nrows", 2, ARG_NUM },
+ { "ncols", 2, ARG_NUM },
+ { "nlines", 2, ARG_NUM },
+ { "label", 1, ARG_STR },
+ { "pty", 1, ARG_ANY },
+ { "frame", 1, ARG_ANY },
+ { "mapnl", 1, ARG_ANY },
+ { "keepopen", 1, ARG_ANY },
+ { "smooth", 1, ARG_ANY },
+ { "shell", 1, ARG_STR|ARG_LIST },
+ 0
+};
+
+l_window(v, a)
+struct value *v;
+register struct value *a;
+{
+ struct ww *w;
+ int col, row, ncol, nrow, id, nline;
+ char *label;
+ char haspty, hasframe, mapnl, keepopen, smooth;
+ char *shf, **sh;
+ char *argv[sizeof default_shell / sizeof *default_shell];
+ register char **pp;
+
+ if ((id = findid()) < 0)
+ return;
+ row = a->v_type == V_ERR ? 1 : a->v_num;
+ a++;
+ col = a->v_type == V_ERR ? 0 : a->v_num;
+ a++;
+ nrow = a->v_type == V_ERR ? wwnrow - row : a->v_num;
+ a++;
+ ncol = a->v_type == V_ERR ? wwncol - col : a->v_num;
+ a++;
+ nline = a->v_type == V_ERR ? default_nline : a->v_num;
+ a++;
+ label = a->v_type == V_ERR ? 0 : a->v_str;
+ if ((haspty = vtobool(++a, 1, -1)) < 0)
+ return;
+ if ((hasframe = vtobool(++a, 1, -1)) < 0)
+ return;
+ if ((mapnl = vtobool(++a, !haspty, -1)) < 0)
+ return;
+ if ((keepopen = vtobool(++a, 0, -1)) < 0)
+ return;
+ if ((smooth = vtobool(++a, default_smooth, -1)) < 0)
+ return;
+ if ((++a)->v_type != V_ERR) {
+ for (pp = argv; a->v_type != V_ERR &&
+ pp < &argv[sizeof argv/sizeof *argv-1]; pp++, a++)
+ *pp = a->v_str;
+ *pp = 0;
+ shf = *(sh = argv);
+ if (*sh = rindex(shf, '/'))
+ (*sh)++;
+ else
+ *sh = shf;
+ } else {
+ sh = default_shell;
+ shf = default_shellfile;
+ }
+ if ((w = openwin(id, row, col, nrow, ncol, nline, label, haspty,
+ hasframe, shf, sh)) == 0)
+ return;
+ w->ww_mapnl = mapnl;
+ w->ww_keepopen = keepopen;
+ w->ww_noupdate = !smooth;
+ v->v_type = V_NUM;
+ v->v_num = id + 1;
+}
+
+struct lcmd_arg arg_def_nline[] = {
+ { "nlines", 1, ARG_NUM },
+ 0
+};
+
+l_def_nline(v, a)
+register struct value *v, *a;
+{
+ v->v_num = default_nline;
+ v->v_type = V_NUM;
+ if (a->v_type != V_ERR)
+ default_nline = a->v_num;
+}
+
+struct lcmd_arg arg_smooth[] = {
+ { "window", 1, ARG_NUM },
+ { "flag", 1, ARG_ANY },
+ 0
+};
+
+l_smooth(v, a)
+register struct value *v, *a;
+{
+ struct ww *w;
+
+ v->v_type = V_NUM;
+ v->v_num = 0;
+ if ((w = vtowin(a++, selwin)) == 0)
+ return;
+ v->v_num = !w->ww_noupdate;
+ w->ww_noupdate = !vtobool(a, v->v_num, v->v_num);
+}
+
+struct lcmd_arg arg_def_smooth[] = {
+ { "flag", 1, ARG_ANY },
+ 0
+};
+
+l_def_smooth(v, a)
+register struct value *v, *a;
+{
+ v->v_type = V_NUM;
+ v->v_num = default_smooth;
+ default_smooth = vtobool(a, v->v_num, v->v_num);
+}
+
+struct lcmd_arg arg_select[] = {
+ { "window", 1, ARG_NUM },
+ 0
+};
+
+l_select(v, a)
+register struct value *v, *a;
+{
+ struct ww *w;
+
+ v->v_type = V_NUM;
+ v->v_num = selwin ? selwin->ww_id + 1 : -1;
+ if (a->v_type == V_ERR)
+ return;
+ if ((w = vtowin(a, (struct ww *)0)) == 0)
+ return;
+ setselwin(w);
+}
+
+struct lcmd_arg arg_debug[] = {
+ { "flag", 1, ARG_ANY },
+ 0
+};
+
+l_debug(v, a)
+register struct value *v, *a;
+{
+ v->v_type = V_NUM;
+ v->v_num = debug;
+ debug = vtobool(a, debug, debug);
+}
+
+struct lcmd_arg arg_escape[] = {
+ { "escapec", 1, ARG_STR },
+ 0
+};
+
+l_escape(v, a)
+register struct value *v, *a;
+{
+ char buf[2];
+
+ buf[0] = escapec;
+ buf[1] = 0;
+ if ((v->v_str = str_cpy(buf)) == 0) {
+ error("Out of memory.");
+ return;
+ }
+ v->v_type = V_STR;
+ if (a->v_type != V_ERR)
+ setescape(a->v_str);
+}
+
+struct lcmd_arg arg_label[] = {
+ { "window", 1, ARG_NUM },
+ { "label", 1, ARG_STR },
+ 0
+};
+
+/*ARGSUSED*/
+l_label(v, a)
+struct value *v;
+register struct value *a;
+{
+ struct ww *w;
+
+ if ((w = vtowin(a, selwin)) == 0)
+ return;
+ if ((++a)->v_type != V_ERR && setlabel(w, a->v_str) < 0)
+ error("Out of memory.");
+ reframe();
+}
+
+struct lcmd_arg arg_foreground[] = {
+ { "window", 1, ARG_NUM },
+ { "flag", 1, ARG_ANY },
+ 0
+};
+
+l_foreground(v, a)
+register struct value *v, *a;
+{
+ struct ww *w;
+ char flag;
+
+ if ((w = vtowin(a, selwin)) == 0)
+ return;
+ v->v_type = V_NUM;
+ v->v_num = isfg(w);
+ flag = vtobool(++a, v->v_num, v->v_num);
+ if (flag == v->v_num)
+ return;
+ deletewin(w);
+ addwin(w, flag);
+ reframe();
+}
+
+struct lcmd_arg arg_terse[] = {
+ { "flag", 1, ARG_ANY },
+ 0
+};
+
+l_terse(v, a)
+register struct value *v, *a;
+{
+ v->v_type = V_NUM;
+ v->v_num = terse;
+ setterse(vtobool(a, terse, terse));
+}
+
+struct lcmd_arg arg_source[] = {
+ { "filename", 1, ARG_STR },
+ 0
+};
+
+l_source(v, a)
+register struct value *v, *a;
+{
+ v->v_type = V_NUM;
+ if (a->v_type != V_ERR && dosource(a->v_str) < 0) {
+ error("Can't open %s.", a->v_str);
+ v->v_num = -1;
+ } else
+ v->v_num = 0;
+}
+
+struct lcmd_arg arg_write[] = {
+ { "window", 1, ARG_NUM },
+ { "", 0, ARG_ANY|ARG_LIST },
+ 0
+};
+
+/*ARGSUSED*/
+l_write(v, a)
+struct value *v;
+register struct value *a;
+{
+ char buf[20];
+ struct ww *w;
+
+ if ((w = vtowin(a++, selwin)) == 0)
+ return;
+ while (a->v_type != V_ERR) {
+ if (a->v_type == V_NUM) {
+ (void) sprintf(buf, "%d", a->v_num);
+ (void) write(w->ww_pty, buf, strlen(buf));
+ } else
+ (void) write(w->ww_pty, a->v_str, strlen(a->v_str));
+ if ((++a)->v_type != V_ERR)
+ (void) write(w->ww_pty, " ", 1);
+ }
+}
+
+struct lcmd_arg arg_close[] = {
+ { "window", 1, ARG_ANY|ARG_LIST },
+ 0
+};
+
+/*ARGSUSED*/
+l_close(v, a)
+struct value *v;
+register struct value *a;
+{
+ struct ww *w;
+
+ if (a->v_type == V_STR && str_match(a->v_str, "all", 3))
+ closewin((struct ww *)0);
+ else
+ for (; a->v_type != V_ERR; a++)
+ if ((w = vtowin(a, (struct ww *)0)) != 0)
+ closewin(w);
+}
+
+struct lcmd_arg arg_cursormodes[] = {
+ { "modes", 1, ARG_NUM },
+ 0
+};
+
+l_cursormodes(v, a)
+register struct value *v, *a;
+{
+
+ v->v_type = V_NUM;
+ v->v_num = wwcursormodes;
+ if (a->v_type != V_ERR)
+ wwsetcursormodes(a->v_num);
+}
+
+struct lcmd_arg arg_unset[] = {
+ { "variable", 1, ARG_ANY },
+ 0
+};
+
+l_unset(v, a)
+register struct value *v, *a;
+{
+ v->v_type = V_NUM;
+ switch (a->v_type) {
+ case V_ERR:
+ v->v_num = -1;
+ return;
+ case V_NUM:
+ if ((a->v_str = str_itoa(a->v_num)) == 0) {
+ error("Out of memory.");
+ v->v_num = -1;
+ return;
+ }
+ a->v_type = V_STR;
+ break;
+ }
+ v->v_num = var_unset(a->v_str);
+}
+
+struct ww *
+vtowin(v, w)
+register struct value *v;
+struct ww *w;
+{
+ switch (v->v_type) {
+ case V_ERR:
+ if (w != 0)
+ return w;
+ error("No window specified.");
+ return 0;
+ case V_STR:
+ error("%s: No such window.", v->v_str);
+ return 0;
+ }
+ if (v->v_num < 1 || v->v_num > NWINDOW
+ || (w = window[v->v_num - 1]) == 0) {
+ error("%d: No such window.", v->v_num);
+ return 0;
+ }
+ return w;
+}
+
+vtobool(v, def, err)
+register struct value *v;
+char def, err;
+{
+ switch (v->v_type) {
+ case V_NUM:
+ return v->v_num != 0;
+ case V_STR:
+ if (str_match(v->v_str, "true", 1)
+ || str_match(v->v_str, "on", 2)
+ || str_match(v->v_str, "yes", 1))
+ return 1;
+ else if (str_match(v->v_str, "false", 1)
+ || str_match(v->v_str, "off", 2)
+ || str_match(v->v_str, "no", 1))
+ return 0;
+ else {
+ error("%s: Illegal boolean value.", v->v_str);
+ return err;
+ }
+ /*NOTREACHED*/
+ case V_ERR:
+ return def;
+ }
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/window/lcmd2.c b/usr.bin/window/lcmd2.c
new file mode 100644
index 0000000..599cc98
--- /dev/null
+++ b/usr.bin/window/lcmd2.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lcmd2.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "string.h"
+#include "value.h"
+#include "var.h"
+#include "lcmd.h"
+#include "alias.h"
+#include <sys/types.h>
+#include <sys/resource.h>
+
+/*ARGSUSED*/
+l_iostat(v, a)
+struct value *v, *a;
+{
+ register struct ww *w;
+
+ if ((w = openiwin(16, "IO Statistics")) == 0) {
+ error("Can't open statistics window: %s.", wwerror());
+ return;
+ }
+ wwprintf(w, "ttflush\twrite\terror\tzero\tchar\n");
+ wwprintf(w, "%d\t%d\t%d\t%d\t%d\n",
+ wwnflush, wwnwr, wwnwre, wwnwrz, wwnwrc);
+ wwprintf(w, "token\tuse\tbad\tsaving\ttotal\tbaud\n");
+ wwprintf(w, "%d\t%d\t%d\t%d\t%d\t%d/%d (%.1f/%.1f)\n",
+ wwntokdef, wwntokuse, wwntokbad, wwntoksave, wwntokc,
+ wwntokc - wwntoksave ?
+ (int) ((float) wwbaud * wwntokc /
+ (wwntokc - wwntoksave)) :
+ wwbaud,
+ wwnwrc ? (int) ((float) wwbaud * (wwnwrc + wwntoksave) /
+ wwnwrc) :
+ wwbaud,
+ wwntokc - wwntoksave ?
+ (float) wwntokc / (wwntokc - wwntoksave) : 1.0,
+ wwnwrc ? (float) (wwnwrc + wwntoksave) / wwnwrc : 1.0);
+ wwprintf(w, "wwwrite\tattempt\tchar\n");
+ wwprintf(w, "%d\t%d\t%d\n",
+ wwnwwr, wwnwwra, wwnwwrc);
+ wwprintf(w, "wwupdat\tline\tmiss\tscan\tclreol\tclreos\tmiss\tline\n");
+ wwprintf(w, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ wwnupdate, wwnupdline, wwnupdmiss, wwnupdscan, wwnupdclreol,
+ wwnupdclreos, wwnupdclreosmiss, wwnupdclreosline);
+ wwprintf(w, "select\terror\tzero\n");
+ wwprintf(w, "%d\t%d\t%d\n",
+ wwnselect, wwnselecte, wwnselectz);
+ wwprintf(w, "read\terror\tzero\tchar\tack\tnack\tstat\terrorc\n");
+ wwprintf(w, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ wwnread, wwnreade, wwnreadz, wwnreadc, wwnreadack, wwnreadnack,
+ wwnreadstat, wwnreadec);
+ wwprintf(w, "ptyread\terror\tzero\tcontrol\tdata\tchar\n");
+ wwprintf(w, "%d\t%d\t%d\t%d\t%d\t%d\n",
+ wwnwread, wwnwreade, wwnwreadz,
+ wwnwreadp, wwnwreadd, wwnwreadc);
+ waitnl(w);
+ closeiwin(w);
+}
+
+struct lcmd_arg arg_time[] = {
+ { "who", 1, ARG_STR },
+ 0
+};
+
+/*ARGSUSED*/
+l_time(v, a)
+struct value *v;
+register struct value *a;
+{
+ register struct ww *w;
+ struct rusage rusage;
+ struct timeval timeval;
+ char *strtime();
+
+ if ((w = openiwin(8, "Timing and Resource Usage")) == 0) {
+ error("Can't open time window: %s.", wwerror());
+ return;
+ }
+
+ (void) gettimeofday(&timeval, (struct timezone *)0);
+ timeval.tv_sec -= starttime.tv_sec;
+ if ((timeval.tv_usec -= starttime.tv_usec) < 0) {
+ timeval.tv_sec--;
+ timeval.tv_usec += 1000000;
+ }
+ (void) getrusage(a->v_type == V_STR
+ && str_match(a->v_str, "children", 1)
+ ? RUSAGE_CHILDREN : RUSAGE_SELF, &rusage);
+
+ wwprintf(w, "%-15s %-15s %-15s\n",
+ "time", "utime", "stime");
+ wwprintf(w, "%-15s ", strtime(&timeval));
+ wwprintf(w, "%-15s ", strtime(&rusage.ru_utime));
+ wwprintf(w, "%-15s\n", strtime(&rusage.ru_stime));
+ wwprintf(w, "%-15s %-15s %-15s %-15s\n",
+ "maxrss", "ixrss", "idrss", "isrss");
+ wwprintf(w, "%-15ld %-15ld %-15ld %-15ld\n",
+ rusage.ru_maxrss, rusage.ru_ixrss,
+ rusage.ru_idrss, rusage.ru_isrss);
+ wwprintf(w, "%-7s %-7s %-7s %-7s %-7s %-7s %-7s %-7s %-7s %-7s\n",
+ "minflt", "majflt", "nswap", "inblk", "oublk",
+ "msgsnd", "msgrcv", "nsigs", "nvcsw", "nivcsw");
+ wwprintf(w, "%-7ld %-7ld %-7ld %-7ld %-7ld %-7ld %-7ld %-7ld %-7ld %-7ld\n",
+ rusage.ru_minflt, rusage.ru_majflt, rusage.ru_nswap,
+ rusage.ru_inblock, rusage.ru_oublock,
+ rusage.ru_msgsnd, rusage.ru_msgrcv, rusage.ru_nsignals,
+ rusage.ru_nvcsw, rusage.ru_nivcsw);
+
+ waitnl(w);
+ closeiwin(w);
+}
+
+char *
+strtime(t)
+register struct timeval *t;
+{
+ char fill = 0;
+ static char buf[20];
+ register char *p = buf;
+
+ if (t->tv_sec > 60*60) {
+ (void) sprintf(p, "%ld:", t->tv_sec / (60*60));
+ while (*p++)
+ ;
+ p--;
+ t->tv_sec %= 60*60;
+ fill++;
+ }
+ if (t->tv_sec > 60) {
+ (void) sprintf(p, fill ? "%02ld:" : "%ld:", t->tv_sec / 60);
+ while (*p++)
+ ;
+ p--;
+ t->tv_sec %= 60;
+ fill++;
+ }
+ (void) sprintf(p, fill ? "%02ld.%02d" : "%ld.%02ld",
+ t->tv_sec, t->tv_usec / 10000);
+ return buf;
+}
+
+/*ARGSUSED*/
+l_list(v, a)
+struct value *v, *a;
+{
+ register struct ww *w, *wp;
+ register i;
+ int n;
+
+ for (n = 0, i = 0; i < NWINDOW; i++)
+ if (window[i] != 0)
+ n++;
+ if (n == 0) {
+ error("No windows.");
+ return;
+ }
+ if ((w = openiwin(n + 2, "Windows")) == 0) {
+ error("Can't open listing window: %s.", wwerror());
+ return;
+ }
+ for (i = 0; i < NWINDOW; i++) {
+ if ((wp = window[i]) == 0)
+ continue;
+ wwprintf(w, "%c %c %-13s %-.*s\n",
+ wp == selwin ? '+' : (wp == lastselwin ? '-' : ' '),
+ i + '1',
+ wp->ww_state == WWS_HASPROC ? "" : "(No process)",
+ wwncol - 20,
+ wp->ww_label ? wp->ww_label : "(No label)");
+ }
+ waitnl(w);
+ closeiwin(w);
+}
+
+/*ARGSUSED*/
+l_variable(v, a)
+struct value *v, *a;
+{
+ register struct ww *w;
+ int printvar();
+
+ if ((w = openiwin(wwnrow - 3, "Variables")) == 0) {
+ error("Can't open variable window: %s.", wwerror());
+ return;
+ }
+ if (var_walk(printvar, (int)w) >= 0)
+ waitnl(w);
+ closeiwin(w);
+}
+
+printvar(w, r)
+register struct ww *w;
+register struct var *r;
+{
+ if (more(w, 0) == 2)
+ return -1;
+ wwprintf(w, "%16s ", r->r_name);
+ switch (r->r_val.v_type) {
+ case V_STR:
+ wwprintf(w, "%s\n", r->r_val.v_str);
+ break;
+ case V_NUM:
+ wwprintf(w, "%d\n", r->r_val.v_num);
+ break;
+ case V_ERR:
+ wwprintf(w, "ERROR\n");
+ break;
+ }
+ return 0;
+}
+
+struct lcmd_arg arg_def_shell[] = {
+ { "", 0, ARG_ANY|ARG_LIST },
+ 0
+};
+
+l_def_shell(v, a)
+ struct value *v, *a;
+{
+ register char **pp;
+ register struct value *vp;
+
+ if (a->v_type == V_ERR) {
+ if ((v->v_str = str_cpy(default_shellfile)) != 0)
+ v->v_type = V_STR;
+ return;
+ }
+ if (v->v_str = default_shellfile) {
+ v->v_type = V_STR;
+ for (pp = default_shell + 1; *pp; pp++) {
+ str_free(*pp);
+ *pp = 0;
+ }
+ }
+ for (pp = default_shell, vp = a;
+ vp->v_type != V_ERR &&
+ pp < &default_shell[sizeof default_shell/sizeof *default_shell-1];
+ pp++, vp++)
+ if ((*pp = vp->v_type == V_STR ?
+ str_cpy(vp->v_str) : str_itoa(vp->v_num)) == 0) {
+ /* just leave default_shell[] the way it is */
+ p_memerror();
+ break;
+ }
+ if (default_shellfile = *default_shell)
+ if (*default_shell = rindex(default_shellfile, '/'))
+ (*default_shell)++;
+ else
+ *default_shell = default_shellfile;
+}
+
+struct lcmd_arg arg_alias[] = {
+ { "", 0, ARG_STR },
+ { "", 0, ARG_STR|ARG_LIST },
+ 0
+};
+
+l_alias(v, a)
+ struct value *v, *a;
+{
+ if (a->v_type == V_ERR) {
+ register struct ww *w;
+ int printalias();
+
+ if ((w = openiwin(wwnrow - 3, "Aliases")) == 0) {
+ error("Can't open alias window: %s.", wwerror());
+ return;
+ }
+ if (alias_walk(printalias, (int)w) >= 0)
+ waitnl(w);
+ closeiwin(w);
+ } else {
+ register struct alias *ap = 0;
+
+ if (ap = alias_lookup(a->v_str)) {
+ if ((v->v_str = str_cpy(ap->a_buf)) == 0) {
+ p_memerror();
+ return;
+ }
+ v->v_type = V_STR;
+ }
+ if (a[1].v_type == V_STR) {
+ register struct value *vp;
+ register char *p, *q;
+ char *str;
+ register n;
+
+ for (n = 0, vp = a + 1; vp->v_type != V_ERR; vp++, n++)
+ for (p = vp->v_str; *p; p++, n++)
+ ;
+ if ((str = str_alloc(n)) == 0) {
+ p_memerror();
+ return;
+ }
+ for (q = str, vp = a + 1; vp->v_type != V_ERR;
+ vp++, q[-1] = ' ')
+ for (p = vp->v_str; *q++ = *p++;)
+ ;
+ q[-1] = 0;
+ if ((ap = alias_set(a[0].v_str, (char *)0)) == 0) {
+ p_memerror();
+ str_free(str);
+ return;
+ }
+ ap->a_buf = str;
+ }
+ }
+}
+
+printalias(w, a)
+register struct ww *w;
+register struct alias *a;
+{
+ if (more(w, 0) == 2)
+ return -1;
+ wwprintf(w, "%16s %s\n", a->a_name, a->a_buf);
+ return 0;
+}
+
+struct lcmd_arg arg_unalias[] = {
+ { "alias", 1, ARG_STR },
+ 0
+};
+
+l_unalias(v, a)
+struct value *v, *a;
+{
+ if (a->v_type == ARG_STR)
+ v->v_num = alias_unset(a->v_str);
+ v->v_type = V_NUM;
+}
+
+struct lcmd_arg arg_echo[] = {
+ { "window", 1, ARG_NUM },
+ { "", 0, ARG_ANY|ARG_LIST },
+ 0
+};
+
+/*ARGSUSED*/
+l_echo(v, a)
+struct value *v;
+register struct value *a;
+{
+ char buf[20];
+ struct ww *w;
+
+ if ((w = vtowin(a++, selwin)) == 0)
+ return;
+ while (a->v_type != V_ERR) {
+ if (a->v_type == V_NUM) {
+ (void) sprintf(buf, "%d", a->v_num);
+ (void) wwwrite(w, buf, strlen(buf));
+ } else
+ (void) wwwrite(w, a->v_str, strlen(a->v_str));
+ if ((++a)->v_type != V_ERR)
+ (void) wwwrite(w, " ", 1);
+ }
+ (void) wwwrite(w, "\r\n", 2);
+}
diff --git a/usr.bin/window/local.h b/usr.bin/window/local.h
new file mode 100644
index 0000000..90378d8
--- /dev/null
+++ b/usr.bin/window/local.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)local.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Things of local interest.
+ */
+
+#define RUNCOM ".windowrc"
+#define ESCAPEC ctrl('p')
+#define NLINE 48 /* default text buffer size */
+
+#ifdef TERMINFO
+#define _PATH_CAPTOINFO "/usr/5bin/captoinfo"
+#define _PATH_TIC "/usr/5bin/tic"
+#define _PATH_RM "/bin/rm"
+#endif
diff --git a/usr.bin/window/main.c b/usr.bin/window/main.c
new file mode 100644
index 0000000..16fe0a5
--- /dev/null
+++ b/usr.bin/window/main.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include "defs.h"
+#include <paths.h>
+#include <stdio.h>
+#include "string.h"
+#include "char.h"
+#include "local.h"
+
+#define next(a) (*++*(a) ? *(a) : (*++(a) ? *(a) : (char *)usage()))
+
+/*ARGSUSED*/
+main(argc, argv)
+char **argv;
+{
+ register char *p;
+ char fflag = 0;
+ char dflag = 0;
+ char xflag = 0;
+ char *cmd = 0;
+ char tflag = 0;
+
+ escapec = ESCAPEC;
+ if (p = rindex(*argv, '/'))
+ p++;
+ else
+ p = *argv;
+ debug = strcmp(p, "a.out") == 0;
+ while (*++argv) {
+ if (**argv == '-') {
+ switch (*++*argv) {
+ case 'f':
+ fflag++;
+ break;
+ case 'c':
+ if (cmd != 0) {
+ (void) fprintf(stderr,
+ "Only one -c allowed.\n");
+ (void) usage();
+ }
+ cmd = next(argv);
+ break;
+ case 'e':
+ setescape(next(argv));
+ break;
+ case 't':
+ tflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'D':
+ debug = !debug;
+ break;
+ case 'x':
+ xflag++;
+ break;
+ default:
+ (void) usage();
+ }
+ } else
+ (void) usage();
+ }
+ if ((p = getenv("SHELL")) == 0)
+ p = _PATH_BSHELL;
+ if ((default_shellfile = str_cpy(p)) == 0) {
+ (void) fprintf(stderr, "Out of memory.\n");
+ exit(1);
+ }
+ if (p = rindex(default_shellfile, '/'))
+ p++;
+ else
+ p = default_shellfile;
+ default_shell[0] = p;
+ default_shell[1] = 0;
+ default_nline = NLINE;
+ default_smooth = 1;
+ (void) gettimeofday(&starttime, (struct timezone *)0);
+ if (wwinit() < 0) {
+ (void) fprintf(stderr, "%s.\n", wwerror());
+ exit(1);
+ }
+
+#ifdef OLD_TTY
+ if (debug)
+ wwnewtty.ww_tchars.t_quitc = wwoldtty.ww_tchars.t_quitc;
+ if (xflag) {
+ wwnewtty.ww_tchars.t_stopc = wwoldtty.ww_tchars.t_stopc;
+ wwnewtty.ww_tchars.t_startc = wwoldtty.ww_tchars.t_startc;
+ }
+#else
+ if (debug) {
+ wwnewtty.ww_termios.c_cc[VQUIT] =
+ wwoldtty.ww_termios.c_cc[VQUIT];
+ wwnewtty.ww_termios.c_lflag |= ISIG;
+ }
+ if (xflag) {
+ wwnewtty.ww_termios.c_cc[VSTOP] =
+ wwoldtty.ww_termios.c_cc[VSTOP];
+ wwnewtty.ww_termios.c_cc[VSTART] =
+ wwoldtty.ww_termios.c_cc[VSTART];
+ wwnewtty.ww_termios.c_iflag |= IXON;
+ }
+#endif
+ if (debug || xflag)
+ (void) wwsettty(0, &wwnewtty);
+
+ if ((cmdwin = wwopen(wwbaud > 2400 ? WWO_REVERSE : 0, 1, wwncol,
+ 0, 0, 0)) == 0) {
+ wwflush();
+ (void) fprintf(stderr, "%s.\r\n", wwerror());
+ goto bad;
+ }
+ cmdwin->ww_mapnl = 1;
+ cmdwin->ww_nointr = 1;
+ cmdwin->ww_noupdate = 1;
+ cmdwin->ww_unctrl = 1;
+ if ((framewin = wwopen(WWO_GLASS|WWO_FRAME, wwnrow, wwncol, 0, 0, 0))
+ == 0) {
+ wwflush();
+ (void) fprintf(stderr, "%s.\r\n", wwerror());
+ goto bad;
+ }
+ wwadd(framewin, &wwhead);
+ if ((boxwin = wwopen(WWO_GLASS, wwnrow, wwncol, 0, 0, 0)) == 0) {
+ wwflush();
+ (void) fprintf(stderr, "%s.\r\n", wwerror());
+ goto bad;
+ }
+ fgwin = framewin;
+
+ wwupdate();
+ wwflush();
+ setvars();
+
+ setterse(tflag);
+ setcmd(1);
+ if (cmd != 0)
+ (void) dolongcmd(cmd, (struct value *)0, 0);
+ if (!fflag)
+ if (dflag || doconfig() < 0)
+ dodefault();
+ if (selwin != 0)
+ setcmd(0);
+
+ mloop();
+
+bad:
+ wwend(1);
+ return 0;
+}
+
+usage()
+{
+ (void) fprintf(stderr, "Usage: window [-e escape-char] [-c command] [-t] [-f] [-d]\n");
+ exit(1);
+ return 0; /* for lint */
+}
diff --git a/usr.bin/window/mloop.c b/usr.bin/window/mloop.c
new file mode 100644
index 0000000..68584e7
--- /dev/null
+++ b/usr.bin/window/mloop.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mloop.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include "defs.h"
+
+mloop()
+{
+ while (!quit) {
+ if (incmd) {
+ docmd();
+ } else if (wwcurwin->ww_state != WWS_HASPROC) {
+ if (!wwcurwin->ww_keepopen)
+ closewin(wwcurwin);
+ setcmd(1);
+ if (wwpeekc() == escapec)
+ (void) wwgetc();
+ error("Process died.");
+ } else {
+ register struct ww *w = wwcurwin;
+ register char *p;
+ register n;
+
+ if (wwibp >= wwibq)
+ wwiomux();
+ for (p = wwibp; p < wwibq && wwmaskc(*p) != escapec;
+ p++)
+ ;
+ if ((n = p - wwibp) > 0) {
+ if (!w->ww_ispty && w->ww_stopped)
+ startwin(w);
+#if defined(sun) && !defined(BSD)
+ /* workaround for SunOS pty bug */
+ while (--n >= 0)
+ (void) write(w->ww_pty, wwibp++, 1);
+#else
+ (void) write(w->ww_pty, wwibp, n);
+ wwibp = p;
+#endif
+ }
+ if (wwpeekc() == escapec) {
+ (void) wwgetc();
+ setcmd(1);
+ }
+ }
+ }
+}
diff --git a/usr.bin/window/mystring.h b/usr.bin/window/mystring.h
new file mode 100644
index 0000000..08cae56
--- /dev/null
+++ b/usr.bin/window/mystring.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)string.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define STR_DEBUG
+
+char *str_cpy();
+char *str_ncpy();
+char *str_cat();
+char *str_itoa();
+
+#define str_cmp(a, b) strcmp(a, b)
+
+#ifdef STR_DEBUG
+struct string {
+ struct string *s_forw;
+ struct string *s_back;
+ char s_data[1];
+};
+
+struct string str_head;
+
+#define str_offset ((unsigned)str_head.s_data - (unsigned)&str_head)
+#define str_stos(s) ((struct string *)((unsigned)(s) - str_offset))
+
+char *str_alloc();
+int str_free();
+#else
+#define str_free(s) free(s)
+#define str_alloc(s) malloc(s)
+#endif
diff --git a/usr.bin/window/parser.h b/usr.bin/window/parser.h
new file mode 100644
index 0000000..747ae4b
--- /dev/null
+++ b/usr.bin/window/parser.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)parser.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include "value.h"
+#include "context.h"
+#include "token.h"
+#include "string.h"
+
+#define p_erred() (cx.x_erred)
+#define p_synerred() (cx.x_synerred)
+#define p_clearerr() (cx.x_erred = cx.x_synerred = 0)
+#define p_abort() (cx.x_abort)
diff --git a/usr.bin/window/parser1.c b/usr.bin/window/parser1.c
new file mode 100644
index 0000000..9cc9790
--- /dev/null
+++ b/usr.bin/window/parser1.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parser1.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "parser.h"
+
+p_start()
+{
+ char flag = 1;
+
+ (void) s_gettok();
+ for (;;) {
+ p_statementlist(flag);
+ if (token == T_EOF || p_abort())
+ break;
+ flag = 0;
+ p_synerror();
+ while (token != T_EOL && token != T_EOF) {
+ if (token == T_STR)
+ str_free(token_str);
+ (void) s_gettok();
+ }
+ if (token == T_EOL)
+ (void) s_gettok();
+ p_clearerr();
+ }
+}
+
+p_statementlist(flag)
+char flag;
+{
+ for (; p_statement(flag) >= 0; p_clearerr())
+ ;
+}
+
+p_statement(flag)
+char flag;
+{
+ switch (token) {
+ case T_EOL:
+ (void) s_gettok();
+ return 0;
+ case T_IF:
+ return p_if(flag);
+ default:
+ return p_expression(flag);
+ }
+}
+
+p_if(flag)
+char flag;
+{
+ struct value t;
+ char true = 0;
+
+top:
+ (void) s_gettok();
+
+ if (p_expr(&t, flag) < 0) {
+ p_synerror();
+ return -1;
+ }
+ switch (t.v_type) {
+ case V_NUM:
+ true = !true && t.v_num != 0;
+ break;
+ case V_STR:
+ p_error("if: Numeric value required.");
+ str_free(t.v_str);
+ case V_ERR:
+ flag = 0;
+ break;
+ }
+
+ if (token != T_THEN) {
+ p_synerror();
+ return -1;
+ }
+
+ (void) s_gettok();
+ p_statementlist(flag && true);
+ if (p_erred())
+ return -1;
+
+ if (token == T_ELSIF)
+ goto top;
+
+ if (token == T_ELSE) {
+ (void) s_gettok();
+ p_statementlist(flag && !true);
+ if (p_erred())
+ return -1;
+ }
+
+ if (token == T_ENDIF) {
+ (void) s_gettok();
+ return 0;
+ }
+
+ p_synerror();
+ return -1;
+}
+
+p_expression(flag)
+char flag;
+{
+ struct value t;
+ char *cmd;
+ int p_function(), p_assign();
+
+ switch (token) {
+ case T_NUM:
+ t.v_type = V_NUM;
+ t.v_num = token_num;
+ (void) s_gettok();
+ break;
+ case T_STR:
+ t.v_type = V_STR;
+ t.v_str = token_str;
+ (void) s_gettok();
+ break;
+ default:
+ if (p_expr(&t, flag) < 0)
+ return -1;
+ if (token == T_EOF) {
+ val_free(t);
+ return 0;
+ }
+ }
+ if (token != T_ASSIGN && p_convstr(&t) < 0)
+ return -1;
+ cmd = t.v_type == V_STR ? t.v_str : 0;
+ if ((*(token == T_ASSIGN ? p_assign : p_function))(cmd, &t, flag) < 0) {
+ if (cmd)
+ str_free(cmd);
+ return -1;
+ }
+ if (cmd)
+ str_free(cmd);
+ val_free(t);
+ if (token == T_EOL)
+ (void) s_gettok();
+ else if (token != T_EOF) {
+ p_synerror();
+ return -1;
+ }
+ return 0;
+}
+
+p_convstr(v)
+register struct value *v;
+{
+ if (v->v_type != V_NUM)
+ return 0;
+ if ((v->v_str = str_itoa(v->v_num)) == 0) {
+ p_memerror();
+ v->v_type = V_ERR;
+ return -1;
+ }
+ v->v_type = V_STR;
+ return 0;
+}
+
+p_synerror()
+{
+ if (!cx.x_synerred) {
+ cx.x_synerred = cx.x_erred = 1;
+ error("Syntax error.");
+ }
+}
+
+/*VARARGS1*/
+p_error(msg, a, b, c)
+char *msg;
+{
+ if (!cx.x_erred) {
+ cx.x_erred = 1;
+ error(msg, a, b, c);
+ }
+}
+
+p_memerror()
+{
+ cx.x_erred = cx.x_abort = 1;
+ error("Out of memory.");
+}
diff --git a/usr.bin/window/parser2.c b/usr.bin/window/parser2.c
new file mode 100644
index 0000000..e9b2f55
--- /dev/null
+++ b/usr.bin/window/parser2.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parser2.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "parser.h"
+#include "var.h"
+#include "lcmd.h"
+#include "alias.h"
+
+/*
+ * name == 0 means we don't have a function name but
+ * want to parse the arguments anyway. flag == 0 in this case.
+ */
+p_function(name, v, flag)
+char *name;
+register struct value *v;
+{
+ struct value t;
+ register struct lcmd_tab *c = 0;
+ register struct alias *a = 0;
+ register struct lcmd_arg *ap; /* this arg */
+ struct lcmd_arg *lp = 0; /* list arg */
+ register i;
+ struct value av[LCMD_NARG + 1];
+ register struct value *vp;
+
+ if (name != 0)
+ if (c = lcmd_lookup(name))
+ name = c->lc_name;
+ else if (a = alias_lookup(name))
+ name = a->a_name;
+ else {
+ p_error("%s: No such command or alias.", name);
+ flag = 0;
+ }
+
+ for (vp = av; vp < &av[LCMD_NARG + 1]; vp++)
+ vp->v_type = V_ERR;
+
+ if (token == T_LP)
+ (void) s_gettok();
+ i = 0;
+ for (;;) {
+ ap = 0;
+ vp = 0;
+ if (token == T_COMMA) /* null argument */
+ t.v_type = V_ERR;
+ else {
+ if (p_expr0(&t, flag) < 0)
+ break;
+ if (t.v_type == V_ERR)
+ flag = 0;
+ }
+ if (token != T_ASSIGN) {
+ if (i >= LCMD_NARG ||
+ c != 0 && (ap = lp) == 0 &&
+ (ap = c->lc_arg + i)->arg_name == 0) {
+ p_error("%s: Too many arguments.", name);
+ flag = 0;
+ } else
+ vp = &av[i++];
+ } else {
+ char *tmp;
+ if (p_convstr(&t) < 0)
+ goto abort;
+ tmp = t.v_type == V_STR ? t.v_str : 0;
+ (void) s_gettok();
+ if (p_expr(&t, flag) < 0) {
+ if (tmp)
+ str_free(tmp);
+ p_synerror();
+ goto abort;
+ }
+ if (t.v_type == V_ERR)
+ flag = 0;
+ if (tmp) {
+ if (c == 0) {
+ /* an aliase */
+ p_error("%s: Bad alias syntax.", name);
+ flag = 0;
+ } else {
+ for (ap = c->lc_arg, vp = av;
+ ap != 0 && ap->arg_name != 0 &&
+ (*ap->arg_name == '\0' ||
+ !str_match(tmp, ap->arg_name,
+ ap->arg_minlen));
+ ap++, vp++)
+ ;
+ if (ap == 0 || ap->arg_name == 0) {
+ p_error("%s: Unknown argument \"%s\".",
+ name, tmp);
+ flag = 0;
+ ap = 0;
+ vp = 0;
+ }
+ }
+ str_free(tmp);
+ }
+ }
+ if (ap != 0) {
+ if (ap->arg_flags & ARG_LIST) {
+ i = vp - av + 1;
+ lp = ap;
+ }
+ if (vp->v_type != V_ERR) {
+ if (*ap->arg_name)
+ p_error("%s: Argument %d (%s) duplicated.",
+ name, vp - av + 1,
+ ap->arg_name);
+ else
+ p_error("%s: Argument %d duplicated.",
+ name, vp - av + 1);
+ flag = 0;
+ vp = 0;
+ } else if (t.v_type == V_ERR) {
+ /* do nothing */
+ } else if ((ap->arg_flags&ARG_TYPE) == ARG_NUM &&
+ t.v_type != V_NUM ||
+ (ap->arg_flags&ARG_TYPE) == ARG_STR &&
+ t.v_type != V_STR) {
+ if (*ap->arg_name)
+ p_error("%s: Argument %d (%s) type mismatch.",
+ name, vp - av + 1,
+ ap->arg_name);
+ else
+ p_error("%s: Argument %d type mismatch.",
+ name, vp - av + 1);
+ flag = 0;
+ vp = 0;
+ }
+ }
+ if (vp != 0)
+ *vp = t;
+ else
+ val_free(t);
+ if (token == T_COMMA)
+ (void) s_gettok();
+ }
+
+ if (p_erred())
+ flag = 0;
+ if (token == T_RP)
+ (void) s_gettok();
+ else if (token != T_EOL && token != T_EOF)
+ flag = 0; /* look for legal follow set */
+ v->v_type = V_ERR;
+ if (flag)
+ if (c != 0)
+ (*c->lc_func)(v, av);
+ else
+ if (a->a_flags & A_INUSE)
+ p_error("%s: Recursive alias.", a->a_name);
+ else {
+ a->a_flags |= A_INUSE;
+ if (dolongcmd(a->a_buf, av, i) < 0)
+ p_memerror();
+ a->a_flags &= ~A_INUSE;
+ }
+ if (p_abort()) {
+ val_free(*v);
+ v->v_type = V_ERR;
+ goto abort;
+ }
+ for (vp = av; vp < &av[LCMD_NARG]; vp++)
+ val_free(*vp);
+ return 0;
+abort:
+ for (vp = av; vp < &av[LCMD_NARG]; vp++)
+ val_free(*vp);
+ return -1;
+}
+
+p_assign(name, v, flag)
+char *name;
+struct value *v;
+char flag;
+{
+ (void) s_gettok();
+
+ if (p_expr(v, flag) < 0) {
+ p_synerror();
+ return -1;
+ }
+ switch (v->v_type) {
+ case V_STR:
+ case V_NUM:
+ if (flag && var_set(name, v) == 0) {
+ p_memerror();
+ val_free(*v);
+ return -1;
+ }
+ break;
+ }
+ return 0;
+}
diff --git a/usr.bin/window/parser3.c b/usr.bin/window/parser3.c
new file mode 100644
index 0000000..d4aa80d
--- /dev/null
+++ b/usr.bin/window/parser3.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parser3.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "parser.h"
+
+/*
+ * =
+ * ? :
+ * ||
+ * &&
+ * |
+ * ^
+ * &
+ * == !=
+ * <= >=
+ * << >>
+ * + -
+ * * / %
+ * unary - + ~ !
+ */
+p_expr(v, flag)
+register struct value *v;
+char flag;
+{
+ struct value t;
+ int ret;
+
+ if (p_expr0(&t, flag) < 0)
+ return -1;
+
+ if (token != T_ASSIGN) {
+ *v = t;
+ return 0;
+ }
+ switch (t.v_type) {
+ case V_NUM:
+ p_error("%d: Not a variable.", t.v_num);
+ case V_ERR:
+ t.v_str = 0;
+ break;
+ }
+ ret = p_assign(t.v_str, v, flag);
+ if (t.v_str != 0)
+ str_free(t.v_str);
+ return ret;
+}
+
+/*
+ * ? :
+ */
+p_expr0(v, flag)
+register struct value *v;
+char flag;
+{
+ struct value t;
+ char true;
+
+ if (p_expr1(v, flag) < 0)
+ return -1;
+ if (token != T_QUEST)
+ return 0;
+ switch (v->v_type) {
+ case V_NUM:
+ true = v->v_num != 0;
+ break;
+ case V_STR:
+ p_error("?: Numeric left operand required.");
+ str_free(v->v_str);
+ v->v_type = V_ERR;
+ case V_ERR:
+ flag = 0;
+ break;
+ }
+ (void) s_gettok();
+ v->v_type = V_ERR;
+ if ((flag && true ? p_expr1(v, 1) : p_expr1(&t, 0)) < 0)
+ return -1;
+ if (token != T_COLON) {
+ val_free(*v);
+ p_synerror();
+ return -1;
+ }
+ (void) s_gettok();
+ return flag && !true ? p_expr1(v, 1) : p_expr1(&t, 0);
+}
+
+/*
+ * ||
+ */
+p_expr1(v, flag)
+register struct value *v;
+char flag;
+{
+ char true = 0;
+
+ if (p_expr2(v, flag) < 0)
+ return -1;
+ if (token != T_OROR)
+ return 0;
+ for (;;) {
+ switch (v->v_type) {
+ case V_NUM:
+ v->v_num = true = true || v->v_num != 0;
+ break;
+ case V_STR:
+ p_error("||: Numeric operands required.");
+ str_free(v->v_str);
+ v->v_type = V_ERR;
+ case V_ERR:
+ flag = 0;
+ break;
+ }
+ if (token != T_OROR)
+ return 0;
+ (void) s_gettok();
+ if (p_expr2(v, flag && !true) < 0)
+ return -1;
+ }
+}
+
+/*
+ * &&
+ */
+p_expr2(v, flag)
+register struct value *v;
+char flag;
+{
+ char true = 1;
+
+ if (p_expr3_10(3, v, flag) < 0)
+ return -1;
+ if (token != T_ANDAND)
+ return 0;
+ for (;;) {
+ switch (v->v_type) {
+ case V_NUM:
+ v->v_num = true = true && v->v_num != 0;
+ break;
+ case V_STR:
+ p_error("&&: Numeric operands required.");
+ str_free(v->v_str);
+ v->v_type = V_ERR;
+ case V_ERR:
+ flag = 0;
+ break;
+ }
+ if (token != T_ANDAND)
+ return 0;
+ (void) s_gettok();
+ if (p_expr3_10(3, v, flag && true) < 0)
+ return -1;
+ }
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/window/parser4.c b/usr.bin/window/parser4.c
new file mode 100644
index 0000000..16ffb0c
--- /dev/null
+++ b/usr.bin/window/parser4.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parser4.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "parser.h"
+
+/*
+ * | 3
+ * ^ 4
+ * & 5
+ * == != 6
+ * < <= > >= 7
+ * << >> 8
+ * + - 9
+ * * / % 10
+ */
+p_expr3_10(level, v, flag)
+register struct value *v;
+char flag;
+{
+ struct value l, r;
+ int op;
+ char *opname;
+
+ if ((level == 10 ? p_expr11(v, flag)
+ : p_expr3_10(level + 1, v, flag)) < 0)
+ return -1;
+ for (;;) {
+ switch (level) {
+ case 3:
+ if (token != T_OR)
+ return 0;
+ opname = "|";
+ break;
+ case 4:
+ if (token != T_XOR)
+ return 0;
+ opname = "^";
+ break;
+ case 5:
+ if (token != T_AND)
+ return 0;
+ opname = "&";
+ break;
+ case 6:
+ if (token == T_EQ)
+ opname = "==";
+ else if (token == T_NE)
+ opname = "!=";
+ else
+ return 0;
+ break;
+ case 7:
+ switch (token) {
+ case T_LT:
+ opname = "<";
+ break;
+ case T_LE:
+ opname = "<=";
+ break;
+ case T_GT:
+ opname = ">";
+ break;
+ case T_GE:
+ opname = ">=";
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 8:
+ if (token == T_LS)
+ opname = "<<";
+ else if (token == T_RS)
+ opname = ">>";
+ else
+ return 0;
+ break;
+ case 9:
+ if (token == T_PLUS)
+ opname = "+";
+ else if (token == T_MINUS)
+ opname = "-";
+ else
+ return 0;
+ break;
+ case 10:
+ switch (token) {
+ case T_MUL:
+ opname = "*";
+ break;
+ case T_DIV:
+ opname = "/";
+ break;
+ case T_MOD:
+ opname = "%";
+ break;
+ default:
+ return 0;
+ }
+ break;
+ }
+ l = *v;
+ if (l.v_type == V_ERR)
+ flag = 0;
+
+ op = token;
+ (void) s_gettok();
+ if ((level == 10 ? p_expr11(&r, flag)
+ : p_expr3_10(level + 1, &r, flag)) < 0) {
+ p_synerror();
+ val_free(l);
+ return -1;
+ }
+
+ if (r.v_type == V_ERR)
+ flag = 0;
+ else switch (op) {
+ case T_EQ:
+ case T_NE:
+ case T_LT:
+ case T_LE:
+ case T_GT:
+ case T_GE:
+ case T_PLUS:
+ if (l.v_type == V_STR) {
+ if (r.v_type == V_NUM)
+ if (p_convstr(&r) < 0)
+ flag = 0;
+ } else
+ if (r.v_type == V_STR)
+ if (p_convstr(&l) < 0)
+ flag = 0;
+ break;
+ case T_LS:
+ case T_RS:
+ if (r.v_type == V_STR) {
+ char *p = r.v_str;
+ r.v_type = V_NUM;
+ r.v_num = strlen(p);
+ str_free(p);
+ }
+ break;
+ case T_OR:
+ case T_XOR:
+ case T_AND:
+ case T_MINUS:
+ case T_MUL:
+ case T_DIV:
+ case T_MOD:
+ default:
+ if (l.v_type == V_STR || r.v_type == V_STR) {
+ p_error("%s: Numeric operands required.",
+ opname);
+ flag = 0;
+ }
+ }
+ if (!flag) {
+ val_free(l);
+ val_free(r);
+ v->v_type = V_ERR;
+ if (p_abort())
+ return -1;
+ continue;
+ }
+
+ v->v_type = V_NUM;
+ switch (op) {
+ case T_EQ:
+ case T_NE:
+ case T_LT:
+ case T_LE:
+ case T_GT:
+ case T_GE:
+ if (l.v_type == V_STR) {
+ int tmp = strcmp(l.v_str, r.v_str);
+ str_free(l.v_str);
+ str_free(r.v_str);
+ l.v_type = V_NUM;
+ l.v_num = tmp;
+ r.v_type = V_NUM;
+ r.v_num = 0;
+ }
+ break;
+ }
+ switch (op) {
+ case T_OR:
+ v->v_num = l.v_num | r.v_num;
+ break;
+ case T_XOR:
+ v->v_num = l.v_num ^ r.v_num;
+ break;
+ case T_AND:
+ v->v_num = l.v_num & r.v_num;
+ break;
+ case T_EQ:
+ v->v_num = l.v_num == r.v_num;
+ break;
+ case T_NE:
+ v->v_num = l.v_num != r.v_num;
+ break;
+ case T_LT:
+ v->v_num = l.v_num < r.v_num;
+ break;
+ case T_LE:
+ v->v_num = l.v_num <= r.v_num;
+ break;
+ case T_GT:
+ v->v_num = l.v_num > r.v_num;
+ break;
+ case T_GE:
+ v->v_num = l.v_num >= r.v_num;
+ break;
+ case T_LS:
+ if (l.v_type == V_STR) {
+ int i;
+ if ((i = strlen(l.v_str)) > r.v_num)
+ i = r.v_num;
+ v->v_str = str_ncpy(l.v_str, i);
+ v->v_type = V_STR;
+ } else
+ v->v_num = l.v_num << r.v_num;
+ break;
+ case T_RS:
+ if (l.v_type == V_STR) {
+ int i;
+ if ((i = strlen(l.v_str)) > r.v_num)
+ i -= r.v_num;
+ else
+ i = 0;
+ v->v_str = str_cpy(l.v_str + i);
+ v->v_type = V_STR;
+ } else
+ v->v_num = l.v_num >> r.v_num;
+ break;
+ case T_PLUS:
+ if (l.v_type == V_STR) {
+ v->v_str = str_cat(l.v_str, r.v_str);
+ v->v_type = V_STR;
+ } else
+ v->v_num = l.v_num + r.v_num;
+ break;
+ case T_MINUS:
+ v->v_num = l.v_num - r.v_num;
+ break;
+ case T_MUL:
+ v->v_num = l.v_num * r.v_num;
+ break;
+ case T_DIV:
+ v->v_num = l.v_num / r.v_num;
+ break;
+ case T_MOD:
+ v->v_num = l.v_num % r.v_num;
+ break;
+ }
+ val_free(l);
+ val_free(r);
+ }
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/window/parser5.c b/usr.bin/window/parser5.c
new file mode 100644
index 0000000..b179756
--- /dev/null
+++ b/usr.bin/window/parser5.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parser5.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "parser.h"
+#include "var.h"
+
+/*
+ * unary $ $? + - ! ~
+ */
+p_expr11(v, flag)
+register struct value *v;
+char flag;
+{
+ int op;
+ char *opname;
+
+ switch (token) {
+ case T_DOLLAR:
+ opname = "$";
+ break;
+ case T_DQ:
+ opname = "$?";
+ break;
+ case T_PLUS:
+ opname = "unary +";
+ break;
+ case T_MINUS:
+ opname = "unary -";
+ break;
+ case T_NOT:
+ opname = "!";
+ break;
+ case T_COMP:
+ opname = "~";
+ break;
+ default:
+ return p_expr12(v, flag);
+ }
+ op = token;
+ (void) s_gettok();
+ if (p_expr11(v, flag) < 0)
+ return -1;
+ switch (v->v_type) {
+ case V_NUM:
+ break;
+ case V_STR:
+ switch (op) {
+ case T_MINUS:
+ case T_NOT:
+ case T_COMP:
+ p_error("%s: Numeric operand required.", opname);
+ str_free(v->v_str);
+ v->v_type = V_ERR;
+ return 0;
+ }
+ break;
+ case V_ERR:
+ return 0;
+ }
+ switch (op) {
+ case T_DOLLAR:
+ case T_DQ:
+ if (v->v_type == V_NUM) {
+ int tmp = cx.x_type == X_BUF && cx.x_arg != 0 &&
+ v->v_num > 0 && v->v_num <= cx.x_narg;
+ if (op == T_DQ)
+ v->v_num = tmp;
+ else if (tmp)
+ *v = cx.x_arg[v->v_num - 1];
+ else {
+ p_error("%d: No such argument.", v->v_num);
+ v->v_type = V_ERR;
+ }
+ } else {
+ char *name = v->v_str;
+ struct var *r = var_lookup(name);
+ if (op == T_DQ) {
+ v->v_type = V_NUM;
+ v->v_num = r != 0;
+ } else if (r != 0)
+ *v = r->r_val;
+ else {
+ p_error("%s: Undefined variable.", name);
+ v->v_type = V_ERR;
+ }
+ str_free(name);
+ }
+ if (v->v_type == V_STR && (v->v_str = str_cpy(v->v_str)) == 0) {
+ p_memerror();
+ return -1;
+ }
+ break;
+ case T_MINUS:
+ v->v_num = - v->v_num;
+ break;
+ case T_NOT:
+ v->v_num = ! v->v_num;
+ break;
+ case T_COMP:
+ v->v_num = ~ v->v_num;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * string, number, ( expr )
+ * Plus function calls.
+ *
+ * Always return v_type == V_ERR when flag == 0.
+ */
+p_expr12(v, flag)
+register struct value *v;
+char flag;
+{
+ v->v_type = V_ERR;
+ switch (token) {
+ case T_NUM:
+ if (flag) {
+ v->v_type = V_NUM;
+ v->v_num = token_num;
+ }
+ (void) s_gettok();
+ break;
+ case T_STR:
+ if (flag) {
+ v->v_type = V_STR;
+ v->v_str = token_str;
+ } else
+ str_free(token_str);
+ (void) s_gettok();
+ break;
+ case T_LP:
+ (void) s_gettok();
+ if (p_expr(v, flag) < 0) {
+ p_synerror();
+ return -1;
+ }
+ if (token != T_RP) {
+ p_synerror();
+ val_free(*v);
+ return -1;
+ }
+ (void) s_gettok();
+ break;
+ default:
+ return -1;
+ }
+ while (token == T_LP) {
+ char *cmd;
+
+ if (p_convstr(v) < 0)
+ return -1;
+ cmd = v->v_type == V_STR ? v->v_str : 0;
+ if (p_function(cmd, v, flag) < 0) {
+ if (cmd)
+ str_free(cmd);
+ return -1;
+ }
+ if (cmd)
+ str_free(cmd);
+ }
+ return 0;
+}
diff --git a/usr.bin/window/scanner.c b/usr.bin/window/scanner.c
new file mode 100644
index 0000000..9bf691b
--- /dev/null
+++ b/usr.bin/window/scanner.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)scanner.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <ctype.h>
+#include "value.h"
+#include "token.h"
+#include "context.h"
+#include "string.h"
+
+s_getc()
+{
+ register c;
+
+ switch (cx.x_type) {
+ case X_FILE:
+ c = getc(cx.x_fp);
+ if (cx.x_bol && c != EOF) {
+ cx.x_bol = 0;
+ cx.x_lineno++;
+ }
+ if (c == '\n')
+ cx.x_bol = 1;
+ return c;
+ case X_BUF:
+ if (*cx.x_bufp != 0)
+ return *cx.x_bufp++ & 0xff;
+ else
+ return EOF;
+ }
+ /*NOTREACHED*/
+}
+
+s_ungetc(c)
+{
+ if (c == EOF)
+ return EOF;
+ switch (cx.x_type) {
+ case X_FILE:
+ cx.x_bol = 0;
+ return ungetc(c, cx.x_fp);
+ case X_BUF:
+ if (cx.x_bufp > cx.x_buf)
+ return *--cx.x_bufp = c;
+ else
+ return EOF;
+ }
+ /*NOTREACHED*/
+}
+
+s_gettok()
+{
+ char buf[100];
+ register char *p = buf;
+ register c;
+ register state = 0;
+
+loop:
+ c = s_getc();
+ switch (state) {
+ case 0:
+ switch (c) {
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ case ';':
+ cx.x_token = T_EOL;
+ state = -1;
+ break;
+ case '#':
+ state = 1;
+ break;
+ case EOF:
+ cx.x_token = T_EOF;
+ state = -1;
+ break;
+ case '"':
+ state = 3;
+ break;
+ case '\'':
+ state = 4;
+ break;
+ case '\\':
+ switch (c = s_gettok1()) {
+ case -1:
+ break;
+ case -2:
+ state = 0;
+ break;
+ default:
+ *p++ = c;
+ state = 2;
+ }
+ break;
+ case '0':
+ cx.x_val.v_num = 0;
+ state = 10;
+ break;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ cx.x_val.v_num = c - '0';
+ state = 11;
+ break;
+ case '>':
+ state = 20;
+ break;
+ case '<':
+ state = 21;
+ break;
+ case '=':
+ state = 22;
+ break;
+ case '!':
+ state = 23;
+ break;
+ case '&':
+ state = 24;
+ break;
+ case '|':
+ state = 25;
+ break;
+ case '$':
+ state = 26;
+ break;
+ case '~':
+ cx.x_token = T_COMP;
+ state = -1;
+ break;
+ case '+':
+ cx.x_token = T_PLUS;
+ state = -1;
+ break;
+ case '-':
+ cx.x_token = T_MINUS;
+ state = -1;
+ break;
+ case '*':
+ cx.x_token = T_MUL;
+ state = -1;
+ break;
+ case '/':
+ cx.x_token = T_DIV;
+ state = -1;
+ break;
+ case '%':
+ cx.x_token = T_MOD;
+ state = -1;
+ break;
+ case '^':
+ cx.x_token = T_XOR;
+ state = -1;
+ break;
+ case '(':
+ cx.x_token = T_LP;
+ state = -1;
+ break;
+ case ')':
+ cx.x_token = T_RP;
+ state = -1;
+ break;
+ case ',':
+ cx.x_token = T_COMMA;
+ state = -1;
+ break;
+ case '?':
+ cx.x_token = T_QUEST;
+ state = -1;
+ break;
+ case ':':
+ cx.x_token = T_COLON;
+ state = -1;
+ break;
+ case '[':
+ cx.x_token = T_LB;
+ state = -1;
+ break;
+ case ']':
+ cx.x_token = T_RB;
+ state = -1;
+ break;
+ default:
+ if (isalpha(c) || c == '_' || c == '.') {
+ *p++ = c;
+ state = 2;
+ break;
+ }
+ cx.x_val.v_num = c;
+ cx.x_token = T_CHAR;
+ state = -1;
+ break;
+ }
+ break;
+ case 1: /* got # */
+ if (c == '\n' || c == EOF) {
+ (void) s_ungetc(c);
+ state = 0;
+ }
+ break;
+ case 2: /* unquoted string */
+ switch (c) {
+ case '"':
+ state = 3;
+ break;
+ case '\'':
+ state = 4;
+ break;
+ case '\\':
+ switch (c = s_gettok1()) {
+ case -2:
+ (void) s_ungetc(' ');
+ case -1:
+ break;
+ default:
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ }
+ break;
+ default:
+ if (isalnum(c) || c == '_' || c == '.') {
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ break;
+ }
+ (void) s_ungetc(c);
+ case EOF:
+ *p = 0;
+ cx.x_token = T_STR;
+ switch (*buf) {
+ case 'i':
+ if (buf[1] == 'f' && buf[2] == 0)
+ cx.x_token = T_IF;
+ break;
+ case 't':
+ if (buf[1] == 'h' && buf[2] == 'e'
+ && buf[3] == 'n' && buf[4] == 0)
+ cx.x_token = T_THEN;
+ break;
+ case 'e':
+ if (buf[1] == 'n' && buf[2] == 'd'
+ && buf[3] == 'i' && buf[4] == 'f'
+ && buf[5] == 0)
+ cx.x_token = T_ENDIF;
+ else if (buf[1] == 'l' && buf[2] == 's')
+ if (buf[3] == 'i' && buf[4] == 'f'
+ && buf[5] == 0)
+ cx.x_token = T_ELSIF;
+ else if (buf[3] == 'e' && buf[4] == 0)
+ cx.x_token = T_ELSE;
+ break;
+ }
+ if (cx.x_token == T_STR
+ && (cx.x_val.v_str = str_cpy(buf)) == 0) {
+ p_memerror();
+ cx.x_token = T_EOF;
+ }
+ state = -1;
+ break;
+ }
+ break;
+ case 3: /* " quoted string */
+ switch (c) {
+ case '\n':
+ (void) s_ungetc(c);
+ case EOF:
+ case '"':
+ state = 2;
+ break;
+ case '\\':
+ switch (c = s_gettok1()) {
+ case -1:
+ case -2: /* newlines are invisible */
+ break;
+ default:
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ }
+ break;
+ default:
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ break;
+ }
+ break;
+ case 4: /* ' quoted string */
+ switch (c) {
+ case '\n':
+ (void) s_ungetc(c);
+ case EOF:
+ case '\'':
+ state = 2;
+ break;
+ case '\\':
+ switch (c = s_gettok1()) {
+ case -1:
+ case -2: /* newlines are invisible */
+ break;
+ default:
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ }
+ break;
+ default:
+ if (p < buf + sizeof buf - 1)
+ *p++ = c;
+ break;
+ }
+ break;
+ case 10: /* got 0 */
+ switch (c) {
+ case 'x':
+ case 'X':
+ cx.x_val.v_num = 0;
+ state = 12;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ cx.x_val.v_num = c - '0';
+ state = 13;
+ break;
+ case '8': case '9':
+ cx.x_val.v_num = c - '0';
+ state = 11;
+ break;
+ default:
+ (void) s_ungetc(c);
+ state = -1;
+ cx.x_token = T_NUM;
+ }
+ break;
+ case 11: /* decimal number */
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ cx.x_val.v_num = cx.x_val.v_num * 10 + c - '0';
+ break;
+ default:
+ (void) s_ungetc(c);
+ state = -1;
+ cx.x_token = T_NUM;
+ }
+ break;
+ case 12: /* hex number */
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ cx.x_val.v_num = cx.x_val.v_num * 16 + c - '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ cx.x_val.v_num = cx.x_val.v_num * 16 + c - 'a' + 10;
+ break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ cx.x_val.v_num = cx.x_val.v_num * 16 + c - 'A' + 10;
+ break;
+ default:
+ (void) s_ungetc(c);
+ state = -1;
+ cx.x_token = T_NUM;
+ }
+ break;
+ case 13: /* octal number */
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ cx.x_val.v_num = cx.x_val.v_num * 8 + c - '0';
+ break;
+ default:
+ (void) s_ungetc(c);
+ state = -1;
+ cx.x_token = T_NUM;
+ }
+ break;
+ case 20: /* got > */
+ switch (c) {
+ case '=':
+ cx.x_token = T_GE;
+ state = -1;
+ break;
+ case '>':
+ cx.x_token = T_RS;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_GT;
+ state = -1;
+ }
+ break;
+ case 21: /* got < */
+ switch (c) {
+ case '=':
+ cx.x_token = T_LE;
+ state = -1;
+ break;
+ case '<':
+ cx.x_token = T_LS;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_LT;
+ state = -1;
+ }
+ break;
+ case 22: /* got = */
+ switch (c) {
+ case '=':
+ cx.x_token = T_EQ;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_ASSIGN;
+ state = -1;
+ }
+ break;
+ case 23: /* got ! */
+ switch (c) {
+ case '=':
+ cx.x_token = T_NE;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_NOT;
+ state = -1;
+ }
+ break;
+ case 24: /* got & */
+ switch (c) {
+ case '&':
+ cx.x_token = T_ANDAND;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_AND;
+ state = -1;
+ }
+ break;
+ case 25: /* got | */
+ switch (c) {
+ case '|':
+ cx.x_token = T_OROR;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_OR;
+ state = -1;
+ }
+ break;
+ case 26: /* got $ */
+ switch (c) {
+ case '?':
+ cx.x_token = T_DQ;
+ state = -1;
+ break;
+ default:
+ (void) s_ungetc(c);
+ cx.x_token = T_DOLLAR;
+ state = -1;
+ }
+ break;
+ default:
+ abort();
+ }
+ if (state >= 0)
+ goto loop;
+ return cx.x_token;
+}
+
+s_gettok1()
+{
+ register c;
+ register n;
+
+ c = s_getc(); /* got \ */
+ switch (c) {
+ case EOF:
+ return -1;
+ case '\n':
+ return -2;
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ default:
+ return c;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7':
+ break;
+ }
+ n = c - '0';
+ c = s_getc(); /* got \[0-7] */
+ if (c < '0' || c > '7') {
+ (void) s_ungetc(c);
+ return n;
+ }
+ n = n * 8 + c - '0';
+ c = s_getc(); /* got \[0-7][0-7] */
+ if (c < '0' || c > '7') {
+ (void) s_ungetc(c);
+ return n;
+ }
+ return n * 8 + c - '0';
+}
diff --git a/usr.bin/window/startup.c b/usr.bin/window/startup.c
new file mode 100644
index 0000000..d0926c0
--- /dev/null
+++ b/usr.bin/window/startup.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)startup.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "value.h"
+#include "var.h"
+#include "char.h"
+#include "local.h"
+
+doconfig()
+{
+ char buf[100];
+ char *home;
+ static char runcom[] = RUNCOM;
+
+ if ((home = getenv("HOME")) == 0)
+ home = ".";
+ (void) sprintf(buf, "%.*s/%s",
+ (sizeof buf - sizeof runcom) / sizeof (char) - 1,
+ home, runcom);
+ return dosource(buf);
+}
+
+/*
+ * The default is two windows of equal size.
+ */
+dodefault()
+{
+ struct ww *w;
+ register r = wwnrow / 2 - 1;
+
+ if (openwin(1, r + 2, 0, wwnrow - r - 2, wwncol, default_nline,
+ (char *) 0, 1, 1, default_shellfile, default_shell) == 0)
+ return;
+ if ((w = openwin(0, 1, 0, r, wwncol, default_nline,
+ (char *) 0, 1, 1, default_shellfile, default_shell)) == 0)
+ return;
+ wwprintf(w, "Escape character is %s.\r\n", unctrl(escapec));
+}
+
+setvars()
+{
+ /* try to use a random ordering to balance the tree */
+ (void) var_setnum("nrow", wwnrow);
+ (void) var_setnum("ncol", wwncol);
+ (void) var_setnum("baud", wwbaud);
+ (void) var_setnum("m_rev", WWM_REV);
+ (void) var_setnum("m_blk", WWM_BLK);
+ (void) var_setnum("m_ul", WWM_UL);
+ (void) var_setnum("m_grp", WWM_GRP);
+ (void) var_setnum("m_dim", WWM_DIM);
+ (void) var_setnum("m_usr", WWM_USR);
+ (void) var_setstr("term", wwterm);
+ (void) var_setnum("modes", wwavailmodes);
+}
diff --git a/usr.bin/window/string.c b/usr.bin/window/string.c
new file mode 100644
index 0000000..a01f04f
--- /dev/null
+++ b/usr.bin/window/string.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)string.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "string.h"
+
+char *malloc();
+
+char *
+str_cpy(s)
+register char *s;
+{
+ char *str;
+ register char *p;
+
+ str = p = str_alloc(strlen(s) + 1);
+ if (p == 0)
+ return 0;
+ while (*p++ = *s++)
+ ;
+ return str;
+}
+
+char *
+str_ncpy(s, n)
+register char *s;
+register n;
+{
+ int l = strlen(s);
+ char *str;
+ register char *p;
+
+ if (n > l)
+ n = l;
+ str = p = str_alloc(n + 1);
+ if (p == 0)
+ return 0;
+ while (--n >= 0)
+ *p++ = *s++;
+ *p = 0;
+ return str;
+}
+
+char *
+str_itoa(i)
+int i;
+{
+ char buf[30];
+
+ (void) sprintf(buf, "%d", i);
+ return str_cpy(buf);
+}
+
+char *
+str_cat(s1, s2)
+char *s1, *s2;
+{
+ char *str;
+ register char *p, *q;
+
+ str = p = str_alloc(strlen(s1) + strlen(s2) + 1);
+ if (p == 0)
+ return 0;
+ for (q = s1; *p++ = *q++;)
+ ;
+ for (q = s2, p--; *p++ = *q++;)
+ ;
+ return str;
+}
+
+/*
+ * match s against p.
+ * s can be a prefix of p with at least min characters.
+ */
+str_match(s, p, min)
+register char *s, *p;
+register min;
+{
+ for (; *s && *p && *s == *p; s++, p++, min--)
+ ;
+ return *s == *p || *s == 0 && min <= 0;
+}
+
+#ifdef STR_DEBUG
+char *
+str_alloc(l)
+int l;
+{
+ register struct string *s;
+
+ s = (struct string *) malloc((unsigned)l + str_offset);
+ if (s == 0)
+ return 0;
+ if (str_head.s_forw == 0)
+ str_head.s_forw = str_head.s_back = &str_head;
+ s->s_forw = str_head.s_forw;
+ s->s_back = &str_head;
+ str_head.s_forw = s;
+ s->s_forw->s_back = s;
+ return s->s_data;
+}
+
+str_free(str)
+char *str;
+{
+ register struct string *s;
+
+ for (s = str_head.s_forw; s != &str_head && s->s_data != str;
+ s = s->s_forw)
+ ;
+ if (s == &str_head)
+ abort();
+ s->s_back->s_forw = s->s_forw;
+ s->s_forw->s_back = s->s_back;
+ free((char *)s);
+}
+#endif
diff --git a/usr.bin/window/string.h b/usr.bin/window/string.h
new file mode 100644
index 0000000..08cae56
--- /dev/null
+++ b/usr.bin/window/string.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)string.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define STR_DEBUG
+
+char *str_cpy();
+char *str_ncpy();
+char *str_cat();
+char *str_itoa();
+
+#define str_cmp(a, b) strcmp(a, b)
+
+#ifdef STR_DEBUG
+struct string {
+ struct string *s_forw;
+ struct string *s_back;
+ char s_data[1];
+};
+
+struct string str_head;
+
+#define str_offset ((unsigned)str_head.s_data - (unsigned)&str_head)
+#define str_stos(s) ((struct string *)((unsigned)(s) - str_offset))
+
+char *str_alloc();
+int str_free();
+#else
+#define str_free(s) free(s)
+#define str_alloc(s) malloc(s)
+#endif
diff --git a/usr.bin/window/token.h b/usr.bin/window/token.h
new file mode 100644
index 0000000..a37b23b
--- /dev/null
+++ b/usr.bin/window/token.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)token.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define token (cx.x_token)
+#define token_num (cx.x_val.v_num)
+#define token_str (cx.x_val.v_str)
+
+#define T_EOL 1
+#define T_EOF 2
+#define T_COMP 3
+#define T_PLUS 4
+#define T_MINUS 5
+#define T_MUL 6
+#define T_DIV 7
+#define T_LP 8
+#define T_RP 9
+#define T_LB 10
+#define T_RB 11
+#define T_DOLLAR 12
+#define T_COMMA 13
+#define T_QUEST 14
+#define T_COLON 15
+#define T_CHAR 16
+#define T_STR 17
+#define T_NUM 18
+#define T_MOD 19
+#define T_XOR 20
+#define T_DQ 21 /* $? */
+#define T_GE 22
+#define T_RS 23
+#define T_GT 24
+#define T_LE 25
+#define T_LS 26
+#define T_LT 27
+#define T_EQ 28
+#define T_ASSIGN 29
+#define T_NE 30
+#define T_NOT 31
+#define T_ANDAND 32
+#define T_AND 33
+#define T_OROR 34
+#define T_OR 35
+
+#define T_IF 40
+#define T_THEN 41
+#define T_ELSIF 42
+#define T_ELSE 43
+#define T_ENDIF 44
diff --git a/usr.bin/window/tt.h b/usr.bin/window/tt.h
new file mode 100644
index 0000000..bd19bb3
--- /dev/null
+++ b/usr.bin/window/tt.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tt.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Interface structure for the terminal drivers.
+ */
+struct tt {
+ /* startup and cleanup */
+ int (*tt_start)();
+ int (*tt_reset)();
+ int (*tt_end)();
+
+ /* terminal functions */
+ int (*tt_move)();
+ int (*tt_insline)();
+ int (*tt_delline)();
+ int (*tt_inschar)();
+ int (*tt_insspace)();
+ int (*tt_delchar)();
+ int (*tt_write)(); /* write a whole block */
+ int (*tt_putc)(); /* write one character */
+ int (*tt_clreol)();
+ int (*tt_clreos)();
+ int (*tt_clear)();
+ int (*tt_scroll_down)();
+ int (*tt_scroll_up)();
+ int (*tt_setscroll)(); /* set scrolling region */
+ int (*tt_setmodes)(); /* set display modes */
+ int (*tt_set_token)(); /* define a token */
+ int (*tt_put_token)(); /* refer to a defined token */
+ int (*tt_compress)(); /* begin, end compression */
+ int (*tt_checksum)(); /* compute checksum */
+ int (*tt_checkpoint)(); /* checkpoint protocol */
+ int (*tt_rint)(); /* input processing */
+
+ /* internal variables */
+ char tt_modes; /* the current display modes */
+ char tt_nmodes; /* the new modes for next write */
+ char tt_insert; /* currently in insert mode */
+ int tt_row; /* cursor row */
+ int tt_col; /* cursor column */
+ int tt_scroll_top; /* top of scrolling region */
+ int tt_scroll_bot; /* bottom of scrolling region */
+
+ /* terminal info */
+ int tt_nrow; /* number of display rows */
+ int tt_ncol; /* number of display columns */
+ char tt_availmodes; /* the display modes supported */
+ char tt_wrap; /* has auto wrap around */
+ char tt_retain; /* can retain below (db flag) */
+ short tt_padc; /* the pad character */
+ int tt_ntoken; /* number of compression tokens */
+ int tt_token_min; /* minimun token size */
+ int tt_token_max; /* maximum token size */
+ int tt_set_token_cost; /* cost in addition to string */
+ int tt_put_token_cost; /* constant cost */
+ int tt_ack; /* checkpoint ack-nack flag */
+
+ /* the frame characters */
+ short *tt_frame;
+
+ /* ttflush() hook */
+ int (*tt_flush)();
+};
+struct tt tt;
+
+/*
+ * tt_padc is used by the compression routine.
+ * It is a short to allow the driver to indicate that there is no padding.
+ */
+#define TT_PADC_NONE 0x100
+
+/*
+ * List of terminal drivers.
+ */
+struct tt_tab {
+ char *tt_name;
+ int tt_len;
+ int (*tt_func)();
+};
+extern struct tt_tab tt_tab[];
+
+/*
+ * Clean interface to termcap routines.
+ * Too may t's.
+ */
+char tt_strings[1024]; /* string buffer */
+char *tt_strp; /* pointer for it */
+
+struct tt_str {
+ char *ts_str;
+ int ts_n;
+};
+
+struct tt_str *tttgetstr();
+struct tt_str *ttxgetstr(); /* tgetstr() and expand delays */
+
+int tttputc();
+#define tttputs(s, n) tputs((s)->ts_str, (n), tttputc)
+#define ttxputs(s) ttwrite((s)->ts_str, (s)->ts_n)
+
+/*
+ * Buffered output without stdio.
+ * These variables have different meanings from the ww_ob* variables.
+ * But I'm too lazy to think up different names.
+ */
+char *tt_ob;
+char *tt_obp;
+char *tt_obe;
+#define ttputc(c) (tt_obp < tt_obe ? (*tt_obp++ = (c)) \
+ : (ttflush(), *tt_obp++ = (c)))
+
+/*
+ * Convenience macros for the drivers
+ * They require char.h
+ */
+#define ttctrl(c) ttputc(ctrl(c))
+#define ttesc(c) (ttctrl('['), ttputc(c))
diff --git a/usr.bin/window/ttf100.c b/usr.bin/window/ttf100.c
new file mode 100644
index 0000000..2b152f2
--- /dev/null
+++ b/usr.bin/window/ttf100.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttf100.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+/*
+ * Freedom 100
+ */
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short f100_frame[16] = {
+ ' ', 'J'|G, 'K'|G, 'A'|G,
+ 'J'|G, 'J'|G, 'B'|G, 'M'|G,
+ 'K'|G, 'D'|G, 'K'|G, 'O'|G,
+ 'C'|G, 'L'|G, 'N'|G, 'I'|G
+};
+extern struct tt_str *gen_AE, *gen_AS;
+
+tt_f100()
+{
+ static struct tt_str ae = { "\033%", 2 };
+ static struct tt_str as = { "\033$", 2 };
+
+ if (tt_generic() < 0)
+ return -1;
+ tt.tt_frame = f100_frame;
+ tt.tt_availmodes |= WWM_GRP;
+ gen_AS = &as;
+ gen_AE = &ae;
+ return 0;
+}
diff --git a/usr.bin/window/ttgeneric.c b/usr.bin/window/ttgeneric.c
new file mode 100644
index 0000000..704cb30
--- /dev/null
+++ b/usr.bin/window/ttgeneric.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttgeneric.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+char PC, *BC, *UP;
+
+ /* normal frame */
+short gen_frame[16] = {
+ ' ', '|', '-', '+',
+ '|', '|', '+', '+',
+ '-', '+', '-', '+',
+ '+', '+', '+', '+'
+};
+
+ /* ANSI graphics frame */
+#define G (WWM_GRP << WWC_MSHIFT)
+short ansi_frame[16] = {
+ ' ', 'x'|G, 'Q'|G, 'm'|G,
+ 'x'|G, 'x'|G, 'l'|G, 't'|G,
+ 'q'|G, 'j'|G, 'q'|G, 'v'|G,
+ 'k'|G, 'u'|G, 'w'|G, 'n'|G
+};
+struct tt_str ansi_AS = {
+ "\033(0", 3
+};
+
+struct tt_str *gen_PC;
+struct tt_str *gen_CM;
+struct tt_str *gen_IM;
+struct tt_str *gen_IC;
+struct tt_str *gen_ICn;
+struct tt_str *gen_IP;
+struct tt_str *gen_EI;
+struct tt_str *gen_DC;
+struct tt_str *gen_DCn;
+struct tt_str *gen_AL;
+struct tt_str *gen_ALn;
+struct tt_str *gen_DL;
+struct tt_str *gen_DLn;
+struct tt_str *gen_CE;
+struct tt_str *gen_CD;
+struct tt_str *gen_CL;
+struct tt_str *gen_VS;
+struct tt_str *gen_VE;
+struct tt_str *gen_TI;
+struct tt_str *gen_TE;
+struct tt_str *gen_SO;
+struct tt_str *gen_SE;
+struct tt_str *gen_US;
+struct tt_str *gen_UE;
+struct tt_str *gen_LE;
+struct tt_str *gen_ND;
+struct tt_str *gen_UP;
+struct tt_str *gen_DO;
+struct tt_str *gen_BC;
+struct tt_str *gen_NL;
+struct tt_str *gen_CR;
+struct tt_str *gen_HO;
+struct tt_str *gen_AS;
+struct tt_str *gen_AE;
+struct tt_str *gen_XS;
+struct tt_str *gen_XE;
+struct tt_str *gen_SF;
+struct tt_str *gen_SFn;
+struct tt_str *gen_SR;
+struct tt_str *gen_SRn;
+struct tt_str *gen_CS;
+char gen_MI;
+char gen_MS;
+char gen_AM;
+char gen_OS;
+char gen_BS;
+char gen_DA;
+char gen_DB;
+char gen_NS;
+char gen_XN;
+int gen_CO;
+int gen_LI;
+int gen_UG;
+int gen_SG;
+
+gen_setinsert(new)
+char new;
+{
+ if (new) {
+ if (gen_IM)
+ ttxputs(gen_IM);
+ } else
+ if (gen_EI)
+ ttxputs(gen_EI);
+ tt.tt_insert = new;
+}
+
+gen_setmodes(new)
+register new;
+{
+ register diff;
+
+ diff = new ^ tt.tt_modes;
+ if (diff & WWM_REV) {
+ if (new & WWM_REV) {
+ if (gen_SO)
+ ttxputs(gen_SO);
+ } else
+ if (gen_SE)
+ ttxputs(gen_SE);
+ }
+ if (diff & WWM_UL) {
+ if (new & WWM_UL) {
+ if (gen_US)
+ ttxputs(gen_US);
+ } else
+ if (gen_UE)
+ ttxputs(gen_UE);
+ }
+ if (diff & WWM_GRP) {
+ if (new & WWM_GRP) {
+ if (gen_AS)
+ ttxputs(gen_AS);
+ } else
+ if (gen_AE)
+ ttxputs(gen_AE);
+ }
+ if (diff & WWM_USR) {
+ if (new & WWM_USR) {
+ if (gen_XS)
+ ttxputs(gen_XS);
+ } else
+ if (gen_XE)
+ ttxputs(gen_XE);
+ }
+ tt.tt_modes = new;
+}
+
+gen_insline(n)
+{
+ if (tt.tt_modes) /* for concept 100 */
+ gen_setmodes(0);
+ if (gen_ALn)
+ ttpgoto(gen_ALn, 0, n, gen_LI - tt.tt_row);
+ else
+ while (--n >= 0)
+ tttputs(gen_AL, gen_LI - tt.tt_row);
+}
+
+gen_delline(n)
+{
+ if (tt.tt_modes) /* for concept 100 */
+ gen_setmodes(0);
+ if (gen_DLn)
+ ttpgoto(gen_DLn, 0, n, gen_LI - tt.tt_row);
+ else
+ while (--n >= 0)
+ tttputs(gen_DL, gen_LI - tt.tt_row);
+}
+
+gen_putc(c)
+register char c;
+{
+ if (tt.tt_insert)
+ gen_setinsert(0);
+ if (tt.tt_nmodes != tt.tt_modes)
+ gen_setmodes(tt.tt_nmodes);
+ ttputc(c);
+ if (++tt.tt_col == gen_CO)
+ if (gen_XN)
+ tt.tt_col = tt.tt_row = -10;
+ else if (gen_AM)
+ tt.tt_col = 0, tt.tt_row++;
+ else
+ tt.tt_col--;
+}
+
+gen_write(p, n)
+ register char *p;
+ register n;
+{
+ if (tt.tt_insert)
+ gen_setinsert(0);
+ if (tt.tt_nmodes != tt.tt_modes)
+ gen_setmodes(tt.tt_nmodes);
+ ttwrite(p, n);
+ tt.tt_col += n;
+ if (tt.tt_col == gen_CO)
+ if (gen_XN)
+ tt.tt_col = tt.tt_row = -10;
+ else if (gen_AM)
+ tt.tt_col = 0, tt.tt_row++;
+ else
+ tt.tt_col--;
+}
+
+gen_move(row, col)
+register int row, col;
+{
+ if (tt.tt_row == row && tt.tt_col == col)
+ return;
+ if (!gen_MI && tt.tt_insert)
+ gen_setinsert(0);
+ if (!gen_MS && tt.tt_modes)
+ gen_setmodes(0);
+ if (row < tt.tt_scroll_top || row > tt.tt_scroll_bot)
+ gen_setscroll(0, tt.tt_nrow - 1);
+ if (tt.tt_row == row) {
+ if (col == 0) {
+ ttxputs(gen_CR);
+ goto out;
+ }
+ if (tt.tt_col == col - 1) {
+ if (gen_ND) {
+ ttxputs(gen_ND);
+ goto out;
+ }
+ } else if (tt.tt_col == col + 1) {
+ if (gen_LE) {
+ ttxputs(gen_LE);
+ goto out;
+ }
+ }
+ }
+ if (tt.tt_col == col) {
+ if (tt.tt_row == row + 1) {
+ if (gen_UP) {
+ ttxputs(gen_UP);
+ goto out;
+ }
+ } else if (tt.tt_row == row - 1) {
+ ttxputs(gen_DO);
+ goto out;
+ }
+ }
+ if (gen_HO && col == 0 && row == 0) {
+ ttxputs(gen_HO);
+ goto out;
+ }
+ tttgoto(gen_CM, col, row);
+out:
+ tt.tt_col = col;
+ tt.tt_row = row;
+}
+
+gen_start()
+{
+ if (gen_VS)
+ ttxputs(gen_VS);
+ if (gen_TI)
+ ttxputs(gen_TI);
+ ttxputs(gen_CL);
+ tt.tt_col = tt.tt_row = 0;
+ tt.tt_insert = 0;
+ tt.tt_nmodes = tt.tt_modes = 0;
+}
+
+gen_end()
+{
+ if (tt.tt_insert)
+ gen_setinsert(0);
+ if (gen_TE)
+ ttxputs(gen_TE);
+ if (gen_VE)
+ ttxputs(gen_VE);
+}
+
+gen_clreol()
+{
+ if (tt.tt_modes) /* for concept 100 */
+ gen_setmodes(0);
+ tttputs(gen_CE, gen_CO - tt.tt_col);
+}
+
+gen_clreos()
+{
+ if (tt.tt_modes) /* for concept 100 */
+ gen_setmodes(0);
+ tttputs(gen_CD, gen_LI - tt.tt_row);
+}
+
+gen_clear()
+{
+ if (tt.tt_modes) /* for concept 100 */
+ gen_setmodes(0);
+ ttxputs(gen_CL);
+}
+
+gen_inschar(c)
+register char c;
+{
+ if (!tt.tt_insert)
+ gen_setinsert(1);
+ if (tt.tt_nmodes != tt.tt_modes)
+ gen_setmodes(tt.tt_nmodes);
+ if (gen_IC)
+ tttputs(gen_IC, gen_CO - tt.tt_col);
+ ttputc(c);
+ if (gen_IP)
+ tttputs(gen_IP, gen_CO - tt.tt_col);
+ if (++tt.tt_col == gen_CO)
+ if (gen_XN)
+ tt.tt_col = tt.tt_row = -10;
+ else if (gen_AM)
+ tt.tt_col = 0, tt.tt_row++;
+ else
+ tt.tt_col--;
+}
+
+gen_insspace(n)
+{
+ if (gen_ICn)
+ ttpgoto(gen_ICn, 0, n, gen_CO - tt.tt_col);
+ else
+ while (--n >= 0)
+ tttputs(gen_IC, gen_CO - tt.tt_col);
+}
+
+gen_delchar(n)
+{
+ if (gen_DCn)
+ ttpgoto(gen_DCn, 0, n, gen_CO - tt.tt_col);
+ else
+ while (--n >= 0)
+ tttputs(gen_DC, gen_CO - tt.tt_col);
+}
+
+gen_scroll_down(n)
+{
+ gen_move(tt.tt_scroll_bot, 0);
+ if (gen_SFn)
+ ttpgoto(gen_SFn, 0, n, n);
+ else
+ while (--n >= 0)
+ ttxputs(gen_SF);
+}
+
+gen_scroll_up(n)
+{
+ gen_move(tt.tt_scroll_top, 0);
+ if (gen_SRn)
+ ttpgoto(gen_SRn, 0, n, n);
+ else
+ while (--n >= 0)
+ ttxputs(gen_SR);
+}
+
+gen_setscroll(top, bot)
+{
+ tttgoto(gen_CS, bot, top);
+ tt.tt_scroll_top = top;
+ tt.tt_scroll_bot = bot;
+ tt.tt_row = tt.tt_col = -10;
+}
+
+tt_generic()
+{
+ gen_PC = tttgetstr("pc");
+ PC = gen_PC ? *gen_PC->ts_str : 0;
+
+ gen_CM = ttxgetstr("cm"); /* may not work */
+ gen_IM = ttxgetstr("im");
+ gen_IC = tttgetstr("ic");
+ gen_ICn = tttgetstr("IC");
+ gen_IP = tttgetstr("ip");
+ gen_EI = ttxgetstr("ei");
+ gen_DC = tttgetstr("dc");
+ gen_DCn = tttgetstr("DC");
+ gen_AL = tttgetstr("al");
+ gen_ALn = tttgetstr("AL");
+ gen_DL = tttgetstr("dl");
+ gen_DLn = tttgetstr("DL");
+ gen_CE = tttgetstr("ce");
+ gen_CD = tttgetstr("cd");
+ gen_CL = ttxgetstr("cl");
+ gen_VS = ttxgetstr("vs");
+ gen_VE = ttxgetstr("ve");
+ gen_TI = ttxgetstr("ti");
+ gen_TE = ttxgetstr("te");
+ gen_SO = ttxgetstr("so");
+ gen_SE = ttxgetstr("se");
+ gen_US = ttxgetstr("us");
+ gen_UE = ttxgetstr("ue");
+ gen_LE = ttxgetstr("le");
+ gen_ND = ttxgetstr("nd");
+ gen_UP = ttxgetstr("up");
+ gen_DO = ttxgetstr("do");
+ gen_BC = ttxgetstr("bc");
+ gen_NL = ttxgetstr("nl");
+ gen_CR = ttxgetstr("cr");
+ gen_HO = ttxgetstr("ho");
+ gen_AS = ttxgetstr("as");
+ gen_AE = ttxgetstr("ae");
+ gen_XS = ttxgetstr("XS");
+ gen_XE = ttxgetstr("XE");
+ gen_SF = ttxgetstr("sf");
+ gen_SFn = ttxgetstr("SF");
+ gen_SR = ttxgetstr("sr");
+ gen_SRn = ttxgetstr("SR");
+ gen_CS = ttxgetstr("cs");
+ gen_MI = tgetflag("mi");
+ gen_MS = tgetflag("ms");
+ gen_AM = tgetflag("am");
+ gen_OS = tgetflag("os");
+ gen_BS = tgetflag("bs");
+ gen_DA = tgetflag("da");
+ gen_DB = tgetflag("db");
+ gen_NS = tgetflag("ns");
+ gen_XN = tgetflag("xn");
+ gen_CO = tgetnum("co");
+ gen_LI = tgetnum("li");
+ gen_UG = tgetnum("ug");
+ gen_SG = tgetnum("sg");
+ if (gen_CL == 0 || gen_OS || gen_CM == 0)
+ return -1;
+
+ /*
+ * Deal with obsolete termcap fields.
+ */
+ if (gen_LE == 0)
+ if (gen_BC)
+ gen_LE = gen_BC;
+ else if (gen_BS) {
+ static struct tt_str bc = { "\b", 1 };
+ gen_BC = &bc;
+ }
+ if (gen_NL == 0) {
+ static struct tt_str nl = { "\n", 1 };
+ gen_NL = &nl;
+ }
+ if (gen_DO == 0)
+ gen_DO = gen_NL;
+ if (gen_CR == 0) {
+ static struct tt_str cr = { "\r", 1 };
+ gen_CR = &cr;
+ }
+ /*
+ * Most terminal will scroll with "nl", but very few specify "sf".
+ * We shouldn't use "do" here.
+ */
+ if (gen_SF == 0 && !gen_NS)
+ gen_SF = gen_NL;
+ BC = gen_LE ? gen_LE->ts_str : 0;
+ UP = gen_UP ? gen_UP->ts_str : 0;
+ /*
+ * Fix up display attributes that we can't handle, or don't
+ * really exist.
+ */
+ if (gen_SG > 0)
+ gen_SO = 0;
+ if (gen_UG > 0 || gen_US && gen_SO && ttstrcmp(gen_US, gen_SO) == 0)
+ gen_US = 0;
+
+ if (gen_IM && gen_IM->ts_n == 0) {
+ free((char *) gen_IM);
+ gen_IM = 0;
+ }
+ if (gen_EI && gen_EI->ts_n == 0) {
+ free((char *) gen_EI);
+ gen_EI = 0;
+ }
+ if (gen_IC && gen_IC->ts_n == 0) {
+ free((char *) gen_IC);
+ gen_IC = 0;
+ }
+ if (gen_IM)
+ tt.tt_inschar = gen_inschar;
+ else if (gen_IC)
+ tt.tt_insspace = gen_insspace;
+ if (gen_DC)
+ tt.tt_delchar = gen_delchar;
+ if (gen_AL)
+ tt.tt_insline = gen_insline;
+ if (gen_DL)
+ tt.tt_delline = gen_delline;
+ if (gen_CE)
+ tt.tt_clreol = gen_clreol;
+ if (gen_CD)
+ tt.tt_clreos = gen_clreos;
+ if (gen_SF)
+ tt.tt_scroll_down = gen_scroll_down;
+ /*
+ * Don't allow scroll_up if da or db but not cs.
+ * See comment in wwscroll.c.
+ */
+ if (gen_SR && (gen_CS || !gen_DA && !gen_DB))
+ tt.tt_scroll_up = gen_scroll_up;
+ if (gen_CS)
+ tt.tt_setscroll = gen_setscroll;
+ if (gen_SO)
+ tt.tt_availmodes |= WWM_REV;
+ if (gen_US)
+ tt.tt_availmodes |= WWM_UL;
+ if (gen_AS)
+ tt.tt_availmodes |= WWM_GRP;
+ if (gen_XS)
+ tt.tt_availmodes |= WWM_USR;
+ tt.tt_wrap = gen_AM;
+ tt.tt_retain = gen_DB;
+ tt.tt_ncol = gen_CO;
+ tt.tt_nrow = gen_LI;
+ tt.tt_start = gen_start;
+ tt.tt_end = gen_end;
+ tt.tt_write = gen_write;
+ tt.tt_putc = gen_putc;
+ tt.tt_move = gen_move;
+ tt.tt_clear = gen_clear;
+ tt.tt_setmodes = gen_setmodes;
+ tt.tt_frame = gen_AS && ttstrcmp(gen_AS, &ansi_AS) == 0 ?
+ ansi_frame : gen_frame;
+ return 0;
+}
diff --git a/usr.bin/window/tth19.c b/usr.bin/window/tth19.c
new file mode 100644
index 0000000..b6dbcfe
--- /dev/null
+++ b/usr.bin/window/tth19.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tth19.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include "char.h"
+
+/*
+kb|h19|heath|h19-b|h19b|heathkit|heath-19|z19|zenith:
+ cr=^M:nl=^J:bl=^G:al=1*\EL:am:le=^H:bs:cd=\EJ:ce=\EK:
+ cl=\EE:cm=\EY%+ %+ :co#80:dc=\EN:dl=1*\EM:do=\EB:
+ ei=\EO:ho=\EH:im=\E@:li#24:mi:nd=\EC:as=\EF:ae=\EG:ms:
+ ta=^I:pt:sr=\EI:se=\Eq:so=\Ep:up=\EA:vs=\Ex4:ve=\Ey4:
+ kb=^h:ku=\EA:kd=\EB:kl=\ED:kr=\EC:kh=\EH:
+ kn#8:k1=\ES:k2=\ET:k3=\EU:k4=\EV:k5=\EW:
+ l6=blue:l7=red:l8=white:k6=\EP:k7=\EQ:k8=\ER:
+ es:hs:ts=\Ej\Ex5\Ex1\EY8%+ \Eo:fs=\Ek\Ey5:ds=\Ey1:
+*/
+
+#define NCOL 80
+#define NROW 24
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short h19_frame[16] = {
+ ' ', '`'|G, 'a'|G, 'e'|G,
+ '`'|G, '`'|G, 'f'|G, 'v'|G,
+ 'a'|G, 'd'|G, 'a'|G, 'u'|G,
+ 'c'|G, 't'|G, 's'|G, 'b'|G
+};
+
+extern struct tt_str *gen_VS;
+extern struct tt_str *gen_VE;
+
+int h19_msp10c;
+
+#define PAD(ms10) { \
+ register i; \
+ for (i = ((ms10) + 5) / h19_msp10c; --i >= 0;) \
+ ttputc('\0'); \
+}
+#define ICPAD() PAD((NCOL - tt.tt_col) * 1) /* 0.1 ms per char */
+#define ILPAD() PAD((NROW - tt.tt_row) * 10) /* 1 ms per char */
+
+#define H19_SETINSERT(m) ttesc((tt.tt_insert = (m)) ? '@' : 'O')
+
+h19_setmodes(new)
+register new;
+{
+ register diff;
+
+ diff = new ^ tt.tt_modes;
+ if (diff & WWM_REV)
+ ttesc(new & WWM_REV ? 'p' : 'q');
+ if (diff & WWM_GRP)
+ ttesc(new & WWM_REV ? 'F' : 'G');
+ tt.tt_modes = new;
+}
+
+h19_insline(n)
+{
+ while (--n >= 0) {
+ ttesc('L');
+ ILPAD();
+ }
+}
+
+h19_delline(n)
+{
+ while (--n >= 0) {
+ ttesc('M');
+ ILPAD();
+ }
+}
+
+h19_putc(c)
+register char c;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ (*tt.tt_setmodes)(tt.tt_nmodes);
+ if (tt.tt_insert)
+ H19_SETINSERT(0);
+ ttputc(c);
+ if (++tt.tt_col == NCOL)
+ tt.tt_col = NCOL - 1;
+}
+
+h19_write(p, n)
+register char *p;
+register n;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ (*tt.tt_setmodes)(tt.tt_nmodes);
+ if (tt.tt_insert)
+ H19_SETINSERT(0);
+ ttwrite(p, n);
+ tt.tt_col += n;
+ if (tt.tt_col == NCOL)
+ tt.tt_col = NCOL - 1;
+}
+
+h19_move(row, col)
+register char row, col;
+{
+ if (tt.tt_row == row) {
+ if (tt.tt_col == col)
+ return;
+ if (col == 0) {
+ ttctrl('m');
+ goto out;
+ }
+ if (tt.tt_col == col - 1) {
+ ttesc('C');
+ goto out;
+ }
+ if (tt.tt_col == col + 1) {
+ ttctrl('h');
+ goto out;
+ }
+ }
+ if (tt.tt_col == col) {
+ if (tt.tt_row == row + 1) {
+ ttesc('A');
+ goto out;
+ }
+ if (tt.tt_row == row - 1) {
+ ttctrl('j');
+ goto out;
+ }
+ }
+ if (col == 0 && row == 0) {
+ ttesc('H');
+ goto out;
+ }
+ ttesc('Y');
+ ttputc(' ' + row);
+ ttputc(' ' + col);
+out:
+ tt.tt_col = col;
+ tt.tt_row = row;
+}
+
+h19_start()
+{
+ if (gen_VS)
+ ttxputs(gen_VS);
+ ttesc('w');
+ ttesc('E');
+ tt.tt_col = tt.tt_row = 0;
+ tt.tt_insert = 0;
+ tt.tt_nmodes = tt.tt_modes = 0;
+}
+
+h19_end()
+{
+ if (tt.tt_insert)
+ H19_SETINSERT(0);
+ if (gen_VE)
+ ttxputs(gen_VE);
+ ttesc('v');
+}
+
+h19_clreol()
+{
+ ttesc('K');
+}
+
+h19_clreos()
+{
+ ttesc('J');
+}
+
+h19_clear()
+{
+ ttesc('E');
+}
+
+h19_inschar(c)
+register char c;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ (*tt.tt_setmodes)(tt.tt_nmodes);
+ if (!tt.tt_insert)
+ H19_SETINSERT(1);
+ ttputc(c);
+ if (tt.tt_insert)
+ ICPAD();
+ if (++tt.tt_col == NCOL)
+ tt.tt_col = NCOL - 1;
+}
+
+h19_delchar(n)
+{
+ while (--n >= 0)
+ ttesc('N');
+}
+
+h19_scroll_down(n)
+{
+ h19_move(NROW - 1, 0);
+ while (--n >= 0)
+ ttctrl('j');
+}
+
+h19_scroll_up(n)
+{
+ h19_move(0, 0);
+ while (--n >= 0)
+ ttesc('I');
+}
+
+tt_h19()
+{
+ float cpms = (float) wwbaud / 10000; /* char per ms */
+
+ h19_msp10c = 10 / cpms; /* ms per 10 char */
+ gen_VS = ttxgetstr("vs");
+ gen_VE = ttxgetstr("ve");
+
+ tt.tt_start = h19_start;
+ tt.tt_end = h19_end;
+
+ tt.tt_insline = h19_insline;
+ tt.tt_delline = h19_delline;
+ tt.tt_inschar = h19_inschar;
+ tt.tt_delchar = h19_delchar;
+ tt.tt_clreol = h19_clreol;
+ tt.tt_clreos = h19_clreos;
+ tt.tt_clear = h19_clear;
+ tt.tt_move = h19_move;
+ tt.tt_write = h19_write;
+ tt.tt_putc = h19_putc;
+ tt.tt_scroll_down = h19_scroll_down;
+ tt.tt_scroll_up = h19_scroll_up;
+ tt.tt_setmodes = h19_setmodes;
+
+ tt.tt_ncol = NCOL;
+ tt.tt_nrow = NROW;
+ tt.tt_availmodes = WWM_REV|WWM_GRP;
+ tt.tt_frame = h19_frame;
+ return 0;
+}
diff --git a/usr.bin/window/tth29.c b/usr.bin/window/tth29.c
new file mode 100644
index 0000000..2482abf
--- /dev/null
+++ b/usr.bin/window/tth29.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tth29.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include "char.h"
+
+/*
+ * H29 Driver
+ *
+ * WWM_USR mode is alternate character set.
+ *
+kC|h29|heath-29|z29|zenith-29:\
+ :am:bc=\ED:bt=\E-:cr=^M:do=^J:nl=^J:bl=^G:\
+ :al=\EL:le=^H:bs:cd=\EJ:ce=\EK:cl=\EE:cm=\EY%+ %+ :co#80:dc=\EN:\
+ :dl=1*\EM:do=\EB:ei=\EO:ho=\EH:im=\E@:li#24:mi:nd=\EC:as=\EF:ae=\EG:\
+ :ms:ta=^I:pt:sr=\EI:se=\Eq:so=\Ep:up=\EA:vs=\Ex4:ve=\Ey4:\
+ :kb=^H:ku=\EA:kd=\EB:kl=\ED:kr=\EC:kh=\EH:kn#1:k0=\E~:l0=HOME:\
+ :k1=\ES:k2=\ET:k3=\EU:k4=\EV:k5=\EW:k6=\EP:k7=\EQ:k8=\ER:k9=\E01:\
+ :es:hs:ts=\Ej\Ex5\Ex1\EY8%+ \Eo:fs=\Ek\Ey5:ds=\Ey1:us=\Es8:ue=\Es0:
+ *
+ */
+
+h29_setmodes(new)
+register new;
+{
+ register modes = '0';
+
+ if (new & WWM_REV)
+ modes += 0x01;
+ if (new & WWM_BLK)
+ modes += 0x02;
+ if (new & WWM_DIM)
+ modes += 0x04;
+ if (new & WWM_UL)
+ modes += 0x08;
+ if (new & WWM_USR)
+ modes += 0x10;
+ ttesc('s');
+ ttputc(modes);
+ if (new & WWM_GRP) {
+ if ((tt.tt_modes & WWM_GRP) == 0)
+ ttesc('F');
+ } else
+ if (tt.tt_modes & WWM_GRP)
+ ttesc('G');
+ tt.tt_modes = new;
+}
+
+tt_h29()
+{
+ if (tt_h19() < 0)
+ return -1;
+ tt.tt_setmodes = h29_setmodes;
+ tt.tt_availmodes |= WWM_BLK|WWM_UL|WWM_DIM|WWM_USR;
+ return 0;
+}
diff --git a/usr.bin/window/ttinit.c b/usr.bin/window/ttinit.c
new file mode 100644
index 0000000..eac904c
--- /dev/null
+++ b/usr.bin/window/ttinit.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttinit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include <stdlib.h>
+
+int tt_h19();
+int tt_h29();
+int tt_f100();
+int tt_tvi925();
+int tt_wyse75();
+int tt_wyse60();
+int tt_zapple();
+int tt_zentec();
+int tt_generic();
+struct tt_tab tt_tab[] = {
+ { "h19", 3, tt_h19 },
+ { "h29", 3, tt_h29 },
+ { "f100", 4, tt_f100 },
+ { "tvi925", 6, tt_tvi925 },
+ { "wyse75", 6, tt_wyse75 },
+ { "wyse60", 6, tt_wyse60 },
+ { "w60", 3, tt_wyse60 },
+ { "zapple", 6, tt_zapple },
+ { "zentec", 6, tt_zentec },
+ { "generic", 0, tt_generic },
+ 0
+};
+
+ttinit()
+{
+ int i;
+ register struct tt_tab *tp;
+ register char *p, *q;
+ register char *t;
+
+ tt_strp = tt_strings;
+
+ /*
+ * Set output buffer size to about 1 second of output time.
+ */
+ i = MIN(wwbaud/10, 512);
+ if ((tt_ob = malloc((unsigned) i)) == 0) {
+ wwerrno = WWE_NOMEM;
+ return -1;
+ }
+ tt_obp = tt_ob;
+ tt_obe = tt_ob + i;
+
+ /*
+ * Use the standard name of the terminal (i.e. the second
+ * name in termcap).
+ */
+ for (p = wwtermcap; *p && *p != '|' && *p != ':'; p++)
+ ;
+ if (*p == '|')
+ p++;
+ for (q = p; *q && *q != '|' && *q != ':'; q++)
+ ;
+ if (q != p && (t = malloc((unsigned) (q - p + 1))) != 0) {
+ wwterm = t;
+ while (p < q)
+ *t++ = *p++;
+ *t = 0;
+ }
+ for (tp = tt_tab; tp->tt_name != 0; tp++)
+ if (strncmp(tp->tt_name, wwterm, tp->tt_len) == 0)
+ break;
+ if (tp->tt_name == 0) {
+ wwerrno = WWE_BADTERM;
+ return -1;
+ }
+ if ((*tp->tt_func)() < 0) {
+ wwerrno = WWE_CANTDO;
+ return -1;
+ }
+ if (wwgetttysize(0, &tt.tt_nrow, &tt.tt_ncol) < 0)
+ return -1;
+ tt.tt_scroll_top = 0;
+ tt.tt_scroll_bot = tt.tt_nrow - 1;
+ return 0;
+}
diff --git a/usr.bin/window/ttoutput.c b/usr.bin/window/ttoutput.c
new file mode 100644
index 0000000..5c582d6
--- /dev/null
+++ b/usr.bin/window/ttoutput.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttoutput.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include <sys/errno.h>
+
+/*
+ * Buffered output package.
+ * We need this because stdio fails on non-blocking writes.
+ */
+
+ttflush()
+{
+ register char *p;
+ register n = tt_obp - tt_ob;
+ extern errno;
+
+ if (n == 0)
+ return;
+ if (tt.tt_checksum)
+ (*tt.tt_checksum)(tt_ob, n);
+ if (tt.tt_flush) {
+ (*tt.tt_flush)();
+ return;
+ }
+ wwnflush++;
+ for (p = tt_ob; p < tt_obp;) {
+ wwnwr++;
+ n = write(1, p, tt_obp - p);
+ if (n < 0) {
+ wwnwre++;
+ if (errno != EWOULDBLOCK) {
+ /* can't deal with this */
+ p = tt_obp;
+ }
+ } else if (n == 0) {
+ /* what to do? */
+ wwnwrz++;
+ } else {
+ wwnwrc += n;
+ p += n;
+ }
+ }
+ tt_obp = tt_ob;
+}
+
+ttputs(s)
+register char *s;
+{
+ while (*s)
+ ttputc(*s++);
+}
+
+ttwrite(s, n)
+ register char *s;
+ register n;
+{
+ switch (n) {
+ case 0:
+ break;
+ case 1:
+ ttputc(*s);
+ break;
+ case 2:
+ if (tt_obe - tt_obp < 2)
+ ttflush();
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s;
+ break;
+ case 3:
+ if (tt_obe - tt_obp < 3)
+ ttflush();
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s;
+ break;
+ case 4:
+ if (tt_obe - tt_obp < 4)
+ ttflush();
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s;
+ break;
+ case 5:
+ if (tt_obe - tt_obp < 5)
+ ttflush();
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s++;
+ *tt_obp++ = *s;
+ break;
+ default:
+ while (n > 0) {
+ register m;
+
+ while ((m = tt_obe - tt_obp) == 0)
+ ttflush();
+ if ((m = tt_obe - tt_obp) > n)
+ m = n;
+ bcopy(s, tt_obp, m);
+ tt_obp += m;
+ s += m;
+ n -= m;
+ }
+ }
+}
diff --git a/usr.bin/window/tttermcap.c b/usr.bin/window/tttermcap.c
new file mode 100644
index 0000000..a1e8508
--- /dev/null
+++ b/usr.bin/window/tttermcap.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tttermcap.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "tt.h"
+
+char *tgetstr();
+char *tgoto();
+char *malloc();
+
+tttputc(c)
+{
+ ttputc(c);
+}
+
+ttxputc(c)
+{
+ *tt_strp++ = c;
+}
+
+struct tt_str *
+tttgetstr(str)
+ char *str;
+{
+ register struct tt_str *s;
+
+ if ((str = tgetstr(str, &tt_strp)) == 0)
+ return 0;
+ if ((s = (struct tt_str *) malloc(sizeof *s)) == 0)
+ return 0;
+ s->ts_str = str;
+ s->ts_n = tt_strp - s->ts_str - 1;
+ return s;
+}
+
+struct tt_str *
+ttxgetstr(str)
+ char *str;
+{
+ register struct tt_str *s;
+ char buf[100];
+ char *bufp = buf;
+
+ if (tgetstr(str, &bufp) == 0)
+ return 0;
+ if ((s = (struct tt_str *) malloc(sizeof *s)) == 0)
+ return 0;
+ s->ts_str = tt_strp;
+ tputs(buf, 1, ttxputc);
+ s->ts_n = tt_strp - s->ts_str;
+ *tt_strp++ = 0;
+ return s;
+}
+
+tttgoto(s, col, row)
+ struct tt_str *s;
+{
+ register char *p = s->ts_str;
+
+ ttputs(tgoto(p, col, row));
+ for (p += s->ts_n; *--p == 0;)
+ ttputc(0);
+}
+
+ttpgoto(s, col, row, n)
+ struct tt_str *s;
+{
+
+ tputs(tgoto(s->ts_str, col, row), n, tttputc);
+}
+
+ttstrcmp(a, b)
+ register struct tt_str *a, *b;
+{
+ int n, r;
+
+ if (r = bcmp(a->ts_str, b->ts_str,
+ (n = a->ts_n - b->ts_n) < 0 ? a->ts_n : b->ts_n))
+ return r;
+ return n;
+}
diff --git a/usr.bin/window/tttvi925.c b/usr.bin/window/tttvi925.c
new file mode 100644
index 0000000..4f9ae2f
--- /dev/null
+++ b/usr.bin/window/tttvi925.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Barto at Celerity Computer Corp.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tttvi925.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+/*
+ * Televideo 925 as emulated by Microterm.
+ */
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short tvi925_frame[16] = {
+ ' ', '~'|G, '|'|G, 'c'|G,
+ '~'|G, '~'|G, '`'|G, 'e'|G,
+ '|'|G, 'a'|G, '|'|G, 'g'|G,
+ 'b'|G, 'f'|G, 'h'|G, 'd'|G
+};
+
+tt_tvi925()
+{
+
+ if (tt_generic() < 0)
+ return -1;
+ tt.tt_availmodes |= WWM_GRP;
+ tt.tt_frame = tvi925_frame;
+ return 0;
+}
diff --git a/usr.bin/window/ttwyse60.c b/usr.bin/window/ttwyse60.c
new file mode 100644
index 0000000..8efff55
--- /dev/null
+++ b/usr.bin/window/ttwyse60.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1987 by David C. Elliott, MIPS Computer Systems.
+ *
+ * Unlimited redistribution allowed as long as this notice
+ * is kept intact.
+ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David C. Elliott, of MIPS Computer Systems.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttwyse60.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short wyse60_frame[16] = {
+ ' ', '6'|G, ':'|G, '1'|G,
+ '6'|G, '6'|G, '2'|G, '4'|G,
+ ':'|G, '5'|G, ':'|G, '='|G,
+ '3'|G, '9'|G, '0'|G, '0'|G
+};
+
+extern struct tt_str *gen_AS;
+extern struct tt_str *gen_AE;
+
+tt_wyse60()
+{
+ static struct tt_str ae = { "\033H\003", 3 };
+ static struct tt_str as = { "\033H\002", 3 };
+
+ if (tt_generic() < 0)
+ return -1;
+ tt.tt_availmodes |= WWM_GRP;
+ tt.tt_frame = wyse60_frame;
+ if (gen_AS == 0)
+ gen_AS = &as;
+ if (gen_AE == 0)
+ gen_AE = &ae;
+ return 0;
+}
diff --git a/usr.bin/window/ttwyse75.c b/usr.bin/window/ttwyse75.c
new file mode 100644
index 0000000..8424d0d
--- /dev/null
+++ b/usr.bin/window/ttwyse75.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 1987 by David C. Elliott, MIPS Computer Systems.
+ *
+ * Unlimited redistribution allowed as long as this notice
+ * is kept intact.
+ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David C. Elliott, of MIPS Computer Systems.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttwyse75.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short wyse75_frame[16] = {
+ ' ', 'x'|G, 'q'|G, 'm'|G,
+ 'x'|G, 'x'|G, 'l'|G, 't'|G,
+ 'q'|G, 'j'|G, 'q'|G, 'v'|G,
+ 'k'|G, 'u'|G, 'w'|G, 'v'|G
+};
+
+extern struct tt_str *gen_AS;
+extern struct tt_str *gen_AE;
+
+tt_wyse75()
+{
+ static struct tt_str ae = { "\033(B", 3 };
+ static struct tt_str as = { "\033(0", 3 };
+
+ if (tt_generic() < 0)
+ return -1;
+ tt.tt_availmodes |= WWM_GRP;
+ tt.tt_frame = wyse75_frame;
+ if (gen_AS == 0)
+ gen_AS = &as;
+ if (gen_AE == 0)
+ gen_AE = &ae;
+ return 0;
+}
diff --git a/usr.bin/window/ttzapple.c b/usr.bin/window/ttzapple.c
new file mode 100644
index 0000000..a285712
--- /dev/null
+++ b/usr.bin/window/ttzapple.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttzapple.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include "char.h"
+
+/*
+zz|zapple|perfect apple:\
+ :am:pt:co#80:li#24:le=^H:nd=^F:up=^K:do=^J:\
+ :ho=\E0:ll=\E1:cm=\E=%+ %+ :ch=\E<%+ :cv=\E>%+ :\
+ :cl=\E4:ce=\E2:cd=\E3:rp=\E@%.%+ :\
+ :so=\E+:se=\E-:\
+ :dc=\Ec:DC=\EC%+ :ic=\Ei:IC=\EI%+ :\
+ :al=\Ea:AL=\EA%+ :dl=\Ed:DL=\ED%+ :\
+ :sf=\Ef:SF=\EF%+ :sr=\Er:SR=\ER%+ :cs=\E?%+ %+ :\
+ :is=\E-\ET :
+*/
+
+#define NCOL 80
+#define NROW 24
+#define TOKEN_MAX 32
+
+extern short gen_frame[];
+
+ /* for error correction */
+int zz_ecc;
+int zz_lastc;
+
+ /* for checkpointing */
+int zz_sum;
+
+zz_setmodes(new)
+{
+ if (new & WWM_REV) {
+ if ((tt.tt_modes & WWM_REV) == 0)
+ ttesc('+');
+ } else
+ if (tt.tt_modes & WWM_REV)
+ ttesc('-');
+ tt.tt_modes = new;
+}
+
+zz_insline(n)
+{
+ if (n == 1)
+ ttesc('a');
+ else {
+ ttesc('A');
+ ttputc(n + ' ');
+ }
+}
+
+zz_delline(n)
+{
+ if (n == 1)
+ ttesc('d');
+ else {
+ ttesc('D');
+ ttputc(n + ' ');
+ }
+}
+
+zz_putc(c)
+ char c;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ zz_setmodes(tt.tt_nmodes);
+ ttputc(c);
+ if (++tt.tt_col == NCOL)
+ tt.tt_col = 0, tt.tt_row++;
+}
+
+zz_write(p, n)
+ register char *p;
+ register n;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ zz_setmodes(tt.tt_nmodes);
+ ttwrite(p, n);
+ tt.tt_col += n;
+ if (tt.tt_col == NCOL)
+ tt.tt_col = 0, tt.tt_row++;
+}
+
+zz_move(row, col)
+ register row, col;
+{
+ register x;
+
+ if (tt.tt_row == row) {
+same_row:
+ if ((x = col - tt.tt_col) == 0)
+ return;
+ if (col == 0) {
+ ttctrl('m');
+ goto out;
+ }
+ switch (x) {
+ case 2:
+ ttctrl('f');
+ case 1:
+ ttctrl('f');
+ goto out;
+ case -2:
+ ttctrl('h');
+ case -1:
+ ttctrl('h');
+ goto out;
+ }
+ if ((col & 7) == 0 && x > 0 && x <= 16) {
+ ttctrl('i');
+ if (x > 8)
+ ttctrl('i');
+ goto out;
+ }
+ ttesc('<');
+ ttputc(col + ' ');
+ goto out;
+ }
+ if (tt.tt_col == col) {
+ switch (row - tt.tt_row) {
+ case 2:
+ ttctrl('j');
+ case 1:
+ ttctrl('j');
+ goto out;
+ case -2:
+ ttctrl('k');
+ case -1:
+ ttctrl('k');
+ goto out;
+ }
+ if (col == 0) {
+ if (row == 0)
+ goto home;
+ if (row == NROW - 1)
+ goto ll;
+ }
+ ttesc('>');
+ ttputc(row + ' ');
+ goto out;
+ }
+ if (col == 0) {
+ if (row == 0) {
+home:
+ ttesc('0');
+ goto out;
+ }
+ if (row == tt.tt_row + 1) {
+ /*
+ * Do newline first to match the sequence
+ * for scroll down and return
+ */
+ ttctrl('j');
+ ttctrl('m');
+ goto out;
+ }
+ if (row == NROW - 1) {
+ll:
+ ttesc('1');
+ goto out;
+ }
+ }
+ /* favor local motion for better compression */
+ if (row == tt.tt_row + 1) {
+ ttctrl('j');
+ goto same_row;
+ }
+ if (row == tt.tt_row - 1) {
+ ttctrl('k');
+ goto same_row;
+ }
+ ttesc('=');
+ ttputc(' ' + row);
+ ttputc(' ' + col);
+out:
+ tt.tt_col = col;
+ tt.tt_row = row;
+}
+
+zz_start()
+{
+ ttesc('T');
+ ttputc(TOKEN_MAX + ' ');
+ ttesc('U');
+ ttputc('!');
+ zz_ecc = 1;
+ zz_lastc = -1;
+ ttesc('v');
+ ttflush();
+ zz_sum = 0;
+ zz_setscroll(0, NROW - 1);
+ zz_clear();
+ zz_setmodes(0);
+}
+
+zz_reset()
+{
+ zz_setscroll(0, NROW - 1);
+ tt.tt_modes = WWM_REV;
+ zz_setmodes(0);
+ tt.tt_col = tt.tt_row = -10;
+}
+
+zz_end()
+{
+ ttesc('T');
+ ttputc(' ');
+ ttesc('U');
+ ttputc(' ');
+ zz_ecc = 0;
+}
+
+zz_clreol()
+{
+ ttesc('2');
+}
+
+zz_clreos()
+{
+ ttesc('3');
+}
+
+zz_clear()
+{
+ ttesc('4');
+ tt.tt_col = tt.tt_row = 0;
+}
+
+zz_insspace(n)
+{
+ if (n == 1)
+ ttesc('i');
+ else {
+ ttesc('I');
+ ttputc(n + ' ');
+ }
+}
+
+zz_delchar(n)
+{
+ if (n == 1)
+ ttesc('c');
+ else {
+ ttesc('C');
+ ttputc(n + ' ');
+ }
+}
+
+zz_scroll_down(n)
+{
+ if (n == 1)
+ if (tt.tt_row == NROW - 1)
+ ttctrl('j');
+ else
+ ttesc('f');
+ else {
+ ttesc('F');
+ ttputc(n + ' ');
+ }
+}
+
+zz_scroll_up(n)
+{
+ if (n == 1)
+ ttesc('r');
+ else {
+ ttesc('R');
+ ttputc(n + ' ');
+ }
+}
+
+zz_setscroll(top, bot)
+{
+ ttesc('?');
+ ttputc(top + ' ');
+ ttputc(bot + ' ');
+ tt.tt_scroll_top = top;
+ tt.tt_scroll_bot = bot;
+}
+
+int zz_debug = 0;
+
+zz_set_token(t, s, n)
+ char *s;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ zz_setmodes(tt.tt_nmodes);
+ if (zz_debug) {
+ char buf[100];
+ zz_setmodes(WWM_REV);
+ (void) sprintf(buf, "%02x=", t);
+ ttputs(buf);
+ tt.tt_col += 3;
+ }
+ ttputc(0x80);
+ ttputc(t + 1);
+ s[n - 1] |= 0x80;
+ ttwrite(s, n);
+ s[n - 1] &= ~0x80;
+}
+
+/*ARGSUSED*/
+zz_put_token(t, s, n)
+ char *s;
+{
+ if (tt.tt_nmodes != tt.tt_modes)
+ zz_setmodes(tt.tt_nmodes);
+ if (zz_debug) {
+ char buf[100];
+ zz_setmodes(WWM_REV);
+ (void) sprintf(buf, "%02x>", t);
+ ttputs(buf);
+ tt.tt_col += 3;
+ }
+ ttputc(t + 0x81);
+}
+
+zz_rint(p, n)
+ char *p;
+{
+ register i;
+ register char *q;
+
+ if (!zz_ecc)
+ return n;
+ for (i = n, q = p; --i >= 0;) {
+ register c = (unsigned char) *p++;
+
+ if (zz_lastc == 0) {
+ switch (c) {
+ case 0:
+ *q++ = 0;
+ zz_lastc = -1;
+ break;
+ case 1: /* start input ecc */
+ zz_ecc = 2;
+ zz_lastc = -1;
+ wwnreadstat++;
+ break;
+ case 2: /* ack checkpoint */
+ tt.tt_ack = 1;
+ zz_lastc = -1;
+ wwnreadack++;
+ break;
+ case 3: /* nack checkpoint */
+ tt.tt_ack = -1;
+ zz_lastc = -1;
+ wwnreadnack++;
+ break;
+ default:
+ zz_lastc = c;
+ wwnreadec++;
+ }
+ } else if (zz_ecc == 1) {
+ if (c)
+ *q++ = c;
+ else
+ zz_lastc = 0;
+ } else {
+ if (zz_lastc < 0) {
+ zz_lastc = c;
+ } else if (zz_lastc == c) {
+ *q++ = zz_lastc;
+ zz_lastc = -1;
+ } else {
+ wwnreadec++;
+ zz_lastc = c;
+ }
+ }
+ }
+ return q - (p - n);
+}
+
+zz_checksum(p, n)
+ register char *p;
+ register n;
+{
+ while (--n >= 0) {
+ register c = *p++ & 0x7f;
+ c ^= zz_sum;
+ zz_sum = c << 1 | c >> 11 & 1;
+ }
+}
+
+zz_compress(flag)
+{
+ if (flag)
+ tt.tt_checksum = 0;
+ else
+ tt.tt_checksum = zz_checksum;
+}
+
+zz_checkpoint()
+{
+ static char x[] = { ctrl('['), 'V', 0, 0 };
+
+ zz_checksum(x, sizeof x);
+ ttesc('V');
+ ttputc(' ' + (zz_sum & 0x3f));
+ ttputc(' ' + (zz_sum >> 6 & 0x3f));
+ ttflush();
+ zz_sum = 0;
+}
+
+tt_zapple()
+{
+ tt.tt_insspace = zz_insspace;
+ tt.tt_delchar = zz_delchar;
+ tt.tt_insline = zz_insline;
+ tt.tt_delline = zz_delline;
+ tt.tt_clreol = zz_clreol;
+ tt.tt_clreos = zz_clreos;
+ tt.tt_scroll_down = zz_scroll_down;
+ tt.tt_scroll_up = zz_scroll_up;
+ tt.tt_setscroll = zz_setscroll;
+ tt.tt_availmodes = WWM_REV;
+ tt.tt_wrap = 1;
+ tt.tt_retain = 0;
+ tt.tt_ncol = NCOL;
+ tt.tt_nrow = NROW;
+ tt.tt_start = zz_start;
+ tt.tt_reset = zz_reset;
+ tt.tt_end = zz_end;
+ tt.tt_write = zz_write;
+ tt.tt_putc = zz_putc;
+ tt.tt_move = zz_move;
+ tt.tt_clear = zz_clear;
+ tt.tt_setmodes = zz_setmodes;
+ tt.tt_frame = gen_frame;
+ tt.tt_padc = TT_PADC_NONE;
+ tt.tt_ntoken = 127;
+ tt.tt_set_token = zz_set_token;
+ tt.tt_put_token = zz_put_token;
+ tt.tt_token_min = 1;
+ tt.tt_token_max = TOKEN_MAX;
+ tt.tt_set_token_cost = 2;
+ tt.tt_put_token_cost = 1;
+ tt.tt_compress = zz_compress;
+ tt.tt_checksum = zz_checksum;
+ tt.tt_checkpoint = zz_checkpoint;
+ tt.tt_reset = zz_reset;
+ tt.tt_rint = zz_rint;
+ return 0;
+}
diff --git a/usr.bin/window/ttzentec.c b/usr.bin/window/ttzentec.c
new file mode 100644
index 0000000..97fbb07
--- /dev/null
+++ b/usr.bin/window/ttzentec.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttzentec.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+/*
+ * Zentec 1021
+ *
+ * We let the termcap entry specify how to enter and exit graphics mode,
+ * since it varies with what the terminal is emulating.
+ */
+
+#define G (WWM_GRP << WWC_MSHIFT)
+short zentec_frame[16] = {
+ ' ', 'x'|G, 'q'|G, 'm'|G,
+ 'x'|G, 'x'|G, 'l'|G, 't'|G,
+ 'q'|G, 'j'|G, 'q'|G, 'v'|G,
+ 'k'|G, 'u'|G, 'w'|G, 'n'|G
+};
+
+tt_zentec()
+{
+ if (tt_generic() < 0)
+ return -1;
+ if (tt.tt_availmodes | WWM_GRP)
+ tt.tt_frame = zentec_frame;
+ return 0;
+}
diff --git a/usr.bin/window/value.h b/usr.bin/window/value.h
new file mode 100644
index 0000000..28a1162
--- /dev/null
+++ b/usr.bin/window/value.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)value.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct value {
+ char v_type;
+ union {
+ int V_num;
+ char *V_str;
+ } v_un;
+};
+#define v_num v_un.V_num
+#define v_str v_un.V_str
+
+#define V_NUM 1
+#define V_STR 2
+#define V_ERR 3
+
+#define val_free(v) ((v).v_type == V_STR ? str_free((v).v_str) : 0)
diff --git a/usr.bin/window/var.c b/usr.bin/window/var.c
new file mode 100644
index 0000000..638dc95
--- /dev/null
+++ b/usr.bin/window/var.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)var.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "value.h"
+#include "var.h"
+#include "string.h"
+
+char *malloc();
+
+struct var *
+var_set1(head, name, v)
+struct var **head;
+char *name;
+struct value *v;
+{
+ register struct var **p;
+ register struct var *r;
+ struct value val;
+
+ /* do this first, easier to recover */
+ val = *v;
+ if (val.v_type == V_STR && val.v_str != 0 &&
+ (val.v_str = str_cpy(val.v_str)) == 0)
+ return 0;
+ if (*(p = var_lookup1(head, name)) == 0) {
+ r = (struct var *) malloc(sizeof (struct var));
+ if (r == 0) {
+ val_free(val);
+ return 0;
+ }
+ if ((r->r_name = str_cpy(name)) == 0) {
+ val_free(val);
+ free((char *) r);
+ return 0;
+ }
+ r->r_left = r->r_right = 0;
+ *p = r;
+ } else {
+ r = *p;
+ val_free(r->r_val);
+ }
+ r->r_val = val;
+ return r;
+}
+
+struct var *
+var_setstr1(head, name, str)
+struct var **head;
+char *name;
+char *str;
+{
+ struct value v;
+
+ v.v_type = V_STR;
+ v.v_str = str;
+ return var_set1(head, name, &v);
+}
+
+struct var *
+var_setnum1(head, name, num)
+struct var **head;
+char *name;
+int num;
+{
+ struct value v;
+
+ v.v_type = V_NUM;
+ v.v_num = num;
+ return var_set1(head, name, &v);
+}
+
+var_unset1(head, name)
+struct var **head;
+char *name;
+{
+ register struct var **p;
+ register struct var *r;
+
+ if (*(p = var_lookup1(head, name)) == 0)
+ return -1;
+ r = *p;
+ *p = r->r_left;
+ while (*p != 0)
+ p = &(*p)->r_right;
+ *p = r->r_right;
+ val_free(r->r_val);
+ str_free(r->r_name);
+ free((char *) r);
+ return 0;
+}
+
+struct var **
+var_lookup1(p, name)
+register struct var **p;
+register char *name;
+{
+ register cmp;
+
+ while (*p != 0) {
+ if ((cmp = strcmp(name, (*p)->r_name)) < 0)
+ p = &(*p)->r_left;
+ else if (cmp > 0)
+ p = &(*p)->r_right;
+ else
+ break;
+ }
+ return p;
+}
+
+var_walk1(r, func, a)
+register struct var *r;
+int (*func)();
+{
+ if (r == 0)
+ return 0;
+ if (var_walk1(r->r_left, func, a) < 0 || (*func)(a, r) < 0
+ || var_walk1(r->r_right, func, a) < 0)
+ return -1;
+ return 0;
+}
diff --git a/usr.bin/window/var.h b/usr.bin/window/var.h
new file mode 100644
index 0000000..95fd611
--- /dev/null
+++ b/usr.bin/window/var.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)var.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct var {
+ struct var *r_left;
+ struct var *r_right;
+ char *r_name;
+ struct value r_val;
+};
+
+struct var *var_set1();
+struct var *var_setstr1();
+struct var *var_setnum1();
+struct var **var_lookup1();
+
+#define var_set(n, v) var_set1(&var_head, n, v)
+#define var_setstr(n, s) var_setstr1(&var_head, n, s)
+#define var_setnum(n, i) var_setnum1(&var_head, n, i)
+#define var_unset(n) var_unset1(&var_head, n)
+#define var_lookup(n) (*var_lookup1(&var_head, n))
+#define var_walk(f, a) var_walk1(var_head, f, a)
+
+struct var *var_head; /* secret, shhh */
diff --git a/usr.bin/window/win.c b/usr.bin/window/win.c
new file mode 100644
index 0000000..7eabf23
--- /dev/null
+++ b/usr.bin/window/win.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)win.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "defs.h"
+#include "char.h"
+
+/*
+ * Higher level routines for dealing with windows.
+ *
+ * There are two types of windows: user window, and information window.
+ * User windows are the ones with a pty and shell. Information windows
+ * are for displaying error messages, and other information.
+ *
+ * The windows are doubly linked in overlapping order and divided into
+ * two groups: foreground and normal. Information
+ * windows are always foreground. User windows can be either.
+ * Addwin() adds a window to the list at the top of one of the two groups.
+ * Deletewin() deletes a window. Front() moves a window to the front
+ * of its group. Wwopen(), wwadd(), and wwdelete() should never be called
+ * directly.
+ */
+
+/*
+ * Open a user window.
+ */
+struct ww *
+openwin(id, row, col, nrow, ncol, nline, label, haspty, hasframe, shf, sh)
+char *label;
+char haspty, hasframe;
+char *shf, **sh;
+{
+ register struct ww *w;
+
+ if (id < 0 && (id = findid()) < 0)
+ return 0;
+ if (row + nrow <= 0 || row > wwnrow - 1
+ || col + ncol <= 0 || col > wwncol - 1) {
+ error("Illegal window position.");
+ return 0;
+ }
+ w = wwopen(haspty ? WWO_PTY : WWO_SOCKET, nrow, ncol, row, col, nline);
+ if (w == 0) {
+ error("Can't open window: %s.", wwerror());
+ return 0;
+ }
+ w->ww_id = id;
+ window[id] = w;
+ w->ww_hasframe = hasframe;
+ w->ww_alt = w->ww_w;
+ if (label != 0 && setlabel(w, label) < 0)
+ error("No memory for label.");
+ wwcursor(w, 1);
+ /*
+ * We have to do this little maneuver to make sure
+ * addwin() puts w at the top, so we don't waste an
+ * insert and delete operation.
+ */
+ setselwin((struct ww *)0);
+ addwin(w, 0);
+ setselwin(w);
+ if (wwspawn(w, shf, sh) < 0) {
+ error("Can't execute %s: %s.", shf, wwerror());
+ closewin(w);
+ return 0;
+ }
+ return w;
+}
+
+findid()
+{
+ register i;
+
+ for (i = 0; i < NWINDOW && window[i] != 0; i++)
+ ;
+ if (i >= NWINDOW) {
+ error("Too many windows.");
+ return -1;
+ }
+ return i;
+}
+
+struct ww *
+findselwin()
+{
+ register struct ww *w, *s = 0;
+ register i;
+
+ for (i = 0; i < NWINDOW; i++)
+ if ((w = window[i]) != 0 && w != selwin &&
+ (s == 0 ||
+ !isfg(w) && (w->ww_order < s->ww_order || isfg(s))))
+ s = w;
+ return s;
+}
+
+/*
+ * Close a user window. Close all if w == 0.
+ */
+closewin(w)
+register struct ww *w;
+{
+ char didit = 0;
+ register i;
+
+ if (w != 0) {
+ closewin1(w);
+ didit++;
+ } else
+ for (i = 0; i < NWINDOW; i++) {
+ if ((w = window[i]) == 0)
+ continue;
+ closewin1(w);
+ didit++;
+ }
+ if (didit) {
+ if (selwin == 0)
+ if (lastselwin != 0) {
+ setselwin(lastselwin);
+ lastselwin = 0;
+ } else if (w = findselwin())
+ setselwin(w);
+ if (lastselwin == 0 && selwin)
+ if (w = findselwin())
+ lastselwin = w;
+ reframe();
+ }
+}
+
+/*
+ * Open an information (display) window.
+ */
+struct ww *
+openiwin(nrow, label)
+char *label;
+{
+ register struct ww *w;
+
+ if ((w = wwopen(0, nrow, wwncol, 2, 0, 0)) == 0)
+ return 0;
+ w->ww_mapnl = 1;
+ w->ww_hasframe = 1;
+ w->ww_nointr = 1;
+ w->ww_noupdate = 1;
+ w->ww_unctrl = 1;
+ w->ww_id = -1;
+ w->ww_center = 1;
+ (void) setlabel(w, label);
+ addwin(w, 1);
+ reframe();
+ return w;
+}
+
+/*
+ * Close an information window.
+ */
+closeiwin(w)
+struct ww *w;
+{
+ closewin1(w);
+ reframe();
+}
+
+closewin1(w)
+register struct ww *w;
+{
+ if (w == selwin)
+ selwin = 0;
+ if (w == lastselwin)
+ lastselwin = 0;
+ if (w->ww_id >= 0 && w->ww_id < NWINDOW)
+ window[w->ww_id] = 0;
+ if (w->ww_label)
+ str_free(w->ww_label);
+ deletewin(w);
+ wwclose(w);
+}
+
+/*
+ * Move the window to the top of its group.
+ * Don't do it if already fully visible.
+ * Wwvisible() doesn't work for tinted windows.
+ * But anything to make it faster.
+ * Always reframe() if doreframe is true.
+ */
+front(w, doreframe)
+register struct ww *w;
+char doreframe;
+{
+ if (w->ww_back != (isfg(w) ? framewin : fgwin) && !wwvisible(w)) {
+ deletewin(w);
+ addwin(w, isfg(w));
+ doreframe = 1;
+ }
+ if (doreframe)
+ reframe();
+}
+
+/*
+ * Add a window at the top of normal windows or foreground windows.
+ * For normal windows, we put it behind the current window.
+ */
+addwin(w, fg)
+register struct ww *w;
+char fg;
+{
+ if (fg) {
+ wwadd(w, framewin);
+ if (fgwin == framewin)
+ fgwin = w;
+ } else
+ wwadd(w, selwin != 0 && selwin != w && !isfg(selwin)
+ ? selwin : fgwin);
+}
+
+/*
+ * Delete a window.
+ */
+deletewin(w)
+register struct ww *w;
+{
+ if (fgwin == w)
+ fgwin = w->ww_back;
+ wwdelete(w);
+}
+
+reframe()
+{
+ register struct ww *w;
+
+ wwunframe(framewin);
+ for (w = wwhead.ww_back; w != &wwhead; w = w->ww_back)
+ if (w->ww_hasframe) {
+ wwframe(w, framewin);
+ labelwin(w);
+ }
+}
+
+labelwin(w)
+register struct ww *w;
+{
+ int mode = w == selwin ? WWM_REV : 0;
+
+ if (!w->ww_hasframe)
+ return;
+ if (w->ww_id >= 0) {
+ char buf[2];
+
+ buf[0] = w->ww_id + '1';
+ buf[1] = 0;
+ wwlabel(w, framewin, 1, buf, mode);
+ }
+ if (w->ww_label) {
+ int col;
+
+ if (w->ww_center) {
+ col = (w->ww_w.nc - strlen(w->ww_label)) / 2;
+ col = MAX(3, col);
+ } else
+ col = 3;
+ wwlabel(w, framewin, col, w->ww_label, mode);
+ }
+}
+
+stopwin(w)
+ register struct ww *w;
+{
+ if (w->ww_pty >= 0 && w->ww_ispty && wwstoptty(w->ww_pty) < 0)
+ error("Can't stop output: %s.", wwerror());
+ else
+ w->ww_stopped = 1;
+}
+
+startwin(w)
+ register struct ww *w;
+{
+ if (w->ww_pty >= 0 && w->ww_ispty && wwstarttty(w->ww_pty) < 0)
+ error("Can't start output: %s.", wwerror());
+ else
+ w->ww_stopped = 0;
+}
+
+sizewin(w, nrow, ncol)
+register struct ww *w;
+{
+ struct ww *back = w->ww_back;
+
+ w->ww_alt.nr = w->ww_w.nr;
+ w->ww_alt.nc = w->ww_w.nc;
+ wwdelete(w);
+ if (wwsize(w, nrow, ncol) < 0)
+ error("Can't resize window: %s.", wwerror());
+ wwadd(w, back);
+ reframe();
+}
+
+waitnl(w)
+struct ww *w;
+{
+ (void) waitnl1(w, "[Type any key to continue]");
+}
+
+more(w, always)
+register struct ww *w;
+char always;
+{
+ int c;
+ char uc = w->ww_unctrl;
+
+ if (!always && w->ww_cur.r < w->ww_w.b - 2)
+ return 0;
+ c = waitnl1(w, "[Type escape to abort, any other key to continue]");
+ w->ww_unctrl = 0;
+ wwputs("\033E", w);
+ w->ww_unctrl = uc;
+ return c == ctrl('[') ? 2 : 1;
+}
+
+waitnl1(w, prompt)
+register struct ww *w;
+char *prompt;
+{
+ char uc = w->ww_unctrl;
+
+ w->ww_unctrl = 0;
+ front(w, 0);
+ wwprintf(w, "\033Y%c%c\033sA%s\033rA ",
+ w->ww_w.nr - 1 + ' ', ' ', prompt); /* print on last line */
+ wwcurtowin(w);
+ while (wwpeekc() < 0)
+ wwiomux();
+ w->ww_unctrl = uc;
+ return wwgetc();
+}
diff --git a/usr.bin/window/window.1 b/usr.bin/window/window.1
new file mode 100644
index 0000000..425cce0
--- /dev/null
+++ b/usr.bin/window/window.1
@@ -0,0 +1,947 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Edward Wang at The University of California, Berkeley.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)window.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt WINDOW 1
+.Os BSD 4.3
+.Sh NAME
+.Nm window
+.Nd window environment
+.Sh SYNOPSIS
+.Nm window
+.Op Fl t
+.Op Fl f
+.Op Fl d
+.Op Fl e Ar escape-char
+.Op Fl c Ar command
+.Sh DESCRIPTION
+.Nm Window
+implements a window environment on
+.Tn ASCII
+terminals.
+.Pp
+A window is a rectangular portion of the physical terminal
+screen associated with a set of processes. Its size and
+position can be changed by the user at any time. Processes
+communicate with their window in the same way they normally
+interact with a terminal\-through their standard input, output,
+and diagnostic file descriptors. The window program handles the
+details of redirecting input and output to and from the
+windows. At any one time, only one window can receive
+input from the keyboard, but all windows can simultaneously send output
+to the display.
+.Pp
+When
+.Nm window
+starts up, the commands (see long commands below)
+contained in the file
+.Pa .windowrc
+in the user's home directory are
+executed. If it does not exist, two equal sized windows spanning
+the terminal screen are created by default.
+.Pp
+The command line options are
+.Bl -tag -width Fl
+.It Fl t
+Turn on terse mode (see
+.Ic terse
+command below).
+.It Fl f
+Fast. Don't perform any startup action.
+.It Fl d
+Ignore
+.Pa .windowrc
+and create the two default
+windows instead.
+.It Fl e Ar escape-char
+Set the escape character to
+.Ar escape-char .
+.Ar Escape-char
+can be a single character, or in the form
+.Ic ^X
+where
+.Ar X
+is any character, meaning
+.No control\- Ns Ar X .
+.It Fl c Ar command
+Execute the string
+.Ar command
+as a long command (see below)
+before doing anything else.
+.El
+.Pp
+Windows can overlap and are framed as necessary. Each window
+is named by one of the digits ``1'' to ``9''. This one-character
+identifier, as well as a user definable label string, are displayed
+with the window on the top edge of its frame. A window can be
+designated to be in the
+.Ar foreground ,
+in which case it will always be
+on top of all normal, non-foreground windows, and can be covered
+only by other foreground windows. A window need not be completely
+within the edges of the terminal screen. Thus a large window
+(possibly larger than the screen) may be positioned to show only
+a portion of its full size.
+.Pp
+Each window has a cursor and a set of control functions. Most intelligent
+terminal operations such as line and
+character deletion and insertion are supported. Display modes
+such as underlining and reverse video are available if they are
+supported by the terminal. In addition,
+similar to terminals with multiple pages of memory,
+each window has a text buffer which can have more lines than the window
+itself.
+.Ss Process Environment
+With each newly created window, a shell program is spawned with its
+process environment tailored to that window. Its standard input,
+output, and diagnostic file descriptors are bound to one end of either
+a pseudo-terminal
+.Xr (pty 4 )
+or a
+.Ux
+domain socket
+.Xr (socketpair 4 ) .
+If a pseudo-terminal is used, then its special
+characters and modes (see
+.Xr stty 1 )
+are copied from the physical
+terminal. A
+.Xr termcap 5
+entry tailored to this window is created
+and passed as environment
+.Xr (environ 5 )
+variable
+.Ev TERMCAP .
+The termcap entry contains the window's size and
+characteristics as well as information from the physical terminal,
+such as the existence of underline, reverse video, and other display
+modes, and the codes produced by the terminal's function keys,
+if any. In addition, the window size attributes of the pseudo-terminal
+are set to reflect the size of this window, and updated whenever
+it is changed by the user. In particular, the editor
+.Xr vi 1
+uses
+this information to redraw its display.
+.Ss Operation
+During normal execution,
+.Nm window
+can be in one of two states:
+conversation mode and command mode. In conversation mode, the
+terminal's real cursor is placed at the cursor position of a particular
+window--called the current window--and input from the keyboard is sent
+to the process in that window. The current window is always
+on top of all other windows, except those in foreground. In addition,
+it is set apart by highlighting its identifier and label in reverse video.
+.Pp
+Typing
+.Nm window Ns 's
+escape character (normally
+.Ic ^P )
+in conversation
+mode switches it into command mode. In command mode, the top line of
+the terminal screen becomes the command prompt window, and
+.Nm window
+interprets input from the keyboard as commands to manipulate windows.
+.Pp
+There are two types of commands: short commands are usually one or two
+key strokes; long commands are strings either typed by the user in the
+command window (see the
+.Dq Ic \&:
+command below), or read from a file (see
+.Ic source
+below).
+.Ss Short Commands
+Below,
+.Ar \&#
+represents one of the digits ``1'' to ``9''
+corresponding to the windows 1 to 9.
+.Ic ^X
+means
+.No control\- Ns Ar X ,
+where
+.Ar X
+is any character. In particular,
+.Ic ^^
+is
+.Li control\-^.
+.Ar Escape
+is the escape key, or
+.Ic ^\&[ .
+.Bl -tag -width Ds
+.It Ar #
+Select window
+.Ar #
+as the current window
+and return to conversation mode.
+.It Ic \&% Ns Ar #
+Select window
+.Ar #
+but stay in command mode.
+.It Ic ^^
+Select the previous window and return to conversation
+mode. This is useful for toggling between two windows.
+.It Ic escape
+Return to conversation mode.
+.It Ic ^P
+Return to conversation mode and write
+.Ic ^P
+to the
+current window. Thus, typing two
+.Ic ^P Ns 's
+in conversation
+mode sends one to the current window. If the
+.Nm window
+escape is changed to some other character, that
+character takes the place of
+.Ic ^P
+here.
+.It Ic ?
+List a short summary of commands.
+.It Ic ^L
+Refresh the screen.
+.It Ic q
+Exit
+.Nm window .
+Confirmation is requested.
+.It Ic ^Z
+Suspend
+.Nm window .
+.It Ic w
+Create a new window. The user is prompted for the positions
+of the upper left and lower right corners of the window.
+The cursor is placed on the screen and the keys ``h'', ``j'',
+``k'', and ``l''
+move the cursor left, down, up, and right, respectively.
+The keys ``H'', ``J'', ``K'', and ``L'' move the cursor to the respective
+limits of the screen. Typing a number before the movement keys
+repeats the movement that number of times. Return enters the cursor position
+as the upper left corner of the window. The lower right corner
+is entered in the same manner. During this process,
+the placement of the new window is indicated by a rectangular
+box drawn on the screen, corresponding to where the new window
+will be framed. Typing escape at any point
+cancels this command.
+.Pp
+This window becomes the current window,
+and is given the first available ID. The default buffer size
+is used (see
+.Ar default_nline
+command below).
+.Pp
+Only fully visible windows can be created this way.
+.It Ic c Ns Ar #
+Close window
+.Ar # .
+The process in the window is sent
+the hangup signal (see
+.Xr kill 1 ) .
+.Xr Csh 1
+should
+handle this signal correctly and cause no problems.
+.It Ic m Ns Ar #
+Move window
+.Ar #
+to another location. A box in the shape
+of the window is drawn on
+the screen to indicate the new position of the window, and the same keys as
+those for the
+.Ic w
+command are used to position the box. The
+window can be moved partially off-screen.
+.It Ic M Ns Ar #
+Move window
+.Ar #
+to its previous position.
+.It Ic s Ns Ar #
+Change the size of window
+.Ar # .
+The user is prompted
+to enter the new lower right corner of the window. A box
+is drawn to indicate the new window size. The same
+keys used in
+.Ic w
+and
+.Ic m
+are used to enter the position.
+.It Ic S Ns Ar #
+Change window
+.Ar #
+to its previous size.
+.It Ic ^Y
+Scroll the current window up by one line.
+.It Ic ^E
+Scroll the current window down by one line.
+.It Ic ^U
+Scroll the current window up by half the window size.
+.It Ic ^D
+Scroll the current window down by half the window size.
+.It Ic ^B
+Scroll the current window up by the full window size.
+.It Ic ^F
+Scroll the current window down by the full window size.
+.It Ic h
+Move the cursor of the current window left by one column.
+.It Ic j
+Move the cursor of the current window down by one line.
+.It Ic k
+Move the cursor of the current window up by one line.
+.It Ic l
+Move the cursor of the current window right by one column.
+.It Ic y
+Yank. The user is prompted to enter two points within the current
+window. Then the content of the current window between those two points
+is saved in the yank buffer.
+.It Ic p
+Put. The content of the yank buffer is written to the current
+window as input.
+.It Ic ^S
+Stop output in the current window.
+.It Ic ^Q
+Start output in the current window.
+.It Ic :
+Enter a line to be executed as long commands.
+Normal line
+editing characters (erase character, erase word, erase line)
+are supported.
+.El
+.Ss Long Commands
+Long commands are a sequence of statements
+parsed much like a programming language, with a syntax
+similar to that of C. Numeric and string expressions and variables
+are supported, as well as conditional statements.
+.Pp
+There are two data types: string and number. A string is a sequence
+of letters or digits beginning with a letter. ``_'' and ``.'' are
+considered letters. Alternately, non-alphanumeric characters can
+be included in strings by quoting them in ``"'' or escaping them
+with ``\\''. In addition, the ``\\'' sequences of C are supported,
+both inside and outside quotes (e.g., ``\\n'' is a new line,
+``\\r'' a carriage return). For example, these are legal strings:
+abcde01234, "&#$^*&#", ab"$#"cd, ab\\$\\#cd, "/usr/ucb/window".
+.Pp
+A number is an integer value in one of three forms:
+a decimal number, an octal number preceded by ``0'',
+or a hexadecimal number preceded by ``0x'' or ``0X''. The natural
+machine integer size is used (i.e., the signed integer type
+of the C compiler). As in C, a non-zero number represents
+a boolean true.
+.Pp
+The character ``#'' begins a comment which terminates at the
+end of the line.
+.Pp
+A statement is either a conditional or an expression. Expression
+statements are terminated with a new line or ``;''. To continue
+an expression on the next line, terminate the first line with ``\\''.
+.Ss Conditional Statement
+.Nm Window
+has a single control structure:
+the fully bracketed if statement in the form
+.Pp
+.Bd -literal -offset indent -compact
+if <expr> then
+\t<statement>
+\t...
+elsif <expr> then
+\t<statement>
+\t...
+else
+\t<statement>
+\t...
+endif
+.Ed
+.Pp
+The
+.Ic else
+and
+.Ic elsif
+parts are optional, and the latter can
+be repeated any number of times.
+<Expr>
+must be numeric.
+.Ss Expressions
+Expressions in
+.Nm window
+are similar to those in the
+C language, with most C operators supported on numeric
+operands. In addition, some are overloaded to operate on strings.
+.Pp
+When an expression is used as a statement, its value is discarded
+after evaluation. Therefore, only expressions with side
+effects (assignments and function calls) are useful as statements.
+.Pp
+Single valued (no arrays) variables are supported, of both
+numeric and string values. Some variables are predefined. They
+are listed below.
+.Pp
+The operators in order of increasing precedence:
+.Bl -tag -width Fl
+.It Xo
+.Aq Va expr1
+.Ic =
+.Aq Va expr2
+.Xc
+Assignment. The variable of name
+.Aq Va expr1 ,
+which must be string valued,
+is assigned the result of
+.Aq Va expr2 .
+Returns the value of
+.Aq Va expr2 .
+.It Xo
+.Aq Va expr1
+.Ic ?
+.Aq Va expr2
+.Ic :
+.Aq Va expr3
+.Xc
+Returns the value of
+.Aq Va expr2
+if
+.Aq Va expr1
+evaluates true
+(non-zero numeric value); returns the value of
+.Aq Va expr3
+otherwise. Only
+one of
+.Aq Va expr2
+and
+.Aq Va expr3
+is evaluated.
+.Aq Va Expr1
+must
+be numeric.
+.It Xo
+.Aq Va expr1
+.Ic \&|\&|
+.Aq Va expr2
+.Xc
+Logical or. Numeric values only. Short circuit evaluation is supported
+(i.e., if
+.Aq Va expr1
+evaluates true, then
+.Aq Va expr2
+is not evaluated).
+.It Xo
+.Aq Va expr1
+.Ic \&&\&&
+.Aq Va expr2
+.Xc
+Logical and with short circuit evaluation. Numeric values only.
+.It Xo
+.Aq Va expr1
+.Ic \&|
+.Aq Va expr2
+.Xc
+Bitwise or. Numeric values only.
+.It Xo
+.Aq Va expr1
+.Ic ^
+.Aq Va expr2
+.Xc
+Bitwise exclusive or. Numeric values only.
+.It Xo
+.Aq Va expr1
+.Ic \&&
+.Aq Va expr2
+.Xc
+Bitwise and. Numeric values only.
+.It Xo
+.Aq Va expr1
+.Ic ==
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic !=
+.Aq expr2
+.Xc
+Comparison (equal and not equal, respectively). The boolean
+result (either 1 or 0) of the comparison is returned. The
+operands can be numeric or string valued. One string operand
+forces the other to be converted to a string in necessary.
+.It Xo
+.Aq Va expr1
+.Ic <
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic >
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic <=
+.Aq Va expr2 ,
+.Xc
+Less than, greater than, less than or equal to,
+greater than or equal to. Both numeric and string values, with
+automatic conversion as above.
+.It Xo
+.Aq Va expr1
+.Ic <<
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic >>
+.Aq Va expr2
+.Xc
+If both operands are numbers,
+.Aq Va expr1
+is bit
+shifted left (or right) by
+.Aq Va expr2
+bits. If
+.Aq Va expr1
+is
+a string, then its first (or last)
+.Aq Va expr2
+characters are
+returns (if
+.Aq Va expr2
+is also a string, then its length is used
+in place of its value).
+.It Xo
+.Aq Va expr1
+.Ic +
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic -
+.Aq Va expr2
+.Xc
+Addition and subtraction on numbers. For ``+'', if one
+argument is a string, then the other is converted to a string,
+and the result is the concatenation of the two strings.
+.It Xo
+.Aq Va expr1
+.Ic \&*
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic \&/
+.Aq Va expr2 ,
+.Aq Va expr1
+.Ic \&%
+.Aq Va expr2
+.Xc
+Multiplication, division, modulo. Numbers only.
+.It Xo
+.Ic \- Ns Aq Va expr ,
+.Ic ~ Ns Aq Va expr ,
+.Ic \&! Ns Aq Va expr ,
+.Ic \&$ Ns Aq Va expr ,
+.Ic \&$? Ns Aq Va expr
+.Xc
+The first three are unary minus, bitwise complement and logical complement
+on numbers only. The operator, ``$'', takes
+.Aq Va expr
+and returns
+the value of the variable of that name. If
+.Aq Va expr
+is numeric
+with value
+.Ar n
+and it appears within an alias macro (see below),
+then it refers to the nth argument of the alias invocation. ``$?''
+tests for the existence of the variable
+.Aq Va expr ,
+and returns 1
+if it exists or 0 otherwise.
+.It Xo
+.Ao Va expr Ac Ns Pq Aq Ar arglist
+.Xc
+Function call.
+.Aq Va Expr
+must be a string that is the unique
+prefix of the name of a builtin
+.Nm window
+function
+or the full name of a user defined alias macro. In the case of a builtin
+function,
+.Aq Ar arglist
+can be in one of two forms:
+.Bd -literal -offset indent
+<expr1>, <expr2>, ...
+argname1 = <expr1>, argname2 = <expr2>, ...
+.Ed
+.Pp
+The two forms can in fact be intermixed, but the result is
+unpredictable. Most arguments can be omitted; default values will
+be supplied for them. The
+.Ar argnames
+can be unique prefixes
+of the argument names. The commas separating
+arguments are used only to disambiguate, and can usually be omitted.
+.Pp
+Only the first argument form is valid for user defined aliases. Aliases
+are defined using the
+.Ic alias
+builtin function (see below). Arguments
+are accessed via a variant of the variable mechanism (see ``$'' operator
+above).
+.Pp
+Most functions return value, but some are used for side effect
+only and so must be used as statements. When a function or an alias is used
+as a statement, the parentheses surrounding
+the argument list may be omitted. Aliases return no value.
+.El
+.Ss Builtin Functions
+The arguments are listed by name in their natural
+order. Optional arguments are in square brackets
+.Sq Op .
+Arguments
+that have no names are in angle brackets
+.Sq <> .
+An argument meant to be a boolean flag (often named
+.Ar flag )
+can be one of
+.Ar on ,
+.Ar off ,
+.Ar yes ,
+.Ar no ,
+.Ar true ,
+or
+.Ar false ,
+with
+obvious meanings, or it can be a numeric expression,
+in which case a non-zero value is true.
+.Bl -tag -width Fl
+.It Xo
+.Ic alias Ns Po Bq Aq Ar string ,
+.Bq Aq Ar string\-list Pc
+.Xc
+If no argument is given, all currently defined alias macros are
+listed. Otherwise,
+.Aq Ar string
+is defined as an alias,
+with expansion
+.Aq Ar string\-list > .
+The previous definition of
+.Aq Ar string ,
+if any, is returned. Default for
+.Aq Ar string\-list
+is no change.
+.It Ic close Ns Pq Aq Ar window\-list
+Close the windows specified in
+.Aq Ar window\-list .
+If
+.Aq Ar window\-list
+is the word
+.Ar all ,
+than all windows are closed. No value is returned.
+.It Ic cursormodes Ns Pq Bq Ar modes
+Set the window cursor to
+.Ar modes .
+.Ar Modes
+is the bitwise
+or of the mode bits defined as the variables
+.Ar m_ul
+(underline),
+.Ar m_rev
+(reverse video),
+.Ar m_blk
+(blinking),
+and
+.Ar m_grp
+(graphics, terminal dependent). Return
+value is the previous modes. Default is no change.
+For example,
+.Li cursor($m_rev$m_blk)
+sets the window cursors to blinking
+reverse video.
+.It Ic default_nline Ns Pq Bq Ar nline
+Set the default buffer size to
+.Ar nline .
+Initially, it is
+48 lines. Returns the old default buffer size. Default is
+no change. Using a very large buffer can slow the program down
+considerably.
+.It Ic default_shell Ns Pq Bq Aq Ar string\-list
+Set the default window shell program to
+.Aq Ar string\-list .
+Returns
+the first string in the old shell setting. Default is no change. Initially,
+the default shell is taken from the environment variable
+.Ev SHELL .
+.It Ic default_smooth Ns Pq Bq Ar flag
+Set the default value of the
+.Ar smooth
+argument
+to the command
+.Nm window
+(see below). The argument
+is a boolean flag (one of
+.Ar on ,
+.Ar off ,
+.Ar yes ,
+.Ar no ,
+.Ar true ,
+.Ar false ,
+or a number,
+as described above). Default is no change.
+The old value (as a number) is returned.
+The initial value is 1 (true).
+.It Xo
+.Ic echo Ns ( Op Ar window ,
+.Bq Aq Ar string\-list )
+.Xc
+Write the list of strings,
+.Aq Ar string-list ,
+to
+.Nm window ,
+separated
+by spaces and terminated with a new line. The strings are only
+displayed in the window, the processes in the window are not
+involved (see
+.Ic write
+below). No value is returned. Default
+is the current window.
+.It Ic escape Ns Pq Bq Ar escapec
+Set the escape character to
+.Ar escape-char .
+Returns the old
+escape character as a one-character string. Default is no
+change.
+.Ar Escapec
+can be a string of a single character, or
+in the form
+.Fl ^X ,
+meaning
+.No control\- Ns Ar X .
+.It Xo
+.Ic foreground Ns ( Bq Ar window ,
+.Bq Ar flag )
+.Xc
+Move
+.Nm window
+in or out of foreground.
+.Ar Flag
+is a boolean value. The old foreground flag
+is returned. Default for
+.Nm window
+is the current window,
+default for
+.Ar flag
+is no change.
+.It Xo
+.Ic label Ns ( Bq Ar window ,
+.Bq Ar label )
+.Xc
+Set the label of
+.Nm window
+to
+.Ar label .
+Returns the old
+label as a string. Default for
+.Nm window
+is the current
+window, default for
+.Ar label
+is no change. To turn
+off a label, set it to an empty string ("").
+.It Ic list Ns Pq
+No arguments. List the identifiers and labels of all windows. No
+value is returned.
+.It Ic select Ns Pq Bq Ar window
+Make
+.Nm window
+the current window. The previous current window
+is returned. Default is no change.
+.It Ic source Ns Pq Ar filename
+Read and execute the long commands in
+.Ar filename .
+Returns \-1 if the file cannot be read, 0 otherwise.
+.It Ic terse Ns Pq Bq flag
+Set terse mode to
+.Ar flag .
+In terse mode, the command window
+stays hidden even in command mode, and errors are reported by
+sounding the terminal's bell.
+.Ar Flag
+can take on the same
+values as in
+.Ar foreground
+above. Returns the old terse flag.
+Default is no change.
+.It Ic unalias Ns Pq Ar alias
+Undefine
+.Ar alias .
+Returns -1 if
+.Ar alias
+does not exist,
+0 otherwise.
+.It Ic unset Ns Pq Ar variable
+Undefine
+.Ar variable .
+Returns -1 if
+.Ar variable
+does not exist,
+0 otherwise.
+.It Ic variables Ns Pq
+No arguments. List all variables. No value is returned.
+.It Xo
+.Ic window Ns ( Bq Ar row ,
+.Bq Ar column ,
+.Bq Ar nrow ,
+.Bq Ar ncol ,
+.Bq Ar nline ,
+.Bq Ar label ,
+.Bq Ar pty ,
+.Bq Ar frame ,
+.Bq Ar mapnl ,
+.Bq Ar keepopen ,
+.Bq Ar smooth ,
+.Bq Ar shell ) .
+.Xc
+Open a window with upper left corner at
+.Ar row ,
+.Ar column
+and size
+.Ar nrow ,
+.Ar ncol .
+If
+.Ar nline
+is specified,
+then that many lines are allocated for the text buffer. Otherwise,
+the default buffer size is used. Default values for
+.Ar row ,
+.Ar column ,
+.Ar nrow ,
+and
+.Ar ncol
+are, respectively,
+the upper, left-most, lower, or right-most extremes of the
+screen.
+.Ar Label
+is the label string.
+.Ar Frame ,
+.Ar pty ,
+and
+.Ar mapnl
+are flag values
+interpreted in the same way as the argument to
+.Ar foreground
+(see above);
+they mean, respectively, put a frame around this window (default true),
+allocate pseudo-terminal for this window rather than socketpair (default
+true), and map new line characters in this window to carriage return
+and line feed (default true if socketpair is used, false otherwise).
+Normally, a window is automatically closed when its process
+exits. Setting
+.Ar keepopen
+to true (default false) prevents this
+action. When
+.Ar smooth
+is true, the screen is updated more frequently
+(for this window) to produce a more terminal-like behavior.
+The default value of
+.Ar smooth
+is set by the
+.Ar default_smooth
+command (see above).
+.Ar Shell
+is a list of strings that will be used as the shell
+program to place in the window (default is the program specified
+by
+.Ar default_shell ,
+see above). The created window's identifier
+is returned as a number.
+.It Xo
+.Ic write Ns ( Bq Ar window ,
+.Bq Aq Ar string\-list )
+.Xc
+Send the list of strings,
+.Aq Ar string-list ,
+to
+.Nm window ,
+separated
+by spaces but not terminated with a new line. The strings are actually
+given to the window as input. No value is returned. Default
+is the current window.
+.El
+.Ss Predefined Variables
+These variables are for information only. Redefining them does
+not affect the internal operation of
+.Nm window .
+.Bl -tag -width modes
+.It Ar baud
+The baud rate as a number between 50 and 38400.
+.It Ar modes
+The display modes (reverse video, underline, blinking, graphics)
+supported by the physical terminal. The value of
+.Ar modes
+is the bitwise or of some of the one bit values,
+.Ar m_blk ,
+.Ar m_grp ,
+.Ar m_rev ,
+and
+.Ar m_ul
+(see below).
+These values are useful
+in setting the window cursors' modes (see
+.Ar cursormodes
+above).
+.It Ar m_blk
+The blinking mode bit.
+.It Ar m_grp
+The graphics mode bit (not very useful).
+.It Ar m_rev
+The reverse video mode bit.
+.It Ar m_ul
+The underline mode bit.
+.It Ar ncol
+The number of columns on the physical screen.
+.It Ar nrow
+The number of rows on the physical screen.
+.It Ar term
+The terminal type. The standard name, found in the second name
+field of the terminal's
+.Ev TERMCAP
+entry, is used.
+.Sh ENVIRONMENT
+.Nm Window
+utilizes these environment variables:
+.Ev HOME ,
+.Ev SHELL ,
+.Ev TERM ,
+.Ev TERMCAP ,
+.Ev WINDOW_ID .
+.Sh FILES
+.Bl -tag -width /dev/[pt]ty[pq]? -compact
+.It Pa ~/.windowrc
+startup command file.
+.It Pa /dev/[pt]ty[pq]?
+pseudo-terminal devices.
+.El
+.Sh HISTORY
+The
+.Nm window
+command appeared in
+.Bx 4.3 .
+.Sh DIAGNOSTICS
+Should be self explanatory.
diff --git a/usr.bin/window/windowrc b/usr.bin/window/windowrc
new file mode 100644
index 0000000..57c695a
--- /dev/null
+++ b/usr.bin/window/windowrc
@@ -0,0 +1,85 @@
+# Copyright (c) 1983, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)windowrc 8.1 (Berkeley) 6/6/93
+#
+
+# Configuration file example for window manager
+# To be installed in ~/.windowrc
+#
+# Create two unequal sized windows of full screen width,
+# and set up some useful aliases.
+#
+
+#
+# Optional settings
+#
+# terse on # set terse mode
+# escape "^A" # set escape character
+# nline 100 # set default buffer size
+ # initially, this is 48
+
+#
+# Make two windows
+# The bottom one is MIN(24, total lines * 3 / 4) lines
+# The top one is the rest of the screen.
+#
+three_fourth = $nrow - ((_ = $nrow * 3 / 4) > 24 ? 24 : $_)
+unset _
+window row = 0, nrow = $three_fourth - 1, label = "Top"
+window row = $three_fourth, label = "Local"
+
+#
+# Useful aliases
+#
+#
+# Standard window
+#
+alias std "window r = $three_fourth, l = $?1 ? $1 : ''"
+#
+# Sysline, add your own options
+#
+alias sysline "_ = select();" \
+ "foreground window(r = 0, nr = 1, nc = $ncol + 1, nl = 0," \
+ "l = sysline, pty = no, frame = no, sh = sysline \\-w), 1;" \
+ "select $_; unset _"
+#
+# Rlogin
+#
+alias rlogin "window r = $three_fourth, l = $1, pty = no, mapnl = no," \
+ "sh = sh \\-c 'echo $TERMCAP | rsh ' + $1 + ' \\'cat > .TERMCAP\\' ;" \
+ "exec rlogin ' + $1"
+alias rl rlogin \$1
+#
+# Two equal windows
+#
+alias two "window r = 1, nr = $nrow / 2 - 1, l = top;" \
+ "window r = $nrow / 2 + 1, l = bottom"
diff --git a/usr.bin/window/ww.h b/usr.bin/window/ww.h
new file mode 100644
index 0000000..82c4acf
--- /dev/null
+++ b/usr.bin/window/ww.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ww.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifdef OLD_TTY
+#include <sgtty.h>
+#else
+#include <termios.h>
+#endif
+#include <setjmp.h>
+#include <machine/endian.h>
+
+#define NWW 30 /* maximum number of windows */
+
+ /* a rectangle */
+struct ww_dim {
+ int nr; /* number of rows */
+ int nc; /* number of columns */
+ int t, b; /* top, bottom */
+ int l, r; /* left, right */
+};
+
+ /* a coordinate */
+struct ww_pos {
+ int r; /* row */
+ int c; /* column */
+};
+
+ /* the window structure */
+struct ww {
+ /* general flags and states */
+ char ww_state; /* state of window */
+ char ww_oflags; /* wwopen flags */
+
+ /* information for overlap */
+ struct ww *ww_forw; /* doubly linked list, for overlapping info */
+ struct ww *ww_back;
+ char ww_index; /* the window index, for wwindex[] */
+ char ww_order; /* the overlapping order */
+
+ /* sizes and positions */
+ struct ww_dim ww_w; /* window size and pos */
+ struct ww_dim ww_b; /* buffer size and pos */
+ struct ww_dim ww_i; /* the part inside the screen */
+ struct ww_pos ww_cur; /* the cursor position, relative to ww_w */
+
+ /* arrays */
+ char **ww_win; /* the window */
+ union ww_char **ww_buf; /* the buffer */
+ char **ww_fmap; /* map for frame and box windows */
+ short *ww_nvis; /* how many ww_buf chars are visible per row */
+
+ /* information for wwwrite() and company */
+ char ww_wstate; /* state for outputting characters */
+ char ww_modes; /* current display modes */
+ char ww_insert; /* insert mode */
+ char ww_mapnl; /* map \n to \r\n */
+ char ww_noupdate; /* don't do updates in wwwrite() */
+ char ww_unctrl; /* expand control characters */
+ char ww_nointr; /* wwwrite() not interruptable */
+ char ww_hascursor; /* has fake cursor */
+
+ /* things for the window process and io */
+ char ww_ispty; /* ww_pty is really a pty, not socket pair */
+ char ww_stopped; /* output stopped */
+ int ww_pty; /* file descriptor of pty or socket pair */
+ int ww_socket; /* other end of socket pair */
+ int ww_pid; /* pid of process, if WWS_HASPROC true */
+ char ww_ttyname[11]; /* "/dev/ttyp?" */
+ char *ww_ob; /* output buffer */
+ char *ww_obe; /* end of ww_ob */
+ char *ww_obp; /* current read position in ww_ob */
+ char *ww_obq; /* current write position in ww_ob */
+
+ /* things for the user, they really don't belong here */
+ char ww_id; /* the user window id */
+ char ww_center; /* center the label */
+ char ww_hasframe; /* frame it */
+ char ww_keepopen; /* keep it open after the process dies */
+ char *ww_label; /* the user supplied label */
+ struct ww_dim ww_alt; /* alternate position and size */
+};
+
+ /* state of a tty */
+struct ww_tty {
+#ifdef OLD_TTY
+ struct sgttyb ww_sgttyb;
+ struct tchars ww_tchars;
+ struct ltchars ww_ltchars;
+ int ww_lmode;
+ int ww_ldisc;
+#else
+ struct termios ww_termios;
+#endif
+ int ww_fflags;
+};
+
+union ww_char {
+ short c_w; /* as a word */
+ struct {
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+ char C_c; /* the character part */
+ char C_m; /* the mode part */
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ char C_m; /* the mode part */
+ char C_c; /* the character part */
+#endif
+ } c_un;
+};
+#define c_c c_un.C_c
+#define c_m c_un.C_m
+
+ /* for display update */
+struct ww_update {
+ int best_gain;
+ int best_col;
+ int gain;
+};
+
+ /* parts of ww_char */
+#define WWC_CMASK 0x00ff
+#define WWC_MMASK 0xff00
+#define WWC_MSHIFT 8
+
+ /* c_m bits */
+#define WWM_REV 0x01 /* reverse video */
+#define WWM_BLK 0x02 /* blinking */
+#define WWM_UL 0x04 /* underlined */
+#define WWM_GRP 0x08 /* graphics */
+#define WWM_DIM 0x10 /* half intensity */
+#define WWM_USR 0x20 /* user specified mode */
+#define WWM_GLS 0x40 /* window only, glass, i.e., transparent */
+
+ /* ww_state values */
+#define WWS_INITIAL 0 /* just opened */
+#define WWS_HASPROC 1 /* has process on pty */
+#define WWS_DEAD 3 /* child died */
+
+ /* flags for ww_fmap */
+#define WWF_U 0x01
+#define WWF_R 0x02
+#define WWF_D 0x04
+#define WWF_L 0x08
+#define WWF_MASK (WWF_U|WWF_R|WWF_D|WWF_L)
+#define WWF_LABEL 0x40
+#define WWF_TOP 0x80
+
+ /* flags to wwopen() */
+#define WWO_PTY 0x01 /* want pty */
+#define WWO_SOCKET 0x02 /* want socket pair */
+#define WWO_REVERSE 0x04 /* make it all reverse video */
+#define WWO_GLASS 0x08 /* make it all glass */
+#define WWO_FRAME 0x10 /* this is a frame window */
+
+ /* special ww_index value */
+#define WWX_NOBODY NWW
+
+ /* error codes */
+#define WWE_NOERR 0
+#define WWE_SYS 1 /* system error */
+#define WWE_NOMEM 2 /* out of memory */
+#define WWE_TOOMANY 3 /* too many windows */
+#define WWE_NOPTY 4 /* no more ptys */
+#define WWE_SIZE 5 /* bad window size */
+#define WWE_BADTERM 6 /* bad terminal type */
+#define WWE_CANTDO 7 /* dumb terminal */
+
+ /* wwtouched[] bits, there used to be more than one */
+#define WWU_TOUCHED 0x01 /* touched */
+
+ /* the window structures */
+struct ww wwhead;
+struct ww *wwindex[NWW + 1]; /* last location is for wwnobody */
+struct ww wwnobody;
+
+ /* tty things */
+struct ww_tty wwoldtty; /* the old (saved) terminal settings */
+struct ww_tty wwnewtty; /* the new (current) terminal settings */
+struct ww_tty wwwintty; /* the terminal settings for windows */
+char *wwterm; /* the terminal name */
+char wwtermcap[1024]; /* place for the termcap */
+
+ /* generally useful variables */
+int wwnrow, wwncol; /* the screen size */
+char wwavailmodes; /* actually supported modes */
+char wwcursormodes; /* the modes for the fake cursor */
+char wwwrap; /* terminal has auto wrap around */
+int wwdtablesize; /* result of getdtablesize() call */
+char **wwsmap; /* the screen map */
+union ww_char **wwos; /* the old (current) screen */
+union ww_char **wwns; /* the new (desired) screen */
+union ww_char **wwcs; /* the checkpointed screen */
+char *wwtouched; /* wwns changed flags */
+struct ww_update *wwupd; /* for display update */
+int wwospeed; /* output baud rate, copied from wwoldtty */
+int wwbaud; /* wwospeed converted into actual number */
+int wwcursorrow, wwcursorcol; /* where we want the cursor to be */
+int wwerrno; /* error number */
+
+ /* statistics */
+int wwnflush, wwnwr, wwnwre, wwnwrz, wwnwrc;
+int wwnwwr, wwnwwra, wwnwwrc;
+int wwntokdef, wwntokuse, wwntokbad, wwntoksave, wwntokc;
+int wwnupdate, wwnupdline, wwnupdmiss;
+int wwnupdscan, wwnupdclreol, wwnupdclreos, wwnupdclreosmiss, wwnupdclreosline;
+int wwnread, wwnreade, wwnreadz;
+int wwnreadc, wwnreadack, wwnreadnack, wwnreadstat, wwnreadec;
+int wwnwread, wwnwreade, wwnwreadz, wwnwreadd, wwnwreadc, wwnwreadp;
+int wwnselect, wwnselecte, wwnselectz;
+
+ /* quicky macros */
+#define wwsetcursor(r,c) (wwcursorrow = (r), wwcursorcol = (c))
+#define wwcurtowin(w) wwsetcursor((w)->ww_cur.r, (w)->ww_cur.c)
+#define wwunbox(w) wwunframe(w)
+#define wwclreol(w,r,c) wwclreol1((w), (r), (c), 0)
+#define wwredrawwin(w) wwredrawwin1((w), (w)->ww_i.t, (w)->ww_i.b, 0)
+#define wwupdate() wwupdate1(0, wwnrow);
+
+ /* things for handling input */
+void wwrint(); /* interrupt handler */
+struct ww *wwcurwin; /* window to copy input into */
+char *wwib; /* input (keyboard) buffer */
+char *wwibe; /* wwib + sizeof buffer */
+char *wwibp; /* current read position in buffer */
+char *wwibq; /* current write position in buffer */
+#define wwmaskc(c) ((c) & 0xff)
+#define wwgetc() (wwibp < wwibq ? wwmaskc(*wwibp++) : -1)
+#define wwpeekc() (wwibp < wwibq ? wwmaskc(*wwibp) : -1)
+#define wwungetc(c) (wwibp > wwib ? *--wwibp = (c) : -1)
+
+ /* things for short circuiting wwiomux() */
+char wwintr; /* interrupting */
+char wwsetjmp; /* want a longjmp() from wwrint() and wwchild() */
+jmp_buf wwjmpbuf; /* jmpbuf for above */
+#define wwinterrupt() wwintr
+#define wwsetintr() do { wwintr = 1; if (wwsetjmp) longjmp(wwjmpbuf, 1); } \
+ while (0)
+#define wwclrintr() (wwintr = 0)
+
+ /* checkpointing */
+int wwdocheckpoint;
+
+ /* the window virtual terminal */
+#define WWT_TERM "window-v2"
+#define WWT_TERMCAP "WW|window-v2|window program version 2:\
+ :am:bs:da:db:ms:pt:cr=^M:nl=^J:bl=^G:ta=^I:\
+ :cm=\\EY%+ %+ :le=^H:nd=\\EC:up=\\EA:do=\\EB:ho=\\EH:\
+ :cd=\\EJ:ce=\\EK:cl=\\EE:me=\\Er^?:"
+#define WWT_REV "se=\\ErA:so=\\EsA:mr=\\EsA:"
+#define WWT_BLK "BE=\\ErB:BS=\\EsB:mb=\\EsB:"
+#define WWT_UL "ue=\\ErD:us=\\EsD:"
+#define WWT_GRP "ae=\\ErH:as=\\EsH:"
+#define WWT_DIM "HE=\\ErP:HS=\\EsP:mh=\\EsP:"
+#define WWT_USR "XE=\\Er`:XS=\\Es`:"
+#define WWT_ALDL "al=\\EL:dl=\\EM:"
+#define WWT_IMEI "im=\\E@:ei=\\EO:ic=:mi:" /* XXX, ic for emacs bug */
+#define WWT_IC "ic=\\EP:"
+#define WWT_DC "dc=\\EN:"
+char wwwintermcap[1024]; /* terminal-specific but window-independent
+ part of the window termcap */
+#ifdef TERMINFO
+ /* where to put the temporary terminfo directory */
+char wwterminfopath[1024];
+#endif
+
+ /* our functions */
+struct ww *wwopen();
+void wwchild();
+void wwalarm();
+void wwquit();
+char **wwalloc();
+char *wwerror();
+
+ /* c library functions */
+char *getenv();
+char *tgetstr();
+char *rindex();
+char *strcpy();
+char *strcat();
+
+#undef MIN
+#undef MAX
+#define MIN(x, y) ((x) > (y) ? (y) : (x))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
diff --git a/usr.bin/window/wwadd.c b/usr.bin/window/wwadd.c
new file mode 100644
index 0000000..377464a
--- /dev/null
+++ b/usr.bin/window/wwadd.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwadd.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+/*
+ * Stick w1 behind w2.
+ */
+wwadd(w1, w2)
+register struct ww *w1;
+struct ww *w2;
+{
+ register i;
+ register struct ww *w;
+
+ w1->ww_order = w2->ww_order + 1;
+ w1->ww_back = w2;
+ w1->ww_forw = w2->ww_forw;
+ w2->ww_forw->ww_back = w1;
+ w2->ww_forw = w1;
+
+ for (w = w1->ww_forw; w != &wwhead; w = w->ww_forw)
+ w->ww_order++;
+ for (i = w1->ww_i.t; i < w1->ww_i.b; i++) {
+ register j;
+ register char *smap = wwsmap[i];
+ register char *win = w1->ww_win[i];
+ union ww_char *ns = wwns[i];
+ union ww_char *buf = w1->ww_buf[i];
+ int nvis = 0;
+ int nchanged = 0;
+
+ for (j = w1->ww_i.l; j < w1->ww_i.r; j++) {
+ w = wwindex[smap[j]];
+ if (w1->ww_order > w->ww_order)
+ continue;
+ if (win[j] & WWM_GLS)
+ continue;
+ if (w != &wwnobody && w->ww_win[i][j] == 0)
+ w->ww_nvis[i]--;
+ smap[j] = w1->ww_index;
+ if (win[j] == 0)
+ nvis++;
+ ns[j].c_w = buf[j].c_w ^ win[j] << WWC_MSHIFT;
+ nchanged++;
+ }
+ if (nchanged > 0)
+ wwtouched[i] |= WWU_TOUCHED;
+ w1->ww_nvis[i] = nvis;
+ }
+}
diff --git a/usr.bin/window/wwalloc.c b/usr.bin/window/wwalloc.c
new file mode 100644
index 0000000..009bf12
--- /dev/null
+++ b/usr.bin/window/wwalloc.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwalloc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+char **
+wwalloc(row, col, nrow, ncol, size)
+{
+ register char *p, **pp;
+ register int i;
+
+ /* fast, call malloc only once */
+ pp = (char **)
+ malloc((unsigned) sizeof (char **) * nrow + size * nrow * ncol);
+ if (pp == 0) {
+ wwerrno = WWE_NOMEM;
+ return 0;
+ }
+ p = (char *)&pp[nrow];
+ col *= size;
+ size /= sizeof (char); /* paranoid */
+ size *= ncol;
+ for (i = 0; i < nrow; i++) {
+ pp[i] = p - col;
+ p += size;
+ }
+ return pp - row;
+}
+
+wwfree(p, row)
+register char **p;
+{
+ free((char *)(p + row));
+}
diff --git a/usr.bin/window/wwbox.c b/usr.bin/window/wwbox.c
new file mode 100644
index 0000000..f4dd52b
--- /dev/null
+++ b/usr.bin/window/wwbox.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwbox.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwbox(w, r, c, nr, nc)
+register struct ww *w;
+register r, c;
+int nr, nc;
+{
+ register r1, c1;
+ register i;
+
+ r1 = r + nr - 1;
+ c1 = c + nc - 1;
+ wwframec(w, r, c, WWF_D|WWF_R);
+ for (i = c + 1; i < c1; i++)
+ wwframec(w, r, i, WWF_L|WWF_R);
+ wwframec(w, r, i, WWF_L|WWF_D);
+ for (i = r + 1; i < r1; i++)
+ wwframec(w, i, c1, WWF_U|WWF_D);
+ wwframec(w, i, c1, WWF_U|WWF_L);
+ for (i = c1 - 1; i > c; i--)
+ wwframec(w, r1, i, WWF_R|WWF_L);
+ wwframec(w, r1, i, WWF_R|WWF_U);
+ for (i = r1 - 1; i > r; i--)
+ wwframec(w, i, c, WWF_D|WWF_U);
+}
diff --git a/usr.bin/window/wwchild.c b/usr.bin/window/wwchild.c
new file mode 100644
index 0000000..944bdd9
--- /dev/null
+++ b/usr.bin/window/wwchild.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwchild.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void
+wwchild()
+{
+ extern errno;
+ int olderrno;
+ register struct ww **wp;
+ union wait w;
+ int pid;
+ char collected = 0;
+
+ olderrno = errno;
+ while ((pid =
+ wait3((int *)&w, WNOHANG|WUNTRACED, (struct rusage *)0)) > 0) {
+ for (wp = wwindex; wp < &wwindex[NWW]; wp++) {
+ if (*wp && (*wp)->ww_state == WWS_HASPROC
+ && (*wp)->ww_pid == pid) {
+ (*wp)->ww_state = WWS_DEAD;
+ collected = 1;
+ break;
+ }
+ }
+ }
+ errno = olderrno;
+ /* jump out of wwiomux when somebody dies */
+ if (collected)
+ wwsetintr();
+}
diff --git a/usr.bin/window/wwclose.c b/usr.bin/window/wwclose.c
new file mode 100644
index 0000000..a27aed9
--- /dev/null
+++ b/usr.bin/window/wwclose.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwclose.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwclose(w)
+register struct ww *w;
+{
+ wwindex[w->ww_index] = 0;
+ if (w->ww_pty >= 0)
+ (void) close(w->ww_pty);
+ if (w->ww_socket >= 0)
+ (void) close(w->ww_socket);
+ wwfree((char **)w->ww_win, w->ww_w.t);
+ wwfree((char **)w->ww_buf, w->ww_b.t);
+ if (w->ww_fmap != 0)
+ wwfree((char **)w->ww_fmap, w->ww_w.t);
+ free((char *)(w->ww_nvis + w->ww_w.t));
+ if (w->ww_ob != 0)
+ free(w->ww_ob);
+ free((char *)w);
+}
diff --git a/usr.bin/window/wwclreol.c b/usr.bin/window/wwclreol.c
new file mode 100644
index 0000000..5f3f64a
--- /dev/null
+++ b/usr.bin/window/wwclreol.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwclreol.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+/*
+ * Clear w to the end of line.
+ * If cleared is true, then the screen line has already been cleared.
+ */
+wwclreol1(w, row, col, cleared)
+register struct ww *w;
+int row, col;
+char cleared;
+{
+ register i;
+
+ /*
+ * Clear the buffer right off
+ */
+ {
+ register union ww_char *buf;
+
+ buf = &w->ww_buf[row][col];
+ for (i = w->ww_b.r - col; --i >= 0;)
+ buf++->c_w = ' ';
+ }
+
+ /*
+ * If can't see it, just return.
+ */
+ if (row < w->ww_i.t || row >= w->ww_i.b
+ || w->ww_i.r <= 0 || w->ww_i.r <= col)
+ return;
+
+ if (col < w->ww_i.l)
+ col = w->ww_i.l;
+
+ /*
+ * Now fix wwns.
+ */
+ {
+ register union ww_char *s;
+ register char *smap, *win;
+
+ i = col;
+ smap = &wwsmap[row][i];
+ s = &wwns[row][i];
+ win = &w->ww_win[row][i];
+ for (i = w->ww_i.r - i; --i >= 0;)
+ if (*smap++ == w->ww_index)
+ s++->c_w = ' ' | *win++ << WWC_MSHIFT;
+ else
+ s++, win++;
+ }
+ if (!cleared)
+ wwtouched[row] |= WWU_TOUCHED;
+}
diff --git a/usr.bin/window/wwclreos.c b/usr.bin/window/wwclreos.c
new file mode 100644
index 0000000..f3d462c
--- /dev/null
+++ b/usr.bin/window/wwclreos.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwclreos.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwclreos(w, row, col)
+register struct ww *w;
+{
+ register i;
+
+ wwclreol(w, row, col);
+ for (i = row + 1; i < w->ww_b.b; i++)
+ wwclreol(w, i, w->ww_b.l);
+ /* XXX */
+ if (!w->ww_noupdate)
+ wwupdate1(w->ww_i.t, w->ww_i.b);
+}
diff --git a/usr.bin/window/wwcursor.c b/usr.bin/window/wwcursor.c
new file mode 100644
index 0000000..cd9cdba
--- /dev/null
+++ b/usr.bin/window/wwcursor.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwcursor.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwcursor(w, on)
+register struct ww *w;
+{
+ register char *win;
+
+ if (on) {
+ if (w->ww_hascursor)
+ return;
+ w->ww_hascursor = 1;
+ } else {
+ if (!w->ww_hascursor)
+ return;
+ w->ww_hascursor = 0;
+ }
+ if (wwcursormodes != 0) {
+ win = &w->ww_win[w->ww_cur.r][w->ww_cur.c];
+ *win ^= wwcursormodes;
+ if (w->ww_cur.r < w->ww_i.t || w->ww_cur.r >= w->ww_i.b
+ || w->ww_cur.c < w->ww_i.l || w->ww_cur.c >= w->ww_i.r)
+ return;
+ if (wwsmap[w->ww_cur.r][w->ww_cur.c] == w->ww_index) {
+ if (*win == 0)
+ w->ww_nvis[w->ww_cur.r]++;
+ else if (*win == wwcursormodes)
+ w->ww_nvis[w->ww_cur.r]--;
+ wwns[w->ww_cur.r][w->ww_cur.c].c_m ^= wwcursormodes;
+ wwtouched[w->ww_cur.r] |= WWU_TOUCHED;
+ }
+ }
+}
+
+wwsetcursormodes(new)
+register new;
+{
+ register i;
+ register struct ww *w;
+ register old = wwcursormodes;
+
+ new &= wwavailmodes;
+ if (new == wwcursormodes)
+ return;
+ for (i = 0; i < NWW; i++)
+ if (wwindex[i] != 0 && (w = wwindex[i])->ww_hascursor) {
+ wwcursor(w, 0);
+ wwcursormodes = new;
+ wwcursor(w, 1);
+ wwcursormodes = old;
+ }
+ wwcursormodes = new;
+}
diff --git a/usr.bin/window/wwdata.c b/usr.bin/window/wwdata.c
new file mode 100644
index 0000000..1280589
--- /dev/null
+++ b/usr.bin/window/wwdata.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwdata.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
diff --git a/usr.bin/window/wwdelchar.c b/usr.bin/window/wwdelchar.c
new file mode 100644
index 0000000..cd58833
--- /dev/null
+++ b/usr.bin/window/wwdelchar.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwdelchar.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwdelchar(w, row, col)
+register struct ww *w;
+{
+ register i;
+ int nvis;
+
+ /*
+ * First, shift the line.
+ */
+ {
+ register union ww_char *p, *q;
+
+ p = &w->ww_buf[row][col];
+ q = p + 1;
+ for (i = w->ww_b.r - col; --i > 0;)
+ *p++ = *q++;
+ p->c_w = ' ';
+ }
+
+ /*
+ * If can't see it, just return.
+ */
+ if (row < w->ww_i.t || row >= w->ww_i.b
+ || w->ww_i.r <= 0 || w->ww_i.r <= col)
+ return;
+
+ if (col < w->ww_i.l)
+ col = w->ww_i.l;
+
+ /*
+ * Now find out how much is actually changed, and fix wwns.
+ */
+ {
+ register union ww_char *buf;
+ register char *win;
+ register union ww_char *ns;
+ register char *smap;
+ char touched;
+
+ nvis = 0;
+ smap = &wwsmap[row][col];
+ for (i = col; i < w->ww_i.r && *smap++ != w->ww_index; i++)
+ ;
+ if (i >= w->ww_i.r)
+ return;
+ col = i;
+ buf = w->ww_buf[row];
+ win = w->ww_win[row];
+ ns = wwns[row];
+ smap = &wwsmap[row][i];
+ touched = wwtouched[row];
+ for (; i < w->ww_i.r; i++) {
+ if (*smap++ != w->ww_index)
+ continue;
+ touched |= WWU_TOUCHED;
+ if (win[i])
+ ns[i].c_w =
+ buf[i].c_w ^ win[i] << WWC_MSHIFT;
+ else {
+ nvis++;
+ ns[i] = buf[i];
+ }
+ }
+ wwtouched[row] = touched;
+ }
+
+ /*
+ * Can/Should we use delete character?
+ */
+ if (tt.tt_delchar != 0 && nvis > (wwncol - col) / 2) {
+ register union ww_char *p, *q;
+
+ xxdelchar(row, col);
+ p = &wwos[row][col];
+ q = p + 1;
+ for (i = wwncol - col; --i > 0;)
+ *p++ = *q++;
+ p->c_w = ' ';
+ }
+}
diff --git a/usr.bin/window/wwdelete.c b/usr.bin/window/wwdelete.c
new file mode 100644
index 0000000..f659736
--- /dev/null
+++ b/usr.bin/window/wwdelete.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwdelete.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+/*
+ * Pull w free from the cover list.
+ */
+wwdelete(w)
+register struct ww *w;
+{
+ register i;
+
+ for (i = w->ww_i.t; i < w->ww_i.b; i++) {
+ register j;
+ register char *smap = wwsmap[i];
+ register union ww_char *ns = wwns[i];
+ register int nchanged = 0;
+
+ for (j = w->ww_i.l; j < w->ww_i.r; j++)
+ if (smap[j] == w->ww_index) {
+ smap[j] = WWX_NOBODY;
+ ns[j].c_w = ' ';
+ nchanged++;
+ }
+ if (nchanged > 0)
+ wwtouched[i] |= WWU_TOUCHED;
+ }
+
+ {
+ register struct ww *wp;
+
+ for (wp = w->ww_forw; wp != &wwhead; wp = wp->ww_forw)
+ wp->ww_order--;
+ }
+
+ if (w->ww_forw != &wwhead)
+ wwdelete1(w->ww_forw,
+ w->ww_i.t, w->ww_i.b, w->ww_i.l, w->ww_i.r);
+
+ w->ww_back->ww_forw = w->ww_forw;
+ w->ww_forw->ww_back = w->ww_back;
+ w->ww_forw = w->ww_back = 0;
+}
+
+wwdelete1(w, t, b, l, r)
+register struct ww *w;
+{
+ int i;
+ int tt, bb, ll, rr;
+ char hasglass;
+
+again:
+ hasglass = 0;
+ tt = MAX(t, w->ww_i.t);
+ bb = MIN(b, w->ww_i.b);
+ ll = MAX(l, w->ww_i.l);
+ rr = MIN(r, w->ww_i.r);
+ if (tt >= bb || ll >= rr) {
+ if ((w = w->ww_forw) == &wwhead)
+ return;
+ goto again;
+ }
+ for (i = tt; i < bb; i++) {
+ register j;
+ register char *smap = wwsmap[i];
+ register union ww_char *ns = wwns[i];
+ register char *win = w->ww_win[i];
+ register union ww_char *buf = w->ww_buf[i];
+ int nvis = w->ww_nvis[i];
+ int nchanged = 0;
+
+ for (j = ll; j < rr; j++) {
+ if (smap[j] != WWX_NOBODY)
+ continue;
+ if (win[j] & WWM_GLS) {
+ hasglass = 1;
+ continue;
+ }
+ smap[j] = w->ww_index;
+ ns[j].c_w = buf[j].c_w ^ win[j] << WWC_MSHIFT;
+ nchanged++;
+ if (win[j] == 0)
+ nvis++;
+ }
+ if (nchanged > 0)
+ wwtouched[i] |= WWU_TOUCHED;
+ w->ww_nvis[i] = nvis;
+ }
+ if ((w = w->ww_forw) == &wwhead)
+ return;
+ if (hasglass)
+ goto again;
+ if (tt > t)
+ wwdelete1(w, t, tt, l, r);
+ if (bb < b)
+ wwdelete1(w, bb, b, l, r);
+ if (ll > l)
+ wwdelete1(w, tt, bb, l, ll);
+ if (rr < r)
+ wwdelete1(w, tt, bb, rr, r);
+}
diff --git a/usr.bin/window/wwdelline.c b/usr.bin/window/wwdelline.c
new file mode 100644
index 0000000..f2aef72
--- /dev/null
+++ b/usr.bin/window/wwdelline.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwdelline.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwdelline(w, row)
+register struct ww *w;
+int row;
+{
+ register i;
+ register union ww_char **cpp, **cqq;
+ register union ww_char *cp;
+ int row1, row2;
+ char deleted;
+ int visible;
+
+ /*
+ * Scroll first.
+ */
+ if ((row1 = row) < w->ww_i.t) {
+ row1 = w->ww_i.t;
+ }
+ if ((row2 = w->ww_b.b) > w->ww_i.b) {
+ row2 = w->ww_i.b;
+ visible = 0;
+ } else
+ visible = 1;
+ deleted = wwscroll1(w, row1, row2, 1, visible);
+
+ /*
+ * Fix the buffer.
+ * But leave clearing the last line for wwclreol().
+ */
+ cpp = &w->ww_buf[row];
+ cqq = cpp + 1;
+ cp = *cpp;
+ for (i = w->ww_b.b - row; --i > 0;)
+ *cpp++ = *cqq++;
+ *cpp = cp;
+
+ /*
+ * Now clear the last line.
+ */
+ if (visible)
+ wwclreol1(w, w->ww_b.b - 1, w->ww_b.l, deleted);
+ else {
+ cp += w->ww_b.l;
+ for (i = w->ww_b.nc; --i >= 0;)
+ cp++->c_w = ' ';
+ }
+}
diff --git a/usr.bin/window/wwdump.c b/usr.bin/window/wwdump.c
new file mode 100644
index 0000000..8f60d4e
--- /dev/null
+++ b/usr.bin/window/wwdump.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwdump.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+static char cmap[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+wwdumpwin(w)
+register struct ww *w;
+{
+ register i, j;
+
+ tt.tt_nmodes = 0;
+ (*tt.tt_clear)();
+ for (i = w->ww_i.t; i < w->ww_i.b; i++) {
+ (*tt.tt_move)(i, w->ww_i.l);
+ for (j = w->ww_i.l; j < w->ww_i.r; j++)
+ (*tt.tt_putc)(w->ww_win[i][j] & WWM_GLS ? 'G' : ' ');
+ }
+}
+
+wwdumpnvis(w)
+register struct ww *w;
+{
+ register i;
+ char buf[20];
+
+ tt.tt_nmodes = 0;
+ (*tt.tt_clear)();
+ for (i = w->ww_i.t; i < w->ww_i.b; i++) {
+ (*tt.tt_move)(i, w->ww_i.l);
+ (void) sprintf(buf, "%d", w->ww_nvis[i]);
+ (*tt.tt_write)(buf, strlen(buf));
+ }
+}
+
+wwdumpsmap()
+{
+ register i, j;
+
+ tt.tt_nmodes = 0;
+ (*tt.tt_clear)();
+ for (i = 0; i < wwnrow; i++) {
+ (*tt.tt_move)(i, 0);
+ for (j = 0; j < wwncol; j++)
+ (*tt.tt_putc)(cmap[wwsmap[i][j]]);
+ }
+}
+
+wwdumpns()
+{
+ register i, j;
+
+ (*tt.tt_clear)();
+ for (i = 0; i < wwnrow; i++) {
+ (*tt.tt_move)(i, 0);
+ for (j = 0; j < wwncol; j++) {
+ tt.tt_nmodes = wwns[i][j].c_m & tt.tt_availmodes;
+ (*tt.tt_putc)(wwns[i][j].c_c);
+ }
+ }
+}
+
+wwdumpos()
+{
+ register i, j;
+
+ (*tt.tt_clear)();
+ for (i = 0; i < wwnrow; i++) {
+ (*tt.tt_move)(i, 0);
+ for (j = 0; j < wwncol; j++) {
+ tt.tt_nmodes = wwos[i][j].c_m & tt.tt_availmodes;
+ (*tt.tt_putc)(wwns[i][j].c_c);
+ }
+ }
+}
diff --git a/usr.bin/window/wwend.c b/usr.bin/window/wwend.c
new file mode 100644
index 0000000..b984d2d
--- /dev/null
+++ b/usr.bin/window/wwend.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwend.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <signal.h>
+#include "ww.h"
+#include "tt.h"
+
+/*ARGSUSED*/
+wwend(exit)
+{
+ if (tt.tt_checkpoint) {
+ (void) alarm(0);
+ wwdocheckpoint = 0;
+ }
+ xxend();
+ (void) signal(SIGIO, SIG_DFL);
+ (void) wwsettty(0, &wwoldtty);
+#ifdef TERMINFO
+ if (exit)
+ wwterminfoend();
+#endif
+}
+
+void
+wwquit()
+{
+ wwend(1);
+ exit(1);
+}
diff --git a/usr.bin/window/wwenviron.c b/usr.bin/window/wwenviron.c
new file mode 100644
index 0000000..2b5d82c
--- /dev/null
+++ b/usr.bin/window/wwenviron.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwenviron.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#if !defined(OLD_TTY) && !defined(TIOCSCTTY) && !defined(TIOCNOTTY)
+#include <sys/ioctl.h>
+#endif
+#include <sys/signal.h>
+
+/*
+ * Set up the environment of this process to run in window 'wp'.
+ */
+wwenviron(wp)
+register struct ww *wp;
+{
+ register i;
+#ifndef TIOCSCTTY
+ int pgrp = getpid();
+#endif
+ char buf[1024];
+
+#ifndef TIOCSCTTY
+ if ((i = open("/dev/tty", 0)) < 0)
+ goto bad;
+ if (ioctl(i, TIOCNOTTY, (char *)0) < 0)
+ goto bad;
+ (void) close(i);
+#endif
+ if ((i = wp->ww_socket) < 0) {
+ if ((i = open(wp->ww_ttyname, 2)) < 0)
+ goto bad;
+ if (wwsettty(i, &wwwintty) < 0)
+ goto bad;
+ if (wwsetttysize(i, wp->ww_w.nr, wp->ww_w.nc) < 0)
+ goto bad;
+ }
+ (void) dup2(i, 0);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ (void) close(i);
+#ifdef TIOCSCTTY
+ (void) setsid();
+ (void) ioctl(0, TIOCSCTTY, 0);
+#else
+ (void) ioctl(0, TIOCSPGRP, (char *)&pgrp);
+ (void) setpgrp(pgrp, pgrp);
+#endif
+ /* SIGPIPE is the only one we ignore */
+ (void) signal(SIGPIPE, SIG_DFL);
+ (void) sigsetmask(0);
+ /*
+ * Two conditions that make destructive setenv ok:
+ * 1. setenv() copies the string,
+ * 2. we've already called tgetent which copies the termcap entry.
+ */
+ (void) sprintf(buf, "%sco#%d:li#%d:%s",
+ WWT_TERMCAP, wp->ww_w.nc, wp->ww_w.nr, wwwintermcap);
+ (void) setenv("TERMCAP", buf, 1);
+ (void) sprintf(buf, "%d", wp->ww_id + 1);
+ (void) setenv("WINDOW_ID", buf, 1);
+ return 0;
+bad:
+ wwerrno = WWE_SYS;
+ return -1;
+}
diff --git a/usr.bin/window/wwerror.c b/usr.bin/window/wwerror.c
new file mode 100644
index 0000000..0bba6f5
--- /dev/null
+++ b/usr.bin/window/wwerror.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwerror.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+char *
+wwerror()
+{
+ extern int errno;
+ char *strerror();
+
+ switch (wwerrno) {
+ case WWE_NOERR:
+ return "No error";
+ case WWE_SYS:
+ return strerror(errno);
+ case WWE_NOMEM:
+ return "Out of memory";
+ case WWE_TOOMANY:
+ return "Too many windows";
+ case WWE_NOPTY:
+ return "Out of pseudo-terminals";
+ case WWE_SIZE:
+ return "Bad window size";
+ case WWE_BADTERM:
+ return "Unknown terminal type";
+ case WWE_CANTDO:
+ return "Can't run window on this terminal";
+ default:
+ return "Unknown error";
+ }
+}
diff --git a/usr.bin/window/wwflush.c b/usr.bin/window/wwflush.c
new file mode 100644
index 0000000..1346e56
--- /dev/null
+++ b/usr.bin/window/wwflush.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwflush.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include <sys/signal.h>
+
+wwflush()
+{
+ register row, col;
+
+ if ((row = wwcursorrow) < 0)
+ row = 0;
+ else if (row >= wwnrow)
+ row = wwnrow - 1;
+ if ((col = wwcursorcol) < 0)
+ col = 0;
+ else if (col >= wwncol)
+ col = wwncol - 1;
+ xxmove(row, col);
+ if (wwdocheckpoint) {
+ xxflush(0);
+ wwcheckpoint();
+ } else
+ xxflush(1);
+}
+
+wwcheckpoint()
+{
+ int s = sigblock(sigmask(SIGALRM) | sigmask(SIGIO));
+
+ tt.tt_ack = 0;
+ do {
+ (*tt.tt_checkpoint)();
+#ifndef OLD_TTY
+ (void) tcdrain(1);
+#endif
+ (void) alarm(3);
+ for (wwdocheckpoint = 0; !wwdocheckpoint && tt.tt_ack == 0;)
+ (void) sigpause(s);
+ } while (tt.tt_ack == 0);
+ (void) alarm(0);
+ wwdocheckpoint = 0;
+ if (tt.tt_ack < 0) {
+ wwcopyscreen(wwcs, wwos);
+ (void) alarm(1);
+ wwreset();
+ wwupdate();
+ wwflush();
+ } else {
+ wwcopyscreen(wwos, wwcs);
+ (void) alarm(3);
+ }
+ (void) sigsetmask(s);
+}
+
+wwcopyscreen(s1, s2)
+ register union ww_char **s1, **s2;
+{
+ register i;
+ register s = wwncol * sizeof **s1;
+
+ for (i = wwnrow; --i >= 0;)
+ bcopy((char *) *s1++, (char *) *s2++, s);
+}
+
+void
+wwalarm()
+{
+ wwdocheckpoint = 1;
+}
diff --git a/usr.bin/window/wwframe.c b/usr.bin/window/wwframe.c
new file mode 100644
index 0000000..bb0badd
--- /dev/null
+++ b/usr.bin/window/wwframe.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwframe.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+#define frameok(w, r, c) (w1 = wwindex[wwsmap[r][c]], \
+ w1->ww_fmap || w1->ww_order > (w)->ww_order)
+
+wwframe(w, wframe)
+register struct ww *w;
+struct ww *wframe;
+{
+ register r, c;
+ char a1, a2, a3;
+ char b1, b2, b3;
+ register char *smap;
+ register code;
+ register struct ww *w1;
+
+ if (w->ww_w.t > 0) {
+ r = w->ww_w.t - 1;
+ c = w->ww_i.l - 1;
+ smap = &wwsmap[r + 1][c + 1];
+ a1 = 0;
+ a2 = 0;
+ b1 = 0;
+ b2 = c < 0 || frameok(w, r, c);
+
+ for (; c < w->ww_i.r; c++) {
+ if (c + 1 >= wwncol) {
+ a3 = 1;
+ b3 = 1;
+ } else {
+ a3 = w->ww_index == *smap++;
+ b3 = frameok(w, r, c + 1);
+ }
+ if (b2) {
+ code = 0;
+ if ((a1 || a2) && b1)
+ code |= WWF_L;
+ if ((a2 || a3) && b3)
+ code |= WWF_R;
+ if (code)
+ wwframec(wframe, r, c, code|WWF_TOP);
+ }
+ a1 = a2;
+ a2 = a3;
+ b1 = b2;
+ b2 = b3;
+ }
+ if ((a1 || a2) && b1 && b2)
+ wwframec(wframe, r, c, WWF_L|WWF_TOP);
+ }
+
+ if (w->ww_w.b < wwnrow) {
+ r = w->ww_w.b;
+ c = w->ww_i.l - 1;
+ smap = &wwsmap[r - 1][c + 1];
+ a1 = 0;
+ a2 = 0;
+ b1 = 0;
+ b2 = c < 0 || frameok(w, r, c);
+
+ for (; c < w->ww_i.r; c++) {
+ if (c + 1 >= wwncol) {
+ a3 = 1;
+ b3 = 1;
+ } else {
+ a3 = w->ww_index == *smap++;
+ b3 = frameok(w, r, c + 1);
+ }
+ if (b2) {
+ code = 0;
+ if ((a1 || a2) && b1)
+ code |= WWF_L;
+ if ((a2 || a3) && b3)
+ code |= WWF_R;
+ if (code)
+ wwframec(wframe, r, c, code);
+ }
+ a1 = a2;
+ a2 = a3;
+ b1 = b2;
+ b2 = b3;
+ }
+ if ((a1 || a2) && b1 && b2)
+ wwframec(wframe, r, c, WWF_L);
+ }
+
+ if (w->ww_w.l > 0) {
+ r = w->ww_i.t - 1;
+ c = w->ww_w.l - 1;
+ a1 = 0;
+ a2 = 0;
+ b1 = 0;
+ b2 = r < 0 || frameok(w, r, c);
+
+ for (; r < w->ww_i.b; r++) {
+ if (r + 1 >= wwnrow) {
+ a3 = 1;
+ b3 = 1;
+ } else {
+ a3 = w->ww_index == wwsmap[r + 1][c + 1];
+ b3 = frameok(w, r + 1, c);
+ }
+ if (b2) {
+ code = 0;
+ if ((a1 || a2) && b1)
+ code |= WWF_U;
+ if ((a2 || a3) && b3)
+ code |= WWF_D;
+ if (code)
+ wwframec(wframe, r, c, code);
+ }
+ a1 = a2;
+ a2 = a3;
+ b1 = b2;
+ b2 = b3;
+ }
+ if ((a1 || a2) && b1 && b2)
+ wwframec(wframe, r, c, WWF_U);
+ }
+
+ if (w->ww_w.r < wwncol) {
+ r = w->ww_i.t - 1;
+ c = w->ww_w.r;
+ a1 = 0;
+ a2 = 0;
+ b1 = 0;
+ b2 = r < 0 || frameok(w, r, c);
+
+ for (; r < w->ww_i.b; r++) {
+ if (r + 1 >= wwnrow) {
+ a3 = 1;
+ b3 = 1;
+ } else {
+ a3 = w->ww_index == wwsmap[r + 1][c - 1];
+ b3 = frameok(w, r + 1, c);
+ }
+ if (b2) {
+ code = 0;
+ if ((a1 || a2) && b1)
+ code |= WWF_U;
+ if ((a2 || a3) && b3)
+ code |= WWF_D;
+ if (code)
+ wwframec(wframe, r, c, code);
+ }
+ a1 = a2;
+ a2 = a3;
+ b1 = b2;
+ b2 = b3;
+ }
+ if ((a1 || a2) && b1 && b2)
+ wwframec(wframe, r, c, WWF_U);
+ }
+}
+
+wwframec(f, r, c, code)
+register struct ww *f;
+register r, c;
+char code;
+{
+ char oldcode;
+ register char *smap;
+
+ if (r < f->ww_i.t || r >= f->ww_i.b || c < f->ww_i.l || c >= f->ww_i.r)
+ return;
+
+ smap = &wwsmap[r][c];
+
+ {
+ register struct ww *w;
+
+ w = wwindex[*smap];
+ if (w->ww_order > f->ww_order) {
+ if (w != &wwnobody && w->ww_win[r][c] == 0)
+ w->ww_nvis[r]--;
+ *smap = f->ww_index;
+ }
+ }
+
+ if (f->ww_fmap != 0) {
+ register char *fmap;
+
+ fmap = &f->ww_fmap[r][c];
+ oldcode = *fmap;
+ *fmap |= code;
+ if (code & WWF_TOP)
+ *fmap &= ~WWF_LABEL;
+ code = *fmap;
+ } else
+ oldcode = 0;
+ {
+ register char *win = &f->ww_win[r][c];
+
+ if (*win == WWM_GLS && *smap == f->ww_index)
+ f->ww_nvis[r]++;
+ *win &= ~WWM_GLS;
+ }
+ if (oldcode != code && (code & WWF_LABEL) == 0) {
+ register short frame;
+
+ frame = tt.tt_frame[code & WWF_MASK];
+ f->ww_buf[r][c].c_w = frame;
+ if (wwsmap[r][c] == f->ww_index) {
+ wwtouched[r] |= WWU_TOUCHED;
+ wwns[r][c].c_w = frame;
+ }
+ }
+}
diff --git a/usr.bin/window/wwgets.c b/usr.bin/window/wwgets.c
new file mode 100644
index 0000000..a7223b7
--- /dev/null
+++ b/usr.bin/window/wwgets.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwgets.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "char.h"
+
+wwgets(buf, n, w)
+char *buf;
+int n;
+register struct ww *w;
+{
+ register char *p = buf;
+ register int c;
+ char uc = w->ww_unctrl;
+ static void rub();
+
+ w->ww_unctrl = 0;
+ for (;;) {
+ wwcurtowin(w);
+ while ((c = wwgetc()) < 0)
+ wwiomux();
+#ifdef OLD_TTY
+ if (c == wwoldtty.ww_sgttyb.sg_erase)
+#else
+ if (c == wwoldtty.ww_termios.c_cc[VERASE])
+#endif
+ {
+ if (p > buf)
+ rub(*--p, w);
+ } else
+#ifdef OLD_TTY
+ if (c == wwoldtty.ww_sgttyb.sg_kill)
+#else
+ if (c == wwoldtty.ww_termios.c_cc[VKILL])
+#endif
+ {
+ while (p > buf)
+ rub(*--p, w);
+ } else
+#ifdef OLD_TTY
+ if (c == wwoldtty.ww_ltchars.t_werasc)
+#else
+ if (c == wwoldtty.ww_termios.c_cc[VWERASE])
+#endif
+ {
+ while (--p >= buf && (*p == ' ' || *p == '\t'))
+ rub(*p, w);
+ while (p >= buf && *p != ' ' && *p != '\t')
+ rub(*p--, w);
+ p++;
+ } else if (c == '\r' || c == '\n') {
+ break;
+ } else {
+ if (p >= buf + n - 1)
+ wwputc(ctrl('g'), w);
+ else
+ if (isctrl(c))
+ wwputs(unctrl(*p++ = c), w);
+ else
+ wwputc(*p++ = c, w);
+ }
+ }
+ *p = 0;
+ w->ww_unctrl = uc;
+}
+
+static void
+rub(c, w)
+struct ww *w;
+{
+ register i;
+
+ for (i = isctrl(c) ? strlen(unctrl(c)) : 1; --i >= 0;)
+ (void) wwwrite(w, "\b \b", 3);
+}
diff --git a/usr.bin/window/wwinit.c b/usr.bin/window/wwinit.c
new file mode 100644
index 0000000..05cc0b0
--- /dev/null
+++ b/usr.bin/window/wwinit.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwinit.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include <sys/signal.h>
+#include <fcntl.h>
+#include <termcap.h>
+#include "char.h"
+
+wwinit()
+{
+ register i, j;
+ char *kp;
+ int s;
+
+ wwdtablesize = getdtablesize();
+ wwhead.ww_forw = &wwhead;
+ wwhead.ww_back = &wwhead;
+
+ s = sigblock(sigmask(SIGIO) | sigmask(SIGCHLD) | sigmask(SIGALRM) |
+ sigmask(SIGHUP) | sigmask(SIGTERM));
+ if (signal(SIGIO, wwrint) == BADSIG ||
+ signal(SIGCHLD, wwchild) == BADSIG ||
+ signal(SIGHUP, wwquit) == BADSIG ||
+ signal(SIGTERM, wwquit) == BADSIG ||
+ signal(SIGPIPE, SIG_IGN) == BADSIG) {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+
+ if (wwgettty(0, &wwoldtty) < 0)
+ return -1;
+ wwwintty = wwoldtty;
+#ifdef OLD_TTY
+ wwwintty.ww_sgttyb.sg_flags &= ~XTABS;
+ wwnewtty.ww_sgttyb = wwoldtty.ww_sgttyb;
+ wwnewtty.ww_sgttyb.sg_erase = -1;
+ wwnewtty.ww_sgttyb.sg_kill = -1;
+ wwnewtty.ww_sgttyb.sg_flags |= CBREAK;
+ wwnewtty.ww_sgttyb.sg_flags &= ~(ECHO|CRMOD);
+ wwnewtty.ww_tchars.t_intrc = -1;
+ wwnewtty.ww_tchars.t_quitc = -1;
+ wwnewtty.ww_tchars.t_startc = -1;
+ wwnewtty.ww_tchars.t_stopc = -1;
+ wwnewtty.ww_tchars.t_eofc = -1;
+ wwnewtty.ww_tchars.t_brkc = -1;
+ wwnewtty.ww_ltchars.t_suspc = -1;
+ wwnewtty.ww_ltchars.t_dsuspc = -1;
+ wwnewtty.ww_ltchars.t_rprntc = -1;
+ wwnewtty.ww_ltchars.t_flushc = -1;
+ wwnewtty.ww_ltchars.t_werasc = -1;
+ wwnewtty.ww_ltchars.t_lnextc = -1;
+ wwnewtty.ww_lmode = wwoldtty.ww_lmode | LLITOUT;
+ wwnewtty.ww_ldisc = wwoldtty.ww_ldisc;
+#else
+#ifndef OXTABS
+#define OXTABS XTABS
+#endif
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE -1
+#endif
+ wwwintty.ww_termios.c_oflag &= ~OXTABS;
+ wwnewtty.ww_termios = wwoldtty.ww_termios;
+ wwnewtty.ww_termios.c_iflag &=
+ ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IMAXBEL);
+ wwnewtty.ww_termios.c_oflag = 0;
+ wwnewtty.ww_termios.c_cflag &= ~(CSIZE | PARENB);
+ wwnewtty.ww_termios.c_cflag |= CS8;
+ wwnewtty.ww_termios.c_lflag = 0;
+ for (i = 0; i < NCCS; i++)
+ wwnewtty.ww_termios.c_cc[i] = _POSIX_VDISABLE;
+ wwnewtty.ww_termios.c_cc[VMIN] = 0;
+ wwnewtty.ww_termios.c_cc[VTIME] = 0;
+#endif
+ wwnewtty.ww_fflags = wwoldtty.ww_fflags | FASYNC;
+ if (wwsettty(0, &wwnewtty) < 0)
+ goto bad;
+
+ if ((wwterm = getenv("TERM")) == 0) {
+ wwerrno = WWE_BADTERM;
+ goto bad;
+ }
+ if (tgetent(wwtermcap, wwterm) != 1) {
+ wwerrno = WWE_BADTERM;
+ goto bad;
+ }
+#ifdef OLD_TTY
+ ospeed = wwoldtty.ww_sgttyb.sg_ospeed;
+ switch (ospeed) {
+ default:
+ case B0:
+ goto bad;
+ case B50:
+ wwbaud = 50;
+ break;
+ case B75:
+ wwbaud = 75;
+ break;
+ case B110:
+ wwbaud = 110;
+ break;
+ case B134:
+ wwbaud = 134;
+ break;
+ case B150:
+ wwbaud = 150;
+ break;
+ case B200:
+ wwbaud = 200;
+ break;
+ case B300:
+ wwbaud = 300;
+ break;
+ case B600:
+ wwbaud = 600;
+ break;
+ case B1200:
+ wwbaud = 1200;
+ break;
+ case B1800:
+ wwbaud = 1800;
+ break;
+ case B2400:
+ wwbaud = 2400;
+ break;
+ case B4800:
+ wwbaud = 4800;
+ break;
+ case B9600:
+ wwbaud = 9600;
+ break;
+#ifdef B19200
+ case B19200:
+#else
+ case EXTA:
+#endif
+ wwbaud = 19200;
+ break;
+#ifdef B38400
+ case B38400:
+#else
+ case EXTB:
+#endif
+ wwbaud = 38400;
+ break;
+#ifdef B57600
+ case B57600:
+ wwbaud = 57600;
+ break;
+#endif
+#ifdef B115200
+ case B115200:
+ wwbaud = 115200;
+ break;
+#endif
+ }
+#else
+ if ((wwbaud = cfgetospeed(&wwoldtty.ww_termios)) == B0)
+ goto bad;
+#endif
+ wwospeed = ospeed;
+
+ if (xxinit() < 0)
+ goto bad;
+ wwnrow = tt.tt_nrow;
+ wwncol = tt.tt_ncol;
+ wwavailmodes = tt.tt_availmodes;
+ wwwrap = tt.tt_wrap;
+
+ if (wwavailmodes & WWM_REV)
+ wwcursormodes = WWM_REV | wwavailmodes & WWM_BLK;
+ else if (wwavailmodes & WWM_UL)
+ wwcursormodes = WWM_UL;
+
+ if ((wwib = malloc((unsigned) 512)) == 0)
+ goto bad;
+ wwibe = wwib + 512;
+ wwibq = wwibp = wwib;
+
+ if ((wwsmap = wwalloc(0, 0, wwnrow, wwncol, sizeof (char))) == 0)
+ goto bad;
+ for (i = 0; i < wwnrow; i++)
+ for (j = 0; j < wwncol; j++)
+ wwsmap[i][j] = WWX_NOBODY;
+
+ wwos = (union ww_char **)
+ wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char));
+ if (wwos == 0)
+ goto bad;
+ /* wwos is cleared in wwstart1() */
+ wwns = (union ww_char **)
+ wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char));
+ if (wwns == 0)
+ goto bad;
+ for (i = 0; i < wwnrow; i++)
+ for (j = 0; j < wwncol; j++)
+ wwns[i][j].c_w = ' ';
+ if (tt.tt_checkpoint) {
+ /* wwcs is also cleared in wwstart1() */
+ wwcs = (union ww_char **)
+ wwalloc(0, 0, wwnrow, wwncol, sizeof (union ww_char));
+ if (wwcs == 0)
+ goto bad;
+ }
+
+ wwtouched = malloc((unsigned) wwnrow);
+ if (wwtouched == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+ for (i = 0; i < wwnrow; i++)
+ wwtouched[i] = 0;
+
+ wwupd = (struct ww_update *) malloc((unsigned) wwnrow * sizeof *wwupd);
+ if (wwupd == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+
+ wwindex[WWX_NOBODY] = &wwnobody;
+ wwnobody.ww_order = NWW;
+
+ kp = wwwintermcap;
+ if (wwavailmodes & WWM_REV)
+ wwaddcap1(WWT_REV, &kp);
+ if (wwavailmodes & WWM_BLK)
+ wwaddcap1(WWT_BLK, &kp);
+ if (wwavailmodes & WWM_UL)
+ wwaddcap1(WWT_UL, &kp);
+ if (wwavailmodes & WWM_GRP)
+ wwaddcap1(WWT_GRP, &kp);
+ if (wwavailmodes & WWM_DIM)
+ wwaddcap1(WWT_DIM, &kp);
+ if (wwavailmodes & WWM_USR)
+ wwaddcap1(WWT_USR, &kp);
+ if (tt.tt_insline && tt.tt_delline || tt.tt_setscroll)
+ wwaddcap1(WWT_ALDL, &kp);
+ if (tt.tt_inschar)
+ wwaddcap1(WWT_IMEI, &kp);
+ if (tt.tt_insspace)
+ wwaddcap1(WWT_IC, &kp);
+ if (tt.tt_delchar)
+ wwaddcap1(WWT_DC, &kp);
+ wwaddcap("kb", &kp);
+ wwaddcap("ku", &kp);
+ wwaddcap("kd", &kp);
+ wwaddcap("kl", &kp);
+ wwaddcap("kr", &kp);
+ wwaddcap("kh", &kp);
+ if ((j = tgetnum("kn")) >= 0) {
+ char cap[32];
+
+ (void) sprintf(kp, "kn#%d:", j);
+ for (; *kp; kp++)
+ ;
+ for (i = 1; i <= j; i++) {
+ (void) sprintf(cap, "k%d", i);
+ wwaddcap(cap, &kp);
+ cap[0] = 'l';
+ wwaddcap(cap, &kp);
+ }
+ }
+ /*
+ * It's ok to do this here even if setenv() is destructive
+ * since tt_init() has already made its own copy of it and
+ * wwterm now points to the copy.
+ */
+ (void) setenv("TERM", WWT_TERM, 1);
+#ifdef TERMINFO
+ if (wwterminfoinit() < 0)
+ goto bad;
+#endif
+
+ if (tt.tt_checkpoint)
+ if (signal(SIGALRM, wwalarm) == BADSIG) {
+ wwerrno = WWE_SYS;
+ goto bad;
+ }
+ /* catch typeahead before ASYNC was set */
+ (void) kill(getpid(), SIGIO);
+ wwstart1();
+ (void) sigsetmask(s);
+ return 0;
+bad:
+ /*
+ * Don't bother to free storage. We're supposed
+ * to exit when wwinit fails anyway.
+ */
+ (void) signal(SIGIO, SIG_DFL);
+ (void) wwsettty(0, &wwoldtty);
+ (void) sigsetmask(s);
+ return -1;
+}
+
+wwaddcap(cap, kp)
+ register char *cap;
+ register char **kp;
+{
+ char tbuf[512];
+ char *tp = tbuf;
+ register char *str, *p;
+
+ if ((str = tgetstr(cap, &tp)) != 0) {
+ while (*(*kp)++ = *cap++)
+ ;
+ (*kp)[-1] = '=';
+ while (*str) {
+ for (p = unctrl(*str++); *(*kp)++ = *p++;)
+ ;
+ (*kp)--;
+ }
+ *(*kp)++ = ':';
+ **kp = 0;
+ }
+}
+
+wwaddcap1(cap, kp)
+ register char *cap;
+ register char **kp;
+{
+ while (*(*kp)++ = *cap++)
+ ;
+ (*kp)--;
+}
+
+wwstart()
+{
+ register i;
+
+ (void) wwsettty(0, &wwnewtty);
+ signal(SIGIO, wwrint);
+ for (i = 0; i < wwnrow; i++)
+ wwtouched[i] = WWU_TOUCHED;
+ wwstart1();
+}
+
+wwstart1()
+{
+ register i, j;
+
+ for (i = 0; i < wwnrow; i++)
+ for (j = 0; j < wwncol; j++) {
+ wwos[i][j].c_w = ' ';
+ if (tt.tt_checkpoint)
+ wwcs[i][j].c_w = ' ';
+ }
+ xxstart();
+ if (tt.tt_checkpoint)
+ wwdocheckpoint = 1;
+}
+
+/*
+ * Reset data structures and terminal from an unknown state.
+ * Restoring wwos has been taken care of elsewhere.
+ */
+wwreset()
+{
+ register i;
+
+ xxreset();
+ for (i = 0; i < wwnrow; i++)
+ wwtouched[i] = WWU_TOUCHED;
+}
diff --git a/usr.bin/window/wwinschar.c b/usr.bin/window/wwinschar.c
new file mode 100644
index 0000000..b283f99
--- /dev/null
+++ b/usr.bin/window/wwinschar.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwinschar.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwinschar(w, row, col, c, m)
+register struct ww *w;
+unsigned char c, m;
+{
+ register i;
+ int nvis;
+ short x = c | m << WWC_MSHIFT;
+
+ /*
+ * First, shift the line.
+ */
+ {
+ register union ww_char *p, *q;
+
+ p = &w->ww_buf[row][w->ww_b.r];
+ q = p - 1;
+ for (i = w->ww_b.r - col; --i > 0;)
+ *--p = *--q;
+ q->c_w = x;
+ }
+
+ /*
+ * If can't see it, just return.
+ */
+ if (row < w->ww_i.t || row >= w->ww_i.b
+ || w->ww_i.r <= 0 || w->ww_i.r <= col)
+ return;
+
+ if (col < w->ww_i.l)
+ col = w->ww_i.l;
+
+ /*
+ * Now find out how much is actually changed, and fix wwns.
+ */
+ {
+ register union ww_char *buf;
+ register char *win;
+ register union ww_char *ns;
+ register char *smap;
+ char touched;
+
+ nvis = 0;
+ smap = &wwsmap[row][col];
+ for (i = col; i < w->ww_i.r && *smap++ != w->ww_index; i++)
+ ;
+ if (i >= w->ww_i.r)
+ return;
+ col = i;
+ buf = w->ww_buf[row];
+ win = w->ww_win[row];
+ ns = wwns[row];
+ smap = &wwsmap[row][i];
+ touched = wwtouched[row];
+ for (; i < w->ww_i.r; i++) {
+ if (*smap++ != w->ww_index)
+ continue;
+ touched |= WWU_TOUCHED;
+ if (win[i])
+ ns[i].c_w =
+ buf[i].c_w ^ win[i] << WWC_MSHIFT;
+ else {
+ nvis++;
+ ns[i] = buf[i];
+ }
+ }
+ wwtouched[row] = touched;
+ }
+
+ /*
+ * Can/Should we use delete character?
+ */
+ if ((tt.tt_inschar || tt.tt_insspace) && nvis > (wwncol - col) / 2) {
+ register union ww_char *p, *q;
+
+ if (tt.tt_inschar)
+ xxinschar(row, col, c, m);
+ else {
+ xxinsspace(row, col);
+ x = ' ';
+ }
+ p = &wwos[row][wwncol];
+ q = p - 1;
+ for (i = wwncol - col; --i > 0;)
+ *--p = *--q;
+ q->c_w = x;
+ }
+}
diff --git a/usr.bin/window/wwinsline.c b/usr.bin/window/wwinsline.c
new file mode 100644
index 0000000..acdeb26
--- /dev/null
+++ b/usr.bin/window/wwinsline.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwinsline.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwinsline(w, row)
+register struct ww *w;
+int row;
+{
+ register i;
+ register union ww_char **cpp, **cqq;
+ register union ww_char *cp;
+ int row1, row2;
+ char deleted;
+ int visible;
+
+ /*
+ * Scroll first.
+ */
+ if ((row1 = row) < w->ww_i.t) {
+ row1 = w->ww_i.t;
+ visible = 0;
+ } else
+ visible = 1;
+ if ((row2 = w->ww_b.b) > w->ww_i.b) {
+ row2 = w->ww_i.b;
+ }
+ deleted = wwscroll1(w, row1, row2, -1, visible);
+
+ /*
+ * Fix the buffer.
+ * But leave clearing the last line for wwclreol().
+ */
+ cpp = &w->ww_buf[w->ww_b.b];
+ cqq = cpp - 1;
+ cp = *cqq;
+ for (i = w->ww_b.b - row; --i > 0;)
+ *--cpp = *--cqq;
+ *cqq = cp;
+
+ /*
+ * Now clear the last line.
+ */
+ if (visible)
+ wwclreol1(w, row, w->ww_b.l, deleted);
+ else {
+ cp += w->ww_b.l;
+ for (i = w->ww_b.nc; --i >= 0;)
+ cp++->c_w = ' ';
+ }
+}
diff --git a/usr.bin/window/wwiomux.c b/usr.bin/window/wwiomux.c
new file mode 100644
index 0000000..ce6978b
--- /dev/null
+++ b/usr.bin/window/wwiomux.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwiomux.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <sys/time.h>
+#include <sys/types.h>
+#if !defined(OLD_TTY) && !defined(TIOCPKT_DATA)
+#include <sys/ioctl.h>
+#endif
+#include <fcntl.h>
+
+/*
+ * Multiple window output handler.
+ * The idea is to copy window outputs to the terminal, via the
+ * display package. We try to give wwcurwin highest priority.
+ * The only return conditions are when there is keyboard input
+ * and when a child process dies, which are serviced by signal
+ * catchers (wwrint() and wwchild()).
+ * When there's nothing to do, we sleep in a select().
+ * This can be done better with interrupt driven io. But that's
+ * not supported on ptys, yet.
+ * The history of this routine is interesting.
+ */
+wwiomux()
+{
+ register struct ww *w;
+ fd_set imask;
+ register n;
+ register char *p;
+ char c;
+ struct timeval tv;
+ char noblock = 0;
+
+ for (;;) {
+ if (wwinterrupt()) {
+ wwclrintr();
+ return;
+ }
+
+ FD_ZERO(&imask);
+ n = -1;
+ for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) {
+ if (w->ww_pty < 0)
+ continue;
+ if (w->ww_obq < w->ww_obe) {
+ if (w->ww_pty > n)
+ n = w->ww_pty;
+ FD_SET(w->ww_pty, &imask);
+ }
+ if (w->ww_obq > w->ww_obp && !w->ww_stopped)
+ noblock = 1;
+ }
+
+ if (!noblock) {
+ if (wwcurwin != 0)
+ wwcurtowin(wwcurwin);
+ wwupdate();
+ wwflush();
+ (void) setjmp(wwjmpbuf);
+ wwsetjmp = 1;
+ if (wwinterrupt()) {
+ wwsetjmp = 0;
+ wwclrintr();
+ return;
+ }
+ /*
+ * Defensive code. If somebody else (for example,
+ * wall) clears the ASYNC flag on us, we will block
+ * forever. So we need a finite timeout and set
+ * the flag again. Anything more clever will probably
+ * need even more system calls. (This is a bug
+ * in the kernel.)
+ * I don't like this one bit.
+ */
+ (void) fcntl(0, F_SETFL, wwnewtty.ww_fflags);
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ }
+ wwnselect++;
+ n = select(n + 1, &imask, (fd_set *)0, (fd_set *)0, &tv);
+ wwsetjmp = 0;
+ noblock = 0;
+
+ if (n < 0)
+ wwnselecte++;
+ else if (n == 0)
+ wwnselectz++;
+ else
+ for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw) {
+ if (w->ww_pty < 0 ||
+ !FD_ISSET(w->ww_pty, &imask))
+ continue;
+ wwnwread++;
+ p = w->ww_obq;
+ if (w->ww_ispty) {
+ if (p == w->ww_ob) {
+ w->ww_obp++;
+ w->ww_obq++;
+ } else
+ p--;
+ c = *p;
+ }
+ n = read(w->ww_pty, p, w->ww_obe - p);
+ if (n < 0) {
+ wwnwreade++;
+ (void) close(w->ww_pty);
+ w->ww_pty = -1;
+ } else if (n == 0) {
+ wwnwreadz++;
+ (void) close(w->ww_pty);
+ w->ww_pty = -1;
+ } else if (!w->ww_ispty) {
+ wwnwreadd++;
+ wwnwreadc += n;
+ w->ww_obq += n;
+ } else if (*p == TIOCPKT_DATA) {
+ n--;
+ wwnwreadd++;
+ wwnwreadc += n;
+ w->ww_obq += n;
+ } else {
+ wwnwreadp++;
+ if (*p & TIOCPKT_STOP)
+ w->ww_stopped = 1;
+ if (*p & TIOCPKT_START)
+ w->ww_stopped = 0;
+ if (*p & TIOCPKT_FLUSHWRITE) {
+ w->ww_stopped = 0;
+ w->ww_obq = w->ww_obp =
+ w->ww_ob;
+ }
+ }
+ if (w->ww_ispty)
+ *p = c;
+ }
+ /*
+ * Try the current window first, if there is output
+ * then process it and go back to the top to try again.
+ * This can lead to starvation of the other windows,
+ * but presumably that what we want.
+ * Update will eventually happen when output from wwcurwin
+ * dies down.
+ */
+ if ((w = wwcurwin) != 0 && w->ww_pty >= 0 &&
+ w->ww_obq > w->ww_obp && !w->ww_stopped) {
+ n = wwwrite(w, w->ww_obp, w->ww_obq - w->ww_obp);
+ if ((w->ww_obp += n) == w->ww_obq)
+ w->ww_obq = w->ww_obp = w->ww_ob;
+ noblock = 1;
+ continue;
+ }
+ for (w = wwhead.ww_forw; w != &wwhead; w = w->ww_forw)
+ if (w->ww_pty >= 0 && w->ww_obq > w->ww_obp &&
+ !w->ww_stopped) {
+ n = wwwrite(w, w->ww_obp,
+ w->ww_obq - w->ww_obp);
+ if ((w->ww_obp += n) == w->ww_obq)
+ w->ww_obq = w->ww_obp = w->ww_ob;
+ if (wwinterrupt())
+ break;
+ }
+ }
+}
diff --git a/usr.bin/window/wwlabel.c b/usr.bin/window/wwlabel.c
new file mode 100644
index 0000000..d4cc99e
--- /dev/null
+++ b/usr.bin/window/wwlabel.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwlabel.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "char.h"
+
+/*
+ * Label window w on f,
+ * at 1 line above w and 'where' columns from it's left edge.
+ * Gross, but it works.
+ */
+wwlabel(w, f, where, l, mode)
+struct ww *w;
+struct ww *f;
+char *l;
+{
+ int row;
+ register j;
+ int jj;
+ register char *win;
+ register union ww_char *buf;
+ register union ww_char *ns;
+ register char *fmap;
+ register char *smap;
+ char touched;
+ unsigned char *p;
+ static unsigned char cbuf[2];
+
+ if (f->ww_fmap == 0)
+ return;
+
+ row = w->ww_w.t - 1;
+ if (row < f->ww_i.t || row >= f->ww_i.b)
+ return;
+ win = f->ww_win[row];
+ buf = f->ww_buf[row];
+ fmap = f->ww_fmap[row];
+ ns = wwns[row];
+ smap = wwsmap[row];
+ touched = wwtouched[row];
+ mode <<= WWC_MSHIFT;
+
+ jj = MIN(w->ww_i.r, f->ww_i.r);
+ j = w->ww_i.l + where;
+ while (j < jj && *l) {
+ if (isctrl(*l))
+ p = unctrl(*l);
+ else {
+ cbuf[0] = *l;
+ p = cbuf;
+ }
+ for (l++; j < jj && *p; j++, p++) {
+ /* can't label if not already framed */
+ if (win[j] & WWM_GLS)
+ continue;
+ if (smap[j] != f->ww_index)
+ buf[j].c_w = mode | *p;
+ else {
+ ns[j].c_w = (buf[j].c_w = mode | *p)
+ ^ win[j] << WWC_MSHIFT;
+ touched |= WWU_TOUCHED;
+ }
+ fmap[j] |= WWF_LABEL;
+ }
+ }
+ wwtouched[row] = touched;
+}
diff --git a/usr.bin/window/wwmisc.c b/usr.bin/window/wwmisc.c
new file mode 100644
index 0000000..3fca477
--- /dev/null
+++ b/usr.bin/window/wwmisc.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwmisc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include "char.h"
+
+/*
+ * Sufficient but not necessary test for total visibility.
+ */
+wwvisible(w)
+register struct ww *w;
+{
+ register i;
+ register nvis = 0;
+
+ for (i = w->ww_i.t; i < w->ww_i.b; i++)
+ nvis += w->ww_nvis[i];
+ if (w->ww_hascursor
+ && w->ww_cur.r >= w->ww_i.t && w->ww_cur.r < w->ww_i.b
+ && w->ww_cur.c >= w->ww_i.l && w->ww_cur.c < w->ww_i.r
+ && wwsmap[w->ww_cur.r][w->ww_cur.c] == w->ww_index)
+ nvis++;
+ return nvis == w->ww_i.nr * w->ww_i.nc;
+}
+
+wwbell()
+{
+ ttputc(ctrl('g'));
+}
diff --git a/usr.bin/window/wwmove.c b/usr.bin/window/wwmove.c
new file mode 100644
index 0000000..65440d1
--- /dev/null
+++ b/usr.bin/window/wwmove.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwmove.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+/*
+ * Move a window. Should be unattached.
+ */
+wwmove(w, row, col)
+register struct ww *w;
+{
+ register dr, dc;
+ register i;
+
+ dr = row - w->ww_w.t;
+ dc = col - w->ww_w.l;
+
+ w->ww_w.t += dr;
+ w->ww_w.b += dr;
+ w->ww_w.l += dc;
+ w->ww_w.r += dc;
+
+ w->ww_b.t += dr;
+ w->ww_b.b += dr;
+ w->ww_b.l += dc;
+ w->ww_b.r += dc;
+
+ w->ww_i.t = MAX(w->ww_w.t, 0);
+ w->ww_i.b = MIN(w->ww_w.b, wwnrow);
+ w->ww_i.nr = w->ww_i.b - w->ww_i.t;
+ w->ww_i.l = MAX(w->ww_w.l, 0);
+ w->ww_i.r = MIN(w->ww_w.r, wwncol);
+ w->ww_i.nc = w->ww_i.r - w->ww_i.l;
+
+ w->ww_cur.r += dr;
+ w->ww_cur.c += dc;
+
+ w->ww_win -= dr;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ w->ww_win[i] -= dc;
+ if (w->ww_fmap != 0) {
+ w->ww_fmap -= dr;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ w->ww_fmap[i] -= dc;
+ }
+ w->ww_nvis -= dr;
+ for (i = w->ww_i.t; i < w->ww_i.b; i++) {
+ register j = w->ww_i.l;
+ register char *win = &w->ww_win[i][j];
+ register char *smap = &wwsmap[i][j];
+ int nvis = 0;
+
+ for (; j < w->ww_i.r; j++, win++, smap++)
+ if (*win == 0 && *smap == w->ww_index)
+ nvis++;
+ w->ww_nvis[i] = nvis;
+ }
+ w->ww_buf -= dr;
+ for (i = w->ww_b.t; i < w->ww_b.b; i++)
+ w->ww_buf[i] -= dc;
+}
diff --git a/usr.bin/window/wwopen.c b/usr.bin/window/wwopen.c
new file mode 100644
index 0000000..8a8be2a
--- /dev/null
+++ b/usr.bin/window/wwopen.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwopen.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+struct ww *
+wwopen(flags, nrow, ncol, row, col, nline)
+{
+ register struct ww *w;
+ register i, j;
+ char m;
+ short nvis;
+
+ w = (struct ww *)calloc(sizeof (struct ww), 1);
+ if (w == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+ w->ww_pty = -1;
+ w->ww_socket = -1;
+
+ for (i = 0; i < NWW && wwindex[i] != 0; i++)
+ ;
+ if (i >= NWW) {
+ wwerrno = WWE_TOOMANY;
+ goto bad;
+ }
+ w->ww_index = i;
+
+ if (nline < nrow)
+ nline = nrow;
+
+ w->ww_w.t = row;
+ w->ww_w.b = row + nrow;
+ w->ww_w.l = col;
+ w->ww_w.r = col + ncol;
+ w->ww_w.nr = nrow;
+ w->ww_w.nc = ncol;
+
+ w->ww_b.t = row;
+ w->ww_b.b = row + nline;
+ w->ww_b.l = col;
+ w->ww_b.r = col + ncol;
+ w->ww_b.nr = nline;
+ w->ww_b.nc = ncol;
+
+ w->ww_i.t = MAX(w->ww_w.t, 0);
+ w->ww_i.b = MIN(w->ww_w.b, wwnrow);
+ w->ww_i.l = MAX(w->ww_w.l, 0);
+ w->ww_i.r = MIN(w->ww_w.r, wwncol);
+ w->ww_i.nr = w->ww_i.b - w->ww_i.t;
+ w->ww_i.nc = w->ww_i.r - w->ww_i.l;
+
+ w->ww_cur.r = w->ww_w.t;
+ w->ww_cur.c = w->ww_w.l;
+
+ if (flags & WWO_PTY) {
+ if (wwgetpty(w) < 0)
+ goto bad;
+ w->ww_ispty = 1;
+ } else if (flags & WWO_SOCKET) {
+ int d[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, d) < 0) {
+ wwerrno = WWE_SYS;
+ goto bad;
+ }
+ (void) fcntl(d[0], F_SETFD, 1);
+ (void) fcntl(d[1], F_SETFD, 1);
+ w->ww_pty = d[0];
+ w->ww_socket = d[1];
+ }
+ if (flags & (WWO_PTY|WWO_SOCKET)) {
+ if ((w->ww_ob = malloc(512)) == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+ w->ww_obe = w->ww_ob + 512;
+ w->ww_obp = w->ww_obq = w->ww_ob;
+ }
+
+ w->ww_win = wwalloc(w->ww_w.t, w->ww_w.l,
+ w->ww_w.nr, w->ww_w.nc, sizeof (char));
+ if (w->ww_win == 0)
+ goto bad;
+ m = 0;
+ if (flags & WWO_GLASS)
+ m |= WWM_GLS;
+ if (flags & WWO_REVERSE)
+ if (wwavailmodes & WWM_REV)
+ m |= WWM_REV;
+ else
+ flags &= ~WWO_REVERSE;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ for (j = w->ww_w.l; j < w->ww_w.r; j++)
+ w->ww_win[i][j] = m;
+
+ if (flags & WWO_FRAME) {
+ w->ww_fmap = wwalloc(w->ww_w.t, w->ww_w.l,
+ w->ww_w.nr, w->ww_w.nc, sizeof (char));
+ if (w->ww_fmap == 0)
+ goto bad;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ for (j = w->ww_w.l; j < w->ww_w.r; j++)
+ w->ww_fmap[i][j] = 0;
+ }
+
+ w->ww_buf = (union ww_char **)
+ wwalloc(w->ww_b.t, w->ww_b.l,
+ w->ww_b.nr, w->ww_b.nc, sizeof (union ww_char));
+ if (w->ww_buf == 0)
+ goto bad;
+ for (i = w->ww_b.t; i < w->ww_b.b; i++)
+ for (j = w->ww_b.l; j < w->ww_b.r; j++)
+ w->ww_buf[i][j].c_w = ' ';
+
+ w->ww_nvis = (short *)malloc((unsigned) w->ww_w.nr * sizeof (short));
+ if (w->ww_nvis == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+ w->ww_nvis -= w->ww_w.t;
+ nvis = m ? 0 : w->ww_w.nc;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ w->ww_nvis[i] = nvis;
+
+ w->ww_state = WWS_INITIAL;
+ w->ww_oflags = flags;
+ return wwindex[w->ww_index] = w;
+bad:
+ if (w != 0) {
+ if (w->ww_win != 0)
+ wwfree(w->ww_win, w->ww_w.t);
+ if (w->ww_fmap != 0)
+ wwfree(w->ww_fmap, w->ww_w.t);
+ if (w->ww_buf != 0)
+ wwfree((char **)w->ww_buf, w->ww_b.t);
+ if (w->ww_nvis != 0)
+ free((char *)(w->ww_nvis + w->ww_w.t));
+ if (w->ww_ob != 0)
+ free(w->ww_ob);
+ if (w->ww_pty >= 0)
+ (void) close(w->ww_pty);
+ if (w->ww_socket >= 0)
+ (void) close(w->ww_socket);
+ free((char *)w);
+ }
+ return 0;
+}
diff --git a/usr.bin/window/wwprintf.c b/usr.bin/window/wwprintf.c
new file mode 100644
index 0000000..8df9d13
--- /dev/null
+++ b/usr.bin/window/wwprintf.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwprintf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <varargs.h>
+
+/*VARARGS2*/
+wwprintf(w, fmt, va_alist)
+struct ww *w;
+char *fmt;
+va_dcl
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start(ap);
+ /* buffer can overflow */
+ (void) wwwrite(w, buf, vsprintf(buf, fmt, ap));
+ va_end(ap);
+}
diff --git a/usr.bin/window/wwpty.c b/usr.bin/window/wwpty.c
new file mode 100644
index 0000000..1d82ff9
--- /dev/null
+++ b/usr.bin/window/wwpty.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwpty.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <fcntl.h>
+#if !defined(OLD_TTY) && !defined(TIOCPKT)
+#include <sys/ioctl.h>
+#endif
+
+wwgetpty(w)
+register struct ww *w;
+{
+ register char c, *p;
+ int tty;
+ int on = 1;
+#define PTY "/dev/XtyXX"
+#define _PT 5
+#define _PQRS 8
+#define _0_9 9
+
+ (void) strcpy(w->ww_ttyname, PTY);
+ for (c = 'p'; c <= 'u'; c++) {
+ w->ww_ttyname[_PT] = 'p';
+ w->ww_ttyname[_PQRS] = c;
+ w->ww_ttyname[_0_9] = '0';
+ if (access(w->ww_ttyname, 0) < 0)
+ break;
+ for (p = "0123456789abcdef"; *p; p++) {
+ w->ww_ttyname[_PT] = 'p';
+ w->ww_ttyname[_0_9] = *p;
+ if ((w->ww_pty = open(w->ww_ttyname, 2)) < 0)
+ continue;
+ w->ww_ttyname[_PT] = 't';
+ if ((tty = open(w->ww_ttyname, 2)) < 0) {
+ (void) close(w->ww_pty);
+ continue;
+ }
+ (void) close(tty);
+ if (ioctl(w->ww_pty, TIOCPKT, (char *)&on) < 0) {
+ (void) close(w->ww_pty);
+ continue;
+ }
+ (void) fcntl(w->ww_pty, F_SETFD, 1);
+ return 0;
+ }
+ }
+ w->ww_pty = -1;
+ wwerrno = WWE_NOPTY;
+ return -1;
+}
diff --git a/usr.bin/window/wwputc.c b/usr.bin/window/wwputc.c
new file mode 100644
index 0000000..af614b2
--- /dev/null
+++ b/usr.bin/window/wwputc.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwputc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwputc(c, w)
+char c;
+struct ww *w;
+{
+ (void) wwwrite(w, &c, sizeof c);
+}
diff --git a/usr.bin/window/wwputs.c b/usr.bin/window/wwputs.c
new file mode 100644
index 0000000..2584da2
--- /dev/null
+++ b/usr.bin/window/wwputs.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwputs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwputs(s, w)
+register char *s;
+struct ww *w;
+{
+ register char *p = s;
+
+ while (*p++)
+ ;
+ (void) wwwrite(w, s, p - s - 1);
+}
diff --git a/usr.bin/window/wwredraw.c b/usr.bin/window/wwredraw.c
new file mode 100644
index 0000000..2a98012
--- /dev/null
+++ b/usr.bin/window/wwredraw.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwredraw.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwredraw()
+{
+ register i, j;
+ register union ww_char *os;
+
+ xxclear();
+ for (i = 0; i < wwnrow; i++) {
+ wwtouched[i] = WWU_TOUCHED;
+ os = wwos[i];
+ for (j = wwncol; --j >= 0;)
+ (os++)->c_w = ' ';
+ }
+}
diff --git a/usr.bin/window/wwredrawwin.c b/usr.bin/window/wwredrawwin.c
new file mode 100644
index 0000000..36590df
--- /dev/null
+++ b/usr.bin/window/wwredrawwin.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwredrawwin.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwredrawwin1(w, row1, row2, offset)
+register struct ww *w;
+int row1, row2, offset;
+{
+ int row;
+ register col;
+ register char *smap;
+ register union ww_char *buf;
+ register char *win;
+ register union ww_char *ns;
+ int x;
+ int nchanged;
+
+ for (row = row1; row < row2; row++) {
+ col = w->ww_i.l;
+ ns = wwns[row];
+ smap = &wwsmap[row][col];
+ buf = w->ww_buf[row + offset];
+ win = w->ww_win[row];
+ nchanged = 0;
+ for (; col < w->ww_i.r; col++)
+ if (*smap++ == w->ww_index &&
+ ns[col].c_w !=
+ (x = buf[col].c_w ^ win[col] << WWC_MSHIFT)) {
+ nchanged++;
+ ns[col].c_w = x;
+ }
+ if (nchanged > 0)
+ wwtouched[row] |= WWU_TOUCHED;
+ }
+}
diff --git a/usr.bin/window/wwrint.c b/usr.bin/window/wwrint.c
new file mode 100644
index 0000000..0cd0901
--- /dev/null
+++ b/usr.bin/window/wwrint.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwrint.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#if defined(OLD_TTY) || defined(VMIN_BUG)
+#include <fcntl.h>
+#endif
+
+/*
+ * Tty input interrupt handler.
+ * (1) Read input into buffer (wwib*).
+ * (2) Set the interrupt flag if anything is read.
+ * Currently, the last is used to get out of the blocking
+ * select() in wwiomux().
+ * To avoid race conditions, we only modify wwibq in here, except
+ * when the buffer is empty; and everywhere else, we only change wwibp.
+ * It should be completely safe.
+ */
+void
+wwrint()
+{
+ register n;
+
+ if (wwibp == wwibq)
+ wwibp = wwibq = wwib;
+ wwnread++;
+#if defined(OLD_TTY) || defined(VMIN_BUG)
+ /* we have set c_cc[VMIN] to 0 */
+ (void) fcntl(0, F_SETFL, O_NONBLOCK|wwnewtty.ww_fflags);
+#endif
+ n = read(0, wwibq, wwibe - wwibq);
+#if defined(OLD_TTY) || defined(VMIN_BUG)
+ (void) fcntl(0, F_SETFL, wwnewtty.ww_fflags);
+#endif
+ if (n > 0) {
+ if (tt.tt_rint)
+ n = (*tt.tt_rint)(wwibq, n);
+ if (n > 0) {
+ wwibq += n;
+ wwnreadc += n;
+ /*
+ * Hasten or delay the next checkpoint,
+ * as the case may be.
+ */
+ if (tt.tt_checkpoint && !wwdocheckpoint)
+ (void) alarm(1);
+ wwsetintr();
+ }
+ } else if (n == 0)
+ wwnreadz++;
+ else
+ wwnreade++;
+}
diff --git a/usr.bin/window/wwscroll.c b/usr.bin/window/wwscroll.c
new file mode 100644
index 0000000..806e398
--- /dev/null
+++ b/usr.bin/window/wwscroll.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwscroll.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwscroll(w, n)
+register struct ww *w;
+int n;
+{
+ register dir;
+ register top;
+
+ if (n == 0)
+ return;
+ dir = n < 0 ? -1 : 1;
+ top = w->ww_b.t - n;
+ if (top > w->ww_w.t)
+ top = w->ww_w.t;
+ else if (top + w->ww_b.nr < w->ww_w.b)
+ top = w->ww_w.b - w->ww_b.nr;
+ n = abs(top - w->ww_b.t);
+ if (n < w->ww_i.nr) {
+ while (--n >= 0) {
+ (void) wwscroll1(w, w->ww_i.t, w->ww_i.b, dir, 0);
+ w->ww_buf += dir;
+ w->ww_b.t -= dir;
+ w->ww_b.b -= dir;
+ }
+ } else {
+ w->ww_buf -= top - w->ww_b.t;
+ w->ww_b.t = top;
+ w->ww_b.b = top + w->ww_b.nr;
+ wwredrawwin(w);
+ }
+}
+
+/*
+ * Scroll one line, between 'row1' and 'row2', in direction 'dir'.
+ * Don't adjust ww_scroll.
+ * And don't redraw 'leaveit' lines.
+ */
+wwscroll1(w, row1, row2, dir, leaveit)
+register struct ww *w;
+int row1, row2, dir;
+int leaveit;
+{
+ register i;
+ int row1x, row2x;
+ int nvis;
+ int nvismax;
+ int scrolled = 0;
+
+ /*
+ * See how many lines on the screen are affected.
+ * And calculate row1x, row2x, and left at the same time.
+ */
+ for (i = row1; i < row2 && w->ww_nvis[i] == 0; i++)
+ ;
+ if (i >= row2) /* can't do any fancy stuff */
+ goto out;
+ row1x = i;
+ for (i = row2 - 1; i >= row1 && w->ww_nvis[i] == 0; i--)
+ ;
+ if (i <= row1x)
+ goto out; /* just one line is easy */
+ row2x = i + 1;
+
+ /*
+ * See how much of this window is visible.
+ */
+ nvismax = wwncol * (row2x - row1x);
+ nvis = 0;
+ for (i = row1x; i < row2x; i++)
+ nvis += w->ww_nvis[i];
+
+ /*
+ * If it's a good idea to scroll and the terminal can, then do it.
+ */
+ if (nvis < nvismax / 2)
+ goto no_scroll; /* not worth it */
+ if ((dir > 0 ? tt.tt_scroll_down == 0 : tt.tt_scroll_up == 0) ||
+ (tt.tt_scroll_top != row1x || tt.tt_scroll_bot != row2x - 1) &&
+ tt.tt_setscroll == 0)
+ if (tt.tt_delline == 0 || tt.tt_insline == 0)
+ goto no_scroll;
+ xxscroll(dir, row1x, row2x);
+ scrolled = 1;
+ /*
+ * Fix up the old screen.
+ */
+ {
+ register union ww_char *tmp;
+ register union ww_char **cpp, **cqq;
+
+ if (dir > 0) {
+ cpp = &wwos[row1x];
+ cqq = cpp + 1;
+ tmp = *cpp;
+ for (i = row2x - row1x; --i > 0;)
+ *cpp++ = *cqq++;
+ *cpp = tmp;
+ } else {
+ cpp = &wwos[row2x];
+ cqq = cpp - 1;
+ tmp = *cqq;
+ for (i = row2x - row1x; --i > 0;)
+ *--cpp = *--cqq;
+ *cqq = tmp;
+ }
+ for (i = wwncol; --i >= 0;)
+ tmp++->c_w = ' ';
+ }
+
+no_scroll:
+ /*
+ * Fix the new screen.
+ */
+ if (nvis == nvismax) {
+ /*
+ * Can shift whole lines.
+ */
+ if (dir > 0) {
+ {
+ register union ww_char *tmp;
+ register union ww_char **cpp, **cqq;
+
+ cpp = &wwns[row1x];
+ cqq = cpp + 1;
+ tmp = *cpp;
+ for (i = row2x - row1x; --i > 0;)
+ *cpp++ = *cqq++;
+ *cpp = tmp;
+ }
+ if (scrolled) {
+ register char *p, *q;
+
+ p = &wwtouched[row1x];
+ q = p + 1;
+ for (i = row2x - row1x; --i > 0;)
+ *p++ = *q++;
+ *p |= WWU_TOUCHED;
+ } else {
+ register char *p;
+
+ p = &wwtouched[row1x];
+ for (i = row2x - row1x; --i >= 0;)
+ *p++ |= WWU_TOUCHED;
+ }
+ wwredrawwin1(w, row1, row1x, dir);
+ wwredrawwin1(w, row2x - 1, row2 - leaveit, dir);
+ } else {
+ {
+ register union ww_char *tmp;
+ register union ww_char **cpp, **cqq;
+
+ cpp = &wwns[row2x];
+ cqq = cpp - 1;
+ tmp = *cqq;
+ for (i = row2x - row1x; --i > 0;)
+ *--cpp = *--cqq;
+ *cqq = tmp;
+ }
+ if (scrolled) {
+ register char *p, *q;
+
+ p = &wwtouched[row2x];
+ q = p - 1;
+ for (i = row2x - row1x; --i > 0;)
+ *--p = *--q;
+ *q |= WWU_TOUCHED;
+ } else {
+ register char *p;
+
+ p = &wwtouched[row1x];
+ for (i = row2x - row1x; --i >= 0;)
+ *p++ |= WWU_TOUCHED;
+ }
+ wwredrawwin1(w, row1 + leaveit, row1x + 1, dir);
+ wwredrawwin1(w, row2x, row2, dir);
+ }
+ } else {
+ if (scrolled) {
+ register char *p;
+
+ p = &wwtouched[row1x];
+ for (i = row2x - row1x; --i >= 0;)
+ *p++ |= WWU_TOUCHED;
+ }
+out:
+ if (dir > 0)
+ wwredrawwin1(w, row1, row2 - leaveit, dir);
+ else
+ wwredrawwin1(w, row1 + leaveit, row2, dir);
+ }
+ return scrolled;
+}
diff --git a/usr.bin/window/wwsize.c b/usr.bin/window/wwsize.c
new file mode 100644
index 0000000..7dee857
--- /dev/null
+++ b/usr.bin/window/wwsize.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwsize.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+/*
+ * Resize a window. Should be unattached.
+ */
+wwsize(w, nrow, ncol)
+register struct ww *w;
+{
+ register i, j;
+ int nline;
+ union ww_char **buf = 0;
+ char **win = 0;
+ short *nvis = 0;
+ char **fmap = 0;
+ char m;
+
+ /*
+ * First allocate new buffers.
+ */
+ win = wwalloc(w->ww_w.t, w->ww_w.l, nrow, ncol, sizeof (char));
+ if (win == 0)
+ goto bad;
+ if (w->ww_fmap != 0) {
+ fmap = wwalloc(w->ww_w.t, w->ww_w.l, nrow, ncol, sizeof (char));
+ if (fmap == 0)
+ goto bad;
+ }
+ if (nrow > w->ww_b.nr || ncol > w->ww_b.nc) {
+ nline = MAX(w->ww_b.nr, nrow);
+ buf = (union ww_char **) wwalloc(w->ww_b.t, w->ww_b.l,
+ nline, ncol, sizeof (union ww_char));
+ if (buf == 0)
+ goto bad;
+ }
+ nvis = (short *)malloc((unsigned) nrow * sizeof (short));
+ if (nvis == 0) {
+ wwerrno = WWE_NOMEM;
+ goto bad;
+ }
+ nvis -= w->ww_w.t;
+ /*
+ * Copy text buffer.
+ */
+ if (buf != 0) {
+ int b, r;
+
+ b = w->ww_b.t + nline;
+ r = w->ww_b.l + ncol;
+ if (ncol < w->ww_b.nc)
+ for (i = w->ww_b.t; i < w->ww_b.b; i++)
+ for (j = w->ww_b.l; j < r; j++)
+ buf[i][j] = w->ww_buf[i][j];
+ else
+ for (i = w->ww_b.t; i < w->ww_b.b; i++) {
+ for (j = w->ww_b.l; j < w->ww_b.r; j++)
+ buf[i][j] = w->ww_buf[i][j];
+ for (; j < r; j++)
+ buf[i][j].c_w = ' ';
+ }
+ for (; i < b; i++)
+ for (j = w->ww_b.l; j < r; j++)
+ buf[i][j].c_w = ' ';
+ }
+ /*
+ * Now free the old stuff.
+ */
+ wwfree((char **)w->ww_win, w->ww_w.t);
+ w->ww_win = win;
+ if (buf != 0) {
+ wwfree((char **)w->ww_buf, w->ww_b.t);
+ w->ww_buf = buf;
+ }
+ if (w->ww_fmap != 0) {
+ wwfree((char **)w->ww_fmap, w->ww_w.t);
+ w->ww_fmap = fmap;
+ }
+ free((char *)(w->ww_nvis + w->ww_w.t));
+ w->ww_nvis = nvis;
+ /*
+ * Set new sizes.
+ */
+ /* window */
+ w->ww_w.b = w->ww_w.t + nrow;
+ w->ww_w.r = w->ww_w.l + ncol;
+ w->ww_w.nr = nrow;
+ w->ww_w.nc = ncol;
+ /* text buffer */
+ if (buf != 0) {
+ w->ww_b.b = w->ww_b.t + nline;
+ w->ww_b.r = w->ww_b.l + ncol;
+ w->ww_b.nr = nline;
+ w->ww_b.nc = ncol;
+ }
+ /* scroll */
+ if ((i = w->ww_b.b - w->ww_w.b) < 0 ||
+ (i = w->ww_cur.r - w->ww_w.b + 1) > 0) {
+ w->ww_buf += i;
+ w->ww_b.t -= i;
+ w->ww_b.b -= i;
+ w->ww_cur.r -= i;
+ }
+ /* interior */
+ w->ww_i.b = MIN(w->ww_w.b, wwnrow);
+ w->ww_i.r = MIN(w->ww_w.r, wwncol);
+ w->ww_i.nr = w->ww_i.b - w->ww_i.t;
+ w->ww_i.nc = w->ww_i.r - w->ww_i.l;
+ /*
+ * Initialize new buffers.
+ */
+ /* window */
+ m = 0;
+ if (w->ww_oflags & WWO_GLASS)
+ m |= WWM_GLS;
+ if (w->ww_oflags & WWO_REVERSE)
+ m |= WWM_REV;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ for (j = w->ww_w.l; j < w->ww_w.r; j++)
+ w->ww_win[i][j] = m;
+ /* frame map */
+ if (fmap != 0)
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ for (j = w->ww_w.l; j < w->ww_w.r; j++)
+ w->ww_fmap[i][j] = 0;
+ /* visibility */
+ j = m ? 0 : w->ww_w.nc;
+ for (i = w->ww_w.t; i < w->ww_w.b; i++)
+ w->ww_nvis[i] = j;
+ /*
+ * Put cursor back.
+ */
+ if (w->ww_hascursor) {
+ w->ww_hascursor = 0;
+ wwcursor(w, 1);
+ }
+ /*
+ * Fool with pty.
+ */
+ if (w->ww_ispty && w->ww_pty >= 0)
+ (void) wwsetttysize(w->ww_pty, nrow, ncol);
+ return 0;
+bad:
+ if (win != 0)
+ wwfree(win, w->ww_w.t);
+ if (fmap != 0)
+ wwfree(fmap, w->ww_w.t);
+ if (buf != 0)
+ wwfree((char **)buf, w->ww_b.t);
+ if (nvis != 0)
+ free((char *)(nvis + w->ww_w.t));
+ return -1;
+}
diff --git a/usr.bin/window/wwspawn.c b/usr.bin/window/wwspawn.c
new file mode 100644
index 0000000..7c3c12b
--- /dev/null
+++ b/usr.bin/window/wwspawn.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwspawn.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <sys/signal.h>
+
+/*
+ * There is a dead lock with vfork and closing of pseudo-ports.
+ * So we have to be sneaky about error reporting.
+ */
+wwspawn(wp, file, argv)
+register struct ww *wp;
+char *file;
+char **argv;
+{
+ int pid;
+ int ret;
+ char erred = 0;
+ int s;
+
+ s = sigblock(sigmask(SIGCHLD));
+ switch (pid = vfork()) {
+ case -1:
+ wwerrno = WWE_SYS;
+ ret = -1;
+ break;
+ case 0:
+ if (wwenviron(wp) >= 0)
+ execvp(file, argv);
+ erred = 1;
+ _exit(1);
+ default:
+ if (erred) {
+ wwerrno = WWE_SYS;
+ ret = -1;
+ } else {
+ wp->ww_pid = pid;
+ wp->ww_state = WWS_HASPROC;
+ ret = pid;
+ }
+ }
+ (void) sigsetmask(s);
+ if (wp->ww_socket >= 0) {
+ (void) close(wp->ww_socket);
+ wp->ww_socket = -1;
+ }
+ return ret;
+}
diff --git a/usr.bin/window/wwsuspend.c b/usr.bin/window/wwsuspend.c
new file mode 100644
index 0000000..a67a130
--- /dev/null
+++ b/usr.bin/window/wwsuspend.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwsuspend.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include <sys/signal.h>
+
+wwsuspend()
+{
+ sig_t oldsig;
+
+ oldsig = signal(SIGTSTP, SIG_IGN);
+ wwend(0);
+ (void) signal(SIGTSTP, SIG_DFL);
+ (void) kill(0, SIGTSTP);
+ (void) signal(SIGTSTP, SIG_IGN);
+ wwstart();
+ (void) signal(SIGTSTP, oldsig);
+}
diff --git a/usr.bin/window/wwterminfo.c b/usr.bin/window/wwterminfo.c
new file mode 100644
index 0000000..8039b1e
--- /dev/null
+++ b/usr.bin/window/wwterminfo.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1982, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwterminfo.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#ifdef TERMINFO
+
+#include "ww.h"
+#include <stdio.h>
+#include <paths.h>
+#include "local.h"
+
+/*
+ * Terminfo support
+ *
+ * Written by Brian Buhrow
+ *
+ * Subsequently modified by Edward Wang
+ */
+
+/*
+ * Initialize the working terminfo directory
+ */
+wwterminfoinit()
+{
+ FILE *fp;
+ char buf[2048];
+
+ /* make the directory */
+ (void) sprintf(wwterminfopath, "%swwinXXXXXX", _PATH_TMP);
+ mktemp(wwterminfopath);
+ if (mkdir(wwterminfopath, 0755) < 0 ||
+ chmod(wwterminfopath, 00755) < 0) {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ (void) setenv("TERMINFO", wwterminfopath, 1);
+ /* make a termcap entry and turn it into terminfo */
+ (void) sprintf(buf, "%s/cap", wwterminfopath);
+ if ((fp = fopen(buf, "w")) == NULL) {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ (void) fprintf(fp, "%sco#%d:li#%d:%s\n",
+ WWT_TERMCAP, wwncol, wwnrow, wwwintermcap);
+ (void) fclose(fp);
+ (void) sprintf(buf,
+ "cd %s; %s cap >info 2>/dev/null; %s info >/dev/null 2>&1",
+ wwterminfopath, _PATH_CAPTOINFO, _PATH_TIC);
+ (void) system(buf);
+ return 0;
+}
+
+/*
+ * Delete the working terminfo directory at shutdown
+ */
+wwterminfoend()
+{
+
+ switch (vfork()) {
+ case -1:
+ /* can't really do (or say) anything about errors */
+ return -1;
+ case 0:
+ execl(_PATH_RM, _PATH_RM, "-rf", wwterminfopath, 0);
+ return -1;
+ default:
+ return 0;
+ }
+}
+
+#endif /* TERMINFO */
diff --git a/usr.bin/window/wwtty.c b/usr.bin/window/wwtty.c
new file mode 100644
index 0000000..2440389
--- /dev/null
+++ b/usr.bin/window/wwtty.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwtty.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#if !defined(OLD_TTY) && !defined(TIOCGWINSZ)
+#include <sys/ioctl.h>
+#endif
+
+wwgettty(d, t)
+register struct ww_tty *t;
+{
+#ifdef OLD_TTY
+ if (ioctl(d, TIOCGETP, (char *)&t->ww_sgttyb) < 0)
+ goto bad;
+ if (ioctl(d, TIOCGETC, (char *)&t->ww_tchars) < 0)
+ goto bad;
+ if (ioctl(d, TIOCGLTC, (char *)&t->ww_ltchars) < 0)
+ goto bad;
+ if (ioctl(d, TIOCLGET, (char *)&t->ww_lmode) < 0)
+ goto bad;
+ if (ioctl(d, TIOCGETD, (char *)&t->ww_ldisc) < 0)
+ goto bad;
+#else
+ if (tcgetattr(d, &t->ww_termios) < 0)
+ goto bad;
+#endif
+ if ((t->ww_fflags = fcntl(d, F_GETFL, 0)) < 0)
+ goto bad;
+ return 0;
+bad:
+ wwerrno = WWE_SYS;
+ return -1;
+}
+
+/*
+ * Set the modes of tty 'd' to 't'
+ * 'o' is the current modes. We set the line discipline only if
+ * it changes, to avoid unnecessary flushing of typeahead.
+ */
+wwsettty(d, t)
+register struct ww_tty *t;
+{
+#ifdef OLD_TTY
+ int i;
+
+ /* XXX, for buggy tty drivers that don't wait for output to drain */
+ while (ioctl(d, TIOCOUTQ, &i) >= 0 && i > 0)
+ usleep(100000);
+ if (ioctl(d, TIOCSETN, (char *)&t->ww_sgttyb) < 0)
+ goto bad;
+ if (ioctl(d, TIOCSETC, (char *)&t->ww_tchars) < 0)
+ goto bad;
+ if (ioctl(d, TIOCSLTC, (char *)&t->ww_ltchars) < 0)
+ goto bad;
+ if (ioctl(d, TIOCLSET, (char *)&t->ww_lmode) < 0)
+ goto bad;
+ if (ioctl(d, TIOCGETD, (char *)&i) < 0)
+ goto bad;
+ if (t->ww_ldisc != i &&
+ ioctl(d, TIOCSETD, (char *)&t->ww_ldisc) < 0)
+ goto bad;
+#else
+#ifdef sun
+ /* XXX, for buggy tty drivers that don't wait for output to drain */
+ (void) tcdrain(d);
+#endif
+ if (tcsetattr(d, TCSADRAIN, &t->ww_termios) < 0)
+ goto bad;
+#endif
+ if (fcntl(d, F_SETFL, t->ww_fflags) < 0)
+ goto bad;
+ return 0;
+bad:
+ wwerrno = WWE_SYS;
+ return -1;
+}
+
+/*
+ * The ttysize and stop-start routines must also work
+ * on the control side of pseudoterminals.
+ */
+
+wwgetttysize(d, r, c)
+ int *r, *c;
+{
+ struct winsize winsize;
+
+ if (ioctl(d, TIOCGWINSZ, (char *)&winsize) < 0) {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ if (winsize.ws_row != 0)
+ *r = winsize.ws_row;
+ if (winsize.ws_col != 0)
+ *c = winsize.ws_col;
+ return 0;
+}
+
+wwsetttysize(d, r, c)
+{
+ struct winsize winsize;
+
+ winsize.ws_row = r;
+ winsize.ws_col = c;
+ winsize.ws_xpixel = winsize.ws_ypixel = 0;
+ if (ioctl(d, TIOCSWINSZ, (char *)&winsize) < 0) {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ return 0;
+}
+
+wwstoptty(d)
+{
+#if !defined(OLD_TTY) && defined(TCOOFF)
+ /* not guaranteed to work on the pty side */
+ if (tcflow(d, TCOOFF) < 0)
+#else
+ if (ioctl(d, TIOCSTOP, (char *)0) < 0)
+#endif
+ {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ return 0;
+}
+
+wwstarttty(d)
+{
+#if !defined(OLD_TTY) && defined(TCOON)
+ /* not guaranteed to work on the pty side */
+ if (tcflow(d, TCOON) < 0)
+#else
+ if (ioctl(d, TIOCSTART, (char *)0) < 0)
+#endif
+ {
+ wwerrno = WWE_SYS;
+ return -1;
+ }
+ return 0;
+}
diff --git a/usr.bin/window/wwunframe.c b/usr.bin/window/wwunframe.c
new file mode 100644
index 0000000..6945ffa
--- /dev/null
+++ b/usr.bin/window/wwunframe.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwunframe.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+
+wwunframe(w)
+register struct ww *w;
+{
+ int i;
+
+ for (i = w->ww_i.t; i < w->ww_i.b; i++) {
+ register j;
+ register char *win = w->ww_win[i];
+ register char *fmap = w->ww_fmap ? w->ww_fmap[i] : 0;
+ register char *smap = wwsmap[i];
+ register union ww_char *ns = wwns[i];
+ int nchanged = 0;
+
+ for (j = w->ww_i.l; j < w->ww_i.r; j++) {
+ if (win[j] & WWM_GLS)
+ continue;
+ win[j] |= WWM_GLS;
+ if (fmap != 0)
+ fmap[j] = 0;
+ if (smap[j] == w->ww_index) {
+ smap[j] = WWX_NOBODY;
+ ns[j].c_w = ' ';
+ nchanged++;
+ }
+ }
+ if (nchanged > 0)
+ wwtouched[i] |= WWU_TOUCHED;
+ w->ww_nvis[i] = 0;
+ }
+
+ if (w->ww_forw != &wwhead)
+ wwdelete1(w->ww_forw,
+ w->ww_i.t, w->ww_i.b, w->ww_i.l, w->ww_i.r);
+}
diff --git a/usr.bin/window/wwupdate.c b/usr.bin/window/wwupdate.c
new file mode 100644
index 0000000..f13453d
--- /dev/null
+++ b/usr.bin/window/wwupdate.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwupdate.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+
+wwupdate1(top, bot)
+{
+ int i;
+ register j;
+ char *touched;
+ struct ww_update *upd;
+ char check_clreos = 0;
+ int scan_top, scan_bot;
+
+ wwnupdate++;
+ {
+ register char *t1 = wwtouched + top, *t2 = wwtouched + bot;
+ register n;
+
+ while (!*t1++)
+ if (t1 == t2)
+ return;
+ while (!*--t2)
+ ;
+ scan_top = top = t1 - wwtouched - 1;
+ scan_bot = bot = t2 - wwtouched + 1;
+ if (scan_bot - scan_top > 1 &&
+ (tt.tt_clreos != 0 || tt.tt_clear != 0)) {
+ int st = tt.tt_clreos != 0 ? scan_top : 0;
+
+ /*
+ * t1 is one past the first touched row,
+ * t2 is on the last touched row.
+ */
+ for (t1--, n = 1; t1 < t2;)
+ if (*t1++)
+ n++;
+ /*
+ * If we can't clreos then we try for clearing
+ * the whole screen.
+ */
+ if (check_clreos = n * 10 > (wwnrow - st) * 9) {
+ scan_top = st;
+ scan_bot = wwnrow;
+ }
+ }
+ }
+ if (tt.tt_clreol == 0 && !check_clreos)
+ goto simple;
+ for (i = scan_top, touched = &wwtouched[i], upd = &wwupd[i];
+ i < scan_bot;
+ i++, touched++, upd++) {
+ register gain = 0;
+ register best_gain = 0;
+ register best_col;
+ register union ww_char *ns, *os;
+
+ if (wwinterrupt())
+ return;
+ if (!check_clreos && !*touched)
+ continue;
+ wwnupdscan++;
+ j = wwncol;
+ ns = &wwns[i][j];
+ os = &wwos[i][j];
+ while (--j >= 0) {
+ /*
+ * The cost of clearing is:
+ * ncol - nblank + X
+ * The cost of straight update is, more or less:
+ * ncol - nsame
+ * We clear if nblank - nsame > X
+ * X is the clreol overhead.
+ * So we make gain = nblank - nsame.
+ */
+ if ((--ns)->c_w == (--os)->c_w)
+ gain--;
+ else
+ best_gain--;
+ if (ns->c_w == ' ')
+ gain++;
+ if (gain > best_gain) {
+ best_col = j;
+ best_gain = gain;
+ }
+ }
+ upd->best_gain = best_gain;
+ upd->best_col = best_col;
+ upd->gain = gain;
+ }
+ if (check_clreos) {
+ register struct ww_update *u;
+ register gain = 0;
+ register best_gain = 0;
+ int best_row;
+ register simple_gain = 0;
+ char didit = 0;
+
+ /*
+ * gain is the advantage of clearing all the lines.
+ * best_gain is the advantage of clearing to eos
+ * at best_row and u->best_col.
+ * simple_gain is the advantage of using only clreol.
+ * We use g > best_gain because u->best_col can be
+ * undefined when u->best_gain is 0 so we can't use it.
+ */
+ for (j = scan_bot - 1, u = wwupd + j; j >= top; j--, u--) {
+ register g = gain + u->best_gain;
+
+ if (g > best_gain) {
+ best_gain = g;
+ best_row = j;
+ }
+ gain += u->gain;
+ if (tt.tt_clreol != 0 && u->best_gain > 4)
+ simple_gain += u->best_gain - 4;
+ }
+ if (tt.tt_clreos == 0) {
+ if (gain > simple_gain && gain > 4) {
+ xxclear();
+ i = top = scan_top;
+ bot = scan_bot;
+ j = 0;
+ didit = 1;
+ }
+ } else
+ if (best_gain > simple_gain && best_gain > 4) {
+ i = best_row;
+ xxclreos(i, j = wwupd[i].best_col);
+ bot = scan_bot;
+ didit = 1;
+ }
+ if (didit) {
+ wwnupdclreos++;
+ wwnupdclreosline += wwnrow - i;
+ u = wwupd + i;
+ while (i < scan_bot) {
+ register union ww_char *os = &wwos[i][j];
+
+ for (j = wwncol - j; --j >= 0;)
+ os++->c_w = ' ';
+ wwtouched[i++] |= WWU_TOUCHED;
+ u++->best_gain = 0;
+ j = 0;
+ }
+ } else
+ wwnupdclreosmiss++;
+ }
+simple:
+ for (i = top, touched = &wwtouched[i], upd = &wwupd[i]; i < bot;
+ i++, touched++, upd++) {
+ register union ww_char *os, *ns;
+ char didit;
+
+ if (!*touched)
+ continue;
+ *touched = 0;
+ wwnupdline++;
+ didit = 0;
+ if (tt.tt_clreol != 0 && upd->best_gain > 4) {
+ wwnupdclreol++;
+ xxclreol(i, j = upd->best_col);
+ for (os = &wwos[i][j], j = wwncol - j; --j >= 0;)
+ os++->c_w = ' ';
+ didit = 1;
+ }
+ ns = wwns[i];
+ os = wwos[i];
+ for (j = 0; j < wwncol;) {
+ register char *p, *q;
+ char m;
+ int c;
+ register n;
+ char buf[512]; /* > wwncol */
+ union ww_char lastc;
+
+ for (; j++ < wwncol && ns++->c_w == os++->c_w;)
+ ;
+ if (j > wwncol)
+ break;
+ p = buf;
+ m = ns[-1].c_m;
+ c = j - 1;
+ os[-1] = ns[-1];
+ *p++ = ns[-1].c_c;
+ n = 5;
+ q = p;
+ while (j < wwncol && ns->c_m == m) {
+ *p++ = ns->c_c;
+ if (ns->c_w == os->c_w) {
+ if (--n <= 0)
+ break;
+ os++;
+ ns++;
+ } else {
+ n = 5;
+ q = p;
+ lastc = *os;
+ *os++ = *ns++;
+ }
+ j++;
+ }
+ n = q - buf;
+ if (!wwwrap || i != wwnrow - 1 || c + n != wwncol)
+ xxwrite(i, c, buf, n, m);
+ else if (tt.tt_inschar || tt.tt_insspace) {
+ if (n > 1) {
+ q[-2] = q[-1];
+ n--;
+ } else
+ c--;
+ xxwrite(i, c, buf, n, m);
+ c += n - 1;
+ if (tt.tt_inschar)
+ xxinschar(i, c, ns[-2].c_c,
+ ns[-2].c_m);
+ else {
+ xxinsspace(i, c);
+ xxwrite(i, c, &ns[-2].c_c, 1,
+ ns[-2].c_m);
+ }
+ } else {
+ if (--n)
+ xxwrite(i, c, buf, n, m);
+ os[-1] = lastc;
+ *touched = WWU_TOUCHED;
+ }
+ didit = 1;
+ }
+ if (!didit)
+ wwnupdmiss++;
+ }
+}
diff --git a/usr.bin/window/wwwrite.c b/usr.bin/window/wwwrite.c
new file mode 100644
index 0000000..9880460
--- /dev/null
+++ b/usr.bin/window/wwwrite.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)wwwrite.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "tt.h"
+#include "char.h"
+
+#define UPDATE() \
+ if (!w->ww_noupdate && w->ww_cur.r >= 0 && w->ww_cur.r < wwnrow && \
+ wwtouched[w->ww_cur.r]) \
+ wwupdate1(w->ww_cur.r, w->ww_cur.r + 1)
+
+/*
+ * To support control character expansion, we save the old
+ * p and q values in r and s, and point p at the beginning
+ * of the expanded string, and q at some safe place beyond it
+ * (p + 10). At strategic points in the loops, we check
+ * for (r && !*p) and restore the saved values back into
+ * p and q. Essentially, we implement a stack of depth 2,
+ * to avoid recursion, which might be a better idea.
+ */
+wwwrite(w, p, n)
+register struct ww *w;
+register unsigned char *p;
+int n;
+{
+ char hascursor;
+ unsigned char *savep = p;
+ unsigned char *q = p + n;
+ char *r = 0;
+ char *s;
+
+#ifdef lint
+ s = 0; /* define it before possible use */
+#endif
+ if (hascursor = w->ww_hascursor)
+ wwcursor(w, 0);
+ while (p < q && !w->ww_stopped && (!wwinterrupt() || w->ww_nointr)) {
+ if (r && !*p) {
+ p = r;
+ q = s;
+ r = 0;
+ continue;
+ }
+ if (w->ww_wstate == 0 &&
+ (isprt(*p) || w->ww_unctrl && isunctrl(*p))) {
+ register i;
+ register union ww_char *bp;
+ int col, col1;
+
+ if (w->ww_insert) { /* this is very slow */
+ if (*p == '\t') {
+ p++;
+ w->ww_cur.c += 8 -
+ (w->ww_cur.c - w->ww_w.l & 7);
+ goto chklf;
+ }
+ if (!isprt(*p)) {
+ r = p + 1;
+ s = q;
+ p = unctrl(*p);
+ q = p + 10;
+ }
+ wwinschar(w, w->ww_cur.r, w->ww_cur.c,
+ *p++, w->ww_modes);
+ goto right;
+ }
+
+ bp = &w->ww_buf[w->ww_cur.r][w->ww_cur.c];
+ i = w->ww_cur.c;
+ while (i < w->ww_w.r && p < q)
+ if (!*p && r) {
+ p = r;
+ q = s;
+ r = 0;
+ } else if (*p == '\t') {
+ register tmp = 8 - (i - w->ww_w.l & 7);
+ p++;
+ i += tmp;
+ bp += tmp;
+ } else if (isprt(*p)) {
+ bp++->c_w = *p++
+ | w->ww_modes << WWC_MSHIFT;
+ i++;
+ } else if (w->ww_unctrl && isunctrl(*p)) {
+ r = p + 1;
+ s = q;
+ p = unctrl(*p);
+ q = p + 10;
+ } else
+ break;
+ col = MAX(w->ww_cur.c, w->ww_i.l);
+ col1 = MIN(i, w->ww_i.r);
+ w->ww_cur.c = i;
+ if (w->ww_cur.r >= w->ww_i.t
+ && w->ww_cur.r < w->ww_i.b) {
+ register union ww_char *ns = wwns[w->ww_cur.r];
+ register char *smap = &wwsmap[w->ww_cur.r][col];
+ register char *win = w->ww_win[w->ww_cur.r];
+ int nchanged = 0;
+
+ bp = w->ww_buf[w->ww_cur.r];
+ for (i = col; i < col1; i++)
+ if (*smap++ == w->ww_index) {
+ nchanged++;
+ ns[i].c_w = bp[i].c_w
+ ^ win[i] << WWC_MSHIFT;
+ }
+ if (nchanged > 0)
+ wwtouched[w->ww_cur.r] |= WWU_TOUCHED;
+ }
+ chklf:
+ if (w->ww_cur.c >= w->ww_w.r)
+ goto crlf;
+ } else switch (w->ww_wstate) {
+ case 0:
+ switch (*p++) {
+ case '\n':
+ if (w->ww_mapnl)
+ crlf:
+ w->ww_cur.c = w->ww_w.l;
+ lf:
+ UPDATE();
+ if (++w->ww_cur.r >= w->ww_w.b) {
+ w->ww_cur.r = w->ww_w.b - 1;
+ if (w->ww_w.b < w->ww_b.b) {
+ (void) wwscroll1(w, w->ww_i.t,
+ w->ww_i.b, 1, 0);
+ w->ww_buf++;
+ w->ww_b.t--;
+ w->ww_b.b--;
+ } else
+ wwdelline(w, w->ww_b.t);
+ }
+ break;
+ case '\b':
+ if (--w->ww_cur.c < w->ww_w.l) {
+ w->ww_cur.c = w->ww_w.r - 1;
+ goto up;
+ }
+ break;
+ case '\r':
+ w->ww_cur.c = w->ww_w.l;
+ break;
+ case ctrl('g'):
+ ttputc(ctrl('g'));
+ break;
+ case ctrl('['):
+ w->ww_wstate = 1;
+ break;
+ }
+ break;
+ case 1:
+ w->ww_wstate = 0;
+ switch (*p++) {
+ case '@':
+ w->ww_insert = 1;
+ break;
+ case 'A':
+ up:
+ UPDATE();
+ if (--w->ww_cur.r < w->ww_w.t) {
+ w->ww_cur.r = w->ww_w.t;
+ if (w->ww_w.t > w->ww_b.t) {
+ (void) wwscroll1(w, w->ww_i.t,
+ w->ww_i.b, -1, 0);
+ w->ww_buf--;
+ w->ww_b.t++;
+ w->ww_b.b++;
+ } else
+ wwinsline(w, w->ww_b.t);
+ }
+ break;
+ case 'B':
+ goto lf;
+ case 'C':
+ right:
+ w->ww_cur.c++;
+ goto chklf;
+ case 'E':
+ w->ww_buf -= w->ww_w.t - w->ww_b.t;
+ w->ww_b.t = w->ww_w.t;
+ w->ww_b.b = w->ww_b.t + w->ww_b.nr;
+ w->ww_cur.r = w->ww_w.t;
+ w->ww_cur.c = w->ww_w.l;
+ wwclreos(w, w->ww_w.t, w->ww_w.l);
+ break;
+ case 'H':
+ UPDATE();
+ w->ww_cur.r = w->ww_w.t;
+ w->ww_cur.c = w->ww_w.l;
+ break;
+ case 'J':
+ wwclreos(w, w->ww_cur.r, w->ww_cur.c);
+ break;
+ case 'K':
+ wwclreol(w, w->ww_cur.r, w->ww_cur.c);
+ break;
+ case 'L':
+ UPDATE();
+ wwinsline(w, w->ww_cur.r);
+ break;
+ case 'M':
+ wwdelline(w, w->ww_cur.r);
+ break;
+ case 'N':
+ wwdelchar(w, w->ww_cur.r, w->ww_cur.c);
+ break;
+ case 'O':
+ w->ww_insert = 0;
+ break;
+ case 'P':
+ wwinschar(w, w->ww_cur.r, w->ww_cur.c, ' ', 0);
+ break;
+ case 'X':
+ wwupdate();
+ break;
+ case 'Y':
+ UPDATE();
+ w->ww_wstate = 2;
+ break;
+ case 'Z':
+ wwupdate();
+ xxflush(0);
+ break;
+ case 's':
+ w->ww_wstate = 4;
+ break;
+ case 'r':
+ w->ww_wstate = 5;
+ break;
+ }
+ break;
+ case 2:
+ w->ww_cur.r = w->ww_w.t +
+ (unsigned)(*p++ - ' ') % w->ww_w.nr;
+ w->ww_wstate = 3;
+ break;
+ case 3:
+ w->ww_cur.c = w->ww_w.l +
+ (unsigned)(*p++ - ' ') % w->ww_w.nc;
+ w->ww_wstate = 0;
+ break;
+ case 4:
+ w->ww_modes |= *p++ & wwavailmodes;
+ w->ww_wstate = 0;
+ break;
+ case 5:
+ w->ww_modes &= ~*p++;
+ w->ww_wstate = 0;
+ break;
+ }
+ }
+ if (hascursor)
+ wwcursor(w, 1);
+ wwnwwr++;
+ wwnwwra += n;
+ n = p - savep;
+ wwnwwrc += n;
+ return n;
+}
diff --git a/usr.bin/window/xx.c b/usr.bin/window/xx.c
new file mode 100644
index 0000000..a5a1cc1
--- /dev/null
+++ b/usr.bin/window/xx.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)xx.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "xx.h"
+#include "tt.h"
+#include <stdlib.h>
+
+xxinit()
+{
+ if (ttinit() < 0)
+ return -1;
+ xxbufsize = tt.tt_nrow * tt.tt_ncol * 2;
+ /* ccinit may choose to change xxbufsize */
+ if (tt.tt_ntoken > 0 && ccinit() < 0)
+ return -1;
+ xxbuf = malloc((unsigned) xxbufsize * sizeof *xxbuf);
+ if (xxbuf == 0) {
+ wwerrno = WWE_NOMEM;
+ return -1;
+ }
+ xxbufp = xxbuf;
+ xxbufe = xxbuf + xxbufsize;
+ return 0;
+}
+
+xxstart()
+{
+ (*tt.tt_start)();
+ if (tt.tt_ntoken > 0)
+ ccstart();
+ xxreset1(); /* might be a restart */
+}
+
+xxreset()
+{
+ if (tt.tt_ntoken > 0)
+ ccreset();
+ xxreset1();
+ (*tt.tt_reset)();
+}
+
+xxreset1()
+{
+ register struct xx *xp, *xq;
+
+ for (xp = xx_head; xp != 0; xp = xq) {
+ xq = xp->link;
+ xxfree(xp);
+ }
+ xx_tail = xx_head = 0;
+ xxbufp = xxbuf;
+}
+
+xxend()
+{
+ if (tt.tt_scroll_top != 0 || tt.tt_scroll_bot != tt.tt_nrow - 1)
+ /* tt.tt_setscroll is known to be defined */
+ (*tt.tt_setscroll)(0, tt.tt_nrow - 1);
+ if (tt.tt_modes)
+ (*tt.tt_setmodes)(0);
+ if (tt.tt_scroll_down)
+ (*tt.tt_scroll_down)(1);
+ (*tt.tt_move)(tt.tt_nrow - 1, 0);
+ if (tt.tt_ntoken > 0)
+ ccend();
+ (*tt.tt_end)();
+ ttflush();
+}
+
+struct xx *
+xxalloc()
+{
+ register struct xx *xp;
+
+ if (xxbufp > xxbufe)
+ abort();
+ if ((xp = xx_freelist) == 0)
+ /* XXX can't deal with failure */
+ xp = (struct xx *) malloc((unsigned) sizeof *xp);
+ else
+ xx_freelist = xp->link;
+ if (xx_head == 0)
+ xx_head = xp;
+ else
+ xx_tail->link = xp;
+ xx_tail = xp;
+ xp->link = 0;
+ return xp;
+}
+
+xxfree(xp)
+ register struct xx *xp;
+{
+ xp->link = xx_freelist;
+ xx_freelist = xp;
+}
+
+xxmove(row, col)
+{
+ register struct xx *xp = xx_tail;
+
+ if (xp == 0 || xp->cmd != xc_move) {
+ xp = xxalloc();
+ xp->cmd = xc_move;
+ }
+ xp->arg0 = row;
+ xp->arg1 = col;
+}
+
+xxscroll(dir, top, bot)
+{
+ register struct xx *xp = xx_tail;
+
+ if (xp != 0 && xp->cmd == xc_scroll &&
+ xp->arg1 == top && xp->arg2 == bot &&
+ (xp->arg0 < 0 && dir < 0 || xp->arg0 > 0 && dir > 0)) {
+ xp->arg0 += dir;
+ return;
+ }
+ xp = xxalloc();
+ xp->cmd = xc_scroll;
+ xp->arg0 = dir;
+ xp->arg1 = top;
+ xp->arg2 = bot;
+}
+
+xxinschar(row, col, c, m)
+{
+ register struct xx *xp;
+
+ xp = xxalloc();
+ xp->cmd = xc_inschar;
+ xp->arg0 = row;
+ xp->arg1 = col;
+ xp->arg2 = c;
+ xp->arg3 = m;
+}
+
+xxinsspace(row, col)
+{
+ register struct xx *xp = xx_tail;
+
+ if (xp != 0 && xp->cmd == xc_insspace && xp->arg0 == row &&
+ col >= xp->arg1 && col <= xp->arg1 + xp->arg2) {
+ xp->arg2++;
+ return;
+ }
+ xp = xxalloc();
+ xp->cmd = xc_insspace;
+ xp->arg0 = row;
+ xp->arg1 = col;
+ xp->arg2 = 1;
+}
+
+xxdelchar(row, col)
+{
+ register struct xx *xp = xx_tail;
+
+ if (xp != 0 && xp->cmd == xc_delchar &&
+ xp->arg0 == row && xp->arg1 == col) {
+ xp->arg2++;
+ return;
+ }
+ xp = xxalloc();
+ xp->cmd = xc_delchar;
+ xp->arg0 = row;
+ xp->arg1 = col;
+ xp->arg2 = 1;
+}
+
+xxclear()
+{
+ register struct xx *xp;
+
+ xxreset1();
+ xp = xxalloc();
+ xp->cmd = xc_clear;
+}
+
+xxclreos(row, col)
+{
+ register struct xx *xp = xxalloc();
+
+ xp->cmd = xc_clreos;
+ xp->arg0 = row;
+ xp->arg1 = col;
+}
+
+xxclreol(row, col)
+{
+ register struct xx *xp = xxalloc();
+
+ xp->cmd = xc_clreol;
+ xp->arg0 = row;
+ xp->arg1 = col;
+}
+
+xxwrite(row, col, p, n, m)
+ char *p;
+{
+ register struct xx *xp;
+
+ if (xxbufp + n + 1 > xxbufe)
+ xxflush(0);
+ xp = xxalloc();
+ xp->cmd = xc_write;
+ xp->arg0 = row;
+ xp->arg1 = col;
+ xp->arg2 = n;
+ xp->arg3 = m;
+ xp->buf = xxbufp;
+ bcopy(p, xxbufp, n);
+ xxbufp += n;
+ *xxbufp++ = char_sep;
+}
diff --git a/usr.bin/window/xx.h b/usr.bin/window/xx.h
new file mode 100644
index 0000000..2c54589
--- /dev/null
+++ b/usr.bin/window/xx.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)xx.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct xx {
+ enum { xc_move, xc_scroll, xc_inschar, xc_insspace, xc_delchar,
+ xc_clear, xc_clreos, xc_clreol, xc_write } cmd;
+ int arg0;
+ int arg1;
+ int arg2;
+ int arg3;
+ char *buf;
+ struct xx *link;
+};
+
+struct xx *xxalloc();
+
+struct xx *xx_head, *xx_tail;
+struct xx *xx_freelist;
+
+char *xxbuf, *xxbufp, *xxbufe;
+int xxbufsize;
+
+#define char_sep '\0'
diff --git a/usr.bin/window/xxflush.c b/usr.bin/window/xxflush.c
new file mode 100644
index 0000000..5b09579
--- /dev/null
+++ b/usr.bin/window/xxflush.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Edward Wang at The University of California, Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)xxflush.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "ww.h"
+#include "xx.h"
+#include "tt.h"
+
+xxflush(intr)
+ register intr;
+{
+ register struct xx *xp, *xq;
+
+ for (xp = xx_head; xp != 0 && !(intr && wwinterrupt()); xp = xq) {
+ switch (xp->cmd) {
+ case xc_move:
+ if (xp->link == 0)
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ break;
+ case xc_scroll:
+ xxflush_scroll(xp);
+ break;
+ case xc_inschar:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ tt.tt_nmodes = xp->arg3;
+ (*tt.tt_inschar)(xp->arg2);
+ break;
+ case xc_insspace:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ (*tt.tt_insspace)(xp->arg2);
+ break;
+ case xc_delchar:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ (*tt.tt_delchar)(xp->arg2);
+ break;
+ case xc_clear:
+ (*tt.tt_clear)();
+ break;
+ case xc_clreos:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ (*tt.tt_clreos)();
+ break;
+ case xc_clreol:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ (*tt.tt_clreol)();
+ break;
+ case xc_write:
+ (*tt.tt_move)(xp->arg0, xp->arg1);
+ tt.tt_nmodes = xp->arg3;
+ (*tt.tt_write)(xp->buf, xp->arg2);
+ break;
+ }
+ xq = xp->link;
+ xxfree(xp);
+ }
+ if ((xx_head = xp) == 0) {
+ xx_tail = 0;
+ xxbufp = xxbuf;
+ }
+ ttflush();
+}
+
+xxflush_scroll(xp)
+ register struct xx *xp;
+{
+ register struct xx *xq;
+
+ top:
+ if (xp->arg0 == 0)
+ return;
+ /*
+ * We handle retain (da and db) by putting the burden on scrolling up,
+ * which is the less common operation. It must ensure that
+ * text is not pushed below the screen, so scrolling down doesn't
+ * have to worry about it.
+ *
+ * Try scrolling region (or scrolling the whole screen) first.
+ * Can we assume "sr" doesn't push text below the screen
+ * so we don't have to worry about retain below?
+ * What about scrolling down with a newline? It probably does
+ * push text above (with da). Scrolling up would then have
+ * to take care of that.
+ * It's easy to be fool proof, but that slows things down.
+ * The current solution is to disallow tt_scroll_up if da or db is true
+ * but cs (scrolling region) is not. Again, we sacrifice scrolling
+ * up in favor of scrolling down. The idea is having scrolling regions
+ * probably means we can scroll (even the whole screen) with impunity.
+ * This lets us work efficiently on simple terminals (use newline
+ * on the bottom to scroll), on any terminal without retain, and
+ * on vt100 style scrolling regions (I think).
+ */
+ if (xp->arg0 > 0) {
+ if ((xq = xp->link) != 0 && xq->cmd == xc_scroll &&
+ xp->arg2 == xq->arg2 && xq->arg0 < 0) {
+ if (xp->arg1 < xq->arg1) {
+ if (xp->arg2 - xp->arg0 <= xq->arg1) {
+ xq->arg0 = xp->arg0;
+ xq->arg1 = xp->arg1;
+ xq->arg2 = xp->arg2;
+ return;
+ }
+ xp->arg2 = xq->arg1 + xp->arg0;
+ xq->arg0 += xp->arg0;
+ xq->arg1 = xp->arg2;
+ if (xq->arg0 > 0)
+ xq->arg1 -= xq->arg0;
+ goto top;
+ } else {
+ if (xp->arg1 - xq->arg0 >= xp->arg2)
+ return;
+ xq->arg2 = xp->arg1 - xq->arg0;
+ xp->arg0 += xq->arg0;
+ xp->arg1 = xq->arg2;
+ if (xp->arg0 < 0)
+ xp->arg1 += xp->arg0;
+ goto top;
+ }
+ }
+ if (xp->arg0 > xp->arg2 - xp->arg1)
+ xp->arg0 = xp->arg2 - xp->arg1;
+ if (tt.tt_scroll_down) {
+ if (tt.tt_scroll_top != xp->arg1 ||
+ tt.tt_scroll_bot != xp->arg2 - 1) {
+ if (tt.tt_setscroll == 0)
+ goto down;
+ (*tt.tt_setscroll)(xp->arg1, xp->arg2 - 1);
+ }
+ tt.tt_scroll_down(xp->arg0);
+ } else {
+ down:
+ (*tt.tt_move)(xp->arg1, 0);
+ (*tt.tt_delline)(xp->arg0);
+ if (xp->arg2 < tt.tt_nrow) {
+ (*tt.tt_move)(xp->arg2 - xp->arg0, 0);
+ (*tt.tt_insline)(xp->arg0);
+ }
+ }
+ } else {
+ xp->arg0 = - xp->arg0;
+ if (xp->arg0 > xp->arg2 - xp->arg1)
+ xp->arg0 = xp->arg2 - xp->arg1;
+ if (tt.tt_scroll_up) {
+ if (tt.tt_scroll_top != xp->arg1 ||
+ tt.tt_scroll_bot != xp->arg2 - 1) {
+ if (tt.tt_setscroll == 0)
+ goto up;
+ (*tt.tt_setscroll)(xp->arg1, xp->arg2 - 1);
+ }
+ tt.tt_scroll_up(xp->arg0);
+ } else {
+ up:
+ if (tt.tt_retain || xp->arg2 != tt.tt_nrow) {
+ (*tt.tt_move)(xp->arg2 - xp->arg0, 0);
+ (*tt.tt_delline)(xp->arg0);
+ }
+ (*tt.tt_move)(xp->arg1, 0);
+ (*tt.tt_insline)(xp->arg0);
+ }
+ }
+}
diff --git a/usr.bin/write/Makefile b/usr.bin/write/Makefile
new file mode 100644
index 0000000..8e6454b
--- /dev/null
+++ b/usr.bin/write/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= write
+BINMODE=2555
+BINGRP= tty
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/write/write.1 b/usr.bin/write/write.1
new file mode 100644
index 0000000..89d7302
--- /dev/null
+++ b/usr.bin/write/write.1
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1989, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)write.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt WRITE 1
+.Os
+.Sh NAME
+.Nm write
+.Nd send a message to another user
+.Sh SYNOPSIS
+.Nm write
+.Ar user
+.Op Ar ttyname
+.Sh DESCRIPTION
+.Nm Write
+allows you to communicate with other users, by copying lines from
+your terminal to theirs.
+.Pp
+When you run the
+.Nm write
+command, the user you are writing to gets a message of the form:
+.Pp
+.Dl Message from yourname@yourhost on yourtty at hh:mm ...
+.Pp
+Any further lines you enter will be copied to the specified user's
+terminal.
+If the other user wants to reply, they must run
+.Nm write
+as well.
+.Pp
+When you are done, type an end-of-file or interrupt character.
+The other user will see the message
+.Ql EOF
+indicating that the
+conversation is over.
+.Pp
+You can prevent people (other than the super-user) from writing to you
+with the
+.Xr mesg 1
+command.
+Some commands, for example
+.Xr nroff 1
+and
+.Xr pr 1 ,
+disallow writing automatically, so that your output isn't overwritten.
+.Pp
+If the user you want to write to is logged in on more than one terminal,
+you can specify which terminal to write to by specifying the terminal
+name as the second operand to the
+.Nm write
+command.
+Alternatively, you can let
+.Nm write
+select one of the terminals \- it will pick the one with the shortest
+idle time.
+This is so that if the user is logged in at work and also dialed up from
+home, the message will go to the right place.
+.Pp
+The traditional protocol for writing to someone is that the string
+.Ql \-o ,
+either at the end of a line or on a line by itself, means that it's the
+other person's turn to talk.
+The string
+.Ql oo
+means that the person believes the conversation to be
+over.
+.Sh SEE ALSO
+.Xr mesg 1 ,
+.Xr talk 1 ,
+.Xr who 1
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c
new file mode 100644
index 0000000..7d99f21
--- /dev/null
+++ b/usr.bin/write/write.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)write.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <utmp.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+extern int errno;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register char *cp;
+ time_t atime;
+ uid_t myuid;
+ int msgsok, myttyfd;
+ char tty[MAXPATHLEN], *mytty, *ttyname();
+ void done();
+
+ /* check that sender has write enabled */
+ if (isatty(fileno(stdin)))
+ myttyfd = fileno(stdin);
+ else if (isatty(fileno(stdout)))
+ myttyfd = fileno(stdout);
+ else if (isatty(fileno(stderr)))
+ myttyfd = fileno(stderr);
+ else {
+ (void)fprintf(stderr, "write: can't find your tty\n");
+ exit(1);
+ }
+ if (!(mytty = ttyname(myttyfd))) {
+ (void)fprintf(stderr, "write: can't find your tty's name\n");
+ exit(1);
+ }
+ if (cp = rindex(mytty, '/'))
+ mytty = cp + 1;
+ if (term_chk(mytty, &msgsok, &atime, 1))
+ exit(1);
+ if (!msgsok) {
+ (void)fprintf(stderr,
+ "write: you have write permission turned off.\n");
+ exit(1);
+ }
+
+ myuid = getuid();
+
+ /* check args */
+ switch (argc) {
+ case 2:
+ search_utmp(argv[1], tty, mytty, myuid);
+ do_write(tty, mytty, myuid);
+ break;
+ case 3:
+ if (!strncmp(argv[2], "/dev/", 5))
+ argv[2] += 5;
+ if (utmp_chk(argv[1], argv[2])) {
+ (void)fprintf(stderr,
+ "write: %s is not logged in on %s.\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+ if (term_chk(argv[2], &msgsok, &atime, 1))
+ exit(1);
+ if (myuid && !msgsok) {
+ (void)fprintf(stderr,
+ "write: %s has messages disabled on %s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+ do_write(argv[2], mytty, myuid);
+ break;
+ default:
+ (void)fprintf(stderr, "usage: write user [tty]\n");
+ exit(1);
+ }
+ done();
+ /* NOTREACHED */
+}
+
+/*
+ * utmp_chk - checks that the given user is actually logged in on
+ * the given tty
+ */
+utmp_chk(user, tty)
+ char *user, *tty;
+{
+ struct utmp u;
+ int ufd;
+
+ if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
+ return(0); /* ignore error, shouldn't happen anyway */
+
+ while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
+ if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
+ strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
+ (void)close(ufd);
+ return(0);
+ }
+
+ (void)close(ufd);
+ return(1);
+}
+
+/*
+ * search_utmp - search utmp for the "best" terminal to write to
+ *
+ * Ignores terminals with messages disabled, and of the rest, returns
+ * the one with the most recent access time. Returns as value the number
+ * of the user's terminals with messages enabled, or -1 if the user is
+ * not logged in at all.
+ *
+ * Special case for writing to yourself - ignore the terminal you're
+ * writing from, unless that's the only terminal with messages enabled.
+ */
+search_utmp(user, tty, mytty, myuid)
+ char *user, *tty, *mytty;
+ uid_t myuid;
+{
+ struct utmp u;
+ time_t bestatime, atime;
+ int ufd, nloggedttys, nttys, msgsok, user_is_me;
+ char atty[UT_LINESIZE + 1];
+
+ if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
+ perror("utmp");
+ exit(1);
+ }
+
+ nloggedttys = nttys = 0;
+ bestatime = 0;
+ user_is_me = 0;
+ while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
+ if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
+ ++nloggedttys;
+ (void)strncpy(atty, u.ut_line, UT_LINESIZE);
+ atty[UT_LINESIZE] = '\0';
+ if (term_chk(atty, &msgsok, &atime, 0))
+ continue; /* bad term? skip */
+ if (myuid && !msgsok)
+ continue; /* skip ttys with msgs off */
+ if (strcmp(atty, mytty) == 0) {
+ user_is_me = 1;
+ continue; /* don't write to yourself */
+ }
+ ++nttys;
+ if (atime > bestatime) {
+ bestatime = atime;
+ (void)strcpy(tty, atty);
+ }
+ }
+
+ (void)close(ufd);
+ if (nloggedttys == 0) {
+ (void)fprintf(stderr, "write: %s is not logged in\n", user);
+ exit(1);
+ }
+ if (nttys == 0) {
+ if (user_is_me) { /* ok, so write to yourself! */
+ (void)strcpy(tty, mytty);
+ return;
+ }
+ (void)fprintf(stderr,
+ "write: %s has messages disabled\n", user);
+ exit(1);
+ } else if (nttys > 1) {
+ (void)fprintf(stderr,
+ "write: %s is logged in more than once; writing to %s\n",
+ user, tty);
+ }
+}
+
+/*
+ * term_chk - check that a terminal exists, and get the message bit
+ * and the access time
+ */
+term_chk(tty, msgsokP, atimeP, showerror)
+ char *tty;
+ int *msgsokP, showerror;
+ time_t *atimeP;
+{
+ struct stat s;
+ char path[MAXPATHLEN];
+
+ (void)sprintf(path, "/dev/%s", tty);
+ if (stat(path, &s) < 0) {
+ if (showerror)
+ (void)fprintf(stderr,
+ "write: %s: %s\n", path, strerror(errno));
+ return(1);
+ }
+ *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */
+ *atimeP = s.st_atime;
+ return(0);
+}
+
+/*
+ * do_write - actually make the connection
+ */
+do_write(tty, mytty, myuid)
+ char *tty, *mytty;
+ uid_t myuid;
+{
+ register char *login, *nows;
+ register struct passwd *pwd;
+ time_t now, time();
+ char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
+ void done();
+
+ /* Determine our login name before the we reopen() stdout */
+ if ((login = getlogin()) == NULL)
+ if (pwd = getpwuid(myuid))
+ login = pwd->pw_name;
+ else
+ login = "???";
+
+ (void)sprintf(path, "/dev/%s", tty);
+ if ((freopen(path, "w", stdout)) == NULL) {
+ (void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
+ exit(1);
+ }
+
+ (void)signal(SIGINT, done);
+ (void)signal(SIGHUP, done);
+
+ /* print greeting */
+ if (gethostname(host, sizeof(host)) < 0)
+ (void)strcpy(host, "???");
+ now = time((time_t *)NULL);
+ nows = ctime(&now);
+ nows[16] = '\0';
+ (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
+ login, host, mytty, nows + 11);
+
+ while (fgets(line, sizeof(line), stdin) != NULL)
+ wr_fputs(line);
+}
+
+/*
+ * done - cleanup and exit
+ */
+void
+done()
+{
+ (void)printf("EOF\r\n");
+ exit(0);
+}
+
+/*
+ * wr_fputs - like fputs(), but makes control characters visible and
+ * turns \n into \r\n
+ */
+wr_fputs(s)
+ register char *s;
+{
+ register char c;
+
+#define PUTC(c) if (putchar(c) == EOF) goto err;
+
+ for (; *s != '\0'; ++s) {
+ c = toascii(*s);
+ if (c == '\n') {
+ PUTC('\r');
+ PUTC('\n');
+ } else if (!isprint(c) && !isspace(c) && c != '\007') {
+ PUTC('^');
+ PUTC(c^0x40); /* DEL to ?, others to alpha */
+ } else
+ PUTC(c);
+ }
+ return;
+
+err: (void)fprintf(stderr, "write: %s\n", strerror(errno));
+ exit(1);
+#undef PUTC
+}
diff --git a/usr.bin/xargs/Makefile b/usr.bin/xargs/Makefile
new file mode 100644
index 0000000..0bfb074
--- /dev/null
+++ b/usr.bin/xargs/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= xargs
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xargs/pathnames.h b/usr.bin/xargs/pathnames.h
new file mode 100644
index 0000000..3f05b3d
--- /dev/null
+++ b/usr.bin/xargs/pathnames.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_ECHO "/bin/echo"
diff --git a/usr.bin/xargs/xargs.1 b/usr.bin/xargs/xargs.1
new file mode 100644
index 0000000..e703c43
--- /dev/null
+++ b/usr.bin/xargs/xargs.1
@@ -0,0 +1,161 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" John B. Roll Jr. and the Institute of Electrical and Electronics
+.\" Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)xargs.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt XARGS 1
+.Os
+.Sh NAME
+.Nm xargs
+.Nd "construct argument list(s) and execute utility"
+.Sh SYNOPSIS
+.Nm xargs
+.Op Fl t
+.Oo Op Fl x
+.Fl n Ar number
+.Oc
+.Op Fl s Ar size
+.Op Ar utility Op Ar arguments ...
+.Sh DESCRIPTION
+The
+.Nm xargs
+utility reads space, tab, newline and end-of-file delimited arguments
+from the standard input and executes the specified
+.Ar utility
+with them as
+arguments.
+.Pp
+The utility and any arguments specified on the command line are given
+to the
+.Ar utility
+upon each invocation, followed by some number of the arguments read
+from standard input.
+The
+.Ar utility
+is repeatedly executed until standard input is exhausted.
+.Pp
+Spaces, tabs and newlines may be embedded in arguments using single
+(``\ '\ '')
+.Ek
+or double (``"'') quotes or backslashes (``\e'').
+Single quotes escape all non-single quote characters, excluding newlines,
+up to the matching single quote.
+Double quotes escape all non-double quote characters, excluding newlines,
+up to the matching double quote.
+Any single character, including newlines, may be escaped by a backslash.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl n Ar number
+Set the maximum number of arguments taken from standard input for each
+invocation of the utility.
+An invocation of
+.Ar utility
+will use less than
+.Ar number
+standard input arguments if the number of bytes accumulated (see the
+.Fl s
+option) exceeds the specified
+.Ar size
+or there are fewer than
+.Ar number
+arguments remaining for the last invocation of
+.Ar utility .
+The current default value for
+.Ar number
+is 5000.
+.It Fl s Ar size
+Set the maximum number of bytes for the command line length provided to
+.Ar utility .
+The sum of the length of the utility name and the arguments passed to
+.Ar utility
+(including
+.Dv NULL
+terminators) will be less than or equal to this number.
+The current default value for
+.Ar size
+is
+.Dv ARG_MAX
+- 2048.
+.It Fl t
+Echo the command to be executed to standard error immediately before it
+is executed.
+.It Fl x
+Force
+.Nm xargs
+to terminate immediately if a command line containing
+.Ar number
+arguments will not fit in the specified (or default) command line length.
+.El
+.Pp
+If no
+.Ar utility
+is specified,
+.Xr echo 1
+is used.
+.Pp
+Undefined behavior may occur if
+.Ar utility
+reads from the standard input.
+.Pp
+The
+.Nm xargs
+utility exits immediately (without processing any further input) if a
+command line cannot be assembled,
+.Ar utility
+cannot be invoked, an invocation of the utility is terminated by a signal
+or an invocation of the utility exits with a value of 255.
+.Pp
+The
+.Nm xargs
+utility exits with a value of 0 if no error occurs.
+If
+.Ar utility
+cannot be invoked,
+.Nm xargs
+exits with a value of 127.
+If any other error occurs,
+.Nm xargs
+exits with a value of 1.
+.Sh SEE ALSO
+.Xr echo 1 ,
+.Xr find 1
+.Sh STANDARDS
+The
+.Nm xargs
+utility is expected to be
+.St -p1003.2
+compliant.
diff --git a/usr.bin/xargs/xargs.c b/usr.bin/xargs/xargs.c
new file mode 100644
index 0000000..e28e840
--- /dev/null
+++ b/usr.bin/xargs/xargs.c
@@ -0,0 +1,325 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * John B. Roll Jr.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include "pathnames.h"
+
+int tflag, rval;
+
+void err __P((const char *, ...));
+void run __P((char **));
+void usage __P((void));
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int ch;
+ register char *p, *bbp, *ebp, **bxp, **exp, **xp;
+ int cnt, indouble, insingle, nargs, nflag, nline, xflag;
+ char **av, *argp;
+
+ /*
+ * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that
+ * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given
+ * that the smallest argument is 2 bytes in length, this means that
+ * the number of arguments is limited to:
+ *
+ * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2.
+ *
+ * We arbitrarily limit the number of arguments to 5000. This is
+ * allowed by POSIX.2 as long as the resulting minimum exec line is
+ * at least LINE_MAX. Realloc'ing as necessary is possible, but
+ * probably not worthwhile.
+ */
+ nargs = 5000;
+ nline = ARG_MAX - 4 * 1024;
+ nflag = xflag = 0;
+ while ((ch = getopt(argc, argv, "n:s:tx")) != EOF)
+ switch(ch) {
+ case 'n':
+ nflag = 1;
+ if ((nargs = atoi(optarg)) <= 0)
+ err("illegal argument count");
+ break;
+ case 's':
+ nline = atoi(optarg);
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'x':
+ xflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (xflag && !nflag)
+ usage();
+
+ /*
+ * Allocate pointers for the utility name, the utility arguments,
+ * the maximum arguments to be read from stdin and the trailing
+ * NULL.
+ */
+ if (!(av = bxp =
+ malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **))))
+ err("%s", strerror(errno));
+
+ /*
+ * Use the user's name for the utility as argv[0], just like the
+ * shell. Echo is the default. Set up pointers for the user's
+ * arguments.
+ */
+ if (!*argv)
+ cnt = strlen(*bxp++ = _PATH_ECHO);
+ else {
+ cnt = 0;
+ do {
+ cnt += strlen(*bxp++ = *argv) + 1;
+ } while (*++argv);
+ }
+
+ /*
+ * Set up begin/end/traversing pointers into the array. The -n
+ * count doesn't include the trailing NULL pointer, so the malloc
+ * added in an extra slot.
+ */
+ exp = (xp = bxp) + nargs;
+
+ /*
+ * Allocate buffer space for the arguments read from stdin and the
+ * trailing NULL. Buffer space is defined as the default or specified
+ * space, minus the length of the utility name and arguments. Set up
+ * begin/end/traversing pointers into the array. The -s count does
+ * include the trailing NULL, so the malloc didn't add in an extra
+ * slot.
+ */
+ nline -= cnt;
+ if (nline <= 0)
+ err("insufficient space for command");
+
+ if (!(bbp = malloc((u_int)nline + 1)))
+ err("%s", strerror(errno));
+ ebp = (argp = p = bbp) + nline - 1;
+
+ for (insingle = indouble = 0;;)
+ switch(ch = getchar()) {
+ case EOF:
+ /* No arguments since last exec. */
+ if (p == bbp)
+ exit(rval);
+
+ /* Nothing since end of last argument. */
+ if (argp == p) {
+ *xp = NULL;
+ run(av);
+ exit(rval);
+ }
+ goto arg1;
+ case ' ':
+ case '\t':
+ /* Quotes escape tabs and spaces. */
+ if (insingle || indouble)
+ goto addch;
+ goto arg2;
+ case '\n':
+ /* Empty lines are skipped. */
+ if (argp == p)
+ continue;
+
+ /* Quotes do not escape newlines. */
+arg1: if (insingle || indouble)
+ err("unterminated quote");
+
+arg2: *p = '\0';
+ *xp++ = argp;
+
+ /*
+ * If max'd out on args or buffer, or reached EOF,
+ * run the command. If xflag and max'd out on buffer
+ * but not on args, object.
+ */
+ if (xp == exp || p == ebp || ch == EOF) {
+ if (xflag && xp != exp && p == ebp)
+ err("insufficient space for arguments");
+ *xp = NULL;
+ run(av);
+ if (ch == EOF)
+ exit(rval);
+ p = bbp;
+ xp = bxp;
+ } else
+ ++p;
+ argp = p;
+ break;
+ case '\'':
+ if (indouble)
+ goto addch;
+ insingle = !insingle;
+ break;
+ case '"':
+ if (insingle)
+ goto addch;
+ indouble = !indouble;
+ break;
+ case '\\':
+ /* Backslash escapes anything, is escaped by quotes. */
+ if (!insingle && !indouble && (ch = getchar()) == EOF)
+ err("backslash at EOF");
+ /* FALLTHROUGH */
+ default:
+addch: if (p < ebp) {
+ *p++ = ch;
+ break;
+ }
+
+ /* If only one argument, not enough buffer space. */
+ if (bxp == xp)
+ err("insufficient space for argument");
+ /* Didn't hit argument limit, so if xflag object. */
+ if (xflag)
+ err("insufficient space for arguments");
+
+ *xp = NULL;
+ run(av);
+ xp = bxp;
+ cnt = ebp - argp;
+ bcopy(argp, bbp, cnt);
+ p = (argp = bbp) + cnt;
+ *p++ = ch;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+void
+run(argv)
+ char **argv;
+{
+ volatile int noinvoke;
+ register char **p;
+ pid_t pid;
+ int status;
+
+ if (tflag) {
+ (void)fprintf(stderr, "%s", *argv);
+ for (p = argv + 1; *p; ++p)
+ (void)fprintf(stderr, " %s", *p);
+ (void)fprintf(stderr, "\n");
+ (void)fflush(stderr);
+ }
+ noinvoke = 0;
+ switch(pid = vfork()) {
+ case -1:
+ err("vfork: %s", strerror(errno));
+ case 0:
+ execvp(argv[0], argv);
+ (void)fprintf(stderr,
+ "xargs: %s: %s\n", argv[0], strerror(errno));
+ noinvoke = 1;
+ _exit(1);
+ }
+ pid = waitpid(pid, &status, 0);
+ if (pid == -1)
+ err("waitpid: %s", strerror(errno));
+ /* If we couldn't invoke the utility, exit 127. */
+ if (noinvoke)
+ exit(127);
+ /* If utility signaled or exited with a value of 255, exit 1-125. */
+ if (WIFSIGNALED(status) || WEXITSTATUS(status) == 255)
+ exit(1);
+ if (WEXITSTATUS(status))
+ rval = 1;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: xargs [-t] [-n number [-x]] [-s size] [utility [argument ...]]\n");
+ exit(1);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "xargs: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile
new file mode 100644
index 0000000..d21abef
--- /dev/null
+++ b/usr.bin/xinstall/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= xinstall
+SRCS= stat_flags.c xinstall.c
+MAN1= install.1
+.PATH: ${.CURDIR}/../../bin/ls
+
+install: maninstall
+ ${INSTALL} ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${PROG} ${DESTDIR}${BINDIR}/install
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xinstall/install.1 b/usr.bin/xinstall/install.1
new file mode 100644
index 0000000..84ec638
--- /dev/null
+++ b/usr.bin/xinstall/install.1
@@ -0,0 +1,165 @@
+.\" Copyright (c) 1987, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)install.1 8.1 (Berkeley) 6/6/93
+.\" $Id$
+.\"
+.Dd October 9, 1995
+.Dt INSTALL 1
+.Os BSD 4.2
+.Sh NAME
+.Nm install
+.Nd install binaries
+.Sh SYNOPSIS
+.Nm install
+.Op Fl cs
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar file1 file2
+.Nm install
+.Op Fl Ccdps
+.Op Fl f Ar flags
+.Op Fl g Ar group
+.Op Fl m Ar mode
+.Op Fl o Ar owner
+.Ar file1
+\&...
+.Ar fileN directory
+.Sh DESCRIPTION
+The file(s) are moved (or copied if the
+.Fl c
+option is specified) to the target file or directory.
+If the destination is a directory, then the
+.Ar file
+is moved into
+.Ar directory
+with its original filename.
+If the target file already exists, it is overwritten if permissions
+allow.
+.Pp
+.Bl -tag -width Ds
+.It Fl C .
+Copy the file, as if the
+.Fl c
+option is specifed,
+except if the target file already exists and the files are the same,
+then don't change the modification time of the target
+.It Fl c
+Copy the file.
+This flag turns off the default behavior of
+.Nm install
+where it deletes the original file after creating the target.
+.It Fl d
+Print debugging information.
+If
+.Fl d
+is specified one or more times,
+then print the renaming steps for
+.Fl C .
+If
+.Fl d
+is specified two or more times,
+then warn about files that aren't installed with
+.Fl C .
+.It Fl f
+Specify the target's file flags.
+(See
+.Xr chflags 1
+for a list of possible flags and their meanings.)
+.It Fl g
+Specify a group.
+.It Fl m
+Specify an alternate mode.
+The default mode is set to rwxr-xr-x (0755).
+The specified mode may be either an octal or symbolic value; see
+.Xr chmod 1
+for a description of possible mode values.
+.It Fl o
+Specify an owner.
+.It Fl p
+Preserve the modification time.
+Copy the file, as if the
+.Fl C
+(Compare and copy) option is specifed,
+except if the target file doesn't already exist or is different,
+then preserve the modification time of the file.
+.It Fl s
+.Nm Install
+exec's the command
+.Xr strip 1
+to strip binaries so that install can be portable over a large
+number of systems and binary types.
+.El
+.Pp
+By default,
+.Nm install
+preserves all file flags, with the exception of the ``nodump'' flag.
+.Pp
+The
+.Nm install
+utility attempts to prevent moving a file onto itself.
+.Pp
+Installing
+.Pa /dev/null
+creates an empty file.
+.Pp
+Upon successful completion a value of 0 is returned.
+Otherwise, a value of 1 is returned.
+.Sh FILES
+.Bl -tag -width INS@XXXX -compact
+.It Pa INS@XXXX
+If the
+.Fl C
+or
+.Fl p
+option is used, then temporary files named INS@XXXX,
+where XXXX is decided by
+.Xr mkstemp 3 ,
+are created in the target directory.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr mv 1 ,
+.Xr strip 1 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm install
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+Temporary files may be left in the target directory if
+.Nm install
+exits abnormally.
diff --git a/usr.bin/xinstall/pathnames.h b/usr.bin/xinstall/pathnames.h
new file mode 100644
index 0000000..853b83c
--- /dev/null
+++ b/usr.bin/xinstall/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#define _PATH_STRIP "/usr/bin/strip"
diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c
new file mode 100644
index 0000000..eeae7da
--- /dev/null
+++ b/usr.bin/xinstall/xinstall.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*-
+ * Todo:
+ * o for -C, compare original files except in -s case.
+ * o for -C, don't change anything if nothing needs be changed. In
+ * particular, don't toggle the immutable flags just to allow null
+ * attribute changes and don't clear the dump flag. (I think inode
+ * ctimes are not updated for null attribute changes, but this is a
+ * bug.)
+ * o independent of -C, if a copy must be made, then copy to a tmpfile,
+ * set all attributes except the immutable flags, then rename, then
+ * set the immutable flags. It's annoying that the immutable flags
+ * defeat the atomicicity of rename - it seems that there must be
+ * o a window where the target is not immutable.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "pathnames.h"
+
+struct passwd *pp;
+struct group *gp;
+int debug, docompare, docopy, dopreserve, dostrip;
+int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+char *group, *owner, pathbuf[MAXPATHLEN];
+char pathbuf2[MAXPATHLEN];
+
+#define DIRECTORY 0x01 /* Tell install it's a directory. */
+#define SETFLAGS 0x02 /* Tell install to set flags. */
+#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
+
+void copy __P((int, char *, int, char *, off_t));
+int compare __P((int, const char *, int, const char *,
+ const struct stat *, const struct stat *));
+void install __P((char *, char *, u_long, u_int));
+u_long string_to_flags __P((char **, u_long *, u_long *));
+void strip __P((char *));
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct stat from_sb, to_sb;
+ mode_t *set;
+ u_long fset;
+ u_int iflags;
+ int ch, no_target;
+ char *flags, *to_name;
+
+ iflags = 0;
+ while ((ch = getopt(argc, argv, "Ccdf:g:m:o:ps")) != EOF)
+ switch((char)ch) {
+ case 'C':
+ docompare = docopy = 1;
+ break;
+ case 'c':
+ docopy = 1;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'f':
+ flags = optarg;
+ if (string_to_flags(&flags, &fset, NULL))
+ errx(EX_USAGE, "%s: invalid flag", flags);
+ iflags |= SETFLAGS;
+ break;
+ case 'g':
+ group = optarg;
+ break;
+ case 'm':
+ if (!(set = setmode(optarg)))
+ errx(EX_USAGE, "invalid file mode: %s",
+ optarg);
+ mode = getmode(set, 0);
+ break;
+ case 'o':
+ owner = optarg;
+ break;
+ case 'p':
+ docompare = docopy = dopreserve = 1;
+ break;
+ case 's':
+ dostrip = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 2)
+ usage();
+
+ /* get group and owner id's */
+ if (group && !(gp = getgrnam(group)))
+ errx(EX_NOUSER, "unknown group %s", group);
+ if (owner && !(pp = getpwnam(owner)))
+ errx(EX_NOUSER, "unknown user %s", owner);
+
+ no_target = stat(to_name = argv[argc - 1], &to_sb);
+ if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
+ for (; *argv != to_name; ++argv)
+ install(*argv, to_name, fset, iflags | DIRECTORY);
+ exit(0);
+ }
+
+ /* can't do file1 file2 directory/file */
+ if (argc != 2)
+ usage();
+
+ if (!no_target) {
+ if (stat(*argv, &from_sb))
+ err(EX_OSERR, "%s", *argv);
+ if (!S_ISREG(to_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (to_sb.st_dev == from_sb.st_dev &&
+ to_sb.st_ino == from_sb.st_ino)
+ errx(EX_USAGE,
+ "%s and %s are the same file", *argv, to_name);
+/*
+ * XXX - It's not at all clear why this code was here, since it completely
+ * duplicates code install(). The version in install() handles the -C flag
+ * correctly, so we'll just disable this for now.
+ */
+#if 0
+ /*
+ * Unlink now... avoid ETXTBSY errors later. Try and turn
+ * off the append/immutable bits -- if we fail, go ahead,
+ * it might work.
+ */
+ if (to_sb.st_flags & NOCHANGEBITS)
+ (void)chflags(to_name,
+ to_sb.st_flags & ~(NOCHANGEBITS));
+ (void)unlink(to_name);
+#endif
+ }
+ install(*argv, to_name, fset, iflags);
+ exit(0);
+}
+
+/*
+ * install --
+ * build a path name and install the file
+ */
+void
+install(from_name, to_name, fset, flags)
+ char *from_name, *to_name;
+ u_long fset;
+ u_int flags;
+{
+ struct stat from_sb, to_sb;
+ int devnull, from_fd, to_fd, serrno;
+ char *p, *old_to_name = 0;
+
+ if (debug >= 2 && !docompare)
+ fprintf(stderr, "install: invoked without -C for %s to %s\n",
+ from_name, to_name);
+
+ /* If try to install NULL file to a directory, fails. */
+ if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
+ if (stat(from_name, &from_sb))
+ err(EX_OSERR, "%s", from_name);
+ if (!S_ISREG(from_sb.st_mode)) {
+ errno = EFTYPE;
+ err(EX_OSERR, "%s", from_name);
+ }
+ /* Build the target path. */
+ if (flags & DIRECTORY) {
+ (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
+ to_name,
+ (p = strrchr(from_name, '/')) ? ++p : from_name);
+ to_name = pathbuf;
+ }
+ devnull = 0;
+ } else {
+ from_sb.st_flags = 0; /* XXX */
+ devnull = 1;
+ }
+
+ if (docompare) {
+ old_to_name = to_name;
+ /*
+ * Make a new temporary file in the same file system
+ * (actually, in in the same directory) as the target so
+ * that the temporary file can be renamed to the target.
+ */
+ snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
+ p = strrchr(pathbuf2, '/');
+ p = (p == NULL ? pathbuf2 : p + 1);
+ snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
+ to_fd = mkstemp(pathbuf2);
+ if (to_fd < 0)
+ /* XXX should fall back to not comparing. */
+ err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
+ to_name = pathbuf2;
+ } else {
+ /*
+ * Unlink now... avoid errors later. Try to turn off the
+ * append/immutable bits -- if we fail, go ahead, it might
+ * work.
+ */
+ if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS)
+ (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
+ unlink(to_name);
+
+ /* Create target. */
+ to_fd = open(to_name,
+ O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (to_fd < 0)
+ err(EX_OSERR, "%s", to_name);
+ }
+
+ if (!devnull) {
+ if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s", from_name);
+ }
+ copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
+ (void)close(from_fd);
+ }
+
+ if (dostrip)
+ strip(to_name);
+
+ /*
+ * Unfortunately, because we strip the installed file and not the
+ * original one, it is impossible to do the comparison without
+ * first laboriously copying things over and then comparing.
+ * It may be possible to better optimize the !dostrip case, however.
+ * For further study.
+ */
+ if (docompare) {
+ struct stat old_sb, new_sb, timestamp_sb;
+ int old_fd;
+ struct utimbuf utb;
+
+ old_fd = open(old_to_name, O_RDONLY, 0);
+ if (old_fd < 0 && errno == ENOENT)
+ goto different;
+ if (old_fd < 0)
+ err(EX_OSERR, "%s", old_to_name);
+ fstat(old_fd, &old_sb);
+ if (old_sb.st_flags & NOCHANGEBITS)
+ (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
+ fstat(to_fd, &new_sb);
+ if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
+ &new_sb)) {
+different:
+ if (debug != 0)
+ fprintf(stderr,
+ "install: renaming for %s: %s to %s\n",
+ from_name, to_name, old_to_name);
+ if (dopreserve && stat(from_name, &timestamp_sb) == 0) {
+ utb.actime = from_sb.st_atime;
+ utb.modtime = from_sb.st_mtime;
+ (void)utime(to_name, &utb);
+ }
+moveit:
+ if (rename(to_name, old_to_name) < 0) {
+ serrno = errno;
+ unlink(to_name);
+ unlink(old_to_name);
+ errno = serrno;
+ err(EX_OSERR, "rename: %s to %s", to_name,
+ old_to_name);
+ }
+ close(old_fd);
+ } else {
+ if (old_sb.st_nlink != 1) {
+ /*
+ * Replace the target, although it hasn't
+ * changed, to snap the extra links. But
+ * preserve the target file times.
+ */
+ if (fstat(old_fd, &timestamp_sb) == 0) {
+ utb.actime = timestamp_sb.st_atime;
+ utb.modtime = timestamp_sb.st_mtime;
+ (void)utime(to_name, &utb);
+ }
+ goto moveit;
+ }
+ if (unlink(to_name) < 0)
+ err(EX_OSERR, "unlink: %s", to_name);
+ close(to_fd);
+ to_fd = old_fd;
+ }
+ to_name = old_to_name;
+ }
+
+ /*
+ * Set owner, group, mode for target; do the chown first,
+ * chown may lose the setuid bits.
+ */
+ if ((group || owner) &&
+ fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR,"%s: chown/chgrp", to_name);
+ }
+ if (fchmod(to_fd, mode)) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s: chmod", to_name);
+ }
+
+ /*
+ * If provided a set of flags, set them, otherwise, preserve the
+ * flags, except for the dump flag.
+ */
+ if (fchflags(to_fd,
+ flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s: chflags", to_name);
+ }
+
+ (void)close(to_fd);
+ if (!docopy && !devnull && unlink(from_name))
+ err(EX_OSERR, "%s", from_name);
+}
+
+/*
+ * compare --
+ * compare two files; non-zero means files differ
+ */
+int
+compare(int from_fd, const char *from_name, int to_fd, const char *to_name,
+ const struct stat *from_sb, const struct stat *to_sb)
+{
+ char *p, *q;
+ int rv;
+ size_t tsize;
+
+ if (from_sb->st_size != to_sb->st_size)
+ return 1;
+
+ tsize = (size_t)from_sb->st_size;
+
+ if (tsize <= 8 * 1024 * 1024) {
+ p = mmap(NULL, tsize, PROT_READ, 0, from_fd, (off_t)0);
+ if ((long)p == -1)
+ err(EX_OSERR, "mmap %s", from_name);
+ q = mmap(NULL, tsize, PROT_READ, 0, to_fd, (off_t)0);
+ if ((long)q == -1)
+ err(EX_OSERR, "mmap %s", to_name);
+
+ rv = memcmp(p, q, tsize);
+ munmap(p, tsize);
+ munmap(q, tsize);
+ } else {
+ rv = 1; /* don't bother in this case */
+ }
+ return rv;
+}
+
+/*
+ * copy --
+ * copy from one file to another
+ */
+void
+copy(from_fd, from_name, to_fd, to_name, size)
+ register int from_fd, to_fd;
+ char *from_name, *to_name;
+ off_t size;
+{
+ register int nr, nw;
+ int serrno;
+ char *p, buf[MAXBSIZE];
+
+ /*
+ * Mmap and write if less than 8M (the limit is so we don't totally
+ * trash memory on big files. This is really a minor hack, but it
+ * wins some CPU back.
+ */
+ if (size <= 8 * 1048576) {
+ if ((p = mmap(NULL, (size_t)size, PROT_READ,
+ 0, from_fd, (off_t)0)) == (char *)-1)
+ err(EX_OSERR, "mmap %s", from_name);
+ if (write(to_fd, p, size) != size)
+ err(EX_OSERR, "%s", to_name);
+ } else {
+ while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
+ if ((nw = write(to_fd, buf, nr)) != nr) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = nw > 0 ? EIO : serrno;
+ err(EX_OSERR, "%s", to_name);
+ }
+ if (nr != 0) {
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_OSERR, "%s", from_name);
+ }
+ }
+}
+
+/*
+ * strip --
+ * use strip(1) to strip the target file
+ */
+void
+strip(to_name)
+ char *to_name;
+{
+ int serrno, status;
+
+ switch (vfork()) {
+ case -1:
+ serrno = errno;
+ (void)unlink(to_name);
+ errno = serrno;
+ err(EX_TEMPFAIL, "fork");
+ case 0:
+ execl(_PATH_STRIP, "strip", to_name, NULL);
+ err(EX_OSERR, "exec(" _PATH_STRIP ")");
+ default:
+ if (wait(&status) == -1 || status)
+ (void)unlink(to_name);
+ }
+}
+
+/*
+ * usage --
+ * print a usage message and die
+ */
+void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: install [-Ccdps] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
+ exit(1);
+}
diff --git a/usr.bin/xstr/Makefile b/usr.bin/xstr/Makefile
new file mode 100644
index 0000000..620a3ba
--- /dev/null
+++ b/usr.bin/xstr/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/9/93
+
+PROG= xstr
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/xstr/pathnames.h b/usr.bin/xstr/pathnames.h
new file mode 100644
index 0000000..08a63dd
--- /dev/null
+++ b/usr.bin/xstr/pathnames.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/9/93
+ */
+
+#define _PATH_TMP "/tmp/xstrXXXXXX"
diff --git a/usr.bin/xstr/xstr.1 b/usr.bin/xstr/xstr.1
new file mode 100644
index 0000000..8795a2d
--- /dev/null
+++ b/usr.bin/xstr/xstr.1
@@ -0,0 +1,159 @@
+.\" Copyright (c) 1980, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)xstr.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt XSTR 1
+.Os BSD 3
+.Sh NAME
+.Nm xstr
+.Nd "extract strings from C programs to implement shared strings"
+.Sh SYNOPSIS
+.Nm xstr
+.Op Fl c
+.Op Fl
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Xstr
+maintains a file
+.Pa strings
+into which strings in component parts of a large program are hashed.
+These strings are replaced with references to this common area.
+This serves to implement shared constant strings, most useful if they
+are also read-only.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl
+.Nm Xstr
+reads from the standard input.
+.It Fl c
+.Nm Xstr
+will extract the strings from the C source
+.Ar file
+or the standard input
+.Pq Fl ,
+replacing
+string references by expressions of the form (&xstr[number])
+for some number.
+An appropriate declaration of
+.Nm xstr
+is prepended to the file.
+The resulting C text is placed in the file
+.Pa x.c ,
+to then be compiled.
+The strings from this file are placed in the
+.Pa strings
+data base if they are not there already.
+Repeated strings and strings which are suffixes of existing strings
+do not cause changes to the data base.
+.El
+.Pp
+After all components of a large program have been compiled a file
+.Pa xs.c
+declaring the common
+.Nm xstr
+space can be created by a command of the form
+.Bd -literal -offset indent
+xstr
+.Ed
+.Pp
+The file
+.Pa xs.c
+should then be compiled and loaded with the rest
+of the program.
+If possible, the array can be made read-only (shared) saving
+space and swap overhead.
+.Pp
+.Nm Xstr
+can also be used on a single file.
+A command
+.Bd -literal -offset indent
+xstr name
+.Ed
+.Pp
+creates files
+.Pa x.c
+and
+.Pa xs.c
+as before, without using or affecting any
+.Pa strings
+file in the same directory.
+.Pp
+It may be useful to run
+.Nm xstr
+after the C preprocessor if any macro definitions yield strings
+or if there is conditional code which contains strings
+which may not, in fact, be needed.
+An appropriate command sequence for running
+.Nm xstr
+after the C preprocessor is:
+.Pp
+.Bd -literal -offset indent -compact
+cc \-E name.c | xstr \-c \-
+cc \-c x.c
+mv x.o name.o
+.Ed
+.Pp
+.Nm Xstr
+does not touch the file
+.Pa strings
+unless new items are added, thus
+.Xr make 1
+can avoid remaking
+.Pa xs.o
+unless truly necessary.
+.Sh FILES
+.Bl -tag -width /tmp/xsxx* -compact
+.It Pa strings
+Data base of strings
+.It Pa x.c
+Massaged C source
+.It Pa xs.c
+C source for definition of array `xstr'
+.It Pa /tmp/xs*
+Temp file when `xstr name' doesn't touch
+.Pa strings
+.El
+.Sh SEE ALSO
+.Xr mkstr 1
+.Sh BUGS
+If a string is a suffix of another string in the data base,
+but the shorter string is seen first by
+.Nm xstr
+both strings will be placed in the data base, when just
+placing the longer one there will do.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.bin/xstr/xstr.c b/usr.bin/xstr/xstr.c
new file mode 100644
index 0000000..5b68c53
--- /dev/null
+++ b/usr.bin/xstr/xstr.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)xstr.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "pathnames.h"
+
+/*
+ * xstr - extract and hash strings in a C program
+ *
+ * Bill Joy UCB
+ * November, 1978
+ */
+
+#define ignore(a) ((void) a)
+
+off_t tellpt;
+off_t hashit();
+void onintr();
+char *savestr();
+off_t yankstr();
+
+off_t mesgpt;
+char *strings = "strings";
+
+int cflg;
+int vflg;
+int readstd;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ argc--, argv++;
+ while (argc > 0 && argv[0][0] == '-') {
+ register char *cp = &(*argv++)[1];
+
+ argc--;
+ if (*cp == 0) {
+ readstd++;
+ continue;
+ }
+ do switch (*cp++) {
+
+ case 'c':
+ cflg++;
+ continue;
+
+ case 'v':
+ vflg++;
+ continue;
+
+ default:
+ fprintf(stderr, "usage: xstr [ -v ] [ -c ] [ - ] [ name ... ]\n");
+ } while (*cp);
+ }
+ if (signal(SIGINT, SIG_IGN) == SIG_DFL)
+ signal(SIGINT, onintr);
+ if (cflg || argc == 0 && !readstd)
+ inithash();
+ else
+ strings = mktemp(strdup(_PATH_TMP));
+ while (readstd || argc > 0) {
+ if (freopen("x.c", "w", stdout) == NULL)
+ perror("x.c"), exit(1);
+ if (!readstd && freopen(argv[0], "r", stdin) == NULL)
+ perror(argv[0]), exit(2);
+ process("x.c");
+ if (readstd == 0)
+ argc--, argv++;
+ else
+ readstd = 0;
+ };
+ flushsh();
+ if (cflg == 0)
+ xsdotc();
+ if (strings[0] == '/')
+ ignore(unlink(strings));
+ exit(0);
+}
+
+char linebuf[BUFSIZ];
+
+process(name)
+ char *name;
+{
+ char *cp;
+ register int c;
+ register int incomm = 0;
+ int ret;
+
+ printf("extern char\txstr[];\n");
+ for (;;) {
+ if (fgets(linebuf, sizeof linebuf, stdin) == NULL) {
+ if (ferror(stdin)) {
+ perror(name);
+ exit(3);
+ }
+ break;
+ }
+ if (linebuf[0] == '#') {
+ if (linebuf[1] == ' ' && isdigit(linebuf[2]))
+ printf("#line%s", &linebuf[1]);
+ else
+ printf("%s", linebuf);
+ continue;
+ }
+ for (cp = linebuf; c = *cp++;) switch (c) {
+
+ case '"':
+ if (incomm)
+ goto def;
+ if ((ret = (int) yankstr(&cp)) == -1)
+ goto out;
+ printf("(&xstr[%d])", ret);
+ break;
+
+ case '\'':
+ if (incomm)
+ goto def;
+ putchar(c);
+ if (*cp)
+ putchar(*cp++);
+ break;
+
+ case '/':
+ if (incomm || *cp != '*')
+ goto def;
+ incomm = 1;
+ cp++;
+ printf("/*");
+ continue;
+
+ case '*':
+ if (incomm && *cp == '/') {
+ incomm = 0;
+ cp++;
+ printf("*/");
+ continue;
+ }
+ goto def;
+
+def:
+ default:
+ putchar(c);
+ break;
+ }
+ }
+out:
+ if (ferror(stdout))
+ perror("x.c"), onintr();
+}
+
+off_t
+yankstr(cpp)
+ register char **cpp;
+{
+ register char *cp = *cpp;
+ register int c, ch;
+ char dbuf[BUFSIZ];
+ register char *dp = dbuf;
+ register char *tp;
+
+ while (c = *cp++) {
+ switch (c) {
+
+ case '"':
+ cp++;
+ goto out;
+
+ case '\\':
+ c = *cp++;
+ if (c == 0)
+ break;
+ if (c == '\n') {
+ if (fgets(linebuf, sizeof linebuf, stdin)
+ == NULL) {
+ if (ferror(stdin)) {
+ perror("x.c");
+ exit(3);
+ }
+ return(-1);
+ }
+ cp = linebuf;
+ continue;
+ }
+ for (tp = "b\bt\tr\rn\nf\f\\\\\"\""; ch = *tp++; tp++)
+ if (c == ch) {
+ c = *tp;
+ goto gotc;
+ }
+ if (!octdigit(c)) {
+ *dp++ = '\\';
+ break;
+ }
+ c -= '0';
+ if (!octdigit(*cp))
+ break;
+ c <<= 3, c += *cp++ - '0';
+ if (!octdigit(*cp))
+ break;
+ c <<= 3, c += *cp++ - '0';
+ break;
+ }
+gotc:
+ *dp++ = c;
+ }
+out:
+ *cpp = --cp;
+ *dp = 0;
+ return (hashit(dbuf, 1));
+}
+
+octdigit(c)
+ char c;
+{
+
+ return (isdigit(c) && c != '8' && c != '9');
+}
+
+inithash()
+{
+ char buf[BUFSIZ];
+ register FILE *mesgread = fopen(strings, "r");
+
+ if (mesgread == NULL)
+ return;
+ for (;;) {
+ mesgpt = tellpt;
+ if (fgetNUL(buf, sizeof buf, mesgread) == NULL)
+ break;
+ ignore(hashit(buf, 0));
+ }
+ ignore(fclose(mesgread));
+}
+
+fgetNUL(obuf, rmdr, file)
+ char *obuf;
+ register int rmdr;
+ FILE *file;
+{
+ register c;
+ register char *buf = obuf;
+
+ while (--rmdr > 0 && (c = xgetc(file)) != 0 && c != EOF)
+ *buf++ = c;
+ *buf++ = 0;
+ return ((feof(file) || ferror(file)) ? NULL : 1);
+}
+
+xgetc(file)
+ FILE *file;
+{
+
+ tellpt++;
+ return (getc(file));
+}
+
+#define BUCKETS 128
+
+struct hash {
+ off_t hpt;
+ char *hstr;
+ struct hash *hnext;
+ short hnew;
+} bucket[BUCKETS];
+
+off_t
+hashit(str, new)
+ char *str;
+ int new;
+{
+ int i;
+ register struct hash *hp, *hp0;
+
+ hp = hp0 = &bucket[lastchr(str) & 0177];
+ while (hp->hnext) {
+ hp = hp->hnext;
+ i = istail(str, hp->hstr);
+ if (i >= 0)
+ return (hp->hpt + i);
+ }
+ if ((hp = (struct hash *) calloc(1, sizeof (*hp))) == NULL) {
+ perror("xstr");
+ exit(8);
+ }
+ hp->hpt = mesgpt;
+ if (!(hp->hstr = strdup(str))) {
+ (void)fprintf(stderr, "xstr: %s\n", strerror(errno));
+ exit(1);
+ }
+ mesgpt += strlen(hp->hstr) + 1;
+ hp->hnext = hp0->hnext;
+ hp->hnew = new;
+ hp0->hnext = hp;
+ return (hp->hpt);
+}
+
+flushsh()
+{
+ register int i;
+ register struct hash *hp;
+ register FILE *mesgwrit;
+ register int old = 0, new = 0;
+
+ for (i = 0; i < BUCKETS; i++)
+ for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext)
+ if (hp->hnew)
+ new++;
+ else
+ old++;
+ if (new == 0 && old != 0)
+ return;
+ mesgwrit = fopen(strings, old ? "r+" : "w");
+ if (mesgwrit == NULL)
+ perror(strings), exit(4);
+ for (i = 0; i < BUCKETS; i++)
+ for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext) {
+ found(hp->hnew, hp->hpt, hp->hstr);
+ if (hp->hnew) {
+ fseek(mesgwrit, hp->hpt, 0);
+ ignore(fwrite(hp->hstr, strlen(hp->hstr) + 1, 1, mesgwrit));
+ if (ferror(mesgwrit))
+ perror(strings), exit(4);
+ }
+ }
+ if (fclose(mesgwrit) == EOF)
+ perror(strings), exit(4);
+}
+
+found(new, off, str)
+ int new;
+ off_t off;
+ char *str;
+{
+ if (vflg == 0)
+ return;
+ if (!new)
+ fprintf(stderr, "found at %d:", (int) off);
+ else
+ fprintf(stderr, "new at %d:", (int) off);
+ prstr(str);
+ fprintf(stderr, "\n");
+}
+
+prstr(cp)
+ register char *cp;
+{
+ register int c;
+
+ while (c = (*cp++ & 0377))
+ if (c < ' ')
+ fprintf(stderr, "^%c", c + '`');
+ else if (c == 0177)
+ fprintf(stderr, "^?");
+ else if (c > 0200)
+ fprintf(stderr, "\\%03o", c);
+ else
+ fprintf(stderr, "%c", c);
+}
+
+xsdotc()
+{
+ register FILE *strf = fopen(strings, "r");
+ register FILE *xdotcf;
+
+ if (strf == NULL)
+ perror(strings), exit(5);
+ xdotcf = fopen("xs.c", "w");
+ if (xdotcf == NULL)
+ perror("xs.c"), exit(6);
+ fprintf(xdotcf, "char\txstr[] = {\n");
+ for (;;) {
+ register int i, c;
+
+ for (i = 0; i < 8; i++) {
+ c = getc(strf);
+ if (ferror(strf)) {
+ perror(strings);
+ onintr();
+ }
+ if (feof(strf)) {
+ fprintf(xdotcf, "\n");
+ goto out;
+ }
+ fprintf(xdotcf, "0x%02x,", c);
+ }
+ fprintf(xdotcf, "\n");
+ }
+out:
+ fprintf(xdotcf, "};\n");
+ ignore(fclose(xdotcf));
+ ignore(fclose(strf));
+}
+
+lastchr(cp)
+ register char *cp;
+{
+
+ while (cp[0] && cp[1])
+ cp++;
+ return (*cp);
+}
+
+istail(str, of)
+ register char *str, *of;
+{
+ register int d = strlen(of) - strlen(str);
+
+ if (d < 0 || strcmp(&of[d], str) != 0)
+ return (-1);
+ return (d);
+}
+
+void
+onintr()
+{
+
+ ignore(signal(SIGINT, SIG_IGN));
+ if (strings[0] == '/')
+ ignore(unlink(strings));
+ ignore(unlink("x.c"));
+ ignore(unlink("xs.c"));
+ exit(7);
+}
diff --git a/usr.bin/yacc/ACKNOWLEDGEMENTS b/usr.bin/yacc/ACKNOWLEDGEMENTS
new file mode 100644
index 0000000..b66bb25
--- /dev/null
+++ b/usr.bin/yacc/ACKNOWLEDGEMENTS
@@ -0,0 +1,25 @@
+ Berkeley Yacc owes much to the unflagging efforts of Keith Bostic.
+His badgering kept me working on it long after I was ready to quit.
+
+ Berkeley Yacc is based on the excellent algorithm for computing LALR(1)
+lookaheads developed by Tom Pennello and Frank DeRemer. The algorithm is
+described in their almost impenetrable article in TOPLAS 4,4.
+
+ Finally, much of the credit for the latest version must go to those
+who pointed out deficiencies of my earlier releases. Among the most
+prolific contributors were
+
+ Benson I. Margulies
+ Dave Gentzel
+ Antoine Verheijen
+ Peter S. Housel
+ Dale Smith
+ Ozan Yigit
+ John Campbell
+ Bill Sommerfeld
+ Paul Hilfinger
+ Gary Bridgewater
+ Dave Bakken
+ Dan Lanciani
+ Richard Sargent
+ Parag Patel
diff --git a/usr.bin/yacc/Makefile b/usr.bin/yacc/Makefile
new file mode 100644
index 0000000..5ff2152
--- /dev/null
+++ b/usr.bin/yacc/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 5.3 (Berkeley) 5/12/90
+
+PROG= yacc
+SRCS= closure.c error.c lalr.c lr0.c main.c mkpar.c output.c reader.c \
+ skeleton.c symtab.c verbose.c warshall.c
+MAN1= yacc.1 yyfix.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/yyfix.sh ${DESTDIR}${BINDIR}/yyfix
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/yacc/NEW_FEATURES b/usr.bin/yacc/NEW_FEATURES
new file mode 100644
index 0000000..b030c62
--- /dev/null
+++ b/usr.bin/yacc/NEW_FEATURES
@@ -0,0 +1,46 @@
+ The -r option has been implemented. The -r option tells Yacc to
+put the read-only tables in y.tab.c and the code and variables in
+y.code.c. Keith Bostic asked for this option so that :yyfix could be
+eliminated.
+
+ The -l and -t options have been implemented. The -l option tells
+Yacc not to include #line directives in the code it produces. The -t
+option causes debugging code to be included in the compiled parser.
+
+ The code for error recovery has been changed to implement the same
+algorithm as AT&T Yacc. There will still be differences in the way
+error recovery works because AT&T Yacc uses more default reductions
+than Berkeley Yacc.
+
+ The environment variable TMPDIR determines the directory where
+temporary files will be created. If TMPDIR is defined, temporary files
+will be created in the directory whose pathname is the value of TMPDIR.
+By default, temporary files are created in /tmp.
+
+ The keywords are now case-insensitive. For example, %nonassoc,
+%NONASSOC, %NonAssoc, and %nOnAsSoC are all equivalent.
+
+ Commas and semicolons that are not part of C code are treated as
+commentary.
+
+ Line-end comments, as in BCPL, are permitted. Line-end comments
+begin with // and end at the next end-of-line. Line-end comments are
+permitted in C code; they are converted to C comments on output.
+
+ The form of y.output files has been changed to look more like
+those produced by AT&T Yacc.
+
+ A new kind of declaration has been added. The form of the declaration
+is
+
+ %ident string
+
+where string is a sequence of characters begining with a double quote
+and ending with either a double quote or the next end-of-line, whichever
+comes first. The declaration will cause a #ident directive to be written
+near the start of the output file.
+
+ If a parser has been compiled with debugging code, that code can be
+enabled by setting an environment variable. If the environment variable
+YYDEBUG is set to 0, debugging output is suppressed. If it is set to 1,
+debugging output is written to standard output.
diff --git a/usr.bin/yacc/NOTES b/usr.bin/yacc/NOTES
new file mode 100644
index 0000000..9db3c96
--- /dev/null
+++ b/usr.bin/yacc/NOTES
@@ -0,0 +1,9 @@
+Berkeley Yacc reflects its origins. The reason so many routines
+use exactly six register variables is that Berkeley Yacc was
+developed on a VAX using PCC. PCC placed at most six variables
+in registers. I went to considerable effort to find which six
+variables most belonged in registers. Changes in machines and
+compilers make that effort worthless, perhaps even harmful.
+
+The code contains many instances where address calculations are
+performed in particular ways to optimize the code for the VAX.
diff --git a/usr.bin/yacc/README b/usr.bin/yacc/README
new file mode 100644
index 0000000..091f233
--- /dev/null
+++ b/usr.bin/yacc/README
@@ -0,0 +1,23 @@
+ Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made
+as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input
+specification that conforms to the AT&T Yacc documentation. Specifications
+that take advantage of undocumented features of AT&T Yacc will probably be
+rejected.
+
+ Berkeley Yacc is distributed with no warranty whatever. The code is certain
+to contain errors. Neither the author nor any contributor takes responsibility
+for any consequences of its use.
+
+ Berkeley Yacc is in the public domain. The data structures and algorithms
+used in Berkeley Yacc are all either taken from documents available to the
+general public or are inventions of the author. Anyone may freely distribute
+source or binary forms of Berkeley Yacc whether unchanged or modified.
+Distributers may charge whatever fees they can obtain for Berkeley Yacc.
+Programs generated by Berkeley Yacc may be distributed freely.
+
+ Please report bugs to
+
+ robert.corbett@eng.Sun.COM
+
+Include a small example if possible. Please include the banner string from
+skeleton.c with the bug report. Do not expect rapid responses.
diff --git a/usr.bin/yacc/closure.c b/usr.bin/yacc/closure.c
new file mode 100644
index 0000000..5f63c5f
--- /dev/null
+++ b/usr.bin/yacc/closure.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)closure.c 5.3 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+short *itemset;
+short *itemsetend;
+unsigned *ruleset;
+
+static unsigned *first_derives;
+static unsigned *EFF;
+
+
+set_EFF()
+{
+ register unsigned *row;
+ register int symbol;
+ register short *sp;
+ register int rowsize;
+ register int i;
+ register int rule;
+
+ rowsize = WORDSIZE(nvars);
+ EFF = NEW2(nvars * rowsize, unsigned);
+
+ row = EFF;
+ for (i = start_symbol; i < nsyms; i++)
+ {
+ sp = derives[i];
+ for (rule = *sp; rule > 0; rule = *++sp)
+ {
+ symbol = ritem[rrhs[rule]];
+ if (ISVAR(symbol))
+ {
+ symbol -= start_symbol;
+ SETBIT(row, symbol);
+ }
+ }
+ row += rowsize;
+ }
+
+ reflexive_transitive_closure(EFF, nvars);
+
+#ifdef DEBUG
+ print_EFF();
+#endif
+}
+
+
+set_first_derives()
+{
+ register unsigned *rrow;
+ register unsigned *vrow;
+ register int j;
+ register unsigned k;
+ register unsigned cword;
+ register short *rp;
+
+ int rule;
+ int i;
+ int rulesetsize;
+ int varsetsize;
+
+ rulesetsize = WORDSIZE(nrules);
+ varsetsize = WORDSIZE(nvars);
+ first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize;
+
+ set_EFF();
+
+ rrow = first_derives + ntokens * rulesetsize;
+ for (i = start_symbol; i < nsyms; i++)
+ {
+ vrow = EFF + ((i - ntokens) * varsetsize);
+ k = BITS_PER_WORD;
+ for (j = start_symbol; j < nsyms; k++, j++)
+ {
+ if (k >= BITS_PER_WORD)
+ {
+ cword = *vrow++;
+ k = 0;
+ }
+
+ if (cword & (1 << k))
+ {
+ rp = derives[j];
+ while ((rule = *rp++) >= 0)
+ {
+ SETBIT(rrow, rule);
+ }
+ }
+ }
+
+ vrow += varsetsize;
+ rrow += rulesetsize;
+ }
+
+#ifdef DEBUG
+ print_first_derives();
+#endif
+
+ FREE(EFF);
+}
+
+
+closure(nucleus, n)
+short *nucleus;
+int n;
+{
+ register int ruleno;
+ register unsigned word;
+ register unsigned i;
+ register short *csp;
+ register unsigned *dsp;
+ register unsigned *rsp;
+ register int rulesetsize;
+
+ short *csend;
+ unsigned *rsend;
+ int symbol;
+ int itemno;
+
+ rulesetsize = WORDSIZE(nrules);
+ rsp = ruleset;
+ rsend = ruleset + rulesetsize;
+ for (rsp = ruleset; rsp < rsend; rsp++)
+ *rsp = 0;
+
+ csend = nucleus + n;
+ for (csp = nucleus; csp < csend; ++csp)
+ {
+ symbol = ritem[*csp];
+ if (ISVAR(symbol))
+ {
+ dsp = first_derives + symbol * rulesetsize;
+ rsp = ruleset;
+ while (rsp < rsend)
+ *rsp++ |= *dsp++;
+ }
+ }
+
+ ruleno = 0;
+ itemsetend = itemset;
+ csp = nucleus;
+ for (rsp = ruleset; rsp < rsend; ++rsp)
+ {
+ word = *rsp;
+ if (word)
+ {
+ for (i = 0; i < BITS_PER_WORD; ++i)
+ {
+ if (word & (1 << i))
+ {
+ itemno = rrhs[ruleno+i];
+ while (csp < csend && *csp < itemno)
+ *itemsetend++ = *csp++;
+ *itemsetend++ = itemno;
+ while (csp < csend && *csp == itemno)
+ ++csp;
+ }
+ }
+ }
+ ruleno += BITS_PER_WORD;
+ }
+
+ while (csp < csend)
+ *itemsetend++ = *csp++;
+
+#ifdef DEBUG
+ print_closure(n);
+#endif
+}
+
+
+
+finalize_closure()
+{
+ FREE(itemset);
+ FREE(ruleset);
+ FREE(first_derives + ntokens * WORDSIZE(nrules));
+}
+
+
+#ifdef DEBUG
+
+print_closure(n)
+int n;
+{
+ register short *isp;
+
+ printf("\n\nn = %d\n\n", n);
+ for (isp = itemset; isp < itemsetend; isp++)
+ printf(" %d\n", *isp);
+}
+
+
+print_EFF()
+{
+ register int i, j;
+ register unsigned *rowp;
+ register unsigned word;
+ register unsigned k;
+
+ printf("\n\nEpsilon Free Firsts\n");
+
+ for (i = start_symbol; i < nsyms; i++)
+ {
+ printf("\n%s", symbol_name[i]);
+ rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars));
+ word = *rowp++;
+
+ k = BITS_PER_WORD;
+ for (j = 0; j < nvars; k++, j++)
+ {
+ if (k >= BITS_PER_WORD)
+ {
+ word = *rowp++;
+ k = 0;
+ }
+
+ if (word & (1 << k))
+ printf(" %s", symbol_name[start_symbol + j]);
+ }
+ }
+}
+
+
+print_first_derives()
+{
+ register int i;
+ register int j;
+ register unsigned *rp;
+ register unsigned cword;
+ register unsigned k;
+
+ printf("\n\n\nFirst Derives\n");
+
+ for (i = start_symbol; i < nsyms; i++)
+ {
+ printf("\n%s derives\n", symbol_name[i]);
+ rp = first_derives + i * WORDSIZE(nrules);
+ k = BITS_PER_WORD;
+ for (j = 0; j <= nrules; k++, j++)
+ {
+ if (k >= BITS_PER_WORD)
+ {
+ cword = *rp++;
+ k = 0;
+ }
+
+ if (cword & (1 << k))
+ printf(" %d\n", j);
+ }
+ }
+
+ fflush(stdout);
+}
+
+#endif
diff --git a/usr.bin/yacc/defs.h b/usr.bin/yacc/defs.h
new file mode 100644
index 0000000..e5e96d2
--- /dev/null
+++ b/usr.bin/yacc/defs.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 5.6 (Berkeley) 5/24/93
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+
+
+/* machine-dependent definitions */
+/* the following definitions are for the Tahoe */
+/* they might have to be changed for other machines */
+
+/* MAXCHAR is the largest unsigned character value */
+/* MAXSHORT is the largest value of a C short */
+/* MINSHORT is the most negative value of a C short */
+/* MAXTABLE is the maximum table size */
+/* BITS_PER_WORD is the number of bits in a C unsigned */
+/* WORDSIZE computes the number of words needed to */
+/* store n bits */
+/* BIT returns the value of the n-th bit starting */
+/* from r (0-indexed) */
+/* SETBIT sets the n-th bit starting from r */
+
+#define MAXCHAR 255
+#define MAXSHORT 32767
+#define MINSHORT -32768
+#define MAXTABLE 32500
+#define BITS_PER_WORD 32
+#define WORDSIZE(n) (((n)+(BITS_PER_WORD-1))/BITS_PER_WORD)
+#define BIT(r, n) ((((r)[(n)>>5])>>((n)&31))&1)
+#define SETBIT(r, n) ((r)[(n)>>5]|=((unsigned)1<<((n)&31)))
+
+
+/* character names */
+
+#define NUL '\0' /* the null character */
+#define NEWLINE '\n' /* line feed */
+#define SP ' ' /* space */
+#define BS '\b' /* backspace */
+#define HT '\t' /* horizontal tab */
+#define VT '\013' /* vertical tab */
+#define CR '\r' /* carriage return */
+#define FF '\f' /* form feed */
+#define QUOTE '\'' /* single quote */
+#define DOUBLE_QUOTE '\"' /* double quote */
+#define BACKSLASH '\\' /* backslash */
+
+
+/* defines for constructing filenames */
+
+#define CODE_SUFFIX ".code.c"
+#define DEFINES_SUFFIX ".tab.h"
+#define OUTPUT_SUFFIX ".tab.c"
+#define VERBOSE_SUFFIX ".output"
+
+
+/* keyword codes */
+
+#define TOKEN 0
+#define LEFT 1
+#define RIGHT 2
+#define NONASSOC 3
+#define MARK 4
+#define TEXT 5
+#define TYPE 6
+#define START 7
+#define UNION 8
+#define IDENT 9
+
+
+/* symbol classes */
+
+#define UNKNOWN 0
+#define TERM 1
+#define NONTERM 2
+
+
+/* the undefined value */
+
+#define UNDEFINED (-1)
+
+
+/* action codes */
+
+#define SHIFT 1
+#define REDUCE 2
+
+
+/* character macros */
+
+#define IS_IDENT(c) (isalnum(c) || (c) == '_' || (c) == '.' || (c) == '$')
+#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7')
+#define NUMERIC_VALUE(c) ((c) - '0')
+
+
+/* symbol macros */
+
+#define ISTOKEN(s) ((s) < start_symbol)
+#define ISVAR(s) ((s) >= start_symbol)
+
+
+/* storage allocation macros */
+
+#define CALLOC(k,n) (calloc((unsigned)(k),(unsigned)(n)))
+#define FREE(x) (free((char*)(x)))
+#define MALLOC(n) (malloc((unsigned)(n)))
+#define NEW(t) ((t*)allocate(sizeof(t)))
+#define NEW2(n,t) ((t*)allocate((unsigned)((n)*sizeof(t))))
+#define REALLOC(p,n) (realloc((char*)(p),(unsigned)(n)))
+
+
+/* the structure of a symbol table entry */
+
+typedef struct bucket bucket;
+struct bucket
+{
+ struct bucket *link;
+ struct bucket *next;
+ char *name;
+ char *tag;
+ short value;
+ short index;
+ short prec;
+ char class;
+ char assoc;
+};
+
+
+/* the structure of the LR(0) state machine */
+
+typedef struct core core;
+struct core
+{
+ struct core *next;
+ struct core *link;
+ short number;
+ short accessing_symbol;
+ short nitems;
+ short items[1];
+};
+
+
+/* the structure used to record shifts */
+
+typedef struct shifts shifts;
+struct shifts
+{
+ struct shifts *next;
+ short number;
+ short nshifts;
+ short shift[1];
+};
+
+
+/* the structure used to store reductions */
+
+typedef struct reductions reductions;
+struct reductions
+{
+ struct reductions *next;
+ short number;
+ short nreds;
+ short rules[1];
+};
+
+
+/* the structure used to represent parser actions */
+
+typedef struct action action;
+struct action
+{
+ struct action *next;
+ short symbol;
+ short number;
+ short prec;
+ char action_code;
+ char assoc;
+ char suppressed;
+};
+
+
+/* global variables */
+
+extern char dflag;
+extern char lflag;
+extern char rflag;
+extern char tflag;
+extern char vflag;
+extern char *symbol_prefix;
+
+extern char *myname;
+extern char *cptr;
+extern char *line;
+extern int lineno;
+extern int outline;
+
+extern char *banner[];
+extern char *tables[];
+extern char *header[];
+extern char *body[];
+extern char *trailer[];
+
+extern char *action_file_name;
+extern char *code_file_name;
+extern char *defines_file_name;
+extern char *input_file_name;
+extern char *output_file_name;
+extern char *text_file_name;
+extern char *union_file_name;
+extern char *verbose_file_name;
+
+extern FILE *action_file;
+extern FILE *code_file;
+extern FILE *defines_file;
+extern FILE *input_file;
+extern FILE *output_file;
+extern FILE *text_file;
+extern FILE *union_file;
+extern FILE *verbose_file;
+
+extern int nitems;
+extern int nrules;
+extern int nsyms;
+extern int ntokens;
+extern int nvars;
+extern int ntags;
+
+extern char unionized;
+extern char line_format[];
+
+extern int start_symbol;
+extern char **symbol_name;
+extern short *symbol_value;
+extern short *symbol_prec;
+extern char *symbol_assoc;
+
+extern short *ritem;
+extern short *rlhs;
+extern short *rrhs;
+extern short *rprec;
+extern char *rassoc;
+
+extern short **derives;
+extern char *nullable;
+
+extern bucket *first_symbol;
+extern bucket *last_symbol;
+
+extern int nstates;
+extern core *first_state;
+extern shifts *first_shift;
+extern reductions *first_reduction;
+extern short *accessing_symbol;
+extern core **state_table;
+extern shifts **shift_table;
+extern reductions **reduction_table;
+extern unsigned *LA;
+extern short *LAruleno;
+extern short *lookaheads;
+extern short *goto_map;
+extern short *from_state;
+extern short *to_state;
+
+extern action **parser;
+extern int SRtotal;
+extern int RRtotal;
+extern short *SRconflicts;
+extern short *RRconflicts;
+extern short *defred;
+extern short *rules_used;
+extern short nunused;
+extern short final_state;
+
+/* global functions */
+
+extern char *allocate();
+extern bucket *lookup();
+extern bucket *make_bucket();
+
+
+/* system variables */
+
+extern int errno;
+
+
+/* system functions */
+
+extern void free();
+extern char *calloc();
+extern char *malloc();
+extern char *realloc();
+extern char *strcpy();
diff --git a/usr.bin/yacc/error.c b/usr.bin/yacc/error.c
new file mode 100644
index 0000000..54a49ad
--- /dev/null
+++ b/usr.bin/yacc/error.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)error.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+/* routines for printing error messages */
+
+#include "defs.h"
+
+
+fatal(msg)
+char *msg;
+{
+ fprintf(stderr, "%s: f - %s\n", myname, msg);
+ done(2);
+}
+
+
+no_space()
+{
+ fprintf(stderr, "%s: f - out of space\n", myname);
+ done(2);
+}
+
+
+open_error(filename)
+char *filename;
+{
+ fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);
+ done(2);
+}
+
+
+unexpected_EOF()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n",
+ myname, lineno, input_file_name);
+ done(1);
+}
+
+
+print_pos(st_line, st_cptr)
+char *st_line;
+char *st_cptr;
+{
+ register char *s;
+
+ if (st_line == 0) return;
+ for (s = st_line; *s != '\n'; ++s)
+ {
+ if (isprint(*s) || *s == '\t')
+ putc(*s, stderr);
+ else
+ putc('?', stderr);
+ }
+ putc('\n', stderr);
+ for (s = st_line; s < st_cptr; ++s)
+ {
+ if (*s == '\t')
+ putc('\t', stderr);
+ else
+ putc(' ', stderr);
+ }
+ putc('^', stderr);
+ putc('\n', stderr);
+}
+
+
+syntax_error(st_lineno, st_line, st_cptr)
+int st_lineno;
+char *st_line;
+char *st_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n",
+ myname, st_lineno, input_file_name);
+ print_pos(st_line, st_cptr);
+ done(1);
+}
+
+
+unterminated_comment(c_lineno, c_line, c_cptr)
+int c_lineno;
+char *c_line;
+char *c_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n",
+ myname, c_lineno, input_file_name);
+ print_pos(c_line, c_cptr);
+ done(1);
+}
+
+
+unterminated_string(s_lineno, s_line, s_cptr)
+int s_lineno;
+char *s_line;
+char *s_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n",
+ myname, s_lineno, input_file_name);
+ print_pos(s_line, s_cptr);
+ done(1);
+}
+
+
+unterminated_text(t_lineno, t_line, t_cptr)
+int t_lineno;
+char *t_line;
+char *t_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n",
+ myname, t_lineno, input_file_name);
+ print_pos(t_line, t_cptr);
+ done(1);
+}
+
+
+unterminated_union(u_lineno, u_line, u_cptr)
+int u_lineno;
+char *u_line;
+char *u_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \
+declaration\n", myname, u_lineno, input_file_name);
+ print_pos(u_line, u_cptr);
+ done(1);
+}
+
+
+over_unionized(u_cptr)
+char *u_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \
+declarations\n", myname, lineno, input_file_name);
+ print_pos(line, u_cptr);
+ done(1);
+}
+
+
+illegal_tag(t_lineno, t_line, t_cptr)
+int t_lineno;
+char *t_line;
+char *t_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", illegal tag\n",
+ myname, t_lineno, input_file_name);
+ print_pos(t_line, t_cptr);
+ done(1);
+}
+
+
+illegal_character(c_cptr)
+char *c_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", illegal character\n",
+ myname, lineno, input_file_name);
+ print_pos(line, c_cptr);
+ done(1);
+}
+
+
+used_reserved(s)
+char *s;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", illegal use of reserved symbol \
+%s\n", myname, lineno, input_file_name, s);
+ done(1);
+}
+
+
+tokenized_start(s)
+char *s;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s cannot be \
+declared to be a token\n", myname, lineno, input_file_name, s);
+ done(1);
+}
+
+
+retyped_warning(s)
+char *s;
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the type of %s has been \
+redeclared\n", myname, lineno, input_file_name, s);
+}
+
+
+reprec_warning(s)
+char *s;
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the precedence of %s has been \
+redeclared\n", myname, lineno, input_file_name, s);
+}
+
+
+revalued_warning(s)
+char *s;
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the value of %s has been \
+redeclared\n", myname, lineno, input_file_name, s);
+}
+
+
+terminal_start(s)
+char *s;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s is a \
+token\n", myname, lineno, input_file_name, s);
+ done(1);
+}
+
+
+restarted_warning()
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has been \
+redeclared\n", myname, lineno, input_file_name);
+}
+
+
+no_grammar()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \
+specified\n", myname, lineno, input_file_name);
+ done(1);
+}
+
+
+terminal_lhs(s_lineno)
+int s_lineno;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", a token appears on the lhs \
+of a production\n", myname, s_lineno, input_file_name);
+ done(1);
+}
+
+
+prec_redeclared()
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \
+specifiers\n", myname, lineno, input_file_name);
+}
+
+
+unterminated_action(a_lineno, a_line, a_cptr)
+int a_lineno;
+char *a_line;
+char *a_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", unterminated action\n",
+ myname, a_lineno, input_file_name);
+ print_pos(a_line, a_cptr);
+ done(1);
+}
+
+
+dollar_warning(a_lineno, i)
+int a_lineno;
+int i;
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", $%d references beyond the \
+end of the current rule\n", myname, a_lineno, input_file_name, i);
+}
+
+
+dollar_error(a_lineno, a_line, a_cptr)
+int a_lineno;
+char *a_line;
+char *a_cptr;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", illegal $-name\n",
+ myname, a_lineno, input_file_name);
+ print_pos(a_line, a_cptr);
+ done(1);
+}
+
+
+untyped_lhs()
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n",
+ myname, lineno, input_file_name);
+ done(1);
+}
+
+
+untyped_rhs(i, s)
+int i;
+char *s;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", $%d (%s) is untyped\n",
+ myname, lineno, input_file_name, i, s);
+ done(1);
+}
+
+
+unknown_rhs(i)
+int i;
+{
+ fprintf(stderr, "%s: e - line %d of \"%s\", $%d is untyped\n",
+ myname, lineno, input_file_name, i);
+ done(1);
+}
+
+
+default_action_warning()
+{
+ fprintf(stderr, "%s: w - line %d of \"%s\", the default action assigns an \
+undefined value to $$\n", myname, lineno, input_file_name);
+}
+
+
+undefined_goal(s)
+char *s;
+{
+ fprintf(stderr, "%s: e - the start symbol %s is undefined\n", myname, s);
+ done(1);
+}
+
+
+undefined_symbol_warning(s)
+char *s;
+{
+ fprintf(stderr, "%s: w - the symbol %s is undefined\n", myname, s);
+}
diff --git a/usr.bin/yacc/lalr.c b/usr.bin/yacc/lalr.c
new file mode 100644
index 0000000..5007e50
--- /dev/null
+++ b/usr.bin/yacc/lalr.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lalr.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "defs.h"
+
+typedef
+ struct shorts
+ {
+ struct shorts *next;
+ short value;
+ }
+ shorts;
+
+int tokensetsize;
+short *lookaheads;
+short *LAruleno;
+unsigned *LA;
+short *accessing_symbol;
+core **state_table;
+shifts **shift_table;
+reductions **reduction_table;
+short *goto_map;
+short *from_state;
+short *to_state;
+
+short **transpose();
+
+static int infinity;
+static int maxrhs;
+static int ngotos;
+static unsigned *F;
+static short **includes;
+static shorts **lookback;
+static short **R;
+static short *INDEX;
+static short *VERTICES;
+static int top;
+
+
+lalr()
+{
+ tokensetsize = WORDSIZE(ntokens);
+
+ set_state_table();
+ set_accessing_symbol();
+ set_shift_table();
+ set_reduction_table();
+ set_maxrhs();
+ initialize_LA();
+ set_goto_map();
+ initialize_F();
+ build_relations();
+ compute_FOLLOWS();
+ compute_lookaheads();
+}
+
+
+
+set_state_table()
+{
+ register core *sp;
+
+ state_table = NEW2(nstates, core *);
+ for (sp = first_state; sp; sp = sp->next)
+ state_table[sp->number] = sp;
+}
+
+
+
+set_accessing_symbol()
+{
+ register core *sp;
+
+ accessing_symbol = NEW2(nstates, short);
+ for (sp = first_state; sp; sp = sp->next)
+ accessing_symbol[sp->number] = sp->accessing_symbol;
+}
+
+
+
+set_shift_table()
+{
+ register shifts *sp;
+
+ shift_table = NEW2(nstates, shifts *);
+ for (sp = first_shift; sp; sp = sp->next)
+ shift_table[sp->number] = sp;
+}
+
+
+
+set_reduction_table()
+{
+ register reductions *rp;
+
+ reduction_table = NEW2(nstates, reductions *);
+ for (rp = first_reduction; rp; rp = rp->next)
+ reduction_table[rp->number] = rp;
+}
+
+
+
+set_maxrhs()
+{
+ register short *itemp;
+ register short *item_end;
+ register int length;
+ register int max;
+
+ length = 0;
+ max = 0;
+ item_end = ritem + nitems;
+ for (itemp = ritem; itemp < item_end; itemp++)
+ {
+ if (*itemp >= 0)
+ {
+ length++;
+ }
+ else
+ {
+ if (length > max) max = length;
+ length = 0;
+ }
+ }
+
+ maxrhs = max;
+}
+
+
+
+initialize_LA()
+{
+ register int i, j, k;
+ register reductions *rp;
+
+ lookaheads = NEW2(nstates + 1, short);
+
+ k = 0;
+ for (i = 0; i < nstates; i++)
+ {
+ lookaheads[i] = k;
+ rp = reduction_table[i];
+ if (rp)
+ k += rp->nreds;
+ }
+ lookaheads[nstates] = k;
+
+ LA = NEW2(k * tokensetsize, unsigned);
+ LAruleno = NEW2(k, short);
+ lookback = NEW2(k, shorts *);
+
+ k = 0;
+ for (i = 0; i < nstates; i++)
+ {
+ rp = reduction_table[i];
+ if (rp)
+ {
+ for (j = 0; j < rp->nreds; j++)
+ {
+ LAruleno[k] = rp->rules[j];
+ k++;
+ }
+ }
+ }
+}
+
+
+set_goto_map()
+{
+ register shifts *sp;
+ register int i;
+ register int symbol;
+ register int k;
+ register short *temp_map;
+ register int state2;
+ register int state1;
+
+ goto_map = NEW2(nvars + 1, short) - ntokens;
+ temp_map = NEW2(nvars + 1, short) - ntokens;
+
+ ngotos = 0;
+ for (sp = first_shift; sp; sp = sp->next)
+ {
+ for (i = sp->nshifts - 1; i >= 0; i--)
+ {
+ symbol = accessing_symbol[sp->shift[i]];
+
+ if (ISTOKEN(symbol)) break;
+
+ if (ngotos == MAXSHORT)
+ fatal("too many gotos");
+
+ ngotos++;
+ goto_map[symbol]++;
+ }
+ }
+
+ k = 0;
+ for (i = ntokens; i < nsyms; i++)
+ {
+ temp_map[i] = k;
+ k += goto_map[i];
+ }
+
+ for (i = ntokens; i < nsyms; i++)
+ goto_map[i] = temp_map[i];
+
+ goto_map[nsyms] = ngotos;
+ temp_map[nsyms] = ngotos;
+
+ from_state = NEW2(ngotos, short);
+ to_state = NEW2(ngotos, short);
+
+ for (sp = first_shift; sp; sp = sp->next)
+ {
+ state1 = sp->number;
+ for (i = sp->nshifts - 1; i >= 0; i--)
+ {
+ state2 = sp->shift[i];
+ symbol = accessing_symbol[state2];
+
+ if (ISTOKEN(symbol)) break;
+
+ k = temp_map[symbol]++;
+ from_state[k] = state1;
+ to_state[k] = state2;
+ }
+ }
+
+ FREE(temp_map + ntokens);
+}
+
+
+
+/* Map_goto maps a state/symbol pair into its numeric representation. */
+
+int
+map_goto(state, symbol)
+int state;
+int symbol;
+{
+ register int high;
+ register int low;
+ register int middle;
+ register int s;
+
+ low = goto_map[symbol];
+ high = goto_map[symbol + 1];
+
+ for (;;)
+ {
+ assert(low <= high);
+ middle = (low + high) >> 1;
+ s = from_state[middle];
+ if (s == state)
+ return (middle);
+ else if (s < state)
+ low = middle + 1;
+ else
+ high = middle - 1;
+ }
+}
+
+
+
+initialize_F()
+{
+ register int i;
+ register int j;
+ register int k;
+ register shifts *sp;
+ register short *edge;
+ register unsigned *rowp;
+ register short *rp;
+ register short **reads;
+ register int nedges;
+ register int stateno;
+ register int symbol;
+ register int nwords;
+
+ nwords = ngotos * tokensetsize;
+ F = NEW2(nwords, unsigned);
+
+ reads = NEW2(ngotos, short *);
+ edge = NEW2(ngotos + 1, short);
+ nedges = 0;
+
+ rowp = F;
+ for (i = 0; i < ngotos; i++)
+ {
+ stateno = to_state[i];
+ sp = shift_table[stateno];
+
+ if (sp)
+ {
+ k = sp->nshifts;
+
+ for (j = 0; j < k; j++)
+ {
+ symbol = accessing_symbol[sp->shift[j]];
+ if (ISVAR(symbol))
+ break;
+ SETBIT(rowp, symbol);
+ }
+
+ for (; j < k; j++)
+ {
+ symbol = accessing_symbol[sp->shift[j]];
+ if (nullable[symbol])
+ edge[nedges++] = map_goto(stateno, symbol);
+ }
+
+ if (nedges)
+ {
+ reads[i] = rp = NEW2(nedges + 1, short);
+
+ for (j = 0; j < nedges; j++)
+ rp[j] = edge[j];
+
+ rp[nedges] = -1;
+ nedges = 0;
+ }
+ }
+
+ rowp += tokensetsize;
+ }
+
+ SETBIT(F, 0);
+ digraph(reads);
+
+ for (i = 0; i < ngotos; i++)
+ {
+ if (reads[i])
+ FREE(reads[i]);
+ }
+
+ FREE(reads);
+ FREE(edge);
+}
+
+
+
+build_relations()
+{
+ register int i;
+ register int j;
+ register int k;
+ register short *rulep;
+ register short *rp;
+ register shifts *sp;
+ register int length;
+ register int nedges;
+ register int done;
+ register int state1;
+ register int stateno;
+ register int symbol1;
+ register int symbol2;
+ register short *shortp;
+ register short *edge;
+ register short *states;
+ register short **new_includes;
+
+ includes = NEW2(ngotos, short *);
+ edge = NEW2(ngotos + 1, short);
+ states = NEW2(maxrhs + 1, short);
+
+ for (i = 0; i < ngotos; i++)
+ {
+ nedges = 0;
+ state1 = from_state[i];
+ symbol1 = accessing_symbol[to_state[i]];
+
+ for (rulep = derives[symbol1]; *rulep >= 0; rulep++)
+ {
+ length = 1;
+ states[0] = state1;
+ stateno = state1;
+
+ for (rp = ritem + rrhs[*rulep]; *rp >= 0; rp++)
+ {
+ symbol2 = *rp;
+ sp = shift_table[stateno];
+ k = sp->nshifts;
+
+ for (j = 0; j < k; j++)
+ {
+ stateno = sp->shift[j];
+ if (accessing_symbol[stateno] == symbol2) break;
+ }
+
+ states[length++] = stateno;
+ }
+
+ add_lookback_edge(stateno, *rulep, i);
+
+ length--;
+ done = 0;
+ while (!done)
+ {
+ done = 1;
+ rp--;
+ if (ISVAR(*rp))
+ {
+ stateno = states[--length];
+ edge[nedges++] = map_goto(stateno, *rp);
+ if (nullable[*rp] && length > 0) done = 0;
+ }
+ }
+ }
+
+ if (nedges)
+ {
+ includes[i] = shortp = NEW2(nedges + 1, short);
+ for (j = 0; j < nedges; j++)
+ shortp[j] = edge[j];
+ shortp[nedges] = -1;
+ }
+ }
+
+ new_includes = transpose(includes, ngotos);
+
+ for (i = 0; i < ngotos; i++)
+ if (includes[i])
+ FREE(includes[i]);
+
+ FREE(includes);
+
+ includes = new_includes;
+
+ FREE(edge);
+ FREE(states);
+}
+
+
+add_lookback_edge(stateno, ruleno, gotono)
+int stateno, ruleno, gotono;
+{
+ register int i, k;
+ register int found;
+ register shorts *sp;
+
+ i = lookaheads[stateno];
+ k = lookaheads[stateno + 1];
+ found = 0;
+ while (!found && i < k)
+ {
+ if (LAruleno[i] == ruleno)
+ found = 1;
+ else
+ ++i;
+ }
+ assert(found);
+
+ sp = NEW(shorts);
+ sp->next = lookback[i];
+ sp->value = gotono;
+ lookback[i] = sp;
+}
+
+
+
+short **
+transpose(R, n)
+short **R;
+int n;
+{
+ register short **new_R;
+ register short **temp_R;
+ register short *nedges;
+ register short *sp;
+ register int i;
+ register int k;
+
+ nedges = NEW2(n, short);
+
+ for (i = 0; i < n; i++)
+ {
+ sp = R[i];
+ if (sp)
+ {
+ while (*sp >= 0)
+ nedges[*sp++]++;
+ }
+ }
+
+ new_R = NEW2(n, short *);
+ temp_R = NEW2(n, short *);
+
+ for (i = 0; i < n; i++)
+ {
+ k = nedges[i];
+ if (k > 0)
+ {
+ sp = NEW2(k + 1, short);
+ new_R[i] = sp;
+ temp_R[i] = sp;
+ sp[k] = -1;
+ }
+ }
+
+ FREE(nedges);
+
+ for (i = 0; i < n; i++)
+ {
+ sp = R[i];
+ if (sp)
+ {
+ while (*sp >= 0)
+ *temp_R[*sp++]++ = i;
+ }
+ }
+
+ FREE(temp_R);
+
+ return (new_R);
+}
+
+
+
+compute_FOLLOWS()
+{
+ digraph(includes);
+}
+
+
+compute_lookaheads()
+{
+ register int i, n;
+ register unsigned *fp1, *fp2, *fp3;
+ register shorts *sp, *next;
+ register unsigned *rowp;
+
+ rowp = LA;
+ n = lookaheads[nstates];
+ for (i = 0; i < n; i++)
+ {
+ fp3 = rowp + tokensetsize;
+ for (sp = lookback[i]; sp; sp = sp->next)
+ {
+ fp1 = rowp;
+ fp2 = F + tokensetsize * sp->value;
+ while (fp1 < fp3)
+ *fp1++ |= *fp2++;
+ }
+ rowp = fp3;
+ }
+
+ for (i = 0; i < n; i++)
+ for (sp = lookback[i]; sp; sp = next)
+ {
+ next = sp->next;
+ FREE(sp);
+ }
+
+ FREE(lookback);
+ FREE(F);
+}
+
+
+digraph(relation)
+short **relation;
+{
+ register int i;
+
+ infinity = ngotos + 2;
+ INDEX = NEW2(ngotos + 1, short);
+ VERTICES = NEW2(ngotos + 1, short);
+ top = 0;
+
+ R = relation;
+
+ for (i = 0; i < ngotos; i++)
+ INDEX[i] = 0;
+
+ for (i = 0; i < ngotos; i++)
+ {
+ if (INDEX[i] == 0 && R[i])
+ traverse(i);
+ }
+
+ FREE(INDEX);
+ FREE(VERTICES);
+}
+
+
+
+traverse(i)
+register int i;
+{
+ register unsigned *fp1;
+ register unsigned *fp2;
+ register unsigned *fp3;
+ register int j;
+ register short *rp;
+
+ int height;
+ unsigned *base;
+
+ VERTICES[++top] = i;
+ INDEX[i] = height = top;
+
+ base = F + i * tokensetsize;
+ fp3 = base + tokensetsize;
+
+ rp = R[i];
+ if (rp)
+ {
+ while ((j = *rp++) >= 0)
+ {
+ if (INDEX[j] == 0)
+ traverse(j);
+
+ if (INDEX[i] > INDEX[j])
+ INDEX[i] = INDEX[j];
+
+ fp1 = base;
+ fp2 = F + j * tokensetsize;
+
+ while (fp1 < fp3)
+ *fp1++ |= *fp2++;
+ }
+ }
+
+ if (INDEX[i] == height)
+ {
+ for (;;)
+ {
+ j = VERTICES[top--];
+ INDEX[j] = infinity;
+
+ if (i == j)
+ break;
+
+ fp1 = base;
+ fp2 = F + j * tokensetsize;
+
+ while (fp1 < fp3)
+ *fp2++ = *fp1++;
+ }
+ }
+}
diff --git a/usr.bin/yacc/lr0.c b/usr.bin/yacc/lr0.c
new file mode 100644
index 0000000..43106ea
--- /dev/null
+++ b/usr.bin/yacc/lr0.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)lr0.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include "defs.h"
+
+extern short *itemset;
+extern short *itemsetend;
+extern unsigned *ruleset;
+
+int nstates;
+core *first_state;
+shifts *first_shift;
+reductions *first_reduction;
+
+int get_state();
+core *new_state();
+
+static core **state_set;
+static core *this_state;
+static core *last_state;
+static shifts *last_shift;
+static reductions *last_reduction;
+
+static int nshifts;
+static short *shift_symbol;
+
+static short *redset;
+static short *shiftset;
+
+static short **kernel_base;
+static short **kernel_end;
+static short *kernel_items;
+
+
+allocate_itemsets()
+{
+ register short *itemp;
+ register short *item_end;
+ register int symbol;
+ register int i;
+ register int count;
+ register int max;
+ register short *symbol_count;
+
+ count = 0;
+ symbol_count = NEW2(nsyms, short);
+
+ item_end = ritem + nitems;
+ for (itemp = ritem; itemp < item_end; itemp++)
+ {
+ symbol = *itemp;
+ if (symbol >= 0)
+ {
+ count++;
+ symbol_count[symbol]++;
+ }
+ }
+
+ kernel_base = NEW2(nsyms, short *);
+ kernel_items = NEW2(count, short);
+
+ count = 0;
+ max = 0;
+ for (i = 0; i < nsyms; i++)
+ {
+ kernel_base[i] = kernel_items + count;
+ count += symbol_count[i];
+ if (max < symbol_count[i])
+ max = symbol_count[i];
+ }
+
+ shift_symbol = symbol_count;
+ kernel_end = NEW2(nsyms, short *);
+}
+
+
+allocate_storage()
+{
+ allocate_itemsets();
+ shiftset = NEW2(nsyms, short);
+ redset = NEW2(nrules + 1, short);
+ state_set = NEW2(nitems, core *);
+}
+
+
+append_states()
+{
+ register int i;
+ register int j;
+ register int symbol;
+
+#ifdef TRACE
+ fprintf(stderr, "Entering append_states()\n");
+#endif
+ for (i = 1; i < nshifts; i++)
+ {
+ symbol = shift_symbol[i];
+ j = i;
+ while (j > 0 && shift_symbol[j - 1] > symbol)
+ {
+ shift_symbol[j] = shift_symbol[j - 1];
+ j--;
+ }
+ shift_symbol[j] = symbol;
+ }
+
+ for (i = 0; i < nshifts; i++)
+ {
+ symbol = shift_symbol[i];
+ shiftset[i] = get_state(symbol);
+ }
+}
+
+
+free_storage()
+{
+ FREE(shift_symbol);
+ FREE(redset);
+ FREE(shiftset);
+ FREE(kernel_base);
+ FREE(kernel_end);
+ FREE(kernel_items);
+ FREE(state_set);
+}
+
+
+
+generate_states()
+{
+ allocate_storage();
+ itemset = NEW2(nitems, short);
+ ruleset = NEW2(WORDSIZE(nrules), unsigned);
+ set_first_derives();
+ initialize_states();
+
+ while (this_state)
+ {
+ closure(this_state->items, this_state->nitems);
+ save_reductions();
+ new_itemsets();
+ append_states();
+
+ if (nshifts > 0)
+ save_shifts();
+
+ this_state = this_state->next;
+ }
+
+ finalize_closure();
+ free_storage();
+}
+
+
+
+int
+get_state(symbol)
+int symbol;
+{
+ register int key;
+ register short *isp1;
+ register short *isp2;
+ register short *iend;
+ register core *sp;
+ register int found;
+ register int n;
+
+#ifdef TRACE
+ fprintf(stderr, "Entering get_state(%d)\n", symbol);
+#endif
+
+ isp1 = kernel_base[symbol];
+ iend = kernel_end[symbol];
+ n = iend - isp1;
+
+ key = *isp1;
+ assert(0 <= key && key < nitems);
+ sp = state_set[key];
+ if (sp)
+ {
+ found = 0;
+ while (!found)
+ {
+ if (sp->nitems == n)
+ {
+ found = 1;
+ isp1 = kernel_base[symbol];
+ isp2 = sp->items;
+
+ while (found && isp1 < iend)
+ {
+ if (*isp1++ != *isp2++)
+ found = 0;
+ }
+ }
+
+ if (!found)
+ {
+ if (sp->link)
+ {
+ sp = sp->link;
+ }
+ else
+ {
+ sp = sp->link = new_state(symbol);
+ found = 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ state_set[key] = sp = new_state(symbol);
+ }
+
+ return (sp->number);
+}
+
+
+
+initialize_states()
+{
+ register int i;
+ register short *start_derives;
+ register core *p;
+
+ start_derives = derives[start_symbol];
+ for (i = 0; start_derives[i] >= 0; ++i)
+ continue;
+
+ p = (core *) MALLOC(sizeof(core) + i*sizeof(short));
+ if (p == 0) no_space();
+
+ p->next = 0;
+ p->link = 0;
+ p->number = 0;
+ p->accessing_symbol = 0;
+ p->nitems = i;
+
+ for (i = 0; start_derives[i] >= 0; ++i)
+ p->items[i] = rrhs[start_derives[i]];
+
+ first_state = last_state = this_state = p;
+ nstates = 1;
+}
+
+
+new_itemsets()
+{
+ register int i;
+ register int shiftcount;
+ register short *isp;
+ register short *ksp;
+ register int symbol;
+
+ for (i = 0; i < nsyms; i++)
+ kernel_end[i] = 0;
+
+ shiftcount = 0;
+ isp = itemset;
+ while (isp < itemsetend)
+ {
+ i = *isp++;
+ symbol = ritem[i];
+ if (symbol > 0)
+ {
+ ksp = kernel_end[symbol];
+ if (!ksp)
+ {
+ shift_symbol[shiftcount++] = symbol;
+ ksp = kernel_base[symbol];
+ }
+
+ *ksp++ = i + 1;
+ kernel_end[symbol] = ksp;
+ }
+ }
+
+ nshifts = shiftcount;
+}
+
+
+
+core *
+new_state(symbol)
+int symbol;
+{
+ register int n;
+ register core *p;
+ register short *isp1;
+ register short *isp2;
+ register short *iend;
+
+#ifdef TRACE
+ fprintf(stderr, "Entering new_state(%d)\n", symbol);
+#endif
+
+ if (nstates >= MAXSHORT)
+ fatal("too many states");
+
+ isp1 = kernel_base[symbol];
+ iend = kernel_end[symbol];
+ n = iend - isp1;
+
+ p = (core *) allocate((unsigned) (sizeof(core) + (n - 1) * sizeof(short)));
+ p->accessing_symbol = symbol;
+ p->number = nstates;
+ p->nitems = n;
+
+ isp2 = p->items;
+ while (isp1 < iend)
+ *isp2++ = *isp1++;
+
+ last_state->next = p;
+ last_state = p;
+
+ nstates++;
+
+ return (p);
+}
+
+
+/* show_cores is used for debugging */
+
+show_cores()
+{
+ core *p;
+ int i, j, k, n;
+ int itemno;
+
+ k = 0;
+ for (p = first_state; p; ++k, p = p->next)
+ {
+ if (k) printf("\n");
+ printf("state %d, number = %d, accessing symbol = %s\n",
+ k, p->number, symbol_name[p->accessing_symbol]);
+ n = p->nitems;
+ for (i = 0; i < n; ++i)
+ {
+ itemno = p->items[i];
+ printf("%4d ", itemno);
+ j = itemno;
+ while (ritem[j] >= 0) ++j;
+ printf("%s :", symbol_name[rlhs[-ritem[j]]]);
+ j = rrhs[-ritem[j]];
+ while (j < itemno)
+ printf(" %s", symbol_name[ritem[j++]]);
+ printf(" .");
+ while (ritem[j] >= 0)
+ printf(" %s", symbol_name[ritem[j++]]);
+ printf("\n");
+ fflush(stdout);
+ }
+ }
+}
+
+
+/* show_ritems is used for debugging */
+
+show_ritems()
+{
+ int i;
+
+ for (i = 0; i < nitems; ++i)
+ printf("ritem[%d] = %d\n", i, ritem[i]);
+}
+
+
+/* show_rrhs is used for debugging */
+show_rrhs()
+{
+ int i;
+
+ for (i = 0; i < nrules; ++i)
+ printf("rrhs[%d] = %d\n", i, rrhs[i]);
+}
+
+
+/* show_shifts is used for debugging */
+
+show_shifts()
+{
+ shifts *p;
+ int i, j, k;
+
+ k = 0;
+ for (p = first_shift; p; ++k, p = p->next)
+ {
+ if (k) printf("\n");
+ printf("shift %d, number = %d, nshifts = %d\n", k, p->number,
+ p->nshifts);
+ j = p->nshifts;
+ for (i = 0; i < j; ++i)
+ printf("\t%d\n", p->shift[i]);
+ }
+}
+
+
+save_shifts()
+{
+ register shifts *p;
+ register short *sp1;
+ register short *sp2;
+ register short *send;
+
+ p = (shifts *) allocate((unsigned) (sizeof(shifts) +
+ (nshifts - 1) * sizeof(short)));
+
+ p->number = this_state->number;
+ p->nshifts = nshifts;
+
+ sp1 = shiftset;
+ sp2 = p->shift;
+ send = shiftset + nshifts;
+
+ while (sp1 < send)
+ *sp2++ = *sp1++;
+
+ if (last_shift)
+ {
+ last_shift->next = p;
+ last_shift = p;
+ }
+ else
+ {
+ first_shift = p;
+ last_shift = p;
+ }
+}
+
+
+
+save_reductions()
+{
+ register short *isp;
+ register short *rp1;
+ register short *rp2;
+ register int item;
+ register int count;
+ register reductions *p;
+ register short *rend;
+
+ count = 0;
+ for (isp = itemset; isp < itemsetend; isp++)
+ {
+ item = ritem[*isp];
+ if (item < 0)
+ {
+ redset[count++] = -item;
+ }
+ }
+
+ if (count)
+ {
+ p = (reductions *) allocate((unsigned) (sizeof(reductions) +
+ (count - 1) * sizeof(short)));
+
+ p->number = this_state->number;
+ p->nreds = count;
+
+ rp1 = redset;
+ rp2 = p->rules;
+ rend = rp1 + count;
+
+ while (rp1 < rend)
+ *rp2++ = *rp1++;
+
+ if (last_reduction)
+ {
+ last_reduction->next = p;
+ last_reduction = p;
+ }
+ else
+ {
+ first_reduction = p;
+ last_reduction = p;
+ }
+ }
+}
+
+
+set_derives()
+{
+ register int i, k;
+ register int lhs;
+ register short *rules;
+
+ derives = NEW2(nsyms, short *);
+ rules = NEW2(nvars + nrules, short);
+
+ k = 0;
+ for (lhs = start_symbol; lhs < nsyms; lhs++)
+ {
+ derives[lhs] = rules + k;
+ for (i = 0; i < nrules; i++)
+ {
+ if (rlhs[i] == lhs)
+ {
+ rules[k] = i;
+ k++;
+ }
+ }
+ rules[k] = -1;
+ k++;
+ }
+
+#ifdef DEBUG
+ print_derives();
+#endif
+}
+
+free_derives()
+{
+ FREE(derives[start_symbol]);
+ FREE(derives);
+}
+
+#ifdef DEBUG
+print_derives()
+{
+ register int i;
+ register short *sp;
+
+ printf("\nDERIVES\n\n");
+
+ for (i = start_symbol; i < nsyms; i++)
+ {
+ printf("%s derives ", symbol_name[i]);
+ for (sp = derives[i]; *sp >= 0; sp++)
+ {
+ printf(" %d", *sp);
+ }
+ putchar('\n');
+ }
+
+ putchar('\n');
+}
+#endif
+
+
+set_nullable()
+{
+ register int i, j;
+ register int empty;
+ int done;
+
+ nullable = MALLOC(nsyms);
+ if (nullable == 0) no_space();
+
+ for (i = 0; i < nsyms; ++i)
+ nullable[i] = 0;
+
+ done = 0;
+ while (!done)
+ {
+ done = 1;
+ for (i = 1; i < nitems; i++)
+ {
+ empty = 1;
+ while ((j = ritem[i]) >= 0)
+ {
+ if (!nullable[j])
+ empty = 0;
+ ++i;
+ }
+ if (empty)
+ {
+ j = rlhs[-j];
+ if (!nullable[j])
+ {
+ nullable[j] = 1;
+ done = 0;
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ for (i = 0; i < nsyms; i++)
+ {
+ if (nullable[i])
+ printf("%s is nullable\n", symbol_name[i]);
+ else
+ printf("%s is not nullable\n", symbol_name[i]);
+ }
+#endif
+}
+
+
+free_nullable()
+{
+ FREE(nullable);
+}
+
+
+lr0()
+{
+ set_derives();
+ set_nullable();
+ generate_states();
+}
diff --git a/usr.bin/yacc/main.c b/usr.bin/yacc/main.c
new file mode 100644
index 0000000..79b332a
--- /dev/null
+++ b/usr.bin/yacc/main.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 5.5 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include <signal.h>
+#include "defs.h"
+
+char dflag;
+char lflag;
+char rflag;
+char tflag;
+char vflag;
+
+char *symbol_prefix;
+char *file_prefix = "y";
+char *myname = "yacc";
+char *temp_form = "yacc.XXXXXXX";
+
+int lineno;
+int outline;
+
+char *action_file_name;
+char *code_file_name;
+char *defines_file_name;
+char *input_file_name = "";
+char *output_file_name;
+char *text_file_name;
+char *union_file_name;
+char *verbose_file_name;
+
+FILE *action_file; /* a temp file, used to save actions associated */
+ /* with rules until the parser is written */
+FILE *code_file; /* y.code.c (used when the -r option is specified) */
+FILE *defines_file; /* y.tab.h */
+FILE *input_file; /* the input file */
+FILE *output_file; /* y.tab.c */
+FILE *text_file; /* a temp file, used to save text until all */
+ /* symbols have been defined */
+FILE *union_file; /* a temp file, used to save the union */
+ /* definition until all symbol have been */
+ /* defined */
+FILE *verbose_file; /* y.output */
+
+int nitems;
+int nrules;
+int nsyms;
+int ntokens;
+int nvars;
+
+int start_symbol;
+char **symbol_name;
+short *symbol_value;
+short *symbol_prec;
+char *symbol_assoc;
+
+short *ritem;
+short *rlhs;
+short *rrhs;
+short *rprec;
+char *rassoc;
+short **derives;
+char *nullable;
+
+extern char *mktemp();
+extern char *getenv();
+
+
+done(k)
+int k;
+{
+ if (action_file) { fclose(action_file); unlink(action_file_name); }
+ if (text_file) { fclose(text_file); unlink(text_file_name); }
+ if (union_file) { fclose(union_file); unlink(union_file_name); }
+ exit(k);
+}
+
+
+void
+onintr(signo)
+ int signo;
+{
+ done(1);
+}
+
+
+set_signals()
+{
+#ifdef SIGINT
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, onintr);
+#endif
+#ifdef SIGTERM
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, onintr);
+#endif
+#ifdef SIGHUP
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, onintr);
+#endif
+}
+
+
+usage()
+{
+ fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname);
+ exit(1);
+}
+
+
+getargs(argc, argv)
+int argc;
+char *argv[];
+{
+ register int i;
+ register char *s;
+
+ if (argc > 0) myname = argv[0];
+ for (i = 1; i < argc; ++i)
+ {
+ s = argv[i];
+ if (*s != '-') break;
+ switch (*++s)
+ {
+ case '\0':
+ input_file = stdin;
+ if (i + 1 < argc) usage();
+ return;
+
+ case '-':
+ ++i;
+ goto no_more_options;
+
+ case 'b':
+ if (*++s)
+ file_prefix = s;
+ else if (++i < argc)
+ file_prefix = argv[i];
+ else
+ usage();
+ continue;
+
+ case 'd':
+ dflag = 1;
+ break;
+
+ case 'l':
+ lflag = 1;
+ break;
+
+ case 'p':
+ if (*++s)
+ symbol_prefix = s;
+ else if (++i < argc)
+ symbol_prefix = argv[i];
+ else
+ usage();
+ continue;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ case 't':
+ tflag = 1;
+ break;
+
+ case 'v':
+ vflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+
+ for (;;)
+ {
+ switch (*++s)
+ {
+ case '\0':
+ goto end_of_option;
+
+ case 'd':
+ dflag = 1;
+ break;
+
+ case 'l':
+ lflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ case 't':
+ tflag = 1;
+ break;
+
+ case 'v':
+ vflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+end_of_option:;
+ }
+
+no_more_options:;
+ if (i + 1 != argc) usage();
+ input_file_name = argv[i];
+}
+
+
+char *
+allocate(n)
+unsigned n;
+{
+ register char *p;
+
+ p = NULL;
+ if (n)
+ {
+ p = CALLOC(1, n);
+ if (!p) no_space();
+ }
+ return (p);
+}
+
+
+create_file_names()
+{
+ int i, len;
+ char *tmpdir;
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == 0) tmpdir = "/tmp";
+
+ len = strlen(tmpdir);
+ i = len + 13;
+ if (len && tmpdir[len-1] != '/')
+ ++i;
+
+ action_file_name = MALLOC(i);
+ if (action_file_name == 0) no_space();
+ text_file_name = MALLOC(i);
+ if (text_file_name == 0) no_space();
+ union_file_name = MALLOC(i);
+ if (union_file_name == 0) no_space();
+
+ strcpy(action_file_name, tmpdir);
+ strcpy(text_file_name, tmpdir);
+ strcpy(union_file_name, tmpdir);
+
+ if (len && tmpdir[len - 1] != '/')
+ {
+ action_file_name[len] = '/';
+ text_file_name[len] = '/';
+ union_file_name[len] = '/';
+ ++len;
+ }
+
+ strcpy(action_file_name + len, temp_form);
+ strcpy(text_file_name + len, temp_form);
+ strcpy(union_file_name + len, temp_form);
+
+ action_file_name[len + 5] = 'a';
+ text_file_name[len + 5] = 't';
+ union_file_name[len + 5] = 'u';
+
+ mktemp(action_file_name);
+ mktemp(text_file_name);
+ mktemp(union_file_name);
+
+ len = strlen(file_prefix);
+
+ output_file_name = MALLOC(len + 7);
+ if (output_file_name == 0)
+ no_space();
+ strcpy(output_file_name, file_prefix);
+ strcpy(output_file_name + len, OUTPUT_SUFFIX);
+
+ if (rflag)
+ {
+ code_file_name = MALLOC(len + 8);
+ if (code_file_name == 0)
+ no_space();
+ strcpy(code_file_name, file_prefix);
+ strcpy(code_file_name + len, CODE_SUFFIX);
+ }
+ else
+ code_file_name = output_file_name;
+
+ if (dflag)
+ {
+ defines_file_name = MALLOC(len + 7);
+ if (defines_file_name == 0)
+ no_space();
+ strcpy(defines_file_name, file_prefix);
+ strcpy(defines_file_name + len, DEFINES_SUFFIX);
+ }
+
+ if (vflag)
+ {
+ verbose_file_name = MALLOC(len + 8);
+ if (verbose_file_name == 0)
+ no_space();
+ strcpy(verbose_file_name, file_prefix);
+ strcpy(verbose_file_name + len, VERBOSE_SUFFIX);
+ }
+}
+
+
+open_files()
+{
+ create_file_names();
+
+ if (input_file == 0)
+ {
+ input_file = fopen(input_file_name, "r");
+ if (input_file == 0)
+ open_error(input_file_name);
+ }
+
+ action_file = fopen(action_file_name, "w");
+ if (action_file == 0)
+ open_error(action_file_name);
+
+ text_file = fopen(text_file_name, "w");
+ if (text_file == 0)
+ open_error(text_file_name);
+
+ if (vflag)
+ {
+ verbose_file = fopen(verbose_file_name, "w");
+ if (verbose_file == 0)
+ open_error(verbose_file_name);
+ }
+
+ if (dflag)
+ {
+ defines_file = fopen(defines_file_name, "w");
+ if (defines_file == 0)
+ open_error(defines_file_name);
+ union_file = fopen(union_file_name, "w");
+ if (union_file == 0)
+ open_error(union_file_name);
+ }
+
+ output_file = fopen(output_file_name, "w");
+ if (output_file == 0)
+ open_error(output_file_name);
+
+ if (rflag)
+ {
+ code_file = fopen(code_file_name, "w");
+ if (code_file == 0)
+ open_error(code_file_name);
+ }
+ else
+ code_file = output_file;
+}
+
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ set_signals();
+ getargs(argc, argv);
+ open_files();
+ reader();
+ lr0();
+ lalr();
+ make_parser();
+ verbose();
+ output();
+ done(0);
+ /*NOTREACHED*/
+}
diff --git a/usr.bin/yacc/mkpar.c b/usr.bin/yacc/mkpar.c
new file mode 100644
index 0000000..b0f94b4
--- /dev/null
+++ b/usr.bin/yacc/mkpar.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mkpar.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include "defs.h"
+
+action **parser;
+int SRtotal;
+int RRtotal;
+short *SRconflicts;
+short *RRconflicts;
+short *defred;
+short *rules_used;
+short nunused;
+short final_state;
+
+static int SRcount;
+static int RRcount;
+
+extern action *parse_actions();
+extern action *get_shifts();
+extern action *add_reductions();
+extern action *add_reduce();
+
+
+make_parser()
+{
+ register int i;
+
+ parser = NEW2(nstates, action *);
+ for (i = 0; i < nstates; i++)
+ parser[i] = parse_actions(i);
+
+ find_final_state();
+ remove_conflicts();
+ unused_rules();
+ if (SRtotal + RRtotal > 0) total_conflicts();
+ defreds();
+}
+
+
+action *
+parse_actions(stateno)
+register int stateno;
+{
+ register action *actions;
+
+ actions = get_shifts(stateno);
+ actions = add_reductions(stateno, actions);
+ return (actions);
+}
+
+
+action *
+get_shifts(stateno)
+int stateno;
+{
+ register action *actions, *temp;
+ register shifts *sp;
+ register short *to_state;
+ register int i, k;
+ register int symbol;
+
+ actions = 0;
+ sp = shift_table[stateno];
+ if (sp)
+ {
+ to_state = sp->shift;
+ for (i = sp->nshifts - 1; i >= 0; i--)
+ {
+ k = to_state[i];
+ symbol = accessing_symbol[k];
+ if (ISTOKEN(symbol))
+ {
+ temp = NEW(action);
+ temp->next = actions;
+ temp->symbol = symbol;
+ temp->number = k;
+ temp->prec = symbol_prec[symbol];
+ temp->action_code = SHIFT;
+ temp->assoc = symbol_assoc[symbol];
+ actions = temp;
+ }
+ }
+ }
+ return (actions);
+}
+
+action *
+add_reductions(stateno, actions)
+int stateno;
+register action *actions;
+{
+ register int i, j, m, n;
+ register int ruleno, tokensetsize;
+ register unsigned *rowp;
+
+ tokensetsize = WORDSIZE(ntokens);
+ m = lookaheads[stateno];
+ n = lookaheads[stateno + 1];
+ for (i = m; i < n; i++)
+ {
+ ruleno = LAruleno[i];
+ rowp = LA + i * tokensetsize;
+ for (j = ntokens - 1; j >= 0; j--)
+ {
+ if (BIT(rowp, j))
+ actions = add_reduce(actions, ruleno, j);
+ }
+ }
+ return (actions);
+}
+
+
+action *
+add_reduce(actions, ruleno, symbol)
+register action *actions;
+register int ruleno, symbol;
+{
+ register action *temp, *prev, *next;
+
+ prev = 0;
+ for (next = actions; next && next->symbol < symbol; next = next->next)
+ prev = next;
+
+ while (next && next->symbol == symbol && next->action_code == SHIFT)
+ {
+ prev = next;
+ next = next->next;
+ }
+
+ while (next && next->symbol == symbol &&
+ next->action_code == REDUCE && next->number < ruleno)
+ {
+ prev = next;
+ next = next->next;
+ }
+
+ temp = NEW(action);
+ temp->next = next;
+ temp->symbol = symbol;
+ temp->number = ruleno;
+ temp->prec = rprec[ruleno];
+ temp->action_code = REDUCE;
+ temp->assoc = rassoc[ruleno];
+
+ if (prev)
+ prev->next = temp;
+ else
+ actions = temp;
+
+ return (actions);
+}
+
+
+find_final_state()
+{
+ register int goal, i;
+ register short *to_state;
+ register shifts *p;
+
+ p = shift_table[0];
+ to_state = p->shift;
+ goal = ritem[1];
+ for (i = p->nshifts - 1; i >= 0; --i)
+ {
+ final_state = to_state[i];
+ if (accessing_symbol[final_state] == goal) break;
+ }
+}
+
+
+unused_rules()
+{
+ register int i;
+ register action *p;
+
+ rules_used = (short *) MALLOC(nrules*sizeof(short));
+ if (rules_used == 0) no_space();
+
+ for (i = 0; i < nrules; ++i)
+ rules_used[i] = 0;
+
+ for (i = 0; i < nstates; ++i)
+ {
+ for (p = parser[i]; p; p = p->next)
+ {
+ if (p->action_code == REDUCE && p->suppressed == 0)
+ rules_used[p->number] = 1;
+ }
+ }
+
+ nunused = 0;
+ for (i = 3; i < nrules; ++i)
+ if (!rules_used[i]) ++nunused;
+
+ if (nunused)
+ if (nunused == 1)
+ fprintf(stderr, "%s: 1 rule never reduced\n", myname);
+ else
+ fprintf(stderr, "%s: %d rules never reduced\n", myname, nunused);
+}
+
+
+remove_conflicts()
+{
+ register int i;
+ register int symbol;
+ register action *p, *pref;
+
+ SRtotal = 0;
+ RRtotal = 0;
+ SRconflicts = NEW2(nstates, short);
+ RRconflicts = NEW2(nstates, short);
+ for (i = 0; i < nstates; i++)
+ {
+ SRcount = 0;
+ RRcount = 0;
+ symbol = -1;
+ for (p = parser[i]; p; p = p->next)
+ {
+ if (p->symbol != symbol)
+ {
+ pref = p;
+ symbol = p->symbol;
+ }
+ else if (i == final_state && symbol == 0)
+ {
+ SRcount++;
+ p->suppressed = 1;
+ }
+ else if (pref->action_code == SHIFT)
+ {
+ if (pref->prec > 0 && p->prec > 0)
+ {
+ if (pref->prec < p->prec)
+ {
+ pref->suppressed = 2;
+ pref = p;
+ }
+ else if (pref->prec > p->prec)
+ {
+ p->suppressed = 2;
+ }
+ else if (pref->assoc == LEFT)
+ {
+ pref->suppressed = 2;
+ pref = p;
+ }
+ else if (pref->assoc == RIGHT)
+ {
+ p->suppressed = 2;
+ }
+ else
+ {
+ pref->suppressed = 2;
+ p->suppressed = 2;
+ }
+ }
+ else
+ {
+ SRcount++;
+ p->suppressed = 1;
+ }
+ }
+ else
+ {
+ RRcount++;
+ p->suppressed = 1;
+ }
+ }
+ SRtotal += SRcount;
+ RRtotal += RRcount;
+ SRconflicts[i] = SRcount;
+ RRconflicts[i] = RRcount;
+ }
+}
+
+
+total_conflicts()
+{
+ fprintf(stderr, "%s: ", myname);
+ if (SRtotal == 1)
+ fprintf(stderr, "1 shift/reduce conflict");
+ else if (SRtotal > 1)
+ fprintf(stderr, "%d shift/reduce conflicts", SRtotal);
+
+ if (SRtotal && RRtotal)
+ fprintf(stderr, ", ");
+
+ if (RRtotal == 1)
+ fprintf(stderr, "1 reduce/reduce conflict");
+ else if (RRtotal > 1)
+ fprintf(stderr, "%d reduce/reduce conflicts", RRtotal);
+
+ fprintf(stderr, ".\n");
+}
+
+
+int
+sole_reduction(stateno)
+int stateno;
+{
+ register int count, ruleno;
+ register action *p;
+
+ count = 0;
+ ruleno = 0;
+ for (p = parser[stateno]; p; p = p->next)
+ {
+ if (p->action_code == SHIFT && p->suppressed == 0)
+ return (0);
+ else if (p->action_code == REDUCE && p->suppressed == 0)
+ {
+ if (ruleno > 0 && p->number != ruleno)
+ return (0);
+ if (p->symbol != 1)
+ ++count;
+ ruleno = p->number;
+ }
+ }
+
+ if (count == 0)
+ return (0);
+ return (ruleno);
+}
+
+
+defreds()
+{
+ register int i;
+
+ defred = NEW2(nstates, short);
+ for (i = 0; i < nstates; i++)
+ defred[i] = sole_reduction(i);
+}
+
+free_action_row(p)
+register action *p;
+{
+ register action *q;
+
+ while (p)
+ {
+ q = p->next;
+ FREE(p);
+ p = q;
+ }
+}
+
+free_parser()
+{
+ register int i;
+
+ for (i = 0; i < nstates; i++)
+ free_action_row(parser[i]);
+
+ FREE(parser);
+}
diff --git a/usr.bin/yacc/output.c b/usr.bin/yacc/output.c
new file mode 100644
index 0000000..67c755a
--- /dev/null
+++ b/usr.bin/yacc/output.c
@@ -0,0 +1,1250 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c 5.7 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+static int nvectors;
+static int nentries;
+static short **froms;
+static short **tos;
+static short *tally;
+static short *width;
+static short *state_count;
+static short *order;
+static short *base;
+static short *pos;
+static int maxtable;
+static short *table;
+static short *check;
+static int lowzero;
+static int high;
+
+
+output()
+{
+ free_itemsets();
+ free_shifts();
+ free_reductions();
+ output_prefix();
+ output_stored_text();
+ output_defines();
+ output_rule_data();
+ output_yydefred();
+ output_actions();
+ free_parser();
+ output_debug();
+ output_stype();
+ if (rflag) write_section(tables);
+ write_section(header);
+ output_trailing_text();
+ write_section(body);
+ output_semantic_actions();
+ write_section(trailer);
+}
+
+
+output_prefix()
+{
+ if (symbol_prefix == NULL)
+ symbol_prefix = "yy";
+ else
+ {
+ ++outline;
+ fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yylex %slex\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyerror %serror\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yychar %schar\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyval %sval\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yylval %slval\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyss %sss\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyvs %svs\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yylen %slen\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yytable %stable\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyname %sname\n", symbol_prefix);
+ ++outline;
+ fprintf(code_file, "#define yyrule %srule\n", symbol_prefix);
+ }
+ ++outline;
+ fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix);
+}
+
+
+output_rule_data()
+{
+ register int i;
+ register int j;
+
+
+ fprintf(output_file, "short %slhs[] = {%42d,", symbol_prefix,
+ symbol_value[start_symbol]);
+
+ j = 10;
+ for (i = 3; i < nrules; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", symbol_value[rlhs[i]]);
+ }
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+
+ fprintf(output_file, "short %slen[] = {%42d,", symbol_prefix, 2);
+
+ j = 10;
+ for (i = 3; i < nrules; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ j++;
+
+ fprintf(output_file, "%5d,", rrhs[i + 1] - rrhs[i] - 1);
+ }
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+}
+
+
+output_yydefred()
+{
+ register int i, j;
+
+ fprintf(output_file, "short %sdefred[] = {%39d,", symbol_prefix,
+ (defred[0] ? defred[0] - 2 : 0));
+
+ j = 10;
+ for (i = 1; i < nstates; i++)
+ {
+ if (j < 10)
+ ++j;
+ else
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+
+ fprintf(output_file, "%5d,", (defred[i] ? defred[i] - 2 : 0));
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+}
+
+
+output_actions()
+{
+ nvectors = 2*nstates + nvars;
+
+ froms = NEW2(nvectors, short *);
+ tos = NEW2(nvectors, short *);
+ tally = NEW2(nvectors, short);
+ width = NEW2(nvectors, short);
+
+ token_actions();
+ FREE(lookaheads);
+ FREE(LA);
+ FREE(LAruleno);
+ FREE(accessing_symbol);
+
+ goto_actions();
+ FREE(goto_map + ntokens);
+ FREE(from_state);
+ FREE(to_state);
+
+ sort_actions();
+ pack_table();
+ output_base();
+ output_table();
+ output_check();
+}
+
+
+token_actions()
+{
+ register int i, j;
+ register int shiftcount, reducecount;
+ register int max, min;
+ register short *actionrow, *r, *s;
+ register action *p;
+
+ actionrow = NEW2(2*ntokens, short);
+ for (i = 0; i < nstates; ++i)
+ {
+ if (parser[i])
+ {
+ for (j = 0; j < 2*ntokens; ++j)
+ actionrow[j] = 0;
+
+ shiftcount = 0;
+ reducecount = 0;
+ for (p = parser[i]; p; p = p->next)
+ {
+ if (p->suppressed == 0)
+ {
+ if (p->action_code == SHIFT)
+ {
+ ++shiftcount;
+ actionrow[p->symbol] = p->number;
+ }
+ else if (p->action_code == REDUCE && p->number != defred[i])
+ {
+ ++reducecount;
+ actionrow[p->symbol + ntokens] = p->number;
+ }
+ }
+ }
+
+ tally[i] = shiftcount;
+ tally[nstates+i] = reducecount;
+ width[i] = 0;
+ width[nstates+i] = 0;
+ if (shiftcount > 0)
+ {
+ froms[i] = r = NEW2(shiftcount, short);
+ tos[i] = s = NEW2(shiftcount, short);
+ min = MAXSHORT;
+ max = 0;
+ for (j = 0; j < ntokens; ++j)
+ {
+ if (actionrow[j])
+ {
+ if (min > symbol_value[j])
+ min = symbol_value[j];
+ if (max < symbol_value[j])
+ max = symbol_value[j];
+ *r++ = symbol_value[j];
+ *s++ = actionrow[j];
+ }
+ }
+ width[i] = max - min + 1;
+ }
+ if (reducecount > 0)
+ {
+ froms[nstates+i] = r = NEW2(reducecount, short);
+ tos[nstates+i] = s = NEW2(reducecount, short);
+ min = MAXSHORT;
+ max = 0;
+ for (j = 0; j < ntokens; ++j)
+ {
+ if (actionrow[ntokens+j])
+ {
+ if (min > symbol_value[j])
+ min = symbol_value[j];
+ if (max < symbol_value[j])
+ max = symbol_value[j];
+ *r++ = symbol_value[j];
+ *s++ = actionrow[ntokens+j] - 2;
+ }
+ }
+ width[nstates+i] = max - min + 1;
+ }
+ }
+ }
+ FREE(actionrow);
+}
+
+goto_actions()
+{
+ register int i, j, k;
+
+ state_count = NEW2(nstates, short);
+
+ k = default_goto(start_symbol + 1);
+ fprintf(output_file, "short %sdgoto[] = {%40d,", symbol_prefix, k);
+ save_column(start_symbol + 1, k);
+
+ j = 10;
+ for (i = start_symbol + 2; i < nsyms; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ k = default_goto(i);
+ fprintf(output_file, "%5d,", k);
+ save_column(i, k);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+ FREE(state_count);
+}
+
+int
+default_goto(symbol)
+int symbol;
+{
+ register int i;
+ register int m;
+ register int n;
+ register int default_state;
+ register int max;
+
+ m = goto_map[symbol];
+ n = goto_map[symbol + 1];
+
+ if (m == n) return (0);
+
+ for (i = 0; i < nstates; i++)
+ state_count[i] = 0;
+
+ for (i = m; i < n; i++)
+ state_count[to_state[i]]++;
+
+ max = 0;
+ default_state = 0;
+ for (i = 0; i < nstates; i++)
+ {
+ if (state_count[i] > max)
+ {
+ max = state_count[i];
+ default_state = i;
+ }
+ }
+
+ return (default_state);
+}
+
+
+
+save_column(symbol, default_state)
+int symbol;
+int default_state;
+{
+ register int i;
+ register int m;
+ register int n;
+ register short *sp;
+ register short *sp1;
+ register short *sp2;
+ register int count;
+ register int symno;
+
+ m = goto_map[symbol];
+ n = goto_map[symbol + 1];
+
+ count = 0;
+ for (i = m; i < n; i++)
+ {
+ if (to_state[i] != default_state)
+ ++count;
+ }
+ if (count == 0) return;
+
+ symno = symbol_value[symbol] + 2*nstates;
+
+ froms[symno] = sp1 = sp = NEW2(count, short);
+ tos[symno] = sp2 = NEW2(count, short);
+
+ for (i = m; i < n; i++)
+ {
+ if (to_state[i] != default_state)
+ {
+ *sp1++ = from_state[i];
+ *sp2++ = to_state[i];
+ }
+ }
+
+ tally[symno] = count;
+ width[symno] = sp1[-1] - sp[0] + 1;
+}
+
+sort_actions()
+{
+ register int i;
+ register int j;
+ register int k;
+ register int t;
+ register int w;
+
+ order = NEW2(nvectors, short);
+ nentries = 0;
+
+ for (i = 0; i < nvectors; i++)
+ {
+ if (tally[i] > 0)
+ {
+ t = tally[i];
+ w = width[i];
+ j = nentries - 1;
+
+ while (j >= 0 && (width[order[j]] < w))
+ j--;
+
+ while (j >= 0 && (width[order[j]] == w) && (tally[order[j]] < t))
+ j--;
+
+ for (k = nentries - 1; k > j; k--)
+ order[k + 1] = order[k];
+
+ order[j + 1] = i;
+ nentries++;
+ }
+ }
+}
+
+
+pack_table()
+{
+ register int i;
+ register int place;
+ register int state;
+
+ base = NEW2(nvectors, short);
+ pos = NEW2(nentries, short);
+
+ maxtable = 1000;
+ table = NEW2(maxtable, short);
+ check = NEW2(maxtable, short);
+
+ lowzero = 0;
+ high = 0;
+
+ for (i = 0; i < maxtable; i++)
+ check[i] = -1;
+
+ for (i = 0; i < nentries; i++)
+ {
+ state = matching_vector(i);
+
+ if (state < 0)
+ place = pack_vector(i);
+ else
+ place = base[state];
+
+ pos[i] = place;
+ base[order[i]] = place;
+ }
+
+ for (i = 0; i < nvectors; i++)
+ {
+ if (froms[i])
+ FREE(froms[i]);
+ if (tos[i])
+ FREE(tos[i]);
+ }
+
+ FREE(froms);
+ FREE(tos);
+ FREE(pos);
+}
+
+
+/* The function matching_vector determines if the vector specified by */
+/* the input parameter matches a previously considered vector. The */
+/* test at the start of the function checks if the vector represents */
+/* a row of shifts over terminal symbols or a row of reductions, or a */
+/* column of shifts over a nonterminal symbol. Berkeley Yacc does not */
+/* check if a column of shifts over a nonterminal symbols matches a */
+/* previously considered vector. Because of the nature of LR parsing */
+/* tables, no two columns can match. Therefore, the only possible */
+/* match would be between a row and a column. Such matches are */
+/* unlikely. Therefore, to save time, no attempt is made to see if a */
+/* column matches a previously considered vector. */
+/* */
+/* Matching_vector is poorly designed. The test could easily be made */
+/* faster. Also, it depends on the vectors being in a specific */
+/* order. */
+
+int
+matching_vector(vector)
+int vector;
+{
+ register int i;
+ register int j;
+ register int k;
+ register int t;
+ register int w;
+ register int match;
+ register int prev;
+
+ i = order[vector];
+ if (i >= 2*nstates)
+ return (-1);
+
+ t = tally[i];
+ w = width[i];
+
+ for (prev = vector - 1; prev >= 0; prev--)
+ {
+ j = order[prev];
+ if (width[j] != w || tally[j] != t)
+ return (-1);
+
+ match = 1;
+ for (k = 0; match && k < t; k++)
+ {
+ if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k])
+ match = 0;
+ }
+
+ if (match)
+ return (j);
+ }
+
+ return (-1);
+}
+
+
+
+int
+pack_vector(vector)
+int vector;
+{
+ register int i, j, k, l;
+ register int t;
+ register int loc;
+ register int ok;
+ register short *from;
+ register short *to;
+ int newmax;
+
+ i = order[vector];
+ t = tally[i];
+ assert(t);
+
+ from = froms[i];
+ to = tos[i];
+
+ j = lowzero - from[0];
+ for (k = 1; k < t; ++k)
+ if (lowzero - from[k] > j)
+ j = lowzero - from[k];
+ for (;; ++j)
+ {
+ if (j == 0)
+ continue;
+ ok = 1;
+ for (k = 0; ok && k < t; k++)
+ {
+ loc = j + from[k];
+ if (loc >= maxtable)
+ {
+ if (loc >= MAXTABLE)
+ fatal("maximum table size exceeded");
+
+ newmax = maxtable;
+ do { newmax += 200; } while (newmax <= loc);
+ table = (short *) REALLOC(table, newmax*sizeof(short));
+ if (table == 0) no_space();
+ check = (short *) REALLOC(check, newmax*sizeof(short));
+ if (check == 0) no_space();
+ for (l = maxtable; l < newmax; ++l)
+ {
+ table[l] = 0;
+ check[l] = -1;
+ }
+ maxtable = newmax;
+ }
+
+ if (check[loc] != -1)
+ ok = 0;
+ }
+ for (k = 0; ok && k < vector; k++)
+ {
+ if (pos[k] == j)
+ ok = 0;
+ }
+ if (ok)
+ {
+ for (k = 0; k < t; k++)
+ {
+ loc = j + from[k];
+ table[loc] = to[k];
+ check[loc] = from[k];
+ if (loc > high) high = loc;
+ }
+
+ while (check[lowzero] != -1)
+ ++lowzero;
+
+ return (j);
+ }
+ }
+}
+
+
+
+output_base()
+{
+ register int i, j;
+
+ fprintf(output_file, "short %ssindex[] = {%39d,", symbol_prefix, base[0]);
+
+ j = 10;
+ for (i = 1; i < nstates; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", base[i]);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\nshort %srindex[] = {%39d,", symbol_prefix,
+ base[nstates]);
+
+ j = 10;
+ for (i = nstates + 1; i < 2*nstates; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", base[i]);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\nshort %sgindex[] = {%39d,", symbol_prefix,
+ base[2*nstates]);
+
+ j = 10;
+ for (i = 2*nstates + 1; i < nvectors - 1; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", base[i]);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+ FREE(base);
+}
+
+
+
+output_table()
+{
+ register int i;
+ register int j;
+
+ ++outline;
+ fprintf(code_file, "#define YYTABLESIZE %d\n", high);
+ fprintf(output_file, "short %stable[] = {%40d,", symbol_prefix,
+ table[0]);
+
+ j = 10;
+ for (i = 1; i <= high; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", table[i]);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+ FREE(table);
+}
+
+
+
+output_check()
+{
+ register int i;
+ register int j;
+
+ fprintf(output_file, "short %scheck[] = {%40d,", symbol_prefix,
+ check[0]);
+
+ j = 10;
+ for (i = 1; i <= high; i++)
+ {
+ if (j >= 10)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 1;
+ }
+ else
+ ++j;
+
+ fprintf(output_file, "%5d,", check[i]);
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+ FREE(check);
+}
+
+
+int
+is_C_identifier(name)
+char *name;
+{
+ register char *s;
+ register int c;
+
+ s = name;
+ c = *s;
+ if (c == '"')
+ {
+ c = *++s;
+ if (!isalpha(c) && c != '_' && c != '$')
+ return (0);
+ while ((c = *++s) != '"')
+ {
+ if (!isalnum(c) && c != '_' && c != '$')
+ return (0);
+ }
+ return (1);
+ }
+
+ if (!isalpha(c) && c != '_' && c != '$')
+ return (0);
+ while (c = *++s)
+ {
+ if (!isalnum(c) && c != '_' && c != '$')
+ return (0);
+ }
+ return (1);
+}
+
+
+output_defines()
+{
+ register int c, i;
+ register char *s;
+
+ for (i = 2; i < ntokens; ++i)
+ {
+ s = symbol_name[i];
+ if (is_C_identifier(s))
+ {
+ fprintf(code_file, "#define ");
+ if (dflag) fprintf(defines_file, "#define ");
+ c = *s;
+ if (c == '"')
+ {
+ while ((c = *++s) != '"')
+ {
+ putc(c, code_file);
+ if (dflag) putc(c, defines_file);
+ }
+ }
+ else
+ {
+ do
+ {
+ putc(c, code_file);
+ if (dflag) putc(c, defines_file);
+ }
+ while (c = *++s);
+ }
+ ++outline;
+ fprintf(code_file, " %d\n", symbol_value[i]);
+ if (dflag) fprintf(defines_file, " %d\n", symbol_value[i]);
+ }
+ }
+
+ ++outline;
+ fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]);
+
+ if (dflag && unionized)
+ {
+ fclose(union_file);
+ union_file = fopen(union_file_name, "r");
+ if (union_file == NULL) open_error(union_file_name);
+ while ((c = getc(union_file)) != EOF)
+ putc(c, defines_file);
+ fprintf(defines_file, " YYSTYPE;\nextern YYSTYPE %slval;\n",
+ symbol_prefix);
+ }
+}
+
+
+output_stored_text()
+{
+ register int c;
+ register FILE *in, *out;
+
+ fclose(text_file);
+ text_file = fopen(text_file_name, "r");
+ if (text_file == NULL)
+ open_error(text_file_name);
+ in = text_file;
+ if ((c = getc(in)) == EOF)
+ return;
+ out = code_file;
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ while ((c = getc(in)) != EOF)
+ {
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ }
+ if (!lflag)
+ fprintf(out, line_format, ++outline + 1, code_file_name);
+}
+
+
+output_debug()
+{
+ register int i, j, k, max;
+ char **symnam, *s;
+
+ ++outline;
+ fprintf(code_file, "#define YYFINAL %d\n", final_state);
+ outline += 3;
+ fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n",
+ tflag);
+ if (rflag)
+ fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n",
+ tflag);
+
+ max = 0;
+ for (i = 2; i < ntokens; ++i)
+ if (symbol_value[i] > max)
+ max = symbol_value[i];
+ ++outline;
+ fprintf(code_file, "#define YYMAXTOKEN %d\n", max);
+
+ symnam = (char **) MALLOC((max+1)*sizeof(char *));
+ if (symnam == 0) no_space();
+
+ /* Note that it is not necessary to initialize the element */
+ /* symnam[max]. */
+ for (i = 0; i < max; ++i)
+ symnam[i] = 0;
+ for (i = ntokens - 1; i >= 2; --i)
+ symnam[symbol_value[i]] = symbol_name[i];
+ symnam[0] = "end-of-file";
+
+ if (!rflag) ++outline;
+ fprintf(output_file, "#if YYDEBUG\nchar *%sname[] = {", symbol_prefix);
+ j = 80;
+ for (i = 0; i <= max; ++i)
+ {
+ if (s = symnam[i])
+ {
+ if (s[0] == '"')
+ {
+ k = 7;
+ while (*++s != '"')
+ {
+ ++k;
+ if (*s == '\\')
+ {
+ k += 2;
+ if (*++s == '\\')
+ ++k;
+ }
+ }
+ j += k;
+ if (j > 80)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = k;
+ }
+ fprintf(output_file, "\"\\\"");
+ s = symnam[i];
+ while (*++s != '"')
+ {
+ if (*s == '\\')
+ {
+ fprintf(output_file, "\\\\");
+ if (*++s == '\\')
+ fprintf(output_file, "\\\\");
+ else
+ putc(*s, output_file);
+ }
+ else
+ putc(*s, output_file);
+ }
+ fprintf(output_file, "\\\"\",");
+ }
+ else if (s[0] == '\'')
+ {
+ if (s[1] == '"')
+ {
+ j += 7;
+ if (j > 80)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 7;
+ }
+ fprintf(output_file, "\"'\\\"'\",");
+ }
+ else
+ {
+ k = 5;
+ while (*++s != '\'')
+ {
+ ++k;
+ if (*s == '\\')
+ {
+ k += 2;
+ if (*++s == '\\')
+ ++k;
+ }
+ }
+ j += k;
+ if (j > 80)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = k;
+ }
+ fprintf(output_file, "\"'");
+ s = symnam[i];
+ while (*++s != '\'')
+ {
+ if (*s == '\\')
+ {
+ fprintf(output_file, "\\\\");
+ if (*++s == '\\')
+ fprintf(output_file, "\\\\");
+ else
+ putc(*s, output_file);
+ }
+ else
+ putc(*s, output_file);
+ }
+ fprintf(output_file, "'\",");
+ }
+ }
+ else
+ {
+ k = strlen(s) + 3;
+ j += k;
+ if (j > 80)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = k;
+ }
+ putc('"', output_file);
+ do { putc(*s, output_file); } while (*++s);
+ fprintf(output_file, "\",");
+ }
+ }
+ else
+ {
+ j += 2;
+ if (j > 80)
+ {
+ if (!rflag) ++outline;
+ putc('\n', output_file);
+ j = 2;
+ }
+ fprintf(output_file, "0,");
+ }
+ }
+ if (!rflag) outline += 2;
+ fprintf(output_file, "\n};\n");
+ FREE(symnam);
+
+ if (!rflag) ++outline;
+ fprintf(output_file, "char *%srule[] = {\n", symbol_prefix);
+ for (i = 2; i < nrules; ++i)
+ {
+ fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]);
+ for (j = rrhs[i]; ritem[j] > 0; ++j)
+ {
+ s = symbol_name[ritem[j]];
+ if (s[0] == '"')
+ {
+ fprintf(output_file, " \\\"");
+ while (*++s != '"')
+ {
+ if (*s == '\\')
+ {
+ if (s[1] == '\\')
+ fprintf(output_file, "\\\\\\\\");
+ else
+ fprintf(output_file, "\\\\%c", s[1]);
+ ++s;
+ }
+ else
+ putc(*s, output_file);
+ }
+ fprintf(output_file, "\\\"");
+ }
+ else if (s[0] == '\'')
+ {
+ if (s[1] == '"')
+ fprintf(output_file, " '\\\"'");
+ else if (s[1] == '\\')
+ {
+ if (s[2] == '\\')
+ fprintf(output_file, " '\\\\\\\\");
+ else
+ fprintf(output_file, " '\\\\%c", s[2]);
+ s += 2;
+ while (*++s != '\'')
+ putc(*s, output_file);
+ putc('\'', output_file);
+ }
+ else
+ fprintf(output_file, " '%c'", s[1]);
+ }
+ else
+ fprintf(output_file, " %s", s);
+ }
+ if (!rflag) ++outline;
+ fprintf(output_file, "\",\n");
+ }
+
+ if (!rflag) outline += 2;
+ fprintf(output_file, "};\n#endif\n");
+}
+
+
+output_stype()
+{
+ if (!unionized && ntags == 0)
+ {
+ outline += 3;
+ fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n");
+ }
+}
+
+
+output_trailing_text()
+{
+ register int c, last;
+ register FILE *in, *out;
+
+ if (line == 0)
+ return;
+
+ in = input_file;
+ out = code_file;
+ c = *cptr;
+ if (c == '\n')
+ {
+ ++lineno;
+ if ((c = getc(in)) == EOF)
+ return;
+ if (!lflag)
+ {
+ ++outline;
+ fprintf(out, line_format, lineno, input_file_name);
+ }
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ last = c;
+ }
+ else
+ {
+ if (!lflag)
+ {
+ ++outline;
+ fprintf(out, line_format, lineno, input_file_name);
+ }
+ do { putc(c, out); } while ((c = *++cptr) != '\n');
+ ++outline;
+ putc('\n', out);
+ last = '\n';
+ }
+
+ while ((c = getc(in)) != EOF)
+ {
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ last = c;
+ }
+
+ if (last != '\n')
+ {
+ ++outline;
+ putc('\n', out);
+ }
+ if (!lflag)
+ fprintf(out, line_format, ++outline + 1, code_file_name);
+}
+
+
+output_semantic_actions()
+{
+ register int c, last;
+ register FILE *out;
+
+ fclose(action_file);
+ action_file = fopen(action_file_name, "r");
+ if (action_file == NULL)
+ open_error(action_file_name);
+
+ if ((c = getc(action_file)) == EOF)
+ return;
+
+ out = code_file;
+ last = c;
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ while ((c = getc(action_file)) != EOF)
+ {
+ if (c == '\n')
+ ++outline;
+ putc(c, out);
+ last = c;
+ }
+
+ if (last != '\n')
+ {
+ ++outline;
+ putc('\n', out);
+ }
+
+ if (!lflag)
+ fprintf(out, line_format, ++outline + 1, code_file_name);
+}
+
+
+free_itemsets()
+{
+ register core *cp, *next;
+
+ FREE(state_table);
+ for (cp = first_state; cp; cp = next)
+ {
+ next = cp->next;
+ FREE(cp);
+ }
+}
+
+
+free_shifts()
+{
+ register shifts *sp, *next;
+
+ FREE(shift_table);
+ for (sp = first_shift; sp; sp = next)
+ {
+ next = sp->next;
+ FREE(sp);
+ }
+}
+
+
+
+free_reductions()
+{
+ register reductions *rp, *next;
+
+ FREE(reduction_table);
+ for (rp = first_reduction; rp; rp = next)
+ {
+ next = rp->next;
+ FREE(rp);
+ }
+}
diff --git a/usr.bin/yacc/reader.c b/usr.bin/yacc/reader.c
new file mode 100644
index 0000000..0a46f29
--- /dev/null
+++ b/usr.bin/yacc/reader.c
@@ -0,0 +1,1810 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)reader.c 5.7 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include "defs.h"
+
+/* The line size must be a positive integer. One hundred was chosen */
+/* because few lines in Yacc input grammars exceed 100 characters. */
+/* Note that if a line exceeds LINESIZE characters, the line buffer */
+/* will be expanded to accomodate it. */
+
+#define LINESIZE 100
+
+char *cache;
+int cinc, cache_size;
+
+int ntags, tagmax;
+char **tag_table;
+
+char saw_eof, unionized;
+char *cptr, *line;
+int linesize;
+
+bucket *goal;
+int prec;
+int gensym;
+char last_was_action;
+
+int maxitems;
+bucket **pitem;
+
+int maxrules;
+bucket **plhs;
+
+int name_pool_size;
+char *name_pool;
+
+char line_format[] = "#line %d \"%s\"\n";
+
+
+cachec(c)
+int c;
+{
+ assert(cinc >= 0);
+ if (cinc >= cache_size)
+ {
+ cache_size += 256;
+ cache = REALLOC(cache, cache_size);
+ if (cache == 0) no_space();
+ }
+ cache[cinc] = c;
+ ++cinc;
+}
+
+
+get_line()
+{
+ register FILE *f = input_file;
+ register int c;
+ register int i;
+
+ if (saw_eof || (c = getc(f)) == EOF)
+ {
+ if (line) { FREE(line); line = 0; }
+ cptr = 0;
+ saw_eof = 1;
+ return;
+ }
+
+ if (line == 0 || linesize != (LINESIZE + 1))
+ {
+ if (line) FREE(line);
+ linesize = LINESIZE + 1;
+ line = MALLOC(linesize);
+ if (line == 0) no_space();
+ }
+
+ i = 0;
+ ++lineno;
+ for (;;)
+ {
+ line[i] = c;
+ if (c == '\n') { cptr = line; return; }
+ if (++i >= linesize)
+ {
+ linesize += LINESIZE;
+ line = REALLOC(line, linesize);
+ if (line == 0) no_space();
+ }
+ c = getc(f);
+ if (c == EOF)
+ {
+ line[i] = '\n';
+ saw_eof = 1;
+ cptr = line;
+ return;
+ }
+ }
+}
+
+
+char *
+dup_line()
+{
+ register char *p, *s, *t;
+
+ if (line == 0) return (0);
+ s = line;
+ while (*s != '\n') ++s;
+ p = MALLOC(s - line + 1);
+ if (p == 0) no_space();
+
+ s = line;
+ t = p;
+ while ((*t++ = *s++) != '\n') continue;
+ return (p);
+}
+
+
+skip_comment()
+{
+ register char *s;
+
+ int st_lineno = lineno;
+ char *st_line = dup_line();
+ char *st_cptr = st_line + (cptr - line);
+
+ s = cptr + 2;
+ for (;;)
+ {
+ if (*s == '*' && s[1] == '/')
+ {
+ cptr = s + 2;
+ FREE(st_line);
+ return;
+ }
+ if (*s == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_comment(st_lineno, st_line, st_cptr);
+ s = cptr;
+ }
+ else
+ ++s;
+ }
+}
+
+
+int
+nextc()
+{
+ register char *s;
+
+ if (line == 0)
+ {
+ get_line();
+ if (line == 0)
+ return (EOF);
+ }
+
+ s = cptr;
+ for (;;)
+ {
+ switch (*s)
+ {
+ case '\n':
+ get_line();
+ if (line == 0) return (EOF);
+ s = cptr;
+ break;
+
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\r':
+ case '\v':
+ case ',':
+ case ';':
+ ++s;
+ break;
+
+ case '\\':
+ cptr = s;
+ return ('%');
+
+ case '/':
+ if (s[1] == '*')
+ {
+ cptr = s;
+ skip_comment();
+ s = cptr;
+ break;
+ }
+ else if (s[1] == '/')
+ {
+ get_line();
+ if (line == 0) return (EOF);
+ s = cptr;
+ break;
+ }
+ /* fall through */
+
+ default:
+ cptr = s;
+ return (*s);
+ }
+ }
+}
+
+
+int
+keyword()
+{
+ register int c;
+ char *t_cptr = cptr;
+
+ c = *++cptr;
+ if (isalpha(c))
+ {
+ cinc = 0;
+ for (;;)
+ {
+ if (isalpha(c))
+ {
+ if (isupper(c)) c = tolower(c);
+ cachec(c);
+ }
+ else if (isdigit(c) || c == '_' || c == '.' || c == '$')
+ cachec(c);
+ else
+ break;
+ c = *++cptr;
+ }
+ cachec(NUL);
+
+ if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0)
+ return (TOKEN);
+ if (strcmp(cache, "type") == 0)
+ return (TYPE);
+ if (strcmp(cache, "left") == 0)
+ return (LEFT);
+ if (strcmp(cache, "right") == 0)
+ return (RIGHT);
+ if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0)
+ return (NONASSOC);
+ if (strcmp(cache, "start") == 0)
+ return (START);
+ if (strcmp(cache, "union") == 0)
+ return (UNION);
+ if (strcmp(cache, "ident") == 0)
+ return (IDENT);
+ }
+ else
+ {
+ ++cptr;
+ if (c == '{')
+ return (TEXT);
+ if (c == '%' || c == '\\')
+ return (MARK);
+ if (c == '<')
+ return (LEFT);
+ if (c == '>')
+ return (RIGHT);
+ if (c == '0')
+ return (TOKEN);
+ if (c == '2')
+ return (NONASSOC);
+ }
+ syntax_error(lineno, line, t_cptr);
+ /*NOTREACHED*/
+}
+
+
+copy_ident()
+{
+ register int c;
+ register FILE *f = output_file;
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c != '"') syntax_error(lineno, line, cptr);
+ ++outline;
+ fprintf(f, "#ident \"");
+ for (;;)
+ {
+ c = *++cptr;
+ if (c == '\n')
+ {
+ fprintf(f, "\"\n");
+ return;
+ }
+ putc(c, f);
+ if (c == '"')
+ {
+ putc('\n', f);
+ ++cptr;
+ return;
+ }
+ }
+}
+
+
+copy_text()
+{
+ register int c;
+ int quote;
+ register FILE *f = text_file;
+ int need_newline = 0;
+ int t_lineno = lineno;
+ char *t_line = dup_line();
+ char *t_cptr = t_line + (cptr - line - 2);
+
+ if (*cptr == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_text(t_lineno, t_line, t_cptr);
+ }
+ if (!lflag) fprintf(f, line_format, lineno, input_file_name);
+
+loop:
+ c = *cptr++;
+ switch (c)
+ {
+ case '\n':
+ next_line:
+ putc('\n', f);
+ need_newline = 0;
+ get_line();
+ if (line) goto loop;
+ unterminated_text(t_lineno, t_line, t_cptr);
+
+ case '\'':
+ case '"':
+ {
+ int s_lineno = lineno;
+ char *s_line = dup_line();
+ char *s_cptr = s_line + (cptr - line - 1);
+
+ quote = c;
+ putc(c, f);
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == quote)
+ {
+ need_newline = 1;
+ FREE(s_line);
+ goto loop;
+ }
+ if (c == '\n')
+ unterminated_string(s_lineno, s_line, s_cptr);
+ if (c == '\\')
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_string(s_lineno, s_line, s_cptr);
+ }
+ }
+ }
+ }
+
+ case '/':
+ putc(c, f);
+ need_newline = 1;
+ c = *cptr;
+ if (c == '/')
+ {
+ putc('*', f);
+ while ((c = *++cptr) != '\n')
+ {
+ if (c == '*' && cptr[1] == '/')
+ fprintf(f, "* ");
+ else
+ putc(c, f);
+ }
+ fprintf(f, "*/");
+ goto next_line;
+ }
+ if (c == '*')
+ {
+ int c_lineno = lineno;
+ char *c_line = dup_line();
+ char *c_cptr = c_line + (cptr - line - 1);
+
+ putc('*', f);
+ ++cptr;
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == '*' && *cptr == '/')
+ {
+ putc('/', f);
+ ++cptr;
+ FREE(c_line);
+ goto loop;
+ }
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_comment(c_lineno, c_line, c_cptr);
+ }
+ }
+ }
+ need_newline = 1;
+ goto loop;
+
+ case '%':
+ case '\\':
+ if (*cptr == '}')
+ {
+ if (need_newline) putc('\n', f);
+ ++cptr;
+ FREE(t_line);
+ return;
+ }
+ /* fall through */
+
+ default:
+ putc(c, f);
+ need_newline = 1;
+ goto loop;
+ }
+}
+
+
+copy_union()
+{
+ register int c;
+ int quote;
+ int depth;
+ int u_lineno = lineno;
+ char *u_line = dup_line();
+ char *u_cptr = u_line + (cptr - line - 6);
+
+ if (unionized) over_unionized(cptr - 6);
+ unionized = 1;
+
+ if (!lflag)
+ fprintf(text_file, line_format, lineno, input_file_name);
+
+ fprintf(text_file, "typedef union");
+ if (dflag) fprintf(union_file, "typedef union");
+
+ depth = 0;
+loop:
+ c = *cptr++;
+ putc(c, text_file);
+ if (dflag) putc(c, union_file);
+ switch (c)
+ {
+ case '\n':
+ next_line:
+ get_line();
+ if (line == 0) unterminated_union(u_lineno, u_line, u_cptr);
+ goto loop;
+
+ case '{':
+ ++depth;
+ goto loop;
+
+ case '}':
+ if (--depth == 0)
+ {
+ fprintf(text_file, " YYSTYPE;\n");
+ FREE(u_line);
+ return;
+ }
+ goto loop;
+
+ case '\'':
+ case '"':
+ {
+ int s_lineno = lineno;
+ char *s_line = dup_line();
+ char *s_cptr = s_line + (cptr - line - 1);
+
+ quote = c;
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, text_file);
+ if (dflag) putc(c, union_file);
+ if (c == quote)
+ {
+ FREE(s_line);
+ goto loop;
+ }
+ if (c == '\n')
+ unterminated_string(s_lineno, s_line, s_cptr);
+ if (c == '\\')
+ {
+ c = *cptr++;
+ putc(c, text_file);
+ if (dflag) putc(c, union_file);
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_string(s_lineno, s_line, s_cptr);
+ }
+ }
+ }
+ }
+
+ case '/':
+ c = *cptr;
+ if (c == '/')
+ {
+ putc('*', text_file);
+ if (dflag) putc('*', union_file);
+ while ((c = *++cptr) != '\n')
+ {
+ if (c == '*' && cptr[1] == '/')
+ {
+ fprintf(text_file, "* ");
+ if (dflag) fprintf(union_file, "* ");
+ }
+ else
+ {
+ putc(c, text_file);
+ if (dflag) putc(c, union_file);
+ }
+ }
+ fprintf(text_file, "*/\n");
+ if (dflag) fprintf(union_file, "*/\n");
+ goto next_line;
+ }
+ if (c == '*')
+ {
+ int c_lineno = lineno;
+ char *c_line = dup_line();
+ char *c_cptr = c_line + (cptr - line - 1);
+
+ putc('*', text_file);
+ if (dflag) putc('*', union_file);
+ ++cptr;
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, text_file);
+ if (dflag) putc(c, union_file);
+ if (c == '*' && *cptr == '/')
+ {
+ putc('/', text_file);
+ if (dflag) putc('/', union_file);
+ ++cptr;
+ FREE(c_line);
+ goto loop;
+ }
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_comment(c_lineno, c_line, c_cptr);
+ }
+ }
+ }
+ goto loop;
+
+ default:
+ goto loop;
+ }
+}
+
+
+int
+hexval(c)
+int c;
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ return (-1);
+}
+
+
+bucket *
+get_literal()
+{
+ register int c, quote;
+ register int i;
+ register int n;
+ register char *s;
+ register bucket *bp;
+ int s_lineno = lineno;
+ char *s_line = dup_line();
+ char *s_cptr = s_line + (cptr - line);
+
+ quote = *cptr++;
+ cinc = 0;
+ for (;;)
+ {
+ c = *cptr++;
+ if (c == quote) break;
+ if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr);
+ if (c == '\\')
+ {
+ char *c_cptr = cptr - 1;
+
+ c = *cptr++;
+ switch (c)
+ {
+ case '\n':
+ get_line();
+ if (line == 0) unterminated_string(s_lineno, s_line, s_cptr);
+ continue;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ n = c - '0';
+ c = *cptr;
+ if (IS_OCTAL(c))
+ {
+ n = (n << 3) + (c - '0');
+ c = *++cptr;
+ if (IS_OCTAL(c))
+ {
+ n = (n << 3) + (c - '0');
+ ++cptr;
+ }
+ }
+ if (n > MAXCHAR) illegal_character(c_cptr);
+ c = n;
+ break;
+
+ case 'x':
+ c = *cptr++;
+ n = hexval(c);
+ if (n < 0 || n >= 16)
+ illegal_character(c_cptr);
+ for (;;)
+ {
+ c = *cptr;
+ i = hexval(c);
+ if (i < 0 || i >= 16) break;
+ ++cptr;
+ n = (n << 4) + i;
+ if (n > MAXCHAR) illegal_character(c_cptr);
+ }
+ c = n;
+ break;
+
+ case 'a': c = 7; break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'v': c = '\v'; break;
+ }
+ }
+ cachec(c);
+ }
+ FREE(s_line);
+
+ n = cinc;
+ s = MALLOC(n);
+ if (s == 0) no_space();
+
+ for (i = 0; i < n; ++i)
+ s[i] = cache[i];
+
+ cinc = 0;
+ if (n == 1)
+ cachec('\'');
+ else
+ cachec('"');
+
+ for (i = 0; i < n; ++i)
+ {
+ c = ((unsigned char *)s)[i];
+ if (c == '\\' || c == cache[0])
+ {
+ cachec('\\');
+ cachec(c);
+ }
+ else if (isprint(c))
+ cachec(c);
+ else
+ {
+ cachec('\\');
+ switch (c)
+ {
+ case 7: cachec('a'); break;
+ case '\b': cachec('b'); break;
+ case '\f': cachec('f'); break;
+ case '\n': cachec('n'); break;
+ case '\r': cachec('r'); break;
+ case '\t': cachec('t'); break;
+ case '\v': cachec('v'); break;
+ default:
+ cachec(((c >> 6) & 7) + '0');
+ cachec(((c >> 3) & 7) + '0');
+ cachec((c & 7) + '0');
+ break;
+ }
+ }
+ }
+
+ if (n == 1)
+ cachec('\'');
+ else
+ cachec('"');
+
+ cachec(NUL);
+ bp = lookup(cache);
+ bp->class = TERM;
+ if (n == 1 && bp->value == UNDEFINED)
+ bp->value = *(unsigned char *)s;
+ FREE(s);
+
+ return (bp);
+}
+
+
+int
+is_reserved(name)
+char *name;
+{
+ char *s;
+
+ if (strcmp(name, ".") == 0 ||
+ strcmp(name, "$accept") == 0 ||
+ strcmp(name, "$end") == 0)
+ return (1);
+
+ if (name[0] == '$' && name[1] == '$' && isdigit(name[2]))
+ {
+ s = name + 3;
+ while (isdigit(*s)) ++s;
+ if (*s == NUL) return (1);
+ }
+
+ return (0);
+}
+
+
+bucket *
+get_name()
+{
+ register int c;
+
+ cinc = 0;
+ for (c = *cptr; IS_IDENT(c); c = *++cptr)
+ cachec(c);
+ cachec(NUL);
+
+ if (is_reserved(cache)) used_reserved(cache);
+
+ return (lookup(cache));
+}
+
+
+int
+get_number()
+{
+ register int c;
+ register int n;
+
+ n = 0;
+ for (c = *cptr; isdigit(c); c = *++cptr)
+ n = 10*n + (c - '0');
+
+ return (n);
+}
+
+
+char *
+get_tag()
+{
+ register int c;
+ register int i;
+ register char *s;
+ int t_lineno = lineno;
+ char *t_line = dup_line();
+ char *t_cptr = t_line + (cptr - line);
+
+ ++cptr;
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (!isalpha(c) && c != '_' && c != '$')
+ illegal_tag(t_lineno, t_line, t_cptr);
+
+ cinc = 0;
+ do { cachec(c); c = *++cptr; } while (IS_IDENT(c));
+ cachec(NUL);
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c != '>')
+ illegal_tag(t_lineno, t_line, t_cptr);
+ ++cptr;
+
+ for (i = 0; i < ntags; ++i)
+ {
+ if (strcmp(cache, tag_table[i]) == 0)
+ return (tag_table[i]);
+ }
+
+ if (ntags >= tagmax)
+ {
+ tagmax += 16;
+ tag_table = (char **)
+ (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *))
+ : MALLOC(tagmax*sizeof(char *)));
+ if (tag_table == 0) no_space();
+ }
+
+ s = MALLOC(cinc);
+ if (s == 0) no_space();
+ strcpy(s, cache);
+ tag_table[ntags] = s;
+ ++ntags;
+ FREE(t_line);
+ return (s);
+}
+
+
+declare_tokens(assoc)
+int assoc;
+{
+ register int c;
+ register bucket *bp;
+ int value;
+ char *tag = 0;
+
+ if (assoc != TOKEN) ++prec;
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c == '<')
+ {
+ tag = get_tag();
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ }
+
+ for (;;)
+ {
+ if (isalpha(c) || c == '_' || c == '.' || c == '$')
+ bp = get_name();
+ else if (c == '\'' || c == '"')
+ bp = get_literal();
+ else
+ return;
+
+ if (bp == goal) tokenized_start(bp->name);
+ bp->class = TERM;
+
+ if (tag)
+ {
+ if (bp->tag && tag != bp->tag)
+ retyped_warning(bp->name);
+ bp->tag = tag;
+ }
+
+ if (assoc != TOKEN)
+ {
+ if (bp->prec && prec != bp->prec)
+ reprec_warning(bp->name);
+ bp->assoc = assoc;
+ bp->prec = prec;
+ }
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ value = UNDEFINED;
+ if (isdigit(c))
+ {
+ value = get_number();
+ if (bp->value != UNDEFINED && value != bp->value)
+ revalued_warning(bp->name);
+ bp->value = value;
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ }
+ }
+}
+
+
+declare_types()
+{
+ register int c;
+ register bucket *bp;
+ char *tag;
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c != '<') syntax_error(lineno, line, cptr);
+ tag = get_tag();
+
+ for (;;)
+ {
+ c = nextc();
+ if (isalpha(c) || c == '_' || c == '.' || c == '$')
+ bp = get_name();
+ else if (c == '\'' || c == '"')
+ bp = get_literal();
+ else
+ return;
+
+ if (bp->tag && tag != bp->tag)
+ retyped_warning(bp->name);
+ bp->tag = tag;
+ }
+}
+
+
+declare_start()
+{
+ register int c;
+ register bucket *bp;
+
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (!isalpha(c) && c != '_' && c != '.' && c != '$')
+ syntax_error(lineno, line, cptr);
+ bp = get_name();
+ if (bp->class == TERM)
+ terminal_start(bp->name);
+ if (goal && goal != bp)
+ restarted_warning();
+ goal = bp;
+}
+
+
+read_declarations()
+{
+ register int c, k;
+
+ cache_size = 256;
+ cache = MALLOC(cache_size);
+ if (cache == 0) no_space();
+
+ for (;;)
+ {
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c != '%') syntax_error(lineno, line, cptr);
+ switch (k = keyword())
+ {
+ case MARK:
+ return;
+
+ case IDENT:
+ copy_ident();
+ break;
+
+ case TEXT:
+ copy_text();
+ break;
+
+ case UNION:
+ copy_union();
+ break;
+
+ case TOKEN:
+ case LEFT:
+ case RIGHT:
+ case NONASSOC:
+ declare_tokens(k);
+ break;
+
+ case TYPE:
+ declare_types();
+ break;
+
+ case START:
+ declare_start();
+ break;
+ }
+ }
+}
+
+
+initialize_grammar()
+{
+ nitems = 4;
+ maxitems = 300;
+ pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *));
+ if (pitem == 0) no_space();
+ pitem[0] = 0;
+ pitem[1] = 0;
+ pitem[2] = 0;
+ pitem[3] = 0;
+
+ nrules = 3;
+ maxrules = 100;
+ plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *));
+ if (plhs == 0) no_space();
+ plhs[0] = 0;
+ plhs[1] = 0;
+ plhs[2] = 0;
+ rprec = (short *) MALLOC(maxrules*sizeof(short));
+ if (rprec == 0) no_space();
+ rprec[0] = 0;
+ rprec[1] = 0;
+ rprec[2] = 0;
+ rassoc = (char *) MALLOC(maxrules*sizeof(char));
+ if (rassoc == 0) no_space();
+ rassoc[0] = TOKEN;
+ rassoc[1] = TOKEN;
+ rassoc[2] = TOKEN;
+}
+
+
+expand_items()
+{
+ maxitems += 300;
+ pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *));
+ if (pitem == 0) no_space();
+}
+
+
+expand_rules()
+{
+ maxrules += 100;
+ plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *));
+ if (plhs == 0) no_space();
+ rprec = (short *) REALLOC(rprec, maxrules*sizeof(short));
+ if (rprec == 0) no_space();
+ rassoc = (char *) REALLOC(rassoc, maxrules*sizeof(char));
+ if (rassoc == 0) no_space();
+}
+
+
+advance_to_start()
+{
+ register int c;
+ register bucket *bp;
+ char *s_cptr;
+ int s_lineno;
+
+ for (;;)
+ {
+ c = nextc();
+ if (c != '%') break;
+ s_cptr = cptr;
+ switch (keyword())
+ {
+ case MARK:
+ no_grammar();
+
+ case TEXT:
+ copy_text();
+ break;
+
+ case START:
+ declare_start();
+ break;
+
+ default:
+ syntax_error(lineno, line, s_cptr);
+ }
+ }
+
+ c = nextc();
+ if (!isalpha(c) && c != '_' && c != '.' && c != '_')
+ syntax_error(lineno, line, cptr);
+ bp = get_name();
+ if (goal == 0)
+ {
+ if (bp->class == TERM)
+ terminal_start(bp->name);
+ goal = bp;
+ }
+
+ s_lineno = lineno;
+ c = nextc();
+ if (c == EOF) unexpected_EOF();
+ if (c != ':') syntax_error(lineno, line, cptr);
+ start_rule(bp, s_lineno);
+ ++cptr;
+}
+
+
+start_rule(bp, s_lineno)
+register bucket *bp;
+int s_lineno;
+{
+ if (bp->class == TERM)
+ terminal_lhs(s_lineno);
+ bp->class = NONTERM;
+ if (nrules >= maxrules)
+ expand_rules();
+ plhs[nrules] = bp;
+ rprec[nrules] = UNDEFINED;
+ rassoc[nrules] = TOKEN;
+}
+
+
+end_rule()
+{
+ register int i;
+
+ if (!last_was_action && plhs[nrules]->tag)
+ {
+ for (i = nitems - 1; pitem[i]; --i) continue;
+ if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag)
+ default_action_warning();
+ }
+
+ last_was_action = 0;
+ if (nitems >= maxitems) expand_items();
+ pitem[nitems] = 0;
+ ++nitems;
+ ++nrules;
+}
+
+
+insert_empty_rule()
+{
+ register bucket *bp, **bpp;
+
+ assert(cache);
+ sprintf(cache, "$$%d", ++gensym);
+ bp = make_bucket(cache);
+ last_symbol->next = bp;
+ last_symbol = bp;
+ bp->tag = plhs[nrules]->tag;
+ bp->class = NONTERM;
+
+ if ((nitems += 2) > maxitems)
+ expand_items();
+ bpp = pitem + nitems - 1;
+ *bpp-- = bp;
+ while (bpp[0] = bpp[-1]) --bpp;
+
+ if (++nrules >= maxrules)
+ expand_rules();
+ plhs[nrules] = plhs[nrules-1];
+ plhs[nrules-1] = bp;
+ rprec[nrules] = rprec[nrules-1];
+ rprec[nrules-1] = 0;
+ rassoc[nrules] = rassoc[nrules-1];
+ rassoc[nrules-1] = TOKEN;
+}
+
+
+add_symbol()
+{
+ register int c;
+ register bucket *bp;
+ int s_lineno = lineno;
+
+ c = *cptr;
+ if (c == '\'' || c == '"')
+ bp = get_literal();
+ else
+ bp = get_name();
+
+ c = nextc();
+ if (c == ':')
+ {
+ end_rule();
+ start_rule(bp, s_lineno);
+ ++cptr;
+ return;
+ }
+
+ if (last_was_action)
+ insert_empty_rule();
+ last_was_action = 0;
+
+ if (++nitems > maxitems)
+ expand_items();
+ pitem[nitems-1] = bp;
+}
+
+
+copy_action()
+{
+ register int c;
+ register int i, n;
+ int depth;
+ int quote;
+ char *tag;
+ register FILE *f = action_file;
+ int a_lineno = lineno;
+ char *a_line = dup_line();
+ char *a_cptr = a_line + (cptr - line);
+
+ if (last_was_action)
+ insert_empty_rule();
+ last_was_action = 1;
+
+ fprintf(f, "case %d:\n", nrules - 2);
+ if (!lflag)
+ fprintf(f, line_format, lineno, input_file_name);
+ if (*cptr == '=') ++cptr;
+
+ n = 0;
+ for (i = nitems - 1; pitem[i]; --i) ++n;
+
+ depth = 0;
+loop:
+ c = *cptr;
+ if (c == '$')
+ {
+ if (cptr[1] == '<')
+ {
+ int d_lineno = lineno;
+ char *d_line = dup_line();
+ char *d_cptr = d_line + (cptr - line);
+
+ ++cptr;
+ tag = get_tag();
+ c = *cptr;
+ if (c == '$')
+ {
+ fprintf(f, "yyval.%s", tag);
+ ++cptr;
+ FREE(d_line);
+ goto loop;
+ }
+ else if (isdigit(c))
+ {
+ i = get_number();
+ if (i > n) dollar_warning(d_lineno, i);
+ fprintf(f, "yyvsp[%d].%s", i - n, tag);
+ FREE(d_line);
+ goto loop;
+ }
+ else if (c == '-' && isdigit(cptr[1]))
+ {
+ ++cptr;
+ i = -get_number() - n;
+ fprintf(f, "yyvsp[%d].%s", i, tag);
+ FREE(d_line);
+ goto loop;
+ }
+ else
+ dollar_error(d_lineno, d_line, d_cptr);
+ }
+ else if (cptr[1] == '$')
+ {
+ if (ntags)
+ {
+ tag = plhs[nrules]->tag;
+ if (tag == 0) untyped_lhs();
+ fprintf(f, "yyval.%s", tag);
+ }
+ else
+ fprintf(f, "yyval");
+ cptr += 2;
+ goto loop;
+ }
+ else if (isdigit(cptr[1]))
+ {
+ ++cptr;
+ i = get_number();
+ if (ntags)
+ {
+ if (i <= 0 || i > n)
+ unknown_rhs(i);
+ tag = pitem[nitems + i - n - 1]->tag;
+ if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]->name);
+ fprintf(f, "yyvsp[%d].%s", i - n, tag);
+ }
+ else
+ {
+ if (i > n)
+ dollar_warning(lineno, i);
+ fprintf(f, "yyvsp[%d]", i - n);
+ }
+ goto loop;
+ }
+ else if (cptr[1] == '-')
+ {
+ cptr += 2;
+ i = get_number();
+ if (ntags)
+ unknown_rhs(-i);
+ fprintf(f, "yyvsp[%d]", -i - n);
+ goto loop;
+ }
+ }
+ if (isalpha(c) || c == '_' || c == '$')
+ {
+ do
+ {
+ putc(c, f);
+ c = *++cptr;
+ } while (isalnum(c) || c == '_' || c == '$');
+ goto loop;
+ }
+ putc(c, f);
+ ++cptr;
+ switch (c)
+ {
+ case '\n':
+ next_line:
+ get_line();
+ if (line) goto loop;
+ unterminated_action(a_lineno, a_line, a_cptr);
+
+ case ';':
+ if (depth > 0) goto loop;
+ fprintf(f, "\nbreak;\n");
+ return;
+
+ case '{':
+ ++depth;
+ goto loop;
+
+ case '}':
+ if (--depth > 0) goto loop;
+ fprintf(f, "\nbreak;\n");
+ return;
+
+ case '\'':
+ case '"':
+ {
+ int s_lineno = lineno;
+ char *s_line = dup_line();
+ char *s_cptr = s_line + (cptr - line - 1);
+
+ quote = c;
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == quote)
+ {
+ FREE(s_line);
+ goto loop;
+ }
+ if (c == '\n')
+ unterminated_string(s_lineno, s_line, s_cptr);
+ if (c == '\\')
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_string(s_lineno, s_line, s_cptr);
+ }
+ }
+ }
+ }
+
+ case '/':
+ c = *cptr;
+ if (c == '/')
+ {
+ putc('*', f);
+ while ((c = *++cptr) != '\n')
+ {
+ if (c == '*' && cptr[1] == '/')
+ fprintf(f, "* ");
+ else
+ putc(c, f);
+ }
+ fprintf(f, "*/\n");
+ goto next_line;
+ }
+ if (c == '*')
+ {
+ int c_lineno = lineno;
+ char *c_line = dup_line();
+ char *c_cptr = c_line + (cptr - line - 1);
+
+ putc('*', f);
+ ++cptr;
+ for (;;)
+ {
+ c = *cptr++;
+ putc(c, f);
+ if (c == '*' && *cptr == '/')
+ {
+ putc('/', f);
+ ++cptr;
+ FREE(c_line);
+ goto loop;
+ }
+ if (c == '\n')
+ {
+ get_line();
+ if (line == 0)
+ unterminated_comment(c_lineno, c_line, c_cptr);
+ }
+ }
+ }
+ goto loop;
+
+ default:
+ goto loop;
+ }
+}
+
+
+int
+mark_symbol()
+{
+ register int c;
+ register bucket *bp;
+
+ c = cptr[1];
+ if (c == '%' || c == '\\')
+ {
+ cptr += 2;
+ return (1);
+ }
+
+ if (c == '=')
+ cptr += 2;
+ else if ((c == 'p' || c == 'P') &&
+ ((c = cptr[2]) == 'r' || c == 'R') &&
+ ((c = cptr[3]) == 'e' || c == 'E') &&
+ ((c = cptr[4]) == 'c' || c == 'C') &&
+ ((c = cptr[5], !IS_IDENT(c))))
+ cptr += 5;
+ else
+ syntax_error(lineno, line, cptr);
+
+ c = nextc();
+ if (isalpha(c) || c == '_' || c == '.' || c == '$')
+ bp = get_name();
+ else if (c == '\'' || c == '"')
+ bp = get_literal();
+ else
+ {
+ syntax_error(lineno, line, cptr);
+ /*NOTREACHED*/
+ }
+
+ if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules])
+ prec_redeclared();
+
+ rprec[nrules] = bp->prec;
+ rassoc[nrules] = bp->assoc;
+ return (0);
+}
+
+
+read_grammar()
+{
+ register int c;
+
+ initialize_grammar();
+ advance_to_start();
+
+ for (;;)
+ {
+ c = nextc();
+ if (c == EOF) break;
+ if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\'' ||
+ c == '"')
+ add_symbol();
+ else if (c == '{' || c == '=')
+ copy_action();
+ else if (c == '|')
+ {
+ end_rule();
+ start_rule(plhs[nrules-1], 0);
+ ++cptr;
+ }
+ else if (c == '%')
+ {
+ if (mark_symbol()) break;
+ }
+ else
+ syntax_error(lineno, line, cptr);
+ }
+ end_rule();
+}
+
+
+free_tags()
+{
+ register int i;
+
+ if (tag_table == 0) return;
+
+ for (i = 0; i < ntags; ++i)
+ {
+ assert(tag_table[i]);
+ FREE(tag_table[i]);
+ }
+ FREE(tag_table);
+}
+
+
+pack_names()
+{
+ register bucket *bp;
+ register char *p, *s, *t;
+
+ name_pool_size = 13; /* 13 == sizeof("$end") + sizeof("$accept") */
+ for (bp = first_symbol; bp; bp = bp->next)
+ name_pool_size += strlen(bp->name) + 1;
+ name_pool = MALLOC(name_pool_size);
+ if (name_pool == 0) no_space();
+
+ strcpy(name_pool, "$accept");
+ strcpy(name_pool+8, "$end");
+ t = name_pool + 13;
+ for (bp = first_symbol; bp; bp = bp->next)
+ {
+ p = t;
+ s = bp->name;
+ while (*t++ = *s++) continue;
+ FREE(bp->name);
+ bp->name = p;
+ }
+}
+
+
+check_symbols()
+{
+ register bucket *bp;
+
+ if (goal->class == UNKNOWN)
+ undefined_goal(goal->name);
+
+ for (bp = first_symbol; bp; bp = bp->next)
+ {
+ if (bp->class == UNKNOWN)
+ {
+ undefined_symbol_warning(bp->name);
+ bp->class = TERM;
+ }
+ }
+}
+
+
+pack_symbols()
+{
+ register bucket *bp;
+ register bucket **v;
+ register int i, j, k, n;
+
+ nsyms = 2;
+ ntokens = 1;
+ for (bp = first_symbol; bp; bp = bp->next)
+ {
+ ++nsyms;
+ if (bp->class == TERM) ++ntokens;
+ }
+ start_symbol = ntokens;
+ nvars = nsyms - ntokens;
+
+ symbol_name = (char **) MALLOC(nsyms*sizeof(char *));
+ if (symbol_name == 0) no_space();
+ symbol_value = (short *) MALLOC(nsyms*sizeof(short));
+ if (symbol_value == 0) no_space();
+ symbol_prec = (short *) MALLOC(nsyms*sizeof(short));
+ if (symbol_prec == 0) no_space();
+ symbol_assoc = MALLOC(nsyms);
+ if (symbol_assoc == 0) no_space();
+
+ v = (bucket **) MALLOC(nsyms*sizeof(bucket *));
+ if (v == 0) no_space();
+
+ v[0] = 0;
+ v[start_symbol] = 0;
+
+ i = 1;
+ j = start_symbol + 1;
+ for (bp = first_symbol; bp; bp = bp->next)
+ {
+ if (bp->class == TERM)
+ v[i++] = bp;
+ else
+ v[j++] = bp;
+ }
+ assert(i == ntokens && j == nsyms);
+
+ for (i = 1; i < ntokens; ++i)
+ v[i]->index = i;
+
+ goal->index = start_symbol + 1;
+ k = start_symbol + 2;
+ while (++i < nsyms)
+ if (v[i] != goal)
+ {
+ v[i]->index = k;
+ ++k;
+ }
+
+ goal->value = 0;
+ k = 1;
+ for (i = start_symbol + 1; i < nsyms; ++i)
+ {
+ if (v[i] != goal)
+ {
+ v[i]->value = k;
+ ++k;
+ }
+ }
+
+ k = 0;
+ for (i = 1; i < ntokens; ++i)
+ {
+ n = v[i]->value;
+ if (n > 256)
+ {
+ for (j = k++; j > 0 && symbol_value[j-1] > n; --j)
+ symbol_value[j] = symbol_value[j-1];
+ symbol_value[j] = n;
+ }
+ }
+
+ if (v[1]->value == UNDEFINED)
+ v[1]->value = 256;
+
+ j = 0;
+ n = 257;
+ for (i = 2; i < ntokens; ++i)
+ {
+ if (v[i]->value == UNDEFINED)
+ {
+ while (j < k && n == symbol_value[j])
+ {
+ while (++j < k && n == symbol_value[j]) continue;
+ ++n;
+ }
+ v[i]->value = n;
+ ++n;
+ }
+ }
+
+ symbol_name[0] = name_pool + 8;
+ symbol_value[0] = 0;
+ symbol_prec[0] = 0;
+ symbol_assoc[0] = TOKEN;
+ for (i = 1; i < ntokens; ++i)
+ {
+ symbol_name[i] = v[i]->name;
+ symbol_value[i] = v[i]->value;
+ symbol_prec[i] = v[i]->prec;
+ symbol_assoc[i] = v[i]->assoc;
+ }
+ symbol_name[start_symbol] = name_pool;
+ symbol_value[start_symbol] = -1;
+ symbol_prec[start_symbol] = 0;
+ symbol_assoc[start_symbol] = TOKEN;
+ for (++i; i < nsyms; ++i)
+ {
+ k = v[i]->index;
+ symbol_name[k] = v[i]->name;
+ symbol_value[k] = v[i]->value;
+ symbol_prec[k] = v[i]->prec;
+ symbol_assoc[k] = v[i]->assoc;
+ }
+
+ FREE(v);
+}
+
+
+pack_grammar()
+{
+ register int i, j;
+ int assoc, prec;
+
+ ritem = (short *) MALLOC(nitems*sizeof(short));
+ if (ritem == 0) no_space();
+ rlhs = (short *) MALLOC(nrules*sizeof(short));
+ if (rlhs == 0) no_space();
+ rrhs = (short *) MALLOC((nrules+1)*sizeof(short));
+ if (rrhs == 0) no_space();
+ rprec = (short *) REALLOC(rprec, nrules*sizeof(short));
+ if (rprec == 0) no_space();
+ rassoc = REALLOC(rassoc, nrules);
+ if (rassoc == 0) no_space();
+
+ ritem[0] = -1;
+ ritem[1] = goal->index;
+ ritem[2] = 0;
+ ritem[3] = -2;
+ rlhs[0] = 0;
+ rlhs[1] = 0;
+ rlhs[2] = start_symbol;
+ rrhs[0] = 0;
+ rrhs[1] = 0;
+ rrhs[2] = 1;
+
+ j = 4;
+ for (i = 3; i < nrules; ++i)
+ {
+ rlhs[i] = plhs[i]->index;
+ rrhs[i] = j;
+ assoc = TOKEN;
+ prec = 0;
+ while (pitem[j])
+ {
+ ritem[j] = pitem[j]->index;
+ if (pitem[j]->class == TERM)
+ {
+ prec = pitem[j]->prec;
+ assoc = pitem[j]->assoc;
+ }
+ ++j;
+ }
+ ritem[j] = -i;
+ ++j;
+ if (rprec[i] == UNDEFINED)
+ {
+ rprec[i] = prec;
+ rassoc[i] = assoc;
+ }
+ }
+ rrhs[i] = j;
+
+ FREE(plhs);
+ FREE(pitem);
+}
+
+
+print_grammar()
+{
+ register int i, j, k;
+ int spacing;
+ register FILE *f = verbose_file;
+
+ if (!vflag) return;
+
+ k = 1;
+ for (i = 2; i < nrules; ++i)
+ {
+ if (rlhs[i] != rlhs[i-1])
+ {
+ if (i != 2) fprintf(f, "\n");
+ fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]);
+ spacing = strlen(symbol_name[rlhs[i]]) + 1;
+ }
+ else
+ {
+ fprintf(f, "%4d ", i - 2);
+ j = spacing;
+ while (--j >= 0) putc(' ', f);
+ putc('|', f);
+ }
+
+ while (ritem[k] >= 0)
+ {
+ fprintf(f, " %s", symbol_name[ritem[k]]);
+ ++k;
+ }
+ ++k;
+ putc('\n', f);
+ }
+}
+
+
+reader()
+{
+ write_section(banner);
+ create_symbol_table();
+ read_declarations();
+ read_grammar();
+ free_symbol_table();
+ free_tags();
+ pack_names();
+ check_symbols();
+ pack_symbols();
+ pack_grammar();
+ free_symbols();
+ print_grammar();
+}
diff --git a/usr.bin/yacc/skeleton.c b/usr.bin/yacc/skeleton.c
new file mode 100644
index 0000000..1e51b2e
--- /dev/null
+++ b/usr.bin/yacc/skeleton.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)skeleton.c 5.7 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+/* The definition of yysccsid in the banner should be replaced with */
+/* a #pragma ident directive if the target C compiler supports */
+/* #pragma ident directives. */
+/* */
+/* If the skeleton is changed, the banner should be changed so that */
+/* the altered version can be easily distinguished from the original. */
+/* */
+/* The #defines included with the banner are there because they are */
+/* useful in subsequent code. The macros #defined in the header or */
+/* the body either are not useful outside of semantic actions or */
+/* are conditional. */
+
+char *banner[] =
+{
+ "#ifndef lint",
+ "static char yysccsid[] = \"@(#)yaccpar 1.9 (Berkeley) 02/21/93\";",
+ "#endif",
+ "#define YYBYACC 1",
+ "#define YYMAJOR 1",
+ "#define YYMINOR 9",
+ "#define yyclearin (yychar=(-1))",
+ "#define yyerrok (yyerrflag=0)",
+ "#define YYRECOVERING (yyerrflag!=0)",
+ 0
+};
+
+
+char *tables[] =
+{
+ "extern short yylhs[];",
+ "extern short yylen[];",
+ "extern short yydefred[];",
+ "extern short yydgoto[];",
+ "extern short yysindex[];",
+ "extern short yyrindex[];",
+ "extern short yygindex[];",
+ "extern short yytable[];",
+ "extern short yycheck[];",
+ "#if YYDEBUG",
+ "extern char *yyname[];",
+ "extern char *yyrule[];",
+ "#endif",
+ 0
+};
+
+
+char *header[] =
+{
+ "#ifdef YYSTACKSIZE",
+ "#undef YYMAXDEPTH",
+ "#define YYMAXDEPTH YYSTACKSIZE",
+ "#else",
+ "#ifdef YYMAXDEPTH",
+ "#define YYSTACKSIZE YYMAXDEPTH",
+ "#else",
+ "#define YYSTACKSIZE 500",
+ "#define YYMAXDEPTH 500",
+ "#endif",
+ "#endif",
+ "int yydebug;",
+ "int yynerrs;",
+ "int yyerrflag;",
+ "int yychar;",
+ "short *yyssp;",
+ "YYSTYPE *yyvsp;",
+ "YYSTYPE yyval;",
+ "YYSTYPE yylval;",
+ "short yyss[YYSTACKSIZE];",
+ "YYSTYPE yyvs[YYSTACKSIZE];",
+ "#define yystacksize YYSTACKSIZE",
+ 0
+};
+
+
+char *body[] =
+{
+ "#define YYABORT goto yyabort",
+ "#define YYREJECT goto yyabort",
+ "#define YYACCEPT goto yyaccept",
+ "#define YYERROR goto yyerrlab",
+ "int",
+ "yyparse()",
+ "{",
+ " register int yym, yyn, yystate;",
+ "#if YYDEBUG",
+ " register char *yys;",
+ " extern char *getenv();",
+ "",
+ " if (yys = getenv(\"YYDEBUG\"))",
+ " {",
+ " yyn = *yys;",
+ " if (yyn >= '0' && yyn <= '9')",
+ " yydebug = yyn - '0';",
+ " }",
+ "#endif",
+ "",
+ " yynerrs = 0;",
+ " yyerrflag = 0;",
+ " yychar = (-1);",
+ "",
+ " yyssp = yyss;",
+ " yyvsp = yyvs;",
+ " *yyssp = yystate = 0;",
+ "",
+ "yyloop:",
+ " if (yyn = yydefred[yystate]) goto yyreduce;",
+ " if (yychar < 0)",
+ " {",
+ " if ((yychar = yylex()) < 0) yychar = 0;",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " {",
+ " yys = 0;",
+ " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];",
+ " if (!yys) yys = \"illegal-symbol\";",
+ " printf(\"%sdebug: state %d, reading %d (%s)\\n\",",
+ " YYPREFIX, yystate, yychar, yys);",
+ " }",
+ "#endif",
+ " }",
+ " if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&",
+ " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)",
+ " {",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: state %d, shifting to state %d\\n\",",
+ " YYPREFIX, yystate, yytable[yyn]);",
+ "#endif",
+ " if (yyssp >= yyss + yystacksize - 1)",
+ " {",
+ " goto yyoverflow;",
+ " }",
+ " *++yyssp = yystate = yytable[yyn];",
+ " *++yyvsp = yylval;",
+ " yychar = (-1);",
+ " if (yyerrflag > 0) --yyerrflag;",
+ " goto yyloop;",
+ " }",
+ " if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&",
+ " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)",
+ " {",
+ " yyn = yytable[yyn];",
+ " goto yyreduce;",
+ " }",
+ " if (yyerrflag) goto yyinrecovery;",
+ "#ifdef lint",
+ " goto yynewerror;",
+ "#endif",
+ "yynewerror:",
+ " yyerror(\"syntax error\");",
+ "#ifdef lint",
+ " goto yyerrlab;",
+ "#endif",
+ "yyerrlab:",
+ " ++yynerrs;",
+ "yyinrecovery:",
+ " if (yyerrflag < 3)",
+ " {",
+ " yyerrflag = 3;",
+ " for (;;)",
+ " {",
+ " if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&",
+ " yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)",
+ " {",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: state %d, error recovery shifting\\",
+ " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);",
+ "#endif",
+ " if (yyssp >= yyss + yystacksize - 1)",
+ " {",
+ " goto yyoverflow;",
+ " }",
+ " *++yyssp = yystate = yytable[yyn];",
+ " *++yyvsp = yylval;",
+ " goto yyloop;",
+ " }",
+ " else",
+ " {",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: error recovery discarding state %d\
+\\n\",",
+ " YYPREFIX, *yyssp);",
+ "#endif",
+ " if (yyssp <= yyss) goto yyabort;",
+ " --yyssp;",
+ " --yyvsp;",
+ " }",
+ " }",
+ " }",
+ " else",
+ " {",
+ " if (yychar == 0) goto yyabort;",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " {",
+ " yys = 0;",
+ " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];",
+ " if (!yys) yys = \"illegal-symbol\";",
+ " printf(\"%sdebug: state %d, error recovery discards token %d\
+ (%s)\\n\",",
+ " YYPREFIX, yystate, yychar, yys);",
+ " }",
+ "#endif",
+ " yychar = (-1);",
+ " goto yyloop;",
+ " }",
+ "yyreduce:",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",",
+ " YYPREFIX, yystate, yyn, yyrule[yyn]);",
+ "#endif",
+ " yym = yylen[yyn];",
+ " yyval = yyvsp[1-yym];",
+ " switch (yyn)",
+ " {",
+ 0
+};
+
+
+char *trailer[] =
+{
+ " }",
+ " yyssp -= yym;",
+ " yystate = *yyssp;",
+ " yyvsp -= yym;",
+ " yym = yylhs[yyn];",
+ " if (yystate == 0 && yym == 0)",
+ " {",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: after reduction, shifting from state 0 to\\",
+ " state %d\\n\", YYPREFIX, YYFINAL);",
+ "#endif",
+ " yystate = YYFINAL;",
+ " *++yyssp = YYFINAL;",
+ " *++yyvsp = yyval;",
+ " if (yychar < 0)",
+ " {",
+ " if ((yychar = yylex()) < 0) yychar = 0;",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " {",
+ " yys = 0;",
+ " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];",
+ " if (!yys) yys = \"illegal-symbol\";",
+ " printf(\"%sdebug: state %d, reading %d (%s)\\n\",",
+ " YYPREFIX, YYFINAL, yychar, yys);",
+ " }",
+ "#endif",
+ " }",
+ " if (yychar == 0) goto yyaccept;",
+ " goto yyloop;",
+ " }",
+ " if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&",
+ " yyn <= YYTABLESIZE && yycheck[yyn] == yystate)",
+ " yystate = yytable[yyn];",
+ " else",
+ " yystate = yydgoto[yym];",
+ "#if YYDEBUG",
+ " if (yydebug)",
+ " printf(\"%sdebug: after reduction, shifting from state %d \\",
+ "to state %d\\n\", YYPREFIX, *yyssp, yystate);",
+ "#endif",
+ " if (yyssp >= yyss + yystacksize - 1)",
+ " {",
+ " goto yyoverflow;",
+ " }",
+ " *++yyssp = yystate;",
+ " *++yyvsp = yyval;",
+ " goto yyloop;",
+ "yyoverflow:",
+ " yyerror(\"yacc stack overflow\");",
+ "yyabort:",
+ " return (1);",
+ "yyaccept:",
+ " return (0);",
+ "}",
+ 0
+};
+
+
+write_section(section)
+char *section[];
+{
+ register int c;
+ register int i;
+ register char *s;
+ register FILE *f;
+
+ f = code_file;
+ for (i = 0; s = section[i]; ++i)
+ {
+ ++outline;
+ while (c = *s)
+ {
+ putc(c, f);
+ ++s;
+ }
+ putc('\n', f);
+ }
+}
diff --git a/usr.bin/yacc/symtab.c b/usr.bin/yacc/symtab.c
new file mode 100644
index 0000000..0c5f55c
--- /dev/null
+++ b/usr.bin/yacc/symtab.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)symtab.c 5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include "defs.h"
+
+/* TABLE_SIZE is the number of entries in the symbol table. */
+/* TABLE_SIZE must be a power of two. */
+
+#define TABLE_SIZE 1024
+
+
+bucket **symbol_table;
+bucket *first_symbol;
+bucket *last_symbol;
+
+
+int
+hash(name)
+char *name;
+{
+ register char *s;
+ register int c, k;
+
+ assert(name && *name);
+ s = name;
+ k = *s;
+ while (c = *++s)
+ k = (31*k + c) & (TABLE_SIZE - 1);
+
+ return (k);
+}
+
+
+bucket *
+make_bucket(name)
+char *name;
+{
+ register bucket *bp;
+
+ assert(name);
+ bp = (bucket *) MALLOC(sizeof(bucket));
+ if (bp == 0) no_space();
+ bp->link = 0;
+ bp->next = 0;
+ bp->name = MALLOC(strlen(name) + 1);
+ if (bp->name == 0) no_space();
+ bp->tag = 0;
+ bp->value = UNDEFINED;
+ bp->index = 0;
+ bp->prec = 0;
+ bp-> class = UNKNOWN;
+ bp->assoc = TOKEN;
+
+ if (bp->name == 0) no_space();
+ strcpy(bp->name, name);
+
+ return (bp);
+}
+
+
+bucket *
+lookup(name)
+char *name;
+{
+ register bucket *bp, **bpp;
+
+ bpp = symbol_table + hash(name);
+ bp = *bpp;
+
+ while (bp)
+ {
+ if (strcmp(name, bp->name) == 0) return (bp);
+ bpp = &bp->link;
+ bp = *bpp;
+ }
+
+ *bpp = bp = make_bucket(name);
+ last_symbol->next = bp;
+ last_symbol = bp;
+
+ return (bp);
+}
+
+
+create_symbol_table()
+{
+ register int i;
+ register bucket *bp;
+
+ symbol_table = (bucket **) MALLOC(TABLE_SIZE*sizeof(bucket *));
+ if (symbol_table == 0) no_space();
+ for (i = 0; i < TABLE_SIZE; i++)
+ symbol_table[i] = 0;
+
+ bp = make_bucket("error");
+ bp->index = 1;
+ bp->class = TERM;
+
+ first_symbol = bp;
+ last_symbol = bp;
+ symbol_table[hash("error")] = bp;
+}
+
+
+free_symbol_table()
+{
+ FREE(symbol_table);
+ symbol_table = 0;
+}
+
+
+free_symbols()
+{
+ register bucket *p, *q;
+
+ for (p = first_symbol; p; p = q)
+ {
+ q = p->next;
+ FREE(p);
+ }
+}
diff --git a/usr.bin/yacc/test/error.output b/usr.bin/yacc/test/error.output
new file mode 100644
index 0000000..0c4db62
--- /dev/null
+++ b/usr.bin/yacc/test/error.output
@@ -0,0 +1,27 @@
+ 0 $accept : S $end
+
+ 1 S : error
+
+state 0
+ $accept : . S $end (0)
+
+ error shift 1
+ . error
+
+ S goto 2
+
+
+state 1
+ S : error . (1)
+
+ . reduce 1
+
+
+state 2
+ $accept : S . $end (0)
+
+ $end accept
+
+
+2 terminals, 2 nonterminals
+2 grammar rules, 3 states
diff --git a/usr.bin/yacc/test/error.tab.c b/usr.bin/yacc/test/error.tab.c
new file mode 100644
index 0000000..ffc6e37
--- /dev/null
+++ b/usr.bin/yacc/test/error.tab.c
@@ -0,0 +1,275 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0,
+};
+short yylen[] = { 2,
+ 1,
+};
+short yydefred[] = { 0,
+ 1, 0,
+};
+short yydgoto[] = { 2,
+};
+short yysindex[] = { -256,
+ 0, 0,
+};
+short yyrindex[] = { 0,
+ 0, 0,
+};
+short yygindex[] = { 0,
+};
+#define YYTABLESIZE 0
+short yytable[] = { 1,
+};
+short yycheck[] = { 256,
+};
+#define YYFINAL 2
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 0
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",
+};
+char *yyrule[] = {
+"$accept : S",
+"S : error",
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 4 "error.y"
+main(){printf("yyparse() = %d\n",yyparse());}
+yylex(){return-1;}
+yyerror(s)char*s;{printf("%s\n",s);}
+#line 80 "error.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/usr.bin/yacc/test/error.tab.h b/usr.bin/yacc/test/error.tab.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.bin/yacc/test/error.tab.h
diff --git a/usr.bin/yacc/test/error.y b/usr.bin/yacc/test/error.y
new file mode 100644
index 0000000..41148ea
--- /dev/null
+++ b/usr.bin/yacc/test/error.y
@@ -0,0 +1,6 @@
+%%
+S: error
+%%
+main(){printf("yyparse() = %d\n",yyparse());}
+yylex(){return-1;}
+yyerror(s)char*s;{printf("%s\n",s);}
diff --git a/usr.bin/yacc/test/ftp.output b/usr.bin/yacc/test/ftp.output
new file mode 100644
index 0000000..f1ab4b2
--- /dev/null
+++ b/usr.bin/yacc/test/ftp.output
@@ -0,0 +1,1625 @@
+ 0 $accept : cmd_list $end
+
+ 1 cmd_list :
+ 2 | cmd_list cmd
+ 3 | cmd_list rcmd
+
+ 4 cmd : USER SP username CRLF
+ 5 | PASS SP password CRLF
+ 6 | PORT SP host_port CRLF
+ 7 | PASV CRLF
+ 8 | TYPE SP type_code CRLF
+ 9 | STRU SP struct_code CRLF
+ 10 | MODE SP mode_code CRLF
+ 11 | ALLO SP NUMBER CRLF
+ 12 | ALLO SP NUMBER SP R SP NUMBER CRLF
+ 13 | RETR check_login SP pathname CRLF
+ 14 | STOR check_login SP pathname CRLF
+ 15 | APPE check_login SP pathname CRLF
+ 16 | NLST check_login CRLF
+ 17 | NLST check_login SP STRING CRLF
+ 18 | LIST check_login CRLF
+ 19 | LIST check_login SP pathname CRLF
+ 20 | STAT check_login SP pathname CRLF
+ 21 | STAT CRLF
+ 22 | DELE check_login SP pathname CRLF
+ 23 | RNTO SP pathname CRLF
+ 24 | ABOR CRLF
+ 25 | CWD check_login CRLF
+ 26 | CWD check_login SP pathname CRLF
+ 27 | HELP CRLF
+ 28 | HELP SP STRING CRLF
+ 29 | NOOP CRLF
+ 30 | MKD check_login SP pathname CRLF
+ 31 | RMD check_login SP pathname CRLF
+ 32 | PWD check_login CRLF
+ 33 | CDUP check_login CRLF
+ 34 | SITE SP HELP CRLF
+ 35 | SITE SP HELP SP STRING CRLF
+ 36 | SITE SP UMASK check_login CRLF
+ 37 | SITE SP UMASK check_login SP octal_number CRLF
+ 38 | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+ 39 | SITE SP IDLE CRLF
+ 40 | SITE SP IDLE SP NUMBER CRLF
+ 41 | STOU check_login SP pathname CRLF
+ 42 | SYST CRLF
+ 43 | SIZE check_login SP pathname CRLF
+ 44 | MDTM check_login SP pathname CRLF
+ 45 | QUIT CRLF
+ 46 | error CRLF
+
+ 47 rcmd : RNFR check_login SP pathname CRLF
+
+ 48 username : STRING
+
+ 49 password :
+ 50 | STRING
+
+ 51 byte_size : NUMBER
+
+ 52 host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER
+
+ 53 form_code : N
+ 54 | T
+ 55 | C
+
+ 56 type_code : A
+ 57 | A SP form_code
+ 58 | E
+ 59 | E SP form_code
+ 60 | I
+ 61 | L
+ 62 | L SP byte_size
+ 63 | L byte_size
+
+ 64 struct_code : F
+ 65 | R
+ 66 | P
+
+ 67 mode_code : S
+ 68 | B
+ 69 | C
+
+ 70 pathname : pathstring
+
+ 71 pathstring : STRING
+
+ 72 octal_number : NUMBER
+
+ 73 check_login :
+
+state 0
+ $accept : . cmd_list $end (0)
+ cmd_list : . (1)
+
+ . reduce 1
+
+ cmd_list goto 1
+
+
+state 1
+ $accept : cmd_list . $end (0)
+ cmd_list : cmd_list . cmd (2)
+ cmd_list : cmd_list . rcmd (3)
+
+ $end accept
+ error shift 2
+ USER shift 3
+ PASS shift 4
+ QUIT shift 5
+ PORT shift 6
+ PASV shift 7
+ TYPE shift 8
+ STRU shift 9
+ MODE shift 10
+ RETR shift 11
+ STOR shift 12
+ APPE shift 13
+ ALLO shift 14
+ RNFR shift 15
+ RNTO shift 16
+ ABOR shift 17
+ DELE shift 18
+ CWD shift 19
+ LIST shift 20
+ NLST shift 21
+ SITE shift 22
+ STAT shift 23
+ HELP shift 24
+ NOOP shift 25
+ MKD shift 26
+ RMD shift 27
+ PWD shift 28
+ CDUP shift 29
+ STOU shift 30
+ SYST shift 31
+ SIZE shift 32
+ MDTM shift 33
+ . error
+
+ cmd goto 34
+ rcmd goto 35
+
+
+state 2
+ cmd : error . CRLF (46)
+
+ CRLF shift 36
+ . error
+
+
+state 3
+ cmd : USER . SP username CRLF (4)
+
+ SP shift 37
+ . error
+
+
+state 4
+ cmd : PASS . SP password CRLF (5)
+
+ SP shift 38
+ . error
+
+
+state 5
+ cmd : QUIT . CRLF (45)
+
+ CRLF shift 39
+ . error
+
+
+state 6
+ cmd : PORT . SP host_port CRLF (6)
+
+ SP shift 40
+ . error
+
+
+state 7
+ cmd : PASV . CRLF (7)
+
+ CRLF shift 41
+ . error
+
+
+state 8
+ cmd : TYPE . SP type_code CRLF (8)
+
+ SP shift 42
+ . error
+
+
+state 9
+ cmd : STRU . SP struct_code CRLF (9)
+
+ SP shift 43
+ . error
+
+
+state 10
+ cmd : MODE . SP mode_code CRLF (10)
+
+ SP shift 44
+ . error
+
+
+state 11
+ cmd : RETR . check_login SP pathname CRLF (13)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 45
+
+
+state 12
+ cmd : STOR . check_login SP pathname CRLF (14)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 46
+
+
+state 13
+ cmd : APPE . check_login SP pathname CRLF (15)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 47
+
+
+state 14
+ cmd : ALLO . SP NUMBER CRLF (11)
+ cmd : ALLO . SP NUMBER SP R SP NUMBER CRLF (12)
+
+ SP shift 48
+ . error
+
+
+state 15
+ rcmd : RNFR . check_login SP pathname CRLF (47)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 49
+
+
+state 16
+ cmd : RNTO . SP pathname CRLF (23)
+
+ SP shift 50
+ . error
+
+
+state 17
+ cmd : ABOR . CRLF (24)
+
+ CRLF shift 51
+ . error
+
+
+state 18
+ cmd : DELE . check_login SP pathname CRLF (22)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 52
+
+
+state 19
+ cmd : CWD . check_login CRLF (25)
+ cmd : CWD . check_login SP pathname CRLF (26)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 53
+
+
+state 20
+ cmd : LIST . check_login CRLF (18)
+ cmd : LIST . check_login SP pathname CRLF (19)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 54
+
+
+state 21
+ cmd : NLST . check_login CRLF (16)
+ cmd : NLST . check_login SP STRING CRLF (17)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 55
+
+
+state 22
+ cmd : SITE . SP HELP CRLF (34)
+ cmd : SITE . SP HELP SP STRING CRLF (35)
+ cmd : SITE . SP UMASK check_login CRLF (36)
+ cmd : SITE . SP UMASK check_login SP octal_number CRLF (37)
+ cmd : SITE . SP CHMOD check_login SP octal_number SP pathname CRLF (38)
+ cmd : SITE . SP IDLE CRLF (39)
+ cmd : SITE . SP IDLE SP NUMBER CRLF (40)
+
+ SP shift 56
+ . error
+
+
+state 23
+ cmd : STAT . check_login SP pathname CRLF (20)
+ cmd : STAT . CRLF (21)
+ check_login : . (73)
+
+ CRLF shift 57
+ SP reduce 73
+
+ check_login goto 58
+
+
+state 24
+ cmd : HELP . CRLF (27)
+ cmd : HELP . SP STRING CRLF (28)
+
+ SP shift 59
+ CRLF shift 60
+ . error
+
+
+state 25
+ cmd : NOOP . CRLF (29)
+
+ CRLF shift 61
+ . error
+
+
+state 26
+ cmd : MKD . check_login SP pathname CRLF (30)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 62
+
+
+state 27
+ cmd : RMD . check_login SP pathname CRLF (31)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 63
+
+
+state 28
+ cmd : PWD . check_login CRLF (32)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 64
+
+
+state 29
+ cmd : CDUP . check_login CRLF (33)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 65
+
+
+state 30
+ cmd : STOU . check_login SP pathname CRLF (41)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 66
+
+
+state 31
+ cmd : SYST . CRLF (42)
+
+ CRLF shift 67
+ . error
+
+
+state 32
+ cmd : SIZE . check_login SP pathname CRLF (43)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 68
+
+
+state 33
+ cmd : MDTM . check_login SP pathname CRLF (44)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 69
+
+
+state 34
+ cmd_list : cmd_list cmd . (2)
+
+ . reduce 2
+
+
+state 35
+ cmd_list : cmd_list rcmd . (3)
+
+ . reduce 3
+
+
+state 36
+ cmd : error CRLF . (46)
+
+ . reduce 46
+
+
+state 37
+ cmd : USER SP . username CRLF (4)
+
+ STRING shift 70
+ . error
+
+ username goto 71
+
+
+state 38
+ cmd : PASS SP . password CRLF (5)
+ password : . (49)
+
+ STRING shift 72
+ CRLF reduce 49
+
+ password goto 73
+
+
+state 39
+ cmd : QUIT CRLF . (45)
+
+ . reduce 45
+
+
+state 40
+ cmd : PORT SP . host_port CRLF (6)
+
+ NUMBER shift 74
+ . error
+
+ host_port goto 75
+
+
+state 41
+ cmd : PASV CRLF . (7)
+
+ . reduce 7
+
+
+state 42
+ cmd : TYPE SP . type_code CRLF (8)
+
+ A shift 76
+ E shift 77
+ I shift 78
+ L shift 79
+ . error
+
+ type_code goto 80
+
+
+state 43
+ cmd : STRU SP . struct_code CRLF (9)
+
+ F shift 81
+ P shift 82
+ R shift 83
+ . error
+
+ struct_code goto 84
+
+
+state 44
+ cmd : MODE SP . mode_code CRLF (10)
+
+ B shift 85
+ C shift 86
+ S shift 87
+ . error
+
+ mode_code goto 88
+
+
+state 45
+ cmd : RETR check_login . SP pathname CRLF (13)
+
+ SP shift 89
+ . error
+
+
+state 46
+ cmd : STOR check_login . SP pathname CRLF (14)
+
+ SP shift 90
+ . error
+
+
+state 47
+ cmd : APPE check_login . SP pathname CRLF (15)
+
+ SP shift 91
+ . error
+
+
+state 48
+ cmd : ALLO SP . NUMBER CRLF (11)
+ cmd : ALLO SP . NUMBER SP R SP NUMBER CRLF (12)
+
+ NUMBER shift 92
+ . error
+
+
+state 49
+ rcmd : RNFR check_login . SP pathname CRLF (47)
+
+ SP shift 93
+ . error
+
+
+state 50
+ cmd : RNTO SP . pathname CRLF (23)
+
+ STRING shift 94
+ . error
+
+ pathname goto 95
+ pathstring goto 96
+
+
+state 51
+ cmd : ABOR CRLF . (24)
+
+ . reduce 24
+
+
+state 52
+ cmd : DELE check_login . SP pathname CRLF (22)
+
+ SP shift 97
+ . error
+
+
+state 53
+ cmd : CWD check_login . CRLF (25)
+ cmd : CWD check_login . SP pathname CRLF (26)
+
+ SP shift 98
+ CRLF shift 99
+ . error
+
+
+state 54
+ cmd : LIST check_login . CRLF (18)
+ cmd : LIST check_login . SP pathname CRLF (19)
+
+ SP shift 100
+ CRLF shift 101
+ . error
+
+
+state 55
+ cmd : NLST check_login . CRLF (16)
+ cmd : NLST check_login . SP STRING CRLF (17)
+
+ SP shift 102
+ CRLF shift 103
+ . error
+
+
+state 56
+ cmd : SITE SP . HELP CRLF (34)
+ cmd : SITE SP . HELP SP STRING CRLF (35)
+ cmd : SITE SP . UMASK check_login CRLF (36)
+ cmd : SITE SP . UMASK check_login SP octal_number CRLF (37)
+ cmd : SITE SP . CHMOD check_login SP octal_number SP pathname CRLF (38)
+ cmd : SITE SP . IDLE CRLF (39)
+ cmd : SITE SP . IDLE SP NUMBER CRLF (40)
+
+ HELP shift 104
+ UMASK shift 105
+ IDLE shift 106
+ CHMOD shift 107
+ . error
+
+
+state 57
+ cmd : STAT CRLF . (21)
+
+ . reduce 21
+
+
+state 58
+ cmd : STAT check_login . SP pathname CRLF (20)
+
+ SP shift 108
+ . error
+
+
+state 59
+ cmd : HELP SP . STRING CRLF (28)
+
+ STRING shift 109
+ . error
+
+
+state 60
+ cmd : HELP CRLF . (27)
+
+ . reduce 27
+
+
+state 61
+ cmd : NOOP CRLF . (29)
+
+ . reduce 29
+
+
+state 62
+ cmd : MKD check_login . SP pathname CRLF (30)
+
+ SP shift 110
+ . error
+
+
+state 63
+ cmd : RMD check_login . SP pathname CRLF (31)
+
+ SP shift 111
+ . error
+
+
+state 64
+ cmd : PWD check_login . CRLF (32)
+
+ CRLF shift 112
+ . error
+
+
+state 65
+ cmd : CDUP check_login . CRLF (33)
+
+ CRLF shift 113
+ . error
+
+
+state 66
+ cmd : STOU check_login . SP pathname CRLF (41)
+
+ SP shift 114
+ . error
+
+
+state 67
+ cmd : SYST CRLF . (42)
+
+ . reduce 42
+
+
+state 68
+ cmd : SIZE check_login . SP pathname CRLF (43)
+
+ SP shift 115
+ . error
+
+
+state 69
+ cmd : MDTM check_login . SP pathname CRLF (44)
+
+ SP shift 116
+ . error
+
+
+state 70
+ username : STRING . (48)
+
+ . reduce 48
+
+
+state 71
+ cmd : USER SP username . CRLF (4)
+
+ CRLF shift 117
+ . error
+
+
+state 72
+ password : STRING . (50)
+
+ . reduce 50
+
+
+state 73
+ cmd : PASS SP password . CRLF (5)
+
+ CRLF shift 118
+ . error
+
+
+state 74
+ host_port : NUMBER . COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ COMMA shift 119
+ . error
+
+
+state 75
+ cmd : PORT SP host_port . CRLF (6)
+
+ CRLF shift 120
+ . error
+
+
+state 76
+ type_code : A . (56)
+ type_code : A . SP form_code (57)
+
+ SP shift 121
+ CRLF reduce 56
+
+
+state 77
+ type_code : E . (58)
+ type_code : E . SP form_code (59)
+
+ SP shift 122
+ CRLF reduce 58
+
+
+state 78
+ type_code : I . (60)
+
+ . reduce 60
+
+
+state 79
+ type_code : L . (61)
+ type_code : L . SP byte_size (62)
+ type_code : L . byte_size (63)
+
+ SP shift 123
+ NUMBER shift 124
+ CRLF reduce 61
+
+ byte_size goto 125
+
+
+state 80
+ cmd : TYPE SP type_code . CRLF (8)
+
+ CRLF shift 126
+ . error
+
+
+state 81
+ struct_code : F . (64)
+
+ . reduce 64
+
+
+state 82
+ struct_code : P . (66)
+
+ . reduce 66
+
+
+state 83
+ struct_code : R . (65)
+
+ . reduce 65
+
+
+state 84
+ cmd : STRU SP struct_code . CRLF (9)
+
+ CRLF shift 127
+ . error
+
+
+state 85
+ mode_code : B . (68)
+
+ . reduce 68
+
+
+state 86
+ mode_code : C . (69)
+
+ . reduce 69
+
+
+state 87
+ mode_code : S . (67)
+
+ . reduce 67
+
+
+state 88
+ cmd : MODE SP mode_code . CRLF (10)
+
+ CRLF shift 128
+ . error
+
+
+state 89
+ cmd : RETR check_login SP . pathname CRLF (13)
+
+ STRING shift 94
+ . error
+
+ pathname goto 129
+ pathstring goto 96
+
+
+state 90
+ cmd : STOR check_login SP . pathname CRLF (14)
+
+ STRING shift 94
+ . error
+
+ pathname goto 130
+ pathstring goto 96
+
+
+state 91
+ cmd : APPE check_login SP . pathname CRLF (15)
+
+ STRING shift 94
+ . error
+
+ pathname goto 131
+ pathstring goto 96
+
+
+state 92
+ cmd : ALLO SP NUMBER . CRLF (11)
+ cmd : ALLO SP NUMBER . SP R SP NUMBER CRLF (12)
+
+ SP shift 132
+ CRLF shift 133
+ . error
+
+
+state 93
+ rcmd : RNFR check_login SP . pathname CRLF (47)
+
+ STRING shift 94
+ . error
+
+ pathname goto 134
+ pathstring goto 96
+
+
+state 94
+ pathstring : STRING . (71)
+
+ . reduce 71
+
+
+state 95
+ cmd : RNTO SP pathname . CRLF (23)
+
+ CRLF shift 135
+ . error
+
+
+state 96
+ pathname : pathstring . (70)
+
+ . reduce 70
+
+
+state 97
+ cmd : DELE check_login SP . pathname CRLF (22)
+
+ STRING shift 94
+ . error
+
+ pathname goto 136
+ pathstring goto 96
+
+
+state 98
+ cmd : CWD check_login SP . pathname CRLF (26)
+
+ STRING shift 94
+ . error
+
+ pathname goto 137
+ pathstring goto 96
+
+
+state 99
+ cmd : CWD check_login CRLF . (25)
+
+ . reduce 25
+
+
+state 100
+ cmd : LIST check_login SP . pathname CRLF (19)
+
+ STRING shift 94
+ . error
+
+ pathname goto 138
+ pathstring goto 96
+
+
+state 101
+ cmd : LIST check_login CRLF . (18)
+
+ . reduce 18
+
+
+state 102
+ cmd : NLST check_login SP . STRING CRLF (17)
+
+ STRING shift 139
+ . error
+
+
+state 103
+ cmd : NLST check_login CRLF . (16)
+
+ . reduce 16
+
+
+state 104
+ cmd : SITE SP HELP . CRLF (34)
+ cmd : SITE SP HELP . SP STRING CRLF (35)
+
+ SP shift 140
+ CRLF shift 141
+ . error
+
+
+state 105
+ cmd : SITE SP UMASK . check_login CRLF (36)
+ cmd : SITE SP UMASK . check_login SP octal_number CRLF (37)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 142
+
+
+state 106
+ cmd : SITE SP IDLE . CRLF (39)
+ cmd : SITE SP IDLE . SP NUMBER CRLF (40)
+
+ SP shift 143
+ CRLF shift 144
+ . error
+
+
+state 107
+ cmd : SITE SP CHMOD . check_login SP octal_number SP pathname CRLF (38)
+ check_login : . (73)
+
+ . reduce 73
+
+ check_login goto 145
+
+
+state 108
+ cmd : STAT check_login SP . pathname CRLF (20)
+
+ STRING shift 94
+ . error
+
+ pathname goto 146
+ pathstring goto 96
+
+
+state 109
+ cmd : HELP SP STRING . CRLF (28)
+
+ CRLF shift 147
+ . error
+
+
+state 110
+ cmd : MKD check_login SP . pathname CRLF (30)
+
+ STRING shift 94
+ . error
+
+ pathname goto 148
+ pathstring goto 96
+
+
+state 111
+ cmd : RMD check_login SP . pathname CRLF (31)
+
+ STRING shift 94
+ . error
+
+ pathname goto 149
+ pathstring goto 96
+
+
+state 112
+ cmd : PWD check_login CRLF . (32)
+
+ . reduce 32
+
+
+state 113
+ cmd : CDUP check_login CRLF . (33)
+
+ . reduce 33
+
+
+state 114
+ cmd : STOU check_login SP . pathname CRLF (41)
+
+ STRING shift 94
+ . error
+
+ pathname goto 150
+ pathstring goto 96
+
+
+state 115
+ cmd : SIZE check_login SP . pathname CRLF (43)
+
+ STRING shift 94
+ . error
+
+ pathname goto 151
+ pathstring goto 96
+
+
+state 116
+ cmd : MDTM check_login SP . pathname CRLF (44)
+
+ STRING shift 94
+ . error
+
+ pathname goto 152
+ pathstring goto 96
+
+
+state 117
+ cmd : USER SP username CRLF . (4)
+
+ . reduce 4
+
+
+state 118
+ cmd : PASS SP password CRLF . (5)
+
+ . reduce 5
+
+
+state 119
+ host_port : NUMBER COMMA . NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ NUMBER shift 153
+ . error
+
+
+state 120
+ cmd : PORT SP host_port CRLF . (6)
+
+ . reduce 6
+
+
+state 121
+ type_code : A SP . form_code (57)
+
+ C shift 154
+ N shift 155
+ T shift 156
+ . error
+
+ form_code goto 157
+
+
+state 122
+ type_code : E SP . form_code (59)
+
+ C shift 154
+ N shift 155
+ T shift 156
+ . error
+
+ form_code goto 158
+
+
+state 123
+ type_code : L SP . byte_size (62)
+
+ NUMBER shift 124
+ . error
+
+ byte_size goto 159
+
+
+state 124
+ byte_size : NUMBER . (51)
+
+ . reduce 51
+
+
+state 125
+ type_code : L byte_size . (63)
+
+ . reduce 63
+
+
+state 126
+ cmd : TYPE SP type_code CRLF . (8)
+
+ . reduce 8
+
+
+state 127
+ cmd : STRU SP struct_code CRLF . (9)
+
+ . reduce 9
+
+
+state 128
+ cmd : MODE SP mode_code CRLF . (10)
+
+ . reduce 10
+
+
+state 129
+ cmd : RETR check_login SP pathname . CRLF (13)
+
+ CRLF shift 160
+ . error
+
+
+state 130
+ cmd : STOR check_login SP pathname . CRLF (14)
+
+ CRLF shift 161
+ . error
+
+
+state 131
+ cmd : APPE check_login SP pathname . CRLF (15)
+
+ CRLF shift 162
+ . error
+
+
+state 132
+ cmd : ALLO SP NUMBER SP . R SP NUMBER CRLF (12)
+
+ R shift 163
+ . error
+
+
+state 133
+ cmd : ALLO SP NUMBER CRLF . (11)
+
+ . reduce 11
+
+
+state 134
+ rcmd : RNFR check_login SP pathname . CRLF (47)
+
+ CRLF shift 164
+ . error
+
+
+state 135
+ cmd : RNTO SP pathname CRLF . (23)
+
+ . reduce 23
+
+
+state 136
+ cmd : DELE check_login SP pathname . CRLF (22)
+
+ CRLF shift 165
+ . error
+
+
+state 137
+ cmd : CWD check_login SP pathname . CRLF (26)
+
+ CRLF shift 166
+ . error
+
+
+state 138
+ cmd : LIST check_login SP pathname . CRLF (19)
+
+ CRLF shift 167
+ . error
+
+
+state 139
+ cmd : NLST check_login SP STRING . CRLF (17)
+
+ CRLF shift 168
+ . error
+
+
+state 140
+ cmd : SITE SP HELP SP . STRING CRLF (35)
+
+ STRING shift 169
+ . error
+
+
+state 141
+ cmd : SITE SP HELP CRLF . (34)
+
+ . reduce 34
+
+
+state 142
+ cmd : SITE SP UMASK check_login . CRLF (36)
+ cmd : SITE SP UMASK check_login . SP octal_number CRLF (37)
+
+ SP shift 170
+ CRLF shift 171
+ . error
+
+
+state 143
+ cmd : SITE SP IDLE SP . NUMBER CRLF (40)
+
+ NUMBER shift 172
+ . error
+
+
+state 144
+ cmd : SITE SP IDLE CRLF . (39)
+
+ . reduce 39
+
+
+state 145
+ cmd : SITE SP CHMOD check_login . SP octal_number SP pathname CRLF (38)
+
+ SP shift 173
+ . error
+
+
+state 146
+ cmd : STAT check_login SP pathname . CRLF (20)
+
+ CRLF shift 174
+ . error
+
+
+state 147
+ cmd : HELP SP STRING CRLF . (28)
+
+ . reduce 28
+
+
+state 148
+ cmd : MKD check_login SP pathname . CRLF (30)
+
+ CRLF shift 175
+ . error
+
+
+state 149
+ cmd : RMD check_login SP pathname . CRLF (31)
+
+ CRLF shift 176
+ . error
+
+
+state 150
+ cmd : STOU check_login SP pathname . CRLF (41)
+
+ CRLF shift 177
+ . error
+
+
+state 151
+ cmd : SIZE check_login SP pathname . CRLF (43)
+
+ CRLF shift 178
+ . error
+
+
+state 152
+ cmd : MDTM check_login SP pathname . CRLF (44)
+
+ CRLF shift 179
+ . error
+
+
+state 153
+ host_port : NUMBER COMMA NUMBER . COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ COMMA shift 180
+ . error
+
+
+state 154
+ form_code : C . (55)
+
+ . reduce 55
+
+
+state 155
+ form_code : N . (53)
+
+ . reduce 53
+
+
+state 156
+ form_code : T . (54)
+
+ . reduce 54
+
+
+state 157
+ type_code : A SP form_code . (57)
+
+ . reduce 57
+
+
+state 158
+ type_code : E SP form_code . (59)
+
+ . reduce 59
+
+
+state 159
+ type_code : L SP byte_size . (62)
+
+ . reduce 62
+
+
+state 160
+ cmd : RETR check_login SP pathname CRLF . (13)
+
+ . reduce 13
+
+
+state 161
+ cmd : STOR check_login SP pathname CRLF . (14)
+
+ . reduce 14
+
+
+state 162
+ cmd : APPE check_login SP pathname CRLF . (15)
+
+ . reduce 15
+
+
+state 163
+ cmd : ALLO SP NUMBER SP R . SP NUMBER CRLF (12)
+
+ SP shift 181
+ . error
+
+
+state 164
+ rcmd : RNFR check_login SP pathname CRLF . (47)
+
+ . reduce 47
+
+
+state 165
+ cmd : DELE check_login SP pathname CRLF . (22)
+
+ . reduce 22
+
+
+state 166
+ cmd : CWD check_login SP pathname CRLF . (26)
+
+ . reduce 26
+
+
+state 167
+ cmd : LIST check_login SP pathname CRLF . (19)
+
+ . reduce 19
+
+
+state 168
+ cmd : NLST check_login SP STRING CRLF . (17)
+
+ . reduce 17
+
+
+state 169
+ cmd : SITE SP HELP SP STRING . CRLF (35)
+
+ CRLF shift 182
+ . error
+
+
+state 170
+ cmd : SITE SP UMASK check_login SP . octal_number CRLF (37)
+
+ NUMBER shift 183
+ . error
+
+ octal_number goto 184
+
+
+state 171
+ cmd : SITE SP UMASK check_login CRLF . (36)
+
+ . reduce 36
+
+
+state 172
+ cmd : SITE SP IDLE SP NUMBER . CRLF (40)
+
+ CRLF shift 185
+ . error
+
+
+state 173
+ cmd : SITE SP CHMOD check_login SP . octal_number SP pathname CRLF (38)
+
+ NUMBER shift 183
+ . error
+
+ octal_number goto 186
+
+
+state 174
+ cmd : STAT check_login SP pathname CRLF . (20)
+
+ . reduce 20
+
+
+state 175
+ cmd : MKD check_login SP pathname CRLF . (30)
+
+ . reduce 30
+
+
+state 176
+ cmd : RMD check_login SP pathname CRLF . (31)
+
+ . reduce 31
+
+
+state 177
+ cmd : STOU check_login SP pathname CRLF . (41)
+
+ . reduce 41
+
+
+state 178
+ cmd : SIZE check_login SP pathname CRLF . (43)
+
+ . reduce 43
+
+
+state 179
+ cmd : MDTM check_login SP pathname CRLF . (44)
+
+ . reduce 44
+
+
+state 180
+ host_port : NUMBER COMMA NUMBER COMMA . NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ NUMBER shift 187
+ . error
+
+
+state 181
+ cmd : ALLO SP NUMBER SP R SP . NUMBER CRLF (12)
+
+ NUMBER shift 188
+ . error
+
+
+state 182
+ cmd : SITE SP HELP SP STRING CRLF . (35)
+
+ . reduce 35
+
+
+state 183
+ octal_number : NUMBER . (72)
+
+ . reduce 72
+
+
+state 184
+ cmd : SITE SP UMASK check_login SP octal_number . CRLF (37)
+
+ CRLF shift 189
+ . error
+
+
+state 185
+ cmd : SITE SP IDLE SP NUMBER CRLF . (40)
+
+ . reduce 40
+
+
+state 186
+ cmd : SITE SP CHMOD check_login SP octal_number . SP pathname CRLF (38)
+
+ SP shift 190
+ . error
+
+
+state 187
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER . COMMA NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ COMMA shift 191
+ . error
+
+
+state 188
+ cmd : ALLO SP NUMBER SP R SP NUMBER . CRLF (12)
+
+ CRLF shift 192
+ . error
+
+
+state 189
+ cmd : SITE SP UMASK check_login SP octal_number CRLF . (37)
+
+ . reduce 37
+
+
+state 190
+ cmd : SITE SP CHMOD check_login SP octal_number SP . pathname CRLF (38)
+
+ STRING shift 94
+ . error
+
+ pathname goto 193
+ pathstring goto 96
+
+
+state 191
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA . NUMBER COMMA NUMBER COMMA NUMBER (52)
+
+ NUMBER shift 194
+ . error
+
+
+state 192
+ cmd : ALLO SP NUMBER SP R SP NUMBER CRLF . (12)
+
+ . reduce 12
+
+
+state 193
+ cmd : SITE SP CHMOD check_login SP octal_number SP pathname . CRLF (38)
+
+ CRLF shift 195
+ . error
+
+
+state 194
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER . COMMA NUMBER COMMA NUMBER (52)
+
+ COMMA shift 196
+ . error
+
+
+state 195
+ cmd : SITE SP CHMOD check_login SP octal_number SP pathname CRLF . (38)
+
+ . reduce 38
+
+
+state 196
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA . NUMBER COMMA NUMBER (52)
+
+ NUMBER shift 197
+ . error
+
+
+state 197
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER . COMMA NUMBER (52)
+
+ COMMA shift 198
+ . error
+
+
+state 198
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA . NUMBER (52)
+
+ NUMBER shift 199
+ . error
+
+
+state 199
+ host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER . (52)
+
+ . reduce 52
+
+
+65 terminals, 16 nonterminals
+74 grammar rules, 200 states
diff --git a/usr.bin/yacc/test/ftp.tab.c b/usr.bin/yacc/test/ftp.tab.c
new file mode 100644
index 0000000..5431bbf
--- /dev/null
+++ b/usr.bin/yacc/test/ftp.tab.c
@@ -0,0 +1,1743 @@
+#ifndef lint
+static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93";
+#endif
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define yyclearin (yychar=(-1))
+#define yyerrok (yyerrflag=0)
+#define YYRECOVERING (yyerrflag!=0)
+#define YYPREFIX "yy"
+#line 26 "ftp.y"
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/ftp.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <time.h>
+
+extern struct sockaddr_in data_dest;
+extern int logged_in;
+extern struct passwd *pw;
+extern int guest;
+extern int logging;
+extern int type;
+extern int form;
+extern int debug;
+extern int timeout;
+extern int maxtimeout;
+extern int pdata;
+extern char hostname[], remotehost[];
+extern char proctitle[];
+extern char *globerr;
+extern int usedefault;
+extern int transflag;
+extern char tmpline[];
+char **glob();
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char cbuf[512];
+char *fromname;
+
+char *index();
+#line 60 "ftp.tab.c"
+#define A 257
+#define B 258
+#define C 259
+#define E 260
+#define F 261
+#define I 262
+#define L 263
+#define N 264
+#define P 265
+#define R 266
+#define S 267
+#define T 268
+#define SP 269
+#define CRLF 270
+#define COMMA 271
+#define STRING 272
+#define NUMBER 273
+#define USER 274
+#define PASS 275
+#define ACCT 276
+#define REIN 277
+#define QUIT 278
+#define PORT 279
+#define PASV 280
+#define TYPE 281
+#define STRU 282
+#define MODE 283
+#define RETR 284
+#define STOR 285
+#define APPE 286
+#define MLFL 287
+#define MAIL 288
+#define MSND 289
+#define MSOM 290
+#define MSAM 291
+#define MRSQ 292
+#define MRCP 293
+#define ALLO 294
+#define REST 295
+#define RNFR 296
+#define RNTO 297
+#define ABOR 298
+#define DELE 299
+#define CWD 300
+#define LIST 301
+#define NLST 302
+#define SITE 303
+#define STAT 304
+#define HELP 305
+#define NOOP 306
+#define MKD 307
+#define RMD 308
+#define PWD 309
+#define CDUP 310
+#define STOU 311
+#define SMNT 312
+#define SYST 313
+#define SIZE 314
+#define MDTM 315
+#define UMASK 316
+#define IDLE 317
+#define CHMOD 318
+#define LEXERR 319
+#define YYERRCODE 256
+short yylhs[] = { -1,
+ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 3, 4, 4,
+ 12, 5, 13, 13, 13, 6, 6, 6, 6, 6,
+ 6, 6, 6, 7, 7, 7, 8, 8, 8, 10,
+ 14, 11, 9,
+};
+short yylen[] = { 2,
+ 0, 2, 2, 4, 4, 4, 2, 4, 4, 4,
+ 4, 8, 5, 5, 5, 3, 5, 3, 5, 5,
+ 2, 5, 4, 2, 3, 5, 2, 4, 2, 5,
+ 5, 3, 3, 4, 6, 5, 7, 9, 4, 6,
+ 5, 2, 5, 5, 2, 2, 5, 1, 0, 1,
+ 1, 11, 1, 1, 1, 1, 3, 1, 3, 1,
+ 1, 3, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0,
+};
+short yydefred[] = { 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 73, 73, 0, 73, 0, 0, 73, 73, 73,
+ 73, 0, 0, 0, 0, 73, 73, 73, 73, 73,
+ 0, 73, 73, 2, 3, 46, 0, 0, 45, 0,
+ 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 24, 0, 0, 0, 0, 0, 21, 0, 0, 27,
+ 29, 0, 0, 0, 0, 0, 42, 0, 0, 48,
+ 0, 50, 0, 0, 0, 0, 0, 60, 0, 0,
+ 64, 66, 65, 0, 68, 69, 67, 0, 0, 0,
+ 0, 0, 0, 71, 0, 70, 0, 0, 25, 0,
+ 18, 0, 16, 0, 73, 0, 73, 0, 0, 0,
+ 0, 32, 33, 0, 0, 0, 4, 5, 0, 6,
+ 0, 0, 0, 51, 63, 8, 9, 10, 0, 0,
+ 0, 0, 11, 0, 23, 0, 0, 0, 0, 0,
+ 34, 0, 0, 39, 0, 0, 28, 0, 0, 0,
+ 0, 0, 0, 55, 53, 54, 57, 59, 62, 13,
+ 14, 15, 0, 47, 22, 26, 19, 17, 0, 0,
+ 36, 0, 0, 20, 30, 31, 41, 43, 44, 0,
+ 0, 35, 72, 0, 40, 0, 0, 0, 37, 0,
+ 0, 12, 0, 0, 38, 0, 0, 0, 52,
+};
+short yydgoto[] = { 1,
+ 34, 35, 71, 73, 75, 80, 84, 88, 45, 95,
+ 184, 125, 157, 96,
+};
+short yysindex[] = { 0,
+ -224, -247, -239, -236, -232, -222, -204, -200, -181, -177,
+ 0, 0, 0, -166, 0, -161, -199, 0, 0, 0,
+ 0, -160, -159, -264, -158, 0, 0, 0, 0, 0,
+ -157, 0, 0, 0, 0, 0, -167, -162, 0, -156,
+ 0, -250, -198, -165, -155, -154, -153, -151, -150, -152,
+ 0, -145, -252, -229, -217, -302, 0, -144, -146, 0,
+ 0, -142, -141, -140, -139, -137, 0, -136, -135, 0,
+ -134, 0, -133, -132, -130, -131, -128, 0, -249, -127,
+ 0, 0, 0, -126, 0, 0, 0, -125, -152, -152,
+ -152, -205, -152, 0, -124, 0, -152, -152, 0, -152,
+ 0, -143, 0, -173, 0, -171, 0, -152, -123, -152,
+ -152, 0, 0, -152, -152, -152, 0, 0, -138, 0,
+ -164, -164, -122, 0, 0, 0, 0, 0, -121, -120,
+ -118, -148, 0, -117, 0, -116, -115, -114, -113, -112,
+ 0, -163, -111, 0, -110, -109, 0, -107, -106, -105,
+ -104, -103, -129, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -101, 0, 0, 0, 0, 0, -100, -102,
+ 0, -98, -102, 0, 0, 0, 0, 0, 0, -99,
+ -97, 0, 0, -95, 0, -96, -94, -92, 0, -152,
+ -93, 0, -91, -90, 0, -88, -87, -86, 0,
+};
+short yyrindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, -83, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -82, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -81, -80, 0, -158, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+short yygindex[] = { 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, -89,
+ -25, 35, 47, 0,
+};
+#define YYTABLESIZE 190
+short yytable[] = { 129,
+ 130, 131, 104, 134, 59, 60, 76, 136, 137, 77,
+ 138, 78, 79, 105, 106, 107, 98, 99, 146, 123,
+ 148, 149, 36, 124, 150, 151, 152, 46, 47, 37,
+ 49, 2, 38, 52, 53, 54, 55, 39, 58, 100,
+ 101, 62, 63, 64, 65, 66, 40, 68, 69, 3,
+ 4, 102, 103, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 81, 132, 133, 41, 82, 83, 42, 14,
+ 51, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 43, 31, 32,
+ 33, 44, 85, 86, 154, 140, 141, 143, 144, 155,
+ 193, 87, 48, 156, 70, 170, 171, 50, 56, 72,
+ 57, 61, 67, 89, 90, 91, 74, 163, 93, 94,
+ 142, 92, 145, 97, 108, 109, 110, 111, 139, 112,
+ 113, 114, 115, 116, 153, 117, 118, 121, 119, 120,
+ 122, 180, 126, 127, 128, 135, 147, 186, 160, 161,
+ 124, 162, 164, 165, 166, 167, 168, 159, 173, 169,
+ 174, 172, 175, 176, 177, 178, 179, 181, 158, 182,
+ 183, 185, 190, 187, 189, 188, 191, 192, 195, 194,
+ 196, 0, 0, 198, 197, 73, 199, 49, 56, 58,
+};
+short yycheck[] = { 89,
+ 90, 91, 305, 93, 269, 270, 257, 97, 98, 260,
+ 100, 262, 263, 316, 317, 318, 269, 270, 108, 269,
+ 110, 111, 270, 273, 114, 115, 116, 12, 13, 269,
+ 15, 256, 269, 18, 19, 20, 21, 270, 23, 269,
+ 270, 26, 27, 28, 29, 30, 269, 32, 33, 274,
+ 275, 269, 270, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 261, 269, 270, 270, 265, 266, 269, 294,
+ 270, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 269, 313, 314,
+ 315, 269, 258, 259, 259, 269, 270, 269, 270, 264,
+ 190, 267, 269, 268, 272, 269, 270, 269, 269, 272,
+ 270, 270, 270, 269, 269, 269, 273, 266, 269, 272,
+ 105, 273, 107, 269, 269, 272, 269, 269, 272, 270,
+ 270, 269, 269, 269, 273, 270, 270, 269, 271, 270,
+ 269, 271, 270, 270, 270, 270, 270, 173, 270, 270,
+ 273, 270, 270, 270, 270, 270, 270, 123, 269, 272,
+ 270, 273, 270, 270, 270, 270, 270, 269, 122, 270,
+ 273, 270, 269, 273, 270, 273, 271, 270, 270, 273,
+ 271, -1, -1, 271, 273, 269, 273, 270, 270, 270,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 319
+#if YYDEBUG
+char *yyname[] = {
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"A","B","C","E","F","I","L","N",
+"P","R","S","T","SP","CRLF","COMMA","STRING","NUMBER","USER","PASS","ACCT",
+"REIN","QUIT","PORT","PASV","TYPE","STRU","MODE","RETR","STOR","APPE","MLFL",
+"MAIL","MSND","MSOM","MSAM","MRSQ","MRCP","ALLO","REST","RNFR","RNTO","ABOR",
+"DELE","CWD","LIST","NLST","SITE","STAT","HELP","NOOP","MKD","RMD","PWD","CDUP",
+"STOU","SMNT","SYST","SIZE","MDTM","UMASK","IDLE","CHMOD","LEXERR",
+};
+char *yyrule[] = {
+"$accept : cmd_list",
+"cmd_list :",
+"cmd_list : cmd_list cmd",
+"cmd_list : cmd_list rcmd",
+"cmd : USER SP username CRLF",
+"cmd : PASS SP password CRLF",
+"cmd : PORT SP host_port CRLF",
+"cmd : PASV CRLF",
+"cmd : TYPE SP type_code CRLF",
+"cmd : STRU SP struct_code CRLF",
+"cmd : MODE SP mode_code CRLF",
+"cmd : ALLO SP NUMBER CRLF",
+"cmd : ALLO SP NUMBER SP R SP NUMBER CRLF",
+"cmd : RETR check_login SP pathname CRLF",
+"cmd : STOR check_login SP pathname CRLF",
+"cmd : APPE check_login SP pathname CRLF",
+"cmd : NLST check_login CRLF",
+"cmd : NLST check_login SP STRING CRLF",
+"cmd : LIST check_login CRLF",
+"cmd : LIST check_login SP pathname CRLF",
+"cmd : STAT check_login SP pathname CRLF",
+"cmd : STAT CRLF",
+"cmd : DELE check_login SP pathname CRLF",
+"cmd : RNTO SP pathname CRLF",
+"cmd : ABOR CRLF",
+"cmd : CWD check_login CRLF",
+"cmd : CWD check_login SP pathname CRLF",
+"cmd : HELP CRLF",
+"cmd : HELP SP STRING CRLF",
+"cmd : NOOP CRLF",
+"cmd : MKD check_login SP pathname CRLF",
+"cmd : RMD check_login SP pathname CRLF",
+"cmd : PWD check_login CRLF",
+"cmd : CDUP check_login CRLF",
+"cmd : SITE SP HELP CRLF",
+"cmd : SITE SP HELP SP STRING CRLF",
+"cmd : SITE SP UMASK check_login CRLF",
+"cmd : SITE SP UMASK check_login SP octal_number CRLF",
+"cmd : SITE SP CHMOD check_login SP octal_number SP pathname CRLF",
+"cmd : SITE SP IDLE CRLF",
+"cmd : SITE SP IDLE SP NUMBER CRLF",
+"cmd : STOU check_login SP pathname CRLF",
+"cmd : SYST CRLF",
+"cmd : SIZE check_login SP pathname CRLF",
+"cmd : MDTM check_login SP pathname CRLF",
+"cmd : QUIT CRLF",
+"cmd : error CRLF",
+"rcmd : RNFR check_login SP pathname CRLF",
+"username : STRING",
+"password :",
+"password : STRING",
+"byte_size : NUMBER",
+"host_port : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER",
+"form_code : N",
+"form_code : T",
+"form_code : C",
+"type_code : A",
+"type_code : A SP form_code",
+"type_code : E",
+"type_code : E SP form_code",
+"type_code : I",
+"type_code : L",
+"type_code : L SP byte_size",
+"type_code : L byte_size",
+"struct_code : F",
+"struct_code : R",
+"struct_code : P",
+"mode_code : S",
+"mode_code : B",
+"mode_code : C",
+"pathname : pathstring",
+"pathstring : STRING",
+"octal_number : NUMBER",
+"check_login :",
+};
+#endif
+#ifndef YYSTYPE
+typedef int YYSTYPE;
+#endif
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH 500
+#endif
+#endif
+int yydebug;
+int yynerrs;
+int yyerrflag;
+int yychar;
+short *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE yyval;
+YYSTYPE yylval;
+short yyss[YYSTACKSIZE];
+YYSTYPE yyvs[YYSTACKSIZE];
+#define yystacksize YYSTACKSIZE
+#line 658 "ftp.y"
+
+extern jmp_buf errcatch;
+
+#define CMD 0 /* beginning of command */
+#define ARGS 1 /* expect miscellaneous arguments */
+#define STR1 2 /* expect SP followed by STRING */
+#define STR2 3 /* expect STRING */
+#define OSTR 4 /* optional SP then STRING */
+#define ZSTR1 5 /* SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+
+struct tab {
+ char *name;
+ short token;
+ short state;
+ short implemented; /* 1 if command is implemented */
+ char *help;
+};
+
+struct tab cmdtab[] = { /* In order defined in RFC 765 */
+ { "USER", USER, STR1, 1, "<sp> username" },
+ { "PASS", PASS, ZSTR1, 1, "<sp> password" },
+ { "ACCT", ACCT, STR1, 0, "(specify account)" },
+ { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
+ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
+ { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
+ { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
+ { "STRU", STRU, ARGS, 1, "(specify file structure)" },
+ { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
+ { "RETR", RETR, STR1, 1, "<sp> file-name" },
+ { "STOR", STOR, STR1, 1, "<sp> file-name" },
+ { "APPE", APPE, STR1, 1, "<sp> file-name" },
+ { "MLFL", MLFL, OSTR, 0, "(mail file)" },
+ { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
+ { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
+ { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
+ { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
+ { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
+ { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
+ { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
+ { "REST", REST, ARGS, 0, "(restart command)" },
+ { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
+ { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
+ { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
+ { "DELE", DELE, STR1, 1, "<sp> file-name" },
+ { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
+ { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
+ { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
+ { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
+ { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { "NOOP", NOOP, ARGS, 1, "" },
+ { "MKD", MKD, STR1, 1, "<sp> path-name" },
+ { "XMKD", MKD, STR1, 1, "<sp> path-name" },
+ { "RMD", RMD, STR1, 1, "<sp> path-name" },
+ { "XRMD", RMD, STR1, 1, "<sp> path-name" },
+ { "PWD", PWD, ARGS, 1, "(return current directory)" },
+ { "XPWD", PWD, ARGS, 1, "(return current directory)" },
+ { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "STOU", STOU, STR1, 1, "<sp> file-name" },
+ { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
+ { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab sitetab[] = {
+ { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
+ { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
+ { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab *
+lookup(p, cmd)
+ register struct tab *p;
+ char *cmd;
+{
+
+ for (; p->name != NULL; p++)
+ if (strcmp(cmd, p->name) == 0)
+ return (p);
+ return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *
+getline(s, n, iop)
+ char *s;
+ register FILE *iop;
+{
+ register c;
+ register char *cs;
+
+ cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+ *cs++ = tmpline[c];
+ if (tmpline[c] == '\n') {
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ tmpline[0] = '\0';
+ return(s);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ while ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, DONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, WONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0 || c == '\n')
+ break;
+ }
+ if (c == EOF && cs == s)
+ return (NULL);
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ return (s);
+}
+
+static int
+toolong()
+{
+ time_t now;
+ extern char *ctime();
+ extern time_t time();
+
+ reply(421,
+ "Timeout (%d seconds): closing control connection.", timeout);
+ (void) time(&now);
+ if (logging) {
+ syslog(LOG_INFO,
+ "User %s timed out after %d seconds at %s",
+ (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
+ }
+ dologout(1);
+}
+
+yylex()
+{
+ static int cpos, state;
+ register char *cp, *cp2;
+ register struct tab *p;
+ int n;
+ char c, *strpbrk();
+ char *copy();
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout);
+ if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ (void) alarm(0);
+#ifdef SETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != NULL)
+ setproctitle("%s: %s", proctitle, cbuf);
+#endif /* SETPROCTITLE */
+ if ((cp = index(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+ if ((cp = strpbrk(cbuf, " \n")))
+ cpos = cp - cbuf;
+ if (cpos == 0)
+ cpos = 4;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cbuf);
+ p = lookup(cmdtab, cbuf);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ *(char **)&yylval = p->name;
+ return (p->token);
+ }
+ break;
+
+ case SITECMD:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ cp = &cbuf[cpos];
+ if ((cp2 = strpbrk(cp, " \n")))
+ cpos = cp2 - cbuf;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cp);
+ p = lookup(sitetab, cp);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ state = CMD;
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ *(char **)&yylval = p->name;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ case ZSTR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ state = state == OSTR ? STR2 : ++state;
+ return (SP);
+ }
+ break;
+
+ case ZSTR2:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR2:
+ cp = &cbuf[cpos];
+ n = strlen(cp);
+ cpos += n - 1;
+ /*
+ * Make sure the string is nonempty and \n terminated.
+ */
+ if (n > 1 && cbuf[cpos] == '\n') {
+ cbuf[cpos] = '\0';
+ *(char **)&yylval = copy(cp);
+ cbuf[cpos] = '\n';
+ state = ARGS;
+ return (STRING);
+ }
+ break;
+
+ case NSTR:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval = atoi(cp);
+ cbuf[cpos] = c;
+ state = STR1;
+ return (NUMBER);
+ }
+ state = STR1;
+ goto dostr1;
+
+ case ARGS:
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval = atoi(cp);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ switch (cbuf[cpos++]) {
+
+ case '\n':
+ state = CMD;
+ return (CRLF);
+
+ case ' ':
+ return (SP);
+
+ case ',':
+ return (COMMA);
+
+ case 'A':
+ case 'a':
+ return (A);
+
+ case 'B':
+ case 'b':
+ return (B);
+
+ case 'C':
+ case 'c':
+ return (C);
+
+ case 'E':
+ case 'e':
+ return (E);
+
+ case 'F':
+ case 'f':
+ return (F);
+
+ case 'I':
+ case 'i':
+ return (I);
+
+ case 'L':
+ case 'l':
+ return (L);
+
+ case 'N':
+ case 'n':
+ return (N);
+
+ case 'P':
+ case 'p':
+ return (P);
+
+ case 'R':
+ case 'r':
+ return (R);
+
+ case 'S':
+ case 's':
+ return (S);
+
+ case 'T':
+ case 't':
+ return (T);
+
+ }
+ break;
+
+ default:
+ fatal("Unknown state in scanner.");
+ }
+ yyerror((char *) 0);
+ state = CMD;
+ longjmp(errcatch,0);
+ }
+}
+
+upper(s)
+ register char *s;
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+char *
+copy(s)
+ char *s;
+{
+ char *p;
+ extern char *malloc(), *strcpy();
+
+ p = malloc((unsigned) strlen(s) + 1);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+
+help(ctab, s)
+ struct tab *ctab;
+ char *s;
+{
+ register struct tab *c;
+ register int width, NCMDS;
+ char *type;
+
+ if (ctab == sitetab)
+ type = "SITE ";
+ else
+ type = "";
+ width = 0, NCMDS = 0;
+ for (c = ctab; c->name != NULL; c++) {
+ int len = strlen(c->name);
+
+ if (len > width)
+ width = len;
+ NCMDS++;
+ }
+ width = (width + 8) &~ 7;
+ if (s == 0) {
+ register int i, j, w;
+ int columns, lines;
+
+ lreply(214, "The following %scommands are recognized %s.",
+ type, "(* =>'s unimplemented)");
+ columns = 76 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ printf(" ");
+ for (j = 0; j < columns; j++) {
+ c = ctab + j * lines + i;
+ printf("%s%c", c->name,
+ c->implemented ? ' ' : '*');
+ if (c + lines >= &ctab[NCMDS])
+ break;
+ w = strlen(c->name) + 1;
+ while (w < width) {
+ putchar(' ');
+ w++;
+ }
+ }
+ printf("\r\n");
+ }
+ (void) fflush(stdout);
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == (struct tab *)0) {
+ reply(502, "Unknown command %s.", s);
+ return;
+ }
+ if (c->implemented)
+ reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+ else
+ reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+ c->name, c->help);
+}
+
+sizecmd(filename)
+char *filename;
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I: {
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG)
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%lu", stbuf.st_size);
+ break;}
+ case TYPE_A: {
+ FILE *fin;
+ register int c, count;
+ struct stat stbuf;
+ fin = fopen(filename, "r");
+ if (fin == NULL) {
+ perror_reply(550, filename);
+ return;
+ }
+ if (fstat(fileno(fin), &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ }
+
+ count = 0;
+ while((c=getc(fin)) != EOF) {
+ if (c == '\n') /* will get expanded to \r\n */
+ count++;
+ count++;
+ }
+ (void) fclose(fin);
+
+ reply(213, "%ld", count);
+ break;}
+ default:
+ reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+ }
+}
+#line 908 "ftp.tab.c"
+#define YYABORT goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR goto yyerrlab
+int
+yyparse()
+{
+ register int yym, yyn, yystate;
+#if YYDEBUG
+ register char *yys;
+ extern char *getenv();
+
+ if (yys = getenv("YYDEBUG"))
+ {
+ yyn = *yys;
+ if (yyn >= '0' && yyn <= '9')
+ yydebug = yyn - '0';
+ }
+#endif
+
+ yynerrs = 0;
+ yyerrflag = 0;
+ yychar = (-1);
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ *yyssp = yystate = 0;
+
+yyloop:
+ if (yyn = yydefred[yystate]) goto yyreduce;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ }
+ if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, shifting to state %d\n",
+ YYPREFIX, yystate, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ yychar = (-1);
+ if (yyerrflag > 0) --yyerrflag;
+ goto yyloop;
+ }
+ if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+ {
+ yyn = yytable[yyn];
+ goto yyreduce;
+ }
+ if (yyerrflag) goto yyinrecovery;
+#ifdef lint
+ goto yynewerror;
+#endif
+yynewerror:
+ yyerror("syntax error");
+#ifdef lint
+ goto yyerrlab;
+#endif
+yyerrlab:
+ ++yynerrs;
+yyinrecovery:
+ if (yyerrflag < 3)
+ {
+ yyerrflag = 3;
+ for (;;)
+ {
+ if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate = yytable[yyn];
+ *++yyvsp = yylval;
+ goto yyloop;
+ }
+ else
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: error recovery discarding state %d\n",
+ YYPREFIX, *yyssp);
+#endif
+ if (yyssp <= yyss) goto yyabort;
+ --yyssp;
+ --yyvsp;
+ }
+ }
+ }
+ else
+ {
+ if (yychar == 0) goto yyabort;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+ YYPREFIX, yystate, yychar, yys);
+ }
+#endif
+ yychar = (-1);
+ goto yyloop;
+ }
+yyreduce:
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+ YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+ yym = yylen[yyn];
+ yyval = yyvsp[1-yym];
+ switch (yyn)
+ {
+case 2:
+#line 99 "ftp.y"
+ {
+ fromname = (char *) 0;
+ }
+break;
+case 4:
+#line 106 "ftp.y"
+ {
+ user((char *) yyvsp[-1]);
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 5:
+#line 111 "ftp.y"
+ {
+ pass((char *) yyvsp[-1]);
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 6:
+#line 116 "ftp.y"
+ {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
+break;
+case 7:
+#line 125 "ftp.y"
+ {
+ passive();
+ }
+break;
+case 8:
+#line 129 "ftp.y"
+ {
+ switch (cmd_type) {
+
+ case TYPE_A:
+ if (cmd_form == FORM_N) {
+ reply(200, "Type set to A.");
+ type = cmd_type;
+ form = cmd_form;
+ } else
+ reply(504, "Form must be N.");
+ break;
+
+ case TYPE_E:
+ reply(504, "Type E not implemented.");
+ break;
+
+ case TYPE_I:
+ reply(200, "Type set to I.");
+ type = cmd_type;
+ break;
+
+ case TYPE_L:
+#if NBBY == 8
+ if (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ } else
+ reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+ UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+ }
+ }
+break;
+case 9:
+#line 164 "ftp.y"
+ {
+ switch (yyvsp[-1]) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+break;
+case 10:
+#line 176 "ftp.y"
+ {
+ switch (yyvsp[-1]) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+break;
+case 11:
+#line 188 "ftp.y"
+ {
+ reply(202, "ALLO command ignored.");
+ }
+break;
+case 12:
+#line 192 "ftp.y"
+ {
+ reply(202, "ALLO command ignored.");
+ }
+break;
+case 13:
+#line 196 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ retrieve((char *) 0, (char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 14:
+#line 203 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ store((char *) yyvsp[-1], "w", 0);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 15:
+#line 210 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ store((char *) yyvsp[-1], "a", 0);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 16:
+#line 217 "ftp.y"
+ {
+ if (yyvsp[-1])
+ send_file_list(".");
+ }
+break;
+case 17:
+#line 222 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ send_file_list((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 18:
+#line 229 "ftp.y"
+ {
+ if (yyvsp[-1])
+ retrieve("/bin/ls -lgA", "");
+ }
+break;
+case 19:
+#line 234 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ retrieve("/bin/ls -lgA %s", (char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 20:
+#line 241 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ statfilecmd((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 21:
+#line 248 "ftp.y"
+ {
+ statcmd();
+ }
+break;
+case 22:
+#line 252 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ delete((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 23:
+#line 259 "ftp.y"
+ {
+ if (fromname) {
+ renamecmd(fromname, (char *) yyvsp[-1]);
+ free(fromname);
+ fromname = (char *) 0;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 24:
+#line 270 "ftp.y"
+ {
+ reply(225, "ABOR command successful.");
+ }
+break;
+case 25:
+#line 274 "ftp.y"
+ {
+ if (yyvsp[-1])
+ cwd(pw->pw_dir);
+ }
+break;
+case 26:
+#line 279 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ cwd((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 27:
+#line 286 "ftp.y"
+ {
+ help(cmdtab, (char *) 0);
+ }
+break;
+case 28:
+#line 290 "ftp.y"
+ {
+ register char *cp = (char *)yyvsp[-1];
+
+ if (strncasecmp(cp, "SITE", 4) == 0) {
+ cp = (char *)yyvsp[-1] + 4;
+ if (*cp == ' ')
+ cp++;
+ if (*cp)
+ help(sitetab, cp);
+ else
+ help(sitetab, (char *) 0);
+ } else
+ help(cmdtab, (char *) yyvsp[-1]);
+ }
+break;
+case 29:
+#line 305 "ftp.y"
+ {
+ reply(200, "NOOP command successful.");
+ }
+break;
+case 30:
+#line 309 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ makedir((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 31:
+#line 316 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ removedir((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 32:
+#line 323 "ftp.y"
+ {
+ if (yyvsp[-1])
+ pwd();
+ }
+break;
+case 33:
+#line 328 "ftp.y"
+ {
+ if (yyvsp[-1])
+ cwd("..");
+ }
+break;
+case 34:
+#line 333 "ftp.y"
+ {
+ help(sitetab, (char *) 0);
+ }
+break;
+case 35:
+#line 337 "ftp.y"
+ {
+ help(sitetab, (char *) yyvsp[-1]);
+ }
+break;
+case 36:
+#line 341 "ftp.y"
+ {
+ int oldmask;
+
+ if (yyvsp[-1]) {
+ oldmask = umask(0);
+ (void) umask(oldmask);
+ reply(200, "Current UMASK is %03o", oldmask);
+ }
+ }
+break;
+case 37:
+#line 351 "ftp.y"
+ {
+ int oldmask;
+
+ if (yyvsp[-3]) {
+ if ((yyvsp[-1] == -1) || (yyvsp[-1] > 0777)) {
+ reply(501, "Bad UMASK value");
+ } else {
+ oldmask = umask(yyvsp[-1]);
+ reply(200,
+ "UMASK set to %03o (was %03o)",
+ yyvsp[-1], oldmask);
+ }
+ }
+ }
+break;
+case 38:
+#line 366 "ftp.y"
+ {
+ if (yyvsp[-5] && (yyvsp[-1] != NULL)) {
+ if (yyvsp[-3] > 0777)
+ reply(501,
+ "CHMOD: Mode value must be between 0 and 0777");
+ else if (chmod((char *) yyvsp[-1], yyvsp[-3]) < 0)
+ perror_reply(550, (char *) yyvsp[-1]);
+ else
+ reply(200, "CHMOD command successful.");
+ }
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 39:
+#line 380 "ftp.y"
+ {
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d",
+ timeout, maxtimeout);
+ }
+break;
+case 40:
+#line 386 "ftp.y"
+ {
+ if (yyvsp[-1] < 30 || yyvsp[-1] > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds",
+ maxtimeout);
+ } else {
+ timeout = yyvsp[-1];
+ (void) alarm((unsigned) timeout);
+ reply(200,
+ "Maximum IDLE time set to %d seconds",
+ timeout);
+ }
+ }
+break;
+case 41:
+#line 400 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ store((char *) yyvsp[-1], "w", 1);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 42:
+#line 407 "ftp.y"
+ {
+#ifdef unix
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d",
+ NBBY, BSD);
+#else /* BSD */
+ reply(215, "UNIX Type: L%d", NBBY);
+#endif /* BSD */
+#else /* unix */
+ reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* unix */
+ }
+break;
+case 43:
+#line 428 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL)
+ sizecmd((char *) yyvsp[-1]);
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 44:
+#line 445 "ftp.y"
+ {
+ if (yyvsp[-3] && yyvsp[-1] != NULL) {
+ struct stat stbuf;
+ if (stat((char *) yyvsp[-1], &stbuf) < 0)
+ perror_reply(550, "%s", (char *) yyvsp[-1]);
+ else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.",
+ (char *) yyvsp[-1]);
+ } else {
+ register struct tm *t;
+ struct tm *gmtime();
+ t = gmtime(&stbuf.st_mtime);
+ reply(213,
+ "19%02d%02d%02d%02d%02d%02d",
+ t->tm_year, t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ }
+ }
+ if (yyvsp[-1] != NULL)
+ free((char *) yyvsp[-1]);
+ }
+break;
+case 45:
+#line 467 "ftp.y"
+ {
+ reply(221, "Goodbye.");
+ dologout(0);
+ }
+break;
+case 46:
+#line 472 "ftp.y"
+ {
+ yyerrok;
+ }
+break;
+case 47:
+#line 477 "ftp.y"
+ {
+ char *renamefrom();
+
+ if (yyvsp[-3] && yyvsp[-1]) {
+ fromname = renamefrom((char *) yyvsp[-1]);
+ if (fromname == (char *) 0 && yyvsp[-1]) {
+ free((char *) yyvsp[-1]);
+ }
+ }
+ }
+break;
+case 49:
+#line 493 "ftp.y"
+ {
+ *(char **)&(yyval) = "";
+ }
+break;
+case 52:
+#line 504 "ftp.y"
+ {
+ register char *a, *p;
+
+ a = (char *)&data_dest.sin_addr;
+ a[0] = yyvsp[-10]; a[1] = yyvsp[-8]; a[2] = yyvsp[-6]; a[3] = yyvsp[-4];
+ p = (char *)&data_dest.sin_port;
+ p[0] = yyvsp[-2]; p[1] = yyvsp[0];
+ data_dest.sin_family = AF_INET;
+ }
+break;
+case 53:
+#line 516 "ftp.y"
+ {
+ yyval = FORM_N;
+ }
+break;
+case 54:
+#line 520 "ftp.y"
+ {
+ yyval = FORM_T;
+ }
+break;
+case 55:
+#line 524 "ftp.y"
+ {
+ yyval = FORM_C;
+ }
+break;
+case 56:
+#line 530 "ftp.y"
+ {
+ cmd_type = TYPE_A;
+ cmd_form = FORM_N;
+ }
+break;
+case 57:
+#line 535 "ftp.y"
+ {
+ cmd_type = TYPE_A;
+ cmd_form = yyvsp[0];
+ }
+break;
+case 58:
+#line 540 "ftp.y"
+ {
+ cmd_type = TYPE_E;
+ cmd_form = FORM_N;
+ }
+break;
+case 59:
+#line 545 "ftp.y"
+ {
+ cmd_type = TYPE_E;
+ cmd_form = yyvsp[0];
+ }
+break;
+case 60:
+#line 550 "ftp.y"
+ {
+ cmd_type = TYPE_I;
+ }
+break;
+case 61:
+#line 554 "ftp.y"
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = NBBY;
+ }
+break;
+case 62:
+#line 559 "ftp.y"
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = yyvsp[0];
+ }
+break;
+case 63:
+#line 565 "ftp.y"
+ {
+ cmd_type = TYPE_L;
+ cmd_bytesz = yyvsp[0];
+ }
+break;
+case 64:
+#line 572 "ftp.y"
+ {
+ yyval = STRU_F;
+ }
+break;
+case 65:
+#line 576 "ftp.y"
+ {
+ yyval = STRU_R;
+ }
+break;
+case 66:
+#line 580 "ftp.y"
+ {
+ yyval = STRU_P;
+ }
+break;
+case 67:
+#line 586 "ftp.y"
+ {
+ yyval = MODE_S;
+ }
+break;
+case 68:
+#line 590 "ftp.y"
+ {
+ yyval = MODE_B;
+ }
+break;
+case 69:
+#line 594 "ftp.y"
+ {
+ yyval = MODE_C;
+ }
+break;
+case 70:
+#line 600 "ftp.y"
+ {
+ /*
+ * Problem: this production is used for all pathname
+ * processing, but only gives a 550 error reply.
+ * This is a valid reply in some cases but not in others.
+ */
+ if (logged_in && yyvsp[0] && strncmp((char *) yyvsp[0], "~", 1) == 0) {
+ *(char **)&(yyval) = *glob((char *) yyvsp[0]);
+ if (globerr != NULL) {
+ reply(550, globerr);
+ yyval = NULL;
+ }
+ free((char *) yyvsp[0]);
+ } else
+ yyval = yyvsp[0];
+ }
+break;
+case 72:
+#line 622 "ftp.y"
+ {
+ register int ret, dec, multby, digit;
+
+ /*
+ * Convert a number that was read as decimal number
+ * to what it would be if it had been read as octal.
+ */
+ dec = yyvsp[0];
+ multby = 1;
+ ret = 0;
+ while (dec) {
+ digit = dec%10;
+ if (digit > 7) {
+ ret = -1;
+ break;
+ }
+ ret += digit * multby;
+ multby *= 8;
+ dec /= 10;
+ }
+ yyval = ret;
+ }
+break;
+case 73:
+#line 647 "ftp.y"
+ {
+ if (logged_in)
+ yyval = 1;
+ else {
+ reply(530, "Please login with USER and PASS.");
+ yyval = 0;
+ }
+ }
+break;
+#line 1688 "ftp.tab.c"
+ }
+ yyssp -= yym;
+ yystate = *yyssp;
+ yyvsp -= yym;
+ yym = yylhs[yyn];
+ if (yystate == 0 && yym == 0)
+ {
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+ yystate = YYFINAL;
+ *++yyssp = YYFINAL;
+ *++yyvsp = yyval;
+ if (yychar < 0)
+ {
+ if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+ if (yydebug)
+ {
+ yys = 0;
+ if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+ if (!yys) yys = "illegal-symbol";
+ printf("%sdebug: state %d, reading %d (%s)\n",
+ YYPREFIX, YYFINAL, yychar, yys);
+ }
+#endif
+ }
+ if (yychar == 0) goto yyaccept;
+ goto yyloop;
+ }
+ if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+ yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+ yystate = yytable[yyn];
+ else
+ yystate = yydgoto[yym];
+#if YYDEBUG
+ if (yydebug)
+ printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ goto yyoverflow;
+ }
+ *++yyssp = yystate;
+ *++yyvsp = yyval;
+ goto yyloop;
+yyoverflow:
+ yyerror("yacc stack overflow");
+yyabort:
+ return (1);
+yyaccept:
+ return (0);
+}
diff --git a/usr.bin/yacc/test/ftp.tab.h b/usr.bin/yacc/test/ftp.tab.h
new file mode 100644
index 0000000..24f0791
--- /dev/null
+++ b/usr.bin/yacc/test/ftp.tab.h
@@ -0,0 +1,63 @@
+#define A 257
+#define B 258
+#define C 259
+#define E 260
+#define F 261
+#define I 262
+#define L 263
+#define N 264
+#define P 265
+#define R 266
+#define S 267
+#define T 268
+#define SP 269
+#define CRLF 270
+#define COMMA 271
+#define STRING 272
+#define NUMBER 273
+#define USER 274
+#define PASS 275
+#define ACCT 276
+#define REIN 277
+#define QUIT 278
+#define PORT 279
+#define PASV 280
+#define TYPE 281
+#define STRU 282
+#define MODE 283
+#define RETR 284
+#define STOR 285
+#define APPE 286
+#define MLFL 287
+#define MAIL 288
+#define MSND 289
+#define MSOM 290
+#define MSAM 291
+#define MRSQ 292
+#define MRCP 293
+#define ALLO 294
+#define REST 295
+#define RNFR 296
+#define RNTO 297
+#define ABOR 298
+#define DELE 299
+#define CWD 300
+#define LIST 301
+#define NLST 302
+#define SITE 303
+#define STAT 304
+#define HELP 305
+#define NOOP 306
+#define MKD 307
+#define RMD 308
+#define PWD 309
+#define CDUP 310
+#define STOU 311
+#define SMNT 312
+#define SYST 313
+#define SIZE 314
+#define MDTM 315
+#define UMASK 316
+#define IDLE 317
+#define CHMOD 318
+#define LEXERR 319
diff --git a/usr.bin/yacc/test/ftp.y b/usr.bin/yacc/test/ftp.y
new file mode 100644
index 0000000..a9ee9cd
--- /dev/null
+++ b/usr.bin/yacc/test/ftp.y
@@ -0,0 +1,1180 @@
+/*
+ * Copyright (c) 1985, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89
+ */
+
+/*
+ * Grammar for FTP commands.
+ * See RFC 959.
+ */
+
+%{
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpcmd.y 5.20.1.1 (Berkeley) 3/2/89";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/ftp.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <time.h>
+
+extern struct sockaddr_in data_dest;
+extern int logged_in;
+extern struct passwd *pw;
+extern int guest;
+extern int logging;
+extern int type;
+extern int form;
+extern int debug;
+extern int timeout;
+extern int maxtimeout;
+extern int pdata;
+extern char hostname[], remotehost[];
+extern char proctitle[];
+extern char *globerr;
+extern int usedefault;
+extern int transflag;
+extern char tmpline[];
+char **glob();
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char cbuf[512];
+char *fromname;
+
+char *index();
+%}
+
+%token
+ A B C E F I
+ L N P R S T
+
+ SP CRLF COMMA STRING NUMBER
+
+ USER PASS ACCT REIN QUIT PORT
+ PASV TYPE STRU MODE RETR STOR
+ APPE MLFL MAIL MSND MSOM MSAM
+ MRSQ MRCP ALLO REST RNFR RNTO
+ ABOR DELE CWD LIST NLST SITE
+ STAT HELP NOOP MKD RMD PWD
+ CDUP STOU SMNT SYST SIZE MDTM
+
+ UMASK IDLE CHMOD
+
+ LEXERR
+
+%start cmd_list
+
+%%
+
+cmd_list: /* empty */
+ | cmd_list cmd
+ = {
+ fromname = (char *) 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd: USER SP username CRLF
+ = {
+ user((char *) $3);
+ free((char *) $3);
+ }
+ | PASS SP password CRLF
+ = {
+ pass((char *) $3);
+ free((char *) $3);
+ }
+ | PORT SP host_port CRLF
+ = {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
+ | PASV CRLF
+ = {
+ passive();
+ }
+ | TYPE SP type_code CRLF
+ = {
+ switch (cmd_type) {
+
+ case TYPE_A:
+ if (cmd_form == FORM_N) {
+ reply(200, "Type set to A.");
+ type = cmd_type;
+ form = cmd_form;
+ } else
+ reply(504, "Form must be N.");
+ break;
+
+ case TYPE_E:
+ reply(504, "Type E not implemented.");
+ break;
+
+ case TYPE_I:
+ reply(200, "Type set to I.");
+ type = cmd_type;
+ break;
+
+ case TYPE_L:
+#if NBBY == 8
+ if (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ } else
+ reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+ UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+ }
+ }
+ | STRU SP struct_code CRLF
+ = {
+ switch ($3) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ | MODE SP mode_code CRLF
+ = {
+ switch ($3) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ | ALLO SP NUMBER CRLF
+ = {
+ reply(202, "ALLO command ignored.");
+ }
+ | ALLO SP NUMBER SP R SP NUMBER CRLF
+ = {
+ reply(202, "ALLO command ignored.");
+ }
+ | RETR check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ retrieve((char *) 0, (char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STOR check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "w", 0);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | APPE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "a", 0);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | NLST check_login CRLF
+ = {
+ if ($2)
+ send_file_list(".");
+ }
+ | NLST check_login SP STRING CRLF
+ = {
+ if ($2 && $4 != NULL)
+ send_file_list((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | LIST check_login CRLF
+ = {
+ if ($2)
+ retrieve("/bin/ls -lgA", "");
+ }
+ | LIST check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ retrieve("/bin/ls -lgA %s", (char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STAT check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ statfilecmd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STAT CRLF
+ = {
+ statcmd();
+ }
+ | DELE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ delete((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | RNTO SP pathname CRLF
+ = {
+ if (fromname) {
+ renamecmd(fromname, (char *) $3);
+ free(fromname);
+ fromname = (char *) 0;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
+ free((char *) $3);
+ }
+ | ABOR CRLF
+ = {
+ reply(225, "ABOR command successful.");
+ }
+ | CWD check_login CRLF
+ = {
+ if ($2)
+ cwd(pw->pw_dir);
+ }
+ | CWD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ cwd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | HELP CRLF
+ = {
+ help(cmdtab, (char *) 0);
+ }
+ | HELP SP STRING CRLF
+ = {
+ register char *cp = (char *)$3;
+
+ if (strncasecmp(cp, "SITE", 4) == 0) {
+ cp = (char *)$3 + 4;
+ if (*cp == ' ')
+ cp++;
+ if (*cp)
+ help(sitetab, cp);
+ else
+ help(sitetab, (char *) 0);
+ } else
+ help(cmdtab, (char *) $3);
+ }
+ | NOOP CRLF
+ = {
+ reply(200, "NOOP command successful.");
+ }
+ | MKD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ makedir((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | RMD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ removedir((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | PWD check_login CRLF
+ = {
+ if ($2)
+ pwd();
+ }
+ | CDUP check_login CRLF
+ = {
+ if ($2)
+ cwd("..");
+ }
+ | SITE SP HELP CRLF
+ = {
+ help(sitetab, (char *) 0);
+ }
+ | SITE SP HELP SP STRING CRLF
+ = {
+ help(sitetab, (char *) $5);
+ }
+ | SITE SP UMASK check_login CRLF
+ = {
+ int oldmask;
+
+ if ($4) {
+ oldmask = umask(0);
+ (void) umask(oldmask);
+ reply(200, "Current UMASK is %03o", oldmask);
+ }
+ }
+ | SITE SP UMASK check_login SP octal_number CRLF
+ = {
+ int oldmask;
+
+ if ($4) {
+ if (($6 == -1) || ($6 > 0777)) {
+ reply(501, "Bad UMASK value");
+ } else {
+ oldmask = umask($6);
+ reply(200,
+ "UMASK set to %03o (was %03o)",
+ $6, oldmask);
+ }
+ }
+ }
+ | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+ = {
+ if ($4 && ($8 != NULL)) {
+ if ($6 > 0777)
+ reply(501,
+ "CHMOD: Mode value must be between 0 and 0777");
+ else if (chmod((char *) $8, $6) < 0)
+ perror_reply(550, (char *) $8);
+ else
+ reply(200, "CHMOD command successful.");
+ }
+ if ($8 != NULL)
+ free((char *) $8);
+ }
+ | SITE SP IDLE CRLF
+ = {
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d",
+ timeout, maxtimeout);
+ }
+ | SITE SP IDLE SP NUMBER CRLF
+ = {
+ if ($5 < 30 || $5 > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds",
+ maxtimeout);
+ } else {
+ timeout = $5;
+ (void) alarm((unsigned) timeout);
+ reply(200,
+ "Maximum IDLE time set to %d seconds",
+ timeout);
+ }
+ }
+ | STOU check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "w", 1);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | SYST CRLF
+ = {
+#ifdef unix
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d",
+ NBBY, BSD);
+#else /* BSD */
+ reply(215, "UNIX Type: L%d", NBBY);
+#endif /* BSD */
+#else /* unix */
+ reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* unix */
+ }
+
+ /*
+ * SIZE is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return size of file in a format suitable for
+ * using with RESTART (we just count bytes).
+ */
+ | SIZE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ sizecmd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+
+ /*
+ * MDTM is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return modification time of file as an ISO 3307
+ * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
+ * where xxx is the fractional second (of any precision,
+ * not necessarily 3 digits)
+ */
+ | MDTM check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL) {
+ struct stat stbuf;
+ if (stat((char *) $4, &stbuf) < 0)
+ perror_reply(550, "%s", (char *) $4);
+ else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.",
+ (char *) $4);
+ } else {
+ register struct tm *t;
+ struct tm *gmtime();
+ t = gmtime(&stbuf.st_mtime);
+ reply(213,
+ "19%02d%02d%02d%02d%02d%02d",
+ t->tm_year, t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ }
+ }
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | QUIT CRLF
+ = {
+ reply(221, "Goodbye.");
+ dologout(0);
+ }
+ | error CRLF
+ = {
+ yyerrok;
+ }
+ ;
+rcmd: RNFR check_login SP pathname CRLF
+ = {
+ char *renamefrom();
+
+ if ($2 && $4) {
+ fromname = renamefrom((char *) $4);
+ if (fromname == (char *) 0 && $4) {
+ free((char *) $4);
+ }
+ }
+ }
+ ;
+
+username: STRING
+ ;
+
+password: /* empty */
+ = {
+ *(char **)&($$) = "";
+ }
+ | STRING
+ ;
+
+byte_size: NUMBER
+ ;
+
+host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER
+ = {
+ register char *a, *p;
+
+ a = (char *)&data_dest.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ p = (char *)&data_dest.sin_port;
+ p[0] = $9; p[1] = $11;
+ data_dest.sin_family = AF_INET;
+ }
+ ;
+
+form_code: N
+ = {
+ $$ = FORM_N;
+ }
+ | T
+ = {
+ $$ = FORM_T;
+ }
+ | C
+ = {
+ $$ = FORM_C;
+ }
+ ;
+
+type_code: A
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = FORM_N;
+ }
+ | A SP form_code
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = $3;
+ }
+ | E
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = FORM_N;
+ }
+ | E SP form_code
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = $3;
+ }
+ | I
+ = {
+ cmd_type = TYPE_I;
+ }
+ | L
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = NBBY;
+ }
+ | L SP byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $3;
+ }
+ /* this is for a bug in the BBN ftp */
+ | L byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $2;
+ }
+ ;
+
+struct_code: F
+ = {
+ $$ = STRU_F;
+ }
+ | R
+ = {
+ $$ = STRU_R;
+ }
+ | P
+ = {
+ $$ = STRU_P;
+ }
+ ;
+
+mode_code: S
+ = {
+ $$ = MODE_S;
+ }
+ | B
+ = {
+ $$ = MODE_B;
+ }
+ | C
+ = {
+ $$ = MODE_C;
+ }
+ ;
+
+pathname: pathstring
+ = {
+ /*
+ * Problem: this production is used for all pathname
+ * processing, but only gives a 550 error reply.
+ * This is a valid reply in some cases but not in others.
+ */
+ if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
+ *(char **)&($$) = *glob((char *) $1);
+ if (globerr != NULL) {
+ reply(550, globerr);
+ $$ = NULL;
+ }
+ free((char *) $1);
+ } else
+ $$ = $1;
+ }
+ ;
+
+pathstring: STRING
+ ;
+
+octal_number: NUMBER
+ = {
+ register int ret, dec, multby, digit;
+
+ /*
+ * Convert a number that was read as decimal number
+ * to what it would be if it had been read as octal.
+ */
+ dec = $1;
+ multby = 1;
+ ret = 0;
+ while (dec) {
+ digit = dec%10;
+ if (digit > 7) {
+ ret = -1;
+ break;
+ }
+ ret += digit * multby;
+ multby *= 8;
+ dec /= 10;
+ }
+ $$ = ret;
+ }
+ ;
+
+check_login: /* empty */
+ = {
+ if (logged_in)
+ $$ = 1;
+ else {
+ reply(530, "Please login with USER and PASS.");
+ $$ = 0;
+ }
+ }
+ ;
+
+%%
+
+extern jmp_buf errcatch;
+
+#define CMD 0 /* beginning of command */
+#define ARGS 1 /* expect miscellaneous arguments */
+#define STR1 2 /* expect SP followed by STRING */
+#define STR2 3 /* expect STRING */
+#define OSTR 4 /* optional SP then STRING */
+#define ZSTR1 5 /* SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+
+struct tab {
+ char *name;
+ short token;
+ short state;
+ short implemented; /* 1 if command is implemented */
+ char *help;
+};
+
+struct tab cmdtab[] = { /* In order defined in RFC 765 */
+ { "USER", USER, STR1, 1, "<sp> username" },
+ { "PASS", PASS, ZSTR1, 1, "<sp> password" },
+ { "ACCT", ACCT, STR1, 0, "(specify account)" },
+ { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
+ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
+ { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
+ { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
+ { "STRU", STRU, ARGS, 1, "(specify file structure)" },
+ { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
+ { "RETR", RETR, STR1, 1, "<sp> file-name" },
+ { "STOR", STOR, STR1, 1, "<sp> file-name" },
+ { "APPE", APPE, STR1, 1, "<sp> file-name" },
+ { "MLFL", MLFL, OSTR, 0, "(mail file)" },
+ { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
+ { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
+ { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
+ { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
+ { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
+ { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
+ { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
+ { "REST", REST, ARGS, 0, "(restart command)" },
+ { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
+ { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
+ { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
+ { "DELE", DELE, STR1, 1, "<sp> file-name" },
+ { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
+ { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
+ { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
+ { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
+ { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { "NOOP", NOOP, ARGS, 1, "" },
+ { "MKD", MKD, STR1, 1, "<sp> path-name" },
+ { "XMKD", MKD, STR1, 1, "<sp> path-name" },
+ { "RMD", RMD, STR1, 1, "<sp> path-name" },
+ { "XRMD", RMD, STR1, 1, "<sp> path-name" },
+ { "PWD", PWD, ARGS, 1, "(return current directory)" },
+ { "XPWD", PWD, ARGS, 1, "(return current directory)" },
+ { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "STOU", STOU, STR1, 1, "<sp> file-name" },
+ { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
+ { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab sitetab[] = {
+ { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
+ { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
+ { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab *
+lookup(p, cmd)
+ register struct tab *p;
+ char *cmd;
+{
+
+ for (; p->name != NULL; p++)
+ if (strcmp(cmd, p->name) == 0)
+ return (p);
+ return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *
+getline(s, n, iop)
+ char *s;
+ register FILE *iop;
+{
+ register c;
+ register char *cs;
+
+ cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+ *cs++ = tmpline[c];
+ if (tmpline[c] == '\n') {
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ tmpline[0] = '\0';
+ return(s);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ while ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, DONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, WONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0 || c == '\n')
+ break;
+ }
+ if (c == EOF && cs == s)
+ return (NULL);
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ return (s);
+}
+
+static int
+toolong()
+{
+ time_t now;
+ extern char *ctime();
+ extern time_t time();
+
+ reply(421,
+ "Timeout (%d seconds): closing control connection.", timeout);
+ (void) time(&now);
+ if (logging) {
+ syslog(LOG_INFO,
+ "User %s timed out after %d seconds at %s",
+ (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
+ }
+ dologout(1);
+}
+
+yylex()
+{
+ static int cpos, state;
+ register char *cp, *cp2;
+ register struct tab *p;
+ int n;
+ char c, *strpbrk();
+ char *copy();
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout);
+ if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ (void) alarm(0);
+#ifdef SETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != NULL)
+ setproctitle("%s: %s", proctitle, cbuf);
+#endif /* SETPROCTITLE */
+ if ((cp = index(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+ if ((cp = strpbrk(cbuf, " \n")))
+ cpos = cp - cbuf;
+ if (cpos == 0)
+ cpos = 4;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cbuf);
+ p = lookup(cmdtab, cbuf);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ *(char **)&yylval = p->name;
+ return (p->token);
+ }
+ break;
+
+ case SITECMD:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ cp = &cbuf[cpos];
+ if ((cp2 = strpbrk(cp, " \n")))
+ cpos = cp2 - cbuf;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cp);
+ p = lookup(sitetab, cp);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ state = CMD;
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ *(char **)&yylval = p->name;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ case ZSTR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ state = state == OSTR ? STR2 : ++state;
+ return (SP);
+ }
+ break;
+
+ case ZSTR2:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR2:
+ cp = &cbuf[cpos];
+ n = strlen(cp);
+ cpos += n - 1;
+ /*
+ * Make sure the string is nonempty and \n terminated.
+ */
+ if (n > 1 && cbuf[cpos] == '\n') {
+ cbuf[cpos] = '\0';
+ *(char **)&yylval = copy(cp);
+ cbuf[cpos] = '\n';
+ state = ARGS;
+ return (STRING);
+ }
+ break;
+
+ case NSTR:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval = atoi(cp);
+ cbuf[cpos] = c;
+ state = STR1;
+ return (NUMBER);
+ }
+ state = STR1;
+ goto dostr1;
+
+ case ARGS:
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval = atoi(cp);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ switch (cbuf[cpos++]) {
+
+ case '\n':
+ state = CMD;
+ return (CRLF);
+
+ case ' ':
+ return (SP);
+
+ case ',':
+ return (COMMA);
+
+ case 'A':
+ case 'a':
+ return (A);
+
+ case 'B':
+ case 'b':
+ return (B);
+
+ case 'C':
+ case 'c':
+ return (C);
+
+ case 'E':
+ case 'e':
+ return (E);
+
+ case 'F':
+ case 'f':
+ return (F);
+
+ case 'I':
+ case 'i':
+ return (I);
+
+ case 'L':
+ case 'l':
+ return (L);
+
+ case 'N':
+ case 'n':
+ return (N);
+
+ case 'P':
+ case 'p':
+ return (P);
+
+ case 'R':
+ case 'r':
+ return (R);
+
+ case 'S':
+ case 's':
+ return (S);
+
+ case 'T':
+ case 't':
+ return (T);
+
+ }
+ break;
+
+ default:
+ fatal("Unknown state in scanner.");
+ }
+ yyerror((char *) 0);
+ state = CMD;
+ longjmp(errcatch,0);
+ }
+}
+
+upper(s)
+ register char *s;
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+char *
+copy(s)
+ char *s;
+{
+ char *p;
+ extern char *malloc(), *strcpy();
+
+ p = malloc((unsigned) strlen(s) + 1);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+
+help(ctab, s)
+ struct tab *ctab;
+ char *s;
+{
+ register struct tab *c;
+ register int width, NCMDS;
+ char *type;
+
+ if (ctab == sitetab)
+ type = "SITE ";
+ else
+ type = "";
+ width = 0, NCMDS = 0;
+ for (c = ctab; c->name != NULL; c++) {
+ int len = strlen(c->name);
+
+ if (len > width)
+ width = len;
+ NCMDS++;
+ }
+ width = (width + 8) &~ 7;
+ if (s == 0) {
+ register int i, j, w;
+ int columns, lines;
+
+ lreply(214, "The following %scommands are recognized %s.",
+ type, "(* =>'s unimplemented)");
+ columns = 76 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ printf(" ");
+ for (j = 0; j < columns; j++) {
+ c = ctab + j * lines + i;
+ printf("%s%c", c->name,
+ c->implemented ? ' ' : '*');
+ if (c + lines >= &ctab[NCMDS])
+ break;
+ w = strlen(c->name) + 1;
+ while (w < width) {
+ putchar(' ');
+ w++;
+ }
+ }
+ printf("\r\n");
+ }
+ (void) fflush(stdout);
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == (struct tab *)0) {
+ reply(502, "Unknown command %s.", s);
+ return;
+ }
+ if (c->implemented)
+ reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+ else
+ reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+ c->name, c->help);
+}
+
+sizecmd(filename)
+char *filename;
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I: {
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG)
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%lu", stbuf.st_size);
+ break;}
+ case TYPE_A: {
+ FILE *fin;
+ register int c, count;
+ struct stat stbuf;
+ fin = fopen(filename, "r");
+ if (fin == NULL) {
+ perror_reply(550, filename);
+ return;
+ }
+ if (fstat(fileno(fin), &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ }
+
+ count = 0;
+ while((c=getc(fin)) != EOF) {
+ if (c == '\n') /* will get expanded to \r\n */
+ count++;
+ count++;
+ }
+ (void) fclose(fin);
+
+ reply(213, "%ld", count);
+ break;}
+ default:
+ reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+ }
+}
diff --git a/usr.bin/yacc/verbose.c b/usr.bin/yacc/verbose.c
new file mode 100644
index 0000000..33ae265
--- /dev/null
+++ b/usr.bin/yacc/verbose.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)verbose.c 5.3 (Berkeley) 1/20/91";
+#endif /* not lint */
+
+#include "defs.h"
+
+static short *null_rules;
+
+verbose()
+{
+ register int i;
+
+ if (!vflag) return;
+
+ null_rules = (short *) MALLOC(nrules*sizeof(short));
+ if (null_rules == 0) no_space();
+ fprintf(verbose_file, "\f\n");
+ for (i = 0; i < nstates; i++)
+ print_state(i);
+ FREE(null_rules);
+
+ if (nunused)
+ log_unused();
+ if (SRtotal || RRtotal)
+ log_conflicts();
+
+ fprintf(verbose_file, "\n\n%d terminals, %d nonterminals\n", ntokens,
+ nvars);
+ fprintf(verbose_file, "%d grammar rules, %d states\n", nrules - 2, nstates);
+}
+
+
+log_unused()
+{
+ register int i;
+ register short *p;
+
+ fprintf(verbose_file, "\n\nRules never reduced:\n");
+ for (i = 3; i < nrules; ++i)
+ {
+ if (!rules_used[i])
+ {
+ fprintf(verbose_file, "\t%s :", symbol_name[rlhs[i]]);
+ for (p = ritem + rrhs[i]; *p >= 0; ++p)
+ fprintf(verbose_file, " %s", symbol_name[*p]);
+ fprintf(verbose_file, " (%d)\n", i - 2);
+ }
+ }
+}
+
+
+log_conflicts()
+{
+ register int i;
+
+ fprintf(verbose_file, "\n\n");
+ for (i = 0; i < nstates; i++)
+ {
+ if (SRconflicts[i] || RRconflicts[i])
+ {
+ fprintf(verbose_file, "State %d contains ", i);
+ if (SRconflicts[i] == 1)
+ fprintf(verbose_file, "1 shift/reduce conflict");
+ else if (SRconflicts[i] > 1)
+ fprintf(verbose_file, "%d shift/reduce conflicts",
+ SRconflicts[i]);
+ if (SRconflicts[i] && RRconflicts[i])
+ fprintf(verbose_file, ", ");
+ if (RRconflicts[i] == 1)
+ fprintf(verbose_file, "1 reduce/reduce conflict");
+ else if (RRconflicts[i] > 1)
+ fprintf(verbose_file, "%d reduce/reduce conflicts",
+ RRconflicts[i]);
+ fprintf(verbose_file, ".\n");
+ }
+ }
+}
+
+
+print_state(state)
+int state;
+{
+ if (state)
+ fprintf(verbose_file, "\n\n");
+ if (SRconflicts[state] || RRconflicts[state])
+ print_conflicts(state);
+ fprintf(verbose_file, "state %d\n", state);
+ print_core(state);
+ print_nulls(state);
+ print_actions(state);
+}
+
+
+print_conflicts(state)
+int state;
+{
+ register int symbol, act, number;
+ register action *p;
+
+ symbol = -1;
+ for (p = parser[state]; p; p = p->next)
+ {
+ if (p->suppressed == 2)
+ continue;
+
+ if (p->symbol != symbol)
+ {
+ symbol = p->symbol;
+ number = p->number;
+ if (p->action_code == SHIFT)
+ act = SHIFT;
+ else
+ act = REDUCE;
+ }
+ else if (p->suppressed == 1)
+ {
+ if (state == final_state && symbol == 0)
+ {
+ fprintf(verbose_file, "%d: shift/reduce conflict \
+(accept, reduce %d) on $end\n", state, p->number - 2);
+ }
+ else
+ {
+ if (act == SHIFT)
+ {
+ fprintf(verbose_file, "%d: shift/reduce conflict \
+(shift %d, reduce %d) on %s\n", state, number, p->number - 2,
+ symbol_name[symbol]);
+ }
+ else
+ {
+ fprintf(verbose_file, "%d: reduce/reduce conflict \
+(reduce %d, reduce %d) on %s\n", state, number - 2, p->number - 2,
+ symbol_name[symbol]);
+ }
+ }
+ }
+ }
+}
+
+
+print_core(state)
+int state;
+{
+ register int i;
+ register int k;
+ register int rule;
+ register core *statep;
+ register short *sp;
+ register short *sp1;
+
+ statep = state_table[state];
+ k = statep->nitems;
+
+ for (i = 0; i < k; i++)
+ {
+ sp1 = sp = ritem + statep->items[i];
+
+ while (*sp >= 0) ++sp;
+ rule = -(*sp);
+ fprintf(verbose_file, "\t%s : ", symbol_name[rlhs[rule]]);
+
+ for (sp = ritem + rrhs[rule]; sp < sp1; sp++)
+ fprintf(verbose_file, "%s ", symbol_name[*sp]);
+
+ putc('.', verbose_file);
+
+ while (*sp >= 0)
+ {
+ fprintf(verbose_file, " %s", symbol_name[*sp]);
+ sp++;
+ }
+ fprintf(verbose_file, " (%d)\n", -2 - *sp);
+ }
+}
+
+
+print_nulls(state)
+int state;
+{
+ register action *p;
+ register int i, j, k, nnulls;
+
+ nnulls = 0;
+ for (p = parser[state]; p; p = p->next)
+ {
+ if (p->action_code == REDUCE &&
+ (p->suppressed == 0 || p->suppressed == 1))
+ {
+ i = p->number;
+ if (rrhs[i] + 1 == rrhs[i+1])
+ {
+ for (j = 0; j < nnulls && i > null_rules[j]; ++j)
+ continue;
+
+ if (j == nnulls)
+ {
+ ++nnulls;
+ null_rules[j] = i;
+ }
+ else if (i != null_rules[j])
+ {
+ ++nnulls;
+ for (k = nnulls - 1; k > j; --k)
+ null_rules[k] = null_rules[k-1];
+ null_rules[j] = i;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < nnulls; ++i)
+ {
+ j = null_rules[i];
+ fprintf(verbose_file, "\t%s : . (%d)\n", symbol_name[rlhs[j]],
+ j - 2);
+ }
+ fprintf(verbose_file, "\n");
+}
+
+
+print_actions(stateno)
+int stateno;
+{
+ register action *p;
+ register shifts *sp;
+ register int as;
+
+ if (stateno == final_state)
+ fprintf(verbose_file, "\t$end accept\n");
+
+ p = parser[stateno];
+ if (p)
+ {
+ print_shifts(p);
+ print_reductions(p, defred[stateno]);
+ }
+
+ sp = shift_table[stateno];
+ if (sp && sp->nshifts > 0)
+ {
+ as = accessing_symbol[sp->shift[sp->nshifts - 1]];
+ if (ISVAR(as))
+ print_gotos(stateno);
+ }
+}
+
+
+print_shifts(p)
+register action *p;
+{
+ register int count;
+ register action *q;
+
+ count = 0;
+ for (q = p; q; q = q->next)
+ {
+ if (q->suppressed < 2 && q->action_code == SHIFT)
+ ++count;
+ }
+
+ if (count > 0)
+ {
+ for (; p; p = p->next)
+ {
+ if (p->action_code == SHIFT && p->suppressed == 0)
+ fprintf(verbose_file, "\t%s shift %d\n",
+ symbol_name[p->symbol], p->number);
+ }
+ }
+}
+
+
+print_reductions(p, defred)
+register action *p;
+register int defred;
+{
+ register int k, anyreds;
+ register action *q;
+
+ anyreds = 0;
+ for (q = p; q ; q = q->next)
+ {
+ if (q->action_code == REDUCE && q->suppressed < 2)
+ {
+ anyreds = 1;
+ break;
+ }
+ }
+
+ if (anyreds == 0)
+ fprintf(verbose_file, "\t. error\n");
+ else
+ {
+ for (; p; p = p->next)
+ {
+ if (p->action_code == REDUCE && p->number != defred)
+ {
+ k = p->number - 2;
+ if (p->suppressed == 0)
+ fprintf(verbose_file, "\t%s reduce %d\n",
+ symbol_name[p->symbol], k);
+ }
+ }
+
+ if (defred > 0)
+ fprintf(verbose_file, "\t. reduce %d\n", defred - 2);
+ }
+}
+
+
+print_gotos(stateno)
+int stateno;
+{
+ register int i, k;
+ register int as;
+ register short *to_state;
+ register shifts *sp;
+
+ putc('\n', verbose_file);
+ sp = shift_table[stateno];
+ to_state = sp->shift;
+ for (i = 0; i < sp->nshifts; ++i)
+ {
+ k = to_state[i];
+ as = accessing_symbol[k];
+ if (ISVAR(as))
+ fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k);
+ }
+}
diff --git a/usr.bin/yacc/warshall.c b/usr.bin/yacc/warshall.c
new file mode 100644
index 0000000..4672244
--- /dev/null
+++ b/usr.bin/yacc/warshall.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Paul Corbett.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)warshall.c 5.4 (Berkeley) 5/24/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+transitive_closure(R, n)
+unsigned *R;
+int n;
+{
+ register int rowsize;
+ register unsigned i;
+ register unsigned *rowj;
+ register unsigned *rp;
+ register unsigned *rend;
+ register unsigned *ccol;
+ register unsigned *relend;
+ register unsigned *cword;
+ register unsigned *rowi;
+
+ rowsize = WORDSIZE(n);
+ relend = R + n*rowsize;
+
+ cword = R;
+ i = 0;
+ rowi = R;
+ while (rowi < relend)
+ {
+ ccol = cword;
+ rowj = R;
+
+ while (rowj < relend)
+ {
+ if (*ccol & (1 << i))
+ {
+ rp = rowi;
+ rend = rowj + rowsize;
+ while (rowj < rend)
+ *rowj++ |= *rp++;
+ }
+ else
+ {
+ rowj += rowsize;
+ }
+
+ ccol += rowsize;
+ }
+
+ if (++i >= BITS_PER_WORD)
+ {
+ i = 0;
+ cword++;
+ }
+
+ rowi += rowsize;
+ }
+}
+
+reflexive_transitive_closure(R, n)
+unsigned *R;
+int n;
+{
+ register int rowsize;
+ register unsigned i;
+ register unsigned *rp;
+ register unsigned *relend;
+
+ transitive_closure(R, n);
+
+ rowsize = WORDSIZE(n);
+ relend = R + n*rowsize;
+
+ i = 0;
+ rp = R;
+ while (rp < relend)
+ {
+ *rp |= (1 << i);
+ if (++i >= BITS_PER_WORD)
+ {
+ i = 0;
+ rp++;
+ }
+
+ rp += rowsize;
+ }
+}
diff --git a/usr.bin/yacc/yacc.1 b/usr.bin/yacc/yacc.1
new file mode 100644
index 0000000..787dc00
--- /dev/null
+++ b/usr.bin/yacc/yacc.1
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1989, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Paul Corbett.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)yacc.1 5.8 (Berkeley) 5/24/93
+.\"
+.TH YACC 1 "May 24, 1993"
+.UC 6
+.SH NAME
+yacc \- an LALR(1) parser generator
+.SH SYNOPSIS
+.B yacc [ -dlrtv ] [ -b
+.I file_prefix
+.B ] [ -p
+.I symbol_prefix
+.B ]
+.I filename
+.SH DESCRIPTION
+.I Yacc
+reads the grammar specification in the file
+.I filename
+and generates an LR(1) parser for it.
+The parsers consist of a set of LALR(1) parsing tables and a driver routine
+written in the C programming language.
+.I Yacc
+normally writes the parse tables and the driver routine to the file
+.IR y.tab.c.
+.PP
+The following options are available:
+.RS
+.TP
+\fB-b \fIfile_prefix\fR
+The
+.B -b
+option changes the prefix prepended to the output file names to
+the string denoted by
+.IR file_prefix.
+The default prefix is the character
+.IR y.
+.TP
+.B -d
+The \fB-d\fR option causes the header file
+.IR y.tab.h
+to be written.
+.TP
+.B -l
+If the
+.B -l
+option is not specified,
+.I yacc
+will insert #line directives in the generated code.
+The #line directives let the C compiler relate errors in the
+generated code to the user's original code.
+If the \fB-l\fR option is specified,
+.I yacc
+will not insert the #line directives.
+Any #line directives specified by the user will be retained.
+.TP
+\fB-p \fIsymbol_prefix\fR
+The
+.B -p
+option changes the prefix prepended to yacc-generated symbols to
+the string denoted by
+.IR symbol_prefix.
+The default prefix is the string
+.IR yy.
+.TP
+.B -r
+The
+.B -r
+option causes
+.I yacc
+to produce separate files for code and tables. The code file
+is named
+.IR y.code.c,
+and the tables file is named
+.IR y.tab.c.
+.TP
+.B -t
+The
+.B -t
+option changes the preprocessor directives generated by
+.I yacc
+so that debugging statements will be incorporated in the compiled code.
+.TP
+.B -v
+The
+.B -v
+option causes a human-readable description of the generated parser to
+be written to the file
+.IR y.output.
+.RE
+.PP
+If the environment variable TMPDIR is set, the string denoted by
+TMPDIR will be used as the name of the directory where the temporary
+files are created.
+.SH FILES
+.IR y.code.c
+.br
+.IR y.tab.c
+.br
+.IR y.tab.h
+.br
+.IR y.output
+.br
+.IR /tmp/yacc.aXXXXXX
+.br
+.IR /tmp/yacc.tXXXXXX
+.br
+.IR /tmp/yacc.uXXXXXX
+.SH DIAGNOSTICS
+If there are rules that are never reduced, the number of such rules is
+reported on standard error.
+If there are any LALR(1) conflicts, the number of conflicts is reported
+on standard error.
diff --git a/usr.bin/yacc/yyfix.1 b/usr.bin/yacc/yyfix.1
new file mode 100644
index 0000000..7af0a9e
--- /dev/null
+++ b/usr.bin/yacc/yyfix.1
@@ -0,0 +1,112 @@
+.\" Copyright (c) 1990, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)yyfix.1 5.4 (Berkeley) 3/23/93
+.\"
+.Dd March 23, 1993
+.Dt YYFIX 1
+.Os BSD 4.4
+.Sh NAME
+.Nm yyfix
+.Nd extract tables from y.tab.c
+.Sh SYNOPSIS
+.Nm yyfix
+.Ar file
+.Op Ar tables
+.Sh DESCRIPTION
+Programs have historically used a script (often named ``:yyfix'') to
+extract tables from the
+.Xr yacc 1
+generated file
+.Pa y.tab.c .
+As the names of the tables generated by the current version of
+.Xr yacc
+are different from those of historical versions of
+.Xr yacc ,
+the shell script
+.Nm yyfix
+is provided to simplify the transition.
+.Pp
+The first (and required) argument to
+.Nm yyfix
+is the name of the file where the extracted tables should be stored.
+.Pp
+If further command line arguments are specified, they are taken as
+the list of tables to be extracted.
+Otherwise,
+.Nm yyfix
+attempts to determine if the
+.Pa y.tab.c
+file is from an old or new
+.Xr yacc ,
+and extracts the appropriate tables.
+.Pp
+The tables
+.Dq yyexca ,
+.Dq yyact ,
+.Dq yypact ,
+.Dq yypgo ,
+.Dq yyr1 ,
+.Dq yyr2 ,
+.Dq yychk ,
+and
+.Dq yydef
+are extracted
+from historical versions of
+.Xr yacc .
+.Pp
+The tables
+.Dq yylhs ,
+.Dq yylen ,
+.Dq yydefred ,
+.Dq yydgoto ,
+.Dq yysindex ,
+.Dq yyrindex ,
+.Dq yygindex ,
+.Dq yytable ,
+.Dq yyname ,
+.Dq yyrule ,
+and
+.Dq yycheck ,
+are extracted from the current version of
+.Xr yacc .
+.Sh FILES
+.Bl -tag -width y.tab.c
+.It Pa y.tab.c
+File from which tables are extracted.
+.El
+.Sh SEE ALSO
+.Xr yacc 1
+.Sh HISTORY
+The
+.Nm
+command appears in
+.Bx 4.4 .
diff --git a/usr.bin/yacc/yyfix.sh b/usr.bin/yacc/yyfix.sh
new file mode 100644
index 0000000..29085f9
--- /dev/null
+++ b/usr.bin/yacc/yyfix.sh
@@ -0,0 +1,71 @@
+#!/bin/sh -
+#
+# Copyright (c) 1990 The Regents of the University of California.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)yyfix.sh 5.2 (Berkeley) 5/12/90
+#
+OLDYACC="yyexca yyact yypact yypgo yyr1 yyr2 yychk yydef"
+NEWYACC="yylhs yylen yydefred yydgoto yysindex yyrindex yygindex \
+ yytable yycheck"
+
+file=$1
+>$file
+shift
+
+if [ $# -eq 0 ] ; then
+ if grep yylhs y.tab.c > /dev/null ; then
+ if grep yyname y.tab.c > /dev/null ; then
+ NEWYACC="$NEWYACC yyname"
+ fi
+ if grep yyrule y.tab.c > /dev/null ; then
+ NEWYACC="$NEWYACC yyrule"
+ fi
+ set $NEWYACC
+ else
+ set $OLDYACC
+ fi
+fi
+
+for i
+do
+ed - y.tab.c << END
+/^\(.*\)$i[ ]*\[]/s//extern \1 $i[];\\
+\1 $i []/
+.ka
+/}/kb
+'br $file
+'a,.w $file
+'a,.d
+w
+q
+END
+done
diff --git a/usr.bin/yes/Makefile b/usr.bin/yes/Makefile
new file mode 100644
index 0000000..1a1c3c0
--- /dev/null
+++ b/usr.bin/yes/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= yes
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/yes/yes.1 b/usr.bin/yes/yes.1
new file mode 100644
index 0000000..c83b9bb
--- /dev/null
+++ b/usr.bin/yes/yes.1
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)yes.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt YES 1
+.Os BSD 4
+.Sh NAME
+.Nm yes
+.Nd be repetitively affirmative
+.Sh SYNOPSIS
+.Nm yes
+.Op Ar expletive
+.Sh DESCRIPTION
+.Nm Yes
+outputs
+.Ar expletive ,
+or, by default,
+.Dq y ,
+forever.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.bin/yes/yes.c b/usr.bin/yes/yes.c
new file mode 100644
index 0000000..5e1edd6
--- /dev/null
+++ b/usr.bin/yes/yes.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1987, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)yes.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc > 1)
+ for(;;)
+ puts(argv[1]);
+ else for (;;)
+ puts("y");
+}
diff --git a/usr.bin/ypcat/Makefile b/usr.bin/ypcat/Makefile
new file mode 100644
index 0000000..c133eb5
--- /dev/null
+++ b/usr.bin/ypcat/Makefile
@@ -0,0 +1,6 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.3 1994/02/17 07:06:10 rgrimes Exp $
+
+PROG= ypcat
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ypcat/ypcat.1 b/usr.bin/ypcat/ypcat.1
new file mode 100644
index 0000000..99321b4
--- /dev/null
+++ b/usr.bin/ypcat/ypcat.1
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1993 Winning Strategies, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Winning Strategies, Inc.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ypcat.1,v 1.1 1994/01/11 19:01:27 nate Exp $
+.\"
+.Dd December 3, 1993
+.Dt YPCAT 1
+.Os
+.Sh NAME
+.Nm ypcat
+.Nd "print the values of all keys in a YP database"
+.Sh SYNOPSIS
+.Nm ypcat
+.Op Fl kt
+.Op Fl d Ar domainname
+.Ar mapname
+.Nm ypcat
+.Fl x
+.Sh DESCRIPTION
+.Nm Ypcat
+prints out the values of all keys from the
+.Tn YP
+database specified by
+.Ar mapname,
+which may be a map name or a map nickname.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d Ar domainname
+Specify a domain other than the default domain.
+.It Fl k
+Display map keys.
+This option is useful with maps in which the values are null or the key
+is not part of the value.
+.It Fl t
+Inhibit translation of map nicknames
+to their corresponding map names.
+.It Fl x
+Display the map nickname table.
+.El
+.Sh SEE ALSO
+.Xr ypmatch 1 ,
+.Xr yp 8
+.Sh AUTHOR
+Theo De Raadt
diff --git a/usr.bin/ypcat/ypcat.c b/usr.bin/ypcat/ypcat.c
new file mode 100644
index 0000000..85158a3
--- /dev/null
+++ b/usr.bin/ypcat/ypcat.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LINT
+static char rcsid[] = "ypcat.c,v 1.2 1993/05/16 02:49:01 deraadt Exp";
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+struct ypalias {
+ char *alias, *name;
+} ypaliases[] = {
+ { "passwd", "passwd.byname" },
+ { "group", "group.byname" },
+ { "networks", "networks.byaddr" },
+ { "hosts", "hosts.byaddr" },
+ { "protocols", "protocols.bynumber" },
+ { "services", "services.byname" },
+ { "aliases", "mail.aliases" },
+ { "ethers", "ethers.byname" },
+};
+
+int key;
+
+usage()
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\typcat [-k] [-d domainname] [-t] mapname\n");
+ fprintf(stderr, "\typcat -x\n");
+ exit(1);
+}
+
+printit(instatus, inkey, inkeylen, inval, invallen, indata)
+int instatus;
+char *inkey;
+int inkeylen;
+char *inval;
+int invallen;
+char *indata;
+{
+ if(instatus != YP_TRUE)
+ return instatus;
+ if(key)
+ printf("%*.*s ", inkeylen, inkeylen, inkey);
+ printf("%*.*s\n", invallen, invallen, inval);
+ return 0;
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ char *domainname;
+ struct ypall_callback ypcb;
+ char *inmap;
+ extern char *optarg;
+ extern int optind;
+ int notrans;
+ int c, r, i;
+
+ notrans = key = 0;
+ yp_get_default_domain(&domainname);
+
+ while( (c=getopt(argc, argv, "xd:kt")) != -1)
+ switch(c) {
+ case 'x':
+ for(i=0; i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ printf("Use \"%s\" for \"%s\"\n",
+ ypaliases[i].alias,
+ ypaliases[i].name);
+ exit(0);
+ case 'd':
+ domainname = optarg;
+ break;
+ case 't':
+ notrans++;
+ break;
+ case 'k':
+ key++;
+ break;
+ default:
+ usage();
+ }
+
+ if(optind + 1 != argc )
+ usage();
+
+ inmap = argv[optind];
+ for(i=0; (!notrans) && i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ if( strcmp(inmap, ypaliases[i].alias) == 0)
+ inmap = ypaliases[i].name;
+ ypcb.foreach = printit;
+ ypcb.data = NULL;
+
+ r = yp_all(domainname, inmap, &ypcb);
+ switch(r) {
+ case 0:
+ break;
+ case YPERR_YPBIND:
+ fprintf(stderr, "ypcat: not running ypbind\n");
+ exit(1);
+ default:
+ fprintf(stderr, "No such map %s. Reason: %s\n",
+ inmap, yperr_string(r));
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/usr.bin/ypmatch/Makefile b/usr.bin/ypmatch/Makefile
new file mode 100644
index 0000000..cf79ca7
--- /dev/null
+++ b/usr.bin/ypmatch/Makefile
@@ -0,0 +1,6 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.3 1994/02/17 07:06:13 rgrimes Exp $
+
+PROG= ypmatch
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ypmatch/ypmatch.1 b/usr.bin/ypmatch/ypmatch.1
new file mode 100644
index 0000000..0b7ac89
--- /dev/null
+++ b/usr.bin/ypmatch/ypmatch.1
@@ -0,0 +1,71 @@
+.\" Copyright (c) 1993 Winning Strategies, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Winning Strategies, Inc.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ypmatch.1,v 1.1 1994/01/11 19:01:30 nate Exp $
+.\"
+.Dd December 3, 1993
+.Dt YPMATCH 1
+.Os
+.Sh NAME
+.Nm ypmatch
+.Nd "print the values of one or more keys in a YP database"
+.Sh SYNOPSIS
+.Nm ypmatch
+.Op Fl kt
+.Op Fl d Ar domainname
+.Ar key ...
+.Ar mapname
+.Nm ypmatch
+.Fl x
+.Sh DESCRIPTION
+.Nm Ypmatch
+prints out the values of one or more keys from the
+.Tn YP
+database specified by
+.Ar mapname,
+which may be a map name or a map nickname.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d Ar domainname
+Specify a domain other than the default domain.
+.It Fl k
+Display map keys.
+This option is useful with maps in which the values are null or the key
+is not part of the value.
+.It Fl t
+Inhibit translation of map nicknames
+to their corresponding map names.
+.It Fl x
+Display the map nickname table.
+.El
+.Sh SEE ALSO
+.Xr ypcat 1 ,
+.Xr yp 8
+.Sh AUTHOR
+Theo De Raadt
diff --git a/usr.bin/ypmatch/ypmatch.c b/usr.bin/ypmatch/ypmatch.c
new file mode 100644
index 0000000..3fe57d6
--- /dev/null
+++ b/usr.bin/ypmatch/ypmatch.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LINT
+static char rcsid[] = "ypmatch.c,v 1.2 1993/05/16 02:49:03 deraadt Exp";
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+struct ypalias {
+ char *alias, *name;
+} ypaliases[] = {
+ { "passwd", "passwd.byname" },
+ { "group", "group.byname" },
+ { "networks", "networks.byaddr" },
+ { "hosts", "hosts.byname" },
+ { "protocols", "protocols.bynumber" },
+ { "services", "services.byname" },
+ { "aliases", "mail.aliases" },
+ { "ethers", "ethers.byname" },
+};
+
+usage()
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\typmatch [-d domain] [-t] [-k] key [key ...] mname\n");
+ fprintf(stderr, "\typmatch -x\n");
+ fprintf(stderr, "where\n");
+ fprintf(stderr, "\tmname may be either a mapname or a nickname for a map\n");
+ fprintf(stderr, "\t-t inhibits map nickname translation\n");
+ fprintf(stderr, "\t-k prints keys as well as values.\n");
+ fprintf(stderr, "\t-x dumps the map nickname translation table.\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ char *domainname;
+ char *inkey, *inmap, *outbuf;
+ extern char *optarg;
+ extern int optind;
+ int outbuflen, key, notrans;
+ int c, r, i;
+
+ notrans = key = 0;
+ yp_get_default_domain(&domainname);
+
+ while( (c=getopt(argc, argv, "xd:kt")) != -1)
+ switch(c) {
+ case 'x':
+ for(i=0; i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ printf("Use \"%s\" for \"%s\"\n",
+ ypaliases[i].alias,
+ ypaliases[i].name);
+ exit(0);
+ case 'd':
+ domainname = optarg;
+ break;
+ case 't':
+ notrans++;
+ break;
+ case 'k':
+ key++;
+ break;
+ default:
+ usage();
+ }
+
+ if( (argc-optind) < 2 )
+ usage();
+
+ inmap = argv[argc-1];
+ for(i=0; (!notrans) && i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ if( strcmp(inmap, ypaliases[i].alias) == 0)
+ inmap = ypaliases[i].name;
+ for(; optind < argc-1; optind++) {
+ inkey = argv[optind];
+
+ r = yp_match(domainname, inmap, inkey,
+ strlen(inkey), &outbuf, &outbuflen);
+ switch(r) {
+ case 0:
+ if(key)
+ printf("%s ", inkey);
+ printf("%*.*s\n", outbuflen, outbuflen, outbuf);
+ break;
+ case YPERR_YPBIND:
+ fprintf(stderr, "yp_match: not running ypbind\n");
+ exit(1);
+ default:
+ fprintf(stderr, "Can't match key %s in map %s. Reason: %s\n",
+ inkey, inmap, yperr_string(r));
+ break;
+ }
+ }
+ exit(0);
+}
diff --git a/usr.bin/ypwhich/Makefile b/usr.bin/ypwhich/Makefile
new file mode 100644
index 0000000..1c964b2
--- /dev/null
+++ b/usr.bin/ypwhich/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.2 1994/02/17 07:06:22 rgrimes Exp $
+
+PROG= ypwhich
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.bin/ypwhich/ypwhich.c b/usr.bin/ypwhich/ypwhich.c
new file mode 100644
index 0000000..f13aa89
--- /dev/null
+++ b/usr.bin/ypwhich/ypwhich.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef LINT
+static char rcsid[] = "ypwhich.c,v 1.2 1993/05/16 02:49:10 deraadt Exp";
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#define ERR_USAGE 1 /* bad arguments - display 'usage' message */
+#define ERR_NOSUCHHOST 2 /* no such host */
+#define ERR_NOBINDING 3 /* error from ypbind -- domain not bound */
+#define ERR_NOYPBIND 4 /* ypbind not running */
+#define ERR_NOMASTER 5 /* could not find master server */
+
+extern bool_t xdr_domainname();
+
+struct ypalias {
+ char *alias, *name;
+} ypaliases[] = {
+ { "passwd", "passwd.byname" },
+ { "group", "group.byname" },
+ { "networks", "networks.byaddr" },
+ { "hosts", "hosts.byaddr" },
+ { "protocols", "protocols.bynumber" },
+ { "services", "services.byname" },
+ { "aliases", "mail.aliases" },
+ { "ethers", "ethers.byname" },
+};
+
+usage()
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\typwhich [-d domain] [[-t] -m [mname] | host]\n");
+ fprintf(stderr, "\typwhich -x\n");
+ exit(ERR_USAGE);
+}
+
+
+/*
+ * Like yp_bind except can query a specific host
+ */
+bind_host(dom, sin)
+char *dom;
+struct sockaddr_in *sin;
+{
+ struct hostent *hent = NULL;
+ struct ypbind_resp ypbr;
+ struct dom_binding *ysd;
+ struct timeval tv;
+ CLIENT *client;
+ int sock, r;
+ u_long ss_addr;
+
+ sock = RPC_ANYSOCK;
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+ client = clntudp_create(sin, YPBINDPROG, YPBINDVERS, tv, &sock);
+ if(client==NULL) {
+ fprintf(stderr, "can't clntudp_create: %s\n",
+ yperr_string(YPERR_YPBIND));
+ return YPERR_YPBIND;
+ }
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ r = clnt_call(client, YPBINDPROC_DOMAIN,
+ xdr_domainname, dom, xdr_ypbind_resp, &ypbr, tv);
+ if( r != RPC_SUCCESS) {
+ fprintf(stderr, "can't clnt_call: %s\n",
+ yperr_string(YPERR_YPBIND));
+ clnt_destroy(client);
+ return YPERR_YPBIND;
+ } else {
+ if (ypbr.ypbind_status != YPBIND_SUCC_VAL) {
+ fprintf(stderr, "can't yp_bind: Reason: %s\n",
+ ypbinderr_string(ypbr.ypbind_respbody.ypbind_error));
+ clnt_destroy(client);
+ return r;
+ }
+ }
+ clnt_destroy(client);
+
+ ss_addr = ypbr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr;
+ /*printf("%08x\n", ss_addr);*/
+ hent = gethostbyaddr((char *)&ss_addr, sizeof(ss_addr), AF_INET);
+ if (hent)
+ printf("%s\n", hent->h_name);
+ else
+ printf("%s\n", inet_ntoa(ss_addr));
+ return 0;
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ char *domainname, *master, *map;
+ struct ypmaplist *ypml, *y;
+ extern char *optarg;
+ extern int optind;
+ struct hostent *hent;
+ struct sockaddr_in sin;
+ int notrans, mode, getmap;
+ int c, r, i;
+
+ yp_get_default_domain(&domainname);
+
+ map = NULL;
+ getmap = notrans = mode = 0;
+ while( (c=getopt(argc, argv, "xd:mt")) != -1)
+ switch(c) {
+ case 'x':
+ for(i=0; i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ printf("\"%s\" is an alias for \"%s\"\n",
+ ypaliases[i].alias,
+ ypaliases[i].name);
+ exit(0);
+ case 'd':
+ domainname = optarg;
+ break;
+ case 't':
+ notrans++;
+ break;
+ case 'm':
+ mode++;
+ break;
+ default:
+ usage();
+ }
+
+ if(mode==0) {
+ switch(argc-optind) {
+ case 0:
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if(bind_host(domainname, &sin))
+ exit(ERR_NOBINDING);
+ break;
+ case 1:
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ if( (sin.sin_addr.s_addr=inet_addr(argv[optind]))==-1) {
+ hent = gethostbyname(argv[optind]);
+ if(!hent) {
+ fprintf(stderr, "ypwhich: host %s unknown\n",
+ argv[optind]);
+ exit(ERR_NOSUCHHOST);
+ }
+ bcopy((char *)hent->h_addr_list[0],
+ (char *)&sin.sin_addr, sizeof sin.sin_addr);
+ }
+ if(bind_host(domainname, &sin))
+ exit(ERR_NOBINDING);
+ break;
+ default:
+ usage();
+ }
+ exit(0);
+ }
+
+ if( argc-optind > 1)
+ usage();
+
+ if(argv[optind]) {
+ map = argv[optind];
+ for(i=0; (!notrans) && i<sizeof ypaliases/sizeof ypaliases[0]; i++)
+ if( strcmp(map, ypaliases[i].alias) == 0)
+ map = ypaliases[i].name;
+ r = yp_master(domainname, map, &master);
+ switch(r) {
+ case 0:
+ printf("%s\n", master);
+ free(master);
+ break;
+ case YPERR_YPBIND:
+ fprintf(stderr, "ypwhich: not running ypbind\n");
+ exit(ERR_NOYPBIND);
+ default:
+ fprintf(stderr, "Can't find master for map %s. Reason: %s\n",
+ map, yperr_string(r));
+ exit(ERR_NOMASTER);
+ }
+ exit(0);
+ }
+
+ ypml = NULL;
+ r = yp_maplist(domainname, &ypml);
+ switch(r) {
+ case 0:
+ for(y=ypml; y; ) {
+ ypml = y;
+ r = yp_master(domainname, ypml->ypml_name, &master);
+ switch(r) {
+ case 0:
+ printf("%s %s\n", ypml->ypml_name, master);
+ free(master);
+ break;
+ default:
+ fprintf(stderr,
+ "YP: can't find the master of %s: Reason: %s\n",
+ ypml->ypml_name, yperr_string(r));
+ break;
+ }
+ y = ypml->ypml_next;
+ free(ypml);
+ }
+ break;
+ case YPERR_YPBIND:
+ fprintf(stderr, "ypwhich: not running ypbind\n");
+ exit(ERR_NOYPBIND);
+ default:
+ fprintf(stderr, "Can't get map list for domain %s. Reason: %s\n",
+ domainname, yperr_string(r));
+ exit(ERR_NOMASTER);
+ }
+ exit(0);
+}
OpenPOWER on IntegriCloud